diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index da5b6c7..f8ec47b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -85,7 +85,7 @@ jobs: - name: Infrastructure configuration scan run: >- docker run --rm -v "$PWD:/src" aquasec/trivy:0.74.0@sha256:62b1e65e8869bc4b4c6aa4fa2b21595256c7c2f6018a9d9ad61caf87187c1969 - config --exit-code 1 --severity HIGH,CRITICAL /src + config --ignorefile /src/.trivyignore.yaml --exit-code 1 --severity HIGH,CRITICAL /src - name: Image vulnerability scan run: | docker run --rm -v /var/run/docker.sock:/var/run/docker.sock aquasec/trivy:0.74.0@sha256:62b1e65e8869bc4b4c6aa4fa2b21595256c7c2f6018a9d9ad61caf87187c1969 image --exit-code 1 --ignore-unfixed --severity HIGH,CRITICAL data-terminal-cloud-backend:test diff --git a/.gitignore b/.gitignore index 027b940..67934f1 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,7 @@ web/dist/ test/e2e/screenshots/ .design-evidence/ device-simulator-windows/story-continuity-rtc/ +/.tmp-*/ +/firmware/**/build/ +/firmware/**/managed_components/ +/firmware/**/backups/ diff --git a/.gitleaks.toml b/.gitleaks.toml index 41786bd..b927d60 100644 --- a/.gitleaks.toml +++ b/.gitleaks.toml @@ -4,8 +4,8 @@ title = "Data terminal cloud secret scanning" useDefault = true [allowlist] -description = "HTTP idempotency keys are replay identifiers, not credentials" +description = "Exact public HTTP idempotency fixtures are replay identifiers, not credentials" condition = "AND" regexTarget = "line" -regexes = ['''Idempotency-Key'''] -paths = ['''(^|/)internal/httpapi/conversation_handlers_test\.go$'''] +regexes = ['''(?:test-idempotency-key-000[12]|test-activate-idempotency-0001|turns-0123456789abcdef|3ca4e514-cbde-41fc-aa32-6f1ed460cb01)'''] +paths = ['''(^|/)internal/httpapi/conversation_handlers_test\.go$''','''(^|/)docs/设备终端接口文档\.md$'''] diff --git a/.trivyignore.yaml b/.trivyignore.yaml new file mode 100644 index 0000000..6a80053 --- /dev/null +++ b/.trivyignore.yaml @@ -0,0 +1,9 @@ +misconfigurations: + - id: KSV-0109 + paths: + - "deploy/kubernetes/base/configmap.yaml" + statement: >- + RTC_TOKEN_ISSUER and the *_TTL values are public runtime metadata, not + credentials. All RTC signing keys, provider credentials, and token key + material are mounted from Kubernetes Secrets or the production CSI + secret store. diff --git a/CHANGELOG.md b/CHANGELOG.md index e51fc94..7bc6666 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,15 @@ ## 未发布 +- 新增V51硬件智能体License池:License明文仅在平台加密库存与设备安全区中出现,平台按Instance ID/Product Key原子分配,设备校验摘要并回执后才允许`PROVISIONING → ACTIVE`;过期、吊销、丢失和解绑会立即阻断新会话。 +- 新增V50设备激活状态机:`FACTORY → CLAIMABLE → BOUND → PROVISIONING → ACTIVE → LOST/REVOKED`,认领、火山凭据预配、解绑、丢失恢复和注销统一写入带修订号的迁移审计;LOST/解绑会暂停火山设备凭据,恢复后重新启用。 +- 新增独立`volc_hardware_agent`运行时和`/device/v3/agent-sessions`控制面;设备每次聊天从平台取得冻结的人设、档案、近期摘要、已确认长期记忆和授权上下文,不再复用RTC房间语义。 +- 新增V49平台代理火山设备注册和`/device/v3/hardware-agent-credentials:provision`:Product Secret只从平台Secret/KMS加载,一机一密在平台AES-256-GCM加密并下发对应设备,Windows端不再接收Product Secret。 +- 硬件会话新增按设备Bot/传输/最低SDK绑定、完整能力快照、动态`session.updated`生效证明、单调事件水位、可靠最终字幕、平台`stop_session`动作、恢复扫描和SaaS运行时可见性。 +- 不具备近端与远端双路PCM录制能力的硬件会话强制降级为仅文本或不留存;通话中记忆工具扩展到硬件会话并继续从数据库反查租户、长者和活动会话边界。 +- 新增V47/V48/V49硬件运行时及凭据迁移、Windows控制面模拟接口、端到端正反向冒烟,以及带清单和SHA-256的服务器部署候选包工具。 +- 维护中心新增租户级固件与OTA升级管理:可信Ed25519公钥、浏览器私有直传、大小与SHA-256校验、发布通道、强制升级标记、兼容设备灰度批次、可靠升级命令、暂停/恢复/退役和审计下载地址。 +- 固件制品支持维护端租户级范围,同时保留原家庭端记录和设备受控下载链路;设备资产列表显示最近上报的当前固件版本。 - 聊天总结改为大模型两级内容价值判断:先判断是否值得保留,再判断是否值得形成故事;纯寒暄不再生成摘要或故事。 - 新增内容价值判断记录,保留模型、提示词版本、置信度和舍弃原因,支持任务幂等与隐私删除。 - 本地开发新增 GPU Ollama/Qwen2.5 7B 部署方式,并通过大模型管理的测试、激活和 worker 生成链路完成端到端验证。 diff --git a/README.md b/README.md index 7833aeb..82d7cdc 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ - 私有数据导出:按长者和分类生成JSON,MinIO私有存储、SHA-256校验、短期签名下载和过期对象清理。 - 主体级隐私删除:精确称呼确认、活动通话保护、立即冻结读取/上传/召回,以及PostgreSQL、Redis、对象存储、pgvector、外部索引和私有导出六子系统的可重试对账与最小删除证明。 - 设备命令可靠链路:家庭端幂等排队、设备HMAC主动拉取、重复投递计数、确认/成功/失败回执和过期终态;Web设备卡片可下发诊断命令。 -- 固件安全发布与管理页:Ed25519受信公钥、浏览器私有直传、大小与SHA-256校验、签名清单验证、确定性灰度、可靠命令、短期下载、暂停、已验证版本回滚,以及逐设备下载/安装/确认进度。 +- 家庭端及维护中心固件安全发布与OTA管理:租户级/家庭级Ed25519受信公钥、浏览器私有直传、大小与SHA-256校验、签名清单验证、兼容设备确定性灰度、可靠命令、设备与维护人员短期下载、暂停、已验证版本回滚,以及逐设备下载/安装/确认进度。 - 双轨录音媒体链路:near/far分片清单幂等、15分钟单对象直传、Outbox可靠校验、WAV PCM16容器/时长/大小/SHA-256核验、延迟删除本地密文及设备回执,以及PLAY/DOWNLOAD独立授权、授权列表/撤销、React双轨播放下载、5分钟私有访问和成功/拒绝审计。 - 联调问题闭环:按客户组织记录问题现象、严重级别、设备/通话/请求编号、责任方、根因、修复版本和脱敏回归证据;未关闭HIGH/CRITICAL形成发布阻断汇总,创建幂等、更新强ETag并全程审计。 - 认证SSE状态事件流:业务事务内生成最小事件,15分钟`Last-Event-ID`续传、活动家庭成员过滤、跨租户游标拒绝、重复事件抑制、设备上线去重/超时离线、React自动重连和2秒合并刷新,避免连续事件放大为整页请求风暴。 @@ -119,6 +119,8 @@ Web镜像在容器启动时根据`WEB_OIDC_AUTHORITY`和`WEB_OIDC_CLIENT_ID`生 真实火山RTC模式由两组独立开关启用:API设置`RTC_TOKEN_ISSUER=volcengine`、`RTC_APP_ID`和`RTC_APP_KEY`,Worker设置`VOICE_PROVIDER=volcengine`、`VOLC_ACCESS_KEY_ID`、`VOLC_SECRET_ACCESS_KEY`、区域、接口版本以及`VOLC_VOICE_CHAT_TEMPLATE_FILE`。生产模板采用`voice-chat-template-bundle-v1`,分别提供`s2s_pure`、`s2s_hybrid`和`cascade`三个完整VoiceChat `Config/AgentConfig`;平台按通话冻结模式选择模板,再覆盖AppId、RoomId、TaskId、目标设备用户、智能体用户,并追加本次已授权且脱敏的背景摘要。级联模式写入`LLMConfig.SystemMessages`,纯端到端模式写入`S2SConfig.ProviderParams.dialog.system_role`。可从[模板示例](deploy/volcengine/voice-chat-template.example.json)复制结构并按照已开通的官方能力替换每个完整`Config`对象;不要把AppKey、AK/SK、ASR/TTS Token提交到仓库。 +火山硬件对话智能体使用独立的`volc_hardware_agent`运行时。Instance ID、Product Key、默认Bot ID、`high_quality`/`low_load`传输方式和最低SDK版本是平台持有的产品级配置,创建设备时只选择运行时,不再填写Bot。设备首次调用`/device/v3/hardware-agent-credentials:provision`时,平台自动建立该产品默认绑定,并使用KMS/Secret中的Product Secret代设备调用火山DynamicRegister;火山从产品额度中分配License并返回本设备的一机一密,License本身不下发设备。设备安全保存Device Secret后进入`ACTIVE`。每次聊天再调用`/device/v3/agent-sessions`取得默认Bot和本次冻结的人设、近期摘要、长期记忆与授权上下文,并通过火山SDK或标准WebSocket动态注入。平台只有在设备证明`session.updated`已应用且Bot、传输方式、配置摘要完全匹配后才激活通话;设备不具备近/远端双路PCM能力时,本次留存自动降级为仅文本。设备必须持续上报v3事件和最终字幕,并执行事件响应中的`stop_session`。完整时序、重试和凭据边界见[设备集成说明](docs/device-integration.md#115-火山硬件对话智能体-v3-控制面)。 + 设备HMAC密钥保存在`device_credential`表中,并使用AES-256-GCM和每条记录独立随机nonce加密;数据库不保存明文。API通过`DEVICE_CREDENTIAL_MASTER_KEY`(64位十六进制、解码后32字节)解密验签,该主密钥必须由Docker/Kubernetes Secret或云KMS CSI独立托管,不能进入数据库。`DEVICE_LOCAL_SECRET`只用于把既有演示设备凭据一次性导入数据库,完成迁移后可从运行环境移除。 数据库连接串、Redis、RTC AppKey、火山AK/SK、方舟API Key、对象存储凭据、回调/告警签名等敏感配置同时支持`NAME_FILE=/run/secrets/name`。文件路径必须是容器内绝对路径、目标必须是普通文件且不超过64KiB;读取时只去掉末尾换行,不在错误中打印内容。`NAME`与`NAME_FILE`同时存在会拒绝启动,防止轮换时仍意外使用旧的明文环境变量。 diff --git a/compose.yaml b/compose.yaml index 6fb5633..0daca24 100644 --- a/compose.yaml +++ b/compose.yaml @@ -55,6 +55,9 @@ x-backend-environment: &backend-environment VOLC_RTC_REGION: ${VOLC_RTC_REGION:-cn-north-1} VOLC_RTC_API_VERSION: ${VOLC_RTC_API_VERSION:-2024-12-01} VOLC_VOICE_CHAT_TEMPLATE_FILE: ${VOLC_VOICE_CHAT_TEMPLATE_FILE:-} + VOLC_HARDWARE_AGENT_BOT_ID: ${VOLC_HARDWARE_AGENT_BOT_ID:-local-hardware-agent-bot} + VOLC_HARDWARE_AGENT_TRANSPORT: ${VOLC_HARDWARE_AGENT_TRANSPORT:-high_quality} + VOLC_HARDWARE_AGENT_MINIMUM_SDK_VERSION: ${VOLC_HARDWARE_AGENT_MINIMUM_SDK_VERSION:-1.0.0} PROVIDER_FAILURE_THRESHOLD: "3" PROVIDER_CIRCUIT_COOLDOWN: 1m RECOVERY_SCAN_INTERVAL: 5s @@ -83,6 +86,7 @@ x-backend-build: &backend-build VERSION: ${PLATFORM_BUILD_VERSION:-dev} COMMIT: ${PLATFORM_BUILD_COMMIT:-unknown} BUILD_TIME: ${PLATFORM_BUILD_TIME:-unknown} + GOPROXY: ${GOPROXY:-https://proxy.golang.org,direct} x-backend-dependencies: &backend-dependencies postgres: @@ -239,6 +243,14 @@ services: HTTP_ADDR: 0.0.0.0:8080 DEVICE_LOCAL_SECRET: local-simulated-device-secret-32-bytes-minimum RTC_TOKEN_SIGNING_SECRET: local-simulated-rtc-token-secret-32-bytes-minimum + VOLC_HARDWARE_PROVISIONING_ENABLED: ${VOLC_HARDWARE_PROVISIONING_ENABLED:-false} + VOLC_HARDWARE_INSTANCE_ID: ${VOLC_HARDWARE_INSTANCE_ID:-} + VOLC_HARDWARE_PRODUCT_KEY: ${VOLC_HARDWARE_PRODUCT_KEY:-} + VOLC_HARDWARE_PRODUCT_SECRET: ${VOLC_HARDWARE_PRODUCT_SECRET:-} + VOLC_HARDWARE_DEVICE_CREDENTIAL_MASTER_KEY: ${VOLC_HARDWARE_DEVICE_CREDENTIAL_MASTER_KEY:-} + VOLC_HARDWARE_REGISTER_URL: ${VOLC_HARDWARE_REGISTER_URL:-https://iot-cn-shanghai.iot.volces.com/2021-12-14/DynamicRegister?Action=DynamicRegister&Version=2021-12-14} + VOLC_HARDWARE_REGISTER_TIMEOUT: ${VOLC_HARDWARE_REGISTER_TIMEOUT:-10s} + VOLC_HARDWARE_DEVICE_NAME_PREFIX: ${VOLC_HARDWARE_DEVICE_NAME_PREFIX:-dt-} ports: - "${API_PORT:-58080}:8080" depends_on: *backend-dependencies @@ -332,6 +344,7 @@ services: VERSION: ${PLATFORM_BUILD_VERSION:-dev} COMMIT: ${PLATFORM_BUILD_COMMIT:-unknown} BUILD_TIME: ${PLATFORM_BUILD_TIME:-unknown} + NPM_CONFIG_REGISTRY: ${NPM_CONFIG_REGISTRY:-https://registry.npmjs.org} environment: WEB_OIDC_AUTHORITY: ${WEB_OIDC_AUTHORITY:-http://localhost:58180/realms/voice-local} WEB_OIDC_CLIENT_ID: ${WEB_OIDC_CLIENT_ID:-voice-platform-web} diff --git a/deploy/backend.Dockerfile b/deploy/backend.Dockerfile index 3b18955..78b706b 100644 --- a/deploy/backend.Dockerfile +++ b/deploy/backend.Dockerfile @@ -1,4 +1,5 @@ FROM golang:1.26.6-alpine AS build +ARG GOPROXY=https://proxy.golang.org,direct WORKDIR /src COPY go.mod go.sum ./ RUN go mod download diff --git a/deploy/kubernetes/base/configmap.yaml b/deploy/kubernetes/base/configmap.yaml index 9c15e24..3a5f5dd 100644 --- a/deploy/kubernetes/base/configmap.yaml +++ b/deploy/kubernetes/base/configmap.yaml @@ -50,6 +50,17 @@ data: VOLC_RTC_BASE_URL: https://rtc.volcengineapi.com VOLC_RTC_REGION: cn-north-1 VOLC_RTC_API_VERSION: "2024-12-01" + # Platform-owned product defaults. Devices receive these only through the + # signed provisioning/session control plane and cannot override them. + VOLC_HARDWARE_AGENT_BOT_ID: "replace-with-product-default-bot-id" + VOLC_HARDWARE_AGENT_TRANSPORT: "high_quality" + VOLC_HARDWARE_AGENT_MINIMUM_SDK_VERSION: "1.0.0" + VOLC_HARDWARE_PROVISIONING_ENABLED: "false" + VOLC_HARDWARE_INSTANCE_ID: "" + VOLC_HARDWARE_PRODUCT_KEY: "" + VOLC_HARDWARE_REGISTER_URL: https://iot-cn-shanghai.iot.volces.com/2021-12-14/DynamicRegister?Action=DynamicRegister&Version=2021-12-14 + VOLC_HARDWARE_REGISTER_TIMEOUT: 10s + VOLC_HARDWARE_DEVICE_NAME_PREFIX: "dt-" CONTENT_GENERATOR: ark CONTENT_SAFETY_PROVIDER: managed_http CONTENT_SAFETY_ENDPOINT: https://content-safety.example.invalid/v1/classify diff --git a/deploy/kubernetes/examples/secret-provider-classes.example.yaml b/deploy/kubernetes/examples/secret-provider-classes.example.yaml index 314a6c9..7e4f96e 100644 --- a/deploy/kubernetes/examples/secret-provider-classes.example.yaml +++ b/deploy/kubernetes/examples/secret-provider-classes.example.yaml @@ -19,7 +19,7 @@ metadata: spec: provider: replace-with-installed-provider parameters: - objects: replace-with-provider-specific-rtc-app-key-cursor-share-and-device-credential-master-key + objects: replace-with-provider-specific-rtc-app-key-cursor-share-device-credential-master-key-volc-hardware-product-secret-and-volc-device-credential-master-key --- apiVersion: secrets-store.csi.x-k8s.io/v1 kind: SecretProviderClass diff --git a/deploy/kubernetes/overlays/production-csi/csi-volumes.yaml b/deploy/kubernetes/overlays/production-csi/csi-volumes.yaml index 95d9c8a..82d3af3 100644 --- a/deploy/kubernetes/overlays/production-csi/csi-volumes.yaml +++ b/deploy/kubernetes/overlays/production-csi/csi-volumes.yaml @@ -6,6 +6,13 @@ metadata: spec: template: spec: + securityContext: + runAsNonRoot: true + runAsUser: 65532 + runAsGroup: 65532 + fsGroup: 65532 + seccompProfile: + type: RuntimeDefault volumes: - $patch: replace - name: common-secrets @@ -35,6 +42,13 @@ metadata: spec: template: spec: + securityContext: + runAsNonRoot: true + runAsUser: 65532 + runAsGroup: 65532 + fsGroup: 65532 + seccompProfile: + type: RuntimeDefault volumes: - $patch: replace - name: common-secrets @@ -58,6 +72,13 @@ metadata: spec: template: spec: + securityContext: + runAsNonRoot: true + runAsUser: 65532 + runAsGroup: 65532 + fsGroup: 65532 + seccompProfile: + type: RuntimeDefault volumes: - $patch: replace - name: common-secrets diff --git a/deploy/migrate.Dockerfile b/deploy/migrate.Dockerfile index 979d085..9aca042 100644 --- a/deploy/migrate.Dockerfile +++ b/deploy/migrate.Dockerfile @@ -1,3 +1,4 @@ FROM migrate/migrate:v4.18.3 -COPY migrations /migrations +COPY --chown=65532:65532 migrations /migrations +USER 65532:65532 diff --git a/deploy/server/README.md b/deploy/server/README.md index b26b515..e020da1 100644 --- a/deploy/server/README.md +++ b/deploy/server/README.md @@ -2,6 +2,8 @@ 该配置用于单机公网演示,不是生产验收配置。只向公网发布HTTP 80和HTTPS 443;PostgreSQL、Redis、MinIO、Keycloak内部端口以及API/Worker端口均不映射到宿主机。当前阿里云演示机使用Let's Encrypt短期IP证书,`certbot-ip-renew.timer`负责自动续期。单机备份覆盖PostgreSQL、Redis、MinIO对象和当前Keycloak数据目录,并执行数据库级隔离恢复校验;五分钟健康检查覆盖部署模式、公网入口、OIDC、容器、模拟服务、备份、外部告警、证书、定时任务和磁盘。跨故障域备份、真实告警接收器、真实RTC、模型、内容安全和云KMS仍按阶段8验收清单执行。 +无法直接从Git拉取或需要冻结当前工作树时,可先在开发机执行`powershell -NoProfile -ExecutionPolicy Bypass -File .\scripts\build-server-deployment-bundle.ps1 -Version 0.76.0-rc.hardware1`,再用`powershell -NoProfile -ExecutionPolicy Bypass -File .\scripts\verify-server-deployment-bundle.ps1 -Archive .\.deploy-artifacts\data-terminal-cloud-0.76.0-rc.hardware1.zip`验证。归档包含`deploy-manifest.json`和`SHA256SUMS`,不会打包`.env`、设备/供应商密钥、本地运行目录、`node_modules`或构建缓存。上传服务器后必须再次核对归档侧车`.sha256`和包内清单,再覆盖候选目录;服务器现有`deploy/server/.env`及`/etc/data-terminal-cloud`密钥目录不得从归档替换。 + 服务器至少需要Docker Engine 29与Compose 2.40。首次部署先生成权限为600的服务器专用配置: ```sh @@ -30,6 +32,15 @@ docker compose -f compose.yaml -f deploy/server/compose.override.yaml \ 需要在演示机接入真实RTC、豆包语音和方舟摘要时,把凭据文件以`0600`权限放入`/etc/data-terminal-cloud/voice-runtime`,并在`deploy/server/.env`设置`RTC_TOKEN_ISSUER=volcengine`、`VOICE_PROVIDER=volcengine`、`RTC_APP_ID`、`CONTENT_GENERATOR`和`ARK_CONTENT_MODEL`。Compose只读挂载该目录,敏感值不进入源码、镜像或环境文件。 +切换火山硬件对话智能体时,维护中心逐台只需把`voice_runtime`设为`volc_hardware_agent`,不填写设备级Bot。平台代设备动态注册时,在`deploy/server/.env`设置`VOLC_HARDWARE_PROVISIONING_ENABLED=true`、`VOLC_HARDWARE_INSTANCE_ID`、`VOLC_HARDWARE_PRODUCT_KEY`、`VOLC_HARDWARE_AGENT_BOT_ID`、`VOLC_HARDWARE_AGENT_TRANSPORT`、`VOLC_HARDWARE_AGENT_MINIMUM_SDK_VERSION`、`VOLC_HARDWARE_PRODUCT_SECRET_FILE=/run/secrets/voice-runtime/volc-hardware-product-secret`和独立随机的`VOLC_HARDWARE_DEVICE_CREDENTIAL_MASTER_KEY`;将Product Secret以单行、`0600`权限写入`/etc/data-terminal-cloud/voice-runtime/volc-hardware-product-secret`。Product Secret不得进入`.env`、源码、镜像、数据库或设备。设备首次预配时,平台根据这些产品默认值自动创建火山绑定并调用DynamicRegister;火山从产品额度中分配License,平台只加密保存并下发本设备的一机一密Device Secret,不下发License明文。发布前必须验证平台代理注册、默认Bot绑定、每次会话上下文注入、`session.updated`确认后激活、`session_updated`存活上报、`stop_session`执行和最终字幕补传。 + +```sh +sudo install -d -m 0700 /etc/data-terminal-cloud/voice-runtime +sudo sh -c 'umask 077; read -r product_secret; printf "%s" "$product_secret" > /etc/data-terminal-cloud/voice-runtime/volc-hardware-product-secret' +``` + +上面的`read`从终端读取,不要把真实Product Secret写进Shell历史。启用前确认默认Bot关联的产品与`VOLC_HARDWARE_PRODUCT_KEY`完全一致;Device Name由`dt-`加平台设备UUID稳定生成。密钥文件或产品默认配置缺失、格式错误时API拒绝启动,未启用时凭据接口明确返回`HARDWARE_AGENT_PROVISIONING_UNAVAILABLE`。 + 设备HMAC密钥由`device_credential`表以AES-256-GCM密文保存。`deploy/server/.env`中的`DEVICE_CREDENTIAL_MASTER_KEY`只负责数据库密文的认证解密,不得写入数据库或设备;既有`DEVICE_LOCAL_SECRET`只用于首次部署升级时导入两台演示设备,确认数据库凭据记录完整后可移除。新增、轮换和吊销设备密钥使用后端镜像内的`/app/device-credential`维护命令,生成的设备明文密钥只显示一次。 通话内记忆工具只通过网关精确开放`/internal/v1/tools/memory-search`,请求必须携带平台为当前通话签发的短期任务令牌。`MEMORY_TOOL_SIGNING_SECRET`必须是服务器独立随机密钥,不得与游标签名、分享签名或RTC密钥复用;网关不会开放其他`/internal`路径。 diff --git a/deploy/server/bootstrap-secrets.sh b/deploy/server/bootstrap-secrets.sh index f58aea0..86a0705 100644 --- a/deploy/server/bootstrap-secrets.sh +++ b/deploy/server/bootstrap-secrets.sh @@ -34,6 +34,7 @@ umask 077 printf 'KEYCLOAK_ADMIN_PASSWORD=%s\n' "$(openssl rand -hex 24)" printf 'DEVICE_LOCAL_SECRET=%s\n' "$(openssl rand -hex 32)" printf 'DEVICE_CREDENTIAL_MASTER_KEY=%s\n' "$(openssl rand -hex 32)" + printf 'VOLC_HARDWARE_DEVICE_CREDENTIAL_MASTER_KEY=%s\n' "$(openssl rand -hex 32)" printf 'MODEL_CREDENTIAL_MASTER_KEY=%s\n' "$(openssl rand -hex 32)" printf 'RTC_TOKEN_SIGNING_SECRET=%s\n' "$(openssl rand -hex 32)" printf 'CURSOR_SIGNING_SECRET=%s\n' "$(openssl rand -hex 32)" diff --git a/deploy/server/compose.override.yaml b/deploy/server/compose.override.yaml index eac5696..7f8f620 100644 --- a/deploy/server/compose.override.yaml +++ b/deploy/server/compose.override.yaml @@ -102,6 +102,16 @@ services: RTC_APP_ID: ${RTC_APP_ID:-local-public-rtc-app} RTC_APP_KEY: "" RTC_APP_KEY_FILE: ${RTC_APP_KEY_FILE:-/run/secrets/voice-runtime/rtc-app-key} + VOLC_HARDWARE_PROVISIONING_ENABLED: ${VOLC_HARDWARE_PROVISIONING_ENABLED:-false} + VOLC_HARDWARE_INSTANCE_ID: ${VOLC_HARDWARE_INSTANCE_ID:-} + VOLC_HARDWARE_PRODUCT_KEY: ${VOLC_HARDWARE_PRODUCT_KEY:-} + VOLC_HARDWARE_AGENT_BOT_ID: ${VOLC_HARDWARE_AGENT_BOT_ID:-} + VOLC_HARDWARE_AGENT_TRANSPORT: ${VOLC_HARDWARE_AGENT_TRANSPORT:-low_load} + VOLC_HARDWARE_AGENT_MINIMUM_SDK_VERSION: ${VOLC_HARDWARE_AGENT_MINIMUM_SDK_VERSION:-1.0.0} + VOLC_HARDWARE_PRODUCT_SECRET: "" + VOLC_HARDWARE_PRODUCT_SECRET_FILE: ${VOLC_HARDWARE_PRODUCT_SECRET_FILE:-} + VOLC_HARDWARE_DEVICE_CREDENTIAL_MASTER_KEY: ${VOLC_HARDWARE_DEVICE_CREDENTIAL_MASTER_KEY:-} + VOLC_HARDWARE_DEVICE_NAME_PREFIX: ${VOLC_HARDWARE_DEVICE_NAME_PREFIX:-dt-} volumes: - /etc/data-terminal-cloud/voice-runtime:/run/secrets/voice-runtime:ro restart: unless-stopped diff --git a/deploy/web.Dockerfile b/deploy/web.Dockerfile index 10a69a7..1c7d710 100644 --- a/deploy/web.Dockerfile +++ b/deploy/web.Dockerfile @@ -1,4 +1,5 @@ FROM node:24-alpine AS build +ARG NPM_CONFIG_REGISTRY=https://registry.npmjs.org WORKDIR /src COPY web/package*.json ./ RUN npm ci @@ -15,6 +16,10 @@ LABEL org.opencontainers.image.version="${VERSION}" \ org.opencontainers.image.created="${BUILD_TIME}" USER root RUN apk upgrade --no-cache +# The image's IPv6 helper shells out to `apk manifest nginx` on every start. +# On small production hosts that command can block even after the package +# database is healthy. This deployment owns default.conf and does not need it. +RUN rm -f /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh COPY --chown=101:101 deploy/web.nginx.conf /etc/nginx/conf.d/default.conf COPY --chmod=755 --chown=101:101 deploy/web-runtime-config.sh /docker-entrypoint.d/40-runtime-config.sh COPY --chown=101:101 --from=build /src/dist /usr/share/nginx/html diff --git a/device-simulator-windows/README.md b/device-simulator-windows/README.md index 8a34da9..89a8863 100644 --- a/device-simulator-windows/README.md +++ b/device-simulator-windows/README.md @@ -1,8 +1,45 @@ -# Windows 语音终端模拟器 +# Windows 硬件对话智能体测试程序 -该程序使用Electron和火山引擎Web RTC SDK模拟一台真实语音终端。它采集Windows麦克风、加入平台分配的RTC房间、发布本地音频、订阅并播放豆包智能体音频,并通过设备v2接口完成激活、状态等待、心跳、Token续签和结束通话。进房后还会启动RTC实时字幕,收集长者与智能体的最终话轮并通过现有`turns:batch`设备接口可靠上传;字幕未成功启动时拒绝继续激活通话,避免生成没有聊天记录的完成通话。 +这是方案 1 的 Windows 端到端验收工具。默认页面不再使用平台代管的 RTC/StartVoiceChat,而是按火山“硬件对话智能体低负载标准协议”直接连接 `wss://ai-gateway.vei.volces.com/v1/realtime`。传统 RTC 模拟器仍保留在页面右上角的“传统 RTC 测试页”。 -创建通话返回的隐私策略允许原录音时,模拟器同时通过RTC原始音频帧回调采集长者`near`和智能体`far`轨道。结束通话后自动封装为单声道WAV PCM16、按平台限制分片、申请短期对象地址直传,并等待后台完整性与容器校验完成。家庭工作台的通话记录会标记“可听聊天语音”,获得独立PLAY授权的账号可在详情中选择“听长者语音”或“听智能体语音”。 +## 实际覆盖的流程 + +1. 使用平台设备 ID、时间戳、随机数、正文摘要和 HMAC-SHA256 调用设备 v2/v3 接口。 +2. 调用平台 `/device/v3/hardware-agent-credentials:provision`;平台自动应用产品级Instance ID、Product Key、默认Bot、传输和最低SDK配置,并使用KMS/Secret挂载的 Product Secret 代设备调用 DynamicRegister。 +3. 火山在动态注册时从产品额度中分配 License;平台只把本设备 Device Secret 下发给设备,Windows 使用 Safe Storage 加密保存后重新拉取配置版本并进入 `ACTIVE`。 +4. 创建 `/device/v3/agent-sessions`,从平台取得产品默认 Bot ID、`low_load` 传输方案、本次冻结的人设、背景、最近摘要、确认记忆和授权范围。 +5. 按火山规定的请求头签名建立 WebSocket,等待 `session.created`,提交本次 `session.update`,收到 `session.updated` 后才向平台回报 `session_started`并开放麦克风。 +6. Windows 麦克风被转换为 PCM16、16kHz、单声道、20ms 分片,通过 `input_audio_buffer.append` 实时上行;`response.audio.delta` 按火山 low-load WebSocket SDK 的 PCM16、16kHz、单声道格式实时播放。 +7. 消费服务端 VAD、用户最终识别、智能体最终字幕、Function Calling 和错误事件;双方最终话轮通过 v3 `turns:batch` 幂等上传。 +8. 智能体触发 `memory_search`、`search_memory` 或 `retrieve_memory` 时,程序使用平台签发的当前会话短期令牌调用同源记忆工具,再把结果作为 `function_call_output` 返回火山。 +9. 周期提交 `session_updated`。平台返回 `stop_session` 时立即关麦、补传字幕、关闭火山连接并回报 `session_ended`。 +10. 页面显示 13 项验收门禁,可导出不包含任何密钥的 JSON 报告。 + +## 使用前配置 + +平台维护中心只需把测试设备配置为: + +- `voice_runtime = volc_hardware_agent` + +平台部署需配置并关联同一火山产品和 Bot: + +- Instance ID +- Product Key +- 默认 Bot ID +- 传输方式 `low_load` +- 最低 SDK 版本不高于 `1.1.0-windows-standard-protocol` +- Product Secret(只进入平台密钥挂载,不进入Windows程序) +- Device Name 由平台按设备ID稳定生成,或使用设备绑定中预先登记的名称 +- 可用的硬件对话智能体 License + +Windows程序不提供 Product Secret、Device Secret 或 Bot ID 输入框。Bot ID、Instance ID、Product Key 和 Device Name 均来自平台;设备不能覆盖产品默认路由。 + +开始测试前还需按控制台中的 Bot 模型选择“智能体模型类型”: + +- 通用 LLM:使用官方 `config.LLMConfig.SystemMessages` 注入平台冻结的人设、背景与授权上下文; +- 豆包端到端语音:使用官方 `config.S2SConfig.ProviderParams.dialog` 注入 `system_role`、`speaking_style` 和 `dialog_id`。 + +两种模式都使用官方 `session.object/config/agent_config/input_audio_format/output_audio_format` 报文结构。字段依据火山[初始化会话配置](https://www.volcengine.com/docs/6348/2139862?lang=zh)与[标准协议自定义接入](https://www.volcengine.com/docs/6348/1904602?lang=zh)文档。 ## 构建 @@ -12,36 +49,70 @@ npm install npm run build ``` -输出:`release/voice-device-simulator-0.1.0-windows-x64.exe`。 +输出文件: -连接公网测试服务器时,可以直接双击EXE,也可以使用`launch-cloud-device.ps1`启动。Electron主进程优先读取启动脚本传入的临时环境变量,否则从当前Windows用户的`%LOCALAPPDATA%\DataTerminalVoiceDevice\server-device-secret`读取设备签名密钥。密钥不写入源码、安装包或浏览器存储,也不会回传到渲染进程;输入框留空并显示“已从Windows安全凭据文件加载”即表示可直接检测。 - -## 自动连续性验证 - -本地回归时可让模拟器自动启动通话、播放一段测试音频,并把双方最终字幕写入JSON结果文件。该入口只接受本机文件和回环地址,不影响正常交互使用: - -```powershell -$env:SIMULATOR_AUTO_TEST = '1' -$env:SIMULATOR_AUTOMATED_AUDIO_PATH = 'C:\path\to\question.wav' -$env:SIMULATOR_AUTO_RESULT_PATH = 'C:\path\to\result.json' -& .\release\voice-device-simulator-0.1.0-windows-x64.exe +```text +release/hardware-agent-tester-0.3.5-windows-x64.exe ``` -测试会等待长者与智能体的最终字幕,结束通话后退出;结果文件包含通话ID、最终话轮和通过/失败状态。测试音频支持WAV、MP3和OGG,大小不得超过10 MB。 +当前测试包未配置商业代码签名证书,Windows 可能显示“未知发布者”。交付前可用同目录 SHA-256 文件校验完整性;正式面向终端用户分发时应由甲方或发布主体使用其代码签名证书签名。 + +`npm run build` 会依次执行协议单测、TypeScript 检查、双页面构建和 Windows x64 便携包封装。 + +## 本地完整自动回归 + +先启动仓库根目录的 Compose 开发环境,然后运行: + +```powershell +Set-Location .\device-simulator-windows +pwsh -NoProfile -File .\test\run-hardware-e2e.ps1 +pwsh -NoProfile -File .\test\run-hardware-e2e.ps1 -ModelMode s2s +pwsh -NoProfile -File .\test\run-hardware-e2e.ps1 -ExecutablePath .\release\hardware-agent-tester-0.3.5-windows-x64.exe +``` + +第一条验证通用 LLM 报文,第二条验证端到端语音报文。回归会临时创建 `low_load` 设备绑定,启动一个校验真实火山签名和事件格式的本机 WebSocket 网关,再启动实际 Electron 窗口。测试只有在以下证据全部成立时才返回 PASS: + +- 动态上下文确认后才开放麦克风; +- PCM 上行和音频下行均发生; +- 用户与智能体各产生一条最终字幕并写入 PostgreSQL; +- 平台下发停止状态,Windows 程序通过下一次 `session_updated` 收到 `stop_session`; +- 会话、硬件会话均为 `COMPLETED`; +- SaaS `conversation.started` 和 `conversation.completed` 事件均已产生; +- 页面 13 项验收门禁全部通过。 + +测试结束会删除临时会话、事件和设备绑定,不污染日常开发数据。 ## 安全边界 -- RTC AppKey、火山AK/SK和豆包Access Token只存在本地后端的受保护文件中; -- 模拟器安装包只接收平台签发的短期RTC Token; -- 设备签名密钥只驻留Electron主进程内存,不写入浏览器存储; -- 当前测试构建只允许设备API地址指向`localhost`、`127.0.0.1`或固定测试服务器`https://121.41.46.238`,避免误连其他环境; -- 关闭软件或结束通话时停止音频采集并销毁RTC引擎。 -- 未取得`record_near`或`record_far`授权的轨道不会注册PCM回调;`upload_allowed=false`时不会采集或上传任何原录音。 +- Product Secret 只存在于平台KMS/Secret挂载和注册调用内存,不到达设备,不写入数据库明文、日志或报告。 +- 平台返回的本设备Device Secret使用 Electron `safeStorage` 加密后保存在当前 Windows 用户目录,不进入源码和安装包;License由火山产品侧分配,不下发Windows程序。 +- 平台设备密钥只驻留 Electron 主进程,渲染页面不能读取已加载的明文。 +- 记忆工具地址必须与配置的平台 API 同源,防止短期令牌被发送给第三方地址。 +- 默认只允许连接火山官方硬件网关;本机 `ws://127.0.0.1` 仅供自动回归。 +- 平台确认 `session.updated` 前不会启动麦克风;收到 `stop_session` 后不会继续发送音频。 +- 导出的报告包含设备标识、Bot、配置摘要、事件和字幕,但不包含 Product Secret、Device Secret、平台签名密钥或记忆工具令牌。 +- 自动回归通过仅在`SIMULATOR_AUTO_TEST=1`时生效的进程环境注入mock一机一密,不写入正式Windows凭据缓存。 -## 实时字幕前提 +## 真实验收与本地模拟的区别 -测试所用RTC AppID必须在火山RTC控制台开启“实时字幕”和“流式语音识别”。流式语音识别凭据来自豆包语音控制台创建的“流式语音识别”应用,包括APP ID、Access Token和Cluster ID;“音视频字幕生成”类型不能替代该应用。未开通时程序会显示`ServiceAccessDenied`并清理本次未激活通话。 +### 0.3.5 播放速度修复 -最终字幕会在程序的“实时字幕与上传”区域显示。每条最终字幕使用通话内稳定键和严格递增序号上传;网络失败时留在内存队列,并在心跳及结束通话前重试。存在未上传字幕时,程序不会结束并丢弃当前通话。 +- 根据火山 low-load WebSocket SDK 示例,将智能体下行 PCM 的源采样率从错误的 24kHz 修正为 16kHz,避免 1.5 倍速和音调升高。 +- Windows 输出设备不是 16kHz 时,由 AudioWorklet 按实际系统输出采样率连续重采样,不改变语速和音高。 -完整本地启动顺序见[本地真实RTC联调说明](../deploy/local-rtc/README.md)。 +### 0.3.4 连续音频播放修复 + +- 火山返回的 `pcm_s16le/16kHz/mono` 音频进入常驻 AudioWorklet 环形缓冲,不再按 WebSocket 分片创建独立播放器。 +- 默认预缓冲 120ms,网络瞬时抖动时自动重新缓冲,减少断音、金属音和语速漂移。 +- 连续处理跨分片的奇数字节,并在开始、结束和断流时做短淡入淡出以避免爆音。 +- 测试报告新增播放分片数、采样数和欠载次数。 + +### 0.3.3 失败会话修复 + +- 火山 `error` 事件与连接 Promise 的异常共用一个失败清理任务,同一轮仅提交一次 `session_failed`,保留首次错误。 +- 失败清理期间禁止发送文字或重复结束;延迟到达的事件不再恢复“通话中”。失败后重新检测即可开始新一轮。 +- 新通话清空上一轮验收结果、错误和计时;失败清理的关闭不再标为正常关闭。 +- `npm test` 包含执行实际渲染器代码的并发失败回归测试,不会连接线上平台或消耗火山额度。 +- 此修复不代表火山 `internal_error: StartVoiceChat failed` 的上游原因已解决。 + +本地自动回归证明 Windows 程序、平台控制面、数据库、PCM 管道和协议状态机完整可执行。最终真实火山验收仍必须使用实际 Instance/Product/Device/Bot/License 凭据,确认火山返回真实 ASR、LLM、TTS 音频;程序不会把本机模拟网关的 PASS 冒充真实供应商验收。 diff --git a/device-simulator-windows/electron/hardware-agent-client.cjs b/device-simulator-windows/electron/hardware-agent-client.cjs new file mode 100644 index 0000000..039daaf --- /dev/null +++ b/device-simulator-windows/electron/hardware-agent-client.cjs @@ -0,0 +1,252 @@ +const crypto = require("node:crypto"); +const WebSocket = require("ws"); + +const DEFAULT_GATEWAY_URL = "wss://ai-gateway.vei.volces.com/v1/realtime"; + +function requiredText(value, name, maximum = 512) { + const normalized = String(value || "").trim(); + if (!normalized || normalized.length > maximum) throw new Error(`${name}不能为空或过长`); + return normalized; +} + +function validateDeviceName(value) { + const name = requiredText(value, "火山设备名称", 128); + if (!/^[a-zA-Z0-9_\-.:@]{1,128}$/.test(name)) throw new Error("火山设备名称格式不正确"); + return name; +} + +function hmacBase64(secret, content) { + return crypto.createHmac("sha256", secret).update(content).digest("base64"); +} + +function websocketSignature({ authType = 1, deviceName, randomNumber, productKey, timestamp, instanceID }, deviceSecret) { + const content = `auth_type=${authType}&device_name=${deviceName}&random_num=${randomNumber}&product_key=${productKey}×tamp=${timestamp}&instance_id=${instanceID}`; + return hmacBase64(deviceSecret, content); +} + +function buildSessionPrompt(sessionConfig) { + const sections = [ + requiredText(sessionConfig.authorized_context, "授权上下文", 4000), + String(sessionConfig.system_prompt || "").trim(), + String(sessionConfig.persona_tone || "").trim() ? `对话语气:${String(sessionConfig.persona_tone).trim()}` : "", + `本次对话标识:${requiredText(sessionConfig.dialog_id, "对话标识", 200)}`, + ].filter(Boolean); + return sections.join("\n\n"); +} + +function buildSessionUpdate(sessionConfig, eventID, modelMode = "llm") { + const normalizedMode = String(modelMode || "llm").trim().toLowerCase(); + if (!new Set(["llm", "s2s"]).has(normalizedMode)) throw new Error("智能体模型类型必须为 llm 或 s2s"); + const dialogID = requiredText(sessionConfig.dialog_id, "对话标识", 200); + const systemPrompt = buildSessionPrompt(sessionConfig); + const config = { + ASRConfig: { TurnDetectionMode: 0 }, + }; + if (normalizedMode === "s2s") { + config.S2SConfig = { + ProviderParams: { + dialog: { + system_role: systemPrompt, + speaking_style: String(sessionConfig.persona_tone || "").trim(), + dialog_id: dialogID, + }, + }, + }; + } else { + config.LLMConfig = { SystemMessages: [systemPrompt] }; + } + return { + event_id: eventID, + type: "session.update", + session: { + object: "realtime.session", + model: "", + input_audio_format: "pcm16", + output_audio_format: "pcm16", + config, + agent_config: { EnableConversationStateCallback: false }, + }, + }; +} + +function normalizeProviderError(event) { + const code = String(event?.error?.code || event?.error?.type || "PROVIDER_ERROR"); + const message = String(event?.error?.message || "火山硬件智能体返回错误"); + return new Error(`${code}: ${message}`); +} + +class HardwareAgentClient { + constructor(options = {}) { + this.WebSocketImpl = options.WebSocketImpl || WebSocket; + this.gatewayURL = options.gatewayURL || DEFAULT_GATEWAY_URL; + this.emit = typeof options.emit === "function" ? options.emit : () => {}; + this.socket = undefined; + this.sessionCreated = undefined; + this.providerConnectionRef = ""; + this.pendingCalls = new Map(); + this.pingTimer = undefined; + this.connectionPromise = undefined; + this.sessionUpdateSent = false; + } + + async connect(input) { + if (this.socket) throw new Error("火山硬件智能体连接已经存在"); + const instanceID = requiredText(input.instanceID, "实例 ID", 200); + const productKey = requiredText(input.productKey, "Product Key", 200); + const deviceName = validateDeviceName(input.deviceName); + const deviceSecret = requiredText(input.deviceSecret, "Device Secret", 4096); + const hardwareID = requiredText(input.hardwareID, "Hardware ID", 200); + const botID = requiredText(input.botID, "Bot ID", 200); + const randomNumber = crypto.randomInt(1, 2_147_483_647); + const timestamp = Math.floor(Date.now() / 1000); + const signature = websocketSignature({ deviceName, randomNumber, productKey, timestamp, instanceID }, deviceSecret); + const target = new URL(this.gatewayURL); + target.searchParams.set("bot", botID); + target.searchParams.set("wait_for_session_update", "true"); + const headers = { + "X-Auth-Type": "1", + "X-Product-Key": productKey, + "X-Device-Name": deviceName, + "X-Random-Num": String(randomNumber), + "X-Timestamp": String(timestamp), + "X-Instance-Id": instanceID, + "X-Signature": signature, + "X-Hardware-Id": hardwareID, + }; + const socket = new this.WebSocketImpl(target.toString(), { headers, handshakeTimeout: 10_000, maxPayload: 8 * 1024 * 1024 }); + this.socket = socket; + this.connectionPromise = new Promise((resolve, reject) => { + const timeout = setTimeout(() => reject(new Error("等待火山 session.updated 超时")), 15_000); + const finish = (callback, value) => { + clearTimeout(timeout); + callback(value); + }; + socket.once("open", () => { + this.emit({ type: "transport.open" }); + // wait_for_session_update=true makes the gateway wait for the client + // configuration before it publishes the usable session. Sending only + // after session.created deadlocks until the provider-side timeout. + this.sendJSON(buildSessionUpdate(input.sessionConfig, `windows-session-update-${crypto.randomUUID()}`, input.modelMode)); + this.sessionUpdateSent = true; + }); + socket.on("message", (data) => { + let event; + try { event = JSON.parse(Buffer.isBuffer(data) ? data.toString("utf8") : String(data)); } + catch { this.emit({ type: "transport.invalid_message" }); return; } + this.handleProviderEvent(event, input.sessionConfig, input.modelMode); + // In the hardware gateway's wait_for_session_update mode, the initial + // session.created event is published only after the pre-creation + // session.update has been accepted. Some Bot versions do not emit an + // additional session.updated event for that initial configuration. + if (event.type === "session.updated" || (event.type === "session.created" && this.sessionUpdateSent)) { + const connectionRef = String(event?.session?.id || this.providerConnectionRef || event.event_id || "").trim(); + if (!connectionRef) return finish(reject, new Error("火山会话确认未提供可关联的会话编号")); + this.providerConnectionRef = connectionRef; + finish(resolve, { providerConnectionRef: connectionRef, session: event.session || {} }); + } else if (event.type === "error" && !this.providerConnectionRef) { + finish(reject, normalizeProviderError(event)); + } + }); + socket.once("unexpected-response", (_request, response) => { + finish(reject, new Error(`火山 WebSocket 鉴权失败:HTTP ${response.statusCode}`)); + }); + socket.once("error", (error) => { + this.emit({ type: "transport.error", message: error.message }); + if (!this.providerConnectionRef) finish(reject, error); + }); + socket.once("close", (code, reason) => { + this.stopPing(); + this.socket = undefined; + this.emit({ type: "transport.close", code, reason: Buffer.from(reason || "").toString("utf8") }); + if (!this.providerConnectionRef) finish(reject, new Error(`火山连接提前关闭:${code}`)); + }); + }); + try { + const result = await this.connectionPromise; + this.startPing(); + return result; + } catch (error) { + this.close(1011, "connection setup failed"); + throw error; + } + } + + handleProviderEvent(event, sessionConfig, modelMode) { + if (event.type === "session.created") { + this.sessionCreated = event.session || {}; + this.providerConnectionRef = String(event?.session?.id || event.event_id || "").trim(); + } + if (event.type === "conversation.item.created" && event?.item?.type === "function_call" && event.item.call_id) { + this.pendingCalls.set(String(event.item.call_id), String(event.item.name || "")); + } + this.emit(event); + } + + sendAudio(bytes) { + const buffer = Buffer.from(bytes); + if (!buffer.length) return; + if (buffer.length > 64 * 1024) throw new Error("单个音频分片不能超过64KiB"); + this.sendJSON({ + event_id: `windows-audio-${crypto.randomUUID()}`, + type: "input_audio_buffer.append", + audio: buffer.toString("base64"), + }); + } + + sendText(text, interruptMode = 1) { + const normalized = requiredText(text, "文本问题", 2000); + this.sendJSON({ + event_id: `windows-text-${crypto.randomUUID()}`, + type: "conversation.item.create", + item: { type: "message", role: "user", content: [{ type: "input_text", text: normalized }], interrupt_mode: interruptMode }, + }); + } + + completeFunctionCall(callID, output) { + const normalizedCallID = requiredText(callID, "函数调用编号", 200); + this.sendJSON({ + event_id: `windows-function-${crypto.randomUUID()}`, + type: "conversation.item.create", + item: { type: "function_call_output", call_id: normalizedCallID, output: JSON.stringify(output) }, + }); + this.sendJSON({ event_id: `windows-response-${crypto.randomUUID()}`, type: "response.create" }); + this.pendingCalls.delete(normalizedCallID); + } + + sendJSON(value) { + if (!this.socket || this.socket.readyState !== this.WebSocketImpl.OPEN) throw new Error("火山硬件智能体连接未就绪"); + this.socket.send(JSON.stringify(value)); + } + + startPing() { + this.stopPing(); + this.pingTimer = setInterval(() => { + try { if (this.socket?.readyState === this.WebSocketImpl.OPEN && typeof this.socket.ping === "function") this.socket.ping(); } + catch { /* close/error callbacks own recovery */ } + }, 20_000); + } + + stopPing() { + if (this.pingTimer) clearInterval(this.pingTimer); + this.pingTimer = undefined; + } + + close(code = 1000, reason = "device session ended") { + this.stopPing(); + const socket = this.socket; + this.socket = undefined; + this.providerConnectionRef = ""; + this.sessionUpdateSent = false; + this.pendingCalls.clear(); + if (!socket) return; + try { socket.close(code, reason); } catch { try { socket.terminate?.(); } catch { /* no-op */ } } + } +} + +module.exports = { + DEFAULT_GATEWAY_URL, + HardwareAgentClient, + buildSessionPrompt, + buildSessionUpdate, + websocketSignature, +}; diff --git a/device-simulator-windows/electron/hardware-agent-client.test.cjs b/device-simulator-windows/electron/hardware-agent-client.test.cjs new file mode 100644 index 0000000..ba9c897 --- /dev/null +++ b/device-simulator-windows/electron/hardware-agent-client.test.cjs @@ -0,0 +1,97 @@ +const assert = require("node:assert/strict"); +const { EventEmitter } = require("node:events"); +const test = require("node:test"); +const { + HardwareAgentClient, + buildSessionUpdate, + websocketSignature, +} = require("./hardware-agent-client.cjs"); + +test("WebSocket signatures use the documented canonical field order", () => { + const connection = websocketSignature({ authType: 1, deviceName: "device-1", randomNumber: 42, productKey: "product-1", timestamp: 1700000000, instanceID: "instance-1" }, "device-secret"); + assert.equal(connection, "6OFE1DUZzRgNy0o5IzZSmqKw3SUP4s3GxK2YHe8phfo="); +}); + +test("LLM session update uses the official Config/AgentConfig schema", () => { + const update = buildSessionUpdate({ dialog_id: "conversation-1", system_prompt: "你是陪伴助手", persona_tone: "亲切", authorized_context: "只可使用已授权背景" }, "event-1"); + assert.equal(update.type, "session.update"); + assert.equal(update.session.object, "realtime.session"); + assert.equal(update.session.input_audio_format, "pcm16"); + assert.equal(update.session.output_audio_format, "pcm16"); + assert.equal(update.session.config.ASRConfig.TurnDetectionMode, 0); + assert.match(update.session.config.LLMConfig.SystemMessages[0], /只可使用已授权背景/); + assert.match(update.session.config.LLMConfig.SystemMessages[0], /你是陪伴助手/); + assert.match(update.session.config.LLMConfig.SystemMessages[0], /亲切/); + assert.equal(update.session.agent_config.EnableConversationStateCallback, false); + assert.equal("system_prompt" in update.session, false); +}); + +test("S2S session update maps persona and dialog id to provider dialog config", () => { + const update = buildSessionUpdate({ dialog_id: "conversation-2", system_prompt: "你是陪伴助手", persona_tone: "亲切自然", authorized_context: "只使用授权信息" }, "event-2", "s2s"); + const dialog = update.session.config.S2SConfig.ProviderParams.dialog; + assert.equal(dialog.dialog_id, "conversation-2"); + assert.equal(dialog.speaking_style, "亲切自然"); + assert.match(dialog.system_role, /只使用授权信息/); + assert.match(dialog.system_role, /你是陪伴助手/); + assert.equal("LLMConfig" in update.session.config, false); +}); + +class FakeWebSocket extends EventEmitter { + static OPEN = 1; + constructor(url, options) { + super(); + this.url = url; + this.options = options; + this.readyState = 0; + this.sent = []; + FakeWebSocket.last = this; + setImmediate(() => { + this.readyState = FakeWebSocket.OPEN; + this.emit("open"); + this.emit("message", Buffer.from(JSON.stringify({ type: "session.created", event_id: "created-event", session: { id: "provider-session-1" } }))); + }); + } + send(body) { + const value = JSON.parse(body); + this.sent.push(value); + if (value.type === "session.update") { + setImmediate(() => this.emit("message", Buffer.from(JSON.stringify({ type: "session.updated", event_id: "updated-event", session: { id: "provider-session-1" } })))); + } + } + ping() {} + close(code, reason) { + this.readyState = 3; + this.emit("close", code, Buffer.from(reason)); + } +} + +test("hardware client waits for dynamic config acknowledgement before becoming ready", async () => { + const events = []; + const client = new HardwareAgentClient({ WebSocketImpl: FakeWebSocket, gatewayURL: "ws://127.0.0.1:19090/v1/realtime", emit: (event) => events.push(event) }); + const connected = await client.connect({ + instanceID: "instance-1", productKey: "product-1", deviceName: "device-1", + deviceSecret: "device-secret-with-at-least-32-characters", hardwareID: "windows-test", botID: "bot-1", + sessionConfig: { dialog_id: "conversation-1", authorized_context: "authorized" }, + }); + assert.equal(connected.providerConnectionRef, "provider-session-1"); + assert.match(FakeWebSocket.last.url, /bot=bot-1/); + assert.equal(FakeWebSocket.last.options.headers["X-Product-Key"], "product-1"); + assert.equal(FakeWebSocket.last.sent[0].type, "session.update"); + client.sendAudio(Buffer.from([1, 2, 3, 4])); + client.sendText("你好"); + assert.equal(FakeWebSocket.last.sent[1].type, "input_audio_buffer.append"); + assert.equal(FakeWebSocket.last.sent[2].type, "conversation.item.create"); + assert.ok(events.some((event) => event.type === "session.created")); + client.close(); +}); + +test("hardware client sends session.update immediately when the WebSocket opens", async () => { + const client = new HardwareAgentClient({ WebSocketImpl: FakeWebSocket, gatewayURL: "ws://127.0.0.1:19090/v1/realtime" }); + await client.connect({ + instanceID: "instance-1", productKey: "product-1", deviceName: "device-1", + deviceSecret: "device-secret-with-at-least-32-characters", hardwareID: "windows-test", botID: "bot-1", + sessionConfig: { dialog_id: "conversation-1", authorized_context: "authorized" }, + }); + assert.equal(FakeWebSocket.last.sent.filter((event) => event.type === "session.update").length, 1); + client.close(); +}); diff --git a/device-simulator-windows/electron/hardware-renderer.test.cjs b/device-simulator-windows/electron/hardware-renderer.test.cjs new file mode 100644 index 0000000..00736f6 --- /dev/null +++ b/device-simulator-windows/electron/hardware-renderer.test.cjs @@ -0,0 +1,153 @@ +const assert = require("node:assert/strict"); +const test = require("node:test"); +const fs = require("node:fs"); +const path = require("node:path"); +const vm = require("node:vm"); +const ts = require("typescript"); + +// Execute the actual renderer with a small DOM/IPC fixture; no network or audio. +const source = fs.readFileSync(path.join(__dirname, "../src/hardware.ts"), "utf8"); +const compiled = ts.transpileModule(source, { + compilerOptions: { target: ts.ScriptTarget.ES2022, module: ts.ModuleKind.CommonJS }, +}).outputText; + +function fixture(overrides = {}) { + const elements = new Map(); + const element = () => ({ + style: {}, value: "", textContent: "", disabled: false, children: [], + append(...items) { this.children.push(...items); }, + prepend(...items) { this.children.unshift(...items); }, + replaceChildren(...items) { this.children = items; }, + querySelector() { return null; }, addEventListener() {}, + }); + const get = (id) => { + if (!elements.has(id)) elements.set(id, element()); + return elements.get(id); + }; + const events = []; + const closes = []; + const texts = []; + const created = { + conversation_id: "test-conversation", session: {}, + runtime: { transport: "low_load", config_digest: "digest", session_nonce: "nonce", provider_session_ref: "ref", bot_id: "test-bot" }, + session_config: { authorized_context: "test", prompt_bundle_version: "test-v1" }, + }; + const ipc = { + getRuntimeDefaults: async () => ({}), getHardwareRuntimeDefaults: async () => ({}), + onHardwareProviderEvent: () => () => {}, + createHardwareAgentSession: async () => created, + closeHardwareAgent: async (input) => { closes.push(input); }, + hardwareAgentEvent: async (input) => { events.push(input); return { status: "FAILED" }; }, + sendHardwareAgentText: async (input) => { texts.push(input); }, + ...overrides, + }; + const context = vm.createContext({ + exports: {}, require: () => ({}), console, URL, + document: { querySelector: () => get("app"), getElementById: get, createElement: element }, + window: { deviceSimulator: ipc, addEventListener() {}, clearInterval() {}, setTimeout, setInterval() {} }, + }); + vm.runInContext(compiled + `\nglobalThis.renderer = { state, controls, setPhase, setCheck, resetSessionState, failHardwareConversation, stopHardwareConversation, startHardwareConversation, handleProviderEvent, sendTextQuestion, buildReport };`, context); + return { r: context.renderer, events, closes, texts, created, get, ipc }; +} + +test("provider error plus connect rejection sends one failure, including late duplicates", async () => { + const f = fixture(); + const r = f.r; + f.ipc.connectHardwareAgent = async () => { + void r.handleProviderEvent({ type: "error", error: { code: "internal_error", message: "StartVoiceChat failed" } }); + throw new Error("wrapped connect rejection"); + }; + r.setPhase("ready", "ready"); + await r.startHardwareConversation(); + await r.failHardwareConversation("SECOND_FAILURE"); + await r.stopHardwareConversation(); + await r.handleProviderEvent({ type: "error", error: { message: "late error" } }); + assert.equal(f.events.length, 1); + assert.equal(f.events[0].eventType, "session_failed"); + assert.equal(f.events[0].errorCode, "VOLC_HARDWARE_AGENT_ERROR"); + assert.equal(f.closes.length, 1); + assert.equal(r.state.failure, "internal_error: StartVoiceChat failed"); + assert.equal(r.state.phase, "error"); + assert.equal(r.controls.stop.disabled, true); + assert.equal(r.controls.check.disabled, false); +}); + +test("concurrent failure callers share pending cleanup; late events cannot reactivate", async () => { + let release; + const gate = new Promise((resolve) => { release = resolve; }); + const f = fixture({ closeHardwareAgent: () => gate }); + f.r.state.platformSession = f.created; + f.r.state.failure = "first cause"; + f.r.setPhase("active", "active"); + const first = f.r.failHardwareConversation("FIRST"); + const second = f.r.failHardwareConversation("SECOND"); + assert.equal(first, second); + assert.equal(f.r.state.phase, "stopping"); + await f.r.handleProviderEvent({ type: "response.done" }); + assert.equal(f.r.state.phase, "stopping"); + release(); + await first; + assert.equal(f.events.length, 1); + assert.equal(f.events[0].errorCode, "FIRST"); + await f.r.handleProviderEvent({ type: "transport.close", code: 1011 }); + assert.equal(f.r.state.checks.get("provider_closed").status, "failed"); + f.r.controls.textQuestion.value = "should not send"; + await f.r.sendTextQuestion(); + assert.equal(f.texts.length, 0); +}); + +test("new call clears stale checks, duration and failure while retaining device preparation", async () => { + const f = fixture(); + f.r.state.platformSession = f.created; + f.r.state.failure = "old failure"; + await f.r.failHardwareConversation("OLD"); + for (const id of f.r.state.checks.keys()) f.r.setCheck(id, "passed", "old"); + f.get("duration").textContent = "48:15"; + f.r.resetSessionState(); + assert.equal(f.r.state.failure, undefined); + assert.equal(f.r.state.failureTask, undefined); + assert.equal(f.get("duration").textContent, "00:00"); + assert.equal(f.r.state.checks.get("platform_authenticated").status, "passed"); + assert.equal(f.r.state.checks.get("credential_resolved").status, "passed"); + assert.equal(f.r.state.checks.get("platform_completed").status, "pending"); + f.r.state.platformSession = { ...f.created, conversation_id: "next-conversation" }; + await f.r.failHardwareConversation("NEW"); + assert.equal(f.events.length, 2); + assert.equal(f.events[1].conversationID, "next-conversation"); + assert.equal(f.events[1].sequenceNo, 1); +}); + +test("failed platform receipt does not trigger a new terminal event on hangup", async () => { + let attempts = 0; + const f = fixture({ hardwareAgentEvent: async () => { attempts += 1; throw new Error("network unavailable"); } }); + f.r.state.platformSession = f.created; + await f.r.failHardwareConversation("FIRST"); + await f.r.stopHardwareConversation(); + assert.equal(attempts, 1); + assert.equal(f.r.state.phase, "error"); + assert.equal(f.r.buildReport().checks.find((c) => c.id === "platform_completed").status, "failed"); +}); + +test("connection rejection without a provider event still reports failure once", async () => { + const f = fixture({ connectHardwareAgent: async () => { throw new Error("connect timeout"); } }); + f.r.setPhase("ready", "ready"); + await f.r.startHardwareConversation(); + assert.equal(f.events.length, 1); + assert.equal(f.events[0].errorCode, "WINDOWS_HARDWARE_START_FAILED"); + assert.match(f.r.state.failure, /connect timeout/); + assert.equal(f.r.state.phase, "error"); +}); + +test("normal hangup still reports completed and closes normally", async () => { + const f = fixture(); + f.ipc.hardwareAgentEvent = async (input) => { f.events.push(input); return { status: "COMPLETED" }; }; + f.r.state.platformSession = f.created; + f.r.setPhase("active", "active"); + await f.r.stopHardwareConversation(); + assert.equal(f.events.length, 1); + assert.equal(f.events[0].eventType, "session_ended"); + assert.equal(f.closes[0].code, 1000); + assert.equal(f.r.state.phase, "completed"); + assert.equal(f.r.state.failureTask, undefined); + assert.equal(f.r.controls.start.disabled, false); +}); diff --git a/device-simulator-windows/electron/main.cjs b/device-simulator-windows/electron/main.cjs index cd9987c..c6ffaec 100644 --- a/device-simulator-windows/electron/main.cjs +++ b/device-simulator-windows/electron/main.cjs @@ -1,14 +1,24 @@ -const { app, BrowserWindow, ipcMain, session } = require("electron"); +const { app, BrowserWindow, dialog, ipcMain, safeStorage, session } = require("electron"); const crypto = require("node:crypto"); const fs = require("node:fs"); const path = require("node:path"); const tls = require("node:tls"); +const { HardwareAgentClient } = require("./hardware-agent-client.cjs"); app.commandLine.appendSwitch("autoplay-policy", "no-user-gesture-required"); +const automatedUI = process.env.SIMULATOR_AUTO_TEST === "1" || process.env.SIMULATOR_REAL_E2E === "1"; +if (automatedUI) { + app.commandLine.appendSwitch("use-fake-device-for-media-stream"); + app.commandLine.appendSwitch("use-fake-ui-for-media-stream"); +} app.disableHardwareAcceleration(); let deviceConfig = null; let desiredConfigRevision = 1; +let mainWindow = null; +let hardwareConfig = null; +let hardwareClient = null; +let activeHardwarePlatformSession = null; function createWindow() { const window = new BrowserWindow({ @@ -26,6 +36,10 @@ function createWindow() { sandbox: true, }, }); + mainWindow = window; + window.once("closed", () => { + if (mainWindow === window) mainWindow = null; + }); const devArgument = process.argv.find((argument) => argument.startsWith("--dev-server-url=")); const devServerURL = devArgument ? devArgument.slice("--dev-server-url=".length) : ""; @@ -47,7 +61,7 @@ function createWindow() { } catch (error) { console.error("renderer diagnostics failed", error); } - if (process.env.SIMULATOR_AUTO_TEST === "1") { + if (automatedUI) { void runAutomatedSmoke(window); } }); @@ -62,9 +76,15 @@ function createWindow() { if (screenshotPath) { window.webContents.once("did-finish-load", () => { setTimeout(async () => { - const image = await window.webContents.capturePage(); - fs.writeFileSync(screenshotPath, image.toPNG()); - app.quit(); + try { + const image = await window.webContents.capturePage(); + fs.writeFileSync(screenshotPath, image.toPNG()); + } catch (error) { + console.error("simulator screenshot failed", error); + process.exitCode = 1; + } finally { + app.quit(); + } }, 1200); }); } @@ -95,24 +115,54 @@ async function runAutomatedSmoke(window) { async function collectAutomatedConversationResult(window) { const delay = (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds)); const deadline = Date.now() + 120_000; + let realQuestionSent = false; let snapshot = {}; while (Date.now() < deadline) { await delay(1000); const encoded = await window.webContents.executeJavaScript(`JSON.stringify({ phase: document.querySelector('#phaseBadge')?.textContent || '', conversation_id: document.querySelector('#conversationID')?.textContent || '', + model_mode: document.querySelector('#modelMode')?.value || '', subject: [...document.querySelectorAll('.transcript-turn--subject span')].map((item) => item.textContent || ''), agent: [...document.querySelectorAll('.transcript-turn--agent span')].map((item) => item.textContent || ''), turns: [...document.querySelectorAll('.transcript-turn')].map((item) => ({speaker: item.classList.contains('transcript-turn--subject') ? 'subject' : 'agent', text: item.querySelector('span')?.textContent || ''})), - events: [...document.querySelectorAll('#eventLog li span')].map((item) => item.textContent || '') + events: [...document.querySelectorAll('#eventLog li span')].map((item) => item.textContent || ''), + checks: [...document.querySelectorAll('#flowChecks .flow-check')].map((item) => ({label: item.querySelector('span')?.textContent || '', detail: item.querySelector('small')?.textContent || '', status: item.classList.contains('flow-check--passed') ? 'passed' : item.classList.contains('flow-check--failed') ? 'failed' : 'pending'})) })`); snapshot = JSON.parse(encoded); console.log(`[auto-test] conversation ${encoded}`); + if (process.env.SIMULATOR_REAL_E2E === "1" && snapshot.phase === "通话中" && !realQuestionSent) { + const question = String(process.env.SIMULATOR_REAL_E2E_QUESTION || "请用一句简短的话介绍你自己。 ").trim(); + await window.webContents.executeJavaScript(`(() => { const input = document.querySelector('#textQuestion'); if (input) input.value = ${JSON.stringify(question)}; document.querySelector('#sendTextButton')?.click(); })()`); + realQuestionSent = true; + console.log("[real-e2e] text question sent"); + } const subjectIndex = snapshot.turns?.findIndex((turn) => turn.speaker === "subject") ?? -1; const hasAgentReply = subjectIndex >= 0 && snapshot.turns.slice(subjectIndex + 1).some((turn) => turn.speaker === "agent"); if (hasAgentReply) { - await window.webContents.executeJavaScript(`document.querySelector('#stopButton')?.click()`); - snapshot.result = "PASS"; + const hardwarePage = await window.webContents.executeJavaScript(`Boolean(document.querySelector('#flowChecks'))`); + const waitForPlatformStop = hardwarePage && process.env.SIMULATOR_AUTO_WAIT_FOR_PLATFORM_STOP === "1"; + if (!waitForPlatformStop) await window.webContents.executeJavaScript(`document.querySelector('#stopButton')?.click()`); + if (hardwarePage) { + const stopDeadline = Date.now() + 30_000; + while (Date.now() < stopDeadline) { + await delay(500); + const phase = await window.webContents.executeJavaScript(`document.querySelector('#phaseBadge')?.textContent || ''`); + if (phase === "已完成" || phase === "异常") break; + } + const finalEncoded = await window.webContents.executeJavaScript(`JSON.stringify({ + phase: document.querySelector('#phaseBadge')?.textContent || '', + conversation_id: document.querySelector('#conversationID')?.textContent || '', + model_mode: document.querySelector('#modelMode')?.value || '', + turns: [...document.querySelectorAll('.transcript-turn')].map((item) => ({speaker: item.classList.contains('transcript-turn--subject') ? 'subject' : 'agent', text: item.querySelector('span')?.textContent || ''})), + events: [...document.querySelectorAll('#eventLog li span')].map((item) => item.textContent || ''), + checks: [...document.querySelectorAll('#flowChecks .flow-check')].map((item) => ({label: item.querySelector('span')?.textContent || '', detail: item.querySelector('small')?.textContent || '', status: item.classList.contains('flow-check--passed') ? 'passed' : item.classList.contains('flow-check--failed') ? 'failed' : 'pending'})) + })`); + snapshot = JSON.parse(finalEncoded); + snapshot.result = snapshot.phase === "已完成" ? "PASS" : "ERROR"; + } else { + snapshot.result = "PASS"; + } break; } if (snapshot.phase === "异常") { @@ -160,7 +210,10 @@ function loadDefaultDeviceSecret() { const localAppData = String(process.env.LOCALAPPDATA || "").trim(); const userProfile = String(process.env.USERPROFILE || "").trim(); const portableDirectory = String(process.env.PORTABLE_EXECUTABLE_DIR || "").trim(); - if (localAppData) candidates.push(path.join(localAppData, "DataTerminalVoiceDevice", "server-device-secret")); + if (localAppData) { + candidates.push(path.join(localAppData, "DataTerminalHardwareAgent", "server-device-secret")); + candidates.push(path.join(localAppData, "DataTerminalVoiceDevice", "server-device-secret")); + } if (userProfile) candidates.push(path.join(userProfile, "AppData", "Local", "DataTerminalVoiceDevice", "server-device-secret")); try { const home = app.getPath("home"); @@ -182,11 +235,134 @@ function loadDefaultDeviceSecret() { ipcMain.handle("sim:get-runtime-defaults", () => ({ apiURL: process.env.SIMULATOR_DEFAULT_API_URL || "https://121.41.46.238", - deviceID: process.env.SIMULATOR_DEFAULT_DEVICE_ID || "10000000-0000-0000-0000-000000000511", + deviceID: process.env.SIMULATOR_DEFAULT_DEVICE_ID || "10000000-0000-0000-0000-000000000512", deviceSecret: "", deviceSecretConfigured: loadDefaultDeviceSecret().length >= 32, })); +function hardwareCredentialPath() { + return path.join(app.getPath("userData"), "volc-hardware-device-credential.json"); +} + +function readSavedHardwareMetadata() { + try { + const stored = JSON.parse(fs.readFileSync(hardwareCredentialPath(), "utf8")); + if (!stored.instanceID || !stored.productKey || !stored.deviceName || !stored.encryptedDeviceSecret) return undefined; + return stored; + } catch { return undefined; } +} + +function loadSavedHardwareCredential(identity) { + if (!safeStorage.isEncryptionAvailable()) return undefined; + try { + const stored = JSON.parse(fs.readFileSync(hardwareCredentialPath(), "utf8")); + if (stored.schemaVersion !== 3 || stored.instanceID !== identity.instanceID || stored.productKey !== identity.productKey || stored.deviceName !== identity.deviceName) return undefined; + const secret = safeStorage.decryptString(Buffer.from(stored.encryptedDeviceSecret, "base64")); + if (secret.length < 16) return undefined; + return { deviceSecret: secret, hardwareID: String(stored.hardwareID || identity.hardwareID || "") }; + } catch { return undefined; } +} + +function saveHardwareCredential(identity, deviceSecret, metadata = {}) { + if (!safeStorage.isEncryptionAvailable()) return false; + const target = hardwareCredentialPath(); + fs.mkdirSync(path.dirname(target), { recursive: true }); + const payload = { + schemaVersion: 3, + instanceID: identity.instanceID, + productKey: identity.productKey, + deviceName: identity.deviceName, + hardwareID: identity.hardwareID, + platformDeviceID: String(deviceConfig?.deviceID || ""), + credentialRef: String(metadata.credentialRef || ""), + credentialVersion: Number(metadata.credentialVersion || 1), + encryptedDeviceSecret: safeStorage.encryptString(deviceSecret).toString("base64"), + updatedAt: new Date().toISOString(), + }; + const temporary = `${target}.${process.pid}.tmp`; + fs.writeFileSync(temporary, `${JSON.stringify(payload, null, 2)}\n`, { encoding: "utf8", mode: 0o600 }); + fs.renameSync(temporary, target); + return true; +} + +function hardwareIdentity(input) { + const instanceID = String(input?.instanceID || "").trim(); + const productKey = String(input?.productKey || "").trim(); + const deviceName = String(input?.deviceName || "").trim(); + const hardwareID = String(input?.hardwareID || "").trim(); + if (!instanceID || instanceID.length > 200) throw new Error("实例 ID 不能为空或过长"); + if (!productKey || productKey.length > 200) throw new Error("Product Key 不能为空或过长"); + if (!/^[a-zA-Z0-9_\-.:@]{1,128}$/.test(deviceName)) throw new Error("火山设备名称格式不正确"); + if (!hardwareID || hardwareID.length > 200) throw new Error("Hardware ID 不能为空或过长"); + return { instanceID, productKey, deviceName, hardwareID }; +} + +ipcMain.handle("sim:get-hardware-runtime-defaults", () => { + const candidateMetadata = readSavedHardwareMetadata(); + const legacyMock = candidateMetadata && [candidateMetadata.instanceID, candidateMetadata.productKey, candidateMetadata.deviceName, candidateMetadata.hardwareID] + .some((value) => String(value || "").toLowerCase().includes("mock") || String(value || "").toLowerCase().includes("e2e")); + const savedMetadata = automatedUI || !legacyMock ? candidateMetadata : undefined; + const instanceID = String(process.env.SIMULATOR_VOLC_INSTANCE_ID || savedMetadata?.instanceID || "").trim(); + const productKey = String(process.env.SIMULATOR_VOLC_PRODUCT_KEY || savedMetadata?.productKey || "").trim(); + const deviceName = String(process.env.SIMULATOR_VOLC_DEVICE_NAME || savedMetadata?.deviceName || "windows-hardware-agent-test").trim(); + const hardwareID = String(process.env.SIMULATOR_VOLC_HARDWARE_ID || savedMetadata?.hardwareID || `windows-${crypto.createHash("sha256").update(app.getPath("userData")).digest("hex").slice(0, 24)}`).trim(); + const modelMode = String(process.env.SIMULATOR_VOLC_MODEL_MODE || "llm").trim().toLowerCase() === "s2s" ? "s2s" : "llm"; + const saved = instanceID && productKey ? loadSavedHardwareCredential({ instanceID, productKey, deviceName, hardwareID }) : undefined; + return { instanceID, productKey, deviceName, hardwareID, modelMode, deviceCredentialConfigured: Boolean(saved) }; +}); + +ipcMain.handle("sim:configure-hardware", async (_event, input) => { + if (!deviceConfig) throw new Error("请先连接平台"); + const requestedHardwareID = String(input?.hardwareID || "").trim(); + if (!requestedHardwareID || requestedHardwareID.length > 200) throw new Error("Hardware ID 不能为空或过长"); + const automatedTest = process.env.SIMULATOR_AUTO_TEST === "1"; + let provisioned; + if (automatedTest) { + provisioned = { + instance_id: String(process.env.SIMULATOR_VOLC_INSTANCE_ID || "").trim(), + product_key: String(process.env.SIMULATOR_VOLC_PRODUCT_KEY || "").trim(), + device_name: String(process.env.SIMULATOR_VOLC_DEVICE_NAME || "").trim(), + hardware_id: String(process.env.SIMULATOR_VOLC_HARDWARE_ID || requestedHardwareID).trim(), + device_secret: String(process.env.SIMULATOR_VOLC_DEVICE_SECRET || "").trim(), + credential_source: "isolated_test_override", + credential_version: 1, + credential_ref: "test-only", + }; + } else { + provisioned = await deviceRequest("POST", "/device/v3/hardware-agent-credentials:provision", { + hardware_id: requestedHardwareID, + }, `windows-hardware-provision-${nonce()}`); + } + const identity = hardwareIdentity({ + instanceID: provisioned?.instance_id, + productKey: provisioned?.product_key, + deviceName: provisioned?.device_name, + hardwareID: provisioned?.hardware_id, + }); + const deviceSecret = String(provisioned?.device_secret || "").trim(); + if (deviceSecret.length < 16 || deviceSecret.length > 4096) throw new Error("Device Secret 长度不正确"); + const persisted = automatedTest ? false : saveHardwareCredential(identity, deviceSecret, { + credentialRef: provisioned?.credential_ref, + credentialVersion: provisioned?.credential_version, + }); + if (!automatedTest && !persisted) throw new Error("Windows 安全存储不可用,拒绝保存 Device Secret"); + hardwareConfig = { ...identity, deviceSecret }; + // 首次预配会由平台发布产品级默认 Bot/传输配置,并可能推进配置版本。 + // 设备必须重新拉取版本,后续创建会话时不能继续上报预配前的旧 revision。 + if (!automatedTest) { + const configuration = await deviceRequest("GET", "/device/v2/config"); + desiredConfigRevision = Number(configuration?.revision || desiredConfigRevision || 1); + } + return { + ...identity, + registered: String(provisioned?.credential_source || "").includes("dynamic_registration"), + persisted, + credentialSource: String(provisioned?.credential_source || "platform_provisioning"), + credentialVersion: Number(provisioned?.credential_version || 1), + configRevision: desiredConfigRevision, + }; +}); + function nonce() { return crypto.randomBytes(16).toString("hex"); } @@ -235,7 +411,7 @@ ipcMain.handle("sim:configure", (_event, input) => configure(input)); ipcMain.handle("sim:probe", async () => { const query = new URLSearchParams({ - firmware_version: "0.3.0-windows-rtc", + firmware_version: "0.3.2-windows-hardware-agent", board_rev: "windows-x64", rtc_sdk_version: "web-4.69.0-electron", codec: "opus", @@ -280,6 +456,14 @@ ipcMain.handle("sim:probe-rtc-network", async () => { return { endpoints }; }); +ipcMain.handle("sim:probe-hardware-network", async () => { + const endpoints = await Promise.all([ + probeTLSEndpoint("ai-gateway.vei.volces.com"), + probeTLSEndpoint("iot-cn-shanghai.iot.volces.com"), + ]); + return { endpoints }; +}); + ipcMain.handle("sim:get-automation-audio", async () => { const configuredPath = String(process.env.SIMULATOR_AUTOMATED_AUDIO_PATH || "").trim(); if (!configuredPath) return undefined; @@ -309,6 +493,173 @@ ipcMain.handle("sim:create-conversation", async () => deviceRequest("POST", "/de requested_goal: "open_conversation", }, `windows-create-${nonce()}`)); +ipcMain.handle("sim:create-hardware-agent-session", async () => deviceRequest("POST", "/device/v3/agent-sessions", { + device_time: new Date().toISOString(), + monotonic_ms: Math.max(0, Math.round(performance.now())), + capabilities: { + hardware_agent: true, + sdk_version: "1.1.0-windows-standard-protocol", + transports: ["low_load"], + input_audio_format: "pcm_s16le_16000_mono", + output_audio_format: "pcm_s16le_16000_mono", + server_event_callback: false, + transcript_upload: true, + dynamic_session_config: true, + credential_rotation: true, + near_pcm_recording: false, + far_pcm_recording: false, + }, + network: { type: "windows-local" }, + requested_goal: "open_conversation", + config_revision: desiredConfigRevision, +}, `windows-hardware-create-${nonce()}`)); + +function emitHardwareEvent(event) { + if (!mainWindow || mainWindow.isDestroyed()) return; + mainWindow.webContents.send("sim:hardware-provider-event", event); +} + +function allowedHardwareGatewayURL() { + const configured = String(process.env.SIMULATOR_HARDWARE_GATEWAY_URL || "wss://ai-gateway.vei.volces.com/v1/realtime").trim(); + const parsed = new URL(configured); + const official = parsed.protocol === "wss:" && parsed.hostname === "ai-gateway.vei.volces.com"; + const local = (parsed.protocol === "ws:" || parsed.protocol === "wss:") && ["127.0.0.1", "localhost", "[::1]"].includes(parsed.hostname); + if (!official && !local) throw new Error("硬件智能体网关只允许火山官方地址或本机测试地址"); + return parsed.toString(); +} + +async function invokeHardwareMemoryTool(event) { + const client = hardwareClient; + const platformSession = activeHardwarePlatformSession; + if (!client || !platformSession) return; + const callID = String(event.call_id || ""); + const functionName = String(client.pendingCalls.get(callID) || "").toLowerCase(); + if (!["memory_search", "search_memory", "retrieve_memory"].includes(functionName)) return; + let argumentsValue = {}; + try { argumentsValue = JSON.parse(String(event.arguments || "{}")); } catch { /* invalid input is handled below */ } + const query = String(argumentsValue.query || argumentsValue.keyword || argumentsValue.text || "").trim(); + const limit = Math.max(1, Math.min(5, Number(argumentsValue.limit || 3))); + try { + if (!query) throw new Error("记忆工具缺少 query 参数"); + const baseURL = new URL(deviceConfig.apiURL); + const target = new URL(platformSession.session_config.tool_url, `${baseURL.origin}/`); + if (target.origin !== baseURL.origin) throw new Error("记忆工具地址必须与平台同源"); + const response = await fetch(target, { + method: "POST", + headers: { + Accept: "application/json", + Authorization: `Bearer ${platformSession.session_config.tool_scope_token}`, + "Content-Type": "application/json", + }, + body: JSON.stringify({ query, limit }), + }); + const raw = await response.text(); + let body; + try { body = raw ? JSON.parse(raw) : {}; } catch { body = { message: raw }; } + if (!response.ok) throw new Error(body?.error?.message || body?.message || `HTTP ${response.status}`); + const result = body?.data ?? body; + client.completeFunctionCall(callID, result); + emitHardwareEvent({ type: "platform.memory_search.completed", call_id: callID, query, result_count: result?.items?.length || 0, degraded: Boolean(result?.degraded) }); + } catch (error) { + client.completeFunctionCall(callID, { items: [], degraded: true, error: error.message }); + emitHardwareEvent({ type: "platform.memory_search.failed", call_id: callID, message: error.message }); + } +} + +ipcMain.handle("sim:connect-hardware-agent", async (_event, input) => { + if (!deviceConfig) throw new Error("请先连接平台"); + if (!hardwareConfig) throw new Error("请先完成火山设备鉴权配置"); + if (hardwareClient) throw new Error("已有硬件智能体连接,请先结束"); + const platformSession = input?.platformSession; + if (!platformSession?.runtime?.bot_id || !platformSession?.session_config?.dialog_id) throw new Error("平台硬件会话配置不完整"); + if (platformSession.runtime.transport !== "low_load") throw new Error("Windows 标准协议测试程序只接受设备绑定为 low_load 的会话"); + const modelMode = String(input?.modelMode || "llm").trim().toLowerCase(); + if (!new Set(["llm", "s2s"]).has(modelMode)) throw new Error("智能体模型类型必须为 llm 或 s2s"); + activeHardwarePlatformSession = platformSession; + const client = new HardwareAgentClient({ + gatewayURL: allowedHardwareGatewayURL(), + emit: (event) => { + emitHardwareEvent(event); + if (event.type === "response.function_call_arguments.done") void invokeHardwareMemoryTool(event); + }, + }); + hardwareClient = client; + try { + return await client.connect({ + ...hardwareConfig, + botID: platformSession.runtime.bot_id, + sessionConfig: platformSession.session_config, + modelMode, + }); + } catch (error) { + hardwareClient = null; + activeHardwarePlatformSession = null; + throw error; + } +}); + +ipcMain.on("sim:hardware-agent-audio", (_event, bytes) => { + try { hardwareClient?.sendAudio(Buffer.from(bytes)); } + catch (error) { emitHardwareEvent({ type: "transport.audio_error", message: error.message }); } +}); + +ipcMain.handle("sim:hardware-agent-text", (_event, input) => { + if (!hardwareClient) throw new Error("火山硬件智能体连接未就绪"); + hardwareClient.sendText(input?.text, Number(input?.interruptMode || 1)); + return { accepted: true }; +}); + +ipcMain.handle("sim:close-hardware-agent", (_event, input) => { + const client = hardwareClient; + hardwareClient = null; + activeHardwarePlatformSession = null; + client?.close(Number(input?.code || 1000), String(input?.reason || "device session ended").slice(0, 100)); + return { closed: true }; +}); + +ipcMain.handle("sim:save-hardware-report", async (_event, report) => { + const result = await dialog.showSaveDialog(mainWindow, { + title: "导出硬件对话智能体测试报告", + defaultPath: `hardware-agent-test-${new Date().toISOString().replace(/[:.]/g, "-")}.json`, + filters: [{ name: "JSON 测试报告", extensions: ["json"] }], + }); + if (result.canceled || !result.filePath) return { saved: false }; + const redact = (value) => { + if (Array.isArray(value)) return value.map(redact); + if (!value || typeof value !== "object") return value; + const output = {}; + for (const [key, item] of Object.entries(value)) { + if (/secret|signature|authorization|tool_scope_token|systemmessages|system_role|system_prompt|authorized_context|speaking_style|instructions/i.test(key)) output[key] = "[REDACTED]"; + else output[key] = redact(item); + } + return output; + }; + const safeReport = redact(JSON.parse(JSON.stringify(report))); + fs.writeFileSync(result.filePath, `${JSON.stringify(safeReport, null, 2)}\n`, "utf8"); + return { saved: true, path: result.filePath }; +}); + +ipcMain.handle("sim:hardware-agent-event", async (_event, input) => deviceRequest( + "POST", + `/device/v3/agent-sessions/${encodeURIComponent(input.conversationID)}/events`, + { + event_id: input.eventID || `windows-hardware-event-${nonce()}`, + sequence_no: Number(input.sequenceNo), + event_type: input.eventType, + occurred_at: new Date().toISOString(), + provider_session_ref: input.providerSessionRef, + session_nonce: input.sessionNonce, + ...(input.eventType === "session_started" ? { + provider_connection_ref: input.providerConnectionRef, + bot_id: input.botID, + transport: input.transport, + config_digest: input.configDigest, + dynamic_config_applied: input.dynamicConfigApplied, + } : {}), + ...(input.errorCode ? { error_code: input.errorCode } : {}), + }, +)); + ipcMain.handle("sim:activate-conversation", async (_event, input) => deviceRequest( "POST", `/device/v2/conversations/${encodeURIComponent(input.conversationID)}/activate`, @@ -338,7 +689,7 @@ ipcMain.handle("sim:heartbeat", async (_event, input) => deviceRequest( conversation_status: "ACTIVE", rtc: { joined: true, uplink: true, downlink: true }, network: { type: "windows-local" }, - firmware_version: "0.3.0-windows-rtc", + firmware_version: "0.3.2-windows-hardware-agent", reported_config_revision: desiredConfigRevision, rtc_credential_expires_at: input.credentialExpiresAt, recording_queue: { pending_segments: 0, failed_segments: 0 }, @@ -356,7 +707,7 @@ ipcMain.handle("sim:submit-transcript-batch", async (_event, input) => { const digest = crypto.createHash("sha256").update(JSON.stringify(payload)).digest("hex").slice(0, 32); return deviceRequest( "POST", - `/device/v2/conversations/${encodeURIComponent(conversationID)}/turns:batch`, + `/${input?.hardwareAgent ? "device/v3/agent-sessions" : "device/v2/conversations"}/${encodeURIComponent(conversationID)}/turns:batch`, payload, `windows-turns-${digest}`, ); @@ -433,6 +784,9 @@ app.whenReady().then(() => { }); app.on("window-all-closed", () => { + hardwareClient?.close(1001, "application closed"); + hardwareClient = null; + activeHardwarePlatformSession = null; if (process.platform !== "darwin") app.quit(); }); diff --git a/device-simulator-windows/electron/pcm-playback-worklet.test.cjs b/device-simulator-windows/electron/pcm-playback-worklet.test.cjs new file mode 100644 index 0000000..3c805d5 --- /dev/null +++ b/device-simulator-windows/electron/pcm-playback-worklet.test.cjs @@ -0,0 +1,76 @@ +const assert = require("node:assert/strict"); +const test = require("node:test"); +const fs = require("node:fs"); +const path = require("node:path"); +const vm = require("node:vm"); + +const source = fs.readFileSync(path.join(__dirname, "../public/pcm-playback.worklet.js"), "utf8"); + +function fixture({ outputRate = 48000, prebufferMs = 120 } = {}) { + const messages = []; + let Processor; + class AudioWorkletProcessor { + constructor() { + this.port = { onmessage: null, postMessage: (message) => messages.push(message) }; + } + } + const context = vm.createContext({ + AudioWorkletProcessor, + Float32Array, + ArrayBuffer, + Math, + sampleRate: outputRate, + registerProcessor: (name, implementation) => { + assert.equal(name, "pcm16-playback"); + Processor = implementation; + }, + }); + vm.runInContext(source, context); + const processor = new Processor({ processorOptions: { sourceSampleRate: 16000, prebufferMs } }); + const push = (values) => { + const samples = new Float32Array(values); + processor.port.onmessage({ data: { type: "push", samples: samples.buffer } }); + }; + const process = () => { + const output = new Float32Array(128); + assert.equal(processor.process([], [[output]]), true); + return output; + }; + return { messages, processor, push, process }; +} + +test("waits for prebuffer then resamples continuous 16k PCM without fragment gaps", () => { + const f = fixture({ outputRate: 48000, prebufferMs: 10 }); + f.push(new Array(120).fill(0.25)); + assert.ok(f.process().every((sample) => sample === 0), "must remain silent below the prebuffer threshold"); + f.push(new Array(400).fill(0.25)); + const first = f.process(); + const second = f.process(); + assert.ok(first.some((sample) => sample > 0.05), "buffered playback should start with a short anti-pop fade"); + assert.ok(second.every((sample) => sample > 0.12), "playback should remain continuous across source fragments"); + assert.equal(f.messages.filter((message) => message.type === "underflow").length, 0); +}); + +test("plays 16k source at real-time duration instead of the former 1.5x speed", () => { + const f = fixture({ outputRate: 48000, prebufferMs: 1 }); + f.push(new Array(1000).fill(0.25)); + f.process(); + assert.ok(Math.abs(f.processor.readPosition - 128 / 3) < 0.001, `consumed ${f.processor.readPosition} source samples`); +}); + +test("reports network starvation and re-enters prebuffering instead of scheduling tiny buffers", () => { + const f = fixture({ outputRate: 48000, prebufferMs: 0.01 }); + f.push(new Array(8).fill(0.5)); + f.process(); + assert.equal(f.messages.filter((message) => message.type === "underflow").length, 1); + assert.ok(f.process().every((sample) => sample === 0), "starved playback should wait for a new prebuffer"); +}); + +test("drains the final response cleanly and emits a completion signal", () => { + const f = fixture({ outputRate: 24000 }); + f.push(new Array(64).fill(-0.25)); + f.processor.port.onmessage({ data: { type: "end" } }); + f.process(); + assert.equal(f.messages.filter((message) => message.type === "drained").length, 1); + assert.equal(f.messages.filter((message) => message.type === "underflow").length, 0); +}); diff --git a/device-simulator-windows/electron/preload.cjs b/device-simulator-windows/electron/preload.cjs index 453efb8..8dae624 100644 --- a/device-simulator-windows/electron/preload.cjs +++ b/device-simulator-windows/electron/preload.cjs @@ -5,8 +5,23 @@ contextBridge.exposeInMainWorld("deviceSimulator", { configure: (input) => ipcRenderer.invoke("sim:configure", input), probe: () => ipcRenderer.invoke("sim:probe"), probeRTCNetwork: () => ipcRenderer.invoke("sim:probe-rtc-network"), + probeHardwareNetwork: () => ipcRenderer.invoke("sim:probe-hardware-network"), + getHardwareRuntimeDefaults: () => ipcRenderer.invoke("sim:get-hardware-runtime-defaults"), + configureHardware: (input) => ipcRenderer.invoke("sim:configure-hardware", input), getAutomationAudio: () => ipcRenderer.invoke("sim:get-automation-audio"), createConversation: () => ipcRenderer.invoke("sim:create-conversation"), + createHardwareAgentSession: () => ipcRenderer.invoke("sim:create-hardware-agent-session"), + connectHardwareAgent: (input) => ipcRenderer.invoke("sim:connect-hardware-agent", input), + sendHardwareAgentAudio: (bytes) => ipcRenderer.send("sim:hardware-agent-audio", bytes), + sendHardwareAgentText: (input) => ipcRenderer.invoke("sim:hardware-agent-text", input), + closeHardwareAgent: (input) => ipcRenderer.invoke("sim:close-hardware-agent", input), + onHardwareProviderEvent: (callback) => { + const listener = (_event, value) => callback(value); + ipcRenderer.on("sim:hardware-provider-event", listener); + return () => ipcRenderer.removeListener("sim:hardware-provider-event", listener); + }, + saveHardwareReport: (report) => ipcRenderer.invoke("sim:save-hardware-report", report), + hardwareAgentEvent: (input) => ipcRenderer.invoke("sim:hardware-agent-event", input), activateConversation: (input) => ipcRenderer.invoke("sim:activate-conversation", input), getConversation: (conversationID) => ipcRenderer.invoke("sim:get-conversation", conversationID), heartbeat: (input) => ipcRenderer.invoke("sim:heartbeat", input), diff --git a/device-simulator-windows/index.html b/device-simulator-windows/index.html index a5e2681..77c4329 100644 --- a/device-simulator-windows/index.html +++ b/device-simulator-windows/index.html @@ -4,10 +4,10 @@ - 语音终端模拟器 + 硬件对话智能体测试程序
- + diff --git a/device-simulator-windows/launch-cloud-device.cmd b/device-simulator-windows/launch-cloud-device.cmd index 760b2e5..b8a1f1b 100644 --- a/device-simulator-windows/launch-cloud-device.cmd +++ b/device-simulator-windows/launch-cloud-device.cmd @@ -1,7 +1,8 @@ @echo off setlocal -set "SECRET_FILE=%~dp0.server-device-secret" -set "DEVICE_EXE=%~dp0voice-device-simulator-0.1.0-windows-x64.exe" +set "SECRET_FILE=%LOCALAPPDATA%\DataTerminalHardwareAgent\server-device-secret" +set "DEVICE_EXE=%~dp0hardware-agent-tester-0.3.5-windows-x64.exe" +if not exist "%DEVICE_EXE%" set "DEVICE_EXE=%~dp0release\hardware-agent-tester-0.3.5-windows-x64.exe" if not exist "%SECRET_FILE%" ( echo Cloud device credential is missing: %SECRET_FILE% @@ -9,12 +10,12 @@ if not exist "%SECRET_FILE%" ( exit /b 1 ) if not exist "%DEVICE_EXE%" ( - echo Voice device simulator is missing: %DEVICE_EXE% + echo Hardware agent tester is missing: %DEVICE_EXE% pause exit /b 1 ) set /p SIMULATOR_DEFAULT_DEVICE_SECRET=<"%SECRET_FILE%" set "SIMULATOR_DEFAULT_API_URL=https://121.41.46.238" -set "SIMULATOR_DEFAULT_DEVICE_ID=10000000-0000-0000-0000-000000000511" +set "SIMULATOR_DEFAULT_DEVICE_ID=10000000-0000-0000-0000-000000000512" start "" "%DEVICE_EXE%" diff --git a/device-simulator-windows/launch-cloud-device.ps1 b/device-simulator-windows/launch-cloud-device.ps1 index 85a8799..c2ac495 100644 --- a/device-simulator-windows/launch-cloud-device.ps1 +++ b/device-simulator-windows/launch-cloud-device.ps1 @@ -1,25 +1,46 @@ [CmdletBinding()] -param() +param( + [switch]$RealE2E, + [string]$ResultPath = (Join-Path $env:TEMP "hardware-agent-real-e2e.json") +) $ErrorActionPreference = "Stop" -$secretPath = Join-Path $env:LOCALAPPDATA "DataTerminalVoiceDevice\server-device-secret" -$executablePath = Join-Path $PSScriptRoot "voice-device-simulator-0.1.0-windows-x64.exe" +$secretPath = Join-Path $env:LOCALAPPDATA "DataTerminalHardwareAgent\server-device-secret" +$executablePath = Join-Path $PSScriptRoot "hardware-agent-tester-0.3.5-windows-x64.exe" +if (-not (Test-Path -LiteralPath $executablePath -PathType Leaf)) { + $executablePath = Join-Path $PSScriptRoot "release\hardware-agent-tester-0.3.5-windows-x64.exe" +} if (-not (Test-Path -LiteralPath $secretPath -PathType Leaf)) { throw "Cloud device credential is missing: $secretPath" } if (-not (Test-Path -LiteralPath $executablePath -PathType Leaf)) { - throw "Voice device simulator is missing: $executablePath" + throw "Hardware agent tester is missing: $executablePath" } $env:SIMULATOR_DEFAULT_API_URL = "https://121.41.46.238" -$env:SIMULATOR_DEFAULT_DEVICE_ID = "10000000-0000-0000-0000-000000000511" +$env:SIMULATOR_DEFAULT_DEVICE_ID = "10000000-0000-0000-0000-000000000512" $env:SIMULATOR_DEFAULT_DEVICE_SECRET = [IO.File]::ReadAllText($secretPath).Trim() try { - Start-Process -FilePath $executablePath + if ($RealE2E) { + $env:SIMULATOR_REAL_E2E = "1" + $env:SIMULATOR_AUTO_RESULT_PATH = $ResultPath + $env:SIMULATOR_VOLC_MODEL_MODE = "llm" + $env:SIMULATOR_REAL_E2E_QUESTION = "Please introduce yourself in one short sentence." + $process = Start-Process -FilePath $executablePath -WindowStyle Hidden -PassThru -Wait + if ($process.ExitCode -ne 0) { throw "Hardware agent tester exited with code $($process.ExitCode)" } + if (-not (Test-Path -LiteralPath $ResultPath -PathType Leaf)) { throw "Real E2E report was not created: $ResultPath" } + } + else { + Start-Process -FilePath $executablePath + } } finally { Remove-Item Env:SIMULATOR_DEFAULT_API_URL -ErrorAction SilentlyContinue Remove-Item Env:SIMULATOR_DEFAULT_DEVICE_ID -ErrorAction SilentlyContinue Remove-Item Env:SIMULATOR_DEFAULT_DEVICE_SECRET -ErrorAction SilentlyContinue + Remove-Item Env:SIMULATOR_REAL_E2E -ErrorAction SilentlyContinue + Remove-Item Env:SIMULATOR_AUTO_RESULT_PATH -ErrorAction SilentlyContinue + Remove-Item Env:SIMULATOR_VOLC_MODEL_MODE -ErrorAction SilentlyContinue + Remove-Item Env:SIMULATOR_REAL_E2E_QUESTION -ErrorAction SilentlyContinue } diff --git a/device-simulator-windows/legacy.html b/device-simulator-windows/legacy.html new file mode 100644 index 0000000..796f112 --- /dev/null +++ b/device-simulator-windows/legacy.html @@ -0,0 +1,13 @@ + + + + + + + 传统 RTC 语音终端模拟器 + + +
+ + + diff --git a/device-simulator-windows/package-lock.json b/device-simulator-windows/package-lock.json index 207c455..643aa1d 100644 --- a/device-simulator-windows/package-lock.json +++ b/device-simulator-windows/package-lock.json @@ -1,14 +1,15 @@ { "name": "data-terminal-voice-device-simulator", - "version": "0.1.0", + "version": "0.3.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "data-terminal-voice-device-simulator", - "version": "0.1.0", + "version": "0.3.5", "dependencies": { - "@volcengine/rtc": "4.69.0" + "@volcengine/rtc": "4.69.0", + "ws": "8.21.3" }, "devDependencies": { "concurrently": "^9.2.1", @@ -4857,6 +4858,27 @@ "dev": true, "license": "ISC" }, + "node_modules/ws": { + "version": "8.21.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.3.tgz", + "integrity": "sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/xmlbuilder": { "version": "15.1.1", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", diff --git a/device-simulator-windows/package.json b/device-simulator-windows/package.json index 1ac764f..5de9991 100644 --- a/device-simulator-windows/package.json +++ b/device-simulator-windows/package.json @@ -1,18 +1,20 @@ { "name": "data-terminal-voice-device-simulator", - "version": "0.1.0", + "version": "0.3.5", "private": true, - "description": "Windows real-audio device simulator for the data terminal cloud platform", + "description": "Windows end-to-end tester for Volcengine hardware conversational agents", "main": "electron/main.cjs", "scripts": { "dev": "concurrently -k \"vite --host 127.0.0.1 --port 5177\" \"wait-on http://127.0.0.1:5177 && electron . --dev-server-url=http://127.0.0.1:5177\"", "typecheck": "tsc --noEmit", + "test": "node --test electron/*.test.cjs", "build:renderer": "vite build", "start": "npm run build:renderer && electron .", - "build": "npm run typecheck && npm run build:renderer && electron-builder --win portable" + "build": "npm test && npm run typecheck && npm run build:renderer && electron-builder --win portable" }, "dependencies": { - "@volcengine/rtc": "4.69.0" + "@volcengine/rtc": "4.69.0", + "ws": "8.21.3" }, "devDependencies": { "concurrently": "^9.2.1", @@ -23,22 +25,25 @@ "wait-on": "^8.0.4" }, "build": { - "appId": "cn.dataterminal.voice-device-simulator", - "productName": "语音终端模拟器", - "artifactName": "voice-device-simulator-${version}-windows-${arch}.${ext}", + "appId": "cn.dataterminal.hardware-agent-tester", + "productName": "硬件对话智能体测试程序", + "artifactName": "hardware-agent-tester-${version}-windows-${arch}.${ext}", "directories": { "output": "release" }, "files": [ "dist/**/*", "electron/**/*", + "!electron/*.test.cjs", "package.json" ], "win": { "target": [ { "target": "portable", - "arch": ["x64"] + "arch": [ + "x64" + ] } ] } diff --git a/device-simulator-windows/public/pcm-capture.worklet.js b/device-simulator-windows/public/pcm-capture.worklet.js new file mode 100644 index 0000000..0fcafc6 --- /dev/null +++ b/device-simulator-windows/public/pcm-capture.worklet.js @@ -0,0 +1,47 @@ +class PCM16CaptureProcessor extends AudioWorkletProcessor { + constructor(options) { + super(); + this.targetRate = Number(options.processorOptions?.targetSampleRate || 16000); + this.frameSamples = Number(options.processorOptions?.frameSamples || 320); + this.ratio = sampleRate / this.targetRate; + this.source = []; + this.position = 0; + this.output = []; + } + + process(inputs) { + const channel = inputs[0]?.[0]; + if (!channel?.length) return true; + for (const sample of channel) this.source.push(sample); + while (this.position + this.ratio <= this.source.length) { + const start = Math.floor(this.position); + const end = Math.max(start + 1, Math.floor(this.position + this.ratio)); + let sum = 0; + for (let index = start; index < end && index < this.source.length; index += 1) sum += this.source[index]; + this.output.push(sum / Math.max(1, end - start)); + this.position += this.ratio; + if (this.output.length >= this.frameSamples) this.flush(); + } + const consumed = Math.floor(this.position); + if (consumed > 0) { + this.source.splice(0, consumed); + this.position -= consumed; + } + return true; + } + + flush() { + const frame = this.output.splice(0, this.frameSamples); + const pcm = new Int16Array(frame.length); + let energy = 0; + for (let index = 0; index < frame.length; index += 1) { + const sample = Math.max(-1, Math.min(1, frame[index])); + energy += sample * sample; + pcm[index] = sample < 0 ? Math.round(sample * 32768) : Math.round(sample * 32767); + } + const volume = Math.min(100, Math.round(Math.sqrt(energy / Math.max(1, frame.length)) * 280)); + this.port.postMessage({ type: "pcm", bytes: pcm.buffer, volume }, [pcm.buffer]); + } +} + +registerProcessor("pcm16-capture", PCM16CaptureProcessor); diff --git a/device-simulator-windows/public/pcm-playback.worklet.js b/device-simulator-windows/public/pcm-playback.worklet.js new file mode 100644 index 0000000..b50c99e --- /dev/null +++ b/device-simulator-windows/public/pcm-playback.worklet.js @@ -0,0 +1,115 @@ +class PCM16PlaybackProcessor extends AudioWorkletProcessor { + constructor(options) { + super(); + const config = options.processorOptions || {}; + this.sourceSampleRate = Number(config.sourceSampleRate || 16000); + this.prebufferSamples = Math.max(1, Math.round(this.sourceSampleRate * Number(config.prebufferMs || 120) / 1000)); + this.capacity = Math.max(this.sourceSampleRate * 30, this.prebufferSamples * 4); + this.ring = new Float32Array(this.capacity); + this.readPosition = 0; + this.writePosition = 0; + this.playing = false; + this.ended = false; + this.drained = false; + this.lastOutput = 0; + this.fadeInRemaining = 0; + this.fadeFrames = Math.max(1, Math.round(sampleRate * 0.005)); + this.underflows = 0; + this.port.onmessage = (message) => this.onMessage(message.data || {}); + } + + onMessage(message) { + if (message.type === "reset") { + this.reset(); + return; + } + if (message.type === "end") { + this.ended = true; + return; + } + if (message.type !== "push" || !(message.samples instanceof ArrayBuffer)) return; + const samples = new Float32Array(message.samples); + if (this.drained) this.reset(); + this.ended = false; + for (let index = 0; index < samples.length; index += 1) { + if (this.writePosition - Math.floor(this.readPosition) >= this.capacity - 1) { + this.readPosition = this.writePosition - this.capacity + 2; + } + this.ring[this.writePosition % this.capacity] = samples[index]; + this.writePosition += 1; + } + } + + reset() { + this.readPosition = 0; + this.writePosition = 0; + this.playing = false; + this.ended = false; + this.drained = false; + this.lastOutput = 0; + this.fadeInRemaining = 0; + } + + available() { + return this.writePosition - this.readPosition; + } + + startWhenReady() { + if (this.playing) return true; + const available = this.available(); + if (available >= this.prebufferSamples || (this.ended && available > 1)) { + this.playing = true; + this.fadeInRemaining = this.fadeFrames; + this.port.postMessage({ type: "started", bufferedSamples: Math.floor(available) }); + return true; + } + return false; + } + + finish(output, fromIndex, eventType) { + const remaining = Math.min(this.fadeFrames, output.length - fromIndex); + for (let index = 0; index < remaining; index += 1) { + output[fromIndex + index] = this.lastOutput * (1 - (index + 1) / remaining); + } + output.fill(0, fromIndex + remaining); + this.lastOutput = 0; + this.playing = false; + if (eventType === "drained") { + if (!this.drained) this.port.postMessage({ type: "drained" }); + this.drained = true; + } else { + this.underflows += 1; + this.port.postMessage({ type: "underflow", count: this.underflows }); + } + } + + process(_inputs, outputs) { + const output = outputs[0]?.[0]; + if (!output) return true; + output.fill(0); + if (!this.startWhenReady()) return true; + + const sourceStep = this.sourceSampleRate / sampleRate; + for (let index = 0; index < output.length; index += 1) { + const base = Math.floor(this.readPosition); + if (base + 1 >= this.writePosition) { + this.finish(output, index, this.ended ? "drained" : "underflow"); + break; + } + const fraction = this.readPosition - base; + const first = this.ring[base % this.capacity]; + const second = this.ring[(base + 1) % this.capacity]; + let value = first + (second - first) * fraction; + if (this.fadeInRemaining > 0) { + value *= 1 - this.fadeInRemaining / this.fadeFrames; + this.fadeInRemaining -= 1; + } + output[index] = value; + this.lastOutput = value; + this.readPosition += sourceStep; + } + return true; + } +} + +registerProcessor("pcm16-playback", PCM16PlaybackProcessor); diff --git a/device-simulator-windows/src/global.d.ts b/device-simulator-windows/src/global.d.ts index 13431d2..dcf681c 100644 --- a/device-simulator-windows/src/global.d.ts +++ b/device-simulator-windows/src/global.d.ts @@ -34,6 +34,32 @@ type CreatedConversation = { }; }; +type CreatedHardwareAgentSession = { + conversation_id: string; + status: "SESSION_READY"; + runtime_type: "volc_hardware_agent"; + runtime: { + provider: "volcengine"; + bot_id: string; + transport: "high_quality" | "low_load"; + provider_session_ref: string; + session_nonce: string; + config_digest: string; + wait_for_session_update: boolean; + }; + session_config: { + dialog_id: string; + system_prompt?: string; + persona_tone?: string; + authorized_context: string; + prompt_bundle_version: string; + tool_url?: string; + tool_scope_token?: string; + }; + privacy: CreatedConversation["privacy"]; + session: CreatedConversation["session"]; +}; + type DeviceFinalTurn = { turn_key: string; sequence_no: number; @@ -50,15 +76,60 @@ interface Window { configure(input: SimulatorConfiguration): Promise<{ apiURL: string; deviceID: string }>; probe(): Promise>; probeRTCNetwork(): Promise<{ endpoints: Array<{ hostname: string; ok: boolean; error?: string }> }>; + probeHardwareNetwork(): Promise<{ endpoints: Array<{ hostname: string; ok: boolean; error?: string }> }>; + getHardwareRuntimeDefaults(): Promise<{ + instanceID: string; + productKey: string; + deviceName: string; + hardwareID: string; + modelMode: "llm" | "s2s"; + deviceCredentialConfigured: boolean; + }>; + configureHardware(input: { + hardwareID: string; + }): Promise<{ + instanceID: string; + productKey: string; + deviceName: string; + hardwareID: string; + registered: boolean; + persisted: boolean; + credentialSource: string; + credentialVersion: number; + }>; getAutomationAudio(): Promise<{ data_url: string; source_name: string } | undefined>; createConversation(): Promise; + createHardwareAgentSession(): Promise; + connectHardwareAgent(input: { platformSession: CreatedHardwareAgentSession; modelMode: "llm" | "s2s" }): Promise<{ + providerConnectionRef: string; + session: Record; + }>; + sendHardwareAgentAudio(bytes: ArrayBuffer): void; + sendHardwareAgentText(input: { text: string; interruptMode?: number }): Promise<{ accepted: boolean }>; + closeHardwareAgent(input?: { code?: number; reason?: string }): Promise<{ closed: boolean }>; + onHardwareProviderEvent(callback: (event: Record) => void): () => void; + saveHardwareReport(report: Record): Promise<{ saved: boolean; path?: string }>; + hardwareAgentEvent(input: { + conversationID: string; + eventID?: string; + sequenceNo: number; + eventType: "session_started" | "session_updated" | "session_ended" | "session_failed"; + providerSessionRef: string; + sessionNonce: string; + providerConnectionRef?: string; + botID?: string; + transport?: "high_quality" | "low_load"; + configDigest?: string; + dynamicConfigApplied?: boolean; + errorCode?: string; + }): Promise<{ conversation_id: string; status: string; action: "continue" | "stop_session"; duplicate: boolean }>; activateConversation(input: { conversationID: string; roomID: string; userID: string }): Promise>; getConversation(conversationID: string): Promise<{ status: string; action: string; failure_code?: string }>; heartbeat(input: { conversationID: string; sequenceNo: number; credentialExpiresAt: string }): Promise<{ actions?: Array<{ type: string; rtc?: RTCGrant }>; desired_config_revision?: number; }>; - submitTranscriptBatch(input: { conversationID: string; turns: DeviceFinalTurn[] }): Promise<{ + submitTranscriptBatch(input: { conversationID: string; turns: DeviceFinalTurn[]; hardwareAgent?: boolean }): Promise<{ conversation_id: string; accepted_count: number; duplicate_count: number; diff --git a/device-simulator-windows/src/hardware.ts b/device-simulator-windows/src/hardware.ts new file mode 100644 index 0000000..356a242 --- /dev/null +++ b/device-simulator-windows/src/hardware.ts @@ -0,0 +1,778 @@ +import "./style.css"; + +type HardwarePhase = "idle" | "checking" | "ready" | "connecting" | "active" | "stopping" | "completed" | "error"; +type ReportCheck = { id: string; label: string; status: "pending" | "passed" | "failed"; detail?: string; at?: string }; +type ReportEvent = { at: string; source: "app" | "platform" | "volcengine"; type: string; detail?: Record }; + +const checkDefinitions: Array<[string, string]> = [ + ["platform_authenticated", "平台设备 HMAC 鉴权"], + ["credential_resolved", "火山一机一密凭据"], + ["context_frozen", "人设、背景与授权上下文冻结"], + ["provider_connected", "火山硬件智能体 WebSocket 建连"], + ["dynamic_config_applied", "session.updated 动态配置确认"], + ["platform_active", "平台确认会话 ACTIVE"], + ["microphone_streaming", "16kHz PCM 麦克风实时上行"], + ["audio_playback_buffered", "16kHz PCM 连续缓冲播放"], + ["heartbeat_ack", "平台 session_updated 存活回执"], + ["user_transcript", "用户最终字幕"], + ["agent_transcript", "智能体最终字幕"], + ["transcript_uploaded", "最终话轮可靠上传"], + ["provider_closed", "火山连接正常关闭"], + ["platform_completed", "平台确认会话 COMPLETED"], +]; + +const state: { + phase: HardwarePhase; + platformSession?: CreatedHardwareAgentSession; + providerConnectionRef?: string; + eventSequence: number; + transcriptSequence: number; + pendingTurns: DeviceFinalTurn[]; + transcriptUpload?: Promise; + heartbeatTimer?: number; + durationTimer?: number; + startedAt?: number; + stopping: boolean; + intentionalProviderClose: boolean; + microphoneStream?: MediaStream; + captureContext?: AudioContext; + captureNode?: AudioWorkletNode; + playbackContext?: AudioContext; + playbackNode?: AudioWorkletNode; + playbackReady?: Promise; + playbackPendingByte?: number; + playbackChunks: number; + playbackSamples: number; + playbackUnderflows: number; + responseTranscripts: Map; + speechStartedAt?: number; + checks: Map; + events: ReportEvent[]; + turns: DeviceFinalTurn[]; + failure?: string; + failureTask?: Promise; +} = { + phase: "idle", + eventSequence: 0, + transcriptSequence: 0, + pendingTurns: [], + stopping: false, + intentionalProviderClose: false, + playbackChunks: 0, + playbackSamples: 0, + playbackUnderflows: 0, + responseTranscripts: new Map(), + checks: new Map(checkDefinitions.map(([id, label]) => [id, { id, label, status: "pending" }])), + events: [], + turns: [], +}; + +const app = document.querySelector("#app")!; +app.innerHTML = ` +
+
+
+

WINDOWS · VOLC HARDWARE AGENT · LOW LOAD

+

硬件对话智能体测试程序

+

真实连接平台设备 v3 控制面和火山硬件对话智能体 WebSocket,验证动态人设、PCM 双向语音、字幕、记忆工具、平台停止与结束闭环。

+
+ +
+ +
+
平台 v3未检测等待设备鉴权
+
火山设备凭据未配置支持动态注册
+
动态会话配置未应用麦克风保持关闭
+
通话时长00:00网关未连接
+
+ +
+
+

SECURE SETUP

平台与火山设备

密钥不写入报告
+

平台设备身份

+ + + +

火山硬件身份

+
+
+ + + +

Product Secret 只保存在平台密钥服务中。平台代设备完成动态注册,设备仅接收自己的一机一密并使用 Windows Safe Storage 加密保存;默认 Bot 由平台产品配置自动分配,设备不能覆盖。

+
+ +
+

REAL END-TO-END FLOW

硬件智能体通话

待机
+
准备检测
+
+
16k PCM 上行0
+
16k PCM 下行0
+
+
+
+
+
平台通话编号
—
+
产品默认 Bot
—
+
传输方案
low_load
+
火山会话编号
—
+
配置摘要
—
+
冻结提示版本
—
+
+
+
+ +
+

ACCEPTANCE GATES

完整流程验收

+
    +
    + +
    +

    FINAL TRANSCRIPTS

    双方最终字幕与平台上传

    0 条
    +
    1. 通话开始后显示火山返回的用户和智能体最终字幕。
    +
    + +
    +

    AUDITABLE EVENT STREAM

    平台与火山事件

    +
    1. 测试程序已启动,等待环境检测。
    +
    +
    +`; + +const $ = (id: string) => document.getElementById(id) as T; +const controls = { + apiURL: $("apiURL"), deviceID: $("deviceID"), deviceSecret: $("deviceSecret"), + instanceID: $("instanceID"), productKey: $("productKey"), + volcDeviceName: $("volcDeviceName"), hardwareID: $("hardwareID"), + modelMode: $("modelMode"), + microphone: $("microphone"), check: $("checkButton"), start: $("startButton"), + stop: $("stopButton"), textQuestion: $("textQuestion"), sendText: $("sendTextButton"), +}; + +function setText(id: string, value: string) { $(id).textContent = value; } +function errorMessage(error: unknown) { return error instanceof Error ? error.message : String(error); } + +function recordEvent(source: ReportEvent["source"], type: string, detail?: Record) { + state.events.push({ at: new Date().toISOString(), source, type, detail }); + if (state.events.length > 1000) state.events.splice(0, state.events.length - 1000); +} + +function log(message: string, level: "info" | "success" | "error" = "info", source: ReportEvent["source"] = "app", type = "log") { + const item = document.createElement("li"); + item.className = level; + const time = document.createElement("time"); + time.textContent = new Date().toLocaleTimeString("zh-CN", { hour12: false }); + const content = document.createElement("span"); + content.textContent = message; + item.append(time, content); + $("eventLog").prepend(item); + recordEvent(source, type, { level, message }); +} + +function setPhase(phase: HardwarePhase, detail: string) { + state.phase = phase; + const labels: Record = { idle: "待机", checking: "检测中", ready: "已就绪", connecting: "连接中", active: "通话中", stopping: "结束中", completed: "已完成", error: "异常" }; + setText("phaseBadge", labels[phase]); + setText("orbLabel", detail); + setText("overallStatus", detail); + $("overallStatus").className = `overall phase-${phase}`; + $("conversationOrb").className = `conversation-orb phase-${phase}`; + controls.check.disabled = ["connecting", "active", "stopping"].includes(phase); + controls.start.disabled = phase !== "ready" && phase !== "completed"; + controls.stop.disabled = !["active", "error"].includes(phase) || !state.platformSession || !!state.failureTask; + controls.sendText.disabled = phase !== "active"; +} + +function setCheck(id: string, status: ReportCheck["status"], detail?: string) { + const check = state.checks.get(id); + if (!check) return; + check.status = status; + check.detail = detail; + check.at = new Date().toISOString(); + renderChecks(); +} + +function resetChecks() { + state.checks = new Map(checkDefinitions.map(([id, label]) => [id, { id, label, status: "pending" }])); + renderChecks(); +} + +function renderChecks() { + const list = $("flowChecks"); + list.replaceChildren(...[...state.checks.values()].map((check) => { + const item = document.createElement("li"); + item.className = `flow-check flow-check--${check.status}`; + const mark = document.createElement("i"); + mark.textContent = check.status === "passed" ? "✓" : check.status === "failed" ? "!" : "·"; + const content = document.createElement("span"); + content.textContent = check.label; + const detail = document.createElement("small"); + detail.textContent = check.detail || (check.status === "pending" ? "等待验证" : check.status === "passed" ? "已通过" : "未通过"); + item.append(mark, content, detail); + return item; + })); +} + +function updateMeter(kind: "mic" | "agent", value: number) { + const normalized = Math.max(0, Math.min(100, Math.round(value))); + $(`${kind}Meter`).style.width = `${normalized}%`; + setText(`${kind}Value`, String(normalized)); +} + +async function loadDefaults() { + const [platform, hardware] = await Promise.all([ + window.deviceSimulator.getRuntimeDefaults(), + window.deviceSimulator.getHardwareRuntimeDefaults(), + ]); + controls.apiURL.value = platform.apiURL; + controls.deviceID.value = platform.deviceID; + if (platform.deviceSecretConfigured) controls.deviceSecret.placeholder = "已从 Windows 安全凭据文件加载"; + controls.instanceID.value = hardware.instanceID; + controls.productKey.value = hardware.productKey; + controls.volcDeviceName.value = hardware.deviceName; + controls.hardwareID.value = hardware.hardwareID; + controls.modelMode.value = hardware.modelMode; + if (hardware.deviceCredentialConfigured) setText("credentialDetail", "本机已有平台签发的一机一密安全缓存"); +} + +async function enumerateMicrophones() { + const permission = await navigator.mediaDevices.getUserMedia({ audio: true }); + permission.getTracks().forEach((track) => track.stop()); + const devices = (await navigator.mediaDevices.enumerateDevices()).filter((device) => device.kind === "audioinput"); + controls.microphone.replaceChildren(new Option("默认麦克风", "")); + devices.forEach((device, index) => controls.microphone.add(new Option(device.label || `麦克风 ${index + 1}`, device.deviceId))); +} + +async function configureAndProbe() { + if (state.failureTask) await state.failureTask; + setPhase("checking", "正在鉴权和检测"); + resetChecks(); + state.events = []; + state.turns = []; + state.failure = undefined; + try { + await window.deviceSimulator.configure({ apiURL: controls.apiURL.value, deviceID: controls.deviceID.value, deviceSecret: controls.deviceSecret.value }); + await window.deviceSimulator.probe(); + setCheck("platform_authenticated", "passed", "设备 bootstrap/config HMAC 请求成功"); + setText("platformStatus", "鉴权成功"); + setText("platformDetail", controls.apiURL.value); + const hardware = await window.deviceSimulator.configureHardware({ hardwareID: controls.hardwareID.value }); + controls.instanceID.value = hardware.instanceID; + controls.productKey.value = hardware.productKey; + controls.volcDeviceName.value = hardware.deviceName; + controls.hardwareID.value = hardware.hardwareID; + setCheck("credential_resolved", "passed", `${hardware.credentialSource}${hardware.persisted ? ",Device Secret 已写入 Windows 安全存储" : ",仅本次内存"}`); + setText("credentialStatus", hardware.registered ? "平台注册成功" : "平台凭据可用"); + setText("credentialDetail", `${hardware.deviceName} · ${hardware.credentialSource} · 火山 License 额度已在动态注册时由服务端消费`); + const network = await window.deviceSimulator.probeHardwareNetwork(); + const failures = network.endpoints.filter((endpoint) => !endpoint.ok); + if (failures.length) throw new Error(`火山网络不可达:${failures.map((item) => `${item.hostname} ${item.error || "失败"}`).join(";")}`); + await enumerateMicrophones(); + setPhase("ready", "可以开始真实通话"); + log("平台 v3、平台代理注册、火山网关和麦克风检测通过。", "success"); + } catch (error) { + state.failure = errorMessage(error); + setPhase("error", "环境检测失败"); + setText("platformStatus", "检测失败"); + setText("platformDetail", state.failure); + log(`环境检测失败:${state.failure}`, "error"); + } +} + +function renderPlatformSession(created: CreatedHardwareAgentSession) { + setText("conversationID", created.conversation_id); + setText("botID", created.runtime.bot_id); + setText("transport", created.runtime.transport); + setText("configDigest", created.runtime.config_digest); + setText("promptVersion", created.session_config.prompt_bundle_version); + setText("providerConnectionRef", "等待火山连接"); +} + +async function startHardwareConversation() { + if (!["ready", "completed"].includes(state.phase)) return; + if (state.failureTask) await state.failureTask; + setPhase("connecting", "正在冻结本次上下文"); + resetSessionState(); + try { + const created = await window.deviceSimulator.createHardwareAgentSession(); + state.platformSession = created; + renderPlatformSession(created); + if (created.runtime.transport !== "low_load") throw new Error(`设备绑定传输模式为 ${created.runtime.transport},Windows 标准协议测试要求 low_load`); + if (!created.session_config.authorized_context || !created.runtime.config_digest || !created.runtime.session_nonce) throw new Error("平台未返回完整冻结上下文或会话凭证"); + setCheck("context_frozen", "passed", `${created.session_config.prompt_bundle_version} · ${created.runtime.config_digest.slice(0, 12)}…`); + recordEvent("platform", "hardware.session.ready", { conversation_id: created.conversation_id, bot_id: created.runtime.bot_id, transport: created.runtime.transport }); + setText("configStatus", "等待 session.updated"); + setText("configDetail", "麦克风仍保持关闭"); + const connected = await window.deviceSimulator.connectHardwareAgent({ platformSession: created, modelMode: controls.modelMode.value as "llm" | "s2s" }); + if (state.failureTask) { await state.failureTask; return; } + state.providerConnectionRef = connected.providerConnectionRef; + setText("providerConnectionRef", connected.providerConnectionRef); + setText("providerStatus", "WebSocket 已连接"); + setCheck("provider_connected", "passed", connected.providerConnectionRef); + setCheck("dynamic_config_applied", "passed", "火山已返回 session.updated"); + setText("configStatus", "已应用"); + setText("configDetail", "允许激活麦克风"); + state.eventSequence = 1; + const activated = await window.deviceSimulator.hardwareAgentEvent({ + conversationID: created.conversation_id, sequenceNo: state.eventSequence, eventType: "session_started", + providerSessionRef: created.runtime.provider_session_ref, sessionNonce: created.runtime.session_nonce, + providerConnectionRef: connected.providerConnectionRef, botID: created.runtime.bot_id, transport: created.runtime.transport, + configDigest: created.runtime.config_digest, dynamicConfigApplied: true, + }); + if (activated.status !== "ACTIVE" || activated.action !== "continue") throw new Error(`平台未激活硬件会话:${activated.status}/${activated.action}`); + if (state.failureTask) { await state.failureTask; return; } + setCheck("platform_active", "passed", "session_started 已进入 ACTIVE"); + await ensurePlaybackNode(); + await startMicrophone(); + if (state.failureTask) { await state.failureTask; await stopAudio(); return; } + setCheck("microphone_streaming", "passed", "PCM16/16kHz/单声道/20ms 分片"); + state.startedAt = Date.now(); + setPhase("active", "请开始说话"); + startTimers(created.session.heartbeat_interval_sec || 15); + log("硬件对话智能体已激活:动态配置确认后才开放麦克风。", "success", "platform", "hardware.session.active"); + } catch (error) { + state.failure ||= errorMessage(error); + log(`启动硬件智能体失败:${state.failure}`, "error"); + await failHardwareConversation("WINDOWS_HARDWARE_START_FAILED"); + } +} + +function resetSessionState() { + stopTimers(); + state.platformSession = undefined; + state.providerConnectionRef = undefined; + state.eventSequence = 0; + state.transcriptSequence = 0; + state.pendingTurns = []; + state.transcriptUpload = undefined; + state.startedAt = undefined; + state.stopping = false; + state.intentionalProviderClose = false; + state.failureTask = undefined; + state.failure = undefined; + state.responseTranscripts.clear(); + state.speechStartedAt = undefined; + state.turns = []; + state.playbackPendingByte = undefined; + state.playbackChunks = 0; + state.playbackSamples = 0; + state.playbackUnderflows = 0; + // Device preparation remains valid, but every call must earn its own checks. + for (const [id, label] of checkDefinitions) { + if (!["platform_authenticated", "credential_resolved"].includes(id)) { + state.checks.set(id, { id, label, status: "pending" }); + } + } + renderChecks(); + setText("duration", "00:00"); + setText("providerStatus", "网关未连接"); + setText("providerConnectionRef", "—"); + setText("conversationID", "—"); + setText("configStatus", "未应用"); + setText("configDetail", "麦克风保持关闭"); + $("liveTranscript").innerHTML = '
  1. 通话开始后显示火山返回的用户和智能体最终字幕。
  2. '; + setText("transcriptStatus", "0 条"); + updateMeter("mic", 0); + updateMeter("agent", 0); +} + +async function startMicrophone() { + const constraints: MediaTrackConstraints = { channelCount: 1, echoCancellation: true, noiseSuppression: true, autoGainControl: true }; + if (controls.microphone.value) constraints.deviceId = { exact: controls.microphone.value }; + const stream = await navigator.mediaDevices.getUserMedia({ audio: constraints }); + const context = new AudioContext({ latencyHint: "interactive" }); + await context.audioWorklet.addModule(new URL("./pcm-capture.worklet.js", window.location.href).toString()); + const source = context.createMediaStreamSource(stream); + const node = new AudioWorkletNode(context, "pcm16-capture", { processorOptions: { targetSampleRate: 16000, frameSamples: 320 } }); + const silent = context.createGain(); + silent.gain.value = 0; + node.port.onmessage = (message) => { + if (state.phase !== "active" && state.phase !== "connecting") return; + if (message.data?.type !== "pcm" || !(message.data.bytes instanceof ArrayBuffer)) return; + updateMeter("mic", Number(message.data.volume || 0)); + window.deviceSimulator.sendHardwareAgentAudio(message.data.bytes); + }; + source.connect(node).connect(silent).connect(context.destination); + await context.resume(); + state.microphoneStream = stream; + state.captureContext = context; + state.captureNode = node; +} + +async function stopAudio() { + state.microphoneStream?.getTracks().forEach((track) => track.stop()); + state.captureNode?.disconnect(); + state.playbackNode?.port.postMessage({ type: "reset" }); + state.playbackNode?.disconnect(); + if (state.captureContext) try { await state.captureContext.close(); } catch { /* no-op */ } + if (state.playbackContext) try { await state.playbackContext.close(); } catch { /* no-op */ } + state.microphoneStream = undefined; + state.captureNode = undefined; + state.captureContext = undefined; + state.playbackNode = undefined; + state.playbackReady = undefined; + state.playbackContext = undefined; + state.playbackPendingByte = undefined; + updateMeter("mic", 0); + updateMeter("agent", 0); +} + +function ensurePlaybackNode(): Promise { + if (state.playbackNode) return Promise.resolve(state.playbackNode); + if (state.playbackReady) return state.playbackReady; + state.playbackReady = (async () => { + const context = new AudioContext({ latencyHint: "interactive" }); + state.playbackContext = context; + await context.audioWorklet.addModule(new URL("./pcm-playback.worklet.js", window.location.href).toString()); + const node = new AudioWorkletNode(context, "pcm16-playback", { + processorOptions: { sourceSampleRate: 16000, prebufferMs: 120 }, + outputChannelCount: [1], + }); + node.port.onmessage = (message) => { + if (message.data?.type === "underflow") { + state.playbackUnderflows = Number(message.data.count || state.playbackUnderflows + 1); + recordEvent("app", "audio.playback.underflow", { count: state.playbackUnderflows }); + } + if (message.data?.type === "drained") { + updateMeter("agent", 0); + setCheck("audio_playback_buffered", "passed", `${state.playbackChunks} 个分片 · ${state.playbackSamples} 个采样 · 欠载 ${state.playbackUnderflows} 次`); + } + }; + node.connect(context.destination); + await context.resume(); + state.playbackNode = node; + return node; + })().catch(async (error) => { + const context = state.playbackContext; + state.playbackReady = undefined; + state.playbackContext = undefined; + if (context) try { await context.close(); } catch { /* no-op */ } + throw error; + }); + return state.playbackReady; +} + +async function playProviderAudio(base64: string) { + if (!base64) return; + const binary = atob(base64); + const bytes = new Uint8Array(binary.length); + for (let index = 0; index < binary.length; index += 1) bytes[index] = binary.charCodeAt(index); + const totalBytes = bytes.byteLength + (state.playbackPendingByte === undefined ? 0 : 1); + const sampleCount = Math.floor(totalBytes / 2); + if (sampleCount === 0) { + if (bytes.byteLength === 1) state.playbackPendingByte = bytes[0]; + return; + } + const samples = new Float32Array(sampleCount); + let energy = 0; + let byteIndex = 0; + let sampleIndex = 0; + if (state.playbackPendingByte !== undefined) { + const value = new DataView(new Uint8Array([state.playbackPendingByte, bytes[0]]).buffer).getInt16(0, true) / 32768; + samples[sampleIndex] = value; + energy += value * value; + sampleIndex += 1; + byteIndex = 1; + state.playbackPendingByte = undefined; + } + const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength); + while (sampleIndex < sampleCount) { + const value = view.getInt16(byteIndex, true) / 32768; + samples[sampleIndex] = value; + energy += value * value; + sampleIndex += 1; + byteIndex += 2; + } + if (byteIndex < bytes.byteLength) state.playbackPendingByte = bytes[byteIndex]; + updateMeter("agent", Math.sqrt(energy / Math.max(1, sampleCount)) * 280); + state.playbackChunks += 1; + state.playbackSamples += sampleCount; + const node = await ensurePlaybackNode(); + node.port.postMessage({ type: "push", samples: samples.buffer }, [samples.buffer]); + setCheck("audio_playback_buffered", "passed", `已接收并缓冲 ${state.playbackChunks} 个火山音频分片`); +} + +function sanitizeProviderEvent(event: Record) { + const redact = (value: unknown, key = ""): unknown => { + if (/systemmessages|system_role|system_prompt|instructions|authorized_context|speaking_style/i.test(key)) return "[REDACTED]"; + if (Array.isArray(value)) return value.map((item) => redact(item)); + if (!value || typeof value !== "object") return value; + return Object.fromEntries(Object.entries(value as Record).map(([itemKey, item]) => [itemKey, redact(item, itemKey)])); + }; + const copy = redact(event) as Record; + if (typeof copy.delta === "string" && event.type === "response.audio.delta") copy.delta = ``; + return copy; +} + +async function handleProviderEvent(event: Record) { + const type = String(event.type || "unknown"); + recordEvent("volcengine", type, sanitizeProviderEvent(event)); + if (type !== "response.audio.delta") log(`火山事件:${type}`, type === "error" ? "error" : "info", "volcengine", type); + // Teardown can still deliver queued provider events. Do not revive a failed call. + if (state.failureTask && type !== "transport.close") return; + switch (type) { + case "session.created": setText("providerStatus", "会话已创建"); break; + case "session.updated": setText("configStatus", "已确认"); break; + case "input_audio_buffer.speech_started": state.speechStartedAt = Date.now(); setPhase("active", "检测到用户说话"); break; + case "input_audio_buffer.speech_stopped": setPhase("active", "智能体正在思考"); break; + case "response.created": setPhase("active", "智能体正在生成"); break; + case "response.audio.delta": await playProviderAudio(String(event.delta || "")); break; + case "response.audio.done": state.playbackNode?.port.postMessage({ type: "end" }); break; + case "conversation.item.input_audio_transcription.completed": + await appendFinalTurn("subject", String(event.transcript || ""), String(event.event_id || crypto.randomUUID()), state.speechStartedAt); state.speechStartedAt = undefined; break; + case "response.audio_transcript.delta": { + const responseID = String(event.response_id || "unknown"); + state.responseTranscripts.set(responseID, `${state.responseTranscripts.get(responseID) || ""}${String(event.delta || "")}`); + break; + } + case "response.audio_transcript.done": { + const responseID = String(event.response_id || "unknown"); + const transcript = String(event.transcript || state.responseTranscripts.get(responseID) || ""); + state.responseTranscripts.delete(responseID); + await appendFinalTurn("agent", transcript, responseID); break; + } + case "response.done": setPhase("active", "继续聆听"); break; + case "platform.memory_search.completed": log(`平台记忆工具返回 ${Number(event.result_count || 0)} 条已授权记忆。`, "success", "platform", type); break; + case "platform.memory_search.failed": log(`平台记忆工具降级:${String(event.message || "未知错误")}`, "error", "platform", type); break; + case "error": { + const providerError = event.error as Record | undefined; + state.failure ||= `${String(providerError?.code || providerError?.type || "PROVIDER_ERROR")}: ${String(providerError?.message || "火山智能体错误")}`; + log(`火山错误:${state.failure}`, "error", "volcengine", "provider.error"); + await failHardwareConversation("VOLC_HARDWARE_AGENT_ERROR"); + break; + } + case "transport.close": + setText("providerStatus", "连接已关闭"); + if (state.failureTask) setCheck("provider_closed", "failed", `失败清理关闭:WebSocket ${Number(event.code || 1000)}`); + else if (state.intentionalProviderClose) setCheck("provider_closed", "passed", `WebSocket ${Number(event.code || 1000)}`); + else if (["connecting", "active"].includes(state.phase)) { state.failure = `火山连接意外关闭:${String(event.code || "")}`; await failHardwareConversation("VOLC_CONNECTION_CLOSED"); } + break; + } +} + +async function appendFinalTurn(speaker: "subject" | "agent", rawText: string, sourceID: string, startedAt?: number) { + const text = rawText.trim(); + if (!text || !state.platformSession) return; + state.transcriptSequence += 1; + const now = Date.now(); + const turn: DeviceFinalTurn = { + turn_key: `hardware-${speaker}-${sourceID}`.slice(0, 240), sequence_no: state.transcriptSequence, speaker, + start_at: new Date(startedAt || now).toISOString(), end_at: new Date(now).toISOString(), final_text: text, interrupted: false, + }; + state.pendingTurns.push(turn); + state.turns.push(turn); + renderTurn(turn); + setCheck(speaker === "subject" ? "user_transcript" : "agent_transcript", "passed", text.slice(0, 50)); + await flushTurns(); +} + +function renderTurn(turn: DeviceFinalTurn) { + const list = $("liveTranscript"); + list.querySelector(".transcript-empty")?.remove(); + const item = document.createElement("li"); + item.className = `transcript-turn transcript-turn--${turn.speaker}`; + const speaker = document.createElement("strong"); speaker.textContent = turn.speaker === "subject" ? "用户" : "智能体"; + const text = document.createElement("span"); text.textContent = turn.final_text; + item.append(speaker, text); list.append(item); + setText("transcriptStatus", `${state.turns.length} 条`); +} + +async function flushTurns() { + if (state.transcriptUpload) return state.transcriptUpload; + if (!state.platformSession || state.pendingTurns.length === 0) return; + const batch = state.pendingTurns.slice(0, 500); + state.transcriptUpload = (async () => { + try { + const result = await window.deviceSimulator.submitTranscriptBatch({ conversationID: state.platformSession!.conversation_id, turns: batch, hardwareAgent: true }); + state.pendingTurns.splice(0, batch.length); + setCheck("transcript_uploaded", "passed", `新增 ${result.accepted_count},重复 ${result.duplicate_count}`); + log(`最终字幕已上传平台:新增 ${result.accepted_count},重复 ${result.duplicate_count}。`, "success", "platform", "hardware.turns.uploaded"); + } catch (error) { log(`字幕上传失败,保留队列待重试:${errorMessage(error)}`, "error"); } + finally { state.transcriptUpload = undefined; } + })(); + await state.transcriptUpload; +} + +async function flushTurnsWithRetry(attempts: number) { + for (let attempt = 1; attempt <= attempts && state.pendingTurns.length > 0; attempt += 1) { + await flushTurns(); + if (state.pendingTurns.length && attempt < attempts) await new Promise((resolve) => window.setTimeout(resolve, attempt * 500)); + } + if (state.pendingTurns.length) throw new Error(`仍有 ${state.pendingTurns.length} 条最终字幕未上传`); +} + +function startTimers(intervalSeconds: number) { + stopTimers(); + state.durationTimer = window.setInterval(() => { + if (!state.startedAt) return; + const seconds = Math.floor((Date.now() - state.startedAt) / 1000); + setText("duration", `${String(Math.floor(seconds / 60)).padStart(2, "0")}:${String(seconds % 60).padStart(2, "0")}`); + }, 1000); + state.heartbeatTimer = window.setInterval(() => void sendHardwareHeartbeat(), Math.max(5, intervalSeconds) * 1000); + void sendHardwareHeartbeat(); +} + +function stopTimers() { + if (state.durationTimer) window.clearInterval(state.durationTimer); + if (state.heartbeatTimer) window.clearInterval(state.heartbeatTimer); + state.durationTimer = undefined; + state.heartbeatTimer = undefined; +} + +async function sendHardwareHeartbeat() { + const created = state.platformSession; + if (!created || state.phase !== "active" || state.stopping) return; + try { + state.eventSequence += 1; + const result = await window.deviceSimulator.hardwareAgentEvent({ + conversationID: created.conversation_id, sequenceNo: state.eventSequence, eventType: "session_updated", + providerSessionRef: created.runtime.provider_session_ref, sessionNonce: created.runtime.session_nonce, + }); + setCheck("heartbeat_ack", "passed", `序号 ${state.eventSequence} · ${result.action}`); + if (result.action === "stop_session") await stopHardwareConversation("platform_stop_session"); + await flushTurns(); + } catch (error) { log(`硬件会话存活上报失败:${errorMessage(error)}`, "error"); } +} + +async function stopHardwareConversation(reason = "windows_user_hangup") { + if (state.failureTask) return state.failureTask; + if (!state.platformSession || state.stopping) return; + state.stopping = true; + setPhase("stopping", "正在可靠结束"); + stopTimers(); + const created = state.platformSession; + try { + await stopAudio(); + await flushTurnsWithRetry(3); + state.intentionalProviderClose = true; + await window.deviceSimulator.closeHardwareAgent({ code: 1000, reason }); + setCheck("provider_closed", "passed", "设备主动关闭 WebSocket"); + state.eventSequence += 1; + const ended = await window.deviceSimulator.hardwareAgentEvent({ + conversationID: created.conversation_id, sequenceNo: state.eventSequence, eventType: "session_ended", + providerSessionRef: created.runtime.provider_session_ref, sessionNonce: created.runtime.session_nonce, + }); + if (ended.status !== "COMPLETED") throw new Error(`平台结束状态不是 COMPLETED:${ended.status}`); + setCheck("platform_completed", "passed", "session_ended 已进入 COMPLETED"); + state.stopping = false; + setPhase("completed", allRequiredChecksPassed() ? "完整流程通过" : "通话完成,部分验收项待覆盖"); + log("硬件对话智能体会话已正常结束,平台状态为 COMPLETED。", "success", "platform", "hardware.session.completed"); + } catch (error) { + state.failure = errorMessage(error); + state.stopping = false; + setPhase("error", "结束流程失败"); + log(`结束硬件会话失败:${state.failure}`, "error"); + } +} + +function failHardwareConversation(errorCode: string): Promise { + // The provider error event and connect() rejection describe the same failure. + // Install a shared task before any asynchronous cleanup, and retain it until + // the next call so later errors/hangup cannot submit another terminal event. + if (state.failureTask) return state.failureTask; + const created = state.platformSession; + state.failureTask = Promise.resolve().then(async () => { + try { + await stopAudio(); + try { await window.deviceSimulator.closeHardwareAgent({ code: 1011, reason: errorCode }); } catch { /* no-op */ } + if (created) { + try { + state.eventSequence += 1; + await window.deviceSimulator.hardwareAgentEvent({ + conversationID: created.conversation_id, sequenceNo: state.eventSequence, eventType: "session_failed", + providerSessionRef: created.runtime.provider_session_ref, sessionNonce: created.runtime.session_nonce, + errorCode, + }); + } catch (error) { log(`平台失败事件回报未成功:${errorMessage(error)}`, "error"); } + } + } finally { + state.stopping = false; + setPhase("error", state.failure || "硬件会话失败"); + } + }); + stopTimers(); + state.stopping = true; + state.intentionalProviderClose = true; + setPhase("stopping", "正在清理失败会话"); + setCheck("platform_completed", "failed", state.failure || errorCode); + if (!state.providerConnectionRef) { + setText("configStatus", "未完成"); + setCheck("dynamic_config_applied", "failed", state.failure || errorCode); + } + return state.failureTask; +} + +function allRequiredChecksPassed() { + const required = ["platform_authenticated", "credential_resolved", "context_frozen", "provider_connected", "dynamic_config_applied", "platform_active", "microphone_streaming", "audio_playback_buffered", "heartbeat_ack", "transcript_uploaded", "provider_closed", "platform_completed"]; + return required.every((id) => state.checks.get(id)?.status === "passed"); +} + +async function sendTextQuestion() { + if (state.phase !== "active" || state.failureTask) return; + const text = controls.textQuestion.value.trim(); + if (!text) return; + try { + await window.deviceSimulator.sendHardwareAgentText({ text, interruptMode: 1 }); + // 文本输入没有 ASR final 事件,设备本地将已被火山接受的最终输入作为用户话轮上报, + // 这样文本验收与真实语音验收都会进入同一套平台聊天记录。 + await appendFinalTurn("subject", text, `text-${crypto.randomUUID()}`); + controls.textQuestion.value = ""; + log(`已通过硬件标准协议发送文本问题:${text}`, "success", "app", "hardware.text.sent"); + } catch (error) { log(`发送文本问题失败:${errorMessage(error)}`, "error"); } +} + +function buildReport(): Record { + return { + schema_version: "hardware-agent-windows-e2e-report-v1", + generated_at: new Date().toISOString(), + result: state.phase === "completed" && allRequiredChecksPassed() ? "PASS" : state.failure ? "FAIL" : "INCOMPLETE", + platform_api: controls.apiURL.value, + platform_device_id: controls.deviceID.value, + volc_identity: { instance_id: controls.instanceID.value, product_key: controls.productKey.value, device_name: controls.volcDeviceName.value, hardware_id: controls.hardwareID.value }, + conversation_id: state.platformSession?.conversation_id, + bot_id: state.platformSession?.runtime.bot_id, + model_mode: controls.modelMode.value, + transport: state.platformSession?.runtime.transport, + audio_playback: { + source_format: "pcm_s16le_16000_mono", + mode: "audio_worklet_ring_buffer", + prebuffer_ms: 120, + chunks: state.playbackChunks, + samples: state.playbackSamples, + underflows: state.playbackUnderflows, + }, + provider_connection_ref: state.providerConnectionRef, + config_digest: state.platformSession?.runtime.config_digest, + prompt_bundle_version: state.platformSession?.session_config.prompt_bundle_version, + failure: state.failure, + checks: [...state.checks.values()], + turns: state.turns, + events: state.events, + }; +} + +async function exportReport() { + try { + const result = await window.deviceSimulator.saveHardwareReport(buildReport()); + if (result.saved) log(`测试报告已保存:${result.path}`, "success"); + } catch (error) { log(`导出测试报告失败:${errorMessage(error)}`, "error"); } +} + +const unsubscribeProvider = window.deviceSimulator.onHardwareProviderEvent((event) => void handleProviderEvent(event)); +controls.check.addEventListener("click", () => void configureAndProbe()); +controls.start.addEventListener("click", () => void startHardwareConversation()); +controls.stop.addEventListener("click", () => void stopHardwareConversation()); +controls.sendText.addEventListener("click", () => void sendTextQuestion()); +controls.textQuestion.addEventListener("keydown", (event) => { if (event.key === "Enter") void sendTextQuestion(); }); +$("exportReport").addEventListener("click", () => void exportReport()); +$("clearLog").addEventListener("click", () => $("eventLog").replaceChildren()); +window.addEventListener("beforeunload", () => { + unsubscribeProvider(); + stopTimers(); + void stopAudio(); + void window.deviceSimulator.closeHardwareAgent({ code: 1001, reason: "window closed" }); +}); + +renderChecks(); +setPhase("idle", "尚未检测"); +void loadDefaults().catch((error) => log(`读取默认配置失败:${errorMessage(error)}`, "error")); diff --git a/device-simulator-windows/src/style.css b/device-simulator-windows/src/style.css index 2419599..0447414 100644 --- a/device-simulator-windows/src/style.css +++ b/device-simulator-windows/src/style.css @@ -16,10 +16,15 @@ button:disabled { cursor: not-allowed; opacity: .45; } .eyebrow { color: #c8652a; font-size: 12px; font-weight: 800; letter-spacing: .16em; margin: 0 0 9px; } h1 { margin: 0; font-family: Georgia, "Microsoft YaHei UI", serif; font-size: 44px; letter-spacing: -.035em; } h2 { margin: 0; font-size: 23px; } +h3 { margin: 24px 0 14px; color: #315966; font-size: 14px; } .subtitle { max-width: 760px; color: #66808e; line-height: 1.8; margin: 12px 0 0; } .overall { display: flex; align-items: center; min-width: 150px; justify-content: center; padding: 13px 18px; border-radius: 999px; background: rgba(255,255,255,.82); box-shadow: 0 12px 30px rgba(36,64,69,.1); color: #647a82; } .overall.phase-ready, .overall.phase-active { color: #1a7d57; } .overall.phase-error { color: #b63d2e; } +.overall.phase-completed { color: #1a7d57; } +.hero-actions { display: grid; justify-items: end; gap: 10px; } +.legacy-link { color: #617985; font-size: 12px; text-decoration: none; border-bottom: 1px solid #b9c8c7; padding-bottom: 2px; } +.legacy-link:hover { color: #c8652a; } .status-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; margin-bottom: 16px; } .status-card, .panel { background: rgba(255,255,255,.9); border: 1px solid rgba(23,61,69,.08); box-shadow: 0 14px 40px rgba(34,61,63,.08); } @@ -77,7 +82,27 @@ input:focus, select:focus { border-color: #d6783f; box-shadow: 0 0 0 3px rgba(21 .transcript-turn strong { color: #315966; font-size: 12px; } .transcript-turn span { color: #263f49; font-size: 14px; line-height: 1.55; } +.hardware-shell { width: min(1260px, calc(100vw - 48px)); } +.hardware-content-grid { grid-template-columns: 1.05fr 1.2fr; } +.hardware-call-panel { min-height: 720px; } +.hardware-session-info { grid-template-columns: 1fr 1fr; } +.text-question { display: grid; grid-template-columns: 1fr 150px; gap: 10px; margin-bottom: 22px; } +.text-question input { margin-top: 0; } +.text-question .secondary { padding: 10px; } +.flow-panel { margin-top: 16px; } +.flow-panel .panel-heading { margin-bottom: 14px; } +.flow-checks { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; list-style: none; padding: 0; margin: 0; } +.flow-check { display: grid; grid-template-columns: 28px 1fr; grid-template-rows: auto auto; column-gap: 10px; align-items: center; padding: 13px; border: 1px solid #e3ebe8; border-radius: 14px; background: #fafcfb; } +.flow-check i { grid-row: 1 / span 2; display: grid; place-items: center; width: 26px; height: 26px; border-radius: 50%; font-style: normal; font-weight: 800; color: #809399; background: #e8efed; } +.flow-check span { min-width: 0; color: #31505e; font-size: 13px; font-weight: 700; } +.flow-check small { min-width: 0; margin-top: 4px; color: #8b9ba1; font-size: 11px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } +.flow-check--passed { border-color: #cce5d7; background: #f4faf6; } +.flow-check--passed i { color: white; background: #2f8d67; } +.flow-check--failed { border-color: #edcbc5; background: #fff7f5; } +.flow-check--failed i { color: white; background: #b94c3d; } + @media (max-width: 1050px) { .status-grid { grid-template-columns: repeat(2, 1fr); } .content-grid { grid-template-columns: 1fr; } + .flow-checks { grid-template-columns: repeat(2, minmax(0, 1fr)); } } diff --git a/device-simulator-windows/test/mock-hardware-gateway.cjs b/device-simulator-windows/test/mock-hardware-gateway.cjs new file mode 100644 index 0000000..6e639b0 --- /dev/null +++ b/device-simulator-windows/test/mock-hardware-gateway.cjs @@ -0,0 +1,108 @@ +const crypto = require("node:crypto"); +const { WebSocketServer } = require("ws"); +const { websocketSignature } = require("../electron/hardware-agent-client.cjs"); + +const port = Number(process.env.MOCK_HARDWARE_GATEWAY_PORT || 19090); +const expectedSecret = process.env.MOCK_VOLC_DEVICE_SECRET || "mock-volc-device-secret-at-least-32-characters"; +const server = new WebSocketServer({ host: "127.0.0.1", port, maxPayload: 1024 * 1024 }); + +function send(socket, value) { socket.send(JSON.stringify(value)); } +async function requestPlatformStop(conversationID) { + const baseURL = String(process.env.MOCK_PLATFORM_API_URL || "").replace(/\/$/, ""); + const deviceID = String(process.env.MOCK_PLATFORM_DEVICE_ID || ""); + const secret = String(process.env.MOCK_PLATFORM_DEVICE_SECRET || ""); + if (!baseURL || !deviceID || !secret || !conversationID) return; + const requestPath = `/device/v2/conversations/${encodeURIComponent(conversationID)}/stop`; + const body = JSON.stringify({ reason: "windows_e2e_platform_stop", device_time: new Date().toISOString(), last_heartbeat_sequence: 1, rtc_left: true, recording: { near_segments: 0, far_segments: 0 } }); + const timestamp = new Date().toISOString(); + const nonce = crypto.randomBytes(16).toString("hex"); + const digest = crypto.createHash("sha256").update(body).digest("hex"); + const canonical = ["POST", requestPath, timestamp, nonce, digest].join("\n"); + const signature = crypto.createHmac("sha256", secret).update(canonical).digest("base64url"); + const response = await fetch(`${baseURL}${requestPath}`, { + method: "POST", + headers: { + Accept: "application/json", "Content-Type": "application/json", "Idempotency-Key": `windows-platform-stop-${crypto.randomUUID()}`, + "X-Device-Id": deviceID, "X-Device-Timestamp": timestamp, "X-Device-Nonce": nonce, + "X-Device-Content-SHA256": digest, "X-Device-Signature": signature, + }, + body, + }); + if (!response.ok) throw new Error(`platform stop HTTP ${response.status}: ${await response.text()}`); + process.stdout.write(`${JSON.stringify({ type: "platform_stop_requested", conversation_id: conversationID })}\n`); +} +function tonePCMBase64() { + const samples = 2400; + const bytes = Buffer.alloc(samples * 2); + for (let index = 0; index < samples; index += 1) { + const sample = Math.round(Math.sin((index / 16000) * Math.PI * 2 * 440) * 1800); + bytes.writeInt16LE(sample, index * 2); + } + return bytes.toString("base64"); +} + +server.on("listening", () => process.stdout.write(`${JSON.stringify({ type: "listening", port })}\n`)); +server.on("connection", (socket, request) => { + const header = (name) => String(request.headers[name.toLowerCase()] || ""); + const expectedSignature = websocketSignature({ + authType: 1, + deviceName: header("x-device-name"), + randomNumber: Number(header("x-random-num")), + productKey: header("x-product-key"), + timestamp: Number(header("x-timestamp")), + instanceID: header("x-instance-id"), + }, expectedSecret); + if (!crypto.timingSafeEqual(Buffer.from(header("x-signature")), Buffer.from(expectedSignature))) { + socket.close(1008, "invalid signature"); + return; + } + const target = new URL(request.url, `ws://127.0.0.1:${port}`); + if (!target.searchParams.get("bot") || target.searchParams.get("wait_for_session_update") !== "true") { + socket.close(1008, "invalid query"); + return; + } + process.stdout.write(`${JSON.stringify({ type: "authenticated", device: header("x-device-name"), bot: target.searchParams.get("bot") })}\n`); + send(socket, { type: "session.created", event_id: "mock-created", session: { id: "mock-provider-session-windows", model: "doubao", input_audio_format: "pcm16", output_audio_format: "pcm16", turn_detection: { type: "server_vad" } } }); + let replied = false; + let dialogID = ""; + socket.on("message", (raw) => { + const event = JSON.parse(raw.toString("utf8")); + if (event.type === "session.update") { + const llmPrompt = event.session?.config?.LLMConfig?.SystemMessages?.[0]; + const s2sDialog = event.session?.config?.S2SConfig?.ProviderParams?.dialog; + const sessionDialogID = String(s2sDialog?.dialog_id || (llmPrompt ? llmPrompt.match(/本次对话标识:([^\n]+)/)?.[1] : "") || "").trim(); + const frozenPrompt = String(llmPrompt || s2sDialog?.system_role || "").trim(); + if (event.session?.object !== "realtime.session" || event.session?.config?.ASRConfig?.TurnDetectionMode !== 0 || !sessionDialogID || !frozenPrompt) { + send(socket, { type: "error", event_id: "mock-error", error: { code: "INVALID_SESSION_CONFIG", message: "frozen context missing" } }); + return; + } + dialogID = sessionDialogID; + send(socket, { type: "session.updated", event_id: "mock-updated", session: { ...event.session, id: "mock-provider-session-windows" } }); + process.stdout.write(`${JSON.stringify({ type: "dynamic_config_applied", dialog_id: dialogID })}\n`); + return; + } + if (event.type === "input_audio_buffer.append" && !replied) { + replied = true; + send(socket, { type: "input_audio_buffer.speech_started", event_id: "mock-speech-start", audio_start_ms: 0 }); + send(socket, { type: "input_audio_buffer.speech_stopped", event_id: "mock-speech-stop", audio_end_ms: 800 }); + send(socket, { type: "conversation.item.input_audio_transcription.completed", event_id: "mock-user-final", item_id: "mock-user-item", transcript: "你好,请介绍一下今天的安排。" }); + send(socket, { type: "response.created", event_id: "mock-response-created", response: { id: "mock-response-1", status: "in_progress" } }); + send(socket, { type: "response.audio_transcript.delta", event_id: "mock-agent-delta", response_id: "mock-response-1", delta: "您好,今天可以先聊聊您最想做的事情。" }); + send(socket, { type: "response.audio.delta", event_id: "mock-audio-delta", response_id: "mock-response-1", delta: tonePCMBase64() }); + send(socket, { type: "response.audio_transcript.done", event_id: "mock-agent-final", response_id: "mock-response-1", transcript: "您好,今天可以先聊聊您最想做的事情。" }); + send(socket, { type: "response.audio.done", event_id: "mock-audio-done", response_id: "mock-response-1" }); + send(socket, { type: "response.done", event_id: "mock-response-done", response: { id: "mock-response-1", status: "completed", output: [] } }); + process.stdout.write(`${JSON.stringify({ type: "round_completed" })}\n`); + if (process.env.MOCK_REQUEST_PLATFORM_STOP === "1") { + setTimeout(() => void requestPlatformStop(dialogID).catch((error) => { + process.stderr.write(`${error.stack || error.message}\n`); + }), 250); + } + } + }); + socket.on("close", (code) => process.stdout.write(`${JSON.stringify({ type: "closed", code })}\n`)); +}); + +function shutdown() { server.close(() => process.exit(0)); } +process.on("SIGINT", shutdown); +process.on("SIGTERM", shutdown); diff --git a/device-simulator-windows/test/run-hardware-e2e.ps1 b/device-simulator-windows/test/run-hardware-e2e.ps1 new file mode 100644 index 0000000..6c3c3d7 --- /dev/null +++ b/device-simulator-windows/test/run-hardware-e2e.ps1 @@ -0,0 +1,120 @@ +param( + [ValidateSet('llm', 's2s')] + [string]$ModelMode = 'llm', + [string]$ExecutablePath = '' +) + +$ErrorActionPreference = 'Stop' +$PSNativeCommandUseErrorActionPreference = $true + +$simulatorRoot = (Resolve-Path -LiteralPath (Join-Path $PSScriptRoot '..')).Path +$projectRoot = (Resolve-Path -LiteralPath (Join-Path $simulatorRoot '..')).Path +$devicePublicID = '10000000-0000-0000-0000-000000000511' +$platformSecret = 'local-simulated-device-secret-32-bytes-minimum' +$volcSecret = 'mock-volc-device-secret-at-least-32-characters' +$mockPort = 19090 +$runID = [Guid]::NewGuid().ToString('N') +$resultPath = Join-Path $env:TEMP "hardware-agent-e2e-$runID.json" +$mockOutput = Join-Path $env:TEMP "hardware-agent-gateway-$runID.log" +$mockError = Join-Path $env:TEMP "hardware-agent-gateway-$runID.err.log" +$mockProcess = $null +$conversationID = $null +$deviceInternalID = $null + +Set-Location -LiteralPath $projectRoot +try { + $ready = Invoke-WebRequest -UseBasicParsing -Uri 'http://localhost:58080/health/ready' -TimeoutSec 10 + if ($ready.StatusCode -ne 200) { throw 'Local API is not ready' } + $deviceInternalID = (docker compose exec -T postgres psql -U voice -d voice_platform -t -A -c "SELECT id FROM device WHERE public_id='$devicePublicID'::uuid" | Out-String).Trim() + if ($deviceInternalID -notmatch '^[0-9a-fA-F-]{36}$') { throw 'Local simulator device is missing' } + $bindingCount = (docker compose exec -T postgres psql -U voice -d voice_platform -t -A -c "SELECT count(*) FROM hardware_agent_device_binding WHERE device_id='$deviceInternalID'::uuid" | Out-String).Trim() + if ($bindingCount -ne '0') { throw 'Simulator device already has a hardware binding; refusing to overwrite it' } + "UPDATE device_twin SET desired_config=desired_config||'{`"voice_runtime`":`"volc_hardware_agent`",`"hardware_agent_transport`":`"low_load`",`"minimum_sdk_version`":`"1.0.0`"}'::jsonb WHERE device_id='$deviceInternalID'::uuid; INSERT INTO hardware_agent_device_binding(tenant_id,device_id,bot_id,transport,minimum_sdk_version,status) SELECT tenant_id,id,'mock-windows-hardware-bot','low_load','1.0.0','ACTIVE' FROM device WHERE id='$deviceInternalID'::uuid;" | docker compose exec -T postgres psql -U voice -d voice_platform -v ON_ERROR_STOP=1 -q | Out-Null + + $mockScript = Join-Path $simulatorRoot 'test\mock-hardware-gateway.cjs' + $env:MOCK_HARDWARE_GATEWAY_PORT = [string]$mockPort + $env:MOCK_VOLC_DEVICE_SECRET = $volcSecret + $env:MOCK_REQUEST_PLATFORM_STOP = '1' + $env:MOCK_PLATFORM_API_URL = 'http://localhost:58080' + $env:MOCK_PLATFORM_DEVICE_ID = $devicePublicID + $env:MOCK_PLATFORM_DEVICE_SECRET = $platformSecret + $mockProcess = Start-Process -FilePath 'node' -ArgumentList @($mockScript) -PassThru -WindowStyle Hidden -RedirectStandardOutput $mockOutput -RedirectStandardError $mockError + $deadline = [DateTime]::UtcNow.AddSeconds(10) + do { + Start-Sleep -Milliseconds 150 + $listening = Test-NetConnection 127.0.0.1 -Port $mockPort -InformationLevel Quiet -WarningAction SilentlyContinue + if ($mockProcess.HasExited) { throw "Mock gateway exited: $(Get-Content -LiteralPath $mockError -Raw -ErrorAction SilentlyContinue)" } + } until ($listening -or [DateTime]::UtcNow -gt $deadline) + if (-not $listening) { throw 'Mock hardware gateway did not start' } + + $env:SIMULATOR_AUTO_TEST = '1' + $env:SIMULATOR_AUTO_WAIT_FOR_PLATFORM_STOP = '1' + $env:SIMULATOR_AUTO_RESULT_PATH = $resultPath + $env:SIMULATOR_DEFAULT_API_URL = 'http://localhost:58080' + $env:SIMULATOR_DEFAULT_DEVICE_ID = $devicePublicID + $env:SIMULATOR_DEFAULT_DEVICE_SECRET = $platformSecret + $env:SIMULATOR_VOLC_INSTANCE_ID = 'mock-instance' + $env:SIMULATOR_VOLC_PRODUCT_KEY = 'mock-product' + $env:SIMULATOR_VOLC_DEVICE_NAME = 'windows-e2e-device' + $env:SIMULATOR_VOLC_HARDWARE_ID = 'windows-e2e-hardware' + $env:SIMULATOR_VOLC_DEVICE_SECRET = $volcSecret + $env:SIMULATOR_VOLC_MODEL_MODE = $ModelMode + $env:SIMULATOR_HARDWARE_GATEWAY_URL = "ws://127.0.0.1:$mockPort/v1/realtime" + Set-Location -LiteralPath $simulatorRoot + if ($ExecutablePath) { + $resolvedExecutable = (Resolve-Path -LiteralPath $ExecutablePath).Path + $electron = Start-Process -FilePath $resolvedExecutable -WorkingDirectory (Split-Path -Parent $resolvedExecutable) -PassThru -Wait + } else { + $electron = Start-Process -FilePath (Join-Path $simulatorRoot 'node_modules\electron\dist\electron.exe') -ArgumentList @('.') -WorkingDirectory $simulatorRoot -PassThru -Wait + } + if ($electron.ExitCode -ne 0) { throw "Electron E2E process failed with exit code $($electron.ExitCode)" } + if (-not (Test-Path -LiteralPath $resultPath -PathType Leaf)) { throw 'Electron E2E result file is missing' } + $result = Get-Content -LiteralPath $resultPath -Raw | ConvertFrom-Json + if ($result.result -ne 'PASS' -or $result.phase -ne '已完成') { throw "Electron E2E failed: $($result | ConvertTo-Json -Depth 8 -Compress)" } + if ($result.model_mode -ne $ModelMode) { throw "Electron E2E used model mode '$($result.model_mode)' instead of '$ModelMode'" } + $conversationID = [string]$result.conversation_id + if ($conversationID -notmatch '^[0-9a-fA-F-]{36}$') { throw 'Electron E2E did not return a conversation ID' } + $databaseState = (docker compose exec -T postgres psql -U voice -d voice_platform -t -A -F '|' -c "SELECT c.runtime_type||'|'||c.status||'|'||hs.status||'|'||hs.sdk_version||'|'||(hs.dynamic_config_applied_at IS NOT NULL)::text||'|'||count(DISTINCT ct.id)||'|'||(SELECT count(*) FROM web_event we WHERE we.resource_public_id=c.public_id AND we.event_type IN('conversation.started','conversation.completed')) FROM conversation c JOIN hardware_agent_session hs ON hs.conversation_id=c.id LEFT JOIN conversation_turn ct ON ct.conversation_id=c.id WHERE c.public_id='$conversationID'::uuid GROUP BY c.runtime_type,c.status,hs.status,hs.sdk_version,hs.dynamic_config_applied_at,c.public_id" | Out-String).Trim() + if ($databaseState -ne 'volc_hardware_agent|COMPLETED|COMPLETED|1.1.0-windows-standard-protocol|true|2|2') { throw "Unexpected platform state: $databaseState" } + $gatewayEvents = Get-Content -LiteralPath $mockOutput -Raw + if ($gatewayEvents -notmatch 'dynamic_config_applied' -or $gatewayEvents -notmatch 'round_completed' -or $gatewayEvents -notmatch 'platform_stop_requested') { throw "Mock gateway did not observe config, media, and platform-stop flow: $gatewayEvents" } + [ordered]@{ + result = 'PASS' + conversation_id = $conversationID + platform_state = $databaseState + transcript_turns = $result.turns.Count + checks_passed = @($result.checks | Where-Object status -eq 'passed').Count + model_mode = $result.model_mode + mock_gateway_log = $mockOutput + } | ConvertTo-Json -Compress +} +finally { + Set-Location -LiteralPath $projectRoot + if ($mockProcess -and -not $mockProcess.HasExited) { Stop-Process -Id $mockProcess.Id -Force -ErrorAction SilentlyContinue } + if ($conversationID -match '^[0-9a-fA-F-]{36}$') { + @" +BEGIN; +CREATE TEMP TABLE hc AS SELECT id,context_snapshot_id,consent_snapshot_id FROM conversation WHERE public_id='$conversationID'::uuid; +DELETE FROM audit_event WHERE resource_id IN(SELECT id FROM hc); +DELETE FROM web_event WHERE resource_public_id='$conversationID'::uuid; +DELETE FROM idempotency_record WHERE resource_id IN(SELECT id FROM hc); +DELETE FROM conversation_turn WHERE conversation_id IN(SELECT id FROM hc); +DELETE FROM conversation_memory_use WHERE conversation_id IN(SELECT id FROM hc); +DELETE FROM memory_tool_call WHERE conversation_id IN(SELECT id FROM hc); +DELETE FROM hardware_agent_event WHERE conversation_id IN(SELECT id FROM hc); +DELETE FROM hardware_agent_session WHERE conversation_id IN(SELECT id FROM hc); +DELETE FROM conversation_usage_budget WHERE conversation_id IN(SELECT id FROM hc); +DELETE FROM outbox_event WHERE aggregate_id IN(SELECT id FROM hc); +DELETE FROM conversation_state_history WHERE conversation_id IN(SELECT id FROM hc); +DELETE FROM conversation WHERE id IN(SELECT id FROM hc); +DELETE FROM context_snapshot WHERE id IN(SELECT context_snapshot_id FROM hc); +DELETE FROM consent_snapshot WHERE id IN(SELECT consent_snapshot_id FROM hc); +COMMIT; +"@ | docker compose exec -T postgres psql -U voice -d voice_platform -v ON_ERROR_STOP=1 -q | Out-Null + } + if ($deviceInternalID -match '^[0-9a-fA-F-]{36}$') { + "DELETE FROM hardware_agent_device_binding WHERE device_id='$deviceInternalID'::uuid; UPDATE device_twin SET desired_config=desired_config-'voice_runtime'-'hardware_agent_transport'-'minimum_sdk_version' WHERE device_id='$deviceInternalID'::uuid;" | docker compose exec -T postgres psql -U voice -d voice_platform -v ON_ERROR_STOP=1 -q | Out-Null + } + @('MOCK_HARDWARE_GATEWAY_PORT','MOCK_VOLC_DEVICE_SECRET','MOCK_REQUEST_PLATFORM_STOP','MOCK_PLATFORM_API_URL','MOCK_PLATFORM_DEVICE_ID','MOCK_PLATFORM_DEVICE_SECRET','SIMULATOR_AUTO_TEST','SIMULATOR_AUTO_WAIT_FOR_PLATFORM_STOP','SIMULATOR_AUTO_RESULT_PATH','SIMULATOR_DEFAULT_API_URL','SIMULATOR_DEFAULT_DEVICE_ID','SIMULATOR_DEFAULT_DEVICE_SECRET','SIMULATOR_VOLC_INSTANCE_ID','SIMULATOR_VOLC_PRODUCT_KEY','SIMULATOR_VOLC_DEVICE_NAME','SIMULATOR_VOLC_HARDWARE_ID','SIMULATOR_VOLC_DEVICE_SECRET','SIMULATOR_VOLC_MODEL_MODE','SIMULATOR_HARDWARE_GATEWAY_URL') | ForEach-Object { Remove-Item "Env:$_" -ErrorAction SilentlyContinue } + Remove-Item -LiteralPath $resultPath,$mockOutput,$mockError -Force -ErrorAction SilentlyContinue +} diff --git a/device-simulator-windows/vite.config.ts b/device-simulator-windows/vite.config.ts index 1de9b95..9c2a8d3 100644 --- a/device-simulator-windows/vite.config.ts +++ b/device-simulator-windows/vite.config.ts @@ -1,4 +1,5 @@ import { defineConfig } from "vite"; +import { resolve } from "node:path"; export default defineConfig({ base: "./", @@ -6,5 +7,11 @@ export default defineConfig({ outDir: "dist", emptyOutDir: true, sourcemap: true, + rollupOptions: { + input: { + hardware: resolve(__dirname, "index.html"), + legacy: resolve(__dirname, "legacy.html"), + }, + }, }, }); diff --git a/device-simulator-windows/远程测试包说明.txt b/device-simulator-windows/远程测试包说明.txt index 8ac033f..727ef79 100644 --- a/device-simulator-windows/远程测试包说明.txt +++ b/device-simulator-windows/远程测试包说明.txt @@ -1,21 +1,24 @@ -云端语音测试设备使用说明 +云端硬件对话智能体测试程序使用说明 -1. 将整个 ZIP 解压到普通文件夹,不要只从压缩包内直接运行。 -2. 双击“启动云端测试设备.cmd”,不要直接双击 EXE。 -3. 首次运行时允许 Windows 麦克风访问。 -4. 点击“检测平台和音频设备”,检测通过后即可开始语音聊天。 +1. 将 ZIP 完整解压到普通文件夹,不要直接在压缩包内运行。 +2. 双击“启动云端测试设备.cmd”,或运行 hardware-agent-tester-0.3.2-windows-x64.exe。 +3. 首次运行允许 Windows 麦克风访问。 +4. 填写平台设备信息和 Hardware ID;火山 Instance ID、Product Key、Device Name 由平台自动下发。 +5. Product Secret 只配置在平台密钥服务中,测试程序不会要求输入;平台代设备注册后只下发本设备的一机一密。 +6. 根据火山控制台中的 Bot 类型选择“通用 LLM”或“豆包端到端语音”。选错会导致 session.update 校验失败。 +7. 点击“检测、鉴权并准备设备”,13项验收区开始记录证据。 +8. 检测通过后点击“开始硬件智能体通话”。只有收到火山 session.updated 且平台确认 ACTIVE 后才会开放麦克风。 +9. 完成双方对话后点击结束;也可以从平台停止通话,程序会在 session_updated 回执中收到 stop_session 并自动结束。 +10. 点击“导出测试报告”,将 JSON 报告交给开发人员验收。 -云端后台:https://121.41.46.238 -测试设备:10000000-0000-0000-0000-000000000511 +平台维护中心必须把本设备设置为 volc_hardware_agent + low_load,并绑定与火山产品关联的真实 Bot ID。公网平台尚未部署 v3 路由时会返回 404,此时不能进行真实测试。 -网络需要允许 HTTPS 443、WSS 以及火山 RTC 所需的出站网络连接。 +网络需要允许 HTTPS 443 和 WSS 到: +- 平台服务器 +- ai-gateway.vei.volces.com -如果检测平台时提示 RTC 网络检测失败,或入房超过 15 秒: -- 关闭 VPN、系统代理和网络加速器; -- 在 Windows 防火墙/杀毒软件中允许本程序联网; -- 优先改用手机热点复测,以排除公司、校园或酒店网络限制; -- 需要放行 TCP 443 到火山 RTC Web SDK 4.69.0 使用的 *.rtc.volcvideo.com 和 *.volcvideos.com。 +`iot-cn-shanghai.iot.volces.com`只需要平台服务器访问,设备无需访问动态注册地址。 -如果手机热点、关闭VPN/代理后仍然无法入房,请在失败后点击页面底部“导出 RTC 日志”,把 Windows“下载”目录中最新生成的 RTC 日志文件交给开发人员分析。 +安全提示:Product Secret 永不到达设备。平台下发的一机一密会使用当前 Windows 用户的 Safe Storage 加密保存,导出的测试报告不包含任何密钥。 -安全提示:本测试包包含服务器测试设备凭据,仅限受控测试使用,请勿公开上传或转发。测试完成后请删除其他电脑上的解压目录和 ZIP 文件。 +当前测试 EXE 未配置商业代码签名证书,Windows 可能提示“未知发布者”。请先用随包 SHA-256 文件核对完整性;正式外发版本应使用甲方或发布主体的代码签名证书签名。 diff --git a/docs/content-memory.md b/docs/content-memory.md index 0a00981..047b851 100644 --- a/docs/content-memory.md +++ b/docs/content-memory.md @@ -23,7 +23,7 @@ | 记忆修订与停用 | 当前记忆、正文或状态、原因、强ETag | 正文变化创建新版本并立即使旧索引失效;停用后不再召回;重新启用必须重新校验授权并重新索引;`PENDING_DELETE/SUPERSEDED/DELETED`均禁止修改 | 新记忆版本或状态 | | 记忆永久删除 | 当前记忆、删除原因、强ETag | 事务内先进入`PENDING_DELETE`并清空活动向量,使新通话立即停止召回;该状态不可通过修改接口恢复;Outbox后台把索引标记删除、清除所有记忆版本正文并记录完成审计 | `DELETED`记忆和索引 | | 新通话记忆检索 | 服务端已确定的客户组织、长者和通话目标 | SQL同时限制租户、长者、当前授权、活动记忆、当前版本、普通敏感级别、有效期和活动索引;最多5条 | 冻结到`context_snapshot.memory_versions`的版本引用和最小背景摘要 | -| 通话中记忆工具 | 供应商持有的单通话短期签名令牌、自然语言查询 | 令牌只绑定服务端既有通话和任务;租户、家庭和长者边界从数据库反查,模型不能传入或扩大;单条240字、总计800字、最多5条;Embedding失败返回空结果并标记降级,不中断通话 | `/internal/v1/tools/memory-search`、`memory_tool_call`和实际使用追踪 | +| 通话中记忆工具 | 供应商持有的单通话短期签名令牌、自然语言查询 | 令牌只绑定服务端既有通话和RTC任务或硬件智能体会话;租户、家庭和长者边界从数据库反查,模型不能传入或扩大;单条240字、总计800字、最多5条;Embedding失败返回空结果并标记降级,不中断通话 | `/internal/v1/tools/memory-search`、`memory_tool_call`和实际使用追踪 | | 可选供应商原生记忆 | 当前人工确认记忆、有效授权、普通敏感级别、受控HTTP适配器 | 数据库始终是主数据;开启后用Outbox同步,修订先清理旧引用,停用、授权撤回、单条删除和主体删除均可靠删除并对账;关闭功能后通话继续走数据库检索 | `provider`索引的`PENDING/ACTIVE/FAILED/DELETED`状态 | | 内容质量反馈 | 故事、事实或记忆的人工确认、修改、拒绝、停用及九类标准问题 | 不复制私密正文;按生成模型和提示词版本聚合候选数、审核动作和问题类型;跨租户统一拒绝 | 维护端“内容质量”与`content_review_signal` | | 跨通话连续上下文 | 最近已完成通话的就绪摘要、当前授权快照、交流偏好 | 只有`story_extraction`和`personalized_memory`在新通话冻结快照中同时有效时,才按同租户同长者读取最近3次摘要;读取结果每条最多240字,供应商上下文按每条140字压缩且总计不超过1000字;撤回任一授权后不再注入 | 冻结到新通话`context_snapshot.sanitized_summary`的短期连续上下文 | diff --git a/docs/device-api-compatibility.md b/docs/device-api-compatibility.md index 1c4ba68..dc04bc6 100644 --- a/docs/device-api-compatibility.md +++ b/docs/device-api-compatibility.md @@ -1,7 +1,7 @@ # 设备 API 兼容性约定 > 当前设备接口主版本:`/device/v2` -> 当前接口契约:OpenAPI 0.73.0 +> 当前接口契约:OpenAPI 0.77.0 > 适用对象:云端 API、终端固件、模拟终端和设备联调工具 ## 1. 兼容目标 @@ -18,6 +18,10 @@ ## 3. 必须升级到`/device/v3`的变更 +当前`/device/v3/hardware-agent-credentials:provision`和`/device/v3/agent-sessions`承载火山硬件对话智能体控制面:前者由平台代设备动态注册且不下发Product Secret,后者在设备直连供应商音频前预取冻结的人设、记忆和授权配置,并回报会话事件与终稿。现有RTC/VoiceChat设备继续使用完整的`/device/v2`接口,平台不得将v3字段或状态语义反向强加给v2固件。 + +v3硬件会话必须在创建时冻结SDK版本、音频格式、可靠字幕、动态配置、凭证轮换和双路PCM录制能力,并以`session_updated`作为平台存活检查。`session_started`只有在设备证明本次`session.updated`已应用且Bot、传输、配置摘要匹配后才会激活。事件响应中的`action=stop_session`是强制控制动作:设备先断开供应商,再以新的单调序号回报结束或失败;`continue`才允许保持连接。低负载传输不具备供应商服务端状态回调,因此不得把设备事件和终稿上报能力声明为可选。硬件v3仍处于目标板卡接入期,本次新增必填能力字段不改变已经量产的v2契约。 + 以下任一变化都属于破坏性变更:删除或重命名字段、改变字段类型或业务含义、给既有请求增加必填字段、缩小已发布枚举导致旧值失效、改变签名算法或规范化规则、让旧固件收到无法安全忽略的新控制动作、改变既有状态机或重试/幂等语义。此类变更必须新增`/device/v3`,并保留v2迁移期和独立契约。 CI在拉取请求中以目标分支OpenAPI为基线执行破坏性变更检查。若确实需要破坏性变更,应先建立新主版本路径和迁移方案,不得通过修改或删除旧版契约绕过门禁。 @@ -42,6 +46,7 @@ CI在拉取请求中以目标分支OpenAPI为基线执行破坏性变更检查 | OpenAPI版本 | 设备兼容变更 | 兼容结论 | |---|---|---| +| 0.77.0 | 新增v3平台代注册端点;Windows硬件智能体客户端不再接收Product Secret,既有v2接口不变 | v2完全兼容;硬件v3客户端升级 | | 0.68.0 | 生产基础设施状态增加实际应用运行环境与身份服务模式证明;未修改设备端点、字段、状态机或终端行为 | v2完全兼容 | | 0.67.0 | 发布候选快照增加生产基础设施强制门禁及主机部署模式、异地备份、外部告警、模拟服务状态;未修改设备端点、字段、状态机或终端行为 | v2完全兼容 | | 0.66.0 | 发布候选技术验收快照增加主机健康当前性门禁;未修改设备端点、字段、状态机或终端行为 | v2完全兼容 | diff --git a/docs/device-api-complete-reference.md b/docs/device-api-complete-reference.md new file mode 100644 index 0000000..36890aa --- /dev/null +++ b/docs/device-api-complete-reference.md @@ -0,0 +1,847 @@ +# 设备相关接口完整说明 + +> 版本:2026-09-05 +> 契约来源:`internal/httpapi/router.go`、实际处理器、`openapi/openapi.yaml` +> 生产 Base URL:`https://121.41.46.238` +> 适用对象:EchoEar/ESP32-S3 固件、Windows 模拟设备、用户 App/Web、平台运维端。 + +## 1. 文档范围 + +本文覆盖当前代码中全部直接或间接管理设备的接口: + +| 接口面 | 方法数 | 鉴权 | 用途 | +|---|---:|---|---| +| 设备直连接口 `/device/v2`、`/device/v3` | 20 | 设备 HMAC | 启动、配置、命令、遥测、固件下载、会话、字幕、媒体、火山凭据 | +| 用户设备管理 `/api/v1/devices*` | 10 | OIDC Bearer | 绑定、设备列表、在线状态、影子、命令 | +| 运维设备管理 `/api/v1/ops/devices*` | 9 | OIDC Bearer + `X-Tenant-Id` | 登记、密钥轮换、绑定码、激活状态机 | +| 家庭端与运维端固件接口 | 31 | OIDC Bearer | 签名密钥、版本、上传、灰度、暂停、回滚、下载 | +| 设备诊断处置 | 2 | OIDC Bearer + `X-Tenant-Id` | 强制刷新配置、吊销设备 | + +火山硬件智能体 WebSocket 不是平台 HTTP API,但设备聊天必须使用,故在第 10 节单独说明。 + +## 2. 通用约定 + +### 2.1 平台响应包络 + +成功: + +```json +{ + "request_id": "req-01J...", + "data": {} +} +``` + +失败: + +```json +{ + "request_id": "req-01J...", + "error": { + "code": "VALIDATION_FAILED", + "message": "请求参数不正确", + "retryable": false, + "field_errors": [{ "field": "hardware_id", "message": "不能为空" }], + "details": {} + } +} +``` + +### 2.2 ID、时间和分页 + +- `{id}`、`device_id`、`conversation_id`、`household_id`、`subject_id` 均为 UUID。 +- 时间均为 RFC3339 UTC,例如 `2026-09-05T06:30:00Z`。 +- `Idempotency-Key` 长度为 16—128 字符;业务重试保留原正文和原幂等键。 +- `If-Match` 使用接口返回的强 ETag,例如 `"7"`。 +- 密钥、Token、绑定码和下载地址不得写入普通日志。 + +### 2.3 设备 HMAC 请求头 + +所有 `/device/v2/*` 和 `/device/v3/*` 请求必须携带: + +| Header | 输入说明 | +|---|---| +| `X-Device-Id` | 平台设备公开 UUID | +| `X-Device-Timestamp` | RFC3339 时间,允许时钟偏差默认 ±5 分钟 | +| `X-Device-Nonce` | 16—128 字符,一次请求只能使用一次 | +| `X-Device-Content-SHA256` | 原始请求正文 SHA-256 小写十六进制;空正文也计算 | +| `X-Device-Signature` | HMAC-SHA256 的 Base64URL 无填充编码 | + +签名原文: + +```text +HTTP_METHOD大写\n +完整REQUEST_URI(含查询字符串)\n +X-Device-Timestamp\n +X-Device-Nonce\n +X-Device-Content-SHA256 +``` + +## 3. 设备启动、配置、命令和遥测 + +### 3.1 GET `/device/v2/bootstrap` + +介绍:设备上电后报告固件和硬件信息,取得平台权威设备状态、绑定信息、配置版本和服务器时间。 + +输入: + +| 位置 | 字段 | 必填 | 说明 | +|---|---|---:|---| +| Query | `firmware_version` | 是 | 当前固件版本,最长 80 | +| Query | `device_time` | 是 | RFC3339 设备时间 | +| Query | `board_rev` | 否 | 硬件版本 | +| Query | `rtc_sdk_version` | 否 | SDK 版本 | +| Query | `codec` | 否 | 编码,例如 `pcm_s16le` | +| Query | `sample_rate` | 否 | 采样率,例如 `16000` | +| Query | `network` | 否 | 网络类型,例如 `wifi` | + +输出:`server_time`、`clock_skew_ms`、设备状态、最低版本、兼容性、`config_revision` 和绑定对象。 + +示例: + +```http +GET /device/v2/bootstrap?firmware_version=1.0.0&board_rev=echoear-v1&codec=pcm_s16le&sample_rate=16000&device_time=2026-09-05T06%3A30%3A00Z&network=wifi +``` + +```json +{ + "request_id": "req-1", + "data": { + "server_time": "2026-09-05T06:30:01Z", + "clock_skew_ms": 1000, + "device": { + "device_id": "10000000-0000-0000-0000-000000000512", + "status": "registered", + "model": "echoear", + "firmware_compatible": true, + "config_revision": 7, + "binding": { "bound": true, "household_id": "11111111-1111-1111-1111-111111111111", "subject_id": "22222222-2222-2222-2222-222222222222", "subject_name": "张大妈" } + } + } +} +``` + +### 3.2 GET `/device/v2/config` + +介绍:读取云端期望配置和当前绑定。设备保存响应 `ETag`,后续用 `If-None-Match` 增量检查。 + +输入:无正文;可选 Header `If-None-Match: "7"`。 + +输出:HTTP `200` 返回 `device_id`、`revision`、`desired_config`、`reported_revision`、`binding`;配置未变化返回 `304` 且无正文。 + +```json +{ + "request_id": "req-2", + "data": { + "device_id": "10000000-0000-0000-0000-000000000512", + "revision": 8, + "desired_config": { "voice_runtime": "volc_hardware_agent", "volume": { "speaker": 70 } }, + "reported_revision": 7, + "binding": { "bound": true, "subject_id": "22222222-2222-2222-2222-222222222222" } + } +} +``` + +### 3.3 GET `/device/v2/commands` + +介绍:拉取未过期的待执行命令,并增加投递次数。 + +输入:Query `limit`,范围 1—20,默认 10。 + +输出:`items[]`;每项包含 `id`、`type`、`payload`、`status`、`expires_at`、`delivery_attempts`、`created_at`。 + +```json +{ + "request_id": "req-3", + "data": { + "items": [{ + "id": "33333333-3333-3333-3333-333333333333", + "type": "apply_config", + "payload": { "revision": 8 }, + "status": "DELIVERED", + "expires_at": "2026-09-05T07:30:00Z", + "delivery_attempts": 1, + "created_at": "2026-09-05T06:30:00Z" + }] + } +} +``` + +### 3.4 POST `/device/v2/commands/{id}/ack` + +介绍:设备回报命令已接收、成功或失败。 + +输入:`status` 为 `ACKNOWLEDGED`、`SUCCEEDED`、`FAILED`;`result_summary` 只能包含有限诊断字段,不得含 Secret/Token。 + +```json +{ + "status": "SUCCEEDED", + "result_summary": { "applied_revision": 8 } +} +``` + +输出:更新后的命令投递对象。 + +```json +{ + "request_id": "req-4", + "data": { "id": "33333333-3333-3333-3333-333333333333", "type": "apply_config", "status": "SUCCEEDED", "result_summary": { "applied_revision": 8 } } +} +``` + +### 3.5 POST `/device/v2/telemetry:batch` + +介绍:批量上报状态、脱敏错误或命令结果;按 `event_key` 和 `sequence_no` 去重,并返回平台动作。 + +输入:`events` 为 1—500 项;`event_type` 为 `status`、`error` 或 `command_result`,且只携带对应同名对象。 + +```json +{ + "events": [{ + "event_key": "boot.000008", + "sequence_no": 8, + "occurred_at": "2026-09-05T06:31:00Z", + "event_type": "status", + "status": { + "firmware_version": "1.0.0", + "reported_config_revision": 8, + "network": { "type": "wifi", "rssi": -51 }, + "rtc": { "joined": false, "uplink": false, "downlink": false }, + "recording_queue": { "pending_segments": 0, "failed_segments": 0 } + } + }] +} +``` + +输出:接收/重复数、最新期望配置版本和动作列表。 + +```json +{ + "request_id": "req-5", + "data": { "accepted_count": 1, "duplicate_count": 0, "desired_config_revision": 8, "actions": [{ "type": "pull_commands" }] } +} +``` + +## 4. 火山硬件智能体平台控制面 + +### 4.1 POST `/device/v3/hardware-agent-credentials:provision` + +介绍:平台使用 Product Secret 代设备调用火山动态注册,只向当前设备返回它自己的一机一密。允许状态:`BOUND`、`PROVISIONING`、`ACTIVE`。 + +输入:Header `Idempotency-Key`;正文只包含稳定 `hardware_id`。 + +```json +{ "hardware_id": "echoear-b43a451634a8" } +``` + +输出:`instance_id`、`product_key`、`device_name`、`hardware_id`、一次性敏感的 `device_secret`、版本和来源。响应必须 `Cache-Control: no-store`。 + +```json +{ + "request_id": "req-6", + "data": { + "instance_id": "6a92945cc0c4689234aef656", + "product_key": "6a9568047dd4b23eacab9b1a", + "device_name": "echoear-100000000512", + "hardware_id": "echoear-b43a451634a8", + "device_secret": "<仅保存到设备安全区>", + "credential_ref": "cred-01J...", + "credential_version": 1, + "credential_source": "platform_dynamic_registration", + "issued_at": "2026-09-05T06:32:00Z" + } +} +``` + +### 4.2 POST `/device/v3/agent-sessions` + +介绍:每次聊天前创建一次平台会话,冻结本次 Bot、授权、人设、记忆、隐私策略和会话证明。仅 `ACTIVE` 设备可调用。 + +输入: + +```json +{ + "device_time": "2026-09-05T06:35:00Z", + "monotonic_ms": 123456, + "capabilities": { + "hardware_agent": true, + "sdk_version": "1.1.0-echoear", + "transports": ["low_load"], + "input_audio_format": "pcm_s16le_16000_mono", + "output_audio_format": "pcm_s16le_16000_mono", + "server_event_callback": false, + "transcript_upload": true, + "dynamic_session_config": true, + "credential_rotation": true, + "near_pcm_recording": false, + "far_pcm_recording": false + }, + "network": { "type": "wifi", "rssi": -52 }, + "requested_goal": "open_conversation", + "config_revision": 8 +} +``` + +输出: + +```json +{ + "request_id": "req-7", + "data": { + "conversation_id": "44444444-4444-4444-4444-444444444444", + "status": "SESSION_READY", + "runtime_type": "volc_hardware_agent", + "runtime": { + "provider": "volcengine", + "bot_id": "botM4gWQ9A9y", + "transport": "low_load", + "provider_session_ref": "provider-ref", + "session_nonce": "<仅内存保存>", + "config_digest": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", + "wait_for_session_update": true + }, + "session_config": { + "dialog_id": "55555555-5555-5555-5555-555555555555", + "system_prompt": "<本次人设>", + "persona_tone": "亲切、自然", + "authorized_context": "<本次授权背景与记忆>", + "prompt_bundle_version": "minimal-v2" + }, + "privacy": { "retention_mode": "transcript_only", "record_near": false, "record_far": false, "upload_allowed": false }, + "session": { "engine_mode": "s2s_hybrid", "heartbeat_interval_sec": 15, "allocation_expires_at": "2026-09-05T06:40:00Z" } + } +} +``` + +### 4.3 POST `/device/v3/agent-sessions/{id}/events` + +介绍:设备回流 `session_started`、`session_updated`、`session_ended`、`session_failed`。`sequence_no` 严格递增。 + +开始事件输入: + +```json +{ + "event_id": "evt-start-1", + "sequence_no": 1, + "event_type": "session_started", + "occurred_at": "2026-09-05T06:35:03Z", + "provider_session_ref": "provider-ref", + "session_nonce": "<创建会话返回值>", + "provider_connection_ref": "event_LUVjX_wvR", + "bot_id": "botM4gWQ9A9y", + "transport": "low_load", + "config_digest": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", + "dynamic_config_applied": true +} +``` + +输出:`status`、平台动作、是否重复和接收时间。 + +```json +{ + "request_id": "req-8", + "data": { "conversation_id": "44444444-4444-4444-4444-444444444444", "status": "ACTIVE", "action": "continue", "duplicate": false, "accepted_at": "2026-09-05T06:35:03Z" } +} +``` + +`action=stop_session` 时必须立即停麦、断开火山,再上报 `session_ended` 或 `session_failed`。 + +### 4.4 POST `/device/v3/agent-sessions/{id}/turns:batch` + +介绍:可靠上传最终字幕;禁止上传增量字幕。单批 1—500 条。 + +输入: + +```json +{ + "turns": [{ + "turn_key": "user-final-1", + "sequence_no": 1, + "speaker": "subject", + "start_at": "2026-09-05T06:35:10Z", + "end_at": "2026-09-05T06:35:12Z", + "final_text": "你好。", + "interrupted": false + }] +} +``` + +输出: + +```json +{ "request_id": "req-9", "data": { "conversation_id": "44444444-4444-4444-4444-444444444444", "accepted_count": 1, "duplicate_count": 0 } } +``` + +## 5. 旧 RTC 会话兼容接口 + +新 EchoEar 使用硬件智能体 v3,不应调用本节的创建/激活接口。本节仅供存量 RTC 设备。 + +### 5.1 POST `/device/v2/conversations` + +介绍:分配 RTC 房间和短期 Token,不启动智能体。 + +输入: + +```json +{ + "device_time": "2026-09-05T06:40:00Z", + "monotonic_ms": 130000, + "capabilities": { "rtc": true, "audio_tracks": ["near", "far"], "upload_resume": true, "rtc_credential_renewal": true }, + "network": { "type": "wifi", "rssi": -50 }, + "requested_goal": "open_conversation" +} +``` + +输出:`conversation_id`、`ROOM_ISSUED`、RTC 房间/用户/Token、隐私策略和心跳配置。 + +```json +{ + "request_id": "req-10", + "data": { + "conversation_id": "66666666-6666-6666-6666-666666666666", + "status": "ROOM_ISSUED", + "rtc": { "app_id": "app", "room_id": "room", "user_id": "device-user", "agent_user_id": "agent-user", "token": "<仅内存>", "expires_at": "2026-09-05T06:45:00Z" }, + "privacy": { "retention_mode": "none", "record_near": false, "record_far": false, "upload_allowed": false }, + "session": { "engine_mode": "cascade", "heartbeat_interval_sec": 10, "allocation_expires_at": "2026-09-05T06:45:00Z" } + } +} +``` + +### 5.2 POST `/device/v2/conversations/{id}/activate` + +介绍:设备确认已成功进入指定 RTC 房间后,触发平台启动旧语音任务。 + +输入: + +```json +{ "joined_at": "2026-09-05T06:40:02Z", "room_id": "room", "rtc_user_id": "device-user", "rtc_join_result": "success", "config_revision": 8, "connection_reference": "rtc-conn-1" } +``` + +输出: + +```json +{ "request_id": "req-11", "data": { "conversation_id": "66666666-6666-6666-6666-666666666666", "status": "STARTING_AGENT", "accepted_at": "2026-09-05T06:40:02Z" } } +``` + +### 5.3 GET `/device/v2/conversations/{id}` + +介绍:读取平台权威会话状态和设备下一步动作。 + +输入:Path `id`。 + +输出示例: + +```json +{ "request_id": "req-12", "data": { "conversation_id": "66666666-6666-6666-6666-666666666666", "status": "ACTIVE", "action": "continue" } } +``` + +失败时可能返回 `status=FAILED`、`action=leave_room`、稳定脱敏的 `failure_code`。 + +### 5.4 POST `/device/v2/conversations/{id}/heartbeat` + +介绍:刷新会话租约、设备在线状态和配置状态;返回停止、续签或配置动作。 + +输入: + +```json +{ + "sequence_no": 2, + "device_time": "2026-09-05T06:40:12Z", + "monotonic_ms": 142000, + "conversation_status": "ACTIVE", + "rtc": { "joined": true, "uplink": true, "downlink": true }, + "network": { "type": "wifi", "rssi": -49 }, + "firmware_version": "1.0.0", + "reported_config_revision": 8, + "recording_queue": { "pending_segments": 0, "failed_segments": 0 }, + "command_results": [] +} +``` + +输出: + +```json +{ "request_id": "req-13", "data": { "lease_expires_at": "2026-09-05T06:40:42Z", "actions": [{ "type": "continue" }], "desired_config_revision": 8, "commands": [] } } +``` + +### 5.5 POST `/device/v2/conversations/{id}/stop` + +介绍:设备挂断并可靠停止旧供应商任务;支持幂等重放。 + +输入: + +```json +{ "reason": "user_hangup", "device_time": "2026-09-05T06:42:00Z", "last_heartbeat_sequence": 5, "rtc_left": true, "recording": { "near_segments": 0, "far_segments": 0 } } +``` + +输出: + +```json +{ "request_id": "req-14", "data": { "conversation_id": "66666666-6666-6666-6666-666666666666", "status": "STOPPING", "accepted_at": "2026-09-05T06:42:00Z" } } +``` + +### 5.6 POST `/device/v2/conversations/{id}/turns:batch` + +介绍:旧 RTC 回调缺少终稿时,由设备补传最终字幕。输入输出与 4.4 完全相同。 + +```json +{ "turns": [{ "turn_key": "agent-final-2", "sequence_no": 2, "speaker": "agent", "start_at": "2026-09-05T06:41:00Z", "end_at": "2026-09-05T06:41:02Z", "final_text": "您好。", "interrupted": false }] } +``` + +## 6. 设备录音上传接口 + +仅当本次 `privacy.upload_allowed=true` 且对应轨道获授权时使用。 + +### 6.1 POST `/device/v2/conversations/{id}/upload-batches` + +介绍:声明待上传录音分片,平台返回每个对象的短期 PUT 地址;业务 API 不接收音频字节。 + +输入: + +```json +{ + "segments": [{ + "track": "near", + "sequence_no": 0, + "codec": "wav_pcm_s16le", + "content_type": "audio/wav", + "duration_ms": 10000, + "expected_size": 320044, + "expected_digest": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }] +} +``` + +输出:批次状态和分片;创建响应中的分片包含 `upload_url`、`upload_until`。 + +```json +{ + "request_id": "req-15", + "data": { + "id": "77777777-7777-7777-7777-777777777777", + "conversation_id": "66666666-6666-6666-6666-666666666666", + "status": "UPLOADING", + "segment_count": 1, + "expected_total_size": 320044, + "verified_count": 0, + "rejected_count": 0, + "verification_attempts": 0, + "expires_at": "2026-09-05T07:02:00Z", + "segments": [{ "id": "88888888-8888-8888-8888-888888888888", "track": "near", "sequence_no": 0, "status": "AUTHORIZED", "upload_url": "https://object.example/put?...", "upload_until": "2026-09-05T07:02:00Z" }] + } +} +``` + +### 6.2 POST `/device/v2/upload-batches/{id}/complete` + +介绍:声明分片已上传,平台开始异步对象大小、摘要和容器校验。 + +输入:无正文;Path `id` 为上传批次 UUID。 + +输出:HTTP `202`,返回批次,`status` 通常为 `VERIFYING`。 + +```json +{ "request_id": "req-16", "data": { "id": "77777777-7777-7777-7777-777777777777", "status": "VERIFYING", "segment_count": 1, "verified_count": 0, "rejected_count": 0, "segments": [] } } +``` + +### 6.3 GET `/device/v2/upload-batches/{id}` + +介绍:轮询后台校验结果。只有 `VERIFIED` 分片返回 `local_delete_after`。 + +输入:Path `id`。 + +输出示例: + +```json +{ "request_id": "req-17", "data": { "id": "77777777-7777-7777-7777-777777777777", "status": "COMPLETED", "segment_count": 1, "verified_count": 1, "rejected_count": 0, "segments": [{ "id": "88888888-8888-8888-8888-888888888888", "status": "VERIFIED", "local_delete_after": "2026-09-05T07:10:00Z" }] } } +``` + +### 6.4 POST `/device/v2/media-segments/{id}/local-delete-ack` + +介绍:到达平台允许删除时间后,设备删除本地密文并回报;不会删除云端对象。 + +输入: + +```json +{ "status": "DELETED" } +``` + +失败示例:`{ "status": "FAILED", "error_code": "FLASH_DELETE_FAILED" }`。 + +输出:更新后的分片对象,包含 `local_delete_status` 和 `local_delete_reported_at`。 + +## 7. 设备固件下载接口 + +### GET `/device/v2/firmware-releases/{id}/download` + +介绍:仅向当前灰度批次中的目标设备签发 5 分钟私有下载地址。 + +输入:Path `id` 为固件版本 UUID,无正文。 + +输出: + +```json +{ "request_id": "req-18", "data": { "download_url": "https://object.example/firmware.bin?...", "download_until": "2026-09-05T07:05:00Z" } } +``` + +非目标设备或暂停/退役版本返回 `403 FIRMWARE_DOWNLOAD_DENIED`。 + +## 8. 用户 App/Web 设备管理接口 + +统一输入 Header:`Authorization: Bearer `。以下表中“示例输入/输出”均省略通用包络字段。 + +| 方法与路径 | 接口介绍 | 输入 | 输出 | 示例 | +|---|---|---|---|---| +| `POST /api/v1/device-claims` | 消费一次性绑定码 | Body:`claim_code`、`household_id`、`subject_id` | `binding_id`、`device_id`、归属、`bound_at` | 入:`{"claim_code":"CLAIM-...","household_id":"","subject_id":""}`;出:`{"binding_id":"","device_id":"","bound_at":"2026-09-05T07:00:00Z"}` | +| `GET /api/v1/devices` | 列出当前账号可管理设备 | 无 | `items: ManagedDevice[]` | `{"items":[{"id":"","model":"echoear","status":"registered","desired_revision":8}]}` | +| `GET /api/v1/devices/{id}` | 查询设备与绑定摘要 | Path:设备 UUID | `ManagedDevice` | `{"id":"","binding_id":"","subject_name":"张大妈","firmware_version":"1.0.0","version":3}` | +| `GET /api/v1/devices/{id}/binding-impact` | 解绑/转移前检查阻断项 | Query:`action=unbind\|transfer`;转移时 `target_subject_id` | 影响、`allowed`、`blockers`、当前 `binding_id` | `{"action":"transfer","allowed":true,"pending_command_count":0,"active_firmware_task_count":0}` | +| `POST /api/v1/devices/{id}/binding-change` | 执行解绑或转移 | Body:`action`、`expected_binding_id`、`reason`、`acknowledged=true`;转移时 `target_subject_id` | 旧/新绑定和变更时间 | 入:`{"action":"unbind","expected_binding_id":"","reason":"用户解除绑定","acknowledged":true}`;出:`{"device_id":"","action":"unbind","changed_at":"..."}` | +| `GET /api/v1/devices/{id}/presence` | 查询实时在线状态 | Path:设备 UUID | `online`、`status`、`last_seen_at`、`source` | `{"device_id":"","online":true,"status":"ACTIVE","last_seen_at":"...","source":"redis"}` | +| `GET /api/v1/devices/{id}/twin` | 查询设备影子 | Path:设备 UUID | 期望/上报配置、版本、能力、同步状态;Header `ETag` | `{"device_id":"","desired_revision":8,"desired_config":{"volume":{"speaker":70}},"sync_state":"applied"}` | +| `PATCH /api/v1/devices/{id}/twin` | 修改非敏感期望配置 | Header:`If-Match`;Body:`desired_config`,顶层仅 `volume/interaction/upload/rtc/policy_refs` | 更新后的 `DeviceTwin` 和新 `ETag` | `{"desired_config":{"volume":{"speaker":60}}}` | +| `GET /api/v1/devices/{id}/commands` | 查询最近 100 条命令 | Path:设备 UUID | `items: DeviceCommand[]` | `{"items":[{"id":"","type":"reboot","status":"SUCCEEDED"}]}` | +| `POST /api/v1/devices/{id}/commands` | 下发可靠命令 | Header:`Idempotency-Key`;Body:`type`、`payload`、`expires_in_sec` | `DeviceCommand`,首次 `202`,重放 `200` | 入:`{"type":"reboot","payload":{"reason":"maintenance"},"expires_in_sec":300}`;出:`{"id":"","status":"QUEUED","delivery_attempts":0}` | + +`ManagedDevice` 主要输出字段:`id`、`binding_id`、`household_id`、`subject_id`、`subject_name`、`model`、`hardware_revision`、`status`、固件/配置版本、`last_reported_at`、`bound_at`、`registered_at`、`version`。 + +## 9. 平台运维设备管理接口 + +统一输入 Header:`Authorization: Bearer `、`X-Tenant-Id: `。只有独立运维账号可调用。 + +| 方法与路径 | 接口介绍 | 输入 | 输出 | 示例 | +|---|---|---|---|---| +| `GET /api/v1/ops/devices` | 查询租户全部设备 | 无 | `items: OpsDevice[]`,不返回密钥 | `{"items":[{"id":"","model":"echoear","activation_state":"ACTIVE","credential_status":"active","voice_runtime":"volc_hardware_agent"}]}` | +| `POST /api/v1/ops/devices` | 登记新设备并生成平台 HMAC 密钥 | Body:`model` 必填;可选硬件/最低版本/归属/运行时/初始激活状态 | `device`、只显示一次的 `credential`;可能同时返回 `claim` | 入:`{"model":"echoear","hardware_revision":"esp32-s3","voice_runtime":"volc_hardware_agent","activation_state":"FACTORY"}`;出:`{"device":{"id":""},"credential":{"device_id":"","secret":"<64位hex>","key_version":1}}` | +| `GET /api/v1/ops/device-assignment-options` | 查询可分配家庭和长者 | 无 | `items[]` 含家庭/长者 ID 和名称 | `{"items":[{"household_id":"","household_name":"张家","subject_id":"","subject_name":"张大妈"}]}` | +| `PATCH /api/v1/ops/devices/{id}` | 更新设备型号、版本策略、状态、归属或运行时 | Header:`If-Match`;Body 至少一个可修改字段 | 更新后的 `OpsDevice` | `{"minimum_firmware_version":"1.1.0","status":"registered"}` | +| `DELETE /api/v1/ops/devices/{id}` | 软删除并吊销设备/凭据/绑定 | Header:`If-Match` | 吊销后的 `OpsDevice` | `{"id":"","status":"revoked","activation_state":"REVOKED","credential_status":"revoked"}` | +| `POST /api/v1/ops/devices/{id}/credentials/rotate` | 轮换平台 HMAC 密钥,旧密钥立即失效 | 无正文 | 只显示一次的新 `secret`、`key_version`、`issued_at` | `{"device_id":"","secret":"<64位hex>","key_version":2,"issued_at":"..."}` | +| `POST /api/v1/ops/devices/{id}/claims` | 为未绑定设备生成 24 小时绑定码 | 无正文 | 只显示一次的 `claim_code` 和过期时间 | `{"device_id":"","claim_code":"CLAIM-...","issued_at":"...","expires_at":"..."}` | +| `POST /api/v1/ops/devices/{id}/activation-transitions` | 执行受控激活状态迁移 | Body:`target_state`、`reason`、`expected_revision` | 更新后的 `OpsDevice` | `{"target_state":"CLAIMABLE","reason":"工厂质检通过","expected_revision":1}` | +| `GET /api/v1/ops/devices/{id}/activation-transitions` | 查询完整激活审计历史 | Path:设备 UUID | `items: DeviceActivationTransition[]` | `{"items":[{"from_state":"FACTORY","to_state":"CLAIMABLE","reason":"工厂质检通过","activation_revision":2,"created_at":"..."}]}` | + +激活状态:`FACTORY → CLAIMABLE → BOUND → PROVISIONING → ACTIVE → LOST/REVOKED`。设备不能自行修改该状态。 + +## 10. 火山硬件智能体 WebSocket + +### 10.1 建连 + +```text +wss://ai-gateway.vei.volces.com/v1/realtime?bot=botM4gWQ9A9y&wait_for_session_update=true +``` + +输入 Header: + +| Header | 说明 | +|---|---| +| `X-Auth-Type` | 固定 `1` | +| `X-Product-Key` | 平台预配返回的 Product Key | +| `X-Device-Name` | 平台预配返回的 Device Name | +| `X-Random-Num` | 本次随机整数 | +| `X-Timestamp` | Unix 秒 | +| `X-Instance-Id` | 平台预配返回的 Instance ID | +| `X-Signature` | 使用火山 Device Secret 计算的 HMAC-SHA256 Base64 | +| `X-Hardware-Id` | 稳定 Hardware ID | + +签名正文顺序固定: + +```text +auth_type=1&device_name={name}&random_num={random}&product_key={key}×tamp={unix}&instance_id={instance} +``` + +### 10.2 动态配置输入 + +连接打开后立即发送: + +```json +{ + "event_id": "windows-session-update-", + "type": "session.update", + "session": { + "object": "realtime.session", + "model": "", + "input_audio_format": "pcm16", + "output_audio_format": "pcm16", + "config": { + "ASRConfig": { "TurnDetectionMode": 0 }, + "LLMConfig": { "SystemMessages": ["<平台冻结的人设与授权上下文>"] } + }, + "agent_config": { "EnableConversationStateCallback": false } + } +} +``` + +输出:`session.created` 或 `session.updated`;在收到确认前不得开启麦克风。 + +### 10.3 音频和文本输入 + +上行音频:PCM16 little-endian、16kHz、单声道,建议每 20ms 320 个采样。 + +```json +{ "event_id": "audio-", "type": "input_audio_buffer.append", "audio": "" } +``` + +文本测试: + +```json +{ + "event_id": "text-", + "type": "conversation.item.create", + "item": { "type": "message", "role": "user", "content": [{ "type": "input_text", "text": "你好" }], "interrupt_mode": 1 } +} +``` + +### 10.4 设备消费的输出事件 + +| 事件 | 输出内容 | 设备动作 | +|---|---|---| +| `input_audio_buffer.speech_started/stopped` | 服务端 VAD 状态 | 更新交互状态 | +| `conversation.item.input_audio_transcription.completed` | 用户最终字幕 | 上传平台 `turns:batch` | +| `response.audio.delta` | Base64 PCM16、16kHz、单声道 | 连续缓冲并按声卡采样率重采样播放 | +| `response.audio.done` | 本轮音频结束 | 排空播放缓冲 | +| `response.audio_transcript.delta/done` | 智能体增量/最终字幕 | 只把最终字幕上传平台 | +| `response.function_call_arguments.done` | 工具名、参数和 `call_id` | 调用平台受控工具并回传结果 | +| `response.done` | 本轮完成 | 继续监听 | +| `error` | 供应商错误 | 脱敏后上报 `session_failed` | + +注意:low-load WebSocket 下行是 16kHz。把它按 24kHz 播放会出现约 1.5 倍速、音调升高和明显电子感。 + +## 11. 固件管理接口 + +### 11.1 家庭/租户授权接口 + +| 方法与路径 | 介绍 | 输入 | 输出 | +|---|---|---|---| +| `GET /api/v1/households/{id}/firmware-signing-keys` | 查询可信 Ed25519 公钥 | Path `household id` | `FirmwareSigningKey[]` | +| `POST /api/v1/households/{id}/firmware-signing-keys` | 登记签名公钥 | `key_id`、`public_key_base64` | `201`,创建结果 | +| `POST /api/v1/households/{id}/firmware-signing-keys/revoke` | 吊销公钥 | `key_id` | `FirmwareSigningKey` | +| `GET /api/v1/households/{id}/firmware-releases` | 查询固件版本 | Path `household id` | `FirmwareRelease[]` | +| `POST /api/v1/households/{id}/firmware-releases` | 创建 `UPLOADING` 版本 | `PrepareFirmwareReleaseRequest` | 版本和短期上传地址 | +| `GET /api/v1/firmware-releases/{id}/impact` | 发布/暂停/退役前影响评估 | Path `release id` | `FirmwareReleaseImpact` | +| `POST /api/v1/firmware-releases/{id}/upload-session` | 重新签发上传地址 | 无正文 | `FirmwareRelease` | +| `POST /api/v1/firmware-releases/{id}/finalize` | 校验大小、SHA-256、Ed25519 后发布 | 无正文 | `FirmwareRelease` | +| `POST /api/v1/firmware-releases/{id}/pause` | 暂停版本和未投递命令 | 无正文 | `FirmwareRelease` | +| `POST /api/v1/firmware-releases/{id}/resume` | 恢复版本分发 | 无正文 | `FirmwareRelease` | +| `POST /api/v1/firmware-releases/{id}/retire` | 退役版本并保留审计 | 无正文 | `FirmwareRelease` | +| `GET /api/v1/households/{id}/firmware-rollouts` | 查询 OTA 批次 | Path `household id` | `FirmwareRollout[]` | +| `POST /api/v1/households/{id}/firmware-rollouts` | 启动灰度升级 | `StartFirmwareRolloutRequest` | `202 FirmwareRollout` | +| `GET /api/v1/firmware-rollouts/{id}` | 查询逐设备升级进度 | Path `rollout id` | `FirmwareRollout` | +| `POST /api/v1/firmware-rollouts/{id}/pause` | 暂停批次 | 无正文 | `FirmwareRollout` | +| `POST /api/v1/firmware-rollouts/{id}/rollback` | 创建回滚批次 | `rollback_release_id` | 新的 `FirmwareRollout` | + +### 11.2 运维固件接口 + +以下接口全部额外要求 `X-Tenant-Id`: + +| 方法与路径 | 介绍 | 输入 | 输出 | +|---|---|---|---| +| `GET /api/v1/ops/firmware-signing-keys` | 查询租户可信公钥 | 无 | `FirmwareSigningKey[]` | +| `POST /api/v1/ops/firmware-signing-keys` | 登记可信公钥 | `key_id`、`public_key_base64` | `201` | +| `POST /api/v1/ops/firmware-signing-keys/revoke` | 吊销公钥 | `key_id` | `FirmwareSigningKey` | +| `GET /api/v1/ops/firmware-releases` | 按 `model/status` 查询版本 | Query `model`、`status` 可选 | `FirmwareRelease[]` | +| `POST /api/v1/ops/firmware-releases` | 创建租户级版本 | `PrepareOpsFirmwareReleaseRequest` | `201 FirmwareRelease` | +| `POST /api/v1/ops/firmware-releases/{id}/upload-session` | 续签上传地址 | 无正文 | `FirmwareRelease` | +| `POST /api/v1/ops/firmware-releases/{id}/finalize` | 正式发布 | 无正文 | `FirmwareRelease` | +| `POST /api/v1/ops/firmware-releases/{id}/pause` | 暂停 | 无正文 | `FirmwareRelease` | +| `POST /api/v1/ops/firmware-releases/{id}/resume` | 恢复 | 无正文 | `FirmwareRelease` | +| `POST /api/v1/ops/firmware-releases/{id}/retire` | 退役 | 无正文 | `FirmwareRelease` | +| `GET /api/v1/ops/firmware-releases/{id}/download` | 运维下载制品 | Path `release id` | 下载地址、摘要、签名元数据 | +| `GET /api/v1/ops/firmware-rollouts` | 查询 OTA 批次 | 无 | `FirmwareRollout[]` | +| `POST /api/v1/ops/firmware-rollouts` | 启动 OTA | `StartFirmwareRolloutRequest` | `202 FirmwareRollout` | +| `GET /api/v1/ops/firmware-rollouts/{id}` | 查询逐设备进度 | Path `rollout id` | `FirmwareRollout` | +| `POST /api/v1/ops/firmware-rollouts/{id}/pause` | 暂停 OTA | 无正文 | `FirmwareRollout` | + +固件请求示例: + +```json +{ "key_id": "release-key-2026-01", "public_key_base64": "<32字节Ed25519公钥的Base64>" } +``` + +```json +{ + "model": "echoear", + "version": "1.1.0", + "expected_size": 2097152, + "expected_digest": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "signing_key_id": "release-key-2026-01", + "signature": "" +} +``` + +```json +{ "release_id": "", "name": "第一批灰度", "target_percentage": 10, "device_ids": [""] } +``` + +典型版本输出: + +```json +{ "request_id": "req-fw", "data": { "id": "", "model": "echoear", "version": "1.1.0", "status": "READY", "sha256": "bbbb...", "created_at": "2026-09-05T07:00:00Z" } } +``` + +典型批次输出: + +```json +{ "request_id": "req-rollout", "data": { "id": "", "release_id": "", "name": "第一批灰度", "status": "RUNNING", "target_percentage": 10, "targeted": 1, "succeeded": 0, "failed": 0 } } +``` + +## 12. 运维诊断设备动作 + +| 方法与路径 | 介绍 | 输入 | 输出 | 示例 | +|---|---|---|---|---| +| `POST /api/v1/ops/diagnostics/devices/{id}/config-refresh` | 强制设备重新拉取配置 | `X-Tenant-Id`、`Idempotency-Key`、设备 UUID;Body:`reason` | `action=refresh_config`、命令 ID/状态、`queued` | 入:`{"reason":"远端配置与影子不一致"}`;出:`{"action":"refresh_config","device_id":"","command_id":"","command_status":"QUEUED","queued":true}` | +| `POST /api/v1/ops/diagnostics/devices/{id}/revoke` | 紧急吊销异常设备 | `X-Tenant-Id`、`Idempotency-Key`、设备 UUID;Body:`reason` | `action=revoke_device`、设备 ID、`queued` | 入:`{"reason":"设备密钥疑似泄露"}`;出:`{"action":"revoke_device","device_id":"","queued":false}` | + +## 13. 主要错误码 + +| HTTP | 错误码 | 含义与处理 | +|---:|---|---| +| 400 | `VALIDATION_FAILED` | 字段、Header、时间或幂等键不符合契约 | +| 401 | `DEVICE_AUTHENTICATION_FAILED` | 检查平台 Device Secret、时钟、URI、正文摘要和 Base64URL | +| 403 | `DEVICE_ACTIVATION_REQUIRED` | 当前激活状态不允许该操作 | +| 403 | `TRANSCRIPT_STORAGE_NOT_ALLOWED` | 当前授权不允许保存字幕 | +| 403 | `FIRMWARE_DOWNLOAD_DENIED` | 设备不是灰度目标或版本不可下载 | +| 404 | `RESOURCE_NOT_FOUND` | 资源不存在或不属于当前主体/设备 | +| 409 | `IDEMPOTENCY_KEY_REUSED` | 同一幂等键对应不同正文,客户端必须换业务请求 | +| 409 | `DEVICE_ACTIVATION_CONFLICT` | 激活状态或修订号冲突,刷新后再处理 | +| 409 | `HARDWARE_AGENT_HARDWARE_MISMATCH` | Hardware ID 与已注册记录不一致,禁止覆盖 | +| 409 | `HARDWARE_SESSION_PROOF_INVALID` | `session_nonce`/配置摘要/Bot/传输不匹配,丢弃本次会话重新创建 | +| 412 | `VERSION_CONFLICT` | `If-Match` 过期,先重新 GET | +| 426 | `FIRMWARE_UPDATE_REQUIRED` | 当前固件低于最低版本 | +| 429 | `USAGE_QUOTA_EXCEEDED` | 额度或并发限制达到上限 | +| 503 | `HARDWARE_AGENT_PROVISIONING_UNAVAILABLE` | 平台 Product Secret/产品配置不可用 | +| 503 | `HARDWARE_AGENT_PROVISIONING_FAILED` | 火山动态注册失败;按 `request_id` 排障,可退避重试 | + +## 14. 设备端推荐调用顺序 + +```text +上电 + → GET /device/v2/bootstrap + → GET /device/v2/config + → GET /device/v2/commands + → POST /device/v2/telemetry:batch + +首次绑定后 + → POST /device/v3/hardware-agent-credentials:provision + → 安全保存火山 Device Secret + +每次聊天 + → POST /device/v3/agent-sessions + → 连接火山 WebSocket + → session.update + → 收到 session.created/session.updated + → POST /device/v3/agent-sessions/{id}/events (session_started) + → 16k PCM 双向音频 + → POST /device/v3/agent-sessions/{id}/turns:batch + → 周期 events (session_updated) + → events (session_ended/session_failed) +``` + +## 15. 机器可读契约 + +- 完整 OpenAPI:[`../openapi/openapi.yaml`](../openapi/openapi.yaml) +- 设备实现与安全说明:[`device-api-reference.md`](device-api-reference.md) +- 设备接入说明:[`device-integration.md`](device-integration.md) +- 固件灰度说明:[`firmware-rollout.md`](firmware-rollout.md) + +若本文与 OpenAPI 或运行时代码不一致,以同一发布版本的 OpenAPI 和处理器为准,并应同步修正文档。 diff --git a/docs/device-api-reference.md b/docs/device-api-reference.md new file mode 100644 index 0000000..eee4b96 --- /dev/null +++ b/docs/device-api-reference.md @@ -0,0 +1,576 @@ +# 设备接口文档 + +> 适用范围:平台设备管理、EchoEar/ESP32-S3 设备接入、火山引擎硬件对话智能体。 +> 契约基线:当前工作区实际路由、处理器与 `openapi/openapi.yaml`,更新日期 2026-09-05。 +> 生产 API:`https://121.41.46.238`(正式域名启用后只替换 Base URL,不改变路径)。 + +## 1. 接入模型 + +设备同时使用两套彼此独立的身份: + +| 身份/密钥 | 用途 | 生成与保存位置 | 是否下发设备 | +|---|---|---|---| +| 平台 `device_id` | 标识平台登记设备 | 平台生成;设备保存公开编号 | 是 | +| 平台 Device Secret | 调用 `/device/*` 时做 HMAC 鉴权 | 平台逐设备生成;平台加密保存,设备安全区保存 | 是,只在登记/轮换时显示一次 | +| 火山 `instance_id` / `product_key` | 标识火山实例和产品 | 平台配置 | 可随预配响应返回,非秘密 | +| 火山 Product Secret | 调用 DynamicRegister 的产品级秘密 | 只保存在平台 KMS/Secret | **否** | +| 火山 `device_name` / `device_secret` | 设备直连火山时使用的一机一密 | 平台代设备动态注册;平台加密保存,设备安全区保存 | 是,只返回给已认证的对应设备 | +| Bot ID | 选择硬件对话智能体 | 平台按设备绑定 | 会话响应返回,非秘密 | + +核心原则:设备固件不内置 Product Secret。设备先用平台 Device Secret 认证,由平台代设备调用火山动态注册,再把该设备专属的火山 Device Secret 返回给设备。 + +### 1.1 火山 License 的准确含义 + +火山硬件对话智能体的 License 是产品侧可用额度。平台调用 DynamicRegister 时,火山自动消耗当前产品的一份有效额度并返回一机一密;设备不接收一段可安装的 License 明文,也不需要向平台提交 License 安装回执。 + +因此,当前有效契约中: + +- 没有 `/device/v3/hardware-agent-license:ack` 路由; +- 没有设备端 `license` 字段; +- 没有 `AVAILABLE → ALLOCATED → ACTIVE` 的设备 License 安装状态机; +- 平台可以维护额度监控或影子账本,但不能用它替代火山的真实额度,也不能阻断已正确取得一机一密的设备。 + +火山返回额度耗尽、过期等错误时,平台统一转换为 `HARDWARE_AGENT_PROVISIONING_FAILED`,同时通过内部日志和请求编号保留供应商错误供运维定位。 + +## 2. 通用响应格式 + +成功响应: + +```json +{ + "request_id": "req-...", + "data": {} +} +``` + +失败响应: + +```json +{ + "request_id": "req-...", + "error": { + "code": "VALIDATION_FAILED", + "message": "请求参数不正确", + "retryable": false, + "field_errors": [], + "details": {} + } +} +``` + +排障时应记录 `request_id`、HTTP 状态码和稳定错误码,不记录任何 Secret、Token、完整人设或完整对话正文。 + +## 3. 设备 HMAC 鉴权 + +所有 `/device/v2/*` 和 `/device/v3/*` 请求必须携带: + +| 请求头 | 说明 | +|---|---| +| `X-Device-Id` | 平台设备公开 UUID | +| `X-Device-Timestamp` | RFC3339 UTC 时间,默认允许前后 5 分钟 | +| `X-Device-Nonce` | 本次请求唯一的 16—128 字符随机值 | +| `X-Device-Content-SHA256` | 原始 HTTP 正文的 SHA-256 小写十六进制;空正文也要计算 | +| `X-Device-Signature` | HMAC-SHA256 结果的无填充 Base64URL | + +签名原文必须逐字使用实际请求 URI(含查询字符串): + +```text +UPPERCASE_HTTP_METHOD +REQUEST_URI_WITH_EXACT_QUERY_STRING +X-Device-Timestamp原值 +X-Device-Nonce原值 +X-Device-Content-SHA256原值 +``` + +伪代码: + +```text +bodyHash = hexLower(SHA256(rawBody)) +canonical = methodUpper + "\n" + requestURI + "\n" + timestamp + "\n" + nonce + "\n" + bodyHash +signature = base64UrlNoPadding(HMAC_SHA256(platformDeviceSecret, canonical)) +``` + +注意事项: + +- JSON 签名后不得重新格式化或改变字段顺序;服务端校验的是收到的原始字节。 +- 每次网络重试都生成新的 `timestamp`、`nonce` 和签名。 +- 需要幂等的业务请求保留原 `Idempotency-Key` 和原正文。 +- 时间未同步时先通过安全时间源校时;不能关闭时间窗或重放保护。 + +## 4. 设备激活状态机 + +主流程: + +```text +FACTORY → CLAIMABLE → BOUND → PROVISIONING → ACTIVE + │ + ├─→ LOST + └─→ REVOKED +``` + +| 状态 | 含义 | 允许的设备操作 | +|---|---|---| +| `FACTORY` | 只有出厂身份,等待质检放行 | 不允许设备 API 鉴权 | +| `CLAIMABLE` | 可生成/消费绑定码 | 启动、配置、命令、遥测;不能通话 | +| `BOUND` | 已绑定家庭和使用人 | 可申请火山一机一密;不能创建会话 | +| `PROVISIONING` | 正在注册或恢复火山运行凭据 | 可重复预配;不能创建会话 | +| `ACTIVE` | 平台身份、绑定和运行凭据就绪 | 全部设备接口 | +| `LOST` | 设备挂失 | 设备鉴权和新会话被拒绝 | +| `REVOKED` | 永久吊销 | 终态,不可恢复 | + +代码允许的恢复/解绑边包括:`BOUND → CLAIMABLE`、`PROVISIONING → BOUND`、`ACTIVE → CLAIMABLE/PROVISIONING/LOST`、`LOST → CLAIMABLE/ACTIVE`;任意非 `REVOKED` 状态均可受控转为 `REVOKED`。状态变更必须由平台业务或运维接口执行,设备不能自行声明已激活。 + +每次变更增加 `activation_revision` 并保存审计历史。进入 `ACTIVE` 时,硬件智能体设备必须已经绑定且存在有效火山一机一密。 + +## 5. 首次激活完整流程 + +1. 运维调用 `POST /api/v1/ops/devices` 登记设备,取得一次性平台 Device Secret。 +2. 工厂烧录/安全注入 `device_id`、平台 Device Secret、API Base URL 和稳定 `hardware_id`;不得烧录 Product Secret。 +3. 运维将设备从 `FACTORY` 放行到 `CLAIMABLE`,并生成绑定码。 +4. 用户 App/Web 调用 `POST /api/v1/device-claims` 消费绑定码,平台把设备变为 `BOUND`。 +5. 设备使用平台 HMAC 调用 `POST /device/v3/hardware-agent-credentials:provision`。 +6. 平台把状态推进到 `PROVISIONING`,自动建立产品默认 Bot/传输/最低 SDK 绑定,再使用服务端 Product Secret 调用火山 DynamicRegister。 +7. 火山消耗产品 License 额度并返回该 `device_name` 对应的 `device_secret`。 +8. 平台加密保存火山 Device Secret,并通过 `no-store` 响应返回给同一设备,然后自动完成到 `ACTIVE` 的迁移。 +9. 设备把火山一机一密写入 NVS 加密分区/安全芯片;以后重复预配会返回已保存的同一凭据,不重复注册。 +10. 每次用户发起聊天时,设备先创建平台硬件智能体会话,注入本次人设和上下文后,再直连火山。 + +## 6. 设备端接口总览 + +### 6.1 启动、配置、命令和固件(v2 公共能力) + +| 方法 | 路径 | 状态要求 | 幂等键 | 作用 | +|---|---|---|---|---| +| `GET` | `/device/v2/bootstrap` | `CLAIMABLE/BOUND/PROVISIONING/ACTIVE` | 否 | 上报启动版本并取得绑定和兼容性 | +| `GET` | `/device/v2/config` | 同上 | 否 | 拉取期望配置;支持 `If-None-Match` | +| `GET` | `/device/v2/commands` | 同上 | 否 | 拉取待执行命令 | +| `POST` | `/device/v2/commands/{id}/ack` | 同上 | 否 | 上报命令阶段或终态 | +| `POST` | `/device/v2/telemetry:batch` | 同上 | 是 | 批量上报设备遥测 | +| `GET` | `/device/v2/firmware-releases/{id}/download` | 同上 | 否 | 下载受控固件制品 | + +`bootstrap` 示例: + +```http +GET /device/v2/bootstrap?firmware_version=1.0.0&board_rev=echoear-v1&rtc_sdk_version=1.0.0&codec=pcm_s16le&sample_rate=16000&device_time=2026-09-03T08%3A00%3A00Z&network=wifi +``` + +配置拉取建议:首次不带 `If-None-Match`;保存响应 `ETag`,后续携带该值。HTTP `304` 表示继续使用本地有效配置。 + +### 6.2 旧 RTC 通话(v2,仅兼容旧设备) + +| 方法 | 路径 | 说明 | +|---|---|---| +| `POST` | `/device/v2/conversations` | 创建旧 RTC 通话 | +| `POST` | `/device/v2/conversations/{id}/activate` | 入房成功后激活供应商任务 | +| `GET` | `/device/v2/conversations/{id}` | 查询通话权威状态 | +| `POST` | `/device/v2/conversations/{id}/heartbeat` | 通话心跳 | +| `POST` | `/device/v2/conversations/{id}/stop` | 停止通话 | +| `POST` | `/device/v2/conversations/{id}/turns:batch` | 补传最终字幕 | + +新 EchoEar 固件的 `voice_runtime=volc_hardware_agent` 时不得调用这组 RTC 创建/激活接口。 + +### 6.3 录音上传(v2 公共媒体能力) + +| 方法 | 路径 | 作用 | +|---|---|---| +| `POST` | `/device/v2/conversations/{id}/upload-batches` | 创建分片上传批次 | +| `GET` | `/device/v2/upload-batches/{id}` | 查询上传校验状态 | +| `POST` | `/device/v2/upload-batches/{id}/complete` | 通知平台分片已上传 | +| `POST` | `/device/v2/media-segments/{id}/local-delete-ack` | 回报本地密文已删除 | + +只有 `ACTIVE` 设备且本次隐私策略允许时才能使用;没有录音授权不得采集、缓存或上传 PCM。 + +## 7. 火山硬件智能体接口(v3) + +### 7.1 申请火山一机一密 + +```http +POST /device/v3/hardware-agent-credentials:provision +Idempotency-Key: 2ee3b675-59c0-4d93-a189-6db7f8db3750 +Cache-Control: no-store +Content-Type: application/json +``` + +状态要求:`BOUND`、`PROVISIONING` 或 `ACTIVE`。 + +请求: + +```json +{ + "hardware_id": "echoear-b43a451634a8" +} +``` + +`hardware_id` 必须来自稳定硬件身份(如 eFuse/Chip ID 的规范化值),设备重启、升级和恢复出厂后保持不变。 + +成功响应: + +```json +{ + "request_id": "req-...", + "data": { + "instance_id": "", + "product_key": "", + "device_name": "echoear-...", + "hardware_id": "echoear-b43a451634a8", + "device_secret": "", + "credential_ref": "", + "credential_version": 1, + "credential_source": "platform_dynamic_registration", + "issued_at": "2026-09-03T08:00:00Z" + } +} +``` + +安全要求: + +- 响应带 `Cache-Control: no-store` 和 `Pragma: no-cache`。 +- `device_secret` 只写入设备安全存储,不进入普通配置文件、串口日志、崩溃转储或平台遥测。 +- `credential_source=platform_stored` 表示幂等返回平台已保存的凭据,不代表重新消耗 License。 +- `hardware_id` 与已注册记录不一致时返回冲突,不允许覆盖原凭据。 + +常见错误: + +| HTTP | 错误码 | 处理 | +|---|---|---| +| `400` | `VALIDATION_FAILED` | 修正 `hardware_id` 或幂等键 | +| `409` | `HARDWARE_AGENT_BINDING_REQUIRED` | 先在平台绑定 Bot/产品配置 | +| `409` | `HARDWARE_AGENT_PRODUCT_MISMATCH` | 运维核对实例、产品和设备绑定 | +| `409` | `HARDWARE_AGENT_HARDWARE_MISMATCH` | 停止自动重试,核对硬件身份 | +| `409` | `DEVICE_ACTIVATION_REQUIRED` | 先完成用户绑定,进入 `BOUND` | +| `503` | `HARDWARE_AGENT_PROVISIONING_UNAVAILABLE` | 平台未配置动态注册;联系运维 | +| `503` | `HARDWARE_AGENT_PROVISIONING_FAILED` | 使用原幂等键、原正文和新签名退避重试;由 `request_id` 查火山原始错误 | + +### 7.2 创建本次硬件智能体会话 + +```http +POST /device/v3/agent-sessions +Idempotency-Key: d7f08e7a-9e1d-455f-b8ec-a745a34d8790 +Content-Type: application/json +``` + +状态要求:仅 `ACTIVE`。每次聊天创建一个新会话,不复用上一通响应。 + +请求示例: + +```json +{ + "device_time": "2026-09-03T08:05:00Z", + "monotonic_ms": 123456, + "capabilities": { + "hardware_agent": true, + "sdk_version": "1.0.0-echoear-platform.1", + "transports": ["low_load"], + "input_audio_format": "pcm_s16le_16000_mono", + "output_audio_format": "pcm_s16le_16000_mono", + "server_event_callback": false, + "transcript_upload": true, + "dynamic_session_config": true, + "credential_rotation": true, + "near_pcm_recording": false, + "far_pcm_recording": false + }, + "network": { + "type": "wifi", + "rssi": -52 + }, + "requested_goal": "open_conversation", + "config_revision": 1 +} +``` + +约束: + +- `transports` 只能包含 `low_load`、`high_quality`,至少一个。 +- `transcript_upload`、`dynamic_session_config`、`credential_rotation` 必须为 `true`。 +- EchoEar 当前使用 `low_load`;`server_event_callback=false` 时设备事件和最终字幕上传是权威回流。 +- `config_revision` 必须使用最近一次 `/device/v2/config` 返回的版本。 + +成功响应核心字段: + +```json +{ + "request_id": "req-...", + "data": { + "conversation_id": "", + "status": "SESSION_READY", + "runtime_type": "volc_hardware_agent", + "runtime": { + "provider": "volcengine", + "bot_id": "", + "transport": "low_load", + "provider_session_ref": "", + "session_nonce": "", + "config_digest": "", + "wait_for_session_update": true + }, + "session_config": { + "dialog_id": "", + "system_prompt": "<本次冻结的人设提示>", + "persona_tone": "<语气>", + "authorized_context": "<已授权背景/近期摘要/长期记忆>", + "prompt_bundle_version": "" + }, + "privacy": { + "retention_mode": "transcript_only", + "record_near": false, + "record_far": false, + "upload_allowed": false + }, + "session": { + "engine_mode": "volc_hardware_agent", + "heartbeat_interval_sec": 10, + "allocation_expires_at": "2026-09-03T08:10:00Z" + } + } +} +``` + +设备处理顺序: + +1. 只在运行内存保存 `session_nonce`。 +2. 使用预配取得的一机一密连接响应指定的 Bot 和传输模式。 +3. 以 `dialog_id` 作为本次业务对话标识。 +4. 在开启麦克风前通过 SDK 动态配置或 WebSocket `session.update` 注入 `system_prompt`、`persona_tone` 和 `authorized_context`。 +5. 收到火山配置已应用确认后,再上报 `session_started`。 + +`tool_scope_token` 如存在也只能保存在运行内存,并且仅用于响应指定的 `tool_url` 和本次会话。 + +### 7.3 上报会话事件 + +```http +POST /device/v3/agent-sessions/{conversation_id}/events +Content-Type: application/json +``` + +开始事件: + +```json +{ + "event_id": "evt-start-0001", + "sequence_no": 1, + "event_type": "session_started", + "occurred_at": "2026-09-03T08:05:03Z", + "provider_session_ref": "", + "session_nonce": "", + "provider_connection_ref": "", + "bot_id": "", + "transport": "low_load", + "config_digest": "", + "dynamic_config_applied": true +} +``` + +后续事件类型: + +- `session_updated`:按 `heartbeat_interval_sec` 上报存活;`sequence_no` 严格递增。 +- `session_ended`:正常结束。 +- `session_failed`:异常结束,可附加稳定、脱敏的 `error_code`。 + +响应: + +```json +{ + "request_id": "req-...", + "data": { + "conversation_id": "", + "status": "ACTIVE", + "action": "continue", + "duplicate": false, + "accepted_at": "2026-09-03T08:05:03Z" + } +} +``` + +`action=stop_session` 时,设备必须立即关闭麦克风和火山连接,然后补报 `session_ended` 或 `session_failed`。相同 `event_id` 重放时必须保持原正文一致。 + +### 7.4 批量上传最终话轮 + +```http +POST /device/v3/agent-sessions/{conversation_id}/turns:batch +Idempotency-Key: 1710b8cc-9bf2-45e8-a16d-f32c3b80cbbe +Content-Type: application/json +``` + +请求: + +```json +{ + "turns": [ + { + "turn_key": "user-final-0001", + "sequence_no": 1, + "speaker": "subject", + "start_at": "2026-09-03T08:05:10Z", + "end_at": "2026-09-03T08:05:12Z", + "final_text": "今天天气怎么样?", + "interrupted": false + }, + { + "turn_key": "agent-final-0002", + "sequence_no": 2, + "speaker": "agent", + "start_at": "2026-09-03T08:05:12Z", + "end_at": "2026-09-03T08:05:15Z", + "final_text": "我来帮您看看。", + "interrupted": false + } + ] +} +``` + +只上传最终字幕;不得上传增量识别结果。单批 1—500 条,`speaker` 只能为 `subject` 或 `agent`。同一 `turn_key`/`sequence_no` 的重试必须保持正文完全一致。 + +成功响应: + +```json +{ + "request_id": "req-...", + "data": { + "conversation_id": "", + "accepted_count": 2, + "duplicate_count": 0 + } +} +``` + +## 8. App/Web 的设备接口 + +以下接口使用用户 OIDC Bearer Token,不使用设备 HMAC: + +| 方法 | 路径 | 作用 | +|---|---|---| +| `POST` | `/api/v1/device-claims` | 消费绑定码并绑定设备 | +| `GET` | `/api/v1/devices` | 列出当前用户可管理设备 | +| `GET` | `/api/v1/devices/{id}` | 查看设备详情 | +| `GET` | `/api/v1/devices/{id}/binding-impact` | 变更绑定前评估影响 | +| `POST` | `/api/v1/devices/{id}/binding-change` | 解绑或改绑 | +| `GET` | `/api/v1/devices/{id}/presence` | 查看在线状态 | +| `GET` | `/api/v1/devices/{id}/twin` | 查看设备影子 | +| `PATCH` | `/api/v1/devices/{id}/twin` | 修改期望配置;必须携带 `If-Match` | +| `GET` | `/api/v1/devices/{id}/commands` | 查看命令历史 | +| `POST` | `/api/v1/devices/{id}/commands` | 下发设备命令 | + +绑定码由平台/运维为 `CLAIMABLE` 设备生成,App 只消费,不自行生成。解绑会影响设备激活状态和火山运行凭据,必须先调用 `binding-impact` 展示影响并要求用户确认。 + +## 9. 平台运维设备接口 + +所有接口要求 OIDC Bearer Token 和有效的 `X-Tenant-Id: `,且仅独立运维账号可调用。 + +| 方法 | 路径 | 作用 | +|---|---|---| +| `GET` | `/api/v1/ops/devices` | 设备列表 | +| `POST` | `/api/v1/ops/devices` | 登记设备并一次性返回平台 Device Secret | +| `GET` | `/api/v1/ops/device-assignment-options` | 可选家庭/长者列表 | +| `PATCH` | `/api/v1/ops/devices/{id}` | 修改设备;必须携带 `If-Match` | +| `DELETE` | `/api/v1/ops/devices/{id}` | 吊销设备;必须携带 `If-Match` | +| `POST` | `/api/v1/ops/devices/{id}/credentials/rotate` | 轮换平台 Device Secret,只返回一次明文 | +| `POST` | `/api/v1/ops/devices/{id}/claims` | 生成一次性绑定码 | +| `POST` | `/api/v1/ops/devices/{id}/activation-transitions` | 人工迁移激活状态 | +| `GET` | `/api/v1/ops/devices/{id}/activation-transitions` | 查询激活迁移历史 | + +### 9.1 登记硬件智能体设备 + +```json +{ + "model": "echoear", + "hardware_revision": "esp32-s3", + "minimum_security_version": "1", + "minimum_firmware_version": "1.0.0", + "household_id": "", + "subject_id": "", + "voice_runtime": "volc_hardware_agent", + "activation_state": "FACTORY" +} +``` + +Instance ID、Product Key、默认 Bot、传输模式和最低 SDK 由平台产品配置统一持有。设备第一次预配时自动生成绑定,运维创建设备或设备自身都不能覆盖这些字段。 + +响应中的 `data.credential.secret` 只显示一次。运维系统应立即通过受控注入工具写入设备安全区,不得复制到工单、聊天记录或普通数据库字段。 + +### 9.2 人工迁移激活状态 + +```json +{ + "target_state": "CLAIMABLE", + "reason": "工厂质检通过", + "expected_revision": 1 +} +``` + +并发控制:列表/详情返回的设备版本对应 `ETag`;修改和删除使用 `If-Match`。激活迁移单独使用 `expected_revision`。版本不一致返回 `412 VERSION_CONFLICT` 或 `409 DEVICE_ACTIVATION_CONFLICT`,刷新后再决定,不能强制覆盖。 + +## 10. 幂等、重试和本地持久化 + +| 数据 | 本地策略 | +|---|---| +| 平台 Device Secret | 安全区长期保存;轮换时原子替换 | +| 火山 Device Secret | 安全区长期保存;不要每次启动都预配 | +| `session_nonce` | 仅本次通话运行内存 | +| `tool_scope_token` | 仅本次通话运行内存 | +| 最终字幕待上传队列 | 授权允许时加密持久化,成功确认后删除 | +| 增量字幕/原始音频 | 默认不长期保存 | + +重试规则: + +- `Idempotency-Key` 长度 16—128 字符。 +- HTTP 超时或 `retryable=true`:原业务正文 + 原幂等键 + 新 HMAC nonce/signature,指数退避。 +- `401`:先检查平台 Device Secret、时钟、请求 URI、正文摘要和 Base64URL 编码,不要盲目轮换密钥。 +- `409 IDEMPOTENCY_KEY_REUSED`:同一幂等键配了不同正文;这是客户端错误。 +- `HARDWARE_SESSION_PROOF_INVALID`:丢弃当前会话内存数据,重新创建新会话,不尝试其他设备的 nonce。 +- `action=stop_session`:优先停止音频和供应商连接,再做结束确认及本地队列收尾。 + +## 11. EchoEar 固件接入清单 + +出厂只写入: + +- 平台 `device_id`; +- 平台 Device Secret; +- `hardware_id`; +- API Base URL 和服务器信任锚; +- 固件签名信任锚。 + +首次绑定后设备取得并安全保存: + +- 火山 `instance_id`; +- 火山 `product_key`; +- 火山 `device_name`; +- 火山 Device Secret; +- `credential_version`。 + +绝不写入设备: + +- 火山 Product Secret; +- 平台数据库主密钥/KMS 密钥; +- 其他设备的 Device Secret; +- 固定的会话 nonce 或工具 Token; +- 人设、长期记忆和聊天记录的永久明文副本。 + +会话侧必须做到: + +- 每通先请求平台会话配置,再连接火山; +- 人设、近期摘要和长期记忆以本次 `session_config` 为准; +- 动态配置确认前不打开麦克风; +- 设备事件和最终字幕可靠回流平台; +- 平台下发 `stop_session` 时可在一个心跳周期内停止。 + +## 12. 当前实现边界 + +- 当前 API 已实现平台 HMAC、激活状态机、火山动态注册、v3 会话控制面、事件回流和最终字幕上传。 +- 设备音频是设备直连火山,平台不代理每一帧音频;平台通过设备事件和字幕回流维护会话权威状态。 +- Windows/EchoEar 客户端仍需按目标 SDK 完成真实音频 HAL、火山协议、断线重连和安全存储验收。 +- `/device/v3/hardware-agent-license:ack` 处理器代码虽曾存在,但没有路由、没有生产契约,也不应由设备调用。 +- 完整字段定义以 [`openapi/openapi.yaml`](../openapi/openapi.yaml) 为机器可读基准;本文件是设备研发和联调的实施说明。 + +## 13. 联调最小验收用例 + +1. 错误平台签名返回 `401`,日志不泄露设备是否存在。 +2. 同 nonce 重放被拒绝;相同业务幂等键配新 nonce 可安全重试。 +3. `CLAIMABLE` 设备不能预配,`BOUND` 设备可以预配但不能创建会话。 +4. 首次预配成功取得火山一机一密;重复预配不重复注册。 +5. Product Secret 在设备固件、NVS、日志和网络响应中均不存在。 +6. `ACTIVE` 设备创建 v3 会话并得到正确 Bot ID、人设和授权上下文。 +7. 未确认动态配置的 `session_started` 被拒绝。 +8. 正常开始、心跳、最终字幕、结束全部回流平台。 +9. 平台返回 `stop_session` 后设备关闭连接并补报结束。 +10. 火山 License 额度耗尽时预配失败,扩容后使用原幂等请求可恢复;设备无需安装 License 或 ACK。 diff --git a/docs/device-integration.md b/docs/device-integration.md index 67c8cd7..49d3b81 100644 --- a/docs/device-integration.md +++ b/docs/device-integration.md @@ -1,5 +1,21 @@ # 设备接入与启动配置联调说明 +## 设备激活状态机 + +平台从V50开始独立维护 `activation_state`,不再用通用的 `device.status` 代替激活生命周期: + +`FACTORY → CLAIMABLE → BOUND → PROVISIONING → ACTIVE → LOST/REVOKED` + +- `FACTORY`:只有设备身份和平台HMAC凭据,生产质检完成后由运维放行。 +- `CLAIMABLE`:可以生成一次性绑定码;设备可同步基础配置,但不能发起对话。 +- `BOUND`:已经绑定家庭和长者,可以调用火山设备凭据预配接口。 +- `PROVISIONING`:平台正在代设备调用火山 DynamicRegister 并保存一机一密;动态注册成功、有效绑定和设备凭据就绪后进入 `ACTIVE`。火山 License 是产品侧动态注册额度,不是设备安装包,也没有设备回执。 +- `ACTIVE`:允许创建和上报对话。硬件智能体会话接口只接受该状态。 +- `LOST`:设备身份认证、对话和火山运行凭据立即停用;核验归属和硬件身份后可恢复到 `ACTIVE`。 +- `REVOKED`:终态。设备平台凭据、火山凭据、绑定和未使用绑定码全部吊销,不能恢复。 + +每次迁移都会增加 `activation_revision` 并写入 `device_activation_transition`。运维人工迁移接口为 `POST /api/v1/ops/devices/{id}/activation-transitions`,请求必须携带当前 `expected_revision`;用户认领、设备预配和删除由对应业务接口自动迁移,不应由客户端伪造状态。 + ## 1. 当前接口 | 方法 | 路径 | 作用 | @@ -270,6 +286,41 @@ powershell -NoProfile -ExecutionPolicy Bypass -File .\scripts\smoke-sim-firmware 脚本会验证创建、首次激活、相同幂等键重放、Worker调用、两次签名回调去重和最终`ACTIVE`状态,并断言供应商仅启动一次。脚本只使用合成数据,验证结束后按本次通话编号精确清理数据库痕迹。 +## 11.5 火山硬件对话智能体 v3 控制面 + +设备首次准备火山连接凭据时,先使用既有平台设备签名调用`POST /device/v3/hardware-agent-credentials:provision`并提交稳定`hardware_id`。平台从产品级配置解析Instance ID、Product Key、默认Bot、传输方式和最低SDK版本,自动建立当前设备的运行绑定;再从KMS或只读Secret文件加载Product Secret,以平台生成的Device Name代设备调用火山DynamicRegister。Product Secret永不到达设备。平台把返回的`device_secret`用独立的`VOLC_HARDWARE_DEVICE_CREDENTIAL_MASTER_KEY`执行AES-256-GCM加密后保存,只在设备认证且`Cache-Control: no-store`的响应中返回给同一设备。设备使用安全芯片、系统KeyStore或Windows Safe Storage保存这份一机一密,后续用于火山WebSocket签名。 + +火山硬件对话智能体 License 是产品侧可用额度。平台调用 DynamicRegister 时由火山自动消耗一份有效额度并返回对应设备的一机一密;设备不会收到 License 明文,也不需要提交安装回执。当前有效路由不包含`POST /device/v3/hardware-agent-license:ack`,预配响应也不包含`license`字段。平台可以维护额度监控或影子账本,但不得把自建池作为设备运行凭据来源。额度耗尽或到期时,应根据火山动态注册错误告警并扩容,扩容后重试原预配请求。 + +同一设备和Hardware ID重复调用时,平台解密返回既有一机一密,不重复注册;Hardware ID、产品或实例不一致时返回409,必须走受控轮换而不是覆盖。Product Secret、Device Secret明文不得进入日志、审计详情、错误响应、通话记录或测试报告。自动回归的mock密钥仅允许在隔离测试进程中注入,不得写入正式设备缓存。 + +设备期望配置中的`voice_runtime`为`volc_hardware_agent`时,不再调用`POST /device/v2/conversations`创建RTC房间。首次预配后设备要重新拉取配置版本,并读取平台发布的`minimum_sdk_version`执行本地预检;云端会以产品默认绑定中的同一版本再次门禁,过低时返回HTTP 426和`HARDWARE_AGENT_SDK_UPDATE_REQUIRED`。每次用户开始聊天必须先调用`POST /device/v3/agent-sessions`,不得复用上一次响应。创建请求必须声明SDK版本、输入/输出音频格式、传输模式、可靠字幕队列、动态会话配置、凭证轮换以及近/远端PCM录制能力。平台会在同一事务中冻结这些能力、有效授权、已发布人设、档案、最近三次已完成通话摘要和最多五条人工确认的长期记忆,并返回: + +- `runtime.bot_id`、传输模式和仅本次有效的`session_nonce`; +- `session_config.dialog_id`,必须作为火山会话的业务对话标识; +- `system_prompt`、`persona_tone`和`authorized_context`,必须在开始发送音频前通过SDK动态配置或标准WebSocket的`session.update`注入; +- `config_digest`,用于设备日志和平台诊断比对,不得当作供应商凭据; +- 可选的`tool_url`和`tool_scope_token`,只允许配置为本次火山会话的只读记忆工具;令牌绑定本次通话和供应商会话引用,设备不得持久化或转给其他会话; +- 本次冻结的隐私策略和会话存活间隔。 + +设备收到火山`session.created`后必须先应用本次`session.update`,收到确认后才向`POST /device/v3/agent-sessions/{id}/events`提交`session_started`。开始事件必须同时提交火山实际会话关联号、`bot_id`、传输模式、平台原样返回的`config_digest`和`dynamic_config_applied=true`;任何一项不一致都不会进入`ACTIVE`,此时不得打开麦克风。通话期间按响应中的心跳间隔提交`session_updated`;响应`action=continue`时保持会话,收到`action=stop_session`时必须立即关闭火山连接,然后提交`session_ended`或`session_failed`。这样,家庭撤回授权、运营停话和设备吊销即使发生在设备直连火山期间,也会收敛到设备执行。 + +平台不是靠轮询火山控制台才知道通话状态:v3设备事件在同一事务内更新权威通话状态,既有数据库触发器随`ACTIVE/COMPLETED/FAILED`写入最小`web_event`,已登录SaaS页面通过认证SSE收到`conversation.started/completed/failed`并刷新列表。`session_updated`只续租并更新硬件事件水位,不向家庭页面逐心跳广播,避免每10秒放大为整页查询;运维诊断仍可读取最后事件时间和序号。 + +录音能力采用保守门禁:只有创建请求同时声明`near_pcm_recording=true`和`far_pcm_recording=true`时,平台才可能依据既有授权返回`transcript_and_audio`。缺任一能力都会把本次会话强制降级为`transcript_only`(没有字幕授权时为`none`),并把`record_near`、`record_far`、`upload_allowed`全部置为`false`,设备不得以混音或供应商未知格式冒充双轨录音。 + +最终字幕写入本地加密重试队列,使用`POST /device/v3/agent-sessions/{id}/turns:batch`上传。只上传用户最终识别事件和智能体`response.audio_transcript.done`,不得长期保存或上传增量字幕;同一`turn_key`或`sequence_no`重放必须保持正文完全一致。高质量传输可额外配置火山服务端状态/字幕回调作为旁路证据;低负载传输按火山当前约束没有该服务端回调,因此设备事件与终稿上传是强制能力,创建请求必须声明`transcript_upload=true`。 + +Product Secret只保存在平台KMS/Secret挂载中;平台注册结果中的`device_secret`在平台数据库加密保存,并只下发给对应设备的安全区或供应商SDK凭据存储。普通会话响应和日志不得包含该值。当前Windows标准WebSocket测试程序覆盖平台代理注册和真实低负载协议;高质量方案的火山硬件SDK二进制和板级音频HAL仍必须由目标芯片工程集成后执行真机验收。 + +本地控制面闭环可执行: + +```powershell +powershell -NoProfile -ExecutionPolicy Bypass -File .\scripts\smoke-hardware-agent.ps1 +``` + +脚本先证明过低SDK被HTTP 426拒绝、未完成动态配置不能激活,再验证产品默认Bot绑定、每次会话上下文预取、录音能力安全降级、开始事件、最终字幕、平台停话、`stop_session`下发、设备结束确认、能力快照和数据库状态收敛,并精确清理临时会话及绑定。 + ## 12. 错误处理 | HTTP状态 | 错误码 | 处理建议 | @@ -279,6 +330,8 @@ powershell -NoProfile -ExecutionPolicy Bypass -File .\scripts\smoke-sim-firmware | 503 | `DEVICE_AUTHENTICATION_UNAVAILABLE` | 退避后使用新随机数重试,不关闭鉴权 | | 503 | `DEVICE_CONFIG_UNAVAILABLE` / `DEVICE_BOOTSTRAP_FAILED` | 退避重试并保留上一次有效配置 | | 503 | `RUNTIME_CONFIG_UNAVAILABLE` | 生产环境没有命中活动语音路由;停止创建通话,由运营发布或恢复目标范围配置 | +| 426 | `HARDWARE_AGENT_SDK_UPDATE_REQUIRED` | 当前火山硬件智能体SDK低于设备绑定门禁;停止创建会话并升级固件/SDK | +| 503 | `HARDWARE_AGENT_PROVISIONING_FAILED` | 火山动态注册失败;用原幂等键、原正文和新签名退避重试,并由运维根据`request_id`检查产品License额度、有效期和火山原始错误 | | 403 | `CONSENT_REQUIRED` | 不重试,等待有效授权 | | 403 | `TRANSCRIPT_STORAGE_NOT_ALLOWED` | 停止补传,等待留存策略或授权恢复,不得本地绕过 | | 409 | `DEVICE_NOT_BOUND` / `CONVERSATION_ALREADY_ACTIVE` | 修复绑定或查询已有通话 | @@ -288,6 +341,8 @@ powershell -NoProfile -ExecutionPolicy Bypass -File .\scripts\smoke-sim-firmware | 409 | `CONFIG_VERSION_CONFLICT` | 先重新获取设备配置再尝试创建新通话 | | 409 | `PROVIDER_EVENT_CONFLICT` | 供应商事件编号复用且正文不同,转人工排查 | | 409 | `CONVERSATION_STATE_CONFLICT` | 查询云端当前通话状态,不要强制覆盖状态机 | +| 401 | `HARDWARE_SESSION_PROOF_INVALID` | 丢弃本地会话,不得尝试其他设备或其他会话的nonce;重新创建会话 | +| 409 | `HARDWARE_AGENT_EVENT_CONFLICT` | 保留原事件和单调序号,查询当前会话状态并人工核对,不得覆盖 | | 409 | `TRANSCRIPT_TURN_CONFLICT` | 保留本地终稿和冲突明细,禁止覆盖云端终稿,转入人工对账 | | 409 | `TELEMETRY_EVENT_CONFLICT` | 保留冲突事件并停止推进本地确认点,人工核对事件键和单调序号 | | 404 | `TELEMETRY_CONVERSATION_NOT_FOUND` | 删除错误通话引用,不得改用任意长者编号重试 | diff --git a/docs/device-registration-to-chat-api.md b/docs/device-registration-to-chat-api.md new file mode 100644 index 0000000..1d430f6 --- /dev/null +++ b/docs/device-registration-to-chat-api.md @@ -0,0 +1,857 @@ +# 设备注册到聊天记录上传接口文档 + +## 1. 设备请求平台时的请求头 + +运营后台和用户 App 调用 `/api/v1/*` 时使用: + +```http +Authorization: Bearer <用户访问令牌> +``` + +设备调用 `/device/*` 时使用以下 HMAC 请求头: + +| 请求头 | 中文含义 | 计算方式 | +|---|---|---| +| `X-Device-Id` | 平台设备编号 | 平台登记设备时返回的 `credential.device_id` | +| `X-Device-Timestamp` | 请求时间 | 当前 UTC 时间,RFC3339 格式,例如 `2026-09-05T09:20:00Z` | +| `X-Device-Nonce` | 防重放随机数 | 每个请求重新生成,长度 16–128 字符,不得重复 | +| `X-Device-Content-SHA256` | 请求正文摘要 | `hex_lower(SHA256(rawBody))`;GET 使用空字节正文 | +| `X-Device-Signature` | 请求签名 | `base64url_no_padding(HMAC-SHA256(platformDeviceSecret, canonical))` | +| `Idempotency-Key` | 业务幂等键 | 仅指定接口需要;同一次请求重试必须保持不变,长度 16–128 字符 | +| `Content-Type` | 正文类型 | 有 JSON 正文时为 `application/json` | + +签名原文 `canonical`: + +```text +{大写HTTP方法}\n{请求路径和查询参数}\n{X-Device-Timestamp}\n{X-Device-Nonce}\n{X-Device-Content-SHA256} +``` + +计算示例: + +```text +body = UTF8('{"hardware_id":"echoear-a1b2c3"}') +bodySha256 = hex_lower(SHA256(body)) +timestamp = "2026-09-05T09:20:00Z" +nonce = "d8b81b916ddd4f4584a80a82" + +canonical = +"POST\n" + +"/device/v3/hardware-agent-credentials:provision\n" + +timestamp + "\n" + +nonce + "\n" + +bodySha256 + +signature = base64url_no_padding( + HMAC-SHA256(platformDeviceSecret, UTF8(canonical)) +) +``` + +注意: + +- 摘要必须针对实际发送的原始字节计算,签名后不能再次格式化 JSON。 +- 查询参数属于签名内容,例如 `/device/v2/commands?limit=10`。 +- GET 空正文 SHA-256 固定为 `e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855`。 +- `platformDeviceSecret` 是平台注册时返回的设备密钥,不是火山 `Product Secret`,也不是火山 `device_secret`。 +- 平台统一响应:成功为 `{"request_id":"...","data":...}`;失败为 `{"request_id":"...","error":...}`。 + +## 2. 完整流程 + +| 顺序 | 调用方 | 动作 | 接口/地址 | 结果 | +|---:|---|---|---|---| +| 1 | 运营平台 | 登记设备 | `POST /api/v1/ops/devices` | 返回平台设备 ID、平台设备密钥和绑定码 | +| 2 | 用户 App | 绑定设备 | `POST /api/v1/device-claims` | 状态变为 `BOUND` | +| 3 | 设备 | 获取火山运行凭据 | `POST /device/v3/hardware-agent-credentials:provision` | 平台动态注册火山设备,消耗一个 License,状态变为 `ACTIVE` | +| 4 | 设备 | 检查固件 | `GET /device/v2/bootstrap` | 得到 `firmware_compatible` | +| 5 | 设备 | 获取设备配置 | `GET /device/v2/config` | 得到配置版本和运行时配置 | +| 6 | 设备 | 判断是否继续 | `firmware_compatible=false` 时停止聊天并进入 OTA 流程 | 只有兼容固件可以继续 | +| 7 | 设备 | 创建对话会话 | `POST /device/v3/agent-sessions` | 获取 Bot ID、系统提示词、人设和授权记忆 | +| 8 | 设备 | 连接火山 | 火山 WebSocket + `session.update` | 注入本次人设并建立 16 kHz PCM 对话 | +| 9 | 设备 | 上报会话开始 | `POST /device/v3/agent-sessions/{id}/events` | 平台会话变为 `ACTIVE` | +| 10 | 设备 | 上传最终聊天文本 | `POST /device/v3/agent-sessions/{id}/turns:batch` | 用户和智能体最终文本入库 | +| 11 | 设备 | 上报会话结束 | 同一个 events 接口,`session_ended` | 平台会话变为 `COMPLETED` | + +激活状态: + +```text +FACTORY → CLAIMABLE → BOUND → PROVISIONING → ACTIVE → LOST/REVOKED +``` + +设备重置流程: + +```text +停止语音 → 上传剩余聊天记录 → 上报session_ended → POST /device/v3/factory-reset +→ 平台解除绑定并暂停火山凭据 → 设备按local_actions清理 → 重启进入CLAIMABLE +``` + +## 3. 接口 + +### 3.1 登记设备 + +地址: + +```http +POST /api/v1/ops/devices +``` + +输入: + +| 字段 | 类型 | 必填 | 中文含义 | +|---|---|---:|---| +| `model` | string | 是 | 设备型号 | +| `hardware_revision` | string | 否 | 硬件版本 | +| `minimum_security_version` | string | 否 | 最低安全版本 | +| `minimum_firmware_version` | string | 否 | 最低固件版本 | +| `household_id` | UUID | 否 | 直接绑定的家庭编号;走绑定码时不传 | +| `subject_id` | UUID | 否 | 直接绑定的服务对象编号;走绑定码时不传 | +| `voice_runtime` | enum | 是 | 传 `volc_hardware_agent` | +| `hardware_agent_bot_id` | string | 否 | 兼容字段;当前方案不传,由平台产品配置决定 | +| `hardware_agent_transport` | enum | 否 | `low_load` 或 `high_quality`;建议由平台配置 | +| `hardware_agent_minimum_sdk_version` | string | 否 | 最低硬件智能体 SDK 版本 | +| `activation_state` | enum | 是 | `FACTORY`、`CLAIMABLE` 或 `BOUND`;一般传 `CLAIMABLE` | + +输出: + +| 字段 | 类型 | 中文含义 | +|---|---|---| +| `device.id` | UUID | 平台设备编号 | +| `device.status` | string | 设备业务状态 | +| `device.activation_state` | enum | 激活状态 | +| `device.activation_revision` | integer | 激活状态版本 | +| `device.model` | string | 设备型号 | +| `device.hardware_revision` | string/null | 硬件版本 | +| `device.minimum_security_version` | string/null | 最低安全版本 | +| `device.minimum_firmware_version` | string/null | 最低固件版本 | +| `device.voice_runtime` | enum | 语音运行时 | +| `device.version` | integer | 设备记录版本 | +| `credential.device_id` | UUID | 平台设备编号 | +| `credential.secret` | string | 设备调用平台所用的 HMAC 密钥,仅明文返回一次 | +| `credential.key_version` | integer | 平台设备密钥版本 | +| `credential.issued_at` | datetime | 密钥签发时间 | +| `claim.claim_code` | string/null | 一次性绑定码 | +| `claim.issued_at` | datetime/null | 绑定码签发时间 | +| `claim.expires_at` | datetime/null | 绑定码过期时间 | + +示例: + +```http +POST /api/v1/ops/devices +Authorization: Bearer +Content-Type: application/json + +{ + "model": "echoear-esp32s3", + "hardware_revision": "v1.0", + "minimum_firmware_version": "1.0.0", + "voice_runtime": "volc_hardware_agent", + "activation_state": "CLAIMABLE" +} +``` + +```json +{ + "request_id": "req-register-001", + "data": { + "device": { + "id": "10000000-0000-0000-0000-000000000512", + "status": "registered", + "activation_state": "CLAIMABLE", + "activation_revision": 1, + "model": "echoear-esp32s3", + "voice_runtime": "volc_hardware_agent", + "version": 1 + }, + "credential": { + "device_id": "10000000-0000-0000-0000-000000000512", + "secret": "", + "key_version": 1, + "issued_at": "2026-09-05T09:00:00Z" + }, + "claim": { + "claim_code": "ABCD-EFGH-IJKL-MNOP", + "issued_at": "2026-09-05T09:00:00Z", + "expires_at": "2026-09-06T09:00:00Z" + } + } +} +``` + +### 3.2 用户绑定设备 + +地址: + +```http +POST /api/v1/device-claims +``` + +输入: + +| 字段 | 类型 | 必填 | 中文含义 | +|---|---|---:|---| +| `claim_code` | string | 是 | 一次性绑定码,不能填写设备 UUID | +| `household_id` | UUID | 是 | 家庭编号 | +| `subject_id` | UUID | 是 | 设备服务对象编号 | + +输出: + +| 字段 | 类型 | 中文含义 | +|---|---|---| +| `binding_id` | UUID | 设备绑定关系编号 | +| `device_id` | UUID | 平台设备编号 | +| `household_id` | UUID | 家庭编号 | +| `subject_id` | UUID | 服务对象编号 | +| `bound_at` | datetime | 绑定时间 | + +示例: + +```http +POST /api/v1/device-claims +Authorization: Bearer +Content-Type: application/json + +{ + "claim_code": "ABCD-EFGH-IJKL-MNOP", + "household_id": "10000000-0000-0000-0000-000000000211", + "subject_id": "10000000-0000-0000-0000-000000000411" +} +``` + +```json +{ + "request_id": "req-bind-001", + "data": { + "binding_id": "10000000-0000-0000-0000-000000000611", + "device_id": "10000000-0000-0000-0000-000000000512", + "household_id": "10000000-0000-0000-0000-000000000211", + "subject_id": "10000000-0000-0000-0000-000000000411", + "bound_at": "2026-09-05T09:10:00Z" + } +} +``` + +### 3.3 占用 License 并获取火山设备凭据 + +地址: + +```http +POST /device/v3/hardware-agent-credentials:provision +``` + +输入: + +| 字段 | 位置 | 类型 | 必填 | 中文含义 | +|---|---|---|---:|---| +| `Idempotency-Key` | Header | string | 是 | 凭据申请幂等键,16–128 字符 | +| `hardware_id` | Body | string | 是 | 不随固件升级变化的物理硬件标识 | + +输出: + +| 字段 | 类型 | 中文含义 | +|---|---|---| +| `instance_id` | string | 火山硬件智能体实例编号 | +| `product_key` | string | 火山产品标识 | +| `device_name` | string | 火山设备名称 | +| `hardware_id` | string | 已绑定的物理硬件标识 | +| `device_secret` | string | 火山设备专属密钥,需要加密保存 | +| `credential_ref` | string | 平台内部凭据引用 | +| `credential_version` | integer | 火山设备凭据版本 | +| `credential_source` | enum | `platform_dynamic_registration` 为新注册,`platform_stored` 为复用已有凭据 | +| `issued_at` | datetime | 凭据签发时间 | + +示例: + +```http +POST /device/v3/hardware-agent-credentials:provision +Idempotency-Key: provision-device-00000512-v1 +Content-Type: application/json +<设备HMAC请求头> + +{"hardware_id":"echoear-esp32s3-a1b2c3d4"} +``` + +```json +{ + "request_id": "req-provision-001", + "data": { + "instance_id": "6a92945cc0c4689234aef656", + "product_key": "6a9568047dd4b23eacab9b1a", + "device_name": "dt-10000000000000000000000000000512", + "hardware_id": "echoear-esp32s3-a1b2c3d4", + "device_secret": "", + "credential_ref": "volc-device-secret://10000000-0000-0000-0000-000000000512", + "credential_version": 1, + "credential_source": "platform_dynamic_registration", + "issued_at": "2026-09-05T09:12:00Z" + } +} +``` + +### 3.4 设备恢复出厂 + +地址: + +```http +POST /device/v3/factory-reset +``` + +输入: + +| 字段 | 类型 | 必填 | 中文含义 | +|---|---|---:|---| +| Header `Idempotency-Key` | string | 是 | 本次重置幂等键,16–128 字符;重试保持不变 | +| `reset_id` | UUID | 是 | 本次重置唯一编号 | +| `reset_counter` | integer | 是 | 设备安全存储中的单调递增重置次数 | +| `occurred_at` | datetime | 是 | 设备确认重置的 UTC 时间;不得晚于请求时间 5 分钟 | +| `trigger` | enum | 是 | `physical_long_press`、`device_menu` 或 `service_tool` | +| `firmware_version` | string | 是 | 当前固件版本 | +| `pending_turn_count` | integer | 是 | 未上传聊天记录数;必须为 `0` | +| `active_conversation_id` | UUID | 否 | 设备认为仍在运行的会话编号 | + +输出: + +| 字段 | 类型 | 中文含义 | +|---|---|---| +| `reset_id` | UUID | 重置编号 | +| `device_id` | UUID | 平台设备编号 | +| `activation_state` | enum | 重置后固定为 `CLAIMABLE` | +| `reset_counter` | integer | 平台接受的重置次数 | +| `binding_removed` | boolean | 是否关闭了原用户绑定 | +| `previous_binding_id` | UUID/null | 原绑定关系编号 | +| `provider_credential_status` | enum | 火山设备凭据状态,通常为 `SUSPENDED` | +| `license_action` | enum | 固定为 `RETAINED`,重置不释放火山 License | +| `ended_conversation_count` | integer | 平台强制结束的残留会话数 | +| `cancelled_command_count` | integer | 平台取消的待执行命令数 | +| `config_revision` | integer | 重置后的设备配置版本 | +| `claim.code` | string | 新的一次性绑定码 | +| `claim.issued_at` | datetime | 绑定码签发时间 | +| `claim.expires_at` | datetime | 绑定码过期时间 | +| `local_actions` | string[] | 设备收到成功响应后必须执行的本地清理动作 | +| `reset_at` | datetime | 平台完成重置的时间 | + +平台同时把旧用户侧的设备状态置为离线,并推送 `device.binding.reset` 事件。设备收到成功响应后,按 `local_actions` 清理本地火山凭据、用户上下文、会话与网络配置;设备平台身份和 HMAC 密钥不得删除。 + +示例: + +```http +POST /device/v3/factory-reset +Idempotency-Key: factory-reset-00000512-0001 +Content-Type: application/json +<设备HMAC请求头> + +{ + "reset_id": "10000000-0000-0000-0000-000000000901", + "reset_counter": 1, + "occurred_at": "2026-09-05T09:30:00Z", + "trigger": "physical_long_press", + "firmware_version": "1.1.0", + "pending_turn_count": 0 +} +``` + +```json +{ + "request_id": "req-reset-001", + "data": { + "reset_id": "10000000-0000-0000-0000-000000000901", + "device_id": "10000000-0000-0000-0000-000000000512", + "activation_state": "CLAIMABLE", + "reset_counter": 1, + "binding_removed": true, + "previous_binding_id": "10000000-0000-0000-0000-000000000611", + "provider_credential_status": "SUSPENDED", + "license_action": "RETAINED", + "ended_conversation_count": 0, + "cancelled_command_count": 0, + "config_revision": 9, + "claim": { + "code": "QRST-UVWX-YZ23-4567", + "issued_at": "2026-09-05T09:30:01Z", + "expires_at": "2026-09-06T09:30:01Z" + }, + "local_actions": [ + "clear_volc_credential", + "clear_user_context", + "clear_session_state", + "clear_network_credentials", + "reboot_to_claimable" + ], + "reset_at": "2026-09-05T09:30:01Z" + } +} +``` + +设备必须保留平台 `device_id`、平台 HMAC `secret` 和 `hardware_id`;其余用户上下文、火山凭据、会话状态和网络配置按 `local_actions` 清除。 + +### 3.5 检查固件版本 + +地址: + +```http +GET /device/v2/bootstrap +``` + +输入: + +| 字段 | 位置 | 类型 | 必填 | 中文含义 | +|---|---|---|---:|---| +| `firmware_version` | Query | string | 是 | 当前固件版本 | +| `device_time` | Query | datetime | 是 | 设备 UTC 时间,RFC3339 | +| `board_rev` | Query | string | 否 | 硬件版本 | +| `rtc_sdk_version` | Query | string | 否 | 火山 SDK 版本 | +| `codec` | Query | string | 否 | 音频格式,例如 `pcm16` | +| `sample_rate` | Query | integer | 否 | 音频采样率,当前为 `16000` | +| `network` | Query | string | 否 | 网络类型,例如 `wifi` | + +输出: + +| 字段 | 类型 | 中文含义 | +|---|---|---| +| `server_time` | datetime | 平台 UTC 时间 | +| `clock_skew_ms` | integer | 平台时间减设备时间的毫秒差 | +| `device.device_id` | UUID | 平台设备编号 | +| `device.status` | string | 设备业务状态 | +| `device.model` | string | 设备型号 | +| `device.hardware_revision` | string/null | 硬件版本 | +| `device.minimum_security_version` | string/null | 最低安全版本 | +| `device.minimum_firmware_version` | string/null | 最低固件版本 | +| `device.firmware_compatible` | boolean | 当前固件是否允许继续聊天 | +| `device.config_revision` | integer | 当前平台配置版本 | +| `device.binding.bound` | boolean | 是否已绑定 | +| `device.binding.household_id` | UUID/null | 家庭编号 | +| `device.binding.subject_id` | UUID/null | 服务对象编号 | +| `device.binding.subject_name` | string/null | 服务对象名称 | + +示例: + +```http +GET /device/v2/bootstrap?firmware_version=1.0.0&board_rev=v1.0&rtc_sdk_version=1.1.0&codec=pcm16&sample_rate=16000&network=wifi&device_time=2026-09-05T09%3A15%3A00Z +<设备HMAC请求头> +``` + +```json +{ + "request_id": "req-bootstrap-001", + "data": { + "server_time": "2026-09-05T09:15:01Z", + "clock_skew_ms": 1000, + "device": { + "device_id": "10000000-0000-0000-0000-000000000512", + "status": "registered", + "model": "echoear-esp32s3", + "minimum_firmware_version": "1.0.0", + "firmware_compatible": true, + "config_revision": 8, + "binding": { + "bound": true, + "household_id": "10000000-0000-0000-0000-000000000211", + "subject_id": "10000000-0000-0000-0000-000000000411", + "subject_name": "测试用户" + } + } + } +} +``` + +### 3.6 获取设备配置 + +地址: + +```http +GET /device/v2/config +``` + +输入: + +| 字段 | 位置 | 类型 | 必填 | 中文含义 | +|---|---|---|---:|---| +| `If-None-Match` | Header | string | 否 | 本地配置 ETag;未变化时返回 `304` | + +输出: + +| 字段 | 类型 | 中文含义 | +|---|---|---| +| Response Header `ETag` | string | 当前配置 ETag | +| `device_id` | UUID | 平台设备编号 | +| `revision` | integer | 配置版本 | +| `desired_config` | object | 平台期望设备应用的配置 | +| `desired_config.voice_runtime` | enum | 语音运行时 | +| `desired_config.hardware_agent_transport` | enum | 火山传输方式 | +| `desired_config.minimum_sdk_version` | string | 最低 SDK 版本 | +| `reported_revision` | integer/null | 设备此前报告的配置版本 | +| `binding` | object | 当前绑定信息,字段与 bootstrap 相同 | + +示例: + +```http +GET /device/v2/config +If-None-Match: "7" +<设备HMAC请求头> +``` + +```json +{ + "request_id": "req-config-001", + "data": { + "device_id": "10000000-0000-0000-0000-000000000512", + "revision": 8, + "desired_config": { + "voice_runtime": "volc_hardware_agent", + "hardware_agent_transport": "low_load", + "minimum_sdk_version": "1.1.0" + }, + "reported_revision": 7, + "binding": { + "bound": true, + "household_id": "10000000-0000-0000-0000-000000000211", + "subject_id": "10000000-0000-0000-0000-000000000411" + } + } +} +``` + +### 3.7 创建会话并获取系统提示词 + +地址: + +```http +POST /device/v3/agent-sessions +``` + +输入: + +| 字段 | 类型 | 必填 | 中文含义 | +|---|---|---:|---| +| Header `Idempotency-Key` | string | 是 | 创建会话幂等键,16–128 字符 | +| `device_time` | datetime | 是 | 设备 UTC 时间 | +| `monotonic_ms` | integer | 否 | 开机后的单调时钟毫秒数 | +| `capabilities.hardware_agent` | boolean | 是 | 必须为 `true` | +| `capabilities.sdk_version` | string | 是 | 硬件智能体 SDK 版本 | +| `capabilities.transports` | string[] | 是 | 支持的传输方式 | +| `capabilities.input_audio_format` | string | 是 | 上行音频能力,当前为 `pcm_s16le_16000_mono` | +| `capabilities.output_audio_format` | string | 是 | 下行音频能力,当前为 `pcm_s16le_16000_mono` | +| `capabilities.server_event_callback` | boolean | 是 | 当前传 `false` | +| `capabilities.transcript_upload` | boolean | 是 | 必须为 `true` | +| `capabilities.dynamic_session_config` | boolean | 是 | 必须为 `true` | +| `capabilities.credential_rotation` | boolean | 是 | 必须为 `true` | +| `capabilities.near_pcm_recording` | boolean | 是 | 是否支持近端录音 | +| `capabilities.far_pcm_recording` | boolean | 是 | 是否支持远端录音 | +| `network.type` | string | 是 | 网络类型 | +| `network.rssi` | integer | 否 | Wi-Fi RSSI | +| `requested_goal` | string | 是 | 固定为 `open_conversation` | +| `config_revision` | integer | 是 | `/device/v2/config` 返回的配置版本 | + +输出: + +| 字段 | 类型 | 中文含义 | +|---|---|---| +| `conversation_id` | UUID | 平台会话编号 | +| `status` | string | 正常为 `SESSION_READY` | +| `runtime_type` | string | `volc_hardware_agent` | +| `runtime.provider` | string | 服务商,当前为 `volcengine` | +| `runtime.bot_id` | string | 本次使用的 Bot ID | +| `runtime.transport` | string | 本次传输方式 | +| `runtime.provider_session_ref` | string | 火山会话引用 | +| `runtime.session_nonce` | string | 会话证明随机值 | +| `runtime.config_digest` | string | 本次动态配置摘要 | +| `runtime.wait_for_session_update` | boolean | 是否必须先应用动态人设 | +| `session_config.dialog_id` | string | 本次对话标识 | +| `session_config.system_prompt` | string | 系统提示词/核心人设 | +| `session_config.persona_tone` | string | 人设语气和表达风格 | +| `session_config.authorized_context` | string | 授权的用户背景、近期摘要和长期记忆 | +| `session_config.prompt_bundle_version` | string | 提示词版本 | +| `session_config.tool_url` | URL/null | 工具调用地址 | +| `session_config.tool_scope_token` | string/null | 本次会话工具令牌 | +| `privacy.retention_mode` | string | 数据保留模式 | +| `privacy.record_near` | boolean | 是否允许近端录音 | +| `privacy.record_far` | boolean | 是否允许远端录音 | +| `privacy.upload_allowed` | boolean | 是否允许上传聊天记录 | +| `session.engine_mode` | string | 会话引擎模式 | +| `session.heartbeat_interval_sec` | integer | 会话状态上报间隔 | +| `session.allocation_expires_at` | datetime | 会话分配过期时间 | + +示例: + +```http +POST /device/v3/agent-sessions +Idempotency-Key: session-device-00000512-0001 +Content-Type: application/json +<设备HMAC请求头> + +{ + "device_time": "2026-09-05T09:20:00Z", + "monotonic_ms": 812345, + "capabilities": { + "hardware_agent": true, + "sdk_version": "1.1.0-echoear", + "transports": ["low_load"], + "input_audio_format": "pcm_s16le_16000_mono", + "output_audio_format": "pcm_s16le_16000_mono", + "server_event_callback": false, + "transcript_upload": true, + "dynamic_session_config": true, + "credential_rotation": true, + "near_pcm_recording": false, + "far_pcm_recording": false + }, + "network": {"type":"wifi","rssi":-52}, + "requested_goal": "open_conversation", + "config_revision": 8 +} +``` + +```json +{ + "request_id": "req-session-001", + "data": { + "conversation_id": "10000000-0000-0000-0000-000000000801", + "status": "SESSION_READY", + "runtime_type": "volc_hardware_agent", + "runtime": { + "provider": "volcengine", + "bot_id": "botM4gWQ9A9y", + "transport": "low_load", + "provider_session_ref": "provider-session-ref", + "session_nonce": "", + "config_digest": "", + "wait_for_session_update": true + }, + "session_config": { + "dialog_id": "dialog-00000801", + "system_prompt": "你是面向长者的陪伴助手……", + "persona_tone": "率真、自然", + "authorized_context": "经授权的背景和记忆……", + "prompt_bundle_version": "persona-v12" + }, + "privacy": { + "retention_mode": "transcript_only", + "record_near": false, + "record_far": false, + "upload_allowed": true + }, + "session": { + "engine_mode": "hardware_agent", + "heartbeat_interval_sec": 30, + "allocation_expires_at": "2026-09-05T09:25:00Z" + } + } +} +``` + +### 3.8 连接火山并注入人设 + +地址: + +```text +wss://ai-gateway.vei.volces.com/v1/realtime?bot={bot_id}&wait_for_session_update=true +``` + +输入: + +| 字段 | 位置 | 中文含义 | 来源/计算 | +|---|---|---|---| +| `bot` | Query | Bot ID | 创建会话返回的 `runtime.bot_id` | +| `X-Auth-Type` | Header | 鉴权类型 | 固定为 `1` | +| `X-Product-Key` | Header | 火山产品标识 | provision 返回的 `product_key` | +| `X-Device-Name` | Header | 火山设备名称 | provision 返回的 `device_name` | +| `X-Random-Num` | Header | 随机数 | 每次连接重新生成 | +| `X-Timestamp` | Header | Unix 秒 | 当前时间 | +| `X-Instance-Id` | Header | 火山实例编号 | provision 返回的 `instance_id` | +| `X-Hardware-Id` | Header | 物理硬件标识 | provision 使用的 `hardware_id` | +| `X-Signature` | Header | 火山鉴权签名 | `base64(HMAC-SHA256(device_secret, canonical))` | +| `session.update` | WebSocket JSON | 动态会话配置 | 注入平台返回的人设和记忆 | + +输出: + +| 事件 | 中文含义 | +|---|---| +| `session.created` / `session.updated` | 火山会话创建/动态人设生效 | +| `conversation.item.input_audio_transcription.completed` | 用户最终识别文本 | +| `response.audio_transcript.done` | 智能体最终回复文本 | +| 音频数据 | 智能体返回的 PCM16 音频,按 16 kHz 单声道播放 | +| error 事件 | 火山连接或会话失败 | + +示例: + +```text +canonical = "auth_type=1&device_name={device_name}&random_num={random_num}&product_key={product_key}×tamp={timestamp}&instance_id={instance_id}" +X-Signature = base64(HMAC-SHA256(device_secret, canonical)) +``` + +```json +{ + "event_id": "evt-session-update-001", + "type": "session.update", + "session": { + "object": "realtime.session", + "model": "", + "input_audio_format": "pcm16", + "output_audio_format": "pcm16", + "config": { + "ASRConfig": {"TurnDetectionMode": 0}, + "LLMConfig": { + "SystemMessages": [ + "\n表达风格:\n授权上下文:\n会话标识:" + ] + } + }, + "agent_config": {"EnableConversationStateCallback": false} + } +} +``` + +### 3.9 上报会话事件 + +地址: + +```http +POST /device/v3/agent-sessions/{conversation_id}/events +``` + +输入: + +| 字段 | 类型 | 必填 | 中文含义 | +|---|---|---:|---| +| Path `conversation_id` | UUID | 是 | 平台会话编号 | +| `event_id` | string | 是 | 事件唯一编号 | +| `sequence_no` | integer | 是 | 事件顺序号,从 1 开始递增 | +| `event_type` | enum | 是 | `session_started`、`session_updated`、`session_ended` 或 `session_failed` | +| `occurred_at` | datetime | 是 | 事件发生时间 | +| `provider_session_ref` | string | 是 | 火山会话引用 | +| `session_nonce` | string | 是 | 会话证明随机值 | +| `provider_connection_ref` | string | 启动时必填 | WebSocket 连接标识 | +| `bot_id` | string | 启动时必填 | 实际连接的 Bot ID | +| `transport` | string | 启动时必填 | 实际传输方式 | +| `config_digest` | string | 启动时必填 | 已应用的人设配置摘要 | +| `dynamic_config_applied` | boolean | 启动时必填 | 必须为 `true` | +| `error_code` | string | 失败时填写 | 失败错误码 | + +输出: + +| 字段 | 类型 | 中文含义 | +|---|---|---| +| `conversation_id` | UUID | 平台会话编号 | +| `status` | string | 会话状态,开始后为 `ACTIVE`,正常结束后为 `COMPLETED` | +| `action` | enum | `continue` 或 `stop_session` | +| `duplicate` | boolean | 是否为重复事件 | +| `accepted_at` | datetime | 平台接收时间 | + +示例: + +开始请求: + +```json +{ + "event_id": "device-event-start-001", + "sequence_no": 1, + "event_type": "session_started", + "occurred_at": "2026-09-05T09:20:03Z", + "provider_session_ref": "provider-session-ref", + "session_nonce": "", + "provider_connection_ref": "ws-connection-001", + "bot_id": "botM4gWQ9A9y", + "transport": "low_load", + "config_digest": "", + "dynamic_config_applied": true +} +``` + +结束示例: + +```json +{ + "event_id": "device-event-end-001", + "sequence_no": 2, + "event_type": "session_ended", + "occurred_at": "2026-09-05T09:25:00Z", + "provider_session_ref": "provider-session-ref", + "session_nonce": "" +} +``` + +响应示例: + +```json +{ + "request_id": "req-event-001", + "data": { + "conversation_id": "10000000-0000-0000-0000-000000000801", + "status": "ACTIVE", + "action": "continue", + "duplicate": false, + "accepted_at": "2026-09-05T09:20:03Z" + } +} +``` + +### 3.10 上传聊天记录 + +地址: + +```http +POST /device/v3/agent-sessions/{conversation_id}/turns:batch +``` + +输入: + +| 字段 | 类型 | 必填 | 中文含义 | +|---|---|---:|---| +| Path `conversation_id` | UUID | 是 | 平台会话编号 | +| Header `Idempotency-Key` | string | 是 | 本批记录幂等键,16–128 字符 | +| `turns` | array | 是 | 最终聊天记录数组,每批 1–500 条 | +| `turns[].turn_key` | string | 是 | 会话内唯一的轮次标识,最长 255 字符 | +| `turns[].sequence_no` | integer | 是 | 对话顺序号,会话内唯一且大于 0 | +| `turns[].speaker` | enum | 是 | 用户为 `subject`,智能体为 `agent` | +| `turns[].start_at` | datetime | 是 | 本轮开始时间 | +| `turns[].end_at` | datetime | 是 | 本轮结束时间,不早于开始时间 | +| `turns[].final_text` | string | 是 | 最终文本,最长 20000 字符 | +| `turns[].interrupted` | boolean | 是 | 本轮是否被打断 | + +输出: + +| 字段 | 类型 | 中文含义 | +|---|---|---| +| `conversation_id` | UUID | 平台会话编号 | +| `accepted_count` | integer | 本次新写入的记录数 | +| `duplicate_count` | integer | 本次幂等重复的记录数 | + +示例: + +```http +POST /device/v3/agent-sessions/10000000-0000-0000-0000-000000000801/turns:batch +Idempotency-Key: turns-conversation-00000801-0001 +Content-Type: application/json +<设备HMAC请求头> + +{ + "turns": [ + { + "turn_key": "user-final-001", + "sequence_no": 1, + "speaker": "subject", + "start_at": "2026-09-05T09:20:10Z", + "end_at": "2026-09-05T09:20:12Z", + "final_text": "今天天气怎么样?", + "interrupted": false + }, + { + "turn_key": "agent-final-001", + "sequence_no": 2, + "speaker": "agent", + "start_at": "2026-09-05T09:20:12Z", + "end_at": "2026-09-05T09:20:16Z", + "final_text": "我来帮你看看。", + "interrupted": false + } + ] +} +``` + +```json +{ + "request_id": "req-turns-001", + "data": { + "conversation_id": "10000000-0000-0000-0000-000000000801", + "accepted_count": 2, + "duplicate_count": 0 + } +} +``` diff --git a/docs/diagrams/device-conversation-lifecycle.html b/docs/diagrams/device-conversation-lifecycle.html new file mode 100644 index 0000000..7175db7 --- /dev/null +++ b/docs/diagrams/device-conversation-lifecycle.html @@ -0,0 +1,14878 @@ + + + + + + + 设备从注册到聊天结束的完整流程 + + + + + + + + + + + +
    + +
    +
    +
    +

    设备从注册到聊天结束的完整流程

    +
    +
    + + + + + + + +
    + + 设备从注册到聊天结束的完整流程 + 由 Archify 生成的时序图。 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 登记设备,生成平台 Device Secret + + + + + + + + 安全注入平台身份(无 Product Secret) + + + + + + + + 消费绑定码:CLAIMABLE → BOUND + + + + + + + + 请求 hardware-agent-credentials:provision + + + + + + + + DynamicRegister:新设备消耗 1 个 License + + + + + + + + 加密保存并下发一机一密 → ACTIVE + + + + + + + + POST /device/v3/agent-sessions + + + + + + + + 返回 Bot、人设、授权上下文与 nonce + + + + + + + + 一机一密建连并注入 session.update + + + + + + + + 配置确认后上报 session_started + + + + + + + + 双向音频 / 打断 / 工具调用 + + + + + + + + session_updated + 最终字幕回流 + + + + + + + + 断开火山并上报 ended / failed → 摘要记忆 + + + + + + + 一、平台注册与绑定 + + + + 二、首次预配(仅一次) + + + + 三、每次开始聊天 + + + + 四、聊天与结束 + + + + + 运维平台 · 登记 / 放行 · 时序参与者 + + + + 运维平台 + 登记 / 放行 + + + + 用户 App · 扫码绑定 · 时序参与者 + + + + 用户 App + 扫码绑定 + + + + EchoEar · ESP32-S3 · 时序参与者 + + + + EchoEar + ESP32-S3 + + + + 自有 SaaS · 设备控制面 · 时序参与者 + + + + 自有 SaaS + 设备控制面 + + + + 火山 IoT · DynamicRegister · 时序参与者 + + + + 火山 IoT + DynamicRegister + + + + 火山智能体 · 实时对话 · 时序参与者 + + + + 火山智能体 + 实时对话 + + + + + 图例 + + + 请求 + + + + 返回 + + + + 安全 + + + + 异步追踪 + + + + 默认消息 + + + +

    + + + + + + + + + +
    + + +
    + +
    + +
    + + + + diff --git a/docs/diagrams/device-conversation-lifecycle.sequence.json b/docs/diagrams/device-conversation-lifecycle.sequence.json new file mode 100644 index 0000000..0074b3f --- /dev/null +++ b/docs/diagrams/device-conversation-lifecycle.sequence.json @@ -0,0 +1,49 @@ +{ + "schema_version": 1, + "diagram_type": "sequence", + "meta": { + "title": "设备从注册到聊天结束的完整流程", + "locale": "zh-CN", + "output": "docs/diagrams/device-conversation-lifecycle.html", + "viewBox": [1080, 590], + "quality_profile": "showcase", + "column_fit": "spread" + }, + "participants": [ + { "id": "ops", "type": "frontend", "label": "运维平台", "sublabel": "登记 / 放行" }, + { "id": "app", "type": "frontend", "label": "用户 App", "sublabel": "扫码绑定" }, + { "id": "device", "type": "external", "label": "EchoEar", "sublabel": "ESP32-S3" }, + { "id": "saas", "type": "backend", "label": "自有 SaaS", "sublabel": "设备控制面" }, + { "id": "volcIot", "type": "cloud", "label": "火山 IoT", "sublabel": "DynamicRegister" }, + { "id": "volcAgent", "type": "cloud", "label": "火山智能体", "sublabel": "实时对话" } + ], + "segments": [ + { "from": 150, "to": 226, "label": "一、平台注册与绑定" }, + { "from": 232, "to": 310, "label": "二、首次预配(仅一次)" }, + { "from": 316, "to": 424, "label": "三、每次开始聊天" }, + { "from": 430, "to": 520, "label": "四、聊天与结束" } + ], + "messages": [ + { "id": "register", "from": "ops", "to": "saas", "y": 166, "label": "登记设备,生成平台 Device Secret", "variant": "emphasis" }, + { "id": "inject", "from": "saas", "to": "device", "y": 194, "label": "安全注入平台身份(无 Product Secret)", "variant": "security" }, + { "id": "claim", "from": "app", "to": "saas", "y": 222, "label": "消费绑定码:CLAIMABLE → BOUND", "variant": "emphasis" }, + { "id": "provision", "from": "device", "to": "saas", "y": 250, "label": "请求 hardware-agent-credentials:provision", "variant": "emphasis" }, + { "id": "dynamicRegister", "from": "saas", "to": "volcIot", "y": 278, "label": "DynamicRegister:新设备消耗 1 个 License", "variant": "security" }, + { "id": "active", "from": "saas", "to": "device", "y": 306, "label": "加密保存并下发一机一密 → ACTIVE", "variant": "return" }, + { "id": "start", "from": "device", "to": "saas", "y": 334, "label": "POST /device/v3/agent-sessions", "variant": "emphasis" }, + { "id": "config", "from": "saas", "to": "device", "y": 362, "label": "返回 Bot、人设、授权上下文与 nonce", "variant": "return" }, + { "id": "connect", "from": "device", "to": "volcAgent", "y": 390, "label": "一机一密建连并注入 session.update", "variant": "security" }, + { "id": "started", "from": "device", "to": "saas", "y": 418, "label": "配置确认后上报 session_started", "variant": "emphasis" }, + { "id": "audio", "from": "device", "to": "volcAgent", "y": 446, "label": "双向音频 / 打断 / 工具调用", "variant": "default" }, + { "id": "sync", "from": "device", "to": "saas", "y": 474, "label": "session_updated + 最终字幕回流", "variant": "dashed" }, + { "id": "ended", "from": "device", "to": "saas", "y": 506, "label": "断开火山并上报 ended / failed → 摘要记忆", "variant": "emphasis" } + ], + "activations": [ + { "participant": "saas", "from": 160, "to": 228, "type": "backend" }, + { "participant": "saas", "from": 240, "to": 312, "type": "backend" }, + { "participant": "volcIot", "from": 268, "to": 286, "type": "cloud" }, + { "participant": "saas", "from": 324, "to": 424, "type": "backend" }, + { "participant": "volcAgent", "from": 380, "to": 512, "type": "cloud" }, + { "participant": "saas", "from": 468, "to": 514, "type": "backend" } + ] +} diff --git a/docs/diagrams/device-conversation-lifecycle.visual-check.1440x900.dark.png b/docs/diagrams/device-conversation-lifecycle.visual-check.1440x900.dark.png new file mode 100644 index 0000000..c8fddd8 Binary files /dev/null and b/docs/diagrams/device-conversation-lifecycle.visual-check.1440x900.dark.png differ diff --git a/docs/diagrams/device-conversation-lifecycle.visual-check.1440x900.light.png b/docs/diagrams/device-conversation-lifecycle.visual-check.1440x900.light.png new file mode 100644 index 0000000..f94b604 Binary files /dev/null and b/docs/diagrams/device-conversation-lifecycle.visual-check.1440x900.light.png differ diff --git a/docs/diagrams/device-conversation-lifecycle.visual-check.2048x1320.dark.png b/docs/diagrams/device-conversation-lifecycle.visual-check.2048x1320.dark.png new file mode 100644 index 0000000..3428c4f Binary files /dev/null and b/docs/diagrams/device-conversation-lifecycle.visual-check.2048x1320.dark.png differ diff --git a/docs/diagrams/device-conversation-lifecycle.visual-check.2048x1320.light.png b/docs/diagrams/device-conversation-lifecycle.visual-check.2048x1320.light.png new file mode 100644 index 0000000..2666edf Binary files /dev/null and b/docs/diagrams/device-conversation-lifecycle.visual-check.2048x1320.light.png differ diff --git a/docs/diagrams/device-conversation-lifecycle.visual-check.html b/docs/diagrams/device-conversation-lifecycle.visual-check.html new file mode 100644 index 0000000..7cc541f --- /dev/null +++ b/docs/diagrams/device-conversation-lifecycle.visual-check.html @@ -0,0 +1,32 @@ + + + + + +Archify visual-check · device-conversation-lifecycle.html + + + +

    Archify visual-check

    device-conversation-lifecycle.html · automated containment pass · visual review pending

    +
    +
    + light 1440 by 900 +
    LIGHT · 1440×900 · containment pass
    +
    +
    + dark 1440 by 900 +
    DARK · 1440×900 · containment pass
    +
    +
    + light 2048 by 1320 +
    LIGHT · 2048×1320 · containment pass
    +
    +
    + dark 2048 by 1320 +
    DARK · 2048×1320 · containment pass
    +
    +
    + + diff --git a/docs/diagrams/device-conversation-lifecycle.visual-check.json b/docs/diagrams/device-conversation-lifecycle.visual-check.json new file mode 100644 index 0000000..03bc292 --- /dev/null +++ b/docs/diagrams/device-conversation-lifecycle.visual-check.json @@ -0,0 +1,547 @@ +{ + "schemaVersion": 1, + "ok": true, + "command": "visual-check", + "status": "pass", + "visualReview": "pending", + "artifact": { + "path": "E:\\Codex\\story\\platform\\docs\\diagrams\\device-conversation-lifecycle.html", + "sha256": "7fade308f989a6d95791c6d018dfcf4c5f50aafb8f6ce6d94c907943362eeea0", + "bytes": 715115 + }, + "state": { + "detail": "read", + "motion": "still" + }, + "chrome": { + "status": "available", + "executable": "C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe" + }, + "diagnostics": [], + "containment": { + "status": "pass", + "viewports": [ + { + "width": 1440, + "height": 900, + "theme": "light", + "innerWidth": 1440, + "innerHeight": 900, + "scrollWidth": 1440, + "scrollHeight": 900, + "overflowX": false, + "overflowY": false, + "ok": true, + "readerWidth": 1376, + "diagramWidth": 1346, + "viewBoxWidth": 1080, + "minimumProjectedNodeTextPx": 7, + "minimumProjectedNodeText": "登记 / 放行", + "minimumProjectedNodeTextDetail": "context", + "minimumRequiredNodeTextPx": 6, + "readabilityOk": true, + "hasLegend": true, + "hasNavigationDock": true, + "legendDockIntersectionArea": 0, + "dockStageIntersectionArea": 0, + "dockStageGap": 10.21875, + "requiredDockStageGap": 10, + "viewerChromeStageOk": true, + "viewerChromeReserve": 51, + "viewerChromeActive": true, + "viewerChromeOk": true, + "resolvedTheme": "light" + }, + { + "width": 1600, + "height": 1000, + "theme": "light", + "innerWidth": 1600, + "innerHeight": 1000, + "scrollWidth": 1600, + "scrollHeight": 1000, + "overflowX": false, + "overflowY": false, + "ok": true, + "readerWidth": 1456, + "diagramWidth": 1426, + "viewBoxWidth": 1080, + "minimumProjectedNodeTextPx": 7, + "minimumProjectedNodeText": "登记 / 放行", + "minimumProjectedNodeTextDetail": "context", + "minimumRequiredNodeTextPx": 6, + "readabilityOk": true, + "hasLegend": true, + "hasNavigationDock": true, + "legendDockIntersectionArea": 0, + "dockStageIntersectionArea": 0, + "dockStageGap": 10.21875, + "requiredDockStageGap": 10, + "viewerChromeStageOk": true, + "viewerChromeReserve": 51, + "viewerChromeActive": true, + "viewerChromeOk": true, + "resolvedTheme": "light" + }, + { + "width": 1920, + "height": 1080, + "theme": "light", + "innerWidth": 1920, + "innerHeight": 1080, + "scrollWidth": 1920, + "scrollHeight": 1080, + "overflowX": false, + "overflowY": false, + "ok": true, + "readerWidth": 1602, + "diagramWidth": 1572, + "viewBoxWidth": 1080, + "minimumProjectedNodeTextPx": 7, + "minimumProjectedNodeText": "登记 / 放行", + "minimumProjectedNodeTextDetail": "context", + "minimumRequiredNodeTextPx": 6, + "readabilityOk": true, + "hasLegend": true, + "hasNavigationDock": true, + "legendDockIntersectionArea": 0, + "dockStageIntersectionArea": 0, + "dockStageGap": 10.21875, + "requiredDockStageGap": 10, + "viewerChromeStageOk": true, + "viewerChromeReserve": 51, + "viewerChromeActive": true, + "viewerChromeOk": true, + "resolvedTheme": "light" + }, + { + "width": 2048, + "height": 1320, + "theme": "light", + "innerWidth": 2048, + "innerHeight": 1320, + "scrollWidth": 2048, + "scrollHeight": 1320, + "overflowX": false, + "overflowY": false, + "ok": true, + "readerWidth": 1920, + "diagramWidth": 1870, + "viewBoxWidth": 1080, + "minimumProjectedNodeTextPx": 7, + "minimumProjectedNodeText": "登记 / 放行", + "minimumProjectedNodeTextDetail": "context", + "minimumRequiredNodeTextPx": 6, + "readabilityOk": true, + "hasLegend": true, + "hasNavigationDock": true, + "legendDockIntersectionArea": 0, + "dockStageIntersectionArea": 0, + "dockStageGap": 10.21875, + "requiredDockStageGap": 10, + "viewerChromeStageOk": true, + "viewerChromeReserve": 41, + "viewerChromeActive": true, + "viewerChromeOk": true, + "resolvedTheme": "light" + } + ] + }, + "readability": { + "status": "pass", + "minimumProjectedNodeTextPx": 6, + "viewports": [ + { + "width": 1440, + "height": 900, + "theme": "light", + "innerWidth": 1440, + "innerHeight": 900, + "scrollWidth": 1440, + "scrollHeight": 900, + "overflowX": false, + "overflowY": false, + "ok": true, + "readerWidth": 1376, + "diagramWidth": 1346, + "viewBoxWidth": 1080, + "minimumProjectedNodeTextPx": 7, + "minimumProjectedNodeText": "登记 / 放行", + "minimumProjectedNodeTextDetail": "context", + "minimumRequiredNodeTextPx": 6, + "readabilityOk": true, + "hasLegend": true, + "hasNavigationDock": true, + "legendDockIntersectionArea": 0, + "dockStageIntersectionArea": 0, + "dockStageGap": 10.21875, + "requiredDockStageGap": 10, + "viewerChromeStageOk": true, + "viewerChromeReserve": 51, + "viewerChromeActive": true, + "viewerChromeOk": true, + "resolvedTheme": "light" + }, + { + "width": 1600, + "height": 1000, + "theme": "light", + "innerWidth": 1600, + "innerHeight": 1000, + "scrollWidth": 1600, + "scrollHeight": 1000, + "overflowX": false, + "overflowY": false, + "ok": true, + "readerWidth": 1456, + "diagramWidth": 1426, + "viewBoxWidth": 1080, + "minimumProjectedNodeTextPx": 7, + "minimumProjectedNodeText": "登记 / 放行", + "minimumProjectedNodeTextDetail": "context", + "minimumRequiredNodeTextPx": 6, + "readabilityOk": true, + "hasLegend": true, + "hasNavigationDock": true, + "legendDockIntersectionArea": 0, + "dockStageIntersectionArea": 0, + "dockStageGap": 10.21875, + "requiredDockStageGap": 10, + "viewerChromeStageOk": true, + "viewerChromeReserve": 51, + "viewerChromeActive": true, + "viewerChromeOk": true, + "resolvedTheme": "light" + }, + { + "width": 1920, + "height": 1080, + "theme": "light", + "innerWidth": 1920, + "innerHeight": 1080, + "scrollWidth": 1920, + "scrollHeight": 1080, + "overflowX": false, + "overflowY": false, + "ok": true, + "readerWidth": 1602, + "diagramWidth": 1572, + "viewBoxWidth": 1080, + "minimumProjectedNodeTextPx": 7, + "minimumProjectedNodeText": "登记 / 放行", + "minimumProjectedNodeTextDetail": "context", + "minimumRequiredNodeTextPx": 6, + "readabilityOk": true, + "hasLegend": true, + "hasNavigationDock": true, + "legendDockIntersectionArea": 0, + "dockStageIntersectionArea": 0, + "dockStageGap": 10.21875, + "requiredDockStageGap": 10, + "viewerChromeStageOk": true, + "viewerChromeReserve": 51, + "viewerChromeActive": true, + "viewerChromeOk": true, + "resolvedTheme": "light" + }, + { + "width": 2048, + "height": 1320, + "theme": "light", + "innerWidth": 2048, + "innerHeight": 1320, + "scrollWidth": 2048, + "scrollHeight": 1320, + "overflowX": false, + "overflowY": false, + "ok": true, + "readerWidth": 1920, + "diagramWidth": 1870, + "viewBoxWidth": 1080, + "minimumProjectedNodeTextPx": 7, + "minimumProjectedNodeText": "登记 / 放行", + "minimumProjectedNodeTextDetail": "context", + "minimumRequiredNodeTextPx": 6, + "readabilityOk": true, + "hasLegend": true, + "hasNavigationDock": true, + "legendDockIntersectionArea": 0, + "dockStageIntersectionArea": 0, + "dockStageGap": 10.21875, + "requiredDockStageGap": 10, + "viewerChromeStageOk": true, + "viewerChromeReserve": 41, + "viewerChromeActive": true, + "viewerChromeOk": true, + "resolvedTheme": "light" + } + ] + }, + "viewerChrome": { + "status": "pass", + "viewports": [ + { + "width": 1440, + "height": 900, + "theme": "light", + "innerWidth": 1440, + "innerHeight": 900, + "scrollWidth": 1440, + "scrollHeight": 900, + "overflowX": false, + "overflowY": false, + "ok": true, + "readerWidth": 1376, + "diagramWidth": 1346, + "viewBoxWidth": 1080, + "minimumProjectedNodeTextPx": 7, + "minimumProjectedNodeText": "登记 / 放行", + "minimumProjectedNodeTextDetail": "context", + "minimumRequiredNodeTextPx": 6, + "readabilityOk": true, + "hasLegend": true, + "hasNavigationDock": true, + "legendDockIntersectionArea": 0, + "dockStageIntersectionArea": 0, + "dockStageGap": 10.21875, + "requiredDockStageGap": 10, + "viewerChromeStageOk": true, + "viewerChromeReserve": 51, + "viewerChromeActive": true, + "viewerChromeOk": true, + "resolvedTheme": "light" + }, + { + "width": 1600, + "height": 1000, + "theme": "light", + "innerWidth": 1600, + "innerHeight": 1000, + "scrollWidth": 1600, + "scrollHeight": 1000, + "overflowX": false, + "overflowY": false, + "ok": true, + "readerWidth": 1456, + "diagramWidth": 1426, + "viewBoxWidth": 1080, + "minimumProjectedNodeTextPx": 7, + "minimumProjectedNodeText": "登记 / 放行", + "minimumProjectedNodeTextDetail": "context", + "minimumRequiredNodeTextPx": 6, + "readabilityOk": true, + "hasLegend": true, + "hasNavigationDock": true, + "legendDockIntersectionArea": 0, + "dockStageIntersectionArea": 0, + "dockStageGap": 10.21875, + "requiredDockStageGap": 10, + "viewerChromeStageOk": true, + "viewerChromeReserve": 51, + "viewerChromeActive": true, + "viewerChromeOk": true, + "resolvedTheme": "light" + }, + { + "width": 1920, + "height": 1080, + "theme": "light", + "innerWidth": 1920, + "innerHeight": 1080, + "scrollWidth": 1920, + "scrollHeight": 1080, + "overflowX": false, + "overflowY": false, + "ok": true, + "readerWidth": 1602, + "diagramWidth": 1572, + "viewBoxWidth": 1080, + "minimumProjectedNodeTextPx": 7, + "minimumProjectedNodeText": "登记 / 放行", + "minimumProjectedNodeTextDetail": "context", + "minimumRequiredNodeTextPx": 6, + "readabilityOk": true, + "hasLegend": true, + "hasNavigationDock": true, + "legendDockIntersectionArea": 0, + "dockStageIntersectionArea": 0, + "dockStageGap": 10.21875, + "requiredDockStageGap": 10, + "viewerChromeStageOk": true, + "viewerChromeReserve": 51, + "viewerChromeActive": true, + "viewerChromeOk": true, + "resolvedTheme": "light" + }, + { + "width": 2048, + "height": 1320, + "theme": "light", + "innerWidth": 2048, + "innerHeight": 1320, + "scrollWidth": 2048, + "scrollHeight": 1320, + "overflowX": false, + "overflowY": false, + "ok": true, + "readerWidth": 1920, + "diagramWidth": 1870, + "viewBoxWidth": 1080, + "minimumProjectedNodeTextPx": 7, + "minimumProjectedNodeText": "登记 / 放行", + "minimumProjectedNodeTextDetail": "context", + "minimumRequiredNodeTextPx": 6, + "readabilityOk": true, + "hasLegend": true, + "hasNavigationDock": true, + "legendDockIntersectionArea": 0, + "dockStageIntersectionArea": 0, + "dockStageGap": 10.21875, + "requiredDockStageGap": 10, + "viewerChromeStageOk": true, + "viewerChromeReserve": 41, + "viewerChromeActive": true, + "viewerChromeOk": true, + "resolvedTheme": "light" + } + ] + }, + "captures": { + "status": "pass", + "screenshots": [ + { + "width": 1440, + "height": 900, + "theme": "light", + "innerWidth": 1440, + "innerHeight": 900, + "scrollWidth": 1440, + "scrollHeight": 900, + "overflowX": false, + "overflowY": false, + "ok": true, + "readerWidth": 1376, + "diagramWidth": 1346, + "viewBoxWidth": 1080, + "minimumProjectedNodeTextPx": 7, + "minimumProjectedNodeText": "登记 / 放行", + "minimumProjectedNodeTextDetail": "context", + "minimumRequiredNodeTextPx": 6, + "readabilityOk": true, + "hasLegend": true, + "hasNavigationDock": true, + "legendDockIntersectionArea": 0, + "dockStageIntersectionArea": 0, + "dockStageGap": 10.21875, + "requiredDockStageGap": 10, + "viewerChromeStageOk": true, + "viewerChromeReserve": 51, + "viewerChromeActive": true, + "viewerChromeOk": true, + "resolvedTheme": "light", + "file": "device-conversation-lifecycle.visual-check.1440x900.light.png" + }, + { + "width": 1440, + "height": 900, + "theme": "dark", + "innerWidth": 1440, + "innerHeight": 900, + "scrollWidth": 1440, + "scrollHeight": 900, + "overflowX": false, + "overflowY": false, + "ok": true, + "readerWidth": 1376, + "diagramWidth": 1346, + "viewBoxWidth": 1080, + "minimumProjectedNodeTextPx": 7, + "minimumProjectedNodeText": "登记 / 放行", + "minimumProjectedNodeTextDetail": "context", + "minimumRequiredNodeTextPx": 6, + "readabilityOk": true, + "hasLegend": true, + "hasNavigationDock": true, + "legendDockIntersectionArea": 0, + "dockStageIntersectionArea": 0, + "dockStageGap": 10.21875, + "requiredDockStageGap": 10, + "viewerChromeStageOk": true, + "viewerChromeReserve": 51, + "viewerChromeActive": true, + "viewerChromeOk": true, + "resolvedTheme": "dark", + "file": "device-conversation-lifecycle.visual-check.1440x900.dark.png" + }, + { + "width": 2048, + "height": 1320, + "theme": "light", + "innerWidth": 2048, + "innerHeight": 1320, + "scrollWidth": 2048, + "scrollHeight": 1320, + "overflowX": false, + "overflowY": false, + "ok": true, + "readerWidth": 1920, + "diagramWidth": 1870, + "viewBoxWidth": 1080, + "minimumProjectedNodeTextPx": 7, + "minimumProjectedNodeText": "登记 / 放行", + "minimumProjectedNodeTextDetail": "context", + "minimumRequiredNodeTextPx": 6, + "readabilityOk": true, + "hasLegend": true, + "hasNavigationDock": true, + "legendDockIntersectionArea": 0, + "dockStageIntersectionArea": 0, + "dockStageGap": 10.21875, + "requiredDockStageGap": 10, + "viewerChromeStageOk": true, + "viewerChromeReserve": 41, + "viewerChromeActive": true, + "viewerChromeOk": true, + "resolvedTheme": "light", + "file": "device-conversation-lifecycle.visual-check.2048x1320.light.png" + }, + { + "width": 2048, + "height": 1320, + "theme": "dark", + "innerWidth": 2048, + "innerHeight": 1320, + "scrollWidth": 2048, + "scrollHeight": 1320, + "overflowX": false, + "overflowY": false, + "ok": true, + "readerWidth": 1920, + "diagramWidth": 1870, + "viewBoxWidth": 1080, + "minimumProjectedNodeTextPx": 7, + "minimumProjectedNodeText": "登记 / 放行", + "minimumProjectedNodeTextDetail": "context", + "minimumRequiredNodeTextPx": 6, + "readabilityOk": true, + "hasLegend": true, + "hasNavigationDock": true, + "legendDockIntersectionArea": 0, + "dockStageIntersectionArea": 0, + "dockStageGap": 10.21875, + "requiredDockStageGap": 10, + "viewerChromeStageOk": true, + "viewerChromeReserve": 41, + "viewerChromeActive": true, + "viewerChromeOk": true, + "resolvedTheme": "dark", + "file": "device-conversation-lifecycle.visual-check.2048x1320.dark.png" + } + ], + "contactSheet": "device-conversation-lifecycle.visual-check.html" + }, + "sidecars": { + "receipt": "device-conversation-lifecycle.visual-check.json", + "contactSheet": "device-conversation-lifecycle.visual-check.html" + } +} diff --git a/docs/echoear-reboot-memory-investigation.md b/docs/echoear-reboot-memory-investigation.md new file mode 100644 index 0000000..4b94156 --- /dev/null +++ b/docs/echoear-reboot-memory-investigation.md @@ -0,0 +1,29 @@ +# EchoEar reboot history investigation — 2026-09-06 + +User reports loss of prior conversation context after power cycling and asks how the platform receives the conversation record. + +## Existing path verified in source + +- Device receives final subject transcription and agent audio transcript events, queues them outside the WebSocket receive callback, and POSTs to `/device/v3/agent-sessions/{conversation_id}/turns:batch`. +- Backend authenticates the bound device, checks transcript retention and consent, deduplicates final turns, and writes `conversation_turn`. The web app exposes these through Chat records → Conversation detail; API: `GET /api/v1/conversations/{id}/turns`. +- This path uploads text, not near/far audio recordings. Earlier device logs contain successful HTTP POSTs during exchanges but did not distinguish accepted transcript counts. New firmware logs accepted/duplicate counts without logging transcript content. +- Each new connection creates a fresh provider session. Authorized platform context is supplied through `session_config.authorized_context`, which firmware includes in `LLMConfig.SystemMessages`. Record storage and cross-session context are separate steps. + +## Defects and local changes + +1. Device omitted periodic session renewal. The server starts a lease on `session_started`, and `session_updated` renews it. Firmware now reads the server heartbeat interval, renews outside the audio callback using the existing reusable HTTP connection, and honors `stop_session`. +2. Recent summary selection excluded all `FAILED` conversations. It now accepts ready summaries from completed or failed conversations while preserving tenant/subject checks and existing consent gates. No raw transcript bypass or automatic long-term memory creation was added. +3. Hardware normal completion requested extraction generation 1; the extraction processor ignores a generation below the highest stored turn sequence. Hardware completion, reported failure, and server stop-timeout recovery now schedule the latest stored sequence with a deduplicated terminal extraction key. Existing extraction consent and content safety checks remain. + +## Verification and pending work + +**Device update 2026-09-06 10:20:** COM6 reconnected. Final heartbeat firmware `0x2ccf40` flashed with hash verification and booted to connected standby. The real user test session `157f866d-c364-4ae8-b169-50ca07024e5a` completed normally with 3 turns (including the preceding diagnostic sentence) and 2 READY summaries. Periodic heartbeats succeeded. Native Volcano callbacks were received for input and output, but contain only device identity and do not honor the attempted per-session callback override in this low_load test. callbacks memory3 validates these as device_probe without writing transcripts; device upload remains canonical. User has been asked to power-cycle and ask which joke was just told; actual recall outcome is pending. See the newest section of `volc-native-subtitle-callback.md`. + +**Update 2026-09-06:** Supersedes the deployment/test limitations below. The user-supplied Desktop PEM successfully authenticated root. Backend `0.78.3-rc.subtitle-memory1` is now deployed; independent PostgreSQL regression tests actually passed (not skipped), including failed-session memory eligibility and terminal extraction generation. Production has stored transcripts and READY summaries (one failed session has 50 turns). All three backend readiness checks pass. Native callback protocol smoke checks also pass; real provider callbacks and device reboot recall remain unverified because COM6 is absent. The latest firmware build is `0x2ccf40`, still **not flashed**. See `volc-native-subtitle-callback.md` for deployment commands and outstanding checks. Earlier entries below are retained as investigation history. + +- Firmware build passed: `firmware/echoear-platform-agent/build/voice-history-heartbeat-build.log`; binary size `0x2ccd50`. +- `go test ./internal/orchestration ./internal/httpapi ./internal/extraction` passed the available tests. New database regression cases cover failed/completed/active summary eligibility, other-subject exclusion, and extraction generation/deduplication. They were **skipped**, not validated against a database: `TEST_DATABASE_URL` is unset and Docker daemon is unavailable. +- **Firmware is not flashed.** COM6 disappeared after the user's power cycle. COM3 is a different USB device; COM4/5 are Bluetooth ports. Do not flash them. +- **Backend changes are not deployed.** Existing deployment key was rejected for `root@121.41.46.238`. Requested the actual SSH username; never request passwords/private key content. +- Awaiting USB reconnection and server access. Next verify actual transcript row counts, summary READY status, subject consent state, extraction job status, new context inclusion, and a real power-cycle recall test. Do not claim that online records or memory restoration have been verified. +- Last verified flashed app remains HTTP-reuse version `0x2cc8a0`; user confirmed continuous speech. Preserve its playback/display improvements and NVS during the next app-only flash. diff --git a/docs/firmware-rollout.md b/docs/firmware-rollout.md index b721850..7b62789 100644 --- a/docs/firmware-rollout.md +++ b/docs/firmware-rollout.md @@ -138,3 +138,26 @@ powershell -NoProfile -ExecutionPolicy Bypass -File .\scripts\smoke-firmware-rol A/B启动确认批次在OpenAPI 0.62.0下扩展可选回执阶段,不增加旧设备必填字段或主状态枚举。启用设备先将制品写入非活动分区并持久化启动尝试,再依次上报`staging`、`reboot_pending`和`boot_verifying`;健康通过才提交成功,健康失败或超时则恢复旧分区并提交`FAILED/rolled_back`及回退版本。云端将三类中间阶段保持为`INSTALLING`,同时保留完整`last_result`供页面展示。低强度验证只执行定向Go用例、前端类型/生产构建和一次B分区启动失败自动回A分区的纵向冒烟,不把模拟结果作为真实Bootloader验收。详细协议见[设备A/B分区固件升级与启动确认协议](device-firmware-ab-protocol.md)。 OpenAPI 0.63.0和V38进一步为每台固件分配保存去重的阶段事件。直接命令ACK与批量遥测补交在更新命令/分配的同一事务内写入事件,重复结果摘要通过SHA-256唯一键保持幂等;批次详情按设备返回完整时间线,页面显示云端接收时间、阶段、命令状态和稳定结果码。`cmd/firmware-conformance`可把该时间线与设备侧Bootloader/分区证据进行离线一致性校验,详细格式和15项检查见[固件真机联调一致性证据采集与校验说明](firmware-conformance-evidence.md)。 + +## 9. 维护端租户级固件管理 + +V46和OpenAPI 0.74.0在保留家庭端固件历史及设备下载协议的前提下,引入维护端租户级固件制品与OTA批次。维护端创建的`firmware_release.household_id`和`firmware_rollout.household_id`为空,`created_source=OPS`;家庭端已有记录继续使用原家庭范围,二者共享租户、型号、硬件版本和业务版本唯一性,避免同一租户发布相互冲突的活动制品。 + +维护中心“固件管理”页面支持选择客户组织、登记或吊销可信公钥、浏览器计算SHA-256后直传私有对象、恢复中断上传、完成签名校验、暂停、恢复、退役和人工下载。人工下载与设备下载都只签发短期私有地址;人工下载额外记录`ops.firmware.release.download`审计事件,设备下载继续要求设备身份、目标批次、版本状态和分配状态全部通过。 + +维护端接口使用`X-Tenant-Id`固定客户组织边界: + +| 方法与路径 | 用途 | +|---|---| +| `GET/POST /api/v1/ops/firmware-signing-keys` | 查询或登记租户可信公钥 | +| `POST /api/v1/ops/firmware-signing-keys/revoke` | 在引用保护通过后吊销公钥 | +| `GET/POST /api/v1/ops/firmware-releases` | 查询租户固件台账或创建租户级上传记录 | +| `POST /api/v1/ops/firmware-releases/{id}/upload-session` | 恢复未完成的私有上传 | +| `POST /api/v1/ops/firmware-releases/{id}/finalize` | 校验对象和签名并发布版本 | +| `POST /api/v1/ops/firmware-releases/{id}/pause` | 暂停版本分发和关联在途批次 | +| `POST /api/v1/ops/firmware-releases/{id}/resume` | 恢复版本分发,不自动恢复旧批次 | +| `POST /api/v1/ops/firmware-releases/{id}/retire` | 永久退役版本 | +| `GET /api/v1/ops/firmware-releases/{id}/download` | 获取带摘要、签名和过期时间的短期人工下载地址 | +| `GET/POST /api/v1/ops/firmware-rollouts` | 查询OTA批次或按兼容设备和灰度比例创建升级批次 | +| `GET /api/v1/ops/firmware-rollouts/{id}` | 查询逐设备下载、安装和确认进度 | +| `POST /api/v1/ops/firmware-rollouts/{id}/pause` | 暂停批次并取消尚未投递的升级命令 | diff --git a/docs/load-alerting-and-production-security.md b/docs/load-alerting-and-production-security.md index 201fef6..98b8f94 100644 --- a/docs/load-alerting-and-production-security.md +++ b/docs/load-alerting-and-production-security.md @@ -84,6 +84,10 @@ docker run --rm -v "${PWD}:/src" -w /src golang:1.26.6-alpine ` npm --prefix web audit --audit-level=high docker run --rm -v "${PWD}:/src" zricethezav/gitleaks:v8.30.1 ` detect --source=/src --config=/src/.gitleaks.toml --no-banner +docker run --rm -v "${PWD}:/src" ` + -v data-terminal-trivy-cache:/root/.cache/trivy aquasec/trivy:0.74.0 ` + config --ignorefile /src/.trivyignore.yaml --exit-code 1 ` + --severity HIGH,CRITICAL /src docker run --rm -v /var/run/docker.sock:/var/run/docker.sock ` -v data-terminal-trivy-cache:/root/.cache/trivy aquasec/trivy:0.74.0 ` image --scanners vuln --ignore-unfixed --severity HIGH,CRITICAL ` diff --git a/docs/viking-memory-integration.md b/docs/viking-memory-integration.md new file mode 100644 index 0000000..ecf3591 --- /dev/null +++ b/docs/viking-memory-integration.md @@ -0,0 +1,34 @@ +# Viking managed memory integration + +Configured collection: `memory`, resource `mem-3a1174a1`, project `default`, Beijing endpoint. The existing collection has emotional-companionship extraction rules `event_v1` and `profile_v1` (assistant isolation enabled). This is the managed Viking memory service, not OpenViking. + +## Runtime + +API and worker accept `VIKING_MEMORY_RESOURCE_ID`, `VIKING_MEMORY_API_KEY_FILE`, `VIKING_MEMORY_EVENT_TYPE` and `VIKING_MEMORY_PROFILE_TYPE`. An empty resource ID disables this integration. The API key is a server-only file; it is never returned to a device or written into a firmware image. The endpoint is HTTPS; redirects are disabled. Error logs contain status/code, never vendor response bodies or transcripts. + +Terminal conversation extraction requests also enqueue an independent `conversation.viking_sync_requested` outbox event. The worker submits final user and assistant turns with AddSession. Frozen transcript/memory consent and current subject/memory/transcript eligibility are checked. No user-only or diagnostic assistant-only conversation is submitted. The database remains the source of full transcripts. + +User identity is SHA-256 of the tenant and subject UUIDs, prefixed `u_`; it survives device restarts and changes of device binding. Assistant identity is the stable platform companion ID, currently shared across platform persona versions. Every conversation has a distinct `c__` session identifier. Batches contain up to 100 messages. A retry uses the same identifier; a late transcript revision replaces that conversation's batch. Audit digests avoid rewriting unchanged successful submissions. Initial indexing/extraction is asynchronous. + +At hardware session creation the API retrieves the current user profile and recent events before opening the allocation transaction, with a three-second overall budget. It revalidates the subject binding and memory consent before incorporating up to 600 characters into the frozen context. Extracted memories are explicitly labeled as fallible background, not instructions or manually confirmed facts. The existing in-call memory-search endpoint also searches Viking, then falls back to local curated memories. Actual use of that tool by the hardware cloud runtime still depends on provider tool support; startup context does not depend on it. Lookup failure leaves normal conversation available. + +There are no firmware/audio changes and no database migrations. Native subtitle callbacks remain in device-probe mode; the reliable device transcript upload supplies these sessions. Pending memory extraction is independent of streamed audio playback. + +## Lifecycle and limits + +Current consent withdrawal immediately gates future Viking reads/writes. Subject privacy erasure calls scoped event/profile deletion before completing the provider-index deletion subtask. Vendor extraction is asynchronous; late vendor extraction after an erasure request needs follow-up reconciliation before treating provider erasure as fully verified. Individual locally curated-memory deletion does not delete independently extracted Viking events/profiles. For the present integration, use the Viking console for editing or deleting those derived entries; a unified platform memory-management UI is not implemented. Do not describe the existing local memory list as a complete Viking list. + +Automatic extraction begins on conversation completion/failure and final transcript delivery. A sudden power-off relies on the existing heartbeat recovery to close the conversation. Startup recalls recent events plus profile; recall of older topic-specific events during the same session depends on the in-call search tool. Full historical backfill is explicit; enabling the integration does not automatically export all past conversations. + +## Verification + +Deployment on 2026-09-06: API and worker `0.78.4-rc.viking1`; callbacks remain `0.78.3-rc.subtitle-memory3`. All three readiness endpoints returned 200. Current symlink points to `/opt/data-terminal-cloud/releases/0.78.4-rc.viking1`; rollback uses the previous `0.78.3-rc.subtitle-memory1` directory including its `compose.subtitle.yaml` override. The same three compose files remain required. + +One real conversation (`157f866d-c364-4ae8-b169-50ca07024e5a`, three final messages) was explicitly queued for backfill. Viking produced two event memories, and the production service retrieved 76 characters of startup memory. Synthetic event/profile entries were deleted and subsequent queries returned zero. Device power-cycle/spoken recall is pending user verification; server retrieval alone is not proof of successful audible recall. + +- Real API-key calls verified against the configured collection: AddSession, event search, profile search. +- Synthetic test used a separate user namespace; no test preference is attached to the real user. +- Tests cover vendor result scope filtering, error redaction, stable identity/session IDs, scoped deletion, SQL outbox deduplication, late transcript replacement, frozen consent, cross-tenant rejection, and subject-bound startup context. +- A dedicated PostgreSQL/Redis environment is used for integration tests, separate from production data. + +Official API references: https://www.volcengine.com/docs/84313/1783353 , https://www.volcengine.com/docs/84313/2100967 , https://www.volcengine.com/docs/84313/2100966 , https://www.volcengine.com/docs/84313/1783358 , https://www.volcengine.com/docs/84313/1783359 . diff --git a/docs/volc-hardware-agent-migration.architecture.json b/docs/volc-hardware-agent-migration.architecture.json new file mode 100644 index 0000000..53d0fb7 --- /dev/null +++ b/docs/volc-hardware-agent-migration.architecture.json @@ -0,0 +1,230 @@ +{ + "schema_version": 1, + "diagram_type": "architecture", + "meta": { + "title": "火山硬件对话智能体迁移后目标架构", + "locale": "zh-CN", + "quality_profile": "showcase", + "viewBox": [1360, 820] + }, + "components": [ + { + "id": "elder", + "type": "external", + "label": "长者用户", + "sublabel": "说话、打断、挂机", + "pos": [40, 230], + "size": [130, 72] + }, + { + "id": "device", + "type": "frontend", + "label": "陪伴硬件", + "sublabel": "麦克风、扬声器、本地队列", + "pos": [230, 230], + "size": [180, 72] + }, + { + "id": "hardwareSdk", + "type": "frontend", + "label": "火山硬件智能体 SDK", + "sublabel": "高质量 UDP/RTC 主链", + "pos": [490, 230], + "size": [200, 72] + }, + { + "id": "volcAgent", + "type": "external", + "label": "火山硬件对话智能体", + "sublabel": "Bot、ASR/LLM/TTS、会话", + "pos": [770, 230], + "size": [220, 72] + }, + { + "id": "deviceRegistry", + "type": "external", + "label": "火山产品与设备管理", + "sublabel": "Product、Device、License、Bot 绑定", + "pos": [770, 50], + "size": [220, 72] + }, + { + "id": "platformApi", + "type": "backend", + "label": "平台会话控制面", + "sublabel": "授权、冻结快照、心跳、停止", + "pos": [490, 470], + "size": [200, 76] + }, + { + "id": "toolGateway", + "type": "security", + "label": "工具与记忆网关", + "sublabel": "MCP/Function Calling、短期会话令牌", + "pos": [1070, 230], + "size": [220, 76] + }, + { + "id": "dataStore", + "type": "database", + "label": "业务主数据", + "sublabel": "PostgreSQL、pgvector、Redis", + "pos": [770, 470], + "size": [220, 76] + }, + { + "id": "asyncPipeline", + "type": "backend", + "label": "异步内容与运营流水线", + "sublabel": "字幕、摘要、故事、费用、审计", + "pos": [1070, 470], + "size": [220, 76] + }, + { + "id": "objectStorage", + "type": "database", + "label": "私有对象存储", + "sublabel": "可选双轨录音与导出", + "pos": [230, 650], + "size": [180, 72] + }, + { + "id": "webApps", + "type": "frontend", + "label": "家庭端与运营端", + "sublabel": "设备、通话、隐私、配置、诊断", + "pos": [490, 650], + "size": [200, 72] + } + ], + "boundaries": [ + { + "kind": "region", + "label": "设备侧", + "wraps": ["device", "hardwareSdk"], + "pad": 22 + }, + { + "kind": "region", + "label": "火山引擎托管域", + "wraps": ["volcAgent", "deviceRegistry"], + "pad": 22 + }, + { + "kind": "security-group", + "label": "自有平台可信域(业务事实唯一主库)", + "wraps": ["platformApi", "toolGateway", "dataStore", "asyncPipeline", "objectStorage", "webApps"], + "pad": 24 + } + ], + "connections": [ + { + "id": "humanVoice", + "from": "elder", + "to": "device", + "label": "双向语音", + "variant": "emphasis" + }, + { + "id": "audioFrames", + "from": "device", + "to": "hardwareSdk", + "label": "PCM/设备事件", + "variant": "emphasis" + }, + { + "id": "agentLink", + "from": "hardwareSdk", + "to": "volcAgent", + "label": "SDK 直连会话", + "variant": "emphasis", + "labelAt": [730, 316] + }, + { + "id": "provisioning", + "from": "deviceRegistry", + "to": "hardwareSdk", + "label": "设备凭证与 Bot 绑定", + "variant": "security" + }, + { + "id": "prepareSession", + "from": "device", + "to": "platformApi", + "label": "HTTPS 会话控制、字幕与状态", + "variant": "security" + }, + { + "id": "toolCalls", + "from": "volcAgent", + "to": "toolGateway", + "label": "MCP / FC", + "variant": "security" + }, + { + "id": "toolData", + "from": "toolGateway", + "to": "dataStore", + "label": "租户与长者范围查询", + "variant": "security" + }, + { + "id": "conversationFacts", + "from": "platformApi", + "to": "dataStore", + "label": "会话/授权快照", + "variant": "emphasis" + }, + { + "id": "asyncJobs", + "from": "dataStore", + "to": "asyncPipeline", + "label": "Outbox 可靠任务", + "variant": "dashed", + "labelAt": [1030, 560] + }, + { + "id": "mediaUpload", + "from": "device", + "to": "objectStorage", + "label": "可选加密录音直传", + "variant": "dashed", + "fromSide": "bottom", + "toSide": "top", + "labelAt": [320, 316] + }, + { + "id": "webControl", + "from": "webApps", + "to": "platformApi", + "label": "HTTPS/OIDC", + "variant": "security" + } + ], + "cards": [ + { + "dot": "emerald", + "title": "保留不动", + "items": [ + "平台继续掌握设备、授权、通话、记忆、费用和审计主数据", + "字幕后的摘要、故事、人工确认记忆和隐私删除链路继续复用" + ] + }, + { + "dot": "amber", + "title": "替换范围", + "items": [ + "设备不再领取平台 RTC Token,也不再等待云端 StartVoiceChat", + "火山硬件 SDK 直接建立会话,旧 RTC 房间与 Agent Task 仅保留兼容期" + ] + }, + { + "dot": "rose", + "title": "上线硬门槛", + "items": [ + "必须验证服务端强制停话、设备凭证吊销、字幕/用量回传和近远端录音能力", + "任何一项无法闭环时,先限定为试点能力,不替换现网主链" + ] + } + ] +} diff --git a/docs/volc-native-subtitle-callback.md b/docs/volc-native-subtitle-callback.md new file mode 100644 index 0000000..ded6256 --- /dev/null +++ b/docs/volc-native-subtitle-callback.md @@ -0,0 +1,61 @@ +# 火山字幕回调与重启记忆修复(2026-09-06) + +## 设备实测更新(北京时间 10:20) + +- **已烧录并启动**:COM6,ESP32-S3 MAC b4:3a:45:16:34:a8。最终固件 app `0x2ccf40`,日志 `build/native-subtitle-stable-{build,flash,device}.log`,烧录哈希校验成功。NVS/Wi-Fi/绑定均保留;设备已联网待机。 +- 实际用户测试会话 `157f866d-c364-4ae8-b169-50ca07024e5a`:设备保存 3 条文字(其中 1 条为前置播放测试),双方实际对话保存成功,状态 COMPLETED,2 条 READY 摘要。设备约每 15 秒续期成功。之后创建的新会话已携带非空历史上下文;实际断电后口头回忆仍需用户反馈。 +- **真实火山回调已收到,但不能据此宣称按会话入库完成。** 当前 low_load 回调只有 message/signature;解码后包含 productKey、deviceName、type、data,说话者 userId 为 input/output,没有 job_id、session_id 或 conversation_id。 +- 平台确实下发了带 conversation_id 的 URL 和 43 字符会话签名。固件 session.update 使用官方 AgentConfig.ServerMessageURLForRTS/ServerMessageSignatureForRTS 字段,但实测仍回调控制台静态 URL 和静态签名。额外测试 ServerMessageUrl/ServerMessageSignature 别名也未改变结果,该实验已从最终固件移除。 +- 不允许通过“设备最新会话”归属回调,否则旧会话延迟字幕可能写入重启后的新会话。**保持 VOLC_SUBTITLE_PRIMARY=false。设备 turns:batch 仍为唯一正式记录来源。** +- callbacks 服务已更新为 `0.78.3-rc.subtitle-memory3`(api/worker 仍为 memory1),静态回调校验签名、设备身份及格式后返回 200,仅记无正文的验证日志,不存第二份文字。支持 Base64 JSON 和 subv 二进制封装。错误签名返回 401。公网验证前后 conversation_turn 行数不变,服务 ready。 +- 两个控制台配置值不变。其静态配置已能将字幕发送到平台,**尚未完成独立的、可靠的会话归档**。独立入库前必须解决会话身份传递,并按实测 input/output 更新和验证说话者映射;不能直接开启 primary。 +- 下一步供应商核对事项:低负载 session.update 是否支持动态回调 URL/签名?支持时需要的准确字段/生效时机是什么?不支持时能否在字幕中返回与 session.created.job_id 相同的编号?必要时评估平台管理每设备独立智能体及启动前冻结配置,但尚未创建、修改或批量克隆任何火山智能体。 + +以下为上一轮部署时的设计和验证记录;与本节冲突的“未烧录/未实测”描述已由本节更新取代。 + +## 已上线 + +- 后端:`0.78.3-rc.subtitle-memory1`,服务器 `/opt/data-terminal-cloud/current` 指向同名 release。 +- `POST /callbacks/v1/volcengine/subtitles` 支持没有 Content-Type 的火山原生请求:外层 message/signature,message 解码后为 `subv` + 四字节大端长度 + subtitle JSON。 +- 当前 `VOLC_SUBTITLE_PRIMARY=false`:回调仅验证,不存第二份文字;设备原有 turns:batch 仍是正式记录来源。 +- 有效摘要现在包含已失败的会话,仍限定租户、用户、授权及 READY 状态。硬件结束、失败、超时按实际最大 turn sequence 安排摘要提取。 +- 无数据库迁移;未发布工作区其他改动。上一个 release 可用于回退。 + +## 火山控制台填写 + +- ServerMessageUrl:`https://121.41.46.238/callbacks/v1/volcengine/subtitles` +- ServerMessageSignature:使用本机被 Git 忽略的 `.deploy-artifacts/volc-subtitle-console-settings.txt` 中同名值。此文档不复制密钥。 + +这两项是基础配置,不能单靠基础 URL 推断归属会话。新固件上报 `native_subtitle_callback=true` 后,平台每次创建会话返回 `session_config.subtitle_callback_url`(带 conversation_id)和 `subtitle_callback_signature`(绑定该会话的 HMAC 值)。固件在 session.update 的 agent_config 中设置 `ServerMessageURLForRTS` 和 `ServerMessageSignatureForRTS`,同时设置 config.SubtitleConfig.SubtitleMode=1。火山按普通自定义 signature 原样回传它,平台进行比较。 + +已冻结的会话各自保留记录来源和说话者映射,不能用“设备当前最新会话”归档延迟回调。完成的分句全部保留,不能只保留 paragraph=true;稳定键按 userId、roundId、sequence 去重。没有供应商时间戳的回调只记录接收时间,不声称等于实际说话时间。网络乱序的分句按入库顺序分配平台序号。 + +## 尚未完成的设备验证 + +- COM6 尚未出现,COM3 的 VID:PID 为 1D6B:011E,是另一台设备;COM4/5 为蓝牙。**没有烧录新固件**。 +- 最新编译:`firmware/echoear-platform-agent/build/native-subtitle-build.log`,app 大小 `0x2ccf40`;保留此前连续播放和眼睛动画修复,新增心跳续期及字幕配置。 +- 不能把平台发出的合成 HTTP 检查当成真实火山回调。当前 low_load 模式是否发送原生服务端字幕还需用设备验证;服务端状态回调仍关闭。 +- 先烧录 app 分区,保留 NVS/Wi-Fi/绑定。确认真实用户和助手字幕均能到达,并核对实际 userId 与冻结映射。未知或有歧义的说话者禁止入库。未验证前不得启用 primary。 +- primary 只作用于新会话;固件收到 subtitle_source=provider 后停止设备文字上传,平台也拒绝该会话的设备补传,防止双份记录。切换后还需验证重连、断电回忆、分句完整性及播放连续性。 + +## 验证结果 + +- 单元测试:协议截断/错误长度、缺字段、错误签名、跨会话签名、无 Content-Type、说话者歧义等通过。 +- 独立临时 PostgreSQL(不是生产数据库):完成/失败/活跃会话的摘要选择、隔离其他用户、末尾提取版本与去重、字幕 probe 不入库、primary 入库与重复处理、未知说话者、冲突、撤回授权测试通过。日志 `.deploy-artifacts/subtitle-db-test.log`。 +- 公网 HTTPS(正常证书校验)协议检查:错误签名 401,正确签名 probe 200;原会话记录数测试前后均为 5,无测试聊天写入。 +- API、callbacks、worker 的 `/health/ready` 均 ready,线上版本接口返回新版本。 + +## 运维命令 + +当前上线额外使用 `deploy/server/compose.subtitle.yaml` 固定三个后端服务镜像和回调环境变量。后续重启这版必须保留该文件: + +```sh +cd /opt/data-terminal-cloud/current +docker compose -p data-terminal-cloud --env-file deploy/server/.env \ + -f compose.yaml -f deploy/server/compose.override.yaml \ + -f deploy/server/compose.subtitle.yaml up -d --no-deps api callbacks worker +``` + +首次启动曾因 Windows 编译文件无 Linux 执行位被拒,已自动回退并修正为打包前 chmod 0755,随后成功上线。部署源码在本机 `.deploy-artifacts/subtitle-release`,包含明确文件清单;保留服务器基线的其他源码。 + +官方格式依据:[实时字幕(对话记录)](https://docs.volcengine.com/docs/6348/1337284),查阅于 2026-09-06。 diff --git a/docs/web-event-stream.md b/docs/web-event-stream.md index 1eb3e60..366460d 100644 --- a/docs/web-event-stream.md +++ b/docs/web-event-stream.md @@ -11,9 +11,12 @@ | 事件类型 | 产生条件 | 资源类型 | 状态示例 | |---|---|---|---| | `device.presence.changed` | 首次有效心跳使设备上线,或后台发现心跳超时使设备离线 | `device` | `online`、`offline` | +| `device.binding.reset` | 设备主动恢复出厂并解除当前用户绑定 | `device` | `CLAIMABLE` | | `conversation.started` | 通话进入`ACTIVE` | `conversation` | `ACTIVE` | | `conversation.completed` | 通话进入`COMPLETED` | `conversation` | `COMPLETED` | | `conversation.failed` | 通话进入`FAILED` | `conversation` | `FAILED` | + +上述通话事件由`conversation.status`统一触发,与底层运行时无关;`legacy_voicechat`和`volc_hardware_agent`都会产生同一套家庭侧SSE语义。硬件智能体的周期`session_updated`只作为平台租约和诊断证据,不逐次生成SSE事件。 | `story.created` | 新故事候选建立 | `story` | 故事当前状态 | | `story.revised` | 故事版本增加 | `story` | 故事当前状态 | | `media.upload.completed` | 媒体上传批次完成 | `upload_batch` | `COMPLETED` | @@ -105,7 +108,8 @@ sequenceDiagram 3. 设备此前不存在在线记录或已离线时,写入一次`online`事件;连续心跳只延长最后出现时间。 4. Redis继续承担低延迟在线查询,PostgreSQL状态表承担状态转换和事件可靠性。 5. Worker每个心跳周期扫描一次;最后出现时间超过两倍会话租约的记录切换为`offline`并只写入一次事件。 -6. 新的有效心跳可再次把离线记录切回在线并产生新的上线事件。 +6. 设备恢复出厂时,平台在同一事务内把旧用户侧在线状态改为`offline`、发送`device.binding.reset`并解除绑定。 +7. 新的有效心跳可再次把离线记录切回在线并产生新的上线事件;恢复出厂后的设备要先完成重新绑定。 ## 7. 浏览器重连与去重 diff --git a/docs/火山硬件对话智能体迁移架构.html b/docs/火山硬件对话智能体迁移架构.html new file mode 100644 index 0000000..c9373e2 --- /dev/null +++ b/docs/火山硬件对话智能体迁移架构.html @@ -0,0 +1,14900 @@ + + + + + + + 火山硬件对话智能体迁移后目标架构 + + + + + + + + + + + +
    + +
    +
    +
    +

    火山硬件对话智能体迁移后目标架构

    +
    +
    + + + + + + + +
    + + 火山硬件对话智能体迁移后目标架构 + 由 Archify 生成的架构图。 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 长者用户 · 说话、打断、挂机 · 架构组件 + + + + 长者用户 + 说话、打断、挂机 + + + + 陪伴硬件 · 麦克风、扬声器、本地队列 · 设备侧 + + + + 陪伴硬件 + 麦克风、扬声器、本地队列 + + + + 火山硬件智能体 SDK · 高质量 UDP/RTC 主链 · 设备侧 + + + + 火山硬件智能体 SDK + 高质量 UDP/RTC 主链 + + + + 火山硬件对话智能体 · Bot、ASR/LLM/TTS、会话 · 火山引擎托管域 + + + + 火山硬件对话智能体 + Bot、ASR/LLM/TTS、会话 + + + + 火山产品与设备管理 · Product、Device、License、Bot 绑定 · 火山引擎托管域 + + + + 火山产品与设备管理 + Product、Device、License、Bot 绑定 + + + + 平台会话控制面 · 授权、冻结快照、心跳、停止 · 自有平台可信域(业务事实唯一主库) + + + + 平台会话控制面 + 授权、冻结快照、心跳、停止 + + + + 工具与记忆网关 · MCP/Function Calling、短期会话令牌 · 自有平台可信域(业务事实唯一主库) + + + + 工具与记忆网关 + MCP/Function Calling、短期会话令牌 + + + + 业务主数据 · PostgreSQL、pgvector、Redis · 自有平台可信域(业务事实唯一主库) + + + + 业务主数据 + PostgreSQL、pgvector、Redis + + + + 异步内容与运营流水线 · 字幕、摘要、故事、费用、审计 · 自有平台可信域(业务事实唯一主库) + + + + 异步内容与运营流水线 + 字幕、摘要、故事、费用、审计 + + + + 私有对象存储 · 可选双轨录音与导出 · 自有平台可信域(业务事实唯一主库) + + + + 私有对象存储 + 可选双轨录音与导出 + + + + 家庭端与运营端 · 设备、通话、隐私、配置、诊断 · 自有平台可信域(业务事实唯一主库) + + + + 家庭端与运营端 + 设备、通话、隐私、配置、诊断 + + + + + + 双向语音 + + + + PCM/设备事件 + + + + SDK 直连会话 + + + + 设备凭证与 Bot 绑定 + + + + HTTPS 会话控制、字幕与状态 + + + + MCP / FC + + + + 租户与长者范围查询 + + + + 会话/授权快照 + + + + Outbox 可靠任务 + + + + 可选加密录音直传 + + + + HTTPS/OIDC + + + + + + 设备侧 + + + + + 火山引擎托管域 + + + + + 自有平台可信域(业务事实唯一主库) + + + + + 图例 + + + 前端 + + + + 后端 + + + + 数据库 + + + + 安全 + + + + 外部系统 + + + +

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

    保留不动

    +
    +
      +
    • • 平台继续掌握设备、授权、通话、记忆、费用和审计主数据
    • +
    • • 字幕后的摘要、故事、人工确认记忆和隐私删除链路继续复用
    • +
    +
    + +
    +
    +
    +

    替换范围

    +
    +
      +
    • • 设备不再领取平台 RTC Token,也不再等待云端 StartVoiceChat
    • +
    • • 火山硬件 SDK 直接建立会话,旧 RTC 房间与 Agent Task 仅保留兼容期
    • +
    +
    + +
    +
    +
    +

    上线硬门槛

    +
    +
      +
    • • 必须验证服务端强制停话、设备凭证吊销、字幕/用量回传和近远端录音能力
    • +
    • • 任何一项无法闭环时,先限定为试点能力,不替换现网主链
    • +
    +
    +
    + +
    + + + + diff --git a/docs/火山硬件对话智能体迁移架构.visual-check.1440x900.dark.png b/docs/火山硬件对话智能体迁移架构.visual-check.1440x900.dark.png new file mode 100644 index 0000000..5595770 Binary files /dev/null and b/docs/火山硬件对话智能体迁移架构.visual-check.1440x900.dark.png differ diff --git a/docs/火山硬件对话智能体迁移架构.visual-check.1440x900.light.png b/docs/火山硬件对话智能体迁移架构.visual-check.1440x900.light.png new file mode 100644 index 0000000..14f6d48 Binary files /dev/null and b/docs/火山硬件对话智能体迁移架构.visual-check.1440x900.light.png differ diff --git a/docs/火山硬件对话智能体迁移架构.visual-check.2048x1320.dark.png b/docs/火山硬件对话智能体迁移架构.visual-check.2048x1320.dark.png new file mode 100644 index 0000000..a51e4df Binary files /dev/null and b/docs/火山硬件对话智能体迁移架构.visual-check.2048x1320.dark.png differ diff --git a/docs/火山硬件对话智能体迁移架构.visual-check.2048x1320.light.png b/docs/火山硬件对话智能体迁移架构.visual-check.2048x1320.light.png new file mode 100644 index 0000000..a2d61c5 Binary files /dev/null and b/docs/火山硬件对话智能体迁移架构.visual-check.2048x1320.light.png differ diff --git a/docs/火山硬件对话智能体迁移架构.visual-check.html b/docs/火山硬件对话智能体迁移架构.visual-check.html new file mode 100644 index 0000000..8a94820 --- /dev/null +++ b/docs/火山硬件对话智能体迁移架构.visual-check.html @@ -0,0 +1,32 @@ + + + + + +Archify visual-check · 火山硬件对话智能体迁移架构.html + + + +

    Archify visual-check

    火山硬件对话智能体迁移架构.html · automated containment pass · visual review pending

    +
    +
    + light 1440 by 900 +
    LIGHT · 1440×900 · containment pass
    +
    +
    + dark 1440 by 900 +
    DARK · 1440×900 · containment pass
    +
    +
    + light 2048 by 1320 +
    LIGHT · 2048×1320 · containment pass
    +
    +
    + dark 2048 by 1320 +
    DARK · 2048×1320 · containment pass
    +
    +
    + + diff --git a/docs/火山硬件对话智能体迁移架构.visual-check.json b/docs/火山硬件对话智能体迁移架构.visual-check.json new file mode 100644 index 0000000..817f9c3 --- /dev/null +++ b/docs/火山硬件对话智能体迁移架构.visual-check.json @@ -0,0 +1,547 @@ +{ + "schemaVersion": 1, + "ok": true, + "command": "visual-check", + "status": "pass", + "visualReview": "pending", + "artifact": { + "path": "E:\\Codex\\story\\platform\\docs\\火山硬件对话智能体迁移架构.html", + "sha256": "baefbdc9512b0614c236f59cc80d8a14b8fb9489a3a6281214d636ad9fb68d8c", + "bytes": 721387 + }, + "state": { + "detail": "read", + "motion": "still" + }, + "chrome": { + "status": "available", + "executable": "C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe" + }, + "diagnostics": [], + "containment": { + "status": "pass", + "viewports": [ + { + "width": 1440, + "height": 900, + "theme": "light", + "innerWidth": 1440, + "innerHeight": 900, + "scrollWidth": 1440, + "scrollHeight": 900, + "overflowX": false, + "overflowY": false, + "ok": true, + "readerWidth": 1042, + "diagramWidth": 1012, + "viewBoxWidth": 1360, + "minimumProjectedNodeTextPx": 6.697058823529412, + "minimumProjectedNodeText": "说话、打断、挂机", + "minimumProjectedNodeTextDetail": "context", + "minimumRequiredNodeTextPx": 6, + "readabilityOk": true, + "hasLegend": true, + "hasNavigationDock": true, + "legendDockIntersectionArea": 0, + "dockStageIntersectionArea": 0, + "dockStageGap": 10.21875, + "requiredDockStageGap": 10, + "viewerChromeStageOk": true, + "viewerChromeReserve": 51, + "viewerChromeActive": true, + "viewerChromeOk": true, + "resolvedTheme": "light" + }, + { + "width": 1600, + "height": 1000, + "theme": "light", + "innerWidth": 1600, + "innerHeight": 1000, + "scrollWidth": 1600, + "scrollHeight": 1000, + "overflowX": false, + "overflowY": false, + "ok": true, + "readerWidth": 1082, + "diagramWidth": 1052, + "viewBoxWidth": 1360, + "minimumProjectedNodeTextPx": 6.961764705882353, + "minimumProjectedNodeText": "说话、打断、挂机", + "minimumProjectedNodeTextDetail": "context", + "minimumRequiredNodeTextPx": 6, + "readabilityOk": true, + "hasLegend": true, + "hasNavigationDock": true, + "legendDockIntersectionArea": 0, + "dockStageIntersectionArea": 0, + "dockStageGap": 10.21875, + "requiredDockStageGap": 10, + "viewerChromeStageOk": true, + "viewerChromeReserve": 51, + "viewerChromeActive": true, + "viewerChromeOk": true, + "resolvedTheme": "light" + }, + { + "width": 1920, + "height": 1080, + "theme": "light", + "innerWidth": 1920, + "innerHeight": 1080, + "scrollWidth": 1920, + "scrollHeight": 1080, + "overflowX": false, + "overflowY": false, + "ok": true, + "readerWidth": 1216, + "diagramWidth": 1186, + "viewBoxWidth": 1360, + "minimumProjectedNodeTextPx": 7.848529411764706, + "minimumProjectedNodeText": "说话、打断、挂机", + "minimumProjectedNodeTextDetail": "context", + "minimumRequiredNodeTextPx": 6, + "readabilityOk": true, + "hasLegend": true, + "hasNavigationDock": true, + "legendDockIntersectionArea": 0, + "dockStageIntersectionArea": 0, + "dockStageGap": 10.21875, + "requiredDockStageGap": 10, + "viewerChromeStageOk": true, + "viewerChromeReserve": 51, + "viewerChromeActive": true, + "viewerChromeOk": true, + "resolvedTheme": "light" + }, + { + "width": 2048, + "height": 1320, + "theme": "light", + "innerWidth": 2048, + "innerHeight": 1320, + "scrollWidth": 2048, + "scrollHeight": 1320, + "overflowX": false, + "overflowY": false, + "ok": true, + "readerWidth": 1619, + "diagramWidth": 1569, + "viewBoxWidth": 1360, + "minimumProjectedNodeTextPx": 9, + "minimumProjectedNodeText": "说话、打断、挂机", + "minimumProjectedNodeTextDetail": "context", + "minimumRequiredNodeTextPx": 6, + "readabilityOk": true, + "hasLegend": true, + "hasNavigationDock": true, + "legendDockIntersectionArea": 0, + "dockStageIntersectionArea": 0, + "dockStageGap": 10.21875, + "requiredDockStageGap": 10, + "viewerChromeStageOk": true, + "viewerChromeReserve": 41, + "viewerChromeActive": true, + "viewerChromeOk": true, + "resolvedTheme": "light" + } + ] + }, + "readability": { + "status": "pass", + "minimumProjectedNodeTextPx": 6, + "viewports": [ + { + "width": 1440, + "height": 900, + "theme": "light", + "innerWidth": 1440, + "innerHeight": 900, + "scrollWidth": 1440, + "scrollHeight": 900, + "overflowX": false, + "overflowY": false, + "ok": true, + "readerWidth": 1042, + "diagramWidth": 1012, + "viewBoxWidth": 1360, + "minimumProjectedNodeTextPx": 6.697058823529412, + "minimumProjectedNodeText": "说话、打断、挂机", + "minimumProjectedNodeTextDetail": "context", + "minimumRequiredNodeTextPx": 6, + "readabilityOk": true, + "hasLegend": true, + "hasNavigationDock": true, + "legendDockIntersectionArea": 0, + "dockStageIntersectionArea": 0, + "dockStageGap": 10.21875, + "requiredDockStageGap": 10, + "viewerChromeStageOk": true, + "viewerChromeReserve": 51, + "viewerChromeActive": true, + "viewerChromeOk": true, + "resolvedTheme": "light" + }, + { + "width": 1600, + "height": 1000, + "theme": "light", + "innerWidth": 1600, + "innerHeight": 1000, + "scrollWidth": 1600, + "scrollHeight": 1000, + "overflowX": false, + "overflowY": false, + "ok": true, + "readerWidth": 1082, + "diagramWidth": 1052, + "viewBoxWidth": 1360, + "minimumProjectedNodeTextPx": 6.961764705882353, + "minimumProjectedNodeText": "说话、打断、挂机", + "minimumProjectedNodeTextDetail": "context", + "minimumRequiredNodeTextPx": 6, + "readabilityOk": true, + "hasLegend": true, + "hasNavigationDock": true, + "legendDockIntersectionArea": 0, + "dockStageIntersectionArea": 0, + "dockStageGap": 10.21875, + "requiredDockStageGap": 10, + "viewerChromeStageOk": true, + "viewerChromeReserve": 51, + "viewerChromeActive": true, + "viewerChromeOk": true, + "resolvedTheme": "light" + }, + { + "width": 1920, + "height": 1080, + "theme": "light", + "innerWidth": 1920, + "innerHeight": 1080, + "scrollWidth": 1920, + "scrollHeight": 1080, + "overflowX": false, + "overflowY": false, + "ok": true, + "readerWidth": 1216, + "diagramWidth": 1186, + "viewBoxWidth": 1360, + "minimumProjectedNodeTextPx": 7.848529411764706, + "minimumProjectedNodeText": "说话、打断、挂机", + "minimumProjectedNodeTextDetail": "context", + "minimumRequiredNodeTextPx": 6, + "readabilityOk": true, + "hasLegend": true, + "hasNavigationDock": true, + "legendDockIntersectionArea": 0, + "dockStageIntersectionArea": 0, + "dockStageGap": 10.21875, + "requiredDockStageGap": 10, + "viewerChromeStageOk": true, + "viewerChromeReserve": 51, + "viewerChromeActive": true, + "viewerChromeOk": true, + "resolvedTheme": "light" + }, + { + "width": 2048, + "height": 1320, + "theme": "light", + "innerWidth": 2048, + "innerHeight": 1320, + "scrollWidth": 2048, + "scrollHeight": 1320, + "overflowX": false, + "overflowY": false, + "ok": true, + "readerWidth": 1619, + "diagramWidth": 1569, + "viewBoxWidth": 1360, + "minimumProjectedNodeTextPx": 9, + "minimumProjectedNodeText": "说话、打断、挂机", + "minimumProjectedNodeTextDetail": "context", + "minimumRequiredNodeTextPx": 6, + "readabilityOk": true, + "hasLegend": true, + "hasNavigationDock": true, + "legendDockIntersectionArea": 0, + "dockStageIntersectionArea": 0, + "dockStageGap": 10.21875, + "requiredDockStageGap": 10, + "viewerChromeStageOk": true, + "viewerChromeReserve": 41, + "viewerChromeActive": true, + "viewerChromeOk": true, + "resolvedTheme": "light" + } + ] + }, + "viewerChrome": { + "status": "pass", + "viewports": [ + { + "width": 1440, + "height": 900, + "theme": "light", + "innerWidth": 1440, + "innerHeight": 900, + "scrollWidth": 1440, + "scrollHeight": 900, + "overflowX": false, + "overflowY": false, + "ok": true, + "readerWidth": 1042, + "diagramWidth": 1012, + "viewBoxWidth": 1360, + "minimumProjectedNodeTextPx": 6.697058823529412, + "minimumProjectedNodeText": "说话、打断、挂机", + "minimumProjectedNodeTextDetail": "context", + "minimumRequiredNodeTextPx": 6, + "readabilityOk": true, + "hasLegend": true, + "hasNavigationDock": true, + "legendDockIntersectionArea": 0, + "dockStageIntersectionArea": 0, + "dockStageGap": 10.21875, + "requiredDockStageGap": 10, + "viewerChromeStageOk": true, + "viewerChromeReserve": 51, + "viewerChromeActive": true, + "viewerChromeOk": true, + "resolvedTheme": "light" + }, + { + "width": 1600, + "height": 1000, + "theme": "light", + "innerWidth": 1600, + "innerHeight": 1000, + "scrollWidth": 1600, + "scrollHeight": 1000, + "overflowX": false, + "overflowY": false, + "ok": true, + "readerWidth": 1082, + "diagramWidth": 1052, + "viewBoxWidth": 1360, + "minimumProjectedNodeTextPx": 6.961764705882353, + "minimumProjectedNodeText": "说话、打断、挂机", + "minimumProjectedNodeTextDetail": "context", + "minimumRequiredNodeTextPx": 6, + "readabilityOk": true, + "hasLegend": true, + "hasNavigationDock": true, + "legendDockIntersectionArea": 0, + "dockStageIntersectionArea": 0, + "dockStageGap": 10.21875, + "requiredDockStageGap": 10, + "viewerChromeStageOk": true, + "viewerChromeReserve": 51, + "viewerChromeActive": true, + "viewerChromeOk": true, + "resolvedTheme": "light" + }, + { + "width": 1920, + "height": 1080, + "theme": "light", + "innerWidth": 1920, + "innerHeight": 1080, + "scrollWidth": 1920, + "scrollHeight": 1080, + "overflowX": false, + "overflowY": false, + "ok": true, + "readerWidth": 1216, + "diagramWidth": 1186, + "viewBoxWidth": 1360, + "minimumProjectedNodeTextPx": 7.848529411764706, + "minimumProjectedNodeText": "说话、打断、挂机", + "minimumProjectedNodeTextDetail": "context", + "minimumRequiredNodeTextPx": 6, + "readabilityOk": true, + "hasLegend": true, + "hasNavigationDock": true, + "legendDockIntersectionArea": 0, + "dockStageIntersectionArea": 0, + "dockStageGap": 10.21875, + "requiredDockStageGap": 10, + "viewerChromeStageOk": true, + "viewerChromeReserve": 51, + "viewerChromeActive": true, + "viewerChromeOk": true, + "resolvedTheme": "light" + }, + { + "width": 2048, + "height": 1320, + "theme": "light", + "innerWidth": 2048, + "innerHeight": 1320, + "scrollWidth": 2048, + "scrollHeight": 1320, + "overflowX": false, + "overflowY": false, + "ok": true, + "readerWidth": 1619, + "diagramWidth": 1569, + "viewBoxWidth": 1360, + "minimumProjectedNodeTextPx": 9, + "minimumProjectedNodeText": "说话、打断、挂机", + "minimumProjectedNodeTextDetail": "context", + "minimumRequiredNodeTextPx": 6, + "readabilityOk": true, + "hasLegend": true, + "hasNavigationDock": true, + "legendDockIntersectionArea": 0, + "dockStageIntersectionArea": 0, + "dockStageGap": 10.21875, + "requiredDockStageGap": 10, + "viewerChromeStageOk": true, + "viewerChromeReserve": 41, + "viewerChromeActive": true, + "viewerChromeOk": true, + "resolvedTheme": "light" + } + ] + }, + "captures": { + "status": "pass", + "screenshots": [ + { + "width": 1440, + "height": 900, + "theme": "light", + "innerWidth": 1440, + "innerHeight": 900, + "scrollWidth": 1440, + "scrollHeight": 900, + "overflowX": false, + "overflowY": false, + "ok": true, + "readerWidth": 1042, + "diagramWidth": 1012, + "viewBoxWidth": 1360, + "minimumProjectedNodeTextPx": 6.697058823529412, + "minimumProjectedNodeText": "说话、打断、挂机", + "minimumProjectedNodeTextDetail": "context", + "minimumRequiredNodeTextPx": 6, + "readabilityOk": true, + "hasLegend": true, + "hasNavigationDock": true, + "legendDockIntersectionArea": 0, + "dockStageIntersectionArea": 0, + "dockStageGap": 10.21875, + "requiredDockStageGap": 10, + "viewerChromeStageOk": true, + "viewerChromeReserve": 51, + "viewerChromeActive": true, + "viewerChromeOk": true, + "resolvedTheme": "light", + "file": "火山硬件对话智能体迁移架构.visual-check.1440x900.light.png" + }, + { + "width": 1440, + "height": 900, + "theme": "dark", + "innerWidth": 1440, + "innerHeight": 900, + "scrollWidth": 1440, + "scrollHeight": 900, + "overflowX": false, + "overflowY": false, + "ok": true, + "readerWidth": 1042, + "diagramWidth": 1012, + "viewBoxWidth": 1360, + "minimumProjectedNodeTextPx": 6.697058823529412, + "minimumProjectedNodeText": "说话、打断、挂机", + "minimumProjectedNodeTextDetail": "context", + "minimumRequiredNodeTextPx": 6, + "readabilityOk": true, + "hasLegend": true, + "hasNavigationDock": true, + "legendDockIntersectionArea": 0, + "dockStageIntersectionArea": 0, + "dockStageGap": 10.21875, + "requiredDockStageGap": 10, + "viewerChromeStageOk": true, + "viewerChromeReserve": 51, + "viewerChromeActive": true, + "viewerChromeOk": true, + "resolvedTheme": "dark", + "file": "火山硬件对话智能体迁移架构.visual-check.1440x900.dark.png" + }, + { + "width": 2048, + "height": 1320, + "theme": "light", + "innerWidth": 2048, + "innerHeight": 1320, + "scrollWidth": 2048, + "scrollHeight": 1320, + "overflowX": false, + "overflowY": false, + "ok": true, + "readerWidth": 1619, + "diagramWidth": 1569, + "viewBoxWidth": 1360, + "minimumProjectedNodeTextPx": 9, + "minimumProjectedNodeText": "说话、打断、挂机", + "minimumProjectedNodeTextDetail": "context", + "minimumRequiredNodeTextPx": 6, + "readabilityOk": true, + "hasLegend": true, + "hasNavigationDock": true, + "legendDockIntersectionArea": 0, + "dockStageIntersectionArea": 0, + "dockStageGap": 10.21875, + "requiredDockStageGap": 10, + "viewerChromeStageOk": true, + "viewerChromeReserve": 41, + "viewerChromeActive": true, + "viewerChromeOk": true, + "resolvedTheme": "light", + "file": "火山硬件对话智能体迁移架构.visual-check.2048x1320.light.png" + }, + { + "width": 2048, + "height": 1320, + "theme": "dark", + "innerWidth": 2048, + "innerHeight": 1320, + "scrollWidth": 2048, + "scrollHeight": 1320, + "overflowX": false, + "overflowY": false, + "ok": true, + "readerWidth": 1619, + "diagramWidth": 1569, + "viewBoxWidth": 1360, + "minimumProjectedNodeTextPx": 9, + "minimumProjectedNodeText": "说话、打断、挂机", + "minimumProjectedNodeTextDetail": "context", + "minimumRequiredNodeTextPx": 6, + "readabilityOk": true, + "hasLegend": true, + "hasNavigationDock": true, + "legendDockIntersectionArea": 0, + "dockStageIntersectionArea": 0, + "dockStageGap": 10.21875, + "requiredDockStageGap": 10, + "viewerChromeStageOk": true, + "viewerChromeReserve": 41, + "viewerChromeActive": true, + "viewerChromeOk": true, + "resolvedTheme": "dark", + "file": "火山硬件对话智能体迁移架构.visual-check.2048x1320.dark.png" + } + ], + "contactSheet": "火山硬件对话智能体迁移架构.visual-check.html" + }, + "sidecars": { + "receipt": "火山硬件对话智能体迁移架构.visual-check.json", + "contactSheet": "火山硬件对话智能体迁移架构.visual-check.html" + } +} diff --git a/docs/火山硬件对话智能体迁移设计.md b/docs/火山硬件对话智能体迁移设计.md new file mode 100644 index 0000000..f5b60e8 --- /dev/null +++ b/docs/火山硬件对话智能体迁移设计.md @@ -0,0 +1,496 @@ +# 火山硬件对话智能体迁移设计 + +> 状态:方案评审稿 +> 适用基线:`v0.70.0-rc.1` +> 编制日期:2026-08-31 + +## 1. 结论 + +本次迁移建议采用“保留业务控制面,替换会话执行面”的方式,不对现有平台做推倒重建。 + +- 保留:设备身份与绑定、家庭和长者档案、授权、通话主记录、配置灰度、字幕存储、摘要/故事/事实候选、人工确认记忆、隐私导出删除、录音管理、费用额度、告警、诊断、固件和审计。 +- 替换:设备领取 RTC Token、设备加入平台分配的 RTC 房间、平台 Worker 调用 `StartVoiceChat/StopVoiceChat`、Agent RTC 用户和 RTC Task 对账。 +- 新增:火山产品/设备/Bot/License 映射、硬件智能体设备凭证生命周期、硬件会话适配器、动态会话配置、MCP/Function Calling 工具网关、硬件会话事件和用量归集。 +- 兼容:旧 RTC VoiceChat 链路和新硬件智能体链路至少并行一个灰度周期,按设备能力位和灰度配置选择,不按固件版本猜测。 + +推荐终端优先采用火山硬件智能体“高质量方案”(UDP/RTC)。现有终端本身已有 RTC、弱网恢复和网络切换验收基础,和该方案的目标更一致。火山官方同时提供低负载 WebSocket 方案;只有在量产板的 RAM、CPU、功耗或网络实测不满足高质量 SDK 时,才切换到低负载方案。官方对比说明高质量方案弱网和实时互动更好,低负载方案资源消耗更低。[火山官方 SDK 集成指引](https://www.volcengine.com/docs/6348/1913817?lang=zh) + +## 2. 当前链路与目标链路 + +### 2.1 当前链路 + +```text +设备 -> 平台创建通话 -> 平台签发 RTC Token -> 设备加入 RTC 房间 + -> 设备确认入房 -> Worker 调用 StartVoiceChat -> 豆包 Agent 入房 + -> 火山/设备回传字幕和状态 -> 平台存储与内容处理 + -> 设备或平台发起停止 -> Worker 调用 StopVoiceChat -> 通话终态 +``` + +当前关键实现: + +- `/device/v2/conversations` 创建通话并返回 RTC Grant; +- `/activate` 在设备入房后可靠投递 `conversation.agent_start_requested`; +- `voiceprovider.VolcengineClient` 把平台稳定任务契约映射为 `StartVoiceChat/StopVoiceChat`; +- RTC 房间、设备 RTC 用户、Agent RTC 用户和供应商 Task 是诊断主关联键; +- 最终字幕进入 `conversation_turn`,继续触发摘要、故事、事实和记忆流水线。 + +### 2.2 目标链路 + +```text +设备 -> 平台准备硬件会话并冻结授权/配置/背景 + -> 设备使用火山硬件智能体 SDK 直连 Bot + -> SDK 收发音频并提供字幕、状态、Function Calling 事件 + -> 设备把最终字幕和状态回传平台 + -> Bot 通过 MCP/Function Calling 访问平台受控记忆/业务工具 + -> 用户、平台或授权撤回触发停止 -> 设备关闭 SDK 会话 -> 平台收敛终态 +``` + +火山硬件智能体的标准协议使用 `product_key + device_name + device_secret` 建立设备身份,连接时指定与产品关联的 `bot`。设备首次可通过 `DynamicRegister` 获取设备独享密钥;WebSocket 标准协议支持字幕事件、端侧 Function Calling、主动播报和文本提问。[火山标准协议文档](https://www.volcengine.com/docs/6348/1904602?lang=zh) + +## 3. 架构原则 + +### 3.1 平台数据库仍是业务事实唯一主库 + +火山侧负责实时对话执行,不成为家庭、长者、授权、记忆和费用策略的主数据源。平台必须保存每次会话实际使用的: + +- 产品、设备、Bot 和 License 的脱敏引用; +- 平台路由版本、角色版本、声音版本、模型版本和提示词版本; +- 发送给火山的会话配置摘要及 SHA-256; +- 授权和背景资料冻结快照; +- 火山会话 ID、传输方案、状态时间线、字幕来源和用量来源。 + +### 3.2 设备必须先通过平台业务门禁,再连接智能体 + +设备拥有火山凭证不等于拥有业务授权。每次对话前仍必须先调用平台准备接口,由平台检查: + +- 设备已登记、未隔离、未吊销并已绑定长者; +- 固件和硬件智能体 SDK 版本满足最低要求; +- `voice_chat` 授权有效; +- 客户组织、设备并发、时长和费用额度未超限; +- 角色、模型、音色和 Bot 路由已发布; +- 背景资料和记忆快照生成成功或按既定规则无记忆降级。 + +平台拒绝准备时,设备不得调用火山 SDK 建立对话。 + +### 3.3 不把长期记忆主库直接迁到火山 + +现有记忆包含人工确认、授权撤回、版本、来源、租户/长者隔离和隐私删除证明。迁移后继续使用平台 `pgvector` 和记忆工具: + +- 会话启动时,把已授权的短背景摘要作为动态会话参数注入; +- 通话中通过平台 MCP/Function Calling 工具查询记忆; +- 工具令牌绑定 `conversation_id + provider_session_id + device_id + subject_id`,短期有效; +- 模型只能提交查询内容,不能自行指定租户或长者; +- 火山原生长期记忆只可作为可删除、可对账的派生缓存,不能成为主库。 + +火山硬件智能体产品能力包含 MCP、联网问答、长期记忆和设备管理,但这不改变本项目的业务数据权威边界。[火山硬件智能体产品页](https://www.volcengine.com/product/ConversationalAI-Embedded) + +## 4. 功能迁移矩阵 + +| 现有功能 | 迁移方式 | 新责任边界 | 结论 | +|---|---|---|---| +| 设备 HMAC 身份、绑定、家庭归属 | 原样保留;新增火山设备映射 | 平台仍负责业务身份,火山凭证只负责智能体建连 | 保留并扩展 | +| `/bootstrap`、配置、命令、遥测 | 原样保留,增加硬件智能体能力位和 SDK 信息 | 平台 | 保留 | +| RTC 房间分配与 Token | 新运行时不再使用 | 火山硬件 SDK 自行建连 | 新链路废弃 | +| `StartVoiceChat/StopVoiceChat` | 新运行时不再调用;旧链路继续兼容 | 设备 SDK / 火山硬件智能体 | 新链路废弃 | +| `s2s_pure/s2s_hybrid/cascade` | 不再作为设备会话模式;改为平台内部服务档位或 Bot/模型配置 | 平台路由 + 火山 Bot | 重定义 | +| 角色、提示词、模型、音色灰度 | 平台继续版本化;基础配置同步 Bot,会话差异通过动态参数注入 | 平台为权威,火山为执行副本 | 保留并改造 | +| 欢迎语、主动播报 | 使用 Bot/会话配置;本地系统提示可用 `input_tts` | 火山 SDK + 设备应用 | 迁移 | +| 背景资料冻结 | 准备会话时生成;等待 `session.updated` 后才开放麦克风 | 平台生成,设备注入 | 保留并改造 | +| 实时打断、VAD、ASR、TTS | 使用火山硬件智能体原生能力 | 火山 | 下沉 | +| 最终字幕 | 设备监听最终事件,写入本地加密队列,批量调用现有 `turns:batch` | 设备可靠上传,平台去重存储 | 保留并改造 | +| 字幕服务端回调 | 若火山提供则作为第二来源,按供应商事件 ID 去重 | 火山回调 + 平台 | 待商务/技术确认 | +| 摘要、开放话题、故事、事实候选 | 输入仍为平台最终话轮 | 平台 Worker | 原样保留 | +| 人工确认记忆、向量检索 | 继续使用现有数据库和流程 | 平台 | 原样保留 | +| 通话中记忆工具 | 从 VoiceChat 模板 URL 改为 MCP/Function Calling 工具网关 | 平台工具网关 | 改造 | +| 设备音量、诊断等指令 | 云端命令链保留;安全的本地动作可同时暴露为端侧 FC | 平台命令 + 设备白名单执行器 | 扩展 | +| 授权撤回停止 | 心跳返回 stop,设备关闭 SDK;必须补充服务端强制终止或凭证吊销 | 平台发令,火山/设备执行 | 关键改造 | +| 双轨录音 | 需验证 SDK 是否提供近端采集和远端解码 PCM 回调 | 设备录制 + 对象存储 | 有条件迁移 | +| 用量与费用 | 增加 License、AI 对话时长和模型用量来源;区分估算与账单 | 平台费用域 | 改造 | +| 诊断工作台 | 房间/Task 改为产品、设备、Bot、License、会话、传输和事件 | 平台 | 改造 | +| 配置灰度与回滚 | 增加 `hardware_agent` 运行时;按设备稳定分桶 | 平台 | 扩展 | +| 固件发布 | SDK、License 兼容、产品信息和安全存储纳入固件门禁 | 平台 + 设备 | 扩展 | +| 隐私导出/删除 | 平台数据链路保留;新增火山会话历史/设备数据删除与凭证吊销对账 | 平台主导 | 扩展 | +| 发布候选验收 | 替换 RTC 三模式矩阵,新增硬件 SDK 真机矩阵 | 平台验收工具 | 改造 | + +## 5. 终端改造 + +### 5.1 新增硬件智能体运行时模块 + +建议固件同时保留两个实现: + +```text +VoiceRuntime +├── LegacyRTCRuntime 现有 RTC + 平台 StartVoiceChat +└── VolcHardwareAgentRuntime 新硬件智能体 SDK +``` + +平台配置返回显式 `voice_runtime`: + +```json +{ + "voice_runtime": "volc_hardware_agent", + "runtime_revision": 12, + "transport": "high_quality", + "minimum_sdk_version": "<现场冻结版本>" +} +``` + +设备只在完整校验配置后切换运行时;切换失败回退上一有效配置。不要根据 `engine_mode` 或固件版本自动推断。 + +### 5.2 凭证与激活 + +设备侧需要保存: + +- 火山 `instance_id`、`product_key`、`device_name`; +- 动态注册换取的设备独享 `device_secret`; +- 当前凭证版本、注册时间和最后成功鉴权时间。 + +安全要求: + +- `product_secret`只允许保存在平台KMS或Secret挂载中,不能进入设备固件、应用、安装包或设备日志; +- 设备使用平台设备身份调用`/device/v3/hardware-agent-credentials:provision`,由平台完成DynamicRegister并下发本设备的`device_secret`; +- `device_secret` 使用安全芯片、TEE 或设备密钥封装后持久化; +- 日志、遥测和崩溃转储不得输出任何 Secret 或完整签名; +- 必须验证设备 Secret 的轮换、吊销和售后换板流程。 + +### 5.3 会话状态机 + +建议复用现有通话状态: + +```text +PREPARING -> SESSION_READY -> STARTING_AGENT -> ACTIVE + -> FAILED +ACTIVE -> STOPPING -> COMPLETED + -> FAILED +``` + +数据库可继续把 `SESSION_READY` 对外兼容映射为 `ROOM_ISSUED`,但新 `/device/v3` 契约应使用真实语义,不再暴露不存在的 RTC Room。 + +终端事件映射: + +| 火山/SDK 事件 | 平台动作 | +|---|---| +| SDK 初始化成功 | 更新遥测,不代表会话成功 | +| `session.created` | 保存火山会话标识,等待会话配置 | +| `session.updated` | 调用平台 activate,允许采集和播放 | +| 用户最终 ASR | 写入本地可靠字幕队列 | +| Agent 最终字幕 | 写入本地可靠字幕队列 | +| Function Call | 白名单校验后端侧执行,或转发平台工具网关 | +| Agent 错误/断开 | 上报稳定错误码,按策略恢复或停止 | +| 正常关闭 | 调用平台 stop 并冲刷字幕、用量和录音队列 | + +## 6. 平台接口设计 + +现有 `/device/v2` 的创建响应把 `rtc` 定义为必填对象。为了不破坏量产固件,建议新增 `/device/v3/agent-sessions`,不要修改 v2 字段语义。 + +### 6.1 准备会话 + +`POST /device/v3/agent-sessions` + +请求沿用设备 HMAC 和幂等键,声明: + +- `capabilities.hardware_agent=true`; +- SDK 版本、传输方案、音频格式; +- 是否支持字幕可靠队列、动态会话配置、凭证轮换、近/远端 PCM 录制; +- 网络和设备时间。 + +响应建议: + +```json +{ + "conversation_id": "...", + "status": "SESSION_READY", + "runtime": { + "provider": "volc_hardware_agent", + "bot_id": "<已关联 Bot>", + "transport": "high_quality", + "session_nonce": "<短期、单次>", + "config_digest": "sha256:...", + "wait_for_session_update": true + }, + "session_config": { + "dialog_id": "<平台生成的非个人标识>", + "system_context": "<已授权且脱敏的本次背景>", + "tool_scope_token": "<短期令牌>" + }, + "privacy": {}, + "heartbeat_interval_sec": 10, + "expires_at": "..." +} +``` + +响应不得返回 `product_secret` 或 `device_secret`。 + +### 6.2 激活会话 + +`POST /device/v3/agent-sessions/{conversation_id}/activate` + +请求包含 `provider_session_id`、`connected_at`、`transport`、`bot_id`、`config_digest` 和设备侧关联号。平台核对准备记录和配置摘要后进入 `ACTIVE`。 + +### 6.3 心跳 + +继续使用严格递增序号,但把 `rtc` 改为: + +```json +{ + "agent_link": { + "connected": true, + "input_active": true, + "output_active": true, + "provider_session_id": "...", + "reconnect_count": 0 + } +} +``` + +平台继续返回 `continue`、`stop`、`stop_after_turn`;新增 `rotate_provider_credential` 和 `refresh_session_config` 前,必须有显式设备能力位。 + +### 6.4 字幕 + +复用 `/device/v2/conversations/{id}/turns:batch` 或在 v3 提供同构接口。只上传最终字幕,不把增量字幕写入长期存储。保持 `turn_key + sequence_no` 去重和冲突检测。 + +### 6.5 停止 + +设备先请求 SDK 停止并停止采集,再调用平台 stop。平台在以下任一条件满足后收敛 `COMPLETED`: + +- 收到可信服务端结束事件; +- 设备报告关闭成功且心跳租约结束; +- 强制终止接口确认成功。 + +如果只能依赖设备报告,授权撤回和设备失控时无法做到服务端 fail-close,不能直接通过生产隐私门禁。 + +## 7. 后端模块与数据模型 + +### 7.1 运行时抽象 + +现有 `voiceprovider.Client` 的 `Start/Query/Stop` 契约只适合 RTC VoiceChat。建议新增: + +```go +type ConversationRuntime interface { + Prepare(context.Context, PrepareRequest) (PrepareResult, error) + Terminate(context.Context, TerminateRequest) error + Reconcile(context.Context, SessionRef) (RuntimeState, error) +} +``` + +- `LegacyVoiceChatRuntime` 封装现有 `voiceprovider.Client`; +- `VolcHardwareAgentRuntime` 不负责创建音频会话,只负责设备/Bot 配置同步、服务端终止、事件对账和用量拉取; +- 通话状态机、幂等、Outbox、审计和租约留在 `orchestration`。 + +### 7.2 新表建议 + +`hardware_agent_device_binding` + +- `device_id`、`provider`、`instance_ref`、`product_ref`; +- `provider_device_name`、`hardware_id_digest`; +- `bot_ref`、`license_ref`、`credential_version`; +- `status`、`registered_at`、`revoked_at`、`last_verified_at`。 + +`conversation_runtime_session` + +- `conversation_id`、`runtime_type`、`transport`; +- `provider_session_ref`、`provider_device_ref`、`bot_ref`; +- `session_nonce_digest`、`config_digest`; +- `status`、`connected_at`、`last_event_at`、`ended_at`; +- 唯一约束:一个通话最多一个活动运行时会话;同一供应商会话只能映射一个通话。 + +`hardware_agent_config_sync` + +- 平台版本 ID、Bot/产品引用、期望摘要、实际摘要; +- 同步状态、供应商请求引用、最后错误码和验证时间; +- 用于发现火山控制台手工修改造成的配置漂移。 + +### 7.3 事件归一化 + +扩展现有 `provider_event`,归一化为: + +- `hardware_agent.session.started`; +- `hardware_agent.session.updated`; +- `hardware_agent.transcript.final`; +- `hardware_agent.function_call`; +- `hardware_agent.usage.final`; +- `hardware_agent.session.stopped`; +- `hardware_agent.session.failed`。 + +设备上传事件和服务端回调必须使用不同 `source_type`,最终以 `provider_event_key` 和话轮键双层去重。 + +## 8. 工具、记忆和业务动作 + +### 8.1 工具分类 + +| 类型 | 示例 | 执行位置 | 控制要求 | +|---|---|---|---| +| 设备本地无副作用 | 查询电量、读取音量 | 设备 | 白名单、参数校验 | +| 设备本地可逆动作 | 调整音量、切换灯效 | 设备 | 范围限制、结果回传 | +| 平台只读工具 | 检索已确认记忆、读取已授权日程 | 平台工具网关 | 会话短令牌、租户/长者反查 | +| 平台有副作用工具 | 创建提醒、联系家人、修改资料 | 平台业务 API | 用户确认、幂等键、权限和审计 | +| 高风险动作 | 删除数据、转账、医疗决策 | 不直接开放给模型 | 使用既有人工流程 | + +### 8.2 工具网关安全 + +- 工具调用必须映射到活动通话,不能只信任模型传来的 `subject_id`; +- 参数使用固定 JSON Schema,拒绝未知字段; +- 每个调用有稳定 `call_id`,平台按 `conversation_id + call_id` 幂等; +- 返回内容限制长度并做提示词注入隔离; +- 工具超时返回结构化降级结果,不中断音频主链; +- 全量记录工具名、结果状态、耗时和资源版本,不记录不必要的敏感正文。 + +## 9. 隐私、录音和删除 + +### 9.1 授权撤回 + +当前系统在撤回字幕、原录音、故事整理或个性化记忆授权时,会把活动通话写入停止状态并投递可靠停止任务。迁移后必须实现等价闭环: + +1. 平台事务内写入撤回、通话停止意图、Outbox 和审计; +2. 在线设备通过心跳/SSE 命令立即关闭 SDK 会话; +3. 平台调用火山服务端终止或吊销接口兜底; +4. 未确认停止前禁止新建会话和工具访问; +5. 超时进入阻断告警和人工处置,而不是静默标记完成。 + +### 9.2 双轨录音 + +这是迁移中最可能发生范围变化的功能。PoC 必须确认: + +- 能否取得送入 SDK 前的近端 PCM; +- 能否取得 SDK 解码后、播放前的远端 PCM; +- 打断、重连、丢包隐藏和采样率变化后时间轴能否对齐; +- SDK 是否允许在本地进行受授权的录制; +- 若使用火山云端录制,能否满足近/远轨分离、短期访问、删除和审计要求。 + +如不能取得远端 PCM,不能把混音录音冒充现有双轨录音。第一阶段应明确限定为 `transcript_only`,待产品接受范围变更或供应商能力补齐后再开放录音。 + +### 9.3 删除和凭证吊销 + +主体隐私删除增加两个子任务: + +- 删除或匿名化火山侧与该设备/会话相关的可识别数据; +- 吊销设备智能体凭证和 License 绑定,并保存最小删除证明。 + +供应商无删除/吊销 API 时,需要人工控制台证据和对账任务,但该方式不适合大规模量产。 + +## 10. 用量、费用与 License + +现有七类用量可保留,但新增来源维度: + +```text +source = provider_callback | device_session | provider_bill +runtime = legacy_voicechat | volc_hardware_agent +``` + +建议新增类别: + +- `HARDWARE_AGENT_LICENSE`:设备 License 购买、激活、到期和转移; +- `HARDWARE_AGENT_VOICE`:AI 对话时长; +- `HARDWARE_AGENT_MODEL`:模型推理额度; +- 视频、声音复刻、联网问答等扩展资源按实际开通单独归类。 + +设备上报只用于实时额度预估,最终账单以火山账单/用量接口为准,并执行会话、设备、账期三级对账。火山产品页显示 License 按设备提供不同对话时长和模型额度,实际采购价格、超额规则、License 回收和换机政策必须在合同中冻结。[火山硬件智能体产品页](https://www.volcengine.com/product/ConversationalAI-Embedded) + +## 11. 灰度迁移步骤 + +### 阶段 0:供应商能力封板 + +在编码前取得正式 SDK、API、License 和数据处理条款,完成第 13 节问题闭环。输出一份签字的能力矩阵,未确认项不能用口头承诺替代。 + +### 阶段 1:独立真机 PoC + +- 1 个测试产品、1 个 Bot、3 台候选板; +- 跑通高质量 SDK 初始化、动态注册、建连、打断、重连、字幕和结束; +- 验证近/远端 PCM、强制停话、凭证吊销、License 更换; +- 暂不连接生产家庭/长者数据。 + +### 阶段 2:平台双运行时 + +- 新增运行时抽象、表和 `/device/v3`; +- 旧 v2/RTC 链路保持不变; +- 新设备只接固定 Bot,先打通授权、状态、字幕、内容处理和诊断; +- 火山配置先由人工控制台维护,平台保存摘要和漂移检查。 + +### 阶段 3:个性化与工具 + +- 接入动态会话参数和冻结背景; +- 接入平台记忆 MCP/Function Calling; +- 接入角色、模型、音色、Bot 的发布和回滚; +- 接入用量、License、隐私删除和供应商对账。 + +### 阶段 4:灰度试点 + +- 内部设备 -> 友好家庭 -> 小比例真实家庭; +- 每个设备的运行时由不可变灰度版本决定; +- 出现阻断问题时只切回 `legacy_rtc`,不修改历史通话运行时; +- 灰度期间两个运行时分别统计成功率、首响、打断、重连、字幕、费用和投诉。 + +### 阶段 5:主链切换与退役 + +只有新链路连续通过真机、隐私、费用和运行恢复门禁后,才把新出厂设备默认设为硬件智能体。旧 `StartVoiceChat` 代码、RTC Token 签发和模板不能立即删除,至少保留到全部在役设备完成升级或退出支持期。 + +## 12. 验收与回滚门禁 + +建议新的真机最小矩阵至少覆盖 3 台量产候选设备、6 通会话: + +1. 正常完整通话; +2. 用户打断和 Agent 被打断; +3. Wi-Fi/蜂窝或批准网络切换恢复; +4. 平台受控停止; +5. 活动通话中授权撤回; +6. 设备断电重启后的字幕队列恢复和资源回收。 + +每通必须核对: + +- 平台通话、火山会话、设备和 Bot 关联唯一; +- 音频上行、下行、首响、打断和恢复符合现场冻结标准; +- 最终字幕完整、顺序稳定、重放不重复; +- 背景和工具调用严格限制在当前租户、长者和通话; +- 结束后 SDK、License 占用、平台租约和工具令牌全部释放; +- 设备、平台、火山、字幕、用量和隐私证据引用完整。 + +建议上线 SLO 在试点前冻结,不在代码中写死。至少包含:建连成功率、首次回复时延、打断成功率、异常重连成功率、字幕完整率、停止收敛时延、重复计费率、供应商账单差异和凭证吊销生效时延。 + +回滚规则: + +- 回滚只影响新会话;活动会话按原运行时完成或受控停止; +- 数据库中的 `runtime_type` 和配置摘要不可改写; +- 设备切回旧运行时前必须确认旧 RTC SDK 和配置仍在; +- 无法强制停话、出现跨租户工具访问、删除不收敛或凭证泄露时,立即停止硬件智能体新会话灰度。 + +## 13. 必须向火山确认的问题 + +以下问题未得到书面答案前,不建议承诺完整替换日期: + +1. 是否提供按设备或会话的服务端强制终止接口?设备离线时多久生效? +2. `device_secret` 是否支持轮换、吊销、批量禁用和换板迁移? +3. 产品、设备、Bot 绑定和 License 是否都有 OpenAPI,而不是只能操作控制台? +4. 是否有服务端会话开始、结束、最终字幕、错误和用量回调?签名和重放保护是什么? +5. 是否允许传入平台生成的 `conversation_id/dialog_id` 并在回调和账单中原样返回? +6. 动态会话参数可以覆盖哪些 Bot 配置?何时生效?是否保证收到 `session.updated` 后再开始识别? +7. 高质量 SDK 是否同时提供近端输入 PCM 和远端播放 PCM 回调?采样率、声道和时间戳如何定义? +8. Function Calling/MCP 的服务端鉴权、超时、重试、幂等和最大正文限制是什么? +9. License 的激活、到期、换机、回收、离线宽限和超额计费规则是什么? +10. 能否按设备、Bot、会话、租户标签导出用量并与账单对账? +11. 会话音频、字幕、日志和模型上下文分别保留多久?如何导出和删除?数据地域在哪里? +12. SDK 支持的芯片、编译器、RTOS/Linux、RAM/Flash/CPU、AEC/AGC/ANS、网络端口和证书更新要求是什么? +13. 高质量和低负载方案能否在同一产品/设备上切换?切换是否会改变 Bot、License、回调和计费语义? +14. 量产并发、建连 QPS、设备数、Bot 数和回调吞吐的配额如何申请和压测? + +## 14. 主要风险 + +| 风险 | 影响 | 控制措施 | +|---|---|---| +| 产品级 Secret 被固件提取 | 同产品设备可被仿冒或批量注册 | 一次性安全注册、设备独享 Secret、安全存储、吊销演练 | +| 火山控制台配置漂移 | 平台记录与实际 Bot 行为不一致 | 平台配置摘要、定时对账、变更工单、生产禁止无审计手改 | +| 只能由设备报告会话结束 | 授权撤回时不能服务端强制停止 | 以供应商强停/吊销能力作为上线硬门槛 | +| 字幕只有设备单路回传 | 断电或固件故障造成内容缺口 | 本地加密可靠队列、服务端回调双源、终稿补传和差异告警 | +| SDK 无远端 PCM | 双轨录音功能无法等价迁移 | PoC 提前验证;不满足时明确限定 `transcript_only` | +| License 与内部设备解绑不同步 | 换机失败、成本浪费或越权使用 | License 台账、绑定状态机、每日对账和售后流程 | +| Bot 数量或动态参数受限 | 现有多租户灰度模型无法直接复制 | 少量基础 Bot + 会话动态参数;配置版本与供应商摘要冻结 | +| 工具调用越权或重复 | 读取他人记忆或执行重复动作 | 会话范围令牌、服务端反查、Schema、幂等、确认和审计 | + +## 15. 评审建议 + +甲方方案评审时可以把范围分成三层: + +- 必须等价迁移:正常语音、打断、设备绑定、授权门禁、通话状态、最终字幕、摘要/故事、人工记忆、额度、诊断和受控停止; +- 需要供应商能力确认:服务端强停、设备凭证吊销、字幕/用量服务端回调、动态会话参数、MCP 鉴权; +- 可能需要范围调整:双轨录音、三种旧 `engine_mode`、RTC 房间级诊断和旧 VoiceChat 模板。 + +推荐先让甲方确认“硬件对话智能体只替换语音会话执行层,现有业务平台继续作为控制面和数据主库”。确认这条边界后,再进入 SDK PoC 和接口改造,能避免后续把火山控制台误当成整个业务平台替代品。 diff --git a/firmware/echoear-platform-agent/CMakeLists.txt b/firmware/echoear-platform-agent/CMakeLists.txt new file mode 100644 index 0000000..7f085a5 --- /dev/null +++ b/firmware/echoear-platform-agent/CMakeLists.txt @@ -0,0 +1,13 @@ +# For more information about build system see +# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html +# The following five lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +# include($ENV{IDF_PATH}/tools/cmake/project.cmake) + +#include($ENV{ADF_PATH}/CMakeLists.txt) +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(VolcRTCDemo) + +# add_compile_definitions(LWIP_DEBUG=1) diff --git a/firmware/echoear-platform-agent/README.md b/firmware/echoear-platform-agent/README.md new file mode 100644 index 0000000..c657ae8 --- /dev/null +++ b/firmware/echoear-platform-agent/README.md @@ -0,0 +1,211 @@ +

    +

    ConversationalAI Embedded Kit 2.0

    + +## EchoEar 平台版固件 + +本目录是在火山官方 Embedded Kit 2.0 基础上适配的 ESP32-S3 EchoEar 固件。它使用低负载 WebSocket 方案,并将设备身份、动态注册和会话配置统一接入自有 SaaS。 + +安全边界: + +- 固件和设备端都不包含火山 `Product Secret`; +- SaaS 使用平台设备身份为 EchoEar 动态申请火山 `Device Secret`; +- EchoEar 只保存自己的平台设备凭据和火山单设备凭据; +- 每次对话前由 SaaS 下发 Bot、人设、记忆上下文和会话配置; +- 字幕和最终轮次回传 SaaS,供会话记录与长期记忆处理。 + +当前测试硬件:ESP32-S3 EchoEar,16 MB Flash、16 MB PSRAM,硬件标识由 STA MAC 生成(当前板为 `echoear-b43a451634a8`)。 + +每次申请对话前,先通过 `/device/v2/bootstrap` 上报实际固件版本并检查 `firmware_compatible`,再获取配置和创建会话。固件版本为 `1.0.0-echoear-platform.1`;本工程基于 Embedded Kit 2.0,硬件智能体 SDK 版本独立上报为 `2.0.0`。会话能力中的 `sdk_version` 不能用固件版本代替。会话尚未创建时保持唤醒采集,不重复拆建音频管线。SPI 屏幕仅在 `on_color_trans_done` 后释放 GFX 帧缓冲,避免异步传输期间提前复用缓冲区。运行状态用眼睛动画表示,避免 80 MHz 下中文逐像素渲染长期占用 CPU:快速眨眼表示聆听,转圈表示思考,正常眼睛表示待机或回答。 + +本板构建使用 16 MB Flash,Flash 与八线 PSRAM 均为 80 MHz。通用默认配置、ESP32-S3 默认配置和当前 `sdkconfig` 必须一致;不要恢复上游的 32 MB Flash 或实验性 120 MHz PSRAM 参数。 + +唤醒词为“嗨,乐鑫”,启用 `CONFIG_SR_WN_WN9_HILEXIN`,使用 `DET_MODE_95` 高灵敏度检测,并将 AFE 线性增益设为 2.0。生成的 `build/srmodels/srmodels.bin` 必须包含 `wn9_hilexin` 模型(约291 KB),并烧录到 `model` 分区 `0x510000`;不能使用未选择任何模型时生成的4字节空文件。 + +EchoEar V1.2 的 ES7210 使用 TDM:I2S 按两路32位接收,AFE 按四路16位解释,布局为 `RMNM`(ch1/ch3 麦克风、ch0 回声参考、ch2 忽略)。不可改成两路16位 `MR`。诊断日志 `MIC_LEVEL` 仅记录各路幅度统计,不保存录音。PCM输出每帧60 ms、16 kHz、单声道16位,读取缓冲区为1920字节,避免截断半帧。唤醒事件在会话服务就绪后直接发布,不依赖已移除的 `CONFIG_LANGUAGE_WAKEUP_MODE`。 + +低负载 WebSocket 任务使用 12 KiB 栈;4 KiB 会在收到 `session.created` 后发生栈溢出并重启。握手期间暂停上传麦克风帧,待引擎进入 STARTED 后再发送。低负载接口返回的 PCM 按 24 kHz 单声道16位解码,再由播放管线转换到板载 16 kHz I2S。`session.created` 的 `job_id` 用作平台启动确认,确保后续字幕和结束事件在 ACTIVE 会话上提交。 + +联网、平台 HTTPS 激活和音频初始化在独立的 24 KiB 启动任务中执行,避免默认 4 KiB `main` 栈溢出导致循环重启;任务退出时释放栈,并记录启动阶段最小栈余量。平台激活成功后才启动音频采集。激活失败时保留设备配置、显示绑定或配置提示,并仅记录平台错误码,不输出响应正文或凭据。 + +STA 连接使用全信道扫描并按信号强度选择同名 AP,实时语音期间关闭 Wi-Fi 省电,避免多 AP 环境中误连弱信号节点造成 WebSocket 写入失败。 + +### Windows 编译与烧录 + +使用 ESP-IDF v5.5.1: + +```powershell +. 'E:\Codex\toolchains\esp-idf-v5.5.1\export.ps1' +idf.py build +idf.py -p COM6 flash +idf.py -p COM6 monitor +``` + +首次烧录前建议完整备份 Flash。当前板原厂备份位于 `backups/b43a451634a8/factory-2026-09-03-16mb.bin`,恢复命令: + +```powershell +esptool.py --chip esp32s3 --port COM6 write_flash 0x0 backups/b43a451634a8/factory-2026-09-03-16mb.bin +``` + +### 首次激活 + +首次启动、缺少平台设备凭据、保存的 Wi-Fi 连接失败或超时,以及手动请求重新配网时,都会进入配置热点,无需清空 NVS: + +1. 连接 `VolcConvAI-XXXXXX`,热点密码与热点名相同; +2. 打开 `http://192.168.4.1`; +3. 填写 2.4 GHz Wi-Fi、平台 Device ID 和平台 Device Secret; +4. 重启后,设备通过 SaaS 完成火山动态注册并获取单设备运行凭据。 + +正常运行时短触圆屏任意位置可直接开始对话,作为唤醒词的可靠备用入口。屏幕底部区域持续按住3秒后设备重启并开启配置热点。该操作保留原来的 Wi-Fi 和设备身份,只记录重新配网请求;成功提交配网页后退出配网模式。触摸任务在联网和平台激活之前创建,因此激活失败时也可以请求重新配网。 + +如果尚未配置平台设备身份,固件不会尝试用 Product Secret 直接访问火山。 + +重新配网回归检查(需要烧录后实机验证): + +- 已保存可用 Wi-Fi、但缺少平台凭据:启动后直接出现热点和配网地址。 +- Wi-Fi 密码错误或 DHCP 超时:连接等待结束后进入热点,不能重启循环。 +- 正常运行时:短触屏幕启动对话;按住屏幕底部区域3秒进入配置热点。 +- 配网页提交错误 Wi-Fi 后可重试;只有取得 IP 地址后才保存配置并重启。 +- 配网页成功提交后正常启动,手动配网请求清除,原平台身份不会因进入配网而被删除。 + +本地 `idf.py build` 仅验证编译、链接和分区大小,不代替热点可见性、触摸坐标及长按时长的实机验证。 + +配网页提交使用 JSON 与后台连接任务,`POST /submit` 受理后立即返回 202,页面通过 `GET /setup-status` 查询连接、保存成功或失败状态。连接失败可以重试;保存成功后等待15秒重启,再由启动流程执行平台激活。Wi-Fi 扫描改为手动触发,连接期间不扫描。更新固件后请刷新配网页,避免继续使用已经打开的旧表单。 + +配网页交互回归:`node --test tests/setup-portal.test.cjs`(模拟设备响应,覆盖失败重试、响应丢失、断联、成功提示和输入校验)。 + +## 快速开始 + +具体操作,请参考 [官网文档](https://www.volcengine.com/docs/6348/1806625)。 + +## 运行设备端(乐鑫) + +以下操作以 macOS 操作系统为例。 + +### 环境与硬件要求 + +- 乐鑫 ESP32-S3-Korvo-2 +- USB 数据线:两条 A 转 Micro-B 数据线,一条作为电源线,一条作为串口线。 +- PC 设备:编译和烧录。支持 Windows、Linux 或者 macOS 操作系统。(本文操作以 macOS 为例) + +### 配置乐鑫环境 + +详见[开发环境配置文档](https://docs.espressif.com/projects/esp-idf/zh_CN/stable/esp32s3/get-started/index.html)。 + +1. 安装 CMake 和 Ninja 编译工具。 + + ```bash + brew install cmake ninja dfu-util + ``` +2. 将乐鑫 ADF 框架克隆到本地,并同步各子仓(submodule)代码。 + + > **注意**:Demo 中使用的 ADF 版本为 `eca11f20e56f9b5321b714da4305e123672d92a9`,对应 IDF 版本为 `v5.4`,请确保 ADF 版本与 IDF 版本匹配。 + > + + ```bash + # 1. clone 乐鑫 ADF 框架 + git clone https://github.com/espressif/esp-adf.git + # 2. 进入esp-adf目录 + cd esp-adf + # 3. 切换到乐鑫 ADF 指定版本 + git reset --hard eca11f20e56f9b5321b714da4305e123672d92a9 + # 4. 同步各子仓代码 + git submodule update --init --recursive + ``` +3. 安装乐鑫 esp32s3 开发环境相关依赖。 + + ```bash + ./install.sh esp32s3 + ``` + + 成功安装所有依赖后,命令行会出现如下提示: + ```bash + All done! You can now run: + . ./export.sh + ``` + + > 如在上述任何步骤中遇到以下错误: + > ` 可前往**访达->应用程序->Python** 文件夹,点击 `Install Certificates.command` 安装证书。更多信息,请参考 [安装 ESP-IDF 工具时出现的下载错误](https://github.com/espressif/esp-idf/issues/4775)。 + > +4. 设置环境变量。 + + > **每次打开命令行窗口均需要运行该命令进行设置。** + > + + ```bash + . ./export.sh + ``` + +### 下载并配置工程 + +1. 将实时对话式 AI 硬件示例工程克隆到 乐鑫 ADF examples 目录下。 + + 1. 进入 esp-adf/examples 目录。 + + ```bash + cd $ADF_PATH/examples + ``` + + 2. 克隆实时对话式 AI 硬件示例工程。 + + ```bash + git clone https://github.com/volcengine/ConversationalAI-Embedded-Kit-2.0.git + ``` +2. 修复乐鑫按键问题 + + 1. 进入 esp-adf 目录。 + + ```bash + cd $ADF_PATH + ``` + + 2. 修复乐鑫按键问题。 + + ```bash + git apply $ADF_PATH/examples/ConversationalAI-Embedded-Kit-2.0/low_load_first/espressif/0002-fix-esp-button.patch + ``` + +### 编译固件 + +进入 `esp-adf/examples/ConversationalAI-Embedded-Kit-2.0/low_load_first/espressif` 目录下编译固件。 + +1. 进入 espressif 目录。 + ```bash + cd $ADF_PATH/examples/ConversationalAI-Embedded-Kit-2.0/low_load_first/espressif + ``` +2. 设置编译目标平台。 + ```bash + idf.py set-target esp32s3 + ``` +3. 设置 实例ID、产品ID、产品秘钥、设备ID等参数。 + ```bash + idf.py menuconfig + ``` + + 进入 `Example Configuration` 菜单,在 `volcano instance id` 中填入你的实例ID,在 `volcano product key` 中填入你的产品Key,在 `volcano product secret` 中填入你的产品秘钥,在 `device name` 中填入你的设备ID, 在 `bot id` 中填入你的智能体ID,并保存。 +4. 编译固件。 + ```bash + idf.py build + ``` + +### 烧录并运行示例 Demo + +1. 打开乐鑫开发板电源开关。 +2. 烧录固件。 + + ```bash + idf.py flash + ``` +3. 运行示例 Demo 并查看串口日志输出。 + + ```bash + idf.py monitor + ``` +4. Wi-Fi 配网。 + + 1. 手机找到名如 VolcConvAI-XXXXXX” 的 Wi-Fi 热点,密码同Wi-Fi名,连接上 Wi-Fi。 + 2. 打开浏览器,在地址栏输入 `http://192.168.4.1`,进入 Wi-Fi 配网页面。 + 3. 输入 Wi-Fi 名称和密码,点击提交。 + + > **注意**:如果需更换 Wi-Fi,请重启设备。如果设备重启后无法连接到之前保存的 Wi-Fi(例如超出了范围或旧网络已关闭),请等待 30s 进入配网模式,再重新执行上面 Wi-Fi 配网的 3 个步骤。 + > diff --git a/firmware/echoear-platform-agent/assets/Happy.eaf b/firmware/echoear-platform-agent/assets/Happy.eaf new file mode 100644 index 0000000..06b307c Binary files /dev/null and b/firmware/echoear-platform-agent/assets/Happy.eaf differ diff --git a/firmware/echoear-platform-agent/assets/boot_360_360.eaf b/firmware/echoear-platform-agent/assets/boot_360_360.eaf new file mode 100644 index 0000000..5bc821f Binary files /dev/null and b/firmware/echoear-platform-agent/assets/boot_360_360.eaf differ diff --git a/firmware/echoear-platform-agent/assets/emotion_angry_284_126.eaf b/firmware/echoear-platform-agent/assets/emotion_angry_284_126.eaf new file mode 100644 index 0000000..005d4bb Binary files /dev/null and b/firmware/echoear-platform-agent/assets/emotion_angry_284_126.eaf differ diff --git a/firmware/echoear-platform-agent/assets/emotion_blink1_284_126.eaf b/firmware/echoear-platform-agent/assets/emotion_blink1_284_126.eaf new file mode 100644 index 0000000..37af2a9 Binary files /dev/null and b/firmware/echoear-platform-agent/assets/emotion_blink1_284_126.eaf differ diff --git a/firmware/echoear-platform-agent/assets/emotion_blink_fast_284_126.eaf b/firmware/echoear-platform-agent/assets/emotion_blink_fast_284_126.eaf new file mode 100644 index 0000000..936dc8c Binary files /dev/null and b/firmware/echoear-platform-agent/assets/emotion_blink_fast_284_126.eaf differ diff --git a/firmware/echoear-platform-agent/assets/emotion_dizzy_284_126.eaf b/firmware/echoear-platform-agent/assets/emotion_dizzy_284_126.eaf new file mode 100644 index 0000000..24a158b Binary files /dev/null and b/firmware/echoear-platform-agent/assets/emotion_dizzy_284_126.eaf differ diff --git a/firmware/echoear-platform-agent/assets/emotion_sleep_284_126.eaf b/firmware/echoear-platform-agent/assets/emotion_sleep_284_126.eaf new file mode 100644 index 0000000..6e76870 Binary files /dev/null and b/firmware/echoear-platform-agent/assets/emotion_sleep_284_126.eaf differ diff --git a/firmware/echoear-platform-agent/components/application/CMakeLists.txt b/firmware/echoear-platform-agent/components/application/CMakeLists.txt new file mode 100644 index 0000000..9c92fb7 --- /dev/null +++ b/firmware/echoear-platform-agent/components/application/CMakeLists.txt @@ -0,0 +1,27 @@ +cmake_minimum_required(VERSION 3.16) + +message(STATUS "................. ${CMAKE_SYSTEM_NAME}") +if("${CMAKE_SYSTEM_NAME}" MATCHES "(Rtos|rtos|RTOS)+") + set(PLATFORM_RTOS TRUE) + add_compile_definitions(PLATFORM_RTOS) +elseif("${CMAKE_SYSTEM_NAME}" MATCHES "(Linux|linux|LINUX)+") + set(PLATFORM_LINUX TRUE) + add_compile_definitions(PLATFORM_LINUX) +elseif("${CMAKE_SYSTEM_NAME}" MATCHES "(android|Android|ANDROID)+") + set(PLATFORM_ANDROID TRUE) + add_compile_definitions(PLATFORM_ANDROID) +elseif("${CMAKE_SYSTEM_NAME}" MATCHES "(Darwin|darwin|MACOSX|macosx|MACOS|macos)+") + set(PLATFORM_MACOS TRUE) + add_compile_definitions(PLATFORM_MACOS) +endif() + +include(${CMAKE_CURRENT_LIST_DIR}/cmake/common.cmake) +if(ESP_PLATFORM) + message(STATUS "ESP_PLATFORM is defined, including esp.cmake") + include(${CMAKE_CURRENT_LIST_DIR}/cmake/espressif.cmake) + return() +elseif(PLATFORM_MACOS) + message(STATUS "PLATFORM_MACOS is defined, including macos.cmake") + include(${CMAKE_CURRENT_LIST_DIR}/cmake/macos.cmake) + return() +endif() \ No newline at end of file diff --git a/firmware/echoear-platform-agent/components/application/application_AI_Generated_Code_Reading_Guide.md b/firmware/echoear-platform-agent/components/application/application_AI_Generated_Code_Reading_Guide.md new file mode 100644 index 0000000..e65473a --- /dev/null +++ b/firmware/echoear-platform-agent/components/application/application_AI_Generated_Code_Reading_Guide.md @@ -0,0 +1,734 @@ +# `application` 代码梳理 + +> 说明:本文档由 AI 在阅读当前目录代码后生成,目标是帮助读者快速理解代码结构、模块职责与主要运行流程,作为代码导读使用。 + +## 1. 目录定位 + +`application` 不是单一的业务文件夹,而是一套面向设备侧 AI 对话场景的“应用层组件”: + +- `framework/` 提供事件驱动框架,解决不同业务模块之间的通信问题。 +- `platform/` 提供硬件抽象层 HAL,屏蔽板级和外设细节。 +- `service/` 提供 AI 对话业务拆分后的多个服务模块。 +- `cmake/` 负责把 framework、platform、service 组装成一个 ESP-IDF 组件。 + +结合各子目录 `README_zh.md` 的设计说明,可以把这套代码理解为: + +- `volc_conv_ai` 是底层云连接 SDK。 +- `application` 则是建立在该 SDK 之上的业务应用框架。 + +也就是说,`application` 关注的是“设备如何组织业务逻辑、如何和硬件协作、如何把 AI 对话跑起来”,而不是连接协议本身。 + +## 2. 总体分层 + +整个目录可以概括成 3 层: + +### 2.1 Framework 层 + +职责是让不同业务模块通过事件 `pub/sub` 通信,不直接相互调用。 + +核心文件: + +- `framework/inc/aios.h` +- `framework/inc/aios_def.h` +- `framework/src/aios.c` + +这是整个应用层的“调度内核”。 + +### 2.2 Platform 层 + +职责是为 AI 对话场景提供硬件抽象: + +- 音频采集 +- 音频播放 +- 屏幕显示 +- 按键 +- 文件系统 +- 全局硬件上下文 + +核心文件: + +- `platform/inc/volc_hal.h` +- `platform/inc/volc_hal_*.h` +- `platform/src/espressif/*.c` + +从当前实现看,`platform/src/espressif` 是主实现平台,明显以 ESP32/ESP-IDF 为目标。 + +### 2.3 Service 层 + +职责是把 AI 对话业务拆成几个相互解耦的服务: + +- `conv_service` + - 负责 AI 对话的建立、运行、退出。 +- `function_call_service` + - 负责处理智能体触发的 function call。 +- `local_logic_service` + - 负责本地预设逻辑,不依赖大模型理解。 +- `service/src/volc_manager_service.c` + - 负责接收高层业务入口事件,并拉起对应 service。 + +这与 `service/README_zh.md` 的描述是一致的。 + +## 3. 这个目录不是完整 App Main + +单看 `application` 目录,本身**没有完整的 `main/app_main`**。 + +它更像是一个“可复用的应用组件库”。真正的组装顺序出现在仓库里的示例,例如: + +- `examples/high_quality_solution/espressif/main/conv_ai_embedded_kit.c` + +该示例展示了应用启动的标准顺序: + +1. `volc_hal_init()` +2. `volc_hal_display_create()` +3. 初始化网络、时间同步等系统能力 +4. `aios_init(VOLC_SERVICE_EVENT_MAX)` +5. 初始化各 service: + - `volc_service_manager_init()` + - `volc_conv_service_manager_init()` + - `function_call_service_init()` + - `local_logic_service_init()` +6. `aios_run()` +7. 在 wakeup 回调中发布 `VOLC_SERVICE_AI_CONVERSATION` 事件 + +因此,`application` 目录的代码更应该被理解成“主程序可直接引用的一套模块化应用骨架”。 + +## 4. Framework:AIOS 事件框架 + +### 4.1 AIOS 是什么 + +`framework` 的 README_zh 说明得很直白:它是为了让不同业务模块通过 `sub/pub event` 进行通信。 + +`aios.c` 的实现表明,这实际上是一个: + +- 单线程事件循环 +- 状态机驱动 +- 带优先级的 session 调度器 +- 带定时器能力的轻量运行时 + +### 4.2 核心概念 + +#### 1. `aios_session_t` + +表示一个业务状态机实例,包含: + +- 优先级 +- 是否启用 +- 当前状态函数 + +应用中的每个 service 基本都对应一个 session。 + +#### 2. `aios_event_t` + +表示一个事件,包含: + +- 事件 ID +- 当前尚未消费它的订阅者位图 +- 事件数据 +- 数据释放回调 + +#### 3. 状态函数 + +每个 session 都由状态函数驱动,签名是: + +```c +aios_ret_t (*aios_state_handler)(struct aios_session *const me, aios_event_t const * const e); +``` + +返回值可以表示: + +- 已处理 +- 未处理 +- 状态迁移 + +### 4.3 调度方式 + +`aios_run()` 会不断调用 `aios_once()`。 + +`aios_once()` 的主逻辑是: + +1. 处理到期定时器,定时器会转化为普通事件。 +2. 如果当前没有事件,则进入可中断睡眠。 +3. 从高优先级到低优先级寻找“当前有订阅事件待处理”的 session。 +4. 找出这个 session 最早应该消费的事件。 +5. 调用 session 的当前状态函数处理事件。 +6. 如果该事件没有剩余订阅者,则释放它。 + +这套机制的重点是: + +- 事件是共享的,但每个订阅者只消费一次。 +- 调度按 session 优先级进行。 +- 不同 service 之间完全依赖事件通信。 + +### 4.4 为什么适合这个项目 + +这套框架特别适合当前项目,因为这里的业务是典型的“多模块协作”: + +- 唤醒触发 AI 对话 +- AI 对话过程中收到字幕 +- 字幕再触发本地逻辑 +- function call 消息再触发本地动作 + +如果全部互相直接调用,耦合会很高;用 AIOS 后,模块只需要关心自己订阅和发布什么事件。 + +## 5. Service 层:业务模块划分 + +`service/README_zh.md` 已给出分工,源码进一步明确了具体职责。 + +### 5.1 公共事件定义 + +`service/inc/volc_service_common.h` 是业务总线的关键头文件,定义了: + +- 事件 ID +- session 优先级 + +主要事件包括: + +- `VOLC_SERVICE_AI_CONVERSATION` + - 请求进入 AI 对话 +- `VOLC_SERVICE_AI_CONVERSATION_START` + - 真正启动 AI 对话线程 +- `VOLC_SERVICE_AI_CONVERSATION_QUIT` + - 退出 AI 对话 +- `VOLC_SERVICE_AI_CONVERSATION_OVER` + - AI 对话线程结束后的通知 +- `VOLC_FUNCTION_CALL_EXEC` + - 执行 function call +- `VOLC_FUNCTION_CALL_TRIGGER` + - function call 被智能体触发 +- `VOLC_LOCAL_LOGIC_PLAY_WELCOME` + - 播放欢迎音 +- `VOLC_LOCAL_LOGIC_PROCESS_SUBTITLE` + - 本地处理字幕 + +优先级定义为: + +- AI 对话服务优先级最低编号 `0` +- 然后是 function call +- 然后是 local logic +- app manager 优先级最高编号 `3` + +在 `aios.c` 中,高优先级 session 会先被选中执行。 + +## 6. `volc_manager_service`:业务入口管理 + +文件: + +- `service/src/volc_manager_service.c` + +这是最上层的“应用业务入口控制器”。 + +### 6.1 它做了什么 + +它订阅两个顶层事件: + +- `VOLC_SERVICE_NETWORK_CONFIG` +- `VOLC_SERVICE_AI_CONVERSATION` + +当前代码里真正有效的是: + +- 收到 `VOLC_SERVICE_AI_CONVERSATION` +- 再发布 `VOLC_SERVICE_AI_CONVERSATION_START` + +因此它的本质是: + +- 作为“业务启动总入口” +- 把更粗粒度的用户/系统动作转换成具体 service 执行动作 + +### 6.2 作用 + +这一层让“谁要求启动 AI 对话”和“AI 对话服务怎么起线程”分离开了。 + +## 7. `conv_service`:核心 AI 对话服务 + +这是整个目录最关键的业务模块。 + +相关文件: + +- `service/conv_service/src/volc_conv_service.c` +- `service/conv_service/src/volc_conv.c` + +两者分工明显: + +- `volc_conv_service.c` + - service 管理器,负责事件响应与线程管理 +- `volc_conv.c` + - AI 对话线程的真正业务实现 + +### 7.1 `volc_conv_service_manager.c` 的职责 + +`volc_conv_service_manager_init()` 会注册一个 session,并订阅: + +- `VOLC_SERVICE_AI_CONVERSATION_START` +- `VOLC_SERVICE_AI_CONVERSATION_INTERRUPT` +- `VOLC_SERVICE_AI_CONVERSATION_QUIT` +- `VOLC_SERVICE_AI_CONVERSATION_OVER` + +收到不同事件后的行为: + +- `START` + - 创建 `conv_ai_service_task` 线程 +- `QUIT` + - 调用 `conv_ai_service_task_stop()` +- `OVER` + - 清空线程句柄 + +也就是说,这一层负责“线程生命周期管理”,而不是 AI 对话本身。 + +### 7.2 `volc_conv.c` 的职责 + +这个文件是 AI 对话业务主实现。 + +它做了 5 件关键事情: + +#### 1. 构造 `volc_conv_ai` 配置 + +通过 `CONV_AI_CONFIG_FORMAT` 拼出连接云端 SDK 所需的 JSON,包含: + +- IoT 配置 +- RTC 配置 +- 编解码参数 +- RTC 参数扩展 + +然后在 `conv_ai_service_init()` 里调用: + +- `volc_create()` + +这说明 `application` 这一层直接把底层 SDK 当成对话引擎使用。 + +#### 2. 建立采集与播放 + +`conv_ai_service_task()` 启动时会: + +- 创建或复用音频采集对象 +- 切换采集模式到 `VOLC_AUDIO_MODE_CAPTURE` +- 创建或复用音频播放器 +- 启动播放器 + +其中: + +- 采集回调 `__audio_capture_cb()` + - 采集到音频后立即调用 `volc_send_audio_data()` +- 音频回调 `__on_volc_audio_data()` + - 收到智能体返回的音频后用 `volc_hal_player_play_data()` 播放 + +这就形成了一个完整的音频闭环: + +- 麦克风采集 -> 发送到云端 +- 云端返回语音 -> 喇叭播放 + +#### 3. 处理会话状态并驱动 UI + +`__on_volc_conversation_status()` 会根据 SDK 回调状态更新显示层: + +- `LISTENING` + - 状态栏显示“智能体聆听中” +- `THINKING` + - 主画面切到思考表情 + - 状态栏显示“智能体思考中” +- `ANSWERING` + - 主画面切到开心表情 + - 状态栏显示“智能体说话中” +- `INTERRUPTED` + - 状态栏显示“智能体被打断” +- `ANSWER_FINISH` + - 状态栏显示“智能体说话完成” + +这表明 UI 与对话状态是强绑定的。 + +#### 4. 处理消息数据 + +`__on_volc_message_data()` 会解析从底层 SDK 收到的二进制消息头: + +- `subv` + - 字幕消息 +- `tool` + - function call 执行消息 +- `info` + - function call 触发提示消息 + +随后: + +- 字幕消息交给 `__on_subtitle_message_received()` +- `tool` 发布 `VOLC_FUNCTION_CALL_EXEC` +- `info` 发布 `VOLC_FUNCTION_CALL_TRIGGER` + +所以 `conv_service` 其实是“AI 中枢”,它把底层 SDK 输出的不同类别消息重新分发给其他 service。 + +#### 5. 管理对话生命周期 + +线程主体会: + +1. 启动采集与播放 +2. 调用 `volc_start()` +3. 进入循环等待,按秒累加 `wait_time` +4. 超时或被要求退出时: + - `volc_stop()` + - 发布 `VOLC_SERVICE_AI_CONVERSATION_OVER` + - 停止采集、停止播放、销毁播放对象 + - 重启唤醒模式采集 + - 恢复待机 UI + +这里的设计非常关键: + +- AI 对话期间使用 `CAPTURE` 模式持续采音 +- AI 对话结束后恢复 `WAKEUP` 模式等待下一次唤醒 + +这体现了“待机态/对话态”双模式切换。 + +## 8. `function_call_service`:函数调用桥接层 + +文件: + +- `service/function_call_service/src/volc_function_call_service.c` +- `service/function_call_service/src/volc_local_function_list.c` + +### 8.1 作用 + +这个 service 专门处理智能体下发的 function call。 + +当前代码里只实现了一个本地函数: + +- `adjust_audio_val` + +### 8.2 两类消息 + +#### 1. 触发提示消息 `VOLC_FUNCTION_CALL_TRIGGER` + +收到后,会判断是否是 `adjust_audio_val`,如果是就先调用: + +- `volc_send_text_to_agent(..., "别着急,我来给你调整音量", VOLC_AGENT_TYPE_TTS, 2)` + +这相当于先让智能体“说一句回应”,再执行工具。 + +#### 2. 执行消息 `VOLC_FUNCTION_CALL_EXEC` + +收到真正的 `tool_calls` JSON 后: + +- 解析函数名 +- 解析参数 +- 如果是 `adjust_audio_val` + - 调用本地音量调整逻辑 + - 再构造带 `func` 头的结果消息 + - 调用 `volc_send_message()` 把执行结果回传给智能体 + +### 8.3 本地函数实现 + +`volc_local_function_list.c` 中实现了: + +- 调大音量 +- 调小音量 + +它通过 HAL 获取当前播放器句柄,再调用: + +- `volc_hal_get_audio_player_volume()` +- `volc_hal_set_audio_player_volume()` + +所以 function call 服务的本质是: + +- 把模型侧工具调用,映射为设备本地硬件动作 + +## 9. `local_logic_service`:本地规则逻辑 + +文件: + +- `service/local_logic_service/src/local_logic_service.c` + +### 9.1 它与 function call 的区别 + +README_zh 已经说明了区别: + +- `function_call_service` + - 处理智能体明确发起的工具调用 +- `local_logic_service` + - 处理设备本地预设逻辑,不依赖智能体工具协议 + +### 9.2 当前实现的两个能力 + +#### 1. 播放欢迎音 + +收到 `VOLC_LOCAL_LOGIC_PLAY_WELCOME` 事件时: + +- 从 `/sdcard/wel.pcm` 读取 PCM 数据 +- 逐块送入播放器播放 + +#### 2. 解析字幕并触发退出逻辑 + +收到 `VOLC_LOCAL_LOGIC_PROCESS_SUBTITLE` 事件时: + +- 检查字幕中是否包含“退下吧”或“拜拜” +- 如果命中,则发送一条 `TTS` 给智能体:“好的拜拜” +- 当后续字幕中真的出现“好的拜拜”时,再发布: + - `VOLC_SERVICE_AI_CONVERSATION_QUIT` + +这个逻辑很有代表性,它说明: + +- 本地逻辑可以直接基于字幕内容快速决策 +- 不必走 function call,也不必重新交给云端理解 + +## 10. Platform:硬件抽象层 + +`platform/README_zh.md` 说明了整体设计原则: + +- 接口正交,尽量屏蔽硬件细节 +- 但为了 AI 对话场景易用性,允许一定“场景定制化” + +核心思想是: + +- 不追求绝对通用 +- 优先服务“AI 对话设备”这个具体场景 + +### 10.1 `volc_hal.h`:全局 HAL 上下文 + +`platform/inc/volc_hal.h` 定义了全局结构 `volc_hal_context_t`,里面集中保存: + +- 按键句柄 +- 音频/视频采集句柄 +- 显示句柄 +- 音频/视频播放器句柄 +- 设备名 + +`volc_hal_init()` 只做必要初始化,不要求把所有外设一次性拉起。 + +### 10.2 `volc_hal.c`:ESP 平台 HAL 根初始化 + +当前 `espressif/volc_hal.c` 会完成: + +- 分配全局 HAL context +- 调用 `basic_board_init()` 完成板级外设探测与初始化 +- 初始化文件系统 +- 根据 MAC 生成设备名 + +其中 `basic_board.c` 又做了: + +- 查询板型 +- 绑定播放设备、录音设备、LCD 设备 +- 配置采样率、位宽、通道数、mic layout +- 打开 codec 设备 + +所以 `volc_hal_init()` 实际上是整个硬件层的入口。 + +## 11. 平台子模块 + +### 11.1 音频采集 `volc_hal_capture` + +头文件: + +- `platform/inc/volc_hal_capture.h` + +实现: + +- `platform/src/espressif/volc_hal_capture.c` + +这个模块的重要特点有两个: + +#### 1. 回调式采集 + +不是业务主动 `read()`,而是内部线程采集后异步回调一帧帧音频数据。 + +这与 README_zh 的设计说明完全一致。 + +#### 2. 支持两种音频模式 + +- `VOLC_AUDIO_MODE_WAKEUP` +- `VOLC_AUDIO_MODE_CAPTURE` + +当前实现里: + +- `WAKEUP` + - 打开带唤醒能力的 AFE/Recorder +- `CAPTURE` + - 打开正常语音采集 + +但底层仍然复用同一套采集模块,只是内部 pipeline 模式不同。 + +这也是 README_zh 中“唤醒和采集属于同一音频采集范畴”的具体落地。 + +### 11.2 音频播放 `volc_hal_player` + +头文件: + +- `platform/inc/volc_hal_player.h` + +实现: + +- `platform/src/espressif/volc_hal_player.c` + +当前重点支持音频播放: + +- 创建时初始化 feeder +- `start()` 启动喂音流程 +- `play_data()` 将数据送入播放链路 +- 支持音量获取和设置 + +业务层基本不需要关心具体解码器或采样参数,因为这些在编译期和平台层已经固化。 + +### 11.3 显示 `volc_hal_display` + +头文件: + +- `platform/inc/volc_hal_display.h` + +实现有两套: + +- `platform/src/espressif/volc_hal_display.c` +- `platform/src/espressif/volc_hal_display_lvgl.c` + +README_zh 提到,显示层不是让业务任意创建区域,而是预先划分屏幕区域。接口中也确实体现了这一点。 + +预定义区域包括: + +- `VOLC_DISPLAY_OBJ_STATUS` +- `VOLC_DISPLAY_OBJ_MAIN` +- `VOLC_DISPLAY_OBJ_SUBTITLE` + +当前主实现 `volc_hal_display.c` 更偏向: + +- 通过 `gfx/mmap_assets` 渲染 EAF 动画表情 +- 叠加状态文字 +- 根据状态切换不同动画 + +这使 UI 能直接表现智能体当前情绪和状态。 + +### 11.4 按键 `volc_hal_button` + +头文件: + +- `platform/inc/volc_hal_button.h` + +实现: + +- `platform/src/espressif/volc_hal_button.c` + +这个模块本质是对底层 `iot_button` 的适配: + +- 统一按钮事件枚举 +- 注册回调 +- 在回调中把底层事件转成 HAL 事件 + +### 11.5 文件 `volc_hal_file` + +头文件: + +- `platform/inc/volc_hal_file.h` + +实现: + +- `platform/src/espressif/volc_hal_file.c` + +这层很薄,基本是 ANSI C 文件接口的简单包装: + +- `open/read/write/seek/close` + +当前主要被 `local_logic_service` 用来播放欢迎音频。 + +## 12. `application` 与 `volc_conv_ai` 的关系 + +这个目录与上层云端对话 SDK 的连接点,主要就在 `service/conv_service/src/volc_conv.c`。 + +对接方式是: + +1. 用 `volc_create()` 创建对话引擎 +2. 用 `volc_start()` 启动 RTC 模式对话 +3. 采集到的音频通过 `volc_send_audio_data()` 上送 +4. 收到的音频通过 `volc_hal_player_play_data()` 播放 +5. 收到的消息按 `subv/tool/info` 分类再转给其他 service +6. 本地业务通过: + - `volc_send_message()` + - `volc_send_text_to_agent()` + 继续和智能体交互 + +所以可以把 `application` 看成: + +- 向下对接 `volc_conv_ai` +- 向上对接硬件和本地业务 + +是一个“设备侧 AI 对话编排层”。 + +## 13. 关键运行链路 + +从实际运行角度,主链路可以总结为: + +### 13.1 待机态 + +1. HAL 初始化 +2. 创建显示层 +3. 启动 AIOS +4. 初始化各个 service +5. 启动 wakeup 模式音频采集 +6. 屏幕提示“请说 hi 乐鑫,启动ai对话” + +### 13.2 唤醒后进入对话 + +1. 唤醒回调发布 `VOLC_SERVICE_AI_CONVERSATION` +2. `volc_manager_service` 转发为 `VOLC_SERVICE_AI_CONVERSATION_START` +3. `conv_service_manager` 创建 `conv_ai_service_task` +4. 采集模块切换到 `CAPTURE` 模式 +5. 启动播放器 +6. 调用 `volc_start()` 建立云端对话 + +### 13.3 对话中 + +1. 本地采集音频并上传 +2. 智能体返回音频并播放 +3. 状态回调刷新 UI +4. 字幕消息更新显示 +5. 字幕触发本地逻辑 +6. 工具调用消息触发 function call 服务 + +### 13.4 对话结束 + +1. 超时、退出指令或其他条件触发结束 +2. `volc_stop()` +3. 停止采集/播放 +4. 恢复 wakeup 模式采集 +5. UI 回到待机提示 + +## 14. 目录级总结 + +### 14.1 `framework/` + +是一个轻量事件驱动状态机框架,负责 service 之间的解耦通信。 + +### 14.2 `platform/` + +是面向 AI 对话设备场景定制的 HAL,不追求绝对通用,但强调对业务简单易用。 + +### 14.3 `service/` + +是具体业务编排层: + +- `volc_manager_service` + - 业务入口与事件分发 +- `conv_service` + - AI 对话主流程 +- `function_call_service` + - 工具调用桥接 +- `local_logic_service` + - 本地规则逻辑 + +### 14.4 `cmake/` + +负责把上述模块连成一个 ESP-IDF component,并依赖底层 `volc_conv_ai` 组件。 + +## 15. 一句话总结 + +`application` 目录本质上是一套“设备侧 AI 对话应用框架”: + +- `framework` 解决模块通信 +- `platform` 解决硬件适配 +- `service` 解决业务编排 +- `conv_service` 作为核心中枢把 `volc_conv_ai`、音频设备、显示设备、本地逻辑和 function call 串起来 + +如果后续继续深入阅读,推荐顺序如下: + +1. `service/inc/volc_service_common.h` +2. `framework/src/aios.c` +3. `service/src/volc_manager_service.c` +4. `service/conv_service/src/volc_conv_service.c` +5. `service/conv_service/src/volc_conv.c` +6. `service/function_call_service/src/volc_function_call_service.c` +7. `service/local_logic_service/src/local_logic_service.c` +8. `platform/src/espressif/volc_hal.c` +9. `platform/src/espressif/volc_hal_capture.c` +10. `platform/src/espressif/volc_hal_player.c` +11. `platform/src/espressif/volc_hal_display.c` diff --git a/firmware/echoear-platform-agent/components/application/cmake/common.cmake b/firmware/echoear-platform-agent/components/application/cmake/common.cmake new file mode 100644 index 0000000..75f488c --- /dev/null +++ b/firmware/echoear-platform-agent/components/application/cmake/common.cmake @@ -0,0 +1,25 @@ +set(VOLC_FRAMEWORK_SRCS "${CMAKE_CURRENT_LIST_DIR}/../framework/src/aios.c" + CACHE INTERNAL "ConversationalAI-Embedded-Kit-2.0 framework src file") + +set(VOLC_FRAMEWORK_INCS "${CMAKE_CURRENT_LIST_DIR}/../framework/inc" + CACHE INTERNAL "ConversationalAI-Embedded-Kit-2.0 framework include dir") + +set(VOLC_PLATFORM_INCS "${CMAKE_CURRENT_LIST_DIR}/../platform/inc" + CACHE INTERNAL "ConversationalAI-Embedded-Kit-2.0 platform include dir") + +set(VOLC_SERVICE_INCS "${CMAKE_CURRENT_LIST_DIR}/../service/inc" + "${CMAKE_CURRENT_LIST_DIR}/../service/conv_service/inc" + "${CMAKE_CURRENT_LIST_DIR}/../service/function_call_service/inc" + "${CMAKE_CURRENT_LIST_DIR}/../service/local_logic_service/inc" + CACHE INTERNAL "ConversationalAI-Embedded-Kit-2.0 service include dir") + +set(VOLC_SERVICE_SRCS "${CMAKE_CURRENT_LIST_DIR}/../service/src/volc_manager_service.c" + "${CMAKE_CURRENT_LIST_DIR}/../service/conv_service/src/volc_conv_service.c" + "${CMAKE_CURRENT_LIST_DIR}/../service/conv_service/src/volc_conv.c" + "${CMAKE_CURRENT_LIST_DIR}/../service/function_call_service/src/volc_function_call_service.c" + "${CMAKE_CURRENT_LIST_DIR}/../service/function_call_service/src/volc_local_function_list.c" + "${CMAKE_CURRENT_LIST_DIR}/../service/local_logic_service/src/local_logic_service.c" + CACHE INTERNAL "ConversationalAI-Embedded-Kit-2.0 service src file") + +set(VOLC_UTIL_INCS "${CMAKE_CURRENT_LIST_DIR}/../utils" + CACHE INTERNAL "ConversationalAI-Embedded-Kit-2.0 util include dir") diff --git a/firmware/echoear-platform-agent/components/application/cmake/espressif.cmake b/firmware/echoear-platform-agent/components/application/cmake/espressif.cmake new file mode 100644 index 0000000..b0b5513 --- /dev/null +++ b/firmware/echoear-platform-agent/components/application/cmake/espressif.cmake @@ -0,0 +1,55 @@ +# Copyright (2025) Beijing Volcano Engine Technology Ltd. +# SPDX-License-Identifier: Apache-2.0 + +# ConversationalAI SDK ESP-IDF component configuration +# Converted from standard CMake to ESP-IDF component format + +# Source files +set(VOLC_ESP_SRCS "${CMAKE_CURRENT_LIST_DIR}/../platform/src/espressif/volc_hal.c" + "${CMAKE_CURRENT_LIST_DIR}/../platform/src/espressif/volc_hal_button.c" + "${CMAKE_CURRENT_LIST_DIR}/../platform/src/espressif/volc_hal_capture.c" + "${CMAKE_CURRENT_LIST_DIR}/../platform/src/espressif/volc_hal_display.c" + "${CMAKE_CURRENT_LIST_DIR}/../platform/src/espressif/volc_hal_file.c" + "${CMAKE_CURRENT_LIST_DIR}/../platform/src/espressif/volc_hal_led.c" + "${CMAKE_CURRENT_LIST_DIR}/../platform/src/espressif/volc_hal_player.c" + "${CMAKE_CURRENT_LIST_DIR}/../platform/src/espressif/basic_board.c" +) +set(VOLC_ESP_DISPLAY_SRCS "${CMAKE_CURRENT_LIST_DIR}/../platform/src/espressif/lvgl_source/echoear_font.c" + "${CMAKE_CURRENT_LIST_DIR}/../platform/src/espressif/lvgl_source/img_app_aiPhone.c" +) +set(VOLC_APP_SRCS ${VOLC_FRAMEWORK_SRCS} + ${VOLC_SERVICE_SRCS} + ${VOLC_ESP_SRCS} + ${VOLC_ESP_DISPLAY_SRCS} +) + +set(VOLC_LVGL_SOURCE_INCS "${CMAKE_CURRENT_LIST_DIR}/../platform/src/espressif/lvgl_source/") +set(VOLC_ASSETS_SOURCE_INCS "${CMAKE_CURRENT_LIST_DIR}/../../../main/") + + +set(VOLC_APP_INCS ${VOLC_FRAMEWORK_INCS} + ${VOLC_PLATFORM_INCS} + ${VOLC_SERVICE_INCS} + ${VOLC_UTIL_INCS} + ${VOLC_LVGL_SOURCE_INCS} + ${VOLC_ASSETS_SOURCE_INCS} +) + +idf_component_register( + SRCS ${VOLC_APP_SRCS} + INCLUDE_DIRS ${VOLC_APP_INCS} + REQUIRES volc_conv_ai + PRIV_REQUIRES lvgl button av_processor esp_board_manager hw_init esp_mmap_assets esp_emote_gfx esp_vocat_base_control +) + +# Compiler flags +target_compile_options(${COMPONENT_LIB} PRIVATE + -w + -Os + -ffunction-sections + -fdata-sections +) + +# Export the library for use by other components +set(VOLC_CONV_APP_LIBRARIES ${COMPONENT_LIB} PARENT_SCOPE) +set(VOLC_CONV_APP_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/../framework/inc PARENT_SCOPE) diff --git a/firmware/echoear-platform-agent/components/application/framework/CMakeLists.txt b/firmware/echoear-platform-agent/components/application/framework/CMakeLists.txt new file mode 100644 index 0000000..ca002ae --- /dev/null +++ b/firmware/echoear-platform-agent/components/application/framework/CMakeLists.txt @@ -0,0 +1,6 @@ +# Copyright (2025) Beijing Volcano Engine Technology Ltd. +# SPDX-License-Identifier: Apache License 2.0 + + +set(FRAMEWORK_SRCS ${CMAKE_CURRENT_LIST_DIR}/src/aios.c) +set(FRAMEWORK_INC ${CMAKE_CURRENT_LIST_DIR}/inc) \ No newline at end of file diff --git a/firmware/echoear-platform-agent/components/application/framework/README.md b/firmware/echoear-platform-agent/components/application/framework/README.md new file mode 100644 index 0000000..7492984 --- /dev/null +++ b/firmware/echoear-platform-agent/components/application/framework/README.md @@ -0,0 +1,5 @@ +# framework + +## brief introduction + +This directory is designed to facilitate communication between different business modules (functional modules). The main communication method is subscribing to (sub) and publishing (pub) events, and the externally provided interface files are located in the `inc` directory. diff --git a/firmware/echoear-platform-agent/components/application/framework/README_zh.md b/firmware/echoear-platform-agent/components/application/framework/README_zh.md new file mode 100644 index 0000000..5f25632 --- /dev/null +++ b/firmware/echoear-platform-agent/components/application/framework/README_zh.md @@ -0,0 +1,5 @@ +# framework + +## 目录简介 + +本目录是为了方便不用业务模块(功能模块)相互间进行通信使用的,主要的方式是sub和pub event事件,对外提供的接口文件在inc。 diff --git a/firmware/echoear-platform-agent/components/application/framework/inc/aios.h b/firmware/echoear-platform-agent/components/application/framework/inc/aios.h new file mode 100644 index 0000000..e5ab45b --- /dev/null +++ b/firmware/echoear-platform-agent/components/application/framework/inc/aios.h @@ -0,0 +1,125 @@ +// Copyright (2025) Beijing Volcano Engine Technology Ltd. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef AIOS_H_ +#define AIOS_H_ + +/* include ------------------------------------------------------------------ */ +#include "aios_config.h" +#include "volc_list.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* default configuration ---------------------------------------------------- */ + +#ifndef AIOS_MAX_ACTORS +#define AIOS_MAX_ACTORS 8 // 默认最多8个Actor +#endif + +#ifndef AIOS_USE_ASSERT +#define AIOS_USE_ASSERT 1 // 默认打开断言 +#endif + + +#include "aios_def.h" + +/* data struct -------------------------------------------------------------- */ + +// 状态返回值的定义 +typedef enum aios_ret { + AIOS_Ret_Null = 0, // 无效值 + AIOS_Ret_Handled, // 已处理,不产生跳转 + AIOS_Ret_NotHandled, // 未处理,不产生跳转 + AIOS_Ret_Tran, // 跳转 +} aios_ret_t; + +// 事件类 +struct aios_event; +typedef void (*aios_event_free_handler)(const void *data); +typedef struct aios_event { + volc_list_head_t node; + uint32_t sub; // 订阅者 + aios_event_id_t id; // 事件主题 + void *data; // 事件数据 + aios_event_free_handler free_handler; // 事件数据释放函数 +} aios_event_t; + +// 数据结构 - 行为树相关 -------------------------------------------------------- +// 状态函数句柄的定义 +struct aios_session; +typedef aios_ret_t (* aios_state_handler)(struct aios_session *const me, aios_event_t const * const e); + +// 状态机类 +typedef struct aios_session { + uint32_t priority : 5; // 优先级,0-31,数值越大优先级越高 + uint32_t enabled : 1; // 是否使能 + uint32_t reserve : 1; // 保留位 + volatile aios_state_handler state; +} aios_session_t; + +// api ------------------------------------------------------------------------- +// 对框架进行初始化,在各状态机初始化之前调用。 +void aios_init(int event_size); +// 启动框架,放在main函数的末尾。 +void aios_run(void); +// 停止框架的运行(不常用) +// 停止框架后,框架会在执行完当前状态机的当前事件后,清空各状态机事件队列,清空事件池, +// 不再执行任何功能,直至框架被再次启动。 +void aios_stop(void); + +// 关于状态机 ----------------------------------------------- +// 状态机初始化函数 +void aios_session_init( aios_session_t * const me, + uint8_t priority, + void const * const parameter); +void aios_session_start(aios_session_t * const me, aios_state_handler state_init); + +aios_ret_t aios_tran(aios_session_t * const me, aios_state_handler state); + +#define AIOS_TRAN(target) aios_tran((aios_session_t * )me, (aios_state_handler)target) +#define AIOS_STATE_CAST(state) ((aios_state_handler)(state)) + +// 关于事件 ------------------------------------------------- +// 事件订阅 +void aios_event_sub(aios_session_t * const me, aios_event_id_t id); +// 事件取消订阅 +void aios_event_unsub(aios_session_t * const me, aios_event_id_t id); +// 事件订阅宏定义 +#define AIOS_EVENT_SUB(_evt) aios_event_sub(&(me->super), _evt) +// 事件取消订阅宏定义 +#define AIOS_EVENT_UNSUB(_evt) aios_event_unsub(&(me->super), _evt) + +// 发布事件(携带数据) +int aios_event_pub(aios_event_id_t id, void *data, aios_event_free_handler free_handler); + +// 发布周期性或延时事件 +void* aios_event_time_pub(aios_event_id_t id, uint32_t timeout_ms, uint32_t period_ms); + +// 取消延时事件或者周期事件的发布 +void aios_event_time_cancel(void** timer); + +/* port --------------------------------------------------------------------- */ +// void* aios_malloc(uint32_t size); +// void aios_free(void* ptr); +// void* aios_mutex_create(void); +// void aios_mutex_lock(void* mutex); +// void aios_mutex_unlock(void* mutex); +// void aios_mutex_destroy(void** mutex); +void aios_port_assert(uint32_t error_id); + +/* hook --------------------------------------------------------------------- */ + +// 结束AIOS Nano的运行的时候,所调用的回调函数。 +void aios_hook_stop(void); + +// 启动AIOS Nano的时候,所调用的回调函数 +void aios_hook_start(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/firmware/echoear-platform-agent/components/application/framework/inc/aios_config.h b/firmware/echoear-platform-agent/components/application/framework/inc/aios_config.h new file mode 100644 index 0000000..e03eeb6 --- /dev/null +++ b/firmware/echoear-platform-agent/components/application/framework/inc/aios_config.h @@ -0,0 +1,15 @@ +// Copyright (2025) Beijing Volcano Engine Technology Ltd. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef AIOS_CONFIG_H__ +#define AIOS_CONFIG_H__ + +/* AIOS General Configuration --------------------------------------- */ +#define AIOS_MCU_TYPE 32 +#define AIOS_MAX_ACTORS 4 +#define AIOS_TEST_PLATFORM 64 +#define AIOS_USE_MAGIC 0 + +/* Assert Configuration ----------------------------------------------------- */ +#define AIOS_USE_ASSERT 1 +#endif diff --git a/firmware/echoear-platform-agent/components/application/framework/inc/aios_def.h b/firmware/echoear-platform-agent/components/application/framework/inc/aios_def.h new file mode 100644 index 0000000..0526c21 --- /dev/null +++ b/firmware/echoear-platform-agent/components/application/framework/inc/aios_def.h @@ -0,0 +1,21 @@ +// Copyright (2025) Beijing Volcano Engine Technology Ltd. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef AIOS_DEF_H__ +#define AIOS_DEF_H__ + +#include "aios_config.h" + +#define AIOS_MAX_SLEEP_MS (1000 * 10) + +typedef enum aios_event_id { + // 系统事件 + Event_Null = 0, + Event_Enter, + Event_Exit, + // 用户事件 + Event_User, +} aios_event_id_t; + +#endif + diff --git a/firmware/echoear-platform-agent/components/application/framework/inc/heap-inl.h b/firmware/echoear-platform-agent/components/application/framework/inc/heap-inl.h new file mode 100644 index 0000000..39ffa76 --- /dev/null +++ b/firmware/echoear-platform-agent/components/application/framework/inc/heap-inl.h @@ -0,0 +1,255 @@ +/* Copyright (c) 2013, Ben Noordhuis + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef UV_SRC_HEAP_H_ +#define UV_SRC_HEAP_H_ + +#include /* NULL */ + +#if defined(__GNUC__) +# define HEAP_EXPORT(declaration) __attribute__((unused)) static declaration +#else +# define HEAP_EXPORT(declaration) static declaration +#endif + +struct heap_node { + struct heap_node* left; + struct heap_node* right; + struct heap_node* parent; +}; + +/* A binary min heap. The usual properties hold: the root is the lowest + * element in the set, the height of the tree is at most log2(nodes) and + * it's always a complete binary tree. + * + * The heap function try hard to detect corrupted tree nodes at the cost + * of a minor reduction in performance. Compile with -DNDEBUG to disable. + */ +struct heap { + struct heap_node* min; + unsigned int nelts; +}; + +/* Return non-zero if a < b. */ +typedef int (*heap_compare_fn)(const struct heap_node* a, + const struct heap_node* b); + +/* Public functions. */ +HEAP_EXPORT(void heap_init(struct heap* heap)); +HEAP_EXPORT(struct heap_node* heap_min(const struct heap* heap)); +HEAP_EXPORT(void heap_insert(struct heap* heap, + struct heap_node* newnode, + heap_compare_fn less_than)); +HEAP_EXPORT(void heap_remove(struct heap* heap, + struct heap_node* node, + heap_compare_fn less_than)); +HEAP_EXPORT(void heap_dequeue(struct heap* heap, heap_compare_fn less_than)); + +/* Implementation follows. */ + +HEAP_EXPORT(void heap_init(struct heap* heap)) { + heap->min = NULL; + heap->nelts = 0; +} + +HEAP_EXPORT(void heap_node_init(struct heap_node* hnode)) { + hnode->left = 0; + hnode->parent = 0; + hnode->right = 0; +} + +HEAP_EXPORT(int heap_node_is_empty(struct heap_node* hnode)) { + return (hnode->left == 0 && hnode->parent == 0 && hnode->right == 0); +} + +HEAP_EXPORT(struct heap_node* heap_min(const struct heap* heap)) { + return heap->min; +} + +/* Swap parent with child. Child moves closer to the root, parent moves away. */ +static void heap_node_swap(struct heap* heap, + struct heap_node* parent, + struct heap_node* child) { + struct heap_node* sibling; + struct heap_node t; + + t = *parent; + *parent = *child; + *child = t; + + parent->parent = child; + if (child->left == child) { + child->left = parent; + sibling = child->right; + } else { + child->right = parent; + sibling = child->left; + } + if (sibling != NULL) + sibling->parent = child; + + if (parent->left != NULL) + parent->left->parent = parent; + if (parent->right != NULL) + parent->right->parent = parent; + + if (child->parent == NULL) + heap->min = child; + else if (child->parent->left == parent) + child->parent->left = child; + else + child->parent->right = child; +} + +HEAP_EXPORT(void heap_insert(struct heap* heap, + struct heap_node* newnode, + heap_compare_fn less_than)) { + struct heap_node** parent; + struct heap_node** child; + unsigned int path; + unsigned int n; + unsigned int k; + + newnode->left = NULL; + newnode->right = NULL; + newnode->parent = NULL; + + /* Calculate the path from the root to the insertion point. This is a min + * heap so we always insert at the left-most free node of the bottom row. + */ + path = 0; + for (k = 0, n = 1 + heap->nelts; n >= 2; k += 1, n /= 2) + path = (path << 1) | (n & 1); + + /* Now traverse the heap using the path we calculated in the previous step. */ + parent = child = &heap->min; + while (k > 0) { + parent = child; + if (path & 1) + child = &(*child)->right; + else + child = &(*child)->left; + path >>= 1; + k -= 1; + } + + /* Insert the new node. */ + newnode->parent = *parent; + *child = newnode; + heap->nelts += 1; + + /* Walk up the tree and check at each node if the heap property holds. + * It's a min heap so parent < child must be true. + */ + while (newnode->parent != NULL && less_than(newnode, newnode->parent)) + heap_node_swap(heap, newnode->parent, newnode); +} + +HEAP_EXPORT(void heap_remove(struct heap* heap, + struct heap_node* node, + heap_compare_fn less_than)) { + struct heap_node* smallest; + struct heap_node** max; + struct heap_node* child; + unsigned int path; + unsigned int k; + unsigned int n; + + if (heap->nelts == 0) + return; + + /* Calculate the path from the min (the root) to the max, the left-most node + * of the bottom row. + */ + path = 0; + for (k = 0, n = heap->nelts; n >= 2; k += 1, n /= 2) + path = (path << 1) | (n & 1); + + /* Now traverse the heap using the path we calculated in the previous step. */ + max = &heap->min; + while (k > 0) { + if (path & 1) + max = &(*max)->right; + else + max = &(*max)->left; + path >>= 1; + k -= 1; + } + + heap->nelts -= 1; + + /* Unlink the max node. */ + child = *max; + *max = NULL; + + if (child == node) { + /* We're removing either the max or the last node in the tree. */ + if (child == heap->min) { + heap->min = NULL; + } + return; + } + + /* Replace the to be deleted node with the max node. */ + child->left = node->left; + child->right = node->right; + child->parent = node->parent; + + if (child->left != NULL) { + child->left->parent = child; + } + + if (child->right != NULL) { + child->right->parent = child; + } + + if (node->parent == NULL) { + heap->min = child; + } else if (node->parent->left == node) { + node->parent->left = child; + } else { + node->parent->right = child; + } + + /* Walk down the subtree and check at each node if the heap property holds. + * It's a min heap so parent < child must be true. If the parent is bigger, + * swap it with the smallest child. + */ + for (;;) { + smallest = child; + if (child->left != NULL && less_than(child->left, smallest)) + smallest = child->left; + if (child->right != NULL && less_than(child->right, smallest)) + smallest = child->right; + if (smallest == child) + break; + heap_node_swap(heap, child, smallest); + } + + /* Walk up the subtree and check that each parent is less than the node + * this is required, because `max` node is not guaranteed to be the + * actual maximum in tree + */ + while (child->parent != NULL && less_than(child, child->parent)) + heap_node_swap(heap, child->parent, child); +} + +HEAP_EXPORT(void heap_dequeue(struct heap* heap, heap_compare_fn less_than)) { + heap_remove(heap, heap->min, less_than); +} + +#undef HEAP_EXPORT + +#endif /* UV_SRC_HEAP_H_ */ diff --git a/firmware/echoear-platform-agent/components/application/framework/inc/volc_list.h b/firmware/echoear-platform-agent/components/application/framework/inc/volc_list.h new file mode 100644 index 0000000..79f33e8 --- /dev/null +++ b/firmware/echoear-platform-agent/components/application/framework/inc/volc_list.h @@ -0,0 +1,79 @@ +// Copyright (2025) Beijing Volcano Engine Technology Ltd. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef __CONV_AI_SRC_UTIL_VOLC_LIST_H__ +#define __CONV_AI_SRC_UTIL_VOLC_LIST_H__ + +// #include + +#ifdef __cplusplus +extern "C" +{ +#endif + + typedef struct volc_list_head_t + { + struct volc_list_head_t *next, *prev; + } volc_list_head_t; + +#define __LIST_ADD(entry, before, after) \ + { \ + volc_list_head_t *new_ = (entry), *prev = (before), *next = (after); \ + (next)->prev = (new_); \ + (new_)->next = (next); \ + (new_)->prev = (prev); \ + (prev)->next = (new_); \ + } +#define volc_list_init(entry) \ + do \ + { \ + (entry)->next = (entry); \ + (entry)->prev = (entry); \ + } while (0) +#define volc_list_add(entry, base) \ + do \ + { \ + __LIST_ADD((entry), (base), (base)->next); \ + } while (0) +#define volc_list_add_after(entry, base) \ + do \ + { \ + __LIST_ADD((entry), (base), (base)->next); \ + } while (0) +#define volc_list_add_before(entry, base) \ + do \ + { \ + __LIST_ADD((entry), (base)->prev, (base)); \ + } while (0) +#define volc_list_add_head(entry, head) volc_list_add_after(entry, head) +#define volc_list_add_tail(entry, head) volc_list_add_before(entry, head) +#define volc_list_del(entry) \ + do \ + { \ + (entry)->prev->next = (entry)->next; \ + (entry)->next->prev = (entry)->prev; \ + (entry)->next = (entry)->prev = (entry); \ + } while (0) +#define volc_list_empty(head) ((head)->next == (head)) +#define volc_list_get_head(head) (volc_list_empty(head) ? (volc_list_head_t *)NULL : (head)->next) +#define volc_list_get_tail(head) (volc_list_empty(head) ? (volc_list_head_t *)NULL : (head)->prev) +#define volc_list_is_head(entry, head) ((entry)->prev == head) +#define volc_list_is_tail(entry, head) ((entry)->next == head) +#define volc_list_entry(ptr, type, member) ((type *)((char *)(ptr) - (unsigned long)(&((type *)0)->member))) +#define volc_list_for_each(h, head) for (h = (head)->next; h != (head); h = h->next) +#define volc_list_for_each_safe(h, n, head) for (h = (head)->next, n = h->next; h != (head); h = n, n = h->next) +#define volc_list_for_each_entry(p, head, type, member) \ + for (p = volc_list_entry((head)->next, type, member); &p->member != (head); \ + p = volc_list_entry(p->member.next, type, member)) +#define volc_list_for_each_entry_safe(p, t, head, type, member) \ + for (p = volc_list_entry((head)->next, type, member), t = volc_list_entry(p->member.next, type, member); \ + &p->member != (head); p = t, t = volc_list_entry(t->member.next, type, member)) +#define volc_list_get_head_entry(head, type, member) \ + (volc_list_empty(head) ? (type *)NULL : volc_list_entry(((head)->next), type, member)) +#define volc_list_get_tail_entry(head, type, member) \ + (volc_list_empty(head) ? (type *)NULL : volc_list_entry(((head)->prev), type, member)) + +#ifdef __cplusplus +} +#endif +#endif /* #define __CONV_AI_SRC_UTIL_VOLC_LIST_H__ */ diff --git a/firmware/echoear-platform-agent/components/application/framework/src/aios.c b/firmware/echoear-platform-agent/components/application/framework/src/aios.c new file mode 100644 index 0000000..120ab96 --- /dev/null +++ b/firmware/echoear-platform-agent/components/application/framework/src/aios.c @@ -0,0 +1,646 @@ +// Copyright (2025) Beijing Volcano Engine Technology Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// include --------------------------------------------------------------------- +#include "../inc/aios.h" +#include +#include +#include "heap-inl.h" + +//socket +#include +#include +#include +#include +// time +#include +#include "volc_osal.h" +#ifdef __cplusplus +extern "C" { +#endif + +/* assert ------------------------------------------------------------------- */ +#if (AIOS_USE_ASSERT != 0) +#define AIOS_ASSERT(test_) do { if (!(test_)) { \ + aios_hook_stop(); \ + aios_port_assert(__LINE__); \ + } } while (0) +#else +#define AIOS_ASSERT(test_) ((void)0) +#endif + +// aios define ------------------------------------------------------------------ + +// **aios** --------------------------------------------------------------------- +enum { + EosRun_OK = 0, + EosRun_NotEnabled, + EosRun_NoEvent, + EosRun_NoActor, + EosRun_NoActorSub, + + // Timer + EosTimer_Empty, + EosTimer_NotTimeout, + EosTimer_ChangeToEmpty, + + EosRunErr_NotInitEnd = -1, + EosRunErr_ActorNotSub = -2, + EosRunErr_MallocFail = -3, + EosRunErr_SubTableNull = -4, + EosRunErr_InvalidEventData = -5, + EosRunErr_HeapMemoryNotEnough = -6, + EosRunErr_TimerRepeated = -7, +}; + +typedef struct timer_item { + struct heap_node node; + aios_event_id_t id; + int64_t timeout_ms; + uint32_t period_ms; +} aios_timer_t; + +typedef struct aios_tag { + uint32_t *sub_table; // event sub table + + uint32_t session_exist; + uint32_t session_enabled; + aios_session_t * sessions[AIOS_MAX_ACTORS]; + + uint32_t sub_general; + void* event_mutex; + volc_list_head_t event_list; + + uint32_t time; + void* timer_mutex; + struct heap timer_heap; + int fd[2]; + + uint8_t enabled : 1; + uint8_t running : 1; + uint8_t init_end : 1; + uint8_t reserved : 5; +} aios_t; + +static aios_t aios; + +// data ------------------------------------------------------------------------ +static const aios_event_t aios_event_table[Event_User] = { + {{ 0 }, 0, Event_Null, 0, NULL}, + {{ 0 }, 0, Event_Enter, 0, NULL}, + {{ 0 }, 0, Event_Exit, 0, NULL}, +}; + +// macro ----------------------------------------------------------------------- +#define HSM_TRIG_(state_, topic_) \ + ((*(state_))(me, &aios_event_table[topic_])) + +// static function ------------------------------------------------------------- +static void aios_session_dispath(aios_session_t * const me, aios_event_t const * const e); + +#ifndef VOLC_OFFSET +#define VOLC_OFFSET(type, member) ((size_t) &((type *)0)->member) +#endif + +#ifndef VOLC_CONTAINER_OF +#define VOLC_CONTAINER_OF(ptr, type, member) \ + ((type *)((char *)(ptr) - VOLC_OFFSET(type, member))) +#endif + +static int __heap_node_less(const struct heap_node* a, const struct heap_node* b) { + aios_timer_t* item_a = VOLC_CONTAINER_OF(a, aios_timer_t, node); + aios_timer_t* item_b = VOLC_CONTAINER_OF(b, aios_timer_t, node); + if (item_a->timeout_ms < item_b->timeout_ms) { + return 1; + } + if (item_a->timeout_ms > item_b->timeout_ms) { + return 0; + } + + return item_a->id > item_b->id; +} + +static int __set_block_mode(int sockfd, int block) { + int flags = fcntl(sockfd, F_GETFL); + if (flags < 0) { + return -1; + } + if (block) { + if ((flags & O_NONBLOCK) != 0) { + flags ^= O_NONBLOCK; + } + } else { + flags |= O_NONBLOCK; + } + if (fcntl(sockfd, F_SETFL, flags) < 0) { + return -1; + } + return 0; +} + +static int __pipe(int fds[2]) { + int ret = 0; + fds[0] = -1; + fds[1] = -1; + int err = 0,opt = 0,write_fd = -1,read_fd = -1; + read_fd = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP); + if (read_fd < 0) { + ret = -1; + goto err_out_label; + } + + struct sockaddr_in servaddr; + uint32_t addr_len = sizeof(servaddr); + memset(&servaddr, 0, sizeof(servaddr)); + servaddr.sin_family = AF_INET; + servaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + servaddr.sin_port = 0; + + if (bind(read_fd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) { + ret = -1; + goto err_out_label; + } + + // get port bind by system + if (getsockname(read_fd, (struct sockaddr*)&servaddr, &addr_len) < 0) { + ret = -1; + goto err_out_label; + } + + if(servaddr.sin_addr.s_addr == htonl(INADDR_ANY)) + servaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + write_fd = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP); + int conn_ret = connect(write_fd, (struct sockaddr*)&servaddr, addr_len); + + __set_block_mode(write_fd, 0); + __set_block_mode(read_fd, 0); + + fds[0] = read_fd; + fds[1] = write_fd; +err_out_label: + if(ret < 0) { + if (read_fd > 0) { + close(read_fd); + } + if (write_fd > 0) { + close(write_fd); + } + } + return ret; +} + +static int __interruptable_init(void) { + if (__pipe(aios.fd) < 0) { + return -1; + } + return 0; +} + +static int __interruptable_deinit(void) { + close(aios.fd[0]); + close(aios.fd[1]); + return 0; +} + +static int __interruptable_sleep(int64_t sleep_ms) { + int ret = 0; + char buf[64] = { 0 }; + fd_set readfds = { 0 }; + struct timeval tv = { 0 }; + if (sleep_ms < 0) { + sleep_ms = AIOS_MAX_SLEEP_MS; + } + tv.tv_sec = sleep_ms / 1000; + tv.tv_usec = (sleep_ms % 1000) * 1000; + FD_ZERO(&readfds); + FD_SET(aios.fd[0], &readfds); + ret = select(aios.fd[0] + 1, &readfds, NULL, NULL, &tv); + if (ret < 0) { + return -1; + } + if (FD_ISSET(aios.fd[0], &readfds)) { + if ((ret = read(aios.fd[0], buf, sizeof(buf))) < 0) { + return -1; + } + } + return 0; +} + +static int __interruptable_break(void) { + char c[1] = { 0x5A}; + return (write(aios.fd[1], c, sizeof(c)) ? 0 : -1); +} + +static int64_t aios_get_time(void) +{ + struct timeval time_crt; + gettimeofday(&time_crt, (void *)0); + int64_t time_crt_ms = time_crt.tv_sec * 1000 + time_crt.tv_usec / 1000; + + return time_crt_ms; +} + +void aios_init(int event_size) +{ + aios.enabled = true; + aios.running = false; + aios.session_exist = 0; + aios.session_enabled = 0; + aios.sub_table = NULL; + + heap_init(&aios.timer_heap); + if (__interruptable_init() < 0) { + printf("aios_init: __interruptable_init fail\n"); + return; + } + + aios.sub_general = 0; + volc_list_init(&aios.event_list); + aios.event_mutex = volc_osal_mutex_create(); + if (NULL == aios.event_mutex) { + printf("aios_init: event_mutex create fail\n"); + return; + } + // printf("event: %p\n", aios.event_mutex); + + aios.init_end = 1; + aios.time = 0; + aios.timer_mutex = volc_osal_mutex_create(); + if (NULL == aios.timer_mutex) { + printf("aios_init: timer_mutex create fail\n"); + return; + } + // printf("timer: %p\n", aios.timer_mutex); + + aios.sub_table = (uint32_t *)volc_osal_malloc(sizeof(uint32_t) * event_size); + if (NULL == aios.sub_table) { + printf("aios_init: sub_table create fail\n"); + return; + } +} + +void aios_deinit(void) +{ + volc_osal_mutex_destroy(&aios.event_mutex); + volc_osal_mutex_destroy(&aios.timer_mutex); + __interruptable_deinit(); +} + +void aios_sub_init(uint32_t *flag_sub, aios_event_id_t topic_max) +{ + aios.sub_table = flag_sub; + for (int i = 0; i < topic_max; i ++) { + aios.sub_table[i] = 0; + } +} + +int64_t aios_evttimer(void) +{ + int64_t system_time = aios_get_time(); + volc_osal_mutex_lock(aios.timer_mutex); + if (aios.timer_heap.nelts == 0) { + volc_osal_mutex_unlock(aios.timer_mutex); + return -1; + } + aios_timer_t* item = VOLC_CONTAINER_OF(heap_min(&aios.timer_heap), aios_timer_t, node); + if (item->timeout_ms > system_time) { + volc_osal_mutex_unlock(aios.timer_mutex); + return item->timeout_ms - system_time; + } + while (item->timeout_ms <= system_time) { + heap_remove(&aios.timer_heap, &item->node, __heap_node_less); + aios_event_pub(item->id, NULL, NULL); + if (item->period_ms == 0) { + continue; + } + item->timeout_ms += item->period_ms; + heap_insert(&aios.timer_heap, &item->node, __heap_node_less); + item = VOLC_CONTAINER_OF(heap_min(&aios.timer_heap), aios_timer_t, node); + } + volc_osal_mutex_unlock(aios.timer_mutex); + return 0; +} + +void *aios_event_get_block(uint8_t priority); +void aios_event_gc(void *data); + +int aios_once(void) +{ + int64_t timeout_ms = -1; + if (aios.init_end == 0) { + return (int)EosRunErr_NotInitEnd; + } + + if (aios.sub_table == NULL) { + return (int)EosRunErr_SubTableNull; + } + + if (aios.enabled == false) { + return (int)EosRun_NotEnabled; + } + + // 检查是否有状态机的注册 + if (aios.session_exist == 0 || aios.session_enabled == 0) { + return (int)EosRun_NoActor; + } + + timeout_ms = aios_evttimer(); + + if (volc_list_empty(&aios.event_list)) { + __interruptable_sleep(timeout_ms); + return (int)EosRun_NoEvent; + } + + // 寻找到优先级最高,且有事件需要处理的Actor + aios_session_t *session = NULL; + uint8_t priority = AIOS_MAX_ACTORS; + for (int i = (int)(AIOS_MAX_ACTORS - 1); i >= 0; i --) { + if ((aios.session_exist & (1 << i)) == 0) + continue; + if ((aios.sub_general & (1 << i)) == 0) + continue; + session = aios.sessions[i]; + priority = i; + break; + } + // 如果没有找到,返回 + if (priority == AIOS_MAX_ACTORS) { + __interruptable_sleep(AIOS_MAX_SLEEP_MS); + return (int)EosRun_NoActorSub; + } + + // 寻找当前Actor的最老的事件 + volc_osal_mutex_lock(aios.event_mutex); + aios_event_t * e = aios_event_get_block(priority); + AIOS_ASSERT(e != NULL); + + volc_osal_mutex_unlock(aios.event_mutex); + + // 对事件进行执行 + if ((aios.sub_table[e->id] & (1 << session->priority)) != 0) + { + // 执行状态的转换 + aios_session_dispath(session, e); + } + else { + __interruptable_sleep(AIOS_MAX_SLEEP_MS); + return (int)EosRunErr_ActorNotSub; + } + // 销毁过期事件与其携带的参数 + volc_osal_mutex_lock(aios.event_mutex); + aios_event_gc(e); + volc_osal_mutex_unlock(aios.event_mutex); + + volc_osal_mutex_lock(aios.timer_mutex); + if (aios.timer_heap.nelts == 0) { + volc_osal_mutex_unlock(aios.timer_mutex); + // __interruptable_sleep(AIOS_MAX_SLEEP_MS); + } else { + aios_timer_t* item = VOLC_CONTAINER_OF(heap_min(&aios.timer_heap), aios_timer_t, node); + volc_osal_mutex_unlock(aios.timer_mutex); + int sleep_ms = item->timeout_ms - aios_get_time(); + __interruptable_sleep(sleep_ms); + } + return (int)EosRun_OK; +} + +void aios_run(void) +{ + aios_hook_start(); + + AIOS_ASSERT(aios.enabled == true); + AIOS_ASSERT(aios.sub_table != 0); + + aios.running = true; + + while (aios.enabled) { + int ret = aios_once(); + AIOS_ASSERT(ret >= 0); + + if (ret == EosRun_NotEnabled) { + break; + } + } +} +void aios_hook_start(void) +{ +} + +void aios_hook_stop(void) +{ +} + +void aios_port_assert(uint32_t error_id) +{ + printf("------------------------------------\n"); + printf("ASSERT >>> Module: EventOS Nano, ErrorId: %d.\n", (int)error_id); + printf("------------------------------------\n"); + + while (1) { + usleep(100000); + } +} + + +void aios_stop(void) +{ + aios.enabled = false; + // TODO: release resource + aios_hook_stop(); +} + +// state machine --------------------------------------------------------------- +void aios_session_init( aios_session_t * const me, + uint8_t priority, + void const * const parameter) +{ + // 框架需要先启动起来 + AIOS_ASSERT(aios.enabled == true); + AIOS_ASSERT(aios.running == false); + AIOS_ASSERT(aios.sub_table != NULL); + // 参数检查 + AIOS_ASSERT(me != (aios_session_t *)0); + AIOS_ASSERT(priority < AIOS_MAX_ACTORS); + + // 防止二次启动 + if (me->enabled == true) + return; + + // 检查优先级的重复注册 + AIOS_ASSERT((aios.session_exist & (1 << priority)) == 0); + + // 注册到框架里 + aios.session_exist |= (1 << priority); + aios.sessions[priority] = me; + // 状态机 + me->priority = priority; + me->state = NULL; +} + +void aios_session_start(aios_session_t * const me, aios_state_handler state_init) +{ + aios_state_handler t; + + me->state = state_init; + me->enabled = true; + aios.session_enabled |= (1 << me->priority); + + // 进入初始状态,执行TRAN动作。这也意味着,进入初始状态,必须无条件执行Tran动作。 + t = me->state; + aios_ret_t ret = t(me, &aios_event_table[Event_Null]); + AIOS_ASSERT(ret == AIOS_Ret_Tran); + ret = me->state(me, &aios_event_table[Event_Enter]); + AIOS_ASSERT(ret != AIOS_Ret_Tran); +} + +// event ----------------------------------------------------------------------- +int aios_event_pub(aios_event_id_t id, void *data, aios_event_free_handler free_handler) +{ + if (aios.init_end == 0) { + return EosRunErr_NotInitEnd; + } + + if (aios.sub_table == NULL) { + return EosRunErr_SubTableNull; + } + + // 保证框架已经运行 + if (aios.enabled == 0) { + return EosRun_NotEnabled; + } + + if (aios.session_exist == 0) { + return EosRun_NoActor; + } + + // 没有状态机使能,返回 + if (aios.session_enabled == 0) { + return EosRun_NotEnabled; + } + // 没有状态机订阅,返回 + if (aios.sub_table[id] == 0) { + return EosRun_NoActorSub; + } + + // 申请事件空间 + aios_event_t *e = (aios_event_t *)volc_osal_malloc(sizeof(aios_event_t)); + if (e == NULL) { + return EosRunErr_MallocFail; + } + e->id = id; + e->sub = aios.sub_table[e->id]; + aios.sub_general |= e->sub; + e->data = data; + e->free_handler = free_handler; + volc_osal_mutex_lock(aios.event_mutex); + volc_list_add_tail(&e->node, &aios.event_list); + volc_osal_mutex_unlock(aios.event_mutex); + __interruptable_break(); + + return EosRun_OK; +} + +void aios_event_sub(aios_session_t * const me, aios_event_id_t id) +{ + aios.sub_table[id] |= (1 << me->priority); +} + +void aios_event_unsub(aios_session_t * const me, aios_event_id_t id) +{ + aios.sub_table[id] &= ~(1 << me->priority); +} + +void* aios_event_time_pub(aios_event_id_t id, uint32_t time_ms, uint32_t period_ms) +{ + aios_timer_t* item = (aios_timer_t *)volc_osal_malloc(sizeof(aios_timer_t)); + AIOS_ASSERT(item != NULL); + memset(item, 0, sizeof(aios_timer_t)); + item->id = id; + item->timeout_ms = aios_get_time() + time_ms; + item->period_ms = period_ms; + volc_osal_mutex_lock(aios.timer_mutex); + heap_insert(&aios.timer_heap, &item->node, __heap_node_less); + volc_osal_mutex_unlock(aios.timer_mutex); + return (void *)item; +} + +void aios_event_time_cancel(void** timer) +{ + if (timer == NULL || NULL == *timer) { + return; + } + aios_timer_t* item = (aios_timer_t *)*timer; + volc_osal_mutex_lock(aios.timer_mutex); + heap_remove(&aios.timer_heap, &item->node, __heap_node_less); + volc_osal_mutex_unlock(aios.timer_mutex); + volc_osal_free(item); + *timer = NULL; +} + +// state tran ------------------------------------------------------------------ +aios_ret_t aios_tran(aios_session_t * const me, aios_state_handler state) +{ + me->state = state; + + return AIOS_Ret_Tran; +} + +// static function ------------------------------------------------------------- +static void aios_session_dispath(aios_session_t * const me, aios_event_t const * const e) +{ + aios_ret_t r; + + AIOS_ASSERT(e != (aios_event_t *)0); + + aios_state_handler s = me->state; + aios_state_handler t; + + r = s(me, e); + if (r == AIOS_Ret_Tran) { + t = me->state; + r = s(me, &aios_event_table[Event_Exit]); + AIOS_ASSERT(r == AIOS_Ret_Handled || r == AIOS_Ret_NotHandled); + r = t(me, &aios_event_table[Event_Enter]); + AIOS_ASSERT(r == AIOS_Ret_Handled || r == AIOS_Ret_NotHandled); + me->state = t; + } + else { + me->state = s; + } +} + +/* heap library ------------------------------------------------------------- */ +void aios_event_gc(void *data) +{ + aios_event_t * e = (aios_event_t *)data; + if (e->sub == 0) { + volc_list_del(&e->node); + if (e->free_handler != NULL) { + e->free_handler(e->data); + } + volc_osal_free(e); + } + aios.sub_general = 0; + volc_list_for_each_entry(e, &aios.event_list, aios_event_t, node) { + aios.sub_general |= e->sub; + } +} + +void *aios_event_get_block(uint8_t priority) +{ + aios_event_t * e = NULL; + volc_list_for_each_entry(e, &aios.event_list, aios_event_t, node) { + if ((e->sub & (1 << priority)) == 0) { + continue; + } + e->sub &=~ (1 << priority); + break; + } + + return (void *)e; +} + +#ifdef __cplusplus +} +#endif diff --git a/firmware/echoear-platform-agent/components/application/platform/README.md b/firmware/echoear-platform-agent/components/application/platform/README.md new file mode 100644 index 0000000..3ca9a1b --- /dev/null +++ b/firmware/echoear-platform-agent/components/application/platform/README.md @@ -0,0 +1,49 @@ +# Platform + +## brief introduction + +This directory serves as the software abstraction layer for the Hardware Conversational AI Agent Development Kit, built around the business characteristic of AI conversation. Its purpose is to shield hardware details, enabling users to focus on software development and business implementation. +Among this directory: + +- inc: Contains header files +- src: Contains specific implementations for different hardware platforms + +## principles + +As a Hardware Abstraction Layer (HAL), we typically expect capabilities to be atomized, interfaces to be orthogonal, and high flexibility to be maintained. However, excessive pursuit of atomization and flexibility often reduces interface usability and impairs business development efficiency. These two aspects are often mutually exclusive. // Nevertheless, this development platform focuses on AI conversation scenarios; therefore, given the relatively fixed business scenarios, we can make trade-offs with greater engineering practical value. // Our core design principles are as follows: + +1. Orthogonal interfaces to reduce coupling and shield hardware details. +2. Interface design takes certain business characteristics into account: + some implementations and configuration parameters are implicitly implemented according to business requirements,and not all software configurable items are retained.This prevents upper-layer business development from being bogged down in endless detailed configuration and parameter tuning. For example: we enable the Acoustic Echo Cancellation (AEC) module by default for audio capture,instead of requiring upper-layer business to configure aec_enabled in the config file;thus, many interface designs at this layer do not make complete sense.For detailed explanations and trade-offs, refer to the implementation details.Interface design takes certain business characteristics into account: ails. + +## implementation + +### volc_hal + +volc_hal_init() Do the necessary actions after the hardware is powered on, it is not necessary to initialize all the hardware modules, but it is necessary to set the device name. + +volc_get_global_hal_context() ithis function could help you get the hal context everywhere. Do not Modify the return value + +### volc_hal_player + +We do not specify specific media configurations (e.g., encoding format, sampling rate, or resolution).This is because we expect all the aforementioned parameters to be uniquely determined and immutable across all scenarios.Therefore, these values must be fixed at compile time; if your device supports multiple parameters,macros should be used to select the desired ones at compile time.This design adheres to Principle 2. + +for volc_hal_player_play_data(),We have not specified whether this function is sync or async. However, it is strongly recommended to implement this function as non-blocking with a certain internal buffer, ensuring that the caller of the function does not need to overly consider the function's call frequency. For example, the function can be called every 1 second to play 1.2 seconds of audio or video data. + +### volc_hal_capture + +We do not specify specific media configurations (e.g., encoding format, sampling rate, or resolution).This is because we expect all the aforementioned parameters to be uniquely determined and immutable across all scenarios.Therefore, these values must be fixed at compile time; if your device supports multiple parameters,macros should be used to select the desired ones at compile time.This design adheres to Principle 2. + +To ensure a uniform rate for media capture data, we adopt the callback function approach: frame-by-frame audio and video data is delivered asynchronously via callbacks, instead of providing an active capture interface. Therefore, during the function implementation, a capture thread needs to be created to asynchronously callback the data out. + +We consider both voice wake-up and voice data capture to fall under the category of audio capture, differing only in mode (internal pipeline). For this reason, we do not redesign a separate module for voice wake-up; instead, we add an additional mode specifically for voice wake-up. + +Finally, note that acoustic echo cancellation (AEC) must be implemented for audio capture. + +### volc_hal_display + +Typically, a set of screen display interfaces allows upper-layer callers to independently create and destroy regions for display purposes.However, to minimize the development effort of upper-layer business logic,we do not allow the business side to create and manage different screen regions on their own.Instead, different screen regions are divided and created during the execution of volc_hal_display_create().The business layer can only obtain different render layer objects,pass data to them, and perform rendering operations. For example, we divide the screen into a status bar(VOLC_DISPLAY_OBJ_STATUS), main screen(VOLC_DISPLAY_OBJ_MAIN), and subtitle display bar(VOLC_DISPLAY_OBJ_SUBTITLE).You may define your own divisions, as all these codes are open-source and modifiable. + +### volc_hal_file + +File systems may have different implementations on different devices,but we generally adhere to the ANSI C style.For devices without a file system, these interfaces may be left unimplemented as this is not mandatory. diff --git a/firmware/echoear-platform-agent/components/application/platform/README_zh.md b/firmware/echoear-platform-agent/components/application/platform/README_zh.md new file mode 100644 index 0000000..1c0a666 --- /dev/null +++ b/firmware/echoear-platform-agent/components/application/platform/README_zh.md @@ -0,0 +1,45 @@ +# Platform + +## 目录简介 + +本目录是硬件对话智能体开发套件基于ai对话这一业务特性,进行的软件抽象层,目的是屏蔽硬件细节,使用户专注软件开发与业务实现。其中inc是header,src是不同硬件平台的具体实现。 + +## 设计原则 + +作为硬件抽象层往往期待,能力原子化,接口正交,且保持高度的灵活性。但是过分追求原子化和灵活性,往往会降低接口易用性,降低业务开发的效率。这两者往往不可兼得。不过本开发平台专注于ai对话场景,因此在业务场景相对固定的情况下,还是能做成一些更有工程实践价值的trade off的。我们设计的基本原则如下: + +1 接口正交,减少耦合且屏蔽硬件细节 + +2 接口设计考虑一定的业务特点,某些实现和配置参数会按照业务要求隐式实现,不会保留所有的软件可配置项,避免上层业务开发陷入无穷无尽的细节配置和调参上。这里举一个例子: 我们默认音频采集都会打开回声消除(aec)模块,而不是让上层业务去config里配置 aec_enabled;因此,这一层不少的接口设计并不百分百的make sense,详细的说明和取舍见实现细节。 + +## 部分实现思路 + +### volc_hal + +volc_hal_init() 只实现硬件上电后必要的初始化动作,例如设置gpid,联网等;音视频采播等模块可以在其他子模块中init + +volc_get_global_hal_context() 方便拿到全局唯一的硬件context,但不要修改该返回值 + +### volc_hal_player + +请注意该模块的接口设计上,我们没有给定一些具体的媒体配置,例如编码格式,采样率或者分辨率;这是因为我们期待上述的所有参数在全场景都是唯一确定且不能改变的。所以这些值需要在编译时就确定好,如果你的设备支持多种参数,那么也应该在编译时用宏做以选择。这一设计遵循原则2; + +对于volc_hal_player_play_data()接口,我们并未规定该函数是同步的还是异步的,但是还是强烈建议你讲该函数实现为非阻塞的,且内部有一定缓存,确保函数的调用者不用过分考虑函数的调用频次;例如,可以1s中调用该函数play 1.2s的audio或video数据 + +### volc_hal_capture + +请注意该模块的接口设计上,我们没有给定一些具体的媒体配置,例如编码格式,采样率或者分辨率;这是因为我们期待上述的所有参数在全场景都是唯一确定且不能改变的。所以这些值需要在编译时就确定好,如果你的设备支持多种参数,那么也应该在编译时用宏做以选择。这一设计遵循原则2; + +为了确保媒体采集数据的速率均匀,我们采取回调函数的方式,异步的回调出一帧一帧的音视频数据,而非主动给一个capture接口。所以需要在函数实现的过程中,创建一个采集线程,异步的将数据回调出来。 + +请注意,我们认为语音唤醒和语音数据采集都属于音频采集的范畴,只是模式(内部pipeline)不同,所以我们并未重新设计一套语音唤醒的模块,而是多设计了一个语音唤醒的mode + +最后,需要注意,音频的采集需要做aec和ns等模块,这一设计遵循原则2; + +### volc_hal_display + +通常情况下一套屏幕显示的接口,会允许上层调用者自行创建和销毁一块用于显示的区域;但是根据原则2,为了最简化上层业务的开发,我们不允许业务侧去自行创建和管理屏幕的不同区域;而是在volc\_hal\_display\_create就将不同的屏幕区域划分且创建好。然后业务层只能拿到不同的渲染层obj,传递数据并进行渲染。例如,我们将屏幕分为状态栏、主屏幕和字幕显示栏。你也可以有自己的划分,因为这些代码都是开源可修改的 + +### volc_hal_file + +不同的设备上文件系统可能有不同的实现,但是我们总体上遵循ANSI C style。对于没有文件系统的设备,这些接口可以不做实现,这并不是必须的 diff --git a/firmware/echoear-platform-agent/components/application/platform/inc/volc_hal.h b/firmware/echoear-platform-agent/components/application/platform/inc/volc_hal.h new file mode 100644 index 0000000..76bfb0c --- /dev/null +++ b/firmware/echoear-platform-agent/components/application/platform/inc/volc_hal.h @@ -0,0 +1,92 @@ +// Copyright (2025) Beijing Volcano Engine Technology Ltd. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef __VOLC_HAL_H__ +#define __VOLC_HAL_H__ + +#include "volc_hal_button.h" +#include "volc_hal_capture.h" +#include "volc_hal_display.h" +#include "volc_hal_player.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define VOLC_DEVICE_NAME_LENGTH 32 +typedef enum { + VOLC_HAL_CAPTURE_AUDIO = 0, // audio capture + VOLC_HAL_CAPTURE_VIDEO, // video capture + VOLC_HAL_CAPTURE_MAX, // Maximum capture count +} volc_hal_capture_e; + +typedef enum { + VOLC_HAL_PLAYER_AUDIO = 0, // audio player + VOLC_HAL_PLAYER_VIDEO, // video player + VOLC_HAL_PLAYER_MAX, // Maximum player count +} volc_hal_player_e; + +/** + * @brief All hardware resources handle and context + * @details generally,this structure should be singleton + * you can get the Instance by volc_get_global_hal_context() + * And, you just set the value when the hardware submodule be inited + * the function volc_hal_init() just do some necessary initialization actions + * such as, malloc the volc_hal_context_t mem and set the device_name + */ +typedef struct { + /** + * @brief the button handle pointer + * @details It should be set value when the volc_hal_button_create() is called + */ + volc_hal_button_t button_handle; + /** + * @brief the capture pointer array, include audio_capture and video_capture + * @details It should be set value when the volc_hal_capture_create() is called + */ + volc_hal_capture_t capture_handle[VOLC_HAL_CAPTURE_MAX]; + /** + * @brief the display pointer + * @details It should be set value when the volc_hal_display_create() is called + */ + volc_hal_display_t display_handle; + /** + * @brief the player pointer array, include audio_player and video_player + * @details It should be set value when the volc_hal_player_create() is called + */ + volc_hal_player_t player_handle[VOLC_HAL_PLAYER_MAX]; + /** + * @brief the device name, in most cases, it is a mac address + * @details It should be set a value when the volc_hal_init() is called + */ + char device_name[VOLC_DEVICE_NAME_LENGTH]; +} volc_hal_context_t; + +/** + * @brief Initialize the HAL module + * @details Do the necessary actions after the hardware is powered on + * it is not necessary to initialize all the hardware modules + * but it is necessary to set the device name + * + * @return int 0 if success, otherwise error code + */ +int volc_hal_init(void); + +/** + * @brief return global hal context + * @details you can get the singleton Instance volc_hal_context_t by volc_get_global_hal_context() + * this function could help you get the hal context everywhere + * please be carefully, Do not Modify the return value + */ +volc_hal_context_t* volc_get_global_hal_context(); + +/** + * @brief Deinitialize the HAL module + */ +void volc_hal_fini(void); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/firmware/echoear-platform-agent/components/application/platform/inc/volc_hal_button.h b/firmware/echoear-platform-agent/components/application/platform/inc/volc_hal_button.h new file mode 100644 index 0000000..1298511 --- /dev/null +++ b/firmware/echoear-platform-agent/components/application/platform/inc/volc_hal_button.h @@ -0,0 +1,59 @@ +// Copyright (2025) Beijing Volcano Engine Technology Ltd. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef __VOLC_HAL_BUTTON_H__ +#define __VOLC_HAL_BUTTON_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void* volc_hal_button_t; + +typedef enum { + VOLC_BUTTON_PRESS_DOWN = 0, // Button press down event + VOLC_BUTTON_PRESS_UP, // Button press up event + VOLC_BUTTON_PRESS_REPEAT, // Button press repeat event + VOLC_BUTTON_PRESS_REPEAT_DONE, // Button press repeat done event + VOLC_BUTTON_SINGLE_CLICK, // Button single click event + VOLC_BUTTON_DOUBLE_CLICK, // Button double click event + VOLC_BUTTON_MULTIPLE_CLICK, // Button multiple click event + VOLC_BUTTON_LONG_PRESS_START, // Button long press start event + VOLC_BUTTON_LONG_PRESS_HOLD, // Button long press hold event + VOLC_BUTTON_LONG_PRESS_UP, // Button long press up event + VOLC_BUTTON_PRESS_END, // Button press end event + VOLC_BUTTON_EVENT_MAX, // Maximum number of button events +} volc_hal_button_event_e; + +typedef void (*volc_hal_button_event_cb_t)(volc_hal_button_t button, volc_hal_button_event_e event, void* user_data); + +typedef struct volc_hal_button_config { + int gpio_num; // GPIO number of the button + int active_level; // Active level of the button (0 or 1) + int long_press_ms; // Long press threshold in milliseconds + int short_press_ms; // Short press threshold in milliseconds + int enable_power_save; // Whether to enable power save mode (0 or 1) + void* user_data; // User data pointer + volc_hal_button_event_cb_t event_cb; // Button event callback function +} volc_hal_button_config_t; + +/** + * @brief Create a button instance + * + * @param config Button configuration + * @return volc_hal_button_t Button instance pointer + */ +volc_hal_button_t volc_hal_button_create(volc_hal_button_config_t* config); + +/** + * @brief Destroy a button instance + * + * @param button Button instance pointer + */ +void volc_hal_button_destroy(volc_hal_button_t button); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/firmware/echoear-platform-agent/components/application/platform/inc/volc_hal_capture.h b/firmware/echoear-platform-agent/components/application/platform/inc/volc_hal_capture.h new file mode 100644 index 0000000..8f065c9 --- /dev/null +++ b/firmware/echoear-platform-agent/components/application/platform/inc/volc_hal_capture.h @@ -0,0 +1,98 @@ +// Copyright (2025) Beijing Volcano Engine Technology Ltd. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef __VOLC_HAL_CAPTURE_H__ +#define __VOLC_HAL_CAPTURE_H__ + +#include "volc_hal_type.h" + +#ifdef __cplusplus +extern "C" { +#endif +/* + * @brief Capture instance pointer + */ +typedef void* volc_hal_capture_t; + +typedef struct { + volc_audio_data_type_e data_type; // Audio data type + void* user_data; // User data pointer +} volc_hal_frame_info_t; + +typedef enum { + VOLC_AUDIO_MODE_WAKEUP = 0, + VOLC_AUDIO_MODE_CAPTURE, +} volc_hal_audio_capture_mode_e; + +typedef void (*volc_hal_capture_data_cb_t)(volc_hal_capture_t capture, const void* data, int len, volc_hal_frame_info_t* frame_info); +typedef void (*volc_hal_audio_wakeup_cb_t)(void *event, void *user_data); + +typedef struct volc_hal_capture_config { + volc_media_type_e media_type; // Media type + volc_hal_capture_data_cb_t data_cb; // Data callback function + volc_hal_audio_wakeup_cb_t audio_wakeup_cb; // audio wakeup cb + void* user_data; // User data pointer +} volc_hal_capture_config_t; + +/** + * @brief Create a capture instance + * @details To ensure a uniform rate for media capture data, we adopt the callback function approach: + * frame-by-frame audio and video data is delivered asynchronously via callbacks, + * instead of providing an active capture interface. + * Therefore, during the function implementation, a capture thread needs to be created + * to asynchronously callback the data out. + * @note We do not specify specific media configurations (e.g., encoding format, sampling rate, or resolution). + * This is because we expect all the aforementioned parameters to be uniquely determined and immutable across all scenarios. + * Therefore, these values must be fixed at compile time; if your device supports multiple parameters, + * macros should be used to select the desired ones at compile time. + * We consider both voice wake-up and voice data capture to fall under the category of audio capture, + * differing only in mode (internal pipeline). + * For this reason, we do not redesign a separate module for voice wake-up; + * instead, we add an additional mode specifically for voice wake-up. + * Finally, note that acoustic echo cancellation (AEC) must be implemented for audio capture. + * + * @param config Capture configuration pointer + * @return volc_hal_capture_t Capture instance pointer + */ +volc_hal_capture_t volc_hal_capture_create(volc_hal_capture_config_t* config); + +/** + * @brief Destroy a capture instance + * + * @param capture Capture instance pointer + */ +void volc_hal_capture_destroy(volc_hal_capture_t capture); + +/** + * @brief Start a capture instance + * + * @param capture Capture instance pointer + * @return int 0 if success, otherwise error code + */ +int volc_hal_capture_start(volc_hal_capture_t capture,volc_hal_audio_capture_mode_e mode); + +/** + * @brief Stop a capture instance + * @details you should start your capture thread within this function. + * + * @param capture Capture instance pointer + * @return int 0 if success, otherwise error code + */ +int volc_hal_capture_stop(volc_hal_capture_t capture); + +/** + * @brief Update audio capture configuration + * @note this function could help you update the capture config + * Please check if it is thread-safe or not; + * + * @param capture Capture instance pointer + * @param config Capture configuration pointer + * @return int 0 if success, otherwise error code + */ +int volc_hal_capture_update_config(volc_hal_capture_t capture, volc_hal_capture_config_t* config); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/firmware/echoear-platform-agent/components/application/platform/inc/volc_hal_display.h b/firmware/echoear-platform-agent/components/application/platform/inc/volc_hal_display.h new file mode 100644 index 0000000..b1db4ba --- /dev/null +++ b/firmware/echoear-platform-agent/components/application/platform/inc/volc_hal_display.h @@ -0,0 +1,85 @@ +// Copyright (2025) Beijing Volcano Engine Technology Ltd. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef __VOLC_HAL_DISPLAY_H__ +#define __VOLC_HAL_DISPLAY_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Display instance pointer + */ +typedef void* volc_hal_display_t; + +/** This enumeration exists to map to distinct regions of the screen. + * Note: Typically, a set of screen display interfaces allows upper-layer callers + * to independently create and destroy regions for display purposes. + * However, to minimize the development effort of upper-layer business logic, + * we do not allow the business side to create and manage different screen regions on their own. + * Instead, different screen regions are divided and created during the execution of volc_hal_display_create(). + * The business layer can only obtain different render layer objects, + * pass data to them, and perform rendering operations. + * For example, we divide the screen into a status bar(VOLC_DISPLAY_OBJ_STATUS), + * main screen(VOLC_DISPLAY_OBJ_MAIN), and subtitle display bar(VOLC_DISPLAY_OBJ_SUBTITLE). + * You may define your own divisions, as all these codes are open-source and modifiable. + */ +typedef enum { + VOLC_DISPLAY_OBJ_STATUS = 0, // Status display object + VOLC_DISPLAY_OBJ_MAIN, + VOLC_DISPLAY_OBJ_SUBTITLE, // Subtitle display object + // VOLC_DISPLAY_OBJ_EMOJI, // Emoji display object + VOLC_DISPLAY_OBJ_MAX, // Maximum display object count +} volc_hal_display_obj_e; + +typedef enum { + VOLC_DISPLAY_TEXT = 0, // Text display type + VOLC_DISPLAY_IMAGE, // Image display type + VOLC_DISPLAY_EMOJI // Emoji display type +} volc_hal_display_type_e; + +typedef struct volc_hal_display_config { + int width; // Width of the display + int height; // Height of the display +} volc_hal_display_config_t; + +/** + * @brief Create a Display instance + * + * @param config Display configuration + * @return volc_hal_display_t Display instance pointer + */ +volc_hal_display_t volc_hal_display_create(volc_hal_display_config_t* config); + +/** + * @brief set brightness + * + * @param display Display instance pointer + * @param brightness the brightness value [0,100] + * @return volc_hal_display_t Display instance pointer + */ +int volc_hal_display_set_brightness(volc_hal_display_t display, int brightness); + +/** + * @brief Destroy a Display instance + * + * @param display Display instance pointer + */ +void volc_hal_display_destroy(volc_hal_display_t display); + +/** + * @brief Set the display emotion + * + * @param display Display instance pointer + * @param type Display type (VOLC_DISPLAY_TEXT, VOLC_DISPLAY_IMAGE, or VOLC_DISPLAY_EMOJI) + * @param content Content to be displayed (string for text, image pointer for image, emoji string for emoji) + * @return int 0 if success, otherwise error code + */ +int volc_hal_display_set_content(volc_hal_display_t display, volc_hal_display_obj_e obj, volc_hal_display_type_e type, const void* content); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/firmware/echoear-platform-agent/components/application/platform/inc/volc_hal_file.h b/firmware/echoear-platform-agent/components/application/platform/inc/volc_hal_file.h new file mode 100644 index 0000000..4efcce8 --- /dev/null +++ b/firmware/echoear-platform-agent/components/application/platform/inc/volc_hal_file.h @@ -0,0 +1,83 @@ +// Copyright (2025) Beijing Volcano Engine Technology Ltd. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef __VOLC_HAL_FILE_H__ +#define __VOLC_HAL_FILE_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * File systems may have different implementations on different devices, + * but we generally adhere to the ANSI C style. + * For devices without a file system, these interfaces may be left unimplemented + * as this is not mandatory. + */ + +/** + * @brief file instance pointer + */ +typedef void* volc_hal_file_t; + +/** + * @brief init the file system + * @return int 0 if success, otherwise error code + */ +int volc_hal_file_system_init(); + +/** + * @brief open a file instance + * @param file_name the file name + * @param mode openmode, just follow ANSI C style(fopen) + * @return volc_hal_file_t file instance pointer + */ +volc_hal_file_t volc_hal_file_open(char* file_name, char* mode); + +/** + * @brief read the data from the file + * @param file the file instance pointer + * @param data the data pointer + * @param size the data size + * @return return the real read data size + */ +uint64_t volc_hal_file_read(volc_hal_file_t file, void *data, uint64_t size); + +/** + * @brief write the data from the file + * @param file the file instance pointer + * @param data the data pointer + * @param size the data size + * @return return the real writed data size + */ +uint64_t volc_hal_file_write(volc_hal_file_t file, void *data, uint64_t size); + +/** + * @brief get the file size + * @param file the file instance pointer + * @return return the file size + */ +uint64_t volc_hal_file_get_size(volc_hal_file_t file); + +/** + * @brief get the file size + * @param file the file instance pointer + * @param offset the file offset (start is 0) + * @return int 0 if success, otherwise error code + */ +int volc_hal_file_seek(volc_hal_file_t file,uint64_t offset); + +/** + * @brief close a file instance + * @param mode openmode, just follow ANSI C style(fopen) + * @return volc_hal_file_t file instance pointer + */ +int volc_hal_file_close(volc_hal_file_t file); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/firmware/echoear-platform-agent/components/application/platform/inc/volc_hal_led.h b/firmware/echoear-platform-agent/components/application/platform/inc/volc_hal_led.h new file mode 100644 index 0000000..06b11e7 --- /dev/null +++ b/firmware/echoear-platform-agent/components/application/platform/inc/volc_hal_led.h @@ -0,0 +1,56 @@ +// Copyright (2025) Beijing Volcano Engine Technology Ltd. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef __VOLC_HAL_LED_H__ +#define __VOLC_HAL_LED_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief LED instance pointer + */ +typedef void* volc_led_t; + +/** + * @brief LED state enumeration + */ +typedef enum { + VOLC_LED_OFF = 0, // LED off state + VOLC_LED_ON, // LED on state +} volc_led_state_e; + +typedef struct volc_led_config { + int gpio_num; // GPIO number of the LED + int active_level; // Active level of the LED (0 or 1) +} volc_led_config_t; + +/** + * @brief Create a LED instance + * + * @param config LED configuration + * @return volc_led_t LED instance pointer + */ +volc_led_t volc_led_create(volc_led_config_t* config); + +/** + * @brief Destroy a LED instance + * + * @param led LED instance pointer + */ +void volc_led_destroy(volc_led_t led); + +/** + * @brief Set the LED state + * + * @param led LED instance pointer + * @param state LED state (VOLC_LED_OFF, VOLC_LED_ON, or VOLC_LED_BLINK) + */ +void volc_led_set_state(volc_led_t led, volc_led_state_e state); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/firmware/echoear-platform-agent/components/application/platform/inc/volc_hal_player.h b/firmware/echoear-platform-agent/components/application/platform/inc/volc_hal_player.h new file mode 100644 index 0000000..5951a97 --- /dev/null +++ b/firmware/echoear-platform-agent/components/application/platform/inc/volc_hal_player.h @@ -0,0 +1,98 @@ +// Copyright (2025) Beijing Volcano Engine Technology Ltd. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef __VOLC_HAL_PLAYER_H__ +#define __VOLC_HAL_PLAYER_H__ + +#include "volc_hal_type.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Player instance pointer + */ +typedef void* volc_hal_player_t; + +typedef struct volc_hal_player_config { + volc_media_type_e media_type; // Media type +} volc_hal_player_config_t; + +/** + * @brief Create a player instance + * @details this function shoudle set the player_handle value in the volc_hal_context_t structure. + * @note We do not specify specific media configurations (e.g., encoding format, sampling rate, or resolution). + * This is because we expect all the aforementioned parameters to be uniquely determined and immutable across all scenarios. + * Therefore, these values must be fixed at compile time; if your device supports multiple parameters, + * macros should be used to select the desired ones at compile time. + * + * @param config Player configuration + * @return volc_hal_player_t Player instance pointer + */ +volc_hal_player_t volc_hal_player_create(volc_hal_player_config_t* config); + +/** + * @brief set audio player volume + * + * @param player Player instance pointer (audio) + * @param volume the volume [0,100] + * @return int 0 if success + */ +int volc_hal_set_audio_player_volume(volc_hal_player_t player, int volume); + +/** + * @brief get audio player volume + * + * @param player Player instance pointer (audio) + * @return the volume + */ +int volc_hal_get_audio_player_volume(volc_hal_player_t player); + +/** + * @brief Destroy a player instance + * @details this function shoudle set the player_handle to NULL in the volc_hal_context_t structure. + * + * @param player Player instance pointer + */ +void volc_hal_player_destroy(volc_hal_player_t player); + +/** + * @brief Start a player instance + * + * @param player Player instance pointer + * @return int 0 if success, otherwise error code + */ +int volc_hal_player_start(volc_hal_player_t player); + +/** + * @brief Stop a player instance + * + * @param player Player instance pointer + * @return int 0 if success, otherwise error code + */ +int volc_hal_player_stop(volc_hal_player_t player); + +/** + * @brief Play data with a player instance + * @note We have not specified whether this function is sync or async. + * However, it is strongly recommended to implement this function as non-blocking + * with a certain internal buffer, ensuring that the caller of the function + * does not need to overly consider the function's call frequency. + * For example, the function can be called every 1 second to play 1.2 seconds of audio or video data. + * + * @param player Player instance pointer + * @param data Data to play + * @param len Data length + * @return int 0 if success, otherwise error code + */ +int volc_hal_player_play_data(volc_hal_player_t player, const void* data, int len); +void volc_hal_player_discard_pending(volc_hal_player_t player); +/* Allow a fully received short response to start without filling the prebuffer. */ +void volc_hal_player_finish_segment(volc_hal_player_t player); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/firmware/echoear-platform-agent/components/application/platform/inc/volc_hal_type.h b/firmware/echoear-platform-agent/components/application/platform/inc/volc_hal_type.h new file mode 100644 index 0000000..e5d2385 --- /dev/null +++ b/firmware/echoear-platform-agent/components/application/platform/inc/volc_hal_type.h @@ -0,0 +1,26 @@ +// Copyright (2025) Beijing Volcano Engine Technology Ltd. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef __VOLC_HAL_TYPE_H__ +#define __VOLC_HAL_TYPE_H__ + +#include "volc_conv_ai.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Media type enumeration + */ +typedef enum volc_media_type { + VOLC_MEDIA_TYPE_AUDIO = 0, // Audio media type + VOLC_MEDIA_TYPE_VIDEO, // Video media type + VOLC_MEDIA_TYPE_MAX, // Maximum media type count +} volc_media_type_e; + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/firmware/echoear-platform-agent/components/application/platform/src/espressif/basic_board.c b/firmware/echoear-platform-agent/components/application/platform/src/espressif/basic_board.c new file mode 100644 index 0000000..7666bac --- /dev/null +++ b/firmware/echoear-platform-agent/components/application/platform/src/espressif/basic_board.c @@ -0,0 +1,225 @@ +/** + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#include +#include +#include + +#include "esp_log.h" +#include "esp_err.h" + +#include "esp_board_manager.h" +#include "dev_audio_codec.h" +#include "dev_display_lcd_spi.h" + +#include "basic_board.h" + +static const char *TAG = "BASIC_BOARD"; + +typedef struct { + const char *name; + const char *mic_layout; + uint32_t sample_rate; + uint8_t sample_bits; + uint8_t channels; +} board_config_t; + +static const board_config_t supported_boards[] = { + {"echoear_core_board_v1_2", "RMNM", 16000, 32, 2}, + {"esp32_s3_korvo2_v3", "RMNM", 16000, 32, 2}, + {"esp_vocat_board_v1_2", "RMNM", 16000, 32, 2}, + {"esp_box_3", "RMNM", 16000, 32, 2}, + {"ESP32_S3_Korvo_2L", "MR", 16000, 16, 2}, +}; + +static const size_t supported_boards_count = sizeof(supported_boards) / sizeof(supported_boards[0]); + +#define DEFAULT_PLAY_VOLUME 30 +#define DEFAULT_REC_GAIN 32.0 +#define DEFAULT_REC_CHANNEL_GAIN 32.0 +#define REC_CHANNEL_MASK BIT(2) + +static inline bool is_board_name(const char *board_name, const char *target_name) +{ + ESP_LOGI(TAG, "board_name: %s, target_name: %s", board_name, target_name); + return strcmp(board_name, target_name) == 0; +} + +static dev_audio_codec_handles_t *play_dev_handles2 = NULL; +static dev_audio_codec_handles_t *rec_dev_handles2 = NULL; + +static esp_err_t setup_board_devices(basic_board_periph_t *periph) +{ + esp_err_t ret = ESP_OK; + dev_audio_codec_handles_t *play_dev_handles = NULL; + dev_audio_codec_handles_t *rec_dev_handles = NULL; + void *lcd_handle = NULL; + + esp_board_manager_init(); + // esp_board_manager_print(); + + ret = esp_board_device_init_all(); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to initialize board devices: %s", esp_err_to_name(ret)); + return ret; + } + + esp_board_device_show("fs_sdcard"); + + ret = esp_board_manager_get_device_handle("audio_dac", (void **)&play_dev_handles); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to get audio_dac handle: %s", esp_err_to_name(ret)); + return ret; + } + + ret = esp_board_manager_get_device_handle("audio_adc", (void **)&rec_dev_handles); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to get audio_adc handle: %s", esp_err_to_name(ret)); + return ret; + } + + periph->play_dev = play_dev_handles->codec_dev; + periph->rec_dev = rec_dev_handles->codec_dev; + play_dev_handles2 = play_dev_handles; + rec_dev_handles2 = rec_dev_handles; + ret = esp_board_manager_get_device_handle("display_lcd", &lcd_handle); + if (ret == ESP_OK && lcd_handle) { + dev_display_lcd_spi_handles_t *lcd_handles = (dev_display_lcd_spi_handles_t *)lcd_handle; + periph->lcd_pannel = lcd_handles->panel_handle; + ESP_LOGI(TAG, "LCD panel handle: %p", periph->lcd_pannel); + } else { + ESP_LOGW(TAG, "Failed to get LCD device handle: %s", esp_err_to_name(ret)); + } + + if (periph->play_dev) { + ret = esp_codec_dev_set_out_vol(periph->play_dev, DEFAULT_PLAY_VOLUME); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to set playback volume: %s", esp_err_to_name(ret)); + return ret; + } + ESP_LOGI(TAG, "Playback device configured with volume: %d", DEFAULT_PLAY_VOLUME); + } + + if (periph->rec_dev) { + ret = esp_codec_dev_set_in_gain(periph->rec_dev, DEFAULT_REC_GAIN); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to set recording gain: %s", esp_err_to_name(ret)); + return ret; + } + + // ret = esp_codec_dev_set_in_channel_gain(periph->rec_dev, REC_CHANNEL_MASK, DEFAULT_REC_CHANNEL_GAIN); + // if (ret != ESP_OK) { + // ESP_LOGE(TAG, "Failed to set recording channel gain: %s", esp_err_to_name(ret)); + // // return ret; + // } + ESP_LOGI(TAG, "Recording device configured with gain: %.1f", DEFAULT_REC_GAIN); + } + + + + return ESP_OK; +} + +static esp_err_t configure_board_settings(basic_board_periph_t *periph) +{ + const board_config_t *config = NULL; + + for (size_t i = 0; i < supported_boards_count; i++) { + ESP_LOGI(TAG, "g_esp_board_info.name: %s, supported_boards[i].name: %s", g_esp_board_info.name, supported_boards[i].name); + if (is_board_name(g_esp_board_info.name, supported_boards[i].name)) { + config = &supported_boards[i]; + break; + } + } + + if (config == NULL) { + ESP_LOGE(TAG, "Unsupported board: %s", g_esp_board_info.name); + ESP_LOGE(TAG, "Supported boards:"); + for (size_t i = 0; i < supported_boards_count; i++) { + ESP_LOGE(TAG, " - %s", supported_boards[i].name); + } + return ESP_ERR_NOT_SUPPORTED; + } + + strcpy(periph->mic_layout, config->mic_layout); + periph->sample_rate = config->sample_rate; + periph->sample_bits = config->sample_bits; + periph->channels = config->channels; + + ESP_LOGI(TAG, "Board: %s", g_esp_board_info.name); + ESP_LOGI(TAG, "Mic layout: %s", periph->mic_layout); + ESP_LOGI(TAG, "Sample rate: %d Hz", periph->sample_rate); + ESP_LOGI(TAG, "Sample bits: %d", periph->sample_bits); + ESP_LOGI(TAG, "Channels: %d", periph->channels); + + return ESP_OK; +} + +esp_err_t basic_board_init(basic_board_periph_t *periph) +{ + esp_err_t ret = ESP_OK; + + if (periph == NULL) { + ESP_LOGE(TAG, "Invalid parameter: periph is NULL"); + return ESP_ERR_INVALID_ARG; + } + memset(periph, 0, sizeof(basic_board_periph_t)); + + ret = setup_board_devices(periph); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to setup board devices"); + return ret; + } + ret = configure_board_settings(periph); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to configure board settings"); + return ret; + } + ESP_LOGI(TAG, "Basic board initialization completed successfully"); + + esp_codec_dev_sample_info_t fs = { + .sample_rate = periph->sample_rate, + .channel = periph->channels, + .bits_per_sample = periph->sample_bits, + }; + + ESP_LOGI(TAG, "sample_rate: %d, channel: %d, bits: %d", fs.sample_rate, fs.channel, fs.bits_per_sample); + + if (periph->play_dev) { + ret = esp_codec_dev_open(periph->play_dev, &fs); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to open audio dac <%p>", periph->play_dev); + return ret; + } + } + if (periph->rec_dev) { + ret = esp_codec_dev_open(periph->rec_dev, &fs); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to open audio_adc <%p>", periph->rec_dev); + return ret; + } + } + + return ESP_OK; +} diff --git a/firmware/echoear-platform-agent/components/application/platform/src/espressif/basic_board.h b/firmware/echoear-platform-agent/components/application/platform/src/espressif/basic_board.h new file mode 100644 index 0000000..aedfad8 --- /dev/null +++ b/firmware/echoear-platform-agent/components/application/platform/src/espressif/basic_board.h @@ -0,0 +1,44 @@ +/** + * ESPRESSIF MIT License + * + * Copyright (c) 2025 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ +typedef struct { + void *play_dev; + void *rec_dev; + void *lcd_pannel; + int sample_rate; + char sample_bits; + char channels; + char mic_layout[8]; +} basic_board_periph_t; + +int basic_board_init(basic_board_periph_t *periph); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ \ No newline at end of file diff --git a/firmware/echoear-platform-agent/components/application/platform/src/espressif/lvgl_source/echoear_font.c b/firmware/echoear-platform-agent/components/application/platform/src/espressif/lvgl_source/echoear_font.c new file mode 100644 index 0000000..10cd284 --- /dev/null +++ b/firmware/echoear-platform-agent/components/application/platform/src/espressif/lvgl_source/echoear_font.c @@ -0,0 +1,50219 @@ +/******************************************************************************* + * Size: 16 px + * Bpp: 1 + * Opts: --bpp 1 --size 16 --no-compress --stride 1 --align 1 --font 方正兰亭纤黑.TTF --range 32-126,19968-40869,12288-12544,65280-65312 --format lvgl -o zh_font.c + ******************************************************************************/ + +#ifdef __has_include + #if __has_include("lvgl.h") + #ifndef LV_LVGL_H_INCLUDE_SIMPLE + #define LV_LVGL_H_INCLUDE_SIMPLE + #endif + #endif +#endif + +#ifdef LV_LVGL_H_INCLUDE_SIMPLE + #include "lvgl.h" +#else + #include "lvgl/lvgl.h" +#endif + + + +#ifndef ZH_FONT +#define ZH_FONT 1 +#endif + +#if 1 + +/*----------------- + * BITMAPS + *----------------*/ + +/*Store the image of the glyphs*/ +static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = { + /* U+0020 " " */ + 0x0, + + /* U+0021 "!" */ + 0xff, 0xc8, + + /* U+0022 "\"" */ + 0xb6, 0xd0, + + /* U+0023 "#" */ + 0x9, 0x2, 0x40, 0x90, 0x44, 0x7f, 0x84, 0x81, + 0x20, 0x88, 0xff, 0x89, 0x2, 0x40, 0x90, 0x44, + 0x0, + + /* U+0024 "$" */ + 0x8, 0x4, 0xf, 0x89, 0x68, 0x94, 0x4a, 0x20, + 0xd0, 0x1e, 0x4, 0x82, 0x71, 0x38, 0x9a, 0x48, + 0xf8, 0x10, 0x8, 0x0, + + /* U+0025 "%" */ + 0x0, 0x3, 0x82, 0x22, 0x21, 0x11, 0x8, 0x90, + 0x44, 0x81, 0xc8, 0x0, 0x4e, 0x4, 0x98, 0x24, + 0x42, 0x22, 0x11, 0x11, 0x8, 0x88, 0x38, + + /* U+0026 "&" */ + 0x3c, 0xc, 0x41, 0x8, 0x21, 0x2, 0x20, 0x78, + 0xa, 0x12, 0x22, 0x82, 0x50, 0x32, 0x2, 0x61, + 0xa7, 0xc2, + + /* U+0027 "'" */ + 0xf0, + + /* U+0028 "(" */ + 0x12, 0x64, 0x48, 0x88, 0x88, 0x88, 0x44, 0x42, + 0x10, + + /* U+0029 ")" */ + 0x84, 0x62, 0x21, 0x11, 0x11, 0x11, 0x22, 0x64, + 0x80, + + /* U+002A "*" */ + 0x20, 0x8f, 0xc8, 0x50, 0x20, + + /* U+002B "+" */ + 0x8, 0x4, 0x2, 0x1, 0xf, 0xf8, 0x40, 0x20, + 0x10, 0x8, 0x0, + + /* U+002C "," */ + 0xd6, + + /* U+002D "-" */ + 0xfe, + + /* U+002E "." */ + 0x80, + + /* U+002F "/" */ + 0x8, 0x84, 0x22, 0x10, 0x88, 0x42, 0x11, 0x8, + 0x0, + + /* U+0030 "0" */ + 0x3c, 0x46, 0x42, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x42, 0x46, 0x3c, + + /* U+0031 "1" */ + 0x8, 0xce, 0x98, 0x84, 0x21, 0x8, 0x42, 0x10, + 0x80, + + /* U+0032 "2" */ + 0x3e, 0x43, 0xc1, 0x1, 0x1, 0x2, 0x6, 0x8, + 0x10, 0x20, 0x40, 0x80, 0xff, + + /* U+0033 "3" */ + 0x3e, 0x63, 0x41, 0x1, 0x1, 0x2, 0x1c, 0x3, + 0x1, 0x1, 0xc1, 0x43, 0x3e, + + /* U+0034 "4" */ + 0x2, 0x3, 0x2, 0x81, 0x41, 0x21, 0x10, 0x88, + 0x84, 0x82, 0x7f, 0xc0, 0x80, 0x40, 0x20, + + /* U+0035 "5" */ + 0x7e, 0x40, 0x40, 0x80, 0xbc, 0xc2, 0x81, 0x1, + 0x1, 0x81, 0x81, 0x42, 0x3c, + + /* U+0036 "6" */ + 0x3c, 0x62, 0x41, 0x80, 0x9c, 0xe2, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x42, 0x3c, + + /* U+0037 "7" */ + 0xff, 0x2, 0x2, 0x4, 0x4, 0x8, 0x8, 0x10, + 0x10, 0x10, 0x20, 0x20, 0x20, + + /* U+0038 "8" */ + 0x3e, 0x42, 0x41, 0x41, 0x41, 0x22, 0x3c, 0x42, + 0x81, 0x81, 0x81, 0xc3, 0x3c, + + /* U+0039 "9" */ + 0x3c, 0x42, 0x82, 0x81, 0x81, 0x81, 0x81, 0x43, + 0x3d, 0x1, 0x82, 0x46, 0x3c, + + /* U+003A ":" */ + 0x80, 0x40, + + /* U+003B ";" */ + 0xf0, 0x0, 0xd6, + + /* U+003C "<" */ + 0x0, 0x80, 0xc1, 0x81, 0x3, 0x6, 0x2, 0x0, + 0xc0, 0x18, 0x2, 0x0, 0xc0, 0x10, + + /* U+003D "=" */ + 0xff, 0x0, 0x0, 0x0, 0xff, + + /* U+003E ">" */ + 0x0, 0x60, 0x8, 0x3, 0x0, 0x40, 0x18, 0x6, + 0x4, 0xc, 0x18, 0x10, 0x10, 0x0, + + /* U+003F "?" */ + 0x3c, 0x42, 0x83, 0x81, 0x3, 0x2, 0xc, 0x18, + 0x10, 0x10, 0x0, 0x0, 0x10, + + /* U+0040 "@" */ + 0x7, 0xf0, 0x1c, 0xc, 0x30, 0x6, 0x43, 0xd2, + 0x44, 0x31, 0x88, 0x31, 0x88, 0x21, 0x88, 0x21, + 0x88, 0x21, 0x88, 0x23, 0x8c, 0x66, 0x47, 0x9c, + 0x40, 0x0, 0x20, 0x2, 0x18, 0xc, 0x7, 0xf0, + + /* U+0041 "A" */ + 0x6, 0x0, 0x60, 0xa, 0x0, 0x90, 0x11, 0x1, + 0x8, 0x10, 0x82, 0x8, 0x3f, 0xc6, 0x4, 0x40, + 0x24, 0x2, 0x80, 0x30, + + /* U+0042 "B" */ + 0xfe, 0x40, 0xa0, 0x30, 0x18, 0xc, 0xb, 0xf9, + 0x2, 0x80, 0xc0, 0x60, 0x30, 0x3f, 0xf0, + + /* U+0043 "C" */ + 0x1f, 0x6, 0x11, 0x1, 0x60, 0x38, 0x1, 0x0, + 0x20, 0x4, 0x0, 0x80, 0x18, 0xd, 0x1, 0x30, + 0x61, 0xf0, + + /* U+0044 "D" */ + 0xfe, 0x20, 0x68, 0xa, 0x1, 0x80, 0x60, 0x18, + 0x6, 0x1, 0x80, 0x60, 0x38, 0xa, 0x4, 0xfe, + 0x0, + + /* U+0045 "E" */ + 0xff, 0xc0, 0x20, 0x10, 0x8, 0x4, 0x3, 0xff, + 0x0, 0x80, 0x40, 0x20, 0x10, 0xf, 0xf8, + + /* U+0046 "F" */ + 0xff, 0xc0, 0x20, 0x10, 0x8, 0x4, 0x3, 0xfd, + 0x0, 0x80, 0x40, 0x20, 0x10, 0x8, 0x0, + + /* U+0047 "G" */ + 0x1f, 0x4, 0x19, 0x1, 0xc0, 0x18, 0x1, 0x0, + 0x20, 0xfc, 0x1, 0x80, 0x38, 0x5, 0x0, 0x98, + 0x21, 0xf8, + + /* U+0048 "H" */ + 0x80, 0x60, 0x18, 0x6, 0x1, 0x80, 0x60, 0x1f, + 0xfe, 0x1, 0x80, 0x60, 0x18, 0x6, 0x1, 0x80, + 0x40, + + /* U+0049 "I" */ + 0xff, 0xf8, + + /* U+004A "J" */ + 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x81, 0x3, + 0x6, 0xe, 0x27, 0x80, + + /* U+004B "K" */ + 0x80, 0xe0, 0x68, 0x32, 0x18, 0x8c, 0x26, 0xb, + 0x83, 0x90, 0xc2, 0x20, 0x48, 0x12, 0x2, 0x80, + 0x40, + + /* U+004C "L" */ + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0xff, + + /* U+004D "M" */ + 0xc0, 0x3c, 0x3, 0xa0, 0x5a, 0x5, 0xa0, 0x59, + 0x9, 0x90, 0x99, 0x11, 0x89, 0x18, 0x91, 0x8a, + 0x18, 0x61, 0x86, 0x10, + + /* U+004E "N" */ + 0xc0, 0x70, 0x1a, 0x6, 0x41, 0x90, 0x62, 0x18, + 0x86, 0x11, 0x82, 0x60, 0x98, 0x16, 0x5, 0x80, + 0xc0, + + /* U+004F "O" */ + 0x1f, 0x83, 0xc, 0x40, 0x2c, 0x2, 0x80, 0x18, + 0x1, 0x80, 0x18, 0x1, 0x80, 0x1c, 0x3, 0x40, + 0x22, 0xc, 0x1f, 0x80, + + /* U+0050 "P" */ + 0xff, 0x20, 0x28, 0x6, 0x1, 0x80, 0x60, 0x18, + 0xb, 0xfc, 0x80, 0x20, 0x8, 0x2, 0x0, 0x80, + 0x0, + + /* U+0051 "Q" */ + 0x1f, 0x3, 0xc, 0x40, 0x2c, 0x2, 0x80, 0x18, + 0x1, 0x80, 0x18, 0x1, 0x80, 0x1c, 0x3, 0x40, + 0x22, 0xc, 0x1f, 0x80, 0x10, 0x0, 0x80, 0x4, + + /* U+0052 "R" */ + 0xff, 0x10, 0x12, 0x1, 0x40, 0x28, 0x5, 0x1, + 0xbf, 0xe4, 0x30, 0x82, 0x10, 0x22, 0x2, 0x40, + 0x28, 0x6, + + /* U+0053 "S" */ + 0x1f, 0xc, 0x11, 0x1, 0x20, 0x24, 0x0, 0x60, + 0x3, 0xc0, 0x6, 0x0, 0x48, 0x9, 0x1, 0x10, + 0x61, 0xf8, + + /* U+0054 "T" */ + 0xff, 0xe0, 0x80, 0x10, 0x2, 0x0, 0x40, 0x8, + 0x1, 0x0, 0x20, 0x4, 0x0, 0x80, 0x10, 0x2, + 0x0, 0x40, + + /* U+0055 "U" */ + 0x80, 0x60, 0x18, 0x6, 0x1, 0x80, 0x60, 0x18, + 0x6, 0x1, 0x80, 0x60, 0x14, 0x9, 0x86, 0x3e, + 0x0, + + /* U+0056 "V" */ + 0x80, 0x24, 0x2, 0x40, 0x22, 0x4, 0x20, 0x42, + 0x8, 0x10, 0x81, 0x8, 0x11, 0x0, 0x90, 0xa, + 0x0, 0x60, 0x6, 0x0, + + /* U+0057 "W" */ + 0x81, 0x81, 0x81, 0x82, 0x82, 0x82, 0x42, 0x82, + 0x42, 0x42, 0x42, 0x44, 0x44, 0x44, 0x24, 0x44, + 0x24, 0x28, 0x28, 0x28, 0x28, 0x28, 0x18, 0x28, + 0x10, 0x10, + + /* U+0058 "X" */ + 0x40, 0x22, 0x4, 0x10, 0x81, 0x8, 0x9, 0x0, + 0x60, 0x6, 0x0, 0xa0, 0x9, 0x1, 0x8, 0x20, + 0x44, 0x4, 0x40, 0x20, + + /* U+0059 "Y" */ + 0x80, 0x28, 0x8, 0x83, 0x10, 0x41, 0x10, 0x14, + 0x2, 0x80, 0x20, 0x4, 0x0, 0x80, 0x10, 0x2, + 0x0, 0x40, + + /* U+005A "Z" */ + 0x7f, 0xc0, 0x20, 0x10, 0xc, 0x2, 0x1, 0x0, + 0x80, 0x60, 0x10, 0x8, 0x4, 0x1, 0x0, 0xff, + 0xc0, + + /* U+005B "[" */ + 0xf8, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x8f, + + /* U+005C "\\" */ + 0x84, 0x10, 0x84, 0x20, 0x84, 0x20, 0x84, 0x20, + 0x80, + + /* U+005D "]" */ + 0xf1, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x1f, + + /* U+005E "^" */ + 0x8, 0x18, 0x24, 0x24, 0x22, 0x42, 0x41, + + /* U+005F "_" */ + 0xff, 0xc0, + + /* U+0060 "`" */ + 0x88, 0x80, + + /* U+0061 "a" */ + 0x3e, 0x43, 0x81, 0x1, 0xf, 0x71, 0x81, 0x81, + 0x83, 0x7d, + + /* U+0062 "b" */ + 0x80, 0x80, 0x80, 0xbc, 0xc2, 0xc1, 0x81, 0x81, + 0x81, 0x81, 0x81, 0xc2, 0xbc, + + /* U+0063 "c" */ + 0x3c, 0x42, 0x81, 0x80, 0x80, 0x80, 0x80, 0x81, + 0x42, 0x3c, + + /* U+0064 "d" */ + 0x1, 0x1, 0x1, 0x3d, 0x47, 0x83, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x43, 0x3d, + + /* U+0065 "e" */ + 0x3c, 0x42, 0x81, 0x81, 0x81, 0xff, 0x80, 0x81, + 0x43, 0x3c, + + /* U+0066 "f" */ + 0x1c, 0x82, 0x3e, 0x20, 0x82, 0x8, 0x20, 0x82, + 0x8, 0x20, + + /* U+0067 "g" */ + 0x3d, 0x43, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x43, 0x3d, 0x1, 0x82, 0x7c, + + /* U+0068 "h" */ + 0x81, 0x2, 0x5, 0xec, 0x70, 0x60, 0xc1, 0x83, + 0x6, 0xc, 0x18, 0x20, + + /* U+0069 "i" */ + 0x9f, 0xf8, + + /* U+006A "j" */ + 0x24, 0x12, 0x49, 0x24, 0x92, 0x4e, + + /* U+006B "k" */ + 0x80, 0x80, 0x80, 0x82, 0x84, 0x88, 0x90, 0xb0, + 0xc8, 0x88, 0x84, 0x82, 0x82, + + /* U+006C "l" */ + 0xff, 0xf8, + + /* U+006D "m" */ + 0xbc, 0xf6, 0x38, 0xe0, 0x83, 0x4, 0x18, 0x20, + 0xc1, 0x6, 0x8, 0x30, 0x41, 0x82, 0xc, 0x10, + 0x40, + + /* U+006E "n" */ + 0xbd, 0x8e, 0xc, 0x18, 0x30, 0x60, 0xc1, 0x83, + 0x4, + + /* U+006F "o" */ + 0x3c, 0x42, 0x83, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x42, 0x3c, + + /* U+0070 "p" */ + 0xbc, 0xc2, 0x83, 0x81, 0x81, 0x81, 0x81, 0x81, + 0xc2, 0xbc, 0x80, 0x80, 0x80, + + /* U+0071 "q" */ + 0x3d, 0x43, 0xc1, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x43, 0x3d, 0x1, 0x1, 0x1, + + /* U+0072 "r" */ + 0xbe, 0x21, 0x8, 0x42, 0x10, 0x84, 0x0, + + /* U+0073 "s" */ + 0x7c, 0xc6, 0x82, 0x80, 0x60, 0x1c, 0x2, 0x82, + 0xc2, 0x3c, + + /* U+0074 "t" */ + 0x44, 0x4f, 0x44, 0x44, 0x44, 0x44, 0x70, + + /* U+0075 "u" */ + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0xc3, 0x7d, + + /* U+0076 "v" */ + 0x81, 0x20, 0x90, 0x48, 0x42, 0x21, 0x10, 0x90, + 0x28, 0x18, 0x4, 0x0, + + /* U+0077 "w" */ + 0x82, 0x14, 0x30, 0x91, 0x84, 0x8a, 0x44, 0x92, + 0x14, 0x90, 0xa5, 0x5, 0x28, 0x30, 0xc0, 0x86, + 0x0, + + /* U+0078 "x" */ + 0x41, 0x11, 0x8, 0x82, 0x80, 0x80, 0xc0, 0x50, + 0x44, 0x42, 0x60, 0x80, + + /* U+0079 "y" */ + 0x81, 0x20, 0x90, 0x44, 0x42, 0x21, 0x20, 0x50, + 0x28, 0x8, 0x4, 0x4, 0x2, 0x6, 0x0, + + /* U+007A "z" */ + 0xff, 0x2, 0x6, 0x4, 0x8, 0x10, 0x20, 0x60, + 0x40, 0xff, + + /* U+007B "{" */ + 0x19, 0x8, 0x42, 0x10, 0x9c, 0x41, 0x8, 0x42, + 0x10, 0x83, + + /* U+007C "|" */ + 0xff, 0xff, + + /* U+007D "}" */ + 0xc1, 0x8, 0x42, 0x10, 0x87, 0x21, 0x8, 0x42, + 0x10, 0x98, + + /* U+007E "~" */ + 0x78, 0x43, 0xc0, + + /* U+3000 " " */ + 0x0, + + /* U+3001 "、" */ + 0x4, 0x21, + + /* U+3002 "。" */ + 0x69, 0x9e, + + /* U+3003 "〃" */ + 0x24, 0xa4, 0x94, 0x92, 0x0, + + /* U+3005 "々" */ + 0x8, 0x6, 0x1, 0x0, 0x7e, 0x20, 0xd0, 0x40, + 0x10, 0x8, 0x1c, 0x3, 0x0, 0x60, 0x8, + + /* U+3008 "〈" */ + 0x11, 0x22, 0x44, 0x88, 0x84, 0x42, 0x21, 0x10, + + /* U+3009 "〉" */ + 0x42, 0x10, 0x42, 0x8, 0x41, 0x10, 0x88, 0x44, + 0x21, 0x0, + + /* U+300A "《" */ + 0x12, 0x28, 0x91, 0x42, 0x8a, 0x14, 0x48, 0x50, + 0xa1, 0xa1, 0x42, 0x42, 0x84, 0x80, + + /* U+300B "》" */ + 0xa0, 0xa1, 0x41, 0x42, 0x82, 0x85, 0x9, 0x14, + 0x28, 0xa1, 0x45, 0xa, 0x28, 0x0, + + /* U+300C "「" */ + 0xf8, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x80, + + /* U+300D "」" */ + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x1f, + + /* U+300E "『" */ + 0xfd, 0xf1, 0x8c, 0x63, 0x18, 0xc6, 0x31, 0x8c, + 0x63, 0x0, + + /* U+300F "』" */ + 0x18, 0xc6, 0x31, 0x8c, 0x63, 0x18, 0xc6, 0x31, + 0xff, 0xe0, + + /* U+3010 "【" */ + 0xfb, 0x69, 0x24, 0x92, 0x4d, 0xb8, + + /* U+3011 "】" */ + 0xed, 0x92, 0x49, 0x24, 0x96, 0xf8, + + /* U+3013 "〓" */ + 0xff, 0xff, 0xc0, 0x0, 0x0, 0x0, 0x3, 0xff, + 0xff, + + /* U+3014 "〔" */ + 0x3c, 0x88, 0x88, 0x88, 0x88, 0x88, 0x8c, 0x30, + + /* U+3015 "〕" */ + 0xc3, 0x11, 0x11, 0x11, 0x11, 0x11, 0x13, 0xc0, + + /* U+3016 "〖" */ + 0xf4, 0xa9, 0x4a, 0x63, 0x18, 0xc6, 0x31, 0x4a, + 0x52, 0x5e, + + /* U+3017 "〗" */ + 0xf9, 0x95, 0x53, 0x33, 0x33, 0x35, 0x59, 0x9f, + + /* U+3041 "ぁ" */ + 0xc, 0x1, 0x21, 0xfc, 0x4, 0x80, 0xf8, 0x76, + 0x97, 0x8e, 0x61, 0x88, 0x3f, 0x9, 0x6, 0x0, + + /* U+3042 "あ" */ + 0x6, 0x0, 0x30, 0x1, 0x60, 0xff, 0x0, 0xc8, + 0x6, 0x40, 0x3f, 0x87, 0xa6, 0x6d, 0x1a, 0x70, + 0xe3, 0x87, 0x18, 0x39, 0xc3, 0x78, 0x78, 0x1, + 0x0, + + /* U+3043 "ぃ" */ + 0x80, 0x10, 0x32, 0x3, 0x40, 0x68, 0x6, 0x80, + 0x92, 0x3, 0xc0, 0x30, 0x0, + + /* U+3044 "い" */ + 0xc0, 0x3, 0x0, 0x8c, 0x6, 0x30, 0xc, 0xc0, + 0x19, 0x0, 0x64, 0x0, 0xd8, 0x0, 0x26, 0x0, + 0xf0, 0x0, + + /* U+3045 "ぅ" */ + 0x10, 0x38, 0x0, 0x7, 0xec, 0x40, 0x81, 0x2, + 0x8, 0x30, 0xc0, + + /* U+3046 "う" */ + 0x1c, 0xf, 0x80, 0x40, 0x3, 0xf3, 0x8d, 0x6, + 0x3, 0x1, 0x81, 0xc0, 0xc0, 0xe0, 0xe0, 0x60, + + /* U+3047 "ぇ" */ + 0x8, 0x3, 0x80, 0x20, 0x0, 0x7e, 0x3, 0x1, + 0xc0, 0xd8, 0x26, 0x10, 0x88, 0x3c, + + /* U+3048 "え" */ + 0xc, 0x0, 0x78, 0x1, 0x0, 0x0, 0x7f, 0x0, + 0x70, 0x6, 0x0, 0xc0, 0x1f, 0x3, 0x30, 0x63, + 0x6, 0x10, 0xc1, 0xf0, + + /* U+3049 "ぉ" */ + 0x0, 0x4, 0x1, 0x5b, 0xe3, 0x10, 0x4, 0x1, + 0xf0, 0xe2, 0x70, 0xa4, 0x2d, 0x71, 0xc0, + + /* U+304A "お" */ + 0x38, 0x3, 0x0, 0x3e, 0x7f, 0xc3, 0x30, 0x3, + 0x0, 0x3f, 0xc7, 0x86, 0xf0, 0x3b, 0x7, 0xb0, + 0xef, 0x3c, 0x70, 0x0, + + /* U+304B "か" */ + 0x6, 0x0, 0xc, 0x0, 0x10, 0xe0, 0x2c, 0xe7, + 0xfc, 0xe1, 0x18, 0xc6, 0x30, 0xc, 0x60, 0x30, + 0xc0, 0x63, 0x1, 0x86, 0x2, 0x38, 0x0, + + /* U+304C "が" */ + 0x0, 0x6, 0xc, 0xb, 0xc, 0xc, 0xc, 0x60, + 0x9, 0x30, 0xff, 0x98, 0xf8, 0x98, 0x10, 0x88, + 0x31, 0x80, 0x31, 0x80, 0x61, 0x80, 0xc3, 0x0, + 0xce, 0x0, + + /* U+304D "き" */ + 0x18, 0x2, 0x40, 0xf1, 0xf0, 0x6, 0xdf, 0xc0, + 0x18, 0x6, 0x7f, 0xf0, 0x78, 0xb, 0x0, 0x60, + 0xf, 0x0, + + /* U+304E "ぎ" */ + 0x0, 0x0, 0x81, 0x84, 0x16, 0x3c, 0x47, 0x80, + 0x6, 0x40, 0x1e, 0xf, 0xc0, 0x3, 0x0, 0xc, + 0x1f, 0xe1, 0x83, 0x8, 0x0, 0x40, 0x3, 0x0, + 0x7, 0x80, + + /* U+304F "く" */ + 0x8, 0x18, 0x60, 0xc3, 0xc, 0x30, 0x60, 0xe0, + 0xc0, 0xc0, 0xc1, 0xc1, 0x81, 0x0, + + /* U+3050 "ぐ" */ + 0x0, 0x7, 0x1, 0x88, 0xc3, 0x33, 0x18, 0x4c, + 0x3, 0x0, 0xc0, 0x18, 0x3, 0x0, 0x60, 0x1c, + 0x3, 0x0, 0x40, + + /* U+3051 "け" */ + 0x40, 0x63, 0x3, 0x10, 0x19, 0x80, 0xdc, 0x7f, + 0xe0, 0x33, 0x1, 0x98, 0xc, 0xd0, 0x66, 0x83, + 0x3c, 0x30, 0xc1, 0x86, 0x18, 0x0, 0x80, + + /* U+3052 "げ" */ + 0x0, 0xc, 0x81, 0xe9, 0x83, 0xe2, 0x7, 0xc, + 0xf, 0x18, 0xfe, 0x30, 0x38, 0x60, 0x70, 0xc0, + 0xe1, 0xa1, 0xc3, 0x43, 0x3, 0xe, 0x6, 0x18, + 0x4, 0x60, 0x0, + + /* U+3053 "こ" */ + 0x20, 0x7, 0xf0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0xc0, 0xf, 0xfc, 0xff, 0x80, + + /* U+3054 "ご" */ + 0x20, 0x8, 0xfe, 0x30, 0x1, 0x80, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40, 0x3, + 0x80, 0x83, 0xfe, 0x0, + + /* U+3055 "さ" */ + 0xc, 0x1, 0x80, 0x10, 0x3, 0xe7, 0xf0, 0x2, + 0x0, 0x63, 0xfe, 0xe1, 0xf8, 0x3, 0x0, 0x30, + 0x1, 0xe0, + + /* U+3056 "ざ" */ + 0x0, 0x30, 0x0, 0xc6, 0x18, 0x18, 0x40, 0x78, + 0x7f, 0x0, 0x8, 0x0, 0x20, 0x7f, 0x86, 0x1c, + 0x20, 0x21, 0x0, 0x6, 0x0, 0x1e, 0x0, + + /* U+3057 "し" */ + 0xc0, 0x60, 0x30, 0x18, 0xc, 0x6, 0x3, 0x1, + 0x80, 0xc0, 0x60, 0x30, 0x7c, 0x73, 0xc0, + + /* U+3058 "じ" */ + 0xc0, 0xb0, 0x3c, 0x37, 0x6, 0xc0, 0x30, 0xc, + 0x3, 0x0, 0xc0, 0x30, 0xc, 0x9, 0x8e, 0x3e, + 0x0, + + /* U+3059 "す" */ + 0x0, 0x0, 0x6, 0x0, 0x10, 0x7, 0xff, 0xff, + 0xfc, 0x4, 0x0, 0xf0, 0x6, 0x60, 0x19, 0x80, + 0x66, 0x1, 0xf8, 0x3, 0xe0, 0x1, 0x0, 0xc, + 0x0, 0xe0, 0x1, 0x0, + + /* U+305A "ず" */ + 0x0, 0x0, 0x6, 0x70, 0x11, 0x80, 0x40, 0xff, + 0xfc, 0x4, 0x0, 0x70, 0x3, 0x60, 0x9, 0x80, + 0x26, 0x0, 0x98, 0x1, 0xe0, 0x1, 0x0, 0xc, + 0x0, 0x60, 0x3, 0x0, + + /* U+305B "せ" */ + 0x0, 0x60, 0x18, 0xc0, 0x31, 0x80, 0x63, 0xf, + 0xff, 0xfd, 0x8c, 0x3, 0x18, 0x6, 0x30, 0xc, + 0xc0, 0x18, 0x0, 0x30, 0x0, 0x3f, 0x80, + + /* U+305C "ぜ" */ + 0x0, 0x0, 0x0, 0x67, 0x8, 0x6c, 0x8, 0x64, + 0x8, 0x60, 0xf, 0xfc, 0xff, 0xe0, 0x8, 0x60, + 0x8, 0x60, 0x8, 0xe0, 0x8, 0x0, 0x8, 0x0, + 0x7, 0xf0, + + /* U+305D "そ" */ + 0x0, 0x3, 0xe, 0x30, 0xc1, 0x98, 0x13, 0x0, + 0x7f, 0xfd, 0xce, 0x30, 0x6, 0x0, 0x60, 0x4, + 0x0, 0x60, 0x3, 0x0, 0x1c, + + /* U+305E "ぞ" */ + 0x0, 0x0, 0x0, 0x2, 0x30, 0x68, 0x18, 0xc4, + 0x19, 0xc0, 0x3, 0x80, 0x3, 0xf8, 0xfd, 0xc0, + 0x63, 0x0, 0x6, 0x0, 0x6, 0x0, 0x6, 0x0, + 0x7, 0x0, 0x3, 0xe0, 0x0, 0x60, + + /* U+305F "た" */ + 0x6, 0x0, 0x20, 0x3, 0x60, 0xff, 0x0, 0x80, + 0xc, 0x7c, 0x60, 0x2, 0x0, 0x30, 0x1, 0x80, + 0x18, 0x1, 0xc3, 0xf0, + + /* U+3060 "だ" */ + 0x0, 0x0, 0xc, 0x8, 0x10, 0x18, 0x2c, 0xc7, + 0xf0, 0x1, 0x80, 0x2, 0x3e, 0xc, 0x0, 0x18, + 0x0, 0x20, 0x0, 0xc0, 0x1, 0x80, 0x6, 0x10, + 0xc, 0x3f, 0x0, + + /* U+3061 "ち" */ + 0xc, 0x0, 0x60, 0x3, 0x31, 0xff, 0x80, 0x80, + 0xc, 0x0, 0x67, 0xc3, 0xe3, 0x1c, 0x8, 0xc0, + 0x40, 0x2, 0x0, 0x30, 0x3, 0x1, 0xe0, + + /* U+3062 "ぢ" */ + 0x0, 0x0, 0x18, 0x18, 0x20, 0x50, 0x4c, 0x47, + 0xf8, 0x1, 0x0, 0x2, 0x0, 0xd, 0xf0, 0x1e, + 0x30, 0x38, 0x30, 0x60, 0x60, 0x0, 0xc0, 0x3, + 0x0, 0x1c, 0x0, 0xf0, 0x0, + + /* U+3063 "っ" */ + 0x7, 0xc3, 0xc2, 0x60, 0x10, 0x1, 0x0, 0x10, + 0x3, 0x0, 0xe0, 0x38, + + /* U+3064 "つ" */ + 0x7, 0xf0, 0x7c, 0x1b, 0xc0, 0x1a, 0x0, 0x30, + 0x0, 0x60, 0x0, 0xc0, 0x3, 0x0, 0x1e, 0x1, + 0xf0, 0x7, 0x80, + + /* U+3065 "づ" */ + 0x0, 0x0, 0x0, 0x7, 0x0, 0x8, 0x0, 0x4, + 0x7, 0xf0, 0x7c, 0x1c, 0xe0, 0x6, 0x0, 0x6, + 0x0, 0x6, 0x0, 0x6, 0x0, 0xc, 0x0, 0x3c, + 0x3, 0xf0, 0x3, 0xc0, + + /* U+3066 "て" */ + 0x0, 0x78, 0x7f, 0xff, 0xe1, 0xc6, 0x0, 0x30, + 0x3, 0x0, 0x18, 0x0, 0xc0, 0x6, 0x0, 0x30, + 0x0, 0xc0, 0x7, 0xe0, 0xf, 0x0, + + /* U+3067 "で" */ + 0x0, 0x70, 0xf, 0xf0, 0xff, 0x82, 0xe3, 0xa, + 0x3, 0x0, 0x6, 0x0, 0x6, 0x0, 0x6, 0x0, + 0x6, 0x0, 0x6, 0x0, 0x3, 0x0, 0x3, 0xf0, + 0x0, 0xf0, + + /* U+3068 "と" */ + 0x60, 0xc, 0x3, 0x0, 0x67, 0x1f, 0x87, 0x83, + 0x81, 0x80, 0xc0, 0x30, 0xc, 0x1, 0x80, 0x3f, + 0x80, + + /* U+3069 "ど" */ + 0x60, 0x18, 0xc1, 0xa3, 0x3, 0x6, 0x30, 0x1f, + 0xc0, 0x78, 0x3, 0x80, 0x18, 0x0, 0xc0, 0x3, + 0x0, 0xc, 0x0, 0x18, 0x0, 0x3f, 0x80, + + /* U+306A "な" */ + 0xc, 0x0, 0x30, 0x0, 0xf0, 0x1f, 0xcc, 0x18, + 0x18, 0xc3, 0x23, 0xc, 0x18, 0x30, 0x60, 0xc3, + 0x1f, 0x84, 0xcf, 0x82, 0x3e, 0xc, 0xc8, 0x1e, + 0x0, + + /* U+306B "に" */ + 0x0, 0xc, 0x0, 0xc7, 0xec, 0x0, 0x80, 0x8, + 0x0, 0x80, 0x8, 0x0, 0x80, 0xa, 0xc0, 0xe7, + 0xf6, 0x0, + + /* U+306C "ぬ" */ + 0x0, 0x0, 0x3, 0x0, 0x6, 0x3, 0x3f, 0x86, + 0xf3, 0x87, 0x23, 0x8c, 0xc3, 0x3d, 0x6, 0x6e, + 0xd, 0x8c, 0x1b, 0x3b, 0xe7, 0xc8, 0xef, 0x13, + 0xe0, 0x3c, 0x0, + + /* U+306D "ね" */ + 0x18, 0x0, 0x20, 0x0, 0x63, 0x81, 0xef, 0x8f, + 0xe1, 0x93, 0x83, 0x6, 0x6, 0x18, 0xc, 0x30, + 0xd8, 0xe7, 0xf3, 0x48, 0xf2, 0x9f, 0x21, 0x8, + 0x0, + + /* U+306E "の" */ + 0x3, 0xe0, 0x1f, 0xf0, 0xe6, 0x71, 0x8c, 0x66, + 0x10, 0x78, 0x20, 0xf0, 0xc1, 0xe1, 0x3, 0xc6, + 0xf, 0xc8, 0x19, 0xf0, 0xe1, 0xc3, 0x80, 0x2, + 0x0, + + /* U+306F "は" */ + 0x40, 0x3, 0x6, 0x10, 0x31, 0x81, 0x88, 0xff, + 0xc0, 0x22, 0x1, 0x10, 0x8, 0x80, 0x45, 0x7e, + 0x3b, 0x1f, 0x99, 0xb4, 0xf8, 0x0, + + /* U+3070 "ば" */ + 0x0, 0x0, 0x80, 0xd, 0x81, 0x62, 0x2, 0x4, + 0x4, 0x91, 0xff, 0x20, 0x10, 0x40, 0x20, 0x80, + 0x41, 0x0, 0x82, 0x8f, 0x87, 0x23, 0xce, 0x4d, + 0x8c, 0xf0, 0x10, 0x0, 0x0, + + /* U+3071 "ぱ" */ + 0x0, 0x0, 0x0, 0x39, 0x0, 0x52, 0x6, 0xec, + 0xc, 0x18, 0x1a, 0x23, 0xfc, 0x40, 0x60, 0x80, + 0xc1, 0x1, 0x82, 0x3, 0x5, 0x3e, 0xe, 0x8f, + 0x9d, 0x1b, 0x11, 0xe0, 0x20, 0x0, + + /* U+3072 "ひ" */ + 0x1e, 0x63, 0xf1, 0x85, 0x87, 0x4, 0x1e, 0x30, + 0x79, 0xc1, 0xb6, 0x6, 0x18, 0x18, 0x60, 0x61, + 0x83, 0x3, 0x1c, 0x7, 0xc0, + + /* U+3073 "び" */ + 0x0, 0x4, 0x0, 0xc, 0xf3, 0x6f, 0xc2, 0x6b, + 0x6, 0x6, 0xc, 0x18, 0x1c, 0x20, 0x2c, 0xc0, + 0x41, 0x80, 0x83, 0x3, 0x6, 0x4, 0x6, 0x10, + 0x7, 0xc0, 0x0, + + /* U+3074 "ぴ" */ + 0x0, 0x6, 0x3e, 0x69, 0x7c, 0x26, 0x18, 0x30, + 0x10, 0x30, 0x30, 0x38, 0x20, 0x2c, 0x60, 0x20, + 0x60, 0x20, 0x60, 0x40, 0x60, 0x40, 0x30, 0x80, + 0x1f, 0x0, + + /* U+3075 "ふ" */ + 0x3, 0x0, 0x1, 0xc0, 0x0, 0xc0, 0x0, 0x0, + 0x3, 0x0, 0x3, 0x0, 0x1, 0x80, 0x1, 0x8c, + 0xc8, 0xc6, 0x78, 0xc3, 0x73, 0xc0, 0x3, 0xc0, + + /* U+3076 "ぶ" */ + 0x1, 0x2, 0x0, 0xe0, 0x80, 0x13, 0x40, 0x0, + 0x80, 0x30, 0x0, 0x18, 0x0, 0x6, 0x0, 0x3, + 0x18, 0xc8, 0xc6, 0x3c, 0x61, 0x9c, 0xf0, 0x40, + 0x78, 0x0, + + /* U+3077 "ぷ" */ + 0x0, 0x0, 0x3, 0x87, 0x0, 0x89, 0x0, 0xf, + 0x3, 0x0, 0x3, 0x0, 0x1, 0x0, 0x1, 0x8c, + 0xc8, 0xc6, 0xf8, 0xc3, 0x73, 0xc0, 0x3, 0x80, + + /* U+3078 "へ" */ + 0x0, 0x0, 0x78, 0x3, 0x30, 0x18, 0x70, 0xe0, + 0xe1, 0x0, 0xe0, 0x1, 0xc0, 0x3, + + /* U+3079 "べ" */ + 0x0, 0x8, 0x0, 0x30, 0x3, 0x3, 0x4, 0x1e, + 0x0, 0xce, 0x6, 0x1c, 0x30, 0x1c, 0x40, 0x3c, + 0x0, 0x70, 0x0, 0x0, + + /* U+307A "ぺ" */ + 0x0, 0x1c, 0x0, 0x48, 0x0, 0x90, 0x20, 0xe1, + 0xf0, 0x7, 0x30, 0x1c, 0x38, 0x70, 0x38, 0x0, + 0x3c, 0x0, 0x1c, 0x0, 0x0, + + /* U+307B "ほ" */ + 0x40, 0x3, 0x3f, 0x30, 0x31, 0x81, 0x88, 0x7f, + 0x40, 0x62, 0x3, 0x10, 0x18, 0x80, 0xc5, 0x1f, + 0x39, 0x1f, 0xc9, 0xbe, 0x38, 0x20, 0x0, + + /* U+307C "ぼ" */ + 0x60, 0x2, 0x67, 0xf3, 0x60, 0x44, 0x60, 0x40, + 0xc7, 0xf8, 0xc0, 0x40, 0xc0, 0x40, 0xc0, 0x40, + 0xc0, 0x60, 0xd3, 0xf0, 0xe4, 0x78, 0x64, 0x7c, + 0x63, 0xc0, + + /* U+307D "ぽ" */ + 0x60, 0x10, 0x47, 0xf7, 0x40, 0x49, 0xc0, 0x47, + 0x87, 0xf0, 0x87, 0xe0, 0x80, 0x40, 0x80, 0x40, + 0x80, 0x40, 0xa7, 0xe0, 0xe4, 0x78, 0xe6, 0xd8, + 0x63, 0x80, + + /* U+307E "ま" */ + 0x6, 0x0, 0xc1, 0xff, 0x3, 0x0, 0x60, 0xc, + 0x9f, 0xf0, 0x30, 0x6, 0xf, 0xc3, 0x1c, 0x63, + 0xec, 0x66, 0xf8, 0x0, + + /* U+307F "み" */ + 0x1f, 0x80, 0x1f, 0x80, 0x4, 0x0, 0x18, 0x80, + 0x21, 0x83, 0xe2, 0x1f, 0x7c, 0x64, 0x3c, 0x98, + 0x3d, 0x20, 0xcf, 0xc3, 0x2, 0xc, 0x0, 0x30, + 0x0, + + /* U+3080 "む" */ + 0x18, 0x0, 0x60, 0x1, 0x82, 0x3f, 0x8e, 0x18, + 0x9, 0xe0, 0xd, 0x80, 0x36, 0x0, 0xd8, 0x3, + 0xe0, 0xc7, 0x3, 0xc, 0xc, 0x38, 0x60, 0x7f, + 0x0, + + /* U+3081 "め" */ + 0x0, 0x80, 0x6, 0x4, 0x1c, 0x1b, 0xfe, 0x39, + 0x19, 0xcc, 0x35, 0xb0, 0xf7, 0x83, 0x8e, 0xe, + 0x3c, 0x7b, 0xa1, 0xbc, 0x1c, 0x0, 0xe0, + + /* U+3082 "も" */ + 0x8, 0x2, 0x1, 0x81, 0xf8, 0x10, 0x4, 0x3, + 0x3, 0xee, 0x35, 0xcc, 0x33, 0xc, 0xc3, 0x19, + 0x83, 0xc0, + + /* U+3083 "ゃ" */ + 0x4, 0x0, 0x10, 0x8, 0x7c, 0x2e, 0x31, 0x81, + 0xf4, 0x1d, 0x37, 0xc0, 0x80, 0x6, 0x0, 0x10, + 0x0, 0x80, + + /* U+3084 "や" */ + 0x3, 0x0, 0x3, 0x80, 0x61, 0xf0, 0xdc, 0x31, + 0xe0, 0x2f, 0x80, 0xd9, 0x8f, 0x81, 0x3c, 0x3, + 0x0, 0x2, 0x0, 0x6, 0x0, 0xc, 0x0, + + /* U+3085 "ゅ" */ + 0x0, 0x10, 0xc2, 0x1c, 0x4d, 0x6b, 0x27, 0xc4, + 0x70, 0x8e, 0x91, 0xca, 0x58, 0xf0, 0x30, 0x0, + 0x0, + + /* U+3086 "ゆ" */ + 0x3, 0x6, 0xc, 0x21, 0xf9, 0x1b, 0x6b, 0x19, + 0xd0, 0xcf, 0x6, 0x78, 0x33, 0xc9, 0x9e, 0x39, + 0xb0, 0xf8, 0xc, 0x0, 0x40, 0x0, + + /* U+3087 "ょ" */ + 0x8, 0x4, 0x2, 0x1, 0xf0, 0x80, 0x40, 0x20, + 0xf0, 0x8e, 0x45, 0xfc, 0x40, + + /* U+3088 "よ" */ + 0x6, 0x0, 0x60, 0x6, 0x0, 0x7e, 0x6, 0x0, + 0x60, 0x6, 0x0, 0x60, 0x6, 0x7, 0xf8, 0x87, + 0xe8, 0x63, 0xc6, 0x27, 0xc2, + + /* U+3089 "ら" */ + 0x0, 0x7, 0x0, 0x30, 0x0, 0x4, 0x0, 0x80, + 0x10, 0x6, 0x7c, 0xf0, 0xdc, 0xd, 0x1, 0x80, + 0x30, 0x1c, 0x1f, 0x1, 0x80, + + /* U+308A "り" */ + 0x63, 0xc1, 0xc1, 0xc1, 0xc1, 0xd1, 0xe1, 0xe1, + 0x61, 0x3, 0x2, 0x6, 0xc, 0x0, + + /* U+308B "る" */ + 0x6, 0xf, 0xf0, 0x98, 0x3, 0x0, 0xc0, 0x3f, + 0xe, 0x33, 0x3, 0x60, 0x61, 0x8c, 0x79, 0x89, + 0xe0, 0xf0, + + /* U+308C "れ" */ + 0xc, 0x0, 0x10, 0x0, 0x37, 0xc7, 0xf1, 0x8d, + 0xc3, 0x3, 0x6, 0xe, 0xc, 0x1c, 0x30, 0x78, + 0x63, 0xb0, 0xee, 0x60, 0xf8, 0xc1, 0xc1, 0x80, + 0x0, + + /* U+308D "ろ" */ + 0x7, 0xc1, 0xfc, 0x4, 0xc0, 0x4, 0x0, 0x40, + 0x7, 0xf0, 0x70, 0xc6, 0x3, 0x60, 0x1f, 0x0, + 0xd0, 0x6, 0x0, 0x60, 0x6, 0x0, 0xe0, + + /* U+308E "ゎ" */ + 0x18, 0x1, 0x0, 0x1c, 0x7, 0xbc, 0x5e, 0x61, + 0x83, 0x30, 0x37, 0x3, 0xd0, 0x61, 0x1c, 0x10, + 0x0, + + /* U+308F "わ" */ + 0xe, 0x0, 0x18, 0x0, 0x30, 0x0, 0x78, 0x7, + 0xef, 0x81, 0xf1, 0x83, 0x81, 0x8e, 0x3, 0x3c, + 0x6, 0x78, 0x1d, 0xb0, 0x72, 0x63, 0xc0, 0xc6, + 0x1, 0x80, 0x0, + + /* U+3090 "ゐ" */ + 0xf, 0x81, 0xfc, 0x0, 0xc0, 0x6, 0x0, 0xfe, + 0x1f, 0x19, 0x90, 0x79, 0x83, 0xc8, 0x1c, 0xc8, + 0xf5, 0xf7, 0xec, 0xe4, 0x3e, 0x0, + + /* U+3091 "ゑ" */ + 0x1f, 0xc0, 0x26, 0x0, 0x30, 0x1, 0xb0, 0xf, + 0xf0, 0x70, 0xc3, 0x73, 0x1, 0x58, 0x7, 0xc0, + 0x61, 0x3, 0x1b, 0x9f, 0xc6, 0xc6, 0xc, 0x10, + 0x0, + + /* U+3092 "を" */ + 0x8, 0x0, 0x60, 0x2, 0x40, 0xfe, 0x1, 0x80, + 0x18, 0x0, 0xd8, 0x7, 0xe2, 0x73, 0xfb, 0x1e, + 0x33, 0xc0, 0x32, 0x1, 0x10, 0xc, 0x0, 0x3f, + 0x0, + + /* U+3093 "ん" */ + 0x3, 0x0, 0xc, 0x0, 0x60, 0x1, 0x0, 0xc, + 0x0, 0x20, 0x1, 0xf0, 0xe, 0x40, 0x31, 0x81, + 0x86, 0x16, 0x18, 0xf0, 0x66, 0xc1, 0xf1, 0x3, + 0x80, + + /* U+30A1 "ァ" */ + 0xff, 0xe0, 0x18, 0x26, 0x7, 0x80, 0x80, 0x10, + 0x6, 0x1, 0x80, 0x60, 0x0, 0x0, + + /* U+30A2 "ア" */ + 0xff, 0xfc, 0x0, 0x60, 0x3, 0x0, 0x98, 0x6, + 0xc0, 0x18, 0x0, 0x60, 0x3, 0x0, 0xc, 0x0, + 0x60, 0x7, 0x0, 0x0, 0x0, + + /* U+30A3 "ィ" */ + 0x1, 0x1, 0xc0, 0x80, 0xc1, 0xc1, 0xe3, 0x91, + 0x88, 0x4, 0x2, 0x1, 0x0, 0x80, + + /* U+30A4 "イ" */ + 0x0, 0x40, 0x1c, 0x7, 0x1, 0xc0, 0x70, 0x1e, + 0xe, 0xc7, 0x98, 0xc3, 0x0, 0x60, 0xc, 0x1, + 0x80, 0x30, 0x6, 0x0, + + /* U+30A5 "ゥ" */ + 0xc, 0x3, 0xf, 0xfa, 0x2, 0x80, 0xa0, 0x60, + 0x10, 0xc, 0x6, 0x3, 0x1, 0x80, + + /* U+30A6 "ウ" */ + 0x6, 0x0, 0x30, 0x1, 0x81, 0xff, 0xfc, 0x3, + 0x60, 0x1b, 0x1, 0xd8, 0xc, 0x0, 0xe0, 0x6, + 0x0, 0x60, 0xe, 0x0, 0xe0, 0x0, 0x0, + + /* U+30A7 "ェ" */ + 0x7f, 0xc0, 0x80, 0x10, 0x2, 0x0, 0x40, 0x8, + 0x3f, 0xf8, + + /* U+30A8 "エ" */ + 0x7f, 0xf9, 0xff, 0xe0, 0x30, 0x0, 0xc0, 0x3, + 0x0, 0xc, 0x0, 0x30, 0x0, 0xc0, 0xff, 0xfc, + + /* U+30A9 "ォ" */ + 0x6, 0x1, 0x80, 0x63, 0xff, 0xe, 0x7, 0x83, + 0x61, 0x98, 0xc6, 0x1, 0x80, 0xe0, + + /* U+30AA "オ" */ + 0x1, 0x80, 0x6, 0x0, 0x18, 0x1f, 0xff, 0x7f, + 0xfc, 0xe, 0x0, 0x78, 0x3, 0x60, 0x19, 0x81, + 0xc6, 0x6, 0x18, 0x0, 0x60, 0x3, 0x80, 0xe, + 0x0, + + /* U+30AB "カ" */ + 0x3, 0x0, 0x18, 0x0, 0xc0, 0xff, 0xf7, 0xff, + 0x81, 0x8c, 0x18, 0x60, 0xc3, 0xc, 0x18, 0x60, + 0xc6, 0xc, 0xe1, 0xe6, 0xe, 0x0, + + /* U+30AC "ガ" */ + 0x0, 0xc, 0xc, 0xd0, 0x21, 0x80, 0x80, 0xff, + 0xf0, 0x18, 0xc0, 0x63, 0x3, 0xc, 0xc, 0x30, + 0x60, 0xc1, 0x83, 0xc, 0x18, 0x60, 0x60, 0x7, + 0x0, + + /* U+30AD "キ" */ + 0x6, 0x0, 0x18, 0x0, 0x61, 0x1f, 0xfc, 0x7f, + 0x0, 0xc, 0x0, 0x37, 0xff, 0xff, 0xf3, 0x0, + 0xc, 0x0, 0x38, 0x0, 0x60, 0x1, 0x80, 0x6, + 0x0, + + /* U+30AE "ギ" */ + 0x0, 0x0, 0x0, 0x20, 0x63, 0x1, 0x84, 0x6, + 0x20, 0x3f, 0x87, 0xf0, 0x0, 0xc0, 0x3, 0x18, + 0x3f, 0xef, 0xf8, 0x10, 0x40, 0x1, 0x80, 0x6, + 0x0, 0x18, 0x0, 0x40, + + /* U+30AF "ク" */ + 0x6, 0x0, 0xe0, 0xf, 0xf1, 0x87, 0x70, 0x6e, + 0xc, 0x1, 0xc0, 0x18, 0x3, 0x0, 0xe0, 0x1c, + 0x7, 0x80, 0x60, 0x0, + + /* U+30B0 "グ" */ + 0x0, 0x8, 0x18, 0xc0, 0xc1, 0x3, 0xf0, 0x1f, + 0xe0, 0xc1, 0x87, 0xc, 0x38, 0x30, 0x1, 0x80, + 0x6, 0x0, 0x30, 0x1, 0x80, 0x1c, 0x1, 0xe0, + 0x3, 0x0, 0x0, + + /* U+30B1 "ケ" */ + 0x4, 0x0, 0x38, 0x0, 0xc0, 0x7, 0x0, 0x1f, + 0xfc, 0xff, 0xf7, 0xc, 0x38, 0x30, 0xc0, 0xc0, + 0x6, 0x0, 0x38, 0x1, 0xc0, 0xe, 0x0, 0x70, + 0x0, + + /* U+30B2 "ゲ" */ + 0x0, 0x4, 0x0, 0x8, 0x38, 0x60, 0x60, 0x1, + 0xc0, 0x3, 0xff, 0x8c, 0x30, 0x30, 0x60, 0xc0, + 0xc0, 0x3, 0x80, 0x6, 0x0, 0x18, 0x0, 0x60, + 0x1, 0x80, 0x0, + + /* U+30B3 "コ" */ + 0xff, 0xff, 0xf0, 0xc, 0x3, 0x0, 0xc0, 0x30, + 0xc, 0x3, 0xff, 0xff, 0xf0, + + /* U+30B4 "ゴ" */ + 0x0, 0x8, 0x0, 0x30, 0x3, 0x0, 0x4, 0xff, + 0xe3, 0xff, 0x80, 0x6, 0x0, 0x18, 0x0, 0x60, + 0x1, 0x80, 0x6, 0x0, 0x18, 0xff, 0xe3, 0xff, + 0x80, + + /* U+30B5 "サ" */ + 0x0, 0x60, 0x18, 0xc0, 0x31, 0x80, 0x63, 0xf, + 0xff, 0xff, 0xff, 0xc3, 0x18, 0x6, 0x30, 0xc, + 0x60, 0x19, 0xc0, 0x3, 0x0, 0xe, 0x0, 0x38, + 0x0, 0xe0, 0x0, 0x0, 0x0, + + /* U+30B6 "ザ" */ + 0x0, 0x0, 0xc, 0x62, 0xc, 0x6c, 0xc, 0x64, + 0xc, 0x60, 0xff, 0xfe, 0xc, 0x60, 0xc, 0x60, + 0xc, 0x60, 0xc, 0xe0, 0x0, 0xc0, 0x1, 0xc0, + 0x3, 0x80, 0x7, 0x0, 0x2, 0x0, + + /* U+30B7 "シ" */ + 0x18, 0x0, 0x70, 0x0, 0xc0, 0x0, 0x0, 0x60, + 0x9, 0xc0, 0x73, 0x3, 0x0, 0x38, 0x1, 0xc0, + 0x1e, 0x1, 0xe0, 0x1f, 0x0, 0x20, 0x0, + + /* U+30B8 "ジ" */ + 0x18, 0x10, 0x70, 0x60, 0x46, 0x0, 0x8, 0x60, + 0x1, 0xc0, 0x32, 0x3, 0x80, 0x1c, 0x1, 0xe0, + 0x1e, 0x3, 0xe0, 0x1e, 0x0, 0x20, 0x0, + + /* U+30B9 "ス" */ + 0x3f, 0xf0, 0xff, 0xc0, 0x6, 0x0, 0x30, 0x1, + 0xc0, 0x6, 0x0, 0x3c, 0x1, 0xb8, 0x1c, 0x71, + 0xe0, 0xee, 0x1, 0x80, 0x0, + + /* U+30BA "ズ" */ + 0x0, 0x6, 0x0, 0x8, 0x0, 0x4, 0x3f, 0xf0, + 0x0, 0x60, 0x0, 0x60, 0x0, 0xc0, 0x1, 0x80, + 0x3, 0xc0, 0x6, 0x60, 0x1c, 0x38, 0x78, 0x1c, + 0x60, 0x8, + + /* U+30BB "セ" */ + 0x6, 0x0, 0xc, 0x0, 0x18, 0x0, 0x31, 0xe0, + 0xff, 0xcf, 0xe3, 0x91, 0x86, 0x3, 0x38, 0x6, + 0x20, 0xc, 0x0, 0x18, 0x0, 0x1f, 0xe0, + + /* U+30BC "ゼ" */ + 0x0, 0x4, 0x18, 0x14, 0x30, 0x30, 0x60, 0x0, + 0xc1, 0x81, 0xff, 0xbf, 0xf6, 0x7e, 0x18, 0xc, + 0x60, 0x19, 0xc0, 0x31, 0x0, 0x60, 0x0, 0x7f, + 0x80, + + /* U+30BD "ソ" */ + 0x0, 0x3c, 0x3, 0x60, 0x67, 0x6, 0x30, 0xc0, + 0xc, 0x1, 0x80, 0x38, 0x7, 0x0, 0xe0, 0x3c, + 0x1, 0x0, + + /* U+30BE "ゾ" */ + 0x0, 0x8, 0x0, 0x10, 0x1, 0x90, 0x2, 0xe0, + 0x31, 0x81, 0x83, 0x6, 0x8, 0x30, 0x0, 0xc0, + 0x6, 0x0, 0x30, 0x1, 0xc0, 0xe, 0x0, 0xf0, + 0x3, 0x0, 0x0, + + /* U+30BF "タ" */ + 0x6, 0x0, 0x60, 0xf, 0xf1, 0x86, 0x30, 0x66, + 0x4c, 0x4f, 0xc0, 0x38, 0x3, 0x80, 0x70, 0xe, + 0x3, 0xc0, 0x70, 0x0, 0x0, + + /* U+30C0 "ダ" */ + 0x0, 0x8, 0x0, 0x30, 0xc1, 0x43, 0x2, 0x1f, + 0xf0, 0xc1, 0x86, 0x4, 0x33, 0x30, 0xf, 0x80, + 0xe, 0x0, 0x30, 0x1, 0x80, 0x1c, 0x1, 0xe0, + 0x6, 0x0, 0x0, + + /* U+30C1 "チ" */ + 0x0, 0x20, 0x3, 0xc3, 0xfc, 0x0, 0x60, 0x1, + 0x80, 0x6, 0xf, 0xff, 0xff, 0xff, 0x1, 0x80, + 0x6, 0x0, 0x30, 0x1, 0xc0, 0x6, 0x0, 0x70, + 0x1, 0x80, 0x0, + + /* U+30C2 "ヂ" */ + 0x0, 0x0, 0x0, 0xe2, 0x1f, 0x8c, 0x1, 0x80, + 0x1, 0x80, 0xff, 0xfc, 0x1, 0x80, 0x1, 0x0, + 0x3, 0x0, 0x7, 0x0, 0xe, 0x0, 0x1c, 0x0, + 0x18, 0x0, + + /* U+30C3 "ッ" */ + 0x8, 0xf3, 0x3c, 0x88, 0x6, 0x1, 0x0, 0xc0, + 0x60, 0x70, 0x38, 0x0, 0x0, + + /* U+30C4 "ツ" */ + 0x6, 0x13, 0x18, 0x76, 0x21, 0x98, 0xce, 0x60, + 0x30, 0x1, 0xc0, 0x6, 0x0, 0x30, 0x1, 0x80, + 0x1c, 0x0, 0xe0, 0x6, 0x0, + + /* U+30C5 "ヅ" */ + 0x0, 0x6, 0x0, 0x0, 0x6, 0xc, 0xc6, 0x18, + 0x66, 0x18, 0x63, 0x30, 0x60, 0x30, 0x0, 0x60, + 0x0, 0xe0, 0x0, 0xc0, 0x1, 0x80, 0x7, 0x0, + 0x1e, 0x0, 0x18, 0x0, + + /* U+30C6 "テ" */ + 0x1f, 0xe0, 0x7f, 0x80, 0x0, 0x0, 0x0, 0xff, + 0xff, 0xff, 0xf0, 0x10, 0x0, 0xc0, 0x3, 0x0, + 0xc, 0x0, 0x60, 0x3, 0x0, 0x38, 0x0, 0x0, + 0x0, + + /* U+30C7 "デ" */ + 0x0, 0x0, 0x0, 0xa, 0x1f, 0xec, 0x0, 0x0, + 0x0, 0x0, 0xff, 0xfc, 0xff, 0xfc, 0x1, 0x0, + 0x1, 0x0, 0x1, 0x0, 0x3, 0x0, 0x3, 0x0, + 0x6, 0x0, 0x1c, 0x0, 0x8, 0x0, + + /* U+30C8 "ト" */ + 0xc3, 0xc, 0x30, 0xc3, 0xed, 0xf3, 0xc3, 0xc, + 0x30, 0xc3, 0x0, + + /* U+30C9 "ド" */ + 0xc1, 0x60, 0x70, 0x98, 0xc, 0x7, 0x83, 0x71, + 0x98, 0xc0, 0x60, 0x30, 0x18, 0xc, 0x6, 0x0, + + /* U+30CA "ナ" */ + 0x1, 0x80, 0x3, 0x0, 0x6, 0x0, 0xc, 0xf, + 0xff, 0xff, 0xff, 0xc0, 0x60, 0x0, 0xc0, 0x3, + 0x80, 0x6, 0x0, 0x1c, 0x0, 0x70, 0x1, 0xc0, + 0x3, 0x0, 0x0, + + /* U+30CB "ニ" */ + 0x3f, 0xe1, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x1f, 0xff, 0xff, 0xf8, + + /* U+30CC "ヌ" */ + 0x3f, 0xf0, 0x6, 0x10, 0x63, 0xcc, 0xf, 0x80, + 0x78, 0x7, 0xc0, 0xee, 0x3c, 0x4f, 0x0, 0x40, + 0x0, + + /* U+30CD "ネ" */ + 0x1, 0x80, 0x6, 0x0, 0x18, 0xf, 0xfc, 0x0, + 0xe0, 0x7, 0x0, 0x3a, 0x3, 0xee, 0x1d, 0x9f, + 0xe6, 0x26, 0x18, 0x0, 0x60, 0x1, 0x80, 0x6, + 0x0, + + /* U+30CE "ノ" */ + 0x0, 0xc0, 0x30, 0x18, 0xe, 0x3, 0x1, 0xc0, + 0xe0, 0x30, 0x38, 0x1c, 0xe, 0x1, 0x0, + + /* U+30CF "ハ" */ + 0x8, 0x10, 0xc, 0x38, 0xc, 0x18, 0x18, 0xc, + 0x18, 0xc, 0x30, 0x6, 0x70, 0x7, 0xe0, 0x2, + + /* U+30D0 "バ" */ + 0x0, 0x0, 0x0, 0x6, 0x0, 0x8, 0x0, 0x4, + 0x4, 0x30, 0xc, 0x38, 0x1c, 0x18, 0x18, 0xc, + 0x30, 0xe, 0x70, 0x7, 0x60, 0x0, + + /* U+30D1 "パ" */ + 0x0, 0x6, 0x0, 0x9, 0x0, 0x9, 0x0, 0x7, + 0x0, 0x0, 0xe, 0x30, 0xc, 0x18, 0x18, 0x1c, + 0x38, 0xe, 0x70, 0x6, 0xe0, 0x7, 0x40, 0x0, + + /* U+30D2 "ヒ" */ + 0x0, 0x30, 0xc, 0x3, 0x6, 0xc7, 0xff, 0x8f, + 0x3, 0x0, 0xc0, 0x30, 0xc, 0x1, 0xff, + + /* U+30D3 "ビ" */ + 0x0, 0x3c, 0xd, 0xc0, 0x6c, 0x0, 0xc3, 0x8d, + 0xf0, 0xfc, 0xc, 0x0, 0xc0, 0xc, 0x0, 0xc0, + 0x7, 0xfe, + + /* U+30D4 "ピ" */ + 0x0, 0x7c, 0x9, 0xc0, 0x7c, 0x0, 0xc3, 0x8d, + 0xf8, 0xfc, 0xc, 0x0, 0xc0, 0xc, 0x0, 0xc0, + 0xf, 0xfc, + + /* U+30D5 "フ" */ + 0xff, 0xf0, 0x7, 0x0, 0x60, 0xc, 0x1, 0xc0, + 0x18, 0x7, 0x0, 0xe0, 0x1c, 0x7, 0x80, 0x20, + 0x0, + + /* U+30D6 "ブ" */ + 0x0, 0xc, 0x0, 0xd0, 0x1, 0xbf, 0xf8, 0xff, + 0xe0, 0x1, 0x80, 0x6, 0x0, 0x30, 0x1, 0x80, + 0xe, 0x0, 0x70, 0x3, 0x80, 0x3c, 0x1, 0xc0, + 0x0, + + /* U+30D7 "プ" */ + 0x0, 0x1c, 0x0, 0x90, 0x1, 0xff, 0xf0, 0xff, + 0xe0, 0x1, 0x0, 0xc, 0x0, 0x30, 0x1, 0x80, + 0xc, 0x0, 0x70, 0x3, 0x80, 0x38, 0x1, 0xc0, + 0x0, + + /* U+30D8 "ヘ" */ + 0x7, 0x0, 0x1f, 0x80, 0x38, 0xe0, 0x70, 0x70, + 0x60, 0x3c, 0x0, 0x1e, 0x0, 0x6, 0x0, 0x0, + + /* U+30D9 "ベ" */ + 0x0, 0x10, 0x0, 0xa0, 0x3, 0x3, 0x0, 0x3f, + 0x1, 0xc6, 0xe, 0xe, 0x20, 0x1c, 0x0, 0x3c, + 0x0, 0x30, 0x0, 0x0, + + /* U+30DA "ペ" */ + 0x0, 0x1c, 0x0, 0x14, 0x0, 0x14, 0x7, 0x8, + 0xf, 0x80, 0x38, 0xc0, 0x70, 0x70, 0x60, 0x38, + 0x0, 0x1e, 0x0, 0x6, + + /* U+30DB "ホ" */ + 0x1, 0x80, 0x3, 0x0, 0x6, 0x3, 0xff, 0xe0, + 0x18, 0x2, 0x30, 0x6, 0x66, 0x18, 0xc6, 0x71, + 0x86, 0xc3, 0x4, 0xe, 0x0, 0x3c, 0x0, 0x70, + 0x0, + + /* U+30DC "ボ" */ + 0x0, 0x6, 0x3, 0x4, 0x6, 0x20, 0xc, 0x7, + 0xff, 0xcf, 0xff, 0x80, 0x60, 0x8, 0xc8, 0x19, + 0x98, 0x63, 0x19, 0x86, 0x3a, 0xc, 0x20, 0x78, + 0x0, + + /* U+30DD "ポ" */ + 0x0, 0x8, 0x0, 0x50, 0x31, 0xc0, 0xc0, 0x3, + 0x3, 0xff, 0xe0, 0x30, 0x0, 0xc0, 0x33, 0x31, + 0x8c, 0x6e, 0x30, 0xb0, 0xc2, 0x3, 0x0, 0x38, + 0x0, + + /* U+30DE "マ" */ + 0xff, 0xfb, 0xff, 0xf0, 0x3, 0x80, 0x1c, 0x0, + 0xe0, 0x77, 0x0, 0xf8, 0x1, 0xc0, 0x1, 0x80, + 0x6, 0x0, + + /* U+30DF "ミ" */ + 0x30, 0x3e, 0xc, 0x0, 0x60, 0xf8, 0x1c, 0x0, + 0x0, 0x70, 0x7c, 0x1f, 0x6, + + /* U+30E0 "ム" */ + 0x3, 0x0, 0x18, 0x0, 0x60, 0x1, 0x80, 0xc, + 0x0, 0x31, 0x81, 0x87, 0x6, 0xe, 0x31, 0xfb, + 0xff, 0x3f, 0x0, 0x80, + + /* U+30E1 "メ" */ + 0x0, 0x20, 0x7, 0x0, 0x60, 0x6, 0xc, 0xc0, + 0x78, 0x3, 0xc0, 0x3e, 0x6, 0x60, 0xc0, 0x38, + 0x7, 0x0, 0x40, 0x0, + + /* U+30E2 "モ" */ + 0x3f, 0xf0, 0x30, 0x1, 0x80, 0xc, 0xf, 0xff, + 0x83, 0x0, 0x18, 0x0, 0xc0, 0x6, 0x0, 0x1f, + 0x80, + + /* U+30E3 "ャ" */ + 0x18, 0x0, 0x80, 0xb, 0xff, 0xf2, 0xfc, 0x60, + 0xcc, 0x4, 0x80, 0x40, 0x4, 0x0, 0x60, 0x6, + 0x0, + + /* U+30E4 "ヤ" */ + 0x6, 0x0, 0x6, 0x0, 0x6, 0xe, 0x3, 0xfe, + 0xff, 0xcc, 0x7b, 0xc, 0x3, 0x18, 0x3, 0x30, + 0x1, 0x0, 0x1, 0x80, 0x1, 0x80, 0x1, 0x80, + 0x1, 0x80, + + /* U+30E5 "ュ" */ + 0x3f, 0x0, 0x20, 0x4, 0x0, 0x80, 0x30, 0x6, + 0x3f, 0xf8, + + /* U+30E6 "ユ" */ + 0x3f, 0xe0, 0xff, 0x80, 0x6, 0x0, 0x30, 0x0, + 0xc0, 0x3, 0x0, 0xc, 0x3f, 0xff, 0xff, 0xfc, + + /* U+30E7 "ョ" */ + 0xff, 0x1, 0x1, 0x1, 0x7f, 0x1, 0x1, 0x7f, + 0x1, + + /* U+30E8 "ヨ" */ + 0xff, 0xc0, 0x30, 0xf, 0xff, 0xff, 0xc0, 0x30, + 0xc, 0x3, 0xff, 0xff, 0xf0, + + /* U+30E9 "ラ" */ + 0x3f, 0xc3, 0xfc, 0x0, 0x0, 0x0, 0xff, 0xef, + 0xff, 0x0, 0x60, 0x6, 0x0, 0xc0, 0x18, 0x3, + 0x0, 0xe0, 0x1c, 0x0, 0x0, + + /* U+30EA "リ" */ + 0x0, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, + 0xc3, 0x2, 0x6, 0xe, 0x1c, 0x38, 0x0, + + /* U+30EB "ル" */ + 0xc, 0xc0, 0xc, 0xc0, 0xc, 0xc0, 0xc, 0xc0, + 0xc, 0xc0, 0xc, 0xc3, 0x18, 0xc7, 0x18, 0xdc, + 0x30, 0xf8, 0x70, 0xe0, 0x60, 0xc0, + + /* U+30EC "レ" */ + 0xc0, 0xc, 0x0, 0xc0, 0xc, 0x0, 0xc0, 0x2c, + 0xe, 0xc1, 0xcc, 0x70, 0xfe, 0xf, 0x0, 0x40, + 0x0, + + /* U+30ED "ロ" */ + 0xff, 0xf0, 0x3c, 0xf, 0x3, 0xc0, 0xf0, 0x3c, + 0xf, 0x3, 0xff, 0xc0, + + /* U+30EE "ヮ" */ + 0xff, 0xc0, 0x60, 0x70, 0x38, 0x10, 0x18, 0x8, + 0xc, 0x1c, 0x4, 0x0, + + /* U+30EF "ワ" */ + 0xff, 0xfc, 0x3, 0xc0, 0x2c, 0x2, 0xc0, 0x6c, + 0x6, 0x0, 0xc0, 0xc, 0x1, 0x80, 0x38, 0x7, + 0x0, 0x60, + + /* U+30F0 "ヰ" */ + 0x0, 0xc0, 0x1, 0x80, 0x3, 0x3, 0xff, 0xe7, + 0xff, 0xc3, 0x18, 0x6, 0x30, 0x7f, 0xff, 0xff, + 0xfe, 0x1, 0x80, 0x3, 0x0, 0x6, 0x0, 0xc, + 0x0, 0x18, 0x0, + + /* U+30F1 "ヱ" */ + 0x3f, 0xf0, 0x0, 0xc0, 0x6, 0x0, 0xf0, 0x3, + 0x0, 0xc, 0x0, 0x30, 0x0, 0xc0, 0xff, 0xff, + 0xff, 0xf0, + + /* U+30F2 "ヲ" */ + 0xff, 0xe0, 0x8, 0x3, 0x7f, 0xe0, 0x1c, 0x3, + 0x0, 0xe0, 0x38, 0xe, 0x3, 0x1, 0x80, 0x0, + + /* U+30F3 "ン" */ + 0xc0, 0xe, 0x0, 0x30, 0x10, 0x3, 0x0, 0x60, + 0x1c, 0x3, 0x80, 0xe0, 0x7c, 0xf, 0x0, 0x40, + 0x0, + + /* U+30F4 "ヴ" */ + 0x0, 0x0, 0xc, 0xc, 0x18, 0x60, 0x30, 0x4f, + 0xff, 0x98, 0x6, 0x30, 0xc, 0x60, 0x38, 0xc0, + 0x60, 0x1, 0xc0, 0x3, 0x0, 0xc, 0x0, 0x70, + 0x1, 0xc0, 0x0, 0x0, 0x0, + + /* U+30F5 "ヵ" */ + 0x4, 0x1, 0x7, 0xfc, 0x11, 0x8, 0x42, 0x11, + 0x84, 0xc3, 0x60, 0x90, 0xc0, + + /* U+30F6 "ヶ" */ + 0xc, 0x0, 0xc0, 0x18, 0x1, 0xff, 0x31, 0xe, + 0x10, 0x3, 0x0, 0x20, 0x6, 0x0, 0xc0, 0x8, + 0x0, + + /* U+4E00 "一" */ + 0xff, 0xfe, + + /* U+4E01 "丁" */ + 0xff, 0xfe, 0x1, 0x0, 0x2, 0x0, 0x4, 0x0, + 0x8, 0x0, 0x10, 0x0, 0x20, 0x0, 0x40, 0x0, + 0x80, 0x1, 0x0, 0x2, 0x0, 0x4, 0x0, 0x18, + 0x3, 0xe0, 0x0, + + /* U+4E03 "七" */ + 0x4, 0x0, 0x8, 0x0, 0x10, 0x0, 0x20, 0x0, + 0x40, 0xc7, 0xfe, 0x31, 0x0, 0x2, 0x0, 0x4, + 0x0, 0x8, 0x0, 0x10, 0x10, 0x20, 0x20, 0x40, + 0x40, 0x7f, 0x0, + + /* U+4E07 "万" */ + 0xff, 0xfe, 0x10, 0x0, 0x20, 0x0, 0x40, 0x0, + 0x80, 0x1, 0xff, 0x2, 0x2, 0x4, 0x4, 0x8, + 0x8, 0x20, 0x10, 0x40, 0x21, 0x0, 0x86, 0x1, + 0x18, 0x3c, 0x0, + + /* U+4E08 "丈" */ + 0x0, 0x40, 0x0, 0x80, 0x1, 0x7, 0xff, 0xf0, + 0x4, 0x2, 0x8, 0x4, 0x10, 0x4, 0x20, 0xc, + 0x80, 0x9, 0x0, 0xc, 0x0, 0x1c, 0x0, 0xc6, + 0xe, 0x3, 0xe0, 0x0, 0x0, + + /* U+4E09 "三" */ + 0x7f, 0xfc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0xf, 0xfe, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf, 0xff, + 0xe0, + + /* U+4E0A "上" */ + 0x0, 0x0, 0x4, 0x0, 0x8, 0x0, 0x10, 0x0, + 0x20, 0x0, 0x3f, 0x80, 0x80, 0x1, 0x0, 0x2, + 0x0, 0x4, 0x0, 0x8, 0x0, 0x10, 0x0, 0x20, + 0x1f, 0xff, 0xc0, + + /* U+4E0B "下" */ + 0xff, 0xfe, 0x4, 0x0, 0x8, 0x0, 0x10, 0x0, + 0x20, 0x0, 0x58, 0x0, 0x8c, 0x1, 0x6, 0x2, + 0x0, 0x4, 0x0, 0x8, 0x0, 0x10, 0x0, 0x20, + 0x0, 0x40, 0x0, + + /* U+4E0C "丌" */ + 0xff, 0xfe, 0x10, 0x40, 0x20, 0x80, 0x41, 0x0, + 0x82, 0x1, 0x4, 0x2, 0x8, 0x4, 0x10, 0x10, + 0x20, 0x20, 0x40, 0x40, 0x81, 0x1, 0x6, 0x2, + 0x10, 0x4, 0x0, + + /* U+4E0D "不" */ + 0xff, 0xfc, 0x4, 0x0, 0x20, 0x0, 0x80, 0x6, + 0x0, 0x29, 0x1, 0x22, 0x8, 0x86, 0xc2, 0x4, + 0x8, 0x0, 0x20, 0x0, 0x80, 0x2, 0x0, 0x8, + 0x0, + + /* U+4E0E "与" */ + 0x10, 0x0, 0x20, 0x0, 0x40, 0x0, 0xff, 0xf1, + 0x0, 0x2, 0x0, 0x4, 0x0, 0x1f, 0xfe, 0x0, + 0x4, 0x0, 0x8, 0x0, 0x17, 0xff, 0x20, 0x0, + 0x40, 0x0, 0x80, 0x2, 0x0, 0xf8, + + /* U+4E10 "丐" */ + 0xff, 0xfc, 0x8, 0x2, 0x20, 0x10, 0xfe, 0x42, + 0x1, 0x8, 0x4, 0x20, 0x1f, 0xfe, 0x0, 0x8, + 0x0, 0x20, 0x0, 0x80, 0x2, 0x0, 0x8, 0x7, + 0xc0, + + /* U+4E11 "丑" */ + 0x7f, 0xf8, 0x4, 0x10, 0x10, 0x20, 0x20, 0x40, + 0x40, 0x80, 0x81, 0xf, 0xfe, 0x2, 0x4, 0x4, + 0x8, 0x8, 0x10, 0x10, 0x20, 0x20, 0x4f, 0xff, + 0xe0, + + /* U+4E13 "专" */ + 0x2, 0x0, 0x4, 0x1, 0xff, 0xf0, 0x20, 0x0, + 0x80, 0x1f, 0xff, 0xc2, 0x0, 0x8, 0x0, 0x1f, + 0xf8, 0x0, 0x30, 0x40, 0xc0, 0x62, 0x0, 0x38, + 0x0, 0x18, 0x0, 0x8, 0x0, + + /* U+4E14 "且" */ + 0x3f, 0xf8, 0x40, 0x10, 0x80, 0x21, 0x0, 0x43, + 0xff, 0x84, 0x1, 0x8, 0x2, 0x10, 0x4, 0x3f, + 0xf8, 0x40, 0x10, 0x80, 0x21, 0x0, 0x4f, 0xff, + 0xe0, + + /* U+4E15 "丕" */ + 0xff, 0xfe, 0x3, 0x0, 0x4, 0x0, 0x10, 0x0, + 0x50, 0x1, 0x24, 0xc, 0x46, 0x30, 0x82, 0x81, + 0x2, 0x2, 0x0, 0x4, 0x0, 0x0, 0xf, 0xff, + 0xe0, + + /* U+4E16 "世" */ + 0x0, 0x8, 0x44, 0x10, 0x88, 0x21, 0x10, 0x42, + 0x20, 0x9f, 0xff, 0xc8, 0x82, 0x11, 0x4, 0x22, + 0x8, 0x44, 0x10, 0x87, 0xe1, 0x0, 0x2, 0x0, + 0x4, 0x0, 0xf, 0xff, 0x80, + + /* U+4E18 "丘" */ + 0x0, 0xc, 0x3f, 0xe0, 0x40, 0x0, 0x80, 0x1, + 0x0, 0x2, 0x0, 0x7, 0xff, 0x88, 0x10, 0x10, + 0x20, 0x20, 0x40, 0x40, 0x80, 0x81, 0x1, 0x2, + 0x1f, 0xff, 0xc0, + + /* U+4E19 "丙" */ + 0xff, 0xfe, 0x2, 0x0, 0x4, 0x3, 0xff, 0xe4, + 0x10, 0x48, 0x20, 0x90, 0x61, 0x21, 0x62, 0x42, + 0x64, 0x98, 0x69, 0x60, 0x52, 0x0, 0x24, 0x0, + 0x48, 0x7, 0x0, + + /* U+4E1A "业" */ + 0x4, 0x40, 0x8, 0x80, 0x11, 0x2, 0x22, 0x24, + 0x44, 0x48, 0x88, 0x89, 0x12, 0x12, 0x24, 0x14, + 0x50, 0x28, 0xa0, 0x11, 0x0, 0x22, 0x0, 0x44, + 0x1f, 0xff, 0xc0, + + /* U+4E1B "丛" */ + 0x8, 0x40, 0x20, 0x80, 0x41, 0x0, 0x82, 0x1, + 0x4, 0x3, 0xc, 0x5, 0x28, 0x9, 0x50, 0x20, + 0x90, 0x42, 0x11, 0xc, 0x34, 0x30, 0x30, 0x0, + 0x1f, 0xff, 0xc0, + + /* U+4E1C "东" */ + 0x4, 0x0, 0x10, 0x3, 0xff, 0xf8, 0x80, 0x1, + 0x8, 0x4, 0x10, 0x8, 0x20, 0x3f, 0xfe, 0x0, + 0x80, 0x1, 0x0, 0x42, 0x41, 0x4, 0x44, 0x8, + 0x50, 0x20, 0x43, 0xc0, 0x0, + + /* U+4E1D "丝" */ + 0x8, 0x10, 0x20, 0x40, 0x40, 0x81, 0x12, 0x24, + 0x48, 0xdf, 0xbf, 0x2, 0x4, 0x8, 0x10, 0x20, + 0x40, 0x41, 0x1, 0xf3, 0xf0, 0x0, 0x0, 0x0, + 0x1f, 0xff, 0xc0, + + /* U+4E1E "丞" */ + 0x3f, 0xf8, 0x0, 0x20, 0x3, 0x83, 0xe4, 0x20, + 0x54, 0x81, 0x26, 0x2, 0x48, 0x8, 0x88, 0x61, + 0xd, 0x82, 0x4, 0x1c, 0x0, 0x0, 0xf, 0xff, + 0xe0, + + /* U+4E22 "丢" */ + 0x7f, 0xfc, 0x2, 0x0, 0x4, 0x0, 0x8, 0x7, + 0xff, 0xc0, 0x20, 0x0, 0x40, 0x7f, 0xff, 0x6, + 0x0, 0x8, 0x80, 0x20, 0x81, 0x80, 0x87, 0xff, + 0x80, 0x0, 0x80, + + /* U+4E24 "两" */ + 0xff, 0xfe, 0x8, 0x40, 0x10, 0x83, 0xff, 0xe4, + 0x44, 0x48, 0x88, 0x91, 0x11, 0x23, 0x32, 0x49, + 0x54, 0x91, 0x39, 0x46, 0x12, 0x8, 0x24, 0x0, + 0x48, 0x7, 0x80, + + /* U+4E25 "严" */ + 0xff, 0xfe, 0x8, 0x80, 0x91, 0x30, 0xa2, 0xc0, + 0x44, 0xf, 0xff, 0xd0, 0x0, 0x20, 0x0, 0x40, + 0x0, 0x80, 0x1, 0x0, 0x4, 0x0, 0x8, 0x0, + 0x0, 0x0, 0x0, + + /* U+4E27 "丧" */ + 0x1, 0x0, 0x2, 0x3, 0xff, 0xf8, 0x8, 0x3, + 0x10, 0x81, 0x22, 0x0, 0x40, 0x7f, 0xff, 0x11, + 0x0, 0x22, 0x18, 0x42, 0xc0, 0x82, 0x1, 0x2, + 0x2, 0xe3, 0x8e, 0x0, 0x80, + + /* U+4E28 "丨" */ + 0xff, 0xfc, + + /* U+4E2A "个" */ + 0x0, 0x0, 0x2, 0x0, 0xa, 0x0, 0x22, 0x0, + 0x82, 0x2, 0x3, 0x18, 0x41, 0x40, 0x81, 0x1, + 0x0, 0x2, 0x0, 0x4, 0x0, 0x8, 0x0, 0x10, + 0x0, 0x20, 0x0, 0x40, 0x0, + + /* U+4E2B "丫" */ + 0x80, 0xb, 0x1, 0x8c, 0x18, 0x11, 0x0, 0x0, + 0x1, 0x0, 0x8, 0x0, 0x40, 0x2, 0x0, 0x10, + 0x0, 0x80, 0x4, 0x0, 0x20, 0x1, 0x0, + + /* U+4E2C "丬" */ + 0x2, 0x5, 0x9, 0x11, 0x22, 0x40, 0x81, 0xe, + 0x25, 0x8a, 0x10, 0x20, 0x40, 0x80, + + /* U+4E2D "中" */ + 0x2, 0x0, 0x10, 0x0, 0x81, 0xff, 0xf8, 0x20, + 0xc1, 0x6, 0x8, 0x30, 0x41, 0xff, 0xfc, 0x10, + 0x40, 0x80, 0x4, 0x0, 0x20, 0x1, 0x0, 0x8, + 0x0, + + /* U+4E30 "丰" */ + 0x1, 0x0, 0x2, 0x3, 0xff, 0xf8, 0x8, 0x0, + 0x10, 0x0, 0x20, 0x1f, 0xfe, 0x0, 0x80, 0x1, + 0x0, 0x2, 0x3, 0xff, 0xf8, 0x8, 0x0, 0x10, + 0x0, 0x20, 0x0, 0x40, 0x0, + + /* U+4E32 "串" */ + 0x2, 0x0, 0x10, 0x1f, 0xfc, 0x84, 0x24, 0x21, + 0x3f, 0xf8, 0x8, 0x0, 0x40, 0xff, 0xfc, 0x10, + 0x60, 0x83, 0xff, 0xf8, 0x20, 0x81, 0x0, 0x8, + 0x0, + + /* U+4E34 "临" */ + 0x10, 0x81, 0x22, 0x2, 0x47, 0xfc, 0x90, 0x9, + 0x64, 0x12, 0x84, 0x24, 0x0, 0x49, 0xfe, 0x92, + 0x45, 0x24, 0x8a, 0x49, 0x14, 0x92, 0x29, 0x24, + 0x52, 0x7f, 0x84, 0x81, 0x0, + + /* U+4E36 "丶" */ + 0xc3, 0xc, 0x20, 0x80, + + /* U+4E38 "丸" */ + 0x4, 0x0, 0x8, 0x0, 0x10, 0x7, 0xff, 0x0, + 0x42, 0x0, 0x84, 0x11, 0x8, 0x12, 0x10, 0x1c, + 0x20, 0x1c, 0x40, 0x2c, 0x80, 0x81, 0x11, 0x2, + 0x24, 0x4, 0x70, 0xf, 0x0, + + /* U+4E39 "丹" */ + 0x1f, 0xf8, 0x20, 0x10, 0x48, 0x20, 0x8c, 0x41, + 0xc, 0x82, 0x1, 0x3f, 0xff, 0x88, 0x4, 0x10, + 0x8, 0x40, 0x10, 0x80, 0x21, 0x0, 0x46, 0x0, + 0x88, 0xe, 0x0, + + /* U+4E3A "为" */ + 0x21, 0x0, 0xc4, 0x1, 0x90, 0x0, 0x40, 0xff, + 0xfc, 0xc, 0x10, 0x20, 0x40, 0x81, 0x2, 0x44, + 0x10, 0x90, 0x42, 0x42, 0x1, 0x10, 0x4, 0xc0, + 0x3c, 0xf, 0x80, + + /* U+4E3B "主" */ + 0x1, 0x0, 0x2, 0x0, 0x0, 0x3, 0xff, 0xe0, + 0x10, 0x0, 0x20, 0x0, 0x40, 0x0, 0x80, 0x7f, + 0xfc, 0x2, 0x0, 0x4, 0x0, 0x8, 0x0, 0x10, + 0x1f, 0xff, 0xc0, + + /* U+4E3D "丽" */ + 0xff, 0xfe, 0x0, 0x0, 0x0, 0x3, 0xf3, 0xe4, + 0x28, 0x48, 0x50, 0x94, 0xa9, 0x29, 0x4a, 0x4a, + 0x94, 0x85, 0x9, 0xa, 0x12, 0x14, 0x24, 0x28, + 0x49, 0x93, 0x80, + + /* U+4E3E "举" */ + 0x2, 0x8, 0x42, 0x30, 0x44, 0x40, 0x0, 0xf, + 0xff, 0xe1, 0x4, 0x6, 0x4c, 0x18, 0x84, 0xdf, + 0xf7, 0x2, 0x0, 0x4, 0x3, 0xff, 0xe0, 0x10, + 0x0, 0x20, 0x0, 0x40, 0x0, + + /* U+4E3F "丿" */ + 0x4, 0x10, 0x41, 0x4, 0x10, 0x41, 0x4, 0x20, + 0x84, 0x31, 0x80, 0x0, + + /* U+4E43 "乃" */ + 0x7f, 0xf0, 0x20, 0x40, 0x82, 0x2, 0x8, 0x8, + 0x40, 0x21, 0xf0, 0x80, 0x44, 0x1, 0x10, 0x4, + 0x40, 0x12, 0x0, 0x48, 0x1, 0x40, 0xf, 0x3, + 0xe0, + + /* U+4E45 "久" */ + 0x4, 0x0, 0x4, 0x0, 0xf, 0xe0, 0x8, 0x20, + 0x10, 0x20, 0x20, 0x40, 0x60, 0x40, 0x0, 0x80, + 0x1, 0xc0, 0x1, 0x40, 0x2, 0x20, 0xc, 0x10, + 0x18, 0x8, 0x60, 0x6, 0x40, 0x2, + + /* U+4E47 "乇" */ + 0x0, 0x8, 0xff, 0xf0, 0x8, 0x0, 0x10, 0x0, + 0x20, 0x0, 0x40, 0x0, 0xff, 0xff, 0x0, 0x2, + 0x0, 0x4, 0x0, 0x8, 0x10, 0x10, 0x20, 0x20, + 0x40, 0x40, 0x80, 0x7e, 0x0, + + /* U+4E48 "么" */ + 0x1, 0x0, 0x2, 0x0, 0x8, 0x0, 0x20, 0x80, + 0xc2, 0x3, 0x8, 0xc, 0x10, 0x30, 0x40, 0x41, + 0x0, 0x4, 0x40, 0x10, 0x40, 0x40, 0x41, 0x0, + 0x47, 0xff, 0x80, 0x0, 0x80, + + /* U+4E49 "义" */ + 0x1, 0x0, 0x22, 0x10, 0x42, 0x20, 0x84, 0x40, + 0x81, 0x1, 0x2, 0x2, 0xc, 0x2, 0x10, 0x2, + 0x40, 0x7, 0x80, 0x6, 0x0, 0x1e, 0x0, 0xc3, + 0x6, 0x1, 0xd0, 0x0, 0x80, + + /* U+4E4B "之" */ + 0x1, 0x0, 0x1, 0x0, 0x0, 0x80, 0x7f, 0xfc, + 0x0, 0xc, 0x0, 0x18, 0x0, 0x20, 0x0, 0xc0, + 0x1, 0x80, 0x6, 0x0, 0x8, 0x0, 0x70, 0x0, + 0xd8, 0x0, 0x87, 0xfe, + + /* U+4E4C "乌" */ + 0x2, 0x0, 0x10, 0x3, 0xff, 0x8, 0x4, 0x20, + 0x10, 0x80, 0x42, 0x1f, 0x8, 0x0, 0x20, 0x0, + 0xff, 0xf0, 0x0, 0x7f, 0xf9, 0x0, 0x4, 0x0, + 0x10, 0x0, 0xc0, 0x3e, + + /* U+4E4D "乍" */ + 0x8, 0x0, 0x10, 0x0, 0x7f, 0xf8, 0x90, 0x2, + 0x20, 0x8, 0x40, 0x20, 0xff, 0x1, 0x0, 0x2, + 0x0, 0x4, 0x0, 0xf, 0xf8, 0x10, 0x0, 0x20, + 0x0, 0x40, 0x0, 0x80, 0x0, + + /* U+4E4E "乎" */ + 0x7f, 0xfc, 0x2, 0x0, 0x84, 0x20, 0x88, 0x81, + 0x93, 0x0, 0x20, 0x3f, 0xff, 0x80, 0x80, 0x1, + 0x0, 0x2, 0x0, 0x4, 0x0, 0x8, 0x0, 0x10, + 0x1, 0xc0, 0x0, + + /* U+4E4F "乏" */ + 0x0, 0x0, 0x7f, 0xfc, 0x0, 0x0, 0x1, 0x0, + 0x0, 0x80, 0x0, 0x80, 0x3f, 0xfc, 0x0, 0x8, + 0x0, 0x30, 0x0, 0xc0, 0x3, 0x0, 0xc, 0x0, + 0x38, 0x0, 0x6c, 0x0, 0x47, 0xfe, + + /* U+4E50 "乐" */ + 0x0, 0x8, 0x7f, 0xe0, 0x80, 0x2, 0x0, 0x4, + 0x10, 0x8, 0x20, 0x10, 0x40, 0x3f, 0xfe, 0x1, + 0x0, 0x22, 0x0, 0x44, 0x41, 0x8, 0x44, 0x10, + 0x50, 0x20, 0x43, 0xc0, 0x0, + + /* U+4E52 "乒" */ + 0x0, 0x8, 0x3f, 0xe0, 0x40, 0x0, 0x80, 0x1, + 0x0, 0x3, 0xff, 0xc4, 0x8, 0x8, 0x10, 0x10, + 0x21, 0xff, 0xfc, 0x0, 0x0, 0x20, 0x1, 0x80, + 0xc, 0x0, 0x0, 0x0, 0x0, + + /* U+4E53 "乓" */ + 0x0, 0x8, 0x3f, 0xe0, 0x40, 0x0, 0x80, 0x1, + 0x0, 0x3, 0xff, 0xc4, 0x8, 0x8, 0x10, 0x10, + 0x21, 0xff, 0xfc, 0x0, 0x0, 0x3, 0x0, 0x1, + 0x0, 0x1, 0x80, 0x0, 0x0, + + /* U+4E54 "乔" */ + 0x7f, 0xfc, 0x2, 0x0, 0x4, 0x0, 0x8, 0xf, + 0xff, 0xe0, 0xc8, 0x3, 0x18, 0x8, 0x8, 0xc8, + 0x2e, 0x10, 0x40, 0x20, 0x80, 0x41, 0x3, 0x2, + 0x8, 0x4, 0x0, + + /* U+4E56 "乖" */ + 0x7f, 0xfc, 0x2, 0x0, 0x4, 0x7, 0xff, 0xf0, + 0x10, 0x2, 0x24, 0x4, 0x49, 0x78, 0x9c, 0x11, + 0x20, 0x22, 0x47, 0xc4, 0x88, 0x88, 0xe0, 0x10, + 0x0, 0x20, 0x0, + + /* U+4E58 "乘" */ + 0x7f, 0xfc, 0x2, 0x3, 0xff, 0xf8, 0x8, 0x1, + 0x12, 0x1e, 0x27, 0x84, 0x48, 0xb9, 0x91, 0x95, + 0xbc, 0xa, 0x80, 0x64, 0x81, 0x8, 0xcc, 0x10, + 0x60, 0x20, 0x0, + + /* U+4E59 "乙" */ + 0x7f, 0xf0, 0x0, 0x80, 0xc, 0x0, 0x60, 0x2, + 0x0, 0x10, 0x0, 0x80, 0xc, 0x0, 0x60, 0x7, + 0x0, 0x18, 0x0, 0xa0, 0x2, 0x7f, 0xf0, + + /* U+4E5C "乜" */ + 0x10, 0x0, 0x20, 0x0, 0x41, 0xe0, 0xbc, 0x4f, + 0x80, 0x82, 0x1, 0x4, 0x2, 0x8, 0xc, 0x11, + 0xf0, 0x20, 0x0, 0x40, 0x8, 0x80, 0x11, 0x0, + 0x61, 0xff, 0x80, + + /* U+4E5D "九" */ + 0x4, 0x0, 0x8, 0x0, 0x10, 0x7, 0xff, 0x0, + 0x42, 0x0, 0x84, 0x1, 0x8, 0x2, 0x10, 0x4, + 0x20, 0x18, 0x40, 0x20, 0x80, 0xc1, 0x11, 0x2, + 0x24, 0x4, 0x70, 0xf, 0x0, + + /* U+4E5E "乞" */ + 0x8, 0x0, 0x20, 0x0, 0xff, 0xf9, 0x0, 0x4, + 0x0, 0x3, 0xfe, 0x0, 0x18, 0x0, 0xc0, 0x3, + 0x0, 0x18, 0x8, 0x40, 0x11, 0x0, 0x22, 0x0, + 0x43, 0xff, 0x0, + + /* U+4E5F "也" */ + 0x1, 0x0, 0x2, 0x0, 0x84, 0x1, 0x8, 0x42, + 0x1f, 0x87, 0xe1, 0x38, 0x42, 0x10, 0x84, 0x21, + 0x8, 0x42, 0xe0, 0x84, 0x9, 0x0, 0x12, 0x0, + 0x24, 0x0, 0x87, 0xff, 0x0, + + /* U+4E60 "习" */ + 0xff, 0xf8, 0x0, 0x40, 0x2, 0x60, 0x10, 0xc0, + 0x81, 0x84, 0x0, 0x20, 0x9, 0x3, 0x88, 0xe0, + 0x78, 0x2, 0x0, 0x10, 0x1, 0x81, 0xf8, + + /* U+4E61 "乡" */ + 0x3, 0x0, 0x20, 0x82, 0xc, 0x60, 0x87, 0xf8, + 0x0, 0x80, 0x8, 0x21, 0x81, 0x10, 0x13, 0xff, + 0x80, 0x8, 0x0, 0x80, 0x18, 0x3, 0x3, 0xe0, + 0x0, 0x0, + + /* U+4E66 "书" */ + 0x4, 0x0, 0x10, 0xc0, 0x40, 0xbf, 0xf8, 0x4, + 0x20, 0x10, 0x80, 0x42, 0x3f, 0xfe, 0x4, 0x8, + 0x10, 0x20, 0x40, 0x81, 0x2, 0x4, 0xf0, 0x10, + 0x0, 0x40, 0x0, + + /* U+4E69 "乩" */ + 0x10, 0x40, 0x20, 0x80, 0x41, 0x0, 0xfa, 0x1, + 0x4, 0x2, 0x8, 0x4, 0x10, 0x8, 0x20, 0xfe, + 0x41, 0x4, 0x82, 0x9, 0x4, 0x12, 0x18, 0x24, + 0x3f, 0xc8, 0xa0, 0x9f, 0x0, + + /* U+4E70 "买" */ + 0x7f, 0xfc, 0x0, 0x8, 0x61, 0x10, 0x24, 0x40, + 0x28, 0x6, 0x10, 0x2, 0x20, 0x0, 0x40, 0xff, + 0xfe, 0x1, 0x0, 0x7, 0x0, 0x31, 0x81, 0x80, + 0xcc, 0x0, 0x0, + + /* U+4E71 "乱" */ + 0x0, 0x41, 0xfc, 0x80, 0x41, 0x0, 0x82, 0x1, + 0x4, 0x1f, 0xe8, 0x4, 0x10, 0x8, 0x20, 0xfe, + 0x41, 0x4, 0x82, 0x9, 0xc, 0x12, 0x18, 0x24, + 0x5f, 0xc8, 0xa0, 0x9f, 0x0, + + /* U+4E73 "乳" */ + 0x0, 0x20, 0x7f, 0x20, 0x11, 0x20, 0x4a, 0x20, + 0x2, 0x20, 0x7f, 0x20, 0x3, 0x20, 0x6, 0x20, + 0x8, 0x20, 0x8, 0xa0, 0xff, 0x21, 0x8, 0x21, + 0x8, 0x21, 0x8, 0x22, 0x38, 0x3e, + + /* U+4E7E "乾" */ + 0x10, 0x40, 0xfe, 0x40, 0x10, 0x7e, 0x10, 0x80, + 0x7d, 0x80, 0x85, 0x0, 0x7c, 0x7c, 0x84, 0x8, + 0x84, 0x10, 0x7c, 0x20, 0x10, 0x41, 0xfe, 0x42, + 0x10, 0x82, 0x10, 0x82, 0x10, 0xfe, + + /* U+4E86 "了" */ + 0xff, 0xfc, 0x0, 0x30, 0x3, 0x0, 0x18, 0x1, + 0x80, 0x4, 0x0, 0x10, 0x0, 0x40, 0x1, 0x0, + 0x4, 0x0, 0x10, 0x0, 0x40, 0x1, 0x0, 0x78, + 0x0, + + /* U+4E88 "予" */ + 0x7f, 0xf0, 0x0, 0x81, 0x8c, 0x1, 0xc0, 0x1, + 0x3, 0xff, 0xf0, 0x20, 0x80, 0x82, 0x2, 0x18, + 0x8, 0x0, 0x20, 0x0, 0x80, 0x2, 0x0, 0x78, + 0x0, + + /* U+4E89 "争" */ + 0x8, 0x0, 0x10, 0x0, 0x7f, 0x81, 0x2, 0x4, + 0xc, 0x17, 0xff, 0x0, 0x82, 0x1, 0x4, 0xff, + 0xfe, 0x4, 0x10, 0x8, 0x23, 0xff, 0xc0, 0x20, + 0x80, 0x40, 0x0, 0x80, 0xf, 0x0, + + /* U+4E8B "事" */ + 0x1, 0x1, 0xff, 0xfc, 0x4, 0x3, 0xff, 0xc4, + 0x10, 0x8f, 0xff, 0x0, 0x40, 0x3f, 0xfc, 0x1, + 0x9, 0xff, 0xfc, 0x4, 0x23, 0xff, 0xc0, 0x10, + 0x80, 0x20, 0x7, 0x80, 0x0, + + /* U+4E8C "二" */ + 0x7f, 0xfc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x3, 0xff, 0xf8, + + /* U+4E8D "亍" */ + 0x3f, 0xf8, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf, + 0xff, 0xe0, 0x20, 0x0, 0x40, 0x0, 0x80, 0x1, + 0x0, 0x2, 0x0, 0x4, 0x0, 0x8, 0x0, 0x10, + 0x1, 0xe0, 0x0, + + /* U+4E8E "于" */ + 0x7f, 0xfc, 0x2, 0x0, 0x4, 0x0, 0x8, 0x0, + 0x10, 0x1f, 0xff, 0xc0, 0x40, 0x0, 0x80, 0x1, + 0x0, 0x2, 0x0, 0x4, 0x0, 0x8, 0x0, 0x10, + 0x1, 0xe0, 0x0, + + /* U+4E8F "亏" */ + 0x3f, 0xfc, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf, + 0xff, 0xe1, 0x0, 0x4, 0x0, 0xf, 0xfc, 0x0, + 0x8, 0x0, 0x10, 0x0, 0x20, 0x0, 0x40, 0x1, + 0x80, 0x3e, 0x0, + + /* U+4E91 "云" */ + 0x7f, 0xfc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x1f, 0xff, 0xc0, 0x80, 0x2, 0x0, 0x8, + 0x60, 0x20, 0x40, 0x80, 0x43, 0xff, 0xc0, 0x0, + 0x40, + + /* U+4E92 "互" */ + 0x7f, 0xfc, 0x20, 0x0, 0x40, 0x0, 0x80, 0x1, + 0xff, 0x2, 0x2, 0x8, 0x4, 0x10, 0x8, 0x3f, + 0xf0, 0x0, 0x20, 0x0, 0x40, 0x0, 0x8f, 0xff, + 0xe0, + + /* U+4E93 "亓" */ + 0x7f, 0xfc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x1f, 0xff, 0xc2, 0x8, 0x4, 0x10, 0x8, + 0x20, 0x10, 0x40, 0x40, 0x80, 0x81, 0x2, 0x2, + 0x18, 0x4, 0x0, + + /* U+4E94 "五" */ + 0xff, 0xfc, 0x8, 0x0, 0x10, 0x0, 0x20, 0x0, + 0x80, 0x1f, 0xfe, 0x2, 0x4, 0x4, 0x8, 0x8, + 0x10, 0x10, 0x20, 0x40, 0x40, 0x80, 0x8f, 0xff, + 0xe0, + + /* U+4E95 "井" */ + 0x8, 0x20, 0x10, 0x40, 0x20, 0x87, 0xff, 0xf0, + 0x82, 0x1, 0x4, 0x2, 0x8, 0x4, 0x10, 0xff, + 0xfe, 0x10, 0x40, 0x20, 0x80, 0x41, 0x1, 0x2, + 0x4, 0x4, 0x10, 0x8, 0x0, + + /* U+4E98 "亘" */ + 0xff, 0xfc, 0x0, 0x0, 0xff, 0xe1, 0x0, 0x42, + 0x0, 0x84, 0x1, 0xf, 0xfe, 0x10, 0x4, 0x20, + 0x8, 0x7f, 0xf0, 0x0, 0x0, 0x0, 0xf, 0xff, + 0xe0, + + /* U+4E9A "亚" */ + 0x7f, 0xfc, 0x8, 0x80, 0x11, 0x2, 0x22, 0x24, + 0x44, 0x44, 0x89, 0x9, 0x12, 0x12, 0x24, 0x14, + 0x50, 0x8, 0x80, 0x11, 0x0, 0x22, 0xf, 0xff, + 0xe0, + + /* U+4E9B "些" */ + 0x8, 0x81, 0x22, 0x4, 0x88, 0x93, 0xec, 0x48, + 0xc1, 0x22, 0x4, 0x88, 0x53, 0xe1, 0xf0, 0x7c, + 0x0, 0x3, 0xff, 0x0, 0x0, 0x0, 0x3, 0xff, + 0xf0, + + /* U+4E9F "亟" */ + 0x7f, 0xf8, 0x0, 0x60, 0x3, 0x3, 0xcb, 0xe4, + 0x90, 0x49, 0x28, 0x92, 0x4e, 0x24, 0x8c, 0x79, + 0x2c, 0x2, 0x88, 0x3c, 0x0, 0x0, 0xf, 0xff, + 0xe0, + + /* U+4EA0 "亠" */ + 0x2, 0x0, 0x2, 0x0, 0x4, 0x0, 0x0, 0xf, + 0xff, 0xe0, + + /* U+4EA1 "亡" */ + 0x1, 0x0, 0x1, 0x0, 0x2, 0x7, 0xff, 0xf2, + 0x0, 0x4, 0x0, 0x8, 0x0, 0x10, 0x0, 0x20, + 0x0, 0x40, 0x0, 0x80, 0x1, 0x0, 0x2, 0x0, + 0x7, 0xff, 0xc0, + + /* U+4EA2 "亢" */ + 0x0, 0x0, 0x8, 0x0, 0x10, 0x3f, 0xff, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x7, 0xf0, 0x10, 0x40, + 0x41, 0x1, 0x4, 0x4, 0x10, 0x10, 0x40, 0x81, + 0x16, 0x4, 0x70, 0xf, 0x0, 0x0, + + /* U+4EA4 "交" */ + 0x1, 0x0, 0x2, 0x3, 0xff, 0xf8, 0x0, 0x0, + 0x86, 0x2, 0x2, 0x8, 0x3, 0x64, 0x10, 0x4, + 0x40, 0x9, 0x80, 0xa, 0x0, 0x1c, 0x0, 0xc6, + 0xe, 0x3, 0xa0, 0x0, 0x80, + + /* U+4EA5 "亥" */ + 0x1, 0x0, 0x2, 0x3, 0xff, 0xf8, 0x20, 0x0, + 0x82, 0x2, 0x8, 0x1f, 0xe2, 0x0, 0x8c, 0x2, + 0x10, 0x18, 0x40, 0xc3, 0x6, 0xf, 0x0, 0x61, + 0x83, 0x1, 0x88, 0x0, 0x0, + + /* U+4EA6 "亦" */ + 0x1, 0x0, 0x3, 0x0, 0x2, 0x7, 0xff, 0xf0, + 0x24, 0x0, 0x48, 0x9, 0x12, 0x12, 0x24, 0x44, + 0x44, 0x88, 0x8a, 0x11, 0x8, 0x22, 0x0, 0x84, + 0x3, 0x8, 0xc, 0x10, 0x20, 0xe0, + + /* U+4EA7 "产" */ + 0x1, 0x0, 0x2, 0x3, 0xff, 0xf8, 0x0, 0x0, + 0x82, 0x0, 0x88, 0x1f, 0xff, 0x20, 0x0, 0x40, + 0x0, 0x80, 0x1, 0x0, 0x2, 0x0, 0x4, 0x0, + 0x10, 0x0, 0x20, 0x0, 0x0, + + /* U+4EA8 "亨" */ + 0x1, 0x0, 0x1, 0x3, 0xff, 0xf8, 0x0, 0x1, + 0xff, 0x84, 0x1, 0x7, 0xfe, 0x0, 0x0, 0x0, + 0x0, 0xff, 0xf8, 0x0, 0xe0, 0x6, 0x0, 0x8, + 0x0, 0x10, 0x1, 0xc0, 0x0, + + /* U+4EA9 "亩" */ + 0x1, 0x0, 0x2, 0x3, 0xff, 0xf8, 0x0, 0x0, + 0x0, 0xf, 0xff, 0x90, 0x41, 0x20, 0x82, 0x41, + 0x4, 0xff, 0xf9, 0x4, 0x12, 0x8, 0x24, 0x10, + 0x4f, 0xff, 0x90, 0x1, 0x0, + + /* U+4EAB "享" */ + 0x1, 0x0, 0x2, 0x3, 0xff, 0xf8, 0x0, 0x3, + 0xff, 0x84, 0x1, 0xf, 0xfe, 0x0, 0x0, 0x7f, + 0xf8, 0x0, 0xe0, 0x6, 0x7, 0xff, 0xf0, 0x10, + 0x0, 0x20, 0x3, 0xc0, 0x0, + + /* U+4EAC "京" */ + 0x1, 0x0, 0x2, 0x3, 0xff, 0xf8, 0x0, 0x0, + 0x0, 0x7, 0xff, 0x8, 0x2, 0x10, 0x4, 0x3f, + 0xf8, 0x2, 0x0, 0x4, 0x0, 0x88, 0x86, 0x10, + 0xd0, 0x20, 0x1, 0xc0, 0x0, + + /* U+4EAD "亭" */ + 0x0, 0x0, 0x8, 0xf, 0xff, 0xc0, 0x0, 0x3f, + 0xf0, 0x80, 0x43, 0xff, 0x0, 0x0, 0x0, 0x3, + 0xff, 0xf8, 0x0, 0x6f, 0xfd, 0x1, 0x0, 0x4, + 0x0, 0x10, 0x3, 0x80, + + /* U+4EAE "亮" */ + 0x2, 0x0, 0x8, 0xf, 0xff, 0xc0, 0x0, 0x3f, + 0xf0, 0x80, 0x42, 0x1, 0xf, 0xfc, 0x0, 0x3, + 0xff, 0xf8, 0x0, 0x43, 0xf0, 0x10, 0x40, 0x41, + 0x11, 0x4, 0x78, 0x1f, + + /* U+4EB2 "亲" */ + 0x1, 0x0, 0x2, 0x1, 0xff, 0xf0, 0x40, 0x0, + 0x82, 0x0, 0x88, 0x3f, 0xff, 0x80, 0x80, 0x1, + 0x0, 0xff, 0xf8, 0x4, 0x0, 0xc9, 0x2, 0x11, + 0x98, 0x20, 0x83, 0xc0, 0x0, + + /* U+4EB3 "亳" */ + 0x1, 0x1, 0xff, 0xfc, 0x0, 0x1, 0xff, 0xc2, + 0x0, 0x87, 0xff, 0x0, 0x0, 0x7f, 0xff, 0x80, + 0x2, 0x7f, 0xe0, 0x8, 0x7, 0xff, 0xe0, 0x20, + 0x20, 0x40, 0x80, 0x7f, 0x0, + + /* U+4EB5 "亵" */ + 0x1, 0x1, 0xff, 0xfc, 0x0, 0x0, 0x82, 0xf, + 0xdf, 0x82, 0x9, 0xf, 0x22, 0x28, 0xf5, 0x12, + 0x6, 0xc3, 0x0, 0xa, 0x20, 0x62, 0x87, 0x82, + 0x11, 0x33, 0xc3, 0x80, 0x0, + + /* U+4EBA "人" */ + 0x1, 0x0, 0x2, 0x0, 0x4, 0x0, 0x8, 0x0, + 0x10, 0x0, 0x60, 0x0, 0xa0, 0x1, 0x40, 0x4, + 0x40, 0x8, 0x80, 0x20, 0x80, 0xc1, 0x83, 0x1, + 0x88, 0x0, 0xe0, 0x0, 0x0, + + /* U+4EBB "亻" */ + 0x2, 0x4, 0x10, 0x40, 0x83, 0xa, 0x24, 0x88, + 0x10, 0x20, 0x40, 0x81, 0x2, 0x0, + + /* U+4EBF "亿" */ + 0x10, 0x0, 0x27, 0xf8, 0xc0, 0x21, 0x0, 0x82, + 0x3, 0xc, 0x4, 0x28, 0x10, 0x50, 0x40, 0x21, + 0x0, 0x44, 0x4, 0x88, 0x9, 0x20, 0x12, 0x40, + 0x64, 0xff, 0x88, 0x0, 0x0, + + /* U+4EC0 "什" */ + 0x4, 0x20, 0xc, 0x20, 0x8, 0x20, 0x10, 0x20, + 0x10, 0x20, 0x30, 0x20, 0x33, 0xff, 0x50, 0x20, + 0x10, 0x20, 0x10, 0x20, 0x10, 0x20, 0x10, 0x20, + 0x10, 0x20, 0x10, 0x20, 0x10, 0x20, + + /* U+4EC1 "仁" */ + 0x8, 0x0, 0x8, 0x0, 0x19, 0xfe, 0x10, 0x0, + 0x30, 0x0, 0x30, 0x0, 0x50, 0x0, 0x50, 0x0, + 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, 0x10, 0x0, + 0x13, 0xff, 0x10, 0x0, 0x10, 0x0, + + /* U+4EC2 "仂" */ + 0x10, 0x80, 0x42, 0x2, 0x8, 0x9, 0xff, 0x60, + 0x85, 0x82, 0x1a, 0x8, 0x68, 0x21, 0x20, 0x84, + 0x86, 0x12, 0x10, 0x48, 0x41, 0x22, 0x4, 0x90, + 0x12, 0x87, 0x80, + + /* U+4EC3 "仃" */ + 0x10, 0x0, 0x2f, 0xfc, 0x80, 0x41, 0x0, 0x86, + 0x1, 0xc, 0x2, 0x28, 0x4, 0x50, 0x8, 0x20, + 0x10, 0x40, 0x20, 0x80, 0x41, 0x0, 0x82, 0x1, + 0x4, 0x4, 0x8, 0x78, 0x0, + + /* U+4EC4 "仄" */ + 0x3f, 0xfe, 0x20, 0x0, 0x20, 0x40, 0x20, 0x40, + 0x20, 0x40, 0x20, 0x40, 0x20, 0xa0, 0x20, 0xa0, + 0x20, 0xa0, 0x21, 0x10, 0x21, 0x18, 0x42, 0xc, + 0x4c, 0x6, 0x98, 0x0, + + /* U+4EC5 "仅" */ + 0x10, 0x0, 0x2f, 0xf8, 0x48, 0x11, 0x10, 0x22, + 0x20, 0x8c, 0x41, 0x28, 0x42, 0x50, 0x88, 0x20, + 0x90, 0x41, 0x40, 0x81, 0x1, 0x5, 0x2, 0x11, + 0x84, 0xc1, 0x8a, 0x0, 0x80, + + /* U+4EC6 "仆" */ + 0x8, 0x40, 0x8, 0x40, 0x18, 0x40, 0x10, 0x40, + 0x30, 0x40, 0x30, 0x50, 0x50, 0x48, 0xd0, 0x46, + 0x10, 0x42, 0x10, 0x40, 0x10, 0x40, 0x10, 0x40, + 0x10, 0x40, 0x10, 0x40, 0x10, 0x40, + + /* U+4EC7 "仇" */ + 0x11, 0x0, 0x22, 0x0, 0x84, 0x1, 0x3f, 0x82, + 0x11, 0xc, 0x22, 0x18, 0x44, 0x50, 0x88, 0x21, + 0x10, 0x42, 0x20, 0x88, 0x41, 0x10, 0x92, 0x41, + 0x24, 0x82, 0x4a, 0x7, 0x80, + + /* U+4EC9 "仉" */ + 0x10, 0x0, 0x27, 0xe0, 0x48, 0x41, 0x10, 0x82, + 0x21, 0xc, 0x42, 0x18, 0x84, 0x51, 0x8, 0x22, + 0x10, 0x44, 0x20, 0x88, 0x41, 0x20, 0x92, 0x41, + 0x25, 0x2, 0x4a, 0x7, 0x0, + + /* U+4ECA "今" */ + 0x0, 0x80, 0x3, 0x0, 0x9, 0x0, 0x23, 0x0, + 0x83, 0x6, 0x21, 0x10, 0x41, 0x80, 0x40, 0x0, + 0x0, 0x7f, 0xf0, 0x0, 0x20, 0x0, 0x80, 0x2, + 0x0, 0x8, 0x0, 0x30, 0x0, 0x0, + + /* U+4ECB "介" */ + 0x1, 0x0, 0x5, 0x0, 0x11, 0x0, 0x41, 0x1, + 0x1, 0xc, 0x1, 0xb2, 0x8, 0x84, 0x10, 0x8, + 0x20, 0x10, 0x40, 0x20, 0x80, 0x81, 0x3, 0x2, + 0xc, 0x4, 0x0, + + /* U+4ECD "仍" */ + 0x10, 0x0, 0x5f, 0xe2, 0x10, 0x88, 0x46, 0x21, + 0x11, 0x84, 0x8a, 0x13, 0xe8, 0x41, 0x21, 0x4, + 0x88, 0x12, 0x20, 0x48, 0x81, 0x24, 0x4, 0x90, + 0x12, 0x87, 0x80, + + /* U+4ECE "从" */ + 0x10, 0x60, 0x10, 0x60, 0x10, 0x60, 0x10, 0x60, + 0x10, 0x60, 0x10, 0x60, 0x18, 0x60, 0x34, 0x60, + 0x22, 0xe0, 0x20, 0x90, 0x21, 0x90, 0x41, 0x8, + 0x42, 0x4, 0x86, 0x2, 0x0, 0x0, + + /* U+4ED1 "仑" */ + 0x1, 0x0, 0x7, 0x0, 0xa, 0x0, 0x63, 0x1, + 0x83, 0x4, 0x1, 0xb4, 0x0, 0x88, 0x18, 0x10, + 0xc0, 0x2e, 0x0, 0x60, 0x0, 0x80, 0x41, 0x0, + 0x82, 0x1, 0x3, 0xfc, 0x0, + + /* U+4ED3 "仓" */ + 0x0, 0x80, 0x3, 0x0, 0x9, 0x0, 0x21, 0x1, + 0x81, 0x6, 0x1, 0xb7, 0xfc, 0x88, 0x8, 0x10, + 0x10, 0x20, 0x20, 0x40, 0x40, 0x87, 0x21, 0x0, + 0x42, 0x0, 0x84, 0x2, 0x7, 0xfc, + + /* U+4ED4 "仔" */ + 0x10, 0x0, 0x2f, 0xf8, 0x80, 0x31, 0x0, 0xc2, + 0x3, 0xc, 0x4, 0x28, 0x10, 0x57, 0xff, 0x20, + 0x40, 0x40, 0x80, 0x81, 0x1, 0x2, 0x2, 0x4, + 0x4, 0x8, 0x8, 0xf0, 0x0, + + /* U+4ED5 "仕" */ + 0x4, 0x0, 0x8, 0x20, 0x8, 0x20, 0x10, 0x20, + 0x10, 0x20, 0x33, 0xff, 0x50, 0x20, 0x50, 0x20, + 0x10, 0x20, 0x10, 0x20, 0x10, 0x20, 0x10, 0x20, + 0x10, 0x20, 0x13, 0xff, 0x10, 0x0, + + /* U+4ED6 "他" */ + 0x10, 0x40, 0x20, 0x80, 0x51, 0x1, 0x22, 0x22, + 0x47, 0xcc, 0xf8, 0xbb, 0x11, 0x52, 0x22, 0x24, + 0x44, 0x48, 0xb0, 0x91, 0x9, 0x20, 0x12, 0x40, + 0x24, 0x80, 0x88, 0xff, 0x0, + + /* U+4ED7 "仗" */ + 0x10, 0x20, 0x20, 0x40, 0x40, 0x81, 0x7f, 0xf2, + 0x2, 0xc, 0x84, 0x19, 0x8, 0x51, 0x10, 0x22, + 0x20, 0x42, 0x80, 0x87, 0x1, 0x6, 0x2, 0x12, + 0x4, 0xc3, 0x8a, 0x0, 0x80, + + /* U+4ED8 "付" */ + 0x10, 0x10, 0x40, 0x41, 0x1, 0x8, 0x4, 0x2f, + 0xfd, 0x80, 0x46, 0x1, 0x29, 0x4, 0x22, 0x10, + 0x84, 0x42, 0x19, 0x8, 0x4, 0x20, 0x10, 0x80, + 0x42, 0xe, 0x0, + + /* U+4ED9 "仙" */ + 0x10, 0x40, 0x41, 0x1, 0x4, 0x8, 0x10, 0x24, + 0x45, 0x91, 0x16, 0x44, 0x69, 0x11, 0xa4, 0x44, + 0x91, 0x12, 0x44, 0x49, 0x11, 0x24, 0x44, 0x9f, + 0xf2, 0x0, 0x40, + + /* U+4EDD "仝" */ + 0x0, 0x80, 0x1, 0x80, 0x2, 0x40, 0x6, 0x60, + 0xc, 0x30, 0x10, 0x18, 0x60, 0x6, 0x40, 0x2, + 0x1f, 0xf8, 0x1, 0x0, 0x1, 0x0, 0x1, 0x0, + 0x1, 0x0, 0x1, 0x0, 0x7f, 0xfe, + + /* U+4EDE "仞" */ + 0x10, 0x0, 0x4f, 0xf1, 0x4, 0x48, 0x11, 0x20, + 0x45, 0x91, 0x16, 0x44, 0x69, 0x11, 0xa8, 0x44, + 0x83, 0x12, 0x8, 0x48, 0x21, 0x21, 0x4, 0x88, + 0x12, 0x47, 0x80, + + /* U+4EDF "仟" */ + 0x10, 0x4, 0x2f, 0xe0, 0x81, 0x1, 0x2, 0x6, + 0x4, 0xc, 0x8, 0x28, 0x10, 0x53, 0xff, 0x20, + 0x40, 0x40, 0x80, 0x81, 0x1, 0x2, 0x2, 0x4, + 0x4, 0x8, 0x8, 0x10, 0x0, + + /* U+4EE1 "仡" */ + 0x11, 0x0, 0x22, 0x0, 0x8f, 0xf9, 0x10, 0x6, + 0x40, 0xc, 0x7f, 0x28, 0x6, 0x10, 0x18, 0x20, + 0x60, 0x41, 0x80, 0x82, 0x9, 0x8, 0x12, 0x20, + 0x24, 0x40, 0x48, 0xff, 0x0, + + /* U+4EE3 "代" */ + 0x10, 0x80, 0x21, 0x20, 0x42, 0x21, 0x4, 0x22, + 0x8, 0x2d, 0xff, 0xa8, 0x20, 0x50, 0x40, 0x20, + 0x40, 0x40, 0x80, 0x81, 0x1, 0x2, 0x12, 0x2, + 0x24, 0x6, 0x48, 0x7, 0x0, + + /* U+4EE4 "令" */ + 0x1, 0x0, 0x3, 0x0, 0x9, 0x0, 0x21, 0x1, + 0x81, 0x4, 0x21, 0xb0, 0x20, 0x80, 0x0, 0x3f, + 0xf8, 0x0, 0x30, 0x0, 0xc0, 0x63, 0x0, 0x2c, + 0x0, 0x30, 0x0, 0x10, 0x0, + + /* U+4EE5 "以" */ + 0x44, 0x10, 0x88, 0x21, 0x8, 0x42, 0x8, 0x84, + 0x1, 0x8, 0x2, 0x10, 0x4, 0x20, 0x8, 0x40, + 0x20, 0x88, 0x41, 0x31, 0x82, 0x82, 0x86, 0x18, + 0x80, 0x60, 0xc1, 0x0, 0x0, + + /* U+4EE8 "仨" */ + 0x10, 0x0, 0x20, 0x0, 0x4f, 0xf1, 0x0, 0x2, + 0x0, 0xc, 0x0, 0x18, 0x0, 0x51, 0xfe, 0x20, + 0x0, 0x40, 0x0, 0x80, 0x1, 0x0, 0x2, 0x0, + 0x4, 0xff, 0xc8, 0x0, 0x0, + + /* U+4EEA "仪" */ + 0x8, 0x0, 0x20, 0x80, 0x49, 0x11, 0x11, 0x22, + 0x20, 0x4c, 0x41, 0x18, 0x42, 0x50, 0x84, 0x20, + 0x90, 0x41, 0x40, 0x81, 0x81, 0x7, 0x2, 0x19, + 0x84, 0xc1, 0x8b, 0x0, 0x80, + + /* U+4EEB "仫" */ + 0x8, 0x40, 0x20, 0x80, 0x42, 0x1, 0xc, 0x2, + 0x10, 0x8c, 0x42, 0x29, 0x4, 0x54, 0x10, 0x20, + 0x40, 0x41, 0x20, 0x84, 0x21, 0x8, 0x42, 0x20, + 0x44, 0xff, 0xc8, 0x0, 0x80, + + /* U+4EEC "们" */ + 0x12, 0x0, 0x45, 0xf1, 0x10, 0x48, 0x1, 0x22, + 0x5, 0x88, 0x16, 0x20, 0x68, 0x81, 0x22, 0x4, + 0x88, 0x12, 0x20, 0x48, 0x81, 0x22, 0x4, 0x88, + 0x12, 0x27, 0x80, + + /* U+4EF0 "仰" */ + 0x10, 0x0, 0x27, 0x80, 0x90, 0xf9, 0x21, 0x12, + 0x42, 0x2c, 0x84, 0x59, 0x8, 0xd2, 0x11, 0xa4, + 0x22, 0x48, 0x44, 0x90, 0x89, 0x2d, 0x12, 0x62, + 0xe4, 0x84, 0x8, 0x8, 0x10, 0x10, + + /* U+4EF2 "仲" */ + 0x10, 0x40, 0x20, 0x80, 0x81, 0x1, 0x3f, 0xf2, + 0x44, 0x2c, 0x88, 0x69, 0x10, 0xd2, 0x21, 0x24, + 0x42, 0x4f, 0xfc, 0x91, 0x9, 0x2, 0x2, 0x4, + 0x4, 0x8, 0x8, 0x10, 0x0, + + /* U+4EF3 "仳" */ + 0x14, 0x20, 0x14, 0x20, 0x24, 0x20, 0x24, 0x22, + 0x24, 0x26, 0x67, 0xac, 0x64, 0x30, 0xa4, 0x20, + 0x24, 0x20, 0x24, 0x20, 0x24, 0x20, 0x24, 0x21, + 0x24, 0xa2, 0x27, 0x22, 0x24, 0x3e, + + /* U+4EF5 "仵" */ + 0x11, 0x0, 0x24, 0x0, 0x8f, 0xf1, 0x12, 0x6, + 0x44, 0xc, 0x88, 0x28, 0x10, 0x10, 0x20, 0x2f, + 0xfe, 0x40, 0x80, 0x81, 0x1, 0x2, 0x2, 0x4, + 0x4, 0x8, 0x8, 0x10, 0x0, + + /* U+4EF6 "件" */ + 0x10, 0x20, 0x22, 0x40, 0x88, 0x81, 0x1f, 0xf6, + 0x22, 0xc, 0x84, 0x29, 0x8, 0x50, 0x10, 0x20, + 0x20, 0x5f, 0xfc, 0x80, 0x81, 0x1, 0x2, 0x2, + 0x4, 0x4, 0x8, 0x8, 0x0, + + /* U+4EF7 "价" */ + 0x10, 0x40, 0x21, 0x40, 0x82, 0x81, 0x8, 0x82, + 0x60, 0xcd, 0x80, 0x68, 0x44, 0x50, 0x88, 0x21, + 0x10, 0x42, 0x20, 0x84, 0x41, 0x10, 0x82, 0x21, + 0x4, 0x82, 0x0, + + /* U+4EFB "任" */ + 0x10, 0x0, 0x2f, 0xf8, 0xc1, 0x1, 0x2, 0x6, + 0x4, 0xc, 0x8, 0x28, 0x10, 0x53, 0xff, 0x20, + 0x40, 0x40, 0x80, 0x81, 0x1, 0x2, 0x2, 0x4, + 0x4, 0xff, 0xc8, 0x0, 0x0, + + /* U+4EFD "份" */ + 0x0, 0x20, 0x22, 0x40, 0x44, 0x41, 0x10, 0x42, + 0x20, 0xcc, 0x80, 0xda, 0x0, 0xd3, 0xfc, 0x21, + 0x8, 0x42, 0x10, 0x84, 0x21, 0x10, 0x42, 0x20, + 0x84, 0x81, 0xa, 0x3c, 0x0, + + /* U+4EFF "仿" */ + 0x0, 0x40, 0x20, 0x80, 0x40, 0x81, 0x3f, 0xf2, + 0x10, 0x4, 0x20, 0x18, 0x40, 0x50, 0xfe, 0xa1, + 0x4, 0x42, 0x8, 0x84, 0x11, 0x8, 0x22, 0x30, + 0x44, 0x40, 0x89, 0x3, 0x16, 0x1c, + + /* U+4F01 "企" */ + 0x3, 0x0, 0x5, 0x0, 0x31, 0x0, 0xc1, 0x2, + 0x11, 0x98, 0x20, 0x80, 0x40, 0x10, 0x80, 0x21, + 0xf0, 0x42, 0x0, 0x84, 0x1, 0x8, 0x2, 0x10, + 0x1f, 0xff, 0x80, + + /* U+4F09 "伉" */ + 0x0, 0x0, 0x20, 0x80, 0x41, 0x1, 0x3f, 0xe2, + 0x0, 0xc, 0x0, 0x18, 0xfc, 0x51, 0x8, 0x22, + 0x10, 0x44, 0x20, 0x88, 0x41, 0x10, 0x82, 0x21, + 0x24, 0x82, 0x49, 0x4, 0x94, 0xe, + + /* U+4F0A "伊" */ + 0x18, 0x0, 0x27, 0xf8, 0x42, 0x11, 0x4, 0x22, + 0x8, 0x4d, 0xff, 0xd8, 0x21, 0x50, 0x42, 0x20, + 0x84, 0x4f, 0xf8, 0x84, 0x11, 0x8, 0x2, 0x20, + 0x4, 0xc0, 0xb, 0x0, 0x0, + + /* U+4F0D "伍" */ + 0x10, 0x0, 0x2f, 0xfc, 0x82, 0x1, 0x4, 0x6, + 0x8, 0xc, 0x10, 0x29, 0xfe, 0x50, 0x84, 0x21, + 0x8, 0x42, 0x10, 0x84, 0x21, 0x8, 0x42, 0x10, + 0x85, 0xff, 0xc8, 0x0, 0x0, + + /* U+4F0E "伎" */ + 0x18, 0x40, 0x20, 0x80, 0x5f, 0xf9, 0x2, 0x2, + 0x4, 0xc, 0x8, 0x29, 0xff, 0x51, 0x2, 0x21, + 0x8, 0x42, 0x10, 0x82, 0x41, 0x3, 0x2, 0xf, + 0x4, 0x61, 0x8b, 0x0, 0x80, + + /* U+4F0F "伏" */ + 0x0, 0x40, 0x20, 0x90, 0x41, 0x31, 0x2, 0x22, + 0x4, 0xd, 0xff, 0xd8, 0x10, 0x50, 0x20, 0x20, + 0xa0, 0x41, 0x40, 0x82, 0xc1, 0x8, 0x82, 0x20, + 0x84, 0x80, 0x8a, 0x0, 0x80, + + /* U+4F10 "伐" */ + 0x8, 0x80, 0x21, 0x20, 0x42, 0x31, 0x4, 0x2, + 0xf, 0xed, 0xf0, 0x28, 0x20, 0x50, 0x42, 0x20, + 0x58, 0x40, 0xe0, 0x81, 0x1, 0xd, 0x12, 0x62, + 0x24, 0x2, 0x48, 0x3, 0x0, + + /* U+4F11 "休" */ + 0x10, 0x40, 0x20, 0x80, 0x41, 0x1, 0x7f, 0xf2, + 0x4, 0xc, 0x1c, 0x28, 0x38, 0x50, 0xa8, 0x21, + 0x50, 0x44, 0x90, 0x91, 0x11, 0x42, 0x32, 0x4, + 0x24, 0x8, 0x8, 0x10, 0x0, + + /* U+4F17 "众" */ + 0x1, 0x0, 0x7, 0x0, 0x19, 0x0, 0x41, 0x83, + 0x0, 0xd9, 0x4, 0x42, 0x8, 0x4, 0x10, 0xc, + 0x20, 0x34, 0xa0, 0x4d, 0x41, 0x86, 0x42, 0x8, + 0xd8, 0x60, 0xe0, 0x80, 0x0, + + /* U+4F18 "优" */ + 0x10, 0x90, 0x10, 0x88, 0x20, 0x80, 0x20, 0x80, + 0x2f, 0xfe, 0x60, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, + 0x20, 0xa0, 0x21, 0x20, 0x21, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x24, 0x22, 0x28, 0x3e, + + /* U+4F19 "伙" */ + 0x10, 0x40, 0x20, 0x80, 0x41, 0x1, 0x2, 0x2, + 0x84, 0x4c, 0x88, 0xb8, 0x92, 0x50, 0x50, 0x20, + 0xa0, 0x41, 0x40, 0x84, 0x41, 0x8, 0xc2, 0x20, + 0x84, 0x80, 0xca, 0x0, 0x80, + + /* U+4F1A "会" */ + 0x0, 0x80, 0x1, 0x80, 0x2, 0x40, 0x4, 0x20, + 0x8, 0x18, 0x30, 0x6, 0x4f, 0xf2, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x7f, 0xfe, 0x2, 0x0, + 0x4, 0x20, 0x8, 0x10, 0x3f, 0xf8, 0x0, 0x4, + + /* U+4F1B "伛" */ + 0x10, 0x0, 0x2f, 0xf8, 0xd0, 0x1, 0x28, 0x42, + 0x48, 0x8c, 0x92, 0x19, 0x14, 0x52, 0x10, 0xa4, + 0x20, 0x48, 0xa0, 0x93, 0x21, 0x24, 0x42, 0x50, + 0x4, 0x80, 0x9, 0xff, 0x80, + + /* U+4F1E "伞" */ + 0x0, 0x80, 0x3, 0x80, 0x19, 0x80, 0x61, 0x81, + 0x10, 0xcc, 0x20, 0x44, 0x42, 0x4, 0x88, 0x1, + 0x20, 0x2, 0x1, 0xff, 0xf8, 0x8, 0x0, 0x10, + 0x0, 0x20, 0x0, 0x40, 0x0, + + /* U+4F1F "伟" */ + 0x10, 0x80, 0x82, 0x2, 0xff, 0xc8, 0x20, 0x20, + 0x81, 0xbf, 0xe6, 0x8, 0x28, 0x20, 0x2f, 0xfc, + 0x82, 0x12, 0x8, 0x48, 0x21, 0x20, 0xbc, 0x82, + 0x2, 0x8, 0x0, + + /* U+4F20 "传" */ + 0x10, 0x80, 0x21, 0x0, 0x9f, 0xf1, 0x8, 0x6, + 0x10, 0xd, 0xff, 0xe8, 0x80, 0x51, 0x0, 0x23, + 0xfc, 0x40, 0x10, 0x80, 0x41, 0x19, 0x82, 0xa, + 0x4, 0xc, 0x8, 0x4, 0x0, + + /* U+4F22 "伢" */ + 0x10, 0x0, 0x27, 0xfc, 0x80, 0x21, 0x10, 0x42, + 0x20, 0x8c, 0xc1, 0x29, 0xff, 0xd0, 0x14, 0x20, + 0x28, 0x40, 0x90, 0x82, 0x21, 0x18, 0x42, 0x40, + 0x85, 0x1, 0x8, 0x1c, 0x0, + + /* U+4F24 "伤" */ + 0x11, 0x0, 0x24, 0x0, 0x8f, 0xf9, 0x20, 0x6, + 0xc8, 0xc, 0x10, 0x29, 0xff, 0x50, 0x42, 0x20, + 0x84, 0x41, 0x8, 0x84, 0x11, 0x8, 0x22, 0x20, + 0x44, 0x81, 0x8a, 0x1e, 0x0, + + /* U+4F25 "伥" */ + 0x2, 0x0, 0x24, 0x0, 0x48, 0x21, 0x10, 0x82, + 0x22, 0xc, 0x48, 0x18, 0x80, 0x57, 0xff, 0x22, + 0x40, 0x44, 0x80, 0x88, 0x81, 0x11, 0x2, 0x21, + 0x4, 0x49, 0x8, 0xe1, 0x91, 0x0, + + /* U+4F26 "伦" */ + 0x0, 0x40, 0x20, 0x80, 0x42, 0x81, 0x8, 0x82, + 0x20, 0x8c, 0x80, 0xd8, 0x80, 0x51, 0x4, 0x22, + 0x30, 0x45, 0x80, 0x8c, 0x1, 0x10, 0x22, 0x20, + 0x44, 0x40, 0x88, 0x7e, 0x0, + + /* U+4F27 "伧" */ + 0x8, 0x40, 0x20, 0xc0, 0x42, 0x81, 0x8, 0x82, + 0x20, 0xcc, 0x80, 0x5a, 0xfe, 0x51, 0x4, 0x22, + 0x8, 0x44, 0x10, 0x89, 0xe1, 0x10, 0x12, 0x20, + 0x24, 0x40, 0x48, 0x7f, 0x0, + + /* U+4F2A "伪" */ + 0x10, 0x80, 0x52, 0x2, 0x28, 0x8, 0x20, 0x2f, + 0xfd, 0x82, 0x1e, 0x8, 0x68, 0x31, 0x20, 0xa4, + 0x84, 0xd2, 0x10, 0x48, 0x81, 0x22, 0x4, 0x90, + 0x12, 0x87, 0x80, + + /* U+4F2B "伫" */ + 0x0, 0x0, 0x20, 0x80, 0x40, 0x81, 0x3f, 0xf2, + 0x40, 0x2c, 0x80, 0x59, 0x0, 0xd0, 0x0, 0xa0, + 0x0, 0x40, 0x0, 0x80, 0x1, 0x0, 0x2, 0x0, + 0x4, 0x0, 0x9, 0xff, 0x90, 0x0, + + /* U+4F2F "伯" */ + 0x0, 0x0, 0x41, 0x1, 0x4, 0x8, 0xff, 0x22, + 0x5, 0x88, 0x16, 0x20, 0x68, 0x81, 0xa3, 0xfc, + 0x88, 0x12, 0x20, 0x48, 0x81, 0x22, 0x4, 0x8f, + 0xf2, 0x20, 0x48, 0x81, + + /* U+4F30 "估" */ + 0x10, 0x40, 0x20, 0x80, 0x41, 0x1, 0x2, 0x2, + 0xff, 0xec, 0x8, 0x18, 0x10, 0x50, 0x20, 0xa7, + 0xfc, 0x48, 0x8, 0x90, 0x11, 0x20, 0x22, 0x40, + 0x44, 0x80, 0x89, 0xff, 0x12, 0x2, + + /* U+4F32 "伲" */ + 0x18, 0x0, 0x27, 0xf8, 0x48, 0x11, 0x10, 0x22, + 0x20, 0x4c, 0x7f, 0x98, 0x80, 0x51, 0x40, 0xa2, + 0x8c, 0x45, 0x20, 0x8b, 0x81, 0x24, 0x12, 0x48, + 0x25, 0x10, 0x4a, 0x3f, 0x0, + + /* U+4F34 "伴" */ + 0x10, 0x40, 0x10, 0x40, 0x14, 0x46, 0x22, 0x44, + 0x21, 0x48, 0x60, 0x40, 0x67, 0xfe, 0xa0, 0x40, + 0x20, 0x40, 0x2f, 0xff, 0x20, 0x40, 0x20, 0x40, + 0x20, 0x40, 0x20, 0x40, 0x20, 0x40, + + /* U+4F36 "伶" */ + 0x18, 0x40, 0x21, 0xc0, 0x42, 0xc1, 0x8, 0xc2, + 0x28, 0xcd, 0x88, 0x58, 0x8, 0x50, 0x0, 0x27, + 0xfc, 0x40, 0x18, 0x80, 0x61, 0x19, 0x82, 0xe, + 0x4, 0xc, 0x8, 0x4, 0x0, + + /* U+4F38 "伸" */ + 0x10, 0x40, 0x20, 0x80, 0x9f, 0xf9, 0x22, 0x12, + 0x44, 0x2c, 0x88, 0x59, 0xff, 0xd2, 0x21, 0x24, + 0x42, 0x4f, 0xfc, 0x91, 0x9, 0x2, 0x2, 0x4, + 0x4, 0x8, 0x8, 0x10, 0x0, + + /* U+4F3A "伺" */ + 0x10, 0x0, 0x5f, 0xf1, 0x0, 0x48, 0x1, 0x27, + 0xf5, 0x80, 0x16, 0x0, 0x69, 0xf9, 0xa4, 0x24, + 0x90, 0x92, 0x42, 0x49, 0xf9, 0x20, 0x4, 0x80, + 0x12, 0x3, 0xc0, + + /* U+4F3C "似" */ + 0x10, 0x0, 0x10, 0x84, 0x24, 0x44, 0x24, 0x44, + 0x24, 0x4, 0x64, 0x4, 0x64, 0x4, 0xa4, 0x4, + 0x24, 0x4, 0x24, 0x48, 0x25, 0x88, 0x26, 0x18, + 0x24, 0x24, 0x20, 0x42, 0x20, 0x80, + + /* U+4F3D "伽" */ + 0x12, 0x0, 0x44, 0x0, 0x88, 0x79, 0x7c, 0x92, + 0x29, 0x2c, 0x52, 0x58, 0xa4, 0xd1, 0x49, 0x22, + 0x92, 0x45, 0x24, 0x8a, 0x49, 0x24, 0x92, 0x49, + 0xe5, 0x12, 0x4c, 0xe0, 0x0, + + /* U+4F43 "佃" */ + 0x10, 0x0, 0x5f, 0xf2, 0x44, 0x49, 0x11, 0x24, + 0x45, 0x91, 0x1a, 0x44, 0x69, 0xff, 0x24, 0x44, + 0x91, 0x12, 0x44, 0x49, 0x11, 0x27, 0xfc, 0x90, + 0x12, 0x40, 0x40, + + /* U+4F46 "但" */ + 0x10, 0x0, 0x2f, 0xf8, 0x90, 0x11, 0x20, 0x22, + 0x40, 0x4c, 0xff, 0xa9, 0x1, 0x52, 0x2, 0x24, + 0x4, 0x4f, 0xf8, 0x80, 0x1, 0x0, 0x2, 0x0, + 0x5, 0xff, 0xc8, 0x0, 0x0, + + /* U+4F4D "位" */ + 0x18, 0x40, 0x20, 0x80, 0x40, 0x81, 0x3f, 0xf2, + 0x0, 0xc, 0x40, 0xa8, 0x81, 0x50, 0x84, 0x21, + 0x8, 0x42, 0x20, 0x82, 0x41, 0x0, 0x82, 0x2, + 0x5, 0xff, 0xc8, 0x0, 0x0, + + /* U+4F4E "低" */ + 0x10, 0x0, 0x2f, 0xf8, 0x90, 0x81, 0x21, 0x6, + 0x42, 0xc, 0x84, 0x29, 0xff, 0xd2, 0x10, 0x24, + 0x20, 0x48, 0x40, 0x90, 0x81, 0x21, 0x12, 0x59, + 0x24, 0xc9, 0x48, 0x9, 0x80, + + /* U+4F4F "住" */ + 0x8, 0x0, 0x8, 0x20, 0x18, 0x20, 0x17, 0xfe, + 0x30, 0x20, 0x30, 0x20, 0x50, 0x20, 0x53, 0xfe, + 0x10, 0x20, 0x10, 0x20, 0x10, 0x20, 0x10, 0x20, + 0x10, 0x20, 0x17, 0xff, 0x10, 0x0, + + /* U+4F50 "佐" */ + 0x10, 0x80, 0x21, 0x0, 0x42, 0x1, 0x7f, 0xf2, + 0x10, 0xc, 0x20, 0x18, 0x80, 0x51, 0xff, 0x24, + 0x20, 0x50, 0x40, 0xa0, 0x81, 0x1, 0x2, 0x2, + 0x5, 0xff, 0xc8, 0x0, 0x0, + + /* U+4F51 "佑" */ + 0x10, 0x40, 0x20, 0x80, 0x42, 0x1, 0x7f, 0xf2, + 0x10, 0xc, 0x20, 0x18, 0x80, 0x51, 0xfe, 0x26, + 0x4, 0x54, 0x8, 0xc8, 0x11, 0x10, 0x22, 0x20, + 0x44, 0x7f, 0x88, 0x81, 0x0, + + /* U+4F53 "体" */ + 0x10, 0x80, 0x21, 0x0, 0x82, 0x1, 0x7f, 0xe2, + 0xc, 0xc, 0x34, 0x28, 0x68, 0x51, 0x50, 0x22, + 0x90, 0x49, 0x10, 0xa2, 0x31, 0x9f, 0xb2, 0x8, + 0x4, 0x10, 0x8, 0x20, 0x0, + + /* U+4F55 "何" */ + 0x10, 0x0, 0x2f, 0xfc, 0x80, 0x11, 0x0, 0x26, + 0x7c, 0x4c, 0x88, 0xa9, 0x11, 0x12, 0x22, 0x24, + 0x44, 0x48, 0x88, 0x9f, 0x11, 0x0, 0x22, 0x0, + 0x44, 0x1, 0x8, 0x1e, 0x0, + + /* U+4F57 "佗" */ + 0x10, 0x40, 0x20, 0x40, 0x9f, 0xf9, 0x20, 0x12, + 0x40, 0x2c, 0x0, 0x18, 0x81, 0x51, 0xc, 0x22, + 0x20, 0x47, 0x0, 0x88, 0x1, 0x10, 0x12, 0x20, + 0x24, 0x40, 0xc8, 0x7f, 0x0, + + /* U+4F58 "佘" */ + 0x1, 0x80, 0x3, 0x0, 0x9, 0x0, 0x61, 0x81, + 0x0, 0x8d, 0xfe, 0xc0, 0x0, 0x0, 0x0, 0x7f, + 0xfe, 0x1, 0x0, 0x22, 0x40, 0x84, 0x42, 0x8, + 0x48, 0x10, 0x41, 0xc0, 0x0, + + /* U+4F59 "余" */ + 0x1, 0x80, 0x3, 0x0, 0x9, 0x0, 0x61, 0x81, + 0x0, 0x8d, 0xfe, 0xc0, 0x20, 0x0, 0x40, 0x7f, + 0xfe, 0x1, 0x0, 0x22, 0x40, 0x84, 0x42, 0x8, + 0x48, 0x10, 0x41, 0xc0, 0x0, + + /* U+4F5A "佚" */ + 0x10, 0x40, 0x24, 0x80, 0x91, 0x1, 0x3f, 0xe6, + 0x44, 0xd, 0x8, 0x28, 0x10, 0x57, 0xff, 0x20, + 0x40, 0x41, 0x40, 0x82, 0x81, 0x8, 0x82, 0x21, + 0x84, 0x81, 0x8a, 0x0, 0x80, + + /* U+4F5B "佛" */ + 0x11, 0x20, 0x5f, 0xf8, 0x84, 0x91, 0x9, 0x26, + 0xff, 0xd5, 0x24, 0xa, 0x48, 0x14, 0x90, 0x2f, + 0xfe, 0x42, 0x44, 0x88, 0x89, 0x11, 0x62, 0x42, + 0x5, 0x4, 0x0, + + /* U+4F5C "作" */ + 0x0, 0x0, 0x22, 0x0, 0x48, 0x1, 0x1f, 0xf2, + 0x48, 0xc, 0x90, 0x1a, 0x20, 0x50, 0x7e, 0xa0, + 0x80, 0x41, 0x0, 0x82, 0x1, 0x7, 0xe2, 0x8, + 0x4, 0x10, 0x8, 0x20, 0x10, 0x40, + + /* U+4F5D "佝" */ + 0x11, 0x0, 0x48, 0x3, 0x3f, 0xc9, 0x81, 0x64, + 0x5, 0x8f, 0x9a, 0x22, 0x48, 0x89, 0x22, 0x24, + 0x88, 0x92, 0x3e, 0x48, 0x1, 0x20, 0x4, 0x80, + 0x32, 0xf, 0x80, + + /* U+4F5E "佞" */ + 0x10, 0x0, 0x2f, 0xf8, 0x80, 0x1, 0x0, 0x2, + 0xff, 0xec, 0x0, 0x28, 0x20, 0x57, 0xff, 0x21, + 0x8, 0x44, 0x20, 0x88, 0x41, 0xd, 0x2, 0x6, + 0x4, 0x73, 0x8b, 0x0, 0x0, + + /* U+4F5F "佟" */ + 0x1, 0x0, 0x22, 0x0, 0x47, 0xf1, 0x10, 0x22, + 0x50, 0x8d, 0x92, 0x18, 0x18, 0x50, 0x78, 0x23, + 0xc, 0x58, 0x4, 0x87, 0x1, 0x1, 0xc2, 0x0, + 0x4, 0xe0, 0x8, 0x3c, 0x10, 0x4, + + /* U+4F60 "你" */ + 0x0, 0x0, 0x22, 0x0, 0x48, 0x1, 0x9f, 0xf2, + 0x20, 0x4c, 0x80, 0x9a, 0x11, 0x50, 0x20, 0xa2, + 0x48, 0x44, 0x90, 0x89, 0x11, 0x22, 0x22, 0x44, + 0x24, 0x8, 0x8, 0x10, 0x11, 0xe0, + + /* U+4F63 "佣" */ + 0x10, 0x0, 0x2f, 0xfc, 0x91, 0x9, 0x22, 0x12, + 0x44, 0x2c, 0xff, 0xd9, 0x10, 0xd2, 0x21, 0x24, + 0x42, 0x4f, 0xfc, 0x91, 0x9, 0x22, 0x12, 0x44, + 0x25, 0x8, 0x4a, 0x17, 0x80, + + /* U+4F64 "佤" */ + 0x10, 0x0, 0x2f, 0xfc, 0x48, 0x1, 0x10, 0x2, + 0x20, 0xc, 0x7f, 0x18, 0x82, 0x51, 0x4, 0x22, + 0x88, 0x48, 0x90, 0x91, 0x21, 0x20, 0x42, 0x40, + 0xa4, 0xb9, 0x49, 0x83, 0x0, + + /* U+4F65 "佥" */ + 0x1, 0x0, 0xc, 0x0, 0x48, 0x2, 0x10, 0x10, + 0x31, 0x80, 0x6b, 0xff, 0x40, 0x0, 0x2, 0x0, + 0x88, 0x42, 0x12, 0x4, 0x8, 0x0, 0x43, 0xff, + 0xf0, + + /* U+4F67 "佧" */ + 0x10, 0x80, 0x21, 0x0, 0x43, 0xf1, 0x4, 0x6, + 0x8, 0xc, 0x10, 0x2b, 0xff, 0xd0, 0x0, 0x20, + 0x80, 0x41, 0x80, 0x82, 0xc1, 0x4, 0x62, 0x8, + 0x4, 0x10, 0x8, 0x20, 0x0, + + /* U+4F69 "佩" */ + 0x10, 0x0, 0x17, 0xfc, 0x24, 0x4, 0x25, 0xf4, + 0x24, 0x44, 0x64, 0x44, 0x65, 0xf4, 0xa5, 0x54, + 0x25, 0x54, 0x25, 0x54, 0x25, 0x54, 0x25, 0x54, + 0x29, 0x75, 0x28, 0x45, 0x30, 0x43, + + /* U+4F6C "佬" */ + 0x8, 0x80, 0x21, 0x0, 0x5f, 0xd1, 0x4, 0x22, + 0x8, 0x8c, 0x12, 0x1b, 0xff, 0xd0, 0x20, 0x21, + 0x80, 0x4c, 0x10, 0xa9, 0x81, 0x1c, 0x22, 0x20, + 0x44, 0x40, 0x88, 0x7e, 0x0, + + /* U+4F6F "佯" */ + 0x8, 0x8, 0x22, 0x10, 0x42, 0x41, 0x3f, 0xf2, + 0x4, 0xc, 0x8, 0x28, 0xff, 0x50, 0x20, 0x20, + 0x40, 0x40, 0x80, 0xbf, 0xf9, 0x2, 0x2, 0x4, + 0x4, 0x8, 0x8, 0x10, 0x0, + + /* U+4F70 "佰" */ + 0x10, 0x0, 0x2f, 0xfc, 0x81, 0x1, 0x2, 0x6, + 0x3f, 0xcc, 0x40, 0xa8, 0x81, 0x51, 0x2, 0x23, + 0xfc, 0x44, 0x8, 0x88, 0x11, 0x10, 0x22, 0x20, + 0x44, 0x7f, 0x88, 0x81, 0x0, + + /* U+4F73 "佳" */ + 0x8, 0x0, 0x20, 0x80, 0x4f, 0xf1, 0x2, 0x2, + 0x4, 0xc, 0x8, 0x29, 0xff, 0xd0, 0x0, 0x20, + 0x40, 0x47, 0xfc, 0x81, 0x1, 0x2, 0x2, 0x4, + 0x4, 0xff, 0xc8, 0x0, 0x0, + + /* U+4F74 "佴" */ + 0x8, 0x0, 0x2f, 0xfc, 0x48, 0x21, 0x10, 0x46, + 0x3f, 0x8c, 0x41, 0x28, 0x82, 0x51, 0x4, 0x23, + 0xf8, 0x44, 0x10, 0x88, 0x21, 0x10, 0x72, 0xff, + 0x84, 0x1, 0x8, 0x2, 0x0, + + /* U+4F76 "佶" */ + 0x10, 0x40, 0x20, 0x80, 0xbf, 0xf9, 0x2, 0x2, + 0x4, 0xc, 0x8, 0x29, 0xff, 0x50, 0x0, 0x20, + 0x0, 0x4f, 0xf8, 0x90, 0x11, 0x20, 0x22, 0x40, + 0x44, 0xff, 0x89, 0x1, 0x0, + + /* U+4F7B "佻" */ + 0x11, 0x20, 0x22, 0x40, 0x84, 0x81, 0x49, 0x22, + 0x52, 0x8c, 0xa5, 0x28, 0x48, 0x50, 0x90, 0x23, + 0x38, 0x5a, 0x4c, 0x84, 0x81, 0x11, 0x2, 0x22, + 0x24, 0x84, 0x4a, 0xf, 0x0, + + /* U+4F7C "佼" */ + 0x0, 0x0, 0x20, 0x80, 0x40, 0x81, 0x3f, 0xf2, + 0x0, 0xc, 0x22, 0x18, 0xc2, 0x53, 0x2, 0xa5, + 0x8, 0x42, 0x20, 0x82, 0x41, 0x7, 0x2, 0x6, + 0x4, 0x16, 0x8, 0xc3, 0x12, 0x1, + + /* U+4F7E "佾" */ + 0x0, 0x0, 0x22, 0x40, 0x44, 0x40, 0x90, 0x82, + 0x20, 0x8c, 0xff, 0xda, 0x82, 0x51, 0x4, 0x23, + 0xf8, 0x44, 0x10, 0x88, 0x21, 0x10, 0x42, 0x3f, + 0x84, 0x41, 0x8, 0x82, 0x11, 0x3c, + + /* U+4F7F "使" */ + 0x10, 0x40, 0x20, 0x80, 0xbf, 0xf9, 0x2, 0x2, + 0x4, 0xc, 0xff, 0x99, 0x11, 0x52, 0x22, 0xa7, + 0xfc, 0x40, 0x88, 0x91, 0x1, 0x14, 0x2, 0x18, + 0x4, 0x7c, 0xb, 0x7, 0x80, + + /* U+4F83 "侃" */ + 0x10, 0x0, 0x17, 0xfc, 0x14, 0x4, 0x24, 0x4, + 0x24, 0x4, 0x24, 0x4, 0x67, 0xfc, 0xa0, 0x0, + 0xa0, 0x0, 0x24, 0x48, 0x24, 0x48, 0x24, 0x48, + 0x24, 0x49, 0x24, 0x49, 0x28, 0x4e, + + /* U+4F84 "侄" */ + 0x10, 0x0, 0x2f, 0xf8, 0x42, 0x1, 0x8, 0x82, + 0x11, 0xc, 0x41, 0x19, 0xff, 0x50, 0x20, 0x20, + 0x40, 0x40, 0x80, 0x9f, 0xf1, 0x2, 0x2, 0x4, + 0x5, 0xff, 0xc8, 0x0, 0x0, + + /* U+4F88 "侈" */ + 0x0, 0x0, 0x21, 0x0, 0x47, 0xf0, 0x98, 0x22, + 0x70, 0x84, 0x1a, 0x18, 0x18, 0x50, 0xc0, 0xae, + 0x40, 0x41, 0xfc, 0x84, 0x11, 0x18, 0x22, 0xcd, + 0x84, 0x4, 0x8, 0x70, 0x17, 0x0, + + /* U+4F89 "侉" */ + 0x8, 0x0, 0x10, 0x40, 0x17, 0xfe, 0x20, 0x90, + 0x21, 0x18, 0x67, 0xfe, 0xac, 0x2, 0xa0, 0x0, + 0x2f, 0xfe, 0x22, 0x0, 0x23, 0xfc, 0x20, 0x4, + 0x20, 0x4, 0x20, 0x8, 0x20, 0xf8, + + /* U+4F8B "例" */ + 0x10, 0x2, 0x5f, 0x84, 0x88, 0x49, 0x20, 0x96, + 0x79, 0x2c, 0x92, 0x6a, 0x24, 0xd6, 0x49, 0x32, + 0x92, 0x42, 0x24, 0x84, 0x49, 0x10, 0x12, 0x40, + 0x25, 0x80, 0x4e, 0x7, 0x80, + + /* U+4F8D "侍" */ + 0x0, 0x40, 0x20, 0x80, 0x41, 0x1, 0x3f, 0xe2, + 0x4, 0xc, 0x8, 0x1b, 0xff, 0xd0, 0x0, 0x20, + 0x10, 0x5f, 0xfc, 0x80, 0x41, 0x30, 0x82, 0x21, + 0x4, 0x22, 0x8, 0x4, 0x10, 0x70, + + /* U+4F8F "侏" */ + 0x0, 0x40, 0x24, 0x80, 0x49, 0x1, 0x3f, 0xe2, + 0x44, 0xd, 0x8, 0x18, 0x10, 0x57, 0xff, 0x20, + 0xc0, 0x41, 0xc0, 0x85, 0x41, 0x12, 0x42, 0xc4, + 0x64, 0x8, 0x8, 0x10, 0x0, + + /* U+4F91 "侑" */ + 0x10, 0x80, 0x22, 0x0, 0xbf, 0xf9, 0x8, 0x2, + 0x20, 0xc, 0xff, 0x99, 0x81, 0x55, 0x2, 0xb3, + 0xfc, 0x44, 0x8, 0x8f, 0xf1, 0x10, 0x22, 0x20, + 0x44, 0x40, 0x88, 0x8e, 0x0, + + /* U+4F94 "侔" */ + 0x0, 0x40, 0x21, 0x0, 0x44, 0x41, 0x10, 0x42, + 0x7f, 0xcc, 0x0, 0x58, 0x90, 0x51, 0xff, 0xa4, + 0x40, 0x50, 0x80, 0x81, 0x1, 0x7f, 0xf2, 0x4, + 0x4, 0x8, 0x8, 0x10, 0x0, + + /* U+4F97 "侗" */ + 0x10, 0x0, 0x2f, 0xfc, 0x50, 0x9, 0x20, 0x12, + 0x5f, 0x2c, 0x80, 0x69, 0x3c, 0xd2, 0x89, 0x25, + 0x12, 0x4a, 0x24, 0x94, 0x49, 0x27, 0x92, 0x40, + 0x24, 0x80, 0x49, 0x7, 0x80, + + /* U+4F9B "供" */ + 0x12, 0x10, 0x24, 0x20, 0x88, 0x41, 0x7f, 0xe6, + 0x21, 0xc, 0x42, 0x28, 0x84, 0x11, 0x8, 0x22, + 0x10, 0x5f, 0xfc, 0x80, 0x1, 0x9, 0x2, 0x21, + 0x85, 0x81, 0x88, 0x0, 0x0, + + /* U+4F9D "依" */ + 0x0, 0x40, 0x20, 0x80, 0x40, 0x81, 0x7f, 0xf2, + 0x4, 0xc, 0x18, 0x18, 0x70, 0x50, 0x96, 0xa2, + 0x38, 0x4c, 0x40, 0xe8, 0x41, 0x10, 0xc2, 0x20, + 0x84, 0x4c, 0x88, 0xe0, 0x91, 0x0, + + /* U+4FA0 "侠" */ + 0x10, 0x40, 0x20, 0x80, 0xdf, 0xf1, 0x2, 0x6, + 0x44, 0x4c, 0x49, 0x28, 0x14, 0x50, 0x20, 0x2f, + 0xfe, 0x40, 0x80, 0x82, 0x81, 0x5, 0x2, 0x11, + 0x4, 0xc1, 0x8a, 0x0, 0x80, + + /* U+4FA3 "侣" */ + 0x10, 0x0, 0x27, 0xf8, 0x48, 0x11, 0x10, 0x22, + 0x20, 0x4c, 0x7f, 0x98, 0x0, 0x50, 0x0, 0x27, + 0xfe, 0x48, 0x4, 0x90, 0x9, 0x20, 0x12, 0x40, + 0x24, 0xff, 0xc9, 0x0, 0x80, + + /* U+4FA5 "侥" */ + 0x11, 0x0, 0x22, 0x18, 0x4f, 0xc1, 0x64, 0x62, + 0x9, 0xc, 0xc, 0x58, 0xc8, 0xd6, 0xe, 0x20, + 0x0, 0x5f, 0xfc, 0x84, 0x81, 0x9, 0x2, 0x12, + 0x24, 0x44, 0x4b, 0x7, 0x80, + + /* U+4FA6 "侦" */ + 0x0, 0x40, 0x20, 0x80, 0x41, 0x1, 0x3, 0xf2, + 0x4, 0xc, 0xff, 0x99, 0x1, 0x52, 0x22, 0x24, + 0x44, 0x48, 0x88, 0x91, 0x11, 0x22, 0x22, 0x7, + 0x4, 0x13, 0x8, 0xc1, 0x16, 0x1, + + /* U+4FA7 "侧" */ + 0x10, 0x2, 0x2f, 0x94, 0x51, 0x29, 0x2a, 0x52, + 0x54, 0xac, 0xa9, 0x59, 0x52, 0xd2, 0xa5, 0x25, + 0x4a, 0x4a, 0x94, 0x88, 0x29, 0xc, 0x12, 0x2c, + 0x24, 0x8c, 0x4a, 0x3, 0x80, + + /* U+4FA8 "侨" */ + 0x8, 0x0, 0x27, 0xf8, 0x41, 0x1, 0x2, 0x2, + 0x7f, 0xec, 0x14, 0x28, 0x24, 0x50, 0x84, 0x22, + 0x4, 0x5a, 0x14, 0x84, 0x21, 0x8, 0x42, 0x10, + 0x84, 0x41, 0x9, 0x2, 0x0, + + /* U+4FA9 "侩" */ + 0x8, 0x40, 0x20, 0x80, 0x42, 0x81, 0x8, 0x82, + 0x60, 0xcd, 0x80, 0x78, 0xfe, 0x50, 0x0, 0x20, + 0x0, 0x5f, 0xfc, 0x82, 0x1, 0x8, 0x82, 0x40, + 0x85, 0xff, 0x88, 0x1, 0x80, + + /* U+4FAA "侪" */ + 0x10, 0x40, 0x20, 0x80, 0xbf, 0xf9, 0x10, 0x42, + 0x21, 0xc, 0x26, 0x18, 0x30, 0x51, 0xdc, 0xac, + 0x6, 0x44, 0x20, 0x88, 0x41, 0x10, 0x82, 0x21, + 0x4, 0x82, 0xa, 0x4, 0x0, + + /* U+4FAC "侬" */ + 0x10, 0x40, 0x10, 0x40, 0x37, 0xfe, 0x24, 0x82, + 0x64, 0x82, 0x60, 0x80, 0xa1, 0x46, 0xa1, 0x6c, + 0x23, 0x30, 0x25, 0x30, 0x2d, 0x18, 0x21, 0xc, + 0x21, 0x22, 0x21, 0xc0, 0x21, 0x0, + + /* U+4FAE "侮" */ + 0x1, 0x0, 0x22, 0x0, 0x4f, 0xf9, 0x10, 0x2, + 0x40, 0x5, 0x7f, 0x98, 0x81, 0x31, 0x32, 0xa2, + 0x4, 0x5f, 0xfc, 0x90, 0x11, 0x23, 0x22, 0x7f, + 0xe4, 0x0, 0x88, 0x2, 0x10, 0x1c, + + /* U+4FAF "侯" */ + 0x10, 0x0, 0x27, 0xf0, 0x40, 0x21, 0x0, 0x42, + 0xff, 0xec, 0x0, 0x18, 0x80, 0x51, 0xfe, 0x24, + 0x40, 0x40, 0x80, 0xbf, 0xf9, 0x3, 0x2, 0x9, + 0x4, 0x61, 0xb, 0x1, 0x80, + + /* U+4FB5 "侵" */ + 0x10, 0x0, 0x2f, 0xf8, 0x40, 0x11, 0x1f, 0xe2, + 0x0, 0x4c, 0xff, 0x98, 0x0, 0x57, 0xff, 0xa8, + 0x2, 0x4f, 0xf8, 0x84, 0x21, 0x8, 0x82, 0xe, + 0x4, 0x36, 0xb, 0x83, 0x80, + + /* U+4FBF "便" */ + 0x10, 0x0, 0x2f, 0xfc, 0x41, 0x1, 0x3f, 0xe2, + 0x44, 0x4c, 0x88, 0x99, 0xff, 0x52, 0x22, 0x24, + 0x44, 0x4f, 0xf8, 0x89, 0x1, 0x1a, 0x2, 0x18, + 0x4, 0x6e, 0xb, 0x3, 0x80, + + /* U+4FC3 "促" */ + 0x10, 0x0, 0x2f, 0xf8, 0x50, 0x11, 0x20, 0x22, + 0x40, 0x4c, 0xff, 0x98, 0x10, 0x50, 0x20, 0x24, + 0x40, 0x48, 0xf8, 0x91, 0x1, 0x32, 0x2, 0x94, + 0x5, 0x1f, 0xc8, 0x0, 0x0, + + /* U+4FC4 "俄" */ + 0x10, 0x20, 0x21, 0x50, 0xbc, 0x91, 0x11, 0x2, + 0x22, 0xd, 0xff, 0xd8, 0x88, 0x51, 0x10, 0x22, + 0x24, 0x5f, 0x50, 0x88, 0x41, 0x11, 0x92, 0x25, + 0x24, 0x53, 0x4b, 0x83, 0x80, + + /* U+4FC5 "俅" */ + 0x10, 0x40, 0x20, 0x98, 0x41, 0x1, 0x7f, 0xf2, + 0x4, 0xc, 0x8, 0x19, 0x11, 0x53, 0x34, 0x22, + 0x70, 0x41, 0xa0, 0x8d, 0x21, 0x62, 0x22, 0x4, + 0x24, 0x8, 0x8, 0xe0, 0x0, + + /* U+4FCA "俊" */ + 0x0, 0x40, 0x21, 0x0, 0x44, 0x41, 0x10, 0x42, + 0x7f, 0xcc, 0x0, 0x18, 0xc6, 0x52, 0x3, 0x29, + 0x80, 0x43, 0xf0, 0x88, 0x21, 0x28, 0x82, 0xa, + 0x4, 0x8, 0x8, 0xee, 0x16, 0x3, + + /* U+4FCE "俎" */ + 0x10, 0x0, 0x23, 0xf8, 0x84, 0x11, 0x88, 0x22, + 0x90, 0x48, 0xbf, 0x90, 0x41, 0x8, 0x82, 0x21, + 0x4, 0x63, 0xf8, 0xa4, 0x12, 0x48, 0x24, 0x10, + 0x40, 0xff, 0xc0, + + /* U+4FCF "俏" */ + 0x14, 0x22, 0x24, 0x4c, 0x84, 0x91, 0x1, 0x2, + 0x7f, 0xec, 0x80, 0x69, 0x0, 0xd3, 0xff, 0x24, + 0x2, 0x48, 0x4, 0x9f, 0xf9, 0x20, 0x12, 0x40, + 0x24, 0x80, 0x49, 0x7, 0x80, + + /* U+4FD0 "俐" */ + 0x10, 0x2, 0x3f, 0x4, 0x84, 0x49, 0x8, 0x96, + 0xfd, 0x2c, 0x22, 0x68, 0xc4, 0xd2, 0xc9, 0x25, + 0x52, 0x4a, 0x24, 0xa4, 0x49, 0x8, 0x12, 0x10, + 0x24, 0x20, 0x48, 0x47, 0x80, + + /* U+4FD1 "俑" */ + 0x10, 0x0, 0x2f, 0xfc, 0x40, 0x11, 0x18, 0xc2, + 0xe, 0xc, 0xff, 0xd9, 0x10, 0xd2, 0x21, 0xa7, + 0xfe, 0x48, 0x84, 0x91, 0x9, 0x3f, 0xf2, 0x44, + 0x24, 0x88, 0x49, 0x10, 0x92, 0x2f, + + /* U+4FD7 "俗" */ + 0x8, 0x0, 0x11, 0x98, 0x13, 0x4, 0x26, 0x22, + 0x24, 0x60, 0x60, 0xb0, 0xa1, 0x18, 0xa2, 0x4, + 0x2c, 0x3, 0x23, 0xfc, 0x22, 0x4, 0x22, 0x4, + 0x22, 0x4, 0x23, 0xfc, 0x22, 0x4, + + /* U+4FD8 "俘" */ + 0x10, 0x0, 0x2f, 0xfc, 0x41, 0x11, 0x12, 0x22, + 0x22, 0x8c, 0x0, 0x29, 0xff, 0x50, 0x4, 0x20, + 0x30, 0x40, 0x80, 0xbf, 0xf9, 0x2, 0x2, 0x4, + 0x4, 0x8, 0x8, 0x70, 0x0, + + /* U+4FDA "俚" */ + 0x10, 0x0, 0x2f, 0xf8, 0x91, 0x11, 0x22, 0x22, + 0x7f, 0xcc, 0x88, 0xa9, 0x11, 0x53, 0xfe, 0x20, + 0x40, 0x40, 0x80, 0x9f, 0xf1, 0x2, 0x2, 0x4, + 0x5, 0xff, 0xc8, 0x0, 0x0, + + /* U+4FDC "俜" */ + 0x10, 0x40, 0x2f, 0xf8, 0x51, 0x11, 0x22, 0x22, + 0x7f, 0xcc, 0x88, 0xa9, 0xff, 0x50, 0x0, 0x2f, + 0xfe, 0x44, 0x0, 0x8f, 0xe1, 0x0, 0x42, 0x0, + 0x84, 0x1, 0x8, 0x7c, 0x0, + + /* U+4FDD "保" */ + 0x10, 0x0, 0x2f, 0xf8, 0x50, 0x11, 0x20, 0x22, + 0x40, 0x4c, 0xff, 0x98, 0x0, 0x50, 0x20, 0x2f, + 0xfe, 0x41, 0xc0, 0x85, 0x41, 0x1a, 0x82, 0x64, + 0xc5, 0x8, 0x48, 0x10, 0x0, + + /* U+4FDE "俞" */ + 0x1, 0x0, 0x3, 0x80, 0x6, 0x40, 0x8, 0x30, + 0x3f, 0xee, 0xc0, 0x0, 0x0, 0x0, 0x3f, 0x4, + 0x21, 0x24, 0x21, 0x24, 0x3f, 0x24, 0x21, 0x24, + 0x3f, 0x24, 0x21, 0x4, 0x21, 0x4, 0x26, 0x3c, + + /* U+4FDF "俟" */ + 0x8, 0x80, 0x21, 0x0, 0x44, 0x41, 0x10, 0x42, + 0x7f, 0xcc, 0x40, 0x28, 0xff, 0x52, 0x20, 0x28, + 0x40, 0x40, 0x80, 0xbf, 0xf9, 0x2, 0x2, 0xa, + 0x4, 0x63, 0xb, 0x1, 0x80, + + /* U+4FE1 "信" */ + 0x10, 0x40, 0x20, 0x80, 0xbf, 0xf9, 0x0, 0x2, + 0x7f, 0xcc, 0x0, 0x28, 0x0, 0x53, 0xfe, 0x20, + 0x0, 0x4f, 0xf8, 0x90, 0x11, 0x20, 0x22, 0x40, + 0x44, 0xff, 0x89, 0x1, 0x0, + + /* U+4FE3 "俣" */ + 0x10, 0x0, 0x2f, 0xf8, 0x50, 0x11, 0x20, 0x22, + 0x40, 0x4c, 0xff, 0x98, 0x0, 0x53, 0xfe, 0xa0, + 0x40, 0x40, 0x80, 0xbf, 0xf9, 0x6, 0x2, 0xa, + 0x4, 0x22, 0x9, 0x83, 0x14, 0x3, + + /* U+4FE6 "俦" */ + 0x0, 0x40, 0x20, 0x80, 0x5f, 0xf9, 0x2, 0x2, + 0x4, 0x4, 0xff, 0x98, 0x20, 0x30, 0x40, 0xaf, + 0xfe, 0x42, 0x10, 0x87, 0xf9, 0x10, 0x42, 0x58, + 0x85, 0x19, 0x8, 0x2, 0x10, 0x1c, + + /* U+4FE8 "俨" */ + 0x10, 0x0, 0x2f, 0xfc, 0x42, 0x41, 0x24, 0x92, + 0x29, 0x4c, 0x12, 0x29, 0xff, 0xd2, 0x0, 0x24, + 0x0, 0x48, 0x0, 0x90, 0x1, 0x20, 0x2, 0x80, + 0x5, 0x0, 0xa, 0x0, 0x0, + + /* U+4FE9 "俩" */ + 0x10, 0x0, 0x2f, 0xfc, 0x42, 0x41, 0x4, 0x82, + 0x7f, 0xec, 0x92, 0x59, 0x24, 0xd2, 0x49, 0x25, + 0x52, 0x4a, 0x54, 0x94, 0x99, 0x32, 0x12, 0x40, + 0x24, 0x80, 0x49, 0x7, 0x0, + + /* U+4FEA "俪" */ + 0x10, 0x0, 0x5f, 0xfc, 0x80, 0x1, 0x7d, 0xf2, + 0x8a, 0x2d, 0x14, 0x6a, 0xaa, 0xd5, 0x55, 0x2a, + 0xaa, 0x53, 0x4c, 0xa2, 0x89, 0x45, 0x12, 0x8a, + 0x25, 0x14, 0x4a, 0x6b, 0x0, + + /* U+4FED "俭" */ + 0x0, 0x40, 0x20, 0x80, 0x42, 0x81, 0x8, 0x82, + 0x20, 0xcd, 0x80, 0xd8, 0xfe, 0x50, 0x0, 0x24, + 0x44, 0x48, 0x90, 0x89, 0x21, 0x10, 0x82, 0x1, + 0x5, 0xff, 0xc8, 0x0, 0x0, + + /* U+4FEE "修" */ + 0x10, 0x80, 0x43, 0xf8, 0xa6, 0x21, 0x52, 0xc6, + 0x82, 0xd, 0x1b, 0xaa, 0xc4, 0xd4, 0x30, 0x2b, + 0x98, 0x50, 0xc0, 0xa6, 0x31, 0x0, 0x82, 0xe, + 0x4, 0x60, 0x0, + + /* U+4FEF "俯" */ + 0x10, 0x20, 0x10, 0x20, 0x17, 0xff, 0x24, 0x40, + 0x24, 0x44, 0x64, 0x84, 0x64, 0xbf, 0xa5, 0x84, + 0x25, 0xa4, 0x26, 0xa4, 0x24, 0x94, 0x24, 0x84, + 0x24, 0x84, 0x28, 0x84, 0x28, 0x98, + + /* U+4FF1 "俱" */ + 0x0, 0x0, 0x27, 0xf0, 0x50, 0x21, 0xa0, 0x42, + 0x3f, 0x8c, 0x81, 0x18, 0xfe, 0x52, 0x4, 0x24, + 0x8, 0x47, 0xf0, 0x90, 0x21, 0x7f, 0xf2, 0x0, + 0x4, 0x22, 0x9, 0x83, 0x16, 0x0, + + /* U+4FF3 "俳" */ + 0x11, 0x10, 0x11, 0x10, 0x21, 0x10, 0x2f, 0x1e, + 0x61, 0x10, 0x61, 0x10, 0xaf, 0x1e, 0xa1, 0x10, + 0x21, 0x10, 0x21, 0x10, 0x2f, 0x1f, 0x21, 0x10, + 0x21, 0x10, 0x21, 0x10, 0x21, 0x10, + + /* U+4FF8 "俸" */ + 0x10, 0x40, 0x20, 0x80, 0x9f, 0xf1, 0x4, 0x2, + 0x7f, 0xcc, 0x10, 0x2b, 0xff, 0xd1, 0x8, 0x22, + 0x48, 0x5f, 0xfc, 0x81, 0x1, 0x7f, 0xe2, 0x4, + 0x4, 0x8, 0x8, 0x10, 0x0, + + /* U+4FFA "俺" */ + 0x10, 0x80, 0x21, 0x0, 0xbf, 0xf1, 0x9, 0x6, + 0x29, 0xd, 0x91, 0xa9, 0xff, 0x52, 0x44, 0x27, + 0xf8, 0x49, 0x10, 0x92, 0x21, 0x3f, 0xc2, 0x48, + 0x24, 0x10, 0x88, 0x1f, 0x0, + + /* U+4FFE "俾" */ + 0x0, 0x80, 0x21, 0x0, 0x5f, 0xf1, 0x22, 0x22, + 0x44, 0x4c, 0xff, 0x99, 0x11, 0x52, 0x22, 0x24, + 0x44, 0x4f, 0xf8, 0x82, 0x1, 0x19, 0x2, 0xff, + 0xe4, 0x4, 0x8, 0x8, 0x10, 0x10, + + /* U+500C "倌" */ + 0x18, 0x40, 0x20, 0x40, 0x5f, 0xf9, 0x20, 0x16, + 0x3f, 0xcc, 0x40, 0xa8, 0x81, 0x51, 0xfe, 0x22, + 0x0, 0x44, 0x0, 0x8f, 0xf1, 0x10, 0x22, 0x20, + 0x44, 0x7f, 0x88, 0x81, 0x0, + + /* U+500D "倍" */ + 0x10, 0x40, 0x20, 0x80, 0x9f, 0xf1, 0x10, 0x42, + 0x11, 0xc, 0x22, 0x2b, 0xff, 0xd0, 0x0, 0x20, + 0x0, 0x4f, 0xf8, 0x90, 0x11, 0x20, 0x22, 0x40, + 0x44, 0xff, 0x89, 0x1, 0x0, + + /* U+500F "倏" */ + 0x10, 0x80, 0x41, 0xf8, 0xa6, 0x11, 0x52, 0x46, + 0x83, 0xd, 0x19, 0xaa, 0xc8, 0x94, 0x16, 0x2b, + 0xfe, 0x50, 0x80, 0xa1, 0xc1, 0x6, 0xc2, 0x18, + 0x44, 0xc0, 0x40, + + /* U+5012 "倒" */ + 0x1f, 0xc2, 0x44, 0x24, 0x92, 0x4b, 0x42, 0x96, + 0xfd, 0x34, 0x2, 0x68, 0x84, 0x91, 0x9, 0x2f, + 0xd2, 0x44, 0x24, 0x88, 0x9, 0x13, 0x12, 0xf8, + 0x24, 0x3, 0xc0, + + /* U+5014 "倔" */ + 0x10, 0x0, 0x2f, 0xfc, 0x90, 0x9, 0x3f, 0xf2, + 0x40, 0xc, 0x84, 0xa9, 0x49, 0x52, 0x92, 0x24, + 0xfc, 0x48, 0x40, 0x90, 0x81, 0x49, 0x12, 0x92, + 0x25, 0x3f, 0xcc, 0x0, 0x80, + + /* U+5018 "倘" */ + 0x0, 0x0, 0x28, 0x84, 0x49, 0x11, 0xa, 0x42, + 0x4, 0xc, 0xff, 0xd9, 0x0, 0xd2, 0x1, 0xa4, + 0xf2, 0x49, 0x24, 0x92, 0x49, 0x24, 0x92, 0x4f, + 0x24, 0x80, 0x49, 0x0, 0x92, 0xf, + + /* U+5019 "候" */ + 0x10, 0x0, 0x20, 0xf8, 0x28, 0x8, 0x28, 0x8, + 0x29, 0xf6, 0x68, 0x80, 0xa9, 0xfc, 0xa9, 0x20, + 0x2a, 0x20, 0x28, 0x20, 0x2b, 0xff, 0x28, 0x50, + 0x20, 0xd8, 0x21, 0x86, 0x26, 0x3, + + /* U+501A "倚" */ + 0x10, 0x0, 0x20, 0x80, 0x5f, 0xf9, 0x2, 0x2, + 0xf, 0xc, 0x61, 0x19, 0x1, 0x57, 0xff, 0xa0, + 0x4, 0x4f, 0x88, 0x91, 0x11, 0x22, 0x22, 0x7c, + 0x44, 0x0, 0x88, 0x1, 0x10, 0x1c, + + /* U+501C "倜" */ + 0x10, 0x0, 0x2f, 0xfc, 0x90, 0x89, 0x2f, 0xd2, + 0x42, 0x2c, 0x84, 0x69, 0x7e, 0xd2, 0x1, 0x24, + 0xf2, 0x49, 0x24, 0x92, 0x49, 0x27, 0x92, 0x40, + 0x25, 0x0, 0x4a, 0x7, 0x80, + + /* U+501F "借" */ + 0x11, 0x10, 0x22, 0x20, 0x9f, 0xf9, 0x8, 0x82, + 0x11, 0xd, 0xff, 0xe8, 0x0, 0x50, 0x0, 0x27, + 0xfc, 0x48, 0x8, 0x9f, 0xf1, 0x20, 0x22, 0x40, + 0x44, 0xff, 0x89, 0x1, 0x0, + + /* U+5021 "倡" */ + 0x13, 0xfc, 0x24, 0x8, 0x88, 0x11, 0x1f, 0xe6, + 0x20, 0x4c, 0x7f, 0xa8, 0x0, 0x10, 0x0, 0x27, + 0xfe, 0x48, 0x4, 0x90, 0x9, 0x3f, 0xf2, 0x40, + 0x24, 0xff, 0xc9, 0x0, 0x80, + + /* U+5025 "倥" */ + 0x0, 0x40, 0x20, 0x80, 0x40, 0x1, 0x3f, 0xf2, + 0x40, 0x24, 0x80, 0x58, 0xc6, 0x52, 0x2, 0xa0, + 0x0, 0x4f, 0xf8, 0x81, 0x1, 0x2, 0x2, 0x4, + 0x4, 0x8, 0xb, 0xff, 0x90, 0x0, + + /* U+5026 "倦" */ + 0x10, 0x44, 0x24, 0x90, 0x81, 0x1, 0x3f, 0xf2, + 0x4, 0xc, 0x10, 0x1b, 0xff, 0xd0, 0x88, 0x22, + 0x8, 0x4f, 0xe8, 0xa8, 0x49, 0x13, 0xa2, 0x20, + 0x44, 0x40, 0x88, 0x7e, 0x0, + + /* U+5028 "倨" */ + 0x10, 0x0, 0x2f, 0xf8, 0x50, 0x11, 0x20, 0x22, + 0x7f, 0xcc, 0x80, 0x19, 0x8, 0x53, 0xff, 0x24, + 0x20, 0x48, 0x40, 0x97, 0xf1, 0x28, 0x22, 0x90, + 0x45, 0x3f, 0x8c, 0x41, 0x0, + + /* U+5029 "倩" */ + 0x10, 0x40, 0x3f, 0xfc, 0x81, 0x1, 0x3f, 0xe2, + 0x4, 0xd, 0xff, 0xe8, 0x0, 0x53, 0xfe, 0x24, + 0x4, 0x4f, 0xf8, 0x90, 0x11, 0x3f, 0xe2, 0x40, + 0x44, 0x80, 0x89, 0xf, 0x0, + + /* U+502A "倪" */ + 0x10, 0x80, 0x2e, 0x78, 0x90, 0x11, 0x20, 0x22, + 0x7b, 0xcc, 0x80, 0xa9, 0x1, 0x52, 0x2, 0x27, + 0xfc, 0x42, 0x40, 0x84, 0x81, 0x9, 0x12, 0x22, + 0x24, 0xc4, 0x4a, 0xf, 0x0, + + /* U+502C "倬" */ + 0x8, 0x40, 0x20, 0xfc, 0x41, 0x1, 0x82, 0x2, + 0x7f, 0xcc, 0x80, 0xa9, 0xff, 0x52, 0x2, 0x24, + 0x4, 0x4f, 0xf8, 0x81, 0x1, 0x7f, 0xf2, 0x4, + 0x4, 0x8, 0x8, 0x10, 0x0, + + /* U+502D "倭" */ + 0x10, 0x0, 0x2f, 0xf8, 0x81, 0x1, 0x3f, 0xf2, + 0xe, 0xc, 0x2a, 0x29, 0x93, 0x54, 0x21, 0x20, + 0x80, 0x5f, 0xfc, 0x84, 0x41, 0x10, 0x82, 0x1e, + 0x4, 0x37, 0xb, 0x81, 0x0, + + /* U+502E "倮" */ + 0x8, 0x0, 0x13, 0xfc, 0x12, 0x44, 0x12, 0x44, + 0x23, 0xfc, 0x22, 0x44, 0x62, 0x44, 0xa3, 0xfc, + 0xa0, 0x40, 0x20, 0x40, 0x27, 0xfe, 0x20, 0x60, + 0x20, 0xd0, 0x23, 0x4c, 0x2c, 0x42, 0x20, 0x40, + + /* U+503A "债" */ + 0x10, 0x40, 0x2f, 0xfc, 0x81, 0x1, 0x3f, 0xe2, + 0x4, 0xd, 0xff, 0xe8, 0x0, 0x53, 0xfe, 0x24, + 0x4, 0x48, 0x88, 0x91, 0x11, 0x22, 0x22, 0xb, + 0x4, 0x61, 0x8b, 0x0, 0x0, + + /* U+503C "值" */ + 0x10, 0x40, 0x2f, 0xfc, 0x41, 0x1, 0x2, 0x2, + 0x3f, 0xcc, 0x80, 0xa8, 0xff, 0x52, 0x2, 0x24, + 0x4, 0x47, 0xf8, 0x90, 0x11, 0x1f, 0xe2, 0x40, + 0x44, 0x80, 0x8b, 0xff, 0x80, + + /* U+503E "倾" */ + 0x14, 0x0, 0x2b, 0xfc, 0x90, 0x81, 0x27, 0xe2, + 0x78, 0x4c, 0x92, 0xa9, 0x25, 0x52, 0x4a, 0x24, + 0xa4, 0x49, 0x48, 0x92, 0xd1, 0x3d, 0xc2, 0x44, + 0x84, 0x18, 0x88, 0x40, 0x80, + + /* U+5043 "偃" */ + 0x10, 0x0, 0x2f, 0xfc, 0x50, 0x1, 0x2f, 0xe2, + 0x50, 0x4c, 0xbf, 0x99, 0x41, 0x52, 0xfe, 0xa4, + 0x40, 0x4f, 0xfc, 0x92, 0x21, 0x23, 0x82, 0x4c, + 0xc4, 0xa0, 0x9, 0xff, 0x90, 0x0, + + /* U+5047 "假" */ + 0x10, 0x0, 0x17, 0xbe, 0x24, 0x82, 0x24, 0x82, + 0x67, 0xbe, 0x64, 0x0, 0xa4, 0x7e, 0xa7, 0xa2, + 0x24, 0x22, 0x24, 0x14, 0x27, 0x94, 0x24, 0x8, + 0x24, 0x14, 0x24, 0x62, 0x24, 0x81, + + /* U+5048 "偈" */ + 0x10, 0x0, 0x2f, 0xf8, 0x50, 0x11, 0x20, 0x22, + 0x7f, 0xc4, 0x80, 0x99, 0xff, 0x31, 0x0, 0xa3, + 0xfe, 0x58, 0x44, 0x91, 0x89, 0x2c, 0x92, 0x40, + 0x24, 0xfe, 0x48, 0x1, 0x90, 0xe, + + /* U+504C "偌" */ + 0x11, 0x10, 0x22, 0x20, 0xbf, 0xf9, 0x8, 0x82, + 0x15, 0xc, 0x10, 0x1b, 0xff, 0xd0, 0x80, 0x22, + 0x0, 0x4f, 0xf8, 0xb8, 0x11, 0x10, 0x22, 0x20, + 0x44, 0x7f, 0x88, 0x81, 0x0, + + /* U+504E "偎" */ + 0x10, 0x0, 0x2f, 0xf8, 0x51, 0x11, 0x22, 0x22, + 0x7f, 0xc4, 0x88, 0x99, 0x11, 0x33, 0xfe, 0xa0, + 0x0, 0x5f, 0xfc, 0x91, 0x1, 0x22, 0x62, 0x43, + 0x4, 0x82, 0x9, 0x63, 0x13, 0x1, + + /* U+504F "偏" */ + 0x0, 0x40, 0x20, 0x80, 0x5f, 0xf9, 0x20, 0x12, + 0x40, 0x24, 0xff, 0xd9, 0x0, 0x32, 0x0, 0xa5, + 0xfe, 0x4c, 0xa4, 0x99, 0x49, 0x2f, 0xf2, 0x65, + 0x25, 0x4a, 0x4a, 0x94, 0x99, 0x7, + + /* U+5055 "偕" */ + 0x14, 0x20, 0x28, 0x44, 0x9e, 0xb1, 0x21, 0x82, + 0x42, 0x2c, 0xb4, 0x59, 0x8f, 0xd0, 0x40, 0x27, + 0xfc, 0x48, 0x8, 0x90, 0x11, 0x3f, 0xe2, 0x40, + 0x44, 0xff, 0x89, 0x1, 0x0, + + /* U+505A "做" */ + 0x12, 0x10, 0x12, 0x10, 0x22, 0x10, 0x2f, 0xdf, + 0x62, 0x24, 0x62, 0x34, 0xa2, 0x14, 0xa7, 0x94, + 0x24, 0x98, 0x24, 0x98, 0x24, 0x88, 0x24, 0x88, + 0x27, 0x94, 0x24, 0x22, 0x20, 0x40, + + /* U+505C "停" */ + 0x10, 0x40, 0x10, 0x20, 0x27, 0xff, 0x20, 0x0, + 0x23, 0xfc, 0x62, 0x4, 0x63, 0xfc, 0xa0, 0x0, + 0xaf, 0xff, 0x28, 0x1, 0x20, 0x0, 0x23, 0xfc, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x21, 0xe0, + + /* U+5065 "健" */ + 0x10, 0x10, 0x10, 0x10, 0x1e, 0xfe, 0x22, 0x12, + 0x24, 0x12, 0x25, 0xff, 0x68, 0x12, 0x6e, 0xfe, + 0xa2, 0x10, 0x2a, 0xfe, 0x2a, 0x10, 0x25, 0xfe, + 0x24, 0x10, 0x2b, 0x0, 0x28, 0xff, 0x30, 0x0, + + /* U+506C "偬" */ + 0x9, 0x0, 0x11, 0x0, 0x13, 0xfe, 0x26, 0x52, + 0x2c, 0x92, 0x61, 0xe2, 0x61, 0x3a, 0xa6, 0x42, + 0x20, 0x9c, 0x20, 0x40, 0x25, 0x44, 0x25, 0x22, + 0x29, 0xa, 0x29, 0x8, 0x21, 0xf0, + + /* U+5076 "偶" */ + 0x10, 0x0, 0x27, 0xf8, 0x91, 0x11, 0x1f, 0xe2, + 0x44, 0x4c, 0x88, 0xa8, 0xff, 0x50, 0x20, 0x27, + 0xfe, 0x48, 0x84, 0x91, 0x49, 0x2f, 0xd2, 0x40, + 0xa4, 0x80, 0xc9, 0x7, 0x0, + + /* U+5077 "偷" */ + 0x8, 0x40, 0x21, 0x40, 0x42, 0xc1, 0x10, 0xc2, + 0x5f, 0x4d, 0x0, 0x59, 0xe1, 0x52, 0x4a, 0x24, + 0x94, 0x4f, 0x28, 0x92, 0x51, 0x3c, 0xa2, 0x48, + 0x44, 0x90, 0x89, 0x67, 0x0, + + /* U+507B "偻" */ + 0x10, 0x40, 0x24, 0x90, 0x81, 0x41, 0x7f, 0xf2, + 0xe, 0xc, 0x2b, 0x2b, 0x91, 0xd0, 0x40, 0x20, + 0x80, 0x5f, 0xfc, 0x88, 0x41, 0x30, 0x82, 0x1e, + 0x4, 0x36, 0xb, 0x83, 0x0, + + /* U+507E "偾" */ + 0x10, 0x40, 0x20, 0x80, 0x9f, 0xf1, 0x2, 0x2, + 0x21, 0xd, 0xff, 0xd8, 0x84, 0x50, 0x0, 0xa7, + 0xfc, 0x48, 0x8, 0x91, 0x11, 0x22, 0x22, 0x44, + 0x44, 0x16, 0x8, 0xc3, 0x16, 0x1, + + /* U+507F "偿" */ + 0x10, 0x40, 0x2c, 0x88, 0x49, 0x21, 0x2, 0x2, + 0xff, 0xed, 0x0, 0x5a, 0xfe, 0xd0, 0x0, 0xa0, + 0x0, 0x5f, 0xfc, 0x82, 0x1, 0x8, 0x82, 0x20, + 0x84, 0xff, 0x88, 0x0, 0x0, + + /* U+5080 "傀" */ + 0x10, 0x80, 0x21, 0x0, 0x5f, 0xf1, 0x22, 0x22, + 0x44, 0x4c, 0xff, 0x99, 0x11, 0x52, 0x22, 0xa7, + 0xfc, 0x41, 0x0, 0x83, 0x1, 0xe, 0x82, 0x17, + 0xe4, 0x48, 0xc9, 0x10, 0x94, 0x1e, + + /* U+5085 "傅" */ + 0x10, 0x40, 0x20, 0x90, 0xff, 0xf9, 0x2, 0x2, + 0x7f, 0xcc, 0x88, 0x99, 0xff, 0x53, 0xfe, 0x24, + 0x44, 0x48, 0x88, 0xbf, 0xf9, 0x10, 0x42, 0x10, + 0x84, 0x1, 0x8, 0x1c, 0x0, + + /* U+5088 "傈" */ + 0x10, 0x0, 0x5f, 0xfc, 0x84, 0x81, 0x7f, 0xe6, + 0x92, 0x4d, 0x24, 0xab, 0xff, 0x50, 0x0, 0x20, + 0x40, 0x5f, 0xfc, 0x83, 0x1, 0xb, 0x82, 0x24, + 0x85, 0x88, 0xc8, 0x10, 0x0, + + /* U+508D "傍" */ + 0x10, 0x40, 0x20, 0x80, 0x9f, 0xf1, 0x10, 0x2, + 0x11, 0xd, 0xff, 0xda, 0x0, 0xd0, 0x20, 0x2f, + 0xfe, 0x42, 0x0, 0x87, 0xe1, 0x10, 0x42, 0x20, + 0x84, 0x81, 0xa, 0x1c, 0x0, + + /* U+50A3 "傣" */ + 0x10, 0x40, 0x20, 0x80, 0x5f, 0xf1, 0x2, 0x2, + 0x8, 0x4, 0xff, 0x98, 0x20, 0x37, 0xff, 0xa1, + 0x10, 0x44, 0x90, 0x9d, 0x71, 0x42, 0x12, 0x14, + 0x4, 0xcb, 0xa, 0x11, 0x10, 0xc0, + + /* U+50A5 "傥" */ + 0x10, 0x40, 0x12, 0x4c, 0x20, 0x40, 0x2f, 0xfe, + 0x28, 0x2, 0x68, 0x0, 0x63, 0xfc, 0xa2, 0x4, + 0x22, 0x4, 0x23, 0xfc, 0x21, 0x20, 0x21, 0x20, + 0x21, 0x21, 0x26, 0x22, 0x2c, 0x1e, + + /* U+50A7 "傧" */ + 0x10, 0x40, 0x20, 0x80, 0x5f, 0xf9, 0x40, 0x12, + 0x0, 0xc, 0x7e, 0x18, 0x80, 0x51, 0x0, 0xa3, + 0xfe, 0x44, 0x20, 0x88, 0x41, 0x7f, 0xf2, 0x0, + 0x4, 0x22, 0x9, 0x83, 0x14, 0x1, + + /* U+50A8 "储" */ + 0x10, 0x40, 0x28, 0x80, 0x97, 0xf1, 0x12, 0x42, + 0x4, 0x8d, 0xbf, 0xe9, 0x8, 0x52, 0x7e, 0x25, + 0x84, 0x49, 0x8, 0x91, 0xf1, 0x34, 0x22, 0x68, + 0x44, 0x8f, 0x88, 0x21, 0x0, + + /* U+50A9 "傩" */ + 0x10, 0x20, 0x20, 0x68, 0x2e, 0x40, 0x22, 0xfe, + 0x22, 0xc8, 0x6b, 0xc8, 0x6d, 0x7f, 0xa4, 0x48, + 0x24, 0x48, 0x24, 0x7f, 0x2a, 0x48, 0x2a, 0x48, + 0x31, 0x48, 0x30, 0x7f, 0x30, 0x40, + + /* U+50AC "催" */ + 0x10, 0x40, 0x14, 0x42, 0x14, 0x42, 0x27, 0xfe, + 0x20, 0x0, 0x62, 0x20, 0x67, 0xfe, 0xa4, 0x20, + 0x2f, 0xfe, 0x24, 0x20, 0x24, 0x20, 0x27, 0xfe, + 0x24, 0x20, 0x27, 0xff, 0x24, 0x0, + + /* U+50B2 "傲" */ + 0x11, 0x8, 0x11, 0x8, 0x11, 0x10, 0x2f, 0xd0, + 0x21, 0x3f, 0x6f, 0xf2, 0x61, 0x32, 0xaf, 0xd2, + 0xa4, 0x12, 0x24, 0x14, 0x27, 0xcc, 0x24, 0x48, + 0x24, 0x4c, 0x24, 0x94, 0x2b, 0xa2, 0x20, 0x40, + + /* U+50BA "傺" */ + 0x2, 0x0, 0x12, 0x20, 0x13, 0xde, 0x24, 0x52, + 0x2b, 0x94, 0x7c, 0x88, 0x61, 0xc, 0xa2, 0xfc, + 0xa4, 0x2, 0x28, 0x0, 0x27, 0xfe, 0x20, 0x20, + 0x23, 0x28, 0x26, 0x24, 0x28, 0x22, 0x21, 0xc0, + + /* U+50BB "傻" */ + 0x0, 0x0, 0x21, 0x0, 0x5f, 0xf0, 0xa8, 0xa2, + 0x4e, 0x44, 0x9e, 0x99, 0x41, 0x53, 0xfe, 0xa1, + 0x10, 0x4d, 0x1c, 0xa7, 0xe1, 0x18, 0x42, 0xc9, + 0x4, 0xc, 0x8, 0x6e, 0x17, 0x3, + + /* U+50CF "像" */ + 0x11, 0x0, 0x27, 0xe0, 0x90, 0x81, 0x7f, 0xe2, + 0x44, 0x4c, 0x88, 0xa9, 0xff, 0x50, 0x42, 0x23, + 0x58, 0x59, 0xe0, 0x8d, 0xc1, 0x65, 0x42, 0x12, + 0x45, 0xc4, 0x8, 0x70, 0x0, + + /* U+50D6 "僖" */ + 0x10, 0x40, 0x3f, 0xfc, 0x81, 0x1, 0x3f, 0xe2, + 0x0, 0xc, 0xff, 0xa9, 0x1, 0x53, 0xfe, 0x21, + 0x10, 0x5f, 0xfc, 0x80, 0x1, 0x3f, 0xe2, 0x40, + 0x44, 0xff, 0x89, 0x1, 0x0, + + /* U+50DA "僚" */ + 0x0, 0x40, 0x20, 0x80, 0x5f, 0xf9, 0x3, 0x2, + 0x29, 0x4c, 0x21, 0x19, 0xff, 0xd5, 0x3, 0x22, + 0x4, 0x47, 0xf8, 0x88, 0x11, 0x1f, 0xe2, 0x4, + 0x4, 0x49, 0xb, 0x11, 0x90, 0xe0, + + /* U+50E6 "僦" */ + 0x0, 0x0, 0x11, 0x10, 0x11, 0x14, 0x2f, 0xd2, + 0x20, 0x10, 0x27, 0xbf, 0x64, 0x94, 0xa4, 0x94, + 0xa4, 0x94, 0x27, 0x94, 0x21, 0x14, 0x25, 0xa4, + 0x29, 0x65, 0x29, 0x25, 0x21, 0x45, 0x26, 0x47, + + /* U+50E7 "僧" */ + 0x11, 0x8, 0x22, 0x20, 0x5f, 0xf9, 0x22, 0x12, + 0x55, 0xac, 0x88, 0x69, 0xff, 0xd0, 0x0, 0x23, + 0xfc, 0x44, 0x8, 0x88, 0x11, 0x1f, 0xe2, 0x20, + 0x44, 0x7f, 0x88, 0x81, 0x0, + + /* U+50EC "僬" */ + 0x12, 0x20, 0x24, 0x40, 0x8f, 0xf9, 0x21, 0x2, + 0xc2, 0xd, 0xff, 0x99, 0x8, 0x53, 0xfe, 0xa4, + 0x20, 0x48, 0x40, 0x9f, 0xf9, 0x20, 0x2, 0x42, + 0x45, 0x12, 0x8a, 0x24, 0x80, + + /* U+50ED "僭" */ + 0x18, 0x0, 0x2f, 0x7c, 0x94, 0xa1, 0x3d, 0xf2, + 0x20, 0x8c, 0x63, 0x29, 0x6a, 0xd0, 0x22, 0x23, + 0xfc, 0x48, 0x8, 0x90, 0x11, 0x1f, 0xe2, 0x40, + 0x44, 0x7f, 0x89, 0x1, 0x0, + + /* U+50EE "僮" */ + 0x0, 0x40, 0x20, 0x80, 0x5f, 0xf1, 0x10, 0x42, + 0x11, 0x5, 0xff, 0xd8, 0x0, 0x53, 0xfe, 0xa4, + 0x44, 0x4f, 0xf8, 0x91, 0x11, 0x3f, 0xe2, 0x4, + 0x4, 0xff, 0x88, 0x10, 0x17, 0xff, + + /* U+50F3 "僳" */ + 0x10, 0x0, 0x3f, 0xfc, 0x84, 0x81, 0x3f, 0xe2, + 0x92, 0x4d, 0x24, 0xa9, 0xff, 0x50, 0x0, 0x26, + 0x4c, 0x42, 0xa0, 0xbf, 0xf9, 0x6, 0x2, 0x17, + 0x5, 0xc9, 0xc8, 0x10, 0x0, + + /* U+50F5 "僵" */ + 0x1f, 0xfe, 0x20, 0x0, 0x9f, 0xf1, 0x22, 0x22, + 0x7f, 0xcc, 0x88, 0x98, 0xee, 0x57, 0xff, 0x20, + 0x0, 0x4f, 0xf8, 0x9f, 0xf1, 0x22, 0x22, 0x3b, + 0x85, 0xff, 0xc8, 0x0, 0x0, + + /* U+50FB "僻" */ + 0x10, 0x8, 0x2f, 0x10, 0x92, 0xf9, 0x24, 0x2, + 0x49, 0x4c, 0xf2, 0xa9, 0x2, 0x52, 0x1f, 0x27, + 0x88, 0x55, 0x10, 0xaa, 0xf9, 0x94, 0x42, 0x38, + 0x84, 0x51, 0x8, 0xa2, 0x0, + + /* U+5106 "儆" */ + 0x14, 0x88, 0x24, 0x88, 0x2f, 0xd0, 0x24, 0x9f, + 0x24, 0x12, 0x67, 0xf2, 0x68, 0x72, 0xbf, 0x52, + 0x29, 0x54, 0x29, 0x4c, 0x29, 0x48, 0x2f, 0x4c, + 0x20, 0x54, 0x20, 0x62, 0x23, 0xa1, + + /* U+5107 "儇" */ + 0x10, 0x0, 0x2f, 0xf8, 0x52, 0x51, 0x24, 0xa2, + 0x7f, 0xcc, 0x0, 0x1b, 0xff, 0xd0, 0x0, 0xa3, + 0xfc, 0x48, 0x8, 0x8f, 0xf1, 0x7, 0x2, 0x12, + 0xc4, 0xe2, 0xa, 0x5b, 0x10, 0xc1, + + /* U+510B "儋" */ + 0x11, 0x0, 0x27, 0xe0, 0x58, 0x41, 0x7f, 0xf2, + 0x49, 0x8c, 0xa4, 0xa9, 0xff, 0xd2, 0x0, 0x25, + 0xfc, 0x48, 0x0, 0x97, 0xe1, 0x2f, 0xe2, 0xa0, + 0x45, 0x3f, 0x8c, 0x81, 0x0, + + /* U+5112 "儒" */ + 0x10, 0x0, 0x17, 0xfe, 0x20, 0x40, 0x2f, 0xfe, + 0x28, 0x42, 0x6b, 0xde, 0x60, 0x40, 0xa1, 0x8, + 0xaf, 0xff, 0x20, 0x40, 0x20, 0x40, 0x27, 0xfe, + 0x24, 0x92, 0x24, 0x92, 0x24, 0x92, 0x24, 0x9e, + + /* U+5121 "儡" */ + 0x10, 0x0, 0x2f, 0xf8, 0x51, 0x11, 0x3f, 0xe2, + 0x44, 0x44, 0x88, 0x99, 0xff, 0x50, 0x0, 0x2f, + 0xbe, 0x55, 0x54, 0xbe, 0xf9, 0x55, 0x52, 0xaa, + 0xa5, 0xf7, 0xca, 0x28, 0x80, + + /* U+513F "儿" */ + 0x8, 0x40, 0x10, 0x80, 0x21, 0x0, 0x42, 0x0, + 0x84, 0x1, 0x8, 0x2, 0x10, 0x4, 0x20, 0x8, + 0x40, 0x30, 0x84, 0x41, 0x9, 0x82, 0x16, 0x4, + 0x38, 0x7, 0x80, + + /* U+5140 "兀" */ + 0xff, 0xfe, 0x10, 0x80, 0x21, 0x0, 0x42, 0x0, + 0x84, 0x1, 0x8, 0x2, 0x10, 0x4, 0x20, 0x8, + 0x40, 0x20, 0x84, 0x41, 0x9, 0x2, 0x16, 0x4, + 0x38, 0x7, 0x80, + + /* U+5141 "允" */ + 0x0, 0x0, 0x2, 0x0, 0x4, 0x0, 0x8, 0x20, + 0x10, 0x10, 0x20, 0x8, 0x7f, 0xfc, 0x44, 0x46, + 0x4, 0x40, 0x4, 0x40, 0x4, 0x40, 0x4, 0x40, + 0x8, 0x42, 0x8, 0x42, 0x30, 0x42, 0x60, 0x3c, + 0x0, 0x0, + + /* U+5143 "元" */ + 0x3f, 0xfc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0xff, 0xfe, 0x8, 0x40, 0x8, 0x40, 0x8, 0x40, + 0x8, 0x40, 0x8, 0x40, 0x8, 0x42, 0x10, 0x42, + 0x20, 0x42, 0xc0, 0x3c, 0x0, 0x0, + + /* U+5144 "兄" */ + 0x3f, 0xf8, 0x40, 0x10, 0x80, 0x21, 0x0, 0x42, + 0x0, 0x84, 0x1, 0x8, 0x2, 0x1f, 0xfc, 0x4, + 0x40, 0x8, 0x80, 0x11, 0x0, 0x22, 0x10, 0x84, + 0x22, 0x8, 0x58, 0x1f, 0x0, 0x0, + + /* U+5145 "充" */ + 0x1, 0x0, 0x2, 0x3, 0xff, 0xf8, 0x10, 0x0, + 0x40, 0x1, 0x4, 0xc, 0x4, 0x3f, 0xfe, 0x4, + 0x44, 0x8, 0x80, 0x11, 0x0, 0x42, 0x10, 0x84, + 0x26, 0x8, 0x70, 0x1f, 0x0, + + /* U+5146 "兆" */ + 0x4, 0x40, 0x4, 0x40, 0x4, 0x40, 0x44, 0x44, + 0x24, 0x48, 0x14, 0x50, 0x4, 0x40, 0x4, 0x40, + 0x1c, 0x70, 0x64, 0x4c, 0x4, 0x40, 0x8, 0x41, + 0x18, 0x41, 0x30, 0x42, 0x60, 0x3e, + + /* U+5148 "先" */ + 0x11, 0x0, 0x22, 0x0, 0x44, 0x1, 0xff, 0xe2, + 0x10, 0x8, 0x20, 0x0, 0x40, 0x7f, 0xff, 0x4, + 0x40, 0x10, 0x80, 0x21, 0x0, 0x42, 0x11, 0x4, + 0x24, 0x8, 0x70, 0x1f, 0x0, + + /* U+5149 "光" */ + 0x1, 0x0, 0x2, 0x1, 0x84, 0x31, 0x8, 0x41, + 0x11, 0x0, 0x24, 0x0, 0x40, 0x7f, 0xff, 0x4, + 0x40, 0x10, 0x80, 0x21, 0x0, 0x42, 0x0, 0x84, + 0x22, 0x8, 0x48, 0x10, 0xe0, 0x3e, + + /* U+514B "克" */ + 0x1, 0x0, 0x2, 0x0, 0x4, 0x7, 0xff, 0xf0, + 0x10, 0x7, 0xff, 0x8, 0x2, 0x10, 0x4, 0x20, + 0x8, 0x7f, 0xf0, 0x11, 0x0, 0x22, 0x0, 0x44, + 0x21, 0x8, 0x44, 0x10, 0xf0, 0x3e, + + /* U+514D "免" */ + 0x0, 0x0, 0x10, 0x0, 0x3f, 0x80, 0x81, 0x2, + 0x4, 0xf, 0xff, 0x8, 0x42, 0x10, 0x84, 0x21, + 0x8, 0x7f, 0xf0, 0xa, 0x0, 0x14, 0x0, 0x48, + 0x21, 0x10, 0x4c, 0x21, 0xe0, 0x7e, + + /* U+5151 "兑" */ + 0x8, 0x10, 0x4, 0x20, 0x4, 0x40, 0x3f, 0xfc, + 0x20, 0x4, 0x20, 0x4, 0x20, 0x4, 0x20, 0x4, + 0x3f, 0xfc, 0x4, 0x40, 0x4, 0x40, 0x4, 0x41, + 0x8, 0x41, 0x10, 0x42, 0x60, 0x7e, 0x0, 0x0, + + /* U+5154 "兔" */ + 0x0, 0x0, 0x8, 0x0, 0x3f, 0x80, 0x81, 0x2, + 0x4, 0xf, 0xff, 0x28, 0x42, 0x10, 0x84, 0x21, + 0x8, 0x7f, 0xf0, 0xa, 0x0, 0x15, 0x0, 0x49, + 0x21, 0x10, 0x4c, 0x20, 0xf0, 0x7e, + + /* U+5155 "兕" */ + 0x7c, 0xf9, 0x12, 0x24, 0x48, 0x91, 0x22, 0x47, + 0x89, 0x0, 0x24, 0x0, 0x9f, 0xfe, 0x4, 0x80, + 0x12, 0x0, 0x48, 0x2, 0x20, 0x18, 0x84, 0xc2, + 0x1c, 0x7, 0xc0, + + /* U+5156 "兖" */ + 0x2, 0x0, 0x2, 0x3, 0xff, 0xf0, 0x0, 0x1, + 0x86, 0x4, 0x3, 0x21, 0x81, 0x4, 0x20, 0x10, + 0x20, 0x7f, 0xe0, 0x22, 0x20, 0x44, 0x0, 0x88, + 0x23, 0x10, 0x8c, 0x21, 0x60, 0x3e, + + /* U+515A "党" */ + 0x1, 0x0, 0x62, 0x60, 0x4, 0x7, 0xff, 0xe8, + 0x0, 0x57, 0xfe, 0x88, 0x4, 0x10, 0x8, 0x20, + 0x10, 0x7f, 0xe0, 0x12, 0x0, 0x24, 0x10, 0x88, + 0x23, 0x8, 0xb8, 0x1f, 0x0, + + /* U+515C "兜" */ + 0x1, 0x0, 0x2, 0x1, 0xdf, 0x72, 0x22, 0x24, + 0x44, 0x48, 0x88, 0x91, 0xf1, 0x22, 0x22, 0x54, + 0x44, 0xcf, 0xb8, 0x0, 0x0, 0x22, 0x0, 0x44, + 0x1, 0x88, 0x46, 0x10, 0xf0, 0x3f, + + /* U+5162 "兢" */ + 0x0, 0x10, 0x20, 0x21, 0xfb, 0xf8, 0x80, 0x81, + 0x1, 0xf, 0xdf, 0xd0, 0xa0, 0xa1, 0x41, 0x7e, + 0xfe, 0x30, 0x50, 0xa0, 0xa1, 0x42, 0x42, 0xa4, + 0xa4, 0x91, 0x50, 0x63, 0x80, + + /* U+5165 "入" */ + 0x4, 0x0, 0x2, 0x0, 0x1, 0x0, 0x1, 0x0, + 0x1, 0x80, 0x1, 0x80, 0x2, 0x40, 0x2, 0x40, + 0x4, 0x20, 0x4, 0x20, 0x8, 0x10, 0x10, 0x8, + 0x20, 0x4, 0x40, 0x2, + + /* U+5168 "全" */ + 0x1, 0x80, 0x1, 0x80, 0x6, 0x40, 0xc, 0x30, + 0x18, 0x8, 0x60, 0x6, 0x1f, 0xf8, 0x1, 0x0, + 0x1, 0x0, 0x3f, 0xfc, 0x1, 0x0, 0x1, 0x0, + 0x1, 0x0, 0x7f, 0xfe, + + /* U+516B "八" */ + 0x4, 0x20, 0x4, 0x20, 0x4, 0x20, 0x4, 0x20, + 0x4, 0x20, 0x8, 0x10, 0x8, 0x10, 0x8, 0x10, + 0x18, 0x8, 0x10, 0x8, 0x30, 0x4, 0x20, 0x6, + 0x40, 0x3, 0x0, 0x0, + + /* U+516C "公" */ + 0x0, 0x0, 0x4, 0x20, 0x4, 0x20, 0x8, 0x10, + 0x18, 0x18, 0x10, 0xc, 0x60, 0x6, 0x41, 0x0, + 0x2, 0x0, 0x4, 0x0, 0x4, 0x20, 0x8, 0x10, + 0x10, 0x8, 0x3f, 0xfc, 0x0, 0x4, + + /* U+516D "六" */ + 0x2, 0x0, 0x2, 0x0, 0x6, 0x0, 0x0, 0xf, + 0xff, 0xe0, 0x0, 0x0, 0x0, 0x2, 0x20, 0x8, + 0x20, 0x30, 0x20, 0x40, 0x41, 0x0, 0x44, 0x0, + 0x58, 0x0, 0xc0, + + /* U+516E "兮" */ + 0x2, 0x40, 0x8, 0x80, 0x20, 0x80, 0x80, 0x82, + 0x0, 0x8b, 0xfe, 0xc1, 0x0, 0x4, 0x0, 0x8, + 0x0, 0x1f, 0xe0, 0x0, 0x40, 0x0, 0x80, 0x1, + 0x0, 0x2, 0x0, 0xf8, 0x0, + + /* U+5170 "兰" */ + 0x10, 0x10, 0x10, 0x40, 0x11, 0x3, 0xff, 0xf0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3f, + 0xf8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x1f, 0xff, 0xc0, + + /* U+5171 "共" */ + 0x8, 0x20, 0x10, 0x40, 0x20, 0x80, 0x41, 0x7, + 0xff, 0xc1, 0x4, 0x2, 0x8, 0x4, 0x10, 0x8, + 0x21, 0xff, 0xfc, 0x0, 0x0, 0x2, 0x0, 0x82, + 0x6, 0x3, 0x30, 0x1, 0x80, + + /* U+5173 "关" */ + 0x10, 0x20, 0x21, 0x0, 0x88, 0x1f, 0xfe, 0x2, + 0x0, 0x8, 0x0, 0x20, 0x3f, 0xff, 0x3, 0x0, + 0xc, 0x0, 0x48, 0x2, 0x30, 0x10, 0x21, 0x80, + 0x68, 0x0, 0x40, + + /* U+5174 "兴" */ + 0x2, 0x0, 0x82, 0x8, 0x84, 0x21, 0x8, 0x41, + 0x9, 0x2, 0x0, 0x0, 0x0, 0x7f, 0xff, 0x0, + 0x0, 0x0, 0x0, 0x20, 0x80, 0x80, 0x82, 0x0, + 0x88, 0x0, 0x80, 0x0, 0x0, + + /* U+5175 "兵" */ + 0x0, 0x8, 0x3f, 0xe0, 0x40, 0x0, 0x80, 0x1, + 0x0, 0x3, 0xff, 0xc4, 0x8, 0x8, 0x10, 0x10, + 0x21, 0xff, 0xfc, 0x0, 0x0, 0x61, 0x1, 0x1, + 0x8c, 0x0, 0x80, 0x0, 0x0, + + /* U+5176 "其" */ + 0x10, 0x10, 0x20, 0x23, 0xff, 0xf8, 0x80, 0x81, + 0x1, 0x3, 0xfe, 0x4, 0x4, 0xf, 0xf8, 0x10, + 0x10, 0x20, 0x23, 0xff, 0xf8, 0x0, 0x0, 0xc6, + 0xe, 0x3, 0xa0, 0x0, 0x80, + + /* U+5177 "具" */ + 0x3f, 0xf8, 0x40, 0x10, 0x80, 0x21, 0xff, 0xc2, + 0x0, 0x84, 0x1, 0xf, 0xfe, 0x10, 0x4, 0x3f, + 0xf8, 0x40, 0x10, 0x80, 0x27, 0xff, 0xf0, 0x0, + 0x3, 0x8e, 0x18, 0x3, 0x80, 0x0, + + /* U+5178 "典" */ + 0x4, 0x40, 0x8, 0x80, 0xff, 0xe2, 0x22, 0x44, + 0x44, 0x88, 0x89, 0xf, 0xfe, 0x22, 0x24, 0x44, + 0x48, 0x88, 0x93, 0xff, 0xf8, 0x0, 0x0, 0xc6, + 0x6, 0x3, 0xb0, 0x0, 0x80, + + /* U+5179 "兹" */ + 0x0, 0x0, 0x10, 0x40, 0x1, 0x7, 0xff, 0xf0, + 0x82, 0x2, 0x8, 0x8, 0x10, 0x11, 0x42, 0x44, + 0x89, 0xf3, 0xe0, 0x20, 0x40, 0x81, 0x2, 0x44, + 0x88, 0x50, 0x9f, 0xbf, 0x0, 0x81, + + /* U+517B "养" */ + 0x18, 0x10, 0x18, 0x41, 0xff, 0xf0, 0x8, 0x7, + 0xff, 0x80, 0x40, 0x0, 0x80, 0x7f, 0xff, 0x4, + 0x40, 0x30, 0x61, 0xa0, 0xbc, 0x41, 0x0, 0x82, + 0x2, 0x4, 0x18, 0x8, 0x0, + + /* U+517C "兼" */ + 0x8, 0x20, 0x8, 0x83, 0xff, 0xf8, 0x24, 0x3, + 0xff, 0x80, 0x91, 0x3f, 0xff, 0x82, 0x44, 0x4, + 0x88, 0xff, 0xf0, 0x11, 0x0, 0x65, 0x1, 0x49, + 0xc, 0x91, 0xa1, 0x20, 0x0, + + /* U+517D "兽" */ + 0x8, 0x30, 0x8, 0x41, 0xff, 0xf2, 0x8, 0x27, + 0xff, 0xc8, 0x20, 0x9f, 0xff, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xe1, 0x0, 0x42, 0x0, + 0x87, 0xff, 0x8, 0x2, 0x0, + + /* U+5180 "冀" */ + 0x4, 0x40, 0xf8, 0xf8, 0x11, 0xf, 0xe3, 0xf0, + 0x0, 0x7, 0xff, 0xf, 0xfe, 0x10, 0x84, 0x3f, + 0xf8, 0x8, 0x41, 0xff, 0xf0, 0x21, 0xf, 0xff, + 0xe0, 0x84, 0x1e, 0x7, 0x80, + + /* U+5181 "冁" */ + 0x48, 0x0, 0x49, 0xfe, 0x11, 0x2, 0xed, 0xfe, + 0xa5, 0x0, 0xa5, 0x28, 0xfd, 0xfe, 0xa5, 0x28, + 0xa5, 0x28, 0x7d, 0xff, 0x21, 0x52, 0xfd, 0x4c, + 0x23, 0x48, 0x22, 0x56, 0x24, 0x60, + + /* U+5182 "冂" */ + 0xff, 0xf8, 0x1, 0x80, 0x18, 0x1, 0x80, 0x18, + 0x1, 0x80, 0x18, 0x1, 0x80, 0x18, 0x1, 0x80, + 0x18, 0x1, 0x80, 0x18, 0x1e, + + /* U+5185 "内" */ + 0x2, 0x0, 0x10, 0x0, 0x81, 0xff, 0xf8, 0x20, + 0xc1, 0x6, 0x8, 0x30, 0xc1, 0x85, 0x8c, 0x46, + 0x6c, 0xb, 0x0, 0x18, 0x0, 0xc0, 0x6, 0x1, + 0xe0, + + /* U+5188 "冈" */ + 0xff, 0xfc, 0x0, 0x60, 0x13, 0x20, 0x98, 0x88, + 0xc2, 0x86, 0x8, 0x30, 0xe1, 0x8d, 0x8c, 0xc4, + 0x6c, 0x13, 0x0, 0x18, 0x0, 0xc0, 0x78, + + /* U+5189 "冉" */ + 0x1, 0x0, 0x2, 0x0, 0xff, 0xe1, 0x8, 0x42, + 0x10, 0x87, 0xff, 0x8, 0x42, 0x10, 0x84, 0x21, + 0x9, 0xff, 0xfc, 0x80, 0x21, 0x0, 0x42, 0x0, + 0x84, 0x1, 0x8, 0x1e, 0x0, + + /* U+518C "册" */ + 0x3e, 0x7c, 0x44, 0x88, 0x89, 0x11, 0x12, 0x22, + 0x24, 0x5f, 0xff, 0xc8, 0x91, 0x11, 0x22, 0x22, + 0x44, 0x44, 0x88, 0x89, 0x12, 0x14, 0x24, 0xe8, + 0x58, 0x33, 0x0, + + /* U+518D "再" */ + 0x7f, 0xfc, 0x2, 0x0, 0xff, 0xe1, 0x8, 0x42, + 0x10, 0x87, 0xff, 0x8, 0x42, 0x10, 0x84, 0x21, + 0x9, 0xff, 0xfc, 0x80, 0x21, 0x0, 0x42, 0x0, + 0x84, 0xf, 0x0, + + /* U+5192 "冒" */ + 0xff, 0xfc, 0x0, 0x6f, 0xfb, 0x0, 0x1b, 0xfe, + 0x80, 0x1, 0xff, 0xc8, 0x2, 0x7f, 0xf2, 0x0, + 0x9f, 0xfc, 0x80, 0x27, 0xff, 0x20, 0x8, + + /* U+5195 "冕" */ + 0x7f, 0xfc, 0x80, 0x9, 0x7f, 0xd2, 0x0, 0x25, + 0xff, 0x41, 0x0, 0x7, 0xfc, 0x18, 0x10, 0x7f, + 0xf8, 0x42, 0x10, 0xff, 0xe0, 0x14, 0x0, 0x68, + 0x23, 0x10, 0x78, 0x1f, 0x0, + + /* U+5196 "冖" */ + 0xff, 0xfe, 0x0, 0x18, 0x0, 0x60, 0x1, + + /* U+5197 "冗" */ + 0x7f, 0xfc, 0x40, 0x4, 0x40, 0x4, 0x40, 0x4, + 0xf, 0xe0, 0x8, 0x20, 0x8, 0x20, 0x8, 0x20, + 0x8, 0x20, 0x8, 0x20, 0x8, 0x23, 0x10, 0x22, + 0x20, 0x22, 0xc0, 0x3e, + + /* U+5199 "写" */ + 0x7f, 0xff, 0x20, 0x4, 0x40, 0x8, 0xff, 0xe1, + 0x0, 0x4, 0x0, 0xf, 0xff, 0x0, 0x2, 0x0, + 0x5, 0xff, 0xc8, 0x0, 0x10, 0x0, 0x20, 0x0, + 0x80, 0x3f, 0x0, + + /* U+519B "军" */ + 0xff, 0xfd, 0x0, 0xa, 0x10, 0x7, 0xff, 0xe1, + 0x0, 0x2, 0x20, 0x8, 0x40, 0x3f, 0xfe, 0x1, + 0x0, 0x2, 0x3, 0xff, 0xf8, 0x8, 0x0, 0x10, + 0x0, 0x20, 0x0, + + /* U+519C "农" */ + 0x1, 0x0, 0x2, 0x1, 0xff, 0xf2, 0x10, 0x24, + 0x20, 0x48, 0x60, 0x1, 0x42, 0x4, 0x58, 0x18, + 0xc0, 0x50, 0x81, 0x20, 0x84, 0x40, 0xc0, 0x98, + 0xc1, 0xc0, 0x42, 0x0, 0x0, + + /* U+51A0 "冠" */ + 0x7f, 0xfe, 0x40, 0x2, 0x40, 0xa, 0x3e, 0x8, + 0x0, 0xfe, 0x0, 0x8, 0x7f, 0x48, 0x14, 0x48, + 0x14, 0x8, 0x14, 0x9, 0x24, 0x79, 0x24, 0x1, + 0x24, 0x2, 0x43, 0xfe, + + /* U+51A2 "冢" */ + 0xff, 0xfd, 0x0, 0xa, 0x0, 0x11, 0xff, 0x80, + 0x20, 0x1, 0x83, 0x1c, 0x98, 0x4b, 0xe0, 0x18, + 0xa0, 0xd3, 0x42, 0x1a, 0x40, 0xc4, 0x67, 0x8, + 0x11, 0xe0, 0x0, + + /* U+51A4 "冤" */ + 0x7f, 0xfe, 0x42, 0x2, 0x47, 0xf2, 0x8, 0x20, + 0x10, 0x20, 0x7f, 0xfc, 0x20, 0x84, 0x20, 0x84, + 0x1f, 0xfc, 0x1, 0x40, 0x3, 0x50, 0x6, 0x49, + 0x1c, 0x42, 0xf0, 0x7e, + + /* U+51A5 "冥" */ + 0xff, 0xfd, 0x0, 0xa, 0xff, 0xd1, 0x0, 0x83, + 0xff, 0x4, 0x2, 0x8, 0x4, 0x1f, 0xf8, 0x1, + 0x1, 0xff, 0xfc, 0x0, 0x0, 0x67, 0x7, 0x1, + 0xd0, 0x0, 0x0, + + /* U+51AB "冫" */ + 0x84, 0x20, 0x0, 0x2, 0x24, 0x4c, 0x80, + + /* U+51AC "冬" */ + 0x4, 0x0, 0x4, 0x0, 0xf, 0xf8, 0x18, 0x8, + 0x34, 0x10, 0x62, 0x20, 0x1, 0xc0, 0x3, 0xc0, + 0xc, 0x3c, 0x70, 0x2, 0x7, 0x80, 0x0, 0x60, + 0x0, 0x0, 0x1c, 0x0, 0x3, 0xe0, 0x0, 0x10, + + /* U+51AF "冯" */ + 0x4f, 0xfc, 0x80, 0x10, 0x88, 0x21, 0x20, 0x40, + 0x40, 0x80, 0x81, 0x9, 0xff, 0x90, 0x1, 0x20, + 0x2, 0x5f, 0xf5, 0x0, 0xa, 0x0, 0x1c, 0x0, + 0x60, 0xf, 0x80, + + /* U+51B0 "冰" */ + 0x0, 0x41, 0x80, 0x81, 0x1, 0x1, 0x2, 0x10, + 0xf6, 0x40, 0x2d, 0x0, 0x5c, 0x1, 0x28, 0x22, + 0x50, 0x44, 0x91, 0x11, 0x12, 0x42, 0x35, 0x84, + 0x30, 0x8, 0x0, 0xf0, 0x0, + + /* U+51B1 "冱" */ + 0x4f, 0xfe, 0x84, 0x0, 0x88, 0x0, 0x90, 0x0, + 0x3f, 0x80, 0x41, 0x1, 0x2, 0x12, 0x4, 0x27, + 0xf8, 0x40, 0x11, 0x0, 0x22, 0x0, 0x44, 0xff, + 0xe0, + + /* U+51B2 "冲" */ + 0x0, 0x43, 0x1, 0x4, 0x4, 0x9, 0xff, 0x24, + 0x44, 0x11, 0x10, 0x44, 0x41, 0x11, 0x24, 0x44, + 0x9f, 0xf4, 0x44, 0x50, 0x10, 0x40, 0x42, 0x1, + 0x0, 0x4, 0x0, + + /* U+51B3 "决" */ + 0x0, 0x80, 0x1, 0x1, 0x2, 0x1, 0x3f, 0xc1, + 0x8, 0x80, 0x11, 0x0, 0x22, 0x10, 0x44, 0x2f, + 0xfe, 0x41, 0x81, 0x5, 0x2, 0x9, 0x4, 0x21, + 0x10, 0x81, 0x6, 0x1, 0x80, + + /* U+51B5 "况" */ + 0x7, 0xfc, 0x88, 0x8, 0x90, 0x11, 0x20, 0x20, + 0x40, 0x40, 0x80, 0x81, 0xff, 0x18, 0x90, 0x21, + 0x20, 0x42, 0x41, 0xc, 0x8a, 0x11, 0x18, 0x42, + 0x23, 0x7, 0x80, + + /* U+51B6 "冶" */ + 0x0, 0x81, 0x2, 0x1, 0x4, 0x41, 0x10, 0x40, + 0x40, 0x41, 0xff, 0xc0, 0x0, 0x90, 0x0, 0x27, + 0xfc, 0x88, 0x9, 0x10, 0x12, 0x20, 0x28, 0x40, + 0x50, 0xff, 0x81, 0x1, 0x0, + + /* U+51B7 "冷" */ + 0x0, 0x82, 0x7, 0x4, 0x12, 0x8, 0x8c, 0xd, + 0x18, 0x62, 0x10, 0x4, 0x0, 0x0, 0x4f, 0xf9, + 0x0, 0x64, 0x3, 0x11, 0x88, 0x81, 0x42, 0x3, + 0x0, 0x2, 0x0, + + /* U+51BB "冻" */ + 0x0, 0x80, 0x81, 0x1, 0x3f, 0xf9, 0x8, 0x0, + 0x22, 0x0, 0x44, 0x1, 0x8, 0x13, 0xff, 0x20, + 0x20, 0x40, 0x41, 0x8, 0xa2, 0x21, 0x24, 0xc2, + 0x33, 0x4, 0x40, 0x70, 0x0, + + /* U+51BC "冼" */ + 0x0, 0x40, 0x84, 0x81, 0x9, 0x1, 0x3f, 0xe2, + 0x44, 0x1, 0x8, 0x0, 0x10, 0x7, 0xff, 0x21, + 0x20, 0x42, 0x41, 0x4, 0x82, 0x9, 0x4, 0x22, + 0x28, 0x84, 0x62, 0x7, 0x80, 0x0, + + /* U+51BD "冽" */ + 0x0, 0x3, 0x1f, 0x85, 0x8, 0x49, 0x20, 0x90, + 0x79, 0x20, 0x92, 0x42, 0x24, 0x96, 0x49, 0x32, + 0x92, 0x82, 0x25, 0x4, 0x4a, 0x10, 0x18, 0x20, + 0x20, 0x80, 0x46, 0x7, 0x80, + + /* U+51C0 "净" */ + 0x0, 0x0, 0x8, 0x8, 0x7e, 0x23, 0x8, 0x58, + 0x41, 0x3f, 0xe0, 0x8, 0x80, 0x22, 0x1f, 0xfd, + 0x2, 0x24, 0x8, 0xa3, 0xfe, 0x80, 0x8a, 0x2, + 0x0, 0x8, 0x1, 0xc0, + + /* U+51C4 "凄" */ + 0x0, 0x82, 0x3f, 0xf4, 0x8, 0x1b, 0xfe, 0x0, + 0x88, 0x7f, 0xf0, 0x8, 0x93, 0xfe, 0x40, 0x1, + 0x4, 0x5, 0xff, 0xe1, 0x8, 0x86, 0x62, 0x7, + 0x1, 0xf3, 0x80, + + /* U+51C6 "准" */ + 0x1, 0x1, 0x4, 0x41, 0x8, 0x2, 0x3f, 0xf2, + 0xc2, 0x2, 0x84, 0x5, 0xff, 0x82, 0x10, 0x24, + 0x20, 0x4f, 0xfd, 0x10, 0x82, 0x21, 0x4, 0x42, + 0x10, 0xff, 0xc1, 0x0, 0x0, + + /* U+51C7 "凇" */ + 0x8, 0x11, 0x10, 0xa2, 0x22, 0x22, 0xf4, 0x44, + 0x88, 0x81, 0x20, 0x83, 0x49, 0x2d, 0x10, 0x58, + 0x40, 0xb0, 0xa2, 0xa2, 0x44, 0x44, 0x48, 0x98, + 0x91, 0x3f, 0x82, 0x1, 0x0, + + /* U+51C9 "凉" */ + 0x0, 0x40, 0x80, 0x81, 0xbf, 0xf9, 0x0, 0x0, + 0x7f, 0xc0, 0x80, 0x81, 0x1, 0x12, 0x2, 0x27, + 0xfc, 0x40, 0x81, 0x9, 0x22, 0x32, 0x44, 0xc4, + 0x53, 0x8, 0x40, 0x70, 0x0, + + /* U+51CB "凋" */ + 0x8f, 0xfe, 0x21, 0x14, 0x84, 0x4a, 0xfd, 0x8, + 0x44, 0x3f, 0xd0, 0x80, 0x52, 0xfd, 0x4a, 0x15, + 0x28, 0x58, 0xbf, 0x64, 0x1, 0x10, 0x4, 0x80, + 0xf0, + + /* U+51CC "凌" */ + 0x0, 0x40, 0x80, 0x81, 0x1f, 0xf1, 0x2, 0x0, + 0xff, 0xe0, 0x0, 0x0, 0x87, 0x6, 0x81, 0x23, + 0xf8, 0x84, 0x11, 0x34, 0x42, 0x45, 0x4, 0xe, + 0x10, 0xe3, 0xc2, 0x0, 0x0, + + /* U+51CF "减" */ + 0x0, 0x10, 0x0, 0x31, 0x0, 0x52, 0x7f, 0xf2, + 0x81, 0x5, 0x2, 0x2, 0xf4, 0x4, 0xa, 0x2b, + 0xd4, 0x94, 0xb1, 0x29, 0x62, 0x52, 0x45, 0xbd, + 0xb2, 0x7, 0x44, 0xa, 0x90, 0x23, + + /* U+51D1 "凑" */ + 0x0, 0x40, 0x0, 0x81, 0x1f, 0xf1, 0x4, 0x2, + 0x8, 0x4, 0xff, 0x80, 0x20, 0x7, 0xff, 0x22, + 0x10, 0x4b, 0xf0, 0xe1, 0x1a, 0x3f, 0xe4, 0x8, + 0x8, 0x1c, 0x1, 0xc7, 0x4, 0x0, + + /* U+51DB "凛" */ + 0x0, 0x40, 0x0, 0x82, 0x7f, 0xfa, 0x0, 0x4, + 0xff, 0xc5, 0x0, 0x82, 0xfd, 0x4, 0xfa, 0x8, + 0x4, 0x8f, 0xf1, 0x1f, 0xf2, 0x0, 0x9, 0xff, + 0xf0, 0x8, 0x7, 0x93, 0x81, 0xe0, + + /* U+51DD "凝" */ + 0x10, 0x0, 0x91, 0x7e, 0x4e, 0x4, 0x51, 0x28, + 0xf, 0x18, 0x0, 0x10, 0x8, 0x3e, 0xf, 0x12, + 0x54, 0x50, 0x44, 0x50, 0x5f, 0x5e, 0x84, 0x50, + 0x8b, 0x70, 0x10, 0x9e, 0x0, 0x0, + + /* U+51E0 "几" */ + 0xf, 0xe0, 0x10, 0x40, 0x20, 0x80, 0x41, 0x0, + 0x82, 0x1, 0x4, 0x2, 0x8, 0x4, 0x10, 0x8, + 0x20, 0x20, 0x44, 0x40, 0x89, 0x1, 0x14, 0x3, + 0xe0, + + /* U+51E1 "凡" */ + 0x1f, 0xe0, 0x20, 0x40, 0x40, 0x80, 0x81, 0x1, + 0x2, 0x2, 0x44, 0x4, 0x48, 0x8, 0x10, 0x10, + 0x20, 0x40, 0x44, 0x80, 0x8b, 0x1, 0x1c, 0x3, + 0xc0, + + /* U+51E4 "凤" */ + 0x3f, 0xf8, 0x40, 0x10, 0x80, 0x21, 0x7f, 0x42, + 0x2, 0x84, 0x5, 0x9, 0x12, 0x11, 0x24, 0x21, + 0x88, 0x43, 0x10, 0x89, 0x2a, 0x61, 0x55, 0x0, + 0xb8, 0x0, 0xc0, + + /* U+51EB "凫" */ + 0x2, 0x0, 0x4, 0x0, 0xff, 0xe1, 0x0, 0x42, + 0x20, 0x84, 0x3e, 0x8, 0x0, 0x1f, 0xfe, 0x0, + 0x4, 0x0, 0x8, 0x3f, 0x10, 0x42, 0xc0, 0x84, + 0x22, 0x8, 0x4c, 0x10, 0xf0, 0x1f, + + /* U+51ED "凭" */ + 0x10, 0x0, 0x2f, 0xe0, 0x81, 0x3, 0x2, 0xa, + 0xff, 0xe4, 0x8, 0x8, 0x10, 0x13, 0xfe, 0x0, + 0x0, 0x1f, 0x80, 0x21, 0x0, 0x42, 0x10, 0x84, + 0x22, 0x8, 0x78, 0xf, 0x0, + + /* U+51EF "凯" */ + 0x10, 0x1, 0x29, 0xf2, 0x52, 0x24, 0xa4, 0x4f, + 0xc8, 0x80, 0x11, 0x3f, 0x22, 0x2, 0x44, 0x4, + 0x88, 0xf9, 0x12, 0x2, 0x24, 0x4, 0x58, 0x10, + 0xbf, 0xa1, 0x60, 0x83, 0x80, + + /* U+51F0 "凰" */ + 0x7f, 0xf8, 0x88, 0x11, 0x7f, 0x22, 0x82, 0x45, + 0xfc, 0x8a, 0x9, 0x13, 0xe2, 0x2f, 0xf4, 0x42, + 0x8, 0x84, 0x11, 0x7f, 0x2c, 0x10, 0x5b, 0xff, + 0xb0, 0x0, 0xc0, + + /* U+51F3 "凳" */ + 0x1, 0x3, 0xf2, 0x80, 0x4c, 0x5a, 0x16, 0x17, + 0xb1, 0x80, 0x3b, 0xff, 0x8, 0x4, 0x3f, 0xf0, + 0x21, 0x0, 0x88, 0x3d, 0xdf, 0x1f, 0xc0, 0x41, + 0x11, 0x4, 0x78, 0xe, + + /* U+51F5 "凵" */ + 0x80, 0xc, 0x0, 0x60, 0x3, 0x0, 0x18, 0x0, + 0xc0, 0x6, 0x0, 0x30, 0x1, 0x80, 0xc, 0x0, + 0x60, 0x3, 0x0, 0x1f, 0xff, 0x80, 0x4, + + /* U+51F6 "凶" */ + 0x0, 0x0, 0x82, 0x4, 0x11, 0x11, 0x18, 0x50, + 0xc1, 0x86, 0xc, 0x30, 0xb1, 0x88, 0xcc, 0x82, + 0x68, 0x3, 0x0, 0x1f, 0xff, 0x80, 0x4, + + /* U+51F8 "凸" */ + 0x1f, 0xc0, 0x82, 0x4, 0x10, 0x20, 0x81, 0x4, + 0x78, 0x3e, 0x0, 0x30, 0x1, 0x80, 0xc, 0x0, + 0x60, 0x3, 0x0, 0x1f, 0xff, 0xc0, 0x4, + + /* U+51F9 "凹" */ + 0xf8, 0xfc, 0x44, 0x62, 0x23, 0x11, 0x18, 0x88, + 0xc4, 0x46, 0x3e, 0x30, 0x1, 0x80, 0xc, 0x0, + 0x60, 0x3, 0x0, 0x1f, 0xff, 0xc0, 0x4, + + /* U+51FA "出" */ + 0x2, 0x0, 0x20, 0x82, 0x18, 0x21, 0x82, 0x18, + 0x21, 0xff, 0xf0, 0x20, 0x2, 0x8, 0x21, 0x82, + 0x18, 0x21, 0x82, 0x1f, 0xff, 0x0, 0x10, + + /* U+51FB "击" */ + 0x1, 0x0, 0x2, 0x0, 0x4, 0x3, 0xff, 0xe0, + 0x10, 0x0, 0x20, 0x0, 0x40, 0x7f, 0xff, 0x1, + 0x0, 0x2, 0x1, 0x4, 0x22, 0x8, 0x44, 0x10, + 0x8f, 0xff, 0x0, 0x2, 0x0, + + /* U+51FC "凼" */ + 0x1, 0x0, 0x8, 0x20, 0x4b, 0x7a, 0x58, 0x5c, + 0xc2, 0xc6, 0x24, 0x31, 0x31, 0x91, 0x4d, 0x9, + 0x60, 0x43, 0xc, 0x18, 0x0, 0xff, 0xfc, 0x0, + 0x20, + + /* U+51FD "函" */ + 0x7f, 0xe0, 0x3, 0x0, 0x61, 0x44, 0x59, 0x24, + 0xc1, 0x6, 0x8, 0x32, 0x59, 0xe2, 0x2c, 0x10, + 0x67, 0x83, 0x0, 0x1f, 0xff, 0x80, 0x4, + + /* U+51FF "凿" */ + 0x4, 0x40, 0x48, 0x90, 0xd1, 0x40, 0x22, 0xf, + 0xff, 0xe1, 0x4, 0x11, 0x11, 0x2f, 0xfa, 0x41, + 0x4, 0x82, 0x9, 0x7f, 0xd2, 0x8, 0x24, 0x10, + 0x4f, 0xff, 0x80, 0x1, 0x0, + + /* U+5200 "刀" */ + 0xff, 0xf8, 0x20, 0x41, 0x2, 0x8, 0x10, 0x40, + 0x82, 0x4, 0x10, 0x21, 0x1, 0x8, 0x8, 0x40, + 0x44, 0x2, 0x60, 0x16, 0x1, 0xe0, 0xf8, + + /* U+5201 "刁" */ + 0x7f, 0xf8, 0x0, 0x40, 0x2, 0x0, 0x10, 0x4, + 0x80, 0x64, 0xc, 0x21, 0x81, 0x30, 0xf, 0x0, + 0x60, 0x2, 0x0, 0x10, 0x1, 0x80, 0xf8, + + /* U+5202 "刂" */ + 0x6, 0x18, 0x61, 0x86, 0x18, 0x61, 0x86, 0x18, + 0x41, 0x4, 0x17, 0x80, + + /* U+5203 "刃" */ + 0xff, 0xf8, 0x10, 0x40, 0x82, 0x44, 0x12, 0x20, + 0xa1, 0x5, 0x10, 0x38, 0x81, 0x4, 0x8, 0x40, + 0x42, 0x2, 0x20, 0x12, 0x1, 0xa0, 0xf8, + + /* U+5206 "分" */ + 0x0, 0x40, 0x8, 0x80, 0x20, 0x80, 0xc0, 0x83, + 0x1, 0x8c, 0x1, 0xb0, 0x0, 0x9f, 0xf8, 0x4, + 0x10, 0x8, 0x20, 0x10, 0x40, 0x40, 0x80, 0x81, + 0x2, 0x2, 0x18, 0x78, 0x0, + + /* U+5207 "切" */ + 0x20, 0x0, 0x9f, 0xf2, 0x8, 0x48, 0x21, 0x2c, + 0x87, 0xc2, 0x12, 0x8, 0x48, 0x21, 0x20, 0x84, + 0x86, 0x12, 0xd0, 0x4c, 0x41, 0x23, 0x4, 0x8, + 0x10, 0x47, 0x80, + + /* U+5208 "刈" */ + 0x0, 0x2, 0x82, 0x44, 0x88, 0x89, 0x11, 0x11, + 0x42, 0x22, 0x84, 0x42, 0x8, 0x84, 0x11, 0x14, + 0x22, 0x28, 0x44, 0x88, 0x89, 0x10, 0x14, 0x10, + 0x38, 0x0, 0x40, 0x7, 0x80, + + /* U+520A "刊" */ + 0x0, 0x7, 0xfc, 0x11, 0x4, 0x44, 0x11, 0x10, + 0x44, 0x41, 0x11, 0x4, 0x7f, 0xd1, 0x10, 0x44, + 0x41, 0x11, 0x4, 0x44, 0x1, 0x10, 0x4, 0x40, + 0x11, 0x3, 0xc0, + + /* U+520D "刍" */ + 0x8, 0x0, 0x40, 0x7, 0xf8, 0x40, 0x44, 0x4, + 0x40, 0x41, 0xff, 0xe0, 0x1, 0x0, 0x8, 0x0, + 0x5f, 0xfe, 0x0, 0x10, 0x0, 0x80, 0x7, 0xff, + 0xe0, 0x1, + + /* U+520E "刎" */ + 0x10, 0x2, 0x40, 0x24, 0xfe, 0x4a, 0x54, 0x95, + 0x29, 0x32, 0x52, 0x44, 0xa4, 0x8b, 0x49, 0x24, + 0x92, 0x89, 0x27, 0x32, 0x48, 0x44, 0x11, 0x8, + 0x26, 0x10, 0x51, 0xc7, 0x80, + + /* U+5211 "刑" */ + 0x0, 0x5, 0xfc, 0x12, 0x22, 0x48, 0x89, 0x22, + 0x24, 0x88, 0x92, 0x22, 0x7f, 0xe9, 0x22, 0x24, + 0x88, 0x92, 0x22, 0x48, 0x81, 0x42, 0x5, 0x8, + 0x18, 0x23, 0xc0, + + /* U+5212 "划" */ + 0x10, 0x2, 0x28, 0x4, 0x48, 0x88, 0x81, 0x11, + 0x3a, 0x2f, 0x84, 0x44, 0x8, 0x88, 0x91, 0x12, + 0x22, 0x18, 0x44, 0x20, 0x88, 0xc4, 0x16, 0x48, + 0x20, 0x90, 0x40, 0xc7, 0x0, + + /* U+5216 "刖" */ + 0x0, 0x2, 0x7e, 0x4, 0x84, 0x89, 0x9, 0x12, + 0x12, 0x27, 0xe4, 0x48, 0x48, 0x90, 0x91, 0x3f, + 0x22, 0x42, 0x44, 0x84, 0x89, 0x8, 0x12, 0x10, + 0x28, 0x20, 0x53, 0xc7, 0x80, + + /* U+5217 "列" */ + 0x0, 0x2, 0xff, 0x4, 0x20, 0x88, 0x81, 0x11, + 0xf2, 0x22, 0x24, 0x48, 0x48, 0xa8, 0x91, 0xca, + 0x22, 0x4, 0x44, 0x10, 0x88, 0x60, 0x11, 0x80, + 0x26, 0x0, 0x58, 0x7, 0x0, + + /* U+5218 "刘" */ + 0x10, 0x0, 0x60, 0x10, 0x84, 0x7d, 0xd1, 0x2, + 0x45, 0x11, 0x12, 0x44, 0x45, 0x11, 0x18, 0x44, + 0x21, 0x11, 0x44, 0x45, 0x1, 0x22, 0x5, 0xc, + 0x18, 0x3, 0xc0, + + /* U+5219 "则" */ + 0x0, 0x5, 0xfc, 0x14, 0x12, 0x52, 0x49, 0x49, + 0x25, 0x24, 0x94, 0x92, 0x52, 0x49, 0x49, 0x25, + 0x24, 0x94, 0x92, 0x47, 0x1, 0x16, 0x5, 0x84, + 0x1c, 0x3, 0xc0, + + /* U+521A "刚" */ + 0x0, 0x7, 0xfc, 0x18, 0x12, 0x61, 0x49, 0xa5, + 0x26, 0xa4, 0x99, 0x92, 0x62, 0x49, 0x99, 0x26, + 0x94, 0x9a, 0x52, 0x60, 0x41, 0x81, 0x6, 0x4, + 0x18, 0xf3, 0x80, + + /* U+521B "创" */ + 0x4, 0x2, 0x18, 0x4, 0x68, 0x88, 0x89, 0x16, + 0xa, 0x38, 0x4, 0x4f, 0xc8, 0x90, 0x91, 0x21, + 0x22, 0x46, 0x44, 0xb0, 0x89, 0x4, 0x12, 0x8, + 0x24, 0x10, 0x4f, 0xcf, 0x80, + + /* U+521D "初" */ + 0x20, 0x0, 0x8f, 0xf0, 0x8, 0x7e, 0x21, 0x8, + 0x84, 0x42, 0x13, 0x48, 0x49, 0x21, 0x78, 0x86, + 0xb2, 0x12, 0x10, 0x48, 0x41, 0x22, 0x4, 0x88, + 0x12, 0x47, 0x80, + + /* U+5220 "删" */ + 0x0, 0x2, 0xf7, 0xa5, 0x29, 0x4a, 0x52, 0x94, + 0xa5, 0x29, 0x4a, 0x7f, 0xfc, 0xa5, 0x29, 0x4a, + 0x52, 0x94, 0xa5, 0x29, 0x4a, 0x52, 0x14, 0xa4, + 0x37, 0x48, 0x61, 0x73, 0x80, + + /* U+5224 "判" */ + 0x8, 0x6, 0x24, 0x14, 0xa4, 0x52, 0x91, 0x8, + 0x47, 0xfd, 0x10, 0x84, 0x42, 0x11, 0x10, 0x47, + 0xfd, 0x11, 0x4, 0x44, 0x1, 0x20, 0x5, 0x80, + 0x1c, 0x3, 0xc0, + + /* U+5228 "刨" */ + 0x20, 0x4, 0x80, 0x13, 0xf2, 0x50, 0x49, 0x81, + 0x27, 0xf4, 0x94, 0x52, 0x51, 0x49, 0x7d, 0x25, + 0x4, 0x94, 0x12, 0x53, 0x89, 0x40, 0x85, 0x2, + 0x17, 0xf3, 0xc0, + + /* U+5229 "利" */ + 0x0, 0x4, 0xfc, 0x15, 0x4, 0x44, 0x11, 0x10, + 0x47, 0xfd, 0x11, 0x4, 0x4a, 0x11, 0x36, 0x45, + 0x45, 0x1d, 0x4, 0x64, 0x1, 0x10, 0x4, 0x40, + 0x11, 0x3, 0xc0, + + /* U+522B "别" */ + 0x0, 0x5, 0xfc, 0x14, 0x12, 0x50, 0x49, 0x41, + 0x25, 0xfc, 0x91, 0x2, 0x44, 0x9, 0xff, 0x24, + 0x44, 0x91, 0x12, 0x4c, 0x41, 0x21, 0x5, 0x4, + 0x1c, 0xe3, 0xc0, + + /* U+522D "刭" */ + 0x0, 0x4, 0xf8, 0x90, 0x42, 0x42, 0x9, 0x36, + 0x27, 0x6, 0x90, 0x2, 0x5f, 0xc9, 0x8, 0x24, + 0x20, 0x90, 0x80, 0x42, 0x21, 0xff, 0x4, 0x0, + 0xf0, + + /* U+522E "刮" */ + 0x0, 0x5, 0xfc, 0x10, 0x82, 0x42, 0x9, 0xff, + 0xa4, 0x20, 0x90, 0x82, 0x42, 0x9, 0x7f, 0x25, + 0x4, 0x94, 0x12, 0x50, 0x41, 0x41, 0x5, 0xfc, + 0x14, 0x13, 0xc0, + + /* U+5230 "到" */ + 0x0, 0x7, 0xfe, 0x11, 0x2, 0x48, 0x89, 0x62, + 0x27, 0xfc, 0x90, 0xa, 0x42, 0x9, 0x8, 0x25, + 0xfc, 0x90, 0x82, 0x42, 0x1, 0x8, 0x5, 0xfe, + 0x18, 0x3, 0xc0, + + /* U+5233 "刳" */ + 0x4, 0x2, 0x8, 0x5, 0xff, 0x48, 0x58, 0x91, + 0x9, 0x2c, 0xa, 0x43, 0xc4, 0x80, 0x9, 0x7f, + 0xd2, 0x40, 0x24, 0xfe, 0x48, 0x4, 0x10, 0x8, + 0x20, 0x30, 0x43, 0xc7, 0x80, + + /* U+5236 "制" */ + 0x8, 0x5, 0x20, 0x14, 0x82, 0x5f, 0xc9, 0x88, + 0x24, 0x20, 0x9f, 0xfa, 0x42, 0x9, 0x8, 0x25, + 0xfc, 0x94, 0x92, 0x52, 0x41, 0x49, 0x5, 0x2c, + 0x10, 0x83, 0xc0, + + /* U+5237 "刷" */ + 0x0, 0x1, 0xfe, 0x14, 0x9, 0x50, 0x25, 0x7f, + 0x95, 0x0, 0x54, 0x21, 0x57, 0xe5, 0x52, 0x95, + 0x4a, 0x55, 0x29, 0x54, 0xa1, 0x93, 0x86, 0x8, + 0x18, 0x21, 0xc0, + + /* U+5238 "券" */ + 0x11, 0x8, 0x32, 0x20, 0x24, 0x3, 0xff, 0xe0, + 0x10, 0x0, 0x40, 0x3f, 0xff, 0x82, 0x10, 0x18, + 0x10, 0xdf, 0xfa, 0x10, 0x48, 0x20, 0x80, 0xc1, + 0x3, 0x6, 0x38, 0x78, 0x0, + + /* U+5239 "刹" */ + 0x1, 0x5, 0x88, 0x11, 0x42, 0x47, 0x9, 0x63, + 0x26, 0x2, 0x90, 0x82, 0x42, 0x9, 0xff, 0xa4, + 0x20, 0x94, 0x92, 0x52, 0x41, 0x88, 0x84, 0x20, + 0x13, 0x83, 0xc0, + + /* U+523A "刺" */ + 0x4, 0x2, 0x8, 0x5, 0xff, 0x48, 0x20, 0x93, + 0xf9, 0x28, 0x92, 0x51, 0x24, 0xa2, 0x49, 0xf, + 0x92, 0x28, 0x24, 0x58, 0x49, 0x2c, 0x14, 0x48, + 0x20, 0x80, 0x41, 0x7, 0x80, + + /* U+523B "刻" */ + 0x8, 0x4, 0x20, 0x1f, 0xfa, 0x44, 0x9, 0x22, + 0x25, 0x8, 0x9c, 0xc2, 0x4e, 0x49, 0x11, 0x24, + 0x88, 0x94, 0x42, 0x62, 0x81, 0x9, 0x4, 0xc6, + 0x14, 0xb, 0xc0, + + /* U+523D "刽" */ + 0x4, 0x2, 0xc, 0x4, 0x24, 0x48, 0x8c, 0x92, + 0xd, 0x28, 0xa, 0x47, 0xc4, 0x80, 0x9, 0x7f, + 0xd2, 0x10, 0x24, 0x44, 0x48, 0x88, 0x12, 0x8, + 0x2f, 0xf8, 0x40, 0x17, 0x80, + + /* U+523F "刿" */ + 0x4, 0x2, 0x89, 0x5, 0x12, 0x4a, 0x24, 0x97, + 0xf9, 0x21, 0x2, 0x46, 0x4, 0x8f, 0xc9, 0x20, + 0x92, 0xa2, 0x24, 0x2c, 0x48, 0x30, 0x10, 0xc0, + 0x26, 0x0, 0x78, 0x7, 0x80, + + /* U+5240 "剀" */ + 0x91, 0x6, 0x44, 0x99, 0x12, 0x7f, 0xc9, 0x0, + 0x27, 0xfc, 0x90, 0x12, 0x40, 0x49, 0x7f, 0x26, + 0x0, 0x98, 0x0, 0x61, 0xc1, 0xf8, 0x6, 0x0, + 0xf0, + + /* U+5241 "剁" */ + 0x0, 0x2, 0x3c, 0x4, 0x88, 0x49, 0x10, 0x92, + 0x21, 0x24, 0x22, 0x52, 0x24, 0x84, 0x9, 0x7f, + 0x92, 0x30, 0x24, 0x78, 0x49, 0x48, 0x16, 0x88, + 0x29, 0x0, 0x42, 0x7, 0x80, + + /* U+5242 "剂" */ + 0x8, 0x4, 0x20, 0x1f, 0xfa, 0x40, 0x49, 0x42, + 0x24, 0xd0, 0x90, 0xc2, 0x4c, 0xc9, 0xc0, 0xa4, + 0x88, 0x92, 0x22, 0x48, 0x81, 0x22, 0x5, 0x8, + 0x18, 0x23, 0xc0, + + /* U+5243 "剃" */ + 0x21, 0x4, 0x48, 0x57, 0xf9, 0x42, 0x25, 0x8, + 0x95, 0xfe, 0x54, 0x81, 0x52, 0x5, 0x7f, 0x94, + 0x22, 0x51, 0x89, 0x4a, 0x21, 0x4b, 0x86, 0x20, + 0x10, 0x81, 0xc0, + + /* U+524A "削" */ + 0x10, 0xc, 0x88, 0x54, 0x92, 0xa0, 0x91, 0x4, + 0xf7, 0xa6, 0x5, 0x3f, 0xe9, 0x81, 0x4c, 0xa, + 0x7f, 0xd3, 0x2, 0x18, 0x10, 0xc0, 0x86, 0x3d, + 0xe0, + + /* U+524C "剌" */ + 0x8, 0x4, 0x20, 0x1f, 0xfa, 0x42, 0x9, 0x7f, + 0x25, 0x24, 0x94, 0x92, 0x52, 0x49, 0x7f, 0x24, + 0x60, 0x92, 0xe2, 0x4a, 0x41, 0xc8, 0x6, 0x20, + 0x10, 0x83, 0xc0, + + /* U+524D "前" */ + 0x8, 0x10, 0x8, 0x43, 0xff, 0xf8, 0x0, 0x0, + 0x0, 0xf, 0xe4, 0x90, 0x49, 0x3f, 0x92, 0x41, + 0x24, 0x82, 0x49, 0xfc, 0x92, 0x8, 0x24, 0x10, + 0x48, 0x20, 0x93, 0xc7, 0x0, + + /* U+5250 "剐" */ + 0x0, 0x5, 0xf8, 0x14, 0x22, 0x50, 0x89, 0x7e, + 0x24, 0x20, 0x90, 0x82, 0x7f, 0xc9, 0x89, 0x26, + 0x64, 0x9b, 0x52, 0x70, 0x41, 0x81, 0x6, 0x4, + 0x18, 0x73, 0xc0, + + /* U+5251 "剑" */ + 0x4, 0x4, 0x30, 0x11, 0x22, 0x48, 0x49, 0x40, + 0xa6, 0xfc, 0x90, 0x2, 0x42, 0x29, 0x49, 0x25, + 0x14, 0x92, 0x52, 0x40, 0x81, 0x3, 0x87, 0xf0, + 0x10, 0x3, 0xc0, + + /* U+5254 "剔" */ + 0x0, 0x5, 0xfc, 0x14, 0x12, 0x5f, 0xc9, 0x41, + 0x25, 0x4, 0x93, 0xe2, 0x48, 0x9, 0x7f, 0x26, + 0x54, 0x92, 0x52, 0x49, 0x41, 0x49, 0x6, 0x64, + 0x13, 0x73, 0xc0, + + /* U+5256 "剖" */ + 0x8, 0x4, 0x20, 0x1f, 0xfa, 0x40, 0x9, 0x22, + 0x24, 0x10, 0x9f, 0xfa, 0x40, 0x9, 0x0, 0x25, + 0xfc, 0x94, 0x12, 0x50, 0x41, 0x41, 0x5, 0xfc, + 0x14, 0x13, 0xc0, + + /* U+525C "剜" */ + 0x4, 0x2, 0x8, 0x5, 0xff, 0x4a, 0x2, 0x92, + 0x1, 0x27, 0x7a, 0x4a, 0x94, 0xa5, 0x29, 0x6a, + 0x52, 0x25, 0xa4, 0x48, 0x48, 0x92, 0x12, 0x28, + 0x2c, 0x60, 0x50, 0x7, 0x80, + + /* U+525E "剞" */ + 0x8, 0x4, 0x20, 0x17, 0xf2, 0x42, 0x9, 0x14, + 0x25, 0x8c, 0x90, 0x2, 0x7f, 0xe9, 0x1, 0x25, + 0xe4, 0x94, 0x92, 0x52, 0x49, 0x79, 0x5, 0x4, + 0x10, 0x10, 0x43, 0x8f, + + /* U+5261 "剡" */ + 0x4, 0x2, 0x8, 0x4, 0x92, 0x49, 0x28, 0x90, + 0xe1, 0x23, 0x22, 0x58, 0x34, 0x82, 0x9, 0x44, + 0xd2, 0x4a, 0x24, 0x10, 0x48, 0x50, 0x11, 0x90, + 0x26, 0x10, 0x70, 0x17, 0x80, + + /* U+5265 "剥" */ + 0x0, 0x7, 0xf8, 0x10, 0x22, 0x40, 0x89, 0x7e, + 0x24, 0x8, 0x9f, 0xfa, 0x62, 0x9, 0x49, 0x24, + 0x28, 0x93, 0xc2, 0x52, 0xc1, 0x88, 0x84, 0x20, + 0x17, 0x3, 0xc0, + + /* U+5267 "剧" */ + 0x0, 0x5, 0xfe, 0x14, 0xa, 0x50, 0x29, 0x7f, + 0xa5, 0x10, 0x94, 0x42, 0x5f, 0xe9, 0x44, 0x25, + 0x7e, 0x95, 0xa, 0x54, 0x21, 0x90, 0x86, 0x7e, + 0x19, 0xb, 0xc0, + + /* U+5269 "剩" */ + 0x0, 0x82, 0x7e, 0x24, 0x10, 0x4f, 0xfe, 0x90, + 0x41, 0x22, 0xaa, 0x5d, 0x64, 0x8a, 0xb9, 0xf5, + 0xd2, 0x18, 0x24, 0x58, 0x49, 0xa8, 0x16, 0x48, + 0x38, 0x80, 0x41, 0x7, 0x80, + + /* U+526A "剪" */ + 0x8, 0x20, 0x10, 0x83, 0xff, 0xfb, 0xf0, 0x24, + 0x24, 0x4f, 0xc8, 0x90, 0x91, 0x3f, 0x2, 0x42, + 0x8, 0x8, 0x63, 0xff, 0xf0, 0x20, 0x40, 0x80, + 0x83, 0x1, 0x38, 0x3e, 0x0, + + /* U+526F "副" */ + 0x0, 0x7, 0xfe, 0x10, 0x2, 0x5f, 0xc9, 0x41, + 0x25, 0x4, 0x93, 0xe2, 0x7f, 0xe9, 0x88, 0xa6, + 0x22, 0x9f, 0xfa, 0x62, 0x21, 0x88, 0x87, 0xfe, + 0x18, 0x9, 0xc0, + + /* U+5272 "割" */ + 0x8, 0x4, 0x20, 0x1f, 0x7a, 0x42, 0x9, 0x7f, + 0x24, 0x20, 0x93, 0xf2, 0x42, 0x9, 0x7f, 0x24, + 0x20, 0x97, 0xf2, 0x50, 0x41, 0x41, 0x5, 0xfc, + 0x14, 0x13, 0xc0, + + /* U+527D "剽" */ + 0x0, 0x7, 0xff, 0x11, 0x22, 0x5f, 0xe9, 0x92, + 0xa6, 0x4a, 0x97, 0xfa, 0x40, 0x9, 0x7f, 0xa4, + 0x0, 0x9f, 0xfe, 0x41, 0x41, 0x64, 0x86, 0x11, + 0x13, 0x83, 0xc0, + + /* U+527F "剿" */ + 0x24, 0x5, 0x24, 0x14, 0xb2, 0x49, 0x29, 0x7f, + 0x25, 0x24, 0x97, 0xf2, 0x52, 0x49, 0x7f, 0x24, + 0x20, 0x9f, 0xfa, 0x47, 0x1, 0x2a, 0x7, 0x26, + 0x10, 0x83, 0xc0, + + /* U+5281 "劁" */ + 0x10, 0x2, 0x24, 0x4, 0xff, 0x49, 0x10, 0x96, + 0x21, 0x27, 0xf2, 0x48, 0x84, 0x9f, 0xe9, 0x22, + 0x12, 0x44, 0x24, 0xf7, 0x48, 0x12, 0x14, 0xa4, + 0x29, 0x24, 0x52, 0x7, 0x80, + + /* U+5282 "劂" */ + 0x0, 0x2, 0xff, 0x25, 0x52, 0x4a, 0xa4, 0x97, + 0xfd, 0x29, 0x2a, 0x52, 0x94, 0xb5, 0x49, 0x6a, + 0x92, 0xfd, 0x25, 0x26, 0x4a, 0x4a, 0x19, 0x14, + 0x32, 0x40, 0x68, 0x87, 0x80, + + /* U+5288 "劈" */ + 0x0, 0x10, 0x7e, 0x20, 0x87, 0xf9, 0xa, 0x23, + 0xf4, 0x84, 0x1f, 0xcf, 0xc4, 0x38, 0x88, 0x51, + 0xfe, 0x3e, 0x20, 0x0, 0x3, 0xff, 0xe0, 0x20, + 0x40, 0xc1, 0x3, 0x2, 0x38, 0x78, + + /* U+5290 "劐" */ + 0x22, 0x7, 0xff, 0x90, 0xa2, 0x51, 0x9, 0x7f, + 0xa7, 0xfe, 0x94, 0x42, 0x5f, 0xe9, 0x44, 0x25, + 0xee, 0x97, 0xfa, 0x50, 0x41, 0x3e, 0x4, 0x7c, + 0x1e, 0x9, 0xc0, + + /* U+5293 "劓" */ + 0x10, 0x5, 0xbe, 0x97, 0xfa, 0x50, 0x29, 0x7f, + 0xa5, 0xfe, 0x90, 0x2, 0x7f, 0xe9, 0xff, 0xa6, + 0x22, 0x97, 0x72, 0x7f, 0xf1, 0x22, 0x5, 0x8, + 0x18, 0x21, 0xc0, + + /* U+529B "力" */ + 0x2, 0x0, 0x8, 0x0, 0x20, 0x0, 0x80, 0x7f, + 0xfc, 0x8, 0x10, 0x20, 0x40, 0x81, 0x2, 0x4, + 0x10, 0x10, 0x40, 0x41, 0x1, 0x8, 0x4, 0x40, + 0x13, 0x0, 0xd0, 0x3e, + + /* U+529D "劝" */ + 0x0, 0x43, 0xf1, 0x0, 0x44, 0x1, 0xff, 0x84, + 0x45, 0x21, 0x12, 0x84, 0x4a, 0x11, 0x10, 0x44, + 0x62, 0x12, 0x88, 0x49, 0x21, 0x41, 0x6, 0x8, + 0x30, 0x47, 0x80, + + /* U+529E "办" */ + 0x4, 0x0, 0x8, 0x0, 0x10, 0x3, 0xff, 0x80, + 0x41, 0x0, 0x82, 0x9, 0x5, 0x22, 0xa, 0x44, + 0x13, 0x10, 0x26, 0x20, 0x40, 0x80, 0x81, 0x1, + 0x4, 0x6, 0x30, 0xf8, 0x0, + + /* U+529F "功" */ + 0x0, 0x80, 0x2, 0xf, 0xc8, 0x4, 0xff, 0x10, + 0x84, 0x42, 0x11, 0x8, 0x44, 0x21, 0x10, 0x84, + 0x46, 0x11, 0xd0, 0x78, 0x41, 0x2, 0x4, 0x18, + 0x10, 0x87, 0x80, + + /* U+52A0 "加" */ + 0x20, 0x0, 0x80, 0x2, 0x7, 0xff, 0x91, 0x22, + 0x44, 0x89, 0x12, 0x24, 0x48, 0x91, 0x22, 0x44, + 0x89, 0x16, 0x24, 0x50, 0x91, 0x42, 0x7d, 0x9, + 0x19, 0xc4, 0x40, + + /* U+52A1 "务" */ + 0x0, 0x0, 0x18, 0x0, 0x3f, 0xe0, 0xc0, 0x46, + 0xc3, 0x18, 0x4c, 0x0, 0x60, 0x7, 0x38, 0xf2, + 0xe, 0x4, 0x1, 0xff, 0xe0, 0x10, 0x40, 0x40, + 0x81, 0x81, 0x6, 0x6, 0x70, 0x78, + + /* U+52A2 "劢" */ + 0x0, 0x43, 0xf9, 0x2, 0x4, 0x8, 0x7f, 0x20, + 0x44, 0xf1, 0x14, 0x44, 0x51, 0x11, 0x44, 0x45, + 0x11, 0x14, 0x4c, 0x51, 0x21, 0x44, 0x86, 0x14, + 0x19, 0x93, 0x80, + + /* U+52A3 "劣" */ + 0x1, 0x0, 0x5, 0x10, 0x19, 0xc, 0x21, 0x3, + 0x40, 0x30, 0x1, 0xc0, 0x1e, 0x0, 0x60, 0x0, + 0x1, 0x0, 0x7f, 0xfc, 0x1, 0x4, 0x2, 0x4, + 0x4, 0x4, 0x8, 0x4, 0x70, 0x78, + + /* U+52A8 "动" */ + 0x0, 0x41, 0xf1, 0x0, 0x4, 0x0, 0x7f, 0x0, + 0x44, 0x1, 0x1f, 0xe4, 0x48, 0x11, 0x20, 0x44, + 0xa1, 0x14, 0x48, 0x51, 0x21, 0xfe, 0x84, 0x4, + 0x10, 0x33, 0x80, + + /* U+52A9 "助" */ + 0x0, 0x41, 0xf1, 0x4, 0x44, 0x11, 0x7f, 0x7c, + 0x45, 0x11, 0x14, 0x44, 0x51, 0x11, 0x7c, 0x45, + 0x11, 0x14, 0x48, 0x53, 0xa1, 0xf1, 0x84, 0x4, + 0x10, 0x23, 0x80, + + /* U+52AA "努" */ + 0x30, 0x0, 0x43, 0xfb, 0xfc, 0x22, 0x14, 0x44, + 0x45, 0x7, 0x4, 0x5, 0xb6, 0x70, 0x2, 0x2, + 0x0, 0xff, 0xf0, 0x8, 0x20, 0x20, 0x40, 0xc0, + 0x83, 0x3, 0x38, 0x7c, 0x0, + + /* U+52AB "劫" */ + 0x10, 0x40, 0x41, 0x1, 0x4, 0x1f, 0xff, 0x10, + 0x44, 0x41, 0x11, 0x4, 0x7f, 0x91, 0x20, 0x44, + 0x81, 0x14, 0x48, 0x51, 0x21, 0x9e, 0x85, 0x84, + 0x10, 0x37, 0x80, + + /* U+52AC "劬" */ + 0x20, 0x20, 0x40, 0x40, 0xf8, 0x82, 0x11, 0x8, + 0x27, 0xff, 0x44, 0x52, 0x88, 0xa5, 0x11, 0x4a, + 0x22, 0x94, 0x45, 0xe9, 0xa, 0x12, 0x10, 0x24, + 0x20, 0x50, 0x47, 0x67, 0x0, + + /* U+52AD "劭" */ + 0x0, 0x20, 0xfc, 0x40, 0x48, 0x80, 0x93, 0xf2, + 0x22, 0x24, 0x44, 0x53, 0x88, 0x80, 0x11, 0x7e, + 0x22, 0x84, 0x45, 0x9, 0x8a, 0x12, 0x14, 0x24, + 0x2f, 0xd8, 0x50, 0x67, 0x0, + + /* U+52B1 "励" */ + 0x0, 0x21, 0xfe, 0x84, 0x2, 0x10, 0x1f, 0x5f, + 0x25, 0x20, 0x94, 0x82, 0x53, 0xc9, 0x49, 0x25, + 0x24, 0x94, 0x92, 0x52, 0x51, 0x51, 0x46, 0x45, + 0x1a, 0x6b, 0x80, + + /* U+52B2 "劲" */ + 0x0, 0x20, 0xfc, 0x40, 0x10, 0x80, 0x27, 0xf0, + 0xc2, 0x26, 0x44, 0x70, 0x48, 0x80, 0x11, 0x7e, + 0x22, 0x20, 0x44, 0x41, 0x88, 0x82, 0x10, 0xf4, + 0x2e, 0x10, 0x40, 0x67, 0x0, + + /* U+52B3 "劳" */ + 0x8, 0x40, 0x10, 0x81, 0xff, 0xf0, 0x42, 0x0, + 0x0, 0x1f, 0xff, 0xe0, 0x80, 0xc1, 0x1, 0x7f, + 0xf8, 0x8, 0x10, 0x10, 0x20, 0x20, 0x40, 0x80, + 0x86, 0x1, 0x30, 0x7c, 0x0, + + /* U+52BE "劾" */ + 0x0, 0x0, 0x20, 0x0, 0x84, 0x3d, 0xd0, 0x21, + 0xfc, 0x91, 0x14, 0x44, 0x7e, 0x9, 0x9, 0x24, + 0x48, 0x92, 0x22, 0x51, 0x9, 0x8c, 0x44, 0x49, + 0x12, 0x8, 0x70, 0x6e, + + /* U+52BF "势" */ + 0x20, 0x80, 0x41, 0x3, 0xef, 0xc1, 0x4, 0x82, + 0xa9, 0x1e, 0x32, 0x48, 0x94, 0xf6, 0x6, 0x4, + 0x1, 0xff, 0xf0, 0x10, 0x20, 0x20, 0x40, 0x81, + 0x6, 0x2, 0x38, 0x7c, 0x0, + + /* U+52C3 "勃" */ + 0x8, 0x20, 0x20, 0x87, 0xe2, 0x2, 0x3f, 0xff, + 0x26, 0x4, 0x98, 0x12, 0x4f, 0x89, 0x4, 0x24, + 0x20, 0x91, 0xf4, 0x7a, 0x11, 0x8, 0x84, 0x22, + 0x13, 0x93, 0xc0, + + /* U+52C7 "勇" */ + 0x7f, 0xf8, 0x81, 0x83, 0xf0, 0xff, 0xf4, 0x10, + 0xbf, 0xfd, 0x4, 0x28, 0x21, 0x7f, 0xfa, 0x20, + 0x5f, 0xfe, 0x8, 0x10, 0xc0, 0x8c, 0x7, 0x83, + 0xc0, + + /* U+52C9 "勉" */ + 0x10, 0x20, 0x7c, 0x41, 0x8b, 0xf6, 0x21, 0x27, + 0xf2, 0x49, 0x24, 0x92, 0x49, 0x24, 0x92, 0x7f, + 0x44, 0x18, 0x88, 0x51, 0x10, 0xa4, 0xe2, 0x40, + 0x2c, 0x80, 0x71, 0xff, 0x0, + + /* U+52CB "勋" */ + 0x3e, 0x10, 0x44, 0x20, 0x88, 0x41, 0x13, 0xf3, + 0xe7, 0xe0, 0x2, 0x5f, 0xc4, 0xa0, 0x89, 0x49, + 0x12, 0x92, 0x25, 0x24, 0x8a, 0x49, 0x11, 0x46, + 0x26, 0x68, 0x70, 0x67, 0x0, + + /* U+52D0 "勐" */ + 0x0, 0x21, 0xf8, 0x80, 0x62, 0x2, 0x3f, 0xff, + 0x24, 0x20, 0x93, 0x82, 0x40, 0x9, 0xff, 0x26, + 0xa4, 0x9a, 0x96, 0x6a, 0x51, 0xbf, 0x47, 0x1, + 0x10, 0xb, 0x80, + + /* U+52D2 "勒" */ + 0x24, 0x23, 0xfc, 0x82, 0x42, 0x9, 0x3f, 0x3c, + 0x24, 0x40, 0x97, 0xe2, 0x54, 0x89, 0x52, 0x25, + 0xf8, 0x91, 0x4, 0x7f, 0xd1, 0x10, 0x44, 0x42, + 0x11, 0x13, 0x80, + + /* U+52D6 "勖" */ + 0x0, 0x23, 0xf8, 0x88, 0x22, 0x2f, 0x88, 0x82, + 0xfe, 0xf8, 0x90, 0x2, 0x5f, 0x89, 0x82, 0x25, + 0xf8, 0x98, 0x22, 0x5f, 0x91, 0x82, 0x45, 0xfa, + 0x18, 0x1b, 0x80, + + /* U+52D8 "勘" */ + 0x2, 0x21, 0x8, 0x84, 0x22, 0x3f, 0xc8, 0x42, + 0x20, 0xf9, 0xf4, 0x22, 0x4f, 0x89, 0x42, 0x27, + 0xfe, 0x94, 0x2, 0x54, 0x91, 0x52, 0x45, 0x1, + 0x17, 0xe8, 0x40, 0x2e, + + /* U+52DF "募" */ + 0x8, 0x43, 0xff, 0xf0, 0x84, 0x1d, 0xec, 0x3f, + 0xf1, 0x0, 0x43, 0xff, 0x1, 0x0, 0xff, 0xfc, + 0x61, 0x2, 0x43, 0x3f, 0xfb, 0x4, 0x20, 0x60, + 0x87, 0x1c, 0x0, + + /* U+52E4 "勤" */ + 0x42, 0x23, 0xfe, 0x84, 0x22, 0xf, 0x88, 0x8, + 0x7d, 0xfc, 0x98, 0x92, 0x5f, 0xc9, 0x8, 0x27, + 0xfc, 0x90, 0x82, 0x5f, 0xd1, 0x8, 0x47, 0xff, + 0x10, 0xb, 0x80, + + /* U+52F0 "勰" */ + 0x0, 0x0, 0x8, 0x3f, 0x9f, 0xd2, 0x42, 0x29, + 0x21, 0x14, 0x90, 0x8b, 0xf9, 0xb9, 0x24, 0x80, + 0x92, 0x22, 0x49, 0x3f, 0x9b, 0xc, 0xc2, 0x6, + 0x6d, 0x23, 0x36, 0x11, 0xad, 0x5, 0x52, 0x91, + 0x76, 0x30, + + /* U+52F9 "勹" */ + 0x8, 0x0, 0x40, 0x1, 0xff, 0xc8, 0x1, 0x20, + 0x5, 0x0, 0x10, 0x0, 0x40, 0x1, 0x0, 0x4, + 0x0, 0x10, 0x0, 0x40, 0x1, 0x0, 0x4, 0x0, + 0x30, 0x1f, 0x80, + + /* U+52FA "勺" */ + 0x8, 0x0, 0x40, 0x1, 0xff, 0xc8, 0x1, 0x20, + 0x5, 0x0, 0x10, 0x80, 0x41, 0x1, 0x2, 0x4, + 0x4, 0x10, 0x0, 0x40, 0x1, 0x0, 0x4, 0x0, + 0x30, 0x1f, 0x80, + + /* U+52FE "勾" */ + 0x8, 0x0, 0x40, 0x1, 0xff, 0xc8, 0x1, 0x62, + 0x5, 0x10, 0x10, 0x40, 0x42, 0x41, 0x8, 0x84, + 0x41, 0x13, 0xfc, 0x48, 0x9, 0x0, 0x4, 0x0, + 0x30, 0x1f, 0x80, + + /* U+52FF "勿" */ + 0x8, 0x0, 0x40, 0x1, 0xff, 0xc8, 0x89, 0x22, + 0x25, 0x11, 0x18, 0x44, 0x41, 0x11, 0x8, 0x84, + 0x42, 0x12, 0x18, 0x50, 0x41, 0x2, 0x4, 0x10, + 0x31, 0x8f, 0x80, + + /* U+5300 "匀" */ + 0x8, 0x0, 0x40, 0x1, 0xff, 0xc8, 0x1, 0x20, + 0x5, 0x30, 0x10, 0x20, 0x40, 0x41, 0x0, 0x14, + 0x3, 0x90, 0x70, 0x5e, 0x1, 0x40, 0x4, 0x0, + 0x30, 0x1f, 0x80, + + /* U+5305 "包" */ + 0x8, 0x0, 0x20, 0x0, 0x7f, 0xe1, 0x0, 0x46, + 0x0, 0x9f, 0xf1, 0x28, 0x22, 0x10, 0x44, 0x3f, + 0x88, 0x40, 0x10, 0x80, 0x29, 0x7, 0x92, 0x0, + 0x24, 0x0, 0x47, 0xff, 0x0, + + /* U+5306 "匆" */ + 0x8, 0x0, 0x40, 0x1, 0xff, 0xc8, 0x89, 0x22, + 0x25, 0x11, 0x19, 0x44, 0x43, 0x11, 0xb, 0x84, + 0x42, 0x12, 0x1e, 0x50, 0x41, 0x2, 0x4, 0x10, + 0x31, 0x8f, 0x80, + + /* U+5308 "匈" */ + 0x8, 0x0, 0x20, 0x0, 0x7f, 0xf9, 0x0, 0x16, + 0x88, 0x28, 0xa0, 0x48, 0x88, 0x91, 0x91, 0x24, + 0xa2, 0x51, 0x44, 0x80, 0x89, 0xff, 0x10, 0x0, + 0x20, 0x0, 0xc0, 0x3f, 0x0, + + /* U+530D "匍" */ + 0x10, 0x0, 0xff, 0xf6, 0x0, 0x71, 0x21, 0x7f, + 0xf4, 0x10, 0x10, 0x40, 0x5f, 0xf9, 0x44, 0x25, + 0xff, 0x94, 0x42, 0x5f, 0xf9, 0x44, 0x25, 0x13, + 0x30, 0x3, 0x80, + + /* U+530F "匏" */ + 0x10, 0x40, 0x20, 0x81, 0xfb, 0xf8, 0xc4, 0x12, + 0x7f, 0x2f, 0x92, 0x40, 0x24, 0x80, 0x49, 0x7e, + 0xf2, 0x41, 0x4, 0xf2, 0xf0, 0x24, 0x0, 0x48, + 0x20, 0x90, 0x4f, 0x1f, 0x80, + + /* U+5310 "匐" */ + 0x0, 0x0, 0x10, 0x0, 0x7f, 0xf8, 0x80, 0x13, + 0xff, 0x2c, 0x0, 0x57, 0xf8, 0x88, 0x11, 0xf, + 0xc2, 0x7f, 0xe4, 0x88, 0x49, 0xff, 0x92, 0x21, + 0x27, 0xfe, 0x48, 0x1, 0x80, 0x3e, + + /* U+5315 "匕" */ + 0x80, 0x8, 0x0, 0x80, 0x28, 0xc, 0x83, 0x8, + 0xc0, 0xf0, 0x8, 0x0, 0x80, 0x8, 0x1, 0x80, + 0x18, 0x1, 0xc0, 0x27, 0xfc, + + /* U+5316 "化" */ + 0x4, 0x40, 0x8, 0x40, 0x8, 0x42, 0x10, 0x44, + 0x30, 0x48, 0x30, 0x50, 0x50, 0x60, 0xd0, 0xc0, + 0x13, 0x40, 0x14, 0x41, 0x10, 0x41, 0x10, 0x41, + 0x10, 0x41, 0x10, 0x3e, + + /* U+5317 "北" */ + 0x4, 0x40, 0x8, 0x80, 0x11, 0x1f, 0xe2, 0x40, + 0x46, 0x0, 0x88, 0x1, 0x10, 0x2, 0x20, 0x4, + 0x40, 0x8, 0x84, 0x71, 0xb, 0x22, 0x10, 0x44, + 0x20, 0x87, 0x80, + + /* U+5319 "匙" */ + 0x7f, 0x20, 0x41, 0x20, 0x41, 0x20, 0x7f, 0x22, + 0x41, 0x2c, 0x7f, 0x30, 0x0, 0x20, 0xff, 0xa0, + 0x8, 0x22, 0x48, 0x22, 0x47, 0xa2, 0x68, 0x3c, + 0x78, 0x0, 0x8f, 0xfe, + + /* U+531A "匚" */ + 0xff, 0xfc, 0x0, 0x20, 0x1, 0x0, 0x8, 0x0, + 0x40, 0x2, 0x0, 0x10, 0x0, 0x80, 0x4, 0x0, + 0x20, 0x1, 0x0, 0xf, 0xff, 0x80, + + /* U+531D "匝" */ + 0xff, 0xfe, 0x4, 0x8, 0x10, 0x27, 0xfc, 0x91, + 0x12, 0x44, 0x49, 0x11, 0x24, 0x44, 0x91, 0x12, + 0x45, 0xc8, 0x10, 0x20, 0x0, 0xff, 0xfc, + + /* U+5320 "匠" */ + 0xff, 0xfe, 0x0, 0x8, 0xff, 0x22, 0x0, 0x88, + 0x2, 0x20, 0x8, 0xff, 0xa2, 0x10, 0x88, 0x42, + 0x61, 0x9, 0x4, 0x28, 0x10, 0xff, 0xfc, + + /* U+5321 "匡" */ + 0xff, 0xfe, 0x0, 0x8, 0x0, 0x27, 0xfe, 0x81, + 0x2, 0x4, 0x8, 0xff, 0x20, 0x40, 0x81, 0x2, + 0x7f, 0xe8, 0x0, 0x20, 0x0, 0xff, 0xfc, + + /* U+5323 "匣" */ + 0xff, 0xfe, 0x0, 0x9, 0xff, 0x24, 0x44, 0x91, + 0x12, 0x7f, 0xc9, 0x11, 0x24, 0x44, 0x9f, 0xf2, + 0x4, 0x8, 0x10, 0x20, 0x40, 0xff, 0xfc, + + /* U+5326 "匦" */ + 0xff, 0xfe, 0x42, 0xb, 0xe8, 0x28, 0x7c, 0xa8, + 0x92, 0xa2, 0x4b, 0xe9, 0x22, 0x25, 0x8e, 0x97, + 0xe4, 0x58, 0xb1, 0xe0, 0x0, 0xff, 0xfc, + + /* U+532A "匪" */ + 0xff, 0xfe, 0x11, 0x8, 0x44, 0x3f, 0x1f, 0x84, + 0x42, 0x11, 0xb, 0xc7, 0xe1, 0x10, 0x84, 0x43, + 0xf1, 0xf8, 0x44, 0x21, 0x10, 0xff, 0xfc, + + /* U+532E "匮" */ + 0xff, 0xfe, 0x4, 0x9, 0xff, 0x27, 0xfc, 0x81, + 0x2, 0xff, 0xe8, 0x0, 0x27, 0xfc, 0x90, 0x12, + 0x44, 0x48, 0x6e, 0x2e, 0x6, 0xff, 0xfc, + + /* U+5339 "匹" */ + 0xff, 0xfe, 0x12, 0x8, 0x48, 0x21, 0x20, 0x84, + 0x82, 0x12, 0x8, 0x88, 0xa2, 0x22, 0x98, 0x8a, + 0xc1, 0xe8, 0x0, 0x20, 0x0, 0xff, 0xfc, + + /* U+533A "区" */ + 0xff, 0xfc, 0x0, 0x24, 0x9, 0x30, 0xc8, 0x44, + 0x41, 0x42, 0x4, 0x10, 0x50, 0x84, 0x44, 0xc1, + 0x2c, 0x5, 0x0, 0xf, 0xff, 0x80, + + /* U+533B "医" */ + 0xff, 0xfe, 0x0, 0x8, 0x80, 0x23, 0xfe, 0x91, + 0x2, 0x4, 0xb, 0xff, 0xa0, 0x40, 0x83, 0x82, + 0x11, 0x89, 0x81, 0xa0, 0x0, 0xff, 0xfc, + + /* U+533E "匾" */ + 0xff, 0xfe, 0x0, 0x9, 0xff, 0xa4, 0x2, 0x9f, + 0xfa, 0x40, 0x9, 0x7f, 0xa6, 0x52, 0x97, 0xfa, + 0xa5, 0x2a, 0x94, 0xa2, 0x5e, 0xff, 0xfc, + + /* U+533F "匿" */ + 0xff, 0xfe, 0x0, 0x8, 0x42, 0x2f, 0xff, 0x84, + 0x22, 0x4, 0xb, 0xff, 0xe1, 0x0, 0x8f, 0xf2, + 0xe0, 0x48, 0x81, 0x21, 0xfc, 0x80, 0x3, 0xff, + 0xf0, + + /* U+5341 "十" */ + 0x1, 0x0, 0x2, 0x0, 0x4, 0x0, 0x8, 0x0, + 0x10, 0x0, 0x20, 0x3f, 0xff, 0x80, 0x80, 0x1, + 0x0, 0x2, 0x0, 0x4, 0x0, 0x8, 0x0, 0x10, + 0x0, 0x20, 0x0, 0x40, 0x0, + + /* U+5343 "千" */ + 0x0, 0xc, 0x7f, 0xf8, 0x4, 0x0, 0x8, 0x0, + 0x10, 0x0, 0x20, 0x0, 0x40, 0x7f, 0xff, 0x1, + 0x0, 0x2, 0x0, 0x4, 0x0, 0x8, 0x0, 0x10, + 0x0, 0x20, 0x0, 0x40, 0x0, + + /* U+5345 "卅" */ + 0x11, 0x8, 0x22, 0x10, 0x44, 0x20, 0x88, 0x41, + 0x10, 0x9f, 0xff, 0xc4, 0x42, 0x8, 0x84, 0x21, + 0x8, 0x42, 0x10, 0x84, 0x23, 0x8, 0x44, 0x10, + 0x90, 0x1, 0x0, + + /* U+5347 "升" */ + 0x0, 0x20, 0x3, 0x41, 0xf8, 0x80, 0x41, 0x0, + 0x82, 0x1, 0x4, 0x2, 0x8, 0x7f, 0xff, 0x8, + 0x20, 0x10, 0x40, 0x20, 0x80, 0x41, 0x1, 0x2, + 0x6, 0x4, 0x10, 0x8, 0x0, + + /* U+5348 "午" */ + 0x8, 0x0, 0x20, 0x0, 0x7f, 0xf1, 0x8, 0x2, + 0x10, 0x8, 0x20, 0x0, 0x40, 0x0, 0x80, 0xff, + 0xfe, 0x2, 0x0, 0x4, 0x0, 0x8, 0x0, 0x10, + 0x0, 0x20, 0x0, 0x40, 0x0, + + /* U+5349 "卉" */ + 0x1, 0x0, 0x2, 0x0, 0x4, 0x3, 0xff, 0xe0, + 0x10, 0x0, 0x20, 0x4, 0x44, 0x8, 0x8, 0xff, + 0xfe, 0x20, 0x20, 0x40, 0x40, 0x80, 0x81, 0x1, + 0x4, 0x2, 0x30, 0x4, 0x0, + + /* U+534A "半" */ + 0x1, 0x0, 0x2, 0x0, 0x84, 0x20, 0x88, 0x80, + 0x10, 0x0, 0x20, 0x1f, 0xff, 0x0, 0x80, 0x1, + 0x1, 0xff, 0xfc, 0x4, 0x0, 0x8, 0x0, 0x10, + 0x0, 0x20, 0x0, 0x40, 0x0, + + /* U+534E "华" */ + 0x8, 0x80, 0x31, 0x8, 0xc2, 0x63, 0x87, 0xd, + 0x18, 0x22, 0xd0, 0x44, 0x20, 0x88, 0x3e, 0x10, + 0x0, 0x2, 0x3, 0xff, 0xf8, 0x8, 0x0, 0x10, + 0x0, 0x20, 0x0, 0x40, 0x0, + + /* U+534F "协" */ + 0x41, 0x0, 0x82, 0x1, 0x4, 0x2, 0x3f, 0x8f, + 0x11, 0x8, 0x22, 0x12, 0x45, 0x24, 0x8a, 0x49, + 0x14, 0xa6, 0x25, 0x8, 0x42, 0x10, 0x84, 0x41, + 0x8, 0x82, 0x12, 0x38, 0x0, + + /* U+5351 "卑" */ + 0x2, 0x0, 0x4, 0x0, 0xff, 0xe1, 0x8, 0x42, + 0x10, 0x87, 0xff, 0x8, 0x42, 0x10, 0x84, 0x3f, + 0xf8, 0xc, 0x0, 0x32, 0x1, 0x84, 0xf, 0xff, + 0xe0, 0x10, 0x0, 0x20, 0x0, 0x40, + + /* U+5352 "卒" */ + 0x1, 0x0, 0x2, 0x1, 0xff, 0xf0, 0x0, 0x0, + 0x82, 0x2, 0x4, 0x7, 0x14, 0x13, 0x64, 0x40, + 0x4, 0x2, 0x3, 0xff, 0xf8, 0x8, 0x0, 0x10, + 0x0, 0x20, 0x0, 0x40, 0x0, + + /* U+5353 "卓" */ + 0x1, 0x0, 0x2, 0x0, 0x7, 0xf8, 0x8, 0x3, + 0xff, 0x84, 0x1, 0x8, 0x2, 0x1f, 0xfc, 0x20, + 0x8, 0x40, 0x10, 0xff, 0xe0, 0x8, 0xf, 0xff, + 0xe0, 0x20, 0x0, 0x40, 0x0, 0x80, + + /* U+5355 "单" */ + 0x8, 0x20, 0x8, 0xc0, 0xff, 0xe2, 0x8, 0x44, + 0x10, 0x87, 0xff, 0x10, 0x42, 0x20, 0x84, 0x3f, + 0xf8, 0x2, 0x0, 0x4, 0x7, 0xff, 0xf0, 0x10, + 0x0, 0x20, 0x0, 0x40, 0x0, + + /* U+5356 "卖" */ + 0x1, 0x0, 0xff, 0xf8, 0x4, 0x0, 0x8, 0xf, + 0xff, 0xe0, 0x0, 0x86, 0x11, 0x2, 0x40, 0x38, + 0x80, 0x1, 0x3, 0xff, 0xf8, 0x8, 0x0, 0x2e, + 0x3, 0x3, 0xb8, 0x0, 0x80, + + /* U+5357 "南" */ + 0x1, 0x0, 0x2, 0x0, 0x4, 0x7, 0xff, 0xf0, + 0x10, 0xf, 0xff, 0x90, 0x1, 0x22, 0x12, 0x42, + 0x44, 0x9f, 0xe9, 0x4, 0x12, 0x8, 0x25, 0xff, + 0x48, 0x20, 0x90, 0x41, 0x20, 0x9e, + + /* U+535A "博" */ + 0x40, 0x58, 0x40, 0x40, 0x4f, 0xfe, 0x40, 0x40, + 0x47, 0xfc, 0xf4, 0x44, 0x47, 0xfc, 0x44, 0x44, + 0x47, 0xfc, 0x44, 0x4c, 0x4f, 0xff, 0x42, 0x8, + 0x43, 0x8, 0x40, 0x8, 0x40, 0x78, + + /* U+535C "卜" */ + 0x80, 0x80, 0x80, 0x80, 0x80, 0xe0, 0xb0, 0x8c, + 0x83, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + + /* U+535E "卞" */ + 0x2, 0x0, 0x2, 0x0, 0x4, 0x7, 0xff, 0xf0, + 0x20, 0x0, 0x40, 0x0, 0x80, 0x1, 0x60, 0x2, + 0x60, 0x4, 0x30, 0x8, 0x0, 0x10, 0x0, 0x20, + 0x0, 0x40, 0x0, 0x80, 0x0, + + /* U+535F "卟" */ + 0x0, 0x83, 0xe2, 0x8, 0x88, 0x22, 0x20, 0x88, + 0x82, 0x22, 0x88, 0x89, 0x22, 0x23, 0x88, 0x86, + 0x22, 0xf, 0x88, 0x22, 0x20, 0x88, 0x80, 0x2, + 0x0, 0x8, 0x0, + + /* U+5360 "占" */ + 0x4, 0x0, 0x20, 0x1, 0x0, 0xf, 0xf0, 0x40, + 0x2, 0x0, 0x10, 0x1f, 0xfc, 0x80, 0x24, 0x1, + 0x20, 0x9, 0x0, 0x48, 0x2, 0x7f, 0xf2, 0x0, + 0x80, + + /* U+5361 "卡" */ + 0x2, 0x0, 0x4, 0x0, 0xf, 0xe0, 0x10, 0x0, + 0x20, 0x0, 0x40, 0x3f, 0xff, 0x80, 0x0, 0x2, + 0x0, 0x6, 0x0, 0xb, 0x0, 0x11, 0x80, 0x20, + 0x80, 0x40, 0x0, 0x80, 0x0, + + /* U+5362 "卢" */ + 0x1, 0x0, 0x2, 0x0, 0x7, 0xf8, 0x8, 0x0, + 0x10, 0x7, 0xff, 0x88, 0x1, 0x10, 0x2, 0x20, + 0x4, 0x40, 0x8, 0xff, 0xf1, 0x0, 0x6, 0x0, + 0x8, 0x0, 0x30, 0x0, 0x0, 0x0, + + /* U+5363 "卣" */ + 0x4, 0x0, 0x3f, 0xc1, 0x0, 0x8, 0xf, 0xff, + 0x40, 0xa, 0x0, 0x5f, 0xf2, 0x80, 0x94, 0x4, + 0xbf, 0xe5, 0x0, 0x28, 0x1, 0x7f, 0xfa, 0x0, + 0x40, + + /* U+5364 "卤" */ + 0x4, 0x0, 0x20, 0x1, 0x0, 0xf, 0xf0, 0x40, + 0x7f, 0xfa, 0x0, 0x56, 0x12, 0x89, 0x14, 0x30, + 0xa1, 0xc5, 0x33, 0x2b, 0x5, 0x40, 0xb, 0xff, + 0xd0, 0x2, + + /* U+5366 "卦" */ + 0x10, 0x20, 0x20, 0x43, 0xfc, 0x80, 0x81, 0x1, + 0x2, 0x2, 0x6, 0x3f, 0xcb, 0x0, 0x11, 0x10, + 0x21, 0xfe, 0x40, 0x40, 0x80, 0x81, 0x1, 0x2, + 0x1f, 0xe4, 0x0, 0x8, 0x0, + + /* U+5367 "卧" */ + 0xff, 0x42, 0x21, 0x8, 0x84, 0x22, 0x10, 0xfe, + 0x62, 0x9, 0x48, 0x24, 0xa0, 0x91, 0xfe, 0x42, + 0x21, 0x8, 0x84, 0x22, 0x10, 0xff, 0x40, 0x1, + 0x0, + + /* U+5369 "卩" */ + 0xff, 0x6, 0xc, 0x18, 0x30, 0x60, 0xc1, 0x83, + 0x7a, 0x4, 0x8, 0x10, 0x0, + + /* U+536B "卫" */ + 0x7f, 0xfc, 0x4, 0x8, 0x8, 0x10, 0x10, 0x20, + 0x20, 0x40, 0x41, 0x0, 0x82, 0x1, 0x3c, 0x2, + 0x0, 0x4, 0x0, 0x8, 0x0, 0x10, 0xf, 0xff, + 0xe0, + + /* U+536E "卮" */ + 0x0, 0x4, 0x7f, 0xf8, 0x80, 0x1, 0x0, 0x3, + 0xff, 0xe4, 0x0, 0x8, 0x0, 0x13, 0xfc, 0x24, + 0x8, 0x48, 0x10, 0x93, 0xc1, 0x20, 0x14, 0x40, + 0x28, 0x80, 0x50, 0xff, 0x0, + + /* U+536F "卯" */ + 0x4, 0x1, 0xe1, 0xf4, 0x4, 0x51, 0x11, 0x44, + 0x45, 0x11, 0x14, 0x44, 0x51, 0x11, 0x4c, 0x45, + 0xd1, 0x14, 0x84, 0x42, 0x1f, 0x10, 0x40, 0xc1, + 0x4, 0x4, 0x0, + + /* U+5370 "印" */ + 0x1c, 0x7, 0xf, 0xe0, 0x43, 0x2, 0x18, 0x10, + 0xc0, 0x87, 0xf4, 0x30, 0x21, 0x81, 0xc, 0x8, + 0x61, 0x43, 0x72, 0xfc, 0x10, 0x0, 0x80, 0x4, + 0x0, + + /* U+5371 "危" */ + 0xc, 0x0, 0x1f, 0xe0, 0x40, 0x81, 0x2, 0xf, + 0xff, 0xe4, 0x0, 0x8, 0x0, 0x13, 0xf8, 0x24, + 0x10, 0x48, 0x20, 0x93, 0xcb, 0x20, 0x14, 0x40, + 0x70, 0xff, 0xa0, 0x0, 0x0, + + /* U+5373 "即" */ + 0xfc, 0xfc, 0x24, 0x61, 0x23, 0xf9, 0x18, 0x48, + 0xc2, 0x47, 0xf2, 0x30, 0x11, 0x88, 0x8c, 0x44, + 0x65, 0x2f, 0xc5, 0x8, 0x8, 0x0, 0x40, + + /* U+5374 "却" */ + 0x8, 0x0, 0x21, 0xf0, 0x84, 0x7f, 0xd1, 0x8, + 0x44, 0x21, 0x10, 0x84, 0x7f, 0xd1, 0x10, 0x44, + 0x91, 0x16, 0x44, 0x50, 0x97, 0xfe, 0x40, 0x5, + 0x0, 0x4, 0x0, + + /* U+5375 "卵" */ + 0x6, 0x1, 0xe1, 0xf4, 0x4, 0x50, 0x91, 0x52, + 0x65, 0x49, 0x94, 0xa5, 0x50, 0x95, 0x46, 0x45, + 0x69, 0x16, 0x44, 0x41, 0x1e, 0x8, 0x40, 0xc1, + 0x4, 0x4, 0x0, + + /* U+5377 "卷" */ + 0x21, 0x8, 0x22, 0x20, 0x24, 0x3, 0xff, 0xe0, + 0x20, 0x0, 0x40, 0x3f, 0xff, 0x84, 0x10, 0x1f, + 0xf8, 0xe0, 0x4e, 0x40, 0x80, 0x87, 0x21, 0x0, + 0x43, 0x1, 0x3, 0xfe, 0x0, + + /* U+5378 "卸" */ + 0x20, 0x0, 0x81, 0xf7, 0xf4, 0x72, 0x11, 0x88, + 0x47, 0xfd, 0x10, 0x84, 0x52, 0x11, 0x4f, 0x45, + 0x21, 0x14, 0x84, 0x52, 0x57, 0x7e, 0x42, 0x1, + 0x0, 0x4, 0x0, + + /* U+537A "卺" */ + 0x3f, 0xf0, 0x0, 0x60, 0x3, 0x7, 0xca, 0x60, + 0x93, 0x6, 0x23, 0x31, 0x81, 0xbf, 0xfc, 0x0, + 0x0, 0x7f, 0xe0, 0x80, 0x41, 0x0, 0x82, 0x1e, + 0x44, 0x0, 0x8f, 0xff, 0x0, + + /* U+537F "卿" */ + 0x10, 0x0, 0xc7, 0xbd, 0x9, 0x4a, 0x92, 0x95, + 0x3d, 0x2a, 0x4a, 0x54, 0x94, 0xa9, 0x29, 0x53, + 0xd2, 0xe4, 0x24, 0x4a, 0x49, 0x16, 0xf2, 0x35, + 0x8, 0x42, 0x10, 0x4, 0x0, + + /* U+5382 "厂" */ + 0x3f, 0xfe, 0x40, 0x0, 0x80, 0x1, 0x0, 0x2, + 0x0, 0x4, 0x0, 0x8, 0x0, 0x10, 0x0, 0x20, + 0x0, 0x40, 0x1, 0x0, 0x2, 0x0, 0x8, 0x0, + 0x10, 0x0, 0x0, + + /* U+5384 "厄" */ + 0x3f, 0xfe, 0x40, 0x0, 0x80, 0x1, 0x3f, 0xc2, + 0x40, 0x84, 0x81, 0x9, 0x2, 0x12, 0x4, 0x24, + 0x70, 0x48, 0x5, 0x10, 0xa, 0x20, 0x1c, 0x3f, + 0xc0, 0x0, 0x0, + + /* U+5385 "厅" */ + 0x3f, 0xfe, 0x40, 0x0, 0x80, 0x1, 0x0, 0x2, + 0xff, 0xe4, 0x8, 0x8, 0x10, 0x10, 0x20, 0x20, + 0x40, 0x40, 0x80, 0x81, 0x2, 0x2, 0x4, 0x4, + 0x10, 0x78, 0x0, + + /* U+5386 "历" */ + 0x3f, 0xfe, 0x41, 0x0, 0x82, 0x1, 0x4, 0x2, + 0xff, 0xc4, 0x10, 0x88, 0x21, 0x10, 0x42, 0x21, + 0x84, 0x42, 0x8, 0x8c, 0x12, 0x10, 0x24, 0x40, + 0xdb, 0xf, 0x0, 0x0, 0x0, + + /* U+5389 "厉" */ + 0x3f, 0xfe, 0x40, 0x0, 0x80, 0x1, 0x7f, 0xf2, + 0x10, 0x4, 0x20, 0x8, 0x7f, 0x10, 0x82, 0x21, + 0x4, 0x42, 0x8, 0x88, 0x22, 0x10, 0x44, 0x40, + 0x91, 0xf, 0x0, + + /* U+538B "压" */ + 0x3f, 0xff, 0x20, 0x0, 0x20, 0x40, 0x20, 0x40, + 0x20, 0x40, 0x27, 0xfe, 0x20, 0x40, 0x20, 0x40, + 0x20, 0x48, 0x20, 0x44, 0x60, 0x40, 0x40, 0x40, + 0x5f, 0xff, 0x0, 0x0, + + /* U+538C "厌" */ + 0x3f, 0xfe, 0x40, 0x0, 0x81, 0x21, 0x2, 0x22, + 0x4, 0x5, 0xff, 0xc8, 0x10, 0x10, 0x60, 0x20, + 0xa0, 0x42, 0x40, 0x84, 0x42, 0x10, 0x44, 0xc0, + 0x61, 0x0, 0x0, + + /* U+538D "厍" */ + 0x3f, 0xfe, 0x40, 0x0, 0x84, 0x1, 0x7f, 0xf2, + 0x20, 0x4, 0x88, 0xa, 0x10, 0x17, 0xff, 0x20, + 0x40, 0x40, 0x80, 0xff, 0xfa, 0x2, 0x4, 0x4, + 0x0, 0x8, 0x0, + + /* U+5395 "厕" */ + 0x7f, 0xfe, 0x80, 0x1, 0x0, 0x12, 0xfc, 0xa5, + 0x29, 0x4a, 0x52, 0x94, 0xa5, 0x29, 0x4a, 0x52, + 0x94, 0xa5, 0x29, 0x1c, 0x56, 0x28, 0x28, 0x88, + 0x56, 0x3, 0x80, + + /* U+5398 "厘" */ + 0x3f, 0xfe, 0x20, 0x0, 0x2f, 0xfc, 0x28, 0x84, + 0x28, 0x84, 0x2f, 0xfc, 0x28, 0x84, 0x2f, 0xfc, + 0x20, 0x80, 0x20, 0x80, 0x2f, 0xfc, 0x40, 0x80, + 0x5f, 0xff, 0x0, 0x0, + + /* U+539A "厚" */ + 0x3f, 0xfe, 0x40, 0x0, 0xbf, 0xf1, 0x40, 0x22, + 0xff, 0xc5, 0xff, 0x88, 0x0, 0x10, 0x0, 0x2f, + 0xfc, 0x40, 0x60, 0xff, 0xfa, 0x2, 0x4, 0x4, + 0x10, 0x38, 0x0, + + /* U+539D "厝" */ + 0x3f, 0xff, 0x22, 0x10, 0x2f, 0xfe, 0x22, 0x10, + 0x22, 0x10, 0x2f, 0xff, 0x20, 0x0, 0x27, 0xfc, + 0x24, 0x4, 0x24, 0x4, 0x27, 0xfc, 0x44, 0x4, + 0x47, 0xfc, 0x4, 0x4, + + /* U+539F "原" */ + 0x3f, 0xff, 0x20, 0x80, 0x20, 0x80, 0x27, 0xfc, + 0x24, 0x4, 0x27, 0xfc, 0x24, 0x4, 0x24, 0x4, + 0x27, 0xfc, 0x20, 0x40, 0x22, 0x48, 0x44, 0x44, + 0x58, 0x43, 0x83, 0xc0, + + /* U+53A2 "厢" */ + 0x7f, 0xfe, 0x88, 0x1, 0x13, 0xf2, 0x24, 0x25, + 0xe8, 0x48, 0x9f, 0x93, 0xa1, 0x2b, 0x42, 0x54, + 0x84, 0xa9, 0xf9, 0x92, 0x14, 0x24, 0x28, 0x4f, + 0xd0, 0x90, 0x80, + + /* U+53A3 "厣" */ + 0x7f, 0xfe, 0x81, 0x61, 0x7f, 0xfa, 0xa, 0x4, + 0x26, 0x9, 0x83, 0x17, 0xff, 0x24, 0x44, 0x4f, + 0xf8, 0x91, 0x11, 0x3f, 0xe4, 0x44, 0x48, 0x8, + 0x0, 0x10, 0x0, + + /* U+53A5 "厥" */ + 0x7f, 0xfe, 0x80, 0x21, 0x28, 0x82, 0xfd, 0xe4, + 0x44, 0x4a, 0xa8, 0x95, 0x45, 0x2a, 0x88, 0x55, + 0x10, 0xbe, 0x61, 0x10, 0xa2, 0x61, 0x48, 0x84, + 0x52, 0x30, 0x40, + + /* U+53A6 "厦" */ + 0x3f, 0xfe, 0x20, 0x0, 0x27, 0xfc, 0x27, 0xfc, + 0x24, 0x4, 0x27, 0xfc, 0x27, 0xfc, 0x24, 0x4, + 0x23, 0xf8, 0x23, 0xfc, 0x4f, 0x8, 0x50, 0xb0, + 0x43, 0xf8, 0xc, 0x6, + + /* U+53A8 "厨" */ + 0x7f, 0xfe, 0x80, 0x1, 0x7f, 0x12, 0x0, 0x25, + 0xfb, 0xea, 0x10, 0x94, 0x21, 0x28, 0x52, 0x5f, + 0xa4, 0x80, 0x29, 0x26, 0x12, 0x68, 0x2b, 0xfc, + 0x50, 0x7, 0x80, + + /* U+53A9 "厩" */ + 0x7f, 0xfe, 0x80, 0x1, 0x7b, 0xf2, 0x94, 0x85, + 0xe9, 0xa, 0x52, 0x14, 0xa4, 0x2f, 0x36, 0x50, + 0x20, 0xa8, 0x61, 0x49, 0x46, 0xe6, 0x99, 0x19, + 0x30, 0x63, 0x80, + + /* U+53AE "厮" */ + 0x7f, 0xfe, 0x92, 0x1, 0x24, 0x7a, 0xfd, 0x4, + 0x92, 0x9, 0xe4, 0x12, 0x4f, 0xa7, 0x94, 0x49, + 0x28, 0xbf, 0x51, 0x0, 0xa4, 0x4a, 0x49, 0x4, + 0x82, 0x9, 0x0, + + /* U+53B6 "厶" */ + 0x0, 0x0, 0x8, 0x0, 0x20, 0x1, 0x0, 0x4, + 0x0, 0x10, 0x0, 0x84, 0x2, 0x10, 0x10, 0x20, + 0x80, 0xc2, 0x1, 0x10, 0x2, 0xff, 0xf8, 0x0, + 0x10, + + /* U+53BB "去" */ + 0x1, 0x0, 0x2, 0x0, 0x4, 0x3, 0xff, 0xe0, + 0x10, 0x0, 0x20, 0x0, 0x40, 0x7f, 0xff, 0x2, + 0x0, 0x8, 0x0, 0x21, 0x80, 0xc1, 0x81, 0x1, + 0x8f, 0xff, 0x0, 0x1, 0x0, + + /* U+53BF "县" */ + 0x3f, 0xf8, 0x40, 0x10, 0x80, 0x21, 0xff, 0xc2, + 0x0, 0x84, 0x1, 0xf, 0xfe, 0x10, 0x4, 0x20, + 0x9, 0xff, 0xfc, 0x10, 0x0, 0x41, 0x1, 0x1, + 0xf, 0xff, 0x0, 0x1, 0x0, + + /* U+53C1 "叁" */ + 0x6, 0x0, 0x31, 0x1, 0x2, 0xf, 0xfc, 0x2, + 0xb, 0xff, 0xf0, 0x84, 0x4, 0x8, 0x3f, 0xd3, + 0x0, 0x38, 0x0, 0x7, 0xf8, 0x0, 0x1, 0xff, + 0xe0, + + /* U+53C2 "参" */ + 0x6, 0x0, 0x23, 0x3, 0x2, 0x1f, 0xfc, 0x2, + 0x3, 0xff, 0xf0, 0x84, 0xc, 0x68, 0xc6, 0x18, + 0xe1, 0x10, 0x38, 0x7, 0xc, 0x21, 0x80, 0x38, + 0x7, 0x0, 0x0, + + /* U+53C8 "又" */ + 0x7f, 0xf0, 0x80, 0x41, 0x1, 0x4, 0x8, 0x10, + 0x20, 0x20, 0x80, 0x84, 0x1, 0x20, 0x6, 0x80, + 0xc, 0x0, 0x78, 0x2, 0x18, 0x70, 0x3a, 0x0, + 0x10, + + /* U+53C9 "叉" */ + 0x7f, 0xf8, 0x40, 0x10, 0x88, 0x20, 0x88, 0x81, + 0x19, 0x1, 0x4, 0x2, 0x8, 0x2, 0x20, 0x6, + 0x80, 0x6, 0x0, 0xa, 0x0, 0x63, 0x7, 0x1, + 0xd0, 0x0, 0x0, + + /* U+53CA "及" */ + 0x7f, 0xf0, 0x10, 0x20, 0x20, 0x80, 0x42, 0x0, + 0xc7, 0x81, 0x81, 0x4, 0x84, 0x9, 0x8, 0x11, + 0x20, 0x43, 0x80, 0x83, 0x2, 0x9, 0xc, 0xe1, + 0xc1, 0x0, 0x0, + + /* U+53CB "友" */ + 0x2, 0x0, 0x2, 0x0, 0x7f, 0xff, 0x4, 0x0, + 0x4, 0x0, 0x4, 0x0, 0xf, 0xfc, 0xa, 0x4, + 0xa, 0x4, 0x11, 0x8, 0x11, 0x90, 0x20, 0xe0, + 0x20, 0x60, 0x43, 0x98, 0x1c, 0x7, + + /* U+53CC "双" */ + 0xfd, 0xfc, 0xa, 0x8, 0x14, 0x16, 0x28, 0x44, + 0x88, 0x85, 0x11, 0xa, 0x24, 0x8, 0x28, 0x18, + 0x60, 0x70, 0x40, 0x91, 0xc2, 0x24, 0xc8, 0x30, + 0x40, 0x80, 0x0, + + /* U+53CD "反" */ + 0x0, 0xc, 0x7f, 0xf0, 0x80, 0x1, 0x0, 0x2, + 0x0, 0x7, 0xff, 0x89, 0x2, 0x12, 0x4, 0x22, + 0x10, 0x44, 0x20, 0x84, 0x82, 0x6, 0x4, 0x1e, + 0x8, 0xc3, 0x26, 0x1, 0x80, + + /* U+53D1 "发" */ + 0x2, 0x0, 0x44, 0x60, 0x88, 0x22, 0x10, 0x7, + 0xff, 0xe0, 0x80, 0x1, 0x0, 0x3, 0xfc, 0xc, + 0x8, 0x14, 0x20, 0x44, 0x41, 0xd, 0x2, 0xc, + 0x8, 0x36, 0x23, 0x83, 0x88, 0x0, + + /* U+53D4 "叔" */ + 0x10, 0x0, 0x23, 0xf8, 0x72, 0x10, 0x84, 0x41, + 0x8, 0x82, 0x11, 0x3f, 0xa2, 0x0, 0x28, 0x10, + 0x50, 0xa8, 0xa2, 0x48, 0x84, 0x93, 0x81, 0x5, + 0x2, 0x31, 0x9c, 0xc1, 0x0, + + /* U+53D6 "取" */ + 0xfe, 0x0, 0x8b, 0xf9, 0x12, 0x12, 0x24, 0x27, + 0xc8, 0x88, 0x91, 0x11, 0x22, 0x3e, 0x24, 0x44, + 0x50, 0x88, 0x61, 0x18, 0x87, 0xe2, 0x80, 0x4c, + 0x80, 0xb0, 0x81, 0x40, 0x0, + + /* U+53D7 "受" */ + 0x0, 0x1, 0xff, 0xe0, 0x1, 0x8, 0x84, 0x0, + 0x23, 0xff, 0xf8, 0x0, 0x60, 0x1, 0x3f, 0xe0, + 0x40, 0x80, 0x84, 0x1, 0x20, 0x3, 0x0, 0x33, + 0x8f, 0x1, 0xc0, + + /* U+53D8 "变" */ + 0x1, 0x0, 0x2, 0x3, 0xff, 0xf8, 0x22, 0x0, + 0x44, 0x4, 0x89, 0x11, 0x11, 0x40, 0x1, 0x3f, + 0xf0, 0x10, 0x20, 0x30, 0x80, 0x32, 0x0, 0x18, + 0x3, 0xce, 0x38, 0x3, 0x80, + + /* U+53D9 "叙" */ + 0x8, 0x0, 0x29, 0xf8, 0x4a, 0x11, 0xc, 0x24, + 0x4, 0x57, 0xc8, 0x82, 0x11, 0x4, 0x24, 0xff, + 0x28, 0x10, 0x60, 0xa8, 0x42, 0x51, 0x88, 0x94, + 0x81, 0x10, 0xce, 0x40, 0x80, + + /* U+53DB "叛" */ + 0x10, 0x5, 0x2b, 0xf9, 0x54, 0x2, 0xc8, 0x1, + 0x10, 0x1f, 0xbf, 0x84, 0x62, 0x8, 0xa4, 0x21, + 0x49, 0xfc, 0x90, 0x88, 0xc1, 0x11, 0x82, 0x23, + 0x8, 0x99, 0x21, 0x61, 0x80, + + /* U+53DF "叟" */ + 0x1, 0x0, 0xfa, 0xf9, 0x4, 0x12, 0x8, 0x27, + 0xf7, 0xc8, 0x20, 0x90, 0x41, 0x3f, 0xfe, 0x1, + 0x0, 0xff, 0xf0, 0x60, 0x60, 0x21, 0x0, 0x3c, + 0x1, 0xce, 0x3c, 0x3, 0x80, + + /* U+53E0 "叠" */ + 0x3f, 0xf0, 0x60, 0x40, 0x3f, 0x81, 0xe0, 0xc7, + 0xef, 0xc8, 0x91, 0xf, 0x1e, 0x20, 0x42, 0xff, + 0xfc, 0x3f, 0xe0, 0x80, 0x41, 0xff, 0x81, 0xff, + 0x4, 0x2, 0x3f, 0xff, 0x80, + + /* U+53E3 "口" */ + 0xff, 0xf8, 0x1, 0x80, 0x18, 0x1, 0x80, 0x18, + 0x1, 0x80, 0x18, 0x1, 0x80, 0x18, 0x1, 0x80, + 0x1f, 0xff, 0x80, 0x10, + + /* U+53E4 "古" */ + 0x1, 0x0, 0x2, 0x0, 0x4, 0x0, 0x8, 0xf, + 0xff, 0xe0, 0x20, 0x0, 0x40, 0x0, 0x80, 0x3f, + 0xf8, 0x40, 0x10, 0x80, 0x21, 0x0, 0x42, 0x0, + 0x84, 0x1, 0xf, 0xfe, 0x10, 0x4, + + /* U+53E5 "句" */ + 0x10, 0x0, 0x80, 0xf, 0xfe, 0xc0, 0x14, 0x0, + 0xdf, 0xc4, 0x82, 0x24, 0x11, 0x20, 0x89, 0x4, + 0x4f, 0xe2, 0x40, 0x10, 0x0, 0x80, 0xc, 0xf, + 0xc0, + + /* U+53E6 "另" */ + 0x7f, 0xf2, 0x0, 0x90, 0x4, 0x80, 0x24, 0x1, + 0x3f, 0xf8, 0x20, 0x1, 0x0, 0xff, 0xf8, 0x40, + 0x42, 0x2, 0x30, 0x11, 0x0, 0x90, 0xb, 0x7, + 0xc0, + + /* U+53E8 "叨" */ + 0xff, 0xfc, 0x48, 0x62, 0x43, 0x12, 0x18, 0x90, + 0xc4, 0x86, 0x24, 0x31, 0x21, 0x89, 0xf, 0xd8, + 0x60, 0x82, 0x4, 0x10, 0x40, 0x86, 0x38, + + /* U+53E9 "叩" */ + 0xf9, 0xfc, 0x48, 0x62, 0x43, 0x12, 0x18, 0x90, + 0xc4, 0x86, 0x24, 0x31, 0x21, 0x89, 0xf, 0xc8, + 0x62, 0x5c, 0x2, 0x0, 0x10, 0x0, 0x80, + + /* U+53EA "只" */ + 0x3f, 0xf8, 0x40, 0x10, 0x80, 0x21, 0x0, 0x42, + 0x0, 0x84, 0x1, 0x8, 0x2, 0x1f, 0xfc, 0x0, + 0x0, 0x8, 0x80, 0x20, 0x80, 0x80, 0x86, 0x0, + 0xd8, 0x0, 0xc0, + + /* U+53EB "叫" */ + 0x0, 0xf, 0xc8, 0x62, 0x43, 0x12, 0x18, 0x90, + 0xc4, 0x86, 0x24, 0x31, 0x21, 0x89, 0x1c, 0x4f, + 0x7e, 0x43, 0x10, 0x18, 0x80, 0x80, 0x4, 0x0, + 0x20, + + /* U+53EC "召" */ + 0x7f, 0xf8, 0x20, 0x41, 0x2, 0x10, 0x10, 0x80, + 0x98, 0x7b, 0x0, 0x7, 0xff, 0x20, 0x9, 0x0, + 0x48, 0x2, 0x40, 0x13, 0xff, 0x90, 0x4, + + /* U+53ED "叭" */ + 0x1, 0x23, 0xc4, 0x89, 0x12, 0x24, 0x48, 0x91, + 0x22, 0x44, 0x89, 0x12, 0x24, 0x48, 0x92, 0x12, + 0x48, 0x4f, 0x21, 0x21, 0x2, 0xc, 0x8, 0x60, + 0x10, 0x0, 0x0, + + /* U+53EE "叮" */ + 0xfb, 0xfe, 0x20, 0x88, 0x82, 0x22, 0x8, 0x88, + 0x22, 0x20, 0x88, 0x82, 0x22, 0x8, 0x88, 0x23, + 0xe0, 0x88, 0x82, 0x0, 0x8, 0x0, 0x20, 0x7, + 0x80, + + /* U+53EF "可" */ + 0xff, 0xfe, 0x0, 0x20, 0x0, 0x41, 0xf8, 0x84, + 0x11, 0x8, 0x22, 0x10, 0x44, 0x20, 0x88, 0x41, + 0x10, 0x7e, 0x20, 0x0, 0x40, 0x0, 0x80, 0x1, + 0x0, 0x3e, 0x0, + + /* U+53F0 "台" */ + 0x2, 0x0, 0x8, 0x0, 0x20, 0x80, 0x80, 0x82, + 0x0, 0x9f, 0xff, 0x80, 0x0, 0x80, 0x0, 0x3f, + 0xf8, 0x40, 0x10, 0x80, 0x21, 0x0, 0x42, 0x0, + 0x87, 0xff, 0x8, 0x2, 0x0, + + /* U+53F1 "叱" */ + 0x0, 0x80, 0x1, 0x3, 0xe2, 0x4, 0x44, 0x68, + 0x88, 0x91, 0x12, 0x22, 0x28, 0x44, 0x60, 0x89, + 0x81, 0x15, 0x2, 0x22, 0x17, 0xc4, 0x28, 0x88, + 0x50, 0x10, 0x80, 0x1f, 0x0, + + /* U+53F2 "史" */ + 0x0, 0x80, 0x1, 0x0, 0xff, 0xf1, 0x4, 0x22, + 0x8, 0x44, 0x10, 0x88, 0x21, 0x1f, 0xfe, 0x0, + 0x84, 0x21, 0x0, 0x26, 0x0, 0x28, 0x0, 0x30, + 0x3, 0x9f, 0xd8, 0x0, 0x0, + + /* U+53F3 "右" */ + 0x0, 0x0, 0x2, 0x0, 0x8, 0x3, 0xff, 0xf0, + 0x40, 0x0, 0x80, 0x2, 0x0, 0x4, 0x0, 0x1f, + 0xf8, 0x60, 0x11, 0xc0, 0x22, 0x80, 0x41, 0x0, + 0x82, 0x1, 0x3, 0xfe, 0x8, 0x4, + + /* U+53F5 "叵" */ + 0xff, 0xfe, 0x0, 0x8, 0x0, 0x23, 0xf8, 0x88, + 0x22, 0x20, 0x88, 0x82, 0x22, 0x8, 0x88, 0x22, + 0x3f, 0x88, 0x0, 0x20, 0x0, 0xff, 0xfc, + + /* U+53F6 "叶" */ + 0x0, 0x43, 0xe1, 0x8, 0x84, 0x22, 0x10, 0x88, + 0x42, 0x2f, 0xf8, 0x84, 0x22, 0x10, 0x88, 0x42, + 0x21, 0x8, 0x84, 0x3e, 0x10, 0x88, 0x40, 0x1, + 0x0, 0x4, 0x0, + + /* U+53F7 "号" */ + 0x3f, 0xf8, 0x40, 0x10, 0x80, 0x21, 0xff, 0xc0, + 0x0, 0x0, 0x0, 0x3f, 0xff, 0x88, 0x0, 0x1f, + 0xf8, 0x0, 0x10, 0x0, 0x20, 0x0, 0x40, 0x1, + 0x0, 0x7e, 0x0, + + /* U+53F8 "司" */ + 0x7f, 0xfc, 0x0, 0x10, 0x0, 0x7f, 0xfd, 0x0, + 0x4, 0x0, 0x17, 0xfc, 0x50, 0x11, 0x40, 0x45, + 0x1, 0x17, 0xfc, 0x50, 0x1, 0x0, 0x4, 0x1, + 0xe0, + + /* U+53F9 "叹" */ + 0xfb, 0xfd, 0x14, 0x1a, 0x24, 0x24, 0x48, 0x48, + 0x90, 0x91, 0x12, 0x22, 0x24, 0x44, 0x50, 0x88, + 0x61, 0xf0, 0xc2, 0x6, 0x40, 0x18, 0x40, 0xc0, + 0x60, 0x0, 0x0, + + /* U+53FB "叻" */ + 0x0, 0x83, 0xe2, 0x8, 0x88, 0x22, 0xff, 0x88, + 0x86, 0x22, 0x18, 0x88, 0x62, 0x21, 0x88, 0x86, + 0x26, 0x18, 0x90, 0x7e, 0x41, 0x82, 0x4, 0x18, + 0x10, 0xc7, 0x80, + + /* U+53FC "叼" */ + 0x7, 0xff, 0xc0, 0x62, 0x3, 0x10, 0x18, 0x80, + 0xc4, 0x26, 0x26, 0x31, 0x41, 0x88, 0xf, 0xc0, + 0x62, 0x3, 0x10, 0x10, 0x1, 0x80, 0xf8, + + /* U+53FD "叽" */ + 0xf3, 0xf2, 0x48, 0x49, 0x21, 0x24, 0x84, 0x92, + 0x12, 0x48, 0x49, 0x21, 0x24, 0x84, 0x92, 0x13, + 0xc8, 0x58, 0x61, 0x61, 0x5, 0xc, 0x14, 0x60, + 0x60, + + /* U+5401 "吁" */ + 0xfb, 0xfd, 0x10, 0x42, 0x20, 0x84, 0x41, 0x8, + 0x82, 0x11, 0x7f, 0xe2, 0x8, 0x44, 0x10, 0x88, + 0x21, 0xf0, 0x42, 0x20, 0x80, 0x1, 0x0, 0x2, + 0x0, 0x78, 0x0, + + /* U+5403 "吃" */ + 0x0, 0x83, 0xc4, 0x9, 0x3f, 0xe4, 0x80, 0x94, + 0x2, 0x47, 0xc9, 0x1, 0x24, 0x8, 0x90, 0x42, + 0x42, 0xf, 0x8, 0x64, 0x41, 0x2, 0x4, 0x8, + 0x10, 0x3f, 0x80, + + /* U+5404 "各" */ + 0x8, 0x0, 0x20, 0x1, 0xff, 0xc, 0x4, 0x68, + 0x23, 0x13, 0x0, 0x38, 0x1, 0xf0, 0x38, 0x3f, + 0x0, 0x3, 0xff, 0x8, 0x4, 0x20, 0x10, 0x80, + 0x43, 0xff, 0x8, 0x4, + + /* U+5406 "吆" */ + 0x0, 0x40, 0x2, 0xf, 0x8, 0x24, 0x42, 0x92, + 0xa, 0x48, 0x49, 0x7f, 0x24, 0x8, 0x90, 0x42, + 0x42, 0x4f, 0x9, 0x24, 0x42, 0x92, 0x8, 0x1f, + 0xf0, 0x0, 0x40, + + /* U+5408 "合" */ + 0x1, 0x0, 0x5, 0x0, 0x11, 0x0, 0xc1, 0x83, + 0x1, 0x9b, 0xfe, 0xc0, 0x0, 0x0, 0x0, 0x3f, + 0xf8, 0x40, 0x10, 0x80, 0x21, 0x0, 0x42, 0x0, + 0x87, 0xff, 0x8, 0x2, 0x0, + + /* U+5409 "吉" */ + 0x1, 0x0, 0x2, 0x3, 0xff, 0xf8, 0x8, 0x0, + 0x10, 0x0, 0x20, 0x1f, 0xff, 0x0, 0x0, 0x0, + 0x0, 0x7f, 0xf0, 0x80, 0x21, 0x0, 0x42, 0x0, + 0x87, 0xff, 0x8, 0x2, 0x0, + + /* U+540A "吊" */ + 0x7f, 0xf2, 0x0, 0x90, 0x4, 0x80, 0x27, 0xff, + 0x1, 0x0, 0x8, 0x0, 0x40, 0xff, 0xfc, 0x10, + 0x60, 0x83, 0x4, 0x18, 0x20, 0xc1, 0x3c, 0x8, + 0x0, + + /* U+540C "同" */ + 0xff, 0xfc, 0x0, 0x60, 0x3, 0x7f, 0xd8, 0x0, + 0xc0, 0x6, 0x7f, 0x32, 0x9, 0x90, 0x4c, 0x82, + 0x67, 0xf3, 0x0, 0x18, 0x0, 0xc0, 0x3c, + + /* U+540D "名" */ + 0x2, 0x0, 0x18, 0x0, 0xff, 0xc6, 0x2, 0x68, + 0x12, 0x19, 0x80, 0x18, 0x1, 0x80, 0x3f, 0xff, + 0x40, 0x11, 0x0, 0x44, 0x1, 0x10, 0x4, 0x7f, + 0xf1, 0x0, 0x40, + + /* U+540E "后" */ + 0x0, 0xc, 0x7f, 0xe0, 0x80, 0x1, 0x0, 0x3, + 0xff, 0xe4, 0x0, 0x8, 0x0, 0x10, 0x0, 0x27, + 0xfc, 0x48, 0x8, 0x90, 0x12, 0x20, 0x24, 0x40, + 0x48, 0xff, 0xa1, 0x1, 0x0, + + /* U+540F "吏" */ + 0x0, 0x0, 0x8, 0x0, 0x20, 0x3f, 0xff, 0x2, + 0x1, 0xff, 0xe4, 0x20, 0x90, 0x82, 0x42, 0x9, + 0xff, 0xe0, 0x20, 0x8c, 0x80, 0xc, 0x0, 0x38, + 0x7, 0x1f, 0xe0, 0x0, + + /* U+5410 "吐" */ + 0x0, 0x40, 0x1, 0xf, 0x4, 0x24, 0x10, 0x90, + 0x42, 0x5f, 0xe9, 0x4, 0x24, 0x10, 0x90, 0x42, + 0x41, 0x9, 0x4, 0x3c, 0x10, 0x80, 0x40, 0x3f, + 0xf0, + + /* U+5411 "向" */ + 0x4, 0x0, 0x20, 0x3f, 0xff, 0x0, 0x18, 0x0, + 0xc0, 0x6, 0x7f, 0x32, 0x9, 0x90, 0x4c, 0x82, + 0x67, 0xf3, 0x0, 0x18, 0x0, 0xc0, 0x6, 0x1, + 0xe0, + + /* U+5412 "吒" */ + 0x0, 0x3b, 0xdf, 0x9, 0x8, 0x24, 0x20, 0x90, + 0x82, 0x42, 0x9, 0xf, 0xe5, 0xe0, 0x90, 0x82, + 0x42, 0xf, 0x8, 0x64, 0x21, 0x0, 0x84, 0x2, + 0x10, 0x7, 0x80, + + /* U+5413 "吓" */ + 0xf7, 0xfe, 0x42, 0x9, 0x8, 0x24, 0x20, 0x90, + 0x82, 0x42, 0xc9, 0x8, 0xa4, 0x21, 0x90, 0x83, + 0xc2, 0x9, 0x8, 0x0, 0x20, 0x0, 0x80, 0x2, + 0x0, + + /* U+5415 "吕" */ + 0x7f, 0xf2, 0x0, 0x90, 0x4, 0x80, 0x27, 0xff, + 0x0, 0x0, 0x0, 0x1f, 0xff, 0x80, 0xc, 0x0, + 0x60, 0x3, 0x0, 0x1f, 0xff, 0xc0, 0x4, + + /* U+5416 "吖" */ + 0x2, 0x7, 0xec, 0x28, 0x91, 0x22, 0x28, 0x88, + 0x2, 0x21, 0x8, 0x84, 0x22, 0x10, 0x88, 0x42, + 0x21, 0xf, 0x84, 0x22, 0x10, 0x0, 0x40, 0x1, + 0x0, + + /* U+5417 "吗" */ + 0x7, 0xfb, 0xc0, 0x29, 0x0, 0xa4, 0x84, 0x92, + 0x12, 0x48, 0x49, 0x21, 0x24, 0xff, 0x90, 0x6, + 0x40, 0x1f, 0x7f, 0x64, 0x1, 0x0, 0x4, 0x0, + 0x20, 0x7, 0x80, + + /* U+541B "君" */ + 0x7f, 0xf8, 0x10, 0x10, 0x20, 0x27, 0xff, 0xf0, + 0x80, 0x82, 0x1, 0x1f, 0xfe, 0x8, 0x0, 0x20, + 0x0, 0x7f, 0xf1, 0x40, 0x24, 0x80, 0x41, 0x0, + 0x83, 0xff, 0x4, 0x2, 0x0, + + /* U+541D "吝" */ + 0x1, 0x0, 0x0, 0x80, 0x7f, 0xfe, 0x8, 0x10, + 0x4, 0x20, 0x2, 0x40, 0x1, 0xc0, 0x1e, 0x3c, + 0x60, 0x2, 0x1f, 0xf8, 0x10, 0x8, 0x10, 0x8, + 0x10, 0x8, 0x1f, 0xf8, 0x10, 0x8, + + /* U+541E "吞" */ + 0x7f, 0xfc, 0x2, 0x0, 0x8, 0x7, 0xff, 0xf0, + 0x44, 0x1, 0x8c, 0x6, 0xc, 0x30, 0x6, 0xdf, + 0xf2, 0x40, 0x20, 0x80, 0x41, 0x0, 0x81, 0xff, + 0x4, 0x2, 0x0, + + /* U+541F "吟" */ + 0x0, 0x40, 0x1, 0xf, 0xa, 0x24, 0x48, 0x93, + 0x12, 0x5a, 0x29, 0xc4, 0x64, 0x10, 0x90, 0x2, + 0x5f, 0xe9, 0x0, 0xbc, 0x4, 0x90, 0x20, 0x0, + 0x80, 0x4, 0x0, + + /* U+5420 "吠" */ + 0x0, 0x40, 0x0, 0x83, 0xe1, 0x24, 0x42, 0x28, + 0x84, 0x11, 0x8, 0x22, 0xff, 0xc4, 0x20, 0x88, + 0xc1, 0x11, 0x42, 0x22, 0x87, 0xc8, 0x88, 0x11, + 0x80, 0x41, 0x83, 0x1, 0x80, + + /* U+5421 "吡" */ + 0x2, 0x21, 0xe4, 0x42, 0x48, 0x84, 0x91, 0x39, + 0x22, 0x52, 0x75, 0x24, 0x8c, 0x49, 0x10, 0x92, + 0x21, 0x24, 0x42, 0x48, 0x87, 0x91, 0x19, 0x2a, + 0x20, 0x64, 0x40, 0x8f, 0x0, + + /* U+5423 "吣" */ + 0x0, 0x0, 0x0, 0x83, 0xc1, 0x84, 0x81, 0x9, + 0x10, 0x12, 0x20, 0xa5, 0x41, 0x4a, 0x82, 0x95, + 0x3, 0x32, 0x2, 0x44, 0x27, 0x88, 0x49, 0x10, + 0x92, 0x23, 0x0, 0x3c, 0x0, + + /* U+5426 "否" */ + 0x7f, 0xfe, 0x0, 0x80, 0x3, 0x80, 0x4, 0xb0, + 0x18, 0x8c, 0xe0, 0x82, 0x0, 0x0, 0x3f, 0xf8, + 0x20, 0x8, 0x20, 0x8, 0x20, 0x8, 0x20, 0x8, + 0x3f, 0xf8, 0x20, 0x8, + + /* U+5427 "吧" */ + 0x7, 0xfb, 0xd1, 0x29, 0x44, 0xa5, 0x12, 0x94, + 0x4a, 0x51, 0x29, 0x44, 0xa5, 0xfe, 0x94, 0x2, + 0x50, 0xf, 0x40, 0x65, 0x1, 0x4, 0x4, 0x10, + 0x10, 0x3f, 0x80, + + /* U+5428 "吨" */ + 0x0, 0x40, 0x1, 0xf, 0x7f, 0xe4, 0x10, 0x94, + 0x4a, 0x51, 0x29, 0x44, 0xa5, 0x12, 0x97, 0xfa, + 0x41, 0xf, 0x4, 0x64, 0x11, 0x80, 0x44, 0x1, + 0x10, 0x7, 0x80, + + /* U+5429 "吩" */ + 0x0, 0x20, 0x1, 0x43, 0xc4, 0x44, 0x88, 0x89, + 0x10, 0x92, 0x41, 0x25, 0x1, 0x4a, 0x1, 0x93, + 0xf9, 0x22, 0x12, 0x44, 0x27, 0x88, 0x48, 0x20, + 0x80, 0x41, 0x1, 0x82, 0x6, 0x78, + + /* U+542B "含" */ + 0x1, 0x0, 0x5, 0x0, 0x11, 0x0, 0xc1, 0x3, + 0x11, 0x98, 0x0, 0xc7, 0xfc, 0x0, 0x10, 0x0, + 0x40, 0x7f, 0xf0, 0x80, 0x21, 0x0, 0x42, 0x0, + 0x87, 0xff, 0x8, 0x2, 0x0, + + /* U+542C "听" */ + 0xf3, 0xb2, 0x48, 0x9, 0x20, 0x24, 0x80, 0x92, + 0x2, 0x4f, 0xf9, 0x21, 0x24, 0x84, 0x92, 0x13, + 0xc8, 0x48, 0x41, 0x21, 0x4, 0x8, 0x10, 0x20, + 0x40, + + /* U+542D "吭" */ + 0x0, 0x80, 0x2, 0xf, 0x8, 0x25, 0xfe, 0x90, + 0x2, 0x40, 0x9, 0x3e, 0x24, 0x88, 0x92, 0x22, + 0x48, 0x89, 0x22, 0x3c, 0x89, 0x82, 0x26, 0x10, + 0x90, 0xc2, 0x46, 0xe, + + /* U+542E "吮" */ + 0x0, 0x0, 0x1, 0x3, 0xc4, 0x4, 0x88, 0x89, + 0x21, 0x92, 0x81, 0x25, 0xff, 0x48, 0x91, 0x91, + 0x21, 0x22, 0x42, 0x44, 0x84, 0x89, 0xf, 0x22, + 0x20, 0x44, 0x41, 0x9, 0x4, 0x1e, 0x0, 0x0, + + /* U+542F "启" */ + 0x1, 0x0, 0x2, 0x3, 0xff, 0xc8, 0x1, 0x20, + 0x4, 0xff, 0xf2, 0x0, 0x8, 0x0, 0x2f, 0xfc, + 0xa0, 0x12, 0x80, 0x4a, 0x1, 0x48, 0x5, 0x3f, + 0xf8, 0x80, 0x40, + + /* U+5431 "吱" */ + 0x0, 0x43, 0xc1, 0x9, 0x7f, 0xe4, 0x10, 0x90, + 0x42, 0x41, 0x9, 0x7f, 0xa4, 0x82, 0x92, 0x12, + 0x44, 0x49, 0x1a, 0x3c, 0x30, 0x80, 0xe0, 0x1c, + 0x60, 0x80, 0x40, + + /* U+5432 "吲" */ + 0x0, 0xf, 0x9e, 0x64, 0x13, 0x20, 0x99, 0x7c, + 0xca, 0x6, 0x50, 0x32, 0x81, 0x97, 0x8c, 0x84, + 0x64, 0x23, 0xe1, 0x19, 0x8, 0x80, 0x44, 0x1c, + 0x20, + + /* U+5434 "吴" */ + 0x3f, 0xf8, 0x40, 0x10, 0x80, 0x21, 0x0, 0x43, + 0xff, 0x80, 0x0, 0x0, 0x0, 0x3f, 0xfe, 0x1, + 0x0, 0x2, 0x3, 0xff, 0xf8, 0xc, 0x0, 0x24, + 0x1, 0x86, 0x3c, 0x3, 0x80, + + /* U+5435 "吵" */ + 0x0, 0x40, 0x0, 0x83, 0xc1, 0x4, 0x82, 0x89, + 0x24, 0x92, 0x48, 0xa5, 0x11, 0x4a, 0x21, 0x98, + 0x49, 0x20, 0xb2, 0x40, 0x47, 0x81, 0x8, 0xc, + 0x0, 0x20, 0x3, 0x80, 0x0, 0x0, + + /* U+5438 "吸" */ + 0x7, 0xf1, 0xe4, 0x22, 0x48, 0x44, 0x90, 0x89, + 0x22, 0x12, 0x67, 0xa4, 0xc1, 0x49, 0x44, 0x92, + 0x89, 0x2c, 0xa3, 0xd0, 0x84, 0x23, 0x80, 0x8d, + 0x83, 0x60, 0xc0, 0x80, 0x0, + + /* U+5439 "吹" */ + 0x1, 0x0, 0x4, 0xf, 0x10, 0x24, 0xff, 0x92, + 0x6, 0x58, 0x19, 0x44, 0xa4, 0x10, 0x90, 0x42, + 0x41, 0x9, 0xa, 0x3c, 0x28, 0x81, 0x10, 0x8, + 0x60, 0xc0, 0xc2, 0x0, + + /* U+543B "吻" */ + 0x0, 0x0, 0x8, 0xf, 0x20, 0x24, 0xff, 0x94, + 0xa6, 0x54, 0x99, 0x92, 0x64, 0x49, 0x92, 0x26, + 0x49, 0x19, 0x44, 0x7f, 0x11, 0x90, 0x86, 0x46, + 0x10, 0x30, 0xc1, 0x9e, + + /* U+543C "吼" */ + 0x0, 0x23, 0xcf, 0x89, 0xa, 0x24, 0x28, 0x91, + 0x22, 0x44, 0x89, 0x1e, 0x25, 0xc8, 0x91, 0x22, + 0x44, 0x9f, 0x12, 0x64, 0x49, 0x1, 0x24, 0x4, + 0xf0, 0x70, 0x0, + + /* U+543E "吾" */ + 0x7f, 0xfc, 0x8, 0x0, 0xff, 0xc0, 0x20, 0x80, + 0x81, 0x1, 0x2, 0x3f, 0xff, 0x80, 0x0, 0x1f, + 0xf8, 0x20, 0x10, 0x40, 0x20, 0x80, 0x41, 0xff, + 0x82, 0x1, 0x0, + + /* U+5440 "呀" */ + 0x7, 0xfd, 0xe0, 0x22, 0x40, 0x44, 0x90, 0x89, + 0x21, 0x12, 0x42, 0x24, 0xff, 0xc8, 0x28, 0x90, + 0x51, 0x21, 0x23, 0xc4, 0x44, 0x30, 0x80, 0xc1, + 0x2, 0x2, 0x0, 0x38, 0x0, + + /* U+5443 "呃" */ + 0x7, 0xff, 0xe8, 0x2, 0x50, 0x4, 0xa0, 0x9, + 0x4f, 0xd2, 0x90, 0xa5, 0x21, 0x4a, 0x42, 0x94, + 0x85, 0x29, 0x73, 0xd2, 0xc, 0x24, 0x10, 0x88, + 0x21, 0x10, 0x46, 0x1f, 0x0, + + /* U+5446 "呆" */ + 0x3f, 0xf1, 0x0, 0x44, 0x1, 0x10, 0x4, 0x40, + 0x10, 0xff, 0xc0, 0x0, 0x0, 0x80, 0xff, 0xfc, + 0x1c, 0x0, 0x68, 0x2, 0x98, 0x32, 0x33, 0x8, + 0x30, 0x20, 0x0, + + /* U+5448 "呈" */ + 0x3f, 0xf8, 0x40, 0x10, 0x80, 0x21, 0xff, 0xc0, + 0x0, 0x0, 0x0, 0x1f, 0xff, 0x0, 0x80, 0x1, + 0x0, 0x7f, 0xf0, 0x4, 0x0, 0x8, 0xf, 0xff, + 0xe0, + + /* U+544A "告" */ + 0x11, 0x0, 0x22, 0x0, 0xff, 0xf1, 0x8, 0x4, + 0x10, 0x0, 0x20, 0x3f, 0xff, 0x80, 0x0, 0x0, + 0x0, 0x7f, 0xf0, 0x80, 0x21, 0x0, 0x42, 0x0, + 0x87, 0xff, 0x8, 0x2, 0x0, + + /* U+544B "呋" */ + 0x0, 0x40, 0x1, 0xf, 0x4, 0x25, 0xfe, 0x90, + 0x42, 0x41, 0x9, 0x8, 0x25, 0xff, 0x90, 0xc2, + 0x43, 0xf, 0x14, 0x20, 0x48, 0x2, 0x10, 0x30, + 0x20, 0x0, 0x40, + + /* U+5450 "呐" */ + 0x0, 0x40, 0x1, 0xf, 0x4, 0x25, 0xff, 0x94, + 0x46, 0x51, 0x19, 0x44, 0x65, 0x29, 0x94, 0xb6, + 0x54, 0x7f, 0x60, 0x65, 0x1, 0x94, 0x4, 0x10, + 0x10, 0x43, 0xc0, + + /* U+5452 "呒" */ + 0xf3, 0xfa, 0x41, 0x9, 0x4, 0x24, 0x10, 0x90, + 0x82, 0x5f, 0xf9, 0x8, 0x24, 0x30, 0x90, 0xc3, + 0xc5, 0x8, 0x14, 0x40, 0x91, 0x4, 0x44, 0x20, + 0xe0, + + /* U+5453 "呓" */ + 0x1, 0x13, 0xc4, 0x49, 0x7f, 0xe4, 0x44, 0x90, + 0x2, 0x40, 0x9, 0x3f, 0xa4, 0x4, 0x90, 0x22, + 0x43, 0xf, 0x18, 0x24, 0xc1, 0x96, 0x4, 0x10, + 0x10, 0x3f, 0x80, + + /* U+5454 "呔" */ + 0x0, 0x40, 0x0, 0x83, 0xe1, 0x4, 0x42, 0x8, + 0x84, 0x11, 0x7f, 0xe2, 0x10, 0x44, 0x20, 0x88, + 0xa1, 0x11, 0x42, 0x26, 0x84, 0x48, 0x8f, 0xb0, + 0x90, 0xd0, 0x82, 0x0, 0x80, + + /* U+5455 "呕" */ + 0x3, 0xff, 0xe4, 0x2, 0x4a, 0x14, 0x94, 0x49, + 0x24, 0x92, 0x46, 0x24, 0x8c, 0x49, 0x18, 0x92, + 0x29, 0x24, 0x93, 0xca, 0x14, 0x94, 0x28, 0x20, + 0x0, 0x7f, 0xc0, + + /* U+5456 "呖" */ + 0x7, 0xff, 0xd0, 0x9, 0x44, 0x25, 0x10, 0x94, + 0x42, 0x57, 0xf9, 0x44, 0x65, 0x11, 0x94, 0x46, + 0x51, 0x1f, 0x4c, 0x61, 0x21, 0x8, 0x84, 0x24, + 0x11, 0x23, 0x80, + + /* U+5457 "呗" */ + 0x3, 0xfd, 0xe4, 0xa, 0x48, 0x14, 0x92, 0x29, + 0x24, 0x52, 0x48, 0xa4, 0x91, 0x49, 0x22, 0x92, + 0x45, 0x24, 0x8a, 0x43, 0x7, 0x85, 0x8, 0x31, + 0x81, 0xc0, 0xc2, 0x0, 0x0, + + /* U+5458 "员" */ + 0x1f, 0xf8, 0x20, 0x10, 0x40, 0x20, 0x80, 0x41, + 0xff, 0x80, 0x0, 0xf, 0xff, 0x10, 0x2, 0x20, + 0x84, 0x41, 0x8, 0x84, 0x11, 0xe, 0x20, 0x23, + 0x3, 0x81, 0xd8, 0x0, 0x0, + + /* U+5459 "呙" */ + 0x7f, 0xf2, 0x0, 0x90, 0x4, 0xff, 0xe0, 0x20, + 0x1, 0x3, 0xff, 0xf0, 0x41, 0x87, 0xc, 0x2c, + 0x66, 0x13, 0x40, 0x18, 0x0, 0xc0, 0x3c, + + /* U+545B "呛" */ + 0x0, 0x40, 0x3, 0xf, 0xa, 0x24, 0x48, 0x92, + 0x12, 0x58, 0x39, 0x40, 0x24, 0xfc, 0x92, 0x12, + 0x48, 0x49, 0x21, 0x3c, 0xb8, 0x92, 0x6, 0x48, + 0x10, 0x20, 0xc0, 0x7e, + + /* U+545C "呜" */ + 0x0, 0x80, 0x2, 0xf, 0x3f, 0xa4, 0x82, 0x92, + 0xa, 0x49, 0xc9, 0x20, 0x24, 0xff, 0x90, 0x6, + 0x40, 0x19, 0x0, 0x7d, 0xf9, 0x90, 0x4, 0x0, + 0x10, 0xf, 0x80, + + /* U+5462 "呢" */ + 0x7, 0xfd, 0xe8, 0xa, 0x50, 0x14, 0xa0, 0x29, + 0x40, 0x52, 0xff, 0xa5, 0x0, 0x4a, 0x40, 0x94, + 0x8d, 0x29, 0x23, 0xd3, 0x84, 0x64, 0x10, 0x88, + 0x21, 0x10, 0x44, 0x3f, 0x0, + + /* U+5464 "呤" */ + 0x0, 0x40, 0x3, 0xf, 0xc, 0x24, 0x48, 0x92, + 0x12, 0x5a, 0x29, 0xc8, 0x64, 0x10, 0x90, 0x2, + 0x5f, 0xe9, 0x1, 0xbc, 0x88, 0x91, 0x40, 0x3, + 0x0, 0x6, 0x0, + + /* U+5466 "呦" */ + 0x2, 0x23, 0xc8, 0x89, 0x62, 0x25, 0x1f, 0x95, + 0x26, 0x7c, 0x99, 0x22, 0x64, 0x89, 0x93, 0x26, + 0x54, 0x99, 0x52, 0x7d, 0xb1, 0x90, 0x44, 0x3, + 0x10, 0xb, 0x80, + + /* U+5468 "周" */ + 0x3f, 0xff, 0x20, 0x1, 0x20, 0x81, 0x27, 0xf9, + 0x20, 0x81, 0x20, 0x81, 0x2f, 0xfd, 0x20, 0x1, + 0x27, 0xf1, 0x24, 0x11, 0x24, 0x11, 0x27, 0xf1, + 0x40, 0x1, 0x40, 0x1, 0x40, 0x1e, + + /* U+5471 "呱" */ + 0x0, 0x19, 0xef, 0xe2, 0x52, 0x44, 0xa4, 0x89, + 0x49, 0x12, 0x92, 0x25, 0x24, 0x4a, 0x48, 0x94, + 0x89, 0x29, 0x12, 0x53, 0x27, 0xed, 0x68, 0x92, + 0x41, 0x3a, 0x40, 0x0, 0x0, + + /* U+5472 "呲" */ + 0x1, 0x21, 0xc2, 0x42, 0x84, 0x85, 0x29, 0x1a, + 0x52, 0x54, 0xbf, 0x29, 0x48, 0x52, 0x90, 0xa5, + 0x21, 0x4a, 0x42, 0x94, 0x8f, 0x29, 0x18, 0x5a, + 0x23, 0xc4, 0x40, 0xf, 0x0, + + /* U+5473 "味" */ + 0x0, 0x80, 0x2, 0xf, 0x8, 0x25, 0xfe, 0x90, + 0x82, 0x42, 0x9, 0xff, 0xe4, 0x30, 0x91, 0xa2, + 0x4a, 0x8f, 0x29, 0x21, 0x22, 0x8, 0x84, 0x2, + 0x0, 0x8, 0x0, + + /* U+5475 "呵" */ + 0xf7, 0xff, 0x20, 0x12, 0x40, 0x24, 0x9e, 0x49, + 0x24, 0x92, 0x49, 0x24, 0x92, 0x49, 0x24, 0x92, + 0x49, 0x27, 0x93, 0xc0, 0x24, 0x80, 0x40, 0x0, + 0x80, 0xf, 0x0, + + /* U+5476 "呶" */ + 0x4, 0x1, 0xe8, 0xfa, 0x51, 0x14, 0xfa, 0x29, + 0x54, 0x52, 0xa8, 0xa6, 0x51, 0x4c, 0x94, 0x99, + 0x29, 0x2c, 0x72, 0x48, 0x47, 0xa9, 0xc8, 0x46, + 0x81, 0x18, 0xc0, 0x20, 0x0, + + /* U+5477 "呷" */ + 0x7, 0xff, 0xd1, 0x19, 0x44, 0x65, 0x11, 0x94, + 0x46, 0x5f, 0xf9, 0x44, 0x65, 0x11, 0x94, 0x47, + 0xdf, 0xf9, 0x44, 0x60, 0x10, 0x0, 0x40, 0x1, + 0x0, 0x4, 0x0, + + /* U+5478 "呸" */ + 0x7, 0xff, 0xc1, 0x9, 0x4, 0x24, 0x20, 0x90, + 0xc2, 0x45, 0x49, 0x15, 0x24, 0x92, 0x94, 0x47, + 0xf1, 0x18, 0x4, 0x0, 0x10, 0x0, 0x0, 0x1f, + 0xf0, + + /* U+547B "呻" */ + 0x0, 0x43, 0xc1, 0x9, 0x7f, 0xe5, 0x11, 0x94, + 0x46, 0x51, 0x19, 0x7f, 0xe5, 0x11, 0x94, 0x46, + 0x51, 0x1f, 0x7f, 0xe4, 0x10, 0x80, 0x40, 0x1, + 0x0, 0x4, 0x0, + + /* U+547C "呼" */ + 0x0, 0x1f, 0xdf, 0x89, 0x4, 0x25, 0x11, 0x92, + 0x4a, 0x49, 0x29, 0x15, 0x24, 0x10, 0x97, 0xfe, + 0x41, 0x9, 0x4, 0x3c, 0x10, 0x90, 0x40, 0x1, + 0x0, 0x3c, 0x0, + + /* U+547D "命" */ + 0x0, 0x80, 0x3, 0x0, 0x9, 0x0, 0x61, 0x81, + 0x80, 0xcc, 0xfe, 0x40, 0x0, 0x0, 0x0, 0x3e, + 0x7c, 0x45, 0x8, 0x8a, 0x11, 0x14, 0x23, 0xe8, + 0x44, 0x53, 0x88, 0x20, 0x0, 0x40, + + /* U+5480 "咀" */ + 0x3, 0xfb, 0xc8, 0x29, 0x20, 0xa4, 0x82, 0x92, + 0xa, 0x4f, 0xe9, 0x20, 0xa4, 0x82, 0x92, 0xa, + 0x4f, 0xe9, 0x20, 0xbc, 0x82, 0x82, 0x8, 0x3f, + 0xf0, + + /* U+5482 "咂" */ + 0x7, 0xff, 0xd0, 0x9, 0x42, 0x25, 0x7f, 0x95, + 0x26, 0x54, 0x99, 0x52, 0x65, 0x49, 0x95, 0x27, + 0xd4, 0xa9, 0x42, 0x25, 0x8, 0x7, 0xfc, + + /* U+5484 "咄" */ + 0x0, 0x40, 0x1, 0xf, 0x44, 0x65, 0x11, 0x94, + 0x46, 0x51, 0x19, 0x7f, 0xe4, 0x10, 0x90, 0x42, + 0x51, 0x19, 0x44, 0x7d, 0x11, 0x94, 0x44, 0x1f, + 0xf0, 0x0, 0x40, + + /* U+5486 "咆" */ + 0x2, 0x0, 0x8, 0xf, 0x3f, 0xa5, 0x2, 0x94, + 0xa, 0x7f, 0x29, 0x44, 0xa5, 0x12, 0x97, 0xca, + 0x50, 0x2f, 0x4f, 0x25, 0x1, 0x94, 0x4, 0x10, + 0x10, 0x3f, 0x80, + + /* U+548B "咋" */ + 0x1, 0x0, 0x4, 0xf, 0x10, 0x24, 0xff, 0x92, + 0x82, 0x52, 0x9, 0x8, 0x24, 0x3f, 0x90, 0x82, + 0x42, 0x9, 0x8, 0x3c, 0x3f, 0x90, 0x82, 0x42, + 0x0, 0x8, 0x0, 0x20, + + /* U+548C "和" */ + 0xfe, 0x0, 0x21, 0xfc, 0x42, 0xf, 0xf4, 0x11, + 0x8, 0x26, 0x10, 0x4e, 0x20, 0xaa, 0x41, 0x52, + 0x83, 0x21, 0x4, 0x42, 0x8, 0x87, 0xf1, 0x8, + 0x22, 0x10, 0x40, + + /* U+548E "咎" */ + 0x10, 0x40, 0x20, 0x80, 0xfd, 0x1, 0x13, 0x86, + 0x24, 0xd4, 0x88, 0x45, 0x10, 0x6, 0x0, 0x33, + 0xff, 0x80, 0x0, 0xff, 0xe1, 0x0, 0x42, 0x0, + 0x84, 0x1, 0xf, 0xfe, 0x10, 0x4, + + /* U+548F "咏" */ + 0x1, 0x0, 0x1, 0xc3, 0xc0, 0x4, 0xbe, 0x9, + 0x4, 0x12, 0x8, 0xa5, 0xda, 0x48, 0xb8, 0x92, + 0x61, 0x24, 0xa2, 0x49, 0x27, 0xa2, 0x48, 0x84, + 0x60, 0x8, 0x0, 0x70, 0x0, + + /* U+5490 "咐" */ + 0x1, 0x9, 0xe2, 0x12, 0x48, 0x24, 0x97, 0xf9, + 0x60, 0x93, 0x41, 0x24, 0xa2, 0x49, 0x24, 0x92, + 0x29, 0x24, 0x13, 0xc8, 0x24, 0x90, 0x49, 0x20, + 0x80, 0x41, 0x0, 0x9c, 0x0, + + /* U+5492 "咒" */ + 0x7e, 0x7c, 0x42, 0x44, 0x42, 0x44, 0x42, 0x44, + 0x7e, 0x7c, 0x0, 0x0, 0xf, 0xe0, 0x8, 0x20, + 0x8, 0x20, 0x8, 0x22, 0x8, 0x22, 0x18, 0x22, + 0x30, 0x22, 0xe0, 0x1e, + + /* U+5494 "咔" */ + 0x0, 0x83, 0xc2, 0x9, 0xf, 0xa4, 0x20, 0x90, + 0x82, 0x42, 0x9, 0x7f, 0xe4, 0x0, 0x90, 0x82, + 0x43, 0x9, 0xb, 0x3c, 0x23, 0x90, 0x80, 0x2, + 0x0, 0x8, 0x0, + + /* U+5495 "咕" */ + 0x0, 0x40, 0x1, 0xf, 0x4, 0x25, 0xff, 0x90, + 0x42, 0x41, 0x9, 0x4, 0x24, 0x10, 0x93, 0xfa, + 0x48, 0x29, 0x20, 0xbc, 0x82, 0x92, 0x8, 0xf, + 0xe0, 0x20, 0x80, + + /* U+5496 "咖" */ + 0x4, 0x0, 0x10, 0xf, 0x43, 0xe7, 0xe9, 0x94, + 0xa6, 0x52, 0x99, 0x4a, 0x65, 0x29, 0x94, 0xa6, + 0x52, 0x99, 0x4a, 0x7d, 0x29, 0x88, 0xbc, 0x22, + 0x90, 0xb0, 0x0, + + /* U+5499 "咙" */ + 0x2, 0x21, 0xe4, 0x22, 0x48, 0x4, 0xbf, 0xe9, + 0x24, 0x12, 0x48, 0x24, 0x92, 0x49, 0x2c, 0x92, + 0x51, 0x28, 0xc3, 0xd1, 0xc, 0x26, 0x20, 0xb4, + 0x41, 0x48, 0x84, 0x1f, 0x0, + + /* U+549A "咚" */ + 0x0, 0x0, 0x2, 0x3, 0xc7, 0xf4, 0x98, 0x29, + 0x30, 0x92, 0x92, 0x24, 0x18, 0x48, 0x78, 0x93, + 0xf, 0x20, 0x2, 0x43, 0x87, 0x80, 0xc9, 0x0, + 0x12, 0x70, 0x0, 0x1c, 0x0, 0x4, + + /* U+549B "咛" */ + 0x0, 0x41, 0xe0, 0x42, 0x5f, 0xf4, 0xa0, 0x29, + 0x40, 0x12, 0x0, 0x25, 0xff, 0xc8, 0x10, 0x90, + 0x21, 0x20, 0x43, 0xc0, 0x84, 0x81, 0x8, 0x2, + 0x0, 0xc, 0x0, 0x70, 0x0, + + /* U+549D "咝" */ + 0x1, 0x9, 0xe2, 0x12, 0x48, 0x44, 0x90, 0x89, + 0x4a, 0x52, 0xf7, 0xa4, 0x42, 0x48, 0x88, 0x92, + 0x11, 0x28, 0x42, 0x5e, 0xf7, 0x80, 0x8, 0x0, + 0x1, 0xff, 0x80, + + /* U+54A3 "咣" */ + 0x0, 0x40, 0x1, 0xf, 0x44, 0xa4, 0x92, 0x92, + 0x52, 0x41, 0x9, 0x4, 0x25, 0xff, 0x91, 0x22, + 0x44, 0x89, 0x12, 0x24, 0x48, 0xf2, 0x26, 0x8, + 0x90, 0x42, 0x42, 0xe, 0x0, 0x0, + + /* U+54A4 "咤" */ + 0x0, 0x80, 0x1, 0xf, 0x7f, 0xe5, 0x1, 0x94, + 0x6, 0x40, 0x9, 0x7f, 0x24, 0x20, 0x90, 0x82, + 0x43, 0xf9, 0x78, 0x3c, 0x21, 0x90, 0x86, 0x42, + 0x10, 0x8, 0x40, 0x3e, + + /* U+54A6 "咦" */ + 0x0, 0x41, 0xef, 0xfe, 0x41, 0x4, 0x82, 0x9, + 0x7f, 0xd2, 0x8, 0xa5, 0xff, 0x4a, 0x20, 0x94, + 0x41, 0x2f, 0xfe, 0x43, 0xf, 0x85, 0x68, 0x31, + 0x1, 0x81, 0xc0, 0x0, 0x0, + + /* U+54A7 "咧" */ + 0x0, 0x7, 0xce, 0x19, 0x21, 0x64, 0x85, 0x93, + 0xd6, 0x49, 0x59, 0x45, 0x65, 0x95, 0x99, 0x96, + 0x42, 0x59, 0x9, 0x7c, 0x45, 0x82, 0x4, 0x18, + 0x10, 0xc1, 0xc0, + + /* U+54A8 "咨" */ + 0x1, 0x0, 0x84, 0x0, 0x8f, 0xf0, 0x22, 0x20, + 0x4c, 0xc6, 0x18, 0x8, 0x48, 0x21, 0xc, 0x8c, + 0xe, 0x0, 0x0, 0xff, 0xe1, 0x0, 0x42, 0x0, + 0x84, 0x1, 0xf, 0xfe, 0x10, 0x4, + + /* U+54A9 "咩" */ + 0x1, 0x8, 0x2, 0x33, 0xc0, 0x44, 0xbf, 0xe9, + 0x4, 0x12, 0x8, 0x24, 0xff, 0x48, 0x20, 0x90, + 0x41, 0x20, 0x82, 0x5f, 0xff, 0x82, 0x9, 0x4, + 0x0, 0x8, 0x0, 0x10, 0x0, + + /* U+54AA "咪" */ + 0x0, 0x41, 0xe8, 0x8a, 0x49, 0x24, 0x92, 0x49, + 0x5, 0x12, 0x8, 0x25, 0xff, 0xc8, 0x70, 0x91, + 0x61, 0x22, 0xa3, 0xc9, 0x24, 0x22, 0x40, 0xc4, + 0x60, 0x8, 0x0, 0x10, 0x0, + + /* U+54AB "咫" */ + 0x3f, 0x7e, 0x21, 0x42, 0x21, 0x42, 0x21, 0x42, + 0x21, 0x42, 0x3f, 0x7e, 0x24, 0x0, 0x24, 0x24, + 0x24, 0x42, 0x22, 0x42, 0x43, 0x1, 0x41, 0x80, + 0x40, 0x70, 0x80, 0x1f, + + /* U+54AC "咬" */ + 0x0, 0x0, 0x1, 0xf, 0x4, 0x25, 0xff, 0x90, + 0x2, 0x44, 0x49, 0x21, 0xa5, 0x3, 0x92, 0x16, + 0x44, 0x89, 0x12, 0x24, 0x30, 0xf0, 0xc2, 0x6, + 0xc0, 0x61, 0xc2, 0x0, + + /* U+54AD "咭" */ + 0x0, 0x1, 0xe0, 0x82, 0x5f, 0xfc, 0x82, 0x9, + 0x4, 0x12, 0x8, 0x24, 0xff, 0x48, 0x0, 0x90, + 0x1, 0x27, 0xfa, 0x48, 0x17, 0x90, 0x29, 0x20, + 0x40, 0x7f, 0x80, 0x81, 0x0, + + /* U+54AF "咯" */ + 0x0, 0x80, 0x4, 0xf, 0x1f, 0xa4, 0x82, 0x97, + 0x12, 0x52, 0xc9, 0xe, 0x24, 0x38, 0x93, 0x1e, + 0x50, 0x9, 0x3f, 0xa4, 0x82, 0xf2, 0xa, 0x48, + 0x20, 0x3f, 0x80, 0x82, + + /* U+54B1 "咱" */ + 0x0, 0x40, 0x4, 0x3c, 0xff, 0x24, 0x19, 0x20, + 0xc9, 0x6, 0x4f, 0xf2, 0x41, 0x92, 0xc, 0x9f, + 0xfc, 0x83, 0x24, 0x19, 0x20, 0x81, 0xfc, 0x8, + 0x20, + + /* U+54B3 "咳" */ + 0x0, 0x41, 0xe0, 0x82, 0x5f, 0xfc, 0x84, 0x9, + 0x11, 0x12, 0x42, 0x25, 0xf8, 0x48, 0x32, 0x90, + 0xc9, 0x23, 0x12, 0x48, 0x44, 0xa1, 0x8f, 0xd, + 0x90, 0x60, 0x81, 0x0, 0x0, + + /* U+54B4 "咴" */ + 0x1, 0x1, 0xe2, 0x2, 0x5f, 0xfc, 0x88, 0x9, + 0x12, 0x12, 0x44, 0x24, 0xc9, 0x49, 0x52, 0x92, + 0xa9, 0x28, 0xa3, 0xd1, 0x44, 0x44, 0x41, 0x88, + 0x82, 0x20, 0x80, 0x80, 0x0, + + /* U+54B8 "咸" */ + 0x0, 0x20, 0x0, 0x28, 0x0, 0x24, 0x3f, 0xff, + 0x20, 0x20, 0x20, 0x20, 0x2f, 0xa2, 0x20, 0x24, + 0x20, 0x24, 0x2f, 0xa4, 0x28, 0x98, 0x28, 0x98, + 0x28, 0x91, 0x4f, 0xb1, 0x40, 0x6a, 0x80, 0x46, + + /* U+54BB "咻" */ + 0x2, 0x23, 0xc8, 0x89, 0x22, 0x25, 0x7f, 0x94, + 0x22, 0x71, 0x89, 0xc6, 0x25, 0x2c, 0x94, 0xb2, + 0x52, 0xa9, 0x52, 0xbd, 0x49, 0x94, 0x20, 0x10, + 0x80, 0x42, 0x0, + + /* U+54BD "咽" */ + 0x7, 0xff, 0xd0, 0x19, 0x40, 0x65, 0x11, 0x94, + 0x46, 0x57, 0xd9, 0x44, 0x65, 0x11, 0x94, 0xc6, + 0x52, 0x99, 0x49, 0x7d, 0x45, 0x94, 0x4, 0x1f, + 0xf0, 0x40, 0x40, + + /* U+54BF "咿" */ + 0x2, 0x3, 0xcb, 0xe9, 0x44, 0xa5, 0x12, 0x9c, + 0x4a, 0x77, 0xf9, 0x44, 0xa5, 0x12, 0x94, 0x4a, + 0x57, 0xe9, 0x44, 0xbd, 0x10, 0x94, 0x80, 0x12, + 0x0, 0x50, 0x0, + + /* U+54C0 "哀" */ + 0x2, 0x0, 0x2, 0x3, 0xff, 0xf8, 0x0, 0x3, + 0xff, 0x88, 0x1, 0x10, 0x2, 0x1f, 0xfc, 0x3, + 0x0, 0x1a, 0x18, 0x43, 0xc3, 0x82, 0x9, 0x13, + 0x83, 0xc0, 0xc0, 0x0, 0x0, + + /* U+54C1 "品" */ + 0x3f, 0xf0, 0x80, 0x42, 0x1, 0x8, 0x4, 0x20, + 0x10, 0xff, 0xc0, 0x0, 0x0, 0x0, 0xfc, 0xfe, + 0x12, 0x18, 0x48, 0x61, 0x21, 0x84, 0x87, 0xf3, + 0xf8, 0x48, 0x40, + + /* U+54C2 "哂" */ + 0x7, 0xff, 0xe1, 0x42, 0x42, 0x84, 0x85, 0x9, + 0x7f, 0xd2, 0x94, 0xa5, 0x29, 0x4a, 0x52, 0x95, + 0x25, 0x2a, 0x7a, 0x58, 0x17, 0xa0, 0x29, 0x40, + 0x40, 0xff, 0x81, 0x1, 0x0, + + /* U+54C4 "哄" */ + 0x1, 0x11, 0xe2, 0x22, 0x44, 0x44, 0xbf, 0xe9, + 0x11, 0x12, 0x22, 0x24, 0x44, 0x48, 0x88, 0x91, + 0x11, 0x2f, 0xfe, 0x40, 0x7, 0x88, 0x88, 0x20, + 0x80, 0x80, 0x82, 0x0, 0x0, + + /* U+54C6 "哆" */ + 0x0, 0x80, 0x2, 0xf, 0x1f, 0xe4, 0x82, 0x95, + 0x92, 0x42, 0x89, 0x4, 0x24, 0x60, 0x96, 0x42, + 0x43, 0xf9, 0x30, 0x65, 0xe2, 0xf0, 0x92, 0x1, + 0x80, 0x78, 0x2, 0x0, + + /* U+54C7 "哇" */ + 0x0, 0x41, 0xe0, 0x82, 0x5f, 0xf4, 0x82, 0x9, + 0x4, 0x12, 0x8, 0x25, 0xff, 0xc8, 0x20, 0x90, + 0x41, 0x2f, 0xfb, 0xc1, 0x4, 0x82, 0x8, 0x4, + 0x1, 0xff, 0xc0, + + /* U+54C8 "哈" */ + 0x0, 0x40, 0x1, 0xc3, 0xc2, 0xc4, 0x98, 0xc9, + 0x60, 0x52, 0x3e, 0x24, 0x0, 0x48, 0x0, 0x93, + 0xf9, 0x24, 0x12, 0x48, 0x27, 0x90, 0x49, 0x20, + 0x90, 0x7f, 0x0, 0x82, 0x0, + + /* U+54C9 "哉" */ + 0x8, 0x40, 0x10, 0xa3, 0xfd, 0x20, 0x42, 0x20, + 0x84, 0x1f, 0xff, 0xc0, 0x10, 0x0, 0x22, 0x7e, + 0x48, 0x84, 0xa1, 0x8, 0xc2, 0x11, 0x17, 0xe5, + 0x28, 0x12, 0x50, 0x43, 0x0, + + /* U+54CC "哌" */ + 0x0, 0x3f, 0xdf, 0x9, 0x40, 0x25, 0x0, 0x94, + 0xfa, 0x52, 0x89, 0x4a, 0x25, 0x29, 0x94, 0xba, + 0x52, 0x4f, 0x49, 0x22, 0x22, 0x8, 0xac, 0x23, + 0x10, 0x0, 0x0, + + /* U+54CD "响" */ + 0x0, 0x80, 0x2, 0xf, 0x7f, 0xe5, 0x1, 0x94, + 0x6, 0x57, 0x99, 0x52, 0x65, 0x49, 0x95, 0x26, + 0x54, 0x9f, 0x5e, 0x65, 0x1, 0x84, 0x4, 0x10, + 0x10, 0x43, 0xc0, + + /* U+54CE "哎" */ + 0x1, 0x20, 0x4, 0x8f, 0x7f, 0xe4, 0x48, 0x90, + 0x2, 0x48, 0x49, 0x21, 0x24, 0x48, 0x91, 0x22, + 0x45, 0x9, 0xc, 0x3c, 0x30, 0x81, 0x20, 0x18, + 0x60, 0x80, 0x40, + + /* U+54CF "哏" */ + 0x3, 0xfb, 0xc8, 0x29, 0x20, 0xa4, 0x82, 0x93, + 0xfa, 0x48, 0x29, 0x20, 0xa4, 0xfe, 0x92, 0x42, + 0x49, 0x1f, 0x25, 0xa4, 0x88, 0x2, 0x50, 0xe, + 0x30, 0x20, 0x0, + + /* U+54D0 "哐" */ + 0x7, 0xff, 0xd0, 0x9, 0x40, 0x25, 0x0, 0x95, + 0xfe, 0x50, 0x89, 0x42, 0x25, 0x3e, 0x94, 0x22, + 0x50, 0x89, 0x42, 0x3d, 0x7f, 0x94, 0x0, 0x1f, + 0xf0, + + /* U+54D1 "哑" */ + 0x7, 0xff, 0xc4, 0x89, 0x12, 0x24, 0x48, 0x99, + 0x26, 0x54, 0x99, 0x52, 0xa5, 0x4a, 0x95, 0x2a, + 0x54, 0xa9, 0x12, 0x3c, 0x48, 0x1, 0x20, 0x3f, + 0xf0, + + /* U+54D2 "哒" */ + 0x4, 0x23, 0xc8, 0x89, 0x22, 0x24, 0x3f, 0x90, + 0x22, 0x78, 0x89, 0x22, 0x24, 0x94, 0x92, 0x4a, + 0x49, 0x3f, 0x28, 0x60, 0x80, 0x85, 0x0, 0x26, + 0x0, 0x87, 0xc0, + + /* U+54D3 "哓" */ + 0x0, 0x0, 0x2, 0x3, 0xc4, 0xf4, 0xbe, 0x9, + 0x11, 0x92, 0x14, 0x24, 0x30, 0xc9, 0xb2, 0x94, + 0x1d, 0x20, 0x2, 0x5f, 0xf4, 0x89, 0xf, 0x12, + 0x10, 0x44, 0x40, 0x88, 0x86, 0x1e, + + /* U+54D4 "哔" */ + 0x4, 0x21, 0xe8, 0x4a, 0x5c, 0xe4, 0xa1, 0x9, + 0x42, 0x12, 0x84, 0x65, 0x49, 0x4b, 0x1e, 0x90, + 0x41, 0x20, 0x82, 0x7f, 0xff, 0x82, 0x9, 0x4, + 0x0, 0x8, 0x0, 0x10, 0x0, + + /* U+54D5 "哕" */ + 0x0, 0x43, 0xd1, 0x19, 0x44, 0x65, 0x11, 0x97, + 0xfe, 0x40, 0x9, 0x10, 0x24, 0x7f, 0x92, 0xe, + 0x58, 0x29, 0x99, 0x3c, 0x8, 0x80, 0xc0, 0xc, + 0x0, 0x40, 0x0, + + /* U+54D7 "哗" */ + 0x1, 0x21, 0xe6, 0x4a, 0x48, 0xb4, 0xb1, 0x89, + 0xa6, 0x12, 0x54, 0x64, 0x89, 0x49, 0x1e, 0x92, + 0x1, 0x20, 0x83, 0xdf, 0xf4, 0x82, 0x9, 0x4, + 0x0, 0x8, 0x0, 0x10, 0x0, + + /* U+54D9 "哙" */ + 0x0, 0x40, 0x0, 0x83, 0xc2, 0x84, 0x88, 0x89, + 0x20, 0xd3, 0x80, 0xe4, 0xfe, 0x48, 0x0, 0x90, + 0x1, 0x20, 0x2, 0x5f, 0xfc, 0x84, 0xf, 0x11, + 0x10, 0x41, 0x1, 0xff, 0x0, 0x2, + + /* U+54DA "哚" */ + 0x1, 0xf1, 0xe2, 0x22, 0x44, 0x44, 0x88, 0x89, + 0x21, 0x12, 0x43, 0xa5, 0x10, 0x48, 0x20, 0x97, + 0xfd, 0x21, 0x82, 0x45, 0x87, 0x92, 0x88, 0x44, + 0xc1, 0x8, 0x0, 0x10, 0x0, + + /* U+54DC "哜" */ + 0x0, 0x40, 0x0, 0x83, 0xdf, 0xf4, 0x90, 0x49, + 0x11, 0x12, 0x14, 0x24, 0x10, 0x48, 0xd8, 0x9e, + 0xd, 0x22, 0x22, 0x44, 0x47, 0x88, 0x88, 0x21, + 0x0, 0xc2, 0x3, 0x4, 0x0, + + /* U+54DD "哝" */ + 0x0, 0x41, 0xe0, 0x82, 0x5f, 0xf4, 0xa2, 0x29, + 0x44, 0x52, 0x18, 0x24, 0x33, 0x48, 0xbc, 0x93, + 0x21, 0x2a, 0x42, 0x64, 0x47, 0x88, 0x49, 0x12, + 0x40, 0x38, 0x0, 0x40, 0x0, + + /* U+54DE "哞" */ + 0x0, 0x80, 0x1, 0x3, 0xc4, 0x44, 0x88, 0x89, + 0x20, 0x92, 0xff, 0xa4, 0x1, 0x49, 0x20, 0x93, + 0xfd, 0x28, 0x82, 0x51, 0x4, 0x82, 0xf, 0x7f, + 0xf0, 0x8, 0x0, 0x10, 0x0, 0x20, + + /* U+54DF "哟" */ + 0x1, 0x23, 0xc8, 0x89, 0x23, 0xe5, 0x31, 0x95, + 0x46, 0x7c, 0x19, 0x24, 0x64, 0x89, 0x94, 0x26, + 0x5e, 0x19, 0x0, 0x7c, 0x1, 0x81, 0x84, 0x38, + 0x10, 0x7, 0x80, + + /* U+54E5 "哥" */ + 0xff, 0xfe, 0x0, 0x10, 0xfe, 0x21, 0x4, 0x43, + 0xf8, 0x80, 0x1, 0x0, 0x0, 0x3f, 0xfe, 0x3f, + 0x88, 0x81, 0x11, 0x2, 0x21, 0xfc, 0x40, 0x0, + 0x80, 0x1e, 0x0, + + /* U+54E6 "哦" */ + 0x0, 0x23, 0xfc, 0xa9, 0x22, 0x64, 0x88, 0x92, + 0x22, 0x7f, 0xf9, 0x22, 0x24, 0x89, 0x93, 0xaa, + 0x78, 0xaf, 0x21, 0x24, 0x9d, 0x82, 0x94, 0x8, + 0x50, 0xe0, 0xc0, + + /* U+54E7 "哧" */ + 0x0, 0x43, 0xc1, 0x9, 0x7f, 0xa4, 0x10, 0x90, + 0x42, 0x5f, 0xf9, 0x12, 0x24, 0x48, 0x95, 0x2a, + 0x54, 0xa9, 0x92, 0x7c, 0x48, 0x82, 0x20, 0x8, + 0x80, 0x4e, 0x0, + + /* U+54E8 "哨" */ + 0x4, 0x47, 0xc9, 0x29, 0x35, 0xa4, 0x10, 0x97, + 0xfe, 0x50, 0x19, 0x40, 0x65, 0xff, 0x94, 0x6, + 0x50, 0x19, 0x7f, 0xfd, 0x1, 0x94, 0x4, 0x10, + 0x10, 0x43, 0xc0, + + /* U+54E9 "哩" */ + 0xf7, 0xfd, 0x28, 0x8a, 0x51, 0x14, 0xbf, 0xe9, + 0x44, 0x52, 0x88, 0xa5, 0xff, 0x48, 0x20, 0x90, + 0x41, 0x2f, 0xfb, 0xc1, 0x4, 0x2, 0x0, 0xff, + 0xe0, + + /* U+54EA "哪" */ + 0xf, 0xbd, 0xc9, 0x4a, 0x92, 0x95, 0x25, 0x2a, + 0x4a, 0x55, 0xf5, 0x29, 0x2a, 0x52, 0x52, 0xaf, + 0xa3, 0x51, 0x47, 0xa2, 0x8c, 0x45, 0x60, 0x8a, + 0x2, 0x14, 0x4, 0xc8, 0x0, + + /* U+54ED "哭" */ + 0x7c, 0x7c, 0x89, 0x9, 0x12, 0x12, 0x24, 0x27, + 0xc7, 0xc0, 0x4, 0x0, 0x44, 0x7f, 0xff, 0x2, + 0x80, 0x5, 0x0, 0x11, 0x0, 0x41, 0x86, 0x0, + 0xf0, 0x0, 0x0, + + /* U+54EE "哮" */ + 0x0, 0x80, 0x2, 0x1f, 0x7e, 0xa4, 0x24, 0x90, + 0xa2, 0x5f, 0xf9, 0x8, 0x25, 0xfe, 0x94, 0x12, + 0x60, 0x89, 0x4, 0x3d, 0xff, 0x90, 0x40, 0x1, + 0x0, 0x3c, 0x0, + + /* U+54F2 "哲" */ + 0x20, 0x0, 0x87, 0xcf, 0xd0, 0x8, 0x40, 0x2d, + 0xff, 0xc4, 0x22, 0x20, 0x89, 0x82, 0x40, 0x0, + 0xff, 0xe4, 0x0, 0x90, 0x2, 0x40, 0x8, 0xff, + 0xe4, 0x0, 0x80, + + /* U+54F3 "哳" */ + 0x2, 0x1, 0xe4, 0xfa, 0x49, 0x4, 0xba, 0x9, + 0x24, 0x12, 0x4f, 0xe4, 0x92, 0x49, 0xa4, 0x9e, + 0x49, 0x24, 0x92, 0x49, 0x27, 0x92, 0x49, 0x28, + 0x80, 0x51, 0x3, 0xc2, 0x0, + + /* U+54FA "哺" */ + 0x0, 0x49, 0xef, 0xfe, 0x41, 0x4, 0x82, 0x9, + 0x7f, 0xd2, 0x88, 0xa5, 0xff, 0x4a, 0x22, 0x94, + 0x45, 0x28, 0x8b, 0xdf, 0xf4, 0xa2, 0x28, 0x44, + 0x40, 0x8b, 0x80, + + /* U+54FC "哼" */ + 0x0, 0x40, 0x1, 0xf, 0x7f, 0xe4, 0x0, 0x90, + 0x2, 0x5f, 0xe9, 0x40, 0xa5, 0xfe, 0x90, 0x2, + 0x5f, 0xe9, 0x1, 0xa4, 0x8, 0xf0, 0x42, 0x41, + 0x0, 0x4, 0x0, 0xf0, + + /* U+54FD "哽" */ + 0x7, 0xff, 0xe0, 0x82, 0x5f, 0xf4, 0xa2, 0x29, + 0x44, 0x52, 0xff, 0xa5, 0x11, 0x4a, 0x22, 0x97, + 0xfd, 0x28, 0x83, 0xca, 0x4, 0xc, 0x0, 0x67, + 0x1, 0x1, 0x0, + + /* U+54FF "哿" */ + 0x20, 0x0, 0x41, 0xfb, 0xfa, 0x11, 0x14, 0x24, + 0x28, 0x4a, 0x9f, 0xa2, 0x0, 0x7f, 0xff, 0x0, + 0x8, 0x0, 0x11, 0xfe, 0x22, 0x4, 0x44, 0x8, + 0x8f, 0xf1, 0x10, 0x2, 0x0, 0x78, + + /* U+5501 "唁" */ + 0x0, 0x41, 0xe0, 0x42, 0x5f, 0xfc, 0x80, 0x9, + 0x3f, 0xd2, 0x0, 0x24, 0x0, 0x49, 0xfe, 0x90, + 0x1, 0x27, 0xfa, 0x48, 0x17, 0x90, 0x29, 0x20, + 0x40, 0x7f, 0x80, 0x81, 0x0, + + /* U+5506 "唆" */ + 0x0, 0x40, 0x1, 0x83, 0xc6, 0x44, 0x98, 0x49, + 0x7f, 0xd2, 0x0, 0x64, 0x46, 0x4b, 0x2, 0x90, + 0x81, 0x23, 0xf2, 0x4c, 0x27, 0xa8, 0x89, 0xa, + 0x0, 0xc, 0x0, 0xe7, 0x6, 0x1, + + /* U+5507 "唇" */ + 0x3f, 0xff, 0x20, 0x0, 0x20, 0x0, 0x2f, 0xfc, + 0x20, 0x0, 0x3f, 0xff, 0x24, 0x80, 0x24, 0x58, + 0x25, 0xa0, 0x66, 0x1f, 0x4f, 0xfc, 0x50, 0x4, + 0x10, 0x4, 0xf, 0xfc, 0x10, 0x4, + + /* U+5509 "唉" */ + 0x0, 0x81, 0xe1, 0x2, 0x44, 0x44, 0x90, 0x49, + 0x7f, 0xd2, 0x0, 0xa4, 0xff, 0x4a, 0x20, 0x90, + 0x41, 0x20, 0x82, 0x5f, 0xf7, 0x85, 0x8, 0x1b, + 0x0, 0xc3, 0x2, 0x1, 0x0, + + /* U+550F "唏" */ + 0x6, 0x9, 0xe3, 0xe2, 0x47, 0xc4, 0xb0, 0x69, + 0x8, 0x12, 0xff, 0xe4, 0x40, 0x48, 0xa0, 0x92, + 0x41, 0x2f, 0xfa, 0x69, 0x17, 0x92, 0x29, 0x24, + 0x40, 0x4b, 0x80, 0x10, 0x0, + + /* U+5510 "唐" */ + 0x0, 0x80, 0x0, 0x80, 0x3f, 0xff, 0x20, 0x0, + 0x20, 0x80, 0x2f, 0xfc, 0x20, 0x84, 0x2f, 0xff, + 0x20, 0x84, 0x2f, 0xfc, 0x20, 0x80, 0x2f, 0xfc, + 0x28, 0x4, 0x48, 0x4, 0x4f, 0xfc, 0x8, 0x4, + + /* U+5511 "唑" */ + 0x0, 0x41, 0xe4, 0x92, 0x49, 0x24, 0x92, 0x49, + 0x65, 0x92, 0xaa, 0xa6, 0x38, 0xcc, 0x20, 0x90, + 0x41, 0x2f, 0xfa, 0x41, 0x7, 0x82, 0x8, 0x4, + 0x1, 0xff, 0xc0, + + /* U+5514 "唔" */ + 0x7, 0xff, 0xc2, 0x9, 0x7f, 0xa4, 0x42, 0x91, + 0xa, 0x44, 0x29, 0x7f, 0xe4, 0x0, 0x90, 0x2, + 0x4f, 0xef, 0x20, 0xa4, 0x82, 0x3, 0xf8, 0x8, + 0x20, + + /* U+551B "唛" */ + 0x0, 0x40, 0x1, 0xf, 0x7f, 0xe4, 0x10, 0x90, + 0x42, 0x5f, 0xf9, 0x4, 0x25, 0xff, 0x90, 0x82, + 0x44, 0x9, 0x3f, 0xa5, 0xc2, 0xf0, 0xb2, 0x3, + 0x80, 0x71, 0xc2, 0x0, + + /* U+5520 "唠" */ + 0x1, 0x10, 0x4, 0x4f, 0x7f, 0xe4, 0x44, 0x90, + 0x2, 0x5f, 0xf9, 0x48, 0x65, 0x21, 0x97, 0xfa, + 0x42, 0x29, 0x8, 0xbc, 0x42, 0x81, 0x8, 0x8, + 0x20, 0xcf, 0x0, + + /* U+5522 "唢" */ + 0x0, 0x40, 0x8, 0x83, 0xd1, 0x14, 0x92, 0x49, + 0x4, 0x12, 0xff, 0xa5, 0x1, 0x4a, 0x22, 0x94, + 0x45, 0x28, 0x8a, 0x51, 0x14, 0xa2, 0x2f, 0xe, + 0x10, 0x23, 0x1, 0x81, 0x80, 0x0, + + /* U+5523 "唣" */ + 0x0, 0x40, 0x2, 0xf, 0x3f, 0xa4, 0x82, 0x93, + 0xfa, 0x48, 0x29, 0x20, 0xa4, 0xfe, 0x90, 0x2, + 0x42, 0xf, 0x7f, 0xe4, 0x20, 0x90, 0x84, 0x2, + 0x20, 0xf, 0x80, + + /* U+5524 "唤" */ + 0x0, 0x80, 0x4, 0xf, 0x1f, 0xa4, 0x84, 0x94, + 0x12, 0x50, 0x89, 0x7f, 0xa5, 0x12, 0x94, 0x4a, + 0x51, 0x29, 0x44, 0xa4, 0xfd, 0xf0, 0xa2, 0x4, + 0x40, 0x60, 0xc2, 0x0, + + /* U+5527 "唧" */ + 0xf7, 0xbe, 0x52, 0x99, 0x4a, 0x65, 0xe9, 0x94, + 0xa6, 0x52, 0x99, 0x4a, 0x65, 0xe9, 0x94, 0x26, + 0x54, 0x9f, 0x5b, 0xa5, 0xa8, 0x94, 0x20, 0x0, + 0x80, + + /* U+552A "唪" */ + 0x0, 0x80, 0x2, 0xf, 0x7f, 0xa4, 0x20, 0x97, + 0xfa, 0x44, 0x9, 0xff, 0xe4, 0x88, 0x92, 0x92, + 0x5f, 0xf9, 0x88, 0x3d, 0xfe, 0x90, 0x82, 0x42, + 0x0, 0x8, 0x0, + + /* U+552C "唬" */ + 0x0, 0x80, 0x3, 0xff, 0x8, 0x25, 0xff, 0x94, + 0x86, 0x5f, 0x99, 0x48, 0x65, 0x3e, 0x94, 0x2, + 0x57, 0x8f, 0x52, 0x21, 0x49, 0x9, 0x24, 0x28, + 0x90, 0xa3, 0x80, + + /* U+552E "售" */ + 0x11, 0x0, 0x21, 0x0, 0xff, 0xfb, 0x4, 0xb, + 0xff, 0xd4, 0x10, 0xf, 0xff, 0x10, 0x40, 0x3f, + 0xfe, 0x0, 0x0, 0xff, 0xf1, 0x0, 0x22, 0x0, + 0x47, 0xff, 0x88, 0x1, 0x0, + + /* U+552F "唯" */ + 0x1, 0x40, 0x2, 0x43, 0xc8, 0x4, 0x9f, 0xe9, + 0x62, 0x13, 0xc4, 0x24, 0xff, 0x49, 0x10, 0x92, + 0x21, 0x24, 0x42, 0x4f, 0xf7, 0x91, 0x9, 0x22, + 0x0, 0x7f, 0xc0, 0x80, 0x0, + + /* U+5530 "唰" */ + 0x0, 0x7, 0xbf, 0x5a, 0x85, 0x6a, 0x15, 0xaf, + 0xd6, 0xa0, 0x5a, 0x91, 0x6b, 0xf5, 0xad, 0x56, + 0xb5, 0x5a, 0xd5, 0x7f, 0x55, 0x15, 0xc4, 0x44, + 0x12, 0x11, 0xc0, + + /* U+5531 "唱" */ + 0x3, 0xfb, 0xc8, 0x29, 0x20, 0xa4, 0xfe, 0x92, + 0xa, 0x4f, 0xe9, 0x0, 0x24, 0x0, 0x97, 0xfe, + 0x50, 0x1f, 0x7f, 0xe5, 0x1, 0x84, 0x4, 0x1f, + 0xf0, 0x40, 0x40, + + /* U+5533 "唳" */ + 0x0, 0x40, 0x0, 0x83, 0xdf, 0xf4, 0xa0, 0x29, + 0x40, 0x52, 0x80, 0xa5, 0xff, 0x4a, 0x20, 0x94, + 0x49, 0x2f, 0xfa, 0x51, 0x7, 0xe5, 0x8, 0x8a, + 0x1, 0x23, 0x5, 0x81, 0x80, 0x0, + + /* U+5537 "唷" */ + 0x0, 0x0, 0x0, 0x83, 0xdf, 0xfc, 0x84, 0x9, + 0x31, 0x92, 0xff, 0xa4, 0x0, 0xc8, 0x0, 0x97, + 0xfd, 0x28, 0xa, 0x5f, 0xf4, 0xa0, 0x2f, 0x7f, + 0xd2, 0x80, 0x81, 0x1, 0x2, 0x1e, + + /* U+553C "唼" */ + 0x0, 0x40, 0x1, 0xf, 0x7f, 0xe4, 0x82, 0x91, + 0x92, 0x5f, 0xf9, 0x0, 0x24, 0x20, 0x97, 0xfe, + 0x44, 0x49, 0x21, 0x3c, 0xc8, 0x80, 0xe0, 0xe, + 0x60, 0xc0, 0x40, + + /* U+553E "唾" */ + 0x7, 0xfd, 0xe0, 0x82, 0x5f, 0xf4, 0x92, 0x49, + 0x24, 0x92, 0x49, 0x25, 0xff, 0xc9, 0x24, 0x92, + 0x49, 0x24, 0x93, 0xd7, 0xd4, 0x82, 0x0, 0x7f, + 0xc0, + + /* U+553F "唿" */ + 0x2, 0x0, 0x4, 0x3, 0xdf, 0xf4, 0xa5, 0x29, + 0x12, 0x52, 0x24, 0xa4, 0x91, 0x4a, 0x42, 0x90, + 0x39, 0x21, 0x2, 0x49, 0x27, 0xd2, 0x28, 0xa1, + 0x41, 0x42, 0x0, 0xf8, 0x0, + + /* U+5541 "啁" */ + 0x7, 0xff, 0xd1, 0x19, 0x5f, 0x65, 0x11, 0x94, + 0x46, 0x57, 0xd9, 0x40, 0x65, 0x7d, 0x95, 0x17, + 0xd4, 0x58, 0x5f, 0x61, 0x1, 0x8, 0x4, 0x20, + 0xf0, + + /* U+5543 "啃" */ + 0x2, 0x43, 0xc9, 0xf9, 0x24, 0x24, 0x90, 0x9f, + 0xfe, 0x40, 0x9, 0x7f, 0xe5, 0x1, 0x94, 0x6, + 0x5f, 0xf9, 0x40, 0x7d, 0xff, 0x94, 0x4, 0x10, + 0x10, 0x47, 0xc0, + + /* U+5544 "啄" */ + 0xf7, 0xfd, 0x20, 0x82, 0x46, 0x4, 0x94, 0x49, + 0x49, 0x12, 0x1e, 0x24, 0xd4, 0x4b, 0x28, 0x90, + 0xc9, 0x22, 0x93, 0xc9, 0x14, 0x62, 0x10, 0x4, + 0x0, 0x78, 0x0, + + /* U+5546 "商" */ + 0x1, 0x0, 0x1, 0x3, 0xff, 0xf8, 0x40, 0x0, + 0x42, 0xf, 0xff, 0x90, 0x91, 0x26, 0x1a, 0x57, + 0xe4, 0x88, 0x49, 0x10, 0x92, 0x21, 0x24, 0x7e, + 0x48, 0x0, 0x90, 0x7, 0x0, + + /* U+5549 "啉" */ + 0x2, 0x21, 0xe4, 0x42, 0x48, 0x84, 0xbb, 0xe9, + 0x23, 0x12, 0x46, 0x25, 0xdc, 0x4b, 0x74, 0x96, + 0x69, 0x35, 0x4a, 0x6a, 0x94, 0xd9, 0x1f, 0x22, + 0x12, 0x44, 0x0, 0x88, 0x0, + + /* U+554A "啊" */ + 0xf, 0x1, 0xd3, 0xfe, 0xa4, 0x15, 0x50, 0x2a, + 0xa7, 0x55, 0x52, 0xaa, 0xa5, 0x55, 0x4a, 0xa9, + 0x95, 0x53, 0x2a, 0xa5, 0x97, 0x78, 0x2a, 0x80, + 0x41, 0x0, 0x82, 0xf, 0x0, + + /* U+5550 "啐" */ + 0x0, 0x41, 0xe0, 0x82, 0x5f, 0xf4, 0x80, 0x9, + 0x11, 0x12, 0x62, 0x24, 0xc6, 0x4a, 0x52, 0x94, + 0x5, 0x20, 0x82, 0x5f, 0xff, 0x82, 0x9, 0x4, + 0x10, 0x8, 0x0, 0x10, 0x0, + + /* U+5555 "啕" */ + 0x2, 0x0, 0x8, 0xf, 0x7f, 0xe5, 0x1, 0x93, + 0xe6, 0x52, 0x19, 0x48, 0x64, 0x21, 0x9f, 0xf6, + 0x52, 0x9f, 0x4a, 0x65, 0xf9, 0x90, 0x4, 0x0, + 0x30, 0xf, 0x80, + + /* U+5556 "啖" */ + 0x0, 0x40, 0x8, 0x83, 0xc9, 0x34, 0x92, 0xc9, + 0xc, 0x12, 0x16, 0x24, 0xc2, 0x4a, 0x22, 0x90, + 0x41, 0x28, 0x8a, 0x49, 0x24, 0x85, 0xf, 0xa, + 0x10, 0x22, 0x1, 0x83, 0x0, 0x0, + + /* U+555C "啜" */ + 0x7, 0xfd, 0xe1, 0xa, 0x54, 0x14, 0x99, 0x49, + 0x31, 0x92, 0x94, 0xa4, 0x10, 0x4b, 0xbe, 0x91, + 0x45, 0x2a, 0x4b, 0xcc, 0xa4, 0x90, 0xc8, 0x31, + 0x80, 0x8c, 0x82, 0x20, 0x0, + + /* U+5561 "啡" */ + 0x1, 0x20, 0x4, 0x8f, 0x12, 0x27, 0xcf, 0x91, + 0x22, 0x44, 0x89, 0xf3, 0xe4, 0x48, 0x91, 0x22, + 0x44, 0x89, 0xf3, 0xfc, 0x48, 0x91, 0x20, 0x4, + 0x80, 0x12, 0x0, + + /* U+5564 "啤" */ + 0x0, 0x40, 0x1, 0x3, 0xdf, 0xf4, 0xa2, 0x29, + 0x44, 0x52, 0x88, 0xa5, 0xff, 0x4a, 0x22, 0x94, + 0x45, 0x2f, 0xfa, 0x46, 0x4, 0x91, 0xf, 0xff, + 0xf2, 0x4, 0x0, 0x8, 0x0, 0x10, + + /* U+5565 "啥" */ + 0x0, 0x40, 0x0, 0xc3, 0xc2, 0x84, 0x88, 0x89, + 0x20, 0xd3, 0xfe, 0xa4, 0x10, 0x48, 0x20, 0x97, + 0xfd, 0x20, 0x82, 0x41, 0x4, 0xbd, 0xcf, 0x40, + 0x92, 0x81, 0x1, 0xfe, 0x2, 0x4, + + /* U+5566 "啦" */ + 0x2, 0x11, 0xe4, 0x22, 0x48, 0x44, 0xfb, 0xe9, + 0x20, 0x12, 0x48, 0xa4, 0x92, 0x49, 0xe4, 0x9e, + 0x49, 0x24, 0x53, 0xc8, 0xa4, 0x90, 0x89, 0x21, + 0x0, 0x5f, 0xc3, 0x80, 0x0, + + /* U+5567 "啧" */ + 0x0, 0x43, 0xdf, 0xf9, 0x4, 0x24, 0xfe, 0x90, + 0x42, 0x5f, 0xf9, 0x0, 0x25, 0xfe, 0x94, 0xa, + 0x51, 0x29, 0x44, 0xbd, 0x12, 0x80, 0xb0, 0xc, + 0x30, 0xc0, 0x40, + + /* U+556A "啪" */ + 0x2, 0x23, 0x88, 0x8a, 0x27, 0xeb, 0xd1, 0xa2, + 0x46, 0x89, 0x1a, 0x24, 0x69, 0xdf, 0xaa, 0x46, + 0x89, 0x1e, 0x24, 0x68, 0x91, 0x82, 0x44, 0x9, + 0xf0, 0xc4, 0x40, + + /* U+556C "啬" */ + 0x1, 0x1, 0xff, 0xfc, 0x84, 0x20, 0x88, 0x80, + 0x12, 0x1f, 0xff, 0xc0, 0x0, 0x3f, 0xfe, 0x40, + 0x4, 0x9f, 0xc9, 0x20, 0x92, 0x7f, 0x24, 0x0, + 0x4f, 0xff, 0x90, 0x1, 0x0, + + /* U+556D "啭" */ + 0x4, 0x11, 0xc8, 0x22, 0xbf, 0xf5, 0x41, 0xa, + 0xa2, 0x15, 0x4f, 0xea, 0x88, 0x57, 0xd0, 0xa2, + 0x41, 0x45, 0x7b, 0xbc, 0x25, 0x13, 0x48, 0x23, + 0x0, 0x43, 0x0, 0x81, 0x0, + + /* U+556E "啮" */ + 0x0, 0x20, 0x4, 0x43, 0xc8, 0xf4, 0x91, 0x9, + 0x22, 0x12, 0xff, 0xe4, 0x0, 0x4a, 0x22, 0x94, + 0x65, 0x29, 0x4a, 0x52, 0x57, 0xb8, 0x29, 0x40, + 0x40, 0xff, 0x80, 0x1, 0x0, + + /* U+5575 "啵" */ + 0x8, 0x21, 0xc8, 0x42, 0x87, 0xf5, 0x9, 0x2a, + 0x12, 0x55, 0xa4, 0x28, 0x7f, 0x50, 0xc4, 0xa1, + 0x89, 0x52, 0x92, 0xa5, 0x47, 0x51, 0x88, 0xa3, + 0x2, 0x59, 0x80, 0x40, 0x0, + + /* U+5576 "啶" */ + 0x0, 0x40, 0x1, 0xf, 0x7f, 0xe5, 0x1, 0x94, + 0x6, 0x5f, 0xf9, 0x4, 0x24, 0x10, 0x92, 0x42, + 0x49, 0xf9, 0x44, 0x3d, 0x90, 0x86, 0x40, 0x27, + 0x0, 0x8f, 0xc0, + + /* U+5577 "啷" */ + 0x2, 0x0, 0x4, 0x7b, 0xde, 0x94, 0xa5, 0x29, + 0x4a, 0x52, 0xf5, 0x25, 0x2a, 0x4a, 0x52, 0x97, + 0xa3, 0x28, 0x46, 0x54, 0x8f, 0xa5, 0x19, 0x5a, + 0xc0, 0xc4, 0x0, 0x8, 0x0, + + /* U+5578 "啸" */ + 0x0, 0x41, 0xef, 0xfa, 0x41, 0x14, 0x82, 0x29, + 0x7f, 0xf2, 0x8, 0xa5, 0xff, 0x48, 0x20, 0x90, + 0x41, 0x2a, 0x8a, 0x55, 0x57, 0xb2, 0xa8, 0xa4, + 0x41, 0x8, 0x86, 0x11, 0x0, + + /* U+557B "啻" */ + 0x1, 0x0, 0x2, 0x1, 0xff, 0xf0, 0x81, 0x0, + 0x82, 0x1f, 0xff, 0xe0, 0x40, 0x9f, 0xfc, 0x21, + 0x8, 0x42, 0x70, 0x4, 0x1, 0xff, 0xc4, 0x0, + 0x88, 0x1, 0xf, 0xfe, 0x20, 0x4, + + /* U+557C "啼" */ + 0x0, 0x40, 0x0, 0x83, 0xdf, 0xfc, 0x90, 0x49, + 0x11, 0x12, 0xff, 0xe6, 0x0, 0xc8, 0x21, 0x97, + 0xfd, 0x28, 0x8b, 0xd1, 0x14, 0xa2, 0x29, 0x44, + 0x40, 0x8b, 0x80, 0x10, 0x0, + + /* U+557E "啾" */ + 0x7, 0x19, 0xe4, 0x32, 0x48, 0x64, 0x92, 0xd9, + 0x7d, 0xb2, 0x4b, 0xa4, 0x87, 0x4b, 0x8c, 0x96, + 0x99, 0x34, 0x72, 0x68, 0x97, 0x91, 0x29, 0x26, + 0x60, 0x58, 0x40, 0xa0, 0x0, + + /* U+5580 "喀" */ + 0x0, 0x40, 0x0, 0x83, 0xdf, 0xf4, 0xa0, 0x29, + 0x10, 0x12, 0x3f, 0x24, 0xc2, 0x4a, 0x48, 0x90, + 0x61, 0x23, 0x72, 0x58, 0x14, 0x9f, 0xcf, 0x20, + 0x90, 0x41, 0x0, 0xfe, 0x1, 0x4, + + /* U+5581 "喁" */ + 0x3, 0xfb, 0xd1, 0x29, 0x3f, 0xa5, 0x12, 0x94, + 0x4a, 0x4f, 0xe9, 0x4, 0x25, 0xff, 0x94, 0x46, + 0x51, 0x9f, 0x7f, 0x65, 0x5, 0x4, 0x4, 0x10, + 0xe0, + + /* U+5582 "喂" */ + 0x7, 0xfd, 0xe8, 0x8a, 0x51, 0x14, 0xbf, 0xe9, + 0x44, 0x52, 0x88, 0xa5, 0xff, 0x48, 0x0, 0x9f, + 0xff, 0x29, 0x2, 0x51, 0x37, 0xa1, 0x89, 0x41, + 0x0, 0xf1, 0x81, 0x0, 0x0, + + /* U+5583 "喃" */ + 0x0, 0x40, 0x1, 0x0, 0x4, 0x3d, 0xff, 0x90, + 0x42, 0x5f, 0xf9, 0x40, 0x65, 0x45, 0x94, 0xa6, + 0x57, 0xd9, 0x44, 0x65, 0x7d, 0xf4, 0x46, 0x51, + 0x10, 0x44, 0x41, 0x17, + + /* U+5584 "善" */ + 0x8, 0x20, 0x8, 0x81, 0xff, 0xf0, 0x8, 0x7, + 0xff, 0xc0, 0x20, 0x3f, 0xff, 0x88, 0x88, 0xff, + 0xfe, 0x0, 0x0, 0x0, 0x1, 0xff, 0xc2, 0x0, + 0x87, 0xff, 0x8, 0x2, 0x0, + + /* U+5587 "喇" */ + 0x2, 0x4, 0x8, 0x1e, 0xfd, 0x68, 0x85, 0xa2, + 0x16, 0xbf, 0x5a, 0xa5, 0x6a, 0x95, 0xaf, 0xd6, + 0x88, 0x5a, 0x51, 0x79, 0xb5, 0x9a, 0x4, 0x8, + 0x10, 0x21, 0xc0, + + /* U+5588 "喈" */ + 0x4, 0x43, 0xd1, 0x39, 0x7d, 0x25, 0x18, 0x94, + 0x46, 0x57, 0x19, 0x67, 0xa4, 0x20, 0x97, 0xfa, + 0x50, 0x29, 0x40, 0xbd, 0xfe, 0x94, 0x8, 0x1f, + 0xe0, 0x40, 0x80, + + /* U+5589 "喉" */ + 0x2, 0x0, 0x5, 0xf3, 0xc8, 0x24, 0xa0, 0x49, + 0x40, 0x92, 0xbf, 0xa7, 0x20, 0x4a, 0x7e, 0x95, + 0x21, 0x28, 0x42, 0x50, 0x84, 0xaf, 0xef, 0x45, + 0x12, 0x8a, 0x1, 0x23, 0x2, 0x80, + + /* U+558A "喊" */ + 0x0, 0x0, 0x0, 0x53, 0xc0, 0x94, 0xbf, 0xf9, + 0x82, 0x13, 0x4, 0x26, 0xf9, 0x4c, 0x12, 0x9b, + 0xe9, 0x34, 0xb2, 0x69, 0x64, 0xd2, 0x8e, 0xbd, + 0x51, 0x6, 0x82, 0x13, 0x8, 0x46, + + /* U+558B "喋" */ + 0x0, 0x90, 0x12, 0x4f, 0xff, 0xe5, 0x24, 0x94, + 0x92, 0x53, 0xc9, 0x40, 0x25, 0xff, 0x90, 0x82, + 0x42, 0x9, 0x7f, 0xfc, 0x68, 0x82, 0x90, 0x32, + 0x30, 0x8, 0x0, + + /* U+558F "喏" */ + 0x2, 0x13, 0xc8, 0x49, 0x7f, 0xe4, 0x84, 0x90, + 0x82, 0x42, 0x9, 0x7f, 0xe4, 0x40, 0x92, 0x2, + 0x4f, 0xe9, 0x60, 0xa6, 0x82, 0xf2, 0x8, 0xf, + 0xe0, 0x20, 0x80, + + /* U+5591 "喑" */ + 0x0, 0x40, 0x0, 0x83, 0xdf, 0xf4, 0x80, 0x49, + 0x11, 0x12, 0xff, 0xe4, 0x0, 0x48, 0x0, 0x93, + 0xf9, 0x28, 0x13, 0xcf, 0xe4, 0xa0, 0x48, 0x40, + 0x80, 0x7f, 0x1, 0x2, 0x0, + + /* U+5594 "喔" */ + 0x7, 0xfd, 0xe8, 0xa, 0x5f, 0xf4, 0xa0, 0x9, + 0x5f, 0xf2, 0x88, 0x25, 0x22, 0x4a, 0xfe, 0x94, + 0x21, 0x28, 0x43, 0xd7, 0xf4, 0x41, 0x0, 0xbf, + 0xe0, 0x0, 0x0, + + /* U+5598 "喘" */ + 0x0, 0x41, 0xe8, 0x8a, 0x51, 0x14, 0xbf, 0xe9, + 0x0, 0x12, 0x0, 0x25, 0xff, 0xc8, 0x40, 0x97, + 0xfd, 0x29, 0x4a, 0x52, 0x97, 0xa5, 0x29, 0x4a, + 0x40, 0x94, 0x81, 0x7, 0x0, + + /* U+5599 "喙" */ + 0x1, 0x0, 0x4, 0x3, 0xcf, 0xe4, 0x90, 0x49, + 0x1f, 0x92, 0xff, 0xe4, 0x30, 0x4b, 0xc4, 0x90, + 0xf1, 0x26, 0x52, 0x71, 0xa7, 0x85, 0x48, 0x32, + 0x51, 0x84, 0x40, 0x70, 0x0, + + /* U+559C "喜" */ + 0x1, 0x1, 0xff, 0xfc, 0x4, 0x3, 0xff, 0xe0, + 0x0, 0x7, 0xff, 0x8, 0x2, 0x1f, 0xfc, 0x8, + 0x21, 0xff, 0xfc, 0x0, 0x1, 0xff, 0xc2, 0x0, + 0x87, 0xff, 0x8, 0x2, 0x0, + + /* U+559D "喝" */ + 0x7, 0xfb, 0xd0, 0x29, 0x40, 0xa5, 0xfe, 0x94, + 0xa, 0x5f, 0xe9, 0x20, 0x24, 0xff, 0x94, 0x46, + 0x73, 0x1f, 0x52, 0x65, 0x1, 0x87, 0xe4, 0x0, + 0x20, 0x3, 0x80, + + /* U+559F "喟" */ + 0x7, 0xfc, 0x11, 0x1f, 0x44, 0x65, 0xff, 0x94, + 0x46, 0x5f, 0xf9, 0x0, 0x24, 0xfe, 0x92, 0xa, + 0x4f, 0xe9, 0x20, 0xbc, 0xfe, 0x92, 0x8, 0x8, + 0x20, 0x23, 0x80, + + /* U+55A7 "喧" */ + 0x0, 0x40, 0x0, 0x83, 0xdf, 0xfc, 0x80, 0x19, + 0x7f, 0xd2, 0x0, 0x25, 0xff, 0x4a, 0x2, 0x97, + 0xfd, 0x28, 0xa, 0x50, 0x17, 0xbf, 0xe8, 0x0, + 0x11, 0xff, 0xc0, + + /* U+55B1 "喱" */ + 0x7, 0xfd, 0xe8, 0x2, 0x50, 0x4, 0xaf, 0xe9, + 0x52, 0x52, 0xa4, 0xa5, 0x7f, 0x4a, 0x92, 0x95, + 0xfd, 0x28, 0x42, 0x50, 0x87, 0xaf, 0xe8, 0x82, + 0x1, 0x4, 0x2, 0xff, 0x80, 0x0, + + /* U+55B3 "喳" */ + 0x0, 0x40, 0x1, 0xf, 0x7f, 0xe4, 0x38, 0x93, + 0x5a, 0x61, 0x19, 0x7f, 0xa5, 0x2, 0x94, 0xa, + 0x5f, 0xe9, 0x40, 0xbd, 0xfe, 0x90, 0x0, 0x3f, + 0xf0, + + /* U+55B5 "喵" */ + 0x1, 0x10, 0x2, 0x23, 0xdf, 0xfc, 0x88, 0x89, + 0x0, 0x12, 0xff, 0xa5, 0x11, 0x4a, 0x22, 0x94, + 0x45, 0x2f, 0xfa, 0x51, 0x17, 0xa2, 0x29, 0x44, + 0x40, 0xff, 0x81, 0x1, 0x0, + + /* U+55B7 "喷" */ + 0x0, 0x0, 0x2, 0xf, 0x7f, 0xa4, 0x20, 0x92, + 0x12, 0x7f, 0xf9, 0x21, 0x24, 0x0, 0x97, 0xfa, + 0x50, 0x29, 0x40, 0xa5, 0x22, 0xf4, 0x8a, 0x7, + 0x80, 0x61, 0x82, 0x0, + + /* U+55B9 "喹" */ + 0x0, 0x80, 0x2, 0xf, 0x7f, 0xe4, 0x48, 0x92, + 0x9a, 0x7f, 0xd9, 0x8, 0x24, 0x20, 0x97, 0xfe, + 0x40, 0x9, 0x8, 0x3d, 0xfe, 0x90, 0x80, 0x3f, + 0xf0, + + /* U+55BB "喻" */ + 0x0, 0x40, 0x1, 0x43, 0xc2, 0xc4, 0x88, 0xc9, + 0x7f, 0x72, 0x0, 0x25, 0xe1, 0x4a, 0x52, 0x94, + 0xa5, 0x2f, 0x4a, 0x52, 0x97, 0xbd, 0x29, 0x48, + 0x40, 0x90, 0x81, 0xe7, 0x0, + + /* U+55BD "喽" */ + 0x0, 0x40, 0x4, 0x93, 0xc5, 0x44, 0xbf, 0xe9, + 0xe, 0x12, 0x2a, 0x25, 0x93, 0xc8, 0x40, 0x90, + 0x81, 0x3f, 0xfe, 0x4c, 0x47, 0x99, 0x88, 0xe, + 0x0, 0x73, 0x3, 0x1, 0x0, + + /* U+55BE "喾" */ + 0x21, 0x8, 0x42, 0x20, 0x40, 0x47, 0xff, 0xf9, + 0x10, 0x33, 0xff, 0x48, 0x40, 0x0, 0x80, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xe1, 0x0, 0x42, 0x0, + 0x87, 0xff, 0x8, 0x2, 0x0, + + /* U+55C4 "嗄" */ + 0xf, 0xff, 0xc2, 0x9, 0x7f, 0xa5, 0x2, 0x97, + 0xfa, 0x50, 0x29, 0x7f, 0xa5, 0x2, 0x97, 0xfa, + 0x48, 0x9, 0x3f, 0xfd, 0x86, 0x89, 0xc0, 0x1c, + 0xf0, 0x80, 0x0, + + /* U+55C5 "嗅" */ + 0x0, 0x0, 0x2, 0xf, 0x7f, 0xa5, 0x2, 0x97, + 0xfa, 0x50, 0x29, 0x7f, 0xa5, 0x2, 0x97, 0xfa, + 0x40, 0x9, 0x5, 0x27, 0xff, 0xf0, 0xc2, 0x2, + 0x80, 0x31, 0x82, 0x1, + + /* U+55C9 "嗉" */ + 0x0, 0x83, 0xdf, 0xf9, 0x8, 0x25, 0xfe, 0x90, + 0x82, 0x7f, 0xf9, 0x18, 0x24, 0x84, 0x97, 0xe2, + 0x44, 0x49, 0x7f, 0xbc, 0x10, 0x86, 0x58, 0x21, + 0x10, 0x38, 0x0, + + /* U+55CC "嗌" */ + 0x2, 0x13, 0xc8, 0x49, 0x12, 0x25, 0xff, 0x90, + 0x2, 0x44, 0x49, 0x60, 0xa4, 0x1, 0x97, 0xfa, + 0x54, 0xa9, 0x52, 0xbd, 0x4a, 0x95, 0x28, 0x3f, + 0xf0, + + /* U+55CD "嗍" */ + 0x8, 0x80, 0x14, 0xfe, 0x52, 0x6b, 0xe9, 0xa2, + 0x26, 0xaa, 0xfa, 0xaa, 0x6a, 0xa9, 0xaa, 0xa6, + 0xbe, 0xfa, 0x22, 0x78, 0x89, 0x84, 0x64, 0x11, + 0x10, 0x8d, 0xc0, + + /* U+55D1 "嗑" */ + 0x0, 0x83, 0xdf, 0xe9, 0x8, 0x24, 0x20, 0x97, + 0xfe, 0x44, 0x9, 0x21, 0x25, 0xfe, 0x90, 0x2, + 0x5f, 0xe9, 0x52, 0xbd, 0x4a, 0x85, 0x28, 0x14, + 0xa0, 0xff, 0xc0, + + /* U+55D2 "嗒" */ + 0x1, 0x10, 0x4, 0x4f, 0x7f, 0xe4, 0x54, 0x90, + 0xc2, 0x46, 0xc9, 0x30, 0xa5, 0x7d, 0x90, 0x2, + 0x40, 0x9, 0x3f, 0xbc, 0x82, 0x92, 0x8, 0xf, + 0xe0, 0x20, 0x80, + + /* U+55D3 "嗓" */ + 0x7, 0xfd, 0xe4, 0x12, 0x47, 0xc4, 0x8f, 0xc9, + 0x20, 0x52, 0xf7, 0xe4, 0x21, 0x4b, 0x9a, 0x93, + 0x1d, 0x29, 0x46, 0x5f, 0xff, 0x8a, 0x88, 0x24, + 0x81, 0x88, 0xc0, 0x10, 0x0, + + /* U+55D4 "嗔" */ + 0x0, 0x43, 0xdf, 0xf9, 0x4, 0x25, 0xfe, 0x94, + 0xa, 0x5f, 0xe9, 0x40, 0xa5, 0xfe, 0x94, 0xa, + 0x5f, 0xe9, 0x40, 0xa7, 0xff, 0xf0, 0x2, 0xc, + 0xe0, 0xc0, 0x40, + + /* U+55D6 "嗖" */ + 0x1, 0x41, 0xec, 0xba, 0x51, 0x14, 0xa2, 0x29, + 0x75, 0xd2, 0x88, 0xa5, 0xff, 0x48, 0x20, 0x90, + 0x41, 0x2f, 0xf2, 0x48, 0x27, 0x88, 0x88, 0xe, + 0x0, 0xe7, 0xc2, 0x0, 0x0, + + /* U+55DC "嗜" */ + 0x0, 0x81, 0xef, 0xd2, 0x42, 0x44, 0xff, 0xf9, + 0x8, 0x12, 0xfe, 0xa4, 0xc1, 0x49, 0xfc, 0x90, + 0x1, 0x2f, 0xf2, 0x58, 0x27, 0xbf, 0xc9, 0x60, + 0x80, 0xff, 0x1, 0x82, 0x0, + + /* U+55DD "嗝" */ + 0x7, 0xff, 0xc0, 0x9, 0x3f, 0xa4, 0x82, 0x93, + 0xfa, 0x40, 0x9, 0x7f, 0xe5, 0x45, 0x95, 0x26, + 0x50, 0x9f, 0x5f, 0x65, 0x11, 0x4, 0x44, 0x11, + 0x70, + + /* U+55DF "嗟" */ + 0x2, 0x13, 0xc4, 0x89, 0x7f, 0xe4, 0x20, 0x90, + 0x82, 0x5f, 0xe9, 0x10, 0x27, 0xff, 0x91, 0x2, + 0x48, 0xf, 0x5f, 0xa3, 0x10, 0x18, 0x40, 0x1f, + 0xf0, + + /* U+55E1 "嗡" */ + 0x0, 0x0, 0x1, 0x23, 0xc4, 0x44, 0x92, 0x69, + 0x4a, 0x32, 0x22, 0x24, 0xfc, 0x48, 0x4, 0x97, + 0xfd, 0x21, 0xa, 0x52, 0x94, 0x84, 0x2f, 0x39, + 0xd1, 0x94, 0x80, 0x21, 0x3, 0xde, + + /* U+55E3 "嗣" */ + 0x7c, 0xfe, 0x10, 0x18, 0x40, 0x5f, 0x3d, 0x0, + 0x7, 0xfb, 0xda, 0xa9, 0x6a, 0xa5, 0xaa, 0x97, + 0xfa, 0x5a, 0xaf, 0x6a, 0x81, 0x82, 0x6, 0x18, + 0xf0, + + /* U+55E4 "嗤" */ + 0x0, 0x40, 0x11, 0x2f, 0x44, 0xa5, 0xfe, 0x90, + 0x42, 0x7f, 0xf9, 0x4, 0x25, 0xfe, 0x94, 0x4a, + 0x51, 0x29, 0x7f, 0xbd, 0x10, 0x80, 0x48, 0x3f, + 0xf0, 0x0, 0x40, + + /* U+55E5 "嗥" */ + 0x0, 0x80, 0x1, 0x3, 0xdf, 0xe4, 0xa0, 0x49, + 0x40, 0x92, 0xff, 0x25, 0x2, 0x4b, 0xfc, 0x90, + 0xc1, 0x2f, 0xfa, 0x44, 0x47, 0x92, 0x48, 0xff, + 0x40, 0x8, 0x0, 0x10, 0x0, 0x20, + + /* U+55E6 "嗦" */ + 0x0, 0x41, 0xef, 0xfa, 0x41, 0x4, 0x82, 0x9, + 0x7f, 0xd3, 0x8, 0xa4, 0x60, 0x4b, 0xf8, 0x90, + 0x81, 0x26, 0x32, 0x5f, 0xd7, 0x82, 0x88, 0x64, + 0xc1, 0x8, 0x40, 0xf0, 0x0, + + /* U+55E8 "嗨" */ + 0x0, 0x0, 0x11, 0x3, 0x93, 0xfd, 0x8, 0xa, + 0x2f, 0xd7, 0x20, 0xa9, 0x49, 0x50, 0x82, 0xa3, + 0xff, 0x42, 0x12, 0x94, 0xa5, 0x48, 0x4c, 0x9f, + 0xf1, 0x1, 0x2, 0x2, 0x8, 0x3c, + + /* U+55EA "嗪" */ + 0x0, 0x83, 0xdf, 0xe9, 0x8, 0x24, 0x20, 0x97, + 0xfa, 0x44, 0x9, 0xff, 0xe4, 0x88, 0x97, 0xf2, + 0x62, 0x39, 0x7f, 0xbc, 0x20, 0x83, 0x70, 0x32, + 0x20, 0x8, 0x0, + + /* U+55EB "嗫" */ + 0x7, 0xfd, 0xe4, 0x12, 0x48, 0x24, 0x9f, 0xc9, + 0x20, 0x92, 0x7f, 0x24, 0x82, 0x4b, 0xfe, 0x90, + 0x1, 0x2f, 0xfa, 0x45, 0x17, 0xa9, 0x48, 0x31, + 0x80, 0x85, 0x82, 0x10, 0x80, + + /* U+55EC "嗬" */ + 0x1, 0x0, 0x4, 0x8e, 0xff, 0xe8, 0x40, 0xa2, + 0x2, 0x97, 0xfa, 0x40, 0xab, 0x2, 0xad, 0xea, + 0x94, 0xaa, 0x52, 0xa9, 0x4a, 0xe5, 0xea, 0x10, + 0x20, 0x40, 0x81, 0x1c, + + /* U+55EF "嗯" */ + 0x7, 0xfd, 0xe8, 0x8a, 0x57, 0xd4, 0xa2, 0x29, + 0x4a, 0x52, 0xa2, 0xa5, 0x1, 0x4b, 0xfe, 0x90, + 0x1, 0x20, 0x83, 0xd9, 0x14, 0x50, 0x90, 0x91, + 0x20, 0x3c, 0x0, + + /* U+55F2 "嗲" */ + 0x0, 0x91, 0xe6, 0x1e, 0x52, 0x44, 0x87, 0x89, + 0x74, 0xf2, 0x3f, 0xa4, 0xc2, 0x48, 0x58, 0x90, + 0xc1, 0x3e, 0xc2, 0x42, 0x77, 0x9c, 0x48, 0x7, + 0x0, 0x18, 0x3, 0xc0, 0x0, + + /* U+55F3 "嗳" */ + 0x7, 0xff, 0xc0, 0x9, 0x24, 0xa4, 0x92, 0x97, + 0xfe, 0x50, 0x19, 0x10, 0x25, 0xff, 0x91, 0x2, + 0x47, 0xe9, 0x30, 0xbd, 0x24, 0x84, 0x60, 0x26, + 0x70, 0x20, 0x0, + + /* U+55F5 "嗵" */ + 0x9, 0xfd, 0xc9, 0x1a, 0x93, 0x45, 0x1, 0xa, + 0x1f, 0xd5, 0xa4, 0xa9, 0x7f, 0x52, 0x92, 0xa5, + 0x25, 0x4b, 0xfa, 0x94, 0x97, 0x29, 0x28, 0xf3, + 0x86, 0x3f, 0x80, + + /* U+55F7 "嗷" */ + 0x2, 0x10, 0x4, 0x23, 0xc8, 0x44, 0xfd, 0x9, + 0x23, 0xf2, 0xf4, 0xa4, 0x99, 0x4f, 0xd2, 0x94, + 0x29, 0x28, 0x52, 0x5e, 0x64, 0xa4, 0x8f, 0x49, + 0x91, 0x15, 0x82, 0xf9, 0x80, 0x0, + + /* U+55FD "嗽" */ + 0x2, 0x11, 0xc4, 0x22, 0xbe, 0x7d, 0x11, 0x2a, + 0xfd, 0x55, 0x52, 0xaa, 0xa4, 0x55, 0x4c, 0xaf, + 0x99, 0x44, 0x32, 0x9c, 0xe7, 0x55, 0x49, 0x26, + 0x40, 0x48, 0x80, 0xa0, 0x80, + + /* U+55FE "嗾" */ + 0x0, 0x0, 0x9, 0xe, 0x27, 0xeb, 0xe0, 0xa5, + 0x22, 0x91, 0xfa, 0x65, 0x29, 0xa4, 0xa6, 0x12, + 0x9b, 0xfa, 0x61, 0x3a, 0x8c, 0x8a, 0x28, 0x69, + 0x21, 0x68, 0x40, + + /* U+5600 "嘀" */ + 0x0, 0x0, 0x0, 0x83, 0xdf, 0xfc, 0x88, 0x89, + 0x12, 0x12, 0xff, 0xa5, 0x11, 0x4a, 0xfa, 0x94, + 0x45, 0x2b, 0xca, 0x54, 0x97, 0xa9, 0x29, 0x5e, + 0x40, 0x80, 0x81, 0xe, 0x0, + + /* U+5601 "嘁" */ + 0x0, 0x20, 0x0, 0x53, 0xc0, 0x84, 0xbf, 0xe9, + 0x82, 0x13, 0x24, 0x26, 0x79, 0x4c, 0x94, 0x99, + 0x29, 0x2f, 0xd2, 0x62, 0xa4, 0xd6, 0x8f, 0xc9, + 0x11, 0x96, 0x42, 0x2b, 0x9, 0x86, + + /* U+5608 "嘈" */ + 0x1, 0x20, 0x2, 0x43, 0xff, 0xfc, 0x89, 0x9, + 0x7f, 0xf3, 0x24, 0x65, 0xff, 0xcc, 0x91, 0x97, + 0xff, 0x20, 0x2, 0x5f, 0xf4, 0xa0, 0x2f, 0x40, + 0x52, 0xff, 0x81, 0xff, 0x2, 0x2, + + /* U+5609 "嘉" */ + 0x1, 0x1, 0xff, 0xfc, 0x4, 0x1, 0xf7, 0xc3, + 0xff, 0x84, 0x1, 0xf, 0xfe, 0x2, 0x10, 0xff, + 0xfe, 0x0, 0x0, 0x40, 0x3, 0xf3, 0xe1, 0x24, + 0x44, 0x4f, 0xb3, 0x91, 0x0, + + /* U+560C "嘌" */ + 0x7, 0xff, 0xc4, 0x89, 0x12, 0x25, 0xfe, 0x99, + 0x2a, 0x64, 0xa9, 0x7f, 0xa4, 0x0, 0x97, 0xfa, + 0x40, 0x9, 0xff, 0xfc, 0x10, 0x82, 0x50, 0x31, + 0x30, 0x18, 0x0, + + /* U+560E "嘎" */ + 0xf, 0xff, 0xc2, 0x9, 0x7f, 0xa5, 0x2, 0x97, + 0xfa, 0x50, 0x29, 0x7f, 0xa5, 0xfe, 0x90, 0x2, + 0x44, 0xf9, 0x7e, 0x3c, 0x24, 0x90, 0x64, 0xe, + 0x90, 0xc1, 0xc0, + + /* U+560F "嘏" */ + 0x20, 0x0, 0x47, 0xf8, 0x89, 0x17, 0xd2, 0x22, + 0x24, 0x44, 0x7f, 0x88, 0x80, 0x79, 0xfe, 0x92, + 0x25, 0x24, 0x52, 0x4e, 0xa4, 0x90, 0x8f, 0x22, + 0x92, 0x44, 0xa4, 0x90, 0x80, + + /* U+5618 "嘘" */ + 0x0, 0x80, 0x3, 0xfe, 0x8, 0x29, 0xff, 0xa4, + 0x86, 0x93, 0xca, 0x58, 0xa9, 0x3e, 0xa4, 0x2, + 0x92, 0x4e, 0x69, 0x62, 0xa6, 0x8, 0x90, 0x62, + 0x43, 0x3f, 0xc0, + + /* U+561B "嘛" */ + 0x0, 0x40, 0x0, 0x83, 0xbf, 0xfd, 0x40, 0xa, + 0x91, 0x15, 0xf7, 0xaa, 0x44, 0x54, 0x88, 0xab, + 0x99, 0x56, 0xf2, 0xb5, 0x67, 0x6a, 0xa9, 0x11, + 0x2, 0x22, 0x8, 0x44, 0x0, + + /* U+561E "嘞" */ + 0x8, 0x93, 0xbf, 0x4a, 0x89, 0x2a, 0x2f, 0xa7, + 0x96, 0x88, 0x5a, 0xf9, 0x6a, 0xa5, 0xaa, 0x96, + 0xbe, 0x5a, 0x22, 0x7b, 0xf9, 0xa2, 0x24, 0x8, + 0x90, 0x25, 0x80, + + /* U+561F "嘟" */ + 0x4, 0x0, 0x8, 0x3f, 0xbd, 0x5d, 0x24, 0xaa, + 0x51, 0x57, 0xfa, 0xa8, 0x86, 0x52, 0xa, 0xaf, + 0x95, 0x71, 0x26, 0x9e, 0x4f, 0x44, 0x9a, 0x89, + 0x60, 0xf2, 0x2, 0x24, 0x0, + + /* U+5623 "嘣" */ + 0x0, 0x40, 0x11, 0x1e, 0x44, 0x69, 0xff, 0xa0, + 0x2, 0x80, 0xa, 0x7b, 0xe9, 0x29, 0xa7, 0xbe, + 0x92, 0x9a, 0x4a, 0x69, 0xef, 0xe4, 0xa6, 0x23, + 0x10, 0x8c, 0x44, 0xf7, + + /* U+5624 "嘤" */ + 0xf, 0x3f, 0xad, 0x5a, 0xb5, 0x6a, 0xd5, 0xa2, + 0x12, 0x94, 0xaa, 0x42, 0x6a, 0x20, 0xaf, 0xfe, + 0x88, 0x4e, 0x42, 0x20, 0xf0, 0x80, 0xe0, 0x3c, + 0x70, + + /* U+5627 "嘧" */ + 0x0, 0x40, 0x0, 0x83, 0xdf, 0xf4, 0x84, 0x9, + 0x15, 0x12, 0xa6, 0xa6, 0x51, 0x48, 0xc5, 0x93, + 0x9, 0x2b, 0xf3, 0xc1, 0x4, 0xa2, 0x29, 0x44, + 0x40, 0xff, 0x80, 0x1, 0x0, + + /* U+562C "嘬" */ + 0x3, 0xfb, 0xd0, 0x29, 0x3f, 0xa5, 0x2, 0x93, + 0xfa, 0x40, 0x9, 0xff, 0xe5, 0x20, 0x97, 0xbe, + 0x52, 0x99, 0x7a, 0xbd, 0x26, 0x84, 0xd0, 0x2e, + 0xb0, 0xc, 0x0, + + /* U+562D "嘭" */ + 0x1, 0x5, 0xef, 0x8a, 0x44, 0x24, 0x88, 0x89, + 0x7c, 0x32, 0x0, 0xa5, 0xf1, 0x4a, 0x24, 0x94, + 0x71, 0x2f, 0x86, 0x51, 0xf, 0x94, 0x28, 0x28, + 0x81, 0xfa, 0x0, 0x8, 0x0, + + /* U+5631 "嘱" */ + 0xef, 0xff, 0x50, 0x6, 0xbf, 0xfd, 0x40, 0xa, + 0xbf, 0x95, 0x7f, 0xaa, 0x91, 0x55, 0xfe, 0xa8, + 0x41, 0xdf, 0xfe, 0x31, 0xc, 0xaf, 0xd1, 0x40, + 0x24, 0x83, 0x80, + + /* U+5632 "嘲" */ + 0x2, 0x3f, 0xbe, 0x9a, 0x22, 0x68, 0x89, 0xaf, + 0xa6, 0xa2, 0xfa, 0xfa, 0x6a, 0x29, 0xa8, 0xa6, + 0xbe, 0xfa, 0x22, 0x7b, 0xe9, 0x82, 0x64, 0x9, + 0x10, 0x25, 0xc0, + + /* U+5634 "嘴" */ + 0x1, 0x21, 0xf3, 0xda, 0x64, 0xcc, 0xc9, 0x19, + 0xe9, 0xf2, 0x40, 0x25, 0x7c, 0x4b, 0xfe, 0x94, + 0x45, 0x2f, 0xfb, 0xd1, 0x14, 0x3f, 0xe8, 0x84, + 0x41, 0x8, 0x82, 0x17, 0x0, + + /* U+5636 "嘶" */ + 0x9, 0x1, 0xd2, 0x7a, 0xbe, 0x85, 0x49, 0xa, + 0x72, 0x15, 0x27, 0xea, 0x4a, 0x53, 0x94, 0xa9, + 0x29, 0x52, 0x52, 0xbe, 0xa7, 0x2b, 0x48, 0x54, + 0x81, 0x9, 0x0, 0x22, 0x0, + + /* U+5639 "嘹" */ + 0x0, 0x40, 0x0, 0x83, 0xdf, 0xf4, 0x85, 0x9, + 0x7a, 0xd2, 0x42, 0x25, 0xff, 0x4e, 0x4, 0x94, + 0x9, 0x2f, 0xf2, 0x50, 0x24, 0x9f, 0x8f, 0x25, + 0x10, 0x89, 0x82, 0x10, 0x0, 0xc0, + + /* U+563B "嘻" */ + 0x0, 0x41, 0xef, 0xfe, 0x41, 0x4, 0xbf, 0xe9, + 0x0, 0x12, 0xff, 0xa5, 0x1, 0x4b, 0xfe, 0x91, + 0x11, 0x3f, 0xfe, 0x40, 0x7, 0xbf, 0xe9, 0x40, + 0x40, 0xff, 0x81, 0x1, 0x0, + + /* U+563F "嘿" */ + 0x7, 0xfd, 0xe8, 0x8a, 0x51, 0x14, 0xaa, 0xa9, + 0x46, 0x52, 0x88, 0xa5, 0xff, 0x48, 0x20, 0x97, + 0xfd, 0x20, 0x82, 0x7f, 0xff, 0x80, 0x8, 0x52, + 0x41, 0x22, 0x42, 0x0, 0x0, + + /* U+564C "噌" */ + 0x2, 0x13, 0xc4, 0x89, 0x7f, 0xe6, 0x11, 0x99, + 0x56, 0x61, 0x19, 0x7f, 0xe4, 0x0, 0x97, 0xfa, + 0x50, 0x29, 0x40, 0xbd, 0xfe, 0x94, 0x8, 0x1f, + 0xe0, 0x40, 0x80, + + /* U+564D "噍" */ + 0x2, 0x23, 0xc8, 0x9, 0x7f, 0xe5, 0x8, 0x9c, + 0x22, 0x5f, 0xe9, 0x42, 0x25, 0xfe, 0x94, 0x22, + 0x50, 0x89, 0x7f, 0xfc, 0x0, 0x89, 0x24, 0x22, + 0x50, 0x88, 0x0, + + /* U+564E "噎" */ + 0x0, 0x80, 0x1f, 0xff, 0x8, 0x25, 0xfe, 0x90, + 0x2, 0x5f, 0xf9, 0x80, 0x64, 0x78, 0x97, 0xfa, + 0x50, 0x29, 0x40, 0xbd, 0xfe, 0x82, 0x10, 0x4, + 0x80, 0xff, 0xc0, + + /* U+5654 "噔" */ + 0x7, 0xc9, 0xe3, 0x62, 0x44, 0x94, 0xa9, 0x49, + 0x21, 0x12, 0xbd, 0xa6, 0x1, 0x4b, 0xfc, 0x94, + 0x9, 0x28, 0x12, 0x5f, 0xe4, 0x90, 0x8e, 0x12, + 0x1, 0xff, 0x80, + + /* U+5657 "噗" */ + 0x1, 0x20, 0x4, 0xaf, 0x52, 0xa4, 0x48, 0x97, + 0xfe, 0x48, 0x49, 0x12, 0x25, 0xff, 0x90, 0x82, + 0x5f, 0xe9, 0x8, 0x24, 0x20, 0xf7, 0xfe, 0x5, + 0x80, 0x21, 0x2, 0x3, + + /* U+5658 "噘" */ + 0xf, 0xfd, 0xd0, 0x2, 0xa8, 0x25, 0x4a, 0x4a, + 0xbf, 0xd5, 0x12, 0xaa, 0xa9, 0x55, 0x54, 0xaa, + 0xa9, 0x57, 0xd3, 0xa2, 0x64, 0x88, 0xa1, 0x11, + 0x42, 0x44, 0x80, 0x0, 0x0, + + /* U+5659 "噙" */ + 0x0, 0x40, 0x1, 0x83, 0x84, 0xc5, 0x32, 0x6a, + 0xbf, 0xb4, 0x0, 0x29, 0x69, 0x52, 0x72, 0xa5, + 0x15, 0x4f, 0xfa, 0x82, 0x5, 0x7f, 0xea, 0x92, + 0x5d, 0x7e, 0x8a, 0x1, 0x4, 0xe, + + /* U+565C "噜" */ + 0x1, 0x0, 0x7, 0xf3, 0xd0, 0x44, 0xff, 0xe9, + 0x44, 0x52, 0xff, 0xa5, 0xff, 0x48, 0x0, 0x9f, + 0xff, 0x20, 0x2, 0x5f, 0xf7, 0xa0, 0x29, 0x7f, + 0xc0, 0xff, 0x81, 0x1, 0x0, + + /* U+5662 "噢" */ + 0x0, 0x0, 0x1, 0x3, 0xdd, 0xf4, 0xa2, 0x29, + 0x55, 0x52, 0x88, 0xa5, 0x7d, 0x4a, 0x72, 0x97, + 0x55, 0x20, 0x82, 0x5f, 0xff, 0x85, 0x8, 0xa, + 0x0, 0x63, 0x3, 0x1, 0x0, + + /* U+5664 "噤" */ + 0x2, 0x10, 0x4, 0x23, 0xff, 0xf4, 0x90, 0x89, + 0x7b, 0x93, 0x4a, 0xa4, 0x0, 0x4b, 0xfc, 0x90, + 0x1, 0x20, 0x2, 0x7f, 0xf7, 0x82, 0x8, 0x25, + 0x1, 0x89, 0x80, 0x60, 0x0, + + /* U+5668 "器" */ + 0x3e, 0x7c, 0x44, 0x88, 0x89, 0x11, 0xf3, 0xe0, + 0x11, 0xf, 0xff, 0xc0, 0xa0, 0x6, 0x3c, 0x70, + 0x6, 0x7c, 0xf8, 0x89, 0x11, 0x12, 0x23, 0xe7, + 0xc4, 0x48, 0x80, + + /* U+5669 "噩" */ + 0x7f, 0xfe, 0x2, 0x1, 0xf5, 0xf2, 0x2a, 0x24, + 0x54, 0x4f, 0xaf, 0x80, 0x40, 0x7f, 0xff, 0x1, + 0x0, 0xfa, 0xf9, 0x15, 0x13, 0xeb, 0xe0, 0x10, + 0x1f, 0xff, 0xc0, + + /* U+566A "噪" */ + 0x3, 0xf9, 0xe4, 0x12, 0x48, 0x24, 0x9f, 0xc9, + 0x0, 0x12, 0xf7, 0xa5, 0x29, 0x4b, 0xde, 0x90, + 0x1, 0x20, 0x82, 0x5f, 0xf7, 0x8a, 0x88, 0x24, + 0x81, 0x88, 0xc0, 0x10, 0x0, + + /* U+566B "噫" */ + 0x0, 0x80, 0x1, 0xf, 0x7f, 0xa4, 0x84, 0x91, + 0x22, 0x7f, 0xf9, 0x0, 0x25, 0xfe, 0x94, 0xa, + 0x5f, 0xe9, 0x40, 0xbc, 0xfc, 0x82, 0x40, 0x28, + 0x50, 0xa1, 0x40, 0x78, + + /* U+566C "噬" */ + 0x2, 0x10, 0x4, 0x23, 0xcf, 0xfc, 0xa1, 0x49, + 0x94, 0x92, 0x0, 0x25, 0xff, 0x48, 0x20, 0x92, + 0x49, 0x24, 0x92, 0x55, 0x54, 0xa6, 0xbf, 0x4, + 0x10, 0x8, 0x3, 0xff, 0x80, + + /* U+5671 "噱" */ + 0x1, 0x1, 0xc3, 0xf2, 0xbf, 0xf5, 0x40, 0x2a, + 0xbe, 0x95, 0x11, 0x2a, 0x1c, 0x55, 0xfe, 0xa8, + 0x81, 0x56, 0xb3, 0xa7, 0xc4, 0x53, 0x41, 0x1a, + 0x43, 0xc4, 0x8, 0x70, 0x0, + + /* U+5676 "噶" */ + 0x2, 0x20, 0x3f, 0xff, 0x2, 0x25, 0xfe, 0x94, + 0xa, 0x5f, 0xe9, 0x40, 0xa4, 0xfc, 0x93, 0xfe, + 0x52, 0x19, 0xd6, 0x7d, 0x1, 0x97, 0xf4, 0x0, + 0x30, 0x7, 0x80, + + /* U+567B "噻" */ + 0x0, 0x40, 0x0, 0x83, 0xdf, 0xf4, 0xc8, 0xa9, + 0x11, 0x12, 0xff, 0xa4, 0x44, 0x4b, 0xfe, 0x91, + 0x11, 0x2f, 0xfe, 0x45, 0x44, 0x92, 0x4f, 0xff, + 0xf2, 0x8, 0x1, 0xff, 0x0, + + /* U+567C "噼" */ + 0x0, 0x13, 0x9e, 0x4a, 0x4b, 0xe9, 0x20, 0xa4, + 0xaa, 0x9e, 0xaa, 0x41, 0x29, 0xf, 0xa7, 0x92, + 0xba, 0x4a, 0xab, 0xfa, 0xa4, 0x93, 0x90, 0x4a, + 0x40, 0x29, 0x0, + + /* U+5685 "嚅" */ + 0x7, 0xfb, 0x82, 0xa, 0xff, 0xea, 0x21, 0xab, + 0xb6, 0x82, 0xa, 0x7b, 0xab, 0xff, 0xa0, 0x82, + 0x9f, 0xea, 0x92, 0xba, 0x4a, 0xa9, 0x28, 0x24, + 0xa0, 0x93, 0x80, + + /* U+5686 "嚆" */ + 0x1, 0x10, 0xf, 0xff, 0xc4, 0x44, 0x82, 0x9, + 0x7f, 0xd2, 0x0, 0x24, 0xfe, 0x49, 0xfc, 0x90, + 0x1, 0x2f, 0xfa, 0x50, 0x17, 0xaf, 0xa9, 0x5f, + 0x40, 0x80, 0x81, 0x7, 0x0, + + /* U+568E "嚎" */ + 0x0, 0x40, 0x1, 0xf, 0x7b, 0xe4, 0xfe, 0x92, + 0xa, 0x4f, 0xe9, 0x0, 0x25, 0xff, 0x97, 0xfe, + 0x42, 0x9, 0x75, 0xa4, 0x6c, 0xf6, 0x6a, 0x6, + 0xb0, 0xe2, 0x40, 0x70, + + /* U+568F "嚏" */ + 0x0, 0x40, 0x1f, 0xee, 0x4, 0x2b, 0xff, 0xa8, + 0x6, 0xbf, 0xfa, 0x7f, 0xa9, 0x12, 0xa3, 0xb2, + 0xbf, 0xfa, 0x4, 0x79, 0x10, 0x86, 0x78, 0x67, + 0xf0, 0x0, 0x0, + + /* U+5693 "嚓" */ + 0x0, 0x40, 0x0, 0x83, 0xdf, 0xfc, 0x90, 0x9, + 0x3f, 0xf2, 0x94, 0xa7, 0xa6, 0x48, 0xfc, 0x92, + 0xd, 0x38, 0x6, 0x4f, 0xe7, 0x92, 0x88, 0x44, + 0x91, 0x8, 0x80, 0x70, 0x0, + + /* U+56A3 "嚣" */ + 0x7c, 0xf9, 0x12, 0x24, 0x48, 0x9f, 0x3e, 0x0, + 0x1, 0xff, 0xe3, 0xff, 0x10, 0x4, 0x3, 0x0, + 0x73, 0xce, 0x0, 0xdf, 0x3e, 0x44, 0x89, 0xf3, + 0xe4, 0x48, 0x80, + + /* U+56AF "嚯" */ + 0x7, 0xf9, 0xe1, 0x2, 0x5f, 0xfc, 0xc4, 0x39, + 0x3b, 0x72, 0x10, 0x25, 0xce, 0x49, 0x20, 0x97, + 0xff, 0x28, 0x82, 0x7f, 0xe7, 0xa2, 0x9, 0x7f, + 0x80, 0xff, 0xc1, 0x0, 0x0, + + /* U+56B7 "嚷" */ + 0x0, 0x40, 0x0, 0x83, 0xdf, 0xfc, 0x80, 0x9, + 0xfb, 0xd2, 0xf7, 0xa4, 0x0, 0x4b, 0xfe, 0x92, + 0x11, 0x2f, 0xfa, 0x48, 0x44, 0xbf, 0xff, 0x14, + 0x51, 0xc7, 0x0, 0xb3, 0x1, 0x80, + + /* U+56BC "嚼" */ + 0x7, 0xfd, 0xe8, 0x92, 0x5f, 0xf4, 0xc9, 0x29, + 0x92, 0x52, 0xdb, 0x25, 0xe1, 0x4a, 0x5f, 0x97, + 0x85, 0x29, 0x4b, 0xde, 0x54, 0xa8, 0x20, 0x78, + 0x40, 0x8b, 0x0, + + /* U+56CA "囊" */ + 0xff, 0xfc, 0x8, 0x7, 0xff, 0xf, 0xfc, 0x2, + 0x3, 0xf7, 0xfb, 0xcf, 0x42, 0x10, 0x7f, 0xf8, + 0x21, 0x3, 0xff, 0x3f, 0xff, 0x5, 0x1, 0xe3, + 0xc9, 0xf1, 0xc0, + + /* U+56D4 "囔" */ + 0x0, 0x80, 0x3f, 0xfe, 0x7f, 0xa9, 0xfe, 0xa0, + 0x82, 0xbd, 0xfa, 0x7b, 0xa9, 0xee, 0xaf, 0xfe, + 0x88, 0x4a, 0x7f, 0xbb, 0x7b, 0x83, 0x48, 0x78, + 0xc0, 0x38, 0x40, + + /* U+56D7 "囗" */ + 0xff, 0xfc, 0x0, 0x60, 0x3, 0x0, 0x18, 0x0, + 0xc0, 0x6, 0x0, 0x30, 0x1, 0x80, 0xc, 0x0, + 0x60, 0x3, 0x0, 0x1f, 0xff, 0xc0, 0x4, + + /* U+56DA "囚" */ + 0xff, 0xfc, 0x10, 0x60, 0x83, 0x4, 0x18, 0x20, + 0xc1, 0x6, 0x14, 0x30, 0x91, 0x88, 0x4d, 0x81, + 0x68, 0x3, 0x0, 0x1f, 0xff, 0xc0, 0x4, + + /* U+56DB "四" */ + 0xff, 0xfc, 0x44, 0x62, 0x23, 0x11, 0x18, 0x88, + 0xc4, 0x46, 0x22, 0x32, 0x11, 0xb0, 0xfd, 0x0, + 0x60, 0x3, 0x0, 0x1f, 0xff, 0xc0, 0x4, + + /* U+56DD "囝" */ + 0xff, 0xfe, 0x0, 0x19, 0xfe, 0x60, 0x11, 0x81, + 0x86, 0x4, 0x1b, 0xff, 0x60, 0x41, 0x81, 0x6, + 0x4, 0x18, 0xe0, 0x60, 0x1, 0xff, 0xfe, 0x0, + 0x10, + + /* U+56DE "回" */ + 0xff, 0xfc, 0x0, 0x60, 0x3, 0x1f, 0x98, 0x84, + 0xc4, 0x26, 0x21, 0x31, 0x9, 0x8f, 0xcc, 0x0, + 0x60, 0x3, 0x0, 0x1f, 0xff, 0xc0, 0x4, + + /* U+56DF "囟" */ + 0x4, 0x0, 0x20, 0x3f, 0xff, 0x0, 0x19, 0x4, + 0xc4, 0x46, 0x16, 0x30, 0x61, 0x83, 0xc, 0x24, + 0x66, 0x13, 0x60, 0x58, 0x0, 0xff, 0xfe, 0x0, + 0x20, + + /* U+56E0 "因" */ + 0xff, 0xfc, 0x10, 0x60, 0x83, 0x4, 0x1b, 0xfe, + 0xc1, 0x6, 0x8, 0x30, 0xa1, 0x84, 0x8c, 0xc3, + 0x68, 0xb, 0x0, 0x1f, 0xff, 0xc0, 0x4, + + /* U+56E1 "囡" */ + 0xff, 0xfe, 0x0, 0x18, 0x40, 0x6f, 0xfd, 0x88, + 0x46, 0x21, 0x19, 0x88, 0x61, 0xe1, 0x83, 0x86, + 0x71, 0x9a, 0x0, 0x60, 0x1, 0xff, 0xfe, 0x0, + 0x10, + + /* U+56E2 "团" */ + 0xff, 0xfe, 0x2, 0x18, 0x8, 0x6f, 0xfd, 0x81, + 0x86, 0xa, 0x18, 0x68, 0x63, 0x21, 0x90, 0x86, + 0x82, 0x18, 0x78, 0x60, 0x1, 0xff, 0xfe, 0x0, + 0x10, + + /* U+56E4 "囤" */ + 0xff, 0xfe, 0x0, 0x18, 0x20, 0x6f, 0xfd, 0x82, + 0x6, 0x8, 0x19, 0x22, 0x64, 0x89, 0x9f, 0xe6, + 0x8, 0x18, 0x21, 0x60, 0x7d, 0x80, 0x7, 0xff, + 0xf8, 0x0, 0x40, + + /* U+56EB "囫" */ + 0xff, 0xfc, 0x0, 0x62, 0x3, 0x3f, 0xd9, 0x4a, + 0xd2, 0x56, 0x24, 0xb1, 0x25, 0xa2, 0x2c, 0x21, + 0x62, 0x73, 0x0, 0x1f, 0xff, 0xc0, 0x4, + + /* U+56ED "园" */ + 0xff, 0xfe, 0x0, 0x19, 0xfe, 0x60, 0x1, 0x80, + 0x6, 0xff, 0xd8, 0x48, 0x62, 0x25, 0x88, 0x96, + 0x42, 0x5a, 0xf, 0x60, 0x1, 0xff, 0xfe, 0x0, + 0x10, + + /* U+56F0 "困" */ + 0xff, 0xfc, 0x0, 0x60, 0x83, 0x7f, 0xd8, 0x20, + 0xc3, 0x86, 0x2a, 0x32, 0x49, 0xb2, 0x2c, 0x10, + 0x60, 0x83, 0x0, 0x1f, 0xff, 0xc0, 0x4, + + /* U+56F1 "囱" */ + 0x2, 0x0, 0x20, 0x3f, 0xff, 0x0, 0x18, 0x40, + 0xc3, 0xf6, 0x21, 0x32, 0x9, 0xa6, 0x8c, 0x8, + 0x60, 0xb3, 0x38, 0x18, 0x0, 0xff, 0xfe, 0x0, + 0x20, + + /* U+56F4 "围" */ + 0xff, 0xfc, 0x20, 0x6f, 0xfb, 0x8, 0x18, 0x40, + 0xcf, 0xe6, 0x10, 0x37, 0xfd, 0x84, 0x2c, 0x23, + 0x61, 0x73, 0x8, 0x1f, 0xff, 0xc0, 0x4, + + /* U+56F5 "囵" */ + 0xff, 0xfc, 0x0, 0x60, 0xc3, 0x9, 0x18, 0x84, + 0xd8, 0x1e, 0x21, 0x31, 0x71, 0x8c, 0x2c, 0x41, + 0x62, 0xb, 0xf, 0x98, 0x0, 0xff, 0xfe, 0x0, + 0x20, + + /* U+56F9 "囹" */ + 0xff, 0xfc, 0x8, 0x60, 0xc3, 0x9, 0x19, 0xa6, + 0xd0, 0x86, 0x7f, 0xb0, 0x9, 0x88, 0x8c, 0x38, + 0x60, 0x43, 0x1, 0x1f, 0xff, 0xc0, 0x4, + + /* U+56FA "固" */ + 0xff, 0xfc, 0x10, 0x60, 0x83, 0x7f, 0xd8, 0x20, + 0xc1, 0x6, 0x7f, 0x32, 0x9, 0x90, 0x4c, 0x82, + 0x67, 0xf3, 0x0, 0x1f, 0xff, 0xc0, 0x4, + + /* U+56FD "国" */ + 0xff, 0xfe, 0x0, 0x1b, 0xff, 0x60, 0x81, 0x82, + 0x6, 0x7f, 0x98, 0x20, 0x60, 0x91, 0x82, 0x26, + 0x8, 0x1b, 0xff, 0x60, 0x1, 0xff, 0xfe, 0x0, + 0x10, + + /* U+56FE "图" */ + 0xff, 0xfe, 0x0, 0x18, 0x40, 0x63, 0xf9, 0x98, + 0x66, 0x92, 0x18, 0x30, 0x67, 0x3d, 0xa4, 0x6, + 0xe, 0x19, 0x80, 0x61, 0xf1, 0x80, 0x7, 0xff, + 0xf8, 0x0, 0x40, + + /* U+56FF "囿" */ + 0xff, 0xfe, 0x8, 0x1b, 0xfe, 0x62, 0x1, 0x8f, + 0xc6, 0x41, 0x1a, 0xfc, 0x64, 0x11, 0x8f, 0xc6, + 0x41, 0x19, 0x4, 0x60, 0x21, 0xff, 0xfe, 0x0, + 0x10, + + /* U+5703 "圃" */ + 0xff, 0xfe, 0x0, 0x18, 0x26, 0x6f, 0xfd, 0x82, + 0x6, 0x7f, 0x9a, 0x22, 0x67, 0xf9, 0xa2, 0x26, + 0x7f, 0x9a, 0x22, 0x68, 0x89, 0x80, 0x47, 0xff, + 0xf8, 0x0, 0x40, + + /* U+5704 "圄" */ + 0xff, 0xfe, 0x0, 0x19, 0xfe, 0x61, 0x1, 0x84, + 0x6, 0x7f, 0x18, 0x44, 0x6f, 0xfd, 0x80, 0x6, + 0x7f, 0x99, 0x2, 0x64, 0x9, 0x8f, 0xc7, 0xff, + 0xf8, 0x0, 0x40, + + /* U+5706 "圆" */ + 0xff, 0xfe, 0x0, 0x18, 0xfc, 0x64, 0x11, 0x90, + 0x46, 0x3f, 0x18, 0x0, 0x67, 0xf9, 0x92, 0x26, + 0x48, 0x99, 0x3a, 0x63, 0x11, 0xb0, 0x27, 0xff, + 0xf8, 0x0, 0x40, + + /* U+5708 "圈" */ + 0xff, 0xfe, 0x0, 0x19, 0x24, 0x60, 0x81, 0xbf, + 0xe6, 0x10, 0x1b, 0xff, 0x62, 0x21, 0x9f, 0xe7, + 0xc2, 0x59, 0x32, 0x64, 0x11, 0x8f, 0x87, 0xff, + 0xf8, 0x0, 0x40, + + /* U+5709 "圉" */ + 0xff, 0xfe, 0x0, 0x18, 0x20, 0x6f, 0xf9, 0x82, + 0x7, 0xff, 0xd8, 0x88, 0x60, 0x41, 0xbf, 0xe6, + 0x8, 0x1f, 0xff, 0x60, 0x81, 0x82, 0x7, 0xff, + 0xf8, 0x0, 0x40, + + /* U+570A "圊" */ + 0xff, 0xfe, 0x0, 0x18, 0x20, 0x6f, 0xf9, 0x9f, + 0xc6, 0x8, 0x1b, 0xde, 0x67, 0xf1, 0x90, 0x46, + 0x7f, 0x19, 0xfc, 0x64, 0x11, 0x91, 0xc7, 0xff, + 0xf8, 0x0, 0x40, + + /* U+571C "圜" */ + 0xff, 0xfe, 0x0, 0x19, 0xfe, 0x65, 0x29, 0x9f, + 0xe6, 0x0, 0x19, 0xfe, 0x67, 0xf9, 0x90, 0x26, + 0x7f, 0x98, 0x52, 0x7e, 0x71, 0x86, 0x27, 0xff, + 0xf8, 0x0, 0x40, + + /* U+571F "土" */ + 0x1, 0x0, 0x2, 0x0, 0x4, 0x0, 0x8, 0x0, + 0x10, 0xf, 0xff, 0x80, 0x40, 0x0, 0x80, 0x1, + 0x0, 0x2, 0x0, 0x4, 0x0, 0x8, 0x0, 0x10, + 0x1f, 0xff, 0xc0, + + /* U+5723 "圣" */ + 0x7f, 0xf8, 0x20, 0x10, 0x20, 0xc0, 0x22, 0x0, + 0x38, 0x3, 0x8f, 0x38, 0x41, 0x80, 0x80, 0x7f, + 0xfc, 0x2, 0x0, 0x4, 0x0, 0x8, 0xf, 0xff, + 0xe0, + + /* U+5728 "在" */ + 0x2, 0x0, 0x8, 0x1, 0xff, 0xf8, 0x20, 0x0, + 0x84, 0x2, 0x8, 0x4, 0x10, 0x1b, 0xfe, 0x50, + 0x40, 0x20, 0x80, 0x41, 0x0, 0x82, 0x1, 0x4, + 0x3, 0xff, 0xc4, 0x0, 0x0, + + /* U+5729 "圩" */ + 0x20, 0x0, 0x47, 0xf8, 0x80, 0x81, 0x1, 0xf, + 0x82, 0x4, 0x4, 0x9, 0xff, 0x90, 0x10, 0x20, + 0x20, 0x40, 0x40, 0xb0, 0x83, 0x81, 0x8, 0x2, + 0x0, 0x4, 0x0, 0x70, 0x0, + + /* U+572A "圪" */ + 0x20, 0x80, 0x43, 0x0, 0x87, 0xf1, 0x10, 0xf, + 0xc0, 0x4, 0x3f, 0x8, 0x6, 0x10, 0x18, 0x20, + 0x20, 0x40, 0x80, 0x82, 0x9, 0x88, 0x2c, 0x30, + 0x40, 0x40, 0x80, 0x7f, 0x0, + + /* U+572C "圬" */ + 0x20, 0x0, 0x47, 0xf8, 0x80, 0x1, 0x0, 0xf, + 0x80, 0x4, 0xff, 0xc8, 0x40, 0x10, 0x80, 0x21, + 0xfc, 0x40, 0x8, 0x80, 0x11, 0xe0, 0x2c, 0x0, + 0x40, 0x1, 0x80, 0x1e, 0x0, + + /* U+572D "圭" */ + 0x1, 0x0, 0x2, 0x1, 0xff, 0xf0, 0x8, 0x0, + 0x10, 0x0, 0x20, 0x3f, 0xff, 0x80, 0x80, 0x1, + 0x0, 0xff, 0xf8, 0x4, 0x0, 0x8, 0x0, 0x10, + 0x1f, 0xff, 0xc0, + + /* U+572E "圮" */ + 0x20, 0x0, 0x47, 0xf8, 0x80, 0x11, 0x0, 0x2f, + 0x80, 0x44, 0x0, 0x88, 0xff, 0x11, 0x0, 0x22, + 0x0, 0x44, 0x0, 0xa8, 0x7, 0x90, 0x20, 0x20, + 0x40, 0x3f, 0x80, + + /* U+572F "圯" */ + 0x20, 0x0, 0x47, 0xf8, 0x88, 0x11, 0x10, 0x2f, + 0xa0, 0x44, 0x40, 0x88, 0xff, 0x11, 0x0, 0x22, + 0x0, 0x44, 0x0, 0xe8, 0xe, 0x10, 0x10, 0x20, + 0x60, 0x3f, 0x80, + + /* U+5730 "地" */ + 0x40, 0x40, 0x80, 0x81, 0x11, 0x2, 0x22, 0x24, + 0x47, 0xde, 0xf8, 0x93, 0x11, 0x22, 0x22, 0x44, + 0x5c, 0x88, 0x81, 0x71, 0xb, 0x20, 0x18, 0x40, + 0x40, 0x80, 0x80, 0xfe, 0x0, + + /* U+5733 "圳" */ + 0x22, 0x24, 0x88, 0x92, 0x22, 0x7e, 0x89, 0x22, + 0x24, 0x88, 0x92, 0x22, 0x48, 0x89, 0x3e, 0x27, + 0x88, 0x90, 0x62, 0x41, 0x9, 0xc, 0x4, 0x20, + 0x10, + + /* U+5739 "圹" */ + 0x20, 0x60, 0x40, 0x40, 0x8f, 0xf9, 0x10, 0xf, + 0xa0, 0x4, 0x40, 0x8, 0x80, 0x11, 0x0, 0x22, + 0x0, 0x44, 0x0, 0xa8, 0x3, 0xb0, 0x8, 0x40, + 0x0, 0x80, 0x2, 0x0, 0x0, + + /* U+573A "场" */ + 0x20, 0x0, 0x9f, 0xe2, 0x1, 0x8, 0x18, 0xf9, + 0x80, 0x98, 0x2, 0x7f, 0xc8, 0x49, 0x21, 0x24, + 0x89, 0x13, 0xa4, 0x71, 0x11, 0x8, 0x84, 0x46, + 0x10, 0x33, 0x80, + + /* U+573B "圻" */ + 0x20, 0x4, 0x47, 0xf8, 0x88, 0x1, 0x10, 0xf, + 0xa0, 0x4, 0x40, 0x8, 0xff, 0x91, 0x8, 0x22, + 0x10, 0x44, 0x20, 0xe8, 0x47, 0x20, 0x80, 0x41, + 0x0, 0x82, 0x2, 0x4, 0x0, + + /* U+573E "圾" */ + 0x20, 0x0, 0x4f, 0xf8, 0x84, 0x21, 0x8, 0x8f, + 0x93, 0x4, 0x37, 0x88, 0x61, 0x10, 0xc4, 0x23, + 0x48, 0x44, 0x90, 0xe8, 0xc6, 0x31, 0x80, 0x45, + 0x81, 0x30, 0x80, 0x40, 0x80, + + /* U+5740 "址" */ + 0x20, 0x20, 0x40, 0x40, 0x80, 0x81, 0x11, 0xf, + 0xa2, 0x4, 0x47, 0xc8, 0x88, 0x11, 0x10, 0x22, + 0x20, 0x44, 0x40, 0xa8, 0x87, 0x91, 0x0, 0x22, + 0x1, 0xff, 0xc0, + + /* U+5742 "坂" */ + 0x20, 0x4, 0x47, 0xf8, 0x88, 0x1, 0x10, 0xf, + 0xa0, 0x4, 0x7f, 0x88, 0xa1, 0x11, 0x44, 0x22, + 0x88, 0x44, 0xa0, 0xf1, 0x43, 0x21, 0x8, 0x45, + 0x0, 0x91, 0x2, 0xc1, 0x0, + + /* U+5747 "均" */ + 0x21, 0x0, 0x84, 0x2, 0x10, 0x8, 0xbf, 0xfa, + 0x4, 0x90, 0x12, 0x58, 0x48, 0x31, 0x20, 0x4, + 0x80, 0xd2, 0xc, 0x4e, 0xc1, 0xc0, 0x4, 0x0, + 0x30, 0xf, 0x80, + + /* U+574A "坊" */ + 0x20, 0x40, 0x40, 0x40, 0x9f, 0xf9, 0x8, 0xf, + 0x90, 0x4, 0x20, 0x8, 0x7f, 0x10, 0x82, 0x21, + 0x4, 0x42, 0x8, 0xa4, 0x13, 0x90, 0x28, 0x20, + 0x40, 0x80, 0x82, 0x1e, 0x0, + + /* U+574C "坌" */ + 0x4, 0x40, 0x18, 0xc0, 0x60, 0x43, 0x0, 0x69, + 0xff, 0x60, 0x82, 0x2, 0x4, 0x38, 0xf8, 0x1, + 0x0, 0x2, 0x1, 0xff, 0xf0, 0x8, 0x0, 0x10, + 0x1f, 0xff, 0xc0, + + /* U+574D "坍" */ + 0x20, 0x0, 0x47, 0xf8, 0x88, 0x11, 0x10, 0x22, + 0x24, 0x5f, 0x44, 0x88, 0x89, 0x11, 0x2, 0x27, + 0xfe, 0x44, 0x8, 0x88, 0x11, 0xd0, 0x2c, 0x20, + 0x40, 0x80, 0x81, 0x1, 0x4, 0x1e, + + /* U+574E "坎" */ + 0x21, 0x0, 0x42, 0x0, 0x87, 0xf1, 0x10, 0x2f, + 0xa4, 0x44, 0x89, 0x9, 0x10, 0x10, 0x20, 0x20, + 0x40, 0x41, 0x40, 0xa2, 0x81, 0x88, 0x8c, 0x31, + 0x80, 0xc1, 0x83, 0x1, 0x0, + + /* U+574F "坏" */ + 0x20, 0x0, 0x4f, 0xfc, 0x81, 0x1, 0x2, 0xf, + 0x84, 0x4, 0x10, 0x8, 0x30, 0x10, 0xa8, 0x21, + 0x48, 0x54, 0x90, 0xd1, 0x16, 0x62, 0x0, 0x4, + 0x0, 0x8, 0x0, 0x10, 0x0, + + /* U+5750 "坐" */ + 0x1, 0x0, 0x22, 0x20, 0x44, 0x41, 0x8, 0x83, + 0x13, 0x5, 0x25, 0x11, 0x51, 0x40, 0x81, 0x1, + 0x0, 0x7f, 0xf0, 0x4, 0x0, 0x8, 0x0, 0x10, + 0x1f, 0xff, 0xc0, + + /* U+5751 "坑" */ + 0x0, 0x40, 0x40, 0x80, 0x81, 0x1, 0x3f, 0xe2, + 0x0, 0x1f, 0x0, 0x8, 0x7c, 0x10, 0x88, 0x21, + 0x10, 0x42, 0x20, 0x84, 0x41, 0xc8, 0x8e, 0x11, + 0x20, 0x42, 0x41, 0x84, 0x86, 0xe, + + /* U+5757 "块" */ + 0x20, 0x40, 0x40, 0x80, 0x81, 0x1, 0x3f, 0xcf, + 0x84, 0x84, 0x9, 0x8, 0x12, 0x10, 0x24, 0x27, + 0xfe, 0x41, 0x80, 0x82, 0x81, 0xcd, 0xc, 0x11, + 0x0, 0x41, 0x3, 0x1, 0x0, + + /* U+575A "坚" */ + 0x8, 0x0, 0x97, 0xf9, 0x24, 0x22, 0x44, 0x44, + 0x85, 0x9, 0xc, 0x12, 0x66, 0x25, 0x3, 0x1, + 0x0, 0x2, 0x1, 0xff, 0xf0, 0x8, 0x0, 0x10, + 0x1f, 0xff, 0xc0, + + /* U+575B "坛" */ + 0x20, 0x0, 0x4f, 0xf8, 0x80, 0x1, 0x0, 0xf, + 0x80, 0x4, 0x0, 0x9, 0xff, 0x90, 0x40, 0x20, + 0x80, 0x42, 0x0, 0xa4, 0x43, 0x90, 0xc8, 0x40, + 0x80, 0xff, 0x80, 0x1, 0x0, + + /* U+575C "坜" */ + 0x20, 0x0, 0x8f, 0xf2, 0x42, 0x9, 0x8, 0xfc, + 0x20, 0x97, 0xf2, 0x42, 0x49, 0x9, 0x24, 0x24, + 0x91, 0x12, 0xc4, 0x7d, 0x11, 0x4, 0x84, 0x26, + 0x30, 0xb3, 0x80, + + /* U+575D "坝" */ + 0x20, 0x0, 0x47, 0xf8, 0x88, 0x11, 0x11, 0x2f, + 0xa2, 0x44, 0x44, 0x88, 0x89, 0x11, 0x12, 0x22, + 0x24, 0x44, 0x48, 0x88, 0x91, 0xc3, 0xc, 0xd, + 0x80, 0x21, 0x83, 0x80, 0x80, + + /* U+575E "坞" */ + 0x20, 0x40, 0x82, 0x2, 0x3f, 0x88, 0x82, 0x22, + 0xb, 0xe8, 0x22, 0x27, 0x8, 0x80, 0x22, 0x0, + 0x8f, 0xf2, 0x0, 0x48, 0x1, 0x3f, 0xf7, 0x0, + 0x10, 0x0, 0x40, 0x1e, + + /* U+575F "坟" */ + 0x20, 0x40, 0x40, 0x80, 0x80, 0x1, 0x3f, 0xef, + 0xa0, 0x84, 0x41, 0x8, 0x44, 0x10, 0x88, 0x20, + 0x90, 0x41, 0x40, 0x81, 0x1, 0xc7, 0xc, 0x19, + 0x0, 0x41, 0x81, 0x0, 0x0, + + /* U+5760 "坠" */ + 0x7e, 0x20, 0x8c, 0x41, 0x10, 0x82, 0x23, 0x4, + 0x25, 0x8, 0x52, 0x17, 0x22, 0x21, 0x83, 0x40, + 0x0, 0x2, 0x1, 0xff, 0xf0, 0x8, 0x0, 0x10, + 0x1f, 0xff, 0xc0, + + /* U+5761 "坡" */ + 0x20, 0x20, 0x40, 0x40, 0x8f, 0xf1, 0x11, 0x2f, + 0xa2, 0x44, 0x44, 0x8, 0xfe, 0x11, 0x44, 0x22, + 0x88, 0x45, 0x10, 0xf1, 0x46, 0x21, 0x80, 0x47, + 0x1, 0x31, 0x82, 0x81, 0x0, + + /* U+5764 "坤" */ + 0x20, 0x20, 0x80, 0x82, 0x2, 0x8, 0xff, 0xfa, + 0x24, 0x88, 0x92, 0x3f, 0xc8, 0x89, 0x22, 0x24, + 0x88, 0x92, 0x3f, 0xde, 0x8, 0x80, 0x20, 0x0, + 0x80, 0x2, 0x0, + + /* U+5766 "坦" */ + 0x20, 0x0, 0x47, 0xf8, 0x88, 0x11, 0x10, 0x2f, + 0xa0, 0x44, 0x7f, 0x88, 0x81, 0x11, 0x2, 0x22, + 0x4, 0x44, 0x8, 0x8f, 0xf1, 0xe0, 0xc, 0x0, + 0x0, 0xff, 0xc0, + + /* U+5768 "坨" */ + 0x20, 0x40, 0x80, 0x82, 0x7f, 0xc9, 0x81, 0xfe, + 0x4, 0x80, 0x2, 0x10, 0x88, 0x46, 0x21, 0x20, + 0x87, 0x2, 0x10, 0xc, 0x41, 0xc1, 0x4, 0x4, + 0x10, 0x1f, 0x80, + + /* U+5769 "坩" */ + 0x21, 0x4, 0x21, 0x4, 0x21, 0x4, 0x27, 0xff, + 0xf9, 0x4, 0x21, 0x4, 0x21, 0x4, 0x21, 0x4, + 0x21, 0xfc, 0x21, 0x4, 0x21, 0x4, 0x3d, 0x4, + 0xc1, 0x4, 0x1, 0xfc, 0x1, 0x4, + + /* U+576A "坪" */ + 0x20, 0x0, 0x4f, 0xf8, 0x81, 0x1, 0x22, 0x2f, + 0xc4, 0x44, 0x49, 0x8, 0x12, 0x10, 0x20, 0x27, + 0xfe, 0x40, 0x80, 0x81, 0x7, 0xc2, 0x0, 0x4, + 0x0, 0x8, 0x0, 0x10, 0x0, + + /* U+576B "坫" */ + 0x20, 0x40, 0x40, 0x80, 0x81, 0x1, 0x3, 0xff, + 0x84, 0x4, 0x8, 0x8, 0x10, 0x10, 0x20, 0x23, + 0xfc, 0x44, 0x8, 0xa8, 0x17, 0x90, 0x20, 0x20, + 0x40, 0x7f, 0x80, 0x81, 0x0, + + /* U+576D "坭" */ + 0x20, 0x0, 0x47, 0xf8, 0x88, 0x11, 0x10, 0x2f, + 0xa0, 0x44, 0x7f, 0x88, 0x80, 0x11, 0x20, 0x22, + 0x44, 0x44, 0xb0, 0xb9, 0x81, 0xa2, 0x1c, 0x44, + 0x20, 0x88, 0x42, 0x1f, 0x0, + + /* U+576F "坯" */ + 0x20, 0x0, 0x4f, 0xfc, 0x81, 0x1, 0x2, 0xf, + 0x88, 0x4, 0x1a, 0x8, 0x54, 0x11, 0xa4, 0x22, + 0x44, 0x48, 0x8c, 0x81, 0x1, 0xe2, 0xc, 0x0, + 0x0, 0xff, 0xc0, + + /* U+5773 "坳" */ + 0x42, 0x11, 0x8, 0x44, 0x41, 0x11, 0x1f, 0xf9, + 0x15, 0x3c, 0x54, 0x21, 0x50, 0x85, 0x44, 0x15, + 0x14, 0x54, 0x92, 0x53, 0xc9, 0x70, 0x66, 0x1, + 0x10, 0x9, 0xc0, + + /* U+5776 "坶" */ + 0x20, 0x0, 0x43, 0xf8, 0x84, 0x11, 0x19, 0x2f, + 0xb3, 0x44, 0x60, 0x89, 0xff, 0x91, 0x2, 0x22, + 0x44, 0x44, 0x48, 0x88, 0x13, 0xdf, 0xf8, 0x0, + 0x40, 0x0, 0x80, 0xe, 0x0, + + /* U+5777 "坷" */ + 0x20, 0x0, 0x47, 0xfc, 0x80, 0x11, 0x0, 0x2f, + 0xbe, 0x44, 0x44, 0x88, 0x89, 0x11, 0x12, 0x22, + 0x24, 0x44, 0x48, 0x8f, 0x91, 0xe0, 0x2c, 0x0, + 0x40, 0x0, 0x80, 0x1e, 0x0, + + /* U+577B "坻" */ + 0x40, 0x4, 0x8f, 0xf1, 0x11, 0x2, 0x22, 0xf, + 0x44, 0x8, 0x88, 0x11, 0xff, 0xa2, 0x30, 0x44, + 0x60, 0x88, 0x41, 0x10, 0x82, 0xe1, 0x1e, 0x53, + 0x20, 0xd2, 0x40, 0x13, 0x0, + + /* U+577C "坼" */ + 0x20, 0x4, 0x47, 0xf0, 0x88, 0x1, 0x10, 0xf, + 0xa0, 0x4, 0x7f, 0xc8, 0x84, 0x11, 0x8, 0x22, + 0x90, 0x4c, 0xe1, 0xe8, 0x64, 0x20, 0xb0, 0x41, + 0x1, 0x2, 0x2, 0x4, 0x0, + + /* U+5782 "垂" */ + 0x7f, 0xfc, 0x2, 0x0, 0x4, 0x3, 0xff, 0xe1, + 0x11, 0x2, 0x22, 0x4, 0x44, 0x7f, 0xff, 0x11, + 0x10, 0x22, 0x21, 0xff, 0xf0, 0x8, 0x0, 0x10, + 0xf, 0xff, 0x80, + + /* U+5783 "垃" */ + 0x20, 0x40, 0x40, 0x80, 0x80, 0x1, 0x3f, 0xef, + 0x80, 0x4, 0x41, 0x8, 0x82, 0x11, 0x8, 0x21, + 0x10, 0x42, 0x20, 0x84, 0x83, 0xc1, 0x8, 0x2, + 0x1, 0xff, 0xc0, + + /* U+5784 "垄" */ + 0x4, 0x40, 0x8, 0x41, 0xff, 0xf8, 0x24, 0x0, + 0x8b, 0x22, 0x38, 0x49, 0xa0, 0xe0, 0x3e, 0x1, + 0x0, 0x2, 0x1, 0xff, 0xf0, 0x8, 0x0, 0x10, + 0x1f, 0xff, 0xc0, + + /* U+5785 "垅" */ + 0x21, 0x0, 0x21, 0x18, 0x21, 0x0, 0x27, 0xfe, + 0xf9, 0x20, 0x21, 0x20, 0x21, 0x24, 0x21, 0x24, + 0x21, 0x28, 0x22, 0x30, 0x2a, 0x30, 0x72, 0x62, + 0x84, 0xa2, 0x5, 0x22, 0x8, 0x3e, + + /* U+5786 "垆" */ + 0x20, 0x40, 0x40, 0xfc, 0x81, 0x1, 0x2, 0xf, + 0xbf, 0xc4, 0x40, 0x88, 0x81, 0x11, 0x2, 0x23, + 0xfc, 0x44, 0x8, 0xf0, 0x6, 0x20, 0x0, 0x40, + 0x1, 0x80, 0x2, 0x0, 0x0, + + /* U+578B "型" */ + 0x0, 0x4, 0xfe, 0x48, 0x88, 0x91, 0x11, 0x2f, + 0xfa, 0x44, 0x44, 0x88, 0x81, 0x21, 0x2, 0x2, + 0x8, 0x2, 0x1, 0xff, 0xf0, 0x8, 0x0, 0x10, + 0x1f, 0xff, 0xc0, + + /* U+578C "垌" */ + 0x20, 0x0, 0x9f, 0xf2, 0x40, 0x49, 0x1, 0xf5, + 0xf4, 0x90, 0x12, 0x4e, 0x49, 0x49, 0x25, 0x24, + 0x94, 0x92, 0x52, 0x4d, 0x39, 0xc4, 0x4, 0x10, + 0x10, 0x43, 0xc0, + + /* U+5792 "垒" */ + 0x2, 0x0, 0x8, 0x80, 0x60, 0x81, 0xff, 0xc0, + 0x0, 0x42, 0x4, 0x19, 0x12, 0x3f, 0x7e, 0x0, + 0x2, 0x2, 0x1, 0xff, 0xf0, 0x8, 0x0, 0x10, + 0x1f, 0xff, 0xc0, + + /* U+5793 "垓" */ + 0x20, 0x40, 0x40, 0x80, 0x9f, 0xf1, 0x4, 0xf, + 0x90, 0x84, 0x42, 0x9, 0xf8, 0x10, 0x22, 0x20, + 0x88, 0x46, 0x20, 0x90, 0xc1, 0xc3, 0x8c, 0x8, + 0x80, 0x60, 0xc1, 0x0, 0x0, + + /* U+579B "垛" */ + 0x20, 0x0, 0x21, 0xf8, 0x21, 0x8, 0x21, 0x8, + 0xf9, 0x8, 0x22, 0xe, 0x20, 0x40, 0x20, 0x40, + 0x27, 0xfe, 0x20, 0xe0, 0x21, 0x50, 0x3a, 0x48, + 0xc6, 0x44, 0xc, 0x42, 0x0, 0x40, + + /* U+57A0 "垠" */ + 0x20, 0x0, 0x47, 0xf8, 0x88, 0x11, 0x10, 0x22, + 0x20, 0x5f, 0x7f, 0x88, 0x81, 0x11, 0x2, 0x23, + 0xfc, 0x44, 0x80, 0x89, 0x19, 0x11, 0x43, 0xe3, + 0x18, 0x4b, 0x0, 0xe3, 0x1, 0x1, + + /* U+57A1 "垡" */ + 0x8, 0x90, 0x21, 0x10, 0xc2, 0x1, 0x7f, 0xe6, + 0x8, 0x14, 0xb, 0x88, 0x38, 0x93, 0x99, 0x21, + 0xc, 0x2, 0x1, 0xff, 0xf0, 0x8, 0x0, 0x10, + 0x1f, 0xff, 0xc0, + + /* U+57A2 "垢" */ + 0x20, 0x4, 0x47, 0xf0, 0x88, 0x1, 0x10, 0xf, + 0xbf, 0xe4, 0x40, 0x8, 0x80, 0x11, 0x0, 0x22, + 0xfc, 0x45, 0x8, 0xea, 0x17, 0x24, 0x20, 0x48, + 0x41, 0x9f, 0x82, 0x21, 0x0, + + /* U+57A3 "垣" */ + 0x20, 0x0, 0x47, 0xfc, 0x80, 0x1, 0x0, 0xf, + 0xbf, 0xc4, 0x40, 0x88, 0x81, 0x11, 0xfe, 0x22, + 0x4, 0x44, 0x8, 0x8f, 0xf3, 0xc0, 0x8, 0x0, + 0x0, 0xff, 0xc0, + + /* U+57A4 "垤" */ + 0x20, 0x0, 0x47, 0xf8, 0x82, 0x1, 0x4, 0x8f, + 0x91, 0x4, 0x41, 0x8, 0xff, 0x10, 0x22, 0x20, + 0x40, 0x47, 0xf8, 0xa1, 0x7, 0x82, 0x0, 0x4, + 0x0, 0xff, 0xc0, + + /* U+57A6 "垦" */ + 0x3f, 0xf8, 0x40, 0x10, 0x80, 0x21, 0xff, 0xc2, + 0x0, 0x87, 0xff, 0x8, 0x41, 0x10, 0x5c, 0x3e, + 0x70, 0x42, 0x1c, 0x4, 0x3, 0xff, 0xe0, 0x10, + 0x1f, 0xff, 0xc0, + + /* U+57A7 "垧" */ + 0x40, 0x80, 0x81, 0x1, 0x1f, 0xfa, 0x20, 0x1f, + 0x40, 0x28, 0xbe, 0x51, 0x44, 0xa2, 0x89, 0x45, + 0x12, 0x8a, 0x25, 0x17, 0xc9, 0xa0, 0x18, 0x40, + 0x20, 0x80, 0x81, 0xf, 0x0, + + /* U+57A9 "垩" */ + 0x7f, 0xfc, 0x8, 0x81, 0x11, 0x11, 0x22, 0x41, + 0x45, 0x0, 0x88, 0x3f, 0xff, 0x80, 0x80, 0x1, + 0x0, 0xff, 0xf8, 0x4, 0x0, 0x8, 0xf, 0xff, + 0xe0, + + /* U+57AB "垫" */ + 0x10, 0x40, 0x20, 0x81, 0xf7, 0xe0, 0x82, 0x41, + 0x54, 0x9f, 0x19, 0x4, 0x3a, 0x88, 0x85, 0x72, + 0x6, 0x2, 0x0, 0xff, 0xf0, 0x8, 0x0, 0x10, + 0x1f, 0xff, 0xc0, + + /* U+57AD "垭" */ + 0x0, 0x1, 0x1f, 0xf4, 0x12, 0x10, 0x48, 0xf9, + 0x25, 0x24, 0x94, 0x52, 0x91, 0x4a, 0x45, 0x29, + 0x14, 0xa4, 0x12, 0x1c, 0x48, 0x81, 0x20, 0x3f, + 0xf0, + + /* U+57AE "垮" */ + 0x0, 0x40, 0x80, 0x81, 0x3f, 0xf2, 0x5, 0xf, + 0x31, 0x8, 0xff, 0x92, 0x0, 0x20, 0x0, 0x4f, + 0xfc, 0x84, 0x1, 0xf, 0xe1, 0x80, 0x4c, 0x0, + 0x80, 0x1, 0x0, 0x3c, 0x0, + + /* U+57B2 "垲" */ + 0x20, 0x40, 0x48, 0x88, 0x91, 0x11, 0x1f, 0xef, + 0x80, 0x4, 0x0, 0x8, 0xff, 0x10, 0x2, 0x20, + 0x4, 0x47, 0xf8, 0x90, 0x1, 0xe0, 0xe, 0x40, + 0x20, 0x40, 0x40, 0xff, 0x0, + + /* U+57B4 "垴" */ + 0x20, 0x20, 0x20, 0x20, 0x27, 0xff, 0x20, 0x0, + 0xf8, 0x88, 0x22, 0x92, 0x22, 0x52, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x52, 0x22, 0x92, 0x3a, 0xa, + 0xc2, 0x2, 0x3, 0xfe, 0x0, 0x2, + + /* U+57B8 "垸" */ + 0x20, 0x40, 0x40, 0x80, 0x9f, 0xf1, 0x20, 0x22, + 0x0, 0x1e, 0x7f, 0x8, 0x0, 0x10, 0x0, 0x27, + 0xfc, 0x42, 0x40, 0xc4, 0x83, 0x9, 0x8, 0x22, + 0x20, 0xc4, 0x82, 0xf, 0x0, + + /* U+57C2 "埂" */ + 0x0, 0x0, 0x9f, 0xfd, 0x1, 0x2, 0x3f, 0xef, + 0x44, 0x48, 0x88, 0x91, 0xff, 0x22, 0x22, 0x44, + 0x44, 0x8f, 0xf9, 0x11, 0x3, 0x94, 0x8, 0x18, + 0x0, 0xce, 0x6, 0x3, 0x0, + + /* U+57C3 "埃" */ + 0x40, 0x80, 0x82, 0x1, 0x8, 0x42, 0x20, 0x4f, + 0x7f, 0x48, 0x40, 0x10, 0xff, 0x22, 0x20, 0x48, + 0x40, 0x80, 0x81, 0x3f, 0xf2, 0x82, 0xe, 0xa, + 0x0, 0x62, 0x3, 0x3, 0x80, + + /* U+57CB "埋" */ + 0x20, 0x0, 0x23, 0xfe, 0x22, 0x22, 0x22, 0x22, + 0xfb, 0xfe, 0x22, 0x22, 0x22, 0x22, 0x23, 0xfe, + 0x20, 0x20, 0x20, 0x20, 0x23, 0xfe, 0x78, 0x20, + 0x80, 0x20, 0x7, 0xff, + + /* U+57CE "城" */ + 0x40, 0x20, 0x80, 0x51, 0x0, 0x82, 0x3f, 0xff, + 0x42, 0x8, 0x84, 0x11, 0xe9, 0x22, 0x52, 0x44, + 0xa8, 0x89, 0x50, 0xd2, 0x46, 0x24, 0x90, 0xbb, + 0x21, 0x9, 0x84, 0x23, 0x0, + + /* U+57CF "埏" */ + 0x20, 0x0, 0x5e, 0xfc, 0x84, 0x41, 0x10, 0x8f, + 0x41, 0x5, 0xa, 0xb, 0xd7, 0x10, 0xa8, 0x2a, + 0x50, 0x54, 0xa0, 0x99, 0x41, 0xb3, 0xfc, 0x60, + 0x1, 0x3f, 0xc0, 0x0, 0x0, + + /* U+57D2 "埒" */ + 0x20, 0x0, 0x23, 0xfe, 0x20, 0x2, 0x22, 0x22, + 0xf9, 0x24, 0x21, 0x0, 0x20, 0x8, 0x20, 0x8, + 0x27, 0xff, 0x20, 0x8, 0x21, 0x8, 0x39, 0x88, + 0xc0, 0x8, 0x0, 0x8, 0x0, 0x78, + + /* U+57D4 "埔" */ + 0x0, 0x48, 0x9f, 0xfd, 0x1, 0x7, 0x82, 0x4, + 0x7f, 0xc8, 0x88, 0x91, 0x11, 0x23, 0xfe, 0x44, + 0x44, 0x6f, 0xfb, 0x11, 0x10, 0x22, 0x20, 0x44, + 0x40, 0x8b, 0x80, + + /* U+57D5 "埕" */ + 0x20, 0x0, 0x47, 0xf8, 0x88, 0x11, 0x10, 0x2f, + 0xbf, 0xc4, 0x0, 0x8, 0x0, 0x13, 0xff, 0x20, + 0x40, 0x40, 0x80, 0x8f, 0xf1, 0xc2, 0xc, 0x4, + 0x0, 0xff, 0xc0, + + /* U+57D8 "埘" */ + 0x0, 0x4, 0x47, 0x84, 0x44, 0x84, 0x44, 0xbf, + 0xf4, 0x84, 0x44, 0x84, 0x47, 0xa4, 0x44, 0xa4, + 0x44, 0x94, 0x44, 0x84, 0x4c, 0x84, 0xf7, 0x84, + 0x4, 0x84, 0x4, 0x84, 0x0, 0x1c, + + /* U+57D9 "埙" */ + 0x20, 0x0, 0x43, 0xf8, 0x84, 0x11, 0x8, 0x22, + 0x10, 0x5f, 0x3f, 0x88, 0x0, 0x11, 0xff, 0x22, + 0x2, 0x44, 0x44, 0x88, 0x89, 0x51, 0x1f, 0x27, + 0x20, 0x19, 0x0, 0xc1, 0x82, 0x0, + + /* U+57DA "埚" */ + 0x0, 0x0, 0x87, 0xf9, 0x8, 0x12, 0x10, 0x2f, + 0x20, 0x48, 0x7f, 0x90, 0x10, 0x20, 0x20, 0x47, + 0xfe, 0x88, 0x85, 0x13, 0xa, 0x25, 0x93, 0x51, + 0xb8, 0x80, 0x41, 0x0, 0x82, 0x1e, + + /* U+57DD "埝" */ + 0x10, 0x20, 0x10, 0x60, 0x10, 0x90, 0x11, 0x8, + 0x7a, 0x46, 0x14, 0x20, 0x13, 0xfc, 0x10, 0x4, + 0x10, 0x8, 0x10, 0x40, 0x11, 0x22, 0x7d, 0x2, + 0x85, 0x5, 0x1, 0x8, 0x0, 0xf8, + + /* U+57DF "域" */ + 0x0, 0x20, 0x80, 0x51, 0x0, 0x82, 0x7f, 0xef, + 0x2, 0x8, 0xf4, 0x91, 0x29, 0x22, 0x54, 0x44, + 0xa8, 0x8f, 0x61, 0x0, 0xc1, 0x85, 0x1c, 0xf5, + 0x40, 0xa, 0x80, 0x23, 0x0, + + /* U+57E0 "埠" */ + 0x0, 0x40, 0x40, 0x80, 0x8f, 0xf1, 0x10, 0x22, + 0x20, 0x5f, 0x7f, 0x88, 0x80, 0x11, 0xfe, 0x22, + 0x4, 0x44, 0x8, 0x8f, 0xf1, 0xc1, 0xc, 0x7f, + 0xe0, 0x4, 0x0, 0x8, 0x0, 0x10, + + /* U+57E4 "埤" */ + 0x20, 0x40, 0x41, 0x0, 0x8f, 0xf1, 0x12, 0x2f, + 0xa4, 0x44, 0x7f, 0x88, 0x91, 0x11, 0x22, 0x22, + 0x44, 0x47, 0xf8, 0x82, 0x1, 0x69, 0xf, 0x7f, + 0xe0, 0x4, 0x0, 0x8, 0x0, 0x10, + + /* U+57ED "埭" */ + 0x20, 0x40, 0x47, 0xf8, 0x81, 0x11, 0x2, 0x2f, + 0x7f, 0xe4, 0x8, 0x88, 0x11, 0x13, 0xfe, 0x20, + 0x40, 0x4c, 0xc8, 0x89, 0xe3, 0x8e, 0x88, 0x44, + 0xc3, 0x8, 0x40, 0xf0, 0x0, + + /* U+57EF "埯" */ + 0x40, 0x40, 0x81, 0x1, 0x3f, 0xf2, 0x9, 0xf, + 0x21, 0x9, 0x89, 0x91, 0xfe, 0x22, 0x24, 0x47, + 0xf8, 0x88, 0x91, 0x11, 0x21, 0xbf, 0xcc, 0x44, + 0x20, 0x8, 0x40, 0x1f, 0x0, + + /* U+57F4 "埴" */ + 0x20, 0x40, 0x4f, 0xfc, 0x81, 0x1, 0x2, 0xf, + 0xbf, 0xc4, 0x40, 0x88, 0xff, 0x11, 0x2, 0x22, + 0x4, 0x47, 0xf8, 0x88, 0x17, 0xdf, 0xe0, 0x20, + 0x41, 0xff, 0xc0, + + /* U+57F8 "埸" */ + 0x20, 0x0, 0x47, 0xf8, 0x88, 0x11, 0x10, 0x22, + 0x3f, 0xdf, 0x40, 0x88, 0xff, 0x10, 0x0, 0x22, + 0x0, 0x47, 0xfc, 0x92, 0x49, 0x44, 0x92, 0x92, + 0x3e, 0x44, 0x43, 0x10, 0x80, 0xce, + + /* U+57F9 "培" */ + 0x20, 0x40, 0x40, 0x40, 0x8f, 0xf9, 0x0, 0xf, + 0x90, 0x84, 0x12, 0x9, 0xff, 0x90, 0x0, 0x20, + 0x0, 0x47, 0xf8, 0x88, 0x11, 0xd0, 0x2c, 0x20, + 0x40, 0x7f, 0x80, 0x81, 0x0, + + /* U+57FA "基" */ + 0x10, 0x11, 0xff, 0xfc, 0x40, 0x40, 0xff, 0x81, + 0x1, 0x2, 0x2, 0x7, 0xfc, 0x8, 0x8, 0xff, + 0xfe, 0x12, 0x40, 0x44, 0x43, 0xff, 0xe8, 0x10, + 0x2f, 0xff, 0x80, + + /* U+57FD "埽" */ + 0x0, 0x0, 0x87, 0xf9, 0x0, 0x12, 0x1f, 0xe4, + 0x0, 0x5e, 0x0, 0x90, 0xfe, 0x27, 0xff, 0x48, + 0x42, 0x80, 0x81, 0xf, 0xf2, 0x12, 0x23, 0x24, + 0x58, 0x48, 0x80, 0x97, 0x0, 0x20, + + /* U+5800 "堀" */ + 0x0, 0x0, 0x8f, 0xfd, 0x20, 0xa, 0x40, 0x1f, + 0x7f, 0xe9, 0x4, 0x12, 0x49, 0x24, 0x92, 0x49, + 0xfc, 0x90, 0x41, 0x20, 0x83, 0xd1, 0x18, 0xa2, + 0x22, 0x7f, 0xc4, 0x0, 0x80, + + /* U+5802 "堂" */ + 0x1, 0x0, 0x62, 0x30, 0x24, 0x83, 0xff, 0xe8, + 0x0, 0x53, 0xfe, 0x84, 0x4, 0x8, 0x8, 0x1f, + 0xf0, 0x2, 0x1, 0xff, 0xf0, 0x8, 0x0, 0x10, + 0x1f, 0xff, 0xc0, + + /* U+5806 "堆" */ + 0x21, 0x0, 0x44, 0x40, 0x88, 0x1, 0x1f, 0xef, + 0xe2, 0x5, 0x44, 0xa, 0xff, 0x11, 0x10, 0x22, + 0x20, 0x47, 0xf8, 0xa8, 0x83, 0x91, 0x8, 0x22, + 0x0, 0x7f, 0xc0, 0x80, 0x0, + + /* U+5807 "堇" */ + 0x8, 0x21, 0xff, 0xfc, 0x20, 0x80, 0x7f, 0x0, + 0x10, 0x7, 0xff, 0x8, 0x42, 0x1f, 0xfc, 0x1, + 0x0, 0xff, 0xf8, 0x4, 0x1, 0xff, 0xc0, 0x10, + 0x1f, 0xff, 0xc0, + + /* U+580B "堋" */ + 0x0, 0x1, 0x1e, 0xf4, 0x4a, 0x51, 0x29, 0xf4, + 0xa5, 0x1e, 0xf4, 0x4a, 0x51, 0x29, 0x44, 0xa5, + 0x1e, 0xf4, 0x4a, 0x4d, 0x29, 0xc8, 0xa4, 0x23, + 0x10, 0x8c, 0x44, 0xf7, + + /* U+580D "堍" */ + 0x0, 0x0, 0x82, 0x1, 0xf, 0xc2, 0x30, 0x84, + 0x42, 0x1e, 0xff, 0x91, 0x11, 0x22, 0x22, 0x44, + 0x44, 0x8f, 0xf9, 0x2, 0x2, 0x6, 0x8f, 0x14, + 0x80, 0x68, 0x41, 0x91, 0xc, 0x1e, + + /* U+5811 "堑" */ + 0x10, 0x0, 0x20, 0x7f, 0xfd, 0x1, 0x2, 0x4, + 0x84, 0xf, 0xef, 0xc2, 0x22, 0x4, 0x44, 0xff, + 0x88, 0x12, 0x10, 0x0, 0x1, 0xff, 0xe0, 0x10, + 0x0, 0x20, 0x3f, 0xff, 0x80, + + /* U+5815 "堕" */ + 0x0, 0x20, 0xf8, 0x81, 0x1f, 0xfa, 0x44, 0x4, + 0x9f, 0xc9, 0x60, 0x92, 0xff, 0x22, 0x82, 0x45, + 0xfc, 0xb2, 0x9, 0x0, 0x70, 0x8, 0x7, 0xff, + 0xc0, 0x20, 0x3f, 0xff, 0x80, + + /* U+5819 "堙" */ + 0x0, 0x0, 0x9f, 0xf9, 0x2, 0x82, 0x3f, 0xef, + 0x4a, 0x48, 0x94, 0x91, 0x29, 0x23, 0xfe, 0x40, + 0x40, 0x80, 0x81, 0x1f, 0xf3, 0x82, 0x8, 0x4, + 0x1, 0xff, 0xc0, + + /* U+581E "堞" */ + 0x0, 0x48, 0x84, 0x91, 0x3f, 0xfa, 0x12, 0x4f, + 0x24, 0x88, 0x4f, 0x10, 0x80, 0x21, 0xff, 0x40, + 0x40, 0x8f, 0xfd, 0x43, 0x87, 0xe, 0x80, 0x24, + 0xc1, 0x88, 0x40, 0x10, 0x0, + + /* U+5820 "堠" */ + 0x22, 0x0, 0x45, 0xf0, 0x90, 0x21, 0x20, 0x4f, + 0xdf, 0xe5, 0x80, 0xd, 0x3f, 0x12, 0x90, 0x25, + 0x20, 0x48, 0x40, 0x97, 0xf9, 0xa3, 0xc, 0x45, + 0x0, 0x91, 0x1, 0x41, 0x80, + + /* U+5821 "堡" */ + 0x10, 0x0, 0x2f, 0xf0, 0x90, 0x23, 0x3f, 0xc6, + 0x4, 0x15, 0xff, 0xc8, 0x38, 0x11, 0x2c, 0x2c, + 0x46, 0x42, 0x80, 0x4, 0x3, 0xff, 0xe0, 0x10, + 0x1f, 0xff, 0xc0, + + /* U+5824 "堤" */ + 0x20, 0x0, 0x23, 0xfc, 0x22, 0x4, 0x23, 0xfc, + 0xfa, 0x4, 0x23, 0xfc, 0x20, 0x0, 0x27, 0xff, + 0x20, 0x20, 0x22, 0x20, 0x3a, 0x3e, 0xe2, 0x20, + 0x5, 0xa0, 0x8, 0xff, 0x0, 0x0, + + /* U+582A "堪" */ + 0x2, 0x8, 0x84, 0x11, 0x3f, 0xfa, 0x10, 0x4f, + 0x3f, 0x88, 0x41, 0x10, 0xfe, 0x21, 0x4, 0x42, + 0x8, 0x9f, 0xfd, 0x10, 0x41, 0xa4, 0x4c, 0x40, + 0x0, 0xff, 0x80, + + /* U+5830 "堰" */ + 0x20, 0x0, 0x5f, 0xfc, 0xa0, 0x1, 0x4f, 0xef, + 0x9f, 0xc5, 0x20, 0x8a, 0x7f, 0x14, 0x20, 0x2b, + 0xfe, 0x51, 0x10, 0xe1, 0xc6, 0x4e, 0x60, 0xa0, + 0x1, 0xff, 0xc0, + + /* U+5835 "堵" */ + 0x20, 0x40, 0x40, 0x84, 0x8f, 0xd1, 0x2, 0x4f, + 0x85, 0x4, 0xff, 0xc8, 0x10, 0x10, 0xc0, 0x23, + 0xfc, 0x4a, 0x8, 0x87, 0xf1, 0xe8, 0x2c, 0x10, + 0x40, 0x3f, 0x80, 0x41, 0x0, + + /* U+5844 "塄" */ + 0x0, 0x0, 0x8f, 0xf9, 0x12, 0x52, 0x24, 0xa4, + 0x49, 0x5e, 0xff, 0x90, 0x10, 0x20, 0x20, 0x4f, + 0xfe, 0x82, 0x1, 0x7, 0xe3, 0x88, 0x48, 0x20, + 0x80, 0x41, 0x1, 0x2, 0xc, 0x38, + + /* U+584C "塌" */ + 0x0, 0x1, 0x1f, 0xe4, 0x40, 0x91, 0x2, 0x47, + 0xfb, 0xd0, 0x24, 0x7f, 0x90, 0x0, 0x47, 0xfd, + 0x2, 0x14, 0x4a, 0x54, 0x21, 0xe3, 0x9c, 0x33, + 0x91, 0x8, 0x41, 0xee, + + /* U+584D "塍" */ + 0x0, 0x20, 0xf2, 0x49, 0x22, 0xa2, 0x5f, 0xf4, + 0x84, 0xf, 0x8, 0x12, 0xff, 0xa4, 0x44, 0x79, + 0x24, 0x9c, 0x45, 0x27, 0xf2, 0x41, 0x4, 0x82, + 0x11, 0x7f, 0xee, 0x0, 0x0, + + /* U+5851 "塑" */ + 0x2, 0x0, 0x44, 0xfb, 0xfd, 0x10, 0x43, 0xe4, + 0x94, 0x49, 0x28, 0x9f, 0xdf, 0x8, 0x42, 0x31, + 0x9c, 0x82, 0x0, 0xff, 0xf0, 0x8, 0x0, 0x10, + 0x1f, 0xff, 0xc0, + + /* U+5854 "塔" */ + 0x2, 0x10, 0x84, 0x21, 0x3f, 0xfa, 0x12, 0x8f, + 0xc, 0x8, 0x26, 0x11, 0x83, 0x27, 0xf8, 0x40, + 0x0, 0x80, 0x1, 0x1f, 0xe2, 0xa0, 0x4e, 0x40, + 0x80, 0xff, 0x1, 0x2, 0x0, + + /* U+5858 "塘" */ + 0x0, 0x40, 0x80, 0x81, 0x1f, 0xf2, 0x20, 0x4, + 0x44, 0x1e, 0xbf, 0x11, 0x12, 0x22, 0xff, 0x44, + 0x48, 0x8b, 0xf1, 0x11, 0x1, 0xaf, 0xcc, 0xe0, + 0x81, 0x41, 0x2, 0x7e, 0x9, 0x4, + + /* U+585E "塞" */ + 0x2, 0x0, 0x8, 0xf, 0xff, 0xe0, 0x1, 0x88, + 0x44, 0xff, 0xc0, 0x84, 0x1f, 0xfe, 0x8, 0x43, + 0xff, 0xf0, 0x84, 0x4, 0x88, 0x7f, 0xfe, 0x8, + 0x7, 0xff, 0x80, + + /* U+5865 "塥" */ + 0x0, 0x1, 0x3f, 0xf4, 0x0, 0x10, 0xfe, 0xf2, + 0x9, 0xf, 0xe4, 0x0, 0x11, 0xff, 0x45, 0x15, + 0x12, 0x94, 0x5f, 0x5d, 0x11, 0x84, 0x44, 0x11, + 0x10, 0x45, 0xc0, + + /* U+586B "填" */ + 0x20, 0x40, 0x27, 0xfe, 0x20, 0x40, 0x23, 0xfc, + 0xfa, 0x4, 0x23, 0xfc, 0x22, 0x4, 0x23, 0xfc, + 0x22, 0x4, 0x23, 0xfc, 0x3a, 0x4, 0xe7, 0xff, + 0x0, 0x0, 0x3, 0x1c, 0x4, 0x2, + + /* U+586C "塬" */ + 0x40, 0x0, 0x8f, 0xf9, 0x10, 0x82, 0x22, 0xf, + 0x5f, 0xc8, 0xa0, 0x91, 0x7f, 0x22, 0x82, 0x49, + 0x4, 0x93, 0xf9, 0x60, 0x87, 0x49, 0x40, 0xb2, + 0x43, 0xc4, 0x44, 0x38, 0x0, + + /* U+587E "塾" */ + 0x8, 0x20, 0xee, 0x41, 0xf9, 0xe2, 0x11, 0x43, + 0xca, 0x8f, 0xc9, 0x3, 0x1a, 0xff, 0xa5, 0x8, + 0x86, 0x60, 0x0, 0x4, 0x3, 0xff, 0xe0, 0x10, + 0x1f, 0xff, 0xc0, + + /* U+5880 "墀" */ + 0x0, 0x0, 0x8f, 0xfd, 0x10, 0xa, 0x3f, 0xff, + 0x40, 0x8, 0xa4, 0xd1, 0x2a, 0x22, 0xf6, 0x46, + 0x2, 0xab, 0xfc, 0xf8, 0x86, 0x41, 0x0, 0xbf, + 0xe1, 0x4, 0x4, 0x8, 0x0, + + /* U+5881 "墁" */ + 0x47, 0xfc, 0x88, 0x9, 0x1f, 0xf2, 0x20, 0x2f, + 0x7f, 0xc8, 0x0, 0x13, 0xff, 0xa4, 0x49, 0x4f, + 0xfe, 0x80, 0x1, 0x1f, 0xf3, 0x98, 0x48, 0x9, + 0x0, 0x1e, 0x7, 0xc3, 0x80, + + /* U+5883 "境" */ + 0x0, 0x40, 0x80, 0x81, 0x1f, 0xfa, 0x10, 0x44, + 0x11, 0x1f, 0xff, 0xd0, 0x0, 0x23, 0xfe, 0x44, + 0x4, 0x8f, 0xf9, 0x10, 0x11, 0xbf, 0xec, 0x12, + 0x0, 0x24, 0x40, 0x88, 0x8e, 0x1f, + + /* U+5885 "墅" */ + 0x7e, 0xfe, 0xa4, 0x9, 0xf8, 0xa2, 0x90, 0x87, + 0xef, 0xe2, 0x2, 0x5f, 0xc5, 0x8, 0x8, 0x13, + 0x11, 0xfe, 0xc0, 0x4, 0x3, 0xff, 0xe0, 0x10, + 0x1f, 0xff, 0xc0, + + /* U+5889 "墉" */ + 0x40, 0x40, 0x9f, 0xfd, 0x20, 0x2, 0x42, 0xf, + 0x9f, 0xc9, 0x3f, 0x92, 0xff, 0x24, 0x20, 0x4b, + 0xfc, 0x94, 0x89, 0xaf, 0xf4, 0x52, 0x20, 0xbf, + 0xc1, 0x48, 0x84, 0x97, 0x0, + + /* U+5892 "墒" */ + 0x0, 0x40, 0x80, 0x81, 0x3f, 0xfa, 0x10, 0x4f, + 0x11, 0x9, 0xff, 0xd2, 0x0, 0xa4, 0x99, 0x4a, + 0xa, 0x93, 0xe5, 0x24, 0x4f, 0xc8, 0x90, 0x9f, + 0x21, 0x0, 0x42, 0x7, 0x0, + + /* U+5893 "墓" */ + 0x4, 0x41, 0xff, 0xfc, 0x11, 0x1, 0xff, 0xc2, + 0x0, 0x87, 0xff, 0x8, 0x2, 0x1f, 0xfc, 0x2, + 0x1, 0xff, 0xfc, 0x64, 0xc7, 0xff, 0x70, 0x10, + 0xf, 0xff, 0x80, + + /* U+5899 "墙" */ + 0x40, 0x40, 0x9f, 0xf9, 0x1, 0x2, 0x32, 0x4f, + 0x4, 0x9, 0xff, 0xd0, 0x0, 0x23, 0xfe, 0x48, + 0x4, 0x93, 0xe8, 0xe4, 0x56, 0x4f, 0xa0, 0x80, + 0x40, 0xff, 0x82, 0x1, 0x0, + + /* U+589A "墚" */ + 0x0, 0x0, 0x45, 0xfc, 0x40, 0x24, 0x49, 0x26, + 0xf0, 0x2b, 0x40, 0x48, 0x44, 0x88, 0x49, 0x38, + 0x40, 0x40, 0x4f, 0xfe, 0x40, 0xe0, 0xe1, 0x50, + 0x6, 0x4c, 0x18, 0x42, 0x0, 0x40, + + /* U+589E "增" */ + 0x22, 0x8, 0x42, 0x20, 0xbf, 0xf9, 0x42, 0x1f, + 0x94, 0xa5, 0x2a, 0x4b, 0xff, 0x90, 0x0, 0x27, + 0xfc, 0x48, 0x8, 0x90, 0x11, 0xbf, 0xec, 0x40, + 0x40, 0xff, 0x81, 0x1, 0x0, + + /* U+589F "墟" */ + 0x40, 0x40, 0x80, 0xf9, 0x1, 0x2, 0x3f, 0xef, + 0x44, 0x48, 0x8e, 0x11, 0x31, 0x22, 0x3c, 0x44, + 0x0, 0x88, 0xa1, 0x11, 0x57, 0xaa, 0xa0, 0x95, + 0x81, 0xa, 0x4, 0xff, 0x80, + + /* U+58A8 "墨" */ + 0x7f, 0xfc, 0x82, 0x9, 0x24, 0x92, 0x8, 0x27, + 0xff, 0xc0, 0x20, 0x1f, 0xff, 0x0, 0x80, 0xff, + 0xfe, 0x80, 0x12, 0x15, 0x13, 0xff, 0xe0, 0x10, + 0x0, 0x20, 0x3f, 0xff, 0x80, + + /* U+58A9 "墩" */ + 0x22, 0x10, 0x20, 0x10, 0x2f, 0xd0, 0x20, 0x1f, + 0xf7, 0x94, 0x24, 0xb4, 0x27, 0xb4, 0x20, 0x14, + 0x27, 0xd8, 0x20, 0x88, 0x33, 0x8, 0xc7, 0xc8, + 0xa, 0x14, 0x2, 0x22, 0x6, 0x40, + + /* U+58BC "墼" */ + 0x8, 0x1, 0xfe, 0xf8, 0x21, 0x13, 0xfe, 0x37, + 0xf0, 0x9, 0x2f, 0x9f, 0xd1, 0x4, 0x14, 0x7f, + 0x38, 0xff, 0x8c, 0x0, 0x3, 0xff, 0xe0, 0x10, + 0x0, 0x20, 0x3f, 0xff, 0x80, + + /* U+58C1 "壁" */ + 0x0, 0x20, 0xfc, 0x21, 0xb, 0xfa, 0x12, 0x27, + 0xcf, 0xef, 0xc4, 0x18, 0x88, 0x51, 0x7e, 0xbe, + 0x20, 0x0, 0x0, 0x4, 0x1, 0xff, 0xc0, 0x10, + 0x0, 0x20, 0x3f, 0xff, 0x80, + + /* U+58C5 "壅" */ + 0x0, 0x0, 0x2, 0x3, 0xff, 0xf8, 0x85, 0x2, + 0x11, 0x9, 0x3f, 0xfc, 0xc4, 0x12, 0xfe, 0x79, + 0xfe, 0x22, 0x20, 0x87, 0xfa, 0x8, 0x7, 0xff, + 0xc0, 0x20, 0x3f, 0xff, 0x80, + + /* U+58D1 "壑" */ + 0xf, 0x0, 0x10, 0xf9, 0xff, 0x14, 0x5, 0x2b, + 0xea, 0x84, 0x45, 0x37, 0x66, 0x3f, 0xcc, 0xa1, + 0x64, 0x7e, 0x84, 0x4, 0x3, 0xff, 0xe0, 0x10, + 0x1f, 0xff, 0xc0, + + /* U+58D5 "壕" */ + 0x40, 0x80, 0x9f, 0xf9, 0x0, 0x2, 0x3f, 0xcf, + 0x7f, 0x88, 0x0, 0x17, 0xff, 0x23, 0xfe, 0x40, + 0x80, 0x9e, 0xb1, 0x7, 0xc7, 0xb3, 0x80, 0x1a, + 0x81, 0xc4, 0x80, 0x70, 0x0, + + /* U+58E4 "壤" */ + 0x20, 0x40, 0x5f, 0xfc, 0x80, 0x1, 0x7d, 0xef, + 0xfb, 0xc4, 0x0, 0xb, 0xff, 0x11, 0x8, 0x2f, + 0xfc, 0x44, 0x20, 0xbf, 0xf9, 0x8e, 0x2c, 0x63, + 0x81, 0x4b, 0x0, 0xe1, 0x80, + + /* U+58EB "士" */ + 0x1, 0x0, 0x2, 0x0, 0x4, 0x0, 0x8, 0x0, + 0x10, 0x1f, 0xff, 0xc0, 0x40, 0x0, 0x80, 0x1, + 0x0, 0x2, 0x0, 0x4, 0x0, 0x8, 0x0, 0x10, + 0xf, 0xff, 0x80, + + /* U+58EC "壬" */ + 0x0, 0xc, 0x7f, 0xc0, 0x4, 0x0, 0x8, 0x0, + 0x10, 0x0, 0x20, 0x0, 0x40, 0x7f, 0xff, 0x1, + 0x0, 0x2, 0x0, 0x4, 0x0, 0x8, 0x0, 0x10, + 0xf, 0xff, 0x80, + + /* U+58EE "壮" */ + 0x8, 0x20, 0x10, 0x40, 0x20, 0x82, 0x41, 0x4, + 0x82, 0x5, 0x7f, 0xca, 0x8, 0x4, 0x10, 0x18, + 0x20, 0x70, 0x41, 0x20, 0x80, 0x41, 0x0, 0x82, + 0x1, 0x7f, 0xc2, 0x0, 0x0, + + /* U+58F0 "声" */ + 0x1, 0x1, 0xff, 0xfc, 0x4, 0x0, 0x8, 0x7, + 0xff, 0xc0, 0x0, 0xf, 0xfe, 0x10, 0x84, 0x21, + 0x8, 0x42, 0x10, 0xff, 0xe2, 0x0, 0x4, 0x0, + 0x10, 0x0, 0x0, 0x0, 0x0, + + /* U+58F3 "壳" */ + 0x1, 0x1, 0xff, 0xfc, 0x4, 0x0, 0x8, 0x7, + 0xff, 0xc0, 0x0, 0x3f, 0xff, 0xc0, 0x1, 0x80, + 0x2, 0x1f, 0x80, 0x21, 0x0, 0x42, 0x11, 0x4, + 0x26, 0x8, 0x78, 0xf, 0x0, 0x0, + + /* U+58F6 "壶" */ + 0x1, 0x1, 0xff, 0xfc, 0x4, 0x0, 0x8, 0x7, + 0xff, 0xc0, 0x0, 0x1f, 0xff, 0x40, 0x2, 0x4, + 0x40, 0x48, 0x90, 0x51, 0x40, 0xa2, 0x80, 0x44, + 0x1f, 0xff, 0xc0, + + /* U+58F9 "壹" */ + 0x1, 0x1, 0xff, 0xfc, 0x4, 0x3, 0xff, 0xe0, + 0x0, 0x1f, 0xff, 0xe0, 0x0, 0xdf, 0xfd, 0x0, + 0x0, 0x7f, 0xf0, 0x80, 0x21, 0xff, 0xc0, 0xc2, + 0x1f, 0xff, 0xc0, + + /* U+5902 "夂" */ + 0x4, 0x0, 0x20, 0x0, 0x80, 0x7, 0xfc, 0x10, + 0x10, 0xa0, 0x44, 0x82, 0x33, 0x18, 0x4, 0x40, + 0xa, 0x0, 0x10, 0x0, 0xa0, 0xc, 0x60, 0xc0, + 0x6c, 0x0, 0x40, + + /* U+5904 "处" */ + 0x10, 0x20, 0x10, 0x20, 0x20, 0x20, 0x3f, 0x20, + 0x22, 0x30, 0x62, 0x28, 0x62, 0x26, 0xa2, 0x22, + 0x14, 0x20, 0x14, 0x20, 0x8, 0x20, 0x14, 0x20, + 0x12, 0x0, 0x21, 0xff, 0x40, 0x7f, 0x0, 0x0, + + /* U+5907 "备" */ + 0x0, 0x0, 0x10, 0x0, 0x7f, 0xe1, 0xc0, 0xc6, + 0x42, 0x18, 0x48, 0x0, 0xe0, 0x1e, 0x3f, 0xc0, + 0x0, 0x7f, 0xf0, 0x84, 0x21, 0x8, 0x43, 0xff, + 0x84, 0x21, 0xf, 0xfe, 0x10, 0x4, + + /* U+590D "复" */ + 0x10, 0x0, 0x7f, 0xfc, 0x80, 0x7, 0xff, 0xca, + 0x0, 0x87, 0xff, 0x8, 0x2, 0x1f, 0xfc, 0x0, + 0x0, 0x10, 0x0, 0x7f, 0xe1, 0x40, 0x8c, 0x66, + 0x0, 0x70, 0xf, 0x1f, 0xa0, 0x0, + + /* U+590F "夏" */ + 0xff, 0xfe, 0x4, 0x0, 0xff, 0xe1, 0x0, 0x43, + 0xff, 0x84, 0x1, 0xf, 0xfe, 0x10, 0x4, 0x3f, + 0xf8, 0x10, 0x0, 0x7f, 0xe1, 0x40, 0xcc, 0x66, + 0x0, 0x78, 0x3f, 0xf, 0x80, + + /* U+5914 "夔" */ + 0x0, 0x0, 0x8, 0x43, 0xff, 0xf8, 0x8, 0x1, + 0x3d, 0xca, 0x8a, 0x95, 0xf7, 0x29, 0xe9, 0x5c, + 0x53, 0xc7, 0xb8, 0x48, 0x41, 0x3f, 0x6c, 0xc2, + 0x22, 0x68, 0x0, 0xf0, 0x7e, 0x1f, + + /* U+5915 "夕" */ + 0x2, 0x0, 0x8, 0x0, 0x40, 0x3, 0xff, 0x8, + 0x4, 0x40, 0x12, 0x80, 0x91, 0x6, 0x42, 0x10, + 0x4, 0x80, 0x4, 0x0, 0x60, 0x3, 0x0, 0x30, + 0x7, 0x0, 0x0, + + /* U+5916 "外" */ + 0x8, 0x20, 0x10, 0x20, 0x10, 0x20, 0x1f, 0x20, + 0x21, 0x20, 0x21, 0x30, 0x61, 0x28, 0x52, 0x26, + 0x8a, 0x22, 0x4, 0x20, 0x4, 0x20, 0x8, 0x20, + 0x10, 0x20, 0x20, 0x20, 0x40, 0x20, + + /* U+5919 "夙" */ + 0x7f, 0xf8, 0x40, 0x8, 0x5f, 0xe8, 0x42, 0x8, + 0x44, 0x8, 0x47, 0xc8, 0x48, 0x48, 0x58, 0x48, + 0x76, 0x88, 0x40, 0x88, 0x41, 0xa, 0xc2, 0xa, + 0x8c, 0xe, 0x90, 0x6, + + /* U+591A "多" */ + 0x1, 0x0, 0x8, 0x0, 0xff, 0x84, 0x6, 0x6c, + 0x20, 0x3, 0x0, 0x30, 0x7, 0x60, 0xe3, 0xfc, + 0x30, 0x33, 0xc1, 0x98, 0xc8, 0x0, 0xc0, 0xc, + 0x3, 0xc0, 0x30, 0x0, + + /* U+591C "夜" */ + 0x1, 0x0, 0x1, 0x3, 0xff, 0xf8, 0x0, 0x1, + 0x10, 0x2, 0x3f, 0x88, 0x82, 0x33, 0x24, 0x66, + 0x29, 0x52, 0x20, 0x82, 0x81, 0x2, 0x2, 0xa, + 0x4, 0x63, 0x8b, 0x0, 0x80, + + /* U+591F "够" */ + 0x10, 0x10, 0x40, 0x40, 0xf9, 0x7a, 0x16, 0x28, + 0x22, 0x9f, 0x42, 0x12, 0x98, 0x25, 0x48, 0x4a, + 0x3e, 0x94, 0x85, 0xea, 0x8a, 0x10, 0x20, 0x21, + 0x80, 0x46, 0x7, 0x70, 0x0, + + /* U+5924 "夤" */ + 0x4, 0x0, 0x7f, 0xcf, 0x6, 0x3, 0xe0, 0x39, + 0x3, 0xff, 0xf8, 0x0, 0x4f, 0xf8, 0x7f, 0xf1, + 0x8, 0x47, 0xff, 0x10, 0x84, 0x7f, 0xf0, 0x22, + 0xf, 0x7, 0xc0, + + /* U+5925 "夥" */ + 0x0, 0x10, 0xfc, 0x22, 0x48, 0xfc, 0x92, 0x17, + 0xea, 0x52, 0x47, 0x24, 0x84, 0x3f, 0x10, 0x10, + 0xd0, 0x20, 0x3f, 0xfc, 0x88, 0x82, 0x13, 0xca, + 0x4a, 0x43, 0x24, 0x8, 0x8, 0xc0, + + /* U+5927 "大" */ + 0x1, 0x0, 0x2, 0x0, 0x4, 0x0, 0x8, 0xf, + 0xff, 0xe0, 0x20, 0x0, 0x40, 0x1, 0x40, 0x2, + 0x80, 0xc, 0x80, 0x11, 0x0, 0x41, 0x83, 0x1, + 0x8c, 0x0, 0xe0, 0x0, 0x0, + + /* U+5929 "天" */ + 0x7f, 0xfc, 0x2, 0x0, 0x4, 0x0, 0x8, 0x0, + 0x10, 0x1f, 0xff, 0xc0, 0x40, 0x1, 0x40, 0x2, + 0x80, 0x8, 0x80, 0x31, 0x80, 0xc1, 0x82, 0x0, + 0xd8, 0x0, 0x40, + + /* U+592A "太" */ + 0x1, 0x0, 0x2, 0x0, 0x4, 0x0, 0x8, 0xf, + 0xff, 0xe0, 0x20, 0x0, 0xc0, 0x1, 0x40, 0x2, + 0x80, 0x8, 0x80, 0x11, 0x0, 0x61, 0x1, 0x21, + 0xc, 0x21, 0xa0, 0x0, 0x80, + + /* U+592B "夫" */ + 0x1, 0x0, 0x2, 0x0, 0x4, 0x3, 0xff, 0xe0, + 0x10, 0x0, 0x20, 0x0, 0x40, 0x7f, 0xff, 0x1, + 0x0, 0x5, 0x0, 0xa, 0x0, 0x22, 0x0, 0x82, + 0x6, 0x3, 0x30, 0x1, 0x80, + + /* U+592D "夭" */ + 0x0, 0xc, 0xff, 0xf0, 0x4, 0x0, 0x8, 0x0, + 0x10, 0x0, 0x20, 0x3f, 0xff, 0x80, 0x80, 0x2, + 0x80, 0x5, 0x0, 0x11, 0x0, 0x22, 0x1, 0x83, + 0xc, 0x1, 0xb0, 0x0, 0x80, + + /* U+592E "央" */ + 0x1, 0x0, 0x2, 0x0, 0x4, 0x1, 0xff, 0xc2, + 0x10, 0x84, 0x21, 0x8, 0x42, 0x10, 0x84, 0xff, + 0xfe, 0x2, 0x0, 0xa, 0x0, 0x22, 0x0, 0xc2, + 0x6, 0x3, 0xb0, 0x1, 0x80, + + /* U+592F "夯" */ + 0x2, 0x0, 0x8, 0xf, 0xff, 0xc0, 0xa0, 0x4, + 0xc0, 0x61, 0xce, 0x40, 0xc1, 0x0, 0xff, 0xf8, + 0x10, 0x20, 0x40, 0x82, 0x2, 0x8, 0x8, 0xc0, + 0x4c, 0x1f, 0x0, + + /* U+5931 "失" */ + 0x1, 0x0, 0x22, 0x0, 0x44, 0x1, 0xff, 0xe6, + 0x10, 0x8, 0x20, 0x0, 0x40, 0x7f, 0xff, 0x1, + 0x0, 0x5, 0x0, 0xa, 0x0, 0x22, 0x1, 0x83, + 0xc, 0x1, 0xe0, 0x0, 0x0, + + /* U+5934 "头" */ + 0x10, 0x80, 0x11, 0x0, 0x1a, 0x0, 0x4, 0x6, + 0x8, 0x3, 0x10, 0x2, 0x20, 0x0, 0x40, 0xff, + 0xfe, 0x1, 0x0, 0x7, 0x0, 0x11, 0x0, 0xc1, + 0x8e, 0x0, 0xc0, 0x0, 0x0, + + /* U+5937 "夷" */ + 0x1, 0x1, 0xff, 0xfc, 0x4, 0x0, 0x8, 0x3, + 0xff, 0xc0, 0x20, 0x8f, 0xff, 0x10, 0x80, 0x21, + 0x0, 0xff, 0xfc, 0x6, 0x8, 0x14, 0xe0, 0x46, + 0x7, 0x3, 0x30, 0x1, 0x80, + + /* U+5938 "夸" */ + 0x1, 0x0, 0x8, 0xf, 0xff, 0xc1, 0x20, 0x8, + 0x40, 0x40, 0x86, 0xfd, 0xa0, 0x1, 0x0, 0x3, + 0xff, 0xe1, 0x0, 0x7, 0xf8, 0x0, 0x20, 0x0, + 0x80, 0x2, 0x0, 0xf0, + + /* U+5939 "夹" */ + 0x1, 0x0, 0x2, 0x1, 0xff, 0xf0, 0x8, 0x2, + 0x10, 0x82, 0x22, 0x0, 0x48, 0x0, 0x80, 0xff, + 0xfe, 0x6, 0x0, 0xa, 0x0, 0x36, 0x0, 0xc6, + 0x6, 0x3, 0x30, 0x1, 0x80, + + /* U+593A "夺" */ + 0x1, 0x0, 0x2, 0x3, 0xff, 0xf8, 0x14, 0x0, + 0x44, 0x3, 0x6, 0x38, 0x3, 0x80, 0x10, 0xff, + 0xfe, 0x0, 0x40, 0xc0, 0x80, 0xc1, 0x0, 0x42, + 0x0, 0x4, 0x0, 0x78, 0x0, + + /* U+593C "夼" */ + 0x0, 0x0, 0x2, 0x0, 0x4, 0x3, 0xff, 0xf0, + 0x24, 0x0, 0x48, 0x1, 0x8, 0xc, 0xc, 0x60, + 0x6, 0x22, 0x10, 0x44, 0x20, 0x88, 0x41, 0x10, + 0x82, 0x21, 0x8, 0x42, 0x20, 0x4, + + /* U+5941 "奁" */ + 0x1, 0x0, 0x8, 0xf, 0xff, 0xc1, 0x20, 0x18, + 0x61, 0xff, 0xfc, 0x0, 0x13, 0x8, 0x42, 0x41, + 0x6, 0x4, 0x26, 0x12, 0x4, 0x40, 0x0, 0xff, + 0xe0, + + /* U+5942 "奂" */ + 0x8, 0x0, 0x20, 0x1, 0xfc, 0x8, 0x10, 0x40, + 0x82, 0xff, 0xc4, 0x21, 0x10, 0x84, 0x42, 0x11, + 0x8, 0x4f, 0xff, 0xc0, 0xc0, 0x5, 0x80, 0x33, + 0x3, 0x3, 0xb0, 0x1, + + /* U+5944 "奄" */ + 0x0, 0x80, 0x1, 0x0, 0x7f, 0xfe, 0x2, 0x40, + 0xd, 0x30, 0x31, 0xc, 0x7f, 0xfa, 0x21, 0x8, + 0x3f, 0xf8, 0x21, 0x8, 0x21, 0x8, 0x3f, 0xf9, + 0x21, 0x3, 0x1, 0x2, 0x0, 0xfe, + + /* U+5947 "奇" */ + 0x1, 0x0, 0x2, 0x1, 0xff, 0xf0, 0x18, 0x0, + 0xcf, 0xe, 0x1, 0x3f, 0xff, 0x80, 0x4, 0x3f, + 0x88, 0x41, 0x10, 0x82, 0x21, 0x4, 0x43, 0xf8, + 0x80, 0x3, 0x0, 0x3c, 0x0, + + /* U+5948 "奈" */ + 0x0, 0x80, 0x2, 0x1, 0xff, 0xf8, 0x12, 0x0, + 0x42, 0x3, 0x3, 0x19, 0xf9, 0x80, 0x0, 0x0, + 0x0, 0xff, 0xfc, 0x2, 0x0, 0x44, 0x81, 0x8, + 0x8c, 0x10, 0xc1, 0xc0, 0x0, + + /* U+5949 "奉" */ + 0x0, 0x80, 0x1, 0x0, 0x7f, 0xfe, 0x1, 0x0, + 0x1, 0x0, 0x3f, 0xfc, 0x2, 0x0, 0x7f, 0xfe, + 0xc, 0x20, 0x11, 0x18, 0x2f, 0xf4, 0x41, 0x2, + 0x3f, 0xfc, 0x1, 0x0, 0x1, 0x0, 0x1, 0x0, + + /* U+594B "奋" */ + 0x0, 0x80, 0x1, 0x0, 0x7f, 0xfe, 0x2, 0x40, + 0x4, 0x20, 0x18, 0x18, 0xff, 0xff, 0x21, 0x8, + 0x21, 0x8, 0x21, 0x8, 0x1f, 0xf8, 0x21, 0x8, + 0x21, 0x8, 0x1f, 0xf8, 0x20, 0x8, + + /* U+594E "奎" */ + 0x1, 0x0, 0x2, 0x1, 0xff, 0xf8, 0x21, 0x0, + 0x81, 0x6, 0x21, 0x93, 0xfc, 0x80, 0x80, 0x1, + 0x0, 0xff, 0xf8, 0x0, 0x0, 0xff, 0xc0, 0x10, + 0x0, 0x20, 0x1f, 0xff, 0x80, + + /* U+594F "奏" */ + 0x1, 0x0, 0x2, 0x1, 0xff, 0xf0, 0x10, 0x0, + 0x20, 0xf, 0xff, 0x81, 0x80, 0x7f, 0xff, 0x8, + 0x20, 0x7f, 0xf2, 0x4, 0x19, 0xff, 0xc0, 0x10, + 0x0, 0x58, 0x7, 0xe, 0x30, 0x0, + + /* U+5951 "契" */ + 0x10, 0x3, 0xff, 0xe1, 0x4, 0x84, 0x12, 0x7c, + 0x48, 0x42, 0x27, 0xd8, 0x84, 0xc, 0x1, 0x0, + 0x8, 0xf, 0xff, 0xc0, 0xc0, 0x4, 0x80, 0x61, + 0x8e, 0x1, 0xc0, + + /* U+5954 "奔" */ + 0x1, 0x0, 0x2, 0x3, 0xff, 0xf8, 0x12, 0x0, + 0xd3, 0x1e, 0x21, 0xc7, 0xfe, 0x0, 0x80, 0x1, + 0x0, 0x10, 0x23, 0xff, 0xf8, 0x40, 0x81, 0x1, + 0x6, 0x2, 0x10, 0x4, 0x0, + + /* U+5955 "奕" */ + 0x2, 0x0, 0x2, 0x3, 0xff, 0xf8, 0x22, 0x2, + 0x44, 0x9, 0x9, 0xa2, 0x10, 0x88, 0xe0, 0x1, + 0x1, 0xff, 0xfc, 0xc, 0x0, 0x24, 0x1, 0x86, + 0x1c, 0x3, 0xc0, + + /* U+5956 "奖" */ + 0x8, 0x40, 0x11, 0x1, 0x27, 0xf1, 0x58, 0x40, + 0xe9, 0x87, 0x4, 0x32, 0x30, 0x5, 0x80, 0x1, + 0x1, 0xff, 0xfc, 0xc, 0x0, 0x14, 0x0, 0xc6, + 0xe, 0x7, 0xa0, 0x0, 0x80, + + /* U+5957 "套" */ + 0x2, 0x0, 0x4, 0x3, 0xff, 0xf8, 0x62, 0x1, + 0x2, 0xf, 0xff, 0xa4, 0x0, 0x8f, 0xf8, 0x10, + 0x0, 0x3f, 0xe0, 0x40, 0x7, 0xff, 0xf0, 0x84, + 0x2, 0x4, 0xf, 0xfe, 0x0, 0x4, + + /* U+5958 "奘" */ + 0x44, 0x20, 0x88, 0x41, 0xf7, 0xf8, 0x21, 0xf, + 0xc2, 0x4, 0x84, 0x9, 0x7f, 0xe2, 0x0, 0x1, + 0x1, 0xff, 0xfc, 0xc, 0x0, 0x14, 0x0, 0xc6, + 0xe, 0x3, 0xe0, 0x0, 0x0, + + /* U+595A "奚" */ + 0x7f, 0xf8, 0x8, 0x22, 0x11, 0x7, 0x8, 0x7f, + 0xc0, 0x8, 0x1, 0xc2, 0x1f, 0xfc, 0x60, 0xb, + 0xff, 0xf0, 0x30, 0x3, 0x30, 0x70, 0x3e, 0x0, + 0x0, + + /* U+5960 "奠" */ + 0x8, 0x0, 0x8, 0x83, 0xff, 0xf8, 0x24, 0x7, + 0xff, 0x88, 0x91, 0x12, 0x3e, 0x28, 0x4, 0x7f, + 0xf8, 0x80, 0x11, 0xff, 0xe0, 0x8, 0xf, 0xff, + 0xe0, 0x60, 0x3, 0x30, 0x78, 0x1f, + + /* U+5962 "奢" */ + 0x1, 0x0, 0x2, 0x1, 0xff, 0xf8, 0x21, 0x1, + 0x91, 0x8d, 0xfc, 0xc0, 0x46, 0x0, 0x98, 0x7f, + 0xfe, 0x2, 0x0, 0x3f, 0xe7, 0x80, 0x40, 0xff, + 0x82, 0x1, 0x3, 0xfe, 0x8, 0x4, + + /* U+5965 "奥" */ + 0x2, 0x0, 0x8, 0x7, 0xdf, 0x90, 0x82, 0x4e, + 0x49, 0x8, 0x24, 0xfe, 0x91, 0x62, 0x5a, 0x68, + 0x8, 0xf, 0xff, 0xc0, 0xc0, 0x4, 0xc0, 0xe0, + 0xec, 0x0, 0x40, + + /* U+5973 "女" */ + 0x2, 0x0, 0x8, 0x0, 0x10, 0x0, 0x20, 0xf, + 0xff, 0xe1, 0x2, 0x2, 0x8, 0x8, 0x10, 0x10, + 0x20, 0x60, 0x80, 0x33, 0x0, 0x1c, 0x0, 0x3c, + 0x1, 0x86, 0x3c, 0x3, 0x0, + + /* U+5974 "奴" */ + 0x20, 0x0, 0x43, 0xf8, 0x82, 0x17, 0xe4, 0x22, + 0x48, 0x48, 0x90, 0x91, 0x22, 0x24, 0x24, 0x48, + 0x48, 0x50, 0x60, 0x60, 0xc0, 0xe3, 0x82, 0x8, + 0x98, 0x60, 0xc1, 0x0, 0x0, + + /* U+5976 "奶" */ + 0x20, 0x0, 0x47, 0xf0, 0x82, 0x27, 0xe4, 0x42, + 0x48, 0x84, 0x91, 0x11, 0x23, 0xa2, 0x41, 0x48, + 0x82, 0x91, 0x4, 0xe4, 0x8, 0xc8, 0x12, 0x50, + 0x2c, 0x40, 0xb1, 0x8f, 0x0, + + /* U+5978 "奸" */ + 0x20, 0x0, 0x47, 0xf8, 0x81, 0x7, 0xc2, 0x4, + 0x84, 0x9, 0x8, 0x12, 0x10, 0x25, 0xff, 0x48, + 0x40, 0xa0, 0x80, 0xc1, 0x0, 0x82, 0x2, 0x84, + 0x8, 0x88, 0x20, 0x10, 0x0, + + /* U+5979 "她" */ + 0x20, 0x20, 0x40, 0x40, 0x84, 0x81, 0x9, 0x2d, + 0x93, 0xc9, 0x7c, 0x92, 0x49, 0x24, 0x92, 0x91, + 0x3c, 0xa2, 0x40, 0xc4, 0x89, 0x88, 0x14, 0x90, + 0x28, 0x20, 0xe0, 0x3f, 0x0, 0x0, + + /* U+597D "好" */ + 0x20, 0x0, 0x47, 0xf9, 0x0, 0x37, 0xc0, 0x84, + 0x82, 0x9, 0x4, 0x12, 0x8, 0x27, 0xff, 0x90, + 0x20, 0xa0, 0x40, 0xc0, 0x81, 0x81, 0x2, 0xc2, + 0x8, 0x4, 0x20, 0x70, 0x0, + + /* U+5981 "妁" */ + 0x20, 0x80, 0x82, 0x2, 0x1f, 0xfe, 0x41, 0x4a, + 0x5, 0x28, 0x14, 0x90, 0x52, 0x21, 0x88, 0x45, + 0x41, 0x13, 0x0, 0x4c, 0x1, 0x28, 0x5, 0x0, + 0x38, 0xf, 0x80, + + /* U+5982 "如" */ + 0x10, 0x0, 0x40, 0x2, 0x7, 0xff, 0x91, 0x22, + 0x44, 0x89, 0x14, 0x24, 0x51, 0x11, 0x44, 0x44, + 0xd1, 0x11, 0x84, 0x45, 0x11, 0x32, 0x7d, 0x81, + 0x1c, 0x4, 0x0, 0x0, + + /* U+5983 "妃" */ + 0x20, 0x0, 0x43, 0xf8, 0x80, 0x17, 0xe0, 0x22, + 0x40, 0x48, 0x80, 0x91, 0x7f, 0x24, 0x80, 0x49, + 0x0, 0x52, 0x0, 0x44, 0x9, 0xe8, 0x16, 0x10, + 0x38, 0x1f, 0x80, + + /* U+5984 "妄" */ + 0x1, 0x0, 0x1, 0x3, 0xff, 0xf9, 0x0, 0x2, + 0x0, 0x7, 0xff, 0x80, 0x0, 0x2, 0x0, 0xff, + 0xfe, 0x10, 0x20, 0x40, 0x80, 0x72, 0x0, 0x1c, + 0x3, 0xc7, 0x98, 0x0, 0x0, + + /* U+5986 "妆" */ + 0x8, 0x20, 0x8, 0x20, 0x8, 0x40, 0x48, 0x40, + 0x4b, 0xff, 0x28, 0x44, 0x28, 0x84, 0x8, 0x84, + 0x9, 0x4, 0x19, 0x88, 0x28, 0x58, 0x48, 0x30, + 0x8, 0x68, 0x9, 0x86, 0xa, 0x1, + + /* U+5987 "妇" */ + 0x20, 0x0, 0x87, 0xf2, 0x0, 0x7f, 0x1, 0x44, + 0x5, 0x10, 0x14, 0xc0, 0x52, 0x7f, 0x88, 0x5, + 0xa0, 0x11, 0x0, 0x47, 0x1, 0x24, 0x5, 0xf, + 0xf8, 0x0, 0x40, + + /* U+5988 "妈" */ + 0x20, 0x0, 0x8f, 0xe4, 0x0, 0xbe, 0x42, 0x49, + 0x9, 0x28, 0x24, 0xa1, 0x24, 0xff, 0x90, 0x5, + 0x40, 0x13, 0x7f, 0x4c, 0x1, 0x68, 0x7, 0x0, + 0x38, 0x7, 0x80, + + /* U+598A "妊" */ + 0x20, 0x0, 0x47, 0xf9, 0x0, 0x87, 0xc1, 0x4, + 0x82, 0x9, 0x4, 0x12, 0x8, 0x45, 0xff, 0x90, + 0x20, 0xa0, 0x40, 0xc0, 0x81, 0x81, 0x6, 0x82, + 0x18, 0x7f, 0xe0, 0x0, 0x0, + + /* U+598D "妍" */ + 0x20, 0x0, 0x47, 0xfd, 0x2, 0x27, 0xc4, 0x44, + 0x88, 0x89, 0x11, 0x12, 0x22, 0x27, 0xff, 0x90, + 0x88, 0xa1, 0x10, 0xc2, 0x21, 0x88, 0x42, 0x90, + 0x88, 0x41, 0x21, 0x2, 0x0, + + /* U+5992 "妒" */ + 0x20, 0x0, 0x80, 0x82, 0x1f, 0xfe, 0x41, 0x49, + 0x5, 0x24, 0x14, 0x90, 0x52, 0x7f, 0x89, 0x5, + 0xc4, 0x1, 0x10, 0xa, 0x80, 0x62, 0x3, 0x10, + 0x8, 0x40, 0x0, + + /* U+5993 "妓" */ + 0x20, 0x20, 0x40, 0x41, 0xf, 0xff, 0xc1, 0x4, + 0x82, 0x9, 0x4, 0x12, 0xff, 0x24, 0x82, 0x91, + 0x8, 0xa1, 0x10, 0xc1, 0x41, 0xc3, 0x86, 0xc7, + 0x8, 0x31, 0xa1, 0x80, 0x80, + + /* U+5996 "妖" */ + 0x20, 0x5, 0xf, 0xc4, 0x4, 0x3e, 0x10, 0x48, + 0x41, 0x21, 0x4, 0xff, 0xe4, 0x10, 0x90, 0xc1, + 0x42, 0x82, 0xa, 0xe, 0x44, 0x4a, 0x1a, 0x18, + 0x30, 0x0, 0x0, + + /* U+5997 "妗" */ + 0x20, 0x20, 0x40, 0xa0, 0x81, 0x47, 0xc4, 0x44, + 0x90, 0x49, 0x44, 0x52, 0x4, 0x24, 0x0, 0x91, + 0xfc, 0xa0, 0x8, 0xc0, 0x21, 0xc0, 0x44, 0xc1, + 0x18, 0x6, 0x20, 0x8, 0x0, + + /* U+5999 "妙" */ + 0x40, 0x41, 0x1, 0x4, 0x4, 0x3e, 0x54, 0x4a, + 0x49, 0x29, 0x19, 0xc4, 0x65, 0x12, 0x50, 0x48, + 0x80, 0x43, 0x2, 0x12, 0x10, 0xc1, 0x82, 0x38, + 0x0, + + /* U+599E "妞" */ + 0x20, 0x0, 0x47, 0xf9, 0x2, 0x17, 0xc4, 0x24, + 0x88, 0x49, 0x10, 0x92, 0x21, 0x25, 0xfe, 0x90, + 0x84, 0xa1, 0x8, 0xc2, 0x11, 0xc4, 0x24, 0x10, + 0x58, 0xff, 0xc0, 0x0, 0x0, + + /* U+59A3 "妣" */ + 0x22, 0x20, 0x44, 0x40, 0x88, 0x81, 0x11, 0x15, + 0xa2, 0x49, 0x75, 0x12, 0x8c, 0x25, 0x10, 0x4a, + 0x20, 0xa4, 0x40, 0xc8, 0x80, 0x91, 0x12, 0xa2, + 0x28, 0x74, 0x60, 0x8f, 0x0, + + /* U+59A4 "妤" */ + 0x20, 0x0, 0x47, 0xfc, 0x80, 0x17, 0xcc, 0x44, + 0x87, 0x9, 0x4, 0x13, 0xff, 0xa4, 0x11, 0x90, + 0x22, 0xa0, 0x48, 0xc0, 0x81, 0xc1, 0x4, 0x42, + 0x18, 0x4, 0x20, 0xf0, 0x0, + + /* U+59A5 "妥" */ + 0x7f, 0xfc, 0x0, 0x10, 0x84, 0x20, 0x88, 0x80, + 0x22, 0x0, 0x80, 0x3f, 0xff, 0x84, 0x8, 0x10, + 0x20, 0x30, 0x80, 0x1e, 0x0, 0x1f, 0x3, 0xc1, + 0xc8, 0x0, 0x0, + + /* U+59A8 "妨" */ + 0x20, 0x20, 0x40, 0x40, 0x8f, 0xff, 0xc4, 0x4, + 0x88, 0x9, 0x10, 0x12, 0x3f, 0x24, 0x42, 0x88, + 0x84, 0xa1, 0x8, 0xc6, 0x11, 0xc8, 0x22, 0xb0, + 0x48, 0x41, 0xa1, 0x1e, 0x0, + + /* U+59A9 "妩" */ + 0x40, 0x0, 0x83, 0xf9, 0x0, 0x87, 0xc1, 0x4, + 0x82, 0x9, 0x8, 0x22, 0xff, 0xcc, 0x20, 0x90, + 0x40, 0xa0, 0xc0, 0xc2, 0x81, 0x85, 0x12, 0x92, + 0x28, 0x64, 0x61, 0x7, 0x0, 0x0, + + /* U+59AA "妪" */ + 0x20, 0x0, 0x47, 0xfd, 0x8, 0x7, 0xd4, 0x24, + 0xa4, 0x89, 0x49, 0x12, 0x8c, 0x25, 0x8, 0x8a, + 0x31, 0xa4, 0x51, 0xc9, 0x21, 0x94, 0x22, 0xa8, + 0x48, 0x40, 0x20, 0xff, 0xc0, 0x0, + + /* U+59AB "妫" */ + 0x20, 0x80, 0x92, 0x4, 0x28, 0x3e, 0x20, 0x4f, + 0xfd, 0x22, 0x14, 0x88, 0x52, 0x21, 0x90, 0xa5, + 0x44, 0x53, 0x10, 0x4e, 0x41, 0x2a, 0x5, 0x18, + 0x38, 0xc7, 0x80, + + /* U+59AE "妮" */ + 0x20, 0x0, 0x87, 0xf9, 0x8, 0x17, 0xd0, 0x24, + 0xbf, 0xcb, 0x40, 0x16, 0x80, 0x49, 0x20, 0x92, + 0x44, 0xa4, 0xb1, 0x89, 0x81, 0x92, 0x15, 0xc4, + 0x38, 0x88, 0x63, 0x1f, 0x0, + + /* U+59AF "妯" */ + 0x10, 0x10, 0x40, 0x20, 0x80, 0x43, 0xef, 0xf2, + 0x51, 0x24, 0xa2, 0x49, 0x44, 0xa2, 0x89, 0x49, + 0xfe, 0x52, 0x24, 0x64, 0x48, 0xc8, 0x92, 0x51, + 0x28, 0x3f, 0xc0, 0x40, 0x80, + + /* U+59B2 "妲" */ + 0x20, 0x0, 0x47, 0xf9, 0x8, 0x17, 0xd0, 0x24, + 0xa0, 0x49, 0x7f, 0x92, 0x81, 0x25, 0x2, 0x92, + 0x4, 0xa4, 0x8, 0xcf, 0xf1, 0x80, 0x4, 0x80, + 0x18, 0xff, 0xc0, 0x0, 0x0, + + /* U+59B9 "妹" */ + 0x20, 0x40, 0x40, 0x81, 0xf, 0xf7, 0xc2, 0x4, + 0x84, 0x9, 0x8, 0x13, 0xff, 0xa4, 0x30, 0x90, + 0xe0, 0xa1, 0xa0, 0xc5, 0x21, 0x92, 0x24, 0x44, + 0x78, 0x8, 0x20, 0x10, 0x0, + + /* U+59BB "妻" */ + 0x1, 0x0, 0x2, 0x1, 0xff, 0xf0, 0x8, 0x3, + 0xff, 0x80, 0x21, 0x3f, 0xff, 0x80, 0x84, 0x3f, + 0xf8, 0x4, 0x0, 0x18, 0x7, 0xff, 0xf0, 0x82, + 0x1, 0xc8, 0x0, 0x78, 0x3f, 0xe, + + /* U+59BE "妾" */ + 0x1, 0x0, 0x2, 0x1, 0xff, 0xf0, 0x80, 0x80, + 0xc2, 0x1f, 0xff, 0xc0, 0x0, 0x2, 0x0, 0xff, + 0xfe, 0x10, 0x20, 0x40, 0x80, 0x76, 0x0, 0x3c, + 0x7, 0x7, 0x90, 0x0, 0x0, + + /* U+59C6 "姆" */ + 0x20, 0x0, 0x47, 0xf8, 0x88, 0x17, 0xd1, 0x24, + 0xa2, 0x49, 0x40, 0x93, 0xff, 0xa5, 0x2, 0x92, + 0x44, 0xa4, 0x48, 0xc8, 0x11, 0x9f, 0xf4, 0x80, + 0x48, 0x0, 0xa0, 0xe, 0x0, + + /* U+59CA "姊" */ + 0x20, 0x20, 0x40, 0x40, 0x8f, 0xff, 0xd1, 0x2, + 0xa2, 0x9, 0x44, 0x12, 0x88, 0x25, 0xff, 0x90, + 0x62, 0xe0, 0xc4, 0x42, 0x89, 0x49, 0x14, 0x22, + 0xf8, 0x84, 0x20, 0x8, 0x0, + + /* U+59CB "始" */ + 0x20, 0x40, 0x40, 0x80, 0x82, 0x27, 0xc4, 0x44, + 0x90, 0x49, 0x7f, 0xd2, 0x0, 0xa4, 0x0, 0x89, + 0xfc, 0xa2, 0x8, 0xc4, 0x11, 0xc8, 0x22, 0xd0, + 0x48, 0x3f, 0xa0, 0x41, 0x0, + + /* U+59D0 "姐" */ + 0x20, 0x0, 0x47, 0xf9, 0x8, 0x17, 0xd0, 0x24, + 0xa0, 0x49, 0x7f, 0x92, 0x81, 0x45, 0x2, 0x92, + 0x4, 0xa7, 0xf8, 0x88, 0x11, 0xd0, 0x24, 0x20, + 0x50, 0xff, 0xc0, 0x0, 0x0, + + /* U+59D1 "姑" */ + 0x20, 0x20, 0x40, 0x40, 0x80, 0x87, 0xdf, 0xf4, + 0x82, 0x9, 0x4, 0x12, 0x8, 0x24, 0x10, 0x49, + 0xfc, 0xa2, 0x8, 0xc4, 0x10, 0x88, 0x22, 0xd0, + 0x48, 0x3f, 0xa0, 0x41, 0x0, + + /* U+59D2 "姒" */ + 0x40, 0x88, 0x84, 0x91, 0x9, 0x27, 0xd0, 0x45, + 0xa0, 0x8b, 0x41, 0x16, 0x82, 0x49, 0x4, 0x92, + 0x8, 0xa5, 0xa0, 0x8e, 0x41, 0x91, 0x44, 0x82, + 0x50, 0x18, 0xe0, 0x20, 0x0, + + /* U+59D3 "姓" */ + 0x20, 0x0, 0x81, 0x4, 0x14, 0x10, 0x50, 0xfb, + 0xfd, 0x29, 0x4, 0xa4, 0x12, 0x10, 0x90, 0x42, + 0x4f, 0xf7, 0x4, 0xc, 0x10, 0x28, 0x41, 0x1, + 0x8, 0x7f, 0xc0, + + /* U+59D4 "委" */ + 0x3f, 0xf8, 0x2, 0x0, 0x4, 0x7, 0xff, 0xf0, + 0x38, 0x1, 0xac, 0xc, 0x47, 0xe2, 0x0, 0x4, + 0x1, 0xff, 0xfc, 0x20, 0x80, 0xc1, 0x0, 0x7c, + 0x0, 0x7c, 0x3f, 0x7, 0x0, + + /* U+59D7 "姗" */ + 0x20, 0x0, 0x23, 0xde, 0x22, 0x52, 0x7a, 0x52, + 0x2a, 0x52, 0x4a, 0x52, 0x4f, 0xff, 0x4a, 0x52, + 0x52, 0x52, 0x32, 0x52, 0x12, 0x52, 0x2a, 0x52, + 0x26, 0x52, 0x44, 0x62, 0x5, 0xee, + + /* U+59D8 "姘" */ + 0x21, 0x4, 0x21, 0x88, 0x40, 0x10, 0xfb, 0xee, + 0x49, 0x8, 0x49, 0x8, 0x49, 0x8, 0x49, 0x8, + 0x97, 0xff, 0x51, 0x8, 0x31, 0x8, 0x39, 0x8, + 0x2a, 0x8, 0x42, 0x8, 0x84, 0x8, + + /* U+59DA "姚" */ + 0x20, 0x90, 0x20, 0x90, 0x20, 0x92, 0xfa, 0x92, + 0x4a, 0x94, 0x4a, 0x9c, 0x48, 0x90, 0x48, 0x90, + 0x91, 0x9c, 0x56, 0x92, 0x30, 0x90, 0x39, 0x10, + 0x29, 0x11, 0x43, 0x12, 0x86, 0x1e, 0x0, 0x0, + + /* U+59DC "姜" */ + 0x8, 0x30, 0x8, 0xc1, 0xff, 0xf0, 0x8, 0x7, + 0xff, 0xc0, 0x20, 0x0, 0x40, 0x7f, 0xff, 0x4, + 0x1, 0xff, 0xfc, 0x20, 0x80, 0xe3, 0x0, 0x3c, + 0x7, 0x87, 0x90, 0x0, 0x0, + + /* U+59DD "姝" */ + 0x20, 0x20, 0x21, 0x20, 0x21, 0x20, 0xf9, 0xfc, + 0x4a, 0x20, 0x4a, 0x20, 0x48, 0x20, 0x4b, 0xfe, + 0x88, 0x60, 0x50, 0xb0, 0x30, 0xa8, 0x39, 0x2c, + 0x26, 0x26, 0x40, 0x20, 0x80, 0x20, + + /* U+59E3 "姣" */ + 0x20, 0x40, 0x40, 0x41, 0xf, 0xff, 0xc0, 0x4, + 0x89, 0x9, 0x21, 0x12, 0x81, 0x26, 0x85, 0x91, + 0x10, 0xa1, 0x20, 0xc1, 0x81, 0xc3, 0x4, 0xc9, + 0x18, 0x61, 0xa1, 0x0, 0x80, + + /* U+59E5 "姥" */ + 0x0, 0x0, 0x40, 0x80, 0x81, 0x9, 0x1f, 0xaf, + 0x84, 0x89, 0xa, 0x13, 0xff, 0xa4, 0x30, 0x48, + 0x81, 0x22, 0x9, 0x5c, 0x61, 0x8b, 0x3, 0x18, + 0x25, 0x20, 0x50, 0x40, 0xc0, 0x7e, + + /* U+59E8 "姨" */ + 0x20, 0x40, 0x4f, 0xfc, 0x81, 0x7, 0xc2, 0x4, + 0xff, 0xc9, 0x8, 0x92, 0xff, 0x26, 0x20, 0x94, + 0x40, 0xaf, 0xfc, 0xc3, 0x9, 0xc5, 0x64, 0x93, + 0x18, 0x43, 0x23, 0x1, 0x80, + + /* U+59EC "姬" */ + 0x20, 0x0, 0x47, 0xfc, 0x88, 0x42, 0x10, 0x87, + 0xa1, 0x9, 0x5f, 0x92, 0xa1, 0x25, 0x42, 0x92, + 0x84, 0xa5, 0xf8, 0xc8, 0x41, 0xd0, 0x84, 0x21, + 0x18, 0x7f, 0xe0, 0x0, 0x0, + + /* U+59F9 "姹" */ + 0x20, 0x20, 0x87, 0xfd, 0x8, 0xf, 0xd0, 0x14, + 0x80, 0x9, 0x7f, 0x12, 0x10, 0x44, 0x20, 0x90, + 0x40, 0xa7, 0xfc, 0xc1, 0x1, 0x82, 0x12, 0x84, + 0x28, 0x8, 0x60, 0x1f, 0x0, + + /* U+59FB "姻" */ + 0x20, 0x0, 0x47, 0xfc, 0x88, 0xf, 0xd1, 0x14, + 0xa2, 0x29, 0x5f, 0x52, 0x88, 0xa5, 0x11, 0x92, + 0x32, 0xa4, 0xa4, 0xca, 0x29, 0x94, 0x12, 0xa0, + 0x28, 0x7f, 0xe0, 0x80, 0x80, + + /* U+59FF "姿" */ + 0x0, 0x1, 0x8, 0x2, 0x3f, 0xc1, 0x12, 0x18, + 0xc8, 0xc2, 0x84, 0x31, 0x23, 0x1, 0x4, 0x3, + 0xff, 0xf0, 0x82, 0x4, 0x10, 0xe, 0x80, 0xf, + 0xf, 0xc3, 0x80, + + /* U+5A01 "威" */ + 0x0, 0x20, 0x0, 0x28, 0x0, 0x24, 0x3f, 0xff, + 0x20, 0x20, 0x20, 0x20, 0x2f, 0xa2, 0x22, 0x24, + 0x2f, 0xa4, 0x24, 0xa4, 0x28, 0x98, 0x29, 0x18, + 0x27, 0x11, 0x43, 0xaa, 0x4c, 0x4a, 0x80, 0x6, + + /* U+5A03 "娃" */ + 0x20, 0x20, 0x40, 0x41, 0xf, 0xf7, 0xc1, 0x4, + 0x82, 0x9, 0x4, 0x12, 0xff, 0xc4, 0x10, 0x90, + 0x20, 0xe7, 0xf8, 0x40, 0x81, 0x41, 0x4, 0x2, + 0x10, 0xff, 0xe0, 0x0, 0x0, + + /* U+5A04 "娄" */ + 0x1, 0x0, 0x22, 0x30, 0x24, 0x87, 0xff, 0xf0, + 0x38, 0x1, 0xac, 0x1c, 0x47, 0x41, 0x1, 0x4, + 0x1, 0xff, 0xfc, 0x20, 0x80, 0x83, 0x0, 0xfc, + 0x0, 0x7c, 0x3f, 0x7, 0x0, + + /* U+5A05 "娅" */ + 0x20, 0x0, 0x4f, 0xfd, 0x2, 0x47, 0xc4, 0x84, + 0xc9, 0x29, 0x92, 0x52, 0xa5, 0x29, 0x4a, 0x92, + 0x94, 0xa1, 0x30, 0xc2, 0x41, 0xc4, 0x84, 0x9, + 0x10, 0xff, 0xe0, 0x0, 0x0, + + /* U+5A06 "娆" */ + 0x20, 0x80, 0x20, 0x8e, 0x23, 0xf0, 0xf8, 0x44, + 0x48, 0x58, 0x48, 0x22, 0x49, 0xd2, 0x4a, 0xe, + 0x90, 0x0, 0x57, 0xfe, 0x30, 0x90, 0x30, 0x90, + 0x29, 0x92, 0x41, 0x12, 0x8e, 0x1e, + + /* U+5A07 "娇" */ + 0x20, 0x0, 0x23, 0xfe, 0x40, 0x40, 0xf8, 0x40, + 0x4b, 0xfe, 0x48, 0xb0, 0x48, 0x90, 0x49, 0x8, + 0x92, 0x6, 0x55, 0x8, 0x31, 0x8, 0x31, 0x8, + 0x69, 0x8, 0xc2, 0x8, 0x84, 0x8, + + /* U+5A08 "娈" */ + 0x1, 0x0, 0x2, 0x3, 0xff, 0xf8, 0x22, 0x1, + 0x44, 0x4, 0x89, 0x11, 0x11, 0x3, 0x0, 0xff, + 0xfe, 0x10, 0x40, 0x40, 0x80, 0x76, 0x0, 0x3c, + 0xf, 0x87, 0xa0, 0x0, 0x0, + + /* U+5A09 "娉" */ + 0x20, 0x20, 0x23, 0xfe, 0x22, 0x22, 0xfa, 0x22, + 0x4b, 0xfe, 0x4a, 0x22, 0x4b, 0xfe, 0x48, 0x0, + 0x97, 0xff, 0x51, 0x0, 0x31, 0xfc, 0x38, 0x4, + 0x2c, 0x4, 0x40, 0x4, 0x80, 0x78, + + /* U+5A0C "娌" */ + 0x20, 0x0, 0x23, 0xfe, 0x22, 0x22, 0xfa, 0x22, + 0x4b, 0xfe, 0x4a, 0x22, 0x4a, 0x22, 0x4b, 0xfe, + 0x90, 0x20, 0x50, 0x20, 0x33, 0xfe, 0x38, 0x20, + 0x68, 0x20, 0x47, 0xff, 0x80, 0x0, + + /* U+5A11 "娑" */ + 0x20, 0x40, 0x22, 0x80, 0x5, 0x36, 0x12, 0x22, + 0x44, 0xa3, 0x6, 0x8, 0x70, 0x23, 0x0, 0x4, + 0x1, 0xff, 0xfc, 0x20, 0x80, 0xc3, 0x0, 0x78, + 0x0, 0xee, 0x3e, 0x3, 0x0, + + /* U+5A13 "娓" */ + 0x20, 0x0, 0x23, 0xfe, 0x22, 0x2, 0xfb, 0xfe, + 0x4a, 0x0, 0x4a, 0x1c, 0x4a, 0xe0, 0x4a, 0x20, + 0x92, 0xfc, 0x52, 0x20, 0x32, 0x26, 0x3b, 0xf8, + 0x2c, 0x22, 0x44, 0x22, 0x84, 0x3e, + + /* U+5A18 "娘" */ + 0x20, 0x40, 0x40, 0x80, 0x8f, 0xf2, 0x10, 0x2f, + 0xa0, 0x49, 0x7f, 0x92, 0x81, 0x25, 0x2, 0x53, + 0xfd, 0x24, 0x81, 0x49, 0x11, 0x91, 0xc3, 0xa2, + 0x9, 0x4b, 0x20, 0xe1, 0x80, 0x0, + + /* U+5A1C "娜" */ + 0x40, 0x0, 0x9f, 0x79, 0xa, 0x97, 0x95, 0x25, + 0x2a, 0x4b, 0xf5, 0x24, 0xaa, 0x49, 0x52, 0xaf, + 0xa2, 0xc9, 0x45, 0x92, 0x8b, 0xa5, 0x1d, 0x4a, + 0xd1, 0x74, 0x0, 0x8, 0x0, + + /* U+5A1F "娟" */ + 0x20, 0x0, 0x43, 0xf8, 0x84, 0x17, 0xcf, 0xe4, + 0x80, 0x9, 0x0, 0x12, 0xff, 0xc9, 0x1, 0xd3, + 0xfe, 0xe4, 0x4, 0xc8, 0x9, 0x5f, 0xf4, 0x20, + 0x38, 0x40, 0x60, 0x8f, 0x0, + + /* U+5A20 "娠" */ + 0x20, 0x0, 0x23, 0xfe, 0x22, 0x0, 0xfa, 0x0, + 0x4a, 0xfc, 0x4a, 0x0, 0x4a, 0x0, 0x4b, 0xfe, + 0x4a, 0xa0, 0x52, 0x92, 0x32, 0x9c, 0x12, 0x90, + 0x2e, 0x88, 0x44, 0xa4, 0xc, 0xc2, + + /* U+5A23 "娣" */ + 0x21, 0x10, 0x80, 0x21, 0xf, 0xf7, 0xc2, 0x25, + 0x84, 0x4b, 0x7f, 0x96, 0x90, 0x4a, 0x20, 0x93, + 0xfe, 0xa1, 0x84, 0x85, 0x9, 0xca, 0x24, 0x25, + 0xd0, 0x88, 0x0, 0x10, 0x0, + + /* U+5A25 "娥" */ + 0x20, 0x10, 0x21, 0xf4, 0x22, 0x92, 0xf8, 0x90, + 0x48, 0x90, 0x4b, 0xff, 0x48, 0x90, 0x48, 0x90, + 0x50, 0xb2, 0x53, 0xd4, 0x30, 0x98, 0x30, 0x99, + 0x28, 0xa9, 0x40, 0x8d, 0x83, 0x86, + + /* U+5A29 "娩" */ + 0x20, 0x0, 0x41, 0x0, 0x83, 0xc2, 0x8, 0x87, + 0xa1, 0x9, 0x7f, 0x92, 0x89, 0x25, 0x12, 0x8a, + 0x25, 0x27, 0xf9, 0x41, 0x1, 0x83, 0x3, 0x8a, + 0x5, 0x34, 0x50, 0xc8, 0xc3, 0xe, + + /* U+5A31 "娱" */ + 0x20, 0x0, 0x21, 0xfc, 0x22, 0x4, 0x42, 0x4, + 0xfa, 0x4, 0x49, 0xfc, 0x48, 0x0, 0x4b, 0xfe, + 0x48, 0x20, 0x90, 0x20, 0x77, 0xff, 0x30, 0x60, + 0x28, 0x50, 0x40, 0x88, 0x83, 0x6, 0x4, 0x0, + + /* U+5A32 "娲" */ + 0x20, 0x0, 0x43, 0xf9, 0x4, 0x12, 0x8, 0x27, + 0x90, 0x49, 0x3f, 0x92, 0x8, 0x24, 0x10, 0x8b, + 0xff, 0x24, 0x45, 0x48, 0x89, 0x92, 0x93, 0x28, + 0xe5, 0x40, 0x50, 0x80, 0xc1, 0xe, + + /* U+5A34 "娴" */ + 0x22, 0x0, 0x42, 0x7c, 0x80, 0x9, 0x1, 0x1f, + 0xa2, 0x29, 0x44, 0x52, 0xbe, 0xa5, 0x11, 0x8a, + 0x73, 0xa5, 0x54, 0xca, 0x89, 0x91, 0x12, 0xa2, + 0x28, 0x40, 0x60, 0x87, 0x80, + + /* U+5A36 "娶" */ + 0x7e, 0xfc, 0x84, 0x9, 0xf9, 0x23, 0xf1, 0xc4, + 0x23, 0x1f, 0xe9, 0xb0, 0xa0, 0x81, 0x0, 0xff, + 0xfe, 0x10, 0x40, 0x61, 0x0, 0x3c, 0x3, 0xe7, + 0x88, 0x0, 0x80, + + /* U+5A3C "娼" */ + 0x0, 0x0, 0x43, 0xf8, 0x84, 0x11, 0x8, 0x2f, + 0x9f, 0xc9, 0x20, 0x92, 0x7f, 0x24, 0x0, 0x48, + 0x1, 0x27, 0xfd, 0x48, 0x9, 0x90, 0x13, 0xbf, + 0xe5, 0x40, 0x70, 0xff, 0xc1, 0x1, + + /* U+5A40 "婀" */ + 0x40, 0x0, 0x8f, 0xfd, 0x24, 0x17, 0xd0, 0x25, + 0xa7, 0x53, 0x52, 0xa6, 0xa5, 0x4d, 0x4a, 0xa9, + 0x94, 0xd3, 0x28, 0xa5, 0x92, 0xb8, 0x24, 0x80, + 0x51, 0x0, 0x82, 0xf, 0x0, + + /* U+5A46 "婆" */ + 0x40, 0x40, 0x9f, 0xf0, 0x44, 0x71, 0x10, 0x7, + 0xf8, 0x14, 0x22, 0x8f, 0x37, 0xc7, 0x4, 0x3, + 0xff, 0xf0, 0x82, 0x6, 0x10, 0x7, 0x80, 0x71, + 0xee, 0x0, 0x0, + + /* U+5A49 "婉" */ + 0x20, 0x20, 0x20, 0x20, 0x23, 0xde, 0xfb, 0x2, + 0x49, 0x0, 0x49, 0xde, 0x4a, 0x52, 0x4a, 0x52, + 0x4a, 0x52, 0x55, 0x52, 0x30, 0x96, 0x18, 0x90, + 0x25, 0x11, 0x43, 0x13, 0xc6, 0xe, + + /* U+5A4A "婊" */ + 0x20, 0x20, 0x40, 0x20, 0x43, 0xfe, 0xf8, 0x20, + 0x4b, 0xfe, 0x48, 0x20, 0x88, 0x20, 0x8b, 0xff, + 0x90, 0x60, 0x90, 0xd2, 0x51, 0x94, 0x36, 0x88, + 0x78, 0x84, 0x44, 0x92, 0x80, 0xe0, + + /* U+5A55 "婕" */ + 0x0, 0x20, 0x20, 0x20, 0x27, 0xfe, 0x20, 0x20, + 0x7b, 0xfc, 0x48, 0x24, 0x48, 0x24, 0x4f, 0xff, + 0x48, 0x24, 0x53, 0xfc, 0x70, 0x20, 0x32, 0x20, + 0x3a, 0x3e, 0x67, 0x20, 0xc4, 0xff, 0x0, 0x0, + + /* U+5A5A "婚" */ + 0x20, 0x4, 0x47, 0xf0, 0x88, 0x87, 0xdf, 0xf4, + 0xa1, 0x9, 0x42, 0x52, 0xf3, 0xa4, 0x0, 0x8b, + 0xfc, 0xa4, 0x8, 0xc8, 0x11, 0x9f, 0xe2, 0xa0, + 0x48, 0x7f, 0xa0, 0x81, 0x0, + + /* U+5A62 "婢" */ + 0x0, 0x20, 0x40, 0x80, 0x8f, 0xf1, 0x11, 0x2d, + 0xa2, 0x49, 0x7f, 0x92, 0x89, 0x25, 0x12, 0x52, + 0x25, 0x27, 0xfb, 0x43, 0x1, 0x88, 0x83, 0x7f, + 0xe9, 0x2, 0x30, 0x4, 0x40, 0x8, + + /* U+5A67 "婧" */ + 0x20, 0x20, 0x47, 0xfc, 0x80, 0x87, 0xdf, 0xf4, + 0x82, 0x9, 0x7f, 0xd2, 0x0, 0x24, 0xfe, 0x92, + 0x4, 0xa3, 0xf8, 0xc8, 0x11, 0xcf, 0xe2, 0xe0, + 0x48, 0x40, 0xa0, 0x87, 0x0, + + /* U+5A6A "婪" */ + 0x8, 0x20, 0x10, 0x43, 0xfb, 0xf8, 0xc1, 0x82, + 0xe6, 0x89, 0x14, 0xa2, 0x48, 0x82, 0x0, 0x4, + 0x1, 0xff, 0xfc, 0x20, 0x40, 0x81, 0x0, 0xfc, + 0x0, 0x7c, 0x3f, 0x7, 0x0, + + /* U+5A74 "婴" */ + 0x7c, 0xfe, 0x52, 0x99, 0x4a, 0x65, 0x29, 0x14, + 0xa4, 0xa1, 0x4c, 0x58, 0xc1, 0x0, 0xff, 0xfc, + 0x40, 0x81, 0x4, 0x3, 0xe0, 0xe, 0x73, 0xc0, + 0x20, + + /* U+5A75 "婵" */ + 0x0, 0x0, 0x41, 0x10, 0x82, 0x41, 0x1f, 0xef, + 0xa2, 0x49, 0x44, 0x92, 0xff, 0x25, 0x12, 0x4a, + 0x25, 0x37, 0xf9, 0x40, 0x81, 0x81, 0x3, 0x7f, + 0xe5, 0x4, 0x10, 0x8, 0x40, 0x10, + + /* U+5A76 "婶" */ + 0x20, 0x20, 0x40, 0x40, 0x8f, 0xf9, 0x10, 0x1f, + 0xa2, 0x29, 0x3f, 0x92, 0x89, 0x25, 0x12, 0x59, + 0xfd, 0xa4, 0x48, 0xc8, 0x91, 0x8f, 0xe2, 0x82, + 0x48, 0x4, 0x20, 0x8, 0x0, + + /* U+5A77 "婷" */ + 0x20, 0x0, 0x20, 0x20, 0x23, 0xff, 0x20, 0x0, + 0xf9, 0xfc, 0x49, 0x4, 0x49, 0xfc, 0x48, 0x0, + 0x4b, 0xff, 0x94, 0x1, 0x50, 0x0, 0x31, 0xfc, + 0x30, 0x20, 0x28, 0x20, 0x40, 0x20, 0x80, 0xe0, + + /* U+5A7A "婺" */ + 0x0, 0x20, 0xfc, 0x80, 0x11, 0xf9, 0xc6, 0x40, + 0x8c, 0x9f, 0xc9, 0xa, 0x8c, 0x24, 0x18, 0x99, + 0xce, 0x48, 0x3, 0xff, 0xf8, 0xc1, 0x3, 0x4, + 0x1, 0xf0, 0x3, 0x9c, 0x70, 0x6, + + /* U+5A7F "婿" */ + 0x20, 0x0, 0x23, 0xfe, 0x20, 0x24, 0xfa, 0x3e, + 0x4b, 0x20, 0x4a, 0xe0, 0x4c, 0x1e, 0x4b, 0xfc, + 0x52, 0x4, 0x33, 0xfc, 0x32, 0x4, 0x2b, 0xfc, + 0x42, 0x4, 0x42, 0x4, 0x82, 0x1c, + + /* U+5A92 "媒" */ + 0x21, 0x4, 0x21, 0x4, 0x27, 0xff, 0xf9, 0x4, + 0x49, 0xfc, 0x49, 0x4, 0x49, 0x4, 0x49, 0xfc, + 0x90, 0x20, 0x77, 0xff, 0x30, 0x70, 0x38, 0xa8, + 0x49, 0x24, 0xc6, 0x23, 0x80, 0x20, + + /* U+5A9A "媚" */ + 0x20, 0x0, 0x47, 0xfc, 0x88, 0x8f, 0xd1, 0x14, + 0xbf, 0xe9, 0x40, 0x12, 0xbf, 0xa5, 0x41, 0x5a, + 0xfe, 0x65, 0x4, 0x4a, 0x9, 0xd7, 0xf2, 0x48, + 0x28, 0x9f, 0xc2, 0x20, 0x80, + + /* U+5A9B "媛" */ + 0x20, 0x0, 0x47, 0xfc, 0x80, 0x97, 0xc9, 0x44, + 0xbf, 0xe9, 0x10, 0x12, 0x20, 0x25, 0xff, 0x89, + 0x0, 0xa3, 0xf8, 0xc6, 0x11, 0x92, 0x42, 0xa3, + 0x8, 0x8f, 0x22, 0xe1, 0x80, + + /* U+5AAA "媪" */ + 0x20, 0x0, 0x21, 0xfc, 0x22, 0x4, 0xf9, 0xfc, + 0x4a, 0x4, 0x4a, 0x4, 0x49, 0xfc, 0x48, 0x0, + 0x53, 0xfe, 0x32, 0x92, 0x12, 0x92, 0x2a, 0x92, + 0x42, 0x92, 0xc7, 0xff, + + /* U+5AB2 "媲" */ + 0x20, 0x40, 0x47, 0xf8, 0x88, 0x57, 0xd5, 0x24, + 0xa4, 0x49, 0x56, 0x92, 0x81, 0x25, 0xfe, 0x92, + 0x20, 0xa4, 0x44, 0xcf, 0xb1, 0x91, 0x92, 0xe2, + 0x28, 0x54, 0x60, 0xcf, 0x0, + + /* U+5AB3 "媳" */ + 0x0, 0x40, 0x40, 0x80, 0x8f, 0xf1, 0x10, 0x2f, + 0xa0, 0x49, 0x7f, 0x92, 0x81, 0x25, 0xfe, 0x4a, + 0x5, 0x27, 0xf9, 0x40, 0x1, 0x84, 0x3, 0x2a, + 0x4d, 0x91, 0x71, 0x24, 0x40, 0x78, + + /* U+5AB5 "媵" */ + 0x0, 0x20, 0x0, 0x20, 0x79, 0x24, 0x48, 0xa8, + 0x4b, 0xfe, 0x48, 0x40, 0x78, 0x40, 0x4b, 0xfe, + 0x49, 0x88, 0x4a, 0x46, 0x7b, 0xfc, 0x48, 0x88, + 0x49, 0x90, 0x48, 0x70, 0x88, 0xdc, 0xbb, 0x6, + + /* U+5AB8 "媸" */ + 0x40, 0x40, 0x88, 0x89, 0x11, 0x17, 0xdf, 0xc5, + 0x84, 0xb, 0xff, 0xe6, 0x10, 0x49, 0xfe, 0x94, + 0x44, 0xc8, 0x88, 0xcf, 0xf2, 0xc2, 0x24, 0x4, + 0x51, 0xff, 0xc0, 0x0, 0x80, + + /* U+5ABE "媾" */ + 0x20, 0x88, 0x23, 0xfe, 0x20, 0x88, 0xfb, 0xfe, + 0x48, 0x88, 0x4f, 0xff, 0x48, 0x20, 0x4b, 0xfe, + 0x52, 0x22, 0x33, 0xfe, 0x12, 0x22, 0x2f, 0xff, + 0x42, 0x2, 0x42, 0x2, 0x2, 0x1c, + + /* U+5AC1 "嫁" */ + 0x20, 0x20, 0x20, 0x20, 0x23, 0xde, 0xfa, 0x2, + 0x48, 0x0, 0x4b, 0xfe, 0x48, 0x60, 0x48, 0xc6, + 0x53, 0x28, 0x50, 0xd8, 0x33, 0x38, 0x38, 0x54, + 0x29, 0x92, 0x42, 0x10, 0x80, 0xe0, + + /* U+5AC2 "嫂" */ + 0x20, 0x20, 0x47, 0x5c, 0x88, 0x8f, 0xd1, 0x14, + 0xba, 0xe9, 0x44, 0x52, 0xff, 0xa4, 0x10, 0x88, + 0x20, 0xa7, 0xf8, 0xc4, 0x11, 0x84, 0x42, 0x85, + 0x8, 0x1f, 0x21, 0xc1, 0x80, + + /* U+5AC9 "嫉" */ + 0x40, 0x20, 0x80, 0x21, 0xf, 0xff, 0x90, 0x5, + 0xa4, 0x13, 0x4f, 0xa5, 0xa4, 0x49, 0x48, 0x92, + 0x11, 0x5d, 0xf9, 0x88, 0x41, 0xb1, 0x45, 0xc2, + 0x90, 0x88, 0xa2, 0x61, 0x80, + + /* U+5ACC "嫌" */ + 0x21, 0x8, 0x20, 0x90, 0x23, 0xff, 0xf8, 0x90, + 0x4b, 0xfe, 0x48, 0x92, 0x4b, 0xff, 0x48, 0x92, + 0x90, 0x92, 0x53, 0xfe, 0x30, 0x90, 0x31, 0x98, + 0x2a, 0x94, 0x44, 0x93, 0x0, 0x90, + + /* U+5AD2 "嫒" */ + 0x20, 0x0, 0x47, 0xfc, 0x84, 0x97, 0xc1, 0x44, + 0xbf, 0xe9, 0x40, 0x52, 0x20, 0x25, 0xff, 0x90, + 0x80, 0xa1, 0xf8, 0xc6, 0x11, 0x8a, 0x42, 0xa3, + 0x8, 0x8f, 0x20, 0x61, 0x80, + + /* U+5AD4 "嫔" */ + 0x20, 0x0, 0x40, 0x80, 0x8f, 0xf9, 0x10, 0x1f, + 0x80, 0x9, 0x3f, 0x12, 0x40, 0x24, 0x80, 0x49, + 0xfe, 0x92, 0x21, 0x44, 0x41, 0x88, 0x83, 0x7f, + 0xe5, 0x0, 0x10, 0x66, 0x41, 0x3, + + /* U+5AD6 "嫖" */ + 0x0, 0x0, 0x4f, 0xfc, 0x82, 0x41, 0x4, 0x8f, + 0xbf, 0xc9, 0x92, 0x93, 0x25, 0x25, 0xfe, 0x48, + 0x0, 0xa7, 0xf9, 0xc0, 0x0, 0xbf, 0xf2, 0x84, + 0x4, 0x69, 0x13, 0x11, 0x80, 0xe0, + + /* U+5AD8 "嫘" */ + 0x20, 0x0, 0x23, 0xfe, 0x22, 0x22, 0x22, 0x22, + 0xfb, 0xfe, 0x4a, 0x22, 0x4b, 0xfe, 0x48, 0x60, + 0x49, 0x8c, 0x4b, 0xf0, 0x50, 0x88, 0x33, 0xfe, + 0x28, 0x28, 0x2b, 0x24, 0x44, 0x23, 0x81, 0xe0, + + /* U+5ADC "嫜" */ + 0x0, 0x0, 0x20, 0x20, 0x23, 0xfe, 0x21, 0x4, + 0xf8, 0x88, 0x4b, 0xfe, 0x48, 0x0, 0x4b, 0xfc, + 0x4a, 0x4, 0x5b, 0xfc, 0x52, 0x4, 0x33, 0xfc, + 0x30, 0x20, 0x2b, 0xff, 0x40, 0x20, 0x80, 0x20, + + /* U+5AE0 "嫠" */ + 0x8, 0x0, 0x8, 0x20, 0x7f, 0x3e, 0x8, 0x64, + 0x7f, 0xac, 0xc, 0x18, 0x3b, 0x2c, 0x40, 0x42, + 0x3f, 0xfe, 0x21, 0x0, 0x3f, 0xfe, 0x22, 0x10, + 0x26, 0x20, 0x41, 0xe0, 0x5f, 0x1e, + + /* U+5AE1 "嫡" */ + 0x20, 0x20, 0x20, 0x20, 0x23, 0xde, 0xf8, 0x88, + 0x48, 0x90, 0x4b, 0xfe, 0x4a, 0x22, 0x4a, 0xfa, + 0x52, 0x22, 0x32, 0xfa, 0x12, 0x8a, 0x2a, 0x8a, + 0x2a, 0xfa, 0x42, 0x2, 0x82, 0x1e, + + /* U+5AE3 "嫣" */ + 0x20, 0x0, 0x23, 0xfe, 0x20, 0x20, 0x7a, 0x3c, + 0x4a, 0x20, 0x4f, 0xff, 0x4a, 0x0, 0x4b, 0xfe, + 0x52, 0x0, 0x33, 0xfe, 0x10, 0x2, 0x2a, 0x4a, + 0x24, 0xaa, 0x44, 0x2, 0x0, 0x3c, + + /* U+5AE6 "嫦" */ + 0x20, 0x20, 0x42, 0x48, 0x80, 0x87, 0xde, 0xf4, + 0xa0, 0x29, 0x3f, 0x92, 0x41, 0x24, 0xfe, 0x48, + 0x20, 0xa0, 0x40, 0xcf, 0xf8, 0x91, 0x12, 0xa2, + 0x28, 0x45, 0xc0, 0x8, 0x0, + + /* U+5AE9 "嫩" */ + 0x20, 0x88, 0x10, 0x44, 0x8, 0xfa, 0xf, 0x11, + 0xe2, 0xbf, 0x22, 0x55, 0x91, 0x2a, 0xa8, 0x95, + 0x54, 0x53, 0xec, 0x38, 0x46, 0x4, 0x71, 0x5, + 0x54, 0x82, 0x48, 0xa2, 0x64, 0x98, 0x2, 0x84, + + /* U+5AEB "嫫" */ + 0x0, 0x88, 0x20, 0x88, 0x27, 0xff, 0x20, 0x88, + 0xf8, 0x0, 0x4b, 0xfe, 0x4a, 0x2, 0x4b, 0xfe, + 0x4a, 0x2, 0x4b, 0xfe, 0x70, 0x20, 0x30, 0x20, + 0x17, 0xff, 0x2c, 0x60, 0x40, 0xd8, 0x87, 0x7, + 0x0, 0x0, + + /* U+5AF1 "嫱" */ + 0x20, 0x20, 0x23, 0xfe, 0x20, 0x20, 0xf9, 0x24, + 0x48, 0x28, 0x4b, 0xff, 0x48, 0x0, 0x4b, 0xfe, + 0x52, 0x2, 0x52, 0xfa, 0x32, 0x8a, 0x3a, 0xfa, + 0x2e, 0x2, 0x43, 0xfe, 0x2, 0x2, + + /* U+5B09 "嬉" */ + 0x20, 0x20, 0x27, 0xff, 0x20, 0x20, 0xfb, 0xfe, + 0x48, 0x0, 0x4b, 0xfe, 0x4a, 0x2, 0x4b, 0xfe, + 0x50, 0x88, 0x37, 0xff, 0x10, 0x0, 0x2b, 0xfe, + 0x22, 0x2, 0x43, 0xfe, 0x2, 0x2, + + /* U+5B16 "嬖" */ + 0x0, 0x20, 0xfc, 0x21, 0xb, 0xfa, 0x12, 0x27, + 0xe4, 0x88, 0x3f, 0xdf, 0x88, 0x71, 0x7e, 0xbe, + 0x20, 0xc, 0x3, 0xff, 0xf8, 0x41, 0x1, 0x84, + 0x0, 0xf0, 0x7, 0x9e, 0x30, 0x2, + + /* U+5B17 "嬗" */ + 0x20, 0x20, 0x27, 0xff, 0x20, 0x0, 0xff, 0xfe, + 0x4c, 0xfa, 0x4c, 0x8a, 0x4c, 0x72, 0x4b, 0xfe, + 0x50, 0x0, 0x33, 0xfc, 0x13, 0xfc, 0x2a, 0x4, + 0x41, 0xf8, 0xc7, 0xff, + + /* U+5B32 "嬲" */ + 0x7d, 0x3e, 0xaa, 0x55, 0x54, 0xab, 0xef, 0xf5, + 0x56, 0xaa, 0xad, 0x55, 0x9a, 0xbf, 0x3f, 0x22, + 0x48, 0xfb, 0x7c, 0x96, 0x29, 0x26, 0x52, 0x55, + 0x28, 0xa2, 0x56, 0x9b, 0x80, + + /* U+5B34 "嬴" */ + 0x1, 0x1, 0xff, 0xfc, 0x80, 0x1, 0xff, 0xc0, + 0x0, 0x7, 0xff, 0xf, 0xfe, 0x0, 0x0, 0x77, + 0xb8, 0xa5, 0x51, 0xd2, 0xe2, 0xb5, 0x47, 0x12, + 0x8a, 0x59, 0x6d, 0x11, 0x80, + + /* U+5B37 "嬷" */ + 0x40, 0x40, 0x80, 0x81, 0x3e, 0xf7, 0xc4, 0x45, + 0xbd, 0xd3, 0x11, 0x26, 0x77, 0x4d, 0x56, 0x98, + 0x88, 0xd0, 0x80, 0xa6, 0xc2, 0xd2, 0x4, 0x88, + 0x91, 0x7f, 0x80, 0x0, 0x0, + + /* U+5B40 "孀" */ + 0x43, 0xfc, 0x80, 0x81, 0x1f, 0xff, 0xa2, 0x15, + 0x5d, 0xaa, 0x8, 0x24, 0x42, 0x49, 0x3e, 0xa7, + 0xc4, 0xc4, 0xf8, 0x9d, 0x12, 0xb7, 0xe4, 0xa4, + 0x50, 0x4f, 0x80, 0x91, 0x0, + + /* U+5B50 "子" */ + 0x3f, 0xf8, 0x0, 0x30, 0x0, 0xc0, 0x2, 0x0, + 0x8, 0x0, 0x20, 0x3f, 0xff, 0x80, 0x80, 0x1, + 0x0, 0x2, 0x0, 0x4, 0x0, 0x8, 0x0, 0x10, + 0x1, 0xe0, 0x0, + + /* U+5B51 "孑" */ + 0x7f, 0xf8, 0x0, 0x40, 0x6, 0x0, 0x20, 0x3, + 0x0, 0x8, 0x0, 0x3f, 0xff, 0x80, 0x2, 0x0, + 0x8, 0x0, 0x20, 0x0, 0x80, 0x2, 0x0, 0x78, + 0x0, + + /* U+5B53 "孓" */ + 0x7f, 0xf8, 0x0, 0x40, 0x6, 0x20, 0x20, 0x43, + 0x0, 0x88, 0x1, 0x20, 0x3, 0x80, 0x3, 0x0, + 0xb, 0x80, 0x21, 0xc0, 0x80, 0x2, 0x0, 0x78, + 0x0, + + /* U+5B54 "孔" */ + 0x0, 0x20, 0x7f, 0x20, 0x3, 0x20, 0x6, 0x20, + 0xc, 0x20, 0x8, 0x20, 0x8, 0x20, 0x1f, 0x20, + 0xe8, 0x20, 0x8, 0x21, 0x8, 0x21, 0x8, 0x21, + 0x8, 0x23, 0x8, 0x1e, 0x78, 0x0, + + /* U+5B55 "孕" */ + 0x7f, 0xf0, 0x10, 0x20, 0x20, 0xf0, 0x80, 0x21, + 0x7e, 0x44, 0xc, 0xb0, 0x27, 0x0, 0x40, 0x0, + 0x81, 0xff, 0xfc, 0x2, 0x0, 0x4, 0x0, 0x8, + 0x0, 0xe0, 0x0, + + /* U+5B57 "字" */ + 0x0, 0x0, 0x2, 0x0, 0x4, 0x7, 0xff, 0xe8, + 0x0, 0x50, 0x0, 0x8f, 0xfc, 0x0, 0x10, 0x0, + 0xc0, 0x2, 0x3, 0xff, 0xf8, 0x8, 0x0, 0x10, + 0x0, 0x20, 0x0, 0x40, 0x7, 0x0, + + /* U+5B58 "存" */ + 0x2, 0x0, 0x2, 0x0, 0x7f, 0xff, 0x4, 0x0, + 0x8, 0x0, 0xb, 0xfe, 0x10, 0x6, 0x10, 0x8, + 0x30, 0x10, 0x50, 0x20, 0xd7, 0xff, 0x10, 0x20, + 0x10, 0x20, 0x10, 0x20, 0x10, 0x20, 0x10, 0xe0, + + /* U+5B59 "孙" */ + 0x0, 0x20, 0xf8, 0x40, 0x10, 0x80, 0x41, 0x1, + 0x2, 0x82, 0x24, 0x84, 0x49, 0xe, 0x92, 0x71, + 0x23, 0x24, 0x44, 0x48, 0x88, 0x81, 0x1, 0x2, + 0x2, 0x4, 0x18, 0x38, 0x0, + + /* U+5B5A "孚" */ + 0x7f, 0xfc, 0x2, 0x0, 0x42, 0x20, 0x4, 0x83, + 0xff, 0xc0, 0x3, 0x0, 0x38, 0x0, 0x40, 0xff, + 0xfe, 0x1, 0x0, 0x2, 0x0, 0x4, 0x0, 0x18, + 0x0, 0xe0, 0x0, + + /* U+5B5B "孛" */ + 0x1, 0x0, 0xff, 0xf8, 0x4, 0x0, 0x8, 0xf, + 0xff, 0xf0, 0x0, 0x60, 0x0, 0x8f, 0xf8, 0x0, + 0x60, 0x3, 0x3, 0xff, 0xf8, 0x8, 0x0, 0x10, + 0x0, 0x20, 0x3, 0xc0, 0x0, + + /* U+5B5C "孜" */ + 0x0, 0x20, 0x7f, 0x20, 0x3, 0x40, 0x2, 0x7f, + 0x4, 0x44, 0x8, 0xc4, 0x8, 0xc4, 0xe, 0x44, + 0x78, 0x48, 0x8, 0x28, 0x8, 0x38, 0x8, 0x10, + 0x8, 0x68, 0x8, 0x86, 0x73, 0x3, + + /* U+5B5D "孝" */ + 0x2, 0x0, 0x4, 0x0, 0xff, 0xa0, 0x10, 0x80, + 0x26, 0x1f, 0xff, 0xc0, 0xc0, 0xf, 0xf8, 0x60, + 0x61, 0x1, 0x1, 0xff, 0xf0, 0x8, 0x0, 0x10, + 0x0, 0x20, 0x3, 0xc0, 0x0, + + /* U+5B5F "孟" */ + 0x3f, 0xf8, 0x0, 0x60, 0x3, 0x7, 0xff, 0xf0, + 0x8, 0x0, 0x10, 0x1, 0xc0, 0x1f, 0xfc, 0x22, + 0x48, 0x44, 0x90, 0x89, 0x21, 0x12, 0x4f, 0xff, + 0xe0, + + /* U+5B62 "孢" */ + 0x1, 0x1, 0xf2, 0x0, 0x2f, 0xf0, 0x90, 0x21, + 0x40, 0x44, 0xfc, 0x88, 0x89, 0xd, 0x12, 0xe3, + 0xe4, 0x44, 0x8, 0x89, 0xe9, 0x10, 0x12, 0x20, + 0x24, 0x40, 0x58, 0x7f, 0x0, + + /* U+5B63 "季" */ + 0x3f, 0xf8, 0x2, 0x0, 0x4, 0x7, 0xff, 0xf0, + 0x34, 0x1, 0xa6, 0x1c, 0x3, 0xc7, 0xf8, 0x0, + 0x60, 0x1, 0x3, 0xff, 0xf8, 0x8, 0x0, 0x10, + 0x0, 0x20, 0x3, 0xc0, 0x0, + + /* U+5B64 "孤" */ + 0x0, 0x1, 0xf7, 0xf0, 0x29, 0x40, 0x92, 0x83, + 0x25, 0x4, 0x4a, 0x8, 0x94, 0x1d, 0x28, 0xe2, + 0x50, 0x44, 0x90, 0x89, 0xa1, 0x33, 0x42, 0x4a, + 0x45, 0x9a, 0xba, 0x0, 0x0, + + /* U+5B65 "孥" */ + 0x10, 0x0, 0x21, 0xfb, 0xfa, 0x11, 0x12, 0x44, + 0x43, 0x87, 0x86, 0xc, 0xb3, 0x40, 0x1, 0x3f, + 0xf8, 0x0, 0x60, 0x3, 0x7, 0xff, 0xf0, 0x10, + 0x0, 0x20, 0x3, 0xc0, 0x0, + + /* U+5B66 "学" */ + 0x21, 0x8, 0x23, 0x20, 0x0, 0x3, 0xff, 0xf4, + 0x0, 0x28, 0x0, 0x47, 0xfc, 0x0, 0x10, 0x0, + 0x40, 0x1, 0x3, 0xff, 0xf8, 0x4, 0x0, 0x8, + 0x0, 0x10, 0x1, 0xc0, 0x0, + + /* U+5B69 "孩" */ + 0x0, 0x20, 0x7c, 0x10, 0x5, 0xff, 0x8, 0x40, + 0x10, 0x80, 0x10, 0x88, 0x15, 0xf0, 0x18, 0x12, + 0xf0, 0x26, 0x10, 0x44, 0x11, 0x88, 0x16, 0x18, + 0x10, 0x2c, 0x10, 0xc6, 0x73, 0x2, + + /* U+5B6A "孪" */ + 0x1, 0x0, 0x1, 0x3, 0xff, 0xf8, 0x12, 0x1, + 0x24, 0x4, 0x49, 0x90, 0x90, 0x8f, 0xf8, 0x0, + 0x20, 0x1, 0x83, 0xff, 0xf8, 0x4, 0x0, 0x8, + 0x0, 0x10, 0x1, 0xc0, 0x0, + + /* U+5B6C "孬" */ + 0xff, 0xfc, 0x6, 0x0, 0x37, 0x83, 0x88, 0xe8, + 0x10, 0x4, 0x3f, 0xbf, 0x2, 0x22, 0x18, 0x44, + 0x20, 0x97, 0xfc, 0xa0, 0x80, 0xc1, 0x6, 0x42, + 0x10, 0x3c, 0x0, + + /* U+5B70 "孰" */ + 0x10, 0x41, 0xfc, 0x80, 0x1, 0x7, 0xef, 0xc8, + 0x44, 0x90, 0x89, 0x3f, 0x12, 0x0, 0xa4, 0xfc, + 0xc8, 0x10, 0xd0, 0x42, 0xa7, 0xf4, 0x51, 0x10, + 0xa2, 0x20, 0xd8, 0x80, 0x0, + + /* U+5B71 "孱" */ + 0x3f, 0xfe, 0x20, 0x2, 0x20, 0x2, 0x3f, 0xfe, + 0x20, 0x0, 0x23, 0xfc, 0x2f, 0xfe, 0x20, 0x40, + 0x20, 0x80, 0x3f, 0x3e, 0x21, 0x6, 0x42, 0x8, + 0x5f, 0x7f, 0x42, 0x8, 0x9c, 0x78, + + /* U+5B73 "孳" */ + 0x0, 0x20, 0x8, 0x83, 0xff, 0xf8, 0x92, 0x47, + 0xcf, 0x1, 0x5, 0xc, 0x92, 0x3f, 0x7e, 0x0, + 0x0, 0x3f, 0xe0, 0x3, 0x7, 0xff, 0xf0, 0x8, + 0x0, 0x10, 0x1, 0xc0, 0x0, + + /* U+5B75 "孵" */ + 0x20, 0x1f, 0x9e, 0x42, 0x24, 0x5, 0x4a, 0xaa, + 0x95, 0x5d, 0xa0, 0x3a, 0xdf, 0x55, 0x82, 0xa9, + 0x9, 0xd2, 0x22, 0xa5, 0xf9, 0x58, 0x84, 0x81, + 0x9, 0x2, 0x22, 0x1c, 0x0, + + /* U+5B7A "孺" */ + 0xf7, 0xfc, 0x20, 0x80, 0xbf, 0xf1, 0x5f, 0xe4, + 0x4, 0x8, 0x7f, 0x1b, 0xff, 0xe0, 0x40, 0x40, + 0x80, 0x8f, 0xf9, 0x12, 0x92, 0x25, 0x24, 0x4a, + 0x58, 0x97, 0x80, + + /* U+5B7D "孽" */ + 0x4, 0x41, 0xff, 0xfc, 0x40, 0x83, 0xef, 0xe4, + 0x44, 0x8f, 0x3f, 0xdf, 0x8, 0x22, 0xfe, 0x7c, + 0x20, 0x0, 0x0, 0x7f, 0xc0, 0x6, 0xf, 0xff, + 0xe0, 0x20, 0x3, 0xc0, 0x0, + + /* U+5B80 "宀" */ + 0x2, 0x0, 0xc, 0x0, 0x10, 0x3f, 0xff, 0x80, + 0x6, 0x0, 0x18, 0x0, 0x40, + + /* U+5B81 "宁" */ + 0x0, 0x0, 0x2, 0x0, 0x4, 0x3, 0xff, 0xe4, + 0x0, 0x48, 0x0, 0x80, 0x0, 0x0, 0x0, 0xff, + 0xfe, 0x2, 0x0, 0x4, 0x0, 0x8, 0x0, 0x10, + 0x0, 0x20, 0x0, 0x40, 0xf, 0x80, + + /* U+5B83 "它" */ + 0x2, 0x0, 0x4, 0xf, 0xff, 0xe0, 0x1, 0x80, + 0x4, 0x80, 0x2, 0x1, 0x8, 0x18, 0x23, 0x80, + 0xf0, 0x2, 0x0, 0x8, 0x1, 0x20, 0x4, 0x80, + 0x21, 0xff, 0x80, + + /* U+5B84 "宄" */ + 0x1, 0x0, 0x0, 0x80, 0x7f, 0xfe, 0x40, 0x2, + 0x44, 0x2, 0x4, 0x0, 0x7f, 0xe0, 0x4, 0x20, + 0x4, 0x20, 0x4, 0x20, 0x8, 0x20, 0x8, 0x21, + 0x10, 0x21, 0x30, 0x22, 0x60, 0x1e, 0x0, 0x0, + + /* U+5B85 "宅" */ + 0x2, 0x0, 0x8, 0xf, 0xff, 0xe0, 0x1, 0x80, + 0x5, 0xff, 0xc0, 0x40, 0x1, 0x0, 0x4, 0x4, + 0x7f, 0xee, 0x40, 0x1, 0x1, 0x4, 0x4, 0x10, + 0x10, 0x7f, 0x80, + + /* U+5B87 "宇" */ + 0x1, 0x0, 0x1, 0x1, 0xff, 0xf4, 0x0, 0x20, + 0x0, 0x7, 0xff, 0x0, 0x40, 0x0, 0x80, 0x1, + 0x1, 0xff, 0xfc, 0x4, 0x0, 0x8, 0x0, 0x10, + 0x0, 0x20, 0x3, 0xc0, 0x0, + + /* U+5B88 "守" */ + 0x1, 0x0, 0x2, 0x1, 0xff, 0xf4, 0x0, 0x20, + 0x2, 0x40, 0x4, 0x3f, 0xff, 0x80, 0x10, 0x20, + 0x20, 0x20, 0x40, 0x20, 0x80, 0x21, 0x0, 0x2, + 0x0, 0x4, 0x0, 0x78, 0x0, + + /* U+5B89 "安" */ + 0x1, 0x0, 0x1, 0x1, 0xff, 0xf2, 0x0, 0x24, + 0x20, 0x40, 0x80, 0x3f, 0xff, 0x84, 0x8, 0x10, + 0x20, 0x30, 0x80, 0x1b, 0x0, 0xc, 0x0, 0x67, + 0xe, 0x1, 0x80, + + /* U+5B8B "宋" */ + 0x1, 0x0, 0x2, 0x3, 0xff, 0xfc, 0x0, 0x18, + 0x10, 0x20, 0x20, 0x3f, 0xff, 0x81, 0xc0, 0x5, + 0x40, 0xa, 0xc0, 0x24, 0xc1, 0x88, 0xc6, 0x10, + 0x50, 0x20, 0x40, 0x40, 0x0, + + /* U+5B8C "完" */ + 0x2, 0x0, 0x2, 0x3, 0xff, 0xf4, 0x0, 0x28, + 0x0, 0x47, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, + 0xfc, 0x11, 0x0, 0x22, 0x0, 0x44, 0x31, 0x8, + 0x46, 0x10, 0xb0, 0x1f, 0x0, + + /* U+5B8F "宏" */ + 0x1, 0x0, 0x2, 0x3, 0xff, 0xfc, 0x0, 0x18, + 0x40, 0x20, 0x80, 0x3f, 0xff, 0x84, 0x0, 0x18, + 0x80, 0x22, 0x0, 0x88, 0x82, 0x10, 0x8c, 0x40, + 0x91, 0xff, 0x80, 0x1, 0x0, + + /* U+5B93 "宓" */ + 0x0, 0x80, 0x0, 0x40, 0x7f, 0xfe, 0x40, 0x2, + 0x41, 0x2, 0x0, 0x88, 0x4, 0x48, 0x4, 0x10, + 0x24, 0x24, 0x24, 0x42, 0x44, 0x82, 0x5, 0x4, + 0x4, 0x4, 0x3c, 0x8, 0x47, 0xf8, + + /* U+5B95 "宕" */ + 0x1, 0x0, 0x2, 0x3, 0xff, 0xfc, 0x0, 0x18, + 0x0, 0x3f, 0xff, 0xc1, 0x0, 0x4, 0x0, 0x18, + 0x0, 0x7f, 0xf1, 0x80, 0x25, 0x0, 0x42, 0x0, + 0x83, 0xff, 0x8, 0x2, 0x0, + + /* U+5B97 "宗" */ + 0x1, 0x0, 0x3, 0x3, 0xff, 0xfe, 0x0, 0x1c, + 0x0, 0x23, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, + 0xfe, 0x2, 0x0, 0x24, 0x0, 0x88, 0xc6, 0x10, + 0x58, 0x20, 0x41, 0xc0, 0x0, + + /* U+5B98 "官" */ + 0x2, 0x0, 0x4, 0xf, 0xff, 0xe0, 0x1, 0xbf, + 0xf4, 0x80, 0x42, 0x1, 0xf, 0xfc, 0x20, 0x0, + 0x80, 0x3, 0xff, 0x8, 0x4, 0x20, 0x10, 0xff, + 0xc2, 0x1, 0x0, + + /* U+5B99 "宙" */ + 0x2, 0x0, 0x8, 0xf, 0xff, 0xe0, 0x81, 0x82, + 0x4, 0x8, 0x7, 0xff, 0x90, 0x82, 0x42, 0x9, + 0xff, 0xe4, 0x20, 0x90, 0x82, 0x42, 0x9, 0xff, + 0xe4, 0x0, 0x80, + + /* U+5B9A "定" */ + 0x1, 0x0, 0x2, 0x1, 0xff, 0xfc, 0x0, 0x10, + 0x0, 0x3f, 0xff, 0xc0, 0x20, 0x0, 0x40, 0x10, + 0x80, 0x41, 0xf8, 0x82, 0x1, 0x84, 0x5, 0x88, + 0x9, 0x90, 0x20, 0xff, 0x80, + + /* U+5B9B "宛" */ + 0x0, 0x80, 0x1, 0x1, 0xff, 0xfa, 0x0, 0x15, + 0x0, 0x23, 0xef, 0x84, 0x51, 0x10, 0xa2, 0x72, + 0x44, 0x94, 0x88, 0x11, 0x60, 0x22, 0x10, 0x84, + 0x22, 0x8, 0x58, 0xf, 0x0, + + /* U+5B9C "宜" */ + 0x1, 0x0, 0x2, 0x1, 0xff, 0xf2, 0x0, 0x25, + 0xff, 0x42, 0x2, 0x4, 0x4, 0xf, 0xf8, 0x10, + 0x10, 0x20, 0x20, 0x7f, 0xc0, 0x80, 0x81, 0x1, + 0x1f, 0xff, 0xc0, + + /* U+5B9D "宝" */ + 0x1, 0x0, 0x2, 0x3, 0xff, 0xf4, 0x0, 0x28, + 0x0, 0xf, 0xff, 0x80, 0x40, 0x0, 0x80, 0x1, + 0x0, 0xff, 0xf0, 0x4, 0x0, 0x8, 0x80, 0x10, + 0x1f, 0xff, 0xc0, + + /* U+5B9E "实" */ + 0x1, 0x0, 0x2, 0x3, 0xff, 0xfc, 0x4, 0x19, + 0x88, 0x20, 0xd0, 0x8, 0x20, 0xc, 0x40, 0x0, + 0x81, 0xff, 0xfc, 0x4, 0x0, 0x16, 0x0, 0xc3, + 0x9e, 0x0, 0xc0, 0x0, 0x0, + + /* U+5BA0 "宠" */ + 0x1, 0x0, 0x2, 0x3, 0xff, 0xfc, 0x0, 0x18, + 0x44, 0x20, 0x84, 0x3f, 0xff, 0x82, 0x40, 0x8, + 0x80, 0x11, 0x10, 0x22, 0xc0, 0x86, 0x12, 0x38, + 0x2d, 0x90, 0x60, 0x3f, 0x0, + + /* U+5BA1 "审" */ + 0x1, 0x0, 0x4, 0xf, 0xff, 0xe0, 0x1, 0x81, + 0x0, 0xff, 0xe4, 0x10, 0x90, 0x42, 0x3f, 0xf9, + 0x4, 0x24, 0x10, 0x8f, 0xfe, 0x1, 0x8, 0x4, + 0x0, 0x10, 0x0, + + /* U+5BA2 "客" */ + 0x1, 0x0, 0x1, 0x0, 0x7f, 0xfe, 0x44, 0x2, + 0x4f, 0xfa, 0x1c, 0x10, 0x22, 0x20, 0x1, 0xc0, + 0xe, 0x7f, 0xf0, 0x0, 0x1f, 0xf8, 0x10, 0x8, + 0x10, 0x8, 0x1f, 0xf8, 0x10, 0x8, + + /* U+5BA3 "宣" */ + 0x1, 0x0, 0x2, 0x1, 0xff, 0xf4, 0x0, 0x21, + 0xff, 0x40, 0x0, 0xf, 0xfe, 0x10, 0x4, 0x3f, + 0xf8, 0x40, 0x10, 0x80, 0x21, 0xff, 0xc0, 0x0, + 0x1f, 0xff, 0xc0, + + /* U+5BA4 "室" */ + 0x2, 0x0, 0x2, 0x1, 0xff, 0xf4, 0x0, 0x23, + 0xff, 0x40, 0x80, 0x2, 0x18, 0x8, 0x10, 0x3f, + 0xf0, 0x2, 0x10, 0x4, 0x3, 0xff, 0xc0, 0x10, + 0x1f, 0xff, 0xc0, + + /* U+5BA5 "宥" */ + 0x2, 0x0, 0x2, 0x3, 0xff, 0xf4, 0x0, 0x28, + 0x40, 0x1f, 0xff, 0xc2, 0x0, 0x8, 0x0, 0x3f, + 0xf8, 0xc0, 0x12, 0x7f, 0xe1, 0x0, 0x41, 0xff, + 0x84, 0x1, 0x8, 0x2, 0x10, 0x78, + + /* U+5BA6 "宦" */ + 0x2, 0x0, 0x4, 0xf, 0xff, 0xe0, 0x1, 0xbf, + 0xfd, 0x4, 0x4, 0x10, 0xf, 0xfe, 0x40, 0x9, + 0x0, 0x23, 0xff, 0x90, 0x40, 0x41, 0x0, 0xff, + 0xf0, + + /* U+5BAA "宪" */ + 0x2, 0x0, 0x4, 0x3, 0xff, 0xf4, 0x0, 0x29, + 0x10, 0x47, 0xff, 0x8, 0x40, 0x20, 0x80, 0x1, + 0x1, 0xff, 0xf8, 0x22, 0x0, 0x44, 0x1, 0x88, + 0x46, 0x10, 0xb8, 0x1f, 0x0, + + /* U+5BAB "宫" */ + 0x2, 0x0, 0x4, 0xf, 0xff, 0xe0, 0x1, 0xbf, + 0xf4, 0x80, 0x42, 0x1, 0xf, 0xfc, 0x0, 0x0, + 0x0, 0x7, 0xff, 0x90, 0x2, 0x40, 0x9, 0xff, + 0xe4, 0x0, 0x80, + + /* U+5BB0 "宰" */ + 0x1, 0x0, 0x2, 0x1, 0xff, 0xf2, 0x8, 0x24, + 0x10, 0x47, 0xff, 0x6, 0xc, 0x2, 0x20, 0xff, + 0xfe, 0x2, 0x0, 0x4, 0x3, 0xff, 0xe0, 0x10, + 0x0, 0x20, 0x0, 0x40, 0x0, + + /* U+5BB3 "害" */ + 0x1, 0x0, 0x4, 0xf, 0xff, 0xe0, 0x1, 0x1, + 0x0, 0xff, 0xe0, 0x10, 0xf, 0xfc, 0x1, 0x3, + 0xff, 0xf0, 0x10, 0xf, 0xfc, 0x20, 0x10, 0x80, + 0x43, 0xff, 0x8, 0x4, + + /* U+5BB4 "宴" */ + 0x1, 0x0, 0x2, 0x3, 0xff, 0xf4, 0x0, 0x21, + 0xff, 0x4, 0x2, 0x7, 0xfc, 0x10, 0x8, 0x1f, + 0xf0, 0x8, 0x3, 0xff, 0xf8, 0x41, 0x1, 0xc4, + 0x0, 0x7c, 0x3e, 0x7, 0x0, + + /* U+5BB5 "宵" */ + 0x1, 0x0, 0x4, 0xf, 0xff, 0xe0, 0x1, 0xa1, + 0x14, 0x64, 0x80, 0x10, 0x1f, 0xfe, 0x40, 0x9, + 0xff, 0xe4, 0x0, 0x90, 0x2, 0x7f, 0xf9, 0x0, + 0x24, 0x0, 0x90, 0x1e, + + /* U+5BB6 "家" */ + 0x2, 0x0, 0x8, 0xf, 0xff, 0xe0, 0x1, 0xbf, + 0xf4, 0x10, 0x0, 0xc1, 0x8c, 0x98, 0xc7, 0xc0, + 0x65, 0x6, 0x32, 0x23, 0x4c, 0x19, 0x19, 0x84, + 0x18, 0xe0, 0x0, + + /* U+5BB8 "宸" */ + 0x0, 0x0, 0x4, 0xf, 0xff, 0xe0, 0x1, 0x80, + 0x0, 0xff, 0xe2, 0x0, 0xb, 0xfe, 0x40, 0x0, + 0xff, 0xf4, 0x90, 0x12, 0x26, 0x48, 0x61, 0x20, + 0x88, 0xf1, 0xc0, 0x0, + + /* U+5BB9 "容" */ + 0x1, 0x0, 0x0, 0x80, 0x7f, 0xfe, 0x40, 0x2, + 0x44, 0x22, 0x18, 0x9c, 0x63, 0xc2, 0x4, 0x20, + 0x18, 0x1c, 0xff, 0xfa, 0x10, 0x8, 0x10, 0x8, + 0x10, 0x8, 0x1f, 0xf8, 0x10, 0x8, + + /* U+5BBD "宽" */ + 0x0, 0x0, 0x2, 0x3, 0xff, 0xfc, 0x0, 0x10, + 0x84, 0x1f, 0xff, 0xc2, 0x10, 0x0, 0x0, 0x3f, + 0xf8, 0x40, 0x10, 0x88, 0x21, 0x18, 0x42, 0x30, + 0x80, 0xa0, 0x42, 0x40, 0xf8, 0x7e, + + /* U+5BBE "宾" */ + 0x1, 0x0, 0x2, 0x1, 0xff, 0xfa, 0x0, 0x15, + 0xff, 0xa2, 0x0, 0x4, 0x0, 0xf, 0xff, 0x10, + 0x20, 0x20, 0x43, 0xff, 0xf8, 0x0, 0x0, 0xc3, + 0xe, 0x1, 0xc0, 0x0, 0x0, + + /* U+5BBF "宿" */ + 0x1, 0x0, 0x4, 0x7, 0xff, 0xd0, 0x1, 0x57, + 0xfc, 0x41, 0x2, 0x8, 0x9, 0xfe, 0x64, 0xb, + 0x90, 0x22, 0x7f, 0x89, 0x2, 0x24, 0x8, 0x9f, + 0xe2, 0x40, 0x80, + + /* U+5BC2 "寂" */ + 0x2, 0x0, 0x4, 0x3, 0xff, 0xf4, 0x0, 0x29, + 0x0, 0x43, 0xbf, 0x84, 0x22, 0x8, 0x44, 0xfe, + 0x88, 0x20, 0xa1, 0x51, 0x42, 0x91, 0x9, 0x5, + 0x2, 0x31, 0x9c, 0xc1, 0x0, + + /* U+5BC4 "寄" */ + 0x1, 0x0, 0x2, 0x1, 0xff, 0xf2, 0x0, 0x24, + 0x8, 0x3, 0xff, 0x0, 0x70, 0x1f, 0x1e, 0xff, + 0xfe, 0x0, 0x10, 0xff, 0x21, 0x2, 0x42, 0x4, + 0x87, 0xf9, 0x0, 0x2, 0x0, 0x38, + + /* U+5BC5 "寅" */ + 0x1, 0x0, 0x2, 0x3, 0xff, 0xf4, 0x0, 0x28, + 0x0, 0x47, 0xfe, 0x0, 0x40, 0x1f, 0xfc, 0x41, + 0x8, 0x82, 0x10, 0xff, 0xe2, 0x8, 0x43, 0xff, + 0x80, 0x0, 0xe, 0x1e, 0x60, 0x2, + + /* U+5BC6 "密" */ + 0x0, 0x80, 0x0, 0x80, 0x7f, 0xfe, 0x40, 0x2, + 0x44, 0x88, 0x14, 0x34, 0x24, 0xc2, 0x47, 0x9, + 0xc, 0x10, 0x73, 0xf0, 0x0, 0x80, 0x0, 0x80, + 0x20, 0x84, 0x20, 0x84, 0x1f, 0xfc, 0x0, 0x4, + + /* U+5BC7 "寇" */ + 0x0, 0x80, 0x0, 0x80, 0x7f, 0xfe, 0x40, 0x2, + 0x40, 0x22, 0x1f, 0x3c, 0x0, 0x20, 0x0, 0x20, + 0x7f, 0x7c, 0x12, 0xc, 0x12, 0x68, 0x12, 0x30, + 0x12, 0x29, 0x22, 0xc1, 0x22, 0x2, 0x43, 0xfe, + + /* U+5BCC "富" */ + 0x2, 0x0, 0x8, 0xf, 0xff, 0xe0, 0x1, 0xbf, + 0xf4, 0x0, 0x3, 0xff, 0x8, 0x4, 0x3f, 0xf0, + 0x0, 0x7, 0xff, 0x90, 0x82, 0x7f, 0xf9, 0x8, + 0x27, 0xff, 0x90, 0x2, + + /* U+5BD0 "寐" */ + 0x1, 0x0, 0x2, 0x1, 0xff, 0xfa, 0x0, 0x10, + 0x0, 0x9, 0x3f, 0x92, 0x8, 0x3c, 0x10, 0x8, + 0x20, 0x17, 0xff, 0xe1, 0xc1, 0x45, 0x82, 0x8a, + 0x89, 0x24, 0x92, 0x88, 0xc4, 0x10, + + /* U+5BD2 "寒" */ + 0x0, 0x0, 0x4, 0xf, 0xff, 0xe2, 0x11, 0x88, + 0x44, 0xff, 0xc0, 0x84, 0x1f, 0xfe, 0x8, 0x43, + 0xff, 0xf0, 0x84, 0x5, 0x8, 0x63, 0xda, 0x0, + 0x10, 0xf8, 0x0, 0x10, + + /* U+5BD3 "寓" */ + 0x1, 0x0, 0x4, 0xf, 0xff, 0xe0, 0x1, 0xbf, + 0xf4, 0x84, 0x43, 0xff, 0x8, 0x44, 0x21, 0x10, + 0xff, 0xc0, 0x10, 0x1f, 0xfe, 0x41, 0x9, 0xff, + 0xa4, 0x0, 0x90, 0xe, + + /* U+5BDD "寝" */ + 0x1, 0x0, 0xff, 0xfd, 0xf, 0xe8, 0x80, 0x41, + 0x3f, 0x8a, 0x1, 0x14, 0xfc, 0xb, 0xff, 0x14, + 0x2, 0xe7, 0xf3, 0x44, 0x20, 0x85, 0x1, 0xf, + 0x2, 0xe1, 0xc0, + + /* U+5BDE "寞" */ + 0x0, 0x0, 0x2, 0x3, 0xff, 0xfc, 0x42, 0x1b, + 0xff, 0xa1, 0x8, 0xf, 0xfe, 0x10, 0x4, 0x3f, + 0xf8, 0x40, 0x10, 0xff, 0xe0, 0x8, 0xf, 0xff, + 0xe0, 0x50, 0x3, 0x18, 0x78, 0xf, + + /* U+5BDF "察" */ + 0x0, 0x0, 0x1, 0x1, 0xff, 0xfa, 0x0, 0x14, + 0x88, 0x23, 0xef, 0x88, 0x51, 0x3d, 0x14, 0x64, + 0x10, 0x37, 0xd1, 0xc0, 0x1a, 0xff, 0xc0, 0x8, + 0x3, 0x13, 0x18, 0x21, 0x83, 0x80, + + /* U+5BE1 "寡" */ + 0x1, 0x1, 0xff, 0xfe, 0x0, 0xc, 0xff, 0xd0, + 0x10, 0x3, 0xdf, 0x7, 0xfe, 0x8, 0x4, 0x1f, + 0xf9, 0xff, 0xfc, 0x20, 0x81, 0xff, 0xcc, 0x22, + 0x60, 0x84, 0xe, 0x38, 0x0, + + /* U+5BE4 "寤" */ + 0x1, 0x1, 0xff, 0xfe, 0xf, 0xea, 0x44, 0x4, + 0x88, 0x9, 0x7f, 0xe, 0x42, 0x5, 0xfe, 0xf8, + 0x0, 0x57, 0xf1, 0x28, 0x22, 0x50, 0x44, 0xbf, + 0x91, 0x41, 0x0, + + /* U+5BE5 "寥" */ + 0x0, 0x0, 0x0, 0x80, 0x7f, 0xfe, 0x40, 0x2, + 0x3f, 0xfc, 0x11, 0x44, 0x9, 0x24, 0xf, 0x7c, + 0x31, 0x84, 0x6, 0x60, 0x79, 0x9f, 0xe, 0x20, + 0x1, 0xc8, 0x1e, 0x30, 0x1, 0xc0, 0x1e, 0x0, + + /* U+5BE8 "寨" */ + 0x1, 0x0, 0x2, 0x3, 0xff, 0xfc, 0x20, 0x13, + 0xff, 0xa0, 0x88, 0xf, 0xff, 0x2, 0x20, 0xff, + 0xfe, 0x8, 0x40, 0x40, 0x43, 0x7f, 0x68, 0x10, + 0x21, 0xa4, 0x1c, 0x47, 0x3, 0x80, + + /* U+5BEE "寮" */ + 0x0, 0x80, 0x7f, 0xfe, 0x40, 0x82, 0x3f, 0xfc, + 0x3, 0x44, 0x1c, 0x38, 0x1f, 0xfc, 0x70, 0xa, + 0x1f, 0xf8, 0x10, 0x8, 0x1f, 0xf8, 0x0, 0x80, + 0xc, 0x98, 0x30, 0x86, 0x43, 0x80, + + /* U+5BF0 "寰" */ + 0x0, 0x0, 0x4, 0xf, 0xff, 0xe0, 0x1, 0xbf, + 0xf4, 0x92, 0x43, 0xff, 0x0, 0x0, 0x7f, 0xf8, + 0xff, 0xc2, 0x1, 0xf, 0xfc, 0x3, 0x8, 0x72, + 0xce, 0x9f, 0x3, 0x83, + + /* U+5BF8 "寸" */ + 0x0, 0x20, 0x0, 0x40, 0x0, 0x87, 0xff, 0xf0, + 0x2, 0x0, 0x4, 0x4, 0x8, 0x4, 0x10, 0x4, + 0x20, 0xc, 0x40, 0x0, 0x80, 0x1, 0x0, 0x2, + 0x0, 0x4, 0x0, 0xf8, 0x0, + + /* U+5BF9 "对" */ + 0x0, 0x4, 0x7e, 0x4, 0x2, 0x4, 0x2, 0xff, + 0x42, 0x4, 0x24, 0x4, 0x14, 0x84, 0x14, 0x44, + 0x8, 0x44, 0xc, 0x24, 0x14, 0x4, 0x12, 0x4, + 0x23, 0x4, 0x40, 0x4, 0x0, 0x38, + + /* U+5BFA "寺" */ + 0x1, 0x0, 0x2, 0x1, 0xff, 0xe0, 0x8, 0x0, + 0x10, 0x1f, 0xff, 0xc0, 0x0, 0x0, 0x10, 0xff, + 0xfe, 0x0, 0x40, 0x40, 0x80, 0x41, 0x0, 0x42, + 0x0, 0x4, 0x0, 0x78, 0x0, + + /* U+5BFB "寻" */ + 0x7f, 0xf8, 0x0, 0x11, 0xff, 0xe0, 0x0, 0x40, + 0x0, 0x8f, 0xff, 0x0, 0x0, 0x0, 0x8, 0xff, + 0xfe, 0x0, 0x20, 0x40, 0x40, 0x40, 0x80, 0x1, + 0x0, 0x3c, 0x0, + + /* U+5BFC "导" */ + 0x3f, 0xf8, 0x40, 0x10, 0x80, 0x21, 0xff, 0xc2, + 0x0, 0x24, 0x0, 0x4f, 0xff, 0x0, 0x0, 0xff, + 0xfe, 0x20, 0x20, 0x60, 0x40, 0x60, 0x80, 0x1, + 0x0, 0x1c, 0x0, + + /* U+5BFF "寿" */ + 0x1, 0x0, 0x2, 0x1, 0xff, 0xf8, 0x8, 0x0, + 0x10, 0x7, 0xff, 0x80, 0x80, 0x7f, 0xff, 0x4, + 0x0, 0x10, 0x0, 0x3f, 0xf8, 0x80, 0x82, 0x61, + 0x8, 0x22, 0x20, 0x4, 0x0, 0x78, + + /* U+5C01 "封" */ + 0x10, 0x8, 0x20, 0x13, 0xf8, 0x20, 0x8f, 0xf1, + 0x0, 0x82, 0x1, 0x3f, 0xa2, 0x0, 0x44, 0x10, + 0x49, 0xfc, 0x10, 0x40, 0x20, 0x80, 0x41, 0x0, + 0x9f, 0xc1, 0x0, 0x1c, 0x0, + + /* U+5C04 "射" */ + 0x10, 0x8, 0x20, 0x11, 0xf8, 0x22, 0x10, 0x47, + 0xef, 0xc8, 0x41, 0x1f, 0xa2, 0x21, 0x44, 0x42, + 0x49, 0xfc, 0xd0, 0x18, 0x20, 0x50, 0x43, 0x20, + 0x98, 0x41, 0x7, 0x9c, 0x0, + + /* U+5C06 "将" */ + 0x10, 0x80, 0x23, 0x0, 0x4f, 0xfc, 0xb0, 0x25, + 0x91, 0x8a, 0xc, 0x4, 0xe0, 0xa, 0x4, 0x3f, + 0xfe, 0xa0, 0x12, 0x48, 0x20, 0x88, 0x41, 0x0, + 0x82, 0x1, 0x4, 0x1c, 0x0, + + /* U+5C09 "尉" */ + 0x0, 0x9, 0xfe, 0x24, 0x8, 0x90, 0x22, 0x7f, + 0xfd, 0x0, 0x25, 0xf8, 0x90, 0x12, 0x40, 0x49, + 0xfe, 0xa4, 0x42, 0xb5, 0x2, 0xa4, 0x8a, 0x10, + 0x21, 0xc7, 0x80, + + /* U+5C0A "尊" */ + 0x8, 0x20, 0x8, 0x83, 0xff, 0xf8, 0x24, 0x3, + 0xff, 0xc8, 0x90, 0x93, 0x1d, 0x20, 0x2, 0x3f, + 0xfc, 0x80, 0x8, 0xff, 0xf0, 0x0, 0x8f, 0xff, + 0xe2, 0x2, 0x3, 0x4, 0x0, 0x70, + + /* U+5C0F "小" */ + 0x1, 0x0, 0x2, 0x0, 0x4, 0x0, 0x8, 0x1, + 0x13, 0x2, 0x22, 0x8, 0x42, 0x10, 0x84, 0x41, + 0x4, 0x82, 0xa, 0x4, 0x1c, 0x8, 0x0, 0x10, + 0x0, 0x20, 0x3, 0xc0, 0x0, + + /* U+5C11 "少" */ + 0x1, 0x0, 0x2, 0x0, 0x64, 0xc0, 0x88, 0x81, + 0x10, 0x84, 0x20, 0x98, 0x40, 0xa0, 0x84, 0x81, + 0x10, 0x0, 0x40, 0x3, 0x0, 0xc, 0x0, 0x60, + 0xe, 0x0, 0x20, 0x0, 0x0, + + /* U+5C14 "尔" */ + 0x8, 0x0, 0x20, 0x0, 0x7f, 0xf9, 0x0, 0x24, + 0x10, 0x58, 0x21, 0x20, 0x40, 0x8, 0x90, 0x11, + 0x10, 0x42, 0x10, 0x84, 0x22, 0x8, 0x28, 0x10, + 0x60, 0x20, 0x3, 0xc0, 0x0, + + /* U+5C15 "尕" */ + 0x7f, 0xf0, 0x8, 0x40, 0x11, 0xf0, 0x40, 0x20, + 0x80, 0x46, 0x0, 0xb8, 0x4e, 0x0, 0x80, 0x19, + 0x20, 0x22, 0x20, 0x84, 0x36, 0x8, 0x30, 0x10, + 0x1, 0xc0, 0x0, + + /* U+5C16 "尖" */ + 0x0, 0x80, 0x1, 0x0, 0x22, 0x40, 0x84, 0x66, + 0x8, 0x20, 0x0, 0x0, 0x40, 0x0, 0x80, 0x7f, + 0xfe, 0x3, 0x0, 0x6, 0x0, 0x12, 0x0, 0xc3, + 0x7, 0x1, 0x90, 0x0, 0x80, + + /* U+5C18 "尘" */ + 0x1, 0x0, 0x2, 0x0, 0x24, 0x80, 0x88, 0xc2, + 0x10, 0xc8, 0x20, 0x80, 0x0, 0x0, 0x80, 0x1, + 0x0, 0xff, 0xf8, 0x4, 0x0, 0x8, 0x0, 0x10, + 0x1f, 0xff, 0xc0, + + /* U+5C1A "尚" */ + 0x2, 0x2, 0x10, 0x98, 0x8c, 0x64, 0xc0, 0x20, + 0x7f, 0xfe, 0x0, 0x30, 0x1, 0x9f, 0xcc, 0x82, + 0x64, 0x13, 0x20, 0x99, 0xfc, 0xc0, 0x6, 0x0, + 0x30, 0xf, + + /* U+5C1C "尜" */ + 0x2, 0x0, 0x28, 0x83, 0x21, 0x90, 0x81, 0x2, + 0x0, 0x8, 0xf, 0xff, 0xc1, 0x20, 0x18, 0x61, + 0x88, 0x68, 0x20, 0x44, 0x88, 0x22, 0x1b, 0x8, + 0x10, 0xe0, 0x0, + + /* U+5C1D "尝" */ + 0x1, 0x0, 0x62, 0x30, 0x64, 0xc0, 0x8, 0xf, + 0xff, 0xf0, 0x0, 0x60, 0x0, 0x9f, 0xfc, 0x0, + 0x0, 0x0, 0x3, 0xff, 0xf8, 0x20, 0x0, 0x86, + 0x2, 0x2, 0xf, 0xfe, 0x0, 0x0, + + /* U+5C22 "尢" */ + 0x2, 0x0, 0x8, 0x0, 0x20, 0x0, 0x80, 0xff, + 0xfc, 0x12, 0x0, 0x48, 0x1, 0x20, 0x4, 0x80, + 0x22, 0x1, 0x88, 0x44, 0x21, 0x60, 0x87, 0x3, + 0xf0, 0x0, 0x0, + + /* U+5C24 "尤" */ + 0x2, 0x0, 0x4, 0x40, 0x8, 0x40, 0x10, 0xf, + 0xff, 0xe0, 0x90, 0x1, 0x20, 0x2, 0x40, 0x4, + 0x80, 0x11, 0x0, 0x22, 0x8, 0x84, 0x12, 0x8, + 0x38, 0xf, 0xc0, + + /* U+5C25 "尥" */ + 0x20, 0x40, 0x20, 0x40, 0x20, 0xfc, 0xfe, 0x84, + 0x29, 0x4, 0x29, 0x4, 0x28, 0x44, 0x28, 0x44, + 0x28, 0x24, 0x28, 0x4, 0x48, 0xd, 0x48, 0x3a, + 0x4c, 0x2, 0x87, 0xfe, 0x80, 0x0, + + /* U+5C27 "尧" */ + 0x0, 0x0, 0x10, 0x0, 0x3f, 0xe7, 0xa0, 0x0, + 0x23, 0x40, 0x38, 0x83, 0xb1, 0x78, 0x1c, 0x0, + 0x1, 0xff, 0xf8, 0x22, 0x0, 0x44, 0x0, 0x88, + 0x42, 0x10, 0x8c, 0x21, 0x60, 0x3e, + + /* U+5C2C "尬" */ + 0x20, 0x20, 0x40, 0x60, 0x81, 0x27, 0xf4, 0x22, + 0x90, 0x25, 0x9, 0xa, 0x12, 0x14, 0x24, 0x28, + 0x48, 0x51, 0x11, 0x22, 0x22, 0x48, 0x54, 0x80, + 0x31, 0x0, 0xe1, 0xff, 0x0, + + /* U+5C31 "就" */ + 0x0, 0x40, 0x20, 0x80, 0x21, 0x27, 0xfa, 0x20, + 0x4, 0xf, 0xdf, 0x90, 0x94, 0x21, 0x28, 0x42, + 0x50, 0xfc, 0xa0, 0x41, 0x42, 0x94, 0x85, 0x29, + 0x32, 0x12, 0x44, 0x44, 0x99, 0xe, + + /* U+5C34 "尴" */ + 0x40, 0x88, 0x89, 0x21, 0x12, 0x7f, 0xe4, 0x85, + 0x4a, 0x8a, 0x94, 0x94, 0x0, 0x29, 0xfe, 0x54, + 0x94, 0xa9, 0x29, 0x52, 0x52, 0xbf, 0xe5, 0x0, + 0x32, 0x0, 0x63, 0xff, 0x0, + + /* U+5C38 "尸" */ + 0x3f, 0xfc, 0x80, 0x12, 0x0, 0x48, 0x1, 0x20, + 0x4, 0xff, 0xf2, 0x0, 0x48, 0x0, 0x20, 0x0, + 0x80, 0x4, 0x0, 0x10, 0x0, 0x40, 0x2, 0x0, + 0x0, + + /* U+5C39 "尹" */ + 0x3f, 0xf8, 0x8, 0x10, 0x10, 0x20, 0x20, 0x4f, + 0xff, 0xe0, 0x81, 0x1, 0x2, 0x2, 0x4, 0x7f, + 0xf8, 0x10, 0x10, 0x20, 0x0, 0xc0, 0x3, 0x0, + 0xc, 0x0, 0x0, 0x0, 0x0, + + /* U+5C3A "尺" */ + 0x3f, 0xf8, 0x40, 0x10, 0x80, 0x21, 0x0, 0x42, + 0x0, 0x87, 0xff, 0x8, 0x82, 0x10, 0x80, 0x21, + 0x0, 0x41, 0x1, 0x1, 0x2, 0x1, 0x8, 0x1, + 0x90, 0x0, 0x80, + + /* U+5C3B "尻" */ + 0x3f, 0xfc, 0x40, 0x8, 0x80, 0x11, 0xff, 0xe2, + 0x0, 0x4, 0x20, 0x8, 0x40, 0x1f, 0xf8, 0x21, + 0x10, 0x42, 0x20, 0x8c, 0x42, 0x10, 0x94, 0x41, + 0x33, 0x3, 0xc0, + + /* U+5C3C "尼" */ + 0x3f, 0xfc, 0x40, 0x8, 0x80, 0x11, 0xff, 0xe2, + 0x0, 0x4, 0x80, 0x9, 0x1, 0x12, 0xc, 0x24, + 0x60, 0x4f, 0x1, 0x10, 0xa, 0x20, 0x14, 0x40, + 0x50, 0xff, 0x80, + + /* U+5C3D "尽" */ + 0x3f, 0xf8, 0x40, 0x10, 0x80, 0x21, 0x0, 0x43, + 0xff, 0x84, 0x10, 0x8, 0x10, 0x10, 0x10, 0x27, + 0x30, 0x81, 0x11, 0x0, 0x14, 0x40, 0x0, 0x70, + 0x0, 0x18, 0x0, + + /* U+5C3E "尾" */ + 0x3f, 0xfc, 0x40, 0x8, 0xff, 0xf1, 0x0, 0x2, + 0x1, 0x85, 0xfc, 0x8, 0x20, 0x11, 0xfe, 0x2f, + 0x80, 0x41, 0x0, 0x8f, 0xfa, 0x64, 0x14, 0x8, + 0x28, 0xf, 0x80, + + /* U+5C3F "尿" */ + 0x3f, 0xfe, 0x20, 0x2, 0x20, 0x2, 0x20, 0x2, + 0x3f, 0xfe, 0x20, 0x0, 0x20, 0x42, 0x2f, 0x44, + 0x21, 0x68, 0x22, 0x50, 0x22, 0x50, 0x44, 0x48, + 0x48, 0x44, 0x50, 0x43, 0x83, 0xc0, + + /* U+5C40 "局" */ + 0x3f, 0xfc, 0x40, 0x8, 0x80, 0x11, 0xff, 0xe2, + 0x0, 0x7, 0xff, 0xc8, 0x0, 0x97, 0xf1, 0x28, + 0x22, 0x50, 0x44, 0xbf, 0x8a, 0x0, 0x24, 0x0, + 0x50, 0xf, 0x0, + + /* U+5C41 "屁" */ + 0x3f, 0xfe, 0x20, 0x2, 0x20, 0x2, 0x20, 0x2, + 0x3f, 0xfe, 0x20, 0x0, 0x24, 0x20, 0x24, 0x22, + 0x27, 0xac, 0x24, 0x30, 0x24, 0x20, 0x64, 0x21, + 0x44, 0x21, 0x45, 0xa2, 0x86, 0x3e, + + /* U+5C42 "层" */ + 0x3f, 0xfe, 0x40, 0x4, 0x80, 0x9, 0xff, 0xf2, + 0x0, 0x4, 0xff, 0x88, 0x0, 0x10, 0x0, 0x2f, + 0xfe, 0x43, 0x1, 0xc, 0x42, 0x20, 0x4c, 0xff, + 0xc0, 0x0, 0x40, + + /* U+5C45 "居" */ + 0x3f, 0xfc, 0x40, 0x8, 0x80, 0x11, 0x0, 0x23, + 0xff, 0xc4, 0x10, 0x8, 0x20, 0x1f, 0xff, 0x20, + 0x80, 0x5f, 0xf8, 0xa0, 0x13, 0x40, 0x24, 0x80, + 0x49, 0xff, 0xa2, 0x1, 0x0, + + /* U+5C48 "屈" */ + 0x3f, 0xfe, 0x40, 0x4, 0x80, 0x9, 0xff, 0xf2, + 0x44, 0x44, 0x88, 0x89, 0x11, 0x13, 0xfe, 0x20, + 0x40, 0x40, 0x81, 0xa1, 0xa, 0x42, 0x14, 0xff, + 0xf0, 0x0, 0x40, + + /* U+5C49 "屉" */ + 0x3f, 0xfe, 0x20, 0x2, 0x3f, 0xfe, 0x20, 0x88, + 0x24, 0x88, 0x24, 0x88, 0x3f, 0xff, 0x24, 0x88, + 0x24, 0x88, 0x24, 0xf8, 0x64, 0x0, 0x44, 0x0, + 0xc7, 0xfe, + + /* U+5C4A "届" */ + 0x3f, 0xfc, 0x80, 0x12, 0x0, 0x4f, 0xff, 0x20, + 0x40, 0x81, 0x2, 0xff, 0xca, 0x11, 0x28, 0x44, + 0xbf, 0xf2, 0x84, 0x52, 0x11, 0x4f, 0xfc, 0x20, + 0x10, + + /* U+5C4B "屋" */ + 0x3f, 0xfe, 0x20, 0x2, 0x3f, 0xfe, 0x20, 0x0, + 0x2f, 0xfe, 0x21, 0x0, 0x22, 0x18, 0x27, 0xfc, + 0x20, 0x42, 0x20, 0x40, 0x4f, 0xfe, 0x40, 0x40, + 0x5f, 0xff, 0x0, 0x0, + + /* U+5C4E "屎" */ + 0x3f, 0xfe, 0x40, 0x4, 0x80, 0x9, 0x0, 0x13, + 0xff, 0xe4, 0x0, 0x8, 0x11, 0x13, 0x24, 0x20, + 0x40, 0x7f, 0xfc, 0x83, 0x82, 0xa, 0x84, 0x64, + 0x8b, 0x8, 0xe0, 0x10, 0x0, + + /* U+5C4F "屏" */ + 0x3f, 0xfc, 0x40, 0x8, 0xff, 0xf1, 0x10, 0x82, + 0x23, 0x4, 0x4, 0xb, 0xff, 0x11, 0x10, 0x22, + 0x20, 0x7f, 0xfc, 0x88, 0x82, 0x11, 0x4, 0x42, + 0x11, 0x4, 0x0, + + /* U+5C50 "屐" */ + 0x7f, 0xfc, 0x80, 0x9, 0xff, 0xf2, 0x0, 0x4, + 0x42, 0x9, 0x7f, 0xd5, 0x8, 0x22, 0x10, 0x4b, + 0xfc, 0xb2, 0x9, 0xa2, 0x64, 0x43, 0x88, 0x8f, + 0x11, 0x61, 0xc0, + + /* U+5C51 "屑" */ + 0x3f, 0xfe, 0x40, 0x4, 0xff, 0xf9, 0x2, 0x2, + 0x44, 0x44, 0x49, 0xb, 0xff, 0x94, 0x1, 0x2f, + 0xfe, 0x50, 0x4, 0xbf, 0xfa, 0x40, 0x14, 0x80, + 0x31, 0x3, 0xc0, + + /* U+5C55 "展" */ + 0x3f, 0xfe, 0x20, 0x2, 0x20, 0x2, 0x3f, 0xfe, + 0x20, 0x0, 0x22, 0x10, 0x2f, 0xfe, 0x22, 0x10, + 0x22, 0x10, 0x3f, 0xff, 0x24, 0x80, 0x24, 0x4c, + 0x64, 0x30, 0x44, 0x98, 0x47, 0x7, + + /* U+5C59 "屙" */ + 0x3f, 0xfe, 0x20, 0x2, 0x3f, 0xfe, 0x20, 0x0, + 0x2f, 0x7f, 0x29, 0x2, 0x2a, 0x2, 0x2a, 0x7a, + 0x2a, 0x4a, 0x29, 0x4a, 0x29, 0x7a, 0x4f, 0x2, + 0x48, 0x4, 0x88, 0x1c, + + /* U+5C5E "属" */ + 0x3f, 0xfe, 0x40, 0x4, 0xff, 0xf9, 0x0, 0x2, + 0xff, 0xe4, 0x8, 0x9, 0xff, 0x12, 0x22, 0x27, + 0xfc, 0x40, 0x80, 0xbf, 0xfb, 0x42, 0x14, 0xbf, + 0xa9, 0x0, 0x62, 0x3, 0x80, + + /* U+5C60 "屠" */ + 0x3f, 0xfc, 0x40, 0x8, 0x80, 0x11, 0xff, 0xe2, + 0x8, 0x5, 0xfe, 0xc8, 0x26, 0x1f, 0xff, 0x20, + 0x80, 0x47, 0xf8, 0xf0, 0x12, 0x3f, 0xe4, 0x40, + 0x48, 0xff, 0xa1, 0x1, 0x0, + + /* U+5C61 "屡" */ + 0x3f, 0xfe, 0x20, 0x2, 0x20, 0x2, 0x3f, 0xfe, + 0x24, 0x44, 0x23, 0x58, 0x2f, 0xfe, 0x21, 0x50, + 0x26, 0x4e, 0x28, 0x80, 0x2f, 0xff, 0x62, 0x10, + 0x43, 0xb0, 0x41, 0xf8, 0x1e, 0x6, + + /* U+5C63 "屣" */ + 0x3f, 0xfe, 0x20, 0x2, 0x3f, 0xfe, 0x22, 0x10, + 0x24, 0x90, 0x24, 0x9e, 0x28, 0x90, 0x23, 0xff, + 0x24, 0x0, 0x2c, 0x10, 0x34, 0x9e, 0x44, 0x90, + 0x45, 0x50, 0x86, 0x2f, + + /* U+5C65 "履" */ + 0x3f, 0xfe, 0x20, 0x2, 0x3f, 0xfe, 0x20, 0x80, + 0x24, 0xfe, 0x29, 0x0, 0x31, 0xfe, 0x25, 0xfe, + 0x25, 0x2, 0x3c, 0xfc, 0x24, 0xfe, 0x25, 0x86, + 0x46, 0x48, 0x44, 0x78, 0x7, 0x87, + + /* U+5C66 "屦" */ + 0x3f, 0xfe, 0x20, 0x2, 0x20, 0x2, 0x3f, 0xfe, + 0x22, 0x20, 0x24, 0xac, 0x29, 0xfe, 0x22, 0x70, + 0x25, 0xae, 0x2c, 0x40, 0x25, 0xff, 0x64, 0x88, + 0x44, 0xd0, 0x44, 0x78, 0x85, 0x86, + + /* U+5C6E "屮" */ + 0x4, 0x0, 0x82, 0x10, 0xc2, 0x18, 0x43, 0x8, + 0x61, 0xc, 0x21, 0xff, 0xe0, 0x84, 0x10, 0x2, + 0x0, 0x40, 0x8, 0x1, 0x0, + + /* U+5C6F "屯" */ + 0x0, 0x0, 0x4, 0x3, 0xff, 0xf0, 0x10, 0x4, + 0x20, 0x88, 0x41, 0x10, 0x82, 0x21, 0x4, 0x7f, + 0xf8, 0x4, 0x0, 0x8, 0x0, 0x10, 0x20, 0x20, + 0x40, 0x20, 0x80, 0x7e, 0x0, + + /* U+5C71 "山" */ + 0x2, 0x0, 0x10, 0x0, 0x81, 0x4, 0x18, 0x20, + 0xc1, 0x6, 0x8, 0x30, 0x41, 0x82, 0xc, 0x10, + 0x60, 0x83, 0x4, 0x18, 0x20, 0xff, 0xfc, 0x0, + 0x20, + + /* U+5C79 "屹" */ + 0x20, 0x0, 0x41, 0x0, 0x84, 0x5, 0x4f, 0xea, + 0xa0, 0x15, 0x40, 0x2a, 0x7e, 0x54, 0x8, 0xa8, + 0x21, 0x50, 0x82, 0xa2, 0xd, 0xc8, 0x24, 0x90, + 0x40, 0x40, 0x80, 0x7e, 0x0, + + /* U+5C7A "屺" */ + 0x20, 0x0, 0x43, 0xf8, 0x80, 0x15, 0x20, 0x2a, + 0x40, 0x54, 0x80, 0xa9, 0x7f, 0x52, 0x80, 0xa5, + 0x1, 0x4a, 0x2, 0x94, 0xf, 0xe8, 0x20, 0x50, + 0x40, 0x1f, 0x80, + + /* U+5C7F "屿" */ + 0x21, 0x0, 0x84, 0x2, 0x10, 0x2a, 0x7f, 0xa9, + 0x2, 0xa4, 0xa, 0x9f, 0xea, 0x1, 0xa8, 0x6, + 0xa0, 0x1a, 0xbf, 0x6a, 0x2, 0x58, 0x8, 0x0, + 0x20, 0xf, 0x0, + + /* U+5C81 "岁" */ + 0x1, 0x1, 0x4, 0x14, 0x10, 0x50, 0x41, 0x7f, + 0xfc, 0x0, 0x0, 0x40, 0x3, 0xff, 0x10, 0xd, + 0xe0, 0x6c, 0x43, 0x0, 0x98, 0x1, 0x80, 0x18, + 0xf, 0x80, 0x0, + + /* U+5C82 "岂" */ + 0x2, 0x2, 0x8, 0x28, 0x20, 0xbf, 0xfe, 0x0, + 0x0, 0x0, 0xf, 0xff, 0x0, 0x4, 0x0, 0x13, + 0xff, 0xc8, 0x0, 0x20, 0x1, 0x80, 0x6, 0x0, + 0x17, 0xff, 0x80, + + /* U+5C88 "岈" */ + 0x20, 0x0, 0x43, 0xfc, 0x80, 0x25, 0x24, 0x4a, + 0x50, 0x94, 0xa1, 0x29, 0x42, 0x52, 0x7f, 0xa4, + 0x29, 0x48, 0x52, 0x91, 0x25, 0x24, 0x4f, 0xd0, + 0x80, 0x81, 0x0, 0x1e, 0x0, + + /* U+5C8C "岌" */ + 0x1, 0x0, 0x82, 0x11, 0x4, 0x23, 0xff, 0xc0, + 0x0, 0x0, 0x0, 0x1f, 0xfc, 0x4, 0x8, 0x8, + 0x20, 0x18, 0xf0, 0x48, 0x20, 0x90, 0x81, 0x12, + 0x4, 0x18, 0x10, 0x6c, 0x4f, 0x7, + + /* U+5C8D "岍" */ + 0x20, 0x0, 0x43, 0xfc, 0x82, 0x25, 0x24, 0x4a, + 0x48, 0x94, 0x91, 0x29, 0x22, 0x52, 0xff, 0xa4, + 0x89, 0x49, 0x12, 0x92, 0x25, 0xe4, 0x44, 0x50, + 0x80, 0x21, 0x0, 0x82, 0x0, + + /* U+5C90 "岐" */ + 0x20, 0x40, 0x81, 0x2, 0x4, 0x2a, 0xff, 0xa8, + 0x42, 0xa1, 0xa, 0xbf, 0xaa, 0x42, 0xa9, 0x12, + 0xa2, 0x4a, 0x8a, 0x2a, 0x10, 0xd8, 0xa0, 0xc, + 0x60, 0x40, 0x40, + + /* U+5C91 "岑" */ + 0x0, 0x80, 0x20, 0x84, 0x20, 0x84, 0x3f, 0xfc, + 0x0, 0x80, 0x1, 0x40, 0x6, 0x20, 0x19, 0x18, + 0x60, 0x87, 0x0, 0x0, 0x1f, 0xf8, 0x0, 0x10, + 0x0, 0x10, 0x0, 0x20, 0x0, 0x40, + + /* U+5C94 "岔" */ + 0x0, 0x40, 0xc, 0x20, 0x18, 0x18, 0x60, 0xe, + 0x1f, 0xfa, 0x4, 0x10, 0xc, 0x10, 0x39, 0xf0, + 0x60, 0x0, 0x1, 0x0, 0x21, 0x4, 0x21, 0x4, + 0x21, 0x4, 0x3f, 0xfc, 0x0, 0x4, + + /* U+5C96 "岖" */ + 0x20, 0x0, 0x8f, 0xf2, 0x20, 0x2a, 0xa2, 0xaa, + 0x8a, 0xa9, 0x4a, 0xa3, 0x2a, 0x88, 0xaa, 0x32, + 0xa9, 0x4a, 0xa4, 0xaa, 0xa2, 0xfa, 0x80, 0x28, + 0x0, 0x3f, 0xc0, + + /* U+5C97 "岗" */ + 0x2, 0x8, 0x21, 0x82, 0x1f, 0xff, 0x0, 0x0, + 0x0, 0xff, 0xf8, 0x1, 0xb0, 0x58, 0x89, 0x87, + 0x18, 0x71, 0x88, 0x9b, 0x5, 0x80, 0x18, 0x7, + + /* U+5C98 "岘" */ + 0x20, 0x0, 0x43, 0xf8, 0x84, 0x15, 0x4b, 0x2a, + 0x96, 0x55, 0x2c, 0xaa, 0x59, 0x54, 0xb2, 0xa9, + 0x65, 0x52, 0xea, 0xa1, 0xc5, 0x47, 0x95, 0x9b, + 0x20, 0x66, 0x41, 0x8f, 0x0, + + /* U+5C99 "岙" */ + 0x7f, 0xf8, 0x8, 0x0, 0x20, 0x3f, 0xff, 0x4, + 0x80, 0x21, 0x1, 0x2, 0x18, 0x86, 0x82, 0xc, + 0x88, 0x42, 0x21, 0x8, 0x84, 0x3f, 0xf0, 0x0, + 0x40, + + /* U+5C9A "岚" */ + 0x1, 0x0, 0x82, 0x11, 0x4, 0x23, 0xff, 0xc0, + 0x0, 0x0, 0x0, 0x1f, 0xfc, 0x20, 0x8, 0x58, + 0x50, 0x89, 0x21, 0xe, 0x42, 0x18, 0x84, 0x69, + 0x9, 0x8a, 0x64, 0x6, 0xc0, 0x6, + + /* U+5C9B "岛" */ + 0x4, 0x0, 0x10, 0x7, 0xff, 0x10, 0x4, 0x4c, + 0x11, 0xc, 0x44, 0x1e, 0x10, 0x0, 0x7f, 0xfc, + 0x0, 0x10, 0x80, 0x62, 0x21, 0x88, 0x87, 0xfe, + 0x20, 0x0, 0x80, 0x7c, + + /* U+5C9C "岜" */ + 0x2, 0x2, 0x8, 0x48, 0x21, 0x3f, 0xfc, 0x0, + 0x3, 0xff, 0xc8, 0x21, 0x20, 0x84, 0x82, 0x13, + 0xff, 0xc8, 0x0, 0x20, 0x1, 0x80, 0xa, 0x0, + 0x27, 0xff, 0x80, + + /* U+5CA2 "岢" */ + 0x1, 0x0, 0x42, 0x8, 0x84, 0x11, 0xff, 0xe0, + 0x0, 0x0, 0x0, 0x3f, 0xff, 0x80, 0x4, 0x0, + 0x8, 0x7f, 0x10, 0x82, 0x21, 0x4, 0x43, 0xf8, + 0x84, 0x1, 0x8, 0x2, 0x0, 0x3c, + + /* U+5CA3 "岣" */ + 0x20, 0x80, 0x82, 0x2, 0x1f, 0xea, 0x41, 0xaa, + 0x6, 0xa7, 0x9a, 0x92, 0x6a, 0x49, 0xa9, 0x26, + 0xa4, 0x9a, 0x9e, 0x6e, 0x1, 0x48, 0x4, 0x0, + 0x10, 0x7, 0x80, + + /* U+5CA9 "岩" */ + 0x20, 0x4, 0x42, 0x8, 0x84, 0x11, 0xff, 0xe0, + 0x0, 0xf, 0xff, 0xc1, 0x0, 0x2, 0x0, 0x8, + 0x0, 0x3f, 0xf9, 0xc0, 0x12, 0x80, 0x21, 0x0, + 0x43, 0xff, 0x84, 0x1, 0x0, + + /* U+5CAB "岫" */ + 0x20, 0x20, 0x80, 0x82, 0x2, 0x2a, 0xff, 0xaa, + 0x26, 0xa8, 0x9a, 0xa2, 0x6a, 0x89, 0xab, 0xfe, + 0xa8, 0x9a, 0xa2, 0x6a, 0x89, 0xfa, 0x24, 0xf, + 0xf0, 0x20, 0x40, + + /* U+5CAC "岬" */ + 0x20, 0x0, 0x8f, 0xf2, 0x22, 0x6a, 0x89, 0xaa, + 0x26, 0xaf, 0xfa, 0xa2, 0x6a, 0x89, 0xaa, 0x26, + 0xaf, 0xfa, 0xa2, 0x6a, 0x8, 0xf8, 0x20, 0x0, + 0x80, 0x2, 0x0, + + /* U+5CAD "岭" */ + 0x20, 0x20, 0x40, 0xc0, 0x82, 0x45, 0x44, 0x4a, + 0x90, 0xd5, 0x48, 0xea, 0x8, 0x54, 0x0, 0xab, + 0xfd, 0x50, 0x12, 0xa0, 0x45, 0x49, 0x5, 0x8c, + 0x0, 0x4, 0x0, 0x4, 0x0, + + /* U+5CB1 "岱" */ + 0x9, 0x0, 0x12, 0x60, 0x44, 0x61, 0x8, 0x76, + 0xff, 0x1c, 0x10, 0x28, 0x21, 0x10, 0x22, 0x20, + 0x38, 0x2, 0x0, 0x4, 0x1, 0x8, 0x42, 0x10, + 0x84, 0x21, 0xf, 0xfe, 0x0, 0x4, + + /* U+5CB3 "岳" */ + 0x0, 0x18, 0x7f, 0xc0, 0x80, 0x1, 0x0, 0x3, + 0xff, 0xc4, 0x8, 0x8, 0x10, 0x10, 0x20, 0xff, + 0xfe, 0x2, 0x1, 0x4, 0x22, 0x8, 0x44, 0x10, + 0x87, 0xff, 0x0, 0x2, 0x0, + + /* U+5CB5 "岵" */ + 0x20, 0x0, 0x40, 0x40, 0x80, 0x85, 0x4f, 0xfa, + 0x82, 0x15, 0x4, 0x2a, 0x8, 0x54, 0x10, 0xa8, + 0xfd, 0x52, 0xa, 0xa4, 0x15, 0x48, 0x2f, 0x90, + 0x41, 0x1f, 0x80, 0x41, 0x0, + + /* U+5CB7 "岷" */ + 0x20, 0x0, 0x47, 0xf8, 0x88, 0x15, 0x50, 0x2a, + 0xa0, 0x55, 0x7f, 0x2a, 0x88, 0x55, 0x10, 0xab, + 0xff, 0x54, 0x42, 0xa8, 0x85, 0x51, 0xf, 0xa1, + 0x21, 0x72, 0x40, 0x83, 0x0, + + /* U+5CB8 "岸" */ + 0x0, 0x80, 0x41, 0x8, 0x82, 0x11, 0xff, 0xe0, + 0x0, 0x0, 0x0, 0xf, 0xff, 0x90, 0x0, 0x27, + 0xfc, 0x40, 0x80, 0x81, 0x1, 0x2, 0x2, 0xff, + 0xe8, 0x8, 0x10, 0x10, 0x40, 0x20, + + /* U+5CBD "岽" */ + 0x1, 0x0, 0x82, 0x9, 0x4, 0x13, 0xff, 0xe0, + 0x40, 0x0, 0x80, 0x3f, 0xff, 0x8c, 0x0, 0x10, + 0x80, 0x41, 0x1, 0xff, 0xf8, 0x4, 0x1, 0x89, + 0x4, 0x11, 0xb0, 0x20, 0x87, 0x80, + + /* U+5CBF "岿" */ + 0x2, 0x4, 0x10, 0x60, 0x82, 0xff, 0xf0, 0x0, + 0x0, 0x0, 0x40, 0x12, 0x7f, 0x90, 0xc, 0x80, + 0x65, 0xff, 0x20, 0x11, 0x0, 0x90, 0x4, 0x9f, + 0xf8, 0x1, + + /* U+5CC1 "峁" */ + 0x2, 0x4, 0x10, 0x60, 0x83, 0xff, 0xf0, 0x0, + 0xc, 0x3, 0x83, 0xf0, 0x11, 0x88, 0x8c, 0x44, + 0x6e, 0x23, 0x91, 0x11, 0xb, 0x98, 0x41, 0x82, + 0x10, 0x10, + + /* U+5CC4 "峄" */ + 0x20, 0x0, 0x47, 0xf8, 0x84, 0x25, 0x44, 0xca, + 0x8f, 0x15, 0x1e, 0x2b, 0xc3, 0xd4, 0x20, 0xa8, + 0x41, 0x57, 0xfa, 0xa1, 0x7, 0xff, 0xf0, 0x84, + 0x0, 0x8, 0x0, 0x10, 0x0, + + /* U+5CCB "峋" */ + 0x21, 0x0, 0x84, 0x2, 0x3f, 0xea, 0x81, 0xac, + 0x6, 0xaf, 0x9a, 0xa2, 0x6a, 0x89, 0xab, 0xe6, + 0xa8, 0x9a, 0xa2, 0x6e, 0xf9, 0x48, 0x4, 0x0, + 0x10, 0x7, 0x80, + + /* U+5CD2 "峒" */ + 0x20, 0x0, 0x8f, 0xf2, 0x20, 0x6a, 0x81, 0xaa, + 0xf6, 0xa8, 0x1a, 0xaf, 0x6a, 0xa5, 0xaa, 0x96, + 0xaa, 0x5a, 0xaf, 0x6a, 0x81, 0x5a, 0x4, 0x8, + 0x10, 0x23, 0xc0, + + /* U+5CD9 "峙" */ + 0x20, 0x20, 0x40, 0x40, 0x87, 0xf5, 0x41, 0xa, + 0x82, 0x15, 0x4, 0x2a, 0x77, 0x54, 0x8, 0xa8, + 0x11, 0x53, 0xfa, 0xa4, 0x45, 0x44, 0x8f, 0x89, + 0x1, 0x2, 0x0, 0x3c, 0x0, + + /* U+5CE1 "峡" */ + 0x20, 0x40, 0x40, 0x80, 0x8f, 0xf5, 0x42, 0xa, + 0xa4, 0x15, 0x49, 0x2a, 0x52, 0x54, 0x20, 0xab, + 0xff, 0x50, 0xc2, 0xa2, 0x85, 0x44, 0x85, 0x91, + 0x80, 0x41, 0x81, 0x0, 0x0, + + /* U+5CE4 "峤" */ + 0x20, 0x0, 0x47, 0xf8, 0x80, 0x85, 0x41, 0xa, + 0x82, 0x15, 0x7b, 0xea, 0x14, 0x54, 0x44, 0xab, + 0x7, 0x51, 0x12, 0xa2, 0x27, 0xc4, 0x40, 0x98, + 0x80, 0x21, 0x1, 0x82, 0x0, + + /* U+5CE5 "峥" */ + 0x20, 0x80, 0x43, 0xe0, 0x84, 0x45, 0x51, 0xa, + 0xc2, 0x15, 0x3b, 0xaa, 0x11, 0x55, 0xff, 0xa8, + 0x45, 0x50, 0x8a, 0xaf, 0xf5, 0x42, 0x25, 0x84, + 0x0, 0x8, 0x0, 0x70, 0x0, + + /* U+5CE6 "峦" */ + 0x2, 0x0, 0x2, 0x3, 0xff, 0xf8, 0x24, 0x0, + 0x48, 0x4, 0x93, 0x11, 0x23, 0x42, 0x43, 0x0, + 0x0, 0x4, 0x1, 0x8, 0x22, 0x10, 0x44, 0x20, + 0x8f, 0xff, 0x0, 0x2, 0x0, + + /* U+5CE8 "峨" */ + 0x20, 0x10, 0x43, 0xb0, 0x9c, 0x55, 0x48, 0x8a, + 0x91, 0x15, 0xff, 0xea, 0x44, 0x54, 0x88, 0xa9, + 0xd5, 0x5e, 0x32, 0xa4, 0x65, 0xc8, 0x94, 0x97, + 0x20, 0x21, 0x41, 0xc1, 0x0, + + /* U+5CEA "峪" */ + 0x20, 0x0, 0x41, 0x10, 0x84, 0x15, 0x51, 0x1a, + 0x82, 0x15, 0xa, 0x2a, 0x22, 0x54, 0x82, 0xaa, + 0x3, 0x53, 0xfa, 0xa4, 0x15, 0x48, 0x25, 0x90, + 0x40, 0x3f, 0x80, 0x41, 0x0, + + /* U+5CED "峭" */ + 0x20, 0x20, 0x84, 0x92, 0xa, 0xaa, 0xa, 0xa8, + 0x22, 0xa7, 0x7a, 0x90, 0x6a, 0x41, 0xa9, 0xfe, + 0xa4, 0x1a, 0x90, 0x6a, 0x7f, 0xf9, 0x4, 0x24, + 0x10, 0x11, 0xc0, + + /* U+5CF0 "峰" */ + 0x20, 0x80, 0x41, 0xf8, 0x84, 0x25, 0x54, 0xca, + 0x8f, 0x15, 0xe, 0x2a, 0xc3, 0xd4, 0x20, 0xab, + 0xfd, 0x50, 0x82, 0xaf, 0xf5, 0x42, 0x5, 0xbf, + 0xe0, 0x8, 0x0, 0x10, 0x0, + + /* U+5CFB "峻" */ + 0x0, 0x20, 0x40, 0x80, 0x82, 0x25, 0x48, 0x2a, + 0xaf, 0xb5, 0x12, 0x2a, 0x43, 0x55, 0x40, 0xa8, + 0xfd, 0x52, 0x12, 0xaa, 0x25, 0x42, 0x85, 0x87, + 0x0, 0x31, 0x81, 0x80, 0x80, + + /* U+5D02 "崂" */ + 0x20, 0x90, 0x41, 0x20, 0x9f, 0xfd, 0x44, 0x8a, + 0x80, 0x15, 0x7f, 0xeb, 0x10, 0xd4, 0x20, 0xab, + 0xfd, 0x50, 0x8a, 0xa2, 0x15, 0x44, 0x2d, 0x88, + 0x40, 0x21, 0x81, 0x9e, 0x0, + + /* U+5D03 "崃" */ + 0x20, 0x40, 0x40, 0x80, 0x8f, 0xe5, 0x42, 0xa, + 0xa4, 0x95, 0x29, 0x2a, 0x54, 0x54, 0x20, 0xab, + 0xfd, 0x51, 0xc2, 0xa5, 0x45, 0x4a, 0x8b, 0xa4, + 0xc9, 0x88, 0x0, 0x10, 0x0, + + /* U+5D06 "崆" */ + 0x20, 0x20, 0x80, 0x82, 0x3f, 0xea, 0x81, 0xaa, + 0x16, 0xa2, 0x2a, 0xb0, 0x6a, 0x0, 0xa9, 0xfa, + 0xa0, 0x8a, 0x82, 0x2a, 0x8, 0x58, 0x20, 0xf, + 0xf0, + + /* U+5D07 "崇" */ + 0x1, 0x0, 0x42, 0x8, 0x84, 0x11, 0xff, 0xe0, + 0x0, 0x0, 0x20, 0x3f, 0xff, 0xc0, 0x1, 0x9f, + 0xfa, 0x0, 0x0, 0x0, 0x7, 0xff, 0xf0, 0x8, + 0x3, 0x13, 0x18, 0x21, 0x3, 0x80, + + /* U+5D0E "崎" */ + 0x20, 0x20, 0x80, 0x82, 0x3f, 0xea, 0x18, 0xa8, + 0xda, 0xac, 0xa, 0xff, 0xea, 0x2, 0xab, 0xea, + 0xa8, 0xaa, 0xa2, 0xaa, 0x8a, 0x59, 0xc8, 0x0, + 0x20, 0x3, 0x80, + + /* U+5D14 "崔" */ + 0x0, 0x80, 0x41, 0x8, 0x82, 0x11, 0xff, 0xe0, + 0x88, 0x2, 0x8, 0x7, 0xff, 0x98, 0x20, 0x5f, + 0xff, 0x20, 0x80, 0x41, 0x0, 0xff, 0xf1, 0x4, + 0x3, 0xff, 0xc4, 0x0, 0x0, + + /* U+5D16 "崖" */ + 0x20, 0x84, 0x41, 0x8, 0xff, 0xf0, 0x0, 0x3, + 0xff, 0xe4, 0x0, 0xb, 0xff, 0x10, 0x40, 0x20, + 0x80, 0x7f, 0xfc, 0x82, 0x1, 0x7f, 0xe4, 0x8, + 0xb, 0xff, 0xc0, 0x0, 0x0, + + /* U+5D1B "崛" */ + 0x20, 0x0, 0x47, 0xf8, 0x88, 0x15, 0x5f, 0xea, + 0xa0, 0x15, 0x44, 0x2a, 0xa9, 0x55, 0x52, 0xaa, + 0xfd, 0x54, 0x42, 0xb0, 0x85, 0x69, 0x15, 0xd2, + 0x20, 0xbf, 0xc1, 0x0, 0x80, + + /* U+5D1E "崞" */ + 0x20, 0x20, 0x40, 0x40, 0x87, 0x7d, 0x40, 0xa, + 0x9f, 0xd5, 0x20, 0xaa, 0x7f, 0x54, 0x0, 0xa9, + 0xfd, 0x50, 0x12, 0xa0, 0xc5, 0x5f, 0xf5, 0x82, + 0x0, 0x4, 0x0, 0x70, 0x0, + + /* U+5D24 "崤" */ + 0x20, 0x4, 0x43, 0x30, 0x81, 0x85, 0x4c, 0xea, + 0xa4, 0x15, 0x7f, 0xea, 0x20, 0x54, 0x80, 0xa9, + 0xfd, 0x56, 0xa, 0xa7, 0xf5, 0x48, 0x2b, 0x9f, + 0xc9, 0x20, 0x80, 0x4f, 0x0, + + /* U+5D26 "崦" */ + 0x20, 0x40, 0x40, 0x80, 0x8f, 0xf5, 0x45, 0xa, + 0x91, 0x15, 0x49, 0xab, 0xfe, 0x55, 0x24, 0xaa, + 0x49, 0x57, 0xf2, 0xa9, 0x25, 0x5f, 0xcf, 0x84, + 0x21, 0x8, 0x80, 0x1f, 0x0, + + /* U+5D27 "崧" */ + 0x1, 0x0, 0x82, 0x9, 0x4, 0x13, 0xff, 0xe0, + 0x0, 0x2, 0x12, 0x3f, 0x22, 0x8, 0x86, 0x32, + 0x46, 0xb0, 0x81, 0x52, 0x44, 0x84, 0x81, 0x10, + 0x82, 0x7f, 0x84, 0x1, 0x0, + + /* U+5D29 "崩" */ + 0x0, 0x80, 0x41, 0x4, 0x82, 0x9, 0xff, 0xf0, + 0x0, 0x0, 0x0, 0xf, 0x9f, 0x91, 0x21, 0x3e, + 0x7e, 0x44, 0x84, 0x89, 0x9, 0xf3, 0xf2, 0x24, + 0x24, 0x48, 0x50, 0xa0, 0xa7, 0x4f, + + /* U+5D2D "崭" */ + 0x41, 0x4, 0x82, 0x9, 0xff, 0xf0, 0x80, 0x2, + 0x1, 0xdf, 0xdc, 0x10, 0x20, 0x48, 0x40, 0xfe, + 0xfe, 0x21, 0x10, 0x42, 0x20, 0xe4, 0x4f, 0x10, + 0x82, 0x21, 0x4, 0x82, 0x0, + + /* U+5D2E "崮" */ + 0x82, 0x18, 0x21, 0xff, 0xf0, 0x0, 0xff, 0xf8, + 0x1, 0x82, 0x1b, 0xff, 0x82, 0x19, 0xfd, 0x90, + 0x59, 0xfd, 0x80, 0x1f, 0xff, 0x80, 0x10, + + /* U+5D34 "崴" */ + 0x0, 0x80, 0x20, 0x82, 0x20, 0x82, 0x3f, 0xfe, + 0x0, 0x8, 0x0, 0x24, 0x3f, 0xfe, 0x20, 0x20, + 0x2f, 0xa2, 0x22, 0x24, 0x2f, 0x94, 0x24, 0x98, + 0x24, 0x92, 0x43, 0x92, 0x4c, 0x2a, 0x80, 0x46, + + /* U+5D3D "崽" */ + 0x41, 0x4, 0x82, 0x9, 0xff, 0xf0, 0x0, 0x3, + 0xff, 0x88, 0x21, 0x10, 0x42, 0x1f, 0xfc, 0x41, + 0x8, 0x7f, 0xf0, 0x0, 0x2, 0x88, 0x5, 0xa, + 0x52, 0x4, 0x43, 0xf0, 0x0, + + /* U+5D3E "崾" */ + 0x20, 0x0, 0x8f, 0xf2, 0x9, 0x2a, 0x24, 0xab, + 0xfe, 0xaa, 0x5a, 0xa9, 0x6a, 0xff, 0xa8, 0x42, + 0xa2, 0xa, 0xb7, 0xea, 0x44, 0xb8, 0xe1, 0x21, + 0xc0, 0x38, 0xc0, + + /* U+5D47 "嵇" */ + 0x0, 0x81, 0xf1, 0x20, 0x82, 0x21, 0x1f, 0xaf, + 0x92, 0x4, 0x24, 0x58, 0x89, 0x3a, 0x1e, 0xa8, + 0x41, 0x50, 0x82, 0x91, 0x11, 0x22, 0x22, 0x44, + 0x44, 0xff, 0x88, 0x1, 0x0, + + /* U+5D4A "嵊" */ + 0x23, 0xfc, 0x81, 0xa, 0x84, 0x2a, 0xff, 0xa9, + 0x52, 0xbd, 0x7a, 0x95, 0x2a, 0xd5, 0xa9, 0xea, + 0xa3, 0x8a, 0x95, 0x36, 0x92, 0x4, 0x44, 0x1, + 0x0, + + /* U+5D4B "嵋" */ + 0x23, 0xfc, 0x91, 0x1a, 0xc4, 0x6a, 0xff, 0xac, + 0x2, 0xb7, 0xfa, 0xd0, 0x6b, 0x7f, 0xad, 0x6, + 0xb4, 0x1b, 0xdf, 0xd3, 0x41, 0x5, 0xfc, 0x24, + 0x10, + + /* U+5D4C "嵌" */ + 0x1, 0x0, 0x41, 0x4, 0x41, 0x4, 0x7f, 0xfc, + 0x0, 0x0, 0x0, 0x0, 0x42, 0x40, 0xff, 0x7e, + 0x42, 0x92, 0x42, 0x94, 0x7e, 0x10, 0x42, 0x28, + 0x42, 0x28, 0x42, 0x44, 0x7e, 0xc6, 0x41, 0x2, + + /* U+5D58 "嵘" */ + 0x20, 0x90, 0x41, 0x20, 0x8f, 0xf5, 0x44, 0x8a, + 0x80, 0x15, 0x7f, 0xaa, 0x81, 0x54, 0x20, 0xab, + 0xfd, 0x50, 0xc2, 0xa3, 0x45, 0x4a, 0x45, 0xa4, + 0x40, 0x8, 0x0, 0x10, 0x0, + + /* U+5D5B "嵛" */ + 0x41, 0x4, 0x82, 0x9, 0xff, 0xf0, 0x14, 0x0, + 0x44, 0x3, 0x6, 0x19, 0xf3, 0x40, 0x1, 0x7e, + 0x8, 0x84, 0x91, 0xf9, 0x22, 0x12, 0x47, 0xe4, + 0x88, 0x41, 0x13, 0x9e, 0x0, + + /* U+5D5D "嵝" */ + 0x0, 0x20, 0x40, 0x48, 0x84, 0xb5, 0x41, 0xa, + 0xbf, 0xf5, 0xe, 0x2a, 0x6b, 0x55, 0x11, 0xa8, + 0x41, 0x57, 0xfe, 0xa4, 0x25, 0xcc, 0x8c, 0x87, + 0x0, 0x39, 0x81, 0x80, 0x0, + + /* U+5D69 "嵩" */ + 0x2, 0x1, 0x8, 0x27, 0xff, 0x80, 0x80, 0xff, + 0xfc, 0x0, 0x3, 0xff, 0x8, 0x4, 0x1f, 0xe1, + 0xff, 0xe4, 0x0, 0x93, 0xf2, 0x4f, 0xc9, 0x0, + 0x24, 0x3, 0x80, + + /* U+5D6B "嵫" */ + 0x21, 0xc, 0x41, 0x10, 0x80, 0x45, 0x5f, 0xfa, + 0x90, 0x95, 0x22, 0x2a, 0x95, 0x55, 0x52, 0xa9, + 0xb9, 0x51, 0x12, 0xa4, 0x45, 0x5c, 0xaf, 0xa6, + 0x41, 0x7f, 0xc0, 0x0, 0x80, + + /* U+5D6C "嵬" */ + 0x41, 0x8, 0x82, 0x11, 0xff, 0xe0, 0x10, 0x3, + 0xff, 0x88, 0x21, 0x10, 0x42, 0x1f, 0xfc, 0x41, + 0x8, 0x7f, 0xf0, 0x8, 0x80, 0x2a, 0x80, 0x97, + 0xc6, 0x20, 0xb0, 0x3f, 0x0, + + /* U+5D6F "嵯" */ + 0x21, 0x8, 0x41, 0x20, 0x8f, 0xfd, 0x42, 0xa, + 0x84, 0x15, 0x7f, 0xaa, 0x20, 0x55, 0xff, 0xa8, + 0x81, 0x52, 0x2, 0xa7, 0xf7, 0xd1, 0x0, 0x62, + 0x0, 0xff, 0xc0, + + /* U+5D74 "嵴" */ + 0x20, 0x20, 0x46, 0x4c, 0x80, 0xa5, 0x4f, 0x4a, + 0xe5, 0x75, 0x11, 0x2a, 0xff, 0x57, 0x3, 0xaa, + 0x5, 0x57, 0xfa, 0xa8, 0x15, 0xdf, 0xe4, 0xa0, + 0x40, 0x40, 0x80, 0x8f, 0x0, + + /* U+5D82 "嶂" */ + 0x0, 0x0, 0x40, 0x80, 0x8f, 0xf1, 0x8, 0x4a, + 0x91, 0x15, 0xff, 0xea, 0x0, 0x55, 0xfe, 0xaa, + 0x5, 0x57, 0xfa, 0xa8, 0x15, 0x5f, 0xeb, 0x84, + 0x9, 0xff, 0xc0, 0x10, 0x0, 0x20, + + /* U+5D99 "嶙" */ + 0x20, 0x40, 0x42, 0x90, 0x83, 0x45, 0x42, 0xa, + 0xbf, 0xd5, 0x3a, 0x2a, 0x52, 0x57, 0x23, 0xa9, + 0x9, 0x55, 0xba, 0xba, 0xa7, 0xc6, 0x40, 0x93, + 0xe0, 0x61, 0x1, 0x2, 0x0, + + /* U+5D9D "嶝" */ + 0x20, 0x0, 0x47, 0x50, 0x82, 0xc5, 0x55, 0x2a, + 0x91, 0x95, 0x5f, 0x2b, 0x1, 0x55, 0xfc, 0xaa, + 0x9, 0x54, 0x12, 0xaf, 0xe5, 0x48, 0x8d, 0x8a, + 0x0, 0xff, 0x80, + + /* U+5DB7 "嶷" */ + 0x1, 0x4, 0x41, 0x4, 0x3f, 0xfc, 0x0, 0x0, + 0x4e, 0xfe, 0x72, 0x44, 0x7e, 0x38, 0x21, 0xfe, + 0x7c, 0x12, 0x90, 0x90, 0xfe, 0x9e, 0x10, 0x90, + 0x18, 0xd0, 0x25, 0x70, 0x41, 0x3e, 0x0, 0x0, + + /* U+5DC5 "巅" */ + 0x0, 0x0, 0x82, 0x8, 0xff, 0xf0, 0x40, 0xf, + 0xff, 0xe1, 0x2, 0x1d, 0xbf, 0x3f, 0x42, 0x42, + 0x94, 0xfd, 0x29, 0xfa, 0x52, 0x14, 0xaf, 0xf2, + 0x84, 0x84, 0x90, 0xb0, 0x80, + + /* U+5DCD "巍" */ + 0x1, 0x0, 0x82, 0x11, 0xff, 0xe0, 0x0, 0x7, + 0xdf, 0xdf, 0xa4, 0x8c, 0x7f, 0x2c, 0x92, 0x95, + 0x24, 0x43, 0xfb, 0xf1, 0x82, 0x23, 0xc2, 0x8b, + 0xa7, 0xb4, 0xb0, 0xcf, 0x0, + + /* U+5DDB "巛" */ + 0x10, 0x0, 0x42, 0x21, 0x10, 0x88, 0x44, 0x22, + 0x11, 0x8, 0x84, 0x42, 0x11, 0x8, 0x44, 0x20, + 0x88, 0x42, 0x21, 0x8, 0x42, 0x11, 0x8, 0x42, + 0x30, + + /* U+5DDD "川" */ + 0x10, 0x84, 0x42, 0x11, 0x8, 0x44, 0x21, 0x10, + 0x84, 0x42, 0x11, 0x8, 0x44, 0x21, 0x10, 0x84, + 0x42, 0x12, 0x8, 0x48, 0x21, 0x60, 0x85, 0x0, + 0x10, + + /* U+5DDE "州" */ + 0x10, 0x84, 0x42, 0x11, 0x8, 0x44, 0x21, 0x54, + 0xa5, 0x52, 0x99, 0x29, 0x64, 0xa5, 0x10, 0x84, + 0x42, 0x12, 0x8, 0x48, 0x21, 0x40, 0x87, 0x2, + 0x10, + + /* U+5DE1 "巡" */ + 0x41, 0x24, 0x44, 0x48, 0x49, 0x20, 0x22, 0x40, + 0x49, 0x1d, 0x12, 0xa, 0x44, 0x12, 0x48, 0x24, + 0x88, 0x44, 0x90, 0x89, 0x11, 0x19, 0x22, 0x0, + 0xa, 0x0, 0x23, 0xff, 0x80, + + /* U+5DE2 "巢" */ + 0x11, 0x19, 0x88, 0x82, 0x31, 0x4, 0x2, 0x7f, + 0xf9, 0x8, 0x27, 0xff, 0x90, 0x82, 0x7f, 0xf8, + 0x8, 0xf, 0xff, 0xc1, 0xa0, 0x1a, 0x61, 0x88, + 0x78, 0x20, 0x0, + + /* U+5DE5 "工" */ + 0x7f, 0xfc, 0x2, 0x0, 0x4, 0x0, 0x8, 0x0, + 0x10, 0x0, 0x20, 0x0, 0x40, 0x0, 0x80, 0x1, + 0x0, 0x2, 0x0, 0x4, 0x0, 0x8, 0xf, 0xff, + 0xe0, + + /* U+5DE6 "左" */ + 0x0, 0x0, 0x2, 0x0, 0x2, 0x0, 0x7f, 0xff, + 0x4, 0x0, 0x4, 0x0, 0x4, 0x0, 0x8, 0x0, + 0xf, 0xfe, 0x18, 0x40, 0x10, 0x40, 0x20, 0x40, + 0x60, 0x40, 0x40, 0x40, 0x1f, 0xff, + + /* U+5DE7 "巧" */ + 0x3, 0xff, 0xf1, 0x0, 0x82, 0x1, 0x8, 0x2, + 0x10, 0x4, 0x3f, 0x88, 0x1, 0x10, 0x2, 0x20, + 0x4, 0x40, 0x8, 0xf0, 0x17, 0x0, 0x20, 0x0, + 0xc0, 0x1f, 0x0, + + /* U+5DE8 "巨" */ + 0xff, 0xfe, 0x0, 0x8, 0x0, 0x20, 0x0, 0xff, + 0xf2, 0x0, 0x48, 0x1, 0x20, 0x4, 0xff, 0xf2, + 0x0, 0x8, 0x0, 0x20, 0x0, 0xff, 0xfc, + + /* U+5DE9 "巩" */ + 0x3, 0xf8, 0xfa, 0x8, 0x22, 0x8, 0x22, 0x8, + 0x22, 0x8, 0x22, 0x48, 0x22, 0x48, 0x22, 0x28, + 0x22, 0x8, 0x22, 0x8, 0x3a, 0x9, 0xc4, 0x9, + 0x4, 0xa, 0xc, 0xe, + + /* U+5DEB "巫" */ + 0x7f, 0xfc, 0x2, 0x0, 0x4, 0x0, 0x88, 0x81, + 0x11, 0x6, 0x22, 0xc, 0x4a, 0x34, 0x92, 0x45, + 0x47, 0x2, 0x0, 0x4, 0x0, 0x8, 0xf, 0xff, + 0xe0, + + /* U+5DEE "差" */ + 0x8, 0x30, 0x8, 0x41, 0xff, 0xf0, 0x10, 0x7, + 0xff, 0xc0, 0x40, 0x1, 0x0, 0x7f, 0xff, 0x8, + 0x0, 0x30, 0x0, 0xff, 0xe2, 0x4, 0x8, 0x8, + 0x7, 0xff, 0xc0, + + /* U+5DEF "巯" */ + 0x0, 0x20, 0x7c, 0x20, 0xb, 0xfe, 0x8, 0x60, + 0x10, 0x48, 0x2d, 0x84, 0x43, 0xfe, 0x0, 0x2, + 0x7c, 0x0, 0x11, 0x28, 0x11, 0x28, 0x11, 0x28, + 0x15, 0x29, 0x6a, 0x29, 0x6, 0x2e, + + /* U+5DF1 "己" */ + 0xff, 0xf0, 0x0, 0x40, 0x1, 0x0, 0x4, 0x0, + 0x13, 0xff, 0xc8, 0x0, 0x20, 0x0, 0x80, 0x2, + 0x0, 0x8, 0x0, 0x60, 0x2, 0xc0, 0x9, 0xff, + 0xc0, + + /* U+5DF2 "已" */ + 0xff, 0xf0, 0x0, 0x40, 0x1, 0x20, 0x4, 0x80, + 0x13, 0xff, 0xc8, 0x0, 0x20, 0x0, 0x80, 0x2, + 0x0, 0x18, 0x0, 0x60, 0x2, 0x7f, 0xf0, + + /* U+5DF3 "巳" */ + 0xff, 0xf2, 0x0, 0x48, 0x1, 0x20, 0x4, 0x80, + 0x13, 0xff, 0xc8, 0x0, 0x20, 0x0, 0x80, 0x2, + 0x0, 0x18, 0x0, 0x60, 0x2, 0x7f, 0xf0, + + /* U+5DF4 "巴" */ + 0xff, 0xf4, 0x10, 0xa0, 0x85, 0x4, 0x28, 0x21, + 0x41, 0xb, 0xff, 0xd0, 0x0, 0x80, 0x4, 0x0, + 0x60, 0x3, 0x0, 0x17, 0xff, 0x0, + + /* U+5DF7 "巷" */ + 0x8, 0x20, 0x10, 0x40, 0x20, 0x83, 0xff, 0xe0, + 0x82, 0x1f, 0xff, 0xc1, 0x10, 0x4, 0x10, 0x3f, + 0xf8, 0xa0, 0x2e, 0x40, 0x40, 0xff, 0x81, 0x0, + 0x42, 0x0, 0x84, 0x2, 0xf, 0xfc, + + /* U+5DFD "巽" */ + 0x7c, 0x7c, 0x88, 0x89, 0xf1, 0xf2, 0xa, 0x17, + 0xe7, 0xe0, 0x0, 0x2, 0x8, 0x3f, 0xff, 0x8, + 0x20, 0x10, 0x43, 0xff, 0xf8, 0x0, 0x0, 0xc3, + 0x1e, 0x1, 0xc0, + + /* U+5DFE "巾" */ + 0x2, 0x0, 0x10, 0x0, 0x81, 0xff, 0xf8, 0x20, + 0xc1, 0x6, 0x8, 0x30, 0x41, 0x82, 0xc, 0x10, + 0x60, 0x83, 0x4, 0x18, 0x27, 0x81, 0x0, 0x8, + 0x0, + + /* U+5E01 "币" */ + 0xff, 0xfc, 0x4, 0x0, 0x10, 0x0, 0x40, 0x7f, + 0xf9, 0x4, 0x24, 0x10, 0x90, 0x42, 0x41, 0x9, + 0x4, 0x24, 0x10, 0x90, 0x4e, 0x1, 0x0, 0x4, + 0x0, + + /* U+5E02 "市" */ + 0x1, 0x0, 0x2, 0x3, 0xff, 0xf8, 0x8, 0x0, + 0x10, 0xf, 0xff, 0x90, 0x41, 0x20, 0x82, 0x41, + 0x4, 0x82, 0x9, 0x4, 0x12, 0x8, 0x24, 0x13, + 0x80, 0x20, 0x0, 0x40, 0x0, + + /* U+5E03 "布" */ + 0x2, 0x0, 0x4, 0x1, 0xff, 0xf8, 0x20, 0x0, + 0x88, 0x1, 0x10, 0x7, 0xff, 0x18, 0x42, 0x50, + 0x84, 0x21, 0x8, 0x42, 0x10, 0x84, 0x21, 0xb, + 0x80, 0x10, 0x0, 0x20, 0x0, + + /* U+5E05 "帅" */ + 0x10, 0x42, 0x41, 0x9, 0x3f, 0xe4, 0x91, 0x92, + 0x46, 0x49, 0x19, 0x24, 0x64, 0x91, 0x92, 0x46, + 0x49, 0x12, 0x24, 0x48, 0x91, 0x22, 0x5d, 0x1, + 0x8, 0x4, 0x0, + + /* U+5E06 "帆" */ + 0x20, 0x0, 0x43, 0xe0, 0x84, 0x47, 0xe8, 0x8a, + 0x51, 0x14, 0xa2, 0x29, 0x64, 0x52, 0xa8, 0xa5, + 0x11, 0x4a, 0x22, 0x94, 0x45, 0x68, 0x92, 0x21, + 0x24, 0x42, 0x49, 0x7, 0x0, + + /* U+5E08 "师" */ + 0x10, 0x2, 0x5f, 0xf9, 0x4, 0x24, 0x10, 0x93, + 0xfa, 0x51, 0x29, 0x44, 0xa5, 0x12, 0x94, 0x4a, + 0x51, 0x22, 0x44, 0x89, 0x12, 0x20, 0x59, 0x1, + 0x8, 0x4, 0x0, + + /* U+5E0C "希" */ + 0x70, 0x30, 0x1f, 0x80, 0x3b, 0x83, 0x90, 0xe0, + 0x40, 0x1f, 0xff, 0xc2, 0x0, 0x8, 0x80, 0x3f, + 0xf8, 0xc2, 0x12, 0x84, 0x21, 0x8, 0x42, 0x10, + 0x84, 0x2f, 0x0, 0x40, 0x0, + + /* U+5E0F "帏" */ + 0x20, 0x40, 0x81, 0xf, 0xbf, 0xea, 0x10, 0xa8, + 0x42, 0xaf, 0xea, 0x84, 0x2a, 0x10, 0xab, 0xfe, + 0xa1, 0x1b, 0x84, 0x48, 0x11, 0x20, 0x58, 0x81, + 0x2, 0x4, 0x0, + + /* U+5E10 "帐" */ + 0x21, 0x0, 0x84, 0x2f, 0xd1, 0x29, 0x48, 0xa5, + 0x42, 0x94, 0xa, 0x7f, 0xe9, 0x50, 0xa5, 0x22, + 0x94, 0x8a, 0x51, 0x2b, 0x44, 0x21, 0x8, 0x87, + 0x12, 0x10, 0x0, + + /* U+5E11 "帑" */ + 0x10, 0x0, 0x41, 0xfb, 0xfa, 0x12, 0x14, 0x44, + 0x45, 0x7, 0x4, 0x5, 0xb6, 0x70, 0x3, 0x1, + 0x0, 0x7f, 0xf1, 0x4, 0x22, 0x8, 0x44, 0x10, + 0x88, 0x27, 0x0, 0x40, 0x0, + + /* U+5E14 "帔" */ + 0x20, 0x20, 0x40, 0x40, 0x87, 0xff, 0xc9, 0x1a, + 0x92, 0x35, 0x24, 0x2a, 0x7f, 0xd4, 0xc2, 0xa9, + 0x85, 0x52, 0x92, 0xa5, 0x65, 0xd1, 0x82, 0x23, + 0x4, 0x89, 0x8b, 0x60, 0x80, + + /* U+5E15 "帕" */ + 0x20, 0x20, 0x81, 0xf, 0xdb, 0xe9, 0x41, 0xa5, + 0x6, 0x94, 0x1a, 0x50, 0x69, 0x7f, 0xa5, 0x6, + 0x94, 0x1a, 0x50, 0x6b, 0x41, 0x21, 0x4, 0x87, + 0xf2, 0x10, 0x40, + + /* U+5E16 "帖" */ + 0x20, 0x40, 0x81, 0x2, 0x4, 0x3f, 0x1f, 0xa4, + 0x42, 0x91, 0xa, 0x44, 0x29, 0x7f, 0xa5, 0x6, + 0x94, 0x1a, 0xd0, 0x68, 0x41, 0x21, 0x4, 0x87, + 0xf2, 0x10, 0x40, + + /* U+5E18 "帘" */ + 0x0, 0x80, 0x0, 0x80, 0x7f, 0xfe, 0x40, 0x2, + 0x46, 0x22, 0x8, 0x18, 0x70, 0x86, 0x0, 0x80, + 0x3f, 0xfc, 0x20, 0x84, 0x20, 0x84, 0x20, 0x84, + 0x20, 0x84, 0x20, 0x9c, 0x0, 0x80, + + /* U+5E19 "帙" */ + 0x20, 0x20, 0x42, 0x43, 0xf4, 0x85, 0x2f, 0xfa, + 0x52, 0x14, 0xa4, 0x29, 0x8, 0x52, 0xff, 0xa4, + 0x21, 0x48, 0xc2, 0x91, 0x45, 0x62, 0xc2, 0x8, + 0x84, 0x20, 0xc8, 0x80, 0x80, + + /* U+5E1A "帚" */ + 0x7f, 0xf8, 0x0, 0x10, 0x0, 0x21, 0xff, 0xc0, + 0x0, 0x8f, 0xff, 0x0, 0x0, 0x7f, 0xff, 0x81, + 0x3, 0x7f, 0xf4, 0x84, 0x21, 0x8, 0x42, 0x10, + 0x84, 0x27, 0x0, 0x40, 0x0, + + /* U+5E1B "帛" */ + 0x4, 0x0, 0x20, 0x1f, 0xfc, 0x80, 0x24, 0x1, + 0x3f, 0xf9, 0x0, 0x4f, 0xfe, 0x0, 0x0, 0x10, + 0x3f, 0xff, 0x4, 0x18, 0x20, 0xc1, 0x6, 0x8, + 0xc0, 0x40, + + /* U+5E1C "帜" */ + 0x20, 0x0, 0x43, 0xfb, 0xf4, 0x15, 0x28, 0x2a, + 0x50, 0x54, 0xa0, 0xa9, 0x41, 0x52, 0x7e, 0xa4, + 0x1, 0x48, 0x2, 0x91, 0x21, 0x64, 0x42, 0x10, + 0x44, 0x60, 0x48, 0x80, 0x80, + + /* U+5E1D "帝" */ + 0x1, 0x0, 0x2, 0x3, 0xff, 0xf8, 0x80, 0x0, + 0x82, 0x1, 0x4, 0x3f, 0xff, 0xc0, 0x1, 0x81, + 0x2, 0x7f, 0xf0, 0x84, 0x21, 0x8, 0x42, 0x10, + 0x84, 0x21, 0x8, 0x4e, 0x0, 0x80, + + /* U+5E26 "带" */ + 0x11, 0x10, 0x22, 0x23, 0xff, 0xf8, 0x88, 0x80, + 0x0, 0xf, 0xff, 0xe0, 0x0, 0x80, 0x81, 0x3f, + 0xf8, 0x42, 0x10, 0x84, 0x21, 0x8, 0x42, 0x10, + 0x80, 0x27, 0x0, 0x40, 0x0, + + /* U+5E27 "帧" */ + 0x20, 0x40, 0x81, 0x2, 0x7, 0xfe, 0x10, 0xab, + 0xfe, 0xa8, 0x1a, 0xa4, 0x6a, 0x91, 0xaa, 0x46, + 0xa9, 0x1a, 0xa4, 0x6e, 0xb1, 0x20, 0xb0, 0x8c, + 0x22, 0x40, 0x40, + + /* U+5E2D "席" */ + 0x0, 0x80, 0x0, 0x40, 0x3f, 0xff, 0x20, 0x0, + 0x22, 0x8, 0x2f, 0xfe, 0x22, 0x8, 0x22, 0x8, + 0x23, 0xf8, 0x20, 0x40, 0x2f, 0xfc, 0x28, 0x44, + 0x28, 0x44, 0x48, 0x44, 0x48, 0x5c, 0x80, 0x40, + + /* U+5E2E "帮" */ + 0x10, 0x3, 0xfd, 0xe1, 0x4, 0x84, 0x12, 0x7e, + 0x48, 0x41, 0x1f, 0xf4, 0x48, 0x17, 0x41, 0x2, + 0xff, 0xe2, 0x10, 0x88, 0x42, 0x21, 0x8, 0x85, + 0xe0, 0x10, 0x0, + + /* U+5E31 "帱" */ + 0x20, 0x40, 0x81, 0x2, 0x3f, 0xfe, 0x10, 0xab, + 0xfe, 0xa2, 0xa, 0xbf, 0xea, 0x20, 0xa9, 0xa, + 0xa7, 0xfa, 0xa0, 0xaf, 0xa2, 0x24, 0x48, 0x80, + 0x22, 0x7, 0x0, + + /* U+5E37 "帷" */ + 0x20, 0x90, 0x41, 0x23, 0xe7, 0xfd, 0x48, 0x8a, + 0xb1, 0x15, 0xa2, 0x2b, 0x7f, 0xd4, 0x88, 0xa9, + 0x11, 0x53, 0xfe, 0xa4, 0x41, 0x8, 0x82, 0x1f, + 0xe4, 0x20, 0x8, 0x40, 0x0, + + /* U+5E38 "常" */ + 0x1, 0x0, 0x84, 0x41, 0x12, 0x3f, 0xff, 0x80, + 0x6, 0x0, 0x13, 0xff, 0x8, 0x4, 0x3f, 0xf0, + 0x4, 0x7, 0xff, 0x90, 0x42, 0x41, 0x9, 0x4, + 0x24, 0x13, 0x80, 0x40, + + /* U+5E3B "帻" */ + 0x20, 0x20, 0x47, 0xff, 0xe0, 0x85, 0x4f, 0xea, + 0x82, 0x15, 0x7f, 0xea, 0x0, 0x54, 0xfe, 0xa9, + 0x5, 0x52, 0x4a, 0xa4, 0x95, 0xc9, 0x22, 0x5, + 0x4, 0x31, 0x89, 0x80, 0x0, + + /* U+5E3C "帼" */ + 0x20, 0x0, 0x8f, 0xf2, 0x40, 0x7f, 0x7d, 0xac, + 0x46, 0xb1, 0x1a, 0xdf, 0x6b, 0x11, 0xac, 0x66, + 0xb1, 0x5a, 0xc4, 0x6f, 0x7d, 0x24, 0x4, 0x8f, + 0xf2, 0x40, 0x40, + + /* U+5E3D "帽" */ + 0x20, 0x0, 0x8f, 0xf2, 0x20, 0x7e, 0xbf, 0xaa, + 0x6, 0xab, 0xfa, 0x80, 0x2a, 0x7f, 0xaa, 0x6, + 0xa7, 0xfa, 0xa0, 0x48, 0x7f, 0x22, 0x4, 0x87, + 0xf2, 0x20, 0x40, + + /* U+5E42 "幂" */ + 0x7f, 0xfe, 0x80, 0x5, 0x7f, 0xe9, 0x0, 0x41, + 0xff, 0x84, 0x1, 0x7, 0xfe, 0x1, 0x0, 0x7f, + 0xfe, 0x10, 0x20, 0xff, 0xf2, 0x88, 0x51, 0x10, + 0x82, 0x27, 0x0, 0x40, 0x0, + + /* U+5E44 "幄" */ + 0x20, 0x0, 0x47, 0xf8, 0x88, 0x17, 0xdf, 0xea, + 0xa0, 0x15, 0x5f, 0xaa, 0x88, 0x55, 0x24, 0xaa, + 0xfd, 0x54, 0x22, 0xa8, 0x45, 0xd7, 0xf2, 0x41, + 0x4, 0xbf, 0xc8, 0x0, 0x0, + + /* U+5E45 "幅" */ + 0x20, 0x0, 0x47, 0xff, 0xe7, 0xf5, 0x48, 0x2a, + 0x90, 0x55, 0x3f, 0xaa, 0x0, 0x55, 0xff, 0xaa, + 0x23, 0x54, 0x46, 0xa8, 0x8d, 0xdf, 0xf2, 0x22, + 0x24, 0x7f, 0xc8, 0x80, 0x80, + + /* U+5E4C "幌" */ + 0x23, 0xfc, 0x44, 0x8, 0x88, 0x17, 0xdf, 0xea, + 0xa0, 0x55, 0x7f, 0xaa, 0x89, 0x54, 0x94, 0xa8, + 0x21, 0x5f, 0xfe, 0xa2, 0x45, 0xc4, 0x82, 0x11, + 0x24, 0x22, 0x49, 0x83, 0x0, + + /* U+5E54 "幔" */ + 0x23, 0xfc, 0x44, 0xb, 0xef, 0xf5, 0x50, 0x2a, + 0xbf, 0xd5, 0x0, 0x2a, 0xff, 0xd6, 0x49, 0xab, + 0xff, 0x50, 0x2, 0xaf, 0xf5, 0xc8, 0x42, 0xf, + 0x4, 0x1e, 0x9, 0xc3, 0x80, + + /* U+5E55 "幕" */ + 0x8, 0x43, 0xff, 0xf0, 0x84, 0xd, 0xec, 0x3f, + 0xf0, 0x80, 0x43, 0xff, 0x1, 0x0, 0xff, 0xfc, + 0x41, 0x7, 0xff, 0xa8, 0x85, 0x22, 0x10, 0x89, + 0xc0, 0x20, 0x0, + + /* U+5E5B "幛" */ + 0x20, 0x20, 0x47, 0xfb, 0xe4, 0x25, 0x44, 0x4a, + 0xff, 0xf5, 0x0, 0x2a, 0xff, 0x55, 0x2, 0xab, + 0xfd, 0x54, 0xa, 0xaf, 0xf5, 0xc1, 0x2, 0x7f, + 0xe4, 0x4, 0x8, 0x8, 0x0, + + /* U+5E5E "幞" */ + 0x20, 0x90, 0x45, 0x2b, 0xe2, 0x45, 0x5f, 0xfa, + 0x90, 0x95, 0x13, 0x2a, 0xff, 0xd4, 0x20, 0xab, + 0xfd, 0x50, 0x82, 0xa1, 0x5, 0xff, 0xf2, 0x6, + 0x4, 0x33, 0x9, 0x81, 0x80, + + /* U+5E61 "幡" */ + 0x20, 0x0, 0x47, 0xe8, 0x81, 0x17, 0xca, 0xca, + 0xbf, 0xf5, 0x1c, 0x2a, 0x54, 0x55, 0x26, 0xaf, + 0xfd, 0x54, 0x8a, 0xa9, 0x15, 0xdf, 0xe2, 0x24, + 0x44, 0x7f, 0x88, 0x81, 0x0, + + /* U+5E62 "幢" */ + 0x20, 0x40, 0x47, 0xfb, 0xe4, 0x25, 0x48, 0x8a, + 0xbf, 0xf5, 0x0, 0x2a, 0xff, 0x55, 0x22, 0xab, + 0xfd, 0x54, 0x8a, 0xaf, 0xf5, 0xc2, 0x2, 0x3f, + 0xc4, 0x8, 0x9, 0xff, 0x80, + + /* U+5E72 "干" */ + 0x7f, 0xfc, 0x2, 0x0, 0x4, 0x0, 0x8, 0x0, + 0x10, 0x0, 0x20, 0x3f, 0xff, 0x80, 0x80, 0x1, + 0x0, 0x2, 0x0, 0x4, 0x0, 0x8, 0x0, 0x10, + 0x0, 0x20, 0x0, + + /* U+5E73 "平" */ + 0x7f, 0xfc, 0x2, 0x0, 0x84, 0x21, 0x88, 0xc1, + 0x11, 0x1, 0x24, 0x0, 0x40, 0x7f, 0xff, 0x1, + 0x0, 0x2, 0x0, 0x4, 0x0, 0x8, 0x0, 0x10, + 0x0, 0x20, 0x0, + + /* U+5E74 "年" */ + 0x10, 0x0, 0x20, 0x0, 0xff, 0xf1, 0x4, 0x4, + 0x8, 0x7, 0xff, 0x88, 0x20, 0x10, 0x40, 0x20, + 0x80, 0x41, 0x3, 0xff, 0xf8, 0x4, 0x0, 0x8, + 0x0, 0x10, 0x0, 0x20, 0x0, + + /* U+5E76 "并" */ + 0x0, 0x10, 0x10, 0x40, 0x11, 0x3, 0xff, 0xe0, + 0x82, 0x1, 0x4, 0x2, 0x8, 0x4, 0x10, 0xff, + 0xfe, 0x10, 0x40, 0x20, 0x80, 0x41, 0x1, 0x2, + 0x4, 0x4, 0x10, 0x8, 0x0, + + /* U+5E78 "幸" */ + 0x1, 0x0, 0x2, 0x1, 0xff, 0xf0, 0x8, 0x0, + 0x10, 0x1f, 0xff, 0xc6, 0x8, 0x2, 0x20, 0x7f, + 0xfc, 0x2, 0x0, 0x4, 0x7, 0xff, 0xf0, 0x10, + 0x0, 0x20, 0x0, 0x40, 0x0, + + /* U+5E7A "幺" */ + 0x2, 0x0, 0x10, 0x0, 0x80, 0x6, 0x4, 0x30, + 0x20, 0x81, 0x7, 0xf8, 0x0, 0x40, 0x2, 0x0, + 0x10, 0x80, 0x83, 0x4, 0x4, 0x20, 0x9, 0xff, + 0xe0, 0x0, 0x40, + + /* U+5E7B "幻" */ + 0x8, 0x0, 0x47, 0xf1, 0x0, 0x48, 0x81, 0x42, + 0x5, 0x10, 0x1f, 0xc0, 0x42, 0x1, 0x10, 0x4, + 0xd0, 0x12, 0x20, 0x50, 0x81, 0xff, 0x4, 0x4, + 0x30, 0xf, 0x80, + + /* U+5E7C "幼" */ + 0x10, 0x40, 0x81, 0x2, 0x4, 0x11, 0x7f, 0x84, + 0x47, 0xe1, 0x11, 0x4, 0x44, 0x11, 0x28, 0x45, + 0x22, 0x14, 0x48, 0x7f, 0x21, 0x3, 0x4, 0x4, + 0x10, 0x23, 0x80, + + /* U+5E7D "幽" */ + 0x2, 0x0, 0x92, 0x24, 0x93, 0x45, 0x1a, 0xaa, + 0xd5, 0x97, 0xcf, 0x32, 0x49, 0xa2, 0xad, 0x55, + 0x6e, 0xbb, 0xc, 0x38, 0x20, 0xff, 0xfc, 0x0, + 0x20, + + /* U+5E7F "广" */ + 0x0, 0x0, 0x1, 0x0, 0x2, 0x1, 0xff, 0xf2, + 0x0, 0x4, 0x0, 0x8, 0x0, 0x10, 0x0, 0x20, + 0x0, 0x40, 0x0, 0x80, 0x1, 0x0, 0x4, 0x0, + 0x8, 0x0, 0x20, 0x0, 0x0, + + /* U+5E80 "庀" */ + 0x0, 0x0, 0x0, 0x40, 0x3f, 0xff, 0x20, 0x0, + 0x20, 0x0, 0x22, 0x0, 0x22, 0x0, 0x22, 0x1c, + 0x22, 0x60, 0x23, 0x80, 0x22, 0x0, 0x22, 0x2, + 0x42, 0x2, 0x42, 0x2, 0x41, 0xfc, + + /* U+5E84 "庄" */ + 0x0, 0x80, 0x0, 0x40, 0x3f, 0xff, 0x20, 0x0, + 0x20, 0x40, 0x20, 0x40, 0x20, 0x40, 0x27, 0xfe, + 0x20, 0x40, 0x20, 0x40, 0x20, 0x40, 0x20, 0x40, + 0x40, 0x40, 0x4f, 0xff, 0x0, 0x0, + + /* U+5E86 "庆" */ + 0x0, 0x80, 0x0, 0x40, 0x3f, 0xff, 0x20, 0x0, + 0x20, 0x40, 0x20, 0x40, 0x20, 0x40, 0x2f, 0xfe, + 0x20, 0xc0, 0x20, 0xc0, 0x20, 0xa0, 0x21, 0x10, + 0x42, 0x18, 0x44, 0xc, 0x98, 0x2, + + /* U+5E87 "庇" */ + 0x0, 0x80, 0x0, 0x40, 0x3f, 0xff, 0x20, 0x0, + 0x20, 0x20, 0x24, 0x20, 0x24, 0x22, 0x27, 0xac, + 0x24, 0x30, 0x24, 0x20, 0x24, 0x20, 0x64, 0x21, + 0x44, 0xa2, 0x47, 0x22, 0x84, 0x3e, + + /* U+5E8A "床" */ + 0x0, 0x80, 0x0, 0x40, 0x3f, 0xfe, 0x20, 0x0, + 0x20, 0x40, 0x20, 0x40, 0x2f, 0xfe, 0x20, 0xe0, + 0x21, 0x60, 0x21, 0x50, 0x22, 0x48, 0x24, 0x44, + 0x48, 0x43, 0x40, 0x40, 0x80, 0x40, + + /* U+5E8B "庋" */ + 0x0, 0x80, 0x0, 0x80, 0x3f, 0xff, 0x20, 0x0, + 0x20, 0x80, 0x2f, 0xfc, 0x20, 0x80, 0x20, 0x80, + 0x2f, 0xf8, 0x24, 0x8, 0x22, 0x10, 0x21, 0x20, + 0x40, 0xc0, 0x43, 0x38, 0x9c, 0x6, + + /* U+5E8F "序" */ + 0x0, 0x80, 0x1, 0x0, 0xff, 0xf9, 0x0, 0x2, + 0xff, 0xc4, 0x1, 0x8, 0x6c, 0x10, 0x20, 0x2f, + 0xfe, 0x40, 0x84, 0x81, 0x19, 0x2, 0x24, 0x4, + 0x8, 0x8, 0x20, 0xf0, 0x0, + + /* U+5E90 "庐" */ + 0x0, 0x80, 0x0, 0x80, 0x3f, 0xff, 0x20, 0x40, + 0x20, 0x40, 0x27, 0xfc, 0x24, 0x4, 0x24, 0x4, + 0x24, 0x4, 0x27, 0xfc, 0x24, 0x4, 0x24, 0x0, + 0x44, 0x0, 0x48, 0x0, 0xd8, 0x0, + + /* U+5E91 "庑" */ + 0x0, 0x40, 0x0, 0x80, 0xff, 0xf9, 0x0, 0x2, + 0x7f, 0xe4, 0x8, 0x8, 0x10, 0x10, 0x20, 0x2f, + 0xfe, 0x41, 0x40, 0x82, 0x81, 0x5, 0x4, 0x12, + 0x28, 0xc4, 0x53, 0x7, 0x80, 0x0, + + /* U+5E93 "库" */ + 0x0, 0x80, 0x1, 0x0, 0xff, 0xf9, 0x0, 0x2, + 0x10, 0x4, 0x20, 0xb, 0xff, 0x11, 0x0, 0x24, + 0x40, 0x5f, 0xf8, 0x81, 0x1, 0x2, 0x3, 0xff, + 0xe8, 0x8, 0x10, 0x10, 0x40, 0x20, + + /* U+5E94 "应" */ + 0x0, 0x80, 0x1, 0x0, 0xff, 0xf9, 0x0, 0x2, + 0x0, 0x5, 0x10, 0x8a, 0x21, 0x12, 0x44, 0x24, + 0x48, 0x48, 0x90, 0x89, 0x41, 0x0, 0x84, 0x1, + 0xb, 0xff, 0xe0, 0x0, 0x0, + + /* U+5E95 "底" */ + 0x0, 0x80, 0x0, 0x40, 0x3f, 0xff, 0x20, 0x0, + 0x20, 0x0, 0x27, 0xfe, 0x24, 0x20, 0x24, 0x20, + 0x24, 0x20, 0x27, 0xfe, 0x24, 0x20, 0x24, 0x20, + 0x24, 0x11, 0x45, 0x91, 0x46, 0xa, 0x80, 0x66, + + /* U+5E96 "庖" */ + 0x0, 0x80, 0x0, 0x80, 0x3f, 0xff, 0x22, 0x0, + 0x22, 0x0, 0x23, 0xfc, 0x24, 0x4, 0x28, 0x4, + 0x2f, 0xe4, 0x24, 0x24, 0x24, 0x24, 0x27, 0xc4, + 0x24, 0x39, 0x44, 0x1, 0x44, 0x2, 0x47, 0xfe, + 0x0, 0x0, + + /* U+5E97 "店" */ + 0x0, 0x80, 0x0, 0x80, 0x3f, 0xff, 0x20, 0x80, + 0x20, 0x80, 0x20, 0xfe, 0x20, 0x80, 0x20, 0x80, + 0x27, 0xfc, 0x24, 0x4, 0x24, 0x4, 0x24, 0x4, + 0x44, 0x4, 0x47, 0xfc, 0x84, 0x4, + + /* U+5E99 "庙" */ + 0x0, 0x80, 0x0, 0x80, 0xff, 0xf9, 0x0, 0x2, + 0x4, 0x4, 0x8, 0xb, 0xff, 0x14, 0x22, 0x28, + 0x44, 0x5f, 0xf8, 0xa1, 0x11, 0x42, 0x24, 0x84, + 0x49, 0xff, 0xa2, 0x1, 0x0, + + /* U+5E9A "庚" */ + 0x0, 0x80, 0x1, 0x0, 0xff, 0xf9, 0x4, 0x2, + 0xff, 0xc4, 0x10, 0x88, 0x21, 0x1f, 0xff, 0x20, + 0x84, 0x5f, 0xf8, 0x86, 0x1, 0xa, 0x4, 0x23, + 0x9, 0x81, 0xc4, 0x0, 0x0, + + /* U+5E9C "府" */ + 0x0, 0x80, 0x0, 0x80, 0x3f, 0xff, 0x20, 0x0, + 0x22, 0x8, 0x24, 0x8, 0x25, 0xfe, 0x2c, 0x8, + 0x34, 0x88, 0x34, 0xc8, 0x24, 0x48, 0x24, 0x8, + 0x44, 0x8, 0x44, 0x8, 0x84, 0x70, + + /* U+5E9E "庞" */ + 0x0, 0x80, 0x1, 0x80, 0xff, 0xf9, 0x0, 0x2, + 0x23, 0x4, 0x41, 0xb, 0xff, 0x91, 0x20, 0x22, + 0x44, 0x44, 0x90, 0x89, 0x41, 0x23, 0x14, 0x4c, + 0x29, 0x68, 0x56, 0x1f, 0x80, + + /* U+5E9F "废" */ + 0x0, 0x80, 0x0, 0x40, 0x3f, 0xff, 0x20, 0x0, + 0x24, 0x88, 0x28, 0x80, 0x2f, 0xfe, 0x21, 0x0, + 0x21, 0xfc, 0x23, 0x4, 0x22, 0x88, 0x44, 0x50, + 0x48, 0x20, 0x51, 0xd8, 0x82, 0x6, + + /* U+5EA0 "庠" */ + 0x0, 0x80, 0x0, 0x80, 0xff, 0xf9, 0x10, 0x42, + 0x11, 0x5, 0xff, 0xc8, 0x10, 0x10, 0x20, 0x27, + 0xfc, 0x40, 0x80, 0x81, 0x1, 0xff, 0xf4, 0x4, + 0x8, 0x8, 0x20, 0x10, 0x0, + + /* U+5EA5 "庥" */ + 0x0, 0x80, 0x0, 0x80, 0x3f, 0xff, 0x20, 0x0, + 0x22, 0x20, 0x22, 0x20, 0x25, 0xfe, 0x2c, 0x20, + 0x2c, 0x70, 0x24, 0x68, 0x24, 0xa8, 0x25, 0x24, + 0x47, 0x23, 0x44, 0x20, 0x4, 0x20, + + /* U+5EA6 "度" */ + 0x0, 0x80, 0x0, 0x80, 0x3f, 0xff, 0x20, 0x0, + 0x22, 0x10, 0x2f, 0xfe, 0x22, 0x10, 0x22, 0x10, + 0x23, 0xf0, 0x20, 0x0, 0x2f, 0xf8, 0x24, 0x18, + 0x22, 0x30, 0x41, 0xc0, 0x43, 0x70, 0x9c, 0x6, + + /* U+5EA7 "座" */ + 0x0, 0x80, 0x1, 0x0, 0xff, 0xf9, 0x0, 0x2, + 0x48, 0x84, 0x93, 0xa, 0xa6, 0x14, 0xd2, 0x20, + 0x80, 0x41, 0x0, 0xbf, 0xf1, 0x4, 0x4, 0x8, + 0xb, 0xff, 0xc0, 0x0, 0x0, + + /* U+5EAD "庭" */ + 0x0, 0x80, 0x0, 0x80, 0x3f, 0xff, 0x20, 0x0, + 0x2e, 0xfe, 0x22, 0x10, 0x24, 0x10, 0x2e, 0xfe, + 0x22, 0x10, 0x2a, 0x10, 0x2a, 0x10, 0x2c, 0xfe, + 0x46, 0x0, 0x49, 0xff, 0x0, 0x0, + + /* U+5EB3 "庳" */ + 0x0, 0x80, 0x1, 0x0, 0xff, 0xf9, 0x4, 0x2, + 0x8, 0x4, 0xff, 0x89, 0x11, 0x13, 0xfe, 0x24, + 0x44, 0x48, 0x88, 0x9f, 0xf1, 0x8, 0x2, 0xff, + 0xe8, 0x4, 0x10, 0x8, 0x20, 0x10, + + /* U+5EB5 "庵" */ + 0x0, 0x80, 0x0, 0x80, 0x3f, 0xff, 0x20, 0x80, + 0x21, 0x0, 0x2f, 0xfe, 0x22, 0x20, 0x22, 0x98, + 0x2f, 0xfe, 0x24, 0x88, 0x27, 0xf8, 0x24, 0x88, + 0x27, 0xf9, 0x44, 0x82, 0x40, 0x82, 0xc0, 0x7e, + + /* U+5EB6 "庶" */ + 0x0, 0x40, 0x0, 0x40, 0x3f, 0xfe, 0x20, 0x0, + 0x22, 0x8, 0x22, 0x8, 0x2f, 0xfe, 0x22, 0x8, + 0x22, 0x8, 0x22, 0x8, 0x23, 0xf8, 0x20, 0x0, + 0x24, 0x4, 0x49, 0x12, 0x48, 0x92, 0xd0, 0x0, + + /* U+5EB7 "康" */ + 0x0, 0x80, 0x0, 0x80, 0x3f, 0xfe, 0x20, 0x0, + 0x20, 0x80, 0x2f, 0xfc, 0x20, 0x84, 0x3f, 0xff, + 0x20, 0x84, 0x2f, 0xfc, 0x20, 0x80, 0x2c, 0xcc, + 0x21, 0xb0, 0x6e, 0x90, 0x50, 0x8e, 0x83, 0x80, + + /* U+5EB8 "庸" */ + 0x0, 0x0, 0x1, 0x1, 0xff, 0xfa, 0xff, 0xc4, + 0x8, 0x8f, 0xff, 0xd0, 0x22, 0x2f, 0xfc, 0x40, + 0x80, 0xbf, 0xf1, 0x7f, 0xe2, 0x84, 0x49, 0xff, + 0x92, 0x11, 0x24, 0x2e, 0x0, + + /* U+5EB9 "庹" */ + 0x0, 0x80, 0x0, 0x40, 0x3f, 0xff, 0x20, 0x0, + 0x22, 0x8, 0x3f, 0xfe, 0x22, 0x8, 0x23, 0xf8, + 0x20, 0x0, 0x27, 0xfc, 0x24, 0x4, 0x27, 0xfc, + 0x64, 0x40, 0x4c, 0x60, 0x48, 0x18, 0x10, 0x6, + + /* U+5EBE "庾" */ + 0x0, 0x80, 0x1, 0x0, 0xff, 0xf9, 0x2, 0x2, + 0x77, 0xc4, 0x88, 0x89, 0x11, 0x13, 0xbe, 0x24, + 0x44, 0x4f, 0xf8, 0x83, 0x1, 0x5, 0x4, 0x11, + 0x9, 0xc1, 0xc0, 0x0, 0x0, + + /* U+5EC9 "廉" */ + 0x0, 0x0, 0x0, 0x80, 0x3f, 0xff, 0x22, 0x10, + 0x23, 0x10, 0x2f, 0xfe, 0x21, 0x20, 0x2f, 0xfc, + 0x21, 0x24, 0x3f, 0xff, 0x21, 0x24, 0x2f, 0xfc, + 0x23, 0x30, 0x45, 0x28, 0x49, 0x26, 0x11, 0x20, + + /* U+5ECA "廊" */ + 0x0, 0x80, 0x0, 0x80, 0x3f, 0xff, 0x22, 0x0, + 0x2f, 0x9f, 0x28, 0x92, 0x28, 0x96, 0x2f, 0x94, + 0x28, 0x94, 0x2f, 0x94, 0x29, 0x12, 0x29, 0x12, + 0x6a, 0x9e, 0x4c, 0x10, 0x0, 0x10, + + /* U+5ED1 "廑" */ + 0x0, 0x40, 0x3f, 0xff, 0x22, 0x8, 0x3f, 0xff, + 0x22, 0x8, 0x23, 0xf8, 0x20, 0x40, 0x2f, 0xfe, + 0x28, 0x42, 0x27, 0xfc, 0x2f, 0xfe, 0x20, 0x40, + 0x47, 0xf8, 0x5f, 0xff, 0x0, 0x0, + + /* U+5ED2 "廒" */ + 0x1, 0x0, 0x2, 0x1, 0xff, 0xfa, 0x0, 0x4, + 0x42, 0xb, 0xf4, 0x11, 0x1f, 0x2f, 0xa2, 0x44, + 0xc8, 0xbf, 0x51, 0x20, 0xa2, 0x79, 0x84, 0x91, + 0xa, 0x25, 0x24, 0x5b, 0x53, 0xe3, 0x0, 0x0, + + /* U+5ED3 "廓" */ + 0x0, 0x80, 0x1, 0x1, 0xff, 0xfa, 0x10, 0x4, + 0x23, 0xeb, 0xb4, 0x93, 0xe9, 0x28, 0x52, 0x4f, + 0xa8, 0x80, 0x49, 0x7e, 0x8a, 0x9, 0x14, 0xfe, + 0x32, 0x45, 0xe0, 0x88, 0x47, 0x10, + + /* U+5ED6 "廖" */ + 0x1, 0x0, 0xff, 0xfd, 0x0, 0x2, 0xff, 0xe4, + 0x14, 0x49, 0xa6, 0x91, 0xdd, 0x2c, 0x42, 0x43, + 0x60, 0xf9, 0xbd, 0x1c, 0xc2, 0x46, 0x48, 0xf3, + 0x10, 0x38, 0x27, 0x80, 0x0, + + /* U+5EDB "廛" */ + 0x0, 0x0, 0x0, 0x80, 0x3f, 0xff, 0x20, 0x0, + 0x2f, 0xfe, 0x28, 0x42, 0x2f, 0xfe, 0x2f, 0xfe, + 0x20, 0x40, 0x27, 0xfc, 0x3f, 0xff, 0x22, 0x0, + 0x2c, 0x4e, 0x53, 0xf9, 0x40, 0x40, 0x5f, 0xff, + 0x0, 0x0, + + /* U+5EE8 "廨" */ + 0x0, 0x40, 0x0, 0x40, 0x3f, 0xff, 0x20, 0x0, + 0x24, 0x0, 0x2f, 0xbe, 0x29, 0x12, 0x2e, 0xac, + 0x2a, 0xa0, 0x2a, 0x88, 0x2f, 0xbe, 0x2a, 0xa8, + 0x2f, 0xbf, 0x52, 0x88, 0x50, 0x88, 0x93, 0x88, + + /* U+5EEA "廪" */ + 0x0, 0x80, 0x3f, 0xff, 0x20, 0x40, 0x2f, 0xff, + 0x20, 0x0, 0x2f, 0xfe, 0x29, 0xfa, 0x29, 0xfa, + 0x2f, 0xfe, 0x20, 0x0, 0x27, 0xfc, 0x3f, 0xff, + 0x40, 0x40, 0x4f, 0x4e, 0x43, 0xc0, + + /* U+5EF4 "廴" */ + 0xf0, 0x0, 0x20, 0x0, 0x80, 0x2, 0x0, 0x8, + 0x0, 0x1e, 0x0, 0x4, 0x0, 0x48, 0x0, 0x90, + 0x0, 0xc0, 0x0, 0x80, 0x2, 0x80, 0x8, 0xff, + 0xc0, 0x0, 0x0, + + /* U+5EF6 "延" */ + 0x0, 0x5, 0xf7, 0xf0, 0x20, 0x81, 0x81, 0x6, + 0x12, 0x18, 0x27, 0xbe, 0x48, 0x4, 0x90, 0x89, + 0x20, 0xa2, 0x41, 0x47, 0xf9, 0x0, 0x7, 0x80, + 0x18, 0xff, 0xe0, 0x0, 0x0, + + /* U+5EF7 "廷" */ + 0xfb, 0xfc, 0x10, 0x40, 0x40, 0x81, 0x1, 0x4, + 0x2, 0xf, 0x3f, 0x82, 0x8, 0x64, 0x10, 0x48, + 0x20, 0x67, 0xfc, 0xc0, 0x1, 0x60, 0xc, 0x7f, + 0xe0, 0x0, 0x0, + + /* U+5EFA "建" */ + 0x0, 0x80, 0x1, 0x3, 0xdf, 0xe0, 0x84, 0x42, + 0x8, 0x89, 0xff, 0xa0, 0x22, 0x7b, 0xfc, 0x10, + 0x81, 0x2f, 0xf2, 0x42, 0x3, 0x4, 0x6, 0xff, + 0xcc, 0x10, 0x27, 0xff, 0x0, 0x0, + + /* U+5EFE "廾" */ + 0x8, 0x20, 0x10, 0x40, 0x20, 0x80, 0x41, 0x0, + 0x82, 0x1f, 0xff, 0xc2, 0x8, 0x4, 0x10, 0x8, + 0x20, 0x10, 0x40, 0x40, 0x80, 0x81, 0x2, 0x2, + 0x8, 0x4, 0x0, + + /* U+5EFF "廿" */ + 0x10, 0x10, 0x20, 0x20, 0x40, 0x47, 0xff, 0xf1, + 0x1, 0x2, 0x2, 0x4, 0x4, 0x8, 0x8, 0x10, + 0x10, 0x20, 0x20, 0x40, 0x40, 0x80, 0x81, 0xff, + 0x2, 0x2, 0x0, + + /* U+5F00 "开" */ + 0x7f, 0xfc, 0x10, 0x40, 0x20, 0x80, 0x41, 0x0, + 0x82, 0x1, 0x4, 0x3f, 0xff, 0x84, 0x10, 0x8, + 0x20, 0x10, 0x40, 0x40, 0x80, 0x81, 0x2, 0x2, + 0x18, 0x4, 0x0, + + /* U+5F01 "弁" */ + 0x0, 0x0, 0x4, 0x0, 0x11, 0x80, 0x41, 0x83, + 0x1, 0x8f, 0xff, 0x0, 0x1, 0x0, 0x0, 0x8, + 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x0, 0x82, + 0x2, 0x4, 0xc, 0x8, 0x30, 0x10, + + /* U+5F02 "异" */ + 0x3f, 0xf8, 0x40, 0x10, 0x80, 0x21, 0x0, 0x43, + 0xff, 0x84, 0x0, 0x48, 0x0, 0x9f, 0xfe, 0x0, + 0x0, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x0, 0x82, + 0x6, 0x4, 0x10, 0x8, 0x0, + + /* U+5F03 "弃" */ + 0x1, 0x0, 0x2, 0x3, 0xff, 0xf8, 0x20, 0x0, + 0x86, 0x2, 0x6, 0xf, 0xfe, 0x0, 0x6, 0x8, + 0x21, 0xff, 0xfc, 0x20, 0x80, 0x41, 0x1, 0x2, + 0x4, 0x4, 0x10, 0x8, 0x0, + + /* U+5F04 "弄" */ + 0x7f, 0xfc, 0x2, 0x0, 0x4, 0x3, 0xff, 0xe0, + 0x10, 0x0, 0x20, 0x3f, 0xff, 0x80, 0x0, 0x8, + 0x21, 0xff, 0xfc, 0x20, 0x80, 0x81, 0x3, 0x2, + 0x8, 0x4, 0x0, + + /* U+5F08 "弈" */ + 0x2, 0x0, 0x2, 0x3, 0xff, 0xf8, 0x22, 0x2, + 0x44, 0x8, 0x89, 0x22, 0x11, 0x8c, 0xe0, 0x30, + 0x0, 0x10, 0x43, 0xff, 0xf8, 0x81, 0x1, 0x2, + 0x6, 0x4, 0x10, 0x8, 0x0, + + /* U+5F0A "弊" */ + 0x8, 0x10, 0x52, 0x40, 0x28, 0xfb, 0xfb, 0x24, + 0x96, 0x4b, 0x23, 0x1a, 0xc6, 0x24, 0x9a, 0x4b, + 0x42, 0x0, 0x0, 0x10, 0x87, 0xff, 0xf0, 0x82, + 0x3, 0x4, 0x18, 0x8, 0x0, + + /* U+5F0B "弋" */ + 0x4, 0x0, 0x8, 0x80, 0x10, 0x80, 0x20, 0x80, + 0x41, 0xe3, 0xfc, 0x18, 0x80, 0x1, 0x0, 0x2, + 0x0, 0x2, 0x0, 0x4, 0x0, 0x4, 0x10, 0xc, + 0x20, 0xc, 0x40, 0x7, 0x0, + + /* U+5F0F "式" */ + 0x0, 0x50, 0x0, 0x90, 0x1, 0x7, 0xff, 0xf0, + 0x4, 0x0, 0x8, 0x0, 0x10, 0x3f, 0xa0, 0x8, + 0x40, 0x10, 0x80, 0x20, 0x80, 0x41, 0x10, 0xfa, + 0x3e, 0x2, 0x40, 0x3, 0x0, + + /* U+5F11 "弑" */ + 0x84, 0x10, 0x90, 0x28, 0xc0, 0x41, 0x9f, 0xf4, + 0xc1, 0x12, 0x2, 0x4, 0x4, 0x7e, 0xe8, 0x10, + 0x90, 0xb1, 0x22, 0x52, 0x44, 0xa4, 0x89, 0xe, + 0xa2, 0x61, 0x58, 0x1, 0x80, + + /* U+5F13 "弓" */ + 0xff, 0xf0, 0x0, 0x80, 0x4, 0xff, 0xe8, 0x0, + 0x40, 0x2, 0x0, 0x1f, 0xff, 0x0, 0x10, 0x0, + 0x80, 0x4, 0x0, 0x20, 0x1, 0x3, 0xf0, + + /* U+5F15 "引" */ + 0x0, 0xf, 0xf0, 0x40, 0x82, 0x4, 0x1f, 0xe0, + 0xc0, 0x6, 0x0, 0x30, 0x1, 0xfe, 0x8, 0x10, + 0x40, 0x82, 0x4, 0x10, 0x20, 0x81, 0x5, 0xf0, + 0x20, + + /* U+5F17 "弗" */ + 0x4, 0x40, 0xff, 0xf8, 0x11, 0x10, 0x22, 0x27, + 0xff, 0xc8, 0x88, 0x11, 0x10, 0x22, 0x20, 0x7f, + 0xfe, 0x10, 0x84, 0x21, 0x10, 0x82, 0x63, 0x4, + 0x8, 0x8, 0x0, + + /* U+5F18 "弘" */ + 0x0, 0x1, 0xf8, 0x40, 0x10, 0x80, 0x22, 0xf, + 0xc4, 0x10, 0x8, 0x20, 0x24, 0x40, 0x48, 0xf8, + 0x90, 0x12, 0x10, 0x24, 0x20, 0x50, 0x40, 0xbf, + 0xc1, 0x0, 0x9c, 0x0, 0x0, + + /* U+5F1B "弛" */ + 0x0, 0x43, 0xc1, 0x1, 0x24, 0x4, 0x93, 0x12, + 0x77, 0x9f, 0x18, 0x24, 0x60, 0x91, 0xf2, 0x7c, + 0x49, 0x1, 0x24, 0x4, 0x81, 0x12, 0x4, 0xc8, + 0x1e, 0x1f, 0x80, + + /* U+5F1F "弟" */ + 0x8, 0x20, 0x8, 0x81, 0xff, 0xf0, 0x8, 0x20, + 0x10, 0x47, 0xff, 0x90, 0x40, 0x20, 0x80, 0x7f, + 0xfe, 0xa, 0x4, 0x14, 0x8, 0x48, 0x11, 0x10, + 0x6c, 0x27, 0xa0, 0x40, 0x0, + + /* U+5F20 "张" */ + 0x2, 0x3, 0xc8, 0x21, 0x21, 0x4, 0x98, 0xf2, + 0x82, 0x8, 0x8, 0x7f, 0xe0, 0x90, 0xf2, 0x40, + 0x49, 0x1, 0x22, 0x4, 0x8c, 0x12, 0x18, 0x4e, + 0x3e, 0x20, 0x0, + + /* U+5F25 "弥" */ + 0x1, 0x1, 0xe2, 0x0, 0x4f, 0xf0, 0x90, 0x2f, + 0x44, 0x90, 0x88, 0x20, 0x10, 0x41, 0x24, 0xf2, + 0x48, 0x24, 0x90, 0x51, 0x10, 0xa2, 0x21, 0x44, + 0x2, 0x8, 0x38, 0x70, 0x0, + + /* U+5F26 "弦" */ + 0x0, 0x43, 0xc1, 0x1, 0x7f, 0xc4, 0x20, 0xf1, + 0x2, 0x4, 0x48, 0x22, 0x21, 0xf8, 0xf0, 0x40, + 0x42, 0x1, 0x11, 0x4, 0x44, 0x12, 0x8, 0xdf, + 0xee, 0x0, 0x40, + + /* U+5F27 "弧" */ + 0x0, 0x1, 0xe7, 0xf0, 0x49, 0x40, 0x92, 0x8f, + 0x25, 0x10, 0x4a, 0x20, 0x94, 0x41, 0x28, 0xf2, + 0x50, 0x24, 0x90, 0x49, 0xa0, 0x95, 0x41, 0x4a, + 0x42, 0x9a, 0xb9, 0x0, 0x0, + + /* U+5F29 "弩" */ + 0x10, 0x0, 0x21, 0xfb, 0xfa, 0x11, 0x12, 0x44, + 0x46, 0x87, 0x6, 0xd, 0xb3, 0x60, 0x1, 0x3f, + 0xf8, 0x0, 0x10, 0xff, 0xe1, 0x0, 0x3, 0xff, + 0xc0, 0x0, 0x80, 0x1, 0x0, 0x7c, + + /* U+5F2A "弪" */ + 0xfb, 0xfc, 0x10, 0x10, 0x20, 0x47, 0xc1, 0x88, + 0xc, 0x90, 0xe0, 0xe0, 0x0, 0x79, 0xfe, 0x10, + 0x40, 0x20, 0x80, 0x41, 0x0, 0x82, 0x1, 0x7f, + 0xfc, 0x0, 0x0, + + /* U+5F2D "弭" */ + 0xf7, 0xfc, 0x24, 0x10, 0x48, 0x27, 0x9f, 0xc8, + 0x20, 0x90, 0x41, 0x20, 0x82, 0x79, 0xfc, 0x12, + 0x8, 0x24, 0x10, 0x49, 0xf8, 0xbc, 0x41, 0x0, + 0x9c, 0x1, 0x0, + + /* U+5F2F "弯" */ + 0x1, 0x0, 0x2, 0x3, 0xff, 0xf8, 0x24, 0x2, + 0x49, 0x88, 0x90, 0xa0, 0x0, 0xbf, 0xfc, 0x0, + 0x8, 0x7f, 0xf1, 0x0, 0x3, 0xff, 0xc0, 0x0, + 0x80, 0x1, 0x0, 0x2, 0x0, 0xf8, + + /* U+5F31 "弱" */ + 0xfc, 0xfc, 0x10, 0x10, 0x40, 0x5f, 0x3f, 0x80, + 0x82, 0x2, 0xf, 0xcf, 0xc1, 0x1, 0x64, 0xc4, + 0x10, 0x93, 0x47, 0x71, 0x21, 0x4, 0x5, 0xe0, + 0xe0, + + /* U+5F39 "弹" */ + 0x0, 0x1, 0xe2, 0x30, 0x40, 0x40, 0x9f, 0xe1, + 0x24, 0x5e, 0x48, 0xa0, 0xff, 0x41, 0x22, 0x82, + 0x45, 0xe7, 0xf8, 0x41, 0x0, 0x82, 0x1, 0x7f, + 0xe2, 0x8, 0x4, 0x10, 0x70, 0x20, + + /* U+5F3A "强" */ + 0xf9, 0xfc, 0xa, 0x4, 0xa, 0x4, 0xf9, 0xf8, + 0x80, 0x40, 0x83, 0xfe, 0x82, 0x42, 0xf2, 0x42, + 0x12, 0x42, 0x13, 0xfe, 0x10, 0x40, 0x10, 0x44, + 0x17, 0xfe, 0xe0, 0x2, + + /* U+5F3C "弼" */ + 0x77, 0xde, 0x22, 0x4, 0x44, 0xb, 0xb6, 0x78, + 0x45, 0x10, 0x8a, 0x21, 0x14, 0x7b, 0xef, 0x14, + 0x42, 0x28, 0x84, 0x51, 0x8, 0xbe, 0x11, 0x44, + 0x3c, 0x8b, 0x80, + + /* U+5F40 "彀" */ + 0x10, 0x0, 0xff, 0x7c, 0x10, 0x44, 0x7e, 0x44, + 0x0, 0x84, 0xff, 0x82, 0xfd, 0x0, 0x0, 0xfe, + 0x7e, 0x44, 0x2, 0x44, 0xfc, 0x28, 0x7e, 0x10, + 0x2, 0x28, 0x2, 0xc6, 0x1f, 0x80, + + /* U+5F50 "彐" */ + 0xff, 0xf8, 0x0, 0x40, 0x2, 0x0, 0x10, 0x0, + 0x80, 0x5, 0xff, 0xe0, 0x1, 0x0, 0x8, 0x0, + 0x40, 0x2, 0x0, 0x1f, 0xff, 0x80, 0x4, + + /* U+5F52 "归" */ + 0x10, 0x0, 0x80, 0x24, 0xff, 0x20, 0x19, 0x0, + 0xc8, 0x6, 0x40, 0x32, 0xff, 0x90, 0xc, 0x80, + 0x64, 0x2, 0x40, 0x12, 0x7f, 0xa0, 0x5, 0x0, + 0x0, + + /* U+5F53 "当" */ + 0x2, 0x2, 0x10, 0x10, 0x84, 0x44, 0x41, 0x24, + 0x1, 0x3, 0xff, 0xe0, 0x1, 0x0, 0x8, 0x0, + 0x7f, 0xfe, 0x0, 0x10, 0x0, 0x80, 0x7, 0xff, + 0xe0, 0x1, + + /* U+5F55 "录" */ + 0x7f, 0xf0, 0x0, 0x20, 0xff, 0xc0, 0x0, 0x80, + 0x1, 0x1f, 0xff, 0xc0, 0x40, 0x30, 0x86, 0x11, + 0x90, 0xe, 0xc0, 0x64, 0x83, 0x8, 0xc8, 0x10, + 0x61, 0xc0, 0x0, + + /* U+5F56 "彖" */ + 0x10, 0x0, 0x3f, 0xe0, 0x80, 0x41, 0xff, 0x80, + 0x1, 0x0, 0x2, 0x3f, 0xff, 0x83, 0x4, 0x3b, + 0x31, 0x8f, 0xc0, 0x66, 0x87, 0x14, 0x80, 0xc8, + 0x9e, 0x10, 0xc3, 0xc0, 0x0, + + /* U+5F57 "彗" */ + 0x10, 0x20, 0x20, 0x43, 0xfb, 0xf0, 0x81, 0x1, + 0x2, 0x1f, 0xdf, 0x84, 0x8, 0x7f, 0x7f, 0x10, + 0x20, 0xff, 0xf0, 0x0, 0x20, 0x0, 0x47, 0xff, + 0x80, 0x1, 0x1f, 0xfe, 0x0, 0x4, + + /* U+5F58 "彘" */ + 0x10, 0x0, 0x3f, 0xf0, 0x40, 0x20, 0xff, 0xc0, + 0x1, 0x1f, 0xff, 0xc0, 0x80, 0x21, 0xe9, 0x7d, + 0x14, 0x82, 0x31, 0x3f, 0x42, 0x8, 0x94, 0x99, + 0x2e, 0x4a, 0x51, 0x7, 0x0, + + /* U+5F5D "彝" */ + 0x10, 0x0, 0x5f, 0xe0, 0xff, 0xc0, 0x0, 0x8f, + 0xff, 0xe2, 0x48, 0x15, 0x2e, 0x3e, 0xfe, 0x38, + 0x20, 0xad, 0x5a, 0x4, 0x7, 0xff, 0xf0, 0x82, + 0x2, 0x4, 0x18, 0x8, 0x0, + + /* U+5F61 "彡" */ + 0x0, 0x20, 0x6, 0x0, 0xc0, 0x38, 0x6, 0x0, + 0x0, 0x30, 0x6, 0x0, 0xc0, 0x38, 0x0, 0x0, + 0xc0, 0x18, 0x3, 0x0, 0xe0, 0x38, 0x0, 0x0, + 0x0, + + /* U+5F62 "形" */ + 0x0, 0x5, 0xff, 0x10, 0x88, 0x41, 0x13, 0x2, + 0x20, 0x4, 0x40, 0xbf, 0xe2, 0x11, 0x18, 0x22, + 0x40, 0x44, 0x4, 0x88, 0x12, 0x10, 0x44, 0x21, + 0x18, 0x4c, 0x20, 0xa0, 0x0, + + /* U+5F64 "彤" */ + 0x3e, 0x4, 0x84, 0x11, 0x8, 0x42, 0x53, 0x4, + 0xa0, 0x28, 0xc0, 0x90, 0x82, 0x7f, 0xc8, 0x42, + 0x60, 0x84, 0x5, 0x8, 0x1a, 0x10, 0x24, 0x21, + 0x90, 0x44, 0x27, 0xb0, 0x0, + + /* U+5F66 "彦" */ + 0x0, 0x0, 0x2, 0x3, 0xff, 0xf8, 0x1, 0x0, + 0x82, 0x0, 0x8, 0x1f, 0xff, 0x20, 0x0, 0x40, + 0xe0, 0x9e, 0x1, 0x0, 0xc2, 0xe, 0x5, 0xe0, + 0x88, 0x6, 0x20, 0xf0, 0x5e, 0x0, + + /* U+5F69 "彩" */ + 0x7f, 0x82, 0x0, 0x4, 0x48, 0x98, 0x25, 0x20, + 0x21, 0x2, 0x4, 0x4, 0x7f, 0x88, 0xc, 0x30, + 0xc, 0x1, 0x16, 0x2, 0x25, 0x84, 0x44, 0x8, + 0x4, 0x30, 0x4, 0xc0, + + /* U+5F6A "彪" */ + 0x8, 0x4, 0x1e, 0x10, 0x20, 0x43, 0xff, 0x4, + 0x90, 0x49, 0xc1, 0x16, 0x44, 0x27, 0xb1, 0x40, + 0x4, 0xbc, 0x19, 0x48, 0x62, 0x91, 0x95, 0x24, + 0x32, 0x40, 0x68, 0xff, 0x0, 0x0, + + /* U+5F6C "彬" */ + 0x11, 0x2, 0x22, 0x8, 0x44, 0x23, 0xde, 0x81, + 0x10, 0x6, 0x20, 0x4e, 0x61, 0x29, 0xac, 0x53, + 0x30, 0xaa, 0x0, 0x54, 0x8, 0x88, 0x21, 0x10, + 0x82, 0x26, 0x4, 0x40, 0x0, + + /* U+5F6D "彭" */ + 0x8, 0x5, 0xff, 0x10, 0x20, 0x40, 0x41, 0xf, + 0xf8, 0x20, 0x0, 0x9f, 0xc2, 0x20, 0x88, 0x41, + 0x20, 0xfe, 0x4, 0x88, 0x11, 0x10, 0x61, 0x41, + 0x9f, 0xe6, 0x0, 0x10, 0x0, + + /* U+5F70 "彰" */ + 0x0, 0x0, 0x10, 0x9, 0xfc, 0x21, 0x10, 0x82, + 0x42, 0x1f, 0xf0, 0x0, 0x1, 0x3f, 0x84, 0x41, + 0x10, 0xfe, 0x41, 0x4, 0xb, 0xf8, 0x20, 0x80, + 0x5f, 0xf3, 0x2, 0x8, 0x4, 0x20, + + /* U+5F71 "影" */ + 0x7f, 0xc, 0x82, 0x11, 0xfc, 0xc2, 0xb, 0x7, + 0xf0, 0x41, 0x1, 0xbf, 0xe2, 0x0, 0x8, 0x7f, + 0x60, 0x82, 0x5, 0xfc, 0x10, 0x40, 0x46, 0xb1, + 0x11, 0xc, 0xe, 0x20, 0x0, + + /* U+5F73 "彳" */ + 0x2, 0x4, 0x8, 0x30, 0x41, 0x2, 0x4, 0x18, + 0x38, 0xc8, 0x88, 0x8, 0x8, 0x8, 0x8, + + /* U+5F77 "彷" */ + 0x0, 0x20, 0x8, 0x20, 0x10, 0x10, 0x23, 0xff, + 0x40, 0x80, 0x8, 0x80, 0x8, 0x80, 0x10, 0xfc, + 0x30, 0x84, 0x70, 0x84, 0x50, 0x84, 0x11, 0x4, + 0x11, 0x4, 0x13, 0x4, 0x12, 0x4, 0x14, 0x38, + + /* U+5F79 "役" */ + 0x0, 0x0, 0x27, 0xe0, 0x88, 0x42, 0x10, 0x89, + 0x21, 0x2, 0x83, 0x88, 0x0, 0x33, 0xfc, 0x62, + 0x9, 0x44, 0x20, 0x84, 0xc1, 0x7, 0x2, 0xc, + 0x4, 0x67, 0xb, 0x1, 0x0, + + /* U+5F7B "彻" */ + 0x12, 0x0, 0x4b, 0xf2, 0x22, 0x50, 0x89, 0x92, + 0x24, 0x5e, 0x92, 0x22, 0x58, 0x89, 0x62, 0x26, + 0x88, 0x92, 0x2c, 0x48, 0xd1, 0x20, 0x44, 0x82, + 0x12, 0x13, 0x80, + + /* U+5F7C "彼" */ + 0x0, 0x20, 0x10, 0x20, 0x10, 0x20, 0x23, 0xfe, + 0x42, 0x22, 0x82, 0x22, 0x12, 0x20, 0x23, 0xfc, + 0x62, 0x84, 0xa2, 0x84, 0xa2, 0x48, 0x22, 0x58, + 0x24, 0x30, 0x24, 0x70, 0x2d, 0x8c, 0x2b, 0x2, + + /* U+5F80 "往" */ + 0x4, 0x20, 0xc, 0x20, 0x18, 0x10, 0x21, 0xff, + 0x40, 0x20, 0xc, 0x20, 0x10, 0x20, 0x31, 0xfe, + 0x50, 0x20, 0x10, 0x20, 0x10, 0x20, 0x10, 0x20, + 0x10, 0x20, 0x13, 0xff, 0x10, 0x0, + + /* U+5F81 "征" */ + 0x8, 0x0, 0x27, 0xfc, 0xc0, 0x43, 0x0, 0x80, + 0x81, 0x1, 0x22, 0x4, 0x44, 0x18, 0x8f, 0x71, + 0x10, 0xa2, 0x20, 0x44, 0x40, 0x88, 0x81, 0x11, + 0x2, 0xff, 0xc4, 0x0, 0x0, + + /* U+5F82 "徂" */ + 0x10, 0x0, 0x13, 0xfc, 0x22, 0x4, 0x42, 0x4, + 0x92, 0x4, 0x13, 0xfc, 0x22, 0x4, 0x62, 0x4, + 0xa2, 0x4, 0x23, 0xfc, 0x22, 0x4, 0x22, 0x4, + 0x22, 0x4, 0x2f, 0xff, 0x20, 0x0, + + /* U+5F84 "径" */ + 0x10, 0x0, 0x2f, 0xf8, 0x80, 0x22, 0x0, 0x89, + 0x6, 0x2, 0x37, 0xb, 0x81, 0xb0, 0x0, 0xa7, + 0xfc, 0x40, 0x80, 0x81, 0x1, 0x2, 0x2, 0x4, + 0x5, 0xff, 0xc8, 0x0, 0x0, + + /* U+5F85 "待" */ + 0x0, 0x40, 0x20, 0x80, 0xc1, 0x1, 0x1f, 0xe4, + 0x4, 0x13, 0x8, 0x5, 0xff, 0x90, 0x0, 0x60, + 0x11, 0x4f, 0xfc, 0x80, 0x41, 0x10, 0x82, 0x11, + 0x4, 0x22, 0x8, 0x4, 0x10, 0x78, + + /* U+5F87 "徇" */ + 0x11, 0x0, 0x44, 0x2, 0x3f, 0xd0, 0x81, 0x94, + 0x4, 0x4f, 0x92, 0x22, 0x58, 0x89, 0xa3, 0xe6, + 0x88, 0x92, 0x22, 0x48, 0xf9, 0x20, 0x4, 0x80, + 0x12, 0x7, 0x80, + + /* U+5F88 "很" */ + 0x10, 0x0, 0x2f, 0xf8, 0x90, 0x12, 0x20, 0x29, + 0x7f, 0xc2, 0x80, 0x89, 0x1, 0x33, 0xfe, 0x64, + 0x81, 0x49, 0xc, 0x91, 0x61, 0x21, 0x2, 0x49, + 0x4, 0xe1, 0x89, 0x0, 0x80, + + /* U+5F89 "徉" */ + 0x12, 0x8, 0x42, 0x20, 0x84, 0x42, 0x3f, 0xf8, + 0x4, 0x2, 0x8, 0x9, 0xff, 0x30, 0x20, 0xa0, + 0x41, 0x40, 0x80, 0xbf, 0xf9, 0x2, 0x2, 0x4, + 0x4, 0x8, 0x8, 0x10, 0x0, + + /* U+5F8A "徊" */ + 0x10, 0x0, 0x4f, 0xfc, 0x90, 0xe, 0x20, 0x19, + 0x4f, 0x22, 0x92, 0x49, 0x24, 0xb2, 0x49, 0xa4, + 0x92, 0x49, 0xe4, 0x90, 0x9, 0x20, 0x12, 0x40, + 0x24, 0xff, 0xc9, 0x0, 0x80, + + /* U+5F8B "律" */ + 0x10, 0x80, 0x4f, 0xf1, 0x2, 0x22, 0x4, 0x49, + 0xff, 0xe4, 0x11, 0x9, 0xfe, 0x30, 0x40, 0xa0, + 0x80, 0x5f, 0xf8, 0x82, 0x1, 0x7f, 0xf2, 0x8, + 0x4, 0x10, 0x8, 0x20, 0x0, + + /* U+5F8C "後" */ + 0x18, 0x40, 0x21, 0x0, 0x8c, 0x22, 0x3f, 0x89, + 0x4, 0x2, 0x71, 0x9, 0xff, 0x30, 0x0, 0xa1, + 0xf8, 0x44, 0x10, 0x94, 0x41, 0x45, 0x2, 0xe, + 0x4, 0x63, 0x8b, 0x0, 0x80, + + /* U+5F90 "徐" */ + 0x10, 0x40, 0x61, 0x40, 0x84, 0xc6, 0x8, 0xc1, + 0x60, 0xc6, 0xfe, 0x8, 0x10, 0x30, 0x20, 0xa7, + 0xfc, 0x40, 0x80, 0x89, 0x41, 0x22, 0x42, 0x44, + 0x44, 0x8, 0x88, 0x60, 0x0, + + /* U+5F92 "徒" */ + 0x8, 0x40, 0x20, 0x80, 0x9f, 0xf6, 0x2, 0x1, + 0x84, 0x2, 0x8, 0x9, 0xff, 0xb0, 0x0, 0xa4, + 0x40, 0x48, 0xf8, 0x91, 0x1, 0x32, 0x2, 0x54, + 0x5, 0x1f, 0xca, 0x0, 0x0, + + /* U+5F95 "徕" */ + 0x10, 0x40, 0x40, 0x81, 0x1f, 0xf2, 0x2, 0x1, + 0x44, 0x44, 0x49, 0x8, 0x10, 0x37, 0xff, 0xa0, + 0xe0, 0x41, 0xc0, 0x85, 0x41, 0x12, 0x42, 0xc4, + 0x65, 0x8, 0x8, 0x10, 0x0, + + /* U+5F97 "得" */ + 0x10, 0x0, 0x27, 0xf8, 0x90, 0x12, 0x20, 0x28, + 0x3f, 0xc2, 0x80, 0x88, 0xff, 0x10, 0x0, 0x67, + 0xff, 0x40, 0x10, 0x80, 0x21, 0x7f, 0xf2, 0x0, + 0x84, 0x41, 0x8, 0x2, 0x10, 0x38, + + /* U+5F98 "徘" */ + 0x11, 0x10, 0x21, 0x10, 0x21, 0x10, 0x4f, 0x1e, + 0x81, 0x10, 0x11, 0x10, 0x21, 0x10, 0x2f, 0x1e, + 0x61, 0x10, 0xa1, 0x10, 0x2f, 0x1f, 0x21, 0x10, + 0x21, 0x10, 0x21, 0x10, 0x21, 0x10, + + /* U+5F99 "徙" */ + 0x10, 0x40, 0x81, 0x6, 0x27, 0xf0, 0x90, 0x12, + 0x40, 0x89, 0x2, 0xff, 0xd8, 0x0, 0xa4, 0x40, + 0x91, 0xf2, 0x44, 0x9, 0x90, 0x29, 0x40, 0xa3, + 0xf3, 0x0, 0x0, + + /* U+5F9C "徜" */ + 0x0, 0x0, 0x28, 0x84, 0x89, 0x12, 0xa, 0x4c, + 0x4, 0x12, 0xff, 0xc5, 0x0, 0x92, 0x1, 0x64, + 0xf2, 0xc9, 0x26, 0x92, 0x49, 0x24, 0x92, 0x4f, + 0x24, 0x80, 0x49, 0x0, 0x92, 0xf, + + /* U+5FA1 "御" */ + 0x12, 0x0, 0x48, 0x3d, 0x1f, 0x4a, 0x48, 0x91, + 0x11, 0x25, 0xfa, 0x48, 0x44, 0xb0, 0x89, 0xa9, + 0xd2, 0x52, 0x24, 0xa4, 0x49, 0x48, 0x92, 0x9d, + 0xe5, 0xc2, 0x8, 0x4, 0x0, + + /* U+5FA8 "徨" */ + 0x0, 0x0, 0x21, 0x0, 0x9f, 0xf1, 0x20, 0x24, + 0x40, 0x52, 0xff, 0x85, 0x1, 0x13, 0xfe, 0x60, + 0x1, 0x4f, 0xf8, 0x81, 0x1, 0x2, 0x2, 0x7f, + 0xc4, 0x8, 0xb, 0xff, 0x90, 0x0, + + /* U+5FAA "循" */ + 0x10, 0x0, 0x2f, 0xe8, 0x90, 0x82, 0x21, 0x8, + 0x7f, 0xe2, 0x84, 0x9, 0x7f, 0x32, 0x82, 0xa5, + 0xfc, 0x4a, 0x8, 0x94, 0x11, 0x2f, 0xe2, 0x90, + 0x45, 0x3f, 0x88, 0x41, 0x0, + + /* U+5FAD "徭" */ + 0x10, 0x0, 0x2f, 0xf8, 0x80, 0x12, 0x22, 0x49, + 0x24, 0x82, 0x40, 0x9, 0xff, 0x32, 0x20, 0xa0, + 0x41, 0x5f, 0xfc, 0x81, 0x1, 0x22, 0x22, 0x44, + 0x44, 0xff, 0x88, 0x1, 0x0, + + /* U+5FAE "微" */ + 0x11, 0x8, 0x29, 0x48, 0x49, 0x50, 0x49, 0x5f, + 0x1f, 0xd2, 0x20, 0x32, 0x2f, 0xd2, 0x60, 0x12, + 0xa0, 0x14, 0x27, 0x8c, 0x24, 0x8c, 0x24, 0xa8, + 0x28, 0xd4, 0x28, 0xb2, 0x20, 0x21, + + /* U+5FB5 "徵" */ + 0x11, 0x8, 0x15, 0x48, 0x25, 0x50, 0x47, 0xdf, + 0x10, 0x12, 0x17, 0xf2, 0x20, 0x32, 0x67, 0xd2, + 0xa1, 0xc, 0x21, 0xc, 0x27, 0xcc, 0x21, 0xc, + 0x2f, 0xd4, 0x20, 0x12, 0x20, 0x20, + + /* U+5FB7 "德" */ + 0x0, 0x40, 0x20, 0x80, 0x9f, 0xfb, 0x2, 0x8, + 0x7f, 0xc2, 0x92, 0x85, 0x25, 0x13, 0xfe, 0x60, + 0x1, 0x40, 0x0, 0x9f, 0xf9, 0x2, 0x2, 0x54, + 0x45, 0x21, 0x4a, 0x42, 0x10, 0xf8, + + /* U+5FBC "徼" */ + 0x11, 0x8, 0x12, 0x8, 0x27, 0x88, 0x24, 0x90, + 0x44, 0x9f, 0x87, 0x92, 0x14, 0xb2, 0x27, 0xb2, + 0x60, 0x12, 0xaf, 0xd4, 0x24, 0xc, 0x27, 0x8c, + 0x24, 0x8c, 0x24, 0x94, 0x28, 0xa2, 0x33, 0xc2, + + /* U+5FBD "徽" */ + 0x11, 0x8, 0x29, 0x48, 0x29, 0x50, 0x4f, 0xdf, + 0x10, 0x12, 0x2f, 0xf2, 0x22, 0x32, 0x6f, 0x92, + 0xa3, 0xa, 0x24, 0x4c, 0x2f, 0xcc, 0x21, 0x8, + 0x29, 0x4c, 0x31, 0x12, 0x26, 0x20, + + /* U+5FC3 "心" */ + 0x0, 0x0, 0x2, 0x0, 0x2, 0x0, 0x44, 0x0, + 0x84, 0x5, 0x1, 0x12, 0x1, 0x24, 0x2, 0x48, + 0x7, 0x10, 0x6, 0x20, 0x8, 0x40, 0x40, 0x80, + 0x81, 0x2, 0x1, 0xfc, 0x0, + + /* U+5FC4 "忄" */ + 0x10, 0x20, 0x42, 0xa5, 0x32, 0x64, 0x8, 0x10, + 0x20, 0x40, 0x81, 0x2, 0x4, 0x0, + + /* U+5FC5 "必" */ + 0x2, 0x0, 0x2, 0x10, 0x22, 0x40, 0x40, 0x82, + 0x82, 0x9, 0x9, 0x12, 0x31, 0x24, 0xc2, 0x8b, + 0x3, 0x1c, 0x4, 0x20, 0x21, 0xc0, 0x84, 0x81, + 0x0, 0xfe, 0x0, + + /* U+5FC6 "忆" */ + 0x20, 0x0, 0x47, 0xf8, 0x80, 0x21, 0x40, 0x8a, + 0x82, 0x14, 0xc, 0x28, 0x10, 0x10, 0x40, 0x21, + 0x0, 0x44, 0x4, 0x90, 0x9, 0x20, 0x12, 0x40, + 0x64, 0x7f, 0x88, 0x0, 0x0, + + /* U+5FC9 "忉" */ + 0x20, 0x0, 0x9f, 0xf2, 0x8, 0x4c, 0x21, 0xa8, + 0x86, 0xa2, 0x1a, 0x8, 0x48, 0x21, 0x20, 0x84, + 0x82, 0x12, 0x10, 0x48, 0x41, 0x22, 0x4, 0x90, + 0x32, 0x8f, 0x80, + + /* U+5FCC "忌" */ + 0x3f, 0xf8, 0x0, 0x8, 0x0, 0x8, 0x3f, 0xfa, + 0x20, 0x2, 0x20, 0x4, 0x3f, 0xfc, 0x0, 0x0, + 0x1, 0x0, 0x28, 0x84, 0x48, 0x84, 0x48, 0x12, + 0x88, 0x10, 0x7, 0xe0, + + /* U+5FCD "忍" */ + 0x3f, 0xf8, 0x2, 0x10, 0x44, 0x21, 0x10, 0x40, + 0x20, 0x81, 0x81, 0xe, 0x3c, 0x20, 0x0, 0x9, + 0x0, 0x51, 0x11, 0x22, 0x12, 0x41, 0x10, 0x82, + 0x1, 0xfc, 0x0, + + /* U+5FCF "忏" */ + 0x20, 0x0, 0x47, 0xfc, 0x80, 0x81, 0x41, 0xa, + 0x82, 0x14, 0x84, 0x28, 0x8, 0x13, 0xff, 0x20, + 0x20, 0x40, 0x40, 0x80, 0x81, 0x1, 0x2, 0x2, + 0x4, 0x4, 0x8, 0x8, 0x0, + + /* U+5FD0 "忐" */ + 0x2, 0x0, 0x8, 0x0, 0x3f, 0xc0, 0x80, 0x2, + 0x0, 0x8, 0xf, 0xff, 0xc0, 0x0, 0x2, 0x1, + 0x44, 0x25, 0x10, 0xa4, 0x9, 0x90, 0x24, 0x40, + 0x80, 0xfc, 0x0, + + /* U+5FD1 "忑" */ + 0x7f, 0xfe, 0x1, 0x0, 0x1, 0x0, 0x1, 0xc0, + 0x1, 0x38, 0x1, 0x4, 0x0, 0x0, 0x1, 0x0, + 0x8, 0x84, 0x48, 0x44, 0x48, 0x2, 0x88, 0x10, + 0x8, 0x10, 0x7, 0xe0, + + /* U+5FD2 "忒" */ + 0x0, 0x48, 0x0, 0x88, 0x1, 0x7, 0xff, 0xf0, + 0x2, 0x1, 0x4, 0x1, 0x88, 0x8, 0x90, 0x50, + 0xa0, 0xa1, 0x41, 0x4a, 0x84, 0x91, 0x19, 0x21, + 0x23, 0xc2, 0x40, 0x3, 0x0, + + /* U+5FD6 "忖" */ + 0x20, 0x8, 0x40, 0x10, 0x80, 0x25, 0x5f, 0xfa, + 0x80, 0x94, 0x1, 0x28, 0x82, 0x10, 0x84, 0x21, + 0x8, 0x41, 0x10, 0x80, 0x21, 0x0, 0x42, 0x0, + 0x84, 0x3, 0x8, 0x3c, 0x0, + + /* U+5FD7 "志" */ + 0x1, 0x0, 0x2, 0x3, 0xff, 0xf8, 0x8, 0x0, + 0x10, 0x0, 0x20, 0x1f, 0xff, 0x0, 0x0, 0x2, + 0x0, 0x92, 0x1, 0x22, 0x14, 0x40, 0x98, 0x81, + 0x1, 0x4, 0x3, 0xf8, 0x0, + + /* U+5FD8 "忘" */ + 0x1, 0x0, 0x1, 0x3, 0xff, 0xf9, 0x0, 0x2, + 0x0, 0x4, 0x0, 0xf, 0xff, 0x0, 0x0, 0x1, + 0x0, 0x11, 0x1, 0x22, 0x12, 0x40, 0xa8, 0x81, + 0x21, 0x2, 0x3, 0xf8, 0x0, + + /* U+5FD9 "忙" */ + 0x20, 0x40, 0x40, 0x80, 0x80, 0x81, 0xbf, 0xfa, + 0xa0, 0x15, 0x40, 0x28, 0x80, 0x11, 0x0, 0x22, + 0x0, 0x44, 0x0, 0x88, 0x1, 0x10, 0x2, 0x20, + 0x4, 0x7f, 0x88, 0x0, 0x0, + + /* U+5FDD "忝" */ + 0x7f, 0xfc, 0x2, 0x0, 0x8, 0x7, 0xff, 0xf0, + 0x44, 0x1, 0x8c, 0x6, 0xc, 0x30, 0x86, 0xc1, + 0x2, 0x22, 0x90, 0x85, 0x21, 0x9, 0x24, 0x10, + 0x1, 0xc0, 0x0, + + /* U+5FE0 "忠" */ + 0x0, 0x80, 0x0, 0x80, 0x3f, 0xfc, 0x20, 0x84, + 0x20, 0x84, 0x20, 0x84, 0x1f, 0xf8, 0x0, 0x80, + 0x1, 0x0, 0x1, 0x80, 0x28, 0x84, 0x48, 0x42, + 0x48, 0x12, 0x88, 0x10, 0xf, 0xe0, + + /* U+5FE1 "忡" */ + 0x20, 0x40, 0x81, 0x2, 0x4, 0xc, 0xff, 0xaa, + 0x46, 0x89, 0x1a, 0x24, 0x68, 0x91, 0x22, 0x44, + 0x8f, 0xf2, 0x24, 0x48, 0x10, 0x20, 0x40, 0x81, + 0x2, 0x4, 0x0, + + /* U+5FE4 "忤" */ + 0x21, 0x0, 0x42, 0x0, 0x87, 0xf5, 0xc9, 0xa, + 0xa2, 0x14, 0x44, 0x28, 0x8, 0x10, 0x10, 0x27, + 0xfe, 0x40, 0x40, 0x80, 0x81, 0x1, 0x2, 0x2, + 0x4, 0x4, 0x8, 0x8, 0x0, + + /* U+5FE7 "忧" */ + 0x21, 0x0, 0x42, 0x20, 0xc4, 0x25, 0xc8, 0xa, + 0xbf, 0xf4, 0x24, 0x28, 0x48, 0x10, 0x90, 0x21, + 0x20, 0x42, 0x40, 0x88, 0x81, 0x11, 0x12, 0x22, + 0x24, 0x84, 0x4a, 0xf, 0x0, + + /* U+5FEA "忪" */ + 0x20, 0x0, 0x41, 0x20, 0x84, 0x41, 0x88, 0x4a, + 0xa0, 0x94, 0x40, 0xa9, 0x10, 0x90, 0x20, 0x20, + 0x80, 0x41, 0x20, 0x84, 0x41, 0x8, 0x42, 0x20, + 0x84, 0xff, 0x88, 0x1, 0x80, + + /* U+5FEB "快" */ + 0x20, 0x80, 0x41, 0x0, 0x82, 0x1, 0xbf, 0xca, + 0x88, 0x94, 0x11, 0x28, 0x22, 0x10, 0x44, 0x27, + 0xfe, 0x41, 0x80, 0x83, 0x1, 0x9, 0x2, 0x21, + 0x4, 0xc1, 0x8b, 0x0, 0x80, + + /* U+5FED "忭" */ + 0x20, 0x40, 0x40, 0x40, 0x80, 0x85, 0x5f, 0xfa, + 0x84, 0x14, 0x88, 0x28, 0x10, 0x10, 0x38, 0x20, + 0x48, 0x40, 0x88, 0x81, 0x1, 0x2, 0x2, 0x4, + 0x4, 0x8, 0x8, 0x10, 0x0, + + /* U+5FEE "忮" */ + 0x20, 0x40, 0x40, 0x80, 0x9f, 0xfd, 0x42, 0xa, + 0x84, 0x14, 0x8, 0x29, 0xff, 0x11, 0x2, 0x21, + 0x8, 0x42, 0x10, 0x82, 0x41, 0x3, 0x2, 0xe, + 0x4, 0x63, 0x8b, 0x0, 0x80, + + /* U+5FF1 "忱" */ + 0x20, 0x20, 0x20, 0x20, 0x23, 0xfe, 0x6a, 0x22, + 0x6a, 0x22, 0x62, 0x22, 0xa0, 0x20, 0xa0, 0x50, + 0x20, 0x50, 0x20, 0x50, 0x20, 0x90, 0x20, 0x90, + 0x21, 0x12, 0x22, 0x12, 0x24, 0x1e, + + /* U+5FF5 "念" */ + 0x0, 0x0, 0x3, 0x0, 0x9, 0x0, 0x21, 0x1, + 0x91, 0x8c, 0x10, 0xc0, 0x0, 0xf, 0xf8, 0x0, + 0x10, 0x0, 0x40, 0xd, 0x0, 0x4c, 0x44, 0x88, + 0x49, 0x2, 0x52, 0x8, 0x3, 0xf0, + + /* U+5FF8 "忸" */ + 0x10, 0x0, 0x13, 0xfc, 0x10, 0x44, 0x14, 0x44, + 0x54, 0x84, 0x50, 0x84, 0x50, 0x84, 0x93, 0xfc, + 0x10, 0x84, 0x10, 0x84, 0x10, 0x84, 0x10, 0x84, + 0x10, 0x84, 0x13, 0xff, 0x10, 0x0, + + /* U+5FFB "忻" */ + 0x20, 0x0, 0x47, 0xf8, 0x88, 0x1, 0x90, 0xa, + 0xa0, 0x14, 0x40, 0x28, 0xff, 0xd1, 0x8, 0x22, + 0x10, 0x44, 0x20, 0x88, 0x41, 0x20, 0x82, 0x41, + 0x4, 0x82, 0xa, 0x4, 0x0, + + /* U+5FFD "忽" */ + 0x0, 0x0, 0x8, 0x0, 0xf, 0xfc, 0x11, 0x24, + 0x22, 0x24, 0x6, 0x44, 0x4, 0x44, 0x18, 0x84, + 0x21, 0x4, 0x2, 0x38, 0x1, 0x0, 0x48, 0x84, + 0x48, 0xd6, 0x48, 0x12, 0x88, 0x10, 0xf, 0xe0, + + /* U+5FFE "忾" */ + 0x21, 0x0, 0x21, 0xfe, 0x2a, 0x0, 0xaa, 0x0, + 0xa5, 0xfc, 0xa0, 0x0, 0xa0, 0x0, 0x23, 0xf8, + 0x20, 0x8, 0x20, 0x8, 0x20, 0x8, 0x20, 0xb, + 0x20, 0xa, 0x20, 0xa, 0x20, 0x6, + + /* U+5FFF "忿" */ + 0x4, 0x40, 0x18, 0xc0, 0x40, 0xc3, 0x0, 0x69, + 0xff, 0x20, 0x82, 0x1, 0x4, 0x4, 0x8, 0x71, + 0xe0, 0x0, 0x1, 0x24, 0x22, 0x44, 0x28, 0x82, + 0x51, 0x4, 0x3, 0xf8, 0x0, + + /* U+6000 "怀" */ + 0x10, 0x0, 0x13, 0xff, 0x10, 0x10, 0x10, 0x20, + 0x54, 0x20, 0x54, 0x40, 0x50, 0x68, 0x90, 0xac, + 0x11, 0x24, 0x13, 0x22, 0x16, 0x21, 0x10, 0x20, + 0x10, 0x20, 0x10, 0x20, 0x10, 0x20, + + /* U+6001 "态" */ + 0x0, 0x0, 0x1, 0x0, 0x7f, 0xfe, 0x1, 0x80, + 0x2, 0x40, 0x2, 0x60, 0xe, 0x30, 0x31, 0xc, + 0x40, 0x2, 0x1, 0x0, 0x28, 0x84, 0x28, 0xd6, + 0x48, 0x12, 0x48, 0x10, 0x7, 0xe0, + + /* U+6002 "怂" */ + 0x8, 0x20, 0x10, 0x40, 0x20, 0x80, 0x41, 0x1, + 0x65, 0x2, 0x69, 0x8, 0x22, 0x20, 0x83, 0x2, + 0x0, 0x2, 0x1, 0x22, 0x12, 0x44, 0xb4, 0x81, + 0x31, 0x2, 0x1, 0xf8, 0x0, + + /* U+6003 "怃" */ + 0x20, 0x0, 0x47, 0xf8, 0xc1, 0x1, 0xc2, 0xa, + 0x84, 0x14, 0x8, 0x29, 0xff, 0xd0, 0x20, 0x20, + 0xa0, 0x41, 0x40, 0x82, 0x81, 0x9, 0x12, 0x22, + 0x24, 0x84, 0x4a, 0xf, 0x0, + + /* U+6004 "怄" */ + 0x20, 0x0, 0x4f, 0xf8, 0x90, 0x1, 0xa8, 0x2b, + 0x48, 0x95, 0x92, 0x29, 0x14, 0x52, 0x10, 0x24, + 0x20, 0x48, 0xa0, 0x92, 0x21, 0x2c, 0x42, 0x50, + 0x44, 0x80, 0x9, 0xff, 0x80, + + /* U+6005 "怅" */ + 0x10, 0x80, 0x10, 0x82, 0x10, 0x8c, 0x54, 0x90, + 0x52, 0xa0, 0x50, 0x80, 0x93, 0xff, 0x10, 0xa0, + 0x10, 0xa0, 0x10, 0x90, 0x10, 0x90, 0x10, 0x88, + 0x10, 0xa4, 0x10, 0xc3, 0x10, 0x80, + + /* U+6006 "怆" */ + 0x10, 0x20, 0x10, 0x30, 0x18, 0x58, 0x14, 0x8c, + 0x51, 0x6, 0x56, 0x1, 0x51, 0xfc, 0x91, 0x4, + 0x11, 0x4, 0x11, 0x4, 0x11, 0x39, 0x11, 0x1, + 0x11, 0x1, 0x11, 0x2, 0x10, 0xfe, + + /* U+600A "怊" */ + 0x20, 0x0, 0x9f, 0xf2, 0x8, 0x4c, 0x21, 0xa8, + 0x86, 0xa4, 0x1a, 0x67, 0x88, 0x0, 0x23, 0xfc, + 0x88, 0x12, 0x20, 0x48, 0x81, 0x22, 0x4, 0x8f, + 0xf2, 0x20, 0x40, + + /* U+600D "怍" */ + 0x10, 0x80, 0x11, 0x0, 0x19, 0xff, 0x55, 0x40, + 0x52, 0x40, 0x52, 0x40, 0x90, 0x7e, 0x10, 0x40, + 0x10, 0x40, 0x10, 0x40, 0x10, 0x7e, 0x10, 0x40, + 0x10, 0x40, 0x10, 0x40, 0x10, 0x40, + + /* U+600E "怎" */ + 0x8, 0x0, 0x10, 0x0, 0x1f, 0xfe, 0x22, 0x0, + 0x22, 0x0, 0x43, 0xfc, 0x2, 0x0, 0x2, 0x0, + 0x3, 0xfc, 0x2, 0x0, 0x1, 0x0, 0x48, 0x84, + 0x48, 0x94, 0x48, 0x12, 0x88, 0x10, 0x7, 0xe0, + + /* U+600F "怏" */ + 0x20, 0x40, 0x40, 0x80, 0x81, 0x1, 0xbf, 0xeb, + 0x44, 0x54, 0x88, 0xa9, 0x11, 0x12, 0x22, 0x2f, + 0xfe, 0x40, 0x80, 0x82, 0x81, 0x5, 0x2, 0x11, + 0x4, 0xc1, 0x8a, 0x0, 0x80, + + /* U+6012 "怒" */ + 0x20, 0x0, 0x87, 0xef, 0xd0, 0x91, 0x22, 0x44, + 0x91, 0x21, 0x83, 0x86, 0xf, 0x24, 0xc3, 0xc, + 0x0, 0x5, 0x20, 0x94, 0x4a, 0x90, 0x26, 0x40, + 0x80, 0xfc, 0x0, + + /* U+6014 "怔" */ + 0x20, 0x0, 0x9f, 0xf2, 0x4, 0xc, 0x10, 0xaa, + 0x42, 0xa9, 0xa, 0x24, 0x28, 0x8f, 0x22, 0x40, + 0x89, 0x2, 0x24, 0x8, 0x90, 0x22, 0x40, 0xbf, + 0xf2, 0x0, 0x0, + + /* U+6015 "怕" */ + 0x20, 0x40, 0x81, 0x2, 0x3f, 0xcc, 0x81, 0xaa, + 0x6, 0xa8, 0x1a, 0x20, 0x48, 0xff, 0x22, 0x4, + 0x88, 0x12, 0x20, 0x48, 0x81, 0x22, 0x4, 0x8f, + 0xf2, 0x20, 0x40, + + /* U+6016 "怖" */ + 0x20, 0x80, 0x41, 0x0, 0x9f, 0xf9, 0x88, 0xa, + 0x94, 0x14, 0x48, 0x29, 0xff, 0x13, 0x22, 0x2a, + 0x44, 0x44, 0x88, 0x89, 0x11, 0x12, 0x22, 0x25, + 0xc4, 0x8, 0x8, 0x10, 0x0, + + /* U+6019 "怙" */ + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x19, 0xff, + 0x54, 0x10, 0x52, 0x10, 0x90, 0x10, 0x90, 0x10, + 0x10, 0xfe, 0x10, 0x82, 0x10, 0x82, 0x10, 0x82, + 0x10, 0x82, 0x10, 0xfe, 0x10, 0x82, + + /* U+601B "怛" */ + 0x20, 0x0, 0x47, 0xf8, 0x88, 0x11, 0xd0, 0x2a, + 0x60, 0x54, 0x7f, 0xa8, 0x81, 0x11, 0x2, 0x22, + 0x4, 0x44, 0x8, 0x8f, 0xf1, 0x0, 0x2, 0x0, + 0x4, 0xff, 0xc8, 0x0, 0x0, + + /* U+601C "怜" */ + 0x20, 0x40, 0x40, 0xc0, 0xc2, 0x85, 0x48, 0x8a, + 0x20, 0xd5, 0x88, 0xe8, 0x8, 0x10, 0x0, 0x27, + 0xfc, 0x40, 0x18, 0x80, 0x61, 0x19, 0x82, 0xe, + 0x4, 0xc, 0x8, 0x4, 0x0, + + /* U+601D "思" */ + 0x3f, 0xfc, 0x21, 0x4, 0x21, 0x4, 0x3f, 0xfc, + 0x21, 0x4, 0x21, 0x4, 0x3f, 0xfc, 0x0, 0x0, + 0x1, 0x0, 0x29, 0x4, 0x48, 0x92, 0x48, 0x12, + 0x88, 0x10, 0x7, 0xe0, + + /* U+6020 "怠" */ + 0x3, 0x0, 0x8, 0x0, 0x60, 0x83, 0x0, 0xc5, + 0xfe, 0x40, 0x0, 0x4f, 0xfe, 0x10, 0x4, 0x20, + 0x8, 0x40, 0x10, 0xff, 0xe0, 0x8, 0x4, 0x98, + 0x49, 0x2, 0x62, 0x4, 0x87, 0xf0, + + /* U+6021 "怡" */ + 0x20, 0x40, 0x41, 0x0, 0xc2, 0x45, 0x48, 0x4a, + 0x20, 0x54, 0xff, 0xa8, 0x0, 0x90, 0x0, 0x23, + 0xfc, 0x44, 0x8, 0x88, 0x11, 0x10, 0x22, 0x20, + 0x44, 0x7f, 0x88, 0x81, 0x0, + + /* U+6025 "急" */ + 0x8, 0x0, 0xf, 0xe0, 0x10, 0x20, 0x20, 0x40, + 0x5f, 0xfc, 0x0, 0x4, 0x3f, 0xfc, 0x0, 0x4, + 0x0, 0x4, 0x3f, 0xfc, 0x1, 0x0, 0x48, 0x84, + 0x48, 0x12, 0x48, 0x10, 0xf, 0xe0, + + /* U+6026 "怦" */ + 0x20, 0x0, 0x47, 0xf8, 0x81, 0x1, 0x92, 0xa, + 0xa4, 0x54, 0x29, 0x28, 0x52, 0x10, 0x20, 0x27, + 0xfe, 0x40, 0x80, 0x81, 0x1, 0x2, 0x2, 0x4, + 0x4, 0x8, 0x8, 0x10, 0x0, + + /* U+6027 "性" */ + 0x20, 0x40, 0x44, 0x80, 0xc9, 0x5, 0x9f, 0xfa, + 0xc4, 0x14, 0x88, 0x28, 0x10, 0x10, 0x20, 0x27, + 0xfc, 0x40, 0x80, 0x81, 0x1, 0x2, 0x2, 0x4, + 0x5, 0xff, 0xc8, 0x0, 0x0, + + /* U+6028 "怨" */ + 0x10, 0x0, 0x21, 0xf8, 0x7a, 0x11, 0x14, 0x22, + 0x28, 0x4e, 0x90, 0xa5, 0x26, 0x4, 0x41, 0x10, + 0x82, 0x40, 0xf9, 0xc, 0x0, 0x48, 0x44, 0x80, + 0x49, 0x2, 0x62, 0x4, 0x3, 0xf0, + + /* U+6029 "怩" */ + 0x20, 0x0, 0x47, 0xf8, 0x88, 0x11, 0x90, 0x2a, + 0xa0, 0x55, 0x7f, 0xa8, 0x80, 0x51, 0x40, 0x22, + 0x8c, 0x45, 0x20, 0x8b, 0x81, 0x24, 0x12, 0x48, + 0x25, 0x10, 0x4a, 0x3f, 0x0, + + /* U+602A "怪" */ + 0x20, 0x0, 0x4f, 0xf8, 0x88, 0x11, 0x88, 0x4a, + 0x89, 0x14, 0xc, 0x28, 0xe7, 0x92, 0x20, 0x20, + 0x40, 0x4f, 0xf8, 0x81, 0x1, 0x2, 0x2, 0x4, + 0x5, 0xff, 0xc8, 0x0, 0x0, + + /* U+602B "怫" */ + 0x20, 0x0, 0x41, 0x40, 0xbf, 0xf5, 0x85, 0x2a, + 0xa, 0x54, 0xff, 0xaa, 0x28, 0x14, 0x50, 0x28, + 0xa0, 0x5f, 0xfc, 0x82, 0x89, 0x9, 0x12, 0x33, + 0xc4, 0xc4, 0xa, 0x8, 0x0, + + /* U+602F "怯" */ + 0x10, 0x40, 0x20, 0x80, 0x41, 0x0, 0x9f, 0xe5, + 0x44, 0xa, 0x8, 0x24, 0x10, 0xb, 0xff, 0x10, + 0x40, 0x21, 0x0, 0x42, 0x40, 0x88, 0x41, 0x20, + 0x82, 0xff, 0x84, 0x0, 0x80, + + /* U+6035 "怵" */ + 0x10, 0x48, 0x20, 0x98, 0x41, 0x2, 0xdf, 0xf5, + 0x44, 0xa, 0xc, 0x24, 0x38, 0x8, 0x68, 0x11, + 0x48, 0x22, 0x90, 0x49, 0x10, 0xa2, 0x11, 0x4, + 0x2, 0x8, 0x4, 0x10, 0x0, + + /* U+603B "总" */ + 0x8, 0x20, 0x8, 0x40, 0x11, 0x1, 0xff, 0xc2, + 0x0, 0x84, 0x1, 0x8, 0x2, 0x10, 0x4, 0x3f, + 0xf8, 0x0, 0x0, 0x4, 0x2, 0x48, 0x44, 0x88, + 0x49, 0x4, 0x62, 0x8, 0x7, 0xf0, + + /* U+603C "怼" */ + 0x0, 0x8, 0x0, 0x11, 0xf8, 0x20, 0x17, 0xf0, + 0x40, 0x86, 0x91, 0x2, 0x12, 0xa, 0x4, 0x62, + 0x8, 0x0, 0xf0, 0x4, 0x0, 0x48, 0x24, 0x88, + 0x49, 0x4, 0x63, 0x8, 0x3, 0xf0, + + /* U+603F "怿" */ + 0x10, 0x0, 0x13, 0xfe, 0x11, 0x2, 0x10, 0x84, + 0x54, 0x58, 0x50, 0x78, 0x53, 0x87, 0x10, 0x20, + 0x13, 0xfe, 0x10, 0x20, 0x10, 0x20, 0x17, 0xff, + 0x10, 0x20, 0x10, 0x20, 0x10, 0x20, + + /* U+6041 "恁" */ + 0x10, 0x0, 0x2f, 0xf8, 0x81, 0x3, 0x2, 0xa, + 0xff, 0xc4, 0x8, 0x8, 0x10, 0x10, 0x20, 0x27, + 0xfc, 0x0, 0x0, 0x4, 0x2, 0x44, 0x24, 0x82, + 0x51, 0x4, 0x43, 0xf8, 0x0, + + /* U+6042 "恂" */ + 0x21, 0x0, 0x42, 0x0, 0xcf, 0xf9, 0x50, 0x1a, + 0x40, 0x34, 0x7c, 0x68, 0x88, 0x91, 0x11, 0x23, + 0xe2, 0x44, 0x44, 0x88, 0x89, 0x1f, 0x12, 0x0, + 0x24, 0x0, 0x48, 0xf, 0x0, + + /* U+6043 "恃" */ + 0x20, 0x40, 0x40, 0x80, 0x8f, 0xf5, 0x42, 0xa, + 0x4, 0x14, 0xff, 0xe8, 0x0, 0x10, 0x8, 0x27, + 0xfe, 0x40, 0x20, 0x88, 0x41, 0x8, 0x82, 0x11, + 0x4, 0x2, 0x8, 0x3c, 0x0, + + /* U+604B "恋" */ + 0x2, 0x0, 0x2, 0x3, 0xff, 0xf8, 0x24, 0x0, + 0x48, 0x4, 0x93, 0x11, 0x23, 0x42, 0x41, 0x0, + 0x0, 0x4, 0x1, 0x44, 0x22, 0x81, 0x29, 0x2, + 0x31, 0x4, 0x3, 0xf0, 0x0, + + /* U+604D "恍" */ + 0x20, 0x40, 0x48, 0x80, 0x91, 0x15, 0x92, 0x4a, + 0x4, 0x14, 0x8, 0x2b, 0xff, 0x90, 0x90, 0x21, + 0x20, 0x42, 0x40, 0x88, 0x81, 0x11, 0x12, 0x22, + 0x24, 0x84, 0x4a, 0x7, 0x0, + + /* U+6050 "恐" */ + 0xf9, 0xf0, 0x42, 0x20, 0x84, 0x41, 0xa, 0x83, + 0xa1, 0x38, 0x42, 0x41, 0x7, 0x0, 0x80, 0x1, + 0x0, 0xa1, 0x11, 0x42, 0x14, 0x81, 0x38, 0x82, + 0x1, 0xf8, 0x0, + + /* U+6052 "恒" */ + 0x20, 0x0, 0x4f, 0xfc, 0x80, 0x1, 0x80, 0xb, + 0x3f, 0xd5, 0x80, 0xa9, 0x1, 0x11, 0xfe, 0x24, + 0x4, 0x48, 0x8, 0x8f, 0xf1, 0x0, 0x2, 0x0, + 0x5, 0xff, 0xc8, 0x0, 0x0, + + /* U+6055 "恕" */ + 0x10, 0x0, 0x41, 0xfb, 0xfa, 0x11, 0x14, 0x24, + 0x28, 0x48, 0x90, 0x8e, 0x21, 0xb, 0x7e, 0xe0, + 0x1, 0x2, 0x1, 0x22, 0x22, 0x44, 0xa8, 0x81, + 0x31, 0x4, 0x3, 0xf8, 0x0, + + /* U+6059 "恙" */ + 0x10, 0x20, 0x21, 0xf, 0xff, 0xc0, 0x80, 0x2, + 0x1, 0xff, 0xe0, 0x20, 0x0, 0x80, 0xff, 0xfc, + 0x0, 0x5, 0x30, 0x14, 0x42, 0x90, 0x26, 0x40, + 0x80, 0xfc, 0x0, + + /* U+605A "恚" */ + 0x0, 0x1, 0xff, 0xe0, 0x20, 0x0, 0x80, 0xff, + 0xfc, 0x0, 0x7, 0xff, 0x80, 0x80, 0x2, 0x3, + 0xff, 0xf0, 0x20, 0x14, 0x42, 0x91, 0x26, 0x40, + 0x90, 0xfc, 0x0, + + /* U+605D "恝" */ + 0x10, 0x0, 0x21, 0xf9, 0xf1, 0x10, 0x82, 0x27, + 0xe4, 0x42, 0x8, 0x84, 0x21, 0x7f, 0x46, 0x11, + 0x30, 0x0, 0x0, 0x24, 0x22, 0x49, 0x24, 0x82, + 0x71, 0x4, 0x3, 0xf0, 0x0, + + /* U+6062 "恢" */ + 0x21, 0x0, 0x42, 0x0, 0x9f, 0xf9, 0x88, 0xa, + 0xa2, 0x14, 0x44, 0x28, 0x88, 0x11, 0x92, 0x24, + 0xac, 0x48, 0xa0, 0xa1, 0x41, 0x42, 0x42, 0x8, + 0x84, 0x20, 0x88, 0x80, 0x80, + + /* U+6063 "恣" */ + 0x1, 0x0, 0x41, 0x0, 0x23, 0xfe, 0x4, 0x44, + 0xc, 0x44, 0x20, 0xa0, 0x21, 0x90, 0x42, 0x8, + 0x9c, 0x6, 0x1, 0x80, 0x48, 0x84, 0x48, 0x16, + 0x48, 0x12, 0x88, 0x10, 0xf, 0xe0, + + /* U+6064 "恤" */ + 0x20, 0x40, 0x41, 0x0, 0x9f, 0xf1, 0xa5, 0x2a, + 0xca, 0x54, 0x94, 0xa9, 0x29, 0x12, 0x52, 0x24, + 0xa4, 0x49, 0x48, 0x92, 0x91, 0x25, 0x22, 0x4a, + 0x45, 0xff, 0xc8, 0x0, 0x0, + + /* U+6067 "恧" */ + 0xff, 0xfc, 0x10, 0x7, 0xff, 0x91, 0x22, 0x44, + 0x89, 0x12, 0x24, 0x48, 0x90, 0xe, 0x2, 0x0, + 0x44, 0x5, 0x10, 0xa4, 0x9, 0x90, 0x20, 0x3f, + 0x0, + + /* U+6068 "恨" */ + 0x23, 0xfc, 0x44, 0x8, 0x88, 0x11, 0x90, 0x2a, + 0xbf, 0xd5, 0x40, 0xa8, 0x81, 0x11, 0xfe, 0x22, + 0x40, 0x44, 0x4c, 0x88, 0xa1, 0x10, 0x82, 0x25, + 0x84, 0x70, 0x88, 0x80, 0x80, + + /* U+6069 "恩" */ + 0x3f, 0xfc, 0x21, 0x4, 0x2f, 0xf4, 0x21, 0x4, + 0x21, 0xc4, 0x2e, 0x34, 0x20, 0x4, 0x3f, 0xfc, + 0x0, 0x0, 0x1, 0x4, 0x28, 0x84, 0x48, 0x12, + 0x88, 0x10, 0x7, 0xe0, + + /* U+606A "恪" */ + 0x20, 0x80, 0x43, 0xf8, 0xc4, 0x15, 0x58, 0x4a, + 0x49, 0x14, 0xc, 0x28, 0x3c, 0x11, 0x86, 0x24, + 0x0, 0x47, 0xf8, 0x88, 0x11, 0x10, 0x22, 0x20, + 0x44, 0x7f, 0x88, 0x81, 0x0, + + /* U+606B "恫" */ + 0x20, 0x0, 0x4f, 0xfc, 0x90, 0xd, 0xaf, 0x9a, + 0xc0, 0x34, 0x80, 0x69, 0x38, 0x92, 0x91, 0x25, + 0x22, 0x4a, 0x44, 0x94, 0x89, 0x27, 0x12, 0x40, + 0x24, 0x80, 0x49, 0xf, 0x0, + + /* U+606C "恬" */ + 0x20, 0x0, 0x4f, 0xf8, 0x81, 0x5, 0xc2, 0xa, + 0x84, 0x14, 0xff, 0xe8, 0x10, 0x10, 0x20, 0x23, + 0xfc, 0x44, 0x8, 0x88, 0x11, 0x10, 0x22, 0x20, + 0x44, 0x7f, 0x88, 0x81, 0x0, + + /* U+606D "恭" */ + 0x0, 0x20, 0x10, 0x40, 0x20, 0x83, 0xff, 0xe0, + 0x82, 0x1, 0x4, 0x3f, 0xff, 0x82, 0x20, 0x8, + 0x20, 0x22, 0x21, 0x84, 0x34, 0x4a, 0x51, 0x14, + 0x84, 0x24, 0x88, 0x49, 0x7, 0x0, + + /* U+606F "息" */ + 0x0, 0x0, 0x4, 0x0, 0xff, 0xe1, 0x0, 0x42, + 0x0, 0x87, 0xff, 0x8, 0x2, 0x1f, 0xfc, 0x20, + 0x8, 0x7f, 0xf0, 0x0, 0x0, 0x8, 0x4, 0x88, + 0x51, 0x2, 0x62, 0x4, 0x3, 0xf0, + + /* U+6070 "恰" */ + 0x20, 0x40, 0x41, 0x40, 0xc6, 0x45, 0x58, 0x4a, + 0x60, 0x54, 0x3e, 0x8, 0x0, 0x10, 0x0, 0x23, + 0xf8, 0x44, 0x10, 0x88, 0x21, 0x10, 0x42, 0x20, + 0x84, 0x7f, 0x8, 0x82, 0x0, + + /* U+6073 "恳" */ + 0x3f, 0xf8, 0x40, 0x10, 0xff, 0xe1, 0x0, 0x43, + 0xff, 0x84, 0x40, 0x88, 0x4e, 0x12, 0x60, 0x38, + 0x3e, 0x2, 0x1, 0x22, 0x12, 0x40, 0x98, 0x81, + 0x21, 0xfc, 0x0, + + /* U+6076 "恶" */ + 0x7f, 0xfc, 0x8, 0x81, 0x11, 0x11, 0x22, 0x41, + 0x45, 0x0, 0x88, 0x3f, 0xff, 0x80, 0x0, 0x3, + 0x0, 0x92, 0x11, 0x22, 0x14, 0x40, 0x98, 0x82, + 0x1, 0xfc, 0x0, + + /* U+6078 "恸" */ + 0x20, 0x10, 0x4e, 0x20, 0xc0, 0x45, 0x41, 0xfa, + 0x1, 0x35, 0xf2, 0x68, 0x84, 0x92, 0x9, 0x25, + 0x12, 0x49, 0x24, 0xa2, 0x89, 0x7f, 0x12, 0x2, + 0x24, 0x8, 0x48, 0x27, 0x0, + + /* U+6079 "恹" */ + 0x20, 0x0, 0x4f, 0xfc, 0x90, 0x1, 0x21, 0x4b, + 0x42, 0x55, 0x84, 0x29, 0x7f, 0xd2, 0x10, 0x24, + 0x20, 0x48, 0xa0, 0x91, 0x41, 0x24, 0x82, 0x88, + 0x85, 0x60, 0x88, 0x80, 0x0, + + /* U+607A "恺" */ + 0x20, 0x40, 0x48, 0x88, 0x91, 0x11, 0xbf, 0xea, + 0x80, 0x14, 0xff, 0xa8, 0x1, 0x10, 0x2, 0x20, + 0x4, 0x4f, 0xf8, 0x90, 0x1, 0x20, 0x2, 0x40, + 0x24, 0x80, 0x48, 0xff, 0x0, + + /* U+607B "恻" */ + 0x20, 0x2, 0x4f, 0xa4, 0x91, 0x4d, 0xaa, 0x9a, + 0xd5, 0x34, 0xaa, 0x69, 0x54, 0x92, 0xa9, 0x25, + 0x52, 0x4a, 0xa4, 0x88, 0x49, 0x1c, 0x12, 0x2c, + 0x24, 0x88, 0x4a, 0x7, 0x80, + + /* U+607C "恼" */ + 0x20, 0x40, 0x40, 0x40, 0x9f, 0xfd, 0x80, 0xa, + 0x91, 0x14, 0x92, 0xa9, 0x19, 0x12, 0x32, 0x24, + 0x64, 0x48, 0xc8, 0x92, 0x51, 0x28, 0x22, 0x40, + 0x44, 0xff, 0x88, 0x1, 0x0, + + /* U+607D "恽" */ + 0x20, 0x0, 0x4f, 0xfc, 0x90, 0x9, 0x24, 0x1a, + 0xff, 0xf4, 0x20, 0x28, 0x90, 0x12, 0x20, 0x27, + 0xfc, 0x40, 0x80, 0x81, 0x1, 0x3f, 0xf2, 0x4, + 0x4, 0x8, 0x8, 0x10, 0x0, + + /* U+607F "恿" */ + 0x7f, 0xf8, 0x20, 0x60, 0x33, 0x3, 0xff, 0xc4, + 0x20, 0x8f, 0xff, 0x10, 0x82, 0x3f, 0xfc, 0x42, + 0x8, 0x80, 0x70, 0x48, 0x2, 0x88, 0x45, 0x2, + 0x52, 0x4, 0x43, 0xf0, 0x0, + + /* U+6083 "悃" */ + 0x20, 0x0, 0x4f, 0xfc, 0x90, 0x9, 0xa2, 0x1a, + 0x5f, 0xb4, 0x88, 0x69, 0x10, 0x92, 0x79, 0x24, + 0xca, 0x4a, 0x84, 0x91, 0x9, 0x22, 0x12, 0x40, + 0x24, 0xff, 0xc9, 0x0, 0x80, + + /* U+6084 "悄" */ + 0x20, 0x40, 0x89, 0x12, 0x14, 0xaa, 0x10, 0xab, + 0xfe, 0x88, 0x1a, 0x20, 0x48, 0xff, 0x22, 0x4, + 0x88, 0x12, 0x3f, 0xc8, 0x81, 0x22, 0x4, 0x88, + 0x12, 0x23, 0xc0, + + /* U+6089 "悉" */ + 0x7f, 0xfc, 0x4, 0x0, 0xc8, 0xc0, 0x10, 0xf, + 0xff, 0xe0, 0xd0, 0x2, 0x90, 0x19, 0x1c, 0xc0, + 0x6, 0x24, 0x1, 0x44, 0x32, 0x81, 0x29, 0x2, + 0x1, 0xf8, 0x0, + + /* U+608C "悌" */ + 0x21, 0x10, 0x43, 0x20, 0x9f, 0xf1, 0x82, 0x2a, + 0x84, 0x54, 0xff, 0xa9, 0x10, 0x12, 0x20, 0x27, + 0xfe, 0x40, 0x84, 0x83, 0x9, 0xa, 0x12, 0x65, + 0xc5, 0x8, 0x8, 0x10, 0x0, + + /* U+608D "悍" */ + 0x20, 0x0, 0x4f, 0xf8, 0x90, 0x11, 0xbf, 0xea, + 0xc0, 0x55, 0x80, 0xa9, 0xff, 0x10, 0x0, 0x27, + 0xfc, 0x40, 0x80, 0x81, 0x1, 0x7f, 0xf2, 0x4, + 0x4, 0x8, 0x8, 0x10, 0x0, + + /* U+6092 "悒" */ + 0x23, 0xfc, 0x44, 0x8, 0xc8, 0x15, 0x9f, 0xea, + 0x80, 0x14, 0x0, 0x29, 0xff, 0x12, 0x22, 0x24, + 0x44, 0x4f, 0xf8, 0x90, 0x1, 0x20, 0x12, 0x40, + 0x24, 0xff, 0x88, 0x0, 0x0, + + /* U+6094 "悔" */ + 0x21, 0x0, 0x47, 0xfc, 0x88, 0x1, 0xa0, 0xa, + 0xbf, 0xd4, 0x40, 0xa8, 0x99, 0x51, 0x2, 0x2f, + 0xfe, 0x44, 0x8, 0x91, 0x91, 0x3f, 0xf2, 0x0, + 0x84, 0x1, 0x8, 0x1e, 0x0, + + /* U+6096 "悖" */ + 0x20, 0x40, 0x4f, 0xf8, 0x81, 0x5, 0x82, 0xb, + 0x7f, 0xd4, 0x80, 0xa9, 0x7e, 0x10, 0x8, 0x20, + 0x20, 0x40, 0x80, 0xbf, 0xf9, 0x2, 0x2, 0x4, + 0x4, 0x8, 0x8, 0x70, 0x0, + + /* U+609A "悚" */ + 0x20, 0x40, 0x40, 0x80, 0x9f, 0xf9, 0x82, 0xa, + 0xbf, 0xd4, 0x48, 0xa8, 0x91, 0x11, 0x22, 0x23, + 0xfc, 0x41, 0x40, 0x83, 0x41, 0xa, 0x42, 0x64, + 0x45, 0x88, 0x48, 0x10, 0x0, + + /* U+609B "悛" */ + 0x20, 0x40, 0x41, 0x20, 0xcc, 0x25, 0x7f, 0xea, + 0x80, 0x34, 0x23, 0x29, 0x81, 0x90, 0x40, 0x21, + 0xf8, 0x46, 0x10, 0x94, 0x41, 0x5, 0x82, 0x6, + 0x4, 0x73, 0x8b, 0x0, 0x80, + + /* U+609D "悝" */ + 0x20, 0x0, 0x47, 0xf8, 0x89, 0x11, 0x92, 0x2a, + 0xbf, 0xd4, 0x48, 0xa8, 0x91, 0x11, 0xfe, 0x20, + 0x40, 0x40, 0x80, 0x8f, 0xf1, 0x2, 0x2, 0x4, + 0x4, 0xff, 0xc8, 0x0, 0x0, + + /* U+609F "悟" */ + 0x20, 0x0, 0x4f, 0xf8, 0x84, 0x1, 0x88, 0xa, + 0xff, 0x94, 0x21, 0x28, 0x42, 0x13, 0xff, 0x20, + 0x0, 0x4f, 0xf8, 0x90, 0x11, 0x20, 0x22, 0x40, + 0x44, 0xff, 0x89, 0x1, 0x0, + + /* U+60A0 "悠" */ + 0x10, 0x0, 0x20, 0x80, 0x93, 0xfb, 0x24, 0x46, + 0x54, 0x94, 0x8a, 0x9, 0xc, 0x12, 0x64, 0x21, + 0x6, 0x0, 0x1, 0x24, 0x12, 0x44, 0x94, 0x81, + 0x31, 0x2, 0x3, 0xf8, 0x0, + + /* U+60A3 "患" */ + 0x1, 0x0, 0xff, 0xf1, 0x4, 0x22, 0x8, 0x47, + 0xff, 0x80, 0x20, 0x1f, 0xfe, 0x20, 0x84, 0x7f, + 0xf8, 0x2, 0x0, 0x8, 0x2, 0x88, 0x49, 0xa, + 0x52, 0x4, 0x43, 0xf0, 0x0, + + /* U+60A6 "悦" */ + 0x22, 0x8, 0x42, 0x20, 0x84, 0x81, 0xbf, 0xeb, + 0x40, 0x55, 0x80, 0xa9, 0x1, 0x12, 0x2, 0x27, + 0xfc, 0x42, 0x40, 0x84, 0x81, 0x9, 0x12, 0x22, + 0x24, 0xc4, 0x4a, 0xf, 0x80, + + /* U+60A8 "您" */ + 0x1, 0x0, 0x24, 0x0, 0x8f, 0xf1, 0x20, 0x26, + 0x44, 0x54, 0x8, 0x8, 0x94, 0x12, 0x26, 0x24, + 0x46, 0x43, 0x80, 0x0, 0x0, 0x10, 0x44, 0x90, + 0x49, 0x4, 0xe2, 0x8, 0x7, 0xf0, + + /* U+60AB "悫" */ + 0x0, 0x0, 0x1, 0x0, 0x7f, 0xfe, 0x1, 0x0, + 0x3f, 0xfc, 0x0, 0x0, 0x7f, 0xfe, 0x40, 0x2, + 0x4f, 0xe2, 0x8, 0x22, 0x8, 0x3e, 0x71, 0x0, + 0x8, 0x84, 0x48, 0x12, 0x48, 0x13, 0x7, 0xe0, + + /* U+60AC "悬" */ + 0x3f, 0xf8, 0x40, 0x10, 0xff, 0xe1, 0x0, 0x43, + 0xff, 0x84, 0x1, 0x8, 0x2, 0x7f, 0xff, 0x10, + 0x20, 0xff, 0xf0, 0x0, 0x12, 0x48, 0x4, 0x82, + 0x51, 0x4, 0x43, 0xf8, 0x0, + + /* U+60AD "悭" */ + 0x21, 0x0, 0x4a, 0xf8, 0x95, 0x11, 0xa9, 0x4a, + 0xd2, 0x94, 0xa2, 0x29, 0x5b, 0x10, 0x80, 0x20, + 0x40, 0x40, 0x80, 0x9f, 0xf1, 0x2, 0x2, 0x4, + 0x5, 0xff, 0xc8, 0x0, 0x0, + + /* U+60AF "悯" */ + 0x22, 0x0, 0x42, 0x7c, 0x84, 0x9, 0xa2, 0x1b, + 0x42, 0x34, 0xbf, 0x69, 0x4, 0xd2, 0x49, 0x24, + 0x62, 0x48, 0x44, 0x91, 0x49, 0x24, 0x92, 0x50, + 0x24, 0x80, 0x49, 0x7, 0x0, + + /* U+60B1 "悱" */ + 0x21, 0x20, 0x42, 0x40, 0x84, 0x81, 0x79, 0xeb, + 0x12, 0x15, 0x24, 0x28, 0x48, 0x57, 0x9e, 0x21, + 0x20, 0x42, 0x40, 0xbc, 0xf9, 0x9, 0x2, 0x12, + 0x4, 0x24, 0x8, 0x48, 0x0, + + /* U+60B2 "悲" */ + 0x8, 0x80, 0x22, 0xf, 0x8f, 0xc2, 0x20, 0xf8, + 0xfc, 0x22, 0x0, 0x88, 0x3e, 0x3f, 0x8, 0x80, + 0x0, 0x5, 0x20, 0xa4, 0x42, 0x90, 0x24, 0x40, + 0x80, 0xfc, 0x0, + + /* U+60B4 "悴" */ + 0x20, 0x40, 0x40, 0x80, 0x9f, 0xf1, 0x80, 0xa, + 0x91, 0x14, 0x62, 0x28, 0xc6, 0x52, 0x52, 0x24, + 0x4, 0x40, 0x80, 0xbf, 0xf9, 0x2, 0x2, 0x4, + 0x4, 0x8, 0x8, 0x10, 0x0, + + /* U+60B8 "悸" */ + 0x23, 0xfc, 0x40, 0x80, 0x81, 0x1, 0xbf, 0xfa, + 0x8e, 0x14, 0x2b, 0x29, 0x81, 0x90, 0xfc, 0x20, + 0x18, 0x40, 0x40, 0xbf, 0xf9, 0x1, 0x2, 0x2, + 0x4, 0x4, 0x8, 0x70, 0x0, + + /* U+60BB "悻" */ + 0x20, 0x40, 0x40, 0x80, 0x8f, 0xf1, 0x42, 0xa, + 0x44, 0x14, 0xff, 0xe8, 0x42, 0x10, 0x48, 0x27, + 0xfe, 0x40, 0x80, 0x81, 0x1, 0x7f, 0xf2, 0x4, + 0x4, 0x8, 0x8, 0x10, 0x0, + + /* U+60BC "悼" */ + 0x20, 0x40, 0x40, 0xfc, 0x81, 0x1, 0x82, 0xa, + 0xbf, 0xd4, 0x40, 0xa8, 0xff, 0x11, 0x2, 0x22, + 0x4, 0x47, 0xf8, 0x81, 0x1, 0x3f, 0xf2, 0x4, + 0x4, 0x8, 0x8, 0x10, 0x0, + + /* U+60C5 "情" */ + 0x20, 0x40, 0x5f, 0xfc, 0x81, 0x5, 0xbf, 0xea, + 0x4, 0x15, 0xff, 0xe8, 0x0, 0x13, 0xfe, 0x24, + 0x4, 0x4f, 0xf8, 0x90, 0x11, 0x3f, 0xe2, 0x40, + 0x44, 0x80, 0x89, 0xe, 0x0, + + /* U+60C6 "惆" */ + 0x20, 0x0, 0x9f, 0xf2, 0x44, 0x6d, 0x7d, 0xac, + 0x46, 0x91, 0x1a, 0x5f, 0xc9, 0x1, 0x24, 0xf4, + 0x94, 0x52, 0x51, 0x49, 0x3d, 0x28, 0x4, 0xa0, + 0x12, 0x83, 0xc0, + + /* U+60CA "惊" */ + 0x20, 0x40, 0x40, 0x80, 0x9f, 0xf9, 0x80, 0xa, + 0xbf, 0xd4, 0x40, 0xa8, 0x81, 0x11, 0x2, 0x23, + 0xfc, 0x40, 0x80, 0x85, 0x21, 0x12, 0x42, 0x44, + 0x45, 0x88, 0x48, 0x70, 0x0, + + /* U+60CB "惋" */ + 0x20, 0x0, 0x40, 0x40, 0x9f, 0xf9, 0xa0, 0x1b, + 0x10, 0x14, 0x7b, 0xa8, 0x99, 0x51, 0x32, 0x26, + 0x64, 0x53, 0x48, 0x82, 0xb1, 0x5, 0x2, 0x12, + 0x24, 0x44, 0x4b, 0x7, 0x0, + + /* U+60D1 "惑" */ + 0x0, 0x90, 0x2, 0x2f, 0xff, 0xc0, 0x20, 0x7e, + 0x49, 0x9, 0x47, 0xe6, 0x40, 0x19, 0xfe, 0xa4, + 0x0, 0x60, 0x20, 0x14, 0x42, 0x90, 0x26, 0x40, + 0x90, 0xfc, 0x0, + + /* U+60D5 "惕" */ + 0x23, 0xfc, 0x44, 0x8, 0x88, 0x11, 0x5f, 0xea, + 0xa0, 0x54, 0x7f, 0xa8, 0x0, 0x11, 0x0, 0x23, + 0xfe, 0x49, 0x24, 0xa2, 0x49, 0x9, 0x12, 0x22, + 0x25, 0x88, 0xc8, 0x27, 0x0, + + /* U+60D8 "惘" */ + 0x20, 0x0, 0x4f, 0xfc, 0x92, 0xd, 0xa0, 0x9a, + 0xdf, 0xb4, 0x88, 0x69, 0x8, 0x92, 0xfd, 0x24, + 0x82, 0x49, 0x4, 0x92, 0x9, 0x27, 0xd2, 0x40, + 0x24, 0x80, 0x49, 0x3, 0x80, + + /* U+60DA "惚" */ + 0x21, 0x0, 0x42, 0x0, 0x8f, 0xf1, 0x94, 0xaa, + 0x89, 0x54, 0x24, 0xa8, 0xc9, 0x13, 0x22, 0x20, + 0x18, 0x40, 0x80, 0x95, 0x1, 0x28, 0x22, 0x91, + 0x45, 0x22, 0x8, 0x7c, 0x0, + + /* U+60DC "惜" */ + 0x21, 0x10, 0x42, 0x20, 0x9f, 0xfd, 0xc8, 0x8a, + 0x11, 0x14, 0xff, 0xe8, 0x0, 0x10, 0x0, 0x23, + 0xfc, 0x44, 0x8, 0x8f, 0xf1, 0x10, 0x22, 0x20, + 0x44, 0x7f, 0x88, 0x81, 0x0, + + /* U+60DD "惝" */ + 0x20, 0x40, 0x91, 0x13, 0x24, 0xaa, 0x10, 0xa7, + 0xfe, 0x90, 0x1a, 0x40, 0x49, 0x7d, 0x25, 0x14, + 0x94, 0x52, 0x51, 0x49, 0x7d, 0x24, 0x4, 0x90, + 0x12, 0x41, 0xc0, + + /* U+60DF "惟" */ + 0x21, 0x40, 0x21, 0x20, 0x23, 0x10, 0x33, 0xfe, + 0xae, 0x20, 0xae, 0x20, 0xab, 0xfe, 0x22, 0x20, + 0x22, 0x20, 0x23, 0xfe, 0x22, 0x20, 0x22, 0x20, + 0x22, 0x20, 0x23, 0xff, 0x22, 0x0, + + /* U+60E0 "惠" */ + 0x2, 0x3, 0xff, 0xf0, 0x20, 0x1f, 0xfe, 0x42, + 0x9, 0xff, 0xe4, 0x20, 0x9f, 0xfe, 0x2, 0x13, + 0xff, 0xe0, 0x0, 0x54, 0xc2, 0x91, 0x2a, 0x40, + 0x90, 0xfc, 0x0, + + /* U+60E6 "惦" */ + 0x20, 0x20, 0x40, 0x40, 0x9f, 0xfd, 0xa1, 0xb, + 0x42, 0x14, 0x87, 0xe9, 0x8, 0x12, 0x10, 0x25, + 0xfc, 0x4a, 0x8, 0x94, 0x11, 0x28, 0x22, 0x90, + 0x45, 0x3f, 0x88, 0x41, 0x0, + + /* U+60E7 "惧" */ + 0x23, 0xf8, 0x44, 0x10, 0x88, 0x25, 0x5f, 0xca, + 0xa0, 0x94, 0x7f, 0x28, 0x82, 0x11, 0x4, 0x23, + 0xf8, 0x44, 0x10, 0xbf, 0xf9, 0x0, 0x2, 0x11, + 0x4, 0x41, 0xb, 0x1, 0x0, + + /* U+60E8 "惨" */ + 0x20, 0x40, 0x41, 0x20, 0x8c, 0x21, 0xdf, 0xea, + 0x84, 0x14, 0xff, 0xe8, 0x64, 0x11, 0x4, 0x2c, + 0xc6, 0x46, 0x20, 0x81, 0x81, 0xc, 0x62, 0x23, + 0x4, 0x18, 0x9, 0x80, 0x0, + + /* U+60E9 "惩" */ + 0x10, 0x0, 0xdf, 0xf4, 0x4, 0x0, 0x10, 0x12, + 0x7c, 0x89, 0xe, 0x24, 0x8, 0x90, 0x2f, 0xfc, + 0x80, 0x0, 0x30, 0x14, 0x4a, 0x90, 0x26, 0x20, + 0x80, 0xfc, 0x0, + + /* U+60EB "惫" */ + 0x8, 0x0, 0x3f, 0xf1, 0xa0, 0xc0, 0x3e, 0xf, + 0xc7, 0x67, 0xff, 0x8, 0x42, 0x1f, 0xfc, 0x21, + 0x8, 0x7f, 0xf0, 0x0, 0x2, 0x48, 0x24, 0x89, + 0x51, 0x4, 0x43, 0xf8, 0x0, + + /* U+60EC "惬" */ + 0x27, 0xfe, 0x48, 0x0, 0x90, 0x85, 0xaf, 0xfa, + 0xc2, 0x14, 0x94, 0xa9, 0x2b, 0x12, 0x10, 0x25, + 0xfe, 0x48, 0x40, 0x90, 0xe1, 0x22, 0x62, 0x58, + 0x24, 0x80, 0x9, 0xff, 0x80, + + /* U+60ED "惭" */ + 0x22, 0x0, 0x48, 0x78, 0xbe, 0x81, 0xa1, 0xa, + 0x62, 0x15, 0x44, 0x2a, 0x8f, 0xd7, 0xd2, 0x22, + 0x24, 0x44, 0x48, 0x9e, 0x91, 0x51, 0x22, 0x24, + 0x44, 0x48, 0x88, 0xa1, 0x0, + + /* U+60EE "惮" */ + 0x21, 0x8, 0x41, 0x20, 0x9f, 0xf5, 0xa2, 0x2a, + 0xc4, 0x54, 0xff, 0xa9, 0x11, 0x12, 0x22, 0x27, + 0xfc, 0x40, 0x80, 0x81, 0x1, 0x7f, 0xf2, 0x4, + 0x4, 0x8, 0x8, 0x10, 0x0, + + /* U+60EF "惯" */ + 0x23, 0xfc, 0x44, 0x88, 0x89, 0x11, 0xbf, 0xfa, + 0xa4, 0x55, 0x48, 0xa8, 0xff, 0x50, 0x0, 0x23, + 0xfc, 0x48, 0x8, 0x91, 0x11, 0x22, 0x22, 0x6, + 0x4, 0x33, 0x8b, 0x80, 0x80, + + /* U+60F0 "惰" */ + 0x20, 0x80, 0x4f, 0xfc, 0xc4, 0x5, 0x5f, 0xea, + 0x42, 0x15, 0x7f, 0xe8, 0x0, 0x11, 0xfe, 0x22, + 0x4, 0x47, 0xf8, 0x88, 0x11, 0x1f, 0xe2, 0x20, + 0x44, 0x40, 0x88, 0x87, 0x0, + + /* U+60F3 "想" */ + 0x10, 0x0, 0x23, 0xfb, 0xf4, 0x11, 0x8f, 0xe3, + 0x10, 0x4b, 0x20, 0xa4, 0x7f, 0x48, 0x82, 0x11, + 0xfc, 0x0, 0x0, 0x24, 0x2, 0x44, 0x24, 0x81, + 0x71, 0x4, 0x43, 0xf8, 0x0, + + /* U+60F4 "惴" */ + 0x20, 0x40, 0x48, 0x88, 0xd1, 0x11, 0xbf, 0xea, + 0x0, 0x14, 0x0, 0x2b, 0xff, 0x90, 0x40, 0x27, + 0xfc, 0x52, 0x48, 0xa4, 0x91, 0x49, 0x22, 0x92, + 0x45, 0x4, 0x8a, 0x7, 0x0, + + /* U+60F6 "惶" */ + 0x20, 0x80, 0x4f, 0xf8, 0xd0, 0x11, 0xa0, 0x2a, + 0x7f, 0xd4, 0x80, 0xa9, 0xff, 0x10, 0x0, 0x27, + 0xfc, 0x40, 0x80, 0x81, 0x1, 0x3f, 0xe2, 0x4, + 0x5, 0xff, 0xc8, 0x0, 0x0, + + /* U+60F9 "惹" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x0, + 0x20, 0x1f, 0xff, 0xc1, 0x0, 0xf, 0xfc, 0x70, + 0x9, 0x20, 0x10, 0x7f, 0xe0, 0x0, 0x0, 0x90, + 0x49, 0x12, 0xb2, 0x4, 0x83, 0xf0, + + /* U+60FA "惺" */ + 0x27, 0xfc, 0x48, 0x8, 0x90, 0x11, 0xbf, 0xea, + 0xc0, 0x54, 0xff, 0xa8, 0x90, 0x11, 0x20, 0x27, + 0xfe, 0x48, 0x80, 0xa1, 0x1, 0x3f, 0xe2, 0x4, + 0x5, 0xff, 0xc8, 0x0, 0x0, + + /* U+6100 "愀" */ + 0x20, 0x10, 0x4f, 0x20, 0xc4, 0x45, 0x4a, 0x9a, + 0x7d, 0x54, 0x2a, 0xa8, 0xc4, 0x11, 0x88, 0x25, + 0xb0, 0x4a, 0x50, 0xa4, 0xa1, 0x49, 0x42, 0x14, + 0x44, 0x28, 0x88, 0x40, 0x0, + + /* U+6101 "愁" */ + 0x0, 0x21, 0xf8, 0x40, 0x40, 0x80, 0x89, 0x2f, + 0xda, 0x86, 0xc, 0x16, 0x14, 0x2a, 0x44, 0x91, + 0x4, 0x24, 0x4, 0x4, 0x2, 0x84, 0x25, 0x1, + 0x31, 0x4, 0x43, 0xf8, 0x0, + + /* U+6106 "愆" */ + 0x9, 0x0, 0x11, 0x9e, 0x20, 0x0, 0x4a, 0x0, + 0x19, 0x3f, 0x30, 0x4, 0x50, 0x84, 0x13, 0x4, + 0x10, 0x18, 0x0, 0x80, 0x4, 0xc0, 0x24, 0x42, + 0x44, 0x9, 0x44, 0x10, 0x7, 0xf0, + + /* U+6108 "愈" */ + 0x1, 0x80, 0x2, 0x40, 0xc, 0x38, 0x77, 0xe7, + 0x0, 0x0, 0x3f, 0x4, 0x21, 0x24, 0x3f, 0x24, + 0x21, 0x24, 0x3f, 0x4, 0x27, 0x1c, 0x1, 0x0, + 0x9, 0x80, 0x28, 0x8a, 0x48, 0x11, 0x7, 0xf0, + + /* U+6109 "愉" */ + 0x20, 0x40, 0x41, 0x40, 0xc6, 0x41, 0x58, 0xca, + 0x7f, 0x55, 0x0, 0x69, 0xe1, 0x52, 0x52, 0x24, + 0xa4, 0x4f, 0x48, 0x92, 0x91, 0x3d, 0x22, 0x48, + 0x44, 0x90, 0x89, 0x67, 0x0, + + /* U+610D "愍" */ + 0x0, 0x20, 0x7f, 0x20, 0x41, 0x3f, 0x7f, 0x44, + 0x48, 0xa4, 0x7f, 0x14, 0x48, 0x8, 0x45, 0x1c, + 0x73, 0x63, 0x0, 0x0, 0x9, 0x0, 0x48, 0x94, + 0x48, 0x12, 0x88, 0x10, 0xf, 0xe0, + + /* U+610E "愎" */ + 0x21, 0x0, 0x47, 0xfc, 0x88, 0x5, 0xbf, 0xea, + 0xa0, 0x54, 0x7f, 0xa8, 0x81, 0x11, 0xfe, 0x20, + 0x80, 0x43, 0xf8, 0x8c, 0x31, 0x64, 0xc2, 0x6, + 0x4, 0x33, 0xb, 0x81, 0x80, + + /* U+610F "意" */ + 0x1, 0x0, 0x2, 0x1, 0xff, 0xf0, 0x41, 0xf, + 0xff, 0xe0, 0x0, 0xf, 0xfe, 0x10, 0x4, 0x3f, + 0xf8, 0x40, 0x10, 0xff, 0xe0, 0x0, 0x0, 0x90, + 0x9, 0x12, 0xb2, 0x8, 0x87, 0xf0, + + /* U+6115 "愕" */ + 0x27, 0xbc, 0x49, 0x48, 0x92, 0x91, 0xa5, 0x2b, + 0x7b, 0xd4, 0x0, 0x29, 0xff, 0x10, 0x0, 0x2f, + 0xfe, 0x44, 0x0, 0x8f, 0xf1, 0x0, 0x22, 0x0, + 0x84, 0x1, 0x8, 0x3e, 0x0, + + /* U+611A "愚" */ + 0x3f, 0xf8, 0x42, 0x10, 0xff, 0xe1, 0x8, 0x43, + 0xff, 0x80, 0x20, 0x1f, 0xff, 0x20, 0x92, 0x41, + 0x24, 0xbd, 0xa8, 0x8, 0x70, 0x48, 0x4, 0x82, + 0x51, 0x4, 0x43, 0xf8, 0x0, + + /* U+611F "感" */ + 0x0, 0x28, 0x0, 0x24, 0x3f, 0xfe, 0x20, 0x20, + 0x2f, 0xa4, 0x20, 0x24, 0x2f, 0xa8, 0x28, 0x91, + 0x4f, 0xb3, 0x40, 0x4e, 0x1, 0x0, 0x28, 0x84, + 0x48, 0x12, 0x88, 0x13, 0x7, 0xe0, + + /* U+6120 "愠" */ + 0x20, 0x0, 0x4f, 0xf0, 0x90, 0x25, 0xbf, 0xcb, + 0x40, 0x94, 0x81, 0x29, 0xfe, 0x10, 0x0, 0x2f, + 0xfc, 0x52, 0x48, 0xa4, 0x91, 0x49, 0x22, 0x92, + 0x47, 0xff, 0xc8, 0x0, 0x0, + + /* U+6123 "愣" */ + 0x27, 0xfe, 0x49, 0x24, 0x92, 0x4d, 0xa4, 0x9b, + 0x7f, 0xf4, 0x0, 0x28, 0x8, 0x17, 0xff, 0x21, + 0x0, 0x43, 0xf8, 0x84, 0x11, 0x10, 0x22, 0x20, + 0x44, 0x80, 0x8a, 0xe, 0x0, + + /* U+6124 "愤" */ + 0x20, 0x40, 0x4f, 0xf8, 0xc1, 0x1, 0x10, 0x8a, + 0xff, 0xf4, 0x42, 0x28, 0x0, 0x13, 0xfe, 0x24, + 0x4, 0x48, 0x88, 0x91, 0x11, 0x22, 0x22, 0xb, + 0x4, 0x61, 0x8b, 0x0, 0x80, + + /* U+6126 "愦" */ + 0x20, 0x40, 0x4f, 0xf8, 0xd1, 0x11, 0xa2, 0x2a, + 0x7f, 0xd4, 0x8, 0x2b, 0xff, 0x90, 0x0, 0x27, + 0xfc, 0x48, 0x8, 0x91, 0x11, 0x22, 0x22, 0xe, + 0x4, 0x23, 0xb, 0x81, 0x0, + + /* U+6127 "愧" */ + 0x20, 0x80, 0x4f, 0xf8, 0xd1, 0x15, 0xe2, 0x2a, + 0x7f, 0xd4, 0x88, 0xa9, 0x11, 0x13, 0xfe, 0x20, + 0x80, 0x41, 0xa0, 0x87, 0xa1, 0xb, 0xd2, 0x24, + 0x24, 0x88, 0x4a, 0xf, 0x0, + + /* U+612B "愫" */ + 0x20, 0x40, 0x4f, 0xfc, 0x81, 0x5, 0xbf, 0xea, + 0x4, 0x15, 0xff, 0xe8, 0x20, 0x11, 0x8c, 0x27, + 0xe0, 0x42, 0x10, 0xbf, 0xf1, 0xa, 0x2, 0x24, + 0xc5, 0x88, 0x48, 0x70, 0x0, + + /* U+613F "愿" */ + 0x3f, 0xff, 0x21, 0x0, 0x21, 0x0, 0x27, 0xfc, + 0x24, 0x4, 0x27, 0xfc, 0x27, 0xfc, 0x20, 0x40, + 0x24, 0x4c, 0x29, 0xc2, 0x20, 0x0, 0x2a, 0x44, + 0x4a, 0x4a, 0x52, 0x10, 0x81, 0xf0, + + /* U+6148 "慈" */ + 0x8, 0x20, 0x8, 0x83, 0xff, 0xf8, 0x81, 0x2, + 0x2c, 0x8f, 0x9e, 0x4, 0x8, 0x12, 0x24, 0x7e, + 0xfc, 0x4, 0x4, 0x0, 0x2, 0x48, 0x24, 0x89, + 0x51, 0x4, 0x43, 0xf8, 0x0, + + /* U+614A "慊" */ + 0x21, 0x10, 0x41, 0x20, 0x9f, 0xf9, 0x85, 0xa, + 0xbf, 0xd4, 0x14, 0xab, 0xff, 0x90, 0x52, 0x20, + 0xa4, 0x4f, 0xf8, 0x86, 0x41, 0x1d, 0x82, 0x6a, + 0x85, 0x14, 0xc8, 0x28, 0x0, + + /* U+614C "慌" */ + 0x21, 0x8, 0x4f, 0xfc, 0xc4, 0x25, 0x42, 0xa, + 0x7f, 0xf4, 0x40, 0x28, 0x80, 0x11, 0x0, 0x23, + 0xfc, 0x40, 0x0, 0x89, 0x21, 0x12, 0x42, 0x24, + 0xa4, 0x89, 0x4a, 0x11, 0x80, + + /* U+614E "慎" */ + 0x20, 0x40, 0x27, 0xfe, 0x20, 0x40, 0x33, 0xfc, + 0xaa, 0x4, 0xa3, 0xfc, 0xa2, 0x4, 0x23, 0xfc, + 0x22, 0x4, 0x23, 0xfc, 0x22, 0x4, 0x2f, 0xff, + 0x20, 0x10, 0x23, 0xc, 0x24, 0x2, + + /* U+6151 "慑" */ + 0x2f, 0xfe, 0x44, 0x10, 0x88, 0x21, 0x9f, 0xc3, + 0x20, 0x95, 0x7f, 0x28, 0x82, 0x17, 0xfe, 0x20, + 0x0, 0x5f, 0x78, 0x82, 0x91, 0x69, 0x22, 0x31, + 0x84, 0x93, 0xe, 0x19, 0x80, + + /* U+6155 "慕" */ + 0x4, 0x41, 0xff, 0xfc, 0x11, 0x1, 0xff, 0xc2, + 0x0, 0x87, 0xff, 0x8, 0x2, 0x1f, 0xfc, 0x2, + 0x1, 0xff, 0xfc, 0x20, 0x81, 0x88, 0xcd, 0x15, + 0x66, 0x29, 0x1, 0x80, 0x0, + + /* U+615D "慝" */ + 0x7f, 0xfc, 0x84, 0x41, 0x7f, 0xf2, 0x11, 0x5, + 0xff, 0xc8, 0x40, 0x13, 0xfc, 0x2a, 0x8, 0x47, + 0xf0, 0x80, 0x1, 0xff, 0xf0, 0x48, 0x4, 0x89, + 0x51, 0x4, 0x43, 0xf8, 0x0, + + /* U+6162 "慢" */ + 0x20, 0x0, 0x47, 0xf8, 0x8f, 0xf5, 0xd0, 0x2a, + 0xbf, 0xd4, 0x0, 0x29, 0xff, 0x92, 0x49, 0x27, + 0xfe, 0x40, 0x0, 0x8f, 0xf1, 0x8, 0x42, 0x9, + 0x4, 0x1e, 0xb, 0xc3, 0x80, + + /* U+6167 "慧" */ + 0x10, 0x21, 0xfd, 0xf8, 0x40, 0x83, 0xf7, 0xe1, + 0x2, 0xf, 0x8f, 0x1f, 0xfe, 0x0, 0x4, 0x7f, + 0xf8, 0x0, 0x11, 0xff, 0xe0, 0x0, 0x5, 0x10, + 0x51, 0x4, 0xe3, 0xf8, 0x0, + + /* U+6168 "慨" */ + 0x20, 0x0, 0x4f, 0x78, 0x92, 0x21, 0xa5, 0x4a, + 0xfa, 0x94, 0x95, 0x29, 0x2a, 0x13, 0xce, 0x24, + 0x10, 0x4a, 0x30, 0x92, 0x61, 0x2d, 0x42, 0x62, + 0xa4, 0x89, 0x48, 0x23, 0x80, + + /* U+6170 "慰" */ + 0x7f, 0x84, 0xc3, 0x9, 0x86, 0x13, 0xff, 0xf6, + 0x0, 0x4d, 0xc4, 0x9f, 0xf5, 0x6a, 0x2, 0xa5, + 0x84, 0x18, 0x30, 0x8, 0x2, 0x4c, 0x24, 0x89, + 0x51, 0x2, 0x43, 0xf8, 0x0, + + /* U+6175 "慵" */ + 0x20, 0x40, 0x4f, 0xfc, 0x90, 0x1, 0xaf, 0xea, + 0xc2, 0x54, 0x9f, 0xa9, 0x7f, 0x12, 0x10, 0x25, + 0xfc, 0x4a, 0x48, 0x97, 0xf1, 0x29, 0x22, 0x9f, + 0xc5, 0x24, 0x8a, 0x4b, 0x0, + + /* U+6177 "慷" */ + 0x27, 0xfe, 0x48, 0x0, 0xd0, 0x85, 0x7f, 0xea, + 0x42, 0x54, 0xff, 0xe9, 0x9, 0x13, 0xfe, 0x24, + 0x20, 0x4a, 0x68, 0x91, 0xe1, 0x4d, 0x62, 0xa2, + 0x27, 0x1c, 0x0, + + /* U+618B "憋" */ + 0x8, 0x10, 0x2a, 0x20, 0x8, 0x3e, 0x7f, 0x44, + 0x49, 0x64, 0x5d, 0x28, 0x6d, 0x18, 0x69, 0x18, + 0x49, 0x24, 0x4f, 0x42, 0x0, 0x0, 0x29, 0x84, + 0x48, 0x12, 0x48, 0x10, 0x7, 0xe0, + + /* U+618E "憎" */ + 0x21, 0x8, 0x42, 0x20, 0x9f, 0xf9, 0xa2, 0x1a, + 0xd5, 0xb4, 0x88, 0x69, 0xff, 0x90, 0x0, 0x23, + 0xfc, 0x44, 0x8, 0x88, 0x11, 0x1f, 0xe2, 0x20, + 0x44, 0x7f, 0x88, 0x81, 0x0, + + /* U+6194 "憔" */ + 0x21, 0x20, 0x21, 0x30, 0x33, 0xfe, 0x2a, 0x20, + 0xa6, 0x20, 0xab, 0xfe, 0xa2, 0x20, 0x23, 0xfe, + 0x22, 0x20, 0x22, 0x20, 0x23, 0xfe, 0x22, 0x0, + 0x22, 0x82, 0x24, 0x92, 0x24, 0x91, + + /* U+619D "憝" */ + 0x10, 0x41, 0xfe, 0x80, 0x1, 0xf3, 0xf4, 0x47, + 0xec, 0x80, 0x9, 0x3f, 0x8c, 0x4, 0x18, 0xd6, + 0xcc, 0x60, 0x0, 0x8, 0x2, 0x8, 0x45, 0x2, + 0x51, 0x4, 0x3, 0xf0, 0x0, + + /* U+61A7 "憧" */ + 0x20, 0x40, 0x4f, 0xf8, 0x8c, 0x25, 0x88, 0x8b, + 0xff, 0xf4, 0x0, 0x29, 0xff, 0x52, 0x22, 0x27, + 0xfc, 0x48, 0x88, 0x8f, 0xe1, 0x3f, 0xe2, 0x4, + 0x4, 0x8, 0xb, 0xff, 0x80, + + /* U+61A8 "憨" */ + 0x7e, 0x20, 0x8, 0x40, 0x10, 0xff, 0xda, 0x27, + 0xea, 0x48, 0x45, 0x1f, 0x86, 0x21, 0xc, 0xff, + 0xac, 0x5, 0x84, 0x0, 0x2, 0x4c, 0x24, 0x81, + 0x31, 0x2, 0x43, 0xf8, 0x0, + + /* U+61A9 "憩" */ + 0x0, 0x20, 0xfc, 0x40, 0x43, 0xf0, 0x84, 0x2f, + 0xe8, 0x42, 0x1f, 0x84, 0x21, 0x36, 0x7e, 0x44, + 0x84, 0x89, 0x9, 0xf3, 0xf0, 0x0, 0x2, 0x98, + 0xc9, 0x2, 0xb2, 0x8, 0x3, 0xf0, + + /* U+61AC "憬" */ + 0x27, 0xfc, 0x48, 0x8, 0x9f, 0xf5, 0xa0, 0x2b, + 0x7f, 0xd4, 0x8, 0x2b, 0xff, 0x90, 0x0, 0x27, + 0xfc, 0x48, 0x8, 0x9f, 0xf1, 0x2, 0x2, 0x25, + 0x85, 0x88, 0xc8, 0x70, 0x0, + + /* U+61B7 "憷" */ + 0x21, 0x8, 0x21, 0x8, 0x27, 0xbe, 0x31, 0x8, + 0xa3, 0xbc, 0xa5, 0x4a, 0xa0, 0x0, 0x27, 0xfe, + 0x20, 0x42, 0x22, 0x40, 0x22, 0x7e, 0x22, 0x40, + 0x25, 0x40, 0x28, 0xfe, 0x28, 0x0, + + /* U+61BE "憾" */ + 0x20, 0x14, 0x40, 0x20, 0x9f, 0xf9, 0xa0, 0x8a, + 0xdd, 0x54, 0x82, 0xa9, 0x75, 0x12, 0xa4, 0x29, + 0xda, 0x50, 0x4c, 0x82, 0x1, 0x2a, 0x22, 0x50, + 0xa5, 0x21, 0x48, 0x7c, 0x0, + + /* U+61C2 "懂" */ + 0x21, 0x10, 0x5f, 0xfc, 0x84, 0x41, 0xff, 0xeb, + 0x4, 0x15, 0xff, 0xe8, 0x10, 0x17, 0xfe, 0x2f, + 0xfc, 0x50, 0x88, 0xbf, 0xf1, 0x2, 0x2, 0x7f, + 0x87, 0xff, 0xc8, 0x0, 0x0, + + /* U+61C8 "懈" */ + 0x22, 0x3e, 0x33, 0x8a, 0x2c, 0x8a, 0xaf, 0xd2, + 0xad, 0x5e, 0xa5, 0x60, 0xa5, 0x54, 0x27, 0xd4, + 0x25, 0x5f, 0x25, 0x64, 0x27, 0xc4, 0x25, 0x7f, + 0x25, 0x44, 0x28, 0x44, 0x29, 0xc4, + + /* U+61CA "懊" */ + 0x20, 0x40, 0x41, 0x0, 0x9d, 0xf1, 0xa2, 0x2a, + 0xcd, 0x54, 0x88, 0xa9, 0x3d, 0x52, 0x5a, 0x25, + 0x4c, 0x40, 0x0, 0x9f, 0xf9, 0x3, 0x2, 0x9, + 0x4, 0x61, 0xb, 0x1, 0x80, + + /* U+61CB "懋" */ + 0x13, 0xe8, 0x10, 0x48, 0x79, 0x9e, 0x10, 0x88, + 0x33, 0xec, 0x59, 0x9c, 0x52, 0xaa, 0x14, 0xa8, + 0x13, 0x88, 0x0, 0x0, 0x0, 0x80, 0x28, 0x44, + 0x48, 0x12, 0x48, 0x10, 0x7, 0xe0, + + /* U+61D1 "懑" */ + 0x61, 0x10, 0xf, 0xfe, 0x1, 0x10, 0x4f, 0xfe, + 0x21, 0x10, 0xf, 0xfe, 0x29, 0x12, 0x4a, 0xaa, + 0x4c, 0x42, 0x8, 0xc, 0x1, 0x0, 0x28, 0x84, + 0x48, 0x12, 0x48, 0x10, 0x7, 0xe0, + + /* U+61D2 "懒" */ + 0x22, 0x10, 0x22, 0x1e, 0x2f, 0xa4, 0x32, 0x24, + 0xa2, 0x48, 0xaf, 0xbe, 0xaa, 0xa2, 0x2a, 0xaa, + 0x2f, 0xaa, 0x22, 0x2a, 0x27, 0x2a, 0x2a, 0x98, + 0x2a, 0x14, 0x22, 0x22, 0x22, 0x40, + + /* U+61D4 "懔" */ + 0x20, 0x40, 0x4f, 0xfc, 0x80, 0x1, 0x9f, 0xfa, + 0xc0, 0x34, 0xbf, 0x69, 0x3e, 0xd2, 0x1, 0x23, + 0xfc, 0x47, 0xf8, 0x80, 0x1, 0x3f, 0xf2, 0x12, + 0x4, 0xc5, 0xc8, 0x70, 0x0, + + /* U+61E6 "懦" */ + 0x27, 0xfc, 0x40, 0x80, 0xbf, 0xfd, 0xc2, 0x1a, + 0xb5, 0xb4, 0x8, 0x29, 0xd7, 0x17, 0xff, 0x20, + 0x80, 0x4f, 0xf8, 0x94, 0x91, 0x29, 0x22, 0x52, + 0x44, 0xa4, 0x89, 0x4b, 0x0, + + /* U+61F5 "懵" */ + 0x21, 0x10, 0x2f, 0xff, 0x20, 0x0, 0x37, 0xfe, + 0xa4, 0x92, 0xa7, 0xfe, 0xa0, 0x0, 0x2f, 0xff, + 0x2b, 0xfd, 0x22, 0x4, 0x23, 0xfc, 0x22, 0x4, + 0x23, 0xfc, 0x23, 0xfc, 0x22, 0x4, + + /* U+61FF "懿" */ + 0x10, 0x21, 0xfa, 0x78, 0x43, 0x17, 0xe2, 0xa0, + 0x1, 0x1f, 0x92, 0x21, 0x4a, 0x7e, 0xb6, 0x7a, + 0xc4, 0x90, 0x1, 0xe2, 0x92, 0x55, 0x24, 0xa8, + 0xcf, 0x51, 0x20, 0x3c, 0x0, + + /* U+6206 "戆" */ + 0x10, 0x41, 0xfd, 0xf8, 0x84, 0xa2, 0xee, 0xb7, + 0xc7, 0x90, 0xbf, 0xdf, 0x0, 0x42, 0x7e, 0x78, + 0x95, 0xfd, 0x28, 0x43, 0xb0, 0x8, 0x4, 0x89, + 0x49, 0x2, 0xc3, 0xf8, 0x0, + + /* U+6208 "戈" */ + 0x8, 0x0, 0x11, 0x80, 0x21, 0x80, 0x40, 0x60, + 0xff, 0x1e, 0x80, 0x1, 0x4, 0x2, 0x18, 0x2, + 0x60, 0x5, 0x80, 0x4, 0x0, 0x38, 0x21, 0x88, + 0x5c, 0x8, 0xa0, 0xe, 0x0, + + /* U+620A "戊" */ + 0x0, 0x80, 0x0, 0x90, 0x0, 0x88, 0x0, 0x80, + 0x3f, 0xfe, 0x20, 0x80, 0x20, 0x84, 0x20, 0x8c, + 0x20, 0x48, 0x20, 0x50, 0x20, 0x60, 0x20, 0x40, + 0x21, 0xa2, 0x46, 0x22, 0x48, 0x12, 0x80, 0xc, + + /* U+620B "戋" */ + 0x8, 0x80, 0x21, 0x0, 0x80, 0x2, 0xfe, 0xfc, + 0x0, 0x10, 0x0, 0x43, 0xcf, 0xf0, 0xc4, 0x0, + 0x8, 0xc0, 0x24, 0x0, 0x61, 0xe, 0x87, 0xc1, + 0x10, 0x3, 0x80, + + /* U+620C "戌" */ + 0x0, 0x40, 0x0, 0x4c, 0x0, 0x40, 0x3f, 0xff, + 0x20, 0x40, 0x20, 0x40, 0x20, 0x44, 0x20, 0x44, + 0x3f, 0x28, 0x20, 0x28, 0x20, 0x30, 0x20, 0x20, + 0x40, 0xd2, 0x43, 0x12, 0x80, 0xe, + + /* U+620D "戍" */ + 0x0, 0x40, 0x0, 0x4c, 0x0, 0x40, 0x3f, 0xff, + 0x20, 0x40, 0x20, 0x40, 0x20, 0x44, 0x28, 0x44, + 0x26, 0x28, 0x21, 0x30, 0x20, 0x30, 0x20, 0x61, + 0x40, 0xd2, 0x43, 0x1a, 0x80, 0xe, + + /* U+620E "戎" */ + 0x0, 0x80, 0x1, 0x30, 0x2, 0x7, 0xff, 0xf0, + 0x4, 0x2, 0x8, 0x4, 0x11, 0x8, 0x24, 0xfe, + 0x48, 0x20, 0xa0, 0x40, 0x80, 0x83, 0x12, 0x1a, + 0x28, 0x62, 0x40, 0x3, 0x0, + + /* U+620F "戏" */ + 0x0, 0x80, 0x1, 0x43, 0xe2, 0x60, 0x44, 0x40, + 0x8b, 0xd1, 0x78, 0x14, 0x20, 0x18, 0x44, 0x30, + 0x90, 0x20, 0xa0, 0xc1, 0x83, 0x42, 0x4, 0x1a, + 0x50, 0x44, 0x80, 0x7, 0x0, + + /* U+6210 "成" */ + 0x0, 0x40, 0x0, 0x4c, 0x0, 0x40, 0x3f, 0xff, + 0x20, 0x40, 0x20, 0x40, 0x3e, 0x44, 0x22, 0x44, + 0x22, 0x28, 0x22, 0x30, 0x22, 0x30, 0x22, 0x31, + 0x4c, 0x52, 0x41, 0x8a, 0x81, 0xe, + + /* U+6211 "我" */ + 0x0, 0x40, 0x3c, 0xa1, 0xa1, 0x20, 0x42, 0x20, + 0x84, 0x1f, 0xff, 0xc2, 0x10, 0x4, 0x20, 0x8, + 0x44, 0x1e, 0x93, 0xe1, 0x40, 0x41, 0x0, 0x86, + 0x21, 0x34, 0x42, 0x84, 0xb8, 0x6, + + /* U+6212 "戒" */ + 0x0, 0x40, 0x0, 0xb0, 0x1, 0x7, 0xff, 0xf0, + 0x4, 0x4, 0x88, 0x9, 0x12, 0x12, 0x24, 0xff, + 0x48, 0x48, 0xa0, 0x91, 0x42, 0x21, 0x14, 0x46, + 0x38, 0x92, 0x40, 0x43, 0x0, + + /* U+6215 "戕" */ + 0x24, 0x28, 0x24, 0x26, 0x24, 0x20, 0x24, 0x27, + 0x3d, 0xf8, 0x4, 0x20, 0x4, 0x22, 0x4, 0x22, + 0xfc, 0x24, 0x24, 0x18, 0x24, 0x18, 0x24, 0x31, + 0x24, 0x49, 0x47, 0x89, 0x44, 0x6, + + /* U+6216 "或" */ + 0x0, 0x10, 0x1, 0x10, 0x2, 0x7, 0xff, 0xf0, + 0x4, 0xf, 0xc8, 0x90, 0x91, 0x21, 0x24, 0x42, + 0x48, 0xfc, 0xa0, 0x0, 0x80, 0x3, 0x11, 0xe6, + 0x3c, 0x32, 0x40, 0x43, 0x0, + + /* U+6217 "戗" */ + 0x8, 0x20, 0x14, 0x24, 0x12, 0x20, 0x21, 0x20, + 0x40, 0x3e, 0x3e, 0xe0, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x24, 0x2e, 0x18, 0x20, 0x18, 0x21, 0x11, + 0x21, 0x69, 0x3e, 0x8a, 0x0, 0x6, + + /* U+6218 "战" */ + 0x20, 0x40, 0x40, 0xa0, 0x81, 0x21, 0x2, 0x3, + 0xc4, 0x4, 0xb, 0x88, 0x78, 0x10, 0x22, 0xfc, + 0x49, 0x8, 0x52, 0x10, 0xc4, 0x21, 0x8, 0x4e, + 0x3f, 0xa2, 0x60, 0x5, 0x40, 0x6, + + /* U+621A "戚" */ + 0x0, 0x20, 0x0, 0x2c, 0x0, 0x20, 0x3f, 0xff, + 0x20, 0x20, 0x22, 0x20, 0x23, 0xe2, 0x22, 0x24, + 0x22, 0x24, 0x2f, 0xe4, 0x21, 0x18, 0x2d, 0x98, + 0x29, 0x51, 0x51, 0x2a, 0x43, 0x2a, 0x8e, 0x46, + + /* U+621B "戛" */ + 0xff, 0xfe, 0x4, 0x0, 0xff, 0xe1, 0x0, 0x43, + 0xff, 0x84, 0x1, 0xf, 0xfe, 0x1f, 0xfc, 0x8, + 0xc0, 0x10, 0x98, 0xff, 0xc6, 0x10, 0xc0, 0x3e, + 0x23, 0xd8, 0x78, 0xf, 0x0, + + /* U+621F "戟" */ + 0x10, 0x41, 0xfc, 0xb0, 0x41, 0x0, 0x82, 0x7, + 0xc7, 0xd0, 0x98, 0x1f, 0x13, 0x42, 0x24, 0x84, + 0x50, 0xf8, 0x60, 0x40, 0x87, 0xf3, 0x11, 0x9, + 0x22, 0x22, 0x84, 0x3, 0x0, + + /* U+6221 "戡" */ + 0x21, 0x20, 0x42, 0x53, 0xfe, 0x91, 0x9, 0x3, + 0xf7, 0xe4, 0x24, 0xf, 0xc9, 0x10, 0x92, 0xff, + 0xa8, 0x80, 0x21, 0x28, 0x42, 0x89, 0x94, 0x5, + 0x2f, 0xe1, 0x40, 0x3, 0x0, + + /* U+6222 "戢" */ + 0x0, 0x20, 0xfe, 0x41, 0x4, 0xb3, 0xf9, 0x20, + 0x2, 0x1f, 0xff, 0xd0, 0x48, 0x3f, 0x92, 0x41, + 0x24, 0x82, 0x29, 0xfc, 0x62, 0x8, 0xd5, 0xf9, + 0x34, 0x25, 0x40, 0x51, 0x80, + + /* U+6224 "戤" */ + 0x0, 0x10, 0xfe, 0x30, 0x84, 0x51, 0xfc, 0x82, + 0x4b, 0xeb, 0x92, 0x13, 0x25, 0x59, 0xca, 0x0, + 0x18, 0xff, 0x11, 0x52, 0x42, 0xa5, 0x55, 0x48, + 0xbf, 0xf1, 0x40, 0x1, 0x80, + + /* U+6225 "戥" */ + 0x0, 0x20, 0x7f, 0x28, 0x41, 0x24, 0x41, 0x20, + 0x7f, 0x20, 0x41, 0x3e, 0x7f, 0x60, 0x8, 0x22, + 0x48, 0x24, 0x7f, 0x28, 0x88, 0x18, 0x7f, 0x10, + 0x8, 0x32, 0x8, 0x4a, 0x7f, 0xe, 0x80, 0x6, + + /* U+622A "截" */ + 0x0, 0x20, 0xff, 0x50, 0x20, 0x90, 0x41, 0xf, + 0xff, 0xe0, 0x4, 0x9, 0x8, 0x1f, 0xd2, 0x44, + 0x25, 0x7f, 0x51, 0x10, 0x41, 0xfc, 0x94, 0x47, + 0x27, 0xf1, 0x50, 0x3, 0x0, + + /* U+622C "戬" */ + 0x0, 0x21, 0xff, 0x50, 0x50, 0x92, 0xa5, 0x5, + 0x57, 0xff, 0xf4, 0x0, 0x9, 0x3f, 0x94, 0x41, + 0x28, 0x82, 0x21, 0xfc, 0xc2, 0x9, 0x94, 0x15, + 0x2f, 0xe1, 0x50, 0x43, 0x0, + + /* U+622E "戮" */ + 0x0, 0x21, 0xfe, 0x40, 0x44, 0xa2, 0xa9, 0x21, + 0x32, 0xf, 0xaf, 0xa6, 0x48, 0xe, 0x12, 0x62, + 0x29, 0x13, 0x50, 0xc0, 0x42, 0x60, 0x83, 0x13, + 0x28, 0xca, 0x46, 0x62, 0xf0, 0x6, + + /* U+6233 "戳" */ + 0x0, 0x10, 0x7f, 0x94, 0x8, 0x92, 0x6a, 0x90, + 0x19, 0x90, 0x6e, 0xbf, 0x0, 0x90, 0x12, 0x11, + 0x3f, 0xd2, 0x62, 0x14, 0xbf, 0xcc, 0x22, 0x8, + 0x3f, 0xd9, 0x22, 0x29, 0x3f, 0xc5, 0x20, 0x3, + + /* U+6234 "戴" */ + 0x8, 0x28, 0xff, 0x48, 0x20, 0x87, 0xff, 0xf0, + 0x2, 0xf, 0xe4, 0x9f, 0xc9, 0x24, 0x94, 0x7f, + 0x28, 0x44, 0x61, 0xfc, 0x47, 0xfd, 0x90, 0x5, + 0x24, 0x41, 0x70, 0x43, 0x0, + + /* U+6237 "户" */ + 0x1, 0x0, 0x2, 0x3, 0xff, 0xc8, 0x1, 0x20, + 0x4, 0x80, 0x12, 0x0, 0x4f, 0xff, 0x20, 0x4, + 0x80, 0x2, 0x0, 0x18, 0x0, 0x40, 0x1, 0x0, + 0x8, 0x0, 0x0, + + /* U+623D "戽" */ + 0x0, 0x0, 0x1, 0x0, 0xff, 0xf1, 0x0, 0x22, + 0x0, 0x47, 0xff, 0x88, 0x0, 0x11, 0x88, 0x20, + 0x90, 0x48, 0x20, 0x88, 0x41, 0x0, 0x84, 0xf, + 0xeb, 0xe2, 0x30, 0x4, 0x40, 0x8, + + /* U+623E "戾" */ + 0x1, 0x0, 0x1, 0x0, 0xff, 0xf1, 0x0, 0x22, + 0x0, 0x47, 0xff, 0x88, 0x0, 0x10, 0x48, 0x20, + 0x88, 0x5f, 0xfc, 0x82, 0x3, 0xa, 0x4, 0x22, + 0x8, 0xc2, 0x26, 0x3, 0x80, 0x0, + + /* U+623F "房" */ + 0x0, 0x80, 0x0, 0x80, 0x3f, 0xfc, 0x20, 0x4, + 0x3f, 0xfc, 0x20, 0x40, 0x20, 0x40, 0x2f, 0xff, + 0x22, 0x0, 0x23, 0xfc, 0x22, 0x4, 0x24, 0x4, + 0x44, 0x4, 0x48, 0x4, 0x90, 0x78, + + /* U+6240 "所" */ + 0x0, 0x0, 0xf9, 0xfd, 0x2, 0x2, 0x4, 0x4, + 0x8, 0xf, 0x90, 0x11, 0x3f, 0xa2, 0x44, 0x44, + 0x88, 0xf9, 0x11, 0x2, 0x22, 0x8, 0x4c, 0x10, + 0x90, 0x41, 0x21, 0x2, 0x0, + + /* U+6241 "扁" */ + 0x0, 0x80, 0x1, 0x0, 0xff, 0xf9, 0x0, 0x12, + 0x0, 0x27, 0xff, 0xc8, 0x0, 0x10, 0x0, 0x2f, + 0xfe, 0x52, 0x44, 0xa4, 0x89, 0x7f, 0xf2, 0x92, + 0x29, 0x24, 0x52, 0x48, 0xc4, 0x97, + + /* U+6243 "扃" */ + 0x0, 0x80, 0x1, 0x0, 0xff, 0xf9, 0x0, 0x12, + 0x0, 0x27, 0xff, 0xc8, 0x0, 0x10, 0x0, 0x2f, + 0xfe, 0x50, 0x4, 0xa7, 0xc9, 0x48, 0x92, 0x91, + 0x29, 0x3e, 0x52, 0x0, 0xc4, 0xf, + + /* U+6247 "扇" */ + 0x0, 0x80, 0x1, 0x0, 0xff, 0xf9, 0x0, 0x12, + 0x0, 0x27, 0xff, 0xc8, 0x0, 0x17, 0xff, 0x20, + 0x82, 0x59, 0x44, 0x9a, 0x49, 0x4, 0x32, 0x7b, + 0xab, 0x10, 0x50, 0x20, 0xe3, 0x8f, + + /* U+6248 "扈" */ + 0x0, 0x0, 0x0, 0x80, 0x3f, 0xfe, 0x20, 0x2, + 0x3f, 0xfe, 0x20, 0x0, 0x27, 0xfc, 0x24, 0x4, + 0x27, 0xfc, 0x20, 0x0, 0x2f, 0xfe, 0x28, 0x42, + 0x2f, 0xfe, 0x48, 0x1, 0x48, 0x2, 0xcf, 0xfe, + + /* U+6249 "扉" */ + 0x0, 0x0, 0x1, 0x0, 0xff, 0xf9, 0x0, 0x12, + 0x0, 0x27, 0xff, 0xc8, 0x0, 0x10, 0x90, 0x2f, + 0x3e, 0x42, 0x40, 0xbc, 0xf9, 0x9, 0x2, 0x12, + 0xb, 0xe7, 0xd0, 0x48, 0x0, 0x90, + + /* U+624B "手" */ + 0xff, 0xf8, 0x2, 0x0, 0x4, 0x0, 0x8, 0x7, + 0xff, 0xc0, 0x20, 0x0, 0x40, 0x0, 0x80, 0xff, + 0xfe, 0x2, 0x0, 0x4, 0x0, 0x8, 0x0, 0x10, + 0x3, 0xc0, 0x0, + + /* U+624C "扌" */ + 0x10, 0x20, 0x47, 0xf1, 0x2, 0x4, 0xf, 0x71, + 0x20, 0x40, 0x81, 0x2, 0x38, 0x0, + + /* U+624D "才" */ + 0x0, 0x20, 0x0, 0x40, 0x0, 0x87, 0xff, 0xf0, + 0xa, 0x0, 0x14, 0x0, 0x48, 0x1, 0x10, 0x6, + 0x20, 0x18, 0x40, 0x60, 0x81, 0x1, 0xc, 0x2, + 0x0, 0xc, 0x0, 0xf0, 0x0, + + /* U+624E "扎" */ + 0x10, 0x40, 0x20, 0x80, 0x41, 0x7, 0xf2, 0x1, + 0x4, 0x2, 0x8, 0x4, 0x10, 0xf, 0x20, 0x70, + 0x41, 0x20, 0x80, 0x41, 0x0, 0x82, 0x11, 0x4, + 0x22, 0x8, 0x7c, 0xf, 0x0, + + /* U+6251 "扑" */ + 0x20, 0x80, 0x82, 0x2, 0x8, 0x3f, 0x20, 0x20, + 0xa0, 0x82, 0xc2, 0x9, 0x8b, 0x22, 0x70, 0x86, + 0x82, 0x2, 0x8, 0x8, 0x20, 0x20, 0x80, 0x82, + 0xe, 0x8, 0x0, + + /* U+6252 "扒" */ + 0x10, 0x88, 0x10, 0x88, 0x10, 0x88, 0x7c, 0x88, + 0x10, 0x88, 0x10, 0x88, 0x10, 0x88, 0x1c, 0x88, + 0xf0, 0x84, 0x11, 0x4, 0x11, 0x4, 0x11, 0x4, + 0x12, 0x2, 0x16, 0x2, 0x64, 0x1, + + /* U+6253 "打" */ + 0x20, 0x0, 0x47, 0xfc, 0x80, 0x47, 0xe0, 0x82, + 0x1, 0x4, 0x2, 0x8, 0x4, 0x16, 0x8, 0x70, + 0x11, 0x40, 0x20, 0x80, 0x41, 0x0, 0x82, 0x1, + 0x4, 0x2, 0x38, 0x78, 0x0, + + /* U+6254 "扔" */ + 0x20, 0x0, 0x9f, 0xe2, 0x10, 0xbe, 0x42, 0x21, + 0x10, 0x84, 0x42, 0x11, 0xce, 0x41, 0xe1, 0x4, + 0x8c, 0x12, 0x20, 0x48, 0x81, 0x24, 0x4, 0x90, + 0x1c, 0x87, 0x80, + + /* U+6258 "托" */ + 0x20, 0x0, 0x40, 0xf0, 0x8e, 0x7, 0xc4, 0x2, + 0x8, 0x4, 0x10, 0x8, 0x21, 0x17, 0xfc, 0xf0, + 0x80, 0x41, 0x0, 0x82, 0x1, 0x4, 0x22, 0x8, + 0x44, 0x10, 0xb8, 0x1f, 0x0, + + /* U+625B "扛" */ + 0x10, 0x0, 0x23, 0xfc, 0x40, 0x87, 0xe1, 0x1, + 0x2, 0x2, 0x4, 0x4, 0x8, 0xe, 0x10, 0xf0, + 0x20, 0x20, 0x40, 0x40, 0x80, 0x81, 0x1, 0x2, + 0x2, 0x7f, 0xd8, 0x0, 0x0, + + /* U+6263 "扣" */ + 0x20, 0x0, 0x8f, 0xf2, 0x20, 0x7e, 0x81, 0x22, + 0x4, 0x88, 0x12, 0x20, 0x4e, 0x81, 0xe2, 0x4, + 0x88, 0x12, 0x20, 0x48, 0x81, 0x23, 0xfc, 0x88, + 0x1c, 0x20, 0x40, + + /* U+6266 "扦" */ + 0x20, 0x0, 0x47, 0xfc, 0x80, 0x87, 0xc1, 0x2, + 0x2, 0x4, 0x4, 0x8, 0xff, 0x9c, 0x10, 0xe0, + 0x20, 0x40, 0x40, 0x80, 0x81, 0x1, 0x2, 0x2, + 0x4, 0x4, 0x38, 0x8, 0x0, + + /* U+6267 "执" */ + 0x20, 0x80, 0x20, 0x80, 0x20, 0x80, 0xfb, 0xf8, + 0x20, 0x88, 0x20, 0x88, 0x20, 0x88, 0x2a, 0x88, + 0xf1, 0x88, 0x21, 0xc8, 0x21, 0x28, 0x21, 0x9, + 0x22, 0xa, 0x24, 0xa, 0xec, 0x6, + + /* U+6269 "扩" */ + 0x20, 0x40, 0x40, 0x40, 0x80, 0x87, 0xdf, 0xf2, + 0x20, 0x4, 0x40, 0x8, 0x80, 0x1d, 0x0, 0xe2, + 0x0, 0x44, 0x0, 0x88, 0x1, 0x20, 0x2, 0x40, + 0x4, 0x80, 0x32, 0x0, 0x0, + + /* U+626A "扪" */ + 0x2, 0x0, 0x42, 0x7c, 0x80, 0xb, 0xd0, 0x12, + 0x20, 0x24, 0x40, 0x48, 0x80, 0x8d, 0x1, 0xe2, + 0x2, 0x44, 0x4, 0x88, 0x9, 0x10, 0x12, 0x20, + 0x24, 0x40, 0x58, 0x87, 0x0, + + /* U+626B "扫" */ + 0x20, 0x0, 0x8f, 0xf2, 0x0, 0x7e, 0x1, 0x20, + 0x4, 0x80, 0x12, 0x0, 0x4e, 0xff, 0xe0, 0x4, + 0x80, 0x12, 0x0, 0x48, 0x1, 0x20, 0x4, 0x9f, + 0xfe, 0x0, 0x40, + + /* U+626C "扬" */ + 0x20, 0x0, 0x9f, 0xe2, 0x1, 0x3e, 0x18, 0x21, + 0x80, 0x98, 0x2, 0x7f, 0xce, 0x49, 0xe1, 0x24, + 0x89, 0x12, 0x24, 0x49, 0x11, 0x28, 0x84, 0x84, + 0x1c, 0x23, 0x80, + + /* U+626D "扭" */ + 0x20, 0x0, 0x23, 0xfc, 0x20, 0x84, 0xf8, 0x84, + 0x20, 0x84, 0x21, 0x4, 0x29, 0x4, 0x33, 0xfc, + 0xe1, 0x4, 0x21, 0x4, 0x21, 0x4, 0x21, 0x4, + 0x21, 0x4, 0x27, 0xff, 0x60, 0x0, + + /* U+626E "扮" */ + 0x20, 0x20, 0x41, 0x20, 0x84, 0x47, 0xc8, 0x42, + 0x20, 0xc4, 0x80, 0xc9, 0x0, 0x11, 0xfc, 0x79, + 0x9, 0x42, 0x10, 0x84, 0x21, 0x10, 0x42, 0x20, + 0x84, 0xc1, 0x3b, 0x3c, 0x0, + + /* U+626F "扯" */ + 0x20, 0x20, 0x40, 0x40, 0x80, 0x87, 0xd1, 0x2, + 0x22, 0x4, 0x47, 0x88, 0x88, 0x1d, 0x10, 0xe2, + 0x20, 0x44, 0x40, 0x88, 0x81, 0x11, 0x2, 0x22, + 0x5, 0xff, 0xf8, 0x0, 0x0, + + /* U+6270 "扰" */ + 0x20, 0x80, 0x41, 0x30, 0x82, 0x27, 0xc4, 0x2, + 0x3f, 0xe4, 0x14, 0x8, 0x28, 0x14, 0x50, 0xf0, + 0xa0, 0x42, 0x40, 0x84, 0x81, 0x9, 0x12, 0x22, + 0x24, 0x84, 0x5a, 0xf, 0x0, + + /* U+6273 "扳" */ + 0x20, 0x0, 0x47, 0xf8, 0x88, 0x7, 0xd0, 0x2, + 0x20, 0x4, 0x7f, 0x88, 0xa1, 0x15, 0x44, 0xf2, + 0x88, 0x44, 0xa0, 0x91, 0x41, 0x21, 0x2, 0x45, + 0x5, 0x31, 0xba, 0xc1, 0x0, + + /* U+6276 "扶" */ + 0x20, 0x40, 0x40, 0x80, 0x81, 0x7, 0xdf, 0xe2, + 0x4, 0x4, 0x8, 0x8, 0x10, 0x17, 0xff, 0xf0, + 0x40, 0x41, 0x80, 0x82, 0x81, 0x9, 0x2, 0x11, + 0x4, 0xc1, 0xbb, 0x1, 0x0, + + /* U+6279 "批" */ + 0x22, 0x20, 0x44, 0x40, 0x88, 0x87, 0xd1, 0x32, + 0x22, 0x44, 0x7d, 0x8, 0x8c, 0x15, 0x10, 0x72, + 0x21, 0x44, 0x40, 0x88, 0x81, 0x11, 0x12, 0x26, + 0x24, 0x74, 0x58, 0x8f, 0x0, + + /* U+627C "扼" */ + 0x20, 0x0, 0x23, 0xff, 0x22, 0x0, 0xfa, 0x0, + 0x22, 0xfc, 0x22, 0x84, 0x22, 0x84, 0x3a, 0x84, + 0xe2, 0x84, 0x24, 0xbc, 0x24, 0x82, 0x24, 0x82, + 0x24, 0x82, 0x24, 0x82, 0xe8, 0x7c, + + /* U+627E "找" */ + 0x10, 0x80, 0x21, 0x20, 0x42, 0x27, 0xe4, 0x1, + 0xf, 0xc2, 0x70, 0x4, 0x20, 0xa, 0x44, 0xf8, + 0x90, 0x20, 0xc0, 0x41, 0x0, 0x8e, 0x11, 0x62, + 0x42, 0x4, 0xb8, 0x7, 0x0, + + /* U+627F "承" */ + 0x1f, 0xfc, 0x0, 0x18, 0x0, 0xe0, 0x78, 0x89, + 0xb, 0xea, 0x8, 0x8c, 0x8, 0x88, 0x13, 0xe4, + 0x10, 0x84, 0x27, 0xf4, 0x20, 0x82, 0x40, 0x81, + 0x0, 0x81, 0x7, 0x0, + + /* U+6280 "技" */ + 0x20, 0x20, 0x40, 0x40, 0x8f, 0xff, 0xc1, 0x2, + 0x2, 0x4, 0x4, 0x8, 0xff, 0x1e, 0x82, 0xe1, + 0x8, 0x41, 0x10, 0x82, 0x41, 0x3, 0x2, 0x7, + 0x4, 0x31, 0xb9, 0x80, 0x80, + + /* U+6284 "抄" */ + 0x20, 0x20, 0x20, 0x20, 0x20, 0x28, 0xf9, 0x24, + 0x21, 0x24, 0x22, 0x22, 0x22, 0x20, 0x7c, 0x20, + 0xa0, 0x24, 0x20, 0x8, 0x20, 0x18, 0x20, 0x20, + 0x20, 0x40, 0x23, 0x80, 0xec, 0x0, + + /* U+6289 "抉" */ + 0x20, 0x40, 0x40, 0x80, 0x81, 0x7, 0xdf, 0xe2, + 0x4, 0x44, 0x8, 0x88, 0x11, 0x1c, 0x22, 0xe7, + 0xfe, 0x41, 0x80, 0x82, 0x81, 0x4, 0x82, 0x11, + 0x84, 0xc0, 0x9b, 0x0, 0x80, + + /* U+628A "把" */ + 0x20, 0x0, 0x23, 0xfc, 0x24, 0x44, 0xfc, 0x44, + 0x24, 0x44, 0x24, 0x44, 0x2b, 0xfc, 0x34, 0x0, + 0xe4, 0x0, 0x24, 0x0, 0x24, 0x3, 0x24, 0x2, + 0x24, 0x2, 0x22, 0x2, 0x63, 0xfc, + + /* U+6291 "抑" */ + 0x20, 0x0, 0x9e, 0x2, 0x47, 0xfd, 0x11, 0x24, + 0x44, 0x91, 0x12, 0x44, 0x4d, 0x11, 0xe4, 0x44, + 0x91, 0x12, 0x44, 0x49, 0x51, 0x26, 0x5c, 0x81, + 0xc, 0x4, 0x0, + + /* U+6292 "抒" */ + 0x20, 0x0, 0x47, 0xfc, 0x80, 0x17, 0xcc, 0x42, + 0x5, 0x4, 0x4, 0x8, 0xff, 0xb8, 0x11, 0xa0, + 0x22, 0x40, 0x48, 0x80, 0x81, 0x1, 0x2, 0x2, + 0x8, 0x4, 0x30, 0x70, 0x0, + + /* U+6293 "抓" */ + 0x0, 0x0, 0x8f, 0xf1, 0x12, 0x47, 0xa4, 0x84, + 0x49, 0x8, 0x92, 0x11, 0x24, 0x2a, 0x48, 0xe4, + 0x90, 0x89, 0x21, 0x12, 0x22, 0x24, 0x44, 0x88, + 0xc9, 0x10, 0xb4, 0x21, 0x0, + + /* U+6295 "投" */ + 0x10, 0x0, 0x11, 0xf8, 0x11, 0x8, 0xf9, 0x8, + 0x11, 0x8, 0x12, 0xe, 0x10, 0x0, 0x1b, 0xfc, + 0xf1, 0x4, 0x11, 0x8, 0x10, 0x90, 0x10, 0x70, + 0x10, 0x60, 0x21, 0x9c, 0xe6, 0x2, + + /* U+6296 "抖" */ + 0x20, 0x10, 0x42, 0x20, 0x82, 0x47, 0xc2, 0x82, + 0x1, 0x4, 0x42, 0x8, 0x44, 0x1e, 0x48, 0xe0, + 0x10, 0x40, 0x7c, 0x9f, 0x41, 0x0, 0x82, 0x1, + 0x4, 0x2, 0x38, 0x4, 0x0, + + /* U+6297 "抗" */ + 0x20, 0x40, 0x40, 0x80, 0x8f, 0xf7, 0xc0, 0x2, + 0x0, 0x4, 0x3e, 0x8, 0x44, 0x1c, 0x88, 0xe1, + 0x10, 0x42, 0x20, 0x84, 0x41, 0x8, 0x92, 0x21, + 0x24, 0xc2, 0x7b, 0x7, 0x80, + + /* U+6298 "折" */ + 0x20, 0x0, 0x47, 0xf8, 0x88, 0x7, 0xd0, 0x2, + 0x20, 0x4, 0x40, 0x8, 0xff, 0x95, 0x8, 0xf2, + 0x10, 0x44, 0x20, 0x88, 0x41, 0x10, 0x82, 0x41, + 0x4, 0x82, 0x32, 0x4, 0x0, + + /* U+629A "抚" */ + 0x20, 0x0, 0x47, 0xf8, 0x81, 0x7, 0xc2, 0x2, + 0x4, 0x4, 0x8, 0x9, 0xff, 0xb8, 0x50, 0xa0, + 0xa0, 0x41, 0x40, 0x84, 0x89, 0x9, 0x12, 0x22, + 0x24, 0x87, 0xba, 0x0, 0x0, + + /* U+629B "抛" */ + 0x24, 0x20, 0x48, 0x40, 0x90, 0x87, 0xa3, 0xe2, + 0x7a, 0x44, 0x94, 0x89, 0x29, 0x1a, 0x52, 0xe4, + 0xa4, 0x49, 0x48, 0x93, 0x11, 0x26, 0xf2, 0x88, + 0x25, 0x10, 0x74, 0x1f, 0x80, + + /* U+629F "抟" */ + 0x0, 0x40, 0x20, 0x40, 0x23, 0xfe, 0xf8, 0x80, + 0x20, 0x80, 0x27, 0xff, 0x21, 0x0, 0x19, 0x0, + 0xe1, 0xfe, 0x20, 0x4, 0x20, 0xc, 0x21, 0x88, + 0x20, 0x50, 0x20, 0x30, 0xe0, 0x18, + + /* U+62A0 "抠" */ + 0x20, 0x0, 0x47, 0xfc, 0x88, 0x7, 0xd4, 0x22, + 0x24, 0x44, 0x49, 0x8, 0x8a, 0x15, 0x8, 0xf2, + 0x10, 0x44, 0x50, 0x89, 0x11, 0x12, 0x22, 0x28, + 0x4, 0x40, 0x38, 0xff, 0x80, + + /* U+62A1 "抡" */ + 0x20, 0x40, 0x41, 0x40, 0x82, 0x87, 0xc8, 0x82, + 0x20, 0x84, 0x80, 0xc8, 0x80, 0x11, 0xc, 0x3a, + 0x21, 0xc5, 0x80, 0x8c, 0x1, 0x10, 0x22, 0x20, + 0x44, 0x40, 0x98, 0x7e, 0x0, + + /* U+62A2 "抢" */ + 0x20, 0x40, 0x41, 0x40, 0x82, 0x47, 0xc8, 0xc2, + 0x20, 0x44, 0x80, 0x48, 0xfe, 0x11, 0x4, 0xfa, + 0x8, 0x44, 0x10, 0x89, 0xc9, 0x10, 0x12, 0x20, + 0x24, 0x40, 0xf8, 0x7f, 0x0, + + /* U+62A4 "护" */ + 0x20, 0x40, 0x81, 0x2, 0x3f, 0xfe, 0x81, 0x22, + 0x4, 0x88, 0x12, 0x20, 0x5e, 0xff, 0xa2, 0x4, + 0x88, 0x2, 0x20, 0x9, 0x0, 0x24, 0x0, 0x90, + 0xc, 0x80, 0x0, + + /* U+62A5 "报" */ + 0x20, 0x0, 0x47, 0xf8, 0x88, 0x11, 0x10, 0x2f, + 0xa7, 0xc4, 0x40, 0x8, 0xff, 0x9d, 0x42, 0xe2, + 0x84, 0x44, 0x90, 0x89, 0x21, 0x11, 0x82, 0x23, + 0x4, 0x49, 0xb8, 0xe0, 0x80, + + /* U+62A8 "抨" */ + 0x0, 0x0, 0x23, 0xfe, 0x20, 0x20, 0xfa, 0x22, + 0x21, 0x22, 0x21, 0x24, 0x20, 0x20, 0x78, 0x20, + 0xa7, 0xff, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x60, 0x20, + + /* U+62AB "披" */ + 0x20, 0x20, 0x20, 0x20, 0x23, 0xfe, 0xfa, 0x22, + 0x22, 0x22, 0x22, 0x20, 0x23, 0xfc, 0x3a, 0x84, + 0xe2, 0x84, 0x22, 0x48, 0x22, 0x48, 0x24, 0x30, + 0x24, 0x70, 0x24, 0xcc, 0x6b, 0x2, + + /* U+62AC "抬" */ + 0x20, 0x40, 0x41, 0x0, 0x82, 0x47, 0xc8, 0x42, + 0x20, 0x44, 0xff, 0x88, 0x0, 0x9e, 0x0, 0xe3, + 0xfc, 0x44, 0x8, 0x88, 0x11, 0x10, 0x22, 0x20, + 0x44, 0x7f, 0xb8, 0x81, 0x0, + + /* U+62B1 "抱" */ + 0x21, 0x0, 0x42, 0x0, 0x87, 0xf7, 0xd0, 0x22, + 0x40, 0x44, 0xfc, 0x88, 0x89, 0x1d, 0x12, 0xe3, + 0xe4, 0x44, 0x8, 0x89, 0xe1, 0x10, 0x12, 0x20, + 0x24, 0x40, 0x58, 0x7f, 0x0, + + /* U+62B5 "抵" */ + 0x0, 0x0, 0x8f, 0xe9, 0x11, 0x7, 0xa2, 0x4, + 0x44, 0x8, 0x88, 0x11, 0xff, 0x1a, 0x20, 0xc4, + 0x40, 0x88, 0x41, 0x10, 0x82, 0x21, 0x14, 0x52, + 0x28, 0xd2, 0xb0, 0x13, 0x0, + + /* U+62B9 "抹" */ + 0x40, 0x40, 0x80, 0x81, 0x1f, 0xff, 0x82, 0x4, + 0x4, 0x8, 0x8, 0x11, 0xff, 0x18, 0x70, 0xc0, + 0xe0, 0x82, 0xa1, 0x9, 0x42, 0x12, 0x44, 0xc4, + 0x49, 0x8, 0x70, 0x10, 0x0, + + /* U+62BB "抻" */ + 0x20, 0x20, 0x80, 0x82, 0x3f, 0xfe, 0x89, 0x22, + 0x24, 0x88, 0x92, 0x3f, 0xca, 0x89, 0xf2, 0x24, + 0x88, 0x92, 0x3f, 0xc8, 0x8, 0x20, 0x20, 0x80, + 0x8e, 0x2, 0x0, + + /* U+62BC "押" */ + 0x20, 0x0, 0x8f, 0xf2, 0x22, 0x7e, 0x89, 0x22, + 0x24, 0x8f, 0xf2, 0x22, 0x4e, 0x89, 0xe2, 0x24, + 0x8f, 0xf2, 0x22, 0x48, 0x8, 0x20, 0x20, 0x80, + 0x8e, 0x2, 0x0, + + /* U+62BD "抽" */ + 0x20, 0x40, 0x81, 0x2, 0x4, 0x3e, 0xff, 0x24, + 0x44, 0x91, 0x12, 0x44, 0x4f, 0x11, 0xe3, 0xfc, + 0x91, 0x12, 0x44, 0x49, 0x11, 0x24, 0x44, 0x8f, + 0xfe, 0x40, 0x40, + + /* U+62BF "抿" */ + 0x20, 0x0, 0x23, 0xfe, 0x22, 0x2, 0xfa, 0x2, + 0x23, 0xfe, 0x22, 0x20, 0x22, 0x20, 0x3a, 0x20, + 0xe3, 0xff, 0x22, 0x20, 0x22, 0x20, 0x22, 0x10, + 0x22, 0x51, 0x23, 0x89, 0xe2, 0x6, + + /* U+62C2 "拂" */ + 0x41, 0x21, 0x1f, 0xff, 0x12, 0x50, 0x49, 0x47, + 0xfd, 0x24, 0x83, 0x92, 0x32, 0x48, 0x4f, 0xfd, + 0x4, 0x94, 0x22, 0x50, 0x8f, 0x44, 0x23, 0x20, + 0x80, + + /* U+62C4 "拄" */ + 0x20, 0x20, 0x40, 0x40, 0x80, 0x87, 0xdf, 0xf2, + 0x2, 0x4, 0x4, 0x9, 0x8, 0x3d, 0xff, 0xa0, + 0x20, 0x40, 0x40, 0x80, 0x81, 0x1, 0x2, 0x2, + 0x4, 0xff, 0xd8, 0x0, 0x0, + + /* U+62C5 "担" */ + 0x20, 0x0, 0x47, 0xf8, 0x88, 0x17, 0xd0, 0x22, + 0x20, 0x44, 0x7f, 0x88, 0x81, 0x1d, 0x2, 0xe2, + 0x4, 0x44, 0x8, 0x8f, 0xf1, 0x0, 0x2, 0x0, + 0x5, 0xff, 0xf0, 0x0, 0x0, + + /* U+62C6 "拆" */ + 0x0, 0x0, 0x47, 0xf8, 0x88, 0x7, 0xd0, 0x2, + 0x20, 0x4, 0x7f, 0xc8, 0x84, 0xd, 0x8, 0xe2, + 0x90, 0x44, 0xe0, 0x88, 0x61, 0x20, 0xb2, 0x41, + 0x4, 0x82, 0x1a, 0x4, 0x0, + + /* U+62C7 "拇" */ + 0x20, 0x0, 0x21, 0xfc, 0x22, 0x4, 0xfa, 0x44, + 0x22, 0x24, 0x22, 0x4, 0x27, 0xff, 0x22, 0x4, + 0xfa, 0x44, 0x22, 0x24, 0x22, 0x4, 0x23, 0xff, + 0x20, 0x4, 0x20, 0x4, 0x60, 0x38, + + /* U+62C8 "拈" */ + 0x20, 0x40, 0x40, 0x80, 0x81, 0x7, 0xc3, 0xf2, + 0x4, 0x4, 0x8, 0x8, 0x10, 0x10, 0x20, 0x7b, + 0xfd, 0x44, 0x8, 0x88, 0x11, 0x10, 0x22, 0x20, + 0x44, 0x7f, 0xb8, 0x81, 0x0, + + /* U+62C9 "拉" */ + 0x20, 0x40, 0x40, 0x80, 0x9f, 0xf7, 0xc0, 0x2, + 0x0, 0x4, 0x41, 0x8, 0x84, 0x1c, 0x88, 0xe1, + 0x10, 0x42, 0x20, 0x84, 0x81, 0x1, 0x2, 0x2, + 0x4, 0xff, 0xf8, 0x0, 0x0, + + /* U+62CA "拊" */ + 0x1, 0x0, 0x44, 0x10, 0x88, 0x27, 0xd7, 0xf2, + 0x20, 0x84, 0xc1, 0x8, 0x92, 0x11, 0x24, 0x7a, + 0x29, 0x44, 0x50, 0x88, 0x21, 0x10, 0x42, 0x20, + 0x84, 0x41, 0x18, 0x8e, 0x0, + + /* U+62CC "拌" */ + 0x20, 0x20, 0x20, 0x22, 0x22, 0x22, 0xf9, 0x24, + 0x21, 0x24, 0x20, 0x20, 0x23, 0xfe, 0x38, 0x20, + 0xe0, 0x20, 0x27, 0xff, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0xe0, 0x20, + + /* U+62CD "拍" */ + 0x0, 0x40, 0x81, 0x2, 0x3f, 0xfe, 0x81, 0x22, + 0x4, 0x88, 0x12, 0x20, 0x4e, 0xff, 0xe2, 0x4, + 0x88, 0x12, 0x20, 0x48, 0x81, 0x22, 0x4, 0x8f, + 0xfe, 0x20, 0x40, + + /* U+62CE "拎" */ + 0x20, 0x40, 0x40, 0xc0, 0x82, 0x47, 0xcc, 0x42, + 0x30, 0x44, 0x88, 0x48, 0x8, 0x14, 0x0, 0x73, + 0xfd, 0x40, 0x18, 0x80, 0x61, 0x8, 0x82, 0xe, + 0x4, 0xc, 0x38, 0x4, 0x0, + + /* U+62D0 "拐" */ + 0x20, 0x0, 0x8f, 0xf2, 0x20, 0x7e, 0x81, 0x22, + 0x4, 0x8f, 0xf2, 0x8, 0xe, 0x20, 0xe7, 0xfc, + 0x82, 0x12, 0x8, 0x48, 0x61, 0x21, 0x4, 0x88, + 0x1e, 0xc7, 0xc0, + + /* U+62D2 "拒" */ + 0x20, 0x0, 0x8f, 0xf2, 0x20, 0x3e, 0x80, 0x22, + 0x0, 0x8f, 0xf2, 0x20, 0x4a, 0x81, 0xf2, 0x4, + 0x8f, 0xf2, 0x20, 0x8, 0x80, 0x22, 0x0, 0x8f, + 0xfe, 0x0, 0x0, + + /* U+62D3 "拓" */ + 0x20, 0x0, 0x47, 0xfc, 0x81, 0x7, 0xc2, 0x2, + 0x8, 0x4, 0x10, 0x8, 0x40, 0x1c, 0xfe, 0x63, + 0x5, 0x4a, 0x8, 0xa4, 0x11, 0x8, 0x22, 0x10, + 0x44, 0x3f, 0xb8, 0x41, 0x0, + + /* U+62D4 "拔" */ + 0x21, 0x20, 0x42, 0x20, 0x84, 0x7, 0xbf, 0xe2, + 0x20, 0x4, 0x40, 0x8, 0xfe, 0x19, 0x4, 0xe5, + 0x18, 0x4a, 0x20, 0x92, 0x81, 0x43, 0x3, 0xe, + 0x6, 0x23, 0x33, 0x81, 0x0, + + /* U+62D6 "拖" */ + 0x21, 0x0, 0x21, 0x0, 0x21, 0xfe, 0xfa, 0x0, + 0x24, 0x40, 0x20, 0x40, 0x22, 0x5c, 0x3b, 0xe4, + 0xe6, 0x44, 0x22, 0x44, 0x22, 0x58, 0x22, 0x43, + 0x22, 0x2, 0x22, 0x2, 0x61, 0xfe, + + /* U+62D7 "拗" */ + 0x42, 0x21, 0x10, 0x84, 0x42, 0x3e, 0x1f, 0x49, + 0x25, 0x7c, 0x94, 0x22, 0x5c, 0x89, 0xc4, 0x25, + 0x14, 0x94, 0x94, 0x57, 0xd1, 0x40, 0x45, 0x2, + 0x1c, 0xb, 0x80, + + /* U+62D8 "拘" */ + 0x21, 0x0, 0x42, 0x0, 0x8f, 0xff, 0xd0, 0x12, + 0x40, 0x24, 0x7c, 0x48, 0x88, 0x9d, 0x11, 0xe2, + 0x22, 0x44, 0x44, 0x8f, 0x89, 0x0, 0x12, 0x0, + 0x24, 0x0, 0xf8, 0x1f, 0x0, + + /* U+62D9 "拙" */ + 0x20, 0x20, 0x80, 0x82, 0x22, 0x7e, 0x89, 0x22, + 0x24, 0x88, 0x92, 0x3f, 0xca, 0x8, 0xf0, 0x20, + 0x88, 0x92, 0x22, 0x48, 0x89, 0x22, 0x24, 0x8f, + 0xf6, 0x0, 0x40, + + /* U+62DA "拚" */ + 0x20, 0x60, 0x40, 0x80, 0x82, 0x27, 0xd8, 0x22, + 0x3f, 0xe4, 0x0, 0x48, 0x0, 0x14, 0x84, 0xf1, + 0x8, 0x4f, 0xfc, 0x84, 0x21, 0x8, 0x42, 0x30, + 0x84, 0x41, 0x39, 0x2, 0x0, + + /* U+62DB "招" */ + 0x20, 0x0, 0x9f, 0xf2, 0x8, 0x7e, 0x21, 0x21, + 0x84, 0x84, 0x12, 0x27, 0x8b, 0x0, 0xf3, 0xfc, + 0x88, 0x12, 0x20, 0x48, 0x81, 0x22, 0x4, 0x8f, + 0xfe, 0x20, 0x40, + + /* U+62DC "拜" */ + 0x0, 0x1, 0xfb, 0xf8, 0x40, 0x80, 0x81, 0x1, + 0x1f, 0xdf, 0x84, 0x4, 0x8, 0x8, 0xfe, 0x10, + 0x21, 0xf8, 0x40, 0x80, 0x81, 0x1f, 0xf2, 0x2, + 0x8, 0x4, 0x20, 0x8, 0x0, + + /* U+62DF "拟" */ + 0x20, 0x88, 0x45, 0x10, 0x89, 0x27, 0xd2, 0x42, + 0x20, 0x84, 0x41, 0x8, 0x82, 0x1d, 0x4, 0xe2, + 0x8, 0x45, 0x20, 0x8a, 0x41, 0x19, 0x42, 0x22, + 0x44, 0x8, 0xf8, 0x20, 0x80, + + /* U+62E2 "拢" */ + 0x21, 0x90, 0x21, 0x8, 0x21, 0x4, 0xfb, 0xfe, + 0x21, 0x20, 0x21, 0x20, 0x21, 0x24, 0x39, 0x24, + 0xe1, 0x28, 0x22, 0x30, 0x22, 0x30, 0x22, 0x61, + 0x24, 0xa2, 0x25, 0x22, 0xe8, 0x3e, + + /* U+62E3 "拣" */ + 0x0, 0x80, 0x41, 0x0, 0x9f, 0xff, 0xc8, 0x2, + 0x10, 0x4, 0xfc, 0x8, 0x88, 0xd, 0x10, 0xe3, + 0xfe, 0x40, 0x40, 0x84, 0xa1, 0x11, 0x22, 0x62, + 0x44, 0x84, 0x78, 0x38, 0x0, + + /* U+62E5 "拥" */ + 0x0, 0x1, 0x1f, 0xf4, 0x44, 0x7d, 0x11, 0x44, + 0x45, 0x1f, 0xf4, 0x44, 0x55, 0x11, 0xe4, 0x45, + 0x1f, 0xf4, 0x84, 0x52, 0x11, 0x48, 0x45, 0x21, + 0x1d, 0x5, 0xc0, + + /* U+62E6 "拦" */ + 0x22, 0x8, 0x42, 0x20, 0x80, 0x47, 0xff, 0xe2, + 0x0, 0x4, 0x0, 0x8, 0x0, 0x1c, 0x0, 0xe3, + 0xfc, 0x40, 0x0, 0x80, 0x1, 0x0, 0x2, 0x0, + 0x4, 0xff, 0xf8, 0x0, 0x0, + + /* U+62E7 "拧" */ + 0x20, 0x40, 0x40, 0x40, 0x8f, 0xff, 0xd0, 0x12, + 0x20, 0x24, 0x0, 0x8, 0x0, 0x15, 0xff, 0x70, + 0x21, 0x40, 0x40, 0x80, 0x81, 0x1, 0x2, 0x2, + 0x4, 0x4, 0x38, 0x70, 0x0, + + /* U+62E8 "拨" */ + 0x20, 0x0, 0x22, 0x48, 0x22, 0x46, 0xfa, 0x40, + 0x23, 0xfe, 0x20, 0x80, 0x20, 0x80, 0x20, 0xfe, + 0x38, 0x82, 0xe1, 0x84, 0x21, 0x44, 0x22, 0x28, + 0x24, 0x30, 0x24, 0x30, 0x28, 0xcc, 0xe3, 0x2, + + /* U+62E9 "择" */ + 0x20, 0x0, 0x27, 0xfe, 0x21, 0x4, 0xf8, 0x88, + 0x20, 0x50, 0x20, 0xf8, 0x27, 0x7, 0x18, 0x20, + 0xe3, 0xfe, 0x20, 0x20, 0x20, 0x20, 0x27, 0xff, + 0x20, 0x20, 0x20, 0x20, 0x60, 0x20, + + /* U+62EC "括" */ + 0x20, 0x0, 0x47, 0xf8, 0x81, 0x7, 0xc2, 0x2, + 0x4, 0x4, 0xff, 0xc8, 0x10, 0x1c, 0x20, 0xe3, + 0xfc, 0x44, 0x8, 0x88, 0x11, 0x10, 0x22, 0x20, + 0x44, 0x7f, 0xb8, 0x81, 0x0, + + /* U+62ED "拭" */ + 0x20, 0x10, 0x40, 0x28, 0x80, 0x47, 0xdf, 0xf2, + 0x1, 0x4, 0x2, 0x8, 0x4, 0x1d, 0xf8, 0xe0, + 0x90, 0x41, 0x20, 0x82, 0x41, 0x4, 0x92, 0xe, + 0xa4, 0xe1, 0x78, 0x1, 0x0, + + /* U+62EE "拮" */ + 0x20, 0x20, 0x20, 0x20, 0x23, 0xff, 0xf8, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x23, 0xfe, 0x38, 0x0, + 0xe0, 0x0, 0x23, 0xfe, 0x22, 0x2, 0x22, 0x2, + 0x22, 0x2, 0x23, 0xfe, 0xe2, 0x2, + + /* U+62EF "拯" */ + 0x20, 0x0, 0x47, 0xf8, 0x80, 0x37, 0xc0, 0x82, + 0x2, 0x4, 0xf6, 0xc8, 0x4e, 0x1c, 0x98, 0xe1, + 0x28, 0x44, 0x48, 0x90, 0x89, 0x6, 0x2, 0x0, + 0x4, 0xff, 0xf8, 0x0, 0x0, + + /* U+62F1 "拱" */ + 0x21, 0x8, 0x21, 0x8, 0x21, 0x8, 0xfb, 0xfe, + 0x21, 0x8, 0x21, 0x8, 0x21, 0x8, 0x39, 0x8, + 0xe1, 0x8, 0x27, 0xff, 0x20, 0x0, 0x20, 0x90, + 0x21, 0xc, 0x22, 0x6, 0xe4, 0x3, + + /* U+62F3 "拳" */ + 0x0, 0x0, 0x22, 0x20, 0x24, 0x83, 0xff, 0xe0, + 0x20, 0x0, 0x40, 0x3f, 0xff, 0x82, 0x10, 0x1f, + 0xf9, 0xc2, 0xc, 0x7f, 0xc0, 0x8, 0x7, 0xff, + 0xc0, 0x20, 0x0, 0x40, 0x3, 0x80, + + /* U+62F4 "拴" */ + 0x20, 0x40, 0x41, 0x40, 0x82, 0x87, 0xc8, 0x82, + 0x20, 0x84, 0x80, 0xc8, 0xfe, 0x10, 0x20, 0xf8, + 0x40, 0x47, 0xf8, 0x81, 0x1, 0x2, 0x2, 0x4, + 0x4, 0xff, 0xf8, 0x0, 0x0, + + /* U+62F6 "拶" */ + 0x20, 0x12, 0x42, 0x48, 0x89, 0x27, 0xd2, 0x42, + 0x12, 0x44, 0x0, 0x4a, 0x20, 0x78, 0xfe, 0xa2, + 0x4, 0x4e, 0x8, 0x92, 0x21, 0x0, 0x82, 0x6, + 0x4, 0x38, 0x3b, 0xc0, 0x0, + + /* U+62F7 "拷" */ + 0x20, 0x40, 0x20, 0x40, 0x20, 0x44, 0x23, 0xf4, + 0xf8, 0x48, 0x27, 0xff, 0x20, 0x20, 0x20, 0xc0, + 0x39, 0xfe, 0xe7, 0x0, 0x21, 0x0, 0x21, 0xfc, + 0x20, 0x4, 0x20, 0x4, 0x20, 0x4, 0xe0, 0x78, + + /* U+62FC "拼" */ + 0x1, 0x4, 0x20, 0x88, 0x20, 0x8, 0xfb, 0xfe, + 0x21, 0x8, 0x21, 0x8, 0x21, 0x8, 0x79, 0x8, + 0xa7, 0xff, 0x21, 0x8, 0x21, 0x8, 0x21, 0x8, + 0x21, 0x8, 0x22, 0x8, 0x64, 0x8, + + /* U+62FD "拽" */ + 0x20, 0x40, 0x40, 0x80, 0x8f, 0xf7, 0xe2, 0x22, + 0x44, 0x44, 0x7f, 0x89, 0x11, 0x3e, 0x22, 0xa3, + 0xfc, 0x40, 0x80, 0x80, 0xb1, 0x1, 0x82, 0xe, + 0x25, 0xe2, 0x78, 0x3, 0x0, + + /* U+62FE "拾" */ + 0x20, 0x40, 0x20, 0x60, 0x20, 0x90, 0xf9, 0xc, + 0x26, 0x6, 0x25, 0xf8, 0x20, 0x0, 0x38, 0x0, + 0xe3, 0xfc, 0x22, 0x4, 0x22, 0x4, 0x22, 0x4, + 0x22, 0x4, 0x23, 0xfc, 0x62, 0x4, + + /* U+62FF "拿" */ + 0x1, 0x0, 0x5, 0x0, 0x21, 0x81, 0xfe, 0xec, + 0x0, 0x23, 0xfe, 0x8, 0x4, 0xf, 0xf8, 0x0, + 0x0, 0xff, 0xf0, 0x4, 0x3, 0xff, 0xc0, 0x10, + 0x1f, 0xff, 0xc0, 0x40, 0x7, 0x80, + + /* U+6301 "持" */ + 0x0, 0x20, 0x20, 0x20, 0x20, 0x20, 0x23, 0xfe, + 0xf8, 0x20, 0x20, 0x20, 0x27, 0xff, 0x20, 0x0, + 0x38, 0x8, 0xe7, 0xff, 0x20, 0x8, 0x23, 0x8, + 0x21, 0x88, 0x20, 0x88, 0x20, 0x8, 0xe0, 0x78, + + /* U+6302 "挂" */ + 0x20, 0x40, 0x40, 0x80, 0x9f, 0xf7, 0xc2, 0x2, + 0x4, 0x4, 0x8, 0x9, 0xff, 0x9c, 0x20, 0xe0, + 0x40, 0x4f, 0xf8, 0x81, 0x1, 0x2, 0x2, 0x4, + 0x4, 0xff, 0xf8, 0x0, 0x0, + + /* U+6307 "指" */ + 0x22, 0x0, 0x44, 0x3c, 0x8f, 0x87, 0xd0, 0x12, + 0x20, 0x24, 0x7f, 0x88, 0x0, 0x1d, 0xfe, 0xe2, + 0x4, 0x44, 0x8, 0x8f, 0xf1, 0x10, 0x22, 0x20, + 0x44, 0x7f, 0xb8, 0x81, 0x0, + + /* U+6308 "挈" */ + 0x8, 0x0, 0xff, 0xf8, 0x20, 0x93, 0xf1, 0x20, + 0x84, 0x4f, 0xd8, 0x82, 0x26, 0x3f, 0xfe, 0x0, + 0x80, 0xff, 0xf8, 0x2, 0x0, 0x4, 0xf, 0xff, + 0xe0, 0x10, 0x3, 0xc0, 0x0, + + /* U+6309 "按" */ + 0x0, 0x40, 0x40, 0x40, 0x8f, 0xff, 0xd0, 0x12, + 0x24, 0x24, 0x8, 0x8, 0x20, 0x15, 0xff, 0xf1, + 0x8, 0x42, 0x10, 0x84, 0x41, 0x6, 0x82, 0x7, + 0x4, 0x31, 0x99, 0x80, 0x80, + + /* U+630E "挎" */ + 0x0, 0x20, 0x20, 0x20, 0x23, 0xfe, 0xf8, 0x88, + 0x21, 0xc, 0x22, 0xfe, 0x20, 0x0, 0x18, 0x0, + 0xf7, 0xfe, 0xa1, 0x0, 0x21, 0xfc, 0x20, 0x4, + 0x20, 0x4, 0x20, 0x4, 0x60, 0x78, + + /* U+6311 "挑" */ + 0x20, 0xa0, 0x41, 0x40, 0x82, 0x97, 0xd5, 0x22, + 0x2a, 0x84, 0x55, 0x8, 0x28, 0x14, 0x50, 0xf3, + 0xb8, 0x49, 0x4c, 0x82, 0x81, 0x9, 0x12, 0x12, + 0x24, 0x44, 0x7b, 0xf, 0x0, + + /* U+6316 "挖" */ + 0x20, 0x20, 0x40, 0x40, 0x8f, 0xff, 0xd0, 0x12, + 0x28, 0xa4, 0x20, 0x88, 0x80, 0x94, 0xfc, 0x70, + 0x11, 0x40, 0x40, 0x83, 0x1, 0xc, 0x12, 0x10, + 0x24, 0x40, 0x78, 0x7f, 0x0, + + /* U+631A "挚" */ + 0x10, 0x40, 0xfb, 0xf0, 0x41, 0x23, 0xec, 0x41, + 0xc, 0xa2, 0x25, 0x5c, 0x81, 0x0, 0x0, 0x3f, + 0xf8, 0x7f, 0xf8, 0x2, 0x0, 0x4, 0xf, 0xff, + 0xe0, 0x10, 0x1, 0xc0, 0x0, + + /* U+631B "挛" */ + 0x1, 0x0, 0x2, 0x3, 0xff, 0xf8, 0x22, 0x3, + 0x45, 0x88, 0x88, 0xa0, 0x0, 0xbf, 0xfe, 0x1, + 0x0, 0x2, 0x1, 0xff, 0xe0, 0x8, 0xf, 0xff, + 0xe0, 0x20, 0x0, 0x40, 0x7, 0x80, + + /* U+631D "挝" */ + 0x24, 0x8, 0x44, 0x10, 0x88, 0x27, 0x87, 0xf2, + 0x0, 0x85, 0xd1, 0x8, 0x92, 0x19, 0x24, 0xe2, + 0x8, 0x44, 0x10, 0x88, 0x21, 0x11, 0xc2, 0x70, + 0x5, 0x30, 0x32, 0x3f, 0x80, + + /* U+631E "挞" */ + 0x24, 0x10, 0x22, 0x10, 0x22, 0x10, 0xf8, 0x10, + 0x20, 0xfe, 0x2e, 0x10, 0x22, 0x20, 0x3a, 0x38, + 0xe2, 0x28, 0x22, 0x44, 0x22, 0x42, 0x22, 0x82, + 0x26, 0x0, 0x2d, 0x80, 0x68, 0x7e, + + /* U+631F "挟" */ + 0x0, 0x40, 0x80, 0x81, 0x1f, 0xf7, 0x82, 0x4, + 0x4, 0x8, 0x49, 0x10, 0x14, 0x18, 0x20, 0xcf, + 0xfe, 0x80, 0x81, 0x2, 0x82, 0x5, 0x4, 0x11, + 0x8, 0xc1, 0x32, 0x1, 0x80, + + /* U+6320 "挠" */ + 0x20, 0x80, 0x41, 0x8, 0x87, 0xe7, 0xd4, 0x2, + 0x5, 0x4, 0x4, 0x48, 0x79, 0x9d, 0xe, 0xe0, + 0x0, 0x4f, 0xf8, 0x82, 0x41, 0x4, 0x82, 0x11, + 0x24, 0x42, 0x7b, 0x7, 0x0, + + /* U+6321 "挡" */ + 0x20, 0x40, 0x91, 0x12, 0x44, 0xfe, 0x92, 0x20, + 0x40, 0x9f, 0xf2, 0x0, 0x4e, 0x1, 0xe0, 0x4, + 0x9f, 0xf2, 0x0, 0x48, 0x1, 0x20, 0x4, 0xbf, + 0xfe, 0x0, 0x40, + + /* U+6322 "挢" */ + 0x20, 0x0, 0x23, 0xfe, 0x20, 0x40, 0xf8, 0x40, + 0x27, 0xfe, 0x20, 0x90, 0x20, 0x90, 0x29, 0x8, + 0x73, 0x4, 0xe6, 0x2, 0x25, 0x8, 0x21, 0x8, + 0x21, 0x8, 0x22, 0x8, 0x64, 0x8, + + /* U+6323 "挣" */ + 0x1, 0x0, 0x83, 0xe1, 0x8, 0x87, 0xa1, 0x4, + 0x3f, 0xc8, 0x8, 0x90, 0x11, 0x1b, 0xff, 0xc0, + 0x44, 0x80, 0x89, 0x1f, 0xf2, 0x2, 0x24, 0x4, + 0x8, 0x8, 0x30, 0xe0, 0x0, + + /* U+6324 "挤" */ + 0x20, 0x40, 0x20, 0x20, 0x27, 0xfe, 0xf9, 0x4, + 0x21, 0x8, 0x20, 0x90, 0x20, 0x60, 0x38, 0xd8, + 0xe7, 0x6, 0x21, 0x8, 0x21, 0x8, 0x21, 0x8, + 0x23, 0x8, 0x22, 0x8, 0xec, 0x8, + + /* U+6325 "挥" */ + 0x20, 0x0, 0x23, 0xfe, 0x24, 0x2, 0xf8, 0x82, + 0x23, 0xfe, 0x21, 0x0, 0x21, 0x20, 0x3a, 0x20, + 0xe3, 0xfe, 0x20, 0x20, 0x20, 0x20, 0x27, 0xff, + 0x20, 0x20, 0x20, 0x20, 0xe0, 0x20, + + /* U+6328 "挨" */ + 0x20, 0x60, 0x20, 0x40, 0x20, 0x88, 0xf9, 0x4, + 0x23, 0xfe, 0x21, 0x2, 0x21, 0xfe, 0x3a, 0x20, + 0xe4, 0x20, 0x20, 0x20, 0x27, 0xfe, 0x20, 0x60, + 0x20, 0x50, 0x21, 0x8c, 0x6e, 0x6, + + /* U+632A "挪" */ + 0x40, 0x0, 0x9f, 0x3d, 0x12, 0x4f, 0xa4, 0xb4, + 0x49, 0x49, 0xf2, 0x91, 0x26, 0x3a, 0x4a, 0xcf, + 0x96, 0x89, 0x25, 0x12, 0x4a, 0x24, 0x94, 0x89, + 0xc9, 0x12, 0x34, 0xc4, 0x0, + + /* U+632B "挫" */ + 0x20, 0x40, 0x40, 0x90, 0x89, 0x27, 0xd2, 0x42, + 0x35, 0x84, 0xaa, 0x89, 0x38, 0x9e, 0x20, 0xe0, + 0x40, 0x47, 0xf8, 0x81, 0x1, 0x2, 0x2, 0x4, + 0x4, 0xff, 0xf8, 0x0, 0x0, + + /* U+632F "振" */ + 0x0, 0x0, 0x23, 0xfe, 0x22, 0x0, 0x7a, 0x0, + 0x22, 0xfe, 0x22, 0x0, 0x22, 0x0, 0x2b, 0xfe, + 0x72, 0xa0, 0x22, 0xa2, 0x22, 0x9c, 0x22, 0x88, + 0x24, 0x8c, 0x24, 0xe6, 0x68, 0x80, + + /* U+6332 "挲" */ + 0x60, 0x40, 0x24, 0xb3, 0x19, 0x11, 0x22, 0x43, + 0x7, 0x8, 0xf0, 0x0, 0x0, 0x3f, 0xfe, 0x1, + 0x0, 0xff, 0xf0, 0x4, 0x7, 0xff, 0xf0, 0x10, + 0x0, 0x20, 0x3, 0x80, 0x0, + + /* U+6339 "挹" */ + 0x3, 0xfc, 0x22, 0x4, 0x22, 0x4, 0xfb, 0xfc, + 0x20, 0x0, 0x20, 0x0, 0x23, 0xfe, 0x1c, 0x42, + 0xe4, 0x42, 0x23, 0xfe, 0x24, 0x0, 0x24, 0x2, + 0x24, 0x2, 0x23, 0xfe, 0x60, 0x0, + + /* U+633A "挺" */ + 0x40, 0x1, 0x3f, 0xf4, 0x22, 0x3c, 0x88, 0x44, + 0x21, 0x20, 0x84, 0xff, 0xd4, 0x48, 0xe9, 0x21, + 0x28, 0x84, 0x6f, 0xd0, 0x80, 0x47, 0x1, 0x23, + 0xfd, 0x0, 0x0, + + /* U+633D "挽" */ + 0x41, 0x0, 0x83, 0xe1, 0x8, 0x47, 0xa1, 0x4, + 0x7f, 0xc8, 0x88, 0x95, 0x11, 0x32, 0x22, 0xc7, + 0xfc, 0x81, 0x1, 0x3, 0x2, 0xa, 0x4, 0x34, + 0x28, 0xc8, 0x76, 0xf, 0x0, + + /* U+6342 "捂" */ + 0x23, 0xfe, 0x20, 0x80, 0xf8, 0x80, 0x23, 0xfc, + 0x20, 0x84, 0x20, 0x84, 0x29, 0x4, 0x37, 0xff, + 0xe0, 0x0, 0x23, 0xfe, 0x22, 0x2, 0x22, 0x2, + 0x22, 0x2, 0x23, 0xfe, 0xe2, 0x2, + + /* U+6343 "捃" */ + 0x0, 0x1, 0x1f, 0xe4, 0x10, 0xbc, 0x42, 0x4f, + 0xfd, 0x4, 0x24, 0xff, 0x8c, 0x80, 0xc2, 0x1, + 0x1f, 0xe4, 0xa0, 0x96, 0x82, 0x42, 0x9, 0xf, + 0xec, 0x20, 0x80, + + /* U+6345 "捅" */ + 0x20, 0x0, 0x4f, 0xfc, 0x88, 0x37, 0xcf, 0x82, + 0x3f, 0xe4, 0x88, 0x49, 0x10, 0x9d, 0xff, 0xe4, + 0x42, 0x48, 0x84, 0x8f, 0xf9, 0x22, 0x12, 0x44, + 0x24, 0x88, 0x79, 0x13, 0x80, + + /* U+6346 "捆" */ + 0x20, 0x0, 0x9f, 0xf2, 0x80, 0x5a, 0x21, 0x2b, + 0xf4, 0xa2, 0x12, 0x88, 0x5e, 0x79, 0xa9, 0x94, + 0xaa, 0x12, 0x88, 0x4a, 0x21, 0x28, 0x4, 0x9f, + 0xfc, 0x80, 0x40, + + /* U+6349 "捉" */ + 0x20, 0x0, 0x47, 0xf8, 0x88, 0x17, 0xd0, 0x22, + 0x20, 0x44, 0x7f, 0x88, 0x10, 0x1c, 0x20, 0xe2, + 0x40, 0x44, 0xf8, 0x89, 0x1, 0x12, 0x2, 0x64, + 0x4, 0xa8, 0x3a, 0x3f, 0x0, + + /* U+634B "捋" */ + 0x20, 0x0, 0x4f, 0xf8, 0x80, 0x7, 0xc2, 0x22, + 0x24, 0x84, 0x41, 0x8, 0x4, 0x1c, 0x8, 0xe7, + 0xfe, 0x40, 0x20, 0x88, 0x41, 0x8, 0x82, 0x1, + 0x4, 0x2, 0x38, 0x3c, 0x0, + + /* U+634C "捌" */ + 0x40, 0x5, 0x3e, 0x14, 0xc9, 0x53, 0x25, 0xfc, + 0x95, 0x3e, 0x54, 0x21, 0x54, 0x85, 0xef, 0x95, + 0xa, 0x54, 0x29, 0x50, 0xa5, 0x44, 0x85, 0x32, + 0x1d, 0xb1, 0xc0, + + /* U+634D "捍" */ + 0x20, 0x0, 0x4f, 0xf8, 0x90, 0x17, 0xff, 0xe2, + 0x40, 0x44, 0x80, 0x89, 0xff, 0x14, 0x0, 0xf7, + 0xfc, 0x40, 0x80, 0x81, 0x1, 0x3f, 0xf2, 0x4, + 0x4, 0x8, 0x38, 0x10, 0x0, + + /* U+634E "捎" */ + 0x20, 0x20, 0x44, 0x4c, 0x84, 0x97, 0xc1, 0x2, + 0x3f, 0xe4, 0x40, 0x48, 0x80, 0x9d, 0xff, 0xe2, + 0x2, 0x44, 0x4, 0x8f, 0xf9, 0x10, 0x12, 0x20, + 0x24, 0x40, 0x78, 0x87, 0x80, + + /* U+634F "捏" */ + 0x20, 0x0, 0x4f, 0xf8, 0x90, 0x17, 0xff, 0xe2, + 0x40, 0x44, 0x80, 0x89, 0xff, 0x1c, 0x0, 0xe0, + 0x40, 0x40, 0x80, 0x9f, 0xf1, 0x2, 0x2, 0x4, + 0x4, 0xff, 0xf8, 0x0, 0x0, + + /* U+6350 "捐" */ + 0x20, 0x0, 0x43, 0xf8, 0x84, 0x17, 0xcf, 0xe2, + 0x0, 0x4, 0x0, 0x8, 0xff, 0x9d, 0x1, 0xe3, + 0xfe, 0x44, 0x4, 0x88, 0x9, 0x1f, 0xf2, 0x20, + 0x24, 0x40, 0x78, 0x87, 0x0, + + /* U+6355 "捕" */ + 0x40, 0x50, 0x80, 0x93, 0xdf, 0xf2, 0x2, 0x4, + 0x7f, 0xc8, 0x88, 0x8d, 0x11, 0x63, 0xfe, 0x44, + 0x44, 0x8f, 0xf9, 0x11, 0x12, 0x22, 0x24, 0x44, + 0x58, 0x8b, 0x80, + + /* U+635E "捞" */ + 0x20, 0x90, 0x47, 0xfc, 0x82, 0x47, 0xc4, 0x82, + 0x0, 0x4, 0x7f, 0xc8, 0x90, 0x9d, 0x21, 0xe3, + 0xfc, 0x40, 0x88, 0x82, 0x11, 0x4, 0x22, 0x18, + 0x44, 0x60, 0xbb, 0x1e, 0x0, + + /* U+635F "损" */ + 0x23, 0xfc, 0x44, 0x8, 0x88, 0x17, 0xd0, 0x22, + 0x3f, 0xc4, 0x0, 0x8, 0xff, 0x9e, 0x1, 0xe4, + 0x42, 0x48, 0x84, 0x91, 0x9, 0x3, 0x12, 0x9, + 0x4, 0x21, 0xbb, 0x80, 0x80, + + /* U+6361 "捡" */ + 0x20, 0x40, 0x41, 0x40, 0x82, 0xc7, 0xc8, 0x82, + 0x20, 0xc4, 0x80, 0xc8, 0xfe, 0x10, 0x0, 0x78, + 0x5, 0x48, 0x90, 0x89, 0x21, 0x10, 0x82, 0x1, + 0x4, 0xff, 0xd8, 0x0, 0x0, + + /* U+6362 "换" */ + 0x41, 0x80, 0x82, 0x1, 0xb, 0xc7, 0xa0, 0x84, + 0x82, 0x8, 0xff, 0x91, 0x11, 0x1a, 0x22, 0xc4, + 0x44, 0x9f, 0xfd, 0x1, 0x2, 0x5, 0x4, 0x11, + 0x8, 0xc1, 0xb2, 0x1, 0x80, + + /* U+6363 "捣" */ + 0x20, 0x40, 0x47, 0xf8, 0x88, 0x17, 0xd2, 0x22, + 0x22, 0x44, 0x47, 0x8, 0x80, 0x19, 0xff, 0x60, + 0x3, 0x49, 0x24, 0x92, 0x49, 0x24, 0x92, 0x7f, + 0x24, 0x0, 0x70, 0x1f, 0x0, + + /* U+6367 "捧" */ + 0x0, 0x0, 0x81, 0x1, 0x3f, 0xf7, 0x84, 0x4, + 0x7f, 0x88, 0x20, 0x13, 0xff, 0x29, 0x10, 0xe4, + 0x90, 0x97, 0xf9, 0x2, 0x2, 0x7f, 0xe4, 0x8, + 0x8, 0x10, 0x30, 0x20, 0x0, + + /* U+6369 "捩" */ + 0x20, 0x20, 0x40, 0x40, 0x9f, 0x77, 0xa0, 0x22, + 0x40, 0x44, 0xff, 0x8d, 0xa, 0x72, 0x10, 0x25, + 0xfc, 0x48, 0x40, 0x91, 0x81, 0x42, 0x82, 0x88, + 0x87, 0x21, 0xb5, 0x81, 0x0, + + /* U+636D "捭" */ + 0x0, 0x80, 0x8f, 0xf9, 0x11, 0x17, 0xa2, 0x24, + 0x7f, 0xc8, 0x88, 0x91, 0x11, 0x1a, 0x22, 0xc7, + 0xfc, 0x83, 0x1, 0x8, 0x82, 0xff, 0xf4, 0x2, + 0x8, 0x4, 0x30, 0x8, 0x0, + + /* U+636E "据" */ + 0x0, 0x0, 0x23, 0xfe, 0x22, 0x2, 0x7a, 0x2, + 0x23, 0xfe, 0x22, 0x0, 0x22, 0x10, 0x2b, 0xff, + 0x72, 0x10, 0x22, 0x10, 0x24, 0xfe, 0x24, 0x82, + 0x24, 0x82, 0x28, 0xfe, 0x68, 0x82, + + /* U+6371 "捱" */ + 0x40, 0x0, 0x8f, 0xfd, 0x10, 0x87, 0xa1, 0x4, + 0x5f, 0xc8, 0x84, 0x11, 0x8, 0x2a, 0xff, 0xe4, + 0x20, 0x88, 0x41, 0x17, 0xf2, 0x21, 0x4, 0x82, + 0x9, 0x7f, 0xf4, 0x0, 0x0, + + /* U+6376 "捶" */ + 0x40, 0x0, 0x8f, 0xf9, 0x1, 0x7, 0xbf, 0xe4, + 0x24, 0x88, 0x49, 0x10, 0x92, 0x2f, 0xff, 0xe2, + 0x48, 0x84, 0x91, 0x1f, 0xf2, 0x2, 0x4, 0x4, + 0x8, 0xff, 0xb0, 0x0, 0x0, + + /* U+6377 "捷" */ + 0x0, 0x20, 0x27, 0xff, 0x20, 0x20, 0x78, 0x20, + 0x23, 0xfc, 0x20, 0x24, 0x27, 0xff, 0x18, 0x24, + 0x63, 0xfc, 0x20, 0x20, 0x22, 0x20, 0x22, 0x3e, + 0x22, 0x20, 0x25, 0xa0, 0x68, 0xff, + + /* U+637A "捺" */ + 0x20, 0x40, 0x40, 0x80, 0x8f, 0xf7, 0xc4, 0x82, + 0x11, 0x4, 0x41, 0x9, 0x7d, 0x90, 0x0, 0xf8, + 0x0, 0x4f, 0xf8, 0x81, 0x1, 0x12, 0x82, 0x24, + 0x84, 0x88, 0xb8, 0x70, 0x0, + + /* U+637B "捻" */ + 0x20, 0x40, 0x41, 0x40, 0x82, 0xc7, 0xc8, 0xc2, + 0x64, 0x44, 0x0, 0x8, 0xfe, 0x1c, 0x4, 0xe0, + 0x10, 0x40, 0x80, 0x95, 0x11, 0x28, 0x22, 0x50, + 0xc4, 0x22, 0x18, 0x7c, 0x0, + + /* U+6380 "掀" */ + 0x20, 0x10, 0x4f, 0x40, 0xa0, 0xf7, 0xc2, 0x22, + 0x85, 0x44, 0xf2, 0x8a, 0x44, 0x1c, 0x88, 0xe9, + 0x10, 0x52, 0x60, 0xa4, 0xa1, 0x49, 0x42, 0x92, + 0x8a, 0x28, 0xb4, 0x61, 0x0, + + /* U+6382 "掂" */ + 0x40, 0x20, 0x80, 0x41, 0x1f, 0xff, 0xa1, 0x4, + 0x42, 0x8, 0x87, 0xd1, 0x8, 0x7a, 0x10, 0x45, + 0xfc, 0x8a, 0x9, 0x14, 0x12, 0x48, 0x24, 0x90, + 0x49, 0x3f, 0xb4, 0x41, 0x0, + + /* U+6387 "掇" */ + 0x0, 0x0, 0x8f, 0x79, 0x2, 0x17, 0xa9, 0x44, + 0x21, 0x88, 0xb4, 0x90, 0x0, 0x3b, 0xfe, 0xc1, + 0x44, 0x82, 0x49, 0x14, 0xa2, 0x10, 0xc4, 0x71, + 0x8, 0x85, 0xb6, 0x31, 0x80, + + /* U+6388 "授" */ + 0x40, 0x4, 0x8f, 0xf9, 0x0, 0x17, 0x92, 0x44, + 0x21, 0x8, 0xff, 0x92, 0x1, 0x18, 0x0, 0xc7, + 0xf8, 0x84, 0x11, 0x4, 0x42, 0xd, 0x4, 0xc, + 0x8, 0x66, 0x33, 0x3, 0x0, + + /* U+6389 "掉" */ + 0x0, 0x40, 0x40, 0xfc, 0x81, 0x7, 0xc2, 0x2, + 0x3f, 0xc4, 0x40, 0x88, 0xff, 0x15, 0x2, 0xf2, + 0x4, 0x47, 0xf8, 0x81, 0x1, 0x3f, 0xf2, 0x4, + 0x4, 0x8, 0x18, 0x10, 0x0, + + /* U+638A "掊" */ + 0x40, 0x40, 0x80, 0x81, 0x1f, 0xf7, 0x90, 0x4, + 0x11, 0x8, 0x2, 0x13, 0xff, 0xb8, 0x0, 0xc0, + 0x0, 0x87, 0xf1, 0x10, 0x22, 0x20, 0x44, 0x40, + 0x88, 0x7f, 0x31, 0x2, 0x0, + + /* U+638C "掌" */ + 0x11, 0x18, 0x1a, 0x43, 0xff, 0xfc, 0x0, 0x19, + 0xff, 0x22, 0x2, 0x7, 0xfc, 0x0, 0x0, 0x7f, + 0xfc, 0x2, 0x0, 0xff, 0xc7, 0xff, 0xf0, 0x10, + 0x0, 0x20, 0x3, 0xc0, 0x0, + + /* U+638E "掎" */ + 0x40, 0x40, 0x80, 0x81, 0x1f, 0xf7, 0x86, 0x4, + 0x13, 0x8, 0xc1, 0x13, 0xff, 0xb8, 0x4, 0xc7, + 0xc8, 0x88, 0x91, 0x11, 0x22, 0x3e, 0x44, 0x0, + 0x88, 0x1, 0x30, 0x1e, 0x0, + + /* U+638F "掏" */ + 0x2, 0x0, 0x84, 0x1, 0x1f, 0xff, 0xe8, 0x14, + 0x3f, 0x28, 0x90, 0x51, 0x20, 0xa8, 0x41, 0xef, + 0xfa, 0x89, 0x25, 0x12, 0x4a, 0x3f, 0x94, 0x0, + 0x28, 0x0, 0xb0, 0x3e, 0x0, + + /* U+6390 "掐" */ + 0x20, 0x80, 0x84, 0x2, 0x1f, 0xbe, 0x82, 0x24, + 0x10, 0x80, 0x2, 0x73, 0xcd, 0x1, 0xe4, 0x4, + 0x90, 0x12, 0x7b, 0xc9, 0x1, 0x24, 0x4, 0x9f, + 0xfc, 0x40, 0x40, + + /* U+6392 "排" */ + 0x41, 0x20, 0x82, 0x41, 0x4, 0x87, 0xf9, 0xe4, + 0x12, 0x8, 0x24, 0x10, 0x48, 0x3f, 0x9e, 0xc1, + 0x20, 0x82, 0x41, 0x3c, 0xfa, 0x9, 0x4, 0x12, + 0x8, 0x24, 0x30, 0x48, 0x0, + + /* U+6396 "掖" */ + 0x20, 0x20, 0x20, 0x20, 0x23, 0xfe, 0xf9, 0x20, + 0x21, 0x20, 0x22, 0x3e, 0x22, 0x42, 0x3e, 0x52, + 0xe6, 0xcc, 0x22, 0x24, 0x22, 0x28, 0x22, 0x10, + 0x22, 0x38, 0x22, 0x46, 0xe2, 0x82, + + /* U+6398 "掘" */ + 0x40, 0x0, 0x8f, 0xfd, 0x20, 0xf, 0xbf, 0xf4, + 0x80, 0x9, 0x4, 0x16, 0x49, 0x74, 0x92, 0x49, + 0xfc, 0x90, 0x41, 0x20, 0x82, 0x51, 0x14, 0xa2, + 0x2a, 0x7f, 0xf4, 0x0, 0x80, + + /* U+63A0 "掠" */ + 0x40, 0x40, 0x80, 0x81, 0x1f, 0xff, 0x80, 0x4, + 0x3f, 0xc8, 0x40, 0x90, 0x81, 0x39, 0x2, 0xc3, + 0xfc, 0x80, 0x81, 0x1, 0x22, 0x12, 0x44, 0x44, + 0x49, 0x8, 0x70, 0x70, 0x0, + + /* U+63A2 "探" */ + 0x0, 0x0, 0x8f, 0xf9, 0x10, 0x17, 0xa1, 0xa4, + 0x11, 0x8, 0x41, 0x11, 0x10, 0x3b, 0xff, 0xc0, + 0xe0, 0x82, 0xc1, 0x5, 0x42, 0x12, 0x44, 0x44, + 0x49, 0x8, 0x30, 0x10, 0x0, + + /* U+63A3 "掣" */ + 0x8, 0x4, 0xfe, 0x8a, 0x21, 0x17, 0xfe, 0x20, + 0x84, 0x4f, 0xe0, 0x92, 0x41, 0x5, 0x1c, 0xff, + 0xfc, 0x2, 0x1, 0xff, 0xe0, 0x8, 0xf, 0xff, + 0xe0, 0x20, 0x3, 0x80, 0x0, + + /* U+63A5 "接" */ + 0x20, 0x40, 0x81, 0x2, 0x7f, 0xfc, 0x86, 0x21, + 0x10, 0xbf, 0xf2, 0x0, 0x1c, 0x40, 0xaf, 0xfc, + 0x88, 0x42, 0x21, 0x8, 0xc8, 0x20, 0xe0, 0x8e, + 0x6c, 0xc0, 0x40, + + /* U+63A7 "控" */ + 0x20, 0x0, 0x40, 0x40, 0x8f, 0xff, 0xd0, 0x12, + 0x20, 0x24, 0x11, 0x8, 0xc1, 0xbc, 0x0, 0xa3, + 0xfe, 0x40, 0x40, 0x80, 0x81, 0x1, 0x2, 0x2, + 0x4, 0xff, 0xf8, 0x0, 0x0, + + /* U+63A8 "推" */ + 0x20, 0x40, 0x21, 0x20, 0x22, 0x0, 0xfb, 0xfe, + 0x26, 0x20, 0x2a, 0x20, 0x23, 0xfe, 0x3a, 0x20, + 0xe2, 0x20, 0x23, 0xfe, 0x22, 0x20, 0x22, 0x20, + 0x22, 0x20, 0x23, 0xff, 0xe2, 0x0, + + /* U+63A9 "掩" */ + 0x20, 0x40, 0x20, 0x80, 0x27, 0xfe, 0xf9, 0x10, + 0x22, 0xc, 0x24, 0x43, 0x23, 0xfc, 0x1a, 0x44, + 0xe3, 0xfc, 0x22, 0x44, 0x22, 0x44, 0x23, 0xfc, + 0x22, 0x41, 0x20, 0x41, 0xe0, 0x7e, + + /* U+63AA "措" */ + 0x20, 0x88, 0x20, 0x88, 0x23, 0xfe, 0xf8, 0x88, + 0x20, 0x88, 0x27, 0xff, 0x20, 0x0, 0x38, 0x0, + 0xe3, 0xfe, 0x22, 0x2, 0x23, 0xfe, 0x22, 0x2, + 0x22, 0x2, 0x23, 0xfe, 0xe2, 0x2, + + /* U+63AC "掬" */ + 0x21, 0x0, 0x88, 0x2, 0x7f, 0xfd, 0x1, 0x28, + 0x84, 0x92, 0x92, 0x2a, 0x4c, 0x21, 0xe7, 0xf4, + 0x86, 0x12, 0x2c, 0x49, 0x2d, 0x28, 0x84, 0x82, + 0x1c, 0x7, 0x80, + + /* U+63AD "掭" */ + 0x0, 0x0, 0x8f, 0xf9, 0x2, 0x7, 0x84, 0x4, + 0xff, 0xe8, 0x24, 0x14, 0x84, 0x32, 0x4, 0xc8, + 0x86, 0x81, 0x11, 0x12, 0xa2, 0x25, 0x24, 0x89, + 0x48, 0x10, 0x30, 0xe0, 0x0, + + /* U+63AE "掮" */ + 0x40, 0x41, 0x1f, 0xf4, 0x40, 0x7d, 0x1, 0x47, + 0xfd, 0x10, 0x5, 0x5f, 0xdd, 0x41, 0xc5, 0x5, + 0x17, 0xf4, 0xd0, 0x52, 0x7f, 0x49, 0x5, 0x44, + 0x1d, 0x11, 0xc0, + + /* U+63B0 "掰" */ + 0xf5, 0x3d, 0x14, 0x44, 0x49, 0x12, 0x24, 0xf8, + 0x7d, 0x0, 0x44, 0xf9, 0x11, 0x24, 0xf4, 0xbd, + 0x12, 0x44, 0x49, 0x12, 0x24, 0x48, 0x92, 0x6e, + 0xc0, + + /* U+63B3 "掳" */ + 0x40, 0x41, 0x1, 0xf4, 0x4, 0x3d, 0xff, 0x44, + 0x45, 0x11, 0xc4, 0x5c, 0x5d, 0x1e, 0xc4, 0x1, + 0x11, 0x4, 0x7f, 0xd2, 0x11, 0x48, 0xc5, 0x26, + 0x1d, 0x33, 0x80, + + /* U+63B4 "掴" */ + 0x20, 0x0, 0x9f, 0xf2, 0x80, 0x7e, 0xfd, 0x28, + 0x84, 0xa2, 0x12, 0xbf, 0x4e, 0x21, 0xe8, 0xa4, + 0xa2, 0x12, 0xbf, 0x4a, 0x1, 0x28, 0x5, 0x1f, + 0xfc, 0x80, 0x40, + + /* U+63B7 "掷" */ + 0x28, 0x80, 0x49, 0x78, 0x94, 0x93, 0x7d, 0x22, + 0x22, 0x84, 0x45, 0xc, 0x8a, 0x77, 0xd2, 0x22, + 0x22, 0x44, 0x44, 0x8c, 0x89, 0x25, 0x12, 0x4a, + 0xc5, 0x4, 0x34, 0x8, 0x0, + + /* U+63B8 "掸" */ + 0x2, 0x8, 0x82, 0x21, 0x1f, 0xf7, 0xa2, 0x24, + 0x44, 0x48, 0xff, 0x91, 0x11, 0x7a, 0x22, 0xc7, + 0xfc, 0x80, 0x81, 0x1, 0x2, 0x7f, 0xf4, 0x4, + 0x8, 0x8, 0x30, 0x10, 0x0, + + /* U+63BA "掺" */ + 0x20, 0x80, 0x42, 0x20, 0x98, 0x27, 0xbf, 0xe2, + 0x8, 0x5, 0xff, 0xc8, 0x4c, 0x39, 0x2c, 0xac, + 0x86, 0x46, 0x60, 0x81, 0x1, 0x1c, 0x42, 0x43, + 0x8, 0x38, 0x33, 0x80, 0x0, + + /* U+63BC "掼" */ + 0x27, 0xfc, 0x48, 0x88, 0x91, 0x17, 0xff, 0xf2, + 0x44, 0x44, 0x88, 0x89, 0xff, 0x18, 0x0, 0xe7, + 0xfc, 0x50, 0x8, 0xa1, 0x11, 0x42, 0x22, 0xf, + 0x4, 0x31, 0xb3, 0x80, 0x80, + + /* U+63BE "掾" */ + 0x21, 0x0, 0x43, 0xf0, 0x88, 0x27, 0xdf, 0xc2, + 0x0, 0x84, 0xff, 0xc8, 0x30, 0x1d, 0xc6, 0xe4, + 0xf0, 0x42, 0x50, 0x99, 0xa1, 0x5, 0x22, 0x32, + 0x44, 0x84, 0x58, 0x70, 0x0, + + /* U+63C4 "揄" */ + 0x40, 0x40, 0x81, 0x41, 0x6, 0x47, 0x90, 0x44, + 0x5f, 0x69, 0x0, 0x11, 0xe1, 0x22, 0x4a, 0xe4, + 0x94, 0x8f, 0x29, 0x12, 0x52, 0x3c, 0xa4, 0x48, + 0x48, 0x90, 0xb1, 0xe6, 0x0, + + /* U+63C6 "揆" */ + 0x0, 0x40, 0x8f, 0x91, 0x4, 0xc7, 0xa9, 0x24, + 0x21, 0x88, 0xc1, 0x13, 0xfd, 0x20, 0x20, 0xe0, + 0x40, 0x9f, 0xf9, 0x2, 0x2, 0x6, 0x4, 0x12, + 0x8, 0xc3, 0x37, 0x1, 0x0, + + /* U+63C9 "揉" */ + 0x23, 0xfc, 0x20, 0x8, 0x20, 0xd0, 0xf8, 0x30, + 0x23, 0xfe, 0x20, 0x62, 0x21, 0xa4, 0x3e, 0x24, + 0xe0, 0x40, 0x27, 0xfe, 0x20, 0xe0, 0x20, 0xd0, + 0x23, 0x4c, 0x2c, 0x42, 0xe0, 0x40, + + /* U+63CD "揍" */ + 0x0, 0x80, 0x82, 0x2, 0xff, 0xc8, 0x20, 0xf0, + 0x80, 0x9f, 0xe2, 0x10, 0xb, 0xff, 0x32, 0x23, + 0x9f, 0xc3, 0x88, 0xcb, 0xfe, 0x20, 0x80, 0x87, + 0x4, 0x33, 0x33, 0x2, + + /* U+63CE "揎" */ + 0x40, 0x40, 0x9f, 0xfd, 0x20, 0xf, 0x80, 0x4, + 0x7f, 0xc8, 0x0, 0x11, 0xff, 0x1a, 0x2, 0xe4, + 0x5, 0x8f, 0xf9, 0x10, 0x12, 0x3f, 0xe4, 0x0, + 0x9, 0xff, 0xf0, 0x0, 0x0, + + /* U+63CF "描" */ + 0x41, 0x10, 0x82, 0x21, 0x3f, 0xff, 0x88, 0x84, + 0x11, 0x8, 0xff, 0x91, 0x11, 0x3a, 0x22, 0xc4, + 0x44, 0x8f, 0xf9, 0x11, 0x12, 0x22, 0x24, 0x44, + 0x48, 0xff, 0xb1, 0x1, 0x0, + + /* U+63D0 "提" */ + 0x20, 0x0, 0x47, 0xf8, 0x88, 0x17, 0xdf, 0xe2, + 0x20, 0x44, 0x7f, 0x86, 0x0, 0x33, 0xff, 0xa0, + 0x40, 0x44, 0x80, 0x89, 0xf1, 0x12, 0x2, 0x54, + 0x5, 0x1f, 0xda, 0x0, 0x0, + + /* U+63D2 "插" */ + 0x20, 0x0, 0x4f, 0xf8, 0x81, 0x7, 0x82, 0x2, + 0xff, 0xe4, 0x8, 0x8, 0x10, 0x1b, 0xbe, 0xe4, + 0x44, 0x48, 0x88, 0x9d, 0xf1, 0x22, 0x22, 0x44, + 0x44, 0xff, 0xb1, 0x1, 0x0, + + /* U+63D6 "揖" */ + 0x23, 0xfc, 0x44, 0x8, 0x88, 0x17, 0xdf, 0xe2, + 0x0, 0x4, 0xff, 0xc8, 0x81, 0x3d, 0xfe, 0xa2, + 0x4, 0x44, 0x8, 0x8f, 0xf1, 0x10, 0x22, 0x7f, + 0xe4, 0x0, 0xb8, 0x1, 0x0, + + /* U+63DE "揞" */ + 0x40, 0x40, 0x80, 0x41, 0x1f, 0xff, 0x88, 0x44, + 0x9, 0x8, 0xff, 0xd0, 0x0, 0x38, 0x0, 0xc3, + 0xfc, 0x84, 0x9, 0xf, 0xf2, 0x10, 0x24, 0x20, + 0x48, 0x7f, 0xb0, 0x81, 0x0, + + /* U+63E0 "揠" */ + 0x0, 0x0, 0x8f, 0xfd, 0x10, 0x7, 0xa7, 0xe4, + 0x48, 0x48, 0x9f, 0x91, 0x3f, 0x3a, 0x10, 0xc5, + 0xfe, 0x89, 0x11, 0x11, 0xc2, 0x27, 0x64, 0x50, + 0x8, 0xff, 0xf0, 0x0, 0x0, + + /* U+63E1 "握" */ + 0x40, 0x0, 0x8f, 0xf9, 0x10, 0x17, 0xbf, 0xe4, + 0x40, 0x8, 0xbf, 0xd5, 0x10, 0x72, 0x44, 0x45, + 0xfc, 0x88, 0x41, 0x10, 0x82, 0x2f, 0xe4, 0x82, + 0xb, 0x7f, 0xf4, 0x0, 0x0, + + /* U+63E3 "揣" */ + 0x40, 0x40, 0x88, 0x89, 0x11, 0x17, 0xbf, 0xe4, + 0x0, 0x8, 0x0, 0x13, 0xff, 0xb8, 0x40, 0xc7, + 0xfc, 0x89, 0x49, 0x12, 0x92, 0x25, 0x24, 0x4a, + 0x48, 0x94, 0xb1, 0x7, 0x0, + + /* U+63E9 "揩" */ + 0x24, 0x20, 0x48, 0x40, 0x9e, 0xb7, 0xa1, 0x82, + 0x42, 0x24, 0xb4, 0x49, 0x8f, 0x18, 0x40, 0xe7, + 0xfc, 0x48, 0x8, 0x90, 0x11, 0x3f, 0xe2, 0x40, + 0x44, 0xff, 0xb1, 0x1, 0x0, + + /* U+63EA "揪" */ + 0x40, 0x10, 0x9e, 0x21, 0x8, 0x47, 0x92, 0x94, + 0xfd, 0x48, 0x4a, 0x91, 0x85, 0x3b, 0x88, 0xca, + 0xa8, 0x95, 0x51, 0x28, 0xa2, 0x11, 0x44, 0x24, + 0x48, 0x48, 0xf0, 0xa0, 0x80, + + /* U+63ED "揭" */ + 0x23, 0xfc, 0x44, 0x8, 0x88, 0x17, 0xdf, 0xe2, + 0x20, 0x44, 0x7f, 0x8a, 0x40, 0x39, 0xff, 0xa6, + 0x22, 0x44, 0xc4, 0x8a, 0x69, 0x10, 0x12, 0x3f, + 0xa4, 0x0, 0xb8, 0x7, 0x0, + + /* U+63F2 "揲" */ + 0x20, 0x88, 0x45, 0x10, 0xbf, 0xff, 0x94, 0x42, + 0x28, 0x84, 0x5f, 0x8, 0x80, 0x11, 0xff, 0xf0, + 0x40, 0x5f, 0xfc, 0x83, 0x81, 0xa, 0x82, 0x24, + 0x85, 0x88, 0xf0, 0x10, 0x0, + + /* U+63F4 "援" */ + 0x40, 0x0, 0x8f, 0xf9, 0x1, 0x27, 0x92, 0x44, + 0x7f, 0xc8, 0x20, 0x10, 0x40, 0x2b, 0xfe, 0xe2, + 0x0, 0x87, 0xf1, 0xc, 0x22, 0x24, 0x84, 0x86, + 0xb, 0x36, 0x31, 0x83, 0x0, + + /* U+63F6 "揶" */ + 0x20, 0x0, 0x5f, 0xf8, 0xa2, 0x97, 0xc5, 0x22, + 0xfa, 0x45, 0x15, 0xa, 0x2a, 0x1c, 0x52, 0xef, + 0xa2, 0x51, 0x44, 0xa2, 0x89, 0x47, 0x12, 0xfa, + 0xc4, 0x14, 0x30, 0x28, 0x0, + + /* U+63F8 "揸" */ + 0x40, 0x40, 0x80, 0x81, 0x1f, 0xe7, 0x8f, 0x4, + 0x25, 0x89, 0x88, 0x91, 0xfe, 0x3a, 0x4, 0xc4, + 0x8, 0x8f, 0xf1, 0x10, 0x22, 0x3f, 0xc4, 0x0, + 0x9, 0xff, 0xb0, 0x0, 0x0, + + /* U+63FD "揽" */ + 0x45, 0x21, 0x14, 0xf4, 0x54, 0x3d, 0x54, 0x45, + 0x89, 0x0, 0x4, 0x7f, 0x9d, 0x2, 0xc4, 0x89, + 0x12, 0x24, 0x4c, 0x90, 0x30, 0x41, 0x45, 0x9, + 0x1c, 0xc7, 0xc0, + + /* U+63FF "揿" */ + 0x22, 0x20, 0x24, 0x20, 0x27, 0xbe, 0xf4, 0x42, + 0x28, 0x52, 0x27, 0x12, 0x22, 0x10, 0x72, 0x10, + 0xaf, 0x90, 0x22, 0x28, 0x22, 0x28, 0x22, 0xac, + 0x23, 0x44, 0x26, 0x42, 0xc0, 0x82, + + /* U+6400 "搀" */ + 0x21, 0x0, 0x47, 0xe0, 0x90, 0x47, 0xbf, 0xe2, + 0x44, 0x44, 0x88, 0x89, 0xff, 0x18, 0x50, 0xe1, + 0x22, 0x4c, 0x78, 0xa6, 0x1, 0x3, 0x82, 0x40, + 0x4, 0x7c, 0x30, 0x6, 0x0, + + /* U+6401 "搁" */ + 0x24, 0x0, 0x93, 0xf2, 0x20, 0x7c, 0x3d, 0x29, + 0x24, 0xaa, 0x92, 0x84, 0x4e, 0x6d, 0xea, 0x4, + 0xa7, 0xd2, 0x91, 0x4a, 0x45, 0x29, 0xf4, 0xa0, + 0x1c, 0x83, 0xc0, + + /* U+6402 "搂" */ + 0x20, 0x40, 0x44, 0x90, 0x85, 0x47, 0xbf, 0xf2, + 0xe, 0x4, 0x2a, 0xd, 0x93, 0xb0, 0x40, 0xa0, + 0x80, 0x5f, 0xfc, 0x8c, 0x41, 0x10, 0x82, 0x1e, + 0x4, 0x36, 0x33, 0x83, 0x0, + + /* U+6405 "搅" */ + 0x24, 0x80, 0x48, 0x90, 0x89, 0x47, 0xff, 0xf2, + 0x80, 0x64, 0x0, 0x9, 0xfe, 0x1a, 0x4, 0xe4, + 0xc8, 0x49, 0x90, 0x93, 0xa1, 0x7, 0x2, 0x1e, + 0x24, 0x6c, 0x73, 0x1f, 0x0, + + /* U+640B "搋" */ + 0x20, 0xc, 0x4f, 0xf0, 0x90, 0x87, 0xa1, 0xe2, + 0x42, 0x4, 0xbf, 0xc9, 0x48, 0x9a, 0xbc, 0xe5, + 0x24, 0x4a, 0x30, 0x95, 0xe1, 0x2a, 0x42, 0x94, + 0xa5, 0x49, 0x5c, 0xa3, 0x80, + + /* U+640C "搌" */ + 0x20, 0x0, 0x4f, 0xf8, 0x90, 0x17, 0xbf, 0xe2, + 0x40, 0x4, 0x92, 0x9, 0xff, 0x1a, 0x48, 0xe4, + 0x90, 0x4f, 0xfc, 0xa5, 0x11, 0x49, 0x42, 0x91, + 0x5, 0x29, 0xb4, 0x60, 0x0, + + /* U+640F "搏" */ + 0x0, 0x41, 0x1, 0x64, 0xff, 0xfc, 0x10, 0x47, + 0xf9, 0x11, 0x24, 0x7f, 0x9d, 0xfe, 0xc4, 0x49, + 0x11, 0x4, 0xff, 0xd0, 0x4, 0x43, 0x11, 0x0, + 0x4c, 0x7, 0x0, + + /* U+6410 "搐" */ + 0x40, 0x40, 0x9f, 0xfd, 0x2, 0x7, 0x98, 0xc4, + 0x7e, 0x8, 0x12, 0x14, 0xdf, 0x32, 0xc1, 0xc7, + 0xfc, 0x88, 0x89, 0x11, 0x12, 0x3f, 0xe4, 0x44, + 0x48, 0xff, 0xb1, 0x1, 0x0, + + /* U+6413 "搓" */ + 0x21, 0x8, 0x41, 0x20, 0x8f, 0xfb, 0x82, 0x2, + 0x3f, 0xc4, 0x8, 0xa, 0x20, 0x3b, 0xff, 0xa1, + 0x0, 0x42, 0x0, 0x8f, 0xf1, 0x21, 0x2, 0x82, + 0x4, 0x7f, 0xf8, 0x0, 0x0, + + /* U+6414 "搔" */ + 0x40, 0x0, 0x9f, 0xf9, 0x11, 0x37, 0x98, 0x84, + 0xe, 0x8, 0xe3, 0x92, 0x10, 0xbb, 0xfe, 0xc4, + 0x44, 0x88, 0x89, 0x1f, 0xf2, 0x22, 0x24, 0x4, + 0x49, 0xff, 0xf0, 0x0, 0x80, + + /* U+641B "搛" */ + 0x42, 0x10, 0x82, 0x41, 0x3f, 0xff, 0x89, 0x4, + 0x7f, 0xc8, 0x24, 0x97, 0xff, 0xb0, 0x92, 0xc1, + 0x24, 0x8f, 0xf9, 0x4, 0x82, 0x19, 0x84, 0x52, + 0x8b, 0x24, 0xf0, 0x48, 0x0, + + /* U+641C "搜" */ + 0x40, 0x40, 0x8e, 0xb9, 0x11, 0x17, 0xa2, 0x24, + 0x75, 0xc8, 0x88, 0x95, 0xff, 0x30, 0x20, 0xc0, + 0x40, 0x8f, 0xf1, 0x8, 0x22, 0x8, 0x84, 0xe, + 0x8, 0x37, 0x33, 0x81, 0x80, + + /* U+641E "搞" */ + 0x0, 0x41, 0x1f, 0xf4, 0x0, 0x3c, 0xfe, 0x42, + 0x9, 0x8, 0x24, 0x3f, 0x90, 0x0, 0xf7, 0xff, + 0x10, 0x14, 0x4f, 0x51, 0x45, 0x44, 0xf5, 0x10, + 0x1c, 0x41, 0xc0, + + /* U+6420 "搠" */ + 0x24, 0x40, 0x44, 0xbc, 0x8a, 0x4f, 0xbe, 0x92, + 0x11, 0x24, 0xab, 0xc9, 0x54, 0x9a, 0xa9, 0xe5, + 0x52, 0x4f, 0xbc, 0x84, 0x49, 0x8, 0x92, 0x23, + 0x24, 0x44, 0x79, 0x1b, 0x80, + + /* U+6421 "搡" */ + 0x27, 0xfc, 0x44, 0x10, 0x87, 0xc7, 0x9c, 0xc2, + 0x40, 0x45, 0xf7, 0xc8, 0x21, 0x13, 0x9a, 0x73, + 0x1d, 0x59, 0x44, 0xbf, 0xf9, 0x7, 0x2, 0x35, + 0x85, 0x88, 0xf0, 0x10, 0x0, + + /* U+6426 "搦" */ + 0x20, 0x0, 0xbf, 0xf2, 0x8, 0x48, 0x21, 0xf7, + 0xbc, 0x90, 0x82, 0x42, 0x9, 0xef, 0xe4, 0x84, + 0x8a, 0xf2, 0x8, 0x49, 0xef, 0x28, 0x84, 0x82, + 0x1c, 0x71, 0xc0, + + /* U+642A "搪" */ + 0x40, 0x40, 0x8f, 0xf9, 0x10, 0x7, 0xa2, 0x4, + 0x5f, 0x88, 0x89, 0x11, 0x7f, 0xba, 0x24, 0xc5, + 0xf8, 0x88, 0x81, 0x17, 0xe2, 0x30, 0x44, 0xa0, + 0x89, 0x3f, 0x34, 0x82, 0x0, + + /* U+642C "搬" */ + 0x40, 0x0, 0x88, 0xf1, 0x3d, 0x27, 0x6a, 0x44, + 0xd4, 0x89, 0x31, 0x92, 0x60, 0x37, 0xfe, 0xcd, + 0x88, 0x9a, 0x91, 0x35, 0x22, 0x49, 0x85, 0x13, + 0xa, 0x29, 0x35, 0xe1, 0x0, + + /* U+642D "搭" */ + 0x42, 0x10, 0x84, 0x21, 0x1f, 0xf7, 0x92, 0x84, + 0xc, 0x8, 0x24, 0xc, 0x86, 0x63, 0xfa, 0x48, + 0x0, 0x80, 0x1, 0x1f, 0xe2, 0x20, 0x44, 0x40, + 0x88, 0xff, 0x31, 0x2, 0x0, + + /* U+6434 "搴" */ + 0x1, 0x0, 0xff, 0xfe, 0x0, 0x9, 0xff, 0xc0, + 0x42, 0x7, 0xff, 0x81, 0x8, 0x7f, 0xff, 0x8, + 0x11, 0xef, 0x9c, 0x7f, 0xc0, 0x8, 0x7, 0xff, + 0xc0, 0x20, 0x1, 0xc0, 0x0, + + /* U+643A "携" */ + 0x22, 0x20, 0x88, 0x82, 0x7f, 0xfd, 0x8, 0x2f, + 0xfc, 0x90, 0x82, 0x7f, 0xcd, 0x8, 0xe7, 0xfc, + 0x80, 0x2, 0x7f, 0x8, 0x47, 0x22, 0x4, 0x90, + 0x1d, 0x83, 0xc0, + + /* U+643D "搽" */ + 0x21, 0x8, 0x27, 0xff, 0x21, 0x8, 0xf9, 0x48, + 0x20, 0xa0, 0x20, 0x90, 0x1b, 0x4c, 0xe4, 0x43, + 0x23, 0xfc, 0x20, 0x40, 0x20, 0x40, 0x22, 0x44, + 0x24, 0x42, 0x28, 0x40, 0xe1, 0xc0, + + /* U+643F "搿" */ + 0x1, 0x3, 0xc4, 0xf2, 0x29, 0x9, 0xa4, 0x2c, + 0x51, 0xe, 0xf4, 0x1, 0x10, 0x4, 0x47, 0x93, + 0xd2, 0xf4, 0x49, 0x11, 0x24, 0x44, 0x91, 0x1e, + 0x48, 0x4b, 0x0, + + /* U+6441 "摁" */ + 0x40, 0x0, 0x8f, 0xf9, 0x11, 0x17, 0xbf, 0xa4, + 0x48, 0x48, 0x9c, 0x91, 0xc5, 0x3a, 0x2, 0xc7, + 0xfc, 0x80, 0x1, 0x1, 0x2, 0x52, 0x24, 0xa1, + 0x2a, 0x42, 0x70, 0xfc, 0x0, + + /* U+6444 "摄" */ + 0x4f, 0xfc, 0x84, 0x11, 0x8, 0x27, 0x9f, 0xc4, + 0x20, 0x88, 0x7f, 0x14, 0x82, 0x37, 0xfc, 0xc0, + 0x0, 0x9f, 0xf9, 0x5, 0x12, 0x69, 0x44, 0x31, + 0x88, 0x85, 0x36, 0x31, 0x80, + + /* U+6445 "摅" */ + 0x40, 0x80, 0x81, 0xf9, 0x2, 0x7, 0xbf, 0xe4, + 0x40, 0x48, 0x90, 0x11, 0xfd, 0x2a, 0x42, 0xe4, + 0x78, 0x88, 0x1, 0x12, 0x82, 0x54, 0x24, 0xa8, + 0xc9, 0x52, 0x74, 0x3c, 0x0, + + /* U+6446 "摆" */ + 0x47, 0xf9, 0x24, 0xa4, 0x92, 0xbe, 0x4a, 0x47, + 0xf9, 0x0, 0x4, 0x8, 0x15, 0xfe, 0xe0, 0x81, + 0x2, 0x4, 0xff, 0xd0, 0x40, 0x42, 0x11, 0x3f, + 0xec, 0x80, 0x40, + + /* U+6447 "摇" */ + 0x40, 0x0, 0x9f, 0xf9, 0x0, 0x7, 0xa2, 0x24, + 0x24, 0x88, 0x40, 0x14, 0xff, 0x32, 0x20, 0xc0, + 0x40, 0x9f, 0xfd, 0x1, 0x2, 0x2, 0x4, 0x44, + 0x48, 0xff, 0xb0, 0x1, 0x0, + + /* U+6448 "摈" */ + 0x20, 0x40, 0x4f, 0xfc, 0x90, 0xf, 0xc0, 0x42, + 0x3f, 0x4, 0x40, 0x8, 0x80, 0x1d, 0xfe, 0xe2, + 0x10, 0x44, 0x20, 0x9f, 0xf9, 0x0, 0x2, 0x1, + 0x4, 0x41, 0x99, 0x0, 0x80, + + /* U+644A "摊" */ + 0x20, 0x40, 0x40, 0x90, 0x9a, 0x7, 0x97, 0xf2, + 0xa8, 0x85, 0x71, 0xb, 0x3f, 0x1a, 0x44, 0xe4, + 0x88, 0x49, 0xf8, 0xaa, 0x21, 0x54, 0x42, 0xa8, + 0x86, 0x1f, 0xf0, 0x20, 0x0, + + /* U+6452 "摒" */ + 0x20, 0x0, 0x4f, 0xfc, 0x90, 0xf, 0xbf, 0xf2, + 0x40, 0x4, 0x91, 0x9, 0x6, 0x1a, 0xff, 0xe4, + 0x88, 0x49, 0x10, 0xaf, 0xf9, 0x44, 0x42, 0x88, + 0x86, 0x21, 0x34, 0x82, 0x0, + + /* U+6454 "摔" */ + 0x40, 0x40, 0x80, 0x81, 0x1f, 0xfa, 0x2, 0xf, + 0x49, 0x48, 0xbd, 0x10, 0x90, 0x1a, 0x52, 0xc9, + 0xf0, 0x80, 0x11, 0x1, 0x2, 0x7f, 0xf4, 0x4, + 0x8, 0x8, 0x30, 0x10, 0x0, + + /* U+6458 "摘" */ + 0x20, 0x40, 0x40, 0x80, 0xbf, 0xff, 0x88, 0x82, + 0x12, 0x4, 0xff, 0x89, 0x11, 0x1a, 0xfa, 0xe4, + 0x44, 0x4b, 0xc8, 0x94, 0x91, 0x29, 0x22, 0x5e, + 0x44, 0x80, 0xb1, 0xe, 0x0, + + /* U+645E "摞" */ + 0x7, 0xfc, 0x88, 0x89, 0x11, 0x17, 0xbf, 0xe4, + 0x44, 0x48, 0xff, 0x90, 0x30, 0x29, 0x8c, 0xe7, + 0xe1, 0x82, 0x21, 0x1f, 0xf2, 0x2, 0x84, 0x64, + 0x8b, 0x8, 0xf0, 0xf0, 0x0, + + /* U+6467 "摧" */ + 0x44, 0x44, 0x88, 0x89, 0x11, 0x17, 0xbf, 0xe4, + 0x24, 0x8, 0x44, 0x11, 0xff, 0x3a, 0x20, 0xcf, + 0xfc, 0x88, 0x81, 0x11, 0x2, 0x3f, 0xe4, 0x44, + 0x8, 0xff, 0xf1, 0x0, 0x0, + + /* U+6469 "摩" */ + 0x0, 0x80, 0x1, 0x1, 0xfd, 0xfa, 0x20, 0x85, + 0xf7, 0xe9, 0xc7, 0x15, 0x35, 0xa0, 0x0, 0x5f, + 0xf8, 0x81, 0x1, 0x3f, 0xe3, 0xff, 0xfc, 0x8, + 0x10, 0x10, 0x21, 0xc0, 0x0, + + /* U+646D "摭" */ + 0x40, 0x20, 0x8f, 0xfd, 0x10, 0x7, 0xa4, 0x44, + 0x48, 0x88, 0xbf, 0xd1, 0x22, 0x2a, 0x44, 0xe4, + 0x88, 0x89, 0xf1, 0x10, 0x2, 0x6a, 0xa4, 0x95, + 0x29, 0x6a, 0x74, 0x0, 0x0, + + /* U+6478 "摸" */ + 0x21, 0x10, 0x42, 0x20, 0xbf, 0xf9, 0x8, 0x8f, + 0x0, 0x4, 0xff, 0x89, 0x1, 0x13, 0xfe, 0x34, + 0x5, 0xcf, 0xf8, 0x81, 0x1, 0x2, 0x2, 0xff, + 0xe4, 0x14, 0x8, 0x46, 0x66, 0x3, + + /* U+6479 "摹" */ + 0x4, 0x21, 0xff, 0xfc, 0x10, 0x81, 0xde, 0xc3, + 0xff, 0x84, 0x1, 0xf, 0xfe, 0x2, 0x0, 0xfb, + 0xfe, 0x6f, 0xd3, 0x4, 0x18, 0x7f, 0x7, 0xff, + 0xc0, 0x20, 0x1, 0xc0, 0x0, + + /* U+647A "摺" */ + 0x20, 0x0, 0xbf, 0xf2, 0x8, 0x7d, 0x29, 0x20, + 0x84, 0x8e, 0x72, 0xce, 0x4c, 0x20, 0xe7, 0xf8, + 0x90, 0x22, 0x7f, 0x89, 0x2, 0x24, 0x9, 0x1f, + 0xec, 0x40, 0x80, + + /* U+6482 "撂" */ + 0x27, 0xfc, 0x50, 0x88, 0xa1, 0x17, 0xff, 0xe2, + 0x84, 0x44, 0xf7, 0x8, 0xfe, 0x1b, 0x4, 0xe9, + 0xb0, 0x43, 0xf0, 0xb8, 0x11, 0x3f, 0xe2, 0x40, + 0x48, 0xff, 0xb1, 0x1, 0x0, + + /* U+6484 "撄" */ + 0x20, 0x0, 0x2f, 0xbe, 0x2a, 0xaa, 0xfa, 0xaa, + 0x2a, 0xaa, 0x2a, 0xaa, 0x27, 0x18, 0x64, 0xa6, + 0xa8, 0x80, 0x2f, 0xff, 0x22, 0x10, 0x26, 0x10, + 0x21, 0xe0, 0x21, 0x98, 0xcc, 0x6, + + /* U+6485 "撅" */ + 0x0, 0x0, 0x8f, 0xfd, 0x28, 0xa7, 0xca, 0x44, + 0xbe, 0xe9, 0x12, 0x52, 0xac, 0xa5, 0x54, 0xfa, + 0xa8, 0x95, 0x51, 0x2f, 0xa2, 0x44, 0xa4, 0x91, + 0x4a, 0x24, 0x74, 0x98, 0x80, + + /* U+6487 "撇" */ + 0x21, 0x10, 0x29, 0x50, 0x25, 0x90, 0xf1, 0x1e, + 0x2f, 0xe4, 0x29, 0x64, 0x29, 0x54, 0x3d, 0x54, + 0xed, 0x58, 0x2d, 0xd8, 0x2d, 0xc8, 0x29, 0x48, + 0x29, 0x54, 0x29, 0x66, 0xc9, 0xe3, + + /* U+6491 "撑" */ + 0x26, 0x44, 0x42, 0xb0, 0xbf, 0xff, 0xc0, 0x12, + 0xbf, 0xa4, 0x41, 0x8, 0xfe, 0x18, 0x0, 0xef, + 0xfc, 0x40, 0x80, 0x9f, 0xf1, 0x2, 0x2, 0xff, + 0xe4, 0x8, 0x30, 0xf0, 0x0, + + /* U+6492 "撒" */ + 0x24, 0x88, 0x24, 0x88, 0x27, 0xd0, 0xf4, 0x9f, + 0x24, 0x92, 0x2f, 0xd2, 0x20, 0x32, 0x27, 0xd2, + 0xf8, 0x54, 0x27, 0xcc, 0x28, 0x48, 0x27, 0xcc, + 0x28, 0x54, 0x28, 0x52, 0xcb, 0xa1, + + /* U+6495 "撕" */ + 0x49, 0x4, 0x92, 0x71, 0x3e, 0x87, 0x49, 0x4, + 0xf2, 0x9, 0x27, 0xd2, 0x4a, 0x37, 0x94, 0xc9, + 0x28, 0x92, 0x51, 0x7e, 0xa2, 0x1, 0x44, 0x94, + 0x8b, 0x9, 0x34, 0x12, 0x0, + + /* U+6496 "撖" */ + 0x20, 0x10, 0x27, 0x90, 0x21, 0x10, 0xf7, 0xee, + 0x24, 0xa4, 0x24, 0xa4, 0x27, 0xe4, 0x34, 0x94, + 0xe4, 0x98, 0x27, 0x98, 0x24, 0x88, 0x24, 0x88, + 0x27, 0xdc, 0x28, 0xa6, 0xc0, 0xc2, + + /* U+6499 "撙" */ + 0x1, 0x10, 0x9f, 0xfd, 0x2, 0x87, 0xbf, 0xe4, + 0x4a, 0x48, 0xa7, 0x91, 0x1, 0x1b, 0xfe, 0xc4, + 0x4, 0x8f, 0xf9, 0x0, 0x2, 0x7f, 0xf4, 0x21, + 0x8, 0x22, 0x30, 0x1c, 0x0, + + /* U+649E "撞" */ + 0x20, 0x40, 0x4f, 0xf8, 0x88, 0x27, 0x88, 0x82, + 0xff, 0xe4, 0x0, 0x9, 0xff, 0x1a, 0x22, 0xe7, + 0xfc, 0x48, 0x88, 0x9f, 0xf1, 0x2, 0x2, 0x7f, + 0xc4, 0x8, 0x33, 0xff, 0x80, + + /* U+64A4 "撤" */ + 0x22, 0x8, 0x22, 0x10, 0x2f, 0xd0, 0xf5, 0x1f, + 0x2f, 0x92, 0x20, 0x72, 0x20, 0x34, 0x27, 0x94, + 0xf8, 0x94, 0x27, 0x8c, 0x28, 0x88, 0x27, 0x8c, + 0x28, 0x94, 0x28, 0xa2, 0xcb, 0xa0, + + /* U+64A9 "撩" */ + 0x0, 0x40, 0x81, 0x1, 0x1f, 0xf7, 0xa5, 0x24, + 0x33, 0x88, 0x42, 0x11, 0xff, 0x26, 0x4, 0xf7, + 0xf8, 0x88, 0x11, 0x1f, 0xe2, 0x2, 0x4, 0x44, + 0x89, 0x8, 0xb0, 0xf0, 0x0, + + /* U+64AC "撬" */ + 0x27, 0xfc, 0x41, 0x0, 0x9f, 0xf7, 0x84, 0x2, + 0xff, 0xe4, 0x10, 0x48, 0x3f, 0x99, 0x80, 0xee, + 0x7c, 0x46, 0x20, 0xb9, 0xf1, 0x18, 0x93, 0xe7, + 0xc4, 0x62, 0x70, 0x87, 0x80, + + /* U+64AD "播" */ + 0x40, 0x0, 0x8f, 0xf9, 0x1, 0x7, 0x92, 0xc4, + 0xff, 0xe8, 0x1c, 0x10, 0x54, 0x1b, 0x26, 0xcf, + 0xfc, 0x88, 0x89, 0x11, 0x12, 0x3f, 0xe4, 0x44, + 0x48, 0xff, 0xb1, 0x1, 0x0, + + /* U+64AE "撮" */ + 0x27, 0xfc, 0x48, 0x8, 0x9f, 0xf7, 0xa0, 0x22, + 0x7f, 0xc4, 0x0, 0xb, 0xff, 0x9c, 0x40, 0xe7, + 0xbe, 0x51, 0x48, 0x9e, 0x51, 0x44, 0xc2, 0x9d, + 0x85, 0xd6, 0xb0, 0x30, 0x80, + + /* U+64B0 "撰" */ + 0x4f, 0xbc, 0x91, 0x49, 0x22, 0x97, 0xfd, 0xe4, + 0x82, 0x28, 0xf7, 0x90, 0x0, 0x28, 0x88, 0xef, + 0xfc, 0x82, 0x21, 0x4, 0x42, 0x7f, 0xf4, 0x0, + 0x8, 0xe3, 0xb2, 0x1, 0x80, + + /* U+64B5 "撵" */ + 0x1, 0x0, 0x42, 0x10, 0x9f, 0xf9, 0x8, 0x4f, + 0x7f, 0xe4, 0x21, 0x8, 0xa5, 0x12, 0x11, 0x20, + 0x80, 0xef, 0xfa, 0x84, 0x81, 0x1f, 0xe2, 0x2, + 0x5, 0xff, 0xc8, 0x8, 0x60, 0x10, + + /* U+64B7 "撷" */ + 0x22, 0x0, 0x44, 0x0, 0x89, 0xf9, 0x7c, 0x8f, + 0x21, 0x4, 0x4d, 0xcb, 0xd0, 0x98, 0x29, 0x60, + 0x53, 0x4e, 0xa4, 0xa5, 0x49, 0x4a, 0x92, 0x95, + 0x25, 0x23, 0x9, 0xc9, 0x64, 0x21, + + /* U+64B8 "撸" */ + 0x21, 0x0, 0x47, 0xe0, 0x90, 0x87, 0xbe, 0xe2, + 0x7f, 0xc4, 0x88, 0x89, 0xff, 0x38, 0x0, 0xaf, + 0xfe, 0x40, 0x0, 0x9f, 0xf1, 0x20, 0x22, 0x7f, + 0xc4, 0xff, 0xb1, 0x1, 0x0, + + /* U+64BA "撺" */ + 0x20, 0x40, 0x5f, 0xfc, 0xa0, 0xf, 0xd8, 0x92, + 0xc0, 0xc4, 0xff, 0x89, 0x11, 0x3b, 0xfe, 0xa0, + 0x40, 0x4f, 0xf8, 0xa1, 0x11, 0x42, 0x22, 0x7f, + 0xc4, 0x8, 0x30, 0x10, 0x0, + + /* U+64BC "撼" */ + 0x0, 0x28, 0x80, 0x41, 0x1f, 0xf7, 0xc1, 0x4, + 0xbe, 0x49, 0x7, 0x12, 0xf6, 0x1d, 0x28, 0xcb, + 0xd4, 0xb0, 0x59, 0x2, 0x2, 0x32, 0x4, 0xa1, + 0x49, 0x42, 0x30, 0x78, 0x0, + + /* U+64C0 "擀" */ + 0x22, 0x8, 0x2f, 0x98, 0x22, 0x14, 0xf2, 0x24, + 0x2f, 0xa2, 0x28, 0x80, 0x2f, 0xbe, 0x38, 0x88, + 0xe8, 0x88, 0x2f, 0x88, 0x22, 0x3e, 0x2f, 0xc8, + 0x22, 0x8, 0x22, 0x8, 0xc2, 0x8, + + /* U+64C2 "擂" */ + 0x20, 0x0, 0x47, 0xf8, 0x81, 0x7, 0xff, 0xf2, + 0xc4, 0x25, 0xbb, 0x48, 0x10, 0x19, 0xee, 0xe3, + 0xfc, 0x44, 0x88, 0x89, 0x11, 0x1f, 0xe2, 0x24, + 0x44, 0x7f, 0xb0, 0x81, 0x0, + + /* U+64C5 "擅" */ + 0x0, 0x40, 0x9f, 0xfd, 0x0, 0x7, 0xbf, 0xe4, + 0x5f, 0x48, 0xa2, 0x91, 0x39, 0x1b, 0xfe, 0xc0, + 0x0, 0x8f, 0xf1, 0x1f, 0xe2, 0x20, 0x44, 0x3f, + 0x9, 0xff, 0xf0, 0x0, 0x0, + + /* U+64CD "操" */ + 0x3, 0xf8, 0x84, 0x11, 0x8, 0x27, 0x9f, 0xc4, + 0x0, 0x8, 0xf7, 0x91, 0x29, 0x33, 0xde, 0xc0, + 0x0, 0x80, 0x81, 0x3f, 0xfa, 0x7, 0x4, 0x35, + 0x9, 0x89, 0xf0, 0x10, 0x0, + + /* U+64CE "擎" */ + 0x12, 0x10, 0xff, 0x40, 0x48, 0xf9, 0xfb, 0x2c, + 0x12, 0x8f, 0xa2, 0xf, 0x5b, 0x1, 0x0, 0x7f, + 0xf8, 0x1, 0x0, 0xff, 0xf0, 0x4, 0xf, 0xff, + 0xe0, 0x10, 0x1, 0xc0, 0x0, + + /* U+64D0 "擐" */ + 0x47, 0xfc, 0x89, 0x49, 0x12, 0x97, 0xbf, 0xe4, + 0x0, 0x9, 0xff, 0xd0, 0x0, 0x3b, 0xfe, 0xc4, + 0x4, 0x8f, 0xf9, 0x3, 0x2, 0x1a, 0xe4, 0xd2, + 0xa, 0x2f, 0xb0, 0x60, 0x80, + + /* U+64D2 "擒" */ + 0x40, 0xc0, 0x82, 0x41, 0x1a, 0x77, 0xff, 0xd4, + 0x0, 0x8, 0xb4, 0x91, 0x39, 0x2a, 0x8a, 0x67, + 0xfd, 0x81, 0x1, 0x3f, 0xf2, 0x49, 0x24, 0xbf, + 0x49, 0x2, 0xb2, 0x7, 0x0, + + /* U+64D7 "擗" */ + 0x40, 0x8, 0x9f, 0x11, 0x22, 0xff, 0xc5, 0x14, + 0x8a, 0x49, 0xf2, 0x92, 0x2, 0x3c, 0x1f, 0xcf, + 0x88, 0x99, 0x11, 0x52, 0xfa, 0xa4, 0x45, 0x78, + 0x88, 0x91, 0x31, 0x22, 0x0, + + /* U+64D8 "擘" */ + 0x0, 0x0, 0x7c, 0x21, 0xb, 0xfa, 0x12, 0x27, + 0xc2, 0x88, 0x1f, 0xdf, 0x84, 0x51, 0x7e, 0x9c, + 0x10, 0x7f, 0xf8, 0x2, 0x1, 0xff, 0xe0, 0x8, + 0x1f, 0xff, 0xc0, 0x20, 0x3, 0x80, + + /* U+64DE "擞" */ + 0x21, 0x8, 0x25, 0x48, 0x25, 0x50, 0xf1, 0x1f, + 0x27, 0xd2, 0x23, 0x92, 0x25, 0x72, 0x20, 0x12, + 0xf2, 0xe, 0x2f, 0xcc, 0x24, 0x4c, 0x24, 0x88, + 0x23, 0x8c, 0x23, 0x52, 0xcc, 0x21, + + /* U+64E2 "擢" */ + 0x40, 0x0, 0x9f, 0xf9, 0x12, 0x97, 0x94, 0xa4, + 0x29, 0xc9, 0x9c, 0x90, 0x91, 0x3b, 0xfe, 0xcc, + 0x40, 0xaf, 0xf9, 0x11, 0x2, 0x3f, 0xe4, 0x44, + 0x8, 0xff, 0xf1, 0x0, 0x0, + + /* U+64E4 "擤" */ + 0x0, 0x81, 0x1f, 0xe4, 0x40, 0xbd, 0xfe, 0x47, + 0xf9, 0x1f, 0xe4, 0x0, 0xf, 0xff, 0xc7, 0xfd, + 0x21, 0x14, 0x7b, 0x93, 0xff, 0x41, 0x11, 0x8, + 0x4c, 0xc1, 0x0, + + /* U+64E6 "擦" */ + 0x20, 0x40, 0x27, 0xfe, 0x24, 0x2, 0xf6, 0x20, + 0x23, 0xde, 0x26, 0x54, 0x2a, 0x98, 0x31, 0xf8, + 0xe1, 0x4, 0x26, 0x3, 0x2b, 0xfc, 0x20, 0x20, + 0x22, 0x24, 0x24, 0x22, 0xc1, 0xc0, + + /* U+6500 "攀" */ + 0x24, 0x88, 0x46, 0x11, 0xda, 0xf2, 0x94, 0xe6, + 0x92, 0xb4, 0xd1, 0x0, 0x80, 0x7e, 0xff, 0x17, + 0xd0, 0xc2, 0x1a, 0xff, 0xe8, 0x8, 0x7, 0xff, + 0xc0, 0x20, 0x3, 0xc0, 0x0, + + /* U+6509 "攉" */ + 0x7, 0xfd, 0x1, 0x4, 0xff, 0xfe, 0x11, 0x4b, + 0x59, 0x1, 0x4, 0x71, 0xd4, 0x90, 0xe7, 0xfd, + 0x11, 0x5, 0xff, 0xd1, 0x10, 0x47, 0xf9, 0x1f, + 0xfc, 0x40, 0x0, + + /* U+6512 "攒" */ + 0x21, 0x0, 0x4a, 0x60, 0x9e, 0xf9, 0x4a, 0x8f, + 0xfb, 0xe4, 0x63, 0x49, 0x2a, 0x9c, 0x2, 0xe7, + 0xfc, 0x48, 0x8, 0x91, 0x11, 0x22, 0x22, 0xe, + 0x4, 0x33, 0xb3, 0x81, 0x80, + + /* U+6518 "攘" */ + 0x40, 0x40, 0x9f, 0xfd, 0x0, 0x3, 0x7d, 0xe4, + 0x7b, 0xc8, 0x0, 0x17, 0xff, 0x31, 0x8, 0xcf, + 0xfc, 0x84, 0x21, 0x7f, 0xfa, 0xe, 0x24, 0x63, + 0x8b, 0x4b, 0x30, 0xe1, 0x80, + + /* U+6525 "攥" */ + 0x42, 0x10, 0x8f, 0x79, 0x25, 0xa7, 0xbf, 0xe4, + 0x7f, 0xc8, 0xff, 0x95, 0x1, 0x31, 0xfc, 0xcf, + 0xfc, 0x85, 0x11, 0x3f, 0x9a, 0x8, 0x84, 0x7f, + 0x88, 0x4b, 0x33, 0x71, 0x0, + + /* U+652B "攫" */ + 0x2f, 0x7e, 0x52, 0x8c, 0xbd, 0xff, 0xfb, 0xf2, + 0x94, 0x64, 0xd7, 0xb, 0xff, 0xbf, 0xfe, 0xaf, + 0xfc, 0x50, 0x80, 0xbf, 0xf9, 0x7f, 0xc2, 0x21, + 0x4, 0x3c, 0x33, 0xcf, 0x80, + + /* U+652E "攮" */ + 0x20, 0x40, 0x4f, 0xfc, 0x8f, 0xf7, 0x9f, 0xe2, + 0x4, 0x4, 0xf7, 0xc8, 0xff, 0x19, 0xfe, 0xe7, + 0xfe, 0x42, 0x10, 0x8f, 0xf1, 0x7f, 0xf2, 0xa, + 0x45, 0xe3, 0x30, 0x71, 0x80, + + /* U+652F "支" */ + 0x1, 0x0, 0x2, 0x0, 0x4, 0x7, 0xff, 0xf0, + 0x10, 0x0, 0x20, 0xf, 0xfe, 0x8, 0x8, 0x8, + 0x10, 0x10, 0x40, 0x11, 0x0, 0x14, 0x0, 0x18, + 0x1, 0xdc, 0x1c, 0x7, 0x80, 0x0, + + /* U+6534 "攴" */ + 0x1, 0x0, 0x2, 0x0, 0x4, 0x0, 0xf, 0xf0, + 0x10, 0x0, 0x20, 0xf, 0xfe, 0x4, 0x4, 0x8, + 0x10, 0x8, 0x20, 0x10, 0x80, 0x1a, 0x0, 0x18, + 0x0, 0xcc, 0x1e, 0x7, 0x80, 0x0, + + /* U+6535 "攵" */ + 0x8, 0x0, 0x20, 0x1, 0x0, 0x7, 0xff, 0x20, + 0x11, 0x40, 0x8d, 0x2, 0x22, 0x18, 0x8, 0x40, + 0x12, 0x0, 0x30, 0x0, 0xc0, 0xc, 0xc0, 0xc0, + 0xcc, 0x0, 0xc0, + + /* U+6536 "收" */ + 0x10, 0x80, 0x44, 0x9, 0x10, 0x24, 0xff, 0x92, + 0x12, 0x58, 0x49, 0x61, 0x24, 0x44, 0x91, 0x22, + 0x44, 0x8f, 0xc, 0x24, 0x30, 0x11, 0xa0, 0x48, + 0x61, 0x40, 0x40, + + /* U+6538 "攸" */ + 0x10, 0x0, 0x20, 0x80, 0x41, 0x1, 0x23, 0xf2, + 0x48, 0x4c, 0x90, 0x99, 0x51, 0x52, 0xa2, 0x24, + 0x48, 0x48, 0x50, 0x90, 0xc1, 0x20, 0x82, 0x43, + 0x4, 0x9, 0x8, 0x61, 0x90, 0x81, + + /* U+6539 "改" */ + 0x0, 0x83, 0xe2, 0x0, 0x90, 0x2, 0x7f, 0xb, + 0x8, 0x2c, 0x4f, 0xd1, 0x20, 0x44, 0x80, 0xa2, + 0x2, 0x88, 0x4, 0x26, 0x28, 0xe1, 0x12, 0x18, + 0x30, 0x80, 0x0, + + /* U+653B "攻" */ + 0x0, 0x40, 0x0, 0xc0, 0xfc, 0x80, 0x20, 0xfe, + 0x21, 0x4, 0x21, 0x8, 0x22, 0x88, 0x20, 0x88, + 0x20, 0x88, 0x20, 0x50, 0x20, 0x50, 0x3c, 0x20, + 0xc0, 0xd0, 0x1, 0xc, 0x6, 0x2, + + /* U+653E "放" */ + 0x10, 0x40, 0x20, 0x80, 0x42, 0x7, 0xf7, 0xf4, + 0x18, 0x88, 0x31, 0x1f, 0x22, 0x22, 0x44, 0x44, + 0x90, 0x88, 0xa1, 0x11, 0xc2, 0x21, 0x4, 0x45, + 0x11, 0x91, 0x2e, 0xc1, 0x81, 0x0, + + /* U+653F "政" */ + 0x0, 0x41, 0xfd, 0x0, 0x42, 0x0, 0x8f, 0xf1, + 0x10, 0x8a, 0x61, 0x17, 0xc2, 0x28, 0x48, 0x50, + 0x90, 0xa0, 0xc1, 0x41, 0x82, 0xf3, 0xe, 0x19, + 0x80, 0x61, 0x81, 0x0, 0x0, + + /* U+6545 "故" */ + 0x10, 0x40, 0x20, 0x80, 0x43, 0x0, 0x84, 0xf, + 0xef, 0xe2, 0x31, 0x4, 0x62, 0x8, 0x44, 0x7c, + 0x91, 0x8, 0xa2, 0x11, 0x44, 0x21, 0x8, 0x45, + 0xf, 0x9b, 0x20, 0x61, 0x0, 0x0, + + /* U+6548 "效" */ + 0x8, 0x10, 0x4, 0x10, 0x0, 0x20, 0x7f, 0xbf, + 0x10, 0x22, 0x12, 0x62, 0x21, 0x64, 0x40, 0xa4, + 0x22, 0x14, 0x1a, 0x14, 0xc, 0x18, 0xe, 0x18, + 0x13, 0x14, 0x21, 0x62, 0x40, 0x81, + + /* U+6549 "敉" */ + 0x10, 0x40, 0x10, 0x40, 0x94, 0x40, 0x54, 0xff, + 0x58, 0x84, 0x11, 0x84, 0xfe, 0x88, 0x10, 0x88, + 0x30, 0x48, 0x58, 0x50, 0x54, 0x30, 0x90, 0x30, + 0x90, 0x58, 0x11, 0x84, 0x12, 0x2, + + /* U+654C "敌" */ + 0x0, 0x41, 0xfc, 0x80, 0x42, 0x0, 0x87, 0xef, + 0xe8, 0x82, 0x31, 0x4, 0x52, 0x8, 0x24, 0xfc, + 0x51, 0x8, 0x62, 0x10, 0x84, 0x21, 0x88, 0x45, + 0x9f, 0x91, 0xa0, 0x41, 0x0, + + /* U+654F "敏" */ + 0x20, 0x10, 0x20, 0x10, 0x5f, 0x20, 0x40, 0x3f, + 0xbf, 0x24, 0x41, 0x64, 0x49, 0x64, 0x41, 0x24, + 0xff, 0xa4, 0x41, 0x18, 0x49, 0x18, 0x41, 0x18, + 0x7f, 0x98, 0x2, 0x24, 0x2, 0x42, 0x1e, 0xc1, + + /* U+6551 "救" */ + 0x10, 0x40, 0x2c, 0x80, 0x41, 0x7, 0xf7, 0xe1, + 0x8, 0x92, 0x51, 0x15, 0x62, 0x2d, 0x44, 0x10, + 0x50, 0x70, 0xa1, 0x50, 0x84, 0x93, 0x1, 0xd, + 0x82, 0x31, 0xb8, 0x80, 0x0, + + /* U+6555 "敕" */ + 0x10, 0x20, 0x10, 0x20, 0xff, 0x40, 0x10, 0x7e, + 0x7e, 0x44, 0x92, 0xc4, 0x92, 0xc4, 0x92, 0x48, + 0x7e, 0x28, 0x10, 0x28, 0x28, 0x10, 0x36, 0x30, + 0x50, 0x68, 0x90, 0xc6, 0x11, 0x2, + + /* U+6556 "敖" */ + 0x10, 0x0, 0x20, 0x80, 0x41, 0x7, 0xf2, 0x1, + 0x7, 0xcf, 0xd1, 0x4, 0x22, 0x7f, 0xa4, 0x21, + 0x48, 0x40, 0xa0, 0xf1, 0x41, 0x21, 0x4, 0x43, + 0x8, 0x89, 0x27, 0x61, 0x0, 0x0, + + /* U+6559 "教" */ + 0x10, 0x40, 0x20, 0x81, 0xf5, 0x0, 0x93, 0xe1, + 0x48, 0x9f, 0xd1, 0x4, 0x62, 0x3e, 0xa4, 0xc4, + 0x50, 0x10, 0xa0, 0x58, 0x87, 0xc3, 0x81, 0xd, + 0x82, 0x31, 0x9c, 0x81, 0x0, + + /* U+655B "敛" */ + 0x8, 0x20, 0x18, 0x40, 0x49, 0x1, 0x1b, 0xf4, + 0x14, 0x5f, 0xd8, 0x80, 0x31, 0x0, 0x24, 0x49, + 0x48, 0x94, 0x51, 0x28, 0xc1, 0x10, 0x80, 0x42, + 0x8f, 0xe8, 0x80, 0x60, 0x80, + + /* U+655D "敝" */ + 0x10, 0x23, 0x4c, 0x81, 0x5, 0xff, 0xd2, 0x91, + 0xca, 0x47, 0x2d, 0x52, 0xb5, 0x4a, 0xd5, 0x33, + 0x44, 0x49, 0x11, 0x24, 0x4a, 0x91, 0x4e, 0x5f, + 0x10, + + /* U+655E "敞" */ + 0x0, 0x0, 0x20, 0x41, 0x48, 0x82, 0xb1, 0xf1, + 0x4, 0x5f, 0xe8, 0xa0, 0x72, 0x40, 0xe4, 0xbd, + 0x29, 0x4a, 0x52, 0x94, 0xa5, 0x28, 0x8b, 0xd1, + 0x10, 0x25, 0x20, 0x59, 0x43, 0xe2, + + /* U+6562 "敢" */ + 0x0, 0x0, 0xf8, 0x40, 0x10, 0x87, 0xfa, 0xf4, + 0x24, 0x48, 0x58, 0x9f, 0xb1, 0x21, 0x14, 0x42, + 0x28, 0xfc, 0x51, 0x8, 0x42, 0x7d, 0x4f, 0x22, + 0x40, 0x58, 0x40, 0xa0, 0x80, + + /* U+6563 "散" */ + 0x24, 0x20, 0x24, 0x20, 0xff, 0x20, 0x24, 0x7f, + 0x24, 0x44, 0x5a, 0xc4, 0x7e, 0x44, 0x42, 0x24, + 0x42, 0x28, 0x7e, 0x28, 0x42, 0x10, 0x7e, 0x18, + 0x42, 0x2c, 0x42, 0x46, 0x4e, 0x80, + + /* U+6566 "敦" */ + 0x0, 0x20, 0x10, 0x43, 0xfd, 0x0, 0x3, 0xf7, + 0xe4, 0x48, 0x58, 0x9f, 0xd1, 0x0, 0x24, 0x7e, + 0x28, 0xc, 0x60, 0x20, 0x47, 0xf9, 0xc0, 0x84, + 0xc1, 0x10, 0xce, 0x40, 0x80, + + /* U+656B "敫" */ + 0x10, 0x20, 0x20, 0x41, 0xf9, 0x2, 0x12, 0x4, + 0x27, 0xcf, 0xd1, 0x10, 0xb2, 0x3f, 0xa4, 0x8, + 0x49, 0xfe, 0xa0, 0x80, 0xc1, 0xf1, 0x82, 0x23, + 0x8, 0x4b, 0x10, 0xb3, 0x4e, 0xc2, + + /* U+656C "敬" */ + 0x12, 0x10, 0x12, 0x20, 0x7f, 0xa0, 0x12, 0x3f, + 0x10, 0x44, 0x3f, 0x64, 0x41, 0xa4, 0x7d, 0x24, + 0x25, 0x28, 0x25, 0x18, 0x25, 0x18, 0x3d, 0x18, + 0x21, 0x24, 0x1, 0x46, 0xe, 0x80, + + /* U+6570 "数" */ + 0x8, 0x20, 0x94, 0x80, 0xa1, 0x7, 0xfb, 0xe1, + 0x8c, 0x85, 0x99, 0x32, 0xd2, 0x0, 0x24, 0x10, + 0x51, 0xfc, 0x60, 0x88, 0xc2, 0x11, 0x81, 0xc3, + 0x3, 0xc9, 0x38, 0x61, 0x0, + + /* U+6572 "敲" */ + 0x8, 0x11, 0xff, 0x20, 0x0, 0x7b, 0xf0, 0x84, + 0x21, 0xf, 0xcf, 0x80, 0x11, 0x7f, 0xa2, 0x81, + 0x25, 0x3a, 0x52, 0x94, 0xa5, 0x28, 0x89, 0xd3, + 0x90, 0x2c, 0xa1, 0xf0, 0x80, + + /* U+6574 "整" */ + 0x10, 0x41, 0xfe, 0x80, 0x41, 0xf3, 0xf6, 0x45, + 0x34, 0x8f, 0xc6, 0x4, 0xc, 0x3f, 0x67, 0x80, + 0x0, 0xff, 0xf8, 0x4, 0x0, 0xf, 0xc2, 0x10, + 0x4, 0x20, 0x3f, 0xff, 0x80, + + /* U+6577 "敷" */ + 0xa, 0x20, 0x10, 0x43, 0xfe, 0x83, 0xf9, 0xf4, + 0x94, 0x4f, 0xe9, 0x12, 0x52, 0x3f, 0x94, 0x49, + 0x29, 0xff, 0x50, 0x80, 0x41, 0xf8, 0x82, 0x12, + 0x88, 0x28, 0xb3, 0xa0, 0x0, + + /* U+6587 "文" */ + 0x0, 0x0, 0x2, 0x0, 0x2, 0x7, 0xff, 0xf1, + 0x1, 0x2, 0x2, 0x2, 0x8, 0x4, 0x10, 0x4, + 0x40, 0x8, 0x80, 0xa, 0x0, 0x8, 0x0, 0x6c, + 0x3, 0x7, 0x38, 0x1, 0x80, + + /* U+658B "斋" */ + 0x1, 0x1, 0xff, 0xfc, 0x60, 0x40, 0x33, 0x0, + 0x7c, 0x1f, 0x7, 0xc0, 0x0, 0x3f, 0xfe, 0x2, + 0x0, 0xff, 0xf9, 0x11, 0x12, 0x22, 0x24, 0x44, + 0x48, 0x88, 0x90, 0x7, 0x0, + + /* U+658C "斌" */ + 0x20, 0x10, 0x47, 0xa8, 0x0, 0x4f, 0xc0, 0x81, + 0x7f, 0xe2, 0x2, 0x14, 0x24, 0x2a, 0x48, 0x24, + 0xf0, 0x49, 0x20, 0xd2, 0x42, 0xa4, 0x95, 0x48, + 0xb1, 0xf9, 0x40, 0x1, 0x80, + + /* U+6590 "斐" */ + 0x2, 0x40, 0x2, 0x40, 0x7e, 0x7e, 0x2, 0x40, + 0x7e, 0x7e, 0x2, 0x40, 0x7e, 0x7e, 0x3, 0x40, + 0x0, 0x80, 0x7f, 0xfe, 0x8, 0x10, 0x4, 0x20, + 0x2, 0x40, 0x1, 0xc0, 0x1e, 0x3c, 0x60, 0x2, + + /* U+6591 "斑" */ + 0x0, 0x1, 0xf2, 0x7c, 0x80, 0x41, 0x3e, 0x82, + 0x9, 0x4, 0x12, 0x3f, 0x24, 0x11, 0x5f, 0x21, + 0x90, 0x42, 0x20, 0x86, 0x41, 0x14, 0x8f, 0xe1, + 0x1, 0x8f, 0xc2, 0x0, 0x0, + + /* U+6593 "斓" */ + 0x40, 0x0, 0x8b, 0xfc, 0x88, 0xf, 0x9f, 0xd2, + 0x84, 0x35, 0x7f, 0x6a, 0x92, 0xb5, 0xb5, 0x6a, + 0x4a, 0x57, 0xf4, 0xa3, 0xb, 0x4b, 0x95, 0xa4, + 0xb1, 0x8, 0x62, 0x17, 0x0, + + /* U+6597 "斗" */ + 0x0, 0x40, 0x20, 0x80, 0x31, 0x0, 0x12, 0x0, + 0x4, 0x6, 0x8, 0x2, 0x10, 0x0, 0x20, 0x0, + 0x4c, 0x7f, 0xe3, 0x1, 0x0, 0x2, 0x0, 0x4, + 0x0, 0x8, 0x0, + + /* U+6599 "料" */ + 0x20, 0x8, 0x40, 0x12, 0x92, 0x25, 0x43, 0x46, + 0x80, 0x84, 0x1, 0x3f, 0x22, 0x10, 0x24, 0x20, + 0x8, 0xf0, 0x3d, 0x97, 0xa5, 0x0, 0x4a, 0x0, + 0x84, 0x1, 0x8, 0x2, 0x0, + + /* U+659B "斛" */ + 0x20, 0x8, 0x79, 0x11, 0x11, 0x24, 0x41, 0x4f, + 0xe0, 0x8a, 0x41, 0x14, 0xb2, 0x3f, 0x34, 0x52, + 0x8, 0xa4, 0x1d, 0xfb, 0xe2, 0x90, 0x45, 0x20, + 0x90, 0x41, 0x23, 0x82, 0x0, + + /* U+659C "斜" */ + 0x8, 0x8, 0x30, 0x10, 0x93, 0x23, 0x13, 0x4c, + 0x0, 0x9f, 0x81, 0x4, 0x22, 0x8, 0x24, 0xfe, + 0x8, 0x20, 0x1d, 0x57, 0xe2, 0x90, 0x49, 0x0, + 0x82, 0x1, 0x1c, 0x2, 0x0, + + /* U+659F "斟" */ + 0x22, 0x4, 0x22, 0x4, 0xff, 0xa4, 0x22, 0x14, + 0x3e, 0x4, 0x22, 0x4, 0x3e, 0x64, 0x22, 0x4, + 0xff, 0x84, 0x40, 0x7, 0x4a, 0x7c, 0x51, 0x4, + 0x40, 0x4, 0x7f, 0x4, 0x0, 0x4, + + /* U+65A1 "斡" */ + 0x10, 0x10, 0xfc, 0x30, 0x10, 0x48, 0x10, 0x88, + 0x7d, 0x4, 0x86, 0x2, 0x7c, 0xc8, 0x84, 0x28, + 0x85, 0x8, 0x7c, 0xc8, 0x10, 0x8, 0xfd, 0xfe, + 0x10, 0x8, 0x10, 0x8, 0x10, 0x8, + + /* U+65A4 "斤" */ + 0x0, 0xc, 0x7f, 0xf0, 0x80, 0x1, 0x0, 0x2, + 0x0, 0x4, 0x0, 0xf, 0xff, 0x90, 0x20, 0x20, + 0x40, 0x40, 0x80, 0x81, 0x2, 0x2, 0x4, 0x4, + 0x18, 0x8, 0x20, 0x10, 0x0, + + /* U+65A5 "斥" */ + 0x0, 0xc, 0x7f, 0xe0, 0x80, 0x1, 0x0, 0x2, + 0x0, 0x7, 0xff, 0xc8, 0x10, 0x10, 0x20, 0x20, + 0x40, 0x4e, 0x80, 0x83, 0xc2, 0x2, 0xf4, 0x4, + 0x18, 0x8, 0x20, 0x10, 0x0, + + /* U+65A7 "斧" */ + 0xc, 0x30, 0x30, 0xc, 0x44, 0x22, 0x3, 0xc0, + 0x3, 0xe0, 0x7c, 0x1e, 0x0, 0x18, 0x1f, 0xe0, + 0x10, 0x0, 0x10, 0x0, 0x1f, 0xfe, 0x10, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x40, 0x20, + + /* U+65A9 "斩" */ + 0x10, 0x0, 0x10, 0xf4, 0xfe, 0x80, 0x20, 0x80, + 0x20, 0x80, 0x48, 0x80, 0x48, 0xff, 0x7e, 0x88, + 0x8, 0x88, 0x8, 0x88, 0xa, 0x88, 0xfd, 0x8, + 0x9, 0x8, 0xb, 0x8, 0xa, 0x8, + + /* U+65AB "斫" */ + 0x0, 0x6, 0x7e, 0xf8, 0x10, 0x80, 0x10, 0x80, + 0x10, 0x80, 0x20, 0x80, 0x3c, 0xff, 0x64, 0x88, + 0x64, 0x88, 0x24, 0x88, 0x24, 0x88, 0x24, 0x88, + 0x3d, 0x8, 0x21, 0x8, 0x2, 0x8, + + /* U+65AD "断" */ + 0x88, 0x2, 0x23, 0xfa, 0xa8, 0x2a, 0xa0, 0x88, + 0x82, 0x22, 0xb, 0xef, 0xe6, 0x22, 0x9c, 0x8a, + 0xaa, 0x2c, 0x88, 0xa2, 0x22, 0x81, 0xb, 0xfc, + 0x20, 0x30, 0x80, + + /* U+65AF "斯" */ + 0x42, 0x0, 0x84, 0xff, 0xfd, 0x2, 0x12, 0x7, + 0xe4, 0x8, 0x4f, 0xd0, 0x91, 0x3f, 0x22, 0x42, + 0x44, 0x84, 0x8b, 0xfd, 0x10, 0x2, 0x22, 0x48, + 0x48, 0x50, 0xa0, 0x1, 0x0, + + /* U+65B0 "新" */ + 0x0, 0x0, 0x8, 0x7e, 0xff, 0x40, 0x22, 0x40, + 0x24, 0x40, 0x4, 0x40, 0xfb, 0x7f, 0x8, 0x48, + 0xff, 0x48, 0x8, 0x48, 0x2c, 0x48, 0x4a, 0xc8, + 0xc8, 0x88, 0x8, 0x88, 0x31, 0x8, + + /* U+65B9 "方" */ + 0x1, 0x0, 0x3, 0x0, 0x2, 0x7, 0xff, 0xf0, + 0x40, 0x0, 0x80, 0x1, 0xfe, 0x4, 0x4, 0x8, + 0x8, 0x10, 0x10, 0x20, 0x20, 0x80, 0x41, 0x0, + 0x84, 0x2, 0x30, 0x78, 0x0, + + /* U+65BC "於" */ + 0x10, 0x10, 0x20, 0x60, 0x20, 0xc7, 0xf2, 0x44, + 0x8, 0x48, 0x30, 0xd0, 0x40, 0x9e, 0x20, 0x44, + 0x60, 0x88, 0x31, 0x10, 0x2, 0x20, 0x4, 0x48, + 0x8, 0x8c, 0x21, 0x6, 0x4c, 0x6, + + /* U+65BD "施" */ + 0x10, 0x80, 0x21, 0x0, 0x43, 0xff, 0xe8, 0x2, + 0x22, 0x4, 0x44, 0x8, 0x48, 0x1c, 0x9e, 0x2b, + 0xe4, 0x52, 0x48, 0xa4, 0x93, 0x49, 0xe4, 0x92, + 0x29, 0x20, 0x7e, 0x40, 0xc0, 0x7e, + + /* U+65C1 "旁" */ + 0x1, 0x0, 0x1, 0x1, 0xff, 0xf0, 0x40, 0x80, + 0x42, 0xf, 0xff, 0xe0, 0x40, 0x80, 0x81, 0x0, + 0x81, 0xff, 0xfc, 0x20, 0x0, 0x7f, 0xc0, 0x80, + 0x81, 0x1, 0xc, 0x2, 0x60, 0x78, + + /* U+65C3 "旃" */ + 0x10, 0x40, 0x8, 0x20, 0x2, 0x3f, 0xcf, 0xe0, + 0x2, 0x10, 0x1, 0xf, 0xf0, 0x82, 0x8, 0x79, + 0x24, 0x24, 0x92, 0x12, 0x41, 0x9, 0x7f, 0xe4, + 0x90, 0x42, 0x48, 0x22, 0x24, 0x11, 0x14, 0x8, + 0xba, 0x3c, + + /* U+65C4 "旄" */ + 0x20, 0x80, 0x41, 0x0, 0x44, 0x7, 0xef, 0xe4, + 0x20, 0x8, 0x1, 0x90, 0x7c, 0x3c, 0x20, 0x48, + 0x44, 0x97, 0xf1, 0x21, 0x2, 0x42, 0x64, 0xbf, + 0x11, 0x8, 0x62, 0x10, 0xd8, 0x3e, + + /* U+65C5 "旅" */ + 0x10, 0x80, 0x21, 0xff, 0xf4, 0x2, 0x10, 0x4, + 0x20, 0xc8, 0x3e, 0x1e, 0x58, 0x24, 0x90, 0x49, + 0x24, 0x92, 0x71, 0x24, 0x42, 0x48, 0x84, 0x90, + 0x91, 0x38, 0xee, 0x40, 0x0, + + /* U+65C6 "旆" */ + 0x10, 0x0, 0x21, 0x0, 0x43, 0xff, 0xe8, 0x2, + 0x10, 0x4, 0x42, 0x8, 0x7f, 0x9c, 0x8, 0x28, + 0x10, 0x53, 0xfc, 0xa4, 0x4b, 0x48, 0x94, 0x91, + 0x29, 0x22, 0x52, 0x47, 0x4c, 0x8, + + /* U+65CB "旋" */ + 0x10, 0x0, 0x21, 0x0, 0x43, 0xff, 0xe8, 0x2, + 0x10, 0x4, 0x40, 0x8, 0x7f, 0x9c, 0x12, 0x28, + 0x24, 0x52, 0x40, 0xa4, 0xf2, 0x49, 0x4, 0xaa, + 0x9, 0x5c, 0x3e, 0x8f, 0xc2, 0x7, 0x0, 0x0, + + /* U+65CC "旌" */ + 0x0, 0x80, 0x11, 0x0, 0x1, 0xfe, 0xfe, 0x0, + 0x22, 0x20, 0x20, 0xa0, 0x21, 0x20, 0x39, 0xfe, + 0x2a, 0x20, 0x28, 0x20, 0x29, 0xfc, 0x68, 0x20, + 0x48, 0x20, 0x48, 0x20, 0xcb, 0xff, 0x98, 0x0, + + /* U+65CE "旎" */ + 0x0, 0x80, 0x22, 0x0, 0x7, 0xff, 0xe8, 0x2, + 0x20, 0x4, 0x7f, 0x88, 0x41, 0x1e, 0x82, 0x25, + 0xfc, 0x4a, 0x1, 0x95, 0x13, 0x2a, 0xc4, 0x56, + 0x9, 0x28, 0x72, 0x90, 0xdd, 0x3e, + + /* U+65CF "族" */ + 0x0, 0x40, 0x10, 0x80, 0x0, 0xfe, 0x7e, 0x80, + 0x21, 0x40, 0x21, 0x40, 0x20, 0x7e, 0x3c, 0x90, + 0x24, 0x90, 0x24, 0x10, 0x25, 0xfe, 0x24, 0x10, + 0x24, 0x28, 0x44, 0x28, 0x44, 0x44, 0x5d, 0x82, + 0x1, 0x0, + + /* U+65D2 "旒" */ + 0x0, 0x0, 0x41, 0x0, 0x87, 0xff, 0xd0, 0x4, + 0x2, 0x8, 0x7f, 0xd0, 0x20, 0x38, 0x84, 0x53, + 0xfc, 0xa0, 0x5, 0x40, 0x2, 0x8a, 0x49, 0x14, + 0x92, 0x29, 0x64, 0x92, 0xfa, 0x27, + + /* U+65D6 "旖" */ + 0x10, 0x80, 0x21, 0xfc, 0x4, 0x7, 0xf1, 0x2, + 0x1f, 0xc4, 0xc, 0xe, 0x67, 0x14, 0x0, 0x29, + 0xfe, 0x53, 0xc9, 0x24, 0x92, 0x49, 0x24, 0x9e, + 0x59, 0x0, 0xae, 0xf, 0x0, + + /* U+65D7 "旗" */ + 0x1, 0x0, 0x42, 0x0, 0x8f, 0xff, 0xd0, 0x4, + 0x10, 0x88, 0x7f, 0xde, 0x42, 0x24, 0xfc, 0x49, + 0x8, 0x92, 0x11, 0x27, 0xe2, 0x48, 0x48, 0xbf, + 0xf3, 0x0, 0x26, 0x66, 0x79, 0x2, 0x0, 0x0, + + /* U+65E0 "无" */ + 0x7f, 0xfc, 0x2, 0x0, 0x4, 0x0, 0x8, 0x0, + 0x20, 0x1f, 0xff, 0xc0, 0xa0, 0x1, 0x40, 0x4, + 0x80, 0x9, 0x0, 0x22, 0x8, 0xc4, 0x12, 0x8, + 0x38, 0x1f, 0xa0, 0x0, 0x0, + + /* U+65E2 "既" */ + 0xf9, 0xfe, 0x20, 0x88, 0x82, 0x3e, 0x48, 0x89, + 0x22, 0x27, 0xff, 0x82, 0x20, 0x10, 0x90, 0x62, + 0x61, 0x88, 0x8a, 0x2d, 0x48, 0xc3, 0x24, 0x18, + 0x70, + + /* U+65E5 "日" */ + 0xff, 0xf0, 0x6, 0x0, 0xc0, 0x18, 0x3, 0x0, + 0x7f, 0xfc, 0x1, 0x80, 0x30, 0x6, 0x0, 0xc0, + 0x1f, 0xff, 0x0, 0x40, + + /* U+65E6 "旦" */ + 0x3f, 0xf8, 0x40, 0x10, 0x80, 0x21, 0x0, 0x43, + 0xff, 0x84, 0x1, 0x8, 0x2, 0x10, 0x4, 0x3f, + 0xf8, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf, 0xff, + 0xe0, + + /* U+65E7 "旧" */ + 0x8f, 0xfc, 0x40, 0x62, 0x3, 0x10, 0x18, 0x80, + 0xc7, 0xfe, 0x20, 0x31, 0x1, 0x88, 0xc, 0x40, + 0x62, 0x3, 0x1f, 0xf8, 0x80, 0xc4, 0x4, + + /* U+65E8 "旨" */ + 0x80, 0x4, 0x7, 0xbf, 0xc3, 0x0, 0x18, 0x0, + 0xbf, 0xf8, 0x0, 0x1f, 0xfe, 0x80, 0x14, 0x0, + 0xbf, 0xfd, 0x0, 0x28, 0x1, 0x7f, 0xfa, 0x0, + 0x40, + + /* U+65E9 "早" */ + 0x3f, 0xf8, 0x40, 0x10, 0x80, 0x21, 0xff, 0xc2, + 0x0, 0x84, 0x1, 0xf, 0xfe, 0x0, 0x80, 0x1, + 0x0, 0x2, 0x3, 0xff, 0xf8, 0x8, 0x0, 0x10, + 0x0, 0x20, 0x0, + + /* U+65EC "旬" */ + 0x8, 0x0, 0x20, 0x1, 0xff, 0xc4, 0x1, 0x20, + 0x5, 0x7f, 0x11, 0x4, 0x44, 0x11, 0x1f, 0xc4, + 0x41, 0x11, 0x4, 0x47, 0xf1, 0x10, 0x4, 0x40, + 0x10, 0xf, 0x80, + + /* U+65ED "旭" */ + 0x20, 0xfc, 0x20, 0x84, 0x20, 0x84, 0xfc, 0x84, + 0x24, 0x84, 0x24, 0xfc, 0x24, 0x84, 0x24, 0x84, + 0x24, 0x84, 0x24, 0x84, 0x24, 0xfc, 0x44, 0x1, + 0x44, 0x2, 0x44, 0x2, 0x87, 0xfe, + + /* U+65EE "旮" */ + 0x8, 0x0, 0x10, 0x1, 0xff, 0x80, 0x41, 0x10, + 0x82, 0x26, 0x7, 0xf0, 0x0, 0x1f, 0xfc, 0x20, + 0x8, 0x40, 0x10, 0xff, 0xe1, 0x0, 0x42, 0x0, + 0x87, 0xff, 0x8, 0x2, 0x0, + + /* U+65EF "旯" */ + 0x3f, 0xf8, 0x40, 0x10, 0xff, 0xe1, 0x0, 0x42, + 0x0, 0x87, 0xff, 0x1, 0x0, 0x2, 0x0, 0xff, + 0xe0, 0x8, 0x40, 0x20, 0x88, 0x41, 0x13, 0x2, + 0x38, 0x7, 0x80, + + /* U+65F0 "旰" */ + 0xf3, 0xfe, 0x41, 0x9, 0x4, 0x24, 0x10, 0x90, + 0x42, 0x41, 0xf, 0x7f, 0xe4, 0x10, 0x90, 0x42, + 0x41, 0xf, 0x4, 0x24, 0x10, 0x90, 0x40, 0x1, + 0x0, + + /* U+65F1 "旱" */ + 0x3f, 0xf8, 0x40, 0x10, 0x80, 0x21, 0xff, 0xc2, + 0x0, 0x84, 0x1, 0xf, 0xfe, 0x0, 0x0, 0x7f, + 0xfc, 0x2, 0x0, 0x4, 0x7, 0xff, 0xf0, 0x10, + 0x0, 0x20, 0x0, 0x40, 0x0, + + /* U+65F6 "时" */ + 0x0, 0x9, 0xf0, 0x12, 0x20, 0x24, 0x5f, 0xf8, + 0x80, 0x91, 0x1, 0x3e, 0x42, 0x44, 0x84, 0x88, + 0x89, 0x11, 0x12, 0x20, 0x24, 0x40, 0x4f, 0x80, + 0x91, 0x1, 0x0, 0x3c, 0x0, + + /* U+65F7 "旷" */ + 0x0, 0x0, 0x0, 0x8f, 0x82, 0x22, 0xff, 0x8a, + 0x2, 0x28, 0x8, 0xa0, 0x3e, 0x80, 0x8a, 0x2, + 0x28, 0x8, 0xa0, 0x22, 0x80, 0xfa, 0x2, 0x10, + 0x0, 0x40, 0x0, 0x0, + + /* U+65FA "旺" */ + 0xfb, 0xfd, 0x10, 0x82, 0x21, 0x4, 0x42, 0x8, + 0x84, 0x11, 0x8, 0x3e, 0xff, 0x44, 0x20, 0x88, + 0x41, 0x10, 0x82, 0x21, 0x7, 0xc2, 0x8, 0xbf, + 0xe0, + + /* U+6600 "昀" */ + 0x1, 0x3, 0xc8, 0x9, 0x3f, 0xe4, 0x81, 0x94, + 0x6, 0x74, 0x1f, 0x8, 0x64, 0x1, 0x90, 0x6, + 0x41, 0x99, 0x18, 0x65, 0x81, 0xf0, 0x6, 0x40, + 0x30, 0xf, 0x80, + + /* U+6602 "昂" */ + 0x7f, 0xf2, 0x0, 0x90, 0x4, 0xff, 0xe4, 0x1, + 0x3f, 0xf8, 0x0, 0x1, 0x80, 0xf1, 0xfc, 0x8, + 0x60, 0x43, 0x2, 0x19, 0xd0, 0xf0, 0xbc, 0x4, + 0x0, + + /* U+6603 "昃" */ + 0x1f, 0xfc, 0x20, 0x8, 0x40, 0x10, 0xff, 0xe1, + 0x0, 0x43, 0xff, 0x80, 0x0, 0x1f, 0xff, 0x20, + 0x80, 0x41, 0x0, 0x83, 0x1, 0x9, 0x4, 0x21, + 0x9, 0x81, 0xc4, 0x0, 0x0, + + /* U+6606 "昆" */ + 0xff, 0xf2, 0x0, 0x48, 0x1, 0x3f, 0xfc, 0x80, + 0x12, 0x0, 0x4f, 0xff, 0x0, 0x0, 0x82, 0xb, + 0xf8, 0xc8, 0x3c, 0x20, 0x81, 0x82, 0x6, 0xe8, + 0x2c, 0x3f, 0x80, + + /* U+660A "昊" */ + 0x3f, 0xf8, 0x40, 0x10, 0xff, 0xe1, 0x0, 0x43, + 0xff, 0x80, 0x0, 0x1f, 0xff, 0x0, 0x80, 0x1, + 0x1, 0xff, 0xfc, 0xa, 0x0, 0x32, 0x1, 0x83, + 0x1c, 0x1, 0xc0, + + /* U+660C "昌" */ + 0x7f, 0xf2, 0x0, 0x90, 0x4, 0xff, 0xe4, 0x1, + 0x3f, 0xf8, 0x0, 0x0, 0x0, 0xff, 0xfc, 0x0, + 0x7f, 0xff, 0x0, 0x18, 0x0, 0xff, 0xfe, 0x0, + 0x20, + + /* U+660E "明" */ + 0x1, 0xff, 0xc8, 0x62, 0x43, 0x12, 0x18, 0x90, + 0xc4, 0xff, 0xe4, 0x31, 0x21, 0x89, 0xc, 0x4f, + 0xe2, 0x43, 0xf4, 0x18, 0x20, 0x82, 0x4, 0x21, + 0xc0, + + /* U+660F "昏" */ + 0x0, 0x17, 0xfd, 0x20, 0x81, 0xff, 0xf8, 0x10, + 0x40, 0x47, 0xe1, 0xe0, 0x0, 0x7f, 0xe2, 0x1, + 0x10, 0x8, 0xff, 0xc4, 0x2, 0x3f, 0xf1, 0x0, + 0x80, + + /* U+6613 "易" */ + 0x3f, 0xf8, 0x80, 0x22, 0x0, 0x8f, 0xfe, 0x20, + 0x8, 0xff, 0xe0, 0x0, 0x4, 0x0, 0x3f, 0xfd, + 0x91, 0x18, 0x44, 0x42, 0x21, 0x31, 0xf, 0xc, + 0x20, 0xcf, 0x0, + + /* U+6614 "昔" */ + 0x8, 0x20, 0x10, 0x41, 0xff, 0xf0, 0x41, 0x0, + 0x82, 0x1f, 0xff, 0xc0, 0x0, 0x0, 0x0, 0x3f, + 0xf8, 0x40, 0x10, 0xff, 0xe1, 0x0, 0x42, 0x0, + 0x87, 0xff, 0x8, 0x2, 0x0, + + /* U+6615 "昕" */ + 0x0, 0xb, 0xef, 0xc8, 0xa0, 0x22, 0x80, 0x8a, + 0x2, 0x28, 0xf, 0xbf, 0xe2, 0x84, 0x8a, 0x12, + 0x28, 0x48, 0xa1, 0x3f, 0x4, 0x84, 0x10, 0x10, + 0x40, 0x81, 0x0, + + /* U+6619 "昙" */ + 0x3f, 0xf8, 0x80, 0x11, 0x0, 0x21, 0xff, 0xc4, + 0x0, 0x87, 0xff, 0x0, 0x0, 0x1f, 0xfc, 0x0, + 0x0, 0x0, 0x3, 0xff, 0xf8, 0x60, 0x1, 0x3, + 0xf, 0xff, 0x0, 0x1, 0x0, + + /* U+661D "昝" */ + 0x10, 0x40, 0x3c, 0x80, 0x89, 0xc3, 0x12, 0x69, + 0x44, 0x21, 0x80, 0xc, 0xff, 0xe0, 0x0, 0x3f, + 0xf8, 0x40, 0x10, 0x80, 0x21, 0xff, 0xc2, 0x0, + 0x87, 0xff, 0x8, 0x2, 0x0, + + /* U+661F "星" */ + 0x3f, 0xf8, 0x80, 0x11, 0x0, 0x21, 0xff, 0xc4, + 0x0, 0x87, 0xff, 0x0, 0x40, 0x10, 0x80, 0x3f, + 0xfc, 0x82, 0x3, 0x4, 0x5, 0xff, 0xe0, 0x10, + 0x1f, 0xff, 0xc0, + + /* U+6620 "映" */ + 0x0, 0x43, 0xc1, 0x9, 0x4, 0x24, 0xfe, 0x92, + 0x4a, 0x49, 0x29, 0x24, 0xbc, 0x92, 0x97, 0xfe, + 0x41, 0x9, 0xa, 0x24, 0x28, 0xf1, 0x12, 0x18, + 0x30, 0x80, 0x0, + + /* U+6625 "春" */ + 0x0, 0x0, 0x8, 0x7, 0xff, 0x80, 0x80, 0x2, + 0x1, 0xff, 0xe0, 0x40, 0x3f, 0xff, 0x10, 0x40, + 0xff, 0xc6, 0x2, 0xe8, 0x8, 0x1f, 0xe0, 0x80, + 0x81, 0xfe, 0x8, 0x8, + + /* U+6627 "昧" */ + 0x0, 0x83, 0xc2, 0x9, 0x8, 0x25, 0xfe, 0x90, + 0x82, 0x42, 0xf, 0x7f, 0xe4, 0x30, 0x91, 0xa2, + 0x4a, 0x89, 0x29, 0x3d, 0x22, 0x88, 0x8c, 0x42, + 0x0, 0x8, 0x0, + + /* U+6628 "昨" */ + 0x0, 0x0, 0x4, 0xf, 0x10, 0x24, 0xff, 0x96, + 0x82, 0x52, 0x9, 0x8, 0x3c, 0x3f, 0x90, 0x82, + 0x42, 0x9, 0x8, 0x24, 0x3f, 0x90, 0x83, 0xc2, + 0x9, 0x8, 0x0, 0x20, + + /* U+662D "昭" */ + 0xf3, 0xfe, 0x42, 0x19, 0x8, 0x64, 0x61, 0x91, + 0x7, 0xc9, 0xe9, 0x0, 0x24, 0xff, 0x92, 0x6, + 0x48, 0x19, 0x20, 0x7c, 0x81, 0x93, 0xfc, 0x8, + 0x10, + + /* U+662F "是" */ + 0x3f, 0xf0, 0x40, 0x20, 0x80, 0x41, 0xff, 0x82, + 0x1, 0x7, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, + 0xfe, 0x22, 0x0, 0x87, 0xe1, 0x88, 0x4, 0xd0, + 0x18, 0xff, 0xe0, 0x0, 0x0, + + /* U+6631 "昱" */ + 0x3f, 0xf8, 0x40, 0x10, 0x80, 0x21, 0xff, 0xc2, + 0x0, 0x84, 0x1, 0xf, 0xfe, 0x0, 0x80, 0x1, + 0x0, 0xff, 0xf8, 0x0, 0x0, 0x41, 0x0, 0x44, + 0x1f, 0xff, 0xc0, + + /* U+6634 "昴" */ + 0x7f, 0xf2, 0x0, 0x90, 0x4, 0xff, 0xe4, 0x1, + 0x3f, 0xf8, 0x0, 0xf, 0x80, 0x81, 0xfc, 0x48, + 0x66, 0x43, 0xd2, 0x11, 0x17, 0x98, 0x83, 0x4, + 0x0, + + /* U+6635 "昵" */ + 0x3, 0xff, 0xc8, 0x19, 0x20, 0x64, 0x81, 0x92, + 0x6, 0x4f, 0xff, 0x20, 0x24, 0xa0, 0x92, 0x8e, + 0x4a, 0x49, 0x2e, 0x3d, 0x20, 0x84, 0x84, 0x32, + 0x10, 0x8f, 0x80, + + /* U+6636 "昶" */ + 0x30, 0x0, 0x11, 0xf8, 0x2, 0x17, 0xc4, 0x20, + 0x88, 0x41, 0x50, 0xba, 0xbf, 0x16, 0x42, 0x2c, + 0x84, 0xd9, 0x9, 0x2b, 0xf6, 0x48, 0x8, 0x98, + 0x1, 0xf, 0xdc, 0x3, 0x80, + + /* U+663C "昼" */ + 0x3f, 0xf8, 0x80, 0x11, 0x0, 0x21, 0xff, 0xc4, + 0x4, 0xf, 0xfc, 0x18, 0x6, 0x50, 0xb, 0xbf, + 0xf0, 0x40, 0x20, 0xff, 0xc0, 0x0, 0xf, 0xff, + 0xe0, + + /* U+663E "显" */ + 0x3f, 0xf8, 0x40, 0x10, 0x80, 0x21, 0xff, 0xc2, + 0x0, 0x84, 0x1, 0xf, 0xfe, 0x2, 0x20, 0x44, + 0x44, 0x48, 0x90, 0xd1, 0x20, 0x22, 0x0, 0x44, + 0x1f, 0xff, 0xc0, + + /* U+6641 "晁" */ + 0x7f, 0xf8, 0x80, 0x11, 0x0, 0x23, 0xff, 0xc4, + 0x0, 0x8f, 0xff, 0x0, 0x0, 0x2, 0x40, 0x64, + 0x98, 0x9, 0x40, 0x73, 0x87, 0x24, 0xe0, 0x88, + 0x22, 0x10, 0x78, 0x3f, 0x0, + + /* U+6643 "晃" */ + 0x3f, 0xf8, 0x80, 0x11, 0x0, 0x21, 0xff, 0xc4, + 0x0, 0x87, 0xff, 0xc, 0x40, 0xc, 0x98, 0x1, + 0x1, 0xff, 0xfc, 0x11, 0x0, 0x42, 0x10, 0x84, + 0x26, 0x8, 0x78, 0x1f, 0x0, + + /* U+664B "晋" */ + 0xff, 0xfe, 0x8, 0x80, 0x91, 0x20, 0x22, 0xf, + 0xff, 0xe0, 0x0, 0x0, 0x0, 0x1f, 0xfc, 0x20, + 0x8, 0x7f, 0xf0, 0x80, 0x21, 0x0, 0x43, 0xff, + 0x84, 0x1, 0x0, + + /* U+664C "晌" */ + 0x0, 0x43, 0xc1, 0x9, 0x7f, 0xe5, 0x1, 0x94, + 0x6, 0x53, 0xdf, 0x49, 0x65, 0x25, 0x94, 0x96, + 0x52, 0x59, 0x49, 0x65, 0x3d, 0xf4, 0x6, 0x10, + 0x10, 0x43, 0xc0, + + /* U+664F "晏" */ + 0x3f, 0xf1, 0x0, 0x44, 0x1, 0xf, 0xfc, 0x40, + 0x10, 0xff, 0xc0, 0x0, 0x3f, 0xff, 0x84, 0x7, + 0xff, 0xf1, 0x2, 0xe, 0x10, 0x7, 0x80, 0x71, + 0xee, 0x0, 0x0, + + /* U+6652 "晒" */ + 0xf7, 0xff, 0x21, 0x42, 0x42, 0x84, 0xbf, 0xe9, + 0x4a, 0x5e, 0x94, 0xa5, 0x29, 0x4a, 0x92, 0x95, + 0x3d, 0x2c, 0xa, 0x50, 0x17, 0xa0, 0x29, 0x7f, + 0xc0, 0x80, 0x80, + + /* U+6653 "晓" */ + 0x2, 0x0, 0x4, 0x3, 0xc4, 0xf4, 0xbe, 0x9, + 0x11, 0x92, 0x14, 0x24, 0x30, 0xf9, 0xb2, 0x94, + 0x1d, 0x20, 0x2, 0x5f, 0xf4, 0x89, 0xf, 0x12, + 0x30, 0x44, 0x41, 0x88, 0x8e, 0x1e, + + /* U+6654 "晔" */ + 0x1, 0x0, 0x2, 0x43, 0xc4, 0x94, 0x91, 0x69, + 0x63, 0x13, 0x4c, 0x26, 0xa8, 0xf9, 0x12, 0x92, + 0x3d, 0x24, 0x2, 0x41, 0x4, 0xbf, 0xf9, 0x4, + 0x1e, 0x8, 0x24, 0x10, 0x0, 0x20, + + /* U+6655 "晕" */ + 0x3f, 0xf8, 0x40, 0x10, 0xff, 0xe1, 0x0, 0x43, + 0xff, 0x80, 0x0, 0x1f, 0xff, 0xa1, 0x1, 0x3f, + 0xfc, 0x10, 0x0, 0x42, 0x0, 0x7f, 0xcf, 0xff, + 0xe0, 0x10, 0x0, 0x20, 0x0, + + /* U+6656 "晖" */ + 0xf7, 0xfd, 0x29, 0xa, 0x44, 0x4, 0xbf, 0xe9, + 0x10, 0x1e, 0x48, 0x25, 0x90, 0x4b, 0xfe, 0x90, + 0x41, 0x20, 0x82, 0x5f, 0xff, 0x82, 0x9, 0x4, + 0x0, 0x8, 0x0, + + /* U+6657 "晗" */ + 0x0, 0x40, 0x1, 0xf, 0xa, 0x24, 0x44, 0x92, + 0x4a, 0x51, 0x19, 0x0, 0x3c, 0x7c, 0x90, 0x12, + 0x40, 0x9, 0x3f, 0xa4, 0x82, 0xf2, 0xa, 0x48, + 0x28, 0x3f, 0x80, 0x82, + + /* U+665A "晚" */ + 0x0, 0x0, 0x2, 0x3, 0xc7, 0xc4, 0x90, 0x89, + 0x42, 0x12, 0xff, 0xa5, 0x11, 0x7a, 0x22, 0x94, + 0x45, 0x2f, 0xfa, 0x42, 0x4, 0x85, 0x9, 0x12, + 0x1e, 0x64, 0x61, 0x88, 0x86, 0x1e, + + /* U+665F "晟" */ + 0x3f, 0xfc, 0x20, 0x4, 0x3f, 0xfc, 0x20, 0x4, + 0x3f, 0xfc, 0x0, 0x10, 0x0, 0x48, 0x3f, 0xfe, + 0x20, 0x40, 0x3e, 0x48, 0x22, 0x30, 0x22, 0x21, + 0x2e, 0x52, 0x40, 0x92, 0xc1, 0xe, + + /* U+6661 "晡" */ + 0x0, 0x49, 0xef, 0xfe, 0x41, 0x4, 0x82, 0x9, + 0x7f, 0xde, 0x88, 0xa5, 0xff, 0x4a, 0x22, 0x94, + 0x45, 0x2f, 0xfb, 0xd1, 0x14, 0xa2, 0x28, 0x44, + 0x40, 0x8b, 0x80, + + /* U+6664 "晤" */ + 0xf7, 0xfe, 0x44, 0x9, 0x7f, 0xa4, 0x42, 0x91, + 0xb, 0xc4, 0x29, 0x7f, 0xe4, 0x0, 0x93, 0xfa, + 0x48, 0x2f, 0x20, 0xa4, 0x82, 0x83, 0xf8, 0x8, + 0x20, + + /* U+6666 "晦" */ + 0x0, 0x0, 0x2, 0x3, 0xcf, 0xf4, 0x90, 0x9, + 0x40, 0x12, 0x7f, 0x24, 0x82, 0x49, 0x24, 0xf2, + 0x9, 0x2f, 0xfe, 0x48, 0x24, 0xa2, 0x49, 0x40, + 0x9e, 0xff, 0xa0, 0x2, 0x0, 0x38, + + /* U+6668 "晨" */ + 0x1f, 0xfc, 0x10, 0x4, 0x1f, 0xfc, 0x10, 0x4, + 0x1f, 0xfc, 0x0, 0x0, 0x3f, 0xfe, 0x20, 0x0, + 0x2f, 0xfc, 0x20, 0x0, 0x3f, 0xfe, 0x24, 0x84, + 0x64, 0x78, 0x44, 0x98, 0x87, 0x6, + + /* U+666E "普" */ + 0x8, 0x20, 0x8, 0x81, 0xff, 0xf8, 0x22, 0x2, + 0x44, 0x80, 0x88, 0x3f, 0xff, 0x80, 0x0, 0x3f, + 0xf8, 0x40, 0x10, 0x80, 0x21, 0xff, 0xc2, 0x0, + 0x87, 0xff, 0x8, 0x2, 0x0, + + /* U+666F "景" */ + 0x3f, 0xf8, 0x40, 0x10, 0xff, 0xe1, 0x0, 0x43, + 0xff, 0x80, 0x20, 0x3f, 0xff, 0x80, 0x0, 0x3f, + 0xf8, 0x40, 0x10, 0xff, 0xe0, 0x8, 0x0, 0xd3, + 0xe, 0x21, 0xb0, 0xc0, 0x80, + + /* U+6670 "晰" */ + 0x2, 0x3, 0xc9, 0xf9, 0x24, 0x25, 0xd0, 0x92, + 0x42, 0x49, 0xff, 0x74, 0xa6, 0xb2, 0x9a, 0x4a, + 0x69, 0x29, 0x24, 0xa4, 0x92, 0xf2, 0x8a, 0x4a, + 0x20, 0x30, 0x80, + + /* U+6674 "晴" */ + 0x0, 0x43, 0xdf, 0xf9, 0x4, 0x25, 0xff, 0x90, + 0x42, 0x5f, 0xff, 0x0, 0x24, 0xfe, 0x92, 0xa, + 0x4f, 0xe9, 0x20, 0xa4, 0xfe, 0xf2, 0xa, 0x8, + 0x20, 0x23, 0x80, + + /* U+6676 "晶" */ + 0x3f, 0xf1, 0x0, 0x43, 0xff, 0x10, 0x4, 0x3f, + 0xf0, 0x0, 0x0, 0x0, 0x3f, 0x3f, 0x84, 0x87, + 0xf3, 0xf8, 0x48, 0x61, 0x21, 0xfc, 0xfe, 0x12, + 0x10, + + /* U+6677 "晷" */ + 0x3f, 0xf8, 0x40, 0x10, 0xff, 0xe1, 0x0, 0x43, + 0xff, 0x81, 0x4, 0x7, 0xee, 0x38, 0x93, 0xe, + 0x20, 0x37, 0xff, 0x80, 0x0, 0xff, 0xc1, 0x0, + 0x83, 0xff, 0x4, 0x2, 0x0, + + /* U+667A "智" */ + 0x20, 0x0, 0x40, 0x1, 0xf9, 0xfc, 0x42, 0x10, + 0x84, 0x3f, 0xe8, 0x46, 0x10, 0x93, 0x3f, 0xc1, + 0x0, 0x7f, 0xf0, 0x80, 0x21, 0x0, 0x43, 0xff, + 0x84, 0x1, 0xf, 0xfe, 0x10, 0x4, + + /* U+667E "晾" */ + 0x0, 0x40, 0x1, 0xf, 0x7f, 0xe4, 0x0, 0x93, + 0xfa, 0x48, 0x2f, 0x20, 0xa4, 0x82, 0x93, 0xfa, + 0x41, 0x9, 0x25, 0x3c, 0x92, 0x84, 0x44, 0x1, + 0x10, 0x1c, 0x0, + + /* U+6682 "暂" */ + 0x20, 0x1, 0xfd, 0xf1, 0x2, 0x4, 0x84, 0xf, + 0xef, 0xe2, 0x11, 0x3f, 0xc2, 0x8, 0x84, 0x3f, + 0xf8, 0x40, 0x10, 0x80, 0x21, 0xff, 0xc2, 0x0, + 0x87, 0xff, 0x8, 0x2, 0x0, + + /* U+6684 "暄" */ + 0x0, 0x40, 0x0, 0x43, 0xdf, 0xfc, 0xa0, 0x19, + 0x3f, 0xd2, 0x0, 0x3c, 0xff, 0x49, 0x2, 0x93, + 0xfd, 0x24, 0xa, 0x48, 0x14, 0x9f, 0xee, 0x0, + 0x0, 0xff, 0xc0, + + /* U+6687 "暇" */ + 0xf7, 0xbd, 0x29, 0xa, 0x52, 0x14, 0xa4, 0x29, + 0x7b, 0xde, 0x80, 0x25, 0xff, 0x4a, 0x22, 0x94, + 0x29, 0x2f, 0x53, 0xd0, 0x44, 0xa0, 0x88, 0x46, + 0xc0, 0x90, 0x40, + + /* U+668C "暌" */ + 0x0, 0x0, 0xf, 0x83, 0xc2, 0xe4, 0xa5, 0x9, + 0x31, 0xd2, 0x21, 0x25, 0xfd, 0x7e, 0x20, 0x90, + 0x41, 0x20, 0x82, 0x5f, 0xf4, 0x82, 0x9, 0xe, + 0x1e, 0x32, 0x21, 0x83, 0x4, 0x0, + + /* U+6691 "暑" */ + 0x3f, 0xf8, 0x40, 0x10, 0xff, 0xe1, 0x0, 0x43, + 0xff, 0x80, 0x40, 0xf, 0xfb, 0x1, 0x18, 0xff, + 0xfe, 0x6, 0x0, 0x7f, 0xe7, 0x80, 0x41, 0xff, + 0x83, 0xff, 0x4, 0x2, 0x0, + + /* U+6696 "暖" */ + 0x7, 0xff, 0xc0, 0x9, 0x24, 0xa4, 0x92, 0x97, + 0xfe, 0x44, 0x9, 0x10, 0x3d, 0xff, 0x91, 0x2, + 0x4f, 0xf9, 0x30, 0xbd, 0x34, 0x84, 0x60, 0x26, + 0x70, 0x20, 0x0, + + /* U+6697 "暗" */ + 0x0, 0x40, 0x0, 0x43, 0xcf, 0xf4, 0x88, 0x49, + 0x9, 0x12, 0xff, 0xfc, 0x0, 0x48, 0x0, 0x91, + 0xf9, 0x24, 0x12, 0x47, 0xe7, 0x90, 0x49, 0x20, + 0x80, 0x3f, 0x0, 0x82, 0x0, + + /* U+669D "暝" */ + 0xf7, 0xff, 0x28, 0x6, 0x4f, 0xf4, 0x90, 0x29, + 0x20, 0x52, 0x7f, 0xbc, 0x81, 0x49, 0xfe, 0x90, + 0x41, 0x2f, 0xff, 0xc0, 0x44, 0xc, 0x48, 0x20, + 0x40, 0x80, 0x40, + + /* U+66A7 "暧" */ + 0x7, 0xff, 0xc0, 0x9, 0x24, 0xa4, 0x94, 0x97, + 0xfe, 0x50, 0x1f, 0x10, 0x25, 0xff, 0x91, 0x2, + 0x47, 0xe9, 0x30, 0xbd, 0x24, 0x84, 0x60, 0x26, + 0x70, 0x20, 0x0, + + /* U+66A8 "暨" */ + 0x7e, 0x7c, 0xfd, 0x21, 0xa, 0x43, 0xf7, 0xf4, + 0x43, 0xb, 0x4a, 0x58, 0x61, 0x1f, 0xfc, 0x20, + 0x8, 0x7f, 0xf0, 0x80, 0x20, 0xff, 0x8f, 0xff, + 0xe0, + + /* U+66AE "暮" */ + 0x8, 0x43, 0xff, 0xf0, 0x84, 0xd, 0xec, 0x3f, + 0xf0, 0x80, 0x43, 0xff, 0x1, 0x0, 0xff, 0xfc, + 0x41, 0x7, 0xff, 0x28, 0xb, 0x3f, 0xe0, 0x7f, + 0x82, 0x2, 0x0, + + /* U+66B4 "暴" */ + 0x3f, 0xf1, 0x0, 0x43, 0xff, 0x10, 0x4, 0x3f, + 0xf0, 0x21, 0x7, 0xff, 0x82, 0x10, 0xff, 0xfc, + 0x61, 0x3, 0x97, 0x30, 0x43, 0xd, 0xc3, 0xc4, + 0xf0, 0xe0, 0x0, + + /* U+66B9 "暹" */ + 0x47, 0xfc, 0x24, 0x4, 0x27, 0xfc, 0x4, 0x4, + 0x7, 0xfc, 0xe2, 0x20, 0x23, 0xfe, 0x24, 0x20, + 0x2f, 0xfc, 0x24, 0x20, 0x27, 0xf8, 0x27, 0xfe, + 0x74, 0x0, 0xd8, 0x0, 0x87, 0xfe, + + /* U+66BE "暾" */ + 0x2, 0x11, 0xc4, 0x42, 0xbe, 0x85, 0x1, 0xea, + 0xfa, 0x55, 0x1c, 0xbb, 0xf9, 0x50, 0x12, 0xaf, + 0x99, 0x42, 0x32, 0x88, 0x67, 0x7c, 0xca, 0x22, + 0x80, 0x48, 0x83, 0x90, 0x0, + + /* U+66D9 "曙" */ + 0x7, 0xfd, 0xe9, 0x2a, 0x52, 0x54, 0xbf, 0xe9, + 0x8, 0x12, 0x7f, 0x3c, 0x24, 0x4b, 0xff, 0x90, + 0x41, 0x23, 0xf2, 0x58, 0x27, 0x8f, 0xc9, 0x20, + 0x80, 0x3f, 0x0, 0x82, 0x0, + + /* U+66DB "曛" */ + 0x0, 0xfd, 0xee, 0x82, 0x41, 0x4, 0xbf, 0xe9, + 0x7f, 0xd2, 0x88, 0xbd, 0x55, 0x4b, 0xfe, 0x90, + 0x41, 0x2f, 0xfa, 0x41, 0x7, 0xff, 0xf8, 0x40, + 0x50, 0xa4, 0x82, 0x24, 0x80, + + /* U+66DC "曜" */ + 0xf7, 0xbd, 0x29, 0x4a, 0x4a, 0x54, 0x9d, 0xe9, + 0x48, 0x5e, 0x44, 0x24, 0xff, 0xca, 0x20, 0x9f, + 0xfd, 0x28, 0x83, 0xdf, 0xf4, 0xa2, 0x9, 0x7f, + 0xc0, 0x80, 0x0, + + /* U+66DD "曝" */ + 0xf7, 0xfd, 0x28, 0xa, 0x5f, 0xf4, 0xa0, 0x29, + 0x7f, 0xd2, 0x22, 0x3d, 0xff, 0xc8, 0x88, 0x9f, + 0xff, 0x24, 0x22, 0x49, 0x64, 0xea, 0x3f, 0x15, + 0x11, 0xc9, 0xc0, 0x70, 0x0, + + /* U+66E6 "曦" */ + 0x1, 0x11, 0xe2, 0x22, 0x5b, 0xb4, 0xbf, 0xe9, + 0x4, 0x13, 0xff, 0xfd, 0xe2, 0x49, 0x12, 0x9f, + 0xff, 0x24, 0x22, 0x77, 0x54, 0xbc, 0xcf, 0x79, + 0x32, 0x15, 0x40, 0xe1, 0x0, + + /* U+66E9 "曩" */ + 0x3f, 0xf1, 0x0, 0x47, 0xff, 0xf, 0xfc, 0x0, + 0x1, 0xff, 0xe7, 0xcf, 0x9f, 0x3e, 0x7f, 0xf8, + 0x21, 0x7, 0xff, 0x3f, 0xff, 0x5, 0x11, 0xe3, + 0x89, 0xf3, 0xc0, + + /* U+66F0 "曰" */ + 0xff, 0xf8, 0x1, 0x80, 0x18, 0x1, 0x80, 0x18, + 0x1, 0xff, 0xd8, 0x1, 0x80, 0x18, 0x1, 0x80, + 0x1f, 0xff, 0x80, 0x18, 0x1, + + /* U+66F2 "曲" */ + 0x8, 0x80, 0x44, 0x3f, 0xff, 0x11, 0x18, 0x88, + 0xc4, 0x46, 0x22, 0x31, 0x11, 0xff, 0xfc, 0x44, + 0x62, 0x23, 0x11, 0x18, 0x88, 0xff, 0xfe, 0x0, + 0x20, + + /* U+66F3 "曳" */ + 0x2, 0x0, 0x4, 0x1, 0xff, 0xe2, 0x10, 0x44, + 0x20, 0x8f, 0xff, 0x10, 0x82, 0x21, 0x4, 0x7f, + 0xf8, 0x82, 0x0, 0x4, 0x40, 0xb, 0x10, 0x38, + 0x2f, 0x88, 0xa0, 0xf, 0x0, + + /* U+66F4 "更" */ + 0xff, 0xfc, 0x2, 0x1, 0xff, 0xe2, 0x10, 0x44, + 0x20, 0x8f, 0xff, 0x10, 0x82, 0x21, 0x4, 0x7f, + 0xf8, 0x44, 0x0, 0x48, 0x0, 0x60, 0x0, 0xf0, + 0xe, 0x1f, 0xa0, 0x0, 0x0, + + /* U+66F7 "曷" */ + 0x3f, 0xf8, 0x80, 0x22, 0x0, 0x8f, 0xfe, 0x20, + 0x8, 0xff, 0xe0, 0x80, 0x7, 0xff, 0x61, 0x87, + 0x8e, 0x12, 0xc6, 0x48, 0x1, 0x3f, 0xf4, 0x0, + 0x10, 0x7, 0x80, + + /* U+66F9 "曹" */ + 0x4, 0x41, 0xff, 0xfc, 0x11, 0x3, 0xff, 0xe4, + 0x44, 0x4f, 0xff, 0x91, 0x11, 0x3f, 0xfe, 0x0, + 0x0, 0x7f, 0xf0, 0x80, 0x21, 0xff, 0xc2, 0x0, + 0x87, 0xff, 0x8, 0x2, 0x0, + + /* U+66FC "曼" */ + 0x1f, 0xf8, 0x40, 0x10, 0x7f, 0xe1, 0x0, 0x41, + 0xff, 0x80, 0x0, 0x1f, 0xff, 0x22, 0x22, 0x7f, + 0xfc, 0x0, 0x0, 0xff, 0xe0, 0x40, 0x80, 0x66, + 0x0, 0xf8, 0x3e, 0xf, 0x80, + + /* U+66FE "曾" */ + 0x10, 0x40, 0x44, 0x3f, 0xff, 0x4, 0x19, 0x24, + 0xc1, 0x7, 0xff, 0xe0, 0x0, 0x7f, 0xf2, 0x0, + 0x90, 0x4, 0xff, 0xe4, 0x1, 0x3f, 0xf9, 0x0, + 0x40, + + /* U+66FF "替" */ + 0x8, 0x20, 0x10, 0x43, 0xfb, 0xf0, 0x41, 0xf, + 0xef, 0xc3, 0xa, 0xd, 0xb6, 0x60, 0x2, 0x3f, + 0xf8, 0x40, 0x10, 0x80, 0x21, 0xff, 0xc2, 0x0, + 0x87, 0xff, 0x8, 0x2, 0x0, + + /* U+6700 "最" */ + 0x3f, 0xf8, 0x40, 0x10, 0xff, 0xe1, 0x0, 0x43, + 0xff, 0x80, 0x0, 0x3f, 0xff, 0x91, 0x0, 0x3e, + 0xfc, 0x44, 0x88, 0xf8, 0xa1, 0x11, 0x82, 0x39, + 0x1f, 0xcd, 0x80, 0xa0, 0x80, + + /* U+6708 "月" */ + 0x1f, 0xf8, 0x80, 0x44, 0x2, 0x20, 0x11, 0x0, + 0x8f, 0xfc, 0x40, 0x22, 0x1, 0x10, 0x8, 0xff, + 0xc4, 0x2, 0x40, 0x12, 0x0, 0xb0, 0x5, 0x1, + 0xc0, + + /* U+6709 "有" */ + 0x1, 0x0, 0x4, 0x1, 0xff, 0xf8, 0x20, 0x0, + 0x80, 0x1, 0xff, 0x4, 0x2, 0x18, 0x4, 0x4f, + 0xf8, 0x20, 0x10, 0x3f, 0xe0, 0x80, 0x41, 0x0, + 0x82, 0x1, 0x4, 0x1e, 0x0, + + /* U+670A "朊" */ + 0x79, 0xfc, 0x90, 0x1, 0x20, 0x3, 0xc0, 0x4, + 0xbf, 0xe9, 0x12, 0x12, 0x24, 0x3c, 0x48, 0x48, + 0x90, 0x91, 0x21, 0x24, 0x4a, 0x48, 0x98, 0xa1, + 0x37, 0x43, 0xc0, + + /* U+670B "朋" */ + 0x3e, 0x7e, 0x44, 0x84, 0x89, 0x9, 0x12, 0x13, + 0xe7, 0xe4, 0x48, 0x48, 0x90, 0x91, 0x21, 0x3e, + 0x7e, 0x44, 0x84, 0x89, 0x9, 0x12, 0x14, 0x24, + 0x28, 0x50, 0x53, 0xa3, 0x80, + + /* U+670D "服" */ + 0x79, 0xfc, 0x49, 0x4, 0x49, 0x4, 0x49, 0x3c, + 0x79, 0x0, 0x49, 0x0, 0x49, 0xfc, 0x79, 0x44, + 0x49, 0x48, 0x49, 0x28, 0x49, 0x30, 0x49, 0x38, + 0x89, 0x4c, 0xb9, 0x82, + + /* U+6710 "朐" */ + 0x0, 0x80, 0xf1, 0x1, 0x27, 0xfa, 0x48, 0x17, + 0xa0, 0x29, 0x3e, 0x52, 0x44, 0xa4, 0x89, 0x79, + 0x12, 0x92, 0x25, 0x27, 0xca, 0x40, 0x14, 0x80, + 0x31, 0x0, 0x6e, 0x1f, 0x0, + + /* U+6714 "朔" */ + 0x40, 0x1, 0x13, 0xf2, 0x88, 0x7f, 0xa1, 0x10, + 0x84, 0x42, 0x19, 0x2f, 0xe4, 0xa1, 0x92, 0x86, + 0x4a, 0x17, 0xef, 0xc4, 0x21, 0x21, 0x4, 0x84, + 0x14, 0x30, 0x60, 0x8f, + + /* U+6715 "朕" */ + 0x0, 0x0, 0xf1, 0x11, 0x21, 0x42, 0x5f, 0xf7, + 0x82, 0x9, 0x4, 0x12, 0x8, 0x25, 0xff, 0x78, + 0x20, 0x90, 0xa1, 0x21, 0x42, 0x46, 0x44, 0x88, + 0x91, 0x20, 0xaf, 0x80, 0x80, + + /* U+6717 "朗" */ + 0x20, 0x0, 0x8f, 0xff, 0x43, 0xa, 0x18, 0x50, + 0xfe, 0xfe, 0x14, 0x30, 0xa1, 0xfd, 0xc, 0xf, + 0xe6, 0x43, 0x12, 0x1b, 0x70, 0xe1, 0x6, 0x19, + 0xe0, + + /* U+671B "望" */ + 0x10, 0x0, 0x21, 0xfb, 0xfa, 0x12, 0x7, 0xe4, + 0x8, 0x48, 0x10, 0x93, 0xbf, 0x38, 0x82, 0x0, + 0x8, 0xff, 0xf8, 0x4, 0x0, 0x8, 0x3, 0xff, + 0x80, 0x20, 0x3f, 0xff, 0x80, + + /* U+671D "朝" */ + 0x10, 0x3, 0xfb, 0xf1, 0x8, 0x44, 0x21, 0x7e, + 0x86, 0xb, 0xf7, 0xe8, 0x60, 0xa1, 0x82, 0x85, + 0xfb, 0xf1, 0x8, 0x7f, 0xa1, 0x11, 0x84, 0x44, + 0x11, 0x23, 0xc0, + + /* U+671F "期" */ + 0x42, 0x7d, 0x9, 0x1f, 0xf4, 0x50, 0x91, 0x42, + 0x45, 0xf9, 0xf4, 0x24, 0x5f, 0x91, 0x42, 0x7d, + 0x9, 0x1f, 0xf4, 0x40, 0x11, 0x24, 0x85, 0xa, + 0x18, 0x11, 0xc0, + + /* U+6726 "朦" */ + 0x0, 0x88, 0x7b, 0xfe, 0x48, 0x88, 0x4b, 0xfe, + 0x4a, 0x2, 0x78, 0xf8, 0x48, 0x0, 0x4b, 0xfe, + 0x48, 0x40, 0x7b, 0xcc, 0x48, 0x78, 0x4b, 0xa8, + 0x48, 0xd4, 0x8b, 0x12, 0xb8, 0xe0, + + /* U+6728 "木" */ + 0x1, 0x0, 0x1, 0x0, 0x7f, 0xfe, 0x3, 0x80, + 0x3, 0x40, 0x3, 0x40, 0x5, 0x20, 0x9, 0x10, + 0x19, 0x18, 0x31, 0xc, 0x61, 0x6, 0x1, 0x0, + 0x1, 0x0, 0x1, 0x0, + + /* U+672A "未" */ + 0x1, 0x0, 0x2, 0x1, 0xff, 0xf0, 0x8, 0x0, + 0x10, 0x0, 0x20, 0x3f, 0xff, 0x81, 0xc0, 0x5, + 0x40, 0x12, 0x40, 0x64, 0xc1, 0x88, 0xc6, 0x10, + 0x50, 0x20, 0x40, 0x40, 0x0, + + /* U+672B "末" */ + 0x1, 0x0, 0x2, 0x1, 0xff, 0xf8, 0x8, 0x0, + 0x10, 0x0, 0x20, 0xf, 0xff, 0x0, 0xc0, 0x3, + 0x40, 0xa, 0x40, 0x24, 0x40, 0x88, 0x42, 0x10, + 0x68, 0x20, 0x0, 0x40, 0x0, + + /* U+672C "本" */ + 0x1, 0x0, 0x1, 0x0, 0x1, 0x0, 0x7f, 0xfe, + 0x3, 0x80, 0x3, 0x40, 0x5, 0x20, 0x5, 0x20, + 0x9, 0x10, 0x19, 0x8, 0x31, 0x4, 0x6f, 0xf3, + 0x1, 0x0, 0x1, 0x0, 0x1, 0x0, + + /* U+672D "札" */ + 0x10, 0x40, 0x20, 0x80, 0x41, 0x7, 0xf2, 0x1, + 0x4, 0x6, 0x8, 0xe, 0x10, 0x2a, 0x20, 0x56, + 0x41, 0x20, 0x82, 0x41, 0x0, 0x82, 0x11, 0x4, + 0x22, 0x8, 0x44, 0xf, 0x80, + + /* U+672F "术" */ + 0x1, 0x0, 0x2, 0x60, 0x4, 0x60, 0x8, 0x4f, + 0xff, 0xe0, 0x70, 0x1, 0x60, 0x2, 0xa0, 0x9, + 0x20, 0x12, 0x60, 0x44, 0x61, 0x8, 0x6c, 0x10, + 0x20, 0x20, 0x0, 0x40, 0x0, + + /* U+6731 "朱" */ + 0x2, 0x0, 0x48, 0x2, 0x20, 0xf, 0xfe, 0x42, + 0x1, 0x8, 0x0, 0x20, 0x3f, 0xff, 0x3, 0x0, + 0x1a, 0x0, 0xa4, 0x4, 0x88, 0x62, 0x1a, 0x8, + 0x10, 0x20, 0x0, + + /* U+6734 "朴" */ + 0x10, 0x40, 0x20, 0x80, 0x41, 0x7, 0xf2, 0x1, + 0x4, 0x6, 0xc, 0xe, 0x14, 0x2a, 0x26, 0x52, + 0x47, 0x20, 0x82, 0x41, 0x0, 0x82, 0x1, 0x4, + 0x2, 0x8, 0x4, 0x10, 0x0, + + /* U+6735 "朵" */ + 0xf, 0xf0, 0x10, 0x20, 0x40, 0x40, 0x80, 0x82, + 0x1, 0xe8, 0x20, 0x0, 0x40, 0x7f, 0xff, 0x3, + 0x80, 0xa, 0x80, 0x24, 0xc1, 0x88, 0x4c, 0x10, + 0x60, 0x20, 0x0, + + /* U+673A "机" */ + 0x20, 0x0, 0x43, 0xe0, 0x84, 0x47, 0xc8, 0x82, + 0x11, 0x4, 0x22, 0x1c, 0x44, 0x34, 0x88, 0xa1, + 0x11, 0x42, 0x20, 0x84, 0x41, 0x10, 0x92, 0x21, + 0x24, 0x82, 0x4a, 0x7, 0x0, + + /* U+673D "朽" */ + 0x10, 0x0, 0x27, 0xfc, 0x44, 0x7, 0xe8, 0x1, + 0x10, 0x6, 0x20, 0x16, 0x7f, 0x2a, 0x2, 0x50, + 0x5, 0x20, 0x8, 0x40, 0x10, 0x80, 0x21, 0x0, + 0x42, 0x1, 0x84, 0x3e, 0x0, + + /* U+6740 "杀" */ + 0x0, 0x0, 0xc0, 0xc0, 0xec, 0x0, 0xe0, 0x1c, + 0x61, 0x80, 0x60, 0x10, 0x3f, 0xff, 0x1, 0x0, + 0x4, 0x1, 0x12, 0x8, 0x46, 0xc1, 0x6, 0x4, + 0x0, 0xe0, 0x0, + + /* U+6742 "杂" */ + 0x4, 0x0, 0x8, 0x1, 0xff, 0x80, 0x21, 0x0, + 0x82, 0x22, 0x4, 0xb8, 0x4f, 0x0, 0x80, 0xff, + 0xfc, 0x2, 0x0, 0x44, 0x1, 0x89, 0x84, 0x10, + 0x90, 0x60, 0x83, 0x80, 0x0, + + /* U+6743 "权" */ + 0x10, 0x0, 0x27, 0xf8, 0x44, 0x13, 0xe8, 0x21, + 0x10, 0x42, 0x21, 0xe, 0x22, 0x1c, 0x44, 0x54, + 0x90, 0xa0, 0xa0, 0x40, 0x80, 0x82, 0x81, 0x8, + 0x82, 0x60, 0xc5, 0x0, 0x80, + + /* U+6746 "杆" */ + 0x20, 0x0, 0x43, 0xfc, 0x80, 0x87, 0xe1, 0x2, + 0x2, 0x4, 0x4, 0x18, 0x8, 0x3d, 0xff, 0x64, + 0x21, 0x40, 0x40, 0x80, 0x81, 0x1, 0x2, 0x2, + 0x4, 0x4, 0x8, 0x8, 0x0, + + /* U+6748 "杈" */ + 0x20, 0x0, 0x4f, 0xf8, 0x88, 0x17, 0xd0, 0x22, + 0x24, 0x84, 0x45, 0x1c, 0x4a, 0x34, 0x88, 0xa5, + 0x11, 0x41, 0x40, 0x81, 0x81, 0x3, 0x2, 0x19, + 0x4, 0x41, 0x8b, 0x1, 0x0, + + /* U+6749 "杉" */ + 0x20, 0x8, 0x40, 0x20, 0x80, 0x87, 0xe6, 0x2, + 0x10, 0x4, 0x1, 0x18, 0x4, 0x3e, 0x10, 0x64, + 0xc1, 0x42, 0xc, 0x80, 0x31, 0x0, 0x82, 0x6, + 0x4, 0x30, 0x8, 0x80, 0x0, + + /* U+674C "杌" */ + 0x20, 0x0, 0x4f, 0xfc, 0x84, 0x47, 0xc8, 0x82, + 0x11, 0x4, 0x22, 0x1c, 0x44, 0x34, 0x88, 0xa9, + 0x11, 0x42, 0x20, 0x84, 0x41, 0x10, 0x92, 0x21, + 0x24, 0x82, 0x49, 0x7, 0x80, + + /* U+674E "李" */ + 0x1, 0x0, 0x2, 0x3, 0xff, 0xf8, 0x1c, 0x0, + 0xd6, 0x6, 0x23, 0x30, 0x1, 0x8f, 0xf8, 0x0, + 0x60, 0x3, 0x3, 0xff, 0xf8, 0x8, 0x0, 0x10, + 0x0, 0x20, 0x3, 0xc0, 0x0, + + /* U+674F "杏" */ + 0x1, 0x0, 0x1, 0x0, 0x7f, 0xfe, 0x3, 0x40, + 0x5, 0x20, 0x9, 0x10, 0x31, 0xc, 0x40, 0x2, + 0x3f, 0xfc, 0x20, 0x4, 0x20, 0x4, 0x20, 0x4, + 0x20, 0x4, 0x3f, 0xfc, 0x20, 0x4, + + /* U+6750 "材" */ + 0x10, 0x8, 0x20, 0x10, 0x40, 0x27, 0xef, 0xf3, + 0x1, 0x86, 0x5, 0x16, 0xa, 0x2a, 0x24, 0x50, + 0x49, 0x21, 0x12, 0x44, 0x20, 0x90, 0x41, 0x40, + 0x82, 0x1, 0x4, 0x1c, 0x0, + + /* U+6751 "村" */ + 0x10, 0x8, 0x20, 0x10, 0x40, 0x27, 0xef, 0xf3, + 0x0, 0x86, 0x1, 0x14, 0x42, 0x2c, 0x44, 0x54, + 0x89, 0x20, 0x92, 0x40, 0x20, 0x80, 0x41, 0x0, + 0x82, 0x1, 0x4, 0x3c, 0x0, + + /* U+6753 "杓" */ + 0x10, 0x80, 0x44, 0x1, 0x1f, 0xff, 0x41, 0x32, + 0x4, 0xc8, 0x15, 0x20, 0x56, 0x21, 0x54, 0x86, + 0x41, 0x19, 0x0, 0x44, 0x1, 0x10, 0x4, 0x40, + 0x11, 0xf, 0x80, + + /* U+6756 "杖" */ + 0x20, 0x10, 0x40, 0x20, 0x80, 0x47, 0xdf, 0xf2, + 0x1, 0x4, 0x42, 0x1c, 0x44, 0x34, 0x88, 0xa9, + 0x21, 0x41, 0x40, 0x82, 0x81, 0x2, 0x2, 0xb, + 0x4, 0x61, 0x89, 0x0, 0x0, + + /* U+675C "杜" */ + 0x20, 0x20, 0x40, 0x40, 0x80, 0x87, 0xe1, 0x2, + 0x2, 0x4, 0x7f, 0xd8, 0x8, 0x3c, 0x10, 0x64, + 0x21, 0x40, 0x42, 0x80, 0x81, 0x1, 0x2, 0x2, + 0x4, 0xff, 0xc8, 0x0, 0x0, + + /* U+675E "杞" */ + 0x20, 0x0, 0x47, 0xf8, 0x80, 0x17, 0xe0, 0x22, + 0x0, 0x44, 0x0, 0x9e, 0x7f, 0x32, 0x80, 0xa1, + 0x1, 0x42, 0x0, 0x84, 0x9, 0x8, 0x12, 0x10, + 0x24, 0x1f, 0x88, 0x0, 0x0, + + /* U+675F "束" */ + 0x1, 0x1, 0xff, 0xfc, 0x4, 0x0, 0x8, 0x3, + 0xff, 0x88, 0x21, 0x10, 0x42, 0x20, 0x84, 0x3f, + 0xf8, 0x7, 0x0, 0x15, 0x0, 0x49, 0x1, 0x11, + 0x9c, 0x20, 0xc0, 0x40, 0x0, + + /* U+6760 "杠" */ + 0x20, 0x0, 0x47, 0xfc, 0x80, 0x87, 0xe1, 0x2, + 0x2, 0x4, 0x4, 0x14, 0x8, 0x36, 0x10, 0xa4, + 0x21, 0x40, 0x40, 0x80, 0x81, 0x1, 0x2, 0x2, + 0x4, 0xff, 0xc8, 0x0, 0x0, + + /* U+6761 "条" */ + 0x4, 0x0, 0x10, 0x0, 0x3f, 0xe0, 0xc0, 0x82, + 0x42, 0x8, 0x58, 0x0, 0xe0, 0x6, 0x38, 0xf1, + 0xe, 0x2, 0x1, 0xff, 0xf0, 0x8, 0x0, 0x92, + 0x6, 0x23, 0x30, 0x41, 0x3, 0x80, + + /* U+6765 "来" */ + 0x1, 0x0, 0x2, 0x1, 0xff, 0xf0, 0x8, 0x1, + 0x11, 0x1, 0x24, 0x0, 0x40, 0x7f, 0xff, 0x3, + 0x80, 0xa, 0x80, 0x24, 0x80, 0x88, 0xc6, 0x10, + 0xd0, 0x20, 0x40, 0x40, 0x0, + + /* U+6768 "杨" */ + 0x20, 0x0, 0x9f, 0xe2, 0x1, 0x3e, 0x18, 0x21, + 0x80, 0x88, 0x7, 0x7f, 0xdc, 0x49, 0xa9, 0x26, + 0x89, 0x12, 0x24, 0x49, 0x11, 0x28, 0x84, 0x84, + 0x12, 0x27, 0x80, + + /* U+6769 "杩" */ + 0x20, 0x0, 0x8f, 0xe2, 0x0, 0xbe, 0x82, 0x22, + 0x8, 0x88, 0x27, 0x20, 0x9a, 0xff, 0xa0, 0x6, + 0x80, 0x12, 0x7f, 0x48, 0x1, 0x20, 0x4, 0x80, + 0x12, 0x7, 0x80, + + /* U+676A "杪" */ + 0x0, 0x40, 0x81, 0x2, 0x4, 0x3e, 0x92, 0x22, + 0x48, 0x89, 0x17, 0x44, 0x1a, 0x10, 0xa0, 0x4a, + 0x80, 0x42, 0x3, 0x8, 0x18, 0x20, 0x80, 0x8c, + 0x2, 0xc0, 0x0, + + /* U+676D "杭" */ + 0x10, 0x20, 0x10, 0x20, 0x13, 0xfe, 0x7c, 0x0, + 0x10, 0x0, 0x11, 0xf8, 0x39, 0x8, 0x35, 0x8, + 0x51, 0x8, 0x51, 0x8, 0x11, 0x8, 0x11, 0x9, + 0x12, 0x9, 0x12, 0x9, 0x14, 0xe, + + /* U+676F "杯" */ + 0x20, 0x0, 0x4f, 0xfc, 0x81, 0x7, 0xc2, 0x2, + 0x8, 0x4, 0x10, 0x1c, 0x50, 0x34, 0xa8, 0xa2, + 0x49, 0x48, 0x8a, 0xb1, 0x9, 0x2, 0x2, 0x4, + 0x4, 0x8, 0x8, 0x10, 0x0, + + /* U+6770 "杰" */ + 0x2, 0x0, 0x8, 0x0, 0x20, 0x3f, 0xff, 0x6, + 0x80, 0x29, 0x1, 0x26, 0x8, 0x84, 0xc2, 0xc, + 0x8, 0x0, 0x0, 0x12, 0x22, 0x48, 0x8a, 0x11, + 0x18, 0x0, 0x40, + + /* U+6772 "杲" */ + 0x3f, 0xfc, 0x20, 0x4, 0x3f, 0xfc, 0x20, 0x4, + 0x20, 0x4, 0x3f, 0xfc, 0x0, 0x0, 0x1, 0x0, + 0x7f, 0xfe, 0x3, 0x40, 0x5, 0x20, 0x19, 0x18, + 0x61, 0x6, 0x1, 0x0, + + /* U+6773 "杳" */ + 0x0, 0x0, 0x1, 0x0, 0x7f, 0xfe, 0x3, 0x40, + 0x5, 0x20, 0x19, 0x18, 0x60, 0x6, 0x3f, 0xfc, + 0x20, 0x4, 0x20, 0x4, 0x3f, 0xfc, 0x20, 0x4, + 0x20, 0x4, 0x3f, 0xfc, 0x20, 0x4, + + /* U+6775 "杵" */ + 0x20, 0x80, 0x42, 0x0, 0x87, 0xf7, 0xd1, 0x2, + 0x22, 0x6, 0x4, 0x1a, 0x8, 0x33, 0xff, 0xa0, + 0x21, 0x40, 0x40, 0x80, 0x81, 0x1, 0x2, 0x2, + 0x4, 0x4, 0x8, 0x8, 0x0, + + /* U+6777 "杷" */ + 0x20, 0x0, 0x47, 0xf8, 0x89, 0x17, 0xd2, 0x22, + 0x24, 0x44, 0x48, 0x9c, 0x91, 0x35, 0xfe, 0xaa, + 0x1, 0x44, 0x0, 0x88, 0x9, 0x10, 0x12, 0x20, + 0x24, 0x40, 0x48, 0x7f, 0x0, + + /* U+677C "杼" */ + 0x20, 0x0, 0x8f, 0xf2, 0x0, 0xbe, 0x44, 0x20, + 0xe0, 0x81, 0x7, 0x7f, 0xea, 0x11, 0xa8, 0x46, + 0x81, 0x22, 0x4, 0x8, 0x10, 0x20, 0x40, 0x81, + 0x2, 0x38, 0x0, + + /* U+677E "松" */ + 0x20, 0x20, 0x42, 0x20, 0x84, 0x47, 0xc8, 0x42, + 0x20, 0x84, 0x40, 0x9d, 0x11, 0xb4, 0x60, 0xa0, + 0x81, 0x41, 0x0, 0x84, 0x41, 0x10, 0x42, 0x20, + 0x84, 0xff, 0x88, 0x1, 0x0, + + /* U+677F "板" */ + 0x20, 0x0, 0x47, 0xf0, 0x88, 0x7, 0xd0, 0x2, + 0x20, 0x4, 0x7f, 0x9c, 0xa1, 0x35, 0x44, 0xa2, + 0x89, 0x44, 0xa0, 0x91, 0x41, 0x21, 0x2, 0x45, + 0x5, 0x31, 0x8a, 0x81, 0x0, + + /* U+6781 "极" */ + 0x20, 0x0, 0x47, 0xf0, 0x84, 0x27, 0xc8, 0x82, + 0x13, 0x4, 0x27, 0x9c, 0x61, 0x34, 0xc2, 0xa1, + 0x49, 0x46, 0x90, 0x89, 0xc1, 0x11, 0x82, 0x45, + 0x85, 0x91, 0x8a, 0x40, 0x80, + + /* U+6784 "构" */ + 0x21, 0x0, 0x84, 0x2, 0x3f, 0xfe, 0x81, 0x24, + 0x4, 0x92, 0x17, 0x10, 0x5a, 0x51, 0xa2, 0x26, + 0x88, 0x92, 0x7f, 0x48, 0x5, 0x20, 0x4, 0x80, + 0x32, 0xf, 0x80, + + /* U+6787 "枇" */ + 0x22, 0x20, 0x44, 0x40, 0x88, 0x87, 0xd1, 0x32, + 0x22, 0x44, 0x75, 0x18, 0x8c, 0x3d, 0x10, 0xa2, + 0x21, 0x44, 0x40, 0x88, 0x81, 0x11, 0x12, 0x2a, + 0x24, 0x64, 0x48, 0x8f, 0x0, + + /* U+6789 "枉" */ + 0x20, 0x0, 0x47, 0xfc, 0x80, 0x87, 0xe1, 0x2, + 0x2, 0x4, 0x4, 0x18, 0x8, 0x3c, 0xff, 0x64, + 0x21, 0x40, 0x40, 0x80, 0x81, 0x1, 0x2, 0x2, + 0x4, 0x7f, 0xc8, 0x0, 0x0, + + /* U+678B "枋" */ + 0x20, 0x0, 0x40, 0x40, 0x8f, 0xff, 0xc4, 0x2, + 0x8, 0x4, 0x10, 0x1c, 0x3f, 0x34, 0x42, 0xa0, + 0x85, 0x41, 0x8, 0x84, 0x11, 0x8, 0x22, 0x20, + 0x44, 0x41, 0x8b, 0x1e, 0x0, + + /* U+6790 "析" */ + 0x20, 0x4, 0x47, 0xf0, 0x88, 0x7, 0xd0, 0x2, + 0x20, 0x4, 0x40, 0x1c, 0xff, 0xb9, 0x8, 0xaa, + 0x11, 0x44, 0x20, 0x88, 0x41, 0x10, 0x82, 0x41, + 0x4, 0x82, 0xa, 0x4, 0x0, + + /* U+6795 "枕" */ + 0x20, 0x40, 0x40, 0x80, 0x8f, 0xf7, 0xe2, 0x22, + 0x44, 0x44, 0x88, 0x9c, 0x10, 0x34, 0x50, 0xa8, + 0xa1, 0x41, 0x40, 0x84, 0x81, 0x9, 0x2, 0x22, + 0x44, 0x84, 0x8a, 0xf, 0x0, + + /* U+6797 "林" */ + 0x8, 0x20, 0x10, 0x40, 0x20, 0x83, 0xf7, 0xf1, + 0x82, 0x3, 0xe, 0xb, 0x1c, 0x15, 0x58, 0x28, + 0xa8, 0x92, 0x4a, 0x24, 0x98, 0x51, 0x10, 0x82, + 0x1, 0x4, 0x2, 0x8, 0x0, + + /* U+6798 "枘" */ + 0x20, 0x20, 0x40, 0x40, 0x80, 0x87, 0xdf, 0xf2, + 0x22, 0x24, 0x44, 0x5c, 0x88, 0xb5, 0x31, 0xa2, + 0x53, 0x45, 0x94, 0x8a, 0x9, 0x10, 0x12, 0x20, + 0x24, 0x40, 0x48, 0x87, 0x80, + + /* U+679A "枚" */ + 0x20, 0x80, 0x42, 0x0, 0x84, 0x7, 0xcf, 0xf2, + 0x21, 0x4, 0xc2, 0x1d, 0x44, 0x34, 0x88, 0xa1, + 0x21, 0x41, 0x40, 0x82, 0x81, 0x2, 0x2, 0x1a, + 0x4, 0x63, 0xb, 0x1, 0x0, + + /* U+679C "果" */ + 0x3f, 0xf8, 0x82, 0x10, 0xff, 0xe2, 0x8, 0x44, + 0x10, 0x87, 0xff, 0x0, 0x40, 0x0, 0x80, 0xff, + 0xfe, 0x7, 0x0, 0x35, 0x80, 0x88, 0xce, 0x10, + 0x60, 0x20, 0x0, + + /* U+679D "枝" */ + 0x20, 0x20, 0x40, 0x40, 0x8f, 0xff, 0xc1, 0x2, + 0x2, 0x4, 0x4, 0x1c, 0xff, 0x34, 0x82, 0xa1, + 0x5, 0x41, 0x10, 0x83, 0x41, 0x3, 0x82, 0x7, + 0x4, 0x31, 0x89, 0x80, 0x80, + + /* U+679E "枞" */ + 0x21, 0x10, 0x42, 0x20, 0x84, 0x47, 0xc8, 0x82, + 0x11, 0x4, 0x22, 0x18, 0x44, 0x38, 0xc8, 0xab, + 0x99, 0x44, 0xd0, 0x88, 0xa1, 0x12, 0x62, 0x44, + 0x45, 0x88, 0xca, 0x20, 0x80, + + /* U+67A2 "枢" */ + 0x20, 0x0, 0x47, 0xf8, 0x88, 0x7, 0xd4, 0x42, + 0x28, 0x84, 0x4a, 0x1c, 0x94, 0x35, 0x10, 0xa2, + 0x31, 0x44, 0xb0, 0x8a, 0x21, 0x1c, 0x22, 0x20, + 0x4, 0x7f, 0xc8, 0x0, 0x0, + + /* U+67A3 "枣" */ + 0x2, 0x0, 0x8, 0xf, 0xff, 0xc0, 0x80, 0x7f, + 0xf9, 0xc, 0x24, 0x68, 0x92, 0x92, 0x32, 0x33, + 0x0, 0x30, 0xf8, 0x0, 0x10, 0x3c, 0x0, 0xf, + 0x0, + + /* U+67A5 "枥" */ + 0x20, 0x0, 0x8f, 0xf2, 0x22, 0x3e, 0x88, 0x22, + 0x20, 0x8f, 0xf7, 0x22, 0x5a, 0x89, 0xa2, 0x26, + 0x90, 0x92, 0x46, 0x49, 0x11, 0x24, 0xc4, 0xa2, + 0x12, 0xb1, 0x80, + + /* U+67A7 "枧" */ + 0x20, 0x0, 0x47, 0xf8, 0x88, 0x17, 0xd3, 0x22, + 0x26, 0x44, 0x4c, 0x9c, 0x99, 0x35, 0x32, 0xaa, + 0x65, 0x44, 0xc8, 0x81, 0x81, 0x5, 0x12, 0x12, + 0x24, 0x46, 0x4b, 0xf, 0x0, + + /* U+67A8 "枨" */ + 0x10, 0x80, 0x10, 0x82, 0x10, 0x84, 0x7c, 0x98, + 0x10, 0xa0, 0x30, 0x80, 0x3b, 0xff, 0x54, 0x90, + 0x50, 0x90, 0x90, 0x90, 0x10, 0x88, 0x10, 0x84, + 0x10, 0x86, 0x10, 0xe3, 0x10, 0x80, + + /* U+67AA "枪" */ + 0x20, 0x40, 0x40, 0xc0, 0x82, 0x47, 0xc8, 0xc2, + 0x20, 0xc4, 0x80, 0x5c, 0xfe, 0x35, 0x4, 0xa2, + 0x9, 0x44, 0x10, 0x89, 0xc9, 0x10, 0x12, 0x20, + 0x24, 0x40, 0x88, 0x7f, 0x0, + + /* U+67AB "枫" */ + 0x20, 0x0, 0x4f, 0xf0, 0x90, 0x27, 0xe1, 0x42, + 0x52, 0x84, 0xa5, 0x1d, 0x32, 0x36, 0x64, 0xa4, + 0x49, 0x49, 0x92, 0x93, 0x21, 0x29, 0x52, 0x62, + 0xa5, 0x1, 0x4a, 0x1, 0x0, + + /* U+67AD "枭" */ + 0x2, 0x0, 0x4, 0x0, 0xff, 0xe1, 0x0, 0x42, + 0x61, 0x4, 0x26, 0x8, 0x0, 0x1f, 0xfe, 0x0, + 0x4, 0x4, 0x9, 0xff, 0xd0, 0x34, 0xe0, 0xa4, + 0x6, 0x46, 0x30, 0x83, 0x81, 0x0, + + /* U+67AF "枯" */ + 0x20, 0x20, 0x40, 0x40, 0x80, 0x87, 0xdf, 0xf2, + 0x2, 0x4, 0x4, 0x1c, 0x8, 0x3c, 0x10, 0xab, + 0xfd, 0x44, 0x8, 0x88, 0x11, 0x10, 0x22, 0x20, + 0x44, 0x7f, 0x88, 0x81, 0x0, + + /* U+67B0 "枰" */ + 0x20, 0x0, 0x23, 0xfe, 0x20, 0x20, 0xfa, 0x22, + 0x22, 0x24, 0x21, 0x24, 0x79, 0x20, 0x60, 0x20, + 0xa7, 0xff, 0xa0, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + + /* U+67B3 "枳" */ + 0x20, 0x0, 0x47, 0xf8, 0x88, 0x17, 0xd0, 0x22, + 0x20, 0x44, 0x40, 0x9c, 0x81, 0x3d, 0x2, 0xab, + 0xfd, 0x40, 0x0, 0x80, 0x41, 0x8, 0x42, 0x20, + 0x44, 0x80, 0xca, 0x0, 0x80, + + /* U+67B5 "枵" */ + 0x23, 0xfc, 0x22, 0x4, 0x22, 0x4, 0xfa, 0x4, + 0x23, 0xfc, 0x20, 0x0, 0x60, 0x0, 0x77, 0xff, + 0xa9, 0x0, 0xa1, 0xfc, 0x20, 0x4, 0x20, 0x4, + 0x20, 0x4, 0x20, 0x4, 0x20, 0xf8, + + /* U+67B6 "架" */ + 0x0, 0x0, 0x40, 0xfb, 0xfa, 0x11, 0x14, 0x22, + 0x28, 0x44, 0x50, 0x97, 0x1e, 0x60, 0x0, 0x1, + 0x1, 0xff, 0xfc, 0xe, 0x0, 0x6a, 0x1, 0x13, + 0x1c, 0x21, 0xc0, 0x40, 0x0, + + /* U+67B7 "枷" */ + 0x24, 0x0, 0x90, 0x2, 0x43, 0xfd, 0xe9, 0x24, + 0xa5, 0x92, 0x97, 0x4a, 0x6d, 0x29, 0xa4, 0xa6, + 0x92, 0x92, 0x4a, 0x4a, 0x29, 0x28, 0xbc, 0xa2, + 0x93, 0x30, 0x0, + + /* U+67B8 "枸" */ + 0x21, 0x0, 0x84, 0x2, 0x3f, 0xfe, 0x81, 0x24, + 0x4, 0x8f, 0x97, 0x22, 0x6a, 0x89, 0xa2, 0x26, + 0x88, 0x92, 0x3e, 0x48, 0x1, 0x20, 0x4, 0x80, + 0x12, 0x7, 0x80, + + /* U+67C1 "柁" */ + 0x20, 0x20, 0x40, 0x40, 0x8f, 0xff, 0xd0, 0x12, + 0x20, 0x24, 0x0, 0x8, 0x41, 0x38, 0x84, 0x69, + 0x31, 0x43, 0x82, 0x84, 0x1, 0x8, 0x12, 0x10, + 0x24, 0x20, 0xc8, 0x3f, 0x0, + + /* U+67C3 "柃" */ + 0x20, 0x40, 0x40, 0xc0, 0x82, 0x87, 0xcc, 0x82, + 0x10, 0x84, 0x48, 0x9d, 0x8, 0x34, 0x0, 0xab, + 0xfd, 0x40, 0x10, 0x80, 0x41, 0x9, 0x2, 0xe, + 0x4, 0xc, 0x8, 0x4, 0x0, + + /* U+67C4 "柄" */ + 0x20, 0x0, 0x27, 0xff, 0x20, 0x20, 0xf8, 0x20, + 0x23, 0xfe, 0x22, 0x22, 0x72, 0x22, 0x6a, 0x22, + 0xa2, 0x52, 0xa2, 0x5a, 0x22, 0x8a, 0x23, 0x2, + 0x22, 0x2, 0x22, 0x2, 0x22, 0x1e, + + /* U+67CF "柏" */ + 0x20, 0x40, 0x81, 0x2, 0x3f, 0xfe, 0x81, 0x22, + 0x4, 0x88, 0x17, 0x20, 0x5a, 0xff, 0xa2, 0x6, + 0x88, 0x12, 0x20, 0x48, 0x81, 0x22, 0x4, 0x8f, + 0xf2, 0x20, 0x40, + + /* U+67D0 "某" */ + 0x10, 0x11, 0xff, 0xfc, 0x40, 0x40, 0x80, 0x81, + 0xff, 0x2, 0x2, 0x4, 0x4, 0xf, 0xf8, 0x1, + 0x1, 0xff, 0xfc, 0xe, 0x0, 0x2a, 0x1, 0x93, + 0x1c, 0x21, 0xe0, 0x40, 0x0, + + /* U+67D1 "柑" */ + 0x21, 0x4, 0x42, 0x8, 0x84, 0x17, 0xdf, 0xf2, + 0x10, 0x44, 0x20, 0x9c, 0x41, 0x34, 0x82, 0xa9, + 0xfd, 0x42, 0x8, 0x84, 0x11, 0x8, 0x22, 0x10, + 0x44, 0x3f, 0x88, 0x41, 0x0, + + /* U+67D2 "柒" */ + 0x0, 0x80, 0x61, 0x0, 0x2, 0xe, 0x3f, 0xe2, + 0x8, 0x0, 0x10, 0x46, 0x31, 0x10, 0x3e, 0x40, + 0x0, 0x2, 0x3, 0xff, 0xf8, 0x3c, 0x0, 0x96, + 0x2, 0x22, 0x38, 0x43, 0x80, 0x80, + + /* U+67D3 "染" */ + 0x41, 0x0, 0x62, 0x0, 0x1f, 0xc2, 0x8, 0x80, + 0x11, 0x22, 0x42, 0x59, 0x87, 0xe6, 0x0, 0x1, + 0x1, 0xff, 0xfc, 0x16, 0x0, 0x4a, 0x3, 0x13, + 0x18, 0x20, 0xc0, 0x40, 0x0, + + /* U+67D4 "柔" */ + 0x7f, 0xf8, 0x0, 0x60, 0x3b, 0x0, 0xc, 0xf, + 0xff, 0xc0, 0xd0, 0x86, 0x22, 0x73, 0x80, 0x0, + 0x1, 0xff, 0xfc, 0x1a, 0x0, 0x52, 0x3, 0x23, + 0x18, 0x41, 0xc0, 0x80, 0x0, + + /* U+67D8 "柘" */ + 0x23, 0xfe, 0x40, 0x83, 0xe1, 0x1, 0x4, 0x2, + 0x8, 0xe, 0x20, 0x1a, 0x7f, 0x51, 0x82, 0xa5, + 0x4, 0x5a, 0x8, 0x84, 0x11, 0x8, 0x22, 0x1f, + 0xc4, 0x20, 0x80, + + /* U+67D9 "柙" */ + 0x20, 0x0, 0x8f, 0xf2, 0x22, 0x7e, 0x89, 0x22, + 0x24, 0x8f, 0xf7, 0x22, 0x5a, 0x89, 0xa2, 0x26, + 0x8f, 0xf2, 0x22, 0x48, 0x8, 0x20, 0x20, 0x80, + 0x82, 0x2, 0x0, + + /* U+67DA "柚" */ + 0x20, 0x40, 0x81, 0x2, 0x4, 0x3e, 0xff, 0x24, + 0x44, 0x91, 0x17, 0x44, 0x5b, 0x11, 0xab, 0xfe, + 0x91, 0x12, 0x44, 0x49, 0x11, 0x24, 0x44, 0x8f, + 0xf2, 0x40, 0x40, + + /* U+67DC "柜" */ + 0x20, 0x0, 0x47, 0xf8, 0x88, 0x1, 0x10, 0xf, + 0xa0, 0x4, 0x7f, 0x9c, 0x81, 0x35, 0x2, 0xa2, + 0x5, 0x47, 0xfa, 0x88, 0x1, 0x10, 0x2, 0x20, + 0x4, 0x7f, 0xc8, 0x0, 0x0, + + /* U+67DD "柝" */ + 0x20, 0x0, 0x47, 0xf8, 0x88, 0x7, 0xd0, 0x2, + 0x20, 0x4, 0x7f, 0xdc, 0x84, 0x35, 0x8, 0xa2, + 0x91, 0x44, 0xe0, 0x88, 0x61, 0x20, 0xa2, 0x41, + 0x4, 0x82, 0xa, 0x4, 0x0, + + /* U+67DE "柞" */ + 0x21, 0x0, 0x42, 0x0, 0x8f, 0xff, 0xd4, 0x2, + 0x48, 0x4, 0x90, 0x1c, 0x3f, 0x54, 0x40, 0xa0, + 0x81, 0x41, 0x0, 0x83, 0xf1, 0x4, 0x2, 0x8, + 0x4, 0x10, 0x8, 0x20, 0x0, + + /* U+67E0 "柠" */ + 0x20, 0x20, 0x40, 0x40, 0x8f, 0xff, 0xd0, 0x12, + 0x20, 0x24, 0x0, 0x1c, 0xff, 0xb4, 0x10, 0xa0, + 0x21, 0x40, 0x40, 0x80, 0x81, 0x1, 0x2, 0x2, + 0x4, 0x4, 0x8, 0x70, 0x0, + + /* U+67E2 "柢" */ + 0x20, 0x0, 0x23, 0xfe, 0x22, 0x20, 0xfa, 0x20, + 0x22, 0x20, 0x22, 0x20, 0x73, 0xff, 0x6a, 0x20, + 0xa2, 0x20, 0xa2, 0x10, 0x22, 0x10, 0x22, 0x11, + 0x22, 0x91, 0x23, 0x4b, 0x20, 0x26, + + /* U+67E5 "查" */ + 0x1, 0x0, 0x2, 0x3, 0xff, 0xf8, 0x1c, 0x0, + 0x54, 0x3, 0x26, 0x18, 0x3, 0xdf, 0xfc, 0x20, + 0x8, 0x7f, 0xf0, 0x80, 0x21, 0x0, 0x43, 0xff, + 0x80, 0x0, 0x3f, 0xff, 0x80, + + /* U+67E9 "柩" */ + 0x20, 0x0, 0x47, 0xfc, 0x88, 0x7, 0xd1, 0x2, + 0x23, 0xc4, 0x48, 0x9c, 0xa2, 0x35, 0x44, 0xa2, + 0x19, 0x44, 0x48, 0x89, 0x19, 0x14, 0x12, 0x20, + 0x4, 0x7f, 0xc8, 0x0, 0x0, + + /* U+67EC "柬" */ + 0x1, 0x1, 0xff, 0xfc, 0x4, 0x0, 0x8, 0x7, + 0xff, 0xc8, 0x20, 0x92, 0x49, 0x24, 0xb2, 0x41, + 0x4, 0xff, 0xf8, 0xe, 0x0, 0x2a, 0x1, 0x93, + 0x1c, 0x21, 0xc0, 0x40, 0x0, + + /* U+67EF "柯" */ + 0x20, 0x0, 0x4f, 0xfc, 0x80, 0x17, 0xc0, 0x22, + 0x3e, 0x44, 0x44, 0x9c, 0x89, 0x35, 0x12, 0xa2, + 0x25, 0x44, 0x48, 0x8f, 0x91, 0x0, 0x22, 0x0, + 0x44, 0x0, 0x88, 0x1e, 0x0, + + /* U+67F0 "柰" */ + 0x2, 0x0, 0x8, 0xf, 0xff, 0xc1, 0xa0, 0x1a, + 0x61, 0x88, 0x78, 0x0, 0x1f, 0xfc, 0x0, 0x3, + 0xff, 0xf0, 0x10, 0x4, 0x48, 0x21, 0x13, 0xc, + 0x30, 0xe0, 0x0, + + /* U+67F1 "柱" */ + 0x20, 0x40, 0x40, 0x80, 0x80, 0x7, 0xdf, 0xe2, + 0x4, 0x4, 0x8, 0x1c, 0x10, 0x35, 0xfe, 0xa0, + 0x41, 0x40, 0x80, 0x81, 0x1, 0x2, 0x2, 0x4, + 0x5, 0xff, 0xc8, 0x0, 0x0, + + /* U+67F3 "柳" */ + 0x40, 0x81, 0x1c, 0xf4, 0x42, 0x7d, 0x29, 0x44, + 0xa5, 0x12, 0x9e, 0x4a, 0x75, 0x29, 0xc4, 0xa7, + 0x1e, 0x94, 0x4a, 0x50, 0x4f, 0x41, 0x21, 0x8, + 0x84, 0xc2, 0x0, + + /* U+67F4 "柴" */ + 0x8, 0x40, 0x10, 0x8d, 0x3d, 0xe2, 0x42, 0x4, + 0x84, 0x29, 0x28, 0x7f, 0x9f, 0x40, 0x0, 0x1, + 0x1, 0xff, 0xfc, 0xd, 0x0, 0x29, 0x1, 0x91, + 0x9c, 0x20, 0xc0, 0x40, 0x0, + + /* U+67FD "柽" */ + 0x20, 0x0, 0x4f, 0xf8, 0x88, 0x27, 0xc8, 0x82, + 0xa, 0x4, 0x1c, 0x1d, 0xc7, 0xb4, 0x20, 0xa0, + 0x41, 0x4f, 0xf8, 0x81, 0x1, 0x2, 0x2, 0x4, + 0x4, 0xff, 0xc8, 0x0, 0x0, + + /* U+67FF "柿" */ + 0x20, 0x20, 0x40, 0x40, 0x8f, 0xff, 0xc1, 0x2, + 0x2, 0x4, 0x7f, 0xdc, 0x88, 0xb7, 0x11, 0xa2, + 0x23, 0x44, 0x44, 0x88, 0x89, 0x11, 0x12, 0x22, + 0xc4, 0x4, 0x8, 0x8, 0x0, + + /* U+6800 "栀" */ + 0x20, 0x0, 0x47, 0xf8, 0x88, 0x7, 0xd0, 0x2, + 0x3f, 0xe4, 0x40, 0x1c, 0xbf, 0x35, 0x42, 0xa2, + 0x85, 0x45, 0x8, 0x8a, 0x71, 0x24, 0x12, 0x48, + 0x25, 0x90, 0x4a, 0x1f, 0x0, + + /* U+6805 "栅" */ + 0x20, 0x0, 0x4f, 0x78, 0x92, 0x97, 0xa5, 0x22, + 0x4a, 0x4c, 0x94, 0xaf, 0xff, 0xda, 0x52, 0xa4, + 0xa5, 0x49, 0x48, 0x92, 0x91, 0x25, 0x22, 0x8a, + 0x45, 0x18, 0x8c, 0xf7, 0x0, + + /* U+6807 "标" */ + 0x20, 0x0, 0x23, 0xfe, 0x20, 0x0, 0xf8, 0x0, + 0x20, 0x0, 0x27, 0xfe, 0x70, 0x20, 0x68, 0x20, + 0xa1, 0x20, 0xa2, 0x24, 0x22, 0x24, 0x24, 0x22, + 0x24, 0x22, 0x20, 0x20, 0x21, 0xe0, + + /* U+6808 "栈" */ + 0x20, 0x80, 0x41, 0x20, 0x82, 0x7, 0xc5, 0xe2, + 0x3c, 0x4, 0x10, 0x1c, 0x20, 0x34, 0xfe, 0xa2, + 0x41, 0x40, 0x90, 0x81, 0x41, 0x1, 0x12, 0x1e, + 0x44, 0xc6, 0x88, 0x7, 0x0, + + /* U+6809 "栉" */ + 0x21, 0x8, 0x42, 0x10, 0x9f, 0xff, 0xc8, 0x42, + 0x10, 0x84, 0x0, 0x1d, 0xff, 0xb4, 0x81, 0xa1, + 0x3, 0x42, 0x4, 0x84, 0x11, 0x8, 0x22, 0x13, + 0xc4, 0x20, 0x8, 0x40, 0x0, + + /* U+680A "栊" */ + 0x21, 0x20, 0x42, 0x30, 0x84, 0x7, 0xdf, 0xf2, + 0x12, 0x4, 0x24, 0x1c, 0x49, 0x34, 0x92, 0xa1, + 0x29, 0x44, 0x60, 0x88, 0x81, 0x13, 0x12, 0x4a, + 0x24, 0xa4, 0x4a, 0xf, 0x0, + + /* U+680B "栋" */ + 0x20, 0x40, 0x20, 0x80, 0x23, 0xfe, 0xf9, 0x0, + 0x21, 0x20, 0x21, 0x20, 0x72, 0x20, 0x6b, 0xfe, + 0xa8, 0x20, 0xa1, 0x20, 0x21, 0x24, 0x22, 0x24, + 0x24, 0x22, 0x20, 0x20, 0x21, 0xe0, + + /* U+680C "栌" */ + 0x20, 0x40, 0x40, 0xfc, 0x81, 0x7, 0xc2, 0x2, + 0x3f, 0xc4, 0x40, 0x9e, 0x81, 0x31, 0x2, 0xa3, + 0xfd, 0x44, 0x8, 0x90, 0x1, 0x20, 0x2, 0x40, + 0x4, 0x80, 0xa, 0x0, 0x0, + + /* U+680E "栎" */ + 0x20, 0x0, 0x47, 0xf0, 0x88, 0x7, 0xd1, 0x2, + 0x22, 0x4, 0x44, 0x1c, 0x88, 0x35, 0xfe, 0xa8, + 0x21, 0x40, 0x40, 0x88, 0xa1, 0x11, 0x22, 0x42, + 0x44, 0x4, 0x8, 0x78, 0x0, + + /* U+680F "栏" */ + 0x21, 0x8, 0x43, 0x10, 0x82, 0x47, 0xdf, 0xf2, + 0x0, 0x4, 0x0, 0x1c, 0x0, 0x34, 0x0, 0xa3, + 0xfd, 0x40, 0x0, 0x80, 0x1, 0x0, 0x2, 0x0, + 0x4, 0xff, 0xc8, 0x0, 0x0, + + /* U+6811 "树" */ + 0x20, 0x2, 0x27, 0xc2, 0x20, 0x42, 0x78, 0x5f, + 0x20, 0x42, 0x22, 0x42, 0x62, 0x92, 0x79, 0x92, + 0x60, 0x8a, 0xa1, 0x82, 0x23, 0x42, 0x22, 0x22, + 0x24, 0x22, 0x28, 0x2, 0x20, 0x1c, + + /* U+6813 "栓" */ + 0x20, 0x40, 0x41, 0x40, 0x82, 0xc7, 0xc8, 0xc2, + 0x20, 0xc4, 0x80, 0x18, 0xfe, 0x38, 0x20, 0xa8, + 0x41, 0x4f, 0xf8, 0x81, 0x1, 0x2, 0x2, 0x4, + 0x5, 0xff, 0xc8, 0x0, 0x0, + + /* U+6816 "栖" */ + 0x20, 0x0, 0x4f, 0xfc, 0x82, 0x47, 0xc4, 0x82, + 0x3f, 0xc4, 0x92, 0x9d, 0x25, 0x36, 0x4a, 0xa5, + 0x15, 0x4a, 0x38, 0x90, 0x11, 0x20, 0x22, 0x40, + 0x44, 0x7f, 0x89, 0x1, 0x0, + + /* U+6817 "栗" */ + 0xff, 0xfe, 0x8, 0x81, 0xff, 0xf2, 0x22, 0x24, + 0x44, 0x48, 0x88, 0x9f, 0xff, 0x0, 0x80, 0xff, + 0xfe, 0x7, 0x0, 0x35, 0x0, 0x89, 0x8e, 0x10, + 0xe0, 0x20, 0x0, + + /* U+681D "栝" */ + 0x20, 0x0, 0x47, 0xf8, 0x80, 0x87, 0xc1, 0x2, + 0x2, 0x4, 0xff, 0xdc, 0x8, 0x34, 0x10, 0xa3, + 0xfd, 0x44, 0x8, 0x88, 0x11, 0x10, 0x22, 0x20, + 0x44, 0x7f, 0x88, 0x81, 0x0, + + /* U+6821 "校" */ + 0x20, 0x40, 0x40, 0x40, 0x8f, 0xff, 0xc0, 0x2, + 0x9, 0x4, 0x21, 0x1c, 0xc1, 0x37, 0x85, 0xa0, + 0x89, 0x41, 0x20, 0x81, 0x81, 0x1, 0x2, 0xd, + 0x4, 0x61, 0x89, 0x0, 0x80, + + /* U+6829 "栩" */ + 0x20, 0x0, 0x9e, 0xf2, 0x8, 0x7f, 0x29, 0x24, + 0xa4, 0x92, 0x57, 0x29, 0x5a, 0x21, 0xa1, 0x8e, + 0x9a, 0xd2, 0x48, 0x48, 0x21, 0x20, 0x84, 0x82, + 0x12, 0x7b, 0xc0, + + /* U+682A "株" */ + 0x20, 0x40, 0x40, 0x80, 0x89, 0x7, 0xdf, 0xe2, + 0x24, 0x4, 0x88, 0x1c, 0x10, 0x37, 0xff, 0xa0, + 0x61, 0x41, 0xc0, 0x85, 0x41, 0x12, 0x42, 0x44, + 0x64, 0x8, 0x8, 0x10, 0x0, + + /* U+6832 "栲" */ + 0x20, 0x40, 0x20, 0x42, 0x23, 0xf4, 0xf8, 0x48, + 0x27, 0xff, 0x30, 0x20, 0x68, 0xc0, 0x61, 0xfe, + 0xa7, 0x0, 0xa9, 0x0, 0x21, 0xfc, 0x20, 0x4, + 0x20, 0x4, 0x20, 0x4, 0x20, 0x78, + + /* U+6833 "栳" */ + 0x20, 0x40, 0x40, 0x80, 0x8f, 0xcf, 0xc2, 0x22, + 0x4, 0x84, 0xb, 0x1d, 0xff, 0xb4, 0x30, 0xa8, + 0x81, 0x46, 0x18, 0x94, 0xc1, 0xe, 0x12, 0x10, + 0x44, 0x20, 0x88, 0x7f, 0x0, + + /* U+6837 "样" */ + 0x20, 0x8, 0x42, 0x20, 0x80, 0x87, 0xdf, 0xe2, + 0x4, 0x4, 0x8, 0x1c, 0xff, 0x34, 0x20, 0xa0, + 0x41, 0x40, 0x80, 0x9f, 0xf9, 0x2, 0x2, 0x4, + 0x4, 0x8, 0x8, 0x10, 0x0, + + /* U+6838 "核" */ + 0x20, 0x40, 0x40, 0x40, 0x9f, 0xff, 0xc4, 0x2, + 0x10, 0x4, 0x42, 0x1c, 0xfc, 0x34, 0x12, 0xa0, + 0x45, 0x43, 0x10, 0x8c, 0x41, 0x21, 0x82, 0x4, + 0x84, 0x30, 0x89, 0x80, 0x0, + + /* U+6839 "根" */ + 0x23, 0xfc, 0x44, 0x8, 0x88, 0x17, 0xd0, 0x22, + 0x3f, 0xc4, 0x40, 0x9c, 0x81, 0x35, 0xfe, 0xa2, + 0x81, 0x44, 0x8c, 0x89, 0x61, 0x11, 0x2, 0x25, + 0x4, 0x71, 0x88, 0x80, 0x80, + + /* U+683C "格" */ + 0x20, 0x80, 0x42, 0x0, 0x8b, 0xe7, 0xd8, 0x42, + 0x49, 0x6, 0xc, 0x1a, 0x38, 0x31, 0x8e, 0xa4, + 0x1, 0x47, 0xf8, 0x88, 0x11, 0x10, 0x22, 0x20, + 0x44, 0x7f, 0x88, 0x81, 0x0, + + /* U+683D "栽" */ + 0x8, 0x40, 0x10, 0xb1, 0xfd, 0x30, 0x42, 0x20, + 0x84, 0x1f, 0xff, 0xc0, 0x10, 0x4, 0x22, 0xff, + 0x48, 0x30, 0x50, 0x70, 0xc1, 0x51, 0x14, 0x85, + 0x31, 0x12, 0x42, 0x43, 0x0, + + /* U+683E "栾" */ + 0x0, 0x0, 0x4, 0xf, 0xff, 0xc1, 0x20, 0x4, + 0x80, 0x92, 0x64, 0x48, 0xc0, 0x80, 0x2, 0x3, + 0xff, 0xf0, 0x30, 0x3, 0xb0, 0x32, 0x33, 0x88, + 0x30, 0x20, 0x0, + + /* U+6840 "桀" */ + 0x10, 0x10, 0x20, 0x20, 0xfb, 0xfb, 0x14, 0x81, + 0x49, 0x0, 0x9f, 0xc2, 0x4, 0x18, 0x8, 0x41, + 0x11, 0xff, 0xfc, 0xe, 0x0, 0x2a, 0x1, 0x93, + 0x1c, 0x21, 0xc0, 0x40, 0x0, + + /* U+6841 "桁" */ + 0x20, 0x80, 0x41, 0x7c, 0x84, 0x7, 0xd0, 0x2, + 0x8, 0x4, 0x10, 0x1c, 0x5f, 0xb5, 0x84, 0xa3, + 0x9, 0x4a, 0x10, 0x84, 0x21, 0x8, 0x42, 0x10, + 0x84, 0x21, 0x8, 0x4e, 0x0, + + /* U+6842 "桂" */ + 0x20, 0x40, 0x40, 0x80, 0x8f, 0xf7, 0xc2, 0x2, + 0x4, 0x4, 0x8, 0x1d, 0xff, 0x34, 0x20, 0xa0, + 0x41, 0x47, 0xf8, 0x81, 0x1, 0x2, 0x2, 0x4, + 0x4, 0xff, 0xc8, 0x0, 0x0, + + /* U+6843 "桃" */ + 0x41, 0x20, 0x82, 0x41, 0x4, 0x97, 0xc9, 0x24, + 0x52, 0x88, 0xa5, 0x18, 0x48, 0x68, 0x90, 0xc3, + 0x39, 0x9a, 0x4d, 0x4, 0x82, 0x11, 0x4, 0x22, + 0x28, 0x84, 0x52, 0xf, 0x0, + + /* U+6844 "桄" */ + 0x20, 0x40, 0x48, 0x88, 0x89, 0x17, 0xd2, 0x42, + 0x5, 0x4, 0x8, 0x1d, 0xff, 0xb4, 0x90, 0xa9, + 0x21, 0x42, 0x42, 0x84, 0x81, 0x9, 0x12, 0x22, + 0x24, 0x84, 0x4a, 0xf, 0x0, + + /* U+6845 "桅" */ + 0x20, 0x80, 0x43, 0xf0, 0x88, 0x47, 0xd0, 0x82, + 0x7f, 0xcc, 0x40, 0x1e, 0xbf, 0x51, 0x42, 0xa2, + 0x84, 0x45, 0x8, 0x8a, 0xf1, 0x24, 0x2, 0x48, + 0x44, 0x90, 0x8a, 0x3f, 0x0, + + /* U+6846 "框" */ + 0x20, 0x0, 0x47, 0xfc, 0x88, 0x7, 0xd0, 0x2, + 0x2f, 0xe4, 0x42, 0x1c, 0x84, 0x35, 0x3e, 0xa2, + 0x11, 0x44, 0x20, 0x8b, 0xf9, 0x10, 0x2, 0x20, + 0x4, 0x7f, 0x88, 0x0, 0x0, + + /* U+6848 "案" */ + 0x1, 0x0, 0x2, 0x3, 0xff, 0xf4, 0x0, 0x20, + 0x40, 0x1f, 0xff, 0xc2, 0x8, 0xe, 0x20, 0x3, + 0xc1, 0xfc, 0x78, 0x4, 0x7, 0xff, 0xf0, 0x38, + 0x1, 0xac, 0x1c, 0x47, 0xc0, 0x80, + + /* U+6849 "桉" */ + 0x10, 0x20, 0x10, 0x10, 0x11, 0xff, 0x7d, 0x1, + 0x11, 0x21, 0x10, 0x40, 0x3b, 0xff, 0x54, 0x84, + 0x50, 0x84, 0x91, 0x88, 0x10, 0x48, 0x10, 0x30, + 0x10, 0x2c, 0x11, 0x82, 0x12, 0x1, + + /* U+684A "桊" */ + 0x21, 0x8, 0x22, 0x20, 0x4, 0x3, 0xff, 0xe0, + 0x20, 0x0, 0x80, 0x3f, 0xff, 0x84, 0x10, 0x31, + 0x18, 0xff, 0xee, 0xc, 0x0, 0x2e, 0x1, 0x93, + 0x1c, 0x21, 0x80, 0x40, 0x0, + + /* U+684C "桌" */ + 0x2, 0x0, 0x3, 0xf8, 0x8, 0x1, 0xff, 0xc2, + 0x0, 0x87, 0xff, 0x8, 0x2, 0x10, 0x4, 0x3f, + 0xf8, 0x2, 0x3, 0xff, 0xf8, 0x2c, 0x0, 0x96, + 0x4, 0x23, 0x30, 0x41, 0x0, + + /* U+684E "桎" */ + 0x20, 0x0, 0x4f, 0xf8, 0x82, 0x7, 0xc4, 0x82, + 0x11, 0x4, 0x41, 0x19, 0xff, 0x3c, 0x20, 0xa0, + 0x41, 0x4f, 0xf8, 0x81, 0x1, 0x2, 0x2, 0x4, + 0x5, 0xff, 0xc8, 0x0, 0x0, + + /* U+6850 "桐" */ + 0x20, 0x0, 0x47, 0xfc, 0x90, 0xf, 0xef, 0x92, + 0x40, 0x24, 0x80, 0x5d, 0x3c, 0xb6, 0x49, 0xa4, + 0x93, 0x49, 0x24, 0x92, 0x49, 0x27, 0x92, 0x40, + 0x24, 0x80, 0x49, 0x7, 0x0, + + /* U+6851 "桑" */ + 0x1f, 0xf8, 0x10, 0x10, 0xf, 0xe0, 0xe, 0x70, + 0x30, 0xc, 0x7f, 0x7e, 0x2, 0x2, 0x34, 0x6c, + 0x1c, 0x38, 0x62, 0x46, 0x7f, 0xfe, 0x3, 0x40, + 0xd, 0x30, 0x31, 0xe, 0x41, 0x0, + + /* U+6853 "桓" */ + 0x20, 0x0, 0x4f, 0xfc, 0x80, 0x7, 0xc0, 0x2, + 0x3f, 0xc4, 0x40, 0x98, 0x81, 0x3d, 0xfe, 0xa2, + 0x5, 0x44, 0x8, 0x8f, 0xf1, 0x0, 0x2, 0x0, + 0x4, 0xff, 0xc8, 0x0, 0x0, + + /* U+6854 "桔" */ + 0x20, 0x20, 0x40, 0x40, 0x9f, 0xff, 0xc1, 0x2, + 0x2, 0x4, 0x4, 0x1c, 0xff, 0x34, 0x0, 0xa0, + 0x1, 0x47, 0xf8, 0x88, 0x11, 0x10, 0x22, 0x20, + 0x44, 0x7f, 0x88, 0x81, 0x0, + + /* U+6855 "桕" */ + 0x20, 0x0, 0x8e, 0xf2, 0x40, 0x7f, 0x1, 0x24, + 0x4, 0x90, 0x17, 0x40, 0x5a, 0xef, 0xac, 0x6, + 0x90, 0x12, 0x40, 0x49, 0x1, 0x24, 0x4, 0x8f, + 0xf2, 0x40, 0x40, + + /* U+6860 "桠" */ + 0x20, 0x0, 0x4f, 0xfc, 0x82, 0x47, 0xc4, 0x82, + 0x49, 0x24, 0x92, 0x5c, 0xa5, 0x35, 0x4a, 0xa2, + 0x95, 0x41, 0x20, 0x82, 0x41, 0x4, 0x82, 0x9, + 0x4, 0xff, 0xc8, 0x0, 0x0, + + /* U+6861 "桡" */ + 0x21, 0x0, 0x42, 0x8, 0x87, 0xe7, 0xf8, 0x42, + 0x9, 0x4, 0xc, 0x5c, 0x79, 0x35, 0xe, 0xa0, + 0x1, 0x4f, 0xf8, 0x84, 0x81, 0x9, 0x2, 0x22, + 0x24, 0x44, 0x4b, 0xf, 0x0, + + /* U+6862 "桢" */ + 0x0, 0x40, 0x40, 0x80, 0x81, 0x1, 0x3, 0xff, + 0x84, 0x4, 0x7f, 0x88, 0x81, 0x39, 0x22, 0xaa, + 0x45, 0x44, 0x8a, 0x89, 0x11, 0x12, 0x22, 0x6, + 0x4, 0x13, 0x8, 0xc3, 0x16, 0x1, + + /* U+6863 "档" */ + 0x10, 0x20, 0x24, 0x44, 0x44, 0x9b, 0xe9, 0x21, + 0x2, 0x2, 0x4, 0xc, 0xff, 0x9c, 0x1, 0x54, + 0x2, 0xa7, 0xfc, 0x40, 0x8, 0x80, 0x11, 0x0, + 0x22, 0x7f, 0xc4, 0x0, 0x80, + + /* U+6864 "桤" */ + 0x20, 0x40, 0x48, 0x88, 0x91, 0x17, 0xdf, 0xe2, + 0x0, 0x4, 0x0, 0x19, 0xff, 0x3c, 0x2, 0xa0, + 0x5, 0x47, 0xf8, 0x90, 0x1, 0x20, 0x12, 0x40, + 0x24, 0x40, 0x48, 0xff, 0x0, + + /* U+6865 "桥" */ + 0x20, 0x0, 0x47, 0xf8, 0x81, 0x3, 0x82, 0x2, + 0x7f, 0xce, 0x14, 0x1e, 0x28, 0x54, 0x88, 0xa2, + 0x9, 0x48, 0x8, 0x84, 0x41, 0x8, 0x82, 0x11, + 0x4, 0x42, 0x9, 0x4, 0x0, + + /* U+6866 "桦" */ + 0x21, 0x20, 0x42, 0x48, 0x88, 0xb7, 0xf1, 0x82, + 0x62, 0x5, 0x7c, 0x5c, 0x88, 0xb5, 0x1e, 0xaa, + 0x1, 0x40, 0x80, 0xbf, 0xf9, 0x2, 0x2, 0x4, + 0x4, 0x8, 0x8, 0x10, 0x0, + + /* U+6867 "桧" */ + 0x0, 0x40, 0x40, 0xc0, 0x82, 0x81, 0xc, 0x8f, + 0xa0, 0xc4, 0x80, 0xdc, 0xfe, 0x3c, 0x0, 0xa8, + 0x1, 0x40, 0x0, 0x9f, 0xf9, 0x4, 0x2, 0x11, + 0x4, 0x41, 0x9, 0xff, 0x12, 0x2, + + /* U+6868 "桨" */ + 0x8, 0x40, 0x11, 0x1, 0x27, 0xf3, 0x48, 0x42, + 0xa8, 0x83, 0x2, 0x1a, 0x18, 0x44, 0xc0, 0xa, + 0x1, 0xff, 0xfc, 0xe, 0x0, 0x6a, 0x1, 0x13, + 0x1c, 0x21, 0xc0, 0x40, 0x0, + + /* U+6869 "桩" */ + 0x20, 0x20, 0x40, 0x40, 0x8f, 0xff, 0xd0, 0x2, + 0x22, 0x4, 0x44, 0x1c, 0x88, 0x55, 0x7e, 0xa2, + 0x21, 0x44, 0x40, 0x88, 0x81, 0x21, 0x2, 0x42, + 0x4, 0xbf, 0xca, 0x0, 0x0, + + /* U+686B "桫" */ + 0x20, 0x10, 0x46, 0x20, 0x80, 0x57, 0xc2, 0xa2, + 0x45, 0x24, 0x52, 0x5c, 0x24, 0x54, 0x48, 0xa0, + 0x15, 0x44, 0x8, 0x88, 0x21, 0x20, 0x82, 0x42, + 0x4, 0x98, 0x8, 0x40, 0x0, + + /* U+6874 "桴" */ + 0x20, 0x0, 0x23, 0xfe, 0x20, 0x42, 0xfa, 0x24, + 0x21, 0x24, 0x20, 0x0, 0x73, 0xfc, 0x68, 0xc, + 0xa0, 0x10, 0xa0, 0x20, 0xa7, 0xff, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x21, 0xe0, + + /* U+6876 "桶" */ + 0x20, 0x0, 0x9f, 0xf2, 0x0, 0xbe, 0x7c, 0x23, + 0xfc, 0x91, 0x17, 0x44, 0x5a, 0xff, 0xa4, 0x46, + 0x91, 0x12, 0x3f, 0xc9, 0x11, 0x24, 0x44, 0x91, + 0x12, 0x45, 0xc0, + + /* U+6877 "桷" */ + 0x21, 0x0, 0x87, 0xc2, 0x21, 0x3f, 0x8, 0x27, + 0xfc, 0x91, 0x17, 0x44, 0x5a, 0xff, 0xa4, 0x46, + 0x91, 0x12, 0x7f, 0xc9, 0x11, 0x24, 0x44, 0xa1, + 0x12, 0x81, 0xc0, + + /* U+6881 "梁" */ + 0x20, 0x0, 0x27, 0xf0, 0x1, 0x26, 0xa, 0x62, + 0x24, 0xa0, 0x11, 0x4c, 0x42, 0x33, 0x38, 0x1, + 0x1, 0xff, 0xfc, 0xd, 0x0, 0x29, 0x1, 0x91, + 0x9c, 0x20, 0xc0, 0x40, 0x0, + + /* U+6883 "梃" */ + 0x20, 0x0, 0x27, 0xfe, 0x20, 0x90, 0xf9, 0x10, + 0x22, 0x10, 0x24, 0x10, 0x77, 0xfe, 0x68, 0x90, + 0xa4, 0x90, 0xa5, 0x10, 0x23, 0x7e, 0x23, 0x0, + 0x23, 0x80, 0x24, 0x7e, 0x28, 0x0, + + /* U+6885 "梅" */ + 0x21, 0x0, 0x21, 0xfe, 0x22, 0x0, 0xfa, 0x0, + 0x25, 0xfc, 0x31, 0x4, 0x6a, 0x24, 0x62, 0x24, + 0xa7, 0xff, 0xa2, 0x44, 0x22, 0x24, 0x23, 0xfe, + 0x20, 0x4, 0x20, 0x4, 0x20, 0x38, + + /* U+6886 "梆" */ + 0x22, 0x0, 0x44, 0x7c, 0x88, 0x97, 0xb9, 0x22, + 0x22, 0x4c, 0x45, 0x1d, 0xea, 0x59, 0x12, 0xa2, + 0x23, 0x5f, 0x44, 0x88, 0x89, 0x11, 0xe2, 0x42, + 0x4, 0x84, 0xa, 0x8, 0x0, + + /* U+688F "梏" */ + 0x20, 0x20, 0x42, 0x40, 0x87, 0xf7, 0xd1, 0x2, + 0x22, 0x4, 0x4, 0x1d, 0xff, 0xb4, 0x0, 0xa0, + 0x1, 0x47, 0xf8, 0x88, 0x11, 0x10, 0x22, 0x20, + 0x44, 0x7f, 0x88, 0x81, 0x0, + + /* U+6893 "梓" */ + 0x20, 0x40, 0x81, 0x2, 0x3f, 0xfe, 0x0, 0x21, + 0x18, 0x84, 0x47, 0x2, 0x1d, 0xff, 0xa8, 0x42, + 0x81, 0x2, 0x7f, 0xc8, 0x10, 0x20, 0x40, 0x81, + 0x2, 0x4, 0x0, + + /* U+6897 "梗" */ + 0x20, 0x0, 0x4f, 0xfc, 0x81, 0x7, 0xdf, 0xe2, + 0x44, 0x44, 0x88, 0x9c, 0xff, 0x36, 0x22, 0xa4, + 0x45, 0x47, 0xf8, 0x89, 0x1, 0xc, 0x2, 0x18, + 0x4, 0x6e, 0xb, 0x3, 0x0, + + /* U+68A2 "梢" */ + 0x20, 0x20, 0x88, 0x92, 0x12, 0xbe, 0x8, 0x23, + 0xfc, 0x88, 0x17, 0xa0, 0x5a, 0xff, 0xa2, 0x6, + 0x88, 0x12, 0x3f, 0xc8, 0x81, 0x22, 0x4, 0x88, + 0x12, 0x23, 0xc0, + + /* U+68A6 "梦" */ + 0x8, 0x20, 0x8, 0x20, 0x7e, 0xfe, 0x18, 0x68, + 0x2e, 0xe4, 0x49, 0x22, 0x4, 0x0, 0x4, 0x0, + 0xf, 0xf8, 0x18, 0x18, 0x3c, 0x10, 0x6, 0x60, + 0x0, 0x80, 0x7, 0x0, 0x78, 0x0, + + /* U+68A7 "梧" */ + 0x20, 0x0, 0x4f, 0xf8, 0x84, 0x7, 0xff, 0xc2, + 0x10, 0x84, 0x21, 0x1c, 0x42, 0x3b, 0xff, 0xa8, + 0x1, 0x47, 0xf0, 0x90, 0x21, 0x20, 0x42, 0x40, + 0x84, 0x7f, 0x9, 0x2, 0x0, + + /* U+68A8 "梨" */ + 0x3, 0x4, 0x78, 0x48, 0x20, 0x93, 0xfd, 0x20, + 0x82, 0x43, 0xc4, 0x9a, 0x41, 0x64, 0xe, 0x1, + 0x1, 0xff, 0xfc, 0xd, 0x0, 0x29, 0x1, 0x91, + 0x8c, 0x20, 0xe0, 0x40, 0x0, + + /* U+68AD "梭" */ + 0x20, 0x40, 0x41, 0x20, 0x84, 0x27, 0xdf, 0xe2, + 0x0, 0x6, 0x32, 0x1a, 0x83, 0x52, 0x40, 0xa1, + 0xf9, 0x46, 0x10, 0x9a, 0x41, 0x5, 0x2, 0x6, + 0x4, 0x33, 0x9, 0x81, 0x0, + + /* U+68AF "梯" */ + 0x21, 0x10, 0x41, 0x20, 0x8f, 0xf7, 0xc2, 0x22, + 0x4, 0x44, 0x7f, 0x9c, 0x90, 0x36, 0x20, 0xa3, + 0xff, 0x40, 0x84, 0x83, 0x9, 0xa, 0x22, 0x25, + 0xc5, 0x88, 0x8, 0x10, 0x0, + + /* U+68B0 "械" */ + 0x10, 0x10, 0x10, 0x16, 0x10, 0x10, 0x7b, 0xff, + 0x10, 0x10, 0x30, 0x10, 0x32, 0x52, 0x3a, 0x52, + 0x57, 0xf4, 0x52, 0x54, 0x92, 0x48, 0x12, 0x48, + 0x12, 0x59, 0x14, 0x65, 0x1c, 0x46, + + /* U+68B3 "梳" */ + 0x20, 0x40, 0x40, 0xc0, 0x9f, 0xf7, 0xc4, 0x2, + 0x11, 0x4, 0x41, 0x1d, 0xff, 0x34, 0x2, 0xa8, + 0x1, 0x44, 0x90, 0x89, 0x21, 0x12, 0x42, 0x24, + 0xa4, 0x89, 0x4b, 0x13, 0x80, + + /* U+68B5 "梵" */ + 0x8, 0x20, 0x8, 0x20, 0x7e, 0xfe, 0x18, 0x30, + 0x1e, 0x68, 0x28, 0xa6, 0x49, 0x20, 0x0, 0x0, + 0xf, 0xe0, 0x8, 0x20, 0x8, 0x20, 0x13, 0x20, + 0x10, 0xa2, 0x30, 0x22, 0x60, 0x3e, + + /* U+68C0 "检" */ + 0x20, 0x40, 0x41, 0x40, 0x82, 0x47, 0xd8, 0x42, + 0x60, 0x64, 0x7f, 0x1c, 0x0, 0x34, 0x0, 0xa4, + 0x49, 0x44, 0x90, 0x89, 0x21, 0x10, 0x82, 0x1, + 0x4, 0xff, 0xc8, 0x0, 0x0, + + /* U+68C2 "棂" */ + 0x20, 0x0, 0x4f, 0xf8, 0x80, 0x17, 0xdf, 0xe2, + 0x0, 0x46, 0x0, 0x9d, 0xff, 0x34, 0x0, 0xa0, + 0x41, 0x48, 0x88, 0x89, 0x21, 0x5, 0x2, 0x19, + 0x4, 0x61, 0x9, 0x1, 0x80, + + /* U+68C9 "棉" */ + 0x20, 0x80, 0x47, 0xf8, 0x88, 0x17, 0xd0, 0x22, + 0x3f, 0xc4, 0x40, 0x9c, 0xff, 0x34, 0x20, 0xa0, + 0x41, 0x47, 0xfc, 0x91, 0x9, 0x22, 0x12, 0x44, + 0x24, 0x8b, 0x88, 0x10, 0x0, + + /* U+68CB "棋" */ + 0x21, 0x8, 0x42, 0x10, 0x9f, 0xff, 0xc8, 0x42, + 0x10, 0x84, 0x3f, 0x1c, 0x42, 0x3c, 0x84, 0xa9, + 0xf9, 0x42, 0x12, 0x9f, 0xf9, 0x0, 0x2, 0x9, + 0x4, 0x61, 0x89, 0x0, 0x80, + + /* U+68CD "棍" */ + 0x20, 0x0, 0x47, 0xf8, 0x88, 0x17, 0xdf, 0xe2, + 0x20, 0x44, 0x40, 0x9c, 0xff, 0x34, 0x0, 0xa2, + 0x21, 0x47, 0x50, 0x88, 0xc1, 0x11, 0x12, 0x22, + 0x24, 0x74, 0x48, 0x8f, 0x0, + + /* U+68D2 "棒" */ + 0x20, 0x0, 0x40, 0x80, 0x9f, 0xff, 0xc2, 0x2, + 0x3f, 0xc4, 0x10, 0x19, 0xff, 0xbc, 0x88, 0xa2, + 0x49, 0x4b, 0xfc, 0x81, 0x1, 0x3f, 0xf2, 0x4, + 0x4, 0x8, 0x8, 0x10, 0x0, + + /* U+68D5 "棕" */ + 0x20, 0x60, 0x20, 0x20, 0x23, 0xfe, 0xfa, 0x2, + 0x20, 0x0, 0x31, 0xfc, 0x68, 0x0, 0x60, 0x0, + 0xa3, 0xfe, 0xa0, 0x20, 0x21, 0x28, 0x21, 0x24, + 0x22, 0x22, 0x24, 0x22, 0x20, 0xe0, + + /* U+68D8 "棘" */ + 0x10, 0x10, 0x20, 0x23, 0xfb, 0xf8, 0x80, 0x87, + 0xef, 0xf2, 0x52, 0x64, 0xa4, 0xc9, 0x49, 0x9c, + 0x94, 0x60, 0x70, 0xf1, 0x62, 0xb6, 0xa9, 0x19, + 0x22, 0x22, 0x4, 0x4, 0x0, + + /* U+68DA "棚" */ + 0x47, 0xbd, 0x12, 0x94, 0x4a, 0x7d, 0x29, 0x47, + 0xbd, 0x12, 0x9f, 0x4a, 0x71, 0x29, 0xc7, 0xbf, + 0x12, 0x94, 0x4a, 0x52, 0x29, 0x48, 0xc5, 0x23, + 0x15, 0x3d, 0xc0, + + /* U+68E0 "棠" */ + 0x0, 0x0, 0x64, 0x60, 0x8, 0x87, 0xff, 0xe8, + 0x0, 0x53, 0xfe, 0x88, 0x4, 0xf, 0xf8, 0x0, + 0x0, 0x4, 0x3, 0xff, 0xf8, 0x34, 0x0, 0xa6, + 0xe, 0x43, 0xa0, 0x80, 0x80, + + /* U+68E3 "棣" */ + 0x20, 0x40, 0x47, 0xf8, 0x81, 0x17, 0xc2, 0x22, + 0x7f, 0xe4, 0x8, 0x9c, 0x11, 0x35, 0xfe, 0xa0, + 0x41, 0x44, 0x88, 0x81, 0xe1, 0xe, 0x82, 0x64, + 0xc4, 0x8, 0x48, 0xf0, 0x0, + + /* U+68EE "森" */ + 0x1, 0x0, 0x1, 0x0, 0x7f, 0xfe, 0x3, 0x80, + 0x5, 0x60, 0x19, 0x18, 0x20, 0x2, 0x8, 0x20, + 0x7e, 0xfe, 0x18, 0x30, 0x1c, 0x68, 0x2a, 0x64, + 0x49, 0xa2, 0x8, 0x20, 0x8, 0x20, + + /* U+68F0 "棰" */ + 0x20, 0x0, 0x27, 0xfe, 0x20, 0x40, 0xff, 0xfe, + 0x22, 0x44, 0x22, 0x44, 0x72, 0x44, 0x6f, 0xff, + 0xa2, 0x44, 0xa2, 0x44, 0x27, 0xfe, 0x20, 0x40, + 0x20, 0x40, 0x27, 0xfe, 0x20, 0x0, + + /* U+68F1 "棱" */ + 0x20, 0x40, 0x40, 0x80, 0x8f, 0xf7, 0xc2, 0x2, + 0x7f, 0xe6, 0x0, 0x1a, 0x86, 0x32, 0x82, 0xa1, + 0xf9, 0x46, 0x10, 0x94, 0x41, 0x25, 0x2, 0xc, + 0x4, 0x67, 0xb, 0x1, 0x0, + + /* U+68F5 "棵" */ + 0x20, 0x0, 0x47, 0xf8, 0x89, 0x17, 0xdf, 0xe2, + 0x24, 0x46, 0x48, 0x9a, 0xff, 0x34, 0x20, 0xa0, + 0x41, 0x4f, 0xfc, 0x83, 0x81, 0xa, 0x82, 0x24, + 0x85, 0x88, 0xc8, 0x10, 0x0, + + /* U+68F9 "棹" */ + 0x20, 0x40, 0x40, 0xfc, 0x81, 0x1, 0x2, 0xf, + 0xbf, 0xc4, 0x40, 0x9c, 0xff, 0x35, 0x2, 0xa2, + 0x5, 0x47, 0xf8, 0x81, 0x1, 0x3f, 0xf2, 0x4, + 0x4, 0x8, 0x8, 0x10, 0x0, + + /* U+68FA "棺" */ + 0x20, 0x40, 0x40, 0x40, 0x9f, 0xf9, 0x20, 0x1f, + 0xff, 0xe6, 0x40, 0x9a, 0x81, 0x31, 0xfe, 0xa2, + 0x1, 0x44, 0x0, 0x8f, 0xf1, 0x10, 0x22, 0x20, + 0x44, 0x7f, 0x88, 0x81, 0x0, + + /* U+68FC "棼" */ + 0x8, 0x10, 0x10, 0x21, 0xfb, 0xf8, 0xe2, 0xc2, + 0xa9, 0x69, 0x22, 0x0, 0x0, 0x2, 0x30, 0x18, + 0x10, 0x60, 0x1d, 0x7f, 0xe8, 0x20, 0x80, 0x41, + 0x1, 0x2, 0x1c, 0x3c, 0x0, + + /* U+6901 "椁" */ + 0x20, 0x20, 0x4f, 0xfc, 0x80, 0x7, 0xdf, 0xe2, + 0x20, 0x4c, 0x40, 0x9c, 0xff, 0x54, 0x0, 0xa3, + 0xfd, 0x40, 0x30, 0x80, 0x81, 0x3f, 0xf2, 0x2, + 0x4, 0x4, 0x8, 0x78, 0x0, + + /* U+6905 "椅" */ + 0x20, 0x20, 0x40, 0x40, 0x8f, 0xf7, 0xc3, 0x2, + 0x19, 0x84, 0x40, 0x9d, 0xff, 0xb4, 0x2, 0xa3, + 0xe5, 0x44, 0x48, 0x88, 0x91, 0x1f, 0x22, 0x0, + 0x44, 0x0, 0x88, 0xf, 0x0, + + /* U+690B "椋" */ + 0x20, 0x40, 0x40, 0x40, 0x9f, 0xff, 0xc0, 0x2, + 0x3f, 0xc4, 0x40, 0x9c, 0x81, 0x35, 0x2, 0xa3, + 0xfd, 0x40, 0x40, 0x80, 0x81, 0x11, 0x22, 0x62, + 0x64, 0x84, 0x48, 0x38, 0x0, + + /* U+690D "植" */ + 0x20, 0x40, 0x4f, 0xfc, 0x81, 0x7, 0xc2, 0x2, + 0x3f, 0xc4, 0x40, 0x9c, 0xff, 0x35, 0x2, 0xaa, + 0x5, 0x47, 0xf8, 0x88, 0x11, 0x1f, 0xe2, 0x20, + 0x45, 0xff, 0xc8, 0x0, 0x0, + + /* U+690E "椎" */ + 0x21, 0x40, 0x42, 0x40, 0x88, 0x7, 0xdf, 0xe2, + 0x62, 0x4, 0xc4, 0x1c, 0xff, 0x35, 0x10, 0xa2, + 0x21, 0x47, 0xf8, 0x88, 0x81, 0x11, 0x2, 0x22, + 0x4, 0x7f, 0xc8, 0x80, 0x0, + + /* U+6910 "椐" */ + 0x20, 0x0, 0x47, 0xf8, 0x90, 0x17, 0xe0, 0x22, + 0x3f, 0xc4, 0x80, 0x1d, 0x8, 0x39, 0xff, 0xac, + 0x21, 0x48, 0x40, 0x97, 0xf1, 0x28, 0x22, 0x50, + 0x45, 0x3f, 0x8a, 0x41, 0x0, + + /* U+6912 "椒" */ + 0x21, 0x0, 0x21, 0x3e, 0x21, 0xe2, 0xf9, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x77, 0xe2, 0x68, 0x14, + 0xa1, 0x14, 0xa5, 0x5c, 0x25, 0x48, 0x29, 0x18, + 0x21, 0x14, 0x21, 0x22, 0x27, 0x41, + + /* U+691F "椟" */ + 0x10, 0x20, 0x10, 0x20, 0x13, 0xfe, 0x78, 0x20, + 0x13, 0xfe, 0x30, 0x2, 0x39, 0x92, 0x58, 0x50, + 0x53, 0x10, 0x90, 0x10, 0x17, 0xff, 0x10, 0x20, + 0x10, 0x78, 0x11, 0x84, 0x16, 0x2, + + /* U+6920 "椠" */ + 0x10, 0x0, 0x40, 0xfb, 0xfa, 0x2, 0x4, 0x9, + 0x8, 0x1f, 0xdf, 0x84, 0x44, 0x8, 0x88, 0xff, + 0x10, 0x20, 0x20, 0x8, 0x7, 0xff, 0xe0, 0x68, + 0x3, 0x4c, 0x18, 0x87, 0x41, 0x0, + + /* U+6924 "椤" */ + 0x20, 0x0, 0x47, 0xfc, 0x92, 0x4f, 0xe4, 0x92, + 0x3f, 0xe4, 0x0, 0x1c, 0x20, 0x54, 0xfe, 0xa1, + 0x9, 0x47, 0x10, 0x91, 0x41, 0x1, 0x2, 0x4, + 0x4, 0x30, 0x9, 0x80, 0x0, + + /* U+692D "椭" */ + 0x40, 0x10, 0x9e, 0x21, 0x27, 0xff, 0xc9, 0x4, + 0xa4, 0x9, 0x4f, 0x9e, 0xb1, 0x65, 0x3e, 0xc9, + 0x45, 0x92, 0x89, 0x25, 0xf2, 0x72, 0x24, 0x84, + 0x49, 0x8, 0x92, 0x17, 0x0, + + /* U+6930 "椰" */ + 0x20, 0x0, 0x5f, 0xf8, 0x92, 0x97, 0xa5, 0x22, + 0x7a, 0x4c, 0x95, 0x19, 0x2a, 0x5a, 0x52, 0xa7, + 0xa3, 0x49, 0x44, 0x92, 0x89, 0x27, 0x12, 0xfa, + 0xc4, 0x14, 0x8, 0x28, 0x0, + + /* U+6934 "椴" */ + 0x20, 0x0, 0x4f, 0x70, 0x90, 0xa7, 0xa1, 0x42, + 0x72, 0x4c, 0x88, 0xdd, 0x20, 0x5b, 0xbe, 0xa4, + 0x45, 0x48, 0x50, 0x9c, 0xa1, 0x60, 0x82, 0x43, + 0x84, 0x88, 0x89, 0x60, 0x0, + + /* U+6939 "椹" */ + 0x21, 0x8, 0x21, 0x8, 0x27, 0xff, 0xf9, 0x8, + 0x21, 0xf8, 0x31, 0x8, 0x69, 0xf8, 0x61, 0x8, + 0xa1, 0x8, 0xa7, 0xff, 0x22, 0x40, 0x22, 0x88, + 0x22, 0x4, 0x23, 0xfe, 0x20, 0x0, + + /* U+693D "椽" */ + 0x21, 0x0, 0x43, 0xf0, 0x88, 0x27, 0xdf, 0xc2, + 0x0, 0x86, 0xff, 0xda, 0x30, 0x31, 0xc6, 0xa4, + 0xf1, 0x42, 0x50, 0x99, 0xa1, 0x5, 0x22, 0x32, + 0x45, 0x84, 0x48, 0x70, 0x0, + + /* U+693F "椿" */ + 0x20, 0x40, 0x4f, 0xf8, 0x81, 0x7, 0xc2, 0x2, + 0x3f, 0xc4, 0x8, 0x1d, 0xff, 0xb4, 0x88, 0xa1, + 0xf9, 0x4c, 0x1c, 0xa8, 0x29, 0x1f, 0xc2, 0x20, + 0x84, 0x7f, 0x8, 0x82, 0x0, + + /* U+6942 "楂" */ + 0x20, 0x40, 0x20, 0x40, 0x23, 0xfc, 0xf8, 0xf0, + 0x23, 0x48, 0x24, 0x46, 0x73, 0xfc, 0xaa, 0x4, + 0xa2, 0x4, 0xa3, 0xfc, 0x22, 0x4, 0x23, 0xfc, + 0x20, 0x0, 0x20, 0x0, 0x2f, 0xff, + + /* U+6954 "楔" */ + 0x11, 0x0, 0x11, 0x3e, 0x13, 0x92, 0x79, 0x12, + 0x13, 0xd2, 0x31, 0x12, 0x37, 0xe2, 0x59, 0x22, + 0x51, 0xc, 0x90, 0x20, 0x17, 0xff, 0x10, 0x60, + 0x10, 0x50, 0x11, 0x8c, 0x14, 0x3, + + /* U+6957 "楗" */ + 0x20, 0x20, 0x5f, 0xf8, 0x84, 0x97, 0x97, 0xf2, + 0x42, 0x4c, 0x84, 0x9f, 0xfe, 0x51, 0x10, 0xa2, + 0xfd, 0x54, 0x40, 0x9b, 0xf1, 0x31, 0x2, 0x70, + 0x5, 0x1f, 0xc8, 0x0, 0x0, + + /* U+695A "楚" */ + 0x8, 0x10, 0x10, 0x21, 0xfb, 0xf8, 0x41, 0x83, + 0xc5, 0xc9, 0x52, 0x40, 0x0, 0x1f, 0xff, 0x1, + 0x4, 0x22, 0x0, 0x47, 0xf0, 0x88, 0x2, 0x90, + 0x5, 0xa0, 0x10, 0xff, 0x80, 0x0, + + /* U+695D "楝" */ + 0x20, 0x20, 0x27, 0xfe, 0x20, 0x20, 0xf8, 0x20, + 0x23, 0xfe, 0x22, 0x22, 0x72, 0xaa, 0x6a, 0xaa, + 0xa2, 0x22, 0xa3, 0xfe, 0x20, 0x70, 0x20, 0xa8, + 0x21, 0x24, 0x26, 0x23, 0x20, 0x20, + + /* U+695E "楞" */ + 0x23, 0xfe, 0x24, 0x92, 0x24, 0x92, 0xfc, 0x92, + 0x23, 0xfe, 0x20, 0x40, 0x70, 0x60, 0x6f, 0xff, + 0xa1, 0x0, 0xa1, 0xfc, 0x21, 0x4, 0x21, 0x4, + 0x22, 0x4, 0x24, 0x4, 0x2c, 0x78, + + /* U+6960 "楠" */ + 0x20, 0x40, 0x40, 0x80, 0x9f, 0xff, 0xc2, 0x2, + 0x3f, 0xe4, 0x80, 0x5d, 0x24, 0xb6, 0x11, 0xa5, + 0xfb, 0x48, 0x84, 0x91, 0x9, 0x2f, 0xd2, 0x44, + 0x24, 0x88, 0x49, 0x17, 0x80, + + /* U+6963 "楣" */ + 0x20, 0x0, 0x47, 0xfc, 0x88, 0x8b, 0xd1, 0x12, + 0x3f, 0xe4, 0x40, 0x1e, 0xbf, 0xb1, 0x41, 0x62, + 0xff, 0x45, 0x4, 0x8a, 0x9, 0x17, 0xf2, 0x48, + 0x24, 0x9f, 0xca, 0x20, 0x80, + + /* U+6966 "楦" */ + 0x20, 0x40, 0x4f, 0xfc, 0x90, 0xf, 0xc0, 0x2, + 0x3f, 0xc4, 0x0, 0x1c, 0xff, 0x35, 0x2, 0xa2, + 0x5, 0x47, 0xf8, 0x88, 0x11, 0x1f, 0xe2, 0x0, + 0x4, 0xff, 0xc8, 0x0, 0x0, + + /* U+696B "楫" */ + 0x23, 0xfc, 0x44, 0x8, 0x88, 0x17, 0xdf, 0xe2, + 0x0, 0x4, 0xff, 0xdc, 0x81, 0x35, 0xfe, 0xa2, + 0x5, 0x44, 0x8, 0x8f, 0xf1, 0x10, 0x22, 0xff, + 0xe4, 0x0, 0x88, 0x1, 0x0, + + /* U+696E "楮" */ + 0x20, 0x40, 0x40, 0x84, 0x8f, 0xd7, 0xc2, 0x42, + 0x5, 0x6, 0xff, 0xdc, 0x10, 0x34, 0xc0, 0xa3, + 0xfd, 0x4a, 0x8, 0x87, 0xf1, 0x8, 0x22, 0x10, + 0x44, 0x3f, 0x88, 0x41, 0x0, + + /* U+6971 "楱" */ + 0x0, 0x0, 0x40, 0x80, 0x8f, 0xf1, 0x2, 0xf, + 0x84, 0x4, 0x7f, 0x88, 0x10, 0x3b, 0xff, 0x69, + 0x11, 0x45, 0xf2, 0xb1, 0x19, 0x1f, 0xe2, 0x4, + 0x4, 0xc, 0x8, 0x66, 0x13, 0x2, + + /* U+6977 "楷" */ + 0x22, 0x20, 0x44, 0x4c, 0x8e, 0xa7, 0xd1, 0x82, + 0x22, 0x24, 0x74, 0x5d, 0x8f, 0x34, 0x40, 0xa3, + 0xfd, 0x44, 0x8, 0x88, 0x11, 0x1f, 0xe2, 0x20, + 0x44, 0x7f, 0x88, 0x81, 0x0, + + /* U+6978 "楸" */ + 0x40, 0x10, 0x9f, 0x21, 0x8, 0x47, 0x92, 0x94, + 0xfd, 0x48, 0x4a, 0xbd, 0x84, 0x6b, 0x88, 0xca, + 0xb1, 0x95, 0x51, 0x48, 0xa2, 0x11, 0x44, 0x24, + 0x48, 0x48, 0x90, 0xa0, 0x0, + + /* U+6979 "楹" */ + 0x27, 0xf8, 0x44, 0x10, 0x88, 0x27, 0xdf, 0xf2, + 0x21, 0x26, 0x5c, 0x9f, 0x1d, 0x32, 0xc6, 0xa0, + 0x1, 0x4f, 0xf8, 0x92, 0x51, 0x24, 0xa2, 0x49, + 0x45, 0xff, 0xc8, 0x0, 0x0, + + /* U+697C "楼" */ + 0x20, 0x40, 0x44, 0x90, 0x85, 0x47, 0xdf, 0xf2, + 0xe, 0x4, 0x2a, 0x1d, 0x93, 0xb4, 0x40, 0xa0, + 0x81, 0x4f, 0xfc, 0x84, 0x41, 0x10, 0x82, 0x1e, + 0x4, 0x37, 0xb, 0x81, 0x0, + + /* U+6980 "榀" */ + 0x23, 0xf8, 0x44, 0x10, 0x88, 0x27, 0xd0, 0x42, + 0x20, 0x84, 0x7f, 0x1c, 0x0, 0x34, 0x0, 0xa7, + 0xbf, 0x49, 0x44, 0x92, 0x89, 0x25, 0x12, 0x4a, + 0x24, 0xf7, 0xc9, 0x28, 0x80, + + /* U+6982 "概" */ + 0x40, 0x0, 0x9e, 0xf9, 0x24, 0x47, 0xca, 0x84, + 0xf5, 0x9, 0x2a, 0x1e, 0x54, 0x67, 0x9e, 0xc8, + 0x21, 0x94, 0x61, 0x24, 0xc2, 0x5a, 0x84, 0xc5, + 0x29, 0x12, 0x50, 0x47, 0x0, + + /* U+6984 "榄" */ + 0x22, 0x90, 0x45, 0x20, 0x8a, 0xbf, 0xd7, 0x42, + 0x2c, 0xc6, 0x0, 0x1a, 0xff, 0x35, 0x2, 0xa2, + 0xc5, 0x45, 0x88, 0x8b, 0x91, 0x7, 0x2, 0x1e, + 0x24, 0x6c, 0x49, 0x1f, 0x80, + + /* U+6986 "榆" */ + 0x20, 0x40, 0x41, 0x40, 0x82, 0xc7, 0xc8, 0x42, + 0x7f, 0x64, 0x0, 0x1c, 0xe1, 0x36, 0x52, 0xa4, + 0xa5, 0x4f, 0x48, 0x92, 0x91, 0x3d, 0x22, 0x48, + 0x44, 0x90, 0x89, 0x27, 0x0, + + /* U+6987 "榇" */ + 0x20, 0x20, 0x40, 0x40, 0x8f, 0xff, 0xc8, 0x22, + 0x8, 0x84, 0x11, 0x1d, 0xff, 0xb4, 0x10, 0xa0, + 0x21, 0x47, 0xfc, 0x80, 0x81, 0x9, 0x42, 0x22, + 0x44, 0x84, 0x48, 0x78, 0x0, + + /* U+6988 "榈" */ + 0x44, 0x1, 0x9, 0xf4, 0x10, 0x50, 0x7d, 0x65, + 0x15, 0x14, 0x5f, 0x5f, 0x71, 0x1, 0xc5, 0xf7, + 0x14, 0x54, 0x51, 0x51, 0x45, 0x45, 0xf5, 0x10, + 0x14, 0x43, 0xc0, + + /* U+6989 "榉" */ + 0x22, 0x44, 0x22, 0x24, 0x20, 0x8, 0xff, 0xfe, + 0x20, 0x90, 0x71, 0x8, 0x79, 0x48, 0x62, 0x44, + 0xa5, 0xfa, 0xa0, 0x40, 0x20, 0x40, 0x27, 0xfe, + 0x20, 0x40, 0x20, 0x40, 0x20, 0x40, + + /* U+698D "榍" */ + 0x47, 0xfd, 0x10, 0x14, 0x40, 0x7d, 0xff, 0x44, + 0x21, 0x10, 0xa7, 0x4a, 0xb5, 0x7f, 0xc5, 0x7, + 0x27, 0xf4, 0x90, 0x52, 0x7f, 0x49, 0x5, 0x44, + 0x15, 0x13, 0xc0, + + /* U+6994 "榔" */ + 0x42, 0x0, 0x84, 0x79, 0x1e, 0x97, 0xa5, 0x24, + 0x4a, 0x48, 0xf5, 0x19, 0x2a, 0x6a, 0x52, 0xc7, + 0xa3, 0x88, 0x45, 0x14, 0x8a, 0x25, 0x14, 0x5a, + 0xc8, 0xc4, 0x10, 0x8, 0x0, + + /* U+6995 "榕" */ + 0x20, 0x20, 0x20, 0x20, 0x23, 0xde, 0xfa, 0x2, + 0x20, 0x88, 0x23, 0x26, 0x72, 0x50, 0x68, 0xd8, + 0xa1, 0x8c, 0xa6, 0x2, 0xa5, 0xfc, 0x21, 0x4, + 0x21, 0x4, 0x21, 0xfc, 0x21, 0x4, + + /* U+6998 "榘" */ + 0x20, 0x0, 0x41, 0xfd, 0xfc, 0x2, 0x48, 0x9, + 0xf, 0xdf, 0xe0, 0x84, 0x3f, 0x1e, 0x80, 0x62, + 0xfe, 0x80, 0x0, 0x4, 0x7, 0xff, 0xf0, 0x34, + 0x1, 0xa4, 0x1c, 0x47, 0x40, 0x81, + + /* U+699B "榛" */ + 0x40, 0x81, 0x3f, 0xe4, 0x8, 0x3c, 0x20, 0x47, + 0xf9, 0x4, 0xe, 0xff, 0xf4, 0x88, 0xc7, 0xfb, + 0x62, 0x14, 0xff, 0x90, 0x20, 0x43, 0xe1, 0x32, + 0x74, 0x8, 0x0, + + /* U+699C "榜" */ + 0x20, 0x40, 0x20, 0x40, 0x23, 0xbc, 0xf9, 0x10, + 0x20, 0x90, 0x27, 0xfe, 0x74, 0x42, 0x68, 0x40, + 0xa7, 0xff, 0xa1, 0x0, 0xa1, 0xf8, 0x21, 0x8, + 0x22, 0x8, 0x22, 0x8, 0x2c, 0x70, + + /* U+69A7 "榧" */ + 0x10, 0x0, 0x13, 0xfe, 0x12, 0x0, 0x7e, 0x28, + 0x12, 0xee, 0x12, 0x28, 0x3a, 0x28, 0x36, 0xee, + 0x52, 0x28, 0x52, 0x28, 0x12, 0xef, 0x12, 0x28, + 0x12, 0x28, 0x12, 0x0, 0x13, 0xff, + + /* U+69A8 "榨" */ + 0x20, 0x40, 0x40, 0x80, 0x9e, 0xff, 0xe0, 0x12, + 0x11, 0x86, 0x50, 0x9a, 0x7f, 0xb1, 0x40, 0xa2, + 0x81, 0x49, 0xf8, 0x82, 0x1, 0x7, 0xe2, 0x8, + 0x4, 0x10, 0x8, 0x20, 0x0, + + /* U+69AB "榫" */ + 0x21, 0x20, 0x42, 0x20, 0x8f, 0xf7, 0xd1, 0x2, + 0x7f, 0xc4, 0x44, 0x1c, 0xff, 0x35, 0x10, 0xaa, + 0x21, 0x47, 0xfc, 0x80, 0x81, 0x3f, 0xf2, 0x2, + 0x4, 0x4, 0x8, 0x8, 0x0, + + /* U+69AD "榭" */ + 0x20, 0x4, 0x4f, 0x88, 0x91, 0x17, 0xa2, 0x22, + 0x7f, 0xec, 0x88, 0x9d, 0xf9, 0x52, 0x2a, 0xa4, + 0x55, 0x5f, 0x88, 0x87, 0x11, 0xa, 0x22, 0x24, + 0x45, 0x88, 0x88, 0x77, 0x0, + + /* U+69B1 "榱" */ + 0x10, 0x20, 0x20, 0x40, 0x5f, 0xf8, 0x80, 0x7, + 0xbf, 0xc6, 0x40, 0x8d, 0xff, 0xad, 0x2, 0x53, + 0xfc, 0xa0, 0xc0, 0x42, 0x90, 0x98, 0xc1, 0xd0, + 0x82, 0x3c, 0x84, 0x40, 0x80, + + /* U+69B4 "榴" */ + 0x10, 0x0, 0x13, 0xfe, 0x12, 0x12, 0x7a, 0x92, + 0x12, 0x52, 0x33, 0x22, 0x32, 0x2e, 0x58, 0x0, + 0x53, 0xfe, 0x52, 0x22, 0x13, 0xfe, 0x12, 0x22, + 0x12, 0x22, 0x13, 0xfe, 0x12, 0x2, + + /* U+69B7 "榷" */ + 0x20, 0x40, 0x41, 0x0, 0x9f, 0xf7, 0xaa, 0x22, + 0x12, 0xc, 0x7f, 0x9c, 0x88, 0x53, 0x10, 0xab, + 0xfd, 0x44, 0x40, 0x88, 0x81, 0x1f, 0xe2, 0x22, + 0x4, 0x7f, 0xc8, 0x80, 0x0, + + /* U+69BB "榻" */ + 0x23, 0xfc, 0x44, 0x8, 0x88, 0x17, 0xdf, 0xe2, + 0x20, 0x44, 0x7f, 0x9c, 0x0, 0x37, 0xdf, 0xa0, + 0x83, 0x4d, 0x24, 0x82, 0x9, 0x1c, 0x72, 0xcb, + 0x24, 0x10, 0x48, 0xe7, 0x0, + + /* U+69C1 "槁" */ + 0x20, 0x40, 0x4f, 0xfc, 0x80, 0x7, 0xdf, 0xc2, + 0x20, 0x84, 0x41, 0x1c, 0xfe, 0x34, 0x0, 0xa7, + 0xff, 0x48, 0x4, 0x93, 0xc9, 0x24, 0x92, 0x4f, + 0x24, 0x80, 0x49, 0x3, 0x80, + + /* U+69CA "槊" */ + 0x4, 0x0, 0x49, 0xf8, 0xa2, 0x17, 0xf4, 0x21, + 0xf, 0xca, 0x50, 0x94, 0xa1, 0x3f, 0x7e, 0x11, + 0x4, 0x42, 0x79, 0x8, 0x7, 0xff, 0xf0, 0x68, + 0x1, 0x4c, 0x1c, 0x87, 0x41, 0x1, + + /* U+69CC "槌" */ + 0x24, 0x10, 0x22, 0x10, 0x22, 0x6e, 0x78, 0x42, + 0x20, 0x42, 0x26, 0x7e, 0x52, 0x40, 0x6a, 0x40, + 0x6a, 0x7e, 0xa2, 0x42, 0x22, 0x42, 0x22, 0x7e, + 0x26, 0x0, 0x25, 0x80, 0x28, 0xff, + + /* U+69CE "槎" */ + 0x21, 0x4, 0x41, 0x90, 0x8f, 0xff, 0xc2, 0x2, + 0x3f, 0xe4, 0x8, 0x1c, 0x10, 0x37, 0xff, 0xa0, + 0x81, 0x42, 0x2, 0x8f, 0xf9, 0x31, 0x2, 0x82, + 0x4, 0x7f, 0xc8, 0x0, 0x0, + + /* U+69D0 "槐" */ + 0x20, 0x80, 0x47, 0xf8, 0x89, 0x17, 0xd2, 0x22, + 0x3f, 0xcc, 0x48, 0x9c, 0x91, 0x35, 0xfe, 0xa0, + 0x81, 0x41, 0x80, 0x83, 0x41, 0xb, 0x72, 0x25, + 0x64, 0xc8, 0x4b, 0xf, 0x0, + + /* U+69D4 "槔" */ + 0x20, 0x80, 0x23, 0xfc, 0x22, 0x4, 0xfa, 0x4, + 0x23, 0xfc, 0x22, 0x4, 0x73, 0xfc, 0x68, 0x40, + 0xa7, 0xfe, 0xa1, 0x10, 0x22, 0x4c, 0x2d, 0xfa, + 0x20, 0x40, 0x20, 0x40, 0x20, 0x40, + + /* U+69DB "槛" */ + 0x20, 0x88, 0x24, 0x88, 0x24, 0x90, 0x7c, 0x9f, + 0x24, 0xb0, 0x24, 0xa4, 0x78, 0x86, 0x60, 0x0, + 0x63, 0xfe, 0xa4, 0x92, 0x24, 0x92, 0x24, 0x92, + 0x24, 0x92, 0x24, 0x92, 0x27, 0xff, + + /* U+69DF "槟" */ + 0x20, 0x40, 0x27, 0xfe, 0x24, 0x2, 0xf8, 0x8, + 0x23, 0xf0, 0x22, 0x0, 0x72, 0x0, 0x6b, 0xfc, + 0xa2, 0x10, 0xa2, 0x10, 0x2f, 0xff, 0x20, 0x0, + 0x21, 0x10, 0x22, 0xc, 0x24, 0x2, + + /* U+69E0 "槠" */ + 0x40, 0x40, 0x88, 0x85, 0x13, 0xd7, 0x82, 0x44, + 0x5, 0xb, 0xbf, 0xd9, 0x8, 0x6a, 0x7e, 0xc7, + 0x85, 0x89, 0x9, 0x13, 0xf2, 0x24, 0x24, 0x68, + 0x48, 0x9f, 0x90, 0x21, 0x0, + + /* U+69ED "槭" */ + 0x40, 0x20, 0x80, 0x51, 0x0, 0x87, 0xff, 0xe4, + 0x92, 0x9, 0x3c, 0x9a, 0x4a, 0x6c, 0x94, 0xc7, + 0xe9, 0x91, 0x51, 0x2b, 0x42, 0x64, 0x84, 0xc9, + 0x2a, 0x15, 0x94, 0xc3, 0x0, + + /* U+69F2 "槲" */ + 0x22, 0x8, 0x47, 0x50, 0x94, 0xa7, 0xc9, 0x42, + 0xf8, 0x8d, 0x31, 0x1a, 0x6a, 0x5b, 0xd4, 0xa9, + 0x89, 0x53, 0x10, 0x9e, 0xf9, 0x4c, 0x42, 0x98, + 0x85, 0x11, 0xc, 0x62, 0x0, + + /* U+69FD "槽" */ + 0x20, 0x90, 0x4f, 0xfc, 0x82, 0x47, 0xff, 0xf2, + 0x49, 0x24, 0xff, 0xdd, 0x24, 0xd7, 0xff, 0xa0, + 0x1, 0x47, 0xf8, 0x88, 0x11, 0x1f, 0xe2, 0x20, + 0x44, 0x7f, 0x88, 0x81, 0x0, + + /* U+69FF "槿" */ + 0x22, 0x8, 0x5f, 0xfc, 0x88, 0x27, 0xdf, 0xc2, + 0x4, 0x4, 0xff, 0xdd, 0x10, 0xb7, 0xff, 0xa0, + 0x41, 0x4f, 0xfc, 0x81, 0x1, 0x3f, 0xe2, 0x4, + 0x5, 0xff, 0xc8, 0x0, 0x0, + + /* U+6A0A "樊" */ + 0x20, 0x8, 0x45, 0x13, 0xe4, 0xf9, 0x34, 0x43, + 0x1, 0x8d, 0x55, 0xa8, 0x4a, 0xd3, 0x44, 0x20, + 0x8, 0x2, 0x3, 0xff, 0xf8, 0x14, 0x0, 0x6c, + 0x1, 0x6, 0x3c, 0x3, 0x80, + + /* U+6A17 "樗" */ + 0x23, 0xfc, 0x40, 0x80, 0x9f, 0xfb, 0xe2, 0x12, + 0x5d, 0xa4, 0x7b, 0x9e, 0x0, 0x31, 0xfe, 0x60, + 0x0, 0x4f, 0xfc, 0x88, 0x1, 0x1f, 0xe2, 0x0, + 0x44, 0x0, 0x88, 0x1e, 0x0, + + /* U+6A18 "樘" */ + 0x10, 0x20, 0x11, 0x24, 0x10, 0x28, 0x7b, 0xfe, + 0x12, 0x2, 0x33, 0xfc, 0x31, 0x4, 0x5d, 0x4, + 0x51, 0xfc, 0x50, 0x20, 0x10, 0x20, 0x13, 0xfe, + 0x10, 0x20, 0x17, 0xff, 0x10, 0x0, + + /* U+6A1F "樟" */ + 0x20, 0x20, 0x47, 0xfc, 0x84, 0x27, 0xc4, 0x82, + 0x7f, 0xe4, 0x0, 0x1c, 0xff, 0x35, 0x2, 0xa3, + 0xfd, 0x44, 0x8, 0x8f, 0xf1, 0x1, 0x2, 0x7f, + 0xe4, 0x4, 0x8, 0x8, 0x0, + + /* U+6A21 "模" */ + 0x21, 0x10, 0x4f, 0xfc, 0x84, 0x47, 0xc0, 0x2, + 0x3f, 0xc4, 0x40, 0x9c, 0xff, 0x35, 0x2, 0xa3, + 0xfd, 0x40, 0x80, 0x81, 0x1, 0x3f, 0xf2, 0xa, + 0x4, 0x22, 0xb, 0x83, 0x80, + + /* U+6A28 "樨" */ + 0x40, 0x0, 0x8f, 0xf9, 0x10, 0x17, 0xbf, 0xe4, + 0x40, 0x8, 0xb5, 0x99, 0x8, 0x6a, 0xd6, 0xc4, + 0x3, 0x89, 0xf9, 0x14, 0x82, 0x41, 0x4, 0xbf, + 0xe9, 0x4, 0x14, 0x8, 0x0, + + /* U+6A2A "横" */ + 0x21, 0x10, 0x4f, 0xf8, 0x84, 0x47, 0x88, 0x82, + 0xff, 0xec, 0x8, 0x1d, 0xff, 0x52, 0x22, 0xa7, + 0xfd, 0x48, 0x88, 0x91, 0x11, 0x3f, 0xe2, 0x0, + 0x4, 0x63, 0xb, 0x1, 0x80, + + /* U+6A2F "樯" */ + 0x20, 0x20, 0x23, 0xfe, 0x20, 0x20, 0xf9, 0x24, + 0x20, 0x28, 0x27, 0xff, 0x70, 0x0, 0x6b, 0xfe, + 0xa2, 0x2, 0xa2, 0xfa, 0x22, 0x8a, 0x22, 0xfa, + 0x22, 0x2, 0x23, 0xfe, 0x22, 0x2, + + /* U+6A31 "樱" */ + 0x20, 0x0, 0x27, 0xdf, 0x25, 0x55, 0x7d, 0x55, + 0x25, 0x55, 0x25, 0x55, 0x33, 0x8c, 0x6e, 0x5b, + 0x60, 0x40, 0x67, 0xff, 0xa1, 0xc, 0x23, 0x8, + 0x20, 0xf0, 0x20, 0xcc, 0x27, 0x3, + + /* U+6A35 "樵" */ + 0x21, 0x20, 0x42, 0x40, 0x8f, 0xff, 0xd1, 0x2, + 0x62, 0x5, 0x7f, 0x9c, 0x88, 0x35, 0xfe, 0xa2, + 0x21, 0x44, 0x40, 0x8f, 0xf9, 0x10, 0x2, 0x22, + 0x44, 0x92, 0x49, 0x24, 0x80, + + /* U+6A3D "樽" */ + 0x21, 0x8, 0x27, 0xff, 0xf8, 0x90, 0x23, 0xfe, + 0x24, 0x92, 0x25, 0x9e, 0x74, 0x2, 0x6b, 0xfe, + 0xa4, 0x2, 0xa3, 0xfe, 0x20, 0x8, 0x27, 0xff, + 0x22, 0x8, 0x21, 0x8, 0x20, 0x38, + + /* U+6A3E "樾" */ + 0x21, 0x8, 0x21, 0xa, 0x27, 0x88, 0x79, 0x3f, + 0x21, 0x28, 0x27, 0xaa, 0x71, 0x2a, 0x69, 0x2a, + 0x65, 0x3c, 0x65, 0xac, 0xa5, 0xd, 0x27, 0x15, + 0x25, 0x2, 0x24, 0xff, 0x28, 0x0, + + /* U+6A44 "橄" */ + 0x20, 0x10, 0x4f, 0x20, 0x84, 0x47, 0xff, 0x62, + 0x4a, 0x4c, 0x94, 0x9d, 0xf9, 0x5a, 0x4a, 0xa4, + 0x99, 0x4f, 0x30, 0x92, 0x21, 0x24, 0x42, 0x7d, + 0x85, 0x14, 0x88, 0x30, 0x0, + + /* U+6A47 "橇" */ + 0x27, 0xfc, 0x40, 0x80, 0x9f, 0xf7, 0xc2, 0x2, + 0x7f, 0xe4, 0x8, 0x5c, 0x1f, 0xb4, 0xc0, 0xae, + 0x7d, 0x47, 0x20, 0xb8, 0xf1, 0x1c, 0x92, 0xe3, + 0xc4, 0x52, 0x48, 0xc7, 0x80, + + /* U+6A50 "橐" */ + 0x0, 0x0, 0x8, 0xf, 0xff, 0xcf, 0xfc, 0x42, + 0x10, 0xff, 0x8f, 0xff, 0xe0, 0x1, 0xbf, 0xf4, + 0x7f, 0xc7, 0x1, 0x23, 0xf8, 0xff, 0xfc, 0x1a, + 0x3, 0xa7, 0x30, 0x83, + + /* U+6A58 "橘" */ + 0x23, 0xfe, 0x40, 0x18, 0x83, 0xc7, 0xdf, 0xf2, + 0x5, 0x24, 0x32, 0x1e, 0x98, 0x35, 0xff, 0xa4, + 0x13, 0x4b, 0x1c, 0x93, 0xe9, 0x24, 0x52, 0x4f, + 0xa4, 0x80, 0x49, 0x3, 0x80, + + /* U+6A59 "橙" */ + 0x20, 0x48, 0x5f, 0xb0, 0x84, 0x87, 0xa9, 0x62, + 0x21, 0xc, 0xbd, 0x1a, 0x1, 0x5b, 0xfc, 0xa4, + 0x9, 0x48, 0x12, 0x9f, 0xe1, 0x10, 0x82, 0x12, + 0x5, 0xff, 0x88, 0x0, 0x0, + + /* U+6A5B "橛" */ + 0x20, 0x0, 0x4f, 0xfc, 0xa0, 0x7, 0xda, 0x46, + 0xbe, 0xed, 0x12, 0x5e, 0xaa, 0xdd, 0x54, 0xaa, + 0xa9, 0x57, 0xd0, 0xa2, 0x21, 0x44, 0xa2, 0x91, + 0x46, 0x24, 0x4c, 0x90, 0x0, + + /* U+6A61 "橡" */ + 0x20, 0x80, 0x21, 0xf8, 0x22, 0x10, 0x7b, 0xfe, + 0x22, 0x22, 0x22, 0x22, 0x33, 0xfe, 0x68, 0x42, + 0x61, 0xac, 0xe6, 0x78, 0x21, 0xb8, 0x26, 0x54, + 0x21, 0x92, 0x26, 0x10, 0x20, 0xe0, + + /* U+6A65 "橥" */ + 0x48, 0x84, 0xa7, 0xf0, 0x82, 0x46, 0x9b, 0x63, + 0x1f, 0xca, 0xe0, 0xa4, 0x7f, 0x8, 0x82, 0x71, + 0xfc, 0xc0, 0x3, 0xff, 0xf8, 0x1c, 0x0, 0xd6, + 0xe, 0x23, 0xa0, 0x40, 0x80, + + /* U+6A71 "橱" */ + 0x40, 0x0, 0x8f, 0xfd, 0x20, 0x7, 0xde, 0x24, + 0x80, 0x49, 0x7b, 0xfe, 0x91, 0x6d, 0x22, 0xca, + 0x55, 0x97, 0xa9, 0x20, 0x52, 0x52, 0x24, 0x98, + 0x49, 0x7c, 0x94, 0x7, 0x0, + + /* U+6A79 "橹" */ + 0x21, 0x0, 0x47, 0xe0, 0x90, 0x87, 0xfe, 0xe2, + 0x44, 0x4c, 0xff, 0x9d, 0xff, 0x58, 0x0, 0xaf, + 0xff, 0x40, 0x0, 0x9f, 0xf1, 0x20, 0x22, 0x7f, + 0xc4, 0xff, 0x89, 0x1, 0x0, + + /* U+6A7C "橼" */ + 0x22, 0x40, 0x44, 0xf8, 0x91, 0x17, 0xab, 0xe2, + 0x90, 0x4c, 0xdf, 0xdc, 0x84, 0x5a, 0x32, 0xa4, + 0x19, 0x5e, 0x68, 0x82, 0x51, 0x1, 0x62, 0xf4, + 0xa4, 0x12, 0x8, 0xc, 0x0, + + /* U+6A80 "檀" */ + 0x20, 0x40, 0x5f, 0xfc, 0x80, 0x7, 0xff, 0xe2, + 0x9f, 0x4d, 0x42, 0x9a, 0x79, 0x5b, 0xfe, 0xa0, + 0x1, 0x4f, 0xf0, 0x9f, 0xe1, 0x20, 0x42, 0x3f, + 0x5, 0xff, 0xc8, 0x0, 0x0, + + /* U+6A84 "檄" */ + 0x42, 0x10, 0x9f, 0x21, 0x22, 0x87, 0xc5, 0xf4, + 0xfa, 0x49, 0x1c, 0x9f, 0xf9, 0x61, 0x12, 0xcf, + 0xa9, 0x88, 0x31, 0x1e, 0x62, 0x44, 0xc4, 0x89, + 0x89, 0x14, 0x94, 0xd1, 0x0, + + /* U+6A8E "檎" */ + 0x20, 0x40, 0x43, 0x40, 0x89, 0x77, 0xff, 0xd2, + 0x0, 0xc, 0xb4, 0x99, 0x39, 0x5a, 0x8a, 0xa7, + 0xfd, 0x41, 0x0, 0x9f, 0xf1, 0x49, 0x22, 0xbf, + 0x45, 0x0, 0x8a, 0x7, 0x0, + + /* U+6A90 "檐" */ + 0x21, 0x0, 0x8f, 0xe2, 0x41, 0x3d, 0xff, 0x29, + 0x99, 0xa9, 0x16, 0x7f, 0xee, 0x0, 0xab, 0xfe, + 0xa0, 0x2, 0x9f, 0x8a, 0xff, 0x2a, 0x4, 0xcf, + 0xf3, 0x20, 0x40, + + /* U+6A91 "檑" */ + 0x20, 0x0, 0x9f, 0xe2, 0x8, 0x3f, 0xff, 0x28, + 0x85, 0xae, 0xd6, 0x8, 0x2d, 0xee, 0xa7, 0xfa, + 0x92, 0x2a, 0x48, 0x89, 0xfe, 0x24, 0x88, 0x9f, + 0xe2, 0x40, 0x80, + + /* U+6A97 "檗" */ + 0x0, 0x10, 0x3f, 0x8, 0x21, 0x7f, 0x21, 0x2, + 0x3f, 0x24, 0x20, 0xff, 0x3f, 0x10, 0x51, 0x10, + 0x51, 0x7e, 0x1f, 0x0, 0x0, 0x80, 0x7f, 0xff, + 0x3, 0xc0, 0x4, 0xb0, 0x38, 0x8e, 0x40, 0x81, + + /* U+6AA0 "檠" */ + 0x24, 0x21, 0xff, 0x40, 0x91, 0xfa, 0x2, 0x2b, + 0xf8, 0x8f, 0x27, 0x12, 0x46, 0x3c, 0xb2, 0x6, + 0x44, 0x2, 0x3, 0xff, 0xf8, 0x1c, 0x0, 0xd6, + 0xe, 0x23, 0xa0, 0x40, 0x80, + + /* U+6AA9 "檩" */ + 0x20, 0x80, 0xbf, 0xf2, 0x0, 0x3f, 0xff, 0x29, + 0xf5, 0xa8, 0x56, 0x9f, 0x6e, 0x1, 0xa7, 0xfa, + 0x9f, 0xe2, 0x0, 0xb, 0xff, 0x22, 0x40, 0xb1, + 0x72, 0x38, 0x0, + + /* U+6AAB "檫" */ + 0x20, 0x40, 0x23, 0xfe, 0x22, 0x0, 0x7b, 0x20, + 0x21, 0xde, 0x23, 0x52, 0x3e, 0x4c, 0x68, 0xfc, + 0x61, 0x6, 0x62, 0x3, 0x25, 0xfe, 0x20, 0x20, + 0x21, 0x24, 0x26, 0x22, 0x20, 0xe0, + + /* U+6AAC "檬" */ + 0x21, 0x10, 0x5f, 0xfc, 0x84, 0x47, 0xbf, 0xf2, + 0x40, 0x2c, 0x3f, 0x18, 0x0, 0x5b, 0xff, 0xa0, + 0xc1, 0x5e, 0x98, 0x86, 0xe1, 0x73, 0x42, 0x1a, + 0x45, 0xc4, 0x48, 0x38, 0x0, + + /* U+6B20 "欠" */ + 0x8, 0x0, 0x20, 0x0, 0x7f, 0xf1, 0x0, 0x22, + 0x10, 0x48, 0x21, 0x10, 0x40, 0x0, 0x80, 0x2, + 0x80, 0x5, 0x0, 0x11, 0x0, 0x63, 0x1, 0x83, + 0xc, 0x1, 0xa0, 0x0, 0x0, + + /* U+6B21 "次" */ + 0x0, 0x80, 0x82, 0x1, 0x4, 0x1, 0xf, 0xf1, + 0x20, 0x20, 0x48, 0x81, 0x11, 0x8, 0x20, 0x10, + 0xc0, 0x41, 0x40, 0x82, 0x82, 0x8, 0x84, 0x30, + 0x80, 0xc0, 0x86, 0x0, 0x80, + + /* U+6B22 "欢" */ + 0x0, 0x80, 0x7c, 0x80, 0x4, 0xfe, 0x5, 0x2, + 0x45, 0x22, 0x2a, 0x24, 0x28, 0x20, 0x18, 0x20, + 0x8, 0x30, 0x18, 0x50, 0x14, 0x58, 0x26, 0x88, + 0x41, 0x4, 0x42, 0x2, 0x4, 0x0, + + /* U+6B23 "欣" */ + 0x2, 0x40, 0xf8, 0x81, 0x3, 0xfa, 0x4, 0x14, + 0x8, 0x48, 0x4, 0x9f, 0x88, 0x24, 0x10, 0x48, + 0x20, 0x90, 0xa1, 0x21, 0x42, 0x42, 0xc8, 0x88, + 0x91, 0x30, 0xa2, 0xc0, 0x80, + + /* U+6B24 "欤" */ + 0x40, 0x40, 0x81, 0x1, 0xf3, 0xfa, 0x4, 0x14, + 0x12, 0x48, 0x24, 0x9f, 0x48, 0x2, 0x10, 0x4, + 0x20, 0x8, 0xa3, 0xd1, 0x40, 0x22, 0xc0, 0x48, + 0x81, 0xa0, 0x9e, 0x80, 0x80, + + /* U+6B27 "欧" */ + 0x0, 0x41, 0xfd, 0x2, 0x3, 0xf5, 0x24, 0x2a, + 0x52, 0x53, 0x24, 0x22, 0x8, 0x44, 0x10, 0x94, + 0x61, 0x48, 0xa2, 0x89, 0x44, 0x4, 0x4f, 0xe8, + 0x80, 0x20, 0x80, 0x80, 0x0, + + /* U+6B32 "欲" */ + 0x12, 0x20, 0x21, 0x20, 0x20, 0xbe, 0x44, 0x42, + 0xc, 0x52, 0x12, 0x10, 0x21, 0x10, 0x40, 0x10, + 0x3f, 0x30, 0x21, 0x28, 0x21, 0x28, 0x21, 0x28, + 0x21, 0x44, 0x3f, 0x42, 0x20, 0x83, + + /* U+6B37 "欷" */ + 0x43, 0x20, 0x78, 0x40, 0x70, 0xff, 0x1a, 0x11, + 0x5, 0x5f, 0xf2, 0x8, 0x4, 0x28, 0x8, 0x7f, + 0x31, 0xa2, 0x51, 0x44, 0xa2, 0x89, 0x45, 0x74, + 0x42, 0x18, 0x44, 0x20, 0x80, + + /* U+6B39 "欹" */ + 0x10, 0x20, 0xfe, 0x40, 0x41, 0xf0, 0xc2, 0x22, + 0x69, 0x48, 0x12, 0xbf, 0xc4, 0x1, 0x8, 0x72, + 0x11, 0x24, 0x62, 0x48, 0xa3, 0x91, 0x40, 0x24, + 0x80, 0x50, 0x87, 0x40, 0x80, + + /* U+6B3A "欺" */ + 0x42, 0x20, 0x42, 0x20, 0xff, 0x7e, 0x42, 0x42, + 0x7e, 0xd2, 0x42, 0x92, 0x42, 0x10, 0x7e, 0x10, + 0x42, 0x10, 0x42, 0x28, 0xff, 0x28, 0x0, 0x24, + 0x24, 0x44, 0x42, 0x82, 0x81, 0x0, + + /* U+6B3E "款" */ + 0x10, 0x20, 0xff, 0x40, 0x10, 0x7e, 0x10, 0x42, + 0x7e, 0x92, 0x0, 0x90, 0x7e, 0x10, 0x0, 0x10, + 0x0, 0x10, 0xff, 0x28, 0x10, 0x28, 0x52, 0x28, + 0x93, 0x44, 0x10, 0x86, 0x71, 0x3, + + /* U+6B43 "歃" */ + 0x0, 0x21, 0xfe, 0x40, 0x40, 0xf7, 0xfa, 0x21, + 0x5, 0x42, 0x12, 0xbd, 0xc4, 0x48, 0x88, 0x91, + 0x11, 0x22, 0x63, 0xdc, 0xa4, 0x89, 0x49, 0x14, + 0xdf, 0xf8, 0xa0, 0x20, 0x0, + + /* U+6B46 "歆" */ + 0x10, 0x41, 0xfd, 0x0, 0x3, 0xf2, 0x24, 0x22, + 0x8a, 0x5f, 0xc4, 0x0, 0x8, 0x3e, 0x10, 0x84, + 0x31, 0x8, 0xa1, 0xf1, 0x44, 0x22, 0x48, 0x48, + 0x8f, 0x90, 0xa1, 0x40, 0x0, + + /* U+6B47 "歇" */ + 0x0, 0x20, 0x7f, 0x20, 0x41, 0x20, 0x41, 0x3e, + 0x7f, 0x42, 0x41, 0x52, 0x7f, 0x90, 0x20, 0x10, + 0x3f, 0x10, 0xc1, 0x10, 0x49, 0x30, 0x57, 0x28, + 0x41, 0x28, 0x7d, 0x44, 0x1, 0x46, 0xe, 0x83, + + /* U+6B49 "歉" */ + 0x42, 0x20, 0x48, 0x43, 0xfe, 0x81, 0x22, 0xef, + 0xf5, 0x44, 0xaa, 0xbf, 0xe4, 0x12, 0x88, 0x25, + 0x11, 0xfe, 0x50, 0x90, 0xa3, 0x31, 0x4a, 0x54, + 0x54, 0x88, 0x89, 0x20, 0x0, + + /* U+6B4C "歌" */ + 0x0, 0x20, 0xff, 0xa0, 0x1, 0x40, 0x79, 0x7e, + 0x49, 0x42, 0x49, 0x52, 0x79, 0x92, 0x0, 0x10, + 0xff, 0x90, 0x1, 0x10, 0x79, 0x28, 0x49, 0x28, + 0x79, 0x28, 0x41, 0x44, 0x1, 0x42, 0x6, 0x80, + + /* U+6B59 "歙" */ + 0xc, 0x20, 0x12, 0x20, 0x61, 0x3e, 0xbe, 0x42, + 0x0, 0x52, 0x7f, 0x10, 0x7f, 0x10, 0x0, 0x10, + 0xf7, 0x30, 0x11, 0x28, 0x53, 0x28, 0x33, 0x28, + 0xd5, 0x24, 0x13, 0x42, 0x70, 0x43, + + /* U+6B62 "止" */ + 0x0, 0x80, 0x1, 0x0, 0x2, 0x0, 0x84, 0x1, + 0x8, 0x2, 0x1f, 0x84, 0x20, 0x8, 0x40, 0x10, + 0x80, 0x21, 0x0, 0x42, 0x0, 0x84, 0x1, 0x8, + 0x1f, 0xff, 0xc0, + + /* U+6B63 "正" */ + 0x7f, 0xfe, 0x1, 0x0, 0x2, 0x0, 0x4, 0x1, + 0x8, 0x2, 0x10, 0x4, 0x3f, 0x8, 0x40, 0x10, + 0x80, 0x21, 0x0, 0x42, 0x0, 0x84, 0xf, 0xff, + 0xe0, + + /* U+6B64 "此" */ + 0x0, 0x40, 0x10, 0x80, 0x21, 0x0, 0x42, 0x4, + 0x84, 0x69, 0x9, 0x13, 0xd4, 0x24, 0x30, 0x48, + 0x40, 0x90, 0x81, 0x21, 0x2, 0x42, 0x14, 0xb4, + 0x2f, 0x88, 0x60, 0xf, 0x0, + + /* U+6B65 "步" */ + 0x0, 0x80, 0x21, 0x0, 0x43, 0xf0, 0x84, 0x1, + 0x8, 0x2, 0x10, 0x3b, 0xdf, 0x80, 0x40, 0x8, + 0x80, 0x61, 0x19, 0x2, 0x40, 0x7, 0x0, 0x70, + 0x1f, 0x0, 0x0, + + /* U+6B66 "武" */ + 0x0, 0x40, 0x0, 0x81, 0xfd, 0x60, 0x2, 0x20, + 0x4, 0x1f, 0xff, 0xc0, 0x10, 0x2, 0x20, 0x44, + 0x40, 0x8e, 0x81, 0x11, 0x2, 0x21, 0x4, 0x42, + 0x28, 0xf4, 0x7e, 0x4, 0x80, 0x6, + + /* U+6B67 "歧" */ + 0x8, 0x20, 0x10, 0x41, 0x27, 0xfa, 0x41, 0x4, + 0xe2, 0x9, 0x4, 0x12, 0x7f, 0xa4, 0x42, 0x48, + 0x84, 0x90, 0x91, 0x21, 0x22, 0x41, 0x87, 0xe3, + 0x10, 0x19, 0x80, 0xc0, 0x80, + + /* U+6B6A "歪" */ + 0x7f, 0xfe, 0x3, 0x0, 0xd, 0x0, 0x69, 0x87, + 0x10, 0xf0, 0x20, 0x1f, 0xff, 0x0, 0x80, 0x11, + 0x0, 0x21, 0xf8, 0x44, 0x0, 0x88, 0x1, 0x10, + 0x1f, 0xff, 0xc0, + + /* U+6B79 "歹" */ + 0xff, 0xfc, 0x10, 0x0, 0x80, 0x3, 0xfc, 0x10, + 0x10, 0x80, 0x45, 0x82, 0x21, 0x10, 0x0, 0xc0, + 0x6, 0x0, 0x20, 0x3, 0x0, 0x70, 0x0, 0x0, + 0x0, + + /* U+6B7B "死" */ + 0xff, 0xfe, 0x20, 0x80, 0x41, 0x1, 0xfa, 0x32, + 0x14, 0xc8, 0x4b, 0x38, 0x98, 0x4a, 0x20, 0x4, + 0x40, 0x10, 0x84, 0x41, 0x9, 0x2, 0x14, 0x7, + 0xd0, 0x0, 0x0, + + /* U+6B7C "歼" */ + 0xfd, 0xfc, 0x40, 0x41, 0x0, 0x81, 0xe1, 0x4, + 0x82, 0x9, 0x4, 0x22, 0xff, 0xf4, 0x10, 0x88, + 0x20, 0x20, 0x40, 0x40, 0x81, 0x1, 0x4, 0x2, + 0x10, 0x4, 0x0, + + /* U+6B81 "殁" */ + 0xfd, 0xf8, 0x42, 0x11, 0x4, 0x23, 0xc8, 0x44, + 0xa0, 0xe9, 0x0, 0x32, 0xff, 0x59, 0x2, 0x11, + 0x8, 0x23, 0x20, 0x83, 0x82, 0x7, 0xc, 0x31, + 0xd1, 0x80, 0x40, + + /* U+6B82 "殂" */ + 0xfd, 0xfc, 0x42, 0x8, 0x84, 0x13, 0xe8, 0x24, + 0x9f, 0xc9, 0x20, 0xb2, 0x41, 0x54, 0x82, 0x11, + 0xfc, 0x22, 0x8, 0x84, 0x13, 0x8, 0x2c, 0x7f, + 0xe0, 0x0, 0x0, + + /* U+6B83 "殃" */ + 0x0, 0x21, 0xf8, 0x40, 0x80, 0x82, 0x1f, 0xe7, + 0xa2, 0x49, 0x44, 0x92, 0x89, 0x65, 0x12, 0xb7, + 0xfe, 0x20, 0x40, 0x41, 0x41, 0x2, 0x84, 0x8, + 0x98, 0x60, 0xe1, 0x0, 0x0, + + /* U+6B84 "殄" */ + 0x0, 0x41, 0xf8, 0xc0, 0x82, 0x41, 0x8, 0x43, + 0xe2, 0x69, 0x8, 0x12, 0x60, 0x65, 0x8, 0xa8, + 0x20, 0x21, 0x88, 0x4c, 0x21, 0x0, 0x86, 0x6, + 0x18, 0x70, 0x1, 0x0, 0x0, + + /* U+6B86 "殆" */ + 0x0, 0x41, 0xf9, 0x0, 0x82, 0x1, 0x8, 0x47, + 0x90, 0x89, 0x7f, 0x92, 0x0, 0xe4, 0x0, 0xab, + 0xfc, 0x24, 0x8, 0x48, 0x11, 0x10, 0x22, 0x20, + 0x48, 0x7f, 0xa0, 0x81, 0x0, + + /* U+6B87 "殇" */ + 0x0, 0x80, 0x2, 0x3, 0xf7, 0xf9, 0x10, 0x2, + 0x20, 0xf, 0x3f, 0x12, 0xc, 0x24, 0x60, 0xc9, + 0x1, 0x57, 0xf8, 0x44, 0x90, 0x89, 0x22, 0x22, + 0x44, 0x88, 0x90, 0x21, 0x41, 0x9c, + + /* U+6B89 "殉" */ + 0x0, 0x3, 0xf4, 0x2, 0x1f, 0xc8, 0x81, 0x44, + 0x5, 0xe7, 0x94, 0xa2, 0x52, 0x89, 0xa9, 0xe4, + 0x48, 0x91, 0x22, 0x48, 0x79, 0x20, 0x5, 0x0, + 0x18, 0x7, 0x80, + + /* U+6B8A "殊" */ + 0x0, 0x41, 0xf4, 0x80, 0x89, 0x2, 0x1f, 0xe7, + 0xc4, 0x9, 0x88, 0x14, 0x10, 0x4b, 0xfe, 0xd0, + 0x40, 0x21, 0xc0, 0x85, 0x41, 0x12, 0x44, 0x44, + 0x59, 0x8, 0x20, 0x10, 0x0, + + /* U+6B8B "残" */ + 0x0, 0x81, 0xf9, 0x20, 0x82, 0x1, 0xf, 0xe3, + 0x88, 0x9, 0x10, 0x12, 0x23, 0x65, 0xf8, 0xa8, + 0x40, 0x20, 0x90, 0x41, 0x41, 0x3, 0x12, 0x1a, + 0x48, 0xc4, 0xa0, 0x7, 0x0, + + /* U+6B8D "殍" */ + 0x0, 0x7, 0xff, 0xf8, 0x80, 0x1, 0x11, 0x24, + 0x12, 0x8f, 0x80, 0x12, 0x7f, 0x44, 0x6, 0xe8, + 0x10, 0x10, 0x40, 0x5f, 0xf9, 0x81, 0x2, 0x2, + 0x8, 0x4, 0x20, 0x78, 0x0, + + /* U+6B92 "殒" */ + 0x1, 0xfd, 0xfa, 0x8, 0x84, 0x11, 0x8, 0x24, + 0x1f, 0xcf, 0x0, 0x12, 0xff, 0xe5, 0x1, 0xaa, + 0x22, 0x24, 0x44, 0x49, 0x9, 0x13, 0x16, 0xd, + 0x98, 0x60, 0xa1, 0x0, 0x80, + + /* U+6B93 "殓" */ + 0x0, 0x41, 0xf8, 0xc0, 0x82, 0x41, 0x8, 0x43, + 0xa0, 0x49, 0x3f, 0x52, 0x0, 0x74, 0x22, 0xb2, + 0x48, 0x24, 0x90, 0x49, 0x21, 0x8, 0x84, 0x1, + 0x18, 0xff, 0xe0, 0x0, 0x0, + + /* U+6B96 "殖" */ + 0x0, 0x41, 0xff, 0xfc, 0x81, 0x2, 0x2, 0x4, + 0x3f, 0xcf, 0x40, 0x92, 0xff, 0x29, 0x2, 0xb2, + 0x5, 0x27, 0xf8, 0x88, 0x11, 0x1f, 0xe4, 0x20, + 0x58, 0xff, 0xc0, + + /* U+6B9A "殚" */ + 0x0, 0x0, 0x2, 0x33, 0xe0, 0x41, 0x1f, 0xe4, + 0x44, 0x4f, 0x88, 0x92, 0xff, 0x2a, 0x22, 0xd4, + 0x45, 0x67, 0xf8, 0x41, 0x1, 0x2, 0x2, 0x7f, + 0xe8, 0x8, 0x30, 0x10, 0x40, 0x20, + + /* U+6B9B "殛" */ + 0xfb, 0xfc, 0x80, 0x31, 0x1, 0x83, 0xba, 0xe5, + 0x54, 0x52, 0xaa, 0xbd, 0x52, 0xa, 0xa4, 0x27, + 0x54, 0x40, 0xa8, 0x87, 0x2, 0x0, 0x8, 0xff, + 0xf0, 0x0, 0x0, + + /* U+6BA1 "殡" */ + 0x0, 0x1, 0xf0, 0x81, 0xf, 0xfa, 0x10, 0x14, + 0x0, 0xf, 0x3f, 0x12, 0x40, 0x44, 0x80, 0xd1, + 0xff, 0x62, 0x20, 0x44, 0x41, 0x3f, 0xf2, 0x0, + 0x8, 0x32, 0x21, 0x83, 0x80, 0x0, + + /* U+6BAA "殪" */ + 0x0, 0x41, 0xf7, 0xfd, 0x1, 0x2, 0x1f, 0xe4, + 0x0, 0xe, 0xff, 0xd5, 0x0, 0xe8, 0x78, 0xb3, + 0xfc, 0x24, 0x8, 0x48, 0x11, 0x1f, 0xe2, 0x10, + 0x88, 0x12, 0x23, 0xff, 0x80, + + /* U+6BB3 "殳" */ + 0x1f, 0xe0, 0x40, 0x81, 0x2, 0x4, 0x8, 0x10, + 0x21, 0x80, 0x70, 0x0, 0xf, 0xfc, 0x10, 0x10, + 0x20, 0x80, 0xc4, 0x1, 0xa0, 0x3, 0x0, 0x73, + 0x8e, 0x1, 0xc0, + + /* U+6BB4 "殴" */ + 0x1, 0xf1, 0xfa, 0x22, 0x4, 0x44, 0x28, 0x8a, + 0x91, 0x13, 0x41, 0xa6, 0x0, 0x4d, 0xfc, 0x99, + 0x9, 0x4a, 0x22, 0x92, 0x84, 0x2, 0xf, 0xcb, + 0x0, 0x61, 0x80, 0x0, 0x0, + + /* U+6BB5 "段" */ + 0x7d, 0xf0, 0x82, 0x21, 0x4, 0x43, 0xe8, 0x84, + 0x20, 0xe8, 0x0, 0x1f, 0xfe, 0x20, 0x84, 0x41, + 0x10, 0x8d, 0x43, 0xe1, 0x82, 0x7, 0x4, 0x31, + 0x88, 0xc0, 0x80, + + /* U+6BB7 "殷" */ + 0x7c, 0xf8, 0x81, 0x11, 0xf2, 0x22, 0x24, 0x47, + 0xd0, 0xe8, 0x80, 0x1f, 0x7f, 0x20, 0x42, 0x40, + 0x88, 0xf8, 0x91, 0x10, 0xc4, 0x23, 0x89, 0x98, + 0xe0, 0x40, 0x0, + + /* U+6BBF "殿" */ + 0x7f, 0x78, 0x82, 0x91, 0x5, 0x23, 0xfa, 0x45, + 0x28, 0x6a, 0x40, 0x1f, 0xff, 0x29, 0x42, 0x52, + 0x48, 0xfe, 0x91, 0x0, 0xc2, 0x91, 0x8a, 0x16, + 0x90, 0x30, 0xc0, + + /* U+6BC1 "毁" */ + 0x10, 0x0, 0xcc, 0xf1, 0x9, 0x22, 0x12, 0x47, + 0x64, 0x88, 0x50, 0xd0, 0x80, 0x1e, 0x7e, 0x0, + 0x44, 0xfc, 0x90, 0x21, 0x20, 0x41, 0x80, 0x83, + 0x1f, 0xc9, 0x80, 0x60, 0x80, + + /* U+6BC2 "毂" */ + 0x10, 0x79, 0xfe, 0x90, 0x41, 0x23, 0xf2, 0x40, + 0x4, 0x9f, 0xf1, 0xe0, 0x60, 0x7f, 0xfe, 0x40, + 0x48, 0x90, 0x91, 0xfd, 0x40, 0x41, 0x8f, 0xf3, + 0x1, 0x19, 0x82, 0x60, 0x0, + + /* U+6BC5 "毅" */ + 0x10, 0x0, 0x10, 0xf3, 0xfd, 0x22, 0x12, 0x42, + 0x44, 0x9f, 0xf1, 0xc4, 0x0, 0x39, 0x7e, 0x14, + 0x48, 0x58, 0x93, 0x29, 0x20, 0xc9, 0x86, 0x83, + 0x11, 0x19, 0x9e, 0x61, 0x0, + + /* U+6BCB "毋" */ + 0x1f, 0xf8, 0x21, 0x10, 0x82, 0x21, 0x8, 0x42, + 0x10, 0x9f, 0xff, 0xc8, 0x42, 0x10, 0x84, 0x21, + 0x8, 0x82, 0x11, 0xff, 0xf0, 0x30, 0x40, 0xc1, + 0x6, 0x1e, 0x0, + + /* U+6BCD "母" */ + 0x1f, 0xf8, 0x20, 0x10, 0x84, 0x21, 0x4, 0x42, + 0x0, 0x9f, 0xff, 0xc8, 0x2, 0x11, 0x84, 0x20, + 0x88, 0x80, 0x11, 0xff, 0xf0, 0x0, 0x40, 0x1, + 0x0, 0x1e, 0x0, + + /* U+6BCF "每" */ + 0x10, 0x0, 0x7f, 0xfc, 0x80, 0x2, 0x0, 0x9, + 0xff, 0x82, 0x1, 0x8, 0x42, 0x10, 0x4, 0xff, + 0xfe, 0x40, 0x10, 0x84, 0x21, 0x0, 0x43, 0xff, + 0xc0, 0x2, 0x0, 0x3c, 0x0, + + /* U+6BD2 "毒" */ + 0x1, 0x0, 0xff, 0xf8, 0x4, 0x1, 0xff, 0xc0, + 0x10, 0x1f, 0xff, 0xc0, 0x0, 0x1f, 0xfc, 0x21, + 0x9, 0xff, 0xfc, 0x88, 0x21, 0x8, 0x43, 0xff, + 0xc0, 0x1, 0x0, 0x3c, 0x0, + + /* U+6BD3 "毓" */ + 0x20, 0x20, 0x7c, 0x1, 0x7, 0xfc, 0x1, 0xf, + 0xc4, 0x8, 0x91, 0x15, 0x7d, 0x7f, 0x0, 0x44, + 0x0, 0xa9, 0x51, 0x12, 0xa3, 0xf5, 0x50, 0x4a, + 0xa0, 0xa5, 0x4e, 0xcb, 0x0, + + /* U+6BD4 "比" */ + 0x1, 0x2, 0x4, 0x8, 0x10, 0x60, 0x42, 0x81, + 0x13, 0xe4, 0x88, 0x14, 0x20, 0x60, 0x81, 0x2, + 0x4, 0x8, 0x10, 0x60, 0x41, 0x99, 0x7, 0x84, + 0x18, 0xf, 0x80, + + /* U+6BD5 "毕" */ + 0x20, 0x40, 0x40, 0x88, 0xfd, 0x61, 0x3, 0x2, + 0x4, 0x24, 0x8, 0x49, 0xd0, 0x9c, 0x3e, 0x1, + 0x0, 0x2, 0x3, 0xff, 0xf8, 0x8, 0x0, 0x10, + 0x0, 0x20, 0x0, 0x40, 0x0, + + /* U+6BD6 "毖" */ + 0x40, 0x80, 0x81, 0x1, 0x2, 0x33, 0xf5, 0x84, + 0xc, 0x29, 0xd0, 0x5c, 0x3f, 0x20, 0x0, 0x1, + 0x0, 0x11, 0x20, 0xa0, 0x82, 0x46, 0x28, 0xb0, + 0x21, 0x82, 0x1e, 0x4, 0x43, 0xf8, + + /* U+6BD7 "毗" */ + 0x1, 0x11, 0xf2, 0x22, 0xa4, 0x45, 0x48, 0x9a, + 0x91, 0x55, 0x3b, 0x3e, 0x44, 0x54, 0x88, 0xa9, + 0x11, 0x52, 0x22, 0xa4, 0x4f, 0xc8, 0x98, 0x95, + 0x30, 0x32, 0x40, 0x47, 0x0, + + /* U+6BD9 "毙" */ + 0x20, 0x80, 0x20, 0x86, 0x3e, 0x9a, 0x20, 0xe2, + 0x26, 0x82, 0x38, 0x7c, 0x0, 0x0, 0x7f, 0xfe, + 0x10, 0x40, 0x1f, 0x44, 0x21, 0x48, 0x72, 0x70, + 0xa, 0x42, 0x4, 0x42, 0x18, 0x42, 0x60, 0x7e, + 0x0, 0x0, + + /* U+6BDB "毛" */ + 0x0, 0x18, 0xff, 0xc0, 0x8, 0x0, 0x10, 0x0, + 0x20, 0x3, 0xff, 0x18, 0x80, 0x1, 0x0, 0x2, + 0x0, 0x3f, 0xfb, 0x88, 0x0, 0x10, 0x10, 0x20, + 0x40, 0x40, 0x80, 0x7f, 0x0, + + /* U+6BE1 "毡" */ + 0xc, 0x41, 0xe0, 0x80, 0x81, 0xf1, 0x2, 0x2, + 0x44, 0x1f, 0x8, 0x8, 0x7f, 0x10, 0x82, 0x2d, + 0x5, 0xe2, 0x8, 0x87, 0xf1, 0x0, 0x12, 0x0, + 0x44, 0x0, 0x87, 0xff, 0x0, + + /* U+6BEA "毪" */ + 0x8, 0x81, 0xe2, 0x20, 0x88, 0x21, 0x3f, 0xc2, + 0x0, 0x47, 0x8, 0xb8, 0x90, 0x11, 0xfe, 0x24, + 0x41, 0xf0, 0x80, 0x9f, 0xf1, 0x2, 0x2, 0x4, + 0x44, 0x0, 0x8f, 0xff, 0x0, + + /* U+6BEB "毫" */ + 0x1, 0x1, 0xff, 0xfc, 0x0, 0x1, 0xff, 0xc2, + 0x0, 0x87, 0xff, 0x0, 0x0, 0x7f, 0xff, 0xbf, + 0xfa, 0x4, 0x1, 0xff, 0xe0, 0x10, 0x7f, 0xff, + 0x0, 0x20, 0xc0, 0x7f, 0x0, + + /* U+6BEF "毯" */ + 0x0, 0x40, 0xf4, 0x82, 0x89, 0x21, 0x2, 0x2, + 0xa, 0x1f, 0x23, 0x8, 0x80, 0x10, 0x20, 0x22, + 0x49, 0xf0, 0xa0, 0x82, 0x81, 0x8, 0x82, 0x60, + 0xa4, 0x0, 0x47, 0xff, 0x0, + + /* U+6BF3 "毳" */ + 0x7f, 0xf8, 0x1, 0x0, 0x1, 0xf8, 0x3f, 0x0, + 0x1, 0xff, 0x7f, 0x4, 0x1, 0xfc, 0x1e, 0x0, + 0xf0, 0xf8, 0x1e, 0x20, 0xf0, 0xfc, 0x17, 0x20, + 0xf8, 0xfe, 0x16, 0x22, 0x18, 0x3e, + + /* U+6BF5 "毵" */ + 0x18, 0x0, 0x24, 0x7c, 0x42, 0x10, 0xff, 0x10, + 0x10, 0x10, 0xff, 0x7c, 0x24, 0x10, 0x4a, 0x10, + 0x99, 0x10, 0x64, 0xfe, 0x8, 0x10, 0x33, 0x11, + 0x44, 0x12, 0x18, 0x12, 0xe0, 0x1e, + + /* U+6BF9 "毹" */ + 0x8, 0x0, 0x8, 0x6, 0x16, 0x78, 0x61, 0x10, + 0xfe, 0x10, 0x0, 0x10, 0x79, 0x7e, 0x4d, 0x10, + 0x4d, 0x10, 0x7d, 0x10, 0x4d, 0x7e, 0x4d, 0x10, + 0x7d, 0x11, 0x49, 0x11, 0x49, 0x12, 0x7f, 0x1e, + + /* U+6BFD "毽" */ + 0x0, 0x11, 0xef, 0xf8, 0x84, 0x51, 0x17, 0xf2, + 0x41, 0x5e, 0xef, 0x8, 0x44, 0x12, 0xfe, 0x25, + 0x11, 0xed, 0xfc, 0x88, 0x41, 0x28, 0x2, 0x8f, + 0xe4, 0x0, 0x4f, 0xff, 0x0, + + /* U+6C05 "氅" */ + 0x29, 0x20, 0xa, 0x3f, 0x7f, 0x64, 0x41, 0x64, + 0x5d, 0x18, 0x5d, 0x1c, 0x41, 0x63, 0x6, 0x0, + 0x3f, 0xfc, 0x1, 0x0, 0x3f, 0xfc, 0x1, 0x0, + 0x7f, 0xff, 0x1, 0x1, 0x1, 0xfe, + + /* U+6C06 "氆" */ + 0x1, 0x0, 0x21, 0x23, 0x9f, 0xf9, 0x4, 0x82, + 0x29, 0x44, 0x12, 0x3d, 0xff, 0x90, 0x0, 0x23, + 0xfc, 0x54, 0xb, 0xcf, 0xf1, 0x10, 0x22, 0x3f, + 0xe4, 0x0, 0x48, 0x0, 0x8f, 0xfe, + + /* U+6C07 "氇" */ + 0x0, 0x80, 0x39, 0xf0, 0xe2, 0x10, 0x27, 0xec, + 0x23, 0xfc, 0xfa, 0x44, 0x21, 0xb8, 0x27, 0xfe, + 0x20, 0x0, 0xfb, 0xfc, 0x23, 0xfc, 0x22, 0x4, + 0x23, 0xfe, 0x20, 0x2, 0x1f, 0xfe, + + /* U+6C0D "氍" */ + 0x7b, 0x82, 0x99, 0x79, 0xee, 0x42, 0x64, 0x87, + 0xf9, 0x6f, 0x77, 0x5, 0x4, 0x1f, 0xc8, 0x64, + 0x1f, 0x7f, 0xe0, 0x90, 0x41, 0xfc, 0x92, 0x41, + 0x27, 0xf2, 0x48, 0x7, 0x80, + + /* U+6C0F "氏" */ + 0x0, 0x8, 0xff, 0xe1, 0x2, 0x2, 0x4, 0x4, + 0x8, 0x8, 0x10, 0x1f, 0xff, 0xa0, 0x40, 0x40, + 0x80, 0x80, 0x81, 0x1, 0x2, 0x2, 0x14, 0x62, + 0x4f, 0x4, 0x90, 0x7, 0x0, + + /* U+6C10 "氐" */ + 0x0, 0x8, 0xff, 0xe1, 0x2, 0x2, 0x4, 0x4, + 0x8, 0x8, 0x10, 0x1f, 0xff, 0xa0, 0x40, 0x40, + 0x40, 0x80, 0x81, 0x9, 0x2, 0x32, 0x25, 0x82, + 0x4c, 0x66, 0x80, 0x47, 0x0, + + /* U+6C11 "民" */ + 0xff, 0xf2, 0x0, 0x48, 0x1, 0x20, 0x4, 0xff, + 0xf2, 0x8, 0x8, 0x10, 0x20, 0x40, 0xff, 0xfe, + 0x4, 0x8, 0x10, 0x20, 0x23, 0x88, 0x8a, 0xc1, + 0x2c, 0x3, 0x80, + + /* U+6C13 "氓" */ + 0x0, 0x0, 0x47, 0xf8, 0x90, 0x17, 0xe0, 0x24, + 0x3f, 0xc8, 0x84, 0x11, 0x8, 0x22, 0x10, 0x43, + 0xfe, 0x88, 0x41, 0x10, 0x82, 0x21, 0x7, 0x49, + 0x20, 0xa2, 0x41, 0x83, 0x0, + + /* U+6C14 "气" */ + 0x10, 0x0, 0x20, 0x0, 0xff, 0xf1, 0x0, 0x4, + 0x0, 0xb, 0xff, 0x20, 0x0, 0x0, 0x0, 0x7f, + 0xf0, 0x0, 0x20, 0x0, 0x40, 0x0, 0x80, 0x1, + 0x20, 0x2, 0x40, 0x3, 0x0, 0x6, + + /* U+6C15 "氕" */ + 0x0, 0x0, 0x20, 0x0, 0x7f, 0xf1, 0x0, 0x4, + 0x0, 0x1b, 0xff, 0x20, 0x0, 0x3f, 0xf8, 0x0, + 0x10, 0x8, 0x20, 0x10, 0x40, 0x40, 0x80, 0x81, + 0x23, 0x2, 0x4c, 0x3, 0x60, 0x6, + + /* U+6C16 "氖" */ + 0x8, 0x0, 0x1f, 0xfe, 0x10, 0x0, 0x2f, 0xfc, + 0x40, 0x0, 0x3f, 0xf8, 0x0, 0x8, 0x3f, 0x88, + 0x8, 0x88, 0x8, 0x88, 0x8, 0xe8, 0x10, 0x48, + 0x10, 0x49, 0x20, 0x4a, 0x41, 0xc6, + + /* U+6C18 "氘" */ + 0x0, 0x0, 0x20, 0x0, 0x7f, 0xf1, 0x0, 0x4, + 0x0, 0x1b, 0xff, 0x20, 0x0, 0x3f, 0xf8, 0x0, + 0x10, 0x0, 0x20, 0x44, 0x40, 0x88, 0x81, 0x11, + 0x22, 0x22, 0x48, 0x42, 0xe0, 0x86, + + /* U+6C19 "氙" */ + 0x0, 0x0, 0x20, 0x0, 0x7f, 0xf1, 0x0, 0x4, + 0x0, 0x13, 0xff, 0x20, 0x0, 0x3f, 0xf8, 0x0, + 0x10, 0x8, 0x21, 0x11, 0x42, 0x22, 0x84, 0x45, + 0x28, 0x8a, 0x9f, 0xf3, 0x0, 0x26, + + /* U+6C1A "氚" */ + 0x0, 0x0, 0x20, 0x0, 0x7f, 0xf1, 0x0, 0x4, + 0x0, 0x1b, 0xff, 0x20, 0x0, 0x3f, 0xf8, 0x0, + 0x10, 0x0, 0x20, 0x92, 0x41, 0x24, 0x82, 0x49, + 0x2c, 0x92, 0x51, 0x22, 0xc2, 0x46, + + /* U+6C1B "氛" */ + 0x8, 0x0, 0x1f, 0xfe, 0x30, 0x0, 0x2f, 0xfc, + 0x40, 0x0, 0x3f, 0xf8, 0x0, 0x8, 0x8, 0x88, + 0x10, 0x48, 0x60, 0x28, 0x3f, 0xc8, 0x8, 0x49, + 0x8, 0x49, 0x10, 0xc5, 0x63, 0x86, + + /* U+6C1F "氟" */ + 0x10, 0x0, 0x3f, 0xf8, 0x80, 0x2, 0xff, 0xc4, + 0x0, 0x7, 0xff, 0x0, 0x2, 0x3f, 0xe4, 0xa, + 0x48, 0xff, 0x91, 0x48, 0x23, 0xff, 0x51, 0x22, + 0xa6, 0x5d, 0x58, 0x81, 0x0, + + /* U+6C21 "氡" */ + 0x10, 0x0, 0x3f, 0xf8, 0x80, 0x2, 0xff, 0xc4, + 0x0, 0x7, 0xff, 0x2, 0x2, 0xf, 0xe4, 0x30, + 0xc8, 0x1e, 0x10, 0xf7, 0xa2, 0x80, 0x50, 0xf8, + 0xa6, 0x1, 0x43, 0xf1, 0x80, + + /* U+6C22 "氢" */ + 0x10, 0x0, 0x1f, 0xfe, 0x20, 0x0, 0x6f, 0xfc, + 0x40, 0x0, 0x3f, 0xf8, 0x0, 0x8, 0x3f, 0xc8, + 0x1, 0x88, 0xf, 0x88, 0x70, 0x68, 0x3f, 0xc9, + 0x4, 0x9, 0x4, 0x5, 0x7f, 0xe6, + + /* U+6C24 "氤" */ + 0x10, 0x0, 0x3f, 0xfe, 0x20, 0x0, 0x5f, 0xf8, + 0x80, 0x0, 0x3f, 0xf8, 0x0, 0x8, 0x7f, 0xc8, + 0x44, 0x48, 0x5f, 0x48, 0x44, 0x48, 0x4a, 0x49, + 0x51, 0x49, 0x7f, 0xc6, 0x40, 0x46, + + /* U+6C26 "氦" */ + 0x10, 0x0, 0x1f, 0xfe, 0x20, 0x0, 0x5f, 0xf8, + 0x80, 0x0, 0x3f, 0xf8, 0x0, 0x8, 0x7f, 0xe8, + 0x18, 0x88, 0x3f, 0x8, 0x26, 0x48, 0x19, 0x89, + 0x63, 0x9, 0xc, 0xc6, 0x70, 0x26, + + /* U+6C27 "氧" */ + 0x10, 0x0, 0x1f, 0xfe, 0x20, 0x0, 0x5f, 0xfc, + 0x80, 0x0, 0x7f, 0xf8, 0x10, 0x8, 0x9, 0x8, + 0x7f, 0xe8, 0x4, 0x8, 0x7f, 0xc8, 0x4, 0x9, + 0xff, 0xe9, 0x4, 0x6, 0x4, 0x6, + + /* U+6C28 "氨" */ + 0x20, 0x0, 0x7f, 0xf9, 0x0, 0x2, 0xff, 0x88, + 0x0, 0xf, 0xfe, 0x2, 0x4, 0x7f, 0xe8, 0x80, + 0x50, 0x20, 0x23, 0xff, 0x41, 0x8, 0x86, 0x21, + 0x23, 0xc2, 0xbc, 0x73, 0x0, + + /* U+6C29 "氩" */ + 0x10, 0x0, 0x7f, 0xf8, 0x80, 0x2, 0xff, 0xc8, + 0x0, 0xf, 0xfe, 0x0, 0x4, 0x7f, 0xe8, 0x12, + 0x11, 0x24, 0xe1, 0x49, 0x42, 0x94, 0x91, 0x20, + 0xbf, 0xfd, 0x80, 0x3, 0x0, + + /* U+6C2A "氪" */ + 0x20, 0x0, 0x7f, 0xf9, 0x0, 0x4, 0xff, 0x87, + 0xff, 0x1, 0x2, 0x3f, 0xf4, 0x4, 0x8, 0x7f, + 0x90, 0x81, 0x21, 0x2, 0x43, 0xfc, 0x91, 0x23, + 0x24, 0x49, 0xb0, 0xf3, 0x0, + + /* U+6C2E "氮" */ + 0x10, 0x0, 0x3f, 0xfe, 0x20, 0x0, 0x4f, 0xf0, + 0x3f, 0xf8, 0x4, 0x8, 0x24, 0x48, 0x4, 0x88, + 0x1b, 0x88, 0x60, 0x68, 0x24, 0x48, 0x34, 0x89, + 0x6, 0x9, 0x19, 0xca, 0x60, 0x26, + + /* U+6C2F "氯" */ + 0x20, 0x0, 0x7f, 0xf9, 0x0, 0x2, 0xff, 0xc8, + 0x0, 0x7, 0xfe, 0x1f, 0xe4, 0x0, 0x48, 0x3f, + 0x91, 0xff, 0xe0, 0x10, 0x43, 0x2c, 0x91, 0x60, + 0xbc, 0xbd, 0x87, 0x1, 0x0, + + /* U+6C30 "氰" */ + 0x20, 0x0, 0x7f, 0xf9, 0x0, 0x6, 0x7f, 0x8f, + 0xff, 0x0, 0x82, 0x1f, 0xf4, 0x3f, 0xe8, 0x4, + 0x11, 0xf7, 0xe1, 0xff, 0x43, 0xfe, 0x97, 0xfc, + 0xa8, 0x9, 0x90, 0xe3, 0x0, + + /* U+6C32 "氲" */ + 0x10, 0x0, 0x3f, 0xfe, 0x20, 0x0, 0x5f, 0xf8, + 0x80, 0x0, 0x1f, 0xf8, 0x3f, 0xc8, 0x20, 0x48, + 0x3f, 0xc8, 0x20, 0x48, 0x1f, 0x88, 0x7f, 0xe9, + 0x49, 0x29, 0xff, 0xfa, 0x0, 0x6, + + /* U+6C34 "水" */ + 0x0, 0x80, 0x0, 0x80, 0x0, 0x80, 0x0, 0x82, + 0x7c, 0xc4, 0x4, 0xc8, 0x4, 0xb0, 0x8, 0xa0, + 0x8, 0x90, 0x10, 0x88, 0x10, 0x8c, 0x20, 0x86, + 0x40, 0x81, 0x0, 0x80, 0x7, 0x0, + + /* U+6C35 "氵" */ + 0x42, 0x10, 0x8c, 0x20, 0x2, 0x22, 0x44, 0x0, + + /* U+6C38 "永" */ + 0x3, 0x0, 0x0, 0xe0, 0x0, 0x0, 0x1f, 0x80, + 0x0, 0x82, 0x0, 0xc4, 0x7c, 0xd8, 0x4, 0xb0, + 0x8, 0xa0, 0x8, 0x90, 0x10, 0x88, 0x20, 0x84, + 0x40, 0x83, 0x0, 0x80, 0x7, 0x80, + + /* U+6C3D "氽" */ + 0x1, 0x0, 0x6, 0x0, 0x13, 0x0, 0x43, 0x3, + 0x11, 0x98, 0x20, 0xc0, 0x42, 0x3e, 0xcc, 0x5, + 0x60, 0x12, 0x80, 0x64, 0x81, 0x88, 0x86, 0x10, + 0xd0, 0x20, 0x1, 0xc0, 0x0, + + /* U+6C40 "汀" */ + 0xc0, 0x0, 0x4f, 0xfc, 0x40, 0x80, 0x1, 0x8, + 0x2, 0x18, 0x4, 0x8, 0x8, 0x0, 0x10, 0x0, + 0x20, 0x40, 0x40, 0x80, 0x82, 0x1, 0x4, 0x2, + 0x8, 0x4, 0x20, 0xf8, 0x0, + + /* U+6C41 "汁" */ + 0x40, 0x40, 0x40, 0x80, 0x1, 0x0, 0x2, 0x8, + 0x4, 0xd, 0xff, 0xc8, 0x10, 0x0, 0x20, 0x0, + 0x40, 0x40, 0x80, 0x81, 0x2, 0x2, 0x4, 0x4, + 0x8, 0x8, 0x20, 0x10, 0x0, + + /* U+6C42 "求" */ + 0x0, 0x0, 0x2, 0x60, 0x4, 0x7, 0xff, 0xf0, + 0x10, 0x4, 0x10, 0x84, 0x22, 0x4, 0xa8, 0x1, + 0x60, 0xe, 0x40, 0x64, 0x43, 0x8, 0x48, 0x10, + 0x60, 0x20, 0x1, 0xc0, 0x0, + + /* U+6C46 "汆" */ + 0x2, 0x0, 0x3, 0x0, 0x19, 0x0, 0x41, 0x87, + 0x10, 0xc0, 0x20, 0x0, 0x42, 0x3e, 0xc8, 0x5, + 0x60, 0x12, 0x80, 0x64, 0x81, 0x88, 0x86, 0x10, + 0xd0, 0x20, 0x1, 0xc0, 0x0, + + /* U+6C47 "汇" */ + 0x47, 0xfe, 0x68, 0x0, 0x10, 0x0, 0x20, 0xc, + 0x40, 0x4, 0x80, 0x1, 0x0, 0x2, 0x0, 0x24, + 0x0, 0x48, 0x1, 0x10, 0x2, 0x20, 0x4, 0x7f, + 0xe8, 0x0, 0x0, + + /* U+6C49 "汉" */ + 0x0, 0x0, 0x4f, 0xf8, 0x48, 0x10, 0x10, 0x20, + 0x20, 0x88, 0x21, 0xc, 0x42, 0x0, 0x88, 0x0, + 0x90, 0x20, 0xc0, 0x81, 0x81, 0x5, 0x82, 0x31, + 0x88, 0xc0, 0xd2, 0x0, 0x0, + + /* U+6C4A "汊" */ + 0x0, 0x0, 0x9f, 0xf8, 0xd0, 0x10, 0x20, 0x20, + 0x44, 0x90, 0x49, 0x18, 0x82, 0x1, 0x8, 0x1, + 0x10, 0x41, 0x40, 0x83, 0x2, 0x7, 0x4, 0x31, + 0x9, 0x81, 0x84, 0x0, 0x80, + + /* U+6C50 "汐" */ + 0x0, 0x80, 0x61, 0x0, 0x62, 0x0, 0xf, 0xf0, + 0x10, 0x28, 0x40, 0x4c, 0xc1, 0x2, 0x42, 0x0, + 0x68, 0x20, 0x30, 0x80, 0x41, 0x1, 0x2, 0xc, + 0x8, 0x30, 0x11, 0x80, 0x0, + + /* U+6C54 "汔" */ + 0x41, 0x0, 0xc4, 0x0, 0x8f, 0xf8, 0x20, 0x0, + 0x40, 0x19, 0x0, 0x18, 0xfe, 0x0, 0x8, 0x0, + 0x60, 0x41, 0x80, 0x84, 0x1, 0x10, 0x14, 0x40, + 0x48, 0x80, 0x91, 0xfe, 0x0, + + /* U+6C55 "汕" */ + 0x0, 0x41, 0x81, 0x3, 0x4, 0x0, 0x10, 0x4, + 0x46, 0x11, 0x16, 0x44, 0x41, 0x11, 0x4, 0x44, + 0x91, 0x12, 0x44, 0x49, 0x11, 0x44, 0x45, 0x1f, + 0xf4, 0x0, 0x40, + + /* U+6C57 "汗" */ + 0x0, 0x0, 0x8f, 0xf8, 0xc1, 0x0, 0x2, 0x0, + 0x4, 0x18, 0x8, 0x8, 0x10, 0x7, 0xff, 0x0, + 0x40, 0x40, 0x80, 0x81, 0x2, 0x2, 0x4, 0x4, + 0x10, 0x8, 0x20, 0x10, 0x0, + + /* U+6C5B "汛" */ + 0x0, 0x0, 0x4f, 0xf8, 0x31, 0x8, 0x1, 0x8, + 0x1, 0x8, 0xc1, 0x8, 0x21, 0x8, 0xf, 0xe8, + 0x1, 0x8, 0x21, 0x8, 0x21, 0x8, 0x21, 0x9, + 0x41, 0x9, 0x41, 0x6, 0x41, 0x6, + + /* U+6C5C "汜" */ + 0x0, 0x0, 0xcf, 0xf8, 0xd0, 0x10, 0x20, 0x28, + 0x40, 0x4c, 0x80, 0x81, 0xff, 0x2, 0x0, 0x4, + 0x0, 0x48, 0x0, 0x90, 0xa, 0x20, 0x14, 0x40, + 0x30, 0x7f, 0x80, + + /* U+6C5D "汝" */ + 0x0, 0x80, 0xc1, 0x0, 0x44, 0x0, 0x8, 0x0, + 0xff, 0xf8, 0x41, 0x18, 0x82, 0x1, 0x8, 0x4, + 0x10, 0x8, 0x20, 0x8c, 0x81, 0x6, 0x4, 0xb, + 0x8, 0x63, 0x27, 0x1, 0x0, + + /* U+6C5E "汞" */ + 0x3f, 0xfc, 0x2, 0x0, 0x4, 0x3, 0xff, 0xf0, + 0x0, 0x0, 0x10, 0x0, 0x21, 0x3e, 0x44, 0x4, + 0xf0, 0x11, 0x40, 0x42, 0x41, 0x4, 0x64, 0x8, + 0x20, 0xe0, 0x0, + + /* U+6C5F "江" */ + 0x40, 0x0, 0x4f, 0xf8, 0x1, 0x0, 0x2, 0x8, + 0x4, 0x8, 0x8, 0x8, 0x10, 0x0, 0x20, 0x0, + 0x40, 0x40, 0x80, 0x81, 0x2, 0x2, 0x4, 0x4, + 0x9, 0xff, 0xe0, 0x0, 0x0, + + /* U+6C60 "池" */ + 0x0, 0x40, 0x80, 0x80, 0x91, 0x0, 0x22, 0x20, + 0x47, 0xd8, 0xf8, 0x83, 0x11, 0x2, 0x22, 0x4, + 0x5c, 0x48, 0x81, 0x11, 0xa, 0x20, 0x14, 0x40, + 0x28, 0x80, 0xe0, 0xff, 0x0, + + /* U+6C61 "污" */ + 0x0, 0x0, 0xcf, 0xf8, 0xc0, 0x0, 0x0, 0x0, + 0x0, 0x19, 0xff, 0xc8, 0x80, 0x1, 0x0, 0x3, + 0xfc, 0x40, 0x8, 0x80, 0x12, 0x0, 0x44, 0x0, + 0x88, 0x1, 0x10, 0x3c, 0x0, + + /* U+6C64 "汤" */ + 0x0, 0x1, 0x1f, 0xe3, 0x1, 0x0, 0x18, 0x1, + 0x83, 0x18, 0x2, 0x7f, 0xc0, 0x49, 0x2, 0x44, + 0x89, 0x14, 0x44, 0x53, 0x21, 0x58, 0x86, 0x4, + 0x28, 0x27, 0x80, + + /* U+6C68 "汨" */ + 0x0, 0x1, 0x9f, 0xf3, 0x40, 0x41, 0x1, 0x84, + 0x5, 0x90, 0x10, 0x7f, 0xc1, 0x1, 0x24, 0x4, + 0x90, 0x14, 0x40, 0x51, 0x1, 0x47, 0xfd, 0x10, + 0x18, 0x40, 0x40, + + /* U+6C69 "汩" */ + 0x40, 0x1, 0x9f, 0xf3, 0x40, 0x41, 0x1, 0x84, + 0x7, 0x10, 0x12, 0x40, 0x41, 0xfd, 0x4, 0x4, + 0x90, 0x12, 0x40, 0x51, 0x1, 0x44, 0x5, 0x1f, + 0xf8, 0x40, 0x40, + + /* U+6C6A "汪" */ + 0x40, 0x0, 0xcf, 0xf8, 0x1, 0x0, 0x2, 0x8, + 0x4, 0x18, 0x8, 0x8, 0x10, 0x3, 0xfe, 0x0, + 0x40, 0x40, 0x80, 0x81, 0x2, 0x2, 0x4, 0x4, + 0x9, 0xff, 0xe0, 0x0, 0x0, + + /* U+6C70 "汰" */ + 0x0, 0x81, 0x2, 0x3, 0x8, 0x0, 0x20, 0xf, + 0xfe, 0x2, 0x6, 0x8, 0x0, 0x50, 0x1, 0x40, + 0x4, 0x82, 0x22, 0x10, 0xc4, 0x45, 0x19, 0x22, + 0x29, 0x0, 0x40, + + /* U+6C72 "汲" */ + 0x0, 0x0, 0x8f, 0xf0, 0x88, 0x20, 0x10, 0x80, + 0x32, 0x18, 0x67, 0x88, 0xc1, 0x1, 0x84, 0x2, + 0x88, 0x49, 0x21, 0x11, 0xc2, 0x63, 0x4, 0x8d, + 0x12, 0x21, 0x80, 0x80, 0x80, + + /* U+6C74 "汴" */ + 0x40, 0x80, 0x40, 0x80, 0x40, 0x0, 0x3f, 0xf8, + 0x4, 0x8, 0x8, 0x8, 0x10, 0x0, 0x38, 0x0, + 0x48, 0x40, 0x88, 0x81, 0x1, 0x2, 0x4, 0x4, + 0x8, 0x8, 0x10, 0x10, 0x0, + + /* U+6C76 "汶" */ + 0x0, 0xc0, 0xc0, 0x80, 0x40, 0x80, 0x7f, 0xf0, + 0x20, 0x98, 0x41, 0x18, 0x82, 0x0, 0x88, 0x1, + 0x10, 0x41, 0x40, 0x83, 0x1, 0x7, 0x4, 0x13, + 0x8, 0xc1, 0xb6, 0x0, 0x80, + + /* U+6C79 "汹" */ + 0x40, 0x8, 0x42, 0x10, 0x44, 0x20, 0x24, 0x90, + 0x45, 0x2c, 0x8c, 0x49, 0x8, 0x82, 0x31, 0x4, + 0x92, 0x4b, 0x14, 0x94, 0x29, 0x20, 0x12, 0x40, + 0x28, 0xff, 0xd0, 0x0, 0x80, + + /* U+6C7D "汽" */ + 0x1, 0x0, 0x61, 0x0, 0x31, 0xfe, 0x2, 0x0, + 0x4, 0x0, 0x45, 0xfc, 0x60, 0x0, 0x30, 0x0, + 0x7, 0xf8, 0x0, 0x8, 0x0, 0x8, 0x20, 0x8, + 0x20, 0x9, 0x20, 0xa, 0x40, 0x6, 0x40, 0x6, + + /* U+6C7E "汾" */ + 0x0, 0x0, 0x20, 0x50, 0x18, 0x88, 0x0, 0x8c, + 0x1, 0x4, 0x42, 0x3, 0x34, 0x1, 0x3, 0xfc, + 0x0, 0x84, 0x10, 0x84, 0x20, 0x84, 0x21, 0x4, + 0x21, 0x4, 0x42, 0x4, 0x4c, 0x78, + + /* U+6C81 "沁" */ + 0x0, 0x40, 0xc0, 0x80, 0xc0, 0x80, 0x8, 0x80, + 0x10, 0x10, 0x20, 0x99, 0x41, 0x4, 0x82, 0x9, + 0x2, 0x52, 0x4, 0xa4, 0x22, 0x8, 0x44, 0x10, + 0x88, 0x21, 0x20, 0x3c, 0x0, + + /* U+6C82 "沂" */ + 0x40, 0x0, 0x47, 0xfc, 0x48, 0x0, 0x10, 0x0, + 0x20, 0x8, 0x40, 0x8, 0xff, 0x81, 0x8, 0x2, + 0x10, 0x44, 0x20, 0x88, 0x41, 0x20, 0x84, 0x41, + 0x9, 0x82, 0x12, 0x4, 0x0, + + /* U+6C83 "沃" */ + 0x60, 0x0, 0x67, 0xf8, 0x61, 0x0, 0x2, 0x0, + 0x4, 0xc, 0x8, 0x5, 0xff, 0x80, 0x20, 0x0, + 0x60, 0x21, 0x40, 0x82, 0x41, 0x8, 0x82, 0x30, + 0x88, 0xc0, 0xc2, 0x0, 0x0, + + /* U+6C85 "沅" */ + 0x0, 0x0, 0x23, 0xfc, 0x18, 0x0, 0x0, 0x0, + 0xc0, 0x0, 0x27, 0xfe, 0x0, 0x90, 0x1, 0x10, + 0x1, 0x10, 0x11, 0x10, 0x21, 0x10, 0x21, 0x11, + 0x22, 0x13, 0x46, 0x12, 0x4c, 0x1e, 0x0, 0x0, + + /* U+6C86 "沆" */ + 0x0, 0x40, 0x30, 0x20, 0x17, 0xfe, 0x0, 0x0, + 0x0, 0x0, 0x63, 0xf0, 0x22, 0x10, 0x2, 0x10, + 0x2, 0x10, 0x12, 0x10, 0x22, 0x10, 0x22, 0x11, + 0x22, 0x11, 0x24, 0xa, 0x48, 0xe, + + /* U+6C88 "沈" */ + 0x0, 0x40, 0x60, 0x40, 0x30, 0x40, 0x7, 0xfe, + 0x4, 0x42, 0x44, 0x42, 0x60, 0x40, 0x30, 0x40, + 0x0, 0xa0, 0x0, 0xa0, 0x10, 0xa0, 0x21, 0x20, + 0x23, 0x21, 0x22, 0x21, 0x44, 0x21, 0x58, 0x3e, + + /* U+6C89 "沉" */ + 0x40, 0x0, 0xcf, 0xfc, 0x50, 0x8, 0x20, 0x10, + 0x0, 0x8, 0x3e, 0xc, 0x44, 0x0, 0x88, 0x1, + 0x10, 0x44, 0x20, 0x88, 0x41, 0x10, 0x82, 0x21, + 0x28, 0x82, 0x52, 0x7, 0x80, + + /* U+6C8C "沌" */ + 0x0, 0x0, 0x81, 0x0, 0xbf, 0xf0, 0x4, 0x0, + 0x8, 0x18, 0x91, 0x19, 0x22, 0x2, 0x44, 0x4, + 0x88, 0x4f, 0xf1, 0x2, 0x2, 0x4, 0x14, 0x8, + 0x30, 0x8, 0xa0, 0x1f, 0x0, + + /* U+6C8F "沏" */ + 0x4, 0x1, 0x13, 0xf2, 0x42, 0x41, 0x9, 0x7, + 0x27, 0x70, 0x96, 0x42, 0x41, 0x9, 0x4, 0x24, + 0x14, 0x92, 0x64, 0x51, 0x11, 0x40, 0x85, 0x2, + 0x1c, 0x13, 0x80, + + /* U+6C90 "沐" */ + 0x0, 0x80, 0xc1, 0x0, 0x2, 0x0, 0x7f, 0xe8, + 0x8, 0x8, 0x18, 0x8, 0x70, 0x1, 0x50, 0x2, + 0xa0, 0x49, 0x21, 0x32, 0x22, 0x44, 0x25, 0x8, + 0x10, 0x10, 0x20, 0x20, 0x0, + + /* U+6C93 "沓" */ + 0x0, 0x80, 0x1, 0x9, 0xf3, 0x20, 0x26, 0x80, + 0x8a, 0x2, 0x13, 0x19, 0xe1, 0x80, 0x0, 0x3f, + 0xf8, 0x40, 0x10, 0xff, 0xe1, 0x0, 0x42, 0x0, + 0x87, 0xff, 0x8, 0x2, 0x0, + + /* U+6C94 "沔" */ + 0x0, 0x0, 0x4f, 0xfc, 0x41, 0x0, 0x12, 0xc, + 0x27, 0xc4, 0x88, 0x81, 0x11, 0x2, 0x22, 0x7, + 0xfc, 0x20, 0x8, 0x80, 0x11, 0x0, 0x22, 0x0, + 0x48, 0x0, 0x90, 0x3e, 0x0, + + /* U+6C99 "沙" */ + 0x40, 0x40, 0x40, 0x80, 0x41, 0x0, 0x12, 0x48, + 0x24, 0x4c, 0x88, 0x49, 0x10, 0x4, 0x22, 0x20, + 0x4c, 0x40, 0x30, 0x80, 0xc2, 0x2, 0x4, 0x18, + 0x9, 0xc0, 0x0, + + /* U+6C9B "沛" */ + 0x0, 0x40, 0x80, 0x80, 0xbf, 0xf8, 0x2, 0x8, + 0x4, 0x8, 0xff, 0x82, 0x11, 0x4, 0x22, 0x8, + 0x44, 0x50, 0x89, 0x21, 0x12, 0x42, 0x24, 0x5, + 0xc8, 0x8, 0x20, 0x10, 0x0, + + /* U+6C9F "沟" */ + 0x42, 0x0, 0x88, 0x1, 0x7f, 0xc1, 0x1, 0x8, + 0x7, 0x24, 0x12, 0x10, 0x40, 0x91, 0x2, 0x44, + 0x90, 0x94, 0xfe, 0x50, 0x5, 0x40, 0x5, 0x0, + 0x38, 0xf, 0x80, + + /* U+6CA1 "没" */ + 0x61, 0xf8, 0x31, 0x8, 0x2, 0x8, 0x82, 0xf, + 0x44, 0x0, 0x20, 0x0, 0x7, 0xfc, 0x2, 0x4, + 0x21, 0x8, 0x21, 0x10, 0x40, 0xa0, 0x40, 0x60, + 0x43, 0x98, 0x9c, 0x6, + + /* U+6CA3 "沣" */ + 0x40, 0x40, 0x60, 0x80, 0x1f, 0xf0, 0x2, 0x8, + 0x4, 0xc, 0x8, 0x9, 0xff, 0x0, 0x20, 0x0, + 0x40, 0x40, 0x80, 0xbf, 0xf9, 0x2, 0x4, 0x4, + 0x8, 0x8, 0x10, 0x10, 0x0, + + /* U+6CA4 "沤" */ + 0x40, 0x0, 0xcf, 0xfc, 0x50, 0x0, 0x20, 0x20, + 0x48, 0x98, 0x99, 0x9, 0x14, 0x2, 0x18, 0x4, + 0x30, 0x48, 0xa0, 0x91, 0x21, 0x24, 0x24, 0x50, + 0x48, 0x80, 0x11, 0xff, 0x80, + + /* U+6CA5 "沥" */ + 0x0, 0x0, 0xcf, 0xfc, 0x11, 0x0, 0x22, 0x8, + 0x44, 0x8, 0xbf, 0x89, 0x11, 0x2, 0x22, 0x4, + 0x44, 0x48, 0x89, 0x12, 0x12, 0x44, 0x24, 0x88, + 0x49, 0x20, 0xa4, 0x8e, 0x0, + + /* U+6CA6 "沦" */ + 0x0, 0x40, 0xc0, 0x80, 0xc2, 0x80, 0x8, 0x80, + 0x20, 0x98, 0x80, 0xda, 0x80, 0x1, 0xc, 0x2, + 0x20, 0x45, 0x80, 0x8c, 0x1, 0x10, 0x24, 0x20, + 0x48, 0x40, 0x90, 0x7e, 0x0, + + /* U+6CA7 "沧" */ + 0x0, 0x40, 0xc0, 0x80, 0x62, 0x80, 0x8, 0x80, + 0x20, 0xd9, 0x80, 0x48, 0xfe, 0x1, 0x4, 0x2, + 0x8, 0x24, 0x10, 0x8b, 0xc1, 0x10, 0x12, 0x20, + 0x28, 0x40, 0x50, 0x7f, 0x0, + + /* U+6CA9 "沩" */ + 0x81, 0x81, 0x26, 0x2, 0x50, 0x0, 0x40, 0x9f, + 0xff, 0x4, 0x16, 0x10, 0x40, 0x51, 0x1, 0x44, + 0x8, 0x94, 0x20, 0x50, 0x81, 0x44, 0x6, 0x30, + 0x19, 0x87, 0x80, + + /* U+6CAA "沪" */ + 0x0, 0x41, 0x81, 0x2, 0x7f, 0xc1, 0x1, 0x4, + 0x7, 0x10, 0x10, 0x40, 0x41, 0xff, 0x4, 0x4, + 0x90, 0x4, 0x40, 0x13, 0x0, 0x48, 0x2, 0x60, + 0x9, 0x0, 0x0, + + /* U+6CAB "沫" */ + 0xc0, 0x40, 0x40, 0x80, 0x3f, 0xf8, 0x2, 0x8, + 0x4, 0x8, 0x8, 0x3, 0xff, 0x80, 0x60, 0x0, + 0xe0, 0x42, 0xa0, 0x89, 0x42, 0x32, 0x44, 0xc4, + 0x70, 0x8, 0x60, 0x10, 0x0, + + /* U+6CAD "沭" */ + 0xc0, 0x80, 0xc1, 0x20, 0x82, 0x20, 0x4, 0x0, + 0xff, 0xd0, 0x10, 0x18, 0x70, 0x0, 0xe0, 0x2, + 0xa0, 0x4d, 0x20, 0x92, 0x22, 0x44, 0x25, 0x8, + 0x10, 0x10, 0x20, 0x20, 0x0, + + /* U+6CAE "沮" */ + 0x0, 0x0, 0x8f, 0xf0, 0x90, 0x20, 0x20, 0x40, + 0x40, 0x98, 0xff, 0x9, 0x2, 0x2, 0x4, 0x4, + 0x8, 0x8f, 0xf1, 0x10, 0x22, 0x20, 0x44, 0x40, + 0x93, 0xff, 0xc0, 0x0, 0x0, + + /* U+6CB1 "沱" */ + 0x40, 0x40, 0x40, 0x40, 0x5f, 0xf8, 0x20, 0x10, + 0x40, 0x28, 0x0, 0x8, 0x81, 0x1, 0xc, 0x2, + 0x20, 0x27, 0x80, 0x88, 0x1, 0x10, 0x12, 0x20, + 0x28, 0x40, 0xd0, 0x7f, 0x0, + + /* U+6CB2 "沲" */ + 0x1, 0x0, 0x84, 0x0, 0x8f, 0xf8, 0x20, 0x0, + 0x84, 0x10, 0x88, 0x11, 0x17, 0x3, 0xf2, 0x1c, + 0x44, 0x8, 0x89, 0x11, 0x12, 0x22, 0xd4, 0x44, + 0x28, 0x80, 0x60, 0xff, 0x0, + + /* U+6CB3 "河" */ + 0x67, 0xff, 0x10, 0x2, 0x0, 0x2, 0x3, 0xe2, + 0x42, 0x22, 0x32, 0x22, 0x2, 0x22, 0x2, 0x22, + 0x12, 0x22, 0x23, 0xe2, 0x20, 0x2, 0x20, 0x2, + 0x40, 0x4, 0x40, 0x3c, + + /* U+6CB8 "沸" */ + 0x41, 0x20, 0x5f, 0xf8, 0x4, 0x94, 0x9, 0x24, + 0xff, 0xc1, 0x24, 0x4, 0x48, 0x8, 0x90, 0x5f, + 0xfe, 0x82, 0x49, 0x8, 0x92, 0x11, 0x68, 0x42, + 0x13, 0x4, 0x0, + + /* U+6CB9 "油" */ + 0x40, 0x20, 0x60, 0x20, 0x30, 0x20, 0x7, 0xff, + 0x4, 0x21, 0xc4, 0x21, 0x24, 0x21, 0x4, 0x21, + 0x7, 0xff, 0x24, 0x21, 0x24, 0x21, 0x24, 0x21, + 0x44, 0x21, 0x47, 0xff, 0x44, 0x1, + + /* U+6CBB "治" */ + 0x40, 0x80, 0x42, 0x0, 0x4, 0x40, 0x10, 0x48, + 0x40, 0x49, 0xff, 0xc0, 0x0, 0x80, 0x0, 0x7, + 0xfc, 0x48, 0x9, 0x10, 0x12, 0x20, 0x24, 0x40, + 0x50, 0xff, 0xa1, 0x1, 0x0, + + /* U+6CBC "沼" */ + 0x67, 0xfe, 0x21, 0x4, 0x2, 0x8, 0x4, 0x14, + 0x10, 0x24, 0x47, 0x81, 0x0, 0x1, 0xff, 0x12, + 0x2, 0x44, 0x4, 0x88, 0x9, 0x10, 0x14, 0x3f, + 0xe8, 0x40, 0x40, + + /* U+6CBD "沽" */ + 0x0, 0x41, 0x0, 0x81, 0x81, 0x1, 0x2, 0x0, + 0xff, 0xe0, 0x8, 0x30, 0x10, 0x0, 0x20, 0x7, + 0xfc, 0x48, 0x8, 0x90, 0x11, 0x20, 0x24, 0x40, + 0x48, 0x80, 0x91, 0xff, 0x42, 0x2, + + /* U+6CBE "沾" */ + 0x0, 0x40, 0x40, 0x80, 0x41, 0x0, 0x42, 0x0, + 0x7, 0xe0, 0x8, 0x18, 0x10, 0x0, 0x20, 0x3, + 0xfc, 0x4, 0x8, 0x48, 0x11, 0x10, 0x22, 0x20, + 0x44, 0x40, 0x90, 0xff, 0x21, 0x2, + + /* U+6CBF "沿" */ + 0xc3, 0xf0, 0x44, 0x20, 0x8, 0x40, 0x10, 0x8c, + 0x41, 0xe9, 0x0, 0x0, 0x0, 0x3, 0xfc, 0x24, + 0x8, 0x88, 0x11, 0x10, 0x22, 0x20, 0x48, 0x7f, + 0x90, 0x81, 0x0, + + /* U+6CC4 "泄" */ + 0x40, 0x40, 0x48, 0x90, 0x51, 0x20, 0x22, 0x40, + 0xff, 0xe8, 0x89, 0x9, 0x12, 0x2, 0x24, 0x4, + 0x48, 0x48, 0x90, 0x91, 0xe1, 0x20, 0x4, 0x40, + 0x8, 0x7f, 0xd0, 0x0, 0x0, + + /* U+6CC5 "泅" */ + 0x67, 0xfe, 0x28, 0x84, 0x11, 0x8, 0x22, 0x14, + 0x44, 0x24, 0x8c, 0x41, 0x18, 0x82, 0x49, 0x24, + 0x8a, 0x4a, 0x14, 0x90, 0xa, 0x20, 0x14, 0x7f, + 0xe8, 0x80, 0x40, + + /* U+6CC9 "泉" */ + 0x1, 0x0, 0x3, 0x0, 0x3f, 0xfc, 0x20, 0x4, + 0x20, 0x4, 0x3f, 0xfc, 0x20, 0x4, 0x3f, 0xfc, + 0x0, 0x80, 0x0, 0xc2, 0x7c, 0xc4, 0x4, 0xb8, + 0x8, 0x90, 0x10, 0x8c, 0xe0, 0x83, 0x7, 0x80, + + /* U+6CCA "泊" */ + 0x0, 0x81, 0x82, 0x1, 0x7f, 0xc1, 0x1, 0x4, + 0x7, 0x10, 0x12, 0x40, 0x41, 0xff, 0x4, 0x4, + 0x90, 0x12, 0x40, 0x51, 0x1, 0x44, 0x5, 0x1f, + 0xf8, 0x40, 0x40, + + /* U+6CCC "泌" */ + 0x0, 0x0, 0xc0, 0x80, 0x40, 0xa0, 0x8, 0x40, + 0x10, 0x98, 0x22, 0x9, 0x44, 0x82, 0x91, 0x9, + 0x22, 0x52, 0x84, 0x86, 0x1, 0x8, 0x44, 0x30, + 0x89, 0xa1, 0x12, 0x3e, 0x0, + + /* U+6CD0 "泐" */ + 0x0, 0x21, 0x3c, 0x82, 0x92, 0x2, 0xff, 0x8a, + 0x25, 0x28, 0x92, 0xa2, 0x42, 0x89, 0x9, 0x24, + 0xa4, 0x94, 0x96, 0x53, 0xd1, 0x48, 0x45, 0x22, + 0x18, 0x93, 0x80, + + /* U+6CD3 "泓" */ + 0x6f, 0x88, 0x61, 0x20, 0x2, 0x42, 0x7c, 0x86, + 0x81, 0x1, 0x4, 0x2, 0xa, 0x7, 0x94, 0x21, + 0x24, 0x42, 0x88, 0x85, 0x11, 0xb, 0xf4, 0x30, + 0x29, 0xc0, 0x40, + + /* U+6CD4 "泔" */ + 0x2, 0x8, 0xc4, 0x10, 0xc8, 0x20, 0x7f, 0xf0, + 0x20, 0x98, 0x41, 0x8, 0x82, 0x1, 0x4, 0x3, + 0xf8, 0x44, 0x10, 0x88, 0x21, 0x10, 0x44, 0x20, + 0x88, 0x7f, 0x30, 0x82, 0x0, + + /* U+6CD5 "法" */ + 0x40, 0x40, 0xc0, 0x80, 0xc1, 0x0, 0x3f, 0xe0, + 0x4, 0x8, 0x8, 0x8, 0x10, 0x7, 0xff, 0x0, + 0x80, 0x41, 0x0, 0x84, 0x42, 0x10, 0x84, 0x40, + 0x89, 0xff, 0xa0, 0x0, 0x80, + + /* U+6CD6 "泖" */ + 0x0, 0x0, 0xcf, 0xbc, 0xd0, 0x48, 0x20, 0x90, + 0x49, 0x28, 0x92, 0x49, 0x24, 0x82, 0x49, 0x4, + 0x92, 0x4f, 0x24, 0x92, 0x49, 0x8, 0xf4, 0x11, + 0x8, 0x42, 0x13, 0x4, 0x0, + + /* U+6CD7 "泗" */ + 0x4f, 0xfc, 0xa4, 0x90, 0x92, 0x42, 0x49, 0xc9, + 0x24, 0xa4, 0x90, 0x92, 0x42, 0x89, 0x4a, 0x3d, + 0x30, 0x14, 0x80, 0x52, 0x1, 0x4f, 0xfe, 0x20, + 0x10, + + /* U+6CDB "泛" */ + 0x40, 0x0, 0x27, 0xfe, 0x10, 0x0, 0x0, 0x40, + 0x0, 0x40, 0x40, 0x0, 0x27, 0xfc, 0x0, 0xc, + 0x0, 0x18, 0x20, 0x30, 0x20, 0x40, 0x20, 0x80, + 0x43, 0x0, 0x4f, 0x0, 0x48, 0xfe, + + /* U+6CDE "泞" */ + 0xc0, 0x40, 0xc0, 0x80, 0x1f, 0xf0, 0x40, 0x28, + 0x80, 0x58, 0x0, 0x18, 0x0, 0x7, 0xff, 0x0, + 0x40, 0x40, 0x80, 0x81, 0x2, 0x2, 0x4, 0x4, + 0x8, 0x8, 0x20, 0xf0, 0x0, + + /* U+6CE0 "泠" */ + 0x40, 0x40, 0x40, 0xc0, 0x42, 0xc0, 0x8, 0xc0, + 0x20, 0xcd, 0x88, 0x48, 0x8, 0x0, 0x0, 0x7, + 0xfc, 0x40, 0x18, 0x80, 0x61, 0x18, 0x84, 0xa, + 0x8, 0xc, 0x10, 0x4, 0x0, + + /* U+6CE1 "泡" */ + 0x2, 0x0, 0x42, 0x0, 0x33, 0xfc, 0x4, 0x4, + 0x8, 0x4, 0xcf, 0xe4, 0x24, 0x24, 0x4, 0x24, + 0x27, 0xe4, 0x24, 0x4, 0x24, 0x4, 0x44, 0x7a, + 0x44, 0x2, 0x44, 0x2, 0x43, 0xfc, + + /* U+6CE2 "波" */ + 0x40, 0x20, 0xc0, 0x40, 0xdf, 0xf8, 0x21, 0x18, + 0x42, 0x38, 0x84, 0x9, 0xff, 0x2, 0x82, 0x4, + 0x84, 0x49, 0x11, 0x11, 0x42, 0x41, 0x84, 0x87, + 0xa, 0x31, 0xa5, 0x80, 0x80, + + /* U+6CE3 "泣" */ + 0x0, 0x40, 0xc0, 0x80, 0xc0, 0x80, 0x3f, 0xe0, + 0x0, 0x18, 0x41, 0x18, 0x82, 0x1, 0x4, 0x2, + 0x10, 0x42, 0x20, 0x84, 0x42, 0x9, 0x4, 0x2, + 0x9, 0xff, 0xd0, 0x0, 0x0, + + /* U+6CE5 "泥" */ + 0x47, 0xfc, 0x48, 0x8, 0x10, 0x10, 0x20, 0x2c, + 0x7f, 0xcc, 0x80, 0x1, 0x20, 0x2, 0x46, 0x24, + 0x90, 0x49, 0xc1, 0x22, 0xa, 0x44, 0x14, 0x88, + 0x32, 0x1f, 0x80, + + /* U+6CE8 "注" */ + 0x0, 0x81, 0x80, 0x80, 0x81, 0x0, 0x7f, 0xe0, + 0x4, 0x18, 0x8, 0x0, 0x10, 0x3, 0xfe, 0x0, + 0x40, 0x40, 0x81, 0x1, 0x2, 0x2, 0x4, 0x4, + 0x13, 0xff, 0xe0, 0x0, 0x0, + + /* U+6CEA "泪" */ + 0x0, 0x1, 0x1f, 0xf2, 0x40, 0x41, 0x1, 0x84, + 0x5, 0x1f, 0xf2, 0x40, 0x41, 0x1, 0x4, 0x4, + 0x9f, 0xf2, 0x40, 0x51, 0x1, 0x44, 0x5, 0x1f, + 0xf8, 0x40, 0x40, + + /* U+6CEB "泫" */ + 0xc0, 0x40, 0xc0, 0x80, 0xbf, 0xf8, 0x4, 0x8, + 0x10, 0x8, 0x41, 0x1, 0x4, 0x7, 0xf0, 0x0, + 0x40, 0x41, 0x1, 0x4, 0x62, 0x10, 0x44, 0x40, + 0x51, 0xff, 0xc0, 0x0, 0x0, + + /* U+6CEE "泮" */ + 0x0, 0x40, 0x80, 0x80, 0x91, 0x10, 0x12, 0x40, + 0x25, 0x18, 0x8, 0x19, 0xff, 0x0, 0x20, 0x0, + 0x40, 0x5f, 0xfd, 0x1, 0x2, 0x2, 0x4, 0x4, + 0x10, 0x8, 0x20, 0x10, 0x0, + + /* U+6CEF "泯" */ + 0x67, 0xfe, 0x34, 0x2, 0x4, 0x2, 0x87, 0xfe, + 0x64, 0x20, 0x24, 0x20, 0x4, 0x20, 0x7, 0xfe, + 0x24, 0x20, 0x24, 0x20, 0x44, 0x11, 0x44, 0x91, + 0x47, 0x9, 0x84, 0xe, + + /* U+6CF0 "泰" */ + 0x1, 0x0, 0x2, 0x1, 0xff, 0xf0, 0x8, 0x0, + 0x10, 0xf, 0xff, 0x80, 0x80, 0x7f, 0xff, 0xc, + 0x20, 0x32, 0x20, 0xb4, 0xb6, 0x8, 0x10, 0x76, + 0x7, 0x23, 0x0, 0x41, 0x3, 0x80, + + /* U+6CF1 "泱" */ + 0x0, 0x40, 0xc0, 0x80, 0xc1, 0x0, 0x3f, 0xc0, + 0x44, 0x98, 0x89, 0x9, 0x12, 0x2, 0x24, 0xf, + 0xfe, 0x40, 0x80, 0x82, 0x82, 0xd, 0x4, 0x31, + 0x8, 0x81, 0x82, 0x0, 0x80, + + /* U+6CF3 "泳" */ + 0x1, 0x1, 0x83, 0x3, 0x0, 0x3, 0xe0, 0x0, + 0x82, 0x2, 0x26, 0xed, 0x0, 0xb8, 0x2, 0xa0, + 0x92, 0x84, 0x49, 0x12, 0x22, 0x58, 0x86, 0x42, + 0x8, 0x38, 0x0, + + /* U+6CF5 "泵" */ + 0xff, 0xfc, 0x20, 0x1, 0xff, 0x18, 0x4, 0xa0, + 0x10, 0x7f, 0xc0, 0x10, 0x0, 0x40, 0xf9, 0x98, + 0x26, 0x81, 0x16, 0x8, 0x44, 0xc1, 0xc, 0x38, + 0x0, + + /* U+6CF6 "泶" */ + 0x1, 0x0, 0x10, 0x88, 0x18, 0x90, 0x0, 0x0, + 0x7f, 0xfe, 0x40, 0x2, 0x40, 0x82, 0x7e, 0x8c, + 0x4, 0xd8, 0x4, 0xa0, 0x8, 0x90, 0x10, 0x88, + 0x20, 0x84, 0x40, 0x82, 0x7, 0x80, + + /* U+6CF7 "泷" */ + 0x42, 0x20, 0x44, 0x30, 0x8, 0x20, 0x7f, 0xf8, + 0x24, 0x8, 0x48, 0x8, 0x91, 0x1, 0x24, 0x2, + 0x50, 0x48, 0xe0, 0x91, 0x82, 0x26, 0x14, 0x9c, + 0x29, 0x48, 0x64, 0x1f, 0x0, + + /* U+6CF8 "泸" */ + 0x40, 0x40, 0xc0, 0x80, 0xc1, 0xf8, 0x2, 0x8, + 0x7f, 0xc8, 0x80, 0x81, 0x1, 0x2, 0x2, 0x7, + 0xfc, 0x48, 0x9, 0x10, 0x2, 0x60, 0x4, 0x80, + 0x13, 0x0, 0x4, 0x0, 0x0, + + /* U+6CFA "泺" */ + 0x0, 0x0, 0x8f, 0xf8, 0x90, 0x0, 0x22, 0x0, + 0x44, 0x19, 0x8, 0x2, 0x10, 0x7, 0xfe, 0x0, + 0x40, 0x40, 0x91, 0x11, 0x22, 0x62, 0x24, 0x84, + 0x52, 0x8, 0x20, 0xf0, 0x0, + + /* U+6CFB "泻" */ + 0x0, 0x0, 0xdf, 0xfc, 0xa8, 0x8, 0x50, 0x18, + 0x3f, 0x8c, 0x40, 0x8, 0x80, 0x1, 0xfe, 0x20, + 0x4, 0x40, 0x8, 0xbf, 0xd1, 0x0, 0x24, 0x0, + 0x48, 0x1, 0x90, 0x3e, 0x0, + + /* U+6CFC "泼" */ + 0x0, 0x80, 0x89, 0x30, 0xa2, 0x10, 0x44, 0x0, + 0xff, 0xf0, 0x20, 0x18, 0x40, 0x1, 0xfe, 0x3, + 0x4, 0xa, 0x11, 0x12, 0x22, 0x46, 0x85, 0x6, + 0xa, 0x1b, 0x21, 0xc1, 0x80, 0x0, + + /* U+6CFD "泽" */ + 0x0, 0x0, 0x8f, 0xf8, 0xc8, 0x10, 0x8, 0x40, + 0xb, 0x18, 0x1c, 0xb, 0xc7, 0x80, 0x20, 0x7, + 0xfc, 0x40, 0x80, 0x81, 0x2, 0x7f, 0xf4, 0x4, + 0x8, 0x8, 0x0, 0x10, 0x0, + + /* U+6CFE "泾" */ + 0x40, 0x0, 0x4f, 0xf8, 0x40, 0x20, 0x0, 0x80, + 0x2, 0x8, 0x1e, 0x8, 0xc3, 0x6, 0x3, 0x0, + 0x0, 0x4f, 0xf8, 0x81, 0x1, 0x2, 0x4, 0x4, + 0x9, 0xff, 0xd0, 0x0, 0x0, + + /* U+6D01 "洁" */ + 0x40, 0x20, 0x30, 0x20, 0x17, 0xff, 0x0, 0x20, + 0x0, 0x20, 0x40, 0x20, 0x23, 0xfe, 0x0, 0x0, + 0x0, 0x0, 0x13, 0xfe, 0x22, 0x2, 0x22, 0x2, + 0x22, 0x2, 0x43, 0xfe, 0x42, 0x2, + + /* U+6D04 "洄" */ + 0x0, 0x1, 0x3f, 0xf2, 0x80, 0x42, 0x1, 0x9, + 0xf7, 0x24, 0x50, 0x91, 0x42, 0x45, 0x49, 0x15, + 0x27, 0xd4, 0x80, 0x52, 0x1, 0x88, 0x6, 0x3f, + 0xf8, 0x80, 0x40, + + /* U+6D07 "洇" */ + 0x0, 0x0, 0x4f, 0xfc, 0x50, 0x8, 0x22, 0x10, + 0x44, 0x28, 0xbf, 0x49, 0x10, 0x82, 0x21, 0x4, + 0x62, 0x49, 0x24, 0x94, 0x29, 0x20, 0x14, 0x40, + 0x28, 0xff, 0xd1, 0x0, 0x80, + + /* U+6D0B "洋" */ + 0x2, 0x8, 0x82, 0x20, 0xc4, 0x40, 0x7f, 0xf0, + 0x4, 0x18, 0x8, 0x1, 0xff, 0x0, 0x20, 0x0, + 0x40, 0x40, 0x81, 0x3f, 0xfa, 0x2, 0x4, 0x4, + 0x10, 0x8, 0x20, 0x10, 0x0, + + /* U+6D0C "洌" */ + 0x0, 0x2, 0xdf, 0x84, 0x8, 0x48, 0x20, 0x98, + 0x79, 0x28, 0x92, 0x42, 0x24, 0x87, 0x49, 0x12, + 0x92, 0x82, 0x25, 0x4, 0x4a, 0x10, 0x14, 0x20, + 0x30, 0x80, 0x66, 0x7, 0x80, + + /* U+6D0E "洎" */ + 0x40, 0x40, 0x40, 0x80, 0x5f, 0xf8, 0x20, 0x10, + 0x40, 0x38, 0x80, 0x49, 0xff, 0x82, 0x1, 0x4, + 0x2, 0x4f, 0xfc, 0x90, 0x9, 0x20, 0x14, 0x40, + 0x28, 0xff, 0xd1, 0x0, 0x80, + + /* U+6D12 "洒" */ + 0x4f, 0xfe, 0x62, 0x40, 0x4, 0x84, 0x7f, 0xe4, + 0x92, 0x45, 0x24, 0x82, 0x49, 0x4, 0x92, 0x2a, + 0x1c, 0x50, 0x9, 0x20, 0x12, 0x40, 0x24, 0xff, + 0xc9, 0x0, 0x80, + + /* U+6D17 "洗" */ + 0x40, 0x40, 0xc4, 0x80, 0x49, 0x0, 0x3f, 0xe0, + 0x44, 0x9, 0x8, 0x8, 0x10, 0x7, 0xff, 0x1, + 0x20, 0x42, 0x40, 0x84, 0x82, 0x11, 0x4, 0x22, + 0x28, 0x84, 0x52, 0x7, 0x80, 0x0, + + /* U+6D19 "洙" */ + 0x40, 0x40, 0x44, 0x80, 0x11, 0x0, 0x3f, 0xe8, + 0x44, 0x9, 0x8, 0x8, 0x10, 0x7, 0xff, 0x0, + 0xc0, 0x41, 0xc0, 0x85, 0x42, 0x12, 0x44, 0xc4, + 0x68, 0x8, 0x20, 0x10, 0x0, + + /* U+6D1A "洚" */ + 0x60, 0x80, 0x31, 0xfc, 0x3, 0x4, 0x6, 0x88, + 0x4, 0x50, 0x40, 0x70, 0x37, 0x8f, 0x0, 0x20, + 0x7, 0xfe, 0x10, 0x20, 0x24, 0x20, 0x24, 0x20, + 0x27, 0xfe, 0x40, 0x20, 0x40, 0x20, + + /* U+6D1B "洛" */ + 0x0, 0x80, 0x61, 0x0, 0x11, 0xfc, 0x13, 0x4, + 0x7, 0x8, 0xc, 0x90, 0x60, 0x60, 0x20, 0xf0, + 0x7, 0xe, 0x0, 0x0, 0x13, 0xfc, 0x22, 0x4, + 0x22, 0x4, 0x22, 0x4, 0x43, 0xfc, 0x42, 0x4, + + /* U+6D1E "洞" */ + 0x0, 0x1, 0x3f, 0xf2, 0x80, 0x42, 0xfd, 0x88, + 0x5, 0xa0, 0x10, 0x9f, 0x42, 0x45, 0x9, 0x15, + 0x24, 0x54, 0x91, 0x52, 0x7d, 0x48, 0x6, 0x20, + 0x18, 0x83, 0xc0, + + /* U+6D25 "津" */ + 0x80, 0x80, 0x9f, 0xf0, 0x82, 0x20, 0x4, 0x41, + 0xff, 0xf8, 0x11, 0x3, 0xfe, 0x0, 0x40, 0x40, + 0x80, 0x9f, 0xf9, 0x2, 0x2, 0x4, 0x9, 0xff, + 0xf0, 0x10, 0x20, 0x20, 0x0, + + /* U+6D27 "洧" */ + 0x0, 0x40, 0x20, 0x80, 0x17, 0xff, 0x1, 0x80, + 0x1, 0x0, 0x43, 0xfe, 0x22, 0x2, 0x6, 0x2, + 0xb, 0xfe, 0x12, 0x2, 0x23, 0xfe, 0x22, 0x2, + 0x22, 0x2, 0x42, 0x2, 0x42, 0x1c, + + /* U+6D2A "洪" */ + 0x42, 0x10, 0x44, 0x20, 0x48, 0x40, 0x3f, 0xe0, + 0x21, 0x8, 0x42, 0x8, 0x84, 0x1, 0x8, 0x2, + 0x10, 0x5f, 0xfc, 0x80, 0x2, 0x8, 0x84, 0x20, + 0x88, 0x80, 0x92, 0x0, 0x80, + + /* U+6D2B "洫" */ + 0x40, 0x80, 0x41, 0x0, 0x3f, 0xf0, 0x49, 0x28, + 0x92, 0x49, 0x24, 0x8a, 0x49, 0x4, 0x92, 0x9, + 0x24, 0x52, 0x49, 0x24, 0x92, 0x49, 0x24, 0x92, + 0x53, 0xff, 0xc0, 0x0, 0x0, + + /* U+6D2E "洮" */ + 0x1, 0x20, 0x82, 0x40, 0xc4, 0x80, 0x49, 0x20, + 0x52, 0x98, 0xa5, 0x18, 0x48, 0x0, 0x90, 0x3, + 0x38, 0x5a, 0x4d, 0x4, 0x82, 0x11, 0x4, 0x22, + 0x28, 0x84, 0x66, 0xf, 0x0, + + /* U+6D31 "洱" */ + 0x80, 0x1, 0xbf, 0xf2, 0x41, 0x1, 0x4, 0x87, + 0xf3, 0x10, 0x42, 0x41, 0x1, 0x4, 0x7, 0xf0, + 0x90, 0x42, 0x41, 0x11, 0x1f, 0x5f, 0x92, 0x0, + 0x48, 0x1, 0x0, + + /* U+6D32 "洲" */ + 0x42, 0x22, 0x64, 0x44, 0x8, 0x88, 0x11, 0x14, + 0xaa, 0xa5, 0x55, 0x44, 0xaa, 0x81, 0x11, 0x22, + 0x22, 0x84, 0x45, 0x10, 0x8a, 0x21, 0x14, 0x82, + 0x32, 0x0, 0x40, + + /* U+6D33 "洳" */ + 0x42, 0x0, 0x48, 0x7c, 0x10, 0x88, 0x7d, 0x10, + 0x4a, 0x28, 0x94, 0x49, 0x28, 0x84, 0x51, 0x9, + 0x22, 0x4a, 0x44, 0x8c, 0x8a, 0x19, 0x14, 0x4b, + 0xe9, 0x84, 0x56, 0x8, 0x80, + + /* U+6D35 "洵" */ + 0x82, 0x1, 0x8, 0x2, 0x7f, 0xc3, 0x1, 0x8, + 0x7, 0x1f, 0x96, 0x42, 0x41, 0x9, 0x7, 0xe4, + 0x90, 0x94, 0x42, 0x51, 0xf9, 0x40, 0x6, 0x0, + 0x18, 0x7, 0x80, + + /* U+6D39 "洹" */ + 0x0, 0x0, 0xcf, 0xfc, 0xc0, 0x0, 0x0, 0x0, + 0x7f, 0xd8, 0x80, 0x89, 0x1, 0x3, 0xfe, 0x4, + 0x4, 0x48, 0x8, 0x9f, 0xf2, 0x0, 0x4, 0x0, + 0x9, 0xff, 0xc0, 0x0, 0x0, + + /* U+6D3B "活" */ + 0xc0, 0x0, 0xcf, 0xf0, 0x81, 0x0, 0x2, 0x0, + 0x4, 0x19, 0xff, 0xd8, 0x10, 0x0, 0x20, 0x27, + 0xfc, 0x48, 0x9, 0x10, 0x12, 0x20, 0x24, 0x40, + 0x50, 0xff, 0xa1, 0x1, 0x0, + + /* U+6D3C "洼" */ + 0x40, 0x40, 0x40, 0x80, 0x1f, 0xf0, 0x2, 0x8, + 0x4, 0x8, 0x8, 0xb, 0xff, 0x80, 0x20, 0x20, + 0x40, 0x4f, 0xf8, 0x81, 0x2, 0x2, 0x4, 0x4, + 0x9, 0xff, 0xc0, 0x0, 0x0, + + /* U+6D3D "洽" */ + 0x0, 0x40, 0x40, 0x40, 0x20, 0xa0, 0x1, 0x10, + 0x2, 0xc, 0x8c, 0x3, 0x43, 0xf8, 0x20, 0x0, + 0x0, 0x0, 0x27, 0xfc, 0x24, 0x4, 0x24, 0x4, + 0x44, 0x4, 0x44, 0x4, 0x47, 0xfc, 0xc4, 0x4, + + /* U+6D3E "派" */ + 0x40, 0x0, 0x4f, 0xf8, 0x50, 0x0, 0x20, 0x8, + 0x4f, 0xc8, 0x94, 0x9, 0x28, 0x2, 0x56, 0x4, + 0xb0, 0x49, 0x20, 0x92, 0x42, 0x24, 0x44, 0x88, + 0x49, 0x1c, 0x44, 0x0, 0x0, + + /* U+6D41 "流" */ + 0x0, 0x40, 0x60, 0x40, 0x2f, 0xfe, 0x0, 0x80, + 0x81, 0x10, 0x46, 0x8, 0x2f, 0xfc, 0x0, 0x2, + 0x0, 0x0, 0x4, 0x48, 0x24, 0x48, 0x44, 0x48, + 0x44, 0x4a, 0x4c, 0x4a, 0x98, 0x4e, + + /* U+6D43 "浃" */ + 0x40, 0x40, 0x40, 0x80, 0x1f, 0xf0, 0x2, 0x8, + 0x44, 0x48, 0x49, 0x8, 0x94, 0x0, 0x20, 0xf, + 0xfe, 0x41, 0x80, 0x82, 0x82, 0x9, 0x4, 0x21, + 0x8, 0x81, 0xa2, 0x0, 0x80, + + /* U+6D45 "浅" */ + 0x40, 0x80, 0xc1, 0x30, 0x42, 0x20, 0x5, 0xe0, + 0x7c, 0x8, 0x10, 0x8, 0x23, 0x87, 0xf8, 0x0, + 0x40, 0x40, 0x90, 0x81, 0xc2, 0x7, 0x14, 0x32, + 0x29, 0x82, 0x40, 0x3, 0x0, + + /* U+6D46 "浆" */ + 0x8, 0x40, 0x48, 0xfe, 0x29, 0x6, 0xa, 0x8c, + 0x18, 0x10, 0x68, 0xe0, 0xb, 0x0, 0x0, 0x0, + 0x1, 0x4, 0x7d, 0x98, 0xd, 0x60, 0x19, 0x20, + 0x31, 0x18, 0xc1, 0x6, 0xf, 0x0, + + /* U+6D47 "浇" */ + 0x41, 0x0, 0xc2, 0x18, 0xf, 0xc0, 0x64, 0x68, + 0x9, 0x8, 0xc, 0x48, 0xc8, 0x86, 0xe, 0x0, + 0x0, 0x5f, 0xfc, 0x84, 0x82, 0x9, 0x4, 0x22, + 0x28, 0xc4, 0x67, 0x7, 0x80, + + /* U+6D48 "浈" */ + 0x0, 0x40, 0x80, 0x80, 0x81, 0x1, 0x83, 0xf1, + 0x4, 0x10, 0xff, 0x99, 0x1, 0x12, 0x22, 0x4, + 0x44, 0x48, 0x88, 0x91, 0x11, 0x22, 0x24, 0xb, + 0x8, 0x31, 0x33, 0x81, 0x80, 0x0, + + /* U+6D4A "浊" */ + 0x0, 0x40, 0xc0, 0x80, 0xc1, 0x0, 0x3f, 0xe0, + 0x44, 0x58, 0x88, 0x89, 0x11, 0x2, 0x22, 0x7, + 0xfc, 0x48, 0x80, 0x81, 0x2, 0x2, 0x44, 0x4, + 0x49, 0xff, 0xe2, 0x0, 0x80, + + /* U+6D4B "测" */ + 0x40, 0x2, 0xdf, 0xa4, 0x21, 0x48, 0x52, 0x98, + 0xa5, 0x29, 0x4a, 0x42, 0x94, 0x85, 0x29, 0xa, + 0x52, 0x54, 0xa4, 0x90, 0x4a, 0x38, 0x14, 0x48, + 0x29, 0x8, 0x64, 0x7, 0x80, + + /* U+6D4D "浍" */ + 0x0, 0x40, 0x40, 0xc0, 0x30, 0xa0, 0x3, 0x10, + 0x6, 0xc, 0xd8, 0x2, 0x63, 0xf8, 0x0, 0x0, + 0x0, 0x0, 0x2f, 0xfe, 0x21, 0x80, 0x42, 0x10, + 0x44, 0x8, 0x4f, 0xfc, 0x80, 0x2, + + /* U+6D4E "济" */ + 0x40, 0x40, 0x40, 0x80, 0x3f, 0xf8, 0x10, 0x48, + 0x21, 0x18, 0x26, 0x8, 0x30, 0x1, 0xdc, 0x1c, + 0x6, 0x44, 0x21, 0x8, 0x42, 0x10, 0x84, 0x21, + 0x9, 0x82, 0x26, 0x4, 0x0, + + /* U+6D4F "浏" */ + 0x42, 0x2, 0xc2, 0x4, 0x80, 0x48, 0x7e, 0x90, + 0x9, 0x29, 0x12, 0x49, 0x24, 0x81, 0x89, 0x23, + 0x12, 0x42, 0x24, 0x8c, 0x49, 0x24, 0x14, 0x48, + 0x29, 0x0, 0x50, 0x7, 0x80, + + /* U+6D51 "浑" */ + 0x0, 0x0, 0x9f, 0xfc, 0xa0, 0x8, 0x4c, 0x19, + 0xff, 0xe8, 0x40, 0x1, 0x10, 0x2, 0x20, 0xf, + 0xfc, 0x40, 0x81, 0x1, 0x2, 0xff, 0xf4, 0x4, + 0x10, 0x8, 0x20, 0x10, 0x0, + + /* U+6D52 "浒" */ + 0x88, 0x40, 0x88, 0x80, 0x93, 0xf8, 0x4, 0x80, + 0x9, 0x1b, 0xa2, 0x19, 0x4, 0x2, 0x8, 0x5, + 0xfe, 0x8, 0x21, 0x10, 0x42, 0x28, 0x84, 0x61, + 0x10, 0x82, 0x20, 0x4, 0x0, + + /* U+6D53 "浓" */ + 0x40, 0x20, 0x20, 0x40, 0x17, 0xfe, 0x4, 0x42, + 0x4, 0x42, 0x40, 0xc0, 0x20, 0xc6, 0x1, 0x2c, + 0x3, 0x30, 0x25, 0x10, 0x29, 0x8, 0x21, 0x4, + 0x41, 0x22, 0x41, 0xc0, 0x41, 0x0, + + /* U+6D54 "浔" */ + 0x67, 0xfc, 0x20, 0x8, 0xf, 0xf0, 0x0, 0x24, + 0x0, 0x44, 0xff, 0x80, 0x2, 0x0, 0x4, 0x2f, + 0xfe, 0x40, 0x11, 0xc, 0x22, 0x4, 0x44, 0x0, + 0x88, 0xe, 0x0, + + /* U+6D59 "浙" */ + 0x2, 0x0, 0x22, 0x3e, 0x12, 0x20, 0xf, 0xa0, + 0x2, 0x20, 0x42, 0x20, 0x22, 0x3f, 0x3, 0x24, + 0xe, 0x24, 0x22, 0x44, 0x22, 0x44, 0x22, 0x44, + 0x42, 0x44, 0x42, 0x44, 0x4c, 0x84, + + /* U+6D5A "浚" */ + 0x0, 0x40, 0x60, 0x80, 0x11, 0x18, 0x2, 0xc, + 0x7, 0xfc, 0x40, 0x2, 0x21, 0x18, 0x6, 0x6, + 0x0, 0x80, 0x1, 0xfc, 0x13, 0x8, 0x25, 0x98, + 0x20, 0xf0, 0x20, 0x60, 0x43, 0x9c, 0x4c, 0x2, + + /* U+6D5C "浜" */ + 0x40, 0x4, 0x47, 0xf0, 0x48, 0x0, 0x10, 0x0, + 0x20, 0x8, 0x7f, 0xc8, 0x84, 0x1, 0x8, 0x22, + 0x10, 0x5f, 0xfc, 0x80, 0x2, 0x18, 0x84, 0x60, + 0xc9, 0x80, 0xc0, 0x0, 0x0, + + /* U+6D5E "浞" */ + 0x0, 0x0, 0xcf, 0xf8, 0xd0, 0x10, 0x20, 0x28, + 0x40, 0x48, 0xff, 0x88, 0x10, 0x0, 0x20, 0x4, + 0x40, 0x48, 0xfc, 0x91, 0x2, 0x32, 0x4, 0x94, + 0xb, 0x1f, 0xc4, 0x0, 0x0, + + /* U+6D60 "浠" */ + 0x0, 0x0, 0xce, 0x10, 0xc3, 0xc0, 0xd, 0xc0, + 0x68, 0x48, 0x10, 0xb, 0xff, 0x80, 0x80, 0x2, + 0x0, 0x4f, 0xf9, 0x31, 0x12, 0xa2, 0x24, 0x44, + 0x48, 0x88, 0x90, 0x17, 0x40, 0x20, + + /* U+6D63 "浣" */ + 0x0, 0x40, 0xc0, 0x80, 0xff, 0xf8, 0x40, 0x18, + 0x0, 0x8, 0x7f, 0x8, 0x0, 0x0, 0x0, 0xf, + 0xfe, 0x42, 0x40, 0x84, 0x82, 0x9, 0x4, 0x22, + 0x28, 0x82, 0x66, 0x7, 0x80, + + /* U+6D66 "浦" */ + 0x40, 0x88, 0x5f, 0xfc, 0x2, 0x0, 0x4, 0xc, + 0x7f, 0xc5, 0x10, 0x82, 0x21, 0x3, 0xfe, 0x28, + 0x84, 0x8f, 0xf9, 0x22, 0x12, 0x44, 0x24, 0x88, + 0x51, 0x13, 0x80, + + /* U+6D69 "浩" */ + 0x42, 0x40, 0x44, 0x80, 0xf, 0xf0, 0x22, 0x8, + 0x84, 0xc, 0x8, 0xb, 0xff, 0x80, 0x0, 0x0, + 0x0, 0x4f, 0xf8, 0x90, 0x11, 0x20, 0x24, 0x40, + 0x48, 0xff, 0x91, 0x1, 0x0, + + /* U+6D6A "浪" */ + 0x0, 0x40, 0xc0, 0x80, 0xdf, 0xf0, 0x20, 0x20, + 0x40, 0x58, 0xff, 0x99, 0x1, 0x2, 0x2, 0x7, + 0xfc, 0x49, 0x0, 0x91, 0x11, 0x22, 0xc4, 0x42, + 0x8, 0x93, 0x11, 0xc1, 0x80, 0x0, + + /* U+6D6E "浮" */ + 0x0, 0x0, 0x27, 0xfe, 0x10, 0x40, 0x4, 0x44, + 0x2, 0x24, 0x40, 0x0, 0x37, 0xfe, 0x0, 0xc, + 0x0, 0x30, 0x20, 0x20, 0x2f, 0xff, 0x20, 0x20, + 0x20, 0x20, 0x40, 0x20, 0x43, 0xc0, + + /* U+6D6F "浯" */ + 0x47, 0xfc, 0x61, 0x0, 0x4, 0x0, 0x3f, 0xc8, + 0x10, 0x88, 0x21, 0x8, 0x42, 0x7, 0xff, 0x0, + 0x0, 0x47, 0xf8, 0x88, 0x12, 0x10, 0x24, 0x20, + 0x48, 0x7f, 0xa0, 0x81, 0x0, + + /* U+6D74 "浴" */ + 0x41, 0x20, 0x44, 0x30, 0x11, 0x10, 0x46, 0x8, + 0x16, 0xc, 0x66, 0x1, 0x86, 0xc, 0x2, 0x27, + 0xf8, 0x88, 0x11, 0x10, 0x22, 0x20, 0x44, 0x7f, + 0x90, 0x81, 0x0, + + /* U+6D77 "海" */ + 0x2, 0x1, 0x88, 0x1, 0x9f, 0xf9, 0x40, 0x0, + 0x80, 0x2, 0xff, 0x21, 0x2, 0x22, 0x64, 0x4, + 0x8, 0x3f, 0xfc, 0x20, 0x22, 0x44, 0x44, 0x84, + 0x89, 0xff, 0xa0, 0x2, 0x40, 0x38, + + /* U+6D78 "浸" */ + 0x67, 0xfc, 0x60, 0x8, 0xf, 0xf0, 0x0, 0x24, + 0x7f, 0xc4, 0x0, 0x3, 0xff, 0x84, 0x1, 0x27, + 0xfc, 0x42, 0x10, 0x84, 0x42, 0x7, 0x4, 0x1b, + 0x9, 0xc1, 0xc0, + + /* U+6D7C "浼" */ + 0x0, 0x0, 0x41, 0x0, 0x23, 0xf8, 0x12, 0x18, + 0x4, 0x20, 0xc7, 0xfe, 0x24, 0x42, 0x4, 0x42, + 0x4, 0x42, 0x27, 0xfe, 0x20, 0x80, 0x20, 0xa0, + 0x21, 0x20, 0x43, 0x21, 0x46, 0x22, 0x58, 0x3e, + + /* U+6D82 "涂" */ + 0x0, 0x80, 0xc1, 0x80, 0x84, 0x80, 0x10, 0x80, + 0x40, 0xdb, 0x7e, 0x18, 0x20, 0x0, 0x40, 0xf, + 0xfc, 0x81, 0x1, 0x12, 0x42, 0x24, 0x44, 0x88, + 0x52, 0x10, 0xa0, 0xe0, 0x0, + + /* U+6D85 "涅" */ + 0x47, 0xfc, 0x70, 0x8, 0x1f, 0xf4, 0x40, 0x24, + 0x80, 0x40, 0xff, 0x80, 0x0, 0x0, 0x20, 0x40, + 0x40, 0x9f, 0xf9, 0x1, 0x4, 0x2, 0x9, 0xff, + 0xf0, 0x0, 0x0, + + /* U+6D88 "消" */ + 0x44, 0x44, 0x89, 0x30, 0x24, 0x80, 0x10, 0x87, + 0xfd, 0x90, 0x12, 0x40, 0x41, 0xff, 0x24, 0x4, + 0x90, 0x14, 0x7f, 0xd1, 0x1, 0x44, 0x5, 0x10, + 0x18, 0x43, 0xc0, + + /* U+6D89 "涉" */ + 0x0, 0x20, 0xc0, 0x40, 0x8, 0xf8, 0x11, 0x8, + 0x22, 0x8, 0x44, 0xb, 0xff, 0x80, 0x0, 0x0, + 0x20, 0x44, 0x48, 0x90, 0xa2, 0x60, 0x84, 0x6, + 0x8, 0x30, 0x23, 0x0, 0x0, + + /* U+6D8C "涌" */ + 0xcf, 0xfc, 0x80, 0x20, 0x23, 0x0, 0x70, 0x8f, + 0xfd, 0x21, 0x10, 0x84, 0x43, 0xff, 0x48, 0x45, + 0x21, 0x14, 0xff, 0xd2, 0x11, 0x88, 0x46, 0x21, + 0x18, 0x85, 0xc0, + + /* U+6D8E "涎" */ + 0x0, 0x0, 0x4e, 0x7e, 0x22, 0x10, 0x4, 0x10, + 0x4, 0x10, 0xc8, 0x50, 0x2e, 0x4c, 0x2, 0x50, + 0xa, 0x50, 0x2a, 0x50, 0x4c, 0x50, 0x44, 0x7e, + 0x46, 0x0, 0x89, 0xfe, 0x10, 0x0, + + /* U+6D91 "涑" */ + 0x0, 0x40, 0xc0, 0x80, 0xbf, 0xf8, 0x2, 0x0, + 0x4, 0x10, 0xff, 0x9a, 0x11, 0x4, 0x22, 0x7, + 0xfc, 0x1, 0xc0, 0x85, 0x42, 0x12, 0xc4, 0x44, + 0x4b, 0x8, 0x60, 0x10, 0x0, + + /* U+6D93 "涓" */ + 0x0, 0x0, 0xc7, 0xf8, 0x48, 0x10, 0x1f, 0xec, + 0x0, 0x8, 0x0, 0x9, 0xff, 0x82, 0x1, 0x7, + 0xfe, 0x48, 0x4, 0x90, 0xa, 0x3f, 0xf4, 0x40, + 0x28, 0x80, 0x51, 0x7, 0x0, + + /* U+6D94 "涔" */ + 0x0, 0x40, 0xc8, 0x88, 0x51, 0x10, 0x3f, 0xe0, + 0x4, 0x18, 0x1c, 0x8, 0x4c, 0x3, 0x44, 0x8, + 0x46, 0x40, 0x0, 0x9f, 0xe1, 0x0, 0x44, 0x1, + 0x8, 0x4, 0x10, 0x10, 0x0, + + /* U+6D95 "涕" */ + 0x43, 0x10, 0xc3, 0x20, 0x7f, 0xf0, 0x2, 0x28, + 0x4, 0x48, 0xff, 0x89, 0x10, 0x4, 0x20, 0xf, + 0xfe, 0x40, 0x84, 0x83, 0xa, 0x1a, 0x14, 0x45, + 0xc9, 0x8, 0x20, 0x10, 0x0, + + /* U+6D9B "涛" */ + 0x0, 0x40, 0x80, 0x80, 0xdf, 0xf8, 0x82, 0x0, + 0x4, 0x0, 0xff, 0x90, 0x20, 0x10, 0x40, 0xf, + 0xfe, 0x2, 0x10, 0x87, 0xf9, 0x10, 0x42, 0x58, + 0x89, 0x19, 0x10, 0x2, 0x20, 0x1c, + + /* U+6D9D "涝" */ + 0x1, 0x10, 0x82, 0x20, 0xbf, 0xf8, 0x8, 0x80, + 0x0, 0x19, 0xff, 0xc2, 0x20, 0x84, 0x41, 0xf, + 0xfc, 0x82, 0x19, 0x4, 0x32, 0x8, 0x64, 0x20, + 0x90, 0x81, 0x26, 0x3e, 0x0, + + /* U+6D9E "涞" */ + 0x0, 0x40, 0x80, 0x80, 0xdf, 0xf0, 0x2, 0x0, + 0x44, 0x58, 0x4b, 0x8, 0x10, 0x7, 0xff, 0x0, + 0xe0, 0x42, 0xc0, 0x85, 0x42, 0x12, 0x44, 0xc4, + 0x68, 0x8, 0x20, 0x10, 0x0, + + /* U+6D9F "涟" */ + 0x8, 0x20, 0xd0, 0x40, 0x93, 0xf8, 0x2, 0x0, + 0x5, 0x8, 0x12, 0xb, 0x24, 0x2, 0x7e, 0x4, + 0x10, 0x88, 0x21, 0x17, 0xfa, 0x20, 0x84, 0xc1, + 0xb, 0x40, 0x24, 0x7f, 0x80, + + /* U+6DA0 "涠" */ + 0x4f, 0xfe, 0x51, 0x4, 0x2f, 0xec, 0x44, 0x1c, + 0x88, 0x2d, 0x7e, 0x42, 0x20, 0x85, 0xfd, 0x28, + 0x8a, 0x51, 0x15, 0x22, 0xea, 0x44, 0x14, 0xff, + 0xf1, 0x0, 0x40, + + /* U+6DA1 "涡" */ + 0xc7, 0xf8, 0x90, 0x20, 0x40, 0x81, 0x2, 0x87, + 0xfb, 0x1, 0x2, 0x4, 0x3, 0xff, 0x8, 0x85, + 0x23, 0x14, 0x8a, 0x52, 0xc5, 0x48, 0x6, 0x20, + 0x18, 0x83, 0xc0, + + /* U+6DA3 "涣" */ + 0x0, 0x80, 0x21, 0x0, 0x11, 0xf8, 0x2, 0x8, + 0x6, 0x10, 0xc, 0x30, 0x47, 0xfe, 0x24, 0x22, + 0x4, 0x22, 0x4, 0x42, 0x2f, 0xff, 0x20, 0x60, + 0x40, 0xd0, 0x41, 0x88, 0x46, 0x6, 0x8, 0x0, + + /* U+6DA4 "涤" */ + 0x1, 0x1, 0x82, 0x1, 0x8f, 0xe1, 0x30, 0x40, + 0x91, 0x1, 0x14, 0x30, 0x38, 0x31, 0x8c, 0x1c, + 0x46, 0x0, 0x80, 0xbf, 0xf2, 0x2, 0x4, 0x45, + 0x9, 0x89, 0x26, 0x11, 0xc1, 0xc0, + + /* U+6DA6 "润" */ + 0x4, 0x1, 0x13, 0xf2, 0x0, 0x42, 0x1, 0xb, + 0xf6, 0x22, 0x14, 0x88, 0x42, 0xfd, 0x8, 0x85, + 0x22, 0x14, 0x88, 0x52, 0xfd, 0x48, 0x6, 0x20, + 0x18, 0x83, 0xc0, + + /* U+6DA7 "涧" */ + 0x4, 0x0, 0x84, 0xfc, 0x80, 0x8, 0x40, 0x10, + 0x9f, 0x39, 0x22, 0x42, 0x44, 0x84, 0xf9, 0x9, + 0x12, 0x52, 0x25, 0x24, 0x4a, 0x4f, 0x94, 0x80, + 0x31, 0x0, 0x62, 0x7, 0x80, + + /* U+6DA8 "涨" */ + 0x0, 0x40, 0x4e, 0x40, 0x22, 0x44, 0x2, 0x48, + 0xce, 0x50, 0x68, 0x40, 0x8, 0xfe, 0x8, 0x50, + 0xe, 0x48, 0x22, 0x48, 0x42, 0x48, 0x42, 0x44, + 0x42, 0x44, 0x42, 0x72, 0x9c, 0x40, + + /* U+6DA9 "涩" */ + 0x47, 0xfe, 0x40, 0x84, 0x9, 0x8, 0x22, 0x14, + 0x8, 0x44, 0x63, 0x80, 0x8, 0x0, 0x10, 0x22, + 0x3e, 0x44, 0x41, 0x8, 0x82, 0x11, 0x4, 0xff, + 0xe8, 0x0, 0x0, + + /* U+6DAA "涪" */ + 0x0, 0x40, 0x80, 0x80, 0x9f, 0xf0, 0x10, 0x48, + 0x10, 0x88, 0x22, 0xb, 0xff, 0x80, 0x0, 0x0, + 0x0, 0x4f, 0xf8, 0x90, 0x12, 0x20, 0x24, 0x40, + 0x48, 0xff, 0xa1, 0x1, 0x0, + + /* U+6DAB "涫" */ + 0x0, 0x40, 0x80, 0x80, 0xbf, 0xf8, 0x40, 0x18, + 0xff, 0xe8, 0x80, 0x81, 0x1, 0x3, 0xfe, 0x4, + 0x0, 0x48, 0x1, 0x1f, 0xf2, 0x20, 0x24, 0x40, + 0x48, 0xff, 0xa1, 0x1, 0x0, + + /* U+6DAE "涮" */ + 0x0, 0x2, 0x9f, 0xc4, 0xa0, 0xa8, 0x41, 0x50, + 0x82, 0xa9, 0xf9, 0x4a, 0x22, 0x85, 0xf5, 0xa, + 0xaa, 0x55, 0x54, 0xaa, 0xaa, 0x55, 0x15, 0x2c, + 0x2a, 0x10, 0x68, 0x27, 0x80, + + /* U+6DAF "涯" */ + 0x0, 0x0, 0x4f, 0xfc, 0x50, 0x80, 0x21, 0x0, + 0x5f, 0xc8, 0x84, 0xd, 0x8, 0x2, 0xff, 0x24, + 0x20, 0x48, 0x40, 0x97, 0xf2, 0x61, 0x4, 0x82, + 0x9, 0x7f, 0xd0, 0x0, 0x0, + + /* U+6DB2 "液" */ + 0x0, 0x40, 0x80, 0x80, 0xbf, 0xf8, 0x2, 0x0, + 0x44, 0x18, 0x9f, 0x9b, 0x21, 0x6, 0xd2, 0x17, + 0x98, 0xa9, 0x11, 0x11, 0x42, 0x21, 0x84, 0x47, + 0x10, 0x91, 0xa1, 0xc0, 0x80, + + /* U+6DB5 "涵" */ + 0x0, 0x0, 0x8f, 0xf8, 0x80, 0x20, 0x1, 0x88, + 0xa4, 0xa9, 0x2a, 0x4a, 0x14, 0x84, 0x31, 0x9, + 0xd2, 0x54, 0x94, 0xa1, 0xa, 0x4e, 0x14, 0x80, + 0x29, 0xff, 0xe0, 0x0, 0x80, + + /* U+6DB8 "涸" */ + 0x0, 0x1, 0x3f, 0xf2, 0x84, 0x42, 0x11, 0x8b, + 0xf5, 0x21, 0x10, 0x84, 0x42, 0x7d, 0x9, 0x15, + 0x24, 0x54, 0x91, 0x52, 0x7d, 0x88, 0x6, 0x3f, + 0xf8, 0x80, 0x40, + + /* U+6DBF "涿" */ + 0x40, 0x0, 0x27, 0xfe, 0x10, 0x40, 0x0, 0x80, + 0x1, 0x82, 0x46, 0x4c, 0x30, 0xd0, 0x3, 0x28, + 0x2, 0x28, 0x2d, 0x68, 0x20, 0xa4, 0x21, 0x24, + 0x26, 0x22, 0x48, 0x20, 0x41, 0xc0, + + /* U+6DC0 "淀" */ + 0x0, 0x0, 0x40, 0x40, 0x5f, 0xf8, 0x20, 0x10, + 0x40, 0x2c, 0xff, 0xc0, 0x8, 0x0, 0x10, 0x2, + 0x20, 0x24, 0x7c, 0x88, 0x81, 0x11, 0x2, 0x52, + 0x8, 0xb4, 0x12, 0x1f, 0x80, + + /* U+6DC4 "淄" */ + 0x0, 0x0, 0x84, 0x88, 0xd1, 0x20, 0x24, 0x40, + 0x24, 0x58, 0x44, 0x98, 0x0, 0x3, 0xfe, 0x4, + 0x44, 0x8, 0x88, 0x91, 0x11, 0x3f, 0xe2, 0x44, + 0x48, 0x88, 0x91, 0xff, 0x22, 0x2, + + /* U+6DC5 "淅" */ + 0x84, 0x1, 0x11, 0xf0, 0x44, 0x7, 0xd0, 0x4, + 0x43, 0x11, 0xf0, 0xe4, 0x83, 0x52, 0x14, 0x49, + 0x51, 0x26, 0x44, 0xa1, 0x22, 0x84, 0x8a, 0x12, + 0x28, 0x40, 0x80, + + /* U+6DC6 "淆" */ + 0x46, 0x8, 0xc3, 0x60, 0x43, 0xc0, 0x38, 0x60, + 0x8, 0x9, 0xff, 0xc8, 0x40, 0x1, 0xfe, 0x6, + 0x4, 0x57, 0xf9, 0x8, 0x12, 0x1f, 0xe4, 0x20, + 0x48, 0x40, 0x90, 0x8e, 0x0, + + /* U+6DC7 "淇" */ + 0x2, 0x8, 0x84, 0x10, 0x88, 0x20, 0xff, 0xf0, + 0x20, 0x80, 0x41, 0x10, 0xfe, 0x11, 0x4, 0x3, + 0xf8, 0x44, 0x10, 0x88, 0x21, 0x7f, 0xf4, 0x0, + 0x8, 0x23, 0x11, 0x81, 0x4, 0x0, + + /* U+6DCB "淋" */ + 0x2, 0x10, 0x62, 0x10, 0x2, 0x10, 0xf, 0xbe, + 0x2, 0x10, 0x42, 0x10, 0x27, 0xb8, 0x6, 0x58, + 0xa, 0x54, 0x2a, 0x54, 0x52, 0x92, 0x52, 0x90, + 0x42, 0x10, 0x42, 0x10, 0x82, 0x10, + + /* U+6DCC "淌" */ + 0x0, 0x0, 0x88, 0x84, 0xc9, 0x10, 0xa, 0x40, + 0x4, 0x8, 0xff, 0xd9, 0x0, 0x82, 0x1, 0x4, + 0xf2, 0x9, 0x24, 0x92, 0x49, 0x24, 0x92, 0x4f, + 0x24, 0x80, 0x51, 0x0, 0xa2, 0xf, + + /* U+6DD1 "淑" */ + 0x40, 0x0, 0x22, 0x3e, 0x1, 0x92, 0x2, 0x12, + 0x82, 0x12, 0x42, 0x12, 0x2f, 0xd4, 0x1, 0x14, + 0x1, 0x14, 0x25, 0xc, 0x29, 0x48, 0x49, 0xc, + 0x41, 0x14, 0x41, 0x22, 0x46, 0x40, + + /* U+6DD6 "淖" */ + 0x0, 0x40, 0x30, 0x7e, 0x10, 0x40, 0x0, 0x40, + 0x7, 0xfc, 0x64, 0x4, 0x27, 0xfc, 0x4, 0x4, + 0x4, 0x4, 0x17, 0xfc, 0x20, 0x40, 0x2f, 0xff, + 0x20, 0x40, 0x40, 0x40, 0x40, 0x40, + + /* U+6DD8 "淘" */ + 0x0, 0x0, 0x84, 0x0, 0x9f, 0xf8, 0x20, 0x10, + 0x90, 0x38, 0x7e, 0x59, 0xa0, 0x80, 0x41, 0xf, + 0xfa, 0x1, 0x4, 0x92, 0x4a, 0x24, 0x94, 0x7f, + 0x28, 0x0, 0x50, 0x1, 0xc0, 0x3e, + + /* U+6DD9 "淙" */ + 0x40, 0x40, 0x60, 0x40, 0x5f, 0xf8, 0x20, 0x10, + 0x40, 0x2c, 0x7f, 0x80, 0x0, 0x0, 0x0, 0x7, + 0xfe, 0x20, 0x80, 0x85, 0x21, 0x12, 0x62, 0x44, + 0x49, 0x88, 0x50, 0x70, 0x0, + + /* U+6DDD "淝" */ + 0x0, 0x0, 0x4f, 0x7e, 0x29, 0x52, 0x9, 0x52, + 0x9, 0x52, 0xcf, 0x52, 0x29, 0x52, 0x9, 0x7e, + 0x9, 0x40, 0x2f, 0x40, 0x49, 0x40, 0x49, 0x41, + 0x59, 0x41, 0x51, 0x7e, 0x97, 0x0, + + /* U+6DDE "淞" */ + 0x4, 0x8, 0x44, 0x28, 0x24, 0x44, 0x1f, 0x44, + 0x4, 0x44, 0xc4, 0x82, 0x4e, 0x92, 0xd, 0x10, + 0x14, 0x20, 0x54, 0x28, 0x64, 0x48, 0x84, 0x44, + 0x84, 0x44, 0x84, 0xfe, 0x84, 0x2, + + /* U+6DE0 "淠" */ + 0x47, 0xfc, 0x50, 0x88, 0x21, 0x10, 0x42, 0x20, + 0x7f, 0xd9, 0x8, 0x9a, 0x11, 0x3, 0xfe, 0x0, + 0x0, 0x5f, 0xfd, 0x8, 0x42, 0x10, 0x84, 0x21, + 0x8, 0x82, 0x22, 0x4, 0x0, + + /* U+6DE1 "淡" */ + 0x0, 0x40, 0xc8, 0x88, 0x89, 0x20, 0x16, 0x8, + 0xb, 0x8, 0x21, 0xb, 0x81, 0x80, 0x20, 0x4, + 0x44, 0x44, 0x91, 0xa, 0x82, 0x5, 0x4, 0x11, + 0x8, 0xc1, 0x82, 0x0, 0x80, + + /* U+6DE4 "淤" */ + 0x0, 0x0, 0x84, 0x20, 0xc8, 0x60, 0x7c, 0xc0, + 0x42, 0x40, 0x84, 0x91, 0x10, 0x9b, 0xa0, 0x5, + 0x10, 0xa, 0x10, 0x94, 0x11, 0x48, 0x2, 0x90, + 0x9, 0x26, 0x12, 0x42, 0x2b, 0x82, + + /* U+6DE6 "淦" */ + 0x40, 0x40, 0x41, 0x40, 0x4, 0xc0, 0x10, 0x48, + 0xc0, 0x68, 0x7e, 0x8, 0x10, 0x0, 0x20, 0xf, + 0xfc, 0x40, 0x81, 0x11, 0x22, 0x12, 0xc4, 0x4, + 0x13, 0xff, 0xc0, 0x0, 0x0, + + /* U+6DEB "淫" */ + 0x40, 0x4, 0x4f, 0xf0, 0x40, 0x0, 0x22, 0x48, + 0x24, 0x8c, 0x0, 0x1, 0xff, 0x0, 0x20, 0x0, + 0x40, 0x40, 0x80, 0xbf, 0xfa, 0x2, 0x4, 0x4, + 0x8, 0xff, 0xd0, 0x0, 0x0, + + /* U+6DEC "淬" */ + 0x40, 0x40, 0x40, 0x80, 0x5f, 0xf0, 0x0, 0x0, + 0x20, 0x88, 0x43, 0x8, 0xc6, 0x2, 0x52, 0x8, + 0x0, 0x40, 0x80, 0xbf, 0xf9, 0x2, 0x4, 0x4, + 0x8, 0x8, 0x10, 0x10, 0x0, + + /* U+6DEE "淮" */ + 0x0, 0x40, 0x84, 0x40, 0xc8, 0x80, 0x3f, 0xf0, + 0xc2, 0x19, 0x84, 0x19, 0xff, 0x82, 0x10, 0x24, + 0x20, 0x4f, 0xfd, 0x10, 0x82, 0x21, 0x4, 0x42, + 0x10, 0xff, 0xe1, 0x0, 0x0, + + /* U+6DF1 "深" */ + 0x0, 0x0, 0x9f, 0xfc, 0xa0, 0x8, 0x40, 0x10, + 0x11, 0x18, 0xc1, 0x88, 0x10, 0x7, 0xff, 0x0, + 0xc0, 0x82, 0xc1, 0x5, 0x42, 0x32, 0x48, 0xc4, + 0x70, 0x8, 0x60, 0x10, 0x0, + + /* U+6DF3 "淳" */ + 0x0, 0x40, 0xc0, 0x80, 0xdf, 0xf8, 0x0, 0x0, + 0x3f, 0xd0, 0x40, 0x98, 0x81, 0x11, 0xfe, 0x0, + 0x0, 0xf, 0xf8, 0x80, 0x61, 0x1, 0x2, 0xff, + 0xe8, 0x4, 0x10, 0x8, 0x20, 0xe0, + + /* U+6DF7 "混" */ + 0x0, 0x0, 0xcf, 0xf8, 0x50, 0x10, 0x3f, 0xe4, + 0x40, 0x4c, 0x80, 0x81, 0xff, 0x0, 0x0, 0x24, + 0x22, 0x4f, 0x58, 0x90, 0xc1, 0x21, 0x14, 0x42, + 0x28, 0xb4, 0x51, 0x8f, 0x80, + + /* U+6DF9 "淹" */ + 0x40, 0x40, 0x20, 0x80, 0xf, 0xfe, 0x1, 0x20, + 0x2, 0x18, 0x48, 0x86, 0x7, 0xfc, 0x8, 0x84, + 0x27, 0xfc, 0x28, 0x84, 0x48, 0x84, 0x47, 0xfc, + 0x40, 0x81, 0x40, 0x42, 0x80, 0x7e, + + /* U+6DFB "添" */ + 0x0, 0x0, 0xcf, 0xf8, 0x82, 0x0, 0x4, 0x8, + 0xff, 0xe8, 0x24, 0x8, 0xc4, 0x3, 0x4, 0x8, + 0x86, 0x41, 0x11, 0x12, 0x92, 0x24, 0xa4, 0x89, + 0x28, 0x10, 0x20, 0xe0, 0x0, + + /* U+6DFC "淼" */ + 0x0, 0x80, 0x0, 0x80, 0x7c, 0xc8, 0x4, 0xb0, + 0x8, 0x90, 0x20, 0x8c, 0x47, 0x92, 0x8, 0x10, + 0x6a, 0xd2, 0x2c, 0x5c, 0x2c, 0x58, 0x4a, 0x54, + 0x48, 0x92, 0x89, 0x10, 0x38, 0x70, + + /* U+6E05 "清" */ + 0x40, 0x40, 0x5f, 0xfc, 0x1, 0x0, 0x3f, 0xe8, + 0x4, 0x9, 0xff, 0xc8, 0x0, 0x3, 0xfe, 0x4, + 0x4, 0x4f, 0xf8, 0x90, 0x12, 0x3f, 0xe4, 0x40, + 0x48, 0x80, 0x91, 0xe, 0x0, + + /* U+6E0A "渊" */ + 0x44, 0x42, 0x68, 0x94, 0x15, 0x48, 0x22, 0x96, + 0x44, 0x20, 0xbf, 0x41, 0x10, 0x82, 0x61, 0x24, + 0xe2, 0x4a, 0xa5, 0x15, 0x2a, 0x52, 0x14, 0x84, + 0x2a, 0x8, 0x40, + + /* U+6E0C "渌" */ + 0x40, 0x0, 0x27, 0xfc, 0x10, 0x4, 0x7, 0xfc, + 0x0, 0x4, 0x40, 0x4, 0x2f, 0xff, 0x0, 0x40, + 0x4, 0x62, 0x23, 0x6c, 0x20, 0xd0, 0x23, 0x48, + 0x4c, 0x46, 0x40, 0x40, 0x43, 0xc0, + + /* U+6E0D "渍" */ + 0x40, 0x40, 0x4f, 0xfc, 0x1, 0x0, 0x3f, 0xe8, + 0x4, 0xd, 0xff, 0xc0, 0x0, 0x3, 0xfe, 0x24, + 0x4, 0x48, 0x88, 0x91, 0x12, 0x22, 0x24, 0x1b, + 0x9, 0xc1, 0x80, 0x0, 0x0, + + /* U+6E0E "渎" */ + 0x0, 0x40, 0x80, 0x80, 0x81, 0x1, 0xbf, 0xf0, + 0x4, 0x19, 0xff, 0xd8, 0x0, 0x91, 0x92, 0x0, + 0xa0, 0xc, 0x40, 0x80, 0x81, 0x7f, 0xf4, 0x4, + 0x8, 0x1e, 0x10, 0xc3, 0x26, 0x1, + + /* U+6E10 "渐" */ + 0x42, 0x0, 0x24, 0x36, 0xf, 0xa0, 0x4, 0x20, + 0x8, 0x20, 0x4a, 0x20, 0x2a, 0x3f, 0xf, 0xa4, + 0x2, 0x24, 0x22, 0x24, 0x23, 0xc4, 0x5e, 0x44, + 0x42, 0x44, 0x42, 0x44, 0x42, 0x84, + + /* U+6E11 "渑" */ + 0x63, 0xfc, 0x12, 0x4, 0x2, 0x4, 0x3, 0xfc, + 0x40, 0x40, 0x60, 0x40, 0x7, 0xfe, 0x4, 0x42, + 0x7, 0xfe, 0x24, 0x42, 0x24, 0x42, 0x27, 0xfe, + 0x40, 0x41, 0x40, 0x41, 0x40, 0x7e, + + /* U+6E14 "渔" */ + 0x1, 0x0, 0x82, 0x0, 0x8f, 0xc0, 0x20, 0x80, + 0x82, 0x10, 0xff, 0x99, 0x11, 0x12, 0x22, 0x7, + 0xfc, 0x8, 0x88, 0x91, 0x12, 0x3f, 0xe4, 0x0, + 0x8, 0x0, 0x17, 0xff, 0x80, 0x0, + + /* U+6E16 "渖" */ + 0x0, 0x40, 0x80, 0x80, 0xbf, 0xf8, 0x40, 0x10, + 0x84, 0x38, 0xff, 0x89, 0x11, 0x2, 0x22, 0x7, + 0xfc, 0x48, 0x88, 0x91, 0x12, 0x3f, 0xe4, 0x44, + 0x48, 0x8, 0x20, 0x10, 0x0, + + /* U+6E17 "渗" */ + 0x0, 0xc0, 0xc2, 0x20, 0x58, 0x20, 0x3f, 0xe8, + 0x8, 0x9, 0xff, 0xc8, 0x44, 0x3, 0x24, 0x18, + 0x86, 0x4e, 0x21, 0x1, 0x82, 0x1c, 0x44, 0x43, + 0x10, 0x38, 0x3, 0x80, 0x0, + + /* U+6E1A "渚" */ + 0x40, 0x80, 0xc1, 0x8, 0x5f, 0xe0, 0x4, 0xc8, + 0x9, 0x9, 0xff, 0xc8, 0x10, 0x0, 0xc0, 0x7, + 0xf8, 0x74, 0x10, 0x8f, 0xe2, 0x10, 0x44, 0x20, + 0x88, 0x7f, 0x20, 0x82, 0x0, + + /* U+6E1D "渝" */ + 0x0, 0x40, 0x41, 0x40, 0x44, 0xc0, 0x10, 0xc0, + 0x5f, 0x49, 0x0, 0x49, 0xe1, 0x2, 0x52, 0x4, + 0xa4, 0x4f, 0x48, 0x92, 0x91, 0x3d, 0x22, 0x48, + 0x48, 0x90, 0x91, 0x67, 0x0, + + /* U+6E20 "渠" */ + 0x0, 0x0, 0x4f, 0xf8, 0x50, 0x0, 0x20, 0xc, + 0x7f, 0x84, 0x81, 0x1, 0xfe, 0x1a, 0x0, 0x67, + 0xfc, 0x0, 0x3, 0xff, 0xf8, 0x2c, 0x0, 0x94, + 0x2, 0x26, 0x38, 0x43, 0x80, 0x80, + + /* U+6E21 "渡" */ + 0x0, 0x40, 0x80, 0x40, 0xdf, 0xf8, 0xa0, 0x0, + 0x48, 0x88, 0xbf, 0xd9, 0x22, 0x12, 0x44, 0x4, + 0xf8, 0x8, 0x0, 0x97, 0xf1, 0x24, 0x24, 0x88, + 0x89, 0xe, 0x12, 0x16, 0x29, 0xc3, + + /* U+6E23 "渣" */ + 0x0, 0x40, 0xc0, 0x80, 0xdf, 0xf0, 0x7, 0x0, + 0x35, 0x8d, 0x88, 0xc8, 0xfe, 0x2, 0x4, 0x4, + 0x8, 0x47, 0xf0, 0x90, 0x21, 0x1f, 0xc4, 0x0, + 0x9, 0xff, 0xc0, + + /* U+6E24 "渤" */ + 0x82, 0x11, 0x8, 0x47, 0xf9, 0x0, 0x9f, 0x1f, + 0x96, 0x42, 0x54, 0x1, 0x43, 0xc5, 0x1, 0x15, + 0x8, 0xd4, 0x7a, 0x56, 0x89, 0x42, 0x46, 0x9, + 0x18, 0xe9, 0xc0, + + /* U+6E25 "渥" */ + 0x40, 0x0, 0x27, 0xfe, 0x14, 0x2, 0x7, 0xfe, + 0x44, 0x0, 0x65, 0xfe, 0x4, 0x40, 0x4, 0x8c, + 0x25, 0xfe, 0x24, 0x22, 0x24, 0x20, 0x29, 0xfe, + 0x48, 0x20, 0x5b, 0xff, 0x0, 0x0, + + /* U+6E29 "温" */ + 0x0, 0x0, 0x8f, 0xf0, 0x90, 0x20, 0x3f, 0xc8, + 0x40, 0x88, 0x81, 0x9, 0xfe, 0x0, 0x0, 0x2f, + 0xfc, 0x52, 0x49, 0x24, 0x92, 0x49, 0x24, 0x92, + 0x4b, 0xff, 0xc0, 0x0, 0x0, + + /* U+6E2B "渫" */ + 0x0, 0x88, 0x9, 0x11, 0x12, 0x21, 0x7f, 0xf0, + 0x48, 0x90, 0x9f, 0x11, 0x0, 0x12, 0x0, 0x7, + 0xfe, 0x0, 0x80, 0xbf, 0xf9, 0x7, 0x4, 0x15, + 0x8, 0x49, 0x13, 0x11, 0x80, 0x20, + + /* U+6E2D "渭" */ + 0x0, 0x0, 0xcf, 0xfc, 0xd1, 0x8, 0xa2, 0x10, + 0x7f, 0xe0, 0x88, 0x51, 0xff, 0x90, 0x0, 0x3, + 0xfc, 0x4, 0x8, 0x8f, 0xf1, 0x10, 0x22, 0x3f, + 0xc4, 0x40, 0x90, 0x81, 0x21, 0x1e, + + /* U+6E2F "港" */ + 0x1, 0x10, 0xc2, 0x20, 0xdf, 0xf0, 0x8, 0x80, + 0x11, 0x9, 0xff, 0xc8, 0x44, 0x1, 0x4, 0x7, + 0xf4, 0x74, 0x24, 0x8f, 0xc1, 0x10, 0x24, 0x20, + 0x48, 0x40, 0x90, 0xfe, 0x0, + + /* U+6E32 "渲" */ + 0x0, 0x40, 0x20, 0x60, 0x17, 0xfe, 0x4, 0x2, + 0x43, 0xfc, 0x20, 0x0, 0x3, 0xfe, 0x2, 0x2, + 0x2, 0x2, 0x23, 0xfe, 0x22, 0x2, 0x23, 0xfe, + 0x40, 0x0, 0x4f, 0xff, 0x0, 0x0, + + /* U+6E34 "渴" */ + 0x47, 0xf8, 0x90, 0x21, 0x40, 0x81, 0xfe, 0x4, + 0xb, 0x1f, 0xe2, 0x20, 0x1, 0xff, 0xc, 0x44, + 0x91, 0x14, 0x5b, 0x51, 0x1, 0x47, 0xf5, 0x0, + 0x18, 0x3, 0x80, + + /* U+6E38 "游" */ + 0x0, 0x20, 0x88, 0x80, 0x11, 0xf8, 0xfa, 0x0, + 0x88, 0x19, 0xf, 0x83, 0x82, 0x5, 0x8, 0x4a, + 0xfe, 0xa4, 0x21, 0x48, 0x42, 0x90, 0x89, 0x21, + 0x12, 0x42, 0x2b, 0x98, 0x0, + + /* U+6E3A "渺" */ + 0x0, 0x10, 0xde, 0x20, 0xa4, 0x40, 0x4a, 0xa0, + 0x95, 0x49, 0xea, 0x4a, 0x64, 0x84, 0xc8, 0xf, + 0x12, 0x52, 0x29, 0x24, 0x22, 0x48, 0x44, 0xf3, + 0x8, 0x8, 0x10, 0x60, 0x0, + + /* U+6E43 "湃" */ + 0x80, 0x0, 0xbf, 0xfc, 0x10, 0x40, 0x20, 0x88, + 0x47, 0xdb, 0xe2, 0x11, 0x4, 0x2, 0x3e, 0x4, + 0x10, 0xbe, 0x21, 0x10, 0x42, 0x27, 0xf8, 0x81, + 0x11, 0x2, 0x24, 0x4, 0x0, + + /* U+6E44 "湄" */ + 0x0, 0x1, 0x3f, 0xf2, 0x84, 0x42, 0x11, 0xf, + 0xff, 0x20, 0x4, 0xbf, 0xc2, 0x81, 0x4b, 0xfd, + 0x28, 0x14, 0xa0, 0x64, 0xff, 0x92, 0x6, 0xcf, + 0xfa, 0x20, 0x40, + + /* U+6E4D "湍" */ + 0x40, 0x40, 0x48, 0x88, 0x11, 0x10, 0x3f, 0xe8, + 0x0, 0x8, 0x0, 0xb, 0xff, 0x80, 0x20, 0x7, + 0xfc, 0x51, 0x28, 0xa2, 0x52, 0x44, 0xa4, 0x89, + 0x49, 0x12, 0x92, 0x3, 0x0, + + /* U+6E4E "湎" */ + 0x40, 0x0, 0xdf, 0xfc, 0xc2, 0x0, 0x4, 0x0, + 0xff, 0xd9, 0x22, 0x8a, 0x45, 0x4, 0xfa, 0x9, + 0x14, 0x52, 0x28, 0xa7, 0xd2, 0x48, 0xa4, 0x91, + 0x49, 0xff, 0x92, 0x1, 0x0, + + /* U+6E53 "湓" */ + 0x0, 0x20, 0x82, 0x60, 0xc8, 0x60, 0x20, 0x60, + 0xbf, 0xb0, 0x11, 0x18, 0x42, 0x1, 0x84, 0xe, + 0x70, 0x0, 0x0, 0xbf, 0xf2, 0x48, 0xa4, 0x91, + 0x49, 0x22, 0xa7, 0xff, 0x80, + + /* U+6E54 "湔" */ + 0x0, 0x0, 0x82, 0x10, 0x82, 0x40, 0x7f, 0xf0, + 0x0, 0x0, 0x0, 0x13, 0xe5, 0x4, 0x4a, 0xf, + 0x94, 0x51, 0x28, 0xa2, 0x52, 0x7c, 0xa4, 0x88, + 0x49, 0x10, 0x92, 0x21, 0x24, 0xce, + + /* U+6E56 "湖" */ + 0x2, 0x0, 0xc4, 0x7c, 0x48, 0x88, 0x39, 0x1c, + 0x23, 0xec, 0x44, 0x43, 0xe8, 0x84, 0x51, 0x8, + 0xbe, 0x51, 0x44, 0xa2, 0x89, 0x7d, 0x14, 0x84, + 0x28, 0x8, 0x50, 0x23, 0x80, + + /* U+6E58 "湘" */ + 0x42, 0x0, 0xc4, 0x7c, 0x48, 0x88, 0x7d, 0x10, + 0x22, 0x28, 0x47, 0xc9, 0xc8, 0x83, 0x51, 0xa, + 0x22, 0x54, 0x7c, 0xc8, 0x8a, 0x11, 0x14, 0x22, + 0x28, 0x47, 0xd0, 0x88, 0x80, + + /* U+6E5B "湛" */ + 0x42, 0x0, 0xc4, 0x10, 0xdf, 0xf8, 0x10, 0x40, + 0x3f, 0x8c, 0x41, 0x0, 0xfe, 0x1, 0x4, 0x2, + 0x8, 0x5f, 0xfc, 0x91, 0x2, 0x24, 0x64, 0x40, + 0x8, 0xff, 0xc0, + + /* U+6E5F "湟" */ + 0x0, 0x0, 0x60, 0x80, 0x37, 0xfe, 0x14, 0x2, + 0x4, 0x2, 0x7, 0xfe, 0x64, 0x2, 0x7, 0xfe, + 0x0, 0x0, 0x27, 0xfe, 0x20, 0x40, 0x20, 0x40, + 0x27, 0xfe, 0x40, 0x40, 0x4f, 0xff, 0x0, 0x0, + + /* U+6E6B "湫" */ + 0x0, 0x18, 0x4f, 0x98, 0x32, 0x18, 0x2, 0x59, + 0x2, 0x59, 0x4f, 0xda, 0x22, 0x3a, 0x2, 0x3a, + 0x7, 0x18, 0x6, 0x98, 0x2a, 0x1c, 0x2a, 0x1c, + 0x42, 0x24, 0x42, 0x26, 0x42, 0x43, 0x42, 0x40, + + /* U+6E6E "湮" */ + 0x4f, 0xfe, 0x42, 0x40, 0x3f, 0xf4, 0x49, 0x24, + 0x92, 0x41, 0x24, 0x83, 0xff, 0x0, 0x20, 0x40, + 0x40, 0x9f, 0xf9, 0x1, 0x2, 0x2, 0x9, 0xff, + 0xe0, 0x0, 0x0, + + /* U+6E7E "湾" */ + 0x0, 0x41, 0x80, 0x81, 0xbf, 0xf9, 0x5, 0x0, + 0x6a, 0xd1, 0x14, 0x50, 0x0, 0x3, 0xfe, 0x0, + 0x4, 0x7, 0xf8, 0x90, 0x2, 0x3f, 0xe4, 0x0, + 0x48, 0x0, 0xa0, 0x1, 0x40, 0x3c, + + /* U+6E7F "湿" */ + 0x67, 0xfc, 0x28, 0x8, 0x1f, 0xf0, 0x20, 0x26, + 0x40, 0x44, 0xff, 0x80, 0x0, 0x14, 0x91, 0x25, + 0x24, 0x4a, 0x48, 0x84, 0x82, 0x9, 0x4, 0xff, + 0xe8, 0x0, 0x0, + + /* U+6E83 "溃" */ + 0x40, 0x40, 0x4f, 0xf8, 0x51, 0x10, 0x22, 0x20, + 0x7f, 0xc8, 0x8, 0xb, 0xff, 0x80, 0x0, 0x7, + 0xfc, 0x48, 0x8, 0x91, 0x11, 0x22, 0x24, 0x4e, + 0x48, 0x33, 0x13, 0x81, 0x80, + + /* U+6E85 "溅" */ + 0x0, 0x10, 0x4f, 0x94, 0x38, 0x92, 0x8, 0x90, + 0xa, 0xfe, 0x4a, 0x90, 0x2a, 0x90, 0xa, 0x9e, + 0xa, 0xb0, 0x2a, 0x92, 0x2a, 0x8c, 0x45, 0x9, + 0x45, 0xb9, 0x48, 0x85, 0x50, 0x6, + + /* U+6E86 "溆" */ + 0xc2, 0x0, 0xcc, 0xf8, 0x25, 0x10, 0x46, 0x29, + 0x4, 0x59, 0xe5, 0x11, 0xa, 0x2, 0x14, 0x1f, + 0xa8, 0x88, 0x51, 0x34, 0x42, 0xa4, 0x89, 0x4a, + 0x90, 0x88, 0xa3, 0x20, 0x0, + + /* U+6E89 "溉" */ + 0x40, 0x0, 0x5e, 0xfc, 0x24, 0x20, 0x4a, 0x48, + 0xf4, 0x89, 0x29, 0xa, 0x52, 0x7, 0x9a, 0x8, + 0x10, 0x54, 0x31, 0x24, 0xe2, 0x59, 0x44, 0xc4, + 0xa9, 0x11, 0x60, 0x43, 0x80, + + /* U+6E8F "溏" */ + 0x0, 0x20, 0x60, 0x20, 0x17, 0xde, 0x4, 0x20, + 0x5, 0xfe, 0x44, 0x22, 0x64, 0x22, 0x37, 0xff, + 0x4, 0x22, 0x5, 0xfe, 0x24, 0x20, 0x25, 0xfe, + 0x4d, 0x2, 0x49, 0x2, 0x49, 0xfe, 0x51, 0x2, + + /* U+6E90 "源" */ + 0x40, 0x0, 0x27, 0xff, 0x14, 0x20, 0x4, 0x40, + 0x5, 0xfe, 0x65, 0x2, 0x5, 0xfe, 0x5, 0x2, + 0x5, 0x2, 0x25, 0xfe, 0x24, 0x20, 0x48, 0xa4, + 0x49, 0x22, 0x4a, 0x21, 0x50, 0xe0, + + /* U+6E98 "溘" */ + 0x40, 0x40, 0x4f, 0xf8, 0x1, 0x0, 0x2, 0x8, + 0xff, 0xe8, 0x20, 0x9, 0x84, 0x7, 0xfe, 0x0, + 0x4, 0x4f, 0xf9, 0x24, 0x92, 0x49, 0x24, 0x92, + 0x49, 0x24, 0xa7, 0xff, 0x80, + + /* U+6E9C "溜" */ + 0x40, 0x80, 0x27, 0x3e, 0x14, 0x12, 0x5, 0x12, + 0x4, 0x92, 0x47, 0xa2, 0x34, 0x6e, 0x0, 0x40, + 0x7, 0xfe, 0x4, 0x42, 0x27, 0xfe, 0x24, 0x42, + 0x24, 0x42, 0x47, 0xfe, 0x44, 0x2, + + /* U+6E9F "溟" */ + 0x4f, 0xfe, 0x50, 0x4, 0x1f, 0xf4, 0x20, 0x26, + 0x7f, 0xc4, 0x80, 0x81, 0x1, 0x3, 0xfe, 0x20, + 0x40, 0x5f, 0xfd, 0x0, 0x2, 0x18, 0xc4, 0xc0, + 0x60, 0x0, 0x0, + + /* U+6EA2 "溢" */ + 0xc2, 0x8, 0xc4, 0x20, 0x84, 0x80, 0x7f, 0xf0, + 0x0, 0x18, 0x42, 0x3, 0x3, 0x8, 0x1, 0xf, + 0xfc, 0x92, 0x49, 0x24, 0x92, 0x49, 0x28, 0x92, + 0x53, 0xff, 0xc0, 0x0, 0x0, + + /* U+6EA5 "溥" */ + 0x40, 0x48, 0x20, 0x44, 0xf, 0xff, 0x0, 0x40, + 0x7, 0xfe, 0x44, 0x42, 0x27, 0xfe, 0x7, 0xfe, + 0x4, 0x42, 0x24, 0x42, 0x2f, 0xff, 0x22, 0x8, + 0x41, 0x8, 0x40, 0x8, 0x40, 0x78, + + /* U+6EA7 "溧" */ + 0x0, 0x0, 0xdf, 0xfc, 0x4, 0x80, 0x7f, 0xe8, + 0x92, 0x4d, 0x24, 0x83, 0xff, 0x0, 0x0, 0x0, + 0x40, 0x5f, 0xfc, 0x83, 0x2, 0xb, 0x84, 0x64, + 0x8b, 0x8, 0xd0, 0x10, 0x0, + + /* U+6EAA "溪" */ + 0x40, 0x0, 0xcf, 0xf8, 0xc1, 0x10, 0x32, 0x40, + 0x8, 0x8, 0x43, 0x9, 0xf8, 0x0, 0x40, 0x7, + 0xf8, 0x40, 0x88, 0xbf, 0xf9, 0x6, 0x4, 0x1b, + 0x8, 0xc1, 0x82, 0x0, 0x80, + + /* U+6EAF "溯" */ + 0x0, 0x1, 0x24, 0xf2, 0x12, 0x47, 0xe9, 0x4, + 0x26, 0x52, 0xf5, 0x4a, 0x45, 0x29, 0x54, 0xa5, + 0x7e, 0xf4, 0x44, 0x51, 0x11, 0x84, 0x46, 0x21, + 0x19, 0x9, 0xc0, + + /* U+6EB1 "溱" */ + 0x0, 0x40, 0x81, 0x0, 0xff, 0xf0, 0x4, 0x0, + 0x8, 0x0, 0xff, 0xb0, 0x20, 0x17, 0xff, 0x2, + 0x10, 0xb, 0xd0, 0xe1, 0x19, 0x7f, 0xe4, 0xc, + 0x8, 0x6e, 0x13, 0x13, 0xa0, 0x20, + + /* U+6EB2 "溲" */ + 0x40, 0x40, 0x27, 0x5e, 0x14, 0x42, 0x4, 0x42, + 0x7, 0x5e, 0x44, 0x42, 0x24, 0x42, 0x7, 0xfe, + 0x0, 0x40, 0x27, 0xfc, 0x22, 0x4, 0x21, 0x18, + 0x40, 0xf0, 0x41, 0xf0, 0x4e, 0xe, + + /* U+6EB4 "溴" */ + 0x0, 0x40, 0x81, 0x0, 0xdf, 0xf0, 0x20, 0x20, + 0x7f, 0xd0, 0x80, 0x99, 0xff, 0x2, 0x2, 0x7, + 0xfc, 0x40, 0x80, 0x81, 0x21, 0x7f, 0xf4, 0xc, + 0x8, 0x24, 0x11, 0x87, 0x24, 0x1, + + /* U+6EB6 "溶" */ + 0x0, 0x80, 0x81, 0x0, 0xbf, 0xf0, 0x40, 0x20, + 0x92, 0x50, 0xcb, 0x18, 0x71, 0x1, 0x18, 0x4, + 0xc, 0x5f, 0xf1, 0x10, 0x22, 0x20, 0x44, 0x40, + 0x90, 0xff, 0x21, 0x2, 0x0, + + /* U+6EB7 "溷" */ + 0x0, 0x0, 0x9f, 0xfc, 0xa0, 0x8, 0x5f, 0xd8, + 0x84, 0x29, 0x31, 0x42, 0x94, 0x84, 0xd1, 0xa, + 0x72, 0x51, 0x55, 0x2c, 0x8a, 0x47, 0x14, 0x80, + 0x29, 0xff, 0xe2, 0x0, 0x80, + + /* U+6EBA "溺" */ + 0xdf, 0x7d, 0x84, 0x10, 0x10, 0x43, 0xdf, 0x90, + 0x41, 0x41, 0x1, 0xf7, 0xc0, 0x41, 0x59, 0x65, + 0x14, 0x54, 0x71, 0xe6, 0x59, 0x81, 0x6, 0x38, + 0xf0, + + /* U+6EBB "溻" */ + 0x47, 0xfc, 0x48, 0x8, 0x50, 0x10, 0x3f, 0xe0, + 0x40, 0x48, 0xff, 0x88, 0x0, 0x7, 0xdf, 0x0, + 0xa2, 0x49, 0x64, 0x82, 0x49, 0x1c, 0xf5, 0xca, + 0x28, 0x10, 0x51, 0xe7, 0x0, + + /* U+6EBD "溽" */ + 0x0, 0x0, 0x9f, 0xf8, 0xa0, 0x0, 0x5f, 0xc0, + 0x80, 0x19, 0xff, 0xda, 0x53, 0x4, 0x98, 0x9, + 0x58, 0x93, 0x29, 0x2f, 0xfa, 0x48, 0x89, 0x11, + 0x12, 0x2, 0x28, 0x3c, 0x0, + + /* U+6EC1 "滁" */ + 0x0, 0x21, 0xbc, 0x41, 0x49, 0x40, 0xa4, 0x49, + 0x48, 0x5a, 0xbf, 0x15, 0x8, 0xa, 0x10, 0x13, + 0xfc, 0xa4, 0x41, 0x4a, 0xa2, 0xf9, 0x29, 0x12, + 0x52, 0x44, 0x24, 0x38, 0x0, + + /* U+6EC2 "滂" */ + 0x0, 0x40, 0x40, 0x80, 0x5f, 0xf0, 0x10, 0x80, + 0x11, 0x9, 0xff, 0xca, 0x20, 0x80, 0x20, 0xf, + 0xfe, 0x2, 0x0, 0x87, 0xe1, 0x10, 0x42, 0x20, + 0x88, 0x81, 0x12, 0x1c, 0x0, + + /* U+6EC7 "滇" */ + 0xc0, 0x80, 0xdf, 0xf8, 0x2, 0x0, 0x3f, 0xc8, + 0x80, 0x88, 0xff, 0x2, 0x2, 0x3, 0xfc, 0x8, + 0x8, 0x8f, 0xf1, 0x20, 0x22, 0xff, 0xf4, 0x0, + 0x10, 0xc3, 0x26, 0x1, 0x0, + + /* U+6ECB "滋" */ + 0x2, 0x8, 0x82, 0x20, 0x84, 0x40, 0x7f, 0xf0, + 0x21, 0x18, 0x82, 0x11, 0x29, 0x4, 0xa2, 0xf, + 0x78, 0x4, 0x21, 0x10, 0x42, 0x29, 0x44, 0x8c, + 0x49, 0xff, 0xa0, 0x20, 0x80, + + /* U+6ECF "滏" */ + 0x0, 0x0, 0x86, 0x30, 0xb0, 0x10, 0x9, 0x80, + 0xc, 0x10, 0xe7, 0x9a, 0x0, 0x83, 0xfc, 0x0, + 0x40, 0x0, 0x80, 0xbf, 0xf2, 0x2, 0x4, 0x64, + 0x88, 0xa, 0x27, 0xff, 0xc0, 0x0, + + /* U+6ED1 "滑" */ + 0x43, 0xfc, 0x48, 0x8, 0x50, 0x10, 0x1f, 0x20, + 0x42, 0x49, 0xff, 0xca, 0x0, 0x1, 0xfe, 0x4, + 0x4, 0x47, 0xf8, 0x90, 0x12, 0x1f, 0xe4, 0x40, + 0x48, 0x80, 0x91, 0xf, 0x0, + + /* U+6ED3 "滓" */ + 0x40, 0x40, 0xc0, 0x80, 0xbf, 0xf8, 0x42, 0x10, + 0x84, 0x28, 0xff, 0x88, 0x82, 0x0, 0x88, 0xf, + 0xfe, 0x40, 0x80, 0x81, 0x2, 0x3f, 0xe4, 0x4, + 0x8, 0x8, 0x20, 0x10, 0x0, + + /* U+6ED4 "滔" */ + 0x40, 0x2, 0x4f, 0xfc, 0x40, 0x0, 0x22, 0x38, + 0x22, 0x4c, 0x0, 0x1, 0xe7, 0x82, 0x1, 0x4, + 0x2, 0x48, 0x4, 0x9e, 0x79, 0x20, 0x14, 0x40, + 0x28, 0xff, 0xd1, 0x0, 0x80, + + /* U+6ED5 "滕" */ + 0x0, 0x20, 0x0, 0x20, 0x79, 0x24, 0x48, 0xa8, + 0x4b, 0xfe, 0x48, 0x40, 0x78, 0x40, 0x4b, 0xfe, + 0x48, 0x88, 0x4b, 0x24, 0x7f, 0x26, 0x48, 0xa8, + 0x48, 0x70, 0x4b, 0xac, 0x88, 0x22, 0xb8, 0xe0, + + /* U+6ED7 "滗" */ + 0x2, 0x10, 0x88, 0x20, 0x9e, 0xf8, 0x51, 0x0, + 0xa4, 0x90, 0x0, 0x93, 0xfe, 0x10, 0x40, 0x0, + 0x80, 0x1f, 0xf0, 0x82, 0x1, 0x4, 0x75, 0xff, + 0x8, 0x10, 0x50, 0x20, 0xa0, 0x7e, + + /* U+6EDA "滚" */ + 0x0, 0x40, 0x80, 0x40, 0xdf, 0xf8, 0x80, 0x0, + 0x10, 0x88, 0x88, 0xc8, 0x24, 0x0, 0x84, 0x7, + 0xfc, 0x40, 0x88, 0x82, 0x89, 0x18, 0xe4, 0xd1, + 0x8, 0x25, 0x90, 0x70, 0x80, + + /* U+6EDE "滞" */ + 0x42, 0x48, 0x44, 0x90, 0x3f, 0xf8, 0x12, 0x48, + 0x0, 0xd, 0xff, 0xc2, 0x0, 0x84, 0x21, 0x27, + 0xfc, 0x48, 0x88, 0x91, 0x12, 0x22, 0x24, 0x44, + 0x48, 0xb, 0x90, 0x10, 0x0, + + /* U+6EDF "滟" */ + 0x4, 0x20, 0x64, 0x3c, 0x1f, 0x44, 0x4, 0x88, + 0x4, 0x88, 0xc4, 0xf6, 0x3f, 0x92, 0x4, 0x92, + 0x4, 0x7e, 0x5f, 0x80, 0x44, 0x80, 0x44, 0x81, + 0x44, 0x82, 0x84, 0x42, 0x84, 0x7e, + + /* U+6EE0 "滠" */ + 0x4f, 0xff, 0x22, 0x4, 0x12, 0x4, 0x3, 0xfc, + 0x2, 0x4, 0x43, 0xfc, 0x32, 0x4, 0xf, 0xfe, + 0x20, 0x0, 0x2f, 0xbf, 0x20, 0xa2, 0x4d, 0x12, + 0x43, 0xc, 0x47, 0x9c, 0x58, 0x63, + + /* U+6EE1 "满" */ + 0x1, 0x10, 0x82, 0x20, 0xbf, 0xf8, 0x8, 0x80, + 0x0, 0x11, 0xff, 0xd0, 0x44, 0x10, 0x88, 0xf, + 0xfe, 0x12, 0x44, 0xa4, 0x8a, 0x4d, 0x94, 0xa2, + 0xa9, 0x88, 0x52, 0x0, 0xc4, 0xe, + + /* U+6EE2 "滢" */ + 0x0, 0x11, 0x84, 0x20, 0xff, 0xf8, 0x0, 0x89, + 0xff, 0xea, 0x0, 0x44, 0x0, 0x83, 0xfc, 0x0, + 0x40, 0x80, 0x81, 0x3f, 0xf2, 0x2, 0x4, 0x4, + 0x90, 0x8, 0x27, 0xff, 0x80, + + /* U+6EE4 "滤" */ + 0x40, 0x40, 0x20, 0x3e, 0x10, 0x40, 0x7, 0xfe, + 0x4, 0x2, 0x44, 0x40, 0x25, 0xfa, 0x4, 0x44, + 0x4, 0x3c, 0x24, 0x0, 0x24, 0x50, 0x25, 0x42, + 0x49, 0x46, 0x4a, 0x45, 0x50, 0x78, + + /* U+6EE5 "滥" */ + 0x1, 0x0, 0x92, 0x40, 0xa4, 0x80, 0x49, 0xe0, + 0x94, 0x11, 0x2a, 0x12, 0x52, 0x4, 0x84, 0x0, + 0x0, 0x9f, 0xf1, 0x24, 0xa2, 0x49, 0x44, 0x92, + 0x89, 0x25, 0x22, 0x4a, 0x4f, 0xff, + + /* U+6EE6 "滦" */ + 0x0, 0x40, 0xc0, 0x80, 0xbf, 0xf8, 0x9, 0x0, + 0x52, 0xd8, 0xa4, 0x8a, 0x48, 0x80, 0x0, 0xf, + 0xfe, 0x41, 0xc1, 0x5, 0x42, 0x12, 0xc4, 0x44, + 0x4b, 0x8, 0x60, 0x10, 0x0, + + /* U+6EE8 "滨" */ + 0x0, 0x81, 0x81, 0x1, 0xbf, 0xf0, 0x40, 0x20, + 0x0, 0x10, 0xfe, 0x11, 0x0, 0x2, 0x0, 0x7, + 0xfc, 0x8, 0x81, 0x11, 0x2, 0xff, 0xf4, 0x0, + 0x10, 0x46, 0x23, 0x3, 0x0, 0x0, + + /* U+6EE9 "滩" */ + 0x0, 0x40, 0x80, 0x90, 0xfa, 0x20, 0x17, 0xf0, + 0x28, 0x9a, 0x71, 0x3, 0x3f, 0x86, 0x44, 0x4, + 0x88, 0x99, 0xfd, 0x32, 0x22, 0x54, 0x45, 0x28, + 0x92, 0x1f, 0xe0, 0x20, 0x0, + + /* U+6EF4 "滴" */ + 0x0, 0x40, 0x80, 0x80, 0xbf, 0xf8, 0x10, 0x40, + 0x11, 0x19, 0xff, 0x8a, 0x11, 0x5, 0xfe, 0x8, + 0x44, 0x53, 0xe8, 0xa4, 0x52, 0x48, 0xa4, 0x9f, + 0x49, 0x0, 0x92, 0x3, 0x0, + + /* U+6EF9 "滹" */ + 0x0, 0x80, 0x81, 0xf8, 0x82, 0x0, 0x7f, 0xf0, + 0x88, 0x39, 0x7e, 0x82, 0x3f, 0x4, 0x0, 0xb, + 0xfc, 0x90, 0x49, 0x24, 0xa2, 0x7f, 0xf5, 0x2, + 0x12, 0x4, 0x2c, 0x78, 0x0, + + /* U+6F02 "漂" */ + 0xdf, 0xfe, 0xc2, 0x40, 0x4, 0x80, 0x7f, 0xe1, + 0x12, 0x52, 0x24, 0x93, 0xff, 0x0, 0x0, 0xf, + 0xfc, 0x80, 0x1, 0x7f, 0xfa, 0x2, 0x8, 0x45, + 0x97, 0x8, 0xe0, 0xe0, 0x0, + + /* U+6F06 "漆" */ + 0x0, 0x80, 0xc1, 0x0, 0xbf, 0xf0, 0xb, 0x8, + 0x69, 0x99, 0x0, 0x80, 0x30, 0x1, 0x98, 0xc, + 0xc, 0x4d, 0x21, 0x2, 0x82, 0xd, 0x4, 0xe9, + 0x98, 0x10, 0xa0, 0xe0, 0x0, + + /* U+6F09 "漉" */ + 0x0, 0x0, 0x80, 0x80, 0xbf, 0xf8, 0x49, 0x0, + 0x92, 0x11, 0xff, 0x92, 0x49, 0x4, 0x92, 0xf, + 0xfc, 0x10, 0x1, 0x28, 0x82, 0x5d, 0xe4, 0xa2, + 0xa, 0x44, 0x64, 0xe8, 0xd9, 0x1e, + + /* U+6F0F "漏" */ + 0xcf, 0xfc, 0xd0, 0x8, 0x20, 0x10, 0x7f, 0xe8, + 0x80, 0x9, 0xff, 0xc2, 0x8, 0x4, 0x10, 0xb, + 0xfc, 0x94, 0x49, 0x2a, 0xb2, 0x91, 0x29, 0x2a, + 0xd2, 0x44, 0xa8, 0x8b, 0x0, + + /* U+6F13 "漓" */ + 0x0, 0x40, 0x80, 0x80, 0xbf, 0xf8, 0x0, 0x0, + 0x9f, 0x51, 0x16, 0x9a, 0x41, 0x3, 0xfe, 0x0, + 0x40, 0x0, 0x80, 0xbf, 0xf9, 0x49, 0x14, 0xbf, + 0x29, 0x61, 0x52, 0x0, 0xc4, 0xe, + + /* U+6F14 "演" */ + 0x0, 0x40, 0x80, 0x80, 0xbf, 0xf8, 0x40, 0x10, + 0xbf, 0xa8, 0x8, 0x1, 0xff, 0x2, 0x22, 0x27, + 0xfc, 0x48, 0x89, 0x11, 0x12, 0x3f, 0xe4, 0x0, + 0x8, 0x63, 0x27, 0x1, 0x80, + + /* U+6F15 "漕" */ + 0x41, 0x20, 0x5f, 0xfc, 0x4, 0x80, 0x7f, 0xe8, + 0x92, 0x4d, 0xff, 0x8a, 0x49, 0x7, 0xfe, 0x20, + 0x0, 0x4f, 0xf8, 0x90, 0x12, 0x3f, 0xe4, 0x40, + 0x48, 0xff, 0x81, 0x1, 0x0, + + /* U+6F20 "漠" */ + 0x2, 0x10, 0x84, 0x21, 0xbf, 0xf9, 0x10, 0x80, + 0x0, 0x11, 0xff, 0x92, 0x1, 0x7, 0xfe, 0x8, + 0x4, 0x1f, 0xf9, 0x2, 0x2, 0x4, 0x5, 0xff, + 0xe8, 0x18, 0x20, 0xcc, 0x4e, 0x7, + + /* U+6F24 "漤" */ + 0x42, 0x10, 0x22, 0x10, 0xf, 0xbe, 0x2, 0x18, + 0x7, 0xb4, 0x4a, 0x52, 0x2a, 0x50, 0x0, 0x80, + 0x2f, 0xfe, 0x21, 0x8, 0x22, 0x10, 0x41, 0xa0, + 0x40, 0x60, 0x43, 0x9c, 0x4c, 0x0, + + /* U+6F29 "漩" */ + 0x4, 0x20, 0x88, 0x80, 0x9, 0xf8, 0xfa, 0x8, + 0x88, 0x9, 0x1f, 0xc3, 0xc5, 0x4, 0x88, 0x9, + 0x50, 0x92, 0xbd, 0x25, 0x42, 0x4a, 0x85, 0x1b, + 0x12, 0x53, 0xe9, 0xa0, 0x0, + + /* U+6F2A "漪" */ + 0x0, 0x10, 0x49, 0x10, 0x2a, 0xfe, 0x4, 0x10, + 0xc, 0x38, 0x9c, 0xc6, 0x42, 0x0, 0x3, 0xff, + 0x6, 0x4, 0xa, 0xf4, 0x32, 0x94, 0x42, 0x94, + 0x42, 0xf4, 0x42, 0x4, 0x42, 0x4, 0x9c, 0x1c, + + /* U+6F2B "漫" */ + 0x47, 0xfc, 0x64, 0x4, 0x27, 0xfc, 0x4, 0x4, + 0x7, 0xfc, 0xc0, 0x0, 0x6f, 0xfe, 0x9, 0x22, + 0xf, 0xfe, 0x20, 0x0, 0x27, 0xfc, 0x42, 0x8, + 0x41, 0x30, 0x41, 0xe0, 0x9e, 0x1e, + + /* U+6F2D "漭" */ + 0x41, 0x10, 0x5f, 0xfc, 0x4, 0x60, 0x2, 0x48, + 0x8, 0x49, 0xff, 0xc8, 0x28, 0x0, 0x88, 0x6, + 0xc, 0x52, 0x24, 0xbf, 0xfa, 0x8, 0x84, 0x21, + 0x8, 0x42, 0x13, 0x4, 0x0, + + /* U+6F2F "漯" */ + 0x4f, 0xfc, 0x50, 0x88, 0x21, 0x10, 0x7f, 0xe8, + 0x84, 0x49, 0xff, 0x80, 0x30, 0x1, 0x88, 0x7, + 0xe0, 0x42, 0x0, 0xbf, 0xf2, 0x2, 0x4, 0x65, + 0x8b, 0x8, 0xe0, 0xf0, 0x0, + + /* U+6F31 "漱" */ + 0x42, 0x8, 0x22, 0x10, 0xf, 0xde, 0x2, 0x12, + 0x4f, 0xea, 0x6a, 0x68, 0x2a, 0x48, 0xa, 0x48, + 0xf, 0xd8, 0x22, 0x18, 0x25, 0x14, 0x26, 0xd4, + 0x4a, 0x24, 0x52, 0x22, 0x42, 0x40, + + /* U+6F33 "漳" */ + 0x0, 0x81, 0x80, 0x81, 0xbf, 0xf1, 0x10, 0x80, + 0x31, 0x3, 0xff, 0xf0, 0x0, 0x27, 0xfe, 0x8, + 0x4, 0x1f, 0xf9, 0x20, 0x12, 0x7f, 0xe4, 0x8, + 0xb, 0xff, 0xe0, 0x20, 0x40, 0x40, + + /* U+6F36 "漶" */ + 0x0, 0x40, 0x9f, 0xf8, 0xa1, 0x10, 0x42, 0x20, + 0x7f, 0x98, 0x8, 0xb, 0xff, 0x4, 0x22, 0xf, + 0xfc, 0x0, 0x81, 0xa, 0x2, 0x52, 0x24, 0xa1, + 0x52, 0x42, 0x60, 0x7c, 0x0, + + /* U+6F3E "漾" */ + 0x2, 0x8, 0xc2, 0x20, 0xff, 0xf8, 0x2, 0x0, + 0x7f, 0xd8, 0x8, 0xb, 0xff, 0x80, 0x60, 0x7, + 0xe0, 0x40, 0x88, 0xbd, 0xa2, 0x1a, 0x84, 0x64, + 0x89, 0x88, 0xd0, 0x70, 0x0, + + /* U+6F46 "潆" */ + 0x41, 0x10, 0x5f, 0xfc, 0x4, 0x40, 0x0, 0x0, + 0xff, 0xf9, 0x18, 0x4a, 0x42, 0x83, 0xf8, 0x0, + 0xd0, 0x46, 0x10, 0x9f, 0xf1, 0x2, 0x4, 0x24, + 0x89, 0x88, 0xd0, 0x70, 0x0, + + /* U+6F47 "潇" */ + 0x41, 0x8, 0x2f, 0xff, 0x1, 0x8, 0x0, 0x40, + 0x7, 0xfe, 0x40, 0x42, 0x2f, 0xff, 0x0, 0x42, + 0x7, 0xfe, 0x20, 0x40, 0x24, 0x42, 0x25, 0x52, + 0x45, 0x4a, 0x4a, 0x4a, 0x50, 0x42, + + /* U+6F4B "潋" */ + 0x2, 0x11, 0x8c, 0x20, 0x94, 0x80, 0x45, 0xf1, + 0x2, 0x55, 0xec, 0x90, 0x19, 0x2, 0x12, 0x15, + 0x28, 0xaa, 0x51, 0x54, 0x62, 0x48, 0x88, 0x23, + 0x93, 0xf4, 0xa0, 0x30, 0x80, 0x0, + + /* U+6F4D "潍" */ + 0x4, 0x40, 0x88, 0xa0, 0xa2, 0x20, 0x47, 0xf1, + 0x39, 0x1b, 0xb2, 0x2, 0x3f, 0x84, 0x48, 0x54, + 0x90, 0x91, 0xfd, 0x2, 0x42, 0x4, 0x88, 0xe9, + 0x12, 0x1f, 0xe0, 0x20, 0x0, + + /* U+6F58 "潘" */ + 0x0, 0x0, 0xcf, 0xf8, 0xc1, 0x0, 0x1a, 0x48, + 0xff, 0xe8, 0x1c, 0x8, 0x54, 0x3, 0x26, 0x2f, + 0xfc, 0x48, 0x89, 0x11, 0x12, 0x3f, 0xe4, 0x44, + 0x48, 0xff, 0xb1, 0x1, 0x0, + + /* U+6F5C "潜" */ + 0x2, 0x8, 0x42, 0x8, 0x2f, 0xbe, 0x2, 0x8, + 0xf, 0xbe, 0x42, 0x18, 0x25, 0xa4, 0x18, 0x2, + 0x7, 0xfc, 0x24, 0x4, 0x24, 0x4, 0x47, 0xfc, + 0x44, 0x4, 0x47, 0xfc, 0x44, 0x4, + + /* U+6F5E "潞" */ + 0x0, 0x10, 0x2f, 0x10, 0x9, 0x3e, 0x9, 0x22, + 0x9, 0x54, 0x4f, 0x8, 0x22, 0x1c, 0x2, 0x62, + 0xb, 0x80, 0x2a, 0x3e, 0x2a, 0x22, 0x4a, 0x22, + 0x4b, 0xa2, 0x5c, 0x3e, 0x40, 0x22, + + /* U+6F62 "潢" */ + 0x41, 0x10, 0x5f, 0xfc, 0x4, 0x40, 0x8, 0x80, + 0xff, 0xe8, 0x8, 0x9, 0xff, 0x2, 0x22, 0x27, + 0xfc, 0x48, 0x88, 0x91, 0x11, 0x3f, 0xe4, 0x0, + 0x8, 0x63, 0x13, 0x1, 0x80, + + /* U+6F66 "潦" */ + 0x0, 0x81, 0x2, 0x6, 0xff, 0xc8, 0x30, 0x5, + 0x2a, 0x8, 0x44, 0xff, 0xc7, 0x3, 0x4, 0x8, + 0x1f, 0xe4, 0x40, 0x91, 0xfe, 0x40, 0x81, 0x12, + 0x49, 0x88, 0xe0, 0xe0, + + /* U+6F6D "潭" */ + 0x0, 0x0, 0x9f, 0xfc, 0x84, 0x80, 0x7f, 0xe8, + 0x92, 0x49, 0xff, 0x88, 0x0, 0x3, 0xfe, 0x27, + 0xfc, 0x48, 0x9, 0x1f, 0xf2, 0x2, 0x5, 0xff, + 0xe8, 0x8, 0x20, 0x10, 0x0, + + /* U+6F6E "潮" */ + 0x84, 0x3d, 0xff, 0x10, 0x44, 0x41, 0x11, 0x8f, + 0x45, 0x45, 0xf0, 0xf4, 0x44, 0x51, 0x11, 0x45, + 0x3c, 0xf4, 0x44, 0x57, 0xf1, 0x44, 0x45, 0x11, + 0x18, 0x49, 0xc0, + + /* U+6F72 "潲" */ + 0x80, 0x10, 0xbe, 0xa4, 0x11, 0x50, 0x20, 0x89, + 0xf7, 0xe8, 0x90, 0x41, 0xa0, 0x87, 0x3f, 0xd, + 0x82, 0xa9, 0x5, 0x51, 0xfa, 0x24, 0x14, 0x48, + 0x30, 0x90, 0x61, 0x23, 0x80, + + /* U+6F74 "潴" */ + 0x0, 0x20, 0x4a, 0x22, 0x2a, 0xfa, 0x4, 0x24, + 0xc, 0x28, 0x55, 0xff, 0x22, 0x20, 0x6, 0x7e, + 0xb, 0xc2, 0x32, 0x42, 0x42, 0x7e, 0x42, 0x42, + 0x42, 0x42, 0x42, 0x7e, 0x5c, 0x42, + + /* U+6F78 "潸" */ + 0x2, 0x10, 0x84, 0x20, 0xbe, 0xf8, 0x10, 0x80, + 0x73, 0x91, 0xda, 0x96, 0x94, 0x90, 0x0, 0x7, + 0xfc, 0x8, 0x8, 0x9f, 0xf2, 0x20, 0x24, 0x7f, + 0xc8, 0x80, 0x91, 0x1, 0x42, 0x1e, + + /* U+6F7A "潺" */ + 0xcf, 0xfe, 0x68, 0x2, 0x28, 0x2, 0xf, 0xfe, + 0x8, 0x0, 0xc8, 0xf8, 0xb, 0xfe, 0x8, 0x20, + 0x48, 0xc0, 0x4b, 0xde, 0x48, 0x84, 0x59, 0x8, + 0x97, 0xbf, 0x91, 0x8, 0xa3, 0x18, + + /* U+6F7C "潼" */ + 0xc0, 0x80, 0xdf, 0xf8, 0x88, 0x40, 0x18, 0x81, + 0xff, 0xf8, 0x0, 0x1b, 0xff, 0x4, 0x42, 0xf, + 0xfc, 0x51, 0x9, 0x1f, 0xe2, 0x7f, 0xe4, 0x8, + 0x8, 0x10, 0x27, 0xff, 0x80, + + /* U+6F84 "澄" */ + 0x0, 0x0, 0x9e, 0x90, 0x84, 0xc0, 0x48, 0xa0, + 0x61, 0x90, 0xff, 0x96, 0x0, 0x83, 0xfc, 0x4, + 0x8, 0x48, 0x11, 0x1f, 0xe2, 0x10, 0x84, 0x12, + 0x13, 0xff, 0xc0, + + /* U+6F88 "澈" */ + 0x84, 0x10, 0x84, 0x40, 0x7e, 0x80, 0x41, 0xe0, + 0x92, 0x53, 0xfc, 0x90, 0x1a, 0x7, 0x94, 0x11, + 0x28, 0x9e, 0x31, 0x44, 0x42, 0x78, 0xc9, 0x12, + 0x92, 0x28, 0xa4, 0xd0, 0x0, + + /* U+6F89 "澉" */ + 0x0, 0x10, 0x9e, 0x40, 0x84, 0x80, 0xfd, 0xe8, + 0x94, 0x49, 0x28, 0x83, 0xea, 0x4, 0x94, 0x9, + 0x28, 0x5e, 0x31, 0x24, 0x42, 0x4c, 0xc5, 0xf2, + 0x90, 0x28, 0xa0, 0x50, 0x0, + + /* U+6F8C "澌" */ + 0x9, 0x0, 0x92, 0x78, 0x7f, 0x0, 0x4a, 0x8, + 0xf4, 0x9, 0x27, 0xc2, 0x52, 0x7, 0xa4, 0x9, + 0x48, 0x92, 0x91, 0x7f, 0x22, 0x2, 0x48, 0x94, + 0x92, 0x9, 0x24, 0x2, 0x0, + + /* U+6F8D "澍" */ + 0x84, 0x4, 0x88, 0x9, 0x7e, 0x10, 0x23, 0xf9, + 0xf8, 0x48, 0x0, 0x87, 0xf1, 0x8, 0x52, 0x10, + 0xa4, 0xbf, 0x9, 0x0, 0x12, 0x48, 0x28, 0xb0, + 0x53, 0xf0, 0xa0, 0xf, 0x0, + + /* U+6F8E "澎" */ + 0x82, 0x4, 0x7f, 0xc4, 0x2, 0x8, 0x2, 0x10, + 0x9f, 0xc2, 0x40, 0x2, 0x1f, 0xc4, 0x10, 0x48, + 0x10, 0x70, 0x5f, 0xc3, 0x40, 0x82, 0x48, 0x84, + 0x85, 0x8, 0x8f, 0xd0, 0x90, 0x20, + + /* U+6F9C "澜" */ + 0x0, 0x0, 0x89, 0xfc, 0x88, 0x8, 0x2, 0x18, + 0x9f, 0x29, 0x7f, 0x42, 0x92, 0x85, 0xb5, 0xa, + 0x4a, 0x57, 0xf5, 0x21, 0xa, 0x47, 0x94, 0xa4, + 0xb1, 0x8, 0x62, 0x13, 0x80, + + /* U+6FA1 "澡" */ + 0x43, 0xf8, 0x44, 0x10, 0x48, 0x20, 0x1f, 0xc0, + 0x0, 0x8, 0xf7, 0x8a, 0x29, 0x3, 0xde, 0x0, + 0x0, 0x40, 0x80, 0xbf, 0xf9, 0x7, 0x4, 0x15, + 0x8, 0xc9, 0x92, 0x10, 0x80, + + /* U+6FA7 "澧" */ + 0x1, 0x20, 0xdf, 0xf8, 0x24, 0x90, 0x7f, 0xe8, + 0x92, 0x4d, 0xff, 0x88, 0x0, 0xf, 0xff, 0x0, + 0x0, 0x4f, 0xf8, 0xa0, 0x12, 0x3f, 0xe4, 0x31, + 0xb, 0xff, 0xc0, + + /* U+6FB3 "澳" */ + 0x40, 0x80, 0xc1, 0x0, 0x5f, 0xf0, 0x22, 0x20, + 0x55, 0x48, 0x88, 0x89, 0x7d, 0x2, 0xb2, 0x6, + 0x54, 0x40, 0x80, 0xbf, 0xfa, 0x2, 0x4, 0x1a, + 0x8, 0xc3, 0x92, 0x0, 0x80, + + /* U+6FB6 "澶" */ + 0x0, 0x0, 0x80, 0x81, 0xbf, 0xf9, 0x0, 0x0, + 0xff, 0xc1, 0x7e, 0x92, 0x85, 0x14, 0xf2, 0xf, + 0xfc, 0x0, 0x0, 0x9f, 0xe1, 0x3f, 0xc4, 0x40, + 0x88, 0x7e, 0x17, 0xff, 0x80, + + /* U+6FB9 "澹" */ + 0x41, 0x0, 0x47, 0xf0, 0x58, 0x20, 0x7f, 0xf0, + 0x48, 0x8c, 0xa4, 0xc9, 0xff, 0x82, 0x0, 0x5, + 0xfe, 0x48, 0x0, 0x97, 0xf2, 0x4f, 0xf4, 0x90, + 0x29, 0x3f, 0xd4, 0x40, 0x80, + + /* U+6FC0 "激" */ + 0x4, 0x10, 0x88, 0x21, 0xbc, 0x40, 0x89, 0x1, + 0x13, 0xe1, 0xe4, 0xb4, 0x59, 0x7, 0xb2, 0x4, + 0x24, 0x3f, 0x51, 0x20, 0x62, 0x78, 0xc4, 0x91, + 0x92, 0x25, 0x24, 0x59, 0x5b, 0xe2, + + /* U+6FC2 "濂" */ + 0x0, 0x41, 0x80, 0x81, 0xbf, 0xf9, 0x48, 0x80, + 0x89, 0x1, 0x7f, 0xf2, 0x28, 0x35, 0xfe, 0x8, + 0xa4, 0x1f, 0xfc, 0xa2, 0x92, 0x5f, 0xe4, 0x8b, + 0xa, 0x35, 0x25, 0xa9, 0xd0, 0x50, + + /* U+6FC9 "濉" */ + 0x40, 0x30, 0x2f, 0x28, 0x9, 0x44, 0x9, 0x7f, + 0x9, 0x48, 0x4f, 0xc8, 0x29, 0x7e, 0x9, 0x48, + 0x29, 0x48, 0x2f, 0x7e, 0x29, 0x48, 0x49, 0x48, + 0x49, 0x48, 0x4f, 0x7f, 0x48, 0x40, + + /* U+6FD1 "濑" */ + 0x84, 0x20, 0x88, 0x78, 0x7d, 0x20, 0x24, 0x49, + 0xf7, 0xda, 0xa8, 0x95, 0x55, 0xa, 0xaa, 0x1f, + 0x54, 0x88, 0xa9, 0x39, 0x52, 0xa9, 0x8b, 0x42, + 0x90, 0x88, 0xa1, 0x20, 0x0, + + /* U+6FD2 "濒" */ + 0x2, 0x0, 0x6a, 0x7e, 0x2a, 0x8, 0xb, 0x8, + 0xa, 0x3e, 0xca, 0x22, 0x15, 0xaa, 0x2, 0x2a, + 0xa, 0xaa, 0x2a, 0xaa, 0x53, 0x2a, 0x51, 0x2a, + 0x42, 0x18, 0x4c, 0x34, 0x90, 0x43, + + /* U+6FDE "濞" */ + 0x0, 0x81, 0x82, 0x0, 0xbb, 0xf0, 0x7f, 0xe0, + 0x80, 0x41, 0xff, 0xb3, 0xff, 0x20, 0x0, 0xf, + 0xfc, 0x1f, 0xf9, 0x22, 0x12, 0x3b, 0xc5, 0xff, + 0xf0, 0x42, 0x21, 0x84, 0x4c, 0x8, + + /* U+6FE0 "濠" */ + 0x40, 0x40, 0x2f, 0xfe, 0x0, 0x0, 0x7, 0xfc, + 0x43, 0xfc, 0x60, 0x0, 0xf, 0xfe, 0xb, 0xfe, + 0x0, 0x80, 0x27, 0x5c, 0x21, 0xb0, 0x26, 0x68, + 0x49, 0xa4, 0x46, 0x22, 0x49, 0xc0, + + /* U+6FE1 "濡" */ + 0x47, 0xfc, 0x40, 0x80, 0x3f, 0xf8, 0x42, 0x10, + 0xb5, 0xa8, 0x8, 0x8, 0x82, 0x7, 0xff, 0x0, + 0x80, 0x41, 0x0, 0x9f, 0xf2, 0x49, 0x24, 0x92, + 0x49, 0x24, 0x92, 0x4b, 0x0, + + /* U+6FEE "濮" */ + 0x4, 0x50, 0x8a, 0xa8, 0x95, 0x50, 0x42, 0x80, + 0xbf, 0xf3, 0x11, 0x16, 0x14, 0x14, 0xfe, 0x8, + 0x20, 0x93, 0xf9, 0x20, 0x82, 0x41, 0x4, 0xbf, + 0xf1, 0xc, 0x22, 0x36, 0x45, 0x83, + + /* U+6FEF "濯" */ + 0x0, 0x0, 0x9f, 0x7c, 0x82, 0x8, 0x34, 0xd0, + 0x8, 0x39, 0xd7, 0x48, 0x98, 0x81, 0xff, 0x4, + 0x20, 0x7f, 0xfd, 0x10, 0x82, 0x3f, 0xf4, 0x42, + 0x8, 0xff, 0xe1, 0x0, 0x0, + + /* U+7011 "瀑" */ + 0x8f, 0xfc, 0xd0, 0x8, 0x3f, 0xf0, 0x40, 0x28, + 0xff, 0xc8, 0x44, 0x7, 0xff, 0x1, 0x10, 0x1f, + 0xfe, 0x84, 0x21, 0x3a, 0x63, 0x85, 0x38, 0x7b, + 0x13, 0x11, 0xa0, 0xe0, 0x0, + + /* U+701A "瀚" */ + 0x44, 0x10, 0x3f, 0x28, 0x4, 0x24, 0x4, 0x42, + 0xf, 0x80, 0x51, 0xee, 0xf, 0x22, 0x11, 0xaa, + 0x11, 0xaa, 0x2f, 0x22, 0x44, 0x26, 0x5f, 0xea, + 0x44, 0x22, 0x44, 0x22, 0x84, 0xee, + + /* U+701B "瀛" */ + 0x0, 0x0, 0x80, 0x40, 0x5f, 0xfe, 0x8, 0x0, + 0xf, 0xfc, 0x80, 0x0, 0x4f, 0xfc, 0xf, 0xfc, + 0x0, 0x80, 0x5f, 0xfc, 0x52, 0xb4, 0x5f, 0x5c, + 0x53, 0xdc, 0x5e, 0x55, 0x92, 0xa5, 0xa7, 0x26, + + /* U+7023 "瀣" */ + 0x2, 0x0, 0x87, 0xf8, 0xa9, 0x10, 0xfd, 0x49, + 0x13, 0x9, 0x45, 0x87, 0x70, 0x88, 0xa0, 0x1f, + 0x3c, 0x42, 0x81, 0x7c, 0xf2, 0xa, 0x4, 0xf7, + 0x93, 0xff, 0xc0, 0x0, 0x0, + + /* U+7035 "瀵" */ + 0xc, 0x88, 0xc5, 0x20, 0xbf, 0xf8, 0xd, 0x1, + 0xe1, 0xf9, 0xff, 0x9a, 0x21, 0x7, 0xfe, 0x8, + 0x84, 0x4e, 0xf1, 0x3f, 0xf2, 0x10, 0x85, 0xff, + 0xe8, 0x0, 0x27, 0x87, 0x80, + + /* U+7039 "瀹" */ + 0x0, 0x40, 0xc1, 0x40, 0x8c, 0x40, 0x6f, 0xe1, + 0x0, 0x19, 0xdd, 0x9a, 0xad, 0x7, 0x76, 0x0, + 0x0, 0x5f, 0xf9, 0x24, 0x92, 0x7f, 0xe4, 0x92, + 0x49, 0x24, 0xa2, 0x4b, 0x0, + + /* U+704C "灌" */ + 0x41, 0x10, 0xdf, 0xfc, 0x84, 0x40, 0x3d, 0xf8, + 0x8a, 0x28, 0xf7, 0xc8, 0x90, 0x3, 0xff, 0x2c, + 0x20, 0xaf, 0xfd, 0x10, 0x82, 0x3f, 0xf4, 0x42, + 0x8, 0xff, 0xc1, 0x0, 0x0, + + /* U+704F "灏" */ + 0x4f, 0xbe, 0x28, 0x88, 0xf, 0x90, 0xf, 0xae, + 0x42, 0x22, 0x1f, 0xea, 0x0, 0x2a, 0xf, 0xaa, + 0x28, 0xaa, 0x4f, 0xaa, 0x42, 0x2a, 0x4a, 0x94, + 0x52, 0x12, 0x8e, 0x22, + + /* U+705E "灞" */ + 0xcf, 0xf9, 0x82, 0x1, 0xff, 0xc5, 0xed, 0x90, + 0x85, 0x8, 0xc1, 0xfb, 0xc2, 0x51, 0x6, 0x3d, + 0x3f, 0x15, 0x2c, 0x53, 0xdf, 0x5f, 0xc6, 0x9, + 0x18, 0x29, 0xc0, + + /* U+706B "火" */ + 0x1, 0x0, 0x2, 0x0, 0x4, 0x3, 0x8, 0x22, + 0x10, 0x82, 0x22, 0x2, 0x48, 0x0, 0xd0, 0x2, + 0x80, 0x4, 0x80, 0x11, 0x0, 0x41, 0x1, 0x1, + 0xc, 0x1, 0xa0, 0x0, 0x80, + + /* U+706C "灬" */ + 0x40, 0x89, 0x22, 0x2c, 0x48, 0x61, 0x11, + + /* U+706D "灭" */ + 0xff, 0xfe, 0x2, 0x0, 0x4, 0x2, 0x8, 0x26, + 0x10, 0x44, 0x21, 0x4, 0xa4, 0x1, 0x40, 0x2, + 0x80, 0x8, 0x80, 0x30, 0x80, 0xc0, 0x82, 0x0, + 0xd8, 0x0, 0x40, + + /* U+706F "灯" */ + 0x10, 0x0, 0x23, 0xfc, 0x40, 0x44, 0xa0, 0x85, + 0x41, 0xa, 0x82, 0x16, 0x4, 0x8, 0x8, 0x10, + 0x10, 0x30, 0x20, 0xa0, 0x41, 0x20, 0x82, 0x41, + 0x8, 0x2, 0x20, 0x3c, 0x0, + + /* U+7070 "灰" */ + 0x8, 0x0, 0x10, 0x1, 0xff, 0xf8, 0x40, 0x0, + 0x84, 0x1, 0x8, 0x3, 0x11, 0x9, 0x24, 0x12, + 0x48, 0x21, 0x40, 0x82, 0x81, 0x8, 0x84, 0x30, + 0x90, 0x80, 0xc2, 0x0, 0x80, + + /* U+7075 "灵" */ + 0x7f, 0xf8, 0x0, 0x10, 0xff, 0xe0, 0x0, 0x40, + 0x0, 0x8f, 0xff, 0x0, 0x0, 0x0, 0x84, 0x21, + 0x18, 0x26, 0x60, 0x12, 0x0, 0x43, 0x3, 0x1, + 0x98, 0x0, 0x80, + + /* U+7076 "灶" */ + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x92, 0x10, + 0x92, 0x10, 0x54, 0xfe, 0x54, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x18, 0x10, 0x24, 0x10, 0x22, 0x10, + 0x40, 0x10, 0x41, 0xff, + + /* U+7078 "灸" */ + 0x0, 0x0, 0x4, 0x0, 0x1f, 0xe0, 0xc0, 0x86, + 0x2, 0x0, 0x18, 0x1, 0xde, 0x3c, 0x3, 0x0, + 0x80, 0x42, 0x8, 0x46, 0x20, 0xc, 0x0, 0x24, + 0x0, 0x86, 0xc, 0x3, 0x80, 0x0, + + /* U+707C "灼" */ + 0x0, 0x40, 0x20, 0x80, 0x42, 0x0, 0x97, 0xf5, + 0x48, 0x2a, 0xa0, 0x55, 0x40, 0xa8, 0x1, 0x10, + 0x42, 0x20, 0x84, 0x60, 0x89, 0x20, 0x12, 0x40, + 0x24, 0x0, 0x50, 0x1, 0x80, 0x3e, + + /* U+707E "灾" */ + 0x1, 0x0, 0x2, 0x3, 0xff, 0xf4, 0x0, 0x28, + 0x10, 0x40, 0x20, 0x10, 0x42, 0x10, 0x88, 0x12, + 0xa0, 0x5, 0x0, 0x19, 0x0, 0x63, 0x1, 0x83, + 0xc, 0x1, 0xa0, 0x0, 0x0, + + /* U+707F "灿" */ + 0x20, 0x20, 0x80, 0x82, 0x2, 0xa, 0x8, 0xaa, + 0x26, 0xa8, 0x9a, 0x22, 0x48, 0x89, 0x22, 0x24, + 0xc8, 0x95, 0x22, 0x52, 0x89, 0x42, 0x26, 0xf, + 0xf8, 0x0, 0x40, + + /* U+7080 "炀" */ + 0x10, 0x0, 0x23, 0xfc, 0x40, 0x10, 0x80, 0xc5, + 0x46, 0xa, 0xb0, 0x14, 0x7f, 0x88, 0x25, 0x10, + 0x92, 0x21, 0x24, 0xac, 0x49, 0x41, 0x12, 0x46, + 0x28, 0x18, 0x50, 0x67, 0x0, + + /* U+7085 "炅" */ + 0x3f, 0xf8, 0x40, 0x10, 0x80, 0x21, 0xff, 0xc2, + 0x0, 0x84, 0x1, 0xf, 0xfe, 0x0, 0x0, 0x1, + 0x0, 0x42, 0x10, 0x4a, 0x40, 0x16, 0x0, 0x46, + 0x7, 0x3, 0x30, 0x1, 0x80, + + /* U+7089 "炉" */ + 0x10, 0x10, 0x20, 0x20, 0x43, 0xfa, 0x94, 0x15, + 0x48, 0x2a, 0x90, 0x44, 0x20, 0x88, 0x7f, 0x18, + 0x82, 0x59, 0x0, 0x92, 0x1, 0x8, 0x4, 0x10, + 0x8, 0x40, 0x0, 0x80, 0x0, + + /* U+708A "炊" */ + 0x10, 0x40, 0x10, 0x80, 0x92, 0xfe, 0x94, 0x82, + 0x55, 0x22, 0x55, 0x22, 0x50, 0x20, 0x10, 0x20, + 0x10, 0x20, 0x28, 0x50, 0x24, 0x50, 0x26, 0x48, + 0x40, 0x84, 0xc3, 0x2, 0x2, 0x0, + + /* U+708E "炎" */ + 0x1, 0x0, 0x82, 0x0, 0xc4, 0x60, 0x88, 0x0, + 0x2c, 0x1, 0x87, 0x1c, 0x41, 0x80, 0x80, 0x1, + 0x8, 0x62, 0x30, 0x4a, 0x80, 0x16, 0x0, 0xc6, + 0x6, 0x3, 0x30, 0x1, 0x80, + + /* U+7092 "炒" */ + 0x10, 0x10, 0x10, 0x10, 0x10, 0x14, 0x52, 0x92, + 0x54, 0x92, 0x55, 0x11, 0x11, 0x10, 0x10, 0x12, + 0x18, 0x12, 0x24, 0x4, 0x22, 0x8, 0x20, 0x30, + 0x40, 0xc0, 0xc3, 0x0, + + /* U+7094 "炔" */ + 0x20, 0x80, 0x41, 0x0, 0x82, 0x5, 0x5f, 0xca, + 0x88, 0x95, 0x11, 0x8, 0x22, 0x10, 0x44, 0x27, + 0xfe, 0x61, 0x80, 0xc5, 0x2, 0x49, 0x4, 0x21, + 0x10, 0x81, 0xa2, 0x0, 0x80, + + /* U+7095 "炕" */ + 0x20, 0x40, 0x20, 0x20, 0x23, 0xfe, 0xa8, 0x0, + 0xa8, 0x0, 0xa9, 0xf8, 0x21, 0x8, 0x21, 0x8, + 0x21, 0x8, 0x31, 0x8, 0x51, 0x8, 0x49, 0x9, + 0x4a, 0x9, 0x86, 0xa, 0x8c, 0xe, + + /* U+7096 "炖" */ + 0x20, 0x40, 0x40, 0x80, 0x9f, 0xf1, 0x42, 0xa, + 0x84, 0x15, 0x89, 0x2d, 0x12, 0x12, 0x24, 0x24, + 0x48, 0x47, 0xf1, 0x41, 0x2, 0x82, 0x14, 0x84, + 0x50, 0x8, 0xa0, 0x1f, 0x0, + + /* U+7099 "炙" */ + 0x0, 0x0, 0x8, 0x0, 0x3f, 0xf1, 0x80, 0x44, + 0x23, 0x6, 0x38, 0x2, 0xc0, 0xe, 0x0, 0xc1, + 0x0, 0x42, 0x10, 0xcc, 0x40, 0x94, 0x0, 0x44, + 0x1, 0x6, 0x1c, 0x3, 0x40, 0x0, + + /* U+709C "炜" */ + 0x20, 0x80, 0x82, 0x2, 0x7f, 0xca, 0x20, 0xa8, + 0x82, 0xdf, 0xe2, 0x8, 0x8, 0x20, 0x27, 0xfc, + 0xc2, 0x15, 0x8, 0x52, 0x21, 0x40, 0xba, 0x2, + 0x8, 0x8, 0x0, + + /* U+709D "炝" */ + 0x10, 0x10, 0x10, 0x30, 0x10, 0x48, 0x54, 0xc4, + 0x55, 0x6, 0x5a, 0x1, 0x51, 0xfc, 0x11, 0x4, + 0x11, 0x4, 0x31, 0x4, 0x29, 0x39, 0x25, 0x1, + 0x41, 0x1, 0x41, 0x3, 0x80, 0xfe, + + /* U+70AB "炫" */ + 0x10, 0x10, 0x10, 0x10, 0x11, 0xff, 0x12, 0x20, + 0x54, 0x40, 0x54, 0x84, 0x10, 0x8c, 0x11, 0xf8, + 0x10, 0x10, 0x28, 0x20, 0x24, 0x44, 0x26, 0x44, + 0x20, 0x82, 0x41, 0xfe, 0x0, 0x1, + + /* U+70AC "炬" */ + 0x10, 0x0, 0x11, 0xff, 0x11, 0x0, 0x11, 0x0, + 0x55, 0x0, 0x55, 0xfe, 0x51, 0x2, 0x11, 0x2, + 0x11, 0x2, 0x19, 0xfe, 0x29, 0x0, 0x25, 0x0, + 0x41, 0x0, 0x41, 0xff, + + /* U+70AD "炭" */ + 0x0, 0x80, 0x41, 0x8, 0x82, 0x11, 0xff, 0xe0, + 0x80, 0xf, 0xff, 0xc2, 0x0, 0x4, 0x20, 0x14, + 0x44, 0x24, 0x90, 0x8a, 0xa1, 0x4, 0x84, 0x30, + 0x99, 0x80, 0xc0, 0x0, 0x0, + + /* U+70AE "炮" */ + 0x21, 0x0, 0x42, 0x0, 0x87, 0xf1, 0x50, 0x2a, + 0xc0, 0x56, 0xfc, 0xa8, 0x89, 0x11, 0x12, 0x23, + 0xe4, 0x64, 0x9, 0x49, 0xea, 0x50, 0x14, 0x20, + 0x30, 0x40, 0x60, 0x7f, 0x0, + + /* U+70AF "炯" */ + 0x20, 0x0, 0x8f, 0xf2, 0x20, 0x68, 0x81, 0xaa, + 0xf6, 0xaa, 0x53, 0x29, 0x48, 0xa5, 0x22, 0x94, + 0x8a, 0x55, 0x2f, 0x52, 0x81, 0x4a, 0x6, 0x8, + 0x18, 0x23, 0xc0, + + /* U+70B1 "炱" */ + 0x6, 0x0, 0x10, 0xc0, 0xc0, 0x43, 0xff, 0xe0, + 0x0, 0x27, 0xff, 0x8, 0x2, 0x10, 0x4, 0x3f, + 0xf8, 0x0, 0x0, 0x84, 0x20, 0x89, 0x80, 0x28, + 0x3, 0x8e, 0x38, 0x3, 0x80, + + /* U+70B3 "炳" */ + 0x20, 0x0, 0x4f, 0xfc, 0x81, 0x5, 0x42, 0xb, + 0x3f, 0xd6, 0x88, 0x89, 0x11, 0x12, 0x22, 0x24, + 0xa4, 0x69, 0x29, 0x54, 0x32, 0x68, 0x24, 0xc0, + 0x50, 0x80, 0xa1, 0xf, 0x0, + + /* U+70B7 "炷" */ + 0x10, 0x20, 0x10, 0x10, 0x10, 0x10, 0x11, 0xff, + 0x54, 0x10, 0x54, 0x10, 0x10, 0x10, 0x11, 0xfe, + 0x18, 0x10, 0x28, 0x10, 0x24, 0x10, 0x22, 0x10, + 0x40, 0x10, 0x43, 0xff, 0x0, 0x0, + + /* U+70B8 "炸" */ + 0x0, 0x80, 0x10, 0x80, 0x10, 0x80, 0x15, 0xff, + 0x55, 0x40, 0x56, 0x40, 0x52, 0x40, 0x10, 0x7e, + 0x10, 0x40, 0x10, 0x40, 0x28, 0x40, 0x28, 0x7e, + 0x24, 0x40, 0x44, 0x40, 0x40, 0x40, 0x0, 0x40, + + /* U+70B9 "点" */ + 0x1, 0x0, 0x2, 0x0, 0x7, 0xf8, 0x8, 0x0, + 0x10, 0x7, 0xff, 0x88, 0x1, 0x10, 0x2, 0x20, + 0x4, 0x7f, 0xf8, 0x0, 0x1, 0x2, 0x24, 0x44, + 0x48, 0x84, 0x60, 0x0, 0x0, + + /* U+70BB "炻" */ + 0x10, 0x0, 0x11, 0xff, 0x10, 0x20, 0x14, 0x20, + 0x54, 0x40, 0x54, 0x40, 0x50, 0x80, 0x10, 0xfe, + 0x11, 0x82, 0x12, 0x82, 0x2a, 0x82, 0x24, 0x82, + 0x24, 0x82, 0x40, 0xfe, 0x40, 0x82, + + /* U+70BC "炼" */ + 0x20, 0x80, 0x82, 0x2, 0x7f, 0xc8, 0x40, 0xa9, + 0x2, 0xbf, 0x8b, 0x22, 0x9, 0x8, 0x27, 0xfc, + 0xc0, 0x85, 0x2, 0x12, 0x8a, 0x44, 0x26, 0x10, + 0x88, 0x1c, 0x0, + + /* U+70BD "炽" */ + 0x20, 0x0, 0x47, 0xf8, 0x88, 0x11, 0x50, 0x2a, + 0xa0, 0x55, 0x40, 0xa8, 0x81, 0x11, 0x2, 0x23, + 0xfc, 0x60, 0x1, 0x20, 0x42, 0x48, 0x44, 0x30, + 0xd0, 0x40, 0xa1, 0x0, 0x80, + + /* U+70C0 "烀" */ + 0x10, 0x0, 0x13, 0xfe, 0x10, 0x20, 0x15, 0x22, + 0x55, 0x22, 0x54, 0xa4, 0x50, 0xa4, 0x10, 0x20, + 0x13, 0xff, 0x10, 0x20, 0x28, 0x20, 0x24, 0x20, + 0x24, 0x20, 0x40, 0x20, 0x41, 0xe0, + + /* U+70C1 "烁" */ + 0x20, 0x0, 0x47, 0xf8, 0x88, 0x5, 0x51, 0xa, + 0xa2, 0x15, 0x44, 0x8, 0x88, 0x11, 0xff, 0x20, + 0x20, 0x62, 0x41, 0x48, 0x92, 0x51, 0x24, 0x42, + 0x30, 0x84, 0x20, 0x78, 0x0, + + /* U+70C2 "烂" */ + 0x0, 0x0, 0x10, 0x84, 0x10, 0x44, 0x10, 0x48, + 0x55, 0xff, 0x54, 0x0, 0x54, 0x0, 0x10, 0x0, + 0x10, 0x0, 0x11, 0xfe, 0x18, 0x0, 0x24, 0x0, + 0x24, 0x0, 0x20, 0x0, 0x43, 0xff, 0x0, 0x0, + + /* U+70C3 "烃" */ + 0x10, 0x0, 0x11, 0xfe, 0x10, 0x4, 0x12, 0x8, + 0x54, 0x10, 0x54, 0x6c, 0x13, 0x83, 0x10, 0x0, + 0x19, 0xfe, 0x18, 0x10, 0x24, 0x10, 0x22, 0x10, + 0x40, 0x10, 0x43, 0xff, 0x0, 0x0, + + /* U+70C8 "烈" */ + 0x0, 0x4, 0xff, 0x8, 0x20, 0x90, 0x41, 0x21, + 0xfa, 0x44, 0x24, 0x9c, 0x49, 0x5, 0x12, 0x4, + 0x4, 0x10, 0x8, 0xc0, 0xf2, 0x0, 0x2, 0x44, + 0x88, 0x88, 0x90, 0x89, 0x80, 0x0, + + /* U+70CA "烊" */ + 0x21, 0x8, 0x42, 0x30, 0x80, 0x41, 0x7f, 0xea, + 0x84, 0x16, 0x8, 0x8, 0xff, 0x10, 0x20, 0x20, + 0x40, 0x40, 0x81, 0x5f, 0xfa, 0x82, 0x8, 0x84, + 0x10, 0x8, 0x0, 0x10, 0x0, + + /* U+70D8 "烘" */ + 0x21, 0x10, 0x42, 0x20, 0x84, 0x45, 0x5f, 0xea, + 0x91, 0x16, 0x22, 0x8, 0x44, 0x10, 0x88, 0x21, + 0x10, 0x6f, 0xfd, 0x40, 0x2, 0x44, 0x84, 0x90, + 0x90, 0xc0, 0xa1, 0x0, 0x80, + + /* U+70D9 "烙" */ + 0x20, 0x80, 0x41, 0x0, 0x87, 0xf1, 0x48, 0x2a, + 0xb8, 0x95, 0x92, 0x2c, 0x18, 0x10, 0x78, 0x23, + 0xe, 0x48, 0x0, 0xcf, 0xf2, 0x90, 0x24, 0xa0, + 0x49, 0x40, 0xa0, 0xff, 0x1, 0x2, + + /* U+70DB "烛" */ + 0x20, 0x40, 0x40, 0x80, 0x81, 0x1, 0x5f, 0xea, + 0xa4, 0x55, 0x48, 0x88, 0x91, 0x11, 0x22, 0x23, + 0xfc, 0x64, 0x81, 0x61, 0x2, 0x42, 0x44, 0x4, + 0x50, 0xff, 0x60, 0x0, 0x80, + + /* U+70DF "烟" */ + 0x20, 0x0, 0x9f, 0xf2, 0x44, 0x6b, 0x11, 0xb4, + 0x46, 0xd7, 0xd3, 0x44, 0x49, 0x11, 0x24, 0x64, + 0x92, 0x95, 0x59, 0x55, 0x41, 0x44, 0x6, 0x1f, + 0xf0, 0x40, 0x40, + + /* U+70E4 "烤" */ + 0x20, 0x80, 0x41, 0x8, 0x8f, 0xd1, 0x44, 0x4a, + 0x89, 0x16, 0xff, 0xcc, 0x10, 0x10, 0xfe, 0x27, + 0x0, 0x42, 0x1, 0x47, 0xf2, 0x80, 0x24, 0x80, + 0x50, 0x1, 0x20, 0x3e, 0x0, + + /* U+70E6 "烦" */ + 0x20, 0x0, 0x9f, 0xf2, 0x4, 0xa, 0x20, 0xab, + 0xfa, 0xa8, 0x2a, 0x24, 0x88, 0x92, 0x22, 0x48, + 0x89, 0x23, 0x24, 0x92, 0xb8, 0x48, 0x92, 0xc, + 0x28, 0xc0, 0x40, + + /* U+70E7 "烧" */ + 0x21, 0x0, 0x42, 0x10, 0x8f, 0xc1, 0x44, 0x4a, + 0x89, 0x16, 0xc, 0x88, 0xe9, 0x10, 0xe, 0x20, + 0x0, 0x6f, 0xf9, 0x44, 0x82, 0x49, 0x4, 0x12, + 0x50, 0x44, 0xa3, 0x7, 0x0, + + /* U+70E8 "烨" */ + 0x21, 0x20, 0x42, 0x48, 0x88, 0xa5, 0x51, 0x8b, + 0x66, 0x16, 0x54, 0x48, 0x88, 0x91, 0xe, 0x22, + 0x0, 0x40, 0x81, 0x5f, 0xfa, 0x42, 0x4, 0x84, + 0x10, 0x8, 0x20, 0x10, 0x0, + + /* U+70E9 "烩" */ + 0x10, 0x20, 0x20, 0x40, 0x41, 0x40, 0xa4, 0x45, + 0x48, 0x4b, 0x20, 0x54, 0xbe, 0x8, 0x0, 0x10, + 0x0, 0x27, 0xfc, 0xa1, 0x1, 0x24, 0x46, 0x10, + 0x48, 0x3f, 0xa0, 0x80, 0x80, + + /* U+70EB "烫" */ + 0x0, 0x0, 0x6f, 0xf0, 0x0, 0xc6, 0x1e, 0x2, + 0x7f, 0xc0, 0x24, 0x8c, 0x91, 0x32, 0x22, 0xd8, + 0xb8, 0x0, 0x0, 0x84, 0x20, 0x89, 0x80, 0x28, + 0x1, 0x8c, 0x3c, 0x7, 0x80, + + /* U+70EC "烬" */ + 0x20, 0x0, 0x47, 0xf8, 0x88, 0x11, 0x10, 0x2a, + 0xa0, 0x55, 0x7f, 0xac, 0x88, 0x11, 0x8, 0x24, + 0x10, 0x48, 0x18, 0x83, 0x8a, 0x80, 0x84, 0x80, + 0x8, 0x70, 0x20, 0x1e, 0x0, 0x0, + + /* U+70ED "热" */ + 0x20, 0x80, 0x41, 0x0, 0x82, 0x7, 0xff, 0x82, + 0x9, 0x4, 0x12, 0xe, 0xe4, 0x70, 0xc8, 0x23, + 0x52, 0x44, 0x2b, 0xb0, 0x30, 0x0, 0x4, 0x88, + 0x89, 0x8, 0xa1, 0x11, 0x0, + + /* U+70EF "烯" */ + 0x20, 0x0, 0x46, 0x10, 0x83, 0xc1, 0x4c, 0xca, + 0xa0, 0x55, 0x10, 0x2d, 0xff, 0x90, 0x80, 0x22, + 0x40, 0x47, 0xf9, 0x59, 0x12, 0x92, 0x24, 0xa4, + 0x58, 0x48, 0xa0, 0x17, 0x0, 0x20, + + /* U+70F7 "烷" */ + 0x20, 0x20, 0x40, 0x40, 0x8f, 0xfd, 0x10, 0x1a, + 0x80, 0x15, 0x1f, 0x8, 0x0, 0x10, 0x0, 0x23, + 0xfe, 0x41, 0x21, 0x42, 0x42, 0x44, 0x84, 0xc9, + 0x30, 0x22, 0x61, 0x83, 0x80, + + /* U+70F9 "烹" */ + 0x2, 0x3, 0xff, 0xf0, 0x0, 0xf, 0xfc, 0x20, + 0x10, 0xff, 0xc0, 0x0, 0x1f, 0xfc, 0x0, 0x60, + 0x6, 0x0, 0x10, 0x3, 0x80, 0x40, 0x19, 0x22, + 0x28, 0x44, 0x40, + + /* U+70FD "烽" */ + 0x20, 0x80, 0x43, 0xf8, 0x8c, 0x21, 0x58, 0xca, + 0x8b, 0x15, 0x1c, 0x2d, 0xc7, 0x91, 0xfe, 0x20, + 0x40, 0x60, 0x81, 0x4f, 0xf2, 0x42, 0x4, 0x7f, + 0xf0, 0x8, 0x20, 0x10, 0x0, + + /* U+7109 "焉" */ + 0x7f, 0xfe, 0x42, 0x0, 0x87, 0xf1, 0x8, 0xf, + 0xff, 0xe4, 0x0, 0xf, 0xff, 0x10, 0x0, 0x3f, + 0xfc, 0x0, 0x9, 0x24, 0x92, 0x48, 0xa8, 0x0, + 0x40, 0x1f, 0x0, + + /* U+710A "焊" */ + 0x20, 0x0, 0x47, 0xf8, 0x88, 0x15, 0x5f, 0xea, + 0xa0, 0x55, 0x40, 0xac, 0xff, 0x10, 0x0, 0x23, + 0xfc, 0x40, 0x81, 0x41, 0x2, 0x7f, 0xf4, 0x84, + 0x10, 0x8, 0x20, 0x10, 0x0, + + /* U+7110 "焐" */ + 0x20, 0x0, 0x4f, 0xfc, 0x82, 0x5, 0x7f, 0xca, + 0x90, 0x96, 0x21, 0x8, 0x42, 0x13, 0xff, 0x20, + 0x0, 0xa7, 0xf9, 0x48, 0x12, 0x50, 0x24, 0x20, + 0x50, 0x7f, 0xa0, 0x81, 0x0, + + /* U+7113 "焓" */ + 0x20, 0x20, 0x40, 0xc0, 0x82, 0x45, 0x4a, 0x6a, + 0xe2, 0x36, 0x0, 0x8, 0xfe, 0x10, 0x4, 0x20, + 0x10, 0x67, 0xf9, 0x48, 0x12, 0x50, 0x24, 0x20, + 0x50, 0x7f, 0xa0, 0x81, 0x0, + + /* U+7115 "焕" */ + 0x20, 0x0, 0x41, 0x0, 0x83, 0xe1, 0x8, 0x4a, + 0xa1, 0x15, 0xc2, 0x2a, 0xff, 0x19, 0x12, 0x22, + 0x24, 0x44, 0x49, 0x5f, 0xfa, 0x43, 0x4, 0x85, + 0x8, 0x13, 0x20, 0xc1, 0x2, 0x1, + + /* U+7116 "焖" */ + 0x22, 0x0, 0x46, 0xfc, 0x84, 0xd, 0x62, 0x1b, + 0x44, 0x36, 0x84, 0x4d, 0x50, 0x92, 0xa5, 0x25, + 0x4a, 0x6a, 0xad, 0xd1, 0x4a, 0x62, 0x94, 0x46, + 0x30, 0x80, 0x61, 0x7, 0x80, + + /* U+7118 "焘" */ + 0x0, 0x0, 0x2, 0x1, 0xff, 0xf8, 0x10, 0x3, + 0xff, 0xc0, 0xc0, 0x3f, 0xff, 0x84, 0x8, 0x18, + 0x10, 0x7f, 0xfd, 0x80, 0x46, 0x30, 0x80, 0x27, + 0x0, 0x0, 0x11, 0x11, 0x21, 0x11, + + /* U+7119 "焙" */ + 0x20, 0x40, 0x40, 0x40, 0x8f, 0xfd, 0x40, 0xa, + 0x90, 0x96, 0x2, 0x9, 0xff, 0x90, 0x0, 0x20, + 0x0, 0x47, 0xf9, 0x48, 0x12, 0x50, 0x24, 0x20, + 0x50, 0x7f, 0xa0, 0x81, 0x0, + + /* U+711A "焚" */ + 0x8, 0x10, 0x8, 0x10, 0x7f, 0x7e, 0x8, 0x10, + 0x1e, 0x38, 0x29, 0x54, 0x48, 0x93, 0x8, 0x10, + 0x0, 0x80, 0x10, 0x84, 0x8, 0x88, 0x1, 0x40, + 0x2, 0x20, 0xc, 0x18, 0x60, 0x6, + + /* U+7126 "焦" */ + 0x10, 0x80, 0x21, 0x0, 0xff, 0xf1, 0x4, 0x6, + 0x8, 0x17, 0xff, 0x88, 0x20, 0x1f, 0xfe, 0x20, + 0x80, 0x41, 0x0, 0xff, 0xf1, 0x0, 0x2, 0x44, + 0x88, 0x88, 0xa1, 0x0, 0x0, + + /* U+712F "焯" */ + 0x20, 0x40, 0x40, 0xf8, 0x81, 0x5, 0x42, 0xa, + 0xbf, 0xd6, 0x40, 0x88, 0xff, 0x11, 0x2, 0x22, + 0x4, 0x47, 0xf9, 0x41, 0x2, 0xbf, 0xf4, 0x84, + 0x10, 0x8, 0x20, 0x10, 0x0, + + /* U+7130 "焰" */ + 0x20, 0x40, 0x82, 0x2, 0xf, 0xc9, 0x42, 0xaa, + 0x12, 0xa0, 0x2, 0x3b, 0xc8, 0x81, 0x22, 0x4, + 0x88, 0x15, 0x3f, 0xd2, 0x81, 0x42, 0x6, 0xf, + 0xf8, 0x20, 0x40, + + /* U+7131 "焱" */ + 0x1, 0x0, 0x82, 0x10, 0xcc, 0x40, 0x14, 0x0, + 0x44, 0x7, 0x7, 0x30, 0x1, 0x8, 0x10, 0x94, + 0x24, 0xa9, 0x51, 0x40, 0xc1, 0xc2, 0x82, 0x44, + 0x88, 0x30, 0xa0, 0x40, 0x0, + + /* U+7136 "然" */ + 0x8, 0x20, 0x20, 0x50, 0x7c, 0x91, 0x9, 0x3, + 0xaf, 0xe8, 0x44, 0x2d, 0x18, 0x2, 0x28, 0x8, + 0x88, 0x23, 0x9, 0x8c, 0x8, 0x0, 0x2, 0x44, + 0x48, 0x48, 0xd0, 0x80, 0x80, + + /* U+7145 "煅" */ + 0x20, 0x0, 0x23, 0xb8, 0x22, 0x28, 0xaa, 0x28, + 0xab, 0xa4, 0xb2, 0x7, 0x22, 0x0, 0x23, 0xfc, + 0x22, 0x44, 0x22, 0x28, 0x63, 0xa8, 0x56, 0x10, + 0x4a, 0x18, 0x82, 0x24, 0x82, 0x42, + + /* U+714A "煊" */ + 0x20, 0x40, 0x40, 0x40, 0x9f, 0xf9, 0x20, 0x1a, + 0xbf, 0xd6, 0x0, 0x8, 0xff, 0x11, 0x2, 0x23, + 0xfc, 0x44, 0x9, 0x48, 0x12, 0x5f, 0xe4, 0x0, + 0x10, 0xff, 0xc0, 0x0, 0x0, + + /* U+714C "煌" */ + 0x0, 0x0, 0x41, 0x0, 0x8f, 0xf1, 0x10, 0x2a, + 0xa0, 0x55, 0x7f, 0xaa, 0x81, 0x11, 0xfe, 0x20, + 0x0, 0x47, 0xf8, 0xc1, 0x2, 0x82, 0x4, 0xbf, + 0xc8, 0x8, 0x21, 0xff, 0x80, + + /* U+714E "煎" */ + 0x18, 0x20, 0x18, 0x83, 0xff, 0xf8, 0x0, 0x7, + 0xe0, 0x48, 0x48, 0x9f, 0x91, 0x21, 0x22, 0x7e, + 0x44, 0x84, 0x19, 0x38, 0xc0, 0x0, 0x4, 0x88, + 0x50, 0x88, 0x60, 0x0, 0x0, + + /* U+715C "煜" */ + 0x20, 0x0, 0x47, 0xf8, 0x90, 0x11, 0x20, 0x2a, + 0xbf, 0xd5, 0x80, 0xad, 0x1, 0x59, 0xfe, 0x20, + 0x40, 0x40, 0x80, 0xdf, 0xf2, 0x80, 0x4, 0x91, + 0x8, 0x2, 0x23, 0xff, 0xc0, 0x0, + + /* U+715E "煞" */ + 0x20, 0x20, 0x3c, 0x40, 0x44, 0x7e, 0x88, 0x44, + 0x7e, 0xc4, 0x2, 0xa4, 0x2, 0x28, 0x7e, 0x18, + 0x2, 0x28, 0x7e, 0xc6, 0x0, 0x0, 0x24, 0x44, + 0x44, 0x42, 0x42, 0x22, + + /* U+7164 "煤" */ + 0x20, 0x0, 0x44, 0x10, 0x9f, 0xfd, 0x50, 0x4a, + 0x9f, 0x96, 0x41, 0xc, 0x82, 0x10, 0xfc, 0x20, + 0x40, 0x6f, 0xfd, 0x42, 0x82, 0x46, 0x84, 0x34, + 0xd0, 0x88, 0x60, 0x10, 0x0, + + /* U+7166 "煦" */ + 0x0, 0x80, 0xf9, 0x1, 0x13, 0xfa, 0x28, 0x14, + 0x60, 0x28, 0xbe, 0x5f, 0x44, 0xa2, 0x89, 0x45, + 0xf2, 0x88, 0x5, 0xf0, 0x18, 0x3, 0xe0, 0x0, + 0x4, 0x90, 0x91, 0x10, 0xc1, 0x1, + + /* U+7167 "照" */ + 0x7d, 0xfc, 0x88, 0x89, 0x11, 0x12, 0x24, 0xc7, + 0xd0, 0x8, 0xbf, 0x91, 0x41, 0x22, 0x82, 0x45, + 0x4, 0xfb, 0xf8, 0x0, 0x1, 0x22, 0x44, 0x44, + 0x48, 0x84, 0xc0, + + /* U+7168 "煨" */ + 0x20, 0x0, 0x47, 0xf8, 0x88, 0x91, 0x11, 0x2a, + 0xbf, 0xd5, 0x44, 0xaa, 0x89, 0x11, 0xfe, 0x20, + 0x0, 0x4f, 0xfc, 0xc9, 0x2, 0xd2, 0x64, 0xa3, + 0x8, 0x4a, 0x20, 0xe3, 0x41, 0x1, + + /* U+716E "煮" */ + 0x0, 0x0, 0x7f, 0xd8, 0x8, 0x40, 0x11, 0xf, + 0xff, 0xe0, 0x60, 0x7, 0xfe, 0x78, 0x4, 0x1f, + 0xf8, 0x20, 0x10, 0x40, 0x20, 0xff, 0xc0, 0x0, + 0x8, 0x90, 0xa1, 0x10, 0x80, + + /* U+7172 "煲" */ + 0x10, 0x0, 0x2f, 0xf8, 0x90, 0x13, 0x3f, 0xee, + 0x4, 0x15, 0xff, 0xc8, 0x30, 0x11, 0xb8, 0x2c, + 0x4e, 0x2, 0x0, 0x84, 0x20, 0x8c, 0x80, 0x24, + 0x3, 0x87, 0x38, 0x1, 0x80, + + /* U+7173 "煳" */ + 0x22, 0x0, 0x44, 0x7c, 0x88, 0x8d, 0x7d, 0x1b, + 0x22, 0x36, 0x47, 0xc8, 0x88, 0x93, 0xd1, 0x24, + 0xa2, 0x49, 0x7d, 0x52, 0x8a, 0xa5, 0x14, 0x7e, + 0x30, 0x88, 0x60, 0x33, 0x80, + + /* U+7178 "煸" */ + 0x20, 0x40, 0x40, 0x40, 0x9f, 0xf9, 0x20, 0x1b, + 0x40, 0x36, 0xff, 0xed, 0x0, 0x12, 0x0, 0x25, + 0xfe, 0x4a, 0x94, 0x95, 0x2a, 0xaf, 0xf4, 0x94, + 0xa9, 0x29, 0x62, 0x52, 0xc8, 0x83, + + /* U+717A "煺" */ + 0x20, 0x0, 0x44, 0xf8, 0x89, 0x15, 0x83, 0xeb, + 0x4, 0x56, 0xc8, 0x88, 0x9f, 0x11, 0x20, 0x22, + 0x52, 0x44, 0x98, 0xc9, 0x12, 0x93, 0x94, 0x24, + 0x8, 0xa0, 0x22, 0x3f, 0x80, + + /* U+717D "煽" */ + 0x20, 0x40, 0x40, 0xc0, 0x8f, 0xf9, 0x10, 0x1a, + 0xa0, 0x35, 0x7f, 0xe8, 0x80, 0x11, 0xef, 0x22, + 0x42, 0x44, 0xa4, 0xcd, 0x2a, 0xd2, 0x14, 0x5c, + 0xe8, 0x8a, 0x61, 0x10, 0xc4, 0xe7, + + /* U+7184 "熄" */ + 0x0, 0x40, 0x40, 0x80, 0x8f, 0xf1, 0x10, 0x2a, + 0xa0, 0x55, 0x7f, 0xaa, 0x81, 0x11, 0xfe, 0x22, + 0x4, 0x47, 0xf8, 0xc0, 0x2, 0x4a, 0x4, 0x52, + 0x48, 0xa1, 0x62, 0x44, 0x40, 0x78, + + /* U+718A "熊" */ + 0x10, 0x40, 0x48, 0x8d, 0x19, 0xe7, 0xfa, 0x10, + 0x6, 0x2f, 0xc3, 0x9f, 0x91, 0xa1, 0x3c, 0x7e, + 0x42, 0x84, 0x85, 0x38, 0xf0, 0x0, 0x4, 0x84, + 0x48, 0x88, 0x40, 0x0, 0x0, + + /* U+718F "熏" */ + 0x7f, 0xfc, 0x2, 0x1, 0xff, 0xf3, 0xff, 0xe4, + 0x92, 0x48, 0x20, 0x9f, 0xff, 0x0, 0x80, 0x7f, + 0xfc, 0x2, 0x3, 0xff, 0xf8, 0x0, 0x4, 0x44, + 0x50, 0x0, 0x0, + + /* U+7194 "熔" */ + 0x20, 0x20, 0x20, 0x20, 0x23, 0xfe, 0x22, 0x2, + 0xa8, 0x90, 0xa9, 0xc, 0xb2, 0x62, 0x20, 0xd0, + 0x21, 0x88, 0x23, 0x6, 0x55, 0xfc, 0x59, 0x4, + 0x49, 0x4, 0x81, 0xfc, 0x81, 0x4, + + /* U+7198 "熘" */ + 0x20, 0x0, 0x9e, 0xf2, 0x41, 0x6b, 0x45, 0xb4, + 0x96, 0xdd, 0x92, 0x46, 0xc8, 0x0, 0x27, 0xfc, + 0x91, 0x15, 0x7f, 0xd5, 0x11, 0x54, 0x46, 0x1f, + 0xf8, 0x40, 0x40, + + /* U+7199 "熙" */ + 0x7f, 0x7c, 0x88, 0x89, 0x11, 0x12, 0xfa, 0x25, + 0x17, 0xca, 0x28, 0x17, 0xd0, 0xa2, 0x21, 0x7f, + 0x7c, 0x0, 0x1, 0x12, 0x22, 0x22, 0x28, 0x22, + 0x20, 0x0, 0x0, + + /* U+719F "熟" */ + 0x8, 0x21, 0xfe, 0x40, 0x0, 0x83, 0xf7, 0xc4, + 0x22, 0x87, 0x95, 0x1f, 0xb2, 0x3, 0x34, 0x8, + 0x5b, 0xff, 0x14, 0x26, 0x19, 0x88, 0x0, 0x0, + 0x8, 0x88, 0x90, 0x89, 0x0, + + /* U+71A0 "熠" */ + 0x20, 0x0, 0x5f, 0xfc, 0x82, 0x9, 0x15, 0x9b, + 0x19, 0x36, 0x73, 0xed, 0x38, 0x90, 0x40, 0x23, + 0xfc, 0x64, 0x9, 0x6f, 0xf2, 0x50, 0x24, 0x20, + 0x50, 0x7f, 0xa0, 0x81, 0x0, + + /* U+71A8 "熨" */ + 0x3f, 0x84, 0x20, 0x84, 0x3f, 0xbf, 0x20, 0x4, + 0x2f, 0xa4, 0x20, 0x14, 0x3f, 0xd4, 0x42, 0x4, + 0x52, 0x84, 0x8e, 0x58, 0x1, 0x0, 0x19, 0x8, + 0xa, 0x90, 0x4, 0x60, 0x78, 0x1e, + + /* U+71AC "熬" */ + 0x10, 0x41, 0xfc, 0x80, 0x41, 0xf7, 0xf4, 0x41, + 0xc, 0x9f, 0xe9, 0x8, 0x14, 0x1e, 0x18, 0x44, + 0x30, 0x89, 0x92, 0x76, 0x14, 0x0, 0x4, 0x88, + 0x88, 0x88, 0xa1, 0x10, 0x0, + + /* U+71B3 "熳" */ + 0x23, 0xfc, 0x44, 0x8, 0x8f, 0xf5, 0x50, 0x2a, + 0xbf, 0xd5, 0x0, 0x2d, 0xff, 0x92, 0x49, 0x27, + 0xfe, 0x40, 0x1, 0x4f, 0xf2, 0x48, 0x44, 0x89, + 0x10, 0x1e, 0x23, 0xc3, 0x80, + + /* U+71B5 "熵" */ + 0x20, 0x40, 0x40, 0x40, 0x9f, 0xf9, 0x8, 0x4a, + 0x89, 0x15, 0xff, 0xed, 0x0, 0x92, 0x49, 0x25, + 0xa, 0x49, 0xe5, 0xd2, 0x4a, 0xe4, 0x94, 0xcf, + 0x30, 0x80, 0x61, 0x7, 0x0, + + /* U+71B9 "熹" */ + 0x1, 0x1, 0xff, 0xfc, 0x4, 0x1, 0xf7, 0xc3, + 0xff, 0x84, 0x1, 0xf, 0xfe, 0x4, 0x10, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xe1, 0xff, 0xc0, 0x0, + 0x8, 0x88, 0xa1, 0x10, 0x0, + + /* U+71C3 "燃" */ + 0x21, 0x10, 0x42, 0x28, 0x8f, 0x55, 0x52, 0x8b, + 0x57, 0xf4, 0x92, 0xa, 0xac, 0x10, 0x94, 0x21, + 0x28, 0x44, 0x89, 0x52, 0xa, 0xc0, 0x24, 0x4a, + 0x50, 0x94, 0x62, 0x20, 0x80, + + /* U+71CE "燎" */ + 0x20, 0x40, 0x40, 0x80, 0x9f, 0xf9, 0x3, 0xa, + 0xa9, 0x55, 0x21, 0x2d, 0xff, 0x91, 0x2, 0x22, + 0x4, 0x47, 0xf8, 0xc8, 0x12, 0x9f, 0xe4, 0x84, + 0x9, 0x49, 0x21, 0x11, 0xc0, 0xe0, + + /* U+71D4 "燔" */ + 0x20, 0x0, 0x47, 0xf0, 0x80, 0x95, 0x49, 0x4b, + 0x7f, 0xf6, 0x16, 0x8, 0x6a, 0x11, 0x12, 0x27, + 0xfc, 0x44, 0x49, 0x48, 0x92, 0x9f, 0xe4, 0xa2, + 0x50, 0x7f, 0xa0, 0x81, 0x0, + + /* U+71D5 "燕" */ + 0x8, 0x40, 0xff, 0xfe, 0x8, 0x40, 0x8, 0x40, + 0x17, 0x90, 0x10, 0x10, 0xf7, 0x9e, 0x14, 0x90, + 0x14, 0x91, 0xf4, 0x92, 0x17, 0x8c, 0x0, 0x4, + 0x44, 0x44, 0x44, 0x42, 0x0, 0x0, + + /* U+71E0 "燠" */ + 0x20, 0x40, 0x41, 0x0, 0x9d, 0xf5, 0x6a, 0x2a, + 0xcd, 0x56, 0x88, 0xad, 0x7d, 0x12, 0x5a, 0x25, + 0x4c, 0x40, 0x81, 0x5f, 0xfa, 0x43, 0x8, 0x9, + 0x10, 0x61, 0x81, 0x0, 0x80, + + /* U+71E5 "燥" */ + 0x20, 0x0, 0x47, 0xf0, 0x88, 0x21, 0x10, 0x4a, + 0xbf, 0x95, 0x0, 0x2b, 0xe7, 0x12, 0x52, 0x27, + 0x9c, 0x40, 0x0, 0x81, 0x2, 0xbf, 0xf4, 0x8a, + 0x9, 0x2b, 0x21, 0x91, 0xc0, 0x20, + + /* U+71E7 "燧" */ + 0x20, 0x44, 0x48, 0xc8, 0x88, 0xa5, 0x57, 0xfa, + 0x82, 0x16, 0xd2, 0x88, 0x8e, 0x11, 0x28, 0x32, + 0xbc, 0x64, 0x94, 0xaa, 0x23, 0x13, 0x84, 0x70, + 0x9, 0x30, 0x22, 0x3f, 0x80, + + /* U+71EE "燮" */ + 0x42, 0x11, 0x3e, 0x4d, 0x1, 0x74, 0xcd, 0x47, + 0x91, 0x0, 0x46, 0x79, 0x95, 0x29, 0x97, 0xa4, + 0x0, 0x7, 0xff, 0x4, 0xc, 0xc, 0xc0, 0xc, + 0xf, 0x4f, 0xc0, + + /* U+71F9 "燹" */ + 0x7f, 0x7e, 0x8, 0x10, 0x73, 0x72, 0x1c, 0x3c, + 0x6e, 0x4c, 0x15, 0x3a, 0x64, 0x48, 0x18, 0x38, + 0x0, 0x80, 0x10, 0x88, 0x9, 0x50, 0x2, 0x60, + 0xc, 0x1c, 0x70, 0x2, + + /* U+7206 "爆" */ + 0x20, 0x0, 0x4f, 0xf8, 0x90, 0x11, 0x3f, 0xeb, + 0x40, 0x56, 0xff, 0xac, 0x44, 0x17, 0xff, 0x21, + 0x10, 0x5f, 0xfc, 0xc4, 0x42, 0x9a, 0xe4, 0xc4, + 0x28, 0x7b, 0x23, 0x11, 0x40, 0x60, + + /* U+721D "爝" */ + 0x20, 0x0, 0x4f, 0xf8, 0x89, 0x25, 0x7f, 0xeb, + 0x4a, 0x56, 0x94, 0x88, 0xd6, 0x13, 0xe2, 0x24, + 0x7e, 0x4f, 0x89, 0x51, 0x92, 0xbe, 0xa4, 0xc9, + 0x50, 0xb8, 0xa1, 0x96, 0x0, + + /* U+7228 "爨" */ + 0x7f, 0xfc, 0x90, 0x89, 0xe1, 0xf2, 0x5a, 0x26, + 0xb5, 0xdf, 0xff, 0xe0, 0x0, 0xfe, 0xfd, 0x28, + 0x40, 0x8b, 0x73, 0xff, 0xf8, 0x43, 0x3, 0xa5, + 0x98, 0x70, 0xcf, 0x1e, 0x0, + + /* U+722A "爪" */ + 0x0, 0xc, 0x1f, 0xf0, 0x11, 0x10, 0x11, 0x10, + 0x11, 0x10, 0x11, 0x10, 0x11, 0x10, 0x11, 0x10, + 0x11, 0x8, 0x11, 0x8, 0x11, 0x8, 0x21, 0x4, + 0x21, 0x4, 0x41, 0x2, 0x41, 0x0, + + /* U+722C "爬" */ + 0x2, 0x0, 0xfd, 0xf9, 0x4a, 0x52, 0x94, 0xa5, + 0x29, 0x4a, 0x52, 0x94, 0xbf, 0x29, 0x40, 0x52, + 0x82, 0xa5, 0x5, 0x4f, 0xf2, 0x88, 0x9, 0x18, + 0x12, 0x1c, 0x24, 0xf, 0x80, + + /* U+7230 "爰" */ + 0x7f, 0xfc, 0x2, 0x10, 0x44, 0x60, 0x80, 0x87, + 0xff, 0xc0, 0x80, 0x1, 0x0, 0x7f, 0xff, 0x8, + 0x0, 0x3f, 0xf0, 0x60, 0x21, 0x31, 0x84, 0x1c, + 0x10, 0x6e, 0xf, 0x3, 0x80, + + /* U+7231 "爱" */ + 0x7f, 0xfc, 0x2, 0x10, 0x44, 0x40, 0x80, 0x8f, + 0xff, 0xd0, 0x0, 0x81, 0x0, 0x7f, 0xff, 0x8, + 0x0, 0x3f, 0xf0, 0x60, 0x61, 0x31, 0x4, 0x1c, + 0x10, 0xee, 0xc, 0x3, 0x80, + + /* U+7235 "爵" */ + 0xff, 0xf8, 0x88, 0x8f, 0xff, 0xa2, 0x22, 0x88, + 0x8b, 0xff, 0xef, 0xc0, 0x21, 0x7f, 0xfc, 0x12, + 0x12, 0x4f, 0xc9, 0x22, 0x4, 0x9c, 0x13, 0x89, + 0xc0, + + /* U+7236 "父" */ + 0x0, 0x0, 0x8, 0x60, 0x20, 0x60, 0x80, 0x62, + 0x0, 0x29, 0x2, 0x2, 0x8, 0x2, 0x10, 0x6, + 0x40, 0x4, 0x80, 0x6, 0x0, 0x1e, 0x0, 0x43, + 0x7, 0x1, 0x90, 0x0, 0x80, + + /* U+7237 "爷" */ + 0x4, 0x40, 0x30, 0x61, 0xa0, 0x38, 0x22, 0x0, + 0x38, 0x1, 0xdc, 0x3c, 0x7, 0x80, 0x0, 0x7f, + 0xf8, 0x8, 0x10, 0x10, 0x20, 0x20, 0x40, 0x4f, + 0x0, 0x80, 0x1, 0x0, 0x0, + + /* U+7238 "爸" */ + 0x8, 0x20, 0x20, 0x31, 0xa0, 0x98, 0x36, 0x0, + 0x38, 0x7, 0x8f, 0x30, 0x1, 0x9f, 0xfc, 0x21, + 0x8, 0x42, 0x10, 0xff, 0xe1, 0x0, 0x12, 0x0, + 0x24, 0x0, 0x47, 0xff, 0x0, + + /* U+7239 "爹" */ + 0xc, 0x30, 0xe8, 0x5c, 0xf, 0x0, 0x73, 0xc7, + 0x10, 0x40, 0xff, 0x8f, 0x4, 0x1, 0xf0, 0xe, + 0x40, 0xe3, 0xf8, 0x38, 0x31, 0x99, 0x80, 0xc, + 0x3, 0xe0, 0x18, 0x0, 0x0, + + /* U+723B "爻" */ + 0x40, 0x10, 0xc0, 0xc0, 0xcc, 0x0, 0xc0, 0xc, + 0xc1, 0xc0, 0xe1, 0x0, 0x4, 0x8, 0x8, 0x40, + 0x32, 0x0, 0x78, 0x0, 0xc0, 0xc, 0xc1, 0xc0, + 0xe8, 0x0, 0x40, + + /* U+723D "爽" */ + 0x2, 0x0, 0x8, 0xf, 0xff, 0xc0, 0x80, 0x4a, + 0x48, 0xc8, 0xcc, 0xac, 0x82, 0x80, 0x4a, 0x50, + 0xc8, 0xc4, 0xb4, 0x80, 0xc0, 0x4, 0x80, 0x61, + 0x8e, 0x1, 0xc0, + + /* U+723F "爿" */ + 0x10, 0x11, 0x1, 0x10, 0x11, 0x1, 0x1f, 0xf0, + 0x1, 0x0, 0x10, 0x1, 0xff, 0xf0, 0x81, 0x8, + 0x11, 0x1, 0x10, 0x16, 0x1, 0xc0, 0x10, + + /* U+7247 "片" */ + 0x0, 0x40, 0x81, 0x2, 0x4, 0x8, 0x10, 0x20, + 0x40, 0xff, 0xf2, 0x0, 0x8, 0x0, 0x20, 0x0, + 0xff, 0x82, 0x2, 0x8, 0x8, 0x60, 0x21, 0x0, + 0x8c, 0x2, 0x20, 0x8, + + /* U+7248 "版" */ + 0x48, 0x0, 0x49, 0xfc, 0x49, 0x0, 0x49, 0x0, + 0x49, 0x0, 0x7d, 0xfe, 0x41, 0x42, 0x41, 0x44, + 0x79, 0x24, 0x49, 0x28, 0x49, 0x18, 0x49, 0x10, + 0x4a, 0x28, 0x8a, 0x46, 0x8d, 0x82, + + /* U+724C "牌" */ + 0x48, 0x40, 0x97, 0xf9, 0x28, 0x92, 0x51, 0x24, + 0xbf, 0xcf, 0xc4, 0x90, 0x89, 0x21, 0x12, 0x7b, + 0xfc, 0x91, 0x1, 0x24, 0x42, 0x5f, 0xf4, 0x81, + 0x11, 0x2, 0x22, 0x4, 0x0, + + /* U+724D "牍" */ + 0x8, 0x20, 0x90, 0x41, 0x27, 0xfa, 0x41, 0x4, + 0x9f, 0xef, 0x80, 0x50, 0x34, 0xa0, 0x8, 0x79, + 0x90, 0x90, 0x21, 0x2f, 0xfa, 0x40, 0x84, 0x83, + 0x91, 0x18, 0xa2, 0xc0, 0x80, + + /* U+7252 "牒" */ + 0x12, 0x44, 0xa4, 0x89, 0x5f, 0xfa, 0x92, 0x25, + 0x24, 0x4f, 0xcf, 0x90, 0x80, 0x21, 0xff, 0x70, + 0x20, 0xaf, 0xfd, 0x41, 0xc2, 0x85, 0x85, 0x12, + 0x92, 0xc4, 0xe4, 0x8, 0x0, + + /* U+7256 "牖" */ + 0x50, 0x20, 0x53, 0xfe, 0x52, 0x2, 0x53, 0xfe, + 0x52, 0x0, 0x7a, 0x14, 0x43, 0xff, 0x42, 0x10, + 0x72, 0xfe, 0x52, 0x92, 0x52, 0xfe, 0x52, 0x92, + 0x56, 0xfe, 0x94, 0x92, 0x94, 0x9e, + + /* U+7259 "牙" */ + 0xff, 0xfc, 0x1, 0x2, 0x4, 0x8, 0x10, 0x40, + 0x41, 0xff, 0xf0, 0x14, 0x0, 0x90, 0x6, 0x40, + 0x31, 0x1, 0x84, 0x8, 0x10, 0xc0, 0x40, 0x1f, + 0x0, + + /* U+725B "牛" */ + 0x1, 0x0, 0x22, 0x0, 0x44, 0x1, 0xff, 0xe2, + 0x10, 0x8, 0x20, 0x10, 0x40, 0x0, 0x80, 0xff, + 0xfe, 0x2, 0x0, 0x4, 0x0, 0x8, 0x0, 0x10, + 0x0, 0x20, 0x0, 0x40, 0x0, + + /* U+725D "牝" */ + 0x8, 0x40, 0x8, 0x40, 0x48, 0x41, 0x48, 0x42, + 0x7e, 0x44, 0x48, 0x48, 0x48, 0x50, 0x8, 0x60, + 0x9, 0x40, 0x1e, 0x40, 0x68, 0x41, 0x8, 0x41, + 0x8, 0x41, 0x8, 0x41, 0x8, 0x3e, + + /* U+725F "牟" */ + 0x0, 0x0, 0x4, 0x0, 0x30, 0x80, 0x80, 0x83, + 0xff, 0x80, 0x0, 0x84, 0x40, 0x10, 0x80, 0x3f, + 0xfc, 0x82, 0x1, 0x4, 0x0, 0x8, 0xf, 0xff, + 0xe0, 0x20, 0x0, 0x40, 0x0, 0x80, + + /* U+7261 "牡" */ + 0x10, 0x20, 0x20, 0x41, 0x40, 0x84, 0x81, 0xf, + 0xc2, 0x12, 0x3f, 0xe4, 0x8, 0x48, 0x10, 0x10, + 0x20, 0x78, 0x43, 0x40, 0x80, 0x81, 0x1, 0x2, + 0x2, 0x4, 0x4, 0xff, 0x80, + + /* U+7262 "牢" */ + 0x1, 0x0, 0x1, 0x3, 0xff, 0xf4, 0x0, 0x28, + 0x10, 0x2, 0x20, 0x7, 0xfe, 0x10, 0x80, 0x21, + 0x0, 0x2, 0x3, 0xff, 0xf8, 0x8, 0x0, 0x10, + 0x0, 0x20, 0x0, 0x40, 0x0, + + /* U+7266 "牦" */ + 0x10, 0x0, 0x13, 0xfc, 0x50, 0x40, 0x50, 0x40, + 0x7c, 0x40, 0x90, 0xfe, 0x91, 0x40, 0x10, 0x40, + 0x14, 0x40, 0x79, 0xfe, 0x92, 0x40, 0x10, 0x42, + 0x10, 0x42, 0x10, 0x42, 0x10, 0x7c, + + /* U+7267 "牧" */ + 0x10, 0x80, 0x21, 0x2, 0x42, 0x4, 0x87, 0xff, + 0xd0, 0x92, 0x21, 0x24, 0xa2, 0x8, 0x44, 0x14, + 0x90, 0x70, 0xa3, 0x41, 0x80, 0x83, 0x1, 0xd, + 0x2, 0x21, 0x85, 0x80, 0x80, + + /* U+7269 "物" */ + 0x10, 0x80, 0xa2, 0x1, 0x47, 0xfb, 0xf2, 0x99, + 0x29, 0x32, 0x92, 0x64, 0x24, 0x8a, 0x59, 0x39, + 0x23, 0xa2, 0x44, 0x49, 0x88, 0xa2, 0x11, 0x8, + 0x22, 0x30, 0xc4, 0x4f, 0x0, + + /* U+726E "牮" */ + 0x8, 0x80, 0x11, 0x30, 0xc2, 0xb, 0xbf, 0xe9, + 0x4, 0x22, 0x4, 0x44, 0x47, 0x88, 0x80, 0x1f, + 0xf8, 0x42, 0x0, 0x4, 0x7, 0xff, 0xf0, 0x10, + 0x0, 0x20, 0x0, 0x40, 0x0, + + /* U+726F "牯" */ + 0x10, 0x20, 0x20, 0x42, 0x4f, 0xff, 0xe1, 0x9, + 0x2, 0x12, 0x4, 0x24, 0x8, 0x8, 0x10, 0x13, + 0xfc, 0x7c, 0xb, 0x48, 0x10, 0x90, 0x21, 0x20, + 0x42, 0x7f, 0x84, 0x81, 0x0, + + /* U+7272 "牲" */ + 0x10, 0x20, 0x51, 0x20, 0x51, 0x20, 0x7d, 0xfe, + 0x91, 0x20, 0x92, 0x20, 0x10, 0x20, 0x14, 0x20, + 0x39, 0xfe, 0xd0, 0x20, 0x10, 0x20, 0x10, 0x20, + 0x10, 0x20, 0x13, 0xff, 0x10, 0x0, + + /* U+7275 "牵" */ + 0x1, 0x0, 0x2, 0x1, 0xff, 0xf0, 0x8, 0x0, + 0xef, 0x6, 0x1, 0x3f, 0xff, 0xc0, 0x1, 0x89, + 0x2, 0x3f, 0xf0, 0x84, 0x0, 0x8, 0xf, 0xff, + 0xe0, 0x20, 0x0, 0x40, 0x0, 0x80, + + /* U+7279 "特" */ + 0x10, 0x20, 0xa0, 0x41, 0x47, 0xf2, 0x81, 0xb, + 0x82, 0x12, 0x7f, 0xe4, 0x0, 0xe, 0x4, 0x73, + 0xff, 0x20, 0x10, 0x44, 0x20, 0x84, 0x41, 0x0, + 0x82, 0x1, 0x4, 0x1c, 0x0, + + /* U+727A "牺" */ + 0x10, 0x0, 0x13, 0xff, 0x50, 0x48, 0x50, 0x48, + 0xbb, 0xfe, 0x92, 0x4a, 0x92, 0x4a, 0x16, 0x4a, + 0x3a, 0xca, 0xd2, 0x8e, 0x13, 0x2, 0x12, 0x2, + 0x12, 0x2, 0x13, 0xfe, 0x12, 0x2, + + /* U+727E "牾" */ + 0x10, 0x0, 0x27, 0xfd, 0x41, 0x2, 0x82, 0x7, + 0xdf, 0xd2, 0x10, 0xa4, 0x21, 0x9, 0xff, 0x1c, + 0x1, 0xe3, 0xf8, 0x44, 0x10, 0x88, 0x21, 0x10, + 0x42, 0x3f, 0x84, 0x41, 0x0, + + /* U+727F "牿" */ + 0x10, 0x20, 0x21, 0x41, 0x44, 0x82, 0x8f, 0xe7, + 0xe2, 0x12, 0x4, 0x24, 0xff, 0x88, 0x0, 0x1c, + 0x1, 0xe3, 0xfc, 0x44, 0x8, 0x88, 0x11, 0x10, + 0x22, 0x3f, 0xc4, 0x40, 0x80, + + /* U+7280 "犀" */ + 0x3f, 0xfe, 0x20, 0x2, 0x3f, 0xfe, 0x20, 0x0, + 0x2c, 0x46, 0x22, 0x48, 0x2f, 0x5e, 0x20, 0x3, + 0x27, 0xfe, 0x28, 0x40, 0x20, 0x40, 0x5f, 0xff, + 0x40, 0x40, 0x0, 0x40, + + /* U+7281 "犁" */ + 0x6, 0x4, 0xf0, 0x88, 0x41, 0x17, 0xfa, 0x23, + 0x4, 0x47, 0xc0, 0xb4, 0xf, 0x0, 0x0, 0x11, + 0x0, 0x7f, 0xf1, 0x4, 0x0, 0x8, 0xf, 0xff, + 0xe0, 0x20, 0x0, 0x40, 0x0, + + /* U+7284 "犄" */ + 0x10, 0x20, 0x51, 0xfe, 0x50, 0x20, 0x50, 0x70, + 0x7c, 0xc8, 0x51, 0x6, 0x93, 0xff, 0x10, 0x4, + 0x39, 0xe4, 0xd1, 0x24, 0x11, 0x24, 0x11, 0xe4, + 0x10, 0x4, 0x10, 0x4, 0x10, 0x1c, + + /* U+728A "犊" */ + 0x10, 0x20, 0x20, 0x41, 0x4f, 0xf2, 0x81, 0x7, + 0xbf, 0xf2, 0x0, 0x64, 0x24, 0x89, 0x28, 0x39, + 0x91, 0xa0, 0x40, 0x5f, 0xf8, 0x81, 0x1, 0x5, + 0x2, 0x31, 0x85, 0x80, 0x80, + + /* U+728B "犋" */ + 0x11, 0xfc, 0xa2, 0x9, 0x44, 0x12, 0x8f, 0xe7, + 0xd0, 0x52, 0x3f, 0xa4, 0x41, 0x8, 0x82, 0x1d, + 0xfd, 0xe2, 0x8, 0x5f, 0xf8, 0x80, 0x81, 0x9, + 0x82, 0x60, 0x85, 0x80, 0x0, + + /* U+728D "犍" */ + 0x10, 0x10, 0x57, 0x7e, 0x51, 0x12, 0x7a, 0xff, + 0x52, 0x12, 0x94, 0x12, 0x97, 0x3c, 0x11, 0x10, + 0x39, 0x7e, 0xd6, 0x10, 0x16, 0xff, 0x12, 0x10, + 0x15, 0x0, 0x14, 0xff, 0x10, 0x0, + + /* U+728F "犏" */ + 0x10, 0x20, 0xa7, 0xfd, 0x48, 0xb, 0xd0, 0x15, + 0x3f, 0xf2, 0x40, 0x24, 0x80, 0x9, 0x7f, 0x1a, + 0xab, 0xe5, 0x54, 0x4b, 0xf8, 0x95, 0x51, 0x4a, + 0xa2, 0x95, 0x46, 0x21, 0x80, + + /* U+7292 "犒" */ + 0x10, 0x20, 0xaf, 0xfd, 0x40, 0x2, 0x80, 0x7, + 0xdf, 0xca, 0x40, 0x94, 0x7f, 0x8, 0x0, 0x1f, + 0xfe, 0xe8, 0x6, 0x53, 0xe8, 0xa4, 0x51, 0x4f, + 0xa2, 0x80, 0x45, 0x3, 0x0, + + /* U+729F "犟" */ + 0xf9, 0xfc, 0x14, 0x8, 0x27, 0xf7, 0xc1, 0x8, + 0x3f, 0xdf, 0x44, 0x82, 0xff, 0x8, 0x12, 0x77, + 0xfe, 0x22, 0x0, 0xff, 0xe2, 0x8, 0xf, 0xff, + 0xe0, 0x20, 0x0, 0x40, 0x0, + + /* U+72AC "犬" */ + 0x0, 0x0, 0x9, 0x0, 0x22, 0x0, 0x84, 0x2, + 0x3, 0xff, 0xf0, 0x30, 0x0, 0xc0, 0x3, 0x0, + 0x12, 0x0, 0x48, 0x2, 0x10, 0x10, 0x20, 0x80, + 0x6c, 0x0, 0xc0, + + /* U+72AD "犭" */ + 0x20, 0x8, 0xc6, 0x81, 0x81, 0xc3, 0x20, 0x10, + 0xc, 0xa, 0x9, 0x18, 0x80, 0x40, 0x20, 0x30, + 0xf0, + + /* U+72AF "犯" */ + 0x0, 0x0, 0x49, 0xfc, 0x29, 0x4, 0x11, 0x4, + 0x71, 0x4, 0x11, 0x4, 0x9, 0x4, 0x19, 0x4, + 0x29, 0x38, 0x49, 0x0, 0x9, 0x0, 0x9, 0x2, + 0x9, 0x2, 0x19, 0x2, 0x71, 0xfc, + + /* U+72B0 "犰" */ + 0x0, 0x80, 0x44, 0x80, 0x28, 0x80, 0x30, 0x80, + 0x33, 0xf8, 0x50, 0x88, 0x10, 0x88, 0x18, 0x88, + 0x18, 0x88, 0x28, 0x88, 0x48, 0x88, 0x49, 0x9, + 0x9, 0x9, 0x9, 0x9, 0x1a, 0x9, 0x76, 0xe, + + /* U+72B4 "犴" */ + 0x0, 0x0, 0x97, 0xf8, 0xc1, 0x1, 0x82, 0x7, + 0x4, 0x12, 0x8, 0x6, 0x10, 0xf, 0xff, 0x28, + 0x40, 0xd0, 0x82, 0x21, 0x0, 0x42, 0x0, 0x84, + 0x1, 0x8, 0x1c, 0x10, 0x0, + + /* U+72B6 "状" */ + 0x8, 0x20, 0x8, 0x2c, 0x48, 0x22, 0x48, 0x20, + 0x4b, 0xff, 0x28, 0x20, 0x8, 0x20, 0x8, 0x50, + 0x18, 0x50, 0x68, 0x50, 0x48, 0x88, 0x8, 0x8c, + 0x9, 0x6, 0xa, 0x3, 0x8, 0x0, + + /* U+72B7 "犷" */ + 0x0, 0x0, 0x80, 0x21, 0xa0, 0x41, 0x8f, 0xf3, + 0x10, 0xa, 0x20, 0x4, 0x40, 0xc, 0x80, 0x39, + 0x0, 0x52, 0x1, 0x24, 0x0, 0x48, 0x0, 0x90, + 0x1, 0x40, 0x6, 0x80, 0x3a, 0x0, + + /* U+72B8 "犸" */ + 0x0, 0x0, 0x93, 0xf8, 0xc0, 0x11, 0x88, 0x25, + 0x10, 0x42, 0x20, 0x86, 0x41, 0xc, 0xff, 0x28, + 0x2, 0x90, 0x4, 0x2f, 0xe8, 0x40, 0x10, 0x80, + 0x23, 0x0, 0xdc, 0xf, 0x0, + + /* U+72B9 "犹" */ + 0x40, 0x80, 0x48, 0x88, 0x30, 0x86, 0x30, 0x80, + 0x57, 0xff, 0x10, 0x90, 0x8, 0x90, 0x18, 0x90, + 0x28, 0x90, 0x49, 0x10, 0x9, 0x11, 0x9, 0x11, + 0xa, 0x11, 0x74, 0x1f, + + /* U+72C1 "狁" */ + 0x40, 0x40, 0x68, 0x40, 0x30, 0x88, 0x31, 0x8, + 0x52, 0x4, 0x13, 0xfe, 0x18, 0x93, 0x18, 0x90, + 0x28, 0x90, 0x48, 0x90, 0x8, 0x90, 0x9, 0x11, + 0x9, 0x12, 0x1a, 0x12, 0x74, 0x1e, + + /* U+72C2 "狂" */ + 0x48, 0x0, 0x97, 0xfc, 0xc0, 0x81, 0x81, 0x5, + 0x2, 0x2, 0x4, 0x2, 0x8, 0xd, 0xff, 0x28, + 0x20, 0x90, 0x40, 0x20, 0x80, 0x41, 0x0, 0x82, + 0x3, 0x7f, 0xdc, 0x0, 0x0, + + /* U+72C3 "狃" */ + 0x0, 0x0, 0x97, 0xf8, 0xc1, 0x10, 0x84, 0x27, + 0x8, 0x42, 0x10, 0x86, 0x21, 0xd, 0xfe, 0x28, + 0x84, 0x91, 0x8, 0x22, 0x10, 0x44, 0x20, 0x88, + 0x43, 0xff, 0xdc, 0x0, 0x0, + + /* U+72C4 "狄" */ + 0x0, 0x20, 0x44, 0x20, 0x28, 0x20, 0x30, 0x20, + 0x32, 0x22, 0x51, 0x24, 0x19, 0x24, 0x18, 0x60, + 0x28, 0x50, 0x48, 0x50, 0x8, 0x50, 0x8, 0x88, + 0x9, 0x8c, 0x1b, 0x6, 0x76, 0x3, + + /* U+72C8 "狈" */ + 0x0, 0x0, 0x4d, 0xfe, 0x29, 0x2, 0x31, 0x2, + 0x71, 0x22, 0xd1, 0x22, 0x19, 0x22, 0x19, 0x22, + 0x29, 0x22, 0x49, 0x22, 0x9, 0x22, 0x8, 0x50, + 0x8, 0x48, 0x19, 0x86, 0x77, 0x1, 0x0, 0x0, + + /* U+72CD "狍" */ + 0x81, 0x0, 0x92, 0x1, 0x4f, 0xf1, 0x10, 0x26, + 0x40, 0x55, 0xfc, 0x84, 0x89, 0x19, 0x12, 0x53, + 0xe5, 0x24, 0x8, 0x49, 0xe0, 0x90, 0x11, 0x20, + 0x26, 0x40, 0x78, 0x7f, 0x0, + + /* U+72CE "狎" */ + 0x40, 0x0, 0x97, 0xfc, 0xc8, 0x89, 0x91, 0x15, + 0x22, 0x22, 0x7f, 0xc6, 0x88, 0x8d, 0x11, 0x2a, + 0x22, 0x97, 0xfc, 0x28, 0x88, 0x41, 0x0, 0x82, + 0x3, 0x4, 0x1c, 0x8, 0x0, + + /* U+72D0 "狐" */ + 0x0, 0x8, 0x97, 0xe1, 0x49, 0x41, 0x12, 0x86, + 0x25, 0x14, 0x4a, 0x4, 0x94, 0x19, 0x28, 0x32, + 0x50, 0xa4, 0x92, 0x49, 0xa0, 0x93, 0x41, 0x4a, + 0x42, 0x9a, 0x9a, 0x0, 0x80, + + /* U+72D2 "狒" */ + 0x80, 0x90, 0xef, 0xf8, 0x82, 0x57, 0x4, 0xa1, + 0x7f, 0xc2, 0x92, 0xd, 0x24, 0x1a, 0x48, 0x57, + 0xff, 0x21, 0x24, 0x44, 0x50, 0x88, 0xe1, 0x21, + 0x1d, 0x82, 0x0, + + /* U+72D7 "狗" */ + 0x81, 0x1, 0x24, 0x5, 0x3f, 0xc8, 0x81, 0xe4, + 0x4, 0x8f, 0x91, 0x22, 0x4c, 0x89, 0x52, 0x26, + 0x48, 0x91, 0x3e, 0x44, 0x1, 0x10, 0x4, 0xc0, + 0x1e, 0x7, 0x80, + + /* U+72D9 "狙" */ + 0x49, 0xfc, 0x52, 0x8, 0x44, 0x13, 0x88, 0x21, + 0x1f, 0xc1, 0x20, 0x86, 0x41, 0x14, 0x82, 0x49, + 0xfc, 0x12, 0x8, 0x24, 0x10, 0x48, 0x21, 0x9f, + 0xce, 0x40, 0x0, + + /* U+72DE "狞" */ + 0x40, 0x20, 0x28, 0x20, 0x33, 0xfe, 0x32, 0x2, + 0x72, 0x2, 0x10, 0x0, 0x1b, 0xff, 0x18, 0x20, + 0x28, 0x20, 0x48, 0x20, 0x8, 0x20, 0x8, 0x20, + 0x8, 0x20, 0x18, 0x20, 0x71, 0xc0, + + /* U+72E0 "狠" */ + 0x45, 0xfe, 0x29, 0x2, 0x31, 0x2, 0x31, 0x2, + 0x71, 0xfe, 0x91, 0x2, 0x9, 0x2, 0x19, 0xfe, + 0x29, 0x20, 0x69, 0x21, 0x49, 0x16, 0x9, 0x18, + 0x9, 0x8, 0x19, 0x46, 0x71, 0x83, + + /* U+72E1 "狡" */ + 0x40, 0x20, 0x28, 0x10, 0x33, 0xff, 0x30, 0x0, + 0x50, 0x88, 0x10, 0x84, 0x9, 0x2, 0x1a, 0x88, + 0x28, 0x88, 0x48, 0x88, 0x48, 0x50, 0x8, 0x20, + 0x8, 0x50, 0x19, 0x8c, 0x76, 0x3, + + /* U+72E8 "狨" */ + 0x0, 0x21, 0x10, 0x49, 0x40, 0x81, 0x3f, 0xfe, + 0x12, 0x4, 0x24, 0x84, 0x49, 0x1b, 0xd2, 0x51, + 0x29, 0x22, 0x30, 0x44, 0x40, 0x91, 0x91, 0x23, + 0x26, 0x99, 0x79, 0x21, 0x0, + + /* U+72E9 "狩" */ + 0x0, 0x20, 0x90, 0x40, 0xaf, 0xf8, 0x90, 0x13, + 0x20, 0xaa, 0x1, 0x6, 0xff, 0x8c, 0x4, 0x29, + 0x8, 0x91, 0x11, 0x22, 0x20, 0x40, 0x40, 0x80, + 0x83, 0x1, 0x1c, 0x1c, 0x0, + + /* U+72EC "独" */ + 0x0, 0x20, 0x48, 0x20, 0x28, 0x20, 0x13, 0xfe, + 0x32, 0x22, 0x52, 0x22, 0xa, 0x22, 0x1a, 0x22, + 0x2b, 0xfe, 0x4a, 0x20, 0x8, 0x20, 0x8, 0x24, + 0x8, 0x22, 0x1b, 0xfe, 0x70, 0x1, + + /* U+72ED "狭" */ + 0x44, 0x20, 0x28, 0x20, 0x33, 0xfe, 0x30, 0x20, + 0x72, 0x22, 0x11, 0x24, 0x8, 0xa8, 0x18, 0x20, + 0x2b, 0xff, 0x68, 0x60, 0x48, 0x50, 0x8, 0x50, + 0x8, 0x88, 0x19, 0x4, 0x76, 0x2, + + /* U+72EE "狮" */ + 0x1, 0x0, 0x51, 0x7f, 0x65, 0x8, 0x25, 0x8, + 0x65, 0x7e, 0x15, 0x4a, 0x15, 0x4a, 0x35, 0x4a, + 0x55, 0x4a, 0x95, 0x4a, 0x11, 0x4a, 0x11, 0x4e, + 0x12, 0x48, 0x12, 0x8, 0xe4, 0x8, + + /* U+72EF "狯" */ + 0x0, 0x20, 0x48, 0x20, 0x38, 0x50, 0x10, 0x88, + 0x71, 0x84, 0x13, 0x2, 0xa, 0xf8, 0x18, 0x0, + 0x28, 0x0, 0x4b, 0xfe, 0x8, 0x40, 0x8, 0x88, + 0x9, 0x4, 0x9, 0xfc, 0x70, 0x2, + + /* U+72F0 "狰" */ + 0x0, 0x0, 0x48, 0x80, 0x28, 0xf8, 0x31, 0x8, + 0x32, 0x10, 0x51, 0xfe, 0x10, 0x22, 0x18, 0x22, + 0x1b, 0xff, 0x28, 0x22, 0x48, 0x22, 0xb, 0xfe, + 0x8, 0x22, 0x8, 0x20, 0x18, 0x20, 0x71, 0xc0, + + /* U+72F1 "狱" */ + 0x0, 0x0, 0xa4, 0x51, 0x48, 0x91, 0x1, 0x6, + 0xf, 0xe3, 0xc6, 0x4, 0x8c, 0x19, 0x18, 0x32, + 0x30, 0xa4, 0x60, 0x49, 0x20, 0x9a, 0x41, 0x24, + 0x42, 0x10, 0xb8, 0x40, 0x80, + + /* U+72F2 "狲" */ + 0x80, 0x10, 0xaf, 0x21, 0x42, 0x41, 0x8, 0x86, + 0x15, 0x44, 0x4a, 0x84, 0xb5, 0x1b, 0xaa, 0x32, + 0x93, 0xa5, 0x24, 0x48, 0x48, 0x90, 0x81, 0x21, + 0x2, 0x42, 0x39, 0x9c, 0x0, + + /* U+72F3 "狳" */ + 0x88, 0x41, 0x43, 0x6, 0x12, 0x18, 0xc4, 0xa6, + 0xc, 0x4f, 0xc1, 0x4, 0xc, 0x10, 0x57, 0xff, + 0x41, 0x9, 0x5, 0x4, 0x94, 0x14, 0x48, 0xe1, + 0x1e, 0x18, 0x0, + + /* U+72F4 "狴" */ + 0x82, 0x21, 0x24, 0x41, 0x48, 0xb1, 0x1d, 0x86, + 0x22, 0x14, 0x44, 0x44, 0xa8, 0x99, 0x9e, 0x50, + 0x41, 0x20, 0x82, 0x4f, 0xf0, 0x82, 0x1, 0x4, + 0x6, 0xff, 0xf8, 0x0, 0x0, + + /* U+72F7 "狷" */ + 0x89, 0xf9, 0x48, 0x26, 0x1f, 0xb8, 0x0, 0x20, + 0x0, 0x4f, 0xf3, 0x20, 0x54, 0xff, 0x92, 0x4, + 0x48, 0x11, 0x3f, 0xc4, 0x81, 0x32, 0xf, 0x88, + 0xe0, + + /* U+72F8 "狸" */ + 0x97, 0xfc, 0xc8, 0x88, 0x91, 0x13, 0x3f, 0xea, + 0x44, 0x42, 0x88, 0x8d, 0xff, 0x28, 0x20, 0x90, + 0x40, 0x2f, 0xf8, 0x41, 0x0, 0x82, 0x1, 0xff, + 0xfc, 0x0, 0x0, + + /* U+72FA "狺" */ + 0x0, 0x20, 0x48, 0x10, 0x33, 0xff, 0x30, 0x0, + 0xd1, 0xfe, 0x10, 0x0, 0x18, 0x0, 0x39, 0xfe, + 0x68, 0x0, 0xc9, 0xfe, 0x9, 0x2, 0x9, 0x2, + 0x9, 0x2, 0x19, 0xfe, 0x71, 0x2, + + /* U+72FB "狻" */ + 0x0, 0x41, 0x10, 0x81, 0x42, 0x41, 0x8, 0x46, + 0x3f, 0xd4, 0x0, 0x44, 0x46, 0xb, 0x3, 0x30, + 0x80, 0xa3, 0xf1, 0x4c, 0x24, 0xb8, 0x81, 0xb, + 0x2, 0xc, 0xc, 0x66, 0x77, 0x3, + + /* U+72FC "狼" */ + 0x0, 0x20, 0x90, 0x40, 0xa7, 0xf1, 0x88, 0x23, + 0x10, 0x4a, 0x3f, 0x84, 0x41, 0xc, 0x82, 0x19, + 0xfc, 0x52, 0x41, 0x24, 0x98, 0x49, 0x40, 0x91, + 0x1, 0x21, 0x6, 0x5b, 0x38, 0xc1, + + /* U+7301 "猁" */ + 0x0, 0x2, 0xaf, 0x84, 0x84, 0x4b, 0x8, 0x9a, + 0x7d, 0x22, 0x22, 0x44, 0xc4, 0x99, 0xc9, 0x55, + 0x53, 0x2a, 0x24, 0x64, 0x48, 0x88, 0x11, 0x10, + 0x22, 0x20, 0x78, 0x47, 0x80, + + /* U+7303 "猃" */ + 0x0, 0x20, 0x90, 0xc0, 0xa2, 0x81, 0x84, 0x87, + 0x10, 0x8a, 0x40, 0xc4, 0x7e, 0xc, 0x0, 0x28, + 0x40, 0x54, 0x93, 0x29, 0x20, 0x49, 0x40, 0x91, + 0x1, 0x2, 0x6, 0xff, 0xb8, 0x0, + + /* U+730A "猊" */ + 0x80, 0x0, 0xae, 0x79, 0x90, 0x13, 0x20, 0x2a, + 0x7b, 0xc4, 0x80, 0x85, 0x1, 0x1a, 0x2, 0x57, + 0xfc, 0xa1, 0x42, 0x42, 0x80, 0x85, 0x1, 0x12, + 0x22, 0x64, 0xbb, 0xf, 0x0, + + /* U+730E "猎" */ + 0x40, 0x88, 0x91, 0x10, 0xcf, 0xf9, 0x84, 0x45, + 0x8, 0x82, 0x7f, 0xc6, 0x0, 0xc, 0x0, 0x29, + 0xfc, 0x94, 0x8, 0x27, 0xf0, 0x50, 0x20, 0xa0, + 0x41, 0x3f, 0x9c, 0x81, 0x0, + + /* U+7313 "猓" */ + 0x93, 0xfc, 0xc4, 0x89, 0x8f, 0xf5, 0x12, 0x21, + 0x24, 0x42, 0x7f, 0x8c, 0x10, 0x28, 0x20, 0xd7, + 0xff, 0x21, 0xc0, 0x45, 0x40, 0x92, 0x43, 0xc4, + 0x7c, 0x8, 0x0, + + /* U+7315 "猕" */ + 0x87, 0x40, 0x51, 0x40, 0x21, 0x7e, 0x61, 0x42, + 0xa7, 0x82, 0x14, 0x10, 0x14, 0x14, 0x38, 0x54, + 0x37, 0x54, 0x51, 0x92, 0x11, 0x92, 0x11, 0x92, + 0x13, 0x90, 0x13, 0x10, 0xee, 0x70, + + /* U+7316 "猖" */ + 0x49, 0xfc, 0x94, 0x8, 0xc8, 0x11, 0x8f, 0xe5, + 0x20, 0x42, 0x3f, 0x86, 0x0, 0xc, 0x0, 0x2b, + 0xfe, 0x94, 0x4, 0x28, 0x8, 0x5f, 0xf0, 0xa0, + 0x23, 0x7f, 0xdc, 0x80, 0x80, + + /* U+7317 "猗" */ + 0x0, 0x41, 0x10, 0x81, 0x5f, 0xf3, 0x2, 0x6, + 0xe, 0x14, 0x63, 0x9, 0x0, 0xf, 0xff, 0x30, + 0x8, 0xaf, 0x92, 0x51, 0x20, 0xa2, 0x41, 0x7c, + 0x82, 0x1, 0xc, 0x2, 0x70, 0x3c, + + /* U+731B "猛" */ + 0x8b, 0xfc, 0xa0, 0x30, 0x80, 0x83, 0x3f, 0xfa, + 0x2, 0x2, 0x4, 0xc, 0x78, 0x18, 0x0, 0x53, + 0xfd, 0x25, 0x28, 0x4a, 0x50, 0x94, 0xa1, 0xff, + 0xfc, 0x0, 0x0, + + /* U+731C "猜" */ + 0x40, 0x20, 0x97, 0xfc, 0xc0, 0x81, 0x9f, 0xf5, + 0x2, 0x2, 0x7f, 0xc2, 0x0, 0xc, 0xfe, 0x29, + 0x4, 0x93, 0xf8, 0x24, 0x10, 0x4f, 0xe0, 0x90, + 0x43, 0x20, 0x9c, 0x47, 0x0, + + /* U+731D "猝" */ + 0x40, 0x20, 0x48, 0x20, 0x33, 0xfe, 0x30, 0x0, + 0x71, 0x8, 0x51, 0x8, 0x9, 0x8c, 0x1a, 0x52, + 0x28, 0x40, 0x68, 0x20, 0xb, 0xff, 0x8, 0x20, + 0x8, 0x20, 0x8, 0x20, 0x70, 0x20, + + /* U+731E "猞" */ + 0x80, 0x40, 0x48, 0xa0, 0x51, 0x18, 0x22, 0xc, + 0xed, 0xfa, 0x20, 0x40, 0x10, 0x40, 0x37, 0xfe, + 0x50, 0x40, 0x93, 0xfc, 0x12, 0x4, 0x12, 0x4, + 0x12, 0x4, 0x13, 0xfc, 0xe2, 0x4, + + /* U+7321 "猡" */ + 0x0, 0x2, 0x5f, 0xf6, 0x49, 0x59, 0x25, 0xa7, + 0xfc, 0x82, 0x3, 0x8, 0xc, 0x7e, 0x52, 0xa, + 0x54, 0x21, 0x8d, 0x4, 0x8, 0x10, 0x40, 0x46, + 0xe, 0xe0, 0x0, + + /* U+7322 "猢" */ + 0x0, 0x1, 0x4, 0x1, 0x48, 0xfa, 0x91, 0x12, + 0x7a, 0x2c, 0x44, 0x48, 0x8f, 0x8b, 0xd1, 0x34, + 0xa2, 0x69, 0x45, 0x52, 0xfc, 0xa5, 0x11, 0x4a, + 0x22, 0xfc, 0x44, 0x10, 0xb0, 0x67, + + /* U+7325 "猥" */ + 0x8b, 0xfc, 0xa4, 0x89, 0x89, 0x13, 0x1f, 0xee, + 0x24, 0x54, 0x48, 0x84, 0xff, 0x18, 0x0, 0x57, + 0xff, 0xa5, 0x2, 0x49, 0x30, 0x91, 0x81, 0x22, + 0x6, 0x53, 0x38, 0xc1, 0x80, 0x0, + + /* U+7329 "猩" */ + 0x83, 0xfc, 0xa4, 0x9, 0x88, 0x13, 0x1f, 0xea, + 0x20, 0x44, 0x7f, 0x84, 0x48, 0x18, 0x90, 0x53, + 0xfd, 0x2c, 0x40, 0x50, 0x80, 0x9f, 0xe1, 0x2, + 0x6, 0xff, 0xf8, 0x0, 0x0, + + /* U+732A "猪" */ + 0x0, 0x40, 0x4c, 0x40, 0x29, 0xfa, 0x10, 0x44, + 0x70, 0x48, 0x13, 0xff, 0x8, 0x20, 0x18, 0x40, + 0x29, 0xfe, 0x4e, 0x82, 0x8, 0xfe, 0x8, 0x82, + 0x8, 0x82, 0x18, 0xfe, 0x70, 0x82, + + /* U+732B "猫" */ + 0x81, 0x10, 0xa2, 0x21, 0x5f, 0xf9, 0x8, 0x8e, + 0x0, 0x2, 0x7f, 0x84, 0x91, 0x19, 0x22, 0x52, + 0x45, 0x27, 0xf8, 0x49, 0x10, 0x92, 0x21, 0x24, + 0x42, 0x7f, 0xb8, 0x81, 0x0, + + /* U+732C "猬" */ + 0x87, 0xfe, 0xa8, 0x85, 0x91, 0x9, 0x3f, 0xfe, + 0x44, 0x34, 0xff, 0xc4, 0x0, 0x19, 0xfe, 0x52, + 0x5, 0x27, 0xf8, 0x48, 0x10, 0x9f, 0xe1, 0x20, + 0x46, 0x40, 0xb8, 0x8f, 0x0, + + /* U+732E "献" */ + 0x0, 0x10, 0x20, 0x2b, 0xfc, 0x50, 0x80, 0x81, + 0xf, 0xff, 0xe2, 0x29, 0x44, 0x44, 0x88, 0xbd, + 0x29, 0x22, 0x52, 0xf4, 0xa4, 0x89, 0x29, 0x14, + 0x52, 0x28, 0x61, 0xa0, 0x80, + + /* U+7331 "猱" */ + 0x93, 0xfc, 0xa0, 0x11, 0x86, 0xc3, 0x3, 0x6, + 0x7f, 0xd4, 0x14, 0x84, 0x49, 0x1b, 0x12, 0x70, + 0xc1, 0x2f, 0xf8, 0x43, 0x80, 0x8a, 0x81, 0x25, + 0x87, 0x89, 0xfa, 0x10, 0x0, + + /* U+7334 "猴" */ + 0x1, 0x0, 0x49, 0x7c, 0x52, 0x4, 0x22, 0x4, + 0x66, 0xff, 0xa6, 0x0, 0x3a, 0x7e, 0x32, 0x90, + 0x52, 0x90, 0xd2, 0x10, 0x12, 0xff, 0x12, 0x30, + 0x12, 0x28, 0x12, 0x44, 0xe2, 0x83, + + /* U+7337 "猷" */ + 0x44, 0x10, 0x48, 0x2b, 0xfc, 0x49, 0x40, 0x82, + 0x8f, 0xff, 0xe2, 0x2a, 0x44, 0x54, 0x98, 0xa7, + 0x31, 0x82, 0x53, 0xfc, 0xa4, 0xa, 0x48, 0x14, + 0x5f, 0xf8, 0xa0, 0x20, 0x80, + + /* U+7338 "猸" */ + 0x5b, 0xfd, 0x48, 0x92, 0x22, 0x78, 0xff, 0x22, + 0x0, 0x4f, 0xf3, 0x30, 0x54, 0xff, 0x93, 0x4, + 0x4c, 0x11, 0x7f, 0xc5, 0x41, 0x15, 0xff, 0xa4, + 0x10, + + /* U+7339 "猹" */ + 0x0, 0x42, 0x41, 0x5, 0x7f, 0xc8, 0x38, 0x61, + 0x5a, 0x99, 0x11, 0x3f, 0x8c, 0x82, 0x52, 0xa, + 0x4f, 0xe1, 0x20, 0x84, 0xfe, 0x10, 0x0, 0xff, + 0xfe, 0x0, 0x0, + + /* U+733E "猾" */ + 0x8b, 0xfa, 0x48, 0x25, 0x20, 0x98, 0xf2, 0x62, + 0x4a, 0x9f, 0xf1, 0x0, 0xc, 0xfe, 0x52, 0xa, + 0x4f, 0xe1, 0x20, 0x84, 0xfe, 0x12, 0x8, 0xc8, + 0x2e, 0x27, 0x80, + + /* U+733F "猿" */ + 0x80, 0x40, 0xb7, 0xf9, 0x41, 0x1, 0x2, 0x6, + 0x7f, 0xf4, 0x0, 0x4, 0xfe, 0x9, 0x4, 0x32, + 0x8, 0xa7, 0xf2, 0x43, 0x0, 0x89, 0x61, 0x71, + 0x7, 0x21, 0x38, 0x71, 0x80, + + /* U+734D "獍" */ + 0x0, 0x41, 0x0, 0x81, 0x5f, 0xf3, 0x8, 0x82, + 0x11, 0xd, 0xff, 0xe8, 0x0, 0xb, 0xfe, 0x34, + 0x4, 0xaf, 0xfa, 0x50, 0x10, 0xbf, 0xe1, 0xa, + 0x2, 0x24, 0x4c, 0xc8, 0xf6, 0xe, + + /* U+7350 "獐" */ + 0x0, 0x41, 0x0, 0x81, 0x5f, 0xfa, 0x88, 0x42, + 0x11, 0xc, 0xff, 0xe8, 0x0, 0x9, 0xfe, 0x32, + 0x4, 0x67, 0xfb, 0x48, 0x14, 0x9f, 0xe1, 0x4, + 0x2, 0xff, 0xcc, 0x10, 0x70, 0x20, + + /* U+7352 "獒" */ + 0x8, 0x20, 0xff, 0x20, 0x8, 0x7f, 0x7f, 0x44, + 0x8, 0x84, 0xf7, 0x38, 0x3f, 0x1c, 0x62, 0x66, + 0xce, 0x80, 0x1, 0x10, 0xff, 0xfe, 0x1, 0x80, + 0x6, 0x40, 0x18, 0x3c, 0xe0, 0x2, + + /* U+7357 "獗" */ + 0x0, 0x0, 0x57, 0xfe, 0x24, 0x4, 0x24, 0xc4, + 0xe5, 0xee, 0x14, 0x8a, 0x35, 0xa6, 0x35, 0xa4, + 0x55, 0xa4, 0x15, 0xe4, 0x14, 0x8c, 0x14, 0x8c, + 0x18, 0x8a, 0x19, 0x12, 0x69, 0x61, + + /* U+7360 "獠" */ + 0x0, 0x41, 0x0, 0x81, 0x5f, 0xf3, 0x5, 0x6, + 0x2a, 0xd4, 0x22, 0x5, 0xff, 0xb, 0x4, 0x32, + 0x8, 0xe7, 0xf3, 0x48, 0x24, 0x9f, 0xc1, 0x4, + 0x2, 0x4b, 0xd, 0x11, 0x70, 0xe0, + + /* U+736C "獬" */ + 0x0, 0x0, 0x42, 0x3e, 0x53, 0x8a, 0x24, 0x8a, + 0x67, 0xd2, 0xad, 0x56, 0x15, 0x60, 0x15, 0x54, + 0x37, 0xd4, 0x35, 0x5f, 0x55, 0x64, 0x17, 0xc4, + 0x15, 0x7f, 0x15, 0x44, 0x18, 0x44, 0x68, 0xc4, + + /* U+736D "獭" */ + 0x1, 0x10, 0x51, 0x1e, 0x57, 0xa6, 0x21, 0x44, + 0x67, 0xbe, 0x15, 0xa2, 0x15, 0xaa, 0x35, 0xaa, + 0x37, 0xaa, 0x51, 0x2a, 0x13, 0xaa, 0x15, 0x90, + 0x1d, 0x1c, 0x11, 0x22, 0x61, 0x40, + + /* U+736F "獯" */ + 0x0, 0x0, 0x97, 0xf9, 0x41, 0x1, 0x3f, 0xf6, + 0x4, 0x14, 0x7f, 0x84, 0xd1, 0x9, 0x6a, 0x33, + 0xfc, 0xa0, 0x82, 0x4f, 0xf0, 0x82, 0x1, 0x7f, + 0xe2, 0x0, 0x5, 0x25, 0x72, 0x49, + + /* U+737E "獾" */ + 0x80, 0x90, 0xaf, 0xfc, 0x82, 0x43, 0x3c, 0xfe, + 0x4a, 0x22, 0xf3, 0xc4, 0x48, 0x19, 0xff, 0x56, + 0x21, 0x37, 0xfc, 0x48, 0x80, 0x9f, 0xf1, 0x22, + 0x2, 0x7f, 0xf8, 0x80, 0x0, + + /* U+7384 "玄" */ + 0x1, 0x0, 0x1, 0x3, 0xff, 0xf8, 0x10, 0x0, + 0x40, 0x3, 0x3, 0x8, 0x8, 0x3f, 0xe0, 0x0, + 0x80, 0x2, 0x40, 0x18, 0xc0, 0x40, 0x81, 0x0, + 0x8f, 0xff, 0x80, 0x1, 0x80, + + /* U+7387 "率" */ + 0x1, 0x0, 0x2, 0x3, 0xff, 0xf8, 0x8, 0x4, + 0x20, 0x4c, 0x89, 0x8b, 0xe2, 0x0, 0x80, 0x36, + 0x49, 0x9f, 0xce, 0x0, 0x80, 0x8, 0xf, 0xff, + 0xe0, 0x20, 0x0, 0x40, 0x0, 0x80, + + /* U+7389 "玉" */ + 0x7f, 0xfc, 0x2, 0x0, 0x4, 0x0, 0x8, 0x0, + 0x10, 0xf, 0xff, 0x80, 0x40, 0x0, 0x90, 0x1, + 0x10, 0x2, 0x30, 0x4, 0x0, 0x8, 0xf, 0xff, + 0xe0, + + /* U+738B "王" */ + 0x7f, 0xfc, 0x2, 0x0, 0x4, 0x0, 0x8, 0x0, + 0x10, 0x0, 0x20, 0x1f, 0xff, 0x0, 0x80, 0x1, + 0x0, 0x2, 0x0, 0x4, 0x0, 0x8, 0xf, 0xff, + 0xe0, + + /* U+738E "玎" */ + 0xfd, 0xff, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0xfc, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x7c, 0x10, + 0x80, 0x10, 0x0, 0xf0, + + /* U+7391 "玑" */ + 0x1, 0xf1, 0xf2, 0x20, 0x84, 0x41, 0x8, 0x82, + 0x11, 0x4, 0x22, 0x3e, 0x44, 0x10, 0x88, 0x21, + 0x10, 0x42, 0x20, 0x84, 0x41, 0xd0, 0x9e, 0x21, + 0x20, 0x82, 0x42, 0x7, 0x0, + + /* U+7396 "玖" */ + 0x0, 0x0, 0x2, 0x3, 0xe4, 0x1, 0xf, 0x82, + 0x21, 0x4, 0x42, 0x9, 0x4, 0x7e, 0x10, 0x20, + 0x20, 0x40, 0xc0, 0x81, 0x81, 0x5, 0x3, 0x91, + 0x18, 0x61, 0x1, 0x81, 0x4, 0x0, + + /* U+739B "玛" */ + 0xf9, 0xf8, 0x80, 0x22, 0x11, 0x8, 0x44, 0x22, + 0x13, 0xe8, 0x42, 0x3f, 0xc8, 0x1, 0x20, 0x4, + 0x8f, 0xd3, 0x80, 0x70, 0x1, 0x0, 0xc, 0x1, + 0xe0, + + /* U+739F "玟" */ + 0x0, 0xc0, 0x0, 0x83, 0xe0, 0x1, 0x3f, 0xe2, + 0x20, 0x84, 0x41, 0x3e, 0x42, 0x10, 0x88, 0x20, + 0x90, 0x41, 0x40, 0x81, 0x1, 0xc5, 0xc, 0x11, + 0x0, 0xc1, 0x81, 0x0, 0x0, + + /* U+73A2 "玢" */ + 0x0, 0xa1, 0xf1, 0x20, 0x84, 0x41, 0x8, 0x42, + 0x20, 0x84, 0xc0, 0xbd, 0x0, 0x91, 0xfc, 0x20, + 0x88, 0x41, 0x10, 0x82, 0x21, 0x48, 0x4f, 0x10, + 0x80, 0x41, 0x3, 0x3c, 0x0, + + /* U+73A9 "玩" */ + 0xf9, 0xfc, 0x40, 0x0, 0x80, 0x1, 0x0, 0x2, + 0x3f, 0xff, 0x12, 0x8, 0x24, 0x10, 0x48, 0x21, + 0x10, 0x4a, 0x21, 0xe4, 0x4c, 0x10, 0x90, 0x41, + 0xe1, 0x0, 0x0, + + /* U+73AB "玫" */ + 0x0, 0x80, 0x1, 0x3, 0xe4, 0x1, 0xf, 0xe2, + 0x20, 0x84, 0x62, 0x3f, 0x44, 0x10, 0x88, 0x21, + 0x10, 0x41, 0x40, 0x81, 0x1, 0xc7, 0xc, 0x13, + 0x0, 0xc1, 0x82, 0x0, 0x0, + + /* U+73AE "玮" */ + 0x0, 0x41, 0xf0, 0x80, 0x8f, 0xf9, 0x2, 0x2, + 0x4, 0x4, 0x7f, 0xbe, 0x10, 0x10, 0x20, 0x23, + 0xfe, 0x40, 0x84, 0x81, 0xb, 0xc2, 0x18, 0x5, + 0xc0, 0x8, 0x0, 0x10, 0x0, + + /* U+73AF "环" */ + 0x3, 0xfd, 0xf0, 0x80, 0x81, 0x1, 0x2, 0x2, + 0x8, 0x4, 0x10, 0x3e, 0x54, 0x10, 0xa8, 0x22, + 0x48, 0x44, 0x88, 0x91, 0x11, 0x2, 0x7, 0x84, + 0x10, 0x8, 0x0, 0x10, 0x0, + + /* U+73B0 "现" */ + 0x3, 0xfd, 0xf8, 0x8, 0x90, 0x11, 0x22, 0x22, + 0x44, 0x44, 0x88, 0xbf, 0x11, 0x12, 0x22, 0x24, + 0x44, 0x41, 0x48, 0x82, 0x81, 0x45, 0x1f, 0x12, + 0x20, 0x44, 0x43, 0xf, 0x0, + + /* U+73B2 "玲" */ + 0x0, 0x61, 0xf0, 0xc0, 0x82, 0x41, 0x4, 0xc2, + 0x10, 0xc4, 0x48, 0xff, 0x8, 0x10, 0x0, 0x23, + 0xfc, 0x40, 0x10, 0x80, 0x61, 0xc8, 0x8e, 0xa, + 0x0, 0xc, 0x0, 0x4, 0x0, + + /* U+73B3 "玳" */ + 0x0, 0xa1, 0xf2, 0x50, 0x84, 0x91, 0x9, 0x2, + 0x33, 0xe4, 0x7c, 0x3f, 0x48, 0x12, 0x90, 0x21, + 0x20, 0x42, 0x40, 0x84, 0x41, 0x28, 0x9f, 0x91, + 0x20, 0x23, 0x40, 0x43, 0x0, + + /* U+73B7 "玷" */ + 0x0, 0x41, 0xf0, 0x80, 0x81, 0x1, 0x3, 0xf2, + 0x4, 0x4, 0x8, 0x8, 0x10, 0x38, 0x20, 0x23, + 0xfc, 0x44, 0x8, 0x88, 0x11, 0x10, 0x2f, 0xa0, + 0x40, 0x7f, 0x80, 0x81, 0x0, + + /* U+73BA "玺" */ + 0x18, 0x0, 0x3f, 0xf8, 0x80, 0x16, 0x4, 0x41, + 0x8b, 0x6, 0x11, 0x90, 0xc0, 0x0, 0x0, 0x7f, + 0xfc, 0x2, 0x0, 0xff, 0xf0, 0x9, 0x0, 0x11, + 0x1f, 0xff, 0xc0, + + /* U+73BB "玻" */ + 0x0, 0x21, 0xf0, 0x40, 0x8f, 0xf1, 0x11, 0x22, + 0x22, 0x44, 0x44, 0x3c, 0xfe, 0x11, 0x44, 0x22, + 0x88, 0x45, 0x10, 0x91, 0x43, 0xa1, 0x88, 0x47, + 0x1, 0x31, 0x84, 0x81, 0x0, + + /* U+73C0 "珀" */ + 0x0, 0x21, 0xf8, 0x40, 0x87, 0xf9, 0x8, 0x12, + 0x10, 0x24, 0x20, 0x7f, 0x40, 0x90, 0xff, 0x21, + 0x2, 0x42, 0x4, 0x84, 0x9, 0x8, 0x17, 0xd0, + 0x30, 0x3f, 0xc0, 0x40, 0x80, + + /* U+73C2 "珂" */ + 0xfb, 0xfe, 0x40, 0x8, 0x80, 0x11, 0x1f, 0x22, + 0x22, 0x5f, 0x44, 0x88, 0x89, 0x11, 0x12, 0x22, + 0x24, 0x47, 0xc8, 0xf0, 0x17, 0x0, 0x20, 0x0, + 0x40, 0x7, 0x0, + + /* U+73C8 "珈" */ + 0x2, 0x0, 0x8, 0xf, 0x23, 0xd3, 0xe9, 0x42, + 0xa5, 0xa, 0x9f, 0x2a, 0x50, 0xa9, 0x42, 0xa5, + 0xa, 0x94, 0x6a, 0x5d, 0x29, 0x84, 0xbc, 0x22, + 0x91, 0xb8, 0x0, + + /* U+73C9 "珉" */ + 0x3, 0xfd, 0xf4, 0x8, 0x88, 0x11, 0x10, 0x22, + 0x3f, 0xc4, 0x44, 0x3e, 0x88, 0x11, 0x10, 0x23, + 0xfe, 0x44, 0x40, 0x88, 0x81, 0x11, 0x3, 0xa1, + 0x38, 0x72, 0x40, 0x83, 0x0, + + /* U+73CA "珊" */ + 0x7, 0xbd, 0xe9, 0x49, 0x12, 0x92, 0x25, 0x24, + 0x4a, 0x48, 0x94, 0xbf, 0xff, 0xa2, 0x52, 0x44, + 0xa4, 0x89, 0x49, 0x12, 0x92, 0xa5, 0x2e, 0x8a, + 0x41, 0x18, 0x84, 0xf7, 0x0, + + /* U+73CD "珍" */ + 0x0, 0x0, 0x0, 0x83, 0xe2, 0x81, 0x8, 0x82, + 0x20, 0x84, 0x8c, 0xc8, 0x20, 0x7c, 0x80, 0x26, + 0x10, 0x40, 0xc0, 0x82, 0x11, 0x18, 0x42, 0x83, + 0x1e, 0x8, 0x0, 0xe0, 0x2, 0x0, + + /* U+73CF "珏" */ + 0xfb, 0xfc, 0x40, 0x80, 0x81, 0x1, 0x2, 0x2, + 0x4, 0xe, 0x7f, 0x88, 0x10, 0x10, 0x20, 0x20, + 0x48, 0x40, 0x90, 0xf1, 0x6, 0x2, 0x0, 0x7f, + 0xe0, + + /* U+73D0 "珐" */ + 0x0, 0x41, 0xf0, 0x80, 0x81, 0x1, 0x3f, 0xe2, + 0x4, 0x4, 0x8, 0x3e, 0x10, 0x13, 0xff, 0x20, + 0x80, 0x41, 0x0, 0x84, 0x41, 0xd0, 0x8c, 0x40, + 0x80, 0xff, 0x80, 0x0, 0x0, + + /* U+73D1 "珑" */ + 0x1, 0x10, 0xf9, 0x8, 0x21, 0x0, 0x27, 0xfe, + 0x21, 0x20, 0x21, 0x20, 0xf9, 0x24, 0x21, 0x24, + 0x21, 0x28, 0x22, 0x30, 0x22, 0x30, 0x3a, 0x61, + 0xc4, 0xa2, 0x5, 0x22, 0x8, 0x3e, + + /* U+73D9 "珙" */ + 0x1, 0x11, 0xf2, 0x20, 0x84, 0x41, 0x1f, 0xe2, + 0x11, 0x4, 0x22, 0x3e, 0x44, 0x10, 0x88, 0x21, + 0x10, 0x4f, 0xfc, 0x80, 0x1, 0xe9, 0x8e, 0x31, + 0x80, 0xc0, 0x81, 0x0, 0x80, + + /* U+73DE "珞" */ + 0x0, 0x80, 0x2, 0x3, 0xe7, 0xf1, 0x18, 0x42, + 0x70, 0x84, 0x92, 0x8, 0x18, 0x7c, 0x58, 0x23, + 0xc, 0x48, 0x0, 0x8f, 0xf1, 0x10, 0x22, 0xa0, + 0x5e, 0x40, 0x80, 0xff, 0x1, 0x2, + + /* U+73E0 "珠" */ + 0x0, 0x40, 0xf9, 0x40, 0x22, 0x40, 0x23, 0xfe, + 0x22, 0x40, 0x24, 0x40, 0xf8, 0x40, 0x27, 0xfe, + 0x20, 0x60, 0x20, 0xe0, 0x21, 0x50, 0x3a, 0x48, + 0xc4, 0x46, 0x0, 0x40, 0x0, 0x40, + + /* U+73E5 "珥" */ + 0x3, 0xff, 0xf2, 0x10, 0x84, 0x21, 0x8, 0x42, + 0x1f, 0x84, 0x21, 0x3e, 0x42, 0x10, 0x84, 0x21, + 0xf8, 0x42, 0x10, 0x84, 0x21, 0x28, 0xff, 0xbe, + 0x80, 0x1, 0x0, 0x2, 0x0, + + /* U+73E7 "珧" */ + 0x0, 0x90, 0xf8, 0x90, 0x20, 0x92, 0x22, 0x92, + 0x22, 0x94, 0x21, 0x98, 0xf8, 0x90, 0x20, 0x90, + 0x21, 0x9c, 0x26, 0x92, 0x20, 0x90, 0x21, 0x10, + 0xf9, 0x12, 0x2, 0x12, 0x6, 0x1e, + + /* U+73E9 "珩" */ + 0x1, 0x1, 0xf2, 0x7c, 0x88, 0x1, 0x30, 0x2, + 0x8, 0x4, 0x20, 0x3e, 0x5f, 0x91, 0x84, 0x25, + 0x8, 0x42, 0x10, 0x84, 0x21, 0x8, 0x4f, 0x90, + 0x80, 0x21, 0x0, 0x5c, 0x0, + + /* U+73ED "班" */ + 0x0, 0x81, 0xf1, 0xf8, 0x82, 0x41, 0x24, 0x82, + 0x49, 0x4, 0x92, 0x3f, 0x24, 0x12, 0x7e, 0x24, + 0x90, 0x41, 0x20, 0x84, 0x41, 0x48, 0x8d, 0x31, + 0x0, 0x4f, 0xc1, 0x0, 0x0, + + /* U+73F2 "珲" */ + 0x3, 0xff, 0xf4, 0x4, 0x89, 0x9, 0x4, 0x2, + 0x7f, 0xe4, 0x20, 0x3e, 0x48, 0x11, 0x90, 0x23, + 0xfe, 0x40, 0x40, 0x80, 0x81, 0xdf, 0xfe, 0x2, + 0x0, 0x4, 0x0, 0x8, 0x0, + + /* U+7403 "球" */ + 0x0, 0x40, 0x0, 0xa3, 0xe1, 0x21, 0x3f, 0xf2, + 0x4, 0x4, 0x8, 0x8, 0x91, 0x7d, 0x34, 0x21, + 0x50, 0x41, 0xa0, 0x85, 0x21, 0x32, 0x43, 0x84, + 0x78, 0x8, 0x0, 0x70, 0x0, + + /* U+7405 "琅" */ + 0x0, 0x40, 0x0, 0x83, 0xef, 0xf1, 0x20, 0x22, + 0x40, 0x44, 0x7f, 0x89, 0x1, 0x7e, 0x2, 0x23, + 0xfc, 0x49, 0x0, 0x91, 0x31, 0x23, 0x83, 0xc2, + 0x18, 0x93, 0x1, 0xc1, 0x80, 0x0, + + /* U+7406 "理" */ + 0x3, 0xfe, 0xfa, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x23, 0xfe, 0x22, 0x22, 0xfa, 0x22, 0x23, 0xfe, + 0x20, 0x20, 0x20, 0x20, 0x23, 0xfe, 0x28, 0x20, + 0xd0, 0x20, 0x7, 0xff, + + /* U+7409 "琉" */ + 0x0, 0x41, 0xf0, 0x40, 0x8f, 0xf1, 0x6, 0x2, + 0x11, 0x4, 0x41, 0x3e, 0xff, 0x10, 0x2, 0x20, + 0x0, 0x44, 0x90, 0x89, 0x21, 0x12, 0x47, 0xa4, + 0xb0, 0x89, 0x41, 0x13, 0x80, + + /* U+740A "琊" */ + 0x7, 0x9f, 0xe1, 0x25, 0x2, 0x5a, 0x24, 0xa4, + 0x49, 0x48, 0x93, 0x19, 0xe5, 0x20, 0xca, 0x42, + 0x92, 0x85, 0x25, 0x4a, 0x4f, 0x24, 0xb0, 0x89, + 0x80, 0x12, 0x0, 0xe4, 0x0, + + /* U+740F "琏" */ + 0x4, 0x20, 0x8, 0x83, 0xcb, 0xfa, 0x12, 0x4, + 0x9, 0x8, 0xd2, 0x3c, 0xa4, 0x21, 0x3e, 0x42, + 0x10, 0x84, 0x21, 0xf, 0xf9, 0x90, 0x8c, 0x61, + 0x1, 0xa0, 0x6, 0x3f, 0x80, + + /* U+7410 "琐" */ + 0x0, 0x41, 0xf4, 0x88, 0x85, 0x21, 0x2, 0x2, + 0x3f, 0xc4, 0x40, 0xbe, 0x91, 0x11, 0x22, 0x22, + 0x44, 0x44, 0x88, 0x89, 0x13, 0xc7, 0x8, 0x19, + 0x80, 0x41, 0x83, 0x0, 0x0, + + /* U+741A "琚" */ + 0xfb, 0xfc, 0x44, 0x8, 0x88, 0x11, 0x1f, 0xe2, + 0x20, 0x1f, 0x44, 0x8, 0xff, 0x91, 0x10, 0x22, + 0x20, 0x45, 0xf8, 0xac, 0x16, 0xa8, 0x20, 0x4f, + 0xc1, 0x20, 0x80, + + /* U+741B "琛" */ + 0xfb, 0xfe, 0x22, 0x2, 0x22, 0x9a, 0x20, 0x88, + 0x21, 0x4, 0xfa, 0x20, 0x27, 0xff, 0x20, 0x70, + 0x20, 0xb0, 0x20, 0xa8, 0x79, 0x24, 0x86, 0x22, + 0x0, 0x20, 0x0, 0x20, + + /* U+7422 "琢" */ + 0xfb, 0xfe, 0x20, 0x20, 0x20, 0xc0, 0x21, 0x46, + 0x26, 0x48, 0xf8, 0x78, 0x22, 0xa8, 0x23, 0x28, + 0x24, 0x68, 0x20, 0xa4, 0x39, 0x22, 0xc6, 0x23, + 0x0, 0x20, 0x1, 0xe0, + + /* U+7425 "琥" */ + 0x0, 0x40, 0x0, 0x7e, 0xf8, 0x40, 0x23, 0xfe, + 0x22, 0x42, 0x22, 0x78, 0xfa, 0xc2, 0x22, 0x7c, + 0x22, 0x0, 0x22, 0xf0, 0x22, 0x90, 0x3e, 0x90, + 0xc4, 0x92, 0x4, 0x92, 0x9, 0x1e, + + /* U+7426 "琦" */ + 0x0, 0x20, 0x0, 0x20, 0xfb, 0xfe, 0x20, 0x20, + 0x20, 0x50, 0x21, 0x8c, 0x22, 0x0, 0xff, 0xff, + 0x20, 0x4, 0x23, 0xe4, 0x22, 0x24, 0x22, 0x24, + 0x3b, 0xe4, 0xc0, 0x4, 0x0, 0x4, 0x0, 0x3c, + + /* U+7428 "琨" */ + 0x3, 0xfd, 0xf4, 0x8, 0x8f, 0xf1, 0x10, 0x22, + 0x20, 0x5f, 0x7f, 0x88, 0x0, 0x11, 0x10, 0x23, + 0xac, 0x44, 0x60, 0xe8, 0x8f, 0x11, 0x10, 0x3a, + 0x20, 0x47, 0x80, + + /* U+742A "琪" */ + 0x1, 0x8, 0x2, 0x13, 0xe4, 0x21, 0x1f, 0xe2, + 0x10, 0x84, 0x21, 0x8, 0x7e, 0x7c, 0x84, 0x21, + 0xf8, 0x42, 0x10, 0x84, 0x21, 0x3f, 0xf2, 0xc0, + 0x1e, 0x12, 0x0, 0xc3, 0x2, 0x0, + + /* U+742C "琬" */ + 0x0, 0x41, 0xe0, 0x81, 0x1f, 0xf2, 0x40, 0x24, + 0x20, 0x8, 0x77, 0xbd, 0x29, 0x22, 0x52, 0x44, + 0xa4, 0x97, 0x49, 0x4, 0xb2, 0x9, 0xf, 0x22, + 0x40, 0xc4, 0x83, 0xf, 0x0, + + /* U+742E "琮" */ + 0x0, 0x20, 0xf8, 0x20, 0x27, 0xfe, 0x24, 0x2, + 0x24, 0x2, 0x23, 0xfc, 0x20, 0x0, 0x70, 0x0, + 0x27, 0xfe, 0x20, 0x20, 0x21, 0x28, 0x29, 0x24, + 0xf2, 0x22, 0x4, 0x22, 0x0, 0xe0, + + /* U+7430 "琰" */ + 0x0, 0x20, 0xf9, 0x26, 0x21, 0x24, 0x20, 0x20, + 0x20, 0x50, 0x20, 0xcc, 0xf9, 0x6, 0x22, 0x20, + 0x22, 0x20, 0x21, 0x26, 0x21, 0x68, 0x20, 0x50, + 0xf8, 0xc8, 0x1, 0x8c, 0x6, 0x2, + + /* U+7433 "琳" */ + 0x2, 0x20, 0x4, 0x43, 0xc8, 0x82, 0x7b, 0xe4, + 0x23, 0x8, 0x6a, 0x3d, 0xdc, 0x25, 0x34, 0x4a, + 0x68, 0x95, 0x49, 0x4a, 0x92, 0x11, 0x7, 0x22, + 0x10, 0x44, 0x0, 0x88, 0x0, + + /* U+7434 "琴" */ + 0x7e, 0x7e, 0x8, 0x10, 0x3e, 0x7c, 0x8, 0x10, + 0x7f, 0xfe, 0x3, 0x80, 0x6, 0x60, 0x39, 0x1c, + 0x40, 0x82, 0x1f, 0xf8, 0x0, 0x10, 0x0, 0x60, + 0x0, 0xc0, 0x1, 0x0, + + /* U+7435 "琵" */ + 0xff, 0xfc, 0x40, 0x87, 0xef, 0xc4, 0x8, 0x10, + 0x23, 0xff, 0xf0, 0x0, 0x10, 0x40, 0x7f, 0x19, + 0x7, 0x84, 0x10, 0x50, 0x41, 0x4f, 0x5, 0xc3, + 0xe0, + + /* U+7436 "琶" */ + 0xfe, 0xfc, 0x20, 0x40, 0x40, 0x83, 0xf7, 0xe1, + 0x2, 0x1f, 0xdf, 0x80, 0x0, 0x3f, 0xfc, 0x41, + 0x8, 0x82, 0x11, 0xff, 0xe2, 0x0, 0x14, 0x0, + 0x47, 0xff, 0x80, + + /* U+743C "琼" */ + 0x0, 0x40, 0x0, 0x43, 0xff, 0xf9, 0x0, 0x2, + 0x0, 0x4, 0x7f, 0xbe, 0x81, 0x11, 0x2, 0x23, + 0xfc, 0x40, 0x40, 0x80, 0x81, 0x51, 0x2f, 0x22, + 0x60, 0x84, 0x40, 0x38, 0x0, + + /* U+7441 "瑁" */ + 0x7, 0xff, 0xf0, 0x12, 0x5f, 0x49, 0x1, 0x25, + 0xf4, 0x80, 0x7, 0x3f, 0x88, 0x82, 0x23, 0xf8, + 0x88, 0x23, 0xbf, 0xb0, 0x82, 0x3, 0xf8, 0x8, + 0x20, + + /* U+7455 "瑕" */ + 0x7, 0xbd, 0xe9, 0x9, 0x12, 0x12, 0x24, 0x24, + 0x48, 0x48, 0xf7, 0xbd, 0x0, 0x23, 0xfe, 0x44, + 0x44, 0x88, 0x51, 0x1e, 0xa1, 0xa0, 0x8c, 0x43, + 0x80, 0x8c, 0x81, 0x20, 0x80, + + /* U+7457 "瑗" */ + 0x3, 0xfe, 0xf8, 0x0, 0x21, 0x24, 0x21, 0x28, + 0x23, 0xfe, 0x20, 0x80, 0xf8, 0x80, 0xfb, 0xff, + 0x20, 0x80, 0x21, 0xfc, 0x21, 0x84, 0x3a, 0x48, + 0xe2, 0x30, 0x4, 0x78, 0xb, 0x87, + + /* U+7459 "瑙" */ + 0x1, 0x23, 0xf4, 0x48, 0x89, 0x11, 0x12, 0x22, + 0x12, 0x44, 0x20, 0x7e, 0x10, 0x11, 0xff, 0x22, + 0x2, 0x45, 0x34, 0x89, 0x89, 0xd6, 0xdc, 0x20, + 0x20, 0x7f, 0xc0, 0x80, 0x80, + + /* U+745A "瑚" */ + 0x2, 0x0, 0x8, 0xf6, 0x22, 0x53, 0xe9, 0x42, + 0x25, 0x8, 0xf4, 0x22, 0x59, 0xe9, 0x48, 0xa5, + 0x22, 0xf4, 0x8a, 0x4d, 0xe9, 0xc8, 0x64, 0x1, + 0x10, 0x9, 0xc0, + + /* U+745B "瑛" */ + 0x1, 0x11, 0xe2, 0x21, 0x3f, 0xf2, 0x8, 0x84, + 0x4, 0x8, 0xff, 0x3d, 0x12, 0x22, 0x24, 0x44, + 0x48, 0x9f, 0xfd, 0x3, 0x3, 0x85, 0x8, 0x11, + 0x0, 0xc1, 0x82, 0x0, 0x0, + + /* U+745C "瑜" */ + 0x0, 0x0, 0x0, 0xc3, 0xe1, 0x41, 0x4, 0x82, + 0x10, 0x84, 0xde, 0xc8, 0x0, 0x11, 0xe2, 0x72, + 0x54, 0x44, 0xa8, 0x8f, 0x51, 0x12, 0xa2, 0xbd, + 0x5e, 0x48, 0x80, 0x91, 0x1, 0xee, + + /* U+745E "瑞" */ + 0x0, 0x21, 0xf4, 0x48, 0x88, 0x91, 0x1f, 0xe2, + 0x0, 0x4, 0x0, 0x3e, 0xff, 0x10, 0x60, 0x23, + 0xfc, 0x45, 0x28, 0x8a, 0x51, 0xd4, 0xac, 0x29, + 0x40, 0x50, 0x80, 0x83, 0x0, + + /* U+745F "瑟" */ + 0x7e, 0xfe, 0x8, 0x10, 0x3e, 0xfc, 0x8, 0x10, + 0x8, 0x10, 0x7e, 0xfe, 0x0, 0x0, 0x9, 0x8, + 0x28, 0x30, 0x28, 0x44, 0x49, 0x82, 0x4e, 0x10, + 0x38, 0x10, 0x47, 0xf0, + + /* U+746D "瑭" */ + 0x0, 0x40, 0x0, 0x83, 0xdf, 0xf2, 0x20, 0x4, + 0x44, 0x8, 0xbf, 0x11, 0x12, 0x7a, 0xff, 0x44, + 0x48, 0x8b, 0xf1, 0x11, 0x2, 0x2f, 0xcf, 0x60, + 0x81, 0x41, 0x2, 0x7e, 0x9, 0x4, + + /* U+7470 "瑰" */ + 0x0, 0x40, 0x1, 0x3, 0xef, 0xf1, 0x12, 0x22, + 0x24, 0x44, 0x7f, 0x88, 0x91, 0x7d, 0x22, 0x23, + 0xfc, 0x41, 0x0, 0x83, 0x41, 0x6, 0xc7, 0x97, + 0xf8, 0x48, 0xc1, 0x90, 0x86, 0x3e, + + /* U+7476 "瑶" */ + 0x1, 0xfe, 0xfb, 0x0, 0x20, 0x2, 0x22, 0x24, + 0x20, 0x24, 0x21, 0x0, 0xfb, 0xfe, 0x22, 0x20, + 0x20, 0x20, 0x27, 0xff, 0x20, 0x20, 0x20, 0x20, + 0xf2, 0x22, 0x3, 0xfe, 0x0, 0x2, + + /* U+7477 "瑷" */ + 0x3, 0xff, 0xf0, 0x0, 0x84, 0x91, 0x8, 0x42, + 0x3f, 0xe4, 0x40, 0x7e, 0x20, 0x11, 0xff, 0x20, + 0x80, 0x41, 0xf8, 0x86, 0x11, 0xea, 0x4c, 0x23, + 0x0, 0x8f, 0x2, 0x61, 0x80, + + /* U+747E "瑾" */ + 0x2, 0x9, 0xff, 0xfd, 0x8, 0x22, 0x1f, 0xc4, + 0x4, 0x8, 0xff, 0xfe, 0x10, 0xa3, 0xff, 0x40, + 0x40, 0x9f, 0xfd, 0x1, 0x3, 0xbf, 0xe8, 0x4, + 0x1, 0xff, 0xc0, + + /* U+7480 "璀" */ + 0x0, 0x20, 0x4, 0x4b, 0xe8, 0x91, 0x1f, 0xe2, + 0x12, 0x4, 0x24, 0x8, 0xff, 0xbb, 0x10, 0x27, + 0xfe, 0x44, 0x40, 0x88, 0x81, 0x1f, 0xff, 0xa2, + 0x0, 0x7f, 0xc0, 0x80, 0x0, + + /* U+7481 "璁" */ + 0x0, 0x0, 0x1, 0x3, 0xdf, 0xf2, 0x24, 0x24, + 0x4f, 0x48, 0xa2, 0x91, 0x19, 0x7a, 0x72, 0x45, + 0x4, 0x8f, 0xf9, 0x1, 0x2, 0x2, 0x5, 0x52, + 0x5d, 0x21, 0x42, 0x44, 0x80, 0xf8, + + /* U+7483 "璃" */ + 0x0, 0x20, 0x0, 0x43, 0xef, 0xf9, 0x4, 0x2, + 0x27, 0x44, 0x4a, 0x88, 0xa1, 0x7d, 0xfe, 0x20, + 0x20, 0x40, 0x40, 0x8f, 0xf1, 0x12, 0xa3, 0xaf, + 0x58, 0x50, 0x80, 0x81, 0x1, 0x6, + + /* U+7487 "璇" */ + 0x0, 0x10, 0x2, 0x20, 0xf2, 0x3f, 0x4f, 0xe0, + 0x44, 0x40, 0x44, 0x3f, 0xf7, 0xa, 0x45, 0x28, + 0x45, 0x28, 0x45, 0x2e, 0x45, 0x28, 0x55, 0x28, + 0xe9, 0x58, 0x9, 0x4f, 0xe, 0x40, + + /* U+748B "璋" */ + 0x0, 0x20, 0x0, 0x43, 0xef, 0xf9, 0x8, 0x42, + 0x9, 0x4, 0xff, 0xc8, 0x0, 0x7d, 0xfe, 0x22, + 0x4, 0x47, 0xf8, 0x88, 0x11, 0x1f, 0xe3, 0x82, + 0x18, 0xff, 0xc0, 0x8, 0x0, 0x10, + + /* U+748E "璎" */ + 0x7, 0xbf, 0xf1, 0x45, 0x2a, 0xaa, 0x55, 0x54, + 0xaa, 0xa8, 0x41, 0x3d, 0x45, 0x22, 0x51, 0x48, + 0x80, 0x9f, 0xfd, 0x8, 0x61, 0xb1, 0x8c, 0x1e, + 0x0, 0x63, 0x83, 0x0, 0x0, + + /* U+7490 "璐" */ + 0x0, 0x0, 0x7, 0x90, 0xf4, 0xbe, 0x24, 0xa6, + 0x24, 0xd4, 0x27, 0x98, 0x21, 0x18, 0xf1, 0x34, + 0x25, 0x43, 0x25, 0x0, 0x25, 0xbc, 0x25, 0x24, + 0x25, 0x24, 0xf5, 0xa4, 0xe, 0x3c, 0x0, 0x24, + + /* U+749C "璜" */ + 0x0, 0x88, 0xfb, 0xfe, 0x20, 0x88, 0x20, 0x88, + 0x27, 0xff, 0x20, 0x20, 0xfb, 0xfe, 0x22, 0x22, + 0x23, 0xfe, 0x22, 0x22, 0x22, 0x22, 0x2b, 0xfe, + 0xf0, 0x0, 0x1, 0x8c, 0x6, 0x3, + + /* U+749E "璞" */ + 0x0, 0x90, 0x9, 0x2b, 0xca, 0x52, 0x4, 0x84, + 0x7f, 0xe8, 0x21, 0x10, 0x24, 0x7b, 0xff, 0x40, + 0x40, 0x87, 0xf9, 0x1, 0x2, 0x2, 0x4, 0x7f, + 0xee, 0xc, 0x20, 0x24, 0x3, 0x87, 0x8, 0x0, + + /* U+74A7 "璧" */ + 0x7e, 0x20, 0xc5, 0xfd, 0x89, 0x3, 0xf2, 0x44, + 0xf, 0xeb, 0xc2, 0x38, 0xbf, 0xcf, 0x8, 0x0, + 0x0, 0xff, 0xf8, 0x4, 0x1, 0xff, 0xe0, 0x12, + 0x0, 0x22, 0x3f, 0xff, 0x80, + + /* U+74A8 "璨" */ + 0x1, 0x1, 0xe3, 0x39, 0x14, 0x12, 0x3d, 0x24, + 0xc9, 0x89, 0x12, 0xbc, 0x59, 0xa3, 0x0, 0x42, + 0x48, 0x82, 0xa1, 0x3f, 0xfa, 0x7, 0xe, 0x15, + 0x0, 0xc9, 0xc2, 0x10, 0x0, + + /* U+74A9 "璩" */ + 0x0, 0x81, 0xe1, 0xf1, 0x1f, 0xf2, 0x44, 0x24, + 0xbf, 0x9, 0x11, 0x3e, 0x1c, 0x25, 0xfe, 0x48, + 0xc0, 0x96, 0x91, 0x22, 0xc3, 0xdb, 0x48, 0x8a, + 0x42, 0xe4, 0x4, 0x30, 0x0, + + /* U+74BA "璺" */ + 0x6f, 0xd9, 0x3d, 0x26, 0x85, 0x93, 0xd2, 0x6d, + 0x59, 0x29, 0x2f, 0xff, 0xe0, 0x1, 0xbf, 0xf4, + 0x8, 0x7, 0xff, 0x80, 0x88, 0xff, 0xfc, + + /* U+74D2 "瓒" */ + 0x0, 0x8, 0xc, 0x53, 0xfe, 0xf2, 0x50, 0x44, + 0xfb, 0xe8, 0xa5, 0x11, 0x6a, 0xb4, 0x2, 0x47, + 0xf8, 0x90, 0x11, 0x21, 0x22, 0xc2, 0x4e, 0xe, + 0x0, 0x33, 0x3, 0x81, 0x80, + + /* U+74DC "瓜" */ + 0x0, 0xc, 0x3f, 0xe0, 0x44, 0x40, 0x88, 0x81, + 0x11, 0x2, 0x22, 0x4, 0x44, 0x8, 0x88, 0x11, + 0x8, 0x22, 0x90, 0x49, 0x21, 0x12, 0x62, 0x22, + 0x48, 0xfc, 0x70, 0x4, 0x80, + + /* U+74DE "瓞" */ + 0x0, 0x10, 0x7e, 0x50, 0x52, 0x90, 0x52, 0xfe, + 0x52, 0x90, 0x52, 0x10, 0x52, 0xfe, 0x52, 0x10, + 0x52, 0x38, 0x52, 0x28, 0x53, 0x44, 0x55, 0xc2, + 0xa5, 0x80, 0xbe, 0xe0, 0x82, 0x3e, + + /* U+74E0 "瓠" */ + 0x8, 0x0, 0x10, 0xfe, 0x7f, 0x24, 0x19, 0x24, + 0x27, 0x24, 0x7f, 0x24, 0x1, 0x24, 0x1, 0x24, + 0x7f, 0x24, 0x21, 0x24, 0x3d, 0x2a, 0x5, 0x2a, + 0x5, 0x4a, 0x5, 0x75, 0x3a, 0x1, + + /* U+74E2 "瓢" */ + 0x0, 0x1, 0xfd, 0xf8, 0xa2, 0xa7, 0xf5, 0x4a, + 0xaa, 0x95, 0x55, 0x3f, 0xaa, 0x0, 0x54, 0x7c, + 0xa8, 0x1, 0x53, 0xfc, 0xd0, 0x89, 0xa5, 0x55, + 0x52, 0x2d, 0x5c, 0x80, 0x80, + + /* U+74E3 "瓣" */ + 0x20, 0x28, 0x4f, 0x8b, 0xf5, 0x78, 0x2a, 0x95, + 0x55, 0x42, 0xa9, 0xbf, 0x57, 0x92, 0xa4, 0x25, + 0x49, 0xfa, 0xbc, 0x96, 0xa1, 0x35, 0x44, 0xaa, + 0x89, 0x6b, 0x20, 0x2, 0x0, + + /* U+74E4 "瓤" */ + 0x10, 0x0, 0xff, 0x7c, 0x0, 0x54, 0xee, 0x54, + 0xee, 0x54, 0x0, 0x54, 0xff, 0x94, 0x24, 0x94, + 0xff, 0x94, 0x24, 0x94, 0xff, 0x9c, 0x22, 0xaa, + 0x6c, 0xaa, 0xaa, 0xb6, 0x31, 0x0, + + /* U+74E6 "瓦" */ + 0xff, 0xfe, 0x20, 0x0, 0x40, 0x0, 0x80, 0x1, + 0xff, 0x2, 0x2, 0x4, 0x4, 0x13, 0x8, 0x21, + 0x10, 0x41, 0x20, 0x80, 0x49, 0x6, 0x97, 0xf1, + 0x20, 0x3, 0xc0, + + /* U+74EE "瓮" */ + 0x0, 0x80, 0x10, 0x80, 0x48, 0x83, 0x24, 0xe8, + 0x84, 0x43, 0xfc, 0x0, 0x8, 0x7f, 0xfe, 0x20, + 0x0, 0x7f, 0xc0, 0x80, 0x81, 0x71, 0x32, 0x12, + 0x69, 0xf4, 0x9c, 0xf, 0x0, + + /* U+74EF "瓯" */ + 0xfd, 0xfd, 0x1, 0x2, 0x92, 0x5, 0x47, 0xc9, + 0x90, 0x93, 0x21, 0x26, 0x52, 0x4c, 0x94, 0xa5, + 0x9, 0x4a, 0x16, 0x4, 0x2f, 0xe9, 0x50, 0x1c, + 0xc0, 0x21, 0x80, + + /* U+74F4 "瓴" */ + 0x8, 0x0, 0x1c, 0xff, 0x16, 0x40, 0x23, 0x40, + 0x48, 0x7c, 0x8, 0x44, 0x0, 0x84, 0x7e, 0xa4, + 0x2, 0x94, 0x4, 0x84, 0x44, 0x84, 0x28, 0x85, + 0x18, 0x85, 0x8, 0xf5, 0x4, 0x87, + + /* U+74F6 "瓶" */ + 0x44, 0x0, 0x53, 0xf8, 0x22, 0x3, 0xe4, 0x2, + 0x8f, 0x85, 0x21, 0xa, 0x42, 0x7f, 0xa4, 0x29, + 0x28, 0x52, 0x51, 0x24, 0x22, 0x48, 0x54, 0x90, + 0xa9, 0x2d, 0x62, 0x63, 0x80, + + /* U+74F7 "瓷" */ + 0x42, 0x0, 0x47, 0xf8, 0x13, 0x10, 0xc6, 0x42, + 0x12, 0x18, 0xc3, 0x2, 0x1, 0x7f, 0xfe, 0x20, + 0x0, 0x40, 0x0, 0xff, 0x81, 0x1, 0x2, 0x32, + 0x47, 0xf4, 0x90, 0x7, 0x0, + + /* U+74FF "瓿" */ + 0x10, 0x0, 0x23, 0xff, 0xf9, 0x0, 0x2, 0x4, + 0x44, 0x1, 0x17, 0x3f, 0xa2, 0x0, 0x44, 0x0, + 0xa8, 0xf9, 0x31, 0x12, 0x22, 0x24, 0x54, 0x48, + 0xaf, 0x97, 0x51, 0x33, 0x80, + + /* U+7504 "甄" */ + 0xff, 0xfc, 0x51, 0x3, 0xfa, 0x5, 0x57, 0xca, + 0xa8, 0x95, 0x51, 0x3f, 0xa2, 0x8, 0x54, 0x10, + 0x99, 0xfd, 0x10, 0x42, 0x28, 0x84, 0x53, 0xea, + 0xb8, 0x39, 0x80, + + /* U+750D "甍" */ + 0x8, 0x21, 0xff, 0xfc, 0x20, 0x83, 0xff, 0xe4, + 0x44, 0x4f, 0xff, 0x80, 0x0, 0x7f, 0xff, 0x80, + 0x2, 0x7f, 0xf0, 0xff, 0xc1, 0x60, 0x82, 0x31, + 0x27, 0xfa, 0x5c, 0x7, 0x80, + + /* U+750F "甏" */ + 0xff, 0x4, 0x10, 0x71, 0xfd, 0x0, 0x0, 0x37, + 0xf7, 0x8f, 0xe0, 0x9, 0x3, 0x7f, 0xb0, 0x0, + 0x0, 0xff, 0xf8, 0x7f, 0x81, 0x1, 0x2, 0x3a, + 0x24, 0x4, 0x4f, 0xe7, 0x80, + + /* U+7511 "甑" */ + 0x44, 0x0, 0x4b, 0xfb, 0xfa, 0x4, 0xb4, 0xd, + 0x6f, 0x92, 0x51, 0x3f, 0xa2, 0x0, 0x64, 0xfe, + 0xa9, 0x5, 0x12, 0xa, 0x27, 0xf4, 0x58, 0x28, + 0xbf, 0xdd, 0xe0, 0xc3, 0x0, + + /* U+7513 "甓" */ + 0x0, 0x10, 0x7c, 0x20, 0x8b, 0xf9, 0x12, 0x23, + 0xcf, 0xe7, 0xc2, 0x14, 0x84, 0x29, 0x7f, 0x5e, + 0x10, 0x0, 0x0, 0xff, 0xf0, 0xff, 0x81, 0x1, + 0x2, 0x72, 0x47, 0xf4, 0x90, 0x7, + + /* U+7518 "甘" */ + 0x10, 0x10, 0x20, 0x20, 0x40, 0x47, 0xff, 0xf1, + 0x1, 0x2, 0x2, 0x4, 0x4, 0x8, 0x8, 0x1f, + 0xf0, 0x20, 0x20, 0x40, 0x40, 0x80, 0x81, 0x1, + 0x3, 0xfe, 0x4, 0x4, 0x0, + + /* U+7519 "甙" */ + 0x0, 0x20, 0x0, 0x2c, 0x0, 0x20, 0xff, 0xfe, + 0x0, 0x20, 0x0, 0x20, 0x41, 0x20, 0xff, 0xa0, + 0x41, 0x20, 0x41, 0x20, 0x3f, 0x20, 0x41, 0x10, + 0x41, 0x11, 0x41, 0x12, 0x3f, 0xa, 0x41, 0x6, + + /* U+751A "甚" */ + 0x10, 0x0, 0x20, 0x23, 0xff, 0xf8, 0x80, 0x81, + 0x1, 0x3, 0xfe, 0x4, 0x4, 0xf, 0xf8, 0x10, + 0x11, 0xff, 0xfc, 0x80, 0x1, 0x19, 0x2, 0x61, + 0x5, 0x1, 0x8, 0x0, 0x1f, 0xff, + + /* U+751C "甜" */ + 0x0, 0x85, 0xf9, 0x8, 0x82, 0x11, 0xf, 0xff, + 0xc8, 0x44, 0x10, 0x88, 0x21, 0x10, 0x7e, 0xf8, + 0x85, 0x11, 0xa, 0x22, 0x14, 0x44, 0x28, 0x88, + 0x5f, 0x1f, 0xa2, 0x21, 0x0, + + /* U+751F "生" */ + 0x1, 0x0, 0x22, 0x0, 0x84, 0x1, 0xff, 0xe4, + 0x10, 0x8, 0x20, 0x20, 0x40, 0x0, 0x80, 0x3f, + 0xfc, 0x2, 0x0, 0x4, 0x0, 0x8, 0x0, 0x10, + 0x1f, 0xff, 0xc0, + + /* U+7525 "甥" */ + 0x10, 0x0, 0x23, 0xfd, 0x44, 0x4a, 0x88, 0x97, + 0xdf, 0xf2, 0x22, 0x64, 0x44, 0x88, 0xff, 0x7c, + 0x0, 0x20, 0x40, 0x47, 0xf8, 0x81, 0x11, 0x4, + 0x2f, 0x88, 0x60, 0x20, 0x81, 0x8e, + + /* U+7528 "用" */ + 0x3f, 0xfe, 0x41, 0x4, 0x82, 0x9, 0x4, 0x13, + 0xff, 0xe4, 0x10, 0x48, 0x20, 0x90, 0x41, 0x3f, + 0xfe, 0x41, 0x4, 0x82, 0xa, 0x4, 0x14, 0x8, + 0x30, 0x13, 0xc0, + + /* U+7529 "甩" */ + 0x3f, 0xfc, 0x21, 0x4, 0x21, 0x4, 0x3f, 0xfc, + 0x21, 0x4, 0x21, 0x4, 0x21, 0x4, 0x3f, 0xfc, + 0x21, 0x4, 0x21, 0x4, 0x21, 0x3d, 0x61, 0x2, + 0x41, 0x2, 0xc0, 0xfe, + + /* U+752B "甫" */ + 0x2, 0x23, 0xff, 0xf0, 0x20, 0x0, 0x80, 0x7f, + 0xf9, 0x8, 0x27, 0xff, 0x90, 0x82, 0x42, 0x9, + 0xff, 0xe4, 0x20, 0x90, 0x82, 0x42, 0x9, 0x9, + 0xe0, + + /* U+752C "甬" */ + 0xff, 0xf9, 0x1, 0x87, 0x71, 0xff, 0xf8, 0x20, + 0xc1, 0x7, 0xff, 0xf0, 0x41, 0x82, 0xf, 0xff, + 0xe0, 0x83, 0x4, 0x18, 0x20, 0xc1, 0x3c, + + /* U+752D "甭" */ + 0x7f, 0xfe, 0x3, 0x0, 0xc, 0x80, 0x68, 0xc7, + 0x0, 0x67, 0xff, 0x88, 0x41, 0x1f, 0xfe, 0x21, + 0x4, 0x42, 0x8, 0xff, 0xf1, 0x8, 0x24, 0x10, + 0x48, 0x27, 0x0, + + /* U+752F "甯" */ + 0x1, 0x0, 0x4, 0xf, 0xff, 0xe0, 0x1, 0x89, + 0x25, 0x20, 0xa4, 0xfc, 0x40, 0x0, 0x3f, 0xf8, + 0x84, 0x23, 0xff, 0x88, 0x42, 0x3f, 0xf8, 0x84, + 0x22, 0x10, 0x88, 0x5c, + + /* U+7530 "田" */ + 0xff, 0xfc, 0x10, 0x60, 0x83, 0x4, 0x18, 0x20, + 0xc1, 0x7, 0xff, 0xf0, 0x41, 0x82, 0xc, 0x10, + 0x60, 0x83, 0xff, 0xf8, 0x0, 0xc0, 0x4, + + /* U+7531 "由" */ + 0x2, 0x0, 0x10, 0x0, 0x81, 0xff, 0xf8, 0x20, + 0xc1, 0x6, 0x8, 0x30, 0x41, 0xff, 0xfc, 0x10, + 0x60, 0x83, 0x4, 0x18, 0x20, 0xff, 0xfe, 0x0, + 0x20, + + /* U+7532 "甲" */ + 0xff, 0xfc, 0x10, 0x60, 0x83, 0x4, 0x1f, 0xff, + 0xc1, 0x6, 0x8, 0x30, 0x41, 0xff, 0xfc, 0x10, + 0x40, 0x80, 0x4, 0x0, 0x20, 0x1, 0x0, + + /* U+7533 "申" */ + 0x2, 0x0, 0x10, 0x3f, 0xff, 0x4, 0x18, 0x20, + 0xc1, 0x7, 0xff, 0xf0, 0x41, 0x82, 0xc, 0x10, + 0x7f, 0xff, 0x4, 0x10, 0x20, 0x1, 0x0, 0x8, + 0x0, + + /* U+7535 "电" */ + 0x2, 0x0, 0x4, 0x3, 0xff, 0xe4, 0x10, 0x48, + 0x20, 0x90, 0x41, 0x3f, 0xfe, 0x41, 0x4, 0x82, + 0x9, 0xff, 0xf2, 0x8, 0x4, 0x10, 0x10, 0x20, + 0x20, 0x40, 0x80, 0x7f, 0x0, + + /* U+7537 "男" */ + 0x7f, 0xf9, 0x4, 0x27, 0xff, 0x90, 0x42, 0x41, + 0x9, 0xff, 0xe0, 0x0, 0x0, 0x80, 0xff, 0xfc, + 0x8, 0x20, 0x40, 0x83, 0x2, 0x18, 0xb, 0x83, + 0xc0, + + /* U+7538 "甸" */ + 0x8, 0x0, 0x40, 0x1, 0xff, 0xc8, 0x1, 0x7f, + 0xc6, 0x89, 0x12, 0x24, 0x48, 0x91, 0x3f, 0xc4, + 0x89, 0x12, 0x24, 0x4f, 0xf1, 0x20, 0x4, 0x0, + 0x30, 0xf, 0x80, + + /* U+753A "町" */ + 0xfd, 0xfe, 0x50, 0x89, 0x42, 0x25, 0x8, 0x94, + 0x23, 0xf0, 0x89, 0x42, 0x25, 0x8, 0x94, 0x22, + 0x50, 0x89, 0x42, 0x3f, 0x8, 0x80, 0x20, 0x7, + 0x80, + + /* U+753B "画" */ + 0xff, 0xfe, 0x0, 0x0, 0x3f, 0xc2, 0x48, 0x94, + 0x91, 0x29, 0x22, 0x53, 0xfc, 0xa4, 0x89, 0x49, + 0x12, 0x9f, 0xe5, 0x0, 0xa, 0x0, 0x17, 0xff, + 0xe0, 0x0, 0x40, + + /* U+753E "甾" */ + 0x21, 0x12, 0x11, 0x21, 0x10, 0xc4, 0x42, 0x11, + 0x80, 0x1, 0xff, 0xc8, 0x42, 0x42, 0x12, 0x10, + 0x9f, 0xfc, 0x84, 0x24, 0x21, 0x3f, 0xf9, 0x0, + 0x40, + + /* U+7540 "畀" */ + 0x3f, 0xfc, 0x82, 0x9, 0x4, 0x12, 0x8, 0x23, + 0xff, 0xc8, 0x20, 0x90, 0x41, 0x1f, 0xfe, 0x0, + 0x1, 0xff, 0xfc, 0x20, 0x40, 0x40, 0x81, 0x1, + 0x6, 0x2, 0x18, 0x4, 0x0, + + /* U+7545 "畅" */ + 0x20, 0x0, 0x87, 0xef, 0xc1, 0x29, 0x18, 0xa4, + 0x82, 0x94, 0xf, 0xff, 0xe9, 0x29, 0xa4, 0xa7, + 0xf4, 0x9a, 0x12, 0x48, 0x91, 0x20, 0x44, 0x82, + 0x12, 0x13, 0x80, + + /* U+7548 "畈" */ + 0x0, 0x5, 0xfb, 0xf2, 0x94, 0x5, 0x28, 0xa, + 0x50, 0x14, 0xbf, 0xbf, 0x51, 0x52, 0xa4, 0xa5, + 0x49, 0x4a, 0x52, 0x94, 0x47, 0xe9, 0x88, 0x24, + 0x90, 0x50, 0x80, 0x40, 0x0, + + /* U+754B "畋" */ + 0x0, 0x41, 0xf8, 0x82, 0x92, 0x5, 0x27, 0xfa, + 0x58, 0x94, 0xb1, 0x3f, 0x22, 0x52, 0x44, 0xa4, + 0x51, 0x48, 0xa2, 0x91, 0x85, 0x23, 0x8f, 0xcd, + 0x90, 0x31, 0x80, 0x80, 0x80, + + /* U+754C "界" */ + 0x3f, 0xfc, 0x20, 0x84, 0x20, 0x84, 0x3f, 0xfc, + 0x20, 0x84, 0x20, 0x84, 0x3f, 0xfc, 0x2, 0x40, + 0x4, 0x20, 0x18, 0x18, 0x64, 0x26, 0x4, 0x20, + 0x4, 0x20, 0x18, 0x20, 0x30, 0x20, + + /* U+754E "畎" */ + 0x0, 0x41, 0xf8, 0xa2, 0x91, 0x25, 0x22, 0xa, + 0x5f, 0xd4, 0x88, 0x3f, 0x10, 0x52, 0x60, 0xa4, + 0xa1, 0x49, 0x42, 0x92, 0x45, 0x28, 0x8f, 0xf0, + 0x90, 0x40, 0x80, 0x0, 0x0, + + /* U+754F "畏" */ + 0x3f, 0xf8, 0x82, 0x10, 0xff, 0xe2, 0x8, 0x44, + 0x10, 0x87, 0xff, 0x0, 0x0, 0x7f, 0xff, 0x22, + 0x0, 0x42, 0x30, 0x87, 0x81, 0x2, 0x2, 0xe3, + 0x86, 0x0, 0xc0, + + /* U+7554 "畔" */ + 0x0, 0x43, 0xe9, 0x1a, 0x94, 0xaa, 0x52, 0xa8, + 0x42, 0xaf, 0xff, 0x84, 0x2a, 0x10, 0xa8, 0x42, + 0xaf, 0xfa, 0x84, 0x2a, 0x10, 0xf8, 0x42, 0x1, + 0x0, 0x4, 0x0, + + /* U+7559 "留" */ + 0x0, 0x7, 0xcf, 0xe0, 0x23, 0x11, 0x19, 0xd0, + 0xf1, 0xb8, 0x8, 0xf, 0xfe, 0x42, 0x12, 0x10, + 0x9f, 0xfc, 0x84, 0x24, 0x21, 0x3f, 0xf9, 0x0, + 0x40, + + /* U+755A "畚" */ + 0x4, 0x0, 0x21, 0x1, 0x2, 0xf, 0xfc, 0x2, + 0x3, 0xff, 0xf0, 0x8c, 0x4, 0x8, 0x7f, 0xf3, + 0x88, 0x72, 0x21, 0xf, 0xfc, 0x22, 0x10, 0xff, + 0xc2, 0x1, 0x0, + + /* U+755B "畛" */ + 0x0, 0x0, 0x1, 0xf, 0x8a, 0x2a, 0x44, 0xaa, + 0x1a, 0xb1, 0x1a, 0x8c, 0x3e, 0x40, 0xaa, 0x32, + 0xa1, 0x8a, 0x8c, 0x2a, 0xc2, 0xa8, 0x33, 0xe1, + 0x8, 0x18, 0x0, 0x80, + + /* U+755C "畜" */ + 0x0, 0x0, 0x2, 0x3, 0xff, 0xf8, 0x20, 0x1, + 0x83, 0xf, 0xf8, 0x0, 0x88, 0x1e, 0xc, 0x4f, + 0xe4, 0x7f, 0xf0, 0x84, 0x21, 0x8, 0x43, 0xff, + 0x84, 0x21, 0xf, 0xfe, 0x10, 0x4, + + /* U+7565 "略" */ + 0x0, 0x80, 0x2, 0xf, 0x9f, 0xaa, 0x42, 0xaa, + 0x92, 0xa1, 0x4a, 0x86, 0x3e, 0x24, 0xab, 0xe, + 0xa0, 0xa, 0x9f, 0xaa, 0x42, 0xa9, 0xb, 0xe4, + 0x28, 0x1f, 0x80, 0x42, + + /* U+7566 "畦" */ + 0x0, 0x43, 0xe1, 0xa, 0xbf, 0xaa, 0x10, 0xa8, + 0x42, 0xaf, 0xff, 0x80, 0x2a, 0x10, 0xa8, 0x42, + 0xaf, 0xea, 0x84, 0x2a, 0x10, 0xd0, 0x42, 0x1f, + 0xf0, + + /* U+756A "番" */ + 0x7f, 0xf8, 0x8, 0x1, 0x26, 0x3f, 0xff, 0x6, + 0x80, 0x69, 0x6, 0x23, 0x2f, 0xfe, 0x42, 0x11, + 0x8, 0x43, 0xff, 0x10, 0x84, 0x3f, 0xf1, 0x0, + 0x40, + + /* U+7572 "畲" */ + 0x1, 0x80, 0x4, 0x80, 0x30, 0xc1, 0xbf, 0x64, + 0x0, 0x27, 0xff, 0x2, 0x28, 0x39, 0x8f, 0x0, + 0x0, 0x7f, 0xf0, 0x84, 0x21, 0xff, 0xc2, 0x10, + 0x87, 0xff, 0x8, 0x2, 0x0, + + /* U+7574 "畴" */ + 0x0, 0x40, 0x0, 0x83, 0xef, 0xf5, 0x42, 0xa, + 0x9f, 0xd5, 0x10, 0x2a, 0x20, 0x7d, 0xff, 0xa8, + 0x89, 0x51, 0xfe, 0xa6, 0x27, 0xd2, 0x48, 0x64, + 0x81, 0x81, 0x0, 0x1c, 0x0, + + /* U+7578 "畸" */ + 0x0, 0x20, 0x0, 0x43, 0xef, 0xf5, 0x42, 0xa, + 0x87, 0x15, 0x31, 0xaa, 0x80, 0x7d, 0xff, 0xa8, + 0x9, 0x57, 0x92, 0xa9, 0x25, 0x52, 0x4f, 0xbc, + 0x91, 0x1, 0x0, 0x2, 0x0, 0x3c, + + /* U+7579 "畹" */ + 0x0, 0x41, 0xf0, 0x42, 0xaf, 0xf5, 0x50, 0x2a, + 0xb0, 0x15, 0x5f, 0xbe, 0x99, 0x55, 0x32, 0xab, + 0x65, 0x51, 0xda, 0xa2, 0x87, 0xc5, 0x18, 0x12, + 0x40, 0x64, 0x81, 0x87, 0x0, + + /* U+757F "畿" */ + 0x1, 0x0, 0x22, 0x20, 0x84, 0x92, 0x6a, 0x27, + 0x97, 0x82, 0x22, 0x9, 0x49, 0x3e, 0xbd, 0xff, + 0xfe, 0x1, 0x1, 0xfa, 0x22, 0x92, 0xc7, 0xe7, + 0x2a, 0x4c, 0x5f, 0xe4, 0xa0, 0x6, + + /* U+7583 "疃" */ + 0x0, 0x40, 0x0, 0x83, 0xef, 0xf5, 0x48, 0x4a, + 0x89, 0x15, 0x7f, 0xea, 0x0, 0x55, 0xfe, 0xfa, + 0x45, 0x57, 0xfa, 0xa9, 0x15, 0x5f, 0xea, 0x84, + 0x1a, 0x7f, 0x81, 0xff, 0x80, + + /* U+7586 "疆" */ + 0xf3, 0xfe, 0x20, 0x0, 0x4f, 0xf0, 0x92, 0x2e, + 0x3f, 0xd0, 0x48, 0xbc, 0x6e, 0xb, 0xff, 0x50, + 0x0, 0xa7, 0xf9, 0x4f, 0xf3, 0x92, 0x29, 0x3f, + 0xc2, 0x0, 0x1d, 0xff, 0x80, + + /* U+758B "疋" */ + 0x7f, 0xfe, 0x0, 0x82, 0x0, 0x82, 0x0, 0x84, + 0x10, 0x80, 0x10, 0x80, 0x10, 0xfe, 0x10, 0x80, + 0x10, 0x80, 0x30, 0x80, 0x28, 0x80, 0x64, 0x80, + 0x43, 0xff, 0x0, 0x0, + + /* U+758F "疏" */ + 0x0, 0x41, 0xf0, 0x40, 0x3f, 0xf8, 0x86, 0x1, + 0x11, 0x14, 0x41, 0x29, 0xff, 0x4c, 0x0, 0xa0, + 0x1, 0x44, 0x92, 0x89, 0x25, 0x12, 0x49, 0xa4, + 0xbc, 0x89, 0x43, 0x13, 0x80, + + /* U+7591 "疑" */ + 0x40, 0x0, 0x46, 0xfe, 0x78, 0x6, 0x42, 0x68, + 0x3c, 0x10, 0x21, 0xfe, 0x7e, 0x12, 0x48, 0x12, + 0x8, 0x90, 0xfe, 0x9e, 0x10, 0x90, 0x1c, 0x90, + 0x21, 0x70, 0x41, 0x3e, 0x0, 0x0, + + /* U+7592 "疒" */ + 0x0, 0x80, 0x0, 0x80, 0x7f, 0xf8, 0x80, 0x9, + 0x0, 0xa, 0x0, 0x1c, 0x0, 0x8, 0x0, 0x30, + 0x1, 0xa0, 0x0, 0x40, 0x0, 0x80, 0x2, 0x0, + 0x4, 0x0, 0x10, 0x0, 0x0, + + /* U+7594 "疔" */ + 0x0, 0x0, 0x0, 0x80, 0x7f, 0xf8, 0x80, 0x5, + 0x0, 0xa, 0xff, 0xc4, 0x8, 0x8, 0x10, 0x70, + 0x21, 0x20, 0x40, 0x40, 0x81, 0x81, 0x2, 0x2, + 0xc, 0x4, 0x10, 0x70, 0x0, + + /* U+7596 "疖" */ + 0x0, 0x40, 0x0, 0x40, 0x1f, 0xff, 0x10, 0x0, + 0x50, 0x0, 0x53, 0xfe, 0x10, 0x82, 0x10, 0x82, + 0x70, 0x82, 0x10, 0x82, 0x10, 0x82, 0x20, 0x9c, + 0x20, 0x80, 0x60, 0x80, 0x40, 0x80, + + /* U+7597 "疗" */ + 0x0, 0x40, 0x0, 0x40, 0x1f, 0xff, 0x10, 0x0, + 0x90, 0x0, 0x57, 0xfe, 0x50, 0x4, 0x10, 0x18, + 0x30, 0x20, 0xd0, 0x20, 0x10, 0x20, 0x10, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x41, 0xc0, + + /* U+7599 "疙" */ + 0x1, 0x0, 0x2, 0x0, 0xff, 0xf9, 0x0, 0x2, + 0x20, 0x14, 0x80, 0x29, 0xff, 0x34, 0x0, 0x67, + 0xf1, 0xc0, 0x40, 0x83, 0x1, 0x8, 0x4, 0x20, + 0x28, 0x80, 0x92, 0x1, 0x43, 0xfc, + + /* U+759A "疚" */ + 0x0, 0x40, 0x0, 0x40, 0x1f, 0xfe, 0x10, 0x0, + 0x50, 0x80, 0x51, 0xf8, 0x11, 0x8, 0x12, 0x8, + 0x74, 0x10, 0x94, 0x30, 0x10, 0x30, 0x30, 0x50, + 0x21, 0x88, 0x46, 0x6, 0x48, 0x0, + + /* U+759D "疝" */ + 0x0, 0x40, 0x0, 0xc0, 0x7f, 0xf8, 0x80, 0x9, + 0x2, 0xa, 0x4, 0x14, 0x8, 0x9, 0x11, 0x32, + 0x23, 0xa4, 0x44, 0x48, 0x89, 0x11, 0x12, 0x22, + 0x24, 0x7f, 0xd0, 0x0, 0x80, + + /* U+759F "疟" */ + 0x0, 0x40, 0x0, 0x80, 0x7f, 0xf8, 0x80, 0x5, + 0x3f, 0xea, 0x40, 0x4, 0x80, 0x9, 0x0, 0x72, + 0x1, 0xaf, 0xfc, 0x48, 0x1, 0x90, 0x2, 0x20, + 0x8, 0x7f, 0xd0, 0x0, 0x0, + + /* U+75A0 "疠" */ + 0x0, 0x40, 0x0, 0x40, 0x1f, 0xff, 0x10, 0x0, + 0x50, 0x0, 0x57, 0xff, 0x50, 0x80, 0x10, 0x80, + 0x30, 0xfc, 0xd0, 0x84, 0x11, 0x4, 0x31, 0x4, + 0x23, 0x4, 0x66, 0x4, 0x4c, 0x38, + + /* U+75A1 "疡" */ + 0x1, 0x0, 0x1, 0x0, 0xff, 0xf9, 0x0, 0xa, + 0xff, 0x14, 0x4, 0x18, 0x70, 0x13, 0x0, 0xef, + 0xfc, 0x44, 0x48, 0x89, 0x11, 0x22, 0x24, 0xc4, + 0x4a, 0x11, 0xa0, 0xce, 0x0, + + /* U+75A3 "疣" */ + 0x0, 0x80, 0x0, 0x40, 0x1f, 0xfe, 0x10, 0x98, + 0x50, 0x84, 0x50, 0x80, 0x17, 0xfe, 0x10, 0xa0, + 0x70, 0xa0, 0x11, 0x20, 0x11, 0x20, 0x33, 0x21, + 0x22, 0x22, 0x24, 0x22, 0x48, 0x3e, 0x40, 0x0, + + /* U+75A4 "疤" */ + 0x0, 0x80, 0x1, 0x0, 0x7f, 0xf8, 0x80, 0x5, + 0x3f, 0xca, 0x48, 0x84, 0x91, 0x9, 0x22, 0x32, + 0x45, 0xa7, 0xf8, 0x48, 0x1, 0x10, 0x2, 0x20, + 0x24, 0x40, 0x50, 0xff, 0x80, 0x0, + + /* U+75A5 "疥" */ + 0x0, 0x40, 0x0, 0x20, 0x1f, 0xff, 0x10, 0x0, + 0x10, 0x60, 0x50, 0x90, 0x31, 0x8, 0x16, 0x6, + 0x71, 0x8, 0x91, 0x8, 0x11, 0x8, 0x31, 0x8, + 0x23, 0x8, 0x62, 0x8, 0x44, 0x8, + + /* U+75AB "疫" */ + 0x0, 0x40, 0x0, 0x40, 0x1f, 0xff, 0x10, 0x0, + 0x10, 0x0, 0x51, 0xf8, 0x51, 0x8, 0x31, 0xc, + 0x12, 0x2, 0x73, 0xfc, 0x11, 0x4, 0x10, 0x88, + 0x20, 0x50, 0x20, 0x70, 0x47, 0x8e, 0x48, 0x0, + + /* U+75AC "疬" */ + 0x0, 0x40, 0x0, 0x40, 0x1f, 0xff, 0x10, 0x0, + 0x10, 0x0, 0x53, 0xff, 0x52, 0x0, 0x72, 0x20, + 0x12, 0x20, 0x32, 0xfe, 0x52, 0x22, 0x12, 0x22, + 0x32, 0x62, 0x24, 0x42, 0x24, 0xc2, 0x49, 0x9c, + + /* U+75AE "疮" */ + 0x0, 0x0, 0x0, 0x40, 0x1f, 0xfe, 0x10, 0x20, + 0x10, 0x60, 0x50, 0x90, 0x53, 0xc, 0x1c, 0x3, + 0x13, 0xf8, 0x72, 0x8, 0x12, 0x8, 0x12, 0x8, + 0x22, 0x7a, 0x22, 0x2, 0x22, 0x2, 0x41, 0xfc, + + /* U+75AF "疯" */ + 0x0, 0x80, 0x0, 0x40, 0x1f, 0xff, 0x10, 0x0, + 0x10, 0x0, 0x57, 0xfc, 0x54, 0x4, 0x34, 0x14, + 0x15, 0x24, 0x34, 0xa4, 0x54, 0x44, 0x14, 0xa4, + 0x25, 0x95, 0x2b, 0x15, 0x48, 0x5, 0x40, 0x2, + + /* U+75B0 "疰" */ + 0x0, 0x80, 0x0, 0x80, 0xff, 0xf9, 0x0, 0xa, + 0x4, 0x15, 0xff, 0x88, 0x10, 0x10, 0x20, 0xe0, + 0x40, 0x4f, 0xf8, 0x81, 0x2, 0x2, 0x4, 0x4, + 0x1b, 0xff, 0xe0, 0x0, 0x0, + + /* U+75B1 "疱" */ + 0x0, 0x0, 0x0, 0x40, 0x1f, 0xff, 0x11, 0x0, + 0x51, 0x0, 0x53, 0xfc, 0x52, 0x4, 0x34, 0x4, + 0x17, 0xf4, 0x3a, 0x14, 0x52, 0x14, 0x13, 0xe4, + 0x12, 0x3d, 0x22, 0x1, 0x22, 0x1, 0x43, 0xfe, + 0x40, 0x0, + + /* U+75B2 "疲" */ + 0x0, 0x40, 0x0, 0x40, 0x1f, 0xff, 0x10, 0x0, + 0x10, 0x20, 0x53, 0xfe, 0x52, 0x22, 0x32, 0x20, + 0x12, 0x20, 0x33, 0xfe, 0x52, 0x82, 0x12, 0x84, + 0x26, 0x48, 0x24, 0x30, 0x2c, 0x58, 0x4b, 0x87, + + /* U+75B3 "疳" */ + 0x0, 0x80, 0x1, 0x0, 0xff, 0xf9, 0x0, 0xa, + 0x40, 0x95, 0xff, 0xd9, 0x2, 0x12, 0x4, 0xe4, + 0x9, 0x4f, 0xf0, 0x90, 0x23, 0x20, 0x44, 0x40, + 0x98, 0xff, 0x21, 0x2, 0x0, + + /* U+75B4 "疴" */ + 0x0, 0x0, 0x0, 0x40, 0x1f, 0xff, 0x10, 0x0, + 0x57, 0xff, 0x50, 0x4, 0x10, 0x4, 0x13, 0xe4, + 0x72, 0x24, 0x12, 0x24, 0x12, 0x24, 0x13, 0xe4, + 0x20, 0x4, 0x20, 0x4, 0x40, 0x3c, + + /* U+75B5 "疵" */ + 0x0, 0x80, 0x0, 0x80, 0xff, 0xf9, 0x0, 0xa, + 0x12, 0x15, 0x24, 0x1a, 0x49, 0x14, 0xfc, 0x69, + 0x21, 0x52, 0x40, 0xa4, 0x81, 0x49, 0x14, 0x92, + 0x29, 0xfc, 0x64, 0xf, 0x0, 0x0, + + /* U+75B8 "疸" */ + 0x0, 0x80, 0x1, 0x0, 0xff, 0xf9, 0x0, 0x2, + 0x0, 0x14, 0xff, 0x29, 0x2, 0x12, 0x4, 0x67, + 0xf9, 0xc8, 0x10, 0x90, 0x21, 0x20, 0x44, 0x7f, + 0x88, 0x0, 0x27, 0xff, 0xc0, 0x0, + + /* U+75B9 "疹" */ + 0x0, 0x80, 0x1, 0x0, 0xff, 0xf9, 0x2, 0xa, + 0xc, 0x14, 0x24, 0x29, 0x86, 0x14, 0x23, 0x61, + 0x81, 0x4c, 0x60, 0x81, 0x1, 0x1c, 0x64, 0x43, + 0x8, 0x18, 0x23, 0xc0, 0x0, + + /* U+75BC "疼" */ + 0x0, 0x80, 0x1, 0x0, 0xff, 0xf9, 0x0, 0xa, + 0x10, 0x14, 0x7f, 0x29, 0x82, 0x34, 0x88, 0x20, + 0xe0, 0xc3, 0xc2, 0xb8, 0x79, 0x8, 0x2, 0xf, + 0x8, 0x0, 0x11, 0xe0, 0x40, 0x3c, + + /* U+75BD "疽" */ + 0x0, 0x80, 0x1, 0x0, 0xff, 0xf9, 0x0, 0x2, + 0x0, 0x14, 0xff, 0x29, 0x2, 0x12, 0x4, 0x27, + 0xf9, 0xc8, 0x10, 0x90, 0x21, 0x3f, 0xc4, 0x40, + 0x88, 0x81, 0x27, 0xff, 0xc0, 0x0, + + /* U+75BE "疾" */ + 0x1, 0x0, 0x2, 0x0, 0xff, 0xf9, 0x0, 0x2, + 0x40, 0x14, 0x80, 0x29, 0xff, 0x54, 0x40, 0x28, + 0x80, 0xc1, 0x2, 0xbf, 0xf1, 0x4, 0x4, 0x14, + 0x8, 0x64, 0x21, 0x86, 0x4c, 0x3, + + /* U+75C2 "痂" */ + 0x0, 0x80, 0x0, 0x80, 0xff, 0xf9, 0x20, 0xa, + 0x43, 0xf5, 0xf4, 0x59, 0x28, 0x92, 0x51, 0xe4, + 0xa2, 0x49, 0x44, 0x92, 0x8b, 0x65, 0x14, 0x8b, + 0xe9, 0x14, 0x64, 0xe8, 0x80, + + /* U+75C3 "痃" */ + 0x0, 0x0, 0x0, 0x80, 0x7f, 0xf8, 0x80, 0x9, + 0x4, 0xa, 0x4, 0x15, 0xff, 0xa8, 0x20, 0x11, + 0x80, 0x64, 0x13, 0x5f, 0xc0, 0x82, 0x2, 0x8, + 0x84, 0x60, 0x89, 0xff, 0x22, 0x1, + + /* U+75C4 "痄" */ + 0x0, 0x40, 0x0, 0x40, 0x1f, 0xff, 0x10, 0x0, + 0x12, 0x0, 0x53, 0xff, 0x52, 0x80, 0x14, 0x80, + 0x70, 0xfe, 0x90, 0x80, 0x10, 0x80, 0x30, 0xfe, + 0x20, 0x80, 0x60, 0x80, 0x40, 0x80, + + /* U+75C5 "病" */ + 0x0, 0x40, 0x0, 0x40, 0x1f, 0xff, 0x10, 0x0, + 0x57, 0xfe, 0x50, 0x20, 0x10, 0x20, 0x17, 0xfe, + 0x74, 0x42, 0xd4, 0x62, 0x14, 0x52, 0x14, 0x8a, + 0x25, 0x2, 0x24, 0x2, 0x44, 0x1c, + + /* U+75C7 "症" */ + 0x0, 0x40, 0x0, 0x40, 0x1f, 0xff, 0x10, 0x0, + 0x50, 0x0, 0x57, 0xfe, 0x10, 0x20, 0x10, 0x20, + 0x32, 0x20, 0xd2, 0x3e, 0x12, 0x20, 0x22, 0x20, + 0x22, 0x20, 0x6f, 0xff, 0x40, 0x0, + + /* U+75C8 "痈" */ + 0x0, 0x40, 0x0, 0x40, 0x1f, 0xff, 0x10, 0x0, + 0x10, 0x0, 0x53, 0xfe, 0x52, 0x22, 0x32, 0x22, + 0x13, 0xfe, 0x72, 0x22, 0x12, 0x22, 0x13, 0xfe, + 0x22, 0x22, 0x24, 0x22, 0x24, 0x22, 0x48, 0x2e, + + /* U+75C9 "痉" */ + 0x0, 0x40, 0x0, 0x40, 0x1f, 0xff, 0x10, 0x0, + 0x53, 0xfc, 0x50, 0xc, 0x30, 0x10, 0x10, 0x78, + 0x77, 0x87, 0x90, 0x0, 0x13, 0xfc, 0x20, 0x20, + 0x20, 0x20, 0x4f, 0xff, 0x40, 0x0, + + /* U+75CA "痊" */ + 0x0, 0x80, 0x0, 0x80, 0x1f, 0xff, 0x10, 0x40, + 0x50, 0xa0, 0x51, 0x10, 0x36, 0xc, 0x1b, 0xfa, + 0x70, 0x40, 0x10, 0x40, 0x17, 0xfc, 0x30, 0x40, + 0x20, 0x40, 0x6f, 0xfe, 0x40, 0x0, + + /* U+75CD "痍" */ + 0x1, 0x0, 0x1, 0x0, 0xfd, 0xf1, 0x4, 0xa, + 0xff, 0xf4, 0x10, 0x2b, 0xff, 0x10, 0x42, 0x2f, + 0xfd, 0xd1, 0x2, 0xa2, 0x1, 0x7f, 0xf4, 0x18, + 0x28, 0x29, 0x90, 0x8c, 0x4e, 0x7, + + /* U+75D2 "痒" */ + 0x0, 0x80, 0x0, 0x80, 0x7f, 0xf8, 0x90, 0x45, + 0x11, 0xa, 0xff, 0xc4, 0x10, 0x8, 0x20, 0x77, + 0xfd, 0x20, 0x80, 0x41, 0x1, 0x7f, 0xf2, 0x4, + 0xc, 0x8, 0x10, 0x10, 0x0, + + /* U+75D4 "痔" */ + 0x0, 0x40, 0x0, 0x80, 0x7f, 0xf8, 0x82, 0x5, + 0x7f, 0xca, 0x8, 0xc, 0x10, 0xf, 0xff, 0x30, + 0x1, 0xa0, 0x20, 0x7f, 0xf9, 0x0, 0x82, 0x31, + 0xc, 0x2, 0x10, 0x1c, 0x0, + + /* U+75D5 "痕" */ + 0x0, 0x0, 0x0, 0x40, 0x1f, 0xff, 0x10, 0x0, + 0x10, 0x0, 0x57, 0xfc, 0x54, 0x4, 0x37, 0xfc, + 0x14, 0x4, 0x77, 0xfc, 0x14, 0x82, 0x14, 0x4c, + 0x24, 0x30, 0x24, 0x98, 0x47, 0x7, + + /* U+75D6 "痖" */ + 0x0, 0x40, 0x0, 0x40, 0x1f, 0xff, 0x10, 0x0, + 0x57, 0xfe, 0x50, 0x90, 0x10, 0x90, 0x14, 0x92, + 0x74, 0x92, 0x12, 0x94, 0x12, 0x94, 0x32, 0x94, + 0x20, 0x90, 0x2f, 0xff, 0x40, 0x0, + + /* U+75D8 "痘" */ + 0x0, 0x40, 0x0, 0x40, 0x1f, 0xff, 0x10, 0x0, + 0x90, 0x0, 0x57, 0xfe, 0x50, 0x0, 0x13, 0xfc, + 0x12, 0x4, 0x72, 0x4, 0x13, 0xfc, 0x11, 0x8, + 0x21, 0x8, 0x20, 0x90, 0x4f, 0xff, 0x40, 0x0, + + /* U+75DB "痛" */ + 0x0, 0x40, 0x0, 0x40, 0x1f, 0xff, 0x10, 0x0, + 0x10, 0x0, 0x57, 0xfe, 0x50, 0xc, 0x31, 0xf0, + 0x17, 0xfe, 0x34, 0x22, 0x57, 0xfe, 0x14, 0x22, + 0x27, 0xfe, 0x24, 0x22, 0x24, 0x22, 0x44, 0x2e, + + /* U+75DE "痞" */ + 0x0, 0x40, 0x0, 0x40, 0x1f, 0xff, 0x10, 0x0, + 0x10, 0x0, 0x57, 0xff, 0x50, 0x20, 0x30, 0x60, + 0x11, 0xac, 0x36, 0x23, 0x50, 0x20, 0x13, 0xfe, + 0x12, 0x2, 0x22, 0x2, 0x23, 0xfe, 0x42, 0x2, + + /* U+75E2 "痢" */ + 0x0, 0x0, 0x1, 0x0, 0xff, 0xf9, 0x0, 0xa, + 0x78, 0x54, 0x44, 0x98, 0x89, 0x17, 0xd2, 0x62, + 0x25, 0x4e, 0x48, 0xaa, 0x92, 0x51, 0x25, 0x20, + 0x48, 0x40, 0xa0, 0x87, 0x0, + + /* U+75E3 "痣" */ + 0x0, 0x0, 0x0, 0x40, 0x1f, 0xff, 0x10, 0x40, + 0x50, 0x40, 0x57, 0xff, 0x30, 0x40, 0x10, 0x40, + 0x37, 0xfe, 0x50, 0x0, 0x10, 0x40, 0x15, 0x22, + 0x29, 0xa, 0x29, 0x9, 0x41, 0xf0, + + /* U+75E4 "痤" */ + 0x0, 0x40, 0x0, 0x40, 0x1f, 0xff, 0x50, 0x20, + 0x52, 0x24, 0x52, 0x24, 0x13, 0x2a, 0x14, 0xaa, + 0x70, 0x20, 0x10, 0x20, 0x17, 0xfe, 0x20, 0x20, + 0x20, 0x20, 0x4f, 0xff, 0x40, 0x0, + + /* U+75E6 "痦" */ + 0x0, 0x40, 0x0, 0x40, 0x1f, 0xff, 0x10, 0x0, + 0x57, 0xfe, 0x50, 0x80, 0x50, 0x80, 0x33, 0xfc, + 0x10, 0x84, 0x37, 0xff, 0x50, 0x0, 0x13, 0xfe, + 0x22, 0x2, 0x22, 0x2, 0x63, 0xfe, 0x42, 0x2, + + /* U+75E7 "痧" */ + 0x0, 0x40, 0x0, 0x40, 0x1f, 0xbe, 0x10, 0x10, + 0x52, 0x10, 0x50, 0x54, 0x30, 0x92, 0x16, 0x91, + 0x30, 0x90, 0x50, 0x14, 0x12, 0x4, 0x24, 0x8, + 0x24, 0x30, 0x68, 0xc0, 0x41, 0x0, + + /* U+75E8 "痨" */ + 0x0, 0x80, 0x1, 0x0, 0xff, 0xf9, 0x0, 0xa, + 0x12, 0x15, 0xff, 0xe8, 0x48, 0x17, 0xfe, 0x28, + 0x85, 0xc1, 0x0, 0xbf, 0xe1, 0x8, 0x42, 0x10, + 0x88, 0x21, 0x10, 0x82, 0x46, 0x38, + + /* U+75EA "痪" */ + 0x0, 0x80, 0x0, 0x80, 0xff, 0xf9, 0x8, 0xa, + 0x3f, 0x14, 0x82, 0x1b, 0x8, 0x13, 0xfe, 0x64, + 0x45, 0x48, 0x88, 0xbf, 0xf9, 0x5, 0x4, 0x13, + 0x18, 0xc1, 0xa2, 0x0, 0x80, + + /* U+75EB "痫" */ + 0x0, 0x80, 0x1, 0x80, 0xff, 0xf9, 0x0, 0xa, + 0x4f, 0xd4, 0x40, 0x9a, 0x11, 0x15, 0xfa, 0x68, + 0x45, 0x51, 0x88, 0xa5, 0xd3, 0x52, 0x24, 0x84, + 0x49, 0x0, 0xa2, 0xe, 0x0, + + /* U+75F0 "痰" */ + 0x0, 0x40, 0x0, 0x40, 0x1f, 0xfe, 0x10, 0x0, + 0x50, 0x22, 0x53, 0x24, 0x50, 0x60, 0x10, 0xd8, + 0x37, 0x6, 0x50, 0x20, 0x16, 0x22, 0x11, 0x24, + 0x20, 0x50, 0x21, 0x88, 0x4e, 0x7, + + /* U+75F1 "痱" */ + 0x0, 0x40, 0x0, 0x40, 0x1f, 0xff, 0x10, 0x0, + 0x50, 0x90, 0x57, 0x9f, 0x30, 0x90, 0x10, 0x90, + 0x77, 0x9e, 0x10, 0x90, 0x10, 0x90, 0x2f, 0x9f, + 0x20, 0x90, 0x60, 0x90, 0x40, 0x90, + + /* U+75F4 "痴" */ + 0x0, 0x40, 0x0, 0x40, 0x1f, 0xff, 0x10, 0x0, + 0x54, 0x0, 0x57, 0xde, 0x55, 0x12, 0x19, 0x12, + 0x11, 0x12, 0x77, 0xd2, 0x11, 0x12, 0x11, 0x12, + 0x32, 0x92, 0x22, 0x7e, 0x24, 0x12, 0x48, 0x12, + + /* U+75F9 "痹" */ + 0x1, 0x0, 0x1, 0x0, 0xff, 0xf9, 0x0, 0x2, + 0x0, 0x15, 0xff, 0x2a, 0x22, 0x37, 0xfc, 0x28, + 0x89, 0xdf, 0xf0, 0x80, 0x1, 0xff, 0xf4, 0x41, + 0x8, 0x82, 0x13, 0x4, 0x4c, 0x8, + + /* U+75FC "痼" */ + 0x0, 0x80, 0x1, 0x0, 0xfd, 0xf1, 0x0, 0xa, + 0xff, 0xd5, 0x0, 0xaa, 0x21, 0x35, 0xfa, 0x28, + 0x85, 0xd3, 0xc8, 0xa8, 0x91, 0x51, 0x26, 0x9e, + 0x49, 0x0, 0x93, 0xff, 0x44, 0x2, + + /* U+75FF "痿" */ + 0x0, 0x80, 0x0, 0x80, 0xff, 0xf9, 0x0, 0xa, + 0x7f, 0x94, 0x8, 0x2b, 0xff, 0x30, 0x70, 0x23, + 0x59, 0xd8, 0x8c, 0x84, 0x1, 0xff, 0xf4, 0x21, + 0x8, 0x64, 0x10, 0x3c, 0x47, 0x86, + + /* U+7600 "瘀" */ + 0x0, 0x80, 0x1, 0x0, 0xff, 0xfd, 0x10, 0x8a, + 0x21, 0x15, 0xf5, 0x9, 0x9, 0x12, 0x23, 0xe7, + 0x0, 0x4a, 0x20, 0x94, 0x21, 0x68, 0x4, 0x96, + 0x9, 0x22, 0x24, 0xc2, 0x0, + + /* U+7601 "瘁" */ + 0x0, 0x80, 0x0, 0x40, 0x1f, 0xff, 0x10, 0x40, + 0x50, 0x40, 0x57, 0xfe, 0x50, 0x8, 0x12, 0x8, + 0x13, 0x1c, 0x74, 0xb6, 0x90, 0x0, 0x10, 0x40, + 0x2f, 0xff, 0x20, 0x40, 0x40, 0x40, 0x40, 0x40, + + /* U+7603 "瘃" */ + 0x0, 0x80, 0x0, 0x40, 0x1f, 0xff, 0x10, 0x0, + 0x57, 0xfe, 0x50, 0xc0, 0x51, 0x2, 0x16, 0x8c, + 0x30, 0xf0, 0x53, 0x48, 0x16, 0x68, 0x30, 0xa4, + 0x23, 0x22, 0x2c, 0x20, 0x41, 0xc0, + + /* U+7605 "瘅" */ + 0x0, 0x80, 0x0, 0x80, 0x1f, 0xfe, 0x10, 0x8, + 0x91, 0x8, 0x51, 0x10, 0x57, 0xfe, 0x54, 0x42, + 0x17, 0xfe, 0x74, 0x42, 0x17, 0xfe, 0x10, 0x40, + 0x20, 0x40, 0x2f, 0xff, 0x40, 0x40, 0x40, 0x40, + + /* U+760A "瘊" */ + 0x0, 0x40, 0x0, 0x40, 0x1f, 0xff, 0x10, 0x0, + 0x52, 0xfc, 0x52, 0x4, 0x54, 0x4, 0x55, 0xfe, + 0x1c, 0x0, 0x3c, 0xfe, 0x55, 0x10, 0x15, 0xfe, + 0x14, 0x30, 0x24, 0x28, 0x24, 0xc4, 0x45, 0x2, + + /* U+760C "瘌" */ + 0x0, 0x80, 0x1, 0x0, 0xff, 0xfd, 0x10, 0x2a, + 0xfd, 0x54, 0x42, 0x98, 0x85, 0x1f, 0xea, 0xf2, + 0x54, 0x5f, 0xa8, 0x98, 0x53, 0x3c, 0x24, 0xa4, + 0x4a, 0x40, 0xa0, 0x87, 0x0, + + /* U+7610 "瘐" */ + 0x0, 0x80, 0x1, 0x80, 0xff, 0xf9, 0x2, 0xa, + 0xe7, 0xd5, 0x8, 0x9a, 0x11, 0x17, 0x3e, 0x68, + 0x45, 0x5f, 0xf8, 0x83, 0x1, 0x5, 0x4, 0x31, + 0x8, 0xc1, 0xa6, 0x0, 0x80, + + /* U+7615 "瘕" */ + 0x0, 0x80, 0x1, 0x0, 0xff, 0xf9, 0x0, 0xa, + 0xff, 0xd5, 0x10, 0xaa, 0x21, 0x37, 0xfe, 0x28, + 0x0, 0xd0, 0x2, 0xbd, 0xf1, 0x42, 0x24, 0xf2, + 0x89, 0x2, 0x12, 0x1a, 0x44, 0x43, + + /* U+7617 "瘗" */ + 0x0, 0x80, 0x1, 0x0, 0xff, 0xf9, 0x0, 0x2, + 0x8, 0x15, 0xff, 0xa9, 0x22, 0x31, 0x48, 0x2f, + 0xfe, 0xc2, 0x82, 0x88, 0xc1, 0x64, 0x62, 0x7f, + 0x88, 0x10, 0x10, 0x20, 0x4f, 0xff, 0x0, 0x0, + + /* U+7618 "瘘" */ + 0x0, 0x80, 0x1, 0x0, 0xfd, 0xf5, 0x24, 0x4a, + 0x2b, 0x15, 0xff, 0x88, 0xa8, 0x16, 0x4e, 0xe1, + 0x1, 0x42, 0x0, 0xff, 0xfb, 0x20, 0x84, 0x62, + 0x8, 0x3c, 0x27, 0xc7, 0x0, + + /* U+7619 "瘙" */ + 0x0, 0x80, 0x1, 0x0, 0xff, 0xf9, 0x0, 0xa, + 0xff, 0xd4, 0xb1, 0xa8, 0x84, 0x30, 0xf0, 0x3e, + 0x3e, 0xc1, 0x2, 0xbf, 0xf1, 0x44, 0x24, 0xff, + 0xc9, 0x10, 0x17, 0xff, 0x40, 0x1, + + /* U+761B "瘛" */ + 0x0, 0x80, 0x1, 0x0, 0xfd, 0xf1, 0x10, 0xa, + 0xfb, 0xd4, 0x44, 0x9b, 0xc9, 0x11, 0x12, 0x6f, + 0xc5, 0x44, 0xb0, 0x82, 0x1, 0x54, 0x44, 0xa1, + 0x4a, 0x44, 0x60, 0xf8, 0x0, + + /* U+761F "瘟" */ + 0x0, 0x40, 0x0, 0x40, 0x1f, 0xff, 0x10, 0x0, + 0x50, 0x0, 0x53, 0xfc, 0x52, 0x4, 0x53, 0xfc, + 0x12, 0x4, 0x33, 0xfc, 0x50, 0x0, 0x17, 0xfe, + 0x24, 0x92, 0x24, 0x92, 0x24, 0x92, 0x5f, 0xff, + 0x0, 0x0, + + /* U+7620 "瘠" */ + 0x1, 0x0, 0x1, 0x0, 0xff, 0xf9, 0x0, 0x2, + 0x88, 0x54, 0xd3, 0x29, 0xac, 0x34, 0xa6, 0x22, + 0x30, 0xdf, 0xfe, 0x90, 0x21, 0x3f, 0xc2, 0x40, + 0x88, 0xff, 0x11, 0x2, 0x42, 0x3c, + + /* U+7622 "瘢" */ + 0x0, 0x80, 0x1, 0x0, 0xff, 0xf9, 0x0, 0xa, + 0x43, 0x95, 0xe5, 0x2c, 0x4a, 0x3a, 0xa6, 0x31, + 0x2, 0xfe, 0xfa, 0xc5, 0x11, 0xa9, 0x43, 0x52, + 0x8a, 0x22, 0x14, 0x4b, 0x53, 0xe3, + + /* U+7624 "瘤" */ + 0x0, 0x80, 0x0, 0x80, 0xff, 0xf9, 0x0, 0xa, + 0xf7, 0xd5, 0x4, 0xaa, 0x49, 0x35, 0xd2, 0x2c, + 0x5d, 0xc0, 0x2, 0xbf, 0xf1, 0x42, 0x22, 0xff, + 0xc9, 0x8, 0x93, 0xff, 0x44, 0x2, + + /* U+7625 "瘥" */ + 0x0, 0x80, 0x0, 0x80, 0xff, 0xf9, 0x10, 0x4a, + 0x11, 0x15, 0xff, 0xe8, 0x20, 0x13, 0xfe, 0x21, + 0x1, 0xdf, 0xfc, 0x84, 0x1, 0x10, 0x6, 0x5f, + 0xc9, 0x88, 0x14, 0x10, 0x43, 0xff, 0x0, 0x0, + + /* U+7626 "瘦" */ + 0x0, 0x80, 0x7f, 0xfc, 0x80, 0x5, 0x73, 0xea, + 0x84, 0x55, 0x8, 0x9b, 0xdf, 0x14, 0x22, 0x6f, + 0xfd, 0x40, 0x80, 0xbf, 0xe1, 0x10, 0x44, 0x1b, + 0x8, 0x3c, 0x27, 0x87, 0x80, + + /* U+7629 "瘩" */ + 0x0, 0x80, 0x1, 0x0, 0xfd, 0xf5, 0x10, 0x8a, + 0xff, 0xd4, 0x4a, 0x18, 0x70, 0x13, 0x1c, 0xfd, + 0xf6, 0x40, 0x0, 0x80, 0x1, 0x3f, 0xc4, 0x40, + 0x88, 0xff, 0x21, 0x2, 0x0, + + /* U+762A "瘪" */ + 0x0, 0x0, 0x1, 0x0, 0xff, 0xf9, 0x4, 0xa, + 0xff, 0xd5, 0x0, 0xab, 0xff, 0x34, 0x2, 0x2f, + 0xfc, 0xdf, 0xfa, 0x87, 0x1, 0x31, 0x83, 0xc1, + 0xe8, 0xfc, 0x91, 0x1, 0x43, 0xfc, + + /* U+762B "瘫" */ + 0x0, 0x80, 0x1, 0x0, 0xff, 0xf9, 0x3, 0xa, + 0xe5, 0x14, 0x5f, 0xd9, 0x64, 0x37, 0x7f, 0xe4, + 0x90, 0x49, 0x20, 0xb3, 0xf9, 0x54, 0x85, 0xa9, + 0xa, 0x1f, 0xe0, 0x20, 0x0, + + /* U+762D "瘭" */ + 0x0, 0x80, 0x1, 0x0, 0xff, 0xf9, 0x0, 0xa, + 0xff, 0xd4, 0x24, 0x2b, 0xff, 0x34, 0x92, 0x2f, + 0xfd, 0xc0, 0x2, 0x9f, 0xe1, 0x0, 0x2, 0xff, + 0xe8, 0x10, 0x13, 0x27, 0x49, 0xc0, + + /* U+7630 "瘰" */ + 0x0, 0x0, 0x1, 0x0, 0xff, 0xf9, 0x0, 0xa, + 0xff, 0xd5, 0x10, 0xab, 0xff, 0x34, 0x42, 0x2f, + 0xfc, 0xc4, 0x22, 0x9f, 0x81, 0x18, 0x86, 0xff, + 0xc8, 0x10, 0x92, 0x27, 0x49, 0xc1, + + /* U+7633 "瘳" */ + 0x0, 0x80, 0x7f, 0xfc, 0x80, 0x5, 0xfb, 0xea, + 0x90, 0x54, 0xa4, 0x99, 0xdd, 0x1c, 0x42, 0x63, + 0x61, 0x79, 0xbc, 0x9c, 0x81, 0x6, 0x4, 0xf3, + 0x8, 0x30, 0x27, 0x80, 0x0, + + /* U+7634 "瘴" */ + 0x0, 0x0, 0x0, 0x80, 0xff, 0xf9, 0x2, 0xa, + 0xff, 0xd4, 0x2, 0x28, 0x48, 0x57, 0xff, 0x20, + 0x1, 0xcf, 0xf2, 0x9f, 0xe1, 0x20, 0x44, 0x3f, + 0xb, 0xff, 0xd0, 0x10, 0x40, 0x20, + + /* U+7635 "瘵" */ + 0x0, 0x80, 0x1, 0x0, 0xff, 0xf9, 0x0, 0xa, + 0x44, 0x14, 0xf7, 0xaa, 0x29, 0x3e, 0x94, 0x22, + 0x10, 0xcb, 0xd2, 0xe0, 0x19, 0x3f, 0xc2, 0x4, + 0x8, 0xca, 0x16, 0x13, 0xc1, 0xc0, + + /* U+7638 "瘸" */ + 0x0, 0x80, 0x1, 0x0, 0xff, 0xf9, 0x10, 0xb, + 0xfb, 0xd4, 0x98, 0xa9, 0x31, 0x3d, 0xdc, 0x20, + 0x40, 0xdf, 0xfa, 0xa2, 0x11, 0x4b, 0xa2, 0xa8, + 0x49, 0x3c, 0x93, 0x5, 0x44, 0x1e, + + /* U+763C "瘼" */ + 0x0, 0x0, 0x0, 0x40, 0x1f, 0xff, 0x10, 0x0, + 0x11, 0x10, 0x57, 0xfe, 0x57, 0xfc, 0x34, 0x4, + 0x17, 0xfc, 0x74, 0x4, 0x17, 0xfc, 0x10, 0x40, + 0x2f, 0xff, 0x20, 0xa0, 0x43, 0x18, 0x4c, 0x6, + + /* U+763E "瘾" */ + 0x0, 0x80, 0x1, 0x0, 0xff, 0xf9, 0x0, 0xa, + 0xe4, 0x16, 0x5f, 0x2d, 0x44, 0x3a, 0xfe, 0x34, + 0x4, 0xeb, 0xfa, 0xc8, 0x11, 0x97, 0xc3, 0x6a, + 0x4a, 0x51, 0x54, 0xa2, 0x48, 0x78, + + /* U+763F "瘿" */ + 0x0, 0x40, 0x7f, 0xfc, 0x80, 0x1, 0x7d, 0xea, + 0xaa, 0x55, 0x56, 0x9a, 0xad, 0x13, 0x8c, 0xec, + 0xa4, 0x42, 0x0, 0xbf, 0xfb, 0x10, 0x84, 0x32, + 0x8, 0x3e, 0x27, 0x83, 0x0, + + /* U+7640 "癀" */ + 0x0, 0x0, 0x1, 0x0, 0xff, 0xf9, 0x0, 0x2, + 0x11, 0x15, 0xff, 0xa8, 0x44, 0x57, 0xff, 0x20, + 0x40, 0xdf, 0xfa, 0xa1, 0x11, 0x7f, 0xe2, 0x84, + 0x49, 0xff, 0x90, 0x0, 0x47, 0xe, 0x0, 0x0, + + /* U+7643 "癃" */ + 0x0, 0x80, 0x1, 0x0, 0xff, 0xf9, 0x2, 0xb, + 0xef, 0xd6, 0x51, 0xad, 0x54, 0x3a, 0x38, 0x35, + 0x8e, 0xe9, 0xf2, 0xca, 0x81, 0x97, 0xe5, 0x32, + 0xa, 0x9f, 0x94, 0x8, 0x48, 0xff, + + /* U+764C "癌" */ + 0x0, 0x80, 0x1, 0x0, 0xff, 0xf9, 0x0, 0xa, + 0x7f, 0x94, 0x81, 0x29, 0xfe, 0x10, 0x0, 0x2f, + 0x3d, 0xd2, 0x4a, 0xa4, 0x91, 0x79, 0xe2, 0xc, + 0x9, 0x18, 0x93, 0xff, 0x40, 0x2, + + /* U+764D "癍" */ + 0x0, 0x80, 0x0, 0x80, 0xff, 0xf9, 0x0, 0xa, + 0xe5, 0xf4, 0xbc, 0x99, 0x9, 0x12, 0x12, 0x6e, + 0xa5, 0x49, 0xbc, 0x91, 0x11, 0x25, 0x25, 0xea, + 0x50, 0x23, 0xe0, 0x0, 0x0, + + /* U+7654 "癔" */ + 0x0, 0x0, 0x1, 0x0, 0xff, 0xf9, 0x4, 0xa, + 0xff, 0xf4, 0x42, 0x2b, 0xff, 0xb0, 0x0, 0x27, + 0xfd, 0xc8, 0x8, 0x9f, 0xf1, 0x3f, 0xe2, 0x8, + 0x9, 0x4a, 0x92, 0x84, 0xc0, 0xf0, + + /* U+7656 "癖" */ + 0x0, 0x0, 0x1, 0x3, 0xff, 0xc8, 0x0, 0xaf, + 0x92, 0xa2, 0xfa, 0x88, 0x5a, 0x2a, 0x2f, 0x29, + 0xa1, 0xfa, 0x81, 0xb, 0xe4, 0x34, 0xbd, 0x52, + 0x45, 0x79, 0x21, 0x24, + + /* U+765C "癜" */ + 0x0, 0x0, 0x1, 0x0, 0xff, 0xf9, 0x0, 0x2, + 0xfd, 0xd5, 0xc, 0xab, 0xf9, 0x34, 0x12, 0x2a, + 0x82, 0xdf, 0xfa, 0xaa, 0x91, 0x55, 0x22, 0xfd, + 0x89, 0x13, 0x14, 0x96, 0x4a, 0x13, + + /* U+765E "癞" */ + 0x0, 0x0, 0x1, 0x0, 0xff, 0xf9, 0x0, 0x2, + 0x21, 0x15, 0xf7, 0xe8, 0x91, 0x37, 0xff, 0x2a, + 0xc3, 0xd5, 0x94, 0xbf, 0x29, 0x12, 0x52, 0x74, + 0xa9, 0x53, 0x14, 0x89, 0xc1, 0x20, + + /* U+7663 "癣" */ + 0x0, 0x80, 0x0, 0x80, 0xff, 0xf9, 0x0, 0xa, + 0x42, 0x35, 0xf2, 0xaa, 0x4f, 0xbf, 0xc4, 0x2a, + 0x89, 0xdf, 0x38, 0xaa, 0x21, 0x54, 0x42, 0xfb, + 0xe8, 0x1, 0x17, 0xf2, 0x40, 0x4, + + /* U+766B "癫" */ + 0x0, 0x40, 0x7f, 0xfc, 0x80, 0x5, 0x7d, 0xfa, + 0x20, 0x95, 0xf2, 0x1a, 0x2f, 0x97, 0xd1, 0xef, + 0xaa, 0x51, 0x54, 0xbe, 0xab, 0xff, 0x54, 0x11, + 0x99, 0x14, 0xc4, 0x0, 0x0, + + /* U+766F "癯" */ + 0x0, 0x0, 0x0, 0x80, 0xff, 0xf9, 0x0, 0xa, + 0xfb, 0xd5, 0xf7, 0xaa, 0x29, 0x37, 0xde, 0x2f, + 0xbc, 0xc4, 0x82, 0x97, 0xe1, 0x7f, 0xe2, 0x44, + 0x8, 0xff, 0x11, 0xff, 0xc2, 0x0, + + /* U+7678 "癸" */ + 0x0, 0x80, 0xfd, 0x30, 0x9, 0x82, 0x22, 0x23, + 0x83, 0x83, 0x2, 0xb, 0xfa, 0x20, 0x83, 0x81, + 0x0, 0xff, 0xf0, 0x4, 0x0, 0x18, 0x0, 0x4c, + 0x3, 0x7, 0x18, 0x1, 0x0, + + /* U+767B "登" */ + 0x0, 0x88, 0xfd, 0x20, 0x11, 0x82, 0x22, 0x62, + 0x83, 0x6, 0xfb, 0x10, 0x1, 0xdf, 0xf8, 0x20, + 0x10, 0x40, 0x20, 0xff, 0xc0, 0x0, 0x0, 0x82, + 0x0, 0x88, 0x3f, 0xff, 0x80, + + /* U+767D "白" */ + 0x4, 0x0, 0x20, 0x3f, 0xff, 0x0, 0x18, 0x0, + 0xc0, 0x6, 0x0, 0x3f, 0xff, 0x80, 0xc, 0x0, + 0x60, 0x3, 0x0, 0x18, 0x0, 0xff, 0xfe, 0x0, + 0x20, + + /* U+767E "百" */ + 0xff, 0xfe, 0x2, 0x0, 0x8, 0x1, 0xff, 0xc2, + 0x0, 0x84, 0x1, 0x8, 0x2, 0x1f, 0xfc, 0x20, + 0x8, 0x40, 0x10, 0x80, 0x21, 0x0, 0x43, 0xff, + 0x84, 0x1, 0x0, + + /* U+7682 "皂" */ + 0x1, 0x0, 0x4, 0x0, 0xff, 0xe1, 0x0, 0x42, + 0x0, 0x87, 0xff, 0x8, 0x2, 0x10, 0x4, 0x3f, + 0xf8, 0x0, 0x0, 0x8, 0x7, 0xff, 0xf0, 0x20, + 0x0, 0x40, 0x80, 0x81, 0x0, 0xfc, + + /* U+7684 "的" */ + 0x0, 0x0, 0x82, 0x2, 0x8, 0x3f, 0x3f, 0x85, + 0x6, 0x14, 0x18, 0x40, 0x61, 0x21, 0xfc, 0x46, + 0x10, 0x98, 0x40, 0x61, 0x1, 0x84, 0x7, 0xf0, + 0x18, 0x40, 0xe1, 0x1e, + + /* U+7686 "皆" */ + 0x81, 0x2, 0x4, 0x8, 0x11, 0xbe, 0x58, 0x81, + 0x86, 0x64, 0x1e, 0x1f, 0x81, 0x0, 0x8, 0x1, + 0xff, 0xc4, 0x1, 0x1f, 0xfc, 0x40, 0x11, 0x0, + 0x47, 0xff, 0x10, 0x4, + + /* U+7687 "皇" */ + 0x0, 0x0, 0x8, 0x0, 0xff, 0xe1, 0x0, 0x42, + 0x0, 0x87, 0xff, 0x8, 0x2, 0x1f, 0xfc, 0x0, + 0x0, 0xff, 0xf8, 0x4, 0x0, 0x8, 0x7, 0xff, + 0xc0, 0x20, 0x0, 0x40, 0x7f, 0xff, + + /* U+7688 "皈" */ + 0x40, 0x0, 0x87, 0xfb, 0xc8, 0x4, 0x90, 0x9, + 0x20, 0x12, 0x7f, 0xa4, 0xa1, 0x79, 0x42, 0x92, + 0x49, 0x24, 0x92, 0x58, 0xc7, 0xa1, 0x88, 0x4c, + 0x81, 0x30, 0xc0, 0x0, 0x0, + + /* U+768B "皋" */ + 0x2, 0x0, 0x7f, 0xf0, 0x80, 0x21, 0xff, 0xc2, + 0x0, 0x84, 0x1, 0xf, 0xfe, 0x1, 0x0, 0xff, + 0xfe, 0x18, 0xc0, 0xc4, 0x76, 0xff, 0xb0, 0x10, + 0x0, 0x20, 0x0, 0x40, 0x0, + + /* U+768E "皎" */ + 0x0, 0x1, 0x1, 0x4, 0x4, 0x3d, 0xff, 0x90, + 0x2, 0x44, 0xc9, 0x21, 0xa5, 0x83, 0xfe, 0x16, + 0x44, 0x49, 0x12, 0x24, 0x28, 0x90, 0x43, 0xc6, + 0x88, 0x31, 0x83, 0x1, + + /* U+7691 "皑" */ + 0x44, 0x44, 0x88, 0x8b, 0xd1, 0x14, 0xbf, 0xe9, + 0x0, 0x12, 0x0, 0x24, 0xff, 0x78, 0x2, 0x90, + 0x5, 0x2f, 0xfa, 0x50, 0x4, 0xa0, 0x1f, 0x40, + 0x32, 0x80, 0x80, 0xff, 0x0, + + /* U+7693 "皓" */ + 0x20, 0x20, 0x42, 0x43, 0xef, 0xfc, 0x51, 0x8, + 0xc2, 0x11, 0x4, 0x23, 0xff, 0xfc, 0x0, 0x88, + 0x1, 0x17, 0xfa, 0x28, 0x14, 0x50, 0x2f, 0xa0, + 0x51, 0x7f, 0x80, 0x81, 0x0, + + /* U+7696 "皖" */ + 0x40, 0x41, 0x1, 0xf, 0x7f, 0xe5, 0x1, 0x90, + 0x2, 0x4f, 0xe9, 0x0, 0x3c, 0x0, 0x97, 0xfe, + 0x44, 0x89, 0x12, 0x24, 0x48, 0xf1, 0x26, 0x8, + 0x90, 0xc1, 0xc0, + + /* U+7699 "皙" */ + 0x10, 0x0, 0x47, 0xef, 0xd0, 0xc, 0x40, 0x59, + 0xfd, 0x54, 0x49, 0x21, 0x4, 0x44, 0x3f, 0xf8, + 0x80, 0x22, 0x0, 0x8f, 0xfe, 0x20, 0x8, 0xff, + 0xe2, 0x0, 0x80, + + /* U+76A4 "皤" */ + 0x40, 0x79, 0xf, 0xf, 0x24, 0xa4, 0x54, 0x97, + 0xfe, 0x43, 0x89, 0x15, 0x3f, 0x83, 0x93, 0xfa, + 0x49, 0x29, 0x24, 0xa4, 0xfe, 0xf2, 0x4a, 0x4f, + 0xe0, 0x20, 0x80, + + /* U+76AE "皮" */ + 0x0, 0x80, 0x0, 0x80, 0x3f, 0xfe, 0x20, 0x82, + 0x20, 0x82, 0x20, 0x80, 0x3f, 0xfc, 0x28, 0x4, + 0x24, 0x8, 0x22, 0x10, 0x21, 0x20, 0x40, 0xc0, + 0x41, 0xe0, 0x4e, 0x1c, 0x18, 0x2, + + /* U+76B1 "皱" */ + 0x20, 0x10, 0x3c, 0x10, 0x44, 0xff, 0x48, 0x91, + 0x88, 0x91, 0x7c, 0x90, 0x4, 0xff, 0x4, 0xc2, + 0x4, 0xc2, 0x7c, 0xa4, 0x4, 0xac, 0x5, 0x98, + 0x7d, 0x2c, 0x5, 0x43, 0x0, 0x0, + + /* U+76B2 "皲" */ + 0x0, 0x10, 0xfe, 0x10, 0x92, 0xff, 0x10, 0x91, + 0xfe, 0x91, 0x20, 0x90, 0x40, 0xfe, 0x50, 0xc2, + 0xfe, 0xc2, 0x10, 0xa4, 0x10, 0xac, 0x1f, 0x18, + 0xf1, 0x18, 0x11, 0x66, 0x12, 0xc0, + + /* U+76B4 "皴" */ + 0x10, 0x10, 0x24, 0x10, 0x44, 0xff, 0x7e, 0x91, + 0x0, 0x91, 0x24, 0x90, 0x42, 0xfe, 0x10, 0xc2, + 0x3c, 0xc2, 0x44, 0xa4, 0xe4, 0xa8, 0x19, 0x18, + 0x1d, 0x38, 0x61, 0x66, 0x2, 0x80, + + /* U+76BF "皿" */ + 0x3f, 0xf8, 0x88, 0x91, 0x11, 0x22, 0x22, 0x44, + 0x44, 0x88, 0x89, 0x11, 0x12, 0x22, 0x24, 0x44, + 0x48, 0x88, 0x91, 0x11, 0x22, 0x22, 0x4f, 0xff, + 0xe0, + + /* U+76C2 "盂" */ + 0x3f, 0xf8, 0x1, 0x0, 0x2, 0x7, 0xff, 0xf0, + 0x8, 0x0, 0x10, 0x3, 0xc0, 0x0, 0x0, 0x3f, + 0xf8, 0x48, 0x90, 0x91, 0x21, 0x22, 0x4f, 0xff, + 0xe0, + + /* U+76C5 "盅" */ + 0x1, 0x0, 0x2, 0x0, 0xff, 0xf2, 0x8, 0x24, + 0x10, 0x48, 0x20, 0x8f, 0xfe, 0x0, 0x80, 0x0, + 0x0, 0x7f, 0xf9, 0x11, 0x12, 0x22, 0x24, 0x44, + 0x5f, 0xff, 0xc0, + + /* U+76C6 "盆" */ + 0x0, 0x0, 0x8, 0x80, 0x60, 0xc1, 0x0, 0x6d, + 0xff, 0x60, 0x82, 0x3, 0x4, 0xc, 0x8, 0x61, + 0xe0, 0x0, 0x0, 0xff, 0xe1, 0x22, 0x42, 0x44, + 0x84, 0x89, 0x3f, 0xff, 0x80, + + /* U+76C8 "盈" */ + 0x7f, 0xf0, 0x20, 0x20, 0x40, 0x40, 0xbe, 0xf1, + 0x4, 0x24, 0xd0, 0x48, 0x60, 0xa7, 0x2e, 0x80, + 0x0, 0x7f, 0xf8, 0x91, 0x11, 0x22, 0x22, 0x44, + 0x5f, 0xff, 0xc0, + + /* U+76CA "益" */ + 0x0, 0x0, 0x20, 0x20, 0x20, 0x80, 0x22, 0xf, + 0xff, 0xe0, 0x0, 0x3, 0x8, 0x18, 0xc, 0xc0, + 0x6, 0x7f, 0xf0, 0x91, 0x21, 0x22, 0x42, 0x44, + 0x84, 0x89, 0x3f, 0xff, 0x80, + + /* U+76CD "盍" */ + 0x1, 0x0, 0x7f, 0xf0, 0x4, 0x0, 0x8, 0xf, + 0xff, 0xe0, 0x80, 0x6, 0x8, 0x1f, 0xfc, 0x0, + 0x0, 0x7f, 0xf0, 0x91, 0x21, 0x22, 0x42, 0x44, + 0x84, 0x89, 0x3f, 0xff, 0x80, + + /* U+76CE "盎" */ + 0x1, 0x0, 0x7f, 0xf0, 0x84, 0x21, 0x8, 0x42, + 0x10, 0x9f, 0xff, 0xc0, 0xa0, 0x6, 0x38, 0xd0, + 0xe, 0x7f, 0xf0, 0x91, 0x21, 0x22, 0x42, 0x44, + 0x9f, 0xff, 0xc0, + + /* U+76CF "盏" */ + 0x4, 0x60, 0x8, 0x1, 0xff, 0xf0, 0x30, 0x7, + 0xff, 0xc0, 0x42, 0x0, 0x79, 0x7, 0xc2, 0x70, + 0x7c, 0x0, 0x0, 0xff, 0xe1, 0x22, 0x42, 0x44, + 0x9f, 0xff, 0xc0, + + /* U+76D0 "盐" */ + 0x8, 0x20, 0x10, 0x40, 0x20, 0x83, 0xf9, 0x80, + 0x82, 0x81, 0x4, 0xc2, 0xc8, 0x7a, 0x10, 0x0, + 0x0, 0x7f, 0xf0, 0x89, 0x21, 0x12, 0x42, 0x24, + 0x84, 0x49, 0x3f, 0xff, 0x80, + + /* U+76D1 "监" */ + 0x4, 0x20, 0x88, 0x81, 0x11, 0x2, 0x25, 0xf4, + 0x5a, 0x8, 0xa2, 0x11, 0x2, 0x0, 0x0, 0x3f, + 0xfc, 0x48, 0x88, 0x91, 0x11, 0x22, 0x22, 0x44, + 0x5f, 0xff, 0xc0, + + /* U+76D2 "盒" */ + 0x0, 0x0, 0x7, 0x0, 0x11, 0x0, 0xc1, 0x8e, + 0xfe, 0xe0, 0x0, 0x7, 0xfc, 0x10, 0x8, 0x20, + 0x10, 0x3f, 0xe0, 0x0, 0x1, 0xff, 0xc2, 0x44, + 0x84, 0x89, 0x3f, 0xff, 0x80, + + /* U+76D4 "盔" */ + 0x8, 0x0, 0x10, 0x3, 0xff, 0xf8, 0x84, 0x1, + 0x8, 0x4, 0x99, 0x8, 0x70, 0x21, 0x98, 0x9c, + 0xc, 0x0, 0x0, 0xff, 0xe1, 0x22, 0x42, 0x44, + 0x84, 0x89, 0x3f, 0xff, 0x80, + + /* U+76D6 "盖" */ + 0x18, 0x30, 0x8, 0x83, 0xff, 0xf8, 0x8, 0x7, + 0xff, 0xc0, 0x20, 0x0, 0x40, 0x7f, 0xff, 0x0, + 0x0, 0x7f, 0xf0, 0x91, 0x21, 0x22, 0x42, 0x44, + 0x84, 0x89, 0x3f, 0xff, 0x80, + + /* U+76D7 "盗" */ + 0x1, 0x0, 0x82, 0x0, 0x8f, 0xf8, 0x92, 0x10, + 0x44, 0x42, 0x14, 0x8, 0x24, 0x21, 0x84, 0x8c, + 0x6, 0x0, 0x0, 0xff, 0xf1, 0x22, 0x22, 0x44, + 0x44, 0x88, 0xbf, 0xff, 0x80, + + /* U+76D8 "盘" */ + 0x0, 0x0, 0x4, 0x0, 0x7f, 0xe0, 0x80, 0x41, + 0x10, 0x82, 0x11, 0x3f, 0xff, 0x88, 0x4, 0x23, + 0x18, 0x80, 0x40, 0xff, 0xe1, 0x22, 0x42, 0x44, + 0x84, 0x89, 0x3f, 0xff, 0x80, + + /* U+76DB "盛" */ + 0x0, 0x90, 0x1, 0x0, 0xff, 0xf9, 0x4, 0x23, + 0xe4, 0x88, 0x4a, 0x50, 0x98, 0xae, 0x4a, 0x1, + 0x8, 0x7f, 0xf0, 0x91, 0x21, 0x22, 0x42, 0x44, + 0x9f, 0xff, 0xc0, + + /* U+76DF "盟" */ + 0x7c, 0xfc, 0x89, 0x9, 0x13, 0xf3, 0xe4, 0x24, + 0x48, 0x48, 0x9f, 0x9f, 0x41, 0x0, 0xc, 0x3f, + 0xfc, 0x48, 0x88, 0x91, 0x11, 0x22, 0x2f, 0xff, + 0xe0, + + /* U+76E5 "盥" */ + 0xc, 0x80, 0xe1, 0x39, 0x2, 0x12, 0x37, 0x27, + 0xa9, 0xc8, 0x98, 0x9d, 0x2f, 0x20, 0x82, 0x2, + 0x0, 0x7f, 0xf0, 0x89, 0x21, 0x12, 0x42, 0x24, + 0x9f, 0xff, 0xc0, + + /* U+76EE "目" */ + 0xff, 0xf0, 0x6, 0x0, 0xc0, 0x1f, 0xff, 0x0, + 0x60, 0xc, 0x1, 0xff, 0xf0, 0x6, 0x0, 0xc0, + 0x1f, 0xff, 0x0, 0x40, + + /* U+76EF "盯" */ + 0xfb, 0xfe, 0x20, 0x88, 0x82, 0x3e, 0x8, 0x88, + 0x22, 0x20, 0x88, 0x82, 0x3e, 0x8, 0x88, 0x22, + 0x20, 0x88, 0x82, 0x3e, 0x8, 0x88, 0x20, 0x7, + 0x0, + + /* U+76F1 "盱" */ + 0xfb, 0xfd, 0x10, 0x42, 0x20, 0x87, 0xc1, 0x8, + 0x82, 0x11, 0x7f, 0xe2, 0x8, 0x7c, 0x10, 0x88, + 0x21, 0x10, 0x42, 0x20, 0x87, 0xc1, 0x8, 0x82, + 0x0, 0x38, 0x0, + + /* U+76F2 "盲" */ + 0x1, 0x0, 0x1, 0x3, 0xff, 0xf9, 0x0, 0x2, + 0x0, 0x7, 0xff, 0x80, 0x0, 0x1f, 0xfe, 0x20, + 0x4, 0x40, 0x8, 0xff, 0xf1, 0x0, 0x23, 0xff, + 0xc4, 0x0, 0x8f, 0xff, 0x10, 0x2, + + /* U+76F4 "直" */ + 0x1, 0x1, 0xff, 0xfc, 0x4, 0x0, 0x8, 0x3, + 0xff, 0x84, 0x1, 0xf, 0xfe, 0x10, 0x4, 0x3f, + 0xf8, 0x40, 0x10, 0x80, 0x21, 0xff, 0xc2, 0x0, + 0x9f, 0xff, 0xc0, + + /* U+76F8 "相" */ + 0x10, 0x0, 0x23, 0xfc, 0x44, 0xb, 0xe8, 0x11, + 0x10, 0x22, 0x3f, 0xce, 0x40, 0x9a, 0x81, 0x51, + 0x2, 0xa3, 0xfc, 0x44, 0x8, 0x88, 0x11, 0x10, + 0x22, 0x3f, 0xc4, 0x40, 0x80, + + /* U+76F9 "盹" */ + 0x0, 0x43, 0xc1, 0x9, 0x7f, 0xe4, 0x10, 0xf4, + 0x4a, 0x51, 0x29, 0x44, 0xa5, 0x12, 0xf3, 0xfa, + 0x41, 0x9, 0x4, 0x24, 0x11, 0xf0, 0x46, 0x41, + 0x10, 0x7, 0xc0, + + /* U+76FC "盼" */ + 0x0, 0x21, 0xe1, 0x22, 0x44, 0x44, 0x88, 0x4f, + 0x20, 0x92, 0x80, 0xa7, 0x0, 0xc9, 0xfc, 0xf1, + 0x9, 0x22, 0x12, 0x44, 0x27, 0x90, 0x48, 0x20, + 0x90, 0x81, 0x2, 0x3c, 0x0, + + /* U+76FE "盾" */ + 0x0, 0x4, 0x3f, 0xf0, 0x20, 0x40, 0x3f, 0xff, + 0x20, 0x40, 0x20, 0x40, 0x27, 0xfc, 0x24, 0x4, + 0x27, 0xfc, 0x24, 0x4, 0x24, 0x4, 0x27, 0xfc, + 0x44, 0x4, 0x47, 0xfc, 0x4, 0x4, + + /* U+7701 "省" */ + 0x0, 0x80, 0x4, 0x90, 0x18, 0x8c, 0x20, 0x82, + 0x40, 0x70, 0x7, 0x80, 0xff, 0xfc, 0x10, 0x4, + 0x10, 0x4, 0x1f, 0xfc, 0x10, 0x4, 0x1f, 0xfc, + 0x10, 0x4, 0x1f, 0xfc, 0x10, 0x4, + + /* U+7704 "眄" */ + 0xf7, 0xff, 0x20, 0x82, 0x49, 0x7, 0x93, 0xe9, + 0x24, 0x52, 0x48, 0xa4, 0x91, 0x79, 0xfe, 0x90, + 0x5, 0x20, 0xa, 0x40, 0x17, 0x80, 0x29, 0x0, + 0xc0, 0x1f, 0x0, + + /* U+7707 "眇" */ + 0x0, 0x43, 0xe1, 0x8, 0x84, 0x22, 0x52, 0x8a, + 0x4b, 0xe9, 0x18, 0xa4, 0x62, 0x10, 0xf8, 0x4a, + 0x21, 0x28, 0x81, 0x22, 0x8, 0xf8, 0xc0, 0xc, + 0x0, 0x40, 0x0, + + /* U+7708 "眈" */ + 0x0, 0x41, 0xe0, 0x82, 0x41, 0x4, 0xbf, 0xef, + 0x44, 0x52, 0x88, 0xa4, 0x10, 0x48, 0x70, 0xf0, + 0xa1, 0x21, 0x42, 0x44, 0x84, 0x89, 0x1f, 0x22, + 0x50, 0x84, 0x86, 0xf, 0x0, + + /* U+7709 "眉" */ + 0x3f, 0xfc, 0x82, 0x12, 0x8, 0x4f, 0xff, 0x20, + 0x0, 0x9f, 0xf2, 0x40, 0x49, 0xff, 0x24, 0x4, + 0x90, 0x12, 0x7f, 0xd1, 0x1, 0x47, 0xfe, 0x10, + 0x10, + + /* U+770B "看" */ + 0x7f, 0xfc, 0x4, 0x1, 0xff, 0xf0, 0x20, 0xf, + 0xff, 0xe1, 0x0, 0x7, 0xfe, 0x18, 0x4, 0x5f, + 0xf9, 0x20, 0x10, 0x7f, 0xe0, 0x80, 0x41, 0xff, + 0x82, 0x1, 0x0, + + /* U+770D "眍" */ + 0xf3, 0xff, 0x24, 0x2, 0x4a, 0x14, 0x92, 0x4f, + 0x24, 0x92, 0x46, 0x24, 0x84, 0x79, 0x18, 0x92, + 0x49, 0x24, 0x92, 0x4a, 0x17, 0x94, 0x9, 0x20, + 0x0, 0x7f, 0xc0, + + /* U+7719 "眙" */ + 0x0, 0x83, 0xc2, 0x9, 0x10, 0x24, 0x44, 0xf2, + 0xa, 0x5f, 0xe9, 0x0, 0x64, 0x0, 0xf3, 0xfa, + 0x48, 0x29, 0x20, 0xa4, 0x82, 0xf2, 0xa, 0x4f, + 0xe0, 0x20, 0x80, + + /* U+771A "眚" */ + 0x1, 0x0, 0x42, 0x1, 0xff, 0xf2, 0x8, 0x3, + 0xff, 0xc0, 0x20, 0x3f, 0xff, 0x80, 0x0, 0x3f, + 0xf8, 0x40, 0x10, 0xff, 0xe1, 0x0, 0x43, 0xff, + 0x84, 0x1, 0xf, 0xfe, 0x10, 0x4, + + /* U+771F "真" */ + 0x1, 0x1, 0xff, 0xfc, 0x4, 0x1, 0xff, 0xc2, + 0x0, 0x87, 0xff, 0x8, 0x2, 0x1f, 0xfc, 0x20, + 0x8, 0x7f, 0xf0, 0x80, 0x27, 0xff, 0xf0, 0x44, + 0x7, 0x7, 0x30, 0x1, 0x0, + + /* U+7720 "眠" */ + 0xf3, 0xfd, 0x24, 0xa, 0x48, 0x17, 0x9f, 0xe9, + 0x22, 0x12, 0x44, 0x24, 0x88, 0x79, 0xfe, 0x92, + 0x21, 0x24, 0x42, 0x48, 0x4f, 0x96, 0x99, 0x31, + 0x52, 0x41, 0x80, + + /* U+7722 "眢" */ + 0x10, 0x0, 0x7d, 0xf9, 0x8a, 0x14, 0xa4, 0xc0, + 0xc8, 0x22, 0x1f, 0xb8, 0x0, 0x1f, 0xfc, 0x20, + 0x8, 0x7f, 0xf0, 0x80, 0x21, 0xff, 0xc2, 0x0, + 0x87, 0xff, 0x8, 0x2, 0x0, + + /* U+7726 "眦" */ + 0x0, 0xa1, 0xe1, 0x42, 0x42, 0x84, 0xa5, 0x19, + 0x4a, 0x5e, 0x9f, 0x25, 0x28, 0x4a, 0x50, 0xf4, + 0xa1, 0x29, 0x42, 0x52, 0x84, 0xa5, 0x1f, 0x4a, + 0x30, 0xfc, 0x42, 0xf, 0x0, + + /* U+7728 "眨" */ + 0xf7, 0xfe, 0x40, 0x9, 0x8, 0x24, 0x10, 0xf0, + 0x2, 0x5f, 0xe9, 0x0, 0xbc, 0x4, 0x90, 0x22, + 0x41, 0x9, 0x18, 0x3c, 0x80, 0x87, 0x0, 0x23, + 0xf0, + + /* U+7729 "眩" */ + 0x0, 0x43, 0xc1, 0x9, 0x7f, 0xe4, 0x20, 0x91, + 0x3, 0xc4, 0x49, 0x21, 0x25, 0xf8, 0xf0, 0x42, + 0x43, 0x9, 0x19, 0x24, 0x44, 0xf2, 0xa, 0x5f, + 0xe0, 0x0, 0x40, + + /* U+772D "眭" */ + 0x0, 0x43, 0xc1, 0x9, 0x3f, 0xa4, 0x10, 0x90, + 0x43, 0xc1, 0x9, 0x7f, 0xe4, 0x10, 0xf0, 0x42, + 0x5f, 0xe9, 0x4, 0x24, 0x10, 0xf0, 0x42, 0x3f, + 0xf0, + + /* U+772F "眯" */ + 0x0, 0x41, 0xe0, 0x82, 0x49, 0x24, 0x92, 0x49, + 0x15, 0x1e, 0x8, 0x25, 0xff, 0x48, 0x70, 0xf1, + 0x61, 0x22, 0xa2, 0x49, 0x44, 0x92, 0x4f, 0x44, + 0x51, 0x8, 0x0, 0x10, 0x0, + + /* U+7735 "眵" */ + 0x0, 0x0, 0x2, 0xf, 0x1f, 0xe4, 0xc2, 0x97, + 0x1a, 0x52, 0x8f, 0x4, 0x24, 0x60, 0x96, 0xc3, + 0xc5, 0xf9, 0x30, 0xa5, 0x24, 0x90, 0x33, 0xc1, + 0x8, 0x38, 0x3, 0x0, + + /* U+7736 "眶" */ + 0xf3, 0xff, 0x24, 0x2, 0x4b, 0xf7, 0x91, 0x9, + 0x22, 0x12, 0x44, 0x24, 0xbf, 0x79, 0x10, 0x92, + 0x21, 0x27, 0xfe, 0x48, 0x7, 0x90, 0x9, 0x3f, + 0xe0, + + /* U+7737 "眷" */ + 0x1, 0x0, 0x62, 0x20, 0x8, 0x83, 0xff, 0xe0, + 0x20, 0x0, 0x80, 0x3f, 0xff, 0x84, 0x10, 0x3f, + 0xf9, 0xc0, 0x1c, 0xff, 0xe1, 0x0, 0x43, 0xff, + 0x84, 0x1, 0xf, 0xfe, 0x10, 0x4, + + /* U+7738 "眸" */ + 0x0, 0x43, 0xc2, 0x9, 0x11, 0x25, 0xfe, 0xf0, + 0x6, 0x40, 0x9, 0x24, 0x24, 0xff, 0xf4, 0x42, + 0x51, 0x9, 0x4, 0x25, 0xff, 0xf0, 0x42, 0x41, + 0x0, 0x4, 0x0, + + /* U+773A "眺" */ + 0x0, 0xa1, 0xe1, 0x42, 0x42, 0x84, 0xa5, 0x2f, + 0x2a, 0x92, 0x14, 0x24, 0x28, 0x48, 0xd8, 0xf2, + 0xa9, 0x29, 0x4a, 0x46, 0x84, 0x89, 0x2f, 0x32, + 0x50, 0x44, 0x83, 0xf, 0x0, + + /* U+773C "眼" */ + 0xf3, 0xfa, 0x48, 0x29, 0x3f, 0xbc, 0x82, 0x92, + 0xa, 0x48, 0x29, 0x3f, 0x3c, 0x91, 0x92, 0x5a, + 0x49, 0x89, 0x22, 0x3c, 0x94, 0x92, 0x8a, 0x4c, + 0x10, + + /* U+7740 "着" */ + 0x8, 0x20, 0x8, 0x43, 0xff, 0xf8, 0x10, 0x7, + 0xff, 0xc0, 0x80, 0x3f, 0xff, 0x84, 0x0, 0x1f, + 0xf8, 0x60, 0x11, 0x7f, 0xe4, 0x80, 0x41, 0xff, + 0x82, 0x1, 0x7, 0xfe, 0x8, 0x4, + + /* U+7741 "睁" */ + 0x0, 0x3, 0xc4, 0x9, 0x3f, 0x24, 0x84, 0x9c, + 0x23, 0xdf, 0xe9, 0x4, 0xa4, 0x12, 0x9f, 0xff, + 0xc1, 0x29, 0x4, 0xa5, 0xfe, 0x90, 0x4b, 0xc1, + 0x9, 0x4, 0x0, 0xe0, + + /* U+7743 "睃" */ + 0x0, 0x41, 0xe1, 0x2, 0x44, 0x64, 0x90, 0x6f, + 0x1f, 0x12, 0x63, 0x25, 0x81, 0xc8, 0x80, 0xf1, + 0xf9, 0x26, 0x12, 0x54, 0x44, 0x85, 0xf, 0x6, + 0x10, 0x33, 0x23, 0x81, 0x0, + + /* U+7747 "睇" */ + 0x1, 0x11, 0xe1, 0x22, 0x4f, 0xf4, 0x82, 0x2f, + 0x4, 0x52, 0x7f, 0xa4, 0x90, 0x4a, 0x20, 0xf3, + 0xff, 0x20, 0x86, 0x43, 0xc, 0x8a, 0x2f, 0x65, + 0xd1, 0x8, 0x0, 0x10, 0x0, + + /* U+7750 "睐" */ + 0x0, 0x83, 0xc2, 0x9, 0x7f, 0xa4, 0x20, 0x94, + 0x8b, 0xca, 0x49, 0x28, 0x25, 0xff, 0xf0, 0xc2, + 0x47, 0x9, 0x1a, 0x24, 0xa4, 0xf4, 0x8a, 0x22, + 0x10, 0x8, 0x0, + + /* U+7751 "睑" */ + 0x0, 0x43, 0xc3, 0x9, 0x12, 0x24, 0x84, 0xf4, + 0xe, 0x4f, 0xc9, 0x0, 0x24, 0x20, 0xf4, 0x92, + 0x52, 0x49, 0x25, 0x24, 0x88, 0xf0, 0x22, 0x3f, + 0xf0, + + /* U+775A "睚" */ + 0xf7, 0xff, 0x28, 0x42, 0x57, 0xf7, 0xa1, 0x9, + 0x42, 0x12, 0x84, 0x25, 0x7f, 0xca, 0x0, 0xf4, + 0x21, 0x2b, 0xfa, 0x50, 0x87, 0xc1, 0x8, 0xbf, + 0xe1, 0x0, 0x0, + + /* U+775B "睛" */ + 0x0, 0x43, 0xdf, 0xf9, 0x4, 0x25, 0xff, 0xf0, + 0x42, 0x5f, 0xf9, 0x0, 0x24, 0xfe, 0xf2, 0xa, + 0x4f, 0xe9, 0x20, 0xa4, 0x82, 0xf3, 0xfa, 0x48, + 0x20, 0x23, 0x80, + + /* U+7761 "睡" */ + 0x0, 0xfd, 0xee, 0x82, 0x41, 0x4, 0x82, 0x9, + 0x7f, 0xde, 0x49, 0x24, 0x92, 0x4b, 0xff, 0x92, + 0x49, 0xe4, 0x92, 0x5f, 0xf4, 0x82, 0xf, 0x4, + 0x10, 0xff, 0x80, + + /* U+7762 "睢" */ + 0x1, 0x41, 0xe2, 0x42, 0x48, 0x84, 0x9f, 0xe9, + 0x62, 0x1f, 0xc4, 0x26, 0xff, 0x49, 0x10, 0xf2, + 0x21, 0x27, 0xfa, 0x48, 0x84, 0x91, 0xf, 0x22, + 0x12, 0x7f, 0xc0, 0x80, 0x0, + + /* U+7763 "督" */ + 0x10, 0x0, 0x3f, 0xf8, 0x41, 0x17, 0xfa, 0x40, + 0x83, 0x9, 0x4f, 0x22, 0x21, 0x9f, 0xfc, 0x20, + 0x8, 0x7f, 0xf0, 0x80, 0x21, 0xff, 0xc2, 0x0, + 0x87, 0xff, 0x8, 0x2, 0x0, + + /* U+7765 "睥" */ + 0x0, 0x40, 0x1, 0x3, 0xdf, 0xf4, 0xa2, 0x29, + 0x44, 0x5e, 0xff, 0xa5, 0x11, 0x4a, 0x22, 0x97, + 0xfd, 0xe1, 0x2, 0x44, 0x4, 0x91, 0x9, 0xff, + 0xfe, 0x4, 0x24, 0x8, 0x0, 0x10, + + /* U+7766 "睦" */ + 0x0, 0x1, 0xe0, 0x82, 0x4f, 0xf4, 0x82, 0x9, + 0x4, 0x1e, 0xff, 0xe4, 0x0, 0x49, 0x8e, 0xf0, + 0x41, 0x20, 0x82, 0x4f, 0xf4, 0x82, 0xf, 0x4, + 0x12, 0xff, 0xc0, + + /* U+7768 "睨" */ + 0x0, 0x81, 0xee, 0x7a, 0x50, 0x14, 0xa0, 0x29, + 0x40, 0x5e, 0xe7, 0xa5, 0x1, 0x4a, 0x2, 0xf7, + 0xfd, 0x21, 0x42, 0x42, 0x84, 0x8d, 0xf, 0x12, + 0x50, 0x44, 0x83, 0xf, 0x0, + + /* U+776B "睫" */ + 0x0, 0x41, 0xe0, 0x82, 0x7f, 0xf4, 0x82, 0x9, + 0x4, 0x12, 0xff, 0x3c, 0x12, 0x4f, 0xff, 0x90, + 0x49, 0xef, 0xf2, 0x41, 0x4, 0xa2, 0x9, 0x47, + 0xde, 0xc8, 0x22, 0x7f, 0x80, 0x0, + + /* U+776C "睬" */ + 0x0, 0x7, 0xdf, 0xf9, 0x0, 0x25, 0x22, 0x92, + 0x4b, 0xc9, 0x49, 0x4, 0x25, 0xff, 0xf0, 0x42, + 0x43, 0x89, 0x15, 0x24, 0x92, 0xf4, 0x4e, 0x41, + 0x0, 0x4, 0x0, + + /* U+7779 "睹" */ + 0x0, 0x81, 0xe1, 0xa, 0x4f, 0xd4, 0x84, 0x49, + 0x9, 0x1e, 0xff, 0xe4, 0x10, 0x48, 0x40, 0x93, + 0xfd, 0xfc, 0xa, 0x47, 0xf4, 0x90, 0x2f, 0x20, + 0x52, 0x3f, 0x80, 0x81, 0x0, + + /* U+777D "睽" */ + 0x0, 0x41, 0xef, 0x52, 0x42, 0xc4, 0xa9, 0x39, + 0x31, 0x9e, 0x41, 0x25, 0x7d, 0x48, 0x20, 0xf0, + 0x41, 0x2f, 0xfa, 0x41, 0x4, 0x87, 0xf, 0x1b, + 0x10, 0x61, 0x83, 0x1, 0x0, + + /* U+777E "睾" */ + 0x4, 0x0, 0xff, 0xf9, 0x11, 0x13, 0xff, 0xe0, + 0x0, 0x0, 0x20, 0xf, 0xfe, 0x0, 0x80, 0xff, + 0xfe, 0x10, 0x41, 0xff, 0xf0, 0x8, 0xf, 0xff, + 0xe0, 0x20, 0x0, 0x40, 0x0, + + /* U+777F "睿" */ + 0x1, 0x0, 0x1, 0xfc, 0x1, 0x0, 0x7f, 0xfe, + 0x40, 0x2, 0x4f, 0xf2, 0x33, 0xd8, 0x44, 0x24, + 0x1f, 0xf8, 0x70, 0xe, 0x1f, 0xf8, 0x10, 0x8, + 0x1f, 0xf8, 0x1f, 0xf8, 0x10, 0x8, + + /* U+7780 "瞀" */ + 0x0, 0x20, 0x7f, 0x20, 0x22, 0x7f, 0x1c, 0xe4, + 0x7f, 0x24, 0x15, 0x18, 0x65, 0x3c, 0x18, 0xc3, + 0x0, 0x0, 0x3f, 0xfc, 0x3f, 0xfc, 0x20, 0x4, + 0x3f, 0xfc, 0x20, 0x4, 0x3f, 0xfc, 0x20, 0x4, + + /* U+7784 "瞄" */ + 0x1, 0x11, 0xe2, 0x22, 0x7f, 0xfc, 0x88, 0x8f, + 0x0, 0x12, 0xff, 0xa5, 0x11, 0x4a, 0x22, 0xf4, + 0x45, 0x2f, 0xfa, 0x51, 0x14, 0xa2, 0x2f, 0x44, + 0x52, 0xff, 0x81, 0x1, 0x0, + + /* U+7785 "瞅" */ + 0x0, 0x11, 0xfe, 0x22, 0x48, 0x44, 0x92, 0xaf, + 0xfd, 0x52, 0x4a, 0xa4, 0x86, 0x4b, 0x98, 0xf6, + 0xb1, 0x34, 0x62, 0x68, 0xa4, 0x92, 0x4f, 0x24, + 0x52, 0x58, 0x80, 0xa0, 0x0, + + /* U+778C "瞌" */ + 0x0, 0x43, 0xdf, 0xe9, 0x4, 0x24, 0x10, 0x97, + 0xff, 0xc2, 0x9, 0x31, 0x25, 0xfe, 0xf0, 0x2, + 0x5f, 0xe9, 0x4a, 0xa5, 0x2a, 0xf4, 0xaa, 0x12, + 0xa0, 0xff, 0xc0, + + /* U+778D "瞍" */ + 0x0, 0x43, 0xdd, 0x79, 0x44, 0x65, 0x11, 0x97, + 0x5f, 0xd1, 0x19, 0x7f, 0xe4, 0x10, 0xf0, 0x42, + 0x5f, 0xe9, 0x20, 0xa4, 0x44, 0xf0, 0xe2, 0x6, + 0xc0, 0xe0, 0xc0, + + /* U+778E "瞎" */ + 0x0, 0x87, 0x84, 0x25, 0xff, 0x30, 0x19, 0x8, + 0x4b, 0xff, 0xc2, 0x12, 0xff, 0x90, 0x87, 0xbf, + 0xe4, 0x1, 0x2f, 0xf9, 0x40, 0xfa, 0x6, 0x5f, + 0xe0, 0x81, + + /* U+7791 "瞑" */ + 0xf7, 0xff, 0x28, 0x6, 0x4f, 0xf4, 0x90, 0x2f, + 0x20, 0x52, 0x7f, 0xa4, 0x81, 0x79, 0xfe, 0x90, + 0x41, 0x20, 0x82, 0x5e, 0xff, 0x80, 0x88, 0x30, + 0xc0, 0x80, 0x40, + + /* U+7792 "瞒" */ + 0x1, 0x11, 0xe2, 0x22, 0x5f, 0xfc, 0x88, 0x89, + 0x0, 0x1e, 0xff, 0xa4, 0x48, 0x4b, 0xfe, 0xf5, + 0x25, 0x2b, 0x4a, 0x57, 0x54, 0xb2, 0x6f, 0x64, + 0x52, 0x80, 0x81, 0xf, 0x0, + + /* U+779F "瞟" */ + 0xf7, 0xfd, 0x22, 0x42, 0x5f, 0xf4, 0xa9, 0x2f, + 0x52, 0x52, 0xff, 0xa4, 0x0, 0x79, 0xfc, 0x90, + 0x1, 0x2f, 0xfe, 0x41, 0x7, 0x92, 0xc8, 0xc4, + 0x40, 0x38, 0x0, + + /* U+77A0 "瞠" */ + 0x4, 0x8b, 0xca, 0x69, 0x8, 0x25, 0xff, 0x98, + 0x7, 0xcf, 0xd9, 0x21, 0x24, 0x84, 0xf3, 0xf2, + 0x42, 0x9, 0x8, 0x25, 0xfe, 0xf0, 0x82, 0x3f, + 0xf0, + + /* U+77A2 "瞢" */ + 0x8, 0x1, 0xff, 0xfc, 0x20, 0x3, 0xff, 0xc4, + 0x44, 0x8f, 0xff, 0x0, 0x0, 0x7f, 0xff, 0xbf, + 0xf2, 0x40, 0x20, 0xff, 0xc1, 0x0, 0x83, 0xff, + 0x7, 0xfe, 0x8, 0x4, 0x0, + + /* U+77A5 "瞥" */ + 0x10, 0x0, 0xa8, 0x80, 0x41, 0xf7, 0xf6, 0x49, + 0x2c, 0x9b, 0x45, 0x34, 0x84, 0x49, 0x76, 0x4, + 0x0, 0x7f, 0xf1, 0x0, 0x21, 0xff, 0xc4, 0x0, + 0x8f, 0xff, 0xf, 0xfe, 0x20, 0x4, + + /* U+77A7 "瞧" */ + 0x1, 0x23, 0xc8, 0x9, 0x7f, 0xe5, 0x8, 0x9c, + 0x23, 0xdf, 0xe9, 0x42, 0x25, 0xfe, 0xf4, 0x22, + 0x50, 0x89, 0x7f, 0xe4, 0x0, 0xf4, 0xaa, 0x22, + 0x50, 0x88, 0x0, + + /* U+77A9 "瞩" */ + 0xf7, 0xfd, 0x28, 0xa, 0x5f, 0xf4, 0xa0, 0xf, + 0x5f, 0x92, 0xbf, 0xa5, 0x49, 0x7a, 0xfe, 0x94, + 0x21, 0x2b, 0xfe, 0x58, 0x8f, 0x57, 0xd8, 0xa0, + 0x22, 0x43, 0x80, + + /* U+77AA "瞪" */ + 0x0, 0x1, 0xff, 0x52, 0x42, 0xc4, 0xa8, 0xa9, + 0x20, 0x9e, 0xbe, 0xa6, 0x0, 0xcb, 0xfe, 0xf4, + 0x5, 0x28, 0xa, 0x5f, 0xf4, 0x80, 0x4e, 0x19, + 0x11, 0xff, 0xc0, + + /* U+77AC "瞬" */ + 0x0, 0x1, 0xef, 0xfa, 0x42, 0x24, 0x94, 0x89, + 0x11, 0x1e, 0xff, 0xa5, 0x1, 0x49, 0x8, 0xf3, + 0xb9, 0x29, 0xa2, 0x6b, 0x44, 0x89, 0xff, 0x31, + 0x10, 0x42, 0x1, 0x4, 0x0, + + /* U+77B0 "瞰" */ + 0x0, 0x11, 0xef, 0x22, 0x42, 0x84, 0xbf, 0xef, + 0x4a, 0x52, 0x94, 0xa5, 0xf9, 0x4a, 0x52, 0xf4, + 0x99, 0x2f, 0x32, 0x52, 0x64, 0xa6, 0xcf, 0xfa, + 0x92, 0x14, 0x80, 0x30, 0x80, + + /* U+77B3 "瞳" */ + 0x0, 0x41, 0xef, 0xfa, 0x40, 0x24, 0x88, 0x89, + 0x7f, 0xfe, 0x0, 0x25, 0xff, 0x4a, 0x22, 0xf7, + 0xfd, 0x28, 0x8a, 0x4f, 0xe7, 0xbf, 0xe8, 0x4, + 0x10, 0x8, 0x3, 0xff, 0x80, + + /* U+77B5 "瞵" */ + 0x0, 0x41, 0xe4, 0x92, 0x45, 0x44, 0xbf, 0xf9, + 0xe, 0x1e, 0x2a, 0x24, 0x92, 0x4a, 0x23, 0xf3, + 0xbf, 0x29, 0x12, 0x6a, 0xa4, 0x8b, 0xff, 0x10, + 0x90, 0x41, 0x23, 0x2, 0x0, + + /* U+77BB "瞻" */ + 0x1, 0x3, 0xcf, 0xe9, 0x41, 0x27, 0xff, 0x94, + 0x93, 0xd4, 0xb9, 0x7f, 0xe5, 0x0, 0xf5, 0xfe, + 0x50, 0x9, 0x4f, 0xa5, 0x7f, 0xe9, 0x6, 0x27, + 0xf0, 0x90, 0x40, + + /* U+77BD "瞽" */ + 0x10, 0x11, 0xfc, 0x20, 0x43, 0xff, 0xf0, 0x80, + 0xf, 0xff, 0xc8, 0x9f, 0x8a, 0x12, 0x1c, 0xff, + 0xc6, 0x0, 0x0, 0xff, 0xe1, 0xff, 0xc2, 0x0, + 0x87, 0xff, 0xf, 0xfe, 0x10, 0x4, + + /* U+77BF "瞿" */ + 0x7e, 0x7d, 0xa, 0x17, 0xe7, 0xd0, 0xa1, 0x7e, + 0xfd, 0xf9, 0xf1, 0x8, 0xf, 0xff, 0x60, 0x82, + 0xff, 0xe2, 0x8, 0xf, 0xfe, 0x20, 0x80, 0xff, + 0xf2, 0x0, 0x0, + + /* U+77CD "矍" */ + 0x7c, 0xf9, 0x12, 0x27, 0xcf, 0x9f, 0x3e, 0x44, + 0x88, 0xe5, 0xc3, 0xff, 0xff, 0xfe, 0x3f, 0xf8, + 0x84, 0x3, 0xff, 0xdf, 0xfc, 0x18, 0x20, 0x1f, + 0xf, 0xcf, 0xc0, + + /* U+77D7 "矗" */ + 0x1, 0x0, 0xff, 0xf8, 0x7f, 0xc0, 0xff, 0x81, + 0xff, 0x3, 0xfe, 0x3f, 0xff, 0x88, 0x8, 0xfe, + 0xfe, 0xf9, 0xf9, 0xf3, 0xf3, 0xe7, 0xe7, 0xcf, + 0xc8, 0x90, 0xbf, 0xbf, 0x80, + + /* U+77DB "矛" */ + 0x3f, 0xfc, 0x0, 0x10, 0x10, 0xc0, 0x16, 0x0, + 0x18, 0xf, 0xff, 0xc0, 0x60, 0x81, 0x43, 0x4, + 0x84, 0x11, 0x0, 0xc2, 0x3, 0x4, 0x0, 0x8, + 0x0, 0xf0, 0x0, + + /* U+77DC "矜" */ + 0x0, 0x10, 0x7e, 0x10, 0x2, 0x28, 0x24, 0x64, + 0x18, 0x46, 0x8, 0x92, 0x7f, 0x10, 0x1a, 0x0, + 0x2a, 0xfe, 0x28, 0x2, 0x48, 0x4, 0x48, 0x4, + 0x8, 0x8, 0x8, 0x18, 0x38, 0x10, + + /* U+77E2 "矢" */ + 0x10, 0x0, 0x40, 0x3, 0xff, 0x88, 0x40, 0x41, + 0x1, 0x4, 0x0, 0x10, 0x3f, 0xff, 0x3, 0x0, + 0xc, 0x0, 0x48, 0x3, 0x30, 0x18, 0x21, 0x80, + 0x68, 0x0, 0x40, + + /* U+77E3 "矣" */ + 0x2, 0x0, 0x10, 0x0, 0x84, 0xc, 0xc, 0x7f, + 0xf8, 0x40, 0x1, 0xff, 0x88, 0x40, 0x42, 0x0, + 0x8, 0xf, 0xff, 0xc0, 0xc0, 0x4, 0x80, 0x21, + 0x87, 0x1, 0xe0, 0x0, + + /* U+77E5 "知" */ + 0x40, 0x1, 0x3, 0xf7, 0xe8, 0x64, 0x21, 0x90, + 0x84, 0x42, 0x11, 0x8, 0x7f, 0xa1, 0x10, 0x84, + 0x42, 0x11, 0x88, 0x49, 0x21, 0x62, 0xff, 0x2, + 0x18, 0x0, 0x0, + + /* U+77E7 "矧" */ + 0x40, 0x5, 0xf, 0x17, 0x84, 0x68, 0x11, 0xa3, + 0xc4, 0x88, 0x12, 0x20, 0x7e, 0x81, 0x23, 0xc4, + 0xc1, 0x13, 0x4, 0x52, 0x11, 0x48, 0x45, 0x1, + 0x18, 0x38, 0x40, + + /* U+77E9 "矩" */ + 0x40, 0x0, 0x83, 0xf9, 0xf4, 0x4, 0x88, 0x9, + 0x1f, 0xc2, 0x20, 0xbf, 0x41, 0x8, 0x82, 0x21, + 0x4, 0x63, 0xf8, 0xa4, 0x1, 0x28, 0x4, 0x50, + 0x10, 0x3f, 0xc0, + + /* U+77EB "矫" */ + 0x40, 0x0, 0x87, 0xf9, 0xe1, 0x5, 0x2, 0xa, + 0x7f, 0xc4, 0x14, 0x3e, 0x48, 0x10, 0x88, 0x26, + 0xc, 0x42, 0x20, 0xc4, 0x42, 0x88, 0x84, 0x91, + 0x10, 0x42, 0x21, 0x4, 0x0, + + /* U+77EC "矬" */ + 0x40, 0x20, 0x41, 0x24, 0x79, 0x24, 0xa1, 0x24, + 0xa3, 0x24, 0x23, 0x2c, 0x22, 0xaa, 0xfc, 0x72, + 0x20, 0x20, 0x33, 0xfe, 0x48, 0x20, 0x48, 0x20, + 0x40, 0x20, 0x87, 0xff, 0x80, 0x0, + + /* U+77ED "短" */ + 0x23, 0xfe, 0x80, 0x1, 0xf0, 0x2, 0x80, 0x9, + 0x1f, 0xd2, 0x20, 0x84, 0x41, 0x7e, 0x82, 0x11, + 0xfc, 0x20, 0x0, 0xa2, 0x21, 0x24, 0x84, 0x1, + 0x18, 0x7f, 0xc0, 0x0, 0x0, + + /* U+77EE "矮" */ + 0x40, 0x0, 0x83, 0xf9, 0xe0, 0x85, 0x1f, 0xfa, + 0x7, 0x4, 0x15, 0x8, 0x49, 0x7d, 0x11, 0x20, + 0x40, 0x4f, 0xfc, 0xc2, 0x21, 0xc8, 0x44, 0xcd, + 0x8, 0xf, 0x21, 0xe1, 0x80, + + /* U+77F3 "石" */ + 0x7f, 0xfe, 0x4, 0x0, 0x8, 0x0, 0x20, 0x0, + 0x80, 0x3, 0xff, 0x8e, 0x1, 0x34, 0x2, 0x48, + 0x4, 0x10, 0x8, 0x20, 0x10, 0x7f, 0xe0, 0x80, + 0x41, 0x0, 0x80, + + /* U+77F6 "矶" */ + 0x7c, 0xf8, 0x21, 0x10, 0x82, 0x21, 0x4, 0x43, + 0x88, 0x85, 0x11, 0x1a, 0x22, 0x14, 0x44, 0x28, + 0x88, 0x51, 0x14, 0xe4, 0x29, 0x8, 0x52, 0x20, + 0xe0, 0x0, 0x0, + + /* U+77F8 "矸" */ + 0x7e, 0xff, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x20, 0x10, 0x3c, 0x10, 0x65, 0xff, 0x64, 0x10, + 0x24, 0x10, 0x24, 0x10, 0x24, 0x10, 0x3c, 0x10, + 0x20, 0x10, 0x0, 0x10, + + /* U+77FD "矽" */ + 0x0, 0x20, 0x7e, 0x20, 0x10, 0x20, 0x10, 0x7f, + 0x10, 0x41, 0x20, 0x82, 0x3c, 0xc2, 0x65, 0x22, + 0x65, 0x14, 0x24, 0x4, 0x24, 0x8, 0x24, 0x10, + 0x3c, 0x20, 0x20, 0xc0, 0x1, 0x0, + + /* U+77FE "矾" */ + 0x7d, 0xf8, 0x11, 0x8, 0x21, 0x8, 0x21, 0x8, + 0x3d, 0x48, 0x25, 0x48, 0x65, 0x28, 0x65, 0x28, + 0x25, 0x8, 0x25, 0x8, 0x25, 0x9, 0x3e, 0x9, + 0x22, 0xa, 0x6, 0xe, + + /* U+77FF "矿" */ + 0x0, 0x0, 0x0, 0x10, 0x7e, 0x0, 0x10, 0xff, + 0x10, 0x80, 0x10, 0x80, 0x20, 0x80, 0x3c, 0x80, + 0x64, 0x80, 0x64, 0x80, 0x24, 0x80, 0x24, 0x80, + 0x25, 0x0, 0x3d, 0x0, 0x1, 0x0, 0x2, 0x0, + + /* U+7800 "砀" */ + 0x7d, 0xfe, 0x20, 0x18, 0x40, 0xc1, 0x2, 0x2, + 0x18, 0xf, 0xbf, 0xd9, 0x14, 0xd2, 0x49, 0x25, + 0x12, 0x4c, 0x24, 0x90, 0x89, 0xe2, 0x12, 0xc, + 0x60, 0x23, 0x80, + + /* U+7801 "码" */ + 0x1, 0xfb, 0xf0, 0x22, 0x10, 0x88, 0x42, 0x21, + 0x9, 0x4, 0x27, 0x90, 0xb2, 0x7f, 0x48, 0x5, + 0x20, 0x14, 0xbf, 0x52, 0x1, 0x78, 0x5, 0x20, + 0x10, 0x3, 0x80, + + /* U+7802 "砂" */ + 0x0, 0x21, 0xf8, 0x40, 0x80, 0x81, 0x9, 0x24, + 0x12, 0x4f, 0x24, 0x52, 0x88, 0x65, 0x12, 0x48, + 0x24, 0x90, 0x11, 0x20, 0x42, 0x41, 0x7, 0x8c, + 0x8, 0x70, 0x1, 0x80, 0x0, + + /* U+7809 "砉" */ + 0x1, 0x1, 0xff, 0xfc, 0x4, 0x3, 0xff, 0xe0, + 0x10, 0x1f, 0xff, 0xc0, 0x40, 0x0, 0x0, 0xff, + 0xfe, 0x10, 0x0, 0x7f, 0xe3, 0x80, 0x49, 0x0, + 0x83, 0xff, 0x4, 0x2, 0x0, + + /* U+780C "砌" */ + 0x1, 0x0, 0xfa, 0x7c, 0x44, 0x49, 0x8, 0x92, + 0x1d, 0x24, 0x62, 0x5e, 0x44, 0xb4, 0x89, 0xa9, + 0x12, 0x52, 0x24, 0xa6, 0xc9, 0x49, 0x13, 0x82, + 0x25, 0x8, 0x40, 0x37, 0x0, + + /* U+780D "砍" */ + 0x0, 0x0, 0xfc, 0x40, 0x20, 0x80, 0x20, 0xfe, + 0x20, 0x82, 0x41, 0x22, 0x79, 0x24, 0xc8, 0x20, + 0xc8, 0x20, 0x48, 0x60, 0x48, 0x50, 0x48, 0x50, + 0x78, 0x88, 0x41, 0x4, 0x6, 0x2, 0x4, 0x0, + + /* U+7811 "砑" */ + 0x7d, 0xfe, 0x20, 0x10, 0x42, 0x21, 0x4, 0x43, + 0xd0, 0x84, 0xbf, 0xd9, 0x6, 0x12, 0x14, 0x24, + 0x28, 0x48, 0x90, 0x92, 0x21, 0xe8, 0x42, 0x60, + 0x80, 0x7, 0x0, + + /* U+7812 "砒" */ + 0x1, 0x10, 0x7d, 0x10, 0x11, 0x10, 0x21, 0x11, + 0x21, 0x12, 0x21, 0xd4, 0x7d, 0x18, 0x65, 0x10, + 0xa5, 0x10, 0x25, 0x10, 0x25, 0x11, 0x3d, 0x11, + 0x25, 0x51, 0x25, 0x93, 0x1, 0x1e, + + /* U+7814 "研" */ + 0xfb, 0xfc, 0x42, 0x20, 0x84, 0x42, 0x8, 0x84, + 0x11, 0xe, 0x22, 0x35, 0xff, 0xe8, 0x88, 0x51, + 0x10, 0xa2, 0x21, 0x44, 0x43, 0x90, 0x84, 0x61, + 0x1, 0x82, 0x0, + + /* U+7816 "砖" */ + 0x0, 0x20, 0x7c, 0x40, 0x11, 0xfe, 0x10, 0x40, + 0x20, 0x40, 0x23, 0xff, 0x38, 0x80, 0x68, 0x80, + 0xa8, 0xfe, 0x28, 0x4, 0x28, 0xc, 0x28, 0xd8, + 0x38, 0x70, 0x28, 0x18, 0x0, 0xc, + + /* U+7817 "砗" */ + 0x0, 0x20, 0x7c, 0x40, 0x11, 0xff, 0x20, 0x40, + 0x20, 0x90, 0x20, 0x90, 0x3d, 0x10, 0x65, 0xfe, + 0xa4, 0x10, 0x24, 0x10, 0x24, 0x10, 0x25, 0xfe, + 0x3c, 0x10, 0x24, 0x10, 0x0, 0x10, + + /* U+7818 "砘" */ + 0x0, 0x20, 0x7c, 0x20, 0x13, 0xff, 0x10, 0x20, + 0x21, 0x22, 0x21, 0x22, 0x3d, 0x22, 0x65, 0x22, + 0x65, 0x22, 0x25, 0xfe, 0x24, 0x20, 0x24, 0x21, + 0x3c, 0x21, 0x24, 0x21, 0x0, 0x3e, + + /* U+781A "砚" */ + 0x7d, 0xfc, 0x11, 0x4, 0x21, 0x14, 0x21, 0x14, + 0x39, 0x14, 0x29, 0x14, 0x69, 0x34, 0xa9, 0x34, + 0x29, 0x34, 0x28, 0x30, 0x28, 0x71, 0x38, 0xd1, + 0x21, 0x93, 0x6, 0x1e, + + /* U+781C "砜" */ + 0xfb, 0xf8, 0x44, 0x11, 0xc, 0xa2, 0x19, 0x47, + 0x3a, 0x8a, 0x59, 0x34, 0x92, 0x29, 0x24, 0x52, + 0xc8, 0xa9, 0x55, 0xd4, 0xaa, 0x31, 0x54, 0x40, + 0xa1, 0x0, 0x80, + + /* U+781D "砝" */ + 0x0, 0x43, 0xe1, 0x2, 0x4, 0x8, 0xff, 0x40, + 0x41, 0x1, 0x7, 0x84, 0x33, 0xff, 0xc8, 0xc1, + 0x22, 0x4, 0x91, 0x12, 0x44, 0x7a, 0x9, 0x1f, + 0xf0, 0x0, 0x40, + + /* U+781F "砟" */ + 0x0, 0x0, 0x0, 0x40, 0x7e, 0x40, 0x10, 0xff, + 0x10, 0xa0, 0x31, 0x20, 0x3c, 0x20, 0x24, 0x3f, + 0x64, 0x20, 0xa4, 0x20, 0x24, 0x20, 0x24, 0x3f, + 0x24, 0x20, 0x3c, 0x20, 0x20, 0x20, 0x0, 0x20, + + /* U+7823 "砣" */ + 0x0, 0x20, 0xf8, 0x20, 0x47, 0xf9, 0x8, 0x12, + 0x10, 0x24, 0x0, 0xf, 0x21, 0x32, 0x46, 0x64, + 0x90, 0x49, 0xc0, 0x92, 0x1, 0x24, 0x13, 0xc8, + 0x24, 0x90, 0xc8, 0x3f, 0x0, + + /* U+7825 "砥" */ + 0x0, 0x1d, 0xf7, 0xc0, 0x88, 0x81, 0x11, 0x4, + 0x22, 0x8, 0x44, 0x1e, 0xff, 0xe5, 0x10, 0xca, + 0x20, 0x94, 0x41, 0x28, 0x82, 0x52, 0x97, 0xb9, + 0x28, 0x4b, 0xc0, 0xb, 0x0, + + /* U+7826 "砦" */ + 0x8, 0x81, 0x22, 0x34, 0xeb, 0x12, 0x31, 0x48, + 0x85, 0x3a, 0x1f, 0xf, 0x80, 0x0, 0xff, 0xfc, + 0x20, 0x1, 0xff, 0x18, 0x4, 0xa0, 0x10, 0x7f, + 0xc2, 0x1, 0x0, + + /* U+7827 "砧" */ + 0x0, 0x10, 0x0, 0x10, 0x7e, 0x10, 0x10, 0x10, + 0x10, 0x1f, 0x20, 0x10, 0x20, 0x10, 0x3c, 0x10, + 0x64, 0xfe, 0xa4, 0x82, 0x24, 0x82, 0x24, 0x82, + 0x24, 0x82, 0x3c, 0x82, 0x24, 0xfe, 0x0, 0x82, + + /* U+7829 "砩" */ + 0x0, 0x90, 0xef, 0xf8, 0x82, 0x52, 0x4, 0xa4, + 0x9, 0x4e, 0xff, 0xb5, 0x24, 0x6a, 0x48, 0x57, + 0xfe, 0xa1, 0x25, 0x44, 0x53, 0x88, 0xe4, 0x21, + 0x0, 0x82, 0x0, + + /* U+782C "砬" */ + 0x0, 0x20, 0x0, 0x10, 0x7e, 0x10, 0x11, 0xff, + 0x10, 0x0, 0x20, 0x0, 0x3c, 0x84, 0x24, 0x84, + 0x64, 0x44, 0xa4, 0x44, 0x24, 0x48, 0x24, 0x48, + 0x24, 0x8, 0x38, 0x10, 0x21, 0xff, + + /* U+782D "砭" */ + 0x0, 0x2, 0x7d, 0xfa, 0x10, 0x0, 0x10, 0x20, + 0x20, 0x10, 0x20, 0x0, 0x3d, 0xfc, 0x64, 0x4, + 0x64, 0x8, 0x24, 0x10, 0x24, 0x20, 0x24, 0x40, + 0x3c, 0x80, 0x21, 0xc0, 0x22, 0x7f, + + /* U+7830 "砰" */ + 0x7d, 0xfe, 0x20, 0x40, 0x84, 0x89, 0x9, 0x23, + 0xca, 0xcc, 0x84, 0x19, 0x8, 0x53, 0xff, 0x24, + 0x20, 0x48, 0x40, 0xf0, 0x81, 0x21, 0x0, 0x2, + 0x0, 0x4, 0x0, + + /* U+7834 "破" */ + 0x0, 0x10, 0x7c, 0x10, 0x11, 0xff, 0x21, 0x12, + 0x21, 0x12, 0x21, 0x10, 0x3d, 0xfe, 0x65, 0x42, + 0xa5, 0x44, 0x25, 0x24, 0x25, 0x38, 0x3e, 0x18, + 0x22, 0x6c, 0x6, 0xc2, 0x5, 0x0, + + /* U+7837 "砷" */ + 0x0, 0x20, 0xf8, 0x40, 0x47, 0xf9, 0x9, 0x12, + 0x12, 0x24, 0x24, 0x5e, 0x7f, 0xb4, 0x91, 0xa9, + 0x22, 0x52, 0x44, 0xa7, 0xf9, 0xc1, 0x2, 0x82, + 0x4, 0x4, 0x0, 0x8, 0x0, + + /* U+7838 "砸" */ + 0x7d, 0xfe, 0x22, 0x10, 0x84, 0x21, 0xb, 0xf3, + 0xd4, 0xac, 0xa9, 0x59, 0x52, 0xd2, 0xa5, 0x25, + 0x4a, 0x4a, 0x9c, 0xf4, 0x21, 0x28, 0x0, 0x1f, + 0xe0, + + /* U+7839 "砹" */ + 0x0, 0x88, 0x7c, 0x88, 0x13, 0xff, 0x10, 0x88, + 0x20, 0x0, 0x21, 0x4, 0x3d, 0x8, 0x64, 0x88, + 0x64, 0x88, 0x24, 0x50, 0x24, 0x60, 0x24, 0x60, + 0x3c, 0x98, 0x23, 0x6, 0x0, 0x0, + + /* U+783A "砺" */ + 0x3, 0xff, 0xf4, 0x0, 0x88, 0x1, 0x10, 0x4, + 0x2f, 0xe8, 0x48, 0x1c, 0x90, 0x69, 0x3e, 0xd2, + 0x44, 0xa4, 0x89, 0x49, 0x12, 0x94, 0x27, 0x48, + 0x40, 0x90, 0x82, 0x4e, 0x0, + + /* U+783B "砻" */ + 0x4, 0xc0, 0x10, 0x8f, 0xff, 0xc2, 0x40, 0x9, + 0x74, 0xde, 0x14, 0x8f, 0xa0, 0x0, 0xff, 0xfc, + 0x20, 0x3, 0xff, 0xb4, 0x2, 0x10, 0x8, 0x7f, + 0xe1, 0x0, 0x80, + + /* U+783C "砼" */ + 0x0, 0x0, 0x0, 0x43, 0xf1, 0x41, 0x2, 0x82, + 0x8, 0x84, 0x20, 0x90, 0x80, 0xbd, 0x0, 0xc8, + 0x1, 0x93, 0xf9, 0x20, 0x82, 0x41, 0x4, 0x82, + 0xf, 0x4, 0x12, 0xff, 0xa0, 0x0, + + /* U+783E "砾" */ + 0x0, 0x1, 0xf7, 0xf8, 0x88, 0x2, 0x11, 0x4, + 0x22, 0x8, 0x44, 0x1c, 0x88, 0x69, 0xfe, 0xd0, + 0x20, 0xa2, 0x51, 0x44, 0xa2, 0x91, 0x27, 0x42, + 0x48, 0x84, 0x0, 0x78, 0x0, + + /* U+7840 "础" */ + 0x0, 0x10, 0xf8, 0x20, 0x44, 0x49, 0x8, 0x92, + 0x11, 0x24, 0x22, 0x4f, 0x7f, 0xb2, 0x8, 0x64, + 0x10, 0x4a, 0x24, 0x94, 0x49, 0x28, 0x93, 0xd1, + 0x24, 0x3f, 0xc0, 0x0, 0x80, + + /* U+7845 "硅" */ + 0x0, 0x0, 0x7c, 0x20, 0x21, 0xfe, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x7b, 0xff, 0x68, 0x0, + 0x28, 0x20, 0x29, 0xfe, 0x28, 0x20, 0x28, 0x20, + 0x38, 0x20, 0x23, 0xff, + + /* U+7847 "硇" */ + 0x0, 0x20, 0x0, 0x81, 0xf7, 0xf8, 0x90, 0x12, + 0x28, 0xa4, 0x4a, 0x4e, 0x94, 0xb5, 0x11, 0x6a, + 0x32, 0x54, 0xa4, 0xa9, 0x29, 0x54, 0x53, 0xa0, + 0x24, 0x3f, 0xc0, 0x80, 0x80, + + /* U+784C "硌" */ + 0x0, 0x0, 0x1, 0x1, 0xf3, 0xf0, 0x8c, 0x22, + 0x18, 0x84, 0x4b, 0x8, 0xc, 0x1c, 0x28, 0x69, + 0x8c, 0xd4, 0x4, 0xa7, 0xf1, 0x48, 0x22, 0x90, + 0x47, 0x20, 0x8a, 0x7f, 0x10, 0x82, + + /* U+784E "硎" */ + 0xf7, 0xc5, 0xa, 0x54, 0x29, 0x50, 0xa5, 0x42, + 0x95, 0xca, 0x5d, 0x7d, 0x74, 0xa5, 0x52, 0x95, + 0x4a, 0x55, 0x28, 0x5d, 0x21, 0x44, 0x84, 0x22, + 0x70, + + /* U+7850 "硐" */ + 0x7d, 0xff, 0x11, 0x1, 0x11, 0x7d, 0x21, 0x1, + 0x39, 0x1, 0x29, 0x79, 0x69, 0x49, 0xa9, 0x49, + 0x29, 0x49, 0x29, 0x49, 0x29, 0x79, 0x39, 0x1, + 0x21, 0x1, 0x1, 0x1e, + + /* U+7852 "硒" */ + 0x7d, 0xff, 0x10, 0x48, 0x10, 0x48, 0x21, 0xfe, + 0x21, 0x4a, 0x7d, 0x4a, 0x65, 0x4a, 0xa5, 0x8a, + 0x25, 0x8e, 0x25, 0x2, 0x25, 0x2, 0x3d, 0x2, + 0x25, 0xfe, 0x21, 0x2, + + /* U+7855 "硕" */ + 0x7d, 0xff, 0x10, 0x10, 0x20, 0x20, 0x21, 0xde, + 0x3d, 0x2, 0x65, 0x12, 0x65, 0x12, 0x25, 0x12, + 0x25, 0x12, 0x25, 0x12, 0x25, 0x2a, 0x3c, 0x44, + 0x20, 0x82, 0x3, 0x1, + + /* U+7856 "硖" */ + 0x0, 0x20, 0xf8, 0x40, 0x47, 0xf8, 0x81, 0x2, + 0x2, 0x4, 0x24, 0x8f, 0x2a, 0x32, 0x10, 0x67, + 0xfe, 0x48, 0xc0, 0x91, 0x41, 0x24, 0x83, 0xc8, + 0x84, 0x20, 0x89, 0x80, 0x80, + + /* U+7857 "硗" */ + 0x0, 0x80, 0x1, 0x1, 0xf2, 0x30, 0x9f, 0x81, + 0x4, 0x44, 0xb, 0x8, 0x8, 0x9e, 0xe9, 0x64, + 0xc, 0xc8, 0x0, 0x9f, 0xf9, 0x24, 0x82, 0x49, + 0x7, 0x92, 0x48, 0x44, 0x83, 0xf, + + /* U+785D "硝" */ + 0x1, 0x12, 0xfb, 0x24, 0x42, 0x51, 0x0, 0x82, + 0x1f, 0xe4, 0x20, 0x4f, 0x40, 0xb2, 0xff, 0xa5, + 0x2, 0x4a, 0x4, 0x97, 0xf9, 0x28, 0x13, 0xd0, + 0x24, 0x20, 0x40, 0x43, 0x80, + + /* U+786A "硪" */ + 0x0, 0x11, 0xf7, 0xa8, 0x84, 0x52, 0x8, 0x84, + 0x7f, 0xef, 0x22, 0x12, 0x44, 0x64, 0x89, 0xc9, + 0x54, 0x97, 0x29, 0x34, 0x62, 0x48, 0x47, 0x93, + 0xa8, 0x29, 0x41, 0xc1, 0x80, + + /* U+786B "硫" */ + 0x0, 0x20, 0x7c, 0x10, 0x13, 0xff, 0x10, 0x60, + 0x20, 0x8c, 0x21, 0x4, 0x3f, 0xfe, 0x64, 0x1, + 0x64, 0x0, 0x25, 0x24, 0x25, 0x24, 0x25, 0x24, + 0x3d, 0x25, 0x22, 0x25, 0x6, 0x27, + + /* U+786C "硬" */ + 0xfb, 0xfe, 0x40, 0x40, 0x8f, 0xf2, 0x11, 0x27, + 0xa2, 0x49, 0x7f, 0xb2, 0x89, 0x65, 0x12, 0x4b, + 0xfc, 0x94, 0x41, 0x25, 0x3, 0xc6, 0x4, 0x1e, + 0x1, 0xc3, 0x80, + + /* U+786D "硭" */ + 0x0, 0x89, 0xf1, 0x10, 0x8f, 0xf9, 0x4, 0x44, + 0x8, 0x88, 0x4, 0x1c, 0x0, 0x6b, 0xff, 0xd1, + 0x0, 0xa2, 0x1, 0x44, 0x2, 0x88, 0x7, 0x10, + 0xa, 0x20, 0x10, 0x7f, 0x0, + + /* U+786E "确" */ + 0x0, 0x40, 0x0, 0x81, 0xf3, 0xf0, 0x88, 0x42, + 0x11, 0x4, 0x7f, 0xc8, 0x44, 0x9c, 0x89, 0x69, + 0xff, 0x52, 0x24, 0xa4, 0x49, 0x48, 0x92, 0x9f, + 0xe7, 0x22, 0x48, 0x84, 0x81, 0x7, + + /* U+7877 "硷" */ + 0x0, 0x20, 0xf8, 0xc0, 0x42, 0x41, 0xc, 0x42, + 0x30, 0x47, 0x3f, 0x5a, 0x0, 0x34, 0x22, 0x2a, + 0x48, 0x52, 0x90, 0xa4, 0xa1, 0x48, 0x83, 0x81, + 0x4, 0x7f, 0xc0, + + /* U+787C "硼" */ + 0x7b, 0xde, 0x44, 0xa4, 0x89, 0x49, 0x12, 0x92, + 0x3d, 0xef, 0x4a, 0x5a, 0x94, 0x95, 0xef, 0x2a, + 0x52, 0x54, 0xa4, 0xa9, 0x49, 0xf2, 0x92, 0x46, + 0x20, 0xbd, 0xc0, + + /* U+7887 "碇" */ + 0x0, 0x10, 0x7c, 0x10, 0x11, 0xfe, 0x11, 0x2, + 0x21, 0x2, 0x20, 0x0, 0x39, 0xfe, 0x68, 0x10, + 0x68, 0x90, 0x28, 0x9f, 0x29, 0x10, 0x29, 0x90, + 0x39, 0x90, 0x22, 0x70, 0x4, 0xe, + + /* U+7889 "碉" */ + 0x3, 0xff, 0xe8, 0x12, 0x20, 0x50, 0xbd, 0x42, + 0x45, 0xe9, 0x14, 0xaf, 0xf2, 0x81, 0xca, 0xf5, + 0x2a, 0x54, 0xa9, 0x5e, 0xbd, 0x42, 0x4, 0x10, + 0x10, 0xc3, 0xc0, + + /* U+788C "碌" */ + 0x7d, 0xfc, 0x10, 0x4, 0x21, 0xfc, 0x20, 0x4, + 0x20, 0x4, 0x3b, 0xff, 0x68, 0x20, 0x69, 0x22, + 0x28, 0xbc, 0x28, 0x68, 0x28, 0xa8, 0x3b, 0x26, + 0x28, 0x20, 0x0, 0xe0, + + /* U+788D "碍" */ + 0x1, 0xfe, 0x7d, 0x2, 0x11, 0x2, 0x21, 0xfe, + 0x21, 0x2, 0x39, 0xfe, 0x28, 0x0, 0x6b, 0xff, + 0xa8, 0x4, 0x28, 0x4, 0x2b, 0xff, 0x28, 0x84, + 0x38, 0x44, 0x20, 0x4, 0x0, 0x3c, + + /* U+788E "碎" */ + 0x0, 0x20, 0xf8, 0x40, 0x47, 0xf9, 0x4, 0x2, + 0x8, 0x87, 0x91, 0x89, 0x55, 0x33, 0x29, 0x66, + 0x2, 0x48, 0x40, 0x9f, 0xf9, 0x21, 0x3, 0xc2, + 0x4, 0x4, 0x0, 0x8, 0x0, + + /* U+7891 "碑" */ + 0x0, 0x20, 0x0, 0x40, 0x7d, 0xfe, 0x11, 0x22, + 0x21, 0x22, 0x21, 0xfe, 0x39, 0x22, 0x29, 0x22, + 0x69, 0x22, 0xa9, 0xfe, 0x28, 0x40, 0x28, 0x90, + 0x2b, 0xff, 0x38, 0x10, 0x20, 0x10, 0x0, 0x10, + + /* U+7893 "碓" */ + 0x0, 0x90, 0xf9, 0x20, 0x44, 0x0, 0x8f, 0xf2, + 0x31, 0x4, 0x62, 0x1e, 0x7f, 0xb4, 0x88, 0x29, + 0x10, 0x53, 0xfc, 0xa4, 0x41, 0x48, 0x83, 0x91, + 0x5, 0x3f, 0xc0, 0x40, 0x0, + + /* U+7897 "碗" */ + 0x0, 0x10, 0x0, 0x10, 0x7d, 0xfe, 0x11, 0x2, + 0x21, 0x80, 0x20, 0xee, 0x3d, 0x32, 0x65, 0x32, + 0x65, 0x32, 0x26, 0xb2, 0x24, 0x56, 0x24, 0x51, + 0x3c, 0x91, 0x21, 0x92, 0x3, 0xe, + + /* U+7898 "碘" */ + 0x0, 0x48, 0x7c, 0x48, 0x11, 0xfe, 0x22, 0x4a, + 0x22, 0x4a, 0x22, 0x4a, 0x79, 0xfe, 0x6a, 0x4a, + 0xaa, 0x4a, 0x2a, 0x4a, 0x2b, 0xff, 0x28, 0x0, + 0x38, 0x4, 0x21, 0x2, 0x2, 0x0, + + /* U+789A "碚" */ + 0x0, 0x20, 0x7c, 0x10, 0x11, 0xff, 0x20, 0x0, + 0x20, 0x84, 0x20, 0x8, 0x3f, 0xff, 0x64, 0x0, + 0x64, 0x0, 0x25, 0xfe, 0x25, 0x2, 0x25, 0x2, + 0x3d, 0x2, 0x25, 0xfe, 0x1, 0x2, + + /* U+789B "碛" */ + 0x0, 0x20, 0x7d, 0xff, 0x10, 0x20, 0x21, 0xfe, + 0x20, 0x20, 0x3f, 0xff, 0x24, 0x0, 0x65, 0xfe, + 0xa5, 0x2, 0x25, 0x22, 0x25, 0x22, 0x25, 0x22, + 0x3c, 0x58, 0x20, 0x86, 0x3, 0x1, + + /* U+789C "碜" */ + 0x0, 0x41, 0xf1, 0x20, 0x8c, 0x22, 0x1f, 0xe4, + 0x4, 0x8, 0x10, 0x1c, 0xff, 0x68, 0x8c, 0xd6, + 0x46, 0xa6, 0x21, 0x41, 0x92, 0x9c, 0x47, 0x3, + 0x8, 0x18, 0x10, 0xc0, 0x2, 0x0, + + /* U+789F "碟" */ + 0x0, 0x48, 0x4, 0x93, 0xe9, 0x21, 0x3f, 0xe4, + 0x24, 0x88, 0x4f, 0x1e, 0x80, 0x25, 0x0, 0xcb, + 0xfc, 0x90, 0x41, 0x3f, 0xf2, 0x43, 0x4, 0x8b, + 0xf, 0x25, 0x1, 0x89, 0x0, 0x10, + + /* U+78A1 "碡" */ + 0x0, 0x20, 0x7d, 0xfe, 0x10, 0x20, 0x21, 0xfe, + 0x20, 0x20, 0x23, 0xff, 0x38, 0x0, 0x69, 0xfc, + 0x69, 0x24, 0x2b, 0xff, 0x29, 0x24, 0x29, 0x4, + 0x39, 0xff, 0x20, 0x4, 0x0, 0x3c, + + /* U+78A3 "碣" */ + 0x1, 0xfe, 0x7d, 0x2, 0x11, 0x2, 0x21, 0xfe, + 0x21, 0x2, 0x21, 0xfe, 0x38, 0x80, 0x69, 0xff, + 0xaa, 0x1, 0x29, 0x31, 0x29, 0x49, 0x29, 0x1, + 0x39, 0xfa, 0x20, 0x2, 0x0, 0x1e, + + /* U+78A5 "碥" */ + 0x0, 0x40, 0x1, 0xf, 0xbf, 0xc9, 0x1, 0x44, + 0x5, 0xf, 0xf4, 0x40, 0x1d, 0x0, 0xd5, 0xff, + 0x55, 0x55, 0x55, 0x55, 0x7f, 0x55, 0x55, 0xd5, + 0x54, 0x95, 0x42, 0x43, + + /* U+78A7 "碧" */ + 0x0, 0x41, 0xfb, 0xf8, 0x84, 0x11, 0x8, 0x27, + 0x9f, 0xc4, 0x20, 0xbf, 0x7f, 0x0, 0x0, 0xff, + 0xfe, 0x10, 0x0, 0x40, 0x3, 0xff, 0xce, 0x0, + 0x83, 0xff, 0x8, 0x2, 0x0, + + /* U+78B0 "碰" */ + 0x1, 0x0, 0x2, 0x13, 0xe2, 0x41, 0x3f, 0xf4, + 0x9, 0x8, 0x12, 0x1d, 0x24, 0xea, 0x4a, 0x52, + 0x94, 0xa5, 0x29, 0x42, 0x42, 0x84, 0x87, 0x9, + 0x8, 0x12, 0x11, 0xff, 0x80, + + /* U+78B1 "碱" */ + 0x0, 0x8, 0x0, 0xa, 0x7c, 0x8, 0x11, 0xff, + 0x22, 0x8, 0x22, 0x8, 0x22, 0xf8, 0x7a, 0xa, + 0x6a, 0xfa, 0xaa, 0x9c, 0x2a, 0x94, 0x2a, 0x94, + 0x3a, 0xf5, 0x22, 0xd, 0x4, 0x15, 0x4, 0x22, + + /* U+78B2 "碲" */ + 0x0, 0x10, 0x7c, 0x10, 0x11, 0xff, 0x20, 0x84, + 0x20, 0x48, 0x3d, 0xff, 0x26, 0x1, 0x64, 0x11, + 0xa5, 0xfe, 0x25, 0x12, 0x25, 0x12, 0x25, 0x12, + 0x3d, 0x12, 0x25, 0x16, 0x0, 0x10, + + /* U+78B3 "碳" */ + 0x0, 0x20, 0x1, 0x22, 0x7d, 0x22, 0x11, 0xfe, + 0x20, 0x0, 0x20, 0x80, 0x3f, 0xff, 0x65, 0x10, + 0xe5, 0x12, 0x25, 0x94, 0x26, 0x30, 0x26, 0x28, + 0x3c, 0x48, 0x20, 0x86, 0x21, 0x1, + + /* U+78B4 "碴" */ + 0x0, 0x10, 0x7c, 0x10, 0x11, 0xff, 0x20, 0x38, + 0x20, 0xd6, 0x3f, 0x10, 0x25, 0xfe, 0x65, 0x2, + 0x65, 0x2, 0x25, 0xfe, 0x25, 0x2, 0x25, 0xfe, + 0x3c, 0x0, 0x23, 0xff, + + /* U+78B9 "碹" */ + 0x0, 0x20, 0x0, 0x20, 0x7d, 0xff, 0x12, 0x1, + 0x20, 0x0, 0x21, 0xfe, 0x3c, 0x0, 0x65, 0xfe, + 0x65, 0x2, 0x25, 0xfe, 0x25, 0x2, 0x25, 0x2, + 0x25, 0xfe, 0x3c, 0x0, 0x23, 0xff, + + /* U+78BE "碾" */ + 0xff, 0xfc, 0x48, 0x9, 0xf, 0xf2, 0x20, 0x4, + 0x49, 0xe, 0xbf, 0xb5, 0x24, 0x6a, 0x48, 0x53, + 0xfe, 0xaa, 0x49, 0x54, 0xe3, 0xa8, 0x80, 0x56, + 0xc1, 0x10, 0x0, + + /* U+78C1 "磁" */ + 0x0, 0x84, 0x0, 0x84, 0x7c, 0x48, 0x13, 0xff, + 0x20, 0x84, 0x21, 0x8, 0x39, 0x32, 0x6a, 0x52, + 0x6b, 0xbc, 0x28, 0x88, 0x29, 0x8, 0x29, 0x54, + 0x3a, 0x52, 0x23, 0xfe, 0x0, 0x21, + + /* U+78C5 "磅" */ + 0x0, 0x20, 0x7c, 0x20, 0x11, 0xfe, 0x10, 0x84, + 0x20, 0x18, 0x3e, 0xee, 0x24, 0x22, 0x64, 0x20, + 0x67, 0xff, 0x24, 0x80, 0x24, 0xfc, 0x24, 0x84, + 0x3d, 0x4, 0x23, 0x4, 0x0, 0x38, + + /* U+78C9 "磉" */ + 0x1, 0xfe, 0x7c, 0x4, 0x10, 0xf8, 0x21, 0xcc, + 0x23, 0xde, 0x38, 0x42, 0x2a, 0x52, 0x69, 0x8c, + 0x69, 0xda, 0x2a, 0x40, 0x2b, 0xff, 0x28, 0x70, + 0x39, 0xac, 0x6, 0x23, 0x0, 0x20, + + /* U+78CA "磊" */ + 0xff, 0xfe, 0x8, 0x0, 0x7f, 0xe3, 0x80, 0x41, + 0xff, 0x80, 0x0, 0x3f, 0xbf, 0x88, 0x10, 0x20, + 0x40, 0xfd, 0xfb, 0xf, 0x12, 0x12, 0x23, 0xe7, + 0xc8, 0x48, 0x80, + + /* U+78CB "磋" */ + 0x0, 0x89, 0xf0, 0x90, 0x8f, 0xf9, 0x2, 0x4, + 0x4, 0x8, 0x3f, 0x9e, 0x10, 0x67, 0xff, 0xc8, + 0x80, 0x91, 0x1, 0x25, 0xf2, 0x51, 0x7, 0xc2, + 0x9, 0x3f, 0xc0, + + /* U+78D0 "磐" */ + 0x10, 0x0, 0xfd, 0xf1, 0x4a, 0x22, 0x50, 0x2f, + 0xff, 0xca, 0x51, 0x92, 0x9c, 0x42, 0x67, 0x9, + 0x1, 0xff, 0xfc, 0x20, 0x1, 0xff, 0xcd, 0x0, + 0x83, 0xff, 0x4, 0x2, 0x0, + + /* U+78D4 "磔" */ + 0x0, 0x84, 0x1, 0x4, 0x7d, 0xff, 0x22, 0x54, + 0x23, 0x54, 0x20, 0x54, 0x38, 0x9f, 0x69, 0x4, + 0x68, 0x24, 0x2b, 0xff, 0x28, 0x70, 0x28, 0xa8, + 0x39, 0x24, 0x22, 0x23, 0x0, 0x20, + + /* U+78D5 "磕" */ + 0x0, 0x20, 0x7d, 0xfe, 0x10, 0x20, 0x20, 0x20, + 0x23, 0xff, 0x20, 0x40, 0x79, 0x88, 0x6b, 0xfe, + 0xa8, 0x0, 0x29, 0xfe, 0x2a, 0x52, 0x2a, 0x52, + 0x3a, 0x52, 0x22, 0x52, 0x7, 0xff, + + /* U+78D9 "磙" */ + 0x0, 0x20, 0x7c, 0x20, 0x11, 0xfe, 0x20, 0x8, + 0x21, 0x4, 0x3a, 0x42, 0x28, 0x88, 0x69, 0xfc, + 0xa8, 0x20, 0x28, 0x62, 0x29, 0x94, 0x2a, 0x88, + 0x38, 0x84, 0x20, 0xe3, 0x0, 0x80, + + /* U+78E8 "磨" */ + 0x1, 0x0, 0x1, 0x1, 0xff, 0xfa, 0x0, 0x4, + 0x41, 0xb, 0xef, 0x91, 0x4, 0x2f, 0xbe, 0x40, + 0x80, 0xbf, 0xf9, 0x8, 0x2, 0x20, 0x4, 0xff, + 0x97, 0x1, 0x23, 0xfe, 0x4, 0x4, + + /* U+78EC "磬" */ + 0x8, 0x79, 0xfe, 0x90, 0x21, 0x23, 0xfc, 0x20, + 0xf, 0xcf, 0xc9, 0x1f, 0x8c, 0x20, 0x67, 0x80, + 0x1, 0x7f, 0xf8, 0x20, 0x1, 0xff, 0xcd, 0x0, + 0x83, 0xff, 0x4, 0x2, 0x0, + + /* U+78F2 "磲" */ + 0x2, 0x7f, 0x79, 0x40, 0x20, 0x40, 0x24, 0x7e, + 0x22, 0x42, 0x39, 0x7e, 0x69, 0x40, 0x6a, 0x7f, + 0xac, 0x0, 0x28, 0x20, 0x2f, 0xff, 0x28, 0x30, + 0x38, 0xe8, 0x27, 0x27, 0x0, 0x20, + + /* U+78F4 "磴" */ + 0x3, 0xd4, 0x7c, 0x58, 0x10, 0xd0, 0x11, 0x8a, + 0x21, 0x4, 0x21, 0xfa, 0x3e, 0x1, 0x65, 0xfc, + 0xa5, 0x4, 0x25, 0x4, 0x25, 0xfc, 0x24, 0x0, + 0x38, 0xd8, 0x23, 0xff, + + /* U+78F7 "磷" */ + 0x2, 0x22, 0x79, 0x24, 0x20, 0x20, 0x27, 0xfe, + 0x20, 0x70, 0x38, 0xa8, 0x2b, 0x26, 0x6c, 0x22, + 0x69, 0xde, 0xaa, 0x44, 0x2f, 0x54, 0x28, 0xa4, + 0x39, 0x9e, 0x23, 0x4, 0x4, 0x4, + + /* U+78FA "磺" */ + 0x0, 0x48, 0x7d, 0xfe, 0x10, 0x48, 0x10, 0x48, + 0x23, 0xff, 0x20, 0x20, 0x7d, 0xfe, 0x65, 0x22, + 0x25, 0xfe, 0x25, 0x22, 0x25, 0x22, 0x24, 0xdc, + 0x3c, 0x0, 0x21, 0x8e, 0x2, 0x0, + + /* U+7901 "礁" */ + 0x0, 0x90, 0x7c, 0x88, 0x11, 0xfe, 0x21, 0x10, + 0x23, 0x10, 0x23, 0xfe, 0x79, 0x10, 0x69, 0xfe, + 0xa9, 0x10, 0x29, 0x10, 0x29, 0xfe, 0x29, 0x0, + 0x39, 0x52, 0x22, 0x4a, 0x2, 0x40, + + /* U+7905 "礅" */ + 0x0, 0x88, 0x78, 0x88, 0x23, 0xe8, 0x20, 0xf, + 0x23, 0xf2, 0x22, 0x32, 0x7a, 0x3a, 0x49, 0xca, + 0xcb, 0xec, 0x48, 0x44, 0x48, 0x84, 0x49, 0xe4, + 0x3a, 0x8a, 0x48, 0x92, 0x3, 0x91, + + /* U+7913 "礓" */ + 0x3, 0xff, 0x7c, 0x0, 0x11, 0xfe, 0x21, 0x22, + 0x21, 0xfe, 0x21, 0x22, 0x7c, 0xdc, 0x67, 0xff, + 0xa4, 0x0, 0x25, 0xfe, 0x25, 0xfe, 0x25, 0x22, + 0x3d, 0xfe, 0x20, 0x0, 0x3, 0xff, + + /* U+791E "礞" */ + 0x0, 0x88, 0xf7, 0xfc, 0x82, 0x21, 0x1f, 0xf2, + 0x20, 0x24, 0x1f, 0x16, 0x0, 0x25, 0xff, 0x48, + 0x60, 0x97, 0x59, 0x23, 0xe2, 0x59, 0xc3, 0x8d, + 0x48, 0xe2, 0x40, 0x38, 0x0, + + /* U+7924 "礤" */ + 0x0, 0x88, 0x7f, 0xff, 0x10, 0x88, 0x11, 0x10, + 0x22, 0xff, 0x3b, 0xca, 0x2a, 0x4c, 0x69, 0x84, + 0x68, 0xfa, 0x2b, 0x1, 0x2b, 0xfe, 0x28, 0x14, + 0x39, 0x12, 0x22, 0x11, 0x0, 0x60, + + /* U+7934 "礴" */ + 0x0, 0x90, 0xf7, 0xfc, 0x82, 0x1, 0x0, 0xa2, + 0x2f, 0x87, 0x2, 0x13, 0x3f, 0x25, 0x7e, 0xc8, + 0x94, 0x91, 0xf9, 0x28, 0x2, 0x5f, 0xf3, 0xc8, + 0x80, 0x89, 0x1, 0x6, 0x0, + + /* U+793A "示" */ + 0x3f, 0xf8, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf, + 0xff, 0xe0, 0x20, 0x0, 0x40, 0xc, 0x88, 0x11, + 0x8, 0x42, 0x11, 0x84, 0x12, 0x8, 0x18, 0x10, + 0x0, 0xe0, 0x0, + + /* U+793B "礻" */ + 0x10, 0x8, 0x0, 0x7e, 0x2, 0x4, 0x8, 0x18, + 0x3a, 0x69, 0x88, 0x8, 0x8, 0x8, 0x8, + + /* U+793C "礼" */ + 0x0, 0x20, 0x8, 0x20, 0x0, 0x20, 0x7e, 0x20, + 0x2, 0x20, 0x4, 0x20, 0xc, 0x20, 0x18, 0x20, + 0x3a, 0x20, 0x69, 0xa0, 0x48, 0x21, 0x8, 0x21, + 0x8, 0x21, 0x8, 0x23, 0x8, 0x1e, + + /* U+793E "社" */ + 0x20, 0x40, 0x40, 0x80, 0x1, 0x7, 0xc2, 0x0, + 0x84, 0x2, 0x7f, 0x8c, 0x10, 0x30, 0x20, 0xe8, + 0x40, 0x48, 0x80, 0x81, 0x1, 0x2, 0x2, 0x4, + 0x4, 0xff, 0xc8, 0x0, 0x0, + + /* U+7940 "祀" */ + 0x10, 0x0, 0x11, 0xfe, 0x9, 0x2, 0x7d, 0x2, + 0x5, 0x2, 0x9, 0x2, 0x11, 0xfe, 0x35, 0x0, + 0x53, 0x0, 0x11, 0x0, 0x11, 0x1, 0x11, 0x1, + 0x11, 0x3, 0x10, 0xfe, 0x10, 0x0, + + /* U+7941 "祁" */ + 0x10, 0x0, 0x10, 0xfc, 0x1, 0x1b, 0xf2, 0x20, + 0x24, 0x40, 0x89, 0x2, 0x12, 0xc, 0x22, 0x3a, + 0x46, 0xd2, 0x84, 0x21, 0x8, 0x42, 0x10, 0x85, + 0xc1, 0x8, 0x2, 0x10, 0x0, + + /* U+7946 "祆" */ + 0x20, 0x0, 0x23, 0xfe, 0x0, 0x20, 0xf8, 0x20, + 0x8, 0x20, 0x10, 0x20, 0x23, 0xff, 0x68, 0x20, + 0xa8, 0x60, 0x20, 0x50, 0x20, 0x90, 0x20, 0x88, + 0x21, 0xc, 0x22, 0x6, 0x24, 0x2, + + /* U+7948 "祈" */ + 0x20, 0x4, 0x47, 0xf0, 0x48, 0x7, 0xd0, 0x0, + 0xa0, 0x2, 0x40, 0x4, 0xff, 0x91, 0x8, 0x6a, + 0x11, 0x4c, 0x20, 0x88, 0x41, 0x10, 0x82, 0x41, + 0x4, 0x82, 0x9, 0x4, 0x0, + + /* U+7949 "祉" */ + 0x20, 0x20, 0x40, 0x40, 0x40, 0x87, 0xc9, 0x0, + 0x92, 0x2, 0x27, 0x8c, 0x48, 0x10, 0x90, 0xe9, + 0x21, 0x4a, 0x40, 0x84, 0x81, 0x9, 0x2, 0x12, + 0x5, 0xff, 0xc8, 0x0, 0x0, + + /* U+7953 "祓" */ + 0x20, 0x80, 0x41, 0x30, 0x2, 0x7, 0xdf, 0xf0, + 0x88, 0x2, 0x20, 0xc, 0x7f, 0x30, 0x82, 0xe9, + 0x89, 0x45, 0x10, 0x89, 0x41, 0x21, 0x2, 0x85, + 0x4, 0x31, 0x88, 0x80, 0x80, + + /* U+7956 "祖" */ + 0x20, 0x0, 0x23, 0xfc, 0x2, 0x4, 0xfa, 0x4, + 0xa, 0x4, 0x13, 0xfc, 0x22, 0x4, 0x62, 0x4, + 0xae, 0x4, 0x23, 0xfc, 0x22, 0x4, 0x22, 0x4, + 0x22, 0x4, 0x27, 0xff, 0x20, 0x0, + + /* U+7957 "祗" */ + 0x20, 0x2, 0x47, 0xf8, 0x8, 0x87, 0xd1, 0x0, + 0xa2, 0x2, 0x44, 0x8, 0xff, 0x91, 0x10, 0xea, + 0x31, 0x44, 0x20, 0x88, 0x41, 0x10, 0x82, 0x2d, + 0x24, 0x69, 0x48, 0xb, 0x80, + + /* U+795A "祚" */ + 0x10, 0x80, 0x10, 0x80, 0x0, 0xff, 0x7d, 0x40, + 0x5, 0x40, 0xa, 0x40, 0x10, 0x7e, 0x30, 0x40, + 0x56, 0x40, 0x10, 0x40, 0x10, 0x7e, 0x10, 0x40, + 0x10, 0x40, 0x10, 0x40, 0x10, 0x40, + + /* U+795B "祛" */ + 0x20, 0x40, 0x40, 0x80, 0x1, 0x7, 0xdf, 0xe0, + 0x84, 0x2, 0x8, 0x8, 0x10, 0x31, 0xff, 0xac, + 0x40, 0x41, 0x0, 0x82, 0x41, 0x8, 0x42, 0x20, + 0x84, 0xff, 0x88, 0x0, 0x80, + + /* U+795C "祜" */ + 0x20, 0x20, 0x40, 0x40, 0x0, 0x87, 0xdf, 0xf0, + 0x82, 0x2, 0x4, 0x8, 0x8, 0x10, 0x10, 0x6b, + 0xfd, 0x44, 0x8, 0x88, 0x11, 0x10, 0x22, 0x20, + 0x44, 0x7f, 0x88, 0x81, 0x0, + + /* U+795D "祝" */ + 0x20, 0x0, 0x47, 0xf8, 0x48, 0x17, 0xd0, 0x20, + 0xa0, 0x42, 0x40, 0x88, 0x81, 0x31, 0xfe, 0xe8, + 0x91, 0x41, 0x20, 0x82, 0x41, 0x8, 0x82, 0x11, + 0x24, 0x42, 0x49, 0x7, 0x80, + + /* U+795E "神" */ + 0x20, 0x20, 0x40, 0x40, 0x0, 0x87, 0xdf, 0xf0, + 0xa2, 0x22, 0x44, 0x48, 0xff, 0x91, 0x11, 0x6a, + 0x23, 0x44, 0x44, 0x8f, 0xf9, 0x1, 0x2, 0x2, + 0x4, 0x4, 0x8, 0x8, 0x0, + + /* U+795F "祟" */ + 0x41, 0x4, 0x82, 0x9, 0xff, 0xf0, 0x8, 0x4, + 0x10, 0x4f, 0xff, 0x80, 0x0, 0x1f, 0xfc, 0x0, + 0x0, 0x0, 0x3, 0xff, 0xf8, 0x8, 0x1, 0x11, + 0x9c, 0x20, 0xe3, 0xc0, 0x0, + + /* U+7960 "祠" */ + 0x20, 0x0, 0x47, 0xfc, 0x40, 0xf, 0xc0, 0x10, + 0xbf, 0xa2, 0x0, 0x48, 0xfc, 0xb5, 0x9, 0xa6, + 0x12, 0x44, 0x24, 0x88, 0x49, 0x1f, 0x92, 0x0, + 0x24, 0x0, 0x48, 0xf, 0x0, + + /* U+7962 "祢" */ + 0x20, 0x80, 0x41, 0x0, 0x7, 0xff, 0xc8, 0x10, + 0xa0, 0x42, 0x84, 0x88, 0x8, 0x30, 0x94, 0xe9, + 0x25, 0x44, 0x48, 0x88, 0x89, 0x11, 0x12, 0x2, + 0x4, 0x4, 0x8, 0x78, 0x0, + + /* U+7965 "祥" */ + 0x42, 0x10, 0x84, 0x40, 0x2, 0x3d, 0xff, 0x10, + 0x40, 0x81, 0x2, 0x7f, 0x94, 0x10, 0xc8, 0x41, + 0x1, 0x4, 0x7f, 0xd0, 0x10, 0x40, 0x41, 0x1, + 0x4, 0x4, 0x0, + + /* U+7967 "祧" */ + 0x20, 0x90, 0x30, 0x90, 0x0, 0x90, 0xfa, 0x92, + 0xa, 0x94, 0x12, 0x94, 0x30, 0x90, 0x20, 0x90, + 0x79, 0x98, 0xa2, 0x96, 0x20, 0x90, 0x21, 0x10, + 0x21, 0x12, 0x22, 0x12, 0x24, 0x1e, + + /* U+7968 "票" */ + 0xff, 0xfe, 0x8, 0x81, 0xff, 0xf2, 0x22, 0x24, + 0x44, 0x4f, 0xff, 0x80, 0x0, 0x1f, 0xfe, 0x0, + 0x1, 0xff, 0xfc, 0x4, 0x0, 0xc9, 0x86, 0x10, + 0xd0, 0xe0, 0x40, + + /* U+796D "祭" */ + 0x0, 0x0, 0x21, 0x0, 0xfb, 0xf1, 0x14, 0x25, + 0x44, 0x99, 0x8a, 0xa, 0x8, 0xf, 0xe8, 0x60, + 0xd, 0x80, 0x0, 0xff, 0xe0, 0x8, 0x1, 0x12, + 0x4, 0x23, 0x10, 0x41, 0x7, 0x0, + + /* U+796F "祯" */ + 0x0, 0x40, 0x40, 0x80, 0x81, 0x0, 0x83, 0xff, + 0x84, 0x2, 0x7f, 0x84, 0x81, 0x11, 0x22, 0x6a, + 0x45, 0x44, 0x88, 0x89, 0x11, 0x12, 0x22, 0x6, + 0x4, 0x13, 0x8, 0xc3, 0x13, 0x1, + + /* U+7977 "祷" */ + 0x20, 0x40, 0x40, 0x80, 0x4f, 0xff, 0xc2, 0x0, + 0xbf, 0xc2, 0x8, 0x8, 0x20, 0x33, 0xff, 0xed, + 0x8, 0x43, 0xfc, 0x88, 0x21, 0x14, 0x42, 0x44, + 0x84, 0x1, 0x8, 0xc, 0x0, + + /* U+7978 "祸" */ + 0x0, 0x0, 0x47, 0xf8, 0x48, 0x10, 0x10, 0x2f, + 0xa0, 0x42, 0x7f, 0x84, 0x8, 0x10, 0x10, 0x77, + 0xff, 0x58, 0x44, 0x91, 0x89, 0x22, 0x92, 0x58, + 0xa4, 0x80, 0x49, 0x0, 0x92, 0xe, + + /* U+797A "祺" */ + 0x21, 0x4, 0x21, 0x4, 0x11, 0x4, 0x3, 0xff, + 0xf9, 0x4, 0x9, 0x4, 0x11, 0xfc, 0x21, 0x4, + 0x69, 0xfc, 0xa5, 0x4, 0x21, 0x4, 0x27, 0xff, + 0x20, 0x0, 0x20, 0x88, 0x23, 0x6, 0x26, 0x1, + + /* U+7980 "禀" */ + 0x0, 0x0, 0x2, 0x3, 0xff, 0xf8, 0x0, 0x7, + 0xff, 0x88, 0x1, 0x17, 0xfa, 0x2f, 0xf4, 0x40, + 0x8, 0x7f, 0xe1, 0xff, 0xf0, 0x0, 0xf, 0xff, + 0xe0, 0x20, 0x18, 0x4e, 0x47, 0x3, + + /* U+7981 "禁" */ + 0x10, 0x20, 0x20, 0x43, 0xfb, 0xf8, 0x81, 0x3, + 0x87, 0xa, 0xd5, 0xa4, 0xc8, 0x80, 0x0, 0x3f, + 0xf8, 0x0, 0x0, 0x0, 0x7, 0xff, 0xf0, 0x10, + 0x6, 0x26, 0x30, 0x43, 0x7, 0x0, + + /* U+7984 "禄" */ + 0x20, 0x0, 0x23, 0xfc, 0x0, 0x4, 0xf9, 0xfc, + 0x8, 0x4, 0x10, 0x4, 0x27, 0xff, 0x60, 0x20, + 0xea, 0x22, 0xa1, 0x34, 0x20, 0x68, 0x21, 0xa8, + 0x26, 0x26, 0x20, 0x20, 0x21, 0xe0, + + /* U+7985 "禅" */ + 0x21, 0x4, 0x10, 0x88, 0x3, 0xfe, 0x7a, 0x22, + 0xa, 0x22, 0x13, 0xfe, 0x12, 0x22, 0x3a, 0x22, + 0x57, 0xfe, 0x90, 0x20, 0x10, 0x20, 0x17, 0xff, + 0x10, 0x20, 0x10, 0x20, 0x10, 0x20, + + /* U+798A "禊" */ + 0x42, 0x0, 0x44, 0xf8, 0x9c, 0x97, 0x91, 0x21, + 0x7a, 0x44, 0x44, 0x8b, 0xf1, 0x31, 0x22, 0xf2, + 0x19, 0x40, 0x80, 0xbf, 0xf9, 0x6, 0x2, 0xa, + 0x4, 0x63, 0xb, 0x1, 0x80, + + /* U+798F "福" */ + 0x20, 0x0, 0x4f, 0xfc, 0x40, 0x7, 0xcf, 0xe0, + 0x90, 0x42, 0x3f, 0x88, 0x0, 0x35, 0xff, 0xa2, + 0x22, 0x44, 0x44, 0x8f, 0xf9, 0x11, 0x12, 0x22, + 0x24, 0x7f, 0xc8, 0x80, 0x80, + + /* U+799A "禚" */ + 0x21, 0x4, 0x41, 0x10, 0x0, 0x47, 0xdf, 0xf0, + 0x82, 0x2, 0x4, 0x8, 0xff, 0x3c, 0x10, 0xa0, + 0x20, 0x47, 0xfc, 0x80, 0x1, 0x10, 0xa2, 0x29, + 0x24, 0x8a, 0x49, 0x10, 0x0, + + /* U+79A7 "禧" */ + 0x40, 0x40, 0x5f, 0xfc, 0x1, 0x7, 0xbf, 0xe1, + 0x0, 0x4, 0xff, 0x89, 0x1, 0x3b, 0xfe, 0xa9, + 0x10, 0x5f, 0xfc, 0x80, 0x1, 0x3f, 0xe2, 0x40, + 0x44, 0xff, 0x89, 0x1, 0x0, + + /* U+79B3 "禳" */ + 0x40, 0x40, 0x5f, 0xfc, 0x80, 0x7, 0xfd, 0xe1, + 0x7b, 0xc4, 0x0, 0x9, 0xff, 0x21, 0x8, 0xd7, + 0xfc, 0x84, 0x21, 0x3f, 0xfa, 0xe, 0x24, 0x63, + 0x8b, 0x4b, 0x10, 0xe1, 0x80, + + /* U+79B9 "禹" */ + 0xff, 0xf0, 0x10, 0x1f, 0xfc, 0x84, 0x24, 0x21, + 0x3f, 0xf8, 0x8, 0x1f, 0xff, 0x82, 0xc, 0x12, + 0x60, 0x93, 0x7f, 0xd8, 0x0, 0xc0, 0x38, + + /* U+79BA "禺" */ + 0x7f, 0xf2, 0x10, 0x9f, 0xfc, 0x84, 0x24, 0x21, + 0x3f, 0xf8, 0x8, 0x1f, 0xff, 0x82, 0xc, 0x12, + 0x6f, 0xfb, 0x0, 0x58, 0x0, 0xc0, 0x38, + + /* U+79BB "离" */ + 0x0, 0x0, 0x2, 0x3, 0xff, 0xf9, 0x41, 0x22, + 0x7c, 0x45, 0xcc, 0x88, 0x1, 0x1f, 0xfe, 0x1, + 0x0, 0x2, 0x1, 0xff, 0xf2, 0x10, 0x24, 0x44, + 0x49, 0xfc, 0x90, 0x1, 0x20, 0xe, + + /* U+79BD "禽" */ + 0x1, 0x0, 0x7, 0x0, 0x31, 0x1, 0x89, 0xcd, + 0xff, 0xe0, 0x0, 0xb, 0xb2, 0x13, 0xe4, 0x28, + 0x28, 0x7f, 0xf0, 0x8, 0x3, 0xff, 0xe4, 0x48, + 0x49, 0xf8, 0x90, 0x1, 0x20, 0x1c, + + /* U+79BE "禾" */ + 0x0, 0xc, 0x3f, 0xfc, 0x1, 0x0, 0x1, 0x0, + 0x1, 0x0, 0x7f, 0xfe, 0x3, 0x80, 0x3, 0x40, + 0x5, 0x20, 0x5, 0x30, 0x9, 0x18, 0x31, 0xc, + 0x61, 0x3, 0x1, 0x0, 0x1, 0x0, + + /* U+79C0 "秀" */ + 0x7f, 0xf8, 0x8, 0x0, 0x20, 0x3f, 0xff, 0x6, + 0x80, 0x29, 0x7, 0x23, 0xb0, 0x3, 0x3f, 0xe0, + 0x21, 0x0, 0x8f, 0x82, 0x2, 0x10, 0x8, 0x80, + 0x4c, 0xf, 0x0, + + /* U+79C1 "私" */ + 0x0, 0x11, 0xfc, 0x60, 0x40, 0x80, 0x81, 0xf, + 0xe2, 0x2, 0x8, 0xc, 0x12, 0x1c, 0x24, 0x54, + 0x88, 0xa1, 0xa, 0x42, 0x14, 0x88, 0x21, 0x11, + 0xe2, 0x3c, 0x44, 0x0, 0x80, + + /* U+79C3 "秃" */ + 0x7f, 0xf8, 0x8, 0x0, 0x20, 0x3f, 0xff, 0x6, + 0x80, 0x29, 0x3, 0x23, 0x30, 0x3, 0x1f, 0xc0, + 0x41, 0x1, 0x4, 0x4, 0x11, 0x30, 0x47, 0x0, + 0xf0, 0x0, 0x0, + + /* U+79C6 "秆" */ + 0x7e, 0xfe, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0xfe, 0x10, 0x10, 0x10, 0x11, 0xff, 0x3c, 0x10, + 0x72, 0x10, 0x50, 0x10, 0x90, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, + + /* U+79C9 "秉" */ + 0x7f, 0xfc, 0x2, 0x0, 0x4, 0x7, 0xff, 0xf0, + 0x10, 0xf, 0xff, 0x0, 0x42, 0x7f, 0xff, 0x1, + 0x8, 0xff, 0xf0, 0xe, 0x0, 0x6a, 0x1, 0x13, + 0x1c, 0x21, 0xc0, 0x40, 0x0, + + /* U+79CB "秋" */ + 0x0, 0x60, 0xf8, 0xc0, 0x41, 0x80, 0x93, 0x2f, + 0xe6, 0x42, 0x2d, 0xc, 0x5a, 0x1c, 0x30, 0x58, + 0xe0, 0xa9, 0xa2, 0x42, 0x44, 0x8c, 0x41, 0x10, + 0x82, 0x40, 0x85, 0x0, 0x80, + + /* U+79CD "种" */ + 0x0, 0x10, 0xf8, 0x20, 0x40, 0x40, 0x8f, 0xf1, + 0x11, 0x2f, 0xa2, 0x44, 0x44, 0x98, 0x89, 0x39, + 0x12, 0xab, 0xfd, 0x40, 0x40, 0x80, 0x81, 0x1, + 0x2, 0x2, 0x4, 0x4, 0x0, + + /* U+79D1 "科" */ + 0x0, 0x8, 0x78, 0x8, 0x10, 0xc8, 0x10, 0x28, + 0xfc, 0x8, 0x11, 0x8, 0x30, 0x88, 0x38, 0x48, + 0x54, 0x8, 0x50, 0xf, 0x93, 0xf8, 0x90, 0x8, + 0x10, 0x8, 0x10, 0x8, 0x10, 0x8, + + /* U+79D2 "秒" */ + 0x0, 0x20, 0x7c, 0x20, 0x10, 0x20, 0x11, 0x24, + 0xfd, 0x24, 0x11, 0x22, 0x12, 0x20, 0x38, 0x24, + 0x54, 0x24, 0x50, 0x8, 0x90, 0x10, 0x10, 0x30, + 0x10, 0x40, 0x11, 0x80, 0x16, 0x0, + + /* U+79D5 "秕" */ + 0x2, 0x21, 0xf4, 0x40, 0x88, 0x81, 0x11, 0x1f, + 0xa2, 0x44, 0x75, 0x8, 0x8c, 0x39, 0x10, 0x6a, + 0x21, 0x44, 0x42, 0x88, 0x81, 0x11, 0x12, 0x2a, + 0x24, 0x64, 0x48, 0x8f, 0x0, + + /* U+79D8 "秘" */ + 0x0, 0x40, 0x10, 0x83, 0xe0, 0x81, 0x1, 0x22, + 0x8, 0x9f, 0x11, 0x8, 0xa5, 0x31, 0x4a, 0x72, + 0x93, 0x59, 0x46, 0x93, 0xd, 0x6, 0x22, 0x18, + 0x44, 0x70, 0x89, 0xa1, 0x14, 0x3c, + + /* U+79DF "租" */ + 0xfd, 0xfc, 0x22, 0x8, 0x44, 0x10, 0x88, 0x2f, + 0xdf, 0xc6, 0x20, 0x8e, 0x41, 0x2a, 0x82, 0x51, + 0xfd, 0x22, 0x8, 0x44, 0x10, 0x88, 0x21, 0x7f, + 0xe2, 0x0, 0x0, + + /* U+79E3 "秣" */ + 0x0, 0x20, 0xf0, 0x20, 0x23, 0xfe, 0x20, 0x20, + 0x20, 0x20, 0xfc, 0x20, 0x21, 0xfe, 0x60, 0x20, + 0x78, 0x70, 0x64, 0xb0, 0xa0, 0xa8, 0xa1, 0x24, + 0x22, 0x22, 0x20, 0x20, 0x20, 0x20, + + /* U+79E4 "秤" */ + 0xfd, 0xfe, 0x20, 0x40, 0x40, 0x88, 0x89, 0x2f, + 0xd2, 0x46, 0x15, 0xc, 0x8, 0x2d, 0xff, 0x54, + 0x21, 0xa8, 0x42, 0x40, 0x80, 0x81, 0x1, 0x2, + 0x2, 0x4, 0x0, + + /* U+79E6 "秦" */ + 0x0, 0x0, 0x8, 0x7, 0xff, 0x80, 0x80, 0x2, + 0x1, 0xff, 0xe0, 0x40, 0x3f, 0xff, 0x18, 0x40, + 0xff, 0xcc, 0x20, 0xef, 0xfc, 0x2, 0x0, 0x3f, + 0x3, 0x23, 0x90, 0x80, + + /* U+79E7 "秧" */ + 0x0, 0x21, 0xf0, 0x40, 0x80, 0x81, 0x1f, 0xef, + 0xa2, 0x44, 0x44, 0x88, 0x89, 0x39, 0x22, 0x6f, + 0xff, 0x40, 0x42, 0x81, 0xc1, 0x4, 0x82, 0x8, + 0x84, 0x60, 0x89, 0x0, 0x80, + + /* U+79E9 "秩" */ + 0x0, 0x20, 0x7d, 0x20, 0x11, 0x20, 0x11, 0xfe, + 0x11, 0x20, 0xfd, 0x20, 0x10, 0x20, 0x3b, 0xfe, + 0x3c, 0x20, 0x54, 0x60, 0xd0, 0x50, 0x90, 0x98, + 0x10, 0x88, 0x11, 0x6, 0x12, 0x2, + + /* U+79EB "秫" */ + 0x0, 0x20, 0xfc, 0x28, 0x10, 0x24, 0x10, 0x20, + 0x11, 0xfe, 0xfc, 0x20, 0x30, 0x70, 0x30, 0x70, + 0x58, 0xa8, 0x55, 0x28, 0x91, 0x24, 0x92, 0x22, + 0x14, 0x20, 0x10, 0x20, 0x10, 0x20, + + /* U+79ED "秭" */ + 0x0, 0x20, 0xf8, 0x40, 0x40, 0xb8, 0x8f, 0x81, + 0x12, 0xf, 0xa4, 0x4, 0x48, 0x1c, 0xff, 0x5c, + 0x22, 0xa8, 0xc5, 0x42, 0x88, 0x89, 0x11, 0x32, + 0xe2, 0x84, 0x4, 0x8, 0x0, + + /* U+79EF "积" */ + 0x7d, 0xfc, 0x22, 0x8, 0x44, 0x10, 0x88, 0x2f, + 0xd0, 0x42, 0x20, 0x8c, 0x41, 0x1c, 0xfe, 0x54, + 0x0, 0xa1, 0x22, 0x42, 0x20, 0x88, 0x21, 0x20, + 0x62, 0x80, 0x40, + + /* U+79F0 "称" */ + 0x1, 0x1, 0xf2, 0x0, 0x87, 0xf1, 0x10, 0x2f, + 0xc4, 0x84, 0x8, 0x8, 0x10, 0x1d, 0x24, 0x6a, + 0x49, 0xc4, 0x92, 0x91, 0x11, 0x22, 0x22, 0x4, + 0x4, 0x8, 0x8, 0x70, 0x0, + + /* U+79F8 "秸" */ + 0x0, 0x41, 0xf0, 0x80, 0x9f, 0xf9, 0x2, 0x2, + 0x4, 0x1f, 0x8, 0x8, 0xff, 0x10, 0x0, 0x70, + 0x0, 0xd7, 0xfa, 0x88, 0x15, 0x10, 0x22, 0x20, + 0x44, 0x7f, 0x88, 0x81, 0x0, + + /* U+79FB "移" */ + 0x0, 0x0, 0x22, 0xf, 0xf, 0xc8, 0x41, 0x23, + 0x8, 0x83, 0x4f, 0x86, 0x8, 0x20, 0x27, 0x41, + 0xc3, 0xf6, 0x90, 0x68, 0xe2, 0xa4, 0x50, 0x80, + 0x82, 0x8, 0x9, 0xc0, + + /* U+79FD "秽" */ + 0x0, 0x23, 0xe8, 0x92, 0x22, 0x48, 0x89, 0xf9, + 0xd8, 0x81, 0x6, 0x8, 0x1c, 0x7f, 0xa9, 0xa, + 0x8c, 0x2a, 0xcd, 0x8, 0x8, 0x20, 0x40, 0x86, + 0x2, 0x60, 0x0, + + /* U+7A00 "稀" */ + 0x1, 0x2, 0x7c, 0xcc, 0x10, 0x30, 0x13, 0xce, + 0x7c, 0x20, 0x13, 0xff, 0x10, 0x80, 0x3d, 0x10, + 0x51, 0xfe, 0x53, 0x12, 0x55, 0x12, 0x11, 0x12, + 0x11, 0x12, 0x10, 0x1e, 0x10, 0x10, + + /* U+7A02 "稂" */ + 0x0, 0x20, 0x7c, 0x20, 0x11, 0xfe, 0x12, 0x2, + 0x12, 0x2, 0x7d, 0xfe, 0x12, 0x2, 0x32, 0x2, + 0x39, 0xfe, 0x56, 0x40, 0x52, 0x22, 0x92, 0x2c, + 0x12, 0x10, 0x12, 0x8, 0x12, 0xc6, 0x13, 0x1, + + /* U+7A03 "稃" */ + 0x0, 0x1, 0x7d, 0xfe, 0x10, 0x20, 0x11, 0x12, + 0x10, 0x92, 0x7c, 0x0, 0x11, 0xfe, 0x38, 0x6, + 0x34, 0x8, 0x50, 0x10, 0x53, 0xff, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0xf0, + + /* U+7A06 "稆" */ + 0xfd, 0xfc, 0x22, 0x8, 0x44, 0x17, 0xe8, 0x23, + 0x1f, 0xc6, 0x0, 0xc, 0x0, 0x2c, 0xff, 0x56, + 0x3, 0x24, 0x6, 0x48, 0x8, 0x90, 0x11, 0x1f, + 0xe2, 0x40, 0x40, + + /* U+7A0B "程" */ + 0x7d, 0xfe, 0x11, 0x2, 0x11, 0x2, 0x7d, 0xfe, + 0x10, 0x0, 0x10, 0x0, 0x33, 0xff, 0x3c, 0x20, + 0x54, 0x20, 0x51, 0xfe, 0x10, 0x20, 0x10, 0x20, + 0x13, 0xff, 0x10, 0x0, + + /* U+7A0D "稍" */ + 0x0, 0x27, 0xe8, 0x92, 0x12, 0x88, 0xa, 0x20, + 0x23, 0xef, 0xf2, 0x20, 0x4c, 0xff, 0x6a, 0x6, + 0x88, 0x1a, 0x3f, 0xc8, 0x81, 0x22, 0x4, 0x88, + 0x12, 0x21, 0xc0, + + /* U+7A0E "税" */ + 0x1, 0x9, 0xf2, 0x20, 0x82, 0x81, 0x1a, 0xef, + 0xa0, 0x44, 0x40, 0x88, 0x81, 0x39, 0x2, 0xbb, + 0xfd, 0x51, 0x42, 0x82, 0x81, 0x5, 0x12, 0x12, + 0x24, 0x44, 0x4b, 0xf, 0x0, + + /* U+7A14 "稔" */ + 0x0, 0x0, 0x10, 0x83, 0xc2, 0x81, 0x4, 0x82, + 0x14, 0x9f, 0xc8, 0xc8, 0x0, 0x11, 0xfc, 0x30, + 0x8, 0xd0, 0x21, 0x85, 0x5, 0x2a, 0x22, 0x50, + 0x44, 0xa1, 0x48, 0x44, 0x10, 0xf8, + + /* U+7A17 "稗" */ + 0x0, 0x81, 0xf1, 0x0, 0x8f, 0xf1, 0x22, 0x22, + 0x44, 0x5f, 0x7f, 0x89, 0x11, 0x12, 0x22, 0x64, + 0x44, 0xe7, 0xfa, 0xa2, 0x5, 0x19, 0x2, 0x7f, + 0xe4, 0x4, 0x8, 0x8, 0x10, 0x10, + + /* U+7A1A "稚" */ + 0x0, 0x50, 0x7c, 0x88, 0x10, 0x88, 0x11, 0xfe, + 0x11, 0x8, 0x7f, 0x8, 0x11, 0xfe, 0x39, 0x8, + 0x35, 0x8, 0x51, 0xfe, 0x91, 0x8, 0x11, 0x8, + 0x11, 0x8, 0x11, 0xff, 0x11, 0x0, + + /* U+7A1E "稞" */ + 0xff, 0xfc, 0x48, 0x88, 0x8f, 0xf7, 0xe2, 0x22, + 0x44, 0x4e, 0x7f, 0x9e, 0x10, 0x54, 0x20, 0xa7, + 0xff, 0x41, 0xc0, 0x85, 0x41, 0x12, 0x42, 0xc4, + 0x64, 0x8, 0x0, + + /* U+7A20 "稠" */ + 0xfb, 0xfc, 0x90, 0x12, 0x5f, 0xff, 0x11, 0x24, + 0x44, 0x97, 0xf7, 0x40, 0x6b, 0x3d, 0xa4, 0x96, + 0x92, 0x52, 0x4f, 0x49, 0x1, 0x28, 0x4, 0xa0, + 0x70, + + /* U+7A23 "稣" */ + 0x10, 0x0, 0x20, 0x38, 0xf3, 0xc1, 0x20, 0x84, + 0x41, 0x1f, 0xc2, 0x12, 0xbf, 0xa5, 0x8, 0x7e, + 0x30, 0x94, 0xb1, 0x29, 0x63, 0xf4, 0xa0, 0x19, + 0x20, 0x2, 0x3f, 0x84, 0x0, 0x8, + + /* U+7A33 "稳" */ + 0x0, 0x81, 0xe3, 0xe0, 0x8c, 0x41, 0x31, 0x2, + 0x7f, 0xce, 0x0, 0x88, 0xff, 0x38, 0x2, 0x78, + 0x5, 0x57, 0xfa, 0x81, 0x1, 0x29, 0x22, 0x90, + 0xa5, 0x23, 0x8, 0x7c, 0x0, + + /* U+7A37 "稷" */ + 0xfb, 0xfc, 0x48, 0x48, 0x8f, 0xf7, 0xe1, 0x22, + 0x3f, 0xce, 0x0, 0x1a, 0xc2, 0x52, 0x23, 0xa1, + 0xf9, 0x46, 0x10, 0x92, 0x41, 0x3, 0x2, 0x1d, + 0x85, 0xc0, 0x80, + + /* U+7A39 "稹" */ + 0x18, 0x41, 0xcf, 0xfc, 0x81, 0x1, 0x1f, 0xef, + 0xa0, 0x44, 0x7f, 0x88, 0x81, 0x39, 0xfe, 0xaa, + 0x5, 0x47, 0xfa, 0x88, 0x11, 0x3f, 0xf2, 0x0, + 0x4, 0x63, 0x89, 0x0, 0x80, + + /* U+7A3B "稻" */ + 0x0, 0x2, 0xf3, 0xfc, 0x40, 0x8, 0x89, 0x27, + 0xd2, 0x42, 0x0, 0xc, 0x77, 0x9c, 0x81, 0x55, + 0x2, 0xa2, 0x6, 0x47, 0x78, 0x88, 0x11, 0x10, + 0x22, 0x3f, 0xc4, 0x40, 0x80, + + /* U+7A3C "稼" */ + 0x0, 0x40, 0xf8, 0x40, 0x27, 0xfe, 0x24, 0x2, + 0x20, 0x0, 0xfb, 0xfe, 0x20, 0x40, 0x61, 0x86, + 0x76, 0x58, 0xa9, 0xb0, 0xa2, 0x68, 0x24, 0xa4, + 0x23, 0x26, 0x24, 0x20, 0x21, 0xc0, + + /* U+7A3D "稽" */ + 0x0, 0x49, 0xf0, 0x88, 0x8f, 0xf9, 0x4, 0x9f, + 0xa0, 0xc4, 0x40, 0x88, 0xbf, 0x39, 0x81, 0x6d, + 0xfd, 0xc3, 0xfe, 0x84, 0x9, 0xf, 0xf2, 0x10, + 0x24, 0x3f, 0xc8, 0x40, 0x80, + + /* U+7A3F "稿" */ + 0x0, 0x41, 0xe0, 0x80, 0x8e, 0xf9, 0x1f, 0xef, + 0xa0, 0x44, 0x40, 0x88, 0xff, 0x38, 0x0, 0x6f, + 0xff, 0x48, 0x6, 0x93, 0xcd, 0x28, 0x92, 0x4f, + 0x24, 0x80, 0x49, 0x3, 0x80, + + /* U+7A46 "穆" */ + 0x8, 0x41, 0xe7, 0xf8, 0x90, 0x11, 0x1f, 0xef, + 0xc0, 0x44, 0x7f, 0x98, 0x10, 0x39, 0xa6, 0xa8, + 0x63, 0x43, 0x12, 0x88, 0xc1, 0x6, 0x2, 0x71, + 0x84, 0xc, 0x9, 0xe0, 0x0, + + /* U+7A51 "穑" */ + 0x0, 0x20, 0xfb, 0xfe, 0x20, 0x20, 0x21, 0x24, + 0xf8, 0x28, 0x27, 0xff, 0x20, 0x0, 0x73, 0xfe, + 0x6a, 0x2, 0xa2, 0xfa, 0xa2, 0x8a, 0x22, 0xfa, + 0x22, 0x2, 0x23, 0xfe, 0x22, 0x2, + + /* U+7A57 "穗" */ + 0x8, 0x21, 0xe7, 0xfc, 0x80, 0x81, 0x1f, 0xe2, + 0x22, 0x5f, 0x7f, 0x88, 0x89, 0x39, 0xfe, 0xa8, + 0x21, 0x4f, 0xfa, 0x80, 0x9, 0x29, 0x22, 0x50, + 0xa4, 0x21, 0x48, 0x7c, 0x0, + + /* U+7A70 "穰" */ + 0x0, 0x20, 0xff, 0xff, 0x20, 0x0, 0x27, 0xfe, + 0xfb, 0xde, 0x20, 0x0, 0x27, 0xfe, 0x31, 0x8, + 0x6b, 0xfe, 0xa1, 0x8, 0xa7, 0xff, 0x20, 0xe2, + 0x23, 0x1c, 0x2d, 0x2c, 0x21, 0xc2, + + /* U+7A74 "穴" */ + 0x0, 0x0, 0x1, 0x0, 0x0, 0x80, 0x7f, 0xfe, + 0x40, 0x2, 0x40, 0x2, 0x0, 0x40, 0x4, 0x20, + 0x4, 0x20, 0x4, 0x20, 0x8, 0x10, 0x8, 0x10, + 0x10, 0x8, 0x20, 0x4, 0x40, 0x3, 0x0, 0x0, + + /* U+7A76 "究" */ + 0x1, 0x0, 0x0, 0x80, 0x7f, 0xfe, 0x40, 0x2, + 0x44, 0x62, 0x18, 0x18, 0x62, 0x6, 0x2, 0x0, + 0x3f, 0xf0, 0x2, 0x10, 0x4, 0x10, 0x4, 0x11, + 0x8, 0x11, 0x10, 0x11, 0x60, 0x1e, + + /* U+7A77 "穷" */ + 0x2, 0x0, 0x2, 0x3, 0xff, 0xf4, 0x0, 0x28, + 0x84, 0x46, 0x87, 0x31, 0x1, 0x82, 0x0, 0xff, + 0xf8, 0x8, 0x10, 0x20, 0x20, 0x40, 0x41, 0x0, + 0x84, 0x1, 0x30, 0x7c, 0x0, + + /* U+7A78 "穸" */ + 0x1, 0x0, 0x0, 0x80, 0x7f, 0xfe, 0x40, 0x2, + 0x44, 0x62, 0x38, 0x1c, 0x44, 0x2, 0xf, 0xfc, + 0x18, 0x8, 0x24, 0x18, 0x42, 0x30, 0x0, 0x40, + 0x1, 0x80, 0x1e, 0x0, 0x60, 0x0, + + /* U+7A79 "穹" */ + 0x2, 0x0, 0x2, 0x3, 0xff, 0xfc, 0x0, 0x19, + 0x87, 0x3c, 0x1, 0x9f, 0xfe, 0x0, 0x4, 0x0, + 0x8, 0xff, 0xf1, 0x0, 0x3, 0xff, 0xc0, 0x0, + 0x80, 0x1, 0x0, 0x2, 0x0, 0xf8, + + /* U+7A7A "空" */ + 0x2, 0x0, 0x4, 0xf, 0xff, 0xe0, 0x1, 0x80, + 0x4, 0x61, 0x82, 0x1, 0xb0, 0x1, 0x3f, 0xf0, + 0x8, 0x0, 0x20, 0x0, 0x80, 0x2, 0x3, 0xff, + 0xf0, + + /* U+7A7F "穿" */ + 0x1, 0x0, 0x1, 0x0, 0x7f, 0xfe, 0x40, 0x2, + 0x46, 0x72, 0x38, 0xc, 0x0, 0x0, 0x7f, 0xfe, + 0x0, 0x20, 0x20, 0x20, 0x3f, 0xff, 0x1, 0xa0, + 0x3, 0x20, 0xc, 0x20, 0x30, 0x20, 0x41, 0xc0, + + /* U+7A80 "窀" */ + 0x2, 0x0, 0x4, 0x3, 0xff, 0xfc, 0x0, 0x18, + 0x8c, 0x26, 0x7, 0x10, 0x81, 0x7f, 0xff, 0x42, + 0x8, 0x84, 0x11, 0x8, 0x23, 0xff, 0xc0, 0x20, + 0x20, 0x40, 0x40, 0x80, 0x80, 0xfe, + + /* U+7A81 "突" */ + 0x2, 0x0, 0x4, 0xf, 0xff, 0xe0, 0x1, 0x88, + 0xc4, 0xc8, 0xec, 0x24, 0x40, 0x80, 0xff, 0xfc, + 0x8, 0x0, 0x50, 0x3, 0x20, 0x18, 0x61, 0x80, + 0x78, 0x0, 0x0, + + /* U+7A83 "窃" */ + 0x1, 0x0, 0x3, 0x1, 0xff, 0xfa, 0x0, 0x14, + 0xc7, 0x2e, 0x1, 0xc4, 0xff, 0x8, 0x22, 0x1c, + 0x45, 0xe0, 0x88, 0x41, 0x10, 0x84, 0x21, 0xc8, + 0x46, 0x20, 0x81, 0x9e, 0x0, + + /* U+7A84 "窄" */ + 0x2, 0x0, 0x4, 0xf, 0xff, 0xe0, 0x1, 0x8c, + 0xc4, 0xc0, 0xe4, 0x80, 0x7, 0xff, 0x34, 0x1, + 0x90, 0xc, 0x7f, 0x81, 0x0, 0x7, 0xf8, 0x10, + 0x0, 0x40, 0x1, 0x0, + + /* U+7A86 "窆" */ + 0x1, 0x0, 0x0, 0x80, 0x7f, 0xfe, 0x40, 0x2, + 0xe, 0x70, 0x70, 0xe, 0x1f, 0xf8, 0x0, 0x0, + 0x0, 0x80, 0x3f, 0xfc, 0x0, 0x38, 0x1, 0xc0, + 0xe, 0x0, 0x30, 0x0, 0x78, 0x0, 0xc7, 0xfe, + + /* U+7A88 "窈" */ + 0x2, 0x0, 0x8, 0xf, 0xff, 0xe0, 0x1, 0x18, + 0xc1, 0x80, 0xe1, 0x8, 0x8, 0x20, 0x49, 0xfb, + 0xe2, 0x21, 0x8, 0x8a, 0x22, 0x49, 0xb, 0xf4, + 0x20, 0x21, 0x81, 0x3c, + + /* U+7A8D "窍" */ + 0x2, 0x0, 0x8, 0xf, 0xff, 0xe0, 0x1, 0x8c, + 0xc5, 0xc0, 0xe0, 0x0, 0x3e, 0xff, 0x21, 0x0, + 0x84, 0x2, 0x1f, 0x88, 0x2, 0x20, 0x8, 0xf0, + 0x2c, 0x0, 0x80, 0x3c, + + /* U+7A91 "窑" */ + 0x1, 0x0, 0x2, 0x3, 0xff, 0xfc, 0x0, 0x10, + 0xc6, 0x6, 0x3, 0xb2, 0x0, 0x8f, 0xfc, 0x21, + 0x0, 0x2, 0x3, 0xff, 0xf8, 0x8, 0x2, 0x10, + 0x84, 0x21, 0xf, 0xfe, 0x0, 0x4, + + /* U+7A92 "窒" */ + 0x1, 0x0, 0x2, 0x3, 0xff, 0xfc, 0x0, 0x11, + 0xc7, 0x1c, 0x1, 0xcf, 0xfe, 0x2, 0x0, 0x18, + 0x20, 0x7f, 0xf0, 0x4, 0x10, 0x8, 0x7, 0xff, + 0xc0, 0x20, 0x3f, 0xff, 0x80, + + /* U+7A95 "窕" */ + 0x2, 0x0, 0x2, 0x3, 0xff, 0xf4, 0x0, 0x20, + 0x44, 0x3, 0x6, 0x38, 0x3, 0x2, 0x40, 0x64, + 0x98, 0x29, 0x40, 0x13, 0x1, 0xe5, 0x8c, 0x88, + 0x81, 0x10, 0x4c, 0x21, 0x70, 0x7e, 0x0, 0x0, + + /* U+7A96 "窖" */ + 0x1, 0x0, 0x2, 0x3, 0xff, 0xfc, 0x0, 0x19, + 0xc7, 0x2d, 0x21, 0xc7, 0xfe, 0x10, 0x80, 0x1, + 0x1, 0xff, 0xfc, 0x0, 0x1, 0xff, 0xc2, 0x0, + 0x84, 0x1, 0xf, 0xfe, 0x10, 0x4, + + /* U+7A97 "窗" */ + 0x0, 0x0, 0x1, 0x1, 0xff, 0xfa, 0x0, 0x10, + 0x63, 0x7, 0x31, 0x90, 0x40, 0x9f, 0xfe, 0x21, + 0x4, 0x47, 0xe8, 0xb0, 0x91, 0xe, 0x22, 0x36, + 0x45, 0x80, 0x8f, 0xff, 0x10, 0x2, + + /* U+7A98 "窘" */ + 0x0, 0x0, 0x2, 0x3, 0xff, 0xf4, 0x0, 0x2b, + 0x87, 0xf, 0xff, 0x2, 0x2, 0x4, 0x4, 0xff, + 0xfe, 0x20, 0x13, 0xff, 0xe1, 0x0, 0x3, 0xff, + 0x9a, 0x1, 0x7, 0xfe, 0x8, 0x4, + + /* U+7A9C "窜" */ + 0x1, 0x0, 0x2, 0x3, 0xff, 0xfc, 0x0, 0x18, + 0xc6, 0x2e, 0x3, 0x8f, 0xfe, 0x10, 0x84, 0x21, + 0x8, 0x7f, 0xf0, 0x4, 0x3, 0xff, 0xe4, 0x10, + 0x4f, 0xff, 0x90, 0x41, 0x0, 0x80, + + /* U+7A9D "窝" */ + 0x0, 0x0, 0x0, 0x80, 0x7f, 0xfe, 0x40, 0x2, + 0xc, 0x30, 0x70, 0xe, 0x1f, 0xf8, 0x10, 0x8, + 0x1f, 0xf8, 0x0, 0x80, 0x3f, 0xfc, 0x21, 0x4, + 0x23, 0xc4, 0x2c, 0x34, 0x20, 0x4, 0x20, 0x1c, + + /* U+7A9F "窟" */ + 0x0, 0x80, 0xff, 0xfd, 0x0, 0x8, 0x71, 0xc1, + 0x0, 0x7, 0xff, 0xc8, 0x0, 0x9f, 0xff, 0x24, + 0x44, 0x48, 0x88, 0x9f, 0xf1, 0x2, 0x4, 0x84, + 0x29, 0xff, 0xd0, 0x0, 0x80, + + /* U+7AA0 "窠" */ + 0x0, 0x0, 0x4, 0xf, 0xff, 0xe0, 0x1, 0x18, + 0x73, 0x80, 0x33, 0xff, 0x8, 0x44, 0x3f, 0xf0, + 0x84, 0x43, 0xff, 0x0, 0x40, 0xff, 0xfc, 0x16, + 0x1, 0x17, 0x38, 0x43, + + /* U+7AA5 "窥" */ + 0x0, 0x0, 0x2, 0x3, 0xff, 0xfc, 0x0, 0x19, + 0xc6, 0x24, 0x3, 0x4, 0x0, 0x9, 0xfe, 0xfe, + 0x4, 0x24, 0xcb, 0xf9, 0x91, 0x13, 0xa3, 0x7, + 0x5, 0x1e, 0x50, 0x6c, 0xc1, 0x9f, 0x4, 0x0, + + /* U+7AA6 "窦" */ + 0x1, 0x0, 0x2, 0x3, 0xff, 0xfc, 0x0, 0x11, + 0x97, 0x8f, 0xff, 0x80, 0x40, 0x7f, 0xff, 0x0, + 0x2, 0x39, 0x9, 0x82, 0x10, 0xc4, 0xf, 0xff, + 0xe0, 0x20, 0x1, 0xbc, 0x7c, 0x7, + + /* U+7AA8 "窨" */ + 0x1, 0x0, 0x2, 0x3, 0xff, 0xfc, 0x0, 0x11, + 0x93, 0x1c, 0x21, 0xcf, 0xfe, 0x4, 0x10, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xe1, 0x0, 0x43, 0xff, + 0x84, 0x1, 0xf, 0xfe, 0x10, 0x4, + + /* U+7AAC "窬" */ + 0x1, 0x0, 0x2, 0x3, 0xff, 0xfc, 0x0, 0x19, + 0xd7, 0x3c, 0x51, 0x83, 0x18, 0x1b, 0xee, 0xc0, + 0x2, 0xfc, 0x11, 0x9, 0x23, 0xf2, 0x44, 0x24, + 0x8f, 0xc9, 0x10, 0x82, 0x27, 0x3c, + + /* U+7AAD "窭" */ + 0x0, 0x0, 0x4, 0xf, 0xff, 0xe0, 0x1, 0x18, + 0x71, 0xc8, 0xa0, 0xa4, 0x3f, 0xff, 0x6, 0x80, + 0xe1, 0xcc, 0x40, 0xff, 0xff, 0x10, 0x40, 0x72, + 0x0, 0x7e, 0x3e, 0x6, + + /* U+7AB3 "窳" */ + 0x0, 0x80, 0x0, 0x80, 0x7f, 0xfe, 0x40, 0x2, + 0x4e, 0x3a, 0x10, 0x0, 0x1, 0x2, 0x3e, 0x7c, + 0x2a, 0x54, 0x2a, 0x54, 0x2a, 0x54, 0x29, 0x54, + 0x29, 0x5a, 0x4a, 0x6a, 0x56, 0xae, 0x5a, 0xb5, + + /* U+7ABF "窿" */ + 0x0, 0x0, 0x4, 0xf, 0xff, 0xe1, 0x21, 0xb8, + 0x75, 0xcf, 0xe5, 0x51, 0x18, 0x38, 0x6f, 0x9d, + 0x8f, 0xc5, 0x0, 0x14, 0xfe, 0x54, 0x41, 0xcf, + 0xe4, 0x4, 0x13, 0xff, + + /* U+7ACB "立" */ + 0x1, 0x0, 0x2, 0x0, 0x2, 0x7, 0xff, 0xf0, + 0x0, 0x4, 0x1, 0x4, 0x4, 0x8, 0x8, 0x8, + 0x20, 0x10, 0x40, 0x21, 0x0, 0x22, 0x0, 0x8, + 0x1f, 0xff, 0xc0, + + /* U+7AD6 "竖" */ + 0x8, 0x0, 0x97, 0xf9, 0x22, 0x22, 0x42, 0x84, + 0x86, 0x9, 0x33, 0x92, 0x80, 0x80, 0x40, 0x0, + 0x80, 0x7f, 0xf8, 0x20, 0x40, 0x61, 0x0, 0x44, + 0x1f, 0xff, 0xc0, + + /* U+7AD9 "站" */ + 0x20, 0x20, 0x40, 0x40, 0x0, 0x87, 0xe1, 0xf0, + 0x2, 0x10, 0x4, 0x12, 0x8, 0x24, 0x10, 0x49, + 0xfc, 0xa2, 0x8, 0x44, 0x10, 0xa8, 0x2f, 0x90, + 0x40, 0x3f, 0x80, 0x41, 0x0, + + /* U+7ADE "竞" */ + 0x1, 0x0, 0x2, 0x1, 0xff, 0xf0, 0x80, 0x80, + 0x82, 0x1f, 0xff, 0xc0, 0x0, 0x0, 0x0, 0x3f, + 0xf8, 0x40, 0x10, 0x80, 0x21, 0xff, 0xc0, 0x48, + 0x0, 0x90, 0x46, 0x10, 0xf0, 0x3e, + + /* U+7ADF "竟" */ + 0x1, 0x0, 0x2, 0x1, 0xff, 0xf0, 0xc1, 0x80, + 0x82, 0x1f, 0xff, 0xc0, 0x0, 0x1f, 0xfc, 0x20, + 0x8, 0x7f, 0xf0, 0x80, 0x21, 0xff, 0xc0, 0x48, + 0x21, 0x90, 0x46, 0x10, 0xf8, 0x3e, + + /* U+7AE0 "章" */ + 0x1, 0x0, 0xff, 0xf8, 0x20, 0x80, 0x43, 0xf, + 0xff, 0xe0, 0x0, 0xf, 0xfe, 0x10, 0x4, 0x3f, + 0xf8, 0x40, 0x10, 0xff, 0xe0, 0x8, 0xf, 0xff, + 0xe0, 0x20, 0x0, 0x40, 0x0, + + /* U+7AE3 "竣" */ + 0x0, 0x0, 0x40, 0x80, 0x82, 0x67, 0xc8, 0x60, + 0x3f, 0xe0, 0x0, 0x62, 0x66, 0x49, 0x3, 0x54, + 0x80, 0xa3, 0xf9, 0x44, 0x21, 0x34, 0x42, 0x85, + 0x1e, 0xc, 0x0, 0x67, 0x3, 0x1, + + /* U+7AE5 "童" */ + 0x1, 0x0, 0x2, 0x1, 0xff, 0xf0, 0x41, 0x0, + 0x84, 0xe, 0xf7, 0x8f, 0xfe, 0x10, 0x84, 0x21, + 0x8, 0x7f, 0xf0, 0x84, 0x21, 0xff, 0xc0, 0x10, + 0x7, 0xff, 0x3f, 0xff, 0x80, + + /* U+7AE6 "竦" */ + 0x20, 0x40, 0x40, 0x80, 0xf, 0xff, 0xc2, 0x0, + 0x4, 0x0, 0x7f, 0xa2, 0x91, 0x29, 0x22, 0x53, + 0xfc, 0xa1, 0xc1, 0x45, 0x41, 0xa, 0x4f, 0xa4, + 0x40, 0x88, 0x40, 0x10, 0x0, + + /* U+7AED "竭" */ + 0x0, 0x0, 0x47, 0xf8, 0x88, 0x10, 0x10, 0x2f, + 0xbf, 0xc0, 0x40, 0xa2, 0xff, 0x48, 0x80, 0x53, + 0xfe, 0xac, 0x5, 0x49, 0x8b, 0x14, 0x92, 0x20, + 0x27, 0x7e, 0xb0, 0x1, 0x0, 0x1e, + + /* U+7AEF "端" */ + 0x20, 0x40, 0x44, 0x90, 0x9, 0x27, 0xdf, 0xc0, + 0x0, 0x11, 0x0, 0x25, 0xff, 0xc8, 0x20, 0x53, + 0xfc, 0xa5, 0x29, 0x4a, 0x51, 0x54, 0xaf, 0x29, + 0x40, 0x52, 0x80, 0x87, 0x0, + + /* U+7AF9 "竹" */ + 0x10, 0x20, 0x10, 0x20, 0x20, 0x40, 0x3e, 0x7f, + 0x50, 0x88, 0x91, 0x8, 0x11, 0x8, 0x10, 0x8, + 0x10, 0x8, 0x10, 0x8, 0x10, 0x8, 0x10, 0x8, + 0x10, 0x8, 0x10, 0x8, 0x10, 0x78, + + /* U+7AFA "竺" */ + 0x10, 0x20, 0x20, 0x40, 0x81, 0x1, 0xf3, 0xf4, + 0x8, 0x9, 0x12, 0x21, 0x42, 0x0, 0x0, 0x0, + 0x0, 0xff, 0xf8, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x1f, 0xff, 0xc0, + + /* U+7AFD "竽" */ + 0x10, 0x20, 0x40, 0x80, 0xfd, 0xfa, 0x4, 0x4, + 0x89, 0x11, 0x22, 0x0, 0x0, 0x1f, 0xfc, 0x1, + 0x0, 0x2, 0x3, 0xff, 0xf8, 0x8, 0x0, 0x10, + 0x0, 0x20, 0x0, 0x40, 0x7, 0x80, + + /* U+7AFF "竿" */ + 0x10, 0x20, 0x20, 0x80, 0xfd, 0xf9, 0x4, 0x4, + 0x89, 0x10, 0xa3, 0x0, 0x0, 0x3f, 0xfe, 0x1, + 0x0, 0x2, 0x0, 0x4, 0x7, 0xff, 0xf0, 0x10, + 0x0, 0x20, 0x0, 0x40, 0x0, 0x80, + + /* U+7B03 "笃" */ + 0x10, 0x20, 0x1f, 0x7f, 0x20, 0x40, 0x44, 0x88, + 0x0, 0x0, 0x3f, 0xf8, 0x0, 0x8, 0x10, 0x8, + 0x10, 0x8, 0x1f, 0xfe, 0x0, 0x2, 0x7f, 0xf2, + 0x0, 0x2, 0x0, 0x2, 0x0, 0x7c, + + /* U+7B04 "笄" */ + 0x10, 0x20, 0x40, 0x80, 0xf9, 0xfa, 0x4, 0x8, + 0x91, 0x0, 0x0, 0x1f, 0xff, 0x4, 0x10, 0x8, + 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x1, 0x2, + 0x2, 0x4, 0x8, 0x8, 0x20, 0x10, + + /* U+7B06 "笆" */ + 0x10, 0x0, 0x10, 0x20, 0x3f, 0x7f, 0x68, 0xd0, + 0x44, 0x88, 0x0, 0x0, 0x3f, 0xfc, 0x21, 0x4, + 0x21, 0x4, 0x21, 0x4, 0x3f, 0xfc, 0x20, 0x0, + 0x20, 0x1, 0x20, 0x2, 0x1f, 0xfe, + + /* U+7B08 "笈" */ + 0x10, 0x0, 0x20, 0x40, 0xfd, 0xfb, 0x44, 0x4, + 0x49, 0x80, 0x1, 0x1f, 0xfc, 0x4, 0x18, 0x8, + 0x60, 0x18, 0xf0, 0x48, 0x20, 0x90, 0x81, 0x12, + 0x4, 0x18, 0x10, 0x6c, 0x4f, 0x7, + + /* U+7B0A "笊" */ + 0x10, 0x20, 0x60, 0x80, 0xfd, 0xfa, 0x4, 0x8, + 0x99, 0x0, 0x0, 0xf, 0xff, 0x10, 0x88, 0x21, + 0x10, 0x42, 0x20, 0x84, 0x41, 0x8, 0x44, 0x10, + 0x98, 0x20, 0xa0, 0x40, 0x80, + + /* U+7B0B "笋" */ + 0x10, 0x20, 0x20, 0x80, 0xfd, 0xfb, 0x44, 0xc, + 0xc9, 0x0, 0x0, 0xf, 0xfe, 0x2, 0x4, 0x4, + 0x9, 0xff, 0xfc, 0x10, 0x23, 0xff, 0xc0, 0x40, + 0x81, 0x0, 0xc, 0x0, 0x70, 0x0, + + /* U+7B0F "笏" */ + 0x10, 0x20, 0x40, 0x80, 0xfd, 0xfa, 0x44, 0x88, + 0x40, 0x82, 0x0, 0xf, 0xff, 0x22, 0x22, 0x84, + 0x44, 0x10, 0x88, 0x22, 0x10, 0x84, 0x26, 0x10, + 0x50, 0x60, 0x81, 0x83, 0xc, 0x7c, 0x0, 0x0, + + /* U+7B11 "笑" */ + 0x0, 0x0, 0x40, 0x80, 0xfd, 0xfa, 0x4, 0xd, + 0x19, 0x11, 0x22, 0x0, 0x1, 0x3f, 0xfe, 0x1, + 0x0, 0x2, 0x3, 0xff, 0xf8, 0x18, 0x0, 0x28, + 0x0, 0x88, 0x6, 0xc, 0x70, 0x3, + + /* U+7B14 "笔" */ + 0x20, 0x40, 0x40, 0x81, 0xf9, 0xfa, 0x85, 0x8, + 0x91, 0x0, 0x1, 0x1f, 0xfc, 0x1, 0x0, 0x2, + 0x0, 0xff, 0xf0, 0x8, 0x0, 0x10, 0xf, 0xff, + 0xc0, 0x40, 0x80, 0x81, 0x0, 0xfc, + + /* U+7B15 "笕" */ + 0x0, 0x0, 0x40, 0x80, 0xf9, 0xfa, 0x4, 0x4, + 0x89, 0x10, 0x0, 0xf, 0xfe, 0x10, 0x4, 0x23, + 0x8, 0x46, 0x10, 0x8c, 0x21, 0x1c, 0x40, 0x78, + 0x21, 0xb0, 0x46, 0x60, 0xf0, 0x7e, + + /* U+7B19 "笙" */ + 0x20, 0x40, 0x40, 0x81, 0xfb, 0xfa, 0x4, 0x8, + 0x91, 0x0, 0x1, 0x8, 0x40, 0x10, 0x80, 0x7f, + 0xfc, 0x82, 0x2, 0x4, 0x1, 0xff, 0xe0, 0x10, + 0x0, 0x20, 0x3f, 0xff, 0x80, + + /* U+7B1B "笛" */ + 0x18, 0x20, 0x10, 0x20, 0x1f, 0x7f, 0x20, 0x40, + 0x4c, 0x90, 0x4, 0x8, 0x0, 0x80, 0x3f, 0xfc, + 0x20, 0x84, 0x20, 0x84, 0x20, 0x84, 0x3f, 0xfc, + 0x20, 0x84, 0x20, 0x84, 0x3f, 0xfc, 0x20, 0x4, + + /* U+7B1E "笞" */ + 0x10, 0x20, 0x20, 0x80, 0xfd, 0xfa, 0x4, 0x8, + 0x81, 0x0, 0x40, 0x3, 0x8, 0x18, 0x8, 0x7f, + 0xf8, 0x0, 0xc, 0x0, 0x1, 0xff, 0xc2, 0x0, + 0x84, 0x1, 0xf, 0xfe, 0x10, 0x4, + + /* U+7B20 "笠" */ + 0x0, 0x0, 0x40, 0x80, 0xfd, 0xfb, 0x4, 0x4, + 0x8b, 0x11, 0x2, 0x0, 0x40, 0x0, 0x80, 0xff, + 0xfe, 0x20, 0x0, 0x40, 0x40, 0x41, 0x0, 0x84, + 0x0, 0x8, 0x3f, 0xff, 0x80, + + /* U+7B24 "笤" */ + 0x10, 0x40, 0x7e, 0xfd, 0x2, 0x2, 0x48, 0x80, + 0x0, 0xf, 0xff, 0x1, 0x2, 0x6, 0x4, 0x18, + 0xf0, 0xc0, 0x0, 0xff, 0xe1, 0x0, 0x42, 0x0, + 0x87, 0xff, 0x8, 0x2, 0x0, + + /* U+7B25 "笥" */ + 0x0, 0x40, 0x40, 0x80, 0xfb, 0xfa, 0x8c, 0x88, + 0x80, 0x8f, 0xff, 0x80, 0x1, 0x0, 0x2, 0xff, + 0xf4, 0x0, 0x8, 0xff, 0x12, 0x2, 0x24, 0x4, + 0x47, 0xf8, 0x80, 0x1, 0x0, 0x3c, + + /* U+7B26 "符" */ + 0x8, 0x20, 0x20, 0x80, 0x7d, 0xf9, 0x2, 0x86, + 0x49, 0x8, 0x91, 0x2, 0x0, 0x8, 0x8, 0x17, + 0xfe, 0x60, 0x21, 0x48, 0x40, 0x88, 0x81, 0x9, + 0x2, 0x2, 0x4, 0x4, 0x8, 0x38, + + /* U+7B28 "笨" */ + 0x10, 0x20, 0x40, 0x80, 0xfd, 0xfa, 0x4, 0x4, + 0x89, 0x10, 0x82, 0x0, 0x40, 0x3f, 0xfe, 0x5, + 0x80, 0xa, 0x80, 0x64, 0xc1, 0x88, 0xec, 0xfe, + 0x60, 0x20, 0x0, 0x40, 0x0, 0x80, + + /* U+7B2A "笪" */ + 0x10, 0x0, 0x40, 0x80, 0xfd, 0xfa, 0x4, 0x8, + 0x91, 0x0, 0x0, 0xf, 0xfe, 0x10, 0x4, 0x20, + 0x8, 0x7f, 0xf0, 0x80, 0x21, 0x0, 0x43, 0xff, + 0x80, 0x0, 0x3f, 0xff, 0x80, + + /* U+7B2B "笫" */ + 0x20, 0x40, 0x7e, 0xfd, 0x2, 0x2, 0x4d, 0x8, + 0x81, 0xf, 0xff, 0x10, 0x40, 0x20, 0x80, 0x41, + 0x0, 0xff, 0xf8, 0xc, 0x10, 0x68, 0x23, 0x13, + 0xd8, 0x20, 0x0, 0x40, 0x0, + + /* U+7B2C "第" */ + 0x0, 0x0, 0x10, 0x20, 0x3f, 0xff, 0x68, 0x50, + 0x44, 0x8, 0x3f, 0xfc, 0x0, 0x84, 0x0, 0x84, + 0x3f, 0xfc, 0x20, 0x80, 0x3f, 0xfe, 0x1, 0x82, + 0x6, 0x82, 0x8, 0x82, 0x30, 0x9c, 0x40, 0x80, + + /* U+7B2E "笮" */ + 0x0, 0x0, 0x40, 0x80, 0xfd, 0xfa, 0x44, 0x88, + 0x10, 0x82, 0x0, 0x7, 0xff, 0x92, 0x0, 0x24, + 0x0, 0x8f, 0xf0, 0x10, 0x0, 0x20, 0x0, 0x7f, + 0xc0, 0x80, 0x1, 0x0, 0x2, 0x0, + + /* U+7B31 "笱" */ + 0x20, 0x40, 0x7d, 0xfd, 0x2, 0x4, 0x68, 0xc1, + 0x0, 0x7, 0xff, 0x90, 0x1, 0x40, 0x2, 0x3f, + 0x84, 0x41, 0x8, 0x82, 0x11, 0x4, 0x23, 0xf8, + 0x40, 0x1, 0x80, 0x3e, 0x0, + + /* U+7B33 "笳" */ + 0x10, 0x20, 0x10, 0x40, 0x3f, 0x7f, 0x20, 0x80, + 0x44, 0x88, 0x94, 0x0, 0x10, 0x0, 0x7e, 0x7e, + 0x12, 0x42, 0x12, 0x42, 0x12, 0x42, 0x12, 0x42, + 0x22, 0x42, 0x22, 0x7e, 0x62, 0x42, 0xdc, 0x42, + + /* U+7B38 "笸" */ + 0x10, 0x20, 0x20, 0x40, 0xfd, 0xf9, 0x42, 0x84, + 0x88, 0x80, 0x0, 0xf, 0xff, 0x90, 0x0, 0x27, + 0xf8, 0x48, 0x10, 0x90, 0x21, 0x20, 0x42, 0x7f, + 0x84, 0x0, 0xf, 0xff, 0x80, + + /* U+7B3A "笺" */ + 0x0, 0x20, 0x10, 0x20, 0x3f, 0x7e, 0x28, 0x80, + 0x45, 0x88, 0x0, 0x40, 0x4, 0x0, 0x7f, 0xfc, + 0x2, 0x0, 0x2, 0x0, 0x7f, 0xfe, 0x1, 0x8, + 0x1, 0x30, 0x1, 0xc2, 0x1e, 0x62, 0x60, 0x1c, + + /* U+7B3C "笼" */ + 0x0, 0x0, 0x40, 0x80, 0xfd, 0xfa, 0x4, 0x88, + 0xd8, 0x80, 0x8, 0x1, 0x8, 0x7f, 0xff, 0x4, + 0x80, 0x11, 0x0, 0x22, 0x20, 0x45, 0x81, 0xc, + 0x26, 0x70, 0x59, 0x20, 0xe0, 0x7e, + + /* U+7B3E "笾" */ + 0x10, 0x20, 0x20, 0x40, 0x3f, 0x7e, 0x40, 0x80, + 0x88, 0x90, 0x40, 0x80, 0x20, 0x80, 0x7, 0xfc, + 0xe0, 0x84, 0x20, 0x84, 0x21, 0x4, 0x21, 0x4, + 0x22, 0xc, 0x24, 0x78, 0x58, 0x0, 0x8f, 0xfe, + + /* U+7B45 "筅" */ + 0x20, 0x40, 0x41, 0x1, 0xfb, 0xf2, 0x9, 0x8, + 0x93, 0x2, 0x20, 0x8, 0x40, 0x1f, 0xfc, 0x41, + 0x0, 0x82, 0x3, 0xff, 0xf0, 0x44, 0x0, 0x88, + 0x42, 0x10, 0x8c, 0x21, 0x70, 0x3e, + + /* U+7B47 "筇" */ + 0x10, 0x20, 0x10, 0x40, 0x3f, 0x7f, 0x20, 0x90, + 0x48, 0x90, 0x85, 0x8, 0x0, 0x0, 0x7e, 0xfc, + 0x10, 0x84, 0x10, 0x88, 0x10, 0x88, 0x10, 0x84, + 0x10, 0x82, 0x3e, 0x82, 0x40, 0x9c, 0x0, 0x80, + + /* U+7B49 "等" */ + 0x10, 0x40, 0x40, 0x80, 0xfb, 0xfa, 0x89, 0x8, + 0xa1, 0x0, 0x40, 0x1f, 0xff, 0x1, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0x0, 0x87, 0xff, 0xe1, 0x2, + 0x1, 0x4, 0x0, 0x8, 0x0, 0xe0, + + /* U+7B4B "筋" */ + 0x10, 0x0, 0x40, 0x80, 0xf9, 0xfa, 0x4, 0x8, + 0x99, 0x0, 0x2a, 0x1f, 0x10, 0x23, 0xfe, 0x7c, + 0x44, 0x88, 0x89, 0x11, 0x13, 0xe2, 0x24, 0x48, + 0x48, 0x90, 0xb1, 0x41, 0x4f, 0x1c, + + /* U+7B4C "筌" */ + 0x10, 0x20, 0x10, 0x20, 0x3f, 0x7f, 0x20, 0x40, + 0x48, 0x8, 0x1, 0x88, 0x6, 0x60, 0x18, 0x18, + 0x6f, 0xf6, 0x1, 0x0, 0x1, 0x0, 0x3f, 0xfc, + 0x1, 0x0, 0x1, 0x0, 0x7f, 0xfe, + + /* U+7B4F "筏" */ + 0x10, 0x0, 0x40, 0x80, 0xf9, 0xfa, 0x5, 0x8, + 0x91, 0x0, 0x20, 0x4, 0x4c, 0x18, 0x82, 0x27, + 0xf8, 0xc1, 0x2, 0x82, 0x21, 0x5, 0x82, 0xc, + 0x4, 0x68, 0x89, 0x9, 0x10, 0xe, + + /* U+7B50 "筐" */ + 0x0, 0x0, 0x40, 0x81, 0xfb, 0xfe, 0x89, 0x8, + 0x81, 0xf, 0xff, 0x90, 0x0, 0x2f, 0xfc, 0x41, + 0x0, 0x82, 0x1, 0x7f, 0xe2, 0x8, 0x5, 0xff, + 0x88, 0x0, 0x1f, 0xff, 0x80, + + /* U+7B51 "筑" */ + 0x0, 0x20, 0x10, 0x20, 0x1f, 0x7f, 0x20, 0x40, + 0x6c, 0x90, 0x44, 0x88, 0x0, 0x0, 0x7d, 0xf8, + 0x12, 0x8, 0x12, 0x8, 0x12, 0x48, 0x12, 0x28, + 0x16, 0x29, 0x7a, 0x9, 0x2, 0x9, 0x4, 0xe, + + /* U+7B52 "筒" */ + 0x10, 0x0, 0x81, 0x3, 0xff, 0xd2, 0x20, 0x80, + 0x11, 0xff, 0xe4, 0x0, 0x90, 0x2, 0x4f, 0xe9, + 0x0, 0x24, 0xfe, 0x92, 0xa, 0x48, 0x29, 0x3f, + 0xa4, 0x0, 0x90, 0xe, + + /* U+7B54 "答" */ + 0x8, 0x10, 0x10, 0x20, 0x3f, 0x3f, 0x20, 0x40, + 0x48, 0x8, 0x5, 0x88, 0x2, 0x40, 0x4, 0x20, + 0x18, 0x18, 0xef, 0xf7, 0x0, 0x0, 0x1f, 0xf8, + 0x10, 0x8, 0x10, 0x8, 0x1f, 0xf8, 0x10, 0x8, + + /* U+7B56 "策" */ + 0x10, 0x40, 0x81, 0x3, 0xef, 0xd0, 0x28, 0x99, + 0x20, 0x8, 0xf, 0xff, 0xc0, 0x80, 0x7f, 0xf9, + 0x8, 0x24, 0x30, 0x91, 0xae, 0xa, 0x40, 0x48, + 0xce, 0x20, 0xc0, 0x80, + + /* U+7B58 "筘" */ + 0x10, 0x20, 0x10, 0x20, 0x3f, 0x7f, 0x20, 0x40, + 0x48, 0x90, 0x4, 0x8, 0x8, 0x0, 0x7e, 0xfe, + 0x8, 0x82, 0x8, 0x82, 0xe, 0x82, 0x78, 0x82, + 0x8, 0x82, 0x8, 0xfe, 0x10, 0x82, 0x70, 0x82, + + /* U+7B5A "筚" */ + 0x10, 0x40, 0x7e, 0xfc, 0xa2, 0x42, 0x24, 0x42, + 0x8, 0x7, 0xd7, 0x88, 0x30, 0x90, 0x41, 0x26, + 0xc2, 0x70, 0xf8, 0x4, 0x3, 0xff, 0xf0, 0x10, + 0x0, 0x20, 0x0, 0x40, 0x0, + + /* U+7B5B "筛" */ + 0x10, 0x20, 0x20, 0x80, 0xfd, 0xfb, 0x44, 0xc, + 0x99, 0x0, 0x0, 0x2, 0xff, 0xa4, 0x10, 0x49, + 0xfc, 0x94, 0x49, 0x28, 0x92, 0x51, 0x20, 0xa2, + 0x42, 0x5, 0x8c, 0x8, 0x30, 0x10, + + /* U+7B5D "筝" */ + 0x0, 0x40, 0x40, 0x81, 0xfb, 0xfa, 0x44, 0x88, + 0x80, 0x83, 0xfc, 0x8, 0x10, 0x3f, 0xfc, 0x2, + 0x8, 0x4, 0x13, 0xff, 0xf8, 0x10, 0x47, 0xff, + 0x80, 0x40, 0x0, 0x80, 0xf, 0x0, + + /* U+7B60 "筠" */ + 0x20, 0x40, 0x40, 0x81, 0xfb, 0xfa, 0x5, 0x8, + 0x91, 0x4, 0x22, 0x8, 0x40, 0x11, 0xfe, 0xfa, + 0x4, 0x4b, 0x8, 0x81, 0x11, 0x0, 0xa2, 0xe, + 0x47, 0x60, 0xb0, 0x3, 0x0, 0x7c, + + /* U+7B62 "筢" */ + 0x20, 0x40, 0x7d, 0xfd, 0x2, 0x4, 0x68, 0xc2, + 0x0, 0x4, 0x7f, 0xbe, 0x91, 0x11, 0x22, 0x22, + 0x44, 0x77, 0xfb, 0x88, 0x1, 0x10, 0x12, 0x20, + 0x24, 0x40, 0x78, 0xff, 0x0, + + /* U+7B6E "筮" */ + 0x10, 0x20, 0x40, 0x80, 0xfd, 0xfa, 0x4, 0x8, + 0x91, 0x0, 0x0, 0x1f, 0xff, 0x0, 0x80, 0x11, + 0x10, 0x22, 0x60, 0xa4, 0xa2, 0x2a, 0x20, 0x10, + 0x0, 0x20, 0x3f, 0xff, 0x80, + + /* U+7B71 "筱" */ + 0x10, 0x0, 0x20, 0x40, 0x3f, 0x7e, 0x40, 0x90, + 0xcc, 0x88, 0x90, 0x40, 0x10, 0x40, 0x24, 0xfe, + 0x64, 0x88, 0xa5, 0x88, 0xa4, 0x48, 0x24, 0x50, + 0x24, 0x30, 0x24, 0x78, 0x21, 0x86, 0x23, 0x0, + + /* U+7B72 "筲" */ + 0x10, 0x20, 0x7f, 0x7d, 0x91, 0x40, 0x4, 0x1, + 0x89, 0x80, 0x10, 0xf, 0xff, 0x10, 0x2, 0x20, + 0x4, 0x7f, 0xf8, 0x80, 0x11, 0xff, 0xe2, 0x0, + 0x44, 0x0, 0x88, 0xf, 0x0, + + /* U+7B75 "筵" */ + 0x8, 0x20, 0x10, 0x40, 0x3f, 0x7e, 0x20, 0x80, + 0x44, 0x90, 0x0, 0x0, 0x7d, 0xfe, 0x8, 0x20, + 0x31, 0x20, 0x7d, 0x3c, 0x5, 0x20, 0x4d, 0x20, + 0x29, 0xfe, 0x10, 0x0, 0x2f, 0xfe, 0x40, 0x0, + + /* U+7B77 "筷" */ + 0x0, 0x40, 0x40, 0x80, 0xfb, 0xfa, 0x44, 0x8, + 0x91, 0x4, 0x10, 0x8, 0x20, 0x13, 0xfc, 0xa8, + 0x89, 0x41, 0x10, 0x9f, 0xf9, 0x6, 0x2, 0x14, + 0x4, 0x64, 0x9, 0x86, 0x12, 0x3, + + /* U+7B79 "筹" */ + 0x0, 0x40, 0x40, 0x81, 0xfb, 0xfa, 0x84, 0x88, + 0x20, 0x1f, 0xff, 0xc1, 0x0, 0x3f, 0xfe, 0x4, + 0x1, 0xff, 0xfc, 0x20, 0x0, 0xff, 0xf2, 0x1, + 0x8, 0x82, 0x20, 0x84, 0x0, 0xf0, + + /* U+7B7B "筻" */ + 0x10, 0x40, 0x3f, 0x7e, 0x48, 0x90, 0x4, 0x8, + 0x7f, 0xfe, 0x0, 0x80, 0x3f, 0xfc, 0x20, 0x84, + 0x3f, 0xfc, 0x21, 0x4, 0x3f, 0xfc, 0x11, 0x4, + 0xa, 0x0, 0xf, 0x0, 0xf0, 0x7e, + + /* U+7B7E "签" */ + 0x10, 0x20, 0x10, 0x20, 0x3f, 0x7e, 0x60, 0x80, + 0x48, 0x98, 0x1, 0x80, 0x6, 0x60, 0x18, 0x18, + 0xef, 0xf6, 0x0, 0x0, 0x11, 0x8, 0x11, 0x8, + 0x10, 0x90, 0x0, 0x20, 0x7f, 0xfe, + + /* U+7B80 "简" */ + 0x20, 0x40, 0xff, 0xfd, 0x42, 0xc5, 0x40, 0x2, + 0x7f, 0xc2, 0x0, 0x80, 0x1, 0x27, 0xf2, 0x48, + 0x24, 0x9f, 0xc9, 0x20, 0x92, 0x41, 0x24, 0xfe, + 0x48, 0x0, 0x90, 0xf, 0x0, + + /* U+7B85 "箅" */ + 0x0, 0x0, 0x40, 0x80, 0xff, 0xfa, 0x44, 0x88, + 0x80, 0x7, 0xff, 0x10, 0x42, 0x1f, 0xfc, 0x41, + 0x8, 0x82, 0x10, 0xff, 0xe0, 0x0, 0xf, 0xff, + 0xe1, 0x4, 0x4, 0x8, 0x30, 0x10, + + /* U+7B8D "箍" */ + 0x8, 0x20, 0x20, 0x40, 0x7f, 0xf9, 0x2, 0x4, + 0x48, 0x82, 0x0, 0x4, 0x7f, 0xbe, 0x88, 0x11, + 0x7e, 0x22, 0xa4, 0x75, 0x4b, 0x8a, 0x91, 0x15, + 0x62, 0x22, 0x4, 0x7f, 0xb8, 0x0, + + /* U+7B90 "箐" */ + 0x20, 0x40, 0x7e, 0xff, 0x22, 0x44, 0x8, 0x4f, + 0xff, 0xe0, 0x20, 0xf, 0xfe, 0x7f, 0xff, 0x0, + 0x0, 0x7f, 0xf0, 0xff, 0xe1, 0x0, 0x43, 0xff, + 0x84, 0x1, 0x8, 0x1e, 0x0, + + /* U+7B94 "箔" */ + 0x0, 0x20, 0x10, 0x20, 0x1f, 0x7f, 0x20, 0x40, + 0x44, 0x88, 0x0, 0x20, 0x30, 0x40, 0xb, 0xfe, + 0x2, 0x2, 0x62, 0x2, 0x3, 0xfe, 0xa, 0x2, + 0x12, 0x2, 0x22, 0x2, 0x23, 0xfe, 0x42, 0x2, + + /* U+7B95 "箕" */ + 0x0, 0x0, 0x40, 0x80, 0xf9, 0xfa, 0x44, 0x88, + 0x10, 0x2, 0x2, 0x3f, 0xff, 0x88, 0x8, 0x1f, + 0xf0, 0x20, 0x20, 0x7f, 0xc0, 0x80, 0x8f, 0xff, + 0xe0, 0x8, 0xe, 0xe, 0x20, 0x2, + + /* U+7B97 "算" */ + 0x20, 0x40, 0x7f, 0xfd, 0x22, 0x86, 0x0, 0x8b, + 0xff, 0x84, 0x1, 0xf, 0xfe, 0x1f, 0xfc, 0x20, + 0x8, 0x40, 0x10, 0x7f, 0xc7, 0xff, 0xf0, 0x82, + 0x2, 0x4, 0x18, 0x8, 0x0, + + /* U+7B9C "箜" */ + 0x0, 0x20, 0x20, 0x40, 0xfd, 0xf9, 0x42, 0x8c, + 0xd0, 0x80, 0x20, 0x1f, 0xff, 0xa0, 0x1, 0x4c, + 0x32, 0x60, 0x1d, 0x7f, 0xe0, 0x8, 0x0, 0x10, + 0x0, 0x20, 0x1f, 0xff, 0x80, + + /* U+7B9D "箝" */ + 0x0, 0x0, 0x40, 0x80, 0xfb, 0xfa, 0x4, 0x8, + 0x92, 0x11, 0x24, 0x8, 0x82, 0x7f, 0xff, 0x22, + 0x8, 0x44, 0x11, 0xef, 0xe5, 0x10, 0x42, 0x20, + 0x84, 0x41, 0x8, 0xfe, 0x71, 0x4, + + /* U+7BA1 "管" */ + 0x10, 0x20, 0x3f, 0x7f, 0x68, 0x50, 0xc5, 0x8, + 0x7f, 0xfe, 0x40, 0x2, 0x5f, 0xfa, 0x10, 0x8, + 0x10, 0x8, 0x1f, 0xf8, 0x10, 0x0, 0x1f, 0xf8, + 0x10, 0x8, 0x1f, 0xf8, 0x10, 0x8, + + /* U+7BA2 "箢" */ + 0x10, 0x20, 0x3f, 0x7e, 0x20, 0x50, 0xc4, 0x88, + 0x0, 0x80, 0x7f, 0xfe, 0x40, 0x0, 0x5f, 0x7c, + 0x11, 0x44, 0x22, 0x44, 0x7a, 0x44, 0x4c, 0x5c, + 0x4, 0x41, 0x18, 0x41, 0x60, 0x7e, + + /* U+7BA6 "箦" */ + 0x20, 0x40, 0x7e, 0xfd, 0x22, 0x44, 0x0, 0x7, + 0xff, 0xc0, 0x20, 0xf, 0xff, 0x0, 0x80, 0x7e, + 0xfc, 0x7f, 0xf0, 0x80, 0x21, 0x0, 0x42, 0x18, + 0x80, 0xcf, 0x3e, 0x1, 0x80, + + /* U+7BA7 "箧" */ + 0x20, 0x40, 0x7d, 0xfd, 0x22, 0x44, 0x20, 0x47, + 0xff, 0xe8, 0x10, 0x13, 0xff, 0x22, 0x44, 0x42, + 0x90, 0xbf, 0xf9, 0x3, 0x2, 0x19, 0xc4, 0xc0, + 0x4f, 0xff, 0xc0, + + /* U+7BA8 "箨" */ + 0x20, 0x40, 0x41, 0x1, 0xfb, 0xfc, 0x89, 0x8, + 0x91, 0x4, 0xff, 0x88, 0x82, 0x7c, 0x88, 0x20, + 0xe0, 0x4f, 0x3c, 0xc1, 0x7, 0x3f, 0xe2, 0x4, + 0x5, 0xff, 0xc8, 0x10, 0x60, 0x20, + + /* U+7BA9 "箩" */ + 0x0, 0x0, 0x40, 0x80, 0xfd, 0xfa, 0x4, 0x88, + 0x80, 0xf, 0xff, 0x91, 0x11, 0x22, 0x22, 0x7f, + 0xfc, 0x8, 0x0, 0x3f, 0xe0, 0x80, 0xc6, 0xc2, + 0x0, 0x18, 0x1, 0xc0, 0x3c, 0x0, + + /* U+7BAA "箪" */ + 0x20, 0x40, 0x7d, 0xfd, 0x22, 0x44, 0x8, 0x0, + 0x82, 0x7, 0xff, 0x90, 0x41, 0x1f, 0xfe, 0x41, + 0x4, 0x82, 0x8, 0xff, 0xf0, 0x8, 0xf, 0xff, + 0xe0, 0x20, 0x0, 0x40, 0x0, + + /* U+7BAB "箫" */ + 0x10, 0x40, 0x3f, 0x7f, 0x20, 0x90, 0x44, 0x8, + 0x3f, 0xfc, 0x0, 0x84, 0x7f, 0xff, 0x0, 0x84, + 0x3f, 0xfc, 0x0, 0x80, 0x14, 0x84, 0x24, 0xa4, + 0x24, 0x94, 0x28, 0x94, 0x40, 0x84, + + /* U+7BAC "箬" */ + 0x20, 0x40, 0x7e, 0xfd, 0x22, 0x44, 0x28, 0x40, + 0x82, 0x1f, 0xff, 0xc2, 0x8, 0x1, 0x0, 0xff, + 0xfe, 0x10, 0x0, 0x7f, 0xe3, 0x80, 0x49, 0x0, + 0x83, 0xff, 0x4, 0x2, 0x0, + + /* U+7BAD "箭" */ + 0x0, 0x0, 0x20, 0x40, 0xfd, 0xfb, 0x24, 0x88, + 0x39, 0x81, 0x84, 0x3f, 0xff, 0x80, 0x0, 0x7f, + 0x24, 0x82, 0x49, 0xfc, 0x92, 0x9, 0x27, 0xf2, + 0x48, 0x20, 0x90, 0x41, 0x23, 0x9c, + + /* U+7BB1 "箱" */ + 0x20, 0x40, 0x40, 0x81, 0xfb, 0xfa, 0x85, 0x8, + 0x91, 0x4, 0x0, 0x8, 0xff, 0x7d, 0x2, 0x23, + 0xfc, 0xe4, 0x9, 0xa8, 0x15, 0x1f, 0xea, 0x20, + 0x44, 0x40, 0x88, 0xff, 0x11, 0x2, + + /* U+7BB4 "箴" */ + 0x10, 0x20, 0x1f, 0x7f, 0x20, 0x48, 0x44, 0xa4, + 0x0, 0x28, 0x3f, 0xff, 0x20, 0x20, 0x2f, 0xa2, + 0x20, 0x24, 0x2f, 0xa4, 0x28, 0x98, 0x28, 0x91, + 0x2f, 0xa9, 0x40, 0x6a, 0xc0, 0x46, + + /* U+7BB8 "箸" */ + 0x20, 0x40, 0xff, 0xff, 0x22, 0x40, 0x10, 0x3, + 0xfe, 0x80, 0x46, 0x3f, 0xff, 0x80, 0x80, 0x6, + 0x0, 0x7f, 0xf3, 0x40, 0x20, 0xff, 0xc1, 0x0, + 0x83, 0xff, 0x4, 0x2, 0x0, + + /* U+7BC1 "篁" */ + 0x20, 0x40, 0x7e, 0xfd, 0x22, 0x4, 0x10, 0x83, + 0xff, 0x84, 0x1, 0xf, 0xfe, 0x10, 0x4, 0x3f, + 0xf8, 0x0, 0x1, 0xff, 0xf0, 0x8, 0x7, 0xff, + 0xc0, 0x20, 0x3f, 0xff, 0x80, + + /* U+7BC6 "篆" */ + 0x20, 0x81, 0xfb, 0xfc, 0x92, 0x8, 0x0, 0x3f, + 0xf0, 0xff, 0x80, 0x2, 0x3f, 0xff, 0x6, 0x1, + 0xec, 0xc8, 0xde, 0x1c, 0xa8, 0x8c, 0x91, 0xc6, + 0x38, 0xf0, 0x0, + + /* U+7BC7 "篇" */ + 0x10, 0x60, 0x7e, 0xfc, 0x80, 0x2, 0x24, 0x83, + 0xff, 0xc4, 0x0, 0x8f, 0xff, 0x10, 0x0, 0x20, + 0x0, 0x5f, 0xf8, 0xc4, 0x91, 0x7f, 0xe5, 0x12, + 0x4a, 0x24, 0xa4, 0x4b, 0x0, + + /* U+7BCC "篌" */ + 0x0, 0x0, 0x20, 0x80, 0xf9, 0xfa, 0x4, 0x88, + 0xc0, 0x82, 0x7f, 0x4, 0x2, 0x17, 0xff, 0x62, + 0x1, 0x47, 0xf8, 0x91, 0x1, 0x2, 0x2, 0xff, + 0xe4, 0x14, 0x8, 0x64, 0x17, 0x7, + + /* U+7BD1 "篑" */ + 0x10, 0x40, 0x7e, 0xfd, 0x22, 0x44, 0x20, 0x43, + 0xff, 0x84, 0x21, 0xf, 0xfe, 0x0, 0x80, 0x7e, + 0xfc, 0x7f, 0xf0, 0x80, 0x21, 0x8, 0x42, 0x1c, + 0x83, 0xc7, 0x18, 0x1, 0x0, + + /* U+7BD3 "篓" */ + 0x20, 0x80, 0xfb, 0xf4, 0x12, 0x22, 0x4, 0x32, + 0x20, 0x29, 0xf, 0xff, 0xc3, 0xb0, 0x72, 0x3a, + 0x10, 0x1f, 0xff, 0xc4, 0x8, 0x18, 0xc0, 0x1f, + 0xf, 0xc3, 0xc0, + + /* U+7BD9 "篙" */ + 0x20, 0x41, 0xfb, 0xfd, 0xa, 0x2, 0x84, 0xff, + 0xfc, 0x0, 0x3, 0xff, 0x8, 0x4, 0x1f, 0xe1, + 0xff, 0xe4, 0x0, 0x93, 0xf2, 0x4f, 0xc9, 0x0, + 0x24, 0x3, 0x80, + + /* U+7BDA "篚" */ + 0x20, 0x40, 0xfd, 0xfd, 0x2, 0x84, 0x48, 0x87, + 0xff, 0xc8, 0x48, 0x1f, 0x9f, 0x21, 0x20, 0x42, + 0x40, 0xbc, 0xf9, 0x9, 0x3, 0xf3, 0xe4, 0x24, + 0xf, 0xff, 0xc0, + + /* U+7BDD "篝" */ + 0x20, 0x40, 0xff, 0xff, 0x26, 0x40, 0x41, 0x7, + 0xff, 0xcf, 0xff, 0x82, 0x8, 0x7f, 0xff, 0x1, + 0x0, 0x7f, 0xf0, 0x84, 0x21, 0xff, 0xcf, 0xff, + 0xe4, 0x1, 0x8, 0x1e, 0x0, + + /* U+7BE1 "篡" */ + 0x10, 0x20, 0x7e, 0xfd, 0x92, 0x40, 0xff, 0xc1, + 0x0, 0x83, 0xff, 0x4, 0x2, 0xf, 0xfc, 0x1f, + 0xf8, 0x4, 0x1, 0xff, 0xf8, 0x48, 0x87, 0x64, + 0xe1, 0xfe, 0x0, 0x0, 0x0, + + /* U+7BE5 "篥" */ + 0x20, 0x81, 0xfb, 0xf9, 0x12, 0x2, 0x4, 0xff, + 0xfc, 0x12, 0x7, 0xff, 0x91, 0x22, 0x44, 0x89, + 0xff, 0xef, 0xff, 0xc1, 0xa0, 0x1a, 0x61, 0x88, + 0x78, 0x20, 0x0, + + /* U+7BE6 "篦" */ + 0x20, 0x40, 0xfc, 0xfd, 0x42, 0x84, 0x50, 0x87, + 0xff, 0x89, 0x5, 0x11, 0xf2, 0x2e, 0x34, 0x7f, + 0xf8, 0x0, 0x1, 0x2, 0x13, 0xf5, 0xc4, 0xc, + 0x29, 0xd0, 0x5c, 0x3f, 0x0, + + /* U+7BEA "篪" */ + 0x10, 0x40, 0x7e, 0xfd, 0x22, 0x40, 0x0, 0x43, + 0xff, 0xe4, 0x1f, 0x88, 0x20, 0x17, 0xff, 0x28, + 0x82, 0x57, 0xf0, 0xa1, 0xf1, 0x4f, 0x84, 0x91, + 0x2a, 0x42, 0x65, 0x87, 0x0, + + /* U+7BEE "篮" */ + 0x20, 0x40, 0x7d, 0xfd, 0x22, 0x84, 0x8, 0x80, + 0x8, 0x9, 0x1f, 0x92, 0x40, 0x25, 0x18, 0x0, + 0x0, 0x7f, 0xf0, 0x92, 0x21, 0x24, 0x42, 0x48, + 0x84, 0x91, 0x3f, 0xff, 0x80, + + /* U+7BF1 "篱" */ + 0x20, 0x40, 0x7e, 0xff, 0x2, 0x44, 0x48, 0xf, + 0xff, 0xe5, 0x89, 0x9, 0xf2, 0x14, 0x14, 0x3f, + 0xf8, 0x4, 0x1, 0xff, 0xf2, 0x22, 0x24, 0xfe, + 0x48, 0x4, 0x90, 0xf, 0x0, + + /* U+7BF7 "篷" */ + 0x0, 0x0, 0x10, 0x20, 0x3f, 0x7e, 0x48, 0x90, + 0x84, 0x80, 0x41, 0xfc, 0x23, 0x8, 0x14, 0xf0, + 0xf, 0x9e, 0xe0, 0x40, 0x23, 0xfc, 0x23, 0xfc, + 0x20, 0x40, 0x67, 0xfe, 0x58, 0x0, 0x87, 0xfe, + + /* U+7BFC "篼" */ + 0x20, 0x40, 0x7d, 0xf9, 0x2, 0x4, 0x40, 0x80, + 0x20, 0xe, 0xfb, 0x91, 0x11, 0x23, 0xe2, 0x44, + 0x44, 0xef, 0xb9, 0x0, 0x0, 0x24, 0x0, 0x48, + 0x3, 0x10, 0xb8, 0x3f, 0x0, + + /* U+7BFE "篾" */ + 0x0, 0x0, 0x81, 0x7, 0xe7, 0xd4, 0x24, 0x89, + 0x1, 0xff, 0xf4, 0x44, 0x5f, 0xff, 0x0, 0x0, + 0x4, 0x47, 0xff, 0xd0, 0x40, 0x70, 0x99, 0x31, + 0x8c, 0x7e, 0x62, 0x7, + + /* U+7C07 "簇" */ + 0x10, 0x40, 0x3f, 0x7e, 0x48, 0x80, 0x84, 0x10, + 0x10, 0x80, 0x11, 0xfe, 0x6a, 0x80, 0x40, 0xfe, + 0x79, 0x20, 0x48, 0x20, 0x4b, 0xfe, 0x48, 0x50, + 0x48, 0x50, 0xc8, 0x8c, 0xbb, 0x2, + + /* U+7C0B "簋" */ + 0x10, 0x40, 0x7e, 0xfd, 0x22, 0x44, 0x0, 0x3, + 0xff, 0x87, 0xff, 0x8, 0x2, 0x1f, 0xfc, 0x21, + 0xc, 0x5d, 0xe0, 0xc0, 0x69, 0xff, 0xc2, 0x24, + 0x9f, 0xff, 0xc0, + + /* U+7C0C "簌" */ + 0x8, 0x20, 0x10, 0x20, 0x1f, 0xfe, 0x20, 0x50, + 0x44, 0x98, 0x8, 0x20, 0x7f, 0xbe, 0x8, 0x42, + 0x3f, 0x52, 0x49, 0x54, 0x49, 0x10, 0x3f, 0x10, + 0x8, 0x28, 0x1f, 0x2c, 0x68, 0x46, 0x8, 0x80, + + /* U+7C0F "簏" */ + 0x10, 0x20, 0x3f, 0x7f, 0x68, 0x88, 0x4, 0x80, + 0x3f, 0xff, 0x22, 0x20, 0x3f, 0xfe, 0x22, 0x22, + 0x22, 0x22, 0x3f, 0xfe, 0x28, 0x20, 0x2f, 0xbc, + 0x68, 0x21, 0x49, 0xa1, 0x4e, 0x3e, + + /* U+7C16 "簖" */ + 0x10, 0x40, 0x7e, 0xfd, 0x22, 0x4, 0x28, 0x84, + 0x40, 0x28, 0x8f, 0xd5, 0x50, 0x22, 0x20, 0x5f, + 0x7e, 0x9c, 0x91, 0x55, 0x23, 0x22, 0x44, 0x44, + 0x8f, 0xd1, 0x0, 0x22, 0x0, + + /* U+7C1F "簟" */ + 0x20, 0x40, 0xff, 0xfe, 0x26, 0x47, 0xff, 0xf0, + 0x44, 0xf, 0xff, 0x91, 0x11, 0x1d, 0xdc, 0x3f, + 0xf8, 0x40, 0x10, 0xff, 0xe1, 0xff, 0xc0, 0x10, + 0x1f, 0xff, 0xc0, 0x40, 0x0, + + /* U+7C26 "簦" */ + 0x10, 0x40, 0x7e, 0xfd, 0xa2, 0x42, 0x20, 0x3, + 0xe5, 0x84, 0x4c, 0x7, 0xf, 0x18, 0xc7, 0x5f, + 0xf8, 0x20, 0x10, 0x40, 0x20, 0xff, 0xc0, 0x42, + 0xf, 0xff, 0xc0, + + /* U+7C27 "簧" */ + 0x20, 0x40, 0xfd, 0xff, 0x26, 0x40, 0x41, 0x7, + 0xff, 0xc1, 0x4, 0x3f, 0xff, 0x80, 0x80, 0x3f, + 0xf8, 0x42, 0x10, 0xff, 0xe1, 0x8, 0x43, 0xff, + 0x80, 0x80, 0x1e, 0x1f, 0x0, + + /* U+7C2A "簪" */ + 0x20, 0x40, 0xfd, 0xff, 0x26, 0x43, 0xf7, 0xe4, + 0x8a, 0xf, 0xdf, 0xc4, 0x8, 0x14, 0x29, 0x4e, + 0x97, 0x0, 0x10, 0xff, 0xe1, 0x0, 0x43, 0xff, + 0x87, 0xff, 0x8, 0x2, 0x0, + + /* U+7C38 "簸" */ + 0x20, 0x40, 0xfd, 0xfb, 0x42, 0x84, 0x48, 0x4, + 0x41, 0x1f, 0xdf, 0x91, 0x25, 0x3e, 0x48, 0x44, + 0xfc, 0xf9, 0x89, 0x14, 0xa7, 0xf9, 0x40, 0x11, + 0x8, 0xad, 0xa0, 0x20, 0x0, + + /* U+7C3F "簿" */ + 0x20, 0x40, 0x7d, 0xfd, 0x24, 0x84, 0x0, 0x44, + 0xff, 0xe4, 0x10, 0x3, 0xff, 0x64, 0x42, 0xf, + 0xfc, 0x1f, 0xf9, 0x20, 0x12, 0xff, 0xf4, 0x40, + 0x90, 0x41, 0x20, 0x1c, 0x0, + + /* U+7C40 "籀" */ + 0x20, 0x80, 0x7d, 0xfd, 0x4, 0x44, 0x40, 0x42, + 0x3f, 0xc4, 0x84, 0xbd, 0x49, 0x13, 0xd2, 0x20, + 0x48, 0xef, 0xfa, 0x91, 0x11, 0x3f, 0xe2, 0x44, + 0x44, 0xff, 0xb1, 0x1, 0x0, + + /* U+7C41 "籁" */ + 0x20, 0x40, 0x7e, 0xfd, 0x2, 0x84, 0x48, 0x81, + 0x4, 0x1f, 0xcf, 0x4, 0x62, 0x3e, 0x7e, 0x94, + 0x85, 0x29, 0x29, 0xf2, 0x50, 0x84, 0xa7, 0xc3, + 0x92, 0x48, 0x84, 0x60, 0x0, + + /* U+7C4D "籍" */ + 0x20, 0x40, 0xfb, 0xf4, 0x8a, 0x20, 0x48, 0x20, + 0x93, 0xf7, 0xf2, 0x9, 0x1e, 0xff, 0x20, 0x3, + 0xf7, 0xe2, 0x10, 0x9e, 0x7e, 0xa5, 0x8, 0x87, + 0xe2, 0x10, 0x80, + + /* U+7C73 "米" */ + 0x1, 0x0, 0xc2, 0x18, 0x84, 0x20, 0x88, 0x80, + 0x92, 0x0, 0x20, 0x3f, 0xff, 0x81, 0xc0, 0x5, + 0x40, 0x1a, 0x40, 0x24, 0xc1, 0x88, 0xc6, 0x10, + 0x70, 0x20, 0x40, 0x40, 0x0, + + /* U+7C74 "籴" */ + 0x3, 0x0, 0x5, 0x0, 0x11, 0x80, 0xc0, 0x8e, + 0x10, 0xe2, 0x22, 0x2, 0x48, 0x0, 0x80, 0xff, + 0xfe, 0x7, 0x0, 0x15, 0x0, 0xc9, 0x3, 0x11, + 0x98, 0x20, 0xc0, 0x40, 0x0, + + /* U+7C7B "类" */ + 0x2, 0x0, 0xc8, 0xc0, 0xa4, 0x0, 0x80, 0x7f, + 0xf8, 0x27, 0x3, 0x23, 0x30, 0x3, 0x2, 0x3, + 0xff, 0xf0, 0x30, 0x1, 0x20, 0x8, 0x61, 0xc0, + 0x78, 0x0, 0x0, + + /* U+7C7C "籼" */ + 0x10, 0x21, 0x28, 0x42, 0x50, 0x82, 0xc1, 0x1, + 0x12, 0x3f, 0xa4, 0x4c, 0x48, 0x98, 0x91, 0x59, + 0x22, 0xaa, 0x46, 0x44, 0x8c, 0x89, 0x11, 0x12, + 0x22, 0x3f, 0xc4, 0x0, 0x80, + + /* U+7C7D "籽" */ + 0x10, 0x0, 0x23, 0xfa, 0x50, 0x12, 0xa0, 0xc5, + 0x83, 0x2, 0x4, 0x3f, 0x8, 0x19, 0xff, 0x30, + 0x20, 0xb0, 0x41, 0x50, 0x84, 0x81, 0x9, 0x2, + 0x2, 0x4, 0x4, 0x78, 0x0, + + /* U+7C89 "粉" */ + 0x20, 0x11, 0x41, 0x22, 0xa2, 0x45, 0x48, 0x42, + 0x10, 0xc4, 0x40, 0xbe, 0x0, 0x11, 0xfc, 0x30, + 0x88, 0xd1, 0x12, 0x82, 0x25, 0x8, 0x42, 0x10, + 0x84, 0x41, 0x9, 0x1e, 0x0, + + /* U+7C91 "粑" */ + 0x20, 0x1, 0x47, 0xfa, 0xa8, 0x95, 0xd1, 0x22, + 0x22, 0x44, 0x44, 0xbe, 0x89, 0x11, 0xfe, 0x32, + 0x0, 0xd4, 0x2, 0xa8, 0x5, 0x10, 0x1a, 0x20, + 0x24, 0x40, 0x88, 0x7f, 0x0, + + /* U+7C92 "粒" */ + 0x20, 0x40, 0x40, 0x82, 0x90, 0x83, 0x5f, 0xe2, + 0x80, 0x1f, 0xa1, 0x8, 0x42, 0x10, 0x88, 0x78, + 0x90, 0xc9, 0x22, 0x82, 0x45, 0x1, 0x2, 0x2, + 0x4, 0xff, 0xc8, 0x0, 0x0, + + /* U+7C95 "粕" */ + 0x20, 0x22, 0x91, 0xa, 0x5f, 0xda, 0x41, 0x61, + 0x4, 0x84, 0x1f, 0xd0, 0x48, 0x41, 0x21, 0xfd, + 0xe4, 0x16, 0x50, 0x68, 0x41, 0xa1, 0x4, 0x87, + 0xf2, 0x10, 0x40, + + /* U+7C97 "粗" */ + 0x20, 0x0, 0x43, 0xfa, 0x94, 0x13, 0x48, 0x22, + 0x10, 0x5f, 0xbf, 0x88, 0x41, 0x10, 0x82, 0x79, + 0x4, 0xcb, 0xfa, 0x84, 0x15, 0x8, 0x22, 0x10, + 0x44, 0xff, 0xc8, 0x0, 0x0, + + /* U+7C98 "粘" */ + 0x20, 0x21, 0x48, 0x42, 0x90, 0x83, 0x41, 0xf2, + 0x2, 0x1f, 0x84, 0x8, 0x8, 0x10, 0x10, 0x61, + 0xfc, 0xf2, 0x9, 0x94, 0x15, 0x8, 0x22, 0x10, + 0x44, 0x3f, 0x88, 0x41, 0x0, + + /* U+7C9C "粜" */ + 0x1, 0x0, 0x82, 0x1, 0x4, 0x23, 0xff, 0xc4, + 0x10, 0x48, 0x20, 0x9f, 0xff, 0x0, 0x0, 0x31, + 0x8, 0x12, 0x63, 0xff, 0xf8, 0x1c, 0x0, 0xd4, + 0x3, 0x26, 0x38, 0x43, 0x80, 0x80, + + /* U+7C9D "粝" */ + 0x20, 0x0, 0x47, 0xfe, 0xa8, 0x5, 0x50, 0x7, + 0x2f, 0xe4, 0x48, 0x3e, 0x90, 0x19, 0x3e, 0x72, + 0x44, 0xd4, 0x8a, 0x89, 0x15, 0x14, 0x22, 0x48, + 0x44, 0x91, 0xa, 0x4e, 0x0, + + /* U+7C9E "粞" */ + 0x20, 0x0, 0x23, 0xff, 0xa8, 0x90, 0xb0, 0x90, + 0x23, 0xfe, 0xfa, 0x92, 0x22, 0x92, 0x22, 0x92, + 0x73, 0x12, 0x6b, 0x1e, 0xa3, 0x2, 0xa2, 0x2, + 0x22, 0x2, 0x23, 0xfe, 0x22, 0x2, + + /* U+7C9F "粟" */ + 0xff, 0xfe, 0x8, 0x81, 0xff, 0xf2, 0x22, 0x24, + 0x44, 0x4f, 0xff, 0x80, 0x0, 0x18, 0x8c, 0x9, + 0x21, 0xff, 0xfc, 0xe, 0x0, 0x6b, 0x7, 0x11, + 0xf0, 0x20, 0x0, + + /* U+7CA2 "粢" */ + 0x41, 0x0, 0x47, 0xf8, 0x9, 0x10, 0x22, 0x21, + 0xe, 0xc, 0x33, 0x23, 0x81, 0x98, 0x8c, 0x9, + 0x21, 0xff, 0xfc, 0xe, 0x0, 0x6b, 0x1, 0x11, + 0x9c, 0x20, 0xc0, 0x40, 0x0, + + /* U+7CA4 "粤" */ + 0x2, 0x0, 0xff, 0xf9, 0x24, 0x92, 0x2a, 0x25, + 0xff, 0x48, 0x70, 0x93, 0x59, 0x20, 0x2, 0x7f, + 0xfc, 0x0, 0x1, 0xff, 0xf1, 0xff, 0xc0, 0x0, + 0x80, 0x1, 0x0, 0x7c, 0x0, + + /* U+7CA5 "粥" */ + 0x0, 0x80, 0xe1, 0x3c, 0x52, 0x88, 0x96, 0x11, + 0x2c, 0x3c, 0x13, 0xa1, 0xfc, 0x40, 0xc8, 0xf2, + 0x9e, 0x25, 0x84, 0x4a, 0x88, 0xa4, 0x11, 0x8, + 0x22, 0x10, 0x78, 0x27, 0x0, + + /* U+7CAA "粪" */ + 0x21, 0x0, 0x22, 0x30, 0x24, 0x87, 0xff, 0xf0, + 0x5c, 0x1, 0x26, 0x1c, 0x43, 0x84, 0x10, 0x7f, + 0xfc, 0x10, 0x40, 0x20, 0x87, 0xff, 0xf0, 0x0, + 0x3, 0x7, 0x18, 0x1, 0x80, + + /* U+7CAE "粮" */ + 0x20, 0x41, 0x57, 0xfa, 0xa8, 0x15, 0x90, 0x22, + 0x3f, 0xdf, 0x40, 0x88, 0x81, 0x19, 0xfe, 0x72, + 0x80, 0xd4, 0x8a, 0x89, 0x65, 0x11, 0x2, 0x21, + 0x4, 0x71, 0x88, 0x80, 0x80, + + /* U+7CB1 "粱" */ + 0x0, 0x0, 0x6f, 0xf0, 0x2, 0x26, 0x14, 0x40, + 0x48, 0xc2, 0x31, 0x48, 0xc4, 0x23, 0x38, 0x84, + 0x0, 0x22, 0x63, 0xff, 0xf8, 0x2c, 0x0, 0x94, + 0x2, 0x26, 0x38, 0x43, 0x80, 0x80, + + /* U+7CB2 "粲" */ + 0x4, 0x0, 0x7, 0x7c, 0x14, 0x4, 0x3f, 0x44, + 0x22, 0x28, 0x52, 0x18, 0xc, 0x6c, 0x30, 0x82, + 0x49, 0x10, 0x1, 0x20, 0x7f, 0xfe, 0x3, 0x40, + 0xd, 0x30, 0x31, 0xe, 0x41, 0x0, + + /* U+7CB3 "粳" */ + 0x20, 0x0, 0x47, 0xfe, 0xa0, 0x85, 0x5f, 0xe2, + 0x22, 0x44, 0x44, 0xbe, 0xff, 0x11, 0x12, 0x72, + 0x24, 0xd7, 0xfa, 0xa8, 0x85, 0xa, 0x2, 0xc, + 0x4, 0x66, 0xb, 0x3, 0x0, + + /* U+7CB9 "粹" */ + 0x20, 0x21, 0x50, 0x42, 0xaf, 0xfd, 0x84, 0x2, + 0x10, 0x9f, 0x31, 0x88, 0xb5, 0x11, 0x29, 0x74, + 0x2, 0xd0, 0x42, 0x9f, 0xfd, 0x1, 0x2, 0x2, + 0x4, 0x4, 0x8, 0x8, 0x0, + + /* U+7CBC "粼" */ + 0x24, 0x89, 0x15, 0xa, 0x4, 0xa, 0x7f, 0xd2, + 0xc, 0x12, 0x17, 0x14, 0x25, 0xa4, 0x44, 0x64, + 0x3b, 0xe4, 0x49, 0x14, 0xad, 0x12, 0x17, 0xd2, + 0x11, 0xa, 0x21, 0x9, 0xc1, 0x0, + + /* U+7CBD "粽" */ + 0x20, 0x21, 0x48, 0x42, 0x9f, 0xfb, 0x50, 0x12, + 0x20, 0x3f, 0xbf, 0x88, 0x0, 0x10, 0x0, 0x63, + 0xfe, 0xf0, 0x42, 0x92, 0xa5, 0x9, 0x22, 0x22, + 0x24, 0x84, 0x48, 0x38, 0x0, + + /* U+7CBE "精" */ + 0x20, 0x41, 0x47, 0xfa, 0xa1, 0x5, 0x9f, 0xe2, + 0x4, 0x1f, 0xff, 0xc8, 0x0, 0x11, 0xfe, 0x72, + 0x4, 0xd7, 0xfa, 0x88, 0x15, 0x1f, 0xe2, 0x20, + 0x44, 0x40, 0x88, 0x8e, 0x0, + + /* U+7CC1 "糁" */ + 0x20, 0x41, 0x41, 0x22, 0xa4, 0x25, 0x5f, 0xe2, + 0x4, 0x1f, 0x7f, 0xc8, 0x46, 0x11, 0x12, 0x74, + 0xc2, 0xd6, 0x22, 0x81, 0x85, 0xc, 0x62, 0x23, + 0x4, 0x18, 0x9, 0xc0, 0x0, + + /* U+7CC5 "糅" */ + 0x23, 0xfc, 0xa8, 0x8, 0xa8, 0xf0, 0x30, 0x20, + 0x23, 0xfe, 0xf8, 0xa6, 0x21, 0x24, 0x36, 0x60, + 0x68, 0x80, 0x67, 0xfe, 0xa0, 0xe0, 0xa1, 0xd0, + 0x23, 0x4c, 0x2c, 0x43, 0x20, 0x40, + + /* U+7CC7 "糇" */ + 0x21, 0x0, 0x42, 0xfa, 0xa4, 0x15, 0x50, 0x46, + 0xaf, 0xff, 0xc0, 0xa, 0x9f, 0x11, 0x48, 0x72, + 0x90, 0xd4, 0x22, 0xab, 0xfd, 0x10, 0x82, 0x22, + 0x84, 0x48, 0x88, 0xa0, 0x80, + + /* U+7CC8 "糈" */ + 0x20, 0x0, 0x23, 0xfe, 0xa8, 0x24, 0xaa, 0x3e, + 0x23, 0x20, 0xfa, 0xe0, 0x24, 0x1e, 0x33, 0xfc, + 0x72, 0x4, 0x6b, 0xfc, 0xa2, 0x4, 0xa3, 0xfc, + 0x22, 0x4, 0x22, 0x4, 0x22, 0x3c, + + /* U+7CCA "糊" */ + 0x21, 0x0, 0x42, 0x7e, 0xa4, 0x8d, 0x7d, 0x17, + 0x12, 0x24, 0x27, 0xfe, 0x48, 0x91, 0xd1, 0x74, + 0xa2, 0xd9, 0x7e, 0x92, 0x8d, 0x25, 0x12, 0x3e, + 0x24, 0x8, 0x48, 0x13, 0x80, + + /* U+7CCC "糌" */ + 0x21, 0x11, 0x53, 0xa2, 0xa9, 0x65, 0x94, 0xb2, + 0x19, 0x1f, 0x30, 0x8, 0x9f, 0x9a, 0x0, 0x73, + 0xfc, 0xd4, 0xa, 0x88, 0x15, 0x1f, 0xe2, 0x20, + 0x44, 0x7f, 0x88, 0x81, 0x0, + + /* U+7CCD "糍" */ + 0x21, 0x8, 0x41, 0x12, 0xa2, 0x45, 0x5f, 0xf2, + 0x10, 0x9f, 0x2a, 0x88, 0xa5, 0x12, 0x54, 0x77, + 0x38, 0xe2, 0x22, 0xa8, 0x45, 0x15, 0x22, 0x4a, + 0x44, 0xff, 0x88, 0x10, 0x80, + + /* U+7CD5 "糕" */ + 0x21, 0x9, 0x51, 0x22, 0xaf, 0xfd, 0x81, 0x2, + 0x2, 0x4, 0x4, 0x3e, 0xff, 0x10, 0x10, 0x70, + 0x20, 0xd7, 0xfe, 0x80, 0x5, 0x10, 0xa2, 0x29, + 0x24, 0x8a, 0x49, 0x0, 0x0, + + /* U+7CD6 "糖" */ + 0x20, 0x21, 0x57, 0xfe, 0xa8, 0x5, 0x51, 0x7, + 0x3f, 0xc4, 0x44, 0xbe, 0xff, 0x91, 0x12, 0x35, + 0xfc, 0xd8, 0x42, 0x97, 0xf5, 0x28, 0x22, 0x50, + 0x44, 0xbf, 0x8a, 0x41, 0x0, + + /* U+7CD7 "糗" */ + 0x20, 0x41, 0x57, 0xfa, 0xa8, 0x15, 0x9f, 0xe2, + 0x20, 0x5f, 0x7f, 0x88, 0x81, 0x19, 0xfe, 0x78, + 0x40, 0xc0, 0x92, 0x9f, 0xfd, 0x2, 0x2, 0xb, + 0x4, 0x61, 0xb, 0x1, 0x80, + + /* U+7CD9 "糙" */ + 0x20, 0x11, 0x48, 0xa2, 0xa9, 0xfd, 0x92, 0x82, + 0x1, 0x1f, 0x1f, 0xc9, 0x80, 0x19, 0x3f, 0x6a, + 0x42, 0xd4, 0x86, 0x89, 0xd, 0x13, 0xf2, 0x20, + 0x4, 0xa0, 0xa, 0x3f, 0x80, + + /* U+7CDC "糜" */ + 0x0, 0x80, 0x1, 0x1, 0xff, 0xfa, 0x20, 0x85, + 0xf7, 0xc9, 0xc6, 0x15, 0x57, 0x32, 0x69, 0x40, + 0x0, 0x99, 0x61, 0xff, 0xfa, 0xe, 0x8, 0x6b, + 0x17, 0x11, 0xe0, 0x20, 0x0, + + /* U+7CDF "糟" */ + 0x20, 0x91, 0x5f, 0xfe, 0xa2, 0x45, 0xbf, 0xf2, + 0x49, 0x3f, 0xff, 0xc9, 0x24, 0x93, 0xff, 0x70, + 0x0, 0xd7, 0xfa, 0x88, 0x15, 0x1f, 0xe2, 0x20, + 0x44, 0x7f, 0x88, 0x81, 0x0, + + /* U+7CE0 "糠" */ + 0x20, 0x21, 0x57, 0xfa, 0xb0, 0x5, 0xa1, 0x7, + 0x5f, 0xc4, 0x84, 0xbe, 0xff, 0x92, 0x12, 0x35, + 0xfc, 0xd8, 0x42, 0x96, 0xd5, 0x23, 0xc2, 0x5a, + 0x84, 0x84, 0x8a, 0x38, 0x0, + + /* U+7CE8 "糨" */ + 0x20, 0x0, 0x46, 0xfa, 0xa5, 0x15, 0x4a, 0x22, + 0x33, 0x9f, 0x82, 0x9, 0x1f, 0x12, 0x2a, 0x77, + 0x54, 0xd2, 0xaa, 0x85, 0xf5, 0x8, 0x82, 0x11, + 0x44, 0x67, 0x89, 0xf0, 0x80, + + /* U+7CEF "糯" */ + 0x20, 0x0, 0x47, 0xfa, 0xa1, 0x5, 0x7f, 0xe2, + 0x5d, 0xc4, 0x8, 0x3e, 0x76, 0x1b, 0xff, 0x30, + 0x80, 0xd1, 0x2, 0x8f, 0xf5, 0x14, 0xa2, 0x29, + 0x44, 0x52, 0x88, 0xa7, 0x0, + + /* U+7CF8 "糸" */ + 0x3, 0x0, 0x8, 0x0, 0x20, 0x61, 0x81, 0x7, + 0xfc, 0x0, 0x20, 0x1, 0x88, 0xc, 0x8, 0x7f, + 0xfc, 0x2, 0xc, 0x4, 0x0, 0x89, 0x86, 0x10, + 0x90, 0x20, 0xc3, 0xc0, 0x0, + + /* U+7CFB "系" */ + 0x0, 0xc, 0xff, 0xf0, 0x4, 0x0, 0x30, 0x81, + 0x82, 0xf, 0xf8, 0x0, 0xc0, 0x2, 0x10, 0x18, + 0x18, 0xff, 0xf8, 0x4, 0x10, 0x89, 0x2, 0x11, + 0x98, 0x20, 0xc3, 0xc0, 0x0, + + /* U+7D0A "紊" */ + 0x1, 0x1, 0xff, 0xfc, 0x20, 0xc0, 0x32, 0x0, + 0x3c, 0x1f, 0x87, 0xc1, 0xc4, 0x1f, 0xe0, 0x1, + 0x20, 0x3c, 0x21, 0xff, 0xf0, 0x4, 0x21, 0x89, + 0x9c, 0x10, 0xc1, 0xc0, 0x0, + + /* U+7D20 "素" */ + 0x1, 0x0, 0xff, 0xf8, 0x4, 0x1, 0xff, 0xc0, + 0x10, 0x1f, 0xff, 0xc3, 0xc, 0x1f, 0xe0, 0x2, + 0x20, 0x38, 0x21, 0xff, 0xf0, 0x8, 0x21, 0x13, + 0x9c, 0x20, 0xc1, 0xc0, 0x0, + + /* U+7D22 "索" */ + 0x1, 0x3, 0xff, 0xe0, 0x10, 0x0, 0x40, 0xff, + 0xfe, 0xc, 0x18, 0xc6, 0x4f, 0xe0, 0x2, 0x0, + 0x70, 0x83, 0xff, 0x0, 0x42, 0x11, 0x31, 0x4, + 0x38, 0xf0, 0x0, + + /* U+7D27 "紧" */ + 0x4b, 0xf8, 0x92, 0x11, 0x22, 0x42, 0x47, 0x4, + 0xb3, 0xc0, 0x40, 0x3, 0xc, 0x1f, 0xe0, 0x2, + 0x40, 0x38, 0x41, 0xff, 0xe0, 0x8, 0x1, 0x93, + 0x1c, 0x21, 0x81, 0xc0, 0x0, + + /* U+7D2B "紫" */ + 0x8, 0x80, 0x91, 0x19, 0x3e, 0xc2, 0x46, 0x14, + 0x98, 0x3e, 0xcf, 0x83, 0x80, 0x1f, 0xf0, 0x3, + 0x20, 0x38, 0x21, 0xff, 0xf0, 0x8, 0x21, 0x13, + 0xc, 0x21, 0x81, 0xc0, 0x0, + + /* U+7D2F "累" */ + 0x7f, 0xf9, 0x4, 0x24, 0x10, 0x9f, 0xfe, 0x41, + 0x9, 0xff, 0xe0, 0xe0, 0x1f, 0xf8, 0x2, 0x0, + 0x70, 0x87, 0xff, 0x80, 0x42, 0x39, 0x33, 0x4, + 0x30, 0xf0, 0x0, + + /* U+7D6E "絮" */ + 0x10, 0x0, 0x7f, 0x7e, 0x21, 0x42, 0x22, 0x42, + 0x1e, 0x42, 0x11, 0x7e, 0x61, 0x0, 0x6, 0x18, + 0x3f, 0xe0, 0x4, 0x10, 0x3f, 0xfc, 0x0, 0x82, + 0xc, 0x98, 0xf0, 0x87, 0x7, 0x0, + + /* U+7D77 "絷" */ + 0x20, 0x83, 0xef, 0xc2, 0x9, 0xe, 0xe4, 0xe1, + 0x94, 0x89, 0x5e, 0x30, 0xc6, 0x8, 0x3f, 0xc0, + 0x20, 0x87, 0xff, 0x10, 0x42, 0x11, 0x43, 0x84, + 0xe0, 0xf0, 0x40, + + /* U+7DA6 "綦" */ + 0x10, 0x11, 0xff, 0xfc, 0x40, 0x40, 0xff, 0x81, + 0x1, 0x3, 0xfe, 0x3f, 0xff, 0x84, 0x90, 0x36, + 0x79, 0x9f, 0xc, 0x10, 0x81, 0xff, 0x80, 0x10, + 0x86, 0x27, 0x31, 0xc1, 0x0, + + /* U+7DAE "綮" */ + 0x10, 0x0, 0x20, 0x81, 0xf9, 0xfa, 0x16, 0x44, + 0x2c, 0x8f, 0xc6, 0x30, 0x1a, 0x47, 0x52, 0x3f, + 0xc0, 0x4, 0x0, 0x70, 0x83, 0xff, 0xc0, 0x10, + 0x43, 0x26, 0x38, 0x43, 0x87, 0x81, + + /* U+7E3B "縻" */ + 0x0, 0x80, 0x0, 0x40, 0x3f, 0xff, 0x20, 0x0, + 0x22, 0x8, 0x2f, 0xbe, 0x26, 0x1c, 0x2b, 0x6b, + 0x23, 0x88, 0x27, 0xf0, 0x20, 0x80, 0x23, 0xc, + 0x2f, 0xfe, 0x40, 0x40, 0x4e, 0x4e, 0x43, 0xc0, + + /* U+7E41 "繁" */ + 0x0, 0x0, 0x7e, 0x41, 0x0, 0xfd, 0xfb, 0x24, + 0x92, 0x9f, 0xf3, 0x12, 0x86, 0x3f, 0xb3, 0x6, + 0x10, 0x7f, 0xc0, 0x10, 0x83, 0xff, 0xc0, 0x10, + 0x43, 0x26, 0x38, 0x43, 0x83, 0x80, + + /* U+7E47 "繇" */ + 0xfc, 0xfe, 0x0, 0x22, 0x48, 0x82, 0xa2, 0x24, + 0xf, 0x8f, 0xc2, 0x24, 0x19, 0x8, 0x41, 0xfe, + 0x7e, 0x20, 0x20, 0x43, 0x54, 0x94, 0x97, 0xe1, + 0x0, 0x4e, 0x0, + + /* U+7E82 "纂" */ + 0x10, 0x40, 0x7e, 0xfd, 0x92, 0x42, 0xff, 0xc1, + 0xff, 0x83, 0xff, 0x8, 0x2, 0xf, 0xf8, 0x7f, + 0xfe, 0x16, 0x20, 0xdf, 0x32, 0x21, 0x11, 0xff, + 0x83, 0x16, 0x19, 0xc3, 0x0, + + /* U+7E9B "纛" */ + 0xff, 0xfe, 0xff, 0xf8, 0x4, 0x3, 0xf7, 0xf3, + 0xff, 0x9f, 0xff, 0xdf, 0xfe, 0x0, 0x0, 0x7d, + 0xfe, 0xf8, 0x89, 0xf3, 0xe3, 0xe6, 0x3f, 0xe3, + 0xea, 0x9a, 0xbc, 0x5c, 0x80, + + /* U+7E9F "纟" */ + 0x4, 0x4, 0x4, 0x4, 0x34, 0x33, 0xe0, 0x20, + 0x20, 0x20, 0x3f, 0x80, 0x0, 0x0, 0xb, 0xf8, + + /* U+7EA0 "纠" */ + 0x8, 0x4, 0x42, 0x12, 0x8, 0x49, 0x21, 0x44, + 0x87, 0xe2, 0x11, 0x8, 0x44, 0x21, 0x20, 0x8d, + 0xf3, 0xd0, 0x8, 0x40, 0x1, 0xe, 0x7, 0xc0, + 0x10, 0x0, 0x40, + + /* U+7EA1 "纡" */ + 0x10, 0x0, 0x47, 0xf8, 0x80, 0x82, 0x21, 0x8, + 0x82, 0x1e, 0x4, 0x4, 0xff, 0x90, 0x10, 0x40, + 0x20, 0xf0, 0x40, 0x0, 0x80, 0x1, 0x0, 0xc2, + 0x1e, 0x4, 0x0, 0x70, 0x0, + + /* U+7EA2 "红" */ + 0x0, 0x0, 0x20, 0x0, 0x8f, 0xf2, 0x1, 0x4, + 0x82, 0x11, 0x4, 0x3c, 0x8, 0x8, 0x10, 0x20, + 0x20, 0x80, 0x43, 0xe0, 0x80, 0x1, 0x0, 0x2, + 0xf, 0x4, 0x21, 0xff, 0x80, + + /* U+7EA3 "纣" */ + 0x8, 0x0, 0x10, 0x8, 0x20, 0x8, 0x25, 0xff, + 0x44, 0x8, 0xf8, 0x8, 0x11, 0x8, 0x20, 0x88, + 0x40, 0x48, 0x7c, 0x8, 0x0, 0x8, 0x0, 0x8, + 0xc, 0x8, 0xf0, 0x8, 0x0, 0x78, + + /* U+7EA4 "纤" */ + 0x10, 0x0, 0x23, 0xf8, 0x80, 0x82, 0x21, 0x4, + 0xc2, 0x1f, 0x4, 0x4, 0x8, 0x19, 0xff, 0x20, + 0x20, 0xb8, 0x40, 0x80, 0x80, 0x1, 0x0, 0x42, + 0x1f, 0x4, 0x0, 0x8, 0x0, + + /* U+7EA5 "纥" */ + 0x10, 0x80, 0x10, 0x80, 0x21, 0xff, 0x45, 0x0, + 0x4a, 0x0, 0xf8, 0xfc, 0x10, 0x8, 0x20, 0x18, + 0x20, 0x30, 0x7c, 0x60, 0x0, 0x41, 0x0, 0x81, + 0x5, 0x1, 0xf9, 0x2, 0x1, 0xfe, + + /* U+7EA6 "约" */ + 0x10, 0x40, 0x21, 0x0, 0x83, 0xf9, 0x24, 0x14, + 0xd0, 0x2f, 0x40, 0x44, 0x20, 0x88, 0x21, 0x20, + 0x62, 0xf8, 0x44, 0x0, 0x8, 0x0, 0x10, 0x20, + 0x3f, 0x80, 0xc0, 0x1f, 0x0, + + /* U+7EA7 "级" */ + 0x10, 0x0, 0x23, 0xf8, 0x21, 0x8, 0x49, 0x8, + 0x89, 0x10, 0xf1, 0x9e, 0x21, 0x82, 0x41, 0x84, + 0xf9, 0x44, 0x3, 0x28, 0x2, 0x10, 0x1a, 0x38, + 0xe4, 0x64, 0xd, 0x83, 0x0, 0x0, + + /* U+7EA8 "纨" */ + 0x10, 0x80, 0x10, 0x80, 0x20, 0x80, 0x43, 0xf8, + 0x48, 0x88, 0xf8, 0x88, 0x10, 0x88, 0x22, 0x88, + 0x61, 0x88, 0x78, 0xe8, 0x1, 0x8, 0x1, 0x9, + 0x3b, 0xa, 0xc2, 0xa, 0x4, 0x6, + + /* U+7EA9 "纩" */ + 0x0, 0x0, 0x20, 0x60, 0x80, 0x41, 0xf, 0xf4, + 0x50, 0x11, 0x20, 0x3c, 0x40, 0x8, 0x80, 0x21, + 0x0, 0x82, 0x3, 0xe4, 0x0, 0x8, 0x0, 0x10, + 0x7, 0x40, 0x30, 0x80, 0x2, 0x0, + + /* U+7EAA "纪" */ + 0x10, 0x0, 0x27, 0xf8, 0x80, 0x12, 0x20, 0x24, + 0x80, 0x5f, 0x0, 0x84, 0x7f, 0x10, 0x80, 0x41, + 0x1, 0xf2, 0x0, 0x4, 0x0, 0x8, 0x11, 0xd0, + 0x3c, 0x1f, 0x80, + + /* U+7EAB "纫" */ + 0x10, 0x0, 0x8f, 0xf4, 0x4, 0x50, 0x11, 0x9a, + 0x47, 0xc9, 0x12, 0x24, 0x49, 0x11, 0x44, 0x47, + 0xe1, 0x10, 0x8, 0x40, 0x21, 0x19, 0x87, 0x8c, + 0x10, 0x67, 0x80, + + /* U+7EAC "纬" */ + 0x8, 0x20, 0x20, 0x40, 0x8f, 0xf9, 0x1, 0x4, + 0x82, 0x1f, 0x7f, 0x84, 0x8, 0x10, 0x10, 0x43, + 0xfe, 0xf0, 0x44, 0x0, 0x88, 0x1, 0x13, 0xc2, + 0xc8, 0x4, 0x0, 0x8, 0x0, + + /* U+7EAD "纭" */ + 0x10, 0x0, 0x23, 0xf8, 0x80, 0x1, 0x0, 0x4, + 0xc0, 0x1f, 0x0, 0x4, 0xff, 0x90, 0x0, 0x40, + 0x41, 0xf1, 0x20, 0x2, 0x20, 0x8, 0x41, 0xd0, + 0x5c, 0x7f, 0x80, 0x0, 0x80, + + /* U+7EAF "纯" */ + 0x0, 0x40, 0x40, 0x80, 0x9f, 0xf2, 0x2, 0x9, + 0x84, 0x9e, 0x89, 0x9, 0x12, 0x22, 0x24, 0x44, + 0x49, 0xf7, 0xf0, 0x1, 0x0, 0x2, 0x20, 0x84, + 0x5e, 0x8, 0x80, 0x1f, 0x0, + + /* U+7EB0 "纰" */ + 0x2, 0x20, 0x44, 0x40, 0x88, 0x82, 0x11, 0x18, + 0xa2, 0x5e, 0x77, 0x8, 0x88, 0x21, 0x10, 0x42, + 0x21, 0xf4, 0x40, 0x8, 0x80, 0x11, 0x11, 0xaa, + 0x3c, 0x64, 0x80, 0x8f, 0x0, + + /* U+7EB1 "纱" */ + 0x10, 0x20, 0x10, 0x20, 0x20, 0x20, 0x45, 0x24, + 0x49, 0x24, 0xf2, 0x22, 0x12, 0x20, 0x20, 0x20, + 0x40, 0x24, 0xf8, 0x24, 0x0, 0x8, 0x0, 0x10, + 0x1c, 0x60, 0xe1, 0x80, 0x6, 0x0, + + /* U+7EB2 "纲" */ + 0x10, 0x0, 0x27, 0xfc, 0x88, 0x9, 0x10, 0x14, + 0x28, 0xa9, 0x52, 0x7c, 0x94, 0x89, 0x29, 0x22, + 0x22, 0x84, 0x45, 0xe9, 0x48, 0x14, 0x90, 0x28, + 0xa7, 0x40, 0x50, 0x80, 0x81, 0xf, + + /* U+7EB3 "纳" */ + 0x10, 0x20, 0x40, 0x40, 0x80, 0x82, 0x1f, 0xf8, + 0xa2, 0x3e, 0x44, 0x44, 0x88, 0x91, 0x31, 0x42, + 0x53, 0xf5, 0x14, 0xa, 0x8, 0x10, 0x11, 0xa0, + 0x3c, 0x40, 0x40, 0x87, 0x80, + + /* U+7EB5 "纵" */ + 0x11, 0x10, 0x42, 0x20, 0x84, 0x42, 0x48, 0x88, + 0x91, 0x1e, 0x22, 0x8, 0x44, 0x10, 0xc8, 0x42, + 0xa9, 0xf4, 0xd0, 0x8, 0xa0, 0x31, 0x6f, 0x44, + 0x41, 0x88, 0x42, 0x20, 0x80, + + /* U+7EB6 "纶" */ + 0x10, 0x20, 0x20, 0xc0, 0x81, 0x42, 0x44, 0x48, + 0x90, 0x5e, 0x40, 0x48, 0x0, 0x10, 0x84, 0x41, + 0x31, 0xf3, 0x80, 0x4, 0x0, 0x8, 0x20, 0x90, + 0x5e, 0x20, 0x80, 0x3e, 0x0, + + /* U+7EB7 "纷" */ + 0x10, 0x20, 0x21, 0x20, 0x82, 0x41, 0x28, 0x84, + 0x90, 0x8f, 0x40, 0x84, 0x80, 0x90, 0xfc, 0x20, + 0x88, 0xf9, 0x10, 0x2, 0x20, 0x8, 0x43, 0xd0, + 0x88, 0x41, 0x1, 0x1c, 0x0, + + /* U+7EB8 "纸" */ + 0x10, 0x0, 0x47, 0xfc, 0x88, 0x82, 0x51, 0x8, + 0xa2, 0x1e, 0x44, 0x8, 0xff, 0x91, 0x10, 0x42, + 0x21, 0xf4, 0x40, 0x8, 0x80, 0x10, 0x93, 0xa1, + 0x38, 0x7a, 0x40, 0x83, 0x0, + + /* U+7EB9 "纹" */ + 0x0, 0x40, 0x20, 0x40, 0x80, 0x1, 0x1f, 0xf4, + 0x90, 0x5f, 0x21, 0x4, 0x42, 0x10, 0x44, 0x40, + 0x91, 0xf0, 0xa0, 0x0, 0x80, 0x3, 0x87, 0xc8, + 0x80, 0x60, 0xc1, 0x0, 0x0, + + /* U+7EBA "纺" */ + 0x0, 0x0, 0x20, 0x40, 0x80, 0x82, 0x1f, 0xf4, + 0x90, 0x11, 0x20, 0x3c, 0x40, 0x18, 0x7e, 0x21, + 0x4, 0x82, 0xb, 0xe4, 0x10, 0x8, 0x20, 0x10, + 0x40, 0x40, 0xbe, 0x81, 0x2, 0x3c, + + /* U+7EBD "纽" */ + 0x18, 0x0, 0x27, 0xf8, 0x82, 0x11, 0x24, 0x24, + 0x88, 0x5f, 0x10, 0x84, 0x21, 0x11, 0xfe, 0x60, + 0x84, 0xf1, 0x8, 0x2, 0x10, 0x4, 0x2f, 0x88, + 0x40, 0xff, 0xc0, + + /* U+7EBE "纾" */ + 0x10, 0x0, 0xcf, 0xf2, 0x0, 0x90, 0xc4, 0x48, + 0xa3, 0xc1, 0x81, 0x7f, 0xc8, 0x11, 0x40, 0x47, + 0xe1, 0x10, 0x4, 0x0, 0x10, 0x18, 0x43, 0x81, + 0x0, 0x38, 0x0, + + /* U+7EBF "线" */ + 0x10, 0x90, 0x41, 0x10, 0x82, 0x2, 0x45, 0xf8, + 0xbc, 0x1e, 0x10, 0x8, 0x20, 0x91, 0xfe, 0x40, + 0x45, 0xf0, 0x90, 0x1, 0x40, 0x1, 0x13, 0x8e, + 0x38, 0x62, 0x41, 0x3, 0x0, + + /* U+7EC0 "绀" */ + 0x11, 0x2, 0x11, 0x2, 0x21, 0x2, 0x23, 0xff, + 0x4d, 0x2, 0x79, 0x2, 0x11, 0x2, 0x11, 0x2, + 0x21, 0xfe, 0x45, 0x2, 0x79, 0x2, 0x1, 0x2, + 0x5, 0x2, 0x79, 0xfe, 0x1, 0x2, + + /* U+7EC1 "绁" */ + 0x12, 0x48, 0x44, 0x91, 0x9, 0x22, 0x52, 0x49, + 0x7f, 0xfe, 0x49, 0x8, 0x92, 0x11, 0x24, 0x42, + 0x49, 0x74, 0x93, 0x9, 0xe0, 0x10, 0x7, 0xa0, + 0x10, 0x7f, 0x80, + + /* U+7EC2 "绂" */ + 0x10, 0x80, 0x21, 0x30, 0x82, 0x2, 0x1f, 0xf4, + 0x88, 0x1f, 0x20, 0x4, 0x7f, 0x10, 0x82, 0x42, + 0x89, 0xf5, 0x10, 0x11, 0x40, 0x21, 0x1, 0x85, + 0x1c, 0x71, 0x80, 0x0, 0x80, + + /* U+7EC3 "练" */ + 0x10, 0x40, 0x10, 0x40, 0x23, 0xfe, 0x20, 0x80, + 0x48, 0x80, 0xf9, 0x70, 0x11, 0x10, 0x22, 0x10, + 0x23, 0xfe, 0x78, 0x10, 0x0, 0x94, 0x1, 0x14, + 0x3d, 0x12, 0x42, 0x10, 0x0, 0xe0, + + /* U+7EC4 "组" */ + 0x10, 0x0, 0x23, 0xf8, 0x84, 0x12, 0x8, 0x24, + 0x90, 0x5e, 0x3f, 0x84, 0x41, 0x10, 0x82, 0x49, + 0x5, 0xf3, 0xf8, 0x4, 0x10, 0x8, 0x2f, 0x90, + 0x40, 0xff, 0xc0, + + /* U+7EC5 "绅" */ + 0x10, 0x20, 0x40, 0x40, 0x8f, 0xfa, 0x51, 0x14, + 0xa2, 0x3e, 0x44, 0x44, 0xff, 0x91, 0x11, 0x42, + 0x22, 0xb4, 0x45, 0x8f, 0xf8, 0x1, 0x3, 0x82, + 0x18, 0x4, 0x0, 0x8, 0x0, + + /* U+7EC6 "细" */ + 0x10, 0x0, 0x47, 0xfc, 0x88, 0x8a, 0x51, 0x14, + 0xa2, 0x3e, 0x44, 0x44, 0x88, 0x91, 0xff, 0x42, + 0x23, 0xf4, 0x44, 0x8, 0x88, 0x11, 0x1f, 0xbf, + 0xe0, 0x40, 0x40, 0x80, 0x80, + + /* U+7EC7 "织" */ + 0x10, 0x0, 0x47, 0xf8, 0x88, 0x12, 0x10, 0x28, + 0xa0, 0x5e, 0x40, 0x88, 0x81, 0x11, 0x2, 0x41, + 0xf9, 0xf0, 0x0, 0x2, 0x40, 0x8, 0x43, 0xa0, + 0x58, 0x80, 0x40, 0x0, 0x0, + + /* U+7EC8 "终" */ + 0x0, 0x40, 0x10, 0x80, 0x20, 0xfe, 0x21, 0x84, + 0x45, 0x84, 0x4a, 0x48, 0x78, 0x70, 0x10, 0x30, + 0x20, 0xcc, 0x23, 0x2, 0x78, 0x40, 0x0, 0x38, + 0x0, 0x0, 0x1, 0x80, 0x78, 0x70, 0x0, 0xc, + + /* U+7EC9 "绉" */ + 0x0, 0x40, 0x21, 0x0, 0x83, 0xf1, 0x8, 0x44, + 0x50, 0x91, 0x2, 0x3c, 0xff, 0x90, 0x1, 0x20, + 0x2, 0x80, 0x7, 0xef, 0xf8, 0x0, 0x10, 0x0, + 0x27, 0x0, 0x50, 0xff, 0x80, 0x1, + + /* U+7ECA "绊" */ + 0x10, 0x40, 0x40, 0x81, 0x9, 0x12, 0x4a, 0x29, + 0x14, 0x9e, 0x8, 0x8, 0xff, 0xa0, 0x20, 0x88, + 0x41, 0xef, 0xfc, 0x1, 0x0, 0x2, 0x7, 0x84, + 0x10, 0x8, 0x0, 0x10, 0x0, + + /* U+7ECB "绋" */ + 0x20, 0x90, 0x8f, 0xfa, 0x62, 0x57, 0x84, 0xa2, + 0x7f, 0xc4, 0x92, 0x11, 0x24, 0x42, 0x48, 0xf7, + 0xfe, 0x1, 0x24, 0x4, 0x4f, 0x88, 0xf0, 0x21, + 0x1, 0x82, 0x0, + + /* U+7ECC "绌" */ + 0x10, 0x20, 0x20, 0x40, 0x88, 0x8a, 0x11, 0x14, + 0xa2, 0x3e, 0x44, 0x44, 0xff, 0x90, 0x10, 0x60, + 0x21, 0xb4, 0x44, 0x88, 0x88, 0x11, 0x13, 0xa2, + 0x28, 0x7f, 0xc0, 0x0, 0x80, + + /* U+7ECD "绍" */ + 0x18, 0x0, 0x4f, 0xf2, 0x8, 0x48, 0x21, 0x48, + 0x87, 0xe4, 0x11, 0x27, 0x88, 0x0, 0x43, 0xfd, + 0xe8, 0x10, 0x20, 0x40, 0x81, 0x3e, 0x5, 0xf, + 0xf0, 0x20, 0x40, + + /* U+7ECE "绎" */ + 0x8, 0x0, 0x27, 0xf8, 0x84, 0x11, 0x4, 0x44, + 0x83, 0x1f, 0x1b, 0x4, 0xc1, 0x90, 0x10, 0x43, + 0xfc, 0xf0, 0x40, 0x0, 0x80, 0x1f, 0xf3, 0xc2, + 0x8, 0x4, 0x0, 0x8, 0x0, + + /* U+7ECF "经" */ + 0x10, 0x0, 0x47, 0xf8, 0x80, 0x22, 0x40, 0x88, + 0x82, 0x1e, 0x1b, 0x8, 0x81, 0x22, 0x1, 0x8b, + 0xfd, 0xe0, 0x80, 0x1, 0x0, 0x2, 0xf, 0x84, + 0x0, 0xff, 0xc0, + + /* U+7ED0 "绐" */ + 0x10, 0x20, 0x10, 0x40, 0x20, 0x88, 0x24, 0x84, + 0x49, 0x2, 0x7b, 0xfe, 0x10, 0x1, 0x20, 0x0, + 0x41, 0xfe, 0x7d, 0x2, 0x1, 0x2, 0x1, 0x2, + 0x3d, 0x2, 0x41, 0xfe, 0x1, 0x2, + + /* U+7ED1 "绑" */ + 0x11, 0x1e, 0x21, 0x12, 0x27, 0xd2, 0x49, 0x12, + 0x49, 0x14, 0x71, 0x14, 0x27, 0xd4, 0x21, 0x12, + 0x49, 0x11, 0x77, 0xd1, 0x1, 0x11, 0x19, 0x1e, + 0x62, 0x10, 0x2, 0x10, 0x4, 0x10, + + /* U+7ED2 "绒" */ + 0x10, 0x0, 0x10, 0x24, 0x20, 0x20, 0x43, 0xff, + 0x48, 0x10, 0xf1, 0x10, 0x11, 0x12, 0x27, 0xd4, + 0x41, 0x14, 0xf9, 0x18, 0x1, 0x18, 0x2, 0x11, + 0x3a, 0x2a, 0xc4, 0xca, 0x4, 0x86, + + /* U+7ED3 "结" */ + 0x10, 0x20, 0x20, 0x40, 0x8f, 0xf9, 0x1, 0x4, + 0x82, 0x1f, 0x4, 0x4, 0xff, 0x10, 0x0, 0x20, + 0x0, 0xb3, 0xf9, 0x84, 0x10, 0x8, 0x21, 0xd0, + 0x4c, 0x3f, 0x80, 0x41, 0x0, + + /* U+7ED4 "绔" */ + 0x10, 0x20, 0x40, 0x80, 0x9f, 0xfa, 0x44, 0x88, + 0x90, 0x9e, 0x7f, 0xcd, 0x0, 0x30, 0x0, 0x47, + 0xff, 0x62, 0x1, 0xf, 0xf0, 0x0, 0x23, 0x80, + 0x98, 0x1, 0x0, 0x3e, 0x0, + + /* U+7ED5 "绕" */ + 0x11, 0x0, 0x42, 0x19, 0x1f, 0xc2, 0x44, 0x49, + 0xb, 0x1e, 0xc, 0x88, 0xe9, 0x21, 0xc, 0x40, + 0x1, 0xef, 0xf8, 0x4, 0x80, 0x9, 0xf, 0x92, + 0x40, 0x44, 0x83, 0x7, 0x0, + + /* U+7ED7 "绗" */ + 0x10, 0x80, 0x20, 0x9f, 0x21, 0x0, 0x42, 0x0, + 0x48, 0x80, 0xf0, 0x80, 0x21, 0x3f, 0x23, 0x4, + 0x45, 0x4, 0xf9, 0x4, 0x1, 0x4, 0x1, 0x4, + 0x9, 0x4, 0xf1, 0x4, 0x1, 0x3c, + + /* U+7ED8 "绘" */ + 0x10, 0x20, 0x10, 0x20, 0x20, 0x50, 0x24, 0x88, + 0x49, 0x4, 0x7b, 0x2, 0x14, 0xfc, 0x10, 0x0, + 0x20, 0x0, 0x40, 0x0, 0x7b, 0xfe, 0x0, 0x40, + 0x0, 0x88, 0x79, 0x4, 0x3, 0xfc, 0x0, 0x2, + + /* U+7ED9 "给" */ + 0x10, 0x20, 0x40, 0xc0, 0x83, 0x42, 0x4, 0xc4, + 0x90, 0xd2, 0xc0, 0x7c, 0x7e, 0x10, 0x0, 0x40, + 0x0, 0x87, 0xfb, 0xe8, 0x10, 0x10, 0x20, 0x20, + 0x4f, 0x40, 0xa0, 0xff, 0x1, 0x2, + + /* U+7EDA "绚" */ + 0x21, 0x0, 0x84, 0x4, 0x3f, 0xd2, 0x81, 0x94, + 0x7, 0xcf, 0x92, 0x22, 0x50, 0x89, 0x43, 0xe7, + 0xc8, 0x90, 0x22, 0x40, 0xf9, 0x78, 0x6, 0x0, + 0x10, 0x7, 0x80, + + /* U+7EDB "绛" */ + 0x0, 0x0, 0x10, 0x80, 0x20, 0xfc, 0x21, 0x84, + 0x42, 0xc8, 0x48, 0x70, 0xf0, 0x58, 0x13, 0x86, + 0x20, 0x20, 0x43, 0xfe, 0xf9, 0x20, 0x2, 0x20, + 0x3, 0xfe, 0x38, 0x20, 0xc0, 0x20, 0x0, 0x20, + + /* U+7EDC "络" */ + 0x0, 0x40, 0x10, 0x40, 0x20, 0xfe, 0x20, 0x82, + 0x4d, 0x84, 0x4a, 0x48, 0x70, 0x30, 0x10, 0x78, + 0x21, 0x87, 0x42, 0x0, 0x79, 0xfe, 0x1, 0x2, + 0x1, 0x2, 0x3d, 0x2, 0x41, 0xfe, 0x1, 0x2, + + /* U+7EDD "绝" */ + 0x10, 0x40, 0x21, 0x0, 0x83, 0xe1, 0x8, 0x84, + 0xa1, 0x11, 0xff, 0xbd, 0x89, 0x9, 0x12, 0x22, + 0x24, 0x84, 0x4b, 0xef, 0x60, 0x10, 0x0, 0x20, + 0x21, 0x40, 0x7c, 0x80, 0x81, 0xfe, + + /* U+7EDE "绞" */ + 0x18, 0x20, 0x20, 0x40, 0x8f, 0xf9, 0x20, 0x4, + 0x89, 0x8f, 0x21, 0x84, 0xc1, 0x11, 0x85, 0x20, + 0x88, 0xf1, 0x20, 0x1, 0x40, 0x1, 0x3, 0xc5, + 0x8, 0x31, 0x81, 0x80, 0x80, + + /* U+7EDF "统" */ + 0x10, 0x40, 0x40, 0x40, 0x9f, 0xfa, 0x6, 0x8, + 0x88, 0x1e, 0x21, 0x8, 0x82, 0x13, 0xfe, 0x40, + 0xa1, 0xf1, 0x40, 0x4, 0x80, 0x9, 0x13, 0x92, + 0x38, 0x42, 0x43, 0x7, 0x80, + + /* U+7EE0 "绠" */ + 0x10, 0x0, 0x4f, 0xfc, 0x81, 0x2, 0x5f, 0xe9, + 0x44, 0x5e, 0x88, 0x88, 0xff, 0x32, 0x22, 0x44, + 0x45, 0xe7, 0xf8, 0x9, 0x0, 0xa, 0xf, 0x88, + 0x0, 0xef, 0x2, 0x1, 0x80, + + /* U+7EE1 "绡" */ + 0x10, 0x20, 0x44, 0x4c, 0x84, 0x92, 0x41, 0x8, + 0xbf, 0xfe, 0x40, 0x48, 0x80, 0x91, 0xff, 0x42, + 0x3, 0xe4, 0x4, 0xf, 0xf8, 0x10, 0x13, 0xa0, + 0x38, 0x40, 0x40, 0x87, 0x80, + + /* U+7EE2 "绢" */ + 0x10, 0x0, 0x43, 0xf8, 0x84, 0x12, 0x4f, 0xe4, + 0x80, 0x1e, 0x0, 0x8, 0xff, 0x91, 0x1, 0x43, + 0xff, 0xf4, 0x4, 0x8, 0x8, 0x1f, 0xf1, 0xa0, + 0x3c, 0x40, 0x40, 0x8f, 0x0, + + /* U+7EE3 "绣" */ + 0x20, 0x0, 0x4f, 0xf1, 0x1, 0x2, 0x42, 0x9, + 0x7f, 0xdc, 0x2c, 0x8, 0x96, 0x26, 0x22, 0x87, + 0xf1, 0xe2, 0x20, 0x4, 0x70, 0x10, 0x2f, 0x20, + 0x40, 0xc1, 0x3, 0x1e, 0x0, + + /* U+7EE5 "绥" */ + 0x10, 0x0, 0x27, 0xf8, 0x80, 0x2, 0x12, 0x24, + 0xa2, 0x5e, 0x21, 0x4, 0x0, 0x13, 0xff, 0x41, + 0x8, 0xf2, 0x10, 0xc, 0x40, 0x7, 0x80, 0x87, + 0x1e, 0x31, 0x81, 0x80, 0x80, + + /* U+7EE6 "绦" */ + 0x0, 0x0, 0x21, 0x0, 0x83, 0xf1, 0xc, 0x24, + 0x68, 0x89, 0x4a, 0x1c, 0x8, 0x8, 0x6c, 0x23, + 0x26, 0x80, 0x41, 0xef, 0xf8, 0x1, 0x0, 0x12, + 0x83, 0x24, 0x99, 0x88, 0x80, 0x70, + + /* U+7EE7 "继" */ + 0x12, 0x10, 0x22, 0x92, 0x42, 0x94, 0x4a, 0x54, + 0xfa, 0x10, 0x12, 0xfe, 0x22, 0x10, 0x22, 0x38, + 0x42, 0x54, 0x7a, 0x54, 0x2, 0x90, 0x2, 0x10, + 0xfa, 0x10, 0x3, 0xff, + + /* U+7EE8 "绨" */ + 0x21, 0x10, 0x41, 0x21, 0xf, 0xf2, 0x42, 0x29, + 0x4, 0x4e, 0x7f, 0x88, 0x90, 0x22, 0x20, 0xc3, + 0xff, 0xe0, 0x84, 0x3, 0x8, 0xa, 0x2f, 0x25, + 0xc0, 0x88, 0x0, 0x10, 0x0, + + /* U+7EE9 "绩" */ + 0x0, 0x40, 0x40, 0x80, 0x9f, 0xfa, 0x2, 0x4, + 0xbf, 0xde, 0x8, 0x5, 0xff, 0x90, 0x0, 0x43, + 0xfc, 0x84, 0xb, 0xe8, 0x10, 0x12, 0x20, 0x24, + 0x5e, 0xe, 0x0, 0x63, 0x3, 0x0, + + /* U+7EEA "绪" */ + 0x20, 0x80, 0x41, 0x9, 0xf, 0xd2, 0x44, 0x49, + 0x9, 0x1e, 0xff, 0xc8, 0x10, 0x20, 0x40, 0xc3, + 0xfd, 0xda, 0x8, 0x7, 0xf0, 0x8, 0x2f, 0x10, + 0x40, 0x3f, 0x80, 0x41, 0x0, + + /* U+7EEB "绫" */ + 0x20, 0x40, 0x40, 0x81, 0xf, 0xf2, 0x2, 0x9, + 0x7f, 0xfe, 0x0, 0x8, 0x46, 0x23, 0x43, 0x80, + 0xf9, 0xe2, 0x10, 0xe, 0x40, 0x24, 0x83, 0x86, + 0x18, 0x33, 0x3, 0x81, 0x80, + + /* U+7EED "续" */ + 0x10, 0x40, 0x40, 0x81, 0xf, 0xf2, 0x2, 0x9, + 0x4, 0x1e, 0x77, 0xc8, 0xc8, 0xa2, 0x50, 0x43, + 0x21, 0xe0, 0x40, 0x1f, 0xf8, 0x2, 0x7, 0xf, + 0x10, 0x61, 0x83, 0x0, 0x80, + + /* U+7EEE "绮" */ + 0x10, 0x20, 0x20, 0x40, 0x8f, 0xf9, 0x1, 0x4, + 0x87, 0x9, 0x31, 0xbc, 0x80, 0x13, 0xff, 0x20, + 0x4, 0x87, 0xc9, 0xe8, 0x90, 0x11, 0x20, 0x3e, + 0x5f, 0x0, 0x80, 0x1, 0x0, 0x1c, + + /* U+7EEF "绯" */ + 0x10, 0x90, 0x41, 0x20, 0x82, 0x42, 0x3c, 0xfc, + 0x89, 0x1e, 0x12, 0xd, 0xe7, 0x90, 0x48, 0x40, + 0x91, 0xf1, 0x20, 0x1e, 0x78, 0x4, 0x81, 0x89, + 0x1c, 0x12, 0x0, 0x24, 0x0, + + /* U+7EF0 "绰" */ + 0x0, 0x40, 0x20, 0x80, 0x81, 0xf2, 0x2, 0x4, + 0xbf, 0xd2, 0x40, 0xbc, 0x81, 0x11, 0xfe, 0x42, + 0x4, 0x87, 0xfb, 0xe1, 0x0, 0x2, 0x0, 0x7f, + 0xe7, 0x8, 0x30, 0x10, 0x0, 0x20, + + /* U+7EF1 "绱" */ + 0x20, 0x40, 0x91, 0x14, 0x24, 0x92, 0x10, 0x97, + 0xff, 0x90, 0x12, 0x40, 0x51, 0x7d, 0x85, 0x17, + 0xd4, 0x50, 0x51, 0x41, 0x7d, 0x34, 0x7, 0x10, + 0x10, 0x43, 0xc0, + + /* U+7EF2 "绲" */ + 0x10, 0x0, 0x13, 0xfe, 0x22, 0x2, 0x4b, 0xfe, + 0x4a, 0x2, 0xf2, 0x2, 0x23, 0xfe, 0x20, 0x0, + 0x42, 0x20, 0x7b, 0xee, 0x2, 0x30, 0x2, 0x21, + 0x1a, 0x61, 0xe3, 0xa1, 0x2, 0x3e, + + /* U+7EF3 "绳" */ + 0x0, 0x0, 0x11, 0xfc, 0x11, 0x4, 0x21, 0x4, + 0x45, 0xfc, 0x48, 0x20, 0x73, 0xfe, 0x12, 0x22, + 0x22, 0x22, 0x43, 0xfe, 0x7a, 0x22, 0x2, 0x22, + 0x3, 0xfe, 0x0, 0x21, 0x78, 0x21, 0x0, 0x3e, + + /* U+7EF4 "维" */ + 0x11, 0x40, 0x42, 0x40, 0x84, 0x2, 0x1f, 0xf9, + 0x22, 0x1e, 0xc4, 0xa, 0xff, 0xa1, 0x10, 0x82, + 0x21, 0xe7, 0xfc, 0x8, 0x80, 0x11, 0x7, 0xa2, + 0x10, 0x7f, 0xc0, 0x80, 0x0, + + /* U+7EF5 "绵" */ + 0x0, 0x40, 0x20, 0x80, 0x87, 0xf1, 0x10, 0x24, + 0xa0, 0x59, 0x3f, 0xbc, 0x81, 0x10, 0xfe, 0x20, + 0x20, 0x80, 0x43, 0xef, 0xf8, 0x11, 0x10, 0x22, + 0x23, 0x44, 0x78, 0x8b, 0x0, 0x10, + + /* U+7EF6 "绶" */ + 0x10, 0x0, 0x4f, 0xfc, 0x81, 0x12, 0x52, 0x2c, + 0x80, 0x9e, 0xff, 0xc9, 0x0, 0x90, 0x0, 0x43, + 0xfd, 0xf2, 0x10, 0x2, 0x40, 0x4, 0x81, 0x86, + 0x1c, 0x33, 0x3, 0x81, 0x80, + + /* U+7EF7 "绷" */ + 0x0, 0x0, 0x9e, 0xf2, 0x4a, 0x51, 0x29, 0x94, + 0xa6, 0x5e, 0xfe, 0x4a, 0x51, 0x29, 0x44, 0xa7, + 0xde, 0xf8, 0x4a, 0x41, 0x29, 0x14, 0xa7, 0xa3, + 0x10, 0x8c, 0x42, 0xf7, + + /* U+7EF8 "绸" */ + 0x10, 0x0, 0x47, 0xfc, 0x88, 0x8a, 0x57, 0xd9, + 0x22, 0x3e, 0x44, 0x48, 0xbe, 0x91, 0x1, 0x42, + 0xfb, 0xf5, 0x14, 0xa, 0x28, 0x57, 0xdf, 0x40, + 0x20, 0x80, 0x42, 0x7, 0x80, + + /* U+7EFA "绺" */ + 0x21, 0x10, 0x21, 0x10, 0x42, 0xd8, 0x52, 0x56, + 0x96, 0x90, 0x61, 0x90, 0x21, 0x80, 0x42, 0x7e, + 0x44, 0x0, 0xf3, 0xfc, 0x2, 0x4, 0x2, 0x4, + 0xfa, 0x4, 0x3, 0xfc, 0x2, 0x4, + + /* U+7EFB "绻" */ + 0x22, 0x24, 0x42, 0x49, 0x1, 0x2, 0x5f, 0xf9, + 0x4, 0x1e, 0x8, 0x9, 0xff, 0x90, 0x48, 0x41, + 0x9, 0xe7, 0xf8, 0x14, 0x28, 0xb, 0xaf, 0x90, + 0x40, 0x20, 0x80, 0x7e, 0x0, + + /* U+7EFC "综" */ + 0x10, 0x20, 0x10, 0x10, 0x23, 0xff, 0x22, 0x1, + 0x4e, 0x1, 0x79, 0xfe, 0x10, 0x0, 0x20, 0x0, + 0x43, 0xff, 0x78, 0x20, 0x0, 0xa4, 0x1, 0x22, + 0x1a, 0x23, 0x62, 0x21, 0x0, 0xe0, + + /* U+7EFD "绽" */ + 0x10, 0x20, 0x20, 0x20, 0x8f, 0xf9, 0x10, 0x14, + 0xe0, 0x2f, 0x7f, 0xc4, 0x8, 0x10, 0x10, 0x41, + 0x20, 0xf2, 0x78, 0x8, 0x80, 0x19, 0xf, 0xaa, + 0x0, 0x8f, 0xc0, 0x0, 0x0, + + /* U+7EFE "绾" */ + 0x10, 0x40, 0x40, 0x41, 0x1f, 0xfa, 0x20, 0x19, + 0xc0, 0x3e, 0x7f, 0x88, 0x81, 0x21, 0xfe, 0x42, + 0x1, 0xe4, 0x0, 0xf, 0xf0, 0x10, 0x23, 0xa0, + 0x58, 0x7f, 0x80, 0x81, 0x0, + + /* U+7EFF "绿" */ + 0x10, 0x0, 0x4f, 0xf0, 0x80, 0x22, 0x5f, 0xc5, + 0x0, 0x9e, 0x1, 0x9, 0xff, 0x90, 0x20, 0x44, + 0x45, 0xb6, 0xd0, 0x83, 0x40, 0x1a, 0xc1, 0xc4, + 0x5c, 0x8, 0x0, 0xf0, 0x0, + + /* U+7F00 "缀" */ + 0x10, 0x0, 0x27, 0xbe, 0x20, 0x82, 0x42, 0xb4, + 0x91, 0x8, 0xf2, 0x96, 0x24, 0x20, 0x47, 0xfe, + 0x40, 0xa2, 0x74, 0xa4, 0x3, 0x14, 0x19, 0x18, + 0xe2, 0x9c, 0xc, 0x62, + + /* U+7F01 "缁" */ + 0x0, 0x0, 0x11, 0x32, 0x22, 0x24, 0x26, 0x44, + 0x4b, 0x22, 0x89, 0x13, 0xf0, 0x0, 0x23, 0xfe, + 0x22, 0x22, 0x42, 0x22, 0x7a, 0x22, 0x3, 0xfe, + 0x2, 0x22, 0x3a, 0x22, 0x43, 0xfe, 0x2, 0x2, + + /* U+7F02 "缂" */ + 0x11, 0x8, 0x2f, 0xfc, 0x84, 0x22, 0x48, 0x44, + 0x9f, 0x9e, 0x8, 0x8, 0xff, 0x11, 0x22, 0x42, + 0x44, 0xf7, 0xf8, 0x1, 0x0, 0x3f, 0xf7, 0x84, + 0x0, 0x8, 0x0, 0x10, 0x0, + + /* U+7F03 "缃" */ + 0x12, 0x0, 0x44, 0x7d, 0x8, 0x8a, 0x3d, 0x19, + 0x22, 0x3e, 0x47, 0xc9, 0x48, 0xa3, 0x51, 0x46, + 0x23, 0xf4, 0x7c, 0x28, 0x88, 0x11, 0x13, 0x22, + 0x28, 0x47, 0xc0, 0x88, 0x80, + + /* U+7F04 "缄" */ + 0x0, 0x10, 0x40, 0x38, 0x80, 0x42, 0x3f, 0xf4, + 0x41, 0x12, 0x82, 0x39, 0x75, 0x12, 0xa, 0x45, + 0xd5, 0xa, 0xb3, 0x95, 0x60, 0x2a, 0x81, 0x5d, + 0x3d, 0x5, 0x42, 0x12, 0x8c, 0x43, + + /* U+7F05 "缅" */ + 0x10, 0x0, 0x27, 0xff, 0x20, 0x40, 0x48, 0x40, + 0x4f, 0xbe, 0xf4, 0x92, 0x14, 0x92, 0x24, 0xf2, + 0x44, 0x92, 0x7c, 0x92, 0x4, 0xf2, 0x4, 0x92, + 0x3c, 0x92, 0xc3, 0xfe, 0x4, 0x2, + + /* U+7F06 "缆" */ + 0x10, 0x90, 0x25, 0x20, 0x8a, 0x7a, 0x15, 0x4, + 0xaa, 0xce, 0x0, 0x4, 0xff, 0x11, 0x2, 0x42, + 0x44, 0xf4, 0x88, 0x9, 0x90, 0x3, 0x3, 0x8a, + 0x28, 0x24, 0x41, 0x8f, 0x80, + + /* U+7F07 "缇" */ + 0x0, 0x0, 0x47, 0xf0, 0x88, 0x22, 0x10, 0x44, + 0x3f, 0x93, 0x41, 0x3c, 0xfe, 0x10, 0x0, 0x47, + 0xfe, 0x80, 0x83, 0xc9, 0x0, 0x13, 0xe0, 0x24, + 0x7, 0xc8, 0x31, 0x50, 0x4, 0x7f, 0x0, 0x0, + + /* U+7F08 "缈" */ + 0x0, 0x8, 0x27, 0x88, 0x24, 0x88, 0x44, 0xaa, + 0x97, 0xaa, 0xf4, 0xaa, 0x24, 0xc9, 0x44, 0x88, + 0x47, 0x8a, 0xf4, 0x8a, 0x4, 0x84, 0x4, 0x88, + 0x7, 0x90, 0xf0, 0x20, 0x0, 0xc0, + + /* U+7F09 "缉" */ + 0x0, 0x0, 0x47, 0xf8, 0x88, 0x12, 0x10, 0x24, + 0x3f, 0xd3, 0x0, 0x3d, 0xff, 0x91, 0x2, 0x43, + 0xfc, 0x84, 0xb, 0xe8, 0x10, 0x1f, 0xe0, 0x20, + 0x5e, 0xff, 0xc0, 0x1, 0x0, 0x2, + + /* U+7F0B "缋" */ + 0x10, 0x40, 0x47, 0xf8, 0x89, 0x12, 0x52, 0x24, + 0xbf, 0xde, 0x8, 0x9, 0xff, 0x90, 0x0, 0x43, + 0xfd, 0xf4, 0x8, 0x9, 0x10, 0x12, 0x23, 0x87, + 0x8, 0x31, 0x81, 0x80, 0x80, + + /* U+7F0C "缌" */ + 0x10, 0x0, 0x47, 0xf8, 0x88, 0x92, 0x51, 0x28, + 0xbf, 0xde, 0x44, 0x88, 0x89, 0x11, 0xfe, 0x40, + 0x1, 0xf0, 0x40, 0xa, 0x90, 0x14, 0x21, 0xc8, + 0xbc, 0x91, 0x0, 0x3c, 0x0, + + /* U+7F0D "缍" */ + 0x10, 0x0, 0x27, 0xf8, 0x81, 0x2, 0x2, 0x4, + 0xbf, 0xde, 0x49, 0x4, 0x92, 0x13, 0xff, 0x42, + 0x48, 0xe4, 0x90, 0xf, 0xf8, 0x2, 0xf, 0x84, + 0x0, 0x7f, 0x80, + + /* U+7F0E "缎" */ + 0x0, 0x80, 0x4e, 0x71, 0x10, 0xa2, 0x21, 0x49, + 0x72, 0x5c, 0x88, 0xc9, 0x0, 0x23, 0xbe, 0x44, + 0x45, 0xe8, 0x50, 0x1e, 0xa0, 0x60, 0x83, 0x41, + 0x18, 0x8d, 0x81, 0x20, 0x0, + + /* U+7F0F "缏" */ + 0x22, 0x0, 0x47, 0xfd, 0x10, 0x82, 0x2f, 0xe9, + 0x52, 0x5d, 0xa4, 0x8b, 0x7f, 0x22, 0x92, 0x45, + 0x25, 0xb, 0xf9, 0x90, 0x80, 0x25, 0x7, 0x44, + 0x10, 0x96, 0x1, 0x43, 0x80, + + /* U+7F11 "缑" */ + 0x11, 0x0, 0x11, 0x7c, 0x21, 0x4, 0x42, 0x4, + 0x4a, 0xff, 0xf6, 0x0, 0x1a, 0x7e, 0x22, 0x90, + 0x42, 0x90, 0x72, 0x10, 0x2, 0xff, 0x2, 0x30, + 0xa, 0x28, 0xf2, 0x44, 0x2, 0x83, + + /* U+7F12 "缒" */ + 0x0, 0x10, 0x24, 0x40, 0x89, 0xf1, 0x2, 0x24, + 0x84, 0x52, 0x8, 0xbd, 0x9e, 0x11, 0x20, 0x42, + 0x7c, 0xe4, 0x88, 0x9, 0x10, 0x12, 0x20, 0x27, + 0xce, 0xc0, 0x1, 0x40, 0x4, 0x7f, + + /* U+7F13 "缓" */ + 0x10, 0x2, 0x4f, 0xf9, 0x9, 0x12, 0x52, 0x49, + 0x0, 0x9e, 0x7e, 0x88, 0x40, 0x13, 0xff, 0x41, + 0x1, 0xe3, 0xf8, 0xc, 0x20, 0x14, 0x43, 0x47, + 0x19, 0x8a, 0x2, 0xe3, 0x80, + + /* U+7F14 "缔" */ + 0x10, 0x40, 0x40, 0x81, 0x1f, 0xfa, 0x8, 0x48, + 0x81, 0x1e, 0xff, 0xc9, 0x0, 0x92, 0x21, 0x40, + 0x41, 0xe7, 0xf8, 0x9, 0x10, 0x12, 0x2f, 0xa4, + 0x40, 0x4b, 0x80, 0x10, 0x0, + + /* U+7F15 "缕" */ + 0x0, 0x40, 0x44, 0x89, 0x5, 0x22, 0x3f, 0xf9, + 0x6, 0x1e, 0x3b, 0x9, 0x91, 0xa0, 0x20, 0x40, + 0x81, 0xef, 0xfc, 0x4, 0x20, 0x18, 0x81, 0x8e, + 0x1c, 0x3b, 0x3, 0x81, 0x80, + + /* U+7F16 "编" */ + 0x0, 0x40, 0x81, 0x2, 0x7f, 0xd1, 0x1, 0x44, + 0x6, 0x5f, 0xfe, 0x40, 0x9, 0x0, 0x45, 0xfd, + 0x19, 0x5f, 0x65, 0x41, 0x7f, 0xa, 0x57, 0xe9, + 0x50, 0xa5, 0x44, 0x83, + + /* U+7F17 "缗" */ + 0x20, 0x0, 0x4f, 0xf9, 0x90, 0x12, 0x20, 0x24, + 0x7f, 0xd2, 0x88, 0x3d, 0xef, 0x12, 0x11, 0x47, + 0x9f, 0x0, 0x3, 0xdf, 0xe0, 0x20, 0x40, 0x7f, + 0x86, 0x81, 0x31, 0xfe, 0x2, 0x4, + + /* U+7F18 "缘" */ + 0x11, 0x0, 0x43, 0xf1, 0x8, 0x22, 0x5f, 0xc8, + 0x80, 0x8e, 0xff, 0xc8, 0x31, 0x11, 0xa4, 0x44, + 0x71, 0xe3, 0x50, 0x19, 0xa0, 0x5, 0x23, 0xb2, + 0x58, 0x84, 0x40, 0x70, 0x0, + + /* U+7F19 "缙" */ + 0x10, 0x0, 0x2f, 0xfc, 0x82, 0x42, 0x54, 0xa4, + 0x89, 0x1e, 0xff, 0xc4, 0x0, 0x10, 0x0, 0x43, + 0xfc, 0xf4, 0x8, 0xf, 0xf0, 0x10, 0x23, 0xa0, + 0x48, 0x7f, 0x80, 0x81, 0x0, + + /* U+7F1A "缚" */ + 0x10, 0x0, 0x40, 0x98, 0x9f, 0xfa, 0x2, 0x5, + 0x3f, 0xd2, 0x88, 0xb8, 0xff, 0x12, 0x22, 0x43, + 0xfd, 0x68, 0x9, 0x1f, 0xf8, 0x8, 0x47, 0x88, + 0x90, 0x1, 0x0, 0x1c, 0x0, + + /* U+7F1B "缛" */ + 0x0, 0x0, 0x4f, 0xfd, 0x10, 0x2, 0x2f, 0xe9, + 0x40, 0x1e, 0xff, 0xc9, 0x29, 0x22, 0x4c, 0x44, + 0xed, 0xe8, 0x14, 0x1f, 0xf8, 0x24, 0x4f, 0x84, + 0x81, 0x1, 0x4, 0xe, 0x0, + + /* U+7F1C "缜" */ + 0x10, 0x40, 0x4f, 0xf8, 0x81, 0x2, 0x5f, 0xe8, + 0xa0, 0x5e, 0x7f, 0x88, 0x81, 0x11, 0xfe, 0x42, + 0x4, 0xe7, 0xf8, 0x8, 0x10, 0x3f, 0xf1, 0x88, + 0x1c, 0x63, 0x81, 0x0, 0x0, + + /* U+7F1D "缝" */ + 0x28, 0x40, 0x48, 0xf9, 0x13, 0x12, 0xa, 0x49, + 0x3, 0x14, 0x39, 0xdb, 0x8, 0x22, 0x7f, 0x44, + 0x21, 0xe9, 0xf8, 0x10, 0x80, 0x2f, 0xfe, 0x42, + 0x1, 0x60, 0x4, 0x7f, 0x80, + + /* U+7F1F "缟" */ + 0x0, 0x20, 0x40, 0x40, 0x9f, 0xfa, 0x0, 0x4, + 0x9f, 0xd2, 0x40, 0xbc, 0x81, 0x10, 0xfe, 0x20, + 0x0, 0x8f, 0xfd, 0x10, 0xd, 0xa7, 0xd0, 0x48, + 0xae, 0x9f, 0x61, 0x0, 0x82, 0x6, + + /* U+7F20 "缠" */ + 0x10, 0x20, 0x10, 0x20, 0x23, 0xff, 0x22, 0x0, + 0x4a, 0x0, 0x4a, 0xfe, 0xf2, 0x92, 0x22, 0xfe, + 0x42, 0x92, 0x7a, 0xfe, 0x2, 0x10, 0x2, 0x10, + 0xc, 0xfe, 0xf4, 0x10, 0x9, 0xff, 0x0, 0x0, + + /* U+7F21 "缡" */ + 0x0, 0x40, 0x40, 0x40, 0x9f, 0xfa, 0x0, 0x4, + 0xcf, 0x52, 0x9a, 0xbd, 0x3, 0x11, 0xfe, 0x20, + 0x20, 0x80, 0x81, 0x1f, 0xff, 0xa4, 0x10, 0x49, + 0x20, 0xbf, 0x7d, 0x0, 0x82, 0x6, + + /* U+7F22 "缢" */ + 0x22, 0x8, 0x42, 0x11, 0x2, 0x42, 0x3f, 0xf9, + 0x0, 0x1e, 0x32, 0x8, 0x83, 0x22, 0x1, 0x43, + 0xfd, 0xe9, 0x48, 0x12, 0x90, 0x25, 0x2f, 0x4a, + 0x41, 0xff, 0xc0, + + /* U+7F23 "缣" */ + 0x11, 0x8, 0x41, 0x20, 0x8f, 0xfa, 0x45, 0x4, + 0xbf, 0xde, 0x14, 0x8d, 0xff, 0x90, 0x52, 0x40, + 0xa5, 0xf7, 0xf8, 0x2, 0x40, 0xd, 0x81, 0xaa, + 0x9d, 0x94, 0xc0, 0x28, 0x0, + + /* U+7F24 "缤" */ + 0x0, 0x40, 0x40, 0x80, 0x9f, 0xf2, 0x20, 0x24, + 0x80, 0x12, 0x7e, 0x3c, 0x80, 0x11, 0x0, 0x43, + 0xfc, 0x84, 0x23, 0xc8, 0x40, 0x3f, 0xf0, 0x0, + 0xf, 0x33, 0x21, 0x81, 0x80, + + /* U+7F25 "缥" */ + 0x0, 0x0, 0x4f, 0xfc, 0x82, 0x42, 0x4, 0x84, + 0x7f, 0xdb, 0x92, 0xbd, 0x25, 0x13, 0xfe, 0x20, + 0x0, 0x87, 0xfb, 0xc0, 0x0, 0x3f, 0xf0, 0x4, + 0xf, 0x69, 0x23, 0x11, 0x80, 0xe0, + + /* U+7F26 "缦" */ + 0x23, 0xfc, 0x44, 0x9, 0xf, 0xf2, 0x50, 0x29, + 0x3f, 0xde, 0x0, 0x9, 0xff, 0x92, 0x49, 0x47, + 0xff, 0xe0, 0x0, 0xf, 0xf0, 0x8, 0x41, 0x89, + 0x1c, 0x1e, 0x3, 0xc3, 0x80, + + /* U+7F27 "缧" */ + 0x0, 0x0, 0x13, 0xfe, 0x22, 0x22, 0x22, 0x22, + 0x43, 0xfe, 0x4a, 0x22, 0x7b, 0xfe, 0x10, 0x60, + 0x21, 0x8c, 0x63, 0xf0, 0x58, 0x88, 0x23, 0xfe, + 0x0, 0x28, 0x7b, 0x24, 0x4, 0x23, 0x0, 0xe0, + + /* U+7F28 "缨" */ + 0x10, 0x0, 0x4f, 0xbc, 0x95, 0xaa, 0x6b, 0x55, + 0x56, 0xbe, 0xad, 0x48, 0xa5, 0x13, 0x11, 0x40, + 0x80, 0xef, 0xfc, 0x4, 0x60, 0x18, 0x87, 0x8e, + 0x0, 0x73, 0x81, 0x0, 0x0, + + /* U+7F29 "缩" */ + 0x20, 0x0, 0x40, 0x41, 0x1f, 0xfa, 0x20, 0x19, + 0x50, 0x1e, 0x4f, 0x88, 0x88, 0x23, 0x7e, 0x4a, + 0x85, 0xe5, 0x8, 0xb, 0xf0, 0x14, 0x27, 0x28, + 0x50, 0x5f, 0x80, 0xa1, 0x0, + + /* U+7F2A "缪" */ + 0x20, 0x0, 0x4f, 0xf9, 0x2, 0x12, 0x35, 0xa9, + 0x1b, 0xde, 0xd0, 0x88, 0x11, 0x10, 0xf0, 0x47, + 0x19, 0x11, 0x8f, 0xcc, 0x40, 0x3, 0x0, 0x39, + 0x9e, 0xc, 0x0, 0xe0, 0x2, 0x0, + + /* U+7F2B "缫" */ + 0x11, 0x24, 0x24, 0x90, 0x89, 0x92, 0x49, 0x4, + 0xbf, 0xde, 0x48, 0x84, 0xff, 0x11, 0x22, 0x43, + 0xfc, 0xf0, 0x80, 0x1f, 0xf8, 0x3, 0x3, 0x8d, + 0x18, 0xe9, 0xc0, 0x10, 0x0, + + /* U+7F2C "缬" */ + 0x22, 0x0, 0x22, 0x3e, 0x4f, 0x88, 0x42, 0x10, + 0x92, 0x3e, 0xa2, 0x22, 0xef, 0xa2, 0x40, 0x2a, + 0x40, 0x2a, 0xbf, 0x2a, 0x49, 0x2a, 0x9, 0x2a, + 0x19, 0x1c, 0xe7, 0x26, 0x8, 0x63, + + /* U+7F2D "缭" */ + 0x0, 0x40, 0x40, 0x80, 0x9f, 0xfa, 0x3, 0x8, + 0xa9, 0x52, 0x21, 0x1d, 0xff, 0x95, 0x2, 0x42, + 0x4, 0x87, 0xfa, 0x48, 0x13, 0x1f, 0xe0, 0x4, + 0x6, 0x49, 0x33, 0x11, 0x80, 0xe0, + + /* U+7F2E "缮" */ + 0x11, 0x8, 0x21, 0x20, 0x9f, 0xfb, 0x2, 0x4, + 0xbf, 0xd2, 0x8, 0x1d, 0xff, 0x90, 0xa4, 0x41, + 0x50, 0xed, 0x5c, 0xf, 0xf0, 0x10, 0x23, 0xa0, + 0x58, 0x7f, 0x80, 0x81, 0x0, + + /* U+7F2F "缯" */ + 0x21, 0x8, 0x42, 0x21, 0x1f, 0xfa, 0x22, 0x19, + 0x55, 0xbe, 0x8a, 0x49, 0xff, 0xa0, 0x0, 0x43, + 0xfd, 0xe4, 0x8, 0x8, 0x10, 0x1f, 0xe1, 0x20, + 0x5c, 0x7f, 0x80, 0x81, 0x0, + + /* U+7F30 "缰" */ + 0x13, 0xff, 0x10, 0x0, 0x23, 0xfe, 0x42, 0x22, + 0x4b, 0xfe, 0x72, 0x22, 0x11, 0xdc, 0x27, 0xff, + 0x20, 0x0, 0x7b, 0xfe, 0x3, 0xfe, 0x2, 0x22, + 0x1b, 0xfe, 0x60, 0x0, 0x7, 0xff, + + /* U+7F31 "缱" */ + 0x14, 0x10, 0x24, 0xfe, 0x22, 0x92, 0x48, 0xfe, + 0x48, 0x10, 0x70, 0xef, 0x26, 0xfe, 0x22, 0x82, + 0x42, 0xfe, 0x7a, 0x80, 0x2, 0xfe, 0x2, 0x82, + 0x3a, 0xfe, 0xc5, 0x0, 0x8, 0xff, + + /* U+7F32 "缲" */ + 0x10, 0x0, 0x23, 0xf0, 0x88, 0x22, 0x4f, 0xc4, + 0x80, 0x1e, 0x73, 0x85, 0x29, 0x11, 0xce, 0x40, + 0x0, 0xb0, 0x80, 0x9f, 0xf8, 0x7, 0x1, 0x95, + 0x9c, 0xc8, 0xc0, 0x10, 0x0, + + /* U+7F33 "缳" */ + 0x10, 0x0, 0x47, 0xf8, 0x8a, 0x52, 0x14, 0xa4, + 0xbf, 0xd2, 0x0, 0x3d, 0xff, 0x90, 0x0, 0x23, + 0xfc, 0x84, 0xb, 0xcf, 0xf0, 0x3, 0x0, 0xa, + 0x47, 0x63, 0x33, 0x4b, 0x0, 0xe1, + + /* U+7F34 "缴" */ + 0x2, 0x10, 0x44, 0x20, 0x9e, 0x42, 0x45, 0x5, + 0x8b, 0xf2, 0xf4, 0xaa, 0x39, 0x33, 0xf2, 0x42, + 0x25, 0x1f, 0x53, 0xd0, 0x60, 0x3c, 0xc0, 0x51, + 0x1f, 0x23, 0x2, 0x49, 0xd, 0xa2, + + /* U+7F35 "缵" */ + 0x11, 0x8, 0x46, 0x30, 0x9e, 0xfa, 0x29, 0x48, + 0xfb, 0xfe, 0x63, 0x49, 0x39, 0x94, 0x0, 0x43, + 0xfc, 0x84, 0x9, 0xc9, 0x10, 0x12, 0x21, 0x86, + 0x1c, 0x33, 0x3, 0x81, 0x80, + + /* U+7F36 "缶" */ + 0x10, 0x0, 0x20, 0x0, 0xff, 0xf1, 0x8, 0x4, + 0x10, 0x0, 0x20, 0x0, 0x40, 0x7f, 0xff, 0x1, + 0x0, 0x42, 0x10, 0x84, 0x21, 0x8, 0x42, 0x10, + 0x87, 0xff, 0x0, 0x2, 0x0, + + /* U+7F38 "缸" */ + 0x20, 0x0, 0x40, 0xfe, 0x7e, 0x10, 0x50, 0x10, + 0x90, 0x10, 0x10, 0x10, 0xfe, 0x10, 0x10, 0x10, + 0x52, 0x10, 0x52, 0x10, 0x52, 0x10, 0x52, 0x10, + 0x7e, 0x10, 0x3, 0xff, + + /* U+7F3A "缺" */ + 0x20, 0x20, 0x80, 0x41, 0xf8, 0x82, 0x8f, 0xe9, + 0x2, 0x42, 0x4, 0xbf, 0x89, 0x8, 0x12, 0x92, + 0xfb, 0x24, 0xa2, 0x49, 0x44, 0x96, 0x47, 0xe8, + 0xc0, 0x60, 0x40, + + /* U+7F42 "罂" */ + 0x7e, 0x7c, 0x84, 0x89, 0x29, 0x52, 0x92, 0xa5, + 0x25, 0x3, 0x85, 0x18, 0xb1, 0x4c, 0x0, 0x1f, + 0xf8, 0xc2, 0x3, 0xff, 0xf8, 0x8, 0x2, 0x10, + 0x4, 0x21, 0xf, 0xfe, 0x0, + + /* U+7F44 "罄" */ + 0x8, 0x79, 0xfe, 0x90, 0x21, 0x23, 0xb4, 0x30, + 0xf, 0xcf, 0xcd, 0x1f, 0x8e, 0x20, 0x67, 0x90, + 0x1, 0x3f, 0xf0, 0x84, 0x7, 0xff, 0xf0, 0x10, + 0x4, 0x21, 0xf, 0xfe, 0x0, + + /* U+7F45 "罅" */ + 0x40, 0x41, 0x1, 0xf4, 0x4, 0x1e, 0xff, 0xa2, + 0x46, 0x89, 0xe2, 0x3c, 0x7e, 0x9f, 0x22, 0x2, + 0xaf, 0xfa, 0xaa, 0x6a, 0x8a, 0xad, 0xfe, 0xb0, + 0x8f, 0xc2, 0x1, 0x38, + + /* U+7F50 "罐" */ + 0x40, 0x91, 0x1f, 0xf7, 0x89, 0x28, 0xf7, 0xa4, + 0x64, 0x9f, 0xff, 0x82, 0x8, 0x48, 0x23, 0xfe, + 0xbf, 0xfa, 0xa2, 0x2a, 0xff, 0xaa, 0x23, 0x6f, + 0xf0, 0xa0, 0x0, + + /* U+7F51 "网" */ + 0xff, 0xfc, 0x0, 0x60, 0x3, 0x48, 0x5a, 0x52, + 0xcc, 0xa6, 0x23, 0x33, 0x19, 0x94, 0xcd, 0x29, + 0x68, 0x8b, 0x0, 0x18, 0x0, 0xc0, 0x3c, + + /* U+7F54 "罔" */ + 0xff, 0xfc, 0x2, 0x62, 0x23, 0x7f, 0xd8, 0x20, + 0xc1, 0x6, 0x4, 0x37, 0xfd, 0x88, 0xc, 0x40, + 0x63, 0xfb, 0x0, 0x18, 0x0, 0xc0, 0x3c, + + /* U+7F55 "罕" */ + 0x7f, 0xfd, 0x0, 0xa, 0x11, 0x10, 0xc1, 0xce, + 0x0, 0x67, 0xff, 0x0, 0x40, 0x0, 0x80, 0x1, + 0x1, 0xff, 0xfc, 0x4, 0x0, 0x8, 0x0, 0x10, + 0x0, 0x20, 0x0, + + /* U+7F57 "罗" */ + 0x7f, 0xfe, 0x88, 0x85, 0x11, 0xa, 0x22, 0x14, + 0x44, 0x2f, 0xff, 0xc1, 0x0, 0x7, 0xfe, 0x10, + 0xc, 0x50, 0x31, 0x18, 0xc0, 0x16, 0x0, 0x38, + 0x3, 0x80, 0x18, 0x0, 0x0, + + /* U+7F58 "罘" */ + 0x3f, 0xfc, 0x44, 0x88, 0x89, 0x11, 0xff, 0xe0, + 0x0, 0x0, 0x0, 0x1f, 0xff, 0x80, 0xc0, 0x3, + 0x60, 0xa, 0x20, 0x64, 0x33, 0x8, 0x10, 0x10, + 0x0, 0x20, 0x0, + + /* U+7F5A "罚" */ + 0x7f, 0xfd, 0x11, 0x14, 0x44, 0x5f, 0xff, 0x0, + 0x0, 0x42, 0x10, 0x8, 0x78, 0x21, 0x20, 0x84, + 0x82, 0x12, 0x48, 0x4a, 0x1, 0x30, 0x4, 0x80, + 0xf0, + + /* U+7F5F "罟" */ + 0x7f, 0xfc, 0x88, 0x89, 0x11, 0x13, 0xff, 0xe0, + 0x10, 0x1f, 0xff, 0xc0, 0x40, 0x0, 0x80, 0x3f, + 0xf8, 0x40, 0x10, 0x80, 0x21, 0x0, 0x43, 0xff, + 0x84, 0x1, 0x0, + + /* U+7F61 "罡" */ + 0x7f, 0xfc, 0x88, 0x89, 0x11, 0x13, 0xff, 0xe0, + 0x0, 0x0, 0x0, 0x1f, 0xff, 0x0, 0x40, 0x10, + 0x80, 0x21, 0xf8, 0x42, 0x0, 0x84, 0xf, 0xff, + 0xe0, + + /* U+7F62 "罢" */ + 0x7f, 0xfc, 0x88, 0x89, 0x11, 0x13, 0xff, 0xe0, + 0x0, 0x0, 0x20, 0xf, 0xfe, 0x0, 0x80, 0x1, + 0x1, 0xff, 0xfc, 0x19, 0x0, 0xc1, 0x7, 0xff, + 0x88, 0x0, 0x80, + + /* U+7F68 "罨" */ + 0x7f, 0xf9, 0x22, 0x24, 0x88, 0x9f, 0xfe, 0x2, + 0x3, 0xff, 0xf0, 0x84, 0x4, 0x88, 0x7f, 0xfa, + 0x88, 0x53, 0xff, 0x8, 0x84, 0x3f, 0xf4, 0x8, + 0x10, 0x1f, 0xc0, + + /* U+7F69 "罩" */ + 0x7f, 0xfc, 0x88, 0x89, 0x11, 0x13, 0xff, 0xe0, + 0x1f, 0xe0, 0x20, 0xf, 0xfe, 0x10, 0x4, 0x3f, + 0xf8, 0x40, 0x10, 0xff, 0xe0, 0x8, 0xf, 0xff, + 0xe0, 0x20, 0x0, 0x40, 0x0, + + /* U+7F6A "罪" */ + 0xff, 0xfa, 0x22, 0x28, 0x88, 0xa2, 0x22, 0xff, + 0xf8, 0x0, 0xf, 0x8f, 0xc2, 0x20, 0x8, 0x83, + 0xe3, 0xe0, 0x88, 0x2, 0x20, 0xf8, 0xfc, 0x22, + 0x0, 0x88, 0x0, + + /* U+7F6E "置" */ + 0x7f, 0xfc, 0x88, 0x89, 0x11, 0x13, 0xff, 0xe0, + 0x10, 0x1f, 0xff, 0xc0, 0x40, 0x1f, 0x7c, 0x3f, + 0xf8, 0x40, 0x10, 0xff, 0xe1, 0x0, 0x43, 0xff, + 0x84, 0x1, 0x3f, 0xff, 0x80, + + /* U+7F71 "罱" */ + 0x7f, 0xfc, 0x88, 0x89, 0xff, 0xf0, 0x8, 0xf, + 0xff, 0xe0, 0x20, 0x1f, 0xff, 0x22, 0x12, 0x42, + 0x44, 0x9f, 0xe9, 0x4, 0x12, 0xff, 0xe4, 0x10, + 0x48, 0x27, 0x80, + + /* U+7F72 "署" */ + 0x7f, 0xfc, 0x88, 0x89, 0x11, 0x13, 0xff, 0xe0, + 0x20, 0xf, 0xf9, 0x0, 0x88, 0x7f, 0xff, 0x2, + 0x0, 0x3f, 0xf3, 0xc0, 0x20, 0xff, 0xc1, 0x0, + 0x83, 0xff, 0x4, 0x2, 0x0, + + /* U+7F74 "罴" */ + 0x7f, 0xfc, 0x88, 0x89, 0x11, 0x13, 0xff, 0xe0, + 0x0, 0x7, 0xff, 0x0, 0x40, 0x0, 0x80, 0xff, + 0xfe, 0x18, 0x40, 0xff, 0xc1, 0x0, 0x64, 0x8, + 0x88, 0x88, 0xa1, 0x11, 0x80, + + /* U+7F79 "罹" */ + 0x7f, 0xfd, 0x11, 0x14, 0x44, 0x5f, 0xff, 0x0, + 0x0, 0x88, 0x83, 0x3f, 0xe9, 0x88, 0xab, 0xfc, + 0xa8, 0x82, 0x22, 0x8, 0xff, 0x22, 0x20, 0x8f, + 0xf2, 0x20, 0x0, + + /* U+7F7E "罾" */ + 0xff, 0xfc, 0x44, 0x62, 0x23, 0xff, 0xf1, 0x84, + 0x7f, 0xfe, 0x49, 0x30, 0x51, 0xff, 0xf8, 0x0, + 0x1f, 0xfc, 0x80, 0x27, 0xff, 0x3f, 0xf9, 0x0, + 0x40, + + /* U+7F81 "羁" */ + 0x7f, 0xfc, 0x88, 0x89, 0x11, 0x13, 0xff, 0xe4, + 0x20, 0x1f, 0xff, 0x90, 0x92, 0x1e, 0x24, 0x7e, + 0x88, 0xa4, 0xed, 0xf8, 0x8, 0x87, 0xbf, 0xf0, + 0x62, 0x0, 0x84, 0x1f, 0x0, + + /* U+7F8A "羊" */ + 0x10, 0x10, 0x10, 0x40, 0x1, 0x3, 0xff, 0xe0, + 0x10, 0x0, 0x20, 0xf, 0xfe, 0x0, 0x80, 0x1, + 0x0, 0x2, 0x3, 0xff, 0xf8, 0x8, 0x0, 0x10, + 0x0, 0x20, 0x0, 0x40, 0x0, + + /* U+7F8C "羌" */ + 0x8, 0x20, 0x8, 0x83, 0xff, 0xf8, 0x8, 0x0, + 0x10, 0xf, 0xff, 0x80, 0x40, 0x1, 0x0, 0xff, + 0xfe, 0x5, 0x0, 0x12, 0x0, 0x64, 0x11, 0x88, + 0x24, 0x10, 0xf0, 0x3f, 0x0, + + /* U+7F8E "美" */ + 0x8, 0x20, 0x8, 0x83, 0xff, 0xf8, 0x8, 0x0, + 0x10, 0xf, 0xff, 0x80, 0x40, 0x7f, 0xff, 0x0, + 0x0, 0x2, 0x3, 0xff, 0xf8, 0x14, 0x0, 0x2c, + 0x1, 0x86, 0x3c, 0x3, 0x80, + + /* U+7F94 "羔" */ + 0x0, 0x0, 0x10, 0x40, 0x11, 0x7, 0xff, 0xf0, + 0x10, 0x0, 0x20, 0x0, 0x40, 0x3f, 0xfe, 0x1, + 0x0, 0x2, 0x3, 0xff, 0xf8, 0x0, 0x4, 0x88, + 0xc8, 0x88, 0xa1, 0x10, 0xc0, 0x0, + + /* U+7F9A "羚" */ + 0x44, 0x20, 0x90, 0xa0, 0x21, 0x47, 0xe4, 0x41, + 0x10, 0x42, 0x44, 0x7f, 0x8, 0x8, 0x0, 0x11, + 0xfd, 0xf8, 0x10, 0x80, 0x41, 0x9, 0x82, 0xe, + 0x8, 0x4, 0x20, 0x4, 0x0, + + /* U+7F9D "羝" */ + 0x44, 0x2, 0x49, 0xfc, 0x29, 0x20, 0xfd, 0x20, + 0x11, 0x20, 0x11, 0x20, 0x7d, 0xff, 0x11, 0x30, + 0x21, 0x30, 0xfd, 0x30, 0x21, 0x10, 0x21, 0x11, + 0x21, 0x79, 0x41, 0x89, 0xc0, 0x26, + + /* U+7F9E "羞" */ + 0x18, 0x10, 0x18, 0x41, 0xff, 0xf0, 0x10, 0x7, + 0xff, 0xc0, 0x80, 0x3f, 0xff, 0x84, 0x0, 0x1f, + 0xf8, 0x44, 0x11, 0x8, 0x24, 0xff, 0xc0, 0x20, + 0x8f, 0xff, 0xc0, + + /* U+7F9F "羟" */ + 0x4, 0x0, 0x93, 0xf8, 0xa0, 0x17, 0xe0, 0x41, + 0x1, 0x2, 0x5, 0x3f, 0x31, 0x89, 0x81, 0x10, + 0x1, 0xf9, 0xf8, 0x80, 0x81, 0x1, 0x2, 0x2, + 0x8, 0x7f, 0xc0, 0x0, 0x0, + + /* U+7FA1 "羡" */ + 0x10, 0x20, 0x10, 0x83, 0xff, 0xf0, 0x8, 0x7, + 0xff, 0x80, 0x20, 0x3f, 0xff, 0x81, 0x0, 0x42, + 0x0, 0x47, 0xf8, 0x92, 0x20, 0x46, 0x43, 0x1e, + 0x8, 0x62, 0x27, 0x3, 0x80, + + /* U+7FA4 "群" */ + 0x0, 0x44, 0xfc, 0x88, 0x48, 0xa0, 0x97, 0xff, + 0xf1, 0x4, 0x42, 0x8, 0x84, 0x3f, 0x3f, 0x20, + 0x10, 0x7c, 0x21, 0x8b, 0xfd, 0x10, 0x82, 0x21, + 0x7, 0xc2, 0x8, 0x84, 0x0, + + /* U+7FA7 "羧" */ + 0x0, 0x20, 0x48, 0x20, 0x48, 0x48, 0xfd, 0x84, + 0x21, 0xfe, 0x20, 0x0, 0x20, 0xc8, 0xfd, 0x6, + 0x22, 0x40, 0x20, 0x7c, 0xfc, 0x84, 0x21, 0x88, + 0x22, 0x48, 0x20, 0x30, 0x40, 0xd8, 0x87, 0x6, + + /* U+7FAF "羯" */ + 0x0, 0x0, 0x97, 0xf9, 0x48, 0x10, 0x90, 0x2f, + 0xbf, 0xc4, 0x40, 0x88, 0xff, 0x7c, 0x80, 0x21, + 0xfe, 0x44, 0x7, 0xf9, 0x89, 0x15, 0x92, 0x20, + 0x28, 0x7e, 0x90, 0x1, 0x40, 0x1e, + + /* U+7FB0 "羰" */ + 0x8, 0x20, 0xa4, 0x49, 0x48, 0x97, 0xdf, 0xe2, + 0x8, 0x4, 0x7f, 0xfe, 0x40, 0x10, 0x88, 0x21, + 0x15, 0xf5, 0x30, 0x88, 0x61, 0x21, 0x84, 0x84, + 0x89, 0x10, 0xa0, 0xc0, 0x80, + + /* U+7FB2 "羲" */ + 0x0, 0x0, 0x30, 0xc3, 0xff, 0xf0, 0x10, 0x7, + 0xff, 0x80, 0x40, 0x3f, 0xff, 0xbf, 0x48, 0x10, + 0x89, 0xff, 0xfc, 0xb2, 0x6, 0x1a, 0x47, 0xe7, + 0x2f, 0xcc, 0x81, 0x6d, 0xe, 0xe, + + /* U+7FB8 "羸" */ + 0x1, 0x1, 0xff, 0xfc, 0x80, 0x1, 0xff, 0xc0, + 0x0, 0x7, 0xff, 0xf, 0xfe, 0x0, 0x0, 0x7a, + 0xb8, 0x9f, 0xd1, 0xe4, 0xe2, 0x49, 0x47, 0xfe, + 0xa9, 0x25, 0x66, 0x51, 0x80, + + /* U+7FB9 "羹" */ + 0x0, 0x0, 0x10, 0x43, 0xff, 0xf8, 0x8, 0x3, + 0xff, 0x9f, 0xff, 0xc9, 0x22, 0x24, 0x12, 0x7f, + 0xfe, 0x2, 0x0, 0xff, 0xe7, 0xff, 0xf0, 0x10, + 0x1f, 0xff, 0xc0, 0xa0, 0x7e, 0x3f, + + /* U+7FBC "羼" */ + 0x3f, 0xfe, 0x20, 0x2, 0x3f, 0xfe, 0x21, 0x10, + 0x2f, 0xfe, 0x20, 0x40, 0x23, 0xf8, 0x2f, 0xfe, + 0x21, 0x52, 0x2f, 0xbf, 0x22, 0x8, 0x27, 0x1c, + 0x5f, 0xff, 0x44, 0x8, 0xc, 0x8, + + /* U+7FBD "羽" */ + 0x7e, 0xfc, 0x8, 0x10, 0x20, 0x48, 0x91, 0x22, + 0x44, 0x48, 0x90, 0x20, 0x42, 0x85, 0x32, 0x65, + 0xa, 0x10, 0x20, 0x40, 0x81, 0x2, 0x4, 0xf0, + 0xe0, + + /* U+7FBF "羿" */ + 0xfd, 0xfc, 0x8, 0x9, 0x12, 0x11, 0xa3, 0x20, + 0x41, 0x4e, 0x9c, 0xa1, 0x41, 0x8, 0x10, 0x10, + 0x21, 0xff, 0xfc, 0x40, 0x80, 0x81, 0x2, 0x2, + 0x18, 0x4, 0x0, + + /* U+7FC1 "翁" */ + 0x0, 0x40, 0x4, 0x20, 0x19, 0x10, 0x22, 0x4e, + 0x44, 0x20, 0xf, 0xf0, 0x0, 0x10, 0x7e, 0xfc, + 0x2, 0x4, 0x22, 0x44, 0x12, 0x24, 0x1a, 0x34, + 0x62, 0xc4, 0x2, 0x4, 0x1e, 0x3c, + + /* U+7FC5 "翅" */ + 0x20, 0x0, 0x43, 0xbb, 0xf1, 0x11, 0x2, 0x22, + 0x15, 0x44, 0x2a, 0xbf, 0x11, 0x4, 0x6e, 0x4b, + 0x64, 0x90, 0x88, 0xc1, 0x11, 0x8e, 0xe3, 0x80, + 0x18, 0xff, 0xc0, 0x0, 0x0, + + /* U+7FCA "翊" */ + 0x20, 0x0, 0x8f, 0xf1, 0x4, 0x7e, 0x11, 0x2, + 0x66, 0x5, 0x59, 0x15, 0x64, 0x11, 0x50, 0x4d, + 0x47, 0x55, 0x66, 0x48, 0x11, 0x38, 0x47, 0x1, + 0x10, 0x3f, 0xc0, + + /* U+7FCC "翌" */ + 0x7e, 0xfc, 0x4, 0x8, 0x89, 0x10, 0xd1, 0x20, + 0x21, 0xcf, 0x5c, 0x80, 0x81, 0x0, 0x40, 0x3f, + 0xfc, 0x10, 0x20, 0x30, 0x40, 0x21, 0xf, 0xff, + 0xe0, + + /* U+7FCE "翎" */ + 0x10, 0x0, 0x33, 0xbc, 0xa1, 0x9, 0x22, 0x15, + 0x15, 0x32, 0x29, 0x40, 0x12, 0xbe, 0x21, 0x4, + 0xc6, 0x12, 0x95, 0x29, 0x9, 0x42, 0x11, 0x4, + 0x23, 0x8, 0x42, 0x73, 0x80, + + /* U+7FD4 "翔" */ + 0x8, 0x1, 0x4f, 0xf5, 0x4, 0x7e, 0x11, 0x22, + 0x64, 0x89, 0x5f, 0x95, 0x48, 0x11, 0x20, 0x47, + 0xe7, 0x72, 0x66, 0x49, 0x11, 0x40, 0x45, 0x1, + 0x18, 0x3f, 0xc0, + + /* U+7FD5 "翕" */ + 0x0, 0x80, 0x1, 0xc0, 0x6, 0x20, 0x3f, 0xfc, + 0x40, 0x2, 0x1f, 0xf8, 0x10, 0x8, 0x1f, 0xf8, + 0x0, 0x0, 0x7e, 0xfc, 0x2, 0x4, 0x32, 0x64, + 0x2, 0x4, 0x3a, 0x74, 0x42, 0x84, 0x1e, 0x3c, + + /* U+7FD8 "翘" */ + 0x40, 0x0, 0x83, 0xb9, 0x11, 0x17, 0xc2, 0x22, + 0x55, 0x45, 0x2a, 0x85, 0x15, 0x3a, 0x22, 0x8c, + 0xcc, 0x2, 0xab, 0xf9, 0x12, 0x42, 0x24, 0x84, + 0x49, 0x3b, 0xd2, 0x0, 0xc7, 0xff, + + /* U+7FDF "翟" */ + 0x7e, 0xfc, 0x4, 0x8, 0xc9, 0x90, 0x10, 0x67, + 0xaf, 0x42, 0x20, 0xf, 0xff, 0x30, 0x40, 0xbf, + 0xfc, 0x41, 0x0, 0xff, 0xf1, 0x4, 0x3, 0xff, + 0xe4, 0x0, 0x0, + + /* U+7FE0 "翠" */ + 0x7e, 0xfc, 0x4, 0x8, 0x69, 0xd0, 0x10, 0x67, + 0xa7, 0x4f, 0xff, 0x80, 0x4, 0x4, 0x18, 0x16, + 0x68, 0x44, 0x8, 0x4, 0x7, 0xff, 0xf0, 0x10, + 0x0, 0x20, 0x0, + + /* U+7FE1 "翡" */ + 0x4, 0x41, 0xf8, 0xfc, 0x11, 0x3, 0xe3, 0xe0, + 0x44, 0x1f, 0x8f, 0xc1, 0x10, 0x0, 0x0, 0xfe, + 0xfc, 0x4, 0x8, 0x89, 0x90, 0x10, 0xa3, 0x2e, + 0x58, 0x40, 0x8f, 0xf, 0x0, + + /* U+7FE5 "翥" */ + 0x1, 0x0, 0x7f, 0xd8, 0x4, 0xc7, 0xff, 0xf0, + 0x30, 0x1f, 0x9f, 0x3, 0xfe, 0x8, 0x4, 0xf, + 0xf0, 0xfd, 0xf8, 0x89, 0x10, 0x91, 0x21, 0xe3, + 0xcc, 0x58, 0x87, 0x87, 0x0, + + /* U+7FE6 "翦" */ + 0x8, 0x21, 0xff, 0xfc, 0x0, 0x3, 0xfa, 0x44, + 0x14, 0x8f, 0xe9, 0x1f, 0xc2, 0x20, 0x4, 0x6, + 0x31, 0xfb, 0xf9, 0x12, 0x11, 0xa2, 0x23, 0xc7, + 0x58, 0x90, 0x8f, 0xe, 0x0, + + /* U+7FE9 "翩" */ + 0x8, 0x0, 0x11, 0xdd, 0xfc, 0x8a, 0x9, 0x14, + 0x1a, 0xaf, 0xf5, 0x50, 0x2a, 0xaf, 0xb1, 0x6b, + 0x22, 0xd6, 0x4d, 0xad, 0xaa, 0xfd, 0x56, 0xb2, + 0x2d, 0x64, 0x6a, 0x48, 0xd1, 0xf7, + + /* U+7FEE "翮" */ + 0xfe, 0xfc, 0x0, 0x97, 0xc2, 0x51, 0x2d, 0x44, + 0xb5, 0xf2, 0xd0, 0x2, 0x7f, 0x89, 0xa2, 0x6e, + 0x2a, 0xdb, 0xe2, 0x64, 0x89, 0x92, 0x26, 0x48, + 0x98, 0x6f, 0xc0, + + /* U+7FF0 "翰" */ + 0x20, 0x21, 0xf8, 0xa0, 0x82, 0x21, 0x18, 0x37, + 0xa0, 0x11, 0x3b, 0x9e, 0x99, 0x44, 0xb2, 0x89, + 0x54, 0xf2, 0x88, 0x83, 0x77, 0xfa, 0x22, 0x4, + 0x44, 0x8, 0x88, 0x77, 0x0, + + /* U+7FF1 "翱" */ + 0x0, 0x0, 0x80, 0x7, 0xcf, 0xe1, 0x9, 0x84, + 0x25, 0xf2, 0xd8, 0x4b, 0x5f, 0x2d, 0x10, 0x27, + 0xf9, 0xb2, 0x8b, 0x55, 0x9, 0x93, 0x25, 0xf0, + 0x91, 0x2, 0x44, 0x3f, + + /* U+7FF3 "翳" */ + 0xfe, 0x72, 0x82, 0x4b, 0xe8, 0xe2, 0x40, 0xbf, + 0xfa, 0x71, 0x2a, 0x27, 0x9f, 0xa1, 0xfd, 0xf8, + 0x10, 0x26, 0x4c, 0x85, 0x6, 0x64, 0xea, 0x14, + 0x23, 0xc7, 0x0, + + /* U+7FFB "翻" */ + 0xfd, 0xdc, 0x49, 0x15, 0x54, 0x7f, 0xd5, 0x11, + 0x55, 0xf1, 0x59, 0x4, 0x5f, 0xb3, 0x92, 0xd6, + 0x4d, 0x17, 0xe4, 0x64, 0x91, 0x7e, 0x46, 0xf, + 0x70, + + /* U+7FFC "翼" */ + 0x7e, 0xfc, 0x84, 0x8, 0xe9, 0xd1, 0xf3, 0xe4, + 0x0, 0x7, 0xff, 0x8, 0x42, 0x1f, 0xfc, 0x21, + 0x8, 0x3d, 0xe1, 0xff, 0xf0, 0x21, 0xf, 0xff, + 0xe0, 0x84, 0x1e, 0x7, 0x80, + + /* U+8000 "耀" */ + 0x0, 0x0, 0x47, 0xbe, 0x91, 0xd, 0x2e, 0xd6, + 0x84, 0x2c, 0x7b, 0x48, 0x24, 0x2c, 0xff, 0x4b, + 0x10, 0x97, 0xfd, 0x24, 0x42, 0x6f, 0xf4, 0x91, + 0x10, 0x3f, 0xe0, 0x40, 0x0, + + /* U+8001 "老" */ + 0x2, 0x0, 0x4, 0x0, 0x8, 0x1, 0xff, 0x60, + 0x21, 0x80, 0x44, 0x3f, 0xff, 0x80, 0xc0, 0x6, + 0x0, 0x30, 0x23, 0xa3, 0x80, 0x78, 0x20, 0x80, + 0x41, 0x1, 0x3, 0xfe, 0x0, + + /* U+8003 "考" */ + 0x2, 0x0, 0x4, 0x1, 0xff, 0x20, 0x11, 0x80, + 0x26, 0x1f, 0xff, 0xc0, 0xc0, 0x7, 0xfe, 0x78, + 0x1, 0x1f, 0xe0, 0x0, 0x40, 0x0, 0x80, 0x1, + 0x0, 0x6, 0x0, 0xf8, 0x0, + + /* U+8004 "耄" */ + 0x4, 0x0, 0xff, 0x90, 0x10, 0xc7, 0xff, 0xf0, + 0x60, 0x1f, 0x7e, 0x87, 0x1, 0x3, 0xfc, 0x7f, + 0xf8, 0x4, 0x1, 0xff, 0xf0, 0x10, 0xf, 0xff, + 0xe0, 0x40, 0x40, 0x7f, 0x0, + + /* U+8005 "者" */ + 0x2, 0x0, 0x4, 0x8, 0xff, 0xa0, 0x10, 0x80, + 0x22, 0x1f, 0xff, 0xc0, 0x60, 0x3, 0x0, 0x1f, + 0xf1, 0xe0, 0x20, 0x7f, 0xc0, 0x80, 0x81, 0x1, + 0x3, 0xfe, 0x4, 0x4, 0x0, + + /* U+8006 "耆" */ + 0x2, 0x0, 0x7f, 0xb0, 0x9, 0x87, 0xff, 0xf0, + 0x60, 0x7, 0x1c, 0x37, 0xc2, 0x7, 0xfc, 0x0, + 0x0, 0x7f, 0xe0, 0x80, 0x41, 0xff, 0x82, 0x1, + 0x7, 0xfe, 0x8, 0x4, 0x0, + + /* U+800B "耋" */ + 0x2, 0x0, 0x7f, 0x90, 0x8, 0xc7, 0xff, 0xf0, + 0x60, 0xf, 0xfe, 0xa2, 0x3, 0x3, 0xf8, 0x7f, + 0xfc, 0x18, 0xc0, 0xff, 0xe0, 0x8, 0x3, 0xff, + 0x9f, 0xff, 0xc0, + + /* U+800C "而" */ + 0xff, 0xfe, 0x2, 0x0, 0x8, 0x3, 0xff, 0xe4, + 0x44, 0x48, 0x88, 0x91, 0x11, 0x22, 0x22, 0x44, + 0x44, 0x88, 0x89, 0x11, 0x12, 0x22, 0x24, 0x44, + 0x48, 0x3, 0x80, + + /* U+800D "耍" */ + 0x7f, 0xfe, 0x4, 0x1, 0xff, 0xf2, 0x22, 0x24, + 0x44, 0x48, 0x88, 0x90, 0x7, 0x3, 0x0, 0xff, + 0xfe, 0x10, 0x20, 0x60, 0x80, 0x3e, 0x0, 0x77, + 0xf, 0x1, 0x80, + + /* U+8010 "耐" */ + 0x0, 0x4, 0xff, 0x84, 0x8, 0x4, 0x8, 0x7f, + 0x7f, 0x84, 0x94, 0x84, 0x94, 0xa4, 0x94, 0xa4, + 0x94, 0x94, 0x94, 0x84, 0x94, 0x84, 0x94, 0x84, + 0x90, 0x84, 0x80, 0x84, 0x83, 0x3c, + + /* U+8012 "耒" */ + 0x1, 0x0, 0x2, 0x1, 0xff, 0xf0, 0x8, 0x3, + 0xff, 0x80, 0x20, 0x0, 0x40, 0x7f, 0xff, 0x3, + 0x80, 0xa, 0x80, 0x35, 0x80, 0x88, 0x86, 0x10, + 0xd0, 0x20, 0x40, 0x40, 0x0, + + /* U+8014 "耔" */ + 0x10, 0x0, 0x23, 0xfb, 0xf0, 0x20, 0x80, 0x87, + 0xc2, 0x2, 0x4, 0x4, 0x8, 0x7e, 0xff, 0x30, + 0x20, 0x60, 0x41, 0x60, 0x82, 0xa1, 0x9, 0x2, + 0x2, 0x4, 0x4, 0x78, 0x0, + + /* U+8015 "耕" */ + 0x10, 0x88, 0x21, 0x13, 0xf2, 0x20, 0x8f, 0xf7, + 0xc8, 0x82, 0x11, 0x4, 0x22, 0x7e, 0x44, 0x33, + 0xfe, 0x61, 0x11, 0x62, 0x22, 0xa4, 0x49, 0x10, + 0x82, 0x61, 0x5, 0x82, 0x0, + + /* U+8016 "耖" */ + 0x10, 0x20, 0x20, 0x43, 0xf0, 0x80, 0x85, 0x47, + 0xd2, 0x42, 0x24, 0x44, 0x88, 0x7e, 0x12, 0x30, + 0x24, 0xb0, 0x11, 0x50, 0x46, 0x81, 0x9, 0x4, + 0x2, 0x30, 0x5, 0x80, 0x0, + + /* U+8017 "耗" */ + 0x10, 0x0, 0x27, 0xf3, 0xf1, 0x0, 0x82, 0x7, + 0xc7, 0xc2, 0x78, 0x4, 0x10, 0x7e, 0x20, 0x30, + 0x40, 0x67, 0xfd, 0x61, 0x2, 0xa2, 0x19, 0x4, + 0x22, 0x8, 0x84, 0x1f, 0x0, + + /* U+8018 "耘" */ + 0x10, 0x0, 0x23, 0xfb, 0xf0, 0x0, 0x80, 0x7, + 0xc0, 0x2, 0x0, 0x4, 0xff, 0xfe, 0x10, 0x10, + 0x40, 0x60, 0xa1, 0x62, 0x22, 0xa4, 0x49, 0x10, + 0x42, 0x7f, 0xc4, 0x0, 0x80, + + /* U+8019 "耙" */ + 0x20, 0x0, 0x43, 0xfb, 0xf4, 0x91, 0x9, 0x2f, + 0xd2, 0x44, 0x24, 0x88, 0x49, 0x7e, 0xfe, 0x21, + 0x0, 0x62, 0x1, 0xa4, 0xb, 0x8, 0x1a, 0x10, + 0x24, 0x20, 0x48, 0x7f, 0x0, + + /* U+801C "耜" */ + 0x10, 0x0, 0x47, 0xff, 0xd0, 0x44, 0x41, 0x7d, + 0x4, 0x47, 0xf1, 0x10, 0x3f, 0x40, 0x31, 0xfd, + 0x64, 0x15, 0xd0, 0x65, 0x41, 0x91, 0x4, 0x47, + 0xf1, 0x10, 0x40, + + /* U+8020 "耠" */ + 0x10, 0x20, 0x20, 0xa3, 0xf1, 0x40, 0x8c, 0x47, + 0xf0, 0x62, 0x1f, 0x4, 0x0, 0x7e, 0x0, 0x31, + 0xfc, 0x72, 0x9, 0x54, 0x12, 0x88, 0x29, 0x10, + 0x42, 0x3f, 0x84, 0x41, 0x0, + + /* U+8022 "耢" */ + 0x10, 0x88, 0x21, 0x13, 0xf7, 0xf8, 0x84, 0x41, + 0x0, 0x1f, 0xbf, 0xc4, 0x98, 0x88, 0x31, 0x79, + 0xfc, 0x70, 0x89, 0x51, 0x12, 0x82, 0x29, 0x8, + 0x42, 0x20, 0x84, 0x9e, 0x0, + + /* U+8025 "耥" */ + 0x20, 0x40, 0x89, 0x1f, 0x94, 0x88, 0x10, 0xfb, + 0xfc, 0x90, 0x12, 0x40, 0x7f, 0x3d, 0x24, 0x95, + 0xd2, 0x56, 0xc9, 0x69, 0x3d, 0xa4, 0x4, 0x90, + 0x12, 0x43, 0xc0, + + /* U+8026 "耦" */ + 0x20, 0x0, 0x43, 0xfb, 0xf4, 0x91, 0xf, 0xe2, + 0x12, 0x4f, 0x24, 0x88, 0x7f, 0x7e, 0x10, 0x23, + 0xfe, 0xa4, 0x45, 0xb8, 0xcb, 0x17, 0xda, 0x20, + 0xa4, 0x40, 0x48, 0x87, 0x0, + + /* U+8027 "耧" */ + 0x10, 0x20, 0x22, 0x4b, 0xf2, 0xa0, 0x9f, 0xff, + 0xc7, 0x2, 0x15, 0x4, 0xc9, 0xfe, 0x1, 0x10, + 0x40, 0x67, 0xfd, 0x62, 0x22, 0xa8, 0x49, 0xf, + 0x2, 0x1b, 0x4, 0xc1, 0x80, + + /* U+8028 "耨" */ + 0x20, 0x0, 0x47, 0xfb, 0xe8, 0x1, 0x1f, 0xc2, + 0x20, 0xe, 0x7f, 0x88, 0xa9, 0x7d, 0x5c, 0x22, + 0xcc, 0x64, 0x11, 0xb7, 0xfd, 0x20, 0x4a, 0x48, + 0x84, 0x81, 0xa, 0x1c, 0x0, + + /* U+8029 "耩" */ + 0x20, 0x88, 0x23, 0xfe, 0xf8, 0x88, 0x23, 0xfe, + 0xf8, 0x88, 0x27, 0xff, 0x20, 0x20, 0xf9, 0xfe, + 0x22, 0x22, 0x71, 0xfe, 0x6a, 0x22, 0xa7, 0xff, + 0x22, 0x2, 0x22, 0x4, 0x22, 0x1c, + + /* U+802A "耪" */ + 0x20, 0x40, 0x81, 0xf, 0xbf, 0xc8, 0x46, 0x20, + 0x93, 0xff, 0xf2, 0x44, 0x48, 0x10, 0xff, 0xfc, + 0xc4, 0x6, 0x9f, 0xa8, 0x42, 0xa2, 0x8, 0x88, + 0x22, 0xc7, 0x0, + + /* U+8031 "耱" */ + 0x20, 0x20, 0x23, 0xfe, 0xfc, 0x0, 0x24, 0x48, + 0xfc, 0xcc, 0x24, 0xfc, 0x25, 0x6b, 0xfc, 0x0, + 0x25, 0xfe, 0x74, 0x20, 0x6c, 0x40, 0xa4, 0xfc, + 0xa7, 0x84, 0x28, 0xfc, 0x28, 0x84, + + /* U+8033 "耳" */ + 0xff, 0xfe, 0x40, 0x20, 0x80, 0x41, 0xff, 0x82, + 0x1, 0x4, 0x2, 0x8, 0x4, 0x1f, 0xf8, 0x20, + 0x10, 0x40, 0x3f, 0xff, 0xc0, 0x0, 0x80, 0x1, + 0x0, 0x2, 0x0, + + /* U+8035 "耵" */ + 0xfe, 0xff, 0x44, 0x8, 0x44, 0x8, 0x7c, 0x8, + 0x44, 0x8, 0x44, 0x8, 0x44, 0x8, 0x7c, 0x8, + 0x44, 0x8, 0x44, 0x8, 0x46, 0x8, 0xfc, 0x8, + 0x4, 0x8, 0x4, 0x70, + + /* U+8036 "耶" */ + 0xff, 0x7c, 0x84, 0x89, 0x9, 0x23, 0xf2, 0x44, + 0x24, 0x88, 0x49, 0x10, 0x91, 0x3f, 0x22, 0x42, + 0x42, 0x84, 0x85, 0x7d, 0xf, 0x12, 0xe0, 0x24, + 0x0, 0x48, 0x0, + + /* U+8037 "耷" */ + 0x0, 0x80, 0x1, 0x0, 0x7f, 0xfe, 0x6, 0x40, + 0xc, 0x20, 0x10, 0x18, 0x7f, 0xff, 0x10, 0x10, + 0x1f, 0xf0, 0x10, 0x10, 0x10, 0x10, 0x1f, 0xf0, + 0x10, 0x10, 0x7f, 0xfe, 0x0, 0x10, 0x0, 0x10, + + /* U+8038 "耸" */ + 0x8, 0x40, 0x30, 0xc0, 0x52, 0x83, 0x18, 0xc8, + 0x0, 0x4f, 0xff, 0x8, 0x4, 0x1f, 0xf8, 0x20, + 0x10, 0x40, 0x20, 0xff, 0xc1, 0x0, 0xef, 0xff, + 0x0, 0x2, 0x0, 0x4, 0x0, + + /* U+803B "耻" */ + 0x0, 0x11, 0xfc, 0x21, 0x10, 0x42, 0x24, 0x87, + 0xc9, 0x8, 0x93, 0xd1, 0x24, 0x22, 0x48, 0x7c, + 0x90, 0x89, 0x21, 0x12, 0x42, 0x74, 0x8f, 0x49, + 0x0, 0xbf, 0xc1, 0x0, 0x0, + + /* U+803D "耽" */ + 0x0, 0x21, 0xf8, 0x41, 0x20, 0x82, 0x5f, 0xe7, + 0xa2, 0x49, 0x44, 0x92, 0x8, 0x24, 0x18, 0x78, + 0x50, 0x90, 0xa1, 0x21, 0x42, 0x64, 0x8f, 0x91, + 0x21, 0x22, 0x42, 0x87, 0x0, + + /* U+803F "耿" */ + 0x0, 0x30, 0xfe, 0x30, 0x44, 0x30, 0x45, 0x30, + 0x7d, 0x22, 0x45, 0x34, 0x44, 0xb4, 0x44, 0x30, + 0x7c, 0x30, 0x44, 0x68, 0x44, 0x48, 0x4e, 0x44, + 0xf4, 0x84, 0x5, 0x82, 0x7, 0x1, + + /* U+8042 "聂" */ + 0x7f, 0xfe, 0x10, 0x8, 0x1f, 0xf8, 0x10, 0x8, + 0x1f, 0xf8, 0x10, 0x8, 0x10, 0xf, 0x7f, 0xf8, + 0x0, 0x0, 0x7e, 0xfe, 0x2, 0x44, 0x34, 0x44, + 0x8, 0x28, 0x36, 0x38, 0x41, 0xc6, + + /* U+8043 "聃" */ + 0x0, 0x21, 0xf8, 0x41, 0x27, 0xf2, 0x49, 0x27, + 0x92, 0x49, 0x3f, 0x92, 0x49, 0x3c, 0x92, 0x49, + 0x24, 0x97, 0xfd, 0x24, 0x13, 0xe8, 0x28, 0x90, + 0x41, 0x20, 0x82, 0x47, 0x0, + + /* U+8046 "聆" */ + 0x0, 0x41, 0xf8, 0xc1, 0x22, 0xc2, 0x44, 0x87, + 0x94, 0x89, 0x48, 0xd2, 0x8, 0x24, 0x0, 0x7b, + 0xfc, 0x90, 0x11, 0x20, 0x43, 0xe9, 0x88, 0x8a, + 0x1, 0xc, 0x2, 0x4, 0x0, + + /* U+804A "聊" */ + 0x0, 0x3, 0xee, 0x75, 0x41, 0x55, 0x25, 0x74, + 0x95, 0x52, 0x55, 0x49, 0x55, 0x25, 0x74, 0x95, + 0x56, 0x55, 0x29, 0x5e, 0x27, 0x91, 0x10, 0x48, + 0x41, 0x61, 0x0, + + /* U+804B "聋" */ + 0x2, 0x0, 0x2, 0x30, 0x7f, 0xfe, 0x4, 0x88, + 0xc, 0xb2, 0x1b, 0xc2, 0x64, 0x7c, 0x0, 0x0, + 0x7f, 0xfe, 0x10, 0x8, 0x1f, 0xf8, 0x10, 0x8, + 0x1f, 0xf8, 0x10, 0x8, 0x7f, 0xff, 0x0, 0x8, + + /* U+804C "职" */ + 0xfe, 0xfc, 0x89, 0x9, 0x12, 0x13, 0xe4, 0x24, + 0x48, 0x48, 0x90, 0x91, 0x21, 0x3e, 0x7e, 0x44, + 0x0, 0x88, 0x91, 0xf9, 0x26, 0x24, 0x20, 0x58, + 0x20, 0xa0, 0x40, + + /* U+804D "聍" */ + 0x0, 0x21, 0xf8, 0x41, 0x2f, 0xfa, 0x50, 0x17, + 0xa0, 0x29, 0x0, 0x12, 0x0, 0x25, 0xff, 0x78, + 0x20, 0x90, 0x41, 0x20, 0x82, 0x61, 0xf, 0x82, + 0x1, 0x4, 0x2, 0x78, 0x0, + + /* U+8052 "聒" */ + 0x0, 0x1, 0xfb, 0xf9, 0x20, 0x82, 0x41, 0x7, + 0x9f, 0xe9, 0x4, 0x12, 0x8, 0x3c, 0x10, 0x49, + 0xfc, 0x92, 0x9, 0x24, 0x13, 0xe8, 0x28, 0x90, + 0x41, 0x3f, 0x82, 0x41, 0x0, + + /* U+8054 "联" */ + 0x1, 0x5, 0xf9, 0x11, 0x21, 0x42, 0x5f, 0xf7, + 0x82, 0x9, 0x4, 0x12, 0x8, 0x25, 0xff, 0x78, + 0x20, 0x90, 0x41, 0x21, 0x43, 0xe2, 0x88, 0x88, + 0x81, 0x20, 0x82, 0x80, 0x80, + + /* U+8058 "聘" */ + 0x0, 0x21, 0xfb, 0xfd, 0x24, 0x8a, 0x4f, 0xf7, + 0x92, 0x29, 0x24, 0x52, 0x7f, 0xa4, 0x0, 0x7b, + 0xfe, 0x91, 0x1, 0x33, 0xf7, 0xc0, 0x20, 0x80, + 0x41, 0x0, 0x82, 0x1e, 0x0, + + /* U+805A "聚" */ + 0xfe, 0xfc, 0x88, 0x9, 0xf3, 0x23, 0xe1, 0x84, + 0x43, 0xf, 0xd9, 0xa1, 0x0, 0x3f, 0xf8, 0x1, + 0x0, 0x36, 0x33, 0x8b, 0x80, 0x51, 0x7, 0x21, + 0xf0, 0x40, 0x0, + + /* U+8069 "聩" */ + 0x0, 0x21, 0xf7, 0xfa, 0x48, 0x94, 0x91, 0x29, + 0x3f, 0xde, 0x4, 0x25, 0xff, 0xc8, 0x0, 0xf3, + 0xfd, 0x24, 0xa, 0x48, 0x94, 0xd1, 0x2f, 0x6, + 0x2, 0x13, 0x85, 0xc0, 0x80, + + /* U+806A "聪" */ + 0x0, 0x89, 0xf9, 0x11, 0x21, 0x42, 0x4d, 0x67, + 0x90, 0x49, 0x20, 0x92, 0x41, 0x3c, 0x7c, 0x48, + 0x20, 0x90, 0x41, 0x22, 0x93, 0xec, 0x1c, 0xa8, + 0xa1, 0x51, 0x2, 0x1c, 0x0, + + /* U+8071 "聱" */ + 0x8, 0x40, 0xfc, 0xfd, 0xfd, 0x17, 0xfe, 0x43, + 0xe3, 0x8, 0x4d, 0xa3, 0x20, 0x3f, 0xfe, 0x10, + 0x10, 0x3f, 0xe0, 0x40, 0x40, 0xff, 0x81, 0x1, + 0x1f, 0xff, 0xc0, 0x4, 0x0, + + /* U+807F "聿" */ + 0x1, 0x0, 0x7f, 0xf0, 0x4, 0x20, 0x8, 0x4f, + 0xff, 0xe0, 0x21, 0xf, 0xfe, 0x0, 0x80, 0x1, + 0x0, 0x7f, 0xf8, 0x4, 0x7, 0xff, 0xf0, 0x10, + 0x0, 0x20, 0x0, 0x40, 0x0, + + /* U+8080 "肀" */ + 0x2, 0x0, 0x4, 0x1, 0xff, 0xe0, 0x10, 0x40, + 0x20, 0x9f, 0xff, 0xc0, 0x82, 0x1, 0x4, 0x7f, + 0xf8, 0x4, 0x10, 0x8, 0x0, 0x10, 0x0, 0x20, + 0x0, 0x40, 0x0, 0x80, 0x0, + + /* U+8083 "肃" */ + 0x1, 0x0, 0xff, 0xf0, 0x4, 0x20, 0x8, 0x4f, + 0xff, 0xe0, 0x21, 0x1f, 0xfe, 0x0, 0x80, 0x1, + 0x0, 0x82, 0x11, 0x25, 0x22, 0x49, 0x45, 0x12, + 0x92, 0x21, 0x20, 0x42, 0x0, + + /* U+8084 "肄" */ + 0x59, 0xfc, 0xc8, 0x49, 0x10, 0x93, 0xdf, 0xf4, + 0x2, 0x4f, 0xbf, 0xa4, 0x8, 0x48, 0x10, 0x11, + 0xfd, 0xd8, 0x40, 0x4f, 0xf9, 0x41, 0x4, 0x42, + 0x10, 0x4, 0x0, + + /* U+8086 "肆" */ + 0x71, 0xfc, 0x80, 0x49, 0xe0, 0x92, 0x1f, 0xf4, + 0x2, 0x4e, 0x3f, 0x90, 0x8, 0x7e, 0x10, 0x21, + 0xfc, 0xa0, 0x41, 0x2f, 0xfc, 0x41, 0xf, 0x42, + 0x0, 0x4, 0x0, + + /* U+8087 "肇" */ + 0x10, 0x0, 0x20, 0x81, 0xfb, 0xfa, 0x16, 0x47, + 0xe3, 0x10, 0x7, 0x20, 0x31, 0xbf, 0xfc, 0x2, + 0x9, 0xff, 0xfc, 0x8, 0x23, 0xff, 0x87, 0xff, + 0x80, 0x40, 0x3f, 0xff, 0x81, 0x0, + + /* U+8089 "肉" */ + 0x2, 0x0, 0x10, 0x3f, 0xff, 0x4, 0x18, 0x60, + 0xc2, 0xc6, 0x61, 0x34, 0x45, 0x82, 0xc, 0x38, + 0x61, 0x23, 0x30, 0xda, 0x0, 0xc0, 0x6, 0x1, + 0xe0, + + /* U+808B "肋" */ + 0x0, 0x41, 0xf1, 0x4, 0x44, 0x11, 0x10, 0x45, + 0xfd, 0xf1, 0x14, 0x44, 0x51, 0x11, 0x7c, 0x45, + 0x11, 0x14, 0x48, 0x51, 0x21, 0x44, 0x86, 0x14, + 0x19, 0xe3, 0x80, + + /* U+808C "肌" */ + 0x79, 0xf0, 0x92, 0x21, 0x24, 0x42, 0x48, 0x87, + 0x91, 0x9, 0x22, 0x12, 0x44, 0x3c, 0x88, 0x49, + 0x10, 0x92, 0x25, 0x24, 0x4a, 0x50, 0x98, 0xa1, + 0x37, 0x83, 0x80, + + /* U+8093 "肓" */ + 0x1, 0x0, 0x1, 0x3, 0xff, 0xf9, 0x0, 0x2, + 0x0, 0x7, 0xff, 0x80, 0x0, 0x1f, 0xfe, 0x20, + 0x4, 0x40, 0x8, 0xff, 0xf1, 0x0, 0x23, 0xff, + 0xc4, 0x0, 0x88, 0x1, 0x10, 0x3c, + + /* U+8096 "肖" */ + 0x84, 0x14, 0x42, 0x24, 0x40, 0x40, 0xff, 0xf8, + 0x1, 0x80, 0x1f, 0xff, 0x80, 0x18, 0x1, 0xff, + 0xf8, 0x1, 0x80, 0x18, 0x1, 0x80, 0xf0, + + /* U+8098 "肘" */ + 0x0, 0x8, 0xf8, 0x11, 0x10, 0x22, 0x20, 0x44, + 0x5f, 0xef, 0x81, 0x11, 0x2, 0x22, 0x44, 0x7c, + 0x88, 0x88, 0x11, 0x10, 0x22, 0x20, 0x44, 0x40, + 0x90, 0x81, 0x27, 0x1c, 0x0, + + /* U+809A "肚" */ + 0x0, 0x20, 0xf8, 0x41, 0x10, 0x82, 0x21, 0x4, + 0x42, 0xf, 0xbf, 0xd1, 0x8, 0x22, 0x10, 0x44, + 0x20, 0xf8, 0x41, 0x10, 0x82, 0x21, 0x4, 0x42, + 0x10, 0xbf, 0xe7, 0x0, 0x0, + + /* U+809B "肛" */ + 0x7c, 0x0, 0x8b, 0xf9, 0x10, 0x82, 0x21, 0x4, + 0x42, 0xf, 0x84, 0x11, 0x8, 0x22, 0x10, 0x44, + 0x20, 0xf8, 0x41, 0x10, 0x82, 0x21, 0x4, 0x42, + 0x10, 0xbf, 0xe7, 0x0, 0x0, + + /* U+809C "肜" */ + 0x0, 0x4, 0xfc, 0x11, 0x8, 0xc2, 0x12, 0x7, + 0xe0, 0x68, 0x41, 0x90, 0x86, 0x21, 0x10, 0x7e, + 0xc0, 0x84, 0x5, 0x8, 0x12, 0x10, 0x4c, 0x21, + 0x10, 0x4c, 0x27, 0xe0, 0x0, + + /* U+809D "肝" */ + 0x79, 0xfe, 0x90, 0x41, 0x20, 0x82, 0x41, 0x7, + 0x82, 0x9, 0x4, 0x12, 0xff, 0xa4, 0x10, 0x78, + 0x20, 0x90, 0x41, 0x20, 0x82, 0x41, 0x8, 0x82, + 0x13, 0x4, 0x0, + + /* U+809F "肟" */ + 0x79, 0xfc, 0x90, 0x1, 0x20, 0x3, 0xdf, 0xf4, + 0x90, 0x9, 0x20, 0x12, 0x40, 0x3c, 0xfe, 0x48, + 0x4, 0x90, 0x9, 0x20, 0x12, 0x40, 0x28, 0x80, + 0x97, 0x1e, 0x0, + + /* U+80A0 "肠" */ + 0x7b, 0xfc, 0x90, 0x19, 0x20, 0xc2, 0x42, 0x7, + 0x98, 0x9, 0x7f, 0xd2, 0x24, 0xa4, 0x49, 0x79, + 0x92, 0x92, 0x45, 0x38, 0x8a, 0x42, 0x18, 0x8c, + 0x77, 0x27, 0x80, + + /* U+80A1 "股" */ + 0x7c, 0xf8, 0x89, 0x11, 0x12, 0x23, 0xe4, 0x44, + 0x50, 0xe8, 0x80, 0x11, 0x7f, 0x3e, 0x44, 0x44, + 0x88, 0x88, 0xa1, 0x11, 0xc2, 0x23, 0x88, 0x48, + 0xd3, 0xe0, 0x40, + + /* U+80A2 "肢" */ + 0x0, 0x20, 0xf0, 0x41, 0x2f, 0xfa, 0x41, 0x7, + 0x82, 0x9, 0x4, 0x12, 0xff, 0x24, 0x82, 0x78, + 0x88, 0x91, 0x11, 0x21, 0x42, 0x41, 0x4, 0x85, + 0x11, 0x31, 0xaf, 0x80, 0x80, + + /* U+80A4 "肤" */ + 0x0, 0x20, 0xf0, 0x41, 0x20, 0x82, 0x4f, 0xc7, + 0x82, 0x9, 0x4, 0x12, 0xff, 0xa4, 0x10, 0x78, + 0x60, 0x90, 0xc1, 0x21, 0x42, 0x44, 0x84, 0x98, + 0x91, 0x60, 0xaf, 0x80, 0x80, + + /* U+80A5 "肥" */ + 0x79, 0xfc, 0x92, 0x49, 0x24, 0x93, 0xc9, 0x24, + 0x92, 0x49, 0x3f, 0x92, 0x40, 0x3c, 0x80, 0x49, + 0x0, 0x92, 0x1, 0x24, 0xa, 0x48, 0x18, 0x90, + 0x37, 0x3f, 0x80, + + /* U+80A9 "肩" */ + 0x1, 0x0, 0x2, 0x3, 0xff, 0xc8, 0x1, 0x20, + 0x4, 0xff, 0xf2, 0x0, 0xb, 0xff, 0x28, 0x4, + 0xa0, 0x12, 0xff, 0xca, 0x1, 0x4f, 0xfd, 0x20, + 0x14, 0x80, 0x42, 0xf, + + /* U+80AA "肪" */ + 0x0, 0x20, 0xf0, 0x41, 0x20, 0x42, 0x5f, 0xf4, + 0x8c, 0xf, 0x18, 0x12, 0x30, 0x24, 0x7e, 0x48, + 0x84, 0xf1, 0x9, 0x22, 0x12, 0x44, 0x24, 0x88, + 0x49, 0x20, 0xa2, 0xc1, 0x5f, 0x1c, + + /* U+80AB "肫" */ + 0x0, 0x20, 0xf0, 0x41, 0x2f, 0xfa, 0x41, 0x7, + 0x92, 0x49, 0x24, 0x92, 0x49, 0x24, 0x92, 0x49, + 0x24, 0xf3, 0xf9, 0x20, 0x82, 0x41, 0x14, 0x82, + 0x31, 0x4, 0x6e, 0xf, 0x80, + + /* U+80AD "肭" */ + 0x0, 0x20, 0xf0, 0x41, 0x20, 0x82, 0x4f, 0xf4, + 0x92, 0x2f, 0x24, 0x52, 0x48, 0xa4, 0x91, 0x49, + 0x52, 0xf2, 0x95, 0x26, 0x3a, 0x48, 0x14, 0x90, + 0x31, 0x20, 0x6e, 0x47, 0x80, + + /* U+80AE "肮" */ + 0x0, 0x0, 0xf0, 0x81, 0x20, 0x82, 0x5f, 0xe4, + 0x80, 0x9, 0x0, 0x1e, 0x0, 0x24, 0x78, 0x48, + 0x90, 0xf1, 0x21, 0x22, 0x42, 0x44, 0x94, 0x89, + 0x29, 0x22, 0x62, 0x45, 0x5d, 0xe, + + /* U+80AF "肯" */ + 0x0, 0x80, 0x21, 0xf8, 0x42, 0x0, 0x84, 0xf, + 0xff, 0xe0, 0x0, 0xf, 0xff, 0x10, 0x2, 0x20, + 0x4, 0x7f, 0xf8, 0x80, 0x11, 0xff, 0xe2, 0x0, + 0x44, 0x1, 0x8, 0x1e, 0x0, + + /* U+80B1 "肱" */ + 0x0, 0x20, 0xf0, 0x81, 0x21, 0x2, 0x5f, 0xf4, + 0x8c, 0xf, 0x10, 0x12, 0x24, 0x24, 0x90, 0x79, + 0x20, 0x94, 0x81, 0x21, 0x22, 0x44, 0x44, 0x90, + 0x51, 0x3f, 0xae, 0x40, 0x80, + + /* U+80B2 "育" */ + 0x1, 0x0, 0x1, 0x3, 0xff, 0xf8, 0x21, 0x1, + 0x81, 0xf, 0xff, 0x0, 0x1, 0x0, 0x0, 0x3f, + 0xf8, 0x40, 0x10, 0xff, 0xe1, 0x0, 0x43, 0xff, + 0x84, 0x1, 0x8, 0x2, 0x10, 0x1c, + + /* U+80B4 "肴" */ + 0x20, 0x30, 0x73, 0x0, 0x78, 0x1e, 0x1c, 0x4, + 0x3, 0xff, 0xf1, 0x0, 0x7, 0xfc, 0x60, 0x12, + 0xff, 0xc2, 0x1, 0xf, 0xfc, 0x20, 0x10, 0x80, + 0x42, 0xf, 0x0, + + /* U+80B7 "肷" */ + 0x78, 0x80, 0x91, 0x1, 0x23, 0xfa, 0x48, 0x14, + 0x90, 0x4f, 0x24, 0x92, 0x89, 0x24, 0x10, 0x48, + 0x20, 0xf0, 0xa1, 0x21, 0x42, 0x42, 0x84, 0x88, + 0x91, 0x20, 0xaf, 0x80, 0x80, + + /* U+80BA "肺" */ + 0x0, 0x20, 0xf0, 0x41, 0x2f, 0xfa, 0x41, 0x7, + 0x82, 0x9, 0x3f, 0x92, 0x89, 0x25, 0x12, 0x7a, + 0x24, 0x94, 0x49, 0x28, 0x92, 0x51, 0x24, 0x82, + 0xd1, 0x4, 0x2e, 0x8, 0x0, + + /* U+80BC "肼" */ + 0x0, 0x88, 0xf1, 0x11, 0x22, 0x22, 0x44, 0x44, + 0x9f, 0xef, 0x11, 0x12, 0x22, 0x24, 0x44, 0x7b, + 0xfe, 0x91, 0x11, 0x22, 0x22, 0x44, 0x44, 0x90, + 0x91, 0x21, 0x2e, 0x82, 0x0, + + /* U+80BD "肽" */ + 0x0, 0x20, 0xf0, 0x41, 0x20, 0x82, 0x5f, 0xf7, + 0x82, 0x9, 0x4, 0x12, 0x18, 0x24, 0x28, 0x78, + 0x50, 0x91, 0x21, 0x22, 0x22, 0x46, 0x44, 0x94, + 0x51, 0x44, 0xef, 0x80, 0x80, + + /* U+80BE "肾" */ + 0x10, 0x2, 0x4f, 0xe9, 0x11, 0x24, 0x28, 0x90, + 0xe2, 0x5c, 0x70, 0x0, 0x1f, 0xfc, 0x40, 0x11, + 0xff, 0xc4, 0x1, 0x1f, 0xfc, 0x40, 0x11, 0x0, + 0x44, 0x1f, 0x0, + + /* U+80BF "肿" */ + 0x0, 0x20, 0xf0, 0x41, 0x20, 0x82, 0x4f, 0xf7, + 0x92, 0x29, 0x24, 0x52, 0x48, 0xa4, 0x91, 0x49, + 0x22, 0xf3, 0xfd, 0x20, 0x82, 0x41, 0x4, 0x82, + 0x11, 0x4, 0x2e, 0x8, 0x0, + + /* U+80C0 "胀" */ + 0x0, 0x80, 0xf1, 0x1, 0x22, 0x12, 0x44, 0x44, + 0x89, 0x9, 0x14, 0x1e, 0x20, 0x24, 0x40, 0x49, + 0xfc, 0xf1, 0x21, 0x22, 0x42, 0x44, 0xc4, 0x88, + 0x89, 0x14, 0xa2, 0x30, 0xdc, 0x40, + + /* U+80C1 "胁" */ + 0x0, 0x40, 0xf0, 0x81, 0x21, 0x2, 0x5f, 0xc4, + 0x84, 0x8f, 0x9, 0x12, 0x52, 0x25, 0x26, 0x4a, + 0x4a, 0xf4, 0x95, 0x22, 0x22, 0x44, 0x44, 0x88, + 0x91, 0x21, 0x2e, 0x9c, 0x0, + + /* U+80C2 "胂" */ + 0x0, 0x21, 0xe0, 0x84, 0xbf, 0xd2, 0x89, 0x4a, + 0x25, 0xe8, 0x94, 0xbf, 0xd2, 0x89, 0x4a, 0x25, + 0xe8, 0x94, 0xbf, 0xd2, 0x8, 0x48, 0x22, 0x20, + 0x8b, 0x82, 0x0, + + /* U+80C3 "胃" */ + 0xff, 0xfc, 0x10, 0x7f, 0xff, 0x4, 0x1f, 0xff, + 0x80, 0x1, 0xff, 0xc8, 0x2, 0x7f, 0xf2, 0x0, + 0x9f, 0xfc, 0x80, 0x24, 0x1, 0x20, 0x70, + + /* U+80C4 "胄" */ + 0x2, 0x7, 0xff, 0xe0, 0x83, 0xff, 0xf8, 0x20, + 0xff, 0xfc, 0x0, 0xf, 0xfe, 0x40, 0x13, 0xff, + 0x90, 0x4, 0xff, 0xe4, 0x1, 0x20, 0x9, 0x3, + 0xc0, + + /* U+80C6 "胆" */ + 0x79, 0xfc, 0x92, 0x9, 0x24, 0x12, 0x48, 0x27, + 0x9f, 0xc9, 0x20, 0x92, 0x41, 0x24, 0x82, 0x79, + 0x4, 0x93, 0xf9, 0x20, 0x2, 0x40, 0x8, 0xbf, + 0xf7, 0x0, 0x0, + + /* U+80CC "背" */ + 0x4, 0x80, 0x9, 0x3, 0xf2, 0x60, 0x27, 0x1, + 0xc8, 0x3c, 0x8f, 0x80, 0x0, 0x1f, 0xfc, 0x20, + 0x8, 0x40, 0x10, 0xff, 0xe1, 0x0, 0x43, 0xff, + 0x84, 0x1, 0x8, 0x2, 0x10, 0x78, + + /* U+80CD "胍" */ + 0x0, 0x0, 0x7b, 0xfc, 0x4a, 0x48, 0x4a, 0x48, + 0x4a, 0x48, 0x7a, 0x48, 0x4a, 0x48, 0x4a, 0x48, + 0x4a, 0x48, 0x7a, 0x44, 0x4a, 0x64, 0x4a, 0x94, + 0x4a, 0x96, 0x8a, 0xf2, 0xbc, 0xa, + + /* U+80CE "胎" */ + 0x0, 0x20, 0xf0, 0x81, 0x22, 0x2, 0x44, 0x44, + 0x90, 0x4f, 0x7f, 0x92, 0x0, 0xa4, 0x0, 0x49, + 0xfc, 0xf2, 0x9, 0x24, 0x12, 0x48, 0x24, 0x90, + 0x51, 0x3f, 0xae, 0x41, 0x0, + + /* U+80D6 "胖" */ + 0x0, 0x20, 0xf0, 0x41, 0x28, 0x8a, 0x49, 0x24, + 0x8a, 0x8f, 0x4, 0x12, 0xff, 0xa4, 0x10, 0x78, + 0x20, 0x97, 0xfd, 0x20, 0x82, 0x41, 0x4, 0x82, + 0x11, 0x4, 0x2e, 0x8, 0x0, + + /* U+80D7 "胗" */ + 0x0, 0x20, 0xf0, 0xc1, 0x22, 0x42, 0x58, 0x67, + 0xe2, 0x29, 0x8, 0x12, 0x60, 0x25, 0x8, 0x78, + 0x20, 0x91, 0x89, 0x24, 0x32, 0x40, 0xc4, 0x82, + 0x11, 0x18, 0x2e, 0xc0, 0x0, + + /* U+80D9 "胙" */ + 0x0, 0x0, 0xf1, 0x1, 0x22, 0x2, 0x4f, 0xf4, + 0x94, 0xf, 0x48, 0x12, 0x10, 0x24, 0x3f, 0x48, + 0x40, 0xf0, 0x81, 0x21, 0x2, 0x43, 0xf4, 0x84, + 0x9, 0x8, 0x22, 0x10, 0x5c, 0x20, + + /* U+80DA "胚" */ + 0x7b, 0xfe, 0x90, 0x41, 0x21, 0x3, 0xc3, 0x4, + 0x8a, 0x9, 0x15, 0x12, 0x49, 0x3c, 0x92, 0x4a, + 0x22, 0x90, 0x41, 0x20, 0x82, 0x40, 0x8, 0xbf, + 0xf7, 0x0, 0x0, + + /* U+80DB "胛" */ + 0x7b, 0xfd, 0x28, 0x94, 0xa2, 0x52, 0x89, 0x7b, + 0xfd, 0x28, 0x94, 0xa2, 0x5e, 0x89, 0x4b, 0xfd, + 0x28, 0x94, 0x82, 0x12, 0x8, 0x88, 0x22, 0xe0, + 0x80, + + /* U+80DC "胜" */ + 0x0, 0x20, 0xf2, 0x41, 0x24, 0x82, 0x49, 0x4, + 0xaf, 0xcf, 0x44, 0x12, 0x88, 0x24, 0x10, 0x49, + 0xfc, 0xf0, 0x41, 0x20, 0x82, 0x41, 0x4, 0x82, + 0x11, 0x7f, 0xee, 0x0, 0x0, + + /* U+80DD "胝" */ + 0x0, 0x4, 0xf7, 0xf1, 0x28, 0x82, 0x51, 0x4, + 0xa2, 0xf, 0x44, 0x12, 0xff, 0xa5, 0x10, 0x4a, + 0x20, 0xf4, 0x41, 0x28, 0x82, 0x51, 0x14, 0xad, + 0x31, 0x6a, 0x6e, 0x1b, 0x0, + + /* U+80DE "胞" */ + 0x0, 0x80, 0xf1, 0x1, 0x27, 0xf2, 0x48, 0x24, + 0xa0, 0x4f, 0xfe, 0x92, 0x45, 0x24, 0x8a, 0x79, + 0xf4, 0x92, 0x9, 0x24, 0x12, 0x49, 0xd4, 0x90, + 0x31, 0x20, 0x6e, 0x7f, 0x0, + + /* U+80E1 "胡" */ + 0x10, 0x0, 0x43, 0xf1, 0x8, 0x7f, 0xa1, 0x10, + 0x84, 0x43, 0xf1, 0x8, 0x5f, 0x21, 0x44, 0x85, + 0x13, 0xf4, 0x48, 0x51, 0x21, 0x7d, 0x85, 0x4, + 0x10, 0x23, 0xc0, + + /* U+80E4 "胤" */ + 0x20, 0x8, 0x22, 0x48, 0x24, 0x88, 0x2f, 0x8, + 0x22, 0x8, 0x27, 0xc8, 0x24, 0x28, 0x27, 0xc8, + 0x24, 0x48, 0x27, 0xc8, 0x24, 0x48, 0x27, 0xc9, + 0x44, 0x49, 0x44, 0x49, 0xc5, 0xce, + + /* U+80E5 "胥" */ + 0xff, 0xfc, 0x4, 0x8, 0x88, 0x11, 0xf, 0x86, + 0x20, 0xb, 0xff, 0xe0, 0x0, 0x1f, 0xf8, 0x40, + 0x10, 0x7f, 0xe1, 0x0, 0x41, 0xff, 0x84, 0x1, + 0x8, 0x2, 0x10, 0x3c, 0x0, + + /* U+80E7 "胧" */ + 0x0, 0x80, 0xf1, 0x21, 0x22, 0x22, 0x5f, 0xf4, + 0x8a, 0xf, 0x14, 0x12, 0x28, 0x24, 0x52, 0x78, + 0xa8, 0x92, 0x31, 0x24, 0x4a, 0x49, 0x14, 0x96, + 0x31, 0x52, 0x6f, 0x87, 0x0, + + /* U+80E8 "胨" */ + 0x0, 0x40, 0xf0, 0x81, 0x21, 0x2, 0x5f, 0xf4, + 0x88, 0x9, 0x10, 0x1e, 0x48, 0x24, 0x90, 0x49, + 0xfe, 0x90, 0x41, 0xe2, 0x92, 0x4d, 0x24, 0x92, + 0x29, 0x44, 0x62, 0x8, 0x5c, 0x70, + + /* U+80E9 "胩" */ + 0x0, 0x40, 0xf0, 0x81, 0x21, 0xfa, 0x42, 0x4, + 0x84, 0xf, 0x8, 0x12, 0xff, 0xa4, 0x0, 0x78, + 0x40, 0x90, 0xe1, 0x21, 0x32, 0x42, 0x14, 0x84, + 0x11, 0x8, 0x2e, 0x10, 0x0, + + /* U+80EA "胪" */ + 0x0, 0x20, 0xf0, 0x7d, 0x20, 0x82, 0x41, 0x4, + 0x9f, 0xcf, 0x20, 0x92, 0x41, 0x24, 0x82, 0x49, + 0x4, 0xf3, 0xf9, 0x24, 0x2, 0x48, 0x4, 0xb0, + 0x11, 0x40, 0x2f, 0x80, 0x0, + + /* U+80EB "胫" */ + 0x79, 0xfc, 0x90, 0x11, 0x20, 0x62, 0x41, 0x87, + 0x87, 0x89, 0x20, 0xd2, 0x80, 0x3c, 0xfe, 0x48, + 0x20, 0x90, 0x41, 0x20, 0x82, 0x41, 0x8, 0xbf, + 0xf3, 0x0, 0x0, + + /* U+80EC "胬" */ + 0x10, 0xfd, 0xfc, 0x9, 0xa, 0x22, 0x22, 0x83, + 0xc6, 0xc, 0x73, 0xa0, 0x40, 0x3f, 0xfc, 0x41, + 0x8, 0x8d, 0x91, 0x64, 0xa2, 0x18, 0x44, 0x4c, + 0x89, 0x1, 0x10, 0x1e, 0x0, + + /* U+80ED "胭" */ + 0x7b, 0xfe, 0x94, 0x45, 0x28, 0x8a, 0x51, 0x17, + 0xaf, 0xa9, 0x44, 0x52, 0x88, 0xa5, 0x19, 0x7a, + 0x52, 0x94, 0x95, 0x2a, 0x2a, 0x50, 0x18, 0xbf, + 0xf7, 0x40, 0x40, + + /* U+80EF "胯" */ + 0x0, 0x20, 0xf0, 0x41, 0x2f, 0xfa, 0x42, 0x87, + 0x88, 0x89, 0x7f, 0xd2, 0x0, 0x24, 0x0, 0x4b, + 0xfe, 0xf2, 0x1, 0x27, 0xf2, 0x40, 0x24, 0x80, + 0x51, 0x1, 0x2e, 0x1e, 0x0, + + /* U+80F0 "胰" */ + 0x0, 0x20, 0xf7, 0xfd, 0x20, 0x82, 0x41, 0x4, + 0xbf, 0xcf, 0x4, 0x92, 0x7f, 0x25, 0x10, 0x4a, + 0x20, 0xf7, 0xfd, 0x21, 0x8a, 0x43, 0x74, 0x89, + 0x11, 0x21, 0xaf, 0x81, 0x80, + + /* U+80F1 "胱" */ + 0x0, 0x20, 0x0, 0x41, 0xe0, 0x82, 0x51, 0x24, + 0x92, 0x89, 0x15, 0x1e, 0x8, 0x25, 0xff, 0x48, + 0x90, 0x91, 0x21, 0xe2, 0x42, 0x44, 0x84, 0x89, + 0x29, 0x22, 0x62, 0x44, 0xdd, 0x6, + + /* U+80F2 "胲" */ + 0x78, 0x20, 0x90, 0x41, 0x2f, 0xfa, 0x42, 0x4, + 0x88, 0xf, 0x21, 0x12, 0xc4, 0x24, 0x79, 0x78, + 0x64, 0x91, 0x11, 0x2c, 0x42, 0x41, 0x84, 0x84, + 0xd1, 0x30, 0xee, 0x80, 0x80, + + /* U+80F3 "胳" */ + 0x0, 0x40, 0xf1, 0x1, 0x23, 0xf2, 0x4c, 0x24, + 0xa8, 0x8f, 0xa, 0x12, 0x8, 0x24, 0x68, 0x4b, + 0x8e, 0xf0, 0x1, 0x27, 0xf2, 0x48, 0x24, 0x90, + 0x49, 0x20, 0xa2, 0x7f, 0x5c, 0x82, + + /* U+80F4 "胴" */ + 0x7b, 0xfe, 0x94, 0x5, 0x28, 0xa, 0x57, 0xd7, + 0xa0, 0x29, 0x40, 0x52, 0xbe, 0xbd, 0x45, 0x4a, + 0x8a, 0x95, 0x15, 0x2b, 0xea, 0x50, 0x18, 0xa0, + 0x37, 0x43, 0xc0, + + /* U+80F6 "胶" */ + 0x0, 0x0, 0xf0, 0x41, 0x20, 0x82, 0x5f, 0xf4, + 0x80, 0x9, 0x12, 0x1e, 0x62, 0x24, 0x83, 0x4a, + 0xa, 0xf1, 0x11, 0x22, 0x42, 0x43, 0x84, 0x82, + 0x9, 0x1a, 0x22, 0x63, 0x5f, 0x1, + + /* U+80F8 "胸" */ + 0x0, 0x0, 0xf2, 0x1, 0x27, 0xfa, 0x50, 0x14, + 0xc2, 0x2f, 0x5a, 0x52, 0x94, 0xa5, 0x29, 0x4a, + 0xd2, 0xf5, 0x65, 0x28, 0x4a, 0x5f, 0x94, 0x80, + 0x31, 0x0, 0x6e, 0xf, 0x0, + + /* U+80FA "胺" */ + 0x0, 0x20, 0xf0, 0x41, 0x2f, 0xfa, 0x50, 0x14, + 0xa4, 0x2f, 0x8, 0x12, 0xff, 0xa4, 0x44, 0x49, + 0x8, 0xf2, 0x11, 0x26, 0x42, 0x42, 0x84, 0x87, + 0x11, 0x31, 0xae, 0x80, 0x80, + + /* U+80FC "胼" */ + 0x1, 0x4, 0xf1, 0x11, 0x21, 0x22, 0x5f, 0xf4, + 0x88, 0x8f, 0x11, 0x12, 0x22, 0x24, 0x44, 0x7b, + 0xfe, 0x91, 0x11, 0x22, 0x22, 0x44, 0x44, 0x90, + 0x91, 0x21, 0x2e, 0x82, 0x0, + + /* U+80FD "能" */ + 0x10, 0x40, 0x48, 0x8d, 0x9, 0xe7, 0xfa, 0x10, + 0x14, 0x20, 0x8, 0x5f, 0x9f, 0x21, 0x0, 0x7e, + 0x42, 0x84, 0x99, 0x9, 0xc3, 0xf2, 0x4, 0x24, + 0x28, 0x48, 0x53, 0x9f, 0x80, + + /* U+8102 "脂" */ + 0x79, 0x0, 0x92, 0x39, 0x27, 0xa, 0x48, 0x17, + 0x90, 0x29, 0x3f, 0xd2, 0x0, 0x24, 0xfe, 0x79, + 0x4, 0x92, 0x9, 0x27, 0xf2, 0x48, 0x24, 0x90, + 0x51, 0x3f, 0xae, 0x41, 0x0, + + /* U+8106 "脆" */ + 0x0, 0x80, 0xf2, 0x1, 0x27, 0xe2, 0x50, 0x44, + 0xa1, 0x9, 0xff, 0xde, 0x80, 0x25, 0x7e, 0x4a, + 0x84, 0xf5, 0x9, 0x2a, 0x12, 0x54, 0xe4, 0xa8, + 0x29, 0x50, 0x63, 0x20, 0xde, 0x3e, + + /* U+8109 "脉" */ + 0x0, 0xc0, 0x78, 0x30, 0x48, 0x0, 0x4b, 0xe0, + 0x48, 0x20, 0x78, 0x22, 0x4b, 0xb4, 0x48, 0xb8, + 0x48, 0xb0, 0x79, 0x28, 0x49, 0x28, 0x49, 0x24, + 0x4a, 0x22, 0x88, 0x20, 0xb8, 0xe0, + + /* U+810A "脊" */ + 0x1, 0x1, 0x84, 0x61, 0x96, 0x1, 0x70, 0x7a, + 0xb8, 0x1b, 0x0, 0x86, 0x1f, 0xff, 0xa0, 0x14, + 0x80, 0x43, 0xff, 0x8, 0x4, 0x3f, 0xf0, 0x80, + 0x42, 0x1, 0x8, 0x3c, + + /* U+810D "脍" */ + 0x0, 0x20, 0x78, 0x20, 0x48, 0x50, 0x48, 0x88, + 0x49, 0x84, 0x7b, 0x2, 0x4a, 0xf8, 0x48, 0x0, + 0x48, 0x0, 0x7b, 0xfe, 0x48, 0x40, 0x48, 0x88, + 0x49, 0x4, 0x89, 0xfc, 0xbb, 0x2, + + /* U+810E "脎" */ + 0x0, 0x0, 0xf3, 0x9, 0x21, 0x62, 0x41, 0x84, + 0x8c, 0x8f, 0x20, 0x92, 0x8, 0x24, 0x10, 0x4b, + 0xfe, 0xf0, 0x41, 0x22, 0xa2, 0x49, 0x24, 0xa2, + 0x31, 0x4, 0x2e, 0x38, 0x0, + + /* U+810F "脏" */ + 0x0, 0x20, 0xf0, 0x21, 0x27, 0xfa, 0x48, 0x4, + 0x91, 0xf, 0x22, 0x12, 0x44, 0x24, 0x88, 0x49, + 0x7c, 0xf2, 0x21, 0x24, 0x42, 0x48, 0x84, 0xa1, + 0x11, 0x5f, 0xee, 0x80, 0x0, + + /* U+8110 "脐" */ + 0x78, 0x20, 0x90, 0x41, 0x2f, 0xfa, 0x48, 0x24, + 0x88, 0x8f, 0xa, 0x12, 0x8, 0x24, 0xef, 0x7a, + 0x2, 0x91, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, + 0x91, 0x21, 0x2e, 0x82, 0x0, + + /* U+8111 "脑" */ + 0x0, 0x20, 0xf0, 0x41, 0x2f, 0xfa, 0x40, 0x4, + 0x89, 0xf, 0x52, 0x92, 0x95, 0x25, 0x12, 0x4a, + 0x24, 0xf4, 0xa9, 0x29, 0x52, 0x54, 0x64, 0xa0, + 0x51, 0x3f, 0xae, 0x1, 0x0, + + /* U+8112 "脒" */ + 0x0, 0x20, 0xf0, 0x41, 0x24, 0x92, 0x49, 0x24, + 0x8a, 0x8f, 0x4, 0x12, 0xff, 0xa4, 0x38, 0x48, + 0x70, 0xf1, 0x51, 0x22, 0xa2, 0x49, 0x24, 0xa2, + 0x31, 0x4, 0x2e, 0x8, 0x0, + + /* U+8113 "脓" */ + 0x0, 0x20, 0x78, 0x20, 0x4b, 0xfe, 0x4a, 0x22, + 0x4a, 0x22, 0x7a, 0x60, 0x48, 0x60, 0x48, 0xa4, + 0x49, 0x98, 0x7b, 0x90, 0x4e, 0x88, 0x48, 0x84, + 0x48, 0x92, 0x88, 0xf0, 0xb8, 0xc0, + + /* U+8114 "脔" */ + 0x2, 0x0, 0x2, 0x3, 0xff, 0xf8, 0x24, 0x2, + 0x49, 0x98, 0xb0, 0xdf, 0xfe, 0x21, 0x4, 0x47, + 0x88, 0x90, 0xd1, 0x8, 0x22, 0x18, 0x44, 0x4e, + 0x8b, 0x1, 0x10, 0x1e, 0x0, + + /* U+8116 "脖" */ + 0x78, 0x20, 0x93, 0xf9, 0x20, 0x82, 0x41, 0x4, + 0xbf, 0xef, 0x40, 0x52, 0xbe, 0xa4, 0x4, 0x48, + 0x10, 0xf0, 0x41, 0x2f, 0xfa, 0x41, 0x4, 0x82, + 0x11, 0x4, 0x2e, 0x38, 0x0, + + /* U+8118 "脘" */ + 0x0, 0x40, 0x78, 0x20, 0x4b, 0xfe, 0x4a, 0x2, + 0x4a, 0x2, 0x79, 0xf8, 0x48, 0x0, 0x48, 0x0, + 0x4b, 0xfe, 0x78, 0x90, 0x48, 0x90, 0x48, 0x90, + 0x49, 0x11, 0x89, 0x11, 0xbe, 0x1e, + + /* U+811A "脚" */ + 0x79, 0x0, 0x92, 0x3d, 0x2f, 0x4a, 0x48, 0x94, + 0x91, 0x2f, 0x22, 0x53, 0xf4, 0xa4, 0x89, 0x4a, + 0x12, 0xf4, 0x25, 0x2a, 0x4a, 0x62, 0x94, 0xfd, + 0x71, 0x2, 0x2e, 0x4, 0x0, + + /* U+811E "脞" */ + 0x0, 0x40, 0xf0, 0x81, 0x25, 0x22, 0x4a, 0x44, + 0xa4, 0x8f, 0x6a, 0x92, 0xb5, 0x26, 0x61, 0x78, + 0x40, 0x97, 0xf9, 0x21, 0x2, 0x42, 0x4, 0x84, + 0x11, 0xff, 0xee, 0x0, 0x0, + + /* U+812C "脬" */ + 0x7b, 0xfc, 0x90, 0x1, 0x28, 0x92, 0x49, 0x47, + 0x80, 0x9, 0x7f, 0x92, 0x2, 0x24, 0x8, 0x78, + 0x20, 0x9f, 0xfd, 0x20, 0x82, 0x41, 0x8, 0x82, + 0x17, 0x1c, 0x0, + + /* U+812F "脯" */ + 0x78, 0x2c, 0x9f, 0xfd, 0x20, 0x82, 0x41, 0x7, + 0xbf, 0xc9, 0x44, 0x92, 0xff, 0x3d, 0x12, 0x4a, + 0x24, 0x97, 0xf9, 0x28, 0x92, 0x51, 0x28, 0xa2, + 0x57, 0x45, 0x80, + + /* U+8131 "脱" */ + 0x0, 0x88, 0xf1, 0x11, 0x21, 0x42, 0x4f, 0xe4, + 0x90, 0x4f, 0x20, 0x92, 0x41, 0x24, 0x82, 0x49, + 0xfc, 0xf1, 0x21, 0x22, 0x42, 0x44, 0x84, 0x89, + 0x31, 0x22, 0x6f, 0x87, 0x80, + + /* U+8132 "脲" */ + 0x7b, 0xfe, 0x4a, 0x2, 0x4a, 0x2, 0x4b, 0xfe, + 0x7a, 0x0, 0x4a, 0x10, 0x4a, 0x12, 0x4a, 0xd6, + 0x7a, 0x5c, 0x4a, 0x58, 0x4a, 0x94, 0x4a, 0x92, + 0x8d, 0x11, 0xbc, 0x70, + + /* U+8136 "脶" */ + 0x79, 0xfc, 0x92, 0x9, 0x24, 0x13, 0xcf, 0xe4, + 0x82, 0x9, 0x4, 0x12, 0xff, 0xbd, 0x11, 0x4a, + 0x62, 0x94, 0xa5, 0x2a, 0x3a, 0x58, 0x18, 0xa0, + 0x37, 0x43, 0x80, + + /* U+8138 "脸" */ + 0x0, 0x20, 0xf0, 0xc1, 0x22, 0x42, 0x4c, 0x47, + 0xa0, 0x69, 0x3f, 0x12, 0x0, 0x24, 0x0, 0x4a, + 0x44, 0xf4, 0x91, 0x24, 0xa2, 0x49, 0x84, 0x81, + 0x19, 0x7f, 0xee, 0x0, 0x0, + + /* U+813E "脾" */ + 0x0, 0x20, 0xf0, 0x81, 0x2f, 0xf2, 0x51, 0x24, + 0xa2, 0x49, 0x44, 0x9e, 0xff, 0x25, 0x12, 0x4a, + 0x24, 0x97, 0xf9, 0xe3, 0x2, 0x48, 0x84, 0xff, + 0xe9, 0x2, 0x22, 0x4, 0x5c, 0x8, + + /* U+8146 "腆" */ + 0x0, 0x50, 0x78, 0x50, 0x4b, 0xfe, 0x4a, 0x52, + 0x4a, 0x52, 0x7a, 0x52, 0x4b, 0xfe, 0x4a, 0x52, + 0x4a, 0x52, 0x7a, 0x52, 0x4f, 0xff, 0x48, 0x0, + 0x48, 0xd8, 0x89, 0x6, 0xba, 0x2, + + /* U+8148 "腈" */ + 0x0, 0x20, 0xf7, 0xfd, 0x20, 0x82, 0x4f, 0xe4, + 0x82, 0xf, 0x4, 0x12, 0x77, 0x24, 0xfe, 0x49, + 0x4, 0xf3, 0xf9, 0x24, 0x12, 0x4f, 0xe4, 0x90, + 0x51, 0x20, 0xae, 0x47, 0x0, + + /* U+814A "腊" */ + 0x78, 0x88, 0x91, 0x11, 0x2f, 0xfa, 0x44, 0x44, + 0x88, 0x8f, 0x7f, 0xd2, 0x0, 0x24, 0x0, 0x4b, + 0xfc, 0xf4, 0x9, 0x2f, 0xf2, 0x50, 0x24, 0xa0, + 0x51, 0x7f, 0xae, 0x81, 0x0, + + /* U+814B "腋" */ + 0x0, 0x20, 0xf0, 0x41, 0x2f, 0xfa, 0x40, 0x4, + 0x8a, 0xf, 0x27, 0xd2, 0x50, 0xa5, 0xa9, 0x4b, + 0xac, 0xf2, 0x49, 0x24, 0x62, 0x48, 0xc4, 0x91, + 0x91, 0x24, 0xae, 0x70, 0x80, + + /* U+814C "腌" */ + 0x0, 0x40, 0x78, 0x40, 0x4b, 0xfe, 0x48, 0x90, + 0x49, 0x8, 0x7a, 0x47, 0x4b, 0xfc, 0x4a, 0x44, + 0x4a, 0x44, 0x7b, 0xfc, 0x4a, 0x44, 0x4b, 0xfc, + 0x4a, 0x41, 0x88, 0x41, 0xb8, 0x7e, + + /* U+8150 "腐" */ + 0x0, 0x80, 0x3f, 0xff, 0x22, 0x4, 0x25, 0xff, + 0x2c, 0x4, 0x34, 0xc4, 0x24, 0x1c, 0x20, 0x40, + 0x2f, 0xfe, 0x28, 0x42, 0x29, 0x32, 0x2a, 0x82, + 0x48, 0xf2, 0x4b, 0x2, 0xc8, 0x3c, + + /* U+8151 "腑" */ + 0x0, 0x20, 0xf0, 0x41, 0x2f, 0xfa, 0x52, 0x4, + 0xa4, 0x4f, 0x48, 0x92, 0xaf, 0xa5, 0xc2, 0x4b, + 0xa4, 0xf5, 0x49, 0x2a, 0x52, 0x54, 0x24, 0xa8, + 0x51, 0x90, 0xaf, 0x26, 0x0, + + /* U+8153 "腓" */ + 0x0, 0x90, 0xf1, 0x21, 0x22, 0x42, 0x7c, 0xf4, + 0x89, 0xf, 0x12, 0x12, 0xe7, 0xa4, 0x48, 0x78, + 0x90, 0x91, 0x21, 0x3e, 0x7a, 0x44, 0x84, 0x89, + 0x11, 0x12, 0x2e, 0x24, 0x0, + + /* U+8154 "腔" */ + 0x0, 0x20, 0xf0, 0x41, 0x27, 0xfa, 0x50, 0x14, + 0x80, 0x2f, 0x11, 0x12, 0x41, 0x25, 0x1, 0x48, + 0x0, 0xf1, 0xf9, 0x20, 0x82, 0x41, 0x4, 0x82, + 0x11, 0x7f, 0xee, 0x0, 0x0, + + /* U+8155 "腕" */ + 0x0, 0x20, 0x78, 0x20, 0x4b, 0xff, 0x4a, 0x1, + 0x48, 0x0, 0x79, 0xde, 0x49, 0x52, 0x4a, 0x52, + 0x4a, 0x52, 0x7b, 0x52, 0x48, 0x96, 0x48, 0x90, + 0x49, 0x11, 0x8b, 0x11, 0xbe, 0x1e, + + /* U+8159 "腙" */ + 0x0, 0x20, 0xf0, 0x41, 0x2f, 0xf2, 0x50, 0x24, + 0xa0, 0xf, 0x3f, 0x92, 0x0, 0x24, 0x0, 0x7b, + 0xfe, 0x90, 0x41, 0x22, 0xa2, 0x49, 0x24, 0xa2, + 0x31, 0x44, 0x2e, 0x38, 0x0, + + /* U+815A "腚" */ + 0x0, 0x20, 0xf0, 0x21, 0x2f, 0xfa, 0x50, 0x14, + 0xa0, 0x2f, 0x7f, 0xd2, 0x8, 0x24, 0x10, 0x49, + 0x20, 0xf2, 0x3d, 0x24, 0x82, 0x49, 0x4, 0xba, + 0x11, 0x4c, 0x2e, 0x7, 0x80, + + /* U+8160 "腠" */ + 0x0, 0x20, 0x78, 0x20, 0x4b, 0xfe, 0x48, 0x20, + 0x48, 0x40, 0x4b, 0xfe, 0x78, 0x40, 0x4b, 0xff, + 0x48, 0x88, 0x79, 0xfc, 0x4e, 0x23, 0x4b, 0xfc, + 0x48, 0x40, 0x48, 0x70, 0x88, 0x8c, 0xbb, 0x2, + + /* U+8165 "腥" */ + 0x79, 0xfc, 0x92, 0x9, 0x27, 0xf2, 0x48, 0x27, + 0x9f, 0xc9, 0x4, 0x12, 0x48, 0x24, 0xff, 0x7a, + 0x20, 0x94, 0x41, 0x27, 0xfa, 0x41, 0x8, 0xbf, + 0xf7, 0x0, 0x0, + + /* U+8167 "腧" */ + 0x0, 0x20, 0x78, 0x70, 0x48, 0x58, 0x49, 0x8c, + 0x4b, 0xfb, 0x78, 0x0, 0x4b, 0xc2, 0x4a, 0x52, + 0x4a, 0x52, 0x7b, 0xd2, 0x4a, 0x52, 0x4b, 0xd2, + 0x4a, 0x42, 0x8a, 0x42, 0xba, 0xcc, + + /* U+8169 "腩" */ + 0x0, 0x20, 0xf0, 0x41, 0x2f, 0xfa, 0x41, 0x4, + 0x82, 0xf, 0x7f, 0xd2, 0xa0, 0xa5, 0x29, 0x4a, + 0xfa, 0xf4, 0x45, 0x28, 0x8a, 0x57, 0xd4, 0xa2, + 0x31, 0x44, 0x6e, 0x8b, 0x0, + + /* U+816D "腭" */ + 0x7b, 0xde, 0x4a, 0x52, 0x4a, 0x52, 0x4b, 0xde, + 0x78, 0x0, 0x49, 0xfc, 0x48, 0x0, 0x7b, 0xff, + 0x49, 0x0, 0x49, 0xfc, 0x48, 0x4, 0x48, 0x4, + 0x88, 0x4, 0xb8, 0x78, + + /* U+816E "腮" */ + 0x7b, 0xfe, 0x4a, 0x22, 0x4a, 0x22, 0x7b, 0xfe, + 0x4a, 0x22, 0x4a, 0x22, 0x4b, 0xfe, 0x78, 0x40, + 0x48, 0x20, 0x4a, 0xa2, 0x4a, 0x82, 0x4c, 0x89, + 0x8c, 0x88, 0xb8, 0x78, + + /* U+8170 "腰" */ + 0x7b, 0xfe, 0x90, 0xa1, 0x2f, 0xfb, 0xd2, 0x94, + 0xa5, 0x29, 0x7f, 0xd2, 0x0, 0x3c, 0x20, 0x4b, + 0xfe, 0x91, 0x11, 0x24, 0x22, 0x47, 0x88, 0x8d, + 0x97, 0x60, 0xc0, + + /* U+8171 "腱" */ + 0x70, 0x10, 0xaf, 0xf9, 0x44, 0x52, 0x97, 0xf7, + 0x21, 0x4a, 0x82, 0x95, 0xde, 0x28, 0x88, 0x55, + 0xfc, 0xea, 0x21, 0x5b, 0xfa, 0x90, 0x85, 0x38, + 0x12, 0x9f, 0xee, 0x0, 0x0, + + /* U+8174 "腴" */ + 0x0, 0x20, 0xf0, 0x41, 0x2e, 0xf2, 0x51, 0x24, + 0xa2, 0x49, 0x44, 0x9e, 0xef, 0x25, 0x12, 0x4a, + 0x24, 0x94, 0x49, 0xef, 0xf2, 0x43, 0x4, 0x85, + 0x9, 0x1a, 0x22, 0x62, 0x5d, 0x2, + + /* U+8179 "腹" */ + 0x0, 0x80, 0xf3, 0xfd, 0x24, 0x2, 0x5f, 0xe4, + 0x90, 0x4f, 0x3f, 0x92, 0x41, 0x24, 0xfe, 0x78, + 0x80, 0x93, 0xf9, 0x2e, 0x32, 0x74, 0x44, 0x87, + 0x11, 0x1b, 0x2f, 0xc1, 0x80, + + /* U+817A "腺" */ + 0x0, 0x40, 0xf0, 0x81, 0x2f, 0xf2, 0x50, 0x24, + 0xa0, 0x49, 0x7f, 0x9e, 0x81, 0x25, 0xfe, 0x48, + 0x20, 0x90, 0x45, 0xee, 0xd2, 0x45, 0xc4, 0x92, + 0x89, 0x24, 0xa3, 0x88, 0xdc, 0x70, + + /* U+817B "腻" */ + 0x0, 0x8, 0x78, 0xa, 0x48, 0x8, 0x4b, 0xfe, + 0x4b, 0xe8, 0x78, 0x8, 0x4b, 0xe8, 0x48, 0x8, + 0x4b, 0xe8, 0x7a, 0x28, 0x4a, 0xa8, 0x4a, 0xa8, + 0x48, 0x85, 0x89, 0x65, 0xba, 0x6, + + /* U+817C "腼" */ + 0x7f, 0xff, 0x48, 0x40, 0x48, 0x80, 0x4b, 0xfe, + 0x7a, 0x92, 0x4a, 0x92, 0x4a, 0xf2, 0x4a, 0x92, + 0x7a, 0x92, 0x4a, 0xf2, 0x4a, 0x92, 0x4a, 0x92, + 0x8b, 0xfe, 0xba, 0x2, + + /* U+817D "腽" */ + 0x79, 0xfc, 0x94, 0x9, 0x27, 0xf2, 0x50, 0x27, + 0xa0, 0x49, 0x3f, 0x92, 0x0, 0x3d, 0xff, 0x4a, + 0x92, 0x95, 0x25, 0x2a, 0x4a, 0x54, 0x98, 0xff, + 0xf7, 0x0, 0x0, + + /* U+817E "腾" */ + 0x0, 0x20, 0x79, 0x24, 0x48, 0x48, 0x4b, 0xfe, + 0x48, 0x40, 0x7b, 0xfe, 0x48, 0x88, 0x4b, 0xfe, + 0x4c, 0x10, 0x79, 0x10, 0x49, 0xfe, 0x48, 0x2, + 0x4b, 0xf4, 0x88, 0x4, 0xb8, 0x3c, + + /* U+817F "腿" */ + 0x7e, 0x7c, 0x94, 0x89, 0x21, 0xf3, 0xc2, 0x24, + 0xe4, 0x49, 0x4f, 0x92, 0x90, 0x25, 0x29, 0x7a, + 0x54, 0x94, 0x91, 0x29, 0x52, 0x53, 0x18, 0xd0, + 0x17, 0x1f, 0xc0, + + /* U+8180 "膀" */ + 0x0, 0x20, 0xf0, 0x41, 0x2f, 0xfa, 0x44, 0x44, + 0x8c, 0x8f, 0x7f, 0xd2, 0x88, 0xa4, 0x10, 0x4b, + 0xfe, 0xf1, 0x1, 0x23, 0xf2, 0x44, 0x24, 0x90, + 0x51, 0x20, 0xaf, 0x8e, 0x0, + + /* U+8182 "膂" */ + 0x20, 0x80, 0x47, 0xf6, 0xa0, 0xce, 0x7c, 0x49, + 0x25, 0x24, 0x6b, 0x9c, 0xe0, 0x0, 0x3f, 0xf8, + 0x80, 0x23, 0xff, 0x88, 0x2, 0x3f, 0xf8, 0x80, + 0x22, 0x7, 0x80, + + /* U+8188 "膈" */ + 0x7b, 0xfe, 0x90, 0x1, 0x27, 0xf2, 0x48, 0x27, + 0x9f, 0xc9, 0x0, 0x12, 0xff, 0xa5, 0x45, 0x7a, + 0x52, 0x95, 0xf5, 0x28, 0x8a, 0x51, 0x18, 0xa2, + 0x37, 0x45, 0x80, + + /* U+818A "膊" */ + 0x0, 0x28, 0xf0, 0x49, 0x2f, 0xfa, 0x41, 0x4, + 0x9f, 0xcf, 0x44, 0x92, 0x7f, 0x25, 0x12, 0x49, + 0xfc, 0xf4, 0x41, 0x2f, 0xfa, 0x48, 0x44, 0x88, + 0x91, 0x1, 0x2e, 0xe, 0x0, + + /* U+818F "膏" */ + 0x1, 0x1, 0xff, 0xfc, 0x0, 0x1, 0xff, 0xc2, + 0x0, 0x83, 0xfe, 0x3f, 0xff, 0xc0, 0x1, 0x9f, + 0xfa, 0x0, 0x0, 0xff, 0xe1, 0x0, 0x43, 0xff, + 0x87, 0xff, 0x8, 0x2, 0x10, 0x1c, + + /* U+8191 "膑" */ + 0x0, 0x0, 0xf0, 0x81, 0x2f, 0xfa, 0x50, 0x14, + 0x80, 0x9, 0x3f, 0x1e, 0x60, 0x24, 0x80, 0x49, + 0xfe, 0x92, 0x21, 0xe4, 0x42, 0x5f, 0xf4, 0x80, + 0x9, 0x12, 0x22, 0x43, 0x5d, 0x1, + + /* U+8198 "膘" */ + 0x7b, 0xfe, 0x48, 0x90, 0x4b, 0xfe, 0x4a, 0x92, + 0x7a, 0x92, 0x4b, 0xfe, 0x48, 0x0, 0x4b, 0xfc, + 0x78, 0x0, 0x4b, 0xff, 0x48, 0x20, 0x49, 0x2c, + 0x8e, 0x23, 0xb8, 0xe0, + + /* U+819B "膛" */ + 0x0, 0x40, 0xf4, 0x89, 0x25, 0x22, 0x5f, 0xf4, + 0xc0, 0x2f, 0x3f, 0x52, 0x82, 0x25, 0x4, 0x79, + 0xf8, 0x90, 0x81, 0x21, 0x2, 0x5f, 0xe4, 0x84, + 0x11, 0xff, 0xee, 0x0, 0x0, + + /* U+819C "膜" */ + 0x0, 0x88, 0xf1, 0x11, 0x2f, 0xfa, 0x44, 0x44, + 0x80, 0x9, 0x3f, 0x9e, 0x41, 0x24, 0xfe, 0x49, + 0x4, 0x92, 0x9, 0xe7, 0xf2, 0x41, 0x4, 0xbf, + 0xe9, 0xc, 0x22, 0x36, 0x5f, 0x83, + + /* U+819D "膝" */ + 0x0, 0x40, 0xf0, 0x81, 0x2f, 0xf2, 0x46, 0x4, + 0x97, 0xf, 0x41, 0x92, 0x10, 0x24, 0x58, 0x4b, + 0x4e, 0xf4, 0x91, 0x25, 0x42, 0x42, 0x4, 0xb5, + 0x91, 0x8, 0xae, 0x70, 0x0, + + /* U+81A3 "膣" */ + 0x0, 0x20, 0xf0, 0x41, 0x2f, 0xfa, 0x50, 0x14, + 0x8c, 0x8f, 0x20, 0x92, 0xff, 0xa4, 0x20, 0x48, + 0x88, 0x97, 0xf9, 0xe0, 0x8a, 0x41, 0x4, 0xbf, + 0xe9, 0x4, 0x22, 0xff, 0xdc, 0x0, + + /* U+81A6 "膦" */ + 0x0, 0x20, 0x79, 0x2c, 0x48, 0xb8, 0x48, 0x20, + 0x4b, 0xfe, 0x78, 0xb0, 0x4b, 0x2c, 0x48, 0x2, + 0x49, 0x8, 0x7b, 0xde, 0x4d, 0x68, 0x48, 0x5f, + 0x48, 0x88, 0x89, 0x8, 0xbe, 0x8, + + /* U+81A8 "膨" */ + 0x79, 0x2, 0x4b, 0xe4, 0x49, 0x8, 0x49, 0x10, + 0x4b, 0xc0, 0x78, 0x2, 0x4b, 0xc4, 0x4a, 0x4c, + 0x4a, 0x58, 0x7b, 0xc1, 0x48, 0x2, 0x4a, 0x46, + 0x49, 0x8c, 0x8f, 0xf8, 0xb8, 0x30, + + /* U+81AA "膪" */ + 0x0, 0x20, 0xf0, 0x41, 0x2f, 0xfa, 0x44, 0x44, + 0x89, 0x9, 0x7f, 0xde, 0x88, 0xa4, 0xfe, 0x49, + 0x24, 0xf2, 0x59, 0x20, 0x2, 0x4f, 0xe4, 0x90, + 0x49, 0x20, 0xa2, 0x7f, 0x5c, 0x82, + + /* U+81B3 "膳" */ + 0x0, 0x80, 0xf1, 0x11, 0x2f, 0xfa, 0x41, 0x4, + 0xbf, 0xef, 0x4, 0x12, 0xff, 0xa4, 0x92, 0x49, + 0x28, 0xf5, 0xad, 0x27, 0xf2, 0x50, 0x24, 0xa0, + 0x51, 0x3f, 0xae, 0x81, 0x0, + + /* U+81BA "膺" */ + 0x0, 0x0, 0x0, 0x80, 0x3f, 0xff, 0x24, 0x90, + 0x29, 0xfe, 0x29, 0x10, 0x3b, 0xfe, 0x29, 0xfe, + 0x29, 0x10, 0x29, 0xee, 0x2f, 0xfc, 0x28, 0x4, + 0x2f, 0xfc, 0x4f, 0xfc, 0x48, 0x4, 0x48, 0x3c, + + /* U+81BB "膻" */ + 0x0, 0x0, 0x78, 0x20, 0x4b, 0xff, 0x48, 0x0, + 0x4b, 0xfe, 0x4a, 0x2, 0x7a, 0xfa, 0x4a, 0xfa, + 0x4b, 0xfe, 0x48, 0x0, 0x7b, 0xfc, 0x49, 0xfc, + 0x4a, 0x4, 0x49, 0xf8, 0x8b, 0xff, 0xb8, 0x0, + + /* U+81C0 "臀" */ + 0x7f, 0xbc, 0x60, 0xa4, 0x7f, 0xc7, 0x6a, 0x0, + 0x7f, 0xfe, 0x6a, 0x24, 0x7f, 0x9c, 0xca, 0x18, + 0x31, 0x67, 0x1f, 0xfc, 0x10, 0x4, 0x1f, 0xfc, + 0x1f, 0xfc, 0x10, 0x4, 0x10, 0x38, + + /* U+81C1 "臁" */ + 0x0, 0x20, 0x78, 0x20, 0x4b, 0xfe, 0x4c, 0x8, + 0x4c, 0x48, 0x4d, 0xfe, 0x7c, 0x50, 0x4d, 0xfc, + 0x4c, 0x54, 0x4b, 0xff, 0x7c, 0x54, 0x4d, 0xfc, + 0x4c, 0x50, 0x4c, 0xd8, 0x8d, 0x56, 0xbc, 0x50, + + /* U+81C2 "臂" */ + 0x0, 0x10, 0x7e, 0x20, 0x85, 0xf9, 0xa, 0x23, + 0xe2, 0x44, 0x1f, 0xd7, 0xc4, 0x2f, 0x9e, 0x0, + 0x10, 0x3f, 0xf8, 0x40, 0x10, 0xff, 0xe1, 0x0, + 0x43, 0xff, 0x84, 0x1, 0x8, 0x1c, + + /* U+81C3 "臃" */ + 0x0, 0x20, 0xf0, 0x41, 0x2f, 0xfa, 0x48, 0x4, + 0xa2, 0x8f, 0x57, 0xd2, 0xd2, 0x24, 0xa4, 0x4a, + 0x3e, 0xf9, 0x91, 0x2e, 0xfa, 0x4e, 0x44, 0x94, + 0x91, 0x67, 0xef, 0x90, 0x0, + + /* U+81C6 "臆" */ + 0x0, 0x0, 0x78, 0x20, 0x4b, 0xfe, 0x48, 0x84, + 0x48, 0x88, 0x4b, 0xff, 0x78, 0x0, 0x4b, 0xfe, + 0x4a, 0x2, 0x4b, 0xfe, 0x7a, 0x2, 0x4b, 0xfe, + 0x48, 0x20, 0x4b, 0x2, 0x8c, 0x89, 0xb8, 0xf8, + + /* U+81CA "臊" */ + 0x79, 0xfc, 0x49, 0x4, 0x49, 0xfc, 0x48, 0x0, + 0x7b, 0xde, 0x4a, 0x52, 0x4b, 0xde, 0x48, 0x0, + 0x78, 0x20, 0x4b, 0xff, 0x48, 0x70, 0x48, 0xa8, + 0x8b, 0x27, 0xb8, 0x20, + + /* U+81CC "臌" */ + 0x1, 0x8, 0xe2, 0x11, 0x5f, 0x22, 0x89, 0xf5, + 0x10, 0x8e, 0x51, 0x15, 0xff, 0xaa, 0x31, 0x54, + 0x64, 0xef, 0xa9, 0x40, 0x52, 0x94, 0x45, 0x28, + 0xd2, 0x7a, 0xed, 0x8, 0x80, + + /* U+81E3 "臣" */ + 0xff, 0xfe, 0x8, 0x8, 0x20, 0x20, 0x80, 0xff, + 0xf2, 0x0, 0x48, 0x1, 0x20, 0x4, 0xff, 0xf2, + 0x8, 0x8, 0x20, 0x20, 0x80, 0xff, 0xfc, + + /* U+81E7 "臧" */ + 0x0, 0x10, 0x0, 0x29, 0x0, 0x42, 0xff, 0xf5, + 0x1, 0xa, 0xfa, 0x5d, 0x24, 0x8a, 0x4a, 0x17, + 0xd5, 0xe8, 0xa9, 0x51, 0x52, 0xbe, 0x45, 0x48, + 0x8a, 0x93, 0x69, 0xf6, 0x90, 0x13, + + /* U+81EA "自" */ + 0x4, 0x0, 0x80, 0xff, 0xf8, 0x1, 0x80, 0x18, + 0x1, 0xff, 0xf8, 0x1, 0x80, 0x1f, 0xff, 0x80, + 0x18, 0x1, 0x80, 0x1f, 0xff, 0x80, 0x10, + + /* U+81EC "臬" */ + 0x2, 0x0, 0x4, 0x0, 0xff, 0xe1, 0x0, 0x43, + 0xff, 0x84, 0x1, 0xf, 0xfe, 0x10, 0x4, 0x3f, + 0xf8, 0x2, 0x0, 0x4, 0x7, 0xff, 0xf0, 0x78, + 0x1, 0x2c, 0x1c, 0x47, 0x40, 0x80, + + /* U+81ED "臭" */ + 0x2, 0x0, 0x7f, 0xf0, 0x80, 0x21, 0xff, 0xc2, + 0x0, 0x87, 0xff, 0x8, 0x2, 0x10, 0x4, 0x3f, + 0xf8, 0x2, 0x63, 0xff, 0xf8, 0x18, 0x0, 0x4c, + 0x7, 0x7, 0x30, 0x1, 0x80, + + /* U+81F3 "至" */ + 0xff, 0xfe, 0x4, 0x0, 0x10, 0x0, 0x41, 0x3, + 0x1, 0x8f, 0xff, 0x80, 0x41, 0x0, 0x80, 0x7f, + 0xfc, 0x2, 0x0, 0x4, 0x0, 0x8, 0xf, 0xff, + 0xe0, + + /* U+81F4 "致" */ + 0x0, 0x41, 0xfc, 0x80, 0x81, 0x1, 0x47, 0xf4, + 0x48, 0x9f, 0xb1, 0x0, 0xa2, 0x8, 0x24, 0x10, + 0x49, 0xfc, 0xa0, 0x40, 0xc0, 0x81, 0x81, 0x5, + 0x9f, 0xd1, 0x80, 0xc0, 0x80, + + /* U+81FB "臻" */ + 0x0, 0x21, 0xf0, 0x40, 0x8f, 0xf2, 0x2, 0x5, + 0x4, 0x12, 0x3f, 0xbe, 0x10, 0x5, 0xff, 0x20, + 0x89, 0xf7, 0xe8, 0x90, 0x89, 0x1f, 0xe2, 0x6, + 0x7, 0x17, 0x31, 0xc9, 0x80, 0x10, + + /* U+81FC "臼" */ + 0xc, 0x7, 0x8f, 0xe0, 0x3, 0x0, 0x18, 0x0, + 0xc0, 0x6, 0x0, 0x3f, 0xbf, 0x80, 0xc, 0x0, + 0x60, 0x3, 0x0, 0x18, 0x0, 0xff, 0xfe, 0x0, + 0x20, + + /* U+81FE "臾" */ + 0x0, 0x80, 0x5, 0x0, 0xf3, 0xf1, 0x4, 0x22, + 0x8, 0x44, 0x10, 0x8f, 0xbf, 0x10, 0x42, 0x20, + 0x84, 0x41, 0x8, 0xff, 0xf0, 0xc, 0x0, 0x14, + 0x0, 0x44, 0x3, 0x6, 0x78, 0x3, + + /* U+8200 "舀" */ + 0x0, 0x7, 0xff, 0xc0, 0x0, 0x84, 0x22, 0x11, + 0x0, 0x0, 0xf3, 0xe8, 0x1, 0x40, 0xb, 0xe7, + 0xd0, 0x2, 0x80, 0x14, 0x0, 0xbf, 0xfd, 0x0, + 0x20, + + /* U+8201 "舁" */ + 0x2, 0x0, 0x78, 0xf8, 0x80, 0x11, 0x0, 0x23, + 0xe7, 0xc4, 0x0, 0x88, 0x1, 0x1f, 0xfe, 0x0, + 0x0, 0x10, 0x23, 0xff, 0xf8, 0x40, 0x81, 0x81, + 0x6, 0x2, 0x18, 0x4, 0x0, + + /* U+8202 "舂" */ + 0x0, 0x80, 0x1, 0x0, 0x7f, 0xfe, 0x1, 0x0, + 0x1, 0x0, 0x3f, 0xfc, 0x2, 0x0, 0x7f, 0xfe, + 0x8, 0x10, 0x36, 0x78, 0xd8, 0xe, 0x10, 0x8, + 0x1e, 0x78, 0x10, 0x8, 0x1f, 0xf8, 0x10, 0x8, + + /* U+8204 "舄" */ + 0x2, 0x0, 0x78, 0xf8, 0x80, 0x11, 0xf3, 0xe2, + 0x0, 0x44, 0x0, 0x8f, 0xff, 0x4, 0x0, 0x1f, + 0xfe, 0x40, 0x7, 0x42, 0x48, 0xa4, 0x92, 0x28, + 0x24, 0x40, 0x80, 0xf, 0x0, + + /* U+8205 "舅" */ + 0x7e, 0xfd, 0x0, 0x17, 0xc7, 0xdf, 0xff, 0x0, + 0x0, 0xff, 0xe4, 0x20, 0x8f, 0xfe, 0x42, 0x8, + 0xf7, 0xcf, 0xff, 0x81, 0x2, 0x18, 0xb, 0x83, + 0xc0, + + /* U+8206 "舆" */ + 0x8, 0x80, 0xe2, 0x39, 0x1f, 0x92, 0x10, 0x27, + 0xa9, 0xc8, 0x78, 0x90, 0x21, 0x3f, 0xf6, 0x40, + 0x84, 0x81, 0xb, 0xff, 0xf8, 0x0, 0x0, 0x42, + 0x7, 0x3, 0x30, 0x1, 0x80, + + /* U+820C "舌" */ + 0x7f, 0xfc, 0x2, 0x0, 0x4, 0x0, 0x8, 0xf, + 0xff, 0xe0, 0x20, 0x0, 0x40, 0x0, 0x80, 0x3f, + 0xf8, 0x40, 0x10, 0x80, 0x21, 0x0, 0x43, 0xff, + 0x84, 0x1, 0x0, + + /* U+820D "舍" */ + 0x3, 0x0, 0x12, 0x0, 0x84, 0x4, 0xc, 0x7f, + 0xee, 0x8, 0x0, 0x20, 0x3f, 0xff, 0x2, 0x0, + 0xff, 0xc2, 0x1, 0x8, 0x4, 0x20, 0x10, 0xff, + 0xc2, 0x1, 0x0, + + /* U+8210 "舐" */ + 0xfd, 0xfc, 0x42, 0x40, 0x84, 0x81, 0x9, 0xf, + 0xd2, 0x4, 0x24, 0x8, 0x7f, 0x7c, 0x90, 0x89, + 0x21, 0x12, 0x42, 0x24, 0x4c, 0x48, 0xaf, 0x9d, + 0x51, 0x21, 0x80, + + /* U+8212 "舒" */ + 0x10, 0x0, 0x33, 0xf8, 0x90, 0x32, 0x14, 0x8f, + 0xc6, 0x2, 0x4, 0x4, 0x7f, 0xff, 0x12, 0x10, + 0x24, 0xf8, 0x41, 0x10, 0x82, 0x21, 0x4, 0x42, + 0xf, 0x84, 0x11, 0x78, 0x0, + + /* U+8214 "舔" */ + 0x7d, 0xfe, 0x10, 0x20, 0x10, 0x20, 0xfd, 0xff, + 0x10, 0x48, 0x10, 0x8c, 0x11, 0x6, 0x7a, 0x20, + 0x48, 0xa0, 0x49, 0xaa, 0x49, 0x2a, 0x49, 0x29, + 0x78, 0x20, 0x48, 0xe0, + + /* U+821B "舛" */ + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x1e, 0xfe, + 0x22, 0x10, 0x22, 0x10, 0x62, 0x90, 0x52, 0x90, + 0x4, 0x90, 0x4, 0xff, 0x8, 0x10, 0x8, 0x10, + 0x10, 0x10, 0x20, 0x10, 0x40, 0x10, + + /* U+821C "舜" */ + 0x7f, 0xfe, 0x22, 0x10, 0x22, 0x43, 0xff, 0xf4, + 0x0, 0x2a, 0x2, 0x47, 0xbf, 0x91, 0x8, 0x52, + 0x91, 0x9, 0x20, 0x13, 0xf8, 0x40, 0x81, 0x1, + 0xc, 0x2, 0x0, + + /* U+821E "舞" */ + 0x20, 0x0, 0xff, 0xff, 0x52, 0x40, 0xa4, 0x87, + 0xff, 0xe2, 0x92, 0x3f, 0xff, 0x80, 0x0, 0x20, + 0x10, 0x7d, 0xff, 0xa, 0x45, 0xa4, 0x80, 0x8f, + 0xe6, 0x2, 0x30, 0x4, 0x0, + + /* U+821F "舟" */ + 0x2, 0x0, 0x4, 0x0, 0x7f, 0xe1, 0x0, 0x42, + 0x20, 0x84, 0x31, 0x8, 0x2, 0x7f, 0xff, 0x20, + 0x8, 0x44, 0x10, 0x84, 0x21, 0x4, 0x42, 0x0, + 0x88, 0x3, 0x10, 0x1c, 0x0, + + /* U+8221 "舡" */ + 0x10, 0x0, 0x40, 0x1, 0xf7, 0xf2, 0x21, 0x5, + 0x42, 0xa, 0x84, 0x11, 0x8, 0x7e, 0x10, 0x44, + 0x20, 0xa8, 0x41, 0x50, 0x82, 0x21, 0x4, 0x42, + 0x10, 0xbf, 0xe7, 0x0, 0x0, + + /* U+8222 "舢" */ + 0x10, 0x21, 0xf0, 0x84, 0x42, 0x11, 0x8, 0x65, + 0x25, 0x54, 0x94, 0x52, 0x7f, 0x49, 0x45, 0x25, + 0x14, 0x95, 0x52, 0x51, 0x49, 0x45, 0x26, 0x17, + 0xfb, 0xc0, 0x40, + + /* U+8223 "舣" */ + 0x10, 0x0, 0x20, 0x41, 0xf4, 0x92, 0x28, 0xa4, + 0x50, 0x4a, 0x90, 0x95, 0x21, 0x22, 0x44, 0xfc, + 0x88, 0x88, 0x91, 0x11, 0x42, 0xa1, 0x85, 0x43, + 0x8, 0x89, 0x21, 0x61, 0x4f, 0x81, + + /* U+8228 "舨" */ + 0x10, 0x0, 0x23, 0xf9, 0xf4, 0x2, 0x28, 0x5, + 0x50, 0xa, 0xa0, 0x15, 0x7f, 0x22, 0xa2, 0xfd, + 0x48, 0x8a, 0x91, 0x54, 0xa2, 0xa9, 0x84, 0x51, + 0x8, 0xa6, 0x21, 0x93, 0x4f, 0x42, + + /* U+822A "航" */ + 0x10, 0x20, 0x10, 0x20, 0x7c, 0x10, 0x45, 0xfe, + 0x54, 0x0, 0x54, 0x0, 0x54, 0x0, 0x44, 0xf8, + 0xfc, 0x88, 0x44, 0x88, 0x54, 0x88, 0x54, 0x88, + 0x54, 0x89, 0x44, 0x89, 0x85, 0xa, 0x9f, 0xe, + + /* U+822B "舫" */ + 0x10, 0x0, 0x40, 0x41, 0xf0, 0x82, 0x2f, 0xf6, + 0x48, 0xa, 0x90, 0x15, 0x20, 0x22, 0x7e, 0xfc, + 0x84, 0x89, 0x9, 0x92, 0x12, 0xa4, 0x25, 0x48, + 0x48, 0xa0, 0xa1, 0x41, 0x4f, 0x3c, + + /* U+822C "般" */ + 0x10, 0x0, 0x21, 0xf1, 0xf2, 0x22, 0x24, 0x45, + 0x48, 0x8a, 0x91, 0x11, 0x41, 0xa2, 0x0, 0xfd, + 0xfc, 0xaa, 0x9, 0x52, 0x22, 0xa2, 0x84, 0x47, + 0x8, 0x8e, 0x21, 0x27, 0x4f, 0x83, + + /* U+822D "舭" */ + 0x11, 0x10, 0xfa, 0x21, 0x14, 0x42, 0x28, 0x95, + 0x51, 0x4a, 0xbb, 0x11, 0x44, 0x7e, 0x88, 0x45, + 0x10, 0x8a, 0x21, 0x54, 0x42, 0xa8, 0x94, 0x55, + 0x30, 0xb2, 0x67, 0x47, 0x80, + + /* U+822F "舯" */ + 0x10, 0x20, 0x40, 0x86, 0xc2, 0x11, 0x7f, 0x55, + 0x25, 0x54, 0x94, 0x52, 0x7f, 0x49, 0x45, 0x25, + 0x57, 0xf5, 0x52, 0x51, 0x8, 0x44, 0x22, 0x10, + 0x89, 0xc2, 0x0, + + /* U+8230 "舰" */ + 0x10, 0x0, 0x23, 0xf9, 0xf4, 0x12, 0x28, 0x24, + 0x56, 0x4a, 0xac, 0x95, 0x59, 0x22, 0xb2, 0xfd, + 0x64, 0x8a, 0xc9, 0x55, 0xd2, 0xa3, 0x84, 0x47, + 0x28, 0x9e, 0x61, 0x6c, 0xcf, 0x9e, + + /* U+8231 "舱" */ + 0x10, 0x20, 0x20, 0xc1, 0xf1, 0x42, 0x24, 0x85, + 0x48, 0x8a, 0xa0, 0x95, 0x80, 0xa2, 0xfc, 0xfd, + 0x8, 0x8a, 0x11, 0x54, 0x22, 0xab, 0xc4, 0x50, + 0x28, 0xa0, 0x61, 0x41, 0xce, 0x7e, + + /* U+8233 "舳" */ + 0x10, 0x20, 0x40, 0x86, 0xc2, 0x11, 0x7f, 0x55, + 0x25, 0x54, 0x94, 0x52, 0x7f, 0x49, 0x45, 0xfd, + 0x54, 0x95, 0x52, 0x51, 0x49, 0x45, 0x26, 0x17, + 0xf9, 0xd0, 0x40, + + /* U+8234 "舴" */ + 0x0, 0x0, 0x21, 0x1, 0xf2, 0x2, 0x27, 0xf4, + 0x54, 0xa, 0xa8, 0x15, 0x90, 0x22, 0x3f, 0xfc, + 0x40, 0x88, 0x81, 0x51, 0x2, 0xa3, 0xf4, 0x44, + 0x8, 0x88, 0x21, 0x10, 0x4e, 0x20, + + /* U+8235 "舵" */ + 0x10, 0x20, 0xf8, 0x21, 0x17, 0xfa, 0x28, 0x15, + 0x50, 0x2a, 0x80, 0x11, 0x21, 0x7e, 0x44, 0x44, + 0x90, 0x89, 0xc1, 0x52, 0x2, 0xa4, 0x14, 0x48, + 0x30, 0x90, 0x67, 0x1f, 0x0, + + /* U+8236 "舶" */ + 0x10, 0x21, 0xf1, 0x4, 0x5f, 0xd1, 0x41, 0x55, + 0x5, 0x54, 0x1f, 0xd0, 0x51, 0x7f, 0x55, 0x5, + 0x54, 0x15, 0x50, 0x51, 0x41, 0x85, 0x6, 0x17, + 0xf9, 0xd0, 0x40, + + /* U+8237 "舷" */ + 0x10, 0x20, 0xf8, 0x21, 0x17, 0xfa, 0x21, 0x5, + 0x44, 0xa, 0x88, 0x91, 0x23, 0x7e, 0x84, 0x45, + 0xf0, 0xa8, 0x41, 0x51, 0x22, 0x22, 0x44, 0x48, + 0x50, 0xbf, 0xa7, 0x0, 0x80, + + /* U+8238 "舸" */ + 0x10, 0x0, 0x11, 0xff, 0x7c, 0x2, 0x44, 0x2, + 0x44, 0x2, 0x55, 0xf2, 0x55, 0x12, 0x45, 0x12, + 0xfd, 0x12, 0x45, 0x12, 0x45, 0x12, 0x55, 0xf2, + 0x54, 0x2, 0x44, 0x2, 0x84, 0x2, 0x9c, 0x3c, + + /* U+8239 "船" */ + 0x10, 0x0, 0x41, 0xf1, 0xf2, 0x22, 0x24, 0x44, + 0x48, 0x8a, 0x91, 0x15, 0x42, 0x22, 0x83, 0xfc, + 0x0, 0x8b, 0xf9, 0x54, 0x12, 0xa8, 0x24, 0x50, + 0x48, 0xa0, 0xa1, 0x7f, 0x4e, 0x82, + + /* U+823B "舻" */ + 0x10, 0x20, 0x20, 0x41, 0xf0, 0xfa, 0x21, 0x5, + 0x5f, 0xca, 0xa0, 0xbf, 0x41, 0x22, 0x82, 0x55, + 0xfc, 0xaa, 0x9, 0x54, 0x2, 0x28, 0x4, 0x50, + 0x10, 0xc0, 0x27, 0x80, 0x0, + + /* U+823E "舾" */ + 0x20, 0x0, 0x47, 0xfd, 0xe1, 0x42, 0x42, 0x86, + 0x85, 0xd, 0x7f, 0x92, 0x95, 0x25, 0x2a, 0xfa, + 0x94, 0x95, 0x29, 0xaa, 0x33, 0x58, 0x24, 0xa0, + 0x49, 0x40, 0xa2, 0xff, 0x5d, 0x2, + + /* U+8244 "艄" */ + 0x11, 0x22, 0xfa, 0x45, 0x12, 0x92, 0xa1, 0x5, + 0x5f, 0xe8, 0xa0, 0x7f, 0x40, 0xa2, 0xff, 0x55, + 0x2, 0xaa, 0x5, 0x17, 0xfa, 0x28, 0x14, 0x50, + 0x30, 0xa0, 0x67, 0x47, 0x80, + + /* U+8247 "艇" */ + 0x20, 0x0, 0x23, 0xbe, 0x78, 0x88, 0x49, 0x8, + 0x69, 0x8, 0x6a, 0x8, 0x4a, 0x8, 0x4b, 0xbe, + 0xf8, 0x88, 0x4a, 0x88, 0x6a, 0x88, 0x5b, 0x7e, + 0x49, 0x0, 0x4a, 0x80, 0x8a, 0x7f, 0xbc, 0x0, + + /* U+8249 "艉" */ + 0x10, 0x0, 0x8, 0x7f, 0x8b, 0x20, 0x46, 0x9f, + 0xe3, 0x48, 0x1, 0x24, 0x70, 0x92, 0xc1, 0xf9, + 0x20, 0x24, 0x93, 0x1a, 0x7e, 0xd, 0x24, 0x4, + 0x9f, 0xe6, 0x59, 0xa, 0x28, 0x8b, 0x74, 0x7c, + + /* U+824B "艋" */ + 0x10, 0x0, 0x10, 0xfe, 0x3c, 0x4, 0x24, 0x8, + 0x34, 0x8, 0x35, 0xff, 0x24, 0x8, 0x24, 0x8, + 0x7c, 0x70, 0x24, 0x0, 0x35, 0xfe, 0x35, 0x2a, + 0x25, 0x2a, 0x65, 0x2a, 0x47, 0xff, 0x5c, 0x0, + + /* U+824F "艏" */ + 0x10, 0x88, 0xf8, 0xa1, 0x17, 0xfa, 0x22, 0x5, + 0x5f, 0xc8, 0xa0, 0xbf, 0x41, 0x22, 0xfe, 0x45, + 0x4, 0xaa, 0x9, 0x57, 0xf2, 0x28, 0x24, 0x50, + 0x50, 0xbf, 0xa7, 0x41, 0x0, + + /* U+8258 "艘" */ + 0x20, 0xa0, 0xf6, 0x5d, 0x28, 0x8a, 0x51, 0x16, + 0xbe, 0xed, 0x44, 0x52, 0x88, 0xfd, 0xff, 0x48, + 0x20, 0xd7, 0xf9, 0xa6, 0x12, 0x44, 0x44, 0x87, + 0x11, 0xf, 0x2f, 0xe1, 0x80, + + /* U+825A "艚" */ + 0x20, 0x50, 0x47, 0xfd, 0x61, 0x43, 0x5f, 0xf5, + 0xa5, 0x2b, 0x4a, 0x52, 0xff, 0xfd, 0x29, 0x49, + 0xac, 0xb3, 0xf9, 0x68, 0x12, 0x4f, 0xec, 0xa0, + 0x51, 0x3f, 0xae, 0x81, 0x0, + + /* U+825F "艟" */ + 0x20, 0x20, 0x40, 0x41, 0xe7, 0xfa, 0x44, 0x46, + 0x84, 0x8d, 0x7f, 0xd2, 0x0, 0x24, 0xfe, 0xf9, + 0x24, 0x93, 0xf9, 0xa4, 0x93, 0x4f, 0xe4, 0x82, + 0x9, 0x1f, 0x22, 0xff, 0xdc, 0x0, + + /* U+8268 "艨" */ + 0x20, 0x88, 0x47, 0xfd, 0x62, 0x22, 0x5f, 0xf6, + 0xa0, 0x29, 0x1f, 0x12, 0x0, 0x7d, 0xff, 0x48, + 0x60, 0xd6, 0x99, 0xa1, 0xe2, 0x5d, 0x44, 0x8e, + 0x51, 0x64, 0x6e, 0x38, 0x0, + + /* U+826E "艮" */ + 0x7f, 0xf1, 0x0, 0x44, 0x1, 0x1f, 0xfc, 0x40, + 0x11, 0x0, 0x47, 0xff, 0x11, 0x0, 0x42, 0x19, + 0x9, 0x84, 0x18, 0x11, 0x30, 0x78, 0x39, 0x0, + 0x10, + + /* U+826F "良" */ + 0x2, 0x0, 0x8, 0x7, 0xff, 0x10, 0x4, 0x40, + 0x11, 0xff, 0xc4, 0x1, 0x1f, 0xfc, 0x44, 0x9, + 0x8, 0x44, 0x36, 0x10, 0x60, 0x4c, 0x61, 0xc0, + 0x70, 0x0, 0x0, + + /* U+8270 "艰" */ + 0x3, 0xf9, 0xf4, 0x10, 0x28, 0x20, 0x50, 0x48, + 0xbf, 0x8a, 0x41, 0x14, 0x82, 0x19, 0xfc, 0x32, + 0x40, 0x64, 0x88, 0xc9, 0x62, 0x51, 0x4, 0xa5, + 0x10, 0x71, 0x80, 0x81, 0x0, + + /* U+8272 "色" */ + 0x4, 0x0, 0x10, 0x0, 0x7f, 0xc0, 0x81, 0x6, + 0x4, 0x1f, 0xff, 0x88, 0x41, 0x10, 0x82, 0x21, + 0x4, 0x7f, 0xf8, 0x80, 0x1, 0x0, 0x2, 0x0, + 0x24, 0x0, 0x48, 0x0, 0x8f, 0xfe, + + /* U+8273 "艳" */ + 0x20, 0x80, 0x87, 0xef, 0xa1, 0x8, 0x88, 0x27, + 0xfc, 0x91, 0x1f, 0xc4, 0x49, 0x11, 0x23, 0xff, + 0xf0, 0x2, 0x40, 0x9, 0x0, 0x24, 0x4, 0x88, + 0x12, 0x3f, 0xc0, + + /* U+8274 "艴" */ + 0x28, 0x21, 0xfc, 0xb8, 0xaa, 0x21, 0x5f, 0xff, + 0xe9, 0x35, 0x12, 0x6a, 0x24, 0xd4, 0x49, 0x7e, + 0xfe, 0x55, 0x0, 0xaa, 0x2, 0x74, 0x14, 0x88, + 0x31, 0x1f, 0x80, + + /* U+8279 "艹" */ + 0x8, 0x20, 0x10, 0x40, 0x20, 0x87, 0xff, 0xf0, + 0x82, 0x1, 0x4, 0x0, + + /* U+827A "艺" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x0, + 0x0, 0x0, 0x0, 0xf, 0xfc, 0x0, 0x10, 0x0, + 0xc0, 0x6, 0x0, 0x30, 0x0, 0x80, 0x12, 0x0, + 0x28, 0x0, 0x8f, 0xff, 0x0, + + /* U+827D "艽" */ + 0x4, 0x20, 0x8, 0x43, 0xff, 0xf8, 0x21, 0x0, + 0x40, 0x0, 0x80, 0x1f, 0xf8, 0x2, 0x10, 0x4, + 0x20, 0x8, 0x40, 0x20, 0x88, 0x41, 0x11, 0x2, + 0x26, 0x4, 0x58, 0x7, 0x0, 0x0, + + /* U+827E "艾" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x0, + 0x82, 0x0, 0x0, 0x4, 0x4, 0x4, 0x10, 0xc, + 0x40, 0x8, 0x80, 0xa, 0x0, 0x1c, 0x0, 0x44, + 0x7, 0x7, 0x30, 0x1, 0x80, + + /* U+827F "艿" */ + 0x4, 0x20, 0x8, 0x43, 0xff, 0xf8, 0x21, 0x0, + 0x0, 0xf, 0xfe, 0x1, 0x4, 0x2, 0x10, 0x4, + 0x3c, 0x10, 0x8, 0x20, 0x10, 0x40, 0x21, 0x0, + 0x44, 0x0, 0xb0, 0x1e, 0x0, + + /* U+8282 "节" */ + 0x4, 0x20, 0x8, 0x43, 0xff, 0xf8, 0x21, 0x0, + 0x0, 0x0, 0x0, 0x1f, 0xfe, 0x2, 0x4, 0x4, + 0x8, 0x8, 0x10, 0x10, 0x20, 0x20, 0x40, 0x4f, + 0x0, 0x80, 0x1, 0x0, 0x0, + + /* U+8284 "芄" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x0, + 0x0, 0x0, 0x80, 0x1f, 0xf8, 0x2, 0x10, 0x4, + 0x20, 0x48, 0x40, 0x70, 0x80, 0x59, 0x10, 0x8a, + 0x26, 0x4, 0x78, 0x7, 0x0, 0x0, + + /* U+8288 "芈" */ + 0x11, 0x10, 0x22, 0x20, 0x44, 0x47, 0x88, 0xf1, + 0x11, 0x2, 0x22, 0x4, 0x44, 0x3f, 0xfe, 0x1, + 0x0, 0x2, 0x0, 0x4, 0x7, 0xff, 0xf0, 0x10, + 0x0, 0x20, 0x0, 0x40, 0x0, + + /* U+828A "芊" */ + 0x4, 0x20, 0x8, 0x43, 0xff, 0xf8, 0x21, 0x0, + 0x0, 0x7, 0xff, 0x0, 0x40, 0x0, 0x80, 0x1, + 0x1, 0xff, 0xfc, 0x4, 0x0, 0x8, 0x0, 0x10, + 0x0, 0x20, 0x0, 0x40, 0x0, + + /* U+828B "芋" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x0, + 0x0, 0x7, 0xff, 0x0, 0x40, 0x0, 0x80, 0x1, + 0x1, 0xff, 0xfc, 0x4, 0x0, 0x8, 0x0, 0x10, + 0x0, 0x20, 0x3, 0xc0, 0x0, + + /* U+828D "芍" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x1, + 0x0, 0x7, 0xff, 0x88, 0x1, 0x20, 0x2, 0x88, + 0x4, 0x8, 0x8, 0x8, 0x10, 0x0, 0x20, 0x0, + 0x40, 0x1, 0x0, 0x7c, 0x0, + + /* U+828E "芎" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x0, + 0x0, 0xf, 0xff, 0x0, 0x2, 0x0, 0x4, 0x7f, + 0xf8, 0x80, 0x1, 0x0, 0x3, 0xff, 0xc0, 0x0, + 0x80, 0x1, 0x0, 0x2, 0x0, 0xf8, + + /* U+828F "芏" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x0, + 0x82, 0x0, 0x20, 0x0, 0x40, 0x3f, 0xfe, 0x1, + 0x0, 0x2, 0x0, 0x4, 0x0, 0x8, 0x0, 0x10, + 0x1f, 0xff, 0xc0, + + /* U+8291 "芑" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x0, + 0x0, 0x7, 0xff, 0x0, 0x2, 0x0, 0x4, 0x0, + 0x8, 0x7f, 0xf0, 0x80, 0x1, 0x0, 0x12, 0x0, + 0x24, 0x0, 0xc7, 0xff, 0x0, + + /* U+8292 "芒" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x0, + 0x92, 0x0, 0x10, 0x0, 0x20, 0x7f, 0xff, 0x10, + 0x0, 0x20, 0x0, 0x40, 0x0, 0x80, 0x1, 0x0, + 0x3, 0xff, 0x80, + + /* U+8297 "芗" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x0, + 0x60, 0x3, 0x6, 0x1f, 0xf0, 0x0, 0x80, 0x6, + 0x8, 0x30, 0x20, 0xff, 0xc0, 0x2, 0x0, 0x18, + 0x1, 0x80, 0x3c, 0x0, 0x0, + + /* U+8298 "芘" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x0, + 0x0, 0x4, 0x0, 0x8, 0x20, 0x90, 0x46, 0x3f, + 0xb0, 0x40, 0x80, 0x82, 0x1, 0x4, 0x12, 0x8, + 0x2d, 0xc8, 0x5c, 0x1f, 0x0, + + /* U+8299 "芙" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x0, + 0x10, 0xf, 0xff, 0x80, 0x40, 0x0, 0x80, 0x1, + 0x1, 0xff, 0xfc, 0xc, 0x0, 0x14, 0x0, 0xc4, + 0x3, 0x6, 0x38, 0x3, 0x80, + + /* U+829C "芜" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x0, + 0x0, 0xf, 0xff, 0x80, 0x40, 0x0, 0x80, 0xff, + 0xfe, 0x4, 0x0, 0xa, 0x0, 0x24, 0x10, 0xc8, + 0x26, 0x10, 0x78, 0x3f, 0x0, + + /* U+829D "芝" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x0, + 0x10, 0x0, 0x0, 0x1f, 0xfe, 0x0, 0xc, 0x0, + 0x20, 0x1, 0x80, 0xc, 0x0, 0x60, 0x3, 0x0, + 0x1e, 0x0, 0x23, 0xff, 0x80, + + /* U+829F "芟" */ + 0x8, 0x40, 0x10, 0x83, 0xff, 0xf8, 0x42, 0x0, + 0x0, 0x3, 0xfc, 0x4, 0x8, 0x18, 0x18, 0xc0, + 0xc, 0x7f, 0xe0, 0x40, 0x40, 0x43, 0x0, 0x68, + 0x0, 0xf0, 0x1e, 0x1f, 0xc0, 0x0, + + /* U+82A1 "芡" */ + 0x8, 0x21, 0xff, 0xfc, 0x20, 0x80, 0x40, 0x1, + 0x0, 0x3, 0xff, 0x8c, 0x2, 0x10, 0x84, 0x41, + 0x10, 0x2, 0x0, 0xa, 0x0, 0x26, 0x0, 0x82, + 0x6, 0x3, 0x30, 0x1, 0x80, + + /* U+82A4 "芤" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x0, + 0x0, 0x1f, 0xe8, 0x0, 0x90, 0x2, 0x20, 0x8, + 0x40, 0x1e, 0x83, 0xe1, 0x0, 0x42, 0x10, 0x84, + 0x21, 0x8, 0x5c, 0xf, 0x0, + + /* U+82A5 "芥" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x0, + 0x10, 0x0, 0x50, 0x3, 0x18, 0x8, 0xc, 0x60, + 0xf, 0x10, 0x40, 0x20, 0x80, 0x41, 0x0, 0x82, + 0x2, 0x4, 0x18, 0x8, 0x0, + + /* U+82A6 "芦" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x0, + 0x10, 0x7, 0xff, 0x10, 0x2, 0x20, 0x4, 0x40, + 0x8, 0xff, 0xf1, 0x0, 0x22, 0x0, 0x4, 0x0, + 0x10, 0x0, 0x20, 0x0, 0x0, + + /* U+82A8 "芨" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x0, + 0x0, 0xf, 0xfe, 0x2, 0x8, 0x4, 0x20, 0x14, + 0x78, 0x28, 0x10, 0x48, 0x41, 0x9, 0x2, 0xc, + 0x8, 0x76, 0x27, 0x3, 0x80, + + /* U+82A9 "芩" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x0, + 0x10, 0x0, 0xd0, 0x3, 0x18, 0x18, 0x8c, 0xc1, + 0x6, 0x0, 0x0, 0xff, 0xc0, 0x0, 0x80, 0x2, + 0x0, 0x8, 0x0, 0x20, 0x0, + + /* U+82AA "芪" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x40, 0x0, + 0x0, 0x7, 0xff, 0x88, 0x20, 0x10, 0x40, 0x3f, + 0xfe, 0x40, 0x80, 0x81, 0x1, 0x2, 0x12, 0x32, + 0x27, 0x86, 0x48, 0x7, 0x0, + + /* U+82AB "芫" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x0, + 0x0, 0xf, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, + 0xfe, 0x8, 0x80, 0x21, 0x0, 0x42, 0x10, 0x84, + 0x22, 0x8, 0x78, 0x1f, 0x0, 0x0, + + /* U+82AC "芬" */ + 0x4, 0x21, 0xff, 0xfc, 0x10, 0x80, 0x0, 0x0, + 0x44, 0x1, 0x4, 0x4, 0x4, 0x30, 0x6, 0x9f, + 0xf2, 0x8, 0x20, 0x10, 0x40, 0x20, 0x80, 0x81, + 0x2, 0x2, 0x18, 0x78, 0x0, + + /* U+82AD "芭" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x0, + 0x0, 0x7, 0xff, 0x88, 0x41, 0x10, 0x82, 0x21, + 0x4, 0x7f, 0xf8, 0x80, 0x1, 0x0, 0x2, 0x0, + 0x24, 0x0, 0x4f, 0xff, 0x0, + + /* U+82AE "芮" */ + 0x8, 0x40, 0x10, 0x83, 0xff, 0xf8, 0x42, 0x0, + 0x10, 0x0, 0x20, 0x1f, 0xff, 0x21, 0x2, 0x42, + 0x4, 0x87, 0x9, 0x11, 0x12, 0x41, 0x25, 0x0, + 0x48, 0x1, 0x10, 0x1e, 0x0, + + /* U+82AF "芯" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x0, + 0x82, 0x0, 0x20, 0x0, 0x40, 0x4, 0x80, 0x48, + 0x88, 0x90, 0x9, 0x20, 0x54, 0x40, 0x98, 0x81, + 0x1, 0x6, 0x3, 0xf8, 0x0, + + /* U+82B0 "芰" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x8, 0x0, + 0x10, 0x1f, 0xff, 0xc0, 0x40, 0x0, 0x80, 0x3f, + 0xf8, 0x20, 0x30, 0x20, 0xc0, 0x22, 0x0, 0x38, + 0x1, 0xdc, 0x3c, 0x7, 0x80, + + /* U+82B1 "花" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x0, + 0x0, 0x1, 0x10, 0x4, 0x21, 0x8, 0x44, 0x30, + 0xb0, 0xa1, 0x82, 0x46, 0x0, 0xb4, 0x11, 0x8, + 0x22, 0x10, 0x44, 0x1f, 0x0, + + /* U+82B3 "芳" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x49, 0x0, + 0x10, 0x0, 0x0, 0x3f, 0xff, 0x84, 0x0, 0x8, + 0x0, 0x1f, 0xf0, 0x20, 0x20, 0x40, 0x41, 0x0, + 0x84, 0x1, 0x30, 0x3c, 0x0, + + /* U+82B4 "芴" */ + 0x2, 0x10, 0x2, 0x10, 0x7f, 0xff, 0x2, 0x10, + 0x8, 0x0, 0x1f, 0xfe, 0x22, 0x22, 0x42, 0x22, + 0x4, 0x42, 0xc, 0x42, 0x18, 0x44, 0x20, 0x84, + 0x41, 0x4, 0x2, 0x4, 0xc, 0x78, + + /* U+82B7 "芷" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x0, + 0x8a, 0x0, 0x10, 0x4, 0x20, 0x8, 0x7c, 0x10, + 0x80, 0x21, 0x0, 0x42, 0x0, 0x84, 0x1, 0x8, + 0x1f, 0xff, 0xc0, + + /* U+82B8 "芸" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x0, + 0x0, 0x7, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x1, 0xff, 0xfc, 0x10, 0x0, 0x41, 0x1, 0x1, + 0xf, 0xff, 0x0, 0x1, 0x0, + + /* U+82B9 "芹" */ + 0x8, 0x40, 0x21, 0xf, 0xff, 0xc2, 0x10, 0x0, + 0x8, 0xff, 0x42, 0x0, 0x8, 0x0, 0x3f, 0xfc, + 0x82, 0x2, 0x8, 0x8, 0x20, 0x40, 0x83, 0x2, + 0x8, 0x8, 0x0, + + /* U+82BD "芽" */ + 0x0, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x0, + 0x0, 0xf, 0xff, 0xc0, 0x8, 0x8, 0x10, 0x20, + 0x20, 0x7f, 0xfc, 0x1, 0x80, 0xd, 0x0, 0x22, + 0x1, 0x84, 0x1c, 0x8, 0x40, 0xf0, + + /* U+82BE "芾" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x0, + 0x10, 0x1f, 0xff, 0xc0, 0x40, 0x0, 0x80, 0x3f, + 0xf8, 0x42, 0x10, 0x84, 0x21, 0x8, 0x42, 0x13, + 0x84, 0x20, 0x0, 0x40, 0x0, + + /* U+82C1 "苁" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x0, + 0x0, 0x1, 0x4, 0x2, 0x8, 0x4, 0x10, 0xc, + 0x20, 0x24, 0xc0, 0x41, 0x41, 0x4, 0x86, 0x18, + 0x98, 0x60, 0x80, 0x0, 0x0, + + /* U+82C4 "苄" */ + 0x8, 0x20, 0x10, 0x41, 0xff, 0xf0, 0x49, 0x0, + 0x10, 0x0, 0x10, 0x3f, 0xff, 0x81, 0x0, 0x2, + 0x0, 0x7, 0x0, 0x9, 0x80, 0x10, 0xe0, 0x20, + 0x0, 0x40, 0x0, 0x80, 0x0, + + /* U+82C7 "苇" */ + 0x4, 0x20, 0x8, 0x43, 0xff, 0xf8, 0x21, 0x0, + 0x10, 0xf, 0xff, 0xc0, 0x40, 0x1f, 0xfe, 0x1, + 0x0, 0xff, 0xf8, 0x4, 0x10, 0x8, 0x20, 0x17, + 0xc0, 0x20, 0x0, 0x40, 0x0, + + /* U+82C8 "苈" */ + 0x4, 0x20, 0x8, 0x41, 0xff, 0xf8, 0x21, 0x0, + 0x0, 0x7, 0xff, 0xc8, 0x20, 0x10, 0x40, 0x2f, + 0xfc, 0x41, 0x8, 0x82, 0x11, 0x8, 0x24, 0x10, + 0x48, 0xc0, 0xa7, 0x1e, 0x0, + + /* U+82CA "苊" */ + 0x8, 0x40, 0x10, 0x83, 0xff, 0xf8, 0x42, 0x0, + 0x0, 0xf, 0xff, 0x90, 0x0, 0x20, 0x0, 0x4f, + 0xf0, 0x90, 0x21, 0x20, 0x42, 0x41, 0x4, 0x8e, + 0x49, 0x0, 0xa1, 0x1, 0x43, 0xfc, + + /* U+82CB "苋" */ + 0x8, 0x40, 0x10, 0x83, 0xff, 0xf8, 0x42, 0x0, + 0x0, 0x7, 0xff, 0x8, 0x2, 0x11, 0x4, 0x22, + 0x8, 0x44, 0x10, 0x8c, 0x20, 0x38, 0x0, 0x50, + 0x23, 0x20, 0x78, 0x3f, 0x0, + + /* U+82CC "苌" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x1, 0x1, + 0x0, 0x82, 0xe, 0x5, 0xe0, 0x8, 0x0, 0xff, + 0xfe, 0x21, 0x0, 0x41, 0x0, 0x83, 0x1, 0x11, + 0x3, 0xc1, 0x84, 0x0, 0x80, + + /* U+82CD "苍" */ + 0x4, 0x20, 0x8, 0x43, 0xff, 0xf8, 0x25, 0x0, + 0x38, 0x1, 0x8c, 0x1c, 0x7, 0xef, 0xf8, 0x10, + 0x10, 0x20, 0x20, 0x40, 0x40, 0x8f, 0x21, 0x0, + 0x42, 0x0, 0x83, 0xfe, 0x0, + + /* U+82CE "苎" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x49, 0x0, + 0x10, 0x0, 0x10, 0x1f, 0xff, 0x20, 0x2, 0x40, + 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x1f, 0xff, 0xc0, + + /* U+82CF "苏" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x0, + 0x20, 0x0, 0x40, 0x1f, 0xfc, 0x1, 0x8, 0x12, + 0x18, 0x44, 0x29, 0x10, 0x4e, 0x20, 0x90, 0x81, + 0x2, 0x2, 0x18, 0x78, 0x0, + + /* U+82D1 "苑" */ + 0x4, 0x21, 0xff, 0xfc, 0x10, 0x80, 0x80, 0x1, + 0x0, 0x7, 0xdf, 0x88, 0xa1, 0x31, 0x42, 0x64, + 0x85, 0x29, 0x8, 0x12, 0xe0, 0x44, 0x11, 0x88, + 0x26, 0x10, 0x58, 0x1f, 0x0, + + /* U+82D2 "苒" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x0, + 0x10, 0x7, 0xff, 0x8, 0x42, 0x10, 0x84, 0x3f, + 0xf8, 0x42, 0x10, 0x84, 0x27, 0xff, 0xf2, 0x0, + 0x84, 0x1, 0x8, 0x2, 0x10, 0x3c, + + /* U+82D3 "苓" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x0, + 0x38, 0x0, 0x88, 0x6, 0xc, 0x70, 0x87, 0x0, + 0x0, 0x7f, 0xf0, 0x0, 0x40, 0x83, 0x0, 0xe8, + 0x0, 0x30, 0x0, 0x18, 0x0, + + /* U+82D4 "苔" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x0, + 0x60, 0x1, 0x4, 0xc, 0x4, 0x3f, 0xfe, 0x0, + 0x2, 0x7f, 0xf0, 0x80, 0x21, 0x0, 0x42, 0x0, + 0x87, 0xff, 0x8, 0x2, 0x0, + + /* U+82D5 "苕" */ + 0x8, 0x21, 0xff, 0xfc, 0x20, 0x80, 0x0, 0x7, + 0xff, 0x80, 0x81, 0x1, 0x2, 0x4, 0x4, 0x30, + 0xf1, 0x80, 0x0, 0xff, 0xe1, 0x0, 0x42, 0x0, + 0x87, 0xff, 0x8, 0x2, 0x0, + + /* U+82D7 "苗" */ + 0x8, 0x40, 0x10, 0x83, 0xff, 0xf8, 0x42, 0x0, + 0x0, 0xf, 0xff, 0x10, 0x42, 0x20, 0x84, 0x41, + 0x8, 0xff, 0xf1, 0x4, 0x22, 0x8, 0x44, 0x10, + 0x8f, 0xff, 0x10, 0x2, 0x0, + + /* U+82D8 "苘" */ + 0x0, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x0, + 0x0, 0xf, 0xff, 0x90, 0x1, 0x20, 0x2, 0x47, + 0xe4, 0x90, 0x49, 0x20, 0x92, 0x41, 0x24, 0x7e, + 0x48, 0x0, 0x90, 0x1, 0x20, 0x1c, + + /* U+82DB "苛" */ + 0x4, 0x20, 0x8, 0x43, 0xff, 0xf8, 0x21, 0x0, + 0x0, 0x1f, 0xff, 0xc0, 0x2, 0x0, 0x4, 0x3f, + 0x88, 0x41, 0x10, 0x82, 0x21, 0x4, 0x43, 0xf8, + 0x84, 0x1, 0x0, 0x2, 0x0, 0x3c, + + /* U+82DC "苜" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x0, + 0x0, 0x7, 0xff, 0x8, 0x2, 0x10, 0x4, 0x3f, + 0xf8, 0x40, 0x10, 0x80, 0x21, 0xff, 0xc2, 0x0, + 0x84, 0x1, 0xf, 0xfe, 0x10, 0x4, + + /* U+82DE "苞" */ + 0x4, 0x20, 0x8, 0x43, 0xff, 0xf8, 0x21, 0x1, + 0x0, 0x7, 0xff, 0x8, 0x2, 0x3f, 0xc4, 0xa0, + 0x88, 0x41, 0x10, 0xfe, 0x21, 0x0, 0x52, 0x7, + 0x24, 0x0, 0x4c, 0x0, 0x8f, 0xfe, + + /* U+82DF "苟" */ + 0x8, 0x40, 0x10, 0x83, 0xff, 0xf8, 0x42, 0x3, + 0x0, 0xf, 0xff, 0xb0, 0x1, 0x1f, 0xc2, 0x20, + 0x84, 0x41, 0x8, 0x82, 0x11, 0xfc, 0x22, 0x0, + 0x40, 0x1, 0x0, 0x3e, 0x0, + + /* U+82E0 "苠" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x0, + 0x0, 0xf, 0xff, 0x90, 0x1, 0x20, 0x2, 0x7f, + 0xfc, 0x81, 0x1, 0xff, 0xfa, 0x2, 0x4, 0x4, + 0x28, 0x4, 0x57, 0x8c, 0xb0, 0xe, + + /* U+82E1 "苡" */ + 0x8, 0x40, 0x10, 0x83, 0xff, 0xf8, 0x42, 0x0, + 0x0, 0x8, 0x42, 0x10, 0x84, 0x20, 0x88, 0x40, + 0x10, 0x80, 0x41, 0x10, 0xc2, 0xc2, 0x46, 0x8, + 0x48, 0x60, 0x41, 0x0, 0x0, + + /* U+82E3 "苣" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x0, + 0x0, 0xf, 0xff, 0xd0, 0x0, 0x20, 0x0, 0x7f, + 0xfc, 0x80, 0x9, 0x0, 0x13, 0xff, 0xe4, 0x0, + 0x8, 0x0, 0x1f, 0xff, 0x80, + + /* U+82E4 "苤" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x0, + 0x0, 0xf, 0xff, 0x80, 0x40, 0x1, 0x0, 0xd, + 0x60, 0x22, 0x33, 0x84, 0x18, 0x8, 0x0, 0x0, + 0x1f, 0xff, 0xc0, + + /* U+82E5 "若" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x0, + 0x20, 0x1f, 0xff, 0xc1, 0x0, 0x4, 0x0, 0x18, + 0x0, 0x7f, 0xf1, 0x40, 0x24, 0x80, 0x41, 0x0, + 0x83, 0xff, 0x4, 0x2, 0x0, + + /* U+82E6 "苦" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x0, + 0x10, 0x1f, 0xff, 0xc0, 0x40, 0x0, 0x80, 0x1, + 0x0, 0x7f, 0xf0, 0x80, 0x21, 0x0, 0x42, 0x0, + 0x87, 0xff, 0x8, 0x2, 0x0, + + /* U+82EB "苫" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x0, + 0x10, 0x0, 0x3f, 0xc0, 0x40, 0x0, 0x80, 0x1, + 0x0, 0x7f, 0xf8, 0x80, 0x11, 0x0, 0x22, 0x0, + 0x47, 0xff, 0x88, 0x1, 0x0, + + /* U+82EF "苯" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x0, + 0x10, 0x1f, 0xff, 0xc0, 0xe0, 0x2, 0xa0, 0x9, + 0x20, 0x32, 0x60, 0xc4, 0x62, 0x7f, 0x38, 0x10, + 0x20, 0x20, 0x0, 0x40, 0x0, + + /* U+82F1 "英" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x0, + 0x10, 0x7, 0xff, 0x8, 0x42, 0x10, 0x84, 0x21, + 0x9, 0xff, 0xfc, 0x6, 0x0, 0x14, 0x0, 0xc6, + 0x6, 0x3, 0xb0, 0x0, 0x80, + + /* U+82F4 "苴" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x0, + 0x0, 0x3, 0xff, 0x8, 0x2, 0x10, 0x4, 0x1f, + 0xf8, 0x40, 0x10, 0x80, 0x20, 0xff, 0xc2, 0x0, + 0x84, 0x1, 0x3f, 0xff, 0x80, + + /* U+82F7 "苷" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x0, + 0x0, 0x1f, 0xff, 0xc4, 0x4, 0x8, 0x8, 0x10, + 0x10, 0x3f, 0xe0, 0x40, 0x40, 0x80, 0x81, 0x1, + 0x3, 0xfe, 0x4, 0x4, 0x0, + + /* U+82F9 "苹" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x0, + 0x0, 0xf, 0xff, 0x80, 0x40, 0x10, 0x84, 0x11, + 0x10, 0x12, 0x40, 0x4, 0x7, 0xff, 0xf0, 0x10, + 0x0, 0x20, 0x0, 0x40, 0x0, 0x80, + + /* U+82FB "苻" */ + 0x4, 0x40, 0x8, 0x83, 0xff, 0xf8, 0x22, 0x0, + 0x81, 0x2, 0x2, 0x9, 0xff, 0xb0, 0x8, 0xe0, + 0x11, 0x44, 0x20, 0x84, 0x41, 0x8, 0x82, 0x1, + 0x4, 0x2, 0x8, 0x3c, 0x0, + + /* U+8301 "茁" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x2, + 0x10, 0x84, 0x21, 0x8, 0x42, 0x1f, 0xfc, 0x1, + 0x0, 0x2, 0x1, 0x4, 0x12, 0x8, 0x24, 0x10, + 0x4f, 0xff, 0x80, 0x1, 0x0, + + /* U+8302 "茂" */ + 0x4, 0x20, 0x4, 0x20, 0x7f, 0xff, 0x4, 0x20, + 0x0, 0x8c, 0x0, 0x80, 0x3f, 0xfe, 0x20, 0x80, + 0x20, 0x80, 0x20, 0x48, 0x20, 0x50, 0x20, 0x61, + 0x20, 0xe3, 0x43, 0x12, 0x44, 0xe, + + /* U+8303 "范" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x2, + 0x0, 0x2, 0x7f, 0x0, 0x82, 0x61, 0x4, 0x62, + 0x8, 0x4, 0x10, 0x49, 0xc1, 0x90, 0x12, 0x20, + 0x28, 0x40, 0x50, 0xff, 0x0, + + /* U+8304 "茄" */ + 0x4, 0x21, 0xff, 0xfc, 0x10, 0x80, 0xa1, 0x1, + 0x0, 0xf, 0xcf, 0x84, 0x91, 0x9, 0x22, 0x12, + 0x44, 0x24, 0x88, 0x89, 0x11, 0x12, 0x22, 0x27, + 0xc8, 0x48, 0xb3, 0x11, 0x0, + + /* U+8305 "茅" */ + 0x4, 0x21, 0xff, 0xfc, 0x10, 0x80, 0x0, 0x3, + 0xff, 0xc1, 0x3, 0x1, 0x98, 0x0, 0xc0, 0x7f, + 0xfe, 0x3, 0x4, 0xa, 0x8, 0x64, 0x23, 0x8, + 0x1c, 0x10, 0x1, 0xe0, 0x0, + + /* U+8306 "茆" */ + 0x4, 0x21, 0xff, 0xfc, 0x10, 0x80, 0x0, 0x1, + 0xe0, 0xc, 0xf, 0x90, 0x91, 0x21, 0x22, 0x42, + 0x44, 0xbc, 0x89, 0x91, 0x10, 0x22, 0xe0, 0x84, + 0x6, 0x8, 0x18, 0x10, 0x0, + + /* U+8307 "茇" */ + 0x8, 0x21, 0xff, 0xfc, 0x20, 0x80, 0x20, 0x0, + 0x41, 0x1f, 0xff, 0xc2, 0x0, 0x4, 0x0, 0x1f, + 0xf8, 0x30, 0x10, 0x90, 0x41, 0x11, 0x4, 0x1c, + 0x10, 0xee, 0xe, 0x3, 0x80, + + /* U+8308 "茈" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x0, + 0x0, 0x0, 0x88, 0x11, 0x11, 0xa3, 0xe4, 0x44, + 0x70, 0x88, 0x81, 0x11, 0x2, 0x22, 0x14, 0x44, + 0x2f, 0xe8, 0x70, 0xf, 0x0, + + /* U+8309 "茉" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x0, 0x0, + 0x10, 0xf, 0xff, 0x80, 0x40, 0x0, 0x80, 0x7f, + 0xfc, 0x7, 0x0, 0x15, 0x0, 0x49, 0x3, 0x11, + 0x98, 0x20, 0xc0, 0x40, 0x0, + + /* U+830C "茌" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x0, + 0x4, 0x2, 0x8, 0x8, 0x10, 0x17, 0xff, 0x60, + 0x41, 0x40, 0x80, 0x81, 0x1, 0x2, 0x2, 0x4, + 0x4, 0xff, 0xc8, 0x0, 0x0, + + /* U+830E "茎" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x0, + 0x0, 0xf, 0xfe, 0x0, 0x8, 0x0, 0x60, 0x3, + 0xe0, 0x78, 0x3a, 0x0, 0x9, 0xff, 0xc0, 0x10, + 0x0, 0x20, 0x3f, 0xff, 0x80, + + /* U+830F "茏" */ + 0x8, 0x21, 0xff, 0xfc, 0x20, 0x80, 0x12, 0x0, + 0x42, 0x1f, 0xff, 0xc1, 0x20, 0x2, 0x40, 0x4, + 0x88, 0x11, 0x20, 0x22, 0x80, 0x86, 0x12, 0x38, + 0x2c, 0x90, 0x70, 0x1f, 0x0, + + /* U+8311 "茑" */ + 0x4, 0x21, 0xff, 0xfc, 0x10, 0x80, 0x10, 0x3, + 0xff, 0x84, 0x1, 0x9, 0x82, 0x10, 0xbc, 0x20, + 0x0, 0x7f, 0xf8, 0x0, 0x17, 0xff, 0xa0, 0x0, + 0x40, 0x0, 0x80, 0x1e, 0x0, + + /* U+8314 "茔" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x0, + 0x0, 0x1f, 0xff, 0xe0, 0x40, 0xc0, 0x81, 0x1, + 0x0, 0xff, 0xf8, 0x4, 0x0, 0x8, 0x0, 0x10, + 0x1f, 0xff, 0xc0, + + /* U+8315 "茕" */ + 0x8, 0x40, 0x21, 0xf, 0xff, 0xc2, 0x10, 0x0, + 0x3, 0xff, 0xf8, 0x0, 0x60, 0x1, 0x3f, 0xe0, + 0x10, 0x80, 0x42, 0x1, 0x8, 0x7f, 0xa0, 0x10, + 0x90, 0x41, 0x41, 0x3, + + /* U+8317 "茗" */ + 0x4, 0x20, 0x8, 0x41, 0xff, 0xf8, 0x21, 0x0, + 0x20, 0x1, 0xff, 0x6, 0x6, 0x32, 0x10, 0x0, + 0xc0, 0x6, 0x0, 0x7f, 0xf3, 0x80, 0x21, 0x0, + 0x42, 0x0, 0x87, 0xff, 0x8, 0x2, + + /* U+831A "茚" */ + 0x4, 0x21, 0xff, 0xfc, 0x10, 0x80, 0x0, 0x1, + 0xe0, 0xe, 0x1f, 0x90, 0x21, 0x20, 0x42, 0x7e, + 0x84, 0x81, 0x9, 0x2, 0x12, 0x14, 0x27, 0xcb, + 0xc8, 0x10, 0x0, 0x20, 0x0, + + /* U+831B "茛" */ + 0x8, 0x21, 0xff, 0xfc, 0x20, 0x80, 0x0, 0x3, + 0xff, 0x84, 0x1, 0xf, 0xfe, 0x10, 0x4, 0x20, + 0x8, 0x7f, 0xf0, 0x84, 0x11, 0x4, 0xc2, 0xe, + 0x4, 0xc6, 0x1e, 0x3, 0x80, + + /* U+831C "茜" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x0, + 0x0, 0x1f, 0xff, 0xc1, 0x10, 0x2, 0x20, 0x7f, + 0xfc, 0x88, 0x89, 0x11, 0x12, 0x43, 0xe5, 0x0, + 0x48, 0x0, 0x9f, 0xff, 0x20, 0x2, + + /* U+8327 "茧" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x49, 0x0, + 0x10, 0x7, 0xff, 0x88, 0x41, 0x10, 0x82, 0x21, + 0x4, 0x7f, 0xf8, 0x84, 0x0, 0x8, 0x40, 0x10, + 0x5f, 0xff, 0x80, 0x0, 0x80, + + /* U+8328 "茨" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x8, + 0x20, 0xc, 0x7f, 0x89, 0x1, 0x2, 0x22, 0x8, + 0xc8, 0x41, 0x80, 0x83, 0x2, 0x9, 0x4, 0x21, + 0x11, 0x81, 0x84, 0x0, 0x0, + + /* U+832B "茫" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x6, + 0x4, 0x0, 0x4, 0x23, 0xff, 0xb1, 0x0, 0x2, + 0x0, 0x4, 0x0, 0x88, 0x1, 0x10, 0x4, 0x20, + 0x8, 0x7f, 0xc0, 0x0, 0x0, + + /* U+832C "茬" */ + 0x8, 0x21, 0xff, 0xfc, 0x20, 0x80, 0x20, 0x0, + 0x40, 0x1f, 0xff, 0xc2, 0x10, 0x18, 0x20, 0x67, + 0xfd, 0x40, 0x80, 0x81, 0x1, 0x2, 0x2, 0x4, + 0x5, 0xff, 0xc8, 0x0, 0x0, + + /* U+832D "茭" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x49, 0x0, + 0x10, 0x1f, 0xff, 0xc0, 0x0, 0x4, 0x18, 0x30, + 0xd, 0x90, 0x44, 0x11, 0x0, 0x14, 0x0, 0x30, + 0x1, 0x9c, 0x3c, 0x7, 0x80, + + /* U+832F "茯" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x0, + 0x5, 0x2, 0x9, 0x8, 0x10, 0x17, 0xff, 0x60, + 0xc1, 0x41, 0x80, 0x82, 0x81, 0x9, 0x2, 0x31, + 0x4, 0x81, 0x8a, 0x0, 0x80, + + /* U+8331 "茱" */ + 0x8, 0x40, 0x10, 0x83, 0xff, 0xf8, 0x42, 0x1, + 0x10, 0x7, 0xff, 0x8, 0x40, 0x20, 0x80, 0xff, + 0xfe, 0x7, 0x0, 0x15, 0x0, 0x49, 0x3, 0x11, + 0x98, 0x20, 0xc0, 0x40, 0x0, + + /* U+8333 "茳" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x6, + 0x0, 0x2, 0xff, 0x80, 0x10, 0x60, 0x20, 0x60, + 0x40, 0x0, 0x80, 0x41, 0x1, 0x2, 0x4, 0x4, + 0x9, 0xff, 0xc0, 0x0, 0x0, + + /* U+8334 "茴" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x0, + 0x0, 0xf, 0xff, 0x90, 0x1, 0x20, 0x2, 0x47, + 0xc4, 0x90, 0x89, 0x21, 0x12, 0x3e, 0x24, 0x0, + 0x48, 0x0, 0x9f, 0xff, 0x20, 0x2, + + /* U+8335 "茵" */ + 0x8, 0x21, 0xff, 0xfc, 0x20, 0x80, 0x0, 0x7, + 0xff, 0xc8, 0x20, 0x90, 0x41, 0x2f, 0xfa, 0x41, + 0x4, 0x87, 0x9, 0x19, 0x12, 0xc1, 0xa4, 0x0, + 0x4f, 0xff, 0x90, 0x1, 0x0, + + /* U+8336 "茶" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x45, 0x0, + 0x38, 0x0, 0xcc, 0x6, 0x4, 0x70, 0x87, 0x3f, + 0xf8, 0x2, 0x0, 0x4, 0x0, 0xc8, 0x82, 0x10, + 0xd8, 0x20, 0x41, 0xc0, 0x0, + + /* U+8338 "茸" */ + 0x8, 0x20, 0x8, 0x20, 0xff, 0xfe, 0x8, 0x20, + 0x0, 0x0, 0xff, 0xfe, 0x20, 0x10, 0x20, 0x10, + 0x1f, 0xf0, 0x20, 0x10, 0x1f, 0xf0, 0x20, 0x10, + 0x20, 0x17, 0xff, 0xf8, 0x0, 0x10, 0x0, 0x10, + + /* U+8339 "茹" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x1, + 0x0, 0x4, 0x1f, 0xbf, 0xa1, 0x11, 0x42, 0x42, + 0x84, 0x89, 0x8, 0x92, 0x10, 0xc4, 0x21, 0x4f, + 0xcc, 0x50, 0xb0, 0x21, 0x0, + + /* U+833A "茺" */ + 0x8, 0x21, 0xff, 0xfc, 0x20, 0x80, 0x8, 0x0, + 0x10, 0x1f, 0xff, 0xc1, 0x0, 0xc, 0x10, 0x7f, + 0xf0, 0x88, 0x90, 0x11, 0x10, 0x42, 0x1, 0x84, + 0x26, 0x8, 0x70, 0x1f, 0x0, + + /* U+833C "茼" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x0, + 0x0, 0xf, 0xff, 0x90, 0x1, 0x27, 0xf2, 0x40, + 0x4, 0x80, 0x9, 0x3f, 0x92, 0x41, 0x24, 0x82, + 0x49, 0xfc, 0x90, 0x1, 0x20, 0x1c, + + /* U+8340 "荀" */ + 0x4, 0x20, 0x8, 0x43, 0xff, 0xf8, 0x21, 0x1, + 0x0, 0x7, 0xff, 0x98, 0x1, 0x20, 0x2, 0xbf, + 0xc4, 0x40, 0x88, 0xff, 0x11, 0x2, 0x22, 0x4, + 0x47, 0xf8, 0x88, 0x1, 0x0, 0x3c, + + /* U+8343 "荃" */ + 0x10, 0x40, 0x41, 0xf, 0xff, 0xc4, 0x50, 0x7, + 0x80, 0x21, 0x3, 0x3, 0xb7, 0xf9, 0x2, 0x0, + 0x8, 0x3, 0xff, 0x0, 0x80, 0x2, 0x3, 0xff, + 0xf0, + + /* U+8346 "荆" */ + 0x1, 0x2, 0x42, 0x7, 0xff, 0x49, 0x8, 0x90, + 0x1, 0x3f, 0xfa, 0x48, 0x44, 0x90, 0x89, 0x21, + 0x13, 0xff, 0xa4, 0x84, 0x49, 0x8, 0x12, 0x10, + 0x28, 0x20, 0x60, 0x47, 0x80, + + /* U+8347 "荇" */ + 0x8, 0x40, 0x10, 0x83, 0xff, 0xf8, 0x2, 0x0, + 0x80, 0x2, 0x3f, 0x98, 0x0, 0x4, 0x0, 0x13, + 0xfe, 0x40, 0x23, 0x80, 0x41, 0x0, 0x82, 0x1, + 0x4, 0x2, 0x8, 0x3c, 0x0, + + /* U+8349 "草" */ + 0x4, 0x20, 0x8, 0x43, 0xff, 0xf8, 0x21, 0x0, + 0x0, 0x7, 0xff, 0x88, 0x1, 0x1f, 0xfe, 0x20, + 0x4, 0x40, 0x8, 0xff, 0xf0, 0x8, 0xf, 0xff, + 0xe0, 0x20, 0x0, 0x40, 0x0, 0x80, + + /* U+834F "荏" */ + 0x4, 0x20, 0x8, 0x41, 0xff, 0xf8, 0x21, 0x0, + 0x80, 0x2, 0x7f, 0xc4, 0x10, 0x10, 0x20, 0x60, + 0x41, 0x4f, 0xfc, 0x81, 0x1, 0x2, 0x2, 0x4, + 0x4, 0xff, 0xc8, 0x0, 0x0, + + /* U+8350 "荐" */ + 0x8, 0x21, 0xff, 0xfc, 0x20, 0x80, 0x20, 0xf, + 0xff, 0xe1, 0x0, 0x4, 0x0, 0x1b, 0xfe, 0x60, + 0x19, 0xc0, 0x42, 0xbf, 0xf9, 0x2, 0x2, 0x4, + 0x4, 0x8, 0x8, 0xf0, 0x0, + + /* U+8351 "荑" */ + 0x8, 0x21, 0xff, 0xfc, 0x20, 0x87, 0xff, 0xf0, + 0x10, 0x7, 0xff, 0x0, 0x42, 0x1f, 0xfc, 0x21, + 0x0, 0xff, 0xfc, 0x4, 0x8, 0x14, 0x10, 0x45, + 0xc3, 0x6, 0x38, 0x3, 0x80, + + /* U+8352 "荒" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x0, + 0x10, 0x1f, 0xff, 0x88, 0x0, 0x10, 0x0, 0x20, + 0x0, 0x7f, 0xf0, 0x0, 0x0, 0x89, 0x1, 0x12, + 0x22, 0x24, 0x48, 0x48, 0xe0, 0x8e, + + /* U+8354 "荔" */ + 0x8, 0x21, 0xff, 0xfc, 0x20, 0x80, 0x10, 0xf, + 0xff, 0x80, 0x81, 0x2, 0x2, 0x38, 0x78, 0x20, + 0x21, 0xff, 0xf8, 0x89, 0x11, 0x12, 0x22, 0x44, + 0x48, 0x90, 0xa7, 0x4e, 0x0, + + /* U+835A "荚" */ + 0x8, 0x20, 0x10, 0x41, 0xff, 0xf0, 0x49, 0x0, + 0x10, 0xf, 0xff, 0x80, 0x40, 0x8, 0x88, 0x1, + 0x1, 0xff, 0xfc, 0xa, 0x0, 0x14, 0x0, 0xc6, + 0x6, 0x3, 0xb0, 0x0, 0x80, + + /* U+835B "荛" */ + 0x4, 0x41, 0xff, 0xfc, 0x11, 0x0, 0x80, 0x0, + 0xff, 0xce, 0x82, 0x0, 0xf8, 0x83, 0xe1, 0xf8, + 0x3c, 0x0, 0x3, 0xff, 0xf8, 0x22, 0x0, 0x84, + 0x23, 0x8, 0x78, 0x1f, 0x0, + + /* U+835C "荜" */ + 0x4, 0x21, 0xff, 0xfc, 0x10, 0x80, 0x0, 0x2, + 0x8, 0x47, 0xd3, 0x8, 0x38, 0x10, 0x41, 0x2e, + 0x84, 0xe0, 0xf8, 0x4, 0x7, 0xff, 0xf0, 0x10, + 0x0, 0x20, 0x0, 0x40, 0x0, + + /* U+835E "荞" */ + 0x8, 0x41, 0xff, 0xfc, 0x21, 0x0, 0x0, 0x7, + 0xff, 0xc0, 0x20, 0x0, 0x80, 0x7f, 0xff, 0x4, + 0x40, 0x30, 0x61, 0x91, 0x34, 0x42, 0x10, 0x84, + 0x2, 0x8, 0x18, 0x10, 0x0, + + /* U+835F "荟" */ + 0x10, 0x21, 0xff, 0xf8, 0x44, 0x80, 0x18, 0x0, + 0x48, 0x3, 0xc, 0x8, 0x6, 0x6f, 0xf2, 0x0, + 0x1, 0xff, 0xf8, 0x10, 0x0, 0x42, 0x1, 0x3, + 0xf, 0xff, 0x0, 0x2, 0x0, + + /* U+8360 "荠" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x0, + 0x10, 0x1f, 0xff, 0x84, 0xc, 0x6, 0x30, 0x7, + 0xc0, 0x7, 0x0, 0x71, 0xff, 0x0, 0x30, 0x82, + 0x1, 0x4, 0x4, 0x8, 0x30, 0x10, + + /* U+8361 "荡" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x0, + 0x0, 0x4, 0xff, 0x4, 0xc, 0x40, 0x60, 0x43, + 0x0, 0xf, 0xfc, 0x4, 0x89, 0x9, 0x12, 0x22, + 0x49, 0x88, 0x94, 0x21, 0x40, 0x9e, + + /* U+8363 "荣" */ + 0x8, 0x40, 0x10, 0x83, 0xff, 0xf0, 0x42, 0x0, + 0x0, 0x1f, 0xff, 0xa0, 0x1, 0x40, 0x2, 0x1, + 0x1, 0xff, 0xf8, 0xe, 0x0, 0x2a, 0x1, 0x92, + 0x4, 0x23, 0x30, 0x41, 0x0, 0x80, + + /* U+8364 "荤" */ + 0x4, 0x41, 0xff, 0xfc, 0x10, 0x3, 0xff, 0xf4, + 0x20, 0x20, 0x80, 0x1f, 0xff, 0x4, 0x0, 0x10, + 0x80, 0x7f, 0xf8, 0x2, 0x0, 0x4, 0xf, 0xff, + 0xe0, 0x10, 0x0, 0x20, 0x0, + + /* U+8365 "荥" */ + 0x8, 0x40, 0x21, 0xf, 0xff, 0xc2, 0x10, 0x0, + 0x3, 0xff, 0xf8, 0x0, 0x60, 0x41, 0x1, 0x3, + 0xe6, 0x40, 0x9e, 0x4, 0x50, 0x31, 0x21, 0x4, + 0x68, 0x10, 0x43, 0x80, + + /* U+8366 "荦" */ + 0x0, 0x20, 0x10, 0x41, 0xff, 0xf8, 0x41, 0x0, + 0x0, 0x1f, 0xff, 0xe0, 0x0, 0xc0, 0x1, 0x11, + 0x0, 0x3f, 0xf0, 0x84, 0x0, 0x8, 0xf, 0xff, + 0xe0, 0x20, 0x0, 0x40, 0x0, 0x80, + + /* U+8367 "荧" */ + 0x4, 0x20, 0x4, 0x20, 0xff, 0xfe, 0x4, 0x20, + 0x0, 0x0, 0x7f, 0xfe, 0x80, 0x2, 0x1, 0x2, + 0x21, 0x80, 0x11, 0x88, 0x19, 0x90, 0x2, 0x40, + 0x6, 0x20, 0x18, 0x18, 0xf0, 0x6, + + /* U+8368 "荨" */ + 0x4, 0x21, 0xff, 0xfc, 0x10, 0x80, 0x0, 0x3, + 0xff, 0x80, 0x1, 0x7, 0xfe, 0x0, 0x4, 0x3f, + 0xf8, 0x0, 0x3, 0xff, 0xf8, 0x0, 0x80, 0xc1, + 0x0, 0x82, 0x0, 0x3c, 0x0, + + /* U+8369 "荩" */ + 0x4, 0x20, 0x8, 0x43, 0xff, 0xf8, 0x21, 0x0, + 0x0, 0x3, 0xff, 0x4, 0x2, 0x8, 0x4, 0x1f, + 0xf8, 0x20, 0x80, 0x80, 0x81, 0x38, 0x84, 0x8, + 0xd0, 0x0, 0x3, 0xe0, 0x0, 0x30, + + /* U+836A "荪" */ + 0x4, 0x20, 0x8, 0x43, 0xff, 0xf8, 0x21, 0x0, + 0x0, 0x1f, 0x84, 0x1, 0x8, 0x4, 0x54, 0x11, + 0x24, 0x3a, 0x4b, 0xc4, 0x90, 0x91, 0x11, 0x22, + 0x22, 0x4, 0x3c, 0x38, 0x0, + + /* U+836B "荫" */ + 0x4, 0x20, 0x8, 0x43, 0xff, 0xf8, 0x21, 0x0, + 0x0, 0xf, 0x9f, 0x91, 0x41, 0x24, 0x82, 0x48, + 0xfc, 0x92, 0x9, 0x14, 0x12, 0x2f, 0xe4, 0x50, + 0x4b, 0x20, 0x90, 0x81, 0x23, 0x1e, + + /* U+836C "荬" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x0, + 0x0, 0x1f, 0xff, 0x80, 0x1, 0xe, 0x42, 0x0, + 0x80, 0x71, 0x0, 0x2, 0x7, 0xff, 0xf0, 0x10, + 0x0, 0x5c, 0x7, 0x6, 0x70, 0x2, + + /* U+836D "荭" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x1, + 0x0, 0x4, 0x3f, 0x93, 0x8, 0x7c, 0x10, 0x10, + 0x20, 0x40, 0x41, 0xf0, 0x80, 0x1, 0x0, 0x2, + 0x1f, 0x7f, 0xc0, + + /* U+836E "荮" */ + 0x8, 0x41, 0xff, 0xfc, 0x21, 0x1, 0x0, 0x2, + 0x0, 0x88, 0x1, 0x12, 0xff, 0xf8, 0x4, 0x21, + 0x8, 0x82, 0x13, 0xe2, 0x20, 0x0, 0x40, 0x0, + 0x9f, 0x83, 0x0, 0x3c, 0x0, + + /* U+836F "药" */ + 0x8, 0x41, 0xff, 0xfc, 0x21, 0x0, 0x84, 0x2, + 0x10, 0x8, 0x3f, 0x92, 0x41, 0x79, 0x2, 0x21, + 0x84, 0x81, 0x8b, 0xe1, 0x10, 0x0, 0x20, 0x0, + 0x5f, 0x81, 0x80, 0x3e, 0x0, + + /* U+8377 "荷" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x1, + 0x0, 0x3, 0xff, 0xc8, 0x2, 0x10, 0x4, 0x67, + 0xc9, 0x48, 0x90, 0x91, 0x21, 0x22, 0x42, 0x7c, + 0x84, 0x1, 0x8, 0x2, 0x10, 0x3c, + + /* U+8378 "荸" */ + 0x4, 0x21, 0xff, 0xfc, 0x10, 0x80, 0x8, 0x7, + 0xff, 0xc0, 0x20, 0x3f, 0xff, 0xc0, 0x1, 0x9f, + 0xf2, 0x0, 0xc0, 0x2, 0x7, 0xff, 0xf0, 0x10, + 0x0, 0x20, 0x1, 0xc0, 0x0, + + /* U+837B "荻" */ + 0x4, 0x21, 0xff, 0xfc, 0x10, 0x80, 0x0, 0x2, + 0x82, 0x3, 0x4, 0xc, 0x89, 0xa4, 0xb2, 0x89, + 0x68, 0x30, 0xc0, 0xa1, 0x42, 0x44, 0x88, 0x88, + 0x81, 0x20, 0xdd, 0x80, 0x0, + + /* U+837C "荼" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x4d, 0x0, + 0x3c, 0x1, 0x86, 0xd, 0xfb, 0x60, 0x81, 0x1, + 0x0, 0xff, 0xf8, 0x4, 0x0, 0x49, 0x3, 0x11, + 0x98, 0x20, 0xc1, 0xc0, 0x0, + + /* U+837D "荽" */ + 0x8, 0x21, 0xff, 0xfc, 0x20, 0x80, 0x0, 0x7, + 0xff, 0xc0, 0x1, 0x8, 0x42, 0x8, 0x88, 0x4, + 0x1, 0xff, 0xfc, 0x60, 0x80, 0xc3, 0x0, 0x7c, + 0x0, 0xfc, 0x3e, 0x7, 0x0, + + /* U+8385 "莅" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x0, + 0x4, 0x2, 0x8, 0xb, 0xff, 0xb0, 0x0, 0xe2, + 0x8, 0x44, 0x10, 0x84, 0x41, 0x9, 0x2, 0x2, + 0x5, 0xff, 0xc8, 0x0, 0x0, + + /* U+8386 "莆" */ + 0x8, 0x41, 0xff, 0xfc, 0x21, 0x0, 0x8, 0x8f, + 0xff, 0xe0, 0x20, 0x1f, 0xfe, 0x20, 0x84, 0x41, + 0x8, 0xff, 0xf1, 0x4, 0x23, 0xff, 0xc4, 0x10, + 0x88, 0x21, 0x10, 0x5e, 0x0, + + /* U+8389 "莉" */ + 0x8, 0x41, 0xff, 0xfc, 0x21, 0x0, 0x0, 0xf, + 0xf0, 0x42, 0x8, 0x84, 0x11, 0x7f, 0xa2, 0x10, + 0x44, 0x60, 0x88, 0xf1, 0x12, 0x9a, 0x29, 0x0, + 0x42, 0x0, 0x84, 0xe, 0x0, + + /* U+838E "莎" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x2, + 0x0, 0x6, 0x8, 0x2, 0x52, 0x41, 0x22, 0x62, + 0x42, 0x48, 0x80, 0x41, 0x20, 0x80, 0x82, 0x6, + 0x8, 0x30, 0x33, 0x80, 0x0, + + /* U+8392 "莒" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x0, + 0x0, 0x3, 0xfe, 0x8, 0x4, 0x10, 0x8, 0x1f, + 0xf0, 0x0, 0x0, 0xff, 0xe2, 0x0, 0x44, 0x0, + 0x88, 0x1, 0xf, 0xfe, 0x20, 0x4, + + /* U+8393 "莓" */ + 0x4, 0x41, 0xff, 0xfc, 0x11, 0x0, 0x80, 0x3, + 0xff, 0xcc, 0x0, 0x37, 0xfe, 0x9, 0x4, 0x21, + 0x9, 0xff, 0xfc, 0x88, 0x21, 0x8, 0x43, 0xff, + 0xe0, 0x2, 0x0, 0x3c, 0x0, + + /* U+8398 "莘" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x0, + 0x10, 0xf, 0xff, 0x80, 0x0, 0x4, 0x10, 0x0, + 0x41, 0xff, 0xfc, 0x4, 0x0, 0x8, 0x7, 0xff, + 0xc0, 0x20, 0x0, 0x40, 0x0, 0x80, + + /* U+839B "莛" */ + 0x4, 0x20, 0x8, 0x43, 0xff, 0xf8, 0x21, 0x0, + 0x0, 0x1f, 0xbf, 0x82, 0x8, 0x8, 0x10, 0x60, + 0x20, 0xfb, 0xf8, 0x10, 0x82, 0x41, 0x2, 0xbf, + 0xe2, 0x0, 0x1b, 0xff, 0x80, 0x0, + + /* U+839C "莜" */ + 0x4, 0x20, 0x8, 0x43, 0xff, 0xf8, 0x21, 0x1, + 0x4, 0x2, 0x8, 0x9, 0x2f, 0xb2, 0xc4, 0xe5, + 0x89, 0x48, 0x90, 0x91, 0x41, 0x21, 0x2, 0x45, + 0x4, 0x31, 0x88, 0x80, 0x80, + + /* U+839E "莞" */ + 0x8, 0x21, 0xff, 0xfc, 0x20, 0x80, 0x8, 0x7, + 0xff, 0xc8, 0x0, 0x90, 0x1, 0xf, 0xf8, 0x0, + 0x1, 0xff, 0xfc, 0x11, 0x0, 0x22, 0x0, 0x84, + 0x22, 0x8, 0x78, 0x1f, 0x0, + + /* U+83A0 "莠" */ + 0x8, 0x43, 0xff, 0xf0, 0x84, 0x1f, 0xfe, 0x2, + 0x0, 0x8, 0xf, 0xff, 0xc1, 0xa0, 0x38, 0x73, + 0xff, 0x30, 0x84, 0x2, 0x1e, 0x10, 0x8, 0xc0, + 0x2e, 0xf, 0x0, + + /* U+83A8 "莨" */ + 0x8, 0x21, 0xff, 0xfc, 0x20, 0x80, 0x8, 0x3, + 0xff, 0x84, 0x1, 0xf, 0xfe, 0x10, 0x4, 0x20, + 0x8, 0x7f, 0xf0, 0x88, 0x11, 0x8, 0xc2, 0xe, + 0x4, 0xcc, 0x1e, 0x7, 0x80, + + /* U+83A9 "莩" */ + 0x8, 0x21, 0xff, 0xfc, 0x20, 0x80, 0x0, 0x7, + 0xff, 0xc4, 0x21, 0x4, 0x4, 0x1f, 0xfc, 0x0, + 0x70, 0x3, 0x3, 0xff, 0xf8, 0x8, 0x0, 0x10, + 0x0, 0x20, 0x3, 0xc0, 0x0, + + /* U+83AA "莪" */ + 0x4, 0x21, 0xff, 0xfc, 0x10, 0x80, 0x0, 0x1, + 0xe4, 0x8d, 0x8, 0x82, 0x10, 0x7f, 0xff, 0x8, + 0x40, 0x12, 0x91, 0xf9, 0x40, 0x41, 0x10, 0x9e, + 0x21, 0x42, 0x5c, 0x3, 0x0, + + /* U+83AB "莫" */ + 0x8, 0x21, 0xff, 0xfc, 0x20, 0x80, 0x0, 0x3, + 0xff, 0x84, 0x1, 0xf, 0xfe, 0x10, 0x4, 0x3f, + 0xf8, 0x0, 0x0, 0x4, 0x7, 0xff, 0xf0, 0x28, + 0x1, 0x8c, 0x3c, 0x3, 0x80, + + /* U+83B0 "莰" */ + 0x8, 0x40, 0x10, 0x83, 0xff, 0xf8, 0x42, 0x2, + 0x10, 0x4, 0x20, 0x8, 0x7f, 0x7d, 0x2, 0x22, + 0x48, 0x48, 0x90, 0x81, 0x1, 0x5, 0x3, 0x93, + 0x18, 0x63, 0x3, 0x1, 0x80, + + /* U+83B1 "莱" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x0, + 0x82, 0xf, 0xff, 0x80, 0x40, 0x8, 0x88, 0x9, + 0x20, 0x2, 0x3, 0xff, 0xf8, 0x1c, 0x0, 0x54, + 0x3, 0x27, 0x18, 0x41, 0xc0, 0x80, + + /* U+83B2 "莲" */ + 0x4, 0x21, 0xff, 0xfc, 0x10, 0x82, 0x4, 0x2, + 0x7f, 0xc2, 0x20, 0x0, 0x88, 0x71, 0x10, 0x21, + 0xf8, 0x40, 0x40, 0x9f, 0xf9, 0x1, 0x3, 0x2, + 0x9, 0x0, 0x21, 0xff, 0x80, + + /* U+83B3 "莳" */ + 0x8, 0x41, 0xff, 0xfc, 0x21, 0x0, 0x0, 0x0, + 0x0, 0x8f, 0x81, 0x11, 0x7f, 0xa2, 0x4, 0x7d, + 0x8, 0x89, 0x11, 0x12, 0x22, 0x20, 0x47, 0xc0, + 0x88, 0x81, 0x0, 0x1c, 0x0, + + /* U+83B4 "莴" */ + 0x8, 0x43, 0xff, 0xf0, 0x84, 0x0, 0x0, 0x3f, + 0xf1, 0x0, 0x43, 0xff, 0x0, 0x80, 0x2, 0x1, + 0xff, 0xe8, 0x20, 0xa1, 0x62, 0x98, 0x6a, 0x0, + 0x28, 0x7, 0x80, + + /* U+83B6 "莶" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x49, 0x0, + 0x28, 0x1, 0x8c, 0xc, 0x6, 0x67, 0xf3, 0x0, + 0x0, 0x42, 0x10, 0x44, 0x40, 0x88, 0x80, 0x2, + 0x1f, 0xff, 0xc0, + + /* U+83B7 "获" */ + 0x8, 0x21, 0xff, 0xfc, 0x20, 0x80, 0x0, 0x4, + 0x85, 0x6, 0x9, 0x8c, 0x10, 0x29, 0xff, 0x90, + 0x40, 0x30, 0xc0, 0xa2, 0x86, 0x44, 0x88, 0x91, + 0x3, 0x61, 0x9d, 0x81, 0x80, + + /* U+83B8 "莸" */ + 0x4, 0x20, 0x7f, 0xff, 0x4, 0x20, 0x0, 0x0, + 0x64, 0x48, 0x2c, 0x44, 0x10, 0x40, 0x2b, 0xff, + 0x48, 0x50, 0x18, 0x50, 0x18, 0x50, 0x28, 0x90, + 0x48, 0x91, 0x9, 0x12, 0x3a, 0x1e, + + /* U+83B9 "莹" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x0, + 0x0, 0x1f, 0xff, 0xe0, 0x0, 0xc0, 0x1, 0x1f, + 0xf0, 0x2, 0x0, 0x4, 0x1, 0xff, 0xc0, 0x10, + 0x0, 0x26, 0x0, 0x44, 0x7f, 0xff, + + /* U+83BA "莺" */ + 0x4, 0x21, 0xff, 0xfc, 0x10, 0x83, 0xff, 0xf8, + 0x0, 0x20, 0x40, 0x47, 0xfe, 0x9, 0x8, 0x11, + 0x70, 0x20, 0x0, 0x7f, 0xf0, 0x0, 0x2f, 0xff, + 0x40, 0x0, 0x80, 0x1f, 0x0, + + /* U+83BC "莼" */ + 0x8, 0x41, 0xff, 0xfc, 0x21, 0x1, 0x0, 0x2, + 0x2, 0x8, 0x7f, 0xd2, 0x8, 0x79, 0x12, 0x12, + 0x24, 0x44, 0x49, 0xe7, 0xf0, 0x1, 0x0, 0x2, + 0x3f, 0x84, 0x40, 0xf, 0x0, + + /* U+83BD "莽" */ + 0x8, 0x21, 0xff, 0xfc, 0x20, 0x80, 0x8, 0x80, + 0x10, 0x9f, 0xff, 0xc0, 0xa0, 0x6, 0x30, 0x30, + 0x18, 0x90, 0x4f, 0xff, 0xf8, 0x41, 0x0, 0x82, + 0x2, 0x4, 0x18, 0x8, 0x0, + + /* U+83C0 "菀" */ + 0x8, 0x21, 0xff, 0xfc, 0x20, 0x80, 0x8, 0xf, + 0xff, 0xf0, 0x0, 0x64, 0x0, 0x9f, 0x7e, 0x22, + 0x84, 0xc5, 0xa, 0x52, 0xe0, 0x24, 0x10, 0x88, + 0x26, 0x10, 0x58, 0x1f, 0x0, + + /* U+83C1 "菁" */ + 0x8, 0x21, 0xff, 0xfc, 0x0, 0x83, 0xff, 0xf0, + 0x10, 0x7, 0xff, 0x80, 0x40, 0x7f, 0xff, 0x0, + 0x0, 0x7f, 0xf0, 0xff, 0xe1, 0x0, 0x43, 0xff, + 0x84, 0x1, 0x8, 0x1e, 0x0, + + /* U+83C5 "菅" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x0, + 0x10, 0xf, 0xff, 0xa0, 0x1, 0x1f, 0xfc, 0x20, + 0x8, 0x40, 0x10, 0xff, 0xe1, 0x0, 0x3, 0xff, + 0x84, 0x1, 0xf, 0xfe, 0x10, 0x4, + + /* U+83C7 "菇" */ + 0x8, 0x41, 0xff, 0xfc, 0x20, 0x0, 0x80, 0x2, + 0x2, 0x4, 0x4, 0x3f, 0x7f, 0x92, 0x10, 0x44, + 0x20, 0x8b, 0xf9, 0xa4, 0x10, 0x88, 0x23, 0x90, + 0x48, 0xbf, 0xa0, 0x41, 0x0, + + /* U+83CA "菊" */ + 0x4, 0x21, 0xff, 0xfc, 0x10, 0x80, 0x80, 0x3, + 0xff, 0xc8, 0x0, 0xb0, 0x81, 0x9, 0x22, 0x2, + 0x4, 0xff, 0xe8, 0x18, 0x10, 0x5c, 0x23, 0x26, + 0x48, 0x40, 0x80, 0x1e, 0x0, + + /* U+83CC "菌" */ + 0x8, 0x21, 0xff, 0xfc, 0x20, 0x80, 0x0, 0x7, + 0xff, 0xc8, 0x0, 0x93, 0xf9, 0x20, 0x82, 0x5f, + 0xf4, 0x86, 0x9, 0x17, 0x12, 0x49, 0x25, 0x10, + 0x4f, 0xff, 0x90, 0x1, 0x0, + + /* U+83CF "菏" */ + 0x8, 0x41, 0xff, 0xfc, 0x21, 0x0, 0x0, 0x4, + 0x0, 0x5, 0xff, 0xc4, 0x2, 0x63, 0xe4, 0x4, + 0x48, 0x8, 0x90, 0x91, 0x21, 0x3e, 0x44, 0x0, + 0x88, 0x1, 0x20, 0x1e, 0x0, + + /* U+83D4 "菔" */ + 0x8, 0x41, 0xff, 0xfc, 0x21, 0x0, 0x0, 0x7, + 0xbf, 0xd1, 0x40, 0xa2, 0x92, 0x3d, 0x18, 0x8b, + 0xfd, 0x15, 0x9, 0xe9, 0x24, 0x52, 0x48, 0xa3, + 0x11, 0x4f, 0xac, 0xa1, 0x80, + + /* U+83D6 "菖" */ + 0x8, 0x21, 0xff, 0xfc, 0x20, 0x81, 0xff, 0xc2, + 0x0, 0x87, 0xff, 0x8, 0x2, 0x1f, 0xfc, 0x0, + 0x0, 0xff, 0xf9, 0x0, 0x13, 0xff, 0xe4, 0x0, + 0x4f, 0xff, 0x90, 0x1, 0x0, + + /* U+83D8 "菘" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x1, + 0x9, 0x2, 0x12, 0x3f, 0x42, 0x8, 0x86, 0x32, + 0x46, 0xb0, 0x81, 0x52, 0x44, 0x84, 0x81, 0x10, + 0x82, 0x7f, 0x84, 0x1, 0x0, + + /* U+83DC "菜" */ + 0x8, 0x21, 0xff, 0xfc, 0x20, 0x80, 0x0, 0x7, + 0xff, 0x80, 0x0, 0x8, 0x44, 0x8, 0x10, 0x1, + 0x1, 0xff, 0xfc, 0xe, 0x0, 0x2a, 0x1, 0x93, + 0x1c, 0x21, 0xc0, 0x40, 0x0, + + /* U+83DD "菝" */ + 0x8, 0x40, 0x10, 0x83, 0xff, 0xf8, 0x42, 0x2, + 0x11, 0x84, 0x20, 0x3e, 0xff, 0x10, 0x80, 0x23, + 0xf8, 0x66, 0x13, 0x94, 0x41, 0x25, 0x2, 0x86, + 0x4, 0x33, 0x39, 0x81, 0x80, + + /* U+83DF "菟" */ + 0x8, 0x21, 0xff, 0xfc, 0x0, 0x80, 0x40, 0x1, + 0xfe, 0x6, 0x8, 0x1f, 0xfe, 0x10, 0x84, 0x21, + 0x8, 0x7f, 0xf0, 0xa, 0x0, 0x15, 0x90, 0x48, + 0xa3, 0x10, 0x78, 0x3f, 0x0, + + /* U+83E0 "菠" */ + 0x4, 0x21, 0xff, 0xfc, 0x10, 0x80, 0x0, 0x2, + 0x2, 0x2, 0xff, 0xc1, 0x8, 0xe2, 0x11, 0x27, + 0xfc, 0xa, 0x8, 0x92, 0x22, 0x22, 0xc4, 0x82, + 0x9, 0x1b, 0x5, 0xc1, 0x80, + + /* U+83E1 "菡" */ + 0x8, 0x41, 0xff, 0xfc, 0x21, 0x0, 0x0, 0x1, + 0xff, 0x0, 0xc, 0x10, 0x61, 0x2e, 0x9a, 0x41, + 0x44, 0x8a, 0x89, 0xe4, 0xd2, 0x38, 0x24, 0x0, + 0x4f, 0xff, 0x80, 0x1, 0x0, + + /* U+83E5 "菥" */ + 0x8, 0x21, 0xff, 0xfc, 0x20, 0x80, 0x0, 0x1, + 0x0, 0x42, 0x2f, 0x3f, 0x40, 0x8, 0x80, 0x31, + 0xfe, 0xb2, 0x21, 0x54, 0x44, 0x90, 0x81, 0x21, + 0x2, 0x82, 0x5, 0x4, 0x0, + + /* U+83E9 "菩" */ + 0x8, 0x21, 0xff, 0xfc, 0x20, 0x80, 0x8, 0x7, + 0xff, 0xc1, 0x6, 0x1, 0x10, 0x7f, 0xff, 0x0, + 0x0, 0x0, 0x0, 0xff, 0xe1, 0x0, 0x42, 0x0, + 0x87, 0xff, 0x8, 0x2, 0x0, + + /* U+83EA "菪" */ + 0x8, 0x21, 0xff, 0xfc, 0x20, 0x80, 0x8, 0xf, + 0xff, 0xf0, 0x0, 0x40, 0x0, 0x7f, 0xff, 0xc, + 0x0, 0x30, 0x0, 0xff, 0xe7, 0x0, 0x42, 0x0, + 0x83, 0xff, 0x8, 0x2, 0x0, + + /* U+83F0 "菰" */ + 0x4, 0x20, 0x8, 0x43, 0xff, 0xf8, 0x21, 0x0, + 0x0, 0x1f, 0x7f, 0x82, 0x94, 0x9, 0x28, 0x12, + 0x48, 0x3c, 0x91, 0xc9, 0x24, 0x92, 0x41, 0x26, + 0x42, 0x52, 0x85, 0x3c, 0xb2, 0x41, + + /* U+83F1 "菱" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x49, 0x0, + 0x10, 0xf, 0xff, 0x80, 0x40, 0x7f, 0xff, 0x10, + 0x0, 0x48, 0x31, 0x1f, 0x9c, 0x41, 0x13, 0x64, + 0x4, 0x30, 0x3, 0x9e, 0x78, 0x2, + + /* U+83F2 "菲" */ + 0x8, 0x41, 0xff, 0xfc, 0x21, 0x0, 0x0, 0x0, + 0x0, 0x1f, 0xf, 0x82, 0x20, 0x4, 0x40, 0xf8, + 0x7c, 0x11, 0x0, 0x22, 0x0, 0x44, 0xf, 0x87, + 0xc1, 0x10, 0x2, 0x20, 0x0, + + /* U+83F8 "菸" */ + 0x4, 0x40, 0x8, 0x83, 0xff, 0xf8, 0x22, 0x0, + 0x82, 0x1f, 0xc6, 0x8, 0x12, 0x10, 0x22, 0x3f, + 0xa2, 0x44, 0x60, 0x88, 0x21, 0x10, 0x4, 0x4c, + 0x8, 0x84, 0x27, 0x6, 0x0, + + /* U+83F9 "菹" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x4, + 0x0, 0x4, 0xff, 0x5, 0x2, 0x42, 0x4, 0x67, + 0xf8, 0x8, 0x10, 0x90, 0x21, 0x3f, 0xc4, 0x40, + 0x88, 0x81, 0x27, 0xff, 0x80, + + /* U+83FD "菽" */ + 0x8, 0x41, 0xff, 0xfc, 0x21, 0x0, 0x0, 0x1, + 0x0, 0x3, 0xbf, 0x84, 0x21, 0x8, 0x44, 0xfe, + 0x48, 0x20, 0x91, 0x50, 0xc4, 0x91, 0x89, 0x5, + 0x82, 0x31, 0x9c, 0xc0, 0x80, + + /* U+8401 "萁" */ + 0x8, 0x41, 0xff, 0xfc, 0x21, 0x0, 0x80, 0x8f, + 0xff, 0xe2, 0x2, 0x7, 0xfc, 0x8, 0x8, 0x10, + 0x10, 0x3f, 0xe0, 0x40, 0x47, 0xff, 0xf0, 0x0, + 0x3, 0x7, 0x38, 0x1, 0x80, + + /* U+8403 "萃" */ + 0x8, 0x21, 0xff, 0xfc, 0x20, 0x80, 0x8, 0x7, + 0xff, 0xc0, 0x0, 0x2, 0x8, 0xc, 0x38, 0x24, + 0x48, 0x80, 0x8, 0x4, 0x7, 0xff, 0xf0, 0x10, + 0x0, 0x20, 0x0, 0x40, 0x0, + + /* U+8404 "萄" */ + 0x4, 0x41, 0xff, 0xfc, 0x11, 0x0, 0x80, 0x3, + 0xff, 0xc9, 0x0, 0xa7, 0xf9, 0x11, 0x2, 0x22, + 0x5, 0xff, 0xe8, 0x88, 0x91, 0x11, 0x23, 0xfe, + 0x40, 0x1, 0x80, 0x3e, 0x0, + + /* U+8406 "萆" */ + 0x8, 0x21, 0xff, 0xfc, 0x20, 0x80, 0x10, 0x3, + 0xff, 0x88, 0x21, 0xf, 0xfe, 0x20, 0x84, 0x3f, + 0xf8, 0x8, 0x0, 0x62, 0x7, 0xff, 0xf0, 0x8, + 0x0, 0x10, 0x0, 0x20, 0x0, + + /* U+840B "萋" */ + 0x8, 0x21, 0xff, 0xfc, 0x4, 0x83, 0xff, 0xe0, + 0x10, 0x7, 0xff, 0x0, 0x42, 0x7f, 0xff, 0x1, + 0x8, 0x3d, 0xe3, 0xff, 0xf8, 0x41, 0x0, 0x74, + 0x0, 0xfe, 0x1e, 0x3, 0x0, + + /* U+840C "萌" */ + 0x8, 0x0, 0x10, 0x83, 0xff, 0xf8, 0x42, 0x0, + 0x0, 0xf, 0x3f, 0x92, 0x41, 0x24, 0x82, 0x49, + 0xfc, 0xf2, 0x9, 0x24, 0x12, 0x4f, 0xe7, 0xa0, + 0x48, 0x40, 0x80, 0x81, 0x2, 0x3c, + + /* U+840D "萍" */ + 0x8, 0x21, 0xff, 0xfc, 0x20, 0x82, 0x0, 0x3, + 0x7f, 0xc2, 0x8, 0x21, 0x11, 0x21, 0x24, 0x22, + 0x58, 0x0, 0x80, 0xbf, 0xf9, 0x2, 0x4, 0x4, + 0x8, 0x8, 0x20, 0x10, 0x0, + + /* U+840E "萎" */ + 0x8, 0x41, 0xff, 0xfc, 0x0, 0x3, 0xff, 0xe0, + 0x10, 0x0, 0x20, 0x3f, 0xff, 0x82, 0xa0, 0xf9, + 0x2e, 0x8, 0x3, 0xff, 0xf8, 0x41, 0x1, 0xc4, + 0x0, 0x78, 0x1f, 0x8f, 0x0, + + /* U+840F "萏" */ + 0x4, 0x20, 0x8, 0x43, 0xff, 0xf8, 0x20, 0x0, + 0xff, 0x2, 0x2, 0x8, 0x8, 0x3f, 0x3e, 0x20, + 0x4, 0x40, 0x8, 0xfd, 0xf1, 0x0, 0x22, 0x0, + 0x47, 0xff, 0x88, 0x1, 0x0, + + /* U+8411 "萑" */ + 0x4, 0x20, 0x7f, 0xff, 0x4, 0x20, 0x8, 0x80, + 0x8, 0x40, 0x1f, 0xfe, 0x30, 0x80, 0x70, 0x80, + 0x5f, 0xfc, 0x10, 0x80, 0x1f, 0xfc, 0x10, 0x80, + 0x10, 0x80, 0x1f, 0xfe, 0x10, 0x0, + + /* U+8418 "萘" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x0, + 0x20, 0x1f, 0xff, 0xc1, 0x10, 0x4, 0x10, 0x37, + 0xd9, 0x80, 0xc, 0x0, 0x1, 0xff, 0xc0, 0x10, + 0x3, 0x26, 0x18, 0x43, 0x3, 0x80, + + /* U+841C "萜" */ + 0x4, 0x21, 0xff, 0xfc, 0x10, 0x80, 0x0, 0x1, + 0x2, 0x2, 0x4, 0x1f, 0xf, 0xca, 0x10, 0x94, + 0x21, 0x29, 0xfa, 0x54, 0x14, 0xa8, 0x21, 0x50, + 0x42, 0x1f, 0x84, 0x41, 0x0, + + /* U+841D "萝" */ + 0x8, 0x21, 0xff, 0xfc, 0x20, 0x80, 0x0, 0x7, + 0xff, 0xc8, 0x88, 0x9f, 0xff, 0x2, 0x0, 0x4, + 0x0, 0x1f, 0xf0, 0xc0, 0x63, 0x61, 0x80, 0xc, + 0x0, 0x60, 0x1f, 0x0, 0x0, + + /* U+8424 "萤" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x0, + 0x0, 0xf, 0xff, 0xa0, 0x41, 0x0, 0x80, 0x3f, + 0xf8, 0x42, 0x10, 0x84, 0x21, 0xff, 0xc2, 0x10, + 0x0, 0x21, 0x3f, 0xff, 0x0, 0x1, + + /* U+8425 "营" */ + 0x4, 0x41, 0xff, 0xfc, 0x11, 0x7, 0xff, 0xf8, + 0x0, 0x30, 0x0, 0x47, 0xfc, 0x8, 0x8, 0x1f, + 0xf0, 0x0, 0x0, 0xff, 0xe1, 0x0, 0x42, 0x0, + 0x87, 0xff, 0x8, 0x2, 0x0, + + /* U+8426 "萦" */ + 0x4, 0x21, 0xff, 0xfc, 0x10, 0x83, 0xff, 0xf8, + 0x10, 0x20, 0xc0, 0x42, 0x18, 0xf, 0xc0, 0x2, + 0x20, 0x38, 0x20, 0xff, 0xe0, 0x8, 0x21, 0x93, + 0xc, 0x21, 0xa1, 0xc0, 0x0, + + /* U+8427 "萧" */ + 0x4, 0x21, 0xff, 0xfc, 0x10, 0x80, 0x8, 0x3, + 0xff, 0x80, 0x21, 0x1f, 0xff, 0x80, 0x84, 0x3f, + 0xf8, 0x2, 0x0, 0x84, 0x21, 0x29, 0x42, 0x52, + 0x8d, 0x21, 0x30, 0x42, 0x0, + + /* U+8428 "萨" */ + 0x8, 0x41, 0xff, 0xfc, 0x21, 0x0, 0x2, 0x7, + 0x82, 0x11, 0x7f, 0xa4, 0x42, 0x50, 0x48, 0x93, + 0xfd, 0x14, 0x2, 0x28, 0x5, 0x90, 0x8, 0x40, + 0x10, 0x80, 0x22, 0x0, 0x0, + + /* U+8431 "萱" */ + 0x8, 0x21, 0xff, 0xfc, 0x20, 0x80, 0x8, 0xb, + 0xef, 0xc3, 0xfe, 0x0, 0x0, 0x1f, 0xfc, 0x20, + 0x8, 0x7f, 0xf0, 0x80, 0x21, 0xff, 0xc0, 0x0, + 0x1f, 0xff, 0xc0, + + /* U+8438 "萸" */ + 0x4, 0x20, 0x8, 0x43, 0xff, 0xf8, 0x21, 0x0, + 0x10, 0x7, 0xaf, 0x8, 0x42, 0x10, 0x84, 0x3d, + 0x78, 0x42, 0x10, 0x84, 0x21, 0xff, 0xc0, 0x18, + 0x0, 0x48, 0x3, 0xc, 0x38, 0x7, + + /* U+843C "萼" */ + 0x8, 0x41, 0xff, 0xfc, 0x0, 0x3, 0xf7, 0xe4, + 0x28, 0x4f, 0xdf, 0x80, 0x0, 0x1f, 0xfc, 0x0, + 0x1, 0xff, 0xfc, 0x40, 0x0, 0xff, 0x80, 0x1, + 0x0, 0x2, 0x0, 0x7c, 0x0, + + /* U+843D "落" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x4, + 0x10, 0x6, 0x7f, 0x1, 0x82, 0x44, 0x88, 0x60, + 0xe0, 0x6, 0x78, 0x30, 0x9, 0x3f, 0xc4, 0x40, + 0x88, 0x81, 0x11, 0xfe, 0x42, 0x4, + + /* U+8446 "葆" */ + 0x4, 0x20, 0x8, 0x41, 0xff, 0xf8, 0x21, 0x0, + 0x0, 0x1, 0x7f, 0x84, 0x81, 0x11, 0x2, 0x23, + 0xfc, 0xc0, 0x82, 0x81, 0x1, 0x3f, 0xf2, 0x6, + 0x4, 0x3b, 0x9, 0x91, 0x90, 0x20, + + /* U+8451 "葑" */ + 0x8, 0x41, 0xff, 0xfc, 0x21, 0x0, 0x0, 0x1, + 0x0, 0x9f, 0xc1, 0x4, 0x3f, 0x88, 0x4, 0xfe, + 0x8, 0x21, 0x93, 0xf9, 0x20, 0x80, 0x41, 0x0, + 0x8f, 0xc1, 0x20, 0x1e, 0x0, + + /* U+8457 "著" */ + 0x8, 0x21, 0xff, 0xfc, 0x20, 0x80, 0x10, 0x3, + 0xfe, 0x80, 0x44, 0x3f, 0xff, 0x80, 0x80, 0x6, + 0x0, 0x7f, 0xf3, 0x40, 0x20, 0xff, 0xc1, 0x0, + 0x83, 0xff, 0x4, 0x2, 0x0, + + /* U+8459 "葙" */ + 0x0, 0x0, 0x10, 0x83, 0xff, 0xf8, 0x42, 0x0, + 0x84, 0x4, 0x3f, 0x88, 0x41, 0x3c, 0x82, 0x21, + 0xfc, 0xa2, 0x9, 0xa4, 0x13, 0xf, 0xea, 0x10, + 0x44, 0x20, 0x88, 0x7f, 0x10, 0x82, + + /* U+845A "葚" */ + 0x4, 0x21, 0xff, 0xfc, 0x10, 0x80, 0x80, 0x47, + 0xff, 0xe2, 0x1, 0x7, 0xfe, 0x8, 0x4, 0x1f, + 0xf8, 0x20, 0x13, 0xff, 0xf9, 0x0, 0x2, 0x63, + 0x84, 0x0, 0xf, 0xff, 0x80, + + /* U+845B "葛" */ + 0x4, 0x21, 0xff, 0xfc, 0x10, 0x81, 0xff, 0xc2, + 0x0, 0x87, 0xff, 0x8, 0x2, 0xf, 0xf8, 0x3f, + 0xfc, 0x82, 0xa, 0x9b, 0x11, 0x41, 0x23, 0xff, + 0x40, 0x0, 0x80, 0x1e, 0x0, + + /* U+845C "葜" */ + 0x4, 0x21, 0xff, 0xfc, 0x10, 0x80, 0x21, 0x0, + 0x80, 0xf, 0xcf, 0x9f, 0x89, 0x4, 0x12, 0xff, + 0x44, 0x11, 0x30, 0x4, 0x7, 0xff, 0xf0, 0x18, + 0x0, 0xcc, 0x3e, 0x7, 0x80, + + /* U+8461 "葡" */ + 0x8, 0x41, 0xff, 0xfc, 0x21, 0x1, 0x0, 0x3, + 0xff, 0xc8, 0x90, 0xbf, 0xf9, 0x2, 0x2, 0x7f, + 0xc4, 0x88, 0x89, 0xff, 0x13, 0xfe, 0x24, 0x44, + 0x48, 0x88, 0x91, 0x36, 0x0, + + /* U+8463 "董" */ + 0x8, 0x41, 0xff, 0xfc, 0x0, 0x3, 0xff, 0xe0, + 0x10, 0x1f, 0xff, 0xc0, 0x40, 0x1f, 0xfc, 0x3f, + 0xf8, 0x42, 0x10, 0xff, 0xe0, 0x8, 0x3, 0xff, + 0x9f, 0xff, 0xc0, + + /* U+8469 "葩" */ + 0x8, 0x21, 0xff, 0xfc, 0x20, 0x80, 0x0, 0x2, + 0x1f, 0xcf, 0x24, 0x92, 0x49, 0x24, 0x92, 0x49, + 0x24, 0xf3, 0xf9, 0x24, 0x2, 0x48, 0x17, 0x90, + 0x29, 0x20, 0x52, 0x3f, 0x0, + + /* U+846B "葫" */ + 0x8, 0x41, 0xff, 0xfc, 0x21, 0x0, 0x80, 0x1, + 0xf, 0xc2, 0x10, 0x9f, 0x21, 0x8, 0x7e, 0x10, + 0x84, 0xd9, 0x9, 0x13, 0xf2, 0x24, 0x27, 0xd0, + 0x48, 0x20, 0x80, 0x87, 0x0, + + /* U+846C "葬" */ + 0x4, 0x21, 0xff, 0xfc, 0x10, 0x83, 0xff, 0xf1, + 0x4, 0x7, 0xe9, 0x98, 0x9c, 0x8b, 0x21, 0x8, + 0x7c, 0xc0, 0x0, 0x20, 0x87, 0xff, 0xf0, 0x82, + 0x2, 0x4, 0x18, 0x8, 0x0, + + /* U+846D "葭" */ + 0x4, 0x21, 0xff, 0xfc, 0x10, 0x80, 0x0, 0x7, + 0xef, 0xc8, 0x40, 0x90, 0x81, 0x3f, 0x7e, 0x40, + 0x0, 0xfd, 0xf9, 0x2, 0x13, 0xf2, 0x44, 0x3, + 0x8, 0x19, 0x90, 0xc0, 0x80, + + /* U+8471 "葱" */ + 0x4, 0x41, 0xff, 0xfc, 0x11, 0x0, 0x80, 0x3, + 0xff, 0x88, 0x49, 0x35, 0x12, 0x47, 0xc4, 0x11, + 0x48, 0xc4, 0x10, 0x31, 0xc2, 0x50, 0x4, 0x99, + 0x51, 0x4, 0x43, 0xf8, 0x0, + + /* U+8473 "葳" */ + 0x4, 0x20, 0x4, 0x20, 0x7f, 0xff, 0x0, 0x20, + 0x3f, 0xfe, 0x20, 0x20, 0x2f, 0xa4, 0x20, 0x24, + 0x24, 0x24, 0x2f, 0xa8, 0x28, 0xa8, 0x29, 0x10, + 0x47, 0x12, 0x4c, 0x2a, 0xd0, 0x46, + + /* U+8475 "葵" */ + 0x8, 0x21, 0xff, 0xfc, 0x20, 0x80, 0x4, 0x47, + 0xe9, 0x0, 0x8c, 0x9b, 0xe, 0xf, 0xe8, 0x61, + 0xd, 0x82, 0x4, 0xff, 0xe0, 0x10, 0x0, 0x3c, + 0x1, 0x86, 0x1c, 0x2, 0x0, + + /* U+8476 "葶" */ + 0x4, 0x21, 0xff, 0xfc, 0x10, 0x80, 0x8, 0x7, + 0xef, 0xc3, 0xff, 0x4, 0x2, 0xf, 0xfc, 0x0, + 0x1, 0xff, 0xfe, 0x0, 0xc, 0xff, 0xd0, 0x8, + 0x0, 0x10, 0x1, 0xc0, 0x0, + + /* U+8478 "葸" */ + 0x4, 0x41, 0xff, 0xfc, 0x11, 0x0, 0x0, 0x3, + 0xff, 0x84, 0x21, 0xf, 0xfe, 0x10, 0x84, 0x21, + 0x8, 0x7f, 0xf0, 0x0, 0x2, 0x88, 0x25, 0x9, + 0x51, 0x4, 0x43, 0xf8, 0x0, + + /* U+847A "葺" */ + 0x8, 0x41, 0xff, 0xfc, 0x21, 0x1, 0xff, 0x82, + 0x1, 0x7, 0xfe, 0x0, 0x0, 0x7f, 0xfe, 0x20, + 0x10, 0x7f, 0xe0, 0xff, 0xc1, 0x0, 0x82, 0x1, + 0xdf, 0xfe, 0x0, 0x4, 0x0, + + /* U+8482 "蒂" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x0, + 0x10, 0xf, 0xff, 0x84, 0x8, 0x4, 0x10, 0xff, + 0xff, 0x2, 0x6, 0xff, 0xe9, 0x8, 0x42, 0x10, + 0x84, 0x21, 0x8, 0x4c, 0x0, 0x80, + + /* U+8487 "蒇" */ + 0x4, 0x20, 0x4, 0x20, 0x7f, 0xff, 0x0, 0x20, + 0x3f, 0xfe, 0x20, 0x20, 0x2f, 0xa4, 0x28, 0xa4, + 0x2a, 0xa4, 0x2a, 0xa8, 0x2a, 0x98, 0x22, 0x11, + 0x45, 0x92, 0x48, 0xaa, 0x50, 0x46, + + /* U+8488 "蒈" */ + 0x4, 0x21, 0xff, 0xfc, 0x10, 0x82, 0x4, 0x27, + 0xeb, 0x88, 0x18, 0x53, 0xa0, 0xb8, 0x3e, 0x2, + 0x0, 0x7f, 0xf0, 0x80, 0x21, 0xff, 0xc2, 0x0, + 0x87, 0xff, 0x8, 0x2, 0x0, + + /* U+8489 "蒉" */ + 0x8, 0x21, 0xff, 0xfc, 0x20, 0x81, 0xff, 0xc2, + 0x10, 0x87, 0xff, 0x0, 0x40, 0x7f, 0xff, 0x0, + 0x0, 0x7f, 0xf0, 0x80, 0x21, 0x8, 0x42, 0x18, + 0x80, 0xcf, 0x1e, 0x1, 0x0, + + /* U+848B "蒋" */ + 0x8, 0x40, 0x10, 0x83, 0xff, 0xf8, 0x42, 0x0, + 0x0, 0x2, 0x3f, 0xa4, 0xc2, 0x4a, 0x48, 0x50, + 0x40, 0xa7, 0x20, 0xd0, 0x43, 0xbf, 0xf9, 0x21, + 0x2, 0x22, 0x4, 0x4, 0x8, 0x38, + + /* U+848C "蒌" */ + 0x8, 0x41, 0xff, 0xfc, 0x0, 0x1, 0x88, 0xc0, + 0x92, 0x1f, 0xff, 0xc1, 0x50, 0x3c, 0x9e, 0xc2, + 0x0, 0x8, 0x3, 0xff, 0xf8, 0x41, 0x1, 0xc4, + 0x0, 0x7c, 0x3f, 0x7, 0x0, + + /* U+848E "蒎" */ + 0x8, 0x21, 0xff, 0xfc, 0x20, 0x82, 0x0, 0x23, + 0x7f, 0x80, 0x80, 0x21, 0x3, 0x32, 0x78, 0x4, + 0x90, 0x9, 0x24, 0x92, 0x31, 0x24, 0x44, 0x48, + 0x89, 0x1c, 0x90, 0x20, 0x0, + + /* U+8497 "蒗" */ + 0x8, 0x21, 0xff, 0xfc, 0x20, 0x82, 0x2, 0x2, + 0x7f, 0xc0, 0x80, 0xa1, 0xff, 0x32, 0x2, 0x4, + 0x4, 0xf, 0xf8, 0x92, 0x9, 0x22, 0x64, 0x43, + 0x8, 0xb2, 0x23, 0x83, 0x80, + + /* U+8499 "蒙" */ + 0x8, 0x41, 0xff, 0xfc, 0x21, 0x0, 0x0, 0xf, + 0xff, 0xc3, 0xfe, 0x0, 0x0, 0x3f, 0xfe, 0x6, + 0x0, 0x62, 0x71, 0x1b, 0x81, 0xcd, 0x4, 0x69, + 0x87, 0x10, 0xf1, 0xc0, 0x0, + + /* U+849C "蒜" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x0, + 0x0, 0xf, 0xdf, 0x80, 0x0, 0x0, 0x0, 0xfe, + 0xfe, 0x20, 0x20, 0xd0, 0x62, 0x94, 0xa5, 0x29, + 0x52, 0x22, 0x64, 0x4, 0x18, 0x30, + + /* U+84A1 "蒡" */ + 0x4, 0x21, 0xff, 0xfc, 0x14, 0x80, 0x8, 0x7, + 0xff, 0xc1, 0x6, 0x1f, 0xff, 0xc0, 0x81, 0x0, + 0x81, 0xff, 0xfc, 0x20, 0x0, 0x7f, 0xc0, 0x80, + 0x86, 0x1, 0x38, 0x3c, 0x0, + + /* U+84AF "蒯" */ + 0x22, 0x4, 0x88, 0x1f, 0xfe, 0x48, 0x89, 0x77, + 0xa6, 0x52, 0x99, 0x4a, 0x5d, 0xe9, 0x94, 0xa6, + 0x52, 0x9f, 0x7a, 0x65, 0x21, 0x94, 0x86, 0x62, + 0x1e, 0xbb, 0x80, + + /* U+84B2 "蒲" */ + 0x8, 0x41, 0xff, 0xfc, 0x20, 0x0, 0x0, 0x86, + 0x8, 0x80, 0xfe, 0xa3, 0xff, 0x24, 0x42, 0x8, + 0x84, 0x1f, 0xf8, 0xa2, 0x12, 0x7f, 0xe4, 0x88, + 0x49, 0x10, 0xa2, 0x27, 0x0, + + /* U+84B4 "蒴" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x0, + 0x20, 0x4, 0x8f, 0x81, 0x11, 0x3d, 0xa2, 0x48, + 0x7c, 0x94, 0x89, 0x29, 0x13, 0xf3, 0xe1, 0x4, + 0x42, 0x10, 0x88, 0x21, 0x20, 0x8e, + + /* U+84B8 "蒸" */ + 0x4, 0x21, 0xff, 0xfc, 0x10, 0x81, 0xff, 0x80, + 0x3, 0x0, 0x8, 0x3f, 0x31, 0x4, 0x5c, 0x10, + 0x99, 0xc6, 0xc, 0x0, 0x1, 0xff, 0xc0, 0x0, + 0x8, 0x88, 0xb0, 0x91, 0x0, + + /* U+84B9 "蒹" */ + 0x4, 0x41, 0xff, 0xfc, 0x11, 0x0, 0x61, 0xf, + 0xff, 0xe0, 0x88, 0xf, 0xfe, 0x2, 0x24, 0xff, + 0xfe, 0x8, 0x91, 0xff, 0xe0, 0x62, 0x1, 0x47, + 0xc, 0x89, 0xe1, 0x10, 0x0, + + /* U+84BA "蒺" */ + 0x4, 0x10, 0x4, 0x10, 0x7f, 0xff, 0x4, 0x10, + 0x0, 0x80, 0x1f, 0xfe, 0x10, 0x0, 0x52, 0x0, + 0x53, 0xfc, 0x14, 0x40, 0x30, 0x40, 0x5f, 0xfe, + 0x20, 0x40, 0x20, 0xa0, 0x23, 0x18, 0x4c, 0x7, + + /* U+84BD "蒽" */ + 0x4, 0x21, 0xff, 0xfc, 0x10, 0x81, 0xff, 0xc2, + 0x0, 0x85, 0xfd, 0x8, 0x42, 0x11, 0x74, 0x2c, + 0x8, 0x7f, 0xf0, 0x4, 0x2, 0x4c, 0x24, 0x89, + 0x51, 0x2, 0x43, 0xf8, 0x0, + + /* U+84BF "蒿" */ + 0x8, 0x21, 0xff, 0xfc, 0x24, 0x80, 0x8, 0x7, + 0xef, 0xc3, 0xff, 0x4, 0x2, 0xf, 0xfc, 0x0, + 0x0, 0xff, 0xf9, 0x0, 0x12, 0x7f, 0x24, 0xfe, + 0x48, 0x0, 0x90, 0x7, 0x0, + + /* U+84C1 "蓁" */ + 0x8, 0x21, 0xff, 0xfc, 0x24, 0x83, 0xff, 0xe0, + 0x20, 0x7, 0xff, 0x1, 0x0, 0x7f, 0xff, 0x18, + 0x20, 0x7f, 0xa1, 0x4, 0x3d, 0xff, 0xc0, 0x30, + 0x3, 0xbe, 0x8, 0x42, 0x0, + + /* U+84C4 "蓄" */ + 0x8, 0x21, 0xff, 0xfc, 0x24, 0x87, 0xff, 0xf0, + 0xc1, 0x7, 0xfc, 0x0, 0x88, 0x1e, 0xc, 0xf, + 0xf4, 0x7f, 0xf0, 0x84, 0x21, 0xff, 0xc2, 0x10, + 0x87, 0xff, 0x8, 0x2, 0x0, + + /* U+84C9 "蓉" */ + 0x8, 0x21, 0xff, 0xfc, 0x20, 0x80, 0x8, 0xf, + 0xff, 0xf0, 0x0, 0x43, 0x1c, 0x39, 0xc6, 0x4, + 0x40, 0x70, 0x73, 0x7f, 0xf9, 0x0, 0x42, 0x0, + 0x83, 0xff, 0x8, 0x2, 0x0, + + /* U+84CA "蓊" */ + 0x4, 0x21, 0xff, 0xfc, 0x10, 0x80, 0x32, 0x1, + 0x93, 0xc, 0x49, 0xe3, 0x8, 0x1, 0xe8, 0x7e, + 0xfc, 0x4, 0x8, 0xc9, 0x90, 0x10, 0x27, 0xaf, + 0x50, 0x40, 0x87, 0x8e, 0x0, + + /* U+84CD "蓍" */ + 0x8, 0x41, 0xff, 0xfc, 0x28, 0x3, 0xfe, 0x40, + 0x23, 0x1f, 0xff, 0xc1, 0x80, 0x3f, 0xfa, 0x98, + 0xc, 0xf, 0xe0, 0xff, 0xe1, 0x0, 0x43, 0xff, + 0x84, 0x1, 0xf, 0xfe, 0x0, + + /* U+84D0 "蓐" */ + 0x4, 0x20, 0x7f, 0xff, 0x4, 0x20, 0x3f, 0xfe, + 0x20, 0x0, 0x27, 0xf8, 0x3f, 0xfe, 0x28, 0x84, + 0x28, 0x78, 0x27, 0x9e, 0x20, 0x0, 0x3f, 0xff, + 0x44, 0x10, 0x42, 0x10, 0x80, 0x70, + + /* U+84D1 "蓑" */ + 0x8, 0x41, 0xff, 0xfc, 0x29, 0x7, 0xff, 0xf0, + 0x0, 0x7, 0xfe, 0x8, 0x4, 0x7f, 0xff, 0x20, + 0x10, 0x7f, 0xe0, 0x1c, 0x20, 0x44, 0x87, 0x6, + 0x12, 0x67, 0x87, 0x0, 0x80, + + /* U+84D3 "蓓" */ + 0x8, 0x21, 0xff, 0xfc, 0x20, 0x80, 0x80, 0x1, + 0x7f, 0xc4, 0x40, 0x8, 0x84, 0x30, 0x90, 0xef, + 0xff, 0x40, 0x0, 0x9f, 0xf1, 0x20, 0x22, 0x40, + 0x44, 0xff, 0x89, 0x1, 0x0, + + /* U+84D6 "蓖" */ + 0x8, 0x41, 0xff, 0xfc, 0x21, 0x0, 0x10, 0x7, + 0xff, 0x89, 0x1, 0x11, 0xf2, 0x26, 0x34, 0x40, + 0x8, 0x7f, 0xe1, 0xfc, 0x62, 0x7, 0x4, 0x10, + 0x4b, 0x90, 0x98, 0x3f, 0x0, + + /* U+84DD "蓝" */ + 0x8, 0x41, 0xff, 0xfc, 0x21, 0x0, 0x0, 0x0, + 0x48, 0x8, 0x9f, 0x91, 0x40, 0x23, 0x98, 0x0, + 0x0, 0x7f, 0xf0, 0x92, 0x21, 0x24, 0x42, 0x48, + 0x84, 0x91, 0x3f, 0xff, 0x80, + + /* U+84DF "蓟" */ + 0x8, 0x41, 0xff, 0xfc, 0x21, 0x1, 0x0, 0x3, + 0xe0, 0x48, 0x88, 0xbf, 0xd1, 0x24, 0xa2, 0x7f, + 0x44, 0x92, 0x89, 0x25, 0x13, 0xf8, 0x20, 0x0, + 0x5f, 0xe0, 0x80, 0xe, 0x0, + + /* U+84E0 "蓠" */ + 0x8, 0x21, 0xff, 0xfc, 0x24, 0x87, 0xff, 0xf0, + 0x2, 0x5, 0xd9, 0x9, 0xf2, 0x14, 0x14, 0x3f, + 0xf8, 0x4, 0x1, 0xff, 0xf2, 0x22, 0x24, 0xfc, + 0x48, 0x4, 0x90, 0xf, 0x0, + + /* U+84E3 "蓣" */ + 0x8, 0x21, 0xff, 0xfc, 0x20, 0x80, 0x0, 0xf, + 0xdf, 0xe0, 0x84, 0xe, 0x8, 0x8, 0xfe, 0xfd, + 0x24, 0x2a, 0x48, 0x54, 0x90, 0xa9, 0x21, 0x5, + 0x82, 0x10, 0x98, 0xc0, 0x80, + + /* U+84E5 "蓥" */ + 0x8, 0x41, 0xff, 0xfc, 0x21, 0x0, 0x0, 0xf, + 0xff, 0xf0, 0x50, 0x43, 0x1c, 0x7f, 0xf7, 0x81, + 0x0, 0x2, 0x1, 0xff, 0xf0, 0x88, 0x80, 0x92, + 0x1f, 0xff, 0xc0, + + /* U+84E6 "蓦" */ + 0x0, 0x41, 0xff, 0xfc, 0x1, 0x1, 0xfd, 0xc3, + 0xff, 0x84, 0x1, 0xf, 0xfe, 0x2, 0x0, 0xfb, + 0xfe, 0x7f, 0xf3, 0x40, 0x98, 0x7e, 0xc7, 0xfe, + 0x80, 0x1, 0x0, 0x1e, 0x0, + + /* U+84EC "蓬" */ + 0x8, 0x21, 0xff, 0xfc, 0x24, 0x82, 0x1f, 0xc2, + 0x70, 0x84, 0x1e, 0x1, 0xef, 0x74, 0x20, 0x27, + 0xfc, 0x40, 0x80, 0x8f, 0xe1, 0x7f, 0xe6, 0x4, + 0xb, 0x0, 0x23, 0xff, 0x80, + + /* U+84F0 "蓰" */ + 0x8, 0x41, 0xff, 0xfc, 0x20, 0x0, 0x82, 0x2, + 0x24, 0x8, 0x4f, 0x20, 0x90, 0x17, 0xff, 0x40, + 0x0, 0x84, 0x83, 0x11, 0xf2, 0x32, 0x4, 0x54, + 0x9, 0x1f, 0xd2, 0x0, 0x0, + + /* U+84FC "蓼" */ + 0x4, 0x21, 0xff, 0xfc, 0x10, 0x81, 0xf7, 0xc1, + 0x24, 0x81, 0x41, 0x7, 0x9e, 0x10, 0xc0, 0xe, + 0x60, 0xe3, 0x3c, 0x39, 0x80, 0xc, 0x1, 0xe7, + 0x0, 0x70, 0xf, 0x0, 0x0, + + /* U+84FF "蓿" */ + 0x8, 0x21, 0xff, 0xfc, 0x20, 0x80, 0x8, 0x7, + 0xff, 0xc8, 0x0, 0x85, 0xff, 0x10, 0x40, 0x27, + 0xf8, 0xc8, 0x12, 0x90, 0x25, 0x3f, 0xc2, 0x40, + 0x84, 0xff, 0x9, 0x2, 0x0, + + /* U+850C "蔌" */ + 0x8, 0x41, 0xff, 0xfc, 0x21, 0x0, 0x0, 0x1, + 0x0, 0xf, 0xcf, 0x84, 0x21, 0x3f, 0x42, 0x52, + 0x28, 0xfc, 0x40, 0x41, 0x81, 0xe2, 0x85, 0x24, + 0x92, 0x10, 0x84, 0xc0, 0x0, + + /* U+8511 "蔑" */ + 0x8, 0x41, 0xff, 0xfc, 0x21, 0x3, 0xff, 0xe4, + 0x44, 0x48, 0x88, 0x9f, 0xff, 0x0, 0x10, 0x1, + 0x10, 0xff, 0xf9, 0x4, 0x23, 0x84, 0x84, 0xce, + 0x70, 0xec, 0xa6, 0x7, 0x0, + + /* U+8513 "蔓" */ + 0x8, 0x41, 0xff, 0xfc, 0x0, 0x1, 0xff, 0xc3, + 0xff, 0x84, 0x1, 0x7, 0xfc, 0x3f, 0xfe, 0x44, + 0x44, 0xff, 0xf8, 0x0, 0x3, 0xff, 0xc0, 0x83, + 0x0, 0xd8, 0x1f, 0x5f, 0x0, + + /* U+8517 "蔗" */ + 0x4, 0x20, 0xff, 0xfc, 0x10, 0x80, 0x4, 0x3, + 0xff, 0xe4, 0x0, 0x9, 0x4, 0x1f, 0xff, 0x24, + 0x10, 0x48, 0x20, 0x8f, 0xc2, 0x0, 0x4, 0x92, + 0x49, 0x24, 0xa4, 0x48, 0x80, + + /* U+851A "蔚" */ + 0x8, 0x21, 0xff, 0xfc, 0x20, 0x83, 0xf8, 0x4, + 0x10, 0x88, 0x21, 0x1f, 0xdf, 0xa0, 0x4, 0x5f, + 0x8, 0x80, 0x51, 0xfe, 0xa2, 0x20, 0x49, 0x50, + 0x94, 0x91, 0x27, 0xe, 0x0, + + /* U+851F "蔟" */ + 0x8, 0x21, 0xff, 0xfc, 0x20, 0x81, 0x8, 0x2, + 0x1f, 0xdf, 0xc0, 0x10, 0x20, 0x20, 0xfe, 0x7a, + 0x40, 0x94, 0x81, 0x3f, 0xfa, 0x43, 0x8, 0x8b, + 0x12, 0x33, 0xad, 0x81, 0x80, + + /* U+8521 "蔡" */ + 0x4, 0x21, 0xff, 0xfc, 0x10, 0x80, 0x44, 0x1, + 0xf7, 0xc4, 0x28, 0xb6, 0x8a, 0x52, 0x8, 0x1b, + 0xc8, 0xc0, 0xf, 0x7f, 0xe0, 0x4, 0x1, 0x8b, + 0xc, 0x11, 0x81, 0xc0, 0x0, + + /* U+852B "蔫" */ + 0x8, 0x21, 0xff, 0xfc, 0x0, 0x83, 0xff, 0xf2, + 0x1f, 0xc4, 0x20, 0x3f, 0xff, 0x80, 0x0, 0x3f, + 0xfc, 0x40, 0x0, 0xff, 0xf8, 0x0, 0x14, 0x92, + 0x31, 0x0, 0x80, 0x1f, 0x0, + + /* U+852C "蔬" */ + 0x8, 0x41, 0xff, 0xfc, 0x21, 0x0, 0x2, 0x7, + 0xbf, 0xe2, 0x10, 0x4, 0x44, 0x29, 0x7c, 0x59, + 0x4, 0xa0, 0x1, 0x49, 0x22, 0x92, 0x55, 0xa4, + 0xbc, 0x49, 0x43, 0x13, 0x80, + + /* U+8537 "蔷" */ + 0x8, 0x21, 0xff, 0xfc, 0x4, 0x3, 0xff, 0xe2, + 0x10, 0x83, 0x26, 0x3f, 0xff, 0x80, 0x0, 0x7f, + 0xf8, 0x80, 0x11, 0x1f, 0x22, 0x42, 0x44, 0x78, + 0x8f, 0xff, 0x10, 0x2, 0x0, + + /* U+8538 "蔸" */ + 0x4, 0x21, 0xff, 0xfc, 0x10, 0x80, 0x8, 0x7, + 0xfe, 0xc8, 0x84, 0x91, 0xf9, 0x22, 0x12, 0x44, + 0x24, 0xff, 0xd9, 0x80, 0x0, 0x2, 0x0, 0x44, + 0x20, 0x88, 0x5e, 0xf, 0x0, + + /* U+8539 "蔹" */ + 0x8, 0x21, 0xff, 0xfc, 0x20, 0x80, 0x41, 0x1, + 0xc4, 0x6, 0x4f, 0xd8, 0x51, 0x5f, 0x64, 0x0, + 0x48, 0x92, 0x91, 0x28, 0xe1, 0x50, 0x80, 0x43, + 0x9f, 0xe8, 0x80, 0x20, 0x80, + + /* U+853A "蔺" */ + 0x4, 0x21, 0xff, 0xfc, 0x10, 0x81, 0x1f, 0xe1, + 0x0, 0x40, 0x90, 0x93, 0xff, 0x24, 0x42, 0x5f, + 0xf4, 0x91, 0x9, 0x3f, 0xd2, 0x44, 0x24, 0xff, + 0xc9, 0x0, 0x90, 0x7, 0x0, + + /* U+853B "蔻" */ + 0x8, 0x41, 0xff, 0xfc, 0x21, 0x0, 0x8, 0x7, + 0xff, 0xd0, 0x8, 0x8f, 0x1f, 0x0, 0x20, 0x1, + 0xf9, 0xf8, 0x20, 0xa6, 0x41, 0x43, 0x2, 0x99, + 0x29, 0x0, 0x61, 0xff, 0x0, + + /* U+853C "蔼" */ + 0x4, 0x21, 0xff, 0xfc, 0x10, 0x83, 0x1f, 0xe3, + 0x20, 0x40, 0x7f, 0x80, 0x81, 0x10, 0xfc, 0x23, + 0xfc, 0x58, 0x48, 0x8b, 0xd1, 0x10, 0x23, 0xbf, + 0xc4, 0x0, 0x80, 0x7, 0x0, + + /* U+853D "蔽" */ + 0x4, 0x21, 0xff, 0xfc, 0x10, 0x80, 0x0, 0x4, + 0x91, 0x5, 0x43, 0xdf, 0xc9, 0x24, 0xb2, 0x4d, + 0x64, 0xd6, 0x29, 0xac, 0x63, 0x58, 0x44, 0x91, + 0x89, 0x24, 0xd2, 0xd0, 0x80, + + /* U+8543 "蕃" */ + 0x8, 0x41, 0xff, 0xfc, 0x0, 0x1, 0xff, 0xc1, + 0x11, 0x1, 0x24, 0x3f, 0xff, 0x82, 0xa0, 0x39, + 0x31, 0xff, 0xfc, 0x84, 0x21, 0xff, 0xc2, 0x10, + 0x87, 0xff, 0x8, 0x2, 0x0, + + /* U+8548 "蕈" */ + 0x8, 0x20, 0xff, 0xfb, 0xff, 0xf8, 0x22, 0x7, + 0xff, 0xc8, 0x88, 0x9f, 0xff, 0x0, 0x0, 0x3f, + 0xf8, 0x40, 0x10, 0xff, 0xe1, 0xff, 0xc0, 0x10, + 0x1f, 0xff, 0xc0, 0x40, 0x0, + + /* U+8549 "蕉" */ + 0x8, 0x41, 0xff, 0xfc, 0x21, 0x0, 0x8c, 0x3, + 0xff, 0xc4, 0x20, 0x1f, 0xfe, 0x50, 0x80, 0x3f, + 0xf8, 0x42, 0x0, 0xff, 0xf1, 0x0, 0x0, 0x8, + 0x88, 0x88, 0xa1, 0x11, 0x0, + + /* U+854A "蕊" */ + 0x8, 0x21, 0xff, 0xfc, 0x20, 0x80, 0x8, 0x2, + 0x88, 0x49, 0x2, 0x72, 0x4, 0x83, 0xf0, 0x10, + 0x10, 0x10, 0x10, 0xa8, 0xa5, 0x15, 0x1a, 0x5a, + 0x74, 0x94, 0x8f, 0x6, 0x0, + + /* U+8556 "蕖" */ + 0x8, 0x43, 0xff, 0xf0, 0x84, 0x19, 0xff, 0x14, + 0x3, 0x1f, 0xe6, 0x40, 0x83, 0xfe, 0x34, 0x1, + 0x1f, 0xff, 0xff, 0xc1, 0xa0, 0xa, 0x61, 0xc8, + 0x78, 0x20, 0x0, + + /* U+8559 "蕙" */ + 0x8, 0x41, 0xff, 0xfc, 0x24, 0x7, 0xff, 0xe0, + 0x10, 0x7, 0xff, 0x10, 0x42, 0x3f, 0xfc, 0x3f, + 0xf8, 0x2, 0x23, 0xff, 0xe0, 0x0, 0x24, 0x90, + 0x89, 0x4, 0xa3, 0xf8, 0x0, + + /* U+855E "蕞" */ + 0x4, 0x21, 0xff, 0xfc, 0x10, 0x81, 0xde, 0xc3, + 0xff, 0x84, 0x1, 0xf, 0xfe, 0x0, 0x0, 0x7f, + 0xfc, 0x7d, 0xf8, 0x89, 0x11, 0xf1, 0x42, 0x21, + 0x1f, 0xe5, 0x0, 0xb1, 0x80, + + /* U+8564 "蕤" */ + 0x4, 0x20, 0xff, 0xfc, 0x10, 0x80, 0x0, 0x3, + 0xe5, 0x2, 0xf, 0xca, 0x94, 0x27, 0x48, 0x1a, + 0x3e, 0xcc, 0x20, 0x34, 0x40, 0xaf, 0xf2, 0x48, + 0x8, 0x8e, 0xe, 0x7, 0x80, + + /* U+8568 "蕨" */ + 0x4, 0x20, 0x7f, 0xff, 0x4, 0x20, 0x3f, 0xfe, + 0x28, 0x90, 0x25, 0x20, 0x3f, 0xbe, 0x22, 0x22, + 0x22, 0xc4, 0x32, 0x90, 0x32, 0x98, 0x4f, 0x98, + 0x44, 0x24, 0x44, 0x26, 0x48, 0xc3, + + /* U+8572 "蕲" */ + 0x8, 0x41, 0xff, 0xfc, 0x21, 0x1, 0x30, 0x2, + 0x47, 0xcf, 0xc8, 0x14, 0x90, 0x3f, 0x20, 0x52, + 0x7e, 0xfc, 0x90, 0x42, 0x20, 0x84, 0x4f, 0xf8, + 0x82, 0x11, 0x4, 0x42, 0x0, + + /* U+8574 "蕴" */ + 0x8, 0x41, 0xff, 0xfc, 0x0, 0x1, 0x1f, 0xc2, + 0x20, 0x89, 0x41, 0x24, 0xfe, 0x71, 0x4, 0x21, + 0xf0, 0x87, 0xfb, 0xd2, 0x90, 0x25, 0x2f, 0xca, + 0x41, 0xff, 0xc0, + + /* U+8579 "蕹" */ + 0x8, 0x41, 0xff, 0xfc, 0x21, 0x0, 0x8, 0xf, + 0xff, 0xc2, 0x14, 0x8, 0x48, 0x25, 0xfe, 0xf6, + 0x20, 0x4f, 0xf9, 0x28, 0x81, 0xdf, 0xe1, 0x22, + 0x4, 0x7f, 0xb0, 0x80, 0x0, + + /* U+857A "蕺" */ + 0x4, 0x21, 0xff, 0xfc, 0x10, 0x83, 0xf9, 0x4, + 0x12, 0x4f, 0xe4, 0x40, 0x8, 0x7f, 0xff, 0x41, + 0x20, 0xfe, 0x28, 0xfc, 0x52, 0x8, 0xc4, 0x19, + 0x3f, 0xe5, 0x40, 0x51, 0x80, + + /* U+857B "蕻" */ + 0x8, 0x20, 0x10, 0x43, 0xff, 0xf8, 0x41, 0x0, + 0x0, 0xf, 0x91, 0x10, 0x22, 0x3c, 0xff, 0x40, + 0x88, 0xf1, 0x11, 0x2, 0x27, 0xef, 0xf2, 0x0, + 0x9, 0x19, 0x3f, 0x61, 0x2, 0x1, + + /* U+857E "蕾" */ + 0x8, 0x21, 0xff, 0xfc, 0x20, 0x83, 0xff, 0xe0, + 0x10, 0x1f, 0xff, 0xef, 0x5e, 0x80, 0x80, 0x3d, + 0x78, 0x7f, 0xf9, 0x4, 0x11, 0xff, 0xe4, 0x10, + 0x47, 0xff, 0x90, 0x1, 0x0, + + /* U+8584 "薄" */ + 0x8, 0x41, 0xff, 0xfc, 0x21, 0x0, 0x4, 0x86, + 0xff, 0xe0, 0x10, 0x23, 0xff, 0x27, 0xfe, 0x8, + 0x84, 0x1f, 0xf9, 0x22, 0x2, 0xff, 0xf4, 0x41, + 0x8, 0x42, 0x20, 0x3c, 0x0, + + /* U+8585 "薅" */ + 0x8, 0x41, 0xff, 0xfc, 0x21, 0x2, 0x3f, 0xe4, + 0x40, 0x1e, 0x9e, 0x15, 0xff, 0x4a, 0xa0, 0x95, + 0x39, 0x2b, 0xb9, 0x94, 0x9, 0xbf, 0xf5, 0x91, + 0x19, 0x12, 0x22, 0x1c, 0x0, + + /* U+8587 "薇" */ + 0x4, 0x41, 0xff, 0xfc, 0x11, 0x0, 0x10, 0x82, + 0xa9, 0x9, 0x53, 0xe3, 0xe9, 0x10, 0x12, 0x2f, + 0xd4, 0x80, 0x2b, 0x1e, 0x62, 0x24, 0x44, 0x8d, + 0x89, 0x16, 0x94, 0x18, 0x80, + + /* U+858F "薏" */ + 0x8, 0x21, 0xff, 0xfc, 0x24, 0x83, 0xff, 0xe0, + 0x82, 0x1f, 0xff, 0xc0, 0x0, 0x1f, 0xfc, 0x20, + 0x8, 0x7f, 0xf0, 0xff, 0xe0, 0x0, 0x4, 0x90, + 0x49, 0x4, 0xe3, 0xf8, 0x0, + + /* U+859B "薛" */ + 0x4, 0x41, 0xff, 0xfc, 0x11, 0x1, 0x1, 0x7, + 0xdf, 0xe8, 0x80, 0x11, 0x22, 0x3e, 0x2c, 0x41, + 0xfe, 0x80, 0x41, 0xf0, 0x82, 0x2f, 0xe4, 0x42, + 0xf, 0x84, 0x11, 0x8, 0x0, + + /* U+859C "薜" */ + 0x4, 0x20, 0x7f, 0xff, 0x4, 0x20, 0x0, 0x10, + 0x3e, 0x8, 0x22, 0x7e, 0x22, 0x42, 0x3e, 0x24, + 0x20, 0xff, 0x20, 0x10, 0x5f, 0x10, 0x51, 0x7e, + 0x51, 0x10, 0x1f, 0x10, 0x11, 0x10, + + /* U+85A4 "薤" */ + 0x8, 0x41, 0xff, 0xfc, 0x21, 0x0, 0x5, 0x7, + 0x8a, 0x4, 0x77, 0x90, 0x28, 0x3c, 0x50, 0x4b, + 0xbd, 0xa1, 0x40, 0xc2, 0x80, 0xbd, 0xe2, 0xa, + 0x9, 0xff, 0xe0, 0x0, 0x0, + + /* U+85A8 "薨" */ + 0x8, 0x21, 0xff, 0xfc, 0x20, 0x81, 0xff, 0xc4, + 0x44, 0x87, 0xff, 0x0, 0x0, 0x7f, 0xff, 0x80, + 0x2, 0x7f, 0xf0, 0x7a, 0x23, 0x17, 0x89, 0x48, + 0x21, 0x10, 0x4c, 0x1f, 0x20, 0x0, + + /* U+85AA "薪" */ + 0x4, 0x21, 0xff, 0xfc, 0x10, 0x80, 0x40, 0x2f, + 0xf7, 0x84, 0x8, 0x1, 0x10, 0x7f, 0xbf, 0x8, + 0x49, 0xff, 0x10, 0x22, 0x21, 0x64, 0x44, 0xa8, + 0x91, 0x11, 0xc, 0x42, 0x0, + + /* U+85AE "薮" */ + 0x8, 0x40, 0x10, 0x83, 0xff, 0xf8, 0x42, 0x0, + 0x0, 0xa, 0x48, 0xd, 0x10, 0x7f, 0xbe, 0x10, + 0x88, 0xfd, 0x92, 0x41, 0x27, 0xf2, 0x82, 0x43, + 0xc, 0x86, 0x7, 0x16, 0x39, 0xc3, 0x81, 0x0, + + /* U+85AF "薯" */ + 0x8, 0x21, 0xff, 0xfc, 0x0, 0x83, 0xff, 0xe4, + 0x44, 0x4f, 0xff, 0x8f, 0xf2, 0x1, 0x10, 0xff, + 0xfe, 0x6, 0x0, 0x7f, 0xe7, 0x80, 0x41, 0xff, + 0x83, 0xff, 0x4, 0x2, 0x0, + + /* U+85B0 "薰" */ + 0x8, 0x41, 0xff, 0xfc, 0x0, 0x3, 0xff, 0xe0, + 0x10, 0xf, 0xff, 0x8f, 0xfe, 0x24, 0x94, 0x41, + 0x8, 0x7f, 0xe1, 0xff, 0xf0, 0x8, 0xf, 0xff, + 0xe0, 0x90, 0x91, 0x11, 0x0, + + /* U+85B7 "薷" */ + 0x8, 0x21, 0xff, 0xfc, 0x20, 0x83, 0xff, 0xe0, + 0x10, 0x1f, 0xff, 0xef, 0x5e, 0x80, 0x80, 0x3d, + 0x79, 0xff, 0xfc, 0x8, 0x3, 0xff, 0xe4, 0x44, + 0x48, 0x88, 0x91, 0x16, 0x0, + + /* U+85B9 "薹" */ + 0x8, 0x21, 0xff, 0xff, 0xff, 0xf8, 0x8, 0x7, + 0xff, 0xc7, 0xff, 0x8f, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x7f, 0xf4, 0x31, 0x81, 0xff, 0xc7, 0xff, + 0xdf, 0xff, 0xc0, + + /* U+85C1 "藁" */ + 0x0, 0x21, 0xff, 0xfc, 0x4, 0x87, 0xff, 0xf1, + 0xff, 0x83, 0xff, 0x0, 0x0, 0x3f, 0xfe, 0x4f, + 0xf4, 0x90, 0x29, 0x3f, 0xf7, 0xff, 0xf0, 0x74, + 0x3, 0x27, 0x38, 0x41, 0x80, + + /* U+85C9 "藉" */ + 0x4, 0x41, 0xff, 0xfc, 0x11, 0x0, 0x84, 0xf, + 0xdf, 0xc2, 0x12, 0x1e, 0x24, 0x9, 0xff, 0x10, + 0x1, 0xfb, 0xf8, 0xc4, 0x12, 0xcf, 0xe9, 0x10, + 0x52, 0x3f, 0x84, 0x41, 0x0, + + /* U+85CF "藏" */ + 0x2, 0x40, 0x7f, 0xff, 0x2, 0x14, 0x40, 0x12, + 0x4f, 0xfe, 0x48, 0x10, 0x3f, 0x92, 0xc, 0x94, + 0xc, 0x94, 0xff, 0x58, 0x54, 0x48, 0x53, 0xd8, + 0x54, 0xb9, 0x13, 0xe6, 0x20, 0x6, + + /* U+85D0 "藐" */ + 0x8, 0x41, 0xff, 0xfc, 0x21, 0x0, 0x82, 0xe, + 0x3f, 0xc3, 0x40, 0x94, 0x81, 0x31, 0xfe, 0x92, + 0x4, 0xe7, 0xfa, 0x62, 0x41, 0x44, 0x8c, 0x89, + 0x21, 0x22, 0x5d, 0x87, 0x80, + + /* U+85D3 "藓" */ + 0x8, 0x41, 0xff, 0xfc, 0x20, 0x0, 0x82, 0x23, + 0xc2, 0x88, 0x9f, 0xff, 0x84, 0x25, 0x8, 0x7e, + 0x78, 0x94, 0x21, 0x28, 0x43, 0xf7, 0xf0, 0x1, + 0x1f, 0xe2, 0x0, 0x4, 0x0, + + /* U+85D5 "藕" */ + 0x4, 0x21, 0xff, 0xfc, 0x10, 0x80, 0x8f, 0xef, + 0xd2, 0x42, 0x3f, 0x9f, 0x49, 0x8, 0xfe, 0xfc, + 0x20, 0x20, 0x40, 0xc7, 0xf9, 0xd1, 0x55, 0x6f, + 0xb2, 0x40, 0x44, 0x83, 0x0, + + /* U+85DC "藜" */ + 0x4, 0x21, 0xff, 0xfc, 0x10, 0x83, 0xf2, 0x1, + 0x7, 0xdf, 0xd2, 0x85, 0x19, 0x3c, 0xce, 0x6, + 0xc0, 0x32, 0x63, 0xb4, 0xf8, 0xa, 0x0, 0xf6, + 0xe, 0x23, 0x81, 0xc0, 0x0, + + /* U+85E4 "藤" */ + 0x8, 0x41, 0xff, 0xfc, 0x21, 0x3, 0x92, 0x45, + 0x14, 0xa, 0x7f, 0xdc, 0x10, 0x2b, 0xff, 0x51, + 0x10, 0xec, 0x9d, 0x45, 0x42, 0x86, 0x9, 0x35, + 0x92, 0x88, 0xac, 0x70, 0x0, + + /* U+85E9 "藩" */ + 0x8, 0x41, 0xff, 0xfc, 0x0, 0x2, 0x3f, 0xc2, + 0x4, 0x80, 0x4a, 0x23, 0xff, 0x20, 0xb0, 0x6, + 0x58, 0x5f, 0xf8, 0x91, 0x12, 0x3f, 0xe4, 0x44, + 0x48, 0xff, 0xa1, 0x1, 0x0, + + /* U+85FB "藻" */ + 0x8, 0x21, 0xff, 0xfc, 0x20, 0x82, 0x1f, 0xc2, + 0x40, 0x80, 0x7f, 0x20, 0x0, 0x27, 0xde, 0x28, + 0xa4, 0x1f, 0x78, 0x81, 0x1, 0x7f, 0xf4, 0x16, + 0x8, 0xcb, 0xa6, 0x10, 0x80, + + /* U+85FF "藿" */ + 0x8, 0x43, 0xff, 0xf7, 0xff, 0x80, 0x40, 0xff, + 0xfe, 0x4, 0x19, 0x97, 0x4f, 0x1c, 0x21, 0x0, + 0xff, 0xef, 0xff, 0xa8, 0x40, 0x3f, 0xf0, 0xff, + 0xf2, 0x0, 0x0, + + /* U+8605 "蘅" */ + 0x4, 0x21, 0xff, 0xfc, 0x14, 0x81, 0x1e, 0x74, + 0x48, 0x11, 0xfc, 0xa, 0x48, 0x13, 0xff, 0x49, + 0x25, 0x8f, 0xc9, 0x4, 0x12, 0x7f, 0x24, 0x18, + 0x48, 0xcc, 0x92, 0x7, 0x0, + + /* U+8611 "蘑" */ + 0x4, 0x20, 0xff, 0xfc, 0x10, 0x81, 0xff, 0xf4, + 0x21, 0xb, 0xef, 0x91, 0x86, 0x25, 0xba, 0x50, + 0x0, 0xbf, 0xfd, 0x8, 0x2, 0x3f, 0xc7, 0xc0, + 0x88, 0xff, 0x1, 0x2, 0x0, + + /* U+8616 "蘖" */ + 0x4, 0x41, 0xff, 0xfc, 0x11, 0x1, 0x0, 0x8d, + 0xce, 0xcf, 0x89, 0x1f, 0x2d, 0x42, 0xfe, 0x84, + 0x20, 0xf8, 0x43, 0xff, 0xf8, 0x3e, 0x1, 0x93, + 0x1c, 0x21, 0xc0, 0x40, 0x0, + + /* U+8627 "蘧" */ + 0x8, 0x21, 0xff, 0xfc, 0x22, 0x2, 0x7, 0xf2, + 0x7f, 0xe0, 0x90, 0x41, 0xfc, 0x2, 0x3c, 0x67, + 0xfe, 0x53, 0x8c, 0xa6, 0xe1, 0xde, 0xf6, 0x6e, + 0x1a, 0x0, 0x23, 0xff, 0x80, + + /* U+8629 "蘩" */ + 0x4, 0x40, 0xff, 0xf8, 0x80, 0x82, 0xf9, 0xf3, + 0xf6, 0x49, 0x25, 0x1f, 0x84, 0x3f, 0xb6, 0x6, + 0x12, 0x7f, 0xc0, 0x10, 0x83, 0xff, 0xc0, 0x10, + 0x4e, 0x23, 0xa1, 0xc0, 0x80, + + /* U+8638 "蘸" */ + 0x8, 0x21, 0xff, 0xfc, 0x0, 0x87, 0xf2, 0x82, + 0x87, 0xc5, 0x12, 0x3f, 0xe4, 0x55, 0xfc, 0xae, + 0xfd, 0x85, 0x22, 0xa, 0x47, 0xf7, 0xe8, 0x28, + 0x5f, 0xd6, 0xa0, 0xca, 0x80, + + /* U+863C "蘼" */ + 0x4, 0x10, 0xff, 0xfc, 0x12, 0x41, 0xff, 0xf2, + 0x0, 0x4, 0x42, 0xb, 0xcf, 0x17, 0x9c, 0x32, + 0xd6, 0x82, 0x41, 0x3c, 0xfa, 0x79, 0xf4, 0x12, + 0xb, 0xe7, 0xe0, 0x48, 0x0, + + /* U+864D "虍" */ + 0x1, 0x0, 0x2, 0x0, 0x4, 0x0, 0xf, 0xe0, + 0x10, 0x7, 0xff, 0xc8, 0x0, 0x90, 0x1, 0x21, + 0x0, 0x42, 0xf8, 0xbe, 0x1, 0x8, 0x2, 0x10, + 0x28, 0x20, 0x50, 0x41, 0x20, 0x7e, + + /* U+864E "虎" */ + 0x1, 0x0, 0x0, 0xfc, 0x1, 0x0, 0x3f, 0xfe, + 0x21, 0x2, 0x21, 0x0, 0x2f, 0xf4, 0x21, 0x4, + 0x20, 0xf8, 0x23, 0xe0, 0x22, 0x20, 0x22, 0x21, + 0x42, 0x22, 0x44, 0x22, 0xc8, 0x3e, + + /* U+864F "虏" */ + 0x1, 0x0, 0x1, 0xfc, 0x1, 0x0, 0x3f, 0xff, + 0x21, 0x2, 0x21, 0x0, 0x2f, 0xf2, 0x21, 0xfc, + 0x20, 0x0, 0x20, 0x80, 0x2f, 0xfe, 0x20, 0x82, + 0x41, 0x2, 0x43, 0x2, 0xdc, 0x3c, + + /* U+8650 "虐" */ + 0x1, 0x0, 0x1, 0xfc, 0x1, 0x0, 0x3f, 0xfe, + 0x21, 0x2, 0x2f, 0xf8, 0x21, 0x4, 0x21, 0xfc, + 0x20, 0x0, 0x27, 0xfc, 0x24, 0x0, 0x7f, 0xff, + 0x44, 0x0, 0x47, 0xfe, + + /* U+8651 "虑" */ + 0x1, 0x0, 0x1, 0xfc, 0x1, 0x0, 0x3f, 0xfe, + 0x20, 0x2, 0x21, 0x2, 0x2f, 0xf8, 0x21, 0x4, + 0x21, 0xfc, 0x20, 0x0, 0x2a, 0x44, 0x2a, 0x4a, + 0x52, 0xa, 0x52, 0x18, 0xc3, 0xf0, + + /* U+8654 "虔" */ + 0x1, 0x0, 0x1, 0xfc, 0x1, 0x0, 0x3f, 0xfe, + 0x21, 0x2, 0x21, 0xf8, 0x2f, 0x4, 0x20, 0xf8, + 0x20, 0x40, 0x2f, 0xfe, 0x22, 0x8, 0x41, 0x30, + 0x40, 0xc0, 0x5f, 0x3e, + + /* U+865A "虚" */ + 0x1, 0x0, 0x3, 0xf8, 0x4, 0x1, 0xff, 0xf2, + 0x10, 0x25, 0xfe, 0x8, 0x41, 0x10, 0xfe, 0x20, + 0x0, 0x42, 0x40, 0xa4, 0x91, 0x29, 0x64, 0x52, + 0x88, 0x24, 0x37, 0xff, 0x80, + + /* U+865E "虞" */ + 0x1, 0xfc, 0x1, 0x0, 0x3f, 0xfe, 0x21, 0x2, + 0x3f, 0xfc, 0x21, 0x4, 0x20, 0xf8, 0x2f, 0xfc, + 0x28, 0x4, 0x27, 0xf8, 0x2f, 0xfe, 0x40, 0x40, + 0x5f, 0xfe, 0x40, 0xa0, 0x9f, 0x1e, + + /* U+8662 "虢" */ + 0x4, 0x20, 0xf8, 0x3e, 0x4, 0x20, 0x95, 0xfe, + 0x59, 0x22, 0x41, 0x3c, 0x9, 0x62, 0xfd, 0x26, + 0x9, 0x18, 0x49, 0x78, 0x49, 0x48, 0x29, 0x48, + 0xb, 0x4a, 0xa, 0xca, 0x34, 0x8e, + + /* U+866B "虫" */ + 0x2, 0x0, 0x4, 0x0, 0x8, 0x3, 0xff, 0xc4, + 0x20, 0x88, 0x41, 0x10, 0x82, 0x21, 0x4, 0x7f, + 0xf8, 0x84, 0x0, 0x8, 0x0, 0x10, 0x80, 0x20, + 0x9f, 0xff, 0x80, 0x1, 0x0, + + /* U+866C "虬" */ + 0x8, 0x20, 0x8, 0x20, 0x8, 0x20, 0x7f, 0x20, + 0x49, 0x20, 0x49, 0x20, 0x49, 0x20, 0x49, 0x20, + 0x7f, 0x20, 0x48, 0x20, 0x9, 0x21, 0x9, 0x21, + 0xff, 0xa2, 0x0, 0x9e, + + /* U+866E "虮" */ + 0x10, 0x0, 0x10, 0xf8, 0x10, 0x88, 0x7e, 0x88, + 0x52, 0x88, 0x52, 0x88, 0x52, 0x88, 0x52, 0x88, + 0x7e, 0x88, 0x50, 0x88, 0x14, 0x88, 0x12, 0x89, + 0x1e, 0x89, 0x61, 0x9, 0x1, 0xe, + + /* U+8671 "虱" */ + 0xff, 0xf0, 0x0, 0x4f, 0xf9, 0x1, 0x4, 0x4, + 0x11, 0xfe, 0x44, 0x49, 0x11, 0x24, 0x7f, 0x91, + 0x10, 0x44, 0x41, 0x41, 0x25, 0xff, 0xd4, 0x1, + 0x30, + + /* U+8679 "虹" */ + 0x10, 0x0, 0x10, 0xfe, 0x10, 0x10, 0x7e, 0x10, + 0x52, 0x10, 0x52, 0x10, 0x52, 0x10, 0x7e, 0x10, + 0x50, 0x10, 0x10, 0x10, 0x16, 0x10, 0x12, 0x10, + 0x3e, 0x10, 0x40, 0xff, + + /* U+867A "虺" */ + 0x0, 0x20, 0xfe, 0x20, 0x49, 0xfc, 0x49, 0x24, + 0x49, 0x24, 0x49, 0x24, 0x49, 0xfc, 0x49, 0x20, + 0x48, 0x20, 0x48, 0x24, 0x48, 0x24, 0x49, 0xfd, + 0x48, 0x1, 0xcc, 0x2, 0x87, 0xfe, + + /* U+867B "虻" */ + 0x10, 0x10, 0x10, 0x8, 0x10, 0x8, 0x7e, 0xff, + 0x52, 0x40, 0x52, 0x40, 0x52, 0x40, 0x7e, 0x40, + 0x50, 0x40, 0x10, 0x40, 0x12, 0x40, 0x12, 0x40, + 0xfd, 0x40, 0x0, 0x7f, + + /* U+867C "虼" */ + 0x10, 0x0, 0x10, 0x40, 0x10, 0x7e, 0x7c, 0x80, + 0x54, 0x80, 0x55, 0x0, 0x54, 0x7e, 0x54, 0x4, + 0x54, 0x8, 0x7c, 0x10, 0x10, 0x20, 0x14, 0x21, + 0x14, 0x41, 0x1e, 0x81, 0xf2, 0x82, 0x0, 0xfe, + + /* U+867D "虽" */ + 0x3f, 0xf0, 0x80, 0x42, 0x1, 0xf, 0xfc, 0x1, + 0x0, 0x4, 0x7, 0xff, 0x90, 0x42, 0x41, 0x9, + 0xff, 0xe4, 0x10, 0x0, 0x44, 0xff, 0xf8, 0x0, + 0x10, + + /* U+867E "虾" */ + 0x10, 0x0, 0x23, 0xfc, 0x40, 0x83, 0xf1, 0x5, + 0x22, 0xa, 0x44, 0x14, 0x8e, 0x29, 0x12, 0x7e, + 0x22, 0xa0, 0x40, 0x48, 0x80, 0x91, 0x1, 0x22, + 0x1f, 0xa4, 0x0, 0x8, 0x0, + + /* U+867F "虿" */ + 0xff, 0xfe, 0x8, 0x0, 0x3f, 0xe0, 0x40, 0x43, + 0x0, 0x88, 0x26, 0xf, 0xfe, 0x10, 0x84, 0x21, + 0x8, 0x7f, 0xf0, 0x84, 0x0, 0x8, 0x4f, 0xff, + 0xc0, 0x0, 0x40, + + /* U+8680 "蚀" */ + 0x20, 0x20, 0x40, 0x41, 0xe0, 0x82, 0x5f, 0xe4, + 0xa2, 0x51, 0x44, 0xa8, 0x89, 0x11, 0x12, 0x23, + 0xfc, 0x44, 0x40, 0x80, 0x81, 0x1, 0x22, 0x82, + 0x46, 0xff, 0x40, 0x0, 0x80, + + /* U+8681 "蚁" */ + 0x10, 0x0, 0x20, 0x40, 0x40, 0x93, 0xe8, 0xa5, + 0x48, 0x4a, 0x90, 0x95, 0x21, 0x2a, 0x44, 0x54, + 0x48, 0xf8, 0xa0, 0x50, 0xc0, 0xa1, 0x81, 0x45, + 0x8f, 0x51, 0x80, 0xc0, 0x80, + + /* U+8682 "蚂" */ + 0x10, 0x0, 0x21, 0xf8, 0x40, 0x13, 0xf4, 0x25, + 0x28, 0x4a, 0x50, 0x94, 0xa1, 0x29, 0x7f, 0x7e, + 0x2, 0xa0, 0x4, 0x43, 0xe8, 0x90, 0x11, 0x20, + 0x3f, 0xa0, 0x40, 0xf, 0x0, + + /* U+868A "蚊" */ + 0x10, 0x10, 0x10, 0x10, 0x10, 0x0, 0x7e, 0xff, + 0x52, 0x42, 0x52, 0x42, 0x52, 0x44, 0x7e, 0x24, + 0x50, 0x24, 0x12, 0x18, 0x12, 0x18, 0x12, 0x2c, + 0xfd, 0x46, 0x1, 0x81, + + /* U+868B "蚋" */ + 0x10, 0x10, 0x20, 0x20, 0x40, 0x43, 0xef, 0xf5, + 0x51, 0x2a, 0xa2, 0x55, 0x44, 0xbe, 0x99, 0x51, + 0x2a, 0x22, 0x94, 0x55, 0x18, 0xa8, 0x13, 0xd0, + 0x28, 0x20, 0x40, 0x43, 0x80, + + /* U+868C "蚌" */ + 0x10, 0x20, 0x20, 0x41, 0xf7, 0xfa, 0xa1, 0x5, + 0x42, 0xa, 0x84, 0x15, 0x3f, 0x3e, 0x10, 0x50, + 0x20, 0x20, 0x40, 0x57, 0xf8, 0xa1, 0x1, 0xc2, + 0xc, 0x44, 0x0, 0x8, 0x0, + + /* U+868D "蚍" */ + 0x10, 0x88, 0x10, 0x88, 0x10, 0x88, 0x7c, 0x89, + 0x54, 0x8a, 0x54, 0xec, 0x54, 0x88, 0x54, 0x88, + 0x7c, 0x88, 0x50, 0x88, 0x10, 0x88, 0x14, 0x89, + 0x14, 0xa9, 0xfc, 0xc9, 0x2, 0x8f, + + /* U+8693 "蚓" */ + 0x11, 0xe4, 0x40, 0x97, 0xc2, 0x55, 0x9, 0x55, + 0xe5, 0x54, 0x15, 0x50, 0x55, 0x41, 0x7d, 0xe4, + 0x40, 0x91, 0x42, 0x45, 0x9, 0x3c, 0x25, 0x10, + 0x90, 0x1e, 0x40, + + /* U+8695 "蚕" */ + 0x7f, 0xf8, 0x8, 0x0, 0x40, 0x3f, 0xff, 0x8, + 0x80, 0x41, 0x86, 0x21, 0x3f, 0xff, 0x22, 0x10, + 0x88, 0x43, 0xff, 0x8, 0x80, 0x2, 0x13, 0xff, + 0xe0, 0x0, 0x0, + + /* U+869C "蚜" */ + 0x10, 0x0, 0x23, 0xfc, 0x40, 0x23, 0xf4, 0x45, + 0x28, 0x8a, 0x61, 0x14, 0xc2, 0x29, 0x7f, 0x7e, + 0x18, 0xa0, 0x50, 0x48, 0xa0, 0x92, 0x47, 0xc8, + 0x80, 0x21, 0x0, 0x1e, 0x0, + + /* U+869D "蚝" */ + 0x10, 0x0, 0x11, 0xfc, 0x10, 0x20, 0x7c, 0x20, + 0x54, 0x20, 0x55, 0xfe, 0x54, 0x20, 0x54, 0x20, + 0x7c, 0x20, 0x53, 0xff, 0x10, 0x20, 0x14, 0x21, + 0x16, 0x21, 0x6a, 0x22, 0x0, 0x3e, + + /* U+86A3 "蚣" */ + 0x10, 0x10, 0x20, 0x90, 0x41, 0x23, 0xe4, 0x45, + 0x48, 0x4a, 0xa0, 0x95, 0x48, 0xaa, 0x10, 0x7c, + 0x40, 0xa0, 0x80, 0x42, 0x20, 0xa4, 0x41, 0x48, + 0x5d, 0xbf, 0x80, 0x0, 0x80, + + /* U+86A4 "蚤" */ + 0x7f, 0xfc, 0x22, 0x10, 0x32, 0xc0, 0x1e, 0xf, + 0xc7, 0xe0, 0x20, 0xf, 0xfe, 0x10, 0x84, 0x21, + 0x8, 0x7f, 0xf0, 0x84, 0x0, 0x8, 0x47, 0xff, + 0xc0, 0x0, 0x40, + + /* U+86A7 "蚧" */ + 0x10, 0x0, 0x20, 0x60, 0x40, 0xc3, 0xe2, 0x45, + 0x48, 0x4a, 0xb0, 0xd5, 0x40, 0xbe, 0x24, 0x50, + 0x48, 0x20, 0x90, 0x51, 0x20, 0xa2, 0x47, 0xe8, + 0x80, 0x11, 0x0, 0x42, 0x0, + + /* U+86A8 "蚨" */ + 0x10, 0x20, 0x20, 0x40, 0x40, 0x83, 0xf7, 0xf5, + 0x22, 0xa, 0x44, 0x14, 0x88, 0x29, 0xff, 0x7e, + 0x20, 0x20, 0xa0, 0x49, 0x40, 0x92, 0xc3, 0xc8, + 0x88, 0x20, 0xc0, 0x0, 0x80, + + /* U+86A9 "蚩" */ + 0x41, 0x4, 0x82, 0x9, 0xff, 0xf0, 0x8, 0x0, + 0x10, 0x1f, 0xff, 0xc0, 0x40, 0x1f, 0xfe, 0x41, + 0x4, 0x82, 0x8, 0xff, 0xf0, 0x8, 0x40, 0x10, + 0x9f, 0xff, 0x80, 0x0, 0x80, + + /* U+86AA "蚪" */ + 0x10, 0x4, 0x10, 0x4, 0x10, 0x44, 0x7e, 0x24, + 0x52, 0x4, 0x52, 0x84, 0x52, 0x64, 0x7e, 0x4, + 0x50, 0x4, 0x10, 0x7, 0x12, 0xfc, 0x12, 0x4, + 0x1e, 0x4, 0x61, 0x4, 0x0, 0x4, + + /* U+86AC "蚬" */ + 0x10, 0x0, 0x10, 0xfe, 0x10, 0x82, 0x7c, 0xb2, + 0x54, 0xb2, 0x54, 0xb2, 0x54, 0xb2, 0x54, 0xb2, + 0x7c, 0xb2, 0x50, 0xba, 0x10, 0x38, 0x14, 0x79, + 0x1e, 0xd9, 0xe1, 0x99, 0x3, 0x1e, + + /* U+86AF "蚯" */ + 0x10, 0x0, 0x21, 0xf8, 0x42, 0x3, 0xf4, 0x5, + 0x28, 0xa, 0x50, 0x14, 0xbf, 0xbf, 0x44, 0x50, + 0x88, 0x21, 0x10, 0x4a, 0x20, 0x94, 0x4f, 0xe8, + 0x80, 0x3f, 0xc0, + + /* U+86B0 "蚰" */ + 0x10, 0x10, 0x20, 0x20, 0x40, 0x43, 0xe7, 0xf5, + 0x49, 0x2a, 0x92, 0x55, 0x24, 0xaa, 0x49, 0x7c, + 0xfe, 0xa1, 0x24, 0x42, 0x48, 0xa4, 0x91, 0xc9, + 0x2c, 0x5f, 0xc0, 0x20, 0x80, + + /* U+86B1 "蚱" */ + 0x10, 0x0, 0x20, 0x80, 0x41, 0xfb, 0xf5, 0x5, + 0x2a, 0xa, 0x64, 0x14, 0x88, 0x29, 0x1f, 0x7e, + 0x20, 0xa0, 0x40, 0x48, 0xf8, 0x91, 0x1, 0xe2, + 0xc, 0x24, 0x0, 0x8, 0x0, + + /* U+86B4 "蚴" */ + 0x10, 0x88, 0x21, 0x11, 0xf4, 0x22, 0xab, 0xf5, + 0x64, 0xaa, 0xf1, 0x55, 0x22, 0xbe, 0x45, 0x51, + 0xa, 0x22, 0x94, 0x5d, 0x28, 0xbe, 0x91, 0xc1, + 0x3c, 0x84, 0x40, 0x1b, 0x80, + + /* U+86B5 "蚵" */ + 0x10, 0x0, 0x10, 0xff, 0x10, 0x2, 0x7e, 0x2, + 0x52, 0xf2, 0x52, 0x92, 0x52, 0x92, 0x52, 0x92, + 0x7e, 0x92, 0x10, 0x92, 0x12, 0xf2, 0x12, 0x2, + 0x12, 0x2, 0xfd, 0x2, 0x0, 0x1c, + + /* U+86B6 "蚶" */ + 0x10, 0x84, 0x21, 0x8, 0x42, 0x13, 0xef, 0xf5, + 0x48, 0x4a, 0x90, 0x95, 0x21, 0x2a, 0x42, 0x7c, + 0xfc, 0xa1, 0x8, 0x52, 0x10, 0xa4, 0x21, 0xc8, + 0x4c, 0x5f, 0x80, 0x21, 0x0, + + /* U+86BA "蚺" */ + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x11, 0xfe, + 0x7d, 0x12, 0x55, 0x12, 0x55, 0x12, 0x55, 0xfe, + 0x55, 0x12, 0x7d, 0x12, 0x53, 0xff, 0x19, 0x2, + 0x15, 0x2, 0x7d, 0x2, 0x1, 0x2, 0x1, 0x1e, + + /* U+86C0 "蛀" */ + 0x10, 0x0, 0x10, 0x10, 0x10, 0x10, 0x7e, 0xff, + 0x52, 0x10, 0x52, 0x10, 0x52, 0x10, 0x52, 0x10, + 0x7e, 0xfe, 0x50, 0x10, 0x10, 0x10, 0x12, 0x10, + 0x1e, 0x10, 0x63, 0xff, + + /* U+86C4 "蛄" */ + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x7e, 0xff, + 0x52, 0x10, 0x52, 0x10, 0x52, 0x10, 0x52, 0x10, + 0x7e, 0xfe, 0x50, 0x82, 0x14, 0x82, 0x12, 0x82, + 0x1e, 0x82, 0xe2, 0xfe, 0x0, 0x82, + + /* U+86C6 "蛆" */ + 0x10, 0x0, 0x21, 0xf8, 0x42, 0x13, 0xf4, 0x25, + 0x28, 0x4a, 0x5f, 0x94, 0xa1, 0x29, 0x42, 0x7e, + 0x84, 0xa1, 0xf8, 0x4a, 0x10, 0x94, 0x2e, 0xd8, + 0x40, 0x3f, 0xc0, + + /* U+86C7 "蛇" */ + 0x10, 0x20, 0x20, 0x20, 0x43, 0xfb, 0xf8, 0x15, + 0x20, 0x2a, 0x40, 0x14, 0xa1, 0x29, 0x46, 0x7e, + 0x90, 0xa1, 0x80, 0x42, 0x0, 0x94, 0x11, 0x28, + 0x3f, 0xd0, 0x40, 0x1f, 0x0, + + /* U+86C9 "蛉" */ + 0x10, 0x20, 0x20, 0x40, 0x41, 0x43, 0xe4, 0x45, + 0x48, 0x4a, 0xa4, 0xd5, 0x8, 0x2a, 0x0, 0x7d, + 0xfc, 0xa0, 0x8, 0x40, 0x20, 0xa0, 0x81, 0x4e, + 0x1f, 0x4c, 0x0, 0x4, 0x0, + + /* U+86CA "蛊" */ + 0x1, 0x0, 0x7f, 0xf9, 0x4, 0x12, 0x8, 0x23, + 0xff, 0xc0, 0x21, 0x0, 0x41, 0x7f, 0xfe, 0x0, + 0x2, 0x7f, 0xf9, 0x11, 0x12, 0x22, 0x24, 0x44, + 0x5f, 0xff, 0xc0, + + /* U+86CB "蛋" */ + 0xff, 0xfc, 0x2, 0x8, 0x84, 0x11, 0xf, 0x83, + 0x10, 0x9, 0xff, 0xa0, 0x40, 0x0, 0x80, 0x7f, + 0xf8, 0x82, 0x11, 0xff, 0xe2, 0x8, 0x0, 0x11, + 0x9f, 0xff, 0x80, 0x0, 0x0, + + /* U+86CE "蛎" */ + 0x10, 0x0, 0x11, 0xff, 0x11, 0x0, 0x7d, 0x0, + 0x55, 0x7f, 0x55, 0x20, 0x55, 0x20, 0x55, 0x20, + 0x7d, 0x3e, 0x11, 0x22, 0x11, 0x22, 0x15, 0x22, + 0x3d, 0x42, 0x42, 0x42, 0x2, 0x9c, + + /* U+86CF "蛏" */ + 0x10, 0x0, 0x11, 0xfe, 0x10, 0x82, 0x7c, 0x84, + 0x54, 0x48, 0x54, 0x30, 0x55, 0xcf, 0x7c, 0x0, + 0x50, 0x20, 0x15, 0xfe, 0x14, 0x20, 0x14, 0x20, + 0x6a, 0x20, 0x3, 0xff, + + /* U+86D0 "蛐" */ + 0x10, 0x48, 0x20, 0x91, 0xf7, 0xfa, 0xaa, 0x55, + 0x54, 0xaa, 0xa9, 0x55, 0x52, 0xaa, 0xa5, 0x7d, + 0xfe, 0x22, 0x94, 0x55, 0x28, 0xaa, 0x57, 0xd4, + 0xa0, 0xbf, 0xc0, 0x40, 0x80, + + /* U+86D1 "蛑" */ + 0x10, 0x0, 0x10, 0x20, 0x10, 0x60, 0x10, 0x44, + 0x7c, 0x82, 0x55, 0xfe, 0x54, 0x1, 0x54, 0x90, + 0x7d, 0x7e, 0x51, 0x10, 0x10, 0x10, 0x14, 0x10, + 0x15, 0xff, 0xfc, 0x10, 0x2, 0x10, 0x0, 0x10, + + /* U+86D4 "蛔" */ + 0x10, 0x0, 0x23, 0xfc, 0x44, 0xb, 0xe8, 0x15, + 0x57, 0xaa, 0xa9, 0x55, 0x52, 0xaa, 0xa5, 0x7d, + 0x4a, 0xa2, 0x94, 0x45, 0xe8, 0xa8, 0x11, 0x50, + 0x3d, 0xbf, 0xc0, 0x40, 0x80, + + /* U+86D8 "蛘" */ + 0x10, 0x84, 0x20, 0x90, 0x41, 0x23, 0xef, 0xf5, + 0x42, 0xa, 0x84, 0x15, 0x7f, 0x3e, 0x10, 0x50, + 0x20, 0x20, 0x40, 0x57, 0xf8, 0xa1, 0x1, 0xc2, + 0xc, 0x44, 0x0, 0x8, 0x0, + + /* U+86D9 "蛙" */ + 0x10, 0x10, 0x10, 0x10, 0x10, 0xfe, 0x7c, 0x10, + 0x54, 0x10, 0x54, 0x10, 0x55, 0xff, 0x54, 0x10, + 0x7c, 0x10, 0x50, 0xfe, 0x14, 0x10, 0x14, 0x10, + 0xfe, 0x10, 0x1, 0xff, + + /* U+86DB "蛛" */ + 0x10, 0x10, 0x10, 0x90, 0x10, 0x90, 0x7c, 0xfe, + 0x55, 0x10, 0x55, 0x10, 0x54, 0x10, 0x55, 0xfe, + 0x7c, 0x38, 0x50, 0x58, 0x10, 0x54, 0x14, 0x92, + 0x1d, 0x11, 0xe2, 0x10, 0x0, 0x10, + + /* U+86DE "蛞" */ + 0x10, 0x0, 0x10, 0xfe, 0x10, 0x10, 0x7c, 0x10, + 0x55, 0xff, 0x54, 0x10, 0x54, 0x10, 0x7c, 0x10, + 0x50, 0xfe, 0x10, 0x82, 0x14, 0x82, 0x14, 0x82, + 0x3e, 0x82, 0x42, 0xfe, 0x0, 0x82, + + /* U+86DF "蛟" */ + 0x10, 0x20, 0x10, 0x20, 0x10, 0x0, 0x7d, 0xfe, + 0x54, 0x0, 0x54, 0x48, 0x54, 0x84, 0x55, 0x2, + 0x7c, 0x88, 0x50, 0x48, 0x14, 0x58, 0x14, 0x30, + 0x1e, 0x30, 0xe2, 0xcc, 0x3, 0x2, + + /* U+86E4 "蛤" */ + 0x10, 0x10, 0x10, 0x30, 0x10, 0x28, 0x7e, 0x44, + 0x52, 0x83, 0x52, 0x7c, 0x52, 0x0, 0x7e, 0x0, + 0x50, 0xfe, 0x50, 0x82, 0x12, 0x82, 0x12, 0x82, + 0x1e, 0x82, 0x62, 0xfe, 0x0, 0x82, + + /* U+86E9 "蛩" */ + 0xfb, 0xf0, 0x44, 0x20, 0x8a, 0x49, 0xd2, 0x9c, + 0x41, 0xe0, 0x40, 0x0, 0x80, 0x3f, 0xfc, 0x42, + 0x8, 0xff, 0xf1, 0x8, 0x2, 0x10, 0xcf, 0xff, + 0xc0, 0x0, 0x0, + + /* U+86ED "蛭" */ + 0x10, 0x0, 0x23, 0xfc, 0x41, 0x3, 0xe4, 0x5, + 0x50, 0x8a, 0xbf, 0x95, 0x60, 0xbe, 0x10, 0x50, + 0x20, 0xa3, 0xfc, 0x50, 0x80, 0xa1, 0xf, 0xa2, + 0x0, 0x7f, 0xc0, + + /* U+86EE "蛮" */ + 0x2, 0x0, 0x4, 0xf, 0xff, 0xc1, 0x20, 0x24, + 0x91, 0x12, 0x28, 0x48, 0x40, 0x80, 0x7f, 0xf9, + 0x8, 0x24, 0x20, 0x9f, 0xfe, 0x42, 0x0, 0x8, + 0x2f, 0xff, 0xc0, 0x1, + + /* U+86F0 "蛰" */ + 0x20, 0x80, 0x41, 0x3, 0xef, 0xc1, 0x4, 0x83, + 0xb9, 0x1c, 0x3a, 0x89, 0x7, 0x21, 0x0, 0x7f, + 0xf8, 0x84, 0x11, 0x8, 0x23, 0xff, 0xc0, 0x20, + 0x1f, 0xff, 0x80, 0x1, 0x0, + + /* U+86F1 "蛱" */ + 0x10, 0x20, 0x20, 0x40, 0x40, 0x83, 0xef, 0xe5, + 0x42, 0xa, 0x94, 0x95, 0x2a, 0x2a, 0x10, 0x79, + 0xfe, 0xa0, 0x40, 0x50, 0xc0, 0xa2, 0x81, 0xcc, + 0x9c, 0x70, 0x80, 0xc0, 0x80, + + /* U+86F2 "蛲" */ + 0x10, 0x80, 0x10, 0x40, 0x10, 0x7e, 0x7d, 0xc0, + 0x54, 0x24, 0x54, 0x39, 0x55, 0xd9, 0x7c, 0x6, + 0x50, 0x0, 0x11, 0xff, 0x14, 0x48, 0x14, 0x48, + 0x7e, 0x89, 0x0, 0x89, 0x3, 0xf, + + /* U+86F3 "蛳" */ + 0x20, 0x80, 0x41, 0x7c, 0x8a, 0x23, 0xd4, 0x46, + 0xab, 0xed, 0x55, 0x5a, 0xaa, 0xb5, 0x55, 0x7a, + 0xaa, 0x45, 0x54, 0xa2, 0xa9, 0x4d, 0x73, 0x92, + 0x88, 0x61, 0x0, 0x82, 0x0, + + /* U+86F4 "蛴" */ + 0x10, 0x20, 0x20, 0x40, 0x40, 0x3, 0xef, 0xf5, + 0x44, 0x8a, 0x89, 0x15, 0xc, 0x2a, 0x18, 0x7c, + 0xcc, 0x22, 0x4, 0x41, 0x20, 0xa2, 0x41, 0x44, + 0x9f, 0x59, 0x0, 0x22, 0x1, 0x84, + + /* U+86F8 "蛸" */ + 0x10, 0x92, 0x21, 0x24, 0x41, 0x53, 0xe0, 0x85, + 0x4f, 0xea, 0x90, 0x55, 0x20, 0xaa, 0x41, 0x7c, + 0xfe, 0xa1, 0x4, 0x53, 0xf8, 0xa4, 0x11, 0xe8, + 0x3c, 0x50, 0x40, 0x23, 0x80, + + /* U+86F9 "蛹" */ + 0x10, 0x0, 0x23, 0xfc, 0x42, 0x13, 0xe3, 0x45, + 0x5f, 0xea, 0xa2, 0x55, 0x44, 0xaa, 0xff, 0x7d, + 0x12, 0xa2, 0x24, 0x67, 0xf8, 0xa8, 0x91, 0xd1, + 0x2c, 0xa2, 0x40, 0x43, 0x80, + + /* U+86FE "蛾" */ + 0x10, 0x8, 0x10, 0x6c, 0x13, 0x8a, 0x7c, 0x88, + 0x54, 0x88, 0x57, 0xff, 0x54, 0x88, 0x7c, 0x8a, + 0x50, 0xea, 0x51, 0xcc, 0x16, 0x88, 0x14, 0x88, + 0x3c, 0x99, 0x40, 0xa5, 0x3, 0x82, + + /* U+8700 "蜀" */ + 0x7f, 0xfd, 0x11, 0x14, 0x44, 0x5f, 0xff, 0x10, + 0x0, 0xff, 0xfc, 0x20, 0x4f, 0xf9, 0x42, 0x25, + 0x8, 0x93, 0xfe, 0x40, 0x81, 0xff, 0xf4, 0x0, + 0x30, 0x7, 0x80, + + /* U+8702 "蜂" */ + 0x10, 0x40, 0x10, 0xfe, 0x10, 0x84, 0x7d, 0x4c, + 0x54, 0x78, 0x54, 0x30, 0x55, 0xcf, 0x54, 0x20, + 0x7d, 0xfe, 0x51, 0xfe, 0x14, 0x20, 0x14, 0x20, + 0x1d, 0xff, 0xe2, 0x20, 0x0, 0x20, + + /* U+8703 "蜃" */ + 0x3f, 0xfe, 0x20, 0x0, 0x2f, 0xf8, 0x20, 0x0, + 0x3f, 0xfe, 0x24, 0x88, 0x24, 0x70, 0x2f, 0x1c, + 0x20, 0x42, 0x2f, 0xfc, 0x28, 0x44, 0x2f, 0xfc, + 0x60, 0x40, 0x40, 0x44, 0xdf, 0xfe, 0x0, 0x0, + + /* U+8707 "蜇" */ + 0x20, 0xfb, 0xf4, 0x2, 0x10, 0x8, 0x40, 0x7d, + 0xfe, 0x88, 0x4e, 0x41, 0x0, 0x40, 0x3f, 0xf9, + 0x4, 0x24, 0x10, 0x8f, 0xfe, 0x1, 0x13, 0xff, + 0xe0, 0x0, 0x40, + + /* U+8708 "蜈" */ + 0x10, 0x0, 0x10, 0xfe, 0x10, 0x82, 0x7c, 0x82, + 0x54, 0x82, 0x54, 0xfe, 0x54, 0x0, 0x55, 0xff, + 0x7c, 0x10, 0x50, 0x10, 0x15, 0xff, 0x14, 0x10, + 0x1e, 0x28, 0x62, 0x44, 0x1, 0x83, 0x2, 0x0, + + /* U+8709 "蜉" */ + 0x10, 0x0, 0x11, 0xfe, 0x10, 0x2, 0x7c, 0x92, + 0x54, 0x94, 0x54, 0x0, 0x54, 0xfe, 0x54, 0x4, + 0x7c, 0x8, 0x50, 0x10, 0x13, 0xff, 0x14, 0x10, + 0x14, 0x10, 0xfa, 0x10, 0x0, 0x70, + + /* U+870A "蜊" */ + 0x10, 0x2, 0x23, 0xc4, 0x42, 0x2b, 0xe4, 0x55, + 0x5e, 0xaa, 0x91, 0x55, 0x22, 0xaa, 0xe5, 0x79, + 0xaa, 0xa5, 0x54, 0x5a, 0x28, 0xa4, 0x11, 0xc8, + 0x2c, 0x90, 0x40, 0x23, 0x80, + + /* U+870D "蜍" */ + 0x10, 0x10, 0x10, 0x30, 0x10, 0x48, 0x7c, 0x84, + 0x55, 0x2, 0x54, 0xfc, 0x54, 0x10, 0x54, 0x10, + 0x7d, 0xfe, 0x50, 0x10, 0x10, 0x94, 0x14, 0x94, + 0x1d, 0x12, 0x64, 0x10, 0x0, 0x60, + + /* U+8712 "蜒" */ + 0x10, 0x0, 0x11, 0xde, 0x10, 0x44, 0x7c, 0x84, + 0x55, 0x14, 0x55, 0xd7, 0x54, 0x54, 0x7c, 0x54, + 0x51, 0x54, 0x51, 0x54, 0x15, 0x9e, 0x14, 0x80, + 0xfb, 0x40, 0x1, 0x3e, 0x0, 0x0, + + /* U+8713 "蜓" */ + 0x10, 0x0, 0x23, 0xfc, 0x41, 0x23, 0xe4, 0x45, + 0x50, 0x8a, 0xa1, 0x15, 0x7f, 0xaa, 0x24, 0x7c, + 0x48, 0xa3, 0x10, 0x56, 0xf8, 0xa4, 0xf, 0xb4, + 0x0, 0x27, 0xc0, 0x0, 0x0, + + /* U+8715 "蜕" */ + 0x10, 0x84, 0x10, 0xc4, 0x10, 0x48, 0x7d, 0xfe, + 0x55, 0x2, 0x55, 0x2, 0x55, 0x2, 0x55, 0x2, + 0x7d, 0xfe, 0x10, 0x48, 0x14, 0x48, 0x12, 0x49, + 0x7e, 0x89, 0x2, 0x89, 0x3, 0xf, + + /* U+8717 "蜗" */ + 0x10, 0x0, 0x21, 0xf8, 0x42, 0x13, 0xe4, 0x25, + 0x4f, 0xca, 0x82, 0x15, 0x4, 0x3e, 0xff, 0x51, + 0x12, 0x2a, 0x64, 0x54, 0xa8, 0xaa, 0x5f, 0xd0, + 0x20, 0x20, 0x40, 0x43, 0x80, + + /* U+8718 "蜘" */ + 0x11, 0x0, 0x22, 0x0, 0x47, 0x7b, 0xf4, 0x95, + 0x69, 0x2a, 0xd2, 0x55, 0x24, 0xaa, 0xf9, 0x7c, + 0x92, 0xa1, 0x24, 0x53, 0x48, 0xaa, 0x9f, 0xd1, + 0xe0, 0x62, 0x40, 0x80, 0x0, + + /* U+871A "蜚" */ + 0x4, 0x40, 0x8, 0x83, 0xf1, 0xf8, 0x22, 0xf, + 0xc7, 0xe0, 0x88, 0x3f, 0x1f, 0x82, 0xa0, 0x1, + 0x0, 0x7f, 0xf8, 0x84, 0x11, 0x8, 0x23, 0xff, + 0xc0, 0x21, 0x3f, 0xff, 0x0, 0x1, + + /* U+871C "蜜" */ + 0x0, 0x80, 0x1, 0x1, 0xff, 0xfa, 0x0, 0x14, + 0x58, 0xa4, 0x86, 0x89, 0x32, 0x83, 0x85, 0x3b, + 0xf0, 0x81, 0x0, 0x7f, 0xf0, 0x84, 0x21, 0xff, + 0xc2, 0x10, 0x1f, 0xff, 0x80, 0x0, + + /* U+871E "蜞" */ + 0x10, 0x84, 0x10, 0x84, 0x7d, 0xfe, 0x54, 0x84, + 0x54, 0x84, 0x54, 0xfc, 0x54, 0x84, 0x54, 0x84, + 0x7c, 0xfc, 0x10, 0x84, 0x15, 0xff, 0x14, 0x0, + 0x1c, 0x48, 0xfc, 0x84, 0x1, 0x2, + + /* U+8721 "蜡" */ + 0x10, 0x88, 0x21, 0x10, 0x47, 0xfb, 0xe4, 0x45, + 0x48, 0x8a, 0xbf, 0xd5, 0x0, 0x2a, 0x0, 0x7d, + 0xfc, 0xa2, 0x8, 0x57, 0xf0, 0xa8, 0x27, 0xd0, + 0x40, 0xbf, 0x80, 0x41, 0x0, + + /* U+8722 "蜢" */ + 0x10, 0x0, 0x10, 0xfe, 0x10, 0xc, 0x7c, 0x10, + 0x55, 0xff, 0x54, 0x10, 0x54, 0x10, 0x54, 0xf0, + 0x7c, 0x0, 0x11, 0xfe, 0x15, 0x4a, 0x15, 0x4a, + 0x7d, 0x4a, 0x3, 0xff, + + /* U+8723 "蜣" */ + 0x10, 0x0, 0x10, 0x44, 0x10, 0x8, 0x7d, 0xfe, + 0x54, 0x10, 0x54, 0xfe, 0x54, 0x20, 0x54, 0x20, + 0x79, 0xff, 0x50, 0x30, 0x14, 0x30, 0x14, 0x50, + 0x1e, 0xd1, 0x60, 0x92, 0x3, 0xe, + + /* U+8725 "蜥" */ + 0x10, 0x80, 0x10, 0x9e, 0x10, 0x90, 0x7d, 0xd0, + 0x54, 0x90, 0x54, 0x9f, 0x55, 0xd2, 0x56, 0xd2, + 0x7e, 0x92, 0x12, 0x92, 0x14, 0x92, 0x14, 0x92, + 0x1c, 0xa2, 0xf2, 0xa2, 0x0, 0x82, + + /* U+8729 "蜩" */ + 0x10, 0x0, 0x23, 0xfc, 0x44, 0x8b, 0xe9, 0x15, + 0x57, 0xaa, 0xa4, 0x55, 0x7f, 0xbe, 0x81, 0x51, + 0x7a, 0xa3, 0x14, 0x56, 0x28, 0xeb, 0xde, 0x50, + 0x20, 0x40, 0x40, 0x87, 0x80, + + /* U+872E "蜮" */ + 0x10, 0x14, 0x10, 0x12, 0x10, 0x10, 0x7d, 0xfe, + 0x54, 0x10, 0x55, 0xf2, 0x55, 0x32, 0x55, 0x34, + 0x7d, 0x34, 0x51, 0xf4, 0x14, 0x8, 0x14, 0x29, + 0xfd, 0xd9, 0x4, 0x26, 0x0, 0x46, + + /* U+8731 "蜱" */ + 0x10, 0x20, 0x10, 0x20, 0x11, 0xfe, 0x7d, 0x12, + 0x55, 0x12, 0x55, 0xfe, 0x55, 0x12, 0x55, 0x12, + 0x7d, 0xfe, 0x50, 0x60, 0x15, 0x88, 0x17, 0xff, + 0x7c, 0x8, 0x0, 0x8, 0x0, 0x8, + + /* U+8734 "蜴" */ + 0x10, 0x0, 0x23, 0xf8, 0x44, 0x10, 0x88, 0x27, + 0xdf, 0xca, 0xa0, 0x95, 0x7f, 0x2a, 0x0, 0x55, + 0x0, 0xfb, 0xfd, 0x49, 0x48, 0x82, 0x91, 0x49, + 0x22, 0xe4, 0x7f, 0x10, 0x80, 0x4e, + + /* U+8737 "蜷" */ + 0x10, 0x92, 0x10, 0x54, 0x7c, 0x10, 0x55, 0xff, + 0x54, 0x20, 0x54, 0x20, 0x55, 0xff, 0x54, 0x48, + 0x7c, 0x84, 0x11, 0x7e, 0x14, 0x85, 0x14, 0x9a, + 0xfe, 0x82, 0x2, 0x42, 0x0, 0x7e, + + /* U+873B "蜻" */ + 0x10, 0x20, 0x11, 0xfe, 0x10, 0x20, 0x7d, 0xfe, + 0x54, 0x20, 0x55, 0xff, 0x54, 0x0, 0x55, 0xfe, + 0x7d, 0x2, 0x11, 0xfe, 0x15, 0x2, 0x15, 0xfe, + 0x3d, 0x2, 0x43, 0x2, 0x1, 0x1c, + + /* U+873E "蜾" */ + 0x10, 0x0, 0x11, 0xfe, 0x11, 0x22, 0x7d, 0xfe, + 0x55, 0x22, 0x55, 0x22, 0x55, 0xfe, 0x7c, 0x20, + 0x50, 0x20, 0x15, 0xff, 0x14, 0x70, 0x14, 0xa8, + 0xfd, 0xa4, 0x3, 0x23, 0x0, 0x20, + + /* U+873F "蜿" */ + 0x10, 0x10, 0x10, 0x10, 0x11, 0xfe, 0x7d, 0x2, + 0x54, 0x80, 0x54, 0xee, 0x54, 0xb2, 0x55, 0x32, + 0x7d, 0xb2, 0x52, 0xb2, 0x10, 0x56, 0x14, 0x50, + 0x14, 0x91, 0xef, 0x92, 0x3, 0xe, + + /* U+8747 "蝇" */ + 0x10, 0x0, 0x11, 0xfc, 0x11, 0x4, 0x7d, 0x4, + 0x55, 0xfc, 0x54, 0x20, 0x55, 0xfc, 0x56, 0x24, + 0x56, 0x24, 0x79, 0xfc, 0x12, 0x24, 0x16, 0x24, + 0x15, 0xfc, 0x14, 0x21, 0xe8, 0x22, 0x0, 0x3e, + + /* U+8748 "蝈" */ + 0x10, 0x0, 0x23, 0xfc, 0x48, 0xb, 0xf7, 0xd5, + 0x62, 0x2a, 0xc4, 0x55, 0x88, 0xab, 0x39, 0x7e, + 0x32, 0x24, 0x54, 0x5b, 0xe8, 0xb0, 0x1f, 0xe0, + 0x20, 0xbf, 0xc0, 0x80, 0x80, + + /* U+8749 "蝉" */ + 0x10, 0x84, 0x10, 0x48, 0x11, 0xfe, 0x7d, 0x12, + 0x55, 0x12, 0x55, 0xfe, 0x55, 0x12, 0x55, 0x12, + 0x79, 0xfe, 0x50, 0x10, 0x10, 0x10, 0x17, 0xff, + 0x1c, 0x10, 0x64, 0x10, 0x0, 0x10, + + /* U+874C "蝌" */ + 0x10, 0x4, 0x27, 0x88, 0x42, 0x53, 0xe4, 0xa5, + 0x5c, 0x4a, 0x90, 0x95, 0x35, 0x3e, 0xe2, 0x52, + 0x84, 0x25, 0xc, 0x5a, 0xf0, 0xa4, 0x27, 0xc8, + 0x40, 0x90, 0x80, 0x21, 0x0, + + /* U+874E "蝎" */ + 0x10, 0x0, 0x23, 0xf8, 0x44, 0x13, 0xe8, 0x27, + 0xdf, 0xca, 0xa0, 0x95, 0x7f, 0x2a, 0x40, 0x7c, + 0xfe, 0xa2, 0x4, 0x4c, 0x88, 0xaa, 0xd1, 0x50, + 0x27, 0xbf, 0x91, 0x1, 0x0, 0xe, + + /* U+8753 "蝓" */ + 0x10, 0x20, 0x10, 0x50, 0x10, 0x48, 0x7c, 0x84, + 0x55, 0xfb, 0x54, 0x0, 0x55, 0xc2, 0x55, 0x52, + 0x7d, 0x52, 0x51, 0xd2, 0x15, 0x52, 0x15, 0xd2, + 0x3d, 0x42, 0x45, 0x42, 0x1, 0xde, + + /* U+8757 "蝗" */ + 0x10, 0x40, 0x10, 0x40, 0x11, 0xfe, 0x7d, 0x2, + 0x55, 0x2, 0x55, 0xfe, 0x55, 0x2, 0x55, 0xfe, + 0x7c, 0x0, 0x51, 0xfe, 0x10, 0x20, 0x14, 0x20, + 0x15, 0xfe, 0xfc, 0x20, 0x0, 0x20, 0x3, 0xff, + + /* U+8759 "蝙" */ + 0x10, 0x20, 0x20, 0x40, 0x47, 0xfb, 0xe8, 0x15, + 0x50, 0x2a, 0xbf, 0xd5, 0x40, 0x2a, 0x80, 0x7d, + 0xfe, 0xa5, 0x54, 0x5a, 0xa8, 0xb7, 0xf1, 0x6a, + 0xbf, 0x55, 0x40, 0xaa, 0x82, 0x43, + + /* U+8760 "蝠" */ + 0x10, 0x0, 0x11, 0xff, 0x10, 0x0, 0x7c, 0xfe, + 0x54, 0x82, 0x54, 0xfe, 0x54, 0x0, 0x54, 0x0, + 0x7d, 0xff, 0x51, 0x11, 0x15, 0xff, 0x15, 0x11, + 0x1d, 0x11, 0x65, 0xff, 0x1, 0x1, + + /* U+8763 "蝣" */ + 0x11, 0x8, 0x10, 0x90, 0x10, 0x9f, 0x7f, 0xe0, + 0x55, 0x20, 0x55, 0x1e, 0x55, 0xc4, 0x7d, 0x48, + 0x51, 0x5f, 0x12, 0x48, 0x12, 0x48, 0x1a, 0x48, + 0x3e, 0x48, 0x42, 0x48, 0x3, 0xd8, + + /* U+8764 "蝤" */ + 0x10, 0x44, 0x10, 0x28, 0x11, 0xff, 0x7c, 0x28, + 0x54, 0x28, 0x55, 0xfe, 0x55, 0x2a, 0x55, 0x2a, + 0x7d, 0x4e, 0x51, 0x82, 0x15, 0xfe, 0x15, 0x2, + 0x15, 0x2, 0xed, 0xfe, 0x1, 0x2, + + /* U+8765 "蝥" */ + 0x0, 0x41, 0xfc, 0x81, 0x93, 0xf7, 0xfa, 0x42, + 0xa5, 0x9, 0xf, 0x2c, 0x61, 0x1, 0x0, 0x7f, + 0xf8, 0x84, 0x11, 0x8, 0x23, 0xff, 0xc0, 0x20, + 0x1f, 0xff, 0x80, 0x0, 0x0, + + /* U+876E "蝮" */ + 0x0, 0x0, 0x10, 0x80, 0x10, 0xfe, 0x11, 0x0, + 0x7f, 0xfe, 0x55, 0x2, 0x55, 0xfe, 0x55, 0x2, + 0x7d, 0xfe, 0x50, 0x40, 0x14, 0xfe, 0x15, 0x86, + 0x1c, 0xcc, 0x60, 0x30, 0x1, 0xce, 0x2, 0x0, + + /* U+8770 "蝰" */ + 0x10, 0x20, 0x20, 0x40, 0x47, 0xfb, 0xe4, 0x45, + 0x52, 0x4a, 0xff, 0xd5, 0x8, 0x2a, 0x10, 0x79, + 0xfe, 0xa8, 0x40, 0x50, 0x80, 0xaf, 0xe1, 0xc2, + 0xc, 0x3f, 0xc0, + + /* U+8774 "蝴" */ + 0x10, 0x80, 0x21, 0x0, 0x42, 0x78, 0x84, 0x97, + 0xdd, 0x2a, 0x92, 0x55, 0x27, 0xaa, 0xe9, 0x7d, + 0x52, 0xa2, 0xa5, 0x45, 0x78, 0x8a, 0x91, 0x5d, + 0x3f, 0x86, 0x41, 0x8, 0x80, 0x17, + + /* U+8776 "蝶" */ + 0x10, 0x24, 0x11, 0x24, 0x13, 0xfe, 0x7d, 0x24, + 0x55, 0x24, 0x55, 0x3c, 0x55, 0x0, 0x7c, 0xfc, + 0x50, 0x20, 0x55, 0xfe, 0x14, 0x70, 0x14, 0x68, + 0xf8, 0xa4, 0x3, 0x23, 0x0, 0x20, + + /* U+877B "蝻" */ + 0x10, 0x0, 0x10, 0x20, 0x10, 0x20, 0x11, 0xff, + 0x7c, 0x20, 0x55, 0xfe, 0x55, 0x2, 0x55, 0x4a, + 0x7d, 0x12, 0x51, 0xfe, 0x15, 0x22, 0x15, 0xfe, + 0x1d, 0x22, 0xe5, 0x22, 0x1, 0x22, 0x1, 0x2e, + + /* U+877C "蝼" */ + 0x10, 0x20, 0x22, 0x48, 0x42, 0xa3, 0xef, 0xf5, + 0x47, 0xa, 0x95, 0x15, 0xc9, 0xaa, 0x20, 0x78, + 0x40, 0xa7, 0xfc, 0x52, 0x20, 0xa8, 0x41, 0xcd, + 0x1c, 0x4f, 0x0, 0xe1, 0x80, + + /* U+877D "蝽" */ + 0x10, 0x20, 0x10, 0x20, 0x13, 0xfe, 0x10, 0x20, + 0x7c, 0x20, 0x55, 0xfe, 0x54, 0x40, 0x57, 0xff, + 0x7c, 0x88, 0x50, 0xfc, 0x13, 0x6, 0x15, 0x5, + 0x15, 0xfc, 0x7d, 0x4, 0x5, 0xfc, 0x1, 0x4, + + /* U+877E "蝾" */ + 0x10, 0x44, 0x10, 0x44, 0x11, 0xff, 0x10, 0x44, + 0x7c, 0x0, 0x55, 0xff, 0x55, 0x1, 0x55, 0x1, + 0x7c, 0x10, 0x51, 0xff, 0x50, 0x30, 0x14, 0x58, + 0x14, 0x54, 0x7d, 0x93, 0x1, 0x10, 0x0, 0x10, + + /* U+8782 "螂" */ + 0x11, 0x0, 0x11, 0x1e, 0x11, 0xd2, 0x7e, 0x52, + 0x56, 0x52, 0x55, 0xd2, 0x56, 0x54, 0x56, 0x52, + 0x7d, 0xd1, 0x52, 0x11, 0x16, 0x91, 0x16, 0x51, + 0x16, 0xd6, 0xed, 0x30, 0x0, 0x10, + + /* U+8783 "螃" */ + 0x10, 0x20, 0x10, 0x10, 0x11, 0xfe, 0x7c, 0x44, + 0x54, 0x8, 0x55, 0xfe, 0x55, 0x22, 0x54, 0x10, + 0x79, 0xff, 0x50, 0x40, 0x14, 0x7c, 0x14, 0x44, + 0xfa, 0x84, 0x0, 0x84, 0x3, 0x38, + + /* U+8785 "螅" */ + 0x10, 0x20, 0x10, 0x40, 0x11, 0xfe, 0x11, 0x2, + 0x7d, 0x2, 0x55, 0xfe, 0x55, 0x2, 0x55, 0xfe, + 0x7d, 0x2, 0x51, 0xfe, 0x10, 0x0, 0x14, 0x10, + 0x15, 0x92, 0x7d, 0x85, 0x2, 0x44, 0x0, 0x78, + + /* U+8788 "螈" */ + 0x10, 0x0, 0x11, 0xfe, 0x11, 0x10, 0x7d, 0x10, + 0x55, 0x7e, 0x55, 0x42, 0x55, 0x7e, 0x7d, 0x42, + 0x51, 0x42, 0x11, 0x7e, 0x19, 0x10, 0x15, 0x54, + 0xfe, 0x52, 0x2, 0x91, 0x6, 0x30, + + /* U+878B "螋" */ + 0x10, 0x10, 0x11, 0xde, 0x11, 0x12, 0x7d, 0x12, + 0x55, 0xde, 0x55, 0x12, 0x55, 0xfe, 0x7c, 0x10, + 0x50, 0x10, 0x11, 0xfc, 0x14, 0x84, 0x14, 0x48, + 0x7c, 0x28, 0x0, 0x78, 0x3, 0x87, + + /* U+878D "融" */ + 0x0, 0x11, 0xfe, 0x20, 0x0, 0x40, 0x0, 0x87, + 0xe7, 0xc8, 0x4a, 0x9f, 0x95, 0x0, 0x2a, 0xff, + 0x55, 0x4a, 0xaa, 0xa5, 0xe5, 0xea, 0x88, 0x91, + 0x51, 0x22, 0xa2, 0x5f, 0x43, 0x81, + + /* U+8793 "螓" */ + 0x10, 0x20, 0x20, 0x40, 0x4f, 0xf8, 0x82, 0x7, + 0xc4, 0xa, 0xbf, 0x95, 0x10, 0x2b, 0xff, 0x7c, + 0x88, 0xa3, 0xe8, 0x48, 0x88, 0xdf, 0xe1, 0x46, + 0x1f, 0x97, 0x0, 0xc9, 0x80, 0x10, + + /* U+8797 "螗" */ + 0x10, 0x20, 0x20, 0x40, 0x47, 0xf8, 0x88, 0x7, + 0xd2, 0xa, 0xaf, 0x95, 0x49, 0x2a, 0xff, 0x7d, + 0x24, 0xa4, 0xf8, 0x48, 0x0, 0xb7, 0xe3, 0xe8, + 0x48, 0xd0, 0x80, 0xbf, 0x2, 0x42, + + /* U+879F "螟" */ + 0x10, 0x0, 0x27, 0xfc, 0x48, 0xb, 0xef, 0xe5, + 0x50, 0x4a, 0xa0, 0x95, 0x7f, 0x2a, 0x82, 0x79, + 0xfc, 0x20, 0x40, 0x5f, 0xf8, 0xa0, 0xf, 0xc8, + 0x80, 0x20, 0x80, 0x80, 0x80, + + /* U+87A8 "螨" */ + 0x10, 0x40, 0x10, 0x48, 0x11, 0xff, 0x7c, 0x40, + 0x55, 0xfe, 0x54, 0x48, 0x54, 0x48, 0x55, 0xfe, + 0x7d, 0x4a, 0x11, 0x6a, 0x15, 0x5e, 0x15, 0x96, + 0x3d, 0x82, 0x45, 0x2, 0x1, 0xe, + + /* U+87AB "螫" */ + 0x10, 0x41, 0xf9, 0x1, 0xb, 0xff, 0xb2, 0x28, + 0x52, 0xa8, 0xc4, 0x85, 0xb6, 0xa1, 0x2, 0x1, + 0xff, 0xe4, 0x20, 0x9f, 0xfe, 0x42, 0x3, 0xff, + 0xe0, 0x0, 0x40, + + /* U+87AC "螬" */ + 0x10, 0x48, 0x13, 0xff, 0x10, 0x48, 0x7c, 0x48, + 0x57, 0xfe, 0x55, 0xfe, 0x56, 0x4a, 0x56, 0x4a, + 0x79, 0xfe, 0x55, 0xfc, 0x15, 0x4, 0x1d, 0xfc, + 0xe5, 0x4, 0x1, 0xfc, 0x1, 0x4, + + /* U+87AD "螭" */ + 0x10, 0x20, 0x20, 0x40, 0x4f, 0x7b, 0xd4, 0xa5, + 0xa6, 0x4b, 0x5b, 0x96, 0x7f, 0x2c, 0x10, 0x78, + 0x20, 0xa7, 0xfc, 0x49, 0x48, 0xd4, 0x91, 0x6f, + 0xbd, 0xc0, 0x40, 0x83, 0x0, + + /* U+87AF "螯" */ + 0x8, 0x20, 0xfc, 0xf9, 0xfd, 0x20, 0x45, 0x4f, + 0x72, 0x87, 0xc2, 0x10, 0x8a, 0x27, 0x62, 0x81, + 0x0, 0x7f, 0xf0, 0x84, 0x21, 0xff, 0xc2, 0x10, + 0x1f, 0xff, 0x80, 0x0, 0x0, + + /* U+87B3 "螳" */ + 0x10, 0x10, 0x10, 0x94, 0x10, 0x50, 0x7d, 0xfe, + 0x55, 0x2, 0x55, 0xfe, 0x54, 0x84, 0x54, 0x84, + 0x7c, 0xfc, 0x50, 0x10, 0x15, 0xfe, 0x14, 0x10, + 0xfa, 0x10, 0x1, 0xff, + + /* U+87B5 "螵" */ + 0x10, 0x0, 0x11, 0xfe, 0x10, 0x48, 0x7d, 0xfe, + 0x56, 0x4a, 0x56, 0x4a, 0x55, 0xfe, 0x54, 0x0, + 0x79, 0xfc, 0x10, 0x0, 0x17, 0xff, 0x14, 0x10, + 0xfc, 0x94, 0x3, 0x23, 0x0, 0xe0, + + /* U+87BA "螺" */ + 0x10, 0x0, 0x11, 0xfe, 0x11, 0x12, 0x11, 0x12, + 0x7d, 0xfe, 0x55, 0x12, 0x55, 0xfe, 0x54, 0x30, + 0x54, 0x44, 0x7c, 0xf8, 0x10, 0x44, 0x15, 0xfe, + 0x14, 0x10, 0xf8, 0x94, 0x1, 0x13, 0x0, 0x70, + + /* U+87BD "螽" */ + 0x4, 0x0, 0x1f, 0xf0, 0x50, 0x41, 0x1f, 0x7, + 0xe3, 0xe0, 0x78, 0x3, 0xe0, 0x4, 0x28, 0x7e, + 0xfc, 0x95, 0x29, 0x2a, 0x53, 0xf7, 0xe0, 0xc1, + 0x5f, 0xdf, 0x80, 0x0, 0x80, + + /* U+87C0 "蟀" */ + 0x10, 0x20, 0x20, 0x40, 0x4f, 0xfb, 0xf2, 0x5, + 0x69, 0x4a, 0xbc, 0x15, 0x49, 0x2b, 0x2b, 0x7e, + 0xfa, 0xa0, 0x10, 0x40, 0x80, 0xbf, 0xf1, 0x42, + 0x1f, 0x84, 0x0, 0x8, 0x0, + + /* U+87C6 "蟆" */ + 0x10, 0x48, 0x10, 0x48, 0x13, 0xfe, 0x10, 0x48, + 0x7c, 0x0, 0x55, 0xfe, 0x55, 0x2, 0x55, 0xfe, + 0x7d, 0x2, 0x51, 0xfe, 0x50, 0x20, 0x14, 0x20, + 0x17, 0xfe, 0x1c, 0x30, 0xe0, 0x48, 0x3, 0x87, + + /* U+87CA "蟊" */ + 0x3f, 0xfc, 0x0, 0x18, 0x6, 0xe0, 0x1, 0x80, + 0x7f, 0xfe, 0x6, 0x84, 0x7b, 0x90, 0x8, 0x10, + 0x7e, 0xfe, 0x4a, 0x92, 0x4a, 0x92, 0x7e, 0xfe, + 0x8, 0x14, 0xfe, 0xfe, 0x0, 0x1, + + /* U+87CB "蟋" */ + 0x10, 0x0, 0x11, 0xfe, 0x10, 0x20, 0x7c, 0xa4, + 0x54, 0x20, 0x55, 0xff, 0x54, 0x30, 0x7c, 0x68, + 0x50, 0xa7, 0x51, 0x21, 0x14, 0x50, 0x15, 0x42, + 0x7f, 0x45, 0x1, 0x49, 0x0, 0x78, + + /* U+87D1 "蟑" */ + 0x0, 0x20, 0x10, 0x20, 0x11, 0xff, 0x10, 0x84, + 0x7c, 0x48, 0x57, 0xff, 0x54, 0x0, 0x55, 0xfe, + 0x55, 0x2, 0x79, 0xfe, 0x11, 0x2, 0x19, 0xfe, + 0x14, 0x20, 0xff, 0xff, 0x4, 0x20, 0x0, 0x20, + + /* U+87D2 "蟒" */ + 0x10, 0x88, 0x10, 0x88, 0x13, 0xff, 0x7c, 0x88, + 0x54, 0x24, 0x57, 0xfe, 0x54, 0x50, 0x54, 0x88, + 0x7d, 0x4, 0x10, 0x8b, 0x17, 0xff, 0x14, 0x88, + 0x1c, 0x88, 0x65, 0x88, 0x3, 0x8, + + /* U+87D3 "蟓" */ + 0x10, 0x80, 0x10, 0xfc, 0x11, 0x18, 0x7f, 0xfe, + 0x55, 0x22, 0x55, 0x22, 0x55, 0xfe, 0x54, 0x42, + 0x7d, 0xac, 0x50, 0x54, 0x11, 0xb4, 0x14, 0x54, + 0x1c, 0x92, 0xe5, 0x10, 0x0, 0xe0, + + /* U+87DB "蟛" */ + 0x10, 0x82, 0x11, 0xe4, 0x10, 0x84, 0x7c, 0x88, + 0x55, 0xe0, 0x54, 0x2, 0x55, 0xe4, 0x56, 0x2c, + 0x7a, 0x38, 0x11, 0xe1, 0x14, 0x2, 0x15, 0x42, + 0x1d, 0x44, 0x61, 0xe8, 0x0, 0x10, + + /* U+87E0 "蟠" */ + 0x10, 0x0, 0x13, 0xfa, 0x10, 0x22, 0x7c, 0xa4, + 0x57, 0xff, 0x54, 0x70, 0x54, 0xa8, 0x55, 0x26, + 0x7f, 0xfe, 0x51, 0x22, 0x15, 0x22, 0x15, 0xfe, + 0x3d, 0x22, 0x41, 0xfe, 0x1, 0x2, + + /* U+87E5 "蟥" */ + 0x10, 0x48, 0x11, 0xfe, 0x7c, 0x48, 0x54, 0x48, + 0x57, 0xff, 0x54, 0x10, 0x55, 0xfe, 0x7d, 0x12, + 0x51, 0xfe, 0x15, 0x12, 0x15, 0x12, 0x15, 0xfe, + 0x7c, 0x0, 0x0, 0xc4, 0x3, 0x3, + + /* U+87EA "蟪" */ + 0x10, 0x20, 0x11, 0xff, 0x10, 0x20, 0x7d, 0xfe, + 0x55, 0x22, 0x55, 0xfe, 0x55, 0x22, 0x7d, 0xfe, + 0x50, 0x24, 0x13, 0xfe, 0x18, 0x1, 0x14, 0xa2, + 0x7e, 0x85, 0x2, 0x89, 0x0, 0xf8, + + /* U+87EE "蟮" */ + 0x10, 0x84, 0x10, 0x48, 0x11, 0xff, 0x7c, 0x20, + 0x55, 0xfe, 0x54, 0x20, 0x55, 0xff, 0x55, 0x20, + 0x7c, 0xa4, 0x51, 0x5a, 0x15, 0xfe, 0x15, 0x2, + 0x1d, 0x2, 0x65, 0xfe, 0x1, 0x2, + + /* U+87F9 "蟹" */ + 0x10, 0x0, 0x79, 0xf9, 0x20, 0x97, 0xf6, 0xe4, + 0xa5, 0xf, 0xdf, 0x92, 0xa4, 0x7f, 0x7f, 0x81, + 0x0, 0x7f, 0xf0, 0x84, 0x21, 0xff, 0xc0, 0x10, + 0x9f, 0xff, 0x80, 0x1, 0x0, + + /* U+87FE "蟾" */ + 0x10, 0x40, 0x21, 0xf0, 0x44, 0x23, 0xff, 0xf5, + 0x52, 0xa, 0xaa, 0xd5, 0x7f, 0xaa, 0x80, 0x7d, + 0x7e, 0xa2, 0x0, 0x55, 0xf8, 0xab, 0xf7, 0xa4, + 0x20, 0x4f, 0xc0, 0x90, 0x80, + + /* U+8803 "蠃" */ + 0x1, 0x1, 0xff, 0xfc, 0x80, 0x1, 0xff, 0xc0, + 0x0, 0x7, 0xff, 0xf, 0xfe, 0x0, 0x80, 0x77, + 0xb8, 0xab, 0x51, 0xd6, 0xe2, 0xbd, 0x47, 0x1a, + 0xb2, 0xfd, 0x6c, 0x11, 0x80, + + /* U+880A "蠊" */ + 0x0, 0x0, 0x20, 0x20, 0x23, 0xff, 0x22, 0x4, + 0x7a, 0x48, 0x6a, 0xfe, 0x6a, 0x48, 0x6a, 0xfe, + 0x6a, 0x4a, 0x7b, 0xff, 0x22, 0x4a, 0x22, 0xfe, + 0x2a, 0x48, 0x2e, 0xcc, 0x5d, 0x4a, 0xc, 0x48, + + /* U+8813 "蠓" */ + 0x10, 0x88, 0x11, 0xfe, 0x10, 0x88, 0x7c, 0x0, + 0x56, 0xfe, 0x54, 0xf8, 0x54, 0x0, 0x55, 0xfe, + 0x7c, 0x60, 0x51, 0xcc, 0x18, 0x78, 0x15, 0xa8, + 0x1c, 0x54, 0x63, 0x92, 0x0, 0xe0, + + /* U+8815 "蠕" */ + 0x10, 0x0, 0x11, 0xfe, 0x10, 0x20, 0x7e, 0xfe, + 0x56, 0xee, 0x54, 0x20, 0x54, 0xec, 0x7d, 0xff, + 0x50, 0x20, 0x10, 0x40, 0x11, 0xfe, 0x15, 0x4a, + 0xfd, 0x4a, 0x5, 0x4a, 0x1, 0x4e, + + /* U+8816 "蠖" */ + 0x10, 0x48, 0x13, 0xff, 0x10, 0x48, 0x7c, 0x90, + 0x55, 0x7c, 0x55, 0xfe, 0x57, 0x10, 0x55, 0xfe, + 0x7d, 0x10, 0x51, 0xfe, 0x15, 0xfc, 0x14, 0x84, + 0x1c, 0x48, 0xe0, 0x30, 0x3, 0xcf, + + /* U+881B "蠛" */ + 0x10, 0x48, 0x13, 0xff, 0x7c, 0x40, 0x55, 0xfe, + 0x55, 0x4a, 0x55, 0x4a, 0x55, 0xfe, 0x7c, 0x14, + 0x50, 0x10, 0x11, 0xfe, 0x11, 0x10, 0x15, 0xd4, + 0xed, 0x9, 0x2, 0x7a, 0x2, 0x6, + + /* U+8821 "蠡" */ + 0x8, 0x0, 0x3f, 0xf0, 0x40, 0x40, 0x7f, 0x87, + 0xff, 0xe0, 0x60, 0x1f, 0x6e, 0x7, 0x28, 0x73, + 0xc8, 0x78, 0x8d, 0x26, 0x43, 0xf3, 0xf4, 0xa9, + 0x2f, 0xcf, 0xc2, 0x84, 0x7f, 0xff, 0x0, 0x0, + + /* U+8822 "蠢" */ + 0x1, 0x0, 0x3f, 0xfc, 0x3f, 0xfc, 0x2, 0x0, + 0x3d, 0xfc, 0x1f, 0xf8, 0x70, 0x16, 0x1f, 0xf0, + 0xf, 0xf0, 0x8, 0x10, 0x7e, 0x7e, 0x4a, 0x92, + 0x7e, 0x7e, 0xfe, 0xfe, 0x1, 0xc2, + + /* U+8832 "蠲" */ + 0x0, 0x1, 0x27, 0xf4, 0x95, 0x44, 0x55, 0xfd, + 0xfc, 0x2, 0x4, 0x9f, 0xe1, 0x51, 0x3, 0xf7, + 0xf5, 0x5b, 0x55, 0x6d, 0x7d, 0xb4, 0x56, 0xd7, + 0xdf, 0x80, 0x40, 0xe, + + /* U+8839 "蠹" */ + 0x2, 0x3, 0xff, 0xf3, 0xff, 0x10, 0x84, 0x3f, + 0xe3, 0xff, 0xf8, 0x0, 0x4f, 0xfc, 0x70, 0x12, + 0x3f, 0x87, 0xcf, 0xa5, 0x4a, 0x7c, 0xfb, 0xf7, + 0xf0, 0x0, 0x0, + + /* U+883C "蠼" */ + 0x0, 0x0, 0x23, 0xde, 0x22, 0x52, 0x23, 0xde, + 0x7b, 0xde, 0x6a, 0x52, 0x69, 0xac, 0x69, 0xfe, + 0x6b, 0xfc, 0x71, 0xfc, 0x21, 0x20, 0x29, 0xfe, + 0x2b, 0xfc, 0x78, 0x88, 0x0, 0x70, 0x7, 0x8a, + + /* U+8840 "血" */ + 0x0, 0x0, 0x4, 0x0, 0x8, 0x1, 0xff, 0xc2, + 0x44, 0x84, 0x89, 0x9, 0x12, 0x12, 0x24, 0x24, + 0x48, 0x48, 0x90, 0x91, 0x21, 0x22, 0x42, 0x44, + 0x84, 0x89, 0x3f, 0xff, 0x80, + + /* U+8844 "衄" */ + 0x8, 0x0, 0x10, 0x7e, 0x7f, 0x22, 0x55, 0x22, + 0x55, 0x22, 0x55, 0x22, 0x55, 0x22, 0x55, 0x7e, + 0x55, 0x22, 0x55, 0x22, 0x55, 0x22, 0x55, 0xa2, + 0x7e, 0x22, 0x0, 0xff, + + /* U+8845 "衅" */ + 0x10, 0x10, 0x10, 0x11, 0x7e, 0x92, 0x6a, 0x94, + 0x6a, 0x14, 0x6a, 0x10, 0x6a, 0xfe, 0x6a, 0x10, + 0x6a, 0x10, 0x6a, 0xff, 0x6a, 0x10, 0x6a, 0x10, + 0xfc, 0x10, 0x0, 0x10, 0x0, 0x10, + + /* U+884C "行" */ + 0x8, 0x0, 0x19, 0xfe, 0x10, 0x0, 0x20, 0x0, + 0x48, 0x0, 0x8, 0x0, 0x13, 0xff, 0x30, 0x8, + 0x30, 0x8, 0xd0, 0x8, 0x10, 0x8, 0x10, 0x8, + 0x10, 0x8, 0x10, 0x8, 0x10, 0xf0, + + /* U+884D "衍" */ + 0x10, 0x0, 0x64, 0xfc, 0x84, 0x2, 0x0, 0x9, + 0x0, 0x2, 0xc0, 0x8, 0x5f, 0xb0, 0x4, 0x60, + 0x9, 0x42, 0x10, 0x88, 0x21, 0x10, 0x42, 0x20, + 0x84, 0x41, 0x8, 0xe, 0x0, + + /* U+8854 "衔" */ + 0x12, 0x0, 0x48, 0x3d, 0x1e, 0x6, 0x40, 0x9, + 0x0, 0x4, 0xe7, 0xc8, 0x82, 0x31, 0x4, 0xaf, + 0x88, 0x44, 0x10, 0x88, 0x21, 0x10, 0x42, 0x28, + 0x84, 0x61, 0x8, 0xe, 0x0, + + /* U+8857 "街" */ + 0x0, 0x0, 0x12, 0x0, 0x22, 0x1f, 0x4f, 0xc0, + 0x42, 0x0, 0x12, 0x0, 0x2f, 0xdf, 0x20, 0x4, + 0x60, 0x4, 0xa2, 0x4, 0xaf, 0xc4, 0x22, 0x4, + 0x22, 0x4, 0x22, 0xe4, 0x2d, 0x4, 0x20, 0x1c, + + /* U+8859 "衙" */ + 0x10, 0x0, 0x5f, 0xdc, 0x88, 0x2, 0x7e, 0x1, + 0x24, 0x4, 0x4b, 0xc8, 0x91, 0x37, 0xf2, 0x60, + 0x5, 0x4f, 0x88, 0x91, 0x11, 0x22, 0x22, 0x44, + 0x44, 0xf8, 0x89, 0x17, 0x0, + + /* U+8861 "衡" */ + 0x12, 0x0, 0x47, 0xbc, 0x92, 0x2, 0x7e, 0x1, + 0x54, 0x2, 0xa8, 0x9, 0xf7, 0xb2, 0xa2, 0xe7, + 0xc4, 0x42, 0x8, 0xbf, 0x91, 0x8, 0x22, 0x38, + 0x44, 0xcc, 0x8a, 0x7, 0x0, + + /* U+8862 "衢" */ + 0x1f, 0x70, 0x52, 0xb9, 0x3d, 0xc2, 0x4a, 0x81, + 0xf7, 0x4, 0xc5, 0xc8, 0xa1, 0x33, 0xfa, 0xac, + 0x84, 0x6f, 0xc8, 0x92, 0x11, 0x3f, 0x22, 0x48, + 0x44, 0xfe, 0x89, 0x3, 0x0, + + /* U+8863 "衣" */ + 0x1, 0x0, 0x1, 0x0, 0x2, 0x3, 0xff, 0xf0, + 0x8, 0x0, 0x30, 0x0, 0xa1, 0x2, 0x44, 0xc, + 0x50, 0x68, 0xc1, 0x10, 0x80, 0x20, 0x80, 0x40, + 0xc0, 0xb0, 0xc3, 0x80, 0x0, + + /* U+8864 "衤" */ + 0x10, 0x4, 0x0, 0xf, 0xc0, 0x20, 0x20, 0x24, + 0x36, 0x3c, 0x35, 0x22, 0x61, 0x0, 0x80, 0x40, + 0x20, + + /* U+8865 "补" */ + 0x10, 0x20, 0x10, 0x20, 0x0, 0x20, 0x7c, 0x20, + 0x4, 0x20, 0x8, 0x38, 0x10, 0x2c, 0x32, 0x26, + 0x5c, 0x23, 0x14, 0x20, 0x12, 0x20, 0x10, 0x20, + 0x10, 0x20, 0x10, 0x20, 0x10, 0x20, + + /* U+8868 "表" */ + 0x0, 0x0, 0x4, 0x3, 0xff, 0xf0, 0x10, 0x7, + 0xff, 0xc0, 0x40, 0x0, 0x80, 0x7f, 0xff, 0x3, + 0x0, 0xa, 0x30, 0x63, 0x81, 0x42, 0xc, 0x83, + 0x1, 0x61, 0xc3, 0x0, 0x0, + + /* U+8869 "衩" */ + 0x20, 0x0, 0x4f, 0xf8, 0x48, 0x17, 0xd2, 0x20, + 0xa4, 0x82, 0x45, 0x8, 0x42, 0x34, 0x88, 0xf8, + 0x90, 0x51, 0x40, 0x81, 0x81, 0x7, 0x2, 0x19, + 0x4, 0x41, 0x89, 0x0, 0x0, + + /* U+886B "衫" */ + 0x10, 0x4, 0x8, 0x8, 0x8, 0x10, 0x7c, 0x60, + 0x4, 0x80, 0x8, 0x6, 0x12, 0x8, 0x12, 0x30, + 0x3c, 0xc0, 0xd4, 0x1, 0x12, 0x6, 0x10, 0x8, + 0x10, 0x30, 0x10, 0xc0, 0x13, 0x0, + + /* U+886C "衬" */ + 0x20, 0x8, 0x20, 0x10, 0x0, 0x27, 0xdf, 0xf0, + 0x80, 0x82, 0x1, 0x9, 0x42, 0x34, 0x44, 0xb8, + 0x88, 0x50, 0x10, 0x90, 0x21, 0x0, 0x42, 0x0, + 0x84, 0x1, 0x8, 0x3c, 0x0, + + /* U+886E "衮" */ + 0x2, 0x0, 0x2, 0x3, 0xff, 0xf0, 0x0, 0x1, + 0x86, 0xc, 0x43, 0x81, 0x20, 0xc, 0x30, 0x3f, + 0xf0, 0x6, 0x0, 0x34, 0x60, 0xc7, 0x6, 0x86, + 0x11, 0x33, 0x3, 0x81, 0x0, + + /* U+8870 "衰" */ + 0x1, 0x0, 0x1, 0x3, 0xff, 0xf8, 0x0, 0x3, + 0xff, 0x84, 0x1, 0x3f, 0xff, 0x90, 0x4, 0x3f, + 0xf8, 0x6, 0x0, 0x12, 0x20, 0xc3, 0x8e, 0x83, + 0x1, 0x71, 0xc3, 0x0, 0x0, + + /* U+8872 "衲" */ + 0x20, 0x20, 0x20, 0x40, 0x40, 0x83, 0xdf, 0xf0, + 0xa2, 0x22, 0x44, 0x44, 0x88, 0x95, 0x31, 0x72, + 0x52, 0x54, 0x94, 0x8a, 0x19, 0x10, 0x12, 0x20, + 0x24, 0x40, 0x48, 0x87, 0x80, + + /* U+8877 "衷" */ + 0x1, 0x0, 0x1, 0x1, 0xfd, 0xf8, 0x8, 0x3, + 0xff, 0xc4, 0x20, 0x88, 0x41, 0x1f, 0xfe, 0x1, + 0x80, 0x5, 0x18, 0x33, 0x43, 0xc1, 0x0, 0x89, + 0xc1, 0xe0, 0x40, 0x0, 0x0, + + /* U+887D "衽" */ + 0x20, 0x0, 0x47, 0xf8, 0x0, 0x87, 0xc1, 0x0, + 0x82, 0x2, 0x4, 0x4, 0x8, 0x15, 0xff, 0x78, + 0x21, 0x50, 0x40, 0x80, 0x81, 0x1, 0x2, 0x2, + 0x4, 0x7f, 0xc8, 0x0, 0x0, + + /* U+887E "衾" */ + 0x1, 0x0, 0x7, 0x0, 0x31, 0x1, 0x91, 0xcc, + 0x10, 0x63, 0xfc, 0x0, 0x8, 0x1, 0x20, 0x2, + 0x1, 0xff, 0xfc, 0xc, 0x0, 0x68, 0x41, 0x8f, + 0xd, 0xc, 0x22, 0xc7, 0x6, 0x0, + + /* U+887F "衿" */ + 0x20, 0x40, 0x81, 0x0, 0xa, 0x3e, 0x44, 0xa, + 0x18, 0x59, 0x32, 0x44, 0x1a, 0x0, 0xb3, 0xf8, + 0xa0, 0x22, 0x1, 0x8, 0x4, 0x20, 0x20, 0x80, + 0x82, 0x4, 0x0, + + /* U+8881 "袁" */ + 0x1, 0x0, 0xff, 0xf8, 0x4, 0x0, 0x8, 0xf, + 0xff, 0xe0, 0x0, 0xf, 0xfe, 0x10, 0x4, 0x20, + 0x8, 0x7f, 0xf0, 0xc, 0x10, 0x64, 0xc3, 0x86, + 0x19, 0x3, 0x3, 0xe1, 0x80, 0x0, + + /* U+8882 "袂" */ + 0x20, 0x40, 0x20, 0x40, 0x0, 0x40, 0xfb, 0xfc, + 0x8, 0x44, 0x10, 0x44, 0x20, 0x44, 0x28, 0x44, + 0x73, 0xfe, 0xa8, 0x40, 0x20, 0xa0, 0x20, 0x90, + 0x21, 0x18, 0x22, 0x4, 0x2c, 0x2, + + /* U+8884 "袄" */ + 0x0, 0x0, 0x11, 0xfc, 0x10, 0x10, 0x78, 0x10, + 0x8, 0x10, 0x10, 0x10, 0x15, 0xff, 0x34, 0x30, + 0x58, 0x30, 0x14, 0x30, 0x10, 0x48, 0x10, 0x48, + 0x10, 0x84, 0x11, 0x2, 0x12, 0x1, + + /* U+8885 "袅" */ + 0x4, 0x0, 0x7f, 0xe0, 0x80, 0x41, 0x10, 0x82, + 0x6, 0x7, 0xff, 0x0, 0x2, 0x1, 0x3c, 0x2, + 0x1, 0xff, 0xfc, 0x14, 0x0, 0xc4, 0xc6, 0x86, + 0x11, 0x26, 0x3, 0x83, 0x80, + + /* U+8888 "袈" */ + 0x20, 0x0, 0x40, 0x3, 0xfb, 0xf1, 0x14, 0x22, + 0x48, 0x48, 0x90, 0x97, 0x1e, 0x1, 0x0, 0x1, + 0x1, 0xff, 0xfc, 0xc, 0x0, 0x68, 0x61, 0x8f, + 0xd, 0xc, 0x22, 0xc7, 0x8e, 0x0, + + /* U+888B "袋" */ + 0x9, 0x20, 0x22, 0x30, 0x82, 0x3, 0x7f, 0xfa, + 0x4, 0x24, 0xc, 0x40, 0x87, 0x0, 0x80, 0xff, + 0xfe, 0x6, 0x0, 0x14, 0x60, 0xc7, 0x6, 0x86, + 0x11, 0x63, 0x83, 0x0, 0x0, + + /* U+888D "袍" */ + 0x41, 0x0, 0x42, 0x0, 0xf, 0xf7, 0x90, 0x21, + 0x40, 0x45, 0x7c, 0x88, 0x89, 0x3d, 0x12, 0xd3, + 0xe4, 0x94, 0x9, 0x9, 0xea, 0x10, 0x14, 0x20, + 0x28, 0x40, 0x50, 0x7f, 0x0, + + /* U+8892 "袒" */ + 0x40, 0x0, 0x47, 0xf8, 0x88, 0x17, 0x90, 0x21, + 0x20, 0x44, 0x7f, 0x8a, 0x81, 0x39, 0x2, 0xb2, + 0x4, 0x64, 0x8, 0xaf, 0xf1, 0x0, 0x2, 0x0, + 0x4, 0xff, 0xc8, 0x0, 0x0, + + /* U+8896 "袖" */ + 0x40, 0x40, 0x81, 0x2, 0x4, 0x3d, 0xff, 0x14, + 0x44, 0x91, 0x12, 0x44, 0x55, 0x11, 0xb7, 0xfd, + 0x51, 0x14, 0x44, 0x51, 0x11, 0x44, 0x45, 0x1f, + 0xf4, 0x40, 0x40, + + /* U+889C "袜" */ + 0x20, 0x20, 0x20, 0x20, 0x0, 0x20, 0xfb, 0xfe, + 0x8, 0x20, 0x10, 0x20, 0x21, 0xfe, 0x68, 0x20, + 0xf8, 0x70, 0x28, 0xa8, 0x20, 0xa8, 0x21, 0x24, + 0x22, 0x22, 0x24, 0x20, 0x20, 0x20, + + /* U+88A2 "袢" */ + 0x20, 0x20, 0x40, 0x40, 0x8, 0x97, 0xc9, 0x20, + 0x8a, 0x82, 0x4, 0x8, 0xff, 0xb4, 0x10, 0xb8, + 0x20, 0x47, 0xfc, 0x80, 0x81, 0x1, 0x2, 0x2, + 0x4, 0x4, 0x8, 0x8, 0x0, + + /* U+88A4 "袤" */ + 0x0, 0x81, 0xff, 0xfc, 0x0, 0x1, 0xff, 0xe0, + 0xc3, 0x0, 0x78, 0x1f, 0xff, 0x81, 0xa2, 0x3d, + 0xc4, 0x83, 0x0, 0x1a, 0x20, 0xc3, 0x8e, 0x83, + 0x1, 0xe1, 0xc0, 0x0, 0x0, + + /* U+88AB "被" */ + 0x20, 0x20, 0x40, 0x40, 0xf, 0xff, 0xd1, 0x10, + 0xa2, 0x2, 0x44, 0x8, 0xff, 0x35, 0x42, 0xb2, + 0x88, 0x54, 0x90, 0x89, 0x41, 0x11, 0x82, 0x47, + 0x4, 0x91, 0x8a, 0xc0, 0x80, + + /* U+88AD "袭" */ + 0x2, 0x60, 0x8, 0x23, 0xff, 0xf8, 0x24, 0x40, + 0x8b, 0x22, 0xf8, 0x59, 0x1f, 0x40, 0x80, 0xff, + 0xfe, 0x6, 0x0, 0x32, 0x61, 0xc3, 0xc, 0x83, + 0x1, 0xf1, 0xc2, 0x0, 0x0, + + /* U+88B1 "袱" */ + 0x0, 0x20, 0x82, 0x50, 0x84, 0xa0, 0x11, 0x2f, + 0x22, 0x2, 0xdf, 0xcb, 0x88, 0x15, 0x10, 0x6a, + 0x61, 0x64, 0xa0, 0xc9, 0x41, 0x12, 0x82, 0x24, + 0x84, 0x51, 0x8, 0xa1, 0x11, 0x0, + + /* U+88B7 "袷" */ + 0x20, 0x20, 0x20, 0x60, 0x0, 0x90, 0xf9, 0xc, + 0xe, 0x3, 0x11, 0xf8, 0x28, 0x0, 0x78, 0x0, + 0xab, 0xfc, 0x22, 0x4, 0x22, 0x4, 0x22, 0x4, + 0x22, 0x4, 0x23, 0xfc, 0x22, 0x4, + + /* U+88BC "袼" */ + 0x20, 0x80, 0x21, 0x0, 0x5, 0xf7, 0xcc, 0x60, + 0xa4, 0x82, 0xa, 0x8, 0x1c, 0x34, 0xc6, 0xb2, + 0x2, 0x53, 0xf8, 0x84, 0x11, 0x8, 0x22, 0x10, + 0x44, 0x3f, 0x88, 0x41, 0x0, + + /* U+88C1 "裁" */ + 0x10, 0x40, 0x20, 0xa1, 0xf9, 0x20, 0x82, 0xf, + 0xff, 0xe2, 0x8, 0x2, 0x11, 0x7f, 0xa4, 0x10, + 0x48, 0x44, 0xa1, 0xb0, 0xc5, 0x11, 0x12, 0x6, + 0x27, 0x32, 0x48, 0x43, 0x0, + + /* U+88C2 "裂" */ + 0x0, 0x4, 0xff, 0x48, 0x60, 0x91, 0x3d, 0x2d, + 0x12, 0x40, 0xc0, 0x86, 0x1, 0x30, 0xcc, 0xff, + 0xfe, 0x6, 0x0, 0x32, 0x61, 0xc3, 0xc, 0x81, + 0x81, 0xf0, 0xc2, 0x0, 0x0, + + /* U+88C5 "装" */ + 0x8, 0x20, 0x90, 0x40, 0xaf, 0xf8, 0x41, 0x3, + 0x82, 0x19, 0x7f, 0x82, 0x0, 0x0, 0x80, 0xff, + 0xfe, 0x6, 0x0, 0x14, 0x20, 0xc7, 0x86, 0x86, + 0x11, 0x63, 0xc3, 0x0, 0x0, + + /* U+88C6 "裆" */ + 0x20, 0x20, 0x40, 0x44, 0x48, 0x88, 0x9, 0x2f, + 0x92, 0x41, 0x4, 0x4, 0xff, 0x92, 0x1, 0x68, + 0x3, 0x70, 0x4, 0xb7, 0xf9, 0x0, 0x12, 0x0, + 0x24, 0x0, 0x48, 0xff, 0x90, 0x1, + + /* U+88C9 "裉" */ + 0x0, 0x0, 0x21, 0xfe, 0x11, 0x2, 0x1, 0x2, + 0x79, 0x2, 0x9, 0xfe, 0x11, 0x2, 0x11, 0x2, + 0x35, 0xfe, 0x59, 0x20, 0x1d, 0x23, 0x15, 0x14, + 0x11, 0x18, 0x11, 0x2c, 0x11, 0xc2, 0x11, 0x1, + + /* U+88CE "裎" */ + 0x40, 0x0, 0x47, 0xf0, 0x90, 0x27, 0xa0, 0x41, + 0x3f, 0x84, 0x0, 0x8, 0x0, 0x2b, 0xfe, 0xb0, + 0x40, 0xa0, 0x81, 0x1f, 0xf2, 0x2, 0x4, 0x4, + 0x9, 0xff, 0xd0, 0x0, 0x0, + + /* U+88D2 "裒" */ + 0x2, 0x0, 0x4, 0xf, 0xff, 0xc0, 0x0, 0x7c, + 0xf9, 0x0, 0x27, 0xcf, 0x90, 0x2, 0x7f, 0xf8, + 0x18, 0x0, 0x93, 0xc, 0x30, 0xd0, 0x30, 0x78, + 0x30, 0x0, 0x0, + + /* U+88D4 "裔" */ + 0x0, 0x0, 0x2, 0x3, 0xff, 0xf0, 0x38, 0x1, + 0x89, 0x8e, 0xc, 0x25, 0x8e, 0x4, 0x2, 0x7f, + 0xf9, 0x9, 0x12, 0x61, 0xa5, 0x7e, 0x48, 0x84, + 0x91, 0xf9, 0x20, 0x2, 0x40, 0x1c, + + /* U+88D5 "裕" */ + 0x20, 0x0, 0x10, 0xc4, 0x1, 0x82, 0x7b, 0x11, + 0x8, 0x30, 0x10, 0x78, 0x10, 0xcc, 0x29, 0x86, + 0x5b, 0x1, 0xa9, 0xfe, 0x21, 0x2, 0x21, 0x2, + 0x21, 0x2, 0x21, 0xfe, 0x21, 0x2, + + /* U+88D8 "裘" */ + 0x2, 0x20, 0x4, 0x23, 0xff, 0xf8, 0x10, 0x3, + 0xa7, 0x80, 0x40, 0x6, 0x9c, 0x10, 0x84, 0xff, + 0xfe, 0xe, 0x0, 0x24, 0x61, 0xc7, 0xc, 0x83, + 0x1, 0xc1, 0xc0, 0x0, 0x0, + + /* U+88D9 "裙" */ + 0x20, 0x0, 0x27, 0xf8, 0x41, 0x13, 0xc2, 0x20, + 0xbf, 0xe2, 0x8, 0x84, 0x21, 0x15, 0xfe, 0x70, + 0x80, 0x53, 0xf8, 0x8a, 0x11, 0x24, 0x22, 0x8, + 0x44, 0x1f, 0x88, 0x21, 0x0, + + /* U+88DF "裟" */ + 0x20, 0x80, 0x25, 0x22, 0xa, 0x33, 0x24, 0x21, + 0x3, 0x4, 0x38, 0x31, 0x80, 0x0, 0x80, 0xff, + 0xfe, 0xa, 0x0, 0x22, 0x61, 0xc7, 0xc, 0x82, + 0x1, 0xe3, 0xc2, 0x0, 0x0, + + /* U+88E2 "裢" */ + 0x44, 0x20, 0x48, 0x40, 0x8b, 0xff, 0x92, 0x1, + 0x5, 0x4, 0xd2, 0x8, 0xa4, 0x35, 0x3e, 0xb2, + 0x10, 0x54, 0x20, 0x8b, 0xf9, 0x10, 0x82, 0x61, + 0x5, 0xa0, 0xc, 0x3f, 0x80, + + /* U+88E3 "裣" */ + 0x40, 0x40, 0x40, 0xc0, 0x2, 0x47, 0x98, 0x41, + 0x60, 0x64, 0x3f, 0x12, 0x0, 0x68, 0x20, 0xa2, + 0x48, 0xb4, 0x91, 0x4, 0xa2, 0x8, 0x84, 0x1, + 0x8, 0xff, 0xd0, 0x0, 0x0, + + /* U+88E4 "裤" */ + 0x40, 0x20, 0x40, 0x40, 0x9f, 0xff, 0xa1, 0x1, + 0x5f, 0xe4, 0x88, 0x11, 0x24, 0x3a, 0x48, 0xd4, + 0xfe, 0x88, 0x21, 0x10, 0x42, 0x2f, 0xf4, 0x81, + 0x9, 0x2, 0x12, 0x4, 0x0, + + /* U+88E5 "裥" */ + 0x42, 0x0, 0x42, 0xfc, 0x0, 0xf, 0xa0, 0x11, + 0x4f, 0x24, 0x92, 0x49, 0x24, 0xba, 0x79, 0xb4, + 0x92, 0x59, 0x24, 0x92, 0x49, 0x27, 0x92, 0x40, + 0x24, 0x80, 0x49, 0x7, 0x80, + + /* U+88E8 "裨" */ + 0x0, 0x40, 0x10, 0x40, 0x13, 0xfe, 0x7a, 0x22, + 0xa, 0x22, 0x13, 0xfe, 0x12, 0x22, 0x2a, 0x22, + 0x2a, 0x22, 0x53, 0xfe, 0xa8, 0x40, 0x21, 0x90, + 0x27, 0xff, 0x20, 0x10, 0x20, 0x10, 0x20, 0x10, + + /* U+88F0 "裰" */ + 0x40, 0x0, 0x4f, 0x78, 0x82, 0x17, 0xa9, 0x41, + 0x31, 0x84, 0xd6, 0x92, 0x0, 0x5b, 0xfe, 0xd1, + 0x44, 0x8a, 0x49, 0x1c, 0xa2, 0x10, 0xc4, 0x31, + 0x8, 0x8d, 0x92, 0x30, 0x0, + + /* U+88F1 "裱" */ + 0x20, 0x20, 0x20, 0x40, 0x4f, 0xfb, 0xc1, 0x0, + 0xbf, 0xe2, 0x4, 0x4, 0x8, 0x15, 0xff, 0x78, + 0x61, 0x51, 0x44, 0x86, 0x71, 0x14, 0xc2, 0x48, + 0xc4, 0x16, 0xc8, 0x30, 0x0, + + /* U+88F3 "裳" */ + 0x22, 0x18, 0x34, 0x43, 0xff, 0xf4, 0x0, 0x29, + 0xff, 0x42, 0x2, 0x7, 0xfc, 0x0, 0x0, 0x1, + 0x1, 0xff, 0xfc, 0x1c, 0x0, 0x45, 0xc3, 0x84, + 0x19, 0x27, 0x3, 0x81, 0x80, + + /* U+88F4 "裴" */ + 0x4, 0x80, 0x9, 0x3, 0xf3, 0xf0, 0x24, 0xf, + 0xcf, 0xc0, 0x90, 0x1, 0x20, 0x7e, 0x7e, 0x7, + 0x81, 0xff, 0xfc, 0x14, 0x0, 0x48, 0xc3, 0x8e, + 0x19, 0x4, 0x3, 0xc7, 0x80, 0x0, + + /* U+88F8 "裸" */ + 0x0, 0x0, 0x47, 0xf8, 0x89, 0x10, 0x12, 0x2f, + 0x3f, 0xc2, 0x48, 0x88, 0x91, 0x15, 0xfe, 0x68, + 0x41, 0x60, 0x80, 0xff, 0xf9, 0x7, 0x2, 0x15, + 0x4, 0x49, 0x8b, 0x10, 0x90, 0x20, + + /* U+88F9 "裹" */ + 0x1, 0x1, 0xff, 0xfc, 0x0, 0x1, 0xff, 0xc2, + 0x10, 0x87, 0xff, 0xf, 0xfe, 0x0, 0x80, 0xff, + 0xfe, 0xe, 0x80, 0xe0, 0xf6, 0x18, 0x11, 0xcb, + 0x1d, 0xc, 0x3, 0xc7, 0x80, + + /* U+88FC "裼" */ + 0x0, 0x0, 0x8f, 0xe2, 0x20, 0x80, 0x82, 0xf3, + 0xf8, 0x48, 0x22, 0x3f, 0x92, 0x0, 0x72, 0x3, + 0x4f, 0xf4, 0xca, 0x52, 0x29, 0x41, 0x25, 0x9, + 0x14, 0xc8, 0x50, 0x6e, + + /* U+88FE "裾" */ + 0x40, 0x0, 0x9f, 0xf2, 0x40, 0x7d, 0x1, 0x17, + 0xfc, 0x90, 0x2, 0x42, 0x1d, 0xff, 0xb4, 0x20, + 0xd0, 0x82, 0x5f, 0xc9, 0x41, 0x29, 0x4, 0xa7, + 0xf3, 0x10, 0x40, + + /* U+8902 "褂" */ + 0x41, 0x10, 0x42, 0x20, 0x1f, 0x47, 0x88, 0x81, + 0x11, 0x4, 0x23, 0x9, 0xf5, 0x24, 0xa, 0xb1, + 0x13, 0xaf, 0xa1, 0x4, 0x42, 0x8, 0x84, 0x11, + 0x9, 0xda, 0x10, 0x4, 0x0, + + /* U+890A "褊" */ + 0x0, 0x41, 0x1, 0x2, 0x7f, 0xc1, 0x1, 0xf4, + 0x4, 0x5f, 0xf2, 0x40, 0x9, 0x0, 0x6d, 0xfe, + 0xd5, 0x53, 0xd5, 0x49, 0x7f, 0x25, 0x54, 0xa5, + 0x52, 0x95, 0x4c, 0x43, + + /* U+8910 "褐" */ + 0x0, 0x0, 0x9f, 0xe2, 0x40, 0x81, 0x2, 0xf7, + 0xf8, 0x50, 0x22, 0x7f, 0x88, 0x80, 0x53, 0xfe, + 0x91, 0x15, 0x4c, 0x51, 0x49, 0x44, 0x5, 0x1f, + 0xd4, 0x0, 0x90, 0xe, + + /* U+8912 "褒" */ + 0x0, 0x0, 0x4, 0xf, 0xff, 0xc0, 0x0, 0x17, + 0xf8, 0x90, 0x26, 0x7f, 0xa8, 0x20, 0x2f, 0xfc, + 0x87, 0x2, 0xab, 0x80, 0x80, 0xd, 0x11, 0xc3, + 0x89, 0x73, 0x86, 0x1, + + /* U+8913 "褓" */ + 0x41, 0x0, 0x22, 0xfc, 0x22, 0x84, 0xf2, 0x84, + 0x12, 0x84, 0x26, 0xfc, 0x26, 0x0, 0x62, 0x10, + 0xb3, 0xfe, 0x32, 0x10, 0x2a, 0x38, 0x22, 0x54, + 0x22, 0x92, 0x23, 0x10, 0x22, 0x10, + + /* U+8919 "褙" */ + 0x40, 0xa0, 0x41, 0x5c, 0x1e, 0xc7, 0x85, 0x11, + 0x7a, 0x24, 0x3, 0x92, 0xff, 0x39, 0x2, 0xd2, + 0x5, 0xa7, 0xf9, 0x8, 0x12, 0x1f, 0xe4, 0x20, + 0x48, 0x40, 0x90, 0x8f, 0x0, + + /* U+891A "褚" */ + 0x40, 0x40, 0x40, 0x84, 0xf, 0xd7, 0x82, 0x41, + 0x5, 0x4, 0xff, 0xca, 0x18, 0x38, 0x40, 0xd3, + 0xfc, 0x9a, 0x9, 0x7, 0xf2, 0x8, 0x24, 0x10, + 0x48, 0x3f, 0x90, 0x41, 0x0, + + /* U+891B "褛" */ + 0x40, 0x40, 0x4c, 0x98, 0x85, 0x47, 0xbf, 0xe1, + 0xe, 0x4, 0x2a, 0x11, 0x93, 0xa8, 0x40, 0xa0, + 0x81, 0xaf, 0xfd, 0x4, 0x42, 0x10, 0x84, 0x1e, + 0x8, 0x3f, 0x13, 0x81, 0x0, + + /* U+8921 "褡" */ + 0x41, 0x8, 0x42, 0x10, 0x9f, 0xff, 0x89, 0x41, + 0x6, 0x4, 0x12, 0xa, 0xc3, 0x2a, 0xfd, 0xf0, + 0x0, 0x87, 0xf9, 0x8, 0x12, 0x10, 0x24, 0x20, + 0x48, 0x7f, 0x90, 0x81, 0x0, + + /* U+8925 "褥" */ + 0x40, 0x0, 0x4f, 0xf8, 0x90, 0x7, 0xaf, 0xe1, + 0x40, 0x4, 0xff, 0xc9, 0x29, 0x2a, 0x5c, 0xb4, + 0xac, 0x89, 0x91, 0x1f, 0xf2, 0x24, 0x44, 0x84, + 0x89, 0x1, 0x14, 0x1e, 0x0, + + /* U+892A "褪" */ + 0x44, 0x0, 0x45, 0xf8, 0xa, 0x17, 0x87, 0xe1, + 0x8, 0x44, 0xd0, 0x88, 0xbf, 0x35, 0x40, 0xb2, + 0xa4, 0x75, 0x30, 0x8a, 0x21, 0x17, 0x32, 0x28, + 0x4, 0xa0, 0xa, 0x3f, 0x80, + + /* U+892B "褫" */ + 0x20, 0xc, 0x4f, 0xe0, 0x10, 0x87, 0xa1, 0xe1, + 0x42, 0x4, 0xbf, 0x89, 0x49, 0x3a, 0xfc, 0xb5, + 0x24, 0x4a, 0x30, 0x95, 0xe1, 0x2a, 0x42, 0x94, + 0xa5, 0x49, 0x4c, 0xa3, 0x80, + + /* U+8930 "褰" */ + 0x2, 0x1, 0xff, 0xfa, 0x21, 0x11, 0xff, 0xc0, + 0x84, 0xf, 0xff, 0x2, 0x10, 0x7f, 0xff, 0x12, + 0x21, 0xff, 0xfc, 0x1c, 0x0, 0x45, 0x87, 0x86, + 0x11, 0xc3, 0xc0, 0x0, 0x0, + + /* U+8934 "褴" */ + 0x40, 0x90, 0x49, 0x20, 0x12, 0x87, 0xa5, 0xf1, + 0x4a, 0x4, 0x99, 0x11, 0x23, 0x38, 0x0, 0xd7, + 0xfc, 0x89, 0x49, 0x12, 0x92, 0x25, 0x24, 0x4a, + 0x48, 0x94, 0x93, 0xff, 0x80, + + /* U+8936 "褶" */ + 0x40, 0x0, 0xbf, 0xf2, 0x8, 0x7d, 0x29, 0x10, + 0x8c, 0x8e, 0x52, 0x4e, 0x52, 0x20, 0xb7, 0xf9, + 0x30, 0x24, 0x7f, 0x91, 0x2, 0x44, 0x9, 0x1f, + 0xe4, 0x40, 0x80, + + /* U+8941 "襁" */ + 0x0, 0x0, 0x4e, 0xf8, 0x85, 0x12, 0x8a, 0x21, + 0x73, 0x84, 0x82, 0x13, 0x1f, 0x2a, 0x2a, 0xa7, + 0x54, 0xb2, 0xa9, 0x25, 0xf2, 0x8, 0x84, 0x11, + 0x48, 0x3f, 0x91, 0xa0, 0x80, + + /* U+8944 "襄" */ + 0x2, 0x3, 0xff, 0xf0, 0x0, 0x1f, 0x3e, 0x45, + 0x9, 0xf3, 0xe7, 0xff, 0x82, 0x10, 0x7f, 0xf8, + 0x21, 0xf, 0xff, 0xc1, 0x40, 0x18, 0xb3, 0xc5, + 0xc1, 0xe0, 0xc0, + + /* U+895E "襞" */ + 0x0, 0x10, 0xfc, 0x21, 0x9, 0xb3, 0xf2, 0x44, + 0xf, 0xef, 0xc2, 0x38, 0xbf, 0x5f, 0x8, 0x0, + 0x91, 0xff, 0xfc, 0xe, 0x0, 0x62, 0xc7, 0x82, + 0x11, 0x73, 0x83, 0x0, 0x80, + + /* U+895F "襟" */ + 0x42, 0x8, 0x22, 0x8, 0x7, 0xbe, 0xf2, 0x18, + 0x17, 0xac, 0x2a, 0x4a, 0x28, 0x0, 0x73, 0xfc, + 0xb0, 0x0, 0x20, 0x0, 0x27, 0xfe, 0x20, 0x40, + 0x22, 0x4c, 0x2c, 0x42, 0x21, 0xc0, + + /* U+8966 "襦" */ + 0x40, 0x0, 0x47, 0xf8, 0x81, 0x0, 0x3f, 0xff, + 0x44, 0x22, 0xbb, 0x48, 0x10, 0x14, 0x84, 0xf7, + 0xff, 0x60, 0x80, 0xa1, 0x1, 0x1f, 0xe2, 0x29, + 0x44, 0x52, 0x88, 0xa5, 0x11, 0x4e, + + /* U+897B "襻" */ + 0x2, 0x88, 0x44, 0xd0, 0x9a, 0xb7, 0x91, 0x41, + 0x77, 0xe5, 0x4f, 0x8, 0x10, 0x2b, 0xff, 0xa1, + 0x10, 0xad, 0xd9, 0x2f, 0xea, 0x2, 0x4, 0xff, + 0xe8, 0x8, 0x10, 0x70, 0x0, + + /* U+897F "西" */ + 0xff, 0xfe, 0x8, 0x80, 0x11, 0x3, 0xff, 0xe4, + 0x44, 0x48, 0x88, 0x91, 0x11, 0x24, 0x22, 0x58, + 0x3c, 0xa0, 0x9, 0x0, 0x12, 0x0, 0x27, 0xff, + 0xc8, 0x0, 0x80, + + /* U+8981 "要" */ + 0xff, 0xfe, 0x8, 0x81, 0xff, 0xf2, 0x22, 0x24, + 0x44, 0x4f, 0xff, 0x80, 0x0, 0x2, 0x0, 0xff, + 0xfe, 0x10, 0x40, 0x61, 0x80, 0x3e, 0x0, 0x3e, + 0x1f, 0x83, 0x80, + + /* U+8983 "覃" */ + 0xff, 0xfe, 0x8, 0x81, 0xff, 0xf2, 0x22, 0x27, + 0xff, 0xc0, 0x0, 0xf, 0xfe, 0x10, 0x4, 0x3f, + 0xf8, 0x40, 0x10, 0x7f, 0xc7, 0xff, 0xf0, 0x10, + 0x0, 0x20, 0x0, + + /* U+8986 "覆" */ + 0xff, 0xfc, 0x12, 0x7, 0xff, 0x91, 0x22, 0x3b, + 0x70, 0x9f, 0xf6, 0xc0, 0x21, 0xfe, 0x17, 0xf8, + 0x90, 0x24, 0x3f, 0x31, 0xfe, 0x4a, 0x11, 0x7, + 0x85, 0xf3, 0xc0, + + /* U+89C1 "见" */ + 0x3f, 0xf8, 0x40, 0x10, 0x8c, 0x21, 0x18, 0x42, + 0x30, 0x84, 0x61, 0x8, 0xc2, 0x11, 0x84, 0x23, + 0x88, 0xf, 0x0, 0x16, 0x8, 0x6c, 0x13, 0x18, + 0x3c, 0x1f, 0x80, 0x0, 0x0, + + /* U+89C2 "观" */ + 0x1, 0xfe, 0x7d, 0x2, 0x5, 0x2, 0x5, 0x32, + 0x45, 0x32, 0x29, 0x32, 0x29, 0x32, 0x19, 0x32, + 0x19, 0x32, 0x19, 0x3a, 0x18, 0x38, 0x24, 0x79, + 0x60, 0xd9, 0x41, 0x99, 0x7, 0x1e, + + /* U+89C4 "规" */ + 0x10, 0x0, 0x23, 0xf8, 0x44, 0x17, 0xeb, 0x21, + 0x16, 0x42, 0x2c, 0x84, 0x59, 0x7e, 0xb2, 0x21, + 0x64, 0x62, 0xe8, 0xa1, 0xc1, 0x47, 0x94, 0x5b, + 0x28, 0x66, 0x61, 0x8f, 0x0, + + /* U+89C5 "觅" */ + 0x7f, 0xfc, 0x0, 0x10, 0x84, 0x60, 0x80, 0x83, + 0xff, 0x84, 0x1, 0x8, 0x42, 0x10, 0x84, 0x21, + 0x8, 0x47, 0x10, 0xa, 0x8, 0x34, 0x11, 0x88, + 0x3c, 0xf, 0x80, + + /* U+89C6 "视" */ + 0x0, 0x0, 0x47, 0xf8, 0x48, 0x17, 0xd3, 0x20, + 0xa6, 0x42, 0x4c, 0x88, 0x99, 0x11, 0x32, 0x6a, + 0x65, 0x4c, 0xe8, 0x81, 0xc1, 0x7, 0x92, 0x1b, + 0x24, 0x66, 0x49, 0xf, 0x0, + + /* U+89C7 "觇" */ + 0x20, 0x0, 0x43, 0xf8, 0x84, 0x11, 0x8, 0x23, + 0xd2, 0x44, 0x24, 0x88, 0x49, 0x10, 0x92, 0xfd, + 0x25, 0xa, 0x4a, 0x14, 0x84, 0x23, 0x8, 0x46, + 0x30, 0x94, 0x7f, 0x49, 0x41, 0xe, + + /* U+89C8 "览" */ + 0x4, 0x40, 0x89, 0x81, 0x13, 0xfa, 0x28, 0x4, + 0x63, 0x0, 0x1, 0xf, 0xfe, 0x10, 0x4, 0x20, + 0x8, 0x46, 0x10, 0x8e, 0x20, 0x1c, 0x10, 0x78, + 0x23, 0xb0, 0x7c, 0x3f, 0x0, + + /* U+89C9 "觉" */ + 0x2, 0x8, 0x8c, 0x41, 0x12, 0x3f, 0xff, 0x80, + 0x6, 0x0, 0x13, 0xff, 0x8, 0x4, 0x23, 0x10, + 0x8c, 0x42, 0x39, 0x8, 0xe4, 0x5, 0x84, 0x66, + 0x1e, 0x1f, 0xc0, + + /* U+89CA "觊" */ + 0x10, 0x1, 0x2b, 0xfa, 0x54, 0x14, 0xa9, 0x2f, + 0xd2, 0x40, 0x24, 0xbf, 0x49, 0x2, 0x92, 0x5, + 0x24, 0xfa, 0x4a, 0x1, 0x84, 0x3, 0x18, 0x4a, + 0x3f, 0x22, 0x60, 0x87, 0x0, + + /* U+89CB "觋" */ + 0xff, 0xfc, 0x22, 0x8, 0x44, 0x94, 0xa9, 0x29, + 0x52, 0x52, 0xa4, 0xb5, 0xc9, 0x6d, 0x92, 0xb9, + 0x34, 0x20, 0xa0, 0x41, 0x48, 0xf6, 0x9e, 0x9, + 0x20, 0x63, 0x80, + + /* U+89CC "觌" */ + 0x10, 0xfc, 0xfa, 0x8, 0x44, 0x10, 0x89, 0xaf, + 0xf3, 0x40, 0x66, 0x9c, 0xcd, 0x8, 0x9a, 0x51, + 0x34, 0x20, 0x6b, 0xf8, 0xc0, 0x83, 0x92, 0x85, + 0x24, 0x92, 0x70, 0x47, 0x0, + + /* U+89CE "觎" */ + 0xc, 0x0, 0xc, 0x7e, 0x33, 0x42, 0x7e, 0x4a, + 0x0, 0x4a, 0x1, 0x4a, 0x79, 0x4a, 0x4d, 0x4a, + 0x4d, 0x4a, 0x7d, 0x4a, 0x4d, 0x18, 0x7d, 0x19, + 0x49, 0x29, 0x49, 0x69, 0x5b, 0x87, + + /* U+89CF "觏" */ + 0x24, 0x7d, 0xfe, 0x88, 0x91, 0x13, 0xf3, 0xa2, + 0x47, 0x5f, 0xee, 0x84, 0x1d, 0x3f, 0x3a, 0x92, + 0x74, 0xfc, 0xfa, 0x48, 0xe7, 0xf9, 0xd8, 0x25, + 0xb0, 0x5b, 0x63, 0xa7, 0x0, + + /* U+89D0 "觐" */ + 0x42, 0x1, 0xff, 0xf9, 0xa, 0x13, 0xf4, 0xa0, + 0x89, 0x4f, 0xf2, 0xa2, 0x65, 0x3f, 0xca, 0x8, + 0x95, 0xfe, 0x28, 0x20, 0xc3, 0xf9, 0x90, 0x85, + 0x3f, 0xea, 0x40, 0x23, 0x0, + + /* U+89D1 "觑" */ + 0x8, 0x0, 0x1e, 0xf8, 0x21, 0x13, 0xfa, 0xa4, + 0x95, 0x49, 0xca, 0x96, 0x55, 0x27, 0xaa, 0x40, + 0x54, 0x94, 0xa9, 0xac, 0xc2, 0xd9, 0x94, 0xa3, + 0x33, 0xea, 0x68, 0x23, 0x0, + + /* U+89D2 "角" */ + 0x0, 0x0, 0x20, 0x1, 0xfe, 0x4, 0x8, 0x20, + 0x41, 0xff, 0xfa, 0x10, 0x48, 0x41, 0x3f, 0xfc, + 0x84, 0x12, 0x10, 0x4f, 0xff, 0x21, 0x5, 0x4, + 0x14, 0x10, 0x60, 0xf, + + /* U+89D6 "觖" */ + 0x0, 0x20, 0x40, 0x40, 0xf0, 0x82, 0x21, 0xc, + 0x9f, 0xdf, 0xc4, 0x94, 0x89, 0x29, 0x12, 0x7e, + 0x24, 0xa7, 0xfd, 0x48, 0x83, 0xf2, 0x85, 0x25, + 0xa, 0x59, 0x30, 0xa1, 0x47, 0x81, + + /* U+89DA "觚" */ + 0x0, 0x0, 0x40, 0x38, 0xf3, 0xa2, 0x25, 0x48, + 0x8a, 0x9f, 0xd5, 0x14, 0xaa, 0x29, 0x54, 0x7e, + 0xa8, 0xa5, 0x51, 0x4a, 0xa3, 0xf5, 0xa5, 0x2d, + 0x4a, 0x5a, 0xb0, 0xda, 0xc7, 0x81, + + /* U+89DC "觜" */ + 0x8, 0x89, 0x3e, 0xc4, 0x8c, 0x13, 0xe1, 0xb4, + 0x78, 0x7f, 0x83, 0x4, 0x17, 0xfe, 0x21, 0x8, + 0x7f, 0xe2, 0x10, 0x88, 0x42, 0x3f, 0xf9, 0x84, + 0x2c, 0x13, 0x80, + + /* U+89DE "觞" */ + 0x0, 0x60, 0x40, 0x81, 0xf3, 0xfe, 0x44, 0x7, + 0xff, 0xca, 0x41, 0x14, 0x84, 0x3f, 0x30, 0x52, + 0xfe, 0xa4, 0x95, 0xf9, 0x4a, 0x94, 0x95, 0x22, + 0x58, 0x48, 0xa3, 0xa7, 0x0, + + /* U+89E3 "解" */ + 0x0, 0x0, 0x41, 0xfd, 0xf0, 0x8a, 0x21, 0x19, + 0x82, 0x2e, 0xcb, 0x94, 0xe0, 0x29, 0x28, 0x7e, + 0x50, 0xa5, 0xfd, 0x4a, 0x43, 0xf0, 0x85, 0x3f, + 0xea, 0x42, 0x20, 0x84, 0x47, 0x8, + + /* U+89E5 "觥" */ + 0x20, 0x10, 0x78, 0x29, 0x12, 0x54, 0x44, 0xaf, + 0xe5, 0x8a, 0x42, 0x14, 0x84, 0x3f, 0x76, 0x52, + 0x48, 0xa4, 0x91, 0xf9, 0x22, 0x92, 0x55, 0x28, + 0xb8, 0x51, 0x63, 0xc3, 0x80, + + /* U+89E6 "触" */ + 0x0, 0x10, 0x20, 0x10, 0x3c, 0x10, 0x44, 0x10, + 0x88, 0xfe, 0xfe, 0x92, 0x52, 0x92, 0x52, 0x92, + 0x7e, 0x92, 0x52, 0xfe, 0x52, 0x90, 0x7e, 0x10, + 0x52, 0x14, 0x52, 0x12, 0xc2, 0xfe, 0x8e, 0x1, + + /* U+89EB "觫" */ + 0x10, 0x20, 0x78, 0x41, 0x17, 0xfa, 0x41, 0xf, + 0xe2, 0xa, 0x5f, 0x94, 0xc9, 0x3f, 0x92, 0x52, + 0xfc, 0xa4, 0x41, 0xf9, 0xc2, 0x95, 0x45, 0x2a, + 0x58, 0x64, 0x63, 0x88, 0x0, + + /* U+89EF "觯" */ + 0x0, 0x0, 0x30, 0x44, 0x3c, 0x28, 0x44, 0xfe, + 0x88, 0x92, 0xfe, 0x92, 0x52, 0xfe, 0x52, 0x92, + 0x7e, 0x92, 0x52, 0xfe, 0x52, 0x10, 0x7e, 0x10, + 0x53, 0xff, 0x52, 0x10, 0x82, 0x10, 0x8e, 0x10, + + /* U+89F3 "觳" */ + 0x8, 0x1, 0xfe, 0xf0, 0x21, 0x23, 0xfa, 0x40, + 0x4, 0x9f, 0xf1, 0xcf, 0x0, 0x1f, 0x7e, 0xc4, + 0x84, 0xf6, 0x90, 0xfd, 0x22, 0x49, 0x87, 0xf3, + 0x9, 0x29, 0xa2, 0xe1, 0x80, + + /* U+8A00 "言" */ + 0x1, 0x0, 0x2, 0x3, 0xff, 0xf8, 0x0, 0x3, + 0xff, 0x80, 0x0, 0x0, 0x0, 0x1f, 0xfc, 0x0, + 0x0, 0x7f, 0xf0, 0x80, 0x21, 0x0, 0x42, 0x0, + 0x87, 0xff, 0x8, 0x2, 0x0, + + /* U+8A07 "訇" */ + 0x8, 0x0, 0x20, 0x0, 0x7f, 0xf9, 0x0, 0x12, + 0x10, 0x2f, 0xff, 0x60, 0x0, 0x9f, 0xf9, 0x0, + 0x2, 0x3f, 0xc4, 0xff, 0xc9, 0x0, 0x92, 0x1, + 0x27, 0xfe, 0x40, 0x1, 0x80, 0x3e, + + /* U+8A3E "訾" */ + 0x8, 0x80, 0x11, 0x11, 0x3a, 0xe2, 0x46, 0x14, + 0xa8, 0x4e, 0x8f, 0xa0, 0x0, 0x7f, 0xff, 0x0, + 0x0, 0x7f, 0xe0, 0x0, 0x1, 0xff, 0x80, 0x0, + 0x7, 0xfe, 0xf, 0xfc, 0x10, 0x8, + + /* U+8A48 "詈" */ + 0x7f, 0xfc, 0x88, 0x89, 0x11, 0x13, 0xff, 0xe0, + 0x10, 0x1f, 0xff, 0xc0, 0x0, 0x1f, 0xfc, 0x0, + 0x0, 0x7f, 0xf0, 0x0, 0x1, 0xff, 0xc4, 0x0, + 0x87, 0xff, 0x10, 0x2, 0x0, + + /* U+8A79 "詹" */ + 0xc, 0x0, 0x3f, 0xe0, 0xc0, 0x83, 0xff, 0xf2, + 0x23, 0x5, 0x91, 0x8f, 0xff, 0x90, 0x0, 0x2f, + 0xfc, 0x40, 0x0, 0x9f, 0xe1, 0x7f, 0xe4, 0x80, + 0x49, 0xff, 0xa2, 0x1, 0x0, + + /* U+8A89 "誉" */ + 0x1, 0x8, 0x22, 0x20, 0x40, 0x87, 0xff, 0xf0, + 0x92, 0x7, 0xff, 0x30, 0x1, 0x9f, 0xfc, 0x0, + 0x0, 0x7f, 0xf0, 0x0, 0x1, 0xff, 0xc2, 0x0, + 0x87, 0xff, 0x8, 0x2, 0x0, + + /* U+8A8A "誊" */ + 0x21, 0x18, 0x32, 0x41, 0xff, 0xf0, 0x10, 0xf, + 0xff, 0xe1, 0x24, 0xc, 0x46, 0x6f, 0x73, 0x3f, + 0xf8, 0x0, 0x0, 0x7f, 0xc1, 0xff, 0xc2, 0x0, + 0x84, 0x1, 0xf, 0xfe, 0x0, + + /* U+8A93 "誓" */ + 0x11, 0xfd, 0xfa, 0x0, 0x44, 0x7, 0xef, 0xf1, + 0x31, 0xc, 0xe2, 0x0, 0x40, 0x3f, 0x7e, 0x3f, + 0xf8, 0x0, 0x0, 0xff, 0xe0, 0x0, 0x7, 0xff, + 0x8f, 0xff, 0x10, 0x2, 0x0, + + /* U+8B07 "謇" */ + 0x1, 0x1, 0xff, 0xfe, 0x11, 0x8, 0xff, 0x83, + 0xff, 0xc0, 0x88, 0x3f, 0xff, 0x8c, 0x98, 0xe6, + 0xce, 0x7f, 0xf0, 0x0, 0x0, 0xff, 0x83, 0xff, + 0x84, 0x1, 0xf, 0xfe, 0x0, + + /* U+8B26 "謦" */ + 0x0, 0x1, 0xfe, 0xf0, 0x42, 0x23, 0xfc, 0x30, + 0xf, 0xcf, 0xd0, 0x94, 0x9e, 0x56, 0xe3, 0xff, + 0xfc, 0x0, 0x0, 0x7f, 0xc1, 0xff, 0xc0, 0x0, + 0x7, 0xff, 0xf, 0xfe, 0x10, 0x4, + + /* U+8B66 "警" */ + 0x24, 0x20, 0xfe, 0xfd, 0xf9, 0x14, 0x15, 0x47, + 0xa1, 0xf, 0x4d, 0x81, 0x40, 0xff, 0xff, 0x0, + 0x0, 0x7f, 0xe0, 0xff, 0xe0, 0x0, 0x7, 0xff, + 0x8f, 0xff, 0x10, 0x2, 0x0, + + /* U+8B6C "譬" */ + 0x0, 0x0, 0xfc, 0x21, 0xb, 0xb3, 0xf2, 0x44, + 0x1f, 0xef, 0xc4, 0x28, 0xbf, 0x1f, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0x7f, 0xc1, 0xff, 0xc0, 0x0, + 0xf, 0xff, 0xf, 0xfe, 0x20, 0x4, + + /* U+8BA0 "讠" */ + 0x20, 0x20, 0x20, 0x0, 0x1e, 0x4, 0x8, 0x10, + 0x20, 0x40, 0x81, 0x63, 0x0, + + /* U+8BA1 "计" */ + 0x40, 0x40, 0x40, 0x80, 0x61, 0x0, 0x2, 0x0, + 0x4, 0x1c, 0xff, 0xc8, 0x10, 0x10, 0x20, 0x20, + 0x40, 0x40, 0x80, 0x81, 0x1, 0x22, 0x2, 0x84, + 0x6, 0x8, 0x0, 0x10, 0x0, + + /* U+8BA2 "订" */ + 0x40, 0x0, 0x4f, 0xfc, 0x40, 0x40, 0x0, 0x80, + 0x1, 0x1c, 0x2, 0x8, 0x4, 0x10, 0x8, 0x20, + 0x10, 0x40, 0x20, 0x80, 0x41, 0x40, 0x83, 0x1, + 0x4, 0x6, 0x0, 0x78, 0x0, + + /* U+8BA3 "讣" */ + 0x0, 0x80, 0x61, 0x0, 0x62, 0x0, 0x4, 0x0, + 0xe, 0x1c, 0x16, 0x8, 0x26, 0x10, 0x46, 0x20, + 0x84, 0x41, 0x0, 0x92, 0x1, 0x44, 0x3, 0x8, + 0x4, 0x10, 0x0, 0x20, 0x0, + + /* U+8BA4 "认" */ + 0x0, 0x40, 0x40, 0x80, 0x41, 0x0, 0x2, 0x0, + 0x4, 0x1c, 0x8, 0x8, 0x18, 0x10, 0x50, 0x20, + 0xa0, 0x41, 0x20, 0x92, 0x41, 0xc8, 0xc2, 0x20, + 0x80, 0xc0, 0xc1, 0x0, 0x0, + + /* U+8BA5 "讥" */ + 0x0, 0x0, 0x21, 0xf8, 0x11, 0x8, 0x1, 0x8, + 0x1, 0x8, 0xe1, 0x8, 0x21, 0x8, 0x21, 0x8, + 0x21, 0x8, 0x21, 0x8, 0x21, 0x8, 0x29, 0x9, + 0x32, 0x9, 0x22, 0x9, 0x4, 0xe, + + /* U+8BA6 "讦" */ + 0x0, 0x0, 0x47, 0xf8, 0x41, 0x0, 0x2, 0x0, + 0x4, 0x0, 0x8, 0x38, 0x10, 0x13, 0xff, 0x20, + 0x40, 0x40, 0x80, 0x81, 0x1, 0x22, 0x3, 0x84, + 0x4, 0x8, 0x0, 0x10, 0x0, + + /* U+8BA7 "讧" */ + 0x40, 0x0, 0x47, 0xf8, 0x41, 0x0, 0x2, 0x0, + 0x4, 0x1c, 0x8, 0x8, 0x10, 0x10, 0x20, 0x20, + 0x40, 0x40, 0x80, 0x91, 0x1, 0x42, 0x3, 0x4, + 0x4, 0xff, 0xc0, + + /* U+8BA8 "讨" */ + 0x0, 0x10, 0xc0, 0x20, 0x40, 0x40, 0x3f, 0xf0, + 0x1, 0x1c, 0x2, 0x8, 0x84, 0x10, 0x88, 0x20, + 0x90, 0x41, 0x20, 0x80, 0x41, 0x20, 0x82, 0x81, + 0x6, 0x2, 0x8, 0x3c, 0x0, + + /* U+8BA9 "让" */ + 0x0, 0x40, 0x40, 0x80, 0x41, 0x0, 0x2, 0x0, + 0x7, 0xdc, 0x8, 0x8, 0x10, 0x10, 0x20, 0x20, + 0x40, 0x40, 0x80, 0x91, 0x1, 0x42, 0x3, 0x4, + 0x4, 0xff, 0xc0, + + /* U+8BAA "讪" */ + 0x0, 0x20, 0x40, 0x40, 0xc0, 0x80, 0x81, 0x0, + 0x22, 0x3c, 0x44, 0x48, 0x88, 0x91, 0x11, 0x22, + 0x22, 0x44, 0x44, 0x88, 0x89, 0x11, 0x12, 0xa2, + 0x26, 0x7f, 0xc0, 0x0, 0x80, + + /* U+8BAB "讫" */ + 0x41, 0x0, 0xc2, 0x0, 0xcf, 0xf8, 0x30, 0x0, + 0x40, 0x1c, 0x7f, 0x8, 0x6, 0x10, 0x18, 0x20, + 0x60, 0x41, 0x80, 0x82, 0x9, 0x48, 0x12, 0xa0, + 0x26, 0x40, 0x48, 0xff, 0x0, + + /* U+8BAD "训" */ + 0x42, 0x22, 0x64, 0x44, 0x48, 0x88, 0x11, 0x1e, + 0x22, 0x24, 0x44, 0x48, 0x88, 0x91, 0x11, 0x22, + 0x22, 0x54, 0x44, 0xc8, 0x89, 0x21, 0x10, 0x42, + 0x21, 0x0, 0x40, + + /* U+8BAE "议" */ + 0x40, 0x80, 0x48, 0x88, 0x50, 0x90, 0x10, 0x20, + 0x20, 0x9c, 0x41, 0x8, 0x42, 0x10, 0x88, 0x20, + 0x90, 0x41, 0x40, 0x81, 0x1, 0x45, 0x83, 0x11, + 0x80, 0xc0, 0xc2, 0x0, 0x80, + + /* U+8BAF "讯" */ + 0x4f, 0xf8, 0x21, 0x8, 0x1, 0x8, 0x1, 0x8, + 0x1, 0x8, 0x61, 0x8, 0x2f, 0xe8, 0x21, 0x8, + 0x21, 0x8, 0x21, 0x8, 0x21, 0x9, 0x29, 0x9, + 0x31, 0x6, 0x1, 0x6, + + /* U+8BB0 "记" */ + 0x63, 0xfc, 0x60, 0x8, 0x40, 0x10, 0x0, 0x2e, + 0x0, 0x44, 0x7f, 0x88, 0x80, 0x11, 0x0, 0x22, + 0x0, 0x44, 0x0, 0x88, 0x9, 0x50, 0x13, 0x20, + 0x64, 0x3f, 0x80, + + /* U+8BB2 "讲" */ + 0x41, 0x10, 0x42, 0x20, 0x4, 0x40, 0x3f, 0xee, + 0x11, 0x4, 0x22, 0x8, 0x44, 0x17, 0xff, 0x21, + 0x10, 0x42, 0x20, 0xa8, 0x41, 0x90, 0x82, 0x41, + 0x1, 0x82, 0x0, + + /* U+8BB3 "讳" */ + 0x40, 0x40, 0xc0, 0x80, 0xdf, 0xf8, 0x2, 0x0, + 0x4, 0x1c, 0x7f, 0x88, 0x10, 0x10, 0x20, 0x2f, + 0xfe, 0x40, 0x84, 0x81, 0x9, 0x22, 0x13, 0x85, + 0xc4, 0x8, 0x0, 0x10, 0x0, + + /* U+8BB4 "讴" */ + 0x40, 0x0, 0x63, 0xfe, 0x32, 0x0, 0x2, 0x84, + 0x2, 0x44, 0xe2, 0x28, 0x22, 0x28, 0x22, 0x10, + 0x22, 0x18, 0x22, 0x28, 0x22, 0x44, 0x22, 0x44, + 0x2a, 0x80, 0x32, 0x0, 0x23, 0xff, + + /* U+8BB5 "讵" */ + 0x0, 0x0, 0x23, 0xfe, 0x12, 0x0, 0x2, 0x0, + 0x3, 0xfc, 0xe2, 0x4, 0x22, 0x4, 0x22, 0x4, + 0x22, 0x4, 0x23, 0xfc, 0x22, 0x0, 0x2a, 0x0, + 0x32, 0x0, 0x23, 0xff, + + /* U+8BB6 "讶" */ + 0x40, 0x0, 0x4f, 0xfc, 0xc0, 0x20, 0x10, 0x40, + 0x20, 0x9c, 0x41, 0x8, 0xff, 0x90, 0x14, 0x20, + 0x48, 0x40, 0x90, 0xa2, 0x21, 0x88, 0x42, 0x20, + 0x81, 0x83, 0x0, 0x1c, 0x0, + + /* U+8BB7 "讷" */ + 0x0, 0x20, 0x40, 0x40, 0x40, 0x80, 0x1f, 0xf0, + 0x22, 0x3c, 0x44, 0x48, 0x88, 0x91, 0x29, 0x22, + 0x52, 0x44, 0x94, 0x8a, 0x9, 0x10, 0x12, 0xa0, + 0x26, 0x40, 0x40, 0x87, 0x80, + + /* U+8BB8 "许" */ + 0x41, 0x0, 0x44, 0x0, 0x8f, 0xf0, 0x32, 0x0, + 0x44, 0x1c, 0x8, 0x8, 0x10, 0x10, 0x20, 0x2f, + 0xfe, 0x40, 0x80, 0x81, 0x1, 0x22, 0x2, 0x84, + 0x6, 0x8, 0x0, 0x10, 0x0, + + /* U+8BB9 "讹" */ + 0x1, 0x20, 0x21, 0x20, 0x11, 0x20, 0x2, 0x22, + 0x6, 0x24, 0xe6, 0x28, 0x2a, 0x38, 0x2a, 0x20, + 0x22, 0x60, 0x22, 0xa0, 0x22, 0x21, 0x22, 0x21, + 0x2a, 0x21, 0x32, 0x23, 0x22, 0x1e, + + /* U+8BBA "论" */ + 0x40, 0x40, 0x41, 0x40, 0x42, 0x40, 0x8, 0x40, + 0x20, 0x5c, 0x80, 0x8, 0x82, 0x11, 0x8, 0x22, + 0x60, 0x47, 0x0, 0x88, 0x1, 0x10, 0x22, 0xa0, + 0x46, 0x41, 0x88, 0x7e, 0x0, + + /* U+8BBC "讼" */ + 0x40, 0x10, 0x41, 0x20, 0x44, 0x20, 0x8, 0x40, + 0x20, 0x5c, 0x40, 0x89, 0x10, 0x90, 0x20, 0x20, + 0x40, 0x41, 0x0, 0x84, 0x21, 0x8, 0x43, 0xa0, + 0x44, 0xff, 0x80, 0x0, 0x80, + + /* U+8BBD "讽" */ + 0x0, 0x0, 0x67, 0xfc, 0x34, 0x4, 0x4, 0x4, + 0x5, 0x14, 0xe5, 0x24, 0x24, 0xa4, 0x24, 0xa4, + 0x24, 0x44, 0x24, 0x44, 0x24, 0xa4, 0x35, 0x25, + 0x2d, 0x15, 0x8, 0x5, 0x8, 0x3, + + /* U+8BBE "设" */ + 0x21, 0xf8, 0x11, 0x8, 0x1, 0x8, 0x1, 0x4, + 0xe6, 0x2, 0x20, 0x0, 0x23, 0xfc, 0x21, 0x4, + 0x21, 0x8, 0x20, 0x98, 0x28, 0x50, 0x30, 0x60, + 0x21, 0x98, 0xe, 0x6, + + /* U+8BBF "访" */ + 0x0, 0x40, 0x80, 0x80, 0x80, 0x80, 0x3f, 0xf0, + 0x10, 0x0, 0x20, 0x38, 0x40, 0x10, 0xfc, 0x21, + 0x8, 0x42, 0x10, 0x84, 0x21, 0x8, 0x42, 0xa0, + 0x86, 0x41, 0x1, 0x2, 0x4, 0x38, + + /* U+8BC0 "诀" */ + 0x0, 0x40, 0x80, 0x80, 0x81, 0x0, 0x1f, 0xc0, + 0x4, 0x9c, 0x9, 0x8, 0x12, 0x10, 0x44, 0x2f, + 0xfe, 0x41, 0x80, 0x83, 0x1, 0x49, 0x3, 0x21, + 0x4, 0x81, 0x2, 0x1, 0x80, + + /* U+8BC1 "证" */ + 0x27, 0xfe, 0x20, 0x40, 0x0, 0x80, 0x11, 0xe, + 0x22, 0x4, 0x47, 0xc8, 0x88, 0x11, 0x10, 0x22, + 0x20, 0x54, 0x40, 0xc8, 0x81, 0x11, 0x0, 0xff, + 0xe0, + + /* U+8BC2 "诂" */ + 0x40, 0x40, 0x40, 0x80, 0x41, 0x0, 0x3f, 0xf0, + 0x4, 0x1c, 0x8, 0x8, 0x10, 0x11, 0xfe, 0x22, + 0x4, 0x44, 0x8, 0x88, 0x11, 0x10, 0x22, 0xa0, + 0x46, 0x7f, 0x88, 0x81, 0x0, + + /* U+8BC3 "诃" */ + 0x40, 0x0, 0xcf, 0xfc, 0xc0, 0x10, 0x0, 0x20, + 0x3e, 0x5c, 0x44, 0x88, 0x89, 0x11, 0x12, 0x22, + 0x24, 0x44, 0x48, 0x8f, 0x91, 0x0, 0x22, 0x80, + 0x46, 0x0, 0x80, 0xe, 0x0, + + /* U+8BC4 "评" */ + 0x40, 0x0, 0xcf, 0xf8, 0x81, 0x0, 0x22, 0x20, + 0x24, 0x5c, 0x49, 0x8, 0x10, 0x10, 0x20, 0x2f, + 0xfe, 0x40, 0x80, 0x81, 0x1, 0x2, 0x2, 0x84, + 0x6, 0x8, 0x0, 0x10, 0x0, + + /* U+8BC5 "诅" */ + 0x0, 0x0, 0x47, 0xf0, 0xc8, 0x20, 0x10, 0x40, + 0x20, 0x9c, 0x7f, 0x8, 0x82, 0x11, 0x4, 0x22, + 0x8, 0x47, 0xf0, 0x88, 0x21, 0xd0, 0x42, 0x20, + 0x81, 0xff, 0xc0, + + /* U+8BC6 "识" */ + 0x0, 0x0, 0x47, 0xf8, 0x48, 0x10, 0x10, 0x20, + 0x20, 0x5c, 0x40, 0x88, 0x81, 0x11, 0x2, 0x23, + 0xfc, 0x40, 0x0, 0x80, 0x41, 0x48, 0x43, 0x20, + 0x84, 0x40, 0x81, 0x0, 0x80, + + /* U+8BC8 "诈" */ + 0x0, 0x0, 0x82, 0x0, 0x88, 0x0, 0x9f, 0xf0, + 0x28, 0x0, 0x90, 0x3a, 0x20, 0x10, 0x7e, 0x20, + 0x80, 0x41, 0x0, 0x82, 0x1, 0x7, 0xe2, 0x48, + 0x5, 0x10, 0xc, 0x20, 0x10, 0x40, + + /* U+8BC9 "诉" */ + 0x0, 0x0, 0x47, 0xf8, 0x48, 0x0, 0x10, 0x0, + 0x20, 0x1c, 0x7f, 0xc8, 0x84, 0x11, 0x8, 0x22, + 0x10, 0x45, 0xe0, 0xa8, 0x61, 0xb0, 0xb2, 0x41, + 0x0, 0x82, 0x2, 0x4, 0x0, + + /* U+8BCA "诊" */ + 0x20, 0x60, 0x20, 0xe0, 0x18, 0x90, 0x3, 0xc, + 0x6, 0x26, 0xe0, 0x40, 0x21, 0x80, 0x22, 0x18, + 0x20, 0x30, 0x20, 0xc0, 0x23, 0x4, 0x28, 0x18, + 0x30, 0x60, 0x21, 0x80, 0x6, 0x0, + + /* U+8BCB "诋" */ + 0x40, 0x2, 0x23, 0xfc, 0x32, 0x20, 0x2, 0x20, + 0x2, 0x20, 0xe2, 0x20, 0x23, 0xff, 0x22, 0x30, + 0x22, 0x30, 0x22, 0x10, 0x22, 0x10, 0x22, 0x51, + 0x3a, 0x99, 0x23, 0x49, 0x0, 0x26, + + /* U+8BCC "诌" */ + 0x0, 0x80, 0x81, 0x0, 0x87, 0xf0, 0x90, 0x40, + 0x60, 0x80, 0x82, 0x39, 0xff, 0x90, 0x1, 0x20, + 0x2, 0x40, 0x4, 0x8f, 0xf9, 0x0, 0x12, 0x80, + 0x26, 0x0, 0x49, 0xff, 0x80, 0x1, + + /* U+8BCD "词" */ + 0x0, 0x0, 0xcf, 0xfc, 0xc0, 0x8, 0x80, 0x10, + 0x7f, 0xa0, 0x0, 0x58, 0x0, 0x91, 0xf9, 0x22, + 0x12, 0x44, 0x24, 0x88, 0x49, 0x1f, 0x92, 0x80, + 0x26, 0x0, 0x40, 0x7, 0x80, + + /* U+8BCE "诎" */ + 0x40, 0x20, 0x40, 0x40, 0x48, 0x88, 0x11, 0x10, + 0x22, 0x3c, 0x44, 0x48, 0xff, 0x90, 0x10, 0x20, + 0x20, 0x44, 0x44, 0x88, 0x89, 0x11, 0x12, 0xa2, + 0x26, 0x7f, 0xc0, 0x0, 0x80, + + /* U+8BCF "诏" */ + 0x47, 0xfc, 0x82, 0x11, 0x8, 0x40, 0x61, 0xe1, + 0x4, 0x88, 0xe2, 0x40, 0x8, 0xff, 0x22, 0x4, + 0x88, 0x12, 0xa0, 0x4e, 0x81, 0x63, 0xfc, 0x8, + 0x10, + + /* U+8BD1 "译" */ + 0x67, 0xfc, 0x62, 0x8, 0x2, 0x60, 0x3, 0xe, + 0x19, 0x84, 0xc1, 0xc8, 0x10, 0x11, 0xfe, 0x20, + 0x40, 0x40, 0x80, 0x9f, 0xf9, 0x42, 0x3, 0x4, + 0x0, 0x8, 0x0, + + /* U+8BD2 "诒" */ + 0x40, 0x40, 0x41, 0x0, 0x44, 0x40, 0x8, 0x40, + 0x20, 0x5c, 0xff, 0xc8, 0x0, 0x90, 0x0, 0x23, + 0xfc, 0x44, 0x8, 0x88, 0x11, 0x10, 0x23, 0xa0, + 0x44, 0x7f, 0x80, 0x81, 0x0, + + /* U+8BD3 "诓" */ + 0x40, 0x0, 0x63, 0xff, 0x32, 0x0, 0x2, 0x0, + 0x2, 0xfe, 0x2, 0x20, 0x62, 0x20, 0x22, 0xfc, + 0x22, 0x20, 0x22, 0x20, 0x22, 0xff, 0x22, 0x0, + 0x3a, 0x0, 0x23, 0xff, + + /* U+8BD4 "诔" */ + 0x40, 0x40, 0x60, 0x40, 0x27, 0xfe, 0x0, 0x40, + 0x0, 0x40, 0xe3, 0xfc, 0x20, 0x40, 0x27, 0xfe, + 0x20, 0x60, 0x20, 0xd0, 0x29, 0x50, 0x31, 0x48, + 0x36, 0x46, 0x28, 0x40, 0x0, 0x40, + + /* U+8BD5 "试" */ + 0x40, 0x20, 0x40, 0x48, 0x40, 0x80, 0x3f, 0xf0, + 0x2, 0x1c, 0x4, 0x8, 0x4, 0x13, 0xe8, 0x21, + 0x10, 0x42, 0x20, 0x84, 0x41, 0x48, 0x83, 0x11, + 0x24, 0xf9, 0x40, 0x3, 0x80, + + /* U+8BD6 "诖" */ + 0x40, 0x40, 0x40, 0x80, 0x5f, 0xf0, 0x2, 0x0, + 0x4, 0x1c, 0x8, 0xb, 0xff, 0x90, 0x20, 0x20, + 0x40, 0x4f, 0xf8, 0x81, 0x1, 0x82, 0x3, 0x4, + 0x5, 0xff, 0xc0, + + /* U+8BD7 "诗" */ + 0x0, 0x20, 0x80, 0x41, 0x80, 0x81, 0x3f, 0xe0, + 0x2, 0x0, 0x4, 0x3b, 0xff, 0x90, 0x0, 0x20, + 0x8, 0x5f, 0xfc, 0x80, 0x21, 0x10, 0x42, 0x18, + 0x86, 0x11, 0x8, 0x2, 0x0, 0x38, + + /* U+8BD8 "诘" */ + 0x0, 0x40, 0xc0, 0x80, 0xbf, 0xf8, 0x2, 0x0, + 0x4, 0x1c, 0x8, 0x9, 0xff, 0x10, 0x0, 0x20, + 0x0, 0x4f, 0xf8, 0x90, 0x11, 0x20, 0x23, 0x40, + 0x44, 0xff, 0x81, 0x1, 0x0, + + /* U+8BD9 "诙" */ + 0x41, 0x0, 0x42, 0x0, 0x5f, 0xf8, 0x8, 0x0, + 0x12, 0x1c, 0x44, 0x8, 0xc8, 0x91, 0x52, 0x24, + 0xac, 0x48, 0xa0, 0x81, 0x41, 0x2, 0x42, 0x88, + 0xc6, 0x60, 0xc0, 0x80, 0x0, + + /* U+8BDA "诚" */ + 0x0, 0x28, 0x40, 0x48, 0x40, 0x80, 0x3f, 0xf0, + 0x42, 0x1c, 0x84, 0x9, 0xe9, 0x12, 0x52, 0x24, + 0xa4, 0x49, 0x30, 0xb2, 0x61, 0xa4, 0x92, 0xfb, + 0x21, 0x9, 0x42, 0x21, 0x80, + + /* U+8BDB "诛" */ + 0x40, 0x40, 0x42, 0x80, 0x49, 0x0, 0x1f, 0xe0, + 0x24, 0x1c, 0x88, 0x8, 0x10, 0x13, 0xff, 0x20, + 0x60, 0x41, 0xc0, 0xa3, 0x41, 0x4a, 0x43, 0x64, + 0x64, 0x8, 0x0, 0x10, 0x0, + + /* U+8BDC "诜" */ + 0x40, 0x40, 0xc4, 0x80, 0xc9, 0x0, 0x3f, 0xe0, + 0x44, 0x1d, 0x88, 0x8, 0x10, 0x17, 0xff, 0x21, + 0x20, 0x42, 0x40, 0x84, 0x81, 0x89, 0x13, 0x22, + 0x24, 0xc4, 0x43, 0x7, 0x0, 0x0, + + /* U+8BDD "话" */ + 0x40, 0x0, 0x8f, 0xf8, 0x81, 0x0, 0x2, 0x0, + 0xff, 0xfc, 0x8, 0x8, 0x10, 0x10, 0x20, 0x23, + 0xfc, 0x44, 0x8, 0x88, 0x11, 0x10, 0x22, 0xa0, + 0x46, 0x7f, 0x80, 0x81, 0x0, + + /* U+8BDE "诞" */ + 0x0, 0x0, 0x27, 0x7e, 0x21, 0x8, 0x12, 0x8, + 0x2, 0x8, 0x4, 0x48, 0x67, 0x4e, 0x21, 0x48, + 0x25, 0x48, 0x25, 0x48, 0x25, 0x48, 0x2a, 0x7e, + 0x23, 0x0, 0x4, 0xfe, 0x8, 0x0, + + /* U+8BDF "诟" */ + 0x40, 0x4, 0x23, 0xf8, 0x12, 0x0, 0x2, 0x0, + 0x2, 0x0, 0xe3, 0xff, 0x22, 0x0, 0x22, 0x0, + 0x22, 0xfc, 0x22, 0x84, 0x2a, 0x84, 0x34, 0x84, + 0x24, 0x84, 0xc, 0xfc, 0x8, 0x84, + + /* U+8BE0 "诠" */ + 0x0, 0x20, 0xc0, 0xc0, 0xc2, 0x40, 0x88, 0xc0, + 0x20, 0xdc, 0x80, 0x48, 0xfe, 0x10, 0x20, 0x20, + 0x40, 0x47, 0xf8, 0x81, 0x1, 0x42, 0x3, 0x4, + 0x0, 0xff, 0xc0, + + /* U+8BE1 "诡" */ + 0x0, 0x0, 0x41, 0x0, 0x21, 0xf8, 0x12, 0x8, + 0x2, 0x10, 0x7, 0xff, 0xe2, 0x0, 0x22, 0xfc, + 0x22, 0x84, 0x22, 0x84, 0x2a, 0x84, 0x2a, 0xbc, + 0x32, 0x81, 0x24, 0x83, 0x4, 0x82, 0x8, 0x7e, + + /* U+8BE2 "询" */ + 0x41, 0x0, 0x86, 0x0, 0x8f, 0xf8, 0x20, 0x10, + 0x80, 0x3c, 0x7c, 0x48, 0x88, 0x91, 0x11, 0x23, + 0xe2, 0x44, 0x44, 0x88, 0x89, 0x5f, 0x13, 0x80, + 0x26, 0x0, 0x48, 0x1f, 0x0, + + /* U+8BE3 "诣" */ + 0x42, 0x0, 0x44, 0x18, 0x4f, 0xc0, 0x10, 0x10, + 0x20, 0x3c, 0x3f, 0x88, 0x0, 0x11, 0xfe, 0x22, + 0x4, 0x44, 0x8, 0x8f, 0xf1, 0x70, 0x23, 0x20, + 0x44, 0x7f, 0x80, 0x81, 0x0, + + /* U+8BE4 "诤" */ + 0x0, 0x0, 0x82, 0x0, 0x8f, 0xc0, 0xb0, 0x80, + 0xc2, 0x1, 0x7f, 0xb8, 0x11, 0x10, 0x22, 0x2f, + 0xfe, 0x40, 0x88, 0x81, 0x11, 0x3f, 0xe2, 0x4, + 0x47, 0x8, 0x8, 0x10, 0x1, 0xc0, + + /* U+8BE5 "该" */ + 0x40, 0x40, 0x40, 0x40, 0x5f, 0xf8, 0x4, 0x0, + 0x10, 0x9c, 0x42, 0x9, 0xfc, 0x10, 0x12, 0x20, + 0xc4, 0x42, 0x10, 0x98, 0x41, 0x41, 0x83, 0xc, + 0xc4, 0x60, 0xc0, 0x0, 0x0, + + /* U+8BE6 "详" */ + 0x2, 0x8, 0x82, 0x20, 0x80, 0x40, 0x3f, 0xf0, + 0x4, 0x1c, 0x8, 0x9, 0xff, 0x10, 0x20, 0x20, + 0x40, 0x40, 0x80, 0xbf, 0xf9, 0x2, 0x2, 0x84, + 0x6, 0x8, 0x0, 0x10, 0x0, + + /* U+8BE7 "诧" */ + 0x0, 0x40, 0x80, 0x80, 0x9f, 0xf8, 0x20, 0x10, + 0x40, 0x3c, 0x7e, 0x8, 0x20, 0x10, 0x40, 0x20, + 0x8e, 0x4f, 0xe0, 0x82, 0x1, 0x4, 0x12, 0x88, + 0x26, 0x10, 0x48, 0x3f, 0x0, + + /* U+8BE8 "诨" */ + 0x47, 0xfe, 0x48, 0x4, 0x12, 0x8, 0x3f, 0xfe, + 0x10, 0x4, 0x44, 0x8, 0x88, 0x11, 0xff, 0x20, + 0x20, 0x40, 0x40, 0xbf, 0xf9, 0xc1, 0x2, 0x2, + 0x0, 0x4, 0x0, + + /* U+8BE9 "诩" */ + 0x0, 0x0, 0x4f, 0x7c, 0x82, 0x8, 0x4, 0x10, + 0x49, 0x3c, 0x91, 0x48, 0xa2, 0x90, 0x41, 0x23, + 0x8e, 0x49, 0x24, 0xa2, 0x9, 0x4, 0x13, 0x88, + 0x24, 0x10, 0x41, 0xe7, 0x0, + + /* U+8BEB "诫" */ + 0x40, 0x10, 0x60, 0x16, 0x30, 0x10, 0x7, 0xff, + 0x0, 0x10, 0xe0, 0x12, 0x24, 0x92, 0x24, 0x94, + 0x2f, 0xd4, 0x24, 0x94, 0x24, 0x98, 0x2c, 0x88, + 0x34, 0x99, 0x24, 0xa5, 0x8, 0x86, + + /* U+8BEC "诬" */ + 0x0, 0x0, 0x8f, 0xfc, 0x81, 0x0, 0x12, 0x0, + 0x24, 0x9c, 0x49, 0x8, 0x92, 0x12, 0xaa, 0x25, + 0x54, 0x51, 0xc4, 0xa1, 0x1, 0x42, 0x3, 0x4, + 0x5, 0xff, 0xc0, + + /* U+8BED "语" */ + 0x47, 0xfc, 0x41, 0x0, 0x4, 0x0, 0x3f, 0xc0, + 0x10, 0x9c, 0x21, 0x8, 0x42, 0x17, 0xff, 0x20, + 0x0, 0x47, 0xf8, 0x88, 0x11, 0x10, 0x23, 0xa0, + 0x44, 0x7f, 0x80, 0x81, 0x0, + + /* U+8BEE "诮" */ + 0x40, 0x20, 0xc4, 0x4c, 0xc4, 0x90, 0x1, 0x0, + 0x3f, 0xfc, 0x40, 0x48, 0x80, 0x91, 0xff, 0x22, + 0x2, 0x44, 0x4, 0x8f, 0xf9, 0x50, 0x13, 0x20, + 0x24, 0x40, 0x40, 0x87, 0x80, + + /* U+8BEF "误" */ + 0x43, 0xfc, 0x44, 0x8, 0x48, 0x10, 0x10, 0x20, + 0x3f, 0xdc, 0x0, 0x9, 0xff, 0x10, 0x20, 0x20, + 0x40, 0x5f, 0xfc, 0x81, 0x1, 0x45, 0x3, 0x11, + 0x4, 0xc1, 0xc2, 0x0, 0x0, + + /* U+8BF0 "诰" */ + 0x61, 0x40, 0x64, 0x80, 0xf, 0xf0, 0x22, 0x0, + 0x44, 0x1c, 0x8, 0xb, 0xff, 0x90, 0x0, 0x20, + 0x0, 0x47, 0xf8, 0x88, 0x11, 0x10, 0x22, 0xa0, + 0x46, 0x7f, 0x80, 0x81, 0x0, + + /* U+8BF1 "诱" */ + 0x0, 0x0, 0x63, 0xfc, 0x30, 0x40, 0x0, 0x40, + 0x7, 0xfe, 0xe0, 0xd0, 0x23, 0x4c, 0x2c, 0x2, + 0x23, 0xf8, 0x21, 0x8, 0x29, 0xe, 0x39, 0x4, + 0x31, 0x4, 0x22, 0x4, 0xc, 0x3c, + + /* U+8BF2 "诲" */ + 0x1, 0x0, 0x82, 0x0, 0x8f, 0xf8, 0x90, 0x0, + 0x40, 0x0, 0x7f, 0xb8, 0x91, 0x11, 0x12, 0x22, + 0x4, 0x5f, 0xfc, 0x88, 0x11, 0x13, 0x22, 0xbf, + 0xe6, 0x0, 0x88, 0x3, 0x0, 0x3c, + + /* U+8BF3 "诳" */ + 0x48, 0x0, 0x4a, 0xfc, 0x98, 0x40, 0x30, 0x80, + 0xa1, 0x1c, 0x42, 0x8, 0x44, 0x11, 0xbf, 0x27, + 0x10, 0x5a, 0x20, 0x84, 0x41, 0x48, 0x83, 0x11, + 0x4, 0x3f, 0xc3, 0x80, 0x0, + + /* U+8BF4 "说" */ + 0x42, 0x8, 0x42, 0x10, 0x42, 0x40, 0x3f, 0xe0, + 0x40, 0x5c, 0x80, 0x89, 0x1, 0x12, 0x2, 0x27, + 0xfc, 0x41, 0x20, 0x82, 0x41, 0x44, 0x93, 0x11, + 0x24, 0x62, 0x43, 0x7, 0x80, + + /* U+8BF5 "诵" */ + 0x47, 0xfc, 0x88, 0x20, 0x1f, 0x1, 0xff, 0xe4, + 0x44, 0x91, 0x12, 0x7f, 0xc9, 0x11, 0x24, 0x44, + 0x9f, 0xf2, 0xc4, 0x4d, 0x11, 0x24, 0x44, 0x11, + 0x70, + + /* U+8BF6 "诶" */ + 0x0, 0x40, 0x41, 0x0, 0x84, 0x40, 0x10, 0x40, + 0x7f, 0xdc, 0x20, 0x8, 0xff, 0x12, 0x20, 0x28, + 0x40, 0x40, 0x80, 0xbf, 0xf9, 0x3, 0x2, 0x8a, + 0x6, 0x23, 0x3, 0x81, 0x80, + + /* U+8BF7 "请" */ + 0x40, 0x40, 0x4f, 0xfc, 0x41, 0x0, 0x3f, 0xe0, + 0x4, 0x1d, 0xff, 0xc8, 0x0, 0x11, 0xfe, 0x24, + 0x4, 0x47, 0xf8, 0x90, 0x11, 0x1f, 0xe3, 0xc0, + 0x44, 0x80, 0x81, 0xe, 0x0, + + /* U+8BF8 "诸" */ + 0x40, 0x80, 0x41, 0x8, 0x9f, 0xa0, 0x4, 0x80, + 0xa, 0x1d, 0xff, 0xc8, 0x20, 0x10, 0x80, 0x27, + 0xfc, 0x54, 0x8, 0x8f, 0xf1, 0x10, 0x23, 0x20, + 0x44, 0x7f, 0x80, 0x81, 0x0, + + /* U+8BF9 "诹" */ + 0x0, 0x0, 0xbf, 0x1, 0x25, 0xf0, 0x49, 0x20, + 0xf2, 0x59, 0x24, 0x92, 0x4a, 0x24, 0x94, 0x4f, + 0x28, 0x92, 0x51, 0x24, 0x43, 0x4c, 0x85, 0xf2, + 0x88, 0x28, 0x80, 0x50, 0x0, + + /* U+8BFA "诺" */ + 0x41, 0x10, 0x42, 0x20, 0xbf, 0xf8, 0x8, 0x80, + 0x15, 0x0, 0x10, 0x1b, 0xff, 0x90, 0x80, 0x22, + 0x0, 0x4f, 0xf8, 0xb8, 0x11, 0x10, 0x22, 0xa0, + 0x44, 0x7f, 0x80, 0x81, 0x0, + + /* U+8BFB "读" */ + 0x0, 0x40, 0xc0, 0x80, 0x9f, 0xf8, 0x2, 0x0, + 0xff, 0xfc, 0x0, 0x48, 0x49, 0x10, 0x50, 0x23, + 0x20, 0x40, 0x40, 0xbf, 0xf9, 0x1, 0x2, 0x8d, + 0x6, 0x71, 0x83, 0x80, 0x80, + + /* U+8BFC "诼" */ + 0x40, 0x0, 0x27, 0xfe, 0x10, 0x20, 0x0, 0xc0, + 0x3, 0x84, 0xec, 0x48, 0x20, 0xb8, 0x23, 0x28, + 0x23, 0x28, 0x21, 0x68, 0x20, 0xa4, 0x29, 0x26, + 0x36, 0x23, 0x20, 0x20, 0x1, 0xc0, + + /* U+8BFD "诽" */ + 0x41, 0x10, 0x21, 0x10, 0xf, 0x1e, 0x1, 0x10, + 0xe1, 0x10, 0x2f, 0x1e, 0x21, 0x10, 0x21, 0x10, + 0x21, 0x10, 0x2f, 0x1f, 0x21, 0x10, 0x29, 0x10, + 0x31, 0x10, 0x1, 0x10, + + /* U+8BFE "课" */ + 0x63, 0xfc, 0x64, 0x88, 0x49, 0x10, 0x1f, 0xe0, + 0x24, 0x5c, 0x48, 0x88, 0xff, 0x10, 0x20, 0x20, + 0x40, 0x4f, 0xfc, 0x81, 0x81, 0x46, 0x83, 0x34, + 0xc5, 0x88, 0x40, 0x10, 0x0, + + /* U+8BFF "诿" */ + 0x40, 0x0, 0xc7, 0xf8, 0x81, 0x0, 0x2, 0x0, + 0x7f, 0xdc, 0x3a, 0x8, 0x93, 0x16, 0x21, 0x20, + 0x80, 0x5f, 0xfc, 0x84, 0x21, 0x10, 0x82, 0x9e, + 0x6, 0x1e, 0x3, 0xc3, 0x80, + + /* U+8C00 "谀" */ + 0x0, 0x40, 0x80, 0x80, 0x9d, 0xf1, 0xa2, 0x20, + 0x44, 0x40, 0x88, 0xb9, 0xd7, 0x12, 0x22, 0x24, + 0x44, 0x4f, 0xf8, 0x83, 0x1, 0x45, 0x3, 0x89, + 0x4, 0x23, 0x1, 0x81, 0x80, 0x0, + + /* U+8C01 "谁" */ + 0x1, 0x0, 0x21, 0x20, 0x12, 0x10, 0x3, 0xff, + 0x6, 0x20, 0xee, 0x20, 0x2b, 0xfe, 0x22, 0x20, + 0x22, 0x20, 0x23, 0xfe, 0x22, 0x20, 0x2a, 0x20, + 0x32, 0x20, 0x23, 0xff, 0x2, 0x0, + + /* U+8C02 "谂" */ + 0x40, 0x40, 0x41, 0x40, 0xc4, 0xc0, 0x14, 0x40, + 0xc4, 0x7c, 0x8, 0x9, 0xfe, 0x10, 0x4, 0x20, + 0x10, 0x42, 0x80, 0x95, 0x11, 0xa8, 0x23, 0x90, + 0xa5, 0x23, 0x0, 0x7c, 0x0, + + /* U+8C03 "调" */ + 0x0, 0x0, 0x4f, 0xfc, 0x50, 0x88, 0x2f, 0xde, + 0x42, 0x24, 0x84, 0x49, 0x7e, 0x92, 0x1, 0x24, + 0xf2, 0x49, 0x24, 0xd2, 0x49, 0x27, 0x92, 0xc0, + 0x21, 0x0, 0x42, 0x7, 0x80, + + /* U+8C04 "谄" */ + 0x40, 0x80, 0xc2, 0x0, 0xc7, 0xf0, 0x10, 0x60, + 0x40, 0x9c, 0x2, 0x9, 0xe7, 0x92, 0x1, 0x24, + 0x2, 0x48, 0x4, 0x9e, 0x79, 0x20, 0x13, 0x40, + 0x24, 0xff, 0xc1, 0x0, 0x80, + + /* U+8C05 "谅" */ + 0x0, 0x40, 0x80, 0x40, 0x9f, 0xf8, 0x0, 0x0, + 0x3f, 0xdc, 0x40, 0x88, 0x81, 0x11, 0x2, 0x23, + 0xfc, 0x40, 0x40, 0x84, 0xa1, 0x51, 0x23, 0x62, + 0x45, 0x84, 0x40, 0x30, 0x0, + + /* U+8C06 "谆" */ + 0x0, 0x40, 0x80, 0x80, 0xbf, 0xf8, 0x0, 0x0, + 0x3f, 0xc0, 0x40, 0xb8, 0x81, 0x11, 0xfe, 0x20, + 0x0, 0x47, 0xf8, 0x80, 0x61, 0x1, 0x2, 0xff, + 0xe5, 0x4, 0xc, 0x8, 0x0, 0xe0, + + /* U+8C07 "谇" */ + 0x40, 0x40, 0x40, 0x80, 0x9f, 0xf0, 0x0, 0x0, + 0x10, 0x9c, 0x63, 0x8, 0xa5, 0x12, 0x52, 0x20, + 0x0, 0x40, 0x80, 0xbf, 0xf9, 0x2, 0x2, 0x84, + 0x6, 0x8, 0x8, 0x10, 0x0, + + /* U+8C08 "谈" */ + 0x40, 0x20, 0x26, 0x22, 0x13, 0x24, 0x0, 0x60, + 0x0, 0x58, 0xe1, 0x8c, 0x26, 0x2, 0x20, 0x20, + 0x20, 0x22, 0x23, 0x24, 0x21, 0x60, 0x28, 0x50, + 0x30, 0x88, 0x23, 0x6, 0x4, 0x1, + + /* U+8C0A "谊" */ + 0x0, 0x40, 0x80, 0x80, 0xbf, 0xf8, 0x40, 0x10, + 0xbf, 0xbc, 0x41, 0x8, 0x82, 0x11, 0xfc, 0x22, + 0x8, 0x44, 0x10, 0x8f, 0xe1, 0x90, 0x42, 0x20, + 0x85, 0xff, 0xc0, + + /* U+8C0B "谋" */ + 0x2, 0x0, 0x84, 0x10, 0x9f, 0xf8, 0x90, 0x40, + 0x20, 0x80, 0x7f, 0x38, 0x82, 0x11, 0xfc, 0x20, + 0x40, 0x40, 0x80, 0x9f, 0xf9, 0x3, 0x2, 0x8d, + 0x6, 0x69, 0x9, 0x11, 0x80, 0x20, + + /* U+8C0C "谌" */ + 0x2, 0x0, 0x84, 0x10, 0xbf, 0xf8, 0x10, 0x40, + 0x3f, 0x9c, 0x41, 0x8, 0xfe, 0x11, 0x4, 0x22, + 0x8, 0x5f, 0xfc, 0x91, 0x1, 0x64, 0x63, 0x40, + 0x4, 0xff, 0xc0, + + /* U+8C0D "谍" */ + 0x42, 0x48, 0x84, 0x90, 0x9f, 0xf8, 0x12, 0x40, + 0x24, 0x9c, 0x46, 0x8, 0x80, 0x11, 0xff, 0x20, + 0x20, 0x4f, 0xfc, 0x81, 0x81, 0x45, 0x83, 0x32, + 0xc0, 0x84, 0x40, 0x8, 0x0, + + /* U+8C0E "谎" */ + 0x1, 0x8, 0xdf, 0xfc, 0x84, 0x20, 0x2, 0x0, + 0xff, 0xfc, 0x40, 0x8, 0x80, 0x11, 0x0, 0x23, + 0xfc, 0x40, 0x0, 0x89, 0x21, 0x52, 0x43, 0x24, + 0x84, 0x89, 0x42, 0x11, 0x80, + + /* U+8C0F "谏" */ + 0x40, 0x40, 0x27, 0xfe, 0x10, 0x40, 0x0, 0x40, + 0x7, 0xfe, 0xe5, 0x42, 0x25, 0x4a, 0x24, 0xd2, + 0x24, 0x42, 0x27, 0xfe, 0x20, 0x60, 0x28, 0xd0, + 0x33, 0x4c, 0x2c, 0x42, 0x0, 0x40, + + /* U+8C10 "谐" */ + 0x44, 0x20, 0x88, 0x44, 0x9e, 0xb0, 0x21, 0x80, + 0x42, 0x3c, 0xb4, 0x49, 0x8f, 0x90, 0x40, 0x23, + 0xfc, 0x48, 0x8, 0x90, 0x11, 0x5f, 0xe3, 0x40, + 0x44, 0x7f, 0x81, 0x1, 0x0, + + /* U+8C11 "谑" */ + 0x0, 0x20, 0x20, 0x3e, 0x10, 0x20, 0x3, 0xfe, + 0x2, 0x22, 0xe3, 0xf8, 0x22, 0x22, 0x22, 0x3c, + 0x22, 0x0, 0x22, 0xfe, 0x2a, 0x80, 0x35, 0xff, + 0x4, 0x80, 0x8, 0xfe, 0x8, 0x0, + + /* U+8C12 "谒" */ + 0x43, 0xfc, 0x44, 0x8, 0x48, 0x10, 0x1f, 0xe0, + 0x20, 0x40, 0x7f, 0x98, 0x40, 0x11, 0xff, 0x24, + 0x22, 0x44, 0xc4, 0x8a, 0x69, 0x50, 0x13, 0xbf, + 0xa4, 0x0, 0xc8, 0x7, 0x0, + + /* U+8C13 "谓" */ + 0x47, 0xfe, 0x48, 0x84, 0x51, 0x8, 0x3f, 0xf0, + 0x44, 0x3c, 0xff, 0xc8, 0x0, 0x11, 0xfe, 0x22, + 0x4, 0x47, 0xf8, 0x88, 0x11, 0x5f, 0xe3, 0x20, + 0x44, 0x40, 0x80, 0x8f, 0x0, + + /* U+8C14 "谔" */ + 0x47, 0xbc, 0x49, 0x48, 0x52, 0x90, 0x25, 0x20, + 0x7b, 0xc0, 0x0, 0x19, 0xff, 0x10, 0x0, 0x2f, + 0xfe, 0x44, 0x0, 0x8f, 0xf1, 0x0, 0x23, 0x80, + 0x46, 0x1, 0x8, 0x3e, 0x0, + + /* U+8C15 "谕" */ + 0x40, 0x40, 0x41, 0x40, 0x44, 0x40, 0x1f, 0xe0, + 0xc0, 0x3c, 0x0, 0x89, 0xe1, 0x12, 0x4a, 0x24, + 0x94, 0x4f, 0x28, 0x92, 0x51, 0x7c, 0xa3, 0x48, + 0x44, 0x90, 0x81, 0x67, 0x0, + + /* U+8C16 "谖" */ + 0x40, 0x2, 0x4f, 0xfc, 0x80, 0x10, 0x11, 0x20, + 0x0, 0x9c, 0x7e, 0x88, 0x20, 0x17, 0xff, 0x21, + 0x0, 0x43, 0xf8, 0x8a, 0x11, 0xd4, 0x43, 0x47, + 0x5, 0xe, 0x0, 0xe3, 0x80, + + /* U+8C17 "谗" */ + 0x41, 0x0, 0x23, 0xf8, 0x16, 0x10, 0xf, 0xfe, + 0x4, 0x42, 0xe4, 0x42, 0x27, 0xfe, 0x20, 0xa0, + 0x21, 0x21, 0x2e, 0x3e, 0x21, 0xc0, 0x28, 0x38, + 0x37, 0x0, 0x20, 0xf8, 0x0, 0x4, + + /* U+8C18 "谘" */ + 0x0, 0x20, 0x44, 0x40, 0x22, 0x7e, 0x12, 0x82, + 0x0, 0x94, 0x2, 0x10, 0xe2, 0x28, 0x24, 0x48, + 0x29, 0x86, 0x20, 0x0, 0x23, 0xfc, 0x22, 0x4, + 0x2a, 0x4, 0x32, 0x4, 0x23, 0xfc, 0x2, 0x4, + + /* U+8C19 "谙" */ + 0x40, 0x40, 0x40, 0x20, 0x27, 0xfe, 0x1, 0x0, + 0x0, 0x88, 0xe7, 0xff, 0x20, 0x0, 0x20, 0x0, + 0x23, 0xfe, 0x22, 0x2, 0x23, 0xfe, 0x2a, 0x2, + 0x32, 0x2, 0x23, 0xfe, 0x2, 0x2, + + /* U+8C1A "谚" */ + 0x0, 0x40, 0x40, 0x40, 0x5f, 0xf8, 0x8, 0x40, + 0x9, 0x1c, 0xff, 0xc9, 0x0, 0x12, 0x1c, 0x25, + 0xc0, 0x48, 0x18, 0x91, 0xc1, 0xac, 0x22, 0x41, + 0x81, 0x1c, 0x2, 0x40, 0x0, + + /* U+8C1B "谛" */ + 0x0, 0x40, 0xc0, 0x80, 0x9f, 0xf8, 0x8, 0x40, + 0x1, 0x1c, 0xff, 0xc9, 0x0, 0x92, 0x21, 0x23, + 0xfc, 0x44, 0x88, 0x89, 0x11, 0x52, 0x23, 0xa4, + 0x44, 0x4b, 0x80, 0x10, 0x0, + + /* U+8C1C "谜" */ + 0x4, 0x10, 0x24, 0x92, 0x32, 0x92, 0x0, 0x54, + 0x0, 0x10, 0xe0, 0xfe, 0x26, 0x10, 0x22, 0x30, + 0x22, 0x58, 0x22, 0x54, 0x22, 0x92, 0x2a, 0x10, + 0x32, 0x10, 0x25, 0x0, 0x8, 0xfe, + + /* U+8C1D "谝" */ + 0x0, 0x40, 0x40, 0x80, 0xdf, 0xf8, 0xa0, 0x10, + 0x40, 0x20, 0xff, 0xf9, 0x0, 0x12, 0x0, 0x25, + 0xfe, 0x4a, 0x94, 0x95, 0x29, 0x6f, 0xf3, 0x54, + 0xa5, 0x29, 0x42, 0x52, 0x84, 0x83, + + /* U+8C1F "谟" */ + 0x1, 0x10, 0x82, 0x20, 0xbf, 0xf9, 0x8, 0x80, + 0x0, 0x0, 0xff, 0xb9, 0x1, 0x13, 0xfe, 0x24, + 0x4, 0x4f, 0xf8, 0x81, 0x1, 0x2, 0x2, 0xff, + 0xe4, 0xc, 0xc, 0x64, 0x17, 0x87, 0x8, 0x0, + + /* U+8C20 "谠" */ + 0x40, 0x40, 0x22, 0x4c, 0x21, 0x40, 0xf, 0xfe, + 0x8, 0x2, 0xe8, 0x0, 0x23, 0xfc, 0x22, 0x4, + 0x22, 0x4, 0x23, 0xfc, 0x20, 0xa0, 0x29, 0x20, + 0x31, 0x21, 0x22, 0x22, 0x1c, 0x1e, + + /* U+8C21 "谡" */ + 0x40, 0x0, 0x63, 0xfe, 0x32, 0x22, 0x3, 0xfe, + 0x2, 0x22, 0xe3, 0xfe, 0x20, 0x0, 0x21, 0x8c, + 0x26, 0x43, 0x20, 0xfc, 0x21, 0x88, 0x2e, 0xc8, + 0x30, 0x30, 0x21, 0xcc, 0x6, 0x3, + + /* U+8C22 "谢" */ + 0x1, 0x4, 0x4f, 0x88, 0x51, 0x10, 0x22, 0x20, + 0x7d, 0xfc, 0x88, 0x89, 0xf9, 0x12, 0x2a, 0x24, + 0x54, 0x5f, 0x88, 0x83, 0x11, 0x4a, 0x23, 0x24, + 0x41, 0x88, 0x80, 0x77, 0x0, + + /* U+8C23 "谣" */ + 0x0, 0x0, 0x27, 0xfe, 0x10, 0x0, 0x12, 0x44, + 0x2, 0x28, 0xe2, 0x0, 0x23, 0xfe, 0x24, 0x20, + 0x20, 0x20, 0x27, 0xff, 0x20, 0x20, 0x2a, 0x24, + 0x32, 0x24, 0x23, 0xfc, 0x0, 0x4, + + /* U+8C24 "谤" */ + 0x0, 0x40, 0x80, 0x80, 0x9f, 0xf0, 0x8, 0x40, + 0x1, 0x1c, 0xfd, 0xc9, 0x10, 0x90, 0x0, 0x2f, + 0xfe, 0x42, 0x0, 0x87, 0xe1, 0x48, 0x43, 0x20, + 0x84, 0x81, 0x2, 0x1c, 0x0, + + /* U+8C25 "谥" */ + 0x42, 0x8, 0x42, 0x10, 0xc2, 0x40, 0x3f, 0xf0, + 0x0, 0x1c, 0x22, 0x8, 0x83, 0x16, 0x1, 0x23, + 0xfc, 0x49, 0x48, 0xb2, 0x91, 0xe5, 0x22, 0x4a, + 0x41, 0xff, 0xc0, + + /* U+8C26 "谦" */ + 0x41, 0x8, 0x20, 0x90, 0x17, 0xff, 0x0, 0x90, + 0x7, 0xfe, 0xe0, 0x92, 0x27, 0xff, 0x20, 0x92, + 0x20, 0x92, 0x27, 0xfe, 0x20, 0x90, 0x29, 0x98, + 0x32, 0x94, 0x2c, 0x93, 0x0, 0x90, + + /* U+8C27 "谧" */ + 0x0, 0x40, 0x40, 0x50, 0x94, 0x60, 0x29, 0xa0, + 0xd6, 0xbc, 0x31, 0x48, 0xc2, 0x12, 0x78, 0x20, + 0x0, 0x47, 0xf8, 0x92, 0x91, 0x25, 0x23, 0xca, + 0x44, 0x94, 0x83, 0xff, 0x80, + + /* U+8C28 "谨" */ + 0x42, 0x8, 0x5f, 0xfc, 0x88, 0x20, 0x1f, 0xc0, + 0x4, 0x1c, 0xff, 0x89, 0x11, 0x13, 0xfe, 0x20, + 0x40, 0x4f, 0xf8, 0x81, 0x1, 0xbf, 0xe3, 0x4, + 0x5, 0xff, 0xc0, + + /* U+8C29 "谩" */ + 0x43, 0xfc, 0x44, 0x8, 0x4f, 0xf0, 0x10, 0x20, + 0x3f, 0xdc, 0x0, 0x9, 0xff, 0x92, 0x49, 0x27, + 0xfe, 0x40, 0x0, 0x8f, 0xf1, 0x48, 0x43, 0x9, + 0x4, 0x1e, 0x3, 0xc3, 0x80, + + /* U+8C2A "谪" */ + 0x0, 0x40, 0x20, 0x20, 0x27, 0xff, 0x1, 0x8, + 0x0, 0x90, 0xe7, 0x6e, 0x24, 0x42, 0x25, 0xfa, + 0x24, 0x42, 0x24, 0xf2, 0x25, 0x12, 0x2d, 0x12, + 0x34, 0xf2, 0x24, 0x2, 0x4, 0x1e, + + /* U+8C2B "谫" */ + 0x0, 0x8, 0x82, 0x20, 0xbf, 0xf8, 0x3e, 0x20, + 0x45, 0x5c, 0xfa, 0x89, 0x15, 0x13, 0xea, 0x24, + 0x4, 0x41, 0x30, 0x9f, 0xf1, 0x84, 0x23, 0x10, + 0x44, 0x60, 0x83, 0xe, 0x0, + + /* U+8C2C "谬" */ + 0x4f, 0xbe, 0x20, 0x82, 0x2, 0x9a, 0x1, 0x9e, + 0xe6, 0x82, 0x20, 0x62, 0x20, 0xe0, 0x23, 0x18, + 0x2c, 0x67, 0x23, 0x98, 0x20, 0x60, 0x2b, 0x8c, + 0x30, 0x30, 0x1, 0xc0, 0x6, 0x0, + + /* U+8C2D "谭" */ + 0x0, 0x0, 0xdf, 0xfc, 0x84, 0x80, 0x7f, 0xf0, + 0x92, 0x3d, 0xff, 0xc8, 0x0, 0x13, 0xfe, 0x24, + 0x4, 0x4f, 0xf8, 0x90, 0x11, 0x9f, 0xc3, 0xff, + 0xe4, 0x8, 0x0, 0x10, 0x0, + + /* U+8C2E "谮" */ + 0x27, 0xbe, 0x35, 0x28, 0x7, 0xbe, 0x2, 0x10, + 0xe3, 0x18, 0x25, 0xaa, 0x20, 0x4, 0x23, 0xfc, + 0x24, 0x4, 0x24, 0x4, 0x2b, 0xfc, 0x3c, 0x4, + 0x23, 0xfc, 0x4, 0x4, + + /* U+8C2F "谯" */ + 0x41, 0x20, 0x21, 0x30, 0x13, 0xff, 0x6, 0x20, + 0xe, 0x20, 0xeb, 0xfe, 0x22, 0x20, 0x23, 0xfe, + 0x22, 0x20, 0x22, 0x20, 0x23, 0xff, 0x2a, 0x0, + 0x3a, 0xa2, 0x24, 0x92, 0x4, 0x91, + + /* U+8C30 "谰" */ + 0x0, 0x0, 0x85, 0xfc, 0x88, 0x8, 0x82, 0x10, + 0x4f, 0x3c, 0xbf, 0x49, 0x52, 0x92, 0xf5, 0x25, + 0x4a, 0x4b, 0xf4, 0x91, 0x9, 0x27, 0x93, 0xd4, + 0xa4, 0x88, 0x41, 0x13, 0x80, + + /* U+8C31 "谱" */ + 0x41, 0x8, 0xc3, 0x20, 0x9f, 0xf0, 0x5, 0x0, + 0x6a, 0x5c, 0x14, 0xb, 0xff, 0x90, 0x0, 0x23, + 0xf8, 0x44, 0x10, 0x88, 0x21, 0x5f, 0xc3, 0xa0, + 0x84, 0x7f, 0x0, 0x82, 0x0, + + /* U+8C32 "谲" */ + 0x47, 0xfe, 0x20, 0xc, 0x11, 0xf0, 0x7, 0xff, + 0x0, 0x92, 0xe3, 0x10, 0x24, 0xe0, 0x27, 0xfe, + 0x24, 0x92, 0x25, 0xa, 0x25, 0xfa, 0x2d, 0xa, + 0x35, 0xfa, 0x24, 0x2, 0x4, 0xe, + + /* U+8C33 "谳" */ + 0x2, 0x8, 0x84, 0x11, 0x3f, 0x28, 0x10, 0x40, + 0x23, 0xf9, 0xb9, 0x13, 0x52, 0x25, 0x24, 0x4f, + 0x58, 0x94, 0xa9, 0x3d, 0x53, 0xd2, 0xa6, 0xa5, + 0x49, 0x4c, 0x42, 0x78, 0x80, + + /* U+8C34 "谴" */ + 0x0, 0x20, 0x93, 0xf9, 0x24, 0x90, 0xf, 0xe0, + 0x2, 0x1b, 0x7b, 0xd2, 0x7f, 0x24, 0x82, 0x49, + 0xfc, 0x92, 0x1, 0x27, 0xf2, 0xc8, 0x26, 0x9f, + 0xca, 0xc0, 0x8, 0xff, 0x80, + + /* U+8C35 "谵" */ + 0x40, 0x80, 0x43, 0xf8, 0xc8, 0x20, 0x3f, 0xf0, + 0x44, 0x9c, 0xb4, 0xc9, 0xff, 0x92, 0x0, 0x25, + 0xfe, 0x48, 0x0, 0x93, 0xf1, 0xaf, 0xf2, 0x90, + 0x21, 0x3f, 0xc2, 0x40, 0x80, + + /* U+8C36 "谶" */ + 0x0, 0x48, 0x22, 0x4c, 0x15, 0xaa, 0x0, 0x8, + 0x7, 0xff, 0x62, 0x88, 0x2e, 0xea, 0x22, 0x8a, + 0x22, 0x8c, 0x26, 0xec, 0x22, 0x88, 0x36, 0xf8, + 0x32, 0xa9, 0x2f, 0xe5, 0x0, 0x6, + + /* U+8C37 "谷" */ + 0x4, 0x20, 0x8, 0x18, 0x30, 0x8c, 0x61, 0x82, + 0x2, 0x40, 0x4, 0x20, 0x18, 0x18, 0x60, 0x6, + 0x5f, 0xf8, 0x10, 0x8, 0x10, 0x8, 0x10, 0x8, + 0x10, 0x8, 0x1f, 0xf8, 0x10, 0x8, + + /* U+8C41 "豁" */ + 0x10, 0x0, 0x20, 0x93, 0xf9, 0x34, 0x14, 0x21, + 0xa, 0x2f, 0xc6, 0x4, 0x1c, 0x3f, 0x24, 0x10, + 0x85, 0xfe, 0x4, 0x43, 0xf3, 0xe4, 0x28, 0x48, + 0x50, 0x90, 0x9f, 0x3f, 0x42, 0x42, + + /* U+8C46 "豆" */ + 0xff, 0xfe, 0x0, 0x0, 0x0, 0x1, 0xff, 0xc2, + 0x0, 0x84, 0x1, 0x8, 0x2, 0x1f, 0xfc, 0x0, + 0x0, 0x30, 0x60, 0x20, 0x80, 0x2, 0xf, 0xff, + 0xe0, + + /* U+8C47 "豇" */ + 0xfe, 0xfe, 0x0, 0x10, 0x0, 0x10, 0xfe, 0x10, + 0x82, 0x10, 0x82, 0x10, 0xfe, 0x10, 0x0, 0x10, + 0x44, 0x10, 0x44, 0x10, 0x28, 0x10, 0x3e, 0x10, + 0xc1, 0xff, + + /* U+8C49 "豉" */ + 0x0, 0x10, 0xfe, 0x10, 0x0, 0x10, 0x0, 0xfe, + 0x7e, 0x10, 0x82, 0x10, 0x82, 0xfe, 0x82, 0x82, + 0x7e, 0x44, 0x0, 0x44, 0x44, 0x28, 0x24, 0x28, + 0x8, 0x30, 0x7e, 0x6c, 0x81, 0x83, + + /* U+8C4C "豌" */ + 0x0, 0x61, 0xf0, 0x40, 0xf, 0xf0, 0x10, 0x2f, + 0xb0, 0x11, 0x3b, 0xa2, 0x99, 0x7d, 0x32, 0x5, + 0x64, 0xa, 0xc9, 0x42, 0xb2, 0x85, 0x1, 0x12, + 0x3d, 0x64, 0x81, 0x87, 0x0, + + /* U+8C55 "豕" */ + 0x7f, 0xfe, 0x1, 0x0, 0x2, 0x0, 0xe, 0xc, + 0x31, 0x18, 0xc3, 0x60, 0x4, 0x90, 0x18, 0x90, + 0x61, 0x90, 0x6, 0x88, 0x8, 0x84, 0x30, 0x82, + 0xc0, 0x80, 0xf, 0x0, + + /* U+8C5A "豚" */ + 0x7b, 0xfc, 0x90, 0x81, 0x23, 0x2, 0x4c, 0x27, + 0xa4, 0x89, 0xa, 0x12, 0x2c, 0x25, 0x98, 0x78, + 0x68, 0x91, 0x51, 0x24, 0xa2, 0x71, 0x28, 0x82, + 0x17, 0x38, 0x0, + + /* U+8C61 "象" */ + 0x0, 0x0, 0x4, 0x0, 0xf, 0xf0, 0x10, 0x30, + 0x7f, 0xfe, 0x20, 0x82, 0x21, 0x2, 0x3f, 0xfe, + 0x2, 0x0, 0xd, 0x1c, 0x73, 0xf0, 0xc, 0xd0, + 0x71, 0x48, 0x6, 0x44, 0x38, 0x43, 0x47, 0x80, + + /* U+8C62 "豢" */ + 0x1, 0x0, 0x62, 0x20, 0x8, 0x83, 0xff, 0xe0, + 0x40, 0x1f, 0xff, 0xc2, 0x8, 0x8, 0x8, 0x6f, + 0xed, 0x4, 0x4, 0xf4, 0xc2, 0x3e, 0x3, 0x9a, + 0x8, 0xd3, 0x9e, 0x20, 0x3, 0x80, + + /* U+8C6A "豪" */ + 0x1, 0x1, 0xff, 0xfc, 0x0, 0x1, 0xff, 0xc1, + 0xff, 0x80, 0x0, 0x3f, 0xff, 0xcf, 0xfd, 0x3, + 0x8, 0x7a, 0x61, 0x1b, 0x81, 0xcc, 0x84, 0x69, + 0x87, 0x10, 0xd0, 0xe0, 0x0, + + /* U+8C6B "豫" */ + 0x0, 0x80, 0x7c, 0xf8, 0x9, 0x18, 0x29, 0xfe, + 0x12, 0x22, 0x2, 0x22, 0xfd, 0xfe, 0x14, 0x42, + 0x14, 0xac, 0x17, 0x78, 0x10, 0xb4, 0x17, 0x54, + 0x10, 0x92, 0x13, 0x10, 0x70, 0xe0, + + /* U+8C73 "豳" */ + 0x1, 0x0, 0xf5, 0xe9, 0x12, 0x68, 0x51, 0xd5, + 0xae, 0x64, 0xda, 0x95, 0x73, 0x65, 0x9b, 0x3e, + 0xa5, 0x58, 0x91, 0x6e, 0x5d, 0x81, 0x7, 0xff, + 0xf0, 0x0, 0x40, + + /* U+8C78 "豸" */ + 0x0, 0x60, 0x3c, 0x1e, 0x0, 0x1, 0x30, 0x8e, + 0x3, 0x80, 0xf2, 0x8, 0x38, 0x2, 0x40, 0xe3, + 0x18, 0x68, 0x4, 0x40, 0xc2, 0x38, 0x30, 0xf, + 0x0, + + /* U+8C79 "豹" */ + 0x0, 0x0, 0x30, 0x81, 0x81, 0x4, 0x97, 0xf4, + 0xc8, 0x27, 0x20, 0x5c, 0x40, 0xc4, 0x1, 0x18, + 0x42, 0xc8, 0x46, 0x30, 0x48, 0xa0, 0x16, 0x40, + 0x30, 0x80, 0x41, 0x1, 0x9c, 0x3e, + + /* U+8C7A "豺" */ + 0x4, 0x8, 0x70, 0x13, 0x48, 0x22, 0x6f, 0xf3, + 0x81, 0x8e, 0x3, 0x22, 0xa, 0xc, 0x24, 0x64, + 0x48, 0x19, 0x10, 0x54, 0x23, 0x30, 0x48, 0x40, + 0x80, 0x83, 0xe, 0x3c, 0x0, + + /* U+8C82 "貂" */ + 0x4, 0x0, 0x67, 0xf6, 0x4, 0x43, 0x11, 0x28, + 0x45, 0xc2, 0x18, 0x93, 0x86, 0x0, 0x65, 0xfc, + 0x34, 0x11, 0x50, 0x59, 0x41, 0x5, 0x4, 0x17, + 0xf3, 0x90, 0x40, + + /* U+8C85 "貅" */ + 0x8, 0x90, 0x61, 0x23, 0x54, 0x42, 0x4b, 0xf5, + 0x11, 0xc, 0x62, 0x25, 0x4e, 0x18, 0x9c, 0x49, + 0x54, 0x32, 0xa8, 0xe6, 0x4b, 0x48, 0x88, 0x91, + 0x1, 0x22, 0x1c, 0x44, 0x0, + + /* U+8C89 "貉" */ + 0x0, 0x0, 0x30, 0x81, 0x81, 0xf0, 0x84, 0x24, + 0xdc, 0x86, 0x25, 0x1c, 0xc, 0x4, 0x3c, 0x39, + 0x87, 0x88, 0x0, 0x33, 0xf0, 0xa4, 0x26, 0x48, + 0x40, 0x90, 0x81, 0x3f, 0x1c, 0x42, + + /* U+8C8A "貊" */ + 0xc, 0x0, 0x23, 0xff, 0x80, 0x82, 0x71, 0x3, + 0x9f, 0xce, 0x20, 0x82, 0x41, 0x1c, 0x82, 0xc5, + 0xfc, 0x1a, 0x8, 0xd4, 0x12, 0x28, 0x28, 0x50, + 0x40, 0xbf, 0x8e, 0x41, 0x0, + + /* U+8C8C "貌" */ + 0x0, 0x40, 0x60, 0x83, 0xf, 0xf0, 0x90, 0x25, + 0xa0, 0x4c, 0x7f, 0xa4, 0x81, 0x19, 0x2, 0xcb, + 0xfc, 0x31, 0x20, 0xa2, 0x42, 0x44, 0x98, 0x91, + 0x21, 0x62, 0x5d, 0x87, 0x0, + + /* U+8C94 "貔" */ + 0x0, 0x40, 0x18, 0x40, 0x23, 0xfe, 0x92, 0x2, + 0x56, 0xda, 0x5a, 0x32, 0x62, 0xca, 0x92, 0x2, + 0x13, 0xfe, 0x6a, 0x10, 0x8a, 0x11, 0x1b, 0xd6, + 0x6a, 0x18, 0x8a, 0x11, 0xb, 0xd1, 0x72, 0x1e, + + /* U+8C98 "貘" */ + 0x0, 0x88, 0x21, 0x11, 0x8f, 0xfc, 0x84, 0x45, + 0xc0, 0xa, 0x3f, 0x98, 0x41, 0x48, 0xfe, 0x31, + 0x5, 0x93, 0xf8, 0x60, 0x81, 0xc1, 0x6, 0xbf, + 0xf1, 0xc, 0x2, 0x36, 0x39, 0x83, + + /* U+8D1D "贝" */ + 0x3f, 0xf8, 0x80, 0x22, 0x0, 0x88, 0x42, 0x21, + 0x8, 0x84, 0x22, 0x10, 0x88, 0x42, 0x21, 0x8, + 0x8c, 0x20, 0x2c, 0x1, 0x8, 0x18, 0x1b, 0x80, + 0x10, + + /* U+8D1E "贞" */ + 0x1, 0x0, 0x2, 0x0, 0x7, 0xf8, 0x8, 0x0, + 0x10, 0x7, 0xff, 0x8, 0x2, 0x10, 0x84, 0x21, + 0x8, 0x42, 0x10, 0x84, 0x21, 0xe, 0x40, 0x23, + 0x1, 0x81, 0xbc, 0x1, 0x80, + + /* U+8D1F "负" */ + 0x4, 0x0, 0x20, 0x1, 0xfe, 0xc, 0x8, 0x60, + 0x43, 0x2, 0x3, 0xff, 0x88, 0x2, 0x21, 0x8, + 0x84, 0x22, 0x10, 0x88, 0x42, 0x23, 0x88, 0x19, + 0x80, 0x81, 0xbc, 0x1, + + /* U+8D21 "贡" */ + 0x3f, 0xf8, 0x2, 0x0, 0x4, 0x7, 0xff, 0xf0, + 0x0, 0x7, 0xff, 0x8, 0x2, 0x10, 0x84, 0x21, + 0x8, 0x42, 0x10, 0x8e, 0x0, 0x33, 0x83, 0x80, + 0xd8, 0x0, 0x40, + + /* U+8D22 "财" */ + 0x0, 0x4, 0x7e, 0x4, 0x42, 0x4, 0x52, 0xff, + 0x52, 0xc, 0x52, 0xc, 0x52, 0x14, 0x52, 0x14, + 0x52, 0x24, 0x52, 0x44, 0x52, 0xc4, 0x39, 0x84, + 0x2c, 0x4, 0x62, 0x4, 0xc0, 0x3c, + + /* U+8D23 "责" */ + 0x1, 0x0, 0xff, 0xf8, 0x4, 0x1, 0xff, 0xc0, + 0x10, 0x0, 0x20, 0x3f, 0xff, 0x80, 0x0, 0x3f, + 0xf8, 0x40, 0x10, 0x84, 0x21, 0x8, 0x42, 0x38, + 0x1, 0x8e, 0x3c, 0x3, 0x0, + + /* U+8D24 "贤" */ + 0x8, 0x0, 0x97, 0xf9, 0x24, 0x22, 0x44, 0x84, + 0x86, 0x9, 0x73, 0x80, 0x0, 0x1f, 0xfc, 0x20, + 0x8, 0x42, 0x10, 0x84, 0x21, 0x18, 0x40, 0x6e, + 0xf, 0x3, 0xa0, 0x0, 0x0, + + /* U+8D25 "败" */ + 0x0, 0x20, 0x7e, 0x20, 0x42, 0x40, 0x52, 0x7f, + 0x52, 0x44, 0x52, 0xc4, 0x53, 0x44, 0x52, 0x44, + 0x52, 0x48, 0x52, 0x28, 0x52, 0x28, 0x28, 0x10, + 0x26, 0x28, 0x60, 0x44, 0xc1, 0x83, 0x1, 0x0, + + /* U+8D26 "账" */ + 0x0, 0x0, 0xf9, 0x1, 0x12, 0x12, 0xa4, 0xc5, + 0x4a, 0xa, 0x90, 0x15, 0x7f, 0xaa, 0x48, 0x54, + 0x90, 0xa9, 0x21, 0x52, 0x21, 0x44, 0x42, 0x48, + 0x48, 0xd6, 0x70, 0x30, 0x0, + + /* U+8D27 "货" */ + 0x8, 0x80, 0x21, 0x18, 0x83, 0xc3, 0x3c, 0x1a, + 0x8, 0x24, 0xf, 0x80, 0x0, 0x1f, 0xfc, 0x20, + 0x8, 0x42, 0x10, 0x84, 0x21, 0x18, 0x40, 0x6e, + 0x7, 0x3, 0xb0, 0x0, 0x0, + + /* U+8D28 "质" */ + 0x0, 0x2, 0x7f, 0xfc, 0x81, 0x1, 0x2, 0x2, + 0xff, 0xe4, 0x8, 0x9, 0xff, 0x12, 0x2, 0x24, + 0x44, 0x48, 0x88, 0x91, 0x11, 0x7, 0x4, 0x19, + 0x89, 0xc0, 0xc4, 0x0, 0x0, + + /* U+8D29 "贩" */ + 0x7d, 0xfe, 0x8a, 0x1, 0x54, 0x2, 0xa8, 0x5, + 0x5f, 0xca, 0xa8, 0x95, 0x51, 0x2a, 0x92, 0x55, + 0x28, 0x22, 0x30, 0xa4, 0x41, 0x31, 0x44, 0x2c, + 0x60, 0x10, 0x0, + + /* U+8D2A "贪" */ + 0x1, 0x0, 0x5, 0x0, 0x21, 0x81, 0x88, 0xcc, + 0x0, 0x63, 0xfc, 0x0, 0x8, 0x1f, 0xfc, 0x20, + 0x8, 0x42, 0x10, 0x84, 0x21, 0x1c, 0x40, 0x67, + 0xf, 0x1, 0x80, 0x0, 0x0, + + /* U+8D2B "贫" */ + 0x4, 0x40, 0x30, 0x61, 0x80, 0x34, 0xff, 0x90, + 0x41, 0x1, 0x2, 0x1c, 0x78, 0x0, 0x0, 0x3f, + 0xf8, 0x40, 0x10, 0x84, 0x21, 0x8, 0x42, 0x2c, + 0x81, 0x86, 0x3c, 0x3, 0x0, + + /* U+8D2C "贬" */ + 0x7d, 0xfe, 0x88, 0x1, 0x50, 0x82, 0xa1, 0x5, + 0x40, 0xa, 0xbf, 0x95, 0x1, 0x2a, 0x4, 0x54, + 0x10, 0xa8, 0x40, 0xa1, 0x1, 0x44, 0x4, 0x1c, + 0x18, 0x4f, 0xc0, + + /* U+8D2D "购" */ + 0x7c, 0x40, 0x89, 0x1, 0x13, 0xfa, 0xa8, 0x15, + 0x50, 0x2a, 0x88, 0x55, 0x10, 0xaa, 0x51, 0x54, + 0x92, 0xa9, 0x24, 0x47, 0xe9, 0x40, 0x52, 0xc0, + 0x28, 0xc0, 0x70, 0xf, 0x0, + + /* U+8D2E "贮" */ + 0x0, 0x10, 0x7e, 0x10, 0x42, 0x8, 0x42, 0xff, + 0x52, 0x81, 0x52, 0x81, 0x52, 0x0, 0x52, 0x0, + 0x52, 0x0, 0x52, 0x0, 0x52, 0x0, 0x18, 0x0, + 0x24, 0x0, 0x22, 0x0, 0x40, 0xff, 0x0, 0x0, + + /* U+8D2F "贯" */ + 0x3f, 0xf8, 0x42, 0x10, 0x84, 0x27, 0xff, 0xf2, + 0x20, 0x87, 0xff, 0x0, 0x0, 0x0, 0x0, 0x3f, + 0xf8, 0x40, 0x10, 0x88, 0x21, 0x10, 0x42, 0x38, + 0x81, 0x8e, 0x3c, 0x3, 0x0, + + /* U+8D30 "贰" */ + 0x0, 0x28, 0x0, 0x4b, 0xff, 0xf8, 0x1, 0x7, + 0xfa, 0x0, 0x4, 0x1f, 0xe8, 0x0, 0x10, 0x7f, + 0x20, 0x82, 0x41, 0x24, 0x82, 0x48, 0x94, 0x81, + 0x22, 0xe2, 0x78, 0x23, 0x80, + + /* U+8D31 "贱" */ + 0x0, 0x40, 0x7c, 0x58, 0x44, 0x44, 0x54, 0x42, + 0x55, 0xfc, 0x54, 0x40, 0x54, 0x40, 0x54, 0x7e, + 0x55, 0xc0, 0x54, 0x24, 0x54, 0x28, 0x20, 0x31, + 0x38, 0xd2, 0x65, 0x12, 0xc0, 0xe, + + /* U+8D32 "贲" */ + 0x1, 0x0, 0x2, 0x1, 0xff, 0xf0, 0x8, 0x1, + 0x1, 0x1f, 0xff, 0xc4, 0x4, 0x0, 0x0, 0x3f, + 0xf8, 0x40, 0x10, 0x84, 0x21, 0x8, 0x42, 0x38, + 0x80, 0xcc, 0xe, 0x7, 0x20, 0x0, + + /* U+8D33 "贳" */ + 0x22, 0x10, 0x44, 0x23, 0xff, 0xf9, 0x10, 0x82, + 0x3f, 0x4, 0x0, 0xf, 0xff, 0x80, 0x0, 0x3f, + 0xf8, 0x40, 0x10, 0x84, 0x21, 0x8, 0x40, 0x3c, + 0x1, 0x87, 0xbc, 0x0, 0x80, + + /* U+8D34 "贴" */ + 0x0, 0x10, 0x7e, 0x10, 0x42, 0x10, 0x52, 0x1f, + 0x52, 0x10, 0x52, 0x10, 0x52, 0x10, 0x52, 0x10, + 0x52, 0xfe, 0x52, 0x82, 0x52, 0x82, 0x38, 0x82, + 0x24, 0x82, 0x42, 0xfe, 0x80, 0x82, + + /* U+8D35 "贵" */ + 0x1, 0x0, 0x7f, 0xf0, 0x84, 0x21, 0x8, 0x43, + 0xff, 0x80, 0x20, 0x3f, 0xff, 0x80, 0x0, 0x3f, + 0xf8, 0x40, 0x10, 0x84, 0x21, 0x8, 0x40, 0x2c, + 0x3, 0x87, 0x18, 0x1, 0x0, + + /* U+8D36 "贶" */ + 0x7d, 0xfe, 0x45, 0x2, 0x45, 0x2, 0x55, 0x2, + 0x55, 0x2, 0x55, 0x2, 0x55, 0xfe, 0x54, 0x48, + 0x54, 0x48, 0x54, 0x48, 0x38, 0x48, 0x24, 0x89, + 0x41, 0x89, 0xc3, 0xe, + + /* U+8D37 "贷" */ + 0x8, 0xb0, 0x21, 0x10, 0xc2, 0x2, 0xbf, 0xf9, + 0x4, 0x22, 0x6, 0x40, 0x3, 0x1f, 0xfc, 0x20, + 0x8, 0x42, 0x10, 0x84, 0x21, 0xc, 0x40, 0x66, + 0x3, 0x83, 0xb8, 0x0, 0x0, + + /* U+8D38 "贸" */ + 0x4, 0x1, 0xe7, 0xf4, 0x4, 0x51, 0x11, 0x4e, + 0x89, 0xc4, 0xe0, 0x0, 0xf, 0xfe, 0x20, 0x8, + 0x84, 0x22, 0x10, 0x88, 0xc2, 0x6, 0xe0, 0xe0, + 0x7c, 0x0, 0x0, + + /* U+8D39 "费" */ + 0x2, 0x20, 0xff, 0xf8, 0x8, 0x91, 0xff, 0xe2, + 0x22, 0x4, 0x44, 0xf, 0xff, 0x82, 0x17, 0x1f, + 0xf8, 0xe0, 0x10, 0x44, 0x20, 0x88, 0x40, 0x3e, + 0x3, 0x83, 0x88, 0x0, 0x0, + + /* U+8D3A "贺" */ + 0x8, 0x0, 0x8, 0x0, 0x7f, 0x7e, 0x11, 0x42, + 0x11, 0x42, 0x11, 0x42, 0x2f, 0x7e, 0x40, 0x0, + 0x1f, 0xfc, 0x10, 0x4, 0x10, 0x84, 0x10, 0x84, + 0x10, 0x84, 0x1, 0x70, 0x1e, 0xe, 0x60, 0x0, + + /* U+8D3B "贻" */ + 0x0, 0x10, 0x7c, 0x20, 0x44, 0x40, 0x54, 0x44, + 0x54, 0x84, 0x55, 0xfe, 0x54, 0x1, 0x54, 0x0, + 0x54, 0xfe, 0x54, 0x82, 0x54, 0x82, 0x38, 0x82, + 0x2c, 0x82, 0x46, 0xfe, 0x80, 0x82, + + /* U+8D3C "贼" */ + 0x0, 0x14, 0x7c, 0x14, 0x44, 0x10, 0x45, 0xff, + 0x54, 0x10, 0x54, 0x92, 0x54, 0x92, 0x55, 0xf4, + 0x54, 0x94, 0x54, 0x8c, 0x54, 0x88, 0x38, 0x99, + 0x2d, 0x39, 0x41, 0x65, 0xc2, 0x46, + + /* U+8D3D "贽" */ + 0x20, 0x80, 0x41, 0x3, 0xef, 0xc1, 0x4, 0x82, + 0xa9, 0x1e, 0x32, 0x8, 0xd5, 0x73, 0x6, 0x0, + 0x0, 0x7f, 0xe0, 0x80, 0x41, 0x10, 0x82, 0x21, + 0x4, 0xb2, 0x6, 0x1c, 0x70, 0x4, + + /* U+8D3E "贾" */ + 0xff, 0xfe, 0x8, 0x81, 0xff, 0xf2, 0x22, 0x24, + 0x44, 0x4f, 0xff, 0x80, 0x0, 0x1f, 0xfc, 0x20, + 0x8, 0x42, 0x10, 0x84, 0x20, 0x17, 0x3, 0xc1, + 0xd0, 0x0, 0x40, + + /* U+8D3F "贿" */ + 0x0, 0x20, 0xf8, 0x41, 0x17, 0xfa, 0xa2, 0x5, + 0x44, 0xa, 0x9f, 0x95, 0x61, 0x2b, 0xc2, 0x54, + 0xfc, 0xa9, 0x8, 0xc3, 0xf1, 0x44, 0x22, 0x48, + 0x48, 0x50, 0xa0, 0x23, 0x0, + + /* U+8D40 "赀" */ + 0x8, 0x81, 0x22, 0x34, 0xeb, 0x12, 0x30, 0x48, + 0x85, 0x3a, 0x1f, 0xf, 0xc0, 0x0, 0x3f, 0xf8, + 0x80, 0x22, 0x0, 0x88, 0x82, 0x23, 0x88, 0x11, + 0xcf, 0x80, 0xc0, + + /* U+8D41 "赁" */ + 0x10, 0x0, 0x2f, 0xf0, 0x81, 0x3, 0x7f, 0xfa, + 0x4, 0x4, 0x8, 0x9, 0xff, 0x0, 0x0, 0x3f, + 0xf8, 0x40, 0x10, 0x84, 0x21, 0x10, 0x40, 0x3c, + 0x1, 0x86, 0x3c, 0x3, 0x0, + + /* U+8D42 "赂" */ + 0x0, 0x40, 0x7c, 0x40, 0x44, 0xfc, 0x44, 0x84, + 0x55, 0xcc, 0x55, 0x48, 0x54, 0x30, 0x54, 0x78, + 0x55, 0x86, 0x54, 0x0, 0x54, 0xfc, 0x10, 0x84, + 0x28, 0x84, 0x24, 0x84, 0x46, 0xfc, 0x80, 0x84, + + /* U+8D43 "赃" */ + 0x0, 0x20, 0xf8, 0x21, 0x17, 0xfa, 0xa8, 0x5, + 0x51, 0xa, 0xa2, 0x15, 0x44, 0x2a, 0xbf, 0x55, + 0x10, 0xaa, 0x20, 0x44, 0x41, 0x48, 0x82, 0x61, + 0x8, 0x5f, 0xe0, 0x80, 0x0, + + /* U+8D44 "资" */ + 0x0, 0x1, 0x84, 0x0, 0x8f, 0xf8, 0x22, 0x21, + 0x6, 0x44, 0x12, 0x31, 0x83, 0x0, 0x1, 0x3f, + 0xf8, 0x40, 0x10, 0x84, 0x21, 0x8, 0x42, 0x38, + 0x80, 0xcc, 0x1e, 0x7, 0x0, 0x0, + + /* U+8D45 "赅" */ + 0x7c, 0x20, 0x44, 0x10, 0x45, 0xff, 0x54, 0x20, + 0x54, 0x44, 0x54, 0x8c, 0x55, 0xf8, 0x54, 0x12, + 0x54, 0x22, 0x54, 0x44, 0x11, 0x88, 0x28, 0x18, + 0x24, 0x64, 0x40, 0x82, 0x1, 0x0, + + /* U+8D46 "赆" */ + 0x7c, 0xfc, 0x89, 0x9, 0x52, 0x12, 0xa4, 0x25, + 0x4f, 0xca, 0x92, 0x15, 0x24, 0x2a, 0x84, 0x55, + 0x44, 0xa8, 0x64, 0x80, 0x1, 0xc4, 0x6, 0x46, + 0x18, 0x3, 0x0, + + /* U+8D47 "赇" */ + 0x0, 0x10, 0x7c, 0x16, 0x44, 0x10, 0x55, 0xff, + 0x54, 0x10, 0x54, 0x10, 0x55, 0x11, 0x54, 0x92, + 0x54, 0x1c, 0x54, 0x38, 0x54, 0xd4, 0x23, 0x12, + 0x38, 0x11, 0x66, 0x10, 0xc0, 0xe0, + + /* U+8D48 "赈" */ + 0x7d, 0xfe, 0x45, 0x0, 0x45, 0x0, 0x55, 0x7e, + 0x55, 0x0, 0x55, 0x0, 0x55, 0xfe, 0x55, 0x50, + 0x55, 0x52, 0x55, 0x4c, 0x39, 0x48, 0x2d, 0x44, + 0x42, 0x76, 0xc6, 0x40, + + /* U+8D49 "赉" */ + 0x2, 0x1, 0xff, 0xe2, 0x21, 0x6, 0x98, 0xff, + 0xfc, 0x1a, 0x1, 0xa6, 0x38, 0x7, 0x3f, 0xf0, + 0x80, 0x42, 0x21, 0x8, 0x84, 0x7, 0xc0, 0xe0, + 0xe4, 0x0, 0x0, + + /* U+8D4A "赊" */ + 0x0, 0x10, 0x7c, 0x30, 0x44, 0x68, 0x54, 0x44, + 0x55, 0x82, 0x55, 0x7c, 0x54, 0x0, 0x54, 0x0, + 0x55, 0xfe, 0x54, 0x10, 0x54, 0x10, 0x28, 0x94, + 0x25, 0x12, 0x41, 0x10, 0xc0, 0x60, + + /* U+8D4B "赋" */ + 0x0, 0x10, 0xf3, 0xe9, 0x20, 0x4a, 0xc0, 0x85, + 0xbf, 0xeb, 0x2, 0x16, 0x14, 0x2d, 0x28, 0x5a, + 0x70, 0xb4, 0x91, 0x49, 0x21, 0x52, 0x52, 0xa4, + 0xa8, 0x7d, 0x70, 0x1, 0x80, + + /* U+8D4C "赌" */ + 0x0, 0x40, 0xf8, 0x81, 0x17, 0xd2, 0xa2, 0x45, + 0x45, 0xa, 0xff, 0xd5, 0x8, 0x2a, 0x20, 0x54, + 0xfc, 0xae, 0x8, 0x47, 0xf1, 0x48, 0x22, 0x50, + 0x48, 0x3f, 0xa0, 0x41, 0x0, + + /* U+8D4D "赍" */ + 0x2, 0x1, 0xff, 0xfc, 0x48, 0x41, 0x91, 0x84, + 0xec, 0xc0, 0x40, 0x3f, 0xff, 0x40, 0x2, 0xbf, + 0xf8, 0x40, 0x10, 0x84, 0x21, 0x10, 0x40, 0x7c, + 0x3, 0x7, 0x18, 0x1, 0x0, + + /* U+8D4E "赎" */ + 0x0, 0x10, 0x7c, 0x10, 0x44, 0xfe, 0x54, 0x10, + 0x55, 0xff, 0x54, 0x1, 0x54, 0x49, 0x54, 0x28, + 0x54, 0xc8, 0x54, 0x8, 0x55, 0xff, 0x38, 0x10, + 0x24, 0x18, 0x40, 0x66, 0xc1, 0x81, + + /* U+8D4F "赏" */ + 0x41, 0x0, 0xc4, 0xcf, 0xff, 0xe0, 0x1, 0xbf, + 0xf4, 0x80, 0x43, 0xff, 0x0, 0x0, 0x7f, 0xf9, + 0x0, 0x24, 0x10, 0x90, 0x42, 0x6, 0xe0, 0xe0, + 0x6c, 0x0, 0x0, + + /* U+8D50 "赐" */ + 0x7c, 0xfc, 0x89, 0x9, 0x53, 0xf2, 0xa4, 0x25, + 0x4f, 0xca, 0x80, 0x15, 0x20, 0x2a, 0x7f, 0x55, + 0x2a, 0xa8, 0xa4, 0xe2, 0x49, 0x6c, 0x94, 0x62, + 0x78, 0xb, 0x80, + + /* U+8D53 "赓" */ + 0x0, 0x40, 0x0, 0x40, 0x3f, 0xfe, 0x20, 0x40, + 0x2f, 0xfc, 0x20, 0x44, 0x3f, 0xfe, 0x20, 0x84, + 0x2f, 0xfc, 0x22, 0x10, 0x27, 0xfc, 0x3c, 0xa, + 0x24, 0x48, 0x44, 0x68, 0x41, 0x98, 0x1e, 0x6, + + /* U+8D54 "赔" */ + 0x0, 0x20, 0xf8, 0x21, 0x17, 0xfa, 0xa0, 0x5, + 0x48, 0x8a, 0x89, 0x15, 0x7f, 0xaa, 0x0, 0x54, + 0x0, 0xab, 0xf8, 0x44, 0x11, 0x48, 0x22, 0x50, + 0x48, 0xff, 0xa0, 0x41, 0x0, + + /* U+8D55 "赕" */ + 0x0, 0x20, 0xf8, 0x41, 0x14, 0x92, 0x25, 0x45, + 0x47, 0xa, 0x99, 0x15, 0x61, 0xaa, 0x10, 0x54, + 0x20, 0xaa, 0x49, 0x53, 0xa1, 0x42, 0x82, 0x44, + 0x88, 0x30, 0xb0, 0x80, 0x80, + + /* U+8D56 "赖" */ + 0x10, 0x41, 0xfc, 0xf0, 0x42, 0x20, 0x88, 0x87, + 0xcf, 0xd2, 0x90, 0xa5, 0x25, 0x4a, 0x4a, 0x7c, + 0x94, 0x21, 0x28, 0xe2, 0x52, 0xb1, 0x89, 0x4, + 0x82, 0x10, 0x84, 0x40, 0x0, + + /* U+8D58 "赘" */ + 0x10, 0x40, 0xfc, 0xfd, 0xfa, 0x20, 0x84, 0x4e, + 0xe5, 0x7, 0x84, 0x11, 0x14, 0x2e, 0xc6, 0x80, + 0x0, 0x7f, 0xe0, 0x80, 0x41, 0x10, 0x82, 0x31, + 0x0, 0x9c, 0x3e, 0x7, 0x0, + + /* U+8D59 "赙" */ + 0x0, 0x28, 0xf8, 0x49, 0x1f, 0xfa, 0x21, 0x5, + 0x5f, 0xca, 0xa4, 0x95, 0x7f, 0x2a, 0x92, 0x55, + 0xfc, 0xaa, 0x40, 0x4f, 0xf9, 0x40, 0x42, 0xc8, + 0x88, 0x81, 0x20, 0xe, 0x0, + + /* U+8D5A "赚" */ + 0x0, 0x88, 0xf0, 0xa1, 0x2f, 0xfa, 0xc2, 0x85, + 0x9f, 0xcb, 0xa, 0x96, 0xff, 0xac, 0x2a, 0x58, + 0x54, 0xb3, 0xf8, 0x43, 0x41, 0x4a, 0xc2, 0x15, + 0x48, 0xca, 0x70, 0x14, 0x0, + + /* U+8D5B "赛" */ + 0x2, 0x3, 0xff, 0xf8, 0x84, 0x42, 0x10, 0x1f, + 0xe1, 0xff, 0xe0, 0x84, 0x3f, 0xff, 0x10, 0x20, + 0xff, 0xed, 0x2, 0x44, 0x88, 0x13, 0xa0, 0x31, + 0xc7, 0x0, 0x80, + + /* U+8D5C "赜" */ + 0x0, 0x21, 0xf8, 0x42, 0x47, 0xfc, 0x81, 0x9, + 0x1f, 0xd7, 0x84, 0x29, 0xff, 0xd2, 0x0, 0xa5, + 0xfd, 0x4a, 0xa, 0xf4, 0x94, 0x89, 0x29, 0x12, + 0x52, 0xb, 0x3f, 0x21, 0x1, 0x81, + + /* U+8D5D "赝" */ + 0x3f, 0xff, 0x24, 0x10, 0x25, 0x10, 0x29, 0xfe, + 0x2b, 0x10, 0x3d, 0xfc, 0x29, 0xfe, 0x29, 0x10, + 0x20, 0xec, 0x2f, 0xfc, 0x28, 0x4, 0x28, 0x44, + 0x48, 0xc4, 0x41, 0x38, 0x1e, 0x6, + + /* U+8D5E "赞" */ + 0x8, 0x10, 0x91, 0x21, 0xfb, 0xfc, 0x48, 0x8f, + 0xff, 0xe5, 0x9, 0xb, 0xa2, 0xe0, 0x82, 0x3f, + 0xf8, 0x40, 0x10, 0x84, 0x21, 0x8, 0x40, 0x3c, + 0x3, 0x87, 0xb8, 0x0, 0x80, + + /* U+8D60 "赠" */ + 0x0, 0x88, 0xf8, 0xa1, 0x17, 0xfa, 0xb1, 0x15, + 0x6a, 0xaa, 0xc4, 0x55, 0x7f, 0xaa, 0x0, 0x55, + 0xfc, 0xaa, 0x8, 0x44, 0x11, 0xcf, 0xe2, 0xd0, + 0x48, 0x3f, 0xa0, 0x41, 0x0, + + /* U+8D61 "赡" */ + 0x0, 0x80, 0xf9, 0xf9, 0x14, 0x22, 0xbf, 0xf5, + 0x62, 0x4a, 0xda, 0x55, 0x7f, 0xab, 0x0, 0x56, + 0xfe, 0xac, 0x0, 0x4b, 0xf9, 0x57, 0xf2, 0x68, + 0x28, 0x5f, 0xe0, 0xa0, 0x80, + + /* U+8D62 "赢" */ + 0x1, 0x1, 0xff, 0xfc, 0x80, 0x1, 0xff, 0xc0, + 0x0, 0x7, 0xff, 0xf, 0xfe, 0x0, 0x0, 0x77, + 0xb8, 0xa9, 0x51, 0xd6, 0xe2, 0xad, 0x47, 0x52, + 0x8a, 0x59, 0x6d, 0x11, 0x80, + + /* U+8D63 "赣" */ + 0x10, 0x0, 0x20, 0x83, 0xfb, 0xf2, 0x2d, 0x42, + 0x81, 0x1f, 0xcd, 0x80, 0x60, 0xbe, 0x3c, 0x44, + 0x10, 0xf9, 0xd9, 0x13, 0xf3, 0xe4, 0x21, 0x9, + 0x5f, 0xd2, 0x84, 0x1b, 0x8, 0xc1, + + /* U+8D64 "赤" */ + 0x1, 0x0, 0x2, 0x1, 0xff, 0xf0, 0x8, 0x0, + 0x10, 0x0, 0x20, 0x3f, 0xff, 0x84, 0x20, 0x8, + 0x40, 0x90, 0x91, 0x21, 0x34, 0x42, 0x21, 0x4, + 0x22, 0x8, 0x18, 0xf0, 0x0, + + /* U+8D66 "赦" */ + 0x8, 0x0, 0x8, 0x20, 0x8, 0x20, 0x7f, 0x20, + 0x8, 0x7f, 0x8, 0x44, 0xff, 0xc4, 0x14, 0x44, + 0x14, 0x24, 0x55, 0x28, 0xa5, 0x18, 0xa4, 0x90, + 0x24, 0x18, 0x24, 0x24, 0x44, 0x46, 0x9c, 0x80, + + /* U+8D67 "赧" */ + 0x10, 0x0, 0x23, 0xfb, 0xfc, 0x10, 0x88, 0x21, + 0x13, 0xdf, 0xe0, 0xa, 0x3f, 0x14, 0xa2, 0xaf, + 0x45, 0x96, 0x53, 0x24, 0xa2, 0x48, 0x84, 0x93, + 0x91, 0x2d, 0xae, 0x51, 0x0, + + /* U+8D6B "赫" */ + 0x10, 0x20, 0x20, 0x43, 0xfb, 0xf8, 0x81, 0x1, + 0x2, 0x1f, 0xdf, 0xca, 0x12, 0x14, 0x24, 0xab, + 0x4d, 0x56, 0x96, 0xa5, 0x29, 0x4a, 0x44, 0x88, + 0x89, 0x11, 0x26, 0x5e, 0x0, + + /* U+8D6D "赭" */ + 0x10, 0x40, 0x41, 0x1f, 0xdf, 0xc4, 0x12, 0x10, + 0x53, 0xf7, 0xf4, 0x84, 0x12, 0x3e, 0xcf, 0x8b, + 0x32, 0x2c, 0x8f, 0x92, 0x22, 0x48, 0x8a, 0x23, + 0xeb, 0x8, 0x80, + + /* U+8D70 "走" */ + 0x1, 0x0, 0x2, 0x0, 0xff, 0xe0, 0x8, 0x0, + 0x10, 0x0, 0x20, 0x3f, 0xff, 0x80, 0x80, 0x11, + 0x0, 0x23, 0xf0, 0xc4, 0x1, 0x88, 0x4, 0xd0, + 0x18, 0x7f, 0xe0, 0x0, 0x0, + + /* U+8D73 "赳" */ + 0x10, 0x0, 0x20, 0x8, 0x40, 0x13, 0xf4, 0x21, + 0x8, 0x42, 0x10, 0xbf, 0xa1, 0x4, 0x42, 0x48, + 0x84, 0x91, 0x79, 0x3b, 0x12, 0x40, 0x26, 0x80, + 0x4d, 0x0, 0xa6, 0x0, 0x43, 0xff, + + /* U+8D74 "赴" */ + 0x0, 0x20, 0x20, 0x40, 0x40, 0x83, 0xf1, 0x1, + 0x2, 0x2, 0x6, 0x3f, 0xca, 0x4, 0x12, 0x48, + 0x22, 0x90, 0x41, 0x38, 0x82, 0x41, 0x4, 0x82, + 0x15, 0x4, 0x26, 0x0, 0x43, 0xff, + + /* U+8D75 "赵" */ + 0x10, 0x0, 0x20, 0x0, 0x42, 0x13, 0xf2, 0x21, + 0x4, 0x82, 0x5, 0x3f, 0xc4, 0x4, 0x8, 0x48, + 0x28, 0x90, 0x51, 0x39, 0x12, 0x46, 0x24, 0x98, + 0x35, 0x0, 0x26, 0x0, 0x43, 0xff, + + /* U+8D76 "赶" */ + 0x10, 0x0, 0x10, 0xfe, 0x7e, 0x10, 0x10, 0x10, + 0x10, 0x10, 0xfe, 0x10, 0x8, 0xff, 0x8, 0x10, + 0x48, 0x10, 0x4f, 0x10, 0x48, 0x10, 0x68, 0x10, + 0x78, 0x10, 0x98, 0x0, 0x87, 0xff, + + /* U+8D77 "起" */ + 0x10, 0x0, 0x21, 0xf8, 0x40, 0x13, 0xe0, 0x21, + 0x0, 0x42, 0x0, 0xbf, 0xbf, 0x8, 0x40, 0x50, + 0x80, 0xa1, 0x1, 0x72, 0x12, 0x84, 0x25, 0x7, + 0x8e, 0x0, 0x24, 0x0, 0x47, 0xfe, + + /* U+8D81 "趁" */ + 0x10, 0x10, 0x10, 0x30, 0x10, 0x28, 0xfe, 0x4c, + 0x11, 0x82, 0x10, 0x30, 0xfe, 0x40, 0x8, 0x88, + 0x48, 0x10, 0x48, 0x62, 0x4e, 0x84, 0x48, 0x18, + 0x68, 0x20, 0x69, 0xc0, 0x98, 0x0, 0x87, 0xfe, + + /* U+8D84 "趄" */ + 0x10, 0x0, 0x10, 0xfc, 0x10, 0x84, 0xfe, 0x84, + 0x10, 0x84, 0x10, 0xfc, 0xfe, 0x84, 0x8, 0x84, + 0x48, 0x84, 0x48, 0xfc, 0x4e, 0x84, 0x48, 0x84, + 0x69, 0xfe, 0x78, 0x0, 0x98, 0x0, 0x87, 0xfe, + + /* U+8D85 "超" */ + 0x10, 0x0, 0x21, 0xf9, 0xf9, 0x10, 0x82, 0x21, + 0x4, 0x42, 0x13, 0xbb, 0x80, 0x24, 0x7e, 0x48, + 0x84, 0x9d, 0x9, 0x22, 0x13, 0x47, 0xe7, 0x80, + 0x13, 0x0, 0x21, 0xff, 0x80, + + /* U+8D8A "越" */ + 0x10, 0x20, 0x10, 0x24, 0x7e, 0x20, 0x10, 0xfe, + 0x11, 0x20, 0xff, 0x24, 0x9, 0x24, 0x49, 0x58, + 0x49, 0x9a, 0x4f, 0x92, 0x49, 0x2a, 0x68, 0x4e, + 0x78, 0x0, 0x98, 0x0, 0x87, 0xfe, + + /* U+8D8B "趋" */ + 0x10, 0x40, 0x21, 0x0, 0x43, 0xf3, 0xf8, 0x41, + 0x11, 0x2, 0x3f, 0xbf, 0x81, 0x4, 0x2, 0x49, + 0xfc, 0x90, 0x9, 0x38, 0x12, 0x40, 0x26, 0x9f, + 0xcd, 0x0, 0x26, 0x0, 0x43, 0xff, + + /* U+8D91 "趑" */ + 0x10, 0x20, 0x11, 0x20, 0x11, 0x20, 0x7d, 0x3e, + 0x10, 0xc2, 0x10, 0x42, 0xfe, 0x10, 0x8, 0x90, + 0x49, 0x10, 0x49, 0x18, 0x4d, 0x28, 0x49, 0x24, + 0x6a, 0x42, 0x68, 0x82, 0x98, 0x0, 0x87, 0xfe, + + /* U+8D94 "趔" */ + 0x20, 0x0, 0x4f, 0xcb, 0xe4, 0x51, 0x10, 0xa2, + 0x3d, 0x5f, 0x4a, 0x85, 0xe5, 0x28, 0x4a, 0x58, + 0x94, 0xa1, 0x9, 0x44, 0x12, 0x90, 0x2b, 0x41, + 0xd2, 0x0, 0x23, 0xff, 0x0, + + /* U+8D9F "趟" */ + 0x20, 0x40, 0x44, 0x8b, 0xe5, 0x21, 0x2, 0x2, + 0x3f, 0xc4, 0x40, 0x9c, 0xbd, 0x11, 0x4a, 0x9a, + 0x95, 0x45, 0xe9, 0x88, 0x13, 0x10, 0x2a, 0x21, + 0xd2, 0x0, 0x23, 0xff, 0x80, + + /* U+8DA3 "趣" */ + 0x20, 0x0, 0x4f, 0xc0, 0x89, 0x7f, 0xd2, 0x12, + 0x24, 0x24, 0x7a, 0xbe, 0x95, 0x11, 0xe6, 0x22, + 0x45, 0x34, 0x9a, 0x89, 0x33, 0x1f, 0xa6, 0x45, + 0x34, 0x8, 0x26, 0x0, 0x47, 0xff, + + /* U+8DB1 "趱" */ + 0x21, 0x8, 0x46, 0x50, 0x9e, 0xf3, 0xa8, 0x42, + 0x7b, 0xc4, 0x65, 0x7e, 0xa9, 0xa, 0x20, 0x13, + 0xfc, 0xb4, 0x9, 0x49, 0x12, 0x93, 0x25, 0x9, + 0x96, 0xe0, 0xa4, 0x0, 0x47, 0xfe, + + /* U+8DB3 "足" */ + 0x3f, 0xf8, 0x40, 0x10, 0x80, 0x21, 0x0, 0x43, + 0xff, 0x80, 0x20, 0x0, 0x40, 0x18, 0x80, 0x21, + 0xfc, 0x42, 0x0, 0xc4, 0x2, 0x48, 0x8, 0x7f, + 0xf0, 0x0, 0x0, + + /* U+8DB4 "趴" */ + 0x7c, 0x88, 0x44, 0x88, 0x44, 0x88, 0x44, 0x88, + 0x7c, 0x88, 0x10, 0x88, 0x50, 0x88, 0x5c, 0x88, + 0x51, 0x4, 0x51, 0x4, 0x51, 0x4, 0x5e, 0x2, + 0xe2, 0x2, 0x4, 0x0, + + /* U+8DB5 "趵" */ + 0x0, 0x0, 0xfc, 0x81, 0x9, 0x2, 0x13, 0xf4, + 0x28, 0x28, 0x50, 0x5f, 0xc0, 0x84, 0x1, 0x48, + 0x42, 0x9c, 0x45, 0x20, 0x8a, 0x40, 0x14, 0x80, + 0x29, 0x60, 0x7f, 0x1, 0x80, 0x3e, + + /* U+8DB8 "趸" */ + 0xff, 0xfc, 0x20, 0x0, 0xff, 0x8c, 0x0, 0xc0, + 0xf0, 0xff, 0xe4, 0x0, 0x90, 0x2, 0x3f, 0xf8, + 0x88, 0x2, 0x1f, 0x8c, 0x80, 0x5a, 0x2, 0x1f, + 0xf0, 0x0, 0x0, + + /* U+8DBA "趺" */ + 0x0, 0x20, 0x7c, 0x20, 0x44, 0x20, 0x44, 0xfc, + 0x7c, 0x20, 0x10, 0x20, 0x10, 0x20, 0x53, 0xff, + 0x5c, 0x20, 0x50, 0x50, 0x50, 0x50, 0x50, 0xd8, + 0x5d, 0x8c, 0xe3, 0x6, 0x6, 0x2, + + /* U+8DBC "趼" */ + 0x7d, 0xfe, 0x89, 0x11, 0x12, 0x23, 0xe4, 0x41, + 0x8, 0x82, 0x11, 0x14, 0xff, 0xae, 0x44, 0x50, + 0x88, 0xa1, 0x11, 0x42, 0x22, 0xe8, 0x4e, 0x10, + 0x80, 0x41, 0x0, + + /* U+8DBE "趾" */ + 0x0, 0x10, 0x7c, 0x10, 0x44, 0x90, 0x44, 0x90, + 0x44, 0x90, 0x7c, 0x9e, 0x10, 0x90, 0x50, 0x90, + 0x5c, 0x90, 0x50, 0x90, 0x50, 0x90, 0x50, 0x90, + 0x7c, 0x90, 0x83, 0xff, + + /* U+8DBF "趿" */ + 0x7d, 0xfc, 0x44, 0x84, 0x44, 0x88, 0x44, 0x88, + 0x38, 0xce, 0x10, 0xc2, 0x50, 0xc2, 0x5c, 0xa4, + 0x50, 0xa4, 0x51, 0x14, 0x51, 0x18, 0x5f, 0x1c, + 0xe2, 0x66, 0x4, 0xc0, + + /* U+8DC3 "跃" */ + 0x7c, 0x0, 0x8b, 0xf9, 0x10, 0x82, 0x21, 0x4, + 0x42, 0x7, 0x4, 0x4, 0xff, 0xa8, 0x10, 0x5c, + 0x60, 0xa0, 0xa1, 0x41, 0x42, 0x84, 0x45, 0xd0, + 0x9c, 0x60, 0x80, 0x80, 0x80, + + /* U+8DC4 "跄" */ + 0x0, 0x20, 0xf8, 0xc1, 0x13, 0x42, 0x24, 0xc4, + 0x50, 0xcf, 0xc0, 0xc4, 0x7e, 0x28, 0x84, 0x5d, + 0x8, 0xa2, 0x11, 0x45, 0xc2, 0x88, 0x15, 0x50, + 0x3f, 0x20, 0x80, 0x3f, 0x0, + + /* U+8DC6 "跆" */ + 0x0, 0x20, 0x7e, 0x40, 0x42, 0x40, 0x42, 0x84, + 0x43, 0x84, 0x3d, 0xfe, 0x8, 0x1, 0x48, 0x0, + 0x4f, 0xfe, 0x49, 0x2, 0x49, 0x2, 0x49, 0x2, + 0x4f, 0x2, 0xf1, 0xfe, 0x1, 0x2, + + /* U+8DCB "跋" */ + 0x0, 0x40, 0xf9, 0x31, 0x12, 0x2, 0x3f, 0xf4, + 0x48, 0x7, 0x10, 0x4, 0x3f, 0x28, 0xc2, 0x5d, + 0x88, 0xa4, 0x91, 0x49, 0x42, 0x81, 0x85, 0x45, + 0x1d, 0x31, 0x80, 0x80, 0x80, + + /* U+8DCC "跌" */ + 0x0, 0x10, 0x7c, 0x90, 0x44, 0x90, 0x44, 0xfe, + 0x45, 0x10, 0x7d, 0x10, 0x10, 0x10, 0x53, 0xff, + 0x5c, 0x10, 0x50, 0x30, 0x50, 0x38, 0x50, 0x48, + 0x5c, 0x84, 0xe1, 0x6, 0x2, 0x0, + + /* U+8DCE "跎" */ + 0x0, 0x20, 0xf8, 0x21, 0x17, 0xfa, 0x28, 0x14, + 0x50, 0x2f, 0x80, 0x4, 0x21, 0x28, 0x44, 0x5c, + 0x90, 0xa1, 0xc1, 0x42, 0xa, 0x84, 0x15, 0x8, + 0x2f, 0x90, 0xe0, 0x3f, 0x0, + + /* U+8DCF "跏" */ + 0x1, 0x0, 0xf2, 0x1, 0x24, 0x7a, 0x4e, 0x97, + 0x95, 0x22, 0x2a, 0x44, 0x54, 0xa8, 0xa9, 0x5d, + 0x52, 0xa2, 0xa5, 0x45, 0x4a, 0x9a, 0x97, 0xe5, + 0xf8, 0x4a, 0x41, 0x70, 0x0, + + /* U+8DD1 "跑" */ + 0x0, 0x80, 0xf9, 0x1, 0x13, 0xf2, 0x28, 0x24, + 0x60, 0x4f, 0xfe, 0x84, 0x45, 0x28, 0x8a, 0x5d, + 0xf4, 0xa2, 0x9, 0x45, 0xea, 0x88, 0x15, 0xd0, + 0x3e, 0x20, 0x40, 0x3f, 0x0, + + /* U+8DD6 "跖" */ + 0x7d, 0xfe, 0x88, 0x41, 0x11, 0x2, 0x22, 0x7, + 0xc8, 0x2, 0x10, 0x14, 0x3f, 0x2e, 0xc2, 0x52, + 0x84, 0xa1, 0x9, 0x42, 0x12, 0x94, 0x27, 0xcf, + 0xc0, 0x10, 0x80, + + /* U+8DD7 "跗" */ + 0x0, 0x80, 0xf1, 0x11, 0x24, 0x22, 0x4b, 0xf7, + 0xb0, 0x82, 0x61, 0x5, 0x52, 0x28, 0x94, 0x5d, + 0x28, 0xa2, 0x11, 0x44, 0x22, 0x88, 0x45, 0x50, + 0x9f, 0x21, 0x0, 0x4e, 0x0, + + /* U+8DDA "跚" */ + 0x79, 0xdc, 0x94, 0xa9, 0x29, 0x53, 0xd2, 0xa1, + 0x25, 0x42, 0xff, 0xd4, 0x95, 0x2f, 0x2a, 0x52, + 0x54, 0xa4, 0xa9, 0x49, 0x52, 0xf2, 0xae, 0x46, + 0x40, 0xbd, 0x80, + + /* U+8DDB "跛" */ + 0x0, 0x10, 0xf8, 0x21, 0x17, 0xfa, 0x28, 0x94, + 0x51, 0x27, 0x22, 0x4, 0x7f, 0xa8, 0xa2, 0x5d, + 0x44, 0xa2, 0x89, 0x44, 0xa2, 0x90, 0xc5, 0xe3, + 0x9c, 0x48, 0x81, 0x60, 0x80, + + /* U+8DDD "距" */ + 0x7d, 0xfe, 0x8a, 0x1, 0x14, 0x2, 0x28, 0x3, + 0x9f, 0xe1, 0x20, 0x52, 0x40, 0xa7, 0x81, 0x49, + 0xfe, 0x92, 0x1, 0x24, 0x2, 0xf8, 0xa, 0x1f, + 0xe0, + + /* U+8DDE "跞" */ + 0x7c, 0xfe, 0x45, 0x0, 0x45, 0x10, 0x45, 0x10, + 0x7d, 0x10, 0x11, 0x10, 0x51, 0xfe, 0x5c, 0x10, + 0x50, 0x10, 0x50, 0x94, 0x51, 0x12, 0x5d, 0x12, + 0xe2, 0x10, 0x0, 0xf0, + + /* U+8DDF "跟" */ + 0x7d, 0xfc, 0x8a, 0x9, 0x14, 0x13, 0xef, 0xe1, + 0x10, 0x42, 0x20, 0x94, 0x7f, 0x2e, 0x90, 0x51, + 0x26, 0xa2, 0x51, 0x44, 0x42, 0xa8, 0x4e, 0x94, + 0x60, 0x30, 0x0, + + /* U+8DE3 "跣" */ + 0x0, 0x20, 0xf9, 0x41, 0x12, 0x82, 0x27, 0xf4, + 0x52, 0xf, 0xa4, 0x4, 0x8, 0x29, 0xff, 0x5c, + 0x50, 0xa0, 0xa1, 0x41, 0x42, 0x84, 0x85, 0xc9, + 0x3c, 0x22, 0x40, 0x87, 0x80, + + /* U+8DE4 "跤" */ + 0x0, 0x0, 0xf8, 0x41, 0x10, 0x2, 0x2f, 0xe7, + 0xc8, 0x82, 0x21, 0x84, 0x81, 0xa9, 0x85, 0x5c, + 0x90, 0xa1, 0x21, 0x41, 0x42, 0x81, 0x5, 0x45, + 0x1d, 0x31, 0x80, 0x80, 0x80, + + /* U+8DE8 "跨" */ + 0x0, 0x20, 0xf0, 0x41, 0x2f, 0xfa, 0x42, 0x84, + 0x88, 0x86, 0x7f, 0xc4, 0x0, 0x28, 0x0, 0x5f, + 0xfe, 0xa2, 0x1, 0x47, 0xf2, 0x80, 0x25, 0x40, + 0x5f, 0x0, 0x80, 0x1e, 0x0, + + /* U+8DEA "跪" */ + 0x0, 0x40, 0xf1, 0x1, 0x23, 0xf2, 0x48, 0x44, + 0x91, 0x89, 0x7f, 0xde, 0x40, 0x8, 0x80, 0x51, + 0x7c, 0xba, 0x89, 0x45, 0x12, 0x8a, 0xe5, 0x14, + 0x2b, 0xa8, 0x78, 0x90, 0x83, 0x3e, + + /* U+8DEB "跫" */ + 0x7d, 0xf8, 0x22, 0x10, 0x45, 0xab, 0x78, 0x70, + 0x20, 0x3, 0xff, 0x8, 0x2, 0x10, 0x4, 0x1f, + 0xf8, 0x22, 0x0, 0x47, 0xf1, 0x48, 0x2, 0xd0, + 0x8, 0x7f, 0xc0, 0x0, 0x0, + + /* U+8DEC "跬" */ + 0x0, 0x20, 0x7c, 0x20, 0x45, 0xfe, 0x44, 0x20, + 0x7c, 0x20, 0x10, 0x20, 0x13, 0xff, 0x50, 0x0, + 0x58, 0x20, 0x51, 0xfe, 0x50, 0x20, 0x50, 0x20, + 0x7c, 0x20, 0xc3, 0xff, + + /* U+8DEF "路" */ + 0x0, 0x40, 0x7c, 0x80, 0x44, 0xfc, 0x45, 0x84, + 0x46, 0x8c, 0x7c, 0x58, 0x10, 0x30, 0x10, 0x58, + 0x51, 0x86, 0x5e, 0x0, 0x51, 0xfc, 0x51, 0x4, + 0x51, 0x4, 0x5d, 0x4, 0xe1, 0xfc, 0x1, 0x4, + + /* U+8DF3 "跳" */ + 0x0, 0x50, 0xf0, 0xa1, 0x21, 0x42, 0x52, 0x97, + 0x95, 0x42, 0x2b, 0x4, 0x14, 0x28, 0x28, 0x5c, + 0xd8, 0xa6, 0xad, 0x41, 0x42, 0x84, 0x85, 0x49, + 0x3f, 0x22, 0x41, 0x87, 0x80, + + /* U+8DF5 "践" */ + 0x0, 0x40, 0x7c, 0x58, 0x44, 0x44, 0x44, 0x42, + 0x47, 0xfc, 0x7c, 0x40, 0x10, 0x40, 0x50, 0xfe, + 0x5d, 0x20, 0x50, 0x24, 0x50, 0x28, 0x50, 0x31, + 0x5c, 0x72, 0xe3, 0x92, 0x0, 0xe, + + /* U+8DF7 "跷" */ + 0x0, 0x80, 0x78, 0x80, 0x48, 0xbe, 0x4b, 0xc0, + 0x48, 0x48, 0x78, 0x72, 0x10, 0xe2, 0x53, 0x1e, + 0x5c, 0x0, 0x53, 0xfe, 0x50, 0x90, 0x50, 0x90, + 0x54, 0x91, 0xe9, 0x12, 0x6, 0xe, + + /* U+8DF8 "跸" */ + 0x1, 0x10, 0xf2, 0x21, 0x24, 0x5a, 0x4e, 0xc4, + 0x91, 0xf, 0x22, 0x44, 0x44, 0xa8, 0xee, 0x5d, + 0x0, 0xa0, 0x41, 0x4f, 0xfa, 0x81, 0x5, 0x42, + 0x1d, 0x4, 0x0, 0x8, 0x0, + + /* U+8DF9 "跹" */ + 0x2, 0x0, 0xfa, 0x7d, 0x14, 0x22, 0x20, 0x47, + 0xc0, 0x82, 0x1, 0x4, 0xdf, 0xa8, 0x84, 0x5d, + 0x8, 0xa2, 0x11, 0x44, 0x22, 0x88, 0x45, 0xd0, + 0x9c, 0x50, 0x0, 0x1f, 0x80, + + /* U+8DFA "跺" */ + 0x7c, 0xf8, 0x89, 0x11, 0x12, 0x22, 0x24, 0x43, + 0x90, 0xe2, 0x4, 0x14, 0x8, 0x2e, 0xff, 0x50, + 0x70, 0xa1, 0x61, 0x42, 0xa2, 0xe9, 0x2a, 0x22, + 0x20, 0x4, 0x0, + + /* U+8DFB "跻" */ + 0x0, 0x20, 0xf8, 0x41, 0x1f, 0xfa, 0x28, 0x27, + 0xc8, 0x82, 0xa, 0x4, 0x1c, 0x28, 0xc7, 0x5e, + 0x2, 0xa1, 0x11, 0x42, 0x22, 0x84, 0x45, 0x48, + 0x9d, 0x21, 0x0, 0x82, 0x0, + + /* U+8DFD "跽" */ + 0x7d, 0xfc, 0x44, 0x4, 0x44, 0x4, 0x45, 0xfc, + 0x7d, 0x0, 0x11, 0x2, 0x51, 0x2, 0x5c, 0xfc, + 0x50, 0x20, 0x50, 0xa0, 0x52, 0x82, 0x5a, 0x86, + 0xa4, 0x88, 0x0, 0x78, + + /* U+8E05 "踅" */ + 0x8, 0x0, 0x8, 0xfc, 0x3c, 0x80, 0x7e, 0xfe, + 0x8, 0x88, 0x11, 0x8, 0x20, 0x0, 0x3f, 0xfc, + 0x20, 0x4, 0x3f, 0xfc, 0x10, 0x80, 0x10, 0xfc, + 0x18, 0x80, 0x2c, 0x80, 0x43, 0xfe, 0x0, 0x0, + + /* U+8E09 "踉" */ + 0x0, 0x20, 0xf8, 0x41, 0x17, 0xf2, 0x28, 0x24, + 0x50, 0x4f, 0xbf, 0x84, 0x41, 0x8, 0x82, 0x51, + 0xfc, 0xba, 0x41, 0x44, 0x9a, 0x89, 0x45, 0x11, + 0xa, 0xa1, 0x3a, 0x71, 0x0, 0x81, + + /* U+8E0A "踊" */ + 0x79, 0xfe, 0x90, 0xd, 0x22, 0x32, 0x43, 0xc4, + 0x81, 0xf, 0x3f, 0xc4, 0x48, 0xa8, 0xff, 0x5d, + 0x22, 0xa2, 0x45, 0x47, 0xfa, 0x89, 0x15, 0xd2, + 0x3c, 0x20, 0x40, 0x43, 0x80, + + /* U+8E0C "踌" */ + 0x0, 0x20, 0xf8, 0x41, 0x1f, 0xfa, 0x22, 0x4, + 0x44, 0xf, 0xbf, 0x84, 0x10, 0x29, 0xff, 0x5c, + 0x88, 0xa1, 0xfd, 0x44, 0x22, 0x8a, 0x47, 0xa2, + 0x98, 0x81, 0x0, 0xe, 0x0, + + /* U+8E0F "踏" */ + 0x0, 0x20, 0xf8, 0x41, 0x1e, 0x92, 0x25, 0xc7, + 0xd2, 0x82, 0x24, 0x84, 0xb8, 0xa8, 0x0, 0x5d, + 0xfc, 0xa4, 0x9, 0x48, 0x12, 0x8f, 0xe5, 0x60, + 0x5d, 0x3f, 0x80, 0x81, 0x0, + + /* U+8E14 "踔" */ + 0x0, 0x20, 0xf8, 0x7d, 0x10, 0x82, 0x21, 0x4, + 0x5f, 0xef, 0xa0, 0x44, 0x7f, 0xa8, 0x81, 0x5d, + 0x2, 0xa3, 0xfd, 0x40, 0x82, 0x9f, 0xf5, 0xc2, + 0x1c, 0x4, 0x0, 0x8, 0x0, + + /* U+8E1D "踝" */ + 0x79, 0xfc, 0x94, 0x49, 0x27, 0xf3, 0xd1, 0x21, + 0x22, 0x42, 0x3f, 0x97, 0x8, 0x28, 0x10, 0x53, + 0xfe, 0xa0, 0xc1, 0x42, 0xc2, 0xe9, 0x6e, 0x22, + 0x20, 0x4, 0x0, + + /* U+8E1E "踞" */ + 0x7d, 0xfe, 0x45, 0x2, 0x45, 0x2, 0x7d, 0xfc, + 0x11, 0x0, 0x11, 0x10, 0x51, 0xfe, 0x5d, 0x10, + 0x51, 0x10, 0x51, 0x7e, 0x51, 0x82, 0x5e, 0x82, + 0xe2, 0x7e, 0x4, 0x82, + + /* U+8E1F "踟" */ + 0x1, 0x0, 0xf2, 0x1, 0x27, 0x3a, 0x54, 0x97, + 0xa9, 0x22, 0x12, 0x44, 0x24, 0xa9, 0xf9, 0x5c, + 0x92, 0xa1, 0x25, 0x45, 0x4a, 0xab, 0x97, 0x92, + 0xf0, 0x42, 0x40, 0x0, 0x0, + + /* U+8E22 "踢" */ + 0x7d, 0xfc, 0x8a, 0x9, 0x17, 0xf2, 0x28, 0x23, + 0x9f, 0xc2, 0x0, 0x14, 0x40, 0x2e, 0xff, 0x52, + 0x52, 0xa1, 0x25, 0x42, 0x4a, 0x89, 0x1f, 0xe2, + 0x20, 0xb, 0x80, + + /* U+8E23 "踣" */ + 0x0, 0x20, 0xf0, 0x41, 0x2f, 0xfa, 0x40, 0x4, + 0x88, 0x86, 0x2, 0x4, 0xff, 0xa8, 0x0, 0x5c, + 0x0, 0xa3, 0xf9, 0x44, 0x12, 0x88, 0x25, 0x50, + 0x5d, 0x3f, 0x80, 0x41, 0x0, + + /* U+8E29 "踩" */ + 0x0, 0x0, 0xf7, 0xf9, 0x20, 0x2, 0x51, 0x24, + 0x92, 0x4f, 0x21, 0x4, 0x8, 0x28, 0x10, 0x5f, + 0xfe, 0xa0, 0xc1, 0x42, 0xc2, 0x8d, 0x47, 0xb2, + 0x50, 0x84, 0x40, 0x8, 0x0, + + /* U+8E2A "踪" */ + 0x0, 0x20, 0x7c, 0x10, 0x45, 0xff, 0x45, 0x1, + 0x45, 0x1, 0x7c, 0xfe, 0x10, 0x0, 0x50, 0x0, + 0x5d, 0xff, 0x50, 0x10, 0x50, 0x94, 0x50, 0x92, + 0x5d, 0x11, 0xe1, 0x10, 0x0, 0x60, + + /* U+8E2C "踬" */ + 0x0, 0x2, 0xf7, 0xf9, 0x28, 0x42, 0x5f, 0xf7, + 0xa1, 0x2, 0x42, 0x4, 0xbf, 0xa9, 0x41, 0x5e, + 0x82, 0xa5, 0x25, 0x4a, 0x4a, 0x94, 0x95, 0xc2, + 0x94, 0x98, 0xc1, 0x40, 0x0, + + /* U+8E2E "踮" */ + 0x0, 0x10, 0x78, 0x10, 0x49, 0xff, 0x49, 0x10, + 0x79, 0x10, 0x11, 0x1f, 0x11, 0x10, 0x5d, 0x10, + 0x51, 0x7e, 0x51, 0x42, 0x51, 0x42, 0x52, 0x42, + 0x7a, 0x42, 0xc2, 0x7e, 0x4, 0x42, + + /* U+8E2F "踯" */ + 0x2, 0x20, 0x7a, 0x4e, 0x49, 0x52, 0x4b, 0xf2, + 0x78, 0x94, 0x10, 0x94, 0x10, 0x94, 0x50, 0x92, + 0x5b, 0xf1, 0x51, 0x11, 0x51, 0x91, 0x51, 0x51, + 0x52, 0x36, 0xfa, 0x10, 0x4, 0x10, + + /* U+8E31 "踱" */ + 0x0, 0x0, 0x78, 0x10, 0x49, 0xff, 0x49, 0x0, + 0x49, 0x44, 0x79, 0xff, 0x11, 0x44, 0x11, 0x44, + 0x51, 0x7c, 0x51, 0x0, 0x59, 0xfe, 0x51, 0x42, + 0x51, 0x24, 0x56, 0x38, 0xfa, 0x3c, 0x4, 0xc3, + + /* U+8E35 "踵" */ + 0x79, 0xfe, 0x48, 0x20, 0x4b, 0xff, 0x48, 0x20, + 0x31, 0xfe, 0x11, 0x22, 0x51, 0xfe, 0x5d, 0x22, + 0x51, 0xfe, 0x50, 0x20, 0x51, 0xfe, 0x7c, 0x20, + 0x3, 0xff, + + /* U+8E39 "踹" */ + 0x0, 0x20, 0x79, 0x22, 0x49, 0x22, 0x49, 0xfe, + 0x48, 0x0, 0x78, 0x0, 0x13, 0xff, 0x50, 0x20, + 0x5f, 0xfe, 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, + 0x5e, 0x52, 0xe2, 0x42, 0x2, 0xe, + + /* U+8E3A "踺" */ + 0x78, 0x10, 0x4f, 0x7e, 0x49, 0x12, 0x4a, 0xff, + 0x7a, 0x12, 0x14, 0x12, 0x17, 0x3c, 0x51, 0x10, + 0x59, 0x7e, 0x55, 0x10, 0x56, 0x7f, 0x52, 0x10, + 0x7b, 0x0, 0x84, 0xff, 0x0, 0x0, + + /* U+8E3D "踽" */ + 0x7f, 0xfe, 0x44, 0x20, 0x45, 0xfe, 0x46, 0x22, + 0x3a, 0x22, 0x11, 0xfe, 0x50, 0x20, 0x5f, 0xff, + 0x52, 0x21, 0x52, 0x29, 0x52, 0xf9, 0x5e, 0x5, + 0xe2, 0x2, 0x2, 0xe, + + /* U+8E40 "蹀" */ + 0x0, 0x24, 0x2, 0x49, 0xe4, 0x92, 0x5f, 0xf4, + 0x92, 0x4f, 0x27, 0x84, 0x40, 0x8, 0x80, 0x51, + 0xfe, 0xb8, 0x41, 0x4f, 0xfa, 0x83, 0x85, 0xa, + 0x8b, 0xa4, 0xb8, 0x88, 0x80, 0x10, + + /* U+8E41 "蹁" */ + 0x0, 0x20, 0xf0, 0x41, 0x2f, 0xfa, 0x50, 0x14, + 0xa0, 0x2f, 0x7f, 0xc4, 0x80, 0x9, 0x0, 0x53, + 0xfe, 0xbe, 0x55, 0x4c, 0xaa, 0x97, 0xf5, 0x32, + 0xaf, 0xa5, 0x61, 0x4a, 0x82, 0x83, + + /* U+8E42 "蹂" */ + 0x79, 0xfe, 0x90, 0x19, 0x23, 0xc2, 0x40, 0x87, + 0x9f, 0xe2, 0x12, 0x44, 0x45, 0x2f, 0x30, 0x5c, + 0x0, 0xa7, 0xfd, 0x41, 0xc2, 0x85, 0x47, 0xd2, + 0x40, 0xc4, 0x40, 0x8, 0x0, + + /* U+8E44 "蹄" */ + 0x0, 0x20, 0x78, 0x20, 0x4b, 0xfe, 0x48, 0x84, + 0x48, 0x48, 0x33, 0xff, 0x12, 0x1, 0x52, 0x21, + 0x5d, 0xfe, 0x51, 0x22, 0x51, 0x22, 0x51, 0x22, + 0x5d, 0x22, 0xf1, 0x2e, 0x0, 0x20, + + /* U+8E47 "蹇" */ + 0x1, 0x1, 0xff, 0xfa, 0x21, 0x11, 0xff, 0xc0, + 0x84, 0x7, 0xfe, 0x3f, 0xff, 0x88, 0x10, 0x3f, + 0xfd, 0xc0, 0x24, 0x7f, 0xc0, 0x8, 0x2, 0x1f, + 0x87, 0x20, 0x33, 0xff, 0x80, + + /* U+8E48 "蹈" */ + 0x0, 0x0, 0xf7, 0xfd, 0x20, 0x8a, 0x49, 0x17, + 0x88, 0x42, 0x0, 0x4, 0x7f, 0xa8, 0x81, 0x5d, + 0x2, 0xa2, 0x5, 0x47, 0xfa, 0x88, 0x15, 0xd0, + 0x3e, 0x3f, 0xc0, 0x40, 0x80, + + /* U+8E49 "蹉" */ + 0x0, 0x88, 0xf0, 0x91, 0x2f, 0xfa, 0x42, 0x4, + 0x84, 0xf, 0x3f, 0x84, 0x10, 0x29, 0xff, 0x5c, + 0x80, 0xa1, 0x1, 0x45, 0xf2, 0x91, 0x5, 0x82, + 0x14, 0x3f, 0xc0, + + /* U+8E4A "蹊" */ + 0x0, 0x0, 0x79, 0xfe, 0x48, 0x22, 0x49, 0x16, + 0x48, 0x20, 0x78, 0xc4, 0x11, 0xf8, 0x10, 0x40, + 0x5d, 0xfe, 0x50, 0x0, 0x53, 0xff, 0x50, 0x30, + 0x5c, 0x48, 0xe1, 0x87, 0x2, 0x0, + + /* U+8E4B "蹋" */ + 0x79, 0xfc, 0x92, 0x9, 0x27, 0xf2, 0x48, 0x27, + 0x9f, 0xc2, 0x0, 0x14, 0xf7, 0xaf, 0x29, 0x51, + 0x52, 0xa0, 0x8d, 0x47, 0x2a, 0x92, 0x9f, 0x84, + 0x20, 0x39, 0x80, + + /* U+8E51 "蹑" */ + 0x7b, 0xfe, 0x49, 0x4, 0x49, 0x4, 0x48, 0xfc, + 0x49, 0x4, 0x78, 0xfc, 0x11, 0x4, 0x53, 0xfe, + 0x5c, 0x4, 0x53, 0xfe, 0x50, 0x52, 0x53, 0x92, + 0x54, 0x8c, 0xf9, 0x4c, 0x6, 0x33, + + /* U+8E52 "蹒" */ + 0x0, 0x88, 0xf1, 0x11, 0x2f, 0xfa, 0x44, 0x44, + 0x80, 0xf, 0x7f, 0xc4, 0x24, 0x29, 0xff, 0x5a, + 0x92, 0xa5, 0xa5, 0x4b, 0x6a, 0x99, 0x35, 0x32, + 0x3d, 0x40, 0x40, 0x87, 0x0, + + /* U+8E59 "蹙" */ + 0x0, 0x48, 0xff, 0xfd, 0x3d, 0x12, 0x42, 0x47, + 0xfb, 0x8, 0x8c, 0x69, 0x44, 0xc4, 0x6, 0x3f, + 0xf8, 0x40, 0x10, 0xff, 0xe1, 0x8, 0x2, 0x1f, + 0xcb, 0x20, 0x33, 0xff, 0x80, 0x0, + + /* U+8E66 "蹦" */ + 0x0, 0x20, 0xf4, 0x45, 0x28, 0x8a, 0x5f, 0xf4, + 0x80, 0xf, 0x7b, 0xc4, 0x94, 0xa9, 0xef, 0x5e, + 0x52, 0xa4, 0xa5, 0x4f, 0x7a, 0x92, 0x95, 0xe5, + 0x3c, 0x8a, 0x41, 0x79, 0x80, + + /* U+8E69 "蹩" */ + 0x2a, 0x20, 0x8, 0x3e, 0x7f, 0x44, 0x49, 0x6c, + 0x6d, 0x38, 0x6b, 0x18, 0x49, 0x66, 0x3f, 0xf8, + 0x20, 0x8, 0x3f, 0xf8, 0x11, 0x0, 0x11, 0xfc, + 0x31, 0x0, 0x29, 0x0, 0xc7, 0xfe, + + /* U+8E6C "蹬" */ + 0x0, 0x20, 0xf7, 0xd9, 0x21, 0x42, 0x54, 0xb4, + 0x90, 0x8f, 0x7f, 0x85, 0x0, 0xa8, 0xfe, 0x5d, + 0x4, 0xa2, 0x9, 0x47, 0xf2, 0x84, 0x45, 0xc5, + 0x14, 0xff, 0xc0, + + /* U+8E6D "蹭" */ + 0x78, 0x88, 0x91, 0x21, 0x2f, 0xfa, 0x51, 0x57, + 0xaa, 0xa2, 0x44, 0x44, 0xff, 0xa8, 0x0, 0x59, + 0xfc, 0xa2, 0x9, 0x44, 0x12, 0x8f, 0xe5, 0xd0, + 0x54, 0x3f, 0x80, 0x41, 0x0, + + /* U+8E6F "蹯" */ + 0x7b, 0xfc, 0x90, 0x49, 0x24, 0xa2, 0x41, 0x7, + 0xbf, 0xe2, 0xe, 0x14, 0x6b, 0x2f, 0xfe, 0x51, + 0x24, 0xa2, 0x49, 0x47, 0xf2, 0xa9, 0x2e, 0x9f, + 0xc0, 0x20, 0x80, + + /* U+8E70 "蹰" */ + 0x7b, 0xff, 0x4a, 0x0, 0x4a, 0xe2, 0x7a, 0x2, + 0x12, 0xef, 0x13, 0x22, 0x53, 0x22, 0x5f, 0x2a, + 0x52, 0xea, 0x52, 0xa, 0x52, 0xa2, 0x5a, 0xc2, + 0xe5, 0xf2, 0x4, 0xe, + + /* U+8E72 "蹲" */ + 0x0, 0x0, 0x78, 0x48, 0x4b, 0xff, 0x48, 0x50, + 0x49, 0xfe, 0x7a, 0x52, 0x12, 0x8e, 0x12, 0x2, + 0x51, 0xfe, 0x52, 0x2, 0x59, 0xfe, 0x50, 0x4, + 0x53, 0xff, 0x50, 0x4, 0xf8, 0xc4, 0x0, 0x38, + + /* U+8E74 "蹴" */ + 0x1, 0x10, 0x79, 0x14, 0x4f, 0xd2, 0x48, 0x10, + 0x48, 0x10, 0x7b, 0xbe, 0x14, 0x94, 0x54, 0x94, + 0x5b, 0x14, 0x51, 0x14, 0x55, 0x54, 0x55, 0x65, + 0x55, 0x25, 0xe9, 0x26, 0x3, 0x46, + + /* U+8E76 "蹶" */ + 0x7b, 0xff, 0x4a, 0x4, 0x4a, 0xa4, 0x7b, 0xf7, + 0x12, 0x4a, 0x12, 0x42, 0x53, 0x56, 0x5b, 0x54, + 0x53, 0x54, 0x52, 0xf4, 0x52, 0x4c, 0x54, 0x8a, + 0xfc, 0x8a, 0x5, 0x11, + + /* U+8E7C "蹼" */ + 0x0, 0x48, 0x0, 0x48, 0x7a, 0x4a, 0x49, 0x4c, + 0x4b, 0xff, 0x78, 0x4, 0x10, 0x88, 0x13, 0xff, + 0x50, 0x20, 0x59, 0xfe, 0x50, 0x20, 0x50, 0x20, + 0x53, 0xff, 0x54, 0x30, 0x78, 0xc8, 0x3, 0x7, + 0x4, 0x0, + + /* U+8E7F "蹿" */ + 0x0, 0x0, 0x78, 0x20, 0x4b, 0xff, 0x4a, 0x1, + 0x48, 0xd8, 0x4b, 0x6, 0x31, 0xfe, 0x12, 0x22, + 0x5d, 0xfe, 0x50, 0x20, 0x53, 0xfe, 0x52, 0x22, + 0x52, 0x22, 0x53, 0xfe, 0xa8, 0x20, 0x0, 0x20, + + /* U+8E81 "躁" */ + 0x79, 0xfc, 0x49, 0x4, 0x49, 0xfc, 0x48, 0x0, + 0x33, 0xde, 0x12, 0x52, 0x53, 0xde, 0x58, 0x0, + 0x50, 0x20, 0x53, 0xff, 0x50, 0x70, 0x5c, 0xa8, + 0xe3, 0x27, 0x0, 0x20, + + /* U+8E85 "躅" */ + 0x79, 0xfe, 0x92, 0xa5, 0x25, 0x4a, 0x4f, 0xf7, + 0x88, 0x2, 0x3f, 0xc4, 0xd0, 0xa8, 0x21, 0x5d, + 0xfa, 0xa2, 0x95, 0x47, 0xea, 0x82, 0x97, 0xbf, + 0xb0, 0x0, 0x40, 0x7, 0x0, + + /* U+8E87 "躇" */ + 0x78, 0x88, 0x4b, 0xff, 0x48, 0x88, 0x48, 0x0, + 0x79, 0xfa, 0x10, 0x24, 0x10, 0x28, 0x13, 0xff, + 0x50, 0x20, 0x58, 0xfe, 0x57, 0x82, 0x50, 0xfe, + 0x5c, 0x82, 0xe0, 0xfe, 0x0, 0x82, + + /* U+8E8F "躏" */ + 0x0, 0x88, 0xff, 0xfd, 0x22, 0x22, 0x5b, 0xf4, + 0x90, 0x26, 0x92, 0x45, 0x3f, 0xaa, 0xd1, 0x5e, + 0xfa, 0xa9, 0x45, 0x53, 0xea, 0xa5, 0x17, 0xcf, + 0xf0, 0x90, 0x41, 0x3, 0x0, + + /* U+8E90 "躐" */ + 0x0, 0x90, 0x79, 0x24, 0x4a, 0x68, 0x49, 0x4, + 0x7b, 0xfe, 0x12, 0x72, 0x12, 0x72, 0x52, 0x8a, + 0x5f, 0xfe, 0x50, 0x0, 0x52, 0xb4, 0x52, 0x24, + 0x56, 0xac, 0xea, 0xad, 0x3, 0x32, + + /* U+8E94 "躔" */ + 0x0, 0x0, 0x78, 0x10, 0x4b, 0xff, 0x4a, 0x0, + 0x4a, 0xfe, 0x7a, 0xfe, 0x12, 0x92, 0x12, 0x7c, + 0x52, 0xfe, 0x5e, 0x10, 0x52, 0xff, 0x52, 0x44, + 0x53, 0x93, 0x5a, 0xfe, 0xe4, 0x10, 0x5, 0xff, + 0x4, 0x0, + + /* U+8E9C "躜" */ + 0x0, 0x84, 0x7a, 0x94, 0x4b, 0xde, 0x48, 0xa4, + 0x7b, 0x9e, 0x12, 0x95, 0x12, 0xe2, 0x50, 0x0, + 0x5f, 0xfe, 0x52, 0x2, 0x52, 0x22, 0x52, 0x22, + 0x5c, 0x58, 0xa1, 0x86, 0x6, 0x0, + + /* U+8E9E "躞" */ + 0x2, 0x22, 0x3d, 0x39, 0x12, 0xa2, 0x89, 0x65, + 0x64, 0xa7, 0x23, 0xd0, 0x10, 0x4d, 0xd8, 0xaa, + 0xaa, 0x5c, 0x79, 0x28, 0x0, 0x15, 0xff, 0x8a, + 0x20, 0x85, 0xd, 0x83, 0x83, 0x80, 0x1e, 0x3c, + + /* U+8EAB "身" */ + 0x1, 0x0, 0x2, 0x0, 0x1f, 0xf8, 0x10, 0x8, + 0x10, 0x8, 0x1f, 0xf8, 0x10, 0x8, 0x1f, 0xfb, + 0x10, 0xe, 0x10, 0xc, 0xff, 0xf8, 0x0, 0x28, + 0x0, 0xc8, 0x3, 0x8, 0x1c, 0x8, 0x60, 0xf0, + + /* U+8EAC "躬" */ + 0x10, 0x0, 0x43, 0xf7, 0xe0, 0x50, 0x81, 0x7e, + 0x7d, 0x9, 0x7, 0xe4, 0x10, 0xa0, 0x42, 0x7f, + 0xf8, 0x10, 0xa0, 0x44, 0x81, 0x22, 0x7, 0x8, + 0x10, 0xe7, 0x80, + + /* U+8EAF "躯" */ + 0x10, 0x0, 0x43, 0xfd, 0xf4, 0x2, 0x2a, 0x27, + 0xd4, 0x48, 0xa5, 0x1f, 0x46, 0x22, 0x88, 0x45, + 0x19, 0xfa, 0x50, 0x35, 0x10, 0xaa, 0x26, 0x50, + 0x10, 0xbf, 0xc7, 0x0, 0x0, + + /* U+8EB2 "躲" */ + 0x10, 0x0, 0x41, 0xf1, 0xf2, 0x22, 0x24, 0x44, + 0x48, 0x8f, 0xa1, 0x11, 0x43, 0x3e, 0x10, 0x44, + 0x20, 0x8b, 0xfb, 0xf1, 0xc0, 0x65, 0x81, 0x4a, + 0x8c, 0xa4, 0xa1, 0x88, 0x8e, 0x10, + + /* U+8EBA "躺" */ + 0x20, 0x0, 0x44, 0x45, 0xe4, 0x92, 0x49, 0x64, + 0x82, 0xf, 0x7f, 0xd2, 0x80, 0xa5, 0x1, 0x7a, + 0xfa, 0x95, 0x17, 0xea, 0x28, 0xd4, 0x52, 0xaf, + 0xa9, 0x40, 0x62, 0x80, 0x9d, 0xe, + + /* U+8ECE "軎" */ + 0x1, 0x0, 0x2, 0x3, 0xff, 0xf8, 0x8, 0x3, + 0xff, 0x88, 0x21, 0xf, 0xfe, 0x20, 0x84, 0x3f, + 0xf8, 0x2, 0x1, 0xff, 0xf1, 0xff, 0xc2, 0x0, + 0x84, 0x1, 0xf, 0xfe, 0x10, 0x4, + + /* U+8F66 "车" */ + 0x0, 0x0, 0x4, 0x0, 0x8, 0x3, 0xff, 0xf0, + 0x40, 0x1, 0x10, 0x4, 0x20, 0x8, 0x40, 0x3f, + 0xfc, 0x1, 0x0, 0x2, 0x0, 0x4, 0xf, 0xff, + 0xe0, 0x10, 0x0, 0x20, 0x0, 0x40, + + /* U+8F67 "轧" */ + 0x10, 0x40, 0x20, 0x83, 0xfd, 0x1, 0x2, 0x2, + 0x84, 0x9, 0x8, 0x12, 0x10, 0x3f, 0xa0, 0x8, + 0x40, 0x10, 0x80, 0x2d, 0xf, 0xe2, 0x10, 0x84, + 0x21, 0x7, 0x82, 0x0, 0x0, + + /* U+8F68 "轨" */ + 0x0, 0x80, 0x41, 0x0, 0x82, 0x7, 0xe4, 0x4, + 0x3f, 0xa, 0x12, 0x14, 0x24, 0x48, 0x48, 0xfc, + 0x90, 0x21, 0x20, 0x42, 0x40, 0xe4, 0x9f, 0x11, + 0x42, 0x22, 0x84, 0x85, 0xa, 0xe, + + /* U+8F69 "轩" */ + 0x20, 0x0, 0x43, 0xfb, 0xf0, 0x82, 0x1, 0x5, + 0x2, 0xa, 0x4, 0x24, 0x8, 0x7f, 0xff, 0x10, + 0x20, 0x20, 0x40, 0x70, 0x87, 0x81, 0x1, 0x2, + 0x2, 0x4, 0x4, 0x8, 0x0, + + /* U+8F6B "轫" */ + 0x20, 0x0, 0x8f, 0xff, 0xc4, 0x50, 0x11, 0x51, + 0x45, 0x45, 0x19, 0x24, 0x7f, 0x91, 0x12, 0x44, + 0x42, 0x11, 0xc8, 0x7c, 0x21, 0x11, 0x4, 0x4d, + 0xe1, 0x20, 0x0, + + /* U+8F6C "转" */ + 0x20, 0x20, 0x40, 0x43, 0xf7, 0xfa, 0x2, 0x4, + 0x4, 0x12, 0x7f, 0xe4, 0x20, 0x7e, 0x40, 0x10, + 0xfc, 0x20, 0x8, 0x78, 0x27, 0x84, 0x81, 0x7, + 0x2, 0x2, 0x4, 0x2, 0x0, + + /* U+8F6D "轭" */ + 0x10, 0x0, 0x43, 0xff, 0xf4, 0x1, 0x8, 0x4, + 0x17, 0xca, 0x28, 0x94, 0x51, 0x3e, 0xa2, 0x11, + 0x44, 0x22, 0xb8, 0x75, 0xf, 0x8a, 0x11, 0x24, + 0x22, 0x48, 0x45, 0xf, 0x0, + + /* U+8F6E "轮" */ + 0x20, 0x20, 0x40, 0xc3, 0xf2, 0x42, 0xc, 0x45, + 0x30, 0x52, 0xc0, 0x24, 0x42, 0x7e, 0x88, 0x11, + 0x60, 0x23, 0x0, 0x74, 0x17, 0x88, 0x21, 0x10, + 0x42, 0x20, 0x84, 0x3e, 0x0, + + /* U+8F6F "软" */ + 0x20, 0x0, 0x41, 0x0, 0x82, 0x7, 0xe7, 0xf4, + 0x10, 0x4a, 0x44, 0xa4, 0x89, 0x48, 0x10, 0xfc, + 0x20, 0x20, 0x40, 0x41, 0x40, 0x82, 0x8f, 0xc8, + 0x82, 0x31, 0x4, 0xc1, 0xa, 0x0, + + /* U+8F70 "轰" */ + 0x0, 0x0, 0x20, 0xf, 0xff, 0xc4, 0x0, 0x21, + 0x1, 0xff, 0xe0, 0x10, 0x3f, 0xff, 0x1, 0x3, + 0xf7, 0xe0, 0x50, 0x91, 0x22, 0x38, 0x50, 0xe1, + 0x8c, 0x59, 0xc0, 0x0, + + /* U+8F71 "轱" */ + 0x10, 0x20, 0x20, 0x43, 0xf0, 0x81, 0xf, 0xf2, + 0x82, 0x9, 0x4, 0x12, 0x8, 0x3e, 0x10, 0x9, + 0xfc, 0x12, 0x8, 0xf4, 0x16, 0x48, 0x20, 0x90, + 0x41, 0x3f, 0x82, 0x41, 0x0, + + /* U+8F72 "轲" */ + 0x10, 0x0, 0x43, 0xff, 0xf0, 0x11, 0x0, 0x25, + 0x1e, 0x4a, 0x24, 0x94, 0x49, 0x3e, 0x92, 0x11, + 0x24, 0x22, 0x48, 0x57, 0x97, 0xc0, 0x21, 0x0, + 0x42, 0x0, 0x84, 0x1e, 0x0, + + /* U+8F73 "轳" */ + 0x20, 0x20, 0x80, 0x7f, 0xf0, 0x82, 0x1, 0x4, + 0x1f, 0xd4, 0x20, 0xa8, 0x41, 0x7c, 0x82, 0x21, + 0xfc, 0x42, 0x8, 0xa8, 0x7, 0x90, 0x2, 0x20, + 0x4, 0x40, 0x9, 0x0, 0x0, + + /* U+8F74 "轴" */ + 0x10, 0x20, 0x20, 0x43, 0xf0, 0x81, 0xf, 0xf5, + 0x12, 0x2a, 0x24, 0x54, 0x48, 0xbe, 0x91, 0x11, + 0xfe, 0x22, 0x44, 0x54, 0x8f, 0xc9, 0x11, 0x12, + 0x22, 0x3f, 0xc4, 0x40, 0x80, + + /* U+8F75 "轵" */ + 0x20, 0x0, 0x47, 0xfb, 0xf8, 0x12, 0x10, 0x24, + 0x20, 0x52, 0x40, 0xa4, 0x81, 0x49, 0x2, 0x7b, + 0xfc, 0x20, 0x0, 0x42, 0x47, 0xc8, 0x41, 0x10, + 0xc2, 0x40, 0x85, 0x0, 0x80, + + /* U+8F76 "轶" */ + 0x10, 0x20, 0x42, 0x43, 0xf4, 0x81, 0xf, 0xf2, + 0x12, 0xa, 0x44, 0x14, 0x8, 0x3f, 0xff, 0x10, + 0x20, 0x20, 0xa0, 0xf1, 0x46, 0x86, 0x41, 0x8, + 0x82, 0x60, 0xc4, 0x80, 0x80, + + /* U+8F77 "轷" */ + 0x10, 0x0, 0x43, 0xfb, 0xf0, 0x81, 0x1, 0x14, + 0x12, 0x4a, 0x24, 0x94, 0x2a, 0x3e, 0x10, 0x13, + 0xfe, 0x20, 0x40, 0x40, 0x87, 0xe1, 0x1, 0x2, + 0x2, 0x4, 0x4, 0x78, 0x0, + + /* U+8F78 "轸" */ + 0x20, 0x20, 0x40, 0xc3, 0xf2, 0x42, 0x8, 0x44, + 0x20, 0x52, 0x84, 0x64, 0x10, 0x7e, 0xcc, 0x10, + 0x20, 0x21, 0x8b, 0xf4, 0x30, 0x80, 0xc1, 0x2, + 0x2, 0x18, 0x4, 0xc0, 0x0, + + /* U+8F79 "轹" */ + 0x0, 0x0, 0x20, 0x1e, 0x21, 0xe0, 0xfd, 0x0, + 0x21, 0x20, 0x41, 0x20, 0x51, 0x20, 0x52, 0x20, + 0x7d, 0xfe, 0x10, 0x20, 0x10, 0x20, 0x3d, 0x24, + 0xd1, 0x22, 0x12, 0x22, 0x12, 0x20, 0x10, 0xe0, + + /* U+8F7A "轺" */ + 0x20, 0x0, 0x87, 0xff, 0xc4, 0x50, 0x11, 0x50, + 0x85, 0x42, 0x19, 0x13, 0xbf, 0x0, 0x11, 0xfc, + 0x44, 0x11, 0xd0, 0x7c, 0x41, 0x11, 0x4, 0x47, + 0xf1, 0x10, 0x40, + + /* U+8F7B "轻" */ + 0x20, 0x0, 0x43, 0xfb, 0xf0, 0x22, 0x0, 0xc4, + 0x3, 0x12, 0xe, 0x24, 0x73, 0x7f, 0x1, 0x11, + 0xfc, 0x20, 0x43, 0xf0, 0x80, 0x81, 0x1, 0x2, + 0x2, 0x7f, 0xc4, 0x0, 0x0, + + /* U+8F7C "轼" */ + 0x10, 0x10, 0x40, 0x2b, 0xf0, 0x41, 0xf, 0xf4, + 0x1, 0xa, 0x2, 0x14, 0x4, 0x3e, 0xe8, 0x10, + 0x90, 0x21, 0x23, 0xf2, 0x40, 0x84, 0x91, 0xe, + 0xa2, 0x61, 0x44, 0x1, 0x80, + + /* U+8F7D "载" */ + 0x10, 0x81, 0xfd, 0x30, 0x42, 0x20, 0x84, 0xf, + 0xff, 0xe0, 0x8, 0x8, 0x10, 0x7f, 0xa4, 0x48, + 0x49, 0xfe, 0xa0, 0x20, 0x80, 0x41, 0x1f, 0xf6, + 0x21, 0x32, 0x42, 0x43, 0x0, + + /* U+8F7E "轾" */ + 0x10, 0x0, 0x43, 0xff, 0xf1, 0x1, 0x2, 0x4, + 0x8, 0x8a, 0x20, 0xa4, 0x7e, 0xfe, 0x10, 0x10, + 0x20, 0x23, 0xff, 0xf0, 0x80, 0x81, 0x1, 0x2, + 0x2, 0x7f, 0xc4, 0x0, 0x0, + + /* U+8F7F "轿" */ + 0x20, 0x0, 0x43, 0xeb, 0xf0, 0x82, 0x1, 0x4, + 0x3f, 0xea, 0xa, 0x24, 0x32, 0x7e, 0x44, 0x13, + 0x6, 0x21, 0x10, 0x42, 0x27, 0xe4, 0x41, 0x8, + 0x82, 0x21, 0x4, 0x82, 0x0, + + /* U+8F81 "辁" */ + 0x20, 0x20, 0x40, 0xc3, 0xf1, 0x42, 0x4, 0x44, + 0x10, 0x4a, 0xc0, 0x64, 0x7f, 0x7c, 0x10, 0x10, + 0x20, 0x23, 0xf8, 0xf0, 0x86, 0x81, 0x1, 0x2, + 0x2, 0xff, 0xc4, 0x0, 0x0, + + /* U+8F82 "辂" */ + 0x20, 0x40, 0x41, 0x3, 0xf3, 0xf2, 0xc, 0x44, + 0x38, 0x92, 0x4a, 0x24, 0x1c, 0x7c, 0xc6, 0x12, + 0x0, 0x23, 0xf9, 0xf4, 0x16, 0x88, 0x21, 0x10, + 0x42, 0x3f, 0x84, 0x41, 0x0, + + /* U+8F83 "较" */ + 0x0, 0x0, 0x20, 0x20, 0x20, 0x10, 0xfd, 0xfe, + 0x40, 0x0, 0x50, 0x48, 0x50, 0x84, 0x91, 0x2, + 0xff, 0x8a, 0x10, 0x48, 0x10, 0x48, 0x1e, 0x30, + 0xf0, 0x20, 0x10, 0x58, 0x11, 0x86, 0x12, 0x0, + + /* U+8F84 "辄" */ + 0x27, 0xfe, 0x44, 0x83, 0xe9, 0x2, 0x12, 0x44, + 0x3c, 0x8a, 0x49, 0x14, 0x92, 0x29, 0x24, 0x3b, + 0xc8, 0x24, 0x90, 0x49, 0x2b, 0xd3, 0x59, 0x7c, + 0xa2, 0x9, 0x44, 0x13, 0x80, + + /* U+8F85 "辅" */ + 0x20, 0x24, 0xfd, 0xff, 0x20, 0x20, 0x40, 0x20, + 0x51, 0xfe, 0x51, 0x22, 0x91, 0x22, 0xfd, 0xfe, + 0x11, 0x22, 0x3d, 0xfe, 0xd1, 0x22, 0x11, 0x22, + 0x11, 0x22, 0x11, 0x2e, + + /* U+8F86 "辆" */ + 0x20, 0x0, 0x8f, 0xff, 0xe2, 0x42, 0x4, 0x88, + 0x3f, 0xf4, 0x92, 0x69, 0x24, 0xfe, 0x49, 0x25, + 0x6a, 0x4a, 0x54, 0xf4, 0x8f, 0x22, 0x12, 0x40, + 0x24, 0x80, 0x49, 0x7, 0x80, + + /* U+8F87 "辇" */ + 0x8, 0x21, 0xfd, 0xfc, 0x20, 0x87, 0xf7, 0xf1, + 0x83, 0x6, 0x89, 0x10, 0xa1, 0xc2, 0x0, 0x7f, + 0xfc, 0x21, 0x0, 0xff, 0xf0, 0x4, 0xf, 0xff, + 0xe0, 0x10, 0x0, 0x20, 0x0, + + /* U+8F88 "辈" */ + 0x4, 0x81, 0xf9, 0xf8, 0x12, 0x7, 0xe7, 0xe0, + 0x48, 0x1f, 0x9f, 0x81, 0x0, 0x7f, 0xfe, 0x10, + 0x0, 0x42, 0x0, 0xff, 0xe0, 0x8, 0xf, 0xff, + 0xe0, 0x20, 0x0, 0x40, 0x0, + + /* U+8F89 "辉" */ + 0x10, 0x0, 0x23, 0xfe, 0x54, 0xc, 0xca, 0x15, + 0xbf, 0xe2, 0x10, 0x3f, 0x24, 0x24, 0x88, 0x49, + 0xfe, 0x90, 0x21, 0x20, 0x42, 0x5f, 0xf4, 0xc1, + 0x13, 0x2, 0x20, 0x4, 0x0, + + /* U+8F8A "辊" */ + 0x20, 0x0, 0x47, 0xfb, 0xe8, 0x12, 0x1f, 0xe4, + 0x20, 0x52, 0x40, 0xa4, 0xff, 0x7c, 0x0, 0x12, + 0x20, 0x27, 0x58, 0x68, 0xc7, 0x91, 0x11, 0x22, + 0x22, 0x74, 0x44, 0x8f, 0x0, + + /* U+8F8B "辋" */ + 0x20, 0x0, 0x4f, 0xff, 0xf0, 0x4a, 0x25, 0x14, + 0x5f, 0xb2, 0x88, 0x65, 0x10, 0xba, 0xfd, 0x14, + 0x82, 0x29, 0x4, 0x72, 0xf, 0xa7, 0x91, 0x40, + 0x22, 0x80, 0x85, 0x7, 0x0, + + /* U+8F8D "辍" */ + 0x23, 0xde, 0x20, 0x42, 0xf8, 0xa2, 0x42, 0x94, + 0x41, 0xc, 0x93, 0x9e, 0x90, 0x20, 0xfb, 0xfe, + 0x10, 0xa2, 0x10, 0x92, 0x1a, 0x94, 0xf1, 0x8c, + 0x11, 0x88, 0x12, 0x36, 0x14, 0x40, + + /* U+8F8E "辎" */ + 0x20, 0x92, 0x42, 0x4b, 0xfd, 0x22, 0x9, 0x24, + 0xa, 0x2a, 0x12, 0x64, 0x0, 0x7e, 0xff, 0x11, + 0x22, 0x22, 0x44, 0x47, 0xff, 0xe9, 0x11, 0x12, + 0x22, 0x3f, 0xc4, 0x40, 0x80, + + /* U+8F8F "辏" */ + 0x20, 0x0, 0x40, 0x80, 0x8f, 0xf7, 0xc2, 0x4, + 0x4, 0x8, 0x7f, 0xa8, 0x10, 0x53, 0xff, 0xf8, + 0x90, 0x47, 0xf8, 0x91, 0x9, 0xdf, 0xee, 0x4, + 0x4, 0xe, 0x8, 0x62, 0x13, 0x2, + + /* U+8F90 "辐" */ + 0x10, 0x0, 0x47, 0xfc, 0x80, 0x7, 0xef, 0xe4, + 0x10, 0x48, 0x20, 0x94, 0x41, 0x48, 0x7c, 0xff, + 0xfe, 0x24, 0x44, 0x48, 0x89, 0xff, 0xfd, 0x22, + 0x22, 0x44, 0x44, 0xff, 0x89, 0x1, + + /* U+8F91 "辑" */ + 0x10, 0x0, 0x43, 0xf8, 0x84, 0x17, 0xe8, 0x22, + 0x1f, 0xc8, 0x0, 0x14, 0xff, 0xa8, 0x82, 0x39, + 0xfc, 0x22, 0x8, 0x44, 0x10, 0xef, 0xef, 0x10, + 0x42, 0xff, 0xc4, 0x1, 0x8, 0x2, + + /* U+8F93 "输" */ + 0x40, 0x40, 0x81, 0x43, 0xe4, 0x42, 0x10, 0x48, + 0x5f, 0x74, 0x0, 0x29, 0xe1, 0x52, 0x52, 0x74, + 0xa4, 0x4f, 0x48, 0x92, 0x97, 0xbd, 0x22, 0x48, + 0x44, 0x90, 0x89, 0x67, 0x0, + + /* U+8F94 "辔" */ + 0x21, 0x4, 0x8f, 0x91, 0x28, 0x4f, 0x95, 0xe2, + 0x48, 0x88, 0x7b, 0x1e, 0x27, 0x83, 0xe0, 0xf8, + 0xbe, 0x1, 0x0, 0xff, 0xf1, 0x0, 0x22, 0x0, + 0x47, 0xff, 0x88, 0x1, 0x0, + + /* U+8F95 "辕" */ + 0x20, 0x40, 0x23, 0xfc, 0xf8, 0x40, 0x40, 0x40, + 0x47, 0xfe, 0xe0, 0x0, 0xa3, 0xfc, 0xa2, 0x4, + 0x72, 0x4, 0x23, 0xfc, 0x20, 0xc2, 0xf9, 0x24, + 0x27, 0x18, 0x21, 0x2e, 0x21, 0xc0, + + /* U+8F96 "辖" */ + 0x20, 0x40, 0x8f, 0xff, 0xf0, 0xa, 0x2, 0x4, + 0x7f, 0xd4, 0x8, 0x28, 0xff, 0x50, 0x20, 0x7f, + 0xfe, 0x40, 0x80, 0xef, 0xf7, 0x20, 0x22, 0x40, + 0x44, 0x7f, 0x89, 0x1, 0x0, + + /* U+8F97 "辗" */ + 0x20, 0x0, 0x47, 0xfb, 0xf0, 0x12, 0x1f, 0xe4, + 0x40, 0x14, 0x92, 0x29, 0x7f, 0x52, 0x48, 0xfc, + 0x90, 0x47, 0xfc, 0x95, 0x13, 0xe9, 0x4a, 0x51, + 0x4, 0xa5, 0x8a, 0x30, 0x0, + + /* U+8F98 "辘" */ + 0x40, 0x20, 0x87, 0xff, 0xf2, 0x42, 0x24, 0x88, + 0x3f, 0xf4, 0x92, 0x69, 0x24, 0xf9, 0xff, 0x24, + 0x0, 0x4a, 0x20, 0xf7, 0x7f, 0x28, 0x82, 0x51, + 0x24, 0xba, 0x4a, 0x47, 0x80, + + /* U+8F99 "辙" */ + 0x42, 0x10, 0x80, 0x23, 0xff, 0x44, 0x28, 0xfa, + 0xfa, 0x54, 0x4, 0xa8, 0x19, 0x7b, 0xca, 0x28, + 0x98, 0x4f, 0x30, 0xe2, 0x27, 0x3c, 0xc2, 0x89, + 0x45, 0x14, 0x8a, 0xf0, 0x0, + + /* U+8F9A "辚" */ + 0x22, 0x20, 0x41, 0x24, 0xf8, 0x20, 0x47, 0xfe, + 0x40, 0xf0, 0xa1, 0xa8, 0xa3, 0x24, 0xa4, 0x23, + 0x71, 0x9e, 0x22, 0x94, 0x74, 0xa4, 0xa1, 0x3e, + 0x21, 0x4, 0x22, 0x4, 0x24, 0x4, + + /* U+8F9B "辛" */ + 0x1, 0x0, 0x2, 0x1, 0xff, 0xf0, 0x0, 0x0, + 0x82, 0x1, 0xc, 0x1, 0x10, 0x7f, 0xff, 0x1, + 0x0, 0x2, 0x1, 0xff, 0xf0, 0x8, 0x0, 0x10, + 0x0, 0x20, 0x0, 0x40, 0x0, + + /* U+8F9C "辜" */ + 0x1, 0x1, 0xff, 0xfc, 0x4, 0x1, 0xff, 0xc2, + 0x0, 0x87, 0xff, 0x0, 0x40, 0x3f, 0xfe, 0x8, + 0x20, 0x8, 0x83, 0xff, 0xf8, 0x8, 0x7, 0xff, + 0xc0, 0x20, 0x0, 0x40, 0x0, + + /* U+8F9E "辞" */ + 0x0, 0x21, 0xf8, 0x40, 0x87, 0xf1, 0x0, 0xf, + 0xc8, 0x84, 0x2, 0x8, 0xff, 0x90, 0x10, 0xf8, + 0x21, 0x10, 0x42, 0x27, 0xf4, 0x41, 0x8, 0x82, + 0x1f, 0x4, 0x22, 0x8, 0x0, + + /* U+8F9F "辟" */ + 0x0, 0x10, 0x3e, 0x10, 0x22, 0xfe, 0x22, 0x4, + 0x22, 0x44, 0x3e, 0x48, 0x20, 0x8, 0x20, 0xf6, + 0x3e, 0x10, 0x72, 0x10, 0x52, 0xfe, 0x52, 0x10, + 0x1e, 0x10, 0x12, 0x10, 0x12, 0x10, + + /* U+8FA3 "辣" */ + 0x20, 0x20, 0x20, 0x43, 0xff, 0xf8, 0x1, 0x4, + 0x82, 0x9, 0x3f, 0x84, 0x89, 0x7f, 0x12, 0x11, + 0xfc, 0x40, 0xe3, 0xf2, 0xc1, 0x5, 0x42, 0x32, + 0x48, 0xc4, 0x60, 0x8, 0x0, + + /* U+8FA8 "辨" */ + 0x21, 0x10, 0x42, 0x23, 0xe5, 0xf0, 0x8, 0x5, + 0x54, 0x82, 0xa1, 0x3f, 0x5f, 0x92, 0x88, 0x25, + 0x10, 0x42, 0x23, 0xe5, 0xf1, 0x10, 0x84, 0x21, + 0x8, 0x82, 0x22, 0x4, 0x0, + + /* U+8FA9 "辩" */ + 0x22, 0x0, 0x22, 0x13, 0xf5, 0xf8, 0x0, 0x4, + 0x82, 0x42, 0x61, 0x3f, 0x5f, 0x90, 0x84, 0x21, + 0x8, 0x42, 0x13, 0xf5, 0xf9, 0x8, 0x42, 0x14, + 0x88, 0x31, 0x30, 0x2, 0x0, + + /* U+8FAB "辫" */ + 0x20, 0x90, 0x22, 0x13, 0xe9, 0xf8, 0x10, 0x5, + 0x4a, 0x42, 0x61, 0x3e, 0x5f, 0x91, 0x8, 0x22, + 0x10, 0x4f, 0x23, 0xe1, 0xf9, 0x0, 0x82, 0x1, + 0x8, 0xf2, 0x20, 0x4, 0x0, + + /* U+8FB0 "辰" */ + 0x3f, 0xfe, 0x20, 0x0, 0x20, 0x0, 0x2f, 0xfc, + 0x20, 0x0, 0x20, 0x0, 0x3f, 0xff, 0x24, 0x80, + 0x24, 0x86, 0x24, 0x58, 0x24, 0x20, 0x44, 0x10, + 0x47, 0x8e, 0x84, 0x0, + + /* U+8FB1 "辱" */ + 0x7f, 0xfd, 0x0, 0x5, 0xff, 0x90, 0x0, 0x7f, + 0xfd, 0x24, 0x24, 0x8f, 0x22, 0x9c, 0x4, 0x2f, + 0xff, 0xf1, 0x2, 0x2, 0x8, 0x0, 0x20, 0x7, + 0x0, + + /* U+8FB6 "辶" */ + 0x40, 0x0, 0x40, 0x0, 0x40, 0x0, 0x0, 0x0, + 0x0, 0x1c, 0x0, 0x8, 0x0, 0x10, 0x0, 0x20, + 0x0, 0x40, 0x0, 0x80, 0x1, 0x0, 0x5, 0x80, + 0x10, 0xff, 0xc0, + + /* U+8FB9 "边" */ + 0x40, 0x80, 0x41, 0x0, 0x82, 0x0, 0x7f, 0xe0, + 0x8, 0x5c, 0x10, 0x88, 0x21, 0x10, 0x82, 0x21, + 0x4, 0x46, 0x8, 0x98, 0x31, 0x63, 0xc5, 0x0, + 0x11, 0xff, 0xc0, + + /* U+8FBD "辽" */ + 0x40, 0x0, 0xcf, 0xfc, 0x80, 0x10, 0x0, 0xc0, + 0x2, 0x1c, 0x4, 0x8, 0x8, 0x10, 0x10, 0x20, + 0x20, 0x40, 0x40, 0x80, 0x81, 0xe, 0x5, 0x0, + 0x1b, 0x0, 0x21, 0xff, 0x80, + + /* U+8FBE "达" */ + 0x40, 0x40, 0x40, 0x80, 0x41, 0x0, 0x7f, 0xf0, + 0x4, 0x1c, 0x8, 0x8, 0x20, 0x10, 0x70, 0x20, + 0x90, 0x42, 0x30, 0x88, 0x31, 0x60, 0x32, 0x0, + 0xb, 0x0, 0x23, 0xff, 0x80, + + /* U+8FC1 "迁" */ + 0x40, 0x0, 0x4f, 0xf8, 0x81, 0x0, 0x2, 0x0, + 0x4, 0x1c, 0x8, 0xb, 0xff, 0x90, 0x20, 0x20, + 0x40, 0x40, 0x80, 0x81, 0x1, 0x2, 0x5, 0x4, + 0x11, 0xff, 0xc0, + + /* U+8FC2 "迂" */ + 0x40, 0x0, 0xcf, 0xf8, 0x81, 0x0, 0x2, 0x0, + 0x4, 0x1d, 0xff, 0xc8, 0x10, 0x10, 0x20, 0x20, + 0x40, 0x40, 0x80, 0x81, 0x1, 0x1e, 0x5, 0x0, + 0x11, 0xff, 0xc0, + + /* U+8FC4 "迄" */ + 0x42, 0x0, 0x84, 0x0, 0x9f, 0xf0, 0x20, 0x0, + 0x80, 0x1c, 0x7f, 0x8, 0x18, 0x10, 0x40, 0x23, + 0x4, 0x4c, 0x8, 0x90, 0x11, 0x3f, 0xc7, 0x0, + 0x1b, 0x0, 0x23, 0xff, 0x0, + + /* U+8FC5 "迅" */ + 0x40, 0x0, 0x47, 0xf8, 0x21, 0x8, 0x1, 0x8, + 0x1, 0x8, 0xe1, 0x8, 0x2f, 0xc8, 0x21, 0x8, + 0x21, 0x8, 0x21, 0x9, 0x21, 0x9, 0x21, 0x6, + 0x71, 0x2, 0xdf, 0xff, + + /* U+8FC7 "过" */ + 0x40, 0x10, 0x40, 0x20, 0xc0, 0x40, 0x3f, 0xf0, + 0x1, 0x1c, 0x42, 0x8, 0x44, 0x10, 0x48, 0x20, + 0x10, 0x40, 0x20, 0x80, 0x41, 0x7, 0x5, 0x80, + 0x10, 0xff, 0xc0, + + /* U+8FC8 "迈" */ + 0x40, 0x0, 0xcf, 0xfc, 0x82, 0x0, 0x4, 0x0, + 0x8, 0x1c, 0x2f, 0x88, 0x41, 0x10, 0x82, 0x21, + 0x4, 0x44, 0x8, 0x88, 0x11, 0x61, 0xc2, 0x0, + 0xb, 0x0, 0x23, 0xff, 0x80, + + /* U+8FCE "迎" */ + 0x43, 0xc0, 0x24, 0x3e, 0x24, 0x22, 0x4, 0x22, + 0x4, 0x22, 0xe4, 0x22, 0x24, 0x22, 0x24, 0x22, + 0x24, 0x22, 0x25, 0x22, 0x26, 0x2e, 0x20, 0x20, + 0x60, 0x20, 0x58, 0x0, 0x8f, 0xff, + + /* U+8FD0 "运" */ + 0x40, 0x0, 0x47, 0xf8, 0xc0, 0x0, 0x0, 0x0, + 0x0, 0x1c, 0xff, 0x88, 0x20, 0x10, 0x88, 0x21, + 0x8, 0x44, 0x10, 0x9f, 0xf1, 0x0, 0x25, 0x80, + 0x11, 0xff, 0xc0, + + /* U+8FD1 "近" */ + 0x40, 0x4, 0x47, 0xf0, 0xc8, 0x0, 0x10, 0x0, + 0x20, 0x1c, 0x7f, 0xc8, 0x84, 0x11, 0x8, 0x22, + 0x10, 0x4c, 0x20, 0x90, 0x41, 0x60, 0x85, 0x80, + 0x11, 0xff, 0xc0, + + /* U+8FD3 "迓" */ + 0x40, 0x0, 0x27, 0xfe, 0x30, 0x10, 0x2, 0x10, + 0x4, 0x10, 0xe7, 0xff, 0x20, 0x30, 0x20, 0x50, + 0x20, 0x90, 0x23, 0x10, 0x24, 0x10, 0x20, 0xf0, + 0x50, 0x0, 0xd8, 0x0, 0x87, 0xfe, + + /* U+8FD4 "返" */ + 0x0, 0x0, 0x23, 0xfe, 0x12, 0x0, 0x2, 0x0, + 0x3, 0xfe, 0xe2, 0x4, 0x22, 0x84, 0x22, 0x48, + 0x22, 0x28, 0x24, 0x30, 0x24, 0x68, 0x28, 0xc6, + 0x23, 0x2, 0x50, 0x0, 0x8f, 0xff, + + /* U+8FD5 "迕" */ + 0x42, 0x0, 0x44, 0x0, 0x8f, 0xf0, 0x22, 0x0, + 0x44, 0x1c, 0x8, 0x8, 0x10, 0x17, 0xff, 0x20, + 0x40, 0x40, 0x80, 0x81, 0x1, 0x2, 0x5, 0x4, + 0xb, 0x0, 0x21, 0xff, 0x80, + + /* U+8FD8 "还" */ + 0x40, 0x0, 0x4f, 0xfc, 0x40, 0x80, 0x2, 0x0, + 0x8, 0x1c, 0x1a, 0x8, 0x52, 0x11, 0x22, 0x24, + 0x42, 0x50, 0x80, 0x81, 0x1, 0x2, 0x5, 0x0, + 0x11, 0xff, 0xc0, + + /* U+8FD9 "这" */ + 0x0, 0x40, 0x80, 0x80, 0x80, 0x81, 0xbf, 0xf0, + 0x0, 0x80, 0x41, 0x38, 0xc2, 0x10, 0xc8, 0x20, + 0x60, 0x40, 0xc0, 0x83, 0x41, 0xc, 0x42, 0x60, + 0x4c, 0x80, 0x16, 0x0, 0x47, 0xff, + + /* U+8FDB "进" */ + 0x1, 0x10, 0x82, 0x20, 0x84, 0x40, 0x3f, 0xe0, + 0x11, 0x0, 0x22, 0x18, 0x44, 0x17, 0xff, 0x22, + 0x10, 0x44, 0x20, 0x88, 0x41, 0x20, 0x85, 0x0, + 0x11, 0xff, 0xc0, + + /* U+8FDC "远" */ + 0x40, 0x0, 0x4f, 0xf8, 0x80, 0x0, 0x0, 0x0, + 0x0, 0x1d, 0xff, 0xc8, 0x48, 0x10, 0x90, 0x22, + 0x22, 0x44, 0x44, 0x90, 0x89, 0x60, 0xe2, 0x0, + 0xb, 0x0, 0x23, 0xff, 0x80, + + /* U+8FDD "违" */ + 0x0, 0x40, 0x60, 0x40, 0x27, 0xfe, 0x10, 0x40, + 0x0, 0x40, 0x3, 0xfc, 0x60, 0x40, 0x20, 0x40, + 0x27, 0xfe, 0x20, 0x42, 0x20, 0x42, 0x20, 0x4c, + 0x70, 0x40, 0x58, 0x0, 0x87, 0xfe, + + /* U+8FDE "连" */ + 0x40, 0x80, 0x41, 0x0, 0x9f, 0xf8, 0x8, 0x0, + 0x12, 0x0, 0x44, 0x38, 0x88, 0x13, 0xff, 0x20, + 0x20, 0x40, 0x40, 0xbf, 0xf9, 0x1, 0x2, 0x2, + 0xb, 0x0, 0x23, 0xff, 0x80, + + /* U+8FDF "迟" */ + 0x40, 0x0, 0xc7, 0xf8, 0x88, 0x10, 0x10, 0x20, + 0x20, 0x40, 0x40, 0xb8, 0xff, 0x11, 0x0, 0x22, + 0x40, 0x4c, 0x60, 0x90, 0x31, 0x40, 0x15, 0x80, + 0x10, 0xff, 0xc0, + + /* U+8FE2 "迢" */ + 0x47, 0xfc, 0x41, 0x8, 0x42, 0x10, 0xc, 0x20, + 0x10, 0x40, 0xc7, 0x18, 0x0, 0x11, 0xfe, 0x22, + 0x4, 0x44, 0x8, 0x88, 0x11, 0x1f, 0xe2, 0x0, + 0xb, 0x0, 0x23, 0xff, 0x80, + + /* U+8FE4 "迤" */ + 0x1, 0x0, 0x41, 0x0, 0x23, 0xfe, 0x16, 0x0, + 0xc, 0x40, 0x2, 0x5c, 0xe2, 0xe4, 0x2f, 0x44, + 0x22, 0x44, 0x22, 0x5c, 0x22, 0x42, 0x22, 0x2, + 0x21, 0xfc, 0x70, 0x0, 0x58, 0x0, 0x87, 0xfe, + + /* U+8FE5 "迥" */ + 0x40, 0x0, 0x4f, 0xf8, 0x90, 0x10, 0x20, 0x20, + 0x4f, 0x5c, 0x92, 0x89, 0x25, 0x12, 0x4a, 0x24, + 0x94, 0x49, 0xe8, 0x90, 0x11, 0x20, 0x22, 0x43, + 0xca, 0x0, 0x23, 0xff, 0x80, + + /* U+8FE6 "迦" */ + 0x4, 0x0, 0x44, 0x0, 0x24, 0x3e, 0xf, 0xa2, + 0x4, 0xa2, 0x4, 0xa2, 0xe4, 0xa2, 0x24, 0xa2, + 0x24, 0xa2, 0x24, 0xa2, 0x28, 0xa2, 0x28, 0xa2, + 0x2b, 0xbe, 0x60, 0x0, 0x58, 0x0, 0x87, 0xff, + + /* U+8FE8 "迨" */ + 0x0, 0x40, 0x81, 0x0, 0x82, 0x40, 0x88, 0x40, + 0x20, 0x40, 0xff, 0xb8, 0x0, 0x90, 0x0, 0x23, + 0xfc, 0x44, 0x8, 0x88, 0x11, 0x10, 0x22, 0x3f, + 0xc4, 0x0, 0x16, 0x0, 0x47, 0xff, + + /* U+8FE9 "迩" */ + 0x1, 0x0, 0x84, 0x0, 0x8f, 0xf0, 0x22, 0x20, + 0x4, 0x1c, 0x8, 0x8, 0x92, 0x12, 0x22, 0x24, + 0x44, 0x50, 0x84, 0x81, 0x1, 0x1e, 0x5, 0x0, + 0x13, 0x0, 0x23, 0xff, 0x80, + + /* U+8FEA "迪" */ + 0x40, 0x40, 0x40, 0x80, 0xdf, 0xf0, 0x22, 0x20, + 0x44, 0x40, 0x88, 0xb9, 0x11, 0x13, 0xfe, 0x24, + 0x44, 0x48, 0x88, 0x91, 0x11, 0x3f, 0xe2, 0x0, + 0xa, 0x0, 0x23, 0xff, 0x80, + + /* U+8FEB "迫" */ + 0x40, 0x40, 0x81, 0x0, 0x9f, 0xf0, 0x20, 0x20, + 0x40, 0x5c, 0x80, 0x89, 0xff, 0x12, 0x2, 0x24, + 0x4, 0x48, 0x8, 0x90, 0x11, 0x3f, 0xe7, 0x0, + 0xb, 0x0, 0x21, 0xff, 0x80, + + /* U+8FED "迭" */ + 0x0, 0x20, 0x84, 0x40, 0x88, 0x80, 0xbf, 0xe0, + 0x42, 0x1c, 0x8, 0xb, 0xff, 0x90, 0x20, 0x20, + 0xe0, 0x41, 0x20, 0x84, 0x21, 0x30, 0x22, 0x40, + 0xb, 0x0, 0x23, 0xff, 0x80, + + /* U+8FEE "迮" */ + 0x41, 0x0, 0x44, 0x0, 0x8f, 0xf8, 0x24, 0x0, + 0x48, 0x1d, 0x1f, 0xc8, 0x20, 0x10, 0x40, 0x20, + 0x80, 0x41, 0xfc, 0x82, 0x1, 0x4, 0x2, 0x8, + 0xb, 0x0, 0x23, 0xff, 0x80, + + /* U+8FF0 "述" */ + 0x40, 0x40, 0x40, 0x48, 0x20, 0x44, 0x0, 0x40, + 0x7, 0xfe, 0x0, 0x40, 0x60, 0xe0, 0x21, 0x50, + 0x23, 0x4c, 0x22, 0x46, 0x24, 0x42, 0x20, 0x40, + 0x20, 0x40, 0x58, 0x0, 0x8f, 0xfe, + + /* U+8FF3 "迳" */ + 0x63, 0xf8, 0x20, 0x8, 0x10, 0x30, 0x0, 0x70, + 0x3, 0x8c, 0x64, 0x2, 0x23, 0xfc, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x27, 0xfe, 0x20, 0x0, + 0x58, 0x0, 0x8f, 0xfe, + + /* U+8FF7 "迷" */ + 0x40, 0x40, 0x88, 0x88, 0x91, 0x20, 0x12, 0x40, + 0x4, 0x1, 0xff, 0xd8, 0x10, 0x10, 0x70, 0x21, + 0x50, 0x44, 0x90, 0x91, 0x19, 0x42, 0x2, 0x4, + 0xa, 0x0, 0x23, 0xff, 0x80, + + /* U+8FF8 "迸" */ + 0x2, 0x8, 0x82, 0x20, 0x84, 0x40, 0x3f, 0xe0, + 0x11, 0x0, 0x22, 0x18, 0x44, 0x17, 0xff, 0x22, + 0x10, 0x44, 0x20, 0x88, 0x41, 0x20, 0x83, 0xc1, + 0x1b, 0xff, 0xc0, + + /* U+8FF9 "迹" */ + 0x40, 0x40, 0x40, 0x40, 0xdf, 0xf8, 0x4, 0x80, + 0x9, 0x1c, 0x92, 0x89, 0x25, 0x12, 0x49, 0x29, + 0x12, 0x42, 0x20, 0x84, 0x41, 0x13, 0x87, 0x0, + 0xb, 0x0, 0x21, 0xff, 0x80, + + /* U+8FFD "追" */ + 0x0, 0x80, 0x40, 0x80, 0x27, 0xfc, 0x4, 0x4, + 0x4, 0x4, 0xe7, 0xf8, 0x24, 0x0, 0x24, 0x0, + 0x27, 0xfc, 0x24, 0x4, 0x24, 0x4, 0x27, 0xfc, + 0x20, 0x0, 0x50, 0x0, 0x8f, 0xff, + + /* U+9000 "退" */ + 0x43, 0xf8, 0x44, 0x10, 0x48, 0x20, 0x1f, 0xc0, + 0x20, 0x9c, 0x41, 0x8, 0xfe, 0x11, 0x0, 0x22, + 0xc8, 0x44, 0x60, 0x88, 0x41, 0x1e, 0x62, 0x20, + 0xb, 0x0, 0x23, 0xff, 0x80, + + /* U+9001 "送" */ + 0x42, 0x8, 0xc2, 0x20, 0x80, 0x40, 0x3f, 0xf0, + 0x4, 0x0, 0x8, 0x18, 0x10, 0x17, 0xff, 0x20, + 0x40, 0x41, 0x40, 0x82, 0x41, 0x18, 0x42, 0xc0, + 0x4a, 0x0, 0x23, 0xff, 0x80, + + /* U+9002 "适" */ + 0x40, 0x0, 0x8f, 0xf8, 0x81, 0x0, 0x2, 0x0, + 0x7f, 0xe0, 0x8, 0x38, 0x10, 0x11, 0xfe, 0x22, + 0x4, 0x44, 0x8, 0x88, 0x11, 0x1f, 0xe6, 0x0, + 0xb, 0x0, 0x23, 0xff, 0x80, + + /* U+9003 "逃" */ + 0x1, 0x20, 0x82, 0x40, 0x84, 0x91, 0xa9, 0x20, + 0x52, 0x80, 0x25, 0x38, 0x48, 0x11, 0x9c, 0x2d, + 0x24, 0x46, 0x40, 0x88, 0x89, 0x31, 0x22, 0x43, + 0xca, 0x0, 0x36, 0x0, 0x43, 0xff, + + /* U+9004 "逄" */ + 0x41, 0x0, 0x43, 0xf0, 0xcc, 0x20, 0x2c, 0x80, + 0x6, 0x1c, 0x73, 0x89, 0x10, 0x93, 0xfe, 0x20, + 0x40, 0x48, 0x80, 0x9f, 0xf1, 0x2, 0x6, 0x4, + 0xb, 0x0, 0x21, 0xff, 0x80, + + /* U+9005 "逅" */ + 0x40, 0x0, 0xcf, 0xf8, 0x90, 0x0, 0x20, 0x0, + 0x7f, 0xe0, 0x80, 0x39, 0x0, 0x12, 0xfe, 0x25, + 0x4, 0x52, 0x8, 0xa4, 0x11, 0xcf, 0xe2, 0x0, + 0xa, 0x0, 0x23, 0xff, 0x80, + + /* U+9006 "逆" */ + 0x0, 0x0, 0x41, 0x8, 0x20, 0x90, 0x7, 0xfe, + 0x0, 0x40, 0x2, 0x44, 0xe2, 0x44, 0x22, 0x44, + 0x22, 0x44, 0x23, 0xfc, 0x20, 0x40, 0x20, 0x80, + 0x20, 0x80, 0x23, 0x0, 0x58, 0x0, 0x8f, 0xfe, + + /* U+9009 "选" */ + 0x42, 0x40, 0x44, 0x80, 0x1f, 0xf0, 0x22, 0x0, + 0xc4, 0x1c, 0x8, 0xb, 0xff, 0x90, 0x90, 0x21, + 0x20, 0x46, 0x44, 0x88, 0x89, 0x60, 0xe5, 0x0, + 0x13, 0x0, 0x21, 0xff, 0x80, + + /* U+900A "逊" */ + 0x0, 0x10, 0x9f, 0x20, 0x86, 0x40, 0x10, 0xa0, + 0x25, 0x5c, 0x5a, 0x89, 0xd5, 0x15, 0x49, 0x22, + 0x92, 0x44, 0x20, 0x88, 0x41, 0x10, 0x82, 0xc7, + 0xa, 0x0, 0x23, 0xff, 0x80, + + /* U+900B "逋" */ + 0x0, 0x48, 0x80, 0x88, 0xbf, 0xf9, 0x2, 0x0, + 0x7f, 0xdc, 0x88, 0x89, 0x11, 0x13, 0xfe, 0x24, + 0x44, 0x4f, 0xf8, 0x91, 0x11, 0x22, 0x27, 0x45, + 0xcb, 0x0, 0x21, 0xff, 0x80, + + /* U+900D "逍" */ + 0x0, 0x40, 0x88, 0x88, 0x89, 0x20, 0x2, 0x0, + 0x7f, 0xc0, 0x80, 0xb9, 0x1, 0x13, 0xfe, 0x24, + 0x4, 0x48, 0x8, 0x9f, 0xf1, 0x20, 0x22, 0x40, + 0x44, 0x7, 0x96, 0x0, 0x43, 0xff, + + /* U+900F "透" */ + 0x0, 0x0, 0x63, 0xfc, 0x20, 0x40, 0x7, 0xfe, + 0x0, 0x60, 0x1, 0xd0, 0xe6, 0x4c, 0x23, 0xf8, + 0x21, 0x10, 0x21, 0x1c, 0x21, 0x4, 0x22, 0x4, + 0x24, 0x3c, 0x58, 0x0, 0x8f, 0xfe, + + /* U+9010 "逐" */ + 0x0, 0x0, 0x4f, 0xfc, 0x81, 0x0, 0xc, 0x20, + 0xe4, 0x9c, 0x16, 0x8, 0xc8, 0x16, 0x3c, 0x20, + 0xec, 0x43, 0x44, 0x98, 0x81, 0x41, 0x2, 0x1c, + 0xb, 0x0, 0x23, 0xff, 0x80, + + /* U+9011 "逑" */ + 0x0, 0x50, 0x80, 0x90, 0xbf, 0xf0, 0x2, 0x0, + 0x44, 0x5c, 0x49, 0x8, 0x94, 0x10, 0x70, 0x21, + 0x50, 0x4c, 0x90, 0xa1, 0x11, 0x2, 0x2, 0x18, + 0xb, 0x0, 0x23, 0xff, 0x0, + + /* U+9012 "递" */ + 0x41, 0x10, 0x42, 0x40, 0x5f, 0xf0, 0x2, 0x20, + 0x4, 0x40, 0xff, 0x99, 0x10, 0x13, 0xfe, 0x20, + 0xc4, 0x42, 0x88, 0x89, 0x11, 0x22, 0xe2, 0x4, + 0xb, 0x0, 0x21, 0xff, 0x80, + + /* U+9014 "途" */ + 0x0, 0x40, 0x80, 0x80, 0x82, 0x80, 0x88, 0x80, + 0x60, 0xc1, 0x3e, 0x38, 0x10, 0x10, 0x20, 0x2f, + 0xfc, 0x40, 0x80, 0x89, 0x21, 0x22, 0x42, 0x84, + 0x44, 0x38, 0x16, 0x0, 0x47, 0xff, + + /* U+9016 "逖" */ + 0x44, 0x90, 0x25, 0x10, 0x6, 0x12, 0xa, 0x52, + 0xe3, 0x54, 0x23, 0x10, 0x25, 0x28, 0x29, 0x28, + 0x21, 0x24, 0x21, 0x42, 0x26, 0x82, 0x70, 0x0, + 0x58, 0x0, 0x87, 0xfe, + + /* U+9017 "逗" */ + 0x4f, 0xfe, 0x60, 0x0, 0x1f, 0xf0, 0x20, 0x2e, + 0x40, 0x44, 0x80, 0x89, 0xff, 0x11, 0x0, 0x21, + 0x10, 0x42, 0x20, 0xbf, 0xfa, 0x80, 0x8, 0xff, + 0xe0, + + /* U+901A "通" */ + 0x67, 0xfc, 0x62, 0x10, 0x43, 0xc0, 0x3f, 0xee, + 0x44, 0x44, 0xff, 0x89, 0x11, 0x12, 0x22, 0x27, + 0xfc, 0x48, 0x88, 0x91, 0x13, 0xa0, 0xe8, 0xff, + 0xe0, + + /* U+901B "逛" */ + 0x48, 0x80, 0x45, 0x7e, 0x26, 0x10, 0x6, 0x10, + 0xa, 0x10, 0x1, 0x10, 0xe3, 0x7e, 0x25, 0x10, + 0x29, 0x10, 0x21, 0x10, 0x21, 0x10, 0x2e, 0x7e, + 0x70, 0x0, 0xd8, 0x0, 0x87, 0xfe, + + /* U+901D "逝" */ + 0x44, 0x0, 0x48, 0xf8, 0x91, 0x0, 0x7a, 0x0, + 0x44, 0x1c, 0x8f, 0x89, 0xd2, 0x16, 0x24, 0x24, + 0x48, 0x49, 0x10, 0x92, 0x21, 0x68, 0x47, 0x0, + 0x9b, 0x0, 0x21, 0xff, 0x80, + + /* U+901E "逞" */ + 0x43, 0xfc, 0x22, 0x4, 0x32, 0x4, 0x2, 0x4, + 0x3, 0xfc, 0x0, 0x0, 0x67, 0xfe, 0x20, 0x40, + 0x23, 0xfc, 0x20, 0x40, 0x20, 0x40, 0x27, 0xfe, + 0x50, 0x0, 0x8f, 0xfe, + + /* U+901F "速" */ + 0x0, 0x0, 0x80, 0x80, 0x9f, 0xf8, 0x82, 0x0, + 0x4, 0x0, 0x7f, 0xb9, 0x11, 0x12, 0x22, 0x23, + 0xfc, 0x40, 0x80, 0x83, 0xc1, 0x1a, 0x62, 0x44, + 0x2a, 0x0, 0x23, 0xff, 0x80, + + /* U+9020 "造" */ + 0x42, 0x40, 0x84, 0x80, 0x9f, 0xf0, 0x22, 0x0, + 0x4, 0x1d, 0xff, 0xc8, 0x0, 0x10, 0x0, 0x27, + 0xfc, 0x48, 0x8, 0x90, 0x11, 0x3f, 0xe7, 0x0, + 0xb, 0x0, 0x21, 0xff, 0x80, + + /* U+9021 "逡" */ + 0x0, 0xc0, 0x82, 0x20, 0x88, 0x20, 0x3f, 0xf0, + 0x10, 0x20, 0x43, 0x9b, 0x20, 0x90, 0xfc, 0x22, + 0x8, 0x5a, 0x20, 0x83, 0x81, 0xe, 0xc2, 0x60, + 0x4b, 0x0, 0x23, 0xff, 0x80, + + /* U+9022 "逢" */ + 0x42, 0x0, 0x87, 0xf0, 0x98, 0x40, 0x49, 0x0, + 0xc, 0x1, 0xe7, 0xb8, 0x20, 0x17, 0xfe, 0x20, + 0x80, 0x4f, 0xf0, 0x82, 0x1, 0x7f, 0xe5, 0x8, + 0xb, 0x0, 0x21, 0xff, 0x80, + + /* U+9026 "逦" */ + 0x0, 0x0, 0x9f, 0xfc, 0x80, 0x0, 0x7d, 0xf0, + 0x8a, 0x3d, 0x14, 0x4a, 0xaa, 0x95, 0x55, 0x28, + 0xa2, 0x51, 0x44, 0xa2, 0x89, 0x59, 0x67, 0x0, + 0x1b, 0x0, 0x23, 0xff, 0x80, + + /* U+902D "逭" */ + 0x0, 0x40, 0x40, 0x40, 0x4f, 0xfe, 0x28, 0x2, + 0x0, 0x0, 0x7, 0xfc, 0x4, 0x4, 0x67, 0xfc, + 0x24, 0x0, 0x27, 0xfc, 0x24, 0x4, 0x24, 0x4, + 0x27, 0xfc, 0x70, 0x0, 0x58, 0x0, 0x8f, 0xfe, + + /* U+902E "逮" */ + 0x40, 0x40, 0x47, 0xf0, 0x81, 0x20, 0x2, 0x40, + 0xff, 0xe0, 0x9, 0x19, 0xfe, 0x10, 0x20, 0x26, + 0x4c, 0x41, 0xe0, 0x8d, 0x61, 0x42, 0x22, 0x1c, + 0xb, 0x0, 0x23, 0xff, 0x80, + + /* U+902F "逯" */ + 0x47, 0xf8, 0x40, 0x10, 0xf, 0xe0, 0x0, 0x4e, + 0xff, 0x64, 0x88, 0x88, 0x92, 0x10, 0x38, 0x21, + 0xd0, 0x5c, 0x98, 0xa1, 0x9, 0xe, 0x5, 0x80, + 0x11, 0xff, 0xc0, + + /* U+9035 "逵" */ + 0x0, 0x40, 0x40, 0x40, 0x23, 0xfc, 0x10, 0x40, + 0x7, 0xfe, 0xe1, 0x8, 0x22, 0x4, 0x24, 0x42, + 0x23, 0xfc, 0x20, 0x40, 0x20, 0x40, 0x27, 0xfe, + 0x60, 0x0, 0x50, 0x0, 0x8f, 0xfe, + + /* U+9036 "逶" */ + 0x0, 0x0, 0x8f, 0xf8, 0x81, 0x0, 0x7f, 0xf0, + 0xe, 0x1c, 0x6b, 0x8b, 0x11, 0x90, 0x40, 0x2f, + 0xfe, 0x44, 0x20, 0x8c, 0x41, 0x7, 0x2, 0x39, + 0xcb, 0x80, 0x23, 0xff, 0x80, + + /* U+9038 "逸" */ + 0x0, 0x0, 0x82, 0x0, 0x8f, 0xc1, 0x10, 0x80, + 0x42, 0x1, 0xff, 0x81, 0x11, 0x32, 0x22, 0x27, + 0xfc, 0x41, 0x0, 0x82, 0xa1, 0x9, 0x52, 0x22, + 0x25, 0x87, 0x96, 0x0, 0x47, 0xff, + + /* U+903B "逻" */ + 0x47, 0xfc, 0x49, 0x48, 0x52, 0x90, 0x25, 0x20, + 0x7f, 0xdc, 0x10, 0x8, 0x20, 0x10, 0xfe, 0x22, + 0xc, 0x4a, 0x30, 0xa2, 0xc1, 0x3, 0x2, 0x38, + 0xb, 0x0, 0x23, 0xff, 0x80, + + /* U+903C "逼" */ + 0x47, 0xfe, 0x20, 0x0, 0x13, 0xfc, 0x2, 0x4, + 0x3, 0xfc, 0xe0, 0x0, 0x27, 0xfe, 0x24, 0x42, + 0x24, 0x42, 0x27, 0xfe, 0x24, 0x42, 0x24, 0x42, + 0x27, 0xfe, 0x58, 0x0, 0x8f, 0xff, + + /* U+903E "逾" */ + 0x40, 0x40, 0x21, 0xb0, 0x3, 0xc, 0xd, 0xf6, + 0x0, 0x0, 0x7, 0x84, 0x64, 0x94, 0x27, 0x94, + 0x24, 0x94, 0x27, 0x94, 0x24, 0x84, 0x24, 0x84, + 0x25, 0x9c, 0x50, 0x0, 0x8f, 0xfe, + + /* U+9041 "遁" */ + 0x40, 0x0, 0x8f, 0xf8, 0x90, 0x80, 0x3f, 0xf0, + 0x42, 0x1c, 0xbf, 0x89, 0x41, 0x12, 0xfe, 0x25, + 0x4, 0x53, 0xf8, 0xa4, 0x11, 0x4f, 0xe5, 0x0, + 0x1b, 0x0, 0x23, 0xff, 0x80, + + /* U+9042 "遂" */ + 0x0, 0x0, 0x41, 0x8, 0x20, 0x90, 0x37, 0xfe, + 0x0, 0x40, 0x1, 0x84, 0xe6, 0x48, 0x20, 0xb0, + 0x23, 0x30, 0x2c, 0x68, 0x21, 0xa4, 0x22, 0x22, + 0x2c, 0x20, 0x21, 0xc0, 0x58, 0x0, 0x8f, 0xfe, + + /* U+9044 "遄" */ + 0x40, 0x40, 0x42, 0x44, 0x22, 0x44, 0x3, 0xfc, + 0x0, 0x0, 0x7, 0xfe, 0x60, 0x40, 0x23, 0xfc, + 0x24, 0x94, 0x24, 0x94, 0x24, 0x94, 0x24, 0x94, + 0x64, 0xc, 0x58, 0x0, 0x8f, 0xfe, + + /* U+9047 "遇" */ + 0x43, 0xfc, 0x22, 0x44, 0x32, 0x44, 0x3, 0xfc, + 0x2, 0x44, 0xe3, 0xfc, 0x20, 0x40, 0x27, 0xfe, + 0x24, 0x42, 0x24, 0x52, 0x24, 0x7a, 0x25, 0x82, + 0x64, 0x1c, 0x58, 0x0, 0x8f, 0xfe, + + /* U+904D "遍" */ + 0x0, 0x0, 0x40, 0x40, 0x67, 0xfe, 0x24, 0x2, + 0x4, 0x2, 0x7, 0xfe, 0xe4, 0x0, 0x25, 0xfe, + 0x26, 0x52, 0x26, 0x52, 0x29, 0xfe, 0x2a, 0x52, + 0x22, 0x52, 0x72, 0x6, 0x58, 0x0, 0x8f, 0xff, + + /* U+904F "遏" */ + 0x47, 0xfc, 0x24, 0x4, 0x37, 0xfc, 0x4, 0x4, + 0x7, 0xfc, 0xe2, 0x0, 0x23, 0xfe, 0x24, 0x42, + 0x28, 0x42, 0x24, 0xb2, 0x25, 0x2, 0x27, 0xe6, + 0x60, 0x1c, 0x58, 0x0, 0x8f, 0xfe, + + /* U+9050 "遐" */ + 0x47, 0xbe, 0x12, 0x41, 0x1, 0x20, 0x80, 0xf7, + 0xc0, 0x40, 0x3, 0x20, 0x0, 0x9e, 0xf8, 0x48, + 0x4c, 0x27, 0x9c, 0x12, 0xe, 0x9, 0xd, 0x8c, + 0x88, 0x45, 0x80, 0x4, 0x7f, 0xf8, + + /* U+9051 "遑" */ + 0x40, 0x80, 0x8f, 0xf8, 0x90, 0x10, 0x3f, 0xe0, + 0x40, 0x5c, 0xff, 0x88, 0x0, 0x13, 0xfe, 0x20, + 0x40, 0x4f, 0xf8, 0x81, 0x1, 0x7f, 0xf7, 0x0, + 0x13, 0x0, 0x23, 0xff, 0x80, + + /* U+9052 "遒" */ + 0x3, 0x8, 0x82, 0x20, 0xbf, 0xf9, 0x5, 0x0, + 0x7f, 0xdc, 0x94, 0x89, 0x49, 0x13, 0x8e, 0x24, + 0x4, 0x4f, 0xf8, 0x90, 0x11, 0x3f, 0xe6, 0x0, + 0xb, 0x0, 0x23, 0xff, 0x80, + + /* U+9053 "道" */ + 0x2, 0x18, 0x82, 0x20, 0xbf, 0xf9, 0x4, 0x0, + 0x7f, 0xdc, 0x80, 0x89, 0xff, 0x12, 0x2, 0x24, + 0x4, 0x4f, 0xf8, 0x90, 0x11, 0x3f, 0xe6, 0x0, + 0xb, 0x0, 0x23, 0xff, 0x80, + + /* U+9057 "遗" */ + 0x40, 0x40, 0x4f, 0xf8, 0x91, 0x10, 0x3f, 0xe0, + 0x4, 0x1d, 0xff, 0xc8, 0x0, 0x13, 0xfe, 0x24, + 0x4, 0x48, 0x88, 0x91, 0x11, 0x5, 0x82, 0xf0, + 0xcb, 0x0, 0x23, 0xff, 0x80, + + /* U+9058 "遘" */ + 0x1, 0x10, 0x9f, 0xfc, 0x84, 0x40, 0x3f, 0xe0, + 0x11, 0x1c, 0xdd, 0x89, 0xff, 0x12, 0x22, 0x27, + 0xfc, 0x48, 0x88, 0xbf, 0xf9, 0x20, 0x27, 0x41, + 0x8b, 0x0, 0x23, 0xff, 0x80, + + /* U+905B "遛" */ + 0x0, 0x0, 0x8e, 0x78, 0x90, 0x90, 0x29, 0x20, + 0x6c, 0x1c, 0x1, 0x89, 0xff, 0x12, 0x22, 0x24, + 0x44, 0x4f, 0xf8, 0x91, 0x11, 0x3f, 0xe5, 0x0, + 0xb, 0x0, 0x23, 0xff, 0x80, + + /* U+9062 "遢" */ + 0x47, 0xfc, 0x24, 0x4, 0x27, 0xfc, 0x4, 0x4, + 0x7, 0xfc, 0xe0, 0x0, 0x2f, 0xbe, 0x20, 0x82, + 0x24, 0x92, 0x21, 0x8e, 0x2e, 0xb2, 0x20, 0x82, + 0x77, 0x9c, 0x58, 0x0, 0x87, 0xfe, + + /* U+9063 "遣" */ + 0x40, 0x40, 0x4f, 0xf8, 0x91, 0x10, 0x3f, 0xe0, + 0x4, 0x1c, 0xf7, 0x89, 0xff, 0x12, 0x2, 0x27, + 0xfc, 0x48, 0x0, 0x9f, 0xf1, 0x20, 0x22, 0x7f, + 0xcb, 0x0, 0x23, 0xff, 0x80, + + /* U+9065 "遥" */ + 0x47, 0xfc, 0x40, 0x8, 0x9, 0x20, 0x10, 0xe, + 0x3f, 0xc4, 0x88, 0x8, 0x10, 0x17, 0xff, 0x24, + 0x44, 0x48, 0x88, 0x9f, 0xf3, 0x0, 0x5, 0x80, + 0x11, 0xff, 0xc0, + + /* U+9068 "遨" */ + 0x1, 0x8, 0x6f, 0xd0, 0x31, 0x1f, 0x7, 0xd2, + 0x1, 0x24, 0x1, 0x24, 0xef, 0xc4, 0x22, 0x14, + 0x23, 0xcc, 0x24, 0x4c, 0x24, 0x94, 0x2b, 0xa2, + 0x50, 0x0, 0x8f, 0xfe, + + /* U+906D "遭" */ + 0x41, 0x20, 0x2f, 0xfe, 0x21, 0x20, 0xf, 0xfc, + 0x9, 0x24, 0xef, 0xfc, 0x2f, 0xfc, 0x20, 0x0, + 0x27, 0xfc, 0x24, 0x4, 0x27, 0xfc, 0x24, 0x4, + 0x67, 0xfc, 0x58, 0x0, 0x8f, 0xfe, + + /* U+906E "遮" */ + 0x0, 0x0, 0x40, 0x20, 0x27, 0xfe, 0x24, 0x0, + 0x4, 0x88, 0x4, 0x88, 0xe5, 0xfe, 0x24, 0x88, + 0x24, 0x88, 0x24, 0xf8, 0x24, 0x0, 0x2a, 0x54, + 0x2a, 0x52, 0x22, 0x52, 0x58, 0x0, 0x8f, 0xfe, + + /* U+9074 "遴" */ + 0x0, 0x40, 0x8c, 0x98, 0x85, 0x0, 0x7f, 0xf0, + 0xc, 0x0, 0x6f, 0x3b, 0x11, 0x91, 0x4, 0x25, + 0x9c, 0x55, 0x50, 0x84, 0xf9, 0x10, 0x47, 0xc0, + 0x8b, 0x0, 0x23, 0xff, 0x80, + + /* U+9075 "遵" */ + 0x41, 0x10, 0x9f, 0xfc, 0x84, 0x80, 0x3f, 0xe0, + 0x52, 0x5c, 0xe3, 0x89, 0xff, 0x12, 0x2, 0x27, + 0xfc, 0x40, 0x10, 0xbf, 0xf9, 0x10, 0x47, 0x7, + 0xb, 0x0, 0x23, 0xff, 0x80, + + /* U+907D "遽" */ + 0x40, 0xfc, 0x41, 0x0, 0x9f, 0xf0, 0x24, 0x0, + 0x7f, 0x9c, 0x8f, 0x9, 0xff, 0x12, 0x20, 0x27, + 0xac, 0x51, 0xa0, 0xad, 0xf1, 0x1c, 0x87, 0xe, + 0x1b, 0x0, 0x23, 0xff, 0x80, + + /* U+907F "避" */ + 0x40, 0x8, 0x4f, 0x10, 0x92, 0xf8, 0x25, 0x0, + 0x4a, 0x40, 0xf2, 0x99, 0xf, 0x93, 0xc4, 0x2e, + 0x88, 0x55, 0x7c, 0xca, 0x21, 0x1c, 0x47, 0x0, + 0x9b, 0x0, 0x23, 0xff, 0x80, + + /* U+9080 "邀" */ + 0x42, 0x10, 0x27, 0x90, 0x38, 0x9e, 0x7, 0xa4, + 0x8, 0xa4, 0xe7, 0x84, 0x21, 0x24, 0x2f, 0xd8, + 0x24, 0x8, 0x27, 0x88, 0x24, 0x94, 0x24, 0xb2, + 0x6b, 0xa2, 0x58, 0x0, 0x8f, 0xfe, + + /* U+9082 "邂" */ + 0x0, 0x0, 0x88, 0x7c, 0x9c, 0x49, 0x48, 0x90, + 0xa1, 0x21, 0xf5, 0x82, 0xa0, 0x75, 0x4c, 0x2f, + 0xbe, 0x55, 0x50, 0xbe, 0x21, 0x55, 0xf3, 0x8, + 0x8e, 0x31, 0x16, 0x0, 0x47, 0xff, + + /* U+9083 "邃" */ + 0x0, 0x0, 0x40, 0x40, 0x27, 0xfe, 0x14, 0x2, + 0x7, 0x1e, 0x0, 0x90, 0xe3, 0x6c, 0x21, 0x84, + 0x26, 0xd8, 0x21, 0x20, 0x2e, 0x78, 0x21, 0xa6, + 0x26, 0x20, 0x71, 0xc0, 0x58, 0x0, 0x87, 0xff, + + /* U+9088 "邈" */ + 0x0, 0x10, 0x42, 0x10, 0x4c, 0x7e, 0x22, 0x42, + 0xb, 0x42, 0x4, 0x7e, 0xea, 0x42, 0x22, 0x42, + 0x2d, 0x7e, 0x23, 0x28, 0x25, 0x28, 0x29, 0x29, + 0x21, 0x4a, 0x66, 0x8e, 0x58, 0x0, 0x8f, 0xff, + + /* U+908B "邋" */ + 0x0, 0x0, 0x84, 0x30, 0x96, 0x41, 0x1a, 0x60, + 0x7f, 0xc0, 0xb4, 0xb9, 0x79, 0x12, 0x2, 0x23, + 0xf8, 0x4b, 0x50, 0x92, 0x21, 0x2d, 0x42, 0x48, + 0xac, 0xfc, 0xd6, 0x0, 0x47, 0xff, + + /* U+9091 "邑" */ + 0x7f, 0xe2, 0x1, 0x10, 0x8, 0xff, 0xc0, 0x0, + 0x0, 0x3, 0xff, 0xd0, 0x42, 0x82, 0x17, 0xff, + 0xa0, 0x3, 0x0, 0x18, 0x0, 0xc0, 0x5, 0xff, + 0xc0, + + /* U+9093 "邓" */ + 0xfc, 0xfc, 0x9, 0x8, 0x12, 0x14, 0x44, 0x44, + 0x88, 0x85, 0x11, 0xc, 0x21, 0x8, 0x43, 0x28, + 0x82, 0x51, 0x5, 0x12, 0xc, 0x4, 0xe0, 0x8, + 0x0, 0x10, 0x0, + + /* U+9095 "邕" */ + 0x23, 0x16, 0x23, 0x10, 0x88, 0x42, 0x27, 0xfe, + 0x20, 0x11, 0xff, 0x80, 0x0, 0xff, 0xf4, 0x10, + 0xa0, 0x85, 0xff, 0xe8, 0x0, 0xc0, 0x7, 0xff, + 0xe0, + + /* U+9097 "邗" */ + 0xff, 0x7e, 0x20, 0x8c, 0x41, 0x10, 0x82, 0x21, + 0x4, 0x82, 0x9, 0x3f, 0xd1, 0x8, 0x22, 0x10, + 0x42, 0x20, 0x84, 0x41, 0x8, 0x82, 0xe1, 0x4, + 0x2, 0x8, 0x0, + + /* U+9099 "邙" */ + 0x10, 0x0, 0x10, 0xfc, 0x1, 0x1f, 0xfa, 0x24, + 0x4, 0x48, 0x9, 0x10, 0x12, 0x20, 0x22, 0x40, + 0x42, 0x80, 0x85, 0x1, 0xa, 0x2, 0x17, 0xf5, + 0xc0, 0x8, 0x0, 0x10, 0x0, + + /* U+909B "邛" */ + 0xfe, 0x7e, 0x20, 0x88, 0x41, 0x10, 0x82, 0x41, + 0x4, 0x82, 0x9, 0x4, 0x11, 0x8, 0x21, 0x10, + 0x42, 0x20, 0x84, 0x79, 0xf, 0x82, 0xe0, 0x4, + 0x0, 0x8, 0x0, + + /* U+909D "邝" */ + 0x4, 0x0, 0x4, 0x3f, 0x0, 0x23, 0x3f, 0xa2, + 0x20, 0x22, 0x20, 0x24, 0x20, 0x24, 0x20, 0x24, + 0x20, 0x22, 0x20, 0x21, 0x20, 0x21, 0x20, 0x21, + 0x20, 0x21, 0x40, 0x2e, 0x40, 0x20, 0x0, 0x20, + + /* U+90A1 "邡" */ + 0x0, 0x0, 0x8, 0x3e, 0x4, 0x22, 0x7f, 0xa2, + 0x20, 0x24, 0x20, 0x24, 0x20, 0x24, 0x3f, 0x24, + 0x21, 0x22, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, + 0x21, 0x21, 0x21, 0x2e, 0x41, 0x20, 0x9e, 0x20, + + /* U+90A2 "邢" */ + 0xff, 0x7c, 0x48, 0x88, 0x91, 0x11, 0x22, 0x42, + 0x44, 0x84, 0x89, 0x3f, 0xd1, 0x12, 0x22, 0x24, + 0x42, 0x88, 0x85, 0x11, 0xa, 0x22, 0xec, 0x44, + 0x10, 0x88, 0x0, + + /* U+90A3 "那" */ + 0x7f, 0x3e, 0x11, 0x22, 0x11, 0x22, 0x11, 0x22, + 0x7f, 0x24, 0x11, 0x24, 0x11, 0x22, 0x7f, 0x23, + 0x11, 0x21, 0x11, 0x21, 0x11, 0x21, 0x21, 0x2e, + 0x21, 0x20, 0x4e, 0x20, + + /* U+90A6 "邦" */ + 0x8, 0x7e, 0x10, 0x87, 0xfd, 0x10, 0x42, 0x20, + 0x84, 0x81, 0x9, 0x1f, 0xd2, 0x4, 0x22, 0x8, + 0x43, 0xfe, 0x84, 0x21, 0x8, 0x82, 0x11, 0x5, + 0xcc, 0x8, 0x30, 0x10, 0x0, + + /* U+90AA "邪" */ + 0x7f, 0xbe, 0x2, 0x22, 0x22, 0x22, 0x42, 0x24, + 0x42, 0x24, 0x7f, 0xa4, 0x6, 0x22, 0xa, 0x21, + 0xa, 0x21, 0x12, 0x21, 0x22, 0x21, 0x42, 0x2e, + 0x2, 0x20, 0x1c, 0x20, + + /* U+90AC "邬" */ + 0x10, 0x0, 0x20, 0xf9, 0xf9, 0x12, 0x12, 0x24, + 0x24, 0x88, 0x49, 0x17, 0x92, 0x20, 0x24, 0x40, + 0x44, 0xfe, 0x84, 0x5, 0x8, 0xa, 0x1f, 0x94, + 0x20, 0x2b, 0x80, 0xd0, 0xf, 0x20, + + /* U+90AE "邮" */ + 0x10, 0x0, 0x41, 0xf1, 0x4, 0x7f, 0x93, 0x92, + 0x4a, 0x49, 0x49, 0x25, 0x3f, 0x92, 0x92, 0x46, + 0x49, 0x19, 0x24, 0x64, 0x91, 0xfe, 0x5a, 0x9, + 0x8, 0x24, 0x0, + + /* U+90AF "邯" */ + 0x21, 0x0, 0x42, 0x7c, 0x84, 0x8f, 0xfd, 0x32, + 0x12, 0x44, 0x25, 0x8, 0x4a, 0x10, 0x92, 0x3f, + 0x24, 0x42, 0x44, 0x84, 0x89, 0x9, 0x12, 0x12, + 0xc7, 0xe4, 0x8, 0x48, 0x0, + + /* U+90B0 "邰" */ + 0x8, 0x0, 0x20, 0xf8, 0x81, 0x11, 0x12, 0x24, + 0x24, 0x9f, 0xe9, 0x0, 0x52, 0x0, 0x22, 0x7f, + 0x46, 0x82, 0x85, 0x5, 0xa, 0xa, 0x14, 0x15, + 0xcf, 0xe8, 0x10, 0x50, 0x0, + + /* U+90B1 "邱" */ + 0x0, 0x0, 0xfe, 0xfd, 0x1, 0x12, 0x2, 0x24, + 0x4, 0x8f, 0xe9, 0x11, 0x12, 0x22, 0x22, 0x44, + 0x42, 0x88, 0x85, 0x11, 0xa, 0x22, 0x14, 0x75, + 0xdf, 0x8, 0x0, 0x10, 0x0, + + /* U+90B3 "邳" */ + 0xff, 0x7c, 0x20, 0x88, 0x41, 0x10, 0x82, 0x43, + 0x44, 0x86, 0x49, 0x14, 0x91, 0x48, 0xa1, 0x90, + 0x42, 0x20, 0x84, 0x1, 0x8, 0x7a, 0xef, 0x4, + 0x0, 0x8, 0x0, + + /* U+90B4 "邴" */ + 0xff, 0x7c, 0x20, 0x88, 0x41, 0x27, 0xfa, 0x49, + 0x14, 0x92, 0x29, 0x24, 0x51, 0x54, 0xa2, 0xa5, + 0x45, 0x8a, 0x8a, 0x5, 0x14, 0xa, 0xe8, 0x14, + 0x11, 0xc8, 0x0, + + /* U+90B5 "邵" */ + 0x7f, 0x3e, 0x11, 0x22, 0x11, 0x22, 0x31, 0x24, + 0x21, 0x24, 0x4e, 0x24, 0x0, 0x22, 0x7f, 0x22, + 0x41, 0x21, 0x41, 0x21, 0x41, 0x22, 0x41, 0x2e, + 0x7f, 0x20, 0x41, 0x20, + + /* U+90B6 "邶" */ + 0x12, 0x0, 0x12, 0x3e, 0x12, 0x22, 0x12, 0x22, + 0xf3, 0xa4, 0x12, 0x24, 0x12, 0x24, 0x12, 0x22, + 0x12, 0x22, 0x12, 0x21, 0x12, 0x21, 0x72, 0xa2, + 0x13, 0x2e, 0x10, 0x20, 0x0, 0x20, + + /* U+90B8 "邸" */ + 0x0, 0x3, 0xf9, 0xf8, 0x84, 0xe2, 0x12, 0x88, + 0x5a, 0x21, 0x4f, 0xf5, 0x22, 0x12, 0x88, 0x46, + 0x21, 0x18, 0x54, 0x69, 0x53, 0xc5, 0x72, 0x4d, + 0x0, 0x84, 0x0, + + /* U+90B9 "邹" */ + 0x10, 0x0, 0x40, 0x7c, 0xf8, 0x8a, 0x11, 0x34, + 0x42, 0x40, 0x84, 0x9f, 0xca, 0x0, 0x94, 0x1, + 0x24, 0x2, 0x45, 0xfc, 0x88, 0x9, 0x10, 0x12, + 0x7f, 0xe7, 0x0, 0x48, 0x0, 0x10, + + /* U+90BA "邺" */ + 0x12, 0x0, 0x24, 0x7c, 0x48, 0x88, 0x91, 0x29, + 0x26, 0x52, 0x55, 0x14, 0xa9, 0x29, 0x52, 0x53, + 0x22, 0x24, 0x44, 0x48, 0x88, 0x91, 0x61, 0xfe, + 0x1c, 0x4, 0x0, 0x8, 0x0, + + /* U+90BB "邻" */ + 0x8, 0x0, 0x18, 0xf8, 0x49, 0x11, 0x1a, 0x24, + 0x94, 0x81, 0x9, 0x0, 0x12, 0x3f, 0xa2, 0x1, + 0x42, 0x4, 0x84, 0x19, 0x9, 0x22, 0x11, 0x85, + 0xc1, 0x8, 0x1, 0x10, 0x0, + + /* U+90BE "邾" */ + 0x8, 0x0, 0x90, 0xf9, 0x21, 0x13, 0xfa, 0x24, + 0x84, 0x51, 0x9, 0x2, 0x12, 0x7f, 0xa2, 0x8, + 0x46, 0x30, 0x84, 0xb9, 0xa, 0x4a, 0x18, 0x85, + 0xc1, 0x8, 0x2, 0x10, 0x0, + + /* U+90C1 "郁" */ + 0x8, 0x0, 0x8, 0x3e, 0x7f, 0xa2, 0x10, 0x22, + 0x10, 0x24, 0x3f, 0x24, 0x61, 0x24, 0x3f, 0x22, + 0x21, 0x21, 0x21, 0x21, 0x3f, 0x21, 0x21, 0x21, + 0x21, 0x2e, 0x21, 0x20, 0x27, 0x20, + + /* U+90C4 "郄" */ + 0x41, 0x0, 0x44, 0xf8, 0x71, 0x11, 0xb2, 0x25, + 0x14, 0x82, 0x9, 0x3f, 0xd2, 0x10, 0x22, 0x28, + 0x42, 0x90, 0x86, 0x41, 0x9, 0x12, 0x32, 0x25, + 0xcf, 0xe8, 0x0, 0x10, 0x0, + + /* U+90C5 "郅" */ + 0xff, 0x7c, 0x20, 0x88, 0x91, 0x12, 0x12, 0x4f, + 0xe4, 0x80, 0x29, 0x0, 0x11, 0x8, 0x21, 0xff, + 0x42, 0x20, 0x84, 0x41, 0x8, 0x82, 0xef, 0xf4, + 0x0, 0x8, 0x0, + + /* U+90C7 "郇" */ + 0x20, 0x0, 0x40, 0x7d, 0xfc, 0x8a, 0x9, 0x2f, + 0x92, 0x49, 0x25, 0x12, 0x49, 0x3c, 0x92, 0x49, + 0x22, 0x92, 0x45, 0xe4, 0x8a, 0x9, 0x30, 0x12, + 0x83, 0xc4, 0x0, 0x8, 0x0, + + /* U+90CA "郊" */ + 0x0, 0x0, 0x10, 0x0, 0x21, 0xf7, 0xfa, 0x20, + 0x4, 0x44, 0x89, 0x10, 0x92, 0x40, 0xa4, 0x2, + 0x44, 0x48, 0x84, 0x51, 0x8, 0xc2, 0x11, 0x84, + 0x24, 0x8b, 0x90, 0x90, 0x40, 0x20, + + /* U+90CE "郎" */ + 0x20, 0x0, 0x83, 0xff, 0xc8, 0xa1, 0x22, 0x84, + 0x8b, 0xf2, 0x48, 0x49, 0x21, 0x24, 0xfc, 0x8a, + 0x2, 0x19, 0x8, 0x62, 0x21, 0xb4, 0xbb, 0x2, + 0x0, 0x8, 0x0, + + /* U+90CF "郏" */ + 0x0, 0x0, 0x20, 0xfb, 0xfd, 0x10, 0x82, 0x29, + 0x14, 0x8a, 0x49, 0x14, 0x12, 0x8, 0x22, 0xff, + 0x46, 0x20, 0x84, 0x61, 0x9, 0x22, 0x34, 0x25, + 0x98, 0x8, 0x0, 0x10, 0x0, + + /* U+90D0 "郐" */ + 0x4, 0x0, 0xc, 0x3e, 0x1a, 0x22, 0x11, 0x22, + 0x20, 0xa4, 0x5f, 0x24, 0x0, 0x24, 0x0, 0x22, + 0x7f, 0xa1, 0x10, 0x21, 0x12, 0x21, 0x21, 0x23, + 0x21, 0x2e, 0x7f, 0xa0, 0x0, 0x20, + + /* U+90D1 "郑" */ + 0x42, 0x0, 0x48, 0xf8, 0x91, 0x17, 0xfa, 0x21, + 0x4, 0x82, 0x9, 0x4, 0x12, 0x7f, 0xa2, 0x10, + 0x42, 0x20, 0x84, 0x61, 0x8, 0xa2, 0x12, 0x25, + 0xc8, 0x28, 0x20, 0x10, 0x0, + + /* U+90D3 "郓" */ + 0xff, 0x7d, 0x22, 0x88, 0x41, 0x17, 0xfa, 0x42, + 0x4, 0x89, 0x9, 0x22, 0x11, 0x7f, 0xa1, 0x8, + 0x42, 0x10, 0x85, 0xfd, 0xc, 0x42, 0xe0, 0x84, + 0x1, 0x8, 0x0, + + /* U+90D7 "郗" */ + 0x0, 0x80, 0x31, 0x1e, 0xe, 0x12, 0x73, 0x92, + 0x8, 0x14, 0x7f, 0xd4, 0x10, 0x16, 0x24, 0x12, + 0x3f, 0x91, 0x64, 0x91, 0x24, 0x91, 0x24, 0x9e, + 0x25, 0x90, 0x4, 0x10, 0x4, 0x10, + + /* U+90DB "郛" */ + 0x7f, 0x3e, 0x2, 0x45, 0x28, 0x90, 0x11, 0x67, + 0xf2, 0x80, 0x65, 0x81, 0x9, 0x4, 0x11, 0xb, + 0xa3, 0xf8, 0x44, 0x20, 0x88, 0x41, 0xe0, 0x82, + 0x7, 0x4, 0x0, + + /* U+90DC "郜" */ + 0x4, 0x0, 0x48, 0x7d, 0xfc, 0x9a, 0x21, 0x28, + 0x42, 0x80, 0x85, 0x3f, 0xea, 0x0, 0x12, 0x0, + 0x22, 0xfe, 0x45, 0x4, 0x8a, 0x9, 0x14, 0x13, + 0xcf, 0xe4, 0x10, 0x48, 0x0, + + /* U+90DD "郝" */ + 0x8, 0x0, 0x10, 0xfb, 0xff, 0x10, 0x42, 0x20, + 0x84, 0x9f, 0xf9, 0x4, 0x92, 0x9, 0x22, 0x53, + 0x42, 0xa6, 0x86, 0x4b, 0xd, 0x12, 0x12, 0x25, + 0xc8, 0x48, 0x23, 0x10, 0x0, + + /* U+90E1 "郡" */ + 0x7f, 0x3e, 0x22, 0x44, 0x44, 0x97, 0xfd, 0x21, + 0x12, 0x8f, 0xe4, 0x88, 0x9, 0x10, 0x11, 0x3f, + 0x22, 0xc2, 0x46, 0x84, 0x89, 0x9, 0x63, 0xf2, + 0x4, 0x24, 0x0, + + /* U+90E2 "郢" */ + 0x7f, 0x7d, 0x2, 0x8a, 0x5, 0x13, 0xfa, 0x40, + 0x4, 0x80, 0x9, 0x3f, 0xd1, 0x8, 0x21, 0x10, + 0x43, 0xfe, 0x84, 0x41, 0x18, 0x82, 0xcf, 0xf4, + 0x0, 0x8, 0x0, + + /* U+90E6 "郦" */ + 0xff, 0xbc, 0x0, 0x4b, 0xde, 0x94, 0xa5, 0x49, + 0x4a, 0x9a, 0xd4, 0xb5, 0xa9, 0x6b, 0x51, 0xb4, + 0xa3, 0x29, 0x46, 0x52, 0xf4, 0xa5, 0x9, 0x4a, + 0x14, 0xe4, 0x0, + + /* U+90E7 "郧" */ + 0x3f, 0x3e, 0x42, 0x4c, 0x84, 0x91, 0xf9, 0x20, + 0x2, 0x8f, 0xf5, 0x10, 0x29, 0x24, 0x51, 0x48, + 0xa2, 0x91, 0x45, 0x32, 0x88, 0x91, 0xe2, 0x12, + 0x18, 0x14, 0x0, + + /* U+90E8 "部" */ + 0x8, 0x0, 0x10, 0x7f, 0xfe, 0x98, 0x1, 0x22, + 0x22, 0xc0, 0x85, 0x3f, 0xea, 0x0, 0x12, 0x0, + 0x22, 0xfe, 0x45, 0x4, 0x8a, 0x9, 0x14, 0x13, + 0xcf, 0xe4, 0x10, 0x48, 0x0, + + /* U+90EB "郫" */ + 0x0, 0x0, 0x20, 0x7d, 0xfc, 0x8a, 0x49, 0x34, + 0x92, 0x4f, 0xe4, 0x92, 0x4a, 0x24, 0x92, 0x7f, + 0x24, 0x20, 0x44, 0x80, 0x8b, 0x21, 0x1f, 0xfa, + 0x20, 0x87, 0x81, 0x8, 0x2, 0x10, + + /* U+90ED "郭" */ + 0x8, 0x0, 0x10, 0x7f, 0xfe, 0x88, 0x1, 0x23, + 0xf2, 0x48, 0x25, 0xf, 0xca, 0x0, 0x12, 0x7f, + 0x22, 0x4, 0x44, 0x30, 0x8f, 0xfd, 0x10, 0x82, + 0xc1, 0x4, 0xe, 0x8, 0x0, + + /* U+90EF "郯" */ + 0x4, 0x0, 0x4, 0xbe, 0x64, 0xa2, 0x35, 0x22, + 0xe, 0x24, 0x1b, 0x24, 0x30, 0xac, 0x44, 0x26, + 0x44, 0xa2, 0x25, 0x21, 0x5, 0x21, 0xa, 0x21, + 0x19, 0x2e, 0x20, 0xa0, 0x40, 0x20, + + /* U+90F4 "郴" */ + 0x11, 0x0, 0x11, 0x1e, 0x11, 0x12, 0x7b, 0xd2, + 0x11, 0x12, 0x31, 0x14, 0x3b, 0x14, 0x33, 0x92, + 0x55, 0x51, 0x55, 0x11, 0x19, 0x11, 0x11, 0x11, + 0x11, 0x16, 0x11, 0x10, 0x11, 0x10, + + /* U+90F8 "郸" */ + 0x22, 0x3e, 0x24, 0x45, 0xfc, 0x9a, 0x49, 0x24, + 0x92, 0x4f, 0xe5, 0x12, 0x4a, 0x24, 0x92, 0x7f, + 0x22, 0x10, 0x44, 0x20, 0x8f, 0xfd, 0x10, 0x82, + 0xc1, 0x4, 0x2, 0x8, 0x0, + + /* U+90FD "都" */ + 0x10, 0x0, 0x20, 0x7d, 0xfe, 0x98, 0x89, 0x21, + 0x22, 0x5f, 0xf5, 0x2, 0xa, 0x8, 0x12, 0x7f, + 0x27, 0x42, 0x44, 0xfc, 0x89, 0x9, 0x12, 0x12, + 0xc7, 0xe4, 0x8, 0x48, 0x0, + + /* U+90FE "郾" */ + 0xff, 0x7d, 0x0, 0x8a, 0xfd, 0x25, 0xfa, 0x4a, + 0x14, 0x97, 0xe9, 0x22, 0x11, 0x7f, 0xe1, 0xa2, + 0x43, 0x38, 0x86, 0x6d, 0x1d, 0x2, 0xcf, 0xfc, + 0x0, 0x8, 0x0, + + /* U+9102 "鄂" */ + 0xf7, 0xbd, 0x29, 0x4a, 0x52, 0x97, 0xbd, 0x40, + 0x2, 0x8f, 0xe5, 0x0, 0x9, 0x7f, 0xd1, 0x40, + 0x22, 0xfe, 0x44, 0x4, 0x88, 0x9, 0xe0, 0x22, + 0x3, 0xc4, 0x0, + + /* U+9104 "鄄" */ + 0xff, 0xbe, 0x28, 0x47, 0xfe, 0x94, 0xa5, 0x29, + 0x4a, 0x92, 0x94, 0xbf, 0xe9, 0x4, 0x11, 0x8, + 0x22, 0xff, 0x44, 0x20, 0x88, 0x41, 0x6f, 0xfa, + 0x0, 0x4, 0x0, + + /* U+9119 "鄙" */ + 0x7f, 0x3c, 0x82, 0x49, 0xfc, 0x90, 0x41, 0x4f, + 0xfa, 0x81, 0x5, 0x1f, 0xc9, 0x40, 0x91, 0x9d, + 0x23, 0x4a, 0x46, 0x94, 0x8c, 0xc9, 0xc7, 0xf2, + 0x10, 0x24, 0x0, + + /* U+911E "鄞" */ + 0x22, 0x1, 0xff, 0x7c, 0x88, 0x99, 0xf1, 0x20, + 0x82, 0x4f, 0xe5, 0x12, 0x4a, 0x3f, 0x92, 0x8, + 0x23, 0xff, 0x44, 0x20, 0x8b, 0xf9, 0x10, 0x83, + 0xdf, 0xf4, 0x0, 0x8, 0x0, + + /* U+9122 "鄢" */ + 0xff, 0x3c, 0x10, 0x49, 0x3c, 0x92, 0x41, 0x4f, + 0xfa, 0x88, 0x5, 0x1f, 0xc9, 0x20, 0x11, 0x7f, + 0xa2, 0x1, 0x46, 0xd4, 0x8d, 0x59, 0xea, 0x12, + 0x1, 0xe4, 0x0, + + /* U+9123 "鄣" */ + 0x0, 0x0, 0x10, 0x7d, 0xfc, 0x89, 0x11, 0x22, + 0x22, 0x5f, 0xf5, 0x0, 0xa, 0x3f, 0x94, 0x41, + 0x24, 0xfe, 0x45, 0x4, 0x8b, 0xf9, 0x10, 0x82, + 0x7f, 0xf7, 0x82, 0x8, 0x4, 0x10, + + /* U+912F "鄯" */ + 0x22, 0x0, 0x44, 0x7b, 0xfe, 0x90, 0x41, 0x27, + 0xf2, 0x81, 0x5, 0x3f, 0xea, 0x24, 0x92, 0x2a, + 0x23, 0xab, 0x45, 0xfc, 0x8a, 0x9, 0x34, 0x12, + 0x8f, 0xe4, 0x10, 0x48, 0x0, + + /* U+9131 "鄱" */ + 0xff, 0x7c, 0x20, 0x89, 0x59, 0x17, 0xfa, 0x41, + 0x4, 0x8f, 0x89, 0x20, 0xd1, 0x3f, 0x21, 0x92, + 0x43, 0x24, 0x85, 0xf9, 0x1c, 0x92, 0xc7, 0xe4, + 0x10, 0x48, 0x0, + + /* U+9139 "鄹" */ + 0xf8, 0x1, 0x2f, 0x7d, 0xc2, 0x9c, 0xa5, 0x27, + 0x32, 0x52, 0x25, 0x2f, 0xaa, 0x20, 0x12, 0x7f, + 0x24, 0x10, 0x44, 0xb6, 0x8e, 0x51, 0x12, 0xa3, + 0xc9, 0x24, 0x22, 0x8, 0x0, + + /* U+9143 "酃" */ + 0x7f, 0x3c, 0x10, 0x4b, 0xfe, 0x97, 0x7d, 0x20, + 0x82, 0x8d, 0xe5, 0x2, 0x9, 0x6e, 0xd1, 0xd6, + 0xa3, 0xad, 0x47, 0x5a, 0x8e, 0xb5, 0x6d, 0xda, + 0x1a, 0xd4, 0x0, + + /* U+9146 "酆" */ + 0x8, 0x1, 0xff, 0x7a, 0xaa, 0x95, 0x55, 0x2f, + 0xfa, 0x95, 0x55, 0x15, 0x4a, 0x7f, 0xd2, 0x0, + 0x22, 0xfe, 0x46, 0x4, 0x8b, 0xf9, 0x12, 0x22, + 0xdf, 0xf4, 0x0, 0x8, 0x0, + + /* U+9149 "酉" */ + 0xff, 0xfe, 0x8, 0x80, 0x11, 0x3, 0xff, 0xe4, + 0x44, 0x48, 0x88, 0x92, 0x11, 0x24, 0x3e, 0x50, + 0x4, 0xff, 0xf9, 0x0, 0x12, 0x0, 0x27, 0xff, + 0xc8, 0x0, 0x80, + + /* U+914A "酊" */ + 0xff, 0x7e, 0x50, 0x10, 0xa0, 0x27, 0xf8, 0x4a, + 0x90, 0x95, 0x21, 0x2a, 0x42, 0x53, 0x84, 0x81, + 0x9, 0xfe, 0x12, 0x4, 0x24, 0x8, 0x4f, 0xf0, + 0x90, 0x27, 0x0, + + /* U+914B "酋" */ + 0x8, 0x20, 0x8, 0x83, 0xff, 0xf8, 0x22, 0x0, + 0x44, 0xf, 0xff, 0x91, 0x11, 0x22, 0x22, 0x48, + 0x7c, 0xa0, 0x9, 0x0, 0x13, 0xff, 0xe4, 0x0, + 0x4f, 0xff, 0x90, 0x1, 0x0, + + /* U+914C "酌" */ + 0x0, 0x3, 0xfd, 0x2, 0x84, 0xa, 0x2f, 0x7e, + 0x86, 0xaa, 0x1a, 0xa0, 0x6a, 0x91, 0xa6, 0x26, + 0x8, 0x97, 0xe0, 0x60, 0x81, 0x82, 0x5, 0xf8, + 0x18, 0x27, 0x80, + + /* U+914D "配" */ + 0xff, 0x7c, 0x50, 0x8, 0xa0, 0x17, 0xf8, 0x2a, + 0x90, 0x55, 0x2f, 0xaa, 0x50, 0x53, 0xa0, 0x81, + 0x41, 0xfe, 0x86, 0x5, 0x14, 0xa, 0x2f, 0xf4, + 0x50, 0x2f, 0x80, + + /* U+914E "酎" */ + 0x0, 0x4, 0xff, 0x4, 0x28, 0x4, 0x28, 0x4, + 0x7f, 0x7e, 0xa9, 0x4, 0xa9, 0x44, 0xa5, 0x44, + 0xa7, 0x24, 0x81, 0x4, 0x7f, 0x4, 0x81, 0x4, + 0x81, 0x4, 0x7f, 0x4, 0x81, 0x3c, + + /* U+914F "酏" */ + 0x0, 0x11, 0xfc, 0x20, 0xa2, 0x41, 0x44, 0xaf, + 0xeb, 0xd5, 0x7a, 0xaa, 0xa5, 0x55, 0x4a, 0xc6, + 0x95, 0x5, 0x33, 0xfa, 0x4c, 0x14, 0x18, 0x28, + 0x3f, 0xd0, 0xe0, 0x9f, 0x0, + + /* U+9150 "酐" */ + 0xff, 0x7e, 0x48, 0x20, 0x90, 0x47, 0xf8, 0x8a, + 0x51, 0x14, 0xa2, 0x29, 0x7f, 0xd3, 0x88, 0x81, + 0x11, 0xfe, 0x22, 0x4, 0x44, 0x8, 0x8f, 0xf1, + 0x10, 0x22, 0x0, + + /* U+9152 "酒" */ + 0x4f, 0xfe, 0x62, 0x40, 0x4, 0x80, 0x3f, 0xec, + 0x92, 0x4d, 0x24, 0x82, 0x49, 0x5, 0xe, 0x28, + 0x4, 0x4f, 0xf9, 0x20, 0x12, 0x40, 0x24, 0x7f, + 0xc1, 0x0, 0x80, + + /* U+9157 "酗" */ + 0xfe, 0x84, 0x50, 0x90, 0xa3, 0x2f, 0xf5, 0x5a, + 0xab, 0x35, 0x52, 0x6a, 0xa4, 0xd7, 0x55, 0x82, + 0xcb, 0xfd, 0x96, 0xa, 0xc, 0x14, 0x1f, 0xef, + 0xf0, 0x40, 0x40, + + /* U+915A "酚" */ + 0x0, 0x9, 0xfc, 0x90, 0xa1, 0x21, 0x42, 0x4f, + 0xc8, 0x55, 0x90, 0xeb, 0x40, 0xd6, 0x7e, 0xcc, + 0x45, 0x8, 0x8b, 0xf1, 0x14, 0x22, 0x28, 0x48, + 0x5f, 0x90, 0xa1, 0x4e, 0x0, + + /* U+915D "酝" */ + 0xfe, 0xfc, 0x50, 0x0, 0xa0, 0x7, 0xf0, 0xa, + 0xa0, 0x15, 0x7f, 0xea, 0x80, 0x67, 0x10, 0x82, + 0x41, 0xfc, 0x92, 0xa, 0x14, 0x14, 0x2f, 0xef, + 0xf0, 0x40, 0x40, + + /* U+915E "酞" */ + 0x0, 0x21, 0xfc, 0x40, 0xa0, 0x81, 0x41, 0xf, + 0xff, 0xd5, 0x44, 0x2a, 0x98, 0x55, 0x28, 0xce, + 0x51, 0x4, 0xa3, 0xf9, 0x44, 0x14, 0x48, 0x2c, + 0x9f, 0xe4, 0xa0, 0x48, 0x0, + + /* U+9161 "酡" */ + 0x0, 0x21, 0xfc, 0x20, 0xa7, 0xf9, 0x48, 0x1f, + 0xf0, 0x35, 0x40, 0x2a, 0xa0, 0x55, 0x46, 0xce, + 0x91, 0x5, 0xc3, 0xfa, 0xc, 0x14, 0x18, 0x28, + 0x3f, 0xd0, 0xe0, 0xbf, 0x0, + + /* U+9162 "酢" */ + 0x0, 0x41, 0xfc, 0x80, 0xa1, 0x1, 0x45, 0xef, + 0xea, 0x15, 0x64, 0x2a, 0x88, 0x55, 0x1e, 0xce, + 0x21, 0x4, 0x43, 0xf8, 0xfc, 0x11, 0x8, 0x22, + 0x1f, 0xc4, 0x20, 0x88, 0x0, + + /* U+9163 "酣" */ + 0x0, 0x85, 0xfd, 0x8, 0xa2, 0x11, 0x4f, 0xff, + 0xe8, 0x55, 0x50, 0xaa, 0xa1, 0x55, 0x42, 0xce, + 0xfd, 0x5, 0xb, 0xfa, 0x14, 0x14, 0x28, 0x28, + 0x5f, 0xdf, 0xa0, 0xa1, 0x0, + + /* U+9164 "酤" */ + 0x0, 0x11, 0xfc, 0x20, 0xa0, 0x41, 0x47, 0xff, + 0xe1, 0x15, 0x42, 0x2a, 0x84, 0x55, 0x8, 0xce, + 0xfd, 0x5, 0xb, 0xfa, 0x14, 0x14, 0x28, 0x28, + 0x5f, 0xdf, 0xa0, 0xa1, 0x0, + + /* U+9165 "酥" */ + 0x0, 0x5, 0xfd, 0xf0, 0xa0, 0x81, 0x41, 0xf, + 0xe2, 0x15, 0x7f, 0xea, 0x88, 0x55, 0x38, 0xce, + 0x71, 0x5, 0x53, 0xfa, 0x94, 0x19, 0x28, 0x32, + 0x1f, 0xc4, 0x20, 0x88, 0x0, + + /* U+9169 "酩" */ + 0x0, 0x23, 0xf9, 0x2, 0x87, 0xca, 0x21, 0xfd, + 0x8a, 0xb1, 0x6a, 0xc1, 0x2b, 0x8, 0xcc, 0xc2, + 0x16, 0xff, 0xc8, 0x61, 0x21, 0x84, 0x87, 0xf3, + 0xf8, 0x48, 0x40, + + /* U+916A "酪" */ + 0x0, 0x0, 0xfe, 0x40, 0x28, 0x7e, 0x28, 0x84, + 0x28, 0xc4, 0xff, 0x48, 0xaa, 0x30, 0xaa, 0x30, + 0xaa, 0xcc, 0xcf, 0x82, 0x82, 0xfc, 0xfe, 0x84, + 0x82, 0x84, 0x82, 0x84, 0xfe, 0xfc, 0x82, 0x84, + + /* U+916C "酬" */ + 0xfe, 0x92, 0x51, 0x24, 0xa2, 0x4f, 0xf4, 0x9a, + 0xbd, 0xb5, 0x7b, 0x6a, 0xad, 0xe3, 0x49, 0x82, + 0x93, 0xfd, 0x26, 0xa, 0x4c, 0x18, 0x9f, 0xf1, + 0x30, 0x20, 0x40, + + /* U+916E "酮" */ + 0xfe, 0xfe, 0x51, 0x4, 0xa2, 0xf, 0xf5, 0xda, + 0xa8, 0x35, 0x57, 0x6a, 0xaa, 0xe7, 0x55, 0x82, + 0xab, 0x5, 0x57, 0xfa, 0xec, 0x14, 0x1f, 0xe8, + 0x30, 0x53, 0xc0, + + /* U+916F "酯" */ + 0x0, 0x81, 0xfd, 0x18, 0xa2, 0xc1, 0x46, 0x1f, + 0xe8, 0x55, 0x5f, 0xaa, 0x80, 0x55, 0x0, 0xce, + 0xfd, 0x5, 0xb, 0xfb, 0xf4, 0x14, 0x28, 0x28, + 0x5f, 0xdf, 0xa0, 0xa1, 0x0, + + /* U+9170 "酰" */ + 0x0, 0x11, 0xfd, 0x20, 0xa2, 0x41, 0x47, 0xff, + 0xf1, 0x15, 0x62, 0x2a, 0x84, 0x55, 0xff, 0xce, + 0x49, 0x4, 0x93, 0xf9, 0x24, 0x12, 0x48, 0x28, + 0xbf, 0xd1, 0x60, 0x43, 0x80, + + /* U+9171 "酱" */ + 0x4, 0x40, 0xc9, 0xfc, 0x56, 0x18, 0xe2, 0x46, + 0x47, 0x0, 0xb0, 0x3f, 0xff, 0x81, 0x20, 0x3f, + 0xfc, 0x48, 0x88, 0xb1, 0xf1, 0x0, 0x23, 0xff, + 0xc4, 0x0, 0x8f, 0xff, 0x0, + + /* U+9172 "酲" */ + 0xfe, 0xfe, 0x28, 0x82, 0x28, 0x82, 0xfe, 0xfe, + 0xaa, 0x0, 0xaa, 0x0, 0xaa, 0xfe, 0xce, 0x10, + 0x82, 0x10, 0xfe, 0xfe, 0x82, 0x10, 0x82, 0x10, + 0xff, 0xff, 0x82, 0x0, + + /* U+9174 "酴" */ + 0x0, 0x21, 0xfc, 0x60, 0xa1, 0x41, 0x46, 0x4f, + 0xf8, 0x55, 0x5f, 0x2a, 0x88, 0x55, 0x10, 0xc7, + 0xfd, 0x4, 0x43, 0xfa, 0xa4, 0x15, 0x28, 0x32, + 0x5f, 0xc4, 0x20, 0x98, 0x0, + + /* U+9175 "酵" */ + 0x0, 0x21, 0xfc, 0x40, 0xa3, 0xe9, 0x41, 0x2f, + 0xe2, 0x95, 0x7f, 0xea, 0x84, 0x55, 0x3e, 0xce, + 0x89, 0x4, 0x23, 0xff, 0xfc, 0x10, 0x88, 0x21, + 0x1f, 0xc2, 0x20, 0x98, 0x0, + + /* U+9176 "酶" */ + 0x0, 0x0, 0xfe, 0x40, 0x28, 0xfe, 0x28, 0x80, + 0x29, 0x0, 0xfe, 0xfc, 0xaa, 0x84, 0xaa, 0xa4, + 0xaa, 0x84, 0xcf, 0xff, 0x82, 0xa4, 0xfe, 0x94, + 0x82, 0x84, 0x82, 0x7e, 0xfe, 0x4, 0x82, 0x38, + + /* U+9177 "酷" */ + 0x0, 0x11, 0xfd, 0x20, 0xa2, 0x41, 0x47, 0xef, + 0xf1, 0x15, 0x62, 0x2a, 0x84, 0x55, 0x76, 0xce, + 0x1, 0x5, 0xfb, 0xfa, 0x14, 0x14, 0x28, 0x28, + 0x5f, 0xdf, 0xa0, 0xa1, 0x0, + + /* U+9178 "酸" */ + 0x0, 0x21, 0xfc, 0x80, 0xa2, 0x21, 0x4c, 0xef, + 0xe6, 0x55, 0x49, 0x2a, 0xa1, 0x55, 0xa0, 0xce, + 0x79, 0x5, 0x13, 0xfd, 0x24, 0x11, 0x88, 0x23, + 0x1f, 0xd9, 0x20, 0x41, 0x0, + + /* U+9179 "酹" */ + 0x0, 0x3, 0xfd, 0xfc, 0xa0, 0x1, 0x41, 0x1f, + 0xea, 0x55, 0x50, 0x2a, 0x82, 0x55, 0x4, 0xcf, + 0xff, 0x4, 0x13, 0xfa, 0x24, 0x12, 0x48, 0x20, + 0x9f, 0xc1, 0x20, 0x8e, 0x0, + + /* U+917D "酽" */ + 0xfe, 0xfe, 0x50, 0x50, 0xa2, 0xaf, 0xf5, 0x6a, + 0xa2, 0x95, 0x5f, 0xea, 0xa0, 0x67, 0x40, 0x82, + 0x81, 0xfd, 0x2, 0xa, 0x4, 0x14, 0xf, 0xf0, + 0x10, 0x20, 0x0, + + /* U+917E "酾" */ + 0xfd, 0xfe, 0x60, 0x0, 0xc0, 0x7, 0xee, 0xfb, + 0x65, 0x36, 0xca, 0x75, 0xd6, 0xe7, 0xad, 0x86, + 0xd7, 0xfc, 0xa6, 0x19, 0x4c, 0x32, 0x9f, 0xe5, + 0x30, 0xda, 0xc0, + + /* U+917F "酿" */ + 0x0, 0x21, 0xfc, 0x40, 0xa3, 0xf1, 0x44, 0x2f, + 0xcf, 0xd5, 0x90, 0xab, 0x21, 0x56, 0x42, 0xcc, + 0xfd, 0x9, 0x43, 0xf2, 0xb4, 0x24, 0x88, 0x4b, + 0x9f, 0x99, 0xa1, 0x20, 0x0, + + /* U+9185 "醅" */ + 0x0, 0x21, 0xfc, 0x20, 0xa3, 0xf9, 0x40, 0xf, + 0xe4, 0x95, 0x41, 0x2a, 0xff, 0xd5, 0x0, 0xce, + 0x1, 0x5, 0xfb, 0xfa, 0x14, 0x14, 0x28, 0x28, + 0x5f, 0xdf, 0xa0, 0xa1, 0x0, + + /* U+9187 "醇" */ + 0x0, 0x11, 0xfc, 0x20, 0xa3, 0xf9, 0x40, 0x2, + 0x80, 0x1f, 0xcf, 0xaa, 0x91, 0x55, 0x3e, 0xaa, + 0x1, 0x8d, 0xfa, 0x8, 0x17, 0xf0, 0xc8, 0x2f, + 0xf0, 0x42, 0x3f, 0x84, 0x41, 0x38, + + /* U+9189 "醉" */ + 0x0, 0x21, 0xfc, 0x40, 0xa0, 0x1, 0x47, 0xef, + 0xe8, 0x95, 0x51, 0x2a, 0xb6, 0x55, 0xaa, 0xcf, + 0x1, 0x4, 0x43, 0xff, 0xfc, 0x11, 0x8, 0x22, + 0x1f, 0xc4, 0x20, 0x88, 0x0, + + /* U+918B "醋" */ + 0x0, 0x49, 0xfc, 0x90, 0xa7, 0xf9, 0x42, 0x4f, + 0xe4, 0x95, 0x7f, 0xea, 0x80, 0x55, 0x0, 0xc6, + 0xfd, 0x5, 0xb, 0xfb, 0xf4, 0x14, 0x28, 0x28, + 0x5f, 0xdf, 0xa0, 0xa1, 0x0, + + /* U+918C "醌" */ + 0xfe, 0xfc, 0x51, 0x8, 0xa2, 0x17, 0xf7, 0xea, + 0xa8, 0x55, 0x5f, 0xaa, 0x80, 0x67, 0x48, 0x82, + 0x95, 0xfd, 0x32, 0xa, 0x4c, 0x14, 0x9f, 0xed, + 0x30, 0x53, 0x80, + + /* U+918D "醍" */ + 0xfe, 0xfc, 0x51, 0x8, 0xa3, 0xf7, 0xf4, 0x2a, + 0xaf, 0xd5, 0x40, 0x2a, 0x80, 0x67, 0x7f, 0x82, + 0x91, 0xfd, 0x3e, 0xa, 0x44, 0x14, 0x8f, 0xf7, + 0x10, 0x67, 0xc0, + + /* U+9190 "醐" */ + 0x0, 0x80, 0x1, 0x3, 0xf2, 0x79, 0x84, 0x93, + 0x3d, 0x3f, 0x92, 0x6d, 0x27, 0xda, 0xe9, 0xd5, + 0x53, 0xba, 0xa7, 0x15, 0x7f, 0xea, 0x98, 0x5d, + 0x30, 0x86, 0x7f, 0x8, 0xc2, 0x37, + + /* U+9191 "醑" */ + 0xfd, 0xfc, 0x60, 0x48, 0xc2, 0xf7, 0xe5, 0xb, + 0x4e, 0x16, 0xa7, 0xf5, 0x0, 0x66, 0x7e, 0x84, + 0x85, 0xf9, 0xfa, 0x12, 0x14, 0x27, 0xef, 0xc8, + 0x50, 0x97, 0x80, + + /* U+9192 "醒" */ + 0xfe, 0xfc, 0x28, 0x84, 0x28, 0xfc, 0xfe, 0x84, + 0xaa, 0xfc, 0xaa, 0x10, 0xaa, 0x90, 0xce, 0xfe, + 0x83, 0x10, 0x82, 0x10, 0xfe, 0xfe, 0x82, 0x10, + 0xff, 0xff, 0x82, 0x0, + + /* U+919A "醚" */ + 0x2, 0x1, 0xfa, 0xa4, 0xc5, 0x51, 0x81, 0xaf, + 0xc1, 0x16, 0xef, 0xed, 0x44, 0x6a, 0x98, 0xcd, + 0x3d, 0xa, 0xab, 0xf5, 0x4c, 0x28, 0x88, 0x50, + 0x1f, 0xd0, 0x21, 0x1f, 0x80, + + /* U+919B "醛" */ + 0x0, 0x8b, 0xfa, 0x22, 0x9f, 0xca, 0x2a, 0xfe, + 0x22, 0xa9, 0x4a, 0xa8, 0xea, 0xfe, 0xce, 0x22, + 0x8, 0x8f, 0xef, 0xe0, 0x88, 0x82, 0x23, 0xf8, + 0x88, 0x3f, 0xc0, + + /* U+91A2 "醢" */ + 0x0, 0x1, 0xf8, 0x80, 0xc7, 0xf1, 0x82, 0x3, + 0x8, 0x1f, 0xbf, 0x2d, 0xe2, 0x5a, 0x44, 0xd4, + 0xf9, 0x98, 0x2, 0x17, 0xf7, 0xea, 0xa8, 0x55, + 0x50, 0xaa, 0xbf, 0x55, 0x43, 0xff, + + /* U+91A3 "醣" */ + 0x0, 0x21, 0xf8, 0x40, 0xc7, 0xf1, 0x88, 0x3, + 0x12, 0x1f, 0xbf, 0xad, 0x49, 0x5a, 0xff, 0xd5, + 0x25, 0xbb, 0xfa, 0x14, 0x87, 0xef, 0xe8, 0x68, + 0x50, 0xd0, 0xbf, 0xbf, 0x41, 0x42, + + /* U+91AA "醪" */ + 0xff, 0xfc, 0x60, 0x88, 0xc5, 0x57, 0xe2, 0x6b, + 0x7f, 0x56, 0x8c, 0xad, 0x64, 0x67, 0xb7, 0x85, + 0x81, 0xf8, 0x42, 0x13, 0x14, 0x28, 0xcf, 0xc6, + 0x10, 0xb0, 0x0, + + /* U+91AD "醭" */ + 0x0, 0x51, 0xfe, 0xa8, 0xa5, 0x51, 0x42, 0x8f, + 0xea, 0xd5, 0x59, 0x2a, 0xff, 0x57, 0x10, 0xc2, + 0xfd, 0x4, 0x43, 0xf8, 0x84, 0x1f, 0xf8, 0x26, + 0x1f, 0xcb, 0x20, 0x61, 0x80, + + /* U+91AE "醮" */ + 0x0, 0x51, 0xfd, 0x20, 0xa3, 0xf1, 0x44, 0x8f, + 0xf9, 0x15, 0x7f, 0xaa, 0xa4, 0x55, 0x48, 0xce, + 0xfd, 0x5, 0x23, 0xfb, 0xf4, 0x10, 0x8, 0x2d, + 0x5f, 0xea, 0xa0, 0xd2, 0x80, + + /* U+91AF "醯" */ + 0x0, 0x1, 0xf8, 0x40, 0xc7, 0xf1, 0x82, 0x3, + 0x8, 0x9f, 0xbf, 0xad, 0x0, 0x5a, 0x94, 0xd5, + 0x2b, 0x9e, 0x5e, 0x10, 0x7, 0xef, 0xe8, 0x55, + 0x50, 0xaa, 0xbf, 0x55, 0x43, 0xff, + + /* U+91B4 "醴" */ + 0x0, 0x51, 0xfb, 0xf8, 0xc5, 0x51, 0x8a, 0xaf, + 0xdf, 0xd6, 0xaa, 0xad, 0x2a, 0x6a, 0xff, 0xcc, + 0x1, 0xb, 0xfb, 0xf4, 0x14, 0x2f, 0xe8, 0x4c, + 0x9f, 0xff, 0xe1, 0x0, 0x0, + + /* U+91B5 "醵" */ + 0x0, 0x20, 0xfe, 0x1e, 0x28, 0x20, 0x28, 0xde, + 0xfc, 0xa0, 0xac, 0xba, 0xac, 0x9c, 0xac, 0x80, + 0xcd, 0x3c, 0x85, 0x76, 0xfd, 0x2c, 0x85, 0x4c, + 0x85, 0x3a, 0xfd, 0xca, 0x82, 0x30, + + /* U+91BA "醺" */ + 0xfd, 0xfc, 0x60, 0x43, 0xf7, 0xe5, 0xaf, 0xeb, + 0x52, 0x56, 0xb6, 0xb5, 0x7f, 0x66, 0x10, 0x84, + 0x21, 0xfb, 0xfa, 0x1f, 0xfc, 0x20, 0xf, 0xd5, + 0x50, 0xca, 0x40, + + /* U+91C7 "采" */ + 0x7f, 0xfc, 0x0, 0x0, 0x4, 0x21, 0x8, 0xc1, + 0x1, 0x0, 0x20, 0x3f, 0xff, 0x81, 0xc0, 0x5, + 0x40, 0x12, 0x40, 0x44, 0x43, 0x8, 0x68, 0x10, + 0x20, 0x20, 0x0, + + /* U+91C9 "釉" */ + 0x0, 0x20, 0xf8, 0x40, 0x40, 0x82, 0xaf, 0xf5, + 0x92, 0x22, 0x24, 0x7f, 0x48, 0x88, 0x91, 0x39, + 0xfe, 0xba, 0x45, 0x54, 0x8c, 0x89, 0x11, 0x12, + 0x22, 0x3f, 0xc4, 0x40, 0x80, + + /* U+91CA "释" */ + 0xfd, 0xfe, 0x10, 0x84, 0x94, 0x44, 0x54, 0x38, + 0x10, 0x78, 0xfd, 0x86, 0x10, 0x20, 0x39, 0xfe, + 0x54, 0x20, 0x50, 0x20, 0x93, 0xff, 0x10, 0x20, + 0x10, 0x20, 0x10, 0x20, + + /* U+91CC "里" */ + 0x7f, 0xfc, 0x82, 0x9, 0x4, 0x13, 0xff, 0xe4, + 0x10, 0x48, 0x20, 0x9f, 0xff, 0x0, 0x80, 0x1, + 0x0, 0xff, 0xf8, 0x4, 0x0, 0x8, 0xf, 0xff, + 0xe0, + + /* U+91CD "重" */ + 0x7f, 0xfc, 0x2, 0x0, 0x4, 0x7, 0xff, 0xf0, + 0x10, 0x7, 0xff, 0x8, 0x42, 0x1f, 0xfc, 0x21, + 0x8, 0x7f, 0xf0, 0x4, 0x3, 0xff, 0xe0, 0x10, + 0x1f, 0xff, 0xc0, + + /* U+91CE "野" */ + 0xfe, 0xfe, 0x48, 0x2f, 0xe5, 0x24, 0x88, 0x92, + 0x23, 0xfb, 0xf1, 0x2, 0x44, 0x9, 0xfe, 0x20, + 0x40, 0x81, 0x2, 0x7, 0x88, 0xe0, 0x20, 0x3, + 0x80, + + /* U+91CF "量" */ + 0x3f, 0xf8, 0x40, 0x10, 0xff, 0xe1, 0x0, 0x41, + 0xff, 0x1f, 0xff, 0xc0, 0x0, 0x1f, 0xfc, 0x3f, + 0xf8, 0x42, 0x10, 0xff, 0xe0, 0x8, 0x7, 0xff, + 0xc0, 0x20, 0x3f, 0xff, 0x80, + + /* U+91D1 "金" */ + 0x1, 0x80, 0x6, 0x80, 0x10, 0x80, 0xc0, 0xc3, + 0x0, 0x6b, 0xff, 0x0, 0x40, 0x0, 0x80, 0x3f, + 0xfc, 0x2, 0x0, 0x44, 0x20, 0x48, 0x80, 0x10, + 0xf, 0xff, 0xc0, + + /* U+91DC "釜" */ + 0xc, 0x30, 0x34, 0xc, 0x42, 0x62, 0x1, 0x80, + 0xe, 0x7e, 0x70, 0x0, 0x1f, 0xf8, 0x1, 0x0, + 0x1, 0x0, 0x3f, 0xfc, 0x1, 0x0, 0x19, 0x18, + 0x1, 0x0, 0x7f, 0xfe, + + /* U+9274 "鉴" */ + 0x48, 0x80, 0x91, 0x1, 0x25, 0xf2, 0x41, 0x4, + 0x91, 0x0, 0x50, 0x3, 0x18, 0x18, 0xc, 0xcf, + 0xe6, 0x2, 0x1, 0xff, 0xe0, 0x88, 0x80, 0x96, + 0x1f, 0xff, 0xc0, + + /* U+928E "銎" */ + 0xfd, 0xf0, 0x22, 0x20, 0x45, 0x48, 0xe8, 0x9e, + 0x28, 0xc0, 0x70, 0x3, 0x1c, 0x7f, 0xf7, 0x1, + 0x0, 0xff, 0xf8, 0x44, 0x0, 0x49, 0x8f, 0xff, + 0xe0, + + /* U+92AE "銮" */ + 0x1, 0x0, 0x2, 0x3, 0xff, 0xf8, 0x22, 0x3, + 0x45, 0x8c, 0xa9, 0xa0, 0xa0, 0x2, 0x20, 0x18, + 0x30, 0xdf, 0xde, 0x4, 0x3, 0xff, 0xe1, 0x11, + 0x1, 0x24, 0x3f, 0xff, 0x80, + + /* U+92C8 "鋈" */ + 0x0, 0x0, 0x67, 0xf8, 0x1, 0x0, 0x2, 0x6, + 0x7f, 0xc0, 0x1c, 0xd, 0xc6, 0x21, 0xc2, 0x1c, + 0x71, 0xff, 0xdc, 0x4, 0x0, 0x8, 0x7, 0xff, + 0xc2, 0x22, 0x3f, 0xff, 0x80, + + /* U+933E "錾" */ + 0x0, 0x0, 0x40, 0x3b, 0xfb, 0x82, 0x4, 0x9, + 0x8, 0x1f, 0xdf, 0x84, 0x22, 0x7f, 0x4, 0x13, + 0x8, 0x19, 0x81, 0xff, 0xfc, 0x8, 0x7, 0xff, + 0x82, 0x26, 0x3f, 0xff, 0x0, + + /* U+936A "鍪" */ + 0x0, 0x20, 0xfe, 0x80, 0x89, 0xf8, 0xe4, 0x4f, + 0xf4, 0x83, 0x22, 0x1a, 0x1b, 0x4c, 0x82, 0x6, + 0xc0, 0x10, 0x41, 0xdf, 0x74, 0x8, 0x17, 0xff, + 0xc2, 0x22, 0x3f, 0xff, 0x80, + + /* U+938F "鎏" */ + 0x60, 0x40, 0x2f, 0xfa, 0x4, 0x42, 0x3f, 0xc1, + 0x20, 0x4, 0x82, 0x52, 0x43, 0x81, 0x60, 0xc, + 0x20, 0xff, 0xfa, 0x4, 0xb, 0xff, 0xc1, 0x13, + 0x1f, 0xff, 0xc0, + + /* U+93CA "鏊" */ + 0x8, 0x20, 0xfc, 0xf9, 0xfe, 0x20, 0x44, 0x4f, + 0x73, 0x7, 0xc6, 0x10, 0x92, 0x4e, 0xc2, 0x6, + 0x80, 0x7f, 0xe3, 0x4, 0x33, 0xff, 0xc1, 0x92, + 0x1f, 0xff, 0x80, + + /* U+93D6 "鏖" */ + 0x0, 0x80, 0x3f, 0xfe, 0x22, 0x20, 0x3f, 0xfe, + 0x3f, 0xfe, 0x2f, 0xdc, 0x28, 0x62, 0x2f, 0x3e, + 0x20, 0xc0, 0x23, 0x38, 0x3d, 0xf6, 0x40, 0x40, + 0x47, 0xf8, 0x5f, 0xfe, 0x0, 0x0, + + /* U+943E "鐾" */ + 0x0, 0x0, 0xfc, 0x21, 0xb, 0xfa, 0x12, 0x27, + 0xc4, 0x88, 0x1f, 0xef, 0x84, 0x5f, 0x3c, 0x1, + 0x10, 0xd, 0x80, 0xff, 0xe6, 0x8, 0x37, 0xff, + 0x83, 0x26, 0x3f, 0xff, 0x80, + + /* U+946B "鑫" */ + 0x0, 0x0, 0x1e, 0x1, 0x87, 0x3b, 0xf3, 0x2, + 0x1, 0xff, 0xe1, 0x22, 0x3f, 0xff, 0x10, 0x20, + 0xb3, 0x6f, 0x9f, 0xc4, 0x8, 0xfd, 0xfd, 0x52, + 0xa3, 0xe2, 0x30, 0x7f, + + /* U+9485 "钅" */ + 0x10, 0x40, 0xfa, 0x4, 0x17, 0x84, 0x8, 0xfe, + 0x20, 0x40, 0x81, 0x63, 0x4, 0x0, + + /* U+9486 "钆" */ + 0x0, 0x0, 0x40, 0x80, 0x81, 0x3, 0xf2, 0x4, + 0x4, 0x10, 0x8, 0x3f, 0x90, 0x8, 0x20, 0x10, + 0x41, 0xfc, 0x80, 0x41, 0x0, 0x82, 0x11, 0x24, + 0x23, 0x88, 0x4c, 0x1f, 0x0, + + /* U+9487 "钇" */ + 0x20, 0x0, 0x21, 0xfc, 0x7c, 0xc, 0x40, 0x8, + 0x80, 0x10, 0xfc, 0x20, 0x10, 0x40, 0x10, 0x40, + 0xfc, 0x80, 0x11, 0x3, 0x11, 0x3, 0x16, 0x2, + 0x1b, 0x2, 0x31, 0xfc, + + /* U+9488 "针" */ + 0x20, 0x20, 0x40, 0x41, 0xf0, 0x82, 0x1, 0x8, + 0x2, 0x1f, 0x7f, 0xc8, 0x8, 0x10, 0x10, 0xfc, + 0x20, 0x40, 0x40, 0x80, 0x81, 0x1, 0x2, 0xc2, + 0x6, 0x4, 0x8, 0x8, 0x0, + + /* U+9489 "钉" */ + 0x20, 0x0, 0x20, 0xff, 0x3e, 0x8, 0x40, 0x8, + 0x80, 0x8, 0xbc, 0x8, 0x10, 0x8, 0x10, 0x8, + 0xfe, 0x8, 0x10, 0x8, 0x10, 0x8, 0x10, 0x8, + 0x16, 0x8, 0x18, 0x8, 0x30, 0x70, + + /* U+948A "钊" */ + 0x0, 0x4, 0x80, 0x12, 0x4, 0x5f, 0x91, 0x40, + 0x47, 0xf1, 0x11, 0x4, 0x44, 0x11, 0xfe, 0x44, + 0x41, 0x11, 0x4, 0x44, 0x1, 0x16, 0x4, 0x60, + 0x13, 0x3, 0xc0, + + /* U+948B "钋" */ + 0x20, 0x40, 0x40, 0x80, 0xf9, 0x2, 0x2, 0x8, + 0x4, 0x1f, 0xca, 0x4, 0x12, 0x8, 0x22, 0xfe, + 0x42, 0x20, 0x80, 0x41, 0x0, 0x82, 0x1, 0x64, + 0x3, 0x8, 0x0, 0x10, 0x0, + + /* U+948C "钌" */ + 0x20, 0x0, 0x43, 0xfc, 0xf0, 0x12, 0x0, 0x48, + 0x1, 0x1f, 0x4, 0x8, 0x8, 0x10, 0x10, 0xfc, + 0x20, 0x40, 0x40, 0x80, 0x81, 0x1, 0x2, 0x42, + 0x7, 0x4, 0x8, 0x78, 0x0, + + /* U+948D "钍" */ + 0x20, 0x20, 0x40, 0x41, 0xf0, 0x82, 0x1, 0x8, + 0x2, 0xf, 0x7f, 0xc8, 0x8, 0x10, 0x10, 0xfc, + 0x20, 0x40, 0x40, 0x80, 0x81, 0x21, 0x3, 0x82, + 0x4, 0x7f, 0xc0, + + /* U+948E "钎" */ + 0x20, 0x0, 0x43, 0xf9, 0xf0, 0x82, 0x1, 0x8, + 0x2, 0x1f, 0x84, 0x8, 0x8, 0x10, 0xfe, 0xfc, + 0x20, 0x40, 0x40, 0x80, 0x81, 0x1, 0x2, 0x42, + 0x7, 0x4, 0x8, 0x8, 0x0, + + /* U+948F "钏" */ + 0x21, 0x25, 0xe4, 0x94, 0x12, 0x60, 0x49, 0xf9, + 0x24, 0x84, 0x92, 0x12, 0x7e, 0x49, 0x21, 0x24, + 0x84, 0x92, 0x92, 0x4e, 0x89, 0x22, 0x24, 0x10, + 0x10, + + /* U+9490 "钐" */ + 0x20, 0x4, 0x40, 0x31, 0xf8, 0xc2, 0x6, 0x8, + 0x0, 0xf, 0x80, 0x88, 0x2, 0x10, 0x18, 0x20, + 0x61, 0xfd, 0x0, 0x80, 0x9, 0x0, 0x62, 0x41, + 0x85, 0xc, 0xc, 0x70, 0x0, + + /* U+9492 "钒" */ + 0x20, 0x0, 0x21, 0xf8, 0x7d, 0x8, 0x41, 0x8, + 0x81, 0x8, 0x79, 0x48, 0x21, 0x48, 0x21, 0x28, + 0xfd, 0x28, 0x21, 0x8, 0x21, 0x8, 0x21, 0x9, + 0x2a, 0x9, 0x32, 0xa, 0x4, 0xe, 0x0, 0x0, + + /* U+9493 "钓" */ + 0x20, 0x0, 0x82, 0x4, 0x8, 0x1f, 0x3f, 0x41, + 0x6, 0x4, 0x17, 0xa0, 0x48, 0x21, 0x20, 0x87, + 0xf1, 0x12, 0x2, 0x48, 0x1, 0x20, 0x4, 0x90, + 0x13, 0x80, 0x4c, 0x1e, + + /* U+9494 "钔" */ + 0x22, 0x1, 0x5, 0xf7, 0x90, 0x60, 0x1, 0x84, + 0x5, 0xd0, 0x12, 0x40, 0x49, 0x1, 0xfc, 0x4, + 0x90, 0x12, 0x40, 0x49, 0x1, 0x2c, 0x4, 0xd0, + 0x10, 0x43, 0x80, + + /* U+9495 "钕" */ + 0x20, 0x40, 0x81, 0x1, 0xe2, 0x2, 0x4, 0x8, + 0x37, 0xcf, 0x21, 0x8, 0x42, 0x11, 0x8, 0xfa, + 0x10, 0x42, 0x20, 0x83, 0x81, 0x3, 0x3, 0x99, + 0x84, 0xc0, 0x80, 0x0, 0x0, + + /* U+9497 "钗" */ + 0x20, 0x0, 0x47, 0xf9, 0xf8, 0x12, 0xa, 0x28, + 0x12, 0x4f, 0x24, 0x88, 0x42, 0x10, 0x44, 0xfc, + 0x90, 0x40, 0xa0, 0x80, 0x81, 0x63, 0x83, 0x98, + 0x84, 0x60, 0xc1, 0x0, 0x80, + + /* U+9499 "钙" */ + 0x20, 0x0, 0x87, 0xf9, 0xe0, 0x84, 0x11, 0x8, + 0x23, 0xcf, 0x44, 0x8, 0x88, 0x11, 0x10, 0xfb, + 0xfc, 0x40, 0x8, 0x80, 0x11, 0x0, 0x22, 0x80, + 0x46, 0x0, 0x88, 0x3e, 0x0, + + /* U+949A "钚" */ + 0x20, 0x0, 0x83, 0xfd, 0xe0, 0x82, 0x1, 0x8, + 0x4, 0xf, 0xc, 0x8, 0x2a, 0x10, 0x54, 0xf9, + 0x24, 0x44, 0x44, 0x88, 0x89, 0x1, 0x2, 0x82, + 0x6, 0x4, 0x0, 0x8, 0x0, + + /* U+949B "钛" */ + 0x20, 0x40, 0x80, 0x81, 0xe1, 0x2, 0x2, 0x8, + 0x3f, 0xef, 0x8, 0x8, 0x30, 0x10, 0x50, 0xf8, + 0xa0, 0x41, 0x20, 0x84, 0x41, 0x5c, 0x43, 0x28, + 0x84, 0x88, 0x80, 0x0, 0x0, + + /* U+949C "钜" */ + 0x20, 0x0, 0x87, 0xf9, 0xe8, 0x4, 0x10, 0x8, + 0x3f, 0xcf, 0x40, 0x88, 0x81, 0x11, 0x2, 0xfa, + 0x4, 0x47, 0xf8, 0x88, 0x1, 0x10, 0x3, 0xa0, + 0x4, 0x7f, 0xc0, + + /* U+949D "钝" */ + 0x20, 0x40, 0x80, 0x81, 0xff, 0xf4, 0x2, 0x8, + 0x4, 0xf, 0x48, 0x88, 0x91, 0x11, 0x22, 0x22, + 0x45, 0xf7, 0xf8, 0x81, 0x1, 0x2, 0x2, 0x84, + 0x46, 0x8, 0x88, 0x1f, 0x0, + + /* U+949E "钞" */ + 0x20, 0x20, 0x40, 0x20, 0x79, 0x28, 0x41, 0x24, + 0x81, 0x22, 0x7a, 0x22, 0x26, 0x20, 0x20, 0x24, + 0xf8, 0x24, 0x20, 0x8, 0x20, 0x10, 0x28, 0x20, + 0x30, 0xc0, 0x27, 0x0, + + /* U+949F "钟" */ + 0x20, 0x21, 0x0, 0x87, 0x82, 0x20, 0xff, 0x82, + 0x25, 0xe8, 0x92, 0x22, 0x48, 0x89, 0x22, 0x27, + 0xef, 0xf2, 0x2, 0x8, 0x8, 0x28, 0x20, 0xc0, + 0x80, 0x2, 0x0, + + /* U+94A0 "钠" */ + 0x20, 0x21, 0x0, 0x87, 0x82, 0x20, 0xff, 0x82, + 0x25, 0xc8, 0x92, 0x22, 0x48, 0x99, 0xfa, 0x54, + 0x8a, 0x52, 0x38, 0xc8, 0x81, 0x2a, 0x4, 0xc8, + 0x10, 0x21, 0xc0, + + /* U+94A1 "钡" */ + 0x20, 0x0, 0x43, 0xf9, 0xf4, 0x12, 0x9, 0x28, + 0x12, 0x5f, 0x24, 0x88, 0x49, 0x10, 0x92, 0xfd, + 0x24, 0x42, 0x48, 0x81, 0x1, 0x63, 0x83, 0x88, + 0x84, 0x60, 0xc1, 0x0, 0x80, + + /* U+94A2 "钢" */ + 0x20, 0x0, 0x47, 0xfd, 0xe8, 0xa, 0x10, 0x58, + 0x28, 0xaf, 0x4a, 0x48, 0x94, 0x91, 0x11, 0xfa, + 0x52, 0x44, 0xa4, 0x8a, 0x29, 0x1c, 0x52, 0xa0, + 0x26, 0x40, 0x48, 0x87, 0x80, + + /* U+94A3 "钣" */ + 0x20, 0xc, 0x87, 0xe1, 0xe8, 0x4, 0x10, 0x8, + 0x20, 0xf, 0x7f, 0x88, 0xa2, 0x11, 0x44, 0xfa, + 0x88, 0x44, 0xa0, 0x89, 0x41, 0x61, 0x3, 0x45, + 0x4, 0xb1, 0x2, 0xc1, 0x0, + + /* U+94A4 "钤" */ + 0x20, 0x40, 0x80, 0xc1, 0xe2, 0x82, 0x8, 0x88, + 0x20, 0x8e, 0xc8, 0xc9, 0x8, 0x10, 0x0, 0xfb, + 0xf8, 0x40, 0x10, 0x80, 0x41, 0x0, 0x82, 0x82, + 0x6, 0x4, 0x0, 0x0, 0x0, + + /* U+94A5 "钥" */ + 0x20, 0x0, 0x87, 0xf7, 0xd0, 0x50, 0x41, 0x81, + 0x5, 0xe7, 0xf2, 0x10, 0x48, 0x41, 0xfd, 0x4, + 0x87, 0xf2, 0x10, 0x4b, 0x41, 0x3b, 0x4, 0x88, + 0x10, 0x43, 0xc0, + + /* U+94A6 "钦" */ + 0x20, 0x40, 0x20, 0x80, 0x7c, 0xfe, 0x40, 0x82, + 0x81, 0x24, 0x79, 0x24, 0x20, 0x20, 0x20, 0x20, + 0xfc, 0x20, 0x20, 0x50, 0x20, 0x50, 0x24, 0x88, + 0x2d, 0x84, 0x33, 0x6, 0x0, 0x0, + + /* U+94A7 "钧" */ + 0x0, 0x0, 0x84, 0x4, 0x10, 0x1e, 0xff, 0x82, + 0x6, 0x10, 0x17, 0x50, 0x48, 0x21, 0x20, 0x47, + 0xe0, 0x52, 0x6, 0x48, 0x61, 0x22, 0x4, 0xa0, + 0x13, 0x0, 0x48, 0x1e, + + /* U+94A8 "钨" */ + 0x20, 0x81, 0xf, 0xe7, 0xa0, 0xa0, 0x82, 0x82, + 0x9, 0xc9, 0xc2, 0x20, 0x8, 0x80, 0xfb, 0xfc, + 0x80, 0x12, 0x7f, 0x48, 0x1, 0x28, 0x4, 0xc0, + 0x32, 0x7, 0x80, + + /* U+94A9 "钩" */ + 0x21, 0x1, 0x4, 0x7, 0xbf, 0xe0, 0x81, 0x84, + 0x5, 0xd2, 0x12, 0x10, 0x48, 0x51, 0xfa, 0x44, + 0x88, 0x92, 0x7f, 0x48, 0x5, 0x28, 0x4, 0xc0, + 0x32, 0xf, 0x80, + + /* U+94AA "钪" */ + 0x0, 0x0, 0x40, 0x81, 0x0, 0x3, 0xdf, 0xe4, + 0x0, 0x10, 0x0, 0x1c, 0x7c, 0x10, 0x88, 0x21, + 0x11, 0xf2, 0x20, 0x84, 0x41, 0x8, 0x82, 0x11, + 0x26, 0x42, 0x4d, 0x84, 0x96, 0xe, + + /* U+94AB "钫" */ + 0x0, 0x0, 0x80, 0x41, 0x0, 0x83, 0xdf, 0xf8, + 0x10, 0x10, 0x20, 0x1e, 0x40, 0x10, 0xfe, 0x21, + 0x5, 0xf2, 0x8, 0x84, 0x11, 0x8, 0x22, 0x20, + 0x45, 0x40, 0x8d, 0x81, 0x2, 0x3c, + + /* U+94AC "钬" */ + 0x20, 0x60, 0x80, 0xc1, 0xe1, 0x82, 0x23, 0x18, + 0x26, 0x4f, 0x4c, 0x88, 0x5a, 0x10, 0x30, 0xf8, + 0xe0, 0x41, 0xa0, 0x82, 0x41, 0x4c, 0xc3, 0x30, + 0x84, 0xc0, 0x81, 0x0, 0x80, + + /* U+94AD "钭" */ + 0x20, 0x8, 0x40, 0x11, 0xf2, 0x22, 0x2, 0x48, + 0x0, 0x9f, 0xa1, 0x4, 0x22, 0x8, 0x4, 0xfc, + 0x8, 0x20, 0x14, 0x4f, 0xf0, 0x80, 0x41, 0x40, + 0x87, 0x1, 0x0, 0x2, 0x0, + + /* U+94AE "钮" */ + 0x20, 0x0, 0x87, 0xf9, 0xe4, 0x14, 0x8, 0x28, + 0x10, 0x4e, 0x20, 0x88, 0x41, 0x11, 0xfe, 0xf9, + 0x4, 0x42, 0x8, 0x84, 0x11, 0x48, 0x23, 0x10, + 0x4c, 0xff, 0xc0, + + /* U+94AF "钯" */ + 0x20, 0x0, 0x87, 0xf9, 0xe9, 0x14, 0x12, 0x28, + 0x24, 0x4e, 0x48, 0x88, 0x91, 0x11, 0xfe, 0xfa, + 0x0, 0x44, 0x0, 0x88, 0x1, 0x10, 0x12, 0xa0, + 0x26, 0x40, 0x48, 0x7f, 0x0, + + /* U+94B0 "钰" */ + 0x20, 0x0, 0x21, 0xfe, 0x7c, 0x20, 0x40, 0x20, + 0x80, 0x20, 0xbc, 0x20, 0x20, 0x20, 0x21, 0xfe, + 0xfc, 0x20, 0x20, 0x28, 0x20, 0x24, 0x20, 0x22, + 0x2c, 0x20, 0x33, 0xff, 0x0, 0x0, + + /* U+94B1 "钱" */ + 0x20, 0x90, 0x20, 0x88, 0x3c, 0x80, 0x40, 0xbe, + 0x83, 0xc0, 0xfc, 0xc0, 0x10, 0xc6, 0x13, 0xf8, + 0xfc, 0x44, 0x10, 0x4c, 0x10, 0x30, 0x14, 0x21, + 0x18, 0xf2, 0x33, 0x12, 0x0, 0xe, + + /* U+94B2 "钲" */ + 0x20, 0x0, 0x21, 0xfe, 0x7c, 0x10, 0x40, 0x10, + 0x80, 0x10, 0x7d, 0x10, 0x21, 0x10, 0x21, 0x1e, + 0xfd, 0x10, 0x21, 0x10, 0x21, 0x10, 0x25, 0x10, + 0x39, 0x10, 0x23, 0xff, + + /* U+94B3 "钳" */ + 0x22, 0x8, 0x88, 0x24, 0x20, 0x9d, 0xff, 0x82, + 0x9, 0xc8, 0x22, 0x20, 0x88, 0x82, 0xfb, 0xf8, + 0x88, 0x22, 0x20, 0x88, 0x82, 0x2a, 0x8, 0xcf, + 0xe0, 0x20, 0x80, + + /* U+94B4 "钴" */ + 0x20, 0x20, 0x80, 0x41, 0xe0, 0x84, 0x1f, 0xf8, + 0x2, 0xf, 0x4, 0x8, 0x8, 0x11, 0xfe, 0xfa, + 0x4, 0x44, 0x8, 0x88, 0x11, 0x10, 0x22, 0xa0, + 0x46, 0x7f, 0x80, 0x81, 0x0, + + /* U+94B5 "钵" */ + 0x20, 0x40, 0x80, 0x81, 0xe1, 0x2, 0x1f, 0xe8, + 0xa, 0xf, 0x1c, 0x8, 0x34, 0x10, 0xa8, 0xf9, + 0x48, 0x44, 0x88, 0x99, 0x9, 0x2f, 0xc2, 0x84, + 0x6, 0x8, 0x0, 0x10, 0x0, + + /* U+94B6 "钶" */ + 0x20, 0x0, 0x8f, 0xfd, 0xe0, 0x14, 0x0, 0x28, + 0x3e, 0x4f, 0x44, 0x88, 0x89, 0x11, 0x12, 0xfa, + 0x24, 0x44, 0x48, 0x8f, 0x91, 0x0, 0x22, 0x80, + 0x46, 0x0, 0x80, 0x1e, 0x0, + + /* U+94B7 "钷" */ + 0x20, 0x0, 0x87, 0xf9, 0xe8, 0x4, 0x10, 0x8, + 0x27, 0xce, 0x48, 0x88, 0x91, 0x11, 0x22, 0xfa, + 0x44, 0x44, 0x88, 0x89, 0xf1, 0x10, 0x3, 0xa0, + 0x4, 0x7f, 0xc0, + + /* U+94B8 "钸" */ + 0x20, 0x40, 0x40, 0x81, 0xef, 0xfa, 0x4, 0x8, + 0x12, 0x1f, 0x24, 0x8, 0xff, 0x11, 0x92, 0xf9, + 0x24, 0x42, 0x48, 0x84, 0x91, 0x9, 0x22, 0x93, + 0xc6, 0x4, 0x8, 0x8, 0x0, + + /* U+94B9 "钹" */ + 0x20, 0x80, 0x81, 0x21, 0xe2, 0x26, 0x3f, 0xf8, + 0x10, 0xe, 0x20, 0x8, 0x7e, 0x11, 0x84, 0xfb, + 0x8, 0x49, 0x20, 0x91, 0x41, 0x1, 0x3, 0xd, + 0x6, 0x61, 0x81, 0x0, 0x0, + + /* U+94BA "钺" */ + 0x20, 0x28, 0x80, 0x51, 0xe0, 0x84, 0x1f, 0xe8, + 0x22, 0xe, 0x44, 0x8, 0x89, 0x11, 0x12, 0xfa, + 0x28, 0x44, 0x50, 0x8a, 0x41, 0x18, 0x82, 0xa3, + 0x46, 0xa, 0x88, 0x23, 0x0, + + /* U+94BB "钻" */ + 0x20, 0x20, 0x20, 0x20, 0x7c, 0x20, 0x40, 0x3f, + 0x80, 0x20, 0x7c, 0x20, 0x20, 0x20, 0x20, 0x20, + 0xfd, 0xde, 0x21, 0x2, 0x21, 0x2, 0x21, 0x2, + 0x2d, 0x2, 0x31, 0xfe, 0x21, 0x2, + + /* U+94BC "钼" */ + 0x20, 0x1, 0xf, 0xf7, 0xa0, 0x60, 0x81, 0x82, + 0x5, 0xef, 0xf2, 0x20, 0x48, 0x81, 0xfa, 0x4, + 0x8f, 0xf2, 0x20, 0x48, 0x81, 0x2a, 0x4, 0xcf, + 0xf0, 0x20, 0x40, + + /* U+94BD "钽" */ + 0x20, 0x0, 0x87, 0xf9, 0xe8, 0x12, 0x10, 0x28, + 0x20, 0x4f, 0x7f, 0x88, 0x81, 0x11, 0x2, 0xfa, + 0x4, 0x44, 0x8, 0x8f, 0xf1, 0x0, 0x3, 0x80, + 0x4, 0xff, 0xc0, + + /* U+94BE "钾" */ + 0x20, 0x1, 0xf, 0xf7, 0xa2, 0x60, 0x89, 0x82, + 0x25, 0xef, 0xf2, 0x22, 0x48, 0x89, 0xfa, 0x24, + 0x8f, 0xf2, 0x22, 0x48, 0x8, 0x28, 0x20, 0xc0, + 0x80, 0x2, 0x0, + + /* U+94BF "钿" */ + 0x20, 0x1, 0xf, 0xf7, 0xa2, 0x70, 0x89, 0x82, + 0x25, 0xe8, 0x92, 0x22, 0x48, 0xff, 0xfa, 0x24, + 0x88, 0x92, 0x22, 0x4a, 0x89, 0x33, 0xfc, 0x88, + 0x10, 0x20, 0x40, + + /* U+94C0 "铀" */ + 0x20, 0x41, 0x1, 0x7, 0x84, 0x10, 0xff, 0x82, + 0x45, 0xe9, 0x12, 0x24, 0x48, 0x91, 0xfb, 0xfc, + 0x89, 0x12, 0x24, 0x48, 0x91, 0x3a, 0x44, 0xcf, + 0xf0, 0x20, 0x40, + + /* U+94C1 "铁" */ + 0x20, 0x20, 0x82, 0x41, 0xe4, 0x84, 0xf, 0xf8, + 0x22, 0xf, 0x44, 0x8, 0x8, 0x11, 0xff, 0xf8, + 0x20, 0x40, 0xa0, 0x81, 0x41, 0x44, 0x43, 0x10, + 0xc4, 0xc0, 0xc1, 0x0, 0x0, + + /* U+94C2 "铂" */ + 0x20, 0x20, 0x40, 0x41, 0xf7, 0xfa, 0x8, 0x18, + 0x10, 0x3f, 0xa0, 0x48, 0x40, 0x90, 0x81, 0xfd, + 0xfe, 0x42, 0x4, 0x84, 0x9, 0x8, 0x12, 0x50, + 0x27, 0x3f, 0xc8, 0x40, 0x80, + + /* U+94C3 "铃" */ + 0x20, 0x40, 0x80, 0xc1, 0xe2, 0x42, 0x8, 0x88, + 0x10, 0x8f, 0x48, 0xc9, 0x8, 0x10, 0x0, 0xfb, + 0xfc, 0x40, 0x10, 0x80, 0x41, 0x9, 0x3, 0x8c, + 0x4, 0x4, 0x0, 0x4, 0x0, + + /* U+94C4 "铄" */ + 0x20, 0x4, 0x87, 0x31, 0xe8, 0x4, 0x11, 0x8, + 0x22, 0xf, 0x44, 0x8, 0x88, 0x11, 0xfe, 0xf8, + 0x20, 0x42, 0x50, 0x84, 0xa1, 0x11, 0x22, 0xa2, + 0x46, 0x84, 0x8, 0x78, 0x0, + + /* U+94C5 "铅" */ + 0x20, 0xf8, 0x41, 0x11, 0xf2, 0x22, 0x4, 0x48, + 0x10, 0x97, 0x21, 0xc8, 0x80, 0x10, 0x0, 0xfd, + 0xfc, 0x42, 0x8, 0x84, 0x11, 0x8, 0x22, 0x50, + 0x47, 0x3f, 0x88, 0x41, 0x0, + + /* U+94C6 "铆" */ + 0x20, 0x80, 0x86, 0x3d, 0xe8, 0x4a, 0x14, 0x98, + 0x29, 0x3f, 0x52, 0x48, 0xa4, 0x91, 0x49, 0xfa, + 0x92, 0x47, 0x24, 0x8a, 0x49, 0x4, 0xf2, 0x91, + 0x6, 0x22, 0x8, 0x84, 0x0, + + /* U+94C8 "铈" */ + 0x20, 0x0, 0x80, 0x84, 0x2, 0x1e, 0xff, 0x80, + 0x22, 0x0, 0x87, 0xbf, 0xc8, 0x89, 0x22, 0x27, + 0xe8, 0x92, 0x22, 0x48, 0x89, 0x22, 0x24, 0xe8, + 0xb3, 0x2, 0x0, 0x8, + + /* U+94C9 "铉" */ + 0x20, 0x40, 0x80, 0x41, 0xef, 0xfa, 0x2, 0x8, + 0x8, 0x1f, 0x21, 0x8, 0x44, 0x11, 0xf8, 0xf8, + 0x20, 0x40, 0x80, 0x82, 0x41, 0x48, 0x43, 0xa0, + 0x86, 0x7f, 0x88, 0x1, 0x0, + + /* U+94CA "铊" */ + 0x20, 0x20, 0x80, 0x41, 0xef, 0xfa, 0x10, 0x18, + 0x20, 0x3f, 0x0, 0x8, 0x41, 0x10, 0x8c, 0xf9, + 0x30, 0x43, 0x80, 0x84, 0x1, 0x8, 0x12, 0x90, + 0x26, 0x20, 0x88, 0x3f, 0x0, + + /* U+94CB "铋" */ + 0x20, 0x40, 0x80, 0x41, 0xe0, 0x94, 0x4, 0x48, + 0x8, 0x8f, 0x51, 0x88, 0xa5, 0x11, 0x49, 0xfc, + 0xa2, 0x49, 0x84, 0x83, 0x11, 0x4c, 0x23, 0x18, + 0x44, 0xd1, 0x81, 0x1e, 0x0, + + /* U+94CC "铌" */ + 0x23, 0xfc, 0x84, 0x9, 0xe8, 0x14, 0x10, 0x28, + 0x20, 0x4f, 0x7f, 0x88, 0x80, 0x11, 0x22, 0xfa, + 0x48, 0x44, 0xa0, 0x89, 0x81, 0x22, 0x13, 0xc4, + 0x24, 0x88, 0x42, 0x1f, 0x0, + + /* U+94CD "铍" */ + 0x20, 0x20, 0x40, 0x41, 0xef, 0xf2, 0x11, 0x28, + 0x22, 0x4f, 0x44, 0x8, 0xff, 0x11, 0x42, 0xfa, + 0x88, 0x44, 0x90, 0x89, 0x41, 0x61, 0x83, 0x47, + 0x4, 0x91, 0x82, 0xc1, 0x0, + + /* U+94CE "铎" */ + 0x23, 0xfc, 0x82, 0x9, 0xe2, 0x14, 0x4, 0x48, + 0x7, 0xe, 0x1b, 0x11, 0xc1, 0xa0, 0x10, 0xfb, + 0xfe, 0x80, 0x41, 0x0, 0x82, 0x5f, 0xf5, 0x82, + 0xc, 0x4, 0x0, 0x8, 0x0, + + /* U+94D0 "铐" */ + 0x20, 0x40, 0x40, 0x42, 0x7d, 0xf4, 0x40, 0x48, + 0x80, 0x50, 0x7b, 0xff, 0x20, 0x40, 0x20, 0xfe, + 0xfb, 0x80, 0x20, 0x80, 0x20, 0xfc, 0x20, 0x4, + 0x2c, 0x4, 0x30, 0x4, 0x20, 0x78, + + /* U+94D1 "铑" */ + 0x20, 0x0, 0x81, 0x1, 0xef, 0xd4, 0x4, 0x28, + 0x8, 0x8f, 0x12, 0x9, 0xff, 0x90, 0x30, 0xf8, + 0x80, 0x46, 0x30, 0x97, 0x81, 0x8, 0x22, 0x90, + 0x46, 0x20, 0x88, 0x7e, 0x0, + + /* U+94D2 "铒" */ + 0x20, 0x0, 0x87, 0xf9, 0xe4, 0x24, 0x8, 0x48, + 0x1f, 0x8e, 0x21, 0x8, 0x42, 0x10, 0x84, 0xf9, + 0xf8, 0x42, 0x10, 0x84, 0x21, 0x9, 0xf3, 0xec, + 0x84, 0x1, 0x0, 0x2, 0x0, + + /* U+94D5 "铕" */ + 0x20, 0x40, 0x80, 0x81, 0xef, 0xfa, 0x4, 0x8, + 0x10, 0x1f, 0x3f, 0x88, 0xc1, 0x12, 0x82, 0xf9, + 0xfc, 0x42, 0x8, 0x87, 0xf1, 0x8, 0x22, 0x90, + 0x46, 0x20, 0x88, 0x47, 0x0, + + /* U+94D6 "铖" */ + 0x20, 0x10, 0x20, 0x16, 0x78, 0x10, 0x43, 0xff, + 0x82, 0x10, 0xf2, 0x10, 0x23, 0xd2, 0x22, 0x52, + 0xfa, 0x54, 0x22, 0x54, 0x22, 0x48, 0x2a, 0x49, + 0x35, 0xd9, 0x24, 0x26, 0x8, 0x46, + + /* U+94D7 "铗" */ + 0x20, 0x20, 0x80, 0x41, 0xef, 0xfc, 0x1, 0x8, + 0x2, 0x4f, 0x24, 0x88, 0x2a, 0x10, 0x10, 0xfb, + 0xfe, 0x40, 0xc0, 0x81, 0x81, 0x44, 0x83, 0x10, + 0x84, 0x40, 0x81, 0x0, 0x80, + + /* U+94D8 "铘" */ + 0x40, 0x1, 0xf, 0x79, 0xc2, 0x94, 0x25, 0x28, + 0x4a, 0x9c, 0x95, 0x11, 0xea, 0x20, 0xd2, 0xf1, + 0xa4, 0x85, 0x45, 0x12, 0x8a, 0x45, 0x14, 0xa, + 0xce, 0x14, 0x10, 0xe8, 0x0, + + /* U+94D9 "铙" */ + 0x21, 0x0, 0x81, 0x19, 0xe7, 0xc2, 0x14, 0x48, + 0x5, 0xf, 0xc, 0x48, 0x69, 0x91, 0xe, 0xf8, + 0x0, 0x47, 0xf8, 0x82, 0x41, 0x4, 0x82, 0x91, + 0x26, 0x62, 0x41, 0x87, 0x0, + + /* U+94DB "铛" */ + 0x20, 0x40, 0x81, 0x4, 0x44, 0x5e, 0x92, 0xc1, + 0x5a, 0x1, 0x7, 0x7f, 0xc8, 0x1, 0x20, 0x7, + 0xe0, 0x12, 0x3f, 0xc8, 0x1, 0x20, 0x4, 0xe0, + 0x12, 0x7f, 0xc0, 0x1, + + /* U+94DC "铜" */ + 0x23, 0xfe, 0x84, 0x5, 0xe8, 0xa, 0x17, 0xd8, + 0x20, 0x2e, 0x40, 0x48, 0xbc, 0x91, 0x49, 0xfa, + 0x92, 0x45, 0x24, 0x8a, 0x49, 0x17, 0x92, 0xa0, + 0x26, 0x40, 0x48, 0x87, 0x0, + + /* U+94DD "铝" */ + 0x21, 0xfc, 0x82, 0x9, 0xe4, 0x14, 0x8, 0x28, + 0x10, 0x4e, 0x3f, 0x88, 0x0, 0x10, 0x0, 0xfb, + 0xfe, 0x44, 0x4, 0x88, 0x9, 0x10, 0x12, 0xa0, + 0x26, 0x7f, 0xc0, 0x80, 0x80, + + /* U+94DE "铞" */ + 0x21, 0xfc, 0x82, 0x9, 0xe4, 0x12, 0x8, 0x28, + 0x1f, 0xde, 0x4, 0x8, 0x8, 0x11, 0xff, 0xfa, + 0x22, 0x44, 0x44, 0x88, 0x89, 0x51, 0x13, 0x22, + 0xc4, 0x4, 0x0, 0x8, 0x0, + + /* U+94DF "铟" */ + 0x23, 0xfd, 0x8, 0x17, 0xa2, 0x50, 0x89, 0x82, + 0x27, 0xeb, 0xf2, 0x22, 0x48, 0x89, 0xfa, 0x54, + 0x89, 0x52, 0x29, 0x4a, 0x81, 0x32, 0x4, 0x8f, + 0xf0, 0x20, 0x40, + + /* U+94E0 "铠" */ + 0x22, 0x44, 0x84, 0x89, 0xe9, 0x14, 0x1f, 0xe8, + 0x0, 0xf, 0x0, 0x9, 0xff, 0x10, 0x2, 0xf8, + 0x4, 0x47, 0xf8, 0x88, 0x1, 0x10, 0x12, 0xa0, + 0x27, 0x40, 0x48, 0x7f, 0x0, + + /* U+94E1 "铡" */ + 0x40, 0x5, 0x1f, 0x57, 0x45, 0x61, 0x55, 0x85, + 0x55, 0xd5, 0x54, 0x55, 0x51, 0x55, 0xf5, 0x55, + 0x15, 0x54, 0x11, 0x50, 0xe1, 0x32, 0x85, 0x91, + 0x11, 0x81, 0xc0, + + /* U+94E2 "铢" */ + 0x20, 0x40, 0x42, 0x81, 0xe9, 0x2, 0x1f, 0xe8, + 0x24, 0xf, 0x88, 0x8, 0x10, 0x13, 0xfe, 0xf8, + 0xe0, 0x41, 0xc0, 0x85, 0x41, 0x52, 0x43, 0x44, + 0x44, 0x8, 0x0, 0x10, 0x0, + + /* U+94E3 "铣" */ + 0x20, 0x20, 0x82, 0x41, 0xe4, 0x82, 0x1f, 0xf8, + 0x22, 0x1f, 0x44, 0x8, 0x8, 0x13, 0xff, 0xf8, + 0x90, 0x41, 0x20, 0x82, 0x41, 0x4, 0x82, 0x91, + 0x26, 0x42, 0x49, 0x7, 0x80, + + /* U+94E4 "铤" */ + 0x40, 0x1, 0x1d, 0xe7, 0x22, 0x20, 0x88, 0x84, + 0x23, 0x90, 0x84, 0x77, 0xd0, 0x48, 0xf9, 0x21, + 0x18, 0x84, 0x6f, 0xd4, 0x80, 0x65, 0x1, 0x23, + 0xf0, 0x0, 0x0, + + /* U+94E5 "铥" */ + 0x20, 0x0, 0x87, 0xfd, 0xe0, 0x82, 0x1, 0x8, + 0x2, 0x1f, 0x3f, 0x88, 0x8, 0x10, 0x10, 0xfb, + 0xfe, 0x40, 0x80, 0x81, 0x1, 0x44, 0x43, 0x10, + 0x44, 0x7f, 0xc0, 0x0, 0x80, + + /* U+94E7 "铧" */ + 0x20, 0x90, 0x20, 0x92, 0x79, 0x14, 0x43, 0x18, + 0x83, 0x30, 0x79, 0x52, 0x21, 0x12, 0x21, 0x1e, + 0xf9, 0x0, 0x20, 0x20, 0x27, 0xff, 0x20, 0x20, + 0x28, 0x20, 0x30, 0x20, 0x20, 0x20, + + /* U+94E8 "铨" */ + 0x0, 0x0, 0x40, 0xc1, 0x1, 0x83, 0xc4, 0x84, + 0x18, 0x90, 0x60, 0x9f, 0x80, 0x10, 0xfc, 0x20, + 0x41, 0xf0, 0x80, 0x8f, 0xf1, 0x2, 0x2, 0x84, + 0x6, 0x8, 0x9, 0xff, 0x0, + + /* U+94E9 "铩" */ + 0x21, 0x4, 0x83, 0x11, 0xe1, 0xc4, 0x3, 0x88, + 0x8, 0x8f, 0x60, 0xc8, 0x8, 0x10, 0x10, 0xfb, + 0xfc, 0x40, 0x40, 0x84, 0xa1, 0x49, 0x23, 0x22, + 0x24, 0x4, 0x8, 0x38, 0x0, + + /* U+94EA "铪" */ + 0x20, 0x60, 0x80, 0xc1, 0xe2, 0x44, 0x8, 0x48, + 0x60, 0x6f, 0x3f, 0x8, 0x0, 0x10, 0x0, 0xfb, + 0xfc, 0x44, 0x8, 0x88, 0x11, 0x10, 0x23, 0xa0, + 0x46, 0x7f, 0x80, 0x81, 0x0, + + /* U+94EB "铫" */ + 0x20, 0x90, 0x40, 0x90, 0x7c, 0x92, 0x42, 0x92, + 0x82, 0x94, 0x79, 0x98, 0x20, 0x90, 0x20, 0x90, + 0xfb, 0x9c, 0x24, 0x92, 0x20, 0x90, 0x21, 0x10, + 0x29, 0x12, 0x32, 0x12, 0x24, 0x1e, + + /* U+94EC "铬" */ + 0x20, 0x80, 0x81, 0xf9, 0xe4, 0x12, 0xc, 0x68, + 0x24, 0x8f, 0xa, 0x8, 0x1c, 0x10, 0xc6, 0xfa, + 0x2, 0x43, 0xf8, 0x84, 0x11, 0x8, 0x23, 0x90, + 0x44, 0x3f, 0x88, 0x41, 0x0, + + /* U+94ED "铭" */ + 0x20, 0x40, 0x82, 0x3, 0x8f, 0xd0, 0x42, 0x43, + 0xa, 0xf2, 0x42, 0x2, 0x8, 0x30, 0xfb, 0xfc, + 0x94, 0x12, 0x10, 0x48, 0x41, 0x29, 0x4, 0xc7, + 0xf0, 0x10, 0x40, + + /* U+94EE "铮" */ + 0x21, 0x0, 0x43, 0xe1, 0xe8, 0x42, 0x21, 0x8, + 0x7f, 0xcf, 0x8, 0x88, 0x11, 0x13, 0xff, 0xf8, + 0x44, 0x40, 0x88, 0x9f, 0xf1, 0x2, 0x22, 0x84, + 0x6, 0x8, 0x0, 0xf0, 0x0, + + /* U+94EF "铯" */ + 0x21, 0x0, 0x83, 0xf1, 0xe8, 0x44, 0x21, 0x8, + 0x3f, 0xcf, 0x48, 0x88, 0x91, 0x11, 0x22, 0xfb, + 0xfc, 0x44, 0x0, 0x88, 0x1, 0x10, 0x12, 0xa0, + 0x26, 0x40, 0x88, 0xff, 0x0, + + /* U+94F0 "铰" */ + 0x0, 0x0, 0x80, 0x81, 0x0, 0x83, 0xdf, 0xf8, + 0x0, 0x10, 0x12, 0x1c, 0x42, 0x11, 0x2, 0x21, + 0x9, 0xf1, 0x20, 0x82, 0x41, 0x3, 0x2, 0x86, + 0x7, 0x16, 0x18, 0xc3, 0x2, 0x1, + + /* U+94F1 "铱" */ + 0x20, 0x0, 0x40, 0x40, 0xf0, 0x82, 0x1f, 0xf4, + 0x2, 0x10, 0xc, 0x3e, 0x18, 0x10, 0x52, 0x21, + 0xb9, 0xf5, 0x20, 0x92, 0x41, 0x4, 0x42, 0x8, + 0x85, 0x14, 0x8c, 0x30, 0x90, 0x80, + + /* U+94F2 "铲" */ + 0x20, 0x40, 0x80, 0x1, 0xef, 0xf4, 0x8, 0x48, + 0x8, 0x8e, 0x12, 0x8, 0xff, 0x91, 0x0, 0xfa, + 0x0, 0x44, 0x0, 0x88, 0x1, 0x50, 0x3, 0x40, + 0x4, 0x80, 0x2, 0x0, 0x0, + + /* U+94F3 "铳" */ + 0x20, 0x20, 0x20, 0x20, 0x7b, 0xfe, 0x40, 0x40, + 0x80, 0x88, 0x71, 0x8, 0x22, 0x4, 0x23, 0xfa, + 0xf8, 0x90, 0x20, 0x90, 0x20, 0x90, 0x29, 0x91, + 0x31, 0x12, 0x22, 0x12, 0x4, 0x1e, + + /* U+94F4 "铴" */ + 0x22, 0x7c, 0x82, 0x9, 0xe0, 0x22, 0x0, 0x88, + 0x2, 0x1f, 0xc8, 0x8, 0x9f, 0x90, 0x15, 0xfa, + 0x4a, 0x44, 0xa4, 0x8a, 0x49, 0x14, 0x92, 0xa2, + 0x26, 0x8c, 0x49, 0x37, 0x0, + + /* U+94F5 "铵" */ + 0x20, 0x20, 0x80, 0x41, 0xef, 0xfa, 0x10, 0x18, + 0x24, 0x3f, 0x8, 0x8, 0x20, 0x11, 0xff, 0xf9, + 0x8, 0x42, 0x10, 0x86, 0x41, 0x3, 0x2, 0x87, + 0x6, 0x31, 0x89, 0x80, 0x80, + + /* U+94F6 "银" */ + 0x23, 0xfc, 0x42, 0x4, 0x7a, 0x4, 0x42, 0x4, + 0x83, 0xfc, 0x7a, 0x4, 0x22, 0x4, 0x23, 0xfc, + 0xfa, 0x40, 0x22, 0x46, 0x22, 0x38, 0x22, 0x10, + 0x2a, 0x48, 0x33, 0x86, 0x0, 0x0, + + /* U+94F7 "铷" */ + 0x42, 0x1, 0x8, 0xf7, 0x22, 0x63, 0xe9, 0x84, + 0xa5, 0xd2, 0x94, 0x4a, 0x51, 0x29, 0xf5, 0x25, + 0x14, 0x94, 0x32, 0x50, 0xc9, 0x76, 0xbd, 0x30, + 0x90, 0x80, 0x0, + + /* U+94F8 "铸" */ + 0x20, 0x40, 0x87, 0xfd, 0xe1, 0x2, 0x2, 0x8, + 0x3f, 0xcf, 0x10, 0x8, 0x20, 0x13, 0xff, 0xf9, + 0x8, 0x43, 0xfc, 0x88, 0x21, 0x54, 0x43, 0x44, + 0x84, 0x1, 0x0, 0x1e, 0x0, + + /* U+94F9 "铹" */ + 0x20, 0x88, 0x81, 0x11, 0xef, 0xfa, 0x4, 0x48, + 0x0, 0xf, 0x7f, 0xc8, 0x90, 0x90, 0x20, 0xf9, + 0xfc, 0x40, 0x88, 0x81, 0x11, 0x2, 0x22, 0x88, + 0x46, 0x20, 0x81, 0x8e, 0x0, + + /* U+94FA "铺" */ + 0x20, 0x24, 0x3b, 0xff, 0x40, 0x20, 0x80, 0x20, + 0x3b, 0xfe, 0x22, 0x22, 0x23, 0xfe, 0xfa, 0x22, + 0x22, 0x22, 0x23, 0xfe, 0x22, 0x22, 0x2a, 0x22, + 0x32, 0x22, 0x22, 0x2e, + + /* U+94FC "铼" */ + 0x20, 0x40, 0x80, 0x81, 0xef, 0xf2, 0x2, 0x8, + 0x24, 0x5f, 0x29, 0x8, 0x10, 0x13, 0xff, 0xf8, + 0x60, 0x41, 0x40, 0x83, 0x41, 0x4a, 0x43, 0x24, + 0x44, 0x88, 0x40, 0x10, 0x0, + + /* U+94FD "铽" */ + 0x20, 0x8, 0x20, 0xa, 0x38, 0x8, 0x43, 0xff, + 0x40, 0x8, 0x78, 0x88, 0x20, 0x48, 0x21, 0x28, + 0x7d, 0x28, 0x25, 0x18, 0x25, 0x8, 0x21, 0x29, + 0x39, 0x25, 0x31, 0xc5, 0x20, 0x2, + + /* U+94FE "链" */ + 0x24, 0x10, 0x44, 0x41, 0xe9, 0xfa, 0x11, 0x8, + 0x2, 0x9e, 0x9, 0x9, 0x92, 0x11, 0x3f, 0xfa, + 0x8, 0x44, 0x10, 0x8b, 0xf9, 0x50, 0x43, 0x20, + 0x84, 0xb0, 0xa, 0x3f, 0x80, + + /* U+94FF "铿" */ + 0x20, 0x80, 0x49, 0xf9, 0xf2, 0x92, 0x25, 0x28, + 0x49, 0x9f, 0x93, 0x9, 0x2f, 0x10, 0x61, 0xf8, + 0x0, 0x40, 0x80, 0x8f, 0xf1, 0x2, 0x3, 0x84, + 0x4, 0xff, 0xc0, 0x0, 0x0, + + /* U+9500 "销" */ + 0x20, 0x21, 0x8, 0x97, 0x92, 0x90, 0x8, 0x83, + 0xfd, 0xe8, 0x12, 0x20, 0x48, 0xff, 0xfa, 0x4, + 0x88, 0x12, 0x3f, 0xca, 0x81, 0x3a, 0x4, 0xc8, + 0x12, 0x21, 0xc0, + + /* U+9501 "锁" */ + 0x20, 0x20, 0x44, 0x49, 0xe4, 0xa2, 0x1, 0x48, + 0x3f, 0xcf, 0x40, 0x88, 0x89, 0x11, 0x12, 0xfa, + 0x24, 0x44, 0x48, 0x88, 0x91, 0x3, 0x83, 0x88, + 0x84, 0x60, 0x81, 0x0, 0x0, + + /* U+9502 "锂" */ + 0x23, 0xfc, 0x84, 0x49, 0xe8, 0x92, 0x11, 0x28, + 0x3f, 0xcf, 0x44, 0x88, 0x89, 0x11, 0xfe, 0xf8, + 0x20, 0x40, 0x40, 0x8f, 0xf1, 0x41, 0x3, 0x2, + 0x4, 0xff, 0xc0, + + /* U+9503 "锃" */ + 0x20, 0x0, 0x87, 0xf9, 0xe8, 0x12, 0x10, 0x28, + 0x3f, 0xcf, 0x0, 0x8, 0x0, 0x11, 0xfe, 0xf8, + 0x40, 0x40, 0x80, 0x8f, 0xf1, 0x2, 0x3, 0x84, + 0x4, 0xff, 0xc0, 0x0, 0x0, + + /* U+9504 "锄" */ + 0x40, 0x11, 0x1e, 0x47, 0x49, 0x21, 0x3f, 0x84, + 0x97, 0x9e, 0x54, 0x49, 0x51, 0x25, 0xf7, 0x95, + 0x12, 0x54, 0x49, 0x51, 0x29, 0x57, 0xe5, 0xa1, + 0x94, 0x5, 0xc0, + + /* U+9505 "锅" */ + 0x21, 0xfc, 0x82, 0x9, 0xf4, 0x12, 0x8, 0x28, + 0x1f, 0xcf, 0x4, 0x8, 0x8, 0x11, 0xff, 0xfa, + 0x22, 0x44, 0xc4, 0x89, 0x49, 0x14, 0x72, 0xa0, + 0x26, 0x40, 0x40, 0x87, 0x0, + + /* U+9506 "锆" */ + 0x21, 0x20, 0x82, 0x41, 0xef, 0xf4, 0x11, 0x8, + 0x22, 0xf, 0x4, 0x9, 0xff, 0x90, 0x0, 0xf8, + 0x0, 0x47, 0xf8, 0x88, 0x11, 0x10, 0x23, 0xa0, + 0x46, 0x7f, 0x88, 0x81, 0x0, + + /* U+9507 "锇" */ + 0x20, 0x10, 0x8f, 0xa9, 0xe4, 0x4a, 0x8, 0x88, + 0x11, 0xf, 0xff, 0xc8, 0x44, 0x10, 0x89, 0xf9, + 0xd4, 0x4e, 0x28, 0x84, 0x21, 0x8, 0xd2, 0x96, + 0xa6, 0x21, 0x49, 0xc1, 0x80, + + /* U+9508 "锈" */ + 0x20, 0x0, 0x87, 0xf9, 0xe0, 0x84, 0x1, 0x8, + 0x3f, 0xcf, 0x15, 0x8, 0xc9, 0x12, 0x1, 0xfb, + 0xf8, 0x41, 0x10, 0x82, 0x39, 0x4, 0x13, 0x90, + 0x46, 0x60, 0x83, 0xf, 0x0, + + /* U+9509 "锉" */ + 0x20, 0x40, 0x82, 0x91, 0xe5, 0x22, 0x12, 0x48, + 0x34, 0x8e, 0x6a, 0x89, 0x34, 0x92, 0x20, 0xf8, + 0x40, 0x47, 0xf8, 0x81, 0x1, 0x42, 0x3, 0x4, + 0x4, 0xff, 0xc0, + + /* U+950A "锊" */ + 0x20, 0xd, 0xe, 0x47, 0x80, 0x50, 0x92, 0x82, + 0x49, 0xe4, 0x2, 0x1, 0x8, 0x4, 0xfb, 0xfc, + 0x80, 0x42, 0x21, 0xa, 0x44, 0x38, 0x10, 0x80, + 0x40, 0xf, 0x0, + + /* U+950B "锋" */ + 0x20, 0x80, 0x43, 0xf9, 0xe4, 0x22, 0x14, 0x88, + 0x6, 0x1f, 0x1f, 0x9, 0xc3, 0x90, 0x20, 0xfb, + 0xfc, 0x40, 0x80, 0x8f, 0xf1, 0x2, 0x2, 0xff, + 0xe6, 0x8, 0x8, 0x10, 0x0, + + /* U+950C "锌" */ + 0x20, 0x20, 0x80, 0x41, 0xef, 0xfc, 0x8, 0x8, + 0x10, 0x8f, 0x11, 0x8, 0x4, 0x11, 0xf7, 0xf8, + 0x20, 0x40, 0x40, 0x9f, 0xf9, 0x1, 0x2, 0x82, + 0x6, 0x4, 0x8, 0x8, 0x0, + + /* U+950D "锍" */ + 0x20, 0x20, 0x80, 0x41, 0xef, 0xfa, 0x2, 0x8, + 0x8, 0x8f, 0x20, 0x88, 0xff, 0x10, 0x1, 0xf8, + 0x0, 0x42, 0x90, 0x85, 0x21, 0xa, 0x42, 0x94, + 0xa6, 0x49, 0x41, 0x13, 0x80, + + /* U+950E "锎" */ + 0x20, 0x0, 0x82, 0x7d, 0xe0, 0xa, 0x10, 0x18, + 0x3f, 0xaf, 0x52, 0x48, 0xa4, 0x91, 0x49, 0xfa, + 0xfa, 0x45, 0x24, 0x8a, 0x49, 0x14, 0x92, 0xb1, + 0x27, 0x40, 0x48, 0x83, 0x80, + + /* U+950F "锏" */ + 0x22, 0x0, 0x42, 0x7d, 0xe0, 0xe, 0x20, 0x18, + 0x4f, 0x2f, 0x92, 0x49, 0x24, 0x92, 0x79, 0xfc, + 0x92, 0x49, 0x24, 0x92, 0x49, 0x27, 0x92, 0x40, + 0x27, 0x80, 0x49, 0x7, 0x80, + + /* U+9510 "锐" */ + 0x21, 0x8, 0x81, 0x11, 0xe2, 0x42, 0x1f, 0xe8, + 0x20, 0x4f, 0x40, 0x88, 0x81, 0x11, 0x2, 0xfb, + 0xfc, 0x41, 0x20, 0x82, 0x41, 0x4, 0x93, 0x91, + 0x26, 0x62, 0x43, 0x7, 0x80, + + /* U+9511 "锑" */ + 0x21, 0x10, 0x81, 0x21, 0xef, 0xf4, 0x2, 0x28, + 0x4, 0x4f, 0x7f, 0x89, 0x10, 0x12, 0x20, 0xff, + 0xfe, 0x40, 0x84, 0x83, 0x9, 0x4a, 0x23, 0x25, + 0xcc, 0x88, 0x0, 0x10, 0x0, + + /* U+9512 "锒" */ + 0x0, 0x40, 0x40, 0x81, 0xf, 0xf3, 0xd0, 0x28, + 0x20, 0x50, 0x7f, 0x9c, 0x81, 0x11, 0x2, 0x23, + 0xfd, 0xf5, 0x4, 0x89, 0x31, 0x12, 0x82, 0x22, + 0x7, 0x43, 0x18, 0xe1, 0x80, 0x0, + + /* U+9513 "锓" */ + 0x20, 0x0, 0x87, 0xf9, 0x0, 0x13, 0xcf, 0xe8, + 0x0, 0x4f, 0x7f, 0x88, 0x0, 0x11, 0xff, 0x22, + 0x3, 0xf7, 0xf0, 0x84, 0x21, 0x4, 0x82, 0xce, + 0x6, 0x16, 0x1, 0xc1, 0x80, + + /* U+9514 "锔" */ + 0x20, 0x0, 0x47, 0xf9, 0xe8, 0x12, 0x1f, 0xe8, + 0x20, 0xf, 0x40, 0x8, 0xff, 0x91, 0x3, 0xfa, + 0xf6, 0x45, 0x2c, 0x8a, 0x59, 0xd7, 0xb3, 0x40, + 0x64, 0x80, 0x82, 0xf, 0x0, + + /* U+9515 "锕" */ + 0x40, 0x0, 0x9f, 0xfd, 0xe4, 0x14, 0x50, 0x28, + 0xa7, 0x4f, 0x52, 0x92, 0xa5, 0x25, 0x4a, 0xf9, + 0x94, 0x93, 0x29, 0x25, 0x92, 0xb8, 0x25, 0x80, + 0x4d, 0x0, 0x80, 0xf, 0x0, + + /* U+9516 "锖" */ + 0x20, 0x20, 0x87, 0xfd, 0xe0, 0x84, 0x1f, 0xf8, + 0x2, 0xf, 0x7f, 0xc8, 0x0, 0x10, 0xfe, 0xfa, + 0x4, 0x43, 0xf8, 0x88, 0x11, 0xf, 0xe2, 0xe0, + 0x46, 0x40, 0x88, 0x87, 0x0, + + /* U+9517 "锗" */ + 0x20, 0x80, 0x81, 0x5, 0xef, 0xd2, 0x4, 0x48, + 0x9, 0xf, 0xff, 0xc8, 0x10, 0x10, 0x40, 0xfb, + 0xfc, 0x4a, 0x8, 0x87, 0xf1, 0x8, 0x23, 0x90, + 0x46, 0x3f, 0x80, 0x41, 0x0, + + /* U+9518 "锘" */ + 0x20, 0x88, 0x81, 0x11, 0xff, 0xfa, 0x4, 0x48, + 0x8, 0x9e, 0x8, 0x9, 0xff, 0x90, 0x40, 0xf9, + 0x0, 0x47, 0xf8, 0x9c, 0x11, 0x8, 0x22, 0x90, + 0x46, 0x3f, 0x88, 0x41, 0x0, + + /* U+9519 "错" */ + 0x20, 0x88, 0x81, 0x11, 0xef, 0xfe, 0x4, 0x48, + 0x8, 0x8f, 0xff, 0xc8, 0x0, 0x10, 0x0, 0xfb, + 0xfc, 0x44, 0x8, 0x8f, 0xf1, 0x10, 0x22, 0xa0, + 0x46, 0x7f, 0x80, 0x81, 0x0, + + /* U+951A "锚" */ + 0x21, 0x10, 0x82, 0x21, 0xff, 0xfe, 0x8, 0x88, + 0x0, 0xf, 0xff, 0x89, 0x11, 0x12, 0x22, 0xfc, + 0x44, 0x4f, 0xf8, 0x91, 0x11, 0x22, 0x22, 0xc4, + 0x46, 0xff, 0x81, 0x1, 0x0, + + /* U+951B "锛" */ + 0x20, 0x40, 0x80, 0x81, 0xef, 0xfa, 0x4, 0x88, + 0x12, 0x8f, 0x44, 0xc9, 0x8, 0x90, 0xfc, 0xf8, + 0x20, 0x42, 0x10, 0x9f, 0xf9, 0x8, 0x42, 0x90, + 0x86, 0x41, 0x9, 0x82, 0x0, + + /* U+951D "锝" */ + 0x20, 0x0, 0x47, 0xf9, 0x8, 0x13, 0xd0, 0x28, + 0x3f, 0xd0, 0x40, 0x9e, 0xff, 0x10, 0x0, 0x27, + 0xff, 0xf0, 0x10, 0x80, 0x21, 0x3f, 0xf2, 0x20, + 0x85, 0x21, 0xc, 0x2, 0x10, 0x3c, + + /* U+951E "锞" */ + 0x20, 0x0, 0x87, 0xf9, 0xe8, 0x94, 0x1f, 0xe8, + 0x22, 0x46, 0x44, 0x88, 0xff, 0x10, 0x10, 0xf8, + 0x20, 0x4f, 0xfc, 0x81, 0xc1, 0x5, 0x42, 0x92, + 0x46, 0xc4, 0x40, 0x8, 0x0, + + /* U+951F "锟" */ + 0x20, 0x0, 0x87, 0xf9, 0xe8, 0x14, 0x1f, 0xe8, + 0x20, 0x4f, 0x40, 0x88, 0xff, 0x10, 0x0, 0xfa, + 0x20, 0x47, 0x58, 0x88, 0xc1, 0x11, 0x12, 0xa2, + 0x26, 0x74, 0x48, 0x8f, 0x0, + + /* U+9521 "锡" */ + 0x20, 0x0, 0x47, 0xf8, 0x88, 0x13, 0xd0, 0x24, + 0x3f, 0xd0, 0x40, 0x8c, 0xff, 0x10, 0x0, 0x22, + 0x1, 0xf7, 0xfc, 0x92, 0x49, 0x24, 0x92, 0x12, + 0x25, 0x64, 0x4d, 0x90, 0x80, 0x4e, + + /* U+9522 "锢" */ + 0x20, 0x0, 0x4f, 0xfd, 0xf0, 0xa, 0x22, 0x18, + 0x5f, 0xaf, 0x88, 0x49, 0x10, 0x92, 0x79, 0xfd, + 0x12, 0x4a, 0x24, 0x93, 0xc9, 0x60, 0x13, 0x40, + 0x24, 0x7f, 0xc1, 0x0, 0x80, + + /* U+9523 "锣" */ + 0x23, 0xfe, 0x85, 0x25, 0xea, 0x4a, 0x14, 0x98, + 0x29, 0x2f, 0x2d, 0x88, 0x20, 0x10, 0x7f, 0xf9, + 0x4, 0x45, 0x18, 0x91, 0x21, 0x1, 0x82, 0x84, + 0x6, 0x30, 0x1, 0x80, 0x0, + + /* U+9524 "锤" */ + 0x20, 0x0, 0x43, 0xfc, 0x78, 0x20, 0x40, 0x20, + 0x83, 0xfe, 0x79, 0x24, 0x21, 0x24, 0x27, 0xff, + 0xf9, 0x24, 0x21, 0x24, 0x23, 0xfe, 0x20, 0x20, + 0x38, 0x20, 0x23, 0xfe, + + /* U+9525 "锥" */ + 0x20, 0xa0, 0x82, 0x21, 0xe4, 0x2, 0x1f, 0xe8, + 0x21, 0xf, 0xc2, 0x9, 0xff, 0x11, 0x8, 0xfa, + 0x10, 0x47, 0xf8, 0x88, 0x41, 0x10, 0x82, 0xa1, + 0x6, 0x7f, 0xc0, 0x80, 0x0, + + /* U+9526 "锦" */ + 0x20, 0x80, 0x81, 0x1, 0xef, 0xf2, 0x10, 0x28, + 0x3f, 0xdf, 0x40, 0x88, 0xff, 0x10, 0x20, 0xf8, + 0x40, 0x47, 0xfc, 0x91, 0x9, 0x22, 0x12, 0xc4, + 0x26, 0x8b, 0x88, 0x10, 0x0, + + /* U+9528 "锨" */ + 0x0, 0x0, 0x81, 0x21, 0x1c, 0x83, 0xa1, 0xe8, + 0x44, 0x50, 0x8a, 0x99, 0xe5, 0x22, 0x48, 0x44, + 0x91, 0xe9, 0x21, 0x12, 0xc2, 0x25, 0x45, 0x4a, + 0x85, 0x15, 0x12, 0x31, 0x8, 0x60, + + /* U+9529 "锩" */ + 0x22, 0x22, 0x41, 0x24, 0x78, 0x20, 0x43, 0xfe, + 0x80, 0x40, 0x78, 0x40, 0x27, 0xff, 0x20, 0x90, + 0x21, 0x8, 0xfb, 0xfe, 0x25, 0x8, 0x21, 0x32, + 0x29, 0x2, 0x31, 0x4, 0x20, 0xfc, + + /* U+952A "锪" */ + 0x21, 0x0, 0x41, 0xfe, 0x7a, 0x4a, 0x44, 0x52, + 0x80, 0x92, 0x79, 0x32, 0x22, 0x22, 0x20, 0xc6, + 0xf8, 0x18, 0x20, 0x40, 0x21, 0x20, 0x2d, 0x2, + 0x3d, 0xa, 0x25, 0x8, 0x1, 0xf0, + + /* U+952B "锫" */ + 0x20, 0x20, 0x80, 0x41, 0xef, 0xfa, 0x8, 0x8, + 0x8, 0x8e, 0x2, 0x9, 0xff, 0x90, 0x0, 0xf8, + 0x0, 0x43, 0xf8, 0x88, 0x11, 0x10, 0x22, 0xa0, + 0x46, 0x3f, 0x80, 0x81, 0x0, + + /* U+952C "锬" */ + 0x20, 0x40, 0x88, 0x89, 0xe9, 0x22, 0x6, 0x8, + 0xa, 0xf, 0x23, 0x9, 0x81, 0x10, 0x20, 0xfc, + 0x44, 0x44, 0x90, 0x83, 0x1, 0x45, 0x3, 0x11, + 0x4, 0x61, 0x1, 0x1, 0x0, + + /* U+952D "锭" */ + 0x20, 0x20, 0x80, 0x21, 0xef, 0xfa, 0x10, 0x18, + 0x20, 0x27, 0x7f, 0xc8, 0x8, 0x10, 0x10, 0xf9, + 0x20, 0x42, 0x3c, 0x84, 0x81, 0x9, 0x2, 0xaa, + 0x6, 0x4c, 0x1, 0x7, 0x80, + + /* U+952E "键" */ + 0x40, 0x20, 0x9f, 0xf9, 0xc4, 0x94, 0x17, 0xf8, + 0x42, 0x4c, 0x84, 0x93, 0xfe, 0x20, 0x90, 0xf2, + 0xfc, 0x94, 0x41, 0x1b, 0xf2, 0xb1, 0x6, 0x70, + 0x9, 0x1f, 0xc0, 0x0, 0x0, + + /* U+952F "锯" */ + 0x20, 0x0, 0x87, 0xf9, 0xe8, 0x12, 0x10, 0x28, + 0x3f, 0xdf, 0x40, 0x8, 0x88, 0x11, 0xff, 0xfa, + 0x20, 0x44, 0x40, 0x8b, 0xf1, 0x58, 0x23, 0x50, + 0x44, 0x9f, 0x82, 0x41, 0x0, + + /* U+9530 "锰" */ + 0x21, 0xfc, 0x80, 0x11, 0xe0, 0x42, 0x1, 0x8, + 0x7f, 0xef, 0x4, 0x8, 0x8, 0x10, 0xe0, 0x20, + 0x1, 0xf7, 0xf8, 0x8a, 0x51, 0x14, 0xa2, 0xa9, + 0x46, 0x52, 0x89, 0xff, 0x80, + + /* U+9531 "锱" */ + 0x41, 0x24, 0x84, 0x51, 0xc9, 0x24, 0x13, 0x48, + 0x13, 0x4e, 0x0, 0x10, 0xff, 0x21, 0x12, 0xfa, + 0x24, 0x84, 0x49, 0xf, 0xf2, 0x11, 0x25, 0xa2, + 0x44, 0x7f, 0x80, 0x81, 0x0, + + /* U+9532 "锲" */ + 0x21, 0x0, 0x27, 0xfe, 0x39, 0x12, 0x41, 0x12, + 0x83, 0xd2, 0x79, 0x12, 0x27, 0xe2, 0x21, 0x2e, + 0xf9, 0x0, 0x20, 0x20, 0x27, 0xff, 0x20, 0x60, + 0x28, 0x50, 0x31, 0x8c, 0x6, 0x3, + + /* U+9534 "锴" */ + 0x22, 0x20, 0x84, 0x4d, 0xef, 0xa2, 0x11, 0x88, + 0x22, 0x2f, 0x54, 0x48, 0xcf, 0x10, 0x20, 0xfb, + 0xfc, 0x44, 0x8, 0x88, 0x11, 0x1f, 0xe2, 0xa0, + 0x46, 0x7f, 0x80, 0x81, 0x0, + + /* U+9535 "锵" */ + 0x21, 0x10, 0x21, 0x3e, 0x79, 0x22, 0x45, 0x44, + 0x85, 0x34, 0xfd, 0x18, 0x23, 0x20, 0x21, 0x44, + 0xfb, 0x7f, 0x25, 0x4, 0x21, 0x44, 0x21, 0x24, + 0x29, 0x4, 0x31, 0x4, 0x21, 0x3c, + + /* U+9536 "锶" */ + 0x23, 0xfc, 0x84, 0x49, 0xe8, 0x92, 0x11, 0x28, + 0x3f, 0xcf, 0x44, 0x88, 0x89, 0x11, 0xfe, 0xf8, + 0x0, 0x40, 0x40, 0x8a, 0x91, 0x54, 0x23, 0x48, + 0xa4, 0x92, 0x0, 0x3c, 0x0, + + /* U+9537 "锷" */ + 0x40, 0x0, 0x8f, 0x79, 0xd2, 0x94, 0x25, 0x28, + 0x7b, 0xc0, 0x0, 0x18, 0xff, 0x20, 0x0, 0xf7, + 0xfe, 0x84, 0x1, 0xf, 0xe2, 0x0, 0x45, 0x80, + 0x86, 0x1, 0x10, 0x3c, 0x0, + + /* U+9538 "锸" */ + 0x20, 0xe, 0x43, 0xf0, 0x78, 0x20, 0x40, 0x20, + 0x87, 0xff, 0xf0, 0x20, 0x20, 0xa0, 0x23, 0x2e, + 0xfa, 0x22, 0x22, 0x22, 0x23, 0xae, 0x22, 0x22, + 0x2a, 0x22, 0x33, 0xfe, 0x2, 0x2, + + /* U+9539 "锹" */ + 0x41, 0x10, 0x9c, 0x21, 0xc8, 0x44, 0x12, 0x98, + 0x25, 0x2e, 0xea, 0x90, 0x97, 0x23, 0x8c, 0xf6, + 0x98, 0x95, 0x71, 0x28, 0xe2, 0x11, 0x24, 0x26, + 0x44, 0x48, 0x40, 0xa0, 0x80, + + /* U+953A "锺" */ + 0x20, 0x0, 0x47, 0xf9, 0x1, 0x3, 0xc2, 0x4, + 0x7f, 0xf0, 0x8, 0x3c, 0xff, 0x11, 0x22, 0x23, + 0xfd, 0xf4, 0x88, 0x8f, 0xf1, 0x2, 0x2, 0xbf, + 0xc6, 0x8, 0x9, 0xff, 0x80, + + /* U+953B "锻" */ + 0x43, 0xb8, 0x88, 0x51, 0xd0, 0xa4, 0x21, 0x48, + 0x64, 0x5c, 0x88, 0xd1, 0x0, 0x23, 0xbe, 0xf4, + 0x44, 0x88, 0x51, 0x1c, 0xa2, 0x60, 0x85, 0x43, + 0xc, 0x89, 0x81, 0x60, 0x0, + + /* U+953C "锼" */ + 0x20, 0x20, 0x47, 0x5d, 0x8, 0x8b, 0xd1, 0x18, + 0x3a, 0xff, 0x44, 0x48, 0xff, 0x90, 0x10, 0xf8, + 0x20, 0x47, 0xf8, 0x84, 0x11, 0x4, 0x42, 0x85, + 0x6, 0x1f, 0x9, 0xc1, 0x80, + + /* U+953E "锾" */ + 0x20, 0x1c, 0x87, 0xc1, 0xe9, 0x24, 0x10, 0x48, + 0x3f, 0xcf, 0x20, 0x8, 0x40, 0x13, 0xff, 0xf9, + 0x0, 0x43, 0xf0, 0x8e, 0x21, 0x54, 0x43, 0x47, + 0x4, 0x8e, 0x2, 0xe3, 0x80, + + /* U+953F "锿" */ + 0x0, 0x0, 0x40, 0x40, 0x9f, 0xfb, 0xc0, 0x4, + 0x0, 0x10, 0x7f, 0xae, 0x81, 0x11, 0x2, 0x23, + 0xfd, 0xf0, 0xc0, 0x82, 0x89, 0x8, 0xe2, 0xb1, + 0x6, 0xa1, 0x8, 0x79, 0x80, 0x80, + + /* U+9540 "镀" */ + 0x0, 0x0, 0x40, 0x41, 0xf, 0xfb, 0xd0, 0x4, + 0x24, 0x90, 0x7f, 0xfc, 0x92, 0x11, 0x24, 0x22, + 0x78, 0x44, 0x3, 0xeb, 0xf1, 0x12, 0x22, 0xa4, + 0x86, 0x86, 0x9, 0x1e, 0x4, 0xc3, + + /* U+9541 "镁" */ + 0x21, 0x8, 0x41, 0x21, 0xef, 0xfa, 0x2, 0x8, + 0x4, 0x1f, 0x7f, 0x88, 0x10, 0x13, 0xff, 0xf8, + 0x0, 0x40, 0x80, 0x8f, 0xf9, 0x3, 0x2, 0x8b, + 0x6, 0x23, 0x9, 0x81, 0x80, + + /* U+9542 "镂" */ + 0x20, 0x20, 0x23, 0x24, 0x78, 0xa8, 0x43, 0xff, + 0x80, 0x70, 0x78, 0xa8, 0x27, 0x27, 0x20, 0x40, + 0xf8, 0x40, 0x23, 0xff, 0x21, 0x88, 0x21, 0x8, + 0x28, 0xd0, 0x30, 0x78, 0x27, 0x86, + + /* U+9544 "镄" */ + 0x40, 0xa1, 0x1f, 0xe7, 0xa, 0xa1, 0xfe, 0x88, + 0xa1, 0x9f, 0xf4, 0x12, 0x50, 0x8a, 0xff, 0xf9, + 0x10, 0x24, 0x44, 0x91, 0x12, 0x50, 0xb1, 0x8c, + 0x30, 0xc0, 0x0, + + /* U+9545 "镅" */ + 0x20, 0x0, 0x8f, 0xf7, 0xa2, 0x50, 0x89, 0x83, + 0xfc, 0xe8, 0x2, 0x2f, 0xc8, 0xa1, 0xfa, 0xfc, + 0x8a, 0x12, 0x28, 0x4b, 0x3f, 0x34, 0x84, 0x93, + 0xf0, 0x88, 0x40, + + /* U+9546 "镆" */ + 0x0, 0x90, 0x41, 0x20, 0x9f, 0xfb, 0xc4, 0x84, + 0x0, 0x10, 0x7f, 0xbe, 0x81, 0x11, 0xfe, 0x22, + 0x5, 0xf7, 0xf8, 0x81, 0x1, 0x2, 0x2, 0x7f, + 0xe5, 0xc, 0xc, 0x24, 0x13, 0x87, 0x0, 0x0, + + /* U+9547 "镇" */ + 0x20, 0x40, 0x8f, 0xfd, 0xe1, 0x4, 0x1f, 0xe8, + 0x20, 0x4e, 0x7f, 0x88, 0x81, 0x11, 0xfe, 0xfa, + 0x4, 0x47, 0xf8, 0x88, 0x11, 0x3f, 0xf3, 0x80, + 0x4, 0x63, 0x81, 0x0, 0x80, + + /* U+9549 "镉" */ + 0x20, 0x0, 0x8f, 0xfd, 0x0, 0x3, 0xdf, 0xe8, + 0x20, 0x4f, 0x7f, 0x88, 0x0, 0x13, 0xff, 0xfd, + 0x1a, 0x49, 0x64, 0x97, 0xe9, 0x22, 0x12, 0xc4, + 0x26, 0x88, 0x49, 0x17, 0x80, + + /* U+954A "镊" */ + 0x0, 0x0, 0x8f, 0xf9, 0x8, 0x23, 0x90, 0x48, + 0x3f, 0x90, 0x41, 0x1c, 0xfe, 0x21, 0x4, 0x47, + 0xfd, 0xe0, 0x1, 0x3f, 0xf2, 0xa, 0x24, 0x52, + 0x8a, 0x63, 0x9, 0xee, 0x4, 0x63, + + /* U+954C "镌" */ + 0x21, 0x20, 0x42, 0x41, 0xef, 0xfa, 0x11, 0x8, + 0x7f, 0xf7, 0x44, 0x8, 0xff, 0x91, 0x10, 0xfb, + 0xde, 0x40, 0x0, 0x8f, 0xc1, 0x48, 0xf3, 0x10, + 0x26, 0x40, 0x41, 0xf, 0x0, + + /* U+954D "镍" */ + 0x20, 0x80, 0x47, 0xf9, 0xe8, 0x12, 0x1f, 0xe8, + 0x20, 0x47, 0x7f, 0x88, 0x81, 0x11, 0xfe, 0xf8, + 0x0, 0x40, 0x80, 0x9f, 0xf9, 0x47, 0x2, 0x95, + 0x6, 0x49, 0xc1, 0x10, 0x0, + + /* U+954E "镎" */ + 0x0, 0x0, 0x20, 0x30, 0x40, 0xc8, 0x79, 0x4, + 0x46, 0x73, 0x81, 0xfc, 0x79, 0x4, 0x21, 0xfc, + 0x20, 0x0, 0xfb, 0xfc, 0x20, 0x20, 0x23, 0xfe, + 0x20, 0x20, 0x27, 0xff, 0x38, 0x20, 0x20, 0xe0, + + /* U+954F "镏" */ + 0x21, 0xc1, 0x8, 0x77, 0xa1, 0x50, 0xa5, 0x82, + 0x55, 0xcf, 0x92, 0x22, 0xc8, 0x0, 0xfb, 0xfc, + 0x88, 0x92, 0x3f, 0xca, 0x89, 0x32, 0x24, 0x8f, + 0xf0, 0x20, 0x40, + + /* U+9550 "镐" */ + 0x0, 0x0, 0x40, 0x81, 0xf, 0xfb, 0xc0, 0x8, + 0x1f, 0xd0, 0x20, 0x9e, 0x41, 0x10, 0xfe, 0x20, + 0x1, 0xf7, 0xfc, 0x88, 0x9, 0x17, 0x92, 0x29, + 0x25, 0x5e, 0x4c, 0x80, 0x91, 0x7, + + /* U+9551 "镑" */ + 0x20, 0x20, 0x43, 0xfe, 0x78, 0x0, 0x40, 0x88, + 0x80, 0x90, 0x7b, 0xfe, 0x22, 0x22, 0x20, 0x20, + 0xfb, 0xde, 0x20, 0x80, 0x20, 0xfc, 0x21, 0x4, + 0x29, 0x4, 0x32, 0x4, 0x24, 0x38, + + /* U+9552 "镒" */ + 0x21, 0x8, 0x82, 0x11, 0xe2, 0x42, 0x3f, 0xf8, + 0x0, 0xe, 0x11, 0x8, 0xc1, 0x12, 0x1, 0xfb, + 0xfc, 0x45, 0x28, 0x8a, 0x51, 0x54, 0xa3, 0x29, + 0x45, 0xff, 0xc0, + + /* U+9553 "镓" */ + 0x20, 0x40, 0x43, 0xfe, 0x7a, 0x2, 0x42, 0x2, + 0x80, 0x0, 0x79, 0xfc, 0x20, 0x80, 0x23, 0x4c, + 0xf8, 0xf0, 0x21, 0x28, 0x26, 0x68, 0x20, 0xa4, + 0x2b, 0x26, 0x36, 0x20, 0x21, 0xc0, + + /* U+9554 "镔" */ + 0x20, 0x40, 0x87, 0xfd, 0xe8, 0xc, 0x10, 0x18, + 0x1f, 0x8f, 0x20, 0x8, 0x40, 0x10, 0xff, 0xf9, + 0x10, 0x42, 0x20, 0x9f, 0xf9, 0x0, 0x3, 0x89, + 0x84, 0x60, 0xc0, 0x0, 0x0, + + /* U+9556 "镖" */ + 0x0, 0x0, 0x4f, 0xfd, 0x2, 0x43, 0xc4, 0x84, + 0x3f, 0xd0, 0x92, 0xbf, 0x25, 0x11, 0xfe, 0x20, + 0x1, 0xf7, 0xf8, 0x80, 0x1, 0x3f, 0xf2, 0x84, + 0x6, 0x69, 0xb, 0x11, 0x90, 0xe0, + + /* U+9557 "镗" */ + 0x24, 0x44, 0x44, 0x91, 0xe1, 0x2, 0x3f, 0xf8, + 0x80, 0x27, 0x7f, 0x48, 0x82, 0x11, 0x4, 0xfb, + 0xf8, 0x40, 0x80, 0x81, 0x1, 0x7f, 0xe3, 0x4, + 0x4, 0xff, 0xc0, + + /* U+9558 "镘" */ + 0x20, 0x0, 0x47, 0xf9, 0x8, 0x13, 0xdf, 0xe4, + 0x20, 0x50, 0x7f, 0xbe, 0x0, 0x11, 0xff, 0x24, + 0x93, 0xf7, 0xfc, 0x80, 0x1, 0x1f, 0xe2, 0x10, + 0xc5, 0x1a, 0xc, 0x1c, 0x13, 0xc7, + + /* U+9559 "镙" */ + 0x20, 0x0, 0x23, 0xfe, 0x42, 0x22, 0x7a, 0x22, + 0x43, 0xfe, 0x82, 0x22, 0x7b, 0xfe, 0x20, 0x40, + 0x23, 0x88, 0xf8, 0xf0, 0x21, 0x84, 0x23, 0xfe, + 0x28, 0x28, 0x33, 0x24, 0x24, 0x23, 0x0, 0xe0, + + /* U+955B "镛" */ + 0x20, 0x20, 0x87, 0xfd, 0x10, 0x5, 0xa7, 0xf8, + 0x41, 0x2f, 0xbf, 0xc9, 0x4, 0x92, 0x3e, 0xfd, + 0xfe, 0x4a, 0x24, 0x97, 0xf9, 0x68, 0x93, 0x5f, + 0xe4, 0xa2, 0x42, 0x47, 0x80, + + /* U+955C "镜" */ + 0x20, 0x20, 0x43, 0xfe, 0x79, 0x8, 0x40, 0x88, + 0x87, 0xff, 0x78, 0x0, 0x23, 0xfc, 0x22, 0x4, + 0xfb, 0xfc, 0x22, 0x4, 0x23, 0xfc, 0x28, 0x90, + 0x38, 0x91, 0x31, 0x11, 0x26, 0x1e, + + /* U+955D "镝" */ + 0x20, 0x20, 0x40, 0x20, 0x7b, 0xde, 0x40, 0x88, + 0x80, 0x90, 0x7b, 0xfe, 0x22, 0x22, 0x22, 0xfa, + 0xfa, 0x22, 0x22, 0xfa, 0x22, 0x8a, 0x22, 0x8a, + 0x2a, 0xfa, 0x3a, 0x2, 0x22, 0x1e, + + /* U+955E "镞" */ + 0x4, 0x20, 0x88, 0x81, 0xc9, 0xf4, 0x7a, 0x8, + 0x4a, 0xe, 0x87, 0x91, 0xd4, 0x24, 0xa8, 0xf9, + 0x10, 0x92, 0xf9, 0x24, 0x42, 0xc9, 0x85, 0x92, + 0x85, 0x29, 0x3, 0xf1, 0x80, + + /* U+955F "镟" */ + 0x40, 0x20, 0x84, 0x41, 0xc9, 0xfc, 0x7a, 0x8, + 0x48, 0x1c, 0x8f, 0xd1, 0xc5, 0x22, 0x88, 0xf5, + 0x50, 0x8a, 0xb9, 0x15, 0x42, 0xca, 0x86, 0x9b, + 0x9, 0x33, 0xd5, 0xe0, 0x0, + + /* U+9561 "镡" */ + 0x20, 0x0, 0x8f, 0xfd, 0x2, 0x43, 0xbf, 0xf8, + 0x49, 0x2e, 0xff, 0xc8, 0x0, 0x11, 0xfe, 0x22, + 0x5, 0xf7, 0xf8, 0x88, 0x11, 0x4f, 0xc3, 0x7f, + 0xe4, 0x4, 0x0, 0x8, 0x0, + + /* U+9562 "镢" */ + 0x20, 0x0, 0x47, 0xfe, 0x76, 0x28, 0x45, 0x48, + 0x87, 0xef, 0x74, 0x92, 0x26, 0xaa, 0x26, 0xa8, + 0xf6, 0xa8, 0x27, 0xe8, 0x24, 0x94, 0x39, 0x14, + 0x29, 0x22, 0xa, 0x23, + + /* U+9563 "镣" */ + 0x20, 0x40, 0x20, 0x40, 0x27, 0xfe, 0x78, 0xa0, + 0xc2, 0x96, 0x81, 0x8, 0x33, 0xfe, 0x26, 0x4, + 0x22, 0x4, 0xfb, 0xfc, 0x22, 0x4, 0x23, 0xfc, + 0x20, 0x40, 0x2a, 0x4c, 0x34, 0x42, 0x1, 0xc0, + + /* U+9564 "镤" */ + 0x0, 0x90, 0x49, 0x29, 0xa, 0x53, 0xc4, 0x88, + 0x7f, 0xf0, 0x21, 0x2c, 0x24, 0x11, 0xff, 0x20, + 0x41, 0xf7, 0xf8, 0x81, 0x1, 0x2, 0x2, 0x7f, + 0xe5, 0xc, 0xc, 0x24, 0x13, 0x87, 0x0, 0x0, + + /* U+9565 "镥" */ + 0x20, 0x80, 0x43, 0xf1, 0xc, 0x43, 0xff, 0xe8, + 0x22, 0x4f, 0x7f, 0x88, 0xff, 0x10, 0x0, 0xff, + 0xfe, 0x40, 0x0, 0x8f, 0xf1, 0x10, 0x23, 0xbf, + 0xc4, 0x7f, 0x80, 0x81, 0x0, + + /* U+9566 "镦" */ + 0x21, 0x10, 0x42, 0x21, 0xdf, 0x42, 0x0, 0xf8, + 0x7e, 0x4e, 0x8e, 0x89, 0x1d, 0x11, 0xca, 0xf7, + 0xd8, 0x41, 0x90, 0x84, 0x21, 0xe, 0x43, 0x71, + 0x44, 0x24, 0xc1, 0xc8, 0x80, + + /* U+9567 "镧" */ + 0x24, 0x0, 0x8d, 0xfd, 0xc0, 0xc, 0x2, 0x18, + 0x9f, 0x2f, 0x7f, 0x52, 0x92, 0xa5, 0xb5, 0xfa, + 0x4a, 0x97, 0xf5, 0x23, 0xa, 0x4b, 0x95, 0xa4, + 0xa5, 0x8, 0x52, 0x17, 0x0, + + /* U+9568 "镨" */ + 0x21, 0x8, 0x81, 0x21, 0xef, 0xfa, 0x4, 0x88, + 0x29, 0x4f, 0x12, 0x9, 0xff, 0x90, 0x0, 0xfb, + 0xfc, 0x44, 0x8, 0x88, 0x11, 0x1f, 0xe2, 0xa0, + 0x46, 0x7f, 0x88, 0x81, 0x0, + + /* U+9569 "镩" */ + 0x20, 0x20, 0x87, 0xfd, 0xe8, 0xa, 0x14, 0x98, + 0x70, 0xd4, 0x7f, 0x88, 0x89, 0x11, 0xfe, 0xf8, + 0x20, 0x47, 0xf8, 0x88, 0x91, 0x51, 0x23, 0xbf, + 0xc4, 0x4, 0x0, 0x8, 0x0, + + /* U+956A "镪" */ + 0x4e, 0xfd, 0x5, 0xb, 0xca, 0x14, 0x14, 0x28, + 0xe7, 0x9d, 0x2, 0x12, 0x4, 0x24, 0x7e, 0xfe, + 0x94, 0x85, 0x29, 0xb, 0xf2, 0x10, 0x86, 0x21, + 0x48, 0x7f, 0x83, 0x80, 0x80, + + /* U+956B "镫" */ + 0x20, 0x24, 0x43, 0xd8, 0x78, 0x92, 0x43, 0x8c, + 0x81, 0x8, 0x72, 0xf6, 0x24, 0x0, 0x23, 0xfc, + 0xfa, 0x4, 0x22, 0x4, 0x23, 0xfc, 0x29, 0x8, + 0x30, 0x90, 0x27, 0xfe, + + /* U+956C "镬" */ + 0x20, 0x88, 0x4f, 0xfd, 0xe2, 0x22, 0x9, 0xf, + 0xaf, 0xc4, 0xff, 0xc8, 0x88, 0x11, 0xff, 0xfa, + 0x20, 0x47, 0xfc, 0x8f, 0xf1, 0x48, 0x63, 0xf, + 0x4, 0x73, 0xc1, 0x0, 0x0, + + /* U+956D "镭" */ + 0x20, 0x0, 0x47, 0xf9, 0x0, 0x83, 0xff, 0xf8, + 0x42, 0x2e, 0xb7, 0x48, 0x8, 0x10, 0xde, 0x73, + 0xfc, 0x44, 0x48, 0x88, 0x91, 0x5f, 0xe3, 0xa2, + 0x44, 0x7f, 0x80, 0x81, 0x0, + + /* U+956F "镯" */ + 0x20, 0x1, 0x1f, 0xf4, 0x4a, 0x5d, 0x29, 0x87, + 0xfe, 0x8, 0x7, 0x7f, 0xd3, 0x21, 0x47, 0xf7, + 0xd2, 0x54, 0x49, 0x51, 0xfd, 0x40, 0x85, 0xff, + 0xd6, 0x0, 0x40, 0xe, + + /* U+9570 "镰" */ + 0x40, 0x40, 0x8f, 0xf9, 0xe4, 0x24, 0x44, 0x88, + 0xbf, 0xfd, 0x14, 0x12, 0xff, 0x24, 0x52, 0xff, + 0xfc, 0x91, 0x49, 0x2f, 0xf2, 0x44, 0x87, 0x9a, + 0x8a, 0x54, 0x84, 0x28, 0x0, + + /* U+9571 "镱" */ + 0x20, 0x20, 0x43, 0xfe, 0x79, 0xc, 0x40, 0x88, + 0x87, 0xff, 0xf8, 0x0, 0x23, 0xfe, 0x22, 0x2, + 0xfb, 0xfe, 0x22, 0x2, 0x23, 0xfe, 0x28, 0x20, + 0x3b, 0x2, 0x34, 0x89, 0x20, 0xf8, + + /* U+9572 "镲" */ + 0x40, 0x40, 0x47, 0xfe, 0x74, 0x2, 0x86, 0x0, + 0x83, 0xbe, 0x76, 0xa4, 0x4d, 0x18, 0x41, 0xf8, + 0xf2, 0xc, 0x44, 0x6, 0x4b, 0xfc, 0x40, 0x40, + 0x52, 0x48, 0x24, 0x46, 0x1, 0xc0, + + /* U+9573 "镳" */ + 0x0, 0x0, 0x80, 0x41, 0x1f, 0xfb, 0xa4, 0x88, + 0x7f, 0xf0, 0x92, 0x5d, 0x24, 0xa3, 0xff, 0x45, + 0x11, 0xeb, 0xb9, 0x14, 0x42, 0x2a, 0xa5, 0x99, + 0xcb, 0x0, 0xa, 0x95, 0x5, 0x26, + + /* U+9576 "镶" */ + 0x20, 0x20, 0x27, 0xff, 0x78, 0x0, 0x43, 0xde, + 0x84, 0x52, 0xfb, 0xde, 0x27, 0xfe, 0x21, 0x8, + 0xfb, 0xfe, 0x21, 0x8, 0x27, 0xff, 0x20, 0x62, + 0x29, 0x9c, 0x37, 0x2c, 0x1, 0xc3, + + /* U+957F "长" */ + 0x10, 0x0, 0x20, 0x0, 0x40, 0x60, 0x83, 0x1, + 0x38, 0x2, 0x0, 0x4, 0x0, 0x7f, 0xff, 0x10, + 0x80, 0x21, 0x0, 0x41, 0x0, 0x81, 0x1, 0x1, + 0x2, 0xe1, 0x86, 0x0, 0x80, + + /* U+95E8 "门" */ + 0x40, 0x1, 0x3f, 0xc0, 0x3, 0x0, 0x18, 0x0, + 0xc0, 0x6, 0x0, 0x30, 0x1, 0x80, 0xc, 0x0, + 0x60, 0x3, 0x0, 0x18, 0x0, 0xc0, 0x6, 0x1, + 0xc0, + + /* U+95E9 "闩" */ + 0x40, 0x1, 0x7f, 0xc0, 0x3, 0x0, 0x18, 0x0, + 0xc0, 0x6, 0x0, 0x33, 0xfd, 0x80, 0xc, 0x0, + 0x60, 0x3, 0x0, 0x18, 0x0, 0xc0, 0x6, 0x1, + 0xe0, + + /* U+95EA "闪" */ + 0x40, 0x1, 0x7f, 0xc8, 0x3, 0x4, 0x18, 0x20, + 0xc1, 0x6, 0x8, 0x30, 0xc1, 0x85, 0xc, 0x44, + 0x66, 0x13, 0x60, 0x58, 0x0, 0xc0, 0x6, 0x3, + 0xc0, + + /* U+95EB "闫" */ + 0x40, 0x0, 0x9f, 0xf0, 0x0, 0x60, 0x1, 0x9f, + 0xe6, 0x0, 0x18, 0x0, 0x67, 0xf1, 0x80, 0x6, + 0x0, 0x1b, 0xfe, 0x60, 0x1, 0x80, 0x6, 0x0, + 0x18, 0x7, 0x80, + + /* U+95ED "闭" */ + 0x40, 0x0, 0x9f, 0xf1, 0x0, 0x60, 0x21, 0x80, + 0x86, 0x7f, 0xd8, 0x8, 0x60, 0x61, 0x82, 0x86, + 0x12, 0x18, 0x88, 0x64, 0x21, 0x80, 0x86, 0xe, + 0x18, 0x3, 0xc0, + + /* U+95EE "问" */ + 0x40, 0x0, 0x9f, 0xf1, 0x0, 0x60, 0x1, 0x8f, + 0xc6, 0x21, 0x18, 0x84, 0x62, 0x11, 0x88, 0x46, + 0x21, 0x18, 0xfc, 0x60, 0x1, 0x80, 0x6, 0x0, + 0x18, 0x7, 0x80, + + /* U+95EF "闯" */ + 0x40, 0x0, 0x9f, 0xf0, 0x0, 0x67, 0xf1, 0x80, + 0xc6, 0x22, 0x19, 0x8, 0x63, 0xf9, 0x80, 0x26, + 0x0, 0x9b, 0xf2, 0x60, 0x9, 0x83, 0xc6, 0x0, + 0x18, 0x3, 0xc0, + + /* U+95F0 "闰" */ + 0x40, 0x0, 0x9f, 0xf0, 0x0, 0x60, 0x1, 0x9f, + 0xe6, 0x8, 0x18, 0x20, 0x67, 0xf1, 0x82, 0x6, + 0x8, 0x18, 0x20, 0x6f, 0xf9, 0x80, 0x6, 0x0, + 0x18, 0x7, 0x80, + + /* U+95F1 "闱" */ + 0x40, 0x0, 0x9f, 0xf2, 0x0, 0x60, 0x81, 0xbf, + 0xe6, 0x8, 0x18, 0x20, 0x67, 0xf1, 0x82, 0x6, + 0xff, 0x98, 0x22, 0x60, 0x89, 0x82, 0xc6, 0x8, + 0x18, 0x7, 0x80, + + /* U+95F2 "闲" */ + 0x40, 0x0, 0x9f, 0xf2, 0x0, 0x60, 0x81, 0x82, + 0x6, 0xff, 0x98, 0x20, 0x61, 0x81, 0x87, 0x86, + 0x29, 0x1b, 0x22, 0x68, 0x81, 0x82, 0x6, 0x0, + 0x18, 0x7, 0x80, + + /* U+95F3 "闳" */ + 0x40, 0x0, 0x9f, 0xf1, 0x0, 0x60, 0x81, 0x82, + 0x6, 0x7f, 0xd8, 0x40, 0x62, 0x41, 0x99, 0x6, + 0xc9, 0x1a, 0x42, 0x63, 0xf9, 0x80, 0x16, 0x0, + 0x18, 0x3, 0xc0, + + /* U+95F4 "间" */ + 0x40, 0x0, 0x9f, 0xf1, 0x0, 0x60, 0x1, 0x8f, + 0xc6, 0x21, 0x18, 0x84, 0x63, 0xf1, 0x88, 0x46, + 0x21, 0x18, 0x84, 0x63, 0xf1, 0x80, 0x6, 0x0, + 0x18, 0x3, 0x80, + + /* U+95F5 "闵" */ + 0x40, 0x0, 0x9f, 0xf2, 0x0, 0x60, 0x81, 0x81, + 0x6, 0xff, 0x98, 0x4, 0x62, 0x21, 0x84, 0x86, + 0xc, 0x18, 0x30, 0x63, 0x31, 0xb0, 0x46, 0x0, + 0x18, 0x3, 0x80, + + /* U+95F6 "闶" */ + 0x40, 0x0, 0x9f, 0xf2, 0x0, 0x60, 0x81, 0x81, + 0x6, 0xff, 0x98, 0x0, 0x63, 0xe1, 0x88, 0x86, + 0x22, 0x19, 0x89, 0x6c, 0x19, 0xa0, 0x6, 0x0, + 0x18, 0x3, 0x80, + + /* U+95F7 "闷" */ + 0x40, 0x0, 0x9f, 0xf1, 0x0, 0x60, 0x81, 0x81, + 0x6, 0x4, 0x18, 0x42, 0x65, 0x9, 0x94, 0x16, + 0x91, 0x5a, 0x44, 0x61, 0x11, 0x87, 0x86, 0x0, + 0x18, 0x7, 0x80, + + /* U+95F8 "闸" */ + 0x40, 0x0, 0x9f, 0xf0, 0x0, 0x60, 0x1, 0x9f, + 0xe6, 0x48, 0x99, 0x22, 0x67, 0xf9, 0x92, 0x26, + 0x48, 0x99, 0xfe, 0x60, 0x81, 0x82, 0x6, 0x8, + 0x18, 0x27, 0x80, + + /* U+95F9 "闹" */ + 0x40, 0x0, 0x9f, 0xf0, 0x0, 0x60, 0x41, 0xbf, + 0xf6, 0x8, 0x18, 0x20, 0x67, 0xf9, 0x92, 0x26, + 0x48, 0x99, 0x22, 0x64, 0x99, 0x92, 0x6, 0x0, + 0x18, 0x3, 0xc0, + + /* U+95FA "闺" */ + 0x40, 0x0, 0x9f, 0xf0, 0x20, 0x60, 0x81, 0x9f, + 0xe6, 0x8, 0x1b, 0xff, 0x60, 0x1, 0x82, 0x6, + 0x7f, 0x98, 0x20, 0x60, 0x81, 0xbf, 0xf6, 0x0, + 0x18, 0x3, 0xc0, + + /* U+95FB "闻" */ + 0x40, 0x0, 0x9f, 0xf2, 0x0, 0x6f, 0xf9, 0x88, + 0x46, 0x21, 0x18, 0xfc, 0x62, 0x11, 0x8f, 0xc6, + 0x21, 0x18, 0x84, 0x6f, 0xfd, 0x80, 0x46, 0x0, + 0x18, 0x1, 0xc0, + + /* U+95FC "闼" */ + 0x40, 0x0, 0x9f, 0xf0, 0x0, 0x64, 0x21, 0x88, + 0x86, 0xf, 0x9b, 0x88, 0x62, 0x71, 0x89, 0x66, + 0x28, 0x98, 0x80, 0x65, 0x1, 0xa3, 0xf6, 0x0, + 0x18, 0x3, 0x80, + + /* U+95FD "闽" */ + 0x40, 0x0, 0x9f, 0xf0, 0x20, 0x60, 0x81, 0x9f, + 0xe6, 0x48, 0x99, 0x22, 0x64, 0x89, 0x9f, 0xe6, + 0x9, 0x18, 0x22, 0x6f, 0xf9, 0x80, 0x16, 0x0, + 0x18, 0x3, 0xc0, + + /* U+95FE "闾" */ + 0x40, 0x0, 0x9f, 0xf0, 0x0, 0x63, 0xf1, 0x88, + 0x46, 0x21, 0x18, 0xfc, 0x60, 0x1, 0x80, 0x6, + 0x7f, 0x99, 0x2, 0x64, 0x9, 0x9f, 0xe6, 0x0, + 0x18, 0x3, 0xc0, + + /* U+9600 "阀" */ + 0x40, 0x0, 0x9f, 0xf0, 0x0, 0x62, 0x51, 0x89, + 0x26, 0x44, 0x5b, 0x7e, 0x74, 0x41, 0x91, 0x26, + 0x43, 0x19, 0x19, 0x65, 0xa5, 0x90, 0x66, 0x0, + 0x18, 0x3, 0xc0, + + /* U+9601 "阁" */ + 0x40, 0x0, 0x9f, 0xf2, 0x40, 0x63, 0xf9, 0x98, + 0x46, 0x92, 0x18, 0x30, 0x67, 0x39, 0xa0, 0x16, + 0x7f, 0x19, 0x4, 0x64, 0x11, 0x9f, 0xc6, 0x0, + 0x18, 0x7, 0xc0, + + /* U+9602 "阂" */ + 0x40, 0x0, 0x9f, 0xf0, 0x20, 0x60, 0x41, 0xbf, + 0xf6, 0x10, 0x18, 0x8c, 0x67, 0xe1, 0x82, 0x26, + 0x31, 0x1b, 0x8, 0x60, 0xf1, 0x8c, 0x26, 0x40, + 0x18, 0x3, 0xc0, + + /* U+9603 "阃" */ + 0x40, 0x0, 0x9f, 0xf0, 0x0, 0x67, 0xf9, 0x90, + 0x26, 0x48, 0x99, 0x7a, 0x64, 0x89, 0x97, 0x26, + 0x6a, 0x99, 0x22, 0x64, 0x9, 0x9f, 0xe6, 0x0, + 0x18, 0x3, 0xc0, + + /* U+9604 "阄" */ + 0x40, 0x0, 0x9f, 0xf0, 0x0, 0x63, 0xf1, 0x90, + 0x86, 0x7f, 0x9b, 0x22, 0x64, 0x89, 0x9f, 0xe6, + 0x48, 0x99, 0xfe, 0x60, 0x85, 0x83, 0xf6, 0x0, + 0x18, 0x3, 0xc0, + + /* U+9605 "阅" */ + 0x40, 0x0, 0x9f, 0xf0, 0x0, 0x62, 0x31, 0x85, + 0x6, 0x7f, 0x99, 0x2, 0x64, 0x9, 0x9f, 0xe6, + 0x14, 0x18, 0x52, 0x62, 0x49, 0x91, 0xe6, 0x80, + 0x18, 0x3, 0xc0, + + /* U+9606 "阆" */ + 0x40, 0x0, 0x9f, 0xf0, 0x0, 0x60, 0x81, 0x9f, + 0xe6, 0x40, 0x99, 0xfe, 0x64, 0x9, 0x9f, 0xe6, + 0x48, 0x19, 0x16, 0x64, 0x31, 0x9c, 0x36, 0x0, + 0x18, 0x3, 0xc0, + + /* U+9608 "阈" */ + 0x40, 0x0, 0x9f, 0xf0, 0x4, 0x60, 0x49, 0xbf, + 0xf6, 0x4, 0x1b, 0xea, 0x68, 0xa9, 0xa2, 0xc6, + 0xfb, 0x18, 0x9, 0x60, 0x55, 0xbe, 0x26, 0x0, + 0x18, 0x3, 0x80, + + /* U+9609 "阉" */ + 0x40, 0x0, 0x9f, 0xf0, 0x20, 0x6f, 0xf9, 0x84, + 0x86, 0x69, 0x1b, 0xff, 0x64, 0x91, 0x9f, 0xc6, + 0x49, 0x19, 0xfd, 0x64, 0x85, 0x81, 0xe6, 0x0, + 0x18, 0x3, 0x80, + + /* U+960A "阊" */ + 0x40, 0x0, 0x9f, 0xf0, 0x0, 0x63, 0xf1, 0x90, + 0x46, 0x3f, 0x19, 0x4, 0x63, 0xe1, 0x9f, 0xe6, + 0x40, 0x99, 0xfe, 0x64, 0x9, 0x9f, 0xe6, 0x0, + 0x18, 0x3, 0xc0, + + /* U+960B "阋" */ + 0x40, 0x0, 0x9f, 0xf0, 0x0, 0x67, 0x79, 0x90, + 0x26, 0x40, 0x99, 0xde, 0x64, 0x9, 0x9f, 0xe6, + 0x14, 0x18, 0x50, 0x62, 0x45, 0x99, 0xe6, 0x80, + 0x18, 0x3, 0xc0, + + /* U+960C "阌" */ + 0x40, 0x0, 0x9f, 0xf0, 0x0, 0x67, 0xfd, 0x82, + 0x26, 0x25, 0x1b, 0xff, 0x68, 0x5, 0x9f, 0xe6, + 0x1, 0x18, 0xe8, 0x60, 0xe1, 0x8c, 0x66, 0x40, + 0x18, 0x3, 0xc0, + + /* U+960D "阍" */ + 0x40, 0x0, 0x9f, 0xf0, 0x0, 0x67, 0xf9, 0x91, + 0x6, 0x7f, 0xd9, 0x11, 0x67, 0x35, 0x80, 0x26, + 0x7f, 0x99, 0x2, 0x67, 0xf9, 0x90, 0x26, 0x7f, + 0x98, 0x3, 0x80, + + /* U+960E "阎" */ + 0x40, 0x0, 0x9f, 0xf0, 0x0, 0x61, 0x1, 0x8f, + 0xc6, 0x41, 0x1a, 0x8, 0x67, 0x79, 0x90, 0x26, + 0x7f, 0x99, 0x2, 0x64, 0x9, 0x9f, 0xe6, 0x0, + 0x18, 0x3, 0x80, + + /* U+960F "阏" */ + 0x40, 0x0, 0x9f, 0xf0, 0x0, 0x62, 0x11, 0xbe, + 0xa6, 0x42, 0x99, 0x19, 0x67, 0x41, 0x94, 0xc6, + 0x50, 0x99, 0x40, 0x69, 0x11, 0xec, 0x26, 0x0, + 0x18, 0x3, 0x80, + + /* U+9610 "阐" */ + 0x40, 0x0, 0x9f, 0xf0, 0x0, 0x61, 0x31, 0x9f, + 0xe6, 0x48, 0x99, 0xfe, 0x64, 0x89, 0x92, 0x26, + 0x7f, 0x98, 0x20, 0x6f, 0xfd, 0x82, 0x6, 0x8, + 0x18, 0x3, 0xc0, + + /* U+9611 "阑" */ + 0x40, 0x0, 0x9f, 0xf2, 0x0, 0x6f, 0xf9, 0x82, + 0x6, 0x7f, 0x99, 0x22, 0x65, 0xa9, 0x92, 0x26, + 0x7f, 0x98, 0x20, 0x61, 0xe1, 0x9a, 0x46, 0x88, + 0x18, 0x23, 0x80, + + /* U+9612 "阒" */ + 0x40, 0x0, 0x9f, 0xf0, 0x0, 0x67, 0xf9, 0x90, + 0x26, 0x7f, 0x99, 0xfe, 0x64, 0x9, 0x9f, 0xe6, + 0x9, 0x1b, 0xff, 0x60, 0x81, 0x8d, 0xc6, 0xc0, + 0x98, 0x3, 0xc0, + + /* U+9614 "阔" */ + 0x40, 0x0, 0x9f, 0xf0, 0x0, 0x65, 0xf9, 0x88, + 0x86, 0x2, 0x1a, 0x7f, 0x64, 0x21, 0x80, 0x86, + 0x5f, 0x99, 0x42, 0x69, 0x9, 0xa7, 0xe6, 0x0, + 0x18, 0x3, 0xc0, + + /* U+9615 "阕" */ + 0x40, 0x0, 0x9f, 0xf2, 0x0, 0x6f, 0x49, 0x84, + 0xc6, 0xa2, 0xd9, 0x84, 0x65, 0xe9, 0xa2, 0x1e, + 0x7f, 0x98, 0x20, 0x61, 0x61, 0x98, 0x66, 0x80, + 0x18, 0x3, 0xc0, + + /* U+9616 "阖" */ + 0x40, 0x0, 0x9f, 0xf0, 0x20, 0x67, 0xf9, 0x82, + 0x6, 0xff, 0xd8, 0x48, 0x66, 0x11, 0x87, 0xa6, + 0x7f, 0x99, 0x4a, 0x65, 0x29, 0xbf, 0xf6, 0x0, + 0x18, 0x3, 0xc0, + + /* U+9617 "阗" */ + 0x40, 0x0, 0x9f, 0xf0, 0x0, 0x6f, 0xfd, 0x82, + 0x6, 0x7f, 0x19, 0x4, 0x67, 0xf1, 0x9f, 0xc6, + 0x41, 0x19, 0xfc, 0x6f, 0xfd, 0x84, 0x86, 0x61, + 0x98, 0x3, 0x80, + + /* U+9619 "阙" */ + 0x40, 0x0, 0x9f, 0xf0, 0x0, 0x64, 0x91, 0x94, + 0x86, 0xfb, 0xda, 0xb5, 0x6a, 0x95, 0xaa, 0x46, + 0xf9, 0x18, 0x8a, 0x64, 0x25, 0x91, 0x16, 0x80, + 0x18, 0x3, 0xc0, + + /* U+961A "阚" */ + 0x40, 0x0, 0x9f, 0xf0, 0x0, 0x67, 0x91, 0x84, + 0x46, 0xfd, 0xf9, 0x2a, 0x67, 0xc9, 0x92, 0x26, + 0x79, 0x99, 0x22, 0x67, 0xd9, 0xa2, 0x96, 0x8, + 0x18, 0x3, 0xc0, + + /* U+961C "阜" */ + 0x0, 0x0, 0x8, 0x0, 0xff, 0xe1, 0x0, 0x42, + 0x0, 0x87, 0xff, 0x8, 0x0, 0x1f, 0xfc, 0x20, + 0x8, 0x40, 0x10, 0xff, 0xe0, 0x8, 0xf, 0xff, + 0xe0, 0x20, 0x0, 0x40, 0x0, 0x80, + + /* U+961D "阝" */ + 0xfe, 0x38, 0xa2, 0x92, 0x48, 0xa3, 0x86, 0x18, + 0x6e, 0x82, 0x0, + + /* U+961F "队" */ + 0x0, 0x83, 0xe2, 0x8, 0x88, 0x24, 0x20, 0x90, + 0x82, 0x42, 0x9, 0x8, 0x24, 0x50, 0x89, 0x42, + 0x25, 0x8, 0x92, 0x2c, 0x84, 0x84, 0x1a, 0x30, + 0x38, 0x80, 0x40, + + /* U+9621 "阡" */ + 0xf8, 0x6, 0x3f, 0xf8, 0x84, 0x24, 0x10, 0x90, + 0x42, 0x41, 0x9, 0x4, 0x25, 0xff, 0x88, 0x42, + 0x21, 0x8, 0x84, 0x2c, 0x10, 0x80, 0x42, 0x1, + 0x8, 0x4, 0x0, + + /* U+9622 "阢" */ + 0xff, 0xfd, 0x22, 0x42, 0x44, 0x84, 0x89, 0xa, + 0x12, 0x12, 0x24, 0x24, 0x48, 0x44, 0x90, 0x89, + 0x21, 0x12, 0x46, 0xc8, 0x8c, 0x11, 0x18, 0x42, + 0x31, 0x7, 0x80, + + /* U+962A "阪" */ + 0xf8, 0x5, 0x17, 0xfa, 0x68, 0x4, 0x90, 0x9, + 0x20, 0x12, 0x7f, 0xa8, 0xa2, 0x49, 0x44, 0x8a, + 0x49, 0x14, 0xa2, 0x28, 0xc5, 0xa1, 0x88, 0x45, + 0x90, 0xb1, 0xa2, 0x80, 0x0, + + /* U+962E "阮" */ + 0xf0, 0x1, 0x27, 0xfa, 0x40, 0x4, 0x80, 0x9, + 0x0, 0x12, 0x0, 0x29, 0xff, 0xc8, 0x90, 0x89, + 0x21, 0x12, 0x42, 0x24, 0x85, 0x89, 0x18, 0x22, + 0x30, 0x83, 0xa2, 0x0, 0x0, + + /* U+9631 "阱" */ + 0x1, 0x9, 0xe2, 0x12, 0x44, 0x25, 0xbf, 0xfa, + 0x10, 0x94, 0x21, 0x28, 0x42, 0x50, 0x84, 0x97, + 0xff, 0x22, 0x12, 0x44, 0x27, 0x88, 0x48, 0x20, + 0x90, 0xc1, 0x23, 0x2, 0x0, + + /* U+9632 "防" */ + 0x0, 0x43, 0xe1, 0x9, 0x0, 0x25, 0xff, 0x91, + 0x2, 0x44, 0xa, 0x10, 0x24, 0x7e, 0x91, 0xa, + 0x24, 0x28, 0x90, 0xa2, 0xc2, 0xb2, 0xa, 0x8, + 0x28, 0x40, 0xa2, 0x1c, + + /* U+9633 "阳" */ + 0xfb, 0xfe, 0x28, 0x19, 0x20, 0x64, 0x81, 0x92, + 0x6, 0x48, 0x19, 0x3f, 0xe2, 0x81, 0x8a, 0x6, + 0x28, 0x1d, 0xa0, 0x68, 0x81, 0x83, 0xfe, 0x8, + 0x10, + + /* U+9634 "阴" */ + 0xfb, 0xfe, 0x28, 0x19, 0x20, 0x64, 0x81, 0x93, + 0xfe, 0x88, 0x19, 0x20, 0x62, 0x81, 0x8b, 0xfe, + 0x28, 0x1b, 0x20, 0x61, 0x81, 0x84, 0x6, 0x20, + 0xf0, + + /* U+9635 "阵" */ + 0x0, 0x81, 0xe1, 0x2, 0x5f, 0xfd, 0x8, 0xa, + 0x12, 0x14, 0x44, 0x28, 0x88, 0x51, 0xfe, 0x90, + 0x21, 0x20, 0x42, 0x40, 0x84, 0xbf, 0xfe, 0x2, + 0x10, 0x4, 0x20, 0x8, 0x0, + + /* U+9636 "阶" */ + 0x0, 0x43, 0xe1, 0x8, 0x8a, 0x26, 0x2c, 0x91, + 0x1a, 0x4c, 0x39, 0x20, 0x29, 0x0, 0x91, 0x12, + 0x24, 0x48, 0x91, 0x22, 0x44, 0xb1, 0x12, 0x8, + 0x48, 0x21, 0x21, 0x4, + + /* U+963B "阻" */ + 0xf3, 0xf9, 0x24, 0x12, 0x48, 0x24, 0x90, 0x4a, + 0x3f, 0x92, 0x41, 0x24, 0x82, 0x45, 0x4, 0x8b, + 0xf9, 0x14, 0x12, 0xc8, 0x24, 0x10, 0x48, 0xff, + 0xf0, 0x0, 0x0, + + /* U+963C "阼" */ + 0x1, 0x1, 0xf2, 0x2, 0x24, 0x4, 0x9f, 0xf9, + 0x28, 0x12, 0x90, 0x29, 0x20, 0x48, 0x7e, 0x90, + 0x81, 0x11, 0x2, 0x22, 0x4, 0x47, 0xeb, 0x8, + 0x10, 0x10, 0x20, 0x20, 0x40, 0x40, + + /* U+963D "阽" */ + 0x0, 0x41, 0xf0, 0x82, 0x21, 0x4, 0x83, 0xf9, + 0x4, 0x12, 0x8, 0x28, 0x10, 0x48, 0x20, 0x8b, + 0xfd, 0x14, 0xa, 0x28, 0x17, 0xd0, 0x28, 0x20, + 0x50, 0x7f, 0xa0, 0x81, 0x0, + + /* U+963F "阿" */ + 0xf7, 0xfe, 0x40, 0x2b, 0x0, 0xa8, 0xf2, 0xa2, + 0x4a, 0x89, 0x2a, 0x24, 0xa4, 0x92, 0x92, 0x4a, + 0x46, 0x2e, 0x0, 0xa0, 0x2, 0x80, 0xa, 0x1, + 0xc0, + + /* U+9640 "陀" */ + 0x0, 0x43, 0xe1, 0x9, 0x7f, 0xe5, 0x1, 0x94, + 0x6, 0x80, 0xa, 0x20, 0xa4, 0x84, 0x92, 0x62, + 0x2e, 0x8, 0xa0, 0x22, 0x81, 0xb2, 0x6, 0x8, + 0x28, 0x1f, 0x80, + + /* U+9642 "陂" */ + 0x0, 0x21, 0xe0, 0x42, 0x4f, 0xfd, 0x11, 0x1a, + 0x22, 0x34, 0x44, 0x28, 0xff, 0x49, 0x42, 0x92, + 0x85, 0x24, 0x92, 0x49, 0xc7, 0xa1, 0x8, 0x45, + 0x91, 0x31, 0xa4, 0x80, 0x80, + + /* U+9644 "附" */ + 0x1, 0x9, 0xe2, 0x12, 0x48, 0x25, 0x17, 0xfa, + 0x60, 0x99, 0x41, 0x28, 0x92, 0x49, 0x24, 0x92, + 0x49, 0x24, 0x12, 0x48, 0x27, 0x10, 0x48, 0x20, + 0x90, 0x41, 0x20, 0x9c, 0x0, + + /* U+9645 "际" */ + 0xf3, 0xfd, 0x20, 0x2, 0x40, 0x4, 0x80, 0xa, + 0x7f, 0xd2, 0x8, 0x24, 0x10, 0x45, 0x20, 0x8a, + 0x49, 0x14, 0x92, 0xd1, 0x14, 0x22, 0x28, 0x4, + 0x10, 0x78, 0x0, + + /* U+9646 "陆" */ + 0x0, 0x43, 0xc1, 0x9, 0x4, 0x2d, 0xff, 0xa0, + 0x42, 0x81, 0xa, 0xff, 0xe8, 0x10, 0x90, 0x42, + 0x51, 0x19, 0x44, 0x7d, 0x11, 0x84, 0x46, 0x1f, + 0xf8, 0x0, 0x40, + + /* U+9647 "陇" */ + 0x1, 0x3, 0xc4, 0x89, 0x11, 0x25, 0xff, 0x91, + 0x42, 0x85, 0x9, 0x14, 0xa4, 0x54, 0x89, 0x52, + 0x29, 0x88, 0xa4, 0x6c, 0xb1, 0x85, 0x46, 0x11, + 0x18, 0x87, 0x80, + + /* U+9648 "陈" */ + 0x0, 0x81, 0xe2, 0x2, 0x5f, 0xf4, 0x90, 0x9, + 0x24, 0x14, 0x48, 0x25, 0x10, 0x4b, 0xfe, 0x88, + 0x41, 0x10, 0x82, 0x29, 0x25, 0xa2, 0x28, 0x44, + 0x50, 0x8, 0x20, 0xf0, 0x0, + + /* U+9649 "陉" */ + 0xfb, 0xf9, 0x10, 0x12, 0x40, 0x44, 0x83, 0xa, + 0x9, 0x12, 0xe1, 0xa4, 0x0, 0x45, 0xfe, 0x88, + 0x41, 0x10, 0x82, 0xc1, 0x4, 0x2, 0x8, 0x7f, + 0xf0, 0x0, 0x0, + + /* U+964B "陋" */ + 0xf3, 0xfd, 0x20, 0x42, 0xa0, 0x85, 0x4f, 0xca, + 0xa4, 0x95, 0x49, 0x26, 0x9a, 0x4d, 0x2c, 0x9a, + 0x89, 0xf4, 0x12, 0x28, 0xe4, 0x40, 0x8, 0x7f, + 0xf0, 0x0, 0x0, + + /* U+964C "陌" */ + 0xf3, 0xff, 0x20, 0x82, 0x41, 0x4, 0x9f, 0xea, + 0x20, 0x52, 0x40, 0xa4, 0x81, 0x45, 0xfe, 0x8a, + 0x5, 0x14, 0xa, 0xe8, 0x14, 0x10, 0x28, 0x3f, + 0xd0, 0x40, 0x80, + + /* U+964D "降" */ + 0x0, 0x1, 0xe2, 0x2, 0x47, 0xe4, 0x98, 0x4a, + 0x51, 0x14, 0x14, 0x28, 0x38, 0x53, 0x8e, 0xa8, + 0x41, 0x2f, 0xfa, 0x41, 0x4, 0xa2, 0xf, 0x7f, + 0xd0, 0x8, 0x20, 0x10, 0x40, 0x20, + + /* U+9650 "限" */ + 0xfb, 0xf9, 0x14, 0x12, 0x48, 0x24, 0x9f, 0xca, + 0x20, 0x92, 0x41, 0x24, 0xfe, 0x45, 0x40, 0x8a, + 0x4d, 0x14, 0xe2, 0xc8, 0x84, 0x14, 0x88, 0x30, + 0xd0, 0x40, 0x0, + + /* U+9654 "陔" */ + 0x0, 0x21, 0xe0, 0x42, 0x4f, 0xfc, 0x82, 0x9, + 0x8, 0x94, 0x23, 0x24, 0xfc, 0x44, 0x12, 0x88, + 0x45, 0x13, 0x12, 0x38, 0x45, 0x81, 0x88, 0x4, + 0x90, 0x30, 0xe0, 0x80, 0x80, + + /* U+9655 "陕" */ + 0x0, 0x41, 0xe0, 0x82, 0x4f, 0xf4, 0x82, 0x9, + 0x24, 0x54, 0x49, 0x24, 0x10, 0x48, 0x20, 0x8f, + 0xff, 0x10, 0x82, 0x22, 0x85, 0x85, 0x8, 0x11, + 0x10, 0xc1, 0xa1, 0x1, 0x0, + + /* U+965B "陛" */ + 0x4, 0x3, 0xd1, 0x9, 0x44, 0xa9, 0xec, 0xa4, + 0x42, 0x91, 0x1a, 0x5c, 0x69, 0x8e, 0x90, 0x42, + 0x41, 0x9, 0x7f, 0xb8, 0x10, 0x80, 0x42, 0x3f, + 0xf8, 0x0, 0x0, + + /* U+965F "陟" */ + 0x0, 0x41, 0xe0, 0x82, 0x49, 0xf5, 0x12, 0xa, + 0x24, 0x14, 0x48, 0x33, 0xff, 0xd0, 0x20, 0x90, + 0x45, 0x24, 0x92, 0x51, 0x67, 0x1, 0x88, 0x4, + 0x10, 0x60, 0x23, 0x0, 0x0, + + /* U+9661 "陡" */ + 0x0, 0x41, 0xf0, 0x82, 0x2f, 0xf4, 0x82, 0x9, + 0x4, 0x14, 0x8, 0x25, 0xff, 0xc8, 0x0, 0x8a, + 0x41, 0x14, 0xfa, 0x29, 0x5, 0x92, 0x8, 0x54, + 0x10, 0x9f, 0xe0, 0x0, 0x0, + + /* U+9662 "院" */ + 0x0, 0x1, 0xe0, 0x82, 0x5f, 0xf5, 0x20, 0x2a, + 0x0, 0x14, 0x7f, 0x28, 0x0, 0x50, 0x0, 0x97, + 0xfd, 0x22, 0x42, 0x44, 0x87, 0x89, 0x18, 0x22, + 0x30, 0x84, 0xa6, 0xf, 0x0, + + /* U+9664 "除" */ + 0x0, 0x41, 0xf1, 0x82, 0x42, 0x84, 0x98, 0x8a, + 0x60, 0xd5, 0xfe, 0x28, 0x10, 0x50, 0x20, 0x97, + 0xfd, 0x20, 0x82, 0x49, 0x47, 0xb2, 0x48, 0x44, + 0x51, 0x8, 0x20, 0x70, 0x0, + + /* U+9667 "陧" */ + 0xf7, 0xfa, 0x50, 0x2a, 0x7f, 0xa9, 0x2, 0xa4, + 0xa, 0x9f, 0xe9, 0x0, 0x24, 0x20, 0x90, 0x82, + 0x5f, 0xee, 0x8, 0x20, 0x20, 0x8f, 0xfe, 0x0, + 0x0, + + /* U+9668 "陨" */ + 0xf1, 0xfa, 0x48, 0x29, 0x20, 0xa8, 0x82, 0xa1, + 0xfa, 0x80, 0xa, 0x3f, 0xe8, 0x81, 0x92, 0x46, + 0x49, 0x19, 0x24, 0x7c, 0x99, 0x80, 0x92, 0xc, + 0x38, 0xc0, 0x40, + + /* U+9669 "险" */ + 0x0, 0x41, 0xe1, 0xc2, 0x46, 0xc5, 0x18, 0xca, + 0xc0, 0x75, 0x7f, 0x28, 0x0, 0x48, 0x0, 0x94, + 0x49, 0x24, 0x92, 0x49, 0x27, 0x12, 0x88, 0x1, + 0x11, 0xff, 0xe0, 0x0, 0x0, + + /* U+966A "陪" */ + 0x0, 0x41, 0xe0, 0x82, 0x5f, 0xf5, 0x0, 0xa, + 0x11, 0x18, 0x4, 0x2b, 0xff, 0xd8, 0x0, 0x90, + 0x1, 0x2f, 0xf2, 0x50, 0x27, 0x20, 0x48, 0x40, + 0x90, 0xff, 0x21, 0x2, 0x0, + + /* U+966C "陬" */ + 0xf7, 0x81, 0x29, 0xfa, 0x92, 0x95, 0x3d, 0x2a, + 0x4a, 0x54, 0x94, 0xa5, 0x29, 0x4b, 0xd4, 0x94, + 0x99, 0x29, 0x33, 0x93, 0x64, 0x7c, 0xc8, 0xa, + 0x50, 0x18, 0x0, + + /* U+9672 "陲" */ + 0xf7, 0xfd, 0x20, 0x82, 0x9f, 0xf5, 0x12, 0x4a, + 0x24, 0x94, 0x49, 0x27, 0xff, 0xc9, 0x24, 0x92, + 0x49, 0x2f, 0xfb, 0x81, 0x4, 0x2, 0x8, 0x7f, + 0xd0, 0x0, 0x0, + + /* U+9674 "陴" */ + 0x0, 0x81, 0xe1, 0x2, 0x5f, 0xf4, 0xa2, 0x2a, + 0x44, 0x54, 0xff, 0xa9, 0x11, 0x52, 0x22, 0x94, + 0x45, 0x2f, 0xfa, 0x44, 0x4, 0x91, 0xe, 0xff, + 0xf0, 0x4, 0x20, 0x8, 0x40, 0x10, + + /* U+9675 "陵" */ + 0x0, 0x41, 0xe0, 0x83, 0xc1, 0x4, 0x9f, 0xea, + 0x4, 0x14, 0xff, 0xe8, 0x0, 0x51, 0x8c, 0x94, + 0x85, 0x23, 0xf2, 0x4c, 0x24, 0xb8, 0x8e, 0x4a, + 0x10, 0xc, 0x20, 0x6e, 0x47, 0x2, + + /* U+9676 "陶" */ + 0x2, 0x3, 0xc8, 0x9, 0x7f, 0xeb, 0x41, 0xab, + 0xe6, 0x92, 0x1a, 0x8, 0x67, 0xfd, 0x90, 0x86, + 0x52, 0x99, 0x4a, 0x79, 0xf9, 0x80, 0x6, 0x0, + 0x38, 0xf, 0x80, + + /* U+9677 "陷" */ + 0x0, 0x83, 0xc4, 0x9, 0x1f, 0xa8, 0x82, 0xa4, + 0x13, 0x0, 0x8a, 0x73, 0xe5, 0x1, 0x94, 0x6, + 0x50, 0x19, 0x7b, 0xf9, 0x1, 0x84, 0x6, 0x1f, + 0xf8, 0x40, 0x40, + + /* U+9685 "隅" */ + 0xf7, 0xfd, 0x28, 0x8a, 0x9f, 0xf5, 0x22, 0x2a, + 0x44, 0x54, 0xff, 0xa8, 0x10, 0x4b, 0xff, 0x98, + 0x43, 0x30, 0xa7, 0xef, 0xec, 0x40, 0x58, 0x80, + 0x31, 0x3, 0x80, + + /* U+9686 "隆" */ + 0x0, 0x0, 0x4, 0xf, 0x3f, 0xa4, 0xc2, 0x94, + 0xb2, 0x83, 0x8a, 0x71, 0xe8, 0xfc, 0x90, 0x2, + 0x49, 0x9, 0x3f, 0xa5, 0x10, 0xa3, 0xfa, 0x1, + 0x8, 0x4, 0x23, 0xff, + + /* U+9688 "隈" */ + 0xf7, 0xfe, 0x51, 0x1a, 0x7f, 0xe9, 0x11, 0xa4, + 0x46, 0x9f, 0xfa, 0x0, 0x27, 0xff, 0x94, 0x86, + 0x51, 0x2e, 0x47, 0x21, 0xc, 0x85, 0x8a, 0x18, + 0x10, + + /* U+968B "隋" */ + 0x0, 0x1, 0xe1, 0x2, 0x5f, 0xfc, 0x88, 0xa, + 0x3f, 0xd4, 0x84, 0x2a, 0xff, 0xd0, 0x0, 0x93, + 0xfd, 0x28, 0xa, 0x4f, 0xf5, 0xa0, 0x28, 0x3f, + 0xd0, 0x80, 0xa1, 0x1, 0x42, 0x1e, + + /* U+968D "隍" */ + 0x0, 0x1, 0xe1, 0x2, 0x5f, 0xf4, 0xa0, 0x2a, + 0x40, 0x54, 0xff, 0xa9, 0x1, 0x53, 0xfe, 0xa0, + 0x1, 0x2f, 0xfe, 0x41, 0x4, 0x82, 0xb, 0x7f, + 0xd0, 0x8, 0x23, 0xff, 0xc0, 0x0, + + /* U+968F "随" */ + 0x0, 0x21, 0xe8, 0x82, 0x57, 0xf5, 0x4, 0xa, + 0xf, 0x95, 0xb1, 0x29, 0xbe, 0x4a, 0x44, 0x94, + 0x89, 0x29, 0xf2, 0x52, 0x27, 0x24, 0x48, 0x4b, + 0x91, 0x40, 0x24, 0x7f, 0x0, + + /* U+9690 "隐" */ + 0x1, 0x1, 0xf2, 0x2, 0x4f, 0xc4, 0x90, 0x8a, + 0x42, 0x15, 0x7f, 0xa8, 0x1, 0x51, 0xfe, 0x90, + 0x5, 0x20, 0xa, 0x4f, 0xe7, 0x12, 0x28, 0xa4, + 0x51, 0x42, 0x64, 0x84, 0x40, 0xf8, + + /* U+9694 "隔" */ + 0xf7, 0xfe, 0x40, 0xa, 0x3f, 0xa8, 0x82, 0xa3, + 0xfa, 0x80, 0x9, 0x7f, 0xe5, 0x41, 0x95, 0x26, + 0x50, 0x9e, 0x5f, 0x61, 0x11, 0x84, 0x46, 0x11, + 0x60, + + /* U+9697 "隗" */ + 0x0, 0x41, 0xe1, 0x2, 0x5f, 0xf4, 0xa2, 0x2a, + 0x44, 0x54, 0xff, 0xa9, 0x11, 0x52, 0x22, 0x97, + 0xfd, 0x21, 0x2, 0x43, 0x4, 0x8e, 0x8f, 0x17, + 0xf0, 0x48, 0x63, 0x10, 0xc4, 0x1e, + + /* U+9698 "隘" */ + 0x2, 0x3, 0xcc, 0x49, 0x11, 0x24, 0x28, 0xaf, + 0xfe, 0x80, 0xa, 0x11, 0x29, 0x82, 0x98, 0x6, + 0x5f, 0xe9, 0x4a, 0xa5, 0x2a, 0xe4, 0xaa, 0x12, + 0xa8, 0xff, 0xe0, 0x0, + + /* U+9699 "隙" */ + 0xf0, 0x41, 0x26, 0xa2, 0xd9, 0x35, 0x42, 0x2a, + 0x7f, 0x94, 0x81, 0x29, 0x2, 0x53, 0xfc, 0x94, + 0x9, 0x2f, 0xf2, 0x41, 0x7, 0x92, 0x88, 0x44, + 0x93, 0x8, 0xa0, 0x70, 0x0, + + /* U+969C "障" */ + 0x0, 0x43, 0xc1, 0x9, 0x7f, 0xe4, 0x84, 0xa1, + 0x22, 0xbf, 0xfa, 0x0, 0x29, 0xfe, 0x94, 0xa, + 0x5f, 0xe9, 0x40, 0xa5, 0xfe, 0xe0, 0x42, 0x3f, + 0xf8, 0x4, 0x20, 0x10, + + /* U+96A7 "隧" */ + 0x0, 0x3, 0xe2, 0x49, 0x45, 0x29, 0x7f, 0xa0, + 0x42, 0xb7, 0x2a, 0x47, 0x25, 0x28, 0x95, 0x32, + 0x51, 0xa9, 0x5a, 0x7d, 0x8, 0x84, 0xe2, 0x28, + 0x9, 0x1f, 0xc0, + + /* U+96B0 "隰" */ + 0xf7, 0xfd, 0x28, 0xa, 0x5f, 0xf5, 0x20, 0x2a, + 0x7f, 0xd4, 0x22, 0x29, 0xa9, 0x4f, 0x9c, 0x92, + 0x11, 0x29, 0x4b, 0xde, 0x6c, 0x0, 0x28, 0x92, + 0x51, 0x0, 0x40, + + /* U+96B3 "隳" */ + 0x0, 0x40, 0xf1, 0x1, 0x2d, 0xfa, 0x9f, 0xe5, + 0x62, 0xa, 0xff, 0xd2, 0xff, 0x25, 0xfe, 0x4a, + 0x4, 0xa7, 0xf9, 0x6, 0x70, 0x73, 0xf, 0x11, + 0xe3, 0x29, 0x4, 0x52, 0x3, 0x0, + + /* U+96B6 "隶" */ + 0x1, 0x0, 0x7f, 0xf0, 0x4, 0x20, 0x8, 0x4f, + 0xff, 0xe0, 0x21, 0x0, 0x42, 0x1f, 0xfc, 0x1, + 0x0, 0x63, 0x10, 0x25, 0xc0, 0x79, 0x7, 0x11, + 0x90, 0x20, 0xc1, 0xc0, 0x0, + + /* U+96B9 "隹" */ + 0x8, 0x0, 0x11, 0x0, 0x42, 0x1, 0xff, 0xf2, + 0x8, 0xc, 0x10, 0x28, 0x20, 0x1f, 0xfe, 0x20, + 0x80, 0x41, 0x0, 0xff, 0xf1, 0x4, 0x2, 0x8, + 0x4, 0x10, 0xf, 0xff, 0x90, 0x0, + + /* U+96BC "隼" */ + 0x11, 0x0, 0x42, 0x3, 0xff, 0xd8, 0x40, 0xbf, + 0xf8, 0x84, 0x3, 0xff, 0x88, 0x40, 0x3f, 0xfc, + 0x80, 0x0, 0x10, 0x3f, 0xff, 0x1, 0x0, 0x4, + 0x0, 0x10, 0x0, + + /* U+96BD "隽" */ + 0x10, 0x80, 0x41, 0x0, 0xff, 0xfb, 0x4, 0xf, + 0xff, 0xd4, 0x10, 0xf, 0xff, 0x10, 0x40, 0x3f, + 0xfe, 0x0, 0x0, 0xff, 0x80, 0x41, 0xe1, 0x80, + 0x46, 0x0, 0xb0, 0x1f, 0x0, + + /* U+96BE "难" */ + 0x0, 0xa0, 0x2, 0x23, 0xe4, 0x0, 0x5f, 0xf8, + 0xa1, 0x12, 0xc2, 0x16, 0xff, 0x19, 0x8, 0x12, + 0x10, 0x67, 0xf8, 0xc8, 0x42, 0x50, 0x84, 0xa1, + 0x10, 0x7f, 0xc0, 0x80, 0x0, + + /* U+96C0 "雀" */ + 0x1, 0x0, 0x32, 0x60, 0x84, 0xf6, 0x6, 0x0, + 0x78, 0xf, 0x8, 0x2f, 0xff, 0x10, 0x40, 0x20, + 0x80, 0x7f, 0xf8, 0x82, 0x1, 0xff, 0xe2, 0x8, + 0x7, 0xff, 0xc8, 0x0, 0x0, + + /* U+96C1 "雁" */ + 0x3f, 0xff, 0x22, 0x40, 0x22, 0x48, 0x24, 0xff, + 0x24, 0x88, 0x2d, 0x88, 0x36, 0xff, 0x24, 0x88, + 0x24, 0x88, 0x24, 0xff, 0x24, 0x88, 0x44, 0x88, + 0x44, 0xff, 0x84, 0x80, + + /* U+96C4 "雄" */ + 0x10, 0x80, 0x41, 0x20, 0x84, 0x7, 0xef, 0xf2, + 0x11, 0x4, 0x62, 0x11, 0x44, 0x28, 0xff, 0x91, + 0x11, 0x22, 0x20, 0xa7, 0xf9, 0x48, 0x86, 0x51, + 0xb, 0xa2, 0x9, 0x7f, 0x80, 0x80, + + /* U+96C5 "雅" */ + 0x0, 0xa0, 0xf9, 0x20, 0x24, 0x60, 0x4f, 0xf4, + 0xb1, 0x9, 0x22, 0x1f, 0x7f, 0x84, 0x88, 0x19, + 0x10, 0x52, 0x20, 0xa7, 0xfa, 0x48, 0x80, 0x91, + 0x1, 0x3f, 0xcc, 0x40, 0x0, + + /* U+96C6 "集" */ + 0x11, 0x0, 0x21, 0x0, 0xff, 0xf3, 0x8, 0xb, + 0xff, 0x84, 0x20, 0xf, 0xfe, 0x10, 0x80, 0x3f, + 0xfc, 0x40, 0x3, 0xff, 0xf8, 0x2c, 0x1, 0x96, + 0xc, 0x23, 0xe0, 0x40, 0x0, + + /* U+96C7 "雇" */ + 0x0, 0x80, 0x0, 0x80, 0x3f, 0xfe, 0x20, 0x2, + 0x20, 0x2, 0x3f, 0xfe, 0x22, 0x20, 0x23, 0xfe, + 0x24, 0x20, 0x2c, 0x20, 0x37, 0xfe, 0x24, 0x20, + 0x27, 0xfe, 0x44, 0x20, 0x47, 0xff, 0x4, 0x0, + + /* U+96C9 "雉" */ + 0x20, 0xa0, 0x40, 0x90, 0x7c, 0x80, 0x51, 0xfe, + 0x93, 0x10, 0x13, 0x10, 0x15, 0xfe, 0xfd, 0x10, + 0x21, 0x10, 0x39, 0xfe, 0x29, 0x10, 0x25, 0x10, + 0x45, 0x10, 0x41, 0xff, 0x81, 0x0, + + /* U+96CC "雌" */ + 0x14, 0x40, 0x28, 0x90, 0x52, 0x2, 0xa7, 0xf5, + 0xe8, 0x8a, 0xb1, 0x15, 0x62, 0x2a, 0x7f, 0x54, + 0x88, 0xa9, 0x11, 0x53, 0xfa, 0xa4, 0x45, 0x48, + 0x8e, 0xb1, 0x31, 0xbf, 0x80, 0x40, + + /* U+96CD "雍" */ + 0x0, 0x0, 0x4, 0xf, 0xff, 0xc4, 0x8, 0x21, + 0x21, 0x4, 0x4, 0xaf, 0xfc, 0x88, 0x26, 0x21, + 0x8f, 0xfc, 0xa2, 0xe, 0xff, 0x12, 0x20, 0xc8, + 0x86, 0x3f, 0xe0, 0x80, + + /* U+96CE "雎" */ + 0x0, 0x80, 0x1, 0x21, 0xf4, 0x42, 0x2f, 0xf4, + 0x51, 0x8, 0xa2, 0x1f, 0xc4, 0x22, 0xff, 0x45, + 0x10, 0xfa, 0x21, 0x17, 0xfa, 0x28, 0x84, 0x51, + 0x9, 0xa2, 0x2c, 0x7f, 0x80, 0x80, + + /* U+96CF "雏" */ + 0x0, 0x40, 0x20, 0x48, 0x3c, 0x80, 0x48, 0xff, + 0x48, 0x88, 0x11, 0x88, 0x7e, 0x88, 0x4, 0xff, + 0x4, 0x88, 0x4, 0x88, 0x3c, 0xff, 0x4, 0x88, + 0x4, 0x88, 0x4, 0x88, 0x7c, 0xff, 0x4, 0x80, + + /* U+96D2 "雒" */ + 0x10, 0x40, 0x20, 0x50, 0x3c, 0x80, 0x44, 0xff, + 0x89, 0x90, 0xa9, 0x90, 0x12, 0x90, 0x2c, 0xfe, + 0xc2, 0x90, 0x0, 0x90, 0x7c, 0xfe, 0x44, 0x90, + 0x44, 0x90, 0x44, 0x90, 0x7c, 0xff, 0x44, 0x80, + + /* U+96D5 "雕" */ + 0x0, 0x28, 0x7f, 0x28, 0x49, 0x40, 0x5d, 0x7e, + 0x49, 0x48, 0x49, 0xc8, 0x5d, 0xfe, 0x41, 0x48, + 0x5d, 0x48, 0x65, 0x48, 0x65, 0x7e, 0x65, 0x48, + 0x59, 0x48, 0x41, 0x7e, 0x87, 0x40, + + /* U+96E0 "雠" */ + 0x20, 0x10, 0x29, 0x28, 0x49, 0x24, 0x7c, 0x3f, + 0xc8, 0x68, 0xcb, 0x68, 0x7d, 0x3f, 0x49, 0x28, + 0x49, 0x28, 0x7d, 0x3f, 0x49, 0xa8, 0x49, 0xa8, + 0x49, 0x28, 0x7c, 0x3f, 0x40, 0x20, + + /* U+96E8 "雨" */ + 0xff, 0xfe, 0x2, 0x0, 0x4, 0x3, 0xff, 0xe4, + 0x10, 0x48, 0x20, 0x96, 0x59, 0x22, 0x8a, 0x41, + 0x4, 0xa2, 0x89, 0x34, 0xd2, 0x8, 0x24, 0x10, + 0x48, 0x27, 0x80, + + /* U+96E9 "雩" */ + 0x7f, 0xfc, 0x2, 0x3, 0xff, 0xfc, 0x8, 0x19, + 0xd7, 0x23, 0xae, 0x0, 0x40, 0x1f, 0xfc, 0x0, + 0x1, 0xff, 0xfc, 0x40, 0x0, 0xff, 0xc0, 0x0, + 0x80, 0x1, 0x0, 0x7c, 0x0, + + /* U+96EA "雪" */ + 0x7f, 0xf8, 0x4, 0xf, 0xff, 0xe0, 0x41, 0xb9, + 0x74, 0x4, 0x3, 0x97, 0x0, 0x40, 0x0, 0x1, + 0xff, 0xe0, 0x0, 0x9f, 0xfe, 0x0, 0x9, 0xff, + 0xe0, 0x0, 0x80, + + /* U+96EF "雯" */ + 0x7f, 0xfc, 0x2, 0x3, 0xff, 0xfc, 0x8, 0x1b, + 0xd7, 0xb0, 0x20, 0x4f, 0x1e, 0x0, 0x80, 0x1, + 0x1, 0xff, 0xfc, 0x40, 0x40, 0x63, 0x0, 0x68, + 0x0, 0x78, 0x1f, 0xf, 0xc0, 0x0, + + /* U+96F3 "雳" */ + 0x3f, 0xfc, 0x2, 0x1, 0xff, 0xfa, 0x8, 0x15, + 0xf3, 0xa0, 0x20, 0x3, 0x4, 0x1f, 0xff, 0x20, + 0x0, 0x41, 0x0, 0xbf, 0xf1, 0x8, 0x22, 0x10, + 0x48, 0xc0, 0xb7, 0x1e, 0x0, + + /* U+96F6 "零" */ + 0x3f, 0xfc, 0x1, 0x0, 0x7f, 0xfe, 0x41, 0x2, + 0x5d, 0x3a, 0x0, 0x0, 0x1f, 0x38, 0x6, 0xc0, + 0x18, 0x38, 0xe1, 0x87, 0x1f, 0xf8, 0x0, 0x10, + 0xc, 0x60, 0x3, 0x80, 0x0, 0x60, + + /* U+96F7 "雷" */ + 0x7f, 0xf8, 0x4, 0xf, 0xff, 0xe0, 0x41, 0xbd, + 0x70, 0x4, 0x3, 0xd7, 0x0, 0x40, 0x3f, 0xf9, + 0x4, 0x24, 0x10, 0x8f, 0xfe, 0x41, 0x8, 0xff, + 0xe4, 0x0, 0x80, + + /* U+96F9 "雹" */ + 0x3f, 0xf8, 0x4, 0xf, 0xff, 0xe0, 0x41, 0xbd, + 0x7c, 0xf5, 0xe1, 0x0, 0x7, 0xfe, 0x20, 0xb, + 0xfe, 0x22, 0x8, 0x8f, 0xef, 0x20, 0x4, 0x80, + 0x11, 0xff, 0x80, + + /* U+96FE "雾" */ + 0x3f, 0xfc, 0x0, 0x80, 0x7f, 0xfe, 0x40, 0x82, + 0x5e, 0xba, 0x1e, 0xb8, 0x4, 0x0, 0x1f, 0xf8, + 0x64, 0x10, 0x7, 0xf8, 0x79, 0x6, 0x1f, 0xf8, + 0x2, 0x8, 0x4, 0x8, 0x78, 0xf8, + + /* U+9700 "需" */ + 0x7f, 0xfc, 0x2, 0x2, 0xff, 0xfc, 0xeb, 0xd0, + 0x10, 0x3, 0xaf, 0x0, 0x40, 0x7f, 0xff, 0x1, + 0x0, 0xff, 0xf9, 0x11, 0x12, 0x22, 0x24, 0x44, + 0x48, 0x8b, 0x80, + + /* U+9701 "霁" */ + 0x3f, 0xfc, 0x1, 0x1, 0xff, 0xfa, 0x4, 0x15, + 0xeb, 0xa3, 0xd7, 0x0, 0x20, 0x3f, 0xff, 0x8, + 0x10, 0xc, 0xc0, 0x7, 0x3, 0xf1, 0xf0, 0x42, + 0x1, 0x84, 0xc, 0x8, 0x0, + + /* U+9704 "霄" */ + 0x7f, 0xfc, 0x4, 0xf, 0xff, 0xe0, 0x41, 0x99, + 0x74, 0xf1, 0xc2, 0x11, 0x6, 0x48, 0x7f, 0xf9, + 0x0, 0x27, 0xff, 0x90, 0x2, 0x7f, 0xf9, 0x0, + 0x24, 0x7, 0x80, + + /* U+9706 "霆" */ + 0x7f, 0xf8, 0x4, 0x3, 0xff, 0xf4, 0x10, 0x2b, + 0xaf, 0x47, 0x5e, 0x0, 0x80, 0x7b, 0xfe, 0x20, + 0x41, 0xe7, 0xf0, 0x41, 0x2, 0xbf, 0xe6, 0x0, + 0x6, 0x0, 0x33, 0xff, 0x0, 0x0, + + /* U+9707 "震" */ + 0x3f, 0xfc, 0x2, 0x1, 0xff, 0xfa, 0x8, 0x15, + 0xf3, 0xa3, 0xe7, 0x0, 0x0, 0x1f, 0xff, 0x20, + 0x0, 0x4f, 0xf0, 0xff, 0xf9, 0x24, 0x44, 0x47, + 0x8, 0xe7, 0x1, 0x0, 0x80, + + /* U+9708 "霈" */ + 0x3f, 0xfc, 0x1, 0x1, 0xff, 0xfa, 0x4, 0x15, + 0xeb, 0xa0, 0x10, 0x7, 0xae, 0x10, 0x20, 0x17, + 0xfe, 0x80, 0x80, 0x9f, 0xf0, 0x22, 0x21, 0x44, + 0x44, 0x89, 0x90, 0x10, 0x0, + + /* U+9709 "霉" */ + 0x7f, 0xfc, 0x2, 0x3, 0xff, 0xfc, 0x8, 0x19, + 0xd7, 0x27, 0xaf, 0x4, 0x0, 0x17, 0xfe, 0x5f, + 0xf8, 0x46, 0x13, 0xff, 0xf9, 0x8, 0x83, 0xff, + 0xe0, 0x2, 0x0, 0x3c, 0x0, + + /* U+970D "霍" */ + 0x3f, 0xfc, 0x1, 0x1, 0xff, 0xfa, 0x4, 0x15, + 0xeb, 0xa3, 0xd7, 0x2, 0x20, 0xf, 0xff, 0x30, + 0x40, 0xbf, 0xf8, 0x41, 0x0, 0xff, 0xe1, 0x4, + 0x3, 0xff, 0xc4, 0x0, 0x0, + + /* U+970E "霎" */ + 0x7f, 0xfc, 0x2, 0x3, 0xff, 0xfd, 0xeb, 0xd8, + 0x10, 0x23, 0x26, 0x1f, 0xff, 0x4, 0x10, 0x4, + 0x41, 0xff, 0xfc, 0x10, 0x7, 0xff, 0xf1, 0x82, + 0x0, 0xf8, 0xf, 0x8e, 0x20, 0x0, + + /* U+970F "霏" */ + 0x7f, 0xf8, 0x8, 0xf, 0xff, 0xe0, 0x81, 0xbe, + 0xf4, 0x8, 0x3, 0xcf, 0x0, 0x0, 0xfc, 0xfc, + 0x12, 0xf, 0xcf, 0xc1, 0x20, 0xfc, 0xfc, 0x12, + 0x0, 0x48, 0x0, + + /* U+9713 "霓" */ + 0x7f, 0xf8, 0x2, 0x3, 0xff, 0xf4, 0x8, 0x2b, + 0xd7, 0x47, 0xae, 0x0, 0x0, 0x3e, 0x7c, 0x40, + 0x8, 0xf9, 0xf1, 0x0, 0x23, 0xff, 0xc0, 0x88, + 0x21, 0x10, 0x7c, 0x1f, 0x0, + + /* U+9716 "霖" */ + 0x3f, 0xfc, 0x1, 0x0, 0x7f, 0xfe, 0x41, 0x2, + 0x5d, 0x7a, 0x1d, 0x78, 0x1, 0x0, 0x8, 0x10, + 0x7e, 0xfe, 0x8, 0x30, 0x1c, 0x58, 0x2a, 0x54, + 0x49, 0x92, 0x8, 0x10, 0x8, 0x10, + + /* U+971C "霜" */ + 0x3f, 0xfc, 0x2, 0x1, 0xff, 0xfa, 0x8, 0x15, + 0xd7, 0xa0, 0x20, 0x7, 0x1e, 0x8, 0xfe, 0x7d, + 0x4, 0x23, 0xf8, 0xe4, 0x12, 0xaf, 0xe9, 0x10, + 0x42, 0x3f, 0x84, 0x41, 0x0, + + /* U+971E "霞" */ + 0x7f, 0xf8, 0x4, 0x3, 0xff, 0xf5, 0xd7, 0xa8, + 0x20, 0x42, 0xc, 0x1f, 0x7e, 0x22, 0x4, 0x7d, + 0xf8, 0x80, 0x1, 0xf7, 0xe2, 0x8, 0x47, 0xc9, + 0x8, 0x1e, 0x11, 0xc3, 0x0, + + /* U+972A "霪" */ + 0x7f, 0xf8, 0x8, 0xf, 0xff, 0xee, 0xbd, 0x82, + 0x4, 0xe3, 0xc4, 0xff, 0x89, 0x24, 0x84, 0x91, + 0x3f, 0xe0, 0x8, 0xf, 0xff, 0x40, 0x81, 0x2, + 0x8, 0xff, 0x80, + + /* U+972D "霭" */ + 0x7f, 0xf8, 0x8, 0xf, 0xff, 0xe0, 0x81, 0xbe, + 0xf4, 0x0, 0x4, 0x7f, 0x89, 0xfe, 0x4, 0xb, + 0xf, 0xc4, 0x7f, 0xd6, 0x31, 0x45, 0x35, 0x5f, + 0xf6, 0x7, 0x80, + + /* U+9730 "霰" */ + 0x7f, 0xfc, 0x2, 0x3, 0xff, 0xfc, 0x8, 0x1b, + 0xd7, 0xa0, 0x0, 0x9, 0x10, 0x3f, 0x3e, 0xff, + 0xc4, 0xfd, 0x91, 0x9, 0x23, 0xf1, 0x87, 0xe3, + 0x88, 0x49, 0x93, 0xa0, 0x0, + + /* U+9732 "露" */ + 0x3f, 0xfc, 0x1, 0x1, 0xff, 0xfa, 0x4, 0x14, + 0xcb, 0x23, 0xd7, 0x0, 0x10, 0x1f, 0x3e, 0x22, + 0xcc, 0x7c, 0x60, 0x27, 0x3a, 0x77, 0xe4, 0x88, + 0x4f, 0x5f, 0x80, 0x21, 0x0, + + /* U+9738 "霸" */ + 0x7f, 0xf8, 0x2, 0x3, 0xff, 0xfd, 0xeb, 0xd0, + 0x10, 0x23, 0x86, 0x3f, 0xdf, 0x21, 0x22, 0x3c, + 0x7c, 0xfc, 0x89, 0x29, 0x11, 0xe3, 0xef, 0xf8, + 0x41, 0x10, 0x82, 0x47, 0x0, + + /* U+9739 "霹" */ + 0x3f, 0xfc, 0x2, 0x1, 0xff, 0xfa, 0x8, 0x15, + 0xf7, 0xa0, 0x0, 0x3, 0x1e, 0x1f, 0x88, 0x21, + 0x6c, 0x7e, 0x90, 0x83, 0xf9, 0xf8, 0x85, 0x1f, + 0xfb, 0xe2, 0x4, 0x44, 0x0, + + /* U+973E "霾" */ + 0x3f, 0xfc, 0x1, 0x0, 0x7f, 0xfe, 0x5f, 0x7a, + 0x1, 0x0, 0xc, 0x30, 0x1d, 0xfe, 0x69, 0x22, + 0x2d, 0xfe, 0x79, 0x22, 0x19, 0xfe, 0x64, 0x20, + 0x1d, 0xfe, 0x64, 0x20, 0x1f, 0xff, + + /* U+9752 "青" */ + 0x1, 0x1, 0xff, 0xfc, 0x4, 0x3, 0xff, 0xe0, + 0x10, 0x1f, 0xff, 0xc0, 0x0, 0x1f, 0xfc, 0x20, + 0x8, 0x7f, 0xf0, 0x80, 0x21, 0xff, 0xc2, 0x0, + 0x84, 0x1, 0x8, 0x1e, 0x0, + + /* U+9753 "靓" */ + 0x10, 0x0, 0x21, 0xfb, 0xfa, 0x17, 0xe5, 0xa1, + 0xb, 0x5f, 0xd6, 0x80, 0x2d, 0x3e, 0x5a, 0x84, + 0xb4, 0xf9, 0x6a, 0x10, 0xc3, 0xe3, 0x98, 0x45, + 0x30, 0x92, 0x67, 0x47, 0x0, + + /* U+9756 "靖" */ + 0x20, 0x40, 0x47, 0xfc, 0x1, 0x7, 0xdf, 0xe0, + 0x4, 0x11, 0xff, 0xe4, 0x0, 0x29, 0xfe, 0x52, + 0x4, 0xa7, 0xf8, 0x88, 0x11, 0x1f, 0xe7, 0xa0, + 0x50, 0x40, 0x80, 0x8f, 0x0, + + /* U+9759 "静" */ + 0x10, 0x41, 0xfd, 0xf0, 0x44, 0x23, 0xe0, 0x81, + 0x1f, 0xdf, 0xc4, 0x80, 0x9, 0x3e, 0xff, 0x44, + 0x24, 0xf8, 0x49, 0x17, 0xf3, 0xe1, 0x24, 0x42, + 0x8, 0x84, 0x17, 0x78, 0x0, + + /* U+975B "靛" */ + 0x20, 0x21, 0xf8, 0x40, 0x8f, 0x7b, 0xd0, 0x12, + 0x0, 0x3f, 0xbf, 0xc0, 0x8, 0x3c, 0x10, 0x89, + 0x20, 0xf2, 0x7a, 0x24, 0x83, 0xc9, 0x8, 0xb2, + 0x11, 0x5c, 0x26, 0x7, 0x80, + + /* U+975E "非" */ + 0x4, 0x40, 0x8, 0x83, 0xf1, 0xf8, 0x22, 0x0, + 0x44, 0x0, 0x88, 0x3f, 0x1f, 0x82, 0x20, 0x4, + 0x40, 0x8, 0x80, 0x11, 0x7, 0xe3, 0xf0, 0x44, + 0x0, 0x88, 0x1, 0x10, 0x0, + + /* U+9760 "靠" */ + 0x8, 0x80, 0x3f, 0xf8, 0x82, 0x7, 0xff, 0xf0, + 0x0, 0x3, 0xff, 0x4, 0x2, 0xf, 0xfc, 0x0, + 0x0, 0xfc, 0xfc, 0x9, 0x3, 0xf3, 0xf0, 0x24, + 0x1f, 0xcf, 0xc0, 0x90, 0x0, + + /* U+9761 "靡" */ + 0x1, 0x0, 0x2, 0x1, 0xfb, 0xf2, 0x21, 0x7, + 0xef, 0xc9, 0x84, 0x15, 0x9e, 0x32, 0xd2, 0x40, + 0x0, 0xfc, 0xf9, 0x9, 0x2, 0x73, 0xcb, 0xe7, + 0xd0, 0x48, 0x20, 0x90, 0x0, + + /* U+9762 "面" */ + 0xff, 0xfe, 0x4, 0x0, 0x8, 0x3, 0xff, 0xe4, + 0x84, 0x49, 0x8, 0x91, 0xf1, 0x24, 0x22, 0x48, + 0x44, 0x8f, 0x89, 0x21, 0x12, 0x42, 0x27, 0xff, + 0xc8, 0x0, 0x80, + + /* U+9765 "靥" */ + 0x3f, 0xfe, 0x20, 0x90, 0x2f, 0xfe, 0x21, 0x20, + 0x26, 0x18, 0x3b, 0xf6, 0x20, 0x80, 0x2f, 0xfe, + 0x29, 0x22, 0x29, 0xe2, 0x49, 0x22, 0x49, 0xe2, + 0xcf, 0xfe, 0x8, 0x2, + + /* U+9769 "革" */ + 0x8, 0x21, 0xff, 0xfc, 0x20, 0x80, 0x41, 0x0, + 0xfe, 0x0, 0x20, 0xf, 0xff, 0x10, 0x82, 0x21, + 0x4, 0x7f, 0xf8, 0x4, 0x7, 0xff, 0xf0, 0x10, + 0x0, 0x20, 0x0, 0x40, 0x0, + + /* U+9773 "靳" */ + 0x44, 0x5, 0xfd, 0xf1, 0x12, 0x2, 0x24, 0x7, + 0xc8, 0x2, 0x10, 0x3f, 0xbf, 0xc9, 0x44, 0x92, + 0x89, 0xfd, 0x10, 0x42, 0x27, 0xf4, 0x41, 0x10, + 0x82, 0x21, 0x4, 0x82, 0x0, + + /* U+9774 "靴" */ + 0x44, 0x51, 0xfc, 0xa1, 0x12, 0x4a, 0x24, 0x93, + 0xc9, 0x42, 0x32, 0x9f, 0xa6, 0x49, 0x48, 0x92, + 0xb0, 0xfd, 0xa0, 0x42, 0x47, 0xf4, 0x91, 0x9, + 0x22, 0x12, 0x44, 0x27, 0x0, + + /* U+9776 "靶" */ + 0x44, 0x1, 0xff, 0xf9, 0x14, 0x92, 0x29, 0x27, + 0xd2, 0x42, 0x24, 0x9f, 0x49, 0x4a, 0x7e, 0x95, + 0x0, 0xfa, 0x0, 0x44, 0xf, 0xf8, 0x11, 0x10, + 0x22, 0x20, 0xc4, 0x3f, 0x0, + + /* U+977C "靼" */ + 0x44, 0x1, 0xfd, 0xf9, 0x12, 0x12, 0x24, 0x27, + 0xc8, 0x42, 0x1f, 0x9f, 0x21, 0x4a, 0x42, 0x94, + 0x84, 0xf9, 0x8, 0x43, 0xf0, 0x80, 0x7, 0xc0, + 0x2, 0x3f, 0xc4, 0x0, 0x0, + + /* U+9785 "鞅" */ + 0x44, 0x21, 0xfc, 0x41, 0x10, 0x82, 0x27, 0xe7, + 0xca, 0x42, 0x14, 0x9f, 0x29, 0x4a, 0x52, 0x95, + 0xfe, 0xf8, 0x40, 0x41, 0x47, 0xf2, 0x81, 0x8, + 0x82, 0x30, 0x84, 0x40, 0x80, + + /* U+978B "鞋" */ + 0x44, 0x21, 0xfc, 0x41, 0x17, 0xf2, 0x21, 0x7, + 0xc2, 0x2, 0x4, 0x1f, 0x7f, 0xca, 0x10, 0x94, + 0x20, 0xf3, 0xf8, 0x40, 0x87, 0xf1, 0x1, 0x2, + 0x2, 0x3f, 0xc4, 0x0, 0x0, + + /* U+978D "鞍" */ + 0x48, 0x21, 0xfc, 0x41, 0x27, 0xfa, 0x48, 0x17, + 0x92, 0x22, 0x8, 0x3f, 0x7f, 0xca, 0x24, 0x94, + 0x88, 0xf9, 0x10, 0x43, 0x47, 0xf1, 0x81, 0x6, + 0x82, 0x30, 0xc4, 0x80, 0x80, + + /* U+9791 "鞑" */ + 0x49, 0x11, 0xf9, 0x21, 0x22, 0x42, 0x41, 0xf7, + 0x81, 0x4, 0x32, 0x3f, 0x24, 0x52, 0x4c, 0xa4, + 0xa9, 0xf9, 0x48, 0x82, 0x8f, 0xe4, 0x2, 0x1c, + 0x4, 0x68, 0x8, 0x8f, 0x80, + + /* U+9792 "鞒" */ + 0x44, 0x3d, 0xfc, 0xc1, 0x10, 0x82, 0x21, 0x7, + 0xc2, 0x2, 0x1f, 0x9f, 0x12, 0x4a, 0x24, 0x95, + 0x86, 0xf8, 0x90, 0x41, 0x27, 0xf2, 0x41, 0xc, + 0x82, 0x11, 0x4, 0x42, 0x0, + + /* U+9794 "鞔" */ + 0x44, 0x40, 0xfe, 0x7c, 0x44, 0x8c, 0x45, 0x8, + 0x7d, 0x10, 0x11, 0xee, 0x7d, 0x22, 0x95, 0x22, + 0x95, 0xfe, 0x7c, 0x40, 0x10, 0x50, 0xfe, 0x51, + 0x10, 0x91, 0x11, 0x11, 0x12, 0x1e, + + /* U+9798 "鞘" */ + 0x44, 0x13, 0xfd, 0x25, 0x11, 0x52, 0x20, 0x87, + 0xcf, 0xe2, 0x10, 0x5f, 0x20, 0xca, 0x7f, 0x94, + 0x82, 0xf9, 0x4, 0x43, 0xff, 0xf4, 0x11, 0x8, + 0x22, 0x10, 0x44, 0x23, 0x80, + + /* U+97A0 "鞠" */ + 0x48, 0x83, 0xf3, 0xf4, 0x90, 0x52, 0x81, 0x4a, + 0x44, 0xc1, 0x52, 0x16, 0x7f, 0x11, 0xa5, 0xf7, + 0xf3, 0x12, 0x17, 0x48, 0xd1, 0xfc, 0x44, 0x81, + 0x12, 0x3, 0x80, + + /* U+97A3 "鞣" */ + 0x4b, 0xff, 0xfa, 0x19, 0x23, 0x42, 0x41, 0x87, + 0xbf, 0xe4, 0xa, 0x7e, 0x65, 0x55, 0x70, 0xa8, + 0x1, 0xf0, 0x40, 0x8f, 0xff, 0xe5, 0x42, 0x12, + 0x44, 0xc4, 0x48, 0x8, 0x0, + + /* U+97AB "鞫" */ + 0x48, 0x81, 0xf9, 0xfd, 0x24, 0xa, 0x52, 0x17, + 0xbf, 0xa4, 0x0, 0x5f, 0x38, 0xd2, 0x1, 0xa4, + 0xe2, 0xf3, 0xe4, 0x84, 0x49, 0x8, 0x9f, 0xdf, + 0x24, 0x0, 0x48, 0xf, 0x0, + + /* U+97AD "鞭" */ + 0x48, 0x80, 0x49, 0x7e, 0xfd, 0x10, 0x49, 0x10, + 0x7b, 0x7e, 0x23, 0x52, 0x7d, 0x7e, 0xa5, 0x52, + 0xa5, 0x52, 0x7d, 0x3e, 0x21, 0x50, 0xfd, 0x20, + 0x21, 0x30, 0x21, 0x4c, 0x21, 0x2, + + /* U+97AF "鞯" */ + 0x44, 0x48, 0xff, 0xfe, 0x44, 0x48, 0x7c, 0x20, + 0x11, 0xff, 0x10, 0x40, 0x7c, 0x40, 0x94, 0xbe, + 0x95, 0x84, 0x7d, 0x88, 0x10, 0xff, 0xfe, 0x88, + 0x10, 0x88, 0x10, 0x88, 0x10, 0xb8, + + /* U+97B2 "鞲" */ + 0x48, 0x88, 0x48, 0x88, 0xfd, 0xfe, 0x48, 0x88, + 0x78, 0xfc, 0x13, 0xff, 0x7c, 0x20, 0x95, 0xfe, + 0x95, 0x22, 0x7d, 0xfe, 0x11, 0x22, 0xff, 0xff, + 0x11, 0x2, 0x11, 0x2, 0x11, 0x1c, + + /* U+97B4 "鞴" */ + 0x48, 0x88, 0x48, 0x88, 0xfd, 0xfe, 0x48, 0x88, + 0x7b, 0xff, 0x20, 0x80, 0x7c, 0xfe, 0xa5, 0x22, + 0xa7, 0x22, 0x7d, 0xfe, 0x21, 0x22, 0xfd, 0xfe, + 0x21, 0x22, 0x21, 0x22, 0x21, 0xe, + + /* U+97E6 "韦" */ + 0x2, 0x0, 0x8, 0xf, 0xff, 0xc0, 0x80, 0x2, + 0x1, 0xff, 0xe0, 0x20, 0x0, 0x80, 0xff, 0xfc, + 0x8, 0x10, 0x20, 0x40, 0x82, 0x2, 0xf8, 0x8, + 0x0, 0x20, 0x0, + + /* U+97E7 "韧" */ + 0x10, 0x0, 0x21, 0xfd, 0xf8, 0x48, 0x80, 0x91, + 0x9, 0x2f, 0xd2, 0x44, 0x24, 0x88, 0x89, 0xfe, + 0x12, 0x24, 0x44, 0x48, 0x88, 0xb1, 0x11, 0x4, + 0x22, 0x10, 0x44, 0x4f, 0x0, + + /* U+97E9 "韩" */ + 0x10, 0x20, 0x20, 0x43, 0xff, 0xf8, 0x81, 0x7, + 0xc2, 0x10, 0xbf, 0xdf, 0x8, 0x42, 0x10, 0x85, + 0xfe, 0xf8, 0x44, 0x40, 0x8f, 0xf1, 0x11, 0x2, + 0xe2, 0x4, 0x4, 0x8, 0x0, + + /* U+97EA "韪" */ + 0x0, 0x20, 0xf8, 0x41, 0x13, 0xfb, 0xe1, 0x4, + 0x42, 0xf, 0x9f, 0x80, 0x8, 0x7f, 0x10, 0x10, + 0xfe, 0xa0, 0x45, 0x38, 0x8a, 0x81, 0x67, 0x2, + 0x13, 0x0, 0x23, 0xff, 0x80, + + /* U+97EB "韫" */ + 0x20, 0x0, 0x21, 0xfc, 0xfd, 0x4, 0x21, 0xfc, + 0x21, 0x4, 0xfd, 0x4, 0x21, 0xfc, 0x20, 0x0, + 0xfd, 0xfe, 0x26, 0x52, 0x26, 0x52, 0x3a, 0x52, + 0x22, 0x52, 0x27, 0xff, 0x20, 0x0, + + /* U+97EC "韬" */ + 0x20, 0x0, 0x43, 0xfb, 0xf0, 0x89, 0x9, 0x12, + 0x11, 0x5f, 0x90, 0x88, 0x10, 0x10, 0xc7, 0xfd, + 0x2, 0x4a, 0x4, 0x97, 0x79, 0x28, 0x12, 0x90, + 0x24, 0x3f, 0xc8, 0x40, 0x80, + + /* U+97ED "韭" */ + 0x2, 0x40, 0x4, 0x81, 0xf9, 0xf8, 0x12, 0x0, + 0x24, 0x0, 0x48, 0x1f, 0x9f, 0x81, 0x20, 0x2, + 0x40, 0xfc, 0xfc, 0x9, 0x0, 0x12, 0x0, 0x24, + 0x1f, 0xff, 0xc0, + + /* U+97F3 "音" */ + 0x1, 0x0, 0x2, 0x1, 0xff, 0xf0, 0x41, 0x0, + 0x42, 0x1f, 0xff, 0xc0, 0x0, 0x1f, 0xfc, 0x20, + 0x8, 0x40, 0x10, 0xff, 0xe1, 0x0, 0x42, 0x0, + 0x87, 0xff, 0x8, 0x2, 0x0, + + /* U+97F5 "韵" */ + 0x10, 0x21, 0xfe, 0x80, 0x1, 0xf9, 0x24, 0x12, + 0x48, 0x3f, 0xec, 0x40, 0xc, 0xbf, 0x1, 0x42, + 0xa, 0x84, 0x65, 0xfb, 0xa, 0x10, 0x14, 0x20, + 0x2f, 0xc0, 0xd0, 0x8f, 0x0, + + /* U+97F6 "韶" */ + 0x10, 0x0, 0x47, 0xff, 0xe2, 0x41, 0x9, 0x28, + 0x44, 0x21, 0x1f, 0x79, 0x80, 0x0, 0x7c, 0xfd, + 0x12, 0x17, 0xc8, 0x51, 0x21, 0x44, 0x85, 0xf3, + 0xf4, 0x48, 0x40, + + /* U+9875 "页" */ + 0x7f, 0xfe, 0x2, 0x0, 0x4, 0x1, 0xff, 0xc2, + 0x0, 0x84, 0x1, 0x8, 0x42, 0x10, 0x84, 0x21, + 0x8, 0x43, 0x10, 0xd, 0x0, 0x31, 0x81, 0x80, + 0xdc, 0x0, 0xc0, + + /* U+9876 "顶" */ + 0xfb, 0xfc, 0x40, 0x40, 0x81, 0x1, 0x1f, 0xe2, + 0x20, 0x44, 0x44, 0x88, 0x89, 0x11, 0x12, 0x22, + 0x24, 0x44, 0xc8, 0x89, 0xd1, 0x4, 0x4e, 0x30, + 0x60, 0x80, 0x0, + + /* U+9877 "顷" */ + 0x8f, 0xfe, 0x2, 0x8, 0x8, 0x3d, 0xde, 0x84, + 0xa, 0x11, 0x28, 0x44, 0xa1, 0x12, 0x84, 0x8a, + 0x13, 0x29, 0x4a, 0xb8, 0x44, 0x86, 0xc, 0x20, + 0x10, + + /* U+9878 "顸" */ + 0xfd, 0xfe, 0x20, 0x40, 0x40, 0x80, 0x8f, 0xe1, + 0x10, 0x42, 0x24, 0xbf, 0x49, 0x8, 0x92, 0x11, + 0x24, 0x22, 0x48, 0x44, 0xc0, 0x82, 0x41, 0x8, + 0x42, 0xe0, 0x40, + + /* U+9879 "项" */ + 0x7, 0xff, 0xf0, 0x40, 0x80, 0x81, 0x2, 0x2, + 0x3f, 0xc4, 0x40, 0x88, 0x89, 0x11, 0x12, 0x22, + 0x24, 0x44, 0x48, 0x88, 0x93, 0x93, 0xa8, 0xc, + 0x80, 0x30, 0x83, 0x80, 0x80, + + /* U+987A "顺" */ + 0x45, 0xfe, 0xa8, 0x41, 0x50, 0x82, 0xa1, 0x5, + 0x5d, 0xca, 0xa0, 0x95, 0x49, 0x2a, 0x92, 0x55, + 0x24, 0xaa, 0x49, 0x54, 0x92, 0xa1, 0xc9, 0x44, + 0xd0, 0x90, 0x40, 0x40, 0x0, + + /* U+987B "须" */ + 0x8, 0x0, 0x17, 0xfc, 0x40, 0x81, 0x2, 0x4, + 0x1f, 0xc1, 0x40, 0x84, 0x89, 0x9, 0x12, 0x22, + 0x25, 0x84, 0x48, 0x28, 0x90, 0x53, 0xa1, 0x4, + 0x84, 0x30, 0xb1, 0x80, 0x80, + + /* U+987C "顼" */ + 0xfb, 0xfe, 0x40, 0x40, 0x81, 0x1, 0x1f, 0xe2, + 0x20, 0x5f, 0x44, 0x88, 0x89, 0x11, 0x12, 0x22, + 0x24, 0x44, 0x48, 0xa9, 0xd7, 0x86, 0xc0, 0x18, + 0x41, 0xc0, 0x40, + + /* U+987D "顽" */ + 0x7d, 0xfe, 0x0, 0x40, 0x1, 0x0, 0xf, 0xef, + 0xd0, 0x49, 0x24, 0x92, 0x49, 0x24, 0x92, 0x49, + 0x24, 0x92, 0x49, 0x31, 0xc4, 0x46, 0xc8, 0x10, + 0x40, 0x40, 0x0, + + /* U+987E "顾" */ + 0x7e, 0xfe, 0x80, 0x41, 0x0, 0x82, 0xf7, 0xe5, + 0x28, 0x4a, 0x52, 0x94, 0xa5, 0x29, 0x4a, 0x56, + 0x94, 0xa1, 0x29, 0x42, 0xc2, 0x91, 0x49, 0xc4, + 0x50, 0x30, 0x40, + + /* U+987F "顿" */ + 0x10, 0x0, 0x10, 0xff, 0xfe, 0x10, 0x10, 0x20, + 0x92, 0xfe, 0x92, 0x82, 0x92, 0x92, 0x92, 0x92, + 0x7e, 0x92, 0x10, 0x92, 0x10, 0x92, 0x10, 0xb0, + 0x12, 0x28, 0x1c, 0x46, 0x11, 0x82, + + /* U+9880 "颀" */ + 0x7d, 0xfe, 0x80, 0x41, 0x0, 0x82, 0xf, 0xe7, + 0xd0, 0x49, 0x24, 0x92, 0x49, 0x24, 0x92, 0x49, + 0x24, 0x92, 0x49, 0x20, 0xc2, 0x42, 0x48, 0x88, + 0x51, 0x60, 0x40, + + /* U+9881 "颁" */ + 0x29, 0xfe, 0x48, 0x41, 0x10, 0x82, 0x17, 0xe8, + 0x10, 0x4f, 0xa4, 0x89, 0x49, 0x12, 0x92, 0x25, + 0x24, 0x4a, 0x48, 0x90, 0xc2, 0x22, 0xc4, 0x4c, + 0xd3, 0x20, 0x40, 0x0, 0x0, + + /* U+9882 "颂" */ + 0x49, 0xfe, 0x90, 0x41, 0x10, 0x84, 0x2f, 0xe8, + 0x50, 0x42, 0x20, 0x88, 0x49, 0x10, 0x92, 0x49, + 0x24, 0x92, 0x4a, 0x14, 0xc7, 0xe2, 0x40, 0x18, + 0x60, 0x0, 0x0, + + /* U+9883 "颃" */ + 0x10, 0x0, 0x10, 0xff, 0x8, 0x10, 0xfe, 0x20, + 0x0, 0xfe, 0x0, 0x82, 0x7c, 0x82, 0x44, 0x92, + 0x44, 0x92, 0x44, 0x92, 0x44, 0x92, 0x44, 0x92, + 0x45, 0xb2, 0x46, 0x2c, 0x80, 0xc2, 0x1, 0x0, + + /* U+9884 "预" */ + 0xff, 0xfe, 0x10, 0x41, 0x41, 0x1, 0xf, 0xe1, + 0x20, 0x5f, 0xc4, 0x89, 0x89, 0x13, 0x12, 0x22, + 0x24, 0x44, 0x48, 0x81, 0x91, 0x6, 0xc2, 0x18, + 0x5c, 0xc0, 0x40, + + /* U+9885 "颅" */ + 0x11, 0xfe, 0x38, 0x40, 0x40, 0x80, 0x81, 0x7, + 0xdd, 0xc8, 0xa0, 0x91, 0x49, 0x22, 0x92, 0x7d, + 0x24, 0x82, 0x49, 0x4, 0xd2, 0x1, 0xc4, 0x4, + 0xd0, 0x30, 0xe0, 0xc0, 0x80, + + /* U+9886 "领" */ + 0x8, 0x0, 0x19, 0xff, 0x14, 0x10, 0x24, 0x10, + 0x22, 0x20, 0x52, 0xfe, 0x88, 0x82, 0x0, 0x92, + 0x7e, 0x92, 0x2, 0x92, 0x4, 0x92, 0x4, 0x92, + 0x28, 0xb0, 0x18, 0x2c, 0x8, 0x46, 0x5, 0x83, + + /* U+9887 "颇" */ + 0x8, 0x0, 0x13, 0xfc, 0x20, 0x83, 0xf1, 0x4, + 0xa2, 0x9, 0x5f, 0x92, 0x41, 0x3f, 0x92, 0x43, + 0x24, 0x8a, 0x49, 0x54, 0x92, 0x69, 0x24, 0x43, + 0x11, 0x89, 0x24, 0xa1, 0x70, 0x81, + + /* U+9888 "颈" */ + 0x7d, 0xff, 0x4, 0x10, 0x8, 0x20, 0x8, 0xfe, + 0x34, 0x82, 0xc2, 0x92, 0x0, 0x92, 0x7e, 0x92, + 0x8, 0x92, 0x8, 0x92, 0x8, 0xb2, 0x8, 0x28, + 0x7c, 0x46, 0x1, 0x83, + + /* U+9889 "颉" */ + 0x10, 0x0, 0x10, 0xff, 0xfe, 0x10, 0x10, 0x20, + 0x10, 0xfe, 0x10, 0x82, 0xfe, 0x92, 0x0, 0x92, + 0x7c, 0x92, 0x44, 0x92, 0x44, 0x92, 0x44, 0x28, + 0x44, 0x24, 0x7c, 0x42, 0x41, 0x81, + + /* U+988A "颊" */ + 0x10, 0xfe, 0x20, 0x23, 0xf8, 0x80, 0x81, 0x1, + 0xf, 0xca, 0x90, 0x84, 0x25, 0x7f, 0x4a, 0x10, + 0x94, 0x21, 0x28, 0xa2, 0x51, 0x25, 0xa4, 0x46, + 0x88, 0x18, 0xa0, 0x60, 0x80, + + /* U+988C "颌" */ + 0x10, 0x0, 0x23, 0xfc, 0xa0, 0x83, 0x21, 0xc, + 0x2d, 0xcf, 0x90, 0x80, 0x29, 0x0, 0x52, 0x7c, + 0xa4, 0x89, 0x49, 0x12, 0x92, 0x21, 0x84, 0x44, + 0x8f, 0x98, 0x90, 0x40, 0x80, + + /* U+988D "颍" */ + 0x40, 0x0, 0x80, 0x1, 0x37, 0xfb, 0x81, 0x4, + 0x22, 0xf, 0xbb, 0x80, 0x41, 0x4, 0x92, 0xb, + 0x25, 0xd6, 0x48, 0xb4, 0x91, 0x69, 0x24, 0xa2, + 0x11, 0xb, 0x22, 0x21, 0x1d, 0x81, + + /* U+988F "颏" */ + 0x10, 0x0, 0x13, 0xff, 0xf8, 0x81, 0x2, 0x4, + 0x1f, 0xc9, 0x20, 0xbe, 0x49, 0x9, 0x92, 0x35, + 0x24, 0x4a, 0x49, 0x24, 0x94, 0xc9, 0x23, 0x47, + 0xc, 0x59, 0x20, 0x41, 0x0, + + /* U+9890 "颐" */ + 0xfd, 0xfe, 0x40, 0x89, 0x4, 0x24, 0x7e, 0xbd, + 0xa, 0x94, 0xaa, 0x52, 0xa9, 0x4a, 0xbd, 0x2a, + 0x44, 0xa9, 0x14, 0x24, 0x1c, 0xfe, 0x88, 0x4, + 0x10, + + /* U+9891 "频" */ + 0x8, 0x0, 0x49, 0xff, 0x48, 0x10, 0x4e, 0x20, + 0x48, 0xfe, 0x48, 0x82, 0xb6, 0x92, 0x10, 0x92, + 0x50, 0x92, 0x52, 0x92, 0x94, 0x92, 0x8, 0xa2, + 0x18, 0x38, 0x20, 0x46, 0x43, 0x83, + + /* U+9893 "颓" */ + 0x7e, 0xff, 0x10, 0x10, 0x10, 0x20, 0xfe, 0xfe, + 0x10, 0x82, 0x7c, 0x92, 0xd2, 0x92, 0x0, 0x92, + 0x3c, 0x92, 0x24, 0x92, 0x24, 0x92, 0x25, 0x28, + 0x46, 0x46, 0xc1, 0x83, + + /* U+9894 "颔" */ + 0x8, 0x0, 0x18, 0xff, 0x26, 0x10, 0x52, 0x20, + 0x88, 0xde, 0x7e, 0x92, 0x4, 0x92, 0x4, 0x92, + 0x0, 0x92, 0x7e, 0x92, 0x42, 0x92, 0x42, 0x92, + 0x42, 0x28, 0x7e, 0x44, 0x40, 0x83, 0x1, 0x0, + + /* U+9896 "颖" */ + 0x40, 0x0, 0x45, 0xff, 0x78, 0x10, 0x42, 0x20, + 0x62, 0xfe, 0x1c, 0x82, 0xfe, 0x92, 0x10, 0x92, + 0x10, 0x92, 0xfe, 0x92, 0x10, 0x92, 0x3c, 0xb2, + 0x52, 0x28, 0x90, 0xc6, 0x11, 0x1, + + /* U+9897 "颗" */ + 0x7e, 0xff, 0x24, 0x22, 0x48, 0x83, 0xf7, 0xe9, + 0x28, 0x4f, 0xd2, 0x84, 0x25, 0x8, 0x4a, 0xfe, + 0x94, 0x21, 0x28, 0xe2, 0xd2, 0xa1, 0xc9, 0x4, + 0x42, 0x30, 0x0, + + /* U+9898 "题" */ + 0x7f, 0xfe, 0x84, 0x41, 0x8, 0x83, 0xf2, 0x4, + 0x2b, 0xcf, 0xd4, 0x80, 0x29, 0x7f, 0x52, 0x8, + 0xa4, 0x91, 0x49, 0x39, 0x42, 0x42, 0x46, 0x88, + 0x73, 0x0, 0x23, 0xff, 0x80, + + /* U+989A "颚" */ + 0x77, 0xff, 0x99, 0x10, 0x99, 0x20, 0x77, 0x7e, + 0x0, 0x42, 0x7e, 0x52, 0x0, 0x52, 0xff, 0x52, + 0x40, 0x52, 0x7e, 0x52, 0x2, 0x5a, 0x2, 0x2c, + 0x2, 0x46, 0x1c, 0x82, + + /* U+989B "颛" */ + 0x8, 0x0, 0x4a, 0xff, 0x4a, 0x10, 0x4a, 0x10, + 0x34, 0x7e, 0x0, 0x42, 0xff, 0x52, 0x10, 0x52, + 0xff, 0x52, 0x95, 0x52, 0x95, 0x52, 0x95, 0x52, + 0x95, 0x2c, 0x95, 0x66, 0x83, 0x80, + + /* U+989C "颜" */ + 0x8, 0x0, 0x8, 0xff, 0x7f, 0x10, 0x22, 0x20, + 0x14, 0xfe, 0x7f, 0x92, 0x40, 0x92, 0x46, 0x92, + 0x58, 0x92, 0x43, 0x92, 0x4c, 0x92, 0x50, 0x38, + 0x43, 0x2c, 0x4c, 0x42, 0x10, 0x80, + + /* U+989D "额" */ + 0x10, 0x0, 0x21, 0xff, 0xf8, 0x44, 0x11, 0x2, + 0xf, 0xcf, 0x90, 0x91, 0x21, 0x54, 0x4a, 0x10, + 0x94, 0x5d, 0x2b, 0x2, 0x53, 0xe4, 0xa4, 0x4a, + 0x8, 0x87, 0x1f, 0x11, 0x22, 0x41, + + /* U+989E "颞" */ + 0xff, 0x7e, 0x84, 0x21, 0xf8, 0x42, 0x13, 0xf7, + 0xe4, 0x28, 0x48, 0x7f, 0xd4, 0x80, 0x29, 0xf7, + 0x52, 0x22, 0xa5, 0x89, 0x69, 0x21, 0xa5, 0x76, + 0x31, 0x18, 0x40, + + /* U+989F "颟" */ + 0x20, 0x0, 0x24, 0x7f, 0xff, 0x10, 0x24, 0x20, + 0x0, 0x7e, 0xff, 0x42, 0x14, 0x42, 0x14, 0x52, + 0xff, 0x52, 0x95, 0x52, 0x95, 0x52, 0xa7, 0x52, + 0xc9, 0x52, 0x81, 0x28, 0x81, 0x46, 0x86, 0x82, + + /* U+98A0 "颠" */ + 0x8, 0x0, 0xff, 0x7e, 0x8, 0x10, 0x7e, 0x10, + 0x42, 0x6e, 0x7e, 0x42, 0x42, 0x52, 0x7e, 0x52, + 0x42, 0x52, 0x7e, 0x52, 0x42, 0x52, 0xff, 0x5a, + 0x0, 0x2c, 0x66, 0x42, 0x80, 0x80, + + /* U+98A1 "颡" */ + 0x7e, 0xfe, 0x84, 0x20, 0xf0, 0x83, 0x33, 0xef, + 0xf8, 0x43, 0x32, 0xb9, 0xa5, 0x33, 0x4a, 0x99, + 0x95, 0xff, 0x28, 0x40, 0x51, 0xf1, 0x4d, 0x4, + 0x42, 0x10, 0x0, + + /* U+98A2 "颢" */ + 0x7e, 0xff, 0x42, 0x10, 0x7e, 0x20, 0x7e, 0xde, + 0x10, 0x82, 0xff, 0x92, 0x0, 0x92, 0x7e, 0x92, + 0x42, 0x92, 0x7e, 0x92, 0x8, 0x1a, 0x4a, 0x2c, + 0x88, 0x46, 0x31, 0x82, + + /* U+98A4 "颤" */ + 0x8, 0x1, 0xfe, 0xfc, 0x0, 0x47, 0xf9, 0xb, + 0xd7, 0xd4, 0xa8, 0xa6, 0x55, 0x7f, 0xaa, 0x0, + 0x54, 0xfc, 0xa9, 0xf9, 0x52, 0x12, 0xa3, 0xc2, + 0x9f, 0xe8, 0x80, 0x20, 0x0, + + /* U+98A5 "颥" */ + 0x7e, 0xfe, 0x20, 0x23, 0xfc, 0x87, 0xbe, 0xe1, + 0x8, 0x4e, 0xd2, 0x80, 0x25, 0x3f, 0x4a, 0x7f, + 0x95, 0x53, 0x2a, 0xa4, 0xf5, 0x49, 0x48, 0x14, + 0x50, 0x50, 0x0, + + /* U+98A6 "颦" */ + 0x8, 0x0, 0x9e, 0xf9, 0x20, 0x87, 0xfe, 0xe2, + 0x99, 0x59, 0xc5, 0x1c, 0xb1, 0xdf, 0xfc, 0x20, + 0x88, 0x7f, 0xf0, 0x84, 0x21, 0xff, 0xcf, 0xff, + 0xe0, 0x10, 0x0, 0x20, 0x0, + + /* U+98A7 "颧" */ + 0x24, 0x1, 0xfe, 0xf8, 0x90, 0x47, 0xb9, 0x9, + 0x95, 0xde, 0xea, 0x8a, 0x15, 0x3f, 0xaa, 0xc8, + 0x55, 0xfe, 0xa9, 0x21, 0x53, 0xfb, 0xa4, 0x82, + 0x8f, 0xe8, 0x90, 0x20, 0x0, + + /* U+98CE "风" */ + 0x3f, 0xf8, 0x20, 0x8, 0x28, 0x28, 0x28, 0x48, + 0x24, 0x48, 0x22, 0x88, 0x21, 0x88, 0x21, 0x8, + 0x22, 0x88, 0x22, 0x48, 0x24, 0x49, 0x48, 0x2a, + 0x40, 0xe, 0x80, 0x6, + + /* U+98D1 "飑" */ + 0x0, 0x40, 0xfc, 0x81, 0x9, 0xf2, 0x14, 0x24, + 0x30, 0x4c, 0xfe, 0x9a, 0xa5, 0x2d, 0x4a, 0x4a, + 0xe4, 0x95, 0x71, 0x6a, 0xa, 0xd4, 0x16, 0x5f, + 0xcc, 0xb0, 0x60, 0x30, 0xc0, 0x1e, + + /* U+98D2 "飒" */ + 0x20, 0x0, 0x47, 0xf0, 0x8, 0x27, 0xd0, 0x40, + 0x32, 0x92, 0x65, 0x24, 0xb2, 0x29, 0x64, 0x54, + 0x48, 0xa9, 0x91, 0x53, 0x21, 0x29, 0x5f, 0xd2, + 0xa0, 0x81, 0x42, 0x1, 0x0, + + /* U+98D3 "飓" */ + 0x7e, 0xfc, 0x42, 0x84, 0x42, 0xfc, 0x66, 0x84, + 0x6a, 0xfc, 0x5a, 0x84, 0x5a, 0xfc, 0x4a, 0x84, + 0x5b, 0xfe, 0x5a, 0x0, 0x5a, 0x48, 0x65, 0x84, + 0x80, 0x82, 0x80, 0x7e, + + /* U+98D5 "飕" */ + 0x0, 0x10, 0x0, 0x10, 0x7e, 0xde, 0x42, 0x92, + 0x46, 0xd6, 0x6a, 0x92, 0x5a, 0xfe, 0x5a, 0x10, + 0x4a, 0xfe, 0x4a, 0x4, 0x5a, 0x6c, 0x5a, 0x18, + 0x66, 0x6c, 0x65, 0x81, 0x80, 0xc1, 0x80, 0x3e, + + /* U+98D8 "飘" */ + 0xfe, 0xfc, 0x51, 0xb, 0xfa, 0x15, 0x56, 0xaa, + 0xad, 0x5f, 0xd6, 0x80, 0x2d, 0x3e, 0x52, 0x1, + 0x35, 0xfe, 0xa8, 0x45, 0x52, 0xaa, 0x79, 0x10, + 0x66, 0x40, 0x40, + + /* U+98D9 "飙" */ + 0x9, 0xfc, 0x25, 0x4, 0xfd, 0x4, 0x11, 0x84, + 0x29, 0x94, 0x45, 0x54, 0x81, 0x54, 0x4d, 0x24, + 0x49, 0x24, 0xfd, 0x24, 0x49, 0x54, 0x49, 0x55, + 0x76, 0x95, 0x92, 0x86, 0x92, 0x2, + + /* U+98DA "飚" */ + 0x0, 0x10, 0x7e, 0x90, 0x42, 0x56, 0x42, 0x30, + 0x66, 0x4c, 0x6a, 0x82, 0x5a, 0x44, 0x5b, 0x65, + 0x4a, 0xee, 0x5a, 0x44, 0x5a, 0xea, 0x67, 0xa9, + 0x65, 0x11, 0x80, 0xc1, 0x80, 0x3e, + + /* U+98DE "飞" */ + 0xff, 0x80, 0x1, 0x8, 0x2, 0x20, 0x5, 0x80, + 0xe, 0x0, 0x1c, 0x0, 0x26, 0x0, 0x43, 0x0, + 0x80, 0x1, 0x0, 0x1, 0x8, 0x2, 0x10, 0x2, + 0x20, 0x3, 0x80, + + /* U+98DF "食" */ + 0x0, 0x80, 0x1, 0x80, 0x2, 0x40, 0xd, 0x30, + 0x10, 0x8c, 0x7f, 0xfa, 0x10, 0x8, 0x10, 0x8, + 0x1f, 0xf8, 0x10, 0x8, 0x1f, 0xf8, 0x10, 0x4, + 0x11, 0x98, 0x10, 0x60, 0x17, 0x1c, 0x18, 0x0, + + /* U+98E7 "飧" */ + 0x20, 0x40, 0x40, 0x80, 0x82, 0x81, 0xc8, 0x84, + 0xb4, 0x89, 0xc8, 0xd2, 0xfe, 0x65, 0x4, 0xb3, + 0xf8, 0x24, 0x10, 0x4f, 0xe1, 0x10, 0x22, 0x2d, + 0x88, 0x46, 0x30, 0xe3, 0x41, 0x0, + + /* U+98E8 "飨" */ + 0x10, 0x40, 0x41, 0x40, 0x82, 0xc2, 0x5a, 0xc8, + 0xe4, 0x7e, 0x7f, 0x8, 0x82, 0x25, 0xfc, 0x4a, + 0x9, 0xf7, 0xf0, 0x48, 0x10, 0x96, 0xc2, 0x23, + 0xc, 0x71, 0xa0, 0x80, 0x0, + + /* U+990D "餍" */ + 0x3f, 0xff, 0x20, 0x48, 0x2f, 0xfe, 0x20, 0xe0, + 0x23, 0x5c, 0x2c, 0xa2, 0x23, 0x58, 0x3f, 0xfe, + 0x24, 0x4, 0x27, 0xfc, 0x27, 0xfc, 0x44, 0x86, + 0x44, 0xf8, 0x87, 0x6, + + /* U+9910 "餐" */ + 0x0, 0x0, 0x7, 0x7e, 0x28, 0x42, 0x3f, 0x24, + 0x62, 0x18, 0x14, 0x64, 0x19, 0x82, 0x66, 0x60, + 0x9, 0x10, 0x3f, 0xfc, 0xd0, 0xa, 0x1f, 0xf8, + 0x1f, 0xf8, 0x11, 0x8, 0x11, 0xf0, 0x1e, 0xe, + + /* U+992E "餮" */ + 0x0, 0x20, 0xfc, 0xa0, 0x42, 0x31, 0xf9, 0x6, + 0x4c, 0x83, 0x86, 0x84, 0x72, 0x33, 0x78, 0x8, + 0x60, 0x7f, 0xf3, 0x80, 0x59, 0xff, 0x83, 0xff, + 0x4, 0x43, 0x8, 0xf8, 0x1e, 0xe, + + /* U+9954 "饔" */ + 0x2, 0x1, 0xff, 0xfc, 0x84, 0x86, 0x57, 0xc3, + 0x7f, 0xc9, 0x44, 0xe, 0xfe, 0x19, 0xfe, 0xc4, + 0xc1, 0xff, 0xfc, 0x80, 0x41, 0xff, 0x83, 0xff, + 0x84, 0x66, 0xb, 0xbc, 0x8, 0x4, + + /* U+9955 "饕" */ + 0x78, 0x7d, 0x10, 0x81, 0xef, 0xf7, 0xf7, 0xa4, + 0x27, 0x87, 0x5f, 0xe, 0x23, 0x3, 0xc0, 0x18, + 0x71, 0xff, 0xfc, 0xff, 0xc1, 0xff, 0x82, 0x21, + 0x85, 0xfe, 0xc, 0x3, 0x0, + + /* U+9963 "饣" */ + 0x0, 0x8, 0x4, 0x7, 0xf2, 0xa, 0x5, 0x24, + 0x10, 0x8, 0x4, 0x2, 0x1, 0x0, 0x90, 0x70, + 0x20, + + /* U+9965 "饥" */ + 0x20, 0x0, 0x83, 0xe1, 0xf4, 0x42, 0x28, 0x88, + 0x91, 0x15, 0x22, 0x8, 0x44, 0x10, 0x88, 0x21, + 0x10, 0x42, 0x20, 0x84, 0x41, 0x58, 0x93, 0x21, + 0x24, 0xc2, 0x41, 0x7, 0x0, 0x0, + + /* U+9967 "饧" */ + 0x23, 0xfd, 0x0, 0x37, 0xc1, 0x11, 0x8, 0x88, + 0xc2, 0x24, 0x2, 0x3f, 0xc8, 0x25, 0x21, 0x24, + 0x84, 0x92, 0x22, 0x48, 0x11, 0x28, 0x44, 0xc2, + 0x12, 0x33, 0x80, + + /* U+9968 "饨" */ + 0x20, 0x20, 0x20, 0x20, 0x3b, 0xfe, 0x48, 0x20, + 0x4a, 0x24, 0xa2, 0x24, 0x22, 0x24, 0x22, 0x24, + 0x23, 0xfc, 0x20, 0x20, 0x20, 0x20, 0x28, 0x23, + 0x38, 0x22, 0x30, 0x22, 0x20, 0x3e, + + /* U+9969 "饩" */ + 0x20, 0x80, 0x40, 0x80, 0x79, 0x7e, 0x4b, 0x0, + 0x96, 0xfc, 0xa4, 0x0, 0x20, 0x0, 0x27, 0xf8, + 0x20, 0x8, 0x20, 0x8, 0x20, 0x8, 0x24, 0x9, + 0x28, 0x9, 0x30, 0x6, 0x20, 0x6, + + /* U+996A "饪" */ + 0x20, 0x0, 0x87, 0xfd, 0xe0, 0x82, 0x41, 0x8, + 0x82, 0x15, 0x4, 0x8, 0x8, 0x13, 0xff, 0x20, + 0x20, 0x40, 0x40, 0x80, 0x81, 0x1, 0x3, 0x82, + 0x4, 0x7f, 0xc0, + + /* U+996B "饫" */ + 0x0, 0x0, 0x40, 0x8, 0x87, 0xe3, 0xe1, 0x4, + 0x42, 0x11, 0x4, 0x2a, 0x8, 0x11, 0xff, 0x20, + 0x20, 0x40, 0xc0, 0x81, 0x41, 0x2, 0x82, 0x88, + 0x86, 0x21, 0x88, 0xc1, 0x80, 0x1, + + /* U+996C "饬" */ + 0x20, 0x80, 0x41, 0x1, 0xe7, 0xfa, 0x52, 0x8, + 0xa4, 0x14, 0x8, 0x9, 0xff, 0x10, 0x22, 0x20, + 0x44, 0x40, 0x88, 0x82, 0x11, 0x44, 0x22, 0x90, + 0x46, 0x61, 0x89, 0x1e, 0x0, + + /* U+996D "饭" */ + 0x20, 0x7, 0x21, 0xf8, 0x7d, 0x0, 0x45, 0x0, + 0x49, 0x0, 0xa9, 0xfe, 0x21, 0x42, 0x21, 0x42, + 0x21, 0x24, 0x21, 0x24, 0x21, 0x18, 0x2a, 0x18, + 0x32, 0x38, 0x24, 0x66, 0xd, 0x81, + + /* U+996E "饮" */ + 0x20, 0x80, 0x40, 0x80, 0x7d, 0xfe, 0x49, 0x2, + 0x89, 0x22, 0xa2, 0x24, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x50, 0x20, 0x50, 0x20, 0x58, 0x2c, 0x88, + 0x31, 0x4, 0x6, 0x2, 0x4, 0x0, + + /* U+996F "饯" */ + 0x20, 0xb0, 0x81, 0x11, 0xf2, 0x2, 0x45, 0xf8, + 0xbc, 0x15, 0x10, 0x8, 0x23, 0x93, 0xf8, 0x20, + 0x44, 0x40, 0x90, 0xa1, 0x41, 0x43, 0x13, 0x1a, + 0x24, 0xc2, 0x40, 0x3, 0x0, + + /* U+9970 "饰" */ + 0x21, 0x0, 0x41, 0x0, 0x79, 0xff, 0x4a, 0x0, + 0x8a, 0x20, 0xa8, 0x20, 0x23, 0xfe, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x2a, 0x2e, 0x30, 0x20, 0x0, 0x20, + + /* U+9971 "饱" */ + 0x21, 0x0, 0x42, 0x1, 0xe7, 0xf2, 0x50, 0x24, + 0xa0, 0x55, 0xfc, 0x88, 0x89, 0x11, 0x12, 0x23, + 0xe4, 0x44, 0x8, 0x89, 0xe9, 0x50, 0x12, 0xa0, + 0x26, 0x40, 0x48, 0x7f, 0x0, + + /* U+9972 "饲" */ + 0x20, 0x0, 0x83, 0xfd, 0xf0, 0xa, 0x40, 0x18, + 0xbf, 0xb4, 0x0, 0x48, 0xfc, 0x91, 0x9, 0x22, + 0x12, 0x44, 0x24, 0x88, 0x49, 0x1f, 0x92, 0x80, + 0x26, 0x0, 0x48, 0xf, 0x0, + + /* U+9974 "饴" */ + 0x20, 0x40, 0x40, 0x81, 0xf2, 0x22, 0x24, 0x48, + 0x90, 0x54, 0x7f, 0x88, 0x0, 0x90, 0x0, 0x21, + 0xfc, 0x42, 0x8, 0x84, 0x11, 0x68, 0x23, 0x90, + 0x46, 0x3f, 0x88, 0x41, 0x0, + + /* U+9975 "饵" */ + 0x20, 0x0, 0x47, 0xfd, 0xf4, 0x12, 0x28, 0x28, + 0x9f, 0xd4, 0x20, 0x88, 0x41, 0x10, 0x82, 0x21, + 0xfc, 0x42, 0x8, 0x84, 0x11, 0x48, 0x33, 0x3f, + 0xc4, 0x0, 0x80, 0x1, 0x0, + + /* U+9976 "饶" */ + 0x0, 0x80, 0x20, 0x80, 0x40, 0x9c, 0x7f, 0xe0, + 0x44, 0x4c, 0x88, 0x58, 0xa8, 0x62, 0x21, 0x92, + 0x22, 0xc, 0x20, 0x0, 0x23, 0xfe, 0x20, 0x90, + 0x20, 0x90, 0x28, 0x92, 0x31, 0x12, 0x6, 0xe, + + /* U+9977 "饷" */ + 0x20, 0x40, 0x41, 0x1, 0xef, 0xfa, 0x50, 0x14, + 0xa0, 0x35, 0x5e, 0x48, 0xa4, 0x91, 0x49, 0x22, + 0x92, 0x45, 0x24, 0x8b, 0xc9, 0x10, 0x12, 0xa0, + 0x26, 0x40, 0x40, 0x87, 0x80, + + /* U+997A "饺" */ + 0x20, 0x40, 0x80, 0x41, 0xef, 0xfa, 0x40, 0x8, + 0x89, 0x14, 0x21, 0x88, 0x81, 0x92, 0x85, 0x21, + 0x10, 0x41, 0x20, 0x81, 0x81, 0x43, 0x3, 0x9, + 0x0, 0x61, 0xc1, 0x0, 0x0, + + /* U+997C "饼" */ + 0x21, 0x8, 0x41, 0x11, 0xe2, 0x42, 0x5f, 0xf8, + 0x90, 0x91, 0x21, 0x28, 0x42, 0x10, 0x84, 0x27, + 0xfe, 0x42, 0x10, 0x84, 0x21, 0x8, 0x42, 0x90, + 0x86, 0x41, 0x1, 0x2, 0x0, + + /* U+997D "饽" */ + 0x20, 0x20, 0x47, 0xf8, 0xe0, 0x82, 0x41, 0x5, + 0x7f, 0xf4, 0x80, 0x49, 0x7e, 0x90, 0xc, 0x20, + 0x30, 0x40, 0x40, 0x9f, 0xf9, 0x41, 0x3, 0x2, + 0x4, 0x4, 0x0, 0x70, 0x0, + + /* U+997F "饿" */ + 0x20, 0x10, 0x20, 0xd0, 0x47, 0x14, 0x79, 0x10, + 0x49, 0x10, 0x8f, 0xfe, 0xa1, 0x10, 0x21, 0x10, + 0x21, 0x54, 0x27, 0x94, 0x21, 0x18, 0x21, 0x10, + 0x39, 0x2b, 0x21, 0x4a, 0x7, 0x6, + + /* U+9980 "馀" */ + 0x20, 0x20, 0x40, 0xc0, 0xe2, 0x42, 0x48, 0x44, + 0xa0, 0x54, 0xbf, 0x8, 0x10, 0x10, 0x20, 0x27, + 0xfc, 0x40, 0x80, 0x85, 0x1, 0x5a, 0x43, 0x24, + 0x44, 0x88, 0x0, 0x30, 0x0, + + /* U+9981 "馁" */ + 0x0, 0x0, 0x40, 0x7d, 0xf, 0x3, 0xc2, 0x24, + 0xa2, 0x51, 0x25, 0x28, 0x40, 0x10, 0x40, 0x27, + 0xfe, 0x42, 0x10, 0x84, 0x21, 0x8, 0x82, 0x8e, + 0x6, 0xa, 0x8, 0xe3, 0x82, 0x0, + + /* U+9984 "馄" */ + 0x20, 0x0, 0x23, 0xfe, 0x42, 0x2, 0x7b, 0xfe, + 0x8a, 0x2, 0x8a, 0x2, 0x23, 0xfe, 0x20, 0x0, + 0x22, 0x10, 0x23, 0xd6, 0x22, 0x18, 0x22, 0x11, + 0x2a, 0x11, 0x33, 0xd1, 0x2, 0x1e, + + /* U+9985 "馅" */ + 0x20, 0x40, 0x41, 0x1, 0xf3, 0xf2, 0x48, 0x64, + 0xa0, 0x94, 0x0, 0x8, 0xf7, 0x91, 0x1, 0x22, + 0x2, 0x44, 0x4, 0x8f, 0x79, 0x10, 0x13, 0xa0, + 0x24, 0x7f, 0xc0, 0x80, 0x80, + + /* U+9986 "馆" */ + 0x20, 0x20, 0x40, 0x41, 0xf, 0xfb, 0xd0, 0x14, + 0x9f, 0xd5, 0x20, 0x88, 0x41, 0x10, 0xfe, 0x21, + 0x0, 0x42, 0x0, 0x87, 0xf9, 0x48, 0x13, 0x10, + 0x24, 0x3f, 0xc0, 0x40, 0x80, + + /* U+9987 "馇" */ + 0x0, 0x20, 0x40, 0x41, 0xf, 0xfb, 0xc3, 0x4, + 0x8b, 0x11, 0x65, 0xa8, 0x0, 0x90, 0xfe, 0x22, + 0x4, 0x44, 0x8, 0x87, 0xf1, 0x10, 0x22, 0x9f, + 0xc6, 0x0, 0x9, 0xff, 0x80, + + /* U+9988 "馈" */ + 0x20, 0x40, 0x87, 0xf9, 0xe9, 0x12, 0x52, 0x28, + 0xbf, 0xd4, 0x8, 0x9, 0xff, 0x90, 0x0, 0x23, + 0xfc, 0x44, 0x8, 0x89, 0x11, 0x52, 0x23, 0x7, + 0x4, 0x31, 0x81, 0x80, 0x80, + + /* U+998A "馊" */ + 0x20, 0x20, 0x47, 0x5d, 0x8, 0x8b, 0xd1, 0x18, + 0xba, 0xf5, 0x44, 0x48, 0xff, 0x90, 0x10, 0x20, + 0x20, 0x47, 0xf8, 0x84, 0x11, 0x4, 0x43, 0x85, + 0x4, 0x1f, 0x1, 0xc1, 0x80, + + /* U+998B "馋" */ + 0x20, 0x80, 0x21, 0xf8, 0x7b, 0x8, 0x4b, 0xfe, + 0x8a, 0x22, 0x8a, 0x22, 0x23, 0xfe, 0x20, 0x50, + 0x20, 0x91, 0x23, 0x1e, 0x24, 0xc0, 0x28, 0x38, + 0x33, 0x80, 0x20, 0x78, 0x0, 0x4, + + /* U+998D "馍" */ + 0x20, 0x10, 0x42, 0x20, 0x9f, 0xfb, 0xc8, 0x84, + 0x80, 0x11, 0x7f, 0xa8, 0x81, 0x11, 0xfe, 0x22, + 0x4, 0x47, 0xf8, 0x81, 0x1, 0x2, 0x2, 0x7f, + 0xe5, 0xc, 0xc, 0x24, 0x13, 0x87, 0x0, 0x0, + + /* U+998F "馏" */ + 0x21, 0xc0, 0x44, 0x3d, 0xe8, 0x4a, 0x54, 0x94, + 0xa5, 0x30, 0x7c, 0x48, 0x8b, 0x90, 0x0, 0x23, + 0xfe, 0x44, 0x44, 0x8f, 0xf9, 0x51, 0x13, 0x22, + 0x24, 0x7f, 0xc0, 0x80, 0x80, + + /* U+9990 "馐" */ + 0x21, 0x8, 0x41, 0x21, 0xf, 0xfb, 0xc2, 0x8, + 0xbf, 0xd5, 0x10, 0x9, 0xff, 0x90, 0x80, 0x21, + 0x0, 0x43, 0xf8, 0xa9, 0x11, 0xaf, 0xe2, 0x84, + 0x80, 0x7f, 0xc0, + + /* U+9991 "馑" */ + 0x22, 0x8, 0x8f, 0xfd, 0xe8, 0x22, 0x5f, 0xc5, + 0x4, 0x12, 0xff, 0xa9, 0x11, 0x13, 0xfe, 0x20, + 0x40, 0x4f, 0xf8, 0x81, 0x1, 0xbf, 0xe3, 0x4, + 0x5, 0xff, 0xc0, + + /* U+9992 "馒" */ + 0x23, 0xfc, 0x44, 0x9, 0xef, 0xf2, 0x50, 0x25, + 0x3f, 0xd0, 0x0, 0x9, 0xff, 0x92, 0x49, 0x27, + 0xfe, 0x40, 0x0, 0x8f, 0xf1, 0x48, 0x43, 0x9, + 0x4, 0x1e, 0x3, 0xc3, 0x80, + + /* U+9993 "馓" */ + 0x22, 0x40, 0x22, 0x48, 0x47, 0xe8, 0x7a, 0x4f, + 0x52, 0x52, 0x87, 0xf2, 0xa0, 0x1a, 0x23, 0xca, + 0x24, 0x4a, 0x23, 0xcc, 0x24, 0x44, 0x23, 0xc4, + 0x2c, 0x4c, 0x34, 0x52, 0x5, 0xf1, + + /* U+9994 "馔" */ + 0x20, 0x0, 0x47, 0xbc, 0x91, 0x4b, 0xe2, 0x94, + 0xbd, 0xf0, 0x8a, 0x68, 0xf7, 0x90, 0x0, 0x20, + 0x8, 0x4f, 0xfc, 0x84, 0x21, 0x8, 0x42, 0x7f, + 0xe5, 0x0, 0xc, 0x62, 0x13, 0x3, + + /* U+9995 "馕" */ + 0x20, 0x20, 0x27, 0xff, 0x43, 0xfe, 0x7b, 0xfe, + 0x48, 0x20, 0xb7, 0xdf, 0x23, 0xde, 0x23, 0xde, + 0x27, 0xff, 0x20, 0x88, 0x21, 0xfc, 0x2f, 0xff, + 0x28, 0xe2, 0x37, 0x1c, 0x1, 0xe7, + + /* U+9996 "首" */ + 0x8, 0x20, 0x8, 0x83, 0xff, 0xf8, 0x8, 0x0, + 0x20, 0x7, 0xff, 0x8, 0x2, 0x10, 0x4, 0x3f, + 0xf8, 0x40, 0x10, 0xff, 0xe1, 0x0, 0x42, 0x0, + 0x87, 0xff, 0x8, 0x2, 0x0, + + /* U+9997 "馗" */ + 0x20, 0x88, 0x20, 0x90, 0x23, 0xfe, 0xf8, 0x40, + 0x29, 0xfc, 0x29, 0x4, 0x29, 0xfc, 0x29, 0x4, + 0x29, 0x4, 0x29, 0xfc, 0x69, 0x4, 0x49, 0xfd, + 0x48, 0x1, 0xc8, 0x2, 0x87, 0xfe, + + /* U+9998 "馘" */ + 0x0, 0x10, 0x48, 0x14, 0x8, 0x10, 0xfd, 0xfe, + 0x20, 0x10, 0x20, 0x10, 0x78, 0x10, 0x89, 0xd2, + 0x79, 0x54, 0x89, 0x54, 0x89, 0xd4, 0x78, 0x8, + 0x88, 0x18, 0x8b, 0xda, 0x78, 0x2a, 0x88, 0x46, + + /* U+9999 "香" */ + 0x7f, 0xf0, 0x8, 0xf, 0xff, 0xc1, 0xa0, 0xa, + 0x40, 0xc8, 0xcc, 0x0, 0xcf, 0xfc, 0x20, 0x10, + 0x80, 0x43, 0xff, 0x8, 0x4, 0x3f, 0xf0, 0x80, + 0x40, + + /* U+99A5 "馥" */ + 0x3c, 0x81, 0xc3, 0xf8, 0x8c, 0x7, 0xef, 0xe2, + 0x10, 0x4f, 0x3f, 0xa9, 0x41, 0x0, 0x82, 0xf9, + 0xfd, 0x11, 0xfb, 0xe6, 0x34, 0x52, 0xc8, 0x83, + 0x1f, 0x1b, 0x22, 0xc1, 0x0, + + /* U+99A8 "馨" */ + 0x10, 0x79, 0xff, 0x10, 0x42, 0x19, 0x67, 0xef, + 0xec, 0x8f, 0xdf, 0x20, 0x1, 0xf, 0xf0, 0xff, + 0xfc, 0x7, 0x1, 0xf5, 0xe5, 0x77, 0xa3, 0xff, + 0x4, 0x2, 0xf, 0xfc, 0x0, + + /* U+9A6C "马" */ + 0x7f, 0xf0, 0x40, 0x41, 0x2, 0x8, 0x8, 0x20, + 0x20, 0x80, 0x83, 0xff, 0xc0, 0x1, 0x0, 0x7, + 0xff, 0x90, 0x0, 0x40, 0x1, 0x0, 0x4, 0x3, + 0xe0, + + /* U+9A6D "驭" */ + 0xfb, 0xfc, 0x12, 0xa, 0x24, 0x14, 0x44, 0x48, + 0x88, 0x91, 0x11, 0x3f, 0x12, 0x2, 0x28, 0x4, + 0x71, 0xe8, 0x40, 0x11, 0x40, 0x24, 0x40, 0xd8, + 0x4f, 0x60, 0x40, + + /* U+9A6E "驮" */ + 0x0, 0x20, 0xf8, 0x40, 0x20, 0x82, 0x41, 0x4, + 0x9f, 0xe9, 0x4, 0x12, 0x8, 0x3e, 0x18, 0x4, + 0x50, 0x8, 0xa1, 0xd1, 0x40, 0x26, 0x40, 0x48, + 0x80, 0xa0, 0x8e, 0xc0, 0x80, + + /* U+9A6F "驯" */ + 0xf8, 0x92, 0x11, 0x25, 0x22, 0x4a, 0x44, 0x94, + 0x89, 0x29, 0x12, 0x5f, 0x24, 0x82, 0x49, 0x4, + 0x92, 0xe9, 0x26, 0x12, 0x48, 0x28, 0x90, 0xd1, + 0x27, 0x40, 0x40, + + /* U+9A70 "驰" */ + 0x0, 0x21, 0xe0, 0x40, 0x48, 0x84, 0x91, 0x69, + 0x3f, 0x52, 0xc4, 0xa4, 0x89, 0x7d, 0x12, 0xa, + 0x24, 0x14, 0x53, 0xa8, 0x88, 0x50, 0x10, 0xa0, + 0x41, 0x40, 0x9c, 0x7e, 0x0, + + /* U+9A71 "驱" */ + 0xf9, 0xfe, 0x12, 0x1, 0x25, 0x12, 0x4b, 0x44, + 0x92, 0x89, 0x23, 0x1f, 0x44, 0x2, 0x8c, 0x5, + 0x29, 0xea, 0x88, 0x15, 0x0, 0x28, 0x0, 0x5f, + 0xef, 0x0, 0x0, + + /* U+9A73 "驳" */ + 0x7d, 0x84, 0x4, 0x78, 0x48, 0x30, 0x48, 0xcc, + 0x49, 0x2, 0x48, 0x84, 0x7c, 0x88, 0x4, 0x48, + 0x4, 0x48, 0x74, 0x30, 0x4, 0x30, 0x4, 0x58, + 0x4, 0x86, 0x3b, 0x3, + + /* U+9A74 "驴" */ + 0x0, 0x10, 0xf0, 0x20, 0x23, 0xfa, 0x44, 0x14, + 0x88, 0x29, 0x10, 0x52, 0x20, 0xbe, 0x7f, 0x4, + 0x82, 0x9, 0x3, 0xd2, 0x0, 0x24, 0x0, 0x50, + 0x1, 0xa0, 0xe, 0xc0, 0x0, 0x0, + + /* U+9A75 "驵" */ + 0xf1, 0xfc, 0x22, 0xa, 0x44, 0x14, 0x88, 0x29, + 0x1f, 0xd2, 0x20, 0xbe, 0x41, 0x4, 0x82, 0x9, + 0xfc, 0xd2, 0xa, 0x24, 0x10, 0x48, 0x20, 0x90, + 0x4e, 0xff, 0xc0, + + /* U+9A76 "驶" */ + 0x0, 0x20, 0x78, 0x20, 0x9, 0xfe, 0x49, 0x22, + 0x49, 0x22, 0x49, 0x22, 0x49, 0x22, 0x7d, 0xfe, + 0x4, 0x22, 0x5, 0x20, 0xf4, 0xa0, 0x4, 0xc0, + 0x4, 0x40, 0x4, 0xb8, 0x7b, 0x6, + + /* U+9A77 "驷" */ + 0xf3, 0xfc, 0x4a, 0x59, 0x29, 0x64, 0xa5, 0x92, + 0x96, 0x4a, 0x5f, 0x29, 0x44, 0xa5, 0x13, 0x1f, + 0xcc, 0x11, 0x20, 0x44, 0x81, 0x13, 0xfd, 0xc8, + 0x10, + + /* U+9A78 "驸" */ + 0x0, 0x80, 0xf1, 0x11, 0x24, 0x22, 0x4b, 0xf4, + 0xb0, 0x89, 0xa1, 0x12, 0x52, 0x3e, 0x94, 0x5, + 0x28, 0xa, 0x50, 0xd4, 0x26, 0x28, 0x40, 0x90, + 0x81, 0x21, 0xe, 0x4e, 0x0, + + /* U+9A79 "驹" */ + 0x1, 0x3, 0xc4, 0x1, 0x3f, 0xe4, 0x81, 0x94, + 0x6, 0x4f, 0x99, 0x22, 0x7e, 0x89, 0xa, 0x24, + 0x28, 0x9e, 0xbe, 0x42, 0x1, 0x8, 0x4, 0x20, + 0x37, 0xf, 0x80, + + /* U+9A7A "驺" */ + 0x0, 0x83, 0xc2, 0x1, 0x1f, 0x84, 0x42, 0x92, + 0x12, 0x40, 0x89, 0x3f, 0xe4, 0x1, 0xf8, 0x4, + 0x20, 0x10, 0xbf, 0xfa, 0x1, 0x8, 0x4, 0x20, + 0x11, 0x7f, 0xdc, 0x1, + + /* U+9A7B "驻" */ + 0x0, 0x1, 0xf8, 0x40, 0x10, 0x80, 0x5f, 0xf4, + 0x82, 0x9, 0x4, 0x12, 0x8, 0x24, 0x10, 0x35, + 0xfe, 0x8, 0x40, 0x10, 0x87, 0xa1, 0x0, 0x42, + 0x0, 0x84, 0x1, 0xff, 0x9c, 0x0, + + /* U+9A7C "驼" */ + 0x0, 0x20, 0xf0, 0x20, 0x27, 0xfa, 0x48, 0x14, + 0x90, 0x29, 0x0, 0x12, 0x21, 0x3e, 0x44, 0x4, + 0x90, 0x9, 0xc0, 0xd2, 0x6, 0x24, 0x10, 0x48, + 0x20, 0x90, 0xce, 0x3f, 0x0, + + /* U+9A7D "驽" */ + 0x10, 0x0, 0x21, 0xfb, 0xfb, 0x11, 0x12, 0x44, + 0x42, 0x87, 0x2, 0xd, 0xbb, 0x60, 0x1, 0x3f, + 0xf0, 0x0, 0x20, 0x40, 0x40, 0xff, 0xe0, 0x0, + 0x5f, 0xfc, 0x80, 0x1, 0x0, 0x3c, + + /* U+9A7E "驾" */ + 0x20, 0x0, 0x80, 0xf, 0xef, 0xc9, 0x21, 0x44, + 0x87, 0x71, 0xf0, 0x0, 0x1f, 0xfc, 0x20, 0x10, + 0x80, 0x83, 0xff, 0xc0, 0x1, 0x0, 0x7, 0xff, + 0x90, 0x0, 0x40, 0x1e, + + /* U+9A7F "驿" */ + 0xfb, 0xfc, 0x12, 0x9, 0x22, 0x22, 0x42, 0x84, + 0x87, 0x9, 0x70, 0xdf, 0x8, 0x2, 0xfe, 0x4, + 0x21, 0xc8, 0x40, 0x1f, 0xf8, 0x21, 0x0, 0xc2, + 0x7, 0x4, 0x0, + + /* U+9A80 "骀" */ + 0x0, 0x20, 0xf8, 0x40, 0x8, 0x40, 0x48, 0x84, + 0x49, 0x4, 0x4b, 0xfe, 0x48, 0x1, 0x7c, 0x0, + 0x5, 0xfe, 0x5, 0x2, 0xf5, 0x2, 0x5, 0x2, + 0x5, 0x2, 0x5, 0xfe, 0x39, 0x2, + + /* U+9A81 "骁" */ + 0x0, 0x80, 0x78, 0x80, 0x4b, 0xfc, 0x48, 0x44, + 0x48, 0x58, 0x48, 0x62, 0x4b, 0x92, 0x7c, 0x4, + 0x8, 0x0, 0xb, 0xfe, 0x8, 0xd0, 0xe8, 0x90, + 0x8, 0x91, 0x9, 0x92, 0x3a, 0xe, + + /* U+9A82 "骂" */ + 0xfc, 0xfc, 0x24, 0x61, 0x23, 0xf9, 0xf0, 0x0, + 0x3f, 0xf8, 0x0, 0x44, 0x4, 0x20, 0x21, 0xff, + 0xc0, 0x3, 0xff, 0xd0, 0x0, 0x80, 0x4, 0x3, + 0xc0, + + /* U+9A84 "骄" */ + 0xf3, 0xfe, 0x10, 0x20, 0x90, 0x20, 0x93, 0xfe, + 0x90, 0x50, 0x90, 0x88, 0xf8, 0x8c, 0xb, 0x2, + 0x8, 0x88, 0x28, 0x88, 0xc8, 0x88, 0x8, 0x88, + 0x9, 0x8, 0x72, 0x8, + + /* U+9A85 "骅" */ + 0x0, 0x0, 0x0, 0x90, 0x78, 0x90, 0x9, 0x16, + 0x4b, 0x18, 0x9f, 0x30, 0x95, 0xd2, 0x91, 0x12, + 0x79, 0x1e, 0x9, 0x0, 0x8, 0x20, 0xeb, 0xff, + 0x8, 0x20, 0x8, 0x20, 0x8, 0x20, 0x70, 0x20, + + /* U+9A86 "骆" */ + 0x0, 0x40, 0xf0, 0x80, 0x23, 0xf2, 0x44, 0x24, + 0x98, 0x89, 0x9, 0x12, 0xc, 0x24, 0x38, 0x7c, + 0x8c, 0xa, 0x4, 0x13, 0xf3, 0xa8, 0x20, 0x50, + 0x40, 0xa0, 0x82, 0x3f, 0x1c, 0x82, + + /* U+9A87 "骇" */ + 0x0, 0x10, 0x7c, 0x10, 0x9, 0xff, 0x48, 0x20, + 0x48, 0x44, 0x48, 0x84, 0x49, 0xf8, 0x7c, 0x12, + 0x4, 0x22, 0x4, 0x44, 0x75, 0x88, 0x5, 0x18, + 0x4, 0x24, 0x4, 0xc2, 0x39, 0x2, + + /* U+9A88 "骈" */ + 0x0, 0x84, 0xf8, 0x44, 0x8, 0x48, 0x49, 0xfe, + 0x48, 0x88, 0x48, 0x88, 0x48, 0x88, 0x7c, 0x88, + 0x5, 0xff, 0x4, 0x88, 0x74, 0x88, 0x4, 0x88, + 0x4, 0x88, 0x5, 0x8, 0x39, 0x8, + + /* U+9A8A "骊" */ + 0xf3, 0xfe, 0xa0, 0x1, 0x4f, 0x7a, 0x92, 0x95, + 0x25, 0x2a, 0x4b, 0x5e, 0xb6, 0x85, 0x6b, 0xa, + 0xd6, 0xd4, 0xa4, 0x29, 0x48, 0x52, 0x90, 0xa5, + 0x26, 0x5a, 0xc0, + + /* U+9A8B "骋" */ + 0xf0, 0x20, 0x13, 0xfe, 0x12, 0x22, 0xd2, 0x22, + 0xd3, 0xfe, 0xd2, 0x22, 0xd3, 0xfe, 0xf8, 0x0, + 0xb, 0xff, 0x9, 0x0, 0xe9, 0xfc, 0x8, 0x4, + 0x8, 0x4, 0x8, 0x4, 0x70, 0x78, + + /* U+9A8C "验" */ + 0x0, 0x43, 0xc3, 0x9, 0xa, 0x24, 0x44, 0x96, + 0xe, 0x5f, 0xc9, 0x0, 0x3e, 0x0, 0xa, 0x48, + 0x29, 0x2e, 0xa5, 0x2, 0x84, 0x8, 0x20, 0x5f, + 0xf7, 0x0, 0x0, + + /* U+9A8F "骏" */ + 0x0, 0x21, 0xe0, 0x80, 0x46, 0x24, 0x9f, 0xe9, + 0x0, 0x32, 0x13, 0x24, 0xc1, 0xfc, 0x40, 0x8, + 0xfc, 0x13, 0x9, 0xaa, 0x24, 0x42, 0x80, 0x82, + 0x1, 0x1b, 0x9d, 0xc1, 0x80, + + /* U+9A90 "骐" */ + 0x1, 0x9, 0xe2, 0x10, 0x4f, 0xfc, 0x88, 0x49, + 0x10, 0x92, 0x1f, 0x24, 0x42, 0x7c, 0x7c, 0x9, + 0x8, 0x12, 0x13, 0xaf, 0xf8, 0x40, 0x0, 0x85, + 0x83, 0x31, 0x9c, 0xc0, 0x80, + + /* U+9A91 "骑" */ + 0x0, 0x21, 0xe0, 0x40, 0x4f, 0xf8, 0x81, 0x9, + 0x7, 0x12, 0x31, 0xa4, 0x80, 0x4b, 0xff, 0xf8, + 0x4, 0x17, 0xc8, 0x28, 0x97, 0x51, 0x20, 0xbe, + 0x41, 0x0, 0x82, 0x1, 0x38, 0x1e, + + /* U+9A92 "骒" */ + 0x79, 0xfe, 0x9, 0x12, 0x49, 0xfe, 0x49, 0x12, + 0x49, 0x12, 0x49, 0xfe, 0x7c, 0x10, 0x4, 0x10, + 0x5, 0xff, 0x34, 0x38, 0xc4, 0x58, 0x4, 0x94, + 0x7, 0x13, 0x78, 0x10, + + /* U+9A93 "骓" */ + 0x0, 0x40, 0x78, 0x90, 0x8, 0x80, 0x8, 0xff, + 0x49, 0x90, 0x49, 0x90, 0x4a, 0x90, 0x48, 0xff, + 0x7c, 0x90, 0x4, 0x90, 0x4, 0xff, 0x74, 0x90, + 0x4, 0x90, 0x4, 0x90, 0x4, 0xff, 0x38, 0x80, + + /* U+9A96 "骖" */ + 0x0, 0x21, 0xf0, 0x81, 0x26, 0x22, 0x8f, 0xe5, + 0x2, 0x12, 0x7f, 0xe4, 0x24, 0x7c, 0x94, 0xa, + 0x66, 0x13, 0x11, 0xa0, 0xc4, 0x46, 0x20, 0x91, + 0x81, 0xc, 0x1c, 0x60, 0x1, 0x0, + + /* U+9A97 "骗" */ + 0x0, 0x21, 0xf0, 0x40, 0x2f, 0xfa, 0x50, 0x15, + 0x20, 0x2a, 0x7f, 0xd4, 0x80, 0x49, 0x7f, 0x7b, + 0x2a, 0x16, 0x54, 0x2c, 0xaf, 0xd7, 0xf0, 0xb2, + 0xa1, 0x65, 0x43, 0x4a, 0xba, 0x83, + + /* U+9A98 "骘" */ + 0x78, 0x20, 0x94, 0x79, 0x48, 0x82, 0xbf, 0xf4, + 0x82, 0x9, 0xe7, 0x1c, 0x38, 0x21, 0x80, 0x7f, + 0xf0, 0x40, 0x40, 0xff, 0xf0, 0x0, 0x4f, 0xfe, + 0x80, 0x1, 0x0, 0x3e, 0x0, + + /* U+9A9A "骚" */ + 0x79, 0xfe, 0x8, 0x92, 0x48, 0x44, 0x48, 0x38, + 0x48, 0xce, 0x49, 0x11, 0x7c, 0xfe, 0x5, 0x12, + 0x5, 0x12, 0xf4, 0xfe, 0x4, 0x12, 0x4, 0x12, + 0x5, 0xff, 0x38, 0x1, + + /* U+9A9B "骛" */ + 0x0, 0x20, 0xfe, 0x80, 0xd1, 0xf7, 0xfa, 0x41, + 0x53, 0xc, 0x8d, 0xa6, 0x20, 0x8f, 0xf8, 0x0, + 0x10, 0x20, 0x20, 0x7f, 0xf0, 0x0, 0x2f, 0xfe, + 0x40, 0x0, 0x80, 0x1e, 0x0, + + /* U+9A9C "骜" */ + 0x8, 0x20, 0xfc, 0xf9, 0xfd, 0x10, 0x46, 0x4f, + 0x73, 0x87, 0xc6, 0x10, 0x9b, 0x46, 0x40, 0x3f, + 0xf0, 0x20, 0x20, 0xff, 0xf0, 0x0, 0x2f, 0xfe, + 0x40, 0x1, 0x80, 0x3e, 0x0, + + /* U+9A9D "骝" */ + 0x0, 0x83, 0xdd, 0xf1, 0x42, 0x65, 0x49, 0x94, + 0xa6, 0x5e, 0x99, 0x45, 0xe4, 0x0, 0xf7, 0xfc, + 0x51, 0x1f, 0x7f, 0xc5, 0x11, 0x14, 0x44, 0x5f, + 0xff, 0x40, 0x40, + + /* U+9A9E "骞" */ + 0x1, 0x1, 0xff, 0xfe, 0x0, 0x9, 0xff, 0xc0, + 0x84, 0x7, 0xff, 0x82, 0x10, 0x7f, 0xff, 0x8, + 0x20, 0xff, 0xf2, 0x40, 0x90, 0x7e, 0xef, 0xfe, + 0x40, 0x1, 0x0, 0x3e, 0x0, + + /* U+9A9F "骟" */ + 0x0, 0x43, 0xc1, 0x1, 0x7f, 0xe5, 0x1, 0x94, + 0x6, 0x5f, 0xf9, 0x40, 0x25, 0x77, 0xf4, 0x44, + 0x55, 0x51, 0x55, 0x7d, 0x11, 0x15, 0xdc, 0x59, + 0x11, 0x84, 0x7e, 0x77, + + /* U+9AA0 "骠" */ + 0xf7, 0xfc, 0x21, 0x42, 0x5f, 0xf4, 0xa5, 0x29, + 0x4a, 0x52, 0xff, 0xa4, 0x0, 0x39, 0xfe, 0x10, + 0x0, 0xef, 0xfe, 0x41, 0x0, 0x9a, 0xc1, 0xc4, + 0x6e, 0x38, 0x0, + + /* U+9AA1 "骡" */ + 0xfb, 0xfe, 0xa, 0x22, 0x4b, 0xfe, 0x9a, 0x22, + 0x93, 0xfe, 0x90, 0xc4, 0x7b, 0xf8, 0x8, 0x60, + 0x9, 0x84, 0x7b, 0xfe, 0x88, 0x20, 0x9, 0xac, + 0xa, 0x22, 0x70, 0xe0, + + /* U+9AA2 "骢" */ + 0x0, 0x41, 0xe0, 0x80, 0x4e, 0xf2, 0x93, 0xe9, + 0x29, 0x52, 0x4c, 0xa4, 0x95, 0x35, 0x42, 0xb, + 0xfc, 0x10, 0x1, 0xa0, 0x84, 0x59, 0x20, 0xb0, + 0xa1, 0x91, 0x5c, 0x3c, 0x0, + + /* U+9AA3 "骣" */ + 0xfb, 0xfe, 0x14, 0x5, 0x4f, 0xfa, 0x90, 0x5, + 0x23, 0xca, 0x5f, 0xfe, 0x84, 0x5, 0x10, 0xa, + 0xef, 0xd4, 0x48, 0x29, 0x10, 0x6f, 0xf0, 0xc4, + 0x4e, 0xbb, 0x0, + + /* U+9AA4 "骤" */ + 0xfb, 0xe0, 0x14, 0xbc, 0x29, 0xa, 0x9e, 0xb5, + 0x3c, 0xca, 0x49, 0xa4, 0xfc, 0xfc, 0x20, 0xb, + 0xfc, 0x10, 0x43, 0xe4, 0xd0, 0x51, 0xc0, 0x8a, + 0x81, 0x24, 0xdd, 0x88, 0x80, + + /* U+9AA5 "骥" */ + 0x0, 0x50, 0x7b, 0xde, 0x8, 0xd1, 0x53, 0x4e, + 0x53, 0xfe, 0x52, 0x22, 0x53, 0xfe, 0x7a, 0x22, + 0xb, 0xfe, 0x8, 0x88, 0x3b, 0xfe, 0xc8, 0x88, + 0xb, 0xff, 0x8, 0x88, 0x7b, 0x7, + + /* U+9AA7 "骧" */ + 0x0, 0x20, 0x7b, 0xff, 0x8, 0x0, 0x4b, 0xde, + 0x53, 0xde, 0x50, 0x0, 0x53, 0xfe, 0x78, 0x88, + 0xb, 0xfe, 0x8, 0x88, 0x7b, 0xff, 0x88, 0x60, + 0x9, 0x9c, 0xb, 0x2c, 0x79, 0xc3, + + /* U+9AA8 "骨" */ + 0x3f, 0xf0, 0x80, 0x42, 0x1, 0xf, 0xe4, 0x20, + 0x93, 0xff, 0xf8, 0x0, 0x6f, 0xfd, 0x20, 0x10, + 0xff, 0xc2, 0x1, 0xf, 0xfc, 0x20, 0x10, 0x80, + 0x42, 0xf, 0x0, + + /* U+9AB0 "骰" */ + 0x7c, 0x78, 0x88, 0x91, 0xd1, 0x22, 0xa2, 0x45, + 0x48, 0x5f, 0xd0, 0xe0, 0x80, 0x7f, 0x7e, 0x44, + 0x44, 0xf8, 0x91, 0x11, 0x22, 0x21, 0x87, 0xc3, + 0x8, 0x9d, 0x97, 0x40, 0x80, + + /* U+9AB1 "骱" */ + 0x0, 0x10, 0xf8, 0x61, 0x10, 0xc3, 0xa2, 0x45, + 0x44, 0x8a, 0x90, 0xbf, 0xc0, 0xc1, 0x24, 0xfe, + 0x48, 0x88, 0x91, 0xf1, 0x22, 0x22, 0x47, 0xc4, + 0x88, 0x91, 0x11, 0x22, 0x2e, 0x84, + + /* U+9AB6 "骶" */ + 0x7c, 0xfc, 0x44, 0x90, 0x74, 0x90, 0x54, 0x90, + 0x54, 0x90, 0xfe, 0x90, 0x82, 0xff, 0xfe, 0x90, + 0x44, 0x90, 0x7c, 0x90, 0x44, 0x90, 0x44, 0xb2, + 0x7c, 0xca, 0x44, 0xaa, 0x4c, 0x14, + + /* U+9AB7 "骷" */ + 0x7e, 0x10, 0x84, 0x21, 0x8, 0x43, 0x97, 0xf5, + 0x21, 0x1f, 0xe2, 0x20, 0x44, 0x3f, 0x8, 0x42, + 0xec, 0xfd, 0x9, 0xa, 0x12, 0x14, 0x27, 0xe8, + 0x48, 0x5f, 0x93, 0xa1, 0x0, + + /* U+9AB8 "骸" */ + 0x0, 0x20, 0xf8, 0x41, 0x10, 0x83, 0xaf, 0xf5, + 0x44, 0xa, 0x88, 0xbf, 0xa2, 0x41, 0xfc, 0xfe, + 0x12, 0x88, 0x49, 0xf1, 0x12, 0x2c, 0x44, 0x41, + 0xf, 0x85, 0x11, 0x31, 0xae, 0x81, + + /* U+9ABA "骺" */ + 0x7c, 0xfc, 0x89, 0x1, 0xd2, 0x2, 0xa4, 0x5, + 0x4f, 0xff, 0xd0, 0x20, 0xa0, 0x3e, 0x40, 0x44, + 0xbc, 0xf9, 0x89, 0x13, 0x13, 0xe6, 0x24, 0x53, + 0xc8, 0xa8, 0x93, 0x11, 0x0, + + /* U+9ABC "骼" */ + 0x0, 0x0, 0x7c, 0x40, 0x44, 0x7e, 0x74, 0xc4, + 0x55, 0x4c, 0x54, 0x28, 0xfe, 0x30, 0x82, 0x38, + 0x7c, 0xc6, 0x44, 0x0, 0x7c, 0xfc, 0x44, 0x84, + 0x44, 0x84, 0x7c, 0x84, 0x44, 0xfc, 0x4c, 0x84, + + /* U+9AC0 "髀" */ + 0x0, 0x40, 0xf8, 0x81, 0x17, 0xf3, 0xa9, 0x25, + 0x52, 0x4a, 0xbf, 0xbf, 0xc9, 0x41, 0x92, 0xff, + 0x24, 0x8b, 0xf9, 0xf1, 0x2, 0x2c, 0x84, 0x7f, + 0xef, 0x82, 0x11, 0x4, 0x26, 0x8, + + /* U+9AC1 "髁" */ + 0x7c, 0xfc, 0x8a, 0x49, 0xd4, 0x92, 0xa7, 0xe5, + 0x52, 0x5f, 0xe4, 0xa0, 0xbf, 0x7f, 0x10, 0x44, + 0x20, 0x8b, 0xfd, 0xf1, 0x43, 0xe3, 0x44, 0x4a, + 0x48, 0xa4, 0x53, 0x8, 0x0, + + /* U+9AC2 "髂" */ + 0x0, 0x20, 0xf8, 0x41, 0x17, 0xfa, 0x28, 0x17, + 0x48, 0xa, 0x9f, 0xbf, 0xa3, 0x41, 0xac, 0x7c, + 0x20, 0x89, 0xb9, 0xf4, 0xa, 0x27, 0xe4, 0x50, + 0x4f, 0xa0, 0x91, 0x3f, 0x2c, 0x2, + + /* U+9AC5 "髅" */ + 0x0, 0x0, 0xf8, 0x49, 0x12, 0x93, 0xa1, 0x45, + 0x5f, 0xea, 0x86, 0x3f, 0x9a, 0x41, 0x53, 0x7d, + 0x2, 0x88, 0x81, 0xf7, 0xfa, 0x24, 0x44, 0x48, + 0x8f, 0x8e, 0x11, 0xe, 0x26, 0xe3, + + /* U+9ACB "髋" */ + 0x0, 0x0, 0x7c, 0x10, 0x45, 0xfe, 0x75, 0x2, + 0x54, 0x48, 0x55, 0xfe, 0xfe, 0x48, 0x82, 0x0, + 0x7c, 0xfc, 0x44, 0x84, 0x7c, 0x94, 0x44, 0x94, + 0x44, 0x30, 0x7c, 0x32, 0x44, 0xd3, 0x5f, 0xe, + + /* U+9ACC "髌" */ + 0x0, 0x0, 0xf8, 0x21, 0x17, 0xfb, 0xa8, 0x15, + 0x50, 0x2a, 0x9f, 0xbf, 0xa0, 0x41, 0x40, 0xfe, + 0xfe, 0x89, 0x11, 0xf2, 0x22, 0x2f, 0xf4, 0x40, + 0xf, 0x80, 0x11, 0x31, 0x2e, 0x81, + + /* U+9AD1 "髑" */ + 0x7d, 0xfd, 0x15, 0x57, 0x55, 0x55, 0x7f, 0x54, + 0x83, 0xfb, 0xf8, 0x34, 0x5f, 0x7d, 0x45, 0x55, + 0xf5, 0x54, 0x5f, 0x51, 0x15, 0x7d, 0xf5, 0x10, + 0x15, 0x81, 0xc0, + + /* U+9AD3 "髓" */ + 0x0, 0x10, 0xfc, 0x21, 0x15, 0xfb, 0xa9, 0x5, + 0x47, 0xea, 0x92, 0x3f, 0xdf, 0xc2, 0x80, 0xfd, + 0x7c, 0x8a, 0x89, 0xf5, 0xf2, 0x2b, 0xe4, 0x54, + 0x4f, 0xa9, 0x91, 0xa0, 0x2f, 0x3f, + + /* U+9AD8 "高" */ + 0x1, 0x0, 0x2, 0x3, 0xff, 0xf8, 0x0, 0x1, + 0xff, 0x82, 0x1, 0x4, 0x2, 0xf, 0xfc, 0x0, + 0x0, 0xff, 0xf9, 0x0, 0x12, 0x7f, 0x24, 0x82, + 0x49, 0xfc, 0x90, 0x1, 0x20, 0xe, + + /* U+9ADF "髟" */ + 0x0, 0x4, 0xfc, 0x11, 0x0, 0xc3, 0xf6, 0x4, + 0x0, 0x8, 0x0, 0x9f, 0x82, 0x20, 0x10, 0xff, + 0x40, 0x40, 0x4, 0x90, 0x12, 0x20, 0x44, 0xe3, + 0x6, 0x8, 0x0, 0x20, 0x0, + + /* U+9AE1 "髡" */ + 0x7e, 0xc, 0x80, 0xe1, 0xfa, 0x2, 0x0, 0x67, + 0xcf, 0x1f, 0xe0, 0xc9, 0xe, 0x3f, 0x60, 0x0, + 0x1, 0xff, 0xfc, 0x22, 0x0, 0x44, 0x10, 0x88, + 0x22, 0x8, 0xb8, 0x1f, 0x0, + + /* U+9AE6 "髦" */ + 0x7e, 0x19, 0x3, 0x87, 0xc0, 0x9f, 0x9c, 0x40, + 0x85, 0xf0, 0xe7, 0xec, 0x0, 0x0, 0x7f, 0xf8, + 0x8, 0x7, 0xff, 0x80, 0x80, 0xff, 0xfc, 0x8, + 0x10, 0x3f, 0x80, + + /* U+9AEB "髫" */ + 0x7f, 0xd, 0x1, 0xc7, 0xe0, 0xdf, 0xcc, 0x40, + 0x42, 0xfc, 0x77, 0xf6, 0x0, 0x0, 0x7f, 0xf8, + 0x10, 0x2f, 0x87, 0xf, 0xfe, 0x20, 0x8, 0xff, + 0xe2, 0x0, 0x80, + + /* U+9AED "髭" */ + 0x7f, 0x4, 0x80, 0x71, 0xfd, 0x2, 0x0, 0x77, + 0xe7, 0x1f, 0xe0, 0x48, 0x87, 0x3f, 0xb0, 0x1, + 0x0, 0x10, 0x8d, 0x1d, 0x62, 0x43, 0x4, 0x84, + 0x29, 0xe8, 0x7c, 0x1f, 0x80, + + /* U+9AEF "髯" */ + 0x7e, 0x1c, 0x81, 0xc1, 0xf0, 0x13, 0xf1, 0xc4, + 0xc, 0xb, 0x87, 0x9f, 0x30, 0x0, 0x80, 0x3f, + 0xf8, 0x42, 0x10, 0xff, 0xe1, 0x8, 0x4f, 0xff, + 0xe4, 0x1, 0x8, 0x1e, 0x0, + + /* U+9AF9 "髹" */ + 0x7e, 0xc, 0x81, 0xe1, 0xf0, 0x33, 0xf3, 0x84, + 0x8, 0x6f, 0x8f, 0x1f, 0x20, 0x1, 0x0, 0x10, + 0x40, 0x4f, 0xfd, 0x83, 0x85, 0xa, 0x82, 0x24, + 0x85, 0x88, 0xc8, 0x10, 0x0, + + /* U+9AFB "髻" */ + 0x7f, 0xc, 0x80, 0xe1, 0xf0, 0x33, 0xf3, 0x84, + 0x0, 0x5f, 0xe7, 0x1f, 0x10, 0x1, 0x80, 0xff, + 0xfe, 0x2, 0x1, 0xff, 0xf0, 0x0, 0x3, 0xff, + 0x84, 0x1, 0xf, 0xfe, 0x0, + + /* U+9B03 "鬃" */ + 0x0, 0x4, 0xfe, 0x71, 0xfd, 0x3, 0xfb, 0xcf, + 0xf0, 0x64, 0x4f, 0x17, 0x20, 0x3f, 0xff, 0x80, + 0x2, 0x7f, 0xf0, 0x0, 0x7, 0xff, 0xf0, 0x8, + 0xf, 0x17, 0xa0, 0xc0, 0x80, + + /* U+9B08 "鬈" */ + 0x3f, 0x4, 0x80, 0xf1, 0xf8, 0x11, 0xf8, 0xcf, + 0xfa, 0xef, 0xce, 0x4, 0x22, 0x4, 0x98, 0x77, + 0xdd, 0xff, 0xfc, 0x30, 0xc3, 0x9e, 0xf9, 0x1e, + 0x41, 0x1, 0x3, 0xfe, 0x0, + + /* U+9B0F "鬏" */ + 0x7e, 0x1c, 0x81, 0xc1, 0xf0, 0x33, 0xf1, 0x84, + 0x8, 0x1f, 0xc0, 0x91, 0x3e, 0x1c, 0x10, 0xfc, + 0x20, 0x22, 0x4b, 0xf6, 0xa1, 0x82, 0x85, 0x85, + 0x12, 0x91, 0x84, 0xc0, 0x0, + + /* U+9B13 "鬓" */ + 0x7f, 0x1c, 0x7e, 0xc1, 0x0, 0x13, 0xf3, 0xcf, + 0xf8, 0x24, 0x83, 0x9f, 0xf8, 0x3f, 0xff, 0x40, + 0x2, 0x3f, 0xf0, 0x40, 0x0, 0xff, 0xcf, 0xff, + 0xe1, 0x84, 0x3c, 0x7, 0x80, + + /* U+9B1F "鬟" */ + 0x7c, 0x38, 0xfd, 0x81, 0xf9, 0xe7, 0xf8, 0x24, + 0x47, 0x87, 0x50, 0x1f, 0xff, 0x3f, 0xfe, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xe1, 0xff, 0xc0, 0xd9, + 0x9f, 0x6f, 0x83, 0x3, 0x80, + + /* U+9B23 "鬣" */ + 0x7c, 0x38, 0xfd, 0x81, 0xf8, 0xf7, 0xfa, 0x77, + 0xc7, 0x6, 0x71, 0x8c, 0xc7, 0x3f, 0xfe, 0x5c, + 0x64, 0xbf, 0xc9, 0xff, 0xf0, 0x0, 0x5, 0xa4, + 0x8b, 0x49, 0x5c, 0xf1, 0x80, + + /* U+9B2F "鬯" */ + 0x0, 0x2, 0x4d, 0x28, 0x88, 0xa9, 0xd2, 0x97, + 0x2a, 0x22, 0x2b, 0x26, 0xa0, 0x2, 0xff, 0xf9, + 0x0, 0x4, 0xf, 0x17, 0xc1, 0x60, 0x5, 0x0, + 0x17, 0xff, 0x80, + + /* U+9B32 "鬲" */ + 0xff, 0xfe, 0x0, 0x0, 0x7f, 0xe0, 0x80, 0x41, + 0xff, 0x80, 0x0, 0x1f, 0xff, 0x20, 0x12, 0x44, + 0x24, 0x84, 0x89, 0x7f, 0xd2, 0x8, 0x24, 0x10, + 0x48, 0x23, 0x80, + + /* U+9B3B "鬻" */ + 0x0, 0x3, 0xc8, 0xe1, 0x68, 0xbb, 0xec, 0xf2, + 0x38, 0x5e, 0x6, 0x1, 0xbf, 0xff, 0x0, 0x0, + 0xff, 0xc3, 0xff, 0x0, 0x0, 0xff, 0xfa, 0xff, + 0xe8, 0x20, 0xa0, 0x8e, + + /* U+9B3C "鬼" */ + 0x0, 0x0, 0x8, 0x1, 0xff, 0xe2, 0x10, 0x44, + 0x20, 0x8f, 0xff, 0x10, 0x82, 0x21, 0x4, 0x42, + 0x8, 0xff, 0xf0, 0x18, 0x80, 0x32, 0x80, 0xaf, + 0xa2, 0x40, 0x58, 0xc1, 0x60, 0xfe, + + /* U+9B41 "魁" */ + 0x8, 0x8, 0x10, 0x11, 0xfc, 0xa2, 0x48, 0x44, + 0x90, 0x8f, 0xe9, 0x12, 0x4a, 0x24, 0x84, 0x3e, + 0x7e, 0x39, 0x10, 0x54, 0x20, 0xaa, 0x42, 0x5c, + 0xac, 0x80, 0x71, 0xff, 0x0, + + /* U+9B42 "魂" */ + 0x0, 0x80, 0x1, 0x3, 0xef, 0xf0, 0x22, 0x20, + 0x44, 0x40, 0x7f, 0xbf, 0x11, 0x12, 0x22, 0x43, + 0xfc, 0xa1, 0x1, 0x43, 0x4, 0x46, 0x8f, 0x97, + 0xe0, 0x68, 0xc1, 0x90, 0x86, 0x1e, + + /* U+9B43 "魃" */ + 0x8, 0x10, 0x18, 0x14, 0x7f, 0x12, 0x49, 0x20, + 0x49, 0x3f, 0x7f, 0x20, 0x49, 0x3e, 0x49, 0x22, + 0x7f, 0x42, 0x8, 0x54, 0x15, 0x4c, 0x16, 0x8c, + 0x17, 0x92, 0x24, 0x23, 0x64, 0x1, 0xc7, 0xff, + + /* U+9B44 "魄" */ + 0x40, 0x80, 0x8f, 0xfb, 0xd1, 0x14, 0xa2, 0x29, + 0x7f, 0xd2, 0x88, 0xbd, 0x11, 0x4b, 0xfe, 0x90, + 0x81, 0x21, 0x82, 0x43, 0x67, 0x8b, 0x28, 0x25, + 0xb0, 0x88, 0x46, 0xf, 0x0, + + /* U+9B45 "魅" */ + 0x18, 0x10, 0x10, 0x10, 0x6f, 0x10, 0x49, 0x3c, + 0x7f, 0x10, 0x49, 0x10, 0x49, 0x7e, 0x7f, 0x18, + 0x8, 0x38, 0x15, 0x54, 0x16, 0x52, 0x16, 0x90, + 0x25, 0x11, 0x64, 0x2, 0x47, 0xfe, + + /* U+9B47 "魇" */ + 0x3f, 0xfe, 0x20, 0x48, 0x2f, 0xfe, 0x20, 0xa0, + 0x3f, 0x56, 0x2f, 0xfc, 0x28, 0x44, 0x2f, 0xfc, + 0x28, 0x44, 0x2f, 0xfc, 0x20, 0xd0, 0x41, 0xdd, + 0x46, 0x42, 0x98, 0x3e, + + /* U+9B48 "魈" */ + 0x8, 0x10, 0x7f, 0x52, 0x49, 0x14, 0x49, 0x10, + 0x49, 0x7e, 0x7f, 0x42, 0x49, 0x7e, 0x7f, 0x42, + 0x8, 0x42, 0x1d, 0x7e, 0x16, 0x42, 0x16, 0xc2, + 0x25, 0xcf, 0x64, 0x1, 0x47, 0xfe, + + /* U+9B49 "魉" */ + 0x10, 0x0, 0x20, 0xff, 0x7e, 0x28, 0x52, 0x28, + 0x52, 0xfe, 0x7e, 0xaa, 0x52, 0xaa, 0x52, 0xaa, + 0x7e, 0xda, 0x10, 0xd6, 0x2a, 0xd6, 0x2c, 0x92, + 0x2d, 0x82, 0x2b, 0x8f, 0x48, 0x1, 0x8f, 0xfe, + + /* U+9B4D "魍" */ + 0x10, 0x0, 0x20, 0xfe, 0x7e, 0x82, 0x52, 0xaa, + 0x52, 0x8a, 0x52, 0xfe, 0x7e, 0x92, 0x52, 0xfe, + 0x7e, 0xa2, 0x10, 0xa2, 0x14, 0xbe, 0x36, 0x82, + 0x35, 0x82, 0x32, 0x8d, 0x48, 0x1, 0xcf, 0xfe, + + /* U+9B4F "魏" */ + 0x0, 0x21, 0xf8, 0x80, 0x47, 0xf0, 0x89, 0x27, + 0x9f, 0xcb, 0x24, 0xa5, 0x49, 0x0, 0xfe, 0x20, + 0x21, 0xf8, 0xd1, 0x11, 0xc3, 0x47, 0xf1, 0x8a, + 0x2c, 0xa4, 0xa0, 0x87, 0x0, + + /* U+9B51 "魑" */ + 0x10, 0x20, 0x20, 0x20, 0x7e, 0xfe, 0x52, 0x50, + 0x52, 0xb4, 0x7e, 0xb4, 0x52, 0xc4, 0x52, 0xfc, + 0x52, 0x20, 0x3c, 0xfc, 0x1a, 0xd4, 0x2e, 0xf4, + 0x2f, 0x84, 0x28, 0x9e, 0x48, 0x2, 0xcf, 0xfe, + + /* U+9B54 "魔" */ + 0x0, 0x0, 0x1, 0x1, 0xff, 0xfa, 0x0, 0x85, + 0xf7, 0xc8, 0xc7, 0x17, 0x55, 0x20, 0x80, 0x4f, + 0xf8, 0x91, 0x11, 0x3f, 0xe2, 0x44, 0x44, 0x77, + 0x10, 0x77, 0x21, 0x21, 0x4c, 0x7e, + + /* U+9C7C "鱼" */ + 0x0, 0x0, 0x10, 0x0, 0x7f, 0x81, 0x81, 0x2, + 0x4, 0xf, 0xff, 0x28, 0x42, 0x10, 0x84, 0x3f, + 0xf8, 0x42, 0x10, 0x84, 0x21, 0xff, 0xc0, 0x0, + 0x0, 0x0, 0x3f, 0xff, 0x80, + + /* U+9C7F "鱿" */ + 0x20, 0x20, 0x3c, 0x24, 0x44, 0x20, 0xc8, 0x20, + 0x7e, 0xfe, 0x52, 0x30, 0x52, 0x30, 0x7e, 0x30, + 0x52, 0x30, 0x52, 0x50, 0x7e, 0x51, 0x0, 0x51, + 0x0, 0x92, 0xfe, 0x8a, 0x1, 0xe, + + /* U+9C81 "鲁" */ + 0x8, 0x0, 0x3f, 0x81, 0x82, 0x5, 0xfb, 0xc3, + 0xff, 0x84, 0x21, 0xf, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xe1, 0x0, 0x43, 0xff, + 0x87, 0xff, 0x8, 0x2, 0x0, + + /* U+9C82 "鲂" */ + 0x0, 0x10, 0x20, 0x10, 0x3c, 0x10, 0x45, 0xff, + 0x88, 0x40, 0xfe, 0x40, 0x52, 0x40, 0x52, 0x7e, + 0x7e, 0x42, 0x52, 0x42, 0x52, 0x42, 0x7e, 0x42, + 0x0, 0x82, 0x0, 0x82, 0xfe, 0x82, 0x1, 0x3c, + + /* U+9C85 "鲅" */ + 0x0, 0x40, 0x20, 0x48, 0x3c, 0x46, 0x44, 0x40, + 0x88, 0xff, 0xfe, 0x40, 0x52, 0x40, 0x52, 0x7e, + 0x7e, 0x44, 0x52, 0xc4, 0x52, 0xac, 0x7e, 0xa8, + 0x1, 0x10, 0x0, 0x28, 0xfe, 0xc6, 0x1, 0x1, + + /* U+9C86 "鲆" */ + 0x10, 0x0, 0x20, 0xfe, 0x3c, 0x10, 0x44, 0x10, + 0xc8, 0x92, 0x7e, 0x92, 0x52, 0x54, 0x52, 0x54, + 0x7e, 0x10, 0x53, 0xff, 0x52, 0x10, 0x7e, 0x10, + 0x0, 0x10, 0x0, 0x10, 0xfe, 0x10, 0x0, 0x10, + + /* U+9C87 "鲇" */ + 0x20, 0x10, 0x3c, 0x10, 0x44, 0x10, 0xc8, 0x1f, + 0x7e, 0x10, 0x52, 0x10, 0x52, 0x10, 0x7e, 0x10, + 0x52, 0xfe, 0x52, 0x82, 0x7e, 0x82, 0x0, 0x82, + 0x0, 0x82, 0xfe, 0xfe, 0x0, 0x82, + + /* U+9C88 "鲈" */ + 0x20, 0x10, 0x3e, 0x1f, 0x44, 0x10, 0x88, 0x10, + 0x7e, 0xfe, 0x52, 0x82, 0x52, 0x82, 0x7e, 0x82, + 0x52, 0x82, 0x52, 0xfe, 0x7e, 0x80, 0x0, 0x80, + 0x2, 0x80, 0xfd, 0x0, 0x1, 0x0, + + /* U+9C8B "鲋" */ + 0x20, 0x44, 0x38, 0x44, 0x48, 0x84, 0x48, 0x84, + 0xfd, 0xbf, 0x55, 0x84, 0x54, 0xa4, 0x7c, 0xa4, + 0x54, 0xa4, 0x54, 0x84, 0x7c, 0x84, 0x0, 0x84, + 0x0, 0x84, 0xfc, 0x84, 0x0, 0x9c, + + /* U+9C8D "鲍" */ + 0x20, 0x40, 0x38, 0x40, 0x48, 0xfe, 0x48, 0x82, + 0xfd, 0x2, 0x55, 0xf2, 0x54, 0x92, 0x7c, 0x92, + 0x54, 0xf2, 0x54, 0x82, 0x7c, 0x9c, 0x0, 0x81, + 0x0, 0x81, 0xfc, 0x81, 0x0, 0xfe, + + /* U+9C8E "鲎" */ + 0x1, 0x0, 0x42, 0x10, 0x40, 0x47, 0xff, 0xf8, + 0x40, 0x31, 0xfe, 0x44, 0x8, 0x1f, 0xfc, 0xe1, + 0x8, 0x42, 0x10, 0xff, 0xe1, 0x8, 0x43, 0xff, + 0x80, 0x0, 0x3f, 0xff, 0x80, + + /* U+9C90 "鲐" */ + 0x20, 0x0, 0x20, 0x20, 0x3c, 0x20, 0x4c, 0x40, + 0x98, 0x44, 0x7e, 0x82, 0x52, 0xfe, 0x52, 0x1, + 0x7e, 0x0, 0x52, 0xfe, 0x52, 0x82, 0x7e, 0x82, + 0x0, 0x82, 0x0, 0x82, 0xfe, 0xfe, 0x0, 0x82, + + /* U+9C91 "鲑" */ + 0x20, 0x20, 0x70, 0x41, 0x27, 0xf4, 0x41, 0x7, + 0xc2, 0xa, 0x84, 0x15, 0x7f, 0xbe, 0x10, 0x54, + 0x20, 0xa8, 0x41, 0xf3, 0xf0, 0x1, 0x0, 0x2, + 0xf, 0xbf, 0xc0, + + /* U+9C92 "鲒" */ + 0x20, 0x10, 0x3c, 0x10, 0x4d, 0xff, 0x88, 0x10, + 0x7e, 0x10, 0x52, 0x10, 0x52, 0xee, 0x7e, 0x0, + 0x52, 0x0, 0x52, 0xfe, 0x7e, 0x82, 0x0, 0x82, + 0x0, 0x82, 0xfe, 0xfe, 0x0, 0x82, + + /* U+9C94 "鲔" */ + 0x20, 0x10, 0x3c, 0x20, 0x45, 0xff, 0x48, 0x40, + 0xfe, 0x40, 0x52, 0xfe, 0x52, 0x82, 0x53, 0x82, + 0x7e, 0xfe, 0x52, 0x82, 0x7e, 0xfe, 0x0, 0x82, + 0x0, 0x82, 0xfe, 0x82, 0x0, 0x8e, + + /* U+9C95 "鲕" */ + 0x0, 0x0, 0x40, 0x0, 0xef, 0xfa, 0x43, 0x9, + 0x4, 0xf, 0xbf, 0xd5, 0x52, 0xaa, 0xa5, 0x7d, + 0x4a, 0xaa, 0x95, 0x55, 0x2b, 0xea, 0x50, 0x14, + 0xa0, 0x29, 0x7f, 0x40, 0x80, 0x87, + + /* U+9C9A "鲚" */ + 0x20, 0x20, 0x78, 0x1, 0x37, 0xfc, 0x44, 0x27, + 0xe4, 0x8a, 0x46, 0x14, 0x8c, 0x3f, 0x26, 0x53, + 0x82, 0xa4, 0x91, 0xf9, 0x20, 0x2, 0x4f, 0xe4, + 0x80, 0x11, 0x0, 0x42, 0x0, + + /* U+9C9B "鲛" */ + 0x0, 0x0, 0x20, 0x10, 0x3c, 0x10, 0x4d, 0xff, + 0x88, 0x0, 0x7e, 0x44, 0x52, 0xc2, 0x53, 0x81, + 0x7e, 0x44, 0x52, 0x44, 0x52, 0x28, 0x7e, 0x38, + 0x0, 0x10, 0x2, 0x6c, 0xfc, 0x82, 0x1, 0x0, + + /* U+9C9C "鲜" */ + 0x0, 0x0, 0x20, 0x44, 0x3c, 0x48, 0x44, 0x8, + 0x88, 0xf6, 0x7e, 0x10, 0x52, 0x10, 0x52, 0xfe, + 0x7e, 0x10, 0x52, 0x10, 0x52, 0x10, 0x7e, 0xff, + 0x0, 0x10, 0x0, 0x10, 0xff, 0x10, 0x0, 0x10, + + /* U+9C9E "鲞" */ + 0x11, 0x10, 0x12, 0x41, 0xff, 0xf0, 0x10, 0xf, + 0xff, 0xe1, 0x4, 0x1d, 0xf6, 0x44, 0x63, 0x3f, + 0xf8, 0x42, 0x10, 0x7f, 0xe1, 0x8, 0x41, 0xef, + 0x1f, 0xff, 0xc0, + + /* U+9C9F "鲟" */ + 0x20, 0x0, 0x20, 0xfe, 0x3c, 0x2, 0x4c, 0x2, + 0x98, 0xfe, 0x7e, 0x2, 0x52, 0x2, 0x52, 0xfe, + 0x7e, 0x0, 0x52, 0x4, 0x52, 0xfe, 0x7e, 0xc4, + 0x0, 0x44, 0x0, 0x4, 0xfe, 0x4, 0x0, 0x38, + + /* U+9CA0 "鲠" */ + 0x0, 0x0, 0x21, 0xff, 0x38, 0x10, 0x48, 0x10, + 0x48, 0xfe, 0xfd, 0x12, 0x55, 0x12, 0x54, 0xfe, + 0x7d, 0x12, 0x55, 0x12, 0x54, 0xfe, 0x7c, 0xa0, + 0x0, 0x60, 0x7c, 0x70, 0x1, 0x8e, 0x2, 0x1, + + /* U+9CA1 "鲡" */ + 0x0, 0x0, 0x43, 0xfc, 0xe0, 0x2, 0x40, 0x9, + 0x1e, 0xef, 0xa6, 0x55, 0x4c, 0xaa, 0xd5, 0x7d, + 0xba, 0xaa, 0xed, 0x55, 0xdb, 0xe9, 0x90, 0x13, + 0x20, 0x26, 0x7f, 0x4c, 0x80, 0xb3, + + /* U+9CA2 "鲢" */ + 0x22, 0x10, 0x74, 0x21, 0x25, 0xfa, 0x49, 0xf, + 0xc2, 0x8a, 0x85, 0x15, 0xd2, 0x3e, 0x9e, 0x55, + 0x8, 0xaa, 0x11, 0xf5, 0xf8, 0x8, 0x40, 0x10, + 0x9f, 0x50, 0x1, 0x1f, 0x80, + + /* U+9CA3 "鲣" */ + 0x20, 0x40, 0x39, 0x7e, 0x49, 0x52, 0x51, 0x52, + 0x7d, 0x4c, 0x55, 0x4c, 0x55, 0x52, 0x7c, 0x61, + 0x54, 0x0, 0x54, 0x20, 0x7d, 0xfe, 0x0, 0x20, + 0xc, 0x20, 0xf3, 0xff, + + /* U+9CA4 "鲤" */ + 0x0, 0x0, 0x43, 0xfc, 0xe4, 0x8a, 0x49, 0x1c, + 0x92, 0x2f, 0xbf, 0xd5, 0x48, 0xaa, 0x91, 0x7d, + 0xfe, 0xa8, 0x41, 0x50, 0x83, 0xef, 0xf0, 0x2, + 0x0, 0x4, 0x3f, 0x7f, 0x80, + + /* U+9CA5 "鲥" */ + 0x20, 0x2, 0x39, 0xc2, 0x49, 0x42, 0x51, 0x5f, + 0xfd, 0x42, 0x55, 0x42, 0x55, 0xc2, 0x7d, 0x4a, + 0x55, 0x4a, 0x55, 0x42, 0x7d, 0x42, 0x1, 0xc2, + 0x5, 0x42, 0xf9, 0x2, 0x0, 0xe, + + /* U+9CA6 "鲦" */ + 0x0, 0x0, 0x40, 0x80, 0xe1, 0xf2, 0x46, 0x25, + 0x1c, 0x9f, 0xa5, 0x15, 0xc, 0x2a, 0x67, 0x7d, + 0x10, 0xa8, 0x21, 0x57, 0xfb, 0xe0, 0x80, 0x9, + 0x40, 0x32, 0x7f, 0xc4, 0x80, 0x30, + + /* U+9CA7 "鲧" */ + 0x0, 0x0, 0x20, 0x1e, 0x39, 0xe0, 0x48, 0x30, + 0x48, 0x40, 0xfc, 0x84, 0x55, 0xf8, 0x54, 0x10, + 0x7c, 0x64, 0x55, 0xbe, 0x54, 0x51, 0x7c, 0x54, + 0x0, 0x92, 0x1, 0x12, 0xfd, 0x11, 0x0, 0x70, + + /* U+9CA8 "鲨" */ + 0x30, 0x40, 0x4, 0x91, 0x91, 0x30, 0x0, 0x83, + 0xe, 0x18, 0x60, 0x3, 0xf8, 0x18, 0x10, 0x7f, + 0xf9, 0x42, 0x10, 0xff, 0xe1, 0x8, 0x43, 0xff, + 0x80, 0x0, 0x3f, 0xff, 0x80, + + /* U+9CA9 "鲩" */ + 0x20, 0x20, 0x20, 0x10, 0x59, 0xfe, 0x49, 0x2, + 0x91, 0x2, 0x7c, 0xfc, 0x54, 0x0, 0x54, 0x0, + 0x7d, 0xfe, 0x54, 0x50, 0x7c, 0x50, 0x0, 0x51, + 0x0, 0x92, 0xfc, 0x92, 0x3, 0xe, + + /* U+9CAB "鲫" */ + 0x0, 0x0, 0x43, 0xdc, 0xe4, 0xca, 0x49, 0x99, + 0x13, 0x2f, 0xbe, 0x55, 0x4c, 0xaa, 0x99, 0x7d, + 0xf2, 0xaa, 0x25, 0x54, 0x4b, 0xea, 0x90, 0x13, + 0x60, 0x3e, 0x3f, 0x44, 0x0, 0x8, + + /* U+9CAD "鲭" */ + 0x20, 0x20, 0x73, 0xfd, 0x20, 0x84, 0x4f, 0xf7, + 0xc2, 0xa, 0xbf, 0xd5, 0x0, 0x3e, 0x7e, 0x55, + 0x4, 0xa9, 0xf9, 0xf4, 0x10, 0x7, 0xe0, 0x10, + 0x5f, 0xa0, 0x80, 0x47, 0x0, + + /* U+9CAE "鲮" */ + 0x0, 0x0, 0x20, 0x10, 0x20, 0x10, 0x38, 0xfe, + 0x48, 0x10, 0xfd, 0xff, 0x54, 0x8, 0x54, 0x46, + 0x7d, 0x23, 0x54, 0x7c, 0x54, 0xc4, 0x7d, 0xc8, + 0x0, 0x28, 0xc, 0x38, 0xf0, 0xc6, 0x1, 0x0, + + /* U+9CB0 "鲰" */ + 0x20, 0x0, 0x3b, 0xe0, 0x49, 0x5e, 0x89, 0x52, + 0x7d, 0xd2, 0x55, 0x52, 0x55, 0x52, 0x7d, 0x4a, + 0x55, 0xca, 0x55, 0x4c, 0x7d, 0x44, 0x1, 0xe4, + 0x2, 0x4c, 0xf8, 0x52, 0x0, 0x40, + + /* U+9CB1 "鲱" */ + 0x20, 0x48, 0x3c, 0x48, 0x45, 0xcf, 0x88, 0x48, + 0x7e, 0x48, 0x52, 0x48, 0x53, 0xce, 0x7e, 0x48, + 0x52, 0x48, 0x52, 0x48, 0x7f, 0xcf, 0x0, 0x48, + 0x0, 0x48, 0xfe, 0x48, 0x0, 0x48, + + /* U+9CB2 "鲲" */ + 0x20, 0x0, 0x20, 0xfe, 0x7c, 0x82, 0x44, 0x82, + 0x88, 0xfe, 0x7e, 0x82, 0x52, 0x82, 0x52, 0x7c, + 0x7e, 0x0, 0x52, 0x89, 0x52, 0xee, 0x7e, 0x88, + 0x0, 0x89, 0x0, 0xa9, 0xfe, 0xc9, 0x0, 0x8e, + + /* U+9CB3 "鲳" */ + 0x0, 0x0, 0x41, 0xf8, 0xe4, 0x12, 0x48, 0x2c, + 0x8f, 0xdf, 0xa0, 0x95, 0x3f, 0x2a, 0x0, 0x7c, + 0x0, 0xab, 0xfd, 0x54, 0xa, 0xa8, 0x17, 0xdf, + 0xe0, 0x20, 0x7f, 0x7f, 0x80, 0x81, + + /* U+9CB4 "鲴" */ + 0x21, 0xfe, 0x72, 0x5, 0x24, 0xc, 0x89, 0x17, + 0xd7, 0xaa, 0xa4, 0x55, 0x48, 0xbe, 0xbd, 0x55, + 0x4a, 0xaa, 0x94, 0xa5, 0xe8, 0x8, 0x10, 0x50, + 0x3f, 0x3f, 0xc0, 0x40, 0x80, + + /* U+9CB5 "鲵" */ + 0x0, 0x0, 0x20, 0xde, 0x39, 0x2, 0x49, 0x2, + 0x91, 0x2, 0x7d, 0xde, 0x55, 0x2, 0x55, 0x2, + 0x7d, 0x2, 0x55, 0xfe, 0x54, 0x50, 0x7c, 0x50, + 0x0, 0x51, 0x4, 0x91, 0xf9, 0x91, 0x3, 0x1e, + + /* U+9CB6 "鲶" */ + 0x10, 0x10, 0x20, 0x20, 0x38, 0x50, 0x48, 0x48, + 0x50, 0x84, 0xfd, 0x22, 0x56, 0x10, 0x55, 0xfc, + 0x7c, 0xc, 0x54, 0x8, 0x54, 0x20, 0x7d, 0x92, + 0x2, 0x82, 0x2, 0x8a, 0xfe, 0x88, 0x0, 0x78, + + /* U+9CB7 "鲷" */ + 0x10, 0x0, 0x43, 0xfc, 0xe4, 0xa, 0x48, 0x18, + 0x92, 0x2f, 0xaf, 0x55, 0x48, 0xaa, 0xff, 0x7d, + 0x2, 0xaa, 0xf5, 0x55, 0x2b, 0xea, 0x50, 0x17, + 0xa1, 0xa0, 0x7c, 0x80, 0x81, 0xf, + + /* U+9CB8 "鲸" */ + 0x20, 0x20, 0x70, 0x1, 0x27, 0xfc, 0x80, 0x7, + 0xcf, 0xca, 0x90, 0x95, 0x21, 0x3e, 0x42, 0x54, + 0xfc, 0xa8, 0x41, 0xf2, 0x90, 0x5, 0x20, 0x52, + 0x3f, 0x4, 0x40, 0x18, 0x0, + + /* U+9CBA "鲺" */ + 0x20, 0x0, 0x39, 0xfc, 0x48, 0x4, 0x91, 0xf4, + 0xfc, 0x44, 0x54, 0x44, 0x55, 0xf4, 0x7d, 0x54, + 0x55, 0x54, 0x55, 0xf4, 0x7c, 0x44, 0x0, 0x45, + 0xc, 0x55, 0xf3, 0xf5, 0x0, 0xa, + + /* U+9CBB "鲻" */ + 0x0, 0x0, 0x40, 0xa4, 0xe2, 0x52, 0x49, 0x24, + 0x8a, 0x5f, 0x8a, 0x55, 0x0, 0x2a, 0x7f, 0x7c, + 0x92, 0xa9, 0x25, 0x52, 0x4b, 0xe7, 0xf0, 0x9, + 0x23, 0x92, 0x58, 0x3f, 0x80, 0x41, + + /* U+9CBC "鲼" */ + 0x0, 0x10, 0x20, 0x10, 0x38, 0xfe, 0x48, 0x10, + 0xfc, 0x44, 0x55, 0xff, 0x54, 0x44, 0x54, 0x0, + 0x7c, 0xfe, 0x54, 0x82, 0x54, 0x92, 0x7c, 0x92, + 0x0, 0x92, 0xe, 0x28, 0xf0, 0xc6, 0x3, 0x0, + + /* U+9CBD "鲽" */ + 0x0, 0xa4, 0x20, 0xa4, 0x59, 0xff, 0x48, 0xa4, + 0x90, 0xa4, 0x7c, 0xbc, 0x54, 0x80, 0x7c, 0x7e, + 0x54, 0x20, 0x55, 0xff, 0x7c, 0x68, 0x0, 0x68, + 0x4, 0xa6, 0xfb, 0x21, 0x0, 0x20, + + /* U+9CC3 "鳃" */ + 0x0, 0x0, 0x21, 0xfe, 0x39, 0x12, 0x49, 0x12, + 0x91, 0x12, 0x7d, 0xfe, 0x55, 0x12, 0x55, 0x12, + 0x7d, 0xfe, 0x54, 0x0, 0x54, 0x30, 0x7d, 0x52, + 0x2, 0x46, 0x2, 0x45, 0xfc, 0x44, 0x0, 0x78, + + /* U+9CC4 "鳄" */ + 0x0, 0x0, 0x21, 0xee, 0x39, 0x2a, 0x49, 0x2a, + 0xc9, 0x2a, 0x7c, 0xc4, 0x54, 0x0, 0x54, 0xfc, + 0x7c, 0x0, 0x55, 0xff, 0x54, 0x80, 0x7c, 0xfc, + 0x0, 0x4, 0x0, 0x4, 0xfe, 0x4, 0x0, 0x38, + + /* U+9CC5 "鳅" */ + 0x0, 0x0, 0x20, 0xd8, 0x3b, 0x98, 0x48, 0x98, + 0x48, 0xba, 0xfc, 0xba, 0x57, 0xfa, 0x54, 0x9c, + 0x7d, 0x98, 0x56, 0xd8, 0x56, 0x9c, 0x7e, 0x9c, + 0x0, 0xa4, 0x0, 0xa6, 0xf8, 0xa2, 0x0, 0x80, + + /* U+9CC6 "鳆" */ + 0x10, 0x40, 0x40, 0x80, 0xe3, 0xfa, 0x4c, 0x4, + 0xbf, 0xdf, 0x90, 0x95, 0x3f, 0x2a, 0x42, 0x7c, + 0xfc, 0xa8, 0x81, 0x51, 0xf3, 0xe4, 0x20, 0x34, + 0x80, 0x6, 0x3f, 0x1e, 0x1, 0xc3, + + /* U+9CC7 "鳇" */ + 0x0, 0x20, 0x20, 0x40, 0x39, 0xfc, 0x49, 0x4, + 0xd1, 0x4, 0x7d, 0xfc, 0x55, 0x4, 0x55, 0xfc, + 0x7c, 0x0, 0x55, 0xfe, 0x54, 0x20, 0x7c, 0x20, + 0x1, 0xfe, 0x0, 0x20, 0xfc, 0x20, 0x3, 0xff, + + /* U+9CCA "鳊" */ + 0x0, 0x20, 0x40, 0x40, 0xe7, 0xfa, 0x48, 0x19, + 0x10, 0x2f, 0xbf, 0xd5, 0x40, 0x2a, 0x80, 0x7d, + 0xfe, 0xad, 0x55, 0x5a, 0xab, 0xf7, 0xf0, 0x2a, + 0xa0, 0xd5, 0x7e, 0xaa, 0x82, 0x43, + + /* U+9CCB "鳋" */ + 0x10, 0x0, 0x47, 0xf8, 0xe4, 0x12, 0x45, 0x48, + 0x8d, 0x8f, 0x8e, 0x15, 0x36, 0x2a, 0x93, 0x7d, + 0xfc, 0xaa, 0x49, 0x54, 0x93, 0xef, 0xe0, 0x12, + 0x41, 0x84, 0xbc, 0xff, 0x80, 0x1, + + /* U+9CCC "鳌" */ + 0x8, 0x20, 0xfc, 0xfd, 0xf9, 0x17, 0xfb, 0x43, + 0xe3, 0x8, 0x4d, 0xa3, 0x20, 0x8f, 0xf0, 0x20, + 0x21, 0xff, 0xb0, 0xff, 0xe1, 0x8, 0x41, 0xef, + 0x1f, 0xff, 0xc0, + + /* U+9CCD "鳍" */ + 0x20, 0x20, 0x71, 0xf9, 0x20, 0xa4, 0x41, 0x8f, + 0xde, 0xea, 0x88, 0x95, 0x6e, 0x2a, 0x62, 0x7c, + 0x7c, 0xa9, 0xf9, 0xf2, 0x10, 0x7, 0xe3, 0xe8, + 0x48, 0x1f, 0x80, 0x21, 0x0, + + /* U+9CCE "鳎" */ + 0x0, 0x0, 0x87, 0xf7, 0x90, 0x52, 0x7f, 0xfd, + 0x5, 0x57, 0xf5, 0x40, 0x1f, 0xf7, 0x54, 0x55, + 0x55, 0x37, 0xcc, 0x40, 0xd7, 0x2, 0x47, 0xf1, + 0x10, 0x1c, 0xc0, + + /* U+9CCF "鳏" */ + 0x0, 0x0, 0x43, 0xfc, 0xe5, 0x2a, 0x4a, 0x54, + 0x94, 0xaf, 0xa9, 0x55, 0x7f, 0xaa, 0x10, 0x7c, + 0xa4, 0xaa, 0x45, 0x50, 0x83, 0xe5, 0x20, 0xa, + 0x40, 0x24, 0x7f, 0x48, 0x80, 0x10, + + /* U+9CD0 "鳐" */ + 0x0, 0x0, 0x20, 0x7e, 0x39, 0xe0, 0x48, 0x2, + 0x89, 0x12, 0x7c, 0x4, 0x54, 0x80, 0x54, 0xfe, + 0x7d, 0x10, 0x54, 0x10, 0x55, 0xff, 0x7c, 0x10, + 0x0, 0x10, 0x1, 0x12, 0xf9, 0xfe, 0x0, 0x2, + + /* U+9CD3 "鳓" */ + 0x20, 0x48, 0x7f, 0xd1, 0x29, 0x22, 0x52, 0xf7, + 0xdc, 0xaa, 0x91, 0x55, 0x7a, 0xbf, 0x55, 0x56, + 0xaa, 0xab, 0xd5, 0xf2, 0x28, 0x1f, 0x90, 0x9, + 0x3f, 0x92, 0x40, 0x2b, 0x0, + + /* U+9CD4 "鳔" */ + 0x10, 0x0, 0x47, 0xfc, 0xe1, 0x42, 0x42, 0x88, + 0x9f, 0xef, 0xca, 0x55, 0x94, 0xaa, 0xff, 0x54, + 0x0, 0xfb, 0xf9, 0x50, 0x3, 0xff, 0xf0, 0x2, + 0x0, 0x95, 0xbe, 0xc8, 0x80, 0x70, + + /* U+9CD5 "鳕" */ + 0x0, 0x0, 0x43, 0xf8, 0xe0, 0x82, 0x41, 0x4, + 0x9f, 0xf2, 0x44, 0x5f, 0x3a, 0xaa, 0x10, 0x7c, + 0x20, 0xa8, 0x1, 0x57, 0xf2, 0xa0, 0x22, 0x8f, + 0xc0, 0x0, 0xbf, 0x7f, 0x0, 0x2, + + /* U+9CD6 "鳖" */ + 0x52, 0x20, 0x60, 0x7f, 0xfd, 0x14, 0x8d, 0x4b, + 0x51, 0x96, 0x23, 0x20, 0x79, 0x87, 0xf0, 0x10, + 0x20, 0xff, 0xf2, 0x84, 0x21, 0xff, 0xc3, 0xff, + 0x80, 0x0, 0x3f, 0xff, 0x80, + + /* U+9CD7 "鳗" */ + 0x21, 0xfe, 0x39, 0x2, 0x49, 0xfe, 0x91, 0x2, + 0x7d, 0xfe, 0x54, 0x0, 0x55, 0xff, 0x7e, 0x49, + 0x55, 0xff, 0x54, 0x0, 0x7d, 0xfe, 0x0, 0x84, + 0x0, 0x48, 0xfc, 0x78, 0x3, 0x87, + + /* U+9CD8 "鳘" */ + 0x20, 0x20, 0xbe, 0x7e, 0xfd, 0x12, 0x51, 0x4f, + 0xfa, 0x89, 0x42, 0x1f, 0xdb, 0x5, 0x1, 0x15, + 0xe0, 0x7f, 0xf1, 0x84, 0x21, 0xff, 0xc3, 0xff, + 0x80, 0x0, 0x3f, 0xff, 0x80, + + /* U+9CD9 "鳙" */ + 0x20, 0x20, 0x39, 0xff, 0x39, 0x0, 0x49, 0x7e, + 0xfd, 0x12, 0x55, 0xff, 0x55, 0x12, 0x7d, 0x3c, + 0x55, 0x7e, 0x55, 0x92, 0x7d, 0x7e, 0x1, 0x92, + 0x1e, 0x7e, 0xe2, 0x92, 0x2, 0x96, + + /* U+9CDC "鳜" */ + 0x23, 0xff, 0x3a, 0x0, 0x52, 0x94, 0x92, 0xa4, + 0x6b, 0xff, 0x6a, 0x4a, 0x6a, 0x52, 0x7b, 0x54, + 0x6b, 0x54, 0x6b, 0x54, 0x7a, 0xec, 0x6, 0x4e, + 0xc, 0x8a, 0xf4, 0x92, 0x9, 0x21, + + /* U+9CDD "鳝" */ + 0x20, 0x84, 0x40, 0x91, 0x67, 0xfa, 0x81, 0xf, + 0xdf, 0xea, 0x84, 0x15, 0x7f, 0xbe, 0x52, 0x54, + 0xa8, 0xa8, 0xa8, 0xa3, 0xf0, 0x8, 0x20, 0x10, + 0x5f, 0x9f, 0x80, 0x41, 0x0, + + /* U+9CDE "鳞" */ + 0x0, 0x20, 0x21, 0x22, 0x38, 0xac, 0x48, 0x20, + 0x89, 0xff, 0x7c, 0x30, 0x54, 0xe8, 0x55, 0x26, + 0x56, 0x23, 0x7c, 0xff, 0x55, 0x54, 0x7e, 0xd4, + 0x0, 0x7f, 0x4, 0x84, 0xf9, 0x4, 0x2, 0x4, + + /* U+9CDF "鳟" */ + 0x0, 0x84, 0x20, 0x48, 0x39, 0xff, 0x48, 0x48, + 0x89, 0xfe, 0x7d, 0x4a, 0x55, 0x8e, 0x55, 0x2, + 0x55, 0xfe, 0x7d, 0x2, 0x55, 0xfe, 0x54, 0x4, + 0x7d, 0xff, 0x0, 0x4, 0xfc, 0x84, 0x0, 0x3c, + + /* U+9CE2 "鳢" */ + 0x20, 0x48, 0x39, 0xfe, 0x49, 0x4a, 0x91, 0xfe, + 0x7d, 0x4a, 0x55, 0x4a, 0x54, 0xb4, 0x7d, 0xff, + 0x54, 0x0, 0x55, 0xfe, 0x7d, 0x2, 0x1, 0xfe, + 0x4, 0x84, 0x7b, 0xff, + + /* U+9E1F "鸟" */ + 0x2, 0x0, 0x10, 0x3, 0xff, 0x8, 0x4, 0x24, + 0x10, 0x8c, 0x42, 0x1, 0x8, 0x78, 0x20, 0x0, + 0xff, 0xf0, 0x0, 0x40, 0x1, 0xff, 0xe4, 0x0, + 0x10, 0x0, 0xc0, 0x3e, + + /* U+9E20 "鸠" */ + 0x20, 0x40, 0x43, 0xf8, 0x84, 0x13, 0xe9, 0x22, + 0x52, 0x44, 0xa0, 0x89, 0x4e, 0x12, 0x80, 0x25, + 0xfe, 0x48, 0x4, 0x97, 0xe9, 0x20, 0x14, 0x60, + 0x28, 0x80, 0xb0, 0xf, 0x0, + + /* U+9E21 "鸡" */ + 0x0, 0x40, 0x2, 0xf, 0xbf, 0x82, 0x82, 0x8a, + 0x49, 0x28, 0x25, 0x27, 0x8c, 0x80, 0x13, 0xfc, + 0xc0, 0x12, 0x80, 0x52, 0xfd, 0x80, 0x4, 0x0, + 0x10, 0xf, 0x80, + + /* U+9E22 "鸢" */ + 0x8, 0x80, 0x11, 0x80, 0x3f, 0xff, 0x40, 0x4, + 0xc4, 0x20, 0xe3, 0xff, 0x9, 0x4, 0x23, 0x60, + 0x80, 0x3, 0xff, 0xc0, 0x1, 0xff, 0xe4, 0x0, + 0x10, 0x7, 0x80, + + /* U+9E23 "鸣" */ + 0x0, 0x0, 0x2, 0xf, 0x3f, 0xa4, 0x82, 0x92, + 0xca, 0x48, 0x29, 0x27, 0x24, 0x80, 0x93, 0xfe, + 0x40, 0x19, 0x0, 0x7d, 0xf9, 0x90, 0x4, 0x0, + 0x10, 0x7, 0x80, + + /* U+9E25 "鸥" */ + 0x0, 0x40, 0x1, 0xf, 0xdf, 0xa0, 0x42, 0xa5, + 0x4a, 0xa4, 0xaa, 0x90, 0xa6, 0x5e, 0x91, 0x2, + 0x67, 0xfa, 0x40, 0x69, 0x7d, 0x80, 0x6, 0x0, + 0x1f, 0xc0, 0x40, 0x1e, + + /* U+9E26 "鸦" */ + 0x0, 0x20, 0xfc, 0x40, 0x23, 0xf0, 0x44, 0x24, + 0x8a, 0x49, 0x12, 0x92, 0x21, 0x3f, 0x5c, 0x8, + 0x80, 0x31, 0xfc, 0x60, 0x9, 0x4f, 0xd6, 0x80, + 0x39, 0x0, 0x42, 0x1, 0x9c, 0x3e, + + /* U+9E28 "鸨" */ + 0x40, 0x40, 0x89, 0xf9, 0xe2, 0x12, 0x15, 0x24, + 0x29, 0x48, 0x50, 0x8f, 0x2e, 0x0, 0x40, 0x10, + 0xfe, 0x20, 0x7, 0xf8, 0x8, 0x8f, 0xd1, 0x0, + 0x22, 0x0, 0x84, 0x1f, 0x0, + + /* U+9E29 "鸩" */ + 0x10, 0x20, 0x10, 0x20, 0x10, 0xfe, 0x7e, 0x82, + 0x52, 0x92, 0x52, 0x9a, 0x10, 0x82, 0x18, 0x9c, + 0x18, 0x80, 0x18, 0xff, 0x38, 0x1, 0x28, 0xfd, + 0x2a, 0x1, 0x24, 0x1, 0x48, 0x3, 0x40, 0x3e, + + /* U+9E2A "鸪" */ + 0x10, 0x20, 0x20, 0x80, 0x42, 0xf7, 0xf4, 0x21, + 0xa, 0x42, 0x10, 0x84, 0x2e, 0x3e, 0x40, 0x84, + 0xff, 0x8, 0x6, 0x17, 0xec, 0x20, 0x17, 0xc0, + 0x30, 0x80, 0x80, 0x1f, 0x0, + + /* U+9E2B "鸫" */ + 0x20, 0x20, 0x83, 0xff, 0xf0, 0x50, 0x41, 0x51, + 0x36, 0x44, 0x19, 0x13, 0xbf, 0x40, 0x10, 0xfd, + 0x50, 0x19, 0x4f, 0xe4, 0x81, 0x12, 0x4, 0x40, + 0x16, 0x7, 0x80, + + /* U+9E2C "鸬" */ + 0x10, 0x20, 0x20, 0x40, 0x73, 0xf0, 0x84, 0x27, + 0xcb, 0x48, 0x90, 0x91, 0x2f, 0x22, 0x40, 0x7c, + 0x7e, 0x80, 0x5, 0x0, 0xa, 0x1f, 0x94, 0x0, + 0x30, 0x0, 0xa0, 0xf, 0x0, + + /* U+9E2D "鸭" */ + 0x0, 0x43, 0xf1, 0x9, 0x5f, 0xe5, 0x41, 0x95, + 0x26, 0x54, 0x5f, 0xd0, 0x65, 0x4e, 0x95, 0x2, + 0x57, 0xff, 0xc0, 0x65, 0x7d, 0x10, 0x4, 0x40, + 0x11, 0x0, 0x44, 0x1e, + + /* U+9E2F "鸯" */ + 0x1, 0x0, 0x7f, 0xf0, 0x84, 0x27, 0xff, 0xf0, + 0x24, 0x1, 0x86, 0x1c, 0x43, 0xcf, 0xfc, 0x20, + 0x8, 0x46, 0xe0, 0x80, 0x0, 0xff, 0xef, 0xff, + 0x40, 0x0, 0x80, 0x1e, 0x0, + + /* U+9E31 "鸱" */ + 0x0, 0x20, 0x38, 0x8f, 0xdf, 0xa4, 0x42, 0x91, + 0x2a, 0x44, 0xe9, 0x10, 0xbf, 0xce, 0x91, 0x2, + 0x47, 0xf9, 0x0, 0x66, 0x7d, 0xa8, 0x7, 0x38, + 0x1a, 0x60, 0xc4, 0x1e, + + /* U+9E32 "鸲" */ + 0x20, 0x20, 0x40, 0x40, 0xfb, 0x72, 0x14, 0x28, + 0x2b, 0x5e, 0x50, 0x94, 0xae, 0x29, 0x40, 0x52, + 0xfe, 0xa4, 0x5, 0xcb, 0xea, 0x10, 0x10, 0x20, + 0x20, 0xc0, 0xc7, 0xf, 0x0, + + /* U+9E33 "鸳" */ + 0x20, 0x0, 0xfb, 0xf4, 0x48, 0x69, 0x2e, 0x18, + 0x83, 0x91, 0xe3, 0xff, 0x8, 0x4, 0x26, 0xf0, + 0x80, 0x3, 0xff, 0xc0, 0x1, 0xff, 0xe4, 0x0, + 0x10, 0xf, 0x80, + + /* U+9E35 "鸵" */ + 0x10, 0x10, 0xfc, 0x21, 0xb, 0xfa, 0x94, 0x14, + 0xa8, 0x28, 0x40, 0x17, 0x11, 0x20, 0x24, 0x7e, + 0x50, 0x4, 0xc0, 0x9, 0xf, 0xd2, 0x10, 0x24, + 0x20, 0x48, 0x4f, 0x1f, 0x0, + + /* U+9E36 "鸶" */ + 0x10, 0x20, 0x45, 0x99, 0xf3, 0xc0, 0x82, 0x7, + 0xef, 0xdf, 0xff, 0xc0, 0x80, 0x1f, 0xf8, 0x20, + 0x10, 0x46, 0xe0, 0x80, 0x0, 0xff, 0xef, 0xfe, + 0x40, 0x0, 0x80, 0x1e, 0x0, + + /* U+9E37 "鸷" */ + 0x20, 0x83, 0xef, 0xc2, 0x9, 0x1e, 0xe4, 0x21, + 0xd4, 0x88, 0x34, 0x0, 0xf, 0xfc, 0x20, 0x10, + 0x99, 0xc2, 0x0, 0x7, 0xff, 0xff, 0xe4, 0x0, + 0x10, 0x7, 0x80, + + /* U+9E38 "鸸" */ + 0x0, 0x13, 0xfc, 0x81, 0x7, 0xc4, 0x11, 0x10, + 0x57, 0xfd, 0x5a, 0x94, 0x6a, 0x57, 0xa9, 0x42, + 0xa5, 0xfa, 0x90, 0x6a, 0x7f, 0xa9, 0x6, 0x4, + 0x18, 0x10, 0x61, 0x8f, + + /* U+9E39 "鸹" */ + 0x0, 0x21, 0xfc, 0x80, 0x43, 0xf0, 0x84, 0x21, + 0xa, 0x5f, 0xd2, 0x84, 0x21, 0x8, 0x5c, 0x10, + 0x80, 0xf9, 0xfe, 0x10, 0xc, 0x20, 0x18, 0x5f, + 0xb0, 0x80, 0x5f, 0x1, 0x42, 0x3e, + + /* U+9E3A "鸺" */ + 0x12, 0x10, 0x44, 0xfc, 0x89, 0x11, 0x7a, 0xa2, + 0x25, 0x4c, 0x48, 0xa9, 0xd7, 0x53, 0x60, 0x2a, + 0x7e, 0x54, 0x4, 0xa8, 0x9, 0x17, 0xd2, 0x20, + 0x24, 0x40, 0x48, 0x87, 0x0, + + /* U+9E3D "鸽" */ + 0x10, 0x20, 0x20, 0x40, 0xa3, 0xf1, 0x24, 0x24, + 0x6a, 0x50, 0x12, 0x9f, 0x21, 0x0, 0x4e, 0x0, + 0x80, 0xf9, 0xfd, 0x10, 0xa, 0x20, 0x14, 0x5f, + 0xa8, 0x80, 0x5f, 0x0, 0xa2, 0x1e, + + /* U+9E3E "鸾" */ + 0x1, 0x0, 0x2, 0x3, 0xff, 0xf8, 0x22, 0x3, + 0x45, 0x8c, 0xa9, 0xa0, 0x81, 0x9f, 0xf8, 0x24, + 0x10, 0x46, 0xe0, 0x80, 0x1, 0xff, 0xe0, 0x0, + 0x5f, 0xfe, 0x80, 0x1, 0x0, 0x3e, + + /* U+9E3F "鸿" */ + 0x0, 0x21, 0x1, 0x2, 0xf, 0xc7, 0xc1, 0x9, + 0x26, 0x24, 0x54, 0x90, 0x42, 0x4e, 0x9, 0x0, + 0x23, 0xf4, 0x80, 0x53, 0x81, 0x51, 0xfd, 0x0, + 0x18, 0x0, 0x60, 0x1e, + + /* U+9E41 "鹁" */ + 0x10, 0x20, 0x20, 0x83, 0xfb, 0xf0, 0x84, 0x2f, + 0xeb, 0x50, 0x50, 0xa0, 0xae, 0x1e, 0x40, 0x8, + 0xfe, 0x3c, 0x7, 0xc7, 0xe8, 0x80, 0x11, 0x0, + 0x42, 0x0, 0x9c, 0x1e, 0x0, + + /* U+9E42 "鹂" */ + 0x0, 0x1, 0xff, 0x20, 0x0, 0xbb, 0xbd, 0x55, + 0x4a, 0xaa, 0x94, 0x55, 0xab, 0xbb, 0x50, 0x75, + 0xbe, 0xab, 0x5, 0x53, 0xfa, 0xa4, 0x15, 0x48, + 0x2a, 0x90, 0x5d, 0x67, 0x80, + + /* U+9E43 "鹃" */ + 0x0, 0x43, 0xf1, 0x8, 0x5f, 0xa1, 0x42, 0x85, + 0x4b, 0xf5, 0x20, 0x10, 0xbf, 0x5c, 0x85, 0x3, + 0xf3, 0xf8, 0x40, 0x61, 0x79, 0xfc, 0x6, 0x10, + 0x18, 0x40, 0xa7, 0x1e, + + /* U+9E44 "鹄" */ + 0x0, 0x20, 0xa0, 0x41, 0x43, 0xf3, 0xf4, 0x25, + 0xa, 0x52, 0x12, 0x84, 0x21, 0x7f, 0x5c, 0x0, + 0x80, 0x1, 0xfd, 0xf0, 0xa, 0x2f, 0xd4, 0x40, + 0x28, 0x80, 0x5f, 0x1, 0x22, 0x1e, + + /* U+9E45 "鹅" */ + 0x4, 0x23, 0xd9, 0xf2, 0x54, 0x49, 0x19, 0x24, + 0x57, 0xfd, 0x12, 0x47, 0x89, 0x50, 0x26, 0x7f, + 0xd8, 0x12, 0x4f, 0x49, 0x1, 0x2c, 0x84, 0x8c, + 0x16, 0x13, 0x80, + + /* U+9E46 "鹆" */ + 0x0, 0x20, 0xa0, 0x84, 0x4f, 0xd0, 0xa1, 0x90, + 0xa4, 0x42, 0x52, 0x88, 0x51, 0x2e, 0xc2, 0x82, + 0x3, 0xf7, 0xc0, 0x51, 0x7d, 0x44, 0x5, 0x10, + 0x17, 0xc0, 0x51, 0x1e, + + /* U+9E47 "鹇" */ + 0x40, 0x20, 0xbd, 0xf8, 0xa, 0x14, 0x95, 0x29, + 0x29, 0x57, 0x50, 0xa4, 0xac, 0x5d, 0x40, 0xd6, + 0x7f, 0xa4, 0x6, 0x4b, 0xcc, 0x90, 0x38, 0x20, + 0x70, 0x40, 0xa3, 0x8f, 0x0, + + /* U+9E48 "鹈" */ + 0x44, 0x20, 0x51, 0xfb, 0xfa, 0x10, 0x95, 0x21, + 0x29, 0x5f, 0xd0, 0xa4, 0x2e, 0x48, 0x40, 0xfe, + 0xfe, 0x24, 0x4, 0xcb, 0xca, 0x90, 0x1d, 0x60, + 0x62, 0x0, 0x84, 0x1f, 0x0, + + /* U+9E49 "鹉" */ + 0x0, 0x0, 0x8, 0x41, 0xf5, 0xf0, 0x2a, 0x20, + 0x46, 0x5f, 0xea, 0x81, 0x11, 0xa, 0x3c, 0x54, + 0x40, 0xb8, 0xfd, 0x50, 0xa, 0xa7, 0xd5, 0x20, + 0x2b, 0xd0, 0x78, 0xa1, 0x0, 0xde, + + /* U+9E4A "鹊" */ + 0x24, 0x20, 0x48, 0xfb, 0xfd, 0x11, 0x23, 0x22, + 0x45, 0x5f, 0xe8, 0x80, 0x16, 0x0, 0x20, 0x7e, + 0x7e, 0x84, 0x5, 0xfb, 0xea, 0x10, 0x14, 0x20, + 0x2f, 0xc0, 0x90, 0x8f, 0x0, + + /* U+9E4B "鹋" */ + 0x48, 0x20, 0x90, 0x83, 0xfa, 0xf2, 0x45, 0x24, + 0x8a, 0x40, 0x10, 0xbf, 0xae, 0x49, 0x40, 0x92, + 0xfe, 0xfc, 0x6, 0x4f, 0xcc, 0x90, 0x19, 0x20, + 0x2f, 0xc0, 0xa0, 0x9e, 0x0, + + /* U+9E4C "鹌" */ + 0x10, 0x20, 0x20, 0x43, 0xfb, 0xf1, 0x45, 0x24, + 0x69, 0x5a, 0x10, 0xbf, 0x2e, 0x4a, 0x40, 0x7c, + 0xff, 0x28, 0x6, 0x50, 0xb, 0xef, 0xd1, 0x0, + 0x22, 0xc0, 0x86, 0x1f, 0x0, + + /* U+9E4E "鹎" */ + 0x10, 0x20, 0x20, 0x81, 0xfb, 0xf2, 0x94, 0x25, + 0x2a, 0x4f, 0xd2, 0x94, 0xa1, 0x29, 0x5c, 0x52, + 0x80, 0x59, 0xfc, 0x80, 0xa, 0x40, 0x1f, 0xef, + 0x21, 0x0, 0x42, 0x1, 0x4, 0x3c, + + /* U+9E4F "鹏" */ + 0x0, 0x8, 0xef, 0x11, 0x52, 0xfa, 0xa5, 0x15, + 0x4a, 0xae, 0xf5, 0x55, 0x28, 0xaa, 0x56, 0x54, + 0xa0, 0xa9, 0x7d, 0xde, 0xa, 0xa4, 0x15, 0x4b, + 0xaa, 0x90, 0x66, 0x20, 0xdd, 0xce, + + /* U+9E51 "鹑" */ + 0x0, 0x20, 0x20, 0x43, 0xfb, 0xf0, 0x4, 0x20, + 0xa, 0x4f, 0x92, 0xa1, 0x21, 0x3e, 0x5e, 0x0, + 0x81, 0xf9, 0xfc, 0x30, 0x8, 0x8f, 0xdf, 0xe0, + 0x22, 0x0, 0x44, 0x1, 0x30, 0x3e, + + /* U+9E55 "鹕" */ + 0x0, 0x1, 0x1c, 0x84, 0x95, 0xfe, 0x51, 0x49, + 0x55, 0x1d, 0x14, 0x95, 0xfe, 0x50, 0x99, 0x42, + 0x5c, 0xf9, 0x90, 0x7e, 0x5d, 0x89, 0x6, 0x44, + 0x11, 0x73, 0xc0, + + /* U+9E57 "鹗" */ + 0x0, 0x10, 0xee, 0x42, 0x65, 0xf4, 0xca, 0x29, + 0x96, 0x4e, 0xea, 0x80, 0x11, 0x3f, 0x2e, 0x0, + 0x41, 0xfe, 0xfd, 0x0, 0xb, 0xf7, 0xd0, 0x20, + 0x20, 0x40, 0x40, 0x81, 0xe, 0x1e, + + /* U+9E58 "鹘" */ + 0x0, 0x20, 0xfc, 0x41, 0xb, 0xf2, 0x14, 0x27, + 0xa8, 0x49, 0x56, 0xbf, 0xe1, 0x40, 0xdc, 0xbd, + 0x80, 0x89, 0xfc, 0xf0, 0xa, 0x27, 0xd3, 0xc0, + 0x28, 0x80, 0x51, 0x1, 0x26, 0x3e, + + /* U+9E5A "鹚" */ + 0x0, 0x10, 0x48, 0x40, 0x91, 0xf7, 0xfa, 0x22, + 0x25, 0x48, 0x8a, 0x91, 0x11, 0x4c, 0xac, 0xaa, + 0x40, 0xcc, 0xfd, 0x10, 0xa, 0x37, 0xd9, 0x90, + 0x37, 0xe0, 0x74, 0x41, 0x4, 0x1e, + + /* U+9E5B "鹛" */ + 0x0, 0x10, 0xfe, 0x21, 0x25, 0xfa, 0x4a, 0x24, + 0x94, 0x4f, 0xea, 0x90, 0x11, 0x2f, 0xae, 0x51, + 0x40, 0xbe, 0x7d, 0x44, 0xa, 0x88, 0x15, 0xf7, + 0xb2, 0x20, 0xe7, 0xc1, 0xc8, 0x8e, + + /* U+9E5C "鹜" */ + 0x0, 0x0, 0x7f, 0x40, 0x24, 0x7c, 0xff, 0x48, + 0x19, 0x30, 0x68, 0x38, 0x98, 0xc7, 0x4, 0x0, + 0x3f, 0xf0, 0x23, 0x70, 0x20, 0x0, 0x1f, 0xfe, + 0xff, 0xe2, 0x0, 0x2, 0x0, 0x7c, + + /* U+9E5E "鹞" */ + 0x0, 0x21, 0xfc, 0x40, 0x3, 0xf4, 0x94, 0x25, + 0x4a, 0x40, 0x92, 0x90, 0x21, 0x3f, 0x5c, 0x90, + 0x80, 0x21, 0xfd, 0xf0, 0x8, 0x8f, 0xd1, 0x0, + 0x32, 0x40, 0x5f, 0x81, 0x1, 0x3e, + + /* U+9E63 "鹣" */ + 0x44, 0x20, 0xa1, 0xff, 0xf8, 0x4a, 0x29, 0xfe, + 0x94, 0xaa, 0x1f, 0xf9, 0xa, 0xa0, 0x2a, 0x7f, + 0xf8, 0x12, 0x80, 0x5b, 0x3f, 0xaa, 0x6, 0xa0, + 0x12, 0x83, 0xc0, + + /* U+9E64 "鹤" */ + 0x8, 0x10, 0x8, 0x10, 0x7f, 0x7e, 0x51, 0x42, + 0x75, 0x4a, 0x3f, 0x42, 0x24, 0x5c, 0x64, 0x40, + 0x3e, 0x7f, 0x24, 0x1, 0x3f, 0x1, 0x24, 0x79, + 0x24, 0x1, 0x3f, 0x2, 0x20, 0x1e, + + /* U+9E66 "鹦" */ + 0x0, 0x13, 0xfc, 0x49, 0x97, 0xe6, 0xd1, 0xdb, + 0x57, 0x6d, 0x5d, 0xd4, 0x59, 0x96, 0x55, 0x42, + 0x21, 0xf2, 0x0, 0x7f, 0xc1, 0x44, 0xfc, 0xd0, + 0x11, 0xe0, 0x78, 0x4e, + + /* U+9E67 "鹧" */ + 0x8, 0x10, 0x10, 0x21, 0xff, 0xfa, 0x2, 0x15, + 0x25, 0x2a, 0x49, 0x5f, 0xd0, 0xa9, 0x2e, 0x52, + 0x40, 0xa4, 0xfd, 0x78, 0xa, 0x0, 0x16, 0xd7, + 0xad, 0x90, 0x6b, 0x20, 0xc0, 0xe, + + /* U+9E68 "鹨" */ + 0x0, 0x10, 0xff, 0x10, 0x11, 0x7e, 0x55, 0x42, + 0x55, 0x52, 0x37, 0x4a, 0xd1, 0x42, 0x1c, 0x5e, + 0x32, 0x40, 0xc9, 0x7e, 0x76, 0x2, 0x9, 0xfa, + 0x32, 0x2, 0x44, 0x2, 0x18, 0x2, 0xe0, 0x3e, + + /* U+9E69 "鹩" */ + 0x10, 0x21, 0xfd, 0xf8, 0x42, 0x13, 0x65, 0x26, + 0x49, 0x5f, 0xd0, 0xa1, 0x2e, 0x42, 0x40, 0xfc, + 0xfe, 0xf8, 0x4, 0x47, 0xca, 0xa0, 0x19, 0x20, + 0x22, 0x0, 0x98, 0x1e, 0x0, + + /* U+9E6A "鹪" */ + 0x28, 0x21, 0x20, 0x87, 0xff, 0xf2, 0x29, 0xc8, + 0x95, 0xfa, 0x24, 0x8b, 0x9f, 0xa0, 0x48, 0xfd, + 0xfc, 0x10, 0xf, 0xd2, 0x41, 0xa9, 0x6, 0x94, + 0x1a, 0x7, 0x80, + + /* U+9E6B "鹫" */ + 0x8, 0x41, 0xfe, 0x90, 0x1, 0x3, 0xf3, 0xe7, + 0xe5, 0x9, 0x92, 0x6d, 0xa3, 0x2, 0x0, 0x3b, + 0xf0, 0x4e, 0xe0, 0x80, 0x0, 0xff, 0xef, 0xfe, + 0x40, 0x0, 0x80, 0x1e, 0x0, + + /* U+9E6C "鹬" */ + 0x0, 0x10, 0x7f, 0x90, 0x3, 0x3e, 0x1c, 0x42, + 0x7f, 0xd2, 0x14, 0xca, 0x65, 0x42, 0x18, 0x5c, + 0x7f, 0xc0, 0x44, 0xbf, 0x52, 0x81, 0x5e, 0x81, + 0x52, 0xba, 0x5e, 0x82, 0x40, 0x82, 0x43, 0x9c, + + /* U+9E6D "鹭" */ + 0x0, 0x80, 0xf1, 0xf9, 0x26, 0x13, 0xd3, 0xc1, + 0x3c, 0xf3, 0xc0, 0x25, 0x7f, 0x76, 0xfe, 0x4, + 0x0, 0x77, 0xe0, 0x9b, 0x81, 0x0, 0x1, 0xff, + 0xdf, 0xfc, 0x80, 0x1, 0x0, 0x7e, + + /* U+9E70 "鹰" */ + 0x0, 0x80, 0x3f, 0xff, 0x20, 0x90, 0x25, 0x7c, + 0x2d, 0xfe, 0x37, 0x10, 0x25, 0xfc, 0x25, 0xfe, + 0x20, 0x80, 0x27, 0xfc, 0x24, 0x98, 0x27, 0xfe, + 0x40, 0x2, 0x5f, 0xfa, 0x40, 0x3e, + + /* U+9E71 "鹱" */ + 0x10, 0x48, 0x23, 0xfe, 0x5c, 0x48, 0x44, 0x90, + 0x55, 0xff, 0x45, 0x10, 0x5b, 0xfc, 0x41, 0xfe, + 0x7d, 0x10, 0x5, 0xfe, 0x3d, 0xfc, 0xc4, 0x84, + 0x4, 0x48, 0x4, 0x78, 0x3b, 0x86, + + /* U+9E73 "鹳" */ + 0x24, 0x13, 0xfd, 0xf2, 0x44, 0x5d, 0xd1, 0x99, + 0x55, 0xdd, 0x10, 0x85, 0x92, 0x10, 0x7e, 0x7f, + 0x20, 0x17, 0xe0, 0x52, 0x3d, 0x7e, 0x5, 0xfc, + 0x14, 0x3, 0x80, + + /* U+9E7E "鹾" */ + 0x20, 0x88, 0x78, 0xa0, 0x87, 0xf1, 0x1, 0x2, + 0x4, 0x1b, 0xbf, 0xb3, 0x10, 0x6b, 0xff, 0xb4, + 0x81, 0x69, 0x3, 0x57, 0xf6, 0x79, 0x8, 0x42, + 0x1f, 0xbf, 0xe1, 0x0, 0x0, + + /* U+9E7F "鹿" */ + 0x0, 0x0, 0x1, 0x0, 0xff, 0xf9, 0x12, 0x2, + 0x24, 0x7, 0xff, 0xc8, 0x90, 0x91, 0x21, 0x3f, + 0xfe, 0x40, 0x0, 0xa1, 0x9, 0x7b, 0xe6, 0x84, + 0x9, 0x8, 0x12, 0xd0, 0xc6, 0x3e, + + /* U+9E82 "麂" */ + 0x0, 0x80, 0x0, 0xc0, 0x3f, 0xff, 0x22, 0x20, + 0x3f, 0xfe, 0x22, 0x22, 0x3f, 0xfe, 0x28, 0x20, + 0x2f, 0x3d, 0x28, 0x21, 0x2f, 0x3e, 0x20, 0x0, + 0x23, 0xf0, 0x44, 0x11, 0x44, 0x12, 0x98, 0x1e, + + /* U+9E87 "麇" */ + 0x0, 0x80, 0xff, 0xfd, 0x11, 0x3, 0xff, 0xe4, + 0x44, 0x4f, 0xff, 0x92, 0x12, 0x27, 0x39, 0x4f, + 0x3c, 0x80, 0x1, 0x3f, 0xc2, 0xff, 0xe8, 0x1c, + 0x11, 0xd7, 0x2c, 0x21, 0x0, + + /* U+9E88 "麈" */ + 0x0, 0x80, 0x3f, 0xff, 0x22, 0x20, 0x3f, 0xfe, + 0x22, 0x22, 0x3d, 0xdc, 0x2f, 0x7c, 0x28, 0x43, + 0x2f, 0x3e, 0x20, 0x40, 0x2f, 0xfe, 0x60, 0x40, + 0x47, 0xf8, 0x5f, 0xff, 0x0, 0x0, + + /* U+9E8B "麋" */ + 0x0, 0x80, 0xff, 0xfd, 0x11, 0x3, 0xff, 0xe4, + 0x44, 0x4f, 0xff, 0x92, 0x13, 0x27, 0x39, 0x4f, + 0x3c, 0x80, 0x1, 0x32, 0xc3, 0xff, 0xf8, 0x3c, + 0x11, 0x97, 0x2c, 0x21, 0x80, + + /* U+9E92 "麒" */ + 0x8, 0x44, 0x10, 0x89, 0xfd, 0x12, 0xa7, 0xf5, + 0x44, 0x4f, 0xe8, 0x95, 0x5f, 0x2a, 0xa2, 0x7f, + 0x44, 0x80, 0xf9, 0x51, 0x12, 0xb7, 0xf5, 0x40, + 0x12, 0x85, 0x27, 0xd1, 0xc0, 0x41, + + /* U+9E93 "麓" */ + 0x8, 0x10, 0x7f, 0x7f, 0x8, 0x18, 0x3f, 0x76, + 0x40, 0x1, 0x3f, 0xff, 0x22, 0x20, 0x3f, 0xfe, + 0x22, 0x22, 0x3f, 0xfe, 0x28, 0x20, 0x4f, 0x3e, + 0x48, 0x21, 0x4b, 0x21, 0x8c, 0x3e, + + /* U+9E9D "麝" */ + 0x0, 0x80, 0x3f, 0xff, 0x22, 0x20, 0x3f, 0xfe, + 0x22, 0x22, 0x3d, 0xfc, 0x2f, 0x3d, 0x28, 0x22, + 0x2f, 0x3e, 0x3f, 0x84, 0x3f, 0x9e, 0x30, 0x84, + 0x3f, 0xa4, 0x5f, 0x94, 0x42, 0x84, 0x9d, 0x9c, + + /* U+9E9F "麟" */ + 0x0, 0x10, 0x8, 0x12, 0x7f, 0x52, 0x54, 0x14, + 0x54, 0xff, 0x7e, 0x38, 0x56, 0x74, 0x56, 0x52, + 0x56, 0x91, 0x7c, 0x6f, 0x54, 0xa4, 0x57, 0x74, + 0x54, 0x2f, 0x54, 0x44, 0x96, 0xc4, 0x1, 0x84, + + /* U+9EA6 "麦" */ + 0x1, 0x0, 0x2, 0x3, 0xff, 0xf8, 0x8, 0x0, + 0x10, 0xf, 0xff, 0x80, 0x40, 0x7f, 0xff, 0x4, + 0x0, 0x10, 0x0, 0x7f, 0xe1, 0x40, 0x8c, 0x42, + 0x0, 0x78, 0x1, 0xf8, 0x7c, 0xf, + + /* U+9EB4 "麴" */ + 0x0, 0x80, 0x82, 0xf, 0xdf, 0xc8, 0x41, 0x6a, + 0x7, 0xa9, 0x1a, 0xd5, 0x4c, 0x59, 0x6c, 0x46, + 0xf, 0xd7, 0x8c, 0x52, 0x59, 0x89, 0x54, 0xc9, + 0x12, 0x84, 0x70, 0xe, + + /* U+9EB8 "麸" */ + 0x10, 0x20, 0x20, 0x43, 0xf8, 0x80, 0x8f, 0xe7, + 0xe2, 0x2, 0x4, 0x3f, 0x88, 0x10, 0x7e, 0x3c, + 0x60, 0x88, 0xc2, 0x92, 0x40, 0xc4, 0x81, 0xd0, + 0x8c, 0x40, 0xe0, 0x0, 0x0, + + /* U+9EBB "麻" */ + 0x0, 0x80, 0x0, 0x80, 0x3f, 0xff, 0x20, 0x0, + 0x22, 0x10, 0x2f, 0x7e, 0x22, 0x10, 0x26, 0x18, + 0x27, 0x38, 0x2a, 0xd4, 0x2a, 0x54, 0x32, 0x92, + 0x52, 0x90, 0x42, 0x10, 0x2, 0x10, + + /* U+9EBD "麽" */ + 0x0, 0x80, 0x1, 0x1, 0xfd, 0xf2, 0x20, 0x85, + 0xf7, 0xc8, 0x82, 0x13, 0x8e, 0x2a, 0xaa, 0x40, + 0x90, 0x86, 0x1, 0x11, 0x2, 0xcc, 0x8, 0x61, + 0x11, 0xff, 0x0, 0x0, 0x0, + + /* U+9EBE "麾" */ + 0x1, 0x0, 0x4, 0x7, 0xef, 0xd1, 0x8, 0x5e, + 0xfd, 0x39, 0xc7, 0x4a, 0xd0, 0x0, 0x5f, 0xf9, + 0x8, 0x5, 0xff, 0x90, 0x80, 0xff, 0xfe, 0x8, + 0x18, 0x1f, 0xc0, + + /* U+9EC4 "黄" */ + 0x8, 0x21, 0xff, 0xfc, 0x20, 0x80, 0x41, 0xf, + 0xff, 0xe0, 0x20, 0xf, 0xfe, 0x10, 0x84, 0x21, + 0x8, 0x7f, 0xf0, 0x84, 0x21, 0xff, 0xc0, 0x0, + 0x7, 0x7, 0x30, 0x1, 0x0, + + /* U+9EC9 "黉" */ + 0x21, 0x8, 0x22, 0x23, 0xff, 0xfc, 0x41, 0x1b, + 0xff, 0xa1, 0x4, 0x3f, 0xff, 0x80, 0x80, 0x3f, + 0xf8, 0x42, 0x10, 0xff, 0xe1, 0x8, 0x43, 0xff, + 0x80, 0x8, 0xe, 0xf, 0x20, 0x0, + + /* U+9ECD "黍" */ + 0x7f, 0xf8, 0x8, 0xf, 0xff, 0xc0, 0x80, 0xf, + 0xc0, 0x80, 0xec, 0x30, 0x43, 0x38, 0x72, 0x1e, + 0x3b, 0x0, 0x68, 0xe, 0x9c, 0x42, 0x8, 0x38, + 0x0, + + /* U+9ECE "黎" */ + 0x0, 0x20, 0x7e, 0x20, 0x8, 0x7e, 0x7e, 0x92, + 0x8, 0x12, 0x3e, 0x62, 0x48, 0x9c, 0xb, 0xc0, + 0xc, 0x30, 0x69, 0x1e, 0x7, 0x60, 0x3, 0x0, + 0x1d, 0x38, 0x61, 0x6, 0x7, 0x0, + + /* U+9ECF "黏" */ + 0x0, 0x10, 0x4, 0x23, 0xf8, 0x40, 0x80, 0x8f, + 0xf1, 0xe5, 0x82, 0x30, 0xc4, 0xc, 0x8, 0x26, + 0xfd, 0x83, 0x9, 0xda, 0x10, 0x84, 0x23, 0x48, + 0x5a, 0x70, 0x84, 0x3f, 0x18, 0x42, + + /* U+9ED1 "黑" */ + 0x7f, 0xfc, 0x82, 0x9, 0x24, 0x92, 0xa, 0x27, + 0xff, 0xc0, 0x20, 0x0, 0x40, 0x3f, 0xfe, 0x1, + 0x1, 0xff, 0xfc, 0x0, 0x1, 0x22, 0x24, 0x44, + 0x40, 0x0, 0x0, + + /* U+9ED4 "黔" */ + 0x0, 0x10, 0x7e, 0x10, 0x52, 0x28, 0x76, 0x6c, + 0x56, 0x44, 0x52, 0xa2, 0x7f, 0x10, 0x10, 0x0, + 0x7e, 0xfe, 0x10, 0x2, 0x7e, 0x4, 0x0, 0x4, + 0x5a, 0x8, 0x55, 0x18, 0x90, 0x10, + + /* U+9ED8 "默" */ + 0x0, 0x10, 0x0, 0x10, 0x7f, 0x14, 0x49, 0x14, + 0x5d, 0x10, 0x49, 0x7e, 0x7f, 0x10, 0x8, 0x10, + 0x7f, 0x30, 0x8, 0x28, 0xb, 0x28, 0x74, 0x28, + 0x55, 0x44, 0x54, 0x46, 0x90, 0x83, + + /* U+9EDB "黛" */ + 0x9, 0x10, 0x21, 0x11, 0xbf, 0xfd, 0x2, 0x10, + 0x3, 0x27, 0xff, 0x8a, 0x4a, 0x12, 0xa4, 0x3f, + 0xf8, 0x2, 0x1, 0xff, 0xf0, 0x8, 0xf, 0xff, + 0xe8, 0x80, 0x31, 0x11, 0x0, + + /* U+9EDC "黜" */ + 0x0, 0x8, 0xfe, 0x11, 0x25, 0x2b, 0x5a, 0x55, + 0xd4, 0xa9, 0x29, 0x5f, 0xdf, 0x84, 0x4, 0x7f, + 0x8, 0x11, 0x15, 0xfa, 0x28, 0xc, 0x55, 0x58, + 0xaa, 0x8f, 0xe4, 0x0, 0x80, + + /* U+9EDD "黝" */ + 0x0, 0x8, 0xfd, 0x11, 0x4a, 0x23, 0xb9, 0xf5, + 0x34, 0xaf, 0xf9, 0x44, 0x12, 0x88, 0x45, 0x7e, + 0x8a, 0x21, 0x95, 0xfd, 0x28, 0xe, 0x94, 0xa3, + 0x2d, 0x44, 0x64, 0xb, 0x80, + + /* U+9EDF "黟" */ + 0x0, 0x0, 0xfc, 0x41, 0x48, 0xfb, 0xb2, 0x17, + 0x6a, 0x4b, 0x43, 0x14, 0x84, 0x3f, 0x10, 0x10, + 0xd0, 0xfc, 0x3c, 0x40, 0x8b, 0xff, 0x20, 0x1, + 0x49, 0x43, 0x2a, 0x48, 0x40, 0x60, + + /* U+9EE0 "黠" */ + 0x0, 0x8, 0x7f, 0x8, 0x4b, 0xff, 0x6d, 0x8, + 0x5d, 0x8, 0x49, 0x8, 0x7f, 0x7f, 0x8, 0x0, + 0x7f, 0x0, 0x8, 0x7f, 0x7e, 0x41, 0x1, 0x41, + 0x55, 0x41, 0x55, 0x7f, 0x80, 0x41, + + /* U+9EE2 "黢" */ + 0x0, 0x10, 0x7e, 0x20, 0x52, 0x44, 0x76, 0x82, + 0x52, 0x7c, 0x7e, 0x44, 0x10, 0x82, 0x10, 0x20, + 0x7e, 0x7c, 0x10, 0x44, 0x7e, 0xc4, 0x0, 0x28, + 0x4a, 0x10, 0x58, 0x28, 0x91, 0xc7, + + /* U+9EE5 "黥" */ + 0x0, 0x10, 0x7e, 0x10, 0x52, 0xff, 0x76, 0x0, + 0x76, 0x0, 0x52, 0x7e, 0x7e, 0x42, 0x10, 0x42, + 0x7e, 0x7e, 0x10, 0x10, 0x7e, 0x54, 0x0, 0x52, + 0x4a, 0x91, 0x68, 0x10, 0x90, 0x30, + + /* U+9EE7 "黧" */ + 0x6, 0x40, 0xf0, 0xff, 0xfa, 0x48, 0xc1, 0x16, + 0xe4, 0xf0, 0x0, 0xf, 0xff, 0x14, 0x8a, 0x25, + 0x24, 0x3f, 0xf0, 0xff, 0xf0, 0x8, 0xf, 0xff, + 0xe4, 0x8, 0x90, 0x90, 0x80, + + /* U+9EE9 "黩" */ + 0x0, 0x10, 0x7e, 0x10, 0x52, 0xfe, 0x76, 0x10, + 0x76, 0x10, 0x52, 0x6f, 0x7e, 0x49, 0x10, 0x28, + 0x7e, 0x88, 0x10, 0x48, 0xe, 0xff, 0x30, 0x10, + 0x4a, 0x18, 0x69, 0x24, 0x90, 0xc3, + + /* U+9EEA "黪" */ + 0x0, 0x10, 0x7e, 0x20, 0x52, 0x44, 0x76, 0xfe, + 0x52, 0x0, 0x7f, 0xfe, 0x10, 0x48, 0x10, 0x84, + 0x7e, 0xb2, 0x11, 0x48, 0x7e, 0x10, 0x2, 0x66, + 0x5a, 0x88, 0x54, 0x30, 0x80, 0xc0, + + /* U+9EEF "黯" */ + 0x0, 0x21, 0xf8, 0x42, 0x97, 0xf7, 0x64, 0x4a, + 0x41, 0x1f, 0xff, 0xc8, 0x0, 0x10, 0x0, 0xfd, + 0xfc, 0x42, 0xb, 0x67, 0xf0, 0x28, 0x2b, 0x50, + 0x55, 0x3f, 0xa8, 0x41, 0x0, + + /* U+9EF9 "黹" */ + 0x4, 0x40, 0xc8, 0x90, 0x51, 0x40, 0x22, 0xf, + 0xff, 0xe2, 0x22, 0x2, 0x48, 0x3f, 0xfe, 0x41, + 0x4, 0x8a, 0x89, 0x24, 0x92, 0x49, 0x25, 0x10, + 0x48, 0x20, 0x90, 0x4f, 0x0, + + /* U+9EFB "黻" */ + 0x28, 0x29, 0x52, 0x51, 0xa8, 0x81, 0x47, 0xef, + 0xf4, 0x2, 0x48, 0x15, 0x1f, 0x7f, 0x22, 0x92, + 0x69, 0x65, 0x52, 0xda, 0xa6, 0xb0, 0x89, 0x22, + 0x92, 0x48, 0xa5, 0xa0, 0x0, + + /* U+9EFC "黼" */ + 0xaa, 0x10, 0xdb, 0xfc, 0xa0, 0x47, 0xf0, 0x81, + 0xf, 0xca, 0x92, 0x84, 0x25, 0x7f, 0x7e, 0xba, + 0x95, 0xb5, 0xfb, 0x4a, 0x54, 0x94, 0xa9, 0x29, + 0x53, 0x93, 0x80, + + /* U+9EFE "黾" */ + 0x7f, 0xe1, 0x0, 0x84, 0x2, 0x1f, 0xf8, 0x4, + 0x0, 0x10, 0xf, 0xff, 0x21, 0x4, 0xff, 0xf2, + 0x10, 0x48, 0x41, 0x3f, 0xfc, 0x84, 0x4, 0x10, + 0x10, 0x3f, 0x80, + + /* U+9F0B "鼋" */ + 0x7f, 0xf8, 0x0, 0x0, 0xff, 0xfe, 0x4, 0x42, + 0x38, 0x7e, 0xff, 0xf0, 0x20, 0x10, 0x3f, 0xf0, + 0x1, 0x0, 0x7f, 0xfc, 0x7f, 0xfc, 0x41, 0x4, + 0x7f, 0xfd, 0x1, 0x2, 0x1, 0xfe, + + /* U+9F0D "鼍" */ + 0x7c, 0xfc, 0x7c, 0xfc, 0x0, 0x0, 0x3f, 0xf8, + 0x3f, 0xf8, 0x3f, 0xf8, 0xff, 0xfe, 0x0, 0x0, + 0x3f, 0xf8, 0x3f, 0xf8, 0x7f, 0xfc, 0x7f, 0xfc, + 0x7f, 0xff, 0x41, 0x2, 0x1, 0xfe, + + /* U+9F0E "鼎" */ + 0x4f, 0xe5, 0x3f, 0x94, 0x82, 0x53, 0xf9, 0x48, + 0x25, 0x3f, 0x94, 0x0, 0x5f, 0x3f, 0x4, 0x80, + 0x12, 0xf, 0xcf, 0xc9, 0x21, 0x24, 0x87, 0x12, + 0x10, + + /* U+9F10 "鼐" */ + 0xff, 0xe0, 0x40, 0x81, 0x1, 0xcf, 0xf9, 0xd0, + 0x29, 0x7f, 0x85, 0xfe, 0x94, 0xa, 0x5f, 0xe9, + 0x0, 0x23, 0xcf, 0x3f, 0x3e, 0x44, 0x89, 0x12, + 0x28, 0x48, 0x80, + + /* U+9F13 "鼓" */ + 0x10, 0x21, 0xfc, 0x40, 0x47, 0xf0, 0x81, 0xf, + 0xe2, 0x0, 0x4, 0x1f, 0xff, 0x41, 0x42, 0x82, + 0x88, 0xfc, 0x90, 0x11, 0x42, 0x21, 0x2, 0x85, + 0xf, 0xd1, 0xa0, 0x40, 0x0, + + /* U+9F17 "鼗" */ + 0x45, 0x0, 0xd5, 0xc0, 0xc8, 0x1d, 0x5e, 0x8, + 0x85, 0xc3, 0xff, 0xe2, 0x4, 0x7f, 0xfe, 0x20, + 0x0, 0x8f, 0xed, 0x9f, 0x92, 0x44, 0x50, 0xa1, + 0xcf, 0xf8, 0xc0, 0x0, + + /* U+9F19 "鼙" */ + 0x10, 0x10, 0xfd, 0xfd, 0xf8, 0x43, 0xf7, 0xe8, + 0x24, 0x4f, 0xc5, 0x9, 0xc, 0x6d, 0x67, 0x3f, + 0xf8, 0x42, 0x10, 0xff, 0xe1, 0xff, 0xc0, 0xc8, + 0x1f, 0xff, 0xc0, 0x20, 0x0, + + /* U+9F20 "鼠" */ + 0x0, 0x3, 0xef, 0xc8, 0x1, 0x3e, 0xfc, 0x80, + 0x13, 0xff, 0xc0, 0x0, 0x21, 0x8, 0xb5, 0xa2, + 0x10, 0x8a, 0x52, 0x25, 0x28, 0x84, 0x27, 0xde, + 0x58, 0x41, 0x80, + + /* U+9F22 "鼢" */ + 0x0, 0x13, 0xb9, 0x48, 0x25, 0x3b, 0xa2, 0x82, + 0x8a, 0xc, 0x1f, 0xe0, 0x24, 0xbe, 0xda, 0x4a, + 0x49, 0x29, 0x24, 0xad, 0xd2, 0x93, 0x4b, 0xfa, + 0x29, 0x13, 0x80, + + /* U+9F2C "鼬" */ + 0x20, 0x13, 0x70, 0x48, 0x41, 0x3b, 0x3f, 0x84, + 0x97, 0xf2, 0x50, 0x9, 0x65, 0x25, 0xdc, 0xfe, + 0x52, 0x5d, 0x49, 0x67, 0x25, 0x94, 0x97, 0xfb, + 0xf9, 0x68, 0x40, + + /* U+9F2F "鼯" */ + 0x20, 0x1, 0xbd, 0xfe, 0x9, 0x6, 0x77, 0xe8, + 0x24, 0x5f, 0xc8, 0x80, 0x11, 0x49, 0x7f, 0xda, + 0x1, 0x25, 0xfb, 0x6a, 0x14, 0x94, 0x29, 0x38, + 0x5b, 0x7f, 0xa4, 0xe1, 0x0, + + /* U+9F37 "鼷" */ + 0x20, 0x1, 0xbb, 0xfa, 0x10, 0x97, 0xe9, 0x48, + 0x46, 0x10, 0x91, 0xbf, 0x7c, 0x4a, 0x24, 0xbd, + 0xfd, 0x28, 0x42, 0xf7, 0xfc, 0xa1, 0x9, 0x45, + 0x1b, 0xd9, 0x21, 0xc1, 0x0, + + /* U+9F39 "鼹" */ + 0x20, 0x1, 0xb9, 0xfa, 0x14, 0x17, 0xef, 0xe8, + 0x4f, 0xdf, 0x84, 0x0, 0x8, 0x0, 0xff, 0xd5, + 0x3, 0x2b, 0xff, 0x51, 0x25, 0xe4, 0x49, 0x4d, + 0x12, 0xc6, 0x37, 0xb3, 0x80, + + /* U+9F3B "鼻" */ + 0x2, 0x0, 0x7f, 0xf0, 0x80, 0x21, 0xff, 0xc2, + 0x0, 0x87, 0xff, 0xf, 0xfe, 0x0, 0x0, 0x7f, + 0xf8, 0x7f, 0xf1, 0x4, 0x21, 0xf7, 0x8f, 0xff, + 0xe1, 0x4, 0x4, 0x8, 0x30, 0x10, + + /* U+9F3D "鼽" */ + 0x10, 0x20, 0xdc, 0x41, 0xf8, 0x82, 0x17, 0xc7, + 0xe2, 0x8f, 0xc5, 0x0, 0xa, 0x7f, 0x94, 0x7f, + 0x29, 0x12, 0x51, 0xd9, 0x2f, 0xfa, 0x52, 0x44, + 0xa4, 0x91, 0x71, 0x43, 0x80, + + /* U+9F3E "鼾" */ + 0x8, 0x0, 0x7f, 0x7f, 0x41, 0x8, 0x7f, 0x8, + 0x41, 0x8, 0x7f, 0x8, 0x7f, 0x8, 0x0, 0x8, + 0xff, 0x7e, 0x7f, 0x8, 0x89, 0x8, 0x76, 0x8, + 0xff, 0x88, 0x22, 0x8, 0x22, 0x8, 0xc2, 0x8, + + /* U+9F44 "齄" */ + 0x10, 0x10, 0x20, 0x21, 0xb7, 0xfb, 0xe1, 0x84, + 0x45, 0x8f, 0xb2, 0xdf, 0x40, 0x0, 0x7e, 0xfc, + 0x85, 0x29, 0xb, 0xf3, 0xf7, 0xe4, 0x20, 0xf, + 0xdf, 0xc0, 0x12, 0x7f, 0xe4, 0x0, + + /* U+9F50 "齐" */ + 0x2, 0x0, 0x4, 0xf, 0xff, 0xc4, 0x8, 0x18, + 0x40, 0x12, 0x0, 0x30, 0x7, 0x38, 0xe0, 0x1c, + 0x41, 0x1, 0x4, 0x4, 0x10, 0x10, 0x40, 0x81, + 0xc, 0x4, 0x0, + + /* U+9F51 "齑" */ + 0x0, 0x0, 0x2, 0x3, 0xff, 0xf8, 0x40, 0x80, + 0x66, 0x0, 0x78, 0x3e, 0x9f, 0x91, 0x22, 0x2e, + 0x74, 0x44, 0x88, 0xb9, 0xd1, 0x12, 0x22, 0xe7, + 0x48, 0x48, 0xb7, 0xff, 0x40, 0x2, + + /* U+9F7F "齿" */ + 0x0, 0x80, 0x21, 0xf8, 0x42, 0x0, 0x84, 0x1, + 0x8, 0x1f, 0xff, 0xd0, 0x0, 0x20, 0x82, 0x41, + 0x84, 0x84, 0xc9, 0x30, 0x52, 0x0, 0x24, 0x0, + 0x4f, 0xff, 0x80, 0x1, 0x0, + + /* U+9F80 "龀" */ + 0x10, 0x40, 0xa0, 0x81, 0x79, 0x2, 0x82, 0x25, + 0x4, 0x5f, 0xc9, 0x4, 0x14, 0x4a, 0x28, 0x94, + 0x61, 0x69, 0x82, 0xb5, 0xc, 0x22, 0x18, 0x44, + 0x2f, 0x88, 0x41, 0xf, 0x0, + + /* U+9F83 "龃" */ + 0x8, 0x0, 0x91, 0xf9, 0x3a, 0x12, 0x44, 0x24, + 0x88, 0x5f, 0xdf, 0x80, 0x21, 0x49, 0x42, 0x92, + 0x85, 0x35, 0xfa, 0x9a, 0x14, 0x14, 0x28, 0x28, + 0x4f, 0xd0, 0x80, 0xff, 0x80, + + /* U+9F84 "龄" */ + 0x8, 0x20, 0x90, 0x41, 0x39, 0x42, 0x44, 0x84, + 0x88, 0x9f, 0xe4, 0x80, 0x8, 0x49, 0x0, 0x93, + 0xfd, 0x34, 0xa, 0x98, 0x25, 0x1c, 0x88, 0x26, + 0xf, 0xc6, 0x0, 0x84, 0x0, + + /* U+9F85 "龅" */ + 0x0, 0x40, 0x50, 0x40, 0x4e, 0xfe, 0x50, 0x82, + 0x51, 0x2, 0xfe, 0xf2, 0x10, 0x92, 0x92, 0x92, + 0x92, 0xf2, 0x9a, 0x82, 0xaa, 0x9c, 0x82, 0x81, + 0x82, 0x81, 0x7e, 0x82, 0x2, 0x7e, + + /* U+9F86 "龆" */ + 0x8, 0x1, 0x23, 0xf4, 0xe2, 0x52, 0x9, 0x48, + 0x47, 0xf9, 0x11, 0x9, 0xa4, 0x80, 0x92, 0xfe, + 0x6a, 0x1a, 0x68, 0x60, 0xa1, 0x82, 0x85, 0xfb, + 0xf0, 0x28, 0x40, + + /* U+9F87 "龇" */ + 0x10, 0x48, 0x50, 0x48, 0x5c, 0x48, 0x51, 0x48, + 0x51, 0x4a, 0xfd, 0x6c, 0x1, 0x48, 0xa9, 0x48, + 0xa9, 0x48, 0xb9, 0x48, 0xd9, 0x48, 0x89, 0x4a, + 0x89, 0x4a, 0xf9, 0xea, 0xa, 0xe, + + /* U+9F88 "龈" */ + 0x8, 0x0, 0x91, 0xf9, 0x3a, 0x12, 0x44, 0x24, + 0x8f, 0xdf, 0xd0, 0x80, 0x21, 0x49, 0x7e, 0x92, + 0xa1, 0x35, 0x4e, 0x9a, 0x65, 0x34, 0x88, 0x28, + 0x8f, 0xdc, 0x80, 0xa0, 0x0, + + /* U+9F89 "龉" */ + 0x8, 0x0, 0x93, 0xfd, 0x39, 0x2, 0x42, 0x4, + 0x9f, 0xdf, 0xc8, 0x84, 0x11, 0x49, 0xff, 0x92, + 0x1, 0x35, 0xfa, 0x9a, 0x14, 0x14, 0x28, 0x28, + 0x4f, 0xdf, 0x80, 0xa1, 0x0, + + /* U+9F8A "龊" */ + 0x8, 0x0, 0x48, 0xfe, 0x4e, 0x82, 0x48, 0x82, + 0x48, 0x82, 0xfe, 0xfe, 0x0, 0x10, 0x52, 0x10, + 0x52, 0x90, 0x5a, 0x9e, 0x6a, 0x90, 0x66, 0x90, + 0x42, 0xd0, 0x7f, 0x30, 0x3, 0x1e, + + /* U+9F8B "龋" */ + 0x10, 0x0, 0x50, 0xfa, 0x4e, 0x10, 0x50, 0xfe, + 0x50, 0x92, 0xfe, 0x92, 0x10, 0xfe, 0x94, 0x10, + 0x94, 0xfe, 0xad, 0x12, 0xad, 0x16, 0x85, 0x7e, + 0x85, 0x6, 0x7d, 0x2, 0x5, 0xe, + + /* U+9F8C "龌" */ + 0x10, 0x0, 0xa3, 0xf9, 0x64, 0x12, 0x8f, 0xe5, + 0x10, 0x15, 0xaf, 0xc0, 0x48, 0x54, 0xa2, 0xa9, + 0x7d, 0x72, 0x23, 0x64, 0x46, 0x4b, 0xe8, 0xa1, + 0xe, 0x5f, 0xc0, 0x0, 0x0, + + /* U+9F99 "龙" */ + 0x4, 0x60, 0x8, 0x20, 0x10, 0x7, 0xff, 0xf0, + 0x88, 0x1, 0x10, 0x2, 0x23, 0x4, 0x44, 0x8, + 0x90, 0x11, 0x40, 0x43, 0x8, 0x8c, 0x12, 0x68, + 0x28, 0x10, 0x60, 0x3f, 0x0, + + /* U+9F9A "龚" */ + 0x2, 0x20, 0x4, 0x21, 0xff, 0xf8, 0x24, 0x40, + 0x8f, 0x22, 0x70, 0x58, 0x1f, 0x4, 0x10, 0x7f, + 0xfc, 0x10, 0x40, 0x20, 0x87, 0xff, 0xf0, 0x0, + 0x3, 0x6, 0x38, 0x3, 0x80, + + /* U+9F9B "龛" */ + 0x1, 0x0, 0xe, 0x0, 0xc4, 0xf, 0xfe, 0xc0, + 0x4, 0xff, 0xc2, 0x1, 0x8, 0x4, 0x3f, 0xf0, + 0x23, 0xf, 0xff, 0xc4, 0x80, 0x12, 0x20, 0x4b, + 0x12, 0xf0, 0x70, 0x7e, + + /* U+9F9F "龟" */ + 0xc, 0x0, 0xf, 0xf0, 0x10, 0x20, 0x60, 0x40, + 0xff, 0xfc, 0x21, 0x4, 0x21, 0x4, 0x3f, 0xfc, + 0x21, 0x4, 0x21, 0x4, 0x3f, 0xfc, 0x21, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x0, 0xfe, + + /* U+9FA0 "龠" */ + 0x1, 0x0, 0xd, 0x0, 0x21, 0x83, 0xbf, 0xe8, + 0x0, 0xf, 0x77, 0x92, 0xa9, 0x25, 0x52, 0x7b, + 0xbc, 0x0, 0x1, 0xff, 0xf2, 0x22, 0x27, 0xff, + 0xc8, 0x88, 0x91, 0x11, 0x22, 0x2e, + + /* U+FF01 "!" */ + 0xff, 0x90, + + /* U+FF02 """ */ + 0x99, 0x99, + + /* U+FF03 "#" */ + 0x8, 0x2, 0x40, 0x90, 0x24, 0x7f, 0xc4, 0x81, + 0x20, 0x48, 0xff, 0x89, 0x2, 0x40, 0x90, 0x4, + 0x0, + + /* U+FF04 "$" */ + 0x8, 0xe, 0x1a, 0xd1, 0x28, 0x8c, 0x41, 0xa0, + 0x3c, 0x9, 0x84, 0x62, 0x31, 0x16, 0xb0, 0xe0, + 0x20, + + /* U+FF05 "%" */ + 0x70, 0x22, 0x21, 0x8, 0x88, 0x22, 0x20, 0x89, + 0x1, 0xc4, 0x0, 0x27, 0x80, 0xb3, 0x4, 0x84, + 0x22, 0x10, 0x88, 0x44, 0x33, 0x10, 0x78, + + /* U+FF06 "&" */ + 0x1e, 0x2, 0x20, 0x21, 0x2, 0x10, 0x22, 0x1, + 0xc0, 0x38, 0x44, 0x44, 0x82, 0x48, 0x18, 0x80, + 0xcc, 0x32, 0x7c, 0x10, + + /* U+FF07 "'" */ + 0x24, 0x0, + + /* U+FF08 "(" */ + 0x12, 0x44, 0x88, 0x88, 0x88, 0x84, 0x42, 0x10, + + /* U+FF09 ")" */ + 0x84, 0x22, 0x11, 0x11, 0x11, 0x12, 0x24, 0x80, + + /* U+FF0A "*" */ + 0x8, 0x4, 0x2, 0x19, 0x37, 0xf0, 0xc0, 0x50, + 0x44, 0x23, 0x0, + + /* U+FF0B "+" */ + 0x8, 0x4, 0x2, 0x1, 0xf, 0xf8, 0x40, 0x20, + 0x10, + + /* U+FF0C "," */ + 0xd6, + + /* U+FF0D "-" */ + 0xff, + + /* U+FF0E "." */ + 0xc0, + + /* U+FF0F "/" */ + 0x1, 0x2, 0x2, 0x4, 0x4, 0x8, 0x10, 0x10, + 0x20, 0x40, 0x40, 0x80, + + /* U+FF10 "0" */ + 0x3c, 0x46, 0x42, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x42, 0x62, 0x3c, + + /* U+FF11 "1" */ + 0x8, 0xcb, 0x90, 0x84, 0x21, 0x8, 0x42, 0x10, + + /* U+FF12 "2" */ + 0x3e, 0x42, 0x81, 0x1, 0x1, 0x2, 0x4, 0x8, + 0x10, 0x20, 0x40, 0x80, 0xff, + + /* U+FF13 "3" */ + 0x3e, 0x63, 0x41, 0x1, 0x1, 0x2, 0x1e, 0x1, + 0x1, 0x1, 0x81, 0x43, 0x3e, + + /* U+FF14 "4" */ + 0x2, 0x3, 0x2, 0x81, 0x41, 0x21, 0x11, 0x8, + 0x84, 0xff, 0x81, 0x0, 0x80, 0x40, + + /* U+FF15 "5" */ + 0x7e, 0x40, 0x80, 0x80, 0xbc, 0xc2, 0x81, 0x1, + 0x1, 0x81, 0x81, 0x42, 0x3c, + + /* U+FF16 "6" */ + 0x3c, 0x62, 0x41, 0x80, 0x9c, 0xe2, 0x81, 0x81, + 0x81, 0x81, 0x41, 0x42, 0x3c, + + /* U+FF17 "7" */ + 0xff, 0x2, 0x2, 0x4, 0x4, 0x8, 0x8, 0x10, + 0x10, 0x10, 0x20, 0x20, 0x20, + + /* U+FF18 "8" */ + 0x3c, 0x21, 0x10, 0x48, 0x24, 0x12, 0x10, 0xf0, + 0x86, 0x81, 0x40, 0xe0, 0x58, 0x63, 0xe0, + + /* U+FF19 "9" */ + 0x3c, 0x42, 0x82, 0x81, 0x81, 0x81, 0x43, 0x39, + 0x1, 0x1, 0x82, 0x46, 0x3c, + + /* U+FF1A ":" */ + 0xc0, 0x3, + + /* U+FF1B ";" */ + 0xc0, 0x3, 0x58, + + /* U+FF1C "<" */ + 0x1, 0x6, 0x8, 0x30, 0xc0, 0x80, 0x40, 0x30, + 0xc, 0x2, 0x1, + + /* U+FF1D "=" */ + 0xff, 0x0, 0x0, 0x0, 0xff, + + /* U+FF1E ">" */ + 0x80, 0x60, 0x10, 0xc, 0x3, 0x1, 0x2, 0xc, + 0x30, 0x40, 0x80, + + /* U+FF1F "?" */ + 0x3c, 0x42, 0x83, 0x81, 0x2, 0x2, 0xc, 0x10, + 0x10, 0x10, 0x0, 0x0, 0x10, + + /* U+FF20 "@" */ + 0x7, 0xf0, 0x1c, 0x1c, 0x30, 0x6, 0x60, 0x82, + 0x47, 0x51, 0xc4, 0x31, 0x88, 0x21, 0x88, 0x21, + 0x88, 0x21, 0x88, 0x21, 0x88, 0x62, 0xc7, 0xa4, + 0x40, 0x18, 0x20, 0x2, 0x18, 0x1c, 0xf, 0xe0 +}; + + +/*--------------------- + * 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 = 71, .box_w = 1, .box_h = 1, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 1, .adv_w = 78, .box_w = 1, .box_h = 13, .ofs_x = 2, .ofs_y = -1}, + {.bitmap_index = 3, .adv_w = 86, .box_w = 3, .box_h = 4, .ofs_x = 1, .ofs_y = 8}, + {.bitmap_index = 5, .adv_w = 155, .box_w = 10, .box_h = 13, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 22, .adv_w = 162, .box_w = 9, .box_h = 17, .ofs_x = 1, .ofs_y = -3}, + {.bitmap_index = 42, .adv_w = 249, .box_w = 13, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 65, .adv_w = 197, .box_w = 11, .box_h = 13, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 83, .adv_w = 46, .box_w = 1, .box_h = 4, .ofs_x = 1, .ofs_y = 8}, + {.bitmap_index = 84, .adv_w = 93, .box_w = 4, .box_h = 17, .ofs_x = 1, .ofs_y = -5}, + {.bitmap_index = 93, .adv_w = 93, .box_w = 4, .box_h = 17, .ofs_x = 1, .ofs_y = -5}, + {.bitmap_index = 102, .adv_w = 114, .box_w = 6, .box_h = 6, .ofs_x = 1, .ofs_y = 6}, + {.bitmap_index = 107, .adv_w = 168, .box_w = 9, .box_h = 9, .ofs_x = 1, .ofs_y = 1}, + {.bitmap_index = 118, .adv_w = 74, .box_w = 2, .box_h = 4, .ofs_x = 1, .ofs_y = -4}, + {.bitmap_index = 119, .adv_w = 158, .box_w = 7, .box_h = 1, .ofs_x = 1, .ofs_y = 5}, + {.bitmap_index = 120, .adv_w = 74, .box_w = 1, .box_h = 1, .ofs_x = 2, .ofs_y = -1}, + {.bitmap_index = 121, .adv_w = 75, .box_w = 5, .box_h = 13, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 130, .adv_w = 156, .box_w = 8, .box_h = 13, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 143, .adv_w = 124, .box_w = 5, .box_h = 13, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 152, .adv_w = 154, .box_w = 8, .box_h = 13, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 165, .adv_w = 155, .box_w = 8, .box_h = 13, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 178, .adv_w = 157, .box_w = 9, .box_h = 13, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 193, .adv_w = 156, .box_w = 8, .box_h = 13, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 206, .adv_w = 157, .box_w = 8, .box_h = 13, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 219, .adv_w = 156, .box_w = 8, .box_h = 13, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 232, .adv_w = 159, .box_w = 8, .box_h = 13, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 245, .adv_w = 157, .box_w = 8, .box_h = 13, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 258, .adv_w = 73, .box_w = 1, .box_h = 10, .ofs_x = 2, .ofs_y = -1}, + {.bitmap_index = 260, .adv_w = 74, .box_w = 2, .box_h = 12, .ofs_x = 2, .ofs_y = -4}, + {.bitmap_index = 263, .adv_w = 167, .box_w = 9, .box_h = 12, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 277, .adv_w = 164, .box_w = 8, .box_h = 5, .ofs_x = 1, .ofs_y = 3}, + {.bitmap_index = 282, .adv_w = 166, .box_w = 9, .box_h = 12, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 296, .adv_w = 155, .box_w = 8, .box_h = 13, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 309, .adv_w = 285, .box_w = 16, .box_h = 16, .ofs_x = 1, .ofs_y = -4}, + {.bitmap_index = 341, .adv_w = 188, .box_w = 12, .box_h = 13, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 361, .adv_w = 187, .box_w = 9, .box_h = 13, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 376, .adv_w = 203, .box_w = 11, .box_h = 13, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 394, .adv_w = 202, .box_w = 10, .box_h = 13, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 411, .adv_w = 187, .box_w = 9, .box_h = 13, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 426, .adv_w = 171, .box_w = 9, .box_h = 13, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 441, .adv_w = 218, .box_w = 11, .box_h = 13, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 459, .adv_w = 201, .box_w = 10, .box_h = 13, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 476, .adv_w = 65, .box_w = 1, .box_h = 13, .ofs_x = 2, .ofs_y = -1}, + {.bitmap_index = 478, .adv_w = 140, .box_w = 7, .box_h = 13, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 490, .adv_w = 186, .box_w = 10, .box_h = 13, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 507, .adv_w = 156, .box_w = 8, .box_h = 13, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 520, .adv_w = 235, .box_w = 12, .box_h = 13, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 540, .adv_w = 202, .box_w = 10, .box_h = 13, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 557, .adv_w = 219, .box_w = 12, .box_h = 13, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 577, .adv_w = 187, .box_w = 10, .box_h = 13, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 594, .adv_w = 218, .box_w = 12, .box_h = 16, .ofs_x = 1, .ofs_y = -4}, + {.bitmap_index = 618, .adv_w = 202, .box_w = 11, .box_h = 13, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 636, .adv_w = 186, .box_w = 11, .box_h = 13, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 654, .adv_w = 172, .box_w = 11, .box_h = 13, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 672, .adv_w = 204, .box_w = 10, .box_h = 13, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 689, .adv_w = 190, .box_w = 12, .box_h = 13, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 709, .adv_w = 265, .box_w = 16, .box_h = 13, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 735, .adv_w = 188, .box_w = 12, .box_h = 13, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 755, .adv_w = 187, .box_w = 11, .box_h = 13, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 773, .adv_w = 171, .box_w = 10, .box_h = 13, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 790, .adv_w = 77, .box_w = 4, .box_h = 16, .ofs_x = 1, .ofs_y = -4}, + {.bitmap_index = 798, .adv_w = 76, .box_w = 5, .box_h = 13, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 807, .adv_w = 77, .box_w = 4, .box_h = 16, .ofs_x = 0, .ofs_y = -4}, + {.bitmap_index = 815, .adv_w = 132, .box_w = 8, .box_h = 7, .ofs_x = 0, .ofs_y = 6}, + {.bitmap_index = 822, .adv_w = 166, .box_w = 10, .box_h = 1, .ofs_x = 0, .ofs_y = -3}, + {.bitmap_index = 824, .adv_w = 89, .box_w = 3, .box_h = 3, .ofs_x = 1, .ofs_y = 10}, + {.bitmap_index = 826, .adv_w = 156, .box_w = 8, .box_h = 10, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 836, .adv_w = 155, .box_w = 8, .box_h = 13, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 849, .adv_w = 141, .box_w = 8, .box_h = 10, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 859, .adv_w = 155, .box_w = 8, .box_h = 13, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 872, .adv_w = 156, .box_w = 8, .box_h = 10, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 882, .adv_w = 74, .box_w = 6, .box_h = 13, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 892, .adv_w = 156, .box_w = 8, .box_h = 13, .ofs_x = 1, .ofs_y = -4}, + {.bitmap_index = 905, .adv_w = 155, .box_w = 7, .box_h = 13, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 917, .adv_w = 51, .box_w = 1, .box_h = 13, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 919, .adv_w = 55, .box_w = 3, .box_h = 16, .ofs_x = -1, .ofs_y = -4}, + {.bitmap_index = 925, .adv_w = 140, .box_w = 8, .box_h = 13, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 938, .adv_w = 50, .box_w = 1, .box_h = 13, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 940, .adv_w = 232, .box_w = 13, .box_h = 10, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 957, .adv_w = 156, .box_w = 7, .box_h = 10, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 966, .adv_w = 156, .box_w = 8, .box_h = 10, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 976, .adv_w = 155, .box_w = 8, .box_h = 13, .ofs_x = 1, .ofs_y = -4}, + {.bitmap_index = 989, .adv_w = 155, .box_w = 8, .box_h = 13, .ofs_x = 1, .ofs_y = -4}, + {.bitmap_index = 1002, .adv_w = 93, .box_w = 5, .box_h = 10, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 1009, .adv_w = 145, .box_w = 8, .box_h = 10, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 1019, .adv_w = 77, .box_w = 4, .box_h = 13, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 1026, .adv_w = 155, .box_w = 8, .box_h = 10, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 1036, .adv_w = 140, .box_w = 9, .box_h = 10, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 1048, .adv_w = 202, .box_w = 13, .box_h = 10, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 1065, .adv_w = 140, .box_w = 9, .box_h = 10, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 1077, .adv_w = 140, .box_w = 9, .box_h = 13, .ofs_x = 0, .ofs_y = -4}, + {.bitmap_index = 1092, .adv_w = 140, .box_w = 8, .box_h = 10, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 1102, .adv_w = 93, .box_w = 5, .box_h = 16, .ofs_x = 0, .ofs_y = -4}, + {.bitmap_index = 1112, .adv_w = 64, .box_w = 1, .box_h = 16, .ofs_x = 2, .ofs_y = -4}, + {.bitmap_index = 1114, .adv_w = 93, .box_w = 5, .box_h = 16, .ofs_x = 1, .ofs_y = -4}, + {.bitmap_index = 1124, .adv_w = 164, .box_w = 9, .box_h = 2, .ofs_x = 1, .ofs_y = 5}, + {.bitmap_index = 1127, .adv_w = 256, .box_w = 1, .box_h = 1, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 1128, .adv_w = 256, .box_w = 4, .box_h = 4, .ofs_x = 2, .ofs_y = -1}, + {.bitmap_index = 1130, .adv_w = 256, .box_w = 4, .box_h = 4, .ofs_x = 3, .ofs_y = -1}, + {.bitmap_index = 1132, .adv_w = 256, .box_w = 6, .box_h = 6, .ofs_x = 5, .ofs_y = 3}, + {.bitmap_index = 1137, .adv_w = 256, .box_w = 10, .box_h = 12, .ofs_x = 3, .ofs_y = 0}, + {.bitmap_index = 1152, .adv_w = 256, .box_w = 4, .box_h = 15, .ofs_x = 7, .ofs_y = -2}, + {.bitmap_index = 1160, .adv_w = 256, .box_w = 5, .box_h = 15, .ofs_x = 4, .ofs_y = -2}, + {.bitmap_index = 1170, .adv_w = 256, .box_w = 7, .box_h = 15, .ofs_x = 6, .ofs_y = -2}, + {.bitmap_index = 1184, .adv_w = 256, .box_w = 7, .box_h = 15, .ofs_x = 3, .ofs_y = -2}, + {.bitmap_index = 1198, .adv_w = 256, .box_w = 4, .box_h = 15, .ofs_x = 7, .ofs_y = -2}, + {.bitmap_index = 1206, .adv_w = 256, .box_w = 4, .box_h = 16, .ofs_x = 5, .ofs_y = -2}, + {.bitmap_index = 1214, .adv_w = 256, .box_w = 5, .box_h = 15, .ofs_x = 7, .ofs_y = -2}, + {.bitmap_index = 1224, .adv_w = 256, .box_w = 5, .box_h = 15, .ofs_x = 4, .ofs_y = -2}, + {.bitmap_index = 1234, .adv_w = 256, .box_w = 3, .box_h = 15, .ofs_x = 8, .ofs_y = -2}, + {.bitmap_index = 1240, .adv_w = 256, .box_w = 3, .box_h = 15, .ofs_x = 5, .ofs_y = -2}, + {.bitmap_index = 1246, .adv_w = 256, .box_w = 9, .box_h = 8, .ofs_x = 3, .ofs_y = 2}, + {.bitmap_index = 1255, .adv_w = 256, .box_w = 4, .box_h = 15, .ofs_x = 7, .ofs_y = -2}, + {.bitmap_index = 1263, .adv_w = 256, .box_w = 4, .box_h = 15, .ofs_x = 5, .ofs_y = -2}, + {.bitmap_index = 1271, .adv_w = 256, .box_w = 5, .box_h = 16, .ofs_x = 7, .ofs_y = -2}, + {.bitmap_index = 1281, .adv_w = 256, .box_w = 4, .box_h = 16, .ofs_x = 5, .ofs_y = -2}, + {.bitmap_index = 1289, .adv_w = 256, .box_w = 11, .box_h = 11, .ofs_x = 3, .ofs_y = 0}, + {.bitmap_index = 1305, .adv_w = 256, .box_w = 13, .box_h = 15, .ofs_x = 2, .ofs_y = -2}, + {.bitmap_index = 1330, .adv_w = 256, .box_w = 11, .box_h = 9, .ofs_x = 3, .ofs_y = 1}, + {.bitmap_index = 1343, .adv_w = 256, .box_w = 14, .box_h = 10, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 1361, .adv_w = 256, .box_w = 7, .box_h = 12, .ofs_x = 4, .ofs_y = 0}, + {.bitmap_index = 1372, .adv_w = 256, .box_w = 9, .box_h = 14, .ofs_x = 3, .ofs_y = -2}, + {.bitmap_index = 1388, .adv_w = 256, .box_w = 10, .box_h = 11, .ofs_x = 3, .ofs_y = 0}, + {.bitmap_index = 1402, .adv_w = 256, .box_w = 12, .box_h = 13, .ofs_x = 2, .ofs_y = -1}, + {.bitmap_index = 1422, .adv_w = 256, .box_w = 10, .box_h = 12, .ofs_x = 3, .ofs_y = 0}, + {.bitmap_index = 1437, .adv_w = 256, .box_w = 12, .box_h = 13, .ofs_x = 2, .ofs_y = -1}, + {.bitmap_index = 1457, .adv_w = 256, .box_w = 15, .box_h = 12, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 1480, .adv_w = 256, .box_w = 16, .box_h = 13, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 1506, .adv_w = 256, .box_w = 10, .box_h = 14, .ofs_x = 3, .ofs_y = -2}, + {.bitmap_index = 1524, .adv_w = 256, .box_w = 13, .box_h = 16, .ofs_x = 2, .ofs_y = -2}, + {.bitmap_index = 1550, .adv_w = 256, .box_w = 7, .box_h = 15, .ofs_x = 5, .ofs_y = -2}, + {.bitmap_index = 1564, .adv_w = 256, .box_w = 10, .box_h = 15, .ofs_x = 3, .ofs_y = -2}, + {.bitmap_index = 1583, .adv_w = 256, .box_w = 13, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 1606, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 1633, .adv_w = 256, .box_w = 11, .box_h = 11, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 1649, .adv_w = 256, .box_w = 14, .box_h = 11, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 1669, .adv_w = 256, .box_w = 11, .box_h = 13, .ofs_x = 3, .ofs_y = -1}, + {.bitmap_index = 1687, .adv_w = 256, .box_w = 13, .box_h = 14, .ofs_x = 2, .ofs_y = -1}, + {.bitmap_index = 1710, .adv_w = 256, .box_w = 9, .box_h = 13, .ofs_x = 3, .ofs_y = -2}, + {.bitmap_index = 1725, .adv_w = 256, .box_w = 10, .box_h = 13, .ofs_x = 3, .ofs_y = -1}, + {.bitmap_index = 1742, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 2, .ofs_y = -2}, + {.bitmap_index = 1770, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 1798, .adv_w = 256, .box_w = 15, .box_h = 12, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 1821, .adv_w = 256, .box_w = 16, .box_h = 13, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 1847, .adv_w = 256, .box_w = 12, .box_h = 14, .ofs_x = 2, .ofs_y = -2}, + {.bitmap_index = 1868, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 1898, .adv_w = 256, .box_w = 13, .box_h = 12, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 1918, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 1945, .adv_w = 256, .box_w = 13, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 1968, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 1997, .adv_w = 256, .box_w = 12, .box_h = 8, .ofs_x = 2, .ofs_y = 2}, + {.bitmap_index = 2009, .adv_w = 256, .box_w = 15, .box_h = 10, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 2028, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 2056, .adv_w = 256, .box_w = 13, .box_h = 13, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 2078, .adv_w = 256, .box_w = 16, .box_h = 13, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 2104, .adv_w = 256, .box_w = 10, .box_h = 13, .ofs_x = 3, .ofs_y = -1}, + {.bitmap_index = 2121, .adv_w = 256, .box_w = 14, .box_h = 13, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 2144, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 2169, .adv_w = 256, .box_w = 12, .box_h = 12, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 2187, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 2214, .adv_w = 256, .box_w = 15, .box_h = 13, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 2239, .adv_w = 256, .box_w = 15, .box_h = 13, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 2264, .adv_w = 256, .box_w = 13, .box_h = 13, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 2286, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 2315, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 2345, .adv_w = 256, .box_w = 14, .box_h = 12, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 2366, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 2393, .adv_w = 256, .box_w = 16, .box_h = 13, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 2419, .adv_w = 256, .box_w = 16, .box_h = 12, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 2443, .adv_w = 256, .box_w = 17, .box_h = 12, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 2469, .adv_w = 256, .box_w = 16, .box_h = 12, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 2493, .adv_w = 256, .box_w = 14, .box_h = 8, .ofs_x = 1, .ofs_y = 2}, + {.bitmap_index = 2507, .adv_w = 256, .box_w = 14, .box_h = 11, .ofs_x = 1, .ofs_y = 1}, + {.bitmap_index = 2527, .adv_w = 256, .box_w = 15, .box_h = 11, .ofs_x = 1, .ofs_y = 1}, + {.bitmap_index = 2548, .adv_w = 256, .box_w = 13, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 2571, .adv_w = 256, .box_w = 16, .box_h = 13, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 2597, .adv_w = 256, .box_w = 16, .box_h = 13, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 2623, .adv_w = 256, .box_w = 11, .box_h = 14, .ofs_x = 2, .ofs_y = -2}, + {.bitmap_index = 2643, .adv_w = 256, .box_w = 15, .box_h = 13, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 2668, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 2693, .adv_w = 256, .box_w = 14, .box_h = 13, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 2716, .adv_w = 256, .box_w = 10, .box_h = 14, .ofs_x = 3, .ofs_y = -2}, + {.bitmap_index = 2734, .adv_w = 256, .box_w = 13, .box_h = 11, .ofs_x = 1, .ofs_y = 1}, + {.bitmap_index = 2752, .adv_w = 256, .box_w = 15, .box_h = 12, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 2775, .adv_w = 256, .box_w = 11, .box_h = 12, .ofs_x = 3, .ofs_y = 0}, + {.bitmap_index = 2792, .adv_w = 256, .box_w = 13, .box_h = 13, .ofs_x = 2, .ofs_y = -1}, + {.bitmap_index = 2814, .adv_w = 256, .box_w = 9, .box_h = 11, .ofs_x = 4, .ofs_y = 1}, + {.bitmap_index = 2827, .adv_w = 256, .box_w = 12, .box_h = 14, .ofs_x = 2, .ofs_y = -2}, + {.bitmap_index = 2848, .adv_w = 256, .box_w = 11, .box_h = 15, .ofs_x = 2, .ofs_y = -2}, + {.bitmap_index = 2869, .adv_w = 256, .box_w = 8, .box_h = 14, .ofs_x = 3, .ofs_y = -2}, + {.bitmap_index = 2883, .adv_w = 256, .box_w = 11, .box_h = 13, .ofs_x = 2, .ofs_y = -1}, + {.bitmap_index = 2901, .adv_w = 256, .box_w = 15, .box_h = 13, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 2926, .adv_w = 256, .box_w = 13, .box_h = 14, .ofs_x = 2, .ofs_y = -2}, + {.bitmap_index = 2949, .adv_w = 256, .box_w = 12, .box_h = 11, .ofs_x = 2, .ofs_y = 1}, + {.bitmap_index = 2966, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 2993, .adv_w = 256, .box_w = 13, .box_h = 13, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 3015, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 3040, .adv_w = 256, .box_w = 13, .box_h = 15, .ofs_x = 2, .ofs_y = -2}, + {.bitmap_index = 3065, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 3090, .adv_w = 256, .box_w = 11, .box_h = 10, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 3104, .adv_w = 256, .box_w = 14, .box_h = 12, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 3125, .adv_w = 256, .box_w = 9, .box_h = 12, .ofs_x = 4, .ofs_y = 0}, + {.bitmap_index = 3139, .adv_w = 256, .box_w = 11, .box_h = 14, .ofs_x = 2, .ofs_y = -2}, + {.bitmap_index = 3159, .adv_w = 256, .box_w = 10, .box_h = 11, .ofs_x = 3, .ofs_y = 0}, + {.bitmap_index = 3173, .adv_w = 256, .box_w = 13, .box_h = 14, .ofs_x = 2, .ofs_y = -2}, + {.bitmap_index = 3196, .adv_w = 256, .box_w = 11, .box_h = 7, .ofs_x = 3, .ofs_y = 2}, + {.bitmap_index = 3206, .adv_w = 256, .box_w = 14, .box_h = 9, .ofs_x = 1, .ofs_y = 1}, + {.bitmap_index = 3222, .adv_w = 256, .box_w = 10, .box_h = 11, .ofs_x = 3, .ofs_y = 1}, + {.bitmap_index = 3236, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 3261, .adv_w = 256, .box_w = 13, .box_h = 13, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 3283, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 3308, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 3333, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 3361, .adv_w = 256, .box_w = 12, .box_h = 13, .ofs_x = 2, .ofs_y = -1}, + {.bitmap_index = 3381, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 3408, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 3433, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 3460, .adv_w = 256, .box_w = 10, .box_h = 10, .ofs_x = 3, .ofs_y = 0}, + {.bitmap_index = 3473, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 3498, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 3527, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 3557, .adv_w = 256, .box_w = 14, .box_h = 13, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 3580, .adv_w = 256, .box_w = 14, .box_h = 13, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 3603, .adv_w = 256, .box_w = 14, .box_h = 12, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 3624, .adv_w = 256, .box_w = 16, .box_h = 13, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 3650, .adv_w = 256, .box_w = 15, .box_h = 12, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 3673, .adv_w = 256, .box_w = 15, .box_h = 13, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 3698, .adv_w = 256, .box_w = 12, .box_h = 12, .ofs_x = 2, .ofs_y = -1}, + {.bitmap_index = 3716, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 3743, .adv_w = 256, .box_w = 12, .box_h = 14, .ofs_x = 2, .ofs_y = -2}, + {.bitmap_index = 3764, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 3791, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 3818, .adv_w = 256, .box_w = 16, .box_h = 13, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 3844, .adv_w = 256, .box_w = 10, .box_h = 10, .ofs_x = 3, .ofs_y = 0}, + {.bitmap_index = 3857, .adv_w = 256, .box_w = 14, .box_h = 12, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 3878, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 3906, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 3931, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 3961, .adv_w = 256, .box_w = 6, .box_h = 14, .ofs_x = 5, .ofs_y = -2}, + {.bitmap_index = 3972, .adv_w = 256, .box_w = 9, .box_h = 14, .ofs_x = 4, .ofs_y = -2}, + {.bitmap_index = 3988, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 4015, .adv_w = 256, .box_w = 13, .box_h = 9, .ofs_x = 1, .ofs_y = 1}, + {.bitmap_index = 4030, .adv_w = 256, .box_w = 12, .box_h = 11, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 4047, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 4072, .adv_w = 256, .box_w = 10, .box_h = 12, .ofs_x = 3, .ofs_y = -1}, + {.bitmap_index = 4087, .adv_w = 256, .box_w = 16, .box_h = 8, .ofs_x = 0, .ofs_y = 2}, + {.bitmap_index = 4103, .adv_w = 256, .box_w = 16, .box_h = 11, .ofs_x = 0, .ofs_y = 2}, + {.bitmap_index = 4125, .adv_w = 256, .box_w = 16, .box_h = 12, .ofs_x = 0, .ofs_y = 2}, + {.bitmap_index = 4149, .adv_w = 256, .box_w = 10, .box_h = 12, .ofs_x = 3, .ofs_y = 0}, + {.bitmap_index = 4164, .adv_w = 256, .box_w = 12, .box_h = 12, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 4182, .adv_w = 256, .box_w = 12, .box_h = 12, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 4200, .adv_w = 256, .box_w = 12, .box_h = 11, .ofs_x = 2, .ofs_y = -1}, + {.bitmap_index = 4217, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 4242, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 4267, .adv_w = 256, .box_w = 16, .box_h = 8, .ofs_x = 0, .ofs_y = 1}, + {.bitmap_index = 4283, .adv_w = 256, .box_w = 14, .box_h = 11, .ofs_x = 1, .ofs_y = 1}, + {.bitmap_index = 4303, .adv_w = 256, .box_w = 16, .box_h = 10, .ofs_x = 0, .ofs_y = 2}, + {.bitmap_index = 4323, .adv_w = 256, .box_w = 15, .box_h = 13, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 4348, .adv_w = 256, .box_w = 15, .box_h = 13, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 4373, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 4398, .adv_w = 256, .box_w = 14, .box_h = 10, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 4416, .adv_w = 256, .box_w = 8, .box_h = 13, .ofs_x = 4, .ofs_y = -1}, + {.bitmap_index = 4429, .adv_w = 256, .box_w = 14, .box_h = 11, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 4449, .adv_w = 256, .box_w = 12, .box_h = 13, .ofs_x = 2, .ofs_y = -1}, + {.bitmap_index = 4469, .adv_w = 256, .box_w = 13, .box_h = 10, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 4486, .adv_w = 256, .box_w = 12, .box_h = 11, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 4503, .adv_w = 256, .box_w = 16, .box_h = 13, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 4529, .adv_w = 256, .box_w = 11, .box_h = 7, .ofs_x = 2, .ofs_y = 2}, + {.bitmap_index = 4539, .adv_w = 256, .box_w = 14, .box_h = 9, .ofs_x = 1, .ofs_y = 1}, + {.bitmap_index = 4555, .adv_w = 256, .box_w = 8, .box_h = 9, .ofs_x = 4, .ofs_y = 1}, + {.bitmap_index = 4564, .adv_w = 256, .box_w = 10, .box_h = 10, .ofs_x = 3, .ofs_y = 0}, + {.bitmap_index = 4577, .adv_w = 256, .box_w = 12, .box_h = 14, .ofs_x = 2, .ofs_y = -2}, + {.bitmap_index = 4598, .adv_w = 256, .box_w = 8, .box_h = 15, .ofs_x = 4, .ofs_y = -2}, + {.bitmap_index = 4613, .adv_w = 256, .box_w = 16, .box_h = 11, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 4635, .adv_w = 256, .box_w = 12, .box_h = 11, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 4652, .adv_w = 256, .box_w = 10, .box_h = 9, .ofs_x = 3, .ofs_y = 1}, + {.bitmap_index = 4664, .adv_w = 256, .box_w = 9, .box_h = 10, .ofs_x = 3, .ofs_y = 0}, + {.bitmap_index = 4676, .adv_w = 256, .box_w = 12, .box_h = 12, .ofs_x = 2, .ofs_y = -1}, + {.bitmap_index = 4694, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 4721, .adv_w = 256, .box_w = 14, .box_h = 10, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 4739, .adv_w = 256, .box_w = 11, .box_h = 11, .ofs_x = 3, .ofs_y = -1}, + {.bitmap_index = 4755, .adv_w = 256, .box_w = 12, .box_h = 11, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 4772, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 4801, .adv_w = 256, .box_w = 10, .box_h = 10, .ofs_x = 3, .ofs_y = 1}, + {.bitmap_index = 4814, .adv_w = 256, .box_w = 12, .box_h = 11, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 4831, .adv_w = 256, .box_w = 15, .box_h = 1, .ofs_x = 0, .ofs_y = 6}, + {.bitmap_index = 4833, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 4860, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 4887, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 4914, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 4943, .adv_w = 256, .box_w = 15, .box_h = 13, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 4968, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 4995, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 5022, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 5049, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 5074, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 5104, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 5129, .adv_w = 256, .box_w = 15, .box_h = 13, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 5154, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 5183, .adv_w = 256, .box_w = 15, .box_h = 13, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 5208, .adv_w = 256, .box_w = 15, .box_h = 13, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 5233, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 5262, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 5289, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 5316, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 5343, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 5370, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 5399, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 5426, .adv_w = 256, .box_w = 15, .box_h = 13, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 5451, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 5478, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 5505, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 5532, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 5561, .adv_w = 256, .box_w = 1, .box_h = 14, .ofs_x = 7, .ofs_y = -2}, + {.bitmap_index = 5563, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 5592, .adv_w = 256, .box_w = 13, .box_h = 14, .ofs_x = 2, .ofs_y = -2}, + {.bitmap_index = 5615, .adv_w = 256, .box_w = 7, .box_h = 15, .ofs_x = 4, .ofs_y = -2}, + {.bitmap_index = 5629, .adv_w = 256, .box_w = 13, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 5654, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 5683, .adv_w = 256, .box_w = 13, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 5708, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 5737, .adv_w = 256, .box_w = 5, .box_h = 5, .ofs_x = 5, .ofs_y = 3}, + {.bitmap_index = 5741, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 5770, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 5797, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 5824, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 5851, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 5878, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 5907, .adv_w = 256, .box_w = 6, .box_h = 15, .ofs_x = 4, .ofs_y = -2}, + {.bitmap_index = 5919, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 5944, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 5974, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 6003, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 6032, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 6061, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 6089, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 6117, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 6146, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 6173, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 6203, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 6232, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 6261, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 6290, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 6317, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 6344, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 6371, .adv_w = 256, .box_w = 14, .box_h = 13, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 6394, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 6421, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 6450, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 6477, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 6506, .adv_w = 256, .box_w = 13, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 6529, .adv_w = 256, .box_w = 13, .box_h = 16, .ofs_x = 1, .ofs_y = -3}, + {.bitmap_index = 6555, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 6582, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 6611, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 6638, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 6667, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 6697, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 6727, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 6752, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 6777, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 6807, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 6836, .adv_w = 256, .box_w = 15, .box_h = 11, .ofs_x = 0, .ofs_y = 0}, + {.bitmap_index = 6857, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 6884, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 6911, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 6938, .adv_w = 256, .box_w = 15, .box_h = 13, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 6963, .adv_w = 256, .box_w = 15, .box_h = 13, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 6988, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 7015, .adv_w = 256, .box_w = 15, .box_h = 13, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 7040, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 7069, .adv_w = 256, .box_w = 15, .box_h = 13, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 7094, .adv_w = 256, .box_w = 15, .box_h = 13, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 7119, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 7144, .adv_w = 256, .box_w = 15, .box_h = 13, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 7169, .adv_w = 256, .box_w = 15, .box_h = 5, .ofs_x = 0, .ofs_y = 8}, + {.bitmap_index = 7179, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 7206, .adv_w = 256, .box_w = 14, .box_h = 17, .ofs_x = 1, .ofs_y = -3}, + {.bitmap_index = 7236, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 7265, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 7294, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 7324, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 7353, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 7382, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 7411, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 7440, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 7469, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 7497, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 7525, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 7554, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 7583, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 7612, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 7641, .adv_w = 256, .box_w = 7, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 7655, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 7684, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 7714, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 7744, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 7771, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 7800, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 7828, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 7857, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 7887, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 7916, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 7945, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 7975, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 8002, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 8029, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 8059, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 8088, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 8118, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 8147, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 8177, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 8206, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 8235, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 8262, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 8289, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 8319, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 8346, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 8375, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 8404, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 8433, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 8462, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 8491, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 8520, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 8549, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 8578, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 8605, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 8635, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 8664, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 8694, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 8723, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 8752, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 8779, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 8808, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 8837, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 8867, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 8894, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 8924, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 8953, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 8982, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 9011, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 9040, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 9069, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 9098, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 9127, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 9157, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 9186, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 9218, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 9247, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 9276, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 9303, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 9332, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 9361, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 9390, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 9420, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 9449, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 9478, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 9505, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 9535, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 9563, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 9593, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 9622, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 9652, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 9681, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 9710, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 9737, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 9767, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 9796, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 9823, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 9852, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 9881, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 9910, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 9940, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 9969, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 9998, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 10027, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 10056, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 10085, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 10114, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 10143, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 10172, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 10199, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 10229, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 10256, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 10285, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 10315, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 10345, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 10374, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 10403, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 10428, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 10457, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 10487, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 10516, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 10545, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 10574, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 10603, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 10632, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 10661, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 10690, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 10720, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 10750, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 10779, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 10809, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 10838, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 10868, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 10898, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 10927, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 10957, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 10986, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 11015, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 11044, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 11073, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 11102, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 11132, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 11161, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 11190, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 11219, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 11249, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 11278, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 11307, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 11336, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 11365, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 11395, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 11425, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 11454, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 11483, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 11512, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 11541, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 11570, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 11599, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 11629, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 11656, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 11685, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 11714, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 11744, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 11774, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 11803, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 11832, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 11861, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 11890, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 11922, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 11951, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 11980, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 12010, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 12040, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 12069, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 12098, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 12127, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 12156, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 12183, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 12213, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 12243, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 12273, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 12302, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 12331, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 12361, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 12390, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 12419, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 12446, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 12473, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 12502, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 12532, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 12562, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 12592, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 12621, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 12650, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 12679, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 12709, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 12738, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 12767, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 12796, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 12825, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 12854, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 12883, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 12915, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 12944, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 12973, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 13002, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 13032, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 13062, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 13092, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 13121, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 13151, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 13181, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 13210, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 13240, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 13272, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 13304, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 13334, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 13363, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 13392, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 13421, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 13451, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 13480, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 13510, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 13539, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 13568, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 13597, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 13627, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 13657, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 13687, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 13716, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 13746, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 13776, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 13808, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 13840, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 13870, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 13899, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 13928, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 13958, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 13990, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 14019, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 14048, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 14077, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 14107, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 14136, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 14165, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 14194, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 14224, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 14254, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 14283, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 14315, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 14344, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 14371, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 14398, .adv_w = 256, .box_w = 16, .box_h = 17, .ofs_x = 0, .ofs_y = -3}, + {.bitmap_index = 14432, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -3}, + {.bitmap_index = 14462, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -3}, + {.bitmap_index = 14492, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 14521, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 14551, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 14580, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 14610, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 14640, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 14670, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -3}, + {.bitmap_index = 14702, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 14732, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 14759, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 14789, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 14818, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 14848, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 14877, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 14905, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 14933, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 14961, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 14991, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 15018, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 15047, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 15074, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 15103, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 15130, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 15159, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 15188, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 15217, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -3}, + {.bitmap_index = 15247, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 15276, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 15306, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 15335, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 15364, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 15393, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 15422, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 15452, .adv_w = 256, .box_w = 12, .box_h = 14, .ofs_x = 2, .ofs_y = -2}, + {.bitmap_index = 15473, .adv_w = 256, .box_w = 13, .box_h = 15, .ofs_x = 2, .ofs_y = -2}, + {.bitmap_index = 15498, .adv_w = 256, .box_w = 13, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 15521, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 15550, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 15577, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 15604, .adv_w = 256, .box_w = 13, .box_h = 14, .ofs_x = 2, .ofs_y = -2}, + {.bitmap_index = 15627, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 15656, .adv_w = 256, .box_w = 14, .box_h = 4, .ofs_x = 1, .ofs_y = 8}, + {.bitmap_index = 15663, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 15691, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 15718, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 15745, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 15774, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 15802, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 15829, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 15857, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 15884, .adv_w = 256, .box_w = 4, .box_h = 13, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 15891, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 15923, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 15950, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 15979, .adv_w = 256, .box_w = 15, .box_h = 13, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 16004, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 16031, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 16060, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 16087, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 16116, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 16143, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 16172, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -3}, + {.bitmap_index = 16202, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 16231, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 16259, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 16286, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 16315, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 16344, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 16373, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 16398, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 16427, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 16457, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 16487, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 16517, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 16547, .adv_w = 256, .box_w = 15, .box_h = 13, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 16572, .adv_w = 256, .box_w = 15, .box_h = 13, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 16597, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 16624, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 16654, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 16683, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 16712, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 16739, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 16767, .adv_w = 256, .box_w = 13, .box_h = 14, .ofs_x = 2, .ofs_y = -2}, + {.bitmap_index = 16790, .adv_w = 256, .box_w = 13, .box_h = 14, .ofs_x = 2, .ofs_y = -2}, + {.bitmap_index = 16813, .adv_w = 256, .box_w = 13, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 16836, .adv_w = 256, .box_w = 13, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 16859, .adv_w = 256, .box_w = 12, .box_h = 15, .ofs_x = 2, .ofs_y = -2}, + {.bitmap_index = 16882, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 16911, .adv_w = 256, .box_w = 13, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 16936, .adv_w = 256, .box_w = 13, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 16959, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 16988, .adv_w = 256, .box_w = 13, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 17011, .adv_w = 256, .box_w = 13, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 17034, .adv_w = 256, .box_w = 6, .box_h = 15, .ofs_x = 9, .ofs_y = -2}, + {.bitmap_index = 17046, .adv_w = 256, .box_w = 13, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 17069, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 17098, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 17125, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 17154, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 17181, .adv_w = 256, .box_w = 13, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 17207, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 17236, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 17263, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 17292, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 17321, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 17350, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 17377, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 17404, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 17431, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 17460, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 17487, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 17516, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 17543, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 17570, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 17597, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 17624, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 17649, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 17676, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 17703, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 17732, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 17759, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 17786, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 17815, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 17842, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 17871, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 17898, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 17927, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 17956, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 17981, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 18010, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 18037, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 18064, .adv_w = 256, .box_w = 13, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 18089, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 18116, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 18145, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 18172, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 18199, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 18226, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 18253, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 18282, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 18310, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 18339, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 18366, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 18393, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 18422, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 18451, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 18478, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 18505, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 18532, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 18559, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 18588, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 18617, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 18647, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 18674, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 18701, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 18729, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 18756, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 18785, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 18812, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 18839, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 18869, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 18896, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 18926, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 18953, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 18980, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 19009, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 19036, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 19065, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 19094, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 19121, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 19150, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 19179, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 19207, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 19236, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 19263, .adv_w = 256, .box_w = 13, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 19288, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 19317, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 19346, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 19373, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 19400, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 19427, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 19455, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 19482, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 19509, .adv_w = 256, .box_w = 17, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 19543, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 19570, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 19597, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 19624, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 19651, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 19678, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 19707, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 19734, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 19763, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 19790, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 19819, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 19849, .adv_w = 256, .box_w = 12, .box_h = 14, .ofs_x = 3, .ofs_y = -1}, + {.bitmap_index = 19870, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 19898, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 19925, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 19953, .adv_w = 256, .box_w = 13, .box_h = 13, .ofs_x = 2, .ofs_y = -1}, + {.bitmap_index = 19975, .adv_w = 256, .box_w = 14, .box_h = 13, .ofs_x = 2, .ofs_y = -1}, + {.bitmap_index = 19998, .adv_w = 256, .box_w = 14, .box_h = 13, .ofs_x = 2, .ofs_y = -1}, + {.bitmap_index = 20021, .adv_w = 256, .box_w = 14, .box_h = 13, .ofs_x = 2, .ofs_y = -1}, + {.bitmap_index = 20044, .adv_w = 256, .box_w = 14, .box_h = 13, .ofs_x = 2, .ofs_y = -1}, + {.bitmap_index = 20067, .adv_w = 256, .box_w = 14, .box_h = 13, .ofs_x = 2, .ofs_y = -1}, + {.bitmap_index = 20090, .adv_w = 256, .box_w = 14, .box_h = 13, .ofs_x = 2, .ofs_y = -1}, + {.bitmap_index = 20113, .adv_w = 256, .box_w = 14, .box_h = 13, .ofs_x = 2, .ofs_y = -1}, + {.bitmap_index = 20136, .adv_w = 256, .box_w = 14, .box_h = 13, .ofs_x = 2, .ofs_y = -1}, + {.bitmap_index = 20159, .adv_w = 256, .box_w = 13, .box_h = 13, .ofs_x = 2, .ofs_y = -1}, + {.bitmap_index = 20181, .adv_w = 256, .box_w = 14, .box_h = 13, .ofs_x = 2, .ofs_y = -1}, + {.bitmap_index = 20204, .adv_w = 256, .box_w = 14, .box_h = 13, .ofs_x = 2, .ofs_y = -1}, + {.bitmap_index = 20227, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 20252, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 20281, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 20310, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 20337, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 20366, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 20395, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 20424, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 20453, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 20482, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 20511, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 20541, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 20570, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 20600, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 20629, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 20658, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 20688, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 20718, .adv_w = 256, .box_w = 8, .box_h = 15, .ofs_x = 6, .ofs_y = -2}, + {.bitmap_index = 20733, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 20762, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 20789, .adv_w = 256, .box_w = 13, .box_h = 15, .ofs_x = 2, .ofs_y = -2}, + {.bitmap_index = 20814, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 20843, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 20873, .adv_w = 256, .box_w = 13, .box_h = 15, .ofs_x = 2, .ofs_y = -2}, + {.bitmap_index = 20898, .adv_w = 256, .box_w = 13, .box_h = 16, .ofs_x = 2, .ofs_y = -2}, + {.bitmap_index = 20924, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 20953, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 20978, .adv_w = 256, .box_w = 7, .box_h = 14, .ofs_x = 7, .ofs_y = -2}, + {.bitmap_index = 20991, .adv_w = 256, .box_w = 15, .box_h = 13, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 21016, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 21045, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 21072, .adv_w = 256, .box_w = 13, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 21097, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 21126, .adv_w = 256, .box_w = 13, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 21149, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 21176, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 21203, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 21232, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 21259, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 21288, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 21317, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 21344, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 21371, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 21398, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -3}, + {.bitmap_index = 21427, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 21454, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 21482, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 21509, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 21536, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 21563, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 21591, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 21618, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 21646, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 21674, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 21701, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 21728, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 21755, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 21783, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 21810, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 21837, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 21864, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 21889, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 21918, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 21947, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 21972, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 21999, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 22024, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 22051, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 22078, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 22108, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 22135, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 22164, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 22194, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 22223, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 22252, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 22279, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 22308, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 22337, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 22366, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 22395, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 22424, .adv_w = 256, .box_w = 12, .box_h = 13, .ofs_x = 2, .ofs_y = -1}, + {.bitmap_index = 22444, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 22474, .adv_w = 256, .box_w = 13, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 22499, .adv_w = 256, .box_w = 13, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 22524, .adv_w = 256, .box_w = 13, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 22547, .adv_w = 256, .box_w = 13, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 22570, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 22597, .adv_w = 256, .box_w = 13, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 22622, .adv_w = 256, .box_w = 13, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 22645, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 22672, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 22697, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 22724, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 22753, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 22782, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 22811, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 22841, .adv_w = 256, .box_w = 14, .box_h = 13, .ofs_x = 2, .ofs_y = -1}, + {.bitmap_index = 22864, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 22891, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 22918, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 22943, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 22970, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 22997, .adv_w = 256, .box_w = 13, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 23020, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 23045, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 23072, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 23099, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 23127, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 23154, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 23183, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 23212, .adv_w = 256, .box_w = 13, .box_h = 15, .ofs_x = 2, .ofs_y = -2}, + {.bitmap_index = 23237, .adv_w = 256, .box_w = 13, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 23260, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 23287, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 23316, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 23344, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 23369, .adv_w = 256, .box_w = 13, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 23394, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 23421, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 23446, .adv_w = 256, .box_w = 13, .box_h = 14, .ofs_x = 2, .ofs_y = -2}, + {.bitmap_index = 23469, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 23494, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 23521, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 23550, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 23580, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 23607, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 23634, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 23663, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 23692, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 23721, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 23749, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 23776, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 23803, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 23833, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 23862, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 23887, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 23915, .adv_w = 256, .box_w = 15, .box_h = 17, .ofs_x = 1, .ofs_y = -3}, + {.bitmap_index = 23947, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 23974, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 24001, .adv_w = 256, .box_w = 13, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 24026, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 24055, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 24085, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 24114, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 24142, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 24170, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 24197, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 24224, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 24253, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 24282, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 24309, .adv_w = 256, .box_w = 15, .box_h = 13, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 24334, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 24363, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 24390, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 24417, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 24442, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 24469, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 24498, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 24525, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 24552, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 24581, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 24610, .adv_w = 256, .box_w = 13, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 24633, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 24661, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 24688, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 24717, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 24744, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 24771, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 24801, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 24830, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 24859, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 24886, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 24913, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 24942, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 24969, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 24994, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 25021, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 25048, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 25078, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 25103, .adv_w = 256, .box_w = 14, .box_h = 13, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 25126, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 25153, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 25180, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 25208, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 25235, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 25265, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 25294, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 25323, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 25351, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 25378, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 25405, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 25432, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 25461, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 25491, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 25520, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 25547, .adv_w = 256, .box_w = 14, .box_h = 17, .ofs_x = 1, .ofs_y = -3}, + {.bitmap_index = 25577, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 25605, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 25634, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 25661, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 25691, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 25720, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 25749, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 25777, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 25805, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 25834, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 25862, .adv_w = 256, .box_w = 13, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 25887, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 25916, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 25945, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 25977, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 26004, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 26031, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 26058, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 26087, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 26114, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 26143, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 26172, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 26200, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 26227, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 26256, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 26285, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 26312, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 26339, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 26366, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 26393, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 26418, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 26443, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 26470, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 26500, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 26529, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 26556, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 26585, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 26615, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 26644, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 26673, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 26702, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 26732, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 26759, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 26786, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 26813, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 26840, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 26867, .adv_w = 256, .box_w = 15, .box_h = 13, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 26892, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 26921, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 26948, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 26975, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 27002, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 27031, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 27058, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 27086, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 27113, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 27143, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 27172, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 27202, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 27232, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 27261, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 27290, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 27322, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 27349, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 27374, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 27402, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 27429, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 27459, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 27486, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 27514, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 27539, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 27566, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 27593, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 27622, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 27651, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 27678, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 27705, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 27735, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 27765, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 27792, .adv_w = 256, .box_w = 15, .box_h = 13, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 27817, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 27846, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 27871, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 27898, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 27925, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 27954, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 27983, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 28012, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 28041, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 28068, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 28098, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 28127, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 28154, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 28184, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 28214, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 28243, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 28270, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 28297, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 28326, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 28355, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 28384, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 28413, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 28440, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 28469, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 28498, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 28528, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 28557, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 28586, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 28616, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 28641, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 28670, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 28698, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 28727, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 28754, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 28781, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 28811, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 28841, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 28868, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 28895, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 28924, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 28951, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 28980, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 29009, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 29038, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 29065, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 29092, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 29119, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -3}, + {.bitmap_index = 29149, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 29174, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 29203, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 29231, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 29256, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 29285, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 29314, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 29343, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 29370, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 29398, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 29425, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 29450, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 29477, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 29504, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 29531, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 29560, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 29587, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 29616, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 29645, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 29670, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 29695, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 29725, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 29750, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 29777, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 29807, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 29836, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 29866, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 29893, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 29922, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 29950, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 29977, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 30006, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 30033, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 30060, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 30090, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 30119, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 30146, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 30175, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 30205, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 30235, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 30264, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 30291, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 30318, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 30347, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 30374, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 30403, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 30430, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 30459, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 30487, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 30512, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 30541, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 30568, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 30597, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 30624, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 30651, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 30680, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 30709, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 30739, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 30768, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 30797, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 30824, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 30851, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 30878, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 30905, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 30933, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 30962, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 30992, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 31021, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 31050, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 31079, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 31106, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 31133, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 31162, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 31190, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 31219, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 31248, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 31275, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 31304, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 31331, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 31358, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 31387, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 31415, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 31442, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 31471, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 31498, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 31527, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 31557, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 31584, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 31611, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 31638, .adv_w = 256, .box_w = 13, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 31661, .adv_w = 256, .box_w = 13, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 31684, .adv_w = 256, .box_w = 13, .box_h = 14, .ofs_x = 2, .ofs_y = -2}, + {.bitmap_index = 31707, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 31732, .adv_w = 256, .box_w = 13, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 31755, .adv_w = 256, .box_w = 13, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 31780, .adv_w = 256, .box_w = 13, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 31803, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 31828, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 31853, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 31880, .adv_w = 256, .box_w = 13, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 31903, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 31928, .adv_w = 256, .box_w = 13, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 31951, .adv_w = 256, .box_w = 13, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 31976, .adv_w = 256, .box_w = 13, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 31999, .adv_w = 256, .box_w = 13, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 32024, .adv_w = 256, .box_w = 13, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 32047, .adv_w = 256, .box_w = 13, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 32070, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 32095, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 32122, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 32147, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 32174, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 32201, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 32228, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 32255, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 32282, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 32309, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 32336, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 32363, .adv_w = 256, .box_w = 15, .box_h = 13, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 32388, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 32417, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 32446, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 32475, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 32504, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 32531, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 32558, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 32585, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 32614, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 32639, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 32668, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 32695, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 32724, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 32753, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 32780, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 32809, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 32836, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 32865, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 32892, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 32922, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 32951, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 32980, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 33007, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 33037, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 33066, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 33093, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 33122, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 33149, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 33178, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 33206, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 33235, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 33262, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 33291, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 33318, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 33345, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 33372, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 33402, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 33431, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 33460, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 33489, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 33516, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 33543, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 33572, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 33601, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 33630, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 33659, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 33686, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 33713, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 33740, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 33770, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 33799, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 33826, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 33853, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 33880, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 33909, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 33939, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 33969, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 33996, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 34025, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 34052, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 34079, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 34106, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 34135, .adv_w = 256, .box_w = 15, .box_h = 13, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 34160, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 34187, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 34212, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 34241, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 34270, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 34300, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 34329, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 34358, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 34387, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 34415, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 34444, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 34473, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 34503, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 34530, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 34557, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 34587, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 34617, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 34647, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 34677, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 34706, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 34736, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 34766, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 34795, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 34824, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 34851, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 34881, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 34910, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 34937, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 34967, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 34996, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 35023, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 35052, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 35079, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 35107, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 35137, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 35166, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 35195, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 35222, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 35251, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 35280, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 35307, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 35337, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 35364, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 35391, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 35420, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 35450, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 35478, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 35507, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 35534, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 35563, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 35593, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 35620, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 35647, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 35677, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 35706, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 35733, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 35762, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 35791, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 35821, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 35848, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 35877, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 35906, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 35933, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 35962, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 35992, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 36021, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 36050, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 36079, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 36109, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 36138, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 36167, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 36196, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 36223, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 36252, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 36281, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 36308, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 36335, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 36364, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 36393, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -3}, + {.bitmap_index = 36423, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 36450, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 36477, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 36504, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -3}, + {.bitmap_index = 36536, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 36566, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -3}, + {.bitmap_index = 36596, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 36625, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 36655, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 36682, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 36712, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 36740, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 36768, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 36797, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 36826, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 36853, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 36883, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 36912, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 36939, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 36968, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 36997, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 37026, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 37055, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 37082, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 37111, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 37140, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 37169, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 37197, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 37226, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 37255, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 37285, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 37310, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 37338, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 37368, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 37397, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 37426, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 37458, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 37488, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 37517, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 37547, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 37574, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 37603, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 37630, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 37659, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 37689, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 37718, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 37743, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 37773, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 37803, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 37830, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 37859, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 37888, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 37917, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 37946, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -3}, + {.bitmap_index = 37976, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 38005, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 38032, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 0, .ofs_y = -3}, + {.bitmap_index = 38060, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 38087, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 38116, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 38146, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 38173, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 38200, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 38229, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 38258, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 38285, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 38314, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 38341, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 38370, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 38395, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 38424, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 38453, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 38482, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 38509, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 38538, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -3}, + {.bitmap_index = 38568, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -3}, + {.bitmap_index = 38598, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 38625, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 38654, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 38683, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 38712, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 38741, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 38771, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 38800, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 38829, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 38858, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 38887, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 38916, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 38945, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 38974, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 39001, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 39030, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 39060, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 39090, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -3}, + {.bitmap_index = 39122, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 39151, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 39181, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 39210, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 39240, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 39269, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 39298, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 39327, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 39356, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 39383, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 39415, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 39444, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 39473, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 39502, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 39532, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 39562, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 39591, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 39621, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 39651, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 39680, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 39710, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 39740, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 39769, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 39798, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 39828, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 39857, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 39887, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 39917, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 39949, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 39979, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 40008, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 40035, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 40065, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 40094, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 40121, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 40151, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 40181, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 40213, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 40242, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 40272, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 40301, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 40330, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 40355, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 40385, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 40414, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 40446, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 40476, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 40506, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 40536, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 40565, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 40594, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 40622, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 40651, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 40681, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 40713, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 40742, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 40772, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 40802, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 40831, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 40860, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 40890, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 40919, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 40949, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 40979, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 41011, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 41043, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 41073, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 41103, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 41133, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 41162, .adv_w = 256, .box_w = 17, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 41194, .adv_w = 256, .box_w = 16, .box_h = 17, .ofs_x = 0, .ofs_y = -3}, + {.bitmap_index = 41228, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 41258, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 41288, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 41318, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 41346, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 41375, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 41404, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 41433, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 41462, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 41489, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 41514, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 41539, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 41569, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 41596, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 41626, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 41658, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 41687, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 41714, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 41743, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 41773, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 41802, .adv_w = 256, .box_w = 15, .box_h = 13, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 41827, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 41856, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 41885, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 41914, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 41943, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 41972, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 42002, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 42031, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 42058, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 42087, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 42117, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 42146, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 42175, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 42202, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 42231, .adv_w = 256, .box_w = 14, .box_h = 7, .ofs_x = 1, .ofs_y = 6}, + {.bitmap_index = 42244, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 42274, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 42301, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -3}, + {.bitmap_index = 42333, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 42360, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 42389, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 42418, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 42445, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 42474, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 42503, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 42532, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 42562, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 42591, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 42620, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 42647, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 42674, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 42703, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 42732, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 42759, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 42786, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 42815, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 42844, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 42871, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 42901, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 42928, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 42955, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 42985, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 43010, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 43039, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 43066, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 43095, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 43123, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 43152, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 43180, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 43207, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 43235, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 43265, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 43295, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 43324, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 43351, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 43380, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 43410, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 43440, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 43472, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 43504, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 43532, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 43562, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 43590, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 43618, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 43645, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 43675, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 43705, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 43734, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 43761, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 43793, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 43823, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 43853, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 43881, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 43910, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 43940, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 43969, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 43996, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 44023, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 44053, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 44082, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 44111, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 44140, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 44167, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 44197, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 44226, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 44255, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 44284, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 44311, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 44340, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 44367, .adv_w = 256, .box_w = 13, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 44393, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 44420, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 44450, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 44477, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 44504, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 44534, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 44564, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 44593, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 44623, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 44652, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 44677, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 44706, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 44733, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 44760, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 44787, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 44814, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 44841, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 44871, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 44898, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 44928, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 44955, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 44984, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 45011, .adv_w = 256, .box_w = 16, .box_h = 13, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 45037, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 45062, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 45090, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 45119, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 45146, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 45173, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 45200, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 45230, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 45258, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 45287, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 45316, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 45346, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 45374, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 45404, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 45434, .adv_w = 256, .box_w = 11, .box_h = 15, .ofs_x = 2, .ofs_y = -2}, + {.bitmap_index = 45455, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 45484, .adv_w = 256, .box_w = 13, .box_h = 15, .ofs_x = 2, .ofs_y = -2}, + {.bitmap_index = 45509, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 45538, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 45565, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 45592, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 45619, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 2, .ofs_y = -2}, + {.bitmap_index = 45646, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 45675, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 45705, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 45734, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 45761, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 45791, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 45821, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 45848, .adv_w = 256, .box_w = 12, .box_h = 16, .ofs_x = 2, .ofs_y = -2}, + {.bitmap_index = 45872, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 45901, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 45926, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 45956, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 45984, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 2, .ofs_y = -2}, + {.bitmap_index = 46011, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 46041, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 46068, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 46097, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 46124, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 46151, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 46180, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 46210, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 46239, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 46268, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 46297, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 46327, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 46357, .adv_w = 256, .box_w = 13, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 46383, .adv_w = 256, .box_w = 13, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 46409, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 46438, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 46465, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 46492, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 46521, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 46550, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 46579, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 46608, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 46637, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 46666, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 46695, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 46722, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 46751, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 46780, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 46809, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 46838, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 46863, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 46893, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 46920, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 46949, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 46978, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 47007, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 47036, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 47065, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 47094, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 47123, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 47153, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 47182, .adv_w = 256, .box_w = 12, .box_h = 15, .ofs_x = 2, .ofs_y = -2}, + {.bitmap_index = 47205, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 47237, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 47266, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 47293, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 47322, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 47347, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 47372, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 47404, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 47433, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 47462, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 47491, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 47518, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 47547, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 47576, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 47603, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 47632, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 47662, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 47691, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 47718, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -3}, + {.bitmap_index = 47750, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 47779, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 47808, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 47833, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 47858, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 47883, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 47912, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 47939, .adv_w = 256, .box_w = 15, .box_h = 13, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 47964, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 47994, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 48021, .adv_w = 256, .box_w = 14, .box_h = 13, .ofs_x = 2, .ofs_y = -1}, + {.bitmap_index = 48044, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 48072, .adv_w = 256, .box_w = 15, .box_h = 13, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 48097, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 48124, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 48154, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 2, .ofs_y = -2}, + {.bitmap_index = 48179, .adv_w = 256, .box_w = 14, .box_h = 13, .ofs_x = 2, .ofs_y = -1}, + {.bitmap_index = 48202, .adv_w = 256, .box_w = 14, .box_h = 13, .ofs_x = 2, .ofs_y = -1}, + {.bitmap_index = 48225, .adv_w = 256, .box_w = 13, .box_h = 13, .ofs_x = 2, .ofs_y = -1}, + {.bitmap_index = 48247, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 48277, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 48304, .adv_w = 256, .box_w = 13, .box_h = 15, .ofs_x = 2, .ofs_y = -2}, + {.bitmap_index = 48329, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 48354, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 48383, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 48412, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 48439, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 48468, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 48495, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 48524, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 48551, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 48578, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 48607, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 48636, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 48663, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 48690, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 48720, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 48749, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 48778, .adv_w = 256, .box_w = 13, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 48804, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 48833, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 48863, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 48892, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 48919, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 48951, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 48978, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 49005, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 49034, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 49062, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 49091, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 49118, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 49145, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 49174, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 49203, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 49232, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 49261, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 49290, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 49317, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 49346, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 49375, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 49404, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 49433, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 49460, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 49487, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 49516, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 49545, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 49574, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 49601, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 49628, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 49655, .adv_w = 256, .box_w = 13, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 49680, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 49709, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 49739, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 49769, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 49799, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 49829, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 49859, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 49889, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 49918, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 49948, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -3}, + {.bitmap_index = 49978, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 50008, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 50037, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 50069, .adv_w = 256, .box_w = 16, .box_h = 17, .ofs_x = 0, .ofs_y = -3}, + {.bitmap_index = 50103, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 50133, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 50162, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 50191, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 50221, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 50250, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 50280, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 50309, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 50339, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 50371, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 50400, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 50430, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 50460, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 50492, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 50524, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 50556, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 50585, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 50617, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 50646, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 50678, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 50708, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 50738, .adv_w = 256, .box_w = 15, .box_h = 17, .ofs_x = 1, .ofs_y = -3}, + {.bitmap_index = 50770, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 50800, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 50829, .adv_w = 256, .box_w = 16, .box_h = 17, .ofs_x = 0, .ofs_y = -3}, + {.bitmap_index = 50863, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 50895, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 50925, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 50952, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 50981, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 51008, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 51038, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 51065, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 51092, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 51119, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 51149, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 51178, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 51207, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 51234, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 51263, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 51292, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 51321, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 51350, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 51379, .adv_w = 256, .box_w = 13, .box_h = 14, .ofs_x = 2, .ofs_y = -2}, + {.bitmap_index = 51402, .adv_w = 256, .box_w = 13, .box_h = 15, .ofs_x = 2, .ofs_y = -2}, + {.bitmap_index = 51427, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 51454, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 51483, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 51510, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 51539, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 51566, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 51595, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 51622, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 51651, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 51681, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 51708, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 51735, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 51765, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 51790, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 51820, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 51848, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 51875, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 51905, .adv_w = 256, .box_w = 13, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 51928, .adv_w = 256, .box_w = 13, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 51953, .adv_w = 256, .box_w = 13, .box_h = 16, .ofs_x = 2, .ofs_y = -2}, + {.bitmap_index = 51979, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 52006, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 52035, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 52065, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 52094, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 52123, .adv_w = 256, .box_w = 13, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 52148, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 52177, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 52206, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 52236, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 52264, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -3}, + {.bitmap_index = 52294, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 52323, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 52352, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 52382, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 52411, .adv_w = 256, .box_w = 8, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 52426, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 52458, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 52487, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 52514, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 52546, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 52576, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 52605, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 52635, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 52664, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 52694, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 52721, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 52750, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 52779, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 52808, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 52837, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 52866, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 52895, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 52924, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 52953, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 52983, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 53013, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 53040, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 53070, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 53099, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 53129, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 53158, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 53187, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 53217, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 53247, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 53277, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 53309, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 53339, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 53368, .adv_w = 256, .box_w = 7, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 53382, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 53409, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 53438, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 53465, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 53493, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 53520, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 53549, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 53576, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 53604, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 53633, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 53662, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 53691, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 53720, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 53749, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 53776, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 53806, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 53833, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 53862, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 53891, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 53920, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 53949, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 53978, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 54007, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 54037, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 54067, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 54097, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 54126, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 54158, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 54188, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 54217, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 54247, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 54277, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 54306, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 54335, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 54364, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 54394, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 54424, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 54451, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 54481, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 54513, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 54542, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 54569, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 54596, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 54623, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 54652, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 54682, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 54711, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 54740, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 54768, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 54798, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 54827, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 54857, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 54886, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 54915, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 54945, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 54974, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 55003, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 55032, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 55061, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 55090, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 55120, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 55150, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 55180, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 55209, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 55238, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 55267, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 55296, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 55325, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 55352, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 55381, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 55410, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 55437, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 55464, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 55493, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 55522, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 55552, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 55581, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 55606, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 55635, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 55663, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 55692, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 55721, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 55750, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 55780, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 55810, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 55839, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 55866, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 55893, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 55922, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 55951, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 55980, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 56009, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 56038, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 56067, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 56096, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 56125, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 56152, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 56179, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 56208, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 56237, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 56266, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 56295, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 56324, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 56353, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 56382, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 56411, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 56440, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 56469, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 56498, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 56527, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 56557, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 56589, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 56618, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 56647, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 56676, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 56705, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 56732, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 56761, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 56790, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 56819, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 56848, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 56877, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 56904, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 56933, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 56962, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 56989, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 57018, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 57047, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 57076, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 57105, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 57132, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 57162, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 57189, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 57218, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 57247, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 57276, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 57303, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 57332, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 57361, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 57390, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 57419, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 57448, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 57477, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 57506, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 57535, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 57564, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 57594, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 57623, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 57652, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 57681, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 57711, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 57743, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 57772, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 57802, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 57831, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 57861, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 57890, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 57919, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 57949, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 57978, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 58007, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 58036, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 58065, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 58094, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 58123, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 58153, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 58182, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 58211, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 58240, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 58270, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 58299, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 58328, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 58357, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 58386, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 58415, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 58444, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 58473, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 58502, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 58529, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 58559, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 58588, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 58618, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 58647, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 58676, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 58705, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 58735, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 58764, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 58794, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 58823, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 58852, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 58882, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 58911, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 58941, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 58971, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 59001, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 59030, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 59059, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 59089, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 59118, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 59147, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 59176, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 59208, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 59235, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 59265, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 59295, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 59324, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 59353, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 59383, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 59413, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 59442, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 59472, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 59501, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 59531, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 59561, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 59593, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 59622, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 59651, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 59680, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 59709, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 59738, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 59770, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 59799, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 59828, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 59858, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 59890, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 59919, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 59946, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 59976, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 60006, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 60036, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 60065, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 60095, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 60125, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 60155, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 60187, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 60217, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 60244, .adv_w = 256, .box_w = 7, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 60258, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 60287, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 60316, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 60343, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 60373, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 60402, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 60429, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 60458, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 60487, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 60514, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 60543, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 60573, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 60602, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 60631, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 60658, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 60685, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 60715, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 60744, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 60773, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 60802, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 60831, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 60860, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 60889, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 60919, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 60948, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 60976, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 61005, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 61035, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 61064, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 61094, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 61121, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 61150, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 61179, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 61209, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 61238, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 61267, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 61296, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 61325, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 61354, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 61384, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 61413, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 61442, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 61471, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 61498, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 61527, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 61557, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 61587, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 61616, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 61645, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 61674, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 61703, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 61730, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 61757, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 61784, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 61814, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 61839, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 61868, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 61897, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 61926, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 61956, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 61985, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 62014, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 62043, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 62073, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 62100, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 62129, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 62156, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 62183, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 62212, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 62241, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 62271, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 62298, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 62327, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 62354, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 62383, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 62410, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 62439, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 62468, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 62498, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 62527, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 62554, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 62583, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 62612, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 62644, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 62674, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 62703, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 62732, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 62762, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 62791, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 62821, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 62851, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 62880, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 62909, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 62941, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 62971, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 63000, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 63030, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 63060, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 63092, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 63121, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 63150, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 63179, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 63208, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 63238, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 63267, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 63296, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 63325, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 63355, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 63384, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 63414, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 63443, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 63472, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 63499, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 63529, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 63558, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 63588, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 63618, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 63648, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 63677, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 63706, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 63736, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 63765, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 63795, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 63822, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 63851, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 63881, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 63908, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 63937, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 63964, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 63993, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 64022, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 64049, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 64078, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 64107, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 64136, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 64165, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 64192, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 64221, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 64250, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 64279, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 64308, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 64337, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 64366, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 64395, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 64424, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 64454, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 64483, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 64512, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 64542, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 64571, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 64600, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 64629, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 64658, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 64687, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 64716, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 64745, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 64774, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 64803, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 64832, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 64861, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 64888, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 64917, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 64947, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 64976, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 65005, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 65034, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 65063, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 65090, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 65119, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 65149, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 65179, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 65209, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 65236, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 65265, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 65292, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 65317, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 65344, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 65371, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 65400, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 65429, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 65458, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 65487, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 65516, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 65545, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 65574, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 65604, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 65632, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 65661, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 65690, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 65719, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 65748, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 65777, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 65806, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 65835, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 65864, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 65893, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 65922, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 65951, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 65980, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 66009, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 66038, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 66067, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 66096, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 66123, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 66153, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 66182, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 66209, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 66238, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 66267, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 66296, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 66325, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 66352, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 66381, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 66410, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 66439, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 66468, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 66497, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 66524, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 66553, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 66582, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 66609, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 66638, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 66667, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 66696, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 66725, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 66752, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 66782, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 66809, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 66838, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 66867, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 66896, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 66923, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 66952, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 66981, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 67010, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 67039, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 67068, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 67097, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 67126, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 67155, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 67184, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 67213, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 67243, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 67272, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 67299, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 67328, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 67358, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 67387, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 67417, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 67446, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 67476, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 67505, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 67535, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 67564, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 67593, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 67623, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 67652, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 67681, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 67710, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 67739, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 67768, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 67798, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 67828, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 67857, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 67886, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 67915, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 67945, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 67974, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 68003, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 68032, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 68061, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 68090, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 68119, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 68148, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 68178, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 68208, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 68237, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 68264, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 68294, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 68323, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 68350, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 68379, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 68408, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 68437, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 68466, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 68495, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 68525, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 68555, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 68582, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 68609, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 68639, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 68666, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 68696, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -3}, + {.bitmap_index = 68726, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 68755, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 68785, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 68815, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 68845, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 68874, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 68906, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 68935, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 68965, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 68995, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 69024, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 69053, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 69078, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 69108, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 69137, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 69167, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 69196, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 69226, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 69256, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 69285, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 69314, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 69343, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 69372, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 69401, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 69430, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 69459, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 69491, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 69520, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 69549, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 69576, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 69605, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 69634, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 69663, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 69693, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 69723, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 69752, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 69781, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 69811, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 69841, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 69871, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 69898, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 69927, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 69957, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 69986, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 70016, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 70046, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 70076, .adv_w = 256, .box_w = 17, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 70110, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 70140, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 70169, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 70199, .adv_w = 256, .box_w = 15, .box_h = 17, .ofs_x = 0, .ofs_y = -3}, + {.bitmap_index = 70231, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 70263, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 70293, .adv_w = 256, .box_w = 16, .box_h = 17, .ofs_x = 0, .ofs_y = -3}, + {.bitmap_index = 70327, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 70357, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 70386, .adv_w = 256, .box_w = 15, .box_h = 17, .ofs_x = 1, .ofs_y = -3}, + {.bitmap_index = 70418, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -3}, + {.bitmap_index = 70447, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 70472, .adv_w = 256, .box_w = 11, .box_h = 14, .ofs_x = 3, .ofs_y = -2}, + {.bitmap_index = 70492, .adv_w = 256, .box_w = 15, .box_h = 13, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 70517, .adv_w = 256, .box_w = 13, .box_h = 14, .ofs_x = 2, .ofs_y = -2}, + {.bitmap_index = 70540, .adv_w = 256, .box_w = 13, .box_h = 15, .ofs_x = 2, .ofs_y = -2}, + {.bitmap_index = 70565, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 70592, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 70619, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 70649, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 70678, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 70705, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 70730, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 70759, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 70788, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 70816, .adv_w = 256, .box_w = 15, .box_h = 13, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 70841, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 70868, .adv_w = 256, .box_w = 13, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 70893, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 70922, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 2, .ofs_y = -2}, + {.bitmap_index = 70949, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 70976, .adv_w = 256, .box_w = 13, .box_h = 15, .ofs_x = 2, .ofs_y = -2}, + {.bitmap_index = 71001, .adv_w = 256, .box_w = 13, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 71026, .adv_w = 256, .box_w = 13, .box_h = 15, .ofs_x = 2, .ofs_y = -2}, + {.bitmap_index = 71051, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 71078, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 71107, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 71134, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 71163, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 71192, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 71219, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 71246, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 71274, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 71301, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 71329, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 71354, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 71383, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 71410, .adv_w = 256, .box_w = 13, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 71435, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 71462, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 71491, .adv_w = 256, .box_w = 15, .box_h = 13, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 71516, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 71543, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 71572, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 71601, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 71628, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 71655, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 71682, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 71709, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 71739, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 71769, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 71798, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 71825, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 71853, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 71883, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 71913, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 71940, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 71965, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 71995, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 72025, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 72054, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 72083, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 72110, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 72137, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 72162, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 72191, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 72221, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 72248, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 72277, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 72304, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 72331, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 72361, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 72390, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 72417, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 72446, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 72473, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 72500, .adv_w = 256, .box_w = 15, .box_h = 13, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 72525, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 72552, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 72579, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 72609, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 72638, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 72667, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 72696, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 72723, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 72752, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 72781, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 72808, .adv_w = 256, .box_w = 12, .box_h = 14, .ofs_x = 2, .ofs_y = -2}, + {.bitmap_index = 72829, .adv_w = 256, .box_w = 13, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 72854, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 72883, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -3}, + {.bitmap_index = 72912, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 72939, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 72968, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 72997, .adv_w = 256, .box_w = 13, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 73022, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 73051, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 73080, .adv_w = 256, .box_w = 13, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 73105, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 73134, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 73161, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 73190, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 73218, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 73247, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 73275, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 73304, .adv_w = 256, .box_w = 13, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 73329, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 73358, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 73385, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 73412, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 73442, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 73470, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 73499, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 73528, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 73558, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 73587, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 73616, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 73643, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 73672, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 73699, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 73728, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 73757, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 73784, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 73813, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 73842, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 73871, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 73900, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 73929, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 73958, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 73987, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 74017, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 74046, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 74075, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 74102, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 74131, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 74160, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 74189, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 74218, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 74247, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 74277, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 74306, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 74333, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 74360, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 74387, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 74417, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 74446, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 74473, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 74501, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 74531, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 74560, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 74589, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 74616, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 74645, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 74674, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 74703, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 74730, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 74759, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 74788, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 74817, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 74846, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 74875, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 74904, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 74933, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 74962, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 74989, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 75018, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 75047, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 75076, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 75101, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 75128, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 75157, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 75187, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 75216, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 75245, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 75275, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 75304, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 75334, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 75363, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 75393, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 75422, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 75449, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 75476, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 75505, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 75534, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 75564, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 75591, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 75620, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 75649, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 75679, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 75708, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 75737, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 75764, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 75791, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 75818, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 75847, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 75876, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 75905, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 75934, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 75964, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 75993, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 76022, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 76051, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 76080, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 76107, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 76136, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 76163, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 76192, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 76221, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 76250, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 76279, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 76308, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 76338, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 76367, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 76396, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 76425, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 76455, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 76484, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 76513, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 76542, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 76572, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 76601, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 76630, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 76657, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 76686, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 76715, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 76742, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 76771, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 76801, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 76830, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 76859, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 76888, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 76917, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 76946, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 76975, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 77002, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 77031, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 77060, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 77089, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 77118, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 77147, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 77176, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 77205, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 77235, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 77265, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 77294, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 77323, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 77352, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 77381, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 77411, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 77440, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 77469, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 77496, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 77525, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 77554, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 77584, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 77613, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 77642, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 77671, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 77700, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 77730, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 77759, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 77788, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 77817, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 77847, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 77874, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 77901, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 77930, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 77960, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 77990, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 78019, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 78048, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 78075, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 78104, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 78131, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 78161, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 78190, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 78219, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 78248, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 78277, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 78307, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 78336, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 78366, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 78395, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 78424, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 78453, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 78482, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 78511, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 78540, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 78570, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 78599, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 78626, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 78655, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 78684, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 78714, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 78744, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 78773, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 78802, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 78831, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 78860, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 78889, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 78918, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 78947, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 78976, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 79005, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 79034, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 79063, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 79093, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 79123, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 79153, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 79182, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 79211, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 79240, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 79269, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 79299, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 79328, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 79357, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 79387, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 79417, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 79446, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -3}, + {.bitmap_index = 79476, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 79506, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 79536, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 79565, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 79594, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 79623, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 79652, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 79681, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 79711, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 79740, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 79769, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 79798, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 79827, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 79856, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 79885, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 79914, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 79943, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 79972, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 79999, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 80029, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 80056, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 80085, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 80115, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 80145, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 80172, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 80202, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 80232, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 80261, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 80290, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 80319, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 80348, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 80378, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 80407, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 80436, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 80465, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 80495, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 80525, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 80554, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 80583, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 80613, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 80643, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 80673, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 80702, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 80731, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 80760, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 80789, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 80818, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 80847, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 80876, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 80906, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 80935, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 80964, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 80993, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 81022, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 81052, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 81082, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 81111, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 81141, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 81171, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 81200, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 81229, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 81257, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 81286, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 81315, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 81344, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 81374, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 81403, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 81432, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 81461, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 81490, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 81519, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 81548, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 81577, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 81604, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 81631, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 81663, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 81692, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 81719, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 81749, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 81778, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 81807, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 81836, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 81866, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 81895, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 81924, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 81953, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 81983, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 82012, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 82041, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 82071, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 82101, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 82130, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 82159, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 82191, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 82220, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 82252, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 82282, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 82309, .adv_w = 256, .box_w = 15, .box_h = 13, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 82334, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 82363, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 82390, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 82420, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 82449, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 82476, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 82501, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 82528, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 82555, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 82582, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 82609, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 82638, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 82667, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 82696, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 82726, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 82753, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 82782, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 82811, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 82840, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 82869, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 82898, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 82925, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 82955, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 82982, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 83012, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 83041, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 83068, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 83097, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 83124, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 83151, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 83178, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 83207, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 83236, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 83265, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 83292, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 83319, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 83348, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 83377, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 83406, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 2, .ofs_y = -2}, + {.bitmap_index = 83433, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 83462, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 83492, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 83521, .adv_w = 256, .box_w = 16, .box_h = 17, .ofs_x = 0, .ofs_y = -3}, + {.bitmap_index = 83555, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 83584, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 83613, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 83642, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 83671, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 83700, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 83730, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 83760, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 83792, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 83821, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 83851, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 83881, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 83911, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 83940, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 83969, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 83998, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 2, .ofs_y = -2}, + {.bitmap_index = 84025, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 84054, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 84084, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 84114, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 84144, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 84174, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 84204, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 84234, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 84264, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 84293, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 84322, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 84352, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 84382, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 84412, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 84442, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 84471, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 84500, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 84529, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 84559, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 84588, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 84617, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 84647, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 84677, .adv_w = 256, .box_w = 4, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 84685, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 84715, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 84744, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 84773, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 84802, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 84831, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 84860, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 84887, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 84916, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 84945, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 84974, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 85003, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 85030, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 85059, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 85089, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 85116, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 85145, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 85172, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 85201, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 85230, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 85259, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 85286, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 85313, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 85340, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 85369, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 85396, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 85425, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 85454, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 85483, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 85512, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 85544, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 85574, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 85603, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 85632, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 85661, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -3}, + {.bitmap_index = 85693, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 85723, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 85755, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 85784, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 85813, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 85840, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 85869, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 85898, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 85927, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 85954, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 85983, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 86010, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 86038, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 86067, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 86096, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 86125, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 86154, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 86183, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 86210, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 86237, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 86266, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 86295, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 86324, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 86353, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 86382, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 86410, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 86437, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 86467, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 86496, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 86523, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 86553, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 86583, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 86610, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 86639, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 86666, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 86698, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 86725, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 86754, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 86781, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 86808, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 86837, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 86866, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 86895, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 86920, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 86950, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 86979, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 87008, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 87038, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 87067, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 87096, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 87123, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 87152, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 87179, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 87208, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 87237, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 87265, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 87295, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 87324, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 87351, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 87376, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 87406, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 87435, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 87464, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 87493, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 87522, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -3}, + {.bitmap_index = 87552, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 87581, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 87610, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 87640, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 87667, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 87696, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 87725, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 87754, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 87783, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 87810, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -3}, + {.bitmap_index = 87840, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 87869, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 87899, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 87931, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 87958, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 87987, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 88017, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 88046, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 88075, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 88104, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 88131, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 88158, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 88187, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 88214, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 88243, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 88272, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 88301, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 88333, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 88362, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 88392, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 88421, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 88450, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 88480, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 88509, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 88539, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 88568, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 88597, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 88627, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 88656, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 88685, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 88714, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 88743, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 88773, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 88800, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 88830, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 88862, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 88891, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 88920, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 88950, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 88979, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 89006, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 89035, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 89065, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 89095, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 89124, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 89151, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 89181, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 89208, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 89240, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 89269, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 89296, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 89323, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 89352, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 89379, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 89409, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 89438, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 89467, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 89496, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 89525, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 89555, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 89584, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 89613, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 89642, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 89669, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 89696, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 89728, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 89758, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 89785, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 89814, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 89844, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 89871, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 89900, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 89929, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 89958, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 89987, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 90016, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 90045, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 90072, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 90102, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 90131, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 90161, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 90188, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 90217, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 90247, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 90277, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 90307, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 90337, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 90367, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 90397, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 90426, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 90456, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 90486, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 90515, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 90544, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 90574, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 90603, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 90632, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 90661, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 90690, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 90719, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 90749, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 90778, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 90808, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 90837, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 90867, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 90896, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 90923, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 90953, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 90982, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 91012, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 91042, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 91072, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 91102, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 91131, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 91160, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 91189, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 91218, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 91248, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 91278, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 91305, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 91332, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 91362, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 91391, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 91421, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 91451, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 91480, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 91510, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 91537, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 91566, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 91595, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 91624, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 91651, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 91680, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 91709, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 91738, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 91768, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 91797, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 91826, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 91853, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 91885, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 91917, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 91944, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 91974, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 92001, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 92030, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 92060, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 92089, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 92118, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 92150, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 92180, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 92209, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 92239, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 92266, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 92295, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 92325, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 92354, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 92383, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 92410, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 92440, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 92470, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 92500, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 92529, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 92558, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 92583, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 92612, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 92641, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 92670, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 92699, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 92728, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 92757, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 92787, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 92816, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 92845, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 92874, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 92906, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 92936, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 92965, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 92994, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 93024, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 93054, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 93084, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 93113, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 93143, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 93173, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 93202, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 93232, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 93261, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 93290, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 93319, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 93348, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 93377, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 93407, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 93436, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 93466, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 93495, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 93524, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 93554, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 93584, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 93613, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 93645, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 93675, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 93704, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 93733, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 93763, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 93793, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 93822, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 93851, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 93880, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 93910, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 93940, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 93969, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 93998, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 94028, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 94058, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 94087, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 94115, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 94144, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 94171, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 94200, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 94230, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 94260, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 94290, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 94319, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 94346, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 94375, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 94404, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 94433, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 94462, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 94492, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 94521, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 94550, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 94577, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 94606, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 94635, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 94664, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 94694, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 94724, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 94754, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 94783, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 94813, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 94843, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 94873, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 94902, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 94932, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 94961, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 94990, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 95020, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 95052, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 95081, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 95110, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 95139, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 95168, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 95196, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 95223, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 95252, .adv_w = 256, .box_w = 14, .box_h = 4, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 95259, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 95286, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 95315, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 95344, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 95371, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 95399, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 95429, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 95459, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 95488, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 95515, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 95544, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 95573, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 95602, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 95632, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 95661, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 95689, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 95718, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 95748, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 95777, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 95807, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 95834, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 95864, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 95894, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 95922, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 95951, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 95980, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 96007, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 96036, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 96065, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 96095, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 96127, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 96156, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 96186, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 96213, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 96242, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 96272, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 96301, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 96333, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 96363, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 96393, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 96422, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 96451, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 96481, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 96510, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 96537, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 96566, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 96593, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 96622, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 96651, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 96680, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 96709, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 96739, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 96768, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 96798, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 96827, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 96854, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 96883, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 96910, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 96939, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 96968, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 96997, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 97027, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 97056, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 97086, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 97115, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 97145, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 97174, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 97203, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 97230, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 97259, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 97288, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 97318, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 97347, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 97376, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 97405, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 97435, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 97463, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 97492, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 97522, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 97549, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 97579, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 97608, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 97637, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 97666, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 97696, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 97725, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 97755, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 97785, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 97814, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 97841, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 97871, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 97898, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 97925, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 97954, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 97983, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 98013, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 98042, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 98071, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 98100, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 98129, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 98158, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 98188, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 98217, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 98247, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 98276, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 98306, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 98335, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 98362, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 98390, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 98420, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 98449, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 98478, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 98508, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 98537, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 98566, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 98595, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 98620, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 98649, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 98678, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 98707, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 98736, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 98763, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 98790, .adv_w = 256, .box_w = 12, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 98813, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 98841, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 98871, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 98900, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 98929, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 98958, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 98988, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 99013, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 99042, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 99072, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 99102, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 99131, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 99160, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 99190, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 99219, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 99248, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 99277, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 99306, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 99336, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 99366, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 99395, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 99425, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 99454, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 99483, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 99511, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 99540, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 99570, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 99599, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 99628, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 99658, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 99687, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 99716, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 99745, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 99772, .adv_w = 256, .box_w = 9, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 99789, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 99819, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 99851, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 99880, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 99910, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 99940, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 99969, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 99997, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 100027, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 100056, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 100085, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 100115, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -3}, + {.bitmap_index = 100147, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 100176, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 100205, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 100234, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 100261, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 100288, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 100315, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 100345, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 100375, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 100405, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 100434, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 100463, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 100493, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 100523, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 100553, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 100583, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 100615, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 100644, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 100673, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 100700, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 100729, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 100754, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 100781, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 100811, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 100841, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 100871, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 100900, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 100930, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 100959, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 100988, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 101015, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 101045, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 101074, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 101104, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 101131, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 101160, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 101190, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 101220, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 101247, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 101277, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -3}, + {.bitmap_index = 101307, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 101336, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 101366, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 101395, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 101424, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 101453, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 101482, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 101512, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 101541, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 101566, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 101593, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 101620, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 101649, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 101679, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 101709, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 101739, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 101769, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 101799, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 101831, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 101861, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 101891, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 101920, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 101949, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 101979, .adv_w = 256, .box_w = 15, .box_h = 13, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 102004, .adv_w = 256, .box_w = 15, .box_h = 13, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 102029, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 102057, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 102086, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 102116, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 102141, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 102170, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 102199, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 102226, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 102255, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 102284, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 102313, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 102342, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 102371, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 102400, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 102429, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 102456, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 102485, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 102514, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 102541, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 102568, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 102597, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 102626, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 102656, .adv_w = 256, .box_w = 15, .box_h = 13, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 102681, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 102710, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 102740, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 102769, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 102799, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 102829, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 102858, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 102888, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 102917, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 102946, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 102975, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 103004, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 103034, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 103062, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 103091, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 103120, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 103149, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 103178, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 103205, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 103233, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 103261, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 103291, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 103323, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 103350, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 103380, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 103409, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 103439, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 103469, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 103498, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 103526, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 103551, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 103578, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 103607, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 103632, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 103661, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 103691, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 103720, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 103747, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 103776, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 103806, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 103835, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 103863, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 103893, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 103923, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 103953, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 103982, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 104009, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 104038, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 104068, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 104098, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 104128, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 104158, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 104187, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 104219, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 104249, .adv_w = 256, .box_w = 15, .box_h = 17, .ofs_x = 1, .ofs_y = -3}, + {.bitmap_index = 104281, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 104310, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 104339, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 104368, .adv_w = 256, .box_w = 14, .box_h = 13, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 104391, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 104420, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 104449, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 104479, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 104509, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 104538, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 104567, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 104597, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 104624, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 104653, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 104680, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 104710, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 104739, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 104768, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 104797, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 104824, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 104853, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 104882, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 104911, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 104941, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 104970, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 105002, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 105032, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 105061, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 105088, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 105118, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 105145, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 105173, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 105198, .adv_w = 256, .box_w = 13, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 105221, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 105248, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 105276, .adv_w = 256, .box_w = 13, .box_h = 14, .ofs_x = 2, .ofs_y = -2}, + {.bitmap_index = 105299, .adv_w = 256, .box_w = 13, .box_h = 15, .ofs_x = 2, .ofs_y = -2}, + {.bitmap_index = 105324, .adv_w = 256, .box_w = 13, .box_h = 14, .ofs_x = 2, .ofs_y = -2}, + {.bitmap_index = 105347, .adv_w = 256, .box_w = 13, .box_h = 15, .ofs_x = 2, .ofs_y = -2}, + {.bitmap_index = 105372, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 105401, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 105426, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 105453, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 105478, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 105505, .adv_w = 256, .box_w = 13, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 105530, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 105559, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 105586, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 105615, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 105644, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 105674, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 105703, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 105730, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 105757, .adv_w = 256, .box_w = 13, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 105782, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 105809, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 105837, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 105867, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 105895, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 105920, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 105945, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 105974, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 106003, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 106033, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 106062, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 106092, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 106121, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 106150, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 106178, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 106207, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 106237, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 106266, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 106295, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 106325, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 106355, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 106385, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 106415, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 106444, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 106473, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 106503, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 106532, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -3}, + {.bitmap_index = 106564, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -3}, + {.bitmap_index = 106594, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 106624, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 106656, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 106688, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 106720, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 106752, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 106781, .adv_w = 256, .box_w = 16, .box_h = 17, .ofs_x = 0, .ofs_y = -3}, + {.bitmap_index = 106815, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 106847, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 106876, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 106906, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -3}, + {.bitmap_index = 106936, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 106966, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 106995, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 107025, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 107055, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 107085, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 107114, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 107144, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 107174, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 107204, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 107234, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 107266, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 107296, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 107326, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 107356, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 107385, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 107414, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 107444, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 107474, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 107506, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 107538, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 107570, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 107599, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 107629, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 107659, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 107691, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 107721, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 107751, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 107780, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 107809, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 107839, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 107869, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 107901, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 107931, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 107961, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 107991, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 108020, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 108052, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 108082, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 108114, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 108146, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 108175, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 108204, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 108234, .adv_w = 256, .box_w = 15, .box_h = 17, .ofs_x = 1, .ofs_y = -3}, + {.bitmap_index = 108266, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 108295, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 108325, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 108354, .adv_w = 256, .box_w = 16, .box_h = 17, .ofs_x = 0, .ofs_y = -3}, + {.bitmap_index = 108388, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 108418, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 108448, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 108478, .adv_w = 256, .box_w = 15, .box_h = 17, .ofs_x = 1, .ofs_y = -3}, + {.bitmap_index = 108510, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 108539, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 108568, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 108598, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 108627, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 108657, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 108687, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 108716, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 108746, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 108776, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 108806, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 108838, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 108868, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 108897, .adv_w = 256, .box_w = 15, .box_h = 17, .ofs_x = 1, .ofs_y = -3}, + {.bitmap_index = 108929, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 108959, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 108989, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 109018, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 109048, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 109076, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 109106, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 109136, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 109166, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 109195, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 109225, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 109254, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 109283, .adv_w = 256, .box_w = 13, .box_h = 15, .ofs_x = 2, .ofs_y = -2}, + {.bitmap_index = 109308, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 109335, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 109365, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 109393, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 2, .ofs_y = -2}, + {.bitmap_index = 109421, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 109451, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 109480, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 109509, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 109537, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 109566, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 109595, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 109622, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 109649, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 109676, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 109706, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 109736, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 109766, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 109796, .adv_w = 256, .box_w = 15, .box_h = 13, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 109821, .adv_w = 256, .box_w = 15, .box_h = 13, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 109846, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 109873, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 109902, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 109929, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 109958, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 109987, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 110014, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 110041, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 110070, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 110097, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 110126, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 110155, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 110184, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 110213, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 110242, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 110269, .adv_w = 256, .box_w = 15, .box_h = 13, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 110294, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 110321, .adv_w = 256, .box_w = 11, .box_h = 14, .ofs_x = 2, .ofs_y = -2}, + {.bitmap_index = 110341, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 110366, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 110393, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 110423, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 110450, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 110479, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 110506, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 110535, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 110565, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 110595, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 110622, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 110649, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 110678, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 110703, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 110730, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 110757, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 110784, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 110814, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 110843, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 110870, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 110899, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 110928, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 110953, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 110980, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 111005, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 111034, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 111062, .adv_w = 256, .box_w = 15, .box_h = 13, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 111087, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 111117, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 111144, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 111173, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 111198, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 111228, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 111256, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 111285, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 111314, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 111341, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 111366, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 111393, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 111420, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 111447, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 111476, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 111505, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 111535, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 111562, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 111591, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 111621, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 111648, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 111677, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 111706, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 111735, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 111765, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 111797, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 111826, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 111855, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 111882, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 111909, .adv_w = 256, .box_w = 13, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 111935, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 111962, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 111991, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 112018, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 112043, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 112072, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 112102, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 112129, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 112156, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 112183, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 112212, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 112241, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 112270, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 112299, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 112326, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 112356, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 112383, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 112410, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 112439, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 112466, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 112496, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 112523, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -3}, + {.bitmap_index = 112551, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 112578, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 112605, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 112632, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 112661, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 112691, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 112720, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 112749, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 112776, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 112803, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 112831, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 112861, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 112889, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 112921, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 112948, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 112975, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 113004, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 113033, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 113062, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 113094, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 113121, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 113151, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 113178, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 113208, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 113238, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 113268, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 113296, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 113323, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 113350, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 113382, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 113411, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 113440, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 113467, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 113499, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 113526, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 113556, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 113586, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 113613, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 113643, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 113672, .adv_w = 256, .box_w = 15, .box_h = 13, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 113697, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 113727, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 113756, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 113783, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 113813, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 113842, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 113871, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 113899, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 113928, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 113958, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 113983, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 114011, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 114039, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 114067, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 114096, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 114126, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 114155, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 114184, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 114214, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 114241, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 114270, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 114300, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 114327, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 114354, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 114384, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 114411, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 114439, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 114469, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 114498, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 114530, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 114559, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 114589, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 114619, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 114649, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 114679, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -3}, + {.bitmap_index = 114709, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 114739, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 114769, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 114799, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 114827, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 114856, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 114885, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 114917, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 114947, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 114977, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 115005, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 115035, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 115062, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 115092, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 115122, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 115152, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 115179, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 115206, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 115235, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 115265, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 115295, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 115325, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 115355, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 115384, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 115414, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 115442, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 115472, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 115502, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 115532, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 115562, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 115592, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 115621, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = -1, .ofs_y = -2}, + {.bitmap_index = 115651, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 115680, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 115707, .adv_w = 256, .box_w = 8, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 115722, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 115752, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 115781, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 115811, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 115840, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 115870, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 115899, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 115928, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 115957, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 115987, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 116016, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 116046, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 116075, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 116104, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 116133, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 116162, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 116191, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 116220, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 116249, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 116276, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 116306, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 116333, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 116363, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 116393, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 116422, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 116452, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 116484, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 116514, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 116544, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 116574, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 116604, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 116633, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 116662, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 116691, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 116720, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 116749, .adv_w = 256, .box_w = 13, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 116772, .adv_w = 256, .box_w = 13, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 116795, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 116825, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 116855, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 116885, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 116912, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 116941, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -3}, + {.bitmap_index = 116968, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 116996, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 117025, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 117054, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 117083, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 117113, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 117143, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 117172, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 117202, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 117229, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 117259, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 117286, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 117314, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 117343, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 117373, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 117403, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 117432, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 117459, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 117488, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 117517, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 117545, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 117572, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 117602, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 117634, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 117664, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 117691, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 117719, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 117746, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 117775, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 117805, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 117835, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 117865, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 117892, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 117917, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 117947, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 117976, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 118003, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 118032, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 118061, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 118091, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 118120, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 118149, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 118178, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 118208, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 118237, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 118267, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 118299, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 118329, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 118358, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 118388, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 118418, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 118443, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 118475, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 118505, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 118532, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 118561, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 118589, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 118621, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 118649, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 118677, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 118707, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 118736, .adv_w = 256, .box_w = 15, .box_h = 17, .ofs_x = 1, .ofs_y = -3}, + {.bitmap_index = 118768, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 118798, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 118828, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 118858, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 118888, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 118920, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 118949, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 118977, .adv_w = 256, .box_w = 15, .box_h = 17, .ofs_x = 1, .ofs_y = -3}, + {.bitmap_index = 119009, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 119039, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 119069, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 119099, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 119127, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 119159, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 119187, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 119214, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 119241, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 119270, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 119300, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 119330, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 119359, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 119389, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 119418, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 119447, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 119477, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 119506, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 119536, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 119563, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 119593, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 119623, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 119653, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 119683, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 119713, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 119743, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 119772, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 119802, .adv_w = 256, .box_w = 15, .box_h = 17, .ofs_x = 0, .ofs_y = -3}, + {.bitmap_index = 119834, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 119864, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 119894, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 119924, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 119953, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 119985, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 120015, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 120044, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 120073, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 120103, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 120133, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 120163, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 120192, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 120221, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = -1, .ofs_y = -2}, + {.bitmap_index = 120253, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 120283, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 120312, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 120344, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 120373, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 120405, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 120435, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 120467, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 120497, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 120529, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 120559, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 120589, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 120619, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 120649, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 120678, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 120710, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 120738, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 120770, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 120798, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 120830, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 120859, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 120889, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 120919, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 120949, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 120978, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 121007, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 121039, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 121068, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 121100, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 121130, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 121160, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 121190, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 121220, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 121249, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 121279, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 121309, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 121338, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 121370, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 121400, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 121429, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 121458, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 121488, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 121518, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 121548, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 121577, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 121604, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 121634, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 121664, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 121693, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 121723, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 121752, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 121782, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 121812, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 121842, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 121871, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 121900, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 121927, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 121956, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 121986, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 122015, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 122042, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 122069, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 122096, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 122125, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 122154, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 122181, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 122210, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 122239, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 122268, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 122297, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 122329, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 122358, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 122386, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 122416, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 122443, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 122475, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 122505, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 122534, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 122563, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 122590, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 122619, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 122648, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 122677, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 122706, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 122735, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 122764, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 122791, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 122820, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 122849, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 122876, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 122905, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 122934, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 122963, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 122992, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 123021, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 123048, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 123077, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 123106, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 123136, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 123165, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 123195, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 123222, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 123251, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 123280, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 123309, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 123338, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 123367, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 123397, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 123427, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 123456, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 123485, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 123515, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 123544, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 123573, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 123602, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 123632, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 123661, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 123691, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 123720, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 123749, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 123778, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 123807, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 123836, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 123865, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 123894, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 123923, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 123952, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 123981, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 124010, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 124039, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 124068, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 124097, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 124126, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 124155, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 124182, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 124211, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 124240, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 124267, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 124297, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 124324, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 124353, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 124383, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 124415, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 124445, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 124472, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 124501, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 124530, .adv_w = 256, .box_w = 9, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 124546, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 124573, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 124602, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 124631, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 124661, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 124690, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 124720, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 124749, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 124779, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 124809, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 124839, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 124866, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 124893, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 124922, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 124951, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 124980, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 125009, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 125039, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 125069, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 125098, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 125127, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 125156, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 125185, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 125214, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 125243, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 125273, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 125300, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 125327, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 125356, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 125386, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 125413, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 125442, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 125472, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 125499, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 125528, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 125557, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 125586, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 125618, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 125648, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 125677, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 125704, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 125733, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 125760, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 125789, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 125816, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 125846, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 125876, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 125906, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 125935, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 125964, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 125993, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 126023, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 126055, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 126085, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 126112, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 126144, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 126176, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 126206, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 126235, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 126264, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 126293, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 126322, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 126351, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 126380, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 126409, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 126439, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 126467, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 126496, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 126526, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 126555, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 126584, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 126613, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 126643, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 126672, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 126702, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 126729, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 126759, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 126791, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 126820, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 126850, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 126879, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 126907, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 126936, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 126966, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 126995, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 127025, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 127054, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 127083, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 127112, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 127140, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 127172, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 127201, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 127230, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 127260, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 127290, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 127319, .adv_w = 256, .box_w = 15, .box_h = 17, .ofs_x = 1, .ofs_y = -3}, + {.bitmap_index = 127351, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 127381, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 127411, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 127440, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 127469, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 127496, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 127525, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 127554, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 127584, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 127614, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 127643, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 127672, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 127701, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 127729, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 127759, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 127788, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 127817, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 127846, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 127875, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 127904, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 127933, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 127963, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 127995, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 128025, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 128052, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 128081, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 128110, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 128140, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 128169, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 128201, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 128230, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 128259, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -3}, + {.bitmap_index = 128289, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 128318, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 128348, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 128378, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 128407, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 128436, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 128466, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 128496, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 128525, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 128555, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 128585, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 128614, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 128643, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 128671, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 128698, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 128727, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 128756, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 128784, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 128811, .adv_w = 256, .box_w = 13, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 128834, .adv_w = 256, .box_w = 13, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 128857, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 128884, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 128913, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 128940, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 128965, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 128992, .adv_w = 256, .box_w = 15, .box_h = 13, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 129017, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 129044, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 129071, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 129100, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 129127, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 129156, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 129183, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 129212, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 129241, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 129268, .adv_w = 256, .box_w = 13, .box_h = 15, .ofs_x = 2, .ofs_y = -2}, + {.bitmap_index = 129293, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 129322, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 129351, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 129380, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 129409, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 129439, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 129468, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 129498, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 129525, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 129554, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 129583, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 129612, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 129644, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 129674, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 129703, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 129733, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 129762, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 129792, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 129822, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 129847, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 129874, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 129904, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 129933, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 129960, .adv_w = 256, .box_w = 15, .box_h = 13, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 129985, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 130014, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 130041, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 130073, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 130103, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 130130, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 130157, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 130186, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 130215, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 130244, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 130274, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 130301, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 130330, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 130358, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 130385, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 130410, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 130439, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 130468, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 130497, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 130526, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 130555, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 130584, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 130613, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 130640, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 130667, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 130694, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 130724, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 130753, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 130782, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 130811, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 130840, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 130869, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 130898, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 130927, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 130954, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 130983, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 131012, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 131039, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 131068, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 131097, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 131126, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 131156, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 131183, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 131213, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 131240, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 131268, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 131295, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 131327, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 131356, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 131385, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 131414, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 131444, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 131474, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 131503, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 131532, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 131559, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 131591, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 131618, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 131647, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 131676, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 131705, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 131734, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 131761, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 131790, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 131819, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 131848, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 131877, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 131906, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 131935, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 131962, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 131989, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 132019, .adv_w = 256, .box_w = 13, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 132044, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 132071, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 132098, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 132128, .adv_w = 256, .box_w = 12, .box_h = 15, .ofs_x = 2, .ofs_y = -2}, + {.bitmap_index = 132151, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 132180, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 132209, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 132238, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 132267, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 132294, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 132321, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 132348, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 132375, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 132404, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 132433, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 132460, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 132488, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 132518, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 132547, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 132576, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 132606, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 132635, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 132664, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 132694, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 132721, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 132750, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 132779, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 132808, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 132837, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 2, .ofs_y = -2}, + {.bitmap_index = 132864, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 132893, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 132923, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 132952, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 132979, .adv_w = 256, .box_w = 13, .box_h = 14, .ofs_x = 2, .ofs_y = -2}, + {.bitmap_index = 133002, .adv_w = 256, .box_w = 13, .box_h = 15, .ofs_x = 2, .ofs_y = -2}, + {.bitmap_index = 133027, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 133054, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 133084, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 133114, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 133143, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 133172, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 133201, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 133231, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 133258, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 133283, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 133312, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 133341, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 133370, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 133397, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 133427, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 133456, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 133485, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 133515, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 133544, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 133573, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 133600, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 133629, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 133656, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 133685, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 133714, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 133744, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 133773, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 133803, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 133830, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 133860, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 133889, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 133918, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 133947, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 133976, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 134005, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 134035, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 134065, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 134093, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 134123, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 134152, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 134181, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 134210, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 134239, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 134268, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 134298, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 134327, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 134356, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 134386, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 134415, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 134444, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 134471, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 134498, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 134527, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 134555, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 134582, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 134611, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 134641, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 134671, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 134700, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 134729, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 134758, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 134788, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 134818, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 134847, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 134876, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 134905, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 134935, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 134964, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 134993, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 135025, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 135052, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 135082, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 135111, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 135139, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 135167, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 135194, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 135223, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 135253, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 135282, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 135312, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 135342, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 135370, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 135397, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 135427, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 135454, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 135483, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 135510, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 135537, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 135566, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 135596, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 135626, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 135654, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 135683, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 135713, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 135742, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 135772, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 135802, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 135832, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 135862, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 135891, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 135923, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 135955, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 135985, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 136017, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 136047, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 136076, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 136108, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 136136, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 136165, .adv_w = 256, .box_w = 14, .box_h = 13, .ofs_x = 2, .ofs_y = -1}, + {.bitmap_index = 136188, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 136218, .adv_w = 256, .box_w = 12, .box_h = 15, .ofs_x = 2, .ofs_y = -2}, + {.bitmap_index = 136241, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 136271, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 136300, .adv_w = 256, .box_w = 15, .box_h = 13, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 136325, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 136354, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 136384, .adv_w = 256, .box_w = 13, .box_h = 15, .ofs_x = 2, .ofs_y = -2}, + {.bitmap_index = 136409, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 136439, .adv_w = 256, .box_w = 13, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 136464, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 136493, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 136525, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 136554, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 136579, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 136608, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 136635, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 136662, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 136689, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 136718, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 136746, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 136776, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 136803, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 136832, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 136861, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 136890, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 136917, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 136947, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 136977, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 137009, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 137039, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 137069, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 137098, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 137125, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 137155, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 137185, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 137212, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 137242, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 137271, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 137298, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 137327, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 137359, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 137389, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 137418, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 137448, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 137477, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 137509, .adv_w = 256, .box_w = 17, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 137541, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = -1, .ofs_y = -2}, + {.bitmap_index = 137573, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 137602, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 137631, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 137660, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 137690, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 137719, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 137744, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 137771, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 137800, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 137830, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 137857, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 137884, .adv_w = 256, .box_w = 15, .box_h = 6, .ofs_x = 0, .ofs_y = 7}, + {.bitmap_index = 137896, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 137925, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -3}, + {.bitmap_index = 137955, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 137984, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 138013, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 138042, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -3}, + {.bitmap_index = 138072, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 138101, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 138130, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 138159, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 138188, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 138218, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 138245, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 138274, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 138301, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 138330, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 138359, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 138388, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 138417, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 138446, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 138476, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 138505, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 138534, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 138563, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 138592, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 138621, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 138650, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 138679, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -3}, + {.bitmap_index = 138709, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 138738, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 138767, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 138796, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 138825, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 138854, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 138883, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 138912, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 138942, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 138969, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 138998, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 139025, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 139055, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 139084, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 139113, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 139142, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 139171, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 139200, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 139230, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 139259, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 139288, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 139317, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 139344, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 139373, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 139402, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 139432, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 139461, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 139490, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 139519, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 139548, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 139578, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 139608, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 139638, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 139668, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 139697, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 139727, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 139756, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 139785, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 139812, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 139841, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 139870, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 139899, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 139928, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 139957, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 139986, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 140015, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 140045, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 140074, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 140103, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 140133, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 140162, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 140191, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 140220, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 140249, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 140278, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 140307, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 140336, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 140365, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 140394, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 140423, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 140452, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 140479, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 140507, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 140537, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 140566, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 140595, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 140625, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 140654, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 140683, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 140712, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 140741, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 140770, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 140799, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 140828, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 140857, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 140887, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 140916, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 140945, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 140977, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 141006, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 141035, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 141065, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 141095, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 141120, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 141149, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 141178, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 141208, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 141237, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 141266, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 141295, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 141325, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 141354, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 141383, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 141412, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 141441, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 141470, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 141499, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 141529, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 141559, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 141589, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 141618, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 141646, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 141676, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 141706, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 141735, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 141765, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 141794, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 141824, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 141854, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 141881, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 141910, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 141939, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 141969, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 141998, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 142027, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 142056, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 142085, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 142114, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 142143, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 142172, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 142201, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 142231, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 142260, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 142290, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 142320, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 142349, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 142378, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 142405, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 142434, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 142463, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 142492, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 142521, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 142550, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 142580, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 142609, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 142638, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 142665, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 142692, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 142721, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 142751, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 142781, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 142810, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 142839, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 142868, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 142897, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 142926, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 142956, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 142985, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 143014, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 143043, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 143072, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 143101, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 143130, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 143159, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 143188, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 143217, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 143246, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 143275, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 143304, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 143333, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 143362, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 143391, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 143421, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 143451, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 143480, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 143509, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 143538, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 143567, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 143596, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 143625, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 143654, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 143683, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 143712, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 143742, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 143771, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 143800, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 143829, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 143859, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 143889, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 143918, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 143947, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 143977, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 144006, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 144035, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 144064, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 144093, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 144120, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 144150, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 144179, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 144209, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 144239, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 144268, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 144297, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 144327, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 144356, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 144385, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 144414, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 144443, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 144470, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 144499, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 144528, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 144557, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 144586, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 144615, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 144645, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 144674, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 144703, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 144732, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 144761, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 144791, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 144821, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 144850, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 144879, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 144909, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 144938, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 144967, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 144996, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 145025, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 145055, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 145084, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 145111, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 145140, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 145170, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 145199, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 145228, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 145260, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 145289, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 145318, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 145347, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 145376, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 145405, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 145434, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 145463, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 145493, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 145522, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 145551, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 145580, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 145609, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 145638, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 145667, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 145696, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 145723, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 145752, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 145781, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 145810, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 145839, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 145868, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 145897, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 145926, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 145955, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 145984, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 146013, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 146042, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 146071, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 146100, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 146129, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 146158, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 146187, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 146216, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 146245, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 146274, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 146303, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 146332, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 146361, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 146390, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 146419, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 146448, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 146475, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 146504, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 146533, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 146562, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 146592, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 146621, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 146648, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 146677, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 146706, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 146736, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 146765, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 146794, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 146823, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 146852, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 146881, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 146910, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 146940, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 146969, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -3}, + {.bitmap_index = 146999, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 147028, .adv_w = 256, .box_w = 15, .box_h = 17, .ofs_x = 1, .ofs_y = -3}, + {.bitmap_index = 147060, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 147089, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 147118, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 147147, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 147174, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 147203, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 147232, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 147262, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 147291, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 147320, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 147349, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 147378, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 147407, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 147436, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 147465, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 147492, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 147521, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 147550, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 147579, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 147608, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 147637, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 147666, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 147695, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 147725, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 147755, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 147785, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 147813, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 147843, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 147871, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 147900, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 147930, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 147960, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 147989, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 148017, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 148047, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 148072, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 148100, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 148130, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 148158, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 148190, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 148215, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 148244, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 148271, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 148300, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 148329, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 148358, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 148386, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 148415, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 148444, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 148474, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 148501, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 148528, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 148557, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 148587, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 148616, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 148643, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 148672, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 148701, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 148730, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 148760, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 148790, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 148817, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 148846, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 148875, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 148904, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 148934, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 148963, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 148995, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 149023, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 149053, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 149080, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 149109, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 149138, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 149165, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 149194, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 149224, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 149252, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 149281, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 149313, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 149342, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 149371, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 149399, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 149429, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 149459, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 149489, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 149519, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 149546, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 149573, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 149601, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 149630, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 149659, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 149689, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 149718, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 149748, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 149777, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 149806, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 149836, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 149863, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 149893, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -3}, + {.bitmap_index = 149925, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 149952, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 149984, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 150014, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 150043, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 150073, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 150103, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 150132, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 150162, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 150191, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 150220, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 150250, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 150280, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 150310, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 150339, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 150367, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 150397, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 150427, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 150456, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 150486, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 150516, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 150546, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 150576, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 150606, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 150636, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 150666, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 150698, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 150727, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 150757, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 150786, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 150816, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 150846, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 150878, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 150908, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 150938, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 150968, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 150998, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 151027, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 151059, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 151086, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 151116, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 151146, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 151178, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 151207, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 151239, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 151271, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 151301, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 151331, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 151363, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 151393, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 151423, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 151453, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 151483, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 151513, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 151542, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 151572, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 151599, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 151629, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 151658, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 151687, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 151715, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 151745, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 151777, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 151806, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 151835, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 151867, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 151897, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 151927, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 151959, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 151989, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 152019, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 152049, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 152079, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 152109, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 152139, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 152169, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 152198, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 152227, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 152256, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 152288, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 152318, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 152348, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 152378, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 152408, .adv_w = 256, .box_w = 15, .box_h = 17, .ofs_x = 0, .ofs_y = -3}, + {.bitmap_index = 152440, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 152470, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 152498, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 152525, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 152557, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 152586, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 152614, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 152644, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 152674, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 152703, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 152732, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 152764, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 152793, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 152822, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 152851, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 152880, .adv_w = 256, .box_w = 9, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 152897, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 152927, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 152956, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 152985, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 153015, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 153044, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 153073, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 153102, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 153131, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 153160, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 153189, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 153219, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 153246, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -3}, + {.bitmap_index = 153276, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 153306, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 153336, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 153365, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 153395, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 153424, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 153453, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 153482, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 153509, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 153539, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 153568, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 153597, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 153626, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 153655, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 153685, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 153715, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 153744, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 153773, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 153802, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 153831, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 153861, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 153893, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 153922, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 153949, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 153979, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 154009, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 154038, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 154067, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 154096, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 154125, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 154154, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 154183, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 154212, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 154244, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 154273, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 154302, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 154331, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 154361, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 154391, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 154420, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 154448, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 154475, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 154504, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 154532, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 154560, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 154588, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 154618, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 154647, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 154676, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 154705, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 154734, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 154763, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 154792, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 154821, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 154850, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 154879, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 154906, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 154935, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 154962, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 154991, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 155021, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 155051, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 155080, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 155107, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 155134, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 155161, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 155188, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -3}, + {.bitmap_index = 155217, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 155247, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 155276, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 155303, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 155332, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 155362, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 155391, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 155418, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 155447, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 155474, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 155503, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 155533, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 155562, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 155591, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 155620, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 155648, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 155678, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 155708, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 155735, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 155764, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 155794, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 155823, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 155855, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 155884, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 155916, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 155945, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 155974, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 156004, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 156034, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 156063, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 156092, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 156121, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 156150, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 156179, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 156208, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 156238, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 156267, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 156297, .adv_w = 256, .box_w = 7, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 156310, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 156339, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 156368, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 156397, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 156426, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 156456, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 156485, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 156512, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 156541, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 156568, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 156597, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 156626, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 156653, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 156682, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 156710, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 156737, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 156764, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 156793, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 156823, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 156851, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 156880, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 156909, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 156938, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 156968, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 156997, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 157026, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 157056, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 157084, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 157114, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 157143, .adv_w = 256, .box_w = 15, .box_h = 13, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 157168, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 157197, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 157226, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 157255, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 157282, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 157311, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 157341, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 157370, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 157400, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 157430, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 157460, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 157489, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 157518, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 157543, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 157570, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 157599, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 157627, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 157657, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 157686, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 157713, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 157743, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 157772, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 157801, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 157830, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 157859, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -3}, + {.bitmap_index = 157889, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 157918, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 157948, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 157978, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 158005, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 158037, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 158066, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 158095, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 158125, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 158154, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 158183, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 158212, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 158239, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 158268, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 158298, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 158325, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 158354, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 158383, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 158412, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 158441, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 158471, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 158501, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 158530, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 158559, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 158584, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 158613, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 158642, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 158671, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 158700, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 158729, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 158758, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 158788, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 158816, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 158845, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 158874, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 158904, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 158934, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 158963, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 158992, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 159021, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 159050, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 159080, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 159109, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 159139, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 159166, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 159196, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 159223, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 159252, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 159281, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 159311, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 159340, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 159370, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 159399, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 159428, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 159457, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 159486, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 159515, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 159545, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 159577, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 159607, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 159636, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 159665, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 159695, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 159725, .adv_w = 256, .box_w = 15, .box_h = 17, .ofs_x = 0, .ofs_y = -3}, + {.bitmap_index = 159757, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 159787, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 159817, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 159846, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 159876, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 159905, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 159932, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 159962, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 159991, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 160018, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 160047, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 160077, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 160106, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -3}, + {.bitmap_index = 160136, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 160165, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 160193, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 160223, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 160252, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 160281, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 160311, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 160340, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 160369, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 160398, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 160428, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 160458, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 160488, .adv_w = 256, .box_w = 15, .box_h = 13, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 160513, .adv_w = 256, .box_w = 16, .box_h = 13, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 160539, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 160569, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 160598, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 160626, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 160653, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 160685, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 160715, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 160744, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 160774, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 160801, .adv_w = 256, .box_w = 13, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 160826, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 160856, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 160885, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 160912, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 160941, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 160971, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 161000, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 161029, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 161061, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 161091, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 161116, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 161145, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 161173, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 161200, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 161230, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 161259, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 161288, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -3}, + {.bitmap_index = 161320, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 161349, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 161378, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 161407, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 161434, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 161463, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 161492, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 161519, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 161548, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 161580, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 161609, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 161638, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 161668, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 161698, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 161727, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 161757, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 161786, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 161814, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 161843, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 161870, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 161899, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 161931, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 161961, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 161991, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 162021, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 162048, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 162077, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 162104, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 162133, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 162165, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 162194, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 162224, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 162254, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 162281, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 162311, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 162339, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 162366, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 162396, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 162425, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 162454, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 162483, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 162513, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 162540, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 162567, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 162599, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 162628, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 162657, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 162686, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 162715, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 162744, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 162773, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 162800, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 162830, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 162860, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 162889, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 162918, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 162947, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 162976, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 163006, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 163035, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 163067, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 163096, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 163125, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 163152, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 163181, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 163211, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 163241, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 163271, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 163301, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 163331, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 163363, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 163395, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 163424, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 163454, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 163484, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 163516, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 163545, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 163574, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 163604, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 163634, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 163661, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 163689, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 163719, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -3}, + {.bitmap_index = 163746, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 163776, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 163803, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 163831, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 163859, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 163888, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 163917, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 163947, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 163976, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 164006, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 164035, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 164064, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 164093, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 164120, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 164149, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 164176, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 164205, .adv_w = 256, .box_w = 15, .box_h = 13, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 164230, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 164258, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 164285, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 164314, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 164343, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 164372, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 164402, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -3}, + {.bitmap_index = 164431, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 164459, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 164491, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 164520, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 164550, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 164580, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 164609, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 164638, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 164665, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 164694, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 164722, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -3}, + {.bitmap_index = 164754, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 164784, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 164813, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 164842, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 164871, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 164900, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 164927, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 164955, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 164984, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 165011, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 165040, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 165069, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 165099, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 165128, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 165158, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 165188, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 165220, .adv_w = 256, .box_w = 16, .box_h = 13, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 165246, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 165276, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 165306, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 165334, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 165364, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 165394, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 165423, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 165453, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 165482, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 165511, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 165538, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 165568, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 165595, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 165625, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 165654, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -3}, + {.bitmap_index = 165684, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 165713, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 165743, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 165770, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 165799, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 165826, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 165854, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 165886, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 165916, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 165944, .adv_w = 256, .box_w = 16, .box_h = 17, .ofs_x = 0, .ofs_y = -3}, + {.bitmap_index = 165978, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 166010, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 166038, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 166067, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 166097, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 166126, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 166156, .adv_w = 256, .box_w = 16, .box_h = 17, .ofs_x = 0, .ofs_y = -3}, + {.bitmap_index = 166190, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 166220, .adv_w = 256, .box_w = 17, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 166252, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 166284, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 166311, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 166340, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 166370, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 166400, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 166430, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 166460, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 166489, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 166519, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 166548, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 166575, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 166604, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 166633, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 166662, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 166692, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 166720, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 166749, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 166778, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 166807, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 166836, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 166865, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 166894, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 166923, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 166952, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 166984, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 167011, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 167040, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 167069, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 167098, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 167127, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 167156, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 167185, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 167214, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 167246, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 167275, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 167303, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 167332, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 167361, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 167390, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 167419, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 167448, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 167477, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 167507, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 167536, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 167566, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 167596, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 167626, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 167655, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 167684, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 167714, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 167743, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 167772, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 167801, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 167830, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 167860, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 167889, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 167918, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 167947, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 167977, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 168006, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 168035, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 168064, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 168093, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 168121, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 168146, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 168173, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 168200, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 168229, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 168258, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 168285, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 168312, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 168341, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 168369, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 168396, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 168425, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 168455, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 168482, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 168509, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 168539, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 168569, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 168598, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 168625, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 168655, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 168682, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 168711, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 168741, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 168770, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 168797, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 168826, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 168858, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 168887, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 168919, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 168949, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 168978, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 169007, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 169036, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 169065, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 169094, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 169124, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 169152, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 169181, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 169208, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 169237, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 169267, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 169296, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 169325, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 169354, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 169384, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 169413, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 169442, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 169474, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 169503, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 169532, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 169561, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 169591, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 169621, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 169650, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 169679, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 169708, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 169738, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 169766, .adv_w = 256, .box_w = 15, .box_h = 13, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 169791, .adv_w = 256, .box_w = 15, .box_h = 13, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 169816, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 169846, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 169875, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 169903, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 169932, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 169961, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 169990, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 170019, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 170048, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 170080, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 170109, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 170136, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 170166, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 170195, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 170225, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 170254, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 170284, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 170314, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 170343, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 170375, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 170405, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 170435, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 170467, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 170497, .adv_w = 256, .box_w = 17, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 170527, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 170556, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 170585, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 170614, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 170643, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 170672, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 170701, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 170731, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 170760, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 170787, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 170815, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 170845, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 170877, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 170906, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 170935, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 170964, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 170993, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 171023, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 171053, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 171085, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 171117, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 171147, .adv_w = 256, .box_w = 13, .box_h = 15, .ofs_x = 2, .ofs_y = -2}, + {.bitmap_index = 171172, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 171199, .adv_w = 256, .box_w = 13, .box_h = 15, .ofs_x = 2, .ofs_y = -2}, + {.bitmap_index = 171224, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 171251, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 171280, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 171307, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 171339, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 171371, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 171398, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 171426, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 171455, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 171483, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 171513, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 171540, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 171569, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 171598, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 171627, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 171654, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 171681, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 171709, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 171739, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 171766, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 171796, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 171825, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 171854, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 171883, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 171913, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 171942, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 171969, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 171998, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 172028, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 2, .ofs_y = -2}, + {.bitmap_index = 172055, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 172084, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 172114, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 172143, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 172170, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 172200, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 172227, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 172256, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 172285, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 172312, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 172339, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 172366, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 172393, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 172422, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 172452, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 172481, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 172511, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 172541, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 172570, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 172599, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 172626, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 172653, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 172680, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 172707, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 172736, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 172763, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 172793, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 172822, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 172849, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 172878, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 172905, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 172934, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 172961, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 172988, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 173017, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 173044, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 173071, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 173101, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 173130, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 173157, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 173184, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 173211, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 173240, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 173267, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 173296, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 173325, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 173354, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 173383, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 173412, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 173441, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 173468, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 173500, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 173527, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 173554, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 173583, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 173612, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 173641, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 173669, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 173698, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 173727, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 173759, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 173788, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 173817, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 173846, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 173873, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 173900, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 173929, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 173958, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 173988, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 174017, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 174046, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 174073, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 174100, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 174130, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 174157, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 174185, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 174214, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 174241, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 174271, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 174301, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 174328, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 174357, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 174386, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 174416, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 174445, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 174475, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 174502, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 174529, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 174558, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 174586, .adv_w = 256, .box_w = 15, .box_h = 13, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 174611, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 174638, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 174663, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 174692, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 174719, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 174747, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 174774, .adv_w = 256, .box_w = 15, .box_h = 13, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 174799, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 174828, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 174857, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 174886, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 174915, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 174942, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 174969, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 174999, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 175028, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 175056, .adv_w = 256, .box_w = 7, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 175070, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 175099, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 175127, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 175156, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 175186, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 175213, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 175242, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 175271, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 175298, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 175327, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 175352, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 175381, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -3}, + {.bitmap_index = 175413, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 175441, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 175468, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 175497, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 175526, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 175555, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 175584, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 175613, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 175640, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 175669, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 175697, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 175724, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 175751, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 175780, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 175809, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 175838, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 175867, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 175894, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 175924, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 175952, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 175979, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 176006, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 176036, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 176066, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 176095, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 176124, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 176151, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 176180, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 176210, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 176240, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 176268, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 176295, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 176324, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 176353, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 176382, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 176409, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 176438, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 176467, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 176496, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 176526, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 176553, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 176580, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 176607, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 176634, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 176661, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 176690, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 176719, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 176748, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 176777, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 176806, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 176835, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 176863, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 176892, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 176921, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 176950, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 176979, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 177008, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 177037, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 177067, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 177096, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 177125, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 177154, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 177184, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 177213, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 177242, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 177271, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 177299, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 177328, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 177357, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 177386, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 177413, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 177442, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 177469, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 177498, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 177527, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 177554, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 177583, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 177613, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 177642, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 177671, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 177700, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 177730, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 177759, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 177786, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 177815, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 177844, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 177874, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 177904, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 177933, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 177963, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 177992, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 178021, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 178051, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 178078, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 178107, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 178136, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 178164, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 178193, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 178223, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 178252, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 178281, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 178308, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 178337, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 178364, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 178393, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 178420, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 178449, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 178478, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 178507, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 178536, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 178563, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 178590, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 178619, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 178648, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 178677, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 178706, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 178735, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 178764, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 178793, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 178823, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 178852, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 178881, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 178910, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 178939, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 178968, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 178997, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 179026, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 179055, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 179084, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 179114, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 179143, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 179172, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 179202, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 179231, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 179260, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 179288, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 179317, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 179346, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 179376, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 179406, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 179436, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 179465, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 179494, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 179523, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 179552, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 179581, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 179610, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 179639, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 179669, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 179698, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 179728, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 179757, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 179786, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 179816, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 179845, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 179874, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 179903, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 179932, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 179961, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 179991, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 180021, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 180050, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 180080, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 180107, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 180134, .adv_w = 256, .box_w = 15, .box_h = 17, .ofs_x = 0, .ofs_y = -3}, + {.bitmap_index = 180166, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 180195, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 180224, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 180254, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 180283, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 180312, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 180344, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 180371, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 180401, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 180431, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 180458, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 180488, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 180517, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 180547, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 180574, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 180604, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 180636, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 180665, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 180695, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 180725, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 180754, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 180783, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 180812, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 180840, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 180872, .adv_w = 256, .box_w = 15, .box_h = 17, .ofs_x = 0, .ofs_y = -3}, + {.bitmap_index = 180904, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 180933, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 180962, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 180991, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 181020, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 181049, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 181078, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 181106, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 181135, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 181164, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 181192, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 181221, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 181251, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 181281, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 181311, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 181341, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 181370, .adv_w = 256, .box_w = 13, .box_h = 15, .ofs_x = 2, .ofs_y = -2}, + {.bitmap_index = 181395, .adv_w = 256, .box_w = 13, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 181420, .adv_w = 256, .box_w = 13, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 181445, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 181472, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 181499, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 181526, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 181553, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 181580, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 181607, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 181634, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 181661, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 181688, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 181715, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 181742, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 181769, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 181796, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 181823, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 181850, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 181877, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 181904, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 181931, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 181958, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 181985, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 182012, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 182039, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 182066, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 182093, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 182120, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 182147, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 182174, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 182201, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 182228, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 182255, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 182282, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 182309, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 182336, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 182363, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 182390, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 182417, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 182444, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 182471, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 182498, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 182525, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 182552, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 182579, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 182606, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 182636, .adv_w = 256, .box_w = 6, .box_h = 14, .ofs_x = 2, .ofs_y = -2}, + {.bitmap_index = 182647, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 182674, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 182701, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 182728, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 182757, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 182786, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 182815, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 182843, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 182868, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 182893, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 182922, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 182950, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 182977, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 183007, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 183036, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 183061, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 183088, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 183117, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 183146, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 183173, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 183200, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 183227, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 183256, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 183283, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 183310, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 183337, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 183367, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 183394, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 183423, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 183452, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 183479, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 183508, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 183537, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 183566, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 183595, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 183620, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 183647, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 183676, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 183705, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 183732, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 183759, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 183789, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 183819, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 183846, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 183873, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 183900, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 183928, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 183953, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 183983, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 184013, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 184042, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 184072, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 184097, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 184127, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 184155, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 184184, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 184212, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 184239, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 184266, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 184296, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 184325, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 184355, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 184382, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 184411, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 184440, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 184469, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 184497, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 184527, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 184556, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 184585, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 184617, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 184647, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 184677, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 184705, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 184735, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 184767, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 184799, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 184829, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 184859, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 184886, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 184915, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 184942, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -3}, + {.bitmap_index = 184972, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 185001, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 185031, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 185058, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 185085, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 185115, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 185142, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 185171, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 185198, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -3}, + {.bitmap_index = 185228, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 185257, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 185286, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 185315, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 185344, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -3}, + {.bitmap_index = 185374, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 185401, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 185430, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 185460, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 185489, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 185518, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 185545, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 185572, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 185601, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 185630, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 185659, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 185688, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 185718, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 185747, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 185776, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 185805, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 185834, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 185863, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 185892, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 185921, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 185950, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 185977, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 186005, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 186034, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 186063, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 186092, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 186121, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 186150, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 186179, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 186208, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 186237, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 186266, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 186295, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 186325, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 186354, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 186381, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 186410, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 186439, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 186469, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 186499, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 186529, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 186559, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 186586, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 186615, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 186644, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 186673, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 186703, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 186732, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 186759, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 186788, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 186817, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 186844, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 186871, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 186898, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 2, .ofs_y = -2}, + {.bitmap_index = 186923, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 186950, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 186979, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 187008, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 187037, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 187064, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 187091, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 187118, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 187148, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 187175, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -3}, + {.bitmap_index = 187204, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 187231, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 187263, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 187290, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 187319, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 187351, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 187381, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 187409, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 187439, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 187468, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 187497, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 187527, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 187556, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 187581, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 187611, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 187639, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -3}, + {.bitmap_index = 187671, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 187701, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 187728, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 187757, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 187785, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 187815, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 187845, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 187875, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 187902, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 187934, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 187964, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 187991, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 188019, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 188048, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 188075, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 188104, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 188133, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 188161, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 188191, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 188220, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 188248, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 188280, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 188307, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 188337, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 188367, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 188394, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 188426, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 188456, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 188485, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 188513, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 188545, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 188575, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -3}, + {.bitmap_index = 188605, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 188634, .adv_w = 256, .box_w = 9, .box_h = 15, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 188651, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -3}, + {.bitmap_index = 188681, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 188708, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 188738, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 188768, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 188795, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 188825, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 188854, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 188884, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 188914, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 188943, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 188973, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 189002, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 189031, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 189060, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 189089, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 189121, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 189150, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 189179, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 189208, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 189237, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 189267, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 189296, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 189326, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 189356, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 189385, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 189414, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 189443, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 189472, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 189501, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 189531, .adv_w = 256, .box_w = 15, .box_h = 17, .ofs_x = 0, .ofs_y = -3}, + {.bitmap_index = 189563, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 189592, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 189619, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 189646, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 189675, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 189705, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 189735, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 189765, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 189794, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 189824, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 189856, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 189881, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 189910, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 189939, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 189964, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 189991, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 190020, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 190047, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 190076, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 190103, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 190131, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -3}, + {.bitmap_index = 190161, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 190188, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 190218, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 190243, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 190272, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 190299, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 190327, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 190357, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 190386, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 190416, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 190444, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 190471, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 190501, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 190531, .adv_w = 256, .box_w = 13, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 190556, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 190584, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 190616, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 190646, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 190676, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 190706, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 190733, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 190763, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 190790, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 190819, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 190848, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 190878, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 190906, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 190938, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -3}, + {.bitmap_index = 190968, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 190998, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 191027, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 191055, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 191084, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 191113, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 191140, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 191169, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 191197, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 191224, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 191252, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 191281, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 191308, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 191337, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 191367, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 191397, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 191424, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 191453, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 191483, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 191513, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 191542, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 191572, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 191601, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 191633, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 191663, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 191692, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 191722, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 191752, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 191784, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 191814, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 191841, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 191871, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 191901, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 191930, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 191959, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 191986, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 192013, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 192042, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 192071, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 192100, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 192129, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 192158, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 192187, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 192216, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 192245, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 192274, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 192303, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 2, .ofs_y = -1}, + {.bitmap_index = 192330, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 192357, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 192385, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 192415, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 192444, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 192474, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 192506, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 192535, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 192565, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 192593, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 192623, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 192655, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 192687, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 192716, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 192748, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 192778, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 192807, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 192837, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 192866, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 192898, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 192930, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 192962, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 192992, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 193022, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 193052, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 193082, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 193111, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 193143, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 193170, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 193200, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 193230, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 193260, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 193289, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 193321, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 193353, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 193380, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 193412, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 193444, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 193474, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 193503, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 193531, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 193560, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 193590, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 193620, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 193652, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 193681, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 193711, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 193741, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 193770, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 193802, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 193832, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 193862, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 193894, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 193924, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 193953, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 193985, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 194017, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 194047, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 194076, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 194106, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 194136, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 194168, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 194198, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 194230, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 194262, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 194294, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 194324, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 194356, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 194386, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 194416, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 194443, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 194472, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 194499, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 194529, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 194561, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 194590, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 194620, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 194650, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 194679, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 194709, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 194738, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 194768, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 194798, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 194827, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 194859, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 194891, .adv_w = 256, .box_w = 16, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 194919, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 194947, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 194976, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 195003, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 195030, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 195057, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 195085, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 195115, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 195144, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 195176, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 195205, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 195232, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 195261, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 195289, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 195318, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 195346, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 195375, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 195402, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 195431, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 195460, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 195487, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 195515, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 195545, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 195574, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 195604, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 195634, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 195662, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 195691, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 195720, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 195748, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 195778, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 195805, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 195833, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 195862, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 195891, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 195921, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 195950, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 195979, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 196008, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 196038, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 196068, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 196098, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 196125, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 196155, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 196185, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 196215, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 196245, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 196275, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 196305, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 196332, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 196362, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 196390, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 196420, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 196452, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 196481, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 196508, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 196537, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 196569, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 196599, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 196629, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 196659, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 196686, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 196715, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 196745, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 196777, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 196806, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 196836, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 196865, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 196895, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 196925, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 196957, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 196989, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 197019, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 197047, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 197076, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 197106, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 197135, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 197162, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 197191, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -3}, + {.bitmap_index = 197221, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 197246, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 197276, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 197306, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 197333, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 197363, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -1}, + {.bitmap_index = 197393, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 197422, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 197451, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 197480, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 197510, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 197540, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 197570, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 197600, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 197629, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 197659, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 197689, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 197718, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 197747, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 197776, .adv_w = 256, .box_w = 15, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 197803, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 2, .ofs_y = -2}, + {.bitmap_index = 197830, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 197860, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 197890, .adv_w = 256, .box_w = 14, .box_h = 14, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 197915, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 197942, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 197971, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -3}, + {.bitmap_index = 197999, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 198028, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 2, .ofs_y = -2}, + {.bitmap_index = 198055, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 198082, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 198109, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 198138, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 198167, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 198196, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 198226, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 198255, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 198287, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 198317, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 198344, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 198374, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 198403, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 198432, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 198461, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 198490, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 198520, .adv_w = 256, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 198547, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 198577, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 198606, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 198635, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 198665, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 198695, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 198724, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 198753, .adv_w = 256, .box_w = 15, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 198782, .adv_w = 256, .box_w = 14, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 198810, .adv_w = 256, .box_w = 16, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, + {.bitmap_index = 198840, .adv_w = 256, .box_w = 15, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, + {.bitmap_index = 198870, .adv_w = 256, .box_w = 1, .box_h = 12, .ofs_x = 3, .ofs_y = -1}, + {.bitmap_index = 198872, .adv_w = 256, .box_w = 4, .box_h = 4, .ofs_x = 6, .ofs_y = 8}, + {.bitmap_index = 198874, .adv_w = 256, .box_w = 10, .box_h = 13, .ofs_x = 3, .ofs_y = -1}, + {.bitmap_index = 198891, .adv_w = 256, .box_w = 9, .box_h = 15, .ofs_x = 4, .ofs_y = -2}, + {.bitmap_index = 198908, .adv_w = 256, .box_w = 14, .box_h = 13, .ofs_x = 1, .ofs_y = -1}, + {.bitmap_index = 198931, .adv_w = 256, .box_w = 12, .box_h = 13, .ofs_x = 2, .ofs_y = -1}, + {.bitmap_index = 198951, .adv_w = 256, .box_w = 4, .box_h = 3, .ofs_x = 6, .ofs_y = 9}, + {.bitmap_index = 198953, .adv_w = 256, .box_w = 4, .box_h = 15, .ofs_x = 7, .ofs_y = -2}, + {.bitmap_index = 198961, .adv_w = 256, .box_w = 4, .box_h = 15, .ofs_x = 5, .ofs_y = -2}, + {.bitmap_index = 198969, .adv_w = 256, .box_w = 9, .box_h = 9, .ofs_x = 3, .ofs_y = 2}, + {.bitmap_index = 198980, .adv_w = 256, .box_w = 9, .box_h = 8, .ofs_x = 3, .ofs_y = 2}, + {.bitmap_index = 198989, .adv_w = 256, .box_w = 2, .box_h = 4, .ofs_x = 3, .ofs_y = -2}, + {.bitmap_index = 198990, .adv_w = 256, .box_w = 8, .box_h = 1, .ofs_x = 4, .ofs_y = 5}, + {.bitmap_index = 198991, .adv_w = 256, .box_w = 2, .box_h = 1, .ofs_x = 2, .ofs_y = 0}, + {.bitmap_index = 198992, .adv_w = 256, .box_w = 8, .box_h = 12, .ofs_x = 4, .ofs_y = -1}, + {.bitmap_index = 199004, .adv_w = 256, .box_w = 8, .box_h = 13, .ofs_x = 4, .ofs_y = -1}, + {.bitmap_index = 199017, .adv_w = 256, .box_w = 5, .box_h = 12, .ofs_x = 5, .ofs_y = 0}, + {.bitmap_index = 199025, .adv_w = 256, .box_w = 8, .box_h = 13, .ofs_x = 4, .ofs_y = -1}, + {.bitmap_index = 199038, .adv_w = 256, .box_w = 8, .box_h = 13, .ofs_x = 4, .ofs_y = -1}, + {.bitmap_index = 199051, .adv_w = 256, .box_w = 9, .box_h = 12, .ofs_x = 3, .ofs_y = 0}, + {.bitmap_index = 199065, .adv_w = 256, .box_w = 8, .box_h = 13, .ofs_x = 4, .ofs_y = -1}, + {.bitmap_index = 199078, .adv_w = 256, .box_w = 8, .box_h = 13, .ofs_x = 4, .ofs_y = -1}, + {.bitmap_index = 199091, .adv_w = 256, .box_w = 8, .box_h = 13, .ofs_x = 4, .ofs_y = -1}, + {.bitmap_index = 199104, .adv_w = 256, .box_w = 9, .box_h = 13, .ofs_x = 4, .ofs_y = -1}, + {.bitmap_index = 199119, .adv_w = 256, .box_w = 8, .box_h = 13, .ofs_x = 4, .ofs_y = -1}, + {.bitmap_index = 199132, .adv_w = 256, .box_w = 2, .box_h = 8, .ofs_x = 3, .ofs_y = -2}, + {.bitmap_index = 199134, .adv_w = 256, .box_w = 2, .box_h = 11, .ofs_x = 3, .ofs_y = -2}, + {.bitmap_index = 199137, .adv_w = 256, .box_w = 8, .box_h = 11, .ofs_x = 4, .ofs_y = 0}, + {.bitmap_index = 199148, .adv_w = 256, .box_w = 8, .box_h = 5, .ofs_x = 4, .ofs_y = 3}, + {.bitmap_index = 199153, .adv_w = 256, .box_w = 8, .box_h = 11, .ofs_x = 4, .ofs_y = 0}, + {.bitmap_index = 199164, .adv_w = 256, .box_w = 8, .box_h = 13, .ofs_x = 1, .ofs_y = 0}, + {.bitmap_index = 199177, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -4} +}; + +/*--------------------- + * CHARACTER MAPPING + *--------------------*/ + +static const uint8_t glyph_id_ofs_list_1[] = { + 0, 1, 2, 3, 0, 4, 0, 0, + 5, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 0, 15, 16, 17, 18, 19 +}; + +static const uint8_t glyph_id_ofs_list_4[] = { + 0, 1, 0, 2, 0, 0, 0, 3, + 4, 5, 6, 7, 8, 9, 10, 0, + 11, 12, 0, 13, 14, 15, 16, 0, + 17, 18, 19, 20, 21, 22, 23, 0, + 0, 0, 24, 0, 25, 26, 0, 27, + 28, 0, 29, 30, 31, 32, 0, 0, + 33, 0, 34, 0, 35, 0, 36, 0, + 37, 38, 39, 40, 0, 41, 42, 43, + 0, 0, 0, 44, 0, 45, 0, 46, + 47, 48, 0, 49, 50, 51, 52, 53, + 54, 0, 55, 56, 57, 0, 58, 0, + 59, 60, 0, 0, 61, 62, 63, 64, + 65, 66, 0, 0, 0, 0, 67, 0, + 0, 68, 0, 0, 0, 0, 0, 0, + 69, 70, 0, 71, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 72, 0, + 0, 0, 0, 0, 0, 0, 73, 0, + 74, 75, 0, 76, 77, 78, 79, 80, + 0, 81, 82, 83, 84, 85, 0, 0, + 86, 0, 87, 88, 0, 0, 0, 89, + 90, 91, 92, 0, 93, 94, 95, 96, + 97, 98, 0, 99, 100, 101, 102, 0, + 0, 0, 103, 104, 0, 105, 0, 0, + 0, 0, 106, 107, 0, 0, 0, 108, + 109, 110, 111, 112, 113, 114, 115, 116, + 0, 117, 118, 119, 0, 120, 121, 0, + 0, 122, 0, 123, 124, 125, 126, 127, + 128, 129, 0, 0, 0, 130, 131, 132, + 0, 133, 0, 134, 135, 136, 0, 0, + 137, 0, 138, 139, 140, 0, 0, 0, + 141, 0, 142, 143, 0, 144, 145, 146 +}; + +static const uint16_t unicode_list_5[] = { + 0x0, 0x2, 0x4, 0x6, 0xe, 0xf, 0x12, 0x13, + 0x14, 0x15, 0x16, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, + 0x23, 0x24, 0x25, 0x27, 0x29, 0x2a, 0x2b, 0x2c, + 0x2f, 0x30, 0x34, 0x35, 0x37, 0x39, 0x3b, 0x3d, + 0x3f, 0x41, 0x42, 0x48, 0x4b, 0x52, 0x53, 0x54, + 0x55, 0x56, 0x58, 0x5a, 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x68, 0x69, + 0x6a, 0x6c, 0x6e, 0x71, 0x74, 0x75, 0x78, 0x79, + 0x7b, 0x80, 0x81, 0x83, 0x84, 0x88, 0x89, 0x8d, + 0x8e, 0x90, 0x92, 0x94, 0x96, 0x99, 0x9c, 0xa0, + 0xa2, 0xa5, 0xa8, 0xaa, 0xab, 0xac, 0xad, 0xae, + 0xaf, 0xb1, 0xb3, 0xb4, 0xba, 0xc4, 0xc8, 0xc9, + 0xca, 0xcf, 0xd3, 0xd4, 0xd5, 0xd6, 0xdc, 0xdd, + 0xdf, 0xe1, 0xe2, 0xe3, 0xe4, 0xe6, 0xe8, 0xeb, + 0xed, 0xee, 0xef, 0xf2, 0xf3, 0xf4, 0xf6, 0xf8, + 0xfd, 0xff, 0x103, 0x111, 0x112, 0x114, 0x117, 0x119, + 0x11d, 0x11e, 0x11f, 0x121, 0x124, 0x126, 0x12a, 0x12b, + 0x12d, 0x12e, 0x12f, 0x131, 0x132, 0x133, 0x13f, 0x141, + 0x143, 0x148, 0x14c, 0x14d, 0x151, 0x153, 0x154, 0x15a, + 0x15f, 0x161, 0x16a, 0x171, 0x17b, 0x17c, 0x180, 0x183, + 0x184, 0x185, 0x18a, 0x18d, 0x192, 0x1a8, 0x1aa, 0x1ac, + 0x1ad, 0x1ae, 0x1b1, 0x1b7, 0x1bf, 0x1c0, 0x1d4, 0x1db, + 0x1df, 0x1eb, 0x1ec, 0x1f1, 0x1f2, 0x1f3, 0x1f8, 0x1fa, + 0x200, 0x20b, 0x20c, 0x210, 0x217, 0x226, 0x244, 0x245, + 0x246, 0x248, 0x249, 0x24a, 0x24b, 0x24d, 0x24e, 0x250, + 0x252, 0x256, 0x259, 0x25a, 0x25b, 0x25f, 0x261, 0x267, + 0x26a, 0x26d, 0x270, 0x271, 0x272, 0x273, 0x275, 0x276, + 0x278, 0x279, 0x27a, 0x27b, 0x27c, 0x27d, 0x27e, 0x280, + 0x281, 0x282, 0x285, 0x286, 0x287, 0x28a, 0x28d, 0x28e, + 0x291, 0x292, 0x297, 0x29a, 0x29b, 0x29c, 0x29e, 0x2a0, + 0x2a1, 0x2a5, 0x2a7, 0x2a9, 0x2aa, 0x2b0, 0x2b1, 0x2b4, + 0x2b5, 0x2b6, 0x2b7, 0x2b8, 0x2ba, 0x2bb, 0x2bc, 0x2c0, + 0x2c1, 0x2c2, 0x2c5, 0x2c9, 0x2cb, 0x2cc, 0x2ce, 0x2d0, + 0x2d1, 0x2d4, 0x2d6, 0x2e0, 0x2e2, 0x2e5, 0x2e6, 0x2e9, + 0x2f0, 0x2f2, 0x2f4, 0x2f5, 0x2f8, 0x2fa, 0x2fb, 0x2fd, + 0x2fe, 0x2ff, 0x300, 0x301, 0x302, 0x304, 0x305, 0x306, + 0x307, 0x308, 0x30b, 0x30c, 0x30d, 0x30f, 0x312, 0x313, + 0x316, 0x317, 0x31b, 0x31c, 0x31d, 0x31e, 0x31f, 0x320, + 0x322, 0x325, 0x329, 0x32d, 0x32e, 0x330, 0x332, 0x333, + 0x335, 0x338, 0x33b, 0x33c, 0x33d, 0x33e, 0x33f, 0x340, + 0x342, 0x344, 0x345, 0x346, 0x347, 0x348, 0x34f, 0x351, + 0x352, 0x355, 0x356, 0x359, 0x35b, 0x361, 0x363, 0x366, + 0x36a, 0x36c, 0x36e, 0x36f, 0x374, 0x377, 0x382, 0x384, + 0x386, 0x387, 0x38d, 0x395, 0x398, 0x3a0, 0x3a2, 0x3a3, + 0x3a4, 0x3a5, 0x3a6, 0x3a7, 0x3a8, 0x3ad, 0x3ae, 0x3af, + 0x3b0, 0x3b1, 0x3b2, 0x3b6, 0x3b7, 0x3b8, 0x3c3, 0x3c4, + 0x3c8, 0x3cc, 0x3ce, 0x3d0, 0x3d5, 0x3d7, 0x3db, 0x3dd, + 0x3e4, 0x3e9, 0x3f5, 0x3fe, 0x3ff, 0x403, 0x404, 0x405, + 0x40a, 0x40b, 0x40d, 0x412, 0x414, 0x415, 0x41a, 0x41b, + 0x41c, 0x41e, 0x41f, 0x422, 0x425, 0x426, 0x428, 0x42b, + 0x42f, 0x433, 0x43e, 0x43f, 0x440, 0x443, 0x444, 0x446, + 0x448, 0x44a, 0x44c, 0x44d, 0x44e, 0x44f, 0x453, 0x454, + 0x456, 0x457, 0x458, 0x45a, 0x45b, 0x45c, 0x45f, 0x461, + 0x463, 0x464, 0x465, 0x466, 0x467, 0x468, 0x469, 0x46b, + 0x46c, 0x46e, 0x470, 0x473, 0x474, 0x475, 0x476, 0x478, + 0x479, 0x47a, 0x47c, 0x47d, 0x47f, 0x484, 0x487, 0x489, + 0x48a, 0x48b, 0x48e, 0x490, 0x491, 0x492, 0x49a, 0x49d, + 0x49f, 0x4a2, 0x4a4, 0x4a7, 0x4a8, 0x4aa, 0x4ab, 0x4ad, + 0x4ae, 0x4b3, 0x4bb, 0x4c0, 0x4c4, 0x4c6, 0x4c7, 0x4cd, + 0x4ce, 0x4cf, 0x4d0, 0x4d1, 0x4d2, 0x4d6, 0x4d9, 0x4db, + 0x4dc, 0x4dd, 0x4de, 0x4e0, 0x4e4, 0x4e5 +}; + +static const uint8_t glyph_id_ofs_list_6[] = { + 0, 1, 2, 3, 0, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, + 15, 0, 16, 17, 18, 19, 20, 0, + 21, 22, 23, 0, 0, 0, 24, 0, + 25, 26, 0, 27, 0, 28, 29, 30, + 0, 31, 32, 33, 34, 35, 36, 37, + 38, 0, 39, 40, 41, 0, 0, 0, + 42, 0, 43, 44, 45, 46, 47, 0, + 48, 0, 0, 49, 50, 51, 52, 0, + 53, 54, 55, 56, 57, 0, 58, 59, + 0, 60, 61, 0, 0, 62, 63, 0, + 64, 65, 0, 66, 0, 67, 0, 0, + 68, 0, 0, 69, 0, 70, 0, 71, + 72, 0, 0, 0, 0, 73, 0, 74, + 75, 76, 77, 78, 79, 80, 81, 0, + 82, 83, 0, 0, 0, 0, 0, 84, + 0, 85, 0, 86, 0, 87, 0, 0, + 0, 0, 0, 0, 0, 0, 88, 89, + 90, 0, 91, 92, 93, 94, 0, 0, + 95, 96, 97, 0, 0, 98, 0, 99, + 0, 100, 0, 101, 0, 0, 0, 0, + 102, 103, 0, 104, 105, 106, 0, 107, + 0, 108, 109, 110, 0, 0, 111, 112, + 113, 0, 114, 0, 0, 0, 0, 0, + 115, 116, 0, 117, 118, 119, 120, 121, + 122, 123, 124, 0, 125, 0, 126, 0, + 127, 128, 0, 0, 0, 129, 0, 0, + 130, 0, 131, 0, 132, 133, 134, 135, + 0, 136, 0, 137, 138, 139, 140, 0, + 0, 141, 142, 143, 144, 145, 146, 147, + 148, 149, 150, 0, 151, 0, 152, 153, + 0, 154, 155, 156, 157 +}; + +static const uint16_t unicode_list_7[] = { + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x8, 0x9, + 0xd, 0xe, 0x15, 0x17, 0x18, 0x1a, 0x1c, 0x21, + 0x22, 0x24, 0x2a, 0x2b, 0x2c, 0x2f, 0x36, 0x3b, + 0x3d, 0x3e, 0x3f, 0x42, 0x45, 0x47, 0x49, 0x4a, + 0x4b, 0x4c, 0x4e, 0x52, 0x57, 0x59, 0x5a, 0x5c, + 0x5e, 0x5f, 0x61, 0x64, 0x65, 0x6b, 0x70, 0x71, + 0x77, 0x7c, 0x7f, 0x80, 0x81, 0x82, 0x85, 0x87, + 0x88, 0x89, 0x90, 0x91, 0x92, 0x93, 0x96, 0x97, + 0x99, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa2, 0xa3, + 0xa4, 0xa5, 0xa6, 0xaa, 0xac, 0xaf, 0xb3, 0xb4, + 0xb7, 0xb8, 0xba, 0xc2, 0xcc, 0xce, 0xd0, 0xd2, + 0xd4, 0xd6, 0xd8, 0xd9, 0xdf, 0xe0, 0xe4, 0xe7, + 0xe8, 0xec, 0xed, 0xee, 0xef, 0xf1, 0xf7, 0xf8, + 0xfa, 0xfc, 0xfe, 0xff, 0x100, 0x101, 0x103, 0x105, + 0x106, 0x107, 0x10a, 0x10d, 0x10e, 0x110, 0x112, 0x118, + 0x119, 0x11b, 0x11c, 0x123, 0x124, 0x127, 0x129, 0x12a, + 0x133, 0x136, 0x139, 0x13a, 0x13e, 0x13f, 0x142, 0x147, + 0x148, 0x14c, 0x14d, 0x14f, 0x151, 0x154, 0x156, 0x15a, + 0x167, 0x168, 0x169, 0x16f, 0x172, 0x173, 0x174, 0x177, + 0x17d, 0x17f, 0x183, 0x184, 0x185, 0x186, 0x187, 0x18c, + 0x191, 0x196, 0x197, 0x1a0, 0x1a1, 0x1a9, 0x1aa, 0x1ae, + 0x1be, 0x1ca, 0x1d2, 0x1d7, 0x1e5, 0x1ef, 0x1f2, 0x1f5, + 0x1f6, 0x1f8, 0x1f9, 0x1fa, 0x1fb, 0x1fc, 0x1fd, 0x1ff, + 0x206, 0x208, 0x20b, 0x20c, 0x20f, 0x210, 0x214, 0x215, + 0x218, 0x219, 0x21a, 0x21e, 0x21f, 0x221, 0x223, 0x224, + 0x225, 0x237, 0x23a, 0x23e, 0x243, 0x244, 0x245, 0x247, + 0x248, 0x249, 0x24a, 0x24b, 0x24e, 0x254, 0x255, 0x256, + 0x259, 0x25b, 0x25d, 0x262, 0x265, 0x267, 0x268, 0x269, + 0x26a, 0x26b, 0x26c, 0x272, 0x275, 0x276, 0x277, 0x278, + 0x279, 0x27a, 0x27b, 0x27c, 0x27f, 0x281, 0x283, 0x284, + 0x285, 0x286, 0x288, 0x28a, 0x28e, 0x291, 0x292, 0x296, + 0x297, 0x29d, 0x29e, 0x29f, 0x2a0, 0x2a1, 0x2a6, 0x2a7, + 0x2ad, 0x2ae, 0x2b6, 0x2bb, 0x2bc, 0x2bd, 0x2be, 0x2bf, + 0x2c1, 0x2c2, 0x2c4, 0x2c6, 0x2c8, 0x2c9, 0x2cd, 0x2cf, + 0x2d3, 0x2dd, 0x2de, 0x2e6, 0x2e9, 0x2ea, 0x2ed, 0x2ef, + 0x2f0, 0x2f3, 0x2f4, 0x2f5, 0x2f8, 0x2fa, 0x2fb, 0x2ff, + 0x308, 0x30a, 0x30f, 0x313, 0x314, 0x315, 0x318, 0x31b, + 0x31d, 0x321, 0x322, 0x326, 0x328, 0x32c, 0x330, 0x334, + 0x339, 0x33b, 0x33c, 0x33f, 0x345, 0x34b, 0x350, 0x35f, + 0x367, 0x368, 0x36c, 0x36f, 0x373, 0x379, 0x380, 0x386, + 0x387, 0x399, 0x39b, 0x39c, 0x39e, 0x3a0, 0x3a4, 0x3ad, + 0x3ae, 0x3b4, 0x3b5, 0x3b9, 0x3ba, 0x3c3, 0x3c4, 0x3d7, + 0x3dc, 0x3e0, 0x3ec, 0x3f0, 0x3ff, 0x406, 0x407, 0x409, + 0x40b, 0x40e, 0x411, 0x414, 0x41d, 0x41f, 0x422, 0x428, + 0x42a, 0x42f, 0x430, 0x431, 0x434, 0x435, 0x437, 0x43a, + 0x43f, 0x440, 0x442, 0x444, 0x445, 0x446, 0x448, 0x449, + 0x44a, 0x44c, 0x44f, 0x452, 0x453, 0x454, 0x455, 0x457, + 0x45c, 0x45d, 0x45f, 0x462, 0x463, 0x464, 0x466, 0x469, + 0x46a, 0x46c, 0x46f, 0x470, 0x471, 0x472, 0x473, 0x475, + 0x47b, 0x47d, 0x480, 0x48e, 0x48f, 0x491, 0x493, 0x494, + 0x498, 0x49c, 0x49d, 0x49e, 0x49f, 0x4a1, 0x4a2, 0x4a3, + 0x4a5, 0x4a8, 0x4ad, 0x4ae, 0x4b1, 0x4b2, 0x4b4, 0x4b9, + 0x4be, 0x4bf, 0x4c0, 0x4c3, 0x4c4, 0x4c5, 0x4c6, 0x4c9, + 0x4ca, 0x4cd, 0x4d4, 0x4d6, 0x4d9, 0x4e1, 0x4e5, 0x4e6, + 0x4eb, 0x4ec, 0x4ed, 0x4ee, 0x4ef, 0x4f2, 0x4f3, 0x4f5, + 0x4f7, 0x4f8, 0x4fe, 0x500, 0x503, 0x507, 0x514, 0x516, + 0x51a, 0x51c, 0x51e, 0x51f, 0x520, 0x521, 0x522, 0x523, + 0x524, 0x527, 0x52c, 0x52e, 0x533, 0x537, 0x53a, 0x53b, + 0x53e, 0x540, 0x544, 0x54c, 0x54d, 0x54f, 0x551, 0x557, + 0x55b, 0x561, 0x564, 0x565, 0x570, 0x575, 0x57d, 0x582, + 0x585, 0x58f, 0x590, 0x591, 0x592, 0x595, 0x59a, 0x5ad, + 0x5b5, 0x5b6, 0x5c5, 0x5cd, 0x5ce, 0x5d0, 0x5d3, 0x5d9, + 0x5dc, 0x5dd, 0x5e4, 0x5e7, 0x5ed, 0x5ef, 0x5f1, 0x5f3, + 0x5f7, 0x5fb, 0x5fc, 0x5fe, 0x601, 0x604, 0x606, 0x60c, + 0x624, 0x631, 0x632, 0x64d, 0x64f, 0x652, 0x65b, 0x66b, + 0x66c, 0x66e, 0x66f, 0x670, 0x672, 0x673, 0x674, 0x675, + 0x676, 0x677, 0x678, 0x67a, 0x67d, 0x67e, 0x67f, 0x680, + 0x681, 0x684, 0x685, 0x687, 0x68b, 0x68c, 0x68e, 0x690, + 0x695, 0x698, 0x69b, 0x69c, 0x69e, 0x69f, 0x6a0, 0x6a2, + 0x6a3, 0x6a4, 0x6a6, 0x6a7, 0x6aa, 0x6ae, 0x6b0, 0x6b2, + 0x6b3, 0x6b4, 0x6b5, 0x6b6, 0x6b7, 0x6b8, 0x6b9, 0x6bb, + 0x6bc, 0x6bd, 0x6be, 0x6bf, 0x6c0, 0x6c1, 0x6c5, 0x6c6, + 0x6cb, 0x6ce, 0x6cf, 0x6d0, 0x6d1, 0x6d3, 0x6d4, 0x6d8, + 0x6d9, 0x6da, 0x6dd, 0x6df, 0x6e0, 0x6e1, 0x6e2, 0x6e7, + 0x6eb, 0x6ed, 0x6ee, 0x6f8, 0x6f9, 0x6fa, 0x6fc, 0x6ff, + 0x700, 0x703, 0x709, 0x70b, 0x713, 0x714, 0x715, 0x716, + 0x717, 0x71a, 0x71c, 0x71f, 0x721, 0x724, 0x725, 0x72a, + 0x72c, 0x72f, 0x730, 0x731, 0x733, 0x735, 0x737, 0x738, + 0x73d, 0x73f, 0x740, 0x742, 0x747, 0x74c, 0x74f, 0x753, + 0x754, 0x755, 0x756, 0x757, 0x758, 0x759, 0x75a, 0x75b, + 0x75c, 0x75d, 0x760, 0x763, 0x764, 0x765, 0x766, 0x769, + 0x76a, 0x76b, 0x76c, 0x770, 0x774, 0x779, 0x77b, 0x77c, + 0x77e, 0x780, 0x781, 0x789, 0x78a, 0x78c, 0x794, 0x795, + 0x79a, 0x79c, 0x79d, 0x7a3, 0x7a7, 0x7a8, 0x7ab, 0x7ac, + 0x7af, 0x7b1, 0x7b2, 0x7b3, 0x7b4, 0x7b5, 0x7b6, 0x7b7, + 0x7bd, 0x7be, 0x7c4, 0x7c6, 0x7c7, 0x7c8, 0x7cc, 0x7ce, + 0x7d0, 0x7d2, 0x7d3, 0x7d8, 0x7da, 0x7dc, 0x7df, 0x7e6, + 0x7ed, 0x7f4, 0x7fc, 0x7ff, 0x800, 0x801, 0x803, 0x805, + 0x808, 0x80b, 0x816, 0x81d, 0x81e, 0x821, 0x822, 0x829, + 0x82f, 0x831, 0x836, 0x839, 0x83f, 0x841, 0x842, 0x844, + 0x848, 0x849, 0x84f, 0x858, 0x859, 0x862, 0x865, 0x866, + 0x867, 0x873, 0x876, 0x878, 0x884, 0x886, 0x887, 0x88a, + 0x88f, 0x89d, 0x8b4, 0x8b8, 0x8d2, 0x8e0, 0x8e8, 0x8f6, + 0x8f8, 0x8f9, 0x8fc, 0x8fd, 0x900, 0x901, 0x902, 0x903, + 0x904, 0x906, 0x909, 0x90a, 0x90c, 0x90d, 0x90e, 0x90f, + 0x912, 0x918, 0x919, 0x91c, 0x91d, 0x91e, 0x920, 0x921, + 0x923, 0x927, 0x92a, 0x92b, 0x92c, 0x92f, 0x930, 0x931, + 0x933, 0x934, 0x935, 0x936, 0x937, 0x938, 0x941, 0x942, + 0x948, 0x949, 0x94c, 0x952, 0x953, 0x956, 0x957, 0x958, + 0x95d, 0x95f, 0x960, 0x967, 0x96f, 0x970, 0x976, 0x979, + 0x97c, 0x97d, 0x98d, 0x98e, 0x98f, 0x991, 0x993, 0x995, + 0x996, 0x997, 0x998, 0x99a, 0x99b, 0x99f, 0x9a1, 0x9a2, + 0x9a5, 0x9a6, 0x9aa, 0x9ab, 0x9ac, 0x9ae, 0x9af, 0x9b0, + 0x9b1, 0x9b2, 0x9b4, 0x9b5, 0x9b7, 0x9b9, 0x9ba, 0x9bb, + 0x9c0, 0x9c1, 0x9c2, 0x9c8, 0x9ce, 0x9d0, 0x9d1, 0x9d2, + 0x9d3, 0x9d4, 0x9d9, 0x9e4, 0x9e5, 0x9ec, 0x9ed, 0x9ee, + 0x9f1, 0x9f6, 0xa03, 0xa05, 0xa0f, 0xa11, 0xa12, 0xa15, + 0xa19, 0xa1a, 0xa1b, 0xa1c, 0xa1d, 0xa1e, 0xa1f, 0xa23, + 0xa25, 0xa26, 0xa2a, 0xa2c, 0xa2e, 0xa30, 0xa32, 0xa33, + 0xa36, 0xa3a, 0xa3b, 0xa40, 0xa41, 0xa42, 0xa44, 0xa45, + 0xa48, 0xa4a, 0xa4c, 0xa54, 0xa55, 0xa57, 0xa5b, 0xa6b, + 0xa6d, 0xa6e, 0xa70, 0xa71, 0xa72, 0xa73, 0xa78, 0xa7c, + 0xa7d, 0xa7f, 0xa81, 0xa84, 0xa85, 0xa87, 0xa88, 0xa8b, + 0xa8c, 0xa8e, 0xa92, 0xa94, 0xa96, 0xa97, 0xa9b, 0xa9c, + 0xa9d, 0xa9f, 0xaa0, 0xaa2, 0xaa3, 0xaa4, 0xaa5, 0xaa6, + 0xaa7, 0xaab, 0xaad, 0xab0, 0xab2, 0xab3, 0xab4, 0xab7, + 0xabc, 0xac3, 0xac5, 0xac8, 0xac9, 0xad0, 0xad2, 0xad7, + 0xad8, 0xade, 0xadf, 0xae0, 0xae1, 0xae4, 0xae7, 0xae8, + 0xaea, 0xaeb, 0xaec, 0xaed, 0xaf1, 0xaf2, 0xaf3, 0xaf4, + 0xaf8, 0xafb, 0xafc, 0xaff, 0xb02, 0xb05, 0xb06, 0xb08, + 0xb09, 0xb0c, 0xb10, 0xb13, 0xb16, 0xb18, 0xb19, 0xb1a, + 0xb1b, 0xb1c, 0xb1d, 0xb1e, 0xb1f, 0xb20, 0xb21, 0xb25, + 0xb28, 0xb29, 0xb2a, 0xb2d, 0xb2f, 0xb30, 0xb31, 0xb34, + 0xb36, 0xb37, 0xb38, 0xb3b, 0xb3c, 0xb40, 0xb41, 0xb42, + 0xb43, 0xb44, 0xb45, 0xb46, 0xb4a, 0xb50, 0xb56, 0xb57, + 0xb5a, 0xb5c, 0xb5d, 0xb5e, 0xb66, 0xb68, 0xb6b, 0xb6d, + 0xb70, 0xb74, 0xb75, 0xb78, 0xb7d, 0xb7e, 0xb7f, 0xb82, + 0xb83, 0xb84, 0xb85, 0xb86, 0xb87, 0xb88, 0xb8a, 0xb8b, + 0xb8e, 0xb91, 0xb93, 0xb94, 0xb95, 0xb96, 0xb97, 0xb98, + 0xb9a, 0xb9e, 0xb9f, 0xba4, 0xba7, 0xba8, 0xbad, 0xbaf, + 0xbb1, 0xbb5, 0xbb6, 0xbb8, 0xbba, 0xbbb, 0xbbe, 0xbc1, + 0xbc3, 0xbc6, 0xbc7, 0xbc8, 0xbca, 0xbcc, 0xbcd, 0xbcf, + 0xbd3, 0xbd6, 0xbd7, 0xbe0, 0xbe1, 0xbe5, 0xbe6, 0xbec, + 0xbf0, 0xbf3, 0xbf5, 0xbf7, 0xbf8, 0xbfa, 0xbfb, 0xc01, + 0xc02, 0xc03, 0xc04, 0xc06, 0xc07, 0xc08, 0xc09, 0xc0a, + 0xc0b, 0xc0e, 0xc0f, 0xc11, 0xc14, 0xc15, 0xc1b, 0xc1c, + 0xc21, 0xc23, 0xc24, 0xc28, 0xc29, 0xc2a, 0xc30, 0xc35, + 0xc3a, 0xc3b, 0xc3e, 0xc3f, 0xc41, 0xc42, 0xc46, 0xc5a, + 0xc63, 0xc65, 0xc67, 0xc69, 0xc6c, 0xc70, 0xc78, 0xc7d, + 0xc82, 0xc83, 0xc8b, 0xc90, 0xc92, 0xca6, 0xca9, 0xcaf, + 0xcb8, 0xcc2, 0xcc3, 0xcc4, 0xcc7, 0xcd2, 0xcd9, 0xcdd, + 0xce3, 0xce5, 0xce6, 0xcec, 0xced, 0xcef, 0xd01, 0xd10, + 0xd1a, 0xd21, 0xd23, 0xd25, 0xd26, 0xd27, 0xd28, 0xd29, + 0xd2a, 0xd2b, 0xd2c, 0xd2d, 0xd30, 0xd31, 0xd32, 0xd33, + 0xd35, 0xd36, 0xd3a, 0xd3c, 0xd3d, 0xd3f, 0xd40, 0xd45, + 0xd47, 0xd49, 0xd4e, 0xd4f, 0xd52, 0xd58, 0xd59, 0xd5a, + 0xd5b, 0xd5c, 0xd5e, 0xd62, 0xd63, 0xd64, 0xd66, 0xd67, + 0xd68, 0xd69, 0xd6c, 0xd6d, 0xd6e, 0xd6f, 0xd73, 0xd76, + 0xd7e, 0xd81, 0xd82, 0xd84, 0xd85, 0xd86, 0xd87, 0xd88, + 0xd89, 0xd8a, 0xd8b, 0xd8e, 0xd91, 0xd94, 0xd97, 0xd99, + 0xd9a, 0xd9b, 0xd9f, 0xda4, 0xda5, 0xdac, 0xdad, 0xdae, + 0xdb0, 0xdb1, 0xdb2, 0xdb3, 0xdb5, 0xdb6, 0xdba, 0xdbb, + 0xdbc, 0xdbd, 0xdbf, 0xdc0, 0xdc3, 0xdc6, 0xdc7, 0xdcc, + 0xdd0, 0xdd4, 0xdd6, 0xdd7, 0xdd8, 0xdda, 0xddd, 0xddf, + 0xde0, 0xde1, 0xde2, 0xde3, 0xde4, 0xde5, 0xde7, 0xde8, + 0xde9, 0xdeb, 0xded, 0xdee, 0xdef, 0xdf1, 0xdf2, 0xdf3, + 0xdf4, 0xdf5, 0xdf6, 0xdf7, 0xdfa, 0xdfd, 0xdfe, 0xe00, + 0xe01, 0xe02, 0xe03, 0xe04, 0xe07, 0xe08, 0xe09, 0xe0a, + 0xe0c, 0xe0e, 0xe0f, 0xe11, 0xe12, 0xe17, 0xe18, 0xe19, + 0xe1a, 0xe1c, 0xe1d, 0xe22, 0xe23, 0xe24, 0xe29, 0xe2c, + 0xe31, 0xe35, 0xe36, 0xe38, 0xe39, 0xe3a, 0xe3b, 0xe3c, + 0xe3d, 0xe3e, 0xe3f, 0xe40, 0xe43, 0xe45, 0xe46, 0xe4a, + 0xe4d, 0xe54, 0xe55, 0xe58, 0xe5d, 0xe5e, 0xe60, 0xe61, + 0xe64, 0xe66, 0xe67, 0xe68, 0xe69, 0xe6a, 0xe6b, 0xe70, + 0xe79, 0xe7a, 0xe7c, 0xe7d, 0xe7e, 0xe82, 0xe84, 0xe88, + 0xe89, 0xe8c, 0xe91, 0xe92, 0xe95, 0xe96, 0xe9b, 0xe9d, + 0xea2, 0xea3, 0xea4, 0xea5, 0xea7, 0xea9, 0xeaa, 0xeab, + 0xead, 0xeb1, 0xeb3, 0xebb, 0xebd, 0xebe, 0xec0, 0xec2, + 0xec3, 0xec4, 0xec5, 0xec7, 0xec8, 0xec9, 0xecb, 0xece, + 0xecf, 0xed2, 0xed3, 0xed5, 0xed7, 0xed9, 0xedf, 0xee1, + 0xee4, 0xee8, 0xee9, 0xeea, 0xeeb, 0xeed, 0xef1, 0xef9, + 0xefb, 0xefc, 0xefe, 0xf04, 0xf05, 0xf08, 0xf0d, 0xf0f, + 0xf11, 0xf13, 0xf18, 0xf1a, 0xf1b, 0xf1c, 0xf1d, 0xf20, + 0xf26, 0xf27, 0xf2a, 0xf2b, 0xf2e, 0xf2f, 0xf36, 0xf37, + 0xf39, 0xf3b, 0xf3c, 0xf41, 0xf45, 0xf47, 0xf48, 0xf4f, + 0xf55, 0xf58, 0xf5a, 0xf5c, 0xf5f, 0xf60, 0xf61, 0xf62, + 0xf63, 0xf65, 0xf6d, 0xf6f, 0xf73, 0xf79, 0xf82, 0xf84, + 0xf88, 0xf93, 0xf94, 0xf95, 0xf9d, 0xf9f, 0xfa0, 0xfa2, + 0xfac, 0xfad, 0xfb0, 0xfb1, 0xfb4, 0xfb9, 0xfbf, 0xfc4, + 0xfc7, 0xfc8, 0xfc9, 0xfcb, 0xfd0, 0xfd2, 0xfd3, 0xfd5, + 0xfd7, 0xfdb, 0xfdd, 0xfe0, 0xfe8, 0xfe9, 0xfeb, 0xfed, + 0xff2, 0xff3, 0xff9, 0xffd, 0xfff, 0x1001, 0x101b, 0x1024, + 0x102d, 0x1033, 0x1040, 0x1046, 0x1049, 0x104a, 0x104f, 0x1050, + 0x1051, 0x1053, 0x1054, 0x1056, 0x1059, 0x105a, 0x1060, 0x1063, + 0x1064, 0x1067, 0x106a, 0x106c, 0x1070, 0x1071, 0x1074, 0x1076, + 0x1078, 0x1079, 0x107d, 0x107e, 0x1081, 0x1086, 0x1087, 0x108b, + 0x108d, 0x108f, 0x1092, 0x10a2, 0x10a6, 0x10a7, 0x10ab, 0x10ac, + 0x10ae, 0x10b2, 0x10b4, 0x10b6, 0x10b7, 0x10ba, 0x10bc, 0x10bf, + 0x10c0, 0x10c2, 0x10c4, 0x10c6, 0x10c8, 0x10ca, 0x10cb, 0x10d4, + 0x10d7, 0x10d8, 0x10dc, 0x10de, 0x10df, 0x10e0, 0x10e1, 0x10e6, + 0x10e7, 0x10e9, 0x10ea, 0x10ed, 0x10f1, 0x10f2, 0x10fb, 0x10fd, + 0x1100, 0x1101, 0x1102, 0x1103, 0x1104, 0x1107, 0x1108, 0x1109, + 0x110a, 0x110b, 0x110c, 0x1111, 0x1112, 0x1115, 0x111b, 0x111d, + 0x111e, 0x1121, 0x1125, 0x1127, 0x1129, 0x112a, 0x112e, 0x112f, + 0x1130, 0x1134, 0x1138, 0x113a, 0x113b, 0x1140, 0x1142, 0x1143, + 0x1148, 0x114a, 0x114c, 0x114f, 0x1150, 0x1151, 0x1157, 0x1159, + 0x115c, 0x115e, 0x1166, 0x1167, 0x116a, 0x116d, 0x116e, 0x116f, + 0x1170, 0x1171, 0x1172, 0x1175, 0x117a, 0x117c, 0x117f, 0x1181, + 0x1183, 0x1189, 0x118a, 0x118b, 0x118f, 0x1191, 0x1192, 0x1195, + 0x1199, 0x119d, 0x119f, 0x11a2, 0x11a7, 0x11ac, 0x11b1, 0x11b2, + 0x11b8, 0x11c2, 0x11c3, 0x11c9, 0x11cf, 0x11d4, 0x11d9, 0x11f4, + 0x11f6, 0x11f7, 0x11f8, 0x1201, 0x1204, 0x120b, 0x120d, 0x120e, + 0x120f, 0x1212, 0x1214, 0x1217, 0x1219, 0x121a, 0x121b, 0x1223, + 0x1224, 0x1225, 0x1226, 0x1228, 0x122b, 0x122f, 0x1230, 0x1232, + 0x1236, 0x1238, 0x123a, 0x1241, 0x1243, 0x1245, 0x1246, 0x1247, + 0x1248, 0x124a, 0x124c, 0x124f, 0x1250, 0x1255, 0x1258, 0x125b, + 0x125d, 0x125e, 0x1261, 0x1263, 0x1264, 0x1267, 0x1269, 0x126a, + 0x126b, 0x126c, 0x126e, 0x1271, 0x1277, 0x1279, 0x127a, 0x127b, + 0x127c, 0x1280, 0x1283, 0x1284, 0x1285, 0x1288, 0x128a, 0x128b, + 0x128d, 0x128e, 0x1290, 0x1292, 0x1297, 0x1299, 0x129a, 0x129c, + 0x129f, 0x12a2, 0x12a4, 0x12a6, 0x12ab, 0x12b0, 0x12b2, 0x12b3, + 0x12b5, 0x12b7, 0x12b8, 0x12b9, 0x12bd, 0x12be, 0x12c0, 0x12c2, + 0x12c3, 0x12c5, 0x12c6, 0x12c8, 0x12ca, 0x12cb, 0x12ce, 0x12d0, + 0x12d1, 0x12d2, 0x12d3, 0x12dc, 0x12de, 0x12df, 0x12ea, 0x12eb, + 0x12ec, 0x12ed, 0x12ee, 0x12ef, 0x12f3, 0x12f4, 0x12f5, 0x12f7, + 0x12f8, 0x12f9, 0x12fb, 0x12fd, 0x1300, 0x1304, 0x1307, 0x130a, + 0x130b, 0x130c, 0x130e, 0x130f, 0x1318, 0x131a, 0x131b, 0x1320, + 0x1322, 0x1323, 0x1324, 0x1325, 0x1326, 0x1327, 0x1329, 0x132a, + 0x132c, 0x132e, 0x1331, 0x1332, 0x1338, 0x133c, 0x1344, 0x1345, + 0x134d, 0x134e, 0x1352, 0x1353, 0x1354, 0x1357, 0x1358, 0x1359, + 0x135b, 0x135c, 0x135d, 0x135e, 0x135f, 0x1360, 0x1361, 0x1363, + 0x1364, 0x1365, 0x1367, 0x1369, 0x136b, 0x136c, 0x136e, 0x136f, + 0x1370, 0x137b, 0x137c, 0x137d, 0x137e, 0x137f, 0x1380, 0x1381, + 0x1382, 0x1383, 0x1384, 0x1386, 0x138f, 0x1391, 0x1392, 0x139c, + 0x139e, 0x13a0, 0x13a1, 0x13aa, 0x13ae, 0x13b2, 0x13bd, 0x13c1, + 0x13c2, 0x13c3, 0x13c8, 0x13ca, 0x13cb, 0x13ce, 0x13d0, 0x13db, + 0x13dd, 0x13e4, 0x13e6, 0x13e8, 0x13ed, 0x13f0, 0x13f3, 0x13f5, + 0x13fb, 0x13fe, 0x1409, 0x140b, 0x140c, 0x1410, 0x1414, 0x1415, + 0x1417, 0x141c, 0x1420, 0x1426, 0x1428, 0x1429, 0x142b, 0x142d, + 0x143a, 0x143b, 0x143f, 0x1448, 0x144b, 0x144f, 0x1454, 0x1458, + 0x145a, 0x145d, 0x146f, 0x1472, 0x1475, 0x1478, 0x1479, 0x147b, + 0x147e, 0x1481, 0x1486, 0x1489, 0x148c, 0x1492, 0x1493, 0x1494, + 0x1497, 0x149b, 0x149d, 0x149f, 0x14a1, 0x14a2, 0x14a3, 0x14a4, + 0x14a8, 0x14af, 0x14b0, 0x14b3, 0x14b6, 0x14b7, 0x14c2, 0x14c3, + 0x14c6, 0x14c8, 0x14cc, 0x14cf, 0x14d2, 0x14d6, 0x14dc, 0x14e5, + 0x14e7, 0x14e9, 0x14eb, 0x14ef, 0x14f6, 0x14fa, 0x14fb, 0x1508, + 0x150d, 0x1518, 0x151a, 0x1525, 0x1532, 0x1533, 0x153a, 0x153c, + 0x1543, 0x1545, 0x154a, 0x154c, 0x1550, 0x1558, 0x1559, 0x155f, + 0x1562, 0x156b, 0x1573, 0x1574, 0x1576, 0x157c, 0x1580, 0x158c, + 0x1594, 0x1597, 0x159b, 0x159f, 0x15a9, 0x15ab, 0x15ac, 0x15b2, + 0x15bb, 0x15c4, 0x15c6, 0x15c7, 0x163b, 0x163c, 0x163d, 0x163e, + 0x163f, 0x1642, 0x164d, 0x1652, 0x1654, 0x1655, 0x1659, 0x165e, + 0x1661, 0x1662, 0x1664, 0x1667, 0x1674, 0x167d, 0x167e, 0x167f, + 0x1680, 0x1681, 0x1682, 0x1685, 0x1694, 0x1696, 0x1697, 0x169c, + 0x169d, 0x169e, 0x169f, 0x16a1, 0x16a2, 0x16a4, 0x16a5, 0x16a6, + 0x16a8, 0x16ad, 0x16ae, 0x16b1, 0x16b5, 0x16b6, 0x16bc, 0x16c5, + 0x16ce, 0x16cf, 0x16d0, 0x16d2, 0x16da, 0x16dc, 0x16dd, 0x16e0, + 0x16e6, 0x16e8, 0x16ea, 0x16ed, 0x16ee, 0x16ef, 0x16f0, 0x16f1, + 0x16f2, 0x16f4, 0x16f6, 0x16fc, 0x1705, 0x1706, 0x170a, 0x170e, + 0x1710, 0x1714, 0x1718, 0x1720, 0x1721, 0x1722, 0x1728, 0x172a, + 0x172b, 0x172c, 0x172e, 0x172f, 0x1730, 0x1731, 0x1733, 0x1734, + 0x1735, 0x1736, 0x173a, 0x173c, 0x173d, 0x173f, 0x1741, 0x1742, + 0x1743, 0x1744, 0x1745, 0x1749, 0x174a, 0x174b, 0x174d, 0x174f, + 0x1750, 0x1753, 0x1758, 0x175b, 0x175c, 0x175d, 0x1761, 0x1762, + 0x1764, 0x1765, 0x176b, 0x176f, 0x1770, 0x1772, 0x1776, 0x1777, + 0x1778, 0x1779, 0x177a, 0x177b, 0x177c, 0x177f, 0x1783, 0x1784, + 0x1785, 0x178b, 0x178d, 0x178f, 0x1791, 0x1794, 0x1798, 0x1799, + 0x179c, 0x179d, 0x179e, 0x17a0, 0x17a1, 0x17a3, 0x17a4, 0x17a7, + 0x17aa, 0x17ab, 0x17ae, 0x17af, 0x17b4, 0x17b6, 0x17ba, 0x17bc, + 0x17be, 0x17bf, 0x17c0, 0x17c1, 0x17c2, 0x17c4, 0x17c5, 0x17c6, + 0x17c8, 0x17c9, 0x17cc, 0x17cd, 0x17ce, 0x17d3, 0x17d4, 0x17d6, + 0x17d7, 0x17d8, 0x17d9, 0x17da, 0x17df, 0x17e0, 0x17e4, 0x17e5, + 0x17e7, 0x17eb, 0x17ee, 0x17ef, 0x17f0, 0x17f1, 0x17f2, 0x17f6, + 0x17f9, 0x17fb, 0x17fc, 0x17fd, 0x17fe, 0x1800, 0x1803, 0x1805, + 0x1806, 0x1809, 0x180a, 0x180b, 0x180c, 0x180e, 0x1810, 0x1811, + 0x1812, 0x1813, 0x1815, 0x1816, 0x1817, 0x1818, 0x1819, 0x181c, + 0x181f, 0x1822, 0x1826, 0x1827, 0x1829, 0x182d, 0x1832, 0x1834, + 0x1835, 0x1836, 0x1839, 0x1840, 0x1842, 0x1845, 0x1846, 0x1849, + 0x184c, 0x184d, 0x184e, 0x1850, 0x1854, 0x1856, 0x1857, 0x1858, + 0x1859, 0x185c, 0x185e, 0x1860, 0x1861, 0x1862, 0x1863, 0x1865, + 0x1866, 0x1868, 0x1869, 0x186a, 0x186c, 0x186d, 0x186e, 0x186f, + 0x1874, 0x1875, 0x1877, 0x1879, 0x187b, 0x187e, 0x1881, 0x1884, + 0x1885, 0x1889, 0x188a, 0x188f, 0x1892, 0x1893, 0x1897, 0x189d, + 0x18a0, 0x18a3, 0x18a4, 0x18a7, 0x18a9, 0x18ac, 0x18ae, 0x18af, + 0x18b0, 0x18b6, 0x18b8, 0x18b9, 0x18ba, 0x18bb, 0x18bc, 0x18be, + 0x18bf, 0x18c1, 0x18c2, 0x18c3, 0x18c4, 0x18c5, 0x18c6, 0x18c9, + 0x18ca, 0x18cd, 0x18d0, 0x18d3, 0x18da, 0x18db, 0x18df, 0x18e0, + 0x18e1, 0x18e2, 0x18e6, 0x18e7, 0x18ec, 0x18f1, 0x18f3, 0x18f4, + 0x18f8, 0x18f9, 0x18fb, 0x18fc, 0x18ff, 0x1901, 0x1906, 0x1907, + 0x1909, 0x190c, 0x190e, 0x1912, 0x1914, 0x1916, 0x1917, 0x1920, + 0x1925, 0x1927, 0x1928, 0x1929, 0x192b, 0x192c, 0x192f, 0x1931, + 0x1932, 0x1935, 0x1938, 0x193b, 0x193c, 0x193e, 0x193f, 0x1940, + 0x1944, 0x1946, 0x1948, 0x194a, 0x194d, 0x194f, 0x1953, 0x1955, + 0x195e, 0x195f, 0x1968, 0x1969, 0x196e, 0x196f, 0x1971, 0x1973, + 0x1976, 0x197a, 0x1986, 0x1989, 0x1999, 0x199a, 0x199e, 0x19a0, + 0x19a1, 0x19a4, 0x19aa, 0x19ab, 0x19b3, 0x19b7, 0x19ba, 0x19bd, + 0x19c0, 0x19c2, 0x19c5, 0x19ca, 0x19cc, 0x19cd, 0x19cf, 0x19d1, + 0x19d2, 0x19d5, 0x19d6, 0x19d8, 0x19dc, 0x19dd, 0x19e2, 0x19e6, + 0x19ea, 0x19ec, 0x19ee, 0x19ef, 0x19f0, 0x19f2, 0x19f5, 0x19f9, + 0x19fa, 0x19fb, 0x19fc, 0x19fd, 0x19ff, 0x1a00, 0x1a01, 0x1a03, + 0x1a04, 0x1a0f, 0x1a14, 0x1a1d, 0x1a21, 0x1a24, 0x1a2a, 0x1a2e, + 0x1a2f, 0x1a30, 0x1a3b, 0x1a3f, 0x1a44, 0x1a45, 0x1a46, 0x1a48, + 0x1a4a, 0x1a4c, 0x1a4e, 0x1a51, 0x1a59, 0x1a61, 0x1a62, 0x1a66, + 0x1a68, 0x1a73, 0x1a77, 0x1a79, 0x1a7d, 0x1a81, 0x1a88, 0x1a89, + 0x1a8d, 0x1a8f, 0x1a93, 0x1a95, 0x1a97, 0x1a9f, 0x1aa3, 0x1aa4, + 0x1aa7, 0x1aa8, 0x1aa9, 0x1ab7, 0x1abc, 0x1ac2, 0x1ace, 0x1ad1, + 0x1ad4, 0x1adb, 0x1add, 0x1ae4, 0x1aec, 0x1aed, 0x1af9, 0x1afb, + 0x1afc, 0x1b09, 0x1b0a, 0x1b2c, 0x1b35, 0x1b36, 0x1b3e, 0x1b50, + 0x1b54, 0x1b67, 0x1b6a, 0x1b79, 0x1b86, 0x1b87, 0x1b88, 0x1b8a, + 0x1b8b, 0x1b90, 0x1b91, 0x1b93, 0x1b97, 0x1b99, 0x1b9a, 0x1b9b, + 0x1ba0, 0x1ba4, 0x1ba5, 0x1ba9, 0x1bad, 0x1baf, 0x1bb0, 0x1bb1, + 0x1bb4, 0x1bb7, 0x1bb8, 0x1bc6, 0x1bc7, 0x1bc8, 0x1bc9, 0x1bca, + 0x1bcc, 0x1bce, 0x1bd2, 0x1bd3, 0x1bd4, 0x1bd6, 0x1bd7, 0x1bd8, + 0x1bdb, 0x1bdc, 0x1bdd, 0x1bde, 0x1be3, 0x1be5, 0x1bf3, 0x1bf4, + 0x1bf6, 0x1bfa, 0x1bff, 0x1c01, 0x1c02, 0x1c03, 0x1c04, 0x1c06, + 0x1c07, 0x1c08, 0x1c0a, 0x1c12, 0x1c14, 0x1c18, 0x1c24, 0x1c25, + 0x1c2b, 0x1c2e, 0x1c30, 0x1c31, 0x1c33, 0x1c34, 0x1c35, 0x1c41, + 0x1c4a, 0x1c4b, 0x1c4c, 0x1c51, 0x1c60, 0x1c65, 0x1c67, 0x1c69, + 0x1c77, 0x1c79, 0x1c7f, 0x1c81, 0x1c82, 0x1c83, 0x1c89, 0x1c8d, + 0x1c8e, 0x1c93, 0x1c95, 0x1c98, 0x1c9f, 0x1ca5, 0x1caa, 0x1caf, + 0x1cb3, 0x1cb4, 0x1cba, 0x1cbb, 0x1cc3, 0x1cc7, 0x1cce, 0x1cd0, + 0x1cd4, 0x1cde, 0x1ce9, 0x1cef, 0x1cf0, 0x1cfb, 0x1d00, 0x1d02, + 0x1d09, 0x1d14, 0x1d21, 0x1d38, 0x1d43, 0x1d45, 0x1d47, 0x1d4b, + 0x1d4c, 0x1d50, 0x1d51, 0x1d52, 0x1d53, 0x1d54, 0x1d56, 0x1d58, + 0x1d5a, 0x1d62, 0x1d63, 0x1d67, 0x1d68, 0x1d6d, 0x1d71, 0x1d74, + 0x1d76, 0x1d78, 0x1d7a, 0x1d7c, 0x1d7d, 0x1d81, 0x1d82, 0x1d84, + 0x1d89, 0x1d8a, 0x1d8d, 0x1d90, 0x1d94, 0x1d95, 0x1d99, 0x1d9a, + 0x1d9b, 0x1d9c, 0x1d9f, 0x1da5, 0x1da6, 0x1da8, 0x1daa, 0x1dad, + 0x1dba, 0x1dc7, 0x1dc8, 0x1dca, 0x1dcb, 0x1dcf, 0x1dd1, 0x1dd2, + 0x1dd3, 0x1dd4, 0x1ddc, 0x1ddd, 0x1dde, 0x1ddf, 0x1de3, 0x1de8, + 0x1de9, 0x1deb, 0x1ded, 0x1df2, 0x1df4, 0x1df9, 0x1dfb, 0x1dfc, + 0x1e03, 0x1e04, 0x1e07, 0x1e08, 0x1e09, 0x1e0a, 0x1e0b, 0x1e0c, + 0x1e0d, 0x1e0e, 0x1e0f, 0x1e12, 0x1e13, 0x1e15, 0x1e16, 0x1e17, + 0x1e1c, 0x1e1e, 0x1e25, 0x1e29, 0x1e2e, 0x1e30, 0x1e31, 0x1e32, + 0x1e36, 0x1e37, 0x1e38, 0x1e39, 0x1e3c, 0x1e3d, 0x1e40, 0x1e44, + 0x1e45, 0x1e46, 0x1e47, 0x1e49, 0x1e4c, 0x1e4f, 0x1e52, 0x1e53, + 0x1e54, 0x1e59, 0x1e5a, 0x1e68, 0x1e6b, 0x1e6d, 0x1e72, 0x1e7b, + 0x1e87, 0x1e88, 0x1e8a, 0x1e99, 0x1e9f, 0x1ea2, 0x1ea4, 0x1ea6, + 0x1ea9, 0x1eac, 0x1eb1, 0x1eb6, 0x1eba, 0x1ebd, 0x1ec4, 0x1ec6, + 0x1ec9, 0x1eca, 0x1ecb, 0x1ecd, 0x1ece, 0x1ed2, 0x1ed5, 0x1ed6, + 0x1edb, 0x1edd, 0x1ee3, 0x1ee4, 0x1ee5, 0x1ee8, 0x1eea, 0x1eeb, + 0x1eec, 0x1ef4, 0x1ef9, 0x1efb, 0x1f00, 0x1f02, 0x1f04, 0x1f08, + 0x1f0d, 0x1f1e, 0x1f20, 0x1f21, 0x1f24, 0x1f25, 0x1f2a, 0x1f2b, + 0x1f35, 0x1f36, 0x1f3d, 0x1f40, 0x1f41, 0x1f43, 0x1f45, 0x1f47, + 0x1f49, 0x1f4b, 0x1f4e, 0x1f4f, 0x1f50, 0x1f51, 0x1f57, 0x1f5c, + 0x1f70, 0x1f72, 0x1f74, 0x1f75, 0x1f76, 0x1f77, 0x1f79, 0x1f7a, + 0x1f88, 0x1f8b, 0x1f91, 0x1f92, 0x1f99, 0x1f9b, 0x1f9c, 0x1f9e, + 0x1fa2, 0x1fa6, 0x1fa9, 0x1fab, 0x1fb7, 0x1fb9, 0x1fc2, 0x1fc3, + 0x1fc4, 0x1fd5, 0x1fed, 0x1ff7, 0x1ff9, 0x1ffb, 0x1ffd, 0x1ffe, + 0x1fff, 0x2001, 0x2009, 0x200a, 0x200f, 0x2011, 0x2012, 0x201a, + 0x201f, 0x2028, 0x202a, 0x202c, 0x202e, 0x2033, 0x2034, 0x2035, + 0x2037, 0x203a, 0x2040, 0x2043, 0x2044, 0x2046, 0x2047, 0x2048, + 0x204a, 0x204b, 0x204c, 0x204d, 0x204e, 0x2050, 0x2052, 0x2053, + 0x2055, 0x2056, 0x2059, 0x205b, 0x2060, 0x2063, 0x2066, 0x2067, + 0x2069, 0x206a, 0x206f, 0x2074, 0x2075, 0x2076, 0x2077, 0x2080, + 0x2081, 0x2085, 0x208d, 0x208f, 0x2093, 0x2094, 0x209a, 0x209e, + 0x20a1, 0x20a6, 0x20aa, 0x20ac, 0x20ad, 0x20af, 0x20b1, 0x20b2, + 0x20b4, 0x20b5, 0x20b8, 0x20ba, 0x20bb, 0x20bc, 0x20be, 0x20bf, + 0x20c0, 0x20c6, 0x20c7, 0x20c9, 0x20ca, 0x20cb, 0x20cc, 0x20cd, + 0x20ce, 0x20cf, 0x20d0, 0x20d3, 0x20d4, 0x20d7, 0x20d8, 0x20d9, + 0x20dd, 0x20de, 0x20df, 0x20e0, 0x20e2, 0x20e3, 0x20e4, 0x20e5, + 0x20e8, 0x20ed, 0x20ef, 0x20f0, 0x20f1, 0x20f3, 0x20f6, 0x20f9, + 0x20fd, 0x20fe, 0x20ff, 0x2101, 0x2102, 0x2103, 0x2105, 0x2106, + 0x210b, 0x210c, 0x210f, 0x2114, 0x2117, 0x211a, 0x211b, 0x211c, + 0x211e, 0x2120, 0x2125, 0x2127, 0x212b, 0x2130, 0x2132, 0x2133, + 0x2134, 0x2136, 0x213a, 0x213b, 0x213d, 0x213f, 0x2140, 0x2141, + 0x2144, 0x2145, 0x2146, 0x2148, 0x214b, 0x214e, 0x214f, 0x2150, + 0x2153, 0x2157, 0x2159, 0x215a, 0x215b, 0x215e, 0x2167, 0x2168, + 0x216f, 0x2171, 0x2177, 0x2179, 0x217e, 0x2186, 0x218a, 0x2193, + 0x2196, 0x2198, 0x2199, 0x219d, 0x219f, 0x21a1, 0x21a2, 0x21a3, + 0x21a6, 0x21a9, 0x21ac, 0x21ae, 0x21b1, 0x21b4, 0x21bf, 0x21c9, + 0x21cc, 0x21cd, 0x21cf, 0x21da, 0x21dd, 0x21e0, 0x21e1, 0x21e3, + 0x21e5, 0x21e8, 0x21e9, 0x21ea, 0x21eb, 0x21ec, 0x21ed, 0x21ef, + 0x21f1, 0x21f2, 0x21f3, 0x21f6, 0x21fa, 0x2200, 0x2209, 0x220a, + 0x220c, 0x220d, 0x220f, 0x2213, 0x2214, 0x2217, 0x2219, 0x221c, + 0x221f, 0x2222, 0x2223, 0x2224, 0x2226, 0x2228, 0x2234, 0x2235, + 0x223a, 0x223b, 0x223d, 0x2241, 0x2243, 0x2244, 0x2248, 0x224a, + 0x2250, 0x2251, 0x2252, 0x2253, 0x2255, 0x2257, 0x225b, 0x225c, + 0x225e, 0x2262, 0x226b, 0x226c, 0x2275, 0x2276, 0x227c, 0x227d, + 0x227e, 0x2280, 0x2281, 0x2283, 0x2286, 0x2287, 0x2294, 0x2298, + 0x2299, 0x229a, 0x229b, 0x229f, 0x22a0, 0x22a7, 0x22a8, 0x22a9, + 0x22ac, 0x22ad, 0x22ba, 0x22bb, 0x22bd, 0x22c0, 0x22c2, 0x22c4, + 0x22c5, 0x22c7, 0x22cb, 0x22ce, 0x22d0, 0x22d6, 0x22d8, 0x22da, + 0x22e8, 0x22f2, 0x22f6, 0x22f7, 0x22fd, 0x22fe, 0x2300, 0x2302, + 0x2304, 0x2306, 0x2307, 0x2308, 0x2309, 0x230e, 0x2311, 0x2313, + 0x2318, 0x2319, 0x231a, 0x231b, 0x231c, 0x231d, 0x2324, 0x2327, + 0x2328, 0x232c, 0x232d, 0x232f, 0x2331, 0x2332, 0x2333, 0x2335, + 0x2337, 0x2338, 0x233a, 0x233e, 0x2340, 0x2341, 0x2342, 0x2344, + 0x2347, 0x2348, 0x234b, 0x234f, 0x2352, 0x2353, 0x2354, 0x2355, + 0x2356, 0x2357, 0x2359, 0x235b, 0x2360, 0x2362, 0x2367, 0x2369, + 0x236b, 0x236d, 0x2370, 0x2371, 0x2372, 0x2378, 0x2385, 0x2386, + 0x2387, 0x2388, 0x2389, 0x2392, 0x2397, 0x23a2, 0x23a4, 0x23a7, + 0x23a8, 0x23a9, 0x23ac, 0x23ae, 0x23b2, 0x23b3, 0x23b5, 0x23b6, + 0x23b7, 0x23ba, 0x23bc, 0x23be, 0x23c0, 0x23c2, 0x23cb, 0x23cc, + 0x23cd, 0x23ce, 0x23cf, 0x23d4, 0x23d9, 0x23dc, 0x23e0, 0x23e4, + 0x23e5, 0x23e6, 0x23eb, 0x23ef, 0x23f0, 0x23f4, 0x2403, 0x2407, + 0x240d, 0x240f, 0x2412, 0x2415, 0x241c, 0x2420, 0x242e, 0x2439, + 0x243f, 0x244f, 0x2455, 0x2456, 0x2457, 0x2459, 0x245b, 0x245c, + 0x2461, 0x2463, 0x2464, 0x246e, 0x2471, 0x2472, 0x2475, 0x2476, + 0x2477, 0x2478, 0x2479, 0x247a, 0x247b, 0x247d, 0x2480, 0x2482, + 0x2483, 0x2488, 0x248a, 0x2492, 0x2493, 0x2495, 0x249b, 0x249c, + 0x249f, 0x24a0, 0x24a5, 0x24aa, 0x24b5, 0x24c2, 0x24ce, 0x24d4, + 0x24d5, 0x24d6, 0x24d8, 0x24d9, 0x24db, 0x24dc, 0x24de, 0x24e1, + 0x24e4, 0x24e6, 0x24e8, 0x24ec, 0x24ed, 0x24f0, 0x24f3, 0x24fa, + 0x24fe, 0x24ff, 0x2501, 0x2502, 0x2504, 0x2506, 0x2508, 0x250a, + 0x250b, 0x2513, 0x2516, 0x2518, 0x251b, 0x251d, 0x251e, 0x2521, + 0x2526, 0x2528, 0x2529, 0x252f, 0x2532, 0x2535, 0x2539, 0x253b, + 0x253e, 0x254e, 0x2552, 0x2554, 0x2556, 0x2557, 0x2558, 0x255a, + 0x2561, 0x256c, 0x2572, 0x258b, 0x258f, 0x2591, 0x2592, 0x2593, + 0x2594, 0x2595, 0x259a, 0x259b, 0x259c, 0x259e, 0x259f, 0x25a1, + 0x25a3, 0x25a8, 0x25ac, 0x25ad, 0x25b0, 0x25b1, 0x25b2, 0x25b3, + 0x25b7, 0x25b8, 0x25ba, 0x25bb, 0x25c0, 0x25c1, 0x25c3, 0x25c7, + 0x25c8, 0x25ce, 0x25da, 0x25e6, 0x25f1, 0x25f4, 0x25f9, 0x25fa, + 0x25fb, 0x25fe, 0x2600, 0x2601, 0x2608, 0x260a, 0x2614, 0x2615, + 0x2618, 0x261a, 0x261e, 0x261f, 0x2621, 0x2623, 0x2625, 0x2626, + 0x262a, 0x262c, 0x262f, 0x2630, 0x2634, 0x2636, 0x2639, 0x263b, + 0x263f, 0x2640, 0x2641, 0x2643, 0x2645, 0x2646, 0x2647, 0x2649, + 0x264c, 0x264e, 0x2653, 0x2655, 0x2657, 0x2659, 0x2660, 0x2662, + 0x2664, 0x2666, 0x2667, 0x266a, 0x266b, 0x266c, 0x266d, 0x266f, + 0x2671, 0x2673, 0x2675, 0x2676, 0x2678, 0x267b, 0x267d, 0x2689, + 0x268c, 0x268d, 0x2690, 0x2692, 0x2694, 0x2696, 0x2699, 0x269b, + 0x26a0, 0x26a8, 0x26ab, 0x26af, 0x26b0, 0x26b2, 0x26b7, 0x26b8, + 0x26bc, 0x26bd, 0x26c1, 0x26c2, 0x26c3, 0x26c4, 0x26c5, 0x26c6, + 0x26c7, 0x26c8, 0x26cc, 0x26cf, 0x26d3, 0x26dc, 0x26e1, 0x26e2, + 0x26e7, 0x26ec, 0x26ee, 0x26f4, 0x26f5, 0x26f8, 0x26fc, 0x2700, + 0x2701, 0x2705, 0x2709, 0x270c, 0x2712, 0x2717, 0x2719, 0x2722, + 0x2726, 0x2727, 0x272a, 0x2731, 0x273a, 0x2741, 0x2742, 0x2745, + 0x2753, 0x275a, 0x275b, 0x275c, 0x2768, 0x278e, 0x278f, 0x2796, + 0x2797, 0x2798, 0x27a4, 0x27ac, 0x27ad, 0x27b0, 0x27b2, 0x27b3, + 0x27b7, 0x27b8, 0x27b9, 0x27ba, 0x27bd, 0x27bf, 0x27c0, 0x27c5, + 0x27c9, 0x27cc, 0x27cd, 0x27ce, 0x27d4, 0x27d7, 0x27d8, 0x27d9, + 0x27dc, 0x27e0, 0x27e2, 0x27e3, 0x27e5, 0x27e7, 0x27e8, 0x27f0, + 0x27f1, 0x27f2, 0x27f4, 0x27f7, 0x27fa, 0x27fb, 0x2803, 0x280a, + 0x2813, 0x2816, 0x2825, 0x283b, 0x283d, 0x2842, 0x2846, 0x284a, + 0x2889, 0x2892, 0x28c1, 0x28c9, 0x2956, 0x295c, 0x2962, 0x299d, + 0x29b6 +}; + +static const uint8_t glyph_id_ofs_list_8[] = { + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 0, + 15, 16, 17, 18, 19, 0, 20, 21, + 22, 23, 24, 25, 0, 0, 26, 27, + 28, 29, 30, 31, 32, 33, 34, 35, + 36, 37, 38, 39, 40, 41, 42, 43, + 44, 45, 46, 47, 48, 49, 50, 0, + 51, 52, 53, 54, 55, 56, 57, 58, + 59, 60, 61, 62, 63, 0, 64, 65, + 66, 67, 68, 69, 70, 0, 71, 72, + 73, 74, 75, 76, 77, 78, 79, 80, + 81, 82, 0, 83, 84, 85, 86, 87, + 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 0, 99, 100, 101, 102, + 103, 0, 104, 105, 106, 107, 108, 109, + 110, 111, 112, 113, 114, 115, 116 +}; + +static const uint16_t unicode_list_10[] = { + 0x0, 0x2, 0xa, 0xc, 0xd, 0x18, 0x19, 0x1c, + 0x1d, 0x1f, 0x20, 0x22, 0x27, 0x29, 0x2a, 0x30, + 0x31, 0x32, 0x36, 0x39, 0x3a, 0x3c, 0x41, 0x46, + 0x49, 0x52, 0x54, 0x56, 0x5c, 0x62, 0x65, 0x66, + 0x67, 0x69, 0x6c, 0x6f, 0x77, 0x78, 0x7a, 0x80, + 0x81, 0x84, 0x85, 0x87, 0x89, 0x8d, 0x92, 0x94, + 0x96, 0x9c, 0x9d, 0xa0, 0xa7, 0xa8, 0xa9, 0xad, + 0xae, 0xb1, 0xb6, 0xb8, 0xb9, 0xbb, 0xc3, 0xc4, + 0xc8, 0xc9, 0xcb, 0xcc, 0xcd, 0xce, 0xd3, 0xd4, + 0xd5, 0xd8, 0xda, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, + 0xe1, 0xe4, 0xe8, 0xea, 0xed, 0xee, 0xef, 0xf0, + 0xf1, 0xf2, 0xf9, 0xfb, 0xfd, 0xfe, 0xff, 0x100, + 0x103, 0x105, 0x107, 0x10a, 0x10b, 0x10e, 0x112, 0x113, + 0x114, 0x115, 0x11a, 0x11c, 0x120, 0x122, 0x131, 0x132, + 0x139, 0x147, 0x148, 0x14b, 0x14c, 0x14e, 0x14f, 0x151, + 0x153, 0x154, 0x15b, 0x15e, 0x160, 0x162, 0x163, 0x164, + 0x165, 0x167, 0x168, 0x169, 0x16a, 0x16c, 0x16d, 0x171, + 0x172, 0x173, 0x175, 0x176, 0x177, 0x179, 0x17a, 0x17c, + 0x17f, 0x182, 0x184, 0x185, 0x186, 0x187, 0x188, 0x189, + 0x18a, 0x18b, 0x18c, 0x18e, 0x194, 0x195, 0x196, 0x19e, + 0x19f, 0x1a1, 0x1a2, 0x1a3, 0x1a4, 0x1a5, 0x1a6, 0x1a9, + 0x1ac, 0x1ad, 0x1af, 0x1b0, 0x1b1, 0x1b2, 0x1b3, 0x1b4, + 0x1b5, 0x1b7, 0x1b8, 0x1b9, 0x1ba, 0x1bb, 0x1bc, 0x1be, + 0x1c0, 0x1c2, 0x1c4, 0x1c5, 0x1ca, 0x1ce, 0x1d1, 0x1d2, + 0x1d5, 0x1d6, 0x1d7, 0x1d8, 0x1d9, 0x1da, 0x1db, 0x1dc, + 0x1de, 0x1e0, 0x1e2, 0x1e6, 0x1f4, 0x1f7, 0x1f9, 0x1fa, + 0x1fe, 0x200, 0x206, 0x20e, 0x210, 0x212, 0x213, 0x214, + 0x218, 0x219, 0x21b, 0x21c, 0x21d, 0x221, 0x222, 0x228, + 0x22d, 0x22f, 0x231, 0x235, 0x236, 0x238, 0x239, 0x23c, + 0x241, 0x242, 0x243, 0x244, 0x245, 0x246, 0x247, 0x248, + 0x24a, 0x250, 0x252, 0x257, 0x259, 0x260, 0x263, 0x264, + 0x265, 0x26b, 0x26e, 0x270, 0x272, 0x27b, 0x282, 0x283, + 0x288, 0x289, 0x28a, 0x28b, 0x28e, 0x292, 0x294, 0x2ab, + 0x2af, 0x2b2, 0x2b4, 0x2b5, 0x2bb, 0x2bc, 0x2c3, 0x2c4, + 0x2c6, 0x2c8, 0x2c9, 0x2ca, 0x2cc, 0x2cd, 0x2ce, 0x2d4, + 0x2d5, 0x2d8, 0x2da, 0x2dc, 0x2e3, 0x2e4, 0x2e6, 0x2e7, + 0x2e9, 0x2ea, 0x2eb, 0x2f0, 0x2f2, 0x2f3, 0x2f4, 0x2f5, + 0x2f7, 0x2f8, 0x2f9, 0x2fb, 0x2fc, 0x2fd, 0x2fe, 0x2ff, + 0x300, 0x301, 0x303, 0x306, 0x30c, 0x30f, 0x311, 0x313, + 0x317, 0x320, 0x322, 0x327, 0x330, 0x336, 0x337, 0x338, + 0x33a, 0x33b, 0x33c, 0x341, 0x342, 0x345, 0x346, 0x347, + 0x34a, 0x34c +}; + +static const uint8_t glyph_id_ofs_list_11[] = { + 0, 0, 1, 2, 0, 3, 4, 5, + 0, 6, 7, 0, 0, 0, 0, 8, + 9, 10, 0, 0, 11, 12, 0, 13, + 0, 14, 0, 0, 15, 16, 17, 0, + 18, 19, 20, 21, 22, 23, 24, 25, + 26, 27, 0, 28, 29, 0, 0, 30, + 31, 32, 0, 0, 0, 33, 34, 0, + 0, 35, 0, 0, 36, 0, 0, 37, + 38, 0, 39, 40, 41, 42, 43, 44, + 0, 45, 46, 47, 48, 49, 0, 50, + 51, 0, 0, 52, 53, 0, 54, 55, + 56, 57, 0, 58, 59, 60, 61, 0, + 0, 0, 0, 62, 0, 0, 0, 63, + 0, 64, 0, 0, 65, 0, 0, 66, + 0, 67, 0, 68, 0, 0, 0, 0, + 0, 69, 70, 71, 72, 73, 74, 75, + 76, 77, 0, 0, 78, 0, 79, 80, + 0, 81, 0, 0, 82, 83, 0, 84, + 0, 0, 85, 86, 87, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 88, + 89, 0, 0, 90, 91, 92, 0, 93, + 0, 94, 0, 95, 96, 97, 98, 0, + 99, 100, 101, 0, 102, 0, 0, 0, + 103, 0, 0, 104, 0, 0, 105, 106, + 0, 107, 0, 0, 0, 0, 0, 108, + 109, 110, 111, 0, 112, 0, 0, 0, + 0, 0, 113, 114, 115, 0, 116, 117, + 118, 119, 0, 120, 121, 122, 123, 124, + 125, 126, 127, 128, 129, 130, 131, 132 +}; + +static const uint16_t unicode_list_12[] = { + 0x0, 0x1, 0x4, 0x5, 0x6, 0xe, 0xf, 0x12, + 0x17, 0x1b, 0x1c, 0x21, 0x24, 0x25, 0x27, 0x29, + 0x31, 0x32, 0x33, 0x34, 0x39, 0x3a, 0x3b, 0x3c, + 0x3d, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x45, 0x46, + 0x49, 0x4a, 0x4e, 0x50, 0x53, 0x55, 0x58, 0x5d, + 0x5f, 0x61, 0x65, 0x66, 0x68, 0x69, 0x6a, 0x6e, + 0x72, 0x73, 0x79, 0x7a, 0x7b, 0x81, 0x82, 0x86, + 0x8a, 0x8c, 0x8d, 0x8f, 0x94, 0x95, 0x96, 0x97, + 0x98, 0x9a, 0xa1, 0xa5, 0xa6, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xba, 0xc1, 0xc5, 0xc6, 0xcf, 0xda, + 0xe0, 0xe2, 0xe3, 0xe4, 0xe5, 0xea, 0xec, 0xf2, + 0xf4, 0xf5, 0xf6, 0xfa, 0xfc, 0xfe, 0xff, 0x101, + 0x103, 0x10b, 0x110, 0x111, 0x112, 0x114, 0x115, 0x117, + 0x120, 0x122, 0x125, 0x12a, 0x138, 0x13b, 0x13d, 0x141, + 0x142, 0x143, 0x146, 0x148, 0x14a, 0x14d, 0x152, 0x153, + 0x156, 0x159, 0x15a, 0x15c, 0x15f, 0x166, 0x168, 0x169, + 0x16c, 0x16e, 0x16f, 0x175, 0x179, 0x185, 0x188, 0x195, + 0x19a, 0x19c, 0x1a0, 0x1a3, 0x1a8, 0x1aa, 0x1b4, 0x1b5, + 0x1c0, 0x1c1, 0x1c2, 0x1c3, 0x1c4, 0x1c5, 0x1c6, 0x1cc, + 0x1d1, 0x1d2, 0x1d3, 0x1df, 0x1e2, 0x1e7, 0x1ed, 0x1f1, + 0x1fb, 0x1fd, 0x202, 0x203, 0x204, 0x207, 0x20d, 0x20e, + 0x210, 0x218, 0x224, 0x225, 0x22d, 0x231, 0x233, 0x237, + 0x238, 0x239, 0x240, 0x242, 0x24a, 0x252, 0x258, 0x259, + 0x25c, 0x25e, 0x265, 0x26d, 0x272, 0x284, 0x288, 0x28e, + 0x29a, 0x29f, 0x2b0, 0x2b2, 0x2c1, 0x2c5, 0x2d6, 0x2d7, + 0x2d8, 0x2d9, 0x2da, 0x2dd, 0x2e3, 0x2e7, 0x2eb, 0x2f4, + 0x2f5, 0x2f7, 0x2fa, 0x302, 0x303, 0x304, 0x305, 0x306, + 0x307, 0x308, 0x309, 0x30a, 0x30b, 0x313, 0x314, 0x315, + 0x316, 0x31c, 0x31e, 0x325, 0x326, 0x32c, 0x32d, 0x330, + 0x331, 0x332, 0x333, 0x335, 0x338, 0x339, 0x33a, 0x33d, + 0x33e, 0x33f, 0x343, 0x349, 0x34d, 0x34f, 0x350, 0x352, + 0x353, 0x354, 0x357, 0x358, 0x359, 0x35a, 0x35d, 0x361, + 0x362, 0x364, 0x367, 0x368, 0x36d, 0x372, 0x376, 0x377, + 0x379, 0x37a, 0x37b, 0x37c, 0x37d, 0x381, 0x382, 0x387, + 0x389, 0x38b, 0x38c, 0x390, 0x391, 0x392, 0x393, 0x396, + 0x39b, 0x39c, 0x39e, 0x3a0, 0x3a1, 0x3a3, 0x3a5, 0x3a7, + 0x3aa, 0x3ab, 0x3ac, 0x3ae, 0x3b2, 0x3b7, 0x3ba, 0x3bd, + 0x3c0, 0x3c4, 0x3c7, 0x3c8, 0x3d0, 0x3d1, 0x3d2, 0x3d5, + 0x3d7, 0x3dc, 0x3e0, 0x3e2, 0x3e9, 0x3ec, 0x3ed, 0x3ee, + 0x3f7, 0x3f9, 0x3fd, 0x3ff, 0x404, 0x405, 0x406, 0x407, + 0x40b, 0x40c, 0x40e, 0x411, 0x414, 0x416, 0x41c, 0x420, + 0x428, 0x431, 0x434, 0x435, 0x436, 0x438, 0x43c, 0x43e, + 0x443, 0x446, 0x449, 0x44f, 0x453, 0x454, 0x45a, 0x45b, + 0x45c, 0x464, 0x469, 0x46e, 0x473, 0x477, 0x482, 0x487, + 0x48c, 0x493, 0x49c, 0x49e, 0x49f, 0x4a4, 0x4aa, 0x4ab, + 0x4bb, 0x4c2, 0x4c5, 0x4c9, 0x4cd, 0x4ce, 0x4d5, 0x4d6, + 0x4dd, 0x4e0, 0x4e2, 0x4ea, 0x4eb, 0x4ec, 0x4ed, 0x4ee, + 0x4f1, 0x4f2, 0x4f4, 0x4f5, 0x4f7, 0x4f9, 0x4fb, 0x500, + 0x506, 0x507, 0x508, 0x50a, 0x50b, 0x50d, 0x50e, 0x511, + 0x514, 0x516, 0x51b, 0x51f, 0x525, 0x52b, 0x52d, 0x534, + 0x536, 0x53a, 0x540, 0x545, 0x54a, 0x54b, 0x54e, 0x54f, + 0x552, 0x557, 0x55b, 0x55d, 0x55e, 0x561, 0x562, 0x568, + 0x56b, 0x56c, 0x56d, 0x56e, 0x571, 0x579, 0x57a, 0x57c, + 0x57d, 0x581, 0x582, 0x585, 0x587, 0x58b, 0x593, 0x599, + 0x59b, 0x59c, 0x5a2, 0x5a3, 0x5a4, 0x5aa, 0x5ae, 0x5b3, + 0x5b4, 0x5b9, 0x5bd, 0x5bf, 0x5ca, 0x5cd, 0x5e7, 0x5e8, + 0x5ef, 0x604, 0x608, 0x60a, 0x60c, 0x60f, 0x64a, 0x64b, + 0x64d, 0x64e, 0x64f, 0x650, 0x651, 0x652, 0x653, 0x654, + 0x655, 0x657, 0x658, 0x659, 0x65a, 0x65b, 0x65f, 0x663, + 0x665, 0x667, 0x66c, 0x66e, 0x66f, 0x674, 0x678, 0x67c, + 0x689, 0x690, 0x6c7, 0x6d1, 0x702, 0x712, 0x713, 0x71c, + 0x790, 0x7af, 0x7ef, 0x7f5 +}; + +static const uint8_t glyph_id_ofs_list_13[] = { + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 0, 12, 13, 14, + 15, 0, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 0, 25, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 35, 0, + 36, 37, 38, 39, 40, 41, 42, 43 +}; + +static const uint16_t unicode_list_18[] = { + 0x0, 0x5, 0x6, 0x8, 0xb, 0x14, 0x19, 0x20, + 0x21, 0x29, 0x2a, 0x32, 0x37, 0x38, 0x39, 0x41, + 0x44, 0x48, 0x49, 0x4b, 0x53, 0x57, 0xdc, 0xdd, + 0xde +}; + +static const uint16_t unicode_list_20[] = { + 0x0, 0x1, 0x2, 0x3, 0x5, 0x6, 0x7, 0x8, + 0x9, 0xa, 0xb, 0xd, 0xe, 0xf, 0x10, 0x11, + 0x13, 0x14, 0x18, 0x1a, 0x1d, 0x20, 0x21, 0x22, + 0x23, 0x24, 0x2e, 0x31, 0x32, 0x37, 0x38, 0x3e, + 0x41, 0x4c, 0x50, 0x5e, 0x60, 0x61, 0x62, 0x65, + 0x67, 0x69, 0x6b, 0x6c, 0x70, 0x71, 0x73, 0x78, + 0x79, 0x7b, 0x7c, 0x7e, 0x83, 0x84, 0x87, 0x88, + 0x8a, 0x8b, 0x8c, 0x90, 0x91, 0x95, 0x97, 0x98, + 0x99, 0x9c, 0xa0, 0xa2, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xaa, 0xb2, 0xb6, 0xb7, 0xb9, 0xbc, 0xc1, + 0xca, 0xcb, 0xcc, 0xcf, 0xd0, 0xd6, 0xd7, 0xd9, + 0xdb, 0xdc, 0xde, 0xe2, 0xe6, 0xe7, 0xea, 0xed, + 0xee, 0xef, 0xf1, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, + 0xfe, 0xff, 0x106, 0x113, 0x116, 0x119, 0x11a, 0x11c, + 0x11d, 0x11f, 0x121, 0x123, 0x129, 0x12c, 0x12e, 0x132, + 0x134, 0x13c, 0x13d, 0x141, 0x149, 0x14b, 0x158, 0x159, + 0x15c, 0x15f, 0x167, 0x17b, 0x213, 0x214, 0x215, 0x216 +}; + +static const uint8_t glyph_id_ofs_list_22[] = { + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 0, 11, 12, 13, 14, + 15, 0, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 0, 26, 27, 0, + 0, 0, 28, 0, 0, 0, 0, 29, + 30, 0, 31, 0, 0, 0, 0, 32, + 33, 0, 0, 0, 0, 34, 0, 0, + 35, 0, 0, 0, 36, 37, 0, 0, + 38, 39, 0, 40, 41, 0, 42, 43, + 0, 0, 0, 0, 0, 44, 0, 45, + 46, 0, 47, 48, 49, 0, 0, 50, + 51, 0, 52, 53, 54, 55, 56, 0, + 0, 57, 0, 58, 59, 60, 0, 61, + 62, 63, 64, 0, 65, 66, 0, 67, + 0, 0, 68, 0, 0, 0, 69, 70, + 71, 0, 0, 0, 72, 0, 0, 73, + 74, 75, 76, 77, 78, 79, 0, 0, + 80, 81, 82, 0, 83, 0, 84, 85, + 86, 87, 0, 88, 0, 89, 90, 0, + 0, 91, 92, 0, 93, 94, 95, 96, + 97, 98 +}; + +static const uint16_t unicode_list_23[] = { + 0x0, 0x7, 0x8, 0x9, 0xf, 0x10, 0x12, 0x15, + 0x16, 0x18, 0x1b, 0x1c, 0x1e, 0x21, 0x27, 0x29, + 0x2a, 0x2b, 0x2c, 0x2d, 0x31, 0x32, 0x35, 0x3c, + 0x3d, 0x3f, 0x42, 0x47, 0x48, 0x4e, 0x4f, 0x57, + 0x59, 0x5a, 0x5c, 0x5d, 0x62, 0x65, 0x6b, 0x6d, + 0x6f, 0x71, 0x73, 0x75, 0x77, 0x7b, 0x7c, 0x7d, + 0x80, 0x84, 0x86, 0x88, 0x89, 0x8a, 0x8b, 0x8d, + 0x8e, 0x8f, 0x90, 0x92, 0x93, 0x94, 0x95, 0x98, + 0x9b, 0x9e, 0x9f, 0xa1, 0xa4, 0xa8, 0xa9, 0xaa, + 0xab, 0xad, 0xb1, 0xb5, 0xb6, 0xb7, 0xbb, 0xbc, + 0xc0, 0xc1, 0xc2, 0xc5, 0xc7, 0xc9, 0xce, 0xd2, + 0xd7, 0xd8, 0xdc, 0xde, 0xf3, 0xf8, 0xfc, 0xfd, + 0x109, 0x10b, 0x113, 0x11d, 0x120, 0x123, 0x124, 0x125, + 0x126, 0x127, 0x128, 0x129, 0x12a, 0x12c, 0x131, 0x134, + 0x137, 0x138, 0x13b, 0x13c, 0x13d, 0x13e, 0x13f, 0x143, + 0x144, 0x146, 0x148, 0x149, 0x14a, 0x14b, 0x14c, 0x14e, + 0x14f, 0x150, 0x151, 0x152, 0x153, 0x157, 0x158, 0x159, + 0x15f, 0x161, 0x163, 0x165, 0x166, 0x167, 0x16a, 0x16b, + 0x16c, 0x174, 0x175, 0x17c, 0x17d, 0x184, 0x187, 0x188, + 0x189, 0x18e, 0x18f, 0x194, 0x1a1, 0x1a3, 0x1a4, 0x1a6, + 0x1a7, 0x1a8, 0x1a9, 0x1ab, 0x1b6, 0x24e, 0x268, 0x288, + 0x2a2, 0x318, 0x344, 0x369, 0x3a4, 0x3b0, 0x418, 0x445, + 0x45f, 0x460, 0x461, 0x462, 0x463, 0x464, 0x465, 0x466, + 0x467, 0x468, 0x469, 0x46a, 0x46c, 0x46d, 0x46e, 0x46f, + 0x471 +}; + +static const uint8_t glyph_id_ofs_list_25[] = { + 0, 1, 2, 3, 4, 5, 6, 0, + 7, 8, 9, 0, 0, 10, 11, 12, + 13, 14, 0, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25 +}; + +static const uint8_t glyph_id_ofs_list_28[] = { + 0, 1, 2, 0, 3, 4, 5, 6, + 7, 8, 0, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 0, 20, + 21, 22, 23, 24, 25, 26, 27, 28, + 0, 29, 30, 31, 32, 33, 0, 34, + 35, 36, 37, 0, 38, 39, 0, 40, + 41, 42, 43, 44, 45, 46, 47, 48, + 0, 49, 50, 51, 52, 0, 53, 54, + 55, 56, 57, 0, 58, 59, 60, 61, + 62, 63, 64, 65, 66, 67, 68, 69, + 70, 0, 71, 72, 73, 74, 75, 0, + 0, 76, 0, 0, 0, 0, 0, 0, + 0, 0, 77 +}; + +static const uint8_t glyph_id_ofs_list_29[] = { + 0, 1, 2, 3, 0, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 0, + 22, 23, 24, 25, 26, 27, 28, 0, + 29, 30, 31, 32, 33, 34, 35, 36, + 37, 38, 39, 0, 40, 41, 42, 43, + 0, 44, 45, 0, 46, 47, 0, 48, + 0, 49, 50, 0, 0, 0, 0, 0, + 0, 0, 51, 0, 0, 0, 52, 0, + 0, 53, 54, 55, 56, 57, 58, 0, + 0, 0, 0, 59, 60, 61, 0, 62, + 63, 0, 64, 0, 65, 66, 67, 68, + 69, 70, 0, 71, 72, 73 +}; + +static const uint16_t unicode_list_30[] = { + 0x0, 0x4, 0x5, 0xb, 0xf, 0x11, 0x12, 0x14, + 0x17, 0x18, 0x19, 0x1a, 0x1c, 0x22, 0x24, 0x25, + 0x26, 0x27, 0x35, 0x36, 0x38, 0x3b, 0x3d, 0x3f, + 0x40, 0x44, 0x47, 0x48, 0x49, 0x4c, 0x57, 0x60, + 0x63, 0x66, 0x69, 0x6c, 0x6d, 0x6e, 0x70, 0x71, + 0x74, 0x75, 0x76, 0x77, 0x79, 0x7c, 0x7d, 0x7e, + 0x7f, 0x82, 0x85, 0x90, 0x98, 0x99, 0x9a, 0x9f, + 0xa3, 0xa6, 0xa7, 0xa9, 0xae, 0xb0, 0xb1, 0xb4, + 0xb6, 0xb7, 0xb8, 0xb9, 0xbd, 0xbe, 0xbf, 0xc3, + 0xc6, 0xcc, 0xce, 0xda, 0xdd, 0xe0, 0xe2, 0xe8, + 0xe9, 0xee, 0x102, 0x103, 0x106, 0x109, 0x10b, 0x10e, + 0x110, 0x111, 0x112, 0x115, 0x119, 0x123, 0x124, 0x126, + 0x12c, 0x135, 0x13b, 0x13d, 0x141, 0x142, 0x144, 0x148, + 0x150, 0x153, 0x15b, 0x15d, 0x15f, 0x162, 0x164, 0x196, + 0x197, 0x199, 0x19a, 0x19b, 0x19c, 0x19d, 0x1a3, 0x1a5, + 0x1a6 +}; + +static const uint8_t glyph_id_ofs_list_32[] = { + 0, 1, 0, 2, 3, 4, 0, 5, + 6, 0, 7, 8, 9, 0, 10, 11, + 12, 13, 14, 15, 16, 17, 18, 0, + 19, 20, 21, 22 +}; + +static const uint16_t unicode_list_33[] = { + 0x0, 0x3, 0x4, 0x5, 0x7, 0xa, 0xb, 0xc, + 0x10, 0x11, 0x19, 0x1a, 0x3f, 0x42, 0x60, 0x86, + 0x87 +}; + +static const uint8_t glyph_id_ofs_list_34[] = { + 0, 0, 1, 0, 2, 3, 4, 5, + 6, 7, 8, 9, 10, 11, 12, 13, + 0, 14, 15, 16, 17, 0, 0, 18, + 0, 19, 20, 0, 21, 22, 23, 0, + 0, 24, 25, 26, 27, 28, 0, 29, + 30, 0, 31, 0, 32, 33, 34, 35, + 36, 37, 38, 39, 40, 41, 42, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 43, 0, 0, 44 +}; + +static const uint8_t glyph_id_ofs_list_35[] = { + 0, 1, 2, 3, 4, 5, 0, 6, + 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 0, + 22, 23, 24, 25, 26, 0, 27, 28, + 29, 0, 0, 30, 31, 32, 33, 34, + 0, 0, 35, 36, 37, 0, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 0, 50, 51 +}; + +static const uint16_t unicode_list_36[] = { + 0x0, 0x1, 0x6, 0x7, 0x8, 0xa, 0xc, 0x10, + 0x11, 0x12, 0x15, 0x1b, 0x1c, 0x21, 0x23, 0x28, + 0x2f, 0x31, 0x36, 0x3b, 0x3d, 0x3f, 0x49, 0x4b, + 0x53, 0x58, 0x5f, 0x63, 0x6f, 0x73, 0x7f, 0x82, + 0x8b, 0x8c, 0x91, 0x92, 0x93, 0x94, 0x95, 0x97, + 0x98, 0x99, 0x9d, 0x9f, 0xa1, 0xa4, 0x1cc +}; + +static const uint8_t glyph_id_ofs_list_37[] = { + 0, 0, 1, 2, 0, 0, 3, 4, + 5, 6, 0, 0, 7, 0, 8, 9, + 0, 10, 11, 12, 0, 13, 14, 0, + 0, 0, 0, 15, 16, 17, 0, 18, + 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 0, 30, 0, 31, 32, + 0, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 0, 42, 43, 44, 45, 0, + 0, 0, 0, 0, 46, 47, 48, 49, + 50, 0, 0, 51, 52, 53, 54, 55, + 56, 57, 0, 0, 58, 59, 60, 61, + 62, 63, 64, 0, 0, 65, 66, 67, + 68, 0, 0, 69 +}; + +static const uint8_t glyph_id_ofs_list_38[] = { + 0, 1, 2, 3, 4, 0, 5, 6, + 0, 7, 8, 9, 10, 11, 12, 0, + 13, 0, 14, 15, 16, 0, 17, 18, + 19, 20, 21, 22, 0, 0, 23, 24, + 25, 0, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 0, 38, + 39, 0, 40, 0, 0, 0, 41, 0, + 42, 43, 0, 44, 45, 46, 0, 47, + 0, 0, 0, 0, 48, 49, 0, 50, + 51, 52, 53, 54, 55, 56, 57 +}; + +static const uint16_t unicode_list_39[] = { + 0x0, 0x1, 0x3, 0xe, 0xf, 0x12, 0x17, 0x18, + 0x1b, 0x22, 0x23, 0x2d, 0x2f, 0x36, 0x44, 0x48, + 0x4b, 0x4d, 0x4e, 0x54, 0x59, 0x5d, 0x5e, 0x5f, + 0x61, 0x64, 0x68, 0x6b, 0x6c, 0x6d, 0x6f, 0x70, + 0x72, 0x75, 0x77, 0x79, 0x7a, 0x7f, 0x89, 0x8b, + 0x8c, 0x8e, 0x9b, 0x9d, 0x9e, 0xa0, 0xa3, 0xa7, + 0xa9, 0xb0, 0xb2, 0xbc, 0xbf, 0xc7, 0xc9, 0xcb, + 0xcd, 0xce, 0xd4, 0xe0, 0xe1, 0x10f, 0x110, 0x113, + 0x114, 0x115, 0x116, 0x117, 0x118, 0x119, 0x11a, 0x11b, + 0x11c, 0x129, 0x12a, 0x12b, 0x12f, 0x130 +}; + +/*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 = 12288, .range_length = 24, .glyph_id_start = 96, + .unicode_list = NULL, .glyph_id_ofs_list = glyph_id_ofs_list_1, .list_length = 24, .type = LV_FONT_FMT_TXT_CMAP_FORMAT0_FULL + }, + { + .range_start = 12353, .range_length = 83, .glyph_id_start = 116, + .unicode_list = NULL, .glyph_id_ofs_list = NULL, .list_length = 0, .type = LV_FONT_FMT_TXT_CMAP_FORMAT0_TINY + }, + { + .range_start = 12449, .range_length = 86, .glyph_id_start = 199, + .unicode_list = NULL, .glyph_id_ofs_list = NULL, .list_length = 0, .type = LV_FONT_FMT_TXT_CMAP_FORMAT0_TINY + }, + { + .range_start = 19968, .range_length = 248, .glyph_id_start = 285, + .unicode_list = NULL, .glyph_id_ofs_list = glyph_id_ofs_list_4, .list_length = 248, .type = LV_FONT_FMT_TXT_CMAP_FORMAT0_FULL + }, + { + .range_start = 20219, .range_length = 1254, .glyph_id_start = 432, + .unicode_list = unicode_list_5, .glyph_id_ofs_list = NULL, .list_length = 486, .type = LV_FONT_FMT_TXT_CMAP_SPARSE_TINY + }, + { + .range_start = 21475, .range_length = 253, .glyph_id_start = 918, + .unicode_list = NULL, .glyph_id_ofs_list = glyph_id_ofs_list_6, .list_length = 253, .type = LV_FONT_FMT_TXT_CMAP_FORMAT0_FULL + }, + { + .range_start = 21733, .range_length = 10679, .glyph_id_start = 1076, + .unicode_list = unicode_list_7, .glyph_id_ofs_list = NULL, .list_length = 3401, .type = LV_FONT_FMT_TXT_CMAP_SPARSE_TINY + }, + { + .range_start = 32415, .range_length = 127, .glyph_id_start = 4477, + .unicode_list = NULL, .glyph_id_ofs_list = glyph_id_ofs_list_8, .list_length = 127, .type = LV_FONT_FMT_TXT_CMAP_FORMAT0_FULL + }, + { + .range_start = 32543, .range_length = 24, .glyph_id_start = 4594, + .unicode_list = NULL, .glyph_id_ofs_list = NULL, .list_length = 0, .type = LV_FONT_FMT_TXT_CMAP_FORMAT0_TINY + }, + { + .range_start = 32568, .range_length = 845, .glyph_id_start = 4618, + .unicode_list = unicode_list_10, .glyph_id_ofs_list = NULL, .list_length = 330, .type = LV_FONT_FMT_TXT_CMAP_SPARSE_TINY + }, + { + .range_start = 33416, .range_length = 232, .glyph_id_start = 4948, + .unicode_list = NULL, .glyph_id_ofs_list = glyph_id_ofs_list_11, .list_length = 232, .type = LV_FONT_FMT_TXT_CMAP_FORMAT0_FULL + }, + { + .range_start = 33655, .range_length = 2038, .glyph_id_start = 5081, + .unicode_list = unicode_list_12, .glyph_id_ofs_list = NULL, .list_length = 492, .type = LV_FONT_FMT_TXT_CMAP_SPARSE_TINY + }, + { + .range_start = 35744, .range_length = 48, .glyph_id_start = 5573, + .unicode_list = NULL, .glyph_id_ofs_list = glyph_id_ofs_list_13, .list_length = 48, .type = LV_FONT_FMT_TXT_CMAP_FORMAT0_FULL + }, + { + .range_start = 35793, .range_length = 25, .glyph_id_start = 5617, + .unicode_list = NULL, .glyph_id_ofs_list = NULL, .list_length = 0, .type = LV_FONT_FMT_TXT_CMAP_FORMAT0_TINY + }, + { + .range_start = 35819, .range_length = 30, .glyph_id_start = 5642, + .unicode_list = NULL, .glyph_id_ofs_list = NULL, .list_length = 0, .type = LV_FONT_FMT_TXT_CMAP_FORMAT0_TINY + }, + { + .range_start = 35850, .range_length = 20, .glyph_id_start = 5672, + .unicode_list = NULL, .glyph_id_ofs_list = NULL, .list_length = 0, .type = LV_FONT_FMT_TXT_CMAP_FORMAT0_TINY + }, + { + .range_start = 35871, .range_length = 25, .glyph_id_start = 5692, + .unicode_list = NULL, .glyph_id_ofs_list = NULL, .list_length = 0, .type = LV_FONT_FMT_TXT_CMAP_FORMAT0_TINY + }, + { + .range_start = 35905, .range_length = 223, .glyph_id_start = 5717, + .unicode_list = unicode_list_18, .glyph_id_ofs_list = NULL, .list_length = 25, .type = LV_FONT_FMT_TXT_CMAP_SPARSE_TINY + }, + { + .range_start = 36129, .range_length = 48, .glyph_id_start = 5742, + .unicode_list = NULL, .glyph_id_ofs_list = NULL, .list_length = 0, .type = LV_FONT_FMT_TXT_CMAP_FORMAT0_TINY + }, + { + .range_start = 36179, .range_length = 535, .glyph_id_start = 5790, + .unicode_list = unicode_list_20, .glyph_id_ofs_list = NULL, .list_length = 136, .type = LV_FONT_FMT_TXT_CMAP_SPARSE_TINY + }, + { + .range_start = 36715, .range_length = 21, .glyph_id_start = 5926, + .unicode_list = NULL, .glyph_id_ofs_list = NULL, .list_length = 0, .type = LV_FONT_FMT_TXT_CMAP_FORMAT0_TINY + }, + { + .range_start = 36737, .range_length = 162, .glyph_id_start = 5947, + .unicode_list = NULL, .glyph_id_ofs_list = glyph_id_ofs_list_22, .list_length = 162, .type = LV_FONT_FMT_TXT_CMAP_FORMAT0_FULL + }, + { + .range_start = 36902, .range_length = 1138, .glyph_id_start = 6046, + .unicode_list = unicode_list_23, .glyph_id_ofs_list = NULL, .list_length = 193, .type = LV_FONT_FMT_TXT_CMAP_SPARSE_TINY + }, + { + .range_start = 38041, .range_length = 46, .glyph_id_start = 6239, + .unicode_list = NULL, .glyph_id_ofs_list = NULL, .list_length = 0, .type = LV_FONT_FMT_TXT_CMAP_FORMAT0_TINY + }, + { + .range_start = 38088, .range_length = 30, .glyph_id_start = 6285, + .unicode_list = NULL, .glyph_id_ofs_list = glyph_id_ofs_list_25, .list_length = 30, .type = LV_FONT_FMT_TXT_CMAP_FORMAT0_FULL + }, + { + .range_start = 38119, .range_length = 20, .glyph_id_start = 6311, + .unicode_list = NULL, .glyph_id_ofs_list = NULL, .list_length = 0, .type = LV_FONT_FMT_TXT_CMAP_FORMAT0_TINY + }, + { + .range_start = 38140, .range_length = 32, .glyph_id_start = 6331, + .unicode_list = NULL, .glyph_id_ofs_list = NULL, .list_length = 0, .type = LV_FONT_FMT_TXT_CMAP_FORMAT0_TINY + }, + { + .range_start = 38173, .range_length = 99, .glyph_id_start = 6363, + .unicode_list = NULL, .glyph_id_ofs_list = glyph_id_ofs_list_28, .list_length = 99, .type = LV_FONT_FMT_TXT_CMAP_FORMAT0_FULL + }, + { + .range_start = 38376, .range_length = 102, .glyph_id_start = 6441, + .unicode_list = NULL, .glyph_id_ofs_list = glyph_id_ofs_list_29, .list_length = 102, .type = LV_FONT_FMT_TXT_CMAP_FORMAT0_FULL + }, + { + .range_start = 38480, .range_length = 423, .glyph_id_start = 6515, + .unicode_list = unicode_list_30, .glyph_id_ofs_list = NULL, .list_length = 121, .type = LV_FONT_FMT_TXT_CMAP_SPARSE_TINY + }, + { + .range_start = 39029, .range_length = 22, .glyph_id_start = 6636, + .unicode_list = NULL, .glyph_id_ofs_list = NULL, .list_length = 0, .type = LV_FONT_FMT_TXT_CMAP_FORMAT0_TINY + }, + { + .range_start = 39052, .range_length = 28, .glyph_id_start = 6658, + .unicode_list = NULL, .glyph_id_ofs_list = glyph_id_ofs_list_32, .list_length = 28, .type = LV_FONT_FMT_TXT_CMAP_FORMAT0_FULL + }, + { + .range_start = 39118, .range_length = 136, .glyph_id_start = 6681, + .unicode_list = unicode_list_33, .glyph_id_ofs_list = NULL, .list_length = 17, .type = LV_FONT_FMT_TXT_CMAP_SPARSE_TINY + }, + { + .range_start = 39267, .range_length = 70, .glyph_id_start = 6698, + .unicode_list = NULL, .glyph_id_ofs_list = glyph_id_ofs_list_34, .list_length = 70, .type = LV_FONT_FMT_TXT_CMAP_FORMAT0_FULL + }, + { + .range_start = 39532, .range_length = 61, .glyph_id_start = 6743, + .unicode_list = NULL, .glyph_id_ofs_list = glyph_id_ofs_list_35, .list_length = 61, .type = LV_FONT_FMT_TXT_CMAP_FORMAT0_FULL + }, + { + .range_start = 39600, .range_length = 461, .glyph_id_start = 6795, + .unicode_list = unicode_list_36, .glyph_id_ofs_list = NULL, .list_length = 47, .type = LV_FONT_FMT_TXT_CMAP_SPARSE_TINY + }, + { + .range_start = 40063, .range_length = 100, .glyph_id_start = 6842, + .unicode_list = NULL, .glyph_id_ofs_list = glyph_id_ofs_list_37, .list_length = 100, .type = LV_FONT_FMT_TXT_CMAP_FORMAT0_FULL + }, + { + .range_start = 40479, .range_length = 79, .glyph_id_start = 6912, + .unicode_list = NULL, .glyph_id_ofs_list = glyph_id_ofs_list_38, .list_length = 79, .type = LV_FONT_FMT_TXT_CMAP_FORMAT0_FULL + }, + { + .range_start = 40560, .range_length = 305, .glyph_id_start = 6970, + .unicode_list = unicode_list_39, .glyph_id_ofs_list = NULL, .list_length = 78, .type = LV_FONT_FMT_TXT_CMAP_SPARSE_TINY + }, + { + .range_start = 65281, .range_length = 32, .glyph_id_start = 7048, + .unicode_list = NULL, .glyph_id_ofs_list = NULL, .list_length = 0, .type = LV_FONT_FMT_TXT_CMAP_FORMAT0_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 = 41, + .bpp = 1, + .kern_classes = 0, + .bitmap_format = 0, +#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 echoear_font_16 = { +#else +lv_font_t echoear_font_16 = { +#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 = 19, /*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 ZH_FONT*/ diff --git a/firmware/echoear-platform-agent/components/application/platform/src/espressif/lvgl_source/img_app_aiPhone.c b/firmware/echoear-platform-agent/components/application/platform/src/espressif/lvgl_source/img_app_aiPhone.c new file mode 100644 index 0000000..517ce28 --- /dev/null +++ b/firmware/echoear-platform-agent/components/application/platform/src/espressif/lvgl_source/img_app_aiPhone.c @@ -0,0 +1,146 @@ +#ifdef __has_include + #if __has_include("lvgl.h") + #ifndef LV_LVGL_H_INCLUDE_SIMPLE + #define LV_LVGL_H_INCLUDE_SIMPLE + #endif + #endif +#endif + +#if defined(LV_LVGL_H_INCLUDE_SIMPLE) + #include "lvgl.h" +#else + #include "lvgl/lvgl.h" +#endif + + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMAGE_OUTPUT_112X112 +#define LV_ATTRIBUTE_IMAGE_OUTPUT_112X112 +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMAGE_OUTPUT_112X112 uint8_t output_112x112_map[] = { + 0xfd, 0xfd, 0xf8, 0xff, 0xfd, 0xfd, 0xf7, 0xff, 0xfc, 0xfc, 0xf6, 0xff, 0xfc, 0xfb, 0xf5, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xfa, 0xf7, 0xf2, 0xff, 0xfa, 0xf8, 0xf2, 0xff, 0xfa, 0xf8, 0xf3, 0xff, 0xfa, 0xf8, 0xf3, 0xff, 0xfa, 0xf8, 0xf2, 0xff, 0xfa, 0xf9, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xf9, 0xf9, 0xf4, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xfc, 0xf9, 0xf4, 0xff, 0xfc, 0xf9, 0xf4, 0xff, 0xfb, 0xf9, 0xf4, 0xff, 0xfc, 0xf9, 0xf5, 0xff, 0xfc, 0xf9, 0xf5, 0xff, 0xfc, 0xf9, 0xf4, 0xff, 0xfc, 0xf9, 0xf5, 0xff, 0xfc, 0xf9, 0xf5, 0xff, 0xfc, 0xf9, 0xf4, 0xff, 0xfc, 0xf9, 0xf5, 0xff, 0xfc, 0xf9, 0xf5, 0xff, 0xfc, 0xf9, 0xf5, 0xff, 0xfc, 0xf9, 0xf5, 0xff, 0xfc, 0xf9, 0xf6, 0xff, 0xfc, 0xf9, 0xf6, 0xff, 0xfc, 0xfa, 0xf5, 0xff, 0xfc, 0xfa, 0xf6, 0xff, 0xfc, 0xfb, 0xf6, 0xff, 0xfc, 0xfa, 0xf6, 0xff, 0xfc, 0xf9, 0xf5, 0xff, 0xfd, 0xf8, 0xf5, 0xff, 0xfd, 0xf8, 0xf4, 0xff, 0xfc, 0xf9, 0xf4, 0xff, 0xfc, 0xf9, 0xf5, 0xff, 0xfd, 0xfa, 0xf6, 0xff, 0xfd, 0xfa, 0xf6, 0xff, 0xfc, 0xf9, 0xf5, 0xff, 0xfd, 0xf8, 0xf5, 0xff, 0xfc, 0xf9, 0xf4, 0xff, 0xfc, 0xf8, 0xf4, 0xff, 0xfc, 0xf8, 0xf4, 0xff, 0xfc, 0xf8, 0xf4, 0xff, 0xfd, 0xf8, 0xf5, 0xff, 0xfd, 0xf8, 0xf5, 0xff, 0xfc, 0xf9, 0xf5, 0xff, 0xfc, 0xfa, 0xf5, 0xff, 0xfc, 0xfa, 0xf6, 0xff, 0xfc, 0xf9, 0xf5, 0xff, 0xfd, 0xf9, 0xf5, 0xff, 0xfc, 0xf9, 0xf5, 0xff, 0xfd, 0xfa, 0xf6, 0xff, 0xfd, 0xfb, 0xf6, 0xff, 0xfd, 0xfa, 0xf6, 0xff, 0xfd, 0xf9, 0xf5, 0xff, 0xfc, 0xf9, 0xf4, 0xff, 0xfc, 0xf9, 0xf4, 0xff, 0xfd, 0xf9, 0xf5, 0xff, 0xfc, 0xfa, 0xf6, 0xff, 0xfc, 0xfa, 0xf6, 0xff, 0xfd, 0xfa, 0xf5, 0xff, 0xfd, 0xf9, 0xf5, 0xff, 0xfc, 0xfa, 0xf5, 0xff, 0xfd, 0xfa, 0xf6, 0xff, 0xfc, 0xf9, 0xf5, 0xff, 0xfc, 0xf9, 0xf4, 0xff, 0xfc, 0xfa, 0xf5, 0xff, 0xfc, 0xfa, 0xf6, 0xff, 0xfc, 0xfa, 0xf6, 0xff, 0xfd, 0xfa, 0xf5, 0xff, 0xfc, 0xfa, 0xf6, 0xff, 0xfd, 0xfa, 0xf6, 0xff, 0xfd, 0xfa, 0xf6, 0xff, 0xfd, 0xfa, 0xf6, 0xff, 0xfd, 0xf9, 0xf6, 0xff, 0xfc, 0xfa, 0xf5, 0xff, 0xfc, 0xfa, 0xf5, 0xff, 0xfc, 0xfa, 0xf6, 0xff, 0xfd, 0xfa, 0xf6, 0xff, 0xfc, 0xfa, 0xf5, 0xff, 0xfc, 0xfa, 0xf6, 0xff, 0xfc, 0xf9, 0xf5, 0xff, 0xfd, 0xf9, 0xf6, 0xff, 0xfc, 0xf9, 0xf5, 0xff, 0xfc, 0xf9, 0xf5, 0xff, 0xfc, 0xf9, 0xf5, 0xff, 0xfd, 0xfa, 0xf6, 0xff, 0xfd, 0xfa, 0xf6, 0xff, 0xfd, 0xf9, 0xf5, 0xff, 0xfd, 0xfa, 0xf5, 0xff, 0xfc, 0xf9, 0xf5, 0xff, 0xfc, 0xf9, 0xf6, 0xff, 0xfc, 0xf9, 0xf5, 0xff, 0xfc, 0xf9, 0xf5, 0xff, 0xfc, 0xfa, 0xf5, 0xff, 0xfc, 0xfa, 0xf5, 0xff, 0xfc, 0xfa, 0xf5, 0xff, 0xfc, 0xf9, 0xf5, 0xff, 0xfc, 0xfa, 0xf5, 0xff, 0xfc, 0xfa, 0xf6, 0xff, 0xfd, 0xfb, 0xf6, 0xff, 0xfd, 0xfb, 0xf6, 0xff, 0xfe, 0xfc, 0xf7, 0xff, 0xfe, 0xfe, 0xf9, 0xff, + 0xfd, 0xfd, 0xf7, 0xff, 0xfd, 0xfd, 0xf7, 0xff, 0xfc, 0xfc, 0xf6, 0xff, 0xfb, 0xfb, 0xf5, 0xff, 0xf9, 0xf9, 0xf4, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf1, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf9, 0xf7, 0xf2, 0xff, 0xf9, 0xf8, 0xf2, 0xff, 0xf9, 0xf8, 0xf2, 0xff, 0xf9, 0xf8, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf8, 0xf9, 0xf3, 0xff, 0xf9, 0xf8, 0xf4, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xfa, 0xf8, 0xf3, 0xff, 0xfb, 0xf9, 0xf3, 0xff, 0xfb, 0xf9, 0xf4, 0xff, 0xfc, 0xf9, 0xf4, 0xff, 0xfb, 0xf9, 0xf4, 0xff, 0xfb, 0xf9, 0xf4, 0xff, 0xfb, 0xf9, 0xf4, 0xff, 0xfb, 0xf8, 0xf4, 0xff, 0xfb, 0xf7, 0xf3, 0xff, 0xfb, 0xf8, 0xf4, 0xff, 0xfb, 0xf9, 0xf4, 0xff, 0xfb, 0xf9, 0xf4, 0xff, 0xfb, 0xf9, 0xf4, 0xff, 0xfb, 0xf8, 0xf4, 0xff, 0xfb, 0xf8, 0xf4, 0xff, 0xfc, 0xfa, 0xf5, 0xff, 0xfb, 0xfa, 0xf5, 0xff, 0xfb, 0xfa, 0xf5, 0xff, 0xfb, 0xfa, 0xf5, 0xff, 0xfb, 0xf9, 0xf5, 0xff, 0xfb, 0xf9, 0xf4, 0xff, 0xfb, 0xf8, 0xf4, 0xff, 0xfa, 0xf8, 0xf4, 0xff, 0xfb, 0xf8, 0xf5, 0xff, 0xfb, 0xf9, 0xf5, 0xff, 0xfc, 0xfa, 0xf5, 0xff, 0xfb, 0xf9, 0xf4, 0xff, 0xfb, 0xf9, 0xf3, 0xff, 0xfb, 0xf8, 0xf3, 0xff, 0xfb, 0xf9, 0xf4, 0xff, 0xfb, 0xf9, 0xf4, 0xff, 0xfb, 0xf9, 0xf4, 0xff, 0xfb, 0xf9, 0xf4, 0xff, 0xfc, 0xf9, 0xf5, 0xff, 0xfc, 0xfa, 0xf5, 0xff, 0xfc, 0xfa, 0xf6, 0xff, 0xfc, 0xfa, 0xf5, 0xff, 0xfc, 0xf9, 0xf5, 0xff, 0xfc, 0xf9, 0xf5, 0xff, 0xfc, 0xf9, 0xf5, 0xff, 0xfc, 0xf9, 0xf5, 0xff, 0xfc, 0xf9, 0xf4, 0xff, 0xfb, 0xf9, 0xf4, 0xff, 0xfb, 0xf9, 0xf4, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xfb, 0xf9, 0xf4, 0xff, 0xfb, 0xf9, 0xf4, 0xff, 0xfc, 0xfa, 0xf5, 0xff, 0xfc, 0xfb, 0xf6, 0xff, 0xfc, 0xfb, 0xf6, 0xff, 0xfd, 0xfa, 0xf5, 0xff, 0xfd, 0xfa, 0xf6, 0xff, 0xfc, 0xf9, 0xf5, 0xff, 0xfb, 0xf9, 0xf5, 0xff, 0xfb, 0xf9, 0xf4, 0xff, 0xfb, 0xf9, 0xf5, 0xff, 0xfb, 0xfa, 0xf5, 0xff, 0xfc, 0xfa, 0xf5, 0xff, 0xfc, 0xf9, 0xf5, 0xff, 0xfc, 0xfa, 0xf5, 0xff, 0xfb, 0xf9, 0xf5, 0xff, 0xfb, 0xf9, 0xf5, 0xff, 0xfc, 0xf9, 0xf5, 0xff, 0xfd, 0xfa, 0xf5, 0xff, 0xfc, 0xfa, 0xf5, 0xff, 0xfc, 0xfa, 0xf6, 0xff, 0xfc, 0xfa, 0xf6, 0xff, 0xfc, 0xfa, 0xf5, 0xff, 0xfc, 0xfa, 0xf5, 0xff, 0xfc, 0xfa, 0xf5, 0xff, 0xfb, 0xf9, 0xf5, 0xff, 0xfb, 0xf9, 0xf4, 0xff, 0xfb, 0xfa, 0xf4, 0xff, 0xfa, 0xf9, 0xf5, 0xff, 0xfc, 0xf9, 0xf5, 0xff, 0xfd, 0xfa, 0xf6, 0xff, 0xfc, 0xfa, 0xf5, 0xff, 0xfb, 0xf9, 0xf5, 0xff, 0xfb, 0xf9, 0xf5, 0xff, 0xfc, 0xfa, 0xf5, 0xff, 0xfc, 0xf9, 0xf5, 0xff, 0xfc, 0xf9, 0xf5, 0xff, 0xfc, 0xf9, 0xf5, 0xff, 0xfb, 0xfa, 0xf5, 0xff, 0xfb, 0xfa, 0xf5, 0xff, 0xfb, 0xfa, 0xf4, 0xff, 0xfb, 0xfa, 0xf4, 0xff, 0xfb, 0xfa, 0xf5, 0xff, 0xfb, 0xfa, 0xf5, 0xff, 0xfb, 0xfb, 0xf5, 0xff, 0xfc, 0xfb, 0xf6, 0xff, 0xfe, 0xfc, 0xf7, 0xff, 0xfe, 0xfe, 0xf8, 0xff, + 0xfd, 0xfd, 0xf7, 0xff, 0xfd, 0xfd, 0xf7, 0xff, 0xfb, 0xfb, 0xf6, 0xff, 0xf9, 0xfa, 0xf5, 0xff, 0xf8, 0xf8, 0xf4, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf7, 0xf6, 0xf1, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf9, 0xf7, 0xf3, 0xff, 0xf9, 0xf7, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf6, 0xf2, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xfa, 0xf8, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf4, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf9, 0xf8, 0xf4, 0xff, 0xf9, 0xf8, 0xf4, 0xff, 0xfa, 0xf8, 0xf4, 0xff, 0xfa, 0xf8, 0xf4, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xfa, 0xf9, 0xf5, 0xff, 0xfa, 0xf9, 0xf5, 0xff, 0xf9, 0xf8, 0xf4, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf9, 0xf8, 0xf4, 0xff, 0xfa, 0xf9, 0xf5, 0xff, 0xfb, 0xf9, 0xf5, 0xff, 0xfb, 0xf9, 0xf5, 0xff, 0xfb, 0xf9, 0xf5, 0xff, 0xfa, 0xf8, 0xf4, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf9, 0xf7, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xfa, 0xf9, 0xf3, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf9, 0xf8, 0xf4, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xfb, 0xf9, 0xf5, 0xff, 0xfa, 0xf9, 0xf5, 0xff, 0xf9, 0xf8, 0xf4, 0xff, 0xf9, 0xf8, 0xf4, 0xff, 0xfa, 0xf8, 0xf4, 0xff, 0xfa, 0xf8, 0xf3, 0xff, 0xf9, 0xf8, 0xf4, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xfa, 0xf8, 0xf4, 0xff, 0xfa, 0xf8, 0xf5, 0xff, 0xf9, 0xf7, 0xf4, 0xff, 0xf9, 0xf7, 0xf3, 0xff, 0xf9, 0xf8, 0xf4, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf9, 0xf7, 0xf3, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xfb, 0xfb, 0xf5, 0xff, 0xfc, 0xfb, 0xf6, 0xff, 0xfe, 0xfb, 0xf6, 0xff, + 0xfd, 0xfd, 0xf7, 0xff, 0xfc, 0xfc, 0xf7, 0xff, 0xfa, 0xfb, 0xf6, 0xff, 0xf8, 0xf9, 0xf5, 0xff, 0xf6, 0xf8, 0xf3, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf6, 0xf6, 0xf4, 0xff, 0xf6, 0xf7, 0xf4, 0xff, 0xf6, 0xf7, 0xf4, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf6, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf7, 0xf6, 0xf1, 0xff, 0xf6, 0xf5, 0xf2, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf4, 0xff, 0xf9, 0xf9, 0xf4, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xf9, 0xf9, 0xf4, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf4, 0xff, 0xf9, 0xf8, 0xf4, 0xff, 0xf9, 0xf9, 0xf5, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xf9, 0xf9, 0xf4, 0xff, 0xf9, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xf9, 0xf9, 0xf4, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf8, 0xf8, 0xf4, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf9, 0xf8, 0xf4, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf9, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xf8, 0xf4, 0xff, 0xf9, 0xf8, 0xf4, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf6, 0xf7, 0xf2, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf6, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xfa, 0xf8, 0xf3, 0xff, 0xfb, 0xf9, 0xf4, 0xff, 0xfc, 0xfb, 0xf5, 0xff, 0xfe, 0xfb, 0xf6, 0xff, + 0xfd, 0xfd, 0xf6, 0xff, 0xfc, 0xfc, 0xf6, 0xff, 0xf9, 0xfa, 0xf6, 0xff, 0xf8, 0xf9, 0xf5, 0xff, 0xf6, 0xf8, 0xf4, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf6, 0xf7, 0xf2, 0xff, 0xf5, 0xf7, 0xf2, 0xff, 0xf6, 0xf7, 0xf2, 0xff, 0xf6, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf8, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf6, 0xf7, 0xf2, 0xff, 0xf6, 0xf7, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf5, 0xf6, 0xf1, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf5, 0xf6, 0xf4, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf6, 0xf6, 0xf4, 0xff, 0xf6, 0xf7, 0xf4, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf7, 0xf6, 0xf1, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf9, 0xf7, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf4, 0xf2, 0xff, 0xf5, 0xf5, 0xf3, 0xff, 0xf5, 0xf5, 0xf3, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xfa, 0xfa, 0xf3, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xf9, 0xf9, 0xf4, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf9, 0xf7, 0xf4, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xfb, 0xf9, 0xf4, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xf9, 0xf9, 0xf4, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf8, 0xf1, 0xff, 0xf9, 0xf8, 0xf2, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xf9, 0xf9, 0xf4, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf6, 0xf7, 0xf4, 0xff, 0xf7, 0xf7, 0xf4, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf9, 0xf7, 0xf2, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xfc, 0xfb, 0xf6, 0xff, 0xfe, 0xfc, 0xf8, 0xff, + 0xfc, 0xfc, 0xf7, 0xff, 0xfb, 0xfc, 0xf6, 0xff, 0xf9, 0xfa, 0xf5, 0xff, 0xf7, 0xf9, 0xf4, 0xff, 0xf7, 0xf8, 0xf4, 0xff, 0xf6, 0xf8, 0xf3, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf6, 0xf8, 0xf3, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf7, 0xf8, 0xf3, 0xff, 0xf7, 0xf8, 0xf2, 0xff, 0xf7, 0xf8, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf6, 0xf7, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf7, 0xf6, 0xf1, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf7, 0xf6, 0xf3, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf6, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf9, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf5, 0xf1, 0xff, 0xf7, 0xf6, 0xf1, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf8, 0xf1, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf6, 0xf7, 0xf2, 0xff, 0xf5, 0xf5, 0xf2, 0xff, 0xf6, 0xf4, 0xf1, 0xff, 0xf5, 0xf5, 0xf3, 0xff, 0xf6, 0xf5, 0xf3, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf9, 0xf7, 0xf3, 0xff, 0xfa, 0xf8, 0xf4, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xfa, 0xfa, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf9, 0xf7, 0xf3, 0xff, 0xfa, 0xf9, 0xf3, 0xff, 0xfa, 0xf9, 0xf3, 0xff, 0xfa, 0xf8, 0xf3, 0xff, 0xf9, 0xf8, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xfb, 0xf9, 0xf4, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xf9, 0xf9, 0xf4, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xfa, 0xf4, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf5, 0xf6, 0xf3, 0xff, 0xf6, 0xf6, 0xf4, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xfa, 0xfa, 0xf3, 0xff, 0xfc, 0xfb, 0xf6, 0xff, 0xfe, 0xfd, 0xf8, 0xff, + 0xfc, 0xfc, 0xf6, 0xff, 0xfb, 0xfc, 0xf6, 0xff, 0xf9, 0xfa, 0xf5, 0xff, 0xf7, 0xf9, 0xf5, 0xff, 0xf6, 0xf8, 0xf4, 0xff, 0xf6, 0xf8, 0xf4, 0xff, 0xf6, 0xf8, 0xf4, 0xff, 0xf6, 0xf8, 0xf4, 0xff, 0xf6, 0xf8, 0xf4, 0xff, 0xf7, 0xf8, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf7, 0xf8, 0xf2, 0xff, 0xf6, 0xf7, 0xf2, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf7, 0xf6, 0xf1, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf7, 0xf6, 0xf3, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf7, 0xf6, 0xf1, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf5, 0xf6, 0xf4, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf5, 0xf2, 0xff, 0xf5, 0xf4, 0xf2, 0xff, 0xf5, 0xf5, 0xf3, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf6, 0xf6, 0xf4, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf9, 0xf7, 0xf2, 0xff, 0xfa, 0xf8, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf9, 0xf9, 0xf4, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf9, 0xf8, 0xf2, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf9, 0xf7, 0xf2, 0xff, 0xf9, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xfb, 0xf9, 0xf4, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf7, 0xf1, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xfa, 0xf9, 0xf3, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xf9, 0xf9, 0xf4, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xfb, 0xfa, 0xf4, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xfa, 0xf8, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf5, 0xf5, 0xf3, 0xff, 0xf5, 0xf6, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xfb, 0xfa, 0xf5, 0xff, 0xfe, 0xfc, 0xf7, 0xff, + 0xfc, 0xfc, 0xf6, 0xff, 0xfb, 0xfc, 0xf6, 0xff, 0xf9, 0xfa, 0xf6, 0xff, 0xf8, 0xf9, 0xf5, 0xff, 0xf7, 0xf9, 0xf4, 0xff, 0xf7, 0xf8, 0xf4, 0xff, 0xf6, 0xf8, 0xf4, 0xff, 0xf7, 0xf8, 0xf4, 0xff, 0xf7, 0xf8, 0xf4, 0xff, 0xf7, 0xf9, 0xf4, 0xff, 0xf8, 0xf9, 0xf3, 0xff, 0xf8, 0xf9, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf7, 0xf8, 0xf2, 0xff, 0xf6, 0xf7, 0xf2, 0xff, 0xf5, 0xf7, 0xf1, 0xff, 0xf5, 0xf6, 0xf1, 0xff, 0xf5, 0xf6, 0xf1, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf7, 0xf6, 0xf1, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf6, 0xf1, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xfa, 0xf7, 0xf3, 0xff, 0xfa, 0xf8, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf7, 0xf6, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf5, 0xf5, 0xf2, 0xff, 0xf5, 0xf5, 0xf3, 0xff, 0xf5, 0xf5, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xfa, 0xf8, 0xf3, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xf9, 0xf9, 0xf4, 0xff, 0xf9, 0xf9, 0xf4, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xfa, 0xf8, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xfa, 0xfa, 0xf3, 0xff, 0xfb, 0xfa, 0xf4, 0xff, 0xfa, 0xfa, 0xf5, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xfb, 0xfb, 0xf5, 0xff, 0xfd, 0xfc, 0xf7, 0xff, + 0xfc, 0xfd, 0xf7, 0xff, 0xfb, 0xfc, 0xf6, 0xff, 0xf9, 0xfb, 0xf5, 0xff, 0xf8, 0xfa, 0xf5, 0xff, 0xf7, 0xf9, 0xf4, 0xff, 0xf7, 0xf9, 0xf4, 0xff, 0xf7, 0xf8, 0xf4, 0xff, 0xf7, 0xf8, 0xf4, 0xff, 0xf7, 0xf8, 0xf4, 0xff, 0xf7, 0xf8, 0xf4, 0xff, 0xf7, 0xf9, 0xf4, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf7, 0xf8, 0xf3, 0xff, 0xf7, 0xf8, 0xf2, 0xff, 0xf7, 0xf8, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf5, 0xf6, 0xf1, 0xff, 0xf5, 0xf6, 0xf1, 0xff, 0xf5, 0xf6, 0xf1, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf9, 0xf7, 0xf2, 0xff, 0xf9, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf6, 0xf7, 0xf2, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf5, 0xf6, 0xf4, 0xff, 0xf6, 0xf7, 0xf4, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf5, 0xf6, 0xf3, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf9, 0xf9, 0xf4, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xf9, 0xf9, 0xf4, 0xff, 0xf9, 0xf9, 0xf4, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xf8, 0xf4, 0xff, 0xf9, 0xf9, 0xf4, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xfa, 0xf9, 0xf3, 0xff, 0xfa, 0xf9, 0xf3, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf9, 0xf7, 0xf2, 0xff, 0xfa, 0xf8, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xf9, 0xf9, 0xf4, 0xff, 0xfa, 0xf9, 0xf3, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xf9, 0xf9, 0xf4, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xf9, 0xfa, 0xf4, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf7, 0xf8, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xfb, 0xfb, 0xf5, 0xff, 0xfe, 0xfc, 0xf7, 0xff, + 0xfc, 0xfc, 0xf6, 0xff, 0xfb, 0xfc, 0xf6, 0xff, 0xfa, 0xfb, 0xf6, 0xff, 0xf8, 0xfa, 0xf5, 0xff, 0xf7, 0xf9, 0xf4, 0xff, 0xf7, 0xf8, 0xf4, 0xff, 0xf7, 0xf8, 0xf4, 0xff, 0xf7, 0xf8, 0xf4, 0xff, 0xf7, 0xf8, 0xf4, 0xff, 0xf7, 0xf9, 0xf4, 0xff, 0xf7, 0xf8, 0xf3, 0xff, 0xf7, 0xf8, 0xf3, 0xff, 0xf6, 0xf8, 0xf2, 0xff, 0xf6, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf6, 0xf7, 0xf1, 0xff, 0xf5, 0xf6, 0xf1, 0xff, 0xf5, 0xf6, 0xf1, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf6, 0xf7, 0xf1, 0xff, 0xf7, 0xf8, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf7, 0xf6, 0xf3, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf9, 0xf8, 0xf2, 0xff, 0xf8, 0xf8, 0xf1, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xfa, 0xfa, 0xf3, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf7, 0xf7, 0xf4, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xfa, 0xf9, 0xf3, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xf9, 0xf9, 0xf4, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xfa, 0xf9, 0xf3, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf9, 0xf7, 0xf2, 0xff, 0xf9, 0xf7, 0xf2, 0xff, 0xfa, 0xf8, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xfa, 0xf8, 0xf2, 0xff, 0xfa, 0xf8, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xfc, 0xfb, 0xf5, 0xff, 0xfe, 0xfc, 0xf7, 0xff, + 0xfc, 0xfc, 0xf6, 0xff, 0xfb, 0xfc, 0xf6, 0xff, 0xf9, 0xfb, 0xf6, 0xff, 0xf8, 0xfa, 0xf5, 0xff, 0xf7, 0xf8, 0xf4, 0xff, 0xf6, 0xf8, 0xf4, 0xff, 0xf6, 0xf8, 0xf3, 0xff, 0xf6, 0xf8, 0xf3, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf6, 0xf8, 0xf3, 0xff, 0xf6, 0xf8, 0xf3, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf5, 0xf7, 0xf2, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf5, 0xf6, 0xf1, 0xff, 0xf5, 0xf6, 0xf1, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf5, 0xf6, 0xf1, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf6, 0xf7, 0xf2, 0xff, 0xf7, 0xf8, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf5, 0xf4, 0xf1, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf9, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf9, 0xf9, 0xf4, 0xff, 0xf7, 0xf7, 0xf4, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xfa, 0xf8, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xfa, 0xf9, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf6, 0xf7, 0xf1, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf8, 0xf6, 0xf1, 0xff, 0xf8, 0xf6, 0xf1, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xfc, 0xfb, 0xf5, 0xff, 0xfe, 0xfb, 0xf7, 0xff, + 0xfc, 0xfc, 0xf6, 0xff, 0xfb, 0xfc, 0xf6, 0xff, 0xf9, 0xfa, 0xf6, 0xff, 0xf8, 0xf9, 0xf5, 0xff, 0xf7, 0xf8, 0xf4, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf5, 0xf7, 0xf3, 0xff, 0xf5, 0xf7, 0xf2, 0xff, 0xf6, 0xf7, 0xf2, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf6, 0xf7, 0xf2, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf4, 0xf5, 0xf1, 0xff, 0xf4, 0xf5, 0xf1, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf6, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf7, 0xf6, 0xf1, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf1, 0xff, 0xf8, 0xf7, 0xf1, 0xff, 0xf9, 0xf7, 0xf2, 0xff, 0xf8, 0xf8, 0xf1, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf6, 0xf5, 0xf0, 0xff, 0xf7, 0xf5, 0xf1, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf6, 0xf5, 0xf2, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf8, 0xf4, 0xff, 0xfa, 0xf9, 0xf5, 0xff, 0xfc, 0xfb, 0xf6, 0xff, 0xfe, 0xfb, 0xf7, 0xff, + 0xfc, 0xfb, 0xf6, 0xff, 0xfb, 0xfb, 0xf5, 0xff, 0xfa, 0xfa, 0xf5, 0xff, 0xf7, 0xf9, 0xf4, 0xff, 0xf6, 0xf8, 0xf3, 0xff, 0xf6, 0xf7, 0xf2, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf5, 0xf6, 0xf1, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf7, 0xf5, 0xf0, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf6, 0xf1, 0xff, 0xf8, 0xf6, 0xf1, 0xff, 0xf8, 0xf7, 0xf1, 0xff, 0xf9, 0xf7, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xfa, 0xf9, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf5, 0xf4, 0xf1, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf7, 0xf6, 0xf1, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf7, 0xf6, 0xf3, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf7, 0xf6, 0xf3, 0xff, 0xf6, 0xf5, 0xf2, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf5, 0xf5, 0xf2, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf5, 0xf5, 0xf2, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf4, 0xf5, 0xf1, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf4, 0xf5, 0xf1, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf7, 0xf7, 0xf4, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xfc, 0xfb, 0xf6, 0xff, 0xfd, 0xfb, 0xf7, 0xff, + 0xfc, 0xfb, 0xf6, 0xff, 0xfb, 0xfa, 0xf5, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xf7, 0xf8, 0xf4, 0xff, 0xf6, 0xf7, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf6, 0xf5, 0xf0, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf6, 0xf2, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf8, 0xf1, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf9, 0xf8, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf1, 0xff, 0xf8, 0xf7, 0xf1, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf9, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf7, 0xf8, 0xf2, 0xff, 0xf8, 0xf7, 0xf1, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf6, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf6, 0xf1, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf7, 0xf6, 0xf1, 0xff, 0xf6, 0xf7, 0xf1, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf5, 0xf2, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf7, 0xf6, 0xf1, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xfa, 0xfa, 0xf3, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf5, 0xf6, 0xf1, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf5, 0xf5, 0xf2, 0xff, 0xf5, 0xf4, 0xf1, 0xff, 0xf5, 0xf4, 0xf1, 0xff, 0xf3, 0xf4, 0xf2, 0xff, 0xf4, 0xf4, 0xf1, 0xff, 0xf6, 0xf5, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf6, 0xf7, 0xf4, 0xff, 0xf5, 0xf7, 0xf3, 0xff, 0xf5, 0xf6, 0xf4, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xfc, 0xfb, 0xf6, 0xff, 0xfe, 0xfb, 0xf7, 0xff, + 0xfc, 0xfa, 0xf5, 0xff, 0xfb, 0xfa, 0xf5, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xf6, 0xf7, 0xf4, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf8, 0xf9, 0xf3, 0xff, 0xf7, 0xf8, 0xf3, 0xff, 0xf7, 0xf8, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf5, 0xf6, 0xf1, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf5, 0xf5, 0xf0, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xfa, 0xf8, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xfa, 0xf7, 0xf3, 0xff, 0xfa, 0xf9, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf7, 0xf5, 0xf1, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xfa, 0xfa, 0xf3, 0xff, 0xf9, 0xfa, 0xf4, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf7, 0xf6, 0xf1, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf3, 0xf5, 0xf1, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf5, 0xf5, 0xf3, 0xff, 0xf5, 0xf6, 0xf3, 0xff, 0xf5, 0xf5, 0xf2, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xfc, 0xfb, 0xf6, 0xff, 0xfd, 0xfc, 0xf7, 0xff, + 0xfc, 0xfa, 0xf5, 0xff, 0xfb, 0xfb, 0xf5, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf5, 0xf6, 0xf4, 0xff, 0xf5, 0xf5, 0xf2, 0xff, 0xf6, 0xf5, 0xf0, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf1, 0xff, 0xf9, 0xf7, 0xf1, 0xff, 0xfa, 0xf8, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf6, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xf9, 0xf4, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf6, 0xf1, 0xff, 0xf8, 0xf7, 0xf1, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf9, 0xf8, 0xf4, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf7, 0xf1, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf4, 0xf5, 0xf1, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf6, 0xf1, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf4, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xfa, 0xf9, 0xf3, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xfb, 0xfb, 0xf5, 0xff, 0xfd, 0xfc, 0xf7, 0xff, + 0xfc, 0xfa, 0xf5, 0xff, 0xfb, 0xfb, 0xf5, 0xff, 0xf8, 0xf8, 0xf4, 0xff, 0xf4, 0xf6, 0xf4, 0xff, 0xf5, 0xf5, 0xf2, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf9, 0xf8, 0xf2, 0xff, 0xf9, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf1, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xf9, 0xf4, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf9, 0xf8, 0xf2, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf6, 0xf1, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf6, 0xf7, 0xf1, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf6, 0xf1, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf7, 0xf6, 0xf1, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf4, 0xff, 0xf9, 0xf8, 0xf4, 0xff, 0xf8, 0xf8, 0xf4, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xf9, 0xf4, 0xff, 0xf8, 0xf7, 0xf4, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf8, 0xf4, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xfc, 0xfb, 0xf5, 0xff, 0xfd, 0xfc, 0xf7, 0xff, + 0xfd, 0xfa, 0xf5, 0xff, 0xfc, 0xfb, 0xf6, 0xff, 0xf9, 0xf9, 0xf4, 0xff, 0xf5, 0xf6, 0xf4, 0xff, 0xf5, 0xf5, 0xf2, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf7, 0xf6, 0xf3, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf7, 0xf6, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf9, 0xf7, 0xf2, 0xff, 0xf9, 0xf8, 0xf2, 0xff, 0xfa, 0xf8, 0xf3, 0xff, 0xf9, 0xf7, 0xf3, 0xff, 0xf9, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf9, 0xf7, 0xf2, 0xff, 0xfa, 0xf8, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf9, 0xf7, 0xf2, 0xff, 0xfa, 0xf8, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xfa, 0xf9, 0xf3, 0xff, 0xfa, 0xf9, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf9, 0xf9, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf9, 0xf7, 0xf2, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf5, 0xf6, 0xf3, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf6, 0xf7, 0xf4, 0xff, 0xf6, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf7, 0xf6, 0xf1, 0xff, 0xf5, 0xf6, 0xf1, 0xff, 0xf7, 0xf6, 0xf1, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf6, 0xf7, 0xf1, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf7, 0xf1, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf8, 0xf8, 0xf4, 0xff, 0xf8, 0xf8, 0xf4, 0xff, 0xf7, 0xf7, 0xf4, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xf9, 0xf4, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xfc, 0xfb, 0xf5, 0xff, 0xfd, 0xfb, 0xf6, 0xff, + 0xfd, 0xfb, 0xf5, 0xff, 0xfc, 0xfb, 0xf5, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf4, 0xf4, 0xf1, 0xff, 0xf4, 0xf4, 0xf1, 0xff, 0xf5, 0xf5, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf9, 0xf8, 0xf2, 0xff, 0xf9, 0xf8, 0xf2, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf9, 0xf8, 0xf2, 0xff, 0xfa, 0xf8, 0xf2, 0xff, 0xfa, 0xf9, 0xf3, 0xff, 0xfa, 0xf8, 0xf3, 0xff, 0xfa, 0xf7, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf9, 0xf8, 0xf2, 0xff, 0xf9, 0xf8, 0xf2, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xf8, 0xf2, 0xff, 0xf9, 0xf7, 0xf2, 0xff, 0xfa, 0xf8, 0xf2, 0xff, 0xfa, 0xf8, 0xf3, 0xff, 0xfa, 0xf8, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xfa, 0xf8, 0xf2, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf9, 0xf8, 0xf2, 0xff, 0xf9, 0xf7, 0xf2, 0xff, 0xf9, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf8, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf9, 0xf8, 0xf2, 0xff, 0xf9, 0xf7, 0xf2, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf6, 0xf5, 0xf2, 0xff, 0xf5, 0xf4, 0xf2, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf6, 0xf7, 0xf2, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf6, 0xf7, 0xf4, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf5, 0xf6, 0xf1, 0xff, 0xf5, 0xf5, 0xf2, 0xff, 0xf5, 0xf5, 0xf2, 0xff, 0xf5, 0xf6, 0xf1, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf6, 0xf7, 0xf1, 0xff, 0xf5, 0xf6, 0xf1, 0xff, 0xf5, 0xf5, 0xf2, 0xff, 0xf5, 0xf5, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf6, 0xf7, 0xf4, 0xff, 0xf6, 0xf7, 0xf5, 0xff, 0xf7, 0xf7, 0xf4, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf4, 0xff, 0xf5, 0xf7, 0xf4, 0xff, 0xf5, 0xf7, 0xf4, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xfb, 0xfb, 0xf5, 0xff, 0xfd, 0xfb, 0xf6, 0xff, + 0xfd, 0xfb, 0xf5, 0xff, 0xfb, 0xfa, 0xf5, 0xff, 0xf9, 0xf7, 0xf2, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf6, 0xf7, 0xf4, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf4, 0xf5, 0xf1, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf4, 0xf5, 0xf1, 0xff, 0xf7, 0xf6, 0xf1, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xfa, 0xf9, 0xf3, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xfb, 0xf9, 0xf4, 0xff, 0xfb, 0xfa, 0xf6, 0xff, 0xfb, 0xfa, 0xf7, 0xff, 0xfa, 0xfb, 0xf8, 0xff, 0xfb, 0xfa, 0xf8, 0xff, 0xfb, 0xfa, 0xf8, 0xff, 0xfb, 0xfa, 0xf7, 0xff, 0xfa, 0xfa, 0xf7, 0xff, 0xfa, 0xfb, 0xf8, 0xff, 0xfa, 0xfb, 0xf8, 0xff, 0xfa, 0xfb, 0xf8, 0xff, 0xfa, 0xfb, 0xf8, 0xff, 0xfa, 0xfb, 0xf8, 0xff, 0xfa, 0xfb, 0xf8, 0xff, 0xfa, 0xfb, 0xf8, 0xff, 0xfb, 0xfb, 0xf8, 0xff, 0xfb, 0xfb, 0xf8, 0xff, 0xfc, 0xfb, 0xf9, 0xff, 0xfc, 0xfc, 0xf9, 0xff, 0xfc, 0xfc, 0xf9, 0xff, 0xfb, 0xfb, 0xf8, 0xff, 0xfb, 0xfa, 0xf8, 0xff, 0xfb, 0xfa, 0xf8, 0xff, 0xfb, 0xfa, 0xf8, 0xff, 0xfc, 0xfa, 0xf8, 0xff, 0xfb, 0xfa, 0xf8, 0xff, 0xfb, 0xfa, 0xf8, 0xff, 0xfb, 0xfa, 0xf7, 0xff, 0xfb, 0xfa, 0xf8, 0xff, 0xfb, 0xfa, 0xf8, 0xff, 0xfa, 0xfa, 0xf8, 0xff, 0xfa, 0xfa, 0xf8, 0xff, 0xfa, 0xfa, 0xf8, 0xff, 0xfa, 0xfa, 0xf7, 0xff, 0xfb, 0xf9, 0xf6, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf7, 0xf8, 0xf3, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf5, 0xf2, 0xff, 0xf6, 0xf5, 0xf2, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf5, 0xf6, 0xf1, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf4, 0xf5, 0xf1, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf5, 0xf6, 0xf1, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf5, 0xf6, 0xf5, 0xff, 0xf6, 0xf6, 0xf5, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf6, 0xf7, 0xf5, 0xff, 0xf5, 0xf6, 0xf5, 0xff, 0xf5, 0xf7, 0xf4, 0xff, 0xf7, 0xf7, 0xf4, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xfb, 0xfb, 0xf5, 0xff, 0xfd, 0xfb, 0xf6, 0xff, + 0xfe, 0xfa, 0xf5, 0xff, 0xfb, 0xfb, 0xf5, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf4, 0xff, 0xf5, 0xf6, 0xf4, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf4, 0xf5, 0xf1, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf7, 0xf6, 0xf1, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf9, 0xf8, 0xf2, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xfb, 0xf9, 0xf4, 0xff, 0xfc, 0xfb, 0xfa, 0xff, 0xfd, 0xfe, 0xff, 0xff, 0xfd, 0xfd, 0xfd, 0xff, 0xfc, 0xfa, 0xf2, 0xff, 0xfb, 0xf8, 0xe6, 0xff, 0xfb, 0xf6, 0xe2, 0xff, 0xfb, 0xf6, 0xe3, 0xff, 0xfb, 0xf6, 0xe2, 0xff, 0xfb, 0xf5, 0xe3, 0xff, 0xfb, 0xf5, 0xe3, 0xff, 0xfb, 0xf6, 0xe3, 0xff, 0xfb, 0xf6, 0xe3, 0xff, 0xfc, 0xf7, 0xe3, 0xff, 0xfc, 0xf7, 0xe3, 0xff, 0xfc, 0xf7, 0xe4, 0xff, 0xfc, 0xf7, 0xe3, 0xff, 0xfc, 0xf7, 0xe4, 0xff, 0xfb, 0xf6, 0xe3, 0xff, 0xfc, 0xf6, 0xe3, 0xff, 0xfc, 0xf7, 0xe3, 0xff, 0xfc, 0xf7, 0xe3, 0xff, 0xfb, 0xf7, 0xe4, 0xff, 0xfc, 0xf7, 0xe4, 0xff, 0xfc, 0xf7, 0xe3, 0xff, 0xfc, 0xf6, 0xe3, 0xff, 0xfc, 0xf7, 0xe3, 0xff, 0xfb, 0xf7, 0xe2, 0xff, 0xfb, 0xf6, 0xe3, 0xff, 0xfb, 0xf6, 0xe3, 0xff, 0xfb, 0xf6, 0xe3, 0xff, 0xfc, 0xf6, 0xe2, 0xff, 0xfb, 0xf7, 0xe2, 0xff, 0xfb, 0xf6, 0xe2, 0xff, 0xfb, 0xf6, 0xe1, 0xff, 0xfa, 0xf5, 0xe0, 0xff, 0xfa, 0xf6, 0xe2, 0xff, 0xfb, 0xf9, 0xed, 0xff, 0xfc, 0xfc, 0xf9, 0xff, 0xfb, 0xfd, 0xff, 0xff, 0xfa, 0xfc, 0xfc, 0xff, 0xf9, 0xfa, 0xf5, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf7, 0xf6, 0xf1, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xf9, 0xf9, 0xf4, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf8, 0xf7, 0xf1, 0xff, 0xf7, 0xf6, 0xf1, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf6, 0xf5, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf5, 0xf6, 0xf1, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf5, 0xf6, 0xf3, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf6, 0xf7, 0xf4, 0xff, 0xf6, 0xf6, 0xf5, 0xff, 0xf7, 0xf7, 0xf4, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf6, 0xf7, 0xf4, 0xff, 0xf5, 0xf6, 0xf5, 0xff, 0xf5, 0xf7, 0xf4, 0xff, 0xf6, 0xf6, 0xf4, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xfc, 0xfa, 0xf5, 0xff, 0xfd, 0xfc, 0xf6, 0xff, + 0xfd, 0xfb, 0xf6, 0xff, 0xfb, 0xfb, 0xf5, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf5, 0xf5, 0xf2, 0xff, 0xf4, 0xf5, 0xf3, 0xff, 0xf4, 0xf5, 0xf3, 0xff, 0xf4, 0xf4, 0xf1, 0xff, 0xf4, 0xf4, 0xf3, 0xff, 0xf5, 0xf5, 0xf2, 0xff, 0xf9, 0xf7, 0xf2, 0xff, 0xfa, 0xf9, 0xf3, 0xff, 0xfa, 0xf9, 0xf3, 0xff, 0xfb, 0xfb, 0xf9, 0xff, 0xfd, 0xfd, 0xff, 0xff, 0xfc, 0xf5, 0xda, 0xff, 0xfc, 0xe4, 0x8e, 0xff, 0xfc, 0xd4, 0x4f, 0xff, 0xfc, 0xca, 0x2c, 0xff, 0xfc, 0xc5, 0x19, 0xff, 0xfc, 0xc5, 0x14, 0xff, 0xfc, 0xc5, 0x15, 0xff, 0xfc, 0xc6, 0x15, 0xff, 0xfc, 0xc6, 0x16, 0xff, 0xfc, 0xc6, 0x16, 0xff, 0xfc, 0xc5, 0x16, 0xff, 0xfc, 0xc5, 0x16, 0xff, 0xfc, 0xc5, 0x16, 0xff, 0xfc, 0xc5, 0x16, 0xff, 0xfc, 0xc6, 0x15, 0xff, 0xfc, 0xc7, 0x15, 0xff, 0xfc, 0xc7, 0x16, 0xff, 0xfc, 0xc6, 0x17, 0xff, 0xfc, 0xc6, 0x17, 0xff, 0xfc, 0xc6, 0x17, 0xff, 0xfb, 0xc7, 0x17, 0xff, 0xfc, 0xc6, 0x18, 0xff, 0xfc, 0xc6, 0x18, 0xff, 0xfc, 0xc8, 0x17, 0xff, 0xfc, 0xc7, 0x17, 0xff, 0xfc, 0xc4, 0x16, 0xff, 0xfb, 0xc3, 0x15, 0xff, 0xfc, 0xc3, 0x15, 0xff, 0xfc, 0xc6, 0x16, 0xff, 0xfd, 0xc6, 0x17, 0xff, 0xfc, 0xc5, 0x15, 0xff, 0xfc, 0xc5, 0x15, 0xff, 0xfc, 0xc5, 0x15, 0xff, 0xfc, 0xc4, 0x14, 0xff, 0xfb, 0xc3, 0x13, 0xff, 0xfb, 0xc3, 0x16, 0xff, 0xfb, 0xc8, 0x24, 0xff, 0xfc, 0xd0, 0x3e, 0xff, 0xfb, 0xdc, 0x74, 0xff, 0xfb, 0xed, 0xbd, 0xff, 0xfa, 0xfb, 0xf6, 0xff, 0xfa, 0xfc, 0xfe, 0xff, 0xfa, 0xfa, 0xf5, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xf9, 0xfa, 0xf4, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf5, 0xf6, 0xf1, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf5, 0xf5, 0xf2, 0xff, 0xf5, 0xf6, 0xf1, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf5, 0xf5, 0xf2, 0xff, 0xf5, 0xf6, 0xf3, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf6, 0xf7, 0xf4, 0xff, 0xf6, 0xf6, 0xf4, 0xff, 0xf6, 0xf7, 0xf6, 0xff, 0xf7, 0xf7, 0xf4, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf7, 0xf7, 0xf4, 0xff, 0xf6, 0xf7, 0xf4, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xfc, 0xfa, 0xf5, 0xff, 0xfd, 0xfc, 0xf7, 0xff, + 0xfd, 0xfb, 0xf6, 0xff, 0xfb, 0xfb, 0xf5, 0xff, 0xf9, 0xf9, 0xf4, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf5, 0xf6, 0xf1, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf5, 0xf5, 0xf2, 0xff, 0xf4, 0xf5, 0xf3, 0xff, 0xf4, 0xf5, 0xf3, 0xff, 0xf5, 0xf5, 0xf2, 0xff, 0xf5, 0xf5, 0xf3, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xfb, 0xf9, 0xf4, 0xff, 0xfd, 0xfd, 0xff, 0xff, 0xfc, 0xf6, 0xe1, 0xff, 0xfb, 0xda, 0x6d, 0xff, 0xfc, 0xc3, 0x0f, 0xff, 0xfd, 0xba, 0x00, 0xff, 0xfd, 0xbb, 0x00, 0xff, 0xfd, 0xbc, 0x00, 0xff, 0xfd, 0xbd, 0x00, 0xff, 0xfd, 0xbe, 0x00, 0xff, 0xfd, 0xbd, 0x00, 0xff, 0xfd, 0xbd, 0x00, 0xff, 0xfd, 0xbe, 0x00, 0xff, 0xfd, 0xbf, 0x00, 0xff, 0xfe, 0xbf, 0x00, 0xff, 0xfe, 0xc0, 0x00, 0xff, 0xfd, 0xbf, 0x00, 0xff, 0xfe, 0xbf, 0x00, 0xff, 0xfd, 0xc1, 0x00, 0xff, 0xfd, 0xc1, 0x00, 0xff, 0xfd, 0xc0, 0x00, 0xff, 0xfe, 0xbf, 0x00, 0xff, 0xfd, 0xbf, 0x00, 0xff, 0xfd, 0xbf, 0x00, 0xff, 0xfd, 0xc0, 0x01, 0xff, 0xfd, 0xbf, 0x01, 0xff, 0xfd, 0xbf, 0x00, 0xff, 0xfd, 0xbf, 0x00, 0xff, 0xfd, 0xbe, 0x00, 0xff, 0xfe, 0xbe, 0x00, 0xff, 0xfe, 0xbe, 0x00, 0xff, 0xfe, 0xbe, 0x00, 0xff, 0xfd, 0xbf, 0x00, 0xff, 0xfe, 0xbf, 0x00, 0xff, 0xfd, 0xbd, 0x00, 0xff, 0xfd, 0xbe, 0x00, 0xff, 0xfe, 0xbe, 0x00, 0xff, 0xfe, 0xbf, 0x00, 0xff, 0xfd, 0xbd, 0x00, 0xff, 0xfc, 0xbb, 0x00, 0xff, 0xfd, 0xbb, 0x00, 0xff, 0xfd, 0xbc, 0x00, 0xff, 0xfd, 0xbb, 0x00, 0xff, 0xfc, 0xbb, 0x00, 0xff, 0xfb, 0xcc, 0x3b, 0xff, 0xfb, 0xec, 0xb4, 0xff, 0xfc, 0xfd, 0xfe, 0xff, 0xfc, 0xfc, 0xf9, 0xff, 0xfb, 0xfa, 0xf5, 0xff, 0xfa, 0xf8, 0xf3, 0xff, 0xf9, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf7, 0xf6, 0xf1, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf5, 0xf6, 0xf3, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf5, 0xf2, 0xff, 0xf5, 0xf5, 0xf3, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf6, 0xf1, 0xff, 0xf5, 0xf5, 0xf2, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf5, 0xf6, 0xf3, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf5, 0xf5, 0xf2, 0xff, 0xf5, 0xf5, 0xf3, 0xff, 0xf5, 0xf6, 0xf4, 0xff, 0xf6, 0xf7, 0xf4, 0xff, 0xf7, 0xf7, 0xf4, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf9, 0xf8, 0xf4, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xfa, 0xf8, 0xf3, 0xff, 0xfc, 0xfa, 0xf5, 0xff, 0xfd, 0xfc, 0xf7, 0xff, + 0xfd, 0xfb, 0xf6, 0xff, 0xfc, 0xfb, 0xf5, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf4, 0xf5, 0xf4, 0xff, 0xf6, 0xf7, 0xf4, 0xff, 0xf6, 0xf7, 0xf4, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xfd, 0xfd, 0xff, 0xff, 0xfc, 0xeb, 0xb6, 0xff, 0xfc, 0xc4, 0x1f, 0xff, 0xfd, 0xba, 0x00, 0xff, 0xfd, 0xbc, 0x00, 0xff, 0xfd, 0xbd, 0x02, 0xff, 0xfd, 0xbd, 0x03, 0xff, 0xfd, 0xbd, 0x04, 0xff, 0xfd, 0xbd, 0x04, 0xff, 0xfd, 0xbd, 0x04, 0xff, 0xfd, 0xbd, 0x04, 0xff, 0xfd, 0xbd, 0x04, 0xff, 0xfd, 0xbd, 0x04, 0xff, 0xfd, 0xbe, 0x02, 0xff, 0xfd, 0xbe, 0x03, 0xff, 0xfd, 0xbf, 0x02, 0xff, 0xfd, 0xbe, 0x03, 0xff, 0xfd, 0xbe, 0x03, 0xff, 0xfd, 0xbf, 0x04, 0xff, 0xfd, 0xbf, 0x05, 0xff, 0xfe, 0xbf, 0x05, 0xff, 0xfe, 0xbe, 0x06, 0xff, 0xfe, 0xbf, 0x05, 0xff, 0xfd, 0xc0, 0x05, 0xff, 0xfd, 0xc1, 0x06, 0xff, 0xfd, 0xbf, 0x07, 0xff, 0xfe, 0xbe, 0x06, 0xff, 0xfe, 0xbf, 0x05, 0xff, 0xfd, 0xbe, 0x05, 0xff, 0xfe, 0xbe, 0x04, 0xff, 0xfe, 0xbf, 0x03, 0xff, 0xfd, 0xbe, 0x03, 0xff, 0xfd, 0xbe, 0x04, 0xff, 0xfe, 0xbe, 0x04, 0xff, 0xfd, 0xbd, 0x04, 0xff, 0xfd, 0xbd, 0x04, 0xff, 0xfd, 0xbe, 0x05, 0xff, 0xfd, 0xbf, 0x04, 0xff, 0xfd, 0xbe, 0x04, 0xff, 0xfd, 0xbe, 0x04, 0xff, 0xfc, 0xbd, 0x03, 0xff, 0xfd, 0xbd, 0x02, 0xff, 0xfd, 0xbd, 0x02, 0xff, 0xfd, 0xbb, 0x00, 0xff, 0xfd, 0xbb, 0x00, 0xff, 0xfd, 0xbe, 0x00, 0xff, 0xfb, 0xdb, 0x6d, 0xff, 0xfd, 0xfb, 0xf4, 0xff, 0xfd, 0xfc, 0xfa, 0xff, 0xfb, 0xfa, 0xf4, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf6, 0xf5, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf5, 0xf6, 0xf3, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf5, 0xf6, 0xf3, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf5, 0xf5, 0xf3, 0xff, 0xf5, 0xf5, 0xf2, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf5, 0xf6, 0xf3, 0xff, 0xf5, 0xf6, 0xf4, 0xff, 0xf5, 0xf5, 0xf4, 0xff, 0xf5, 0xf6, 0xf4, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf9, 0xf9, 0xf4, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xfb, 0xfb, 0xf5, 0xff, 0xfd, 0xfc, 0xf7, 0xff, + 0xfd, 0xfb, 0xf6, 0xff, 0xfb, 0xfb, 0xf5, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf6, 0xf1, 0xff, 0xf5, 0xf6, 0xf1, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf6, 0xf7, 0xf4, 0xff, 0xf8, 0xf8, 0xf4, 0xff, 0xfa, 0xf8, 0xf3, 0xff, 0xfc, 0xf9, 0xf4, 0xff, 0xfc, 0xfd, 0xff, 0xff, 0xfc, 0xe6, 0xa2, 0xff, 0xfc, 0xbd, 0x05, 0xff, 0xfd, 0xba, 0x00, 0xff, 0xfd, 0xbd, 0x04, 0xff, 0xfd, 0xbd, 0x04, 0xff, 0xfd, 0xbd, 0x05, 0xff, 0xfd, 0xbc, 0x06, 0xff, 0xfd, 0xbd, 0x06, 0xff, 0xfd, 0xbd, 0x06, 0xff, 0xfd, 0xbd, 0x05, 0xff, 0xfd, 0xbd, 0x04, 0xff, 0xfd, 0xbd, 0x05, 0xff, 0xfd, 0xbd, 0x05, 0xff, 0xfd, 0xbd, 0x04, 0xff, 0xfd, 0xbd, 0x04, 0xff, 0xfd, 0xbd, 0x04, 0xff, 0xfd, 0xbd, 0x05, 0xff, 0xfd, 0xbd, 0x05, 0xff, 0xfd, 0xbe, 0x05, 0xff, 0xfd, 0xbe, 0x06, 0xff, 0xfd, 0xbd, 0x06, 0xff, 0xfd, 0xbd, 0x06, 0xff, 0xfd, 0xbd, 0x06, 0xff, 0xfd, 0xbe, 0x06, 0xff, 0xfd, 0xc0, 0x07, 0xff, 0xfd, 0xbf, 0x08, 0xff, 0xfd, 0xbe, 0x08, 0xff, 0xfd, 0xbe, 0x07, 0xff, 0xfd, 0xbe, 0x07, 0xff, 0xfd, 0xbd, 0x06, 0xff, 0xfd, 0xbd, 0x05, 0xff, 0xfd, 0xbd, 0x05, 0xff, 0xfd, 0xbd, 0x05, 0xff, 0xfd, 0xbc, 0x04, 0xff, 0xfd, 0xbc, 0x04, 0xff, 0xfd, 0xbd, 0x05, 0xff, 0xfd, 0xbe, 0x06, 0xff, 0xfd, 0xbe, 0x05, 0xff, 0xfe, 0xbe, 0x05, 0xff, 0xfd, 0xbe, 0x05, 0xff, 0xfd, 0xbd, 0x04, 0xff, 0xfd, 0xbc, 0x04, 0xff, 0xfd, 0xbc, 0x04, 0xff, 0xfd, 0xbc, 0x03, 0xff, 0xfd, 0xbd, 0x03, 0xff, 0xfd, 0xbd, 0x00, 0xff, 0xfc, 0xb9, 0x00, 0xff, 0xfb, 0xd1, 0x4f, 0xff, 0xfd, 0xfb, 0xf3, 0xff, 0xfc, 0xfb, 0xf8, 0xff, 0xfa, 0xf9, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf5, 0xf6, 0xf3, 0xff, 0xf5, 0xf5, 0xf2, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf6, 0xf5, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf5, 0xf5, 0xf2, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf5, 0xf6, 0xf4, 0xff, 0xf6, 0xf7, 0xf4, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf6, 0xf7, 0xf4, 0xff, 0xf7, 0xf6, 0xf4, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xfb, 0xfb, 0xf5, 0xff, 0xfd, 0xfc, 0xf7, 0xff, + 0xfd, 0xfb, 0xf6, 0xff, 0xfb, 0xfb, 0xf5, 0xff, 0xf9, 0xf9, 0xf4, 0xff, 0xf7, 0xf8, 0xf3, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xfb, 0xf9, 0xf3, 0xff, 0xfd, 0xfc, 0xfe, 0xff, 0xfc, 0xed, 0xba, 0xff, 0xfc, 0xc0, 0x06, 0xff, 0xfd, 0xbc, 0x00, 0xff, 0xfd, 0xbd, 0x06, 0xff, 0xfd, 0xbd, 0x05, 0xff, 0xfd, 0xbc, 0x06, 0xff, 0xfd, 0xbc, 0x06, 0xff, 0xfd, 0xbc, 0x06, 0xff, 0xfd, 0xbc, 0x08, 0xff, 0xfd, 0xbb, 0x07, 0xff, 0xfd, 0xbb, 0x05, 0xff, 0xfd, 0xbc, 0x04, 0xff, 0xfd, 0xbc, 0x05, 0xff, 0xfd, 0xbd, 0x05, 0xff, 0xfd, 0xbd, 0x06, 0xff, 0xfd, 0xbc, 0x06, 0xff, 0xfd, 0xbc, 0x06, 0xff, 0xfd, 0xbc, 0x06, 0xff, 0xfd, 0xbc, 0x06, 0xff, 0xfd, 0xbc, 0x08, 0xff, 0xfe, 0xbc, 0x09, 0xff, 0xfd, 0xbc, 0x08, 0xff, 0xfe, 0xbd, 0x08, 0xff, 0xfd, 0xbd, 0x09, 0xff, 0xfd, 0xbe, 0x09, 0xff, 0xfd, 0xbf, 0x0a, 0xff, 0xfe, 0xbf, 0x0b, 0xff, 0xfd, 0xbf, 0x0a, 0xff, 0xfd, 0xbe, 0x0a, 0xff, 0xfd, 0xbd, 0x0a, 0xff, 0xfe, 0xbd, 0x09, 0xff, 0xfe, 0xbd, 0x08, 0xff, 0xfd, 0xbd, 0x07, 0xff, 0xfd, 0xbd, 0x07, 0xff, 0xfd, 0xbc, 0x06, 0xff, 0xfd, 0xbb, 0x05, 0xff, 0xfe, 0xbd, 0x07, 0xff, 0xfd, 0xbe, 0x07, 0xff, 0xfd, 0xbe, 0x08, 0xff, 0xfd, 0xbf, 0x06, 0xff, 0xfd, 0xbe, 0x06, 0xff, 0xfd, 0xbd, 0x06, 0xff, 0xfd, 0xbc, 0x05, 0xff, 0xfd, 0xbc, 0x05, 0xff, 0xfd, 0xbd, 0x06, 0xff, 0xfd, 0xbd, 0x06, 0xff, 0xfd, 0xbc, 0x05, 0xff, 0xfd, 0xbb, 0x03, 0xff, 0xfd, 0xb8, 0x00, 0xff, 0xfc, 0xd7, 0x63, 0xff, 0xfd, 0xfd, 0xfc, 0xff, 0xfc, 0xfa, 0xf5, 0xff, 0xfa, 0xf9, 0xf3, 0xff, 0xf9, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf9, 0xf7, 0xf3, 0xff, 0xfa, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf9, 0xf9, 0xf4, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf9, 0xf8, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xfc, 0xfb, 0xf5, 0xff, 0xfd, 0xfb, 0xf6, 0xff, + 0xfd, 0xfb, 0xf6, 0xff, 0xfb, 0xfb, 0xf7, 0xff, 0xfb, 0xfa, 0xf5, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xfa, 0xf8, 0xf3, 0xff, 0xfc, 0xfa, 0xf7, 0xff, 0xfd, 0xf8, 0xe8, 0xff, 0xfc, 0xc7, 0x28, 0xff, 0xfd, 0xbc, 0x00, 0xff, 0xfd, 0xbe, 0x06, 0xff, 0xfd, 0xbc, 0x05, 0xff, 0xfd, 0xbb, 0x05, 0xff, 0xfd, 0xbb, 0x04, 0xff, 0xfd, 0xbb, 0x05, 0xff, 0xfd, 0xbb, 0x07, 0xff, 0xfd, 0xbb, 0x08, 0xff, 0xfd, 0xbb, 0x08, 0xff, 0xfd, 0xbb, 0x08, 0xff, 0xfd, 0xbc, 0x08, 0xff, 0xfd, 0xbc, 0x07, 0xff, 0xfd, 0xbc, 0x08, 0xff, 0xfd, 0xbc, 0x08, 0xff, 0xfd, 0xbc, 0x08, 0xff, 0xfe, 0xbb, 0x08, 0xff, 0xfd, 0xba, 0x07, 0xff, 0xfd, 0xbb, 0x08, 0xff, 0xfd, 0xbc, 0x08, 0xff, 0xfe, 0xbc, 0x0a, 0xff, 0xfe, 0xbc, 0x0a, 0xff, 0xfd, 0xbd, 0x0a, 0xff, 0xfd, 0xbd, 0x0a, 0xff, 0xfd, 0xbd, 0x0b, 0xff, 0xfd, 0xbd, 0x0b, 0xff, 0xfd, 0xbe, 0x0b, 0xff, 0xfd, 0xbd, 0x0b, 0xff, 0xfd, 0xbd, 0x0b, 0xff, 0xfd, 0xbc, 0x0a, 0xff, 0xfd, 0xbc, 0x0b, 0xff, 0xfd, 0xbc, 0x0c, 0xff, 0xfd, 0xbc, 0x0b, 0xff, 0xfd, 0xbc, 0x0a, 0xff, 0xfd, 0xbb, 0x08, 0xff, 0xfd, 0xbc, 0x07, 0xff, 0xfd, 0xbc, 0x07, 0xff, 0xfd, 0xbd, 0x09, 0xff, 0xfd, 0xbd, 0x09, 0xff, 0xfd, 0xbd, 0x09, 0xff, 0xfd, 0xbd, 0x07, 0xff, 0xfd, 0xbc, 0x08, 0xff, 0xfd, 0xbc, 0x07, 0xff, 0xfd, 0xbc, 0x05, 0xff, 0xfd, 0xbd, 0x06, 0xff, 0xfd, 0xbc, 0x06, 0xff, 0xfd, 0xbc, 0x08, 0xff, 0xfd, 0xbb, 0x06, 0xff, 0xfd, 0xba, 0x02, 0xff, 0xfc, 0xb7, 0x00, 0xff, 0xfb, 0xe6, 0xa3, 0xff, 0xfc, 0xfd, 0xff, 0xff, 0xfb, 0xf9, 0xf3, 0xff, 0xf9, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf9, 0xf7, 0xf3, 0xff, 0xfb, 0xf8, 0xf4, 0xff, 0xf9, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf9, 0xf8, 0xf2, 0xff, 0xfa, 0xf8, 0xf3, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xfa, 0xf9, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf9, 0xf7, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xfc, 0xfa, 0xf5, 0xff, 0xfe, 0xfb, 0xf6, 0xff, + 0xfd, 0xfb, 0xf7, 0xff, 0xfc, 0xfb, 0xf7, 0xff, 0xfa, 0xfa, 0xf5, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xfc, 0xf9, 0xf4, 0xff, 0xfd, 0xfd, 0xff, 0xff, 0xfc, 0xde, 0x85, 0xff, 0xfd, 0xb9, 0x00, 0xff, 0xfd, 0xbd, 0x07, 0xff, 0xfd, 0xbc, 0x08, 0xff, 0xfd, 0xbb, 0x08, 0xff, 0xfd, 0xba, 0x06, 0xff, 0xfd, 0xba, 0x05, 0xff, 0xfd, 0xba, 0x07, 0xff, 0xfd, 0xba, 0x09, 0xff, 0xfd, 0xba, 0x08, 0xff, 0xfd, 0xba, 0x09, 0xff, 0xfd, 0xbb, 0x09, 0xff, 0xfd, 0xbc, 0x0a, 0xff, 0xfe, 0xbc, 0x0a, 0xff, 0xfe, 0xbb, 0x0a, 0xff, 0xfd, 0xbb, 0x09, 0xff, 0xfd, 0xbb, 0x09, 0xff, 0xfd, 0xba, 0x09, 0xff, 0xfe, 0xba, 0x08, 0xff, 0xfd, 0xbb, 0x08, 0xff, 0xfd, 0xbb, 0x0a, 0xff, 0xfe, 0xbc, 0x0c, 0xff, 0xfd, 0xbc, 0x0c, 0xff, 0xfd, 0xbc, 0x0b, 0xff, 0xfd, 0xbd, 0x0a, 0xff, 0xfd, 0xbc, 0x0b, 0xff, 0xfd, 0xbc, 0x0a, 0xff, 0xfd, 0xbc, 0x0b, 0xff, 0xfd, 0xbc, 0x0c, 0xff, 0xfd, 0xbc, 0x0b, 0xff, 0xfd, 0xbc, 0x0b, 0xff, 0xfd, 0xbc, 0x0c, 0xff, 0xfd, 0xbb, 0x0d, 0xff, 0xfd, 0xbb, 0x0d, 0xff, 0xfe, 0xbb, 0x0d, 0xff, 0xfd, 0xbb, 0x0b, 0xff, 0xfe, 0xbb, 0x0a, 0xff, 0xfd, 0xbc, 0x0a, 0xff, 0xfd, 0xbd, 0x0b, 0xff, 0xfd, 0xbd, 0x0c, 0xff, 0xfd, 0xbc, 0x0c, 0xff, 0xfd, 0xbc, 0x0b, 0xff, 0xfe, 0xbc, 0x0a, 0xff, 0xfd, 0xbb, 0x09, 0xff, 0xfd, 0xbb, 0x08, 0xff, 0xfd, 0xbc, 0x07, 0xff, 0xfd, 0xbc, 0x08, 0xff, 0xfe, 0xbc, 0x09, 0xff, 0xfd, 0xbb, 0x08, 0xff, 0xfd, 0xba, 0x05, 0xff, 0xfd, 0xb6, 0x00, 0xff, 0xfc, 0xc3, 0x29, 0xff, 0xfd, 0xfa, 0xef, 0xff, 0xfd, 0xfb, 0xf7, 0xff, 0xfa, 0xf8, 0xf3, 0xff, 0xf9, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf7, 0xf6, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xfa, 0xf9, 0xf3, 0xff, 0xfb, 0xf9, 0xf4, 0xff, 0xfb, 0xfa, 0xf4, 0xff, 0xfc, 0xfa, 0xf5, 0xff, 0xfc, 0xfa, 0xf5, 0xff, 0xfc, 0xfa, 0xf5, 0xff, 0xfc, 0xfa, 0xf5, 0xff, 0xfb, 0xfa, 0xf5, 0xff, 0xfa, 0xf9, 0xf5, 0xff, 0xf9, 0xf8, 0xf4, 0xff, 0xf9, 0xf8, 0xf4, 0xff, 0xfa, 0xf9, 0xf3, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xfb, 0xf9, 0xf5, 0xff, 0xfb, 0xf9, 0xf5, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xfb, 0xfa, 0xf4, 0xff, 0xfa, 0xf9, 0xf3, 0xff, 0xf9, 0xf7, 0xf3, 0xff, 0xf9, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xfc, 0xf9, 0xf5, 0xff, 0xfe, 0xfb, 0xf6, 0xff, + 0xfd, 0xfb, 0xf6, 0xff, 0xfc, 0xfb, 0xf6, 0xff, 0xf9, 0xfa, 0xf5, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xfa, 0xf7, 0xf2, 0xff, 0xfc, 0xf9, 0xf6, 0xff, 0xfd, 0xf7, 0xec, 0xff, 0xfc, 0xc4, 0x27, 0xff, 0xfd, 0xbb, 0x00, 0xff, 0xfe, 0xbc, 0x0a, 0xff, 0xfd, 0xba, 0x09, 0xff, 0xfd, 0xba, 0x09, 0xff, 0xfd, 0xb9, 0x08, 0xff, 0xfd, 0xb9, 0x08, 0xff, 0xfd, 0xb9, 0x0a, 0xff, 0xfe, 0xb9, 0x0a, 0xff, 0xfd, 0xb9, 0x0a, 0xff, 0xfd, 0xb9, 0x0b, 0xff, 0xfd, 0xb9, 0x0b, 0xff, 0xfd, 0xb9, 0x0a, 0xff, 0xfd, 0xba, 0x0c, 0xff, 0xfd, 0xba, 0x0b, 0xff, 0xfd, 0xba, 0x0b, 0xff, 0xfd, 0xba, 0x0b, 0xff, 0xfd, 0xb9, 0x0b, 0xff, 0xfd, 0xb9, 0x09, 0xff, 0xfd, 0xb9, 0x09, 0xff, 0xfe, 0xba, 0x0b, 0xff, 0xfe, 0xbb, 0x0d, 0xff, 0xfd, 0xbb, 0x0d, 0xff, 0xfd, 0xbc, 0x0d, 0xff, 0xfd, 0xbd, 0x0d, 0xff, 0xfd, 0xbc, 0x0d, 0xff, 0xfd, 0xbb, 0x0d, 0xff, 0xfd, 0xbb, 0x0c, 0xff, 0xfe, 0xba, 0x0c, 0xff, 0xfe, 0xba, 0x0c, 0xff, 0xfd, 0xba, 0x0b, 0xff, 0xfe, 0xbb, 0x0c, 0xff, 0xfe, 0xbb, 0x0c, 0xff, 0xfe, 0xbc, 0x0c, 0xff, 0xfd, 0xbc, 0x0d, 0xff, 0xfd, 0xbc, 0x0d, 0xff, 0xfd, 0xbc, 0x0c, 0xff, 0xfd, 0xbd, 0x0d, 0xff, 0xfd, 0xbc, 0x0e, 0xff, 0xfd, 0xbc, 0x0e, 0xff, 0xfd, 0xbc, 0x0e, 0xff, 0xfd, 0xbb, 0x0e, 0xff, 0xfd, 0xbb, 0x0d, 0xff, 0xfd, 0xbb, 0x0d, 0xff, 0xfe, 0xbb, 0x0c, 0xff, 0xfe, 0xbb, 0x0b, 0xff, 0xfe, 0xbc, 0x0a, 0xff, 0xfe, 0xbd, 0x0a, 0xff, 0xfd, 0xbd, 0x0a, 0xff, 0xfd, 0xbb, 0x07, 0xff, 0xfd, 0xba, 0x04, 0xff, 0xfc, 0xb4, 0x00, 0xff, 0xfc, 0xe6, 0xab, 0xff, 0xfd, 0xfe, 0xff, 0xff, 0xfa, 0xf8, 0xf2, 0xff, 0xf9, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf5, 0xf3, 0xff, 0xf5, 0xf6, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf8, 0xf8, 0xf4, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xfa, 0xf8, 0xf4, 0xff, 0xfb, 0xf9, 0xf4, 0xff, 0xfb, 0xfa, 0xf4, 0xff, 0xfb, 0xfa, 0xf4, 0xff, 0xfc, 0xfb, 0xf5, 0xff, 0xfd, 0xfb, 0xf6, 0xff, 0xff, 0xfe, 0xf9, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xfe, 0xfe, 0xf8, 0xff, 0xfd, 0xfb, 0xf6, 0xff, 0xfc, 0xfa, 0xf5, 0xff, 0xfb, 0xfa, 0xf4, 0xff, 0xfb, 0xf8, 0xf3, 0xff, 0xfa, 0xf8, 0xf3, 0xff, 0xf9, 0xf8, 0xf2, 0xff, 0xf9, 0xf9, 0xf4, 0xff, 0xfb, 0xf9, 0xf4, 0xff, 0xfc, 0xf9, 0xf5, 0xff, 0xfe, 0xfb, 0xf6, 0xff, + 0xfd, 0xfb, 0xf6, 0xff, 0xfc, 0xfb, 0xf6, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xf9, 0xf8, 0xf4, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf9, 0xf7, 0xf2, 0xff, 0xfa, 0xf7, 0xf2, 0xff, 0xfd, 0xfa, 0xfb, 0xff, 0xfc, 0xe9, 0xb9, 0xff, 0xfc, 0xb8, 0x00, 0xff, 0xfd, 0xba, 0x06, 0xff, 0xfd, 0xbb, 0x0b, 0xff, 0xfd, 0xba, 0x0b, 0xff, 0xfd, 0xb9, 0x0a, 0xff, 0xfd, 0xb8, 0x09, 0xff, 0xfd, 0xb8, 0x09, 0xff, 0xfd, 0xb9, 0x0b, 0xff, 0xfd, 0xb9, 0x0b, 0xff, 0xfd, 0xb8, 0x0b, 0xff, 0xfd, 0xb8, 0x0b, 0xff, 0xfd, 0xb7, 0x0c, 0xff, 0xfd, 0xb6, 0x0c, 0xff, 0xfd, 0xb6, 0x0c, 0xff, 0xfd, 0xb7, 0x0c, 0xff, 0xfd, 0xb8, 0x0b, 0xff, 0xfd, 0xb9, 0x0b, 0xff, 0xfd, 0xb8, 0x0b, 0xff, 0xfd, 0xb8, 0x09, 0xff, 0xfd, 0xb7, 0x0a, 0xff, 0xfd, 0xb9, 0x0b, 0xff, 0xfd, 0xba, 0x0c, 0xff, 0xfd, 0xba, 0x0d, 0xff, 0xfd, 0xba, 0x0e, 0xff, 0xfd, 0xba, 0x0e, 0xff, 0xfe, 0xba, 0x0d, 0xff, 0xfd, 0xb9, 0x0d, 0xff, 0xfd, 0xb8, 0x0c, 0xff, 0xfd, 0xb7, 0x0b, 0xff, 0xfd, 0xb6, 0x0a, 0xff, 0xfd, 0xb7, 0x0a, 0xff, 0xfd, 0xb8, 0x0b, 0xff, 0xfd, 0xba, 0x0c, 0xff, 0xfe, 0xbb, 0x0c, 0xff, 0xfd, 0xbb, 0x0c, 0xff, 0xfe, 0xbb, 0x0c, 0xff, 0xfd, 0xbb, 0x0c, 0xff, 0xfd, 0xbb, 0x0d, 0xff, 0xfd, 0xbb, 0x0d, 0xff, 0xfd, 0xbb, 0x0e, 0xff, 0xfd, 0xbb, 0x0e, 0xff, 0xfe, 0xba, 0x0e, 0xff, 0xfd, 0xb9, 0x0d, 0xff, 0xfd, 0xb9, 0x0d, 0xff, 0xfd, 0xbb, 0x0c, 0xff, 0xfe, 0xbb, 0x0c, 0xff, 0xfe, 0xbb, 0x0c, 0xff, 0xfe, 0xbb, 0x0b, 0xff, 0xfd, 0xbc, 0x0b, 0xff, 0xfd, 0xbb, 0x0b, 0xff, 0xfd, 0xb9, 0x07, 0xff, 0xfd, 0xb2, 0x00, 0xff, 0xfc, 0xce, 0x5e, 0xff, 0xfd, 0xfe, 0xff, 0xff, 0xfb, 0xf9, 0xf4, 0xff, 0xf9, 0xf8, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf5, 0xf2, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xf8, 0xf8, 0xf4, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf9, 0xf9, 0xf4, 0xff, 0xfb, 0xf9, 0xf4, 0xff, 0xfb, 0xf9, 0xf4, 0xff, 0xfc, 0xf9, 0xf5, 0xff, 0xfd, 0xfa, 0xf6, 0xff, 0xfe, 0xfc, 0xf7, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf3, 0xef, 0xec, 0xff, 0xda, 0xce, 0xcb, 0xff, 0xc1, 0xaa, 0xa5, 0xff, 0xae, 0x8d, 0x88, 0xff, 0x9d, 0x74, 0x6e, 0xff, 0x90, 0x63, 0x5d, 0xff, 0x8a, 0x59, 0x53, 0xff, 0x87, 0x55, 0x50, 0xff, 0x89, 0x57, 0x52, 0xff, 0x91, 0x63, 0x5e, 0xff, 0xa0, 0x79, 0x74, 0xff, 0xb2, 0x95, 0x91, 0xff, 0xca, 0xb7, 0xb3, 0xff, 0xe5, 0xdb, 0xd8, 0xff, 0xfb, 0xfb, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xf9, 0xff, 0xfc, 0xfa, 0xf5, 0xff, 0xfb, 0xf9, 0xf4, 0xff, 0xfa, 0xf9, 0xf3, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xfb, 0xf9, 0xf4, 0xff, 0xfc, 0xf9, 0xf5, 0xff, 0xfe, 0xfb, 0xf6, 0xff, + 0xfd, 0xfb, 0xf6, 0xff, 0xfb, 0xfb, 0xf5, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf5, 0xf1, 0xff, 0xf9, 0xf6, 0xf1, 0xff, 0xfb, 0xf6, 0xf1, 0xff, 0xfd, 0xfa, 0xfc, 0xff, 0xfc, 0xda, 0x88, 0xff, 0xfd, 0xb5, 0x00, 0xff, 0xfd, 0xb9, 0x08, 0xff, 0xfd, 0xb9, 0x0b, 0xff, 0xfe, 0xba, 0x0b, 0xff, 0xfd, 0xb9, 0x0b, 0xff, 0xfd, 0xb8, 0x0a, 0xff, 0xfd, 0xb8, 0x0a, 0xff, 0xfd, 0xb8, 0x0b, 0xff, 0xfd, 0xb7, 0x0b, 0xff, 0xfd, 0xb5, 0x0c, 0xff, 0xfd, 0xb5, 0x0c, 0xff, 0xfd, 0xb5, 0x0c, 0xff, 0xfd, 0xb4, 0x0c, 0xff, 0xfd, 0xb3, 0x0d, 0xff, 0xfd, 0xb3, 0x0c, 0xff, 0xfd, 0xb4, 0x0c, 0xff, 0xfd, 0xb6, 0x0c, 0xff, 0xfc, 0xb6, 0x0b, 0xff, 0xfd, 0xb7, 0x0b, 0xff, 0xfd, 0xb7, 0x0b, 0xff, 0xfd, 0xb7, 0x0b, 0xff, 0xfd, 0xb6, 0x0a, 0xff, 0xfd, 0xb7, 0x0c, 0xff, 0xfd, 0xb7, 0x0e, 0xff, 0xfd, 0xb6, 0x0e, 0xff, 0xfd, 0xb6, 0x0e, 0xff, 0xfd, 0xb6, 0x0d, 0xff, 0xfd, 0xb6, 0x0c, 0xff, 0xfd, 0xb6, 0x0b, 0xff, 0xfd, 0xb5, 0x0a, 0xff, 0xfd, 0xb5, 0x09, 0xff, 0xfd, 0xb5, 0x09, 0xff, 0xfd, 0xb6, 0x0a, 0xff, 0xfd, 0xb7, 0x0a, 0xff, 0xfd, 0xb8, 0x0b, 0xff, 0xfd, 0xb8, 0x0b, 0xff, 0xfd, 0xb9, 0x0b, 0xff, 0xfd, 0xb9, 0x0b, 0xff, 0xfd, 0xb9, 0x0b, 0xff, 0xfd, 0xb9, 0x0c, 0xff, 0xfe, 0xba, 0x0d, 0xff, 0xfd, 0xb9, 0x0d, 0xff, 0xfd, 0xb7, 0x0d, 0xff, 0xfd, 0xb7, 0x0c, 0xff, 0xfd, 0xb7, 0x0c, 0xff, 0xfd, 0xb8, 0x0c, 0xff, 0xfd, 0xb8, 0x0c, 0xff, 0xfd, 0xb9, 0x0c, 0xff, 0xfd, 0xb9, 0x0b, 0xff, 0xfd, 0xb8, 0x0b, 0xff, 0xfd, 0xb8, 0x09, 0xff, 0xfd, 0xb4, 0x00, 0xff, 0xfc, 0xc1, 0x31, 0xff, 0xfd, 0xfa, 0xf5, 0xff, 0xfc, 0xfa, 0xf5, 0xff, 0xf8, 0xf9, 0xf3, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf9, 0xf9, 0xf4, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xfc, 0xfa, 0xf5, 0xff, 0xfd, 0xfb, 0xf6, 0xff, 0xff, 0xfe, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xdf, 0xd4, 0xd0, 0xff, 0xad, 0x8f, 0x8b, 0xff, 0x7e, 0x4d, 0x48, 0xff, 0x62, 0x22, 0x1c, 0xff, 0x52, 0x0b, 0x02, 0xff, 0x4b, 0x00, 0x00, 0xff, 0x48, 0x00, 0x00, 0xff, 0x49, 0x00, 0x00, 0xff, 0x4b, 0x01, 0x00, 0xff, 0x4e, 0x03, 0x00, 0xff, 0x4e, 0x03, 0x00, 0xff, 0x4c, 0x02, 0x00, 0xff, 0x4b, 0x00, 0x00, 0xff, 0x49, 0x00, 0x00, 0xff, 0x49, 0x00, 0x00, 0xff, 0x4c, 0x02, 0x00, 0xff, 0x59, 0x11, 0x0a, 0xff, 0x70, 0x33, 0x2e, 0xff, 0x9a, 0x6f, 0x6d, 0xff, 0xd2, 0xbe, 0xbb, 0xff, 0xfb, 0xf9, 0xf4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xf8, 0xff, 0xfb, 0xf9, 0xf4, 0xff, 0xfb, 0xf9, 0xf4, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xfb, 0xfa, 0xf5, 0xff, 0xfe, 0xfd, 0xf6, 0xff, + 0xfd, 0xfb, 0xf5, 0xff, 0xfb, 0xfb, 0xf5, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf6, 0xf1, 0xff, 0xf9, 0xf6, 0xf0, 0xff, 0xfc, 0xf5, 0xef, 0xff, 0xfd, 0xf9, 0xf8, 0xff, 0xfc, 0xd0, 0x68, 0xff, 0xfd, 0xb3, 0x00, 0xff, 0xfd, 0xb7, 0x0a, 0xff, 0xfd, 0xb7, 0x0b, 0xff, 0xfd, 0xb8, 0x0b, 0xff, 0xfd, 0xb7, 0x0b, 0xff, 0xfd, 0xb7, 0x0b, 0xff, 0xfd, 0xb7, 0x0b, 0xff, 0xfc, 0xb7, 0x0b, 0xff, 0xfd, 0xb5, 0x0b, 0xff, 0xfd, 0xb4, 0x0c, 0xff, 0xfd, 0xb4, 0x0c, 0xff, 0xfd, 0xb3, 0x0c, 0xff, 0xfd, 0xb2, 0x0d, 0xff, 0xfd, 0xb1, 0x0e, 0xff, 0xfd, 0xb1, 0x0c, 0xff, 0xfd, 0xb2, 0x0c, 0xff, 0xfd, 0xb3, 0x0d, 0xff, 0xfd, 0xb3, 0x0c, 0xff, 0xfd, 0xb4, 0x0b, 0xff, 0xfd, 0xb4, 0x0c, 0xff, 0xfd, 0xb4, 0x0b, 0xff, 0xfd, 0xb4, 0x0a, 0xff, 0xfd, 0xb5, 0x0b, 0xff, 0xfd, 0xb5, 0x0c, 0xff, 0xfd, 0xb4, 0x0d, 0xff, 0xfd, 0xb3, 0x0c, 0xff, 0xfd, 0xb4, 0x0c, 0xff, 0xfd, 0xb4, 0x0c, 0xff, 0xfd, 0xb4, 0x0c, 0xff, 0xfd, 0xb4, 0x0a, 0xff, 0xfd, 0xb4, 0x08, 0xff, 0xfd, 0xb3, 0x09, 0xff, 0xfd, 0xb3, 0x0b, 0xff, 0xfd, 0xb4, 0x0a, 0xff, 0xfd, 0xb6, 0x0b, 0xff, 0xfd, 0xb6, 0x0c, 0xff, 0xfd, 0xb7, 0x0c, 0xff, 0xfd, 0xb8, 0x0b, 0xff, 0xfd, 0xb8, 0x0b, 0xff, 0xfd, 0xb7, 0x0b, 0xff, 0xfd, 0xb7, 0x0c, 0xff, 0xfd, 0xb7, 0x0b, 0xff, 0xfd, 0xb6, 0x0b, 0xff, 0xfd, 0xb6, 0x0c, 0xff, 0xfd, 0xb5, 0x0d, 0xff, 0xfd, 0xb5, 0x0c, 0xff, 0xfd, 0xb5, 0x0c, 0xff, 0xfd, 0xb6, 0x0c, 0xff, 0xfc, 0xb7, 0x0b, 0xff, 0xfd, 0xb7, 0x0b, 0xff, 0xfd, 0xb6, 0x0a, 0xff, 0xfd, 0xb4, 0x03, 0xff, 0xfc, 0xbb, 0x1c, 0xff, 0xfd, 0xf6, 0xe7, 0xff, 0xfc, 0xfb, 0xf7, 0xff, 0xfa, 0xf9, 0xf3, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf9, 0xf8, 0xf4, 0xff, 0xf7, 0xf8, 0xf4, 0xff, 0xf8, 0xf9, 0xf4, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xfb, 0xfa, 0xf5, 0xff, 0xfd, 0xfa, 0xf6, 0xff, 0xfe, 0xfd, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xf9, 0xf5, 0xff, 0xc1, 0xa8, 0xa2, 0xff, 0x7f, 0x49, 0x41, 0xff, 0x54, 0x0f, 0x05, 0xff, 0x49, 0x00, 0x00, 0xff, 0x4b, 0x02, 0x00, 0xff, 0x52, 0x07, 0x00, 0xff, 0x54, 0x0c, 0x00, 0xff, 0x55, 0x0d, 0x02, 0xff, 0x54, 0x0d, 0x03, 0xff, 0x54, 0x0d, 0x03, 0xff, 0x54, 0x0e, 0x02, 0xff, 0x55, 0x0f, 0x03, 0xff, 0x55, 0x0f, 0x04, 0xff, 0x54, 0x0e, 0x03, 0xff, 0x54, 0x0d, 0x03, 0xff, 0x55, 0x0c, 0x01, 0xff, 0x55, 0x0c, 0x01, 0xff, 0x56, 0x0b, 0x01, 0xff, 0x55, 0x09, 0x00, 0xff, 0x51, 0x03, 0x00, 0xff, 0x4c, 0x00, 0x00, 0xff, 0x4e, 0x04, 0x00, 0xff, 0x6e, 0x32, 0x2b, 0xff, 0xb5, 0x96, 0x91, 0xff, 0xf7, 0xf3, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xf9, 0xf5, 0xff, 0xfb, 0xf9, 0xf5, 0xff, 0xfd, 0xfa, 0xf6, 0xff, 0xfe, 0xfd, 0xf7, 0xff, + 0xfd, 0xfa, 0xf5, 0xff, 0xfb, 0xfb, 0xf5, 0xff, 0xf9, 0xf9, 0xf4, 0xff, 0xf9, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf9, 0xf7, 0xf2, 0xff, 0xf9, 0xf6, 0xf0, 0xff, 0xfc, 0xf5, 0xed, 0xff, 0xfe, 0xf7, 0xf5, 0xff, 0xfc, 0xcc, 0x5d, 0xff, 0xfd, 0xb3, 0x00, 0xff, 0xfd, 0xb5, 0x09, 0xff, 0xfd, 0xb5, 0x0b, 0xff, 0xfd, 0xb6, 0x0b, 0xff, 0xfd, 0xb4, 0x0c, 0xff, 0xfd, 0xb3, 0x0c, 0xff, 0xfd, 0xb4, 0x0c, 0xff, 0xfd, 0xb4, 0x0c, 0xff, 0xfd, 0xb3, 0x0c, 0xff, 0xfd, 0xb2, 0x0c, 0xff, 0xfd, 0xb1, 0x0d, 0xff, 0xfd, 0xb0, 0x0d, 0xff, 0xfd, 0xaf, 0x0d, 0xff, 0xfd, 0xaf, 0x0d, 0xff, 0xfd, 0xaf, 0x0d, 0xff, 0xfd, 0xb0, 0x0c, 0xff, 0xfd, 0xb1, 0x0d, 0xff, 0xfd, 0xb1, 0x0d, 0xff, 0xfd, 0xb1, 0x0c, 0xff, 0xfd, 0xb1, 0x0c, 0xff, 0xfd, 0xb1, 0x0c, 0xff, 0xfd, 0xb1, 0x0b, 0xff, 0xfd, 0xb2, 0x0b, 0xff, 0xfd, 0xb3, 0x0b, 0xff, 0xfd, 0xb2, 0x0d, 0xff, 0xfd, 0xb2, 0x0d, 0xff, 0xfd, 0xb2, 0x0c, 0xff, 0xfd, 0xb2, 0x0d, 0xff, 0xfd, 0xb3, 0x0c, 0xff, 0xfd, 0xb3, 0x0c, 0xff, 0xfd, 0xb2, 0x0b, 0xff, 0xfd, 0xb1, 0x0b, 0xff, 0xfd, 0xb0, 0x0d, 0xff, 0xfd, 0xb1, 0x0d, 0xff, 0xfd, 0xb3, 0x0d, 0xff, 0xfd, 0xb4, 0x0c, 0xff, 0xfd, 0xb3, 0x0c, 0xff, 0xfd, 0xb4, 0x0c, 0xff, 0xfd, 0xb4, 0x0b, 0xff, 0xfd, 0xb3, 0x0c, 0xff, 0xfd, 0xb4, 0x0b, 0xff, 0xfd, 0xb4, 0x0b, 0xff, 0xfd, 0xb3, 0x0b, 0xff, 0xfd, 0xb4, 0x0d, 0xff, 0xfd, 0xb3, 0x0d, 0xff, 0xfd, 0xb3, 0x0c, 0xff, 0xfd, 0xb3, 0x0c, 0xff, 0xfd, 0xb4, 0x0c, 0xff, 0xfd, 0xb4, 0x0c, 0xff, 0xfd, 0xb4, 0x0c, 0xff, 0xfd, 0xb4, 0x0c, 0xff, 0xfd, 0xb2, 0x05, 0xff, 0xfc, 0xb8, 0x19, 0xff, 0xfc, 0xf6, 0xe3, 0xff, 0xfd, 0xfb, 0xf7, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xf9, 0xf9, 0xf4, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf9, 0xf7, 0xf3, 0xff, 0xf7, 0xf6, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xfb, 0xf9, 0xf4, 0xff, 0xfc, 0xfa, 0xf5, 0xff, 0xfd, 0xfb, 0xf5, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xca, 0xb4, 0xaf, 0xff, 0x72, 0x36, 0x2e, 0xff, 0x4c, 0x00, 0x00, 0xff, 0x4f, 0x02, 0x00, 0xff, 0x55, 0x0b, 0x00, 0xff, 0x55, 0x0e, 0x02, 0xff, 0x55, 0x0f, 0x03, 0xff, 0x57, 0x0f, 0x03, 0xff, 0x56, 0x10, 0x04, 0xff, 0x57, 0x10, 0x05, 0xff, 0x58, 0x10, 0x05, 0xff, 0x58, 0x10, 0x05, 0xff, 0x58, 0x10, 0x04, 0xff, 0x58, 0x10, 0x04, 0xff, 0x57, 0x10, 0x04, 0xff, 0x57, 0x0f, 0x04, 0xff, 0x57, 0x0e, 0x04, 0xff, 0x57, 0x0e, 0x02, 0xff, 0x57, 0x0d, 0x03, 0xff, 0x57, 0x0d, 0x04, 0xff, 0x57, 0x0d, 0x03, 0xff, 0x57, 0x0c, 0x02, 0xff, 0x57, 0x0b, 0x01, 0xff, 0x55, 0x0a, 0x00, 0xff, 0x4e, 0x02, 0x00, 0xff, 0x48, 0x00, 0x00, 0xff, 0x69, 0x2d, 0x27, 0xff, 0xc8, 0xb0, 0xab, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xf9, 0xff, 0xfd, 0xfa, 0xf5, 0xff, 0xfe, 0xfd, 0xf7, 0xff, + 0xfc, 0xfa, 0xf4, 0xff, 0xfb, 0xfa, 0xf5, 0xff, 0xfa, 0xf9, 0xf3, 0xff, 0xf9, 0xf8, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf9, 0xf7, 0xf2, 0xff, 0xf9, 0xf6, 0xf1, 0xff, 0xfa, 0xf5, 0xef, 0xff, 0xfc, 0xf4, 0xec, 0xff, 0xfd, 0xf5, 0xf4, 0xff, 0xfc, 0xcb, 0x5d, 0xff, 0xfd, 0xb0, 0x00, 0xff, 0xfd, 0xb2, 0x0b, 0xff, 0xfe, 0xb2, 0x0b, 0xff, 0xfd, 0xb3, 0x0b, 0xff, 0xfd, 0xb1, 0x0c, 0xff, 0xfd, 0xaf, 0x0d, 0xff, 0xfd, 0xaf, 0x0d, 0xff, 0xfd, 0xaf, 0x0d, 0xff, 0xfd, 0xaf, 0x0c, 0xff, 0xfd, 0xaf, 0x0d, 0xff, 0xfd, 0xae, 0x0d, 0xff, 0xfd, 0xae, 0x0d, 0xff, 0xfd, 0xad, 0x0d, 0xff, 0xfd, 0xae, 0x0d, 0xff, 0xfd, 0xae, 0x0d, 0xff, 0xfe, 0xaf, 0x0c, 0xff, 0xfd, 0xae, 0x0c, 0xff, 0xfe, 0xaf, 0x0c, 0xff, 0xfd, 0xaf, 0x0b, 0xff, 0xfe, 0xaf, 0x0a, 0xff, 0xfe, 0xaf, 0x0a, 0xff, 0xfd, 0xaf, 0x0a, 0xff, 0xfd, 0xaf, 0x0a, 0xff, 0xfd, 0xb0, 0x0b, 0xff, 0xfd, 0xaf, 0x0c, 0xff, 0xfd, 0xaf, 0x0d, 0xff, 0xfd, 0xb0, 0x0e, 0xff, 0xfd, 0xb0, 0x0e, 0xff, 0xfd, 0xb1, 0x0e, 0xff, 0xfd, 0xb0, 0x0d, 0xff, 0xfc, 0xaf, 0x0d, 0xff, 0xfd, 0xaf, 0x0d, 0xff, 0xfd, 0xae, 0x0e, 0xff, 0xfd, 0xae, 0x0e, 0xff, 0xfd, 0xb0, 0x0e, 0xff, 0xfd, 0xb0, 0x0d, 0xff, 0xfd, 0xaf, 0x0c, 0xff, 0xfd, 0xb0, 0x0c, 0xff, 0xfd, 0xb0, 0x0b, 0xff, 0xfe, 0xb0, 0x0b, 0xff, 0xfd, 0xb0, 0x0a, 0xff, 0xfd, 0xb0, 0x0c, 0xff, 0xfd, 0xb0, 0x0d, 0xff, 0xfd, 0xb1, 0x0e, 0xff, 0xfd, 0xb2, 0x0d, 0xff, 0xfd, 0xb2, 0x0c, 0xff, 0xfd, 0xb1, 0x0e, 0xff, 0xfd, 0xb0, 0x0e, 0xff, 0xfd, 0xb0, 0x0e, 0xff, 0xfd, 0xb0, 0x0e, 0xff, 0xfd, 0xb1, 0x0e, 0xff, 0xfd, 0xaf, 0x06, 0xff, 0xfc, 0xb6, 0x19, 0xff, 0xfd, 0xf5, 0xe4, 0xff, 0xfc, 0xfa, 0xf7, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xfa, 0xfa, 0xf3, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xf9, 0xf8, 0xf4, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xfa, 0xf8, 0xf4, 0xff, 0xfc, 0xf9, 0xf4, 0xff, 0xfd, 0xfa, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf4, 0xf1, 0xeb, 0xff, 0x91, 0x65, 0x5d, 0xff, 0x4d, 0x03, 0x00, 0xff, 0x4e, 0x03, 0x00, 0xff, 0x55, 0x0c, 0x01, 0xff, 0x56, 0x0e, 0x02, 0xff, 0x56, 0x0f, 0x03, 0xff, 0x57, 0x10, 0x05, 0xff, 0x58, 0x11, 0x05, 0xff, 0x58, 0x11, 0x06, 0xff, 0x58, 0x11, 0x07, 0xff, 0x58, 0x11, 0x07, 0xff, 0x59, 0x11, 0x07, 0xff, 0x59, 0x11, 0x07, 0xff, 0x59, 0x10, 0x06, 0xff, 0x58, 0x10, 0x05, 0xff, 0x58, 0x0f, 0x04, 0xff, 0x57, 0x0e, 0x01, 0xff, 0x54, 0x0d, 0x00, 0xff, 0x51, 0x0b, 0x00, 0xff, 0x4f, 0x0a, 0x00, 0xff, 0x4e, 0x09, 0x00, 0xff, 0x4d, 0x08, 0x00, 0xff, 0x4d, 0x08, 0x00, 0xff, 0x4e, 0x08, 0x00, 0xff, 0x52, 0x0a, 0x00, 0xff, 0x54, 0x09, 0x00, 0xff, 0x53, 0x09, 0x00, 0xff, 0x4c, 0x01, 0x00, 0xff, 0x4c, 0x03, 0x00, 0xff, 0x9a, 0x6f, 0x68, 0xff, 0xfb, 0xf7, 0xf3, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xfd, 0xfd, 0xf8, 0xff, + 0xfd, 0xfa, 0xf5, 0xff, 0xfb, 0xfb, 0xf5, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf9, 0xf6, 0xf1, 0xff, 0xf9, 0xf5, 0xf0, 0xff, 0xf9, 0xf5, 0xef, 0xff, 0xfa, 0xf4, 0xed, 0xff, 0xfb, 0xf2, 0xeb, 0xff, 0xfc, 0xf4, 0xf2, 0xff, 0xfc, 0xc7, 0x5d, 0xff, 0xfd, 0xaa, 0x00, 0xff, 0xfd, 0xae, 0x0c, 0xff, 0xfc, 0xaf, 0x0d, 0xff, 0xfc, 0xaf, 0x0d, 0xff, 0xfd, 0xae, 0x0d, 0xff, 0xfd, 0xad, 0x0c, 0xff, 0xfd, 0xad, 0x0c, 0xff, 0xfd, 0xad, 0x0c, 0xff, 0xfd, 0xab, 0x0b, 0xff, 0xfd, 0xac, 0x0c, 0xff, 0xfd, 0xad, 0x0d, 0xff, 0xfd, 0xad, 0x0d, 0xff, 0xfd, 0xad, 0x0d, 0xff, 0xfd, 0xad, 0x0c, 0xff, 0xfd, 0xae, 0x0c, 0xff, 0xfd, 0xae, 0x0c, 0xff, 0xfd, 0xad, 0x0c, 0xff, 0xfd, 0xad, 0x0c, 0xff, 0xfd, 0xab, 0x06, 0xff, 0xfd, 0xa9, 0x00, 0xff, 0xfd, 0xaa, 0x00, 0xff, 0xfd, 0xa9, 0x00, 0xff, 0xfd, 0xa9, 0x00, 0xff, 0xfd, 0xa9, 0x00, 0xff, 0xfd, 0xac, 0x08, 0xff, 0xfd, 0xae, 0x0d, 0xff, 0xfc, 0xaf, 0x0e, 0xff, 0xfd, 0xaf, 0x0f, 0xff, 0xfd, 0xaf, 0x0f, 0xff, 0xfd, 0xaf, 0x0e, 0xff, 0xfc, 0xae, 0x0e, 0xff, 0xfd, 0xae, 0x0e, 0xff, 0xfd, 0xae, 0x0e, 0xff, 0xfd, 0xae, 0x0e, 0xff, 0xfc, 0xae, 0x0e, 0xff, 0xfd, 0xae, 0x0e, 0xff, 0xfd, 0xae, 0x09, 0xff, 0xfd, 0xab, 0x00, 0xff, 0xfd, 0xab, 0x00, 0xff, 0xfe, 0xaa, 0x00, 0xff, 0xfd, 0xa9, 0x00, 0xff, 0xfd, 0xac, 0x04, 0xff, 0xfd, 0xb0, 0x0e, 0xff, 0xfd, 0xb1, 0x0f, 0xff, 0xfc, 0xb2, 0x0f, 0xff, 0xfd, 0xb2, 0x0f, 0xff, 0xfd, 0xb0, 0x0f, 0xff, 0xfd, 0xaf, 0x0e, 0xff, 0xfc, 0xae, 0x0e, 0xff, 0xfd, 0xaf, 0x0e, 0xff, 0xfd, 0xaf, 0x0f, 0xff, 0xfd, 0xae, 0x08, 0xff, 0xfc, 0xb5, 0x1a, 0xff, 0xfd, 0xf5, 0xe4, 0xff, 0xfd, 0xfb, 0xf7, 0xff, 0xfb, 0xf9, 0xf4, 0xff, 0xfa, 0xf9, 0xf3, 0xff, 0xf9, 0xf8, 0xf4, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf5, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xfc, 0xfa, 0xf5, 0xff, 0xfd, 0xfb, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe5, 0xdb, 0xd6, 0xff, 0x6f, 0x36, 0x2e, 0xff, 0x46, 0x00, 0x00, 0xff, 0x51, 0x09, 0x00, 0xff, 0x55, 0x0d, 0x03, 0xff, 0x57, 0x0e, 0x03, 0xff, 0x58, 0x10, 0x05, 0xff, 0x58, 0x11, 0x06, 0xff, 0x58, 0x11, 0x07, 0xff, 0x58, 0x12, 0x07, 0xff, 0x58, 0x12, 0x07, 0xff, 0x59, 0x12, 0x08, 0xff, 0x59, 0x12, 0x08, 0xff, 0x5a, 0x11, 0x08, 0xff, 0x5a, 0x10, 0x07, 0xff, 0x58, 0x0f, 0x04, 0xff, 0x52, 0x0c, 0x00, 0xff, 0x4e, 0x09, 0x00, 0xff, 0x5e, 0x0d, 0x06, 0xff, 0x78, 0x15, 0x1c, 0xff, 0x91, 0x1e, 0x32, 0xff, 0xa0, 0x24, 0x3e, 0xff, 0xa5, 0x27, 0x41, 0xff, 0xa3, 0x25, 0x3d, 0xff, 0x96, 0x20, 0x31, 0xff, 0x79, 0x16, 0x1d, 0xff, 0x5c, 0x0c, 0x07, 0xff, 0x4d, 0x06, 0x00, 0xff, 0x4d, 0x07, 0x00, 0xff, 0x51, 0x09, 0x01, 0xff, 0x4f, 0x07, 0x00, 0xff, 0x44, 0x00, 0x00, 0xff, 0x7b, 0x46, 0x41, 0xff, 0xf2, 0xed, 0xe9, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xfd, 0xfa, 0xf5, 0xff, 0xfb, 0xfb, 0xf6, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf9, 0xf6, 0xf1, 0xff, 0xf8, 0xf4, 0xf0, 0xff, 0xf9, 0xf4, 0xed, 0xff, 0xfa, 0xf2, 0xeb, 0xff, 0xfb, 0xf1, 0xea, 0xff, 0xfc, 0xf2, 0xf1, 0xff, 0xfb, 0xc4, 0x5d, 0xff, 0xfd, 0xa8, 0x00, 0xff, 0xfd, 0xad, 0x0d, 0xff, 0xfd, 0xae, 0x0e, 0xff, 0xfd, 0xad, 0x0d, 0xff, 0xfd, 0xad, 0x0d, 0xff, 0xfd, 0xac, 0x0d, 0xff, 0xfd, 0xac, 0x0d, 0xff, 0xfd, 0xaa, 0x0d, 0xff, 0xfd, 0xa9, 0x0c, 0xff, 0xfd, 0xa9, 0x0c, 0xff, 0xfd, 0xaa, 0x0e, 0xff, 0xfd, 0xac, 0x0e, 0xff, 0xfd, 0xad, 0x0f, 0xff, 0xfd, 0xad, 0x0e, 0xff, 0xfd, 0xad, 0x0e, 0xff, 0xfd, 0xad, 0x0f, 0xff, 0xfd, 0xac, 0x0e, 0xff, 0xfd, 0xaa, 0x07, 0xff, 0xfb, 0xb6, 0x27, 0xff, 0xfb, 0xca, 0x60, 0xff, 0xfc, 0xc9, 0x5e, 0xff, 0xfc, 0xc9, 0x5e, 0xff, 0xfc, 0xcb, 0x63, 0xff, 0xfc, 0xca, 0x60, 0xff, 0xfc, 0xb1, 0x1b, 0xff, 0xfd, 0xab, 0x09, 0xff, 0xfd, 0xad, 0x0f, 0xff, 0xfd, 0xae, 0x10, 0xff, 0xfd, 0xaf, 0x10, 0xff, 0xfd, 0xae, 0x10, 0xff, 0xfc, 0xae, 0x0f, 0xff, 0xfc, 0xae, 0x0f, 0xff, 0xfc, 0xae, 0x0e, 0xff, 0xfd, 0xae, 0x0e, 0xff, 0xfd, 0xad, 0x0e, 0xff, 0xfd, 0xac, 0x0a, 0xff, 0xfc, 0xb5, 0x1d, 0xff, 0xfc, 0xce, 0x65, 0xff, 0xfc, 0xcd, 0x66, 0xff, 0xfc, 0xcc, 0x66, 0xff, 0xfc, 0xcd, 0x6a, 0xff, 0xfc, 0xc5, 0x4e, 0xff, 0xfd, 0xb0, 0x0e, 0xff, 0xfd, 0xb0, 0x0f, 0xff, 0xfd, 0xb0, 0x11, 0xff, 0xfd, 0xb0, 0x0f, 0xff, 0xfd, 0xaf, 0x0f, 0xff, 0xfc, 0xae, 0x0f, 0xff, 0xfc, 0xae, 0x0f, 0xff, 0xfc, 0xae, 0x0f, 0xff, 0xfd, 0xae, 0x0f, 0xff, 0xfd, 0xac, 0x09, 0xff, 0xfc, 0xb3, 0x1a, 0xff, 0xfd, 0xf4, 0xe3, 0xff, 0xfc, 0xfa, 0xf7, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xfc, 0xf9, 0xf4, 0xff, 0xfd, 0xfa, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe4, 0xda, 0xd5, 0xff, 0x61, 0x24, 0x1c, 0xff, 0x48, 0x00, 0x00, 0xff, 0x52, 0x0c, 0x02, 0xff, 0x54, 0x0e, 0x04, 0xff, 0x57, 0x0f, 0x05, 0xff, 0x58, 0x10, 0x05, 0xff, 0x59, 0x11, 0x07, 0xff, 0x59, 0x12, 0x07, 0xff, 0x59, 0x12, 0x07, 0xff, 0x5a, 0x12, 0x07, 0xff, 0x59, 0x13, 0x06, 0xff, 0x5a, 0x12, 0x06, 0xff, 0x5a, 0x11, 0x07, 0xff, 0x5a, 0x10, 0x05, 0xff, 0x56, 0x0e, 0x02, 0xff, 0x52, 0x0a, 0x00, 0xff, 0x76, 0x16, 0x1b, 0xff, 0xb6, 0x2c, 0x4e, 0xff, 0xd5, 0x35, 0x6a, 0xff, 0xcc, 0x33, 0x68, 0xff, 0xb9, 0x2b, 0x56, 0xff, 0xa8, 0x25, 0x44, 0xff, 0x9e, 0x23, 0x3b, 0xff, 0xa1, 0x24, 0x3c, 0xff, 0xb2, 0x2a, 0x47, 0xff, 0xc8, 0x36, 0x56, 0xff, 0xd4, 0x3b, 0x5a, 0xff, 0xc0, 0x32, 0x49, 0xff, 0x80, 0x18, 0x1d, 0xff, 0x4d, 0x07, 0x00, 0xff, 0x49, 0x05, 0x00, 0xff, 0x4f, 0x07, 0x00, 0xff, 0x44, 0x00, 0x00, 0xff, 0x73, 0x3b, 0x36, 0xff, 0xf5, 0xf2, 0xef, 0xff, + 0xfd, 0xfa, 0xf5, 0xff, 0xfc, 0xfb, 0xf6, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf8, 0xf6, 0xf1, 0xff, 0xf9, 0xf5, 0xf0, 0xff, 0xf9, 0xf4, 0xed, 0xff, 0xfb, 0xf2, 0xea, 0xff, 0xfb, 0xf0, 0xe8, 0xff, 0xfc, 0xf1, 0xef, 0xff, 0xfc, 0xc2, 0x5d, 0xff, 0xfd, 0xa7, 0x03, 0xff, 0xfd, 0xac, 0x0f, 0xff, 0xfd, 0xad, 0x0f, 0xff, 0xfd, 0xac, 0x0e, 0xff, 0xfd, 0xac, 0x0f, 0xff, 0xfd, 0xaa, 0x0e, 0xff, 0xfd, 0xaa, 0x0e, 0xff, 0xfd, 0xa9, 0x0e, 0xff, 0xfd, 0xa8, 0x0f, 0xff, 0xfd, 0xa7, 0x0e, 0xff, 0xfd, 0xa8, 0x0f, 0xff, 0xfd, 0xa9, 0x10, 0xff, 0xfd, 0xab, 0x10, 0xff, 0xfe, 0xad, 0x12, 0xff, 0xfd, 0xad, 0x13, 0xff, 0xfd, 0xab, 0x11, 0xff, 0xfd, 0xa9, 0x0f, 0xff, 0xfd, 0xaa, 0x0d, 0xff, 0xfb, 0xea, 0xc2, 0xff, 0xfb, 0xff, 0xff, 0xff, 0xfb, 0xff, 0xff, 0xff, 0xfb, 0xfe, 0xff, 0xff, 0xfb, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfc, 0xdc, 0x9a, 0xff, 0xfd, 0xa7, 0x01, 0xff, 0xfd, 0xab, 0x0d, 0xff, 0xfd, 0xad, 0x11, 0xff, 0xfc, 0xad, 0x12, 0xff, 0xfd, 0xad, 0x11, 0xff, 0xfd, 0xad, 0x11, 0xff, 0xfd, 0xae, 0x10, 0xff, 0xfc, 0xae, 0x10, 0xff, 0xfd, 0xae, 0x10, 0xff, 0xfd, 0xad, 0x10, 0xff, 0xfd, 0xa8, 0x02, 0xff, 0xfc, 0xce, 0x6d, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfc, 0xf9, 0xec, 0xff, 0xfc, 0xb5, 0x22, 0xff, 0xfd, 0xad, 0x0b, 0xff, 0xfd, 0xaf, 0x10, 0xff, 0xfc, 0xae, 0x10, 0xff, 0xfd, 0xae, 0x10, 0xff, 0xfd, 0xad, 0x11, 0xff, 0xfd, 0xad, 0x11, 0xff, 0xfd, 0xad, 0x10, 0xff, 0xfd, 0xad, 0x0f, 0xff, 0xfd, 0xaa, 0x09, 0xff, 0xfc, 0xb0, 0x1b, 0xff, 0xfd, 0xf4, 0xe2, 0xff, 0xfb, 0xfa, 0xf7, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf9, 0xf8, 0xf2, 0xff, 0xfb, 0xf9, 0xf4, 0xff, 0xfd, 0xfa, 0xf5, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xf2, 0xed, 0xe8, 0xff, 0x6b, 0x31, 0x2a, 0xff, 0x48, 0x00, 0x00, 0xff, 0x52, 0x0c, 0x02, 0xff, 0x54, 0x0e, 0x04, 0xff, 0x55, 0x10, 0x06, 0xff, 0x58, 0x11, 0x07, 0xff, 0x59, 0x12, 0x07, 0xff, 0x5a, 0x13, 0x08, 0xff, 0x5a, 0x13, 0x08, 0xff, 0x5a, 0x13, 0x08, 0xff, 0x5b, 0x13, 0x08, 0xff, 0x5b, 0x12, 0x07, 0xff, 0x5b, 0x11, 0x05, 0xff, 0x5a, 0x10, 0x04, 0xff, 0x54, 0x0c, 0x00, 0xff, 0x6b, 0x11, 0x0e, 0xff, 0xc0, 0x2e, 0x52, 0xff, 0xd2, 0x36, 0x64, 0xff, 0x98, 0x21, 0x38, 0xff, 0x62, 0x0d, 0x10, 0xff, 0x4d, 0x05, 0x00, 0xff, 0x49, 0x03, 0x00, 0xff, 0x49, 0x01, 0x00, 0xff, 0x49, 0x01, 0x00, 0xff, 0x47, 0x00, 0x00, 0xff, 0x48, 0x01, 0x00, 0xff, 0x4f, 0x03, 0x00, 0xff, 0x62, 0x0c, 0x09, 0xff, 0x8c, 0x1e, 0x26, 0xff, 0xc9, 0x34, 0x4a, 0xff, 0xcb, 0x32, 0x4a, 0xff, 0x78, 0x14, 0x17, 0xff, 0x49, 0x05, 0x00, 0xff, 0x50, 0x07, 0x00, 0xff, 0x47, 0x00, 0x00, 0xff, 0x83, 0x4f, 0x4a, 0xff, + 0xfd, 0xfa, 0xf5, 0xff, 0xfc, 0xfa, 0xf5, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf9, 0xf6, 0xf1, 0xff, 0xf9, 0xf5, 0xf0, 0xff, 0xfa, 0xf4, 0xed, 0xff, 0xfb, 0xf2, 0xea, 0xff, 0xfb, 0xef, 0xe7, 0xff, 0xfd, 0xef, 0xec, 0xff, 0xfc, 0xc1, 0x5d, 0xff, 0xfd, 0xa6, 0x06, 0xff, 0xfd, 0xaa, 0x11, 0xff, 0xfd, 0xab, 0x12, 0xff, 0xfd, 0xab, 0x11, 0xff, 0xfc, 0xaa, 0x11, 0xff, 0xfd, 0xa8, 0x10, 0xff, 0xfd, 0xa8, 0x10, 0xff, 0xfd, 0xa7, 0x10, 0xff, 0xfc, 0xa5, 0x0f, 0xff, 0xfc, 0xa5, 0x0f, 0xff, 0xfd, 0xa5, 0x10, 0xff, 0xfd, 0xa7, 0x11, 0xff, 0xfd, 0xa8, 0x12, 0xff, 0xfd, 0xab, 0x13, 0xff, 0xfd, 0xab, 0x15, 0xff, 0xfd, 0xa9, 0x14, 0xff, 0xfd, 0xa5, 0x0a, 0xff, 0xfb, 0xbd, 0x46, 0xff, 0xfb, 0xfd, 0xfa, 0xff, 0xfc, 0xfc, 0xf7, 0xff, 0xfb, 0xfb, 0xf6, 0xff, 0xfb, 0xfb, 0xf6, 0xff, 0xfb, 0xfb, 0xf6, 0xff, 0xfc, 0xfc, 0xfb, 0xff, 0xfd, 0xf6, 0xe7, 0xff, 0xfc, 0xb1, 0x23, 0xff, 0xfd, 0xa8, 0x09, 0xff, 0xfd, 0xaa, 0x11, 0xff, 0xfd, 0xaa, 0x13, 0xff, 0xfd, 0xab, 0x13, 0xff, 0xfc, 0xac, 0x13, 0xff, 0xfd, 0xac, 0x14, 0xff, 0xfd, 0xad, 0x14, 0xff, 0xfd, 0xad, 0x14, 0xff, 0xfd, 0xab, 0x12, 0xff, 0xfd, 0xa6, 0x03, 0xff, 0xfc, 0xd0, 0x75, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfc, 0xfc, 0xf8, 0xff, 0xfc, 0xfc, 0xf8, 0xff, 0xfd, 0xfe, 0xfc, 0xff, 0xfd, 0xf9, 0xea, 0xff, 0xfc, 0xb5, 0x26, 0xff, 0xfd, 0xac, 0x0c, 0xff, 0xfc, 0xad, 0x11, 0xff, 0xfd, 0xac, 0x12, 0xff, 0xfd, 0xac, 0x12, 0xff, 0xfe, 0xab, 0x12, 0xff, 0xfd, 0xac, 0x11, 0xff, 0xfd, 0xad, 0x12, 0xff, 0xfd, 0xad, 0x11, 0xff, 0xfd, 0xaa, 0x0a, 0xff, 0xfc, 0xb1, 0x1b, 0xff, 0xfd, 0xf4, 0xe2, 0xff, 0xfb, 0xfa, 0xf7, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xfb, 0xf9, 0xf4, 0xff, 0xfe, 0xfc, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8b, 0x5d, 0x56, 0xff, 0x47, 0x00, 0x00, 0xff, 0x53, 0x0d, 0x03, 0xff, 0x54, 0x0e, 0x04, 0xff, 0x56, 0x10, 0x06, 0xff, 0x57, 0x12, 0x07, 0xff, 0x59, 0x13, 0x08, 0xff, 0x5a, 0x13, 0x09, 0xff, 0x5b, 0x14, 0x09, 0xff, 0x5a, 0x15, 0x08, 0xff, 0x5a, 0x14, 0x09, 0xff, 0x5b, 0x14, 0x08, 0xff, 0x5c, 0x13, 0x06, 0xff, 0x5c, 0x10, 0x04, 0xff, 0x51, 0x0b, 0x00, 0xff, 0x81, 0x18, 0x1f, 0xff, 0xde, 0x38, 0x64, 0xff, 0x9d, 0x23, 0x3c, 0xff, 0x52, 0x06, 0x02, 0xff, 0x48, 0x03, 0x00, 0xff, 0x49, 0x01, 0x00, 0xff, 0x48, 0x00, 0x00, 0xff, 0x4f, 0x02, 0x00, 0xff, 0x5d, 0x11, 0x00, 0xff, 0x66, 0x1d, 0x01, 0xff, 0x69, 0x20, 0x01, 0xff, 0x64, 0x18, 0x01, 0xff, 0x5a, 0x09, 0x00, 0xff, 0x4e, 0x00, 0x00, 0xff, 0x46, 0x00, 0x00, 0xff, 0x4d, 0x04, 0x00, 0xff, 0x82, 0x18, 0x1d, 0xff, 0xd6, 0x35, 0x4c, 0xff, 0xae, 0x24, 0x31, 0xff, 0x55, 0x06, 0x00, 0xff, 0x51, 0x05, 0x00, 0xff, 0x4b, 0x00, 0x00, 0xff, + 0xfd, 0xfa, 0xf5, 0xff, 0xfc, 0xfb, 0xf5, 0xff, 0xfb, 0xfa, 0xf5, 0xff, 0xfa, 0xf8, 0xf3, 0xff, 0xf9, 0xf6, 0xf1, 0xff, 0xfa, 0xf4, 0xef, 0xff, 0xfb, 0xf3, 0xec, 0xff, 0xfb, 0xf1, 0xe9, 0xff, 0xfc, 0xee, 0xe6, 0xff, 0xfd, 0xee, 0xeb, 0xff, 0xfb, 0xbf, 0x5f, 0xff, 0xfd, 0xa4, 0x08, 0xff, 0xfd, 0xa7, 0x12, 0xff, 0xfd, 0xa8, 0x13, 0xff, 0xfd, 0xa8, 0x13, 0xff, 0xfd, 0xa7, 0x12, 0xff, 0xfc, 0xa6, 0x12, 0xff, 0xfd, 0xa5, 0x12, 0xff, 0xfd, 0xa4, 0x12, 0xff, 0xfd, 0xa4, 0x12, 0xff, 0xfc, 0xa4, 0x12, 0xff, 0xfc, 0xa4, 0x12, 0xff, 0xfd, 0xa5, 0x13, 0xff, 0xfd, 0xa6, 0x13, 0xff, 0xfd, 0xa8, 0x15, 0xff, 0xfd, 0xa8, 0x15, 0xff, 0xfd, 0xa7, 0x15, 0xff, 0xfc, 0xa2, 0x08, 0xff, 0xfb, 0xdd, 0xa2, 0xff, 0xfb, 0xff, 0xff, 0xff, 0xfb, 0xfb, 0xf6, 0xff, 0xfb, 0xfb, 0xf5, 0xff, 0xf9, 0xfa, 0xf5, 0xff, 0xf9, 0xfa, 0xf5, 0xff, 0xfa, 0xfa, 0xf6, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfc, 0xcb, 0x76, 0xff, 0xfd, 0xa3, 0x04, 0xff, 0xfd, 0xa7, 0x11, 0xff, 0xfd, 0xa7, 0x13, 0xff, 0xfd, 0xa8, 0x15, 0xff, 0xfd, 0xa9, 0x16, 0xff, 0xfd, 0xaa, 0x16, 0xff, 0xfd, 0xab, 0x15, 0xff, 0xfd, 0xab, 0x15, 0xff, 0xfd, 0xaa, 0x14, 0xff, 0xfd, 0xa4, 0x04, 0xff, 0xfc, 0xce, 0x74, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfc, 0xfc, 0xf8, 0xff, 0xfc, 0xfb, 0xf7, 0xff, 0xfc, 0xfd, 0xfb, 0xff, 0xfd, 0xf9, 0xe9, 0xff, 0xfc, 0xb4, 0x25, 0xff, 0xfd, 0xab, 0x0b, 0xff, 0xfd, 0xad, 0x13, 0xff, 0xfd, 0xac, 0x15, 0xff, 0xfd, 0xab, 0x14, 0xff, 0xfe, 0xab, 0x13, 0xff, 0xfd, 0xac, 0x14, 0xff, 0xfd, 0xad, 0x14, 0xff, 0xfd, 0xac, 0x14, 0xff, 0xfd, 0xaa, 0x0e, 0xff, 0xfc, 0xb0, 0x1d, 0xff, 0xfd, 0xf3, 0xe1, 0xff, 0xfc, 0xfa, 0xf6, 0xff, 0xfa, 0xf8, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xfa, 0xf8, 0xf4, 0xff, 0xfd, 0xf9, 0xf5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc6, 0xaf, 0xaa, 0xff, 0x4a, 0x03, 0x00, 0xff, 0x50, 0x0b, 0x01, 0xff, 0x54, 0x0e, 0x05, 0xff, 0x55, 0x10, 0x06, 0xff, 0x57, 0x11, 0x07, 0xff, 0x58, 0x12, 0x08, 0xff, 0x5a, 0x13, 0x08, 0xff, 0x5b, 0x14, 0x09, 0xff, 0x5c, 0x15, 0x09, 0xff, 0x5c, 0x16, 0x09, 0xff, 0x5c, 0x15, 0x09, 0xff, 0x5c, 0x13, 0x07, 0xff, 0x5b, 0x12, 0x05, 0xff, 0x54, 0x0e, 0x00, 0xff, 0x88, 0x1c, 0x26, 0xff, 0xdf, 0x3b, 0x6a, 0xff, 0x74, 0x14, 0x1c, 0xff, 0x46, 0x04, 0x00, 0xff, 0x4c, 0x03, 0x00, 0xff, 0x4c, 0x00, 0x00, 0xff, 0x72, 0x24, 0x01, 0xff, 0xad, 0x64, 0x02, 0xff, 0xcf, 0x91, 0x02, 0xff, 0xd5, 0x9e, 0x03, 0xff, 0xd2, 0x9c, 0x02, 0xff, 0xd1, 0x9a, 0x02, 0xff, 0xd5, 0x9b, 0x03, 0xff, 0xd6, 0x97, 0x03, 0xff, 0xc2, 0x7e, 0x03, 0xff, 0x8f, 0x45, 0x02, 0xff, 0x5a, 0x0b, 0x01, 0xff, 0x45, 0x00, 0x00, 0xff, 0x54, 0x05, 0x03, 0xff, 0xbe, 0x2c, 0x3f, 0xff, 0xc3, 0x2c, 0x3f, 0xff, 0x55, 0x05, 0x00, 0xff, 0x4f, 0x04, 0x00, 0xff, + 0xfd, 0xfa, 0xf5, 0xff, 0xfc, 0xfa, 0xf5, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xfa, 0xf8, 0xf3, 0xff, 0xf9, 0xf6, 0xf0, 0xff, 0xfa, 0xf4, 0xed, 0xff, 0xfb, 0xf3, 0xec, 0xff, 0xfb, 0xf0, 0xe8, 0xff, 0xfb, 0xed, 0xe5, 0xff, 0xfd, 0xed, 0xea, 0xff, 0xfc, 0xbe, 0x5f, 0xff, 0xfd, 0xa2, 0x09, 0xff, 0xfc, 0xa5, 0x13, 0xff, 0xfd, 0xa6, 0x15, 0xff, 0xfd, 0xa6, 0x16, 0xff, 0xfd, 0xa6, 0x16, 0xff, 0xfd, 0xa5, 0x17, 0xff, 0xfd, 0xa4, 0x17, 0xff, 0xfd, 0xa4, 0x16, 0xff, 0xfd, 0xa3, 0x15, 0xff, 0xfd, 0xa3, 0x15, 0xff, 0xfd, 0xa3, 0x16, 0xff, 0xfd, 0xa4, 0x16, 0xff, 0xfd, 0xa5, 0x17, 0xff, 0xfd, 0xa6, 0x18, 0xff, 0xfd, 0xa6, 0x18, 0xff, 0xfd, 0xa3, 0x11, 0xff, 0xfc, 0xae, 0x2a, 0xff, 0xfb, 0xf7, 0xea, 0xff, 0xfc, 0xfc, 0xfa, 0xff, 0xfb, 0xfb, 0xf6, 0xff, 0xfb, 0xfb, 0xf5, 0xff, 0xfa, 0xfb, 0xf5, 0xff, 0xfa, 0xfa, 0xf5, 0xff, 0xfa, 0xfa, 0xf5, 0xff, 0xfc, 0xfe, 0xfd, 0xff, 0xfd, 0xeb, 0xcc, 0xff, 0xfc, 0xa4, 0x13, 0xff, 0xfd, 0xa5, 0x10, 0xff, 0xfd, 0xa5, 0x14, 0xff, 0xfd, 0xa5, 0x16, 0xff, 0xfd, 0xa6, 0x18, 0xff, 0xfd, 0xa7, 0x17, 0xff, 0xfd, 0xa8, 0x17, 0xff, 0xfd, 0xa8, 0x19, 0xff, 0xfd, 0xa8, 0x17, 0xff, 0xfd, 0xa2, 0x06, 0xff, 0xfc, 0xcd, 0x74, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfc, 0xfc, 0xf8, 0xff, 0xfc, 0xfb, 0xf7, 0xff, 0xfc, 0xfd, 0xfa, 0xff, 0xfd, 0xf9, 0xeb, 0xff, 0xfc, 0xb4, 0x27, 0xff, 0xfd, 0xa9, 0x0c, 0xff, 0xfd, 0xab, 0x15, 0xff, 0xfc, 0xab, 0x16, 0xff, 0xfd, 0xaa, 0x16, 0xff, 0xfd, 0xaa, 0x15, 0xff, 0xfc, 0xab, 0x16, 0xff, 0xfd, 0xab, 0x17, 0xff, 0xfd, 0xab, 0x17, 0xff, 0xfd, 0xa8, 0x10, 0xff, 0xfc, 0xaf, 0x1e, 0xff, 0xfd, 0xf3, 0xe2, 0xff, 0xfc, 0xfa, 0xf7, 0xff, 0xfa, 0xf8, 0xf1, 0xff, 0xf8, 0xf8, 0xf1, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf9, 0xf7, 0xf2, 0xff, 0xfa, 0xf8, 0xf3, 0xff, 0xfc, 0xfa, 0xf5, 0xff, 0xfe, 0xfd, 0xf9, 0xff, 0xfa, 0xf7, 0xf3, 0xff, 0x6d, 0x35, 0x2d, 0xff, 0x4d, 0x04, 0x00, 0xff, 0x53, 0x0e, 0x04, 0xff, 0x54, 0x10, 0x06, 0xff, 0x57, 0x11, 0x07, 0xff, 0x57, 0x12, 0x08, 0xff, 0x58, 0x12, 0x09, 0xff, 0x5a, 0x13, 0x09, 0xff, 0x5d, 0x15, 0x09, 0xff, 0x5d, 0x15, 0x09, 0xff, 0x5c, 0x15, 0x09, 0xff, 0x5c, 0x14, 0x08, 0xff, 0x5d, 0x13, 0x06, 0xff, 0x58, 0x0f, 0x02, 0xff, 0x73, 0x14, 0x12, 0xff, 0xe1, 0x3c, 0x66, 0xff, 0x72, 0x14, 0x19, 0xff, 0x48, 0x04, 0x00, 0xff, 0x4a, 0x00, 0x00, 0xff, 0x70, 0x20, 0x01, 0xff, 0xc6, 0x7a, 0x02, 0xff, 0xd7, 0x99, 0x03, 0xff, 0xa4, 0x6a, 0x02, 0xff, 0x77, 0x33, 0x01, 0xff, 0x60, 0x17, 0x01, 0xff, 0x58, 0x0d, 0x01, 0xff, 0x55, 0x0b, 0x01, 0xff, 0x59, 0x10, 0x01, 0xff, 0x6a, 0x23, 0x01, 0xff, 0x92, 0x4f, 0x03, 0xff, 0xc8, 0x8b, 0x03, 0xff, 0xda, 0x9b, 0x03, 0xff, 0x96, 0x4d, 0x02, 0xff, 0x4c, 0x00, 0x00, 0xff, 0x45, 0x00, 0x00, 0xff, 0xb7, 0x28, 0x3d, 0xff, 0xba, 0x26, 0x3c, 0xff, 0x4a, 0x02, 0x00, 0xff, + 0xfd, 0xf9, 0xf4, 0xff, 0xfb, 0xfa, 0xf4, 0xff, 0xfb, 0xfa, 0xf4, 0xff, 0xfb, 0xf7, 0xf2, 0xff, 0xf9, 0xf5, 0xf0, 0xff, 0xfa, 0xf4, 0xed, 0xff, 0xfb, 0xf3, 0xec, 0xff, 0xfc, 0xf1, 0xe9, 0xff, 0xfc, 0xed, 0xe6, 0xff, 0xfd, 0xed, 0xe9, 0xff, 0xfc, 0xbd, 0x60, 0xff, 0xfd, 0xa1, 0x0c, 0xff, 0xfd, 0xa4, 0x16, 0xff, 0xfd, 0xa5, 0x19, 0xff, 0xfd, 0xa4, 0x1a, 0xff, 0xfd, 0xa4, 0x1a, 0xff, 0xfd, 0xa4, 0x19, 0xff, 0xfc, 0xa3, 0x18, 0xff, 0xfd, 0xa3, 0x17, 0xff, 0xfd, 0xa3, 0x18, 0xff, 0xfd, 0xa3, 0x18, 0xff, 0xfd, 0xa3, 0x19, 0xff, 0xfd, 0xa2, 0x19, 0xff, 0xfc, 0xa3, 0x19, 0xff, 0xfd, 0xa4, 0x1a, 0xff, 0xfd, 0xa4, 0x19, 0xff, 0xfd, 0x9d, 0x08, 0xff, 0xfb, 0xcc, 0x7b, 0xff, 0xfb, 0xff, 0xff, 0xff, 0xfb, 0xfb, 0xf7, 0xff, 0xfc, 0xfb, 0xf7, 0xff, 0xfc, 0xfb, 0xf7, 0xff, 0xfc, 0xfe, 0xfc, 0xff, 0xfc, 0xfb, 0xf7, 0xff, 0xfb, 0xfb, 0xf7, 0xff, 0xfc, 0xfc, 0xf8, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfb, 0xba, 0x51, 0xff, 0xfd, 0xa0, 0x0a, 0xff, 0xfd, 0xa4, 0x15, 0xff, 0xfd, 0xa3, 0x16, 0xff, 0xfd, 0xa3, 0x17, 0xff, 0xfd, 0xa4, 0x18, 0xff, 0xfd, 0xa5, 0x18, 0xff, 0xfd, 0xa5, 0x19, 0xff, 0xfd, 0xa5, 0x18, 0xff, 0xfd, 0x9f, 0x08, 0xff, 0xfc, 0xcb, 0x77, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfc, 0xfd, 0xf9, 0xff, 0xfc, 0xfc, 0xf8, 0xff, 0xfc, 0xfe, 0xfc, 0xff, 0xfd, 0xf9, 0xeb, 0xff, 0xfc, 0xb2, 0x28, 0xff, 0xfd, 0xa7, 0x0f, 0xff, 0xfd, 0xa9, 0x18, 0xff, 0xfd, 0xa8, 0x1a, 0xff, 0xfd, 0xa8, 0x19, 0xff, 0xfd, 0xa8, 0x19, 0xff, 0xfd, 0xa8, 0x1b, 0xff, 0xfc, 0xa8, 0x1b, 0xff, 0xfd, 0xa9, 0x1b, 0xff, 0xfd, 0xa7, 0x13, 0xff, 0xfc, 0xaf, 0x20, 0xff, 0xfc, 0xf3, 0xe2, 0xff, 0xfc, 0xfa, 0xf7, 0xff, 0xfa, 0xf8, 0xf1, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xfa, 0xf8, 0xf3, 0xff, 0xfc, 0xfa, 0xf5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc5, 0xae, 0xa8, 0xff, 0x49, 0x02, 0x00, 0xff, 0x53, 0x0d, 0x02, 0xff, 0x54, 0x0f, 0x05, 0xff, 0x56, 0x11, 0x06, 0xff, 0x57, 0x12, 0x08, 0xff, 0x58, 0x12, 0x09, 0xff, 0x5a, 0x12, 0x09, 0xff, 0x5b, 0x13, 0x0a, 0xff, 0x5c, 0x14, 0x0a, 0xff, 0x5b, 0x15, 0x09, 0xff, 0x5a, 0x14, 0x09, 0xff, 0x5b, 0x13, 0x08, 0xff, 0x5c, 0x11, 0x05, 0xff, 0x5a, 0x0e, 0x03, 0xff, 0xcc, 0x32, 0x4b, 0xff, 0x91, 0x1f, 0x28, 0xff, 0x47, 0x03, 0x00, 0xff, 0x4b, 0x00, 0x00, 0xff, 0x97, 0x46, 0x02, 0xff, 0xe5, 0x97, 0x03, 0xff, 0x9a, 0x55, 0x02, 0xff, 0x53, 0x0f, 0x01, 0xff, 0x49, 0x00, 0x01, 0xff, 0x50, 0x05, 0x01, 0xff, 0x54, 0x08, 0x01, 0xff, 0x56, 0x0a, 0x02, 0xff, 0x56, 0x0a, 0x02, 0xff, 0x53, 0x08, 0x01, 0xff, 0x51, 0x05, 0x00, 0xff, 0x4d, 0x01, 0x00, 0xff, 0x4e, 0x02, 0x01, 0xff, 0x77, 0x2c, 0x02, 0xff, 0xd0, 0x8b, 0x02, 0xff, 0xc6, 0x7e, 0x03, 0xff, 0x56, 0x0c, 0x00, 0xff, 0x46, 0x00, 0x00, 0xff, 0xc8, 0x2a, 0x46, 0xff, 0x9a, 0x1a, 0x2c, 0xff, + 0xfd, 0xfa, 0xf4, 0xff, 0xfb, 0xfb, 0xf5, 0xff, 0xfb, 0xf9, 0xf4, 0xff, 0xfa, 0xf7, 0xf2, 0xff, 0xfb, 0xf6, 0xf0, 0xff, 0xfc, 0xf5, 0xee, 0xff, 0xfc, 0xf3, 0xec, 0xff, 0xfc, 0xf1, 0xe9, 0xff, 0xfd, 0xed, 0xe6, 0xff, 0xfd, 0xec, 0xe9, 0xff, 0xfc, 0xbc, 0x61, 0xff, 0xfd, 0x9e, 0x0f, 0xff, 0xfd, 0xa2, 0x1b, 0xff, 0xfd, 0xa3, 0x1d, 0xff, 0xfd, 0xa3, 0x1e, 0xff, 0xfd, 0xa2, 0x1f, 0xff, 0xfd, 0xa2, 0x1d, 0xff, 0xfd, 0xa1, 0x1a, 0xff, 0xfd, 0xa1, 0x19, 0xff, 0xfd, 0xa1, 0x1a, 0xff, 0xfd, 0xa1, 0x1b, 0xff, 0xfc, 0xa1, 0x1b, 0xff, 0xfd, 0xa1, 0x1c, 0xff, 0xfd, 0xa1, 0x1b, 0xff, 0xfd, 0xa1, 0x1c, 0xff, 0xfd, 0x9f, 0x17, 0xff, 0xfc, 0xa1, 0x17, 0xff, 0xfc, 0xed, 0xd1, 0xff, 0xfd, 0xff, 0xfe, 0xff, 0xfc, 0xfb, 0xf7, 0xff, 0xfd, 0xfc, 0xf8, 0xff, 0xfc, 0xf8, 0xf0, 0xff, 0xfa, 0xe8, 0xc9, 0xff, 0xfc, 0xfd, 0xfb, 0xff, 0xfc, 0xfc, 0xf8, 0xff, 0xfc, 0xfc, 0xf8, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfc, 0xdd, 0xa8, 0xff, 0xfc, 0x9e, 0x0a, 0xff, 0xfd, 0xa2, 0x15, 0xff, 0xfd, 0xa2, 0x15, 0xff, 0xfd, 0xa2, 0x17, 0xff, 0xfd, 0xa3, 0x18, 0xff, 0xfd, 0xa4, 0x19, 0xff, 0xfd, 0xa4, 0x19, 0xff, 0xfd, 0xa4, 0x18, 0xff, 0xfd, 0x9d, 0x09, 0xff, 0xfc, 0xca, 0x79, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfd, 0xfd, 0xf9, 0xff, 0xfd, 0xfd, 0xf9, 0xff, 0xfc, 0xfe, 0xfe, 0xff, 0xfd, 0xf9, 0xeb, 0xff, 0xfc, 0xb0, 0x2b, 0xff, 0xfd, 0xa5, 0x11, 0xff, 0xfd, 0xa6, 0x1a, 0xff, 0xfd, 0xa5, 0x1b, 0xff, 0xfd, 0xa5, 0x1a, 0xff, 0xfd, 0xa6, 0x1a, 0xff, 0xfd, 0xa6, 0x1b, 0xff, 0xfd, 0xa7, 0x1b, 0xff, 0xfd, 0xa8, 0x1b, 0xff, 0xfd, 0xa5, 0x14, 0xff, 0xfc, 0xae, 0x23, 0xff, 0xfc, 0xf3, 0xe2, 0xff, 0xfc, 0xf9, 0xf6, 0xff, 0xfb, 0xf8, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xfa, 0xf8, 0xf3, 0xff, 0xfd, 0xf9, 0xf5, 0xff, 0xff, 0xff, 0xff, 0xff, 0x84, 0x55, 0x4f, 0xff, 0x4a, 0x01, 0x00, 0xff, 0x54, 0x0e, 0x04, 0xff, 0x55, 0x10, 0x05, 0xff, 0x57, 0x11, 0x07, 0xff, 0x57, 0x12, 0x08, 0xff, 0x58, 0x12, 0x08, 0xff, 0x59, 0x13, 0x09, 0xff, 0x5a, 0x14, 0x0a, 0xff, 0x5a, 0x14, 0x0a, 0xff, 0x5a, 0x14, 0x09, 0xff, 0x5a, 0x13, 0x0a, 0xff, 0x5c, 0x13, 0x08, 0xff, 0x57, 0x0f, 0x03, 0xff, 0x7a, 0x1b, 0x15, 0xff, 0xd3, 0x3e, 0x45, 0xff, 0x49, 0x03, 0x00, 0xff, 0x48, 0x00, 0x00, 0xff, 0xa6, 0x55, 0x03, 0xff, 0xdc, 0x8c, 0x03, 0xff, 0x6c, 0x22, 0x02, 0xff, 0x4b, 0x03, 0x01, 0xff, 0x54, 0x0b, 0x01, 0xff, 0x58, 0x0d, 0x01, 0xff, 0x58, 0x0d, 0x01, 0xff, 0x59, 0x0d, 0x01, 0xff, 0x59, 0x0d, 0x02, 0xff, 0x58, 0x0c, 0x02, 0xff, 0x56, 0x0b, 0x01, 0xff, 0x56, 0x0b, 0x01, 0xff, 0x59, 0x0b, 0x01, 0xff, 0x5a, 0x0b, 0x00, 0xff, 0x53, 0x05, 0x00, 0xff, 0x53, 0x05, 0x01, 0xff, 0xa8, 0x5d, 0x02, 0xff, 0xd6, 0x88, 0x03, 0xff, 0x54, 0x0c, 0x00, 0xff, 0x56, 0x03, 0x06, 0xff, 0xde, 0x32, 0x55, 0xff, + 0xfd, 0xfa, 0xf5, 0xff, 0xfb, 0xfb, 0xf6, 0xff, 0xfb, 0xf9, 0xf4, 0xff, 0xfb, 0xf8, 0xf3, 0xff, 0xfa, 0xf6, 0xf1, 0xff, 0xfb, 0xf5, 0xee, 0xff, 0xfb, 0xf3, 0xec, 0xff, 0xfc, 0xf0, 0xe9, 0xff, 0xfd, 0xed, 0xe6, 0xff, 0xfd, 0xed, 0xe9, 0xff, 0xfc, 0xba, 0x62, 0xff, 0xfd, 0x9b, 0x10, 0xff, 0xfd, 0xa0, 0x1d, 0xff, 0xfd, 0xa1, 0x20, 0xff, 0xfd, 0xa1, 0x20, 0xff, 0xfd, 0xa0, 0x20, 0xff, 0xfd, 0x9f, 0x1f, 0xff, 0xfd, 0x9f, 0x1e, 0xff, 0xfd, 0x9f, 0x1d, 0xff, 0xfd, 0xa0, 0x1e, 0xff, 0xfd, 0xa0, 0x1e, 0xff, 0xfd, 0xa1, 0x1f, 0xff, 0xfd, 0xa1, 0x1e, 0xff, 0xfd, 0xa1, 0x1d, 0xff, 0xfd, 0x9f, 0x1d, 0xff, 0xfd, 0x9a, 0x12, 0xff, 0xfc, 0xba, 0x57, 0xff, 0xfd, 0xff, 0xfe, 0xff, 0xfc, 0xfd, 0xfa, 0xff, 0xfc, 0xfc, 0xf9, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfb, 0xe4, 0xc2, 0xff, 0xf9, 0xa6, 0x3a, 0xff, 0xfc, 0xfb, 0xf5, 0xff, 0xfb, 0xfd, 0xfa, 0xff, 0xfb, 0xfc, 0xf8, 0xff, 0xfb, 0xfd, 0xfb, 0xff, 0xfc, 0xf8, 0xed, 0xff, 0xfc, 0xa9, 0x32, 0xff, 0xfd, 0x9e, 0x0e, 0xff, 0xfd, 0xa0, 0x14, 0xff, 0xfd, 0x9f, 0x17, 0xff, 0xfd, 0xa1, 0x18, 0xff, 0xfc, 0xa1, 0x19, 0xff, 0xfc, 0xa2, 0x18, 0xff, 0xfd, 0xa1, 0x18, 0xff, 0xfd, 0x9a, 0x0b, 0xff, 0xfc, 0xc8, 0x7b, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfd, 0xfd, 0xf8, 0xff, 0xfc, 0xfc, 0xf8, 0xff, 0xfc, 0xfe, 0xfd, 0xff, 0xfd, 0xf8, 0xec, 0xff, 0xfc, 0xaf, 0x2f, 0xff, 0xfd, 0xa3, 0x13, 0xff, 0xfd, 0xa3, 0x1b, 0xff, 0xfd, 0xa3, 0x1b, 0xff, 0xfd, 0xa3, 0x19, 0xff, 0xfd, 0xa4, 0x1a, 0xff, 0xfd, 0xa4, 0x1d, 0xff, 0xfd, 0xa5, 0x1c, 0xff, 0xfd, 0xa6, 0x1a, 0xff, 0xfd, 0xa3, 0x15, 0xff, 0xfc, 0xaa, 0x23, 0xff, 0xfc, 0xf2, 0xe0, 0xff, 0xfc, 0xf9, 0xf6, 0xff, 0xfa, 0xf7, 0xf2, 0xff, 0xf9, 0xf8, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf1, 0xff, 0xfa, 0xf9, 0xf3, 0xff, 0xff, 0xfd, 0xf9, 0xff, 0xec, 0xe5, 0xe1, 0xff, 0x5a, 0x1a, 0x12, 0xff, 0x50, 0x09, 0x00, 0xff, 0x54, 0x0f, 0x04, 0xff, 0x56, 0x10, 0x06, 0xff, 0x57, 0x11, 0x07, 0xff, 0x57, 0x11, 0x08, 0xff, 0x57, 0x12, 0x08, 0xff, 0x58, 0x13, 0x09, 0xff, 0x59, 0x14, 0x09, 0xff, 0x59, 0x14, 0x09, 0xff, 0x5a, 0x13, 0x0a, 0xff, 0x5a, 0x13, 0x09, 0xff, 0x5b, 0x13, 0x08, 0xff, 0x58, 0x10, 0x05, 0xff, 0x6a, 0x17, 0x0d, 0xff, 0xdd, 0x4d, 0x2f, 0xff, 0x71, 0x17, 0x0a, 0xff, 0x91, 0x41, 0x02, 0xff, 0xdf, 0x88, 0x05, 0xff, 0x62, 0x16, 0x03, 0xff, 0x50, 0x08, 0x02, 0xff, 0x57, 0x0e, 0x03, 0xff, 0x57, 0x0f, 0x03, 0xff, 0x58, 0x0e, 0x02, 0xff, 0x58, 0x0e, 0x02, 0xff, 0x58, 0x0e, 0x02, 0xff, 0x57, 0x0d, 0x01, 0xff, 0x53, 0x0a, 0x00, 0xff, 0x4f, 0x07, 0x00, 0xff, 0x4d, 0x06, 0x00, 0xff, 0x51, 0x07, 0x00, 0xff, 0x56, 0x09, 0x00, 0xff, 0x5a, 0x0b, 0x00, 0xff, 0x58, 0x0a, 0x00, 0xff, 0x4e, 0x00, 0x01, 0xff, 0xa7, 0x55, 0x02, 0xff, 0xcc, 0x7b, 0x03, 0xff, 0x47, 0x02, 0x00, 0xff, 0x73, 0x11, 0x16, 0xff, + 0xfd, 0xfa, 0xf5, 0xff, 0xfc, 0xfa, 0xf5, 0xff, 0xfc, 0xf9, 0xf4, 0xff, 0xfb, 0xf8, 0xf3, 0xff, 0xf9, 0xf6, 0xf1, 0xff, 0xfa, 0xf4, 0xef, 0xff, 0xfa, 0xf2, 0xed, 0xff, 0xfc, 0xef, 0xe9, 0xff, 0xfd, 0xed, 0xe5, 0xff, 0xfd, 0xee, 0xe9, 0xff, 0xfc, 0xba, 0x64, 0xff, 0xfd, 0x9a, 0x11, 0xff, 0xfd, 0x9e, 0x1e, 0xff, 0xfd, 0x9e, 0x21, 0xff, 0xfd, 0x9e, 0x22, 0xff, 0xfd, 0x9d, 0x21, 0xff, 0xfd, 0x9c, 0x21, 0xff, 0xfd, 0x9c, 0x20, 0xff, 0xfd, 0x9e, 0x1f, 0xff, 0xfd, 0x9e, 0x1f, 0xff, 0xfd, 0x9e, 0x20, 0xff, 0xfd, 0x9f, 0x21, 0xff, 0xfd, 0x9f, 0x20, 0xff, 0xfd, 0x9f, 0x20, 0xff, 0xfd, 0x9d, 0x20, 0xff, 0xfc, 0x99, 0x15, 0xff, 0xfb, 0xdd, 0xaf, 0xff, 0xfc, 0xff, 0xff, 0xff, 0xfc, 0xfd, 0xf9, 0xff, 0xfd, 0xfd, 0xf9, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfb, 0xc5, 0x80, 0xff, 0xfc, 0x8b, 0x03, 0xff, 0xfc, 0xe5, 0xc5, 0xff, 0xfc, 0xff, 0xff, 0xff, 0xfb, 0xfc, 0xf8, 0xff, 0xfb, 0xfc, 0xf7, 0xff, 0xfc, 0xff, 0xff, 0xff, 0xfc, 0xc9, 0x82, 0xff, 0xfd, 0x97, 0x09, 0xff, 0xfd, 0x9c, 0x16, 0xff, 0xfd, 0x9d, 0x18, 0xff, 0xfd, 0x9f, 0x1b, 0xff, 0xfc, 0x9f, 0x1c, 0xff, 0xfd, 0x9f, 0x1b, 0xff, 0xfd, 0x9e, 0x1a, 0xff, 0xfd, 0x97, 0x0d, 0xff, 0xfc, 0xc7, 0x7b, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfd, 0xfd, 0xf8, 0xff, 0xfc, 0xfc, 0xf7, 0xff, 0xfb, 0xfe, 0xfd, 0xff, 0xfd, 0xf8, 0xee, 0xff, 0xfc, 0xac, 0x31, 0xff, 0xfd, 0xa0, 0x14, 0xff, 0xfd, 0xa1, 0x1c, 0xff, 0xfd, 0xa1, 0x1c, 0xff, 0xfd, 0xa1, 0x19, 0xff, 0xfd, 0xa2, 0x1b, 0xff, 0xfd, 0xa2, 0x1d, 0xff, 0xfd, 0xa2, 0x1d, 0xff, 0xfd, 0xa2, 0x1b, 0xff, 0xfd, 0xa0, 0x16, 0xff, 0xfc, 0xa8, 0x25, 0xff, 0xfd, 0xf1, 0xe0, 0xff, 0xfc, 0xf9, 0xf6, 0xff, 0xfa, 0xf8, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf9, 0xf8, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xfa, 0xf8, 0xf3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc9, 0xb4, 0xb0, 0xff, 0x4b, 0x03, 0x00, 0xff, 0x54, 0x0d, 0x02, 0xff, 0x55, 0x0f, 0x05, 0xff, 0x56, 0x11, 0x06, 0xff, 0x57, 0x11, 0x07, 0xff, 0x57, 0x12, 0x08, 0xff, 0x58, 0x12, 0x08, 0xff, 0x59, 0x14, 0x08, 0xff, 0x59, 0x14, 0x08, 0xff, 0x5a, 0x14, 0x09, 0xff, 0x5b, 0x14, 0x09, 0xff, 0x5a, 0x14, 0x09, 0xff, 0x5b, 0x14, 0x09, 0xff, 0x5b, 0x13, 0x07, 0xff, 0x54, 0x0d, 0x03, 0xff, 0x83, 0x24, 0x0f, 0xff, 0xe1, 0x63, 0x18, 0xff, 0xda, 0x75, 0x07, 0xff, 0x6f, 0x20, 0x04, 0xff, 0x53, 0x0a, 0x03, 0xff, 0x5a, 0x10, 0x04, 0xff, 0x59, 0x10, 0x04, 0xff, 0x5a, 0x10, 0x04, 0xff, 0x5a, 0x0f, 0x03, 0xff, 0x59, 0x0e, 0x02, 0xff, 0x52, 0x0a, 0x00, 0xff, 0x55, 0x0a, 0x00, 0xff, 0x6f, 0x17, 0x07, 0xff, 0x8a, 0x25, 0x0e, 0xff, 0x94, 0x29, 0x10, 0xff, 0x86, 0x21, 0x0e, 0xff, 0x67, 0x10, 0x06, 0xff, 0x50, 0x06, 0x00, 0xff, 0x52, 0x08, 0x00, 0xff, 0x57, 0x09, 0x01, 0xff, 0x51, 0x02, 0x01, 0xff, 0xc7, 0x70, 0x03, 0xff, 0xa3, 0x50, 0x03, 0xff, 0x3b, 0x00, 0x00, 0xff, + 0xfd, 0xf9, 0xf5, 0xff, 0xfc, 0xf9, 0xf4, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xfa, 0xf7, 0xf3, 0xff, 0xf9, 0xf5, 0xf1, 0xff, 0xf9, 0xf4, 0xef, 0xff, 0xf9, 0xf2, 0xed, 0xff, 0xfc, 0xef, 0xe9, 0xff, 0xfd, 0xec, 0xe5, 0xff, 0xfd, 0xed, 0xe9, 0xff, 0xfc, 0xba, 0x67, 0xff, 0xfd, 0x99, 0x15, 0xff, 0xfd, 0x9b, 0x20, 0xff, 0xfd, 0x9b, 0x21, 0xff, 0xfd, 0x9b, 0x22, 0xff, 0xfd, 0x9a, 0x22, 0xff, 0xfd, 0x9a, 0x22, 0xff, 0xfd, 0x9a, 0x21, 0xff, 0xfd, 0x9a, 0x21, 0xff, 0xfd, 0x9b, 0x21, 0xff, 0xfd, 0x9c, 0x22, 0xff, 0xfd, 0x9d, 0x22, 0xff, 0xfc, 0x9d, 0x22, 0xff, 0xfd, 0x9d, 0x22, 0xff, 0xfd, 0x99, 0x1b, 0xff, 0xfc, 0xa8, 0x3d, 0xff, 0xfc, 0xf9, 0xf0, 0xff, 0xfc, 0xfd, 0xfb, 0xff, 0xfc, 0xfc, 0xf8, 0xff, 0xfd, 0xfe, 0xfb, 0xff, 0xfd, 0xfa, 0xf3, 0xff, 0xfa, 0xa3, 0x3b, 0xff, 0xfd, 0x8b, 0x04, 0xff, 0xfc, 0xc2, 0x7a, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfc, 0xfd, 0xfa, 0xff, 0xfb, 0xfc, 0xf8, 0xff, 0xfc, 0xff, 0xfe, 0xff, 0xfd, 0xed, 0xd5, 0xff, 0xfc, 0x9c, 0x1d, 0xff, 0xfd, 0x9a, 0x16, 0xff, 0xfd, 0x9c, 0x1a, 0xff, 0xfd, 0x9d, 0x1d, 0xff, 0xfd, 0x9e, 0x1e, 0xff, 0xfd, 0x9d, 0x1e, 0xff, 0xfd, 0x9c, 0x1d, 0xff, 0xfd, 0x96, 0x0f, 0xff, 0xfc, 0xc6, 0x7d, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfd, 0xfd, 0xf9, 0xff, 0xfc, 0xfc, 0xf8, 0xff, 0xfc, 0xfe, 0xfd, 0xff, 0xfd, 0xf8, 0xef, 0xff, 0xfc, 0xaa, 0x33, 0xff, 0xfd, 0x9d, 0x18, 0xff, 0xfd, 0xa0, 0x1e, 0xff, 0xfd, 0xa0, 0x1e, 0xff, 0xfc, 0xa0, 0x1e, 0xff, 0xfd, 0xa1, 0x1d, 0xff, 0xfd, 0xa1, 0x1c, 0xff, 0xfd, 0xa1, 0x1c, 0xff, 0xfd, 0xa0, 0x1c, 0xff, 0xfd, 0x9d, 0x18, 0xff, 0xfc, 0xa7, 0x29, 0xff, 0xfc, 0xf1, 0xe0, 0xff, 0xfc, 0xf9, 0xf5, 0xff, 0xfb, 0xf7, 0xf2, 0xff, 0xfa, 0xf9, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xfa, 0xf4, 0xff, 0xfb, 0xf9, 0xf4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa7, 0x85, 0x81, 0xff, 0x49, 0x00, 0x00, 0xff, 0x55, 0x0d, 0x03, 0xff, 0x56, 0x0f, 0x05, 0xff, 0x58, 0x11, 0x07, 0xff, 0x56, 0x12, 0x07, 0xff, 0x57, 0x12, 0x07, 0xff, 0x59, 0x13, 0x07, 0xff, 0x59, 0x14, 0x08, 0xff, 0x5a, 0x15, 0x08, 0xff, 0x5c, 0x14, 0x09, 0xff, 0x5d, 0x15, 0x09, 0xff, 0x5d, 0x15, 0x09, 0xff, 0x5d, 0x14, 0x08, 0xff, 0x5d, 0x13, 0x07, 0xff, 0x5d, 0x11, 0x05, 0xff, 0x57, 0x0c, 0x03, 0xff, 0x6b, 0x19, 0x07, 0xff, 0x65, 0x16, 0x06, 0xff, 0x56, 0x0c, 0x04, 0xff, 0x5b, 0x11, 0x05, 0xff, 0x5b, 0x11, 0x05, 0xff, 0x5c, 0x10, 0x04, 0xff, 0x5c, 0x10, 0x04, 0xff, 0x5a, 0x0f, 0x03, 0xff, 0x53, 0x0a, 0x01, 0xff, 0x82, 0x20, 0x0a, 0xff, 0xd8, 0x4a, 0x1b, 0xff, 0xfb, 0x5f, 0x24, 0xff, 0xf4, 0x5f, 0x26, 0xff, 0xed, 0x5c, 0x28, 0xff, 0xf6, 0x5a, 0x2c, 0xff, 0xf6, 0x53, 0x2c, 0xff, 0xc5, 0x39, 0x20, 0xff, 0x71, 0x14, 0x0a, 0xff, 0x50, 0x07, 0x00, 0xff, 0x53, 0x06, 0x00, 0xff, 0x6c, 0x17, 0x01, 0xff, 0xde, 0x81, 0x04, 0xff, 0x57, 0x0f, 0x02, 0xff, + 0xfd, 0xfa, 0xf5, 0xff, 0xfb, 0xf9, 0xf4, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xfa, 0xf7, 0xf2, 0xff, 0xf9, 0xf5, 0xf0, 0xff, 0xf8, 0xf3, 0xef, 0xff, 0xfa, 0xf2, 0xec, 0xff, 0xfb, 0xef, 0xe9, 0xff, 0xfd, 0xed, 0xe5, 0xff, 0xfd, 0xed, 0xea, 0xff, 0xfc, 0xb6, 0x69, 0xff, 0xfd, 0x95, 0x18, 0xff, 0xfd, 0x98, 0x22, 0xff, 0xfd, 0x97, 0x23, 0xff, 0xfd, 0x97, 0x24, 0xff, 0xfd, 0x96, 0x25, 0xff, 0xfd, 0x96, 0x24, 0xff, 0xfd, 0x97, 0x24, 0xff, 0xfd, 0x97, 0x24, 0xff, 0xfd, 0x97, 0x23, 0xff, 0xfd, 0x99, 0x24, 0xff, 0xfd, 0x9b, 0x24, 0xff, 0xfd, 0x9b, 0x24, 0xff, 0xfd, 0x9a, 0x23, 0xff, 0xfd, 0x92, 0x16, 0xff, 0xfc, 0xcc, 0x8e, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfc, 0xfc, 0xf8, 0xff, 0xfb, 0xfc, 0xf8, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfb, 0xe0, 0xbe, 0xff, 0xfb, 0x8f, 0x17, 0xff, 0xfd, 0x90, 0x14, 0xff, 0xfc, 0x9e, 0x33, 0xff, 0xfd, 0xf8, 0xf0, 0xff, 0xfc, 0xfe, 0xfe, 0xff, 0xfc, 0xfd, 0xfa, 0xff, 0xfc, 0xfd, 0xf9, 0xff, 0xfd, 0xff, 0xfe, 0xff, 0xfc, 0xb7, 0x5e, 0xff, 0xfd, 0x95, 0x10, 0xff, 0xfd, 0x9a, 0x1b, 0xff, 0xfd, 0x9b, 0x1d, 0xff, 0xfd, 0x9b, 0x1f, 0xff, 0xfd, 0x9b, 0x20, 0xff, 0xfd, 0x9b, 0x1f, 0xff, 0xfd, 0x95, 0x12, 0xff, 0xfc, 0xc6, 0x7f, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfd, 0xfd, 0xf9, 0xff, 0xfc, 0xfc, 0xf8, 0xff, 0xfc, 0xfe, 0xfc, 0xff, 0xfd, 0xf8, 0xed, 0xff, 0xfc, 0xa8, 0x34, 0xff, 0xfd, 0x9c, 0x1b, 0xff, 0xfd, 0x9e, 0x20, 0xff, 0xfd, 0x9e, 0x1f, 0xff, 0xfd, 0x9f, 0x21, 0xff, 0xfd, 0x9f, 0x20, 0xff, 0xfd, 0x9f, 0x20, 0xff, 0xfd, 0x9f, 0x1f, 0xff, 0xfd, 0x9f, 0x1f, 0xff, 0xfd, 0x9c, 0x1a, 0xff, 0xfc, 0xa5, 0x2d, 0xff, 0xfd, 0xf2, 0xe1, 0xff, 0xfd, 0xf9, 0xf5, 0xff, 0xfb, 0xf7, 0xf2, 0xff, 0xfa, 0xf8, 0xf2, 0xff, 0xfa, 0xf8, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xfb, 0xf9, 0xf4, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8e, 0x61, 0x5b, 0xff, 0x4b, 0x00, 0x00, 0xff, 0x55, 0x0d, 0x02, 0xff, 0x56, 0x10, 0x05, 0xff, 0x57, 0x12, 0x07, 0xff, 0x58, 0x13, 0x07, 0xff, 0x59, 0x15, 0x07, 0xff, 0x5b, 0x15, 0x06, 0xff, 0x5d, 0x16, 0x07, 0xff, 0x5f, 0x17, 0x07, 0xff, 0x60, 0x17, 0x08, 0xff, 0x61, 0x17, 0x08, 0xff, 0x60, 0x16, 0x08, 0xff, 0x60, 0x16, 0x08, 0xff, 0x60, 0x14, 0x08, 0xff, 0x5f, 0x12, 0x07, 0xff, 0x5d, 0x11, 0x06, 0xff, 0x5a, 0x0f, 0x04, 0xff, 0x59, 0x0f, 0x05, 0xff, 0x5a, 0x11, 0x05, 0xff, 0x5a, 0x11, 0x05, 0xff, 0x5d, 0x12, 0x05, 0xff, 0x5d, 0x11, 0x05, 0xff, 0x5b, 0x0f, 0x03, 0xff, 0x58, 0x0c, 0x02, 0xff, 0xac, 0x37, 0x14, 0xff, 0xff, 0x63, 0x25, 0xff, 0xd5, 0x49, 0x1a, 0xff, 0x86, 0x20, 0x08, 0xff, 0x62, 0x0d, 0x01, 0xff, 0x5c, 0x09, 0x01, 0xff, 0x61, 0x0b, 0x01, 0xff, 0x85, 0x1c, 0x0c, 0xff, 0xd9, 0x3e, 0x29, 0xff, 0xfc, 0x47, 0x3a, 0xff, 0x93, 0x1d, 0x18, 0xff, 0x50, 0x06, 0x00, 0xff, 0x54, 0x05, 0x00, 0xff, 0x78, 0x25, 0x02, 0xff, 0x5d, 0x0d, 0x01, 0xff, + 0xfd, 0xfb, 0xf6, 0xff, 0xfb, 0xfb, 0xf5, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xfa, 0xf7, 0xf2, 0xff, 0xf9, 0xf5, 0xf0, 0xff, 0xf8, 0xf3, 0xee, 0xff, 0xfa, 0xf1, 0xec, 0xff, 0xfb, 0xef, 0xe9, 0xff, 0xfd, 0xed, 0xe6, 0xff, 0xfd, 0xed, 0xea, 0xff, 0xfc, 0xb5, 0x6a, 0xff, 0xfd, 0x93, 0x1b, 0xff, 0xfd, 0x96, 0x24, 0xff, 0xfd, 0x95, 0x25, 0xff, 0xfe, 0x95, 0x27, 0xff, 0xfd, 0x95, 0x28, 0xff, 0xfd, 0x94, 0x28, 0xff, 0xfd, 0x94, 0x28, 0xff, 0xfe, 0x94, 0x28, 0xff, 0xfd, 0x95, 0x28, 0xff, 0xfd, 0x96, 0x28, 0xff, 0xfd, 0x97, 0x28, 0xff, 0xfd, 0x97, 0x27, 0xff, 0xfd, 0x95, 0x22, 0xff, 0xfc, 0x9a, 0x2c, 0xff, 0xfd, 0xf1, 0xdd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfb, 0xfc, 0xf8, 0xff, 0xfc, 0xfd, 0xf9, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfb, 0xba, 0x74, 0xff, 0xfd, 0x8c, 0x15, 0xff, 0xfd, 0x92, 0x1f, 0xff, 0xfc, 0x8d, 0x14, 0xff, 0xfb, 0xdb, 0xb4, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfc, 0xfd, 0xfa, 0xff, 0xfc, 0xfc, 0xf8, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfc, 0xdd, 0xb7, 0xff, 0xfc, 0x92, 0x15, 0xff, 0xfd, 0x96, 0x1d, 0xff, 0xfd, 0x97, 0x1f, 0xff, 0xfd, 0x97, 0x21, 0xff, 0xfd, 0x98, 0x22, 0xff, 0xfd, 0x99, 0x22, 0xff, 0xfd, 0x92, 0x16, 0xff, 0xfc, 0xc5, 0x82, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfc, 0xfd, 0xf9, 0xff, 0xfb, 0xfc, 0xf8, 0xff, 0xfb, 0xfe, 0xfb, 0xff, 0xfd, 0xf7, 0xeb, 0xff, 0xfc, 0xa6, 0x34, 0xff, 0xfd, 0x9a, 0x1a, 0xff, 0xfd, 0x9c, 0x21, 0xff, 0xfd, 0x9c, 0x23, 0xff, 0xfd, 0x9c, 0x23, 0xff, 0xfd, 0x9d, 0x24, 0xff, 0xfd, 0x9c, 0x24, 0xff, 0xfd, 0x9c, 0x22, 0xff, 0xfc, 0x9c, 0x22, 0xff, 0xfd, 0x9a, 0x1c, 0xff, 0xfc, 0xa4, 0x30, 0xff, 0xfd, 0xf2, 0xe2, 0xff, 0xfc, 0xf8, 0xf4, 0xff, 0xfb, 0xf7, 0xf1, 0xff, 0xfa, 0xf7, 0xf3, 0xff, 0xfa, 0xf7, 0xf2, 0xff, 0xfa, 0xf7, 0xf2, 0xff, 0xfc, 0xf9, 0xf5, 0xff, 0xff, 0xff, 0xfd, 0xff, 0x81, 0x4c, 0x46, 0xff, 0x4e, 0x01, 0x00, 0xff, 0x56, 0x0d, 0x01, 0xff, 0x58, 0x10, 0x04, 0xff, 0x5a, 0x14, 0x07, 0xff, 0x5d, 0x16, 0x07, 0xff, 0x5e, 0x17, 0x07, 0xff, 0x60, 0x19, 0x06, 0xff, 0x62, 0x1a, 0x07, 0xff, 0x64, 0x1a, 0x07, 0xff, 0x65, 0x1b, 0x08, 0xff, 0x65, 0x1a, 0x08, 0xff, 0x63, 0x19, 0x08, 0xff, 0x64, 0x18, 0x08, 0xff, 0x63, 0x17, 0x08, 0xff, 0x60, 0x15, 0x08, 0xff, 0x5e, 0x13, 0x06, 0xff, 0x5c, 0x12, 0x06, 0xff, 0x5b, 0x11, 0x06, 0xff, 0x5a, 0x11, 0x05, 0xff, 0x5a, 0x11, 0x05, 0xff, 0x5c, 0x11, 0x05, 0xff, 0x5e, 0x0f, 0x04, 0xff, 0x55, 0x0b, 0x02, 0xff, 0xa8, 0x36, 0x15, 0xff, 0xff, 0x67, 0x28, 0xff, 0xa7, 0x31, 0x13, 0xff, 0x5f, 0x0a, 0x00, 0xff, 0x8f, 0x1f, 0x10, 0xff, 0xc7, 0x38, 0x2b, 0xff, 0xd5, 0x39, 0x3b, 0xff, 0xc6, 0x2b, 0x3e, 0xff, 0x8c, 0x17, 0x23, 0xff, 0x51, 0x03, 0x00, 0xff, 0x9e, 0x21, 0x1c, 0xff, 0xff, 0x43, 0x4a, 0xff, 0x8c, 0x18, 0x19, 0xff, 0x52, 0x06, 0x00, 0xff, 0x56, 0x05, 0x00, 0xff, 0x5e, 0x09, 0x01, 0xff, + 0xfd, 0xfb, 0xf6, 0xff, 0xfb, 0xfb, 0xf5, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xf9, 0xf7, 0xf1, 0xff, 0xf9, 0xf5, 0xef, 0xff, 0xf8, 0xf3, 0xee, 0xff, 0xfb, 0xf1, 0xeb, 0xff, 0xfc, 0xef, 0xe9, 0xff, 0xfd, 0xec, 0xe6, 0xff, 0xfd, 0xed, 0xea, 0xff, 0xfc, 0xb3, 0x6c, 0xff, 0xfd, 0x91, 0x1e, 0xff, 0xfd, 0x94, 0x27, 0xff, 0xfd, 0x93, 0x28, 0xff, 0xfd, 0x92, 0x28, 0xff, 0xfd, 0x91, 0x29, 0xff, 0xfd, 0x91, 0x2a, 0xff, 0xfd, 0x91, 0x2a, 0xff, 0xfd, 0x91, 0x2b, 0xff, 0xfd, 0x92, 0x2c, 0xff, 0xfd, 0x93, 0x2b, 0xff, 0xfd, 0x94, 0x2a, 0xff, 0xfd, 0x95, 0x2a, 0xff, 0xfd, 0x8f, 0x1d, 0xff, 0xfb, 0xb8, 0x6d, 0xff, 0xfc, 0xff, 0xff, 0xff, 0xfc, 0xfc, 0xf8, 0xff, 0xfb, 0xfc, 0xf8, 0xff, 0xfc, 0xff, 0xfd, 0xff, 0xfd, 0xf7, 0xeb, 0xff, 0xfb, 0x98, 0x36, 0xff, 0xfd, 0x8e, 0x1f, 0xff, 0xfd, 0x91, 0x24, 0xff, 0xfd, 0x8b, 0x17, 0xff, 0xfb, 0xb3, 0x66, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfc, 0xfd, 0xfa, 0xff, 0xfc, 0xfc, 0xf8, 0xff, 0xfc, 0xfc, 0xf9, 0xff, 0xfd, 0xfa, 0xf2, 0xff, 0xfb, 0xa1, 0x40, 0xff, 0xfd, 0x90, 0x19, 0xff, 0xfd, 0x94, 0x22, 0xff, 0xfd, 0x95, 0x23, 0xff, 0xfd, 0x95, 0x24, 0xff, 0xfd, 0x96, 0x25, 0xff, 0xfd, 0x8f, 0x17, 0xff, 0xfc, 0xc4, 0x81, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfc, 0xfd, 0xf8, 0xff, 0xfb, 0xfc, 0xf7, 0xff, 0xfb, 0xfd, 0xfa, 0xff, 0xfd, 0xf7, 0xeb, 0xff, 0xfd, 0xa3, 0x35, 0xff, 0xfd, 0x96, 0x1b, 0xff, 0xfd, 0x98, 0x22, 0xff, 0xfd, 0x98, 0x24, 0xff, 0xfd, 0x99, 0x25, 0xff, 0xfd, 0x99, 0x24, 0xff, 0xfd, 0x9a, 0x24, 0xff, 0xfd, 0x9a, 0x25, 0xff, 0xfd, 0x9a, 0x24, 0xff, 0xfd, 0x98, 0x1e, 0xff, 0xfc, 0xa3, 0x32, 0xff, 0xfd, 0xf2, 0xe2, 0xff, 0xfd, 0xf9, 0xf4, 0xff, 0xfb, 0xf8, 0xf2, 0xff, 0xfb, 0xf8, 0xf3, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xfa, 0xf8, 0xf2, 0xff, 0xfd, 0xfb, 0xf5, 0xff, 0xff, 0xff, 0xfc, 0xff, 0x7b, 0x43, 0x3e, 0xff, 0x4e, 0x01, 0x00, 0xff, 0x56, 0x0d, 0x01, 0xff, 0x59, 0x11, 0x04, 0xff, 0x5d, 0x16, 0x06, 0xff, 0x61, 0x19, 0x07, 0xff, 0x65, 0x1b, 0x07, 0xff, 0x67, 0x1d, 0x07, 0xff, 0x6a, 0x1f, 0x07, 0xff, 0x6d, 0x20, 0x06, 0xff, 0x6e, 0x21, 0x07, 0xff, 0x6c, 0x20, 0x07, 0xff, 0x6a, 0x1f, 0x08, 0xff, 0x67, 0x1c, 0x08, 0xff, 0x65, 0x1a, 0x08, 0xff, 0x63, 0x17, 0x07, 0xff, 0x60, 0x15, 0x07, 0xff, 0x5d, 0x13, 0x07, 0xff, 0x5a, 0x11, 0x07, 0xff, 0x5a, 0x11, 0x06, 0xff, 0x5a, 0x11, 0x06, 0xff, 0x5b, 0x10, 0x04, 0xff, 0x58, 0x0b, 0x02, 0xff, 0x7d, 0x1f, 0x0b, 0xff, 0xff, 0x68, 0x29, 0xff, 0xb3, 0x37, 0x17, 0xff, 0x65, 0x0a, 0x04, 0xff, 0xc7, 0x39, 0x1d, 0xff, 0xff, 0x4f, 0x39, 0xff, 0xed, 0x40, 0x3c, 0xff, 0xd8, 0x33, 0x38, 0xff, 0xd7, 0x30, 0x3f, 0xff, 0xf3, 0x3b, 0x5e, 0xff, 0xcd, 0x2e, 0x4d, 0xff, 0x58, 0x02, 0x01, 0xff, 0xad, 0x22, 0x2a, 0xff, 0xef, 0x3b, 0x5a, 0xff, 0x66, 0x0c, 0x08, 0xff, 0x5a, 0x08, 0x00, 0xff, 0x5f, 0x0a, 0x01, 0xff, + 0xfd, 0xfb, 0xf6, 0xff, 0xfc, 0xfa, 0xf5, 0xff, 0xfa, 0xf8, 0xf3, 0xff, 0xf9, 0xf6, 0xf1, 0xff, 0xf8, 0xf5, 0xef, 0xff, 0xf9, 0xf4, 0xee, 0xff, 0xfb, 0xf2, 0xec, 0xff, 0xfc, 0xef, 0xea, 0xff, 0xfd, 0xec, 0xe6, 0xff, 0xfd, 0xec, 0xe9, 0xff, 0xfc, 0xaf, 0x6d, 0xff, 0xfd, 0x8c, 0x22, 0xff, 0xfe, 0x91, 0x2a, 0xff, 0xfe, 0x90, 0x2a, 0xff, 0xfd, 0x8f, 0x2b, 0xff, 0xfe, 0x8f, 0x2c, 0xff, 0xfd, 0x8e, 0x2c, 0xff, 0xfd, 0x8e, 0x2c, 0xff, 0xfd, 0x8f, 0x2e, 0xff, 0xfd, 0x90, 0x2e, 0xff, 0xfd, 0x91, 0x2e, 0xff, 0xfd, 0x91, 0x2c, 0xff, 0xfd, 0x91, 0x29, 0xff, 0xfc, 0x8e, 0x21, 0xff, 0xfb, 0xe0, 0xbf, 0xff, 0xfb, 0xff, 0xff, 0xff, 0xfb, 0xfb, 0xf7, 0xff, 0xfb, 0xfc, 0xf9, 0xff, 0xfc, 0xff, 0xff, 0xff, 0xfb, 0xd7, 0xb4, 0xff, 0xfc, 0x86, 0x1d, 0xff, 0xfd, 0x8d, 0x26, 0xff, 0xfd, 0x8f, 0x27, 0xff, 0xfd, 0x8c, 0x22, 0xff, 0xfc, 0x90, 0x2e, 0xff, 0xfc, 0xef, 0xe1, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfc, 0xfc, 0xf8, 0xff, 0xfc, 0xfb, 0xf7, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfc, 0xc7, 0x8f, 0xff, 0xfc, 0x8a, 0x17, 0xff, 0xfd, 0x91, 0x25, 0xff, 0xfd, 0x92, 0x26, 0xff, 0xfe, 0x93, 0x27, 0xff, 0xfd, 0x93, 0x27, 0xff, 0xfd, 0x8c, 0x18, 0xff, 0xfc, 0xc1, 0x80, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfc, 0xfc, 0xf8, 0xff, 0xfc, 0xfb, 0xf7, 0xff, 0xfc, 0xfd, 0xfa, 0xff, 0xfd, 0xf6, 0xeb, 0xff, 0xfd, 0x9e, 0x37, 0xff, 0xfd, 0x92, 0x1d, 0xff, 0xfd, 0x95, 0x25, 0xff, 0xfd, 0x96, 0x27, 0xff, 0xfd, 0x96, 0x27, 0xff, 0xfd, 0x97, 0x27, 0xff, 0xfd, 0x97, 0x26, 0xff, 0xfd, 0x98, 0x27, 0xff, 0xfd, 0x99, 0x27, 0xff, 0xfd, 0x96, 0x21, 0xff, 0xfc, 0x9f, 0x33, 0xff, 0xfd, 0xf1, 0xe2, 0xff, 0xfc, 0xf9, 0xf4, 0xff, 0xfb, 0xf8, 0xf2, 0xff, 0xfb, 0xf8, 0xf3, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xfc, 0xf9, 0xf4, 0xff, 0xfe, 0xfc, 0xf7, 0xff, 0xff, 0xff, 0xfb, 0xff, 0x78, 0x3e, 0x39, 0xff, 0x4e, 0x01, 0x00, 0xff, 0x56, 0x0e, 0x01, 0xff, 0x5a, 0x13, 0x04, 0xff, 0x60, 0x18, 0x07, 0xff, 0x66, 0x1d, 0x08, 0xff, 0x6d, 0x22, 0x06, 0xff, 0x73, 0x25, 0x05, 0xff, 0x78, 0x28, 0x05, 0xff, 0x7b, 0x2a, 0x05, 0xff, 0x7c, 0x2a, 0x05, 0xff, 0x7a, 0x28, 0x05, 0xff, 0x75, 0x25, 0x06, 0xff, 0x6d, 0x21, 0x07, 0xff, 0x69, 0x1c, 0x08, 0xff, 0x65, 0x19, 0x08, 0xff, 0x61, 0x16, 0x08, 0xff, 0x5d, 0x14, 0x08, 0xff, 0x5b, 0x12, 0x08, 0xff, 0x5a, 0x12, 0x07, 0xff, 0x59, 0x10, 0x07, 0xff, 0x5a, 0x0f, 0x06, 0xff, 0x56, 0x0a, 0x03, 0xff, 0xca, 0x4a, 0x1b, 0xff, 0xec, 0x5a, 0x22, 0xff, 0x72, 0x0f, 0x09, 0xff, 0xbc, 0x37, 0x1d, 0xff, 0xff, 0x53, 0x38, 0xff, 0xf1, 0x3a, 0x40, 0xff, 0xe8, 0x33, 0x40, 0xff, 0xea, 0x32, 0x40, 0xff, 0xe0, 0x2e, 0x39, 0xff, 0xc4, 0x26, 0x2f, 0xff, 0xeb, 0x38, 0x5c, 0xff, 0xc5, 0x2b, 0x4c, 0xff, 0x52, 0x01, 0x00, 0xff, 0xea, 0x3a, 0x63, 0xff, 0xa5, 0x20, 0x34, 0xff, 0x55, 0x06, 0x00, 0xff, 0x5d, 0x09, 0x01, 0xff, + 0xfd, 0xfa, 0xf5, 0xff, 0xfc, 0xfa, 0xf4, 0xff, 0xfa, 0xf7, 0xf2, 0xff, 0xf9, 0xf6, 0xf1, 0xff, 0xf7, 0xf5, 0xf0, 0xff, 0xf8, 0xf3, 0xee, 0xff, 0xfa, 0xf1, 0xed, 0xff, 0xfc, 0xef, 0xea, 0xff, 0xfd, 0xec, 0xe6, 0xff, 0xfd, 0xec, 0xe9, 0xff, 0xfc, 0xac, 0x6d, 0xff, 0xfd, 0x89, 0x25, 0xff, 0xfd, 0x8e, 0x2f, 0xff, 0xfd, 0x8d, 0x2e, 0xff, 0xfd, 0x8c, 0x2d, 0xff, 0xfd, 0x8c, 0x2f, 0xff, 0xfd, 0x8b, 0x2f, 0xff, 0xfd, 0x8b, 0x2e, 0xff, 0xfd, 0x8b, 0x2f, 0xff, 0xfd, 0x8c, 0x2f, 0xff, 0xfd, 0x8d, 0x2f, 0xff, 0xfe, 0x8f, 0x2e, 0xff, 0xfe, 0x8b, 0x26, 0xff, 0xfb, 0xa3, 0x4f, 0xff, 0xfc, 0xfc, 0xf7, 0xff, 0xfd, 0xfd, 0xf9, 0xff, 0xfc, 0xfb, 0xf7, 0xff, 0xfc, 0xfc, 0xf9, 0xff, 0xfc, 0xff, 0xfd, 0xff, 0xfa, 0xa5, 0x5e, 0xff, 0xfd, 0x7f, 0x15, 0xff, 0xfd, 0x85, 0x20, 0xff, 0xfd, 0x86, 0x20, 0xff, 0xfd, 0x84, 0x20, 0xff, 0xfd, 0x7a, 0x11, 0xff, 0xfa, 0xc5, 0x99, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfc, 0xfc, 0xf8, 0xff, 0xfc, 0xfb, 0xf7, 0xff, 0xfd, 0xfe, 0xfc, 0xff, 0xfd, 0xec, 0xdc, 0xff, 0xfc, 0x91, 0x2c, 0xff, 0xfd, 0x8c, 0x23, 0xff, 0xfd, 0x8e, 0x27, 0xff, 0xfd, 0x8f, 0x29, 0xff, 0xfd, 0x8f, 0x2a, 0xff, 0xfd, 0x88, 0x1b, 0xff, 0xfc, 0xc0, 0x82, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfc, 0xfc, 0xf8, 0xff, 0xfc, 0xfb, 0xf7, 0xff, 0xfc, 0xfd, 0xfa, 0xff, 0xfc, 0xf5, 0xec, 0xff, 0xfc, 0x99, 0x39, 0xff, 0xfd, 0x8f, 0x20, 0xff, 0xfd, 0x93, 0x27, 0xff, 0xfd, 0x93, 0x29, 0xff, 0xfd, 0x93, 0x2a, 0xff, 0xfd, 0x94, 0x2a, 0xff, 0xfc, 0x94, 0x2a, 0xff, 0xfd, 0x95, 0x2a, 0xff, 0xfd, 0x96, 0x2a, 0xff, 0xfd, 0x94, 0x25, 0xff, 0xfc, 0x9c, 0x37, 0xff, 0xfd, 0xf1, 0xe3, 0xff, 0xfc, 0xf9, 0xf5, 0xff, 0xfb, 0xf8, 0xf2, 0xff, 0xfb, 0xf8, 0xf3, 0xff, 0xfb, 0xf9, 0xf4, 0xff, 0xfc, 0xfa, 0xf4, 0xff, 0xfe, 0xfd, 0xf8, 0xff, 0xfe, 0xfe, 0xfa, 0xff, 0x77, 0x3c, 0x37, 0xff, 0x50, 0x03, 0x00, 0xff, 0x5a, 0x10, 0x01, 0xff, 0x5e, 0x15, 0x04, 0xff, 0x64, 0x1b, 0x06, 0xff, 0x6d, 0x22, 0x07, 0xff, 0x77, 0x29, 0x05, 0xff, 0x80, 0x2e, 0x04, 0xff, 0x86, 0x32, 0x05, 0xff, 0x89, 0x32, 0x05, 0xff, 0x89, 0x31, 0x05, 0xff, 0x86, 0x30, 0x04, 0xff, 0x7f, 0x2c, 0x05, 0xff, 0x75, 0x26, 0x07, 0xff, 0x6c, 0x20, 0x07, 0xff, 0x67, 0x1b, 0x08, 0xff, 0x62, 0x18, 0x08, 0xff, 0x5e, 0x15, 0x08, 0xff, 0x5b, 0x13, 0x08, 0xff, 0x5a, 0x12, 0x08, 0xff, 0x5b, 0x11, 0x07, 0xff, 0x57, 0x0e, 0x05, 0xff, 0x69, 0x15, 0x08, 0xff, 0xf7, 0x67, 0x26, 0xff, 0xb9, 0x39, 0x18, 0xff, 0x8e, 0x1c, 0x14, 0xff, 0xfa, 0x5e, 0x38, 0xff, 0xfd, 0x47, 0x46, 0xff, 0xf5, 0x35, 0x4c, 0xff, 0xf7, 0x39, 0x4d, 0xff, 0xfb, 0x3a, 0x4e, 0xff, 0xfe, 0x39, 0x4f, 0xff, 0xf3, 0x33, 0x47, 0xff, 0xc8, 0x23, 0x38, 0xff, 0xf1, 0x39, 0x6b, 0xff, 0x6d, 0x0b, 0x16, 0xff, 0x9e, 0x1f, 0x3a, 0xff, 0xda, 0x34, 0x62, 0xff, 0x59, 0x07, 0x00, 0xff, 0x5a, 0x09, 0x00, 0xff, + 0xfd, 0xfb, 0xf6, 0xff, 0xfc, 0xfa, 0xf5, 0xff, 0xfa, 0xf7, 0xf2, 0xff, 0xf9, 0xf7, 0xf2, 0xff, 0xf8, 0xf5, 0xf0, 0xff, 0xf8, 0xf3, 0xee, 0xff, 0xfa, 0xf1, 0xec, 0xff, 0xfc, 0xef, 0xea, 0xff, 0xfd, 0xec, 0xe7, 0xff, 0xfd, 0xed, 0xea, 0xff, 0xfc, 0xab, 0x6f, 0xff, 0xfd, 0x85, 0x27, 0xff, 0xfd, 0x89, 0x32, 0xff, 0xfd, 0x89, 0x31, 0xff, 0xfd, 0x89, 0x31, 0xff, 0xfd, 0x89, 0x32, 0xff, 0xfd, 0x89, 0x32, 0xff, 0xfd, 0x89, 0x32, 0xff, 0xfd, 0x89, 0x31, 0xff, 0xfd, 0x89, 0x31, 0xff, 0xfd, 0x8a, 0x30, 0xff, 0xfd, 0x8b, 0x30, 0xff, 0xfd, 0x86, 0x23, 0xff, 0xfb, 0xcd, 0xa0, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfc, 0xfc, 0xf7, 0xff, 0xfc, 0xfb, 0xf7, 0xff, 0xfb, 0xfd, 0xfa, 0xff, 0xfb, 0xf6, 0xee, 0xff, 0xfa, 0xb3, 0x7a, 0xff, 0xfc, 0xae, 0x6f, 0xff, 0xfc, 0xb0, 0x74, 0xff, 0xfc, 0xb0, 0x74, 0xff, 0xfb, 0xaf, 0x73, 0xff, 0xfb, 0xaa, 0x6c, 0xff, 0xf9, 0xc5, 0x9d, 0xff, 0xfb, 0xfd, 0xfb, 0xff, 0xfb, 0xfb, 0xf7, 0xff, 0xfb, 0xfb, 0xf6, 0xff, 0xfc, 0xfb, 0xf6, 0xff, 0xfd, 0xff, 0xfe, 0xff, 0xfb, 0xad, 0x6b, 0xff, 0xfd, 0x84, 0x1e, 0xff, 0xfd, 0x8b, 0x2a, 0xff, 0xfd, 0x8d, 0x2b, 0xff, 0xfd, 0x8d, 0x2c, 0xff, 0xfd, 0x86, 0x1f, 0xff, 0xfc, 0xbf, 0x85, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfc, 0xfc, 0xf8, 0xff, 0xfc, 0xfc, 0xf8, 0xff, 0xfc, 0xfd, 0xfa, 0xff, 0xfd, 0xf5, 0xec, 0xff, 0xfc, 0x95, 0x3a, 0xff, 0xfd, 0x8b, 0x20, 0xff, 0xfd, 0x8f, 0x29, 0xff, 0xfd, 0x90, 0x2c, 0xff, 0xfd, 0x91, 0x2f, 0xff, 0xfd, 0x92, 0x2f, 0xff, 0xfd, 0x93, 0x2f, 0xff, 0xfd, 0x93, 0x2f, 0xff, 0xfd, 0x93, 0x2f, 0xff, 0xfd, 0x91, 0x29, 0xff, 0xfc, 0x9a, 0x3a, 0xff, 0xfd, 0xf0, 0xe4, 0xff, 0xfc, 0xf9, 0xf5, 0xff, 0xfa, 0xf8, 0xf2, 0xff, 0xfa, 0xf9, 0xf3, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xfb, 0xfb, 0xf5, 0xff, 0xfe, 0xfd, 0xf8, 0xff, 0xfe, 0xfe, 0xf9, 0xff, 0x74, 0x39, 0x33, 0xff, 0x52, 0x03, 0x00, 0xff, 0x5b, 0x11, 0x01, 0xff, 0x62, 0x18, 0x04, 0xff, 0x69, 0x1f, 0x05, 0xff, 0x74, 0x28, 0x05, 0xff, 0x82, 0x30, 0x03, 0xff, 0x8d, 0x33, 0x03, 0xff, 0x9c, 0x41, 0x04, 0xff, 0xae, 0x57, 0x02, 0xff, 0xb1, 0x5f, 0x02, 0xff, 0x97, 0x3f, 0x03, 0xff, 0x87, 0x32, 0x04, 0xff, 0x7c, 0x2b, 0x06, 0xff, 0x6f, 0x23, 0x08, 0xff, 0x67, 0x1d, 0x08, 0xff, 0x62, 0x18, 0x07, 0xff, 0x5e, 0x15, 0x08, 0xff, 0x5c, 0x13, 0x08, 0xff, 0x5a, 0x12, 0x08, 0xff, 0x5a, 0x11, 0x07, 0xff, 0x55, 0x0c, 0x04, 0xff, 0x7f, 0x22, 0x0d, 0xff, 0xff, 0x73, 0x2c, 0xff, 0xa7, 0x28, 0x1a, 0xff, 0xb6, 0x33, 0x23, 0xff, 0xff, 0x6a, 0x41, 0xff, 0xfc, 0x3f, 0x53, 0xff, 0xfa, 0x38, 0x55, 0xff, 0xfc, 0x3b, 0x55, 0xff, 0xfd, 0x3c, 0x55, 0xff, 0xfd, 0x3c, 0x54, 0xff, 0xff, 0x3b, 0x55, 0xff, 0xe0, 0x2a, 0x45, 0xff, 0xe3, 0x2f, 0x5e, 0xff, 0xa5, 0x21, 0x43, 0xff, 0x6c, 0x0e, 0x1a, 0xff, 0xec, 0x40, 0x7c, 0xff, 0x64, 0x0a, 0x08, 0xff, 0x57, 0x07, 0x00, 0xff, + 0xfd, 0xfb, 0xf6, 0xff, 0xfc, 0xfa, 0xf5, 0xff, 0xfb, 0xf8, 0xf3, 0xff, 0xf9, 0xf7, 0xf2, 0xff, 0xf9, 0xf5, 0xf0, 0xff, 0xf9, 0xf3, 0xee, 0xff, 0xfb, 0xf1, 0xed, 0xff, 0xfd, 0xf0, 0xea, 0xff, 0xfe, 0xec, 0xe6, 0xff, 0xfd, 0xec, 0xe9, 0xff, 0xfc, 0xa9, 0x71, 0xff, 0xfd, 0x81, 0x2a, 0xff, 0xfd, 0x86, 0x33, 0xff, 0xfd, 0x86, 0x33, 0xff, 0xfd, 0x87, 0x34, 0xff, 0xfd, 0x88, 0x35, 0xff, 0xfd, 0x89, 0x34, 0xff, 0xfd, 0x88, 0x34, 0xff, 0xfd, 0x88, 0x34, 0xff, 0xfd, 0x88, 0x33, 0xff, 0xfd, 0x87, 0x32, 0xff, 0xfd, 0x84, 0x2e, 0xff, 0xfc, 0x8d, 0x38, 0xff, 0xfc, 0xf1, 0xe3, 0xff, 0xfc, 0xfe, 0xfb, 0xff, 0xfc, 0xfb, 0xf7, 0xff, 0xfb, 0xfb, 0xf6, 0xff, 0xfb, 0xfb, 0xf7, 0xff, 0xfb, 0xfc, 0xf8, 0xff, 0xfc, 0xff, 0xff, 0xff, 0xfb, 0xff, 0xff, 0xff, 0xfb, 0xff, 0xff, 0xff, 0xfb, 0xff, 0xff, 0xff, 0xfa, 0xff, 0xff, 0xff, 0xf9, 0xff, 0xff, 0xff, 0xf9, 0xfd, 0xfd, 0xff, 0xf9, 0xfa, 0xf6, 0xff, 0xfa, 0xfa, 0xf6, 0xff, 0xfb, 0xfb, 0xf6, 0xff, 0xfb, 0xfb, 0xf6, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfb, 0xd9, 0xbd, 0xff, 0xfd, 0x82, 0x25, 0xff, 0xfd, 0x88, 0x2b, 0xff, 0xfd, 0x8a, 0x2e, 0xff, 0xfd, 0x8a, 0x30, 0xff, 0xfc, 0x84, 0x24, 0xff, 0xfc, 0xbe, 0x87, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfd, 0xfd, 0xf8, 0xff, 0xfc, 0xfc, 0xf8, 0xff, 0xfc, 0xfe, 0xfb, 0xff, 0xfd, 0xf5, 0xed, 0xff, 0xfc, 0x93, 0x3c, 0xff, 0xfd, 0x87, 0x23, 0xff, 0xfd, 0x8c, 0x2d, 0xff, 0xfd, 0x8d, 0x30, 0xff, 0xfd, 0x8f, 0x32, 0xff, 0xfd, 0x8f, 0x32, 0xff, 0xfd, 0x90, 0x32, 0xff, 0xfd, 0x91, 0x32, 0xff, 0xfd, 0x91, 0x32, 0xff, 0xfd, 0x8f, 0x2c, 0xff, 0xfc, 0x98, 0x3b, 0xff, 0xfc, 0xf0, 0xe4, 0xff, 0xfc, 0xf9, 0xf5, 0xff, 0xfa, 0xf8, 0xf2, 0xff, 0xfa, 0xf9, 0xf3, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xfb, 0xfb, 0xf5, 0xff, 0xfc, 0xfc, 0xf7, 0xff, 0xff, 0xff, 0xfe, 0xff, 0x80, 0x4b, 0x45, 0xff, 0x50, 0x01, 0x00, 0xff, 0x5e, 0x12, 0x01, 0xff, 0x64, 0x18, 0x03, 0xff, 0x6d, 0x20, 0x04, 0xff, 0x7a, 0x2b, 0x04, 0xff, 0x8d, 0x36, 0x02, 0xff, 0xc8, 0x83, 0x02, 0xff, 0xf0, 0xc4, 0x01, 0xff, 0xfa, 0xdb, 0x01, 0xff, 0xff, 0xe6, 0x01, 0xff, 0xdb, 0xa8, 0x02, 0xff, 0x8f, 0x39, 0x05, 0xff, 0x80, 0x2e, 0x05, 0xff, 0x72, 0x25, 0x07, 0xff, 0x68, 0x1d, 0x08, 0xff, 0x62, 0x18, 0x07, 0xff, 0x5e, 0x16, 0x07, 0xff, 0x5c, 0x13, 0x07, 0xff, 0x5b, 0x12, 0x08, 0xff, 0x5a, 0x11, 0x08, 0xff, 0x54, 0x0a, 0x04, 0xff, 0x8c, 0x2a, 0x10, 0xff, 0xff, 0x75, 0x2e, 0xff, 0xa8, 0x25, 0x20, 0xff, 0xc6, 0x40, 0x2b, 0xff, 0xff, 0x74, 0x45, 0xff, 0xfd, 0x3d, 0x5f, 0xff, 0xfd, 0x3b, 0x5f, 0xff, 0xfd, 0x3d, 0x5e, 0xff, 0xfd, 0x3d, 0x5e, 0xff, 0xfd, 0x3d, 0x5e, 0xff, 0xff, 0x3c, 0x5d, 0xff, 0xee, 0x30, 0x51, 0xff, 0xdf, 0x2c, 0x5d, 0xff, 0xbb, 0x2d, 0x5d, 0xff, 0x61, 0x09, 0x12, 0xff, 0xeb, 0x44, 0x81, 0xff, 0x66, 0x0c, 0x0d, 0xff, 0x53, 0x06, 0x00, 0xff, + 0xfd, 0xfa, 0xf6, 0xff, 0xfc, 0xfa, 0xf5, 0xff, 0xfa, 0xf9, 0xf3, 0xff, 0xf9, 0xf7, 0xf2, 0xff, 0xf9, 0xf5, 0xef, 0xff, 0xf9, 0xf3, 0xee, 0xff, 0xfa, 0xf1, 0xec, 0xff, 0xfd, 0xef, 0xe8, 0xff, 0xfe, 0xeb, 0xe5, 0xff, 0xfd, 0xec, 0xe9, 0xff, 0xfc, 0xa6, 0x74, 0xff, 0xfc, 0x7d, 0x2c, 0xff, 0xfd, 0x82, 0x35, 0xff, 0xfd, 0x83, 0x37, 0xff, 0xfd, 0x84, 0x38, 0xff, 0xfd, 0x85, 0x39, 0xff, 0xfd, 0x86, 0x3a, 0xff, 0xfd, 0x86, 0x3a, 0xff, 0xfd, 0x85, 0x38, 0xff, 0xfc, 0x85, 0x36, 0xff, 0xfd, 0x83, 0x35, 0xff, 0xfd, 0x7c, 0x27, 0xff, 0xfb, 0xaf, 0x78, 0xff, 0xfc, 0xff, 0xff, 0xff, 0xfb, 0xfc, 0xf8, 0xff, 0xfb, 0xfb, 0xf6, 0xff, 0xfa, 0xfa, 0xf6, 0xff, 0xfb, 0xfb, 0xf6, 0xff, 0xfc, 0xfc, 0xf8, 0xff, 0xfb, 0xfc, 0xf8, 0xff, 0xfb, 0xfd, 0xf9, 0xff, 0xfb, 0xfd, 0xf9, 0xff, 0xfb, 0xfc, 0xf9, 0xff, 0xfb, 0xfc, 0xf8, 0xff, 0xfb, 0xfc, 0xf8, 0xff, 0xfb, 0xfb, 0xf7, 0xff, 0xfa, 0xfa, 0xf7, 0xff, 0xfb, 0xfb, 0xf7, 0xff, 0xfb, 0xfa, 0xf6, 0xff, 0xfb, 0xfb, 0xf6, 0xff, 0xfc, 0xfc, 0xf9, 0xff, 0xfd, 0xf9, 0xf4, 0xff, 0xfb, 0x96, 0x4f, 0xff, 0xfd, 0x81, 0x28, 0xff, 0xfd, 0x86, 0x31, 0xff, 0xfd, 0x87, 0x33, 0xff, 0xfd, 0x81, 0x27, 0xff, 0xfc, 0xbc, 0x89, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfd, 0xfd, 0xf8, 0xff, 0xfc, 0xfc, 0xf8, 0xff, 0xfc, 0xfe, 0xfc, 0xff, 0xfd, 0xf5, 0xed, 0xff, 0xfd, 0x91, 0x3f, 0xff, 0xfd, 0x85, 0x27, 0xff, 0xfd, 0x89, 0x30, 0xff, 0xfd, 0x8b, 0x34, 0xff, 0xfd, 0x8c, 0x35, 0xff, 0xfd, 0x8d, 0x35, 0xff, 0xfd, 0x8d, 0x35, 0xff, 0xfd, 0x8e, 0x35, 0xff, 0xfd, 0x8e, 0x33, 0xff, 0xfd, 0x8c, 0x2d, 0xff, 0xfc, 0x95, 0x3d, 0xff, 0xfc, 0xef, 0xe4, 0xff, 0xfc, 0xfa, 0xf6, 0xff, 0xfa, 0xf8, 0xf2, 0xff, 0xfa, 0xf9, 0xf3, 0xff, 0xfb, 0xf9, 0xf4, 0xff, 0xfb, 0xfa, 0xf4, 0xff, 0xfc, 0xfb, 0xf5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xd0, 0xbd, 0xb8, 0xff, 0x56, 0x0d, 0x02, 0xff, 0x5d, 0x10, 0x00, 0xff, 0x65, 0x18, 0x03, 0xff, 0x6e, 0x21, 0x04, 0xff, 0x7a, 0x28, 0x03, 0xff, 0xab, 0x5e, 0x02, 0xff, 0xff, 0xe3, 0x01, 0xff, 0xff, 0xe3, 0x00, 0xff, 0xfe, 0xe1, 0x00, 0xff, 0xff, 0xe6, 0x00, 0xff, 0xf4, 0xd1, 0x01, 0xff, 0x97, 0x43, 0x04, 0xff, 0x80, 0x2d, 0x06, 0xff, 0x73, 0x25, 0x07, 0xff, 0x68, 0x1d, 0x07, 0xff, 0x62, 0x19, 0x08, 0xff, 0x5e, 0x16, 0x07, 0xff, 0x5e, 0x14, 0x07, 0xff, 0x5c, 0x12, 0x08, 0xff, 0x5a, 0x11, 0x08, 0xff, 0x54, 0x0b, 0x04, 0xff, 0x84, 0x26, 0x0e, 0xff, 0xff, 0x76, 0x2d, 0xff, 0xaf, 0x29, 0x25, 0xff, 0xc2, 0x39, 0x2f, 0xff, 0xff, 0x84, 0x46, 0xff, 0xfd, 0x42, 0x6a, 0xff, 0xfd, 0x3e, 0x6b, 0xff, 0xfd, 0x3e, 0x69, 0xff, 0xfd, 0x3e, 0x69, 0xff, 0xfd, 0x3e, 0x69, 0xff, 0xff, 0x3c, 0x68, 0xff, 0xef, 0x2f, 0x55, 0xff, 0xea, 0x32, 0x6b, 0xff, 0xac, 0x29, 0x56, 0xff, 0x66, 0x0d, 0x19, 0xff, 0xe6, 0x41, 0x81, 0xff, 0x5d, 0x09, 0x07, 0xff, 0x52, 0x03, 0x00, 0xff, + 0xfd, 0xfa, 0xf5, 0xff, 0xfc, 0xf9, 0xf5, 0xff, 0xfb, 0xf9, 0xf4, 0xff, 0xf9, 0xf7, 0xf2, 0xff, 0xf9, 0xf4, 0xef, 0xff, 0xf9, 0xf3, 0xee, 0xff, 0xf9, 0xf0, 0xeb, 0xff, 0xfb, 0xed, 0xe6, 0xff, 0xfd, 0xeb, 0xe4, 0xff, 0xfd, 0xeb, 0xe8, 0xff, 0xfc, 0xa5, 0x76, 0xff, 0xfd, 0x78, 0x2f, 0xff, 0xfd, 0x7e, 0x38, 0xff, 0xfd, 0x80, 0x3a, 0xff, 0xfc, 0x81, 0x3c, 0xff, 0xfd, 0x82, 0x3d, 0xff, 0xfd, 0x82, 0x3e, 0xff, 0xfd, 0x82, 0x3e, 0xff, 0xfc, 0x82, 0x3c, 0xff, 0xfd, 0x82, 0x3a, 0xff, 0xfd, 0x7e, 0x36, 0xff, 0xfc, 0x7c, 0x30, 0xff, 0xfc, 0xdf, 0xc9, 0xff, 0xfc, 0xff, 0xff, 0xff, 0xfc, 0xfb, 0xf7, 0xff, 0xfb, 0xfb, 0xf6, 0xff, 0xfb, 0xfb, 0xf6, 0xff, 0xfb, 0xfe, 0xfb, 0xff, 0xfd, 0xff, 0xfe, 0xff, 0xfc, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfe, 0xff, 0xfc, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xfe, 0xff, 0xfc, 0xff, 0xfd, 0xff, 0xfc, 0xfd, 0xfa, 0xff, 0xfb, 0xfb, 0xf7, 0xff, 0xfb, 0xfb, 0xf7, 0xff, 0xfc, 0xfb, 0xf7, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfb, 0xc5, 0x9f, 0xff, 0xfd, 0x7b, 0x26, 0xff, 0xfd, 0x83, 0x33, 0xff, 0xfc, 0x84, 0x34, 0xff, 0xfd, 0x7d, 0x29, 0xff, 0xfc, 0xb9, 0x8b, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfd, 0xfd, 0xf9, 0xff, 0xfb, 0xfc, 0xf8, 0xff, 0xfc, 0xfe, 0xfb, 0xff, 0xfd, 0xf5, 0xed, 0xff, 0xfd, 0x8f, 0x43, 0xff, 0xfd, 0x82, 0x2b, 0xff, 0xfd, 0x87, 0x33, 0xff, 0xfd, 0x88, 0x35, 0xff, 0xfd, 0x89, 0x37, 0xff, 0xfd, 0x8a, 0x38, 0xff, 0xfd, 0x8a, 0x39, 0xff, 0xfd, 0x8b, 0x38, 0xff, 0xfd, 0x8a, 0x37, 0xff, 0xfe, 0x88, 0x32, 0xff, 0xfc, 0x93, 0x40, 0xff, 0xfc, 0xef, 0xe4, 0xff, 0xfb, 0xfa, 0xf6, 0xff, 0xfa, 0xf8, 0xf2, 0xff, 0xfa, 0xf9, 0xf3, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xfb, 0xf9, 0xf4, 0xff, 0xfe, 0xfc, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0x94, 0x67, 0x5f, 0xff, 0x57, 0x07, 0x00, 0xff, 0x64, 0x17, 0x02, 0xff, 0x6f, 0x20, 0x02, 0xff, 0x7a, 0x27, 0x03, 0xff, 0xa2, 0x51, 0x02, 0xff, 0xfa, 0xd3, 0x01, 0xff, 0xff, 0xe2, 0x00, 0xff, 0xfe, 0xdb, 0x01, 0xff, 0xf8, 0xd1, 0x01, 0xff, 0xc6, 0x86, 0x03, 0xff, 0x8c, 0x35, 0x05, 0xff, 0x7f, 0x2c, 0x06, 0xff, 0x72, 0x23, 0x07, 0xff, 0x68, 0x1d, 0x08, 0xff, 0x63, 0x18, 0x07, 0xff, 0x60, 0x15, 0x08, 0xff, 0x5f, 0x14, 0x06, 0xff, 0x5c, 0x12, 0x07, 0xff, 0x59, 0x11, 0x07, 0xff, 0x56, 0x0c, 0x03, 0xff, 0x70, 0x17, 0x07, 0xff, 0xfa, 0x72, 0x29, 0xff, 0xc2, 0x38, 0x29, 0xff, 0xac, 0x1c, 0x2e, 0xff, 0xfb, 0x89, 0x46, 0xff, 0xfe, 0x5d, 0x69, 0xff, 0xfd, 0x3d, 0x76, 0xff, 0xfd, 0x41, 0x75, 0xff, 0xfd, 0x40, 0x74, 0xff, 0xfd, 0x40, 0x73, 0xff, 0xfe, 0x3d, 0x6f, 0xff, 0xeb, 0x29, 0x58, 0xff, 0xf5, 0x3b, 0x89, 0xff, 0x73, 0x12, 0x29, 0xff, 0x8e, 0x21, 0x3d, 0xff, 0xd2, 0x3d, 0x76, 0xff, 0x4a, 0x00, 0x00, 0xff, 0x6e, 0x1f, 0x03, 0xff, + 0xfd, 0xf9, 0xf5, 0xff, 0xfc, 0xf9, 0xf4, 0xff, 0xfb, 0xf8, 0xf3, 0xff, 0xfa, 0xf7, 0xf2, 0xff, 0xf9, 0xf5, 0xf0, 0xff, 0xf9, 0xf4, 0xee, 0xff, 0xfa, 0xf1, 0xea, 0xff, 0xfb, 0xec, 0xe6, 0xff, 0xfd, 0xea, 0xe4, 0xff, 0xfd, 0xeb, 0xe7, 0xff, 0xfc, 0xa2, 0x79, 0xff, 0xfd, 0x75, 0x33, 0xff, 0xfd, 0x7b, 0x3b, 0xff, 0xfd, 0x7b, 0x3c, 0xff, 0xfd, 0x7d, 0x3e, 0xff, 0xfd, 0x7f, 0x41, 0xff, 0xfd, 0x7f, 0x41, 0xff, 0xfd, 0x7f, 0x41, 0xff, 0xfd, 0x7f, 0x40, 0xff, 0xfd, 0x7d, 0x3e, 0xff, 0xfd, 0x76, 0x33, 0xff, 0xfb, 0x97, 0x60, 0xff, 0xfc, 0xfe, 0xfa, 0xff, 0xfc, 0xfe, 0xfa, 0xff, 0xfc, 0xfc, 0xf8, 0xff, 0xfc, 0xfb, 0xf7, 0xff, 0xfb, 0xfd, 0xfa, 0xff, 0xfb, 0xf0, 0xe7, 0xff, 0xfc, 0xe8, 0xdc, 0xff, 0xfc, 0xe8, 0xdd, 0xff, 0xfc, 0xe8, 0xde, 0xff, 0xfc, 0xe8, 0xdf, 0xff, 0xfc, 0xe9, 0xe0, 0xff, 0xfc, 0xe9, 0xdf, 0xff, 0xfc, 0xe9, 0xdf, 0xff, 0xfc, 0xea, 0xdf, 0xff, 0xfc, 0xe9, 0xdd, 0xff, 0xfc, 0xf7, 0xf1, 0xff, 0xfc, 0xfe, 0xfb, 0xff, 0xfc, 0xfc, 0xf8, 0xff, 0xfc, 0xfb, 0xf7, 0xff, 0xfc, 0xfe, 0xfb, 0xff, 0xfc, 0xf0, 0xe4, 0xff, 0xfc, 0x86, 0x40, 0xff, 0xfd, 0x7e, 0x32, 0xff, 0xfd, 0x80, 0x36, 0xff, 0xfd, 0x79, 0x2b, 0xff, 0xfc, 0xb8, 0x8e, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfc, 0xfd, 0xf9, 0xff, 0xfc, 0xfc, 0xf8, 0xff, 0xfc, 0xfe, 0xfb, 0xff, 0xfd, 0xf4, 0xed, 0xff, 0xfc, 0x8b, 0x46, 0xff, 0xfd, 0x7d, 0x2f, 0xff, 0xfd, 0x82, 0x36, 0xff, 0xfd, 0x84, 0x38, 0xff, 0xfd, 0x84, 0x39, 0xff, 0xfd, 0x85, 0x3b, 0xff, 0xfd, 0x86, 0x3c, 0xff, 0xfd, 0x86, 0x3d, 0xff, 0xfd, 0x86, 0x3c, 0xff, 0xfd, 0x84, 0x36, 0xff, 0xfc, 0x8f, 0x44, 0xff, 0xfc, 0xf0, 0xe4, 0xff, 0xfc, 0xf9, 0xf5, 0xff, 0xfa, 0xf8, 0xf2, 0xff, 0xf9, 0xf9, 0xf2, 0xff, 0xfa, 0xf8, 0xf3, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xfb, 0xf9, 0xf4, 0xff, 0xfd, 0xfa, 0xf5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xcb, 0xb4, 0xae, 0xff, 0x57, 0x08, 0x00, 0xff, 0x62, 0x16, 0x02, 0xff, 0x6e, 0x1f, 0x03, 0xff, 0x79, 0x29, 0x04, 0xff, 0x87, 0x30, 0x04, 0xff, 0xac, 0x5a, 0x03, 0xff, 0xc2, 0x78, 0x03, 0xff, 0xb6, 0x64, 0x03, 0xff, 0xa5, 0x4e, 0x03, 0xff, 0x90, 0x39, 0x04, 0xff, 0x85, 0x31, 0x05, 0xff, 0x7a, 0x28, 0x06, 0xff, 0x6e, 0x20, 0x07, 0xff, 0x66, 0x1a, 0x08, 0xff, 0x62, 0x17, 0x08, 0xff, 0x61, 0x15, 0x07, 0xff, 0x5f, 0x13, 0x06, 0xff, 0x5d, 0x11, 0x05, 0xff, 0x5a, 0x10, 0x04, 0xff, 0x59, 0x0d, 0x02, 0xff, 0x57, 0x08, 0x00, 0xff, 0xd6, 0x5b, 0x20, 0xff, 0xea, 0x5d, 0x2a, 0xff, 0x9a, 0x0b, 0x27, 0xff, 0xd8, 0x4f, 0x41, 0xff, 0xff, 0x88, 0x64, 0xff, 0xfd, 0x4e, 0x79, 0xff, 0xfd, 0x43, 0x7c, 0xff, 0xfd, 0x43, 0x7b, 0xff, 0xfc, 0x40, 0x76, 0xff, 0xf4, 0x34, 0x6c, 0xff, 0xfb, 0x38, 0x86, 0xff, 0xe8, 0x46, 0x71, 0xff, 0x54, 0x0b, 0x00, 0xff, 0xd8, 0x45, 0x84, 0xff, 0xa1, 0x29, 0x4c, 0xff, 0x42, 0x00, 0x00, 0xff, 0x9c, 0x52, 0x03, 0xff, + 0xfd, 0xfa, 0xf5, 0xff, 0xfc, 0xf9, 0xf4, 0xff, 0xfa, 0xf8, 0xf2, 0xff, 0xf9, 0xf7, 0xf2, 0xff, 0xf9, 0xf5, 0xf0, 0xff, 0xfb, 0xf4, 0xee, 0xff, 0xfb, 0xf2, 0xeb, 0xff, 0xfa, 0xed, 0xe7, 0xff, 0xfc, 0xea, 0xe4, 0xff, 0xfd, 0xeb, 0xe7, 0xff, 0xfb, 0x9f, 0x7a, 0xff, 0xfd, 0x72, 0x37, 0xff, 0xfd, 0x78, 0x3f, 0xff, 0xfd, 0x78, 0x40, 0xff, 0xfd, 0x7a, 0x42, 0xff, 0xfd, 0x7c, 0x44, 0xff, 0xfd, 0x7b, 0x44, 0xff, 0xfd, 0x7b, 0x44, 0xff, 0xfd, 0x7a, 0x43, 0xff, 0xfd, 0x78, 0x41, 0xff, 0xfc, 0x6f, 0x33, 0xff, 0xfc, 0xc6, 0xab, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfd, 0xfd, 0xf8, 0xff, 0xfd, 0xfc, 0xf8, 0xff, 0xfd, 0xfd, 0xf9, 0xff, 0xfc, 0xf9, 0xf5, 0xff, 0xf9, 0x8e, 0x65, 0xff, 0xfc, 0x72, 0x40, 0xff, 0xfc, 0x75, 0x44, 0xff, 0xfc, 0x75, 0x44, 0xff, 0xfc, 0x75, 0x43, 0xff, 0xfc, 0x75, 0x43, 0xff, 0xfc, 0x75, 0x42, 0xff, 0xfc, 0x76, 0x41, 0xff, 0xfc, 0x77, 0x40, 0xff, 0xfc, 0x6f, 0x35, 0xff, 0xfa, 0xaf, 0x8d, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfc, 0xfc, 0xf8, 0xff, 0xfc, 0xfb, 0xf7, 0xff, 0xfc, 0xfc, 0xf7, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfb, 0xaa, 0x7f, 0xff, 0xfd, 0x76, 0x2f, 0xff, 0xfd, 0x7d, 0x3a, 0xff, 0xfd, 0x76, 0x2f, 0xff, 0xfc, 0xb6, 0x90, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfc, 0xfc, 0xf9, 0xff, 0xfb, 0xfc, 0xf8, 0xff, 0xfb, 0xfe, 0xfc, 0xff, 0xfd, 0xf4, 0xed, 0xff, 0xfc, 0x89, 0x48, 0xff, 0xfd, 0x7a, 0x32, 0xff, 0xfd, 0x7e, 0x3a, 0xff, 0xfd, 0x80, 0x3c, 0xff, 0xfd, 0x81, 0x3d, 0xff, 0xfd, 0x81, 0x3d, 0xff, 0xfd, 0x82, 0x3f, 0xff, 0xfd, 0x83, 0x40, 0xff, 0xfd, 0x83, 0x3f, 0xff, 0xfd, 0x80, 0x3a, 0xff, 0xfc, 0x8a, 0x47, 0xff, 0xfd, 0xee, 0xe4, 0xff, 0xfb, 0xf8, 0xf5, 0xff, 0xfa, 0xf7, 0xf2, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf9, 0xf9, 0xf4, 0xff, 0xfc, 0xfa, 0xf5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3, 0xac, 0xa7, 0xff, 0x52, 0x06, 0x00, 0xff, 0x61, 0x14, 0x02, 0xff, 0x6b, 0x1d, 0x05, 0xff, 0x74, 0x25, 0x06, 0xff, 0x7e, 0x2c, 0x05, 0xff, 0x89, 0x30, 0x04, 0xff, 0x8f, 0x33, 0x04, 0xff, 0x91, 0x36, 0x04, 0xff, 0x8e, 0x36, 0x04, 0xff, 0x86, 0x32, 0x05, 0xff, 0x7d, 0x2a, 0x05, 0xff, 0x74, 0x22, 0x06, 0xff, 0x6a, 0x1c, 0x07, 0xff, 0x65, 0x18, 0x07, 0xff, 0x62, 0x15, 0x07, 0xff, 0x5f, 0x13, 0x06, 0xff, 0x5e, 0x11, 0x06, 0xff, 0x5c, 0x0f, 0x05, 0xff, 0x5b, 0x0e, 0x03, 0xff, 0x59, 0x0d, 0x02, 0xff, 0x4c, 0x04, 0x00, 0xff, 0x87, 0x28, 0x0e, 0xff, 0xff, 0x77, 0x2b, 0xff, 0xbd, 0x2f, 0x27, 0xff, 0x9e, 0x0a, 0x28, 0xff, 0xe3, 0x53, 0x52, 0xff, 0xff, 0x75, 0x7c, 0xff, 0xff, 0x5a, 0x90, 0xff, 0xff, 0x4a, 0x8e, 0xff, 0xfe, 0x43, 0x8d, 0xff, 0xff, 0x46, 0x9b, 0xff, 0xdd, 0x36, 0x85, 0xff, 0xcf, 0x7e, 0x0e, 0xff, 0xdf, 0x78, 0x41, 0xff, 0xe6, 0x46, 0x98, 0xff, 0x5f, 0x0a, 0x0b, 0xff, 0x4c, 0x00, 0x00, 0xff, 0xcf, 0x8f, 0x03, 0xff, + 0xfd, 0xfa, 0xf5, 0xff, 0xfc, 0xf9, 0xf4, 0xff, 0xfa, 0xf7, 0xf2, 0xff, 0xf9, 0xf6, 0xf1, 0xff, 0xf8, 0xf5, 0xef, 0xff, 0xf9, 0xf3, 0xee, 0xff, 0xfb, 0xf1, 0xeb, 0xff, 0xfb, 0xed, 0xe8, 0xff, 0xfd, 0xea, 0xe5, 0xff, 0xfd, 0xeb, 0xe8, 0xff, 0xfb, 0x9e, 0x7e, 0xff, 0xfd, 0x71, 0x3b, 0xff, 0xfd, 0x76, 0x42, 0xff, 0xfd, 0x76, 0x43, 0xff, 0xfd, 0x77, 0x46, 0xff, 0xfd, 0x79, 0x47, 0xff, 0xfc, 0x79, 0x48, 0xff, 0xfd, 0x78, 0x48, 0xff, 0xfd, 0x76, 0x46, 0xff, 0xfd, 0x72, 0x40, 0xff, 0xfc, 0x7e, 0x4d, 0xff, 0xfd, 0xf0, 0xe7, 0xff, 0xfd, 0xff, 0xfb, 0xff, 0xfd, 0xfd, 0xf8, 0xff, 0xfd, 0xfd, 0xf9, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfb, 0xd9, 0xcb, 0xff, 0xfc, 0x6c, 0x3e, 0xff, 0xfd, 0x6f, 0x40, 0xff, 0xfd, 0x6f, 0x41, 0xff, 0xfd, 0x6f, 0x42, 0xff, 0xfd, 0x70, 0x42, 0xff, 0xfd, 0x70, 0x42, 0xff, 0xfd, 0x71, 0x41, 0xff, 0xfd, 0x72, 0x40, 0xff, 0xfd, 0x72, 0x3f, 0xff, 0xfd, 0x6d, 0x38, 0xff, 0xfc, 0x7e, 0x4e, 0xff, 0xfd, 0xf5, 0xef, 0xff, 0xfe, 0xff, 0xfc, 0xff, 0xfd, 0xfc, 0xf7, 0xff, 0xfd, 0xfc, 0xf7, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfc, 0xdb, 0xca, 0xff, 0xfc, 0x76, 0x38, 0xff, 0xfd, 0x7a, 0x3b, 0xff, 0xfd, 0x73, 0x32, 0xff, 0xfc, 0xb4, 0x8f, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfd, 0xfd, 0xf8, 0xff, 0xfc, 0xfd, 0xf9, 0xff, 0xfd, 0xfe, 0xfc, 0xff, 0xfd, 0xf4, 0xed, 0xff, 0xfc, 0x85, 0x4b, 0xff, 0xfd, 0x78, 0x35, 0xff, 0xfd, 0x7b, 0x3d, 0xff, 0xfd, 0x7c, 0x3e, 0xff, 0xfd, 0x7e, 0x40, 0xff, 0xfd, 0x7e, 0x41, 0xff, 0xfc, 0x7f, 0x42, 0xff, 0xfc, 0x7f, 0x43, 0xff, 0xfd, 0x80, 0x43, 0xff, 0xfd, 0x7d, 0x3e, 0xff, 0xfc, 0x88, 0x4b, 0xff, 0xfc, 0xee, 0xe4, 0xff, 0xfb, 0xf8, 0xf4, 0xff, 0xf9, 0xf7, 0xf2, 0xff, 0xfa, 0xf7, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xfa, 0xf9, 0xf3, 0xff, 0xfc, 0xfa, 0xf5, 0xff, 0xff, 0xff, 0xff, 0xff, 0x98, 0x6f, 0x69, 0xff, 0x4e, 0x02, 0x00, 0xff, 0x5e, 0x13, 0x03, 0xff, 0x66, 0x19, 0x05, 0xff, 0x6e, 0x1f, 0x06, 0xff, 0x75, 0x25, 0x07, 0xff, 0x7d, 0x2b, 0x07, 0xff, 0x81, 0x2e, 0x07, 0xff, 0x83, 0x2f, 0x07, 0xff, 0x80, 0x2d, 0x07, 0xff, 0x7b, 0x2a, 0x06, 0xff, 0x74, 0x24, 0x05, 0xff, 0x6c, 0x1e, 0x07, 0xff, 0x66, 0x1a, 0x07, 0xff, 0x64, 0x17, 0x06, 0xff, 0x61, 0x14, 0x06, 0xff, 0x5f, 0x12, 0x06, 0xff, 0x5c, 0x10, 0x05, 0xff, 0x59, 0x0e, 0x03, 0xff, 0x59, 0x0c, 0x03, 0xff, 0x52, 0x07, 0x00, 0xff, 0x66, 0x13, 0x03, 0xff, 0x53, 0x07, 0x01, 0xff, 0xbf, 0x48, 0x1b, 0xff, 0xff, 0x6b, 0x30, 0xff, 0xb3, 0x23, 0x28, 0xff, 0x9d, 0x06, 0x23, 0xff, 0xc4, 0x26, 0x45, 0xff, 0xdf, 0x41, 0x74, 0xff, 0xe8, 0x49, 0x88, 0xff, 0xda, 0x3e, 0x7e, 0xff, 0xa6, 0x20, 0x49, 0xff, 0x5b, 0x01, 0x07, 0xff, 0x8b, 0x22, 0x2e, 0xff, 0xff, 0x5b, 0x9e, 0xff, 0x88, 0x1d, 0x36, 0xff, 0x45, 0x00, 0x00, 0xff, 0x79, 0x2a, 0x02, 0xff, 0xe2, 0x9e, 0x04, 0xff, + 0xfd, 0xfb, 0xf5, 0xff, 0xfc, 0xfb, 0xf5, 0xff, 0xfa, 0xf8, 0xf2, 0xff, 0xf9, 0xf6, 0xf1, 0xff, 0xf9, 0xf5, 0xf0, 0xff, 0xf9, 0xf3, 0xee, 0xff, 0xfb, 0xf1, 0xec, 0xff, 0xfc, 0xee, 0xe9, 0xff, 0xfd, 0xeb, 0xe6, 0xff, 0xfd, 0xeb, 0xe9, 0xff, 0xfc, 0x9c, 0x81, 0xff, 0xfd, 0x6d, 0x3f, 0xff, 0xfd, 0x72, 0x46, 0xff, 0xfd, 0x73, 0x48, 0xff, 0xfd, 0x74, 0x4b, 0xff, 0xfd, 0x76, 0x4c, 0xff, 0xfd, 0x76, 0x4c, 0xff, 0xfd, 0x75, 0x4b, 0xff, 0xfd, 0x74, 0x49, 0xff, 0xfd, 0x6a, 0x3c, 0xff, 0xfb, 0xab, 0x8c, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xf7, 0xff, 0xfe, 0xfd, 0xf8, 0xff, 0xfd, 0xfd, 0xfa, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfb, 0xa5, 0x8c, 0xff, 0xfd, 0x66, 0x3b, 0xff, 0xfd, 0x70, 0x45, 0xff, 0xfd, 0x70, 0x45, 0xff, 0xfd, 0x70, 0x47, 0xff, 0xfd, 0x70, 0x48, 0xff, 0xfd, 0x71, 0x48, 0xff, 0xfd, 0x71, 0x48, 0xff, 0xfd, 0x72, 0x47, 0xff, 0xfd, 0x72, 0x45, 0xff, 0xfd, 0x70, 0x44, 0xff, 0xfc, 0x68, 0x39, 0xff, 0xfc, 0xcd, 0xba, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfd, 0xfd, 0xf9, 0xff, 0xfe, 0xfd, 0xf9, 0xff, 0xfd, 0xfe, 0xfa, 0xff, 0xfd, 0xfb, 0xf7, 0xff, 0xfc, 0x90, 0x65, 0xff, 0xfd, 0x73, 0x3a, 0xff, 0xfd, 0x70, 0x35, 0xff, 0xfc, 0xb3, 0x92, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfc, 0xfd, 0xfa, 0xff, 0xfd, 0xfd, 0xf8, 0xff, 0xfd, 0xff, 0xfb, 0xff, 0xfd, 0xf5, 0xee, 0xff, 0xfc, 0x82, 0x4e, 0xff, 0xfd, 0x73, 0x39, 0xff, 0xfd, 0x77, 0x3f, 0xff, 0xfd, 0x79, 0x42, 0xff, 0xfd, 0x7a, 0x43, 0xff, 0xfd, 0x7b, 0x45, 0xff, 0xfd, 0x7c, 0x45, 0xff, 0xfd, 0x7d, 0x45, 0xff, 0xfc, 0x7c, 0x46, 0xff, 0xfd, 0x7a, 0x41, 0xff, 0xfc, 0x86, 0x50, 0xff, 0xfd, 0xef, 0xe6, 0xff, 0xfb, 0xf8, 0xf4, 0xff, 0xfa, 0xf7, 0xf2, 0xff, 0xfa, 0xf7, 0xf1, 0xff, 0xf9, 0xf8, 0xf2, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xfa, 0xf8, 0xf3, 0xff, 0xfe, 0xfb, 0xf7, 0xff, 0xfa, 0xf7, 0xf3, 0xff, 0x6d, 0x31, 0x2a, 0xff, 0x51, 0x07, 0x00, 0xff, 0x5b, 0x12, 0x03, 0xff, 0x60, 0x16, 0x05, 0xff, 0x67, 0x1b, 0x07, 0xff, 0x6d, 0x1f, 0x08, 0xff, 0x71, 0x23, 0x08, 0xff, 0x74, 0x25, 0x08, 0xff, 0x76, 0x26, 0x07, 0xff, 0x74, 0x25, 0x07, 0xff, 0x71, 0x22, 0x07, 0xff, 0x6b, 0x1e, 0x07, 0xff, 0x66, 0x1a, 0x07, 0xff, 0x64, 0x17, 0x07, 0xff, 0x61, 0x15, 0x06, 0xff, 0x5f, 0x12, 0x05, 0xff, 0x5e, 0x0f, 0x04, 0xff, 0x5a, 0x0d, 0x03, 0xff, 0x54, 0x09, 0x02, 0xff, 0x4e, 0x05, 0x01, 0xff, 0x93, 0x2b, 0x0c, 0xff, 0xca, 0x4a, 0x13, 0xff, 0x5b, 0x0d, 0x02, 0xff, 0x54, 0x08, 0x01, 0xff, 0xca, 0x4c, 0x25, 0xff, 0xff, 0x67, 0x3f, 0xff, 0xce, 0x36, 0x38, 0xff, 0xa0, 0x0f, 0x21, 0xff, 0x8b, 0x03, 0x11, 0xff, 0x81, 0x01, 0x0c, 0xff, 0x72, 0x00, 0x05, 0xff, 0x72, 0x07, 0x12, 0xff, 0xb4, 0x2d, 0x56, 0xff, 0xf7, 0x4d, 0x96, 0xff, 0x98, 0x23, 0x43, 0xff, 0x49, 0x01, 0x00, 0xff, 0x4d, 0x00, 0x00, 0xff, 0xcf, 0x87, 0x03, 0xff, 0xa6, 0x56, 0x07, 0xff, + 0xfd, 0xfb, 0xf6, 0xff, 0xfc, 0xfb, 0xf6, 0xff, 0xfa, 0xf8, 0xf3, 0xff, 0xf9, 0xf6, 0xf1, 0xff, 0xf9, 0xf5, 0xf0, 0xff, 0xfa, 0xf3, 0xef, 0xff, 0xfb, 0xf1, 0xed, 0xff, 0xfc, 0xef, 0xea, 0xff, 0xfd, 0xec, 0xe7, 0xff, 0xfd, 0xec, 0xe9, 0xff, 0xfc, 0x9a, 0x83, 0xff, 0xfd, 0x69, 0x43, 0xff, 0xfd, 0x70, 0x4a, 0xff, 0xfd, 0x72, 0x4c, 0xff, 0xfd, 0x72, 0x4f, 0xff, 0xfd, 0x72, 0x50, 0xff, 0xfd, 0x73, 0x4f, 0xff, 0xfd, 0x72, 0x4d, 0xff, 0xfd, 0x6f, 0x4a, 0xff, 0xfc, 0x71, 0x4b, 0xff, 0xfc, 0xe7, 0xda, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfc, 0xf5, 0xf1, 0xff, 0xfb, 0x77, 0x57, 0xff, 0xfd, 0x69, 0x44, 0xff, 0xfd, 0x6d, 0x48, 0xff, 0xfd, 0x6d, 0x48, 0xff, 0xfd, 0x6d, 0x49, 0xff, 0xfd, 0x6e, 0x4a, 0xff, 0xfd, 0x6e, 0x4b, 0xff, 0xfd, 0x6f, 0x4b, 0xff, 0xfd, 0x6f, 0x4b, 0xff, 0xfd, 0x6f, 0x49, 0xff, 0xfd, 0x6e, 0x49, 0xff, 0xfd, 0x66, 0x3e, 0xff, 0xfb, 0x96, 0x78, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfc, 0xcb, 0xb7, 0xff, 0xfc, 0x6f, 0x3c, 0xff, 0xfd, 0x6c, 0x38, 0xff, 0xfc, 0xb4, 0x98, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfd, 0xfa, 0xf6, 0xff, 0xfb, 0x80, 0x51, 0xff, 0xfd, 0x70, 0x3c, 0xff, 0xfd, 0x75, 0x43, 0xff, 0xfd, 0x76, 0x45, 0xff, 0xfd, 0x77, 0x47, 0xff, 0xfd, 0x79, 0x48, 0xff, 0xfd, 0x7a, 0x49, 0xff, 0xfd, 0x7b, 0x49, 0xff, 0xfd, 0x7b, 0x49, 0xff, 0xfd, 0x77, 0x44, 0xff, 0xfd, 0x84, 0x53, 0xff, 0xfc, 0xef, 0xe7, 0xff, 0xfc, 0xf8, 0xf4, 0xff, 0xfa, 0xf7, 0xf2, 0xff, 0xfa, 0xf7, 0xf2, 0xff, 0xf9, 0xf7, 0xf2, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xfb, 0xf9, 0xf4, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xd7, 0xc7, 0xc3, 0xff, 0x52, 0x09, 0x00, 0xff, 0x56, 0x0c, 0x00, 0xff, 0x59, 0x11, 0x03, 0xff, 0x5d, 0x15, 0x06, 0xff, 0x61, 0x18, 0x08, 0xff, 0x66, 0x1a, 0x08, 0xff, 0x69, 0x1c, 0x08, 0xff, 0x6a, 0x1d, 0x08, 0xff, 0x69, 0x1d, 0x08, 0xff, 0x69, 0x1d, 0x08, 0xff, 0x67, 0x1b, 0x08, 0xff, 0x65, 0x19, 0x07, 0xff, 0x63, 0x17, 0x07, 0xff, 0x60, 0x15, 0x06, 0xff, 0x5e, 0x12, 0x05, 0xff, 0x5c, 0x0f, 0x04, 0xff, 0x59, 0x0d, 0x03, 0xff, 0x4f, 0x06, 0x01, 0xff, 0x59, 0x0d, 0x02, 0xff, 0xb8, 0x48, 0x0e, 0xff, 0xbe, 0x48, 0x14, 0xff, 0x60, 0x10, 0x03, 0xff, 0x53, 0x08, 0x00, 0xff, 0x52, 0x08, 0x00, 0xff, 0x55, 0x08, 0x02, 0xff, 0xa2, 0x31, 0x21, 0xff, 0xf1, 0x58, 0x4e, 0xff, 0xfd, 0x56, 0x66, 0xff, 0xea, 0x44, 0x68, 0xff, 0xde, 0x3d, 0x6a, 0xff, 0xe5, 0x42, 0x7a, 0xff, 0xf6, 0x4c, 0x91, 0xff, 0xe0, 0x44, 0x7f, 0xff, 0x7f, 0x17, 0x29, 0xff, 0x48, 0x01, 0x00, 0xff, 0x45, 0x00, 0x00, 0xff, 0x9e, 0x52, 0x01, 0xff, 0xdb, 0x95, 0x06, 0xff, 0x6b, 0x10, 0x04, 0xff, + 0xfe, 0xfb, 0xf6, 0xff, 0xfd, 0xfa, 0xf6, 0xff, 0xfb, 0xf8, 0xf4, 0xff, 0xfa, 0xf6, 0xf2, 0xff, 0xf9, 0xf4, 0xef, 0xff, 0xfa, 0xf3, 0xee, 0xff, 0xfb, 0xf1, 0xed, 0xff, 0xfc, 0xef, 0xea, 0xff, 0xfd, 0xec, 0xe7, 0xff, 0xfd, 0xec, 0xe8, 0xff, 0xfc, 0x98, 0x85, 0xff, 0xfd, 0x65, 0x46, 0xff, 0xfd, 0x6c, 0x4e, 0xff, 0xfe, 0x6f, 0x50, 0xff, 0xfd, 0x70, 0x52, 0xff, 0xfd, 0x6f, 0x52, 0xff, 0xfd, 0x6e, 0x51, 0xff, 0xfd, 0x6e, 0x4f, 0xff, 0xfd, 0x6a, 0x4c, 0xff, 0xfc, 0x72, 0x55, 0xff, 0xfc, 0xcb, 0xbe, 0xff, 0xfc, 0xd9, 0xcf, 0xff, 0xfc, 0xd7, 0xcd, 0xff, 0xfc, 0xd8, 0xcf, 0xff, 0xfc, 0xdd, 0xd5, 0xff, 0xfb, 0xa7, 0x97, 0xff, 0xfd, 0x63, 0x46, 0xff, 0xfd, 0x6a, 0x4c, 0xff, 0xfd, 0x6a, 0x4b, 0xff, 0xfd, 0x69, 0x4b, 0xff, 0xfd, 0x6a, 0x4c, 0xff, 0xfd, 0x6b, 0x4e, 0xff, 0xfd, 0x6b, 0x50, 0xff, 0xfd, 0x6c, 0x50, 0xff, 0xfd, 0x6b, 0x4f, 0xff, 0xfd, 0x6c, 0x4f, 0xff, 0xfd, 0x6c, 0x4e, 0xff, 0xfd, 0x69, 0x4a, 0xff, 0xfc, 0x6c, 0x4b, 0xff, 0xfb, 0xc5, 0xb6, 0xff, 0xfb, 0xd9, 0xcf, 0xff, 0xfc, 0xd6, 0xcb, 0xff, 0xfc, 0xd6, 0xcb, 0xff, 0xfc, 0xdd, 0xd2, 0xff, 0xfc, 0xb9, 0xa5, 0xff, 0xfc, 0x6d, 0x43, 0xff, 0xfd, 0x6b, 0x40, 0xff, 0xfc, 0x8b, 0x6b, 0xff, 0xfc, 0xd9, 0xcb, 0xff, 0xfc, 0xd9, 0xcb, 0xff, 0xfc, 0xd8, 0xcb, 0xff, 0xfc, 0xdd, 0xd1, 0xff, 0xfc, 0xc2, 0xb0, 0xff, 0xfc, 0x72, 0x49, 0xff, 0xfd, 0x70, 0x44, 0xff, 0xfd, 0x72, 0x47, 0xff, 0xfd, 0x74, 0x49, 0xff, 0xfd, 0x74, 0x4b, 0xff, 0xfd, 0x75, 0x4c, 0xff, 0xfd, 0x76, 0x4d, 0xff, 0xfd, 0x78, 0x4e, 0xff, 0xfd, 0x78, 0x4d, 0xff, 0xfd, 0x73, 0x47, 0xff, 0xfc, 0x80, 0x55, 0xff, 0xfd, 0xef, 0xe8, 0xff, 0xfc, 0xf8, 0xf4, 0xff, 0xfb, 0xf7, 0xf2, 0xff, 0xfa, 0xf8, 0xf3, 0xff, 0xf9, 0xf8, 0xf2, 0xff, 0xfa, 0xf9, 0xf3, 0xff, 0xfc, 0xf9, 0xf5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa1, 0x7d, 0x78, 0xff, 0x4a, 0x00, 0x00, 0xff, 0x57, 0x0d, 0x01, 0xff, 0x59, 0x10, 0x04, 0xff, 0x5c, 0x13, 0x07, 0xff, 0x5f, 0x15, 0x08, 0xff, 0x62, 0x17, 0x07, 0xff, 0x63, 0x18, 0x08, 0xff, 0x64, 0x19, 0x08, 0xff, 0x64, 0x19, 0x07, 0xff, 0x62, 0x18, 0x07, 0xff, 0x62, 0x18, 0x07, 0xff, 0x61, 0x17, 0x07, 0xff, 0x5e, 0x14, 0x07, 0xff, 0x5c, 0x12, 0x06, 0xff, 0x5b, 0x10, 0x03, 0xff, 0x59, 0x0d, 0x02, 0xff, 0x4d, 0x04, 0x01, 0xff, 0x7a, 0x26, 0x03, 0xff, 0xd3, 0x66, 0x0a, 0xff, 0x9b, 0x39, 0x09, 0xff, 0x4f, 0x07, 0x01, 0xff, 0x52, 0x09, 0x01, 0xff, 0x57, 0x0b, 0x02, 0xff, 0x58, 0x0b, 0x03, 0xff, 0x57, 0x0b, 0x02, 0xff, 0x50, 0x06, 0x00, 0xff, 0x61, 0x0e, 0x07, 0xff, 0x8c, 0x20, 0x22, 0xff, 0xb1, 0x2e, 0x42, 0xff, 0xb9, 0x31, 0x51, 0xff, 0xa5, 0x29, 0x46, 0xff, 0x7b, 0x16, 0x25, 0xff, 0x50, 0x04, 0x01, 0xff, 0x46, 0x01, 0x00, 0xff, 0x42, 0x00, 0x00, 0xff, 0x88, 0x3b, 0x01, 0xff, 0xe9, 0xa4, 0x04, 0xff, 0x7d, 0x24, 0x05, 0xff, 0x7c, 0x2a, 0x20, 0xff, + 0xfe, 0xfb, 0xf6, 0xff, 0xfc, 0xf9, 0xf5, 0xff, 0xfb, 0xf7, 0xf4, 0xff, 0xfa, 0xf6, 0xf2, 0xff, 0xf8, 0xf4, 0xef, 0xff, 0xfa, 0xf2, 0xee, 0xff, 0xfb, 0xf1, 0xed, 0xff, 0xfc, 0xef, 0xea, 0xff, 0xfd, 0xeb, 0xe6, 0xff, 0xfd, 0xec, 0xe8, 0xff, 0xfc, 0x95, 0x87, 0xff, 0xfd, 0x62, 0x4a, 0xff, 0xfd, 0x6a, 0x52, 0xff, 0xfd, 0x6b, 0x54, 0xff, 0xfe, 0x6d, 0x55, 0xff, 0xfd, 0x6d, 0x55, 0xff, 0xfd, 0x6b, 0x53, 0xff, 0xfd, 0x6a, 0x52, 0xff, 0xfd, 0x69, 0x51, 0xff, 0xfd, 0x66, 0x4e, 0xff, 0xfd, 0x62, 0x4b, 0xff, 0xfc, 0x63, 0x4c, 0xff, 0xfd, 0x62, 0x4c, 0xff, 0xfc, 0x62, 0x4b, 0xff, 0xfc, 0x62, 0x4c, 0xff, 0xfd, 0x61, 0x4b, 0xff, 0xfd, 0x67, 0x50, 0xff, 0xfd, 0x68, 0x4f, 0xff, 0xfd, 0x69, 0x4f, 0xff, 0xfd, 0x68, 0x50, 0xff, 0xfd, 0x68, 0x50, 0xff, 0xfd, 0x6a, 0x52, 0xff, 0xfd, 0x6b, 0x53, 0xff, 0xfd, 0x6b, 0x54, 0xff, 0xfd, 0x6b, 0x54, 0xff, 0xfd, 0x6a, 0x52, 0xff, 0xfd, 0x6a, 0x51, 0xff, 0xfd, 0x6a, 0x4f, 0xff, 0xfd, 0x66, 0x4b, 0xff, 0xfd, 0x62, 0x45, 0xff, 0xfc, 0x63, 0x47, 0xff, 0xfd, 0x62, 0x47, 0xff, 0xfd, 0x62, 0x47, 0xff, 0xfc, 0x64, 0x48, 0xff, 0xfc, 0x67, 0x48, 0xff, 0xfd, 0x6c, 0x4c, 0xff, 0xfd, 0x6d, 0x4c, 0xff, 0xfd, 0x68, 0x46, 0xff, 0xfc, 0x66, 0x44, 0xff, 0xfc, 0x67, 0x45, 0xff, 0xfc, 0x67, 0x47, 0xff, 0xfc, 0x68, 0x47, 0xff, 0xfd, 0x67, 0x47, 0xff, 0xfd, 0x6c, 0x4b, 0xff, 0xfd, 0x6f, 0x4a, 0xff, 0xfd, 0x70, 0x4b, 0xff, 0xfd, 0x71, 0x4d, 0xff, 0xfd, 0x72, 0x4f, 0xff, 0xfd, 0x73, 0x50, 0xff, 0xfd, 0x74, 0x51, 0xff, 0xfd, 0x75, 0x51, 0xff, 0xfd, 0x74, 0x50, 0xff, 0xfd, 0x6f, 0x4a, 0xff, 0xfc, 0x7b, 0x57, 0xff, 0xfd, 0xf0, 0xe8, 0xff, 0xfc, 0xf9, 0xf4, 0xff, 0xfa, 0xf7, 0xf2, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xfb, 0xfa, 0xf5, 0xff, 0xfe, 0xfc, 0xf8, 0xff, 0xfc, 0xfa, 0xf5, 0xff, 0x6d, 0x34, 0x2e, 0xff, 0x4d, 0x03, 0x00, 0xff, 0x56, 0x0d, 0x01, 0xff, 0x59, 0x0f, 0x03, 0xff, 0x5b, 0x12, 0x06, 0xff, 0x5c, 0x14, 0x07, 0xff, 0x5d, 0x15, 0x08, 0xff, 0x5f, 0x16, 0x09, 0xff, 0x5f, 0x16, 0x09, 0xff, 0x5f, 0x16, 0x08, 0xff, 0x5f, 0x16, 0x08, 0xff, 0x5d, 0x15, 0x08, 0xff, 0x5c, 0x13, 0x07, 0xff, 0x5b, 0x12, 0x06, 0xff, 0x59, 0x10, 0x04, 0xff, 0x54, 0x0a, 0x02, 0xff, 0x54, 0x08, 0x01, 0xff, 0xa3, 0x4b, 0x03, 0xff, 0xd6, 0x73, 0x04, 0xff, 0x75, 0x25, 0x03, 0xff, 0x46, 0x02, 0x00, 0xff, 0x53, 0x0a, 0x02, 0xff, 0x56, 0x0c, 0x02, 0xff, 0x57, 0x0c, 0x03, 0xff, 0x58, 0x0c, 0x03, 0xff, 0x58, 0x0b, 0x03, 0xff, 0x55, 0x09, 0x01, 0xff, 0x4f, 0x06, 0x00, 0xff, 0x4f, 0x04, 0x00, 0xff, 0x51, 0x03, 0x00, 0xff, 0x50, 0x02, 0x00, 0xff, 0x4c, 0x02, 0x00, 0xff, 0x4a, 0x03, 0x00, 0xff, 0x49, 0x02, 0x00, 0xff, 0x47, 0x00, 0x01, 0xff, 0x94, 0x43, 0x01, 0xff, 0xeb, 0x9f, 0x03, 0xff, 0x8c, 0x34, 0x08, 0xff, 0x6e, 0x14, 0x0f, 0xff, 0xd8, 0xba, 0xb1, 0xff, + 0xfd, 0xfa, 0xf6, 0xff, 0xfc, 0xf9, 0xf5, 0xff, 0xfb, 0xf7, 0xf3, 0xff, 0xfa, 0xf6, 0xf2, 0xff, 0xf9, 0xf5, 0xf0, 0xff, 0xf9, 0xf3, 0xee, 0xff, 0xfa, 0xf1, 0xec, 0xff, 0xfc, 0xef, 0xe9, 0xff, 0xfe, 0xeb, 0xe6, 0xff, 0xfd, 0xeb, 0xe9, 0xff, 0xfc, 0x92, 0x89, 0xff, 0xfd, 0x5e, 0x4e, 0xff, 0xfd, 0x66, 0x55, 0xff, 0xfd, 0x67, 0x56, 0xff, 0xfd, 0x69, 0x57, 0xff, 0xfd, 0x69, 0x58, 0xff, 0xfd, 0x68, 0x56, 0xff, 0xfd, 0x67, 0x55, 0xff, 0xfd, 0x65, 0x54, 0xff, 0xfd, 0x63, 0x54, 0xff, 0xfe, 0x63, 0x53, 0xff, 0xfd, 0x64, 0x54, 0xff, 0xfd, 0x65, 0x55, 0xff, 0xfd, 0x66, 0x54, 0xff, 0xfd, 0x65, 0x54, 0xff, 0xfd, 0x66, 0x54, 0xff, 0xfd, 0x66, 0x54, 0xff, 0xfd, 0x65, 0x54, 0xff, 0xfd, 0x66, 0x55, 0xff, 0xfd, 0x66, 0x55, 0xff, 0xfd, 0x66, 0x55, 0xff, 0xfd, 0x68, 0x56, 0xff, 0xfd, 0x6a, 0x58, 0xff, 0xfd, 0x6b, 0x59, 0xff, 0xfd, 0x6b, 0x57, 0xff, 0xfd, 0x6a, 0x55, 0xff, 0xfd, 0x6a, 0x54, 0xff, 0xfd, 0x69, 0x53, 0xff, 0xfd, 0x68, 0x51, 0xff, 0xfd, 0x67, 0x51, 0xff, 0xfd, 0x68, 0x51, 0xff, 0xfd, 0x68, 0x51, 0xff, 0xfd, 0x67, 0x50, 0xff, 0xfd, 0x69, 0x50, 0xff, 0xfd, 0x6b, 0x52, 0xff, 0xfd, 0x6b, 0x53, 0xff, 0xfd, 0x6b, 0x52, 0xff, 0xfd, 0x6b, 0x51, 0xff, 0xfd, 0x6a, 0x50, 0xff, 0xfd, 0x6a, 0x50, 0xff, 0xfd, 0x6a, 0x51, 0xff, 0xfd, 0x6b, 0x50, 0xff, 0xfd, 0x6b, 0x51, 0xff, 0xfd, 0x6c, 0x51, 0xff, 0xfd, 0x6d, 0x50, 0xff, 0xfd, 0x6e, 0x51, 0xff, 0xfd, 0x6f, 0x53, 0xff, 0xfe, 0x6f, 0x54, 0xff, 0xfd, 0x6f, 0x55, 0xff, 0xfd, 0x70, 0x55, 0xff, 0xfd, 0x71, 0x54, 0xff, 0xfd, 0x71, 0x53, 0xff, 0xfd, 0x6c, 0x4c, 0xff, 0xfc, 0x77, 0x59, 0xff, 0xfd, 0xf0, 0xe8, 0xff, 0xfc, 0xfa, 0xf5, 0xff, 0xfa, 0xf8, 0xf3, 0xff, 0xfa, 0xfa, 0xf3, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xfc, 0xfb, 0xf5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xd4, 0xc3, 0xbe, 0xff, 0x4c, 0x06, 0x00, 0xff, 0x51, 0x09, 0x00, 0xff, 0x56, 0x0c, 0x03, 0xff, 0x58, 0x0e, 0x02, 0xff, 0x58, 0x10, 0x04, 0xff, 0x59, 0x12, 0x08, 0xff, 0x5b, 0x13, 0x09, 0xff, 0x5d, 0x14, 0x09, 0xff, 0x5d, 0x14, 0x09, 0xff, 0x5d, 0x14, 0x0a, 0xff, 0x5c, 0x14, 0x09, 0xff, 0x5a, 0x13, 0x09, 0xff, 0x59, 0x11, 0x07, 0xff, 0x58, 0x0f, 0x05, 0xff, 0x50, 0x06, 0x01, 0xff, 0x66, 0x19, 0x02, 0xff, 0xc8, 0x75, 0x03, 0xff, 0xba, 0x66, 0x03, 0xff, 0x58, 0x0f, 0x01, 0xff, 0x4a, 0x05, 0x01, 0xff, 0x52, 0x0a, 0x02, 0xff, 0x55, 0x0c, 0x03, 0xff, 0x57, 0x0c, 0x03, 0xff, 0x58, 0x0c, 0x03, 0xff, 0x58, 0x0c, 0x03, 0xff, 0x50, 0x09, 0x00, 0xff, 0x91, 0x1c, 0x23, 0xff, 0xc6, 0x2b, 0x40, 0xff, 0x64, 0x0b, 0x09, 0xff, 0x51, 0x05, 0x00, 0xff, 0x53, 0x05, 0x00, 0xff, 0x4c, 0x04, 0x00, 0xff, 0x44, 0x01, 0x00, 0xff, 0x64, 0x10, 0x04, 0xff, 0xc3, 0x67, 0x03, 0xff, 0xe4, 0x8a, 0x05, 0xff, 0x88, 0x25, 0x06, 0xff, 0x63, 0x01, 0x00, 0xff, 0xa2, 0x68, 0x5e, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xfd, 0xfb, 0xf6, 0xff, 0xfd, 0xfa, 0xf5, 0xff, 0xfb, 0xf8, 0xf4, 0xff, 0xfa, 0xf7, 0xf2, 0xff, 0xf9, 0xf5, 0xf0, 0xff, 0xf9, 0xf3, 0xef, 0xff, 0xf9, 0xf1, 0xec, 0xff, 0xfc, 0xee, 0xe9, 0xff, 0xfd, 0xeb, 0xe6, 0xff, 0xfd, 0xeb, 0xe9, 0xff, 0xfc, 0x8e, 0x8a, 0xff, 0xfd, 0x58, 0x50, 0xff, 0xfe, 0x60, 0x58, 0xff, 0xfd, 0x62, 0x58, 0xff, 0xfd, 0x64, 0x59, 0xff, 0xfd, 0x64, 0x5a, 0xff, 0xfd, 0x64, 0x59, 0xff, 0xfd, 0x63, 0x59, 0xff, 0xfe, 0x61, 0x57, 0xff, 0xfd, 0x61, 0x58, 0xff, 0xfd, 0x61, 0x58, 0xff, 0xfd, 0x63, 0x5a, 0xff, 0xfd, 0x65, 0x5a, 0xff, 0xfd, 0x65, 0x59, 0xff, 0xfd, 0x63, 0x59, 0xff, 0xfd, 0x63, 0x59, 0xff, 0xfd, 0x63, 0x58, 0xff, 0xfd, 0x63, 0x59, 0xff, 0xfd, 0x63, 0x59, 0xff, 0xfd, 0x64, 0x5a, 0xff, 0xfd, 0x64, 0x59, 0xff, 0xfd, 0x65, 0x5a, 0xff, 0xfd, 0x67, 0x5b, 0xff, 0xfd, 0x69, 0x5b, 0xff, 0xfd, 0x68, 0x5a, 0xff, 0xfd, 0x67, 0x59, 0xff, 0xfd, 0x66, 0x58, 0xff, 0xfd, 0x66, 0x57, 0xff, 0xfd, 0x66, 0x56, 0xff, 0xfd, 0x65, 0x56, 0xff, 0xfd, 0x66, 0x57, 0xff, 0xfd, 0x67, 0x57, 0xff, 0xfd, 0x68, 0x57, 0xff, 0xfd, 0x69, 0x57, 0xff, 0xfd, 0x69, 0x58, 0xff, 0xfe, 0x6a, 0x58, 0xff, 0xfd, 0x6a, 0x57, 0xff, 0xfd, 0x69, 0x57, 0xff, 0xfd, 0x69, 0x56, 0xff, 0xfd, 0x69, 0x56, 0xff, 0xfd, 0x69, 0x56, 0xff, 0xfd, 0x6a, 0x55, 0xff, 0xfd, 0x6b, 0x55, 0xff, 0xfe, 0x6b, 0x56, 0xff, 0xfe, 0x6b, 0x56, 0xff, 0xfd, 0x6b, 0x56, 0xff, 0xfd, 0x6b, 0x57, 0xff, 0xfd, 0x6c, 0x57, 0xff, 0xfd, 0x6c, 0x57, 0xff, 0xfd, 0x6d, 0x57, 0xff, 0xfd, 0x6f, 0x57, 0xff, 0xfd, 0x6e, 0x55, 0xff, 0xfd, 0x69, 0x4f, 0xff, 0xfc, 0x76, 0x5e, 0xff, 0xfd, 0xf0, 0xe9, 0xff, 0xfc, 0xf9, 0xf5, 0xff, 0xfb, 0xf8, 0xf3, 0xff, 0xfa, 0xf9, 0xf3, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xfb, 0xfb, 0xf5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa0, 0x7f, 0x7b, 0xff, 0x43, 0x00, 0x00, 0xff, 0x53, 0x0b, 0x01, 0xff, 0x54, 0x0c, 0x03, 0xff, 0x56, 0x0d, 0x03, 0xff, 0x57, 0x0f, 0x05, 0xff, 0x58, 0x11, 0x07, 0xff, 0x5a, 0x12, 0x08, 0xff, 0x5a, 0x12, 0x09, 0xff, 0x5b, 0x13, 0x0a, 0xff, 0x5b, 0x13, 0x0a, 0xff, 0x5a, 0x12, 0x09, 0xff, 0x5a, 0x11, 0x08, 0xff, 0x59, 0x10, 0x06, 0xff, 0x52, 0x07, 0x04, 0xff, 0x87, 0x34, 0x03, 0xff, 0xdb, 0x87, 0x03, 0xff, 0x92, 0x49, 0x02, 0xff, 0x48, 0x03, 0x01, 0xff, 0x4e, 0x08, 0x01, 0xff, 0x54, 0x0c, 0x03, 0xff, 0x55, 0x0d, 0x04, 0xff, 0x56, 0x0d, 0x04, 0xff, 0x57, 0x0d, 0x04, 0xff, 0x59, 0x0c, 0x04, 0xff, 0x58, 0x0b, 0x02, 0xff, 0x53, 0x09, 0x00, 0xff, 0xd2, 0x33, 0x4a, 0xff, 0xfd, 0x41, 0x64, 0xff, 0x5a, 0x08, 0x09, 0xff, 0x48, 0x02, 0x00, 0xff, 0x5c, 0x08, 0x05, 0xff, 0x7c, 0x14, 0x10, 0xff, 0xb4, 0x2b, 0x1c, 0xff, 0xd6, 0x49, 0x17, 0xff, 0xb1, 0x4c, 0x07, 0xff, 0x79, 0x0f, 0x04, 0xff, 0x9d, 0x50, 0x4d, 0xff, 0x89, 0x40, 0x35, 0xff, 0x9f, 0x6d, 0x64, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xfd, 0xfa, 0xf5, 0xff, 0xfc, 0xfa, 0xf5, 0xff, 0xfb, 0xf8, 0xf4, 0xff, 0xfa, 0xf7, 0xf2, 0xff, 0xf8, 0xf5, 0xf0, 0xff, 0xf7, 0xf3, 0xee, 0xff, 0xf9, 0xf1, 0xed, 0xff, 0xfc, 0xee, 0xe9, 0xff, 0xfd, 0xea, 0xe6, 0xff, 0xfd, 0xeb, 0xe9, 0xff, 0xfc, 0x91, 0x92, 0xff, 0xfd, 0x52, 0x54, 0xff, 0xfd, 0x5b, 0x5b, 0xff, 0xfd, 0x5d, 0x5c, 0xff, 0xfd, 0x5f, 0x5d, 0xff, 0xfd, 0x60, 0x5e, 0xff, 0xfd, 0x60, 0x5d, 0xff, 0xfd, 0x5f, 0x5d, 0xff, 0xfd, 0x5e, 0x5c, 0xff, 0xfd, 0x5e, 0x5c, 0xff, 0xfd, 0x5e, 0x5c, 0xff, 0xfd, 0x5f, 0x5d, 0xff, 0xfd, 0x61, 0x5e, 0xff, 0xfd, 0x62, 0x5f, 0xff, 0xfd, 0x61, 0x5f, 0xff, 0xfd, 0x60, 0x5f, 0xff, 0xfd, 0x60, 0x5e, 0xff, 0xfd, 0x61, 0x5e, 0xff, 0xfd, 0x62, 0x5e, 0xff, 0xfd, 0x63, 0x5e, 0xff, 0xfd, 0x63, 0x5d, 0xff, 0xfd, 0x64, 0x5e, 0xff, 0xfd, 0x65, 0x5f, 0xff, 0xfd, 0x66, 0x5f, 0xff, 0xfd, 0x65, 0x5e, 0xff, 0xfd, 0x64, 0x5c, 0xff, 0xfc, 0x64, 0x5b, 0xff, 0xfd, 0x64, 0x5b, 0xff, 0xfc, 0x64, 0x5a, 0xff, 0xfd, 0x64, 0x5a, 0xff, 0xfd, 0x64, 0x5b, 0xff, 0xfd, 0x65, 0x5a, 0xff, 0xfd, 0x65, 0x5b, 0xff, 0xfd, 0x66, 0x5c, 0xff, 0xfd, 0x67, 0x5d, 0xff, 0xfd, 0x68, 0x5c, 0xff, 0xfd, 0x67, 0x5c, 0xff, 0xfd, 0x67, 0x5c, 0xff, 0xfd, 0x67, 0x5b, 0xff, 0xfd, 0x65, 0x5a, 0xff, 0xfd, 0x66, 0x59, 0xff, 0xfd, 0x66, 0x58, 0xff, 0xfd, 0x68, 0x59, 0xff, 0xfd, 0x68, 0x59, 0xff, 0xfd, 0x68, 0x5a, 0xff, 0xfd, 0x69, 0x59, 0xff, 0xfd, 0x67, 0x58, 0xff, 0xfd, 0x67, 0x58, 0xff, 0xfd, 0x67, 0x59, 0xff, 0xfd, 0x69, 0x59, 0xff, 0xfd, 0x6a, 0x59, 0xff, 0xfd, 0x69, 0x58, 0xff, 0xfd, 0x64, 0x53, 0xff, 0xfc, 0x78, 0x6a, 0xff, 0xfc, 0xf3, 0xee, 0xff, 0xfc, 0xf9, 0xf5, 0xff, 0xfb, 0xf7, 0xf3, 0xff, 0xfa, 0xf9, 0xf3, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xfb, 0xfb, 0xf5, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xd1, 0xc0, 0xbc, 0xff, 0x57, 0x17, 0x10, 0xff, 0x48, 0x00, 0x00, 0xff, 0x53, 0x0a, 0x01, 0xff, 0x55, 0x0e, 0x04, 0xff, 0x56, 0x0f, 0x05, 0xff, 0x56, 0x11, 0x07, 0xff, 0x57, 0x11, 0x07, 0xff, 0x58, 0x11, 0x09, 0xff, 0x59, 0x12, 0x09, 0xff, 0x59, 0x12, 0x0a, 0xff, 0x59, 0x12, 0x09, 0xff, 0x5a, 0x10, 0x07, 0xff, 0x56, 0x0a, 0x06, 0xff, 0x89, 0x35, 0x04, 0xff, 0xd7, 0x85, 0x03, 0xff, 0x6d, 0x26, 0x02, 0xff, 0x47, 0x02, 0x02, 0xff, 0x52, 0x0a, 0x02, 0xff, 0x53, 0x0c, 0x03, 0xff, 0x54, 0x0e, 0x05, 0xff, 0x57, 0x0f, 0x06, 0xff, 0x58, 0x0f, 0x07, 0xff, 0x58, 0x0f, 0x07, 0xff, 0x5a, 0x0d, 0x05, 0xff, 0x58, 0x0a, 0x00, 0xff, 0x66, 0x0d, 0x09, 0xff, 0xf2, 0x41, 0x66, 0xff, 0xe4, 0x37, 0x56, 0xff, 0x90, 0x13, 0x19, 0xff, 0xbe, 0x20, 0x2e, 0xff, 0xd0, 0x2c, 0x38, 0xff, 0xc4, 0x2c, 0x2f, 0xff, 0x95, 0x1d, 0x16, 0xff, 0x5b, 0x06, 0x01, 0xff, 0x6c, 0x0f, 0x0e, 0xff, 0xbd, 0x83, 0x7f, 0xff, 0xff, 0xff, 0xfa, 0xff, 0x7d, 0x42, 0x38, 0xff, 0xac, 0x85, 0x7c, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xfd, 0xfa, 0xf5, 0xff, 0xfc, 0xfa, 0xf5, 0xff, 0xfb, 0xf8, 0xf3, 0xff, 0xfa, 0xf7, 0xf2, 0xff, 0xf9, 0xf6, 0xf0, 0xff, 0xf8, 0xf3, 0xee, 0xff, 0xf9, 0xf1, 0xec, 0xff, 0xfb, 0xed, 0xe8, 0xff, 0xfd, 0xe9, 0xe4, 0xff, 0xfe, 0xea, 0xe8, 0xff, 0xfc, 0x9e, 0xa3, 0xff, 0xfd, 0x4c, 0x55, 0xff, 0xfd, 0x57, 0x5d, 0xff, 0xfd, 0x58, 0x5f, 0xff, 0xfd, 0x5a, 0x61, 0xff, 0xfd, 0x5c, 0x62, 0xff, 0xfd, 0x5c, 0x62, 0xff, 0xfc, 0x5c, 0x62, 0xff, 0xfd, 0x5c, 0x61, 0xff, 0xfd, 0x5b, 0x61, 0xff, 0xfd, 0x5b, 0x61, 0xff, 0xfd, 0x5c, 0x61, 0xff, 0xfd, 0x5d, 0x61, 0xff, 0xfd, 0x5f, 0x63, 0xff, 0xfd, 0x5f, 0x64, 0xff, 0xfd, 0x5e, 0x63, 0xff, 0xfd, 0x5e, 0x62, 0xff, 0xfd, 0x5f, 0x63, 0xff, 0xfc, 0x5f, 0x64, 0xff, 0xfd, 0x60, 0x63, 0xff, 0xfd, 0x60, 0x63, 0xff, 0xfd, 0x61, 0x62, 0xff, 0xfd, 0x62, 0x62, 0xff, 0xfd, 0x62, 0x62, 0xff, 0xfd, 0x62, 0x60, 0xff, 0xfd, 0x61, 0x60, 0xff, 0xfd, 0x61, 0x5f, 0xff, 0xfd, 0x63, 0x5e, 0xff, 0xfd, 0x63, 0x5e, 0xff, 0xfd, 0x62, 0x5f, 0xff, 0xfd, 0x63, 0x5f, 0xff, 0xfd, 0x63, 0x5e, 0xff, 0xfd, 0x63, 0x5e, 0xff, 0xfd, 0x63, 0x5e, 0xff, 0xfd, 0x64, 0x5f, 0xff, 0xfd, 0x65, 0x5f, 0xff, 0xfd, 0x65, 0x5f, 0xff, 0xfd, 0x64, 0x5f, 0xff, 0xfd, 0x64, 0x5d, 0xff, 0xfd, 0x64, 0x5d, 0xff, 0xfd, 0x64, 0x5c, 0xff, 0xfd, 0x63, 0x5b, 0xff, 0xfd, 0x63, 0x5b, 0xff, 0xfd, 0x64, 0x5c, 0xff, 0xfd, 0x64, 0x5c, 0xff, 0xfd, 0x64, 0x5b, 0xff, 0xfd, 0x63, 0x5a, 0xff, 0xfd, 0x63, 0x5a, 0xff, 0xfd, 0x63, 0x5b, 0xff, 0xfd, 0x63, 0x5a, 0xff, 0xfd, 0x64, 0x5a, 0xff, 0xfd, 0x64, 0x5b, 0xff, 0xfd, 0x5d, 0x54, 0xff, 0xfc, 0x82, 0x7c, 0xff, 0xfd, 0xfa, 0xf6, 0xff, 0xfd, 0xf9, 0xf4, 0xff, 0xfc, 0xf8, 0xf3, 0xff, 0xfa, 0xf9, 0xf3, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xfc, 0xf9, 0xf5, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xea, 0xe1, 0xdd, 0xff, 0xa0, 0x77, 0x72, 0xff, 0x54, 0x0d, 0x05, 0xff, 0x53, 0x0c, 0x02, 0xff, 0x55, 0x0f, 0x05, 0xff, 0x57, 0x10, 0x06, 0xff, 0x57, 0x11, 0x07, 0xff, 0x57, 0x11, 0x08, 0xff, 0x58, 0x12, 0x09, 0xff, 0x58, 0x11, 0x09, 0xff, 0x58, 0x11, 0x08, 0xff, 0x5a, 0x10, 0x06, 0xff, 0x53, 0x08, 0x04, 0xff, 0xc1, 0x6d, 0x04, 0xff, 0x7a, 0x34, 0x02, 0xff, 0x47, 0x02, 0x01, 0xff, 0x54, 0x0c, 0x02, 0xff, 0x55, 0x0d, 0x03, 0xff, 0x56, 0x0e, 0x05, 0xff, 0x56, 0x0f, 0x07, 0xff, 0x57, 0x10, 0x08, 0xff, 0x58, 0x10, 0x08, 0xff, 0x57, 0x10, 0x08, 0xff, 0x59, 0x0e, 0x05, 0xff, 0x54, 0x08, 0x00, 0xff, 0x83, 0x18, 0x1f, 0xff, 0xff, 0x4c, 0x7d, 0xff, 0xa2, 0x24, 0x3c, 0xff, 0x44, 0x03, 0x03, 0xff, 0x66, 0x0a, 0x0c, 0xff, 0x59, 0x07, 0x05, 0xff, 0x50, 0x03, 0x00, 0xff, 0x55, 0x05, 0x00, 0xff, 0x52, 0x00, 0x00, 0xff, 0xa8, 0x7c, 0x78, 0xff, 0xff, 0xff, 0xff, 0xff, 0x9c, 0x72, 0x68, 0xff, 0x6f, 0x34, 0x2b, 0xff, 0xee, 0xe6, 0xe0, 0xff, 0xff, 0xff, 0xfd, 0xff, + 0xfd, 0xfa, 0xf5, 0xff, 0xfb, 0xfb, 0xf5, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xfa, 0xf7, 0xf2, 0xff, 0xf9, 0xf6, 0xf1, 0xff, 0xf9, 0xf3, 0xef, 0xff, 0xf9, 0xf1, 0xed, 0xff, 0xfb, 0xed, 0xe8, 0xff, 0xfd, 0xe9, 0xe4, 0xff, 0xfd, 0xe8, 0xe6, 0xff, 0xfc, 0xba, 0xbf, 0xff, 0xfc, 0x4a, 0x5c, 0xff, 0xfd, 0x51, 0x5e, 0xff, 0xfd, 0x53, 0x60, 0xff, 0xfd, 0x55, 0x63, 0xff, 0xfd, 0x57, 0x65, 0xff, 0xfd, 0x58, 0x65, 0xff, 0xfd, 0x59, 0x66, 0xff, 0xfd, 0x5a, 0x65, 0xff, 0xfd, 0x59, 0x65, 0xff, 0xfd, 0x59, 0x65, 0xff, 0xfd, 0x59, 0x65, 0xff, 0xfd, 0x59, 0x65, 0xff, 0xfd, 0x5b, 0x66, 0xff, 0xfd, 0x5c, 0x65, 0xff, 0xfd, 0x5c, 0x64, 0xff, 0xfd, 0x5c, 0x64, 0xff, 0xfd, 0x5d, 0x65, 0xff, 0xfd, 0x5e, 0x66, 0xff, 0xfd, 0x5e, 0x66, 0xff, 0xfd, 0x5e, 0x65, 0xff, 0xfd, 0x5e, 0x66, 0xff, 0xfd, 0x5e, 0x66, 0xff, 0xfd, 0x5f, 0x66, 0xff, 0xfd, 0x5f, 0x65, 0xff, 0xfd, 0x5f, 0x65, 0xff, 0xfd, 0x5f, 0x64, 0xff, 0xfd, 0x60, 0x64, 0xff, 0xfd, 0x61, 0x64, 0xff, 0xfd, 0x60, 0x65, 0xff, 0xfd, 0x60, 0x64, 0xff, 0xfd, 0x60, 0x63, 0xff, 0xfd, 0x60, 0x63, 0xff, 0xfd, 0x60, 0x63, 0xff, 0xfd, 0x60, 0x63, 0xff, 0xfd, 0x60, 0x62, 0xff, 0xfd, 0x60, 0x62, 0xff, 0xfd, 0x60, 0x62, 0xff, 0xfd, 0x61, 0x61, 0xff, 0xfd, 0x62, 0x60, 0xff, 0xfd, 0x62, 0x5f, 0xff, 0xfd, 0x61, 0x5e, 0xff, 0xfd, 0x60, 0x5f, 0xff, 0xfd, 0x60, 0x5f, 0xff, 0xfd, 0x61, 0x5e, 0xff, 0xfd, 0x61, 0x5e, 0xff, 0xfd, 0x60, 0x5d, 0xff, 0xfd, 0x60, 0x5d, 0xff, 0xfd, 0x5f, 0x5d, 0xff, 0xfd, 0x5f, 0x5d, 0xff, 0xfd, 0x5f, 0x5d, 0xff, 0xfd, 0x5f, 0x5d, 0xff, 0xfd, 0x54, 0x53, 0xff, 0xfc, 0x9e, 0x9c, 0xff, 0xfd, 0xff, 0xfb, 0xff, 0xfd, 0xf8, 0xf3, 0xff, 0xfb, 0xf7, 0xf2, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf9, 0xf8, 0xf2, 0xff, 0xf9, 0xf8, 0xf2, 0xff, 0xfa, 0xf8, 0xf3, 0xff, 0xfc, 0xf9, 0xf4, 0xff, 0xff, 0xff, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0x84, 0x50, 0x4b, 0xff, 0x4d, 0x03, 0x00, 0xff, 0x56, 0x0e, 0x04, 0xff, 0x57, 0x10, 0x06, 0xff, 0x58, 0x11, 0x08, 0xff, 0x57, 0x11, 0x08, 0xff, 0x57, 0x12, 0x09, 0xff, 0x58, 0x12, 0x09, 0xff, 0x57, 0x11, 0x08, 0xff, 0x57, 0x0f, 0x07, 0xff, 0x52, 0x08, 0x04, 0xff, 0xbf, 0x68, 0x04, 0xff, 0x70, 0x26, 0x04, 0xff, 0x4e, 0x08, 0x02, 0xff, 0x56, 0x0d, 0x03, 0xff, 0x57, 0x0e, 0x04, 0xff, 0x57, 0x0f, 0x06, 0xff, 0x57, 0x10, 0x07, 0xff, 0x57, 0x10, 0x08, 0xff, 0x57, 0x10, 0x08, 0xff, 0x57, 0x0f, 0x07, 0xff, 0x58, 0x0c, 0x04, 0xff, 0x50, 0x05, 0x00, 0xff, 0xad, 0x28, 0x40, 0xff, 0xff, 0x4f, 0x85, 0xff, 0x64, 0x12, 0x1f, 0xff, 0x22, 0x00, 0x00, 0xff, 0x55, 0x07, 0x06, 0xff, 0xa3, 0x1c, 0x1f, 0xff, 0x53, 0x06, 0x01, 0xff, 0x46, 0x01, 0x00, 0xff, 0x34, 0x00, 0x00, 0xff, 0x99, 0x73, 0x72, 0xff, 0xae, 0x87, 0x7f, 0xff, 0x7e, 0x47, 0x3b, 0xff, 0xec, 0xe5, 0xde, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xf9, 0xff, + 0xfd, 0xfa, 0xf6, 0xff, 0xfc, 0xfb, 0xf6, 0xff, 0xfb, 0xf9, 0xf4, 0xff, 0xfa, 0xf7, 0xf2, 0xff, 0xf9, 0xf6, 0xf1, 0xff, 0xf9, 0xf4, 0xef, 0xff, 0xf9, 0xf1, 0xed, 0xff, 0xfb, 0xed, 0xe8, 0xff, 0xfd, 0xe9, 0xe5, 0xff, 0xfd, 0xe5, 0xe2, 0xff, 0xfd, 0xd8, 0xd9, 0xff, 0xfc, 0x5d, 0x71, 0xff, 0xfd, 0x48, 0x5c, 0xff, 0xfd, 0x4d, 0x62, 0xff, 0xfd, 0x4f, 0x64, 0xff, 0xfd, 0x51, 0x66, 0xff, 0xfd, 0x53, 0x67, 0xff, 0xfd, 0x54, 0x68, 0xff, 0xfd, 0x55, 0x68, 0xff, 0xfd, 0x55, 0x68, 0xff, 0xfd, 0x55, 0x68, 0xff, 0xfd, 0x56, 0x68, 0xff, 0xfd, 0x55, 0x69, 0xff, 0xfd, 0x55, 0x69, 0xff, 0xfd, 0x56, 0x68, 0xff, 0xfd, 0x57, 0x67, 0xff, 0xfd, 0x57, 0x68, 0xff, 0xfd, 0x58, 0x68, 0xff, 0xfd, 0x58, 0x68, 0xff, 0xfd, 0x59, 0x68, 0xff, 0xfd, 0x5a, 0x68, 0xff, 0xfd, 0x5b, 0x69, 0xff, 0xfd, 0x5b, 0x69, 0xff, 0xfd, 0x5b, 0x69, 0xff, 0xfd, 0x5c, 0x68, 0xff, 0xfd, 0x5d, 0x67, 0xff, 0xfd, 0x5e, 0x67, 0xff, 0xfd, 0x5e, 0x67, 0xff, 0xfd, 0x5e, 0x68, 0xff, 0xfd, 0x5e, 0x67, 0xff, 0xfd, 0x5e, 0x66, 0xff, 0xfc, 0x5e, 0x66, 0xff, 0xfd, 0x5d, 0x65, 0xff, 0xfd, 0x5d, 0x65, 0xff, 0xfd, 0x5e, 0x64, 0xff, 0xfd, 0x5d, 0x64, 0xff, 0xfd, 0x5d, 0x64, 0xff, 0xfd, 0x5d, 0x65, 0xff, 0xfd, 0x5e, 0x65, 0xff, 0xfc, 0x5f, 0x64, 0xff, 0xfc, 0x5f, 0x64, 0xff, 0xfd, 0x5e, 0x63, 0xff, 0xfd, 0x5d, 0x63, 0xff, 0xfd, 0x5d, 0x62, 0xff, 0xfd, 0x5d, 0x62, 0xff, 0xfd, 0x5c, 0x61, 0xff, 0xfd, 0x5c, 0x60, 0xff, 0xfd, 0x5c, 0x61, 0xff, 0xfd, 0x5b, 0x61, 0xff, 0xfd, 0x5b, 0x61, 0xff, 0xfd, 0x5b, 0x61, 0xff, 0xfd, 0x59, 0x61, 0xff, 0xfc, 0x53, 0x5a, 0xff, 0xfc, 0xcb, 0xc9, 0xff, 0xfd, 0xfe, 0xf8, 0xff, 0xfc, 0xf8, 0xf3, 0xff, 0xfa, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf1, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf7, 0xf6, 0xf1, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xfa, 0xf8, 0xf3, 0xff, 0xfc, 0xfa, 0xf4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa2, 0x7c, 0x77, 0xff, 0x4a, 0x00, 0x00, 0xff, 0x56, 0x0d, 0x03, 0xff, 0x57, 0x0f, 0x05, 0xff, 0x57, 0x11, 0x07, 0xff, 0x58, 0x12, 0x09, 0xff, 0x58, 0x13, 0x0a, 0xff, 0x58, 0x13, 0x09, 0xff, 0x56, 0x12, 0x09, 0xff, 0x56, 0x10, 0x07, 0xff, 0x50, 0x08, 0x04, 0xff, 0xbb, 0x61, 0x06, 0xff, 0x74, 0x27, 0x05, 0xff, 0x4f, 0x08, 0x03, 0xff, 0x56, 0x0d, 0x04, 0xff, 0x58, 0x0e, 0x05, 0xff, 0x56, 0x0f, 0x07, 0xff, 0x56, 0x10, 0x08, 0xff, 0x57, 0x10, 0x08, 0xff, 0x58, 0x10, 0x08, 0xff, 0x57, 0x0f, 0x07, 0xff, 0x58, 0x0b, 0x03, 0xff, 0x55, 0x06, 0x00, 0xff, 0xd9, 0x3b, 0x65, 0xff, 0xf1, 0x45, 0x78, 0xff, 0x3c, 0x04, 0x08, 0xff, 0x41, 0x02, 0x00, 0xff, 0x7f, 0x13, 0x13, 0xff, 0xca, 0x2c, 0x34, 0xff, 0x3d, 0x00, 0x00, 0xff, 0x46, 0x09, 0x09, 0xff, 0x3a, 0x00, 0x00, 0xff, 0x7e, 0x4b, 0x48, 0xff, 0xf6, 0xf0, 0xe9, 0xff, 0xfc, 0xfa, 0xf4, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xfe, 0xfc, 0xf6, 0xff, 0xfe, 0xfe, 0xf9, 0xff, + 0xfd, 0xfa, 0xf6, 0xff, 0xfc, 0xfb, 0xf6, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xf9, 0xf7, 0xf2, 0xff, 0xf8, 0xf5, 0xf0, 0xff, 0xf8, 0xf4, 0xef, 0xff, 0xfa, 0xf0, 0xec, 0xff, 0xfa, 0xed, 0xe8, 0xff, 0xfc, 0xe9, 0xe5, 0xff, 0xfd, 0xe4, 0xe0, 0xff, 0xfd, 0xe5, 0xe3, 0xff, 0xfc, 0x91, 0xa1, 0xff, 0xfc, 0x3f, 0x5c, 0xff, 0xfd, 0x49, 0x63, 0xff, 0xfd, 0x4a, 0x66, 0xff, 0xfd, 0x4c, 0x68, 0xff, 0xfd, 0x4d, 0x69, 0xff, 0xfd, 0x4f, 0x6a, 0xff, 0xfd, 0x50, 0x6b, 0xff, 0xfd, 0x50, 0x6b, 0xff, 0xfd, 0x51, 0x6b, 0xff, 0xfd, 0x52, 0x6c, 0xff, 0xfd, 0x52, 0x6c, 0xff, 0xfd, 0x51, 0x6c, 0xff, 0xfd, 0x52, 0x6b, 0xff, 0xfd, 0x53, 0x6b, 0xff, 0xfe, 0x53, 0x6c, 0xff, 0xfd, 0x54, 0x6b, 0xff, 0xfd, 0x53, 0x6b, 0xff, 0xfd, 0x54, 0x6c, 0xff, 0xfd, 0x55, 0x6c, 0xff, 0xfd, 0x56, 0x6c, 0xff, 0xfd, 0x57, 0x6b, 0xff, 0xfd, 0x56, 0x6a, 0xff, 0xfd, 0x57, 0x69, 0xff, 0xfd, 0x58, 0x6a, 0xff, 0xfd, 0x5a, 0x6b, 0xff, 0xfd, 0x5a, 0x6a, 0xff, 0xfd, 0x59, 0x6a, 0xff, 0xfd, 0x5a, 0x6a, 0xff, 0xfc, 0x5c, 0x6a, 0xff, 0xfd, 0x5a, 0x69, 0xff, 0xfd, 0x59, 0x69, 0xff, 0xfd, 0x59, 0x68, 0xff, 0xfd, 0x5a, 0x68, 0xff, 0xfd, 0x5b, 0x67, 0xff, 0xfd, 0x5a, 0x68, 0xff, 0xfd, 0x59, 0x68, 0xff, 0xfd, 0x5a, 0x67, 0xff, 0xfd, 0x5c, 0x67, 0xff, 0xfd, 0x5c, 0x66, 0xff, 0xfd, 0x5b, 0x66, 0xff, 0xfd, 0x5c, 0x66, 0xff, 0xfd, 0x5b, 0x65, 0xff, 0xfd, 0x5a, 0x65, 0xff, 0xfd, 0x59, 0x64, 0xff, 0xfd, 0x57, 0x64, 0xff, 0xfd, 0x57, 0x64, 0xff, 0xfd, 0x58, 0x65, 0xff, 0xfd, 0x58, 0x64, 0xff, 0xfd, 0x57, 0x63, 0xff, 0xfd, 0x50, 0x5e, 0xff, 0xfb, 0x74, 0x80, 0xff, 0xfd, 0xf3, 0xf0, 0xff, 0xfc, 0xf8, 0xf3, 0xff, 0xfa, 0xf7, 0xf2, 0xff, 0xf9, 0xf7, 0xf2, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xfa, 0xf9, 0xf3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc1, 0xa9, 0xa4, 0xff, 0x4a, 0x00, 0x00, 0xff, 0x55, 0x0c, 0x03, 0xff, 0x55, 0x0d, 0x04, 0xff, 0x57, 0x10, 0x06, 0xff, 0x58, 0x12, 0x09, 0xff, 0x58, 0x13, 0x0a, 0xff, 0x59, 0x13, 0x09, 0xff, 0x56, 0x12, 0x08, 0xff, 0x55, 0x10, 0x07, 0xff, 0x51, 0x08, 0x04, 0xff, 0xbb, 0x59, 0x09, 0xff, 0x75, 0x25, 0x07, 0xff, 0x4f, 0x08, 0x03, 0xff, 0x55, 0x0d, 0x04, 0xff, 0x56, 0x0e, 0x06, 0xff, 0x56, 0x10, 0x07, 0xff, 0x56, 0x10, 0x08, 0xff, 0x57, 0x10, 0x08, 0xff, 0x58, 0x10, 0x08, 0xff, 0x58, 0x0f, 0x06, 0xff, 0x55, 0x0a, 0x01, 0xff, 0x6b, 0x0f, 0x13, 0xff, 0xfc, 0x4b, 0x86, 0xff, 0xce, 0x36, 0x61, 0xff, 0x46, 0x01, 0x00, 0xff, 0x59, 0x07, 0x04, 0xff, 0x67, 0x0f, 0x10, 0xff, 0xc6, 0x2c, 0x35, 0xff, 0x39, 0x00, 0x00, 0xff, 0x8c, 0x60, 0x5f, 0xff, 0x52, 0x19, 0x19, 0xff, 0x64, 0x28, 0x27, 0xff, 0xfe, 0xfc, 0xf7, 0xff, 0xff, 0xff, 0xfa, 0xff, 0xfd, 0xfb, 0xf6, 0xff, 0xfd, 0xfb, 0xf6, 0xff, 0xfe, 0xfd, 0xf7, 0xff, + 0xfd, 0xfa, 0xf6, 0xff, 0xfc, 0xfa, 0xf5, 0xff, 0xfa, 0xf9, 0xf3, 0xff, 0xf9, 0xf7, 0xf2, 0xff, 0xf8, 0xf5, 0xf0, 0xff, 0xf6, 0xf3, 0xee, 0xff, 0xf8, 0xf0, 0xec, 0xff, 0xf9, 0xed, 0xe9, 0xff, 0xfc, 0xe9, 0xe5, 0xff, 0xfd, 0xe5, 0xe1, 0xff, 0xfd, 0xe2, 0xde, 0xff, 0xfc, 0xd0, 0xd3, 0xff, 0xfc, 0x54, 0x74, 0xff, 0xfd, 0x40, 0x63, 0xff, 0xfd, 0x48, 0x6a, 0xff, 0xfd, 0x4a, 0x6b, 0xff, 0xfd, 0x4b, 0x6b, 0xff, 0xfd, 0x4c, 0x6c, 0xff, 0xfd, 0x4e, 0x6d, 0xff, 0xfd, 0x4e, 0x6d, 0xff, 0xfd, 0x4e, 0x6e, 0xff, 0xfd, 0x4f, 0x6e, 0xff, 0xfd, 0x50, 0x6f, 0xff, 0xfd, 0x50, 0x6f, 0xff, 0xfd, 0x50, 0x6e, 0xff, 0xfd, 0x51, 0x6d, 0xff, 0xfd, 0x51, 0x6d, 0xff, 0xfd, 0x50, 0x6e, 0xff, 0xfd, 0x51, 0x6e, 0xff, 0xfd, 0x51, 0x6e, 0xff, 0xfd, 0x51, 0x6e, 0xff, 0xfd, 0x52, 0x6e, 0xff, 0xfd, 0x52, 0x6d, 0xff, 0xfd, 0x52, 0x6c, 0xff, 0xfd, 0x53, 0x6c, 0xff, 0xfd, 0x53, 0x6d, 0xff, 0xfd, 0x54, 0x6d, 0xff, 0xfd, 0x54, 0x6d, 0xff, 0xfd, 0x54, 0x6d, 0xff, 0xfd, 0x55, 0x6e, 0xff, 0xfd, 0x57, 0x6d, 0xff, 0xfd, 0x56, 0x6d, 0xff, 0xfd, 0x55, 0x6c, 0xff, 0xfd, 0x55, 0x6d, 0xff, 0xfd, 0x56, 0x6b, 0xff, 0xfd, 0x56, 0x6b, 0xff, 0xfd, 0x57, 0x6b, 0xff, 0xfd, 0x56, 0x69, 0xff, 0xfd, 0x55, 0x69, 0xff, 0xfd, 0x57, 0x69, 0xff, 0xfd, 0x58, 0x69, 0xff, 0xfd, 0x57, 0x69, 0xff, 0xfd, 0x57, 0x68, 0xff, 0xfd, 0x57, 0x68, 0xff, 0xfd, 0x56, 0x68, 0xff, 0xfd, 0x54, 0x68, 0xff, 0xfd, 0x54, 0x68, 0xff, 0xfd, 0x54, 0x67, 0xff, 0xfd, 0x54, 0x66, 0xff, 0xfd, 0x54, 0x65, 0xff, 0xfd, 0x51, 0x63, 0xff, 0xfc, 0x4a, 0x5f, 0xff, 0xfb, 0xc1, 0xc6, 0xff, 0xfc, 0xfc, 0xf7, 0xff, 0xfb, 0xf7, 0xf2, 0xff, 0xf9, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf4, 0xf5, 0xf1, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xff, 0xff, 0xfa, 0xff, 0xe8, 0xdf, 0xdb, 0xff, 0x58, 0x15, 0x10, 0xff, 0x52, 0x07, 0x00, 0xff, 0x54, 0x0c, 0x03, 0xff, 0x56, 0x0e, 0x06, 0xff, 0x56, 0x11, 0x08, 0xff, 0x57, 0x12, 0x09, 0xff, 0x57, 0x12, 0x09, 0xff, 0x56, 0x11, 0x08, 0xff, 0x55, 0x10, 0x07, 0xff, 0x50, 0x09, 0x04, 0xff, 0xbc, 0x55, 0x0d, 0xff, 0x75, 0x23, 0x09, 0xff, 0x4f, 0x08, 0x03, 0xff, 0x54, 0x0c, 0x04, 0xff, 0x55, 0x0e, 0x06, 0xff, 0x57, 0x10, 0x07, 0xff, 0x58, 0x10, 0x07, 0xff, 0x59, 0x10, 0x08, 0xff, 0x59, 0x10, 0x07, 0xff, 0x5a, 0x0d, 0x05, 0xff, 0x4f, 0x07, 0x00, 0xff, 0x97, 0x22, 0x3a, 0xff, 0xff, 0x55, 0x9a, 0xff, 0xa0, 0x27, 0x45, 0xff, 0x37, 0x00, 0x00, 0xff, 0x31, 0x00, 0x00, 0xff, 0x56, 0x0b, 0x0d, 0xff, 0xcb, 0x2f, 0x3a, 0xff, 0x38, 0x00, 0x00, 0xff, 0x9f, 0x79, 0x76, 0xff, 0x67, 0x34, 0x34, 0xff, 0x4d, 0x0c, 0x0b, 0xff, 0xee, 0xe5, 0xe1, 0xff, 0xff, 0xff, 0xfa, 0xff, 0xfc, 0xfc, 0xf6, 0xff, 0xfc, 0xfc, 0xf6, 0xff, 0xfe, 0xfd, 0xf6, 0xff, + 0xfd, 0xfa, 0xf5, 0xff, 0xfc, 0xf9, 0xf5, 0xff, 0xfa, 0xf9, 0xf3, 0xff, 0xf9, 0xf7, 0xf2, 0xff, 0xf8, 0xf5, 0xf0, 0xff, 0xf7, 0xf3, 0xee, 0xff, 0xf8, 0xf0, 0xec, 0xff, 0xf9, 0xed, 0xe9, 0xff, 0xfb, 0xe9, 0xe6, 0xff, 0xfd, 0xe5, 0xe2, 0xff, 0xfd, 0xe0, 0xde, 0xff, 0xfd, 0xe2, 0xe0, 0xff, 0xfc, 0xaa, 0xb8, 0xff, 0xfc, 0x3f, 0x69, 0xff, 0xfd, 0x42, 0x6c, 0xff, 0xfd, 0x46, 0x6e, 0xff, 0xfd, 0x47, 0x6f, 0xff, 0xfd, 0x47, 0x70, 0xff, 0xfd, 0x49, 0x71, 0xff, 0xfd, 0x49, 0x70, 0xff, 0xfd, 0x49, 0x71, 0xff, 0xfd, 0x4b, 0x71, 0xff, 0xfd, 0x4c, 0x73, 0xff, 0xfd, 0x4d, 0x72, 0xff, 0xfd, 0x4d, 0x71, 0xff, 0xfd, 0x4e, 0x70, 0xff, 0xfd, 0x4e, 0x70, 0xff, 0xfd, 0x4e, 0x71, 0xff, 0xfd, 0x4e, 0x72, 0xff, 0xfd, 0x4e, 0x73, 0xff, 0xfd, 0x4f, 0x71, 0xff, 0xfd, 0x4f, 0x6f, 0xff, 0xfd, 0x4e, 0x6e, 0xff, 0xfc, 0x4e, 0x6d, 0xff, 0xfd, 0x4f, 0x6e, 0xff, 0xfd, 0x51, 0x6e, 0xff, 0xfd, 0x50, 0x6f, 0xff, 0xfd, 0x50, 0x6f, 0xff, 0xfd, 0x50, 0x6f, 0xff, 0xfd, 0x51, 0x70, 0xff, 0xfd, 0x52, 0x71, 0xff, 0xfd, 0x53, 0x71, 0xff, 0xfd, 0x52, 0x70, 0xff, 0xfd, 0x51, 0x6f, 0xff, 0xfd, 0x51, 0x6e, 0xff, 0xfd, 0x52, 0x6e, 0xff, 0xfd, 0x53, 0x6f, 0xff, 0xfd, 0x53, 0x6d, 0xff, 0xfd, 0x53, 0x6c, 0xff, 0xfd, 0x53, 0x6b, 0xff, 0xfd, 0x53, 0x6a, 0xff, 0xfd, 0x53, 0x6a, 0xff, 0xfd, 0x53, 0x6a, 0xff, 0xfd, 0x53, 0x6a, 0xff, 0xfd, 0x52, 0x6b, 0xff, 0xfd, 0x50, 0x6a, 0xff, 0xfd, 0x4f, 0x6a, 0xff, 0xfd, 0x4f, 0x69, 0xff, 0xfd, 0x4f, 0x67, 0xff, 0xfd, 0x4f, 0x65, 0xff, 0xfd, 0x44, 0x5e, 0xff, 0xfb, 0x8d, 0x9b, 0xff, 0xfc, 0xf7, 0xf2, 0xff, 0xfb, 0xf5, 0xf1, 0xff, 0xfa, 0xf6, 0xf1, 0xff, 0xfa, 0xf7, 0xf2, 0xff, 0xf9, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf4, 0xf4, 0xf2, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf7, 0xf6, 0xf3, 0xff, 0xf9, 0xf7, 0xf3, 0xff, 0xfd, 0xfa, 0xf5, 0xff, 0xff, 0xff, 0xfc, 0xff, 0x7e, 0x4c, 0x49, 0xff, 0x4c, 0x00, 0x00, 0xff, 0x54, 0x0b, 0x03, 0xff, 0x54, 0x0e, 0x05, 0xff, 0x56, 0x10, 0x07, 0xff, 0x57, 0x11, 0x09, 0xff, 0x56, 0x11, 0x09, 0xff, 0x55, 0x11, 0x08, 0xff, 0x56, 0x0f, 0x07, 0xff, 0x50, 0x09, 0x04, 0xff, 0xbc, 0x4f, 0x0f, 0xff, 0x75, 0x20, 0x09, 0xff, 0x4f, 0x09, 0x03, 0xff, 0x54, 0x0c, 0x04, 0xff, 0x56, 0x0d, 0x05, 0xff, 0x57, 0x0e, 0x05, 0xff, 0x57, 0x0f, 0x06, 0xff, 0x58, 0x0f, 0x06, 0xff, 0x58, 0x0d, 0x03, 0xff, 0x50, 0x07, 0x00, 0xff, 0x61, 0x0b, 0x0c, 0xff, 0xe4, 0x43, 0x7d, 0xff, 0xff, 0x51, 0x98, 0xff, 0x5a, 0x10, 0x1d, 0xff, 0x2c, 0x00, 0x00, 0xff, 0x2a, 0x00, 0x00, 0xff, 0x71, 0x13, 0x19, 0xff, 0xc8, 0x30, 0x3c, 0xff, 0x2f, 0x00, 0x00, 0xff, 0xa5, 0x84, 0x81, 0xff, 0x81, 0x55, 0x54, 0xff, 0x3c, 0x00, 0x00, 0xff, 0xd8, 0xc8, 0xc5, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xfc, 0xfb, 0xf6, 0xff, 0xfc, 0xfc, 0xf6, 0xff, 0xfe, 0xfc, 0xf6, 0xff, + 0xfd, 0xfa, 0xf5, 0xff, 0xfc, 0xf9, 0xf5, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xf9, 0xf8, 0xf2, 0xff, 0xf8, 0xf6, 0xf1, 0xff, 0xf7, 0xf4, 0xef, 0xff, 0xf8, 0xf1, 0xed, 0xff, 0xf8, 0xee, 0xea, 0xff, 0xfa, 0xea, 0xe7, 0xff, 0xfc, 0xe5, 0xe3, 0xff, 0xfd, 0xe1, 0xde, 0xff, 0xfd, 0xdc, 0xdb, 0xff, 0xfd, 0xde, 0xdd, 0xff, 0xfb, 0x91, 0xa9, 0xff, 0xfc, 0x3a, 0x6d, 0xff, 0xfd, 0x3d, 0x6f, 0xff, 0xfd, 0x42, 0x72, 0xff, 0xfd, 0x43, 0x72, 0xff, 0xfd, 0x44, 0x73, 0xff, 0xfd, 0x43, 0x73, 0xff, 0xfd, 0x45, 0x72, 0xff, 0xfd, 0x47, 0x74, 0xff, 0xfd, 0x48, 0x76, 0xff, 0xfd, 0x49, 0x76, 0xff, 0xfd, 0x49, 0x75, 0xff, 0xfd, 0x49, 0x74, 0xff, 0xfd, 0x49, 0x74, 0xff, 0xfd, 0x49, 0x75, 0xff, 0xfd, 0x49, 0x75, 0xff, 0xfd, 0x4b, 0x74, 0xff, 0xfd, 0x4b, 0x74, 0xff, 0xfd, 0x4b, 0x72, 0xff, 0xfd, 0x4a, 0x72, 0xff, 0xfd, 0x4a, 0x71, 0xff, 0xfd, 0x4b, 0x71, 0xff, 0xfd, 0x4d, 0x70, 0xff, 0xfd, 0x4e, 0x71, 0xff, 0xfd, 0x4e, 0x70, 0xff, 0xfd, 0x4d, 0x71, 0xff, 0xfd, 0x4d, 0x72, 0xff, 0xfd, 0x4e, 0x74, 0xff, 0xfd, 0x4e, 0x73, 0xff, 0xfe, 0x50, 0x72, 0xff, 0xfd, 0x51, 0x72, 0xff, 0xfd, 0x51, 0x70, 0xff, 0xfd, 0x50, 0x71, 0xff, 0xfe, 0x51, 0x71, 0xff, 0xfd, 0x51, 0x70, 0xff, 0xfd, 0x51, 0x6e, 0xff, 0xfd, 0x50, 0x6d, 0xff, 0xfd, 0x4f, 0x6c, 0xff, 0xfd, 0x4f, 0x6c, 0xff, 0xfd, 0x4f, 0x6c, 0xff, 0xfd, 0x4f, 0x6c, 0xff, 0xfd, 0x4d, 0x6b, 0xff, 0xfd, 0x4c, 0x6a, 0xff, 0xfd, 0x4a, 0x6a, 0xff, 0xfd, 0x4a, 0x69, 0xff, 0xfd, 0x47, 0x68, 0xff, 0xfd, 0x3e, 0x61, 0xff, 0xfb, 0x7e, 0x92, 0xff, 0xfc, 0xee, 0xea, 0xff, 0xfc, 0xf5, 0xf0, 0xff, 0xfa, 0xf5, 0xf0, 0xff, 0xfa, 0xf6, 0xf1, 0xff, 0xfa, 0xf7, 0xf2, 0xff, 0xf9, 0xf7, 0xf2, 0xff, 0xf6, 0xf7, 0xf4, 0xff, 0xf5, 0xf5, 0xf3, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf5, 0xf6, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xfb, 0xf8, 0xf3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb7, 0x9a, 0x97, 0xff, 0x49, 0x00, 0x00, 0xff, 0x54, 0x0b, 0x02, 0xff, 0x55, 0x0d, 0x04, 0xff, 0x54, 0x0f, 0x07, 0xff, 0x55, 0x11, 0x08, 0xff, 0x55, 0x11, 0x08, 0xff, 0x54, 0x10, 0x07, 0xff, 0x55, 0x0e, 0x06, 0xff, 0x50, 0x09, 0x03, 0xff, 0xbc, 0x49, 0x13, 0xff, 0x76, 0x1e, 0x09, 0xff, 0x50, 0x08, 0x02, 0xff, 0x56, 0x0b, 0x03, 0xff, 0x56, 0x0c, 0x04, 0xff, 0x56, 0x0c, 0x04, 0xff, 0x54, 0x0b, 0x01, 0xff, 0x4e, 0x08, 0x00, 0xff, 0x55, 0x08, 0x02, 0xff, 0x89, 0x1b, 0x2e, 0xff, 0xe3, 0x41, 0x7d, 0xff, 0xff, 0x55, 0xa4, 0xff, 0xaa, 0x2f, 0x5a, 0xff, 0x2f, 0x00, 0x00, 0xff, 0x32, 0x00, 0x00, 0xff, 0x66, 0x0f, 0x18, 0xff, 0xd9, 0x35, 0x4d, 0xff, 0x62, 0x0f, 0x14, 0xff, 0x38, 0x00, 0x00, 0xff, 0xb5, 0x8d, 0x89, 0xff, 0x9f, 0x7c, 0x7b, 0xff, 0x31, 0x00, 0x00, 0xff, 0xb9, 0xa1, 0x9e, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xfb, 0xf6, 0xff, 0xfc, 0xfc, 0xf6, 0xff, 0xfe, 0xfc, 0xf6, 0xff, + 0xfd, 0xfa, 0xf5, 0xff, 0xfc, 0xf9, 0xf5, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf8, 0xf5, 0xf1, 0xff, 0xf7, 0xf4, 0xef, 0xff, 0xf8, 0xf2, 0xed, 0xff, 0xf8, 0xef, 0xeb, 0xff, 0xf8, 0xeb, 0xe8, 0xff, 0xfb, 0xe6, 0xe4, 0xff, 0xfd, 0xe1, 0xde, 0xff, 0xfd, 0xdc, 0xdb, 0xff, 0xfd, 0xd9, 0xd9, 0xff, 0xfc, 0xda, 0xda, 0xff, 0xfb, 0x96, 0xaf, 0xff, 0xfc, 0x3f, 0x75, 0xff, 0xfd, 0x35, 0x6f, 0xff, 0xfd, 0x3d, 0x73, 0xff, 0xfc, 0x40, 0x74, 0xff, 0xfd, 0x40, 0x74, 0xff, 0xfd, 0x41, 0x74, 0xff, 0xfd, 0x43, 0x75, 0xff, 0xfd, 0x45, 0x76, 0xff, 0xfd, 0x45, 0x76, 0xff, 0xfd, 0x44, 0x76, 0xff, 0xfd, 0x45, 0x76, 0xff, 0xfd, 0x45, 0x76, 0xff, 0xfd, 0x45, 0x77, 0xff, 0xfd, 0x45, 0x76, 0xff, 0xfd, 0x46, 0x75, 0xff, 0xfd, 0x47, 0x75, 0xff, 0xfd, 0x46, 0x74, 0xff, 0xfd, 0x45, 0x73, 0xff, 0xfd, 0x45, 0x73, 0xff, 0xfd, 0x46, 0x73, 0xff, 0xfd, 0x46, 0x72, 0xff, 0xfd, 0x48, 0x72, 0xff, 0xfd, 0x49, 0x71, 0xff, 0xfd, 0x47, 0x72, 0xff, 0xfd, 0x47, 0x73, 0xff, 0xfd, 0x48, 0x74, 0xff, 0xfd, 0x49, 0x74, 0xff, 0xfd, 0x4b, 0x74, 0xff, 0xfd, 0x4d, 0x73, 0xff, 0xfd, 0x4d, 0x72, 0xff, 0xfd, 0x4d, 0x72, 0xff, 0xfd, 0x4e, 0x72, 0xff, 0xfd, 0x4e, 0x72, 0xff, 0xfd, 0x4d, 0x71, 0xff, 0xfd, 0x4e, 0x70, 0xff, 0xfd, 0x4d, 0x6f, 0xff, 0xfd, 0x4c, 0x6f, 0xff, 0xfd, 0x4b, 0x6f, 0xff, 0xfd, 0x4a, 0x6e, 0xff, 0xfd, 0x49, 0x6c, 0xff, 0xfd, 0x48, 0x6b, 0xff, 0xfd, 0x46, 0x6b, 0xff, 0xfd, 0x3f, 0x67, 0xff, 0xfc, 0x43, 0x6a, 0xff, 0xfb, 0x8e, 0xa1, 0xff, 0xfc, 0xeb, 0xe8, 0xff, 0xfc, 0xf5, 0xef, 0xff, 0xfb, 0xf4, 0xee, 0xff, 0xfa, 0xf6, 0xf0, 0xff, 0xfa, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf4, 0xf5, 0xf3, 0xff, 0xf5, 0xf5, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xfc, 0xf8, 0xf4, 0xff, 0xff, 0xff, 0xfb, 0xff, 0xea, 0xe1, 0xdd, 0xff, 0x59, 0x15, 0x0f, 0xff, 0x52, 0x07, 0x00, 0xff, 0x55, 0x0c, 0x03, 0xff, 0x56, 0x0e, 0x05, 0xff, 0x54, 0x10, 0x07, 0xff, 0x53, 0x10, 0x07, 0xff, 0x53, 0x0f, 0x07, 0xff, 0x55, 0x0d, 0x05, 0xff, 0x50, 0x08, 0x03, 0xff, 0xba, 0x41, 0x15, 0xff, 0x75, 0x1b, 0x0a, 0xff, 0x50, 0x08, 0x01, 0xff, 0x55, 0x0a, 0x02, 0xff, 0x51, 0x09, 0x00, 0xff, 0x4b, 0x06, 0x00, 0xff, 0x5a, 0x0a, 0x06, 0xff, 0x93, 0x1d, 0x33, 0xff, 0xdf, 0x3c, 0x72, 0xff, 0xff, 0x50, 0x9a, 0xff, 0xf5, 0x48, 0x8d, 0xff, 0x97, 0x26, 0x4e, 0xff, 0x44, 0x06, 0x0d, 0xff, 0x34, 0x00, 0x00, 0xff, 0x33, 0x00, 0x00, 0xff, 0x8b, 0x1e, 0x2e, 0xff, 0x66, 0x0f, 0x1c, 0xff, 0x42, 0x03, 0x00, 0xff, 0x4d, 0x00, 0x00, 0xff, 0xa9, 0x89, 0x87, 0xff, 0xc0, 0xa7, 0xa3, 0xff, 0x2f, 0x00, 0x00, 0xff, 0x96, 0x72, 0x71, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfb, 0xf6, 0xff, 0xfc, 0xfc, 0xf6, 0xff, 0xfd, 0xfc, 0xf7, 0xff, + 0xfd, 0xfa, 0xf5, 0xff, 0xfc, 0xfb, 0xf5, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf8, 0xf6, 0xf1, 0xff, 0xf8, 0xf4, 0xef, 0xff, 0xf7, 0xf2, 0xed, 0xff, 0xf7, 0xf0, 0xec, 0xff, 0xf7, 0xec, 0xe9, 0xff, 0xf9, 0xe7, 0xe5, 0xff, 0xfb, 0xe2, 0xdf, 0xff, 0xfd, 0xdc, 0xdb, 0xff, 0xfd, 0xd7, 0xd8, 0xff, 0xfc, 0xd5, 0xd5, 0xff, 0xfd, 0xd7, 0xd7, 0xff, 0xfc, 0xaf, 0xbf, 0xff, 0xfc, 0x5e, 0x8e, 0xff, 0xfd, 0x35, 0x74, 0xff, 0xfd, 0x32, 0x71, 0xff, 0xfd, 0x37, 0x74, 0xff, 0xfd, 0x39, 0x76, 0xff, 0xfd, 0x3b, 0x77, 0xff, 0xfd, 0x3d, 0x77, 0xff, 0xfd, 0x3d, 0x78, 0xff, 0xfd, 0x3c, 0x78, 0xff, 0xfd, 0x3d, 0x77, 0xff, 0xfd, 0x3e, 0x76, 0xff, 0xfd, 0x3d, 0x77, 0xff, 0xfd, 0x3e, 0x77, 0xff, 0xfd, 0x3f, 0x76, 0xff, 0xfd, 0x41, 0x75, 0xff, 0xfd, 0x40, 0x74, 0xff, 0xfd, 0x40, 0x73, 0xff, 0xfd, 0x40, 0x74, 0xff, 0xfd, 0x40, 0x74, 0xff, 0xfd, 0x40, 0x72, 0xff, 0xfd, 0x40, 0x73, 0xff, 0xfd, 0x41, 0x73, 0xff, 0xfd, 0x41, 0x72, 0xff, 0xfd, 0x42, 0x72, 0xff, 0xfd, 0x42, 0x73, 0xff, 0xfd, 0x43, 0x75, 0xff, 0xfd, 0x45, 0x75, 0xff, 0xfd, 0x46, 0x75, 0xff, 0xfd, 0x46, 0x74, 0xff, 0xfd, 0x47, 0x74, 0xff, 0xfd, 0x47, 0x73, 0xff, 0xfd, 0x48, 0x73, 0xff, 0xfd, 0x49, 0x72, 0xff, 0xfd, 0x49, 0x72, 0xff, 0xfd, 0x49, 0x73, 0xff, 0xfd, 0x49, 0x72, 0xff, 0xfd, 0x48, 0x72, 0xff, 0xfd, 0x46, 0x71, 0xff, 0xfd, 0x42, 0x6e, 0xff, 0xfc, 0x39, 0x68, 0xff, 0xfc, 0x41, 0x6d, 0xff, 0xfc, 0x6d, 0x8c, 0xff, 0xfc, 0xbd, 0xc5, 0xff, 0xfd, 0xf1, 0xea, 0xff, 0xfd, 0xf2, 0xeb, 0xff, 0xfc, 0xf2, 0xed, 0xff, 0xfa, 0xf4, 0xef, 0xff, 0xf9, 0xf5, 0xf0, 0xff, 0xf9, 0xf6, 0xf1, 0xff, 0xf7, 0xf5, 0xf1, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf4, 0xf5, 0xf1, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xfc, 0xf9, 0xf4, 0xff, 0xfe, 0xfc, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0x81, 0x4e, 0x4a, 0xff, 0x4b, 0x00, 0x00, 0xff, 0x55, 0x0c, 0x03, 0xff, 0x55, 0x0d, 0x04, 0xff, 0x54, 0x0f, 0x06, 0xff, 0x53, 0x0f, 0x07, 0xff, 0x54, 0x0e, 0x07, 0xff, 0x54, 0x0d, 0x05, 0xff, 0x50, 0x08, 0x02, 0xff, 0xb8, 0x36, 0x15, 0xff, 0x72, 0x17, 0x09, 0xff, 0x4b, 0x05, 0x00, 0xff, 0x4a, 0x05, 0x00, 0xff, 0x5e, 0x0b, 0x0a, 0xff, 0xa1, 0x21, 0x3d, 0xff, 0xec, 0x3e, 0x79, 0xff, 0xff, 0x4b, 0x95, 0xff, 0xe3, 0x40, 0x7d, 0xff, 0x8f, 0x22, 0x42, 0xff, 0x3d, 0x06, 0x0d, 0xff, 0x36, 0x02, 0x07, 0xff, 0xb4, 0x1f, 0x4a, 0xff, 0x38, 0x00, 0x00, 0xff, 0x36, 0x00, 0x00, 0xff, 0x37, 0x00, 0x00, 0xff, 0x7c, 0x29, 0x01, 0xff, 0x75, 0x23, 0x04, 0xff, 0x33, 0x00, 0x00, 0xff, 0xab, 0x88, 0x85, 0xff, 0xdd, 0xcf, 0xca, 0xff, 0x37, 0x00, 0x00, 0xff, 0x78, 0x48, 0x47, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xf8, 0xff, 0xfd, 0xfc, 0xf7, 0xff, 0xfe, 0xfd, 0xf8, 0xff, + 0xfd, 0xfa, 0xf5, 0xff, 0xfb, 0xfb, 0xf5, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf9, 0xf7, 0xf1, 0xff, 0xf8, 0xf5, 0xf0, 0xff, 0xf7, 0xf2, 0xee, 0xff, 0xf7, 0xf0, 0xec, 0xff, 0xf8, 0xed, 0xea, 0xff, 0xf8, 0xe9, 0xe6, 0xff, 0xf9, 0xe4, 0xe2, 0xff, 0xfb, 0xde, 0xdd, 0xff, 0xfc, 0xd9, 0xd9, 0xff, 0xfc, 0xd5, 0xd5, 0xff, 0xfd, 0xd2, 0xd3, 0xff, 0xfc, 0xd4, 0xd4, 0xff, 0xfd, 0xce, 0xd2, 0xff, 0xfd, 0xa4, 0xba, 0xff, 0xfc, 0x6e, 0x9a, 0xff, 0xfc, 0x4b, 0x85, 0xff, 0xfc, 0x3b, 0x7c, 0xff, 0xfd, 0x35, 0x78, 0xff, 0xfd, 0x32, 0x77, 0xff, 0xfd, 0x32, 0x78, 0xff, 0xfc, 0x35, 0x78, 0xff, 0xfc, 0x36, 0x77, 0xff, 0xfd, 0x35, 0x76, 0xff, 0xfd, 0x34, 0x75, 0xff, 0xfd, 0x34, 0x75, 0xff, 0xfd, 0x36, 0x76, 0xff, 0xfc, 0x37, 0x74, 0xff, 0xfd, 0x37, 0x72, 0xff, 0xfd, 0x37, 0x73, 0xff, 0xfc, 0x39, 0x75, 0xff, 0xfc, 0x38, 0x73, 0xff, 0xfd, 0x37, 0x72, 0xff, 0xfd, 0x36, 0x73, 0xff, 0xfd, 0x37, 0x71, 0xff, 0xfc, 0x38, 0x71, 0xff, 0xfc, 0x38, 0x71, 0xff, 0xfd, 0x39, 0x73, 0xff, 0xfd, 0x3a, 0x75, 0xff, 0xfd, 0x3c, 0x76, 0xff, 0xfd, 0x3e, 0x75, 0xff, 0xfc, 0x3d, 0x74, 0xff, 0xfd, 0x3a, 0x72, 0xff, 0xfd, 0x3f, 0x73, 0xff, 0xfd, 0x43, 0x72, 0xff, 0xfd, 0x44, 0x72, 0xff, 0xfd, 0x44, 0x73, 0xff, 0xfd, 0x45, 0x74, 0xff, 0xfd, 0x46, 0x73, 0xff, 0xfd, 0x45, 0x73, 0xff, 0xfd, 0x42, 0x70, 0xff, 0xfc, 0x43, 0x72, 0xff, 0xfc, 0x8a, 0xa2, 0xff, 0xfc, 0xc2, 0xc7, 0xff, 0xfd, 0xe6, 0xe1, 0xff, 0xfd, 0xee, 0xe7, 0xff, 0xfd, 0xed, 0xe7, 0xff, 0xfc, 0xf1, 0xea, 0xff, 0xfb, 0xf3, 0xed, 0xff, 0xf9, 0xf4, 0xef, 0xff, 0xf8, 0xf6, 0xf1, 0xff, 0xf8, 0xf6, 0xf1, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf5, 0xf4, 0xf0, 0xff, 0xf5, 0xf4, 0xf0, 0xff, 0xf5, 0xf5, 0xf0, 0xff, 0xf6, 0xf5, 0xf0, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xfd, 0xfa, 0xf5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb4, 0x96, 0x92, 0xff, 0x49, 0x00, 0x00, 0xff, 0x55, 0x0a, 0x02, 0xff, 0x55, 0x0c, 0x03, 0xff, 0x54, 0x0d, 0x05, 0xff, 0x54, 0x0e, 0x06, 0xff, 0x54, 0x0d, 0x06, 0xff, 0x54, 0x0b, 0x04, 0xff, 0x4e, 0x06, 0x01, 0xff, 0xb4, 0x30, 0x13, 0xff, 0x68, 0x13, 0x06, 0xff, 0x62, 0x0a, 0x0d, 0xff, 0xad, 0x22, 0x3f, 0xff, 0xf6, 0x3e, 0x77, 0xff, 0xff, 0x45, 0x88, 0xff, 0xcb, 0x33, 0x66, 0xff, 0x72, 0x16, 0x2d, 0xff, 0x33, 0x02, 0x04, 0xff, 0x22, 0x00, 0x00, 0xff, 0x24, 0x00, 0x00, 0xff, 0x46, 0x06, 0x0f, 0xff, 0xbd, 0x1e, 0x4f, 0xff, 0x3c, 0x03, 0x01, 0xff, 0x50, 0x08, 0x02, 0xff, 0x57, 0x03, 0x02, 0xff, 0xcd, 0x82, 0x03, 0xff, 0x85, 0x48, 0x01, 0xff, 0x2f, 0x00, 0x00, 0xff, 0xa6, 0x84, 0x81, 0xff, 0xf4, 0xef, 0xea, 0xff, 0x46, 0x0b, 0x0b, 0xff, 0x5b, 0x1f, 0x1f, 0xff, 0xf8, 0xf4, 0xf0, 0xff, 0xff, 0xff, 0xfa, 0xff, 0xfe, 0xfe, 0xf8, 0xff, 0xfe, 0xff, 0xf8, 0xff, + 0xfd, 0xfb, 0xf6, 0xff, 0xfc, 0xfb, 0xf5, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf5, 0xf0, 0xff, 0xf8, 0xf3, 0xef, 0xff, 0xf7, 0xf1, 0xed, 0xff, 0xf7, 0xef, 0xec, 0xff, 0xf7, 0xeb, 0xe8, 0xff, 0xf9, 0xe7, 0xe4, 0xff, 0xfa, 0xe1, 0xdf, 0xff, 0xfb, 0xdc, 0xdb, 0xff, 0xfc, 0xd8, 0xd8, 0xff, 0xfd, 0xd6, 0xd6, 0xff, 0xfc, 0xd3, 0xd4, 0xff, 0xfd, 0xd1, 0xd3, 0xff, 0xfd, 0xd4, 0xd4, 0xff, 0xfd, 0xd3, 0xd3, 0xff, 0xfc, 0xc7, 0xcd, 0xff, 0xfc, 0xb6, 0xc3, 0xff, 0xfd, 0xa9, 0xbb, 0xff, 0xfc, 0xa3, 0xb7, 0xff, 0xfc, 0xa3, 0xb8, 0xff, 0xfc, 0xa5, 0xb9, 0xff, 0xfc, 0xa5, 0xb8, 0xff, 0xfc, 0xa7, 0xb9, 0xff, 0xfc, 0xa8, 0xbb, 0xff, 0xfd, 0xa8, 0xbb, 0xff, 0xfd, 0xa9, 0xbc, 0xff, 0xfc, 0xa9, 0xbc, 0xff, 0xfc, 0xa9, 0xba, 0xff, 0xfc, 0xaa, 0xbb, 0xff, 0xfc, 0xab, 0xbb, 0xff, 0xfd, 0xaa, 0xbb, 0xff, 0xfd, 0xa8, 0xb9, 0xff, 0xfd, 0xa7, 0xb8, 0xff, 0xfd, 0xa7, 0xb8, 0xff, 0xfd, 0xa8, 0xb9, 0xff, 0xfc, 0xa8, 0xb9, 0xff, 0xfc, 0xa8, 0xb9, 0xff, 0xfc, 0xa9, 0xba, 0xff, 0xfc, 0xaa, 0xba, 0xff, 0xfc, 0xad, 0xbb, 0xff, 0xfc, 0xb2, 0xbe, 0xff, 0xfc, 0x84, 0xa4, 0xff, 0xfc, 0x38, 0x72, 0xff, 0xfd, 0x39, 0x6e, 0xff, 0xfd, 0x3f, 0x72, 0xff, 0xfd, 0x40, 0x73, 0xff, 0xfd, 0x42, 0x74, 0xff, 0xfd, 0x42, 0x73, 0xff, 0xfd, 0x41, 0x73, 0xff, 0xfd, 0x3e, 0x71, 0xff, 0xfc, 0x44, 0x74, 0xff, 0xfc, 0xd6, 0xd5, 0xff, 0xfd, 0xed, 0xe5, 0xff, 0xfd, 0xe8, 0xe3, 0xff, 0xfd, 0xeb, 0xe5, 0xff, 0xfc, 0xef, 0xe9, 0xff, 0xfa, 0xf2, 0xec, 0xff, 0xf8, 0xf4, 0xee, 0xff, 0xf9, 0xf5, 0xf0, 0xff, 0xf8, 0xf6, 0xf1, 0xff, 0xf8, 0xf6, 0xf1, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf5, 0xf4, 0xf1, 0xff, 0xf3, 0xf4, 0xf0, 0xff, 0xf5, 0xf4, 0xef, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xfc, 0xfa, 0xf5, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xdf, 0xd2, 0xce, 0xff, 0x4f, 0x0a, 0x05, 0xff, 0x51, 0x06, 0x00, 0xff, 0x53, 0x0a, 0x02, 0xff, 0x54, 0x0b, 0x03, 0xff, 0x53, 0x0b, 0x03, 0xff, 0x51, 0x0a, 0x02, 0xff, 0x4b, 0x06, 0x00, 0xff, 0x49, 0x02, 0x00, 0xff, 0xba, 0x28, 0x25, 0xff, 0xd3, 0x2b, 0x4a, 0xff, 0xf4, 0x2d, 0x65, 0xff, 0xf3, 0x30, 0x69, 0xff, 0xad, 0x26, 0x4b, 0xff, 0x56, 0x0d, 0x1a, 0xff, 0x2a, 0x00, 0x00, 0xff, 0x25, 0x00, 0x00, 0xff, 0x2e, 0x00, 0x00, 0xff, 0x2f, 0x00, 0x00, 0xff, 0x28, 0x00, 0x00, 0xff, 0x48, 0x06, 0x10, 0xff, 0xc7, 0x21, 0x50, 0xff, 0x60, 0x0d, 0x06, 0xff, 0x50, 0x08, 0x02, 0xff, 0x3e, 0x00, 0x00, 0xff, 0xbe, 0x83, 0x02, 0xff, 0x85, 0x4a, 0x01, 0xff, 0x2e, 0x00, 0x00, 0xff, 0xa2, 0x80, 0x7c, 0xff, 0xff, 0xff, 0xff, 0xff, 0x61, 0x2d, 0x2c, 0xff, 0x43, 0x00, 0x00, 0xff, 0xe3, 0xd5, 0xd0, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xfe, 0xfd, 0xf7, 0xff, 0xfe, 0xff, 0xf8, 0xff, + 0xfe, 0xfb, 0xf6, 0xff, 0xfd, 0xfa, 0xf5, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf6, 0xf1, 0xff, 0xf8, 0xf4, 0xf0, 0xff, 0xf7, 0xf2, 0xee, 0xff, 0xf8, 0xf0, 0xec, 0xff, 0xf7, 0xec, 0xe9, 0xff, 0xf8, 0xe9, 0xe5, 0xff, 0xf9, 0xe4, 0xe2, 0xff, 0xfa, 0xe0, 0xde, 0xff, 0xfb, 0xdc, 0xdc, 0xff, 0xfc, 0xd9, 0xd9, 0xff, 0xfd, 0xd7, 0xd7, 0xff, 0xfc, 0xd3, 0xd5, 0xff, 0xfc, 0xd1, 0xd3, 0xff, 0xfd, 0xd0, 0xd2, 0xff, 0xfd, 0xcf, 0xd1, 0xff, 0xfd, 0xd0, 0xd1, 0xff, 0xfd, 0xd2, 0xd0, 0xff, 0xfd, 0xd3, 0xd0, 0xff, 0xfd, 0xd3, 0xd0, 0xff, 0xfe, 0xd4, 0xd1, 0xff, 0xfe, 0xd4, 0xd2, 0xff, 0xfd, 0xd4, 0xd3, 0xff, 0xfd, 0xd4, 0xd3, 0xff, 0xfd, 0xd4, 0xd3, 0xff, 0xfd, 0xd4, 0xd3, 0xff, 0xfd, 0xd3, 0xd3, 0xff, 0xfd, 0xd4, 0xd3, 0xff, 0xfd, 0xd4, 0xd3, 0xff, 0xfd, 0xd4, 0xd3, 0xff, 0xfd, 0xd4, 0xd3, 0xff, 0xfd, 0xd4, 0xd3, 0xff, 0xfd, 0xd4, 0xd2, 0xff, 0xfd, 0xd4, 0xd3, 0xff, 0xfd, 0xd5, 0xd3, 0xff, 0xfd, 0xd5, 0xd3, 0xff, 0xfd, 0xd5, 0xd3, 0xff, 0xfd, 0xd5, 0xd2, 0xff, 0xfd, 0xd5, 0xd2, 0xff, 0xfd, 0xd6, 0xd3, 0xff, 0xfe, 0xd8, 0xd5, 0xff, 0xfd, 0xdb, 0xd7, 0xff, 0xfc, 0xa4, 0xb6, 0xff, 0xfc, 0x44, 0x78, 0xff, 0xfd, 0x33, 0x6b, 0xff, 0xfd, 0x3b, 0x74, 0xff, 0xfd, 0x3c, 0x76, 0xff, 0xfd, 0x3c, 0x75, 0xff, 0xfd, 0x3d, 0x73, 0xff, 0xfd, 0x3c, 0x71, 0xff, 0xfd, 0x33, 0x6a, 0xff, 0xfc, 0xb3, 0xc0, 0xff, 0xfd, 0xef, 0xe8, 0xff, 0xfd, 0xea, 0xe5, 0xff, 0xfc, 0xed, 0xe8, 0xff, 0xfa, 0xf0, 0xeb, 0xff, 0xf8, 0xf2, 0xed, 0xff, 0xf8, 0xf3, 0xef, 0xff, 0xf8, 0xf5, 0xf0, 0xff, 0xf7, 0xf5, 0xf1, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf4, 0xf4, 0xf1, 0xff, 0xf4, 0xf4, 0xef, 0xff, 0xf3, 0xf4, 0xf1, 0xff, 0xf4, 0xf5, 0xf3, 0xff, 0xf4, 0xf5, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xfb, 0xfb, 0xf5, 0xff, 0xff, 0xfe, 0xf9, 0xff, 0xf9, 0xf7, 0xf2, 0xff, 0x65, 0x27, 0x25, 0xff, 0x41, 0x00, 0x00, 0xff, 0x4b, 0x07, 0x00, 0xff, 0x4b, 0x06, 0x00, 0xff, 0x48, 0x05, 0x00, 0xff, 0x54, 0x08, 0x03, 0xff, 0x88, 0x12, 0x1f, 0xff, 0xcc, 0x1f, 0x46, 0xff, 0xf4, 0x26, 0x5e, 0xff, 0xec, 0x35, 0x67, 0xff, 0xd0, 0x5f, 0x79, 0xff, 0xd2, 0xab, 0xae, 0xff, 0x51, 0x3c, 0x39, 0xff, 0x19, 0x00, 0x00, 0xff, 0x2d, 0x00, 0x00, 0xff, 0x30, 0x01, 0x00, 0xff, 0x31, 0x01, 0x00, 0xff, 0x2c, 0x00, 0x00, 0xff, 0x3d, 0x03, 0x01, 0xff, 0x77, 0x10, 0x1d, 0xff, 0xc9, 0x24, 0x53, 0xff, 0x49, 0x03, 0x00, 0xff, 0x45, 0x05, 0x00, 0xff, 0x40, 0x00, 0x00, 0xff, 0xbc, 0x86, 0x02, 0xff, 0x86, 0x4c, 0x01, 0xff, 0x30, 0x00, 0x00, 0xff, 0xa1, 0x7f, 0x7b, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7e, 0x56, 0x53, 0xff, 0x31, 0x00, 0x00, 0xff, 0xc1, 0xa9, 0xa5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xf7, 0xff, 0xfe, 0xff, 0xf8, 0xff, + 0xfd, 0xfa, 0xf6, 0xff, 0xfb, 0xf9, 0xf5, 0xff, 0xf9, 0xf9, 0xf4, 0xff, 0xf9, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf9, 0xf6, 0xf2, 0xff, 0xf9, 0xf5, 0xf0, 0xff, 0xf8, 0xf4, 0xee, 0xff, 0xf7, 0xf1, 0xec, 0xff, 0xf7, 0xee, 0xea, 0xff, 0xf7, 0xeb, 0xe7, 0xff, 0xf8, 0xe8, 0xe4, 0xff, 0xf9, 0xe4, 0xe1, 0xff, 0xfa, 0xe0, 0xdf, 0xff, 0xfb, 0xde, 0xdc, 0xff, 0xfb, 0xdb, 0xda, 0xff, 0xfc, 0xd8, 0xd8, 0xff, 0xfb, 0xd6, 0xd5, 0xff, 0xfb, 0xd3, 0xd4, 0xff, 0xfb, 0xd2, 0xd3, 0xff, 0xfb, 0xd1, 0xd2, 0xff, 0xfc, 0xd1, 0xd1, 0xff, 0xfc, 0xd1, 0xd1, 0xff, 0xfc, 0xd1, 0xd0, 0xff, 0xfc, 0xd1, 0xd0, 0xff, 0xfc, 0xd2, 0xd1, 0xff, 0xfb, 0xd2, 0xd1, 0xff, 0xfb, 0xd2, 0xd1, 0xff, 0xfc, 0xd1, 0xd0, 0xff, 0xfc, 0xd1, 0xd0, 0xff, 0xfd, 0xd1, 0xd0, 0xff, 0xfc, 0xd2, 0xd1, 0xff, 0xfc, 0xd3, 0xd1, 0xff, 0xfd, 0xd3, 0xd2, 0xff, 0xfc, 0xd3, 0xd2, 0xff, 0xfa, 0xd3, 0xd2, 0xff, 0xfb, 0xd3, 0xd2, 0xff, 0xfc, 0xd3, 0xd2, 0xff, 0xfd, 0xd3, 0xd2, 0xff, 0xfc, 0xd3, 0xd3, 0xff, 0xfc, 0xd3, 0xd2, 0xff, 0xfd, 0xd2, 0xd2, 0xff, 0xfd, 0xd2, 0xd1, 0xff, 0xfd, 0xd3, 0xd1, 0xff, 0xfd, 0xd3, 0xd2, 0xff, 0xfd, 0xd5, 0xd3, 0xff, 0xfd, 0xdc, 0xd8, 0xff, 0xfc, 0xbe, 0xc6, 0xff, 0xfc, 0x58, 0x86, 0xff, 0xfd, 0x2e, 0x6e, 0xff, 0xfd, 0x37, 0x75, 0xff, 0xfd, 0x37, 0x75, 0xff, 0xfd, 0x37, 0x74, 0xff, 0xfd, 0x39, 0x72, 0xff, 0xfd, 0x2d, 0x69, 0xff, 0xfc, 0x8e, 0xa9, 0xff, 0xfd, 0xf5, 0xee, 0xff, 0xfc, 0xee, 0xe9, 0xff, 0xfa, 0xef, 0xeb, 0xff, 0xf9, 0xf1, 0xec, 0xff, 0xf7, 0xf2, 0xee, 0xff, 0xf7, 0xf3, 0xef, 0xff, 0xf7, 0xf5, 0xf0, 0xff, 0xf7, 0xf5, 0xf1, 0xff, 0xf5, 0xf6, 0xf4, 0xff, 0xf5, 0xf5, 0xf2, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf4, 0xf5, 0xf1, 0xff, 0xf3, 0xf4, 0xf1, 0xff, 0xf3, 0xf5, 0xf2, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xfc, 0xfb, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xd4, 0x9d, 0x9c, 0xff, 0x87, 0x09, 0x11, 0xff, 0x70, 0x00, 0x08, 0xff, 0x7a, 0x06, 0x11, 0xff, 0xa5, 0x0e, 0x28, 0xff, 0xde, 0x1b, 0x49, 0xff, 0xf9, 0x31, 0x64, 0xff, 0xeb, 0x5b, 0x7c, 0xff, 0xdf, 0x9b, 0xa5, 0xff, 0xe7, 0xda, 0xd7, 0xff, 0xf9, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0x64, 0x52, 0x52, 0xff, 0x1d, 0x00, 0x00, 0xff, 0x2d, 0x01, 0x00, 0xff, 0x31, 0x01, 0x00, 0xff, 0x30, 0x00, 0x00, 0xff, 0x82, 0x10, 0x28, 0xff, 0x6e, 0x0b, 0x15, 0xff, 0x59, 0x08, 0x14, 0xff, 0xc5, 0x28, 0x5a, 0xff, 0x48, 0x04, 0x00, 0xff, 0x48, 0x06, 0x00, 0xff, 0x42, 0x00, 0x00, 0xff, 0xbd, 0x8a, 0x02, 0xff, 0x88, 0x4d, 0x01, 0xff, 0x32, 0x00, 0x00, 0xff, 0xa1, 0x7e, 0x7b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa9, 0x8e, 0x8b, 0xff, 0x2d, 0x00, 0x00, 0xff, 0x97, 0x72, 0x6f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xf7, 0xff, 0xff, 0xff, 0xf8, 0xff, + 0xfd, 0xfb, 0xf6, 0xff, 0xfc, 0xfa, 0xf5, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf9, 0xf6, 0xf0, 0xff, 0xf8, 0xf4, 0xef, 0xff, 0xf7, 0xf2, 0xed, 0xff, 0xf6, 0xf0, 0xeb, 0xff, 0xf6, 0xed, 0xe9, 0xff, 0xf7, 0xeb, 0xe7, 0xff, 0xf9, 0xe8, 0xe5, 0xff, 0xf9, 0xe5, 0xe2, 0xff, 0xf9, 0xe2, 0xe0, 0xff, 0xf9, 0xdf, 0xde, 0xff, 0xfa, 0xdd, 0xdc, 0xff, 0xfa, 0xdb, 0xda, 0xff, 0xf9, 0xd9, 0xd9, 0xff, 0xf9, 0xd7, 0xd7, 0xff, 0xfa, 0xd6, 0xd6, 0xff, 0xf9, 0xd6, 0xd5, 0xff, 0xf9, 0xd5, 0xd5, 0xff, 0xf9, 0xd5, 0xd4, 0xff, 0xf9, 0xd5, 0xd4, 0xff, 0xf9, 0xd6, 0xd5, 0xff, 0xfa, 0xd7, 0xd5, 0xff, 0xfa, 0xd6, 0xd4, 0xff, 0xfa, 0xd6, 0xd5, 0xff, 0xfb, 0xd7, 0xd5, 0xff, 0xfa, 0xd7, 0xd5, 0xff, 0xfb, 0xd8, 0xd6, 0xff, 0xfc, 0xd8, 0xd7, 0xff, 0xfb, 0xd8, 0xd7, 0xff, 0xfb, 0xd9, 0xd7, 0xff, 0xfb, 0xd9, 0xd7, 0xff, 0xfb, 0xd9, 0xd7, 0xff, 0xfb, 0xd8, 0xd7, 0xff, 0xfa, 0xd8, 0xd6, 0xff, 0xfa, 0xd7, 0xd6, 0xff, 0xfa, 0xd7, 0xd6, 0xff, 0xfb, 0xd7, 0xd6, 0xff, 0xfb, 0xd7, 0xd6, 0xff, 0xfc, 0xd7, 0xd5, 0xff, 0xfc, 0xd6, 0xd5, 0xff, 0xfd, 0xd7, 0xd5, 0xff, 0xfd, 0xd6, 0xd4, 0xff, 0xfd, 0xdc, 0xd8, 0xff, 0xfd, 0xd2, 0xd4, 0xff, 0xfb, 0x73, 0x9b, 0xff, 0xfd, 0x2d, 0x70, 0xff, 0xfd, 0x31, 0x73, 0xff, 0xfd, 0x34, 0x74, 0xff, 0xfd, 0x37, 0x73, 0xff, 0xfd, 0x2f, 0x6b, 0xff, 0xfc, 0x67, 0x90, 0xff, 0xfd, 0xf3, 0xef, 0xff, 0xfd, 0xf2, 0xee, 0xff, 0xfa, 0xf2, 0xee, 0xff, 0xf8, 0xf4, 0xef, 0xff, 0xf7, 0xf4, 0xef, 0xff, 0xf8, 0xf4, 0xf0, 0xff, 0xf7, 0xf5, 0xf0, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf5, 0xf6, 0xf3, 0xff, 0xf5, 0xf5, 0xf3, 0xff, 0xf5, 0xf5, 0xf2, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf4, 0xf5, 0xf3, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf4, 0xf5, 0xf3, 0xff, 0xf5, 0xf6, 0xf4, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xfb, 0xf9, 0xf4, 0xff, 0xfd, 0xfb, 0xf6, 0xff, 0xff, 0xff, 0xfb, 0xff, 0xff, 0xbf, 0xc7, 0xff, 0xfa, 0x6a, 0x8b, 0xff, 0xfd, 0x4d, 0x79, 0xff, 0xff, 0x5e, 0x8a, 0xff, 0xfb, 0x97, 0xb0, 0xff, 0xf2, 0xd3, 0xd7, 0xff, 0xf8, 0xfc, 0xf4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xfe, 0xff, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7e, 0x5b, 0x5a, 0xff, 0x29, 0x00, 0x00, 0xff, 0x31, 0x01, 0x00, 0xff, 0x38, 0x01, 0x00, 0xff, 0x33, 0x00, 0x00, 0xff, 0xb3, 0x1d, 0x4c, 0xff, 0x6d, 0x0d, 0x20, 0xff, 0x50, 0x08, 0x12, 0xff, 0xc7, 0x2e, 0x63, 0xff, 0x48, 0x03, 0x00, 0xff, 0x49, 0x05, 0x00, 0xff, 0x43, 0x00, 0x00, 0xff, 0xbf, 0x8a, 0x02, 0xff, 0x87, 0x4b, 0x01, 0xff, 0x32, 0x00, 0x00, 0xff, 0xa7, 0x84, 0x83, 0xff, 0xff, 0xff, 0xff, 0xff, 0x9c, 0x78, 0x75, 0xff, 0x35, 0x00, 0x00, 0xff, 0x6f, 0x3d, 0x3c, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, 0xf8, 0xff, 0xfe, 0xff, 0xf9, 0xff, + 0xfd, 0xfb, 0xf6, 0xff, 0xfb, 0xfb, 0xf5, 0xff, 0xf9, 0xf9, 0xf4, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf9, 0xf7, 0xf2, 0xff, 0xf9, 0xf6, 0xf1, 0xff, 0xf7, 0xf4, 0xef, 0xff, 0xf7, 0xf3, 0xee, 0xff, 0xf6, 0xf2, 0xed, 0xff, 0xf6, 0xef, 0xeb, 0xff, 0xf7, 0xed, 0xe9, 0xff, 0xf7, 0xeb, 0xe8, 0xff, 0xf8, 0xe9, 0xe6, 0xff, 0xf9, 0xe7, 0xe4, 0xff, 0xf8, 0xe4, 0xe2, 0xff, 0xf9, 0xe2, 0xe0, 0xff, 0xf9, 0xe0, 0xdf, 0xff, 0xfa, 0xdf, 0xde, 0xff, 0xf9, 0xdd, 0xdc, 0xff, 0xfa, 0xdc, 0xdb, 0xff, 0xf9, 0xdb, 0xda, 0xff, 0xf8, 0xdb, 0xd9, 0xff, 0xf8, 0xda, 0xd9, 0xff, 0xf8, 0xda, 0xd9, 0xff, 0xf9, 0xdb, 0xdb, 0xff, 0xf9, 0xdc, 0xdb, 0xff, 0xf9, 0xdc, 0xda, 0xff, 0xf8, 0xdc, 0xda, 0xff, 0xfa, 0xdd, 0xdb, 0xff, 0xfa, 0xdd, 0xdb, 0xff, 0xfb, 0xde, 0xdc, 0xff, 0xfa, 0xde, 0xdd, 0xff, 0xf9, 0xde, 0xdc, 0xff, 0xf9, 0xde, 0xdc, 0xff, 0xfa, 0xde, 0xdc, 0xff, 0xfa, 0xdf, 0xdb, 0xff, 0xfa, 0xde, 0xdb, 0xff, 0xf9, 0xdd, 0xdb, 0xff, 0xf9, 0xdd, 0xdb, 0xff, 0xf9, 0xdd, 0xdc, 0xff, 0xfa, 0xdd, 0xdb, 0xff, 0xf9, 0xdd, 0xdb, 0xff, 0xf9, 0xdc, 0xdb, 0xff, 0xfa, 0xdb, 0xda, 0xff, 0xfa, 0xda, 0xd8, 0xff, 0xfb, 0xd9, 0xd7, 0xff, 0xfd, 0xda, 0xd7, 0xff, 0xfe, 0xde, 0xdc, 0xff, 0xfd, 0xe1, 0xdf, 0xff, 0xfb, 0x95, 0xb1, 0xff, 0xfc, 0x31, 0x76, 0xff, 0xfd, 0x2c, 0x71, 0xff, 0xfd, 0x34, 0x73, 0xff, 0xfd, 0x32, 0x71, 0xff, 0xfc, 0x45, 0x7c, 0xff, 0xfd, 0xe4, 0xe6, 0xff, 0xfd, 0xf7, 0xf2, 0xff, 0xfa, 0xf5, 0xf0, 0xff, 0xf9, 0xf5, 0xf0, 0xff, 0xf8, 0xf5, 0xf1, 0xff, 0xf7, 0xf5, 0xf0, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf5, 0xf5, 0xf2, 0xff, 0xf5, 0xf5, 0xf3, 0xff, 0xf5, 0xf5, 0xf4, 0xff, 0xf5, 0xf5, 0xf3, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf4, 0xf5, 0xf3, 0xff, 0xf6, 0xf6, 0xf4, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xfb, 0xf9, 0xf4, 0xff, 0xfc, 0xfb, 0xf6, 0xff, 0xfd, 0xff, 0xfc, 0xff, 0xfe, 0xff, 0xfd, 0xff, 0xfc, 0xfc, 0xf7, 0xff, 0xfd, 0xff, 0xfb, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfb, 0xff, 0xfe, 0xfc, 0xf7, 0xff, 0xfd, 0xfb, 0xf6, 0xff, 0xfd, 0xfb, 0xf6, 0xff, 0xfd, 0xfc, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb2, 0x6e, 0x6b, 0xff, 0x4a, 0x00, 0x00, 0xff, 0x35, 0x00, 0x00, 0xff, 0x3a, 0x01, 0x00, 0xff, 0x2f, 0x00, 0x00, 0xff, 0xae, 0x1f, 0x4f, 0xff, 0x72, 0x0f, 0x24, 0xff, 0x55, 0x09, 0x13, 0xff, 0xc4, 0x2f, 0x66, 0xff, 0x46, 0x03, 0x00, 0xff, 0x4a, 0x04, 0x00, 0xff, 0x43, 0x00, 0x00, 0xff, 0xbd, 0x89, 0x02, 0xff, 0x88, 0x4c, 0x01, 0xff, 0x38, 0x00, 0x00, 0xff, 0x85, 0x57, 0x53, 0xff, 0x79, 0x4a, 0x48, 0xff, 0x41, 0x00, 0x00, 0xff, 0x44, 0x00, 0x00, 0xff, 0x52, 0x14, 0x12, 0xff, 0xec, 0xe6, 0xe1, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xfe, 0xff, 0xf9, 0xff, + 0xfd, 0xfb, 0xf6, 0xff, 0xfb, 0xfb, 0xf6, 0xff, 0xf9, 0xf9, 0xf4, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf7, 0xf5, 0xf0, 0xff, 0xf7, 0xf4, 0xef, 0xff, 0xf7, 0xf3, 0xee, 0xff, 0xf7, 0xf2, 0xed, 0xff, 0xf7, 0xf0, 0xeb, 0xff, 0xf7, 0xee, 0xeb, 0xff, 0xf8, 0xec, 0xea, 0xff, 0xf7, 0xea, 0xe8, 0xff, 0xf7, 0xe9, 0xe6, 0xff, 0xf8, 0xe7, 0xe5, 0xff, 0xf9, 0xe5, 0xe4, 0xff, 0xf8, 0xe4, 0xe3, 0xff, 0xf8, 0xe3, 0xe1, 0xff, 0xf7, 0xe2, 0xe0, 0xff, 0xf7, 0xe2, 0xdf, 0xff, 0xf7, 0xe1, 0xdf, 0xff, 0xf7, 0xe1, 0xde, 0xff, 0xf7, 0xe1, 0xdf, 0xff, 0xf7, 0xe2, 0xdf, 0xff, 0xf6, 0xe1, 0xdf, 0xff, 0xf6, 0xe1, 0xde, 0xff, 0xf6, 0xe1, 0xdf, 0xff, 0xf8, 0xe2, 0xe0, 0xff, 0xf9, 0xe3, 0xe1, 0xff, 0xf9, 0xe4, 0xe1, 0xff, 0xf8, 0xe3, 0xe1, 0xff, 0xf8, 0xe3, 0xe1, 0xff, 0xf8, 0xe3, 0xe1, 0xff, 0xf8, 0xe3, 0xe0, 0xff, 0xf8, 0xe3, 0xe0, 0xff, 0xf8, 0xe3, 0xdf, 0xff, 0xf7, 0xe2, 0xe0, 0xff, 0xf7, 0xe2, 0xe0, 0xff, 0xf8, 0xe3, 0xe0, 0xff, 0xf8, 0xe3, 0xe0, 0xff, 0xf7, 0xe2, 0xdf, 0xff, 0xf7, 0xe1, 0xdf, 0xff, 0xf8, 0xdf, 0xdd, 0xff, 0xf8, 0xdd, 0xdc, 0xff, 0xf8, 0xdc, 0xdb, 0xff, 0xfa, 0xdc, 0xdb, 0xff, 0xfb, 0xdd, 0xdb, 0xff, 0xfd, 0xe0, 0xde, 0xff, 0xfd, 0xe9, 0xe4, 0xff, 0xfc, 0xb0, 0xc3, 0xff, 0xfc, 0x3f, 0x7f, 0xff, 0xfd, 0x28, 0x6f, 0xff, 0xfd, 0x32, 0x73, 0xff, 0xfd, 0x30, 0x71, 0xff, 0xfc, 0xc7, 0xd3, 0xff, 0xfc, 0xfd, 0xf6, 0xff, 0xf9, 0xf5, 0xf1, 0xff, 0xf8, 0xf5, 0xf1, 0xff, 0xf8, 0xf5, 0xf1, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf4, 0xf5, 0xf1, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf5, 0xf6, 0xf4, 0xff, 0xf6, 0xf7, 0xf4, 0xff, 0xf5, 0xf5, 0xf3, 0xff, 0xf4, 0xf4, 0xf1, 0xff, 0xf4, 0xf5, 0xf1, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf7, 0xf8, 0xf3, 0xff, 0xf9, 0xf8, 0xf4, 0xff, 0xfa, 0xf9, 0xf5, 0xff, 0xfa, 0xfa, 0xf5, 0xff, 0xfa, 0xfb, 0xf5, 0xff, 0xfb, 0xfa, 0xf5, 0xff, 0xfb, 0xf9, 0xf4, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf9, 0xf8, 0xf4, 0xff, 0xfa, 0xf9, 0xf5, 0xff, 0xfb, 0xf9, 0xf4, 0xff, 0xfc, 0xfa, 0xf5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb7, 0x78, 0x74, 0xff, 0x48, 0x00, 0x00, 0xff, 0x26, 0x00, 0x00, 0xff, 0x2b, 0x00, 0x00, 0xff, 0x44, 0x03, 0x09, 0xff, 0xd0, 0x2a, 0x6a, 0xff, 0x5c, 0x09, 0x14, 0xff, 0x58, 0x09, 0x14, 0xff, 0xc2, 0x30, 0x63, 0xff, 0x45, 0x03, 0x00, 0xff, 0x49, 0x04, 0x00, 0xff, 0x3c, 0x00, 0x01, 0xff, 0xb9, 0x86, 0x02, 0xff, 0x8c, 0x50, 0x01, 0xff, 0x43, 0x00, 0x01, 0xff, 0x46, 0x00, 0x00, 0xff, 0x43, 0x00, 0x00, 0xff, 0x4a, 0x04, 0x00, 0xff, 0x4a, 0x04, 0x00, 0xff, 0x42, 0x00, 0x00, 0xff, 0xc9, 0xb6, 0xb2, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xf8, 0xff, + 0xfd, 0xfa, 0xf6, 0xff, 0xfc, 0xfb, 0xf6, 0xff, 0xf8, 0xf8, 0xf4, 0xff, 0xf5, 0xf6, 0xf4, 0xff, 0xf4, 0xf6, 0xf4, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf7, 0xf6, 0xf1, 0xff, 0xf8, 0xf6, 0xf1, 0xff, 0xf7, 0xf5, 0xf0, 0xff, 0xf8, 0xf4, 0xef, 0xff, 0xf8, 0xf4, 0xef, 0xff, 0xf7, 0xf2, 0xee, 0xff, 0xf7, 0xf1, 0xed, 0xff, 0xf7, 0xf0, 0xeb, 0xff, 0xf7, 0xee, 0xeb, 0xff, 0xf7, 0xec, 0xe9, 0xff, 0xf6, 0xeb, 0xe8, 0xff, 0xf7, 0xea, 0xe8, 0xff, 0xf7, 0xea, 0xe7, 0xff, 0xf7, 0xe9, 0xe6, 0xff, 0xf6, 0xe8, 0xe6, 0xff, 0xf6, 0xe8, 0xe5, 0xff, 0xf7, 0xe7, 0xe4, 0xff, 0xf7, 0xe7, 0xe4, 0xff, 0xf7, 0xe7, 0xe4, 0xff, 0xf7, 0xe7, 0xe4, 0xff, 0xf6, 0xe6, 0xe3, 0xff, 0xf5, 0xe6, 0xe3, 0xff, 0xf5, 0xe7, 0xe3, 0xff, 0xf6, 0xe7, 0xe3, 0xff, 0xf8, 0xe8, 0xe4, 0xff, 0xf8, 0xe8, 0xe4, 0xff, 0xf8, 0xe8, 0xe4, 0xff, 0xf8, 0xe8, 0xe4, 0xff, 0xf8, 0xe7, 0xe5, 0xff, 0xf8, 0xe7, 0xe4, 0xff, 0xf8, 0xe7, 0xe4, 0xff, 0xf7, 0xe7, 0xe4, 0xff, 0xf6, 0xe7, 0xe4, 0xff, 0xf7, 0xe7, 0xe4, 0xff, 0xf7, 0xe8, 0xe4, 0xff, 0xf7, 0xe8, 0xe4, 0xff, 0xf6, 0xe7, 0xe4, 0xff, 0xf6, 0xe5, 0xe3, 0xff, 0xf7, 0xe4, 0xe1, 0xff, 0xf6, 0xe2, 0xe0, 0xff, 0xf7, 0xe0, 0xde, 0xff, 0xf8, 0xdf, 0xdd, 0xff, 0xf8, 0xdf, 0xdd, 0xff, 0xfa, 0xe0, 0xde, 0xff, 0xfc, 0xe3, 0xe0, 0xff, 0xfd, 0xee, 0xe8, 0xff, 0xfc, 0xd2, 0xd9, 0xff, 0xfb, 0x59, 0x8e, 0xff, 0xfd, 0x20, 0x69, 0xff, 0xfc, 0x23, 0x6c, 0xff, 0xfc, 0xc2, 0xd0, 0xff, 0xfc, 0xfe, 0xf6, 0xff, 0xfa, 0xf6, 0xf2, 0xff, 0xf9, 0xf6, 0xf2, 0xff, 0xf7, 0xf5, 0xf1, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf4, 0xff, 0xf5, 0xf6, 0xf4, 0xff, 0xf5, 0xf5, 0xf2, 0xff, 0xf5, 0xf5, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf7, 0xf4, 0xff, 0xf5, 0xf6, 0xf4, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf7, 0xf6, 0xf3, 0xff, 0xf8, 0xf8, 0xf4, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xfd, 0xfa, 0xf5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb1, 0x7a, 0x75, 0xff, 0x73, 0x09, 0x19, 0xff, 0xa3, 0x29, 0x5f, 0xff, 0xa5, 0x2a, 0x5e, 0xff, 0xcf, 0x31, 0x74, 0xff, 0x8f, 0x18, 0x3a, 0xff, 0x37, 0x00, 0x00, 0xff, 0x5d, 0x0b, 0x17, 0xff, 0xc5, 0x32, 0x62, 0xff, 0x48, 0x03, 0x00, 0xff, 0x41, 0x00, 0x00, 0xff, 0x4d, 0x09, 0x01, 0xff, 0xd9, 0xab, 0x03, 0xff, 0x70, 0x33, 0x01, 0xff, 0x45, 0x00, 0x00, 0xff, 0x4a, 0x05, 0x00, 0xff, 0x49, 0x05, 0x00, 0xff, 0x4a, 0x05, 0x00, 0xff, 0x4a, 0x06, 0x00, 0xff, 0x3e, 0x00, 0x00, 0xff, 0x98, 0x74, 0x70, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xf9, 0xff, + 0xfd, 0xfa, 0xf6, 0xff, 0xfb, 0xfb, 0xf6, 0xff, 0xf7, 0xf8, 0xf4, 0xff, 0xf4, 0xf5, 0xf4, 0xff, 0xf3, 0xf5, 0xf3, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf7, 0xf6, 0xf1, 0xff, 0xf7, 0xf6, 0xf1, 0xff, 0xf8, 0xf5, 0xf0, 0xff, 0xf8, 0xf4, 0xf0, 0xff, 0xf8, 0xf3, 0xef, 0xff, 0xf7, 0xf2, 0xef, 0xff, 0xf7, 0xf2, 0xed, 0xff, 0xf7, 0xf1, 0xec, 0xff, 0xf7, 0xf0, 0xec, 0xff, 0xf6, 0xef, 0xeb, 0xff, 0xf7, 0xef, 0xeb, 0xff, 0xf7, 0xee, 0xeb, 0xff, 0xf7, 0xed, 0xea, 0xff, 0xf7, 0xed, 0xea, 0xff, 0xf7, 0xec, 0xe9, 0xff, 0xf7, 0xec, 0xe9, 0xff, 0xf7, 0xec, 0xe9, 0xff, 0xf7, 0xeb, 0xe8, 0xff, 0xf6, 0xea, 0xe8, 0xff, 0xf6, 0xeb, 0xe7, 0xff, 0xf6, 0xea, 0xe7, 0xff, 0xf6, 0xea, 0xe7, 0xff, 0xf6, 0xeb, 0xe8, 0xff, 0xf6, 0xec, 0xe8, 0xff, 0xf7, 0xeb, 0xe8, 0xff, 0xf7, 0xec, 0xe8, 0xff, 0xf6, 0xec, 0xe8, 0xff, 0xf6, 0xeb, 0xe8, 0xff, 0xf6, 0xeb, 0xe8, 0xff, 0xf7, 0xeb, 0xe7, 0xff, 0xf6, 0xeb, 0xe7, 0xff, 0xf7, 0xeb, 0xe7, 0xff, 0xf7, 0xec, 0xe8, 0xff, 0xf7, 0xec, 0xe9, 0xff, 0xf6, 0xec, 0xe8, 0xff, 0xf6, 0xeb, 0xe8, 0xff, 0xf6, 0xea, 0xe7, 0xff, 0xf7, 0xe9, 0xe6, 0xff, 0xf7, 0xe7, 0xe4, 0xff, 0xf7, 0xe5, 0xe2, 0xff, 0xf7, 0xe3, 0xe1, 0xff, 0xf7, 0xe2, 0xdf, 0xff, 0xf7, 0xe2, 0xe0, 0xff, 0xf9, 0xe5, 0xe2, 0xff, 0xfc, 0xe9, 0xe4, 0xff, 0xfd, 0xf3, 0xec, 0xff, 0xfb, 0xe8, 0xe7, 0xff, 0xfb, 0x98, 0xb5, 0xff, 0xfb, 0xa9, 0xc2, 0xff, 0xfb, 0xf5, 0xf2, 0xff, 0xfb, 0xf7, 0xf2, 0xff, 0xf9, 0xf7, 0xf2, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf3, 0xf5, 0xf3, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf6, 0xf5, 0xf2, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf5, 0xf6, 0xf4, 0xff, 0xf5, 0xf6, 0xf3, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf6, 0xf7, 0xf4, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xfc, 0xfb, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb8, 0x7e, 0x7f, 0xff, 0x51, 0x00, 0x05, 0xff, 0x6e, 0x14, 0x2d, 0xff, 0x6f, 0x14, 0x2d, 0xff, 0x5f, 0x0b, 0x1b, 0xff, 0x3c, 0x00, 0x00, 0xff, 0x41, 0x00, 0x00, 0xff, 0x63, 0x0c, 0x18, 0xff, 0xc6, 0x34, 0x61, 0xff, 0x43, 0x00, 0x00, 0xff, 0x50, 0x09, 0x01, 0xff, 0xce, 0x96, 0x03, 0xff, 0x9e, 0x67, 0x03, 0xff, 0x40, 0x00, 0x00, 0xff, 0x48, 0x05, 0x00, 0xff, 0x4a, 0x06, 0x01, 0xff, 0x50, 0x08, 0x02, 0xff, 0x57, 0x0a, 0x03, 0xff, 0x4d, 0x06, 0x01, 0xff, 0x41, 0x00, 0x00, 0xff, 0x66, 0x32, 0x30, 0xff, 0xfd, 0xfc, 0xf7, 0xff, 0xff, 0xff, 0xfa, 0xff, + 0xfd, 0xfa, 0xf6, 0xff, 0xfc, 0xfb, 0xf6, 0xff, 0xf9, 0xf8, 0xf4, 0xff, 0xf5, 0xf6, 0xf4, 0xff, 0xf5, 0xf5, 0xf3, 0xff, 0xf5, 0xf5, 0xf2, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf7, 0xf6, 0xf1, 0xff, 0xf8, 0xf5, 0xf0, 0xff, 0xf8, 0xf4, 0xf0, 0xff, 0xf8, 0xf4, 0xf0, 0xff, 0xf7, 0xf3, 0xef, 0xff, 0xf7, 0xf2, 0xee, 0xff, 0xf6, 0xf2, 0xed, 0xff, 0xf6, 0xf2, 0xed, 0xff, 0xf7, 0xf2, 0xed, 0xff, 0xf7, 0xf1, 0xed, 0xff, 0xf7, 0xf0, 0xed, 0xff, 0xf7, 0xf0, 0xec, 0xff, 0xf7, 0xf0, 0xec, 0xff, 0xf7, 0xf0, 0xec, 0xff, 0xf7, 0xef, 0xec, 0xff, 0xf7, 0xef, 0xeb, 0xff, 0xf7, 0xef, 0xeb, 0xff, 0xf7, 0xef, 0xea, 0xff, 0xf7, 0xee, 0xeb, 0xff, 0xf6, 0xee, 0xeb, 0xff, 0xf7, 0xef, 0xeb, 0xff, 0xf7, 0xef, 0xeb, 0xff, 0xf7, 0xef, 0xeb, 0xff, 0xf7, 0xf0, 0xeb, 0xff, 0xf7, 0xf0, 0xeb, 0xff, 0xf7, 0xf0, 0xeb, 0xff, 0xf7, 0xef, 0xeb, 0xff, 0xf6, 0xef, 0xea, 0xff, 0xf6, 0xee, 0xea, 0xff, 0xf7, 0xef, 0xea, 0xff, 0xf8, 0xf0, 0xeb, 0xff, 0xf7, 0xf0, 0xec, 0xff, 0xf7, 0xf0, 0xeb, 0xff, 0xf6, 0xef, 0xeb, 0xff, 0xf8, 0xee, 0xeb, 0xff, 0xf8, 0xed, 0xea, 0xff, 0xf7, 0xeb, 0xe8, 0xff, 0xf7, 0xe9, 0xe6, 0xff, 0xf8, 0xe8, 0xe5, 0xff, 0xf8, 0xe6, 0xe3, 0xff, 0xf7, 0xe5, 0xe2, 0xff, 0xf8, 0xe6, 0xe3, 0xff, 0xf8, 0xea, 0xe6, 0xff, 0xfa, 0xee, 0xea, 0xff, 0xfb, 0xf3, 0xee, 0xff, 0xfa, 0xfd, 0xf5, 0xff, 0xfa, 0xfe, 0xf6, 0xff, 0xfa, 0xf7, 0xf2, 0xff, 0xf9, 0xf7, 0xf1, 0xff, 0xf8, 0xf6, 0xf2, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf6, 0xf5, 0xf0, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf5, 0xf6, 0xf3, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf5, 0xf6, 0xf4, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf6, 0xf7, 0xf4, 0xff, 0xf6, 0xf7, 0xf5, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf8, 0xf4, 0xff, 0xf9, 0xf9, 0xf4, 0xff, 0xfa, 0xf9, 0xf5, 0xff, 0xfc, 0xfb, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xcd, 0x84, 0x8f, 0xff, 0x3e, 0x00, 0x00, 0xff, 0x3f, 0x00, 0x00, 0xff, 0x45, 0x02, 0x00, 0xff, 0x4b, 0x02, 0x00, 0xff, 0x87, 0x11, 0x20, 0xff, 0x4b, 0x01, 0x00, 0xff, 0x66, 0x0c, 0x18, 0xff, 0xc6, 0x31, 0x5d, 0xff, 0x40, 0x00, 0x00, 0xff, 0xc0, 0x86, 0x02, 0xff, 0xa2, 0x6d, 0x02, 0xff, 0x38, 0x00, 0x01, 0xff, 0x44, 0x03, 0x00, 0xff, 0x4d, 0x06, 0x01, 0xff, 0x57, 0x09, 0x03, 0xff, 0x56, 0x08, 0x03, 0xff, 0x54, 0x08, 0x02, 0xff, 0x51, 0x07, 0x01, 0xff, 0x46, 0x02, 0x00, 0xff, 0x45, 0x05, 0x03, 0xff, 0xdb, 0xcf, 0xcb, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xfd, 0xfa, 0xf6, 0xff, 0xfc, 0xfb, 0xf6, 0xff, 0xf9, 0xf9, 0xf4, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf5, 0xf5, 0xf3, 0xff, 0xf5, 0xf5, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf6, 0xf5, 0xf0, 0xff, 0xf8, 0xf6, 0xf1, 0xff, 0xf8, 0xf6, 0xf1, 0xff, 0xf8, 0xf5, 0xf0, 0xff, 0xf7, 0xf4, 0xf0, 0xff, 0xf7, 0xf3, 0xef, 0xff, 0xf7, 0xf4, 0xef, 0xff, 0xf7, 0xf4, 0xef, 0xff, 0xf8, 0xf4, 0xef, 0xff, 0xf7, 0xf3, 0xef, 0xff, 0xf7, 0xf2, 0xef, 0xff, 0xf7, 0xf1, 0xee, 0xff, 0xf7, 0xf1, 0xee, 0xff, 0xf7, 0xf1, 0xee, 0xff, 0xf7, 0xf2, 0xed, 0xff, 0xf7, 0xf2, 0xee, 0xff, 0xf7, 0xf1, 0xed, 0xff, 0xf7, 0xf1, 0xed, 0xff, 0xf6, 0xf1, 0xed, 0xff, 0xf6, 0xf1, 0xed, 0xff, 0xf6, 0xf1, 0xee, 0xff, 0xf6, 0xf1, 0xee, 0xff, 0xf7, 0xf1, 0xee, 0xff, 0xf7, 0xf2, 0xee, 0xff, 0xf7, 0xf1, 0xed, 0xff, 0xf7, 0xf1, 0xed, 0xff, 0xf6, 0xf1, 0xed, 0xff, 0xf5, 0xf1, 0xed, 0xff, 0xf5, 0xf1, 0xec, 0xff, 0xf6, 0xf2, 0xed, 0xff, 0xf7, 0xf2, 0xed, 0xff, 0xf7, 0xf3, 0xee, 0xff, 0xf7, 0xf2, 0xee, 0xff, 0xf7, 0xf2, 0xee, 0xff, 0xf8, 0xf2, 0xee, 0xff, 0xf8, 0xf1, 0xed, 0xff, 0xf8, 0xef, 0xec, 0xff, 0xf7, 0xed, 0xea, 0xff, 0xf7, 0xeb, 0xe8, 0xff, 0xf8, 0xea, 0xe7, 0xff, 0xf7, 0xe9, 0xe5, 0xff, 0xf6, 0xe9, 0xe6, 0xff, 0xf6, 0xeb, 0xe8, 0xff, 0xf8, 0xed, 0xeb, 0xff, 0xf9, 0xf1, 0xed, 0xff, 0xfa, 0xf3, 0xee, 0xff, 0xfa, 0xf5, 0xf0, 0xff, 0xf9, 0xf6, 0xf1, 0xff, 0xf9, 0xf7, 0xf1, 0xff, 0xf7, 0xf6, 0xf1, 0xff, 0xf6, 0xf5, 0xf0, 0xff, 0xf4, 0xf4, 0xf1, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf4, 0xf5, 0xf1, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf5, 0xf6, 0xf3, 0xff, 0xf5, 0xf6, 0xf3, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf6, 0xf5, 0xf2, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf9, 0xf7, 0xf3, 0xff, 0xfa, 0xf9, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf5, 0xf5, 0xf2, 0xff, 0xf6, 0xf6, 0xf4, 0xff, 0xf6, 0xf7, 0xf4, 0xff, 0xf8, 0xf8, 0xf4, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xfb, 0xf9, 0xf5, 0xff, 0xfa, 0xfa, 0xf5, 0xff, 0xfc, 0xfb, 0xf5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x84, 0x9b, 0xff, 0x4d, 0x00, 0x00, 0xff, 0x71, 0x12, 0x0f, 0xff, 0x55, 0x08, 0x03, 0xff, 0x55, 0x06, 0x07, 0xff, 0xbd, 0x21, 0x3c, 0xff, 0x4b, 0x01, 0x01, 0xff, 0x66, 0x0d, 0x17, 0xff, 0xc1, 0x30, 0x59, 0xff, 0x48, 0x0a, 0x00, 0xff, 0xd1, 0xa0, 0x02, 0xff, 0x94, 0x57, 0x02, 0xff, 0x69, 0x28, 0x01, 0xff, 0x43, 0x00, 0x00, 0xff, 0x4a, 0x05, 0x01, 0xff, 0x54, 0x08, 0x01, 0xff, 0x5f, 0x0b, 0x02, 0xff, 0x59, 0x09, 0x02, 0xff, 0x4f, 0x05, 0x01, 0xff, 0x48, 0x03, 0x00, 0xff, 0x3a, 0x00, 0x00, 0xff, 0xa2, 0x82, 0x80, 0xff, 0xff, 0xfc, 0xf9, 0xff, + 0xfd, 0xfa, 0xf5, 0xff, 0xfb, 0xfb, 0xf5, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf5, 0xf5, 0xf2, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf5, 0xf5, 0xf2, 0xff, 0xf6, 0xf7, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf8, 0xf5, 0xf1, 0xff, 0xf8, 0xf6, 0xf2, 0xff, 0xf8, 0xf6, 0xf1, 0xff, 0xf7, 0xf5, 0xf0, 0xff, 0xf8, 0xf4, 0xf0, 0xff, 0xf7, 0xf5, 0xf0, 0xff, 0xf7, 0xf5, 0xf1, 0xff, 0xf8, 0xf5, 0xf0, 0xff, 0xf8, 0xf5, 0xf0, 0xff, 0xf8, 0xf4, 0xef, 0xff, 0xf7, 0xf3, 0xef, 0xff, 0xf8, 0xf3, 0xef, 0xff, 0xf7, 0xf4, 0xef, 0xff, 0xf8, 0xf4, 0xf0, 0xff, 0xf7, 0xf4, 0xef, 0xff, 0xf7, 0xf4, 0xf0, 0xff, 0xf6, 0xf4, 0xf0, 0xff, 0xf7, 0xf4, 0xf0, 0xff, 0xf7, 0xf4, 0xf0, 0xff, 0xf6, 0xf3, 0xf0, 0xff, 0xf7, 0xf3, 0xef, 0xff, 0xf7, 0xf4, 0xf0, 0xff, 0xf7, 0xf3, 0xf0, 0xff, 0xf6, 0xf3, 0xef, 0xff, 0xf6, 0xf3, 0xef, 0xff, 0xf6, 0xf3, 0xef, 0xff, 0xf6, 0xf3, 0xef, 0xff, 0xf6, 0xf3, 0xef, 0xff, 0xf7, 0xf4, 0xef, 0xff, 0xf7, 0xf5, 0xf0, 0xff, 0xf8, 0xf4, 0xf0, 0xff, 0xf8, 0xf5, 0xf1, 0xff, 0xf8, 0xf4, 0xf0, 0xff, 0xf8, 0xf3, 0xef, 0xff, 0xf7, 0xf1, 0xee, 0xff, 0xf6, 0xf0, 0xec, 0xff, 0xf7, 0xef, 0xeb, 0xff, 0xf7, 0xed, 0xea, 0xff, 0xf7, 0xec, 0xe9, 0xff, 0xf7, 0xec, 0xe9, 0xff, 0xf7, 0xec, 0xea, 0xff, 0xf8, 0xee, 0xeb, 0xff, 0xf8, 0xf2, 0xed, 0xff, 0xf9, 0xf4, 0xee, 0xff, 0xf9, 0xf5, 0xf0, 0xff, 0xf9, 0xf6, 0xf1, 0xff, 0xf9, 0xf7, 0xf2, 0xff, 0xf7, 0xf6, 0xf1, 0xff, 0xf6, 0xf5, 0xf0, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf5, 0xf5, 0xf2, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf5, 0xf6, 0xf3, 0xff, 0xf7, 0xf6, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf5, 0xf6, 0xf3, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xfa, 0xfa, 0xf5, 0xff, 0xfb, 0xfa, 0xf6, 0xff, 0xfc, 0xfb, 0xf5, 0xff, 0xfd, 0xfb, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xed, 0x8e, 0xa7, 0xff, 0x50, 0x00, 0x04, 0xff, 0x71, 0x14, 0x11, 0xff, 0x4c, 0x06, 0x02, 0xff, 0x4f, 0x05, 0x07, 0xff, 0xc2, 0x28, 0x41, 0xff, 0x45, 0x01, 0x01, 0xff, 0x6c, 0x10, 0x1a, 0xff, 0xbd, 0x31, 0x50, 0xff, 0x43, 0x0a, 0x00, 0xff, 0xc5, 0x96, 0x02, 0xff, 0xb6, 0x81, 0x02, 0xff, 0x7c, 0x3e, 0x01, 0xff, 0x3f, 0x00, 0x00, 0xff, 0x4e, 0x06, 0x01, 0xff, 0x54, 0x08, 0x01, 0xff, 0x4a, 0x03, 0x01, 0xff, 0x4d, 0x03, 0x01, 0xff, 0x4e, 0x04, 0x01, 0xff, 0x46, 0x03, 0x01, 0xff, 0x42, 0x00, 0x00, 0xff, 0x47, 0x0a, 0x09, 0xff, 0x53, 0x1f, 0x1d, 0xff, + 0xfd, 0xfa, 0xf5, 0xff, 0xfb, 0xfa, 0xf5, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf4, 0xf4, 0xf2, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf5, 0xf6, 0xf3, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf7, 0xf5, 0xf1, 0xff, 0xf8, 0xf6, 0xf1, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf6, 0xf1, 0xff, 0xf7, 0xf5, 0xf1, 0xff, 0xf8, 0xf5, 0xf1, 0xff, 0xf8, 0xf6, 0xf1, 0xff, 0xf9, 0xf6, 0xf1, 0xff, 0xf8, 0xf6, 0xf1, 0xff, 0xf8, 0xf6, 0xf1, 0xff, 0xf8, 0xf6, 0xf1, 0xff, 0xf8, 0xf5, 0xf1, 0xff, 0xf8, 0xf5, 0xf1, 0xff, 0xf8, 0xf6, 0xf1, 0xff, 0xf8, 0xf6, 0xf1, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf7, 0xf6, 0xf1, 0xff, 0xf8, 0xf6, 0xf2, 0xff, 0xf8, 0xf6, 0xf2, 0xff, 0xf7, 0xf6, 0xf1, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf7, 0xf5, 0xf1, 0xff, 0xf7, 0xf5, 0xf1, 0xff, 0xf7, 0xf5, 0xf1, 0xff, 0xf8, 0xf5, 0xf0, 0xff, 0xf8, 0xf5, 0xf1, 0xff, 0xf8, 0xf5, 0xf0, 0xff, 0xf8, 0xf5, 0xf0, 0xff, 0xf7, 0xf5, 0xf1, 0xff, 0xf7, 0xf6, 0xf1, 0xff, 0xf8, 0xf6, 0xf1, 0xff, 0xf9, 0xf6, 0xf2, 0xff, 0xf8, 0xf6, 0xf2, 0xff, 0xf8, 0xf6, 0xf1, 0xff, 0xf8, 0xf4, 0xf0, 0xff, 0xf7, 0xf3, 0xef, 0xff, 0xf6, 0xf2, 0xed, 0xff, 0xf7, 0xf1, 0xec, 0xff, 0xf6, 0xf0, 0xec, 0xff, 0xf6, 0xef, 0xeb, 0xff, 0xf7, 0xef, 0xeb, 0xff, 0xf7, 0xf0, 0xec, 0xff, 0xf7, 0xf2, 0xee, 0xff, 0xf8, 0xf4, 0xef, 0xff, 0xfa, 0xf6, 0xf1, 0xff, 0xf9, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf6, 0xf1, 0xff, 0xf5, 0xf5, 0xf0, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf5, 0xf5, 0xf2, 0xff, 0xf5, 0xf6, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf1, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xfc, 0xfa, 0xf5, 0xff, 0xfc, 0xfb, 0xf6, 0xff, 0xfd, 0xfc, 0xf7, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf2, 0xf4, 0xee, 0xff, 0xea, 0x73, 0x93, 0xff, 0x53, 0x00, 0x0a, 0xff, 0x6b, 0x14, 0x10, 0xff, 0x4e, 0x08, 0x04, 0xff, 0x5a, 0x09, 0x0d, 0xff, 0xc5, 0x29, 0x41, 0xff, 0x3a, 0x00, 0x00, 0xff, 0x8a, 0x1f, 0x2c, 0xff, 0xa7, 0x2a, 0x3e, 0xff, 0x41, 0x0a, 0x00, 0xff, 0xc5, 0x96, 0x02, 0xff, 0xac, 0x7b, 0x02, 0xff, 0x79, 0x3b, 0x01, 0xff, 0x3f, 0x00, 0x00, 0xff, 0x47, 0x03, 0x01, 0xff, 0x50, 0x05, 0x02, 0xff, 0x55, 0x07, 0x02, 0xff, 0x50, 0x04, 0x01, 0xff, 0x42, 0x01, 0x00, 0xff, 0x39, 0x00, 0x00, 0xff, 0x39, 0x00, 0x00, 0xff, 0x42, 0x00, 0x01, 0xff, 0x53, 0x05, 0x10, 0xff, + 0xfd, 0xfa, 0xf5, 0xff, 0xfb, 0xfa, 0xf5, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf4, 0xf5, 0xf3, 0xff, 0xf4, 0xf4, 0xf2, 0xff, 0xf5, 0xf5, 0xf3, 0xff, 0xf5, 0xf5, 0xf3, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf7, 0xf5, 0xf1, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf7, 0xf6, 0xf1, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf6, 0xf2, 0xff, 0xf7, 0xf5, 0xf1, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf6, 0xf1, 0xff, 0xf7, 0xf6, 0xf1, 0xff, 0xf7, 0xf6, 0xf1, 0xff, 0xf7, 0xf6, 0xf1, 0xff, 0xf7, 0xf6, 0xf1, 0xff, 0xf7, 0xf6, 0xf1, 0xff, 0xf7, 0xf6, 0xf1, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf9, 0xf6, 0xf1, 0xff, 0xf8, 0xf6, 0xf0, 0xff, 0xf7, 0xf4, 0xef, 0xff, 0xf7, 0xf3, 0xee, 0xff, 0xf6, 0xf2, 0xee, 0xff, 0xf7, 0xf1, 0xed, 0xff, 0xf7, 0xf1, 0xee, 0xff, 0xf7, 0xf2, 0xef, 0xff, 0xf7, 0xf3, 0xef, 0xff, 0xf8, 0xf4, 0xf0, 0xff, 0xf8, 0xf6, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf1, 0xff, 0xf7, 0xf6, 0xf1, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf5, 0xf5, 0xf2, 0xff, 0xf5, 0xf5, 0xf4, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf6, 0xf1, 0xff, 0xf7, 0xf6, 0xf1, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf9, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf9, 0xf9, 0xf4, 0xff, 0xfa, 0xf9, 0xf3, 0xff, 0xfa, 0xf9, 0xf3, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xfb, 0xfa, 0xf4, 0xff, 0xfc, 0xfb, 0xf5, 0xff, 0xfe, 0xff, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0xdd, 0xd9, 0xff, 0x9e, 0x7b, 0x7c, 0xff, 0xb4, 0x35, 0x55, 0xff, 0xd3, 0x24, 0x54, 0xff, 0x5c, 0x0a, 0x14, 0xff, 0x63, 0x10, 0x0c, 0xff, 0x4b, 0x08, 0x02, 0xff, 0x77, 0x14, 0x1c, 0xff, 0xb2, 0x24, 0x36, 0xff, 0x32, 0x00, 0x00, 0xff, 0xbf, 0x36, 0x49, 0xff, 0x74, 0x16, 0x20, 0xff, 0x46, 0x0b, 0x00, 0xff, 0xc7, 0x92, 0x02, 0xff, 0xad, 0x7b, 0x02, 0xff, 0x79, 0x3c, 0x01, 0xff, 0x3e, 0x00, 0x01, 0xff, 0x47, 0x03, 0x01, 0xff, 0x4b, 0x04, 0x01, 0xff, 0x3d, 0x00, 0x00, 0xff, 0x38, 0x00, 0x00, 0xff, 0x4b, 0x03, 0x08, 0xff, 0x7a, 0x16, 0x2e, 0xff, 0xb2, 0x2d, 0x5a, 0xff, 0xdf, 0x3f, 0x80, 0xff, 0xf8, 0x4b, 0x98, 0xff, + 0xfd, 0xfa, 0xf5, 0xff, 0xfb, 0xf9, 0xf4, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf5, 0xf5, 0xf0, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf5, 0xf5, 0xf2, 0xff, 0xf4, 0xf5, 0xf3, 0xff, 0xf5, 0xf5, 0xf2, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf5, 0xf6, 0xf4, 0xff, 0xf5, 0xf6, 0xf3, 0xff, 0xf5, 0xf6, 0xf4, 0xff, 0xf5, 0xf6, 0xf3, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf5, 0xf5, 0xf3, 0xff, 0xf5, 0xf5, 0xf4, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf5, 0xf2, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf6, 0xf5, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf5, 0xf6, 0xf1, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf5, 0xf6, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf6, 0xf1, 0xff, 0xf7, 0xf5, 0xf0, 0xff, 0xf7, 0xf4, 0xef, 0xff, 0xf7, 0xf4, 0xf0, 0xff, 0xf7, 0xf4, 0xf0, 0xff, 0xf8, 0xf4, 0xf0, 0xff, 0xf8, 0xf4, 0xf0, 0xff, 0xf8, 0xf5, 0xf1, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf5, 0xf5, 0xf2, 0xff, 0xf5, 0xf5, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf6, 0xf7, 0xf2, 0xff, 0xf7, 0xf6, 0xf1, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xfa, 0xf8, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xfb, 0xf9, 0xf4, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xfb, 0xf9, 0xf4, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xfb, 0xfa, 0xf5, 0xff, 0xfb, 0xf9, 0xf5, 0xff, 0xfc, 0xfa, 0xf5, 0xff, 0xfc, 0xfa, 0xf5, 0xff, 0xfc, 0xfa, 0xf5, 0xff, 0xfd, 0xfd, 0xf7, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xff, 0xeb, 0xf1, 0xff, 0x98, 0x78, 0x7b, 0xff, 0x45, 0x13, 0x18, 0xff, 0xb1, 0x1c, 0x44, 0xff, 0x56, 0x0b, 0x1f, 0xff, 0x8e, 0x20, 0x3d, 0xff, 0x65, 0x0e, 0x1a, 0xff, 0x5a, 0x0c, 0x07, 0xff, 0x48, 0x07, 0x01, 0xff, 0xa8, 0x25, 0x32, 0xff, 0x7c, 0x15, 0x1d, 0xff, 0x55, 0x0b, 0x0f, 0xff, 0xcf, 0x3a, 0x4f, 0xff, 0x42, 0x00, 0x02, 0xff, 0x4a, 0x0d, 0x00, 0xff, 0xc7, 0x92, 0x02, 0xff, 0xb7, 0x83, 0x02, 0xff, 0x7b, 0x40, 0x01, 0xff, 0x39, 0x00, 0x00, 0xff, 0x37, 0x00, 0x00, 0xff, 0x3c, 0x00, 0x00, 0xff, 0x67, 0x0e, 0x1e, 0xff, 0xaa, 0x2a, 0x55, 0xff, 0xe6, 0x44, 0x85, 0xff, 0xff, 0x50, 0x9d, 0xff, 0xff, 0x51, 0x9f, 0xff, 0xff, 0x4e, 0x9c, 0xff, 0xff, 0x4c, 0x9c, 0xff, + 0xfd, 0xfa, 0xf5, 0xff, 0xfc, 0xfa, 0xf5, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf6, 0xf5, 0xf2, 0xff, 0xf5, 0xf4, 0xf1, 0xff, 0xf5, 0xf5, 0xf2, 0xff, 0xf4, 0xf5, 0xf3, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf5, 0xf6, 0xf4, 0xff, 0xf5, 0xf6, 0xf4, 0xff, 0xf5, 0xf6, 0xf4, 0xff, 0xf5, 0xf6, 0xf3, 0xff, 0xf5, 0xf5, 0xf2, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf5, 0xf6, 0xf3, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf6, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf4, 0xf5, 0xf3, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf6, 0xf6, 0xf4, 0xff, 0xf6, 0xf6, 0xf4, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf6, 0xf1, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf9, 0xf8, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf6, 0xf1, 0xff, 0xf8, 0xf5, 0xf1, 0xff, 0xf7, 0xf6, 0xf1, 0xff, 0xf8, 0xf6, 0xf1, 0xff, 0xf7, 0xf6, 0xf1, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf5, 0xf5, 0xf2, 0xff, 0xf5, 0xf5, 0xf2, 0xff, 0xf7, 0xf6, 0xf3, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf9, 0xf8, 0xf4, 0xff, 0xfb, 0xf9, 0xf4, 0xff, 0xfc, 0xf9, 0xf4, 0xff, 0xfc, 0xfb, 0xf5, 0xff, 0xfc, 0xfb, 0xf6, 0xff, 0xfe, 0xfe, 0xf8, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xfc, 0xff, 0xfc, 0xf8, 0xf6, 0xff, 0xff, 0xa7, 0xc7, 0xff, 0xa7, 0x32, 0x55, 0xff, 0x22, 0x00, 0x00, 0xff, 0xbe, 0x2f, 0x59, 0xff, 0x62, 0x16, 0x2a, 0xff, 0x00, 0x00, 0x00, 0xff, 0x97, 0x24, 0x42, 0xff, 0x64, 0x0e, 0x1a, 0xff, 0x43, 0x02, 0x00, 0xff, 0x52, 0x08, 0x08, 0xff, 0xc9, 0x2c, 0x3d, 0xff, 0x3d, 0x02, 0x02, 0xff, 0xae, 0x2e, 0x3f, 0xff, 0x8c, 0x1f, 0x29, 0xff, 0x36, 0x00, 0x00, 0xff, 0x4d, 0x0e, 0x01, 0xff, 0xca, 0x98, 0x03, 0xff, 0x71, 0x3b, 0x01, 0xff, 0x47, 0x14, 0x00, 0xff, 0x3a, 0x00, 0x00, 0xff, 0x7a, 0x17, 0x2e, 0xff, 0xcd, 0x36, 0x70, 0xff, 0xfd, 0x4c, 0x97, 0xff, 0xff, 0x52, 0x9f, 0xff, 0xff, 0x50, 0x9b, 0xff, 0xfe, 0x50, 0x98, 0xff, 0xfd, 0x4f, 0x98, 0xff, 0xfe, 0x4d, 0x99, 0xff, 0xfe, 0x4c, 0x9a, 0xff, + 0xfd, 0xfb, 0xf6, 0xff, 0xfb, 0xfb, 0xf6, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf5, 0xf6, 0xf3, 0xff, 0xf6, 0xf7, 0xf4, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf5, 0xf6, 0xf4, 0xff, 0xf5, 0xf5, 0xf4, 0xff, 0xf5, 0xf5, 0xf3, 0xff, 0xf4, 0xf5, 0xf3, 0xff, 0xf4, 0xf5, 0xf3, 0xff, 0xf5, 0xf5, 0xf3, 0xff, 0xf5, 0xf5, 0xf2, 0xff, 0xf5, 0xf5, 0xf2, 0xff, 0xf4, 0xf5, 0xf1, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf6, 0xf7, 0xf4, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf4, 0xf5, 0xf3, 0xff, 0xf4, 0xf5, 0xf3, 0xff, 0xf6, 0xf6, 0xf4, 0xff, 0xf6, 0xf7, 0xf4, 0xff, 0xf6, 0xf6, 0xf4, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf6, 0xf1, 0xff, 0xf7, 0xf6, 0xf1, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf5, 0xf7, 0xf4, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf5, 0xf5, 0xf3, 0xff, 0xf5, 0xf5, 0xf3, 0xff, 0xf5, 0xf6, 0xf5, 0xff, 0xf5, 0xf7, 0xf4, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf9, 0xf8, 0xf4, 0xff, 0xfb, 0xf9, 0xf5, 0xff, 0xfc, 0xfb, 0xf6, 0xff, 0xfd, 0xfb, 0xf7, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xfa, 0xf6, 0xff, 0xdb, 0xd0, 0xcd, 0xff, 0xbb, 0xa3, 0xa2, 0xff, 0xa2, 0x84, 0x82, 0xff, 0x9b, 0x7c, 0x7a, 0xff, 0xab, 0x91, 0x8f, 0xff, 0xcf, 0xc6, 0xc1, 0xff, 0xfa, 0xd6, 0xe0, 0xff, 0xff, 0x73, 0xa7, 0xff, 0xd5, 0x2e, 0x67, 0xff, 0x25, 0x00, 0x00, 0xff, 0x9b, 0x2e, 0x4e, 0xff, 0x93, 0x2d, 0x4c, 0xff, 0x00, 0x00, 0x00, 0xff, 0x05, 0x00, 0x00, 0xff, 0xa7, 0x26, 0x44, 0xff, 0x56, 0x0a, 0x11, 0xff, 0x2f, 0x00, 0x00, 0xff, 0x90, 0x1b, 0x25, 0xff, 0x9d, 0x1f, 0x2b, 0xff, 0x54, 0x0e, 0x13, 0xff, 0xd0, 0x3a, 0x4d, 0xff, 0x45, 0x03, 0x01, 0xff, 0x40, 0x01, 0x00, 0xff, 0x44, 0x04, 0x01, 0xff, 0x53, 0x1b, 0x00, 0xff, 0x33, 0x00, 0x00, 0xff, 0x73, 0x14, 0x2f, 0xff, 0xd5, 0x3c, 0x77, 0xff, 0xff, 0x4e, 0x9e, 0xff, 0xff, 0x4e, 0xa0, 0xff, 0xfe, 0x4e, 0x9a, 0xff, 0xfd, 0x4f, 0x98, 0xff, 0xfd, 0x50, 0x98, 0xff, 0xfd, 0x50, 0x99, 0xff, 0xfc, 0x50, 0x99, 0xff, 0xfd, 0x4e, 0x9b, 0xff, 0xfe, 0x4d, 0x9c, 0xff, + 0xfd, 0xfb, 0xf6, 0xff, 0xfc, 0xfb, 0xf6, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf5, 0xf6, 0xf3, 0xff, 0xf5, 0xf5, 0xf2, 0xff, 0xf5, 0xf5, 0xf2, 0xff, 0xf4, 0xf5, 0xf3, 0xff, 0xf5, 0xf6, 0xf3, 0xff, 0xf5, 0xf6, 0xf5, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf7, 0xf6, 0xf3, 0xff, 0xf6, 0xf6, 0xf4, 0xff, 0xf5, 0xf6, 0xf4, 0xff, 0xf4, 0xf5, 0xf3, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf5, 0xf5, 0xf2, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf5, 0xf5, 0xf3, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf5, 0xf6, 0xf3, 0xff, 0xf5, 0xf5, 0xf2, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf4, 0xf6, 0xf3, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf5, 0xf6, 0xf3, 0xff, 0xf4, 0xf5, 0xf4, 0xff, 0xf6, 0xf7, 0xf4, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xfb, 0xf9, 0xf4, 0xff, 0xfc, 0xfa, 0xf5, 0xff, 0xff, 0xfe, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xeb, 0xe6, 0xe3, 0xff, 0xa6, 0x86, 0x84, 0xff, 0x68, 0x2e, 0x2d, 0xff, 0x46, 0x03, 0x03, 0xff, 0x3b, 0x00, 0x00, 0xff, 0x39, 0x00, 0x00, 0xff, 0x37, 0x00, 0x00, 0xff, 0x2d, 0x00, 0x00, 0xff, 0x84, 0x1c, 0x32, 0xff, 0xfd, 0x50, 0x8e, 0xff, 0xfd, 0x45, 0x87, 0xff, 0x5a, 0x10, 0x1f, 0xff, 0x4e, 0x0f, 0x1b, 0xff, 0xd3, 0x44, 0x75, 0xff, 0x17, 0x05, 0x07, 0xff, 0x05, 0x00, 0x00, 0xff, 0x1f, 0x04, 0x08, 0xff, 0xa5, 0x21, 0x3c, 0xff, 0x37, 0x01, 0x01, 0xff, 0x43, 0x03, 0x04, 0xff, 0xce, 0x31, 0x41, 0xff, 0x48, 0x06, 0x07, 0xff, 0xb5, 0x32, 0x40, 0xff, 0x7d, 0x19, 0x1f, 0xff, 0x3a, 0x00, 0x00, 0xff, 0x40, 0x01, 0x00, 0xff, 0x37, 0x00, 0x00, 0xff, 0x64, 0x0e, 0x22, 0xff, 0xcc, 0x3a, 0x71, 0xff, 0xff, 0x4f, 0x9c, 0xff, 0xff, 0x51, 0x9e, 0xff, 0xfe, 0x4e, 0x9a, 0xff, 0xfd, 0x4d, 0x9a, 0xff, 0xfd, 0x4e, 0x99, 0xff, 0xfd, 0x51, 0x9a, 0xff, 0xfd, 0x51, 0x9a, 0xff, 0xfd, 0x50, 0x9a, 0xff, 0xfd, 0x50, 0x9b, 0xff, 0xfd, 0x4e, 0x9c, 0xff, 0xfe, 0x4d, 0x9d, 0xff, + 0xfd, 0xfb, 0xf6, 0xff, 0xfc, 0xfb, 0xf6, 0xff, 0xf9, 0xf9, 0xf4, 0xff, 0xf5, 0xf6, 0xf3, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf4, 0xf5, 0xf3, 0xff, 0xf4, 0xf5, 0xf4, 0xff, 0xf6, 0xf6, 0xf4, 0xff, 0xf7, 0xf6, 0xf3, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf4, 0xff, 0xf5, 0xf6, 0xf4, 0xff, 0xf4, 0xf5, 0xf3, 0xff, 0xf4, 0xf5, 0xf3, 0xff, 0xf5, 0xf5, 0xf2, 0xff, 0xf5, 0xf5, 0xf2, 0xff, 0xf4, 0xf5, 0xf3, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf6, 0xf3, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf5, 0xf6, 0xf3, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf7, 0xf6, 0xf1, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf8, 0xf6, 0xf2, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf5, 0xf6, 0xf3, 0xff, 0xf5, 0xf5, 0xf2, 0xff, 0xf5, 0xf6, 0xf4, 0xff, 0xf6, 0xf7, 0xf4, 0xff, 0xf5, 0xf6, 0xf3, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf4, 0xf4, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf5, 0xf6, 0xf4, 0xff, 0xf5, 0xf6, 0xf4, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xfa, 0xf8, 0xf4, 0xff, 0xfc, 0xf9, 0xf5, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xfd, 0xfc, 0xf8, 0xff, 0xaa, 0x8b, 0x87, 0xff, 0x53, 0x15, 0x12, 0xff, 0x3c, 0x00, 0x00, 0xff, 0x3c, 0x00, 0x00, 0xff, 0x42, 0x00, 0x00, 0xff, 0x44, 0x01, 0x00, 0xff, 0x41, 0x00, 0x00, 0xff, 0x35, 0x00, 0x00, 0xff, 0x81, 0x19, 0x30, 0xff, 0xfc, 0x4a, 0x89, 0xff, 0xff, 0x4e, 0x92, 0xff, 0xb1, 0x2e, 0x57, 0xff, 0x20, 0x00, 0x00, 0xff, 0xc8, 0x39, 0x68, 0xff, 0x69, 0x1f, 0x37, 0xff, 0x03, 0x00, 0x00, 0xff, 0x06, 0x00, 0x00, 0xff, 0x57, 0x0d, 0x19, 0xff, 0x70, 0x0f, 0x1b, 0xff, 0x26, 0x00, 0x00, 0xff, 0x98, 0x20, 0x25, 0xff, 0x91, 0x1f, 0x24, 0xff, 0x6c, 0x0f, 0x16, 0xff, 0xbf, 0x2c, 0x3b, 0xff, 0x3d, 0x00, 0x00, 0xff, 0x3e, 0x00, 0x00, 0xff, 0x3d, 0x00, 0x00, 0xff, 0x9e, 0x2a, 0x4e, 0xff, 0xfc, 0x53, 0x9a, 0xff, 0xff, 0x54, 0x9e, 0xff, 0xfd, 0x4e, 0x98, 0xff, 0xfd, 0x4e, 0x99, 0xff, 0xfd, 0x4e, 0x9a, 0xff, 0xfd, 0x4e, 0x9a, 0xff, 0xfd, 0x4e, 0x9b, 0xff, 0xfd, 0x50, 0x9b, 0xff, 0xfd, 0x51, 0x9c, 0xff, 0xfd, 0x51, 0x9d, 0xff, 0xfd, 0x52, 0x9e, 0xff, 0xfd, 0x50, 0x9e, 0xff, 0xfe, 0x4f, 0xa0, 0xff, + 0xfd, 0xfb, 0xf7, 0xff, 0xfb, 0xfb, 0xf7, 0xff, 0xf9, 0xf9, 0xf4, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf5, 0xf4, 0xf1, 0xff, 0xf5, 0xf4, 0xf1, 0xff, 0xf3, 0xf5, 0xf2, 0xff, 0xf5, 0xf6, 0xf3, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf7, 0xf6, 0xf3, 0xff, 0xf5, 0xf5, 0xf4, 0xff, 0xf4, 0xf5, 0xf3, 0xff, 0xf4, 0xf5, 0xf3, 0xff, 0xf4, 0xf5, 0xf3, 0xff, 0xf5, 0xf5, 0xf3, 0xff, 0xf4, 0xf4, 0xf3, 0xff, 0xf5, 0xf5, 0xf3, 0xff, 0xf5, 0xf6, 0xf3, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf7, 0xf6, 0xf1, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf7, 0xf6, 0xf1, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf5, 0xf6, 0xf1, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf5, 0xf6, 0xf3, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf5, 0xf5, 0xf2, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf7, 0xf2, 0xff, 0xf5, 0xf6, 0xf3, 0xff, 0xf5, 0xf5, 0xf2, 0xff, 0xf5, 0xf6, 0xf3, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf5, 0xf6, 0xf3, 0xff, 0xf5, 0xf6, 0xf3, 0xff, 0xf5, 0xf6, 0xf3, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf6, 0xf6, 0xf4, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xfc, 0xf9, 0xf5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xeb, 0xe4, 0xdf, 0xff, 0x77, 0x43, 0x40, 0xff, 0x3f, 0x00, 0x00, 0xff, 0x43, 0x00, 0x00, 0xff, 0x41, 0x00, 0x01, 0xff, 0x83, 0x30, 0x01, 0xff, 0xb0, 0x57, 0x02, 0xff, 0x43, 0x03, 0x01, 0xff, 0x33, 0x00, 0x00, 0xff, 0x70, 0x14, 0x23, 0xff, 0xf9, 0x4a, 0x87, 0xff, 0xff, 0x4f, 0x90, 0xff, 0xf8, 0x49, 0x88, 0xff, 0x48, 0x0a, 0x12, 0xff, 0x62, 0x14, 0x21, 0xff, 0xcd, 0x41, 0x70, 0xff, 0x0f, 0x01, 0x02, 0xff, 0x0c, 0x00, 0x00, 0xff, 0x1e, 0x03, 0x05, 0xff, 0x84, 0x15, 0x22, 0xff, 0x32, 0x00, 0x01, 0xff, 0x4c, 0x09, 0x09, 0xff, 0xca, 0x36, 0x38, 0xff, 0x44, 0x04, 0x04, 0xff, 0xc8, 0x2a, 0x3d, 0xff, 0x64, 0x0d, 0x11, 0xff, 0x35, 0x00, 0x00, 0xff, 0x45, 0x02, 0x02, 0xff, 0xba, 0x32, 0x5f, 0xff, 0xff, 0x55, 0xa0, 0xff, 0xff, 0x51, 0x9a, 0xff, 0xfd, 0x50, 0x97, 0xff, 0xfd, 0x4d, 0x9b, 0xff, 0xfd, 0x4c, 0x9c, 0xff, 0xfe, 0x4d, 0x9c, 0xff, 0xfe, 0x4d, 0x9d, 0xff, 0xfe, 0x4e, 0x9e, 0xff, 0xfe, 0x4f, 0x9f, 0xff, 0xfe, 0x4f, 0xa1, 0xff, 0xfe, 0x51, 0xa2, 0xff, 0xfe, 0x52, 0xa2, 0xff, 0xfe, 0x51, 0xa3, 0xff, 0xff, 0x50, 0xa5, 0xff, + 0xfc, 0xfb, 0xf6, 0xff, 0xfa, 0xfb, 0xf6, 0xff, 0xf8, 0xf9, 0xf3, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf4, 0xf5, 0xf0, 0xff, 0xf4, 0xf5, 0xf1, 0xff, 0xf5, 0xf6, 0xf1, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf5, 0xf6, 0xf3, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf5, 0xf5, 0xf2, 0xff, 0xf4, 0xf5, 0xf3, 0xff, 0xf5, 0xf6, 0xf4, 0xff, 0xf5, 0xf6, 0xf3, 0xff, 0xf5, 0xf5, 0xf3, 0xff, 0xf4, 0xf5, 0xf3, 0xff, 0xf5, 0xf5, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf7, 0xf6, 0xf1, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf7, 0xf6, 0xf1, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf9, 0xf8, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf6, 0xf6, 0xf0, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf6, 0xf7, 0xf1, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf5, 0xf6, 0xf3, 0xff, 0xf5, 0xf6, 0xf4, 0xff, 0xf5, 0xf6, 0xf3, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf8, 0xf7, 0xf4, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xfb, 0xf9, 0xf4, 0xff, 0xfc, 0xf9, 0xf5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe4, 0xda, 0xd6, 0xff, 0x5e, 0x25, 0x23, 0xff, 0x3d, 0x00, 0x00, 0xff, 0x45, 0x00, 0x00, 0xff, 0x48, 0x01, 0x01, 0xff, 0xb3, 0x52, 0x05, 0xff, 0xd6, 0x7c, 0x04, 0xff, 0x6e, 0x2f, 0x02, 0xff, 0x2f, 0x00, 0x00, 0xff, 0x51, 0x09, 0x11, 0xff, 0xec, 0x45, 0x7d, 0xff, 0xff, 0x4f, 0x8e, 0xff, 0xff, 0x50, 0x92, 0xff, 0xae, 0x2f, 0x55, 0xff, 0x1b, 0x00, 0x00, 0xff, 0xc7, 0x3d, 0x67, 0xff, 0x67, 0x20, 0x35, 0xff, 0x05, 0x00, 0x00, 0xff, 0x0f, 0x00, 0x00, 0xff, 0x62, 0x10, 0x18, 0xff, 0x60, 0x0c, 0x12, 0xff, 0x25, 0x00, 0x00, 0xff, 0xb2, 0x2f, 0x2b, 0xff, 0x78, 0x19, 0x17, 0xff, 0x83, 0x17, 0x20, 0xff, 0xac, 0x23, 0x30, 0xff, 0x2c, 0x00, 0x00, 0xff, 0x45, 0x03, 0x04, 0xff, 0xc8, 0x37, 0x68, 0xff, 0xff, 0x51, 0x9d, 0xff, 0xfe, 0x4d, 0x96, 0xff, 0xfd, 0x4a, 0x96, 0xff, 0xfd, 0x46, 0x9f, 0xff, 0xff, 0x4c, 0xa0, 0xff, 0xff, 0x4f, 0x9e, 0xff, 0xfb, 0x4b, 0x99, 0xff, 0xf8, 0x4a, 0x97, 0xff, 0xf8, 0x4c, 0x98, 0xff, 0xf8, 0x4c, 0x9b, 0xff, 0xf8, 0x4c, 0x9d, 0xff, 0xf8, 0x4d, 0x9f, 0xff, 0xf8, 0x4e, 0xa0, 0xff, 0xfa, 0x4f, 0xa1, 0xff, 0xfa, 0x4c, 0xa0, 0xff, + 0xfc, 0xfb, 0xf5, 0xff, 0xfa, 0xfa, 0xf5, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf4, 0xf6, 0xf2, 0xff, 0xf3, 0xf4, 0xf1, 0xff, 0xf3, 0xf4, 0xf0, 0xff, 0xf4, 0xf5, 0xf1, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf5, 0xf6, 0xf1, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf5, 0xf6, 0xf1, 0xff, 0xf4, 0xf6, 0xf2, 0xff, 0xf5, 0xf6, 0xf1, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf4, 0xf5, 0xf3, 0xff, 0xf3, 0xf4, 0xf2, 0xff, 0xf3, 0xf4, 0xf2, 0xff, 0xf4, 0xf5, 0xf3, 0xff, 0xf3, 0xf5, 0xf3, 0xff, 0xf4, 0xf5, 0xf3, 0xff, 0xf3, 0xf5, 0xf3, 0xff, 0xf3, 0xf4, 0xf2, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf6, 0xf7, 0xf1, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf7, 0xf6, 0xf1, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf5, 0xf5, 0xf0, 0xff, 0xf6, 0xf5, 0xf0, 0xff, 0xf7, 0xf6, 0xf1, 0xff, 0xf7, 0xf6, 0xf1, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf5, 0xf6, 0xf1, 0xff, 0xf7, 0xf6, 0xf1, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf7, 0xf6, 0xf1, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf9, 0xf8, 0xf4, 0xff, 0xf9, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xfc, 0xfa, 0xf5, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xeb, 0xe4, 0xdf, 0xff, 0x5c, 0x25, 0x23, 0xff, 0x3d, 0x00, 0x00, 0xff, 0x45, 0x00, 0x01, 0xff, 0x48, 0x02, 0x02, 0xff, 0xc6, 0x56, 0x1a, 0xff, 0xb7, 0x59, 0x10, 0xff, 0x37, 0x05, 0x01, 0xff, 0x29, 0x00, 0x00, 0xff, 0x45, 0x06, 0x05, 0xff, 0xd4, 0x3a, 0x66, 0xff, 0xff, 0x4e, 0x90, 0xff, 0xff, 0x4d, 0x8c, 0xff, 0xfa, 0x4b, 0x89, 0xff, 0x4e, 0x0b, 0x14, 0xff, 0x5a, 0x13, 0x22, 0xff, 0xd7, 0x47, 0x7a, 0xff, 0x17, 0x03, 0x05, 0xff, 0x13, 0x00, 0x00, 0xff, 0x1d, 0x01, 0x01, 0xff, 0x66, 0x10, 0x15, 0xff, 0x30, 0x00, 0x00, 0xff, 0x51, 0x0c, 0x0a, 0xff, 0xb3, 0x2e, 0x27, 0xff, 0x4a, 0x07, 0x08, 0xff, 0xce, 0x32, 0x41, 0xff, 0x46, 0x04, 0x04, 0xff, 0x3f, 0x02, 0x04, 0xff, 0xcb, 0x36, 0x69, 0xff, 0xff, 0x50, 0x9a, 0xff, 0xfe, 0x4d, 0x92, 0xff, 0xfd, 0x4a, 0x95, 0xff, 0xfd, 0x49, 0x94, 0xff, 0xff, 0x82, 0x59, 0xff, 0xf0, 0x80, 0x37, 0xff, 0xc9, 0x3b, 0x49, 0xff, 0xb4, 0x29, 0x3e, 0xff, 0xae, 0x27, 0x38, 0xff, 0xad, 0x26, 0x3a, 0xff, 0xac, 0x25, 0x3a, 0xff, 0xac, 0x25, 0x3a, 0xff, 0xaa, 0x25, 0x39, 0xff, 0xa7, 0x25, 0x38, 0xff, 0xa4, 0x23, 0x36, 0xff, 0xa3, 0x21, 0x32, 0xff, + 0xfb, 0xfa, 0xf5, 0xff, 0xf9, 0xf9, 0xf4, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf3, 0xf4, 0xf2, 0xff, 0xf1, 0xf3, 0xf0, 0xff, 0xf1, 0xf2, 0xf0, 0xff, 0xf1, 0xf3, 0xf0, 0xff, 0xf2, 0xf4, 0xf1, 0xff, 0xf3, 0xf4, 0xf1, 0xff, 0xf4, 0xf4, 0xf0, 0xff, 0xf4, 0xf4, 0xf0, 0xff, 0xf4, 0xf4, 0xf0, 0xff, 0xf4, 0xf4, 0xf0, 0xff, 0xf4, 0xf4, 0xf0, 0xff, 0xf4, 0xf5, 0xf1, 0xff, 0xf4, 0xf5, 0xf1, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf3, 0xf4, 0xf2, 0xff, 0xf2, 0xf3, 0xf1, 0xff, 0xf2, 0xf3, 0xf1, 0xff, 0xf2, 0xf3, 0xf1, 0xff, 0xf2, 0xf3, 0xf1, 0xff, 0xf2, 0xf3, 0xf1, 0xff, 0xf1, 0xf2, 0xf1, 0xff, 0xf1, 0xf2, 0xf1, 0xff, 0xf3, 0xf3, 0xf0, 0xff, 0xf5, 0xf4, 0xf0, 0xff, 0xf5, 0xf6, 0xf0, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf7, 0xf6, 0xf1, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf5, 0xf4, 0xef, 0xff, 0xf5, 0xf4, 0xef, 0xff, 0xf5, 0xf4, 0xf0, 0xff, 0xf4, 0xf4, 0xf0, 0xff, 0xf4, 0xf4, 0xf0, 0xff, 0xf5, 0xf5, 0xf0, 0xff, 0xf7, 0xf6, 0xf1, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf5, 0xf6, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xfc, 0xfa, 0xf5, 0xff, 0xff, 0xfd, 0xf9, 0xff, 0xff, 0xff, 0xfb, 0xff, 0x77, 0x49, 0x46, 0xff, 0x3a, 0x00, 0x00, 0xff, 0x45, 0x02, 0x01, 0xff, 0x40, 0x00, 0x00, 0xff, 0xbb, 0x47, 0x1f, 0xff, 0xb0, 0x4b, 0x1e, 0xff, 0x2c, 0x00, 0x00, 0xff, 0x33, 0x00, 0x00, 0xff, 0x2a, 0x00, 0x00, 0xff, 0x82, 0x1e, 0x35, 0xff, 0xff, 0x4f, 0x8e, 0xff, 0xfd, 0x4b, 0x8a, 0xff, 0xff, 0x4e, 0x91, 0xff, 0xb4, 0x30, 0x59, 0xff, 0x21, 0x00, 0x00, 0xff, 0xc1, 0x38, 0x67, 0xff, 0x83, 0x27, 0x47, 0xff, 0x09, 0x00, 0x00, 0xff, 0x1e, 0x00, 0x00, 0xff, 0x23, 0x00, 0x00, 0xff, 0x26, 0x00, 0x00, 0xff, 0x2b, 0x00, 0x00, 0xff, 0x35, 0x01, 0x01, 0xff, 0x34, 0x01, 0x00, 0xff, 0x42, 0x05, 0x06, 0xff, 0x52, 0x0a, 0x0a, 0xff, 0x3e, 0x01, 0x04, 0xff, 0xcd, 0x37, 0x6a, 0xff, 0xff, 0x51, 0x98, 0xff, 0xfd, 0x4b, 0x91, 0xff, 0xfd, 0x47, 0x95, 0xff, 0xfd, 0x4d, 0x8f, 0xff, 0xff, 0xa4, 0x31, 0xff, 0xdc, 0x8e, 0x0c, 0xff, 0xac, 0x2c, 0x28, 0xff, 0xa4, 0x1e, 0x2c, 0xff, 0xa2, 0x1b, 0x2c, 0xff, 0xa7, 0x20, 0x2a, 0xff, 0xa9, 0x26, 0x27, 0xff, 0xa7, 0x25, 0x27, 0xff, 0xa7, 0x26, 0x26, 0xff, 0xa8, 0x27, 0x27, 0xff, 0xa3, 0x25, 0x26, 0xff, 0x9f, 0x23, 0x23, 0xff, 0x9e, 0x21, 0x1f, 0xff, + 0xfa, 0xfa, 0xf5, 0xff, 0xf8, 0xf8, 0xf4, 0xff, 0xf5, 0xf5, 0xf2, 0xff, 0xf2, 0xf3, 0xf0, 0xff, 0xef, 0xf2, 0xef, 0xff, 0xee, 0xf1, 0xef, 0xff, 0xef, 0xf1, 0xef, 0xff, 0xf0, 0xf2, 0xf0, 0xff, 0xf1, 0xf3, 0xef, 0xff, 0xf2, 0xf3, 0xef, 0xff, 0xf2, 0xf3, 0xef, 0xff, 0xf2, 0xf3, 0xef, 0xff, 0xf3, 0xf3, 0xef, 0xff, 0xf3, 0xf3, 0xef, 0xff, 0xf3, 0xf4, 0xf0, 0xff, 0xf3, 0xf4, 0xf1, 0xff, 0xf3, 0xf4, 0xf1, 0xff, 0xf2, 0xf4, 0xf1, 0xff, 0xf1, 0xf3, 0xf0, 0xff, 0xf0, 0xf2, 0xf0, 0xff, 0xf1, 0xf2, 0xf0, 0xff, 0xef, 0xf1, 0xf0, 0xff, 0xef, 0xf1, 0xef, 0xff, 0xef, 0xf1, 0xef, 0xff, 0xf0, 0xf1, 0xef, 0xff, 0xf2, 0xf2, 0xf0, 0xff, 0xf3, 0xf4, 0xf0, 0xff, 0xf4, 0xf5, 0xf0, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf6, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf6, 0xf7, 0xf2, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf4, 0xf5, 0xf0, 0xff, 0xf4, 0xf3, 0xee, 0xff, 0xf3, 0xf2, 0xed, 0xff, 0xf2, 0xf2, 0xed, 0xff, 0xf2, 0xf2, 0xee, 0xff, 0xf3, 0xf3, 0xef, 0xff, 0xf4, 0xf4, 0xf0, 0xff, 0xf6, 0xf5, 0xf0, 0xff, 0xf6, 0xf6, 0xf1, 0xff, 0xf4, 0xf4, 0xf1, 0xff, 0xf5, 0xf4, 0xf0, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf5, 0xf6, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf4, 0xf5, 0xf3, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf6, 0xf5, 0xf1, 0xff, 0xf7, 0xf6, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf7, 0xf6, 0xf3, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xfb, 0xf9, 0xf4, 0xff, 0xfc, 0xfa, 0xf6, 0xff, 0xfd, 0xfb, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb2, 0x97, 0x94, 0xff, 0x39, 0x00, 0x00, 0xff, 0x45, 0x01, 0x00, 0xff, 0x3d, 0x00, 0x00, 0xff, 0x8f, 0x2a, 0x14, 0xff, 0xc9, 0x51, 0x25, 0xff, 0x32, 0x01, 0x00, 0xff, 0x31, 0x00, 0x00, 0xff, 0x31, 0x00, 0x00, 0xff, 0x44, 0x07, 0x0c, 0xff, 0xe9, 0x44, 0x79, 0xff, 0xff, 0x4c, 0x8a, 0xff, 0xfd, 0x4a, 0x89, 0xff, 0xff, 0x4e, 0x8f, 0xff, 0x87, 0x1f, 0x35, 0xff, 0x45, 0x06, 0x10, 0xff, 0xff, 0x4f, 0x92, 0xff, 0x7b, 0x21, 0x3f, 0xff, 0x12, 0x00, 0x00, 0xff, 0x1d, 0x00, 0x00, 0xff, 0x22, 0x00, 0x00, 0xff, 0x24, 0x00, 0x00, 0xff, 0x28, 0x00, 0x00, 0xff, 0x29, 0x00, 0x00, 0xff, 0x29, 0x00, 0x00, 0xff, 0x2d, 0x00, 0x00, 0xff, 0x54, 0x0b, 0x17, 0xff, 0xd5, 0x3c, 0x71, 0xff, 0xff, 0x51, 0x96, 0xff, 0xfd, 0x4d, 0x8e, 0xff, 0xfd, 0x48, 0x93, 0xff, 0xfd, 0x4c, 0x8d, 0xff, 0xff, 0xa8, 0x2a, 0xff, 0xd6, 0x82, 0x10, 0xff, 0xa3, 0x1e, 0x2f, 0xff, 0xa3, 0x1d, 0x2f, 0xff, 0xad, 0x26, 0x2c, 0xff, 0xd7, 0x6d, 0x16, 0xff, 0xec, 0x9f, 0x0a, 0xff, 0xec, 0xa9, 0x0a, 0xff, 0xec, 0xad, 0x0b, 0xff, 0xed, 0xae, 0x0a, 0xff, 0xee, 0xad, 0x0a, 0xff, 0xed, 0xac, 0x09, 0xff, 0xed, 0xac, 0x08, 0xff, 0xef, 0xa9, 0x07, 0xff, + 0xf9, 0xfa, 0xf4, 0xff, 0xf7, 0xf8, 0xf3, 0xff, 0xf3, 0xf5, 0xf2, 0xff, 0xf0, 0xf2, 0xf0, 0xff, 0xee, 0xf0, 0xef, 0xff, 0xee, 0xf0, 0xee, 0xff, 0xed, 0xf0, 0xee, 0xff, 0xef, 0xf1, 0xef, 0xff, 0xf0, 0xf2, 0xef, 0xff, 0xf2, 0xf2, 0xef, 0xff, 0xf2, 0xf3, 0xee, 0xff, 0xf2, 0xf3, 0xef, 0xff, 0xf2, 0xf3, 0xef, 0xff, 0xf2, 0xf3, 0xef, 0xff, 0xf2, 0xf3, 0xf0, 0xff, 0xf2, 0xf4, 0xf0, 0xff, 0xf2, 0xf4, 0xf1, 0xff, 0xf1, 0xf3, 0xf1, 0xff, 0xf0, 0xf2, 0xf0, 0xff, 0xef, 0xf2, 0xf0, 0xff, 0xef, 0xf1, 0xef, 0xff, 0xef, 0xf1, 0xef, 0xff, 0xef, 0xf0, 0xef, 0xff, 0xef, 0xf1, 0xef, 0xff, 0xf0, 0xf1, 0xef, 0xff, 0xf1, 0xf2, 0xf0, 0xff, 0xf3, 0xf4, 0xf0, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf5, 0xf6, 0xf1, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf4, 0xf5, 0xf1, 0xff, 0xf3, 0xf4, 0xef, 0xff, 0xf2, 0xf2, 0xee, 0xff, 0xf1, 0xf1, 0xec, 0xff, 0xf1, 0xf0, 0xec, 0xff, 0xf2, 0xf1, 0xed, 0xff, 0xf3, 0xf2, 0xee, 0xff, 0xf3, 0xf3, 0xef, 0xff, 0xf5, 0xf4, 0xf0, 0xff, 0xf3, 0xf4, 0xf0, 0xff, 0xf3, 0xf4, 0xf1, 0xff, 0xf3, 0xf4, 0xf2, 0xff, 0xf4, 0xf4, 0xf2, 0xff, 0xf4, 0xf5, 0xf4, 0xff, 0xf5, 0xf6, 0xf4, 0xff, 0xf5, 0xf7, 0xf5, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf5, 0xf5, 0xf2, 0xff, 0xf4, 0xf5, 0xf3, 0xff, 0xf5, 0xf5, 0xf2, 0xff, 0xf4, 0xf5, 0xf1, 0xff, 0xf4, 0xf5, 0xf1, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xfc, 0xfb, 0xf6, 0xff, 0xff, 0xff, 0xfb, 0xff, 0xf3, 0xee, 0xea, 0xff, 0x58, 0x1e, 0x1d, 0xff, 0x40, 0x00, 0x00, 0xff, 0x42, 0x00, 0x00, 0xff, 0x56, 0x0a, 0x05, 0xff, 0xdc, 0x56, 0x2c, 0xff, 0x50, 0x11, 0x08, 0xff, 0x2d, 0x00, 0x00, 0xff, 0x34, 0x01, 0x00, 0xff, 0x27, 0x00, 0x00, 0xff, 0xa0, 0x2b, 0x4a, 0xff, 0xff, 0x50, 0x8e, 0xff, 0xfd, 0x49, 0x85, 0xff, 0xfd, 0x48, 0x86, 0xff, 0xff, 0x47, 0x8b, 0xff, 0xe1, 0x61, 0x45, 0xff, 0xc0, 0x42, 0x46, 0xff, 0xff, 0x4c, 0x95, 0xff, 0xf6, 0x48, 0x88, 0xff, 0xca, 0x39, 0x6b, 0xff, 0xc4, 0x38, 0x69, 0xff, 0xc5, 0x38, 0x69, 0xff, 0xc6, 0x39, 0x6a, 0xff, 0xc6, 0x3a, 0x6c, 0xff, 0xc5, 0x3a, 0x6c, 0xff, 0xc4, 0x39, 0x6a, 0xff, 0xcc, 0x39, 0x6d, 0xff, 0xf7, 0x49, 0x8b, 0xff, 0xff, 0x4f, 0x94, 0xff, 0xfc, 0x4c, 0x8e, 0xff, 0xfd, 0x47, 0x92, 0xff, 0xfd, 0x52, 0x89, 0xff, 0xff, 0xae, 0x27, 0xff, 0xd3, 0x7b, 0x13, 0xff, 0xa4, 0x1c, 0x30, 0xff, 0xa1, 0x1d, 0x2d, 0xff, 0xaf, 0x28, 0x2b, 0xff, 0xec, 0x8d, 0x0f, 0xff, 0xdd, 0x87, 0x0c, 0xff, 0xaa, 0x3e, 0x1d, 0xff, 0xa2, 0x32, 0x24, 0xff, 0xa7, 0x36, 0x27, 0xff, 0xaa, 0x38, 0x27, 0xff, 0xab, 0x39, 0x27, 0xff, 0xaa, 0x39, 0x26, 0xff, 0xaa, 0x38, 0x23, 0xff, 0x84, 0x29, 0x12, 0xff, + 0xfa, 0xf9, 0xf4, 0xff, 0xf7, 0xf8, 0xf2, 0xff, 0xf3, 0xf4, 0xf1, 0xff, 0xef, 0xf2, 0xef, 0xff, 0xed, 0xf0, 0xee, 0xff, 0xed, 0xef, 0xed, 0xff, 0xed, 0xef, 0xed, 0xff, 0xee, 0xf0, 0xee, 0xff, 0xef, 0xf1, 0xee, 0xff, 0xf1, 0xf2, 0xee, 0xff, 0xf1, 0xf3, 0xee, 0xff, 0xf1, 0xf3, 0xee, 0xff, 0xf2, 0xf3, 0xee, 0xff, 0xf2, 0xf3, 0xef, 0xff, 0xf1, 0xf3, 0xf0, 0xff, 0xf2, 0xf3, 0xf1, 0xff, 0xf1, 0xf3, 0xf0, 0xff, 0xf1, 0xf3, 0xf1, 0xff, 0xf0, 0xf2, 0xf0, 0xff, 0xf0, 0xf2, 0xef, 0xff, 0xef, 0xf1, 0xef, 0xff, 0xef, 0xf1, 0xef, 0xff, 0xf0, 0xf1, 0xef, 0xff, 0xef, 0xf1, 0xef, 0xff, 0xf0, 0xf2, 0xef, 0xff, 0xf2, 0xf3, 0xf0, 0xff, 0xf3, 0xf5, 0xf1, 0xff, 0xf5, 0xf6, 0xf1, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf5, 0xf6, 0xf1, 0xff, 0xf4, 0xf4, 0xf0, 0xff, 0xf3, 0xf3, 0xef, 0xff, 0xf1, 0xf1, 0xec, 0xff, 0xf0, 0xef, 0xeb, 0xff, 0xf0, 0xf0, 0xeb, 0xff, 0xf1, 0xf1, 0xec, 0xff, 0xf2, 0xf2, 0xee, 0xff, 0xf3, 0xf3, 0xef, 0xff, 0xf2, 0xf4, 0xf0, 0xff, 0xf2, 0xf4, 0xf1, 0xff, 0xf3, 0xf4, 0xf1, 0xff, 0xf3, 0xf4, 0xf2, 0xff, 0xf3, 0xf4, 0xf2, 0xff, 0xf4, 0xf4, 0xf2, 0xff, 0xf5, 0xf5, 0xf3, 0xff, 0xf5, 0xf6, 0xf4, 0xff, 0xf5, 0xf6, 0xf4, 0xff, 0xf5, 0xf6, 0xf4, 0xff, 0xf4, 0xf5, 0xf3, 0xff, 0xf3, 0xf5, 0xf3, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf5, 0xf5, 0xf2, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf6, 0xf7, 0xf4, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xfa, 0xf8, 0xf3, 0xff, 0xfc, 0xfb, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb6, 0x9b, 0x98, 0xff, 0x3c, 0x00, 0x00, 0xff, 0x47, 0x03, 0x00, 0xff, 0x3d, 0x00, 0x00, 0xff, 0xa7, 0x35, 0x1d, 0xff, 0xa0, 0x38, 0x1e, 0xff, 0x27, 0x00, 0x00, 0xff, 0x34, 0x01, 0x00, 0xff, 0x2d, 0x00, 0x00, 0xff, 0x46, 0x09, 0x0f, 0xff, 0xf1, 0x47, 0x7e, 0xff, 0xff, 0x4b, 0x87, 0xff, 0xfd, 0x49, 0x84, 0xff, 0xfd, 0x47, 0x83, 0xff, 0xfc, 0x3f, 0x88, 0xff, 0xff, 0x94, 0x36, 0xff, 0xff, 0x73, 0x58, 0xff, 0xfc, 0x40, 0x8b, 0xff, 0xff, 0x48, 0x88, 0xff, 0xff, 0x4c, 0x8d, 0xff, 0xff, 0x4e, 0x8f, 0xff, 0xff, 0x4e, 0x91, 0xff, 0xff, 0x4f, 0x93, 0xff, 0xff, 0x4f, 0x95, 0xff, 0xff, 0x4f, 0x94, 0xff, 0xff, 0x4e, 0x94, 0xff, 0xff, 0x4d, 0x94, 0xff, 0xff, 0x4a, 0x90, 0xff, 0xfd, 0x49, 0x8e, 0xff, 0xfd, 0x44, 0x92, 0xff, 0xfd, 0x53, 0x84, 0xff, 0xff, 0xb7, 0x23, 0xff, 0xd2, 0x7a, 0x16, 0xff, 0xa3, 0x19, 0x30, 0xff, 0xa2, 0x1c, 0x2d, 0xff, 0xaf, 0x24, 0x2b, 0xff, 0xee, 0x7a, 0x1d, 0xff, 0xd0, 0x64, 0x19, 0xff, 0x90, 0x14, 0x26, 0xff, 0xa5, 0x1f, 0x2a, 0xff, 0xab, 0x27, 0x2b, 0xff, 0x99, 0x20, 0x2e, 0xff, 0x9e, 0x22, 0x2f, 0xff, 0x9f, 0x23, 0x30, 0xff, 0xa0, 0x23, 0x30, 0xff, 0x89, 0x1a, 0x23, 0xff, 0x3a, 0x00, 0x02, 0xff, + 0xfa, 0xf9, 0xf3, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf3, 0xf4, 0xf0, 0xff, 0xef, 0xf1, 0xee, 0xff, 0xed, 0xef, 0xed, 0xff, 0xec, 0xee, 0xed, 0xff, 0xec, 0xef, 0xed, 0xff, 0xed, 0xef, 0xed, 0xff, 0xef, 0xf0, 0xed, 0xff, 0xf0, 0xf2, 0xed, 0xff, 0xf1, 0xf2, 0xee, 0xff, 0xf2, 0xf3, 0xee, 0xff, 0xf1, 0xf2, 0xee, 0xff, 0xf1, 0xf3, 0xef, 0xff, 0xf2, 0xf3, 0xef, 0xff, 0xf0, 0xf3, 0xf0, 0xff, 0xf1, 0xf3, 0xf0, 0xff, 0xf1, 0xf3, 0xf0, 0xff, 0xf0, 0xf3, 0xf0, 0xff, 0xf0, 0xf2, 0xf0, 0xff, 0xf0, 0xf2, 0xf0, 0xff, 0xf0, 0xf2, 0xef, 0xff, 0xf1, 0xf2, 0xef, 0xff, 0xf1, 0xf3, 0xef, 0xff, 0xf2, 0xf3, 0xf0, 0xff, 0xf3, 0xf4, 0xf1, 0xff, 0xf4, 0xf5, 0xf1, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf5, 0xf6, 0xf3, 0xff, 0xf4, 0xf6, 0xf2, 0xff, 0xf4, 0xf6, 0xf2, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf4, 0xf5, 0xf1, 0xff, 0xf3, 0xf4, 0xf0, 0xff, 0xf3, 0xf2, 0xee, 0xff, 0xf1, 0xf0, 0xec, 0xff, 0xf0, 0xef, 0xeb, 0xff, 0xf0, 0xef, 0xeb, 0xff, 0xf0, 0xf0, 0xec, 0xff, 0xf1, 0xf2, 0xee, 0xff, 0xf2, 0xf4, 0xef, 0xff, 0xf3, 0xf4, 0xf0, 0xff, 0xf3, 0xf4, 0xf1, 0xff, 0xf3, 0xf4, 0xf1, 0xff, 0xf3, 0xf4, 0xf2, 0xff, 0xf3, 0xf4, 0xf2, 0xff, 0xf4, 0xf4, 0xf1, 0xff, 0xf5, 0xf5, 0xf2, 0xff, 0xf5, 0xf5, 0xf3, 0xff, 0xf5, 0xf5, 0xf3, 0xff, 0xf6, 0xf5, 0xf4, 0xff, 0xf5, 0xf5, 0xf3, 0xff, 0xf4, 0xf5, 0xf3, 0xff, 0xf4, 0xf5, 0xf3, 0xff, 0xf4, 0xf5, 0xf3, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf6, 0xf7, 0xf5, 0xff, 0xf6, 0xf7, 0xf4, 0xff, 0xfa, 0xf8, 0xf3, 0xff, 0xfe, 0xfc, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0x75, 0x48, 0x46, 0xff, 0x3d, 0x00, 0x00, 0xff, 0x44, 0x03, 0x00, 0xff, 0x4e, 0x06, 0x04, 0xff, 0xd8, 0x47, 0x30, 0xff, 0x4e, 0x0d, 0x08, 0xff, 0x2e, 0x00, 0x00, 0xff, 0x33, 0x01, 0x01, 0xff, 0x26, 0x00, 0x00, 0xff, 0x9a, 0x26, 0x44, 0xff, 0xff, 0x4c, 0x8b, 0xff, 0xfd, 0x48, 0x83, 0xff, 0xfe, 0x48, 0x83, 0xff, 0xfd, 0x47, 0x82, 0xff, 0xfd, 0x3f, 0x82, 0xff, 0xfc, 0xac, 0x22, 0xff, 0xf7, 0x58, 0x6a, 0xff, 0xf9, 0x34, 0x8b, 0xff, 0xfd, 0x3a, 0x89, 0xff, 0xfd, 0x3c, 0x8c, 0xff, 0xfe, 0x3d, 0x8e, 0xff, 0xfd, 0x3f, 0x90, 0xff, 0xfd, 0x3f, 0x92, 0xff, 0xfd, 0x3e, 0x93, 0xff, 0xfd, 0x3f, 0x91, 0xff, 0xfd, 0x3f, 0x90, 0xff, 0xfd, 0x40, 0x91, 0xff, 0xfd, 0x41, 0x92, 0xff, 0xfd, 0x3d, 0x95, 0xff, 0xfe, 0x50, 0x83, 0xff, 0xff, 0xb9, 0x1e, 0xff, 0xd3, 0x7b, 0x17, 0xff, 0xa7, 0x19, 0x34, 0xff, 0xa3, 0x1d, 0x2e, 0xff, 0xaa, 0x1f, 0x2d, 0xff, 0xee, 0x64, 0x2d, 0xff, 0xd6, 0x57, 0x26, 0xff, 0x95, 0x14, 0x27, 0xff, 0xb1, 0x25, 0x28, 0xff, 0xfb, 0x4e, 0x2d, 0xff, 0xd6, 0x3f, 0x2f, 0xff, 0x9c, 0x25, 0x2f, 0xff, 0xa0, 0x28, 0x2f, 0xff, 0xa3, 0x29, 0x31, 0xff, 0xa2, 0x28, 0x30, 0xff, 0x53, 0x0b, 0x0b, 0xff, 0x35, 0x00, 0x00, 0xff, + 0xf9, 0xfa, 0xf3, 0xff, 0xf7, 0xf8, 0xf3, 0xff, 0xf3, 0xf4, 0xf0, 0xff, 0xf0, 0xf1, 0xef, 0xff, 0xed, 0xef, 0xec, 0xff, 0xed, 0xee, 0xec, 0xff, 0xec, 0xee, 0xec, 0xff, 0xed, 0xef, 0xec, 0xff, 0xee, 0xf0, 0xec, 0xff, 0xef, 0xf1, 0xed, 0xff, 0xf1, 0xf2, 0xed, 0xff, 0xf1, 0xf2, 0xee, 0xff, 0xf1, 0xf3, 0xee, 0xff, 0xf1, 0xf3, 0xef, 0xff, 0xf1, 0xf3, 0xf0, 0xff, 0xf1, 0xf3, 0xf0, 0xff, 0xf1, 0xf3, 0xf0, 0xff, 0xf1, 0xf4, 0xf1, 0xff, 0xf1, 0xf3, 0xf0, 0xff, 0xf1, 0xf3, 0xf1, 0xff, 0xf1, 0xf3, 0xf0, 0xff, 0xf2, 0xf4, 0xf1, 0xff, 0xf2, 0xf4, 0xf0, 0xff, 0xf2, 0xf4, 0xf1, 0xff, 0xf3, 0xf5, 0xf1, 0xff, 0xf3, 0xf5, 0xf1, 0xff, 0xf4, 0xf6, 0xf2, 0xff, 0xf5, 0xf6, 0xf3, 0xff, 0xf5, 0xf6, 0xf3, 0xff, 0xf4, 0xf5, 0xf3, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf3, 0xf4, 0xf0, 0xff, 0xf2, 0xf3, 0xee, 0xff, 0xf1, 0xf0, 0xec, 0xff, 0xf0, 0xef, 0xeb, 0xff, 0xf0, 0xef, 0xeb, 0xff, 0xf1, 0xf0, 0xec, 0xff, 0xf2, 0xf2, 0xee, 0xff, 0xf3, 0xf3, 0xef, 0xff, 0xf3, 0xf4, 0xf1, 0xff, 0xf3, 0xf4, 0xf1, 0xff, 0xf3, 0xf4, 0xf2, 0xff, 0xf3, 0xf4, 0xf2, 0xff, 0xf3, 0xf4, 0xf2, 0xff, 0xf3, 0xf4, 0xf2, 0xff, 0xf4, 0xf4, 0xf1, 0xff, 0xf4, 0xf5, 0xf3, 0xff, 0xf5, 0xf5, 0xf3, 0xff, 0xf5, 0xf5, 0xf4, 0xff, 0xf5, 0xf5, 0xf3, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf4, 0xf5, 0xf3, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xff, 0xff, 0xfa, 0xff, 0xee, 0xea, 0xe6, 0xff, 0x4f, 0x17, 0x16, 0xff, 0x41, 0x00, 0x00, 0xff, 0x3e, 0x01, 0x00, 0xff, 0x78, 0x17, 0x15, 0xff, 0xc4, 0x39, 0x33, 0xff, 0x2d, 0x00, 0x00, 0xff, 0x32, 0x01, 0x01, 0xff, 0x2e, 0x00, 0x00, 0xff, 0x3d, 0x05, 0x09, 0xff, 0xe9, 0x40, 0x74, 0xff, 0xff, 0x48, 0x85, 0xff, 0xfd, 0x48, 0x82, 0xff, 0xfd, 0x47, 0x83, 0xff, 0xfd, 0x46, 0x82, 0xff, 0xfd, 0x3b, 0x83, 0xff, 0xfc, 0x92, 0x38, 0xff, 0xfb, 0xb0, 0x21, 0xff, 0xfb, 0x8e, 0x3b, 0xff, 0xfc, 0x8e, 0x3c, 0xff, 0xfc, 0x8c, 0x40, 0xff, 0xfc, 0x89, 0x45, 0xff, 0xfc, 0x88, 0x4a, 0xff, 0xfc, 0x86, 0x4b, 0xff, 0xfc, 0x7e, 0x4f, 0xff, 0xfc, 0x79, 0x53, 0xff, 0xfd, 0x79, 0x55, 0xff, 0xfd, 0x78, 0x59, 0xff, 0xfd, 0x76, 0x5c, 0xff, 0xfd, 0x7e, 0x59, 0xff, 0xff, 0xbf, 0x1e, 0xff, 0xd2, 0x77, 0x19, 0xff, 0xa3, 0x17, 0x33, 0xff, 0xa5, 0x1e, 0x30, 0xff, 0xa8, 0x1c, 0x2e, 0xff, 0xe7, 0x4e, 0x3a, 0xff, 0xd9, 0x4f, 0x34, 0xff, 0x99, 0x1a, 0x29, 0xff, 0xb5, 0x28, 0x28, 0xff, 0xfb, 0x4c, 0x2d, 0xff, 0xda, 0x40, 0x30, 0xff, 0xa0, 0x26, 0x30, 0xff, 0x9f, 0x27, 0x2f, 0xff, 0xa2, 0x29, 0x30, 0xff, 0xa6, 0x2a, 0x33, 0xff, 0x7d, 0x1c, 0x20, 0xff, 0x38, 0x00, 0x00, 0xff, 0x3c, 0x00, 0x00, 0xff, + 0xfa, 0xfa, 0xf4, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf4, 0xf5, 0xf1, 0xff, 0xf1, 0xf1, 0xef, 0xff, 0xee, 0xef, 0xed, 0xff, 0xed, 0xee, 0xec, 0xff, 0xec, 0xee, 0xec, 0xff, 0xed, 0xef, 0xec, 0xff, 0xed, 0xef, 0xec, 0xff, 0xef, 0xf0, 0xec, 0xff, 0xf0, 0xf1, 0xed, 0xff, 0xf1, 0xf2, 0xee, 0xff, 0xf1, 0xf2, 0xee, 0xff, 0xf2, 0xf3, 0xef, 0xff, 0xf1, 0xf3, 0xef, 0xff, 0xf1, 0xf4, 0xf0, 0xff, 0xf2, 0xf4, 0xf0, 0xff, 0xf2, 0xf4, 0xf0, 0xff, 0xf2, 0xf4, 0xf1, 0xff, 0xf2, 0xf5, 0xf1, 0xff, 0xf3, 0xf5, 0xf1, 0xff, 0xf3, 0xf5, 0xf1, 0xff, 0xf3, 0xf5, 0xf1, 0xff, 0xf3, 0xf5, 0xf2, 0xff, 0xf3, 0xf5, 0xf2, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf3, 0xf6, 0xf2, 0xff, 0xf4, 0xf6, 0xf3, 0xff, 0xf3, 0xf5, 0xf2, 0xff, 0xf3, 0xf5, 0xf3, 0xff, 0xf3, 0xf5, 0xf3, 0xff, 0xf2, 0xf5, 0xf2, 0xff, 0xf2, 0xf5, 0xf2, 0xff, 0xf3, 0xf5, 0xf2, 0xff, 0xf3, 0xf4, 0xf1, 0xff, 0xf2, 0xf3, 0xef, 0xff, 0xf1, 0xf1, 0xed, 0xff, 0xf0, 0xf0, 0xec, 0xff, 0xf0, 0xf0, 0xec, 0xff, 0xf1, 0xf1, 0xec, 0xff, 0xf1, 0xf2, 0xee, 0xff, 0xf3, 0xf3, 0xf0, 0xff, 0xf3, 0xf4, 0xf0, 0xff, 0xf3, 0xf4, 0xf1, 0xff, 0xf3, 0xf4, 0xf2, 0xff, 0xf3, 0xf4, 0xf1, 0xff, 0xf3, 0xf4, 0xf2, 0xff, 0xf3, 0xf4, 0xf2, 0xff, 0xf3, 0xf4, 0xf2, 0xff, 0xf3, 0xf4, 0xf2, 0xff, 0xf5, 0xf5, 0xf3, 0xff, 0xf5, 0xf6, 0xf4, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf5, 0xf5, 0xf2, 0xff, 0xf5, 0xf5, 0xf2, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf5, 0xf5, 0xf2, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf5, 0xf6, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xfb, 0xfa, 0xf5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xd4, 0xc7, 0xc4, 0xff, 0x3d, 0x00, 0x00, 0xff, 0x42, 0x02, 0x00, 0xff, 0x3c, 0x00, 0x00, 0xff, 0x9f, 0x25, 0x30, 0xff, 0x9b, 0x25, 0x32, 0xff, 0x27, 0x00, 0x00, 0xff, 0x31, 0x00, 0x02, 0xff, 0x25, 0x00, 0x00, 0xff, 0x81, 0x1e, 0x34, 0xff, 0xff, 0x4a, 0x87, 0xff, 0xfd, 0x47, 0x81, 0xff, 0xfd, 0x47, 0x81, 0xff, 0xfd, 0x46, 0x81, 0xff, 0xfd, 0x45, 0x80, 0xff, 0xfd, 0x41, 0x7e, 0xff, 0xfd, 0x42, 0x79, 0xff, 0xfd, 0x79, 0x50, 0xff, 0xfd, 0x8c, 0x43, 0xff, 0xfd, 0x8e, 0x44, 0xff, 0xfc, 0x90, 0x45, 0xff, 0xfc, 0x90, 0x44, 0xff, 0xfc, 0x92, 0x41, 0xff, 0xfc, 0x94, 0x3d, 0xff, 0xfd, 0x95, 0x3b, 0xff, 0xfd, 0x96, 0x39, 0xff, 0xfc, 0x99, 0x39, 0xff, 0xfd, 0x9f, 0x37, 0xff, 0xfd, 0xa4, 0x33, 0xff, 0xff, 0xa5, 0x37, 0xff, 0xd7, 0x60, 0x37, 0xff, 0xa6, 0x1a, 0x33, 0xff, 0xa6, 0x1e, 0x30, 0xff, 0xa4, 0x1b, 0x2e, 0xff, 0xdd, 0x3b, 0x3c, 0xff, 0xe2, 0x48, 0x3c, 0xff, 0x9b, 0x20, 0x2c, 0xff, 0x9a, 0x1f, 0x2b, 0xff, 0xe7, 0x43, 0x2c, 0xff, 0xd9, 0x3e, 0x30, 0xff, 0x9d, 0x25, 0x30, 0xff, 0x9f, 0x26, 0x30, 0xff, 0xa2, 0x29, 0x32, 0xff, 0xa4, 0x2a, 0x33, 0xff, 0xa4, 0x29, 0x32, 0xff, 0x54, 0x0b, 0x0b, 0xff, 0x3c, 0x01, 0x00, 0xff, 0x40, 0x01, 0x00, 0xff, + 0xfa, 0xfa, 0xf4, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf2, 0xf2, 0xf0, 0xff, 0xef, 0xf0, 0xee, 0xff, 0xee, 0xef, 0xed, 0xff, 0xed, 0xef, 0xec, 0xff, 0xed, 0xef, 0xec, 0xff, 0xee, 0xf0, 0xed, 0xff, 0xef, 0xf0, 0xec, 0xff, 0xf0, 0xf1, 0xed, 0xff, 0xf1, 0xf2, 0xed, 0xff, 0xf1, 0xf3, 0xee, 0xff, 0xf2, 0xf3, 0xef, 0xff, 0xf2, 0xf4, 0xf0, 0xff, 0xf3, 0xf4, 0xf1, 0xff, 0xf2, 0xf5, 0xf1, 0xff, 0xf2, 0xf5, 0xf1, 0xff, 0xf2, 0xf5, 0xf1, 0xff, 0xf3, 0xf5, 0xf2, 0xff, 0xf3, 0xf6, 0xf2, 0xff, 0xf4, 0xf6, 0xf3, 0xff, 0xf4, 0xf6, 0xf3, 0xff, 0xf4, 0xf6, 0xf3, 0xff, 0xf4, 0xf6, 0xf3, 0xff, 0xf3, 0xf6, 0xf2, 0xff, 0xf3, 0xf6, 0xf2, 0xff, 0xf3, 0xf5, 0xf2, 0xff, 0xf3, 0xf5, 0xf3, 0xff, 0xf1, 0xf4, 0xf2, 0xff, 0xf1, 0xf4, 0xf2, 0xff, 0xf1, 0xf4, 0xf2, 0xff, 0xf2, 0xf5, 0xf2, 0xff, 0xf3, 0xf5, 0xf3, 0xff, 0xf3, 0xf4, 0xf2, 0xff, 0xf3, 0xf4, 0xf1, 0xff, 0xf2, 0xf3, 0xee, 0xff, 0xf1, 0xf1, 0xed, 0xff, 0xf1, 0xf1, 0xed, 0xff, 0xf1, 0xf1, 0xed, 0xff, 0xf2, 0xf2, 0xee, 0xff, 0xf2, 0xf3, 0xef, 0xff, 0xf4, 0xf4, 0xf0, 0xff, 0xf3, 0xf4, 0xf0, 0xff, 0xf4, 0xf4, 0xf2, 0xff, 0xf4, 0xf4, 0xf1, 0xff, 0xf3, 0xf4, 0xf2, 0xff, 0xf3, 0xf4, 0xf2, 0xff, 0xf5, 0xf4, 0xf1, 0xff, 0xf4, 0xf4, 0xf2, 0xff, 0xf4, 0xf5, 0xf3, 0xff, 0xf5, 0xf6, 0xf4, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf6, 0xf3, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf5, 0xf5, 0xf3, 0xff, 0xf4, 0xf5, 0xf3, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xfa, 0xfa, 0xf4, 0xff, 0xfc, 0xfb, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc5, 0xb4, 0xb2, 0xff, 0x38, 0x00, 0x00, 0xff, 0x42, 0x01, 0x00, 0xff, 0x3a, 0x00, 0x00, 0xff, 0xb4, 0x2d, 0x4c, 0xff, 0x7d, 0x1c, 0x2f, 0xff, 0x26, 0x00, 0x00, 0xff, 0x2f, 0x00, 0x01, 0xff, 0x29, 0x00, 0x00, 0xff, 0xc9, 0x36, 0x61, 0xff, 0xff, 0x48, 0x83, 0xff, 0xfd, 0x46, 0x7e, 0xff, 0xfd, 0x46, 0x7e, 0xff, 0xfd, 0x46, 0x7f, 0xff, 0xfd, 0x46, 0x7f, 0xff, 0xfd, 0x44, 0x7e, 0xff, 0xfd, 0x42, 0x7e, 0xff, 0xfd, 0x3b, 0x82, 0xff, 0xfd, 0x3b, 0x84, 0xff, 0xfd, 0x3d, 0x86, 0xff, 0xfd, 0x3e, 0x87, 0xff, 0xfd, 0x3d, 0x87, 0xff, 0xfd, 0x3a, 0x87, 0xff, 0xfc, 0x3a, 0x87, 0xff, 0xfd, 0x3c, 0x88, 0xff, 0xfd, 0x3d, 0x87, 0xff, 0xfd, 0x3c, 0x85, 0xff, 0xfd, 0x3b, 0x84, 0xff, 0xff, 0x3c, 0x87, 0xff, 0xe9, 0x2c, 0x70, 0xff, 0xa9, 0x19, 0x36, 0xff, 0xa7, 0x21, 0x31, 0xff, 0xa5, 0x1d, 0x2f, 0xff, 0xd0, 0x2e, 0x3c, 0xff, 0xeb, 0x43, 0x43, 0xff, 0xa2, 0x23, 0x2d, 0xff, 0x9c, 0x23, 0x2d, 0xff, 0x9e, 0x23, 0x2d, 0xff, 0xa7, 0x27, 0x2f, 0xff, 0xa0, 0x25, 0x31, 0xff, 0x9f, 0x27, 0x31, 0xff, 0xa1, 0x28, 0x32, 0xff, 0xa3, 0x29, 0x33, 0xff, 0xa7, 0x2a, 0x35, 0xff, 0x90, 0x21, 0x28, 0xff, 0x40, 0x03, 0x01, 0xff, 0x42, 0x03, 0x00, 0xff, 0x41, 0x02, 0x01, 0xff, + 0xfa, 0xfa, 0xf4, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf2, 0xf3, 0xf1, 0xff, 0xf0, 0xf2, 0xef, 0xff, 0xef, 0xf1, 0xee, 0xff, 0xef, 0xf1, 0xee, 0xff, 0xef, 0xf1, 0xed, 0xff, 0xef, 0xf1, 0xed, 0xff, 0xef, 0xf1, 0xed, 0xff, 0xf0, 0xf1, 0xed, 0xff, 0xf1, 0xf2, 0xed, 0xff, 0xf1, 0xf2, 0xee, 0xff, 0xf2, 0xf3, 0xef, 0xff, 0xf3, 0xf4, 0xef, 0xff, 0xf3, 0xf4, 0xf0, 0xff, 0xf3, 0xf5, 0xf1, 0xff, 0xf2, 0xf5, 0xf1, 0xff, 0xf2, 0xf5, 0xf2, 0xff, 0xf3, 0xf6, 0xf2, 0xff, 0xf3, 0xf6, 0xf3, 0xff, 0xf4, 0xf6, 0xf3, 0xff, 0xf4, 0xf7, 0xf3, 0xff, 0xf4, 0xf6, 0xf3, 0xff, 0xf4, 0xf6, 0xf3, 0xff, 0xf4, 0xf6, 0xf3, 0xff, 0xf3, 0xf6, 0xf2, 0xff, 0xf2, 0xf5, 0xf2, 0xff, 0xf2, 0xf4, 0xf2, 0xff, 0xf1, 0xf4, 0xf2, 0xff, 0xf1, 0xf4, 0xf2, 0xff, 0xf1, 0xf4, 0xf2, 0xff, 0xf1, 0xf4, 0xf2, 0xff, 0xf2, 0xf5, 0xf3, 0xff, 0xf3, 0xf5, 0xf3, 0xff, 0xf3, 0xf5, 0xf1, 0xff, 0xf3, 0xf4, 0xf0, 0xff, 0xf2, 0xf3, 0xef, 0xff, 0xf2, 0xf2, 0xee, 0xff, 0xf2, 0xf2, 0xee, 0xff, 0xf2, 0xf3, 0xef, 0xff, 0xf2, 0xf4, 0xef, 0xff, 0xf3, 0xf4, 0xf0, 0xff, 0xf4, 0xf5, 0xf1, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf4, 0xf4, 0xf2, 0xff, 0xf3, 0xf4, 0xf1, 0xff, 0xf3, 0xf4, 0xf2, 0xff, 0xf4, 0xf4, 0xf0, 0xff, 0xf4, 0xf5, 0xf3, 0xff, 0xf4, 0xf5, 0xf3, 0xff, 0xf5, 0xf5, 0xf4, 0xff, 0xf5, 0xf6, 0xf4, 0xff, 0xf7, 0xf6, 0xf3, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf6, 0xf6, 0xf4, 0xff, 0xf5, 0xf6, 0xf4, 0xff, 0xf5, 0xf6, 0xf4, 0xff, 0xf5, 0xf6, 0xf4, 0xff, 0xf5, 0xf6, 0xf4, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf9, 0xf9, 0xf4, 0xff, 0xfb, 0xfa, 0xf5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc9, 0xb9, 0xb5, 0xff, 0x38, 0x00, 0x00, 0xff, 0x3f, 0x00, 0x00, 0xff, 0x37, 0x00, 0x00, 0xff, 0xb6, 0x2d, 0x55, 0xff, 0x71, 0x18, 0x2d, 0xff, 0x25, 0x00, 0x00, 0xff, 0x29, 0x00, 0x00, 0xff, 0x4f, 0x0d, 0x16, 0xff, 0xf9, 0x45, 0x7c, 0xff, 0xff, 0x45, 0x7d, 0xff, 0xfd, 0x44, 0x7c, 0xff, 0xfd, 0x45, 0x7d, 0xff, 0xfd, 0x46, 0x7d, 0xff, 0xfd, 0x46, 0x7e, 0xff, 0xfd, 0x44, 0x7e, 0xff, 0xfd, 0x42, 0x7e, 0xff, 0xfd, 0x41, 0x7d, 0xff, 0xfd, 0x42, 0x7e, 0xff, 0xfd, 0x44, 0x80, 0xff, 0xfd, 0x45, 0x81, 0xff, 0xfd, 0x45, 0x80, 0xff, 0xfd, 0x45, 0x80, 0xff, 0xfd, 0x44, 0x80, 0xff, 0xfd, 0x45, 0x80, 0xff, 0xfd, 0x44, 0x80, 0xff, 0xfd, 0x44, 0x7f, 0xff, 0xfd, 0x43, 0x7f, 0xff, 0xfc, 0x40, 0x7b, 0xff, 0xb9, 0x23, 0x40, 0xff, 0xa4, 0x1f, 0x30, 0xff, 0xa5, 0x1f, 0x31, 0xff, 0xc2, 0x28, 0x39, 0xff, 0xf3, 0x40, 0x48, 0xff, 0xac, 0x25, 0x31, 0xff, 0x9a, 0x22, 0x2c, 0xff, 0x9f, 0x25, 0x2f, 0xff, 0xa0, 0x26, 0x30, 0xff, 0xa0, 0x26, 0x30, 0xff, 0xa1, 0x27, 0x31, 0xff, 0xa1, 0x28, 0x32, 0xff, 0xa3, 0x29, 0x33, 0xff, 0xa4, 0x29, 0x34, 0xff, 0xaa, 0x2b, 0x37, 0xff, 0x77, 0x17, 0x1c, 0xff, 0x3c, 0x01, 0x00, 0xff, 0x43, 0x04, 0x01, 0xff, 0x43, 0x02, 0x01, 0xff, + 0xfa, 0xfa, 0xf4, 0xff, 0xf8, 0xf8, 0xf4, 0xff, 0xf5, 0xf7, 0xf3, 0xff, 0xf3, 0xf5, 0xf2, 0xff, 0xf2, 0xf4, 0xf1, 0xff, 0xf1, 0xf4, 0xf1, 0xff, 0xf1, 0xf4, 0xf1, 0xff, 0xf0, 0xf3, 0xf0, 0xff, 0xf1, 0xf3, 0xef, 0xff, 0xf1, 0xf2, 0xee, 0xff, 0xf1, 0xf2, 0xee, 0xff, 0xf1, 0xf2, 0xed, 0xff, 0xf2, 0xf3, 0xee, 0xff, 0xf2, 0xf3, 0xee, 0xff, 0xf3, 0xf4, 0xef, 0xff, 0xf3, 0xf4, 0xf0, 0xff, 0xf3, 0xf5, 0xf1, 0xff, 0xf3, 0xf5, 0xf2, 0xff, 0xf3, 0xf5, 0xf2, 0xff, 0xf3, 0xf6, 0xf3, 0xff, 0xf3, 0xf6, 0xf3, 0xff, 0xf4, 0xf7, 0xf3, 0xff, 0xf5, 0xf7, 0xf4, 0xff, 0xf5, 0xf7, 0xf3, 0xff, 0xf4, 0xf7, 0xf3, 0xff, 0xf4, 0xf6, 0xf3, 0xff, 0xf3, 0xf6, 0xf2, 0xff, 0xf2, 0xf5, 0xf2, 0xff, 0xf2, 0xf5, 0xf2, 0xff, 0xf1, 0xf4, 0xf2, 0xff, 0xf1, 0xf4, 0xf1, 0xff, 0xf1, 0xf4, 0xf2, 0xff, 0xf1, 0xf4, 0xf2, 0xff, 0xf2, 0xf5, 0xf3, 0xff, 0xf3, 0xf5, 0xf3, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf3, 0xf4, 0xf0, 0xff, 0xf3, 0xf4, 0xf0, 0xff, 0xf3, 0xf4, 0xf0, 0xff, 0xf3, 0xf4, 0xf0, 0xff, 0xf4, 0xf4, 0xf0, 0xff, 0xf4, 0xf5, 0xf1, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf3, 0xf4, 0xf2, 0xff, 0xf4, 0xf4, 0xf1, 0xff, 0xf4, 0xf4, 0xf2, 0xff, 0xf5, 0xf5, 0xf3, 0xff, 0xf5, 0xf5, 0xf3, 0xff, 0xf5, 0xf5, 0xf3, 0xff, 0xf6, 0xf4, 0xf3, 0xff, 0xf6, 0xf4, 0xf3, 0xff, 0xf5, 0xf5, 0xf4, 0xff, 0xf5, 0xf6, 0xf4, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf5, 0xf6, 0xf4, 0xff, 0xf5, 0xf6, 0xf4, 0xff, 0xf6, 0xf7, 0xf4, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xfb, 0xfa, 0xf5, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xe4, 0xdb, 0xd7, 0xff, 0x41, 0x09, 0x09, 0xff, 0x3b, 0x00, 0x00, 0xff, 0x35, 0x00, 0x00, 0xff, 0xac, 0x26, 0x4f, 0xff, 0x7d, 0x18, 0x35, 0xff, 0x1d, 0x00, 0x00, 0xff, 0x21, 0x00, 0x00, 0xff, 0x91, 0x21, 0x3b, 0xff, 0xff, 0x47, 0x82, 0xff, 0xfd, 0x43, 0x79, 0xff, 0xfd, 0x43, 0x7a, 0xff, 0xfd, 0x44, 0x7c, 0xff, 0xfd, 0x43, 0x7d, 0xff, 0xfd, 0x44, 0x7e, 0xff, 0xfd, 0x43, 0x7d, 0xff, 0xfd, 0x42, 0x7d, 0xff, 0xfd, 0x43, 0x7d, 0xff, 0xfc, 0x44, 0x7d, 0xff, 0xfd, 0x45, 0x7f, 0xff, 0xfd, 0x45, 0x80, 0xff, 0xfd, 0x46, 0x80, 0xff, 0xfd, 0x46, 0x80, 0xff, 0xfd, 0x45, 0x80, 0xff, 0xfd, 0x44, 0x7f, 0xff, 0xfd, 0x42, 0x7f, 0xff, 0xfd, 0x42, 0x7e, 0xff, 0xff, 0x43, 0x81, 0xff, 0xdf, 0x32, 0x60, 0xff, 0xa5, 0x1e, 0x31, 0xff, 0xa6, 0x20, 0x33, 0xff, 0xb3, 0x22, 0x36, 0xff, 0xf4, 0x3f, 0x4b, 0xff, 0xbf, 0x2c, 0x39, 0xff, 0x9a, 0x21, 0x2d, 0xff, 0x9f, 0x25, 0x2f, 0xff, 0xa0, 0x26, 0x30, 0xff, 0xa1, 0x27, 0x31, 0xff, 0xa2, 0x27, 0x32, 0xff, 0xa3, 0x28, 0x32, 0xff, 0xa3, 0x2a, 0x33, 0xff, 0xa3, 0x2a, 0x34, 0xff, 0xa4, 0x2a, 0x34, 0xff, 0xab, 0x2b, 0x36, 0xff, 0x66, 0x11, 0x13, 0xff, 0x3c, 0x02, 0x00, 0xff, 0x42, 0x04, 0x02, 0xff, 0x42, 0x02, 0x01, 0xff, + 0xfa, 0xfa, 0xf5, 0xff, 0xf8, 0xf8, 0xf4, 0xff, 0xf6, 0xf7, 0xf4, 0xff, 0xf4, 0xf6, 0xf3, 0xff, 0xf3, 0xf6, 0xf3, 0xff, 0xf2, 0xf6, 0xf3, 0xff, 0xf2, 0xf6, 0xf3, 0xff, 0xf2, 0xf6, 0xf2, 0xff, 0xf3, 0xf5, 0xf1, 0xff, 0xf3, 0xf4, 0xf0, 0xff, 0xf2, 0xf4, 0xef, 0xff, 0xf2, 0xf3, 0xee, 0xff, 0xf2, 0xf3, 0xee, 0xff, 0xf2, 0xf3, 0xee, 0xff, 0xf3, 0xf3, 0xef, 0xff, 0xf3, 0xf4, 0xef, 0xff, 0xf3, 0xf4, 0xf0, 0xff, 0xf3, 0xf5, 0xf1, 0xff, 0xf2, 0xf5, 0xf1, 0xff, 0xf3, 0xf6, 0xf2, 0xff, 0xf4, 0xf6, 0xf3, 0xff, 0xf4, 0xf7, 0xf4, 0xff, 0xf5, 0xf7, 0xf4, 0xff, 0xf5, 0xf7, 0xf4, 0xff, 0xf5, 0xf8, 0xf3, 0xff, 0xf4, 0xf7, 0xf3, 0xff, 0xf4, 0xf7, 0xf3, 0xff, 0xf3, 0xf6, 0xf2, 0xff, 0xf2, 0xf5, 0xf2, 0xff, 0xf1, 0xf4, 0xf2, 0xff, 0xf0, 0xf4, 0xf1, 0xff, 0xf1, 0xf4, 0xf2, 0xff, 0xf1, 0xf4, 0xf2, 0xff, 0xf2, 0xf5, 0xf3, 0xff, 0xf3, 0xf5, 0xf3, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf4, 0xf6, 0xf2, 0xff, 0xf4, 0xf6, 0xf2, 0xff, 0xf4, 0xf5, 0xf1, 0xff, 0xf4, 0xf5, 0xf1, 0xff, 0xf4, 0xf5, 0xf1, 0xff, 0xf4, 0xf5, 0xf1, 0xff, 0xf4, 0xf6, 0xf2, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf5, 0xf6, 0xf3, 0xff, 0xf4, 0xf5, 0xf3, 0xff, 0xf4, 0xf4, 0xf1, 0xff, 0xf5, 0xf5, 0xf3, 0xff, 0xf5, 0xf5, 0xf2, 0xff, 0xf5, 0xf5, 0xf3, 0xff, 0xf5, 0xf5, 0xf3, 0xff, 0xf5, 0xf5, 0xf2, 0xff, 0xf6, 0xf5, 0xf2, 0xff, 0xf5, 0xf5, 0xf4, 0xff, 0xf5, 0xf6, 0xf3, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf6, 0xf7, 0xf4, 0xff, 0xf6, 0xf7, 0xf5, 0xff, 0xf5, 0xf6, 0xf4, 0xff, 0xf6, 0xf7, 0xf4, 0xff, 0xf8, 0xf8, 0xf2, 0xff, 0xfa, 0xfa, 0xf5, 0xff, 0xfd, 0xfc, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7d, 0x54, 0x53, 0xff, 0x2f, 0x00, 0x00, 0xff, 0x32, 0x00, 0x00, 0xff, 0x79, 0x14, 0x2b, 0xff, 0xca, 0x28, 0x61, 0xff, 0x24, 0x00, 0x00, 0xff, 0x1c, 0x00, 0x00, 0xff, 0xcd, 0x34, 0x5d, 0xff, 0xff, 0x44, 0x7c, 0xff, 0xfd, 0x43, 0x76, 0xff, 0xfd, 0x44, 0x77, 0xff, 0xfd, 0x44, 0x79, 0xff, 0xfd, 0x43, 0x7c, 0xff, 0xfd, 0x42, 0x7e, 0xff, 0xfd, 0x42, 0x7d, 0xff, 0xfd, 0x43, 0x7d, 0xff, 0xfd, 0x45, 0x7c, 0xff, 0xfd, 0x46, 0x7d, 0xff, 0xfd, 0x46, 0x7f, 0xff, 0xfd, 0x45, 0x80, 0xff, 0xfd, 0x46, 0x81, 0xff, 0xfd, 0x46, 0x80, 0xff, 0xfd, 0x45, 0x80, 0xff, 0xfd, 0x44, 0x7f, 0xff, 0xfd, 0x42, 0x7d, 0xff, 0xfe, 0x42, 0x7e, 0xff, 0xfd, 0x40, 0x7c, 0xff, 0xbb, 0x25, 0x41, 0xff, 0xa5, 0x1f, 0x30, 0xff, 0xa7, 0x1e, 0x32, 0xff, 0xe4, 0x3c, 0x48, 0xff, 0xd9, 0x38, 0x44, 0xff, 0x9e, 0x21, 0x31, 0xff, 0xa1, 0x25, 0x31, 0xff, 0xa0, 0x27, 0x32, 0xff, 0xa1, 0x28, 0x32, 0xff, 0xa1, 0x28, 0x33, 0xff, 0xa1, 0x28, 0x32, 0xff, 0xa3, 0x29, 0x33, 0xff, 0xa4, 0x2a, 0x34, 0xff, 0xa3, 0x2b, 0x34, 0xff, 0xa4, 0x2b, 0x35, 0xff, 0xaa, 0x2c, 0x36, 0xff, 0x61, 0x0f, 0x11, 0xff, 0x3a, 0x01, 0x00, 0xff, 0x41, 0x04, 0x01, 0xff, 0x41, 0x02, 0x01, 0xff, + 0xfa, 0xfa, 0xf5, 0xff, 0xf7, 0xf9, 0xf4, 0xff, 0xf6, 0xf7, 0xf4, 0xff, 0xf4, 0xf6, 0xf4, 0xff, 0xf3, 0xf6, 0xf4, 0xff, 0xf4, 0xf6, 0xf4, 0xff, 0xf4, 0xf7, 0xf5, 0xff, 0xf4, 0xf6, 0xf4, 0xff, 0xf4, 0xf6, 0xf3, 0xff, 0xf4, 0xf6, 0xf2, 0xff, 0xf4, 0xf5, 0xf1, 0xff, 0xf4, 0xf4, 0xf0, 0xff, 0xf4, 0xf4, 0xee, 0xff, 0xf3, 0xf3, 0xee, 0xff, 0xf3, 0xf3, 0xee, 0xff, 0xf3, 0xf3, 0xef, 0xff, 0xf3, 0xf3, 0xef, 0xff, 0xf3, 0xf4, 0xf0, 0xff, 0xf3, 0xf5, 0xf1, 0xff, 0xf4, 0xf6, 0xf2, 0xff, 0xf4, 0xf7, 0xf3, 0xff, 0xf5, 0xf7, 0xf4, 0xff, 0xf6, 0xf8, 0xf4, 0xff, 0xf6, 0xf8, 0xf4, 0xff, 0xf7, 0xf8, 0xf4, 0xff, 0xf6, 0xf8, 0xf4, 0xff, 0xf5, 0xf8, 0xf4, 0xff, 0xf5, 0xf7, 0xf3, 0xff, 0xf4, 0xf6, 0xf2, 0xff, 0xf3, 0xf5, 0xf2, 0xff, 0xf2, 0xf4, 0xf1, 0xff, 0xf1, 0xf4, 0xf2, 0xff, 0xf1, 0xf4, 0xf2, 0xff, 0xf1, 0xf4, 0xf2, 0xff, 0xf4, 0xf5, 0xf3, 0xff, 0xf4, 0xf6, 0xf2, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf7, 0xf2, 0xff, 0xf6, 0xf7, 0xf2, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf5, 0xf6, 0xf3, 0xff, 0xf5, 0xf6, 0xf4, 0xff, 0xf5, 0xf6, 0xf4, 0xff, 0xf5, 0xf6, 0xf3, 0xff, 0xf5, 0xf6, 0xf4, 0xff, 0xf5, 0xf6, 0xf4, 0xff, 0xf5, 0xf6, 0xf4, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf5, 0xf6, 0xf4, 0xff, 0xf5, 0xf6, 0xf4, 0xff, 0xf6, 0xf7, 0xf4, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf6, 0xf7, 0xf5, 0xff, 0xf5, 0xf6, 0xf4, 0xff, 0xf4, 0xf6, 0xf4, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xfc, 0xfa, 0xf5, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xed, 0xdc, 0xdd, 0xff, 0x58, 0x14, 0x1d, 0xff, 0x26, 0x00, 0x00, 0xff, 0x2c, 0x00, 0x00, 0xff, 0xa9, 0x20, 0x4d, 0xff, 0xc2, 0x24, 0x5d, 0xff, 0x58, 0x0d, 0x1f, 0xff, 0xef, 0x3e, 0x6f, 0xff, 0xff, 0x43, 0x77, 0xff, 0xfd, 0x43, 0x75, 0xff, 0xfd, 0x44, 0x77, 0xff, 0xfd, 0x45, 0x78, 0xff, 0xfd, 0x44, 0x79, 0xff, 0xfd, 0x42, 0x79, 0xff, 0xfd, 0x43, 0x7a, 0xff, 0xfd, 0x44, 0x7c, 0xff, 0xfd, 0x45, 0x7c, 0xff, 0xfd, 0x46, 0x7d, 0xff, 0xfd, 0x46, 0x7d, 0xff, 0xfd, 0x45, 0x7d, 0xff, 0xfd, 0x45, 0x7e, 0xff, 0xfd, 0x46, 0x80, 0xff, 0xfd, 0x46, 0x80, 0xff, 0xfd, 0x44, 0x7e, 0xff, 0xfd, 0x41, 0x7d, 0xff, 0xff, 0x42, 0x7e, 0xff, 0xe9, 0x37, 0x68, 0xff, 0xa7, 0x1e, 0x31, 0xff, 0xa4, 0x1d, 0x2f, 0xff, 0xcb, 0x30, 0x40, 0xff, 0xef, 0x46, 0x4e, 0xff, 0xa3, 0x22, 0x32, 0xff, 0x9f, 0x24, 0x32, 0xff, 0xa2, 0x27, 0x33, 0xff, 0xa2, 0x28, 0x35, 0xff, 0xa3, 0x2a, 0x35, 0xff, 0xa4, 0x2a, 0x35, 0xff, 0xa3, 0x29, 0x33, 0xff, 0xa3, 0x29, 0x33, 0xff, 0xa3, 0x29, 0x33, 0xff, 0xa3, 0x29, 0x33, 0xff, 0xa3, 0x2a, 0x34, 0xff, 0xaa, 0x2b, 0x36, 0xff, 0x6c, 0x13, 0x16, 0xff, 0x38, 0x02, 0x00, 0xff, 0x40, 0x04, 0x02, 0xff, 0x41, 0x02, 0x01, 0xff, + 0xf9, 0xfa, 0xf5, 0xff, 0xf8, 0xf9, 0xf4, 0xff, 0xf6, 0xf7, 0xf4, 0xff, 0xf5, 0xf7, 0xf5, 0xff, 0xf4, 0xf6, 0xf4, 0xff, 0xf4, 0xf7, 0xf5, 0xff, 0xf5, 0xf7, 0xf5, 0xff, 0xf6, 0xf7, 0xf5, 0xff, 0xf6, 0xf7, 0xf4, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf6, 0xf7, 0xf2, 0xff, 0xf5, 0xf6, 0xf1, 0xff, 0xf5, 0xf5, 0xf0, 0xff, 0xf4, 0xf4, 0xef, 0xff, 0xf3, 0xf3, 0xef, 0xff, 0xf4, 0xf3, 0xef, 0xff, 0xf3, 0xf3, 0xef, 0xff, 0xf3, 0xf4, 0xf0, 0xff, 0xf4, 0xf5, 0xf1, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf5, 0xf7, 0xf3, 0xff, 0xf7, 0xf8, 0xf4, 0xff, 0xf7, 0xf8, 0xf4, 0xff, 0xf7, 0xf9, 0xf4, 0xff, 0xf7, 0xf9, 0xf4, 0xff, 0xf8, 0xf8, 0xf4, 0xff, 0xf7, 0xf9, 0xf4, 0xff, 0xf7, 0xf8, 0xf4, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf5, 0xf6, 0xf3, 0xff, 0xf3, 0xf5, 0xf2, 0xff, 0xf2, 0xf4, 0xf2, 0xff, 0xf2, 0xf4, 0xf2, 0xff, 0xf2, 0xf4, 0xf2, 0xff, 0xf4, 0xf5, 0xf3, 0xff, 0xf5, 0xf6, 0xf3, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf5, 0xf7, 0xf3, 0xff, 0xf5, 0xf6, 0xf4, 0xff, 0xf5, 0xf6, 0xf4, 0xff, 0xf5, 0xf6, 0xf4, 0xff, 0xf5, 0xf6, 0xf4, 0xff, 0xf6, 0xf6, 0xf5, 0xff, 0xf5, 0xf6, 0xf5, 0xff, 0xf5, 0xf6, 0xf4, 0xff, 0xf5, 0xf6, 0xf3, 0xff, 0xf6, 0xf6, 0xf4, 0xff, 0xf5, 0xf6, 0xf5, 0xff, 0xf6, 0xf6, 0xf4, 0xff, 0xf6, 0xf7, 0xf5, 0xff, 0xf6, 0xf7, 0xf5, 0xff, 0xf5, 0xf6, 0xf4, 0xff, 0xf5, 0xf6, 0xf4, 0xff, 0xf4, 0xf5, 0xf4, 0xff, 0xf4, 0xf6, 0xf4, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xfc, 0xfa, 0xf5, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xdc, 0xc4, 0xc6, 0xff, 0xcd, 0x34, 0x6e, 0xff, 0x7c, 0x16, 0x36, 0xff, 0x1f, 0x00, 0x00, 0xff, 0x17, 0x00, 0x00, 0xff, 0x79, 0x14, 0x35, 0xff, 0xd5, 0x28, 0x60, 0xff, 0xfe, 0x3f, 0x72, 0xff, 0xfd, 0x41, 0x74, 0xff, 0xfd, 0x42, 0x75, 0xff, 0xfd, 0x43, 0x76, 0xff, 0xfd, 0x43, 0x76, 0xff, 0xfd, 0x43, 0x76, 0xff, 0xfd, 0x43, 0x76, 0xff, 0xfd, 0x44, 0x78, 0xff, 0xfd, 0x44, 0x7b, 0xff, 0xfd, 0x45, 0x7b, 0xff, 0xfd, 0x46, 0x7a, 0xff, 0xfd, 0x46, 0x79, 0xff, 0xfd, 0x45, 0x7a, 0xff, 0xfd, 0x45, 0x7b, 0xff, 0xfc, 0x45, 0x7c, 0xff, 0xfd, 0x44, 0x7d, 0xff, 0xfd, 0x43, 0x7c, 0xff, 0xfd, 0x41, 0x7a, 0xff, 0xff, 0x40, 0x7b, 0xff, 0xc5, 0x29, 0x4a, 0xff, 0x9f, 0x1d, 0x2c, 0xff, 0xb1, 0x21, 0x32, 0xff, 0xf4, 0x48, 0x50, 0xff, 0xb8, 0x2e, 0x3a, 0xff, 0x9b, 0x22, 0x2f, 0xff, 0xa1, 0x26, 0x32, 0xff, 0xa1, 0x28, 0x33, 0xff, 0xa2, 0x29, 0x36, 0xff, 0xa4, 0x2a, 0x36, 0xff, 0xa5, 0x2b, 0x35, 0xff, 0xa3, 0x28, 0x32, 0xff, 0xa2, 0x24, 0x2e, 0xff, 0xa2, 0x24, 0x2e, 0xff, 0xa1, 0x26, 0x30, 0xff, 0xa1, 0x26, 0x30, 0xff, 0xa6, 0x25, 0x30, 0xff, 0x74, 0x12, 0x13, 0xff, 0x37, 0x00, 0x00, 0xff, 0x3f, 0x02, 0x01, 0xff, 0x41, 0x01, 0x00, 0xff, + 0xfa, 0xf9, 0xf4, 0xff, 0xf8, 0xf8, 0xf4, 0xff, 0xf7, 0xf8, 0xf4, 0xff, 0xf6, 0xf7, 0xf4, 0xff, 0xf5, 0xf7, 0xf4, 0xff, 0xf6, 0xf7, 0xf4, 0xff, 0xf6, 0xf7, 0xf4, 0xff, 0xf7, 0xf7, 0xf4, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf2, 0xff, 0xf7, 0xf6, 0xf1, 0xff, 0xf6, 0xf5, 0xf0, 0xff, 0xf5, 0xf4, 0xf0, 0xff, 0xf5, 0xf4, 0xef, 0xff, 0xf5, 0xf4, 0xef, 0xff, 0xf5, 0xf4, 0xf0, 0xff, 0xf5, 0xf5, 0xf1, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf8, 0xf8, 0xf4, 0xff, 0xf8, 0xf9, 0xf4, 0xff, 0xf9, 0xf9, 0xf4, 0xff, 0xf8, 0xf9, 0xf5, 0xff, 0xf9, 0xf9, 0xf5, 0xff, 0xf9, 0xf9, 0xf5, 0xff, 0xf8, 0xf9, 0xf4, 0xff, 0xf8, 0xf8, 0xf4, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf5, 0xf6, 0xf3, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf4, 0xf5, 0xf2, 0xff, 0xf5, 0xf5, 0xf2, 0xff, 0xf5, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf6, 0xf4, 0xff, 0xf5, 0xf6, 0xf4, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf6, 0xf7, 0xf4, 0xff, 0xf6, 0xf6, 0xf5, 0xff, 0xf6, 0xf7, 0xf4, 0xff, 0xf6, 0xf6, 0xf4, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf4, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf6, 0xf7, 0xf4, 0xff, 0xf6, 0xf7, 0xf4, 0xff, 0xf5, 0xf6, 0xf4, 0xff, 0xf4, 0xf6, 0xf3, 0xff, 0xf5, 0xf6, 0xf3, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf8, 0xf7, 0xf3, 0xff, 0xfa, 0xf9, 0xf5, 0xff, 0xfd, 0xfb, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0x61, 0x5c, 0xff, 0x4d, 0x00, 0x09, 0xff, 0xc6, 0x2f, 0x67, 0xff, 0xba, 0x2a, 0x62, 0xff, 0x42, 0x0a, 0x16, 0xff, 0x00, 0x00, 0x00, 0xff, 0x93, 0x21, 0x3d, 0xff, 0xff, 0x41, 0x76, 0xff, 0xfd, 0x3f, 0x71, 0xff, 0xfd, 0x40, 0x72, 0xff, 0xfd, 0x41, 0x73, 0xff, 0xfd, 0x42, 0x74, 0xff, 0xfd, 0x42, 0x74, 0xff, 0xfd, 0x44, 0x75, 0xff, 0xfd, 0x44, 0x77, 0xff, 0xfd, 0x45, 0x7a, 0xff, 0xfd, 0x46, 0x7a, 0xff, 0xfd, 0x46, 0x78, 0xff, 0xfd, 0x45, 0x77, 0xff, 0xfd, 0x44, 0x77, 0xff, 0xfd, 0x44, 0x79, 0xff, 0xfd, 0x44, 0x7a, 0xff, 0xfd, 0x43, 0x7a, 0xff, 0xfd, 0x40, 0x78, 0xff, 0xff, 0x3f, 0x78, 0xff, 0xf4, 0x37, 0x6e, 0xff, 0xa9, 0x20, 0x33, 0xff, 0x9d, 0x1d, 0x2b, 0xff, 0xd8, 0x33, 0x3c, 0xff, 0xde, 0x3c, 0x40, 0xff, 0x9b, 0x22, 0x2c, 0xff, 0x9f, 0x24, 0x30, 0xff, 0xa0, 0x26, 0x31, 0xff, 0xa1, 0x27, 0x32, 0xff, 0xa1, 0x28, 0x33, 0xff, 0xa3, 0x29, 0x34, 0xff, 0xa3, 0x27, 0x32, 0xff, 0xa8, 0x3b, 0x44, 0xff, 0xaf, 0x54, 0x5c, 0xff, 0xae, 0x4e, 0x55, 0xff, 0xaa, 0x41, 0x49, 0xff, 0xac, 0x41, 0x4a, 0xff, 0xa4, 0x44, 0x49, 0xff, 0x85, 0x32, 0x2f, 0xff, 0x65, 0x32, 0x30, 0xff, 0x43, 0x07, 0x06, 0xff, 0x40, 0x00, 0x00, 0xff, + 0xfb, 0xfa, 0xf5, 0xff, 0xfa, 0xf9, 0xf5, 0xff, 0xf8, 0xf8, 0xf4, 0xff, 0xf6, 0xf7, 0xf4, 0xff, 0xf6, 0xf7, 0xf5, 0xff, 0xf7, 0xf7, 0xf4, 0xff, 0xf7, 0xf8, 0xf4, 0xff, 0xf8, 0xf8, 0xf4, 0xff, 0xf9, 0xf8, 0xf4, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf9, 0xf7, 0xf2, 0xff, 0xf8, 0xf7, 0xf1, 0xff, 0xf7, 0xf7, 0xf1, 0xff, 0xf7, 0xf6, 0xf1, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf9, 0xf9, 0xf4, 0xff, 0xfa, 0xf9, 0xf5, 0xff, 0xfa, 0xfa, 0xf5, 0xff, 0xfa, 0xfa, 0xf6, 0xff, 0xfb, 0xfa, 0xf6, 0xff, 0xfa, 0xfa, 0xf5, 0xff, 0xfa, 0xfa, 0xf5, 0xff, 0xfa, 0xfa, 0xf5, 0xff, 0xf9, 0xfa, 0xf5, 0xff, 0xf9, 0xf9, 0xf4, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf6, 0xf6, 0xf3, 0xff, 0xf6, 0xf7, 0xf3, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf7, 0xf7, 0xf2, 0xff, 0xf6, 0xf7, 0xf2, 0xff, 0xf6, 0xf6, 0xf2, 0xff, 0xf7, 0xf7, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf9, 0xf9, 0xf4, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf9, 0xf8, 0xf4, 0xff, 0xf9, 0xf9, 0xf4, 0xff, 0xf9, 0xf9, 0xf5, 0xff, 0xf9, 0xf8, 0xf4, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xf9, 0xf9, 0xf4, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf9, 0xf8, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf7, 0xf8, 0xf3, 0xff, 0xf8, 0xf8, 0xf3, 0xff, 0xf9, 0xf8, 0xf2, 0xff, 0xf9, 0xf9, 0xf3, 0xff, 0xfa, 0xf9, 0xf4, 0xff, 0xfd, 0xfb, 0xf6, 0xff, 0xff, 0xff, 0xfb, 0xff, 0xf7, 0xf5, 0xf2, 0xff, 0x59, 0x24, 0x23, 0xff, 0x37, 0x00, 0x00, 0xff, 0x3b, 0x00, 0x00, 0xff, 0x84, 0x18, 0x35, 0xff, 0xae, 0x20, 0x4a, 0xff, 0x24, 0x00, 0x00, 0xff, 0xb9, 0x2c, 0x4e, 0xff, 0xff, 0x3f, 0x73, 0xff, 0xfd, 0x3d, 0x6d, 0xff, 0xfd, 0x3e, 0x6e, 0xff, 0xfd, 0x3f, 0x6e, 0xff, 0xfd, 0x40, 0x70, 0xff, 0xfd, 0x41, 0x71, 0xff, 0xfd, 0x42, 0x73, 0xff, 0xfd, 0x43, 0x75, 0xff, 0xfd, 0x44, 0x77, 0xff, 0xfd, 0x45, 0x77, 0xff, 0xfd, 0x45, 0x76, 0xff, 0xfd, 0x44, 0x74, 0xff, 0xfd, 0x43, 0x74, 0xff, 0xfd, 0x43, 0x75, 0xff, 0xfd, 0x42, 0x76, 0xff, 0xfd, 0x41, 0x76, 0xff, 0xfd, 0x3f, 0x73, 0xff, 0xff, 0x3e, 0x76, 0xff, 0xd7, 0x2d, 0x55, 0xff, 0x9d, 0x1e, 0x2b, 0xff, 0xab, 0x23, 0x2f, 0xff, 0xf3, 0x45, 0x42, 0xff, 0xb3, 0x29, 0x30, 0xff, 0x9b, 0x21, 0x2c, 0xff, 0x9e, 0x24, 0x2e, 0xff, 0x9e, 0x25, 0x2f, 0xff, 0x9f, 0x26, 0x30, 0xff, 0xa0, 0x26, 0x30, 0xff, 0xa2, 0x27, 0x31, 0xff, 0xa0, 0x22, 0x2c, 0xff, 0xae, 0x51, 0x58, 0xff, 0xc2, 0x8f, 0x94, 0xff, 0xc2, 0x8d, 0x91, 0xff, 0xbc, 0x7a, 0x80, 0xff, 0xbe, 0x7c, 0x81, 0xff, 0xa6, 0x6f, 0x6e, 0xff, 0xaa, 0x79, 0x77, 0xff, 0xa8, 0x84, 0x83, 0xff, 0x47, 0x0e, 0x0e, 0xff, 0x3e, 0x00, 0x00, 0xff, + 0xfc, 0xfd, 0xf8, 0xff, 0xfd, 0xfc, 0xf9, 0xff, 0xfc, 0xfc, 0xf8, 0xff, 0xfb, 0xfb, 0xf6, 0xff, 0xfa, 0xfa, 0xf6, 0xff, 0xfa, 0xfa, 0xf6, 0xff, 0xfb, 0xfa, 0xf6, 0xff, 0xfa, 0xfa, 0xf6, 0xff, 0xfa, 0xfa, 0xf6, 0xff, 0xfa, 0xfa, 0xf6, 0xff, 0xfa, 0xfb, 0xf6, 0xff, 0xfb, 0xfb, 0xf6, 0xff, 0xfc, 0xfb, 0xf7, 0xff, 0xfc, 0xfb, 0xf6, 0xff, 0xfb, 0xfb, 0xf6, 0xff, 0xfa, 0xfa, 0xf5, 0xff, 0xfa, 0xfa, 0xf5, 0xff, 0xfb, 0xfa, 0xf6, 0xff, 0xfb, 0xfa, 0xf6, 0xff, 0xfc, 0xfb, 0xf7, 0xff, 0xfc, 0xfb, 0xf7, 0xff, 0xfc, 0xfb, 0xf7, 0xff, 0xfc, 0xfb, 0xf7, 0xff, 0xfc, 0xfb, 0xf7, 0xff, 0xfc, 0xfb, 0xf7, 0xff, 0xfc, 0xfb, 0xf7, 0xff, 0xfc, 0xfb, 0xf7, 0xff, 0xfb, 0xfb, 0xf7, 0xff, 0xfc, 0xfb, 0xf7, 0xff, 0xfc, 0xfb, 0xf7, 0xff, 0xfb, 0xfa, 0xf7, 0xff, 0xfb, 0xfa, 0xf6, 0xff, 0xfb, 0xfa, 0xf6, 0xff, 0xfb, 0xfa, 0xf6, 0xff, 0xfa, 0xfa, 0xf6, 0xff, 0xfa, 0xfa, 0xf5, 0xff, 0xfa, 0xf9, 0xf5, 0xff, 0xf9, 0xf9, 0xf5, 0xff, 0xfa, 0xfa, 0xf6, 0xff, 0xfc, 0xfb, 0xf7, 0xff, 0xfc, 0xfb, 0xf6, 0xff, 0xfc, 0xfb, 0xf6, 0xff, 0xfb, 0xfb, 0xf6, 0xff, 0xfc, 0xfb, 0xf6, 0xff, 0xfc, 0xfb, 0xf6, 0xff, 0xfc, 0xfb, 0xf7, 0xff, 0xfd, 0xfc, 0xf7, 0xff, 0xfd, 0xfc, 0xf7, 0xff, 0xfc, 0xfc, 0xf7, 0xff, 0xfc, 0xfc, 0xf6, 0xff, 0xfc, 0xfb, 0xf6, 0xff, 0xfc, 0xfb, 0xf6, 0xff, 0xfc, 0xfc, 0xf6, 0xff, 0xfc, 0xfb, 0xf6, 0xff, 0xfc, 0xfc, 0xf6, 0xff, 0xfc, 0xfb, 0xf6, 0xff, 0xfc, 0xfb, 0xf5, 0xff, 0xfc, 0xfb, 0xf5, 0xff, 0xfc, 0xfb, 0xf6, 0xff, 0xfc, 0xfb, 0xf6, 0xff, 0xfd, 0xfb, 0xf6, 0xff, 0xfd, 0xfc, 0xf7, 0xff, 0xfe, 0xfd, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xd0, 0xc3, 0xc0, 0xff, 0x3d, 0x00, 0x00, 0xff, 0x43, 0x00, 0x00, 0xff, 0x40, 0x00, 0x00, 0xff, 0x32, 0x00, 0x00, 0xff, 0xa1, 0x10, 0x29, 0xff, 0xb1, 0x13, 0x31, 0xff, 0xe9, 0x37, 0x61, 0xff, 0xff, 0x3e, 0x6e, 0xff, 0xfe, 0x3c, 0x6b, 0xff, 0xfe, 0x3c, 0x6b, 0xff, 0xfd, 0x3d, 0x6b, 0xff, 0xfd, 0x3e, 0x6b, 0xff, 0xfd, 0x3f, 0x6b, 0xff, 0xfd, 0x41, 0x6d, 0xff, 0xfd, 0x42, 0x6f, 0xff, 0xfd, 0x41, 0x71, 0xff, 0xfe, 0x42, 0x72, 0xff, 0xfd, 0x43, 0x71, 0xff, 0xfd, 0x42, 0x70, 0xff, 0xfd, 0x42, 0x70, 0xff, 0xfe, 0x42, 0x71, 0xff, 0xfd, 0x41, 0x73, 0xff, 0xfd, 0x40, 0x72, 0xff, 0xfe, 0x3f, 0x71, 0xff, 0xfd, 0x3c, 0x6f, 0xff, 0xb3, 0x25, 0x39, 0xff, 0x9a, 0x1d, 0x29, 0xff, 0xca, 0x31, 0x34, 0xff, 0xe9, 0x44, 0x3b, 0xff, 0x9e, 0x1f, 0x29, 0xff, 0x9d, 0x21, 0x2b, 0xff, 0x9d, 0x22, 0x2c, 0xff, 0x9d, 0x22, 0x2d, 0xff, 0x9e, 0x23, 0x2d, 0xff, 0xa0, 0x25, 0x2e, 0xff, 0xa2, 0x25, 0x2e, 0xff, 0xa2, 0x24, 0x2d, 0xff, 0xa3, 0x2a, 0x34, 0xff, 0xa5, 0x31, 0x3b, 0xff, 0xa3, 0x31, 0x39, 0xff, 0xa4, 0x2d, 0x36, 0xff, 0x95, 0x25, 0x2b, 0xff, 0x77, 0x20, 0x1b, 0xff, 0x79, 0x22, 0x1f, 0xff, 0x7a, 0x1f, 0x1b, 0xff, 0x51, 0x09, 0x07, 0xff, 0x3d, 0x00, 0x00, 0xff, + 0xfe, 0xff, 0xfc, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfd, 0xff, 0xfe, 0xff, 0xfc, 0xff, 0xfe, 0xfe, 0xfb, 0xff, 0xfe, 0xfe, 0xfa, 0xff, 0xfd, 0xfd, 0xfa, 0xff, 0xfe, 0xfd, 0xf8, 0xff, 0xfe, 0xfc, 0xf8, 0xff, 0xfd, 0xfd, 0xf8, 0xff, 0xfe, 0xfd, 0xf8, 0xff, 0xfe, 0xfd, 0xf9, 0xff, 0xfe, 0xfe, 0xfb, 0xff, 0xfe, 0xfe, 0xfa, 0xff, 0xfe, 0xfd, 0xfa, 0xff, 0xfe, 0xfe, 0xfa, 0xff, 0xfe, 0xfe, 0xfa, 0xff, 0xfe, 0xfe, 0xfb, 0xff, 0xfe, 0xfe, 0xfb, 0xff, 0xfe, 0xfe, 0xfb, 0xff, 0xfe, 0xfe, 0xfb, 0xff, 0xfe, 0xfe, 0xfb, 0xff, 0xfe, 0xfd, 0xfa, 0xff, 0xfe, 0xfd, 0xf8, 0xff, 0xfd, 0xfd, 0xf8, 0xff, 0xfe, 0xfd, 0xf9, 0xff, 0xfe, 0xfd, 0xf9, 0xff, 0xfe, 0xfd, 0xf9, 0xff, 0xfe, 0xfd, 0xf9, 0xff, 0xfe, 0xfd, 0xfa, 0xff, 0xfe, 0xfd, 0xfa, 0xff, 0xfe, 0xfe, 0xfa, 0xff, 0xff, 0xfe, 0xfb, 0xff, 0xfe, 0xfe, 0xfb, 0xff, 0xfe, 0xfd, 0xf9, 0xff, 0xfe, 0xfc, 0xf8, 0xff, 0xfe, 0xfd, 0xf9, 0xff, 0xfe, 0xfd, 0xf9, 0xff, 0xfe, 0xfe, 0xfa, 0xff, 0xfe, 0xfe, 0xfb, 0xff, 0xfe, 0xfe, 0xfb, 0xff, 0xfe, 0xfe, 0xfb, 0xff, 0xfe, 0xfe, 0xfb, 0xff, 0xfe, 0xfe, 0xfa, 0xff, 0xfe, 0xfe, 0xfb, 0xff, 0xfe, 0xfe, 0xfc, 0xff, 0xfe, 0xff, 0xfc, 0xff, 0xfe, 0xff, 0xfc, 0xff, 0xfe, 0xfe, 0xfb, 0xff, 0xfe, 0xfe, 0xfb, 0xff, 0xfe, 0xfe, 0xfb, 0xff, 0xfe, 0xfe, 0xfb, 0xff, 0xfe, 0xfe, 0xfa, 0xff, 0xfe, 0xfe, 0xfa, 0xff, 0xfe, 0xfe, 0xf9, 0xff, 0xfe, 0xfd, 0xf9, 0xff, 0xfe, 0xfe, 0xfa, 0xff, 0xfe, 0xfe, 0xfa, 0xff, 0xfe, 0xfe, 0xfa, 0xff, 0xfd, 0xfd, 0xf9, 0xff, 0xfe, 0xfd, 0xf9, 0xff, 0xfe, 0xfe, 0xfc, 0xff, 0xfe, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0x9d, 0x7d, 0x7b, 0xff, 0x37, 0x00, 0x00, 0xff, 0x43, 0x01, 0x00, 0xff, 0x3f, 0x00, 0x00, 0xff, 0x54, 0x05, 0x09, 0xff, 0xcf, 0x1c, 0x40, 0xff, 0xca, 0x1a, 0x3f, 0xff, 0xfb, 0x3b, 0x68, 0xff, 0xff, 0x3c, 0x6b, 0xff, 0xfe, 0x3b, 0x68, 0xff, 0xfe, 0x3a, 0x68, 0xff, 0xfe, 0x3b, 0x69, 0xff, 0xfe, 0x3c, 0x69, 0xff, 0xfe, 0x3d, 0x68, 0xff, 0xfe, 0x3e, 0x68, 0xff, 0xfe, 0x3e, 0x69, 0xff, 0xfe, 0x3d, 0x6c, 0xff, 0xfe, 0x3f, 0x6e, 0xff, 0xfe, 0x3f, 0x6e, 0xff, 0xfe, 0x3f, 0x6c, 0xff, 0xfe, 0x3f, 0x6c, 0xff, 0xfe, 0x3f, 0x6e, 0xff, 0xfd, 0x3f, 0x70, 0xff, 0xfd, 0x3f, 0x71, 0xff, 0xff, 0x40, 0x74, 0xff, 0xe8, 0x36, 0x5f, 0xff, 0x9e, 0x1f, 0x2a, 0xff, 0xa0, 0x1e, 0x2b, 0xff, 0xea, 0x42, 0x38, 0xff, 0xc7, 0x33, 0x2e, 0xff, 0x9b, 0x1e, 0x27, 0xff, 0x9d, 0x20, 0x2a, 0xff, 0x9d, 0x20, 0x2a, 0xff, 0x9d, 0x20, 0x2b, 0xff, 0x9e, 0x22, 0x2c, 0xff, 0xa0, 0x23, 0x2d, 0xff, 0xa1, 0x23, 0x2d, 0xff, 0xa1, 0x23, 0x2d, 0xff, 0xa0, 0x20, 0x2b, 0xff, 0x9f, 0x1e, 0x28, 0xff, 0x9f, 0x1e, 0x27, 0xff, 0xa1, 0x1f, 0x28, 0xff, 0x7f, 0x12, 0x13, 0xff, 0x66, 0x09, 0x04, 0xff, 0x69, 0x0a, 0x05, 0xff, 0x6b, 0x0b, 0x07, 0xff, 0x68, 0x0c, 0x08, 0xff, 0x46, 0x03, 0x02, 0xff, +}; + +const lv_image_dsc_t img_app_pos = { + .header.cf = LV_COLOR_FORMAT_ARGB8888, + .header.magic = LV_IMAGE_HEADER_MAGIC, + .header.w = 112, + .header.h = 112, + .data_size = 12544 * 4, + .data = output_112x112_map, +}; diff --git a/firmware/echoear-platform-agent/components/application/platform/src/espressif/lvgl_source/volc_lvgl_source.h b/firmware/echoear-platform-agent/components/application/platform/src/espressif/lvgl_source/volc_lvgl_source.h new file mode 100644 index 0000000..b1a5ea2 --- /dev/null +++ b/firmware/echoear-platform-agent/components/application/platform/src/espressif/lvgl_source/volc_lvgl_source.h @@ -0,0 +1,10 @@ +#ifndef VOLC_LVGL_SOURCE_H__ +#define VOLC_LVGL_SOURCE_H__ + +#include "lvgl.h" + +extern lv_font_t echoear_font_16; + +extern lv_image_dsc_t img_app_pos; + +#endif \ No newline at end of file diff --git a/firmware/echoear-platform-agent/components/application/platform/src/espressif/volc_hal.c b/firmware/echoear-platform-agent/components/application/platform/src/espressif/volc_hal.c new file mode 100644 index 0000000..a8c5d36 --- /dev/null +++ b/firmware/echoear-platform-agent/components/application/platform/src/espressif/volc_hal.c @@ -0,0 +1,60 @@ +// Copyright (2025) Beijing Volcano Engine Technology Ltd. +// SPDX-License-Identifier: Apache-2.0 + +#include "volc_hal_file.h" +#include "volc_hal.h" +#include "volc_osal.h" +#include "driver/gpio.h" +#include "esp_mac.h" +#include + +#include "audio_processor.h" +#include "basic_board.h" + +#define DEVICE_NAME_PREFIX "esp32_" + +volc_hal_context_t* global_context = NULL; + +basic_board_periph_t global_periph = {0}; + +static void __gmf_audio_init() +{ + audio_manager_config_t config = DEFAULT_AUDIO_MANAGER_CONFIG(); + basic_board_init(&global_periph); + config.play_dev = global_periph.play_dev; + config.rec_dev = global_periph.rec_dev; + strcpy(config.mic_layout, global_periph.mic_layout); + config.board_sample_rate = global_periph.sample_rate; + config.board_bits = global_periph.sample_bits; + config.board_channels = global_periph.channels; + audio_manager_init(&config); +} + +static void __generate_device_name(char* device_name){ + uint8_t mac[6] = {0}; + ESP_ERROR_CHECK(esp_read_mac(mac, ESP_MAC_WIFI_STA)); + // printf("WIFI_STA MAC 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x \n", + // mac[0], mac[1], mac[2], + // mac[3], mac[4], mac[5]); + snprintf(device_name,VOLC_DEVICE_NAME_LENGTH,"%s%02X%02X%02X%02X%02X%02X",DEVICE_NAME_PREFIX,mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); +} + +int volc_hal_init(void) { + if(global_context == NULL){ + global_context = (volc_hal_context_t*) volc_osal_calloc(1,sizeof(volc_hal_context_t)); + } + __gmf_audio_init(); + volc_hal_file_system_init(); + __generate_device_name(global_context->device_name); + return 0; +} + +volc_hal_context_t* volc_get_global_hal_context(){ + return global_context; +} + +void volc_hal_fini(void) { + volc_osal_free(global_context); + global_context = NULL; + return; +} diff --git a/firmware/echoear-platform-agent/components/application/platform/src/espressif/volc_hal_button.c b/firmware/echoear-platform-agent/components/application/platform/src/espressif/volc_hal_button.c new file mode 100644 index 0000000..277ccf8 --- /dev/null +++ b/firmware/echoear-platform-agent/components/application/platform/src/espressif/volc_hal_button.c @@ -0,0 +1,136 @@ +// Copyright (2025) Beijing Volcano Engine Technology Ltd. +// SPDX-License-Identifier: Apache-2.0 + +#include + +#include "volc_hal.h" +#include "volc_hal_button.h" +#include "iot_button.h" +#include "button_gpio.h" +#include "util/volc_log.h" +#include "volc_osal.h" + +typedef struct { + button_handle_t btn; + volc_hal_button_config_t config; +} volc_hal_button_impl_t; + +static void __button_event_cb_adapter(void* btn_handle, void* usr_data) +{ + if (NULL == usr_data) { + return; + } + + struct { + volc_hal_button_impl_t* button_impl; + button_event_t event; + } *adapter_data = (void*)usr_data; + + if (NULL != adapter_data->button_impl && NULL != adapter_data->button_impl->config.event_cb) { + adapter_data->button_impl->config.event_cb( + (volc_hal_button_t)adapter_data->button_impl, + (volc_hal_button_event_e)adapter_data->event, + adapter_data->button_impl->config.user_data + ); + } +} + +volc_hal_button_t volc_hal_button_create(volc_hal_button_config_t* config) +{ + volc_hal_context_t* g_hal_context = volc_get_global_hal_context(); + if(g_hal_context == NULL){ + return NULL; + } + if (NULL == config) { + LOGW("invalid input config %p", config); + return NULL; + } + volc_hal_button_impl_t *button_impl = (volc_hal_button_impl_t *)volc_osal_malloc(sizeof(volc_hal_button_impl_t)); + if (NULL == button_impl) { + LOGE("memory alloc failed"); + return NULL; + } + button_impl->config.gpio_num = config->gpio_num; + button_impl->config.active_level = config->active_level; + button_impl->config.long_press_ms = config->long_press_ms; + button_impl->config.short_press_ms = config->short_press_ms; + button_impl->config.enable_power_save = config->enable_power_save; + button_impl->config.user_data = config->user_data; + button_impl->config.event_cb = config->event_cb; + + button_config_t btn_config = { + // .type = BUTTON_TYPE_GPIO, + // .gpio_button_config.gpio_num = button_impl->config.gpio_num, + // .gpio_button_config.active_level = button_impl->config.active_level, + .long_press_time = button_impl->config.long_press_ms, + .short_press_time = button_impl->config.short_press_ms + }; + button_driver_t driver_config = { + .enable_power_save = button_impl->config.enable_power_save, + .get_key_level = NULL, + .enter_power_save = NULL, + .del = NULL, + }; + iot_button_create(&btn_config,&driver_config,&button_impl->btn); + if (NULL == button_impl->btn) { + LOGE("button create failed"); + volc_osal_free(button_impl); + return NULL; + } + + struct { + volc_hal_button_impl_t* button_impl; + button_event_t event; + } *adapter_data = NULL; + + button_event_t events[] = { + BUTTON_PRESS_DOWN, + BUTTON_PRESS_UP, + BUTTON_PRESS_REPEAT, + BUTTON_PRESS_REPEAT_DONE, + BUTTON_SINGLE_CLICK, + BUTTON_DOUBLE_CLICK, + BUTTON_MULTIPLE_CLICK, + BUTTON_LONG_PRESS_START, + BUTTON_LONG_PRESS_HOLD, + BUTTON_LONG_PRESS_UP, + BUTTON_PRESS_END + }; + + for (int i = 0; i < sizeof(events)/sizeof(events[0]); i++) { + adapter_data = (void*)volc_osal_malloc(sizeof(*adapter_data)); + if (NULL == adapter_data) { + LOGE("Failed to allocate adapter data for event %d", events[i]); + continue; + } + + adapter_data->button_impl = button_impl; + adapter_data->event = events[i]; + + iot_button_register_cb(button_impl->btn, events[i], NULL,__button_event_cb_adapter, adapter_data); + } + g_hal_context->button_handle = (volc_hal_button_t)button_impl; + + return (volc_hal_button_t)button_impl; +} + +void volc_hal_button_destroy(volc_hal_button_t button) +{ + volc_hal_context_t* g_hal_context = volc_get_global_hal_context(); + if(g_hal_context == NULL){ + return; + } + if (NULL == button) { + LOGW("invalid input button %p", button); + return; + } + volc_hal_button_impl_t *button_impl = (volc_hal_button_impl_t *)button; + + if (NULL != button_impl->btn) { + iot_button_delete(button_impl->btn); + button_impl->btn = NULL; + } + + g_hal_context->button_handle = NULL; + volc_osal_free(button_impl); +} \ No newline at end of file diff --git a/firmware/echoear-platform-agent/components/application/platform/src/espressif/volc_hal_capture.c b/firmware/echoear-platform-agent/components/application/platform/src/espressif/volc_hal_capture.c new file mode 100644 index 0000000..bf62c3b --- /dev/null +++ b/firmware/echoear-platform-agent/components/application/platform/src/espressif/volc_hal_capture.c @@ -0,0 +1,286 @@ +// Copyright (2025) Beijing Volcano Engine Technology Ltd. +// SPDX-License-Identifier: Apache-2.0 + +#include + +#include "volc_hal.h" +#include "volc_hal_capture.h" +#include "volc_osal.h" + +#include "audio_processor.h" +#include "av_processor_type.h" +#include "basic_board.h" + +#include "esp_codec_dev.h" +#include "util/volc_log.h" + +typedef enum { + WAKEUP = 0, + CAPTURE, + INITED, +} audio_pipeline_state_e; + +typedef struct { + av_processor_encoder_config_t recorder_cfg; + audio_pipeline_state_e state; +} volc_hal_audio_capture_config_t; + +typedef struct { + //TODO: add video capture config + void* video_capture_config; +} volc_hal_video_capture_config_t; + +typedef struct { + volc_media_type_e media_type; + union { + volc_hal_audio_capture_config_t audio_capture_config; + volc_hal_video_capture_config_t video_capture_config; + }; + volc_hal_capture_data_cb_t data_cb; // Data callback function + volc_hal_audio_wakeup_cb_t audio_wakeup_cb; // audio wakeup cb + void* user_data; // User data pointer + volc_osal_tid_t capture_thread; + volatile bool is_started; +} volc_hal_capture_impl_t; + +extern basic_board_periph_t global_periph; + +static int __volc_capture_get_default_read_size(volc_hal_capture_t capture) +{ + // 60ms +#if (CONFIG_VOLC_AUDIO_G711A) + return (160*3); +#else + // 60 ms of 16 kHz mono PCM16: consume the complete encoder frame. + return 16000 * 60 / 1000 * sizeof(int16_t); +#endif +} + +static void __volc_audio_capture_task(void *arg) +{ + volc_hal_capture_impl_t *impl = (volc_hal_capture_impl_t *)arg; + LOGI("capture task started..."); + int read_size = __volc_capture_get_default_read_size(impl); + uint8_t *read_buf = volc_osal_malloc(read_size); +// mem_assert(read_buf); + + while(impl->is_started) { + int ret = audio_recorder_read_data(read_buf, read_size); + + if (ret > 0 && impl->data_cb && impl->audio_capture_config.state == CAPTURE) { + #ifdef CONFIG_VOLC_AUDIO_G711A + volc_hal_frame_info_t frame_info = { + .data_type = VOLC_AUDIO_DATA_TYPE_G711A, + .user_data = impl->user_data, + }; + impl->data_cb(impl, (const void *)read_buf, ret, &frame_info); + #else + volc_hal_frame_info_t frame_info = { + .data_type = VOLC_AUDIO_DATA_TYPE_PCM, + .user_data = impl->user_data, + }; + impl->data_cb(impl, (const void *)read_buf, ret, &frame_info); + #endif + } + } + // audio_recorder_pause(); + if (impl->capture_thread) { + volc_osal_thread_destroy(impl->capture_thread); + impl->capture_thread = NULL; + } + volc_osal_free(read_buf); + volc_osal_thread_exit(NULL); +} + +volc_hal_capture_t volc_hal_capture_create(volc_hal_capture_config_t *config) +{ + volc_hal_context_t *g_hal_context = volc_get_global_hal_context(); + if (g_hal_context == NULL) + { + return NULL; + } + volc_hal_capture_impl_t *capture = (volc_hal_capture_impl_t *)volc_osal_calloc(1, sizeof(volc_hal_capture_impl_t)); + // config + capture->is_started = false; + capture->media_type = config->media_type; + capture->data_cb = config->data_cb; + capture->audio_wakeup_cb = config->audio_wakeup_cb; + capture->user_data = config->user_data; + esp_codec_dev_set_in_gain(global_periph.rec_dev, 52.0); + switch (capture->media_type) + { + case VOLC_MEDIA_TYPE_AUDIO: +#if (CONFIG_VOLC_AUDIO_G711A) + capture->audio_capture_config.recorder_cfg.format = AV_PROCESSOR_FORMAT_ID_G711A; +#else + capture->audio_capture_config.recorder_cfg.format = AV_PROCESSOR_FORMAT_ID_PCM; +#endif + capture->audio_capture_config.recorder_cfg.params.g711.audio_info.sample_rate = 16000; + capture->audio_capture_config.recorder_cfg.params.g711.audio_info.sample_bits = 16; + capture->audio_capture_config.recorder_cfg.params.g711.audio_info.channels = 1; + capture->audio_capture_config.recorder_cfg.params.g711.audio_info.frame_duration = 60; + capture->audio_capture_config.state = INITED; + g_hal_context->capture_handle[VOLC_HAL_CAPTURE_AUDIO] = (volc_hal_capture_t)capture; + break; + case VOLC_MEDIA_TYPE_VIDEO: + // TODO: Add video capture support + LOGE("Video capture is not supported yet"); + break; + default: + break; + } + return (volc_hal_capture_t)capture; +} + +static void __start_audio_capture(volc_hal_capture_impl_t *capture) +{ +// audio_pipeline_unlink(impl->audio_capture_config.audio_pipeline); + av_processor_afe_config_t afe_config = DEFAULT_AV_PROCESSOR_AFE_CONFIG(); + afe_config.ai_mode_wakeup = false; + audio_recorder_config_t recorder_config = DEFAULT_AUDIO_RECORDER_CONFIG(); + memcpy((void *)&recorder_config.encoder_cfg, &capture->audio_capture_config.recorder_cfg, sizeof(av_processor_encoder_config_t)); + memcpy(&recorder_config.afe_config, &afe_config, sizeof(av_processor_afe_config_t)); + recorder_config.recorder_event_cb = capture->audio_wakeup_cb; + recorder_config.recorder_ctx = NULL; + audio_recorder_open(&recorder_config); + + volc_osal_thread_param_t param = {0}; + snprintf(param.name, sizeof(param.name), "%s", "volc_capture_task"); + param.stack_size = 4 * 1024; + param.priority = 5; + param.stack_in_ext = 1; + capture->is_started = true; + capture->audio_capture_config.state = CAPTURE; + esp_err_t ret = volc_osal_thread_create(&capture->capture_thread, ¶m, __volc_audio_capture_task, capture); + + LOGI("capture task start... mode: CAPTURE"); + return; +} + +static void __start_audio_wake(volc_hal_capture_impl_t *capture) +{ + av_processor_afe_config_t afe_config = DEFAULT_AV_PROCESSOR_AFE_CONFIG(); + afe_config.ai_mode_wakeup = true; + audio_recorder_config_t recorder_config = DEFAULT_AUDIO_RECORDER_CONFIG(); + memcpy((void *)&recorder_config.encoder_cfg, &capture->audio_capture_config.recorder_cfg, sizeof(av_processor_encoder_config_t)); + memcpy(&recorder_config.afe_config, &afe_config, sizeof(av_processor_afe_config_t)); + recorder_config.recorder_event_cb = capture->audio_wakeup_cb; + recorder_config.recorder_ctx = NULL; + audio_recorder_open(&recorder_config); + + volc_osal_thread_param_t param = {0}; + snprintf(param.name, sizeof(param.name), "%s", "volc_capture_task"); + param.stack_size = 4 * 1024; + param.priority = 5; + param.stack_in_ext = 1; + capture->is_started = true; + capture->audio_capture_config.state = WAKEUP; + esp_err_t ret = volc_osal_thread_create(&capture->capture_thread, ¶m, __volc_audio_capture_task, capture); + + LOGI("capture task start... mode: WAKEUP"); + return; +} + +int volc_hal_capture_start(volc_hal_capture_t capture,volc_hal_audio_capture_mode_e mode) +{ + if(capture == NULL){ + return -1; + } + volc_hal_capture_impl_t *impl = (volc_hal_capture_impl_t *)capture; + if (impl->media_type == VOLC_MEDIA_TYPE_AUDIO && impl->audio_capture_config.state == INITED) { + switch (mode) + { + case VOLC_AUDIO_MODE_WAKEUP: + __start_audio_wake(impl); + break; + case VOLC_AUDIO_MODE_CAPTURE: + __start_audio_capture(impl); + break; + default: + break; + } + } + return 0; +} + +int volc_hal_capture_stop(volc_hal_capture_t capture) +{ + volc_hal_capture_impl_t *impl = (volc_hal_capture_impl_t *)capture; + + if (!impl || !impl->is_started) { + return 0; + } + // stop thread + impl->is_started = false; + switch (impl->media_type) { + case VOLC_MEDIA_TYPE_AUDIO: + // audio_recorder_pause(); + // audio_recorder_resume(); + audio_recorder_close(); + impl->audio_capture_config.state = INITED; + break; + case VOLC_MEDIA_TYPE_VIDEO: + // TODO: Add video capture support + LOGE("Video capture is not supported yet"); + break; + default: + break; + } + return 0; +} + +void volc_hal_capture_destroy(volc_hal_capture_t capture) +{ + volc_hal_context_t* g_hal_context = volc_get_global_hal_context(); + if(g_hal_context == NULL){ + return; + } + volc_hal_capture_impl_t *impl = (volc_hal_capture_impl_t *)capture; + if (!impl) { + return; + } + + volc_hal_capture_stop(capture); + switch (impl->media_type) { + case VOLC_MEDIA_TYPE_AUDIO: + g_hal_context->capture_handle[VOLC_HAL_CAPTURE_AUDIO] = NULL; + break; + case VOLC_MEDIA_TYPE_VIDEO: + // TODO: Add video capture support + LOGE("Video capture is not supported yet"); + break; + default: + break; + } + volc_osal_free(impl); +} + +int volc_hal_capture_update_config(volc_hal_capture_t capture, volc_hal_capture_config_t* config) { + volc_hal_capture_impl_t *impl = (volc_hal_capture_impl_t *)capture; + if (!impl || !config) { + LOGE("capture or config is NULL"); + return -1; + } + + if (impl->media_type != config->media_type) { + LOGE("media_type not match"); + return -1; + } + + if (impl->user_data != config->user_data) { + LOGE("user_data not match"); + return -1; + } + + if (impl->is_started) { + LOGE("capture is started, can not update config"); + return -1; + } + if(config->data_cb){ + impl->data_cb = config->data_cb; + } + if(config->audio_wakeup_cb){ + impl->audio_wakeup_cb = config->audio_wakeup_cb; + } + return 0; +} diff --git a/firmware/echoear-platform-agent/components/application/platform/src/espressif/volc_hal_display.c b/firmware/echoear-platform-agent/components/application/platform/src/espressif/volc_hal_display.c new file mode 100644 index 0000000..61dd8ed --- /dev/null +++ b/firmware/echoear-platform-agent/components/application/platform/src/espressif/volc_hal_display.c @@ -0,0 +1,354 @@ +// Copyright (2025) Beijing Volcano Engine Technology Ltd. +// SPDX-License-Identifier: Apache-2.0 + +#include "volc_hal.h" +#include "volc_hal_display.h" +#include "volc_lvgl_source.h" +#include "hw_init.h" +#include "esp_lcd_panel_ops.h" +#include "esp_mmap_assets.h" +#include "esp_board_manager.h" +#include "dev_display_lcd_spi.h" +#include "freertos/semphr.h" +#include "esp_heap_caps.h" +#include "dev_ledc_ctrl.h" +#include "gfx.h" +#include "lvgl.h" +#include +#include "volc_osal.h" +#include +#include "basic_board.h" + +#include "mmap_generate_eaf.h" + +typedef struct volc_hal_display_impl { + volatile bool is_init; + /* Global state */ + esp_lcd_panel_handle_t s_panel; + mmap_assets_handle_t s_assets; + gfx_handle_t s_gfx; + gfx_obj_t *s_anim; + gfx_obj_t *s_label; + gfx_obj_t *s_subtitle; + gfx_obj_t *s_setup_hint; + esp_lcd_panel_io_handle_t display_io_handle; + SemaphoreHandle_t flush_done; + int s_current_emote; + volc_osal_tid_t display_thread; +} volc_hal_display_impl_t; + +static volc_hal_display_impl_t* global_display_impl = NULL; +extern basic_board_periph_t global_periph; + +static esp_lv_adapter_rotation_t __get_configured_rotation(void) +{ +#if CONFIG_EXAMPLE_DISPLAY_ROTATION_0 + return ESP_LV_ADAPTER_ROTATE_0; +#elif CONFIG_EXAMPLE_DISPLAY_ROTATION_90 + return ESP_LV_ADAPTER_ROTATE_90; +#elif CONFIG_EXAMPLE_DISPLAY_ROTATION_180 + return ESP_LV_ADAPTER_ROTATE_180; +#elif CONFIG_EXAMPLE_DISPLAY_ROTATION_270 + return ESP_LV_ADAPTER_ROTATE_270; +#else + return ESP_LV_ADAPTER_ROTATE_0; +#endif +} + +/* ========== Asset Management ========== */ + +static esp_err_t __mount_assets(void) +{ + mmap_assets_config_t cfg = { + .partition_label = "eaf", + .max_files = MMAP_EAF_FILES, + .checksum = MMAP_EAF_CHECKSUM, + .flags = { + .mmap_enable = 1, + }, + }; + + return mmap_assets_new(&cfg, &global_display_impl->s_assets); +} + +/* ========== GFX Emote Engine ========== */ + +static bool __lcd_flush_done(esp_lcd_panel_io_handle_t io, esp_lcd_panel_io_event_data_t *event, void *context) +{ + volc_hal_display_impl_t *display = context; + BaseType_t wake = pdFALSE; + xSemaphoreGiveFromISR(display->flush_done, &wake); + return wake == pdTRUE; +} + +static void __gfx_flush_cb(gfx_handle_t handle, int x1, int y1, int x2, int y2, const void *data) +{ + if (global_display_impl->s_panel) { + if (esp_lcd_panel_draw_bitmap(global_display_impl->s_panel, x1, y1, x2, y2, data) == ESP_OK) { + // GFX's own wait expires after 20 ms; wait here until DMA really finishes. + xSemaphoreTake(global_display_impl->flush_done, portMAX_DELAY); + } + } + gfx_emote_flush_ready(handle, true); +} + +static esp_err_t __init_gfx_engine(void) +{ + gfx_core_config_t cfg = { + .flush_cb = __gfx_flush_cb, + .user_data = global_display_impl->s_panel, + .flags = { + .swap = 1, + .double_buffer = 0, + .buff_dma = 1, + .buff_spiram = 0, + }, + .h_res = HW_LCD_H_RES, + .v_res = HW_LCD_V_RES, + .fps = 15, + .task = { + .task_priority = 4, + .task_stack = 7168, + .task_affinity = 1, + .task_stack_caps = MALLOC_CAP_SPIRAM, + }, + .buffers = { + .buf_pixels = HW_LCD_H_RES * 20, + }, + }; + + global_display_impl->s_gfx = gfx_emote_init(&cfg); + return global_display_impl->s_gfx ? ESP_OK : ESP_FAIL; +} + + +static esp_err_t __create_animation(void) +{ + /* Get EAF data from mmap */ + const uint8_t *eaf_data = mmap_assets_get_mem(global_display_impl->s_assets, global_display_impl->s_current_emote); + int eaf_size = mmap_assets_get_size(global_display_impl->s_assets, global_display_impl->s_current_emote); + + gfx_emote_lock(global_display_impl->s_gfx); + + /* Create animation object */ + global_display_impl->s_anim = gfx_anim_create(global_display_impl->s_gfx); + if (!global_display_impl->s_anim) { + gfx_emote_unlock(global_display_impl->s_gfx); + return ESP_FAIL; + } + + /* Set animation source */ + esp_err_t ret = gfx_anim_set_src(global_display_impl->s_anim, eaf_data, eaf_size); + if (ret != ESP_OK) { + gfx_obj_delete(global_display_impl->s_anim); + global_display_impl->s_anim = NULL; + gfx_emote_unlock(global_display_impl->s_gfx); + return ret; + } + + gfx_anim_set_mirror(global_display_impl->s_anim, true, 50); /* Enable mirror for dual-eye display */ + if(global_display_impl->s_current_emote == 0){ + gfx_obj_align(global_display_impl->s_anim, GFX_ALIGN_LEFT_MID, 35, 0); + } else { + gfx_obj_align(global_display_impl->s_anim, GFX_ALIGN_CENTER, 0, 0); + } + gfx_anim_set_segment(global_display_impl->s_anim, 0, UINT32_MAX, 30, true); /* All frames, 30 FPS, loop */ + gfx_emote_unlock(global_display_impl->s_gfx); + return ESP_OK; +} + +static void __emote_task(void *arg) +{ + /* Start animation playback */ + gfx_emote_lock(global_display_impl->s_gfx); + gfx_anim_start(global_display_impl->s_anim); + gfx_emote_unlock(global_display_impl->s_gfx); + + /* The display task owns animation playback for the lifetime of the app. */ + while (1) { + vTaskDelay(pdMS_TO_TICKS(100)); + } +} + +static void __clear_panel(void) +{ + const int rows = 20; + uint16_t *black = heap_caps_calloc(HW_LCD_H_RES * rows, sizeof(uint16_t), MALLOC_CAP_DMA); + if (!black) return; + for (int y = 0; y < HW_LCD_V_RES; y += rows) { + int y2 = y + rows > HW_LCD_V_RES ? HW_LCD_V_RES : y + rows; + if (esp_lcd_panel_draw_bitmap(global_display_impl->s_panel, 0, y, HW_LCD_H_RES, y2, black) != ESP_OK) break; + xSemaphoreTake(global_display_impl->flush_done, portMAX_DELAY); + } + free(black); +} + +static esp_err_t __switch_animation(int emote_index) +{ + if (!global_display_impl->s_anim || !global_display_impl->s_gfx) { + return ESP_FAIL; + } + + /* Get new EAF data from mmap */ + const uint8_t *eaf_data = mmap_assets_get_mem(global_display_impl->s_assets, emote_index); + int eaf_size = mmap_assets_get_size(global_display_impl->s_assets, emote_index); + if(global_display_impl->s_current_emote == emote_index) { + return ESP_OK; + } + gfx_emote_lock(global_display_impl->s_gfx); + + /* Stop current animation */ + gfx_anim_stop(global_display_impl->s_anim); + + /* Update animation source */ + esp_err_t ret = gfx_anim_set_src(global_display_impl->s_anim, eaf_data, eaf_size); + if (ret != ESP_OK) { + gfx_emote_unlock(global_display_impl->s_gfx); + return ret; + } + + /* gfx_draw_animation() derives the object size from the decoded frame. */ + global_display_impl->s_anim->width = 0; + global_display_impl->s_anim->height = 0; + gfx_anim_set_mirror(global_display_impl->s_anim, true, 50); /* Enable mirror for dual-eye display */ + if(emote_index == 0){ + gfx_obj_align(global_display_impl->s_anim, GFX_ALIGN_LEFT_MID, 35, 0); + } else { + gfx_obj_align(global_display_impl->s_anim, GFX_ALIGN_CENTER, 0, 0); + } + + gfx_anim_set_segment(global_display_impl->s_anim, 0, UINT32_MAX, 30, true); /* All frames, 30 FPS, loop */ + + /* Restart animation */ + gfx_anim_start(global_display_impl->s_anim); + + gfx_emote_unlock(global_display_impl->s_gfx); + + global_display_impl->s_current_emote = emote_index; + return ESP_OK; +} + +volc_hal_display_t volc_hal_display_create(volc_hal_display_config_t* config) +{ + + volc_hal_context_t* g_hal_context = volc_get_global_hal_context(); + if(g_hal_context == NULL){ + return NULL; + } + + global_display_impl = (volc_hal_display_impl_t*)volc_osal_calloc(1,sizeof(volc_hal_display_impl_t)); + // init display + + esp_lv_adapter_rotation_t rotation = __get_configured_rotation(); + /* Determine tear avoid mode based on LCD interface */ +#if CONFIG_EXAMPLE_LCD_INTERFACE_MIPI_DSI + esp_lv_adapter_tear_avoid_mode_t tear_avoid_mode = ESP_LV_ADAPTER_TEAR_AVOID_MODE_DEFAULT_MIPI_DSI; +#elif CONFIG_EXAMPLE_LCD_INTERFACE_RGB + esp_lv_adapter_tear_avoid_mode_t tear_avoid_mode = ESP_LV_ADAPTER_TEAR_AVOID_MODE_DEFAULT_RGB; +#else + esp_lv_adapter_tear_avoid_mode_t tear_avoid_mode = ESP_LV_ADAPTER_TEAR_AVOID_MODE_DEFAULT; +#endif + // ESP_ERROR_CHECK(hw_lcd_init(&global_display_impl->s_panel, &global_display_impl->display_io_handle, tear_avoid_mode, rotation)); + global_display_impl->s_panel = global_periph.lcd_pannel; + dev_display_lcd_spi_handles_t *lcd = NULL; + ESP_ERROR_CHECK(esp_board_manager_get_device_handle("display_lcd", (void **)&lcd)); + global_display_impl->display_io_handle = lcd->io_handle; + global_display_impl->flush_done = xSemaphoreCreateBinary(); + if (!global_display_impl->flush_done) return NULL; + esp_lcd_panel_io_callbacks_t callbacks = {.on_color_trans_done = __lcd_flush_done}; + ESP_ERROR_CHECK(esp_lcd_panel_io_register_event_callbacks(lcd->io_handle, &callbacks, global_display_impl)); + __clear_panel(); + ESP_ERROR_CHECK(__mount_assets()); + ESP_ERROR_CHECK(__init_gfx_engine()); + /* + * The upstream 360x360 boot asset is not rendered reliably on every + * EchoEar panel revision and can leave uninitialised-looking pixels at + * the bottom of the round display. Start with the compact built-in eye + * animation instead; application status text explains the setup state. + */ + global_display_impl->s_current_emote = MMAP_EAF_HAPPY_EAF; + __create_animation(); + volc_osal_thread_param_t param = {0}; + snprintf(param.name, sizeof(param.name), "%s", "volc_capture_task"); + param.stack_size = 8 * 1024; + param.priority = 4; + param.stack_in_ext = 1; + esp_err_t ret = volc_osal_thread_create(&global_display_impl->display_thread, ¶m, __emote_task, NULL); + + global_display_impl->is_init = true; + + g_hal_context->display_handle = (volc_hal_display_t)global_display_impl; + volc_hal_display_set_brightness(global_display_impl,80); + return (volc_hal_display_t)global_display_impl; +} + +int volc_hal_display_set_brightness(volc_hal_display_t display, int brightness) +{ + if(display == NULL) return -1; + if (brightness > 100) { + brightness = 100; + } + if (brightness < 0) { + brightness = 0; + } + + periph_ledc_handle_t *ledc_handle = NULL; + + esp_board_manager_get_device_handle("lcd_brightness", (void **)&ledc_handle); + + dev_ledc_ctrl_config_t *dev_ledc_cfg = NULL; + esp_err_t config_ret = esp_board_manager_get_device_config("lcd_brightness", (void*)&dev_ledc_cfg); + if (config_ret != ESP_OK) { + return -1; + } + // ESP_LOGI(TAG, "dev_ledc_cfg.ledc_name: %s, name: %s, type: %s", dev_ledc_cfg->ledc_name, dev_ledc_cfg->name, dev_ledc_cfg->type); + periph_ledc_config_t *ledc_config = NULL; + esp_board_manager_get_periph_config(dev_ledc_cfg->ledc_name, (void**)&ledc_config); + uint32_t duty = (brightness * ((1 << (uint32_t)ledc_config->duty_resolution) - 1)) / 100; + // ESP_LOGI(TAG, "duty_cycle: %" PRIu32 ", speed_mode: %d, channel: %d, duty_resolution: %d", duty, ledc_handle->speed_mode, ledc_handle->channel, ledc_config->duty_resolution); + // ESP_BOARD_RETURN_ON_ERROR(ledc_set_duty(ledc_handle->speed_mode, ledc_handle->channel, duty), TAG, "LEDC set duty failed"); + ledc_set_duty(ledc_handle->speed_mode, ledc_handle->channel, duty); + // ESP_BOARD_RETURN_ON_ERROR(ledc_update_duty(ledc_handle->speed_mode, ledc_handle->channel), TAG, "LEDC update duty failed"); + ledc_update_duty(ledc_handle->speed_mode, ledc_handle->channel); + return 0; + // return bsp_display_brightness_set(brightness); +} + +void volc_hal_display_destroy(volc_hal_display_t display) +{ + volc_hal_context_t* g_hal_context = volc_get_global_hal_context(); + if(g_hal_context == NULL){ + return; + } + volc_hal_display_impl_t* global_display_impl = (volc_hal_display_impl_t*)display; + if(global_display_impl && global_display_impl->is_init){ + global_display_impl->is_init = false; + } + volc_osal_free(global_display_impl); + global_display_impl = NULL; + g_hal_context->display_handle = NULL; + return; +} + +int volc_hal_display_set_content(volc_hal_display_t display, volc_hal_display_obj_e obj, volc_hal_display_type_e type, const void* content) +{ + volc_hal_context_t* g_hal_context = volc_get_global_hal_context(); + if(g_hal_context == NULL || g_hal_context->display_handle == NULL){ + return -1; + } + volc_hal_display_impl_t* global_display_impl = (volc_hal_display_impl_t*)(g_hal_context->display_handle); + + if(obj == VOLC_DISPLAY_OBJ_STATUS && type == VOLC_DISPLAY_TEXT){ + ESP_LOGI("DISPLAY", "Status: %s", (const char *)content); + } + if(obj == VOLC_DISPLAY_OBJ_SUBTITLE && type == VOLC_DISPLAY_TEXT){ + ESP_LOGI("DISPLAY", "Subtitle: %s", (const char *)content); + } + if(obj == VOLC_DISPLAY_OBJ_MAIN && type == VOLC_DISPLAY_IMAGE){ + int index = *((int*)content); + if(index < MMAP_EAF_FILES){ + __switch_animation(index); + } + } + return 0; +} diff --git a/firmware/echoear-platform-agent/components/application/platform/src/espressif/volc_hal_display_lvgl.c b/firmware/echoear-platform-agent/components/application/platform/src/espressif/volc_hal_display_lvgl.c new file mode 100644 index 0000000..857ae98 --- /dev/null +++ b/firmware/echoear-platform-agent/components/application/platform/src/espressif/volc_hal_display_lvgl.c @@ -0,0 +1,176 @@ +// Copyright (2025) Beijing Volcano Engine Technology Ltd. +// SPDX-License-Identifier: Apache-2.0 + +#include "volc_hal.h" +#include "volc_hal_display.h" +#include "volc_lvgl_source.h" +#include "lvgl.h" +#include "bsp/echoear.h" +#include +#include "volc_osal.h" +#include + +#define LVGL_TASK_PRIORITY 1 +#define LVGL_TASK_CORE_ID 1 +#define LVGL_TASK_STACK_SIZE 8 * 1024 +#define LVGL_TASK_MAX_SLEEP_MS 500 +#define LVGL_TASK_TIMER_PERIOD_MS 50 +#define LVGL_TIMER_INTERVAL 500 + +typedef struct volc_hal_display_impl { + lv_obj_t* screen; + lv_obj_t* display_obj[VOLC_DISPLAY_OBJ_MAX]; + // use for display subtitle + lv_timer_t* text_timer; + char subtitle_texts[64]; + char status_texts[64]; + volatile bool is_init; +} volc_hal_display_impl_t; + +static volc_hal_display_impl_t* global_display_impl = NULL; +volc_hal_display_t global_display = NULL; + +static void __subtitle_update_cb(lv_timer_t* timer) +{ + volc_hal_context_t* g_hal_context = volc_get_global_hal_context(); + if(g_hal_context == NULL){ + return; + } + volc_hal_display_impl_t* global_display_impl = (volc_hal_display_impl_t*)(g_hal_context->display_handle); + if(global_display_impl && global_display_impl->is_init){ + lv_label_set_text(global_display_impl->display_obj[VOLC_DISPLAY_OBJ_SUBTITLE], global_display_impl->subtitle_texts); + lv_label_set_text(global_display_impl->display_obj[VOLC_DISPLAY_OBJ_STATUS], global_display_impl->status_texts); + lv_obj_remove_flag(global_display_impl->display_obj[VOLC_DISPLAY_OBJ_SUBTITLE], LV_OBJ_FLAG_HIDDEN); + lv_obj_remove_flag(global_display_impl->display_obj[VOLC_DISPLAY_OBJ_STATUS], LV_OBJ_FLAG_HIDDEN); + } +} + +static void __status_obj_init(volc_hal_display_impl_t* global_display_impl) +{ + if(global_display_impl && global_display_impl->display_obj[VOLC_DISPLAY_OBJ_STATUS] == NULL){ + global_display_impl->display_obj[VOLC_DISPLAY_OBJ_STATUS] = lv_label_create(global_display_impl->screen); + lv_obj_set_style_text_font(global_display_impl->display_obj[VOLC_DISPLAY_OBJ_STATUS], &echoear_font_16, 0); + lv_obj_align(global_display_impl->display_obj[VOLC_DISPLAY_OBJ_STATUS], LV_ALIGN_TOP_MID, 0, 50); + lv_obj_set_height(global_display_impl->display_obj[VOLC_DISPLAY_OBJ_STATUS], 30); // 设置固定高度度 + } +} + +static void __subtitle_obj_init(volc_hal_display_impl_t* global_display_impl) +{ + if(global_display_impl && global_display_impl->display_obj[VOLC_DISPLAY_OBJ_SUBTITLE] == NULL){ + global_display_impl->display_obj[VOLC_DISPLAY_OBJ_SUBTITLE] = lv_label_create(global_display_impl->screen); + lv_obj_set_style_text_font(global_display_impl->display_obj[VOLC_DISPLAY_OBJ_SUBTITLE], &echoear_font_16, 0); + lv_obj_align(global_display_impl->display_obj[VOLC_DISPLAY_OBJ_SUBTITLE], LV_ALIGN_BOTTOM_MID, 0, -50); + lv_obj_set_height(global_display_impl->display_obj[VOLC_DISPLAY_OBJ_SUBTITLE], 30); // 设置固定高度度 + } + if(global_display_impl && global_display_impl->text_timer == NULL){ + global_display_impl->text_timer = lv_timer_create(__subtitle_update_cb, LVGL_TIMER_INTERVAL, NULL); + } +} + +static void __main_obj_init(volc_hal_display_impl_t* global_display_impl) +{ + if(global_display_impl && global_display_impl->display_obj[VOLC_DISPLAY_OBJ_MAIN] == NULL){ + global_display_impl->display_obj[VOLC_DISPLAY_OBJ_MAIN] = lv_img_create(global_display_impl->screen); + // size需要调整 + lv_obj_set_size(global_display_impl->display_obj[VOLC_DISPLAY_OBJ_MAIN], 250, 250); + lv_obj_align(global_display_impl->display_obj[VOLC_DISPLAY_OBJ_MAIN], LV_ALIGN_CENTER, 0, 0); + } +} + +volc_hal_display_t volc_hal_display_create(volc_hal_display_config_t* config) +{ + + volc_hal_context_t* g_hal_context = volc_get_global_hal_context(); + if(g_hal_context == NULL){ + return NULL; + } + + volc_hal_display_impl_t* global_display_impl = (volc_hal_display_impl_t*)volc_osal_calloc(1,sizeof(volc_hal_display_impl_t)); + // init display + // bsp_display_cfg_t cfg = { + // .lvgl_port_cfg = { + // .task_priority = LVGL_TASK_PRIORITY, + // .task_stack = LVGL_TASK_STACK_SIZE, + // .task_affinity = LVGL_TASK_CORE_ID, + // .task_max_sleep_ms = LVGL_TASK_MAX_SLEEP_MS, + // .task_stack_caps = MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT, + // .timer_period_ms = LVGL_TASK_TIMER_PERIOD_MS, + // }, + // .buffer_size = BSP_LCD_H_RES * 50, + // .double_buffer = true, + // .flags = { + // .buff_spiram = false, + // // .default_dummy_draw = default_dummy_draw, // Avoid white screen during initialization + // }, + // }; + // lv_disp_t *disp = bsp_display_start_with_config(&cfg); + // // bsp_display_brightness_init(); + // bsp_display_backlight_on(); + global_display_impl->screen = lv_scr_act(); + // printf("global_display_impl->screen %p \n",global_display_impl->screen); + __subtitle_obj_init(global_display_impl); + __status_obj_init(global_display_impl); + __main_obj_init(global_display_impl); + global_display_impl->is_init = true; + + g_hal_context->display_handle = (volc_hal_display_t)global_display_impl; + return (volc_hal_display_t)global_display_impl; +} + +int volc_hal_display_set_brightness(volc_hal_display_t display, int brightness) +{ + if(display == NULL) return -1; + if (brightness > 100) { + brightness = 100; + } + if (brightness < 0) { + brightness = 0; + } + return bsp_display_brightness_set(brightness); +} + +void volc_hal_display_destroy(volc_hal_display_t display) +{ + volc_hal_context_t* g_hal_context = volc_get_global_hal_context(); + if(g_hal_context == NULL){ + return; + } + volc_hal_display_impl_t* global_display_impl = (volc_hal_display_impl_t*)display; + if(global_display_impl && global_display_impl->is_init){ + global_display_impl->is_init = false; + lv_timer_del(global_display_impl->text_timer); + lv_obj_del(global_display_impl->screen); + } + volc_osal_free(global_display_impl); + global_display_impl = NULL; + g_hal_context->display_handle = NULL; + return; +} + +int volc_hal_display_set_content(volc_hal_display_t display, volc_hal_display_obj_e obj, volc_hal_display_type_e type, const void* content) +{ + volc_hal_context_t* g_hal_context = volc_get_global_hal_context(); + if(g_hal_context == NULL || g_hal_context->display_handle == NULL){ + return -1; + } + volc_hal_display_impl_t* global_display_impl = (volc_hal_display_impl_t*)(g_hal_context->display_handle); + + if(obj == VOLC_DISPLAY_OBJ_STATUS && type == VOLC_DISPLAY_TEXT){ + memset(global_display_impl->status_texts, 0, 64); + int count = strlen(content) >= 63 ? 63 : strlen(content); + memcpy(global_display_impl->status_texts,content,count); + } + if(obj == VOLC_DISPLAY_OBJ_SUBTITLE && type == VOLC_DISPLAY_TEXT){ + memset(global_display_impl->subtitle_texts, 0, 64); + int count = strlen(content) >= 63 ? 63 : strlen(content); + memcpy(global_display_impl->subtitle_texts,content,count); + } + if(obj == VOLC_DISPLAY_OBJ_MAIN && type == VOLC_DISPLAY_IMAGE){ + lv_image_dsc_t* img_app_pos = (lv_image_dsc_t*)content; + // lv_obj_set_size(global_display_impl->display_obj[VOLC_DISPLAY_OBJ_MAIN], 112, 112); + lv_img_set_src(global_display_impl->display_obj[VOLC_DISPLAY_OBJ_MAIN], img_app_pos); + lv_obj_remove_flag(global_display_impl->display_obj[VOLC_DISPLAY_OBJ_MAIN] ,LV_OBJ_FLAG_HIDDEN); + } + return 0; +} diff --git a/firmware/echoear-platform-agent/components/application/platform/src/espressif/volc_hal_file.c b/firmware/echoear-platform-agent/components/application/platform/src/espressif/volc_hal_file.c new file mode 100644 index 0000000..1e6f070 --- /dev/null +++ b/firmware/echoear-platform-agent/components/application/platform/src/espressif/volc_hal_file.c @@ -0,0 +1,55 @@ +// Copyright (2025) Beijing Volcano Engine Technology Ltd. +// SPDX-License-Identifier: Apache-2.0 + +#include + +#include "volc_hal_file.h" +// #include "board.h" +// #include "esp_peripherals.h" +#include "util/volc_log.h" +#include "basic_board.h" +#include "audio_processor.h" + +int volc_hal_file_system_init() +{ + return 0; +} + +volc_hal_file_t volc_hal_file_open(char* file_name, char* mode) +{ + FILE* file = fopen(file_name,mode); + if (file == NULL) { + LOGE("volc_hal_file_open failed"); + return NULL; + } + return (volc_hal_file_t)file; +} + +uint64_t volc_hal_file_read(volc_hal_file_t file, void *data, uint64_t size) +{ + return fread(data,1,size,(FILE*)file); +} + +uint64_t volc_hal_file_write(volc_hal_file_t file, void *data, uint64_t size) +{ + return fwrite(data,1,size,(FILE*)file); +} + +uint64_t volc_hal_file_get_size(volc_hal_file_t file) +{ + uint64_t file_offset = ftell((FILE*)file); + fseek((FILE*)file, 0, SEEK_END); + uint64_t file_size_long = ftell((FILE*)file); + fseek((FILE*)file,file_offset,SEEK_SET); + return file_size_long; +} + +int volc_hal_file_seek(volc_hal_file_t file,uint64_t offset) +{ + return fseek((FILE*)file,offset,SEEK_SET); +} + +int volc_hal_file_close(volc_hal_file_t file) +{ + return fclose((FILE*)file); +} \ No newline at end of file diff --git a/firmware/echoear-platform-agent/components/application/platform/src/espressif/volc_hal_led.c b/firmware/echoear-platform-agent/components/application/platform/src/espressif/volc_hal_led.c new file mode 100644 index 0000000..766402e --- /dev/null +++ b/firmware/echoear-platform-agent/components/application/platform/src/espressif/volc_hal_led.c @@ -0,0 +1,18 @@ +// Copyright (2025) Beijing Volcano Engine Technology Ltd. +// SPDX-License-Identifier: Apache-2.0 + +#include "volc_hal_led.h" + +#include + +volc_led_t volc_led_create(volc_led_config_t* config) { + return NULL; +} + +void volc_led_destroy(volc_led_t led) { + +} + +void volc_led_set_state(volc_led_t led, volc_led_state_e state) { + +} \ No newline at end of file diff --git a/firmware/echoear-platform-agent/components/application/platform/src/espressif/volc_hal_player.c b/firmware/echoear-platform-agent/components/application/platform/src/espressif/volc_hal_player.c new file mode 100644 index 0000000..29bcdae --- /dev/null +++ b/firmware/echoear-platform-agent/components/application/platform/src/espressif/volc_hal_player.c @@ -0,0 +1,315 @@ +// Copyright (2025) Beijing Volcano Engine Technology Ltd. +// SPDX-License-Identifier: Apache-2.0 + +#include + +#include "volc_hal.h" +#include "volc_hal_player.h" +#include "volc_osal.h" +#include "basic_board.h" + +#include "audio_processor.h" +#include "esp_codec_dev.h" +#include "util/volc_log.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/stream_buffer.h" +#include "freertos/semphr.h" +#include "esp_heap_caps.h" + +#define DEFAULT_AUDIO_VOL 70 +#define PCM_QUEUE_BYTES (256 * 1024) +#define PCM_PREBUFFER_BYTES (16000 * 2 * 750 / 1000) +#define PCM_PREBUFFER_WAIT_MS 1500 +#define PCM_REPRIME_IDLE_MS 600 +typedef struct { + av_processor_decoder_config_t feeder_cfg; + int volume; +} audio_player_config_t; + +typedef struct { + //TODO: add video player config + void* video_player_config; +} video_player_config_t; + +typedef struct { + volc_media_type_e media_type; // Media type + union { + audio_player_config_t audio_player_config; + video_player_config_t video_player_config; + }; + volatile bool is_started; + StreamBufferHandle_t pcm_stream; + StaticStreamBuffer_t stream_state; + uint8_t *stream_storage; + SemaphoreHandle_t worker_stopped; + volatile bool discard_pending; + volatile bool segment_complete; +} volc_player_impl_t; + +extern basic_board_periph_t global_periph; + +static void pcm_playback_task(void *arg) +{ + volc_player_impl_t *impl = arg; + uint8_t pcm[1024]; + bool primed = false; + uint64_t first_byte_ms = 0; + uint64_t last_feed_ms = 0; + while (impl->is_started) { + if (impl->discard_pending) { + while (xStreamBufferReceive(impl->pcm_stream, pcm, sizeof(pcm), 0)) {} + impl->discard_pending = false; + impl->segment_complete = false; + primed = false; + first_byte_ms = 0; + } + size_t available = xStreamBufferBytesAvailable(impl->pcm_stream); + if (!primed) { + if (!available) { + vTaskDelay(pdMS_TO_TICKS(10)); + continue; + } + if (!first_byte_ms) first_byte_ms = volc_osal_get_time_ms(); + if (available < PCM_PREBUFFER_BYTES && !impl->segment_complete && + volc_osal_get_time_ms() - first_byte_ms < PCM_PREBUFFER_WAIT_MS) { + vTaskDelay(pdMS_TO_TICKS(10)); + continue; + } + primed = true; + LOGI("PLAYBACK start source=16000Hz mono PCM16 buffered=%u wait_ms=%u complete=%d", + (unsigned)available, (unsigned)(volc_osal_get_time_ms() - first_byte_ms), impl->segment_complete); + } + size_t count = xStreamBufferReceive(impl->pcm_stream, pcm, sizeof(pcm), pdMS_TO_TICKS(30)); + if (!count) { + /* An empty ingress queue does not mean the speaker has run dry: + * the downstream feeder may still hold 256 ms of PCM. Keep feeding + * arriving chunks immediately instead of inserting another prefill + * delay after every short network gap. */ + if (volc_osal_get_time_ms() - last_feed_ms >= PCM_REPRIME_IDLE_MS) { + primed = false; + first_byte_ms = 0; + LOGI("PLAYBACK idle after %u ms without PCM", PCM_REPRIME_IDLE_MS); + } + continue; + } + if (audio_feeder_feed_data(pcm, count) == ESP_OK) { + last_feed_ms = volc_osal_get_time_ms(); + } else if (impl->is_started) { + LOGE("PLAYBACK feeder write failed"); + } + } + xSemaphoreGive(impl->worker_stopped); + vTaskDelete(NULL); +} + +volc_hal_player_t volc_hal_player_create(volc_hal_player_config_t* config) +{ + volc_hal_context_t* g_hal_context = volc_get_global_hal_context(); + if(g_hal_context == NULL){ + return NULL; + } + volc_player_impl_t* impl = volc_osal_calloc(1, sizeof(volc_player_impl_t)); + if (!impl) return NULL; + // mem_assert(impl); + impl->media_type = config->media_type; + switch (impl->media_type) { + case VOLC_MEDIA_TYPE_AUDIO: + if(global_periph.play_dev){ + esp_codec_dev_set_out_vol(global_periph.play_dev, DEFAULT_AUDIO_VOL); + } + impl->audio_player_config.volume = DEFAULT_AUDIO_VOL; +#if (CONFIG_VOLC_AUDIO_G711A) + impl->audio_player_config.feeder_cfg.format = AV_PROCESSOR_FORMAT_ID_G711A; + impl->audio_player_config.feeder_cfg.params.g711.audio_info.sample_rate = 8000; + impl->audio_player_config.feeder_cfg.params.g711.audio_info.sample_bits = 16; + impl->audio_player_config.feeder_cfg.params.g711.audio_info.channels = 1; + impl->audio_player_config.feeder_cfg.params.g711.audio_info.frame_duration = 20; +#else + impl->audio_player_config.feeder_cfg.format = AV_PROCESSOR_FORMAT_ID_PCM; + /* Hardware gateway PCM16 is 16 kHz, as in the low-load SDK and + * desktop client. The underlying model's 24 kHz output is not the + * sample rate of this gateway stream. */ + impl->audio_player_config.feeder_cfg.params.pcm.audio_info.sample_rate = 16000; + impl->audio_player_config.feeder_cfg.params.pcm.audio_info.sample_bits = 16; + impl->audio_player_config.feeder_cfg.params.pcm.audio_info.channels = 1; + impl->audio_player_config.feeder_cfg.params.pcm.audio_info.frame_duration = 20; +#endif + audio_feeder_config_t feeder_config = DEFAULT_AUDIO_FEEDER_CONFIG(); + memcpy((void *)&feeder_config.decoder_cfg, &impl->audio_player_config.feeder_cfg, sizeof(av_processor_decoder_config_t)); + if (audio_feeder_open(&feeder_config) != ESP_OK) { + volc_osal_free(impl); + return NULL; + } + impl->stream_storage = heap_caps_malloc(PCM_QUEUE_BYTES + 1, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + impl->worker_stopped = xSemaphoreCreateBinary(); + if (impl->stream_storage) { + impl->pcm_stream = xStreamBufferCreateStatic(PCM_QUEUE_BYTES + 1, 1, + impl->stream_storage, &impl->stream_state); + } + if (!impl->pcm_stream || !impl->worker_stopped) { + if (impl->pcm_stream) vStreamBufferDelete(impl->pcm_stream); + if (impl->worker_stopped) vSemaphoreDelete(impl->worker_stopped); + heap_caps_free(impl->stream_storage); + audio_feeder_close(); + volc_osal_free(impl); + return NULL; + } + // audio_processor_mixer_open(); + impl->is_started = false; + g_hal_context->player_handle[VOLC_HAL_PLAYER_AUDIO] = impl; + break; + case VOLC_MEDIA_TYPE_VIDEO: + // TODO: Add video player support + break; + default: + break; + } + return (volc_hal_player_t)impl; +} + +int volc_hal_set_audio_player_volume(volc_hal_player_t player, int volume){ + volc_player_impl_t* impl = (volc_player_impl_t*)player; + if (impl == NULL) { + return -1; + } + if(impl->media_type == VOLC_MEDIA_TYPE_AUDIO){ + impl->audio_player_config.volume = volume; + return esp_codec_dev_set_out_vol(global_periph.play_dev, volume); + } else if(impl->media_type == VOLC_MEDIA_TYPE_VIDEO){ + LOGE("media type %d not supported set volume", impl->media_type); + return -1; + } +} + +int volc_hal_get_audio_player_volume(volc_hal_player_t player){ + volc_player_impl_t* impl = (volc_player_impl_t*)player; + if (impl == NULL) { + return -1; + } + return impl->audio_player_config.volume; +} + +void volc_hal_player_destroy(volc_hal_player_t player) +{ + volc_hal_context_t* g_hal_context = volc_get_global_hal_context(); + if(g_hal_context == NULL){ + return; + } + volc_player_impl_t* impl = (volc_player_impl_t*)player; + if (impl == NULL) { + return; + } + volc_hal_player_stop(impl); + switch (impl->media_type) { + case VOLC_MEDIA_TYPE_AUDIO: + audio_feeder_close(); + vStreamBufferDelete(impl->pcm_stream); + vSemaphoreDelete(impl->worker_stopped); + heap_caps_free(impl->stream_storage); + + g_hal_context->player_handle[VOLC_HAL_PLAYER_AUDIO] = NULL; + break; + case VOLC_MEDIA_TYPE_VIDEO: + // TODO: Add video player support + break; + default: + break; + } + volc_osal_free(impl); +} + +int volc_hal_player_start(volc_hal_player_t player) +{ + volc_player_impl_t* impl = (volc_player_impl_t*)player; + if (impl == NULL) { + LOGE("volc_hal_player_start: impl is NULL"); + return -1; + } + if(impl->is_started) return 0; + switch (impl->media_type) { + case VOLC_MEDIA_TYPE_AUDIO: + if (audio_feeder_run() != ESP_OK) return -1; + xStreamBufferReset(impl->pcm_stream); + impl->is_started = true; + if (xTaskCreatePinnedToCore(pcm_playback_task, "pcm_playback", 4096, impl, 5, NULL, 1) != pdPASS) { + impl->is_started = false; + audio_feeder_stop(); + return -1; + } + return 0; + case VOLC_MEDIA_TYPE_VIDEO: + // TODO: Add video player support + break; + default: + break; + } + LOGE("media type %d not supported", impl->media_type); + return -1; +} + +int volc_hal_player_stop(volc_hal_player_t player) +{ + volc_player_impl_t* impl = (volc_player_impl_t*)player; + if (impl == NULL) { + LOGE("volc_hal_player_stop: impl is NULL"); + return -1; + } + if(impl->is_started == false) return 0; + switch (impl->media_type) { + case VOLC_MEDIA_TYPE_AUDIO: + impl->is_started = false; + /* Stop the feeder first to release any in-flight blocking write. */ + int ret = audio_feeder_stop(); + xSemaphoreTake(impl->worker_stopped, portMAX_DELAY); + return ret; + break; + case VOLC_MEDIA_TYPE_VIDEO: + // TODO: Add video player support + break; + default: + break; + } + LOGE("media type %d not supported", impl->media_type); + return -1; +} + +int volc_hal_player_play_data(volc_hal_player_t player, const void* data, int len) +{ + if (player == NULL || data == NULL || len <= 0) { + LOGE("player play data failed, player is %p, data is %p, len is %d", player, data, len); + return -1; + } + volc_player_impl_t* impl = (volc_player_impl_t*)player; + switch (impl->media_type) { + case VOLC_MEDIA_TYPE_AUDIO: + if (!impl->is_started || impl->discard_pending) return 0; + if ((len & 1) || xStreamBufferSpacesAvailable(impl->pcm_stream) < (size_t)len) { + LOGE("PLAYBACK queue overflow or unaligned PCM: bytes=%d free=%u", len, + (unsigned)xStreamBufferSpacesAvailable(impl->pcm_stream)); + return -1; + } + impl->segment_complete = false; + return xStreamBufferSend(impl->pcm_stream, data, len, 0) == (size_t)len ? 0 : -1; + break; + case VOLC_MEDIA_TYPE_VIDEO: + // TODO: Add video player support + break; + default: + break; + } + LOGE("media type %d not supported", impl->media_type); + return -1; +} + +void volc_hal_player_discard_pending(volc_hal_player_t player) +{ + volc_player_impl_t *impl = player; + if (impl && impl->is_started) impl->discard_pending = true; +} + +void volc_hal_player_finish_segment(volc_hal_player_t player) +{ + volc_player_impl_t *impl = player; + if (impl && impl->is_started) impl->segment_complete = true; +} diff --git a/firmware/echoear-platform-agent/components/application/service/CMakeLists.txt b/firmware/echoear-platform-agent/components/application/service/CMakeLists.txt new file mode 100644 index 0000000..7f70682 --- /dev/null +++ b/firmware/echoear-platform-agent/components/application/service/CMakeLists.txt @@ -0,0 +1,12 @@ +# Copyright (2025) Beijing Volcano Engine Technology Ltd. +# SPDX-License-Identifier: Apache License 2.0 + + +file(GLOB SERVICE_SRCS_COMMON "${CMAKE_CURRENT_LIST_DIR}/src/*.c") +file(GLOB SERVICE_SRCS_CONV "${CMAKE_CURRENT_LIST_DIR}/conv_service/src/*.c") +file(GLOB SERVICE_SRCS_FUNCTION_CALL "${CMAKE_CURRENT_LIST_DIR}/function_call_service/src/*.c") +set(SERVICE_SRCS ${SERVICE_SRCS_COMMON} ${SERVICE_SRCS_CONV} ${SERVICE_SRCS_FUNCTION_CALL}) + +set(SERVICE_INC "${CMAKE_CURRENT_LIST_DIR}/inc" + "${CMAKE_CURRENT_LIST_DIR}/conv_service/inc" + "${CMAKE_CURRENT_LIST_DIR}/function_call_service/inc") diff --git a/firmware/echoear-platform-agent/components/application/service/README.md b/firmware/echoear-platform-agent/components/application/service/README.md new file mode 100644 index 0000000..2f3995a --- /dev/null +++ b/firmware/echoear-platform-agent/components/application/service/README.md @@ -0,0 +1,10 @@ +# Service + +## brief introduction + +This directory presents the business module division of the Hardware Conversational Agent Development Kit for AI conversation services. All business modules are named "service", and different services communicate through event subscription (sub) and publication (pub) at the framework layer. The main modules are as follows: + +- conv_service:The core module of the AI Conversational Agent, responsible for the initiation, operation, and termination of AI conversation services. +- function_call_service:Interacts with the conv\_service module, responsible for executing function calls and returning the results to the agent. +- local_logic_service:A local module based on predefined business logic (not the agent's business logic). It performs tasks such as parsing subtitles and taking certain actions based on quick responses to the subtitles (different from function calls). +- src: An externally exposed module responsible for initiating the AI conversation. diff --git a/firmware/echoear-platform-agent/components/application/service/README_zh.md b/firmware/echoear-platform-agent/components/application/service/README_zh.md new file mode 100644 index 0000000..902c237 --- /dev/null +++ b/firmware/echoear-platform-agent/components/application/service/README_zh.md @@ -0,0 +1,11 @@ +# Service + +## 目录简介 + +本目录是硬件对话智能体开发套件对于ai对话业务进行的业务模块划分,不同的业务模块我们以service命名;不同的service通过framework层event的sub和pub进行通信。主要分为一下模块 + +- conv_service: 智能体ai对话的主要模块,负责ai对话业务的开始,运行,与结束。 +- function_call_service: 与conv_service模块交互,负责function call的执行并将结果返回给智能体 +- local_logic_service: 本地基于既定业务逻辑(非智能体的业务逻辑)的模块,例如解析字幕并根据字幕快速响应做出某些action等(区别于function call) +- src: 对外暴露的模块,负责ai对话的拉起 + diff --git a/firmware/echoear-platform-agent/components/application/service/conv_service/inc/volc_conv.h b/firmware/echoear-platform-agent/components/application/service/conv_service/inc/volc_conv.h new file mode 100644 index 0000000..9f2504c --- /dev/null +++ b/firmware/echoear-platform-agent/components/application/service/conv_service/inc/volc_conv.h @@ -0,0 +1,19 @@ +// Copyright (2025) Beijing Volcano Engine Technology Ltd. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef VOLC_CONV_SERVICE_H__ +#define VOLC_CONV_SERVICE_H__ + +#include "volc_conv_ai.h" +typedef struct { + volc_event_handler_t volc_event_handler; + volc_engine_t engine; + int wait_time; +} volc_conv_service_t; + +void conv_ai_service_task(void *pvParameters); +void conv_ai_service_task_stop(); +void conv_ai_service_init(); +volc_conv_service_t get_conv_ai_service(); + +#endif /* VOLC_CONV_SERVICE_H__ */ diff --git a/firmware/echoear-platform-agent/components/application/service/conv_service/inc/volc_conv_service.h b/firmware/echoear-platform-agent/components/application/service/conv_service/inc/volc_conv_service.h new file mode 100644 index 0000000..b8ca4b2 --- /dev/null +++ b/firmware/echoear-platform-agent/components/application/service/conv_service/inc/volc_conv_service.h @@ -0,0 +1,12 @@ +// Copyright (2025) Beijing Volcano Engine Technology Ltd. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef VOLC_CONV_SERVICE_MANAGER_H__ +#define VOLC_CONV_SERVICE_MANAGER_H__ + +#include +#include "aios.h" + +void volc_conv_service_manager_init(void); + +#endif // VOLC_CONV_SERVICE_MANAGER_H__ diff --git a/firmware/echoear-platform-agent/components/application/service/conv_service/src/volc_conv.c b/firmware/echoear-platform-agent/components/application/service/conv_service/src/volc_conv.c new file mode 100644 index 0000000..51c6a4d --- /dev/null +++ b/firmware/echoear-platform-agent/components/application/service/conv_service/src/volc_conv.c @@ -0,0 +1,523 @@ +// Copyright (2025) Beijing Volcano Engine Technology Ltd. +// SPDX-License-Identifier: Apache-2.0 +#include +#include + +#include "aios.h" +#include "cJSON.h" +#include "volc_hal.h" +#include "volc_osal.h" +#include "util/volc_log.h" +#include "volc_service_common.h" +#include "volc_conv.h" +#include "volc_hal.h" +#include "volc_hal_capture.h" +#include "volc_hal_display.h" +#include "volc_hal_player.h" +// #include "volc_hal_file.h" + +#include "volc_local_function_list.h" +#include "mmap_generate_eaf.h" +#include "freertos/FreeRTOS.h" +#include "freertos/queue.h" +extern int8_t get_rssi(void); + +#if __cplusplus +extern "C" { +#endif + +#define CONV_AI_CONFIG_FORMAT "{\ + \"ver\": 1,\ + \"iot\": {\ + \"instance_id\": \"%s\",\ + \"product_key\": \"%s\",\ + \"device_secret\": \"%s\",\ + \"device_name\": \"%s\"\ + },\ + \"rtc\": {\ + \"log_level\": 3,\ + \"audio\": {\ + \"publish\": true,\ + \"subscribe\": true,\ + \"codec\": 3\ + },\ + \"video\": {\ + \"publish\": false,\ + \"subscribe\": false,\ + \"codec\": 1\ + },\ + \"params\":[\ + \"{\\\"debug\\\":{\\\"log_to_console\\\":1}}\",\ + \"{\\\"rtc\\\":{\\\"access\\\":{\\\"concurrent_requests\\\":1}}}\",\ + \"{\\\"rtc\\\":{\\\"ice\\\":{\\\"concurrent_agents\\\":1}}}\",\ + \"{\\\"rtc\\\":{\\\"thread\\\":{\\\"stack_in_ext\\\":1}}}\",\ + \"{\\\"rtc\\\":{\\\"network\\\":{\\\"enable_audio_jitter2\\\":0}}}\",\ + \"{\\\"rtc\\\":{\\\"report\\\":{\\\"enable\\\":0}}}\",\ + \"{\\\"audio\\\":{\\\"codec\\\":{\\\"pcma\\\":{\\\"s_samples_per_frame\\\":480}}}}\"\ + ]\ + }\ +}" + +#define CONV_SERVICE_TIMEOUT 60 + +static char config_buf[1024] = {0}; +static volc_conv_service_t conv_service = {0}; + +static volatile bool is_stop = false; +static volatile bool provider_disconnected = false; +static volc_hal_display_t global_display = NULL; +static bool session_started_reported = false; +static QueueHandle_t platform_report_queue; +static volatile bool diagnostic_say_pending; + +void conv_ai_service_diagnostic_say(void) +{ + diagnostic_say_pending = true; + aios_event_pub(VOLC_SERVICE_AI_CONVERSATION, NULL, NULL); +} + +/* Implemented by the platform client in the top-level firmware component. */ +extern const char *platform_runtime_instance_id(void); +extern const char *platform_runtime_product_key(void); +extern const char *platform_runtime_device_name(void); +extern const char *platform_runtime_device_secret(void); +extern int platform_conversation_begin(void); +extern const char *platform_last_error_code(void); +extern const char *platform_conversation_bot_id(void); +extern const char *platform_conversation_update_json(void); +extern int platform_conversation_started(const char *provider_connection_ref); +extern int platform_conversation_poll(void); +extern int platform_conversation_turn(const char *speaker, const char *source_id, const char *text); +extern void platform_conversation_finish(bool failed, const char *error_code); + +volc_conv_service_t get_conv_ai_service() +{ + return conv_service; +} +// use dump audio +// volc_hal_file_t* audioFile = NULL; +// int a_cnt = 0; +static void __audio_capture_cb(volc_hal_capture_t capture, const void* data, int len, volc_hal_frame_info_t* frame_info){ + volc_audio_frame_info_t info = {0}; + info.commit = false; + info.data_type = frame_info->data_type; + if(!is_stop) + { + static uint64_t report_ms, magnitude; + static unsigned samples, peak, frames, failures, max_send_ms; + const int16_t *pcm = data; + if (frame_info->data_type == VOLC_AUDIO_DATA_TYPE_PCM) { + for (int i = 0; i < len / sizeof(int16_t); ++i) { + unsigned value = pcm[i] < 0 ? -(int)pcm[i] : pcm[i]; + magnitude += value; + if (value > peak) peak = value; + samples++; + } + } + uint64_t before = volc_osal_get_time_ms(); + int sent = volc_send_audio_data(conv_service.engine, data, len, &info); + unsigned elapsed = volc_osal_get_time_ms() - before; + if (elapsed > max_send_ms) max_send_ms = elapsed; + frames++; + if (sent < 0) failures++; + if (before - report_ms >= 2000) { + int rssi = get_rssi(); + LOGI("UPLINK mean=%u peak=%u frames=%u failed=%u max_send_ms=%u rssi=%d", + samples ? (unsigned)(magnitude / samples) : 0, peak, frames, failures, max_send_ms, rssi); + report_ms = before; + magnitude = samples = peak = frames = failures = max_send_ms = 0; + } + // if(audioFile == NULL){ + // audioFile = volc_hal_file_open("/sdcard/cap.pcm","wb"); + // volc_hal_file_write(audioFile,data,len); + // } + } +} + +static void __on_volc_event(volc_engine_t handle, volc_event_t *event, void *user_data) +{ + switch (event->code) + { + case VOLC_EV_CONNECTED: + LOGI("Volc Engine connected"); + break; + case VOLC_EV_DISCONNECTED: + LOGI( "Volc Engine disconnected"); + provider_disconnected = true; + is_stop = true; + break; + default: + LOGI("Volc Engine event: %d", event->code); + break; + } +} + +static void __on_volc_conversation_status(volc_engine_t handle, volc_conv_status_e status, void *user_data) +{ + int index = MMAP_EAF_HAPPY_EAF; + switch (status) + { + case VOLC_CONV_STATUS_LISTENING: + index = MMAP_EAF_EMOTION_BLINK_FAST_284_126_EAF; + volc_hal_display_set_content(global_display,VOLC_DISPLAY_OBJ_MAIN,VOLC_DISPLAY_IMAGE,&index); + volc_hal_display_set_content(global_display,VOLC_DISPLAY_OBJ_STATUS,VOLC_DISPLAY_TEXT,"智能体聆听中"); + break; + case VOLC_CONV_STATUS_THINKING: + index = MMAP_EAF_EMOTION_DIZZY_284_126_EAF; + volc_hal_display_set_content(global_display,VOLC_DISPLAY_OBJ_MAIN,VOLC_DISPLAY_IMAGE,&index); + volc_hal_display_set_content(global_display,VOLC_DISPLAY_OBJ_STATUS,VOLC_DISPLAY_TEXT,"智能体思考中"); + break; + case VOLC_CONV_STATUS_ANSWERING: + index = MMAP_EAF_HAPPY_EAF; + volc_hal_display_set_content(global_display,VOLC_DISPLAY_OBJ_MAIN,VOLC_DISPLAY_IMAGE,&index); + volc_hal_display_set_content(global_display,VOLC_DISPLAY_OBJ_STATUS,VOLC_DISPLAY_TEXT,"智能体说话中"); + break; + case VOLC_CONV_STATUS_INTERRUPTED: + { + volc_hal_context_t *hal = volc_get_global_hal_context(); + if (hal) volc_hal_player_discard_pending(hal->player_handle[VOLC_HAL_PLAYER_AUDIO]); + } + volc_hal_display_set_content(global_display,VOLC_DISPLAY_OBJ_STATUS,VOLC_DISPLAY_TEXT,"智能体被打断"); + break; + case VOLC_CONV_STATUS_ANSWER_FINISH: + volc_hal_display_set_content(global_display,VOLC_DISPLAY_OBJ_STATUS,VOLC_DISPLAY_TEXT,"智能体说话完成"); + break; + default: + break; + } + return; +} + +static void __on_volc_audio_data(volc_engine_t handle, const void *data_ptr, size_t data_len, volc_audio_frame_info_t *info_ptr, void *user_data) +{ + int error = 0; + volc_hal_context_t* g_hal_context = volc_get_global_hal_context(); + if (g_hal_context == NULL) { + LOGE("volc_get_global_hal_context failed"); + return; + } + + volc_hal_player_t player_handle = g_hal_context->player_handle[VOLC_HAL_PLAYER_AUDIO]; + + if (player_handle == NULL) + { + LOGE("player pipeline is NULL"); + return; + } + if(!is_stop){ + volc_hal_player_play_data(player_handle, data_ptr, data_len); + } +} + +static void __on_volc_video_data(volc_engine_t handle, const void *data_ptr, size_t data_len, volc_video_frame_info_t *info_ptr, void *user_data) +{ +} + +static int sub_offset = 0; +static void __on_subtitle_message_received(const cJSON* root) { + /* + { + "data" : + [ + { + "definite" : false, + "language" : "zh", + "mode" : 1, + "paragraph" : false, + "sequence" : 0, + "text" : "\\u4f60\\u597d", + "userId" : "voiceChat_xxxxx" + } + ], + "type" : "subtitle" + } + */ + cJSON * type_obj = cJSON_GetObjectItem(root, "type"); + if (type_obj != NULL && strcmp("subtitle", cJSON_GetStringValue(type_obj)) == 0) { + cJSON* data_obj_arr = cJSON_GetObjectItem(root, "data"); + cJSON* obji = NULL; + cJSON_ArrayForEach(obji, data_obj_arr) { + cJSON* user_id_obj = cJSON_GetObjectItem(obji, "userId"); + cJSON* text_obj = cJSON_GetObjectItem(obji, "text"); + cJSON* definite_obj = cJSON_GetObjectItem(obji, "definite"); + char* sub = cJSON_GetStringValue(text_obj); + + // print_string(sub + sub_offset -1); + if(cJSON_IsTrue(definite_obj)){ + sub_offset = sub_offset == 0 ? 0 : sub_offset -1; + volc_hal_display_set_content(global_display,VOLC_DISPLAY_OBJ_SUBTITLE,VOLC_DISPLAY_TEXT,sub + sub_offset); + sub_offset = 0; + aios_event_pub(VOLC_LOCAL_LOGIC_PROCESS_SUBTITLE, sub, NULL); + } else if(strlen(sub) - sub_offset > 18){ + sub_offset = sub_offset == 0 ? 0 : sub_offset -1; + volc_hal_display_set_content(global_display,VOLC_DISPLAY_OBJ_SUBTITLE,VOLC_DISPLAY_TEXT,sub + sub_offset); + if(strlen(sub) - sub_offset > 36){ + sub_offset = strlen(sub); + } + } + } + } +} + +/* HTTPS reporting must never run inside the WebSocket receive mutex. */ +static void process_platform_reports(void) +{ + cJSON *root; + while (platform_report_queue && xQueueReceive(platform_report_queue, &root, 0) == pdTRUE) { + cJSON *type_obj = cJSON_GetObjectItemCaseSensitive(root, "type"); + const char *type = cJSON_IsString(type_obj) ? type_obj->valuestring : ""; + if ((strcmp(type, "session.updated") == 0 || strcmp(type, "session.created") == 0) && !session_started_reported) { + cJSON *session = cJSON_GetObjectItemCaseSensitive(root, "session"); + cJSON *id = cJSON_IsObject(session) ? cJSON_GetObjectItemCaseSensitive(session, "id") : NULL; + cJSON *job_id = cJSON_GetObjectItemCaseSensitive(root, "job_id"); + const char *provider_ref = cJSON_IsString(id) && id->valuestring[0] ? id->valuestring : + (cJSON_IsString(job_id) ? job_id->valuestring : NULL); + if (provider_ref && provider_ref[0] && platform_conversation_started(provider_ref) == 0) { + session_started_reported = true; + LOGI("Platform confirmed dynamic session configuration"); + } + } else if (strcmp(type, "conversation.item.input_audio_transcription.completed") == 0) { + cJSON *text = cJSON_GetObjectItemCaseSensitive(root, "transcript"); + cJSON *source = cJSON_GetObjectItemCaseSensitive(root, "event_id"); + if (cJSON_IsString(text) && text->valuestring[0]) { + (void)platform_conversation_turn("subject", cJSON_IsString(source) ? source->valuestring : "subject", text->valuestring); + } + } else if (strcmp(type, "response.audio_transcript.done") == 0) { + cJSON *text = cJSON_GetObjectItemCaseSensitive(root, "transcript"); + cJSON *source = cJSON_GetObjectItemCaseSensitive(root, "response_id"); + if (cJSON_IsString(text) && text->valuestring[0]) { + (void)platform_conversation_turn("agent", cJSON_IsString(source) ? source->valuestring : "agent", text->valuestring); + } + } + cJSON_Delete(root); + } +} + +static void __on_volc_message_data(volc_engine_t handle, const void *message, size_t size, volc_message_info_t *info_ptr, void *user_data) +{ + static char message_buffer[4096]; + if (size > 0 && size < sizeof(message_buffer) - 1) { + memcpy(message_buffer, message, size); + message_buffer[size] = 0; + const bool standard_protocol = message_buffer[0] == '{'; + if (!standard_protocol && size < 8) return; + cJSON *root = cJSON_Parse(standard_protocol ? message_buffer : message_buffer + 8); + if (root != NULL) { + if (standard_protocol) { + const char *type = cJSON_GetStringValue(cJSON_GetObjectItem(root, "type")); + if (type && (!strcmp(type, "error") || !strcmp(type, "conversation.item.created"))) { + LOGI("Provider control event: %s", message_buffer); + } + if (type && !strcmp(type, "response.audio.done") && !is_stop) { + /* Flush the final sub-frame through the PCM decoder. */ + static const uint8_t silence[2048] = {0}; + volc_hal_context_t *hal = volc_get_global_hal_context(); + if (hal) { + volc_hal_player_t player = hal->player_handle[VOLC_HAL_PLAYER_AUDIO]; + volc_hal_player_play_data(player, silence, sizeof(silence)); + volc_hal_player_finish_segment(player); + } + } + if (type && (strstr(type, "speech_") || strstr(type, "response."))) { + conv_service.wait_time = 0; + } + bool report = type && (!strcmp(type, "session.created") || !strcmp(type, "session.updated") || + !strcmp(type, "conversation.item.input_audio_transcription.completed") || + !strcmp(type, "response.audio_transcript.done")); + if (report && platform_report_queue && xQueueSend(platform_report_queue, &root, 0) == pdTRUE) { + root = NULL; /* Conversation task owns queued messages. */ + } else if (report) { + LOGE("Platform report queue unavailable or full"); + } + cJSON_Delete(root); + } else if (message_buffer[0] == 's' && message_buffer[1] == 'u' && message_buffer[2] == 'b' && message_buffer[3] == 'v') { + __on_subtitle_message_received(root); + conv_service.wait_time = 0; + } else if (message_buffer[0] == 't' && message_buffer[1] == 'o' && message_buffer[2] == 'o' && message_buffer[3] == 'l') { + // function calling 消息 + aios_event_pub(VOLC_FUNCTION_CALL_EXEC, root, NULL); + }else if (message_buffer[0] == 'i' && message_buffer[1] == 'n' && message_buffer[2] == 'f' && message_buffer[3] == 'o') { + aios_event_pub(VOLC_FUNCTION_CALL_TRIGGER, root, NULL); + } + } + } +} + +void conv_ai_service_init() +{ + if (!platform_report_queue) platform_report_queue = xQueueCreate(8, sizeof(cJSON *)); + snprintf(config_buf, sizeof(config_buf), CONV_AI_CONFIG_FORMAT, + platform_runtime_instance_id(), + platform_runtime_product_key(), + platform_runtime_device_secret(), + platform_runtime_device_name()); + + conv_service.volc_event_handler = (volc_event_handler_t){ + .on_volc_event = __on_volc_event, + .on_volc_conversation_status = __on_volc_conversation_status, + .on_volc_audio_data = __on_volc_audio_data, + .on_volc_video_data = __on_volc_video_data, + .on_volc_message_data = __on_volc_message_data, + }; + + int ret = volc_create(&conv_service.engine, config_buf, &conv_service.volc_event_handler, &conv_service); + if (ret != 0) { + LOGE("volc_create failed, ret: %d", ret); + return; + } else { + LOGE("volc_create success"); + } +} + +void conv_ai_service_task(void *pvParameters) +{ + volc_hal_context_t* g_hal_context = volc_get_global_hal_context(); + char* status_str = NULL; + bool provider_started = false; + bool audio_started = false; + session_started_reported = false; + provider_disconnected = false; + if (g_hal_context == NULL) { + LOGE("volc_get_global_hal_context failed"); + status_str = "系统未初始化,ai对话连接失败"; + goto CONV_AI_QUIT; + } + global_display = g_hal_context->display_handle; + int ret = 0; + volc_hal_display_set_content(global_display, VOLC_DISPLAY_OBJ_STATUS,VOLC_DISPLAY_TEXT, "ai对话创建中"); + + ret = platform_conversation_begin(); + if (ret != 0) { + LOGE("platform_conversation_begin failed, ret: %d", ret); + status_str = "平台会话创建失败"; + if (strcmp(platform_last_error_code(), "FIRMWARE_UPDATE_REQUIRED") == 0) { + status_str = "平台要求更新固件"; + } + goto CONV_AI_QUIT; + } + + /* Capture may run while the WebSocket is still handshaking. Hold frames + * until volc_start has moved the engine into STARTED state. */ + is_stop = true; + + // step 1: start audio capture & play + volc_hal_capture_config_t capture_audio_config = {0}; + capture_audio_config.media_type = VOLC_MEDIA_TYPE_AUDIO; + capture_audio_config.data_cb = __audio_capture_cb; + // capture_audio_config.audio_wakeup_cb = (volc_hal_audio_wakeup_cb_t)rec_engine_cb; + + capture_audio_config.user_data = g_hal_context; + volc_hal_capture_t capture_handle = g_hal_context->capture_handle[VOLC_HAL_PLAYER_AUDIO]; + if (g_hal_context->capture_handle[VOLC_HAL_PLAYER_AUDIO] == NULL) { + g_hal_context->capture_handle[VOLC_HAL_PLAYER_AUDIO] = volc_hal_capture_create(&capture_audio_config); + capture_handle = g_hal_context->capture_handle[VOLC_HAL_PLAYER_AUDIO]; + } + audio_started = true; + volc_hal_capture_stop(g_hal_context->capture_handle[VOLC_HAL_CAPTURE_AUDIO]); + volc_hal_capture_update_config(capture_handle, &capture_audio_config); + volc_hal_capture_start(g_hal_context->capture_handle[VOLC_HAL_PLAYER_AUDIO], VOLC_AUDIO_MODE_CAPTURE); + + volc_hal_player_config_t player_audio_config = {0}; + player_audio_config.media_type = VOLC_MEDIA_TYPE_AUDIO; + if (g_hal_context->player_handle[VOLC_HAL_PLAYER_AUDIO] == NULL) { + g_hal_context->player_handle[VOLC_HAL_PLAYER_AUDIO] = volc_hal_player_create(&player_audio_config); + } + + volc_hal_display_set_content(global_display, VOLC_DISPLAY_OBJ_STATUS, VOLC_DISPLAY_TEXT, "ai对话连接中"); + volc_hal_player_start(g_hal_context->player_handle[VOLC_HAL_PLAYER_AUDIO]); + + // step 2: start ai conversation + const char *session_update = platform_conversation_update_json(); + size_t params_size = strlen(session_update) + 64; + char *params = volc_osal_malloc(params_size); + if (params == NULL) { + status_str = "会话配置内存不足"; + goto CONV_AI_QUIT; + } + snprintf(params, params_size, "{\"audio\":{\"codec\":0},\"session_update\":%s}", session_update); + volc_opt_t opt = { + .mode = VOLC_MODE_WS, + .bot_id = (char *)platform_conversation_bot_id(), + .params = params, + }; +#if defined ENABLE_PLAY_WELCOME + aios_event_pub(VOLC_LOCAL_LOGIC_PLAY_WELCOME, NULL, NULL); +#endif + ret = volc_start(conv_service.engine, &opt); + volc_osal_free(params); + if (ret != 0) { + LOGE("volc_start failed, ret: %d", ret); + status_str = "ai对话连接失败,准备退出"; + goto CONV_AI_QUIT; + } + provider_started = true; + volc_hal_display_set_content(global_display, VOLC_DISPLAY_OBJ_STATUS, VOLC_DISPLAY_TEXT, "ai对话已连接"); + is_stop = false; + + while (!is_stop && !provider_disconnected) { + process_platform_reports(); + if (session_started_reported && platform_conversation_poll()) { + status_str = "平台已结束本次对话"; + goto CONV_AI_QUIT; + } + if (diagnostic_say_pending && session_started_reported) { + diagnostic_say_pending = false; + char request[640]; + snprintf(request, sizeof(request), "{\"event_id\":\"diag-%llu\",\"type\":\"conversation.item.create\",\"item\":{\"type\":\"message\",\"role\":\"user\",\"content\":[{\"type\":\"input_text\",\"text\":\"请只说这句话:你好,这是语音播放测试。现在使用自然的语速,连续清楚地说完这句话。\"}],\"interrupt_mode\":1}}", (unsigned long long)volc_osal_get_time_ms()); + volc_message_info_t info = {.is_binary = false}; + int sent = volc_send_message(conv_service.engine, request, strlen(request), &info); + LOGI("PLAYBACK diagnostic sentence requested ret=%d", sent); + const char *respond = "{\"type\":\"response.create\",\"response\":{\"modalities\":[\"text\",\"audio\"]}}"; + volc_send_message(conv_service.engine, respond, strlen(respond), &info); + conv_service.wait_time = 0; + } + sleep(1); + conv_service.wait_time++; + if(conv_service.wait_time >= CONV_SERVICE_TIMEOUT){ + status_str = "ai对话已超时,准备退出"; + goto CONV_AI_QUIT; + } + } + status_str = "ai对话已断开"; + +CONV_AI_QUIT: + is_stop = true; + // volc_hal_file_close(audioFile); + volc_stop(conv_service.engine); + process_platform_reports(); + platform_conversation_finish(!provider_started || !session_started_reported, + !provider_started ? "VOLC_START_FAILED" : (!session_started_reported ? "SESSION_UPDATE_NOT_CONFIRMED" : NULL)); + + if (g_hal_context && audio_started) { + volc_hal_capture_stop(g_hal_context->capture_handle[VOLC_HAL_CAPTURE_AUDIO]); + if (g_hal_context->player_handle[VOLC_HAL_PLAYER_AUDIO]) { + volc_hal_player_stop(g_hal_context->player_handle[VOLC_HAL_PLAYER_AUDIO]); + volc_hal_player_destroy(g_hal_context->player_handle[VOLC_HAL_PLAYER_AUDIO]); + g_hal_context->player_handle[VOLC_HAL_PLAYER_AUDIO] = NULL; + } + } + + if(status_str != NULL){ + volc_hal_display_set_content(global_display, VOLC_DISPLAY_OBJ_STATUS, VOLC_DISPLAY_TEXT, status_str); + } + volc_hal_display_set_content(global_display, VOLC_DISPLAY_OBJ_SUBTITLE, VOLC_DISPLAY_TEXT, ""); + conv_service.wait_time = 0; + // just show the status: Ai对话已断开 + sleep(1); + if (g_hal_context && audio_started) { + volc_hal_capture_start(g_hal_context->capture_handle[VOLC_HAL_CAPTURE_AUDIO],VOLC_AUDIO_MODE_WAKEUP); + } + aios_event_pub(VOLC_SERVICE_AI_CONVERSATION_OVER, NULL, NULL); + volc_osal_thread_exit(NULL); +} + +void conv_ai_service_task_stop(void) +{ + // sleep for AI Agent say goodbye + sleep(1); + is_stop = true; +} + +#if __cplusplus +} +#endif diff --git a/firmware/echoear-platform-agent/components/application/service/conv_service/src/volc_conv_service.c b/firmware/echoear-platform-agent/components/application/service/conv_service/src/volc_conv_service.c new file mode 100644 index 0000000..2116a2e --- /dev/null +++ b/firmware/echoear-platform-agent/components/application/service/conv_service/src/volc_conv_service.c @@ -0,0 +1,84 @@ +// Copyright (2025) Beijing Volcano Engine Technology Ltd. +// SPDX-License-Identifier: Apache-2.0 +#include + +#include "aios.h" +#include "volc_hal.h" +#include "volc_osal.h" +#include "volc_hal_display.h" +#include "volc_hal_file.h" + +#include "volc_service_common.h" +#include "volc_conv_service.h" +#include "volc_conv.h" + +#if __cplusplus +extern "C" { +#endif + +typedef struct volc_conv_service_manager { + aios_session_t super; + volc_osal_tid_t conv_thread_id; +} volc_conv_service_manager_t; + +volc_conv_service_manager_t volc_conv_service_manager; + +static aios_ret_t __state_conversation(volc_conv_service_manager_t * const me, aios_event_t const * const e) +{ + volc_hal_context_t* g_hal_context = volc_get_global_hal_context(); + if (g_hal_context == NULL) { + return AIOS_Ret_NotHandled; + } + volc_hal_display_t global_display = g_hal_context->display_handle; + switch (e->id) { + case VOLC_SERVICE_AI_CONVERSATION_START: + if (me->conv_thread_id == NULL) { + volc_osal_thread_param_t param = {0}; + volc_hal_display_set_content(global_display,VOLC_DISPLAY_OBJ_STATUS,VOLC_DISPLAY_TEXT,"ai对话启动中"); + snprintf(param.name, sizeof(param.name), "%s", "conv_ai"); + param.stack_size = 8*1024; + param.priority = 5; + param.stack_in_ext = 1; + volc_osal_thread_create(&me->conv_thread_id, ¶m, conv_ai_service_task,NULL); + } + return AIOS_Ret_Handled; + case VOLC_SERVICE_AI_CONVERSATION_INTERRUPT: + return AIOS_Ret_Handled; + // control the Ai_Conversation task quit + case VOLC_SERVICE_AI_CONVERSATION_QUIT: + if (me->conv_thread_id != NULL) { + conv_ai_service_task_stop(); + me->conv_thread_id = NULL; + } + return AIOS_Ret_Handled; + // notice the Ai_Conversation over by task + case VOLC_SERVICE_AI_CONVERSATION_OVER: + if (me->conv_thread_id != NULL) { + me->conv_thread_id = NULL; + } + return AIOS_Ret_Handled; + default: + return AIOS_Ret_NotHandled; + } +} + +static aios_ret_t __state_init(volc_conv_service_manager_t * const me, aios_event_t const * const e) +{ + AIOS_EVENT_SUB(VOLC_SERVICE_AI_CONVERSATION_START); // 订阅事件 + AIOS_EVENT_SUB(VOLC_SERVICE_AI_CONVERSATION_INTERRUPT); // 订阅事件 + AIOS_EVENT_SUB(VOLC_SERVICE_AI_CONVERSATION_QUIT); // 订阅事件 + AIOS_EVENT_SUB(VOLC_SERVICE_AI_CONVERSATION_OVER); // 订阅事件 + me->conv_thread_id = NULL; + conv_ai_service_init(); + return AIOS_TRAN(__state_conversation); +} + +void volc_conv_service_manager_init(void) +{ + aios_session_init(&volc_conv_service_manager.super, VOLC_SERVICE_PRIORITY_AI_CONVERSATION, NULL); + aios_session_start(&volc_conv_service_manager.super, AIOS_STATE_CAST(__state_init)); +} + +#if __cplusplus +} +#endif diff --git a/firmware/echoear-platform-agent/components/application/service/function_call_service/inc/volc_function_call_service.h b/firmware/echoear-platform-agent/components/application/service/function_call_service/inc/volc_function_call_service.h new file mode 100644 index 0000000..baf3f73 --- /dev/null +++ b/firmware/echoear-platform-agent/components/application/service/function_call_service/inc/volc_function_call_service.h @@ -0,0 +1,9 @@ +// Copyright (2025) Beijing Volcano Engine Technology Ltd. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef VOLC_FUNCTION_CALL_SERVICE_H__ +#define VOLC_FUNCTION_CALL_SERVICE_H__ + +void function_call_service_init(); + +#endif /* VOLC_FUNCTION_CALL_SERVICE_H__ */ diff --git a/firmware/echoear-platform-agent/components/application/service/function_call_service/inc/volc_local_function_list.h b/firmware/echoear-platform-agent/components/application/service/function_call_service/inc/volc_local_function_list.h new file mode 100644 index 0000000..ccfc58f --- /dev/null +++ b/firmware/echoear-platform-agent/components/application/service/function_call_service/inc/volc_local_function_list.h @@ -0,0 +1,9 @@ +// Copyright (2025) Beijing Volcano Engine Technology Ltd. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef VOLC_LOCAL_FUNCTION_LIST_H__ +#define VOLC_LOCAL_FUNCTION_LIST_H__ + +void adjust_audio_val(const char* action); + +#endif diff --git a/firmware/echoear-platform-agent/components/application/service/function_call_service/src/volc_function_call_service.c b/firmware/echoear-platform-agent/components/application/service/function_call_service/src/volc_function_call_service.c new file mode 100644 index 0000000..368c87d --- /dev/null +++ b/firmware/echoear-platform-agent/components/application/service/function_call_service/src/volc_function_call_service.c @@ -0,0 +1,134 @@ +// Copyright (2025) Beijing Volcano Engine Technology Ltd. +// SPDX-License-Identifier: Apache-2.0 + +#include + +#include "cJSON.h" + +#include "volc_service_common.h" +#include "volc_conv.h" +#include "volc_function_call_service.h" +#include "volc_local_function_list.h" +#include "aios.h" +#include +#if __cplusplus +extern "C" { +#endif + +typedef struct volc_function_call_service { + aios_session_t super; +} volc_function_call_service_t; + +volc_function_call_service_t g_volc_function_call_service; + +static void __on_function_calling_message_received(const cJSON *root) +{ + /* + { + "subscriber_user_id" : "", + "tool_calls" : + [ + { + "function" : + { + "arguments" : "{\\"location\\": \\"\\u5317\\u4eac\\u5e02\\"}", + "name" : "get_current_weather" + }, + "id" : "call_py400kek0e3pczrqdxgnb3lo", + "type" : "function" + } + ] + } + */ + + cJSON *tool_obj_arr = cJSON_GetObjectItem(root, "tool_calls"); + cJSON *obji = NULL; + cJSON_ArrayForEach(obji, tool_obj_arr) + { + cJSON *id_obj = cJSON_GetObjectItem(obji, "id"); + cJSON *function_obj = cJSON_GetObjectItem(obji, "function"); + if (id_obj && function_obj) + { + cJSON *arguments_obj = cJSON_GetObjectItem(function_obj, "arguments"); + cJSON *name_obj = cJSON_GetObjectItem(function_obj, "name"); + char *arguments_json_str = cJSON_GetStringValue(arguments_obj); + const char *func_name = cJSON_GetStringValue(name_obj); + const char *func_id = cJSON_GetStringValue(id_obj); + if (strcmp(func_name, "adjust_audio_val") == 0 && arguments_json_str) + { + cJSON *arguments_json = cJSON_Parse(arguments_json_str); + cJSON *action_obj = cJSON_GetObjectItem(arguments_json, "action"); + const char *action = (action_obj->valuestring); + adjust_audio_val(action); + volc_conv_service_t conv_service = get_conv_ai_service(); + cJSON *fc_obj = cJSON_CreateObject(); + cJSON_AddStringToObject(fc_obj, "ToolCallID", func_id); + cJSON_AddStringToObject(fc_obj, "Content", "音量已经调整了"); + char *json_string = cJSON_Print(fc_obj); + static char fc_message_buffer[256] = {'f', 'u', 'n', 'c'}; + int json_str_len = strlen(json_string); + fc_message_buffer[4] = (json_str_len >> 24) & 0xff; + fc_message_buffer[5] = (json_str_len >> 16) & 0xff; + fc_message_buffer[6] = (json_str_len >> 8) & 0xff; + fc_message_buffer[7] = (json_str_len >> 0) & 0xff; + memcpy(fc_message_buffer + 8, json_string, json_str_len); + cJSON_Delete(fc_obj); + volc_message_info_t info = {0}; + info.is_binary = 1; + volc_send_message(conv_service.engine, fc_message_buffer, json_str_len + 8, &info); + } + } + } + cJSON_Delete(root); +} + +void __on_function_calling_trigger(const cJSON *root) +{ +/* +{ + "event_type": "function_calling", // 事件类型,固定为此值 + "function": "get_current_weather", // 被触发的函数名,与您在 StartVoiceChat 中定义的名称一致 + "tool_call_id": "call_12345", // 本次函数调用的唯一ID,后续回传结果时需要用到 + "response_id": "resp_abcde" // 本次响应的唯一ID,可用于问题排查 +} +*/ + cJSON *event_type_obj = cJSON_GetObjectItemCaseSensitive(root, "event_type"); + if(event_type_obj && strcmp(event_type_obj->valuestring, "function_calling") == 0 ) { + cJSON *function_obj = cJSON_GetObjectItemCaseSensitive(root, "function"); + if(function_obj && strcmp(function_obj->valuestring, "adjust_audio_val") == 0){ + volc_conv_service_t conv_service = get_conv_ai_service(); + volc_send_text_to_agent(conv_service.engine, "别着急,我来给你调整音量", VOLC_AGENT_TYPE_TTS,2); + } + } + cJSON_Delete(root); +} + + +static aios_ret_t __state_conversation(volc_function_call_service_t * const me, aios_event_t const * const e) +{ + switch (e->id) { + case VOLC_FUNCTION_CALL_EXEC: + __on_function_calling_message_received((const cJSON *)e->data); + return AIOS_Ret_Handled; + case VOLC_FUNCTION_CALL_TRIGGER: + __on_function_calling_trigger((const cJSON *)e->data); + return AIOS_Ret_Handled; + } +} + +static aios_ret_t __state_init(volc_function_call_service_t * const me, aios_event_t const * const e) +{ + AIOS_EVENT_SUB(VOLC_FUNCTION_CALL_EXEC); // 订阅事件 + AIOS_EVENT_SUB(VOLC_FUNCTION_CALL_TRIGGER); // 订阅事件 + + return AIOS_TRAN(__state_conversation); +} + +void function_call_service_init(void){ + aios_session_init(&g_volc_function_call_service.super, VOLC_SERVICE_PRIORITY_FUNCTION_CALL, NULL); + aios_session_start(&g_volc_function_call_service.super, AIOS_STATE_CAST(__state_init)); +} + +#if __cplusplus +} +#endif diff --git a/firmware/echoear-platform-agent/components/application/service/function_call_service/src/volc_local_function_list.c b/firmware/echoear-platform-agent/components/application/service/function_call_service/src/volc_local_function_list.c new file mode 100644 index 0000000..538d58e --- /dev/null +++ b/firmware/echoear-platform-agent/components/application/service/function_call_service/src/volc_local_function_list.c @@ -0,0 +1,41 @@ +#include "volc_local_function_list.h" +#include "volc_hal.h" +#include "volc_hal_player.h" + +#include + +void __volc_up_audio_val() +{ + volc_hal_context_t *g_hal_context = volc_get_global_hal_context(); + if (g_hal_context == NULL) + { + return; + } + int volc_audio_vol = volc_hal_get_audio_player_volume(g_hal_context->player_handle[VOLC_HAL_PLAYER_AUDIO]); + volc_audio_vol = (volc_audio_vol + 10) <= 100 ? (volc_audio_vol + 10) : 100; + volc_hal_set_audio_player_volume(g_hal_context->player_handle[VOLC_HAL_PLAYER_AUDIO], volc_audio_vol); +} + +void __volc_down_audio_val() +{ + volc_hal_context_t *g_hal_context = volc_get_global_hal_context(); + if (g_hal_context == NULL) + { + return; + } + int volc_audio_vol = volc_hal_get_audio_player_volume(g_hal_context->player_handle[VOLC_HAL_PLAYER_AUDIO]); + volc_audio_vol = (volc_audio_vol - 10) <= 0 ? 0 : (volc_audio_vol - 10); + volc_hal_set_audio_player_volume(g_hal_context->player_handle[VOLC_HAL_PLAYER_AUDIO], volc_audio_vol); +} + +void adjust_audio_val(const char *action) +{ + if (strcmp(action, "up") == 0) + { + __volc_up_audio_val(); + } + else if (strcmp(action, "down") == 0) + { + __volc_down_audio_val(); + } +} diff --git a/firmware/echoear-platform-agent/components/application/service/inc/volc_manager_service.h b/firmware/echoear-platform-agent/components/application/service/inc/volc_manager_service.h new file mode 100644 index 0000000..bf4d2a4 --- /dev/null +++ b/firmware/echoear-platform-agent/components/application/service/inc/volc_manager_service.h @@ -0,0 +1,11 @@ +// Copyright (2025) Beijing Volcano Engine Technology Ltd. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef VOLC_SERVICE_MANAGER_H__ +#define VOLC_SERVICE_MANAGER_H__ +#include +#include "aios.h" + +void volc_service_manager_init(void); + +#endif /* VOLC_SERVICE_MANAGER_H__ */ diff --git a/firmware/echoear-platform-agent/components/application/service/inc/volc_service_common.h b/firmware/echoear-platform-agent/components/application/service/inc/volc_service_common.h new file mode 100644 index 0000000..f96ff6e --- /dev/null +++ b/firmware/echoear-platform-agent/components/application/service/inc/volc_service_common.h @@ -0,0 +1,38 @@ +// Copyright (2025) Beijing Volcano Engine Technology Ltd. +// SPDX-License-Identifier: Apache-2.0 +#ifndef VOLC_CONV_COMMON_H__ +#define VOLC_CONV_COMMON_H__ +#include "aios.h" + +enum { + VOLC_SERVICE_EVENT_TEST = Event_User, + VOLC_SERVICE_NETWORK_CONFIG, + VOLC_SERVICE_AI_CONVERSATION, + + // Ai_Conversation event: + VOLC_SERVICE_AI_CONVERSATION_START = 100, + VOLC_SERVICE_AI_CONVERSATION_INTERRUPT = 101, + VOLC_SERVICE_AI_CONVERSATION_QUIT = 102, // control the Ai_Conversation task quit + VOLC_SERVICE_AI_CONVERSATION_OVER = 103, // notice the Ai_Conversation over by task + + // function_call event: + VOLC_FUNCTION_CALL_EXEC = 200, + VOLC_FUNCTION_CALL_TRIGGER = 201, + + + // local logic event: + VOLC_LOCAL_LOGIC_PLAY_WELCOME = 300, + VOLC_LOCAL_LOGIC_PROCESS_SUBTITLE = 301, + + + VOLC_SERVICE_EVENT_MAX, +}; + +enum { + VOLC_SERVICE_PRIORITY_AI_CONVERSATION = 0, + VOLC_SERVICE_PRIORITY_FUNCTION_CALL, + VOLC_SERVICE_PRIORITY_LOCAL_LOGIC, + VOLC_SERVICE_PRIORITY_APP_MANAGER, +}; + +#endif /* VOLC_CONV_COMMON_H__ */ diff --git a/firmware/echoear-platform-agent/components/application/service/local_logic_service/inc/local_logic_service.h b/firmware/echoear-platform-agent/components/application/service/local_logic_service/inc/local_logic_service.h new file mode 100644 index 0000000..1fea9ba --- /dev/null +++ b/firmware/echoear-platform-agent/components/application/service/local_logic_service/inc/local_logic_service.h @@ -0,0 +1,9 @@ +// Copyright (2025) Beijing Volcano Engine Technology Ltd. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef VOLC_LOCAL_LOGIC_SERVICE_H__ +#define VOLC_LOCAL_LOGIC_SERVICE_H__ + +void local_logic_service_init(); + +#endif /* VOLC_LOCAL_LOGIC_SERVICE_H__ */ \ No newline at end of file diff --git a/firmware/echoear-platform-agent/components/application/service/local_logic_service/src/local_logic_service.c b/firmware/echoear-platform-agent/components/application/service/local_logic_service/src/local_logic_service.c new file mode 100644 index 0000000..2bd6e7f --- /dev/null +++ b/firmware/echoear-platform-agent/components/application/service/local_logic_service/src/local_logic_service.c @@ -0,0 +1,99 @@ +// Copyright (2025) Beijing Volcano Engine Technology Ltd. +// SPDX-License-Identifier: Apache-2.0 +#include + +#include "volc_conv_ai.h" + +#include "volc_service_common.h" +#include "aios.h" +#include "volc_conv.h" +#include "volc_hal.h" +#include "volc_hal_file.h" +#include "volc_hal_player.h" + +#if __cplusplus +extern "C" { +#endif + +static const char *welcome_audio_file_name = "/sdcard/wel.pcm"; +typedef struct volc_local_logic_service { + aios_session_t super; +} volc_local_logic_service_t; + +volc_local_logic_service_t g_volc_local_logic_service; +static int has_quit = 0; +static void __play_welcome(){ + volc_hal_context_t* g_hal_context = volc_get_global_hal_context(); + if(g_hal_context == NULL) return; + volc_hal_player_t player = g_hal_context->player_handle[VOLC_HAL_PLAYER_AUDIO]; + volc_hal_file_t* audioFile = volc_hal_file_open(welcome_audio_file_name, "rb"); + if(audioFile == NULL) return; + char audio_data[160]; + if(player){ + int offset = 0; + uint64_t size = volc_hal_file_get_size(audioFile); + while(offset < size - 160){ + volc_hal_file_read(audioFile,audio_data,160); + volc_hal_player_play_data(player, audio_data, 160); + offset += 160; + } + } + volc_hal_file_close(audioFile); + return; +} + +static int __has_substring(const char *haystack, const char *needle) { + // 处理边界:子串为空串,按惯例返回包含(可根据需求调整) + if (needle[0] == '\0') { + return 1; + } + // strstr 返回非 NULL 表示找到子串 + return strstr(haystack, needle) != NULL ? 1 : 0; +} + +static void __parser_and_process_subtitle(char *subtitle) +{ + volc_conv_service_t conv_service = get_conv_ai_service(); + + if (__has_substring(subtitle, "退下吧") || __has_substring(subtitle, "拜拜")) + { + if (!has_quit) + { + volc_send_text_to_agent(conv_service.engine, "好的拜拜", VOLC_AGENT_TYPE_TTS, 1); + has_quit = 1; + } + } + if (__has_substring(subtitle, "好的拜拜")) + { + aios_event_pub(VOLC_SERVICE_AI_CONVERSATION_QUIT, NULL, NULL); + has_quit = 0; + } +} + +static aios_ret_t __state_conversation(volc_local_logic_service_t * const me, aios_event_t const * const e) +{ + switch (e->id) { + case VOLC_LOCAL_LOGIC_PLAY_WELCOME: + __play_welcome(); + return AIOS_Ret_Handled; + case VOLC_LOCAL_LOGIC_PROCESS_SUBTITLE: + __parser_and_process_subtitle(e->data); + return AIOS_Ret_Handled; + } +} + +static aios_ret_t __state_init(volc_local_logic_service_t * const me, aios_event_t const * const e) +{ + AIOS_EVENT_SUB(VOLC_LOCAL_LOGIC_PLAY_WELCOME); // 订阅事件 + AIOS_EVENT_SUB(VOLC_LOCAL_LOGIC_PROCESS_SUBTITLE); // 订阅事件 + return AIOS_TRAN(__state_conversation); +} + +void local_logic_service_init(void){ + aios_session_init(&g_volc_local_logic_service.super, VOLC_SERVICE_PRIORITY_LOCAL_LOGIC, NULL); + aios_session_start(&g_volc_local_logic_service.super, AIOS_STATE_CAST(__state_init)); +} + +#if __cplusplus +} +#endif diff --git a/firmware/echoear-platform-agent/components/application/service/src/volc_manager_service.c b/firmware/echoear-platform-agent/components/application/service/src/volc_manager_service.c new file mode 100644 index 0000000..7d1e2a4 --- /dev/null +++ b/firmware/echoear-platform-agent/components/application/service/src/volc_manager_service.c @@ -0,0 +1,43 @@ +// Copyright (2025) Beijing Volcano Engine Technology Ltd. +// SPDX-License-Identifier: Apache-2.0 +#include "volc_hal.h" +#include "volc_manager_service.h" +#include "volc_conv_service.h" +#include "volc_service_common.h" + +typedef struct volc_service_manager { + aios_session_t super; +} volc_service_manager_t; + +volc_service_manager_t volc_service_manager; + +static aios_ret_t __state_wait_service_event(volc_service_manager_t* const me, aios_event_t const * const e) +{ + volc_hal_context_t* g_hal_context = volc_get_global_hal_context(); + if(g_hal_context == NULL){ + return AIOS_Ret_NotHandled; + } + + switch ((int)e->id) { + case VOLC_SERVICE_NETWORK_CONFIG: + return AIOS_Ret_Handled; + case VOLC_SERVICE_AI_CONVERSATION: + aios_event_pub(VOLC_SERVICE_AI_CONVERSATION_START, NULL, NULL); + return AIOS_Ret_Handled; + default: + return AIOS_Ret_NotHandled; + } +} + +static aios_ret_t __state_init(volc_service_manager_t* const me, aios_event_t const * const e) +{ + AIOS_EVENT_SUB(VOLC_SERVICE_NETWORK_CONFIG); // 订阅事件 + AIOS_EVENT_SUB(VOLC_SERVICE_AI_CONVERSATION); // 订阅事件 + return AIOS_TRAN(__state_wait_service_event); +} + +void volc_service_manager_init(void) +{ + aios_session_init(&volc_service_manager.super, VOLC_SERVICE_PRIORITY_APP_MANAGER, NULL); + aios_session_start(&volc_service_manager.super, AIOS_STATE_CAST(__state_init)); +} diff --git a/firmware/echoear-platform-agent/components/application/utils/placehold b/firmware/echoear-platform-agent/components/application/utils/placehold new file mode 100644 index 0000000..ac8522f --- /dev/null +++ b/firmware/echoear-platform-agent/components/application/utils/placehold @@ -0,0 +1 @@ +xxx \ No newline at end of file diff --git a/firmware/echoear-platform-agent/components/application/volc_config.h b/firmware/echoear-platform-agent/components/application/volc_config.h new file mode 100644 index 0000000..c60cd08 --- /dev/null +++ b/firmware/echoear-platform-agent/components/application/volc_config.h @@ -0,0 +1,8 @@ +#ifndef __VOLC_CONFIG_H__ +#define __VOLC_CONFIG_H__ + +#ifndef VOLC_LCD_DISPLAY +#define VOLC_LCD_DISPLAY 1(CONFIG_) +#endif + +#endif \ No newline at end of file diff --git a/firmware/echoear-platform-agent/components/av_processor/.gitignore b/firmware/echoear-platform-agent/components/av_processor/.gitignore new file mode 100644 index 0000000..3e19759 --- /dev/null +++ b/firmware/echoear-platform-agent/components/av_processor/.gitignore @@ -0,0 +1 @@ +**/gen_bmgr_codes/ \ No newline at end of file diff --git a/firmware/echoear-platform-agent/components/av_processor/CHANGELOG.md b/firmware/echoear-platform-agent/components/av_processor/CHANGELOG.md new file mode 100644 index 0000000..a6cc628 --- /dev/null +++ b/firmware/echoear-platform-agent/components/av_processor/CHANGELOG.md @@ -0,0 +1,7 @@ +# Changelog + +## v0.1.0 + +### Features + +- Initial version of `av_processor` \ No newline at end of file diff --git a/firmware/echoear-platform-agent/components/av_processor/CMakeLists.txt b/firmware/echoear-platform-agent/components/av_processor/CMakeLists.txt new file mode 100644 index 0000000..ce782a0 --- /dev/null +++ b/firmware/echoear-platform-agent/components/av_processor/CMakeLists.txt @@ -0,0 +1,7 @@ + +idf_component_register( + INCLUDE_DIRS "include" "./" + PRIV_INCLUDE_DIRS "private_include" + SRC_DIRS "./" + REQUIRES esp_capture esp_video_codec +) diff --git a/firmware/echoear-platform-agent/components/av_processor/Kconfig.projbuild b/firmware/echoear-platform-agent/components/av_processor/Kconfig.projbuild new file mode 100644 index 0000000..17b2c1a --- /dev/null +++ b/firmware/echoear-platform-agent/components/av_processor/Kconfig.projbuild @@ -0,0 +1,82 @@ +menu "Audio/Video Processor Configuration" + + # Media Dump Configuration + menu "Media Dump" + config MEDIA_DUMP_ENABLE + bool "Enable Media Dump" + default n + help + Enable media dump feature. When disabled, related options are hidden. + + # Audio AEC Save Point + choice MEDIA_DUMP_AUDIO_POINT + prompt "Audio Save Mode (AEC point)" + depends on MEDIA_DUMP_ENABLE + default MEDIA_DUMP_AUDIO_NONE + help + Select which audio to save for media dump with respect to AEC processing. + Only one can be selected, or select None to disable both. + + config MEDIA_DUMP_AUDIO_BEFORE_AEC + bool "Save Before AEC" + help + Save raw microphone audio before AEC processing for media dump. + + config MEDIA_DUMP_AUDIO_AFTER_AEC + bool "Save After AEC" + help + Save audio after AEC processing for media dump. + + config MEDIA_DUMP_AUDIO_NONE + bool "None" + help + Do not save audio for media dump. + endchoice + + config MEDIA_DUMP_DURATION_SEC + int "Dump Duration (seconds)" + depends on MEDIA_DUMP_ENABLE + default 20 + range 1 3600 + help + Maximum duration to capture media dump in seconds. Default 20s. + + choice MEDIA_DUMP_SINK + prompt "Media Dump Output Method" + depends on MEDIA_DUMP_ENABLE + default MEDIA_DUMP_SINK_SDCARD + help + Select where to save media dump data. + + config MEDIA_DUMP_SINK_UDP + bool "UDP" + + config MEDIA_DUMP_SINK_SDCARD + bool "SD Card File" + endchoice + + if MEDIA_DUMP_SINK_UDP + config MEDIA_DUMP_UDP_IP + string "UDP Destination IP" + default "192.168.1.100" + help + Destination IPv4 address for UDP media dump. + + config MEDIA_DUMP_UDP_PORT + int "UDP Destination Port" + default 5000 + range 1 65535 + help + Destination UDP port for media dump. + endif + + if MEDIA_DUMP_SINK_SDCARD + config MEDIA_DUMP_SDCARD_FILENAME + string "SD Card File Name" + default "/sdcard/media_dump.bin" + help + File path on SD card to save media dump data. Ensure SD is mounted. + endif + endmenu + +endmenu diff --git a/firmware/echoear-platform-agent/components/av_processor/LICENSE b/firmware/echoear-platform-agent/components/av_processor/LICENSE new file mode 100644 index 0000000..876250e --- /dev/null +++ b/firmware/echoear-platform-agent/components/av_processor/LICENSE @@ -0,0 +1,22 @@ +Espressif Modified MIT License + + Copyright (c) 2025 Espressif Systems (Shanghai) CO., LTD + + Permission is hereby granted for use EXCLUSIVELY with Espressif Systems products. + This includes the right to use, copy, modify, merge, publish, distribute, and sublicense + the Software, subject to the following conditions: + + 1. This Software MUST BE USED IN CONJUNCTION WITH ESPRESSIF SYSTEMS PRODUCTS. + 2. The above copyright notice and this permission notice shall be included in all copies + or substantial portions of the Software. + 3. Redistribution of the Software in source or binary form FOR USE WITH NON-ESPRESSIF PRODUCTS + is strictly prohibited. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE + FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + + SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT diff --git a/firmware/echoear-platform-agent/components/av_processor/README.md b/firmware/echoear-platform-agent/components/av_processor/README.md new file mode 100644 index 0000000..6c2b7ee --- /dev/null +++ b/firmware/echoear-platform-agent/components/av_processor/README.md @@ -0,0 +1,332 @@ +# AV Processor Component (Audio/Video Processing) + +This component provides a unified encapsulation for audio recording/playback/data feeding and video capture/decoding/rendering on ESP platforms, implemented based on middleware such as GMF (General Media Framework), esp-audio-render, esp-video-codec, and esp-capture. + +## Feature Overview 🎛️ + +- **Audio 🎧**: + - Recording (AEC/VAD/AFE optional) + - Playback (supports URL and local files, decoding depends on configuration) + - Data feeding (feeder, suitable for scenarios like RTC/streaming where receiving and playing happen simultaneously) + - Optional mixer with volume fade and focus control between multiple streams +- **Video 🎥**: + - Multi-channel video capture + - Video decoding and rendering callbacks + +--- + +## Quick Start + +### 1. Audio Usage + +1. Initialize audio manager: + +```c +// Method 1: Use default configuration macro (recommended) +audio_manager_config_t cfg = DEFAULT_AUDIO_MANAGER_CONFIG(); +cfg.play_dev = your_play_dev; +cfg.rec_dev = your_rec_dev; +strcpy(cfg.mic_layout, "RMNM"); // Microphone layout (optional, for AFE configuration) +cfg.board_sample_rate = 16000; +cfg.board_bits = 32; +cfg.board_channels = 2; +cfg.play_volume = 80; +cfg.rec_volume = 60; +cfg.rec_ref_volume = 60; +cfg.enable_mixer = true; // Enable when mixing feeder and playback is needed + +audio_manager_init(&cfg); +``` + +2. Recording (polling read): + +```c +// Recording event callback (optional) +void recorder_event_cb(void *event, void *ctx) { + // Handle recording events (e.g., AFE events) +} + +// Configure recorder (using default configuration macro) +audio_recorder_config_t recorder_cfg = DEFAULT_AUDIO_RECORDER_CONFIG(); +// Optional: Configure AFE (runtime configuration, takes priority over Kconfig) +recorder_cfg.afe_config.vad_enable = true; +recorder_cfg.afe_config.vad_mode = 4; +recorder_cfg.afe_config.agc_enable = true; +recorder_cfg.recorder_event_cb = recorder_event_cb; + +// Open recorder +audio_recorder_open(&recorder_cfg); +uint8_t buf[2048]; +audio_recorder_read_data(buf, sizeof(buf)); + +audio_recorder_close(); +``` + +3. Feeder playback (suitable for external stream → local playback): + +```c +// Configure feeder (using default configuration macro) +audio_feeder_config_t feeder_cfg = DEFAULT_AUDIO_FEEDER_CONFIG(); +// Optional: When using OPUS decoder, task_stack is recommended to be >= 4096 * 10 +// feeder_cfg.feeder_task_config.task_stack = 4096 * 10; + +audio_feeder_open(&feeder_cfg); +audio_feeder_run(); +// Feed external data blocks multiple times as needed +audio_feeder_feed_data(pkt, pkt_len); + +audio_feeder_stop(); +audio_feeder_close(); +``` + +4. Normal playback (URL or local): + +```c +// Configure player (using default configuration macro) +audio_playback_config_t playback_cfg = DEFAULT_AUDIO_PLAYBACK_CONFIG(); + +audio_playback_open(&playback_cfg); +audio_playback_play("http://:/audio.mp3"); + +audio_playback_pause(); +audio_playback_resume(); +audio_playback_stop(); +audio_playback_close(); +``` + +5. Focus/fade control (when mixer is enabled): + +```c +// Note: Mixer must be opened after both playback and feeder are opened +audio_processor_mixer_open(); +audio_processor_ramp_control(AUDIO_MIXER_FOCUS_FEEDER); // Focus on feeder audio +audio_processor_ramp_control(AUDIO_MIXER_FOCUS_PLAYBACK); // Focus on playback audio +audio_processor_mixer_close(); +``` + +### 2. Video Usage + +1. Render (decode) callback: + +```c +void decoded_cb(void *ctx, const uint8_t *data, size_t size) { + // Handle decoded frame data +} + +video_render_config_t rcfg = { + .decode_cfg = your_vdec_cfg, + .resolution = {.width = 640, .height = 480}, + .decode_cb = decoded_cb, +}; +video_render_handle_t r = video_render_open(&rcfg); +video_render_start(r); + +video_frame_t f = {.data = enc_frame, .size = enc_size}; +video_render_frame_feed(r, &f); + +video_render_stop(r); +video_render_close(r); +``` + +1. Capture (multi-channel): + +```c +void capture_cb(void *ctx, int index, video_frame_t *frame) { + // Handle captured video frame +} + +video_capture_config_t ccfg = {0}; +ccfg.camera_config = &your_cam_cfg; +ccfg.sink_num = 2; +ccfg.sink_cfg[0] = your_sink0_cfg; +ccfg.sink_cfg[1] = your_sink1_cfg; +ccfg.capture_frame_cb = capture_cb; + +video_capture_handle_t c = video_capture_open(&ccfg); +video_capture_start(c); +video_capture_stop(c); +video_capture_close(c); +``` + +--- + +## Media Dump 💾 + +- Enable condition ✅: Takes effect after enabling `MEDIA_DUMP_ENABLE` in `menuconfig`. +- Save methods 📤: Supports SD card and UDP outputs (choose one). + - SD card 💾: Enable `CONFIG_MEDIA_DUMP_SINK_SDCARD`, output to file `CONFIG_MEDIA_DUMP_SDCARD_FILENAME`, duration controlled by `CONFIG_MEDIA_DUMP_DURATION_SEC`. + - UDP 📡: Enable `CONFIG_MEDIA_DUMP_SINK_UDP`, send raw media data via `CONFIG_MEDIA_DUMP_UDP_IP` and `CONFIG_MEDIA_DUMP_UDP_PORT` (can use `script/udp_reciver.py` script). +- Save point 🎚️ (before/after AEC processing): Select in "Audio Save Mode (AEC point)" in `menuconfig` + - `MEDIA_DUMP_AUDIO_BEFORE_AEC` (Save Before AEC): Save raw microphone audio before AEC (for analyzing noise/feedback) + - `MEDIA_DUMP_AUDIO_AFTER_AEC` (Save After AEC): Save audio after AEC processing (for evaluating AEC effectiveness) +- Typical usage 🔍: Export raw audio/video data when reproducing issues, check saturation, feedback, and format correctness offline using tools like Audacity. + +## Configuration ⚙️ + +### Runtime Configuration (Recommended) + +AFE configuration can be set at runtime via the `afe_config` field in the `audio_recorder_config_t` structure, which takes priority over Kconfig configuration: + +```c +audio_recorder_config_t recorder_cfg = DEFAULT_AUDIO_RECORDER_CONFIG(); +// Configure AFE +recorder_cfg.afe_config.ai_mode_wakeup = false; // AI mode: true=wakeup mode, false=direct mode +recorder_cfg.afe_config.vad_enable = true; // Enable VAD +recorder_cfg.afe_config.vad_mode = 4; // VAD mode (1-4), larger values are more sensitive +recorder_cfg.afe_config.vad_min_speech_ms = 64; // Minimum speech duration (ms) +recorder_cfg.afe_config.vad_min_noise_ms = 1000; // Minimum noise duration (ms) +recorder_cfg.afe_config.agc_enable = true; // Enable AGC +recorder_cfg.afe_config.enable_vcmd_detect = false; // Enable VCMD +recorder_cfg.afe_config.vcmd_timeout_ms = 5000; // VCMD timeout (ms) +recorder_cfg.afe_config.mn_language = "cn"; // Model language: "cn" or "en" +recorder_cfg.afe_config.wakeup_time_ms = 10000; // Wakeup time (ms) +recorder_cfg.afe_config.wakeup_end_time_ms = 2000; // Wakeup end time (ms) + +// Pass configuration when opening recorder +audio_recorder_open(&recorder_cfg); +``` + +### Kconfig Configuration (Alternative) + +If fields in `audio_recorder_config_t.afe_config` use default values or are not set, Kconfig configuration will be used. Configure via `idf.py menuconfig`: + +#### 💾 Media Dump Configuration + +Configure under **"Component config" -> "Audio/Video Processor Configuration" -> "Media Dump"** in `menuconfig`: + +- **`MEDIA_DUMP_ENABLE`**: Enable media data dump functionality, disabled by default + - When enabled, raw audio/video data can be saved for debugging and analysis + +- **`MEDIA_DUMP_AUDIO_POINT`**: Audio save point selection (choice type) + - **`MEDIA_DUMP_AUDIO_BEFORE_AEC`**: Save raw audio before AEC processing + - Useful for analyzing noise and feedback issues + - **`MEDIA_DUMP_AUDIO_AFTER_AEC`**: Save audio after AEC processing + - Useful for evaluating AEC processing effectiveness + - **`MEDIA_DUMP_AUDIO_NONE`**: Do not save audio (default) + +- **`MEDIA_DUMP_DURATION_SEC`**: Save duration in seconds, default 20 + - Type: integer (int) + - Range: 1-3600 seconds + +- **`MEDIA_DUMP_SINK`**: Save method selection (choice type) + - **`MEDIA_DUMP_SINK_SDCARD`**: Save to SD card file (default) + - File path configuration: `CONFIG_MEDIA_DUMP_SDCARD_FILENAME` (default: `/sdcard/media_dump.bin`) + - Ensure SD card is properly mounted + - **`MEDIA_DUMP_SINK_UDP`**: Send via UDP + - Target IP configuration: `CONFIG_MEDIA_DUMP_UDP_IP` (default: `192.168.1.100`) + - Target port configuration: `CONFIG_MEDIA_DUMP_UDP_PORT` (default: 5000) + - Can use `script/udp_reciver.py` script to receive data + +### Task Configuration + +The audio processing component uses multiple FreeRTOS tasks internally, which can be customized via task configuration fields in each module's configuration structure: + +#### Task Configuration for Each Module + +- **Recorder** (`audio_recorder_config_t`): + - `afe_feed_task_config`: AFE feed task configuration (default stack size 3KB) + - `afe_fetch_task_config`: AFE fetch task configuration (default stack size 3KB) + - `recorder_task_config`: Recorder task configuration (default stack size 5KB, recommend >= 40KB when using OPUS encoder) + +- **Player** (`audio_playback_config_t`): + - `playback_task_config`: Playback task configuration (default stack size 4KB) + +- **Feeder** (`audio_feeder_config_t`): + - `feeder_task_config`: Feeder task configuration (default stack size 5KB, recommend >= 40KB when using OPUS decoder) + +**Important Notes**: +- When using OPUS encoder or decoder, `task_stack` of `recorder_task_config` and `feeder_task_config` should be set to at least `4096 * 10` bytes (40KB). +- When `task_stack` in task configuration is set to 0, default values will be used. +- When `task_stack_in_ext` is set to `true`, task stack will be allocated in external memory, helping to save internal RAM. +- Task configuration should be set in each module's configuration structure (e.g., `audio_recorder_config_t`, `audio_playback_config_t`, `audio_feeder_config_t`), not in `audio_manager_config_t`. + +--- + +## API Reference 📚 + +Header files: + +- `include/audio_processor.h` +- `include/video_processor.h` +- `include/av_processor_type.h` + +Common functions (selection): + +- Audio management: + - `audio_manager_init`/`audio_manager_deinit`: Initialize/deinitialize audio manager +- Recording: + - `audio_recorder_open`: Open recorder (supports encoder configuration and event callback) + - `audio_recorder_read_data`: Read recording data + - `audio_recorder_pause`/`audio_recorder_resume`: Pause/resume recording + - `audio_recorder_get_afe_manager_handle`: Get AFE manager handle + - `audio_recorder_close`: Close recorder +- Feeder: + - `audio_feeder_open`: Open feeder (supports decoder configuration) + - `audio_feeder_run`: Start feeder + - `audio_feeder_feed_data`: Feed audio data + - `audio_feeder_stop`: Stop feeder + - `audio_feeder_close`: Close feeder +- Playback: + - `audio_playback_open`/`audio_playback_close`: Open/close player + - `audio_playback_play`: Play audio (supports URL or local file, e.g., `http://...` or `file:///sdcard/...`) + - `audio_playback_stop`: Stop playback + - `audio_playback_pause`/`audio_playback_resume`: Pause/resume playback + - `audio_playback_get_state`: Get playback state +- Mixer: + - `audio_processor_mixer_open`: Open mixer (must be called after both playback and feeder are opened) + - `audio_processor_mixer_close`: Close mixer + - `audio_processor_ramp_control`: Control audio focus and volume fade +- Video rendering: + - `video_render_open`/`video_render_close`: Open/close video renderer + - `video_render_start`/`video_render_stop`: Start/stop rendering + - `video_render_frame_feed`: Feed video frame +- Video capture: + - `video_capture_open`/`video_capture_close`: Open/close video capturer + - `video_capture_start`/`video_capture_stop`: Start/stop capture + +--- + +## FAQ ❓ + +### Audio Issues + +- **"Self-questioning" phenomenon occurs (device playback sound is repeatedly captured by microphone)**: + - Enable `MEDIA_DUMP_ENABLE` in `menuconfig`, reproduce the issue and export saved audio data for further analysis + - Open the exported audio file using [Audacity](https://www.audacityteam.org/download/), observe waveform/spectrum to determine if saturation or feedback exists + - If saturation clipping occurs, reduce microphone gain (`esp_codec_dev_set_in_gain`); or appropriately reduce speaker volume (`esp_codec_dev_set_out_vol`) to avoid excessive feedback + +- **Stack overflow when using OPUS encoder/decoder**: + - Ensure `task_stack` of `recorder_task_config` and `feeder_task_config` is set to at least `4096 * 10` bytes (40KB) + - Consider allocating task stack in external memory (set `task_stack_in_ext = true`) + +- **Mixer not working properly**: + - Ensure that both player (`audio_playback_open`) and feeder (`audio_feeder_open`) are opened before calling `audio_processor_mixer_open()` + - Ensure `enable_mixer = true` is set when calling `audio_manager_init` + +### Development and Debugging + +- **Modifying component source code (espressif__av_processor)**: + - If you need to modify code within `espressif__av_processor`, you can execute the `script/move_av_processor.py` script + - The script will copy `managed_components/espressif__av_processor` to your project's `PROJECT_DIR/components/espressif__av_processor` directory + - The script will automatically delete the original directory under `managed_components` and update `idf_component.yml` + - Before using, set the environment variable: `export PROJECT_DIR=/path/to/project` + +- **Media data dump (Media Dump)**: + - UDP method: Can use `script/udp_reciver.py` script to receive UDP data + - SD card method: Ensure SD card is properly mounted and file path is accessible + +--- + +## Version Information + +Current version: v0.5.0 + +## License + +This component follows the Apache-2.0 license. See the `LICENSE` file included in the repository for details. + +## Related Resources + +- Project repository: https://github.com/espressif/esp-gmf/tree/main/components/av_processor +- Issue reporting: https://github.com/espressif/esp-gmf/issues +- Documentation: https://github.com/espressif/esp-gmf/blob/main/components/av_processor/README.md diff --git a/firmware/echoear-platform-agent/components/av_processor/README_CN.md b/firmware/echoear-platform-agent/components/av_processor/README_CN.md new file mode 100644 index 0000000..6aab937 --- /dev/null +++ b/firmware/echoear-platform-agent/components/av_processor/README_CN.md @@ -0,0 +1,346 @@ +# AV Processor 组件(音视频处理) + +本组件提供在 ESP 平台上进行音频录制/播放/喂数以及视频捕获/解码/渲染的统一封装,基于 GMF(General Media Framework)、esp-audio-render、esp-video-codec、esp-capture 等中间组件实现。 + +## 功能概览 🎛️ + +- **音频 🎧**: + - 录音(AEC/VAD/AFE 可选) + - 播放(支持 URL、本地文件,解码由配置决定) + - 数据喂入(feeder,适合边接收边播放的场景,如 RTC/流媒体) + - 可选混音器,多路流间音量渐变(fade)与焦点控制 +- **视频 🎥**: + - 多路视频采集 + - 视频解码与渲染回调 + +--- + +## 快速开始 + +### 1. 音频使用 + +1. 初始化音频管理器: + +```c +// 方式一:使用默认配置宏(推荐) +audio_manager_config_t cfg = DEFAULT_AUDIO_MANAGER_CONFIG(); +cfg.play_dev = your_play_dev; +cfg.rec_dev = your_rec_dev; +strcpy(cfg.mic_layout, "RMNM"); // 麦克风布局(可选,用于 AFE 配置) +cfg.board_sample_rate = 16000; +cfg.board_bits = 32; +cfg.board_channels = 2; +cfg.play_volume = 80; +cfg.rec_volume = 60; +cfg.rec_ref_volume = 60; +cfg.enable_mixer = true; // 需要 feeder 与 playback 混音时开启 + +audio_manager_init(&cfg); +``` + +2. 录音(可轮询读取): + +```c +// 录音事件回调(可选) +void recorder_event_cb(void *event, void *ctx) { + // 处理录音事件(如 AFE 事件) +} + +// 配置录音器(使用默认配置宏) +audio_recorder_config_t recorder_cfg = DEFAULT_AUDIO_RECORDER_CONFIG(); +// 可选:配置 AFE(运行时配置,优先级高于 Kconfig) +recorder_cfg.afe_config.vad_enable = true; +recorder_cfg.afe_config.vad_mode = 4; +recorder_cfg.afe_config.agc_enable = true; +recorder_cfg.recorder_event_cb = recorder_event_cb; + +// 打开录音器 +audio_recorder_open(&recorder_cfg); +uint8_t buf[2048]; +audio_recorder_read_data(buf, sizeof(buf)); + +audio_recorder_close(); +``` + +3. 喂数播放(feeder,适合外部码流→本地播放): + +```c +// 配置 feeder(使用默认配置宏) +audio_feeder_config_t feeder_cfg = DEFAULT_AUDIO_FEEDER_CONFIG(); +// 可选:使用 OPUS 解码器时,task_stack 建议 >= 4096 * 10 +// feeder_cfg.feeder_task_config.task_stack = 4096 * 10; + +audio_feeder_open(&feeder_cfg); +audio_feeder_run(); +// 按需多次喂入外部数据块 +audio_feeder_feed_data(pkt, pkt_len); + +audio_feeder_stop(); +audio_feeder_close(); +``` + +4. 普通播放(URL 或本地): + +```c +// 配置播放器(使用默认配置宏) +audio_playback_config_t playback_cfg = DEFAULT_AUDIO_PLAYBACK_CONFIG(); + +audio_playback_open(&playback_cfg); +audio_playback_play("http://:/audio.mp3"); + +audio_playback_pause(); +audio_playback_resume(); +audio_playback_stop(); +audio_playback_close(); +``` + +5. 提示播放 + +```c +// 配置播放器(使用默认配置宏) +audio_prompt_config_t prompt_cfg = DEFAULT_AUDIO_PROMPT_CONFIG(); + +audio_prompt_open(&prompt_cfg); +audio_prompt_play("spiffs://audio.mp3"); +audio_prompt_close(); +``` +> 提示播放 和 配置播放器是相似的,只是 `提示播放` 是阻塞式的播放,适合使用短音频的播放。目的是在使用 普通播放 的播放的时候又需要播放 +> 提示音,可以有很好的相应 + +6. 焦点/渐变控制(启用混音器时): + +```c +// 注意:使用混音器前需要先打开播放器和 feeder +audio_processor_mixer_open(); +audio_processor_ramp_control(AUDIO_MIXER_FOCUS_FEEDER); // 侧重喂数音频 +audio_processor_ramp_control(AUDIO_MIXER_FOCUS_PLAYBACK); // 侧重音频播放 +audio_processor_mixer_close(); +``` + +### 2. 视频使用 + +1. 渲染(解码)回调: + +```c +void decoded_cb(void *ctx, const uint8_t *data, size_t size) { + // 处理解码后的帧数据 +} + +video_render_config_t rcfg = { + .decode_cfg = your_vdec_cfg, + .resolution = {.width = 640, .height = 480}, + .decode_cb = decoded_cb, +}; +video_render_handle_t r = video_render_open(&rcfg); +video_render_start(r); + +video_frame_t f = {.data = enc_frame, .size = enc_size}; +video_render_frame_feed(r, &f); + +video_render_stop(r); +video_render_close(r); +``` + +1. 采集(多路): + +```c +void capture_cb(void *ctx, int index, video_frame_t *frame) { + // 处理采集到的视频帧 +} + +video_capture_config_t ccfg = {0}; +ccfg.camera_config = &your_cam_cfg; +ccfg.sink_num = 2; +ccfg.sink_cfg[0] = your_sink0_cfg; +ccfg.sink_cfg[1] = your_sink1_cfg; +ccfg.capture_frame_cb = capture_cb; + +video_capture_handle_t c = video_capture_open(&ccfg); +video_capture_start(c); +video_capture_stop(c); +video_capture_close(c); +``` + +--- + +## 媒体数据保存(Media Dump)💾 + +- 使能条件 ✅:在 `menuconfig` 勾选 `MEDIA_DUMP_ENABLE` 后生效。 +- 保存方式 📤:支持 SD 卡与 UDP 两种输出(可二选一)。 + - SD 卡 💾:开启 `CONFIG_MEDIA_DUMP_SINK_SDCARD`,输出到文件 `CONFIG_MEDIA_DUMP_SDCARD_FILENAME`,时长由 `CONFIG_MEDIA_DUMP_DURATION_SEC` 控制。 + - UDP 📡:开启 `CONFIG_MEDIA_DUMP_SINK_UDP`,通过 `CONFIG_MEDIA_DUMP_UDP_IP` 与 `CONFIG_MEDIA_DUMP_UDP_PORT` 发送原始媒体数据(可使用 `script/udp_reciver.py` 脚本)。 +- 保存点位 🎚️(AEC 处理前/后):在 `menuconfig` 的“Audio Save Mode (AEC point)”中选择 + - `MEDIA_DUMP_AUDIO_BEFORE_AEC`(Save Before AEC):保存 AEC 前的麦克风原始音频(便于分析底噪/回授) + - `MEDIA_DUMP_AUDIO_AFTER_AEC`(Save After AEC):保存 AEC 处理后的音频(便于评估 AEC 效果) +- 典型用途 🔍:问题复现时导出原始音/视频数据,离线用 Audacity 等工具检查饱和、回授、格式是否正确。 + +## 配置说明 ⚙️ + +### 运行时配置(推荐) + +AFE 配置可以通过 `audio_recorder_config_t` 结构体中的 `afe_config` 字段在运行时配置,优先级高于 Kconfig 配置: + +```c +audio_recorder_config_t recorder_cfg = DEFAULT_AUDIO_RECORDER_CONFIG(); +// 配置 AFE +recorder_cfg.afe_config.ai_mode_wakeup = false; // AI 模式:true=唤醒模式,false=直连模式 +recorder_cfg.afe_config.vad_enable = true; // 使能 VAD +recorder_cfg.afe_config.vad_mode = 4; // VAD 模式(1-4),值越大越敏感 +recorder_cfg.afe_config.vad_min_speech_ms = 64; // 最小语音时长(ms) +recorder_cfg.afe_config.vad_min_noise_ms = 1000; // 最小噪声时长(ms) +recorder_cfg.afe_config.agc_enable = true; // 使能 AGC +recorder_cfg.afe_config.enable_vcmd_detect = false; // 使能 VCMD +recorder_cfg.afe_config.vcmd_timeout_ms = 5000; // VCMD 超时(ms) +recorder_cfg.afe_config.mn_language = "cn"; // 模型语言:"cn" 或 "en" +recorder_cfg.afe_config.wakeup_time_ms = 10000; // 唤醒时间(ms) +recorder_cfg.afe_config.wakeup_end_time_ms = 2000; // 唤醒结束时间(ms) + +// 打开录音器时传入配置 +audio_recorder_open(&recorder_cfg); +``` + +### Kconfig 配置(备选) + +如果 `audio_recorder_config_t.afe_config` 中的字段使用默认值或未设置,则使用 Kconfig 配置。通过 `idf.py menuconfig` 进入配置菜单进行配置。 + + +#### 💾 Media Dump 配置 + +在 `menuconfig` 的 **"Component config" -> "Audio/Video Processor Configuration" -> "Media Dump"** 下配置: + +- **`MEDIA_DUMP_ENABLE`**:使能媒体数据保存功能,默认关闭 + - 启用后可以保存原始音频/视频数据用于调试分析 + +- **`MEDIA_DUMP_AUDIO_POINT`**:音频保存点位选择(choice 类型) + - **`MEDIA_DUMP_AUDIO_BEFORE_AEC`**:保存 AEC 处理前的原始音频 + - 便于分析底噪、回授等问题 + - **`MEDIA_DUMP_AUDIO_AFTER_AEC`**:保存 AEC 处理后的音频 + - 便于评估 AEC 处理效果 + - **`MEDIA_DUMP_AUDIO_NONE`**:不保存音频(默认) + +- **`MEDIA_DUMP_DURATION_SEC`**:保存时长(秒),默认 20 + - 类型:整数(int) + - 范围:1-3600 秒 + +- **`MEDIA_DUMP_SINK`**:保存方式选择(choice 类型) + - **`MEDIA_DUMP_SINK_SDCARD`**:保存到 SD 卡文件(默认) + - 文件路径配置项:`CONFIG_MEDIA_DUMP_SDCARD_FILENAME`(默认:`/sdcard/media_dump.bin`) + - 需要确保 SD 卡已正确挂载 + - **`MEDIA_DUMP_SINK_UDP`**:通过 UDP 发送 + - 目标 IP 配置项:`CONFIG_MEDIA_DUMP_UDP_IP`(默认:`192.168.1.100`) + - 目标端口配置项:`CONFIG_MEDIA_DUMP_UDP_PORT`(默认:5000) + - 可使用 `script/udp_reciver.py` 脚本接收数据 + +### 任务配置说明 + +音频处理组件内部使用多个 FreeRTOS 任务,可以通过各功能模块的配置结构体中的任务配置字段进行自定义: + +#### 各功能模块任务配置 + +- **录音器**(`audio_recorder_config_t`): + - `afe_feed_task_config`:AFE feed 任务配置(默认栈大小 3KB) + - `afe_fetch_task_config`:AFE fetch 任务配置(默认栈大小 3KB) + - `recorder_task_config`:录音任务配置(默认栈大小 5KB,使用 OPUS 编码器时建议 >= 40KB) + +- **播放器**(`audio_playback_config_t`): + - `playback_task_config`:播放任务配置(默认栈大小 4KB) + +- **Feeder**(`audio_feeder_config_t`): + - `feeder_task_config`:Feeder 任务配置(默认栈大小 5KB,使用 OPUS 解码器时建议 >= 40KB) + +**重要提示**: +- 当使用 OPUS 编码器或解码器时,`recorder_task_config` 和 `feeder_task_config` 的 `task_stack` 需要设置为至少 `4096 * 10` 字节(40KB)。 +- 任务配置中的 `task_stack` 设置为 0 时,将使用默认值。 +- `task_stack_in_ext` 设置为 `true` 时,任务栈将分配在外部内存中,有助于节省内部 RAM。 +- 任务配置应在各自模块的配置结构体中设置(如 `audio_recorder_config_t`、`audio_playback_config_t`、`audio_feeder_config_t`),而不是在 `audio_manager_config_t` 中。 + +--- + +## API 参考 📚 + +头文件: + +- `include/audio_processor.h` +- `include/video_processor.h` +- `include/av_processor_type.h` + +常用函数(节选): + +- 音频管理: + - `audio_manager_init`/`audio_manager_deinit`:初始化/反初始化音频管理器 +- 录音: + - `audio_recorder_open`:打开录音器(支持编码器配置和事件回调) + - `audio_recorder_read_data`:读取录音数据 + - `audio_recorder_pause`/`audio_recorder_resume`:暂停/恢复录音 + - `audio_recorder_get_afe_manager_handle`:获取 AFE manager 句柄 + - `audio_recorder_close`:关闭录音器 +- 喂数: + - `audio_feeder_open`:打开 feeder(支持解码器配置) + - `audio_feeder_run`:启动 feeder + - `audio_feeder_feed_data`:喂入音频数据 + - `audio_feeder_stop`:停止 feeder + - `audio_feeder_close`:关闭 feeder +- 播放: + - `audio_playback_open`/`audio_playback_close`:打开/关闭播放器 + - `audio_playback_play`:播放音频(支持 URL 或本地文件,如 `http://...` 或 `file:///sdcard/...`) + - `audio_playback_stop`:停止播放 + - `audio_playback_pause`/`audio_playback_resume`:暂停/恢复播放 + - `audio_playback_get_state`:获取播放状态 +- 混音器: + - `audio_processor_mixer_open`:打开混音器(必须在 playback 和 feeder 都打开后调用) + - `audio_processor_mixer_close`:关闭混音器 + - `audio_processor_ramp_control`:控制音频焦点和音量渐变 +- 视频渲染: + - `video_render_open`/`video_render_close`:打开/关闭视频渲染器 + - `video_render_start`/`video_render_stop`:启动/停止渲染 + - `video_render_frame_feed`:喂入视频帧 +- 视频采集: + - `video_capture_open`/`video_capture_close`:打开/关闭视频采集器 + - `video_capture_start`/`video_capture_stop`:启动/停止采集 + +--- + +## 常见问题 ❓ + +### 音频问题 + +- **出现"自问自答"现象(设备播放的声音被麦克风重复收录)**: + - 在 `menuconfig` 使能 `MEDIA_DUMP_ENABLE`,复现问题并导出保存的音频数据以便进一步分析 + - 使用 [Audacity](https://www.audacityteam.org/download/) 打开导出的音频文件,观察波形/频谱判断是否存在饱和或回授 + - 如出现饱和剪切,调低麦克风增益(`esp_codec_dev_set_in_gain`);或适当减小扬声器音量(`esp_codec_dev_set_out_vol`),避免过度回授 + +- **OPUS 编解码器使用时出现栈溢出**: + - 确保 `recorder_task_config` 和 `feeder_task_config` 的 `task_stack` 设置为至少 `4096 * 10` 字节(40KB) + - 考虑将任务栈分配在外部内存(设置 `task_stack_in_ext = true`) + +- **混音器无法正常工作**: + - 确保在调用 `audio_processor_mixer_open()` 之前,已经打开了播放器(`audio_playback_open`)和 feeder(`audio_feeder_open`) + - 确保在 `audio_manager_init` 时设置了 `enable_mixer = true` + +### 开发调试 + +- **修改组件源码(espressif__av_processor)**: + - 如需修改 `espressif__av_processor` 内的代码,可执行 `script/move_av_processor.py` 脚本 + - 脚本会将 `managed_components/espressif__av_processor` 复制到你的工程 `PROJECT_DIR/components/espressif__av_processor` 下 + - 脚本会自动删除 `managed_components` 下的原目录,并更新 `idf_component.yml` + - 使用前需要设置环境变量:`export PROJECT_DIR=/path/to/project` + +- **媒体数据保存(Media Dump)**: + - UDP 方式:可使用 `script/udp_reciver.py` 脚本接收 UDP 数据 + - SD 卡方式:确保 SD 卡已正确挂载,文件路径可访问 + +--- + +## 版本信息 + +当前版本:v0.5.0 + +## 许可证 + +本组件遵循 Apache-2.0 许可证,详见仓库随附的 `LICENSE`。 + +## 相关资源 + +- 项目仓库:https://github.com/espressif/esp-gmf/tree/main/components/av_processor +- 问题反馈:https://github.com/espressif/esp-gmf/issues +- 文档:https://github.com/espressif/esp-gmf/blob/main/components/av_processor/README.md diff --git a/firmware/echoear-platform-agent/components/av_processor/audio_processor.c b/firmware/echoear-platform-agent/components/av_processor/audio_processor.c new file mode 100644 index 0000000..0884053 --- /dev/null +++ b/firmware/echoear-platform-agent/components/av_processor/audio_processor.c @@ -0,0 +1,1574 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include "esp_err.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_log.h" + +#include "esp_gmf_element.h" +#include "esp_gmf_pool.h" +#include "esp_gmf_pipeline.h" +#include "esp_gmf_audio_helper.h" +#include "esp_gmf_audio_enc.h" +#include "esp_fourcc.h" +#include "esp_audio_enc_default.h" +#include "esp_gmf_audio_dec.h" +#include "esp_gmf_rate_cvt.h" +#include "esp_gmf_bit_cvt.h" +#include "esp_gmf_ch_cvt.h" +#include "esp_audio_types.h" +#include "esp_audio_dec_default.h" +#include "esp_audio_simple_dec_default.h" +#include "av_pipeline_pool.h" +#include "esp_gmf_fifo.h" +#include "esp_codec_dev.h" +#include "esp_gmf_new_databus.h" + +#include "esp_vad.h" +#include "esp_afe_config.h" +#include "esp_gmf_afe.h" + +#include "esp_audio_render.h" +#include "esp_gmf_io_codec_dev.h" +#include "gmf_loader_setup_defaults.h" +#include "audio_processor.h" + +#if CONFIG_MEDIA_DUMP_ENABLE +#include "media_dump.h" +#endif /* CONFIG_MEDIA_DUMP_ENABLE */ + +#define AUDIO_OPUS_PIP_TASK_STACK_SIZE (4096 * 10) +#define AUDIO_BUFFER_SIZE (2048) +#define AUDIO_RINGBUFFER_SIZE (8192) +#define AUDIO_FIFO_NUM (5) + +static char *TAG = "AUDIO_PROCESSOR"; + +enum audio_render_stream_type_e { + AUDIO_RENDER_STREAM_FEEDER = 0, + AUDIO_RENDER_STREAM_PLAYBACK = 1, + AUDIO_RENDER_STREAM_TYPE_MAX = 2, +}; + +typedef struct { + esp_asp_handle_t player; + enum audio_player_state_e state; + esp_gmf_pool_handle_t pool; + esp_asp_event_func event_cb; + void *ctx; + audio_playback_config_t config; +} audio_playback_t; + +typedef struct { + esp_asp_handle_t player; + enum audio_player_state_e state; + esp_gmf_pool_handle_t pool; + audio_prompt_config_t config; +} audio_prompt_t; + +typedef struct { + esp_gmf_fifo_handle_t fifo; + recorder_event_callback_t cb; + void *ctx; + enum audio_player_state_e state; + esp_gmf_task_handle_t task; + esp_gmf_pipeline_handle_t pipe; + esp_gmf_afe_manager_handle_t afe_manager; + esp_gmf_pool_handle_t pool; + afe_config_t *afe_cfg; + audio_recorder_config_t config; +} audio_recorder_t; + +typedef struct { + esp_gmf_pipeline_handle_t pipe; + esp_gmf_db_handle_t fifo; + esp_gmf_task_handle_t task; + esp_gmf_pool_handle_t pool; + audio_feeder_config_t config; + enum audio_player_state_e state; +} audio_feeder_t; + +typedef struct { + esp_gmf_pool_handle_t pool; + audio_manager_config_t config; + esp_audio_render_handle_t render_handle; + esp_audio_render_stream_handle_t stream[AUDIO_RENDER_STREAM_TYPE_MAX]; + bool enable_mixer; +} audio_manager_t; + +static audio_manager_t audio_manager; +static audio_recorder_t audio_recorder; +static audio_feeder_t audio_feeder; +static audio_playback_t audio_playback; +static audio_prompt_t audio_prompt; + +static inline esp_opus_enc_frame_duration_t convert_opus_dec_frame_duration(int frame_duration) +{ + switch (frame_duration) { + case 2: + return ESP_OPUS_DEC_FRAME_DURATION_2_5_MS; + case 5: + return ESP_OPUS_DEC_FRAME_DURATION_5_MS; + case 10: + return ESP_OPUS_DEC_FRAME_DURATION_10_MS; + case 20: + return ESP_OPUS_DEC_FRAME_DURATION_20_MS; + case 40: + return ESP_OPUS_DEC_FRAME_DURATION_40_MS; + case 60: + return ESP_OPUS_DEC_FRAME_DURATION_60_MS; + case 80: + return ESP_OPUS_DEC_FRAME_DURATION_80_MS; + case 100: + return ESP_OPUS_DEC_FRAME_DURATION_100_MS; + case 120: + return ESP_OPUS_DEC_FRAME_DURATION_120_MS; + default: + return ESP_OPUS_DEC_FRAME_DURATION_INVALID; + } +} + +static inline esp_opus_enc_frame_duration_t convert_opus_enc_frame_duration(int frame_duration) +{ + switch (frame_duration) { + case 2: + return ESP_OPUS_ENC_FRAME_DURATION_2_5_MS; + case 5: + return ESP_OPUS_ENC_FRAME_DURATION_5_MS; + case 10: + return ESP_OPUS_ENC_FRAME_DURATION_10_MS; + case 20: + return ESP_OPUS_ENC_FRAME_DURATION_20_MS; + case 40: + return ESP_OPUS_ENC_FRAME_DURATION_40_MS; + case 60: + return ESP_OPUS_ENC_FRAME_DURATION_60_MS; + case 80: + return ESP_OPUS_ENC_FRAME_DURATION_80_MS; + case 100: + return ESP_OPUS_ENC_FRAME_DURATION_100_MS; + case 120: + return ESP_OPUS_ENC_FRAME_DURATION_120_MS; + default: + return -1; + } +} + +static int audio_render_write_hdlr(uint8_t *pcm_data, uint32_t len, void *ctx) +{ + ESP_LOGD(TAG, "Audio render write handler, data size: %ld", len); + esp_codec_dev_write(audio_manager.config.play_dev, pcm_data, len); + return ESP_OK; +} + +static int playback_out_data_callback(uint8_t *data, int data_size, void *ctx) +{ + if (audio_manager.enable_mixer) { + esp_audio_render_stream_write(audio_manager.stream[AUDIO_RENDER_STREAM_PLAYBACK], data, data_size); + } else { + esp_codec_dev_write(audio_manager.config.play_dev, data, data_size); + } + return ESP_OK; +} + +static int prompt_out_data_callback(uint8_t *data, int data_size, void *ctx) +{ + esp_codec_dev_write(audio_manager.config.play_dev, data, data_size); + return ESP_OK; +} + +static int playback_event_callback(esp_asp_event_pkt_t *event, void *ctx) +{ + if (audio_playback.event_cb) { + audio_playback.event_cb(event, audio_playback.ctx); + } + if (event->type == ESP_ASP_EVENT_TYPE_MUSIC_INFO) { + esp_asp_music_info_t info = {0}; + memcpy(&info, event->payload, event->payload_size); + ESP_LOGI(TAG, "Get info, rate:%d, channels:%d, bits:%d", info.sample_rate, info.channels, info.bits); + } else if (event->type == ESP_ASP_EVENT_TYPE_STATE) { + esp_asp_state_t st = 0; + memcpy(&st, event->payload, event->payload_size); + ESP_LOGI(TAG, "Get State, %d,%s", st, esp_audio_simple_player_state_to_str(st)); + if (((st == ESP_ASP_STATE_STOPPED) || (st == ESP_ASP_STATE_FINISHED) || (st == ESP_ASP_STATE_ERROR))) { + audio_playback.state = AUDIO_RUN_STATE_IDLE; + } + } + return ESP_OK; +} + +static int prompt_event_callback(esp_asp_event_pkt_t *event, void *ctx) +{ + if (event->type == ESP_ASP_EVENT_TYPE_MUSIC_INFO) { + esp_asp_music_info_t info = {0}; + memcpy(&info, event->payload, event->payload_size); + ESP_LOGI(TAG, "Get info, rate:%d, channels:%d, bits:%d", info.sample_rate, info.channels, info.bits); + } else if (event->type == ESP_ASP_EVENT_TYPE_STATE) { + esp_asp_state_t st = 0; + memcpy(&st, event->payload, event->payload_size); + ESP_LOGI(TAG, "prompt_event_callback: Get State, %d,%s", st, esp_audio_simple_player_state_to_str(st)); + if (((st == ESP_ASP_STATE_STOPPED) || (st == ESP_ASP_STATE_FINISHED) || (st == ESP_ASP_STATE_ERROR))) { + audio_prompt.state = AUDIO_RUN_STATE_IDLE; + } + } + return ESP_OK; +} + +static esp_err_t recorder_pipeline_event(esp_gmf_event_pkt_t *event, void *ctx) +{ + ESP_LOGD(TAG, "CB: RECV Pipeline EVT: el:%s-%p, type:%d, sub:%s, payload:%p, size:%d,%p", + OBJ_GET_TAG(event->from), event->from, event->type, esp_gmf_event_get_state_str(event->sub), + event->payload, event->payload_size, ctx); + return ESP_OK; +} + +static int recorder_outport_acquire_write(void *handle, esp_gmf_data_bus_block_t *blk, int wanted_size, int block_ticks) +{ + return esp_gmf_fifo_acquire_write(audio_recorder.fifo, blk, wanted_size, block_ticks); +} + +static int recorder_outport_release_write(void *handle, esp_gmf_data_bus_block_t *blk, int block_ticks) +{ + return esp_gmf_fifo_release_write(audio_recorder.fifo, blk, block_ticks); +} + +static int recorder_inport_acquire_read(void *handle, esp_gmf_data_bus_block_t *blk, int wanted_size, int block_ticks) +{ + int read_ret = esp_codec_dev_read(audio_manager.config.rec_dev, blk->buf, wanted_size); + if (read_ret != ESP_CODEC_DEV_OK) { + ESP_LOGE(TAG, "Microphone read failed: %d", read_ret); + blk->valid_size = 0; + return ESP_GMF_IO_FAIL; + } + blk->valid_size = wanted_size; + // Report only aggregate levels, never raw audio. Four packed 16-bit TDM lanes. + static TickType_t last_level_log; + TickType_t now = xTaskGetTickCount(); + if (audio_manager.config.board_bits == 32 && + now - last_level_log >= pdMS_TO_TICKS(2000)) { + last_level_log = now; + const int16_t *samples = (const int16_t *)blk->buf; + unsigned peak[4] = {0}; + unsigned sum[4] = {0}; + unsigned frames = wanted_size / (4 * sizeof(int16_t)); + for (unsigned frame = 0; frame < frames; frame++) { + for (unsigned channel = 0; channel < 4; channel++) { + int value = samples[frame * 4 + channel]; + unsigned magnitude = value < 0 ? -value : value; + sum[channel] += magnitude; + if (magnitude > peak[channel]) peak[channel] = magnitude; + } + } + if (frames) ESP_LOGI(TAG, "MIC_LEVEL mean=%u,%u,%u,%u peak=%u,%u,%u,%u", + sum[0]/frames, sum[1]/frames, sum[2]/frames, sum[3]/frames, + peak[0], peak[1], peak[2], peak[3]); + } +#if CONFIG_MEDIA_DUMP_ENABLE && CONFIG_MEDIA_DUMP_AUDIO_BEFORE_AEC + media_dump_feed(NULL, (uint8_t *)blk->buf, blk->valid_size); +#endif /* CONFIG_MEDIA_DUMP_ENABLE && CONFIG_MEDIA_DUMP_AUDIO_BEFORE_AEC */ + return ESP_GMF_IO_OK; +} + +static int recorder_inport_release_read(void *handle, esp_gmf_data_bus_block_t *blk, int block_ticks) +{ + return ESP_GMF_IO_OK; +} + +static void esp_gmf_afe_event_cb(esp_gmf_obj_handle_t obj, esp_gmf_afe_evt_t *event, void *user_data) +{ + if (audio_recorder.cb) { + audio_recorder.cb((void *)event, audio_recorder.ctx); + } + av_processor_afe_config_t *cfg = &audio_recorder.config.afe_config; + bool handle_vcmd = cfg && cfg->ai_mode_wakeup && cfg->enable_vcmd_detect; + + switch (event->type) { + case ESP_GMF_AFE_EVT_WAKEUP_START: { + if (handle_vcmd) { + esp_gmf_afe_vcmd_detection_cancel(obj); + esp_gmf_afe_vcmd_detection_begin(obj); + } + esp_gmf_afe_wakeup_info_t *info = event->event_data; + ESP_LOGI(TAG, "WAKEUP_START [%d : %d]", info->wake_word_index, info->wakenet_model_index); + break; + } + case ESP_GMF_AFE_EVT_WAKEUP_END: { + if (handle_vcmd) { + esp_gmf_afe_vcmd_detection_cancel(obj); + } + ESP_LOGD(TAG, "WAKEUP_END"); + break; + } + case ESP_GMF_AFE_EVT_VAD_START: { + if (handle_vcmd) { + esp_gmf_afe_vcmd_detection_cancel(obj); + esp_gmf_afe_vcmd_detection_begin(obj); + } + ESP_LOGD(TAG, "VAD_START"); + break; + } + case ESP_GMF_AFE_EVT_VAD_END: { + if (handle_vcmd) { + esp_gmf_afe_vcmd_detection_cancel(obj); + } + ESP_LOGD(TAG, "VAD_END"); + break; + } + case ESP_GMF_AFE_EVT_VCMD_DECT_TIMEOUT: { + ESP_LOGD(TAG, "VCMD_DECT_TIMEOUT"); + break; + } + default: { + esp_gmf_afe_vcmd_info_t *info = event->event_data; + ESP_LOGW(TAG, "Command %d, phrase_id %d, prob %f, str: %s", + event->type, info->phrase_id, info->prob, info->str); + break; + } + } +} + +static esp_err_t recorder_setup_afe_config(void) +{ + av_processor_afe_config_t *cfg = &audio_recorder.config.afe_config; + + srmodel_list_t *models = esp_srmodel_init(cfg && cfg->model ? cfg->model : "model"); + if (!models) { + if (cfg && cfg->ai_mode_wakeup) { + ESP_LOGE(TAG, "Failed to initialize SR model. When ai_mode_wakeup is enabled, model is must be set"); + return ESP_FAIL; + } + ESP_LOGW(TAG, "No voice wakeup model found"); + } + audio_recorder.afe_cfg = afe_config_init(audio_manager.config.mic_layout, models, + cfg ? cfg->afe_type : AFE_TYPE_SR, AFE_MODE_LOW_COST); + if (!audio_recorder.afe_cfg) { + ESP_LOGE(TAG, "Failed to initialize AFE config"); + return ESP_FAIL; + } + audio_recorder.afe_cfg->aec_init = true; + audio_recorder.afe_cfg->vad_init = cfg ? cfg->vad_enable : true; + if (audio_recorder.afe_cfg->vad_init) { + audio_recorder.afe_cfg->vad_mode = cfg ? cfg->vad_mode : 4; + audio_recorder.afe_cfg->vad_min_speech_ms = cfg ? cfg->vad_min_speech_ms : 64; + audio_recorder.afe_cfg->vad_min_noise_ms = cfg ? cfg->vad_min_noise_ms : 1000; + } + audio_recorder.afe_cfg->agc_init = cfg ? cfg->agc_enable : true; + audio_recorder.afe_cfg->wakenet_init = cfg ? cfg->ai_mode_wakeup : false; + if (audio_recorder.afe_cfg->wakenet_init) { + audio_recorder.afe_cfg->wakenet_mode = DET_MODE_95; + audio_recorder.afe_cfg->afe_linear_gain = 2.0f; + } + + esp_gmf_afe_manager_cfg_t afe_manager_cfg = DEFAULT_GMF_AFE_MANAGER_CFG(audio_recorder.afe_cfg, NULL, NULL, NULL, NULL); + if (audio_recorder.config.afe_feed_task_config.task_stack > 0) { + afe_manager_cfg.feed_task_setting.stack_size = audio_recorder.config.afe_feed_task_config.task_stack; + } + if (audio_recorder.config.afe_feed_task_config.task_prio > 0) { + afe_manager_cfg.feed_task_setting.prio = audio_recorder.config.afe_feed_task_config.task_prio; + } + if (audio_recorder.config.afe_feed_task_config.task_core >= 0) { + afe_manager_cfg.feed_task_setting.core = audio_recorder.config.afe_feed_task_config.task_core; + } + if (audio_recorder.config.afe_fetch_task_config.task_stack > 0) { + afe_manager_cfg.fetch_task_setting.stack_size = audio_recorder.config.afe_fetch_task_config.task_stack; + } + if (audio_recorder.config.afe_fetch_task_config.task_prio > 0) { + afe_manager_cfg.fetch_task_setting.prio = audio_recorder.config.afe_fetch_task_config.task_prio; + } + if (audio_recorder.config.afe_fetch_task_config.task_core >= 0) { + afe_manager_cfg.fetch_task_setting.core = audio_recorder.config.afe_fetch_task_config.task_core; + } + esp_err_t ret = esp_gmf_afe_manager_create(&afe_manager_cfg, &audio_recorder.afe_manager); + if (ret != ESP_OK) { + afe_config_free(audio_recorder.afe_cfg); + ESP_LOGE(TAG, "Failed to create AFE manager"); + return ESP_FAIL; + } + esp_gmf_element_handle_t gmf_afe = NULL; + esp_gmf_afe_cfg_t gmf_afe_cfg = DEFAULT_GMF_AFE_CFG(audio_recorder.afe_manager, esp_gmf_afe_event_cb, NULL, models); + gmf_afe_cfg.vcmd_detect_en = cfg ? cfg->enable_vcmd_detect : false; + if (gmf_afe_cfg.vcmd_detect_en) { + gmf_afe_cfg.vcmd_timeout = cfg ? cfg->vcmd_timeout_ms : 5000; + } + if (cfg && cfg->ai_mode_wakeup) { + gmf_afe_cfg.mn_language = cfg->mn_language ? cfg->mn_language : "cn"; + gmf_afe_cfg.wakeup_end = cfg->wakeup_end_time_ms; + gmf_afe_cfg.wakeup_time = cfg->wakeup_time_ms; + } else { + gmf_afe_cfg.wakeup_end = 0; + gmf_afe_cfg.wakeup_time = 0; + } + + ret = esp_gmf_afe_init(&gmf_afe_cfg, &gmf_afe); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, {goto _quit;}, "Failed to initialize AFE element"); + + afe_config_free(audio_recorder.afe_cfg); + audio_recorder.afe_cfg = NULL; + + esp_gmf_pool_register_element(audio_recorder.pool, gmf_afe, NULL); + return ESP_OK; + +_quit: + if (audio_recorder.afe_cfg) { + afe_config_free(audio_recorder.afe_cfg); + audio_recorder.afe_cfg = NULL; + } + if (audio_recorder.afe_manager) { + esp_gmf_afe_manager_destroy(audio_recorder.afe_manager); + audio_recorder.afe_manager = NULL; + } + return ret; +} + +static void recorder_get_pipeline_config(const av_processor_encoder_config_t *encoder_cfg, + const char ***elements, int *element_count, + const char **input_name, const char **output_name) +{ + switch (encoder_cfg->format) { + case AV_PROCESSOR_FORMAT_ID_G711A: + case AV_PROCESSOR_FORMAT_ID_G711U: + static const char *pipeline_g711[] = {"ai_afe", "aud_rate_cvt", "aud_enc"}; + *elements = pipeline_g711; + *element_count = sizeof(pipeline_g711) / sizeof(pipeline_g711[0]); + break; + case AV_PROCESSOR_FORMAT_ID_OPUS: + static const char *pipeline_opus[] = {"ai_afe", "aud_enc"}; + *elements = pipeline_opus; + *element_count = sizeof(pipeline_opus) / sizeof(pipeline_opus[0]); + break; + case AV_PROCESSOR_FORMAT_ID_PCM: + default: + static const char *pipeline_pcm[] = {"ai_afe", "aud_rate_cvt", "aud_ch_cvt", "aud_bit_cvt", "aud_enc"}; + *elements = pipeline_pcm; + *element_count = sizeof(pipeline_pcm) / sizeof(pipeline_pcm[0]); + break; + } + + *input_name = (*elements)[0]; + *output_name = (*elements)[*element_count - 1]; +} + +static esp_err_t recorder_setup_pipeline_ports(const char *input_name, const char *output_name) +{ + esp_gmf_port_handle_t outport = NEW_ESP_GMF_PORT_OUT_BLOCK( + recorder_outport_acquire_write, + recorder_outport_release_write, + NULL, NULL, AUDIO_BUFFER_SIZE, portMAX_DELAY); + + esp_err_t ret = esp_gmf_pipeline_reg_el_port(audio_recorder.pipe, output_name, ESP_GMF_IO_DIR_WRITER, outport); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ESP_FAIL;}, "Failed to register output port"); + + esp_gmf_port_handle_t inport = NEW_ESP_GMF_PORT_IN_BYTE( + recorder_inport_acquire_read, + recorder_inport_release_read, + NULL, NULL, AUDIO_BUFFER_SIZE, portMAX_DELAY); + + ret = esp_gmf_pipeline_reg_el_port(audio_recorder.pipe, input_name, ESP_GMF_IO_DIR_READER, inport); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ESP_FAIL;}, "Failed to register input port"); + + return ESP_OK; +} + +static esp_err_t recorder_setup_task_and_run(void) +{ + esp_gmf_task_cfg_t cfg = DEFAULT_ESP_GMF_TASK_CONFIG(); + if (audio_recorder.config.recorder_task_config.task_stack > 0) { + cfg.thread.stack = audio_recorder.config.recorder_task_config.task_stack; + if (audio_recorder.config.encoder_cfg.format == AV_PROCESSOR_FORMAT_ID_OPUS && cfg.thread.stack < (4096 * 10)) { + ESP_LOGW(TAG, "Recorder OPUS encoder requires larger stack size, using default 40k stack"); + cfg.thread.stack = AUDIO_OPUS_PIP_TASK_STACK_SIZE; + } + } else { + cfg.thread.stack = AUDIO_OPUS_PIP_TASK_STACK_SIZE; + } + cfg.thread.prio = audio_recorder.config.recorder_task_config.task_prio; + cfg.thread.core = audio_recorder.config.recorder_task_config.task_core; + cfg.thread.stack_in_ext = audio_recorder.config.recorder_task_config.task_stack_in_ext; + cfg.name = "audio_recorder_task"; + + esp_err_t ret = esp_gmf_task_init(&cfg, &audio_recorder.task); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to create recorder task"); + return ESP_FAIL; + } + esp_gmf_pipeline_bind_task(audio_recorder.pipe, audio_recorder.task); + esp_gmf_pipeline_loading_jobs(audio_recorder.pipe); + esp_gmf_pipeline_set_event(audio_recorder.pipe, recorder_pipeline_event, NULL); + esp_gmf_task_set_timeout(audio_recorder.task, 8000); + ret = esp_gmf_pipeline_run(audio_recorder.pipe); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ESP_FAIL;}, "Failed to run recorder pipeline"); + + return ESP_OK; +} + +static void recorder_cleanup_on_error(void) +{ + if (audio_recorder.pipe) { + esp_gmf_pipeline_stop(audio_recorder.pipe); + esp_gmf_pipeline_destroy(audio_recorder.pipe); + audio_recorder.pipe = NULL; + } + if (audio_recorder.task) { + esp_gmf_task_deinit(audio_recorder.task); + audio_recorder.task = NULL; + } + if (audio_recorder.fifo) { + esp_gmf_fifo_destroy(audio_recorder.fifo); + audio_recorder.fifo = NULL; + } + if (audio_recorder.afe_cfg) { + afe_config_free(audio_recorder.afe_cfg); + audio_recorder.afe_cfg = NULL; + } + if (audio_recorder.afe_manager) { + ESP_LOGW(TAG, "AFE manager destroy"); + esp_gmf_afe_manager_destroy(audio_recorder.afe_manager); + audio_recorder.afe_manager = NULL; + } +} + +static esp_err_t recorder_configure_all_encoders(void) +{ + if (!audio_recorder.pipe) { + ESP_LOGE(TAG, "Pipeline not initialized for encoder configuration"); + return ESP_ERR_INVALID_ARG; + } + esp_err_t ret = ESP_OK; + const av_processor_encoder_config_t *encoder_cfg = &audio_recorder.config.encoder_cfg; + esp_gmf_element_handle_t enc_el = NULL; + esp_gmf_pipeline_get_el_by_name(audio_recorder.pipe, "aud_enc", &enc_el); + if (!enc_el) { + ESP_LOGE(TAG, "Failed to get encoder element"); + return ESP_FAIL; + } + esp_gmf_info_sound_t info = {0}; + switch (encoder_cfg->format) { + case AV_PROCESSOR_FORMAT_ID_G711A: + case AV_PROCESSOR_FORMAT_ID_G711U: + ESP_LOGI(TAG, "Configuring G711 encoder"); + esp_gmf_obj_handle_t rate_cvt = NULL; + esp_gmf_pipeline_get_el_by_name(audio_recorder.pipe, "aud_rate_cvt", &rate_cvt); + if (rate_cvt) { + ret = esp_gmf_rate_cvt_set_dest_rate(rate_cvt, 8000); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ESP_FAIL;}, "Failed to set G711 rate converter destination rate"); + } + esp_gmf_info_sound_t req_info = { + .sample_rates = 16000, + .channels = 1, + .bits = 16, + }; + ret = esp_gmf_pipeline_report_info(audio_recorder.pipe, ESP_GMF_INFO_SOUND, &req_info, sizeof(req_info)); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ESP_FAIL;}, "Failed to report sound info for G711 encoder"); + info.sample_rates = 8000; + info.channels = 1; + info.bits = 16; + if (encoder_cfg->format == AV_PROCESSOR_FORMAT_ID_G711A) { + info.format_id = ESP_AUDIO_TYPE_G711A; + } else { + info.format_id = ESP_AUDIO_TYPE_G711U; + } + ret = esp_gmf_audio_enc_reconfig_by_sound_info(enc_el, &info); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ESP_FAIL;}, "Failed to configure G711 encoder"); + break; + + case AV_PROCESSOR_FORMAT_ID_OPUS: + ESP_LOGI(TAG, "Configuring OPUS encoder"); + esp_opus_enc_config_t opus_enc_cfg = ESP_OPUS_ENC_CONFIG_DEFAULT(); + opus_enc_cfg.channel = encoder_cfg->params.opus.audio_info.channels; + opus_enc_cfg.sample_rate = encoder_cfg->params.opus.audio_info.sample_rate; + opus_enc_cfg.frame_duration = convert_opus_enc_frame_duration(encoder_cfg->params.opus.audio_info.frame_duration); + opus_enc_cfg.enable_vbr = encoder_cfg->params.opus.enable_vbr; + opus_enc_cfg.bitrate = encoder_cfg->params.opus.bitrate; + esp_audio_enc_config_t opus_cfg = { + .type = ESP_AUDIO_TYPE_OPUS, + .cfg_sz = sizeof(esp_opus_enc_config_t), + .cfg = &opus_enc_cfg, + }; + ret = esp_gmf_audio_enc_reconfig(enc_el, &opus_cfg); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ESP_FAIL;}, "Failed to configure OPUS encoder"); + esp_gmf_obj_handle_t rate_cvt_opus = NULL; + ret = esp_gmf_pipeline_get_el_by_name(audio_recorder.pipe, "aud_rate_cvt", &rate_cvt_opus); + if (ret == ESP_OK && rate_cvt_opus) { + ret = esp_gmf_rate_cvt_set_dest_rate(rate_cvt_opus, 16000); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ESP_FAIL;}, "Failed to set OPUS rate converter destination rate"); + } + info.sample_rates = 16000; + info.channels = 1; + info.bits = 16; + info.format_id = ESP_AUDIO_TYPE_OPUS; + ret = esp_gmf_pipeline_report_info(audio_recorder.pipe, ESP_GMF_INFO_SOUND, &info, sizeof(info)); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ESP_FAIL;}, "Failed to report sound info for OPUS encoder"); + break; + case AV_PROCESSOR_FORMAT_ID_PCM: + default: + ESP_LOGI(TAG, "Configuring PCM encoder"); + esp_gmf_obj_handle_t rate_cvt_pcm = NULL; + esp_gmf_pipeline_get_el_by_name(audio_recorder.pipe, "aud_rate_cvt", &rate_cvt_pcm); + if (rate_cvt_pcm) { + ret = esp_gmf_rate_cvt_set_dest_rate(rate_cvt_pcm, encoder_cfg->params.pcm.audio_info.sample_rate); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ESP_FAIL;}, "Failed to set PCM rate converter destination rate"); + } + esp_gmf_element_handle_t ch_cvt = NULL; + esp_gmf_pipeline_get_el_by_name(audio_recorder.pipe, "aud_ch_cvt", &ch_cvt); + if (ch_cvt) { + ret = esp_gmf_ch_cvt_set_dest_channel(ch_cvt, encoder_cfg->params.pcm.audio_info.channels); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ESP_FAIL;}, "Failed to set PCM channel converter channels"); + } + esp_gmf_element_handle_t bit_cvt = NULL; + esp_gmf_pipeline_get_el_by_name(audio_recorder.pipe, "aud_bit_cvt", &bit_cvt); + if (bit_cvt) { + ret = esp_gmf_bit_cvt_set_dest_bits(bit_cvt, encoder_cfg->params.pcm.audio_info.sample_bits); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ESP_FAIL;}, "Failed to set PCM bit converter bits"); + } + esp_pcm_enc_config_t pcm_enc_cfg = ESP_PCM_ENC_CONFIG_DEFAULT(); + pcm_enc_cfg.sample_rate = encoder_cfg->params.pcm.audio_info.sample_rate; + pcm_enc_cfg.channel = encoder_cfg->params.pcm.audio_info.channels; + pcm_enc_cfg.bits_per_sample = encoder_cfg->params.pcm.audio_info.sample_bits; + pcm_enc_cfg.frame_duration = encoder_cfg->params.pcm.audio_info.frame_duration; + + esp_audio_enc_config_t enc_cfg = { + .type = ESP_AUDIO_TYPE_PCM, + .cfg_sz = sizeof(esp_pcm_enc_config_t), + .cfg = &pcm_enc_cfg, + }; + ret = esp_gmf_audio_enc_reconfig(enc_el, &enc_cfg); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ESP_FAIL;}, "Failed to configure PCM encoder"); + + info.sample_rates = 16000; + info.channels = 1; + info.bits = 16; + ret = esp_gmf_pipeline_report_info(audio_recorder.pipe, ESP_GMF_INFO_SOUND, &info, sizeof(info)); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ESP_FAIL;}, "Failed to report sound info for PCM encoder"); + break; + } + + ESP_LOGI(TAG, "All encoders configured successfully"); + return ESP_OK; +} + +static int feeder_outport_acquire_write(void *handle, esp_gmf_data_bus_block_t *load, int wanted_size, int block_ticks) +{ + return wanted_size; +} + +static int feeder_outport_release_write(void *handle, esp_gmf_data_bus_block_t *blk, int block_ticks) +{ + if (audio_manager.enable_mixer) { + esp_audio_render_stream_write(audio_manager.stream[AUDIO_RENDER_STREAM_FEEDER], blk->buf, blk->valid_size); + } else { + esp_codec_dev_write(audio_manager.config.play_dev, blk->buf, blk->valid_size); + } + return blk->valid_size; +} + +static int feeder_inport_acquire_read(void *handle, esp_gmf_data_bus_block_t *blk, int wanted_size, int block_ticks) +{ + if (audio_feeder.config.decoder_cfg.format == AV_PROCESSOR_FORMAT_ID_PCM) { + esp_gmf_data_bus_block_t _blk = {0}; + _blk.buf = blk->buf; + _blk.buf_length = blk->buf_length; + int ret = esp_gmf_db_acquire_read(audio_feeder.fifo, &_blk, wanted_size, block_ticks); + if (ret < 0) { + ESP_LOGE(TAG, "Fifo acquire read failed (%d)", ret); + return ret; + } + memcpy(blk->buf, _blk.buf, _blk.valid_size); + blk->valid_size = _blk.valid_size; + esp_gmf_db_release_read(audio_feeder.fifo, &_blk, block_ticks); + blk->valid_size = _blk.valid_size; + } else { + int ret = esp_gmf_db_acquire_read(audio_feeder.fifo, blk, wanted_size, + block_ticks); + if (ret < 0) { + ESP_LOGE(TAG, "Fifo acquire read failed (%d)", ret); + return ret; + } + esp_gmf_db_release_read(audio_feeder.fifo, blk, block_ticks); + } + return ESP_GMF_ERR_OK; +} + +static int feeder_inport_release_read(void *handle, esp_gmf_data_bus_block_t *blk, int block_ticks) +{ + return blk->valid_size; +} + +static esp_err_t feeder_configure_all_decoders(void) +{ + esp_gmf_element_handle_t dec_el = NULL; + esp_gmf_pipeline_get_el_by_name(audio_feeder.pipe, "aud_dec", &dec_el); + if (!dec_el) { + ESP_LOGE(TAG, "Failed to get decoder element"); + return ESP_FAIL; + } + esp_err_t ret = ESP_OK; + const av_processor_decoder_config_t *decoder_cfg = &audio_feeder.config.decoder_cfg; + esp_gmf_info_sound_t info = {0}; + switch (decoder_cfg->format) { + case AV_PROCESSOR_FORMAT_ID_PCM: + ESP_LOGI(TAG, "Configuring PCM decoder"); + info.sample_rates = decoder_cfg->params.pcm.audio_info.sample_rate; + info.channels = decoder_cfg->params.pcm.audio_info.channels; + info.bits = decoder_cfg->params.pcm.audio_info.sample_bits; + info.format_id = ESP_AUDIO_SIMPLE_DEC_TYPE_PCM; + ret = esp_gmf_audio_dec_reconfig_by_sound_info(dec_el, &info); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ESP_FAIL;}, "Failed to configure PCM decoder"); + break; + + case AV_PROCESSOR_FORMAT_ID_OPUS: + ESP_LOGI(TAG, "Configuring OPUS decoder"); + esp_opus_dec_cfg_t opus_dec_cfg = ESP_OPUS_DEC_CONFIG_DEFAULT(); + opus_dec_cfg.frame_duration = convert_opus_dec_frame_duration(decoder_cfg->params.opus.audio_info.frame_duration); + opus_dec_cfg.channel = decoder_cfg->params.opus.audio_info.channels; + opus_dec_cfg.sample_rate = decoder_cfg->params.opus.audio_info.sample_rate; + + esp_audio_simple_dec_cfg_t dec_cfg = { + .dec_type = ESP_AUDIO_TYPE_OPUS, + .dec_cfg = &opus_dec_cfg, + .cfg_size = sizeof(esp_opus_dec_cfg_t), + }; + ret = esp_gmf_audio_dec_reconfig(dec_el, &dec_cfg); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ESP_FAIL;}, "Failed to configure OPUS decoder"); + + info.sample_rates = decoder_cfg->params.opus.audio_info.sample_rate; + info.channels = decoder_cfg->params.opus.audio_info.channels; + info.bits = 16; + info.format_id = ESP_FOURCC_OPUS; + break; + + case AV_PROCESSOR_FORMAT_ID_G711A: + ESP_LOGI(TAG, "Configuring G711A decoder"); + info.sample_rates = 8000; + info.channels = 1; + info.bits = 16; + info.format_id = ESP_AUDIO_SIMPLE_DEC_TYPE_G711A; + ret = esp_gmf_audio_dec_reconfig_by_sound_info(dec_el, &info); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ESP_FAIL;}, "Failed to configure G711A decoder"); + break; + + case AV_PROCESSOR_FORMAT_ID_G711U: + ESP_LOGI(TAG, "Configuring G711U decoder"); + info.sample_rates = 8000; + info.channels = 1; + info.bits = 16; + info.format_id = ESP_AUDIO_SIMPLE_DEC_TYPE_G711U; + ret = esp_gmf_audio_dec_reconfig_by_sound_info(dec_el, &info); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ESP_FAIL;}, "Failed to configure G711U decoder"); + break; + + default: + ESP_LOGE(TAG, "Unsupported decoder format: %d", decoder_cfg->format); + return ESP_ERR_NOT_SUPPORTED; + } + + esp_gmf_pipeline_report_info(audio_feeder.pipe, ESP_GMF_INFO_SOUND, &info, sizeof(info)); + + esp_gmf_element_handle_t rate_cvt_el = NULL; + esp_gmf_pipeline_get_el_by_name(audio_feeder.pipe, "aud_rate_cvt", &rate_cvt_el); + if (rate_cvt_el) { + esp_gmf_rate_cvt_set_dest_rate(rate_cvt_el, audio_manager.config.board_sample_rate); + } + esp_gmf_element_handle_t ch_cvt_el = NULL; + esp_gmf_pipeline_get_el_by_name(audio_feeder.pipe, "aud_ch_cvt", &ch_cvt_el); + if (ch_cvt_el) { + esp_gmf_ch_cvt_set_dest_channel(ch_cvt_el, audio_manager.config.board_channels); + } + esp_gmf_element_handle_t bit_cvt_el = NULL; + esp_gmf_pipeline_get_el_by_name(audio_feeder.pipe, "aud_bit_cvt", &bit_cvt_el); + if (bit_cvt_el) { + esp_gmf_bit_cvt_set_dest_bits(bit_cvt_el, audio_manager.config.board_bits); + } + ESP_LOGI(TAG, "All decoders configured successfully"); + return ESP_OK; +} + +static esp_err_t feeder_setup_pipeline_ports(void) +{ + esp_gmf_port_handle_t inport = { 0 }; + if (audio_feeder.config.decoder_cfg.format == AV_PROCESSOR_FORMAT_ID_PCM) { + inport = NEW_ESP_GMF_PORT_IN_BYTE( + feeder_inport_acquire_read, + feeder_inport_release_read, + NULL, NULL, AUDIO_BUFFER_SIZE, portMAX_DELAY); + } else { + inport = NEW_ESP_GMF_PORT_IN_BLOCK( + feeder_inport_acquire_read, + feeder_inport_release_read, + NULL, NULL, AUDIO_BUFFER_SIZE, portMAX_DELAY); + } + esp_err_t ret = esp_gmf_pipeline_reg_el_port(audio_feeder.pipe, "aud_dec", ESP_GMF_IO_DIR_READER, inport); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ESP_FAIL;}, "Failed to register playback input port"); + + esp_gmf_port_handle_t outport = NEW_ESP_GMF_PORT_OUT_BYTE( + feeder_outport_acquire_write, + feeder_outport_release_write, + NULL, NULL, AUDIO_BUFFER_SIZE, portMAX_DELAY); + + ret = esp_gmf_pipeline_reg_el_port(audio_feeder.pipe, "aud_rate_cvt", ESP_GMF_IO_DIR_WRITER, outport); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ESP_FAIL;}, "Failed to register playback output port"); + + return ESP_OK; +} + +static void feeder_cleanup_on_error(void) +{ + if (audio_feeder.pipe) { + esp_gmf_pipeline_destroy(audio_feeder.pipe); + audio_feeder.pipe = NULL; + } + if (audio_feeder.task) { + esp_gmf_task_deinit(audio_feeder.task); + audio_feeder.task = NULL; + } + if (audio_feeder.fifo) { + esp_gmf_db_deinit(audio_feeder.fifo); + audio_feeder.fifo = NULL; + } +} + +esp_err_t audio_manager_init(audio_manager_config_t *config) +{ + ESP_LOGI(TAG, "Initializing audio manager..."); + if (config == NULL) { + ESP_LOGE(TAG, "Invalid configuration"); + return ESP_FAIL; + } + audio_manager.config = *config; + + esp_audio_dec_register_default(); + esp_audio_simple_dec_register_default(); + esp_audio_enc_register_default(); + + audio_recorder.state = AUDIO_RUN_STATE_CLOSED; + audio_feeder.state = AUDIO_RUN_STATE_CLOSED; + audio_playback.state = AUDIO_RUN_STATE_CLOSED; + // Default disable mixer + audio_manager.enable_mixer = false; + ESP_LOGI(TAG, "Audio manager initialized successfully"); + return ESP_OK; +} + +esp_err_t audio_manager_deinit(void) +{ + ESP_LOGI(TAG, "Deinitializing audio manager..."); + esp_audio_dec_unregister_default(); + esp_audio_simple_dec_unregister_default(); + esp_audio_enc_unregister_default(); + ESP_LOGI(TAG, "Audio manager deinitialized successfully"); + return ESP_OK; +} + +esp_err_t audio_playback_open(audio_playback_config_t *config) +{ + ESP_LOGI(TAG, "Opening audio playback..."); + + if (config == NULL) { + ESP_LOGE(TAG, "Invalid configuration"); + return ESP_FAIL; + } + audio_playback.config = *config; + + av_audio_playback_pool_init(&audio_playback.pool); + esp_asp_cfg_t cfg = { + .in.cb = NULL, + .in.user_ctx = NULL, + .out.cb = playback_out_data_callback, + .out.user_ctx = NULL, + .task_prio = audio_playback.config.playback_task_config.task_prio, + .task_stack = audio_playback.config.playback_task_config.task_stack, + .task_core = audio_playback.config.playback_task_config.task_core, + .task_stack_in_ext = audio_playback.config.playback_task_config.task_stack_in_ext, + }; + esp_err_t err = esp_audio_simple_player_new(&cfg, &audio_playback.player); + if (err != ESP_OK || !audio_playback.player) { + ESP_LOGE(TAG, "Failed to create audio playback player"); + return ESP_FAIL; + } + err = esp_audio_simple_player_set_event(audio_playback.player, playback_event_callback, NULL); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to set playback event callback"); + esp_audio_simple_player_destroy(audio_playback.player); + audio_playback.player = NULL; + return ESP_FAIL; + } + audio_playback.state = AUDIO_RUN_STATE_IDLE; + ESP_LOGI(TAG, "Audio playback opened successfully"); + return ESP_OK; +} + +esp_err_t audio_playback_close(void) +{ + if (audio_playback.state == AUDIO_RUN_STATE_CLOSED) { + ESP_LOGW(TAG, "Audio playback is already closed"); + return ESP_OK; + } + ESP_LOGI(TAG, "Closing audio playback..."); + if (audio_playback.state == AUDIO_RUN_STATE_PLAYING) { + esp_audio_simple_player_stop(audio_playback.player); + } + if (audio_playback.player) { + esp_err_t err = esp_audio_simple_player_destroy(audio_playback.player); + ESP_GMF_RET_ON_NOT_OK(TAG, err, {return ESP_FAIL;}, "Failed to destroy audio playback player"); + audio_playback.player = NULL; + } + if (audio_playback.pool) { + av_audio_playback_pool_deinit(audio_playback.pool); + audio_playback.pool = NULL; + } + audio_playback.state = AUDIO_RUN_STATE_CLOSED; + ESP_LOGI(TAG, "Audio playback closed successfully"); + return ESP_OK; +} + +esp_err_t audio_playback_play(const char *url) +{ + if (!url) { + ESP_LOGE(TAG, "Invalid URL for playback"); + return ESP_FAIL; + } + if (audio_playback.state == AUDIO_RUN_STATE_CLOSED) { + ESP_LOGE(TAG, "Cannot play playback - not opened"); + return ESP_FAIL; + } + if (audio_playback.state == AUDIO_RUN_STATE_PLAYING) { + ESP_LOGW(TAG, "Audio playback is already playing, stopping current playback"); + esp_audio_simple_player_stop(audio_playback.player); + } + ESP_LOGI(TAG, "Starting playback: %s, state: %d", url, audio_playback.state); + esp_err_t err = esp_audio_simple_player_run(audio_playback.player, url, NULL); + ESP_GMF_RET_ON_NOT_OK(TAG, err, {return ESP_FAIL;}, "Failed to start playback"); + + audio_playback.state = AUDIO_RUN_STATE_PLAYING; + return ESP_OK; +} + +esp_err_t audio_playback_play_wait_end(const char *url) +{ + if (!url) { + ESP_LOGE(TAG, "Invalid URL for playback"); + return ESP_FAIL; + } + if (audio_playback.state == AUDIO_RUN_STATE_CLOSED) { + ESP_LOGE(TAG, "Cannot play playback - not opened"); + return ESP_FAIL; + } + if (audio_playback.state == AUDIO_RUN_STATE_PLAYING) { + ESP_LOGW(TAG, "Audio playback is already playing, waiting for it to end"); + return ESP_OK; + } + ESP_LOGI(TAG, "Waiting for playback to end: %s", url); + esp_err_t err = esp_audio_simple_player_run_to_end(audio_playback.player, url, NULL); + ESP_GMF_RET_ON_NOT_OK(TAG, err, {return ESP_FAIL;}, "Failed to wait for playback to end"); + audio_playback.state = AUDIO_RUN_STATE_IDLE; + return ESP_OK; +} + +esp_err_t audio_playback_pause(void) +{ + if (audio_playback.state == AUDIO_RUN_STATE_CLOSED) { + ESP_LOGE(TAG, "Cannot pause playback - not opened"); + return ESP_FAIL; + } + if (audio_playback.state == AUDIO_RUN_STATE_PAUSED) { + ESP_LOGW(TAG, "Audio playback is already paused"); + return ESP_OK; + } + ESP_LOGI(TAG, "Pausing audio playback..."); + esp_err_t err = esp_audio_simple_player_pause(audio_playback.player); + ESP_GMF_RET_ON_NOT_OK(TAG, err, {return ESP_FAIL;}, "Failed to pause playback"); + audio_playback.state = AUDIO_RUN_STATE_PAUSED; + ESP_LOGI(TAG, "Audio playback paused successfully"); + return ESP_OK; +} + +esp_err_t audio_playback_resume(void) +{ + if (audio_playback.state == AUDIO_RUN_STATE_CLOSED) { + ESP_LOGE(TAG, "Cannot resume playback - not opened"); + return ESP_FAIL; + } + if (audio_playback.state == AUDIO_RUN_STATE_PLAYING) { + ESP_LOGW(TAG, "Audio playback is already playing"); + return ESP_OK; + } + ESP_LOGI(TAG, "Resuming audio playback..."); + esp_err_t err = esp_audio_simple_player_resume(audio_playback.player); + ESP_GMF_RET_ON_NOT_OK(TAG, err, {return ESP_FAIL;}, "Failed to resume playback"); + audio_playback.state = AUDIO_RUN_STATE_PLAYING; + ESP_LOGI(TAG, "Audio playback resumed successfully"); + return ESP_OK; +} + +enum audio_player_state_e audio_playback_get_state(void) +{ + return audio_playback.state; +} + +esp_err_t audio_playback_stop(void) +{ + if (audio_playback.state == AUDIO_RUN_STATE_CLOSED) { + ESP_LOGE(TAG, "Cannot stop playback - not opened"); + return ESP_FAIL; + } + if (audio_playback.state == AUDIO_RUN_STATE_IDLE) { + ESP_LOGW(TAG, "Audio playback is already idle"); + return ESP_OK; + } + ESP_LOGI(TAG, "Stopping audio playback..."); + esp_err_t err = esp_audio_simple_player_stop(audio_playback.player); + ESP_GMF_RET_ON_NOT_OK(TAG, err, {return ESP_FAIL;}, "Failed to stop playback"); + + audio_playback.state = AUDIO_RUN_STATE_IDLE; + ESP_LOGI(TAG, "Audio playback stopped successfully"); + return ESP_OK; +} + + +esp_err_t audio_prompt_open(audio_prompt_config_t *config) +{ + if (config == NULL) { + ESP_LOGE(TAG, "Invalid configuration"); + return ESP_FAIL; + } + audio_prompt.config = *config; + + av_audio_playback_pool_init(&audio_prompt.pool); + + esp_asp_cfg_t cfg = { + .in.cb = NULL, + .in.user_ctx = NULL, + .out.cb = prompt_out_data_callback, + .out.user_ctx = NULL, + .task_prio = audio_prompt.config.prompt_task_config.task_prio, + .task_stack = audio_prompt.config.prompt_task_config.task_stack, + .task_core = audio_prompt.config.prompt_task_config.task_core, + .task_stack_in_ext = audio_prompt.config.prompt_task_config.task_stack_in_ext, + }; + esp_err_t err = esp_audio_simple_player_new(&cfg, &audio_prompt.player); + if (err != ESP_OK || !audio_prompt.player) { + ESP_LOGE(TAG, "Failed to create audio playback player"); + return ESP_FAIL; + } + err = esp_audio_simple_player_set_event(audio_prompt.player, prompt_event_callback, NULL); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to set playback event callback"); + esp_audio_simple_player_destroy(audio_playback.player); + audio_prompt.player = NULL; + return ESP_FAIL; + } + audio_prompt.state = AUDIO_RUN_STATE_IDLE; + ESP_LOGI(TAG, "Audio prompt opened successfully"); + return ESP_OK; +} + +esp_err_t audio_prompt_close(void) +{ + if (audio_prompt.state == AUDIO_RUN_STATE_CLOSED) { + ESP_LOGW(TAG, "Audio prompt is already closed"); + return ESP_OK; + } + ESP_LOGI(TAG, "Closing audio prompt..."); + if (audio_prompt.state == AUDIO_RUN_STATE_IDLE) { + ESP_LOGW(TAG, "Audio prompt is already idle"); + return ESP_OK; + } + ESP_LOGI(TAG, "Stopping audio prompt..."); + if (audio_prompt.state == AUDIO_RUN_STATE_PLAYING) { + // NOTE: Normally, not will reach here, because the prompt is played to end automatically. + ESP_LOGW(TAG, "Audio prompt is playing, stopping current prompt"); + esp_audio_simple_player_stop(audio_prompt.player); + } + if (audio_prompt.player) { + esp_err_t err = esp_audio_simple_player_destroy(audio_prompt.player); + ESP_GMF_RET_ON_NOT_OK(TAG, err, {return ESP_FAIL;}, "Failed to destroy audio prompt player"); + audio_prompt.player = NULL; + } + if (audio_prompt.pool) { + av_audio_playback_pool_deinit(audio_prompt.pool); + audio_prompt.pool = NULL; + } + audio_prompt.state = AUDIO_RUN_STATE_IDLE; + ESP_LOGI(TAG, "Audio prompt stopped successfully"); + return ESP_OK; +} + +esp_err_t audio_prompt_play(const char *url) +{ + if (!url) { + ESP_LOGE(TAG, "Invalid URL for prompt"); + return ESP_FAIL; + } + if (audio_prompt.state == AUDIO_RUN_STATE_CLOSED) { + ESP_LOGE(TAG, "Cannot play prompt - not opened"); + return ESP_FAIL; + } + if (audio_prompt.state == AUDIO_RUN_STATE_PLAYING) { + // NOTE: Normally, not will reach here, because the prompt is played to end automatically. + ESP_LOGW(TAG, "Audio prompt is already playing, stopping current prompt"); + esp_audio_simple_player_stop(audio_prompt.player); + } + audio_prompt.state = AUDIO_RUN_STATE_PLAYING; + ESP_LOGI(TAG, "Starting prompt: %s, state: %d", url, audio_prompt.state); + esp_err_t err = esp_audio_simple_player_run_to_end(audio_prompt.player, url, NULL); + ESP_GMF_RET_ON_NOT_OK(TAG, err, {return ESP_FAIL;}, "Failed to start prompt"); + audio_prompt.state = AUDIO_RUN_STATE_IDLE; + + return ESP_OK; +} + +esp_err_t audio_recorder_open(audio_recorder_config_t *config) +{ + ESP_LOGI(TAG, "Opening audio recorder..."); + + if (config == NULL) { + ESP_LOGE(TAG, "Invalid configuration"); + return ESP_FAIL; + } + memcpy(&audio_recorder.config, config, sizeof(audio_recorder_config_t)); + + esp_err_t err = esp_gmf_fifo_create(5, 1, &audio_recorder.fifo); + if (err != ESP_GMF_ERR_OK) { + ESP_LOGE(TAG, "Failed to create FIFO, err: %d", err); + return ESP_FAIL; + } + av_audio_recorder_pool_init(&audio_recorder.pool, &audio_recorder.config.encoder_cfg); + ESP_GMF_POOL_SHOW_ITEMS(audio_recorder.pool); + err = recorder_setup_afe_config(); + ESP_GMF_RET_ON_NOT_OK(TAG, err, {goto cleanup_and_exit;}, "Failed to setup AFE config"); + + const char **elements; + int element_count = 0; + const char *input_name = NULL, *output_name = NULL; + recorder_get_pipeline_config(&audio_recorder.config.encoder_cfg, &elements, &element_count, &input_name, &output_name); + + err = esp_gmf_pool_new_pipeline(audio_recorder.pool, NULL, elements, element_count, NULL, &audio_recorder.pipe); + if (err != ESP_OK || audio_recorder.pipe == NULL) { + ESP_LOGE(TAG, "Failed to create recorder pipeline: %02x", err); + goto cleanup_and_exit; + } + err = recorder_configure_all_encoders(); + ESP_GMF_RET_ON_NOT_OK(TAG, err, {goto cleanup_and_exit;}, "Failed to configure encoders"); + + err = recorder_setup_pipeline_ports(input_name, output_name); + ESP_GMF_RET_ON_NOT_OK(TAG, err, {goto cleanup_and_exit;}, "Failed to setup pipeline ports"); + + err = recorder_setup_task_and_run(); + ESP_GMF_RET_ON_NOT_OK(TAG, err, {goto cleanup_and_exit;}, "Failed to setup task and run"); + + audio_recorder.cb = config->recorder_event_cb; + audio_recorder.ctx = config->recorder_ctx; + audio_recorder.state = AUDIO_RUN_STATE_PLAYING; + + ESP_LOGI(TAG, "Audio recorder opened successfully"); + return ESP_OK; + +cleanup_and_exit: + ESP_LOGE(TAG, "Failed to open audio recorder, cleaning up..."); + recorder_cleanup_on_error(); + return ESP_FAIL; +} + +esp_err_t audio_recorder_pause(void) +{ + if (audio_recorder.state == AUDIO_RUN_STATE_CLOSED) { + ESP_LOGE(TAG, "Cannot pause recorder - not opened"); + return ESP_FAIL; + } + if (audio_recorder.state == AUDIO_RUN_STATE_PAUSED) { + ESP_LOGW(TAG, "Audio recorder is already paused"); + return ESP_OK; + } + ESP_LOGI(TAG, "Pausing audio recorder..."); + // esp_gmf_fifo_abort(audio_recorder.fifo); + esp_gmf_fifo_done_write(audio_recorder.fifo); + esp_err_t ret = esp_gmf_pipeline_pause(audio_recorder.pipe); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ESP_FAIL;}, "Failed to pause recorder pipeline"); + audio_recorder.state = AUDIO_RUN_STATE_PAUSED; + ESP_LOGI(TAG, "Audio recorder paused successfully"); + return ESP_OK; +} + +esp_err_t audio_recorder_resume(void) +{ + if (audio_recorder.state == AUDIO_RUN_STATE_CLOSED) { + ESP_LOGE(TAG, "Cannot resume recorder - not opened"); + return ESP_FAIL; + } + if (audio_recorder.state == AUDIO_RUN_STATE_PLAYING) { + ESP_LOGW(TAG, "Audio recorder is already running"); + return ESP_OK; + } + ESP_LOGI(TAG, "Resuming audio recorder..."); + esp_err_t ret = esp_gmf_pipeline_resume(audio_recorder.pipe); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ESP_FAIL;}, "Failed to resume recorder pipeline"); + audio_recorder.state = AUDIO_RUN_STATE_PLAYING; + ESP_LOGI(TAG, "Audio recorder resumed successfully"); + return ESP_OK; +} + +esp_err_t audio_recorder_get_afe_manager_handle(esp_gmf_afe_manager_handle_t *afe_manager_handle) +{ + if (!afe_manager_handle) { + ESP_LOGE(TAG, "Invalid parameters for recorder get origin AFE handle"); + return ESP_FAIL; + } + if (!audio_recorder.afe_manager) { + ESP_LOGE(TAG, "Audio recorder AFE manager not initialized"); + return ESP_FAIL; + } + *afe_manager_handle = audio_recorder.afe_manager; + return ESP_OK; +} + + +esp_err_t audio_recorder_close(void) +{ + if (audio_recorder.state == AUDIO_RUN_STATE_CLOSED) { + ESP_LOGW(TAG, "Audio recorder is already closed"); + return ESP_OK; + } + // NOTE: Set task priority to 23 is a workaround for the issue that the recorder task is preempted by other tasks. + UBaseType_t basic_priority = uxTaskPriorityGet(NULL); + vTaskPrioritySet(NULL, 23); + vTaskDelay(pdMS_TO_TICKS(10)); + ESP_LOGI(TAG, "Closing audio recorder..."); + if (audio_recorder.pipe) { + esp_gmf_fifo_abort(audio_recorder.fifo); + esp_gmf_pipeline_stop(audio_recorder.pipe); + audio_recorder.pipe = NULL; + } + if (audio_recorder.task) { + esp_gmf_task_deinit(audio_recorder.task); + audio_recorder.task = NULL; + } + if (audio_recorder.fifo) { + esp_gmf_fifo_destroy(audio_recorder.fifo); + audio_recorder.fifo = NULL; + } + if (audio_recorder.afe_cfg) { + afe_config_free(audio_recorder.afe_cfg); + audio_recorder.afe_cfg = NULL; + } + if (audio_recorder.afe_manager) { + esp_gmf_afe_manager_destroy(audio_recorder.afe_manager); + audio_recorder.afe_manager = NULL; + } + if (audio_recorder.pipe) { + esp_gmf_pipeline_destroy(audio_recorder.pipe); + audio_recorder.pipe = NULL; + } + if (audio_recorder.pool) { + av_audio_recorder_pool_deinit(audio_recorder.pool); + audio_recorder.pool = NULL; + } + audio_recorder.cb = NULL; + audio_recorder.ctx = NULL; + audio_recorder.state = AUDIO_RUN_STATE_CLOSED; + + vTaskPrioritySet(NULL, basic_priority); + + ESP_LOGI(TAG, "Audio recorder closed successfully"); + return ESP_OK; +} + +esp_err_t audio_recorder_read_data(uint8_t *data, int data_size) +{ + if (!data || data_size <= 0) { + ESP_LOGE(TAG, "Invalid parameters for recorder read"); + return ESP_FAIL; + } + if (audio_recorder.state == AUDIO_RUN_STATE_CLOSED || !audio_recorder.fifo) { + ESP_LOGE(TAG, "Recorder not available for reading"); + return ESP_FAIL; + } + esp_gmf_data_bus_block_t blk = {0}; + int ret = esp_gmf_fifo_acquire_read(audio_recorder.fifo, &blk, data_size, portMAX_DELAY); + if (ret < 0) { + // ESP_LOGE(TAG, "Failed to acquire read from recorder FIFO (%d)", ret); + return ret; + } + int bytes_to_copy = blk.valid_size; + if (bytes_to_copy > data_size) { + ESP_LOGI(TAG, "Read data size is larger than expected, valid size: %d, expected: %d", bytes_to_copy, data_size); + bytes_to_copy = data_size; + } + memcpy(data, blk.buf, bytes_to_copy); + esp_gmf_fifo_release_read(audio_recorder.fifo, &blk, portMAX_DELAY); + +#if CONFIG_MEDIA_DUMP_ENABLE && CONFIG_MEDIA_DUMP_AUDIO_AFTER_AEC + media_dump_feed(NULL, (uint8_t *)blk.buf, blk.valid_size); +#endif /* CONFIG_MEDIA_DUMP_ENABLE && CONFIG_MEDIA_DUMP_AUDIO_AFTER_AEC */ + return bytes_to_copy; +} +esp_err_t audio_feeder_feed_data(uint8_t *data, int data_size) +{ + if (!data || data_size <= 0) { + ESP_LOGE(TAG, "Invalid parameters for playback feed"); + return ESP_FAIL; + } + if (audio_feeder.state == AUDIO_RUN_STATE_CLOSED || !audio_feeder.fifo) { + ESP_LOGW(TAG, "Playback not available for feeding"); + return ESP_FAIL; + } + esp_gmf_data_bus_block_t blk = {0}; + + if (audio_feeder.config.decoder_cfg.format == AV_PROCESSOR_FORMAT_ID_PCM) { + int ret = esp_gmf_db_acquire_write(audio_feeder.fifo, &blk, data_size, portMAX_DELAY); + if (ret < 0) { + ESP_LOGE(TAG, "Failed to acquire write to playback FIFO (0x%x)", ret); + return ESP_FAIL; + } + blk.buf = (uint8_t *)data; + blk.valid_size = data_size; + ret = esp_gmf_db_release_write(audio_feeder.fifo, &blk, portMAX_DELAY); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ESP_FAIL;}, "Failed to release write to playback FIFO"); + } else { + int ret = esp_gmf_db_acquire_write(audio_feeder.fifo, &blk, data_size, portMAX_DELAY); + if (ret < 0) { + ESP_LOGE(TAG, "Failed to acquire write to playback FIFO (0x%x)", ret); + return ESP_FAIL; + } + int bytes_to_copy = (data_size < blk.buf_length) ? data_size : blk.buf_length; + memcpy(blk.buf, data, bytes_to_copy); + blk.valid_size = bytes_to_copy; + + ret = esp_gmf_db_release_write(audio_feeder.fifo, &blk, portMAX_DELAY); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ESP_FAIL;}, "Failed to release write to playback FIFO"); + } + return ESP_OK; +} + +esp_err_t audio_feeder_open(audio_feeder_config_t *config) +{ + ESP_LOGI(TAG, "Opening audio feeder..."); + if (config == NULL) { + ESP_LOGE(TAG, "Invalid decoder configuration"); + return ESP_FAIL; + } + memcpy(&audio_feeder.config, config, sizeof(audio_feeder_config_t)); + esp_err_t err; + if (audio_feeder.config.decoder_cfg.format == AV_PROCESSOR_FORMAT_ID_PCM) { + err = esp_gmf_db_new_ringbuf(1, AUDIO_RINGBUFFER_SIZE, &audio_feeder.fifo); + ESP_GMF_RET_ON_NOT_OK(TAG, err, {return ESP_FAIL;}, "Failed to create playback ringbuffer"); + } else { + err = esp_gmf_db_new_fifo(AUDIO_FIFO_NUM, 1, &audio_feeder.fifo); + ESP_GMF_RET_ON_NOT_OK(TAG, err, {return ESP_FAIL;}, "Failed to create playback FIFO"); + } + const char *elements[] = {"aud_dec", "aud_ch_cvt", "aud_bit_cvt", "aud_rate_cvt"}; + av_audio_feeder_pool_init(&audio_feeder.pool, &audio_feeder.config.decoder_cfg); + ESP_GMF_POOL_SHOW_ITEMS(audio_feeder.pool); + err = esp_gmf_pool_new_pipeline(audio_feeder.pool, NULL, elements, + sizeof(elements) / sizeof(char *), NULL, &audio_feeder.pipe); + if (err != ESP_OK || !audio_feeder.pipe) { + ESP_LOGE(TAG, "Failed to create feeder pipeline"); + goto cleanup_and_exit; + } + err = feeder_configure_all_decoders(); + ESP_GMF_RET_ON_NOT_OK(TAG, err, {goto cleanup_and_exit;}, "Failed to configure decoders"); + + err = feeder_setup_pipeline_ports(); + ESP_GMF_RET_ON_NOT_OK(TAG, err, {goto cleanup_and_exit;}, "Failed to setup pipeline ports"); + + esp_gmf_task_cfg_t cfg = DEFAULT_ESP_GMF_TASK_CONFIG(); + if (audio_feeder.config.feeder_task_config.task_stack > 0) { + cfg.thread.stack = audio_feeder.config.feeder_task_config.task_stack; + if (audio_feeder.config.decoder_cfg.format == AV_PROCESSOR_FORMAT_ID_OPUS && cfg.thread.stack < (4096 * 10)) { + ESP_LOGW(TAG, "Feeder OPUS decoder requires larger stack size, using default 40k stack"); + cfg.thread.stack = AUDIO_OPUS_PIP_TASK_STACK_SIZE; // 40k stack + } + } else { + cfg.thread.stack = AUDIO_OPUS_PIP_TASK_STACK_SIZE; + } + cfg.thread.prio = audio_feeder.config.feeder_task_config.task_prio; + cfg.thread.core = audio_feeder.config.feeder_task_config.task_core; + cfg.thread.stack_in_ext = audio_feeder.config.feeder_task_config.task_stack_in_ext; + cfg.name = "audio_feeder_task"; + + err = esp_gmf_task_init(&cfg, &audio_feeder.task); + ESP_GMF_RET_ON_NOT_OK(TAG, err, {goto cleanup_and_exit;}, "Failed to create playback task"); + + esp_gmf_pipeline_bind_task(audio_feeder.pipe, audio_feeder.task); + esp_gmf_pipeline_loading_jobs(audio_feeder.pipe); + + audio_feeder.state = AUDIO_RUN_STATE_IDLE; + ESP_LOGI(TAG, "Audio feeder opened successfully"); + return ESP_OK; + +cleanup_and_exit: + ESP_LOGE(TAG, "Failed to open audio feeder, cleaning up..."); + feeder_cleanup_on_error(); + return ESP_FAIL; +} + +esp_err_t audio_feeder_close(void) +{ + if (audio_feeder.state == AUDIO_RUN_STATE_CLOSED) { + ESP_LOGW(TAG, "Audio feeder is already closed"); + return ESP_OK; + } + ESP_LOGI(TAG, "Closing audio feeder..."); + if (audio_feeder.state == AUDIO_RUN_STATE_PLAYING) { + audio_feeder_stop(); + } + if (audio_feeder.pipe) { + esp_gmf_pipeline_destroy(audio_feeder.pipe); + audio_feeder.pipe = NULL; + } + if (audio_feeder.task) { + esp_gmf_task_deinit(audio_feeder.task); + audio_feeder.task = NULL; + } + if (audio_feeder.fifo) { + esp_gmf_db_deinit(audio_feeder.fifo); + audio_feeder.fifo = NULL; + } + if (audio_feeder.pool) { + av_audio_feeder_pool_deinit(audio_feeder.pool); + audio_feeder.pool = NULL; + } + audio_feeder.state = AUDIO_RUN_STATE_CLOSED; + ESP_LOGI(TAG, "Audio playback closed successfully"); + return ESP_OK; +} + +esp_err_t audio_feeder_run(void) +{ + if (audio_feeder.state == AUDIO_RUN_STATE_CLOSED) { + ESP_LOGE(TAG, "Cannot run playback - not opened"); + return ESP_FAIL; + } + if (audio_feeder.state == AUDIO_RUN_STATE_PLAYING) { + ESP_LOGW(TAG, "Audio playback is already running"); + return ESP_OK; + } + esp_err_t ret = esp_gmf_pipeline_run(audio_feeder.pipe); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ESP_FAIL;}, "Failed to start playback pipeline"); + + audio_feeder.state = AUDIO_RUN_STATE_PLAYING; + ESP_LOGI(TAG, "Audio playback started successfully"); + return ESP_OK; +} + +esp_err_t audio_feeder_stop(void) +{ + if (audio_feeder.state == AUDIO_RUN_STATE_CLOSED) { + ESP_LOGE(TAG, "Cannot stop feeder - not opened"); + return ESP_FAIL; + } + if (audio_feeder.state == AUDIO_RUN_STATE_IDLE) { + ESP_LOGW(TAG, "Audio feeder is already stopped"); + return ESP_OK; + } + // esp_gmf_db_abort(audio_feeder.fifo); + esp_gmf_db_done_write(audio_feeder.fifo); + ESP_LOGI(TAG, "Stopping audio feeder..."); + esp_err_t ret = esp_gmf_pipeline_stop(audio_feeder.pipe); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ESP_FAIL;}, "Failed to stop feeder pipeline"); + + audio_feeder.state = AUDIO_RUN_STATE_IDLE; + ESP_LOGI(TAG, "Audio feeder stopped successfully"); + return ESP_OK; +} + +esp_err_t audio_processor_mixer_open(void) +{ + ESP_LOGI(TAG, "Initializing audio render..."); + esp_audio_render_cfg_t cfg = { + .max_stream_num = AUDIO_RENDER_STREAM_TYPE_MAX, + .out_sample_info = { + .sample_rate = audio_manager.config.board_sample_rate, + .bits_per_sample = audio_manager.config.board_bits, + .channel = audio_manager.config.board_channels, + }, + .out_writer = audio_render_write_hdlr, + }; + esp_err_t ret = esp_audio_render_create(&cfg, &audio_manager.render_handle); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to create audio render"); + + esp_audio_render_stream_get(audio_manager.render_handle, ESP_AUDIO_RENDER_FIRST_STREAM, &audio_manager.stream[AUDIO_RENDER_STREAM_FEEDER]); + esp_audio_render_stream_get(audio_manager.render_handle, ESP_AUDIO_RENDER_SECOND_STREAM, &audio_manager.stream[AUDIO_RENDER_STREAM_PLAYBACK]); + + esp_audio_render_mixer_gain_t feeder_mixer_gain = {0}; + feeder_mixer_gain.initial_gain = audio_feeder.config.feeder_mixer_gain.initial_gain; + feeder_mixer_gain.target_gain = audio_feeder.config.feeder_mixer_gain.target_gain; + feeder_mixer_gain.transition_time = audio_feeder.config.feeder_mixer_gain.transition_time; + esp_audio_render_stream_set_mixer_gain(audio_manager.stream[AUDIO_RENDER_STREAM_FEEDER], &feeder_mixer_gain); + + esp_audio_render_mixer_gain_t playback_mixer_gain = {0}; + playback_mixer_gain.initial_gain = audio_playback.config.playback_mixer_gain.initial_gain; + playback_mixer_gain.target_gain = audio_playback.config.playback_mixer_gain.target_gain; + playback_mixer_gain.transition_time = audio_playback.config.playback_mixer_gain.transition_time; + esp_audio_render_stream_set_mixer_gain(audio_manager.stream[AUDIO_RENDER_STREAM_PLAYBACK], &playback_mixer_gain); + + esp_audio_render_sample_info_t render_sample_info = { + .sample_rate = audio_manager.config.board_sample_rate, + .bits_per_sample = audio_manager.config.board_bits, + .channel = audio_manager.config.board_channels, + }; + esp_audio_render_set_out_sample_info(audio_manager.render_handle, &render_sample_info); + esp_err_t err = esp_audio_render_stream_open(audio_manager.stream[AUDIO_RENDER_STREAM_PLAYBACK], &render_sample_info); + ESP_GMF_RET_ON_NOT_OK(TAG, err, {return err;}, "Failed to open audio render stream"); + err = esp_audio_render_stream_open(audio_manager.stream[AUDIO_RENDER_STREAM_FEEDER], &render_sample_info); + ESP_GMF_RET_ON_NOT_OK(TAG, err, {return err;}, "Failed to open audio render stream"); + + audio_manager.enable_mixer = true; + + return ESP_OK; +} + +esp_err_t audio_processor_mixer_close(void) +{ + if (!audio_manager.enable_mixer) { + ESP_LOGE(TAG, "Audio mixer not enabled"); + return ESP_FAIL; + } + if (audio_manager.stream[AUDIO_RENDER_STREAM_PLAYBACK]) { + esp_audio_render_stream_close(audio_manager.stream[AUDIO_RENDER_STREAM_PLAYBACK]); + } + if (audio_manager.stream[AUDIO_RENDER_STREAM_FEEDER]) { + esp_audio_render_stream_close(audio_manager.stream[AUDIO_RENDER_STREAM_FEEDER]); + } + esp_audio_render_destroy(audio_manager.render_handle); + for (int i = 0; i < AUDIO_RENDER_STREAM_TYPE_MAX; i++) { + esp_audio_render_stream_close(audio_manager.stream[i]); + audio_manager.stream[i] = NULL; + } + audio_manager.render_handle = NULL; + return ESP_OK; +} + +esp_err_t audio_processor_ramp_control(audio_mixer_focus_mode_t mode) +{ + if (!audio_manager.enable_mixer || !audio_manager.render_handle) { + ESP_LOGE(TAG, "Audio render not initialized or mixer not enabled, enable_mixer: %d, render_handle: %p", audio_manager.enable_mixer, audio_manager.render_handle); + return ESP_ERR_INVALID_STATE; + } + if (!audio_manager.stream[AUDIO_RENDER_STREAM_FEEDER] || !audio_manager.stream[AUDIO_RENDER_STREAM_PLAYBACK]) { + ESP_LOGE(TAG, "Audio render streams not available"); + return ESP_ERR_INVALID_STATE; + } + switch (mode) { + case AUDIO_MIXER_FOCUS_FEEDER: + esp_audio_render_stream_set_fade(audio_manager.stream[AUDIO_RENDER_STREAM_FEEDER], true); + esp_audio_render_stream_set_fade(audio_manager.stream[AUDIO_RENDER_STREAM_PLAYBACK], false); + break; + case AUDIO_MIXER_FOCUS_PLAYBACK: + esp_audio_render_stream_set_fade(audio_manager.stream[AUDIO_RENDER_STREAM_PLAYBACK], true); + esp_audio_render_stream_set_fade(audio_manager.stream[AUDIO_RENDER_STREAM_FEEDER], false); + break; + case AUDIO_MIXER_FOCUS_BALANCED: + esp_audio_render_stream_set_fade(audio_manager.stream[AUDIO_RENDER_STREAM_FEEDER], true); + esp_audio_render_stream_set_fade(audio_manager.stream[AUDIO_RENDER_STREAM_PLAYBACK], true); + break; + default: + ESP_LOGE(TAG, "Invalid focus mode: %d", mode); + return ESP_ERR_INVALID_ARG; + } + return ESP_OK; +} diff --git a/firmware/echoear-platform-agent/components/av_processor/av_pipeline_pool.c b/firmware/echoear-platform-agent/components/av_processor/av_pipeline_pool.c new file mode 100644 index 0000000..add2e5c --- /dev/null +++ b/firmware/echoear-platform-agent/components/av_processor/av_pipeline_pool.c @@ -0,0 +1,182 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "esp_err.h" +#include "esp_log.h" +#include "esp_audio_dec_default.h" +#include "esp_audio_enc_default.h" +#include "esp_audio_simple_dec_default.h" +#include "esp_gmf_audio_dec.h" +#include "esp_gmf_audio_enc.h" +#include "esp_gmf_ch_cvt.h" +#include "esp_gmf_bit_cvt.h" +#include "esp_gmf_rate_cvt.h" +#include "esp_gmf_pool.h" +#include "esp_gmf_element.h" +#include "esp_gmf_fade.h" +#include "esp_gmf_err.h" +#include "av_processor_type.h" + +static char *TAG = "AV_PIPELINE_POOL"; + +esp_err_t av_audio_playback_pool_init(esp_gmf_pool_handle_t *pool) +{ + *pool = NULL; + esp_gmf_err_t ret = ESP_GMF_ERR_OK; + esp_gmf_element_handle_t hd = NULL; + esp_err_t err = esp_gmf_pool_init(pool); + ESP_GMF_RET_ON_NOT_OK(TAG, err, {return err;}, "Failed to initialize GMF pool"); + esp_audio_simple_dec_cfg_t es_dec_cfg = DEFAULT_ESP_GMF_AUDIO_DEC_CONFIG(); + esp_gmf_audio_dec_init(&es_dec_cfg, &hd); + esp_gmf_pool_register_element(*pool, hd, NULL); + return ret; +} + +esp_err_t av_audio_playback_pool_deinit(esp_gmf_pool_handle_t pool) +{ + return esp_gmf_pool_deinit(pool); +} + +esp_err_t av_audio_feeder_pool_init(esp_gmf_pool_handle_t *pool, const av_processor_decoder_config_t *decoder_cfg) +{ + *pool = NULL; + esp_err_t ret = ESP_OK; + esp_audio_simple_dec_cfg_t dec_cfg = DEFAULT_ESP_GMF_AUDIO_DEC_CONFIG(); + esp_gmf_element_handle_t el = NULL; + esp_gmf_pool_init(pool); + + // Default to PCM if no config provided + av_processor_format_id_t format = AV_PROCESSOR_FORMAT_ID_PCM; + if (decoder_cfg) { + format = decoder_cfg->format; + } + + switch (format) { + case AV_PROCESSOR_FORMAT_ID_PCM: + dec_cfg.dec_type = ESP_AUDIO_SIMPLE_DEC_TYPE_PCM; + break; + + case AV_PROCESSOR_FORMAT_ID_G711A: { + + dec_cfg.dec_type = ESP_AUDIO_SIMPLE_DEC_TYPE_G711A; + esp_g711_dec_cfg_t g711_dec_cfg = {0}; + g711_dec_cfg.channel = decoder_cfg ? decoder_cfg->params.g711.audio_info.channels : 1; + dec_cfg.dec_cfg = &g711_dec_cfg; + dec_cfg.cfg_size = sizeof(esp_g711_dec_cfg_t); + break; + } + + case AV_PROCESSOR_FORMAT_ID_G711U: { + dec_cfg.dec_type = ESP_AUDIO_SIMPLE_DEC_TYPE_G711U; + esp_g711_dec_cfg_t g711_dec_cfg = {0}; + g711_dec_cfg.channel = decoder_cfg ? decoder_cfg->params.g711.audio_info.channels : 1; + dec_cfg.dec_cfg = &g711_dec_cfg; + dec_cfg.cfg_size = sizeof(esp_g711_dec_cfg_t); + break; + } + + case AV_PROCESSOR_FORMAT_ID_OPUS: { + dec_cfg.dec_type = ESP_AUDIO_SIMPLE_DEC_TYPE_RAW_OPUS; + esp_opus_dec_cfg_t opus_dec_cfg = ESP_OPUS_DEC_CONFIG_DEFAULT(); + if (decoder_cfg) { + // Convert frame_duration from int to esp_opus_enc_frame_duration_t + int frame_duration = decoder_cfg->params.opus.audio_info.frame_duration; + switch (frame_duration) { + case 2: opus_dec_cfg.frame_duration = ESP_OPUS_DEC_FRAME_DURATION_2_5_MS; break; + case 5: opus_dec_cfg.frame_duration = ESP_OPUS_DEC_FRAME_DURATION_5_MS; break; + case 10: opus_dec_cfg.frame_duration = ESP_OPUS_DEC_FRAME_DURATION_10_MS; break; + case 20: opus_dec_cfg.frame_duration = ESP_OPUS_DEC_FRAME_DURATION_20_MS; break; + case 40: opus_dec_cfg.frame_duration = ESP_OPUS_DEC_FRAME_DURATION_40_MS; break; + case 60: opus_dec_cfg.frame_duration = ESP_OPUS_DEC_FRAME_DURATION_60_MS; break; + case 80: opus_dec_cfg.frame_duration = ESP_OPUS_DEC_FRAME_DURATION_80_MS; break; + case 100: opus_dec_cfg.frame_duration = ESP_OPUS_DEC_FRAME_DURATION_100_MS; break; + case 120: opus_dec_cfg.frame_duration = ESP_OPUS_DEC_FRAME_DURATION_120_MS; break; + default: opus_dec_cfg.frame_duration = ESP_OPUS_DEC_FRAME_DURATION_60_MS; break; + } + opus_dec_cfg.channel = decoder_cfg->params.opus.audio_info.channels; + opus_dec_cfg.sample_rate = decoder_cfg->params.opus.audio_info.sample_rate; + } + dec_cfg.dec_cfg = &opus_dec_cfg; + dec_cfg.cfg_size = sizeof(esp_opus_dec_cfg_t); + break; + } + + case AV_PROCESSOR_FORMAT_ID_AAC: + dec_cfg.dec_type = ESP_AUDIO_SIMPLE_DEC_TYPE_AAC; + esp_aac_dec_cfg_t aac_dec_cfg = {0}; + aac_dec_cfg.no_adts_header = decoder_cfg->params.aac.no_adts_header; + aac_dec_cfg.aac_plus_enable = decoder_cfg->params.aac.aac_plus_enable; + dec_cfg.dec_cfg = &aac_dec_cfg; + dec_cfg.cfg_size = sizeof(esp_aac_dec_cfg_t); + break; + + default: + ESP_LOGE(TAG, "Unsupported format: %d", format); + + return ESP_ERR_NOT_SUPPORTED; + } + + esp_gmf_audio_dec_init(&dec_cfg, &el); + esp_gmf_pool_register_element(*pool, el, NULL); + + esp_ae_ch_cvt_cfg_t es_ch_cvt_cfg = DEFAULT_ESP_GMF_CH_CVT_CONFIG(); + ret = esp_gmf_ch_cvt_init(&es_ch_cvt_cfg, &el); + ESP_GMF_RET_ON_ERROR(TAG, ret, return ret, "Failed to init audio ch cvt"); + ret = esp_gmf_pool_register_element(*pool, el, NULL); + ESP_GMF_RET_ON_ERROR(TAG, ret, return ret, "Failed to register element in pool"); + + esp_ae_bit_cvt_cfg_t es_bit_cvt_cfg = DEFAULT_ESP_GMF_BIT_CVT_CONFIG(); + ret = esp_gmf_bit_cvt_init(&es_bit_cvt_cfg, &el); + ESP_GMF_RET_ON_ERROR(TAG, ret, return ret, "Failed to init audio bit cvt"); + ret = esp_gmf_pool_register_element(*pool, el, NULL); + ESP_GMF_RET_ON_ERROR(TAG, ret, return ret, "Failed to register element in pool"); + + esp_ae_rate_cvt_cfg_t es_rate_cvt_cfg = DEFAULT_ESP_GMF_RATE_CVT_CONFIG(); + esp_gmf_rate_cvt_init(&es_rate_cvt_cfg, &el); + esp_gmf_pool_register_element(*pool, el, NULL); + return ret; +} + +esp_err_t av_audio_feeder_pool_deinit(esp_gmf_pool_handle_t pool) +{ + return esp_gmf_pool_deinit(pool); +} + +esp_err_t av_audio_recorder_pool_init(esp_gmf_pool_handle_t *pool, const av_processor_encoder_config_t *encoder_cfg) +{ + *pool = NULL; + esp_gmf_err_t ret = ESP_GMF_ERR_OK; + esp_audio_enc_config_t enc_cfg = DEFAULT_ESP_GMF_AUDIO_ENC_CONFIG(); + esp_gmf_element_handle_t el = NULL; + esp_err_t err = esp_gmf_pool_init(pool); + ESP_GMF_RET_ON_NOT_OK(TAG, err, {return err;}, "Failed to initialize GMF pool"); + + esp_gmf_audio_enc_init(&enc_cfg, &el); + esp_gmf_pool_register_element(*pool, el, NULL); + + esp_ae_ch_cvt_cfg_t es_ch_cvt_cfg = DEFAULT_ESP_GMF_CH_CVT_CONFIG(); + ret = esp_gmf_ch_cvt_init(&es_ch_cvt_cfg, &el); + ESP_GMF_RET_ON_ERROR(TAG, ret, return ret, "Failed to init audio ch cvt"); + ret = esp_gmf_pool_register_element(*pool, el, NULL); + ESP_GMF_RET_ON_ERROR(TAG, ret, return ret, "Failed to register element in pool"); + + esp_ae_bit_cvt_cfg_t es_bit_cvt_cfg = DEFAULT_ESP_GMF_BIT_CVT_CONFIG(); + ret = esp_gmf_bit_cvt_init(&es_bit_cvt_cfg, &el); + ESP_GMF_RET_ON_ERROR(TAG, ret, return ret, "Failed to init audio bit cvt"); + ret = esp_gmf_pool_register_element(*pool, el, NULL); + ESP_GMF_RET_ON_ERROR(TAG, ret, return ret, "Failed to register element in pool"); + + esp_ae_rate_cvt_cfg_t es_rate_cvt_cfg = DEFAULT_ESP_GMF_RATE_CVT_CONFIG(); + esp_gmf_rate_cvt_init(&es_rate_cvt_cfg, &el); + esp_gmf_pool_register_element(*pool, el, NULL); + + return err; +} + +esp_err_t av_audio_recorder_pool_deinit(esp_gmf_pool_handle_t pool) +{ + return esp_gmf_pool_deinit(pool); +} \ No newline at end of file diff --git a/firmware/echoear-platform-agent/components/av_processor/idf_component.yml b/firmware/echoear-platform-agent/components/av_processor/idf_component.yml new file mode 100644 index 0000000..aa36f31 --- /dev/null +++ b/firmware/echoear-platform-agent/components/av_processor/idf_component.yml @@ -0,0 +1,31 @@ +dependencies: + espressif/gmf_ai_audio: + version: '~0.7' + public: true + rules: + - if: "target in [esp32p4, esp32s3, esp32]" + espressif/esp_audio_simple_player: + version: '^0.9.4' + public: true + espressif/gmf_loader: + version: "^0.7" + public: true + espressif/esp_video_codec: + public: true + version: "^0.5" + espressif/esp_capture: + public: true + version: "^0.7" + espressif/esp_audio_render: + public: true + version: "^0.7.0" + +description: Audio/Video Processor module for ESP-ADF +documentation: https://github.com/espressif/esp-adf/blob/master/components/av_processor/README.md +issues: https://github.com/espressif/esp-adf/issues +repository: git://github.com/espressif/esp-adf.git +tags: +- gmf_multimedia +- av_processor +url: https://github.com/espressif/esp-adf/tree/master/components/av_processor +version: 0.1.0 \ No newline at end of file diff --git a/firmware/echoear-platform-agent/components/av_processor/include/audio_processor.h b/firmware/echoear-platform-agent/components/av_processor/include/audio_processor.h new file mode 100644 index 0000000..5426a56 --- /dev/null +++ b/firmware/echoear-platform-agent/components/av_processor/include/audio_processor.h @@ -0,0 +1,488 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include "esp_err.h" +#include "esp_gmf_afe_manager.h" +#include "esp_audio_simple_player_advance.h" +#include "av_processor_type.h" + +/* Brief: Default audio manager configuration + * Note: When use OPUS decoder or OPUS encoder, the recorder_task_config and feeder_task_config + * task stack size need be set to more than 4096 * 10 bytes. + */ +#define DEFAULT_AUDIO_MANAGER_CONFIG() { \ + .play_dev = NULL, \ + .rec_dev = NULL, \ + .mic_layout = {0}, \ + .board_sample_rate = 16000, \ + .board_bits = 32, \ + .board_channels = 2, \ +} + +#define DEFAULT_AUDIO_FEEDER_CONFIG() { \ + .feeder_task_config = { \ + .task_stack = 0, \ + .task_prio = 0, \ + .task_core = 0, \ + .task_stack_in_ext = true, \ + }, \ + .feeder_mixer_gain = { \ + .initial_gain = 0.0, \ + .target_gain = 0.5, \ + .transition_time = 1500 \ + }, \ +} + +#define DEFAULT_AUDIO_PLAYBACK_CONFIG() { \ + .playback_task_config = { \ + .task_stack = 4096, \ + .task_prio = 5, \ + .task_core = 0, \ + .task_stack_in_ext = true, \ + }, \ + .playback_mixer_gain = { \ + .initial_gain = 0.0, \ + .target_gain = 1.0, \ + .transition_time = 1500 \ + } \ +} + +#define DEFAULT_AUDIO_PROMPT_CONFIG() { \ + .prompt_task_config = { \ + .task_stack = 4096, \ + .task_prio = 5, \ + .task_core = 0, \ + .task_stack_in_ext = true \ + } \ +} + +#define DEFAULT_AUDIO_RECORDER_CONFIG() { \ + .afe_feed_task_config = { \ + .task_stack = 40 * 1024, \ + .task_prio = 5, \ + .task_core = 0, \ + .task_stack_in_ext = true, \ + }, \ + .afe_fetch_task_config = { \ + .task_stack = 6 * 1024, \ + .task_prio = 5, \ + .task_core = 1, \ + .task_stack_in_ext = true \ + }, \ + .recorder_task_config = { \ + .task_stack = 4096, \ + .task_prio = 5, \ + .task_core = 0, \ + .task_stack_in_ext = true \ + }, \ + .recorder_event_cb = NULL, \ + .recorder_ctx = NULL, \ +} + +/** + * @brief Default AFE configuration macro + */ +#define DEFAULT_AV_PROCESSOR_AFE_CONFIG() { \ + .model = "model", \ + .ai_mode_wakeup = false, \ + .vad_enable = true, \ + .vad_mode = 4, \ + .vad_min_speech_ms = 64, \ + .vad_min_noise_ms = 1000, \ + .agc_enable = true, \ + .afe_type = AFE_TYPE_SR, \ + .enable_vcmd_detect = false, \ + .vcmd_timeout_ms = 5000, \ + .mn_language = "cn", \ + .wakeup_end_time_ms = 2000, \ + .wakeup_time_ms = 10000 \ +} +/** + * @brief Type definition for the audio recorder event callback function + * + * @param[in] event Pointer to the event data, which can contain information about the recorder's state + * @param[in] ctx User-defined context pointer, passed when registering the callback + */ +typedef void (*recorder_event_callback_t)(void *event, void *ctx); + +/** + * @brief AFE (Audio Front-End) configuration structure + * + * This structure contains all AFE-related configuration parameters that were previously + * configured via Kconfig. These parameters control the behavior of the audio front-end + * processing including wake word detection, VAD, AGC, and other audio processing features. + */ + typedef struct { + const char *model; /*!< Model path/name for SR model initialization */ + bool ai_mode_wakeup; /*!< AI mode: true for WAKEUP mode, false for DIRECT mode */ + bool vad_enable; /*!< Enable Voice Activity Detection (VAD) */ + int vad_mode; /*!< VAD mode value (1-4), larger values are more sensitive to human voice */ + int vad_min_speech_ms; /*!< Minimum speech duration in milliseconds */ + int vad_min_noise_ms; /*!< Minimum noise duration in milliseconds */ + bool agc_enable; /*!< Enable Automatic Gain Control (AGC) */ + afe_type_t afe_type; /*!< AFE type: AFE_TYPE_SR (0) or AFE_TYPE_VC (1) */ + bool enable_vcmd_detect; /*!< Enable Voice Command Detection */ + int vcmd_timeout_ms; /*!< Voice command detection timeout in milliseconds */ + const char *mn_language; /*!< Model language (e.g., "cn", "en") */ + int wakeup_end_time_ms; /*!< Wakeup end time in milliseconds */ + int wakeup_time_ms; /*!< Wakeup time in milliseconds */ +} av_processor_afe_config_t; + +/* Brief: Audio mixer gain configuration */ +typedef struct { + float initial_gain; /*!< Initial gain value (0.0 - 1.0) */ + float target_gain; /*!< Target gain value (0.0 - 1.0) */ + int transition_time; /*!< Transition time in milliseconds */ +} audio_mixer_gain_config_t; + +/* Brief: Common task configuration */ +typedef struct { + int task_stack; /*!< Task stack size (0 means use default) */ + int task_prio; /*!< Task priority */ + int task_core; /*!< Task core ID */ + bool task_stack_in_ext; /*!< Whether to allocate task stack in external memory */ +} audio_task_config_t; + +typedef struct { + audio_task_config_t feeder_task_config; /*!< Feeder task configuration */ + av_processor_decoder_config_t decoder_cfg; /*!< Decoder configuration */ + audio_mixer_gain_config_t feeder_mixer_gain; /*!< Feeder mixer gain configuration */ +} audio_feeder_config_t; + +typedef struct { + audio_task_config_t playback_task_config; /*!< Playback task configuration */ + audio_mixer_gain_config_t playback_mixer_gain; /*!< Playback mixer gain configuration */ +} audio_playback_config_t; + +typedef struct { + audio_task_config_t prompt_task_config; /*!< Prompt task configuration */ +} audio_prompt_config_t; + +typedef struct { + audio_task_config_t afe_feed_task_config; /*!< AFE feed task configuration */ + av_processor_afe_config_t afe_config; /*!< AFE configuration */ + audio_task_config_t afe_fetch_task_config; /*!< AFE fetch task configuration */ + audio_task_config_t recorder_task_config; /*!< Recorder task configuration */ + av_processor_encoder_config_t encoder_cfg; /*!< Encoder configuration */ + recorder_event_callback_t recorder_event_cb; /*!< Recorder event callback */ + void *recorder_ctx; /*!< Recorder context */ +} audio_recorder_config_t; + +/* Brief: Audio manager configuration */ +typedef struct { + void *play_dev; + void *rec_dev; + char mic_layout[8]; + int board_sample_rate; + int board_bits; + int board_channels; +} audio_manager_config_t; + +/** + * @brief Audio focus control mode - determines which audio stream gets priority + */ +typedef enum { + AUDIO_MIXER_FOCUS_FEEDER = 0, /*!< Focus on FEEDER stream - boost feeder, reduce playback */ + AUDIO_MIXER_FOCUS_PLAYBACK = 1, /*!< Focus on PLAYBACK stream - boost playback, reduce feeder */ + AUDIO_MIXER_FOCUS_BALANCED = 2, /*!< Balanced mode - both streams at equal levels */ +} audio_mixer_focus_mode_t; + +enum audio_player_state_e { + AUDIO_RUN_STATE_IDLE, + AUDIO_RUN_STATE_PLAYING, + AUDIO_RUN_STATE_CLOSED, + AUDIO_RUN_STATE_PAUSED, +}; + +/** + * @brief Initializes the audio manager module. + * + * @param[in] config Pointer to the audio manager configuration structure + * + * @return + * - ESP_OK On success + * - Other Appropriate esp_err_t error code on failure + */ +esp_err_t audio_manager_init(audio_manager_config_t *config); + +/** + * @brief Deinitializes the audio manager component + * + * @return + * - ESP_OK On success + * - Other Appropriate esp_err_t error code on failure + */ +esp_err_t audio_manager_deinit(); + +/** + * @brief Opens the audio recorder and registers an event callback + * + * @param[in] config Pointer to the audio recorder configuration structure. + * + * @return + * - ESP_OK On success + * - Other Appropriate esp_err_t error code on failure + */ +esp_err_t audio_recorder_open(audio_recorder_config_t *config); + +/** + * @brief Pauses the audio recorder system + * + * @return + * - ESP_OK On success + * - Other Appropriate esp_err_t error code on failure + */ +esp_err_t audio_recorder_pause(void); + +/** + * @brief Resumes the audio recorder system + * + * @return + * - ESP_OK On success + * - Other Appropriate esp_err_t error code on failure + */ +esp_err_t audio_recorder_resume(void); + +/** + * @brief Get the AFE manager handle of the audio recorder + * + * @param[out] afe_manager_handle Pointer to the AFE manager handle + * + * @return + * - ESP_OK On success + * - Other Appropriate esp_err_t error code on failure + */ +esp_err_t audio_recorder_get_afe_manager_handle(esp_gmf_afe_manager_handle_t *afe_manager_handle); + +/** + * @brief Closes the audio recorder + * + * @return + * - ESP_OK On success + * - Other Appropriate esp_err_t error code on failure + */ +esp_err_t audio_recorder_close(void); + +/** + * @brief Reads audio data from the recorder. + * + * @param[out] data Pointer to the buffer where the audio data will be stored + * @param[in] data_size Size of the buffer to store the audio data. + * + * @return + * - ESP_OK On success + * - Other Appropriate esp_err_t error code on failure + */ +esp_err_t audio_recorder_read_data(uint8_t *data, int data_size); + +/** + * @brief Opens the audio feeder system + * + * @param[in] config Pointer to the audio feeder configuration structure. + * + * @return + * - ESP_OK On success + * - Other Appropriate esp_err_t error code on failure + */ +esp_err_t audio_feeder_open(audio_feeder_config_t *config); + +/** + * @brief Closes the audio feeder module + * + * @return + * - ESP_OK On success + * - Other Appropriate esp_err_t error code on failure + */ +esp_err_t audio_feeder_close(void); + +/** + * @brief Starts the audio feeder operation. + * + * @return + * - ESP_OK On success + * - Other Appropriate esp_err_t error code on failure + */ +esp_err_t audio_feeder_run(void); + +/** + * @brief Stops the ongoing audio feeder + * + * @return + * - ESP_OK On successfully stopping the audio playback + * - Other Appropriate esp_err_t error code on failure + */ +esp_err_t audio_feeder_stop(void); + +/** + * @brief Feeds audio data into the feeder system + * + * @param[in] data Pointer to the audio data to be fed into the feeder system + * @param[in] data_size Size of the audio data to be fed into the feeder system + * + * @return + * - ESP_OK On success + * - Other Appropriate esp_err_t error code on failure + */ +esp_err_t audio_feeder_feed_data(uint8_t *data, int data_size); + +/** + * @brief Open the audio playback module + * + * @param[in] config Pointer to the audio playback configuration structure. + * + * @return + * - ESP_OK On success + * - Other Appropriate esp_err_t error code on failure + */ +esp_err_t audio_playback_open(audio_playback_config_t *config); + +/** + * @brief Close the audio playback module + * + * @return + * - ESP_OK On success + * - Other Appropriate esp_err_t error code on failure + */ +esp_err_t audio_playback_close(void); + +/** + * @brief Play an audio playback from the specified URL. Eg: "http://192.168.1.100:8000/audio.mp3", "file:///sdcard/audio.mp3" + * + * @param[in] url URL pointing to the audio playback file to be played + * + * @return + * - ESP_OK On success + * - Other Appropriate esp_err_t error code on failure + */ +esp_err_t audio_playback_play(const char *url); + +/** + * @brief Wait for the audio playback to end. This function will block until the audio playback is ended. + * + * @param[in] url URL pointing to the audio playback file to be played + * + * @return + * - ESP_OK On success + * - Other Appropriate esp_err_t error code on failure + */ +esp_err_t audio_playback_play_wait_end(const char *url); + +/** + * @brief Pauses the audio playback module + * + * @return + * - ESP_OK On success + * - Other Appropriate esp_err_t error code on failure + */ +esp_err_t audio_playback_pause(void); +/** + * @brief Resumes the audio playback module + * + * @return + * - ESP_OK On success + * - Other Appropriate esp_err_t error code on failure + */ +esp_err_t audio_playback_resume(void); + +/** + * @brief Get the state of the audio playback module + * + * @return + * - enum audio_player_state_e Playback state + */ +enum audio_player_state_e audio_playback_get_state(void); + +/** + * @brief Stop the currently playing audio playback module + * + * @return + * - ESP_OK On success + * - Other Appropriate esp_err_t error code on failure + */ +esp_err_t audio_playback_stop(void); + +/** + * @brief Open the audio prompt module + * @note This function not provide pause and resume functionality, because the prompt is played to end automatically. + * This function's priority is higher than the audio playback and feeder, so when the prompt is playing, the audio playback + * and feeder decoded data will be ignored. + * + * @param[in] config Pointer to the audio prompt configuration structure. + * + * @return + * - ESP_OK On success + * - Other Appropriate esp_err_t error code on failure + */ + esp_err_t audio_prompt_open(audio_prompt_config_t *config); + + /** + * @brief Close the audio prompt module + * + * @return + * - ESP_OK On success + * - Other Appropriate esp_err_t error code on failure + */ + esp_err_t audio_prompt_close(void); + + /** + * @brief Play an audio prompt from the specified URL. + * + * @param[in] url URL pointing to the audio prompt file to be played + * @note Audio prompt is not supported mixer, so when play prompt, the audio playback and feeder decoded data will be ignored. + * + * @return + * - ESP_OK On success + * - Other Appropriate esp_err_t error code on failure + */ + esp_err_t audio_prompt_play(const char *url); + +/** + * @brief Open the audio mixer module + * + * @note This function must be called after audio_playback_open and audio_feeder_open and the audio_mixer must be enabled + * @return + * - ESP_OK On success + * - Other Appropriate esp_err_t error code on failure + */ +esp_err_t audio_processor_mixer_open(void); + +/** + * @brief Close the audio mixer module + * + * @return + * - ESP_OK On success + * - Other Appropriate esp_err_t error code on failure + */ +esp_err_t audio_processor_mixer_close(void); + +/** + * @brief Control audio focus between FEEDER and PLAYBACK streams + * + * @note This function controls the fade processors for both feeder and playback streams + * to create smooth volume transitions and focus on the desired audio source. + * + * @param[in] mode The focus control mode: + * - AUDIO_FOCUS_FEEDER: Focus on FEEDER stream (boost feeder, reduce playback) + * - AUDIO_FOCUS_PLAYBACK: Focus on PLAYBACK stream (boost playback, reduce feeder) + * - AUDIO_FOCUS_BALANCED: Balanced mode (both streams at equal levels) + * + * @return + * - ESP_OK On success + * - ESP_ERR_INVALID_ARG Invalid mode parameter + * - ESP_ERR_INVALID_STATE Audio render not initialized or streams not available + * - ESP_FAIL Failed to control fade processors + */ +esp_err_t audio_processor_ramp_control(audio_mixer_focus_mode_t mode); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/av_processor/include/av_processor_type.h b/firmware/echoear-platform-agent/components/av_processor/include/av_processor_type.h new file mode 100644 index 0000000..3c87ae2 --- /dev/null +++ b/firmware/echoear-platform-agent/components/av_processor/include/av_processor_type.h @@ -0,0 +1,145 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include +#include +#include "esp_afe_config.h" + +/** + * @brief AV Processor FourCC definition + */ +#define AV_PROCESSOR_FOURCC(a, b, c, d) ((uint32_t)(a) | ((uint32_t)(b) << 8) | ((uint32_t)(c) << 16) | ((uint32_t)(d) << 24)) + +/** + * @brief AV Processor format ID definition + * + * @note Aligned with GMF FourCC definition for audio video codecs and formats + * Detailed info refer to `https://github.com/espressif/esp-gmf/blob/main/gmf_core/helpers/include/esp_fourcc.h` + */ +typedef enum { + AV_PROCESSOR_FORMAT_ID_PCM = AV_PROCESSOR_FOURCC('P', 'C', 'M', ' '), /*!< PCM (Uncompressed) encoder */ + AV_PROCESSOR_FORMAT_ID_G711A = AV_PROCESSOR_FOURCC('A', 'L', 'A', 'U'), /*!< G.711 A-law encoder */ + AV_PROCESSOR_FORMAT_ID_G711U = AV_PROCESSOR_FOURCC('U', 'L', 'A', 'W'), /*!< G.711 μ-law encoder */ + AV_PROCESSOR_FORMAT_ID_OPUS = AV_PROCESSOR_FOURCC('O', 'P', 'U', 'S'), /*!< OPUS encoder */ + AV_PROCESSOR_FORMAT_ID_AAC = AV_PROCESSOR_FOURCC('A', 'A', 'C', ' '), /*!< AAC encoder */ +} av_processor_format_id_t; + +/** + * @brief Audio encoder format type + */ +typedef enum { + AV_PROCESSOR_ENCODER_PCM = 0, /*!< PCM (Uncompressed) encoder */ + AV_PROCESSOR_ENCODER_G711A, /*!< G.711 A-law encoder */ + AV_PROCESSOR_ENCODER_G711U, /*!< G.711 μ-law encoder */ + AV_PROCESSOR_ENCODER_OPUS, /*!< OPUS encoder */ + AV_PROCESSOR_ENCODER_MAX, /*!< Maximum encoder type */ +} av_processor_encoder_format_t; + +/** + * @brief Audio decoder format type + */ +typedef enum { + AV_PROCESSOR_DECODER_PCM = 0, /*!< PCM (Uncompressed) decoder */ + AV_PROCESSOR_DECODER_G711A, /*!< G.711 A-law decoder */ + AV_PROCESSOR_DECODER_G711U, /*!< G.711 μ-law decoder */ + AV_PROCESSOR_DECODER_OPUS, /*!< OPUS decoder */ + AV_PROCESSOR_DECODER_AAC, /*!< AAC decoder */ + AV_PROCESSOR_DECODER_MAX, /*!< Maximum decoder type */ +} av_processor_decoder_format_t; + +typedef struct { + uint32_t sample_rate; /*!< Sample rate in Hz (e.g., 8000, 16000, 24000, 32000, 44100, 48000) */ + uint8_t sample_bits; /*!< Sample bits in bits (e.g., 8, 16, 24, 32) */ + uint8_t channels; /*!< Number of audio channels (1-4) */ + int frame_duration; /*!< Frame duration in milliseconds */ +} av_processor_audio_info_t; + +/** + * @brief PCM encoder/decoder configuration + */ +typedef struct { + av_processor_audio_info_t audio_info; +} av_processor_pcm_config_t; + +/** + * @brief OPUS encoder configuration + */ +typedef struct { + av_processor_audio_info_t audio_info; + bool enable_vbr; /*!< Enable Variable Bit Rate (VBR) */ + int bitrate; /*!< Bitrate in bps */ +} av_processor_opus_encoder_config_t; + +/** + * @brief OPUS decoder configuration + */ +typedef struct { + av_processor_audio_info_t audio_info; +} av_processor_opus_decoder_config_t; + +/** + * @brief AAC decoder configuration + */ +typedef struct { + av_processor_audio_info_t audio_info; + bool no_adts_header; /*!< Set if AAC frame data not contain ADTS header */ + bool aac_plus_enable; /*!< Set if need AAC-Plus support */ +} av_processor_aac_decoder_config_t; + +/** + * @brief G.711 encoder/decoder configuration + */ +typedef struct { + av_processor_audio_info_t audio_info; +} av_processor_g711_config_t; + +typedef struct { + av_processor_audio_info_t audio_info; + int bitrate; /*!< Bitrate in bps */ + bool adts_used; /*!< Whether write ADTS header: true - add ADTS header, false - raw aac data only */ +} av_processor_aac_encoder_config_t; + +/** + * @brief Audio encoder configuration structure + * + * This structure contains the encoder format type and corresponding format-specific parameters. + * Only the parameters for the selected format (indicated by format) should be used. + */ +typedef struct { + av_processor_format_id_t format; /*!< Encoder format ID */ + union { + av_processor_pcm_config_t pcm; /*!< PCM encoder configuration (used when format == AV_PROCESSOR_FORMAT_ID_PCM) */ + av_processor_opus_encoder_config_t opus; /*!< OPUS encoder configuration (used when format == AV_PROCESSOR_FORMAT_ID_OPUS) */ + av_processor_g711_config_t g711; /*!< G.711 encoder configuration (used when format == AV_PROCESSOR_FORMAT_ID_G711A or AV_PROCESSOR_FORMAT_ID_G711U) */ + av_processor_aac_encoder_config_t aac; /*!< AAC encoder configuration (used when format == AV_PROCESSOR_FORMAT_ID_AAC) */ + } params; /*!< Format-specific encoder parameters */ +} av_processor_encoder_config_t; + +/** + * @brief Audio decoder configuration structure + * + * This structure contains the decoder format type and corresponding format-specific parameters. + * Only the parameters for the selected format (indicated by format) should be used. + */ +typedef struct { + av_processor_format_id_t format; /*!< Decoder format ID */ + union { + av_processor_pcm_config_t pcm; /*!< PCM decoder configuration (used when format == AV_PROCESSOR_FORMAT_ID_PCM) */ + av_processor_opus_decoder_config_t opus; /*!< OPUS decoder configuration (used when format == AV_PROCESSOR_FORMAT_ID_OPUS) */ + av_processor_g711_config_t g711; /*!< G.711 decoder configuration (used when format == AV_PROCESSOR_FORMAT_ID_G711A or AV_PROCESSOR_FORMAT_ID_G711U) */ + av_processor_aac_decoder_config_t aac; /*!< AAC decoder configuration (used when format == AV_PROCESSOR_FORMAT_ID_AAC) */ + } params; /*!< Format-specific decoder parameters */ +} av_processor_decoder_config_t; + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/av_processor/include/video_processor.h b/firmware/echoear-platform-agent/components/av_processor/include/video_processor.h new file mode 100644 index 0000000..d94ef8c --- /dev/null +++ b/firmware/echoear-platform-agent/components/av_processor/include/video_processor.h @@ -0,0 +1,174 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include "esp_capture_sink.h" +#include "esp_err.h" +#include "esp_video_dec.h" + +/** + * @brief Default maximum number of video paths + */ + #define DEFAULT_VID_MAX_PATH 2 + +/** + * @brief Video capture handle type + */ +typedef void *video_capture_handle_t; + +/** + * @brief Video render handle type + */ +typedef void *video_render_handle_t; + +/** + * @brief Video frame structure + */ +typedef struct { + uint8_t *data; /*!< Frame data buffer */ + size_t size; /*!< Frame data size */ +} video_frame_t; + +/** + * @brief Video render decode callback function type + * + * @param[in] ctx User context pointer + * @param[in] data Decoded frame data + * @param[in] size Size of decoded frame data + */ +typedef void (*video_render_decode_callback_t)(void *ctx, const uint8_t *data, size_t size); + +/** + * @brief Video capture frame callback function type + * + * @param[in] ctx User context pointer + * @param[in] index Frame index + * @param[in] vid_frame Captured video frame + */ +typedef void (*video_capture_frame_callback_t)(void *ctx, int index, video_frame_t *vid_frame); + +/** + * @brief Video render configuration structure + */ +typedef struct { + esp_video_dec_cfg_t decode_cfg; /*!< Video decoder configuration */ + esp_video_codec_resolution_t resolution; /*!< Video resolution */ + video_render_decode_callback_t decode_cb; /*!< Decode callback function */ + void *decode_cb_ctx; /*!< Decode callback context */ +} video_render_config_t; + +/** + * @brief Video capture configuration structure + */ +typedef struct { + void *camera_config; /*!< Camera configuration */ + int sink_num; /*!< Number of sinks */ + esp_capture_sink_cfg_t sink_cfg[DEFAULT_VID_MAX_PATH]; /*!< Sink configurations */ + video_capture_frame_callback_t capture_frame_cb; /*!< Capture frame callback function */ + void *capture_frame_cb_ctx; /*!< Capture frame callback context */ +} video_capture_config_t; + +/** + * @brief Opens a video capture session + * + * @param[in] config Video capture configuration + * + * @return + * - video_capture_handle On success + * - NULL On failure + */ +video_capture_handle_t video_capture_open(video_capture_config_t *config); + +/** + * @brief Closes a video capture session + * + * @param[in] handle Video capture handle + */ +void video_capture_close(video_capture_handle_t handle); + +/** + * @brief Starts video capture + * + * @param[in] handle Video capture handle + * + * @return + * - ESP_OK On success + * - Other Appropriate esp_err_t error code on failure + */ +esp_err_t video_capture_start(video_capture_handle_t handle); + +/** + * @brief Stops video capture + * + * @param[in] handle Video capture handle + * + * @return + * - ESP_OK On success + * - Other Appropriate esp_err_t error code on failure + */ +esp_err_t video_capture_stop(video_capture_handle_t handle); + +/** + * @brief Opens a video render session + * + * @param[in] config Video render configuration + * + * @return + * - video_render_handle On success + * - NULL On failure + */ +video_render_handle_t video_render_open(video_render_config_t *config); + +/** + * @brief Closes a video render session + * + * @param[in] handle Video render handle + */ +void video_render_close(video_render_handle_t handle); + +/** + * @brief Starts video rendering + * + * @param[in] handle Video render handle + * + * @return + * - ESP_OK On success + * - Other Appropriate esp_err_t error code on failure + */ +esp_err_t video_render_start(video_render_handle_t handle); + +/** + * @brief Stops video rendering + * + * @param[in] handle Video render handle + * + * @return + * - ESP_OK On success + * - Other Appropriate esp_err_t error code on failure + */ +esp_err_t video_render_stop(video_render_handle_t handle); + +/** + * @brief Feeds a video frame to the render pipeline + * + * @param[in] handle Video render handle + * @param[in] frame Video frame to feed + * + * @return + * - ESP_OK On success + * - Other Appropriate esp_err_t error code on failure + */ +esp_err_t video_render_frame_feed(video_render_handle_t handle, video_frame_t *frame); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/av_processor/media_dump.c b/firmware/echoear-platform-agent/components/av_processor/media_dump.c new file mode 100644 index 0000000..c86b611 --- /dev/null +++ b/firmware/echoear-platform-agent/components/av_processor/media_dump.c @@ -0,0 +1,224 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "esp_log.h" +#include "esp_timer.h" +#include "sdkconfig.h" + +#include "media_dump.h" + +#if CONFIG_MEDIA_DUMP_SINK_UDP +#include "lwip/inet.h" +#include "lwip/sockets.h" +#endif /* CONFIG_MEDIA_DUMP_SINK_UDP */ + +#if CONFIG_MEDIA_DUMP_ENABLE + +static const char *TAG = "MEDIA_DUMP"; + +typedef struct { + bool opened; + int64_t start_time_us; + int64_t max_duration_us; + bool expired; + bool notified; +#if CONFIG_MEDIA_DUMP_SINK_SDCARD + FILE *fp; +#endif /* CONFIG_MEDIA_DUMP_SINK_SDCARD */ +#if CONFIG_MEDIA_DUMP_SINK_UDP + int sock; + struct sockaddr_in dest; +#endif /* CONFIG_MEDIA_DUMP_SINK_UDP */ +} media_dump_ctx_t; + +static media_dump_ctx_t s_ctx = {0}; + +static inline bool media_dump_time_exceeded(void) +{ + media_dump_ctx_t *ctx = &s_ctx; + if (!ctx->opened) { + return false; + } + int64_t now = esp_timer_get_time(); + if ((now - ctx->start_time_us) >= ctx->max_duration_us) { + ctx->expired = true; + if (!ctx->notified) { + ESP_LOGI(TAG, "Dump duration reached (%ds)", (int)CONFIG_MEDIA_DUMP_DURATION_SEC); + ctx->notified = true; + } + return true; + } + return false; +} + +static void media_dump_release(media_dump_ctx_t *ctx) +{ +#if CONFIG_MEDIA_DUMP_SINK_SDCARD + if (ctx->fp) { + fflush(ctx->fp); + fclose(ctx->fp); + ctx->fp = NULL; + } +#endif /* CONFIG_MEDIA_DUMP_SINK_SDCARD */ +#if CONFIG_MEDIA_DUMP_SINK_UDP + if (ctx->sock >= 0) { + close(ctx->sock); + ctx->sock = -1; + } +#endif /* CONFIG_MEDIA_DUMP_SINK_UDP */ + ctx->opened = false; +} + +int media_dump_open(const char *filename) +{ + media_dump_ctx_t *ctx = &s_ctx; + /* allow re-open anytime: close existing and start a new session */ + if (ctx->opened) { + media_dump_release(ctx); + } + /* reset state and start fresh */ + memset(ctx, 0, sizeof(*ctx)); + ctx->max_duration_us = (int64_t)CONFIG_MEDIA_DUMP_DURATION_SEC * 1000000LL; + ctx->start_time_us = esp_timer_get_time(); + +#if CONFIG_MEDIA_DUMP_SINK_SDCARD + const char *path = NULL; + if (filename) { + path = filename; + } else { + path = CONFIG_MEDIA_DUMP_SDCARD_FILENAME; + } + ctx->fp = fopen(path, "wb"); + if (!ctx->fp) { + ESP_LOGE(TAG, "Dump open file failed: %s, error: %s", path, strerror(errno)); + return -1; + } + ESP_LOGI(TAG, "Dump file: %s, %ds", path, CONFIG_MEDIA_DUMP_DURATION_SEC); +#endif /* CONFIG_MEDIA_DUMP_SINK_SDCARD */ + +#if CONFIG_MEDIA_DUMP_SINK_UDP + ctx->sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); + if (ctx->sock < 0) { + ESP_LOGE(TAG, "Dump create UDP socket failed"); + return -1; + } + memset(&ctx->dest, 0, sizeof(ctx->dest)); + ctx->dest.sin_family = AF_INET; + ctx->dest.sin_port = htons(CONFIG_MEDIA_DUMP_UDP_PORT); + ctx->dest.sin_addr.s_addr = inet_addr(CONFIG_MEDIA_DUMP_UDP_IP); + ESP_LOGI(TAG, "Dump UDP %s:%d, %ds", CONFIG_MEDIA_DUMP_UDP_IP, + CONFIG_MEDIA_DUMP_UDP_PORT, CONFIG_MEDIA_DUMP_DURATION_SEC); +#endif /* CONFIG_MEDIA_DUMP_SINK_UDP */ + + ctx->opened = true; + return 0; +} + +int media_dump_write(const uint8_t *data, size_t size) +{ + media_dump_ctx_t *ctx = &s_ctx; + if (!ctx->opened || ctx->expired || !data || size == 0) { + return -1; + } + if (media_dump_time_exceeded()) { + ESP_LOGW(TAG, "Dump duration reached, ignore write"); + return 0; + } + +#if CONFIG_MEDIA_DUMP_SINK_SDCARD + if (ctx->fp) { + size_t w = fwrite(data, 1, size, ctx->fp); + if (w != size) { + ESP_LOGW(TAG, "Dump short file write: %u/%u", (unsigned)w, (unsigned)size); + } + return (int)w; + } +#endif /* CONFIG_MEDIA_DUMP_SINK_SDCARD */ + +#if CONFIG_MEDIA_DUMP_SINK_UDP + if (ctx->sock >= 0) { + int sent = sendto(ctx->sock, data, size, 0, (struct sockaddr *)&ctx->dest, sizeof(ctx->dest)); + if (sent < 0) { + ESP_LOGW(TAG, "Dump UDP send failed"); + return -1; + } + return sent; + } +#endif /* CONFIG_MEDIA_DUMP_SINK_UDP */ + return -1; +} + +int media_dump_close(void) +{ + media_dump_ctx_t *ctx = &s_ctx; + if (!ctx->opened) { + return 0; + } + /* normal close: always release immediately */ + media_dump_release(ctx); + return 0; +} + +int media_dump_feed(const char *filename, const uint8_t *data, size_t size) +{ + media_dump_ctx_t *ctx = &s_ctx; + /* feed mode: maintain a single open session across calls */ + if (ctx->expired) { + return 0; + } + if (!ctx->opened) { + if (media_dump_open(filename) != 0) { + return -1; + } + } + ESP_LOGI(TAG, "Dump duration not reached, write now"); + int ret = media_dump_write(data, size); + /* If duration reached during this write, close now */ + if (ctx->expired && ctx->opened) { + (void)media_dump_close(); + ESP_LOGI(TAG, "Dump duration reached, close now"); + } + return ret; +} + +#else /* !CONFIG_MEDIA_DUMP_ENABLE */ + +int media_dump_open(const char *filename) +{ + return 0; +} + +int media_dump_write(const uint8_t *data, size_t size) +{ + (void)data; + (void)size; + return 0; +} + +int media_dump_close(void) +{ + return 0; +} + +int media_dump_feed(const char *filename, const uint8_t *data, size_t size) +{ + (void)filename; + (void)data; + (void)size; + return 0; +} + +#endif /* CONFIG_MEDIA_DUMP_ENABLE */ diff --git a/firmware/echoear-platform-agent/components/av_processor/private_include/av_pipeline_pool.h b/firmware/echoear-platform-agent/components/av_processor/private_include/av_pipeline_pool.h new file mode 100644 index 0000000..f2b7c6b --- /dev/null +++ b/firmware/echoear-platform-agent/components/av_processor/private_include/av_pipeline_pool.h @@ -0,0 +1,101 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#include "esp_err.h" +#include "esp_gmf_pool.h" +#include "av_processor_type.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * @brief Initialize audio playback pool + * + * Creates and initializes a memory pool for audio playback operations. + * This pool manages memory for audio data that is being played back from + * various sources (files, streams, etc.). + * + * @param pool Pointer to store the created pool handle + * @return + * - ESP_OK Pool initialized successfully + * - ESP_ERR_NO_MEM Insufficient memory to create pool + * - ESP_ERR_INVALID_ARG Invalid parameter provided + */ +esp_err_t av_audio_playback_pool_init(esp_gmf_pool_handle_t *pool); + +/** + * @brief Deinitialize audio playback pool + * + * Destroys the audio playback pool and frees all associated memory. + * + * @param pool Handle of the pool to deinitialize + * @return + * - ESP_OK Pool deinitialized successfully + * - ESP_ERR_INVALID_ARG Invalid pool handle + */ +esp_err_t av_audio_playback_pool_deinit(esp_gmf_pool_handle_t pool); + +/** + * @brief Initialize audio recorder pool + * + * @param pool Pointer to store the created pool handle + * @param encoder_cfg Pointer to encoder configuration. If NULL, default PCM configuration will be used. + * @return + * - ESP_OK Pool initialized successfully + * - ESP_ERR_NO_MEM Insufficient memory to create pool + * - ESP_ERR_INVALID_ARG Invalid parameter provided + */ +esp_err_t av_audio_recorder_pool_init(esp_gmf_pool_handle_t *pool, const av_processor_encoder_config_t *encoder_cfg); + +/** + * @brief Deinitialize audio recorder pool + * + * Destroys the audio recorder pool and frees all associated memory. + * Make sure encoder is not running or else may cause memory violation error + * + * @param pool Handle of the pool to deinitialize + * @return + * - ESP_OK Pool deinitialized successfully + * - ESP_ERR_INVALID_ARG Invalid pool handle + */ +esp_err_t av_audio_recorder_pool_deinit(esp_gmf_pool_handle_t pool); + +/** + * @brief Initialize audio feeder pool + * + * Creates and initializes a memory pool for audio feeder operations. + * This pool manages memory for audio data that is being fed into + * various processing pipelines or output streams. + * + * @param pool Pointer to store the created pool handle + * @param decoder_cfg Pointer to decoder configuration. If NULL, default PCM configuration will be used. + * @return + * - ESP_OK Pool initialized successfully + * - ESP_ERR_NO_MEM Insufficient memory to create pool + * - ESP_ERR_INVALID_ARG Invalid parameter provided + */ +esp_err_t av_audio_feeder_pool_init(esp_gmf_pool_handle_t *pool, const av_processor_decoder_config_t *decoder_cfg); + +/** + * @brief Deinitialize audio feeder pool + * + * Destroys the audio feeder pool and frees all associated memory. + * Make sure decoder is not running or else may cause memory violation error + * + * @param pool Handle of the pool to deinitialize + * @return + * - ESP_OK Pool deinitialized successfully + * - ESP_ERR_INVALID_ARG Invalid pool handle + */ +esp_err_t av_audio_feeder_pool_deinit(esp_gmf_pool_handle_t pool); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/av_processor/private_include/media_dump.h b/firmware/echoear-platform-agent/components/av_processor/private_include/media_dump.h new file mode 100644 index 0000000..ef4e1c8 --- /dev/null +++ b/firmware/echoear-platform-agent/components/av_processor/private_include/media_dump.h @@ -0,0 +1,84 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +/** + * @brief Open a media dump session + * + * When enabled by Kconfig, subsequent writes are dumped to the selected sink. + * If a session is already open, it will be closed and a new one started. + * The session duration is limited by `CONFIG_MEDIA_DUMP_DURATION_SEC`; after + * the timeout is reached, additional writes are not accepted. + * + * Sinks: + * - SDCARD: write to `CONFIG_MEDIA_DUMP_SDCARD_FILENAME` + * - UDP: send to `CONFIG_MEDIA_DUMP_UDP_ADDRESS` (format: IP:Port, e.g., "192.168.1.100:5000") + * + * @param[in] filename Optional filename for the dump file + * - If NULL, use `CONFIG_MEDIA_DUMP_SDCARD_FILENAME` + * - If not NULL, use the filename as the dump file name + * @return + * - 0: Session opened successfully + * - -1: Failed to open (e.g., file/socket creation failure) + */ +int media_dump_open(const char *filename); + +/** + * @brief Write media data to the dump sink + * + * Effective only when a session is open and not expired. + * + * @param[in] data Pointer to the data buffer to write + * @param[in] size Number of bytes in the buffer + * @return + * - >0: Bytes actually written/sent (depends on sink) + * - 0: Maximum duration reached, this write is ignored + * - -1: Session not open, already expired, or parameters invalid + */ +int media_dump_write(const uint8_t *data, size_t size); + +/** + * @brief Close the current media dump session and release resources + * + * Calling multiple times is safe; if no session is open, it returns immediately. + * + * @return + * - 0: Always returns 0 + */ +int media_dump_close(void); + +/** + * @brief Feed data with auto open/close behavior + * + * If no session is open, media_dump_open() is called automatically. After the + * write, if the session duration has been reached, the session is closed + * automatically. If the session is already expired, this function returns 0. + + * If want to call this function, can use `extern int media_dump_feed(const char *filename, const uint8_t *data, size_t size);` in the file. + * + * @param[in] filename Optional filename for the dump file + * - If NULL, use `CONFIG_MEDIA_DUMP_SDCARD_FILENAME` + * - If not NULL, use the filename as the dump file name + * @param[in] data Pointer to the data buffer to write + * @param[in] size Number of bytes in the buffer + * @return + * - Same return semantics as media_dump_write() + * - -1: Auto open failed + */ +int media_dump_feed(const char *filename, const uint8_t *data, size_t size); + +#ifdef __cplusplus +} +#endif diff --git a/firmware/echoear-platform-agent/components/av_processor/script/move_av_processor.py b/firmware/echoear-platform-agent/components/av_processor/script/move_av_processor.py new file mode 100644 index 0000000..fe3a515 --- /dev/null +++ b/firmware/echoear-platform-agent/components/av_processor/script/move_av_processor.py @@ -0,0 +1,107 @@ +#!/usr/bin/env python3 +import os +import shutil +import sys +import logging +from pathlib import Path + + +def main() -> int: + logging.basicConfig( + level=logging.INFO, + format="%(levelname)s: %(message)s", + ) + # Compute source directory (parent of this script's directory, i.e. espressif__av_processor) + script_path = Path(__file__).resolve() + av_processor_dir = script_path.parent.parent + + if av_processor_dir.name != "espressif__av_processor": + logging.error("Failed to resolve source directory. Expected 'espressif__av_processor', got: %s", av_processor_dir) + return 1 + + # Ensure source espressif__av_processor is under managed_components + if av_processor_dir.parent.name != "managed_components": + logging.error("This script must be run when espressif__av_processor is under 'managed_components'. Current path: %s", av_processor_dir) + return 1 + + # Read PROJECT_DIR environment variable + project_dir_env = os.environ.get("PROJECT_DIR") + if not project_dir_env: + logging.error("PROJECT_DIR is not set. Example: export PROJECT_DIR=/path/to/project") + return 1 + + project_dir = Path(project_dir_env).resolve() + if not project_dir.exists() or not project_dir.is_dir(): + logging.error("PROJECT_DIR does not exist or is not a directory: %s", project_dir) + return 1 + + # Target components directory + components_dir = project_dir / "components" + try: + components_dir.mkdir(parents=True, exist_ok=True) + except Exception as e: + logging.error("Failed to create target directory: %s -> %s", components_dir, e) + return 1 + + # Destination espressif__av_processor path + dest_dir = components_dir / "espressif__av_processor" + + # If destination exists, remove it before copying + if dest_dir.exists(): + logging.info("Destination exists, removing: %s", dest_dir) + try: + shutil.rmtree(dest_dir) + except Exception as e: + logging.error("Failed to remove existing destination: %s -> %s", dest_dir, e) + return 1 + + # Perform copy + logging.info("Copying %s -> %s", av_processor_dir, dest_dir) + try: + shutil.copytree(av_processor_dir, dest_dir) + except Exception as e: + logging.error("Copy failed: %s", e) + return 1 + + # Switch to a safe working directory to avoid deleting the running/current dir + try: + os.chdir(project_dir) + except Exception as e: + logging.error("Failed to change working directory: %s -> %s", project_dir, e) + return 1 + + # Remove source directory (parent av_processor) + logging.info("Removing source directory: %s", av_processor_dir) + try: + shutil.rmtree(av_processor_dir) + except Exception as e: + logging.error("Failed to remove source directory: %s -> %s", av_processor_dir, e) + logging.info("Copy completed, but source removal failed. Please clean up manually.") + return 1 + + # Update PROJECT_DIR/main/idf_component.yml to remove 'espressif/av_processor' + idf_component_path = project_dir / "main" / "idf_component.yml" + if idf_component_path.exists(): + try: + content = idf_component_path.read_text(encoding="utf-8") + lines = content.splitlines() + new_lines = [line for line in lines if "espressif/av_processor" not in line] + if len(new_lines) != len(lines): + idf_component_path.write_text("\n".join(new_lines) + ("\n" if content.endswith("\n") else ""), encoding="utf-8") + logging.info("Removed 'espressif/av_processor' from %s", idf_component_path) + else: + logging.info("No 'espressif/av_processor' entry found in %s", idf_component_path) + except Exception as e: + logging.error("Failed to update %s: %s", idf_component_path, e) + return 1 + else: + logging.info("idf_component.yml not found at %s, skipping update", idf_component_path) + + logging.info("Copied espressif__av_processor to PROJECT_DIR/components, removed original directory, and updated idf_component.yml.") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) + + diff --git a/firmware/echoear-platform-agent/components/av_processor/script/udp_reciver.py b/firmware/echoear-platform-agent/components/av_processor/script/udp_reciver.py new file mode 100644 index 0000000..141550f --- /dev/null +++ b/firmware/echoear-platform-agent/components/av_processor/script/udp_reciver.py @@ -0,0 +1,93 @@ +# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD +# SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT +# +# See LICENSE file for details. + +#!/usr/bin/env python3 +import argparse +import socket +import sys +from pathlib import Path +import os +from datetime import datetime +import logging + +def get_primary_ip(): + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + try: + s.connect(("8.8.8.8", 80)) + return s.getsockname()[0] + except Exception: + return "127.0.0.1" + finally: + s.close() + +def gen_filename(): + ts = datetime.now().strftime("%Y%m%d%H%M%S") + return f"media_dump_{ts}.bin" + +BUF_SIZE = 10240 + +def main(): + logging.basicConfig(level=logging.INFO, format="[%(levelname)s] %(message)s") + parser = argparse.ArgumentParser(description="Receive UDP media dump and save to file") + parser.add_argument("--port", type=int, default=5000, help="UDP port (default: 5000)") + args = parser.parse_args() + + primary_ip = get_primary_ip() + logging.info(f"Host primary IP: {primary_ip}, port: {args.port} (set MEDIA_DUMP_UDP_IP:PORT to this)") + + out_path = Path(gen_filename()) + out_path.parent.mkdir(parents=True, exist_ok=True) + + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + try: + sock.bind(("0.0.0.0", args.port)) + except OSError as e: + logging.error(f"Bind failed on 0.0.0.0:{args.port} - {e}") + sys.exit(1) + + logging.info(f"Listening on 0.0.0.0:{args.port}, output file: {out_path.name} (created on first packet), buf={BUF_SIZE}") + total = 0 + f = None + try: + while True: + try: + data, addr = sock.recvfrom(BUF_SIZE) + except OSError: + break + if not data: + logging.info("\nClose received, finishing and exiting") + break + if f is None: + f = open(out_path, "wb") + logging.info(f"Receiving... writing to {out_path.resolve()}") + f.write(data) + total += len(data) + sys.stdout.write(f"\r[INFO] recv={len(data)} bytes, total={total} bytes") + sys.stdout.flush() + except KeyboardInterrupt: + pass + finally: + sock.close() + # ensure the progress line ends with newline + try: + sys.stdout.write("\n") + sys.stdout.flush() + except Exception: + pass + try: + if f is not None: + f.flush() + os.fsync(f.fileno()) + f.close() + logging.info(f"Saved file: {out_path.resolve()}") + else: + logging.info("No packets received, no file saved") + except Exception as e: + logging.error(f"Finalize file failed: {e}") + logging.info(f"Done. Total bytes received: {total}") + sys.exit(0) + +if __name__ == "__main__": + main() diff --git a/firmware/echoear-platform-agent/components/av_processor/video_processor.c b/firmware/echoear-platform-agent/components/av_processor/video_processor.c new file mode 100644 index 0000000..3466489 --- /dev/null +++ b/firmware/echoear-platform-agent/components/av_processor/video_processor.c @@ -0,0 +1,470 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/queue.h" + +#include "esp_log.h" +#include "driver/gpio.h" +#include "esp_gmf_oal_thread.h" +#include "esp_gmf_oal_mem.h" +#include "esp_video_dec.h" +#include "esp_video_dec_reg.h" +#include "esp_video_enc_default.h" +#include "esp_video_codec_utils.h" +#include "esp_video_dec_default.h" +#include "esp_video_codec_version.h" +#include "esp_video_codec_types.h" +#include "esp_capture.h" +#include "esp_capture_defaults.h" + +#include "video_processor.h" + +static const char *TAG = "VIDEO_PROCESSOR"; + +#define VIDEO_RENDER_QUEUE_DEFAULT_SIZE (5) +#define _EVENT_GROUP_STOP_BIT (1 << 0) + +#define VIDEO_DECODER_WORKER_TASK_STACK_SIZE (4096 * 10) +#define VIDEO_DECODER_WORKER_TASK_PRIORITY (12) +#define VIDEO_DECODER_WORKER_TASK_CORE (0) +#define VIDEO_DECODER_WORKER_TASK_NAME "video_decoder_worker" + +#define VIDEO_CAPTURE_WORKER_TASK_STACK_SIZE (4096 * 10) +#define VIDEO_CAPTURE_WORKER_TASK_PRIORITY (12) +#define VIDEO_CAPTURE_WORKER_TASK_CORE (0) +#define VIDEO_CAPTURE_WORKER_TASK_NAME "video_capture_worker" + +typedef enum { + VID_PROCESSOR_CMD_STOP, + VID_PROCESSOR_CMD_DATA, + VID_PROCESSOR_CMD_MAX, +} video_processor_cmd_t; + +typedef struct { + video_processor_cmd_t cmd; + video_frame_t frame; +} video_processor_event_t; + +/** + * @brief Video render internal structure + */ +typedef struct { + video_render_config_t config; /*!< Render configuration */ + esp_gmf_oal_thread_t vdec_thread; /*!< Decoder thread handle */ + QueueHandle_t frame_queue; /*!< Frame queue handle */ + EventGroupHandle_t event_group; /*!< Event group handle */ + video_frame_t out_frame; /*!< Output frame buffer */ + void *ctx; /*!< User context */ + esp_video_dec_handle_t dec_handle; /*!< Decoder handle */ + bool is_running; /*!< Running state flag */ +} video_render_t; + +/** + * @brief Video capture internal structure + */ +typedef struct { + video_capture_config_t config; /*!< Capture configuration */ + esp_capture_handle_t capture; /*!< Capture handle */ + esp_capture_audio_src_if_t *aud_src; /*!< Audio source interface */ + esp_capture_video_src_if_t *vid_src; /*!< Video source interface */ + esp_capture_sink_handle_t sink[DEFAULT_VID_MAX_PATH]; /*!< Sink handles */ + esp_gmf_oal_thread_t capture_thread; /*!< Capture thread handle */ + EventGroupHandle_t event_group; /*!< Event group handle */ + QueueHandle_t frame_queue; /*!< Frame queue handle */ + bool is_running; /*!< Running state flag */ +} video_capture_t; + +static esp_capture_video_src_if_t *create_video_source(void *cam_config) +{ +#if CONFIG_IDF_TARGET_ESP32S3 + return esp_capture_new_video_dvp_src((esp_capture_video_dvp_src_cfg_t *)cam_config); +#else + ESP_LOGE(TAG, "Video source not supported for this target"); + return NULL; +#endif /* CONFIG_IDF_TARGET_ESP32S3 */ +} + +static void decoder_worker_thread(void *arg) +{ + video_render_t *render = (video_render_t *)arg; + if (!render) { + ESP_LOGE(TAG, "Invalid render handle"); + esp_gmf_oal_thread_delete(NULL); + } + if (!render->out_frame.data) { + ESP_LOGE(TAG, "Failed to allocate memory for output frame"); + esp_gmf_oal_thread_delete(NULL); + } + render->is_running = true; + video_processor_event_t evt_frame = {0}; + while (render->is_running) { + if (xQueueReceive(render->frame_queue, &evt_frame, portMAX_DELAY) == pdPASS) { + ESP_LOGD(TAG, "vdec_task: %d", evt_frame.cmd); + switch (evt_frame.cmd) { + case VID_PROCESSOR_CMD_DATA: + ESP_LOGD(TAG, "Processing video frame, size: %zu", evt_frame.frame.size); + esp_video_dec_in_frame_t in_frame = { + .pts = 0, + .data = evt_frame.frame.data, + .size = evt_frame.frame.size, + }; + esp_video_dec_out_frame_t out_frame = { + .data = render->out_frame.data, + .size = render->out_frame.size, + }; + int ret = esp_video_dec_process(render->dec_handle, &in_frame, &out_frame); + if (ret != ESP_VC_ERR_OK) { + ESP_LOGE(TAG, "Failed to decode frame. ret: %d", ret); + break; + } + if (render->config.decode_cb) { + render->config.decode_cb(render->config.decode_cb_ctx, out_frame.data, out_frame.size); + } + break; + case VID_PROCESSOR_CMD_STOP: + ESP_LOGI(TAG, "Received stop command, exiting decoder thread"); + render->is_running = false; + break; + + default: + ESP_LOGE(TAG, "Unknown command: %d", evt_frame.cmd); + break; + } + if (evt_frame.frame.data) { + esp_gmf_oal_free(evt_frame.frame.data); + evt_frame.frame.data = NULL; + } + } + } + xEventGroupClearBits(render->event_group, _EVENT_GROUP_STOP_BIT); + ESP_LOGI(TAG, "Decoder thread exiting"); + vTaskDelete(NULL); +} + +static void capture_worker_thread(void *arg) +{ + video_capture_t *capture = (video_capture_t *)arg; + if (!capture) { + ESP_LOGE(TAG, "Invalid capture handle"); + esp_gmf_oal_thread_delete(NULL); + } + for (int i = 0; i < capture->config.sink_num; i++) { + esp_capture_sink_enable(capture->sink[i], ESP_CAPTURE_RUN_MODE_ALWAYS); + } + capture->is_running = true; + ESP_LOGI(TAG, "Capture thread started"); + + uint32_t gcd_fps = capture->config.sink_cfg[0].video_info.fps; + for (int i = 1; i < capture->config.sink_num; i++) { + uint32_t current_fps = capture->config.sink_cfg[i].video_info.fps; + uint32_t a = gcd_fps, b = current_fps; + while (b != 0) { + uint32_t temp = b; + b = a % b; + a = temp; + } + gcd_fps = a; + } + uint32_t base_delay_ms = 1000 / gcd_fps; + ESP_LOGI(TAG, "Multi-path capture: %d paths, gcd_fps: %" PRIu32 ", base_delay: %" PRIu32 "ms", + capture->config.sink_num, gcd_fps, base_delay_ms); + + while (capture->is_running) { + for (int i = 0; i < capture->config.sink_num; i++) { + esp_capture_stream_frame_t frame = {0}; + frame.stream_type = ESP_CAPTURE_STREAM_TYPE_VIDEO; + // Acquire frame from sink + esp_err_t ret = esp_capture_sink_acquire_frame(capture->sink[i], &frame, true); + if (ret != ESP_CAPTURE_ERR_OK) { + ESP_LOGD(TAG, "Acquire frame from sink %d timeout", i); + vTaskDelay(pdMS_TO_TICKS(base_delay_ms)); + continue; + } + if (frame.stream_type == ESP_CAPTURE_STREAM_TYPE_VIDEO && frame.data && frame.size > 0) { + if (capture->config.capture_frame_cb) { + video_frame_t vid_frame = { + .data = frame.data, + .size = frame.size, + }; + capture->config.capture_frame_cb(capture->config.capture_frame_cb_ctx, i, &vid_frame); + } + } + esp_capture_sink_release_frame(capture->sink[i], &frame); + } + } + ESP_LOGI(TAG, "Capture thread exiting"); + xEventGroupSetBits(capture->event_group, _EVENT_GROUP_STOP_BIT); + esp_gmf_oal_thread_delete(NULL); +} + +video_render_handle_t video_render_open(video_render_config_t *config) +{ + if (!config) { + ESP_LOGE(TAG, "Invalid config"); + return NULL; + } + esp_video_dec_register_default(); + + video_render_t *render = esp_gmf_oal_calloc(1, sizeof(video_render_t)); + if (!render) { + ESP_LOGE(TAG, "Failed to allocate memory for render"); + return NULL; + } + render->config.decode_cb_ctx = config->decode_cb_ctx; + render->config.decode_cb = config->decode_cb; + render->config.decode_cfg = config->decode_cfg; + render->config.resolution.width = config->resolution.width; + render->config.resolution.height = config->resolution.height; + render->frame_queue = xQueueCreate(VIDEO_RENDER_QUEUE_DEFAULT_SIZE, sizeof(video_processor_event_t)); + if (!render->frame_queue) { + goto __exit; + } + render->event_group = xEventGroupCreate(); + if (!render->event_group) { + goto __exit; + } + + ESP_LOGI(TAG, "Video render opened successfully"); + return render; + +__exit: + if (render->frame_queue) { + vQueueDelete(render->frame_queue); + render->frame_queue = NULL; + } + if (render->event_group) { + vEventGroupDelete(render->event_group); + render->event_group = NULL; + } + if (render) { + esp_gmf_oal_free(render); + render = NULL; + } + return NULL; +} + +esp_err_t video_render_start(video_render_handle_t handle) +{ + video_render_t *render = (video_render_t *)handle; + if (!render) { + return ESP_ERR_INVALID_ARG; + } + uint32_t actual_size = 0; + render->out_frame.size = esp_video_codec_get_image_size(render->config.decode_cfg.out_fmt, &render->config.resolution); + render->out_frame.data = esp_video_codec_align_alloc(128, render->out_frame.size, &actual_size); + if (!render->out_frame.data) { + ESP_LOGE(TAG, "Failed to allocate memory for output frame"); + return ESP_ERR_NO_MEM; + } + ESP_LOGI(TAG, "Output frame buffer allocated, size: %zu", render->out_frame.size); + + int ret = esp_video_dec_open(&render->config.decode_cfg, &render->dec_handle); + if (!render->dec_handle || ret != ESP_VC_ERR_OK) { + ESP_LOGE(TAG, "Failed to open video decoder, error: %d", ret); + esp_gmf_oal_free(render->out_frame.data); + render->out_frame.data = NULL; + return ESP_ERR_NO_MEM; + } + esp_gmf_oal_thread_create(&render->vdec_thread, VIDEO_DECODER_WORKER_TASK_NAME, + decoder_worker_thread, (void *)render, + VIDEO_DECODER_WORKER_TASK_STACK_SIZE, VIDEO_DECODER_WORKER_TASK_PRIORITY, + true, VIDEO_DECODER_WORKER_TASK_CORE); + + ESP_LOGI(TAG, "Video render started successfully"); + return ESP_OK; +} + +esp_err_t video_render_frame_feed(video_render_handle_t handle, video_frame_t *vid_frame) +{ + video_render_t *render = (video_render_t *)handle; + if (!render) { + ESP_LOGE(TAG, "Invalid render handle"); + return ESP_ERR_INVALID_ARG; + } + if (!vid_frame || !vid_frame->data || vid_frame->size == 0) { + ESP_LOGE(TAG, "Invalid frame data"); + return ESP_ERR_INVALID_ARG; + } + video_processor_event_t evt_frame = {0}; + evt_frame.cmd = VID_PROCESSOR_CMD_DATA; + evt_frame.frame.data = esp_gmf_oal_malloc(vid_frame->size); + if (!evt_frame.frame.data) { + ESP_LOGE(TAG, "Failed to allocate memory for frame data"); + return ESP_ERR_NO_MEM; + } + memcpy(evt_frame.frame.data, vid_frame->data, vid_frame->size); + evt_frame.frame.size = vid_frame->size; + + if (xQueueSend(render->frame_queue, &evt_frame, portMAX_DELAY) != pdPASS) { + ESP_LOGE(TAG, "Failed to send frame to queue"); + esp_gmf_oal_free(evt_frame.frame.data); + return ESP_FAIL; + } + return ESP_OK; +} + +esp_err_t video_render_stop(video_render_handle_t handle) +{ + video_render_t *render = (video_render_t *)handle; + if (!render) { + ESP_LOGE(TAG, "Invalid render handle"); + return ESP_ERR_INVALID_ARG; + } + if (!render->is_running) { + ESP_LOGW(TAG, "Video render is not running"); + return ESP_OK; + } + video_processor_event_t evt_frame = {0}; + evt_frame.cmd = VID_PROCESSOR_CMD_STOP; + + if (xQueueSend(render->frame_queue, &evt_frame, portMAX_DELAY) != pdPASS) { + ESP_LOGE(TAG, "Failed to send stop command to queue"); + return ESP_FAIL; + } + xEventGroupWaitBits(render->event_group, _EVENT_GROUP_STOP_BIT, pdTRUE, pdFALSE, portMAX_DELAY); + + ESP_LOGI(TAG, "Video render stopped successfully"); + return ESP_OK; +} + +void video_render_close(video_render_handle_t handle) +{ + video_render_t *render = (video_render_t *)handle; + if (!render) { + ESP_LOGE(TAG, "Invalid render handle"); + return; + } + if (render->is_running) { + video_render_stop(handle); + } + if (render->dec_handle) { + esp_video_dec_close(render->dec_handle); + render->dec_handle = NULL; + } + if (render->out_frame.data) { + esp_gmf_oal_free(render->out_frame.data); + render->out_frame.data = NULL; + } + if (render->frame_queue) { + vQueueDelete(render->frame_queue); + render->frame_queue = NULL; + } + esp_gmf_oal_free(render); + ESP_LOGI(TAG, "Video render closed successfully"); +} + +video_capture_handle_t video_capture_open(video_capture_config_t *config) +{ + if (!config) { + ESP_LOGE(TAG, "Invalid config"); + return NULL; + } + // capture structure + video_capture_t *capture = esp_gmf_oal_calloc(1, sizeof(video_capture_t)); + if (!capture) { + ESP_LOGE(TAG, "Failed to allocate memory for capture"); + return NULL; + } + capture->config = *config; + capture->frame_queue = xQueueCreate(2, sizeof(video_processor_event_t)); + if (!capture->frame_queue) { + ESP_LOGE(TAG, "Failed to create frame queue"); + goto __exit; + } + capture->event_group = xEventGroupCreate(); + if (!capture->event_group) { + goto __exit; + } + esp_video_enc_register_default(); + return capture; + +__exit: + if (capture->frame_queue) { + vQueueDelete(capture->frame_queue); + capture->frame_queue = NULL; + } + if (capture->event_group) { + vEventGroupDelete(capture->event_group); + capture->event_group = NULL; + } + if (capture) { + esp_gmf_oal_free(capture); + capture = NULL; + } + return NULL; +} + +esp_err_t video_capture_start(video_capture_handle_t handle) +{ + video_capture_t *capture = (video_capture_t *)handle; + if (!capture) { + ESP_LOGE(TAG, "Invalid capture handle"); + return ESP_ERR_INVALID_ARG; + } + capture->vid_src = create_video_source(capture->config.camera_config); + if (!capture->vid_src) { + ESP_LOGE(TAG, "Failed to create video source or not support"); + return ESP_ERR_NO_MEM; + } + for (int i = 0; i < capture->config.sink_num; i++) { + // TODO: support other format + esp_capture_video_info_t fixed_caps = { + .format_id = ESP_CAPTURE_FMT_ID_RGB565_BE, + .width = capture->config.sink_cfg[i].video_info.width, + .height = capture->config.sink_cfg[i].video_info.height, + .fps = capture->config.sink_cfg[i].video_info.fps, + }; + capture->vid_src->set_fixed_caps(capture->vid_src, &fixed_caps); + } + esp_capture_cfg_t capture_cfg = { + .video_src = capture->vid_src, + }; + esp_capture_open(&capture_cfg, &capture->capture); + if (capture->capture == NULL) { + ESP_LOGE(TAG, "Fail to create capture"); + return ESP_ERR_NO_MEM; + } + for (int i = 0; i < capture->config.sink_num; i++) { + esp_capture_sink_cfg_t sink_cfg = capture->config.sink_cfg[i]; + esp_capture_sink_setup(capture->capture, i, &sink_cfg, &capture->sink[i]); + esp_capture_sink_enable(capture->sink[i], ESP_CAPTURE_RUN_MODE_ALWAYS); + } + esp_capture_start(capture->capture); + + esp_gmf_oal_thread_create(&capture->capture_thread, VIDEO_CAPTURE_WORKER_TASK_NAME, capture_worker_thread, + (void *)capture, VIDEO_CAPTURE_WORKER_TASK_STACK_SIZE, VIDEO_CAPTURE_WORKER_TASK_PRIORITY, + true, VIDEO_CAPTURE_WORKER_TASK_CORE); + + return ESP_OK; +} + +void video_capture_close(video_capture_handle_t handle) +{ + video_capture_t *capture = (video_capture_t *)handle; + if (!capture) { + ESP_LOGE(TAG, "Invalid capture handle"); + return; + } + if (capture->capture) { + esp_capture_stop(capture->capture); + esp_capture_close(capture->capture); + } + capture->is_running = false; + xEventGroupWaitBits(capture->event_group, _EVENT_GROUP_STOP_BIT, pdTRUE, pdFALSE, portMAX_DELAY); + if (capture->vid_src) { + capture->vid_src = NULL; + } + if (capture->frame_queue) { + vQueueDelete(capture->frame_queue); + } + esp_gmf_oal_free(capture); +} diff --git a/firmware/echoear-platform-agent/components/echoear/CMakeLists.txt b/firmware/echoear-platform-agent/components/echoear/CMakeLists.txt new file mode 100644 index 0000000..d717042 --- /dev/null +++ b/firmware/echoear-platform-agent/components/echoear/CMakeLists.txt @@ -0,0 +1,6 @@ +idf_component_register( + SRCS "echoear.c" + INCLUDE_DIRS "include" + PRIV_INCLUDE_DIRS "priv_include" + REQUIRES driver esp_lcd fatfs +) diff --git a/firmware/echoear-platform-agent/components/echoear/Kconfig b/firmware/echoear-platform-agent/components/echoear/Kconfig new file mode 100644 index 0000000..a47df4c --- /dev/null +++ b/firmware/echoear-platform-agent/components/echoear/Kconfig @@ -0,0 +1,108 @@ +menu "Board Support Package(EchoEar)" + choice BSP_PCB_VERSION + prompt "PCB version" + default BSP_PCB_VERSION_V1_2 + + config BSP_PCB_VERSION_V1_0 + bool "V1.0" + + config BSP_PCB_VERSION_V1_2 + bool "V1.2" + endchoice + + config BSP_ERROR_CHECK + bool "Enable error check in BSP" + default y + help + Error check assert the application before returning the error code. + + menu "I2C" + config BSP_I2C_NUM + int "I2C peripheral index" + default 1 + range 0 1 + help + ESP32S3 has two I2C peripherals, pick the one you want to use. + + config BSP_I2C_FAST_MODE + bool "Enable I2C fast mode" + default y + help + I2C has two speed modes: normal (100kHz) and fast (400kHz). + + config BSP_I2C_CLK_SPEED_HZ + int + default 400000 if BSP_I2C_FAST_MODE + default 100000 + endmenu + + menu "SPIFFS - Virtual File System" + config BSP_SPIFFS_FORMAT_ON_MOUNT_FAIL + bool "Format SPIFFS if mounting fails" + default n + help + Format SPIFFS if it fails to mount the filesystem. + + config BSP_SPIFFS_MOUNT_POINT + string "SPIFFS mount point" + default "/spiffs" + help + Mount point of SPIFFS in the Virtual File System. + + config BSP_SPIFFS_PARTITION_LABEL + string "Partition label of SPIFFS" + default "storage" + help + Partition label which stores SPIFFS. + + config BSP_SPIFFS_MAX_FILES + int "Max files supported for SPIFFS VFS" + default 5 + help + Supported max files for SPIFFS in the Virtual File System. + endmenu + + menu "SD card - Virtual File System" + config BSP_SD_FORMAT_ON_MOUNT_FAIL + bool "Format SD card if mounting fails" + default n + help + The SDMMC host will format (FAT) the SD card if it fails to mount the filesystem. + + config BSP_SD_MOUNT_POINT + string "SD card mount point" + default "/sdcard" + help + Mount point of the SD card in the Virtual File System + endmenu + + menu "Display" + config BSP_DISPLAY_BRIGHTNESS_LEDC_CH + int "LEDC channel index" + default 1 + range 0 7 + help + LEDC channel is used to generate PWM signal that controls display brightness. + Set LEDC index that should be used. + + config BSP_LCD_DRAW_BUF_HEIGHT + int "LCD framebuf height" + default 100 + range 10 360 + help + Framebuf is used for lvgl rendering output. + + config BSP_LCD_DRAW_BUF_DOUBLE + bool "LCD double framebuf" + default n + help + Whether to enable double framebuf. + endmenu + + config BSP_HEAD_LED_LEDC_CH + int "LEDC channel index" + default 2 + range 0 7 + help + LEDC channel is used to generate PWM signal that controls led brightness. +endmenu diff --git a/firmware/echoear-platform-agent/components/echoear/LICENSE b/firmware/echoear-platform-agent/components/echoear/LICENSE new file mode 100644 index 0000000..b0b5d33 --- /dev/null +++ b/firmware/echoear-platform-agent/components/echoear/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall release + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/firmware/echoear-platform-agent/components/echoear/README.md b/firmware/echoear-platform-agent/components/echoear/README.md new file mode 100644 index 0000000..e69de29 diff --git a/firmware/echoear-platform-agent/components/echoear/echoear.c b/firmware/echoear-platform-agent/components/echoear/echoear.c new file mode 100644 index 0000000..12c9c3d --- /dev/null +++ b/firmware/echoear-platform-agent/components/echoear/echoear.c @@ -0,0 +1,650 @@ +/* + * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "driver/gpio.h" +#include "driver/ledc.h" +#include "driver/spi_master.h" +#include "esp_err.h" +#include "esp_log.h" +#include "esp_check.h" +#include "esp_lcd_panel_io.h" +#include "esp_lcd_panel_vendor.h" +#include "esp_lcd_panel_ops.h" + +#include "bsp/esp-bsp.h" +#include "bsp/display.h" +#include "bsp/touch.h" +#include "esp_lcd_st77916.h" +#include "esp_lcd_touch_cst816s.h" +#include "esp_lvgl_port.h" +#include "bsp_err_check.h" + +#include +#include +#include +#include +#include +#include "driver/uart.h" +#include "esp_sleep.h" +#include "esp_timer.h" +#include "sdkconfig.h" + +#define GPIO_WAKEUP_LEVEL 0 + +static const char *TAG = "BSP"; + +static lv_disp_t *disp; +static lv_indev_t *disp_indev = NULL; +static esp_lcd_touch_handle_t tp; // LCD touch handle +static esp_lcd_panel_handle_t panel_handle = NULL; + +static i2c_master_bus_handle_t i2c_handle = NULL; +static sdmmc_card_t *bsp_sdcard = NULL; // Global uSD card handler +static bool i2c_initialized = false; + +esp_err_t bsp_i2c_init(void) +{ + /* I2C was initialized before */ + if (i2c_initialized) { + return ESP_OK; + } + + const i2c_master_bus_config_t i2c_config = { + .i2c_port = BSP_I2C_NUM, + .sda_io_num = BSP_I2C_SDA, + .scl_io_num = BSP_I2C_SCL, + .clk_source = I2C_CLK_SRC_DEFAULT, + }; + BSP_ERROR_CHECK_RETURN_ERR(i2c_new_master_bus(&i2c_config, &i2c_handle)); + + i2c_initialized = true; + return ESP_OK; +} + +esp_err_t bsp_i2c_deinit(void) +{ + BSP_ERROR_CHECK_RETURN_ERR(i2c_del_master_bus(i2c_handle)); + i2c_initialized = false; + return ESP_OK; +} + +i2c_master_bus_handle_t bsp_i2c_get_handle(void) +{ + bsp_i2c_init(); + return i2c_handle; +} + +void bsp_i2c_set_handle(i2c_master_bus_handle_t handle) +{ + i2c_handle = handle; + i2c_initialized = true; +} + +// Bit number used to represent command and parameter +#define LCD_CMD_BITS 32 +#define LCD_PARAM_BITS 8 +#define LCD_LEDC_CH CONFIG_BSP_DISPLAY_BRIGHTNESS_LEDC_CH + +esp_err_t bsp_display_brightness_init(void) +{ + // Setup LEDC peripheral for PWM backlight control + const ledc_channel_config_t LCD_backlight_channel = { + .gpio_num = BSP_LCD_BACKLIGHT, + .speed_mode = LEDC_LOW_SPEED_MODE, + .channel = LCD_LEDC_CH, + .intr_type = LEDC_INTR_DISABLE, + .timer_sel = LEDC_TIMER_0, + .duty = 0, + .hpoint = 0 + }; + const ledc_timer_config_t LCD_backlight_timer = { + .speed_mode = LEDC_LOW_SPEED_MODE, + .duty_resolution = LEDC_TIMER_10_BIT, + .timer_num = LEDC_TIMER_0, + .freq_hz = 4000, + .clk_cfg = LEDC_AUTO_CLK + }; + + BSP_ERROR_CHECK_RETURN_ERR(ledc_timer_config(&LCD_backlight_timer)); + BSP_ERROR_CHECK_RETURN_ERR(ledc_channel_config(&LCD_backlight_channel)); + + return ESP_OK; +} + +esp_err_t bsp_display_brightness_set(int brightness_percent) +{ + if (brightness_percent > 100) { + brightness_percent = 100; + } + if (brightness_percent < 0) { + brightness_percent = 0; + } + + ESP_LOGI(TAG, "Setting LCD backlight: %d%%", brightness_percent); + uint32_t duty_cycle = (1023 * brightness_percent) / 100; // LEDC resolution set to 10bits, thus: 100% = 1023 + BSP_ERROR_CHECK_RETURN_ERR(ledc_set_duty(LEDC_LOW_SPEED_MODE, LCD_LEDC_CH, duty_cycle)); + BSP_ERROR_CHECK_RETURN_ERR(ledc_update_duty(LEDC_LOW_SPEED_MODE, LCD_LEDC_CH)); + + return ESP_OK; +} + +esp_err_t bsp_display_backlight_off(void) +{ + return bsp_display_brightness_set(0); +} + +esp_err_t bsp_display_backlight_on(void) +{ + return bsp_display_brightness_set(100); +} + +static const st77916_lcd_init_cmd_t vendor_specific_init_default[] = { + {0xF0, (uint8_t []){0x28}, 1, 0}, + {0xF2, (uint8_t []){0x28}, 1, 0}, + {0x73, (uint8_t []){0xF0}, 1, 0}, + {0x7C, (uint8_t []){0xD1}, 1, 0}, + {0x83, (uint8_t []){0xE0}, 1, 0}, + {0x84, (uint8_t []){0x61}, 1, 0}, + {0xF2, (uint8_t []){0x82}, 1, 0}, + {0xF0, (uint8_t []){0x00}, 1, 0}, + {0xF0, (uint8_t []){0x01}, 1, 0}, + {0xF1, (uint8_t []){0x01}, 1, 0}, + {0xB0, (uint8_t []){0x56}, 1, 0}, + {0xB1, (uint8_t []){0x4D}, 1, 0}, + {0xB2, (uint8_t []){0x24}, 1, 0}, + {0xB4, (uint8_t []){0x87}, 1, 0}, + {0xB5, (uint8_t []){0x44}, 1, 0}, + {0xB6, (uint8_t []){0x8B}, 1, 0}, + {0xB7, (uint8_t []){0x40}, 1, 0}, + {0xB8, (uint8_t []){0x86}, 1, 0}, + {0xBA, (uint8_t []){0x00}, 1, 0}, + {0xBB, (uint8_t []){0x08}, 1, 0}, + {0xBC, (uint8_t []){0x08}, 1, 0}, + {0xBD, (uint8_t []){0x00}, 1, 0}, + {0xC0, (uint8_t []){0x80}, 1, 0}, + {0xC1, (uint8_t []){0x10}, 1, 0}, + {0xC2, (uint8_t []){0x37}, 1, 0}, + {0xC3, (uint8_t []){0x80}, 1, 0}, + {0xC4, (uint8_t []){0x10}, 1, 0}, + {0xC5, (uint8_t []){0x37}, 1, 0}, + {0xC6, (uint8_t []){0xA9}, 1, 0}, + {0xC7, (uint8_t []){0x41}, 1, 0}, + {0xC8, (uint8_t []){0x01}, 1, 0}, + {0xC9, (uint8_t []){0xA9}, 1, 0}, + {0xCA, (uint8_t []){0x41}, 1, 0}, + {0xCB, (uint8_t []){0x01}, 1, 0}, + {0xD0, (uint8_t []){0x91}, 1, 0}, + {0xD1, (uint8_t []){0x68}, 1, 0}, + {0xD2, (uint8_t []){0x68}, 1, 0}, + {0xF5, (uint8_t []){0x00, 0xA5}, 2, 0}, + {0xDD, (uint8_t []){0x4F}, 1, 0}, + {0xDE, (uint8_t []){0x4F}, 1, 0}, + {0xF1, (uint8_t []){0x10}, 1, 0}, + {0xF0, (uint8_t []){0x00}, 1, 0}, + {0xF0, (uint8_t []){0x02}, 1, 0}, + {0xE0, (uint8_t []){0xF0, 0x0A, 0x10, 0x09, 0x09, 0x36, 0x35, 0x33, 0x4A, 0x29, 0x15, 0x15, 0x2E, 0x34}, 14, 0}, + {0xE1, (uint8_t []){0xF0, 0x0A, 0x0F, 0x08, 0x08, 0x05, 0x34, 0x33, 0x4A, 0x39, 0x15, 0x15, 0x2D, 0x33}, 14, 0}, + {0xF0, (uint8_t []){0x10}, 1, 0}, + {0xF3, (uint8_t []){0x10}, 1, 0}, + {0xE0, (uint8_t []){0x07}, 1, 0}, + {0xE1, (uint8_t []){0x00}, 1, 0}, + {0xE2, (uint8_t []){0x00}, 1, 0}, + {0xE3, (uint8_t []){0x00}, 1, 0}, + {0xE4, (uint8_t []){0xE0}, 1, 0}, + {0xE5, (uint8_t []){0x06}, 1, 0}, + {0xE6, (uint8_t []){0x21}, 1, 0}, + {0xE7, (uint8_t []){0x01}, 1, 0}, + {0xE8, (uint8_t []){0x05}, 1, 0}, + {0xE9, (uint8_t []){0x02}, 1, 0}, + {0xEA, (uint8_t []){0xDA}, 1, 0}, + {0xEB, (uint8_t []){0x00}, 1, 0}, + {0xEC, (uint8_t []){0x00}, 1, 0}, + {0xED, (uint8_t []){0x0F}, 1, 0}, + {0xEE, (uint8_t []){0x00}, 1, 0}, + {0xEF, (uint8_t []){0x00}, 1, 0}, + {0xF8, (uint8_t []){0x00}, 1, 0}, + {0xF9, (uint8_t []){0x00}, 1, 0}, + {0xFA, (uint8_t []){0x00}, 1, 0}, + {0xFB, (uint8_t []){0x00}, 1, 0}, + {0xFC, (uint8_t []){0x00}, 1, 0}, + {0xFD, (uint8_t []){0x00}, 1, 0}, + {0xFE, (uint8_t []){0x00}, 1, 0}, + {0xFF, (uint8_t []){0x00}, 1, 0}, + {0x60, (uint8_t []){0x40}, 1, 0}, + {0x61, (uint8_t []){0x04}, 1, 0}, + {0x62, (uint8_t []){0x00}, 1, 0}, + {0x63, (uint8_t []){0x42}, 1, 0}, + {0x64, (uint8_t []){0xD9}, 1, 0}, + {0x65, (uint8_t []){0x00}, 1, 0}, + {0x66, (uint8_t []){0x00}, 1, 0}, + {0x67, (uint8_t []){0x00}, 1, 0}, + {0x68, (uint8_t []){0x00}, 1, 0}, + {0x69, (uint8_t []){0x00}, 1, 0}, + {0x6A, (uint8_t []){0x00}, 1, 0}, + {0x6B, (uint8_t []){0x00}, 1, 0}, + {0x70, (uint8_t []){0x40}, 1, 0}, + {0x71, (uint8_t []){0x03}, 1, 0}, + {0x72, (uint8_t []){0x00}, 1, 0}, + {0x73, (uint8_t []){0x42}, 1, 0}, + {0x74, (uint8_t []){0xD8}, 1, 0}, + {0x75, (uint8_t []){0x00}, 1, 0}, + {0x76, (uint8_t []){0x00}, 1, 0}, + {0x77, (uint8_t []){0x00}, 1, 0}, + {0x78, (uint8_t []){0x00}, 1, 0}, + {0x79, (uint8_t []){0x00}, 1, 0}, + {0x7A, (uint8_t []){0x00}, 1, 0}, + {0x7B, (uint8_t []){0x00}, 1, 0}, + {0x80, (uint8_t []){0x48}, 1, 0}, + {0x81, (uint8_t []){0x00}, 1, 0}, + {0x82, (uint8_t []){0x06}, 1, 0}, + {0x83, (uint8_t []){0x02}, 1, 0}, + {0x84, (uint8_t []){0xD6}, 1, 0}, + {0x85, (uint8_t []){0x04}, 1, 0}, + {0x86, (uint8_t []){0x00}, 1, 0}, + {0x87, (uint8_t []){0x00}, 1, 0}, + {0x88, (uint8_t []){0x48}, 1, 0}, + {0x89, (uint8_t []){0x00}, 1, 0}, + {0x8A, (uint8_t []){0x08}, 1, 0}, + {0x8B, (uint8_t []){0x02}, 1, 0}, + {0x8C, (uint8_t []){0xD8}, 1, 0}, + {0x8D, (uint8_t []){0x04}, 1, 0}, + {0x8E, (uint8_t []){0x00}, 1, 0}, + {0x8F, (uint8_t []){0x00}, 1, 0}, + {0x90, (uint8_t []){0x48}, 1, 0}, + {0x91, (uint8_t []){0x00}, 1, 0}, + {0x92, (uint8_t []){0x0A}, 1, 0}, + {0x93, (uint8_t []){0x02}, 1, 0}, + {0x94, (uint8_t []){0xDA}, 1, 0}, + {0x95, (uint8_t []){0x04}, 1, 0}, + {0x96, (uint8_t []){0x00}, 1, 0}, + {0x97, (uint8_t []){0x00}, 1, 0}, + {0x98, (uint8_t []){0x48}, 1, 0}, + {0x99, (uint8_t []){0x00}, 1, 0}, + {0x9A, (uint8_t []){0x0C}, 1, 0}, + {0x9B, (uint8_t []){0x02}, 1, 0}, + {0x9C, (uint8_t []){0xDC}, 1, 0}, + {0x9D, (uint8_t []){0x04}, 1, 0}, + {0x9E, (uint8_t []){0x00}, 1, 0}, + {0x9F, (uint8_t []){0x00}, 1, 0}, + {0xA0, (uint8_t []){0x48}, 1, 0}, + {0xA1, (uint8_t []){0x00}, 1, 0}, + {0xA2, (uint8_t []){0x05}, 1, 0}, + {0xA3, (uint8_t []){0x02}, 1, 0}, + {0xA4, (uint8_t []){0xD5}, 1, 0}, + {0xA5, (uint8_t []){0x04}, 1, 0}, + {0xA6, (uint8_t []){0x00}, 1, 0}, + {0xA7, (uint8_t []){0x00}, 1, 0}, + {0xA8, (uint8_t []){0x48}, 1, 0}, + {0xA9, (uint8_t []){0x00}, 1, 0}, + {0xAA, (uint8_t []){0x07}, 1, 0}, + {0xAB, (uint8_t []){0x02}, 1, 0}, + {0xAC, (uint8_t []){0xD7}, 1, 0}, + {0xAD, (uint8_t []){0x04}, 1, 0}, + {0xAE, (uint8_t []){0x00}, 1, 0}, + {0xAF, (uint8_t []){0x00}, 1, 0}, + {0xB0, (uint8_t []){0x48}, 1, 0}, + {0xB1, (uint8_t []){0x00}, 1, 0}, + {0xB2, (uint8_t []){0x09}, 1, 0}, + {0xB3, (uint8_t []){0x02}, 1, 0}, + {0xB4, (uint8_t []){0xD9}, 1, 0}, + {0xB5, (uint8_t []){0x04}, 1, 0}, + {0xB6, (uint8_t []){0x00}, 1, 0}, + {0xB7, (uint8_t []){0x00}, 1, 0}, + {0xB8, (uint8_t []){0x48}, 1, 0}, + {0xB9, (uint8_t []){0x00}, 1, 0}, + {0xBA, (uint8_t []){0x0B}, 1, 0}, + {0xBB, (uint8_t []){0x02}, 1, 0}, + {0xBC, (uint8_t []){0xDB}, 1, 0}, + {0xBD, (uint8_t []){0x04}, 1, 0}, + {0xBE, (uint8_t []){0x00}, 1, 0}, + {0xBF, (uint8_t []){0x00}, 1, 0}, + {0xC0, (uint8_t []){0x10}, 1, 0}, + {0xC1, (uint8_t []){0x47}, 1, 0}, + {0xC2, (uint8_t []){0x56}, 1, 0}, + {0xC3, (uint8_t []){0x65}, 1, 0}, + {0xC4, (uint8_t []){0x74}, 1, 0}, + {0xC5, (uint8_t []){0x88}, 1, 0}, + {0xC6, (uint8_t []){0x99}, 1, 0}, + {0xC7, (uint8_t []){0x01}, 1, 0}, + {0xC8, (uint8_t []){0xBB}, 1, 0}, + {0xC9, (uint8_t []){0xAA}, 1, 0}, + {0xD0, (uint8_t []){0x10}, 1, 0}, + {0xD1, (uint8_t []){0x47}, 1, 0}, + {0xD2, (uint8_t []){0x56}, 1, 0}, + {0xD3, (uint8_t []){0x65}, 1, 0}, + {0xD4, (uint8_t []){0x74}, 1, 0}, + {0xD5, (uint8_t []){0x88}, 1, 0}, + {0xD6, (uint8_t []){0x99}, 1, 0}, + {0xD7, (uint8_t []){0x01}, 1, 0}, + {0xD8, (uint8_t []){0xBB}, 1, 0}, + {0xD9, (uint8_t []){0xAA}, 1, 0}, + {0xF3, (uint8_t []){0x01}, 1, 0}, + {0xF0, (uint8_t []){0x00}, 1, 0}, + {0x21, (uint8_t []){}, 0, 0}, + {0x11, (uint8_t []){}, 0, 0}, + {0x00, (uint8_t []){}, 0, 120}, +}; + +esp_err_t bsp_display_new(const bsp_display_config_t *config, esp_lcd_panel_handle_t *ret_panel, esp_lcd_panel_io_handle_t *ret_io) +{ + esp_err_t ret = ESP_OK; + assert(config != NULL && config->max_transfer_sz > 0); + + ESP_RETURN_ON_ERROR(bsp_display_brightness_init(), TAG, "Brightness init failed"); + + /* Initialize I2C */ + BSP_ERROR_CHECK_RETURN_ERR(bsp_i2c_init()); + + ESP_LOGD(TAG, "Initialize SPI bus"); + const spi_bus_config_t buscfg = { + .sclk_io_num = BSP_LCD_PCLK, + .data0_io_num = BSP_LCD_DATA0, + .data1_io_num = BSP_LCD_DATA1, + .data2_io_num = BSP_LCD_DATA2, + .data3_io_num = BSP_LCD_DATA3, + .max_transfer_sz = config->max_transfer_sz, + }; + ESP_RETURN_ON_ERROR(spi_bus_initialize(BSP_LCD_SPI_NUM, &buscfg, SPI_DMA_CH_AUTO), TAG, "SPI init failed"); + + ESP_LOGD(TAG, "Install panel IO"); + const esp_lcd_panel_io_spi_config_t io_config = { + .dc_gpio_num = BSP_LCD_DC, + .cs_gpio_num = BSP_LCD_CS, + .pclk_hz = BSP_LCD_PIXEL_CLOCK_HZ, + .lcd_cmd_bits = LCD_CMD_BITS, + .lcd_param_bits = LCD_PARAM_BITS, + .spi_mode = 0, + .trans_queue_depth = 1, + .flags = { + .quad_mode = true, + }, + }; + ESP_GOTO_ON_ERROR(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)BSP_LCD_SPI_NUM, &io_config, ret_io), err, TAG, "New panel IO failed"); + + ESP_LOGD(TAG, "Install LCD driver"); + const st77916_vendor_config_t vendor_config = { + .init_cmds = vendor_specific_init_default, + .init_cmds_size = sizeof(vendor_specific_init_default) / sizeof(vendor_specific_init_default[0]), + .flags = { + .use_qspi_interface = 1, + }, + }; + esp_lcd_panel_dev_config_t panel_config = { + .reset_gpio_num = BSP_LCD_RST, // Shared with Touch reset + .color_space = BSP_LCD_COLOR_SPACE, + .bits_per_pixel = BSP_LCD_BITS_PER_PIXEL, + .flags = { + .reset_active_high = true, + }, + .vendor_config = (void *) &vendor_config, + }; + + ESP_LOGI(TAG, "Install st77916 panel driver"); + ESP_ERROR_CHECK(esp_lcd_new_panel_st77916(*ret_io, (const esp_lcd_panel_dev_config_t *)&panel_config, ret_panel)); + + esp_lcd_panel_reset(*ret_panel); + esp_lcd_panel_init(*ret_panel); + esp_lcd_panel_disp_on_off(*ret_panel, true); + return ret; + +err: + if (*ret_panel) { + esp_lcd_panel_del(*ret_panel); + } + if (*ret_io) { + esp_lcd_panel_io_del(*ret_io); + } + spi_bus_free(BSP_LCD_SPI_NUM); + return ret; +} + +static lv_disp_t *bsp_display_lcd_init(const bsp_display_cfg_t *cfg) +{ + assert(cfg != NULL); + esp_lcd_panel_io_handle_t io_handle = NULL; + const bsp_display_config_t bsp_disp_cfg = { + .max_transfer_sz = (BSP_LCD_H_RES * CONFIG_BSP_LCD_DRAW_BUF_HEIGHT) * sizeof(uint16_t), + }; + BSP_ERROR_CHECK_RETURN_NULL(bsp_display_new(&bsp_disp_cfg, &panel_handle, &io_handle)); + + esp_lcd_panel_disp_on_off(panel_handle, true); + + /* Add LCD screen */ + ESP_LOGD(TAG, "Add LCD screen"); + const lvgl_port_display_cfg_t disp_cfg = { + .io_handle = io_handle, + .panel_handle = panel_handle, + .buffer_size = cfg->buffer_size, + .double_buffer = cfg->double_buffer, + .hres = BSP_LCD_H_RES, + .vres = BSP_LCD_V_RES, + .monochrome = false, + /* Rotation values must be same as used in esp_lcd for initial settings of the screen */ + .rotation = { + .swap_xy = false, + .mirror_x = false, + .mirror_y = false, + }, + .flags = { + .buff_dma = cfg->flags.buff_dma, + .buff_spiram = cfg->flags.buff_spiram, + .swap_bytes = true, + // .default_dummy_draw = cfg->flags.default_dummy_draw, + } + }; + + return lvgl_port_add_disp(&disp_cfg); +} + +esp_err_t bsp_touch_new(const bsp_touch_config_t *config, esp_lcd_touch_handle_t *ret_touch) +{ + /* Initialize I2C */ + BSP_ERROR_CHECK_RETURN_ERR(bsp_i2c_init()); + + /* Initialize touch */ + esp_lcd_touch_config_t tp_cfg = { + .x_max = BSP_LCD_H_RES, + .y_max = BSP_LCD_V_RES, + .rst_gpio_num = GPIO_NUM_NC, // Shared with LCD reset + .int_gpio_num = BSP_LCD_TOUCH_INT, + .levels = { + .reset = 0, + .interrupt = 0, + }, + .flags = { + .swap_xy = 0, + .mirror_x = 0, + .mirror_y = 0, + }, + }; + + esp_lcd_panel_io_handle_t tp_io_handle = NULL; + esp_lcd_panel_io_i2c_config_t tp_io_config = ESP_LCD_TOUCH_IO_I2C_CST816S_CONFIG(); + + ESP_RETURN_ON_ERROR(esp_lcd_new_panel_io_i2c_v2(i2c_handle, &tp_io_config, &tp_io_handle), TAG, ""); + + ESP_RETURN_ON_ERROR(esp_lcd_touch_new_i2c_cst816s(tp_io_handle, &tp_cfg, ret_touch), TAG, "New CST816S failed"); + + return ESP_OK; +} + +static lv_indev_t *bsp_display_indev_init(lv_disp_t *disp) +{ + BSP_ERROR_CHECK_RETURN_NULL(bsp_touch_new(NULL, &tp)); + assert(tp); + + /* Add touch input (for selected screen) */ + const lvgl_port_touch_cfg_t touch_cfg = { + .disp = disp, + .handle = tp, + }; + + return lvgl_port_add_touch(&touch_cfg); +} + +lv_disp_t *bsp_display_start(void) +{ + bsp_display_cfg_t cfg = { + .lvgl_port_cfg = ESP_LVGL_PORT_INIT_CONFIG(), + .buffer_size = BSP_LCD_H_RES * CONFIG_BSP_LCD_DRAW_BUF_HEIGHT, +#if CONFIG_BSP_LCD_DRAW_BUF_DOUBLE + .double_buffer = 1, +#else + .double_buffer = 0, +#endif + .flags = { + .buff_dma = true, + .buff_spiram = false, + } + }; + return bsp_display_start_with_config(&cfg); +} + +lv_disp_t *bsp_display_start_with_config(const bsp_display_cfg_t *cfg) +{ + assert(cfg != NULL); + (lvgl_port_init(&cfg->lvgl_port_cfg)); + BSP_ERROR_CHECK_RETURN_NULL(bsp_display_brightness_init()); + + BSP_NULL_CHECK(disp = bsp_display_lcd_init(cfg), NULL); + lv_display_set_user_data(disp, (void *)panel_handle); + + BSP_NULL_CHECK(disp_indev = bsp_display_indev_init(disp), NULL); + + return disp; +} + +lv_indev_t *bsp_display_get_input_dev(void) +{ + return disp_indev; +} + +bool bsp_display_lock(uint32_t timeout_ms) +{ + return lvgl_port_lock(timeout_ms); +} + +void bsp_display_unlock(void) +{ + lvgl_port_unlock(); +} + + +esp_err_t bsp_power_init(bool power_en) +{ + gpio_config_t power_gpio_config = { + .mode = GPIO_MODE_OUTPUT, + .pin_bit_mask = BIT64(BSP_POWER_OFF), + }; +#if defined(BSP_POWER_CODEC_EN) + power_gpio_config.pin_bit_mask |= BIT64(BSP_POWER_CODEC_EN); +#endif + ESP_ERROR_CHECK(gpio_config(&power_gpio_config)); + + gpio_set_level(BSP_POWER_OFF, !power_en); +#if defined(BSP_POWER_CODEC_EN) + gpio_set_level(BSP_POWER_CODEC_EN, power_en); + ESP_LOGI(TAG, "Using PCB version V1.2"); +#else + ESP_LOGI(TAG, "Using PCB version V1.0"); +#endif + + vTaskDelay(pdMS_TO_TICKS(100)); + + return ESP_OK; +} + +sdmmc_card_t *bsp_sdcard_get_handle(void) +{ + return bsp_sdcard; +} + +void bsp_sdcard_get_sdmmc_host(const int slot, sdmmc_host_t *config) +{ + assert(config); + + sdmmc_host_t host_config = SDMMC_HOST_DEFAULT(); + + memcpy(config, &host_config, sizeof(sdmmc_host_t)); +} + +void bsp_sdcard_sdmmc_get_slot(const int slot, sdmmc_slot_config_t *config) +{ + assert(config); + + sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT(); + + /* SD card is connected to Slot 0 pins. Slot 0 uses IO MUX, so not specifying the pins here */ + slot_config.cmd = BSP_SD_CMD; + slot_config.clk = BSP_SD_CLK; + slot_config.d0 = BSP_SD_D0; + slot_config.width = 1; + slot_config.flags |= SDMMC_SLOT_FLAG_INTERNAL_PULLUP; + + *config = slot_config; +} + +esp_err_t bsp_sdcard_sdmmc_mount(bsp_sdcard_cfg_t *cfg) +{ + sdmmc_host_t sdhost = {0}; + sdmmc_slot_config_t sdslot = {0}; + const esp_vfs_fat_sdmmc_mount_config_t mount_config = { +#ifdef CONFIG_BSP_SD_FORMAT_ON_MOUNT_FAIL + .format_if_mount_failed = true, +#else + .format_if_mount_failed = false, +#endif + .max_files = 5, + .allocation_unit_size = 16 * 1024 + }; + assert(cfg); + + if (!cfg->mount) { + cfg->mount = &mount_config; + } + + if (!cfg->host) { + bsp_sdcard_get_sdmmc_host(SDMMC_HOST_SLOT_0, &sdhost); + cfg->host = &sdhost; + } + + if (!cfg->slot.sdmmc) { + bsp_sdcard_sdmmc_get_slot(SDMMC_HOST_SLOT_0, &sdslot); + cfg->slot.sdmmc = &sdslot; + } + +#if CONFIG_FATFS_LFN_NONE + ESP_LOGW(TAG, "Warning: Long filenames on SD card are disabled in menuconfig!"); +#endif + + return esp_vfs_fat_sdmmc_mount(BSP_SD_MOUNT_POINT, cfg->host, cfg->slot.sdmmc, cfg->mount, &bsp_sdcard); +} + +esp_err_t bsp_sdcard_mount(void) +{ + bsp_sdcard_cfg_t cfg = {0}; + return bsp_sdcard_sdmmc_mount(&cfg); +} + +esp_err_t bsp_sdcard_unmount(void) +{ + esp_err_t ret = ESP_OK; + + ret |= esp_vfs_fat_sdcard_unmount(BSP_SD_MOUNT_POINT, bsp_sdcard); + bsp_sdcard = NULL; + + return ret; +} + +esp_err_t bsp_set_head_led(bool on) +{ + return gpio_set_level(BSP_HEAD_LED, !on); +} + +esp_err_t bsp_set_peripheral_power(bool on) +{ + return gpio_set_level(BSP_POWER_OFF, !on); +} diff --git a/firmware/echoear-platform-agent/components/echoear/idf_component.yml b/firmware/echoear-platform-agent/components/echoear/idf_component.yml new file mode 100644 index 0000000..f7eee2a --- /dev/null +++ b/firmware/echoear-platform-agent/components/echoear/idf_component.yml @@ -0,0 +1,20 @@ +targets: + - esp32s3 + +tags: + - bsp + +dependencies: + idf: ">=4.4.5" + + lvgl/lvgl: "9.2.*" + + espressif/esp_lcd_st77916: "^1.0.1" + + espressif/esp_lcd_touch_cst816s: + version: "^1.1.0" + public: true + + espressif/esp_lvgl_port: + version: "^2.6.0" + public: true diff --git a/firmware/echoear-platform-agent/components/echoear/include/bsp/display.h b/firmware/echoear-platform-agent/components/echoear/include/bsp/display.h new file mode 100644 index 0000000..4e99efb --- /dev/null +++ b/firmware/echoear-platform-agent/components/echoear/include/bsp/display.h @@ -0,0 +1,110 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief BSP LCD + * + * This file offers API for basic LCD control. + * It is useful for users who want to use the LCD without the default Graphical Library LVGL. + * + * For standard LCD initialization with LVGL graphical library, you can call all-in-one function bsp_display_start(). + */ + +#pragma once +#include "esp_lcd_types.h" + +/* LCD color formats */ +#define ESP_LCD_COLOR_FORMAT_RGB565 (1) +#define ESP_LCD_COLOR_FORMAT_RGB888 (2) + +/* LCD display color format */ +#define BSP_LCD_COLOR_FORMAT (ESP_LCD_COLOR_FORMAT_RGB565) +/* LCD display color bytes endianness */ +#define BSP_LCD_BIGENDIAN (1) +/* LCD display color bits */ +#define BSP_LCD_BITS_PER_PIXEL (16) +/* LCD display color space */ +#define BSP_LCD_COLOR_SPACE (ESP_LCD_COLOR_SPACE_RGB) +/* LCD definition */ +#define BSP_LCD_H_RES (360) +#define BSP_LCD_V_RES (360) + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief BSP display configuration structure + * + */ +typedef struct { + int max_transfer_sz; /*!< Maximum transfer size, in bytes. */ +} bsp_display_config_t; + +/** + * @brief Create new display panel + * + * For maximum flexibility, this function performs only reset and initialization of the display. + * You must turn on the display explicitly by calling esp_lcd_panel_disp_on_off(). + * The display's backlight is not turned on either. You can use bsp_display_backlight_on/off(), + * bsp_display_brightness_set() (on supported boards) or implement your own backlight control. + * + * If you want to free resources allocated by this function, you can use esp_lcd API, ie.: + * + * \code{.c} + * esp_lcd_panel_del(panel); + * esp_lcd_panel_io_del(io); + * spi_bus_free(spi_num_from_configuration); + * \endcode + * + * @param[in] config display configuration + * @param[out] ret_panel esp_lcd panel handle + * @param[out] ret_io esp_lcd IO handle + * @return + * - ESP_OK On success + * - Else esp_lcd failure + */ +esp_err_t bsp_display_new(const bsp_display_config_t *config, esp_lcd_panel_handle_t *ret_panel, esp_lcd_panel_io_handle_t *ret_io); + +/** + * @brief Set display's brightness + * + * Brightness is controlled with PWM signal to a pin controlling backlight. + * Display must be already initialized by calling bsp_display_new() + * + * @param[in] brightness_percent Brightness in [%] + * @return + * - ESP_OK On success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t bsp_display_brightness_set(int brightness_percent); + +/** + * @brief Turn on display backlight + * + * Display must be already initialized by calling bsp_display_new() + * + * @return + * - ESP_OK On success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t bsp_display_backlight_on(void); + +/** + * @brief Turn off display backlight + * + * Display must be already initialized by calling bsp_display_new() + * + * @return + * - ESP_OK On success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t bsp_display_backlight_off(void); + +#ifdef __cplusplus +} +#endif diff --git a/firmware/echoear-platform-agent/components/echoear/include/bsp/echoear.h b/firmware/echoear-platform-agent/components/echoear/include/bsp/echoear.h new file mode 100644 index 0000000..ed06ea6 --- /dev/null +++ b/firmware/echoear-platform-agent/components/echoear/include/bsp/echoear.h @@ -0,0 +1,345 @@ +/* + * SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "sdkconfig.h" +#include "soc/usb_pins.h" +#include "driver/gpio.h" +#include "driver/i2c_master.h" +#include "driver/sdmmc_host.h" +#include "driver/sdspi_host.h" +#include "esp_vfs_fat.h" +#include "lvgl.h" +#include "esp_lvgl_port.h" +#include "bsp/display.h" + +/************************************************************************************************** + * BSP Capabilities + **************************************************************************************************/ + +#define BSP_CAPS_DISPLAY 1 +#define BSP_CAPS_TOUCH 1 +#define BSP_CAPS_BUTTONS 0 +#define BSP_CAPS_AUDIO 0 +#define BSP_CAPS_AUDIO_SPEAKER 0 +#define BSP_CAPS_AUDIO_MIC 0 +#define BSP_CAPS_SDCARD 1 +#define BSP_CAPS_IMU 0 + +/* I2C */ +#define BSP_I2C_SCL (GPIO_NUM_1) +#define BSP_I2C_SDA (GPIO_NUM_2) +#define BSP_IMU_INT (GPIO_NUM_21) + +/* Audio */ +#define BSP_I2S_SCLK (GPIO_NUM_40) // BCLK +#define BSP_I2S_MCLK (GPIO_NUM_42) +#define BSP_I2S_LCLK (GPIO_NUM_39) // WS +#define BSP_I2S_DOUT (GPIO_NUM_41) // To Codec ES8311 +#define BSP_I2S_DSIN (GPIO_NUM_3) +#define BSP_POWER_AMP_IO (GPIO_NUM_15) +#define BSP_POWER_CODEC_EN (GPIO_NUM_48) + +/* Display */ +#define BSP_LCD_DATA3 (GPIO_NUM_12) +#define BSP_LCD_DATA2 (GPIO_NUM_11) +#define BSP_LCD_DATA1 (GPIO_NUM_13) +#define BSP_LCD_DATA0 (GPIO_NUM_46) +#define BSP_LCD_PCLK (GPIO_NUM_18) +#define BSP_LCD_CS (GPIO_NUM_14) +#define BSP_LCD_DC (GPIO_NUM_45) +#define BSP_LCD_RST (GPIO_NUM_47) +#define BSP_LCD_BACKLIGHT (GPIO_NUM_44) +#define BSP_LCD_TOUCH_INT (GPIO_NUM_10) + +/* Power */ +#define BSP_POWER_OFF (GPIO_NUM_9) + +/* SD card */ +#define BSP_SD_D0 (GPIO_NUM_17) +#define BSP_SD_CMD (GPIO_NUM_38) +#define BSP_SD_CLK (GPIO_NUM_16) + +/* USB */ +#define BSP_USB_DM (GPIO_NUM_19) +#define BSP_USB_DP (GPIO_NUM_20) + +/* Others */ +#define BSP_UART1_TX (GPIO_NUM_5) +#define BSP_UART1_RX (GPIO_NUM_4) +#define BSP_TOUCH_PAD1 (GPIO_NUM_7) +#define BSP_TOUCH_PAD2 (GPIO_NUM_6) +#define BSP_HEAD_LED (GPIO_NUM_43) // GREEN LED + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * @brief BSP display configuration structure + * + */ +typedef struct { + lvgl_port_cfg_t lvgl_port_cfg; /*!< LVGL port configuration */ + uint32_t buffer_size; /*!< Size of the buffer for the screen in pixels */ + bool double_buffer; /*!< True, if should be allocated two buffers */ + struct { + unsigned int buff_dma : 1; /*!< Allocated LVGL buffer will be DMA capable */ + unsigned int buff_spiram : 1; /*!< Allocated LVGL buffer will be in PSRAM */ + unsigned int default_dummy_draw : 1; /* Use dummy draw to bypass the display driver */ + } flags; +} bsp_display_cfg_t; + +/************************************************************************************************** + * + * I2C interface + * + * There are multiple devices connected to I2C peripheral: + * - Codec ES8311 (configuration only) + * - ADC ES7210 (configuration only) + * - Encryption chip ATECC608A (NOT populated on most boards) + * - LCD Touch controller + * - Inertial Measurement Unit ICM-42607-P + * + * After initialization of I2C, use BSP_I2C_NUM macro when creating I2C devices drivers ie.: + * \code{.c} + * icm42670_handle_t imu = icm42670_create(BSP_I2C_NUM, ICM42670_I2C_ADDRESS); + * \endcode + **************************************************************************************************/ +#define BSP_I2C_NUM CONFIG_BSP_I2C_NUM + +/** + * @brief Init I2C driver + * + * @return + * - ESP_OK On success + * - ESP_ERR_INVALID_ARG I2C parameter error + * - ESP_FAIL I2C driver installation error + * + */ +esp_err_t bsp_i2c_init(void); + +/** + * @brief Deinit I2C driver and free its resources + * + * @return + * - ESP_OK On success + * - ESP_ERR_INVALID_ARG I2C parameter error + * + */ +esp_err_t bsp_i2c_deinit(void); + +/** + * @brief Set I2C driver handle + * + * @param handle I2C handle + */ +void bsp_i2c_set_handle(i2c_master_bus_handle_t handle); + +/** + * @brief Get I2C driver handle + * + * @return + * - I2C handle + */ +i2c_master_bus_handle_t bsp_i2c_get_handle(void); + +/************************************************************************************************** + * + * LCD interface + * + * ESP-BOX is shipped with 2.4inch ST7789 display controller. + * It features 16-bit colors, 320x240 resolution and capacitive touch controller. + * + * LVGL is used as graphics library. LVGL is NOT thread safe, therefore the user must take LVGL mutex + * by calling bsp_display_lock() before calling and LVGL API (lv_...) and then give the mutex with + * bsp_display_unlock(). + * + * Display's backlight must be enabled explicitly by calling bsp_display_backlight_on() + **************************************************************************************************/ +#define BSP_LCD_PIXEL_CLOCK_HZ (40 * 1000 * 1000) +#define BSP_LCD_SPI_NUM (SPI2_HOST) + +/** + * @brief Initialize display + * + * This function initializes SPI, display controller and starts LVGL handling task. + * LCD backlight must be enabled separately by calling bsp_display_brightness_set() + * + * @return + */ +lv_disp_t *bsp_display_start(void); + +/** + * @brief Initialize display + * + * This function initializes SPI, display controller and starts LVGL handling task. + * LCD backlight must be enabled separately by calling bsp_display_brightness_set() + * + * @param cfg display configuration + * + * @return + */ +lv_disp_t *bsp_display_start_with_config(const bsp_display_cfg_t *cfg); + +/** + * @brief Get pointer to input device (touch, buttons, ...) + * + * @note The LVGL input device is initialized in bsp_display_start() function. + * + * @return + */ +lv_indev_t *bsp_display_get_input_dev(void); + +/** + * @brief Take LVGL mutex + * + * @param timeout_ms Timeout in [ms]. 0 will block indefinitely. + * @return + * @return + */ +bool bsp_display_lock(uint32_t timeout_ms); + +/** + * @brief Give LVGL mutex + * + */ +void bsp_display_unlock(void); + +/** + * @brief Initialize display brightness + * + * @return + * - ESP_OK On success + * - ESP_ERR_INVALID_ARG I2C parameter error + * - ESP_FAIL I2C driver installation error + * + */ +esp_err_t bsp_display_brightness_init(void); + +/** + * @brief Initialize power + * + * @param power_en power enable + * + * @return + * - ESP_OK On success + * - ESP_ERR_INVALID_ARG I2C parameter error + * - ESP_FAIL I2C driver installation error + * + */ +esp_err_t bsp_power_init(bool power_en); + +/************************************************************************************************** + * + * SD card + * + * After mounting the SD card, it can be accessed with stdio functions ie.: + * \code{.c} + * FILE* f = fopen(BSP_SD_MOUNT_POINT"/hello.txt", "w"); + * fprintf(f, "Hello %s!\n", bsp_sdcard->cid.name); + * fclose(f); + * \endcode + * + * @attention IO2 is also routed to RGB LED and push button + **************************************************************************************************/ +#define BSP_SD_MOUNT_POINT CONFIG_BSP_SD_MOUNT_POINT + +/** + * @brief BSP SD card configuration structure + */ +typedef struct { + const esp_vfs_fat_sdmmc_mount_config_t *mount; + sdmmc_host_t *host; + union { + const sdmmc_slot_config_t *sdmmc; + } slot; +} bsp_sdcard_cfg_t; + +/** + * @brief Mount microSD card to virtual file system + * + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_STATE if esp_vfs_fat_sdmmc_mount was already called + * - ESP_ERR_NO_MEM if memory can not be allocated + * - ESP_FAIL if partition can not be mounted + * - other error codes from SDMMC or SPI drivers, SDMMC protocol, or FATFS drivers + */ +esp_err_t bsp_sdcard_mount(void); + +/** + * @brief Unmount micorSD card from virtual file system + * + * @return + * - ESP_OK on success + * - ESP_ERR_NOT_FOUND if the partition table does not contain FATFS partition with given label + * - ESP_ERR_INVALID_STATE if esp_vfs_fat_spiflash_mount was already called + * - ESP_ERR_NO_MEM if memory can not be allocated + * - ESP_FAIL if partition can not be mounted + * - other error codes from wear levelling library, SPI flash driver, or FATFS drivers + */ +esp_err_t bsp_sdcard_unmount(void); + +/** + * @brief Get SD card handle + * + * @return + */ +sdmmc_card_t *bsp_sdcard_get_handle(void); + +/** + * @brief Get SD card MMC host config + * + * @param slot SD card slot + * @param config Structure which will be filled + */ +void bsp_sdcard_get_sdmmc_host(const int slot, sdmmc_host_t *config); + +/** + * @brief Get SD card MMC slot config + * + * @param slot SD card slot + * @param config Structure which will be filled + */ +void bsp_sdcard_sdmmc_get_slot(const int slot, sdmmc_slot_config_t *config); + +/** + * @brief Mount microSD card to virtual file system (MMC mode) + * + * @param cfg SD card configuration + * + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_STATE if esp_vfs_fat_sdmmc_mount was already called + * - ESP_ERR_NO_MEM if memory cannot be allocated + * - ESP_FAIL if partition cannot be mounted + * - other error codes from SDMMC or SPI drivers, SDMMC protocol, or FATFS drivers + */ +esp_err_t bsp_sdcard_sdmmc_mount(bsp_sdcard_cfg_t *cfg); + +/** + * @brief Get SD card handle + * + * @return + */ +sdmmc_card_t *bsp_sdcard_get_handle(void); + +/** + * @brief Set peripheral power + * + * @param on true to turn on, false to turn off + * + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_ARG if the parameter is invalid + */ +esp_err_t bsp_set_peripheral_power(bool on); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/echoear/include/bsp/esp-bsp.h b/firmware/echoear-platform-agent/components/echoear/include/bsp/esp-bsp.h new file mode 100644 index 0000000..9238f81 --- /dev/null +++ b/firmware/echoear-platform-agent/components/echoear/include/bsp/esp-bsp.h @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once +#include "bsp/echoear.h" diff --git a/firmware/echoear-platform-agent/components/echoear/include/bsp/touch.h b/firmware/echoear-platform-agent/components/echoear/include/bsp/touch.h new file mode 100644 index 0000000..3c9857c --- /dev/null +++ b/firmware/echoear-platform-agent/components/echoear/include/bsp/touch.h @@ -0,0 +1,51 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief BSP Touchscreen + * + * This file offers API for basic touchscreen initialization. + * It is useful for users who want to use the touchscreen without the default Graphical Library LVGL. + * + * For standard LCD initialization with LVGL graphical library, you can call all-in-one function bsp_display_start(). + */ + +#pragma once +#include "esp_lcd_touch.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief BSP touch configuration structure + * + */ +typedef struct { + void *dummy; /*!< Prepared for future use. */ +} bsp_touch_config_t; + +/** + * @brief Create new touchscreen + * + * If you want to free resources allocated by this function, you can use esp_lcd_touch API, ie.: + * + * \code{.c} + * esp_lcd_touch_del(tp); + * \endcode + * + * @param[in] config touch configuration + * @param[out] ret_touch esp_lcd_touch touchscreen handle + * @return + * - ESP_OK On success + * - Else esp_lcd_touch failure + */ +esp_err_t bsp_touch_new(const bsp_touch_config_t *config, esp_lcd_touch_handle_t *ret_touch); + +#ifdef __cplusplus +} +#endif diff --git a/firmware/echoear-platform-agent/components/echoear/priv_include/bsp_err_check.h b/firmware/echoear-platform-agent/components/echoear/priv_include/bsp_err_check.h new file mode 100644 index 0000000..e001999 --- /dev/null +++ b/firmware/echoear-platform-agent/components/echoear/priv_include/bsp_err_check.h @@ -0,0 +1,58 @@ +/* + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "esp_check.h" +#include "sdkconfig.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Assert on error, if selected in menuconfig. Otherwise return error code. */ +#if CONFIG_BSP_ERROR_CHECK +#define BSP_ERROR_CHECK_RETURN_ERR(x) ESP_ERROR_CHECK(x) +#define BSP_ERROR_CHECK_RETURN_NULL(x) ESP_ERROR_CHECK(x) +#define BSP_ERROR_CHECK(x, ret) ESP_ERROR_CHECK(x) +#define BSP_NULL_CHECK(x, ret) assert(x) +#define BSP_NULL_CHECK_GOTO(x, goto_tag) assert(x) +#else +#define BSP_ERROR_CHECK_RETURN_ERR(x) do { \ + esp_err_t err_rc_ = (x); \ + if (unlikely(err_rc_ != ESP_OK)) { \ + return err_rc_; \ + } \ + } while(0) + +#define BSP_ERROR_CHECK_RETURN_NULL(x) do { \ + if (unlikely((x) != ESP_OK)) { \ + return NULL; \ + } \ + } while(0) + +#define BSP_NULL_CHECK(x, ret) do { \ + if ((x) == NULL) { \ + return ret; \ + } \ + } while(0) + +#define BSP_ERROR_CHECK(x, ret) do { \ + if (unlikely((x) != ESP_OK)) { \ + return ret; \ + } \ + } while(0) + +#define BSP_NULL_CHECK_GOTO(x, goto_tag) do { \ + if ((x) == NULL) { \ + goto goto_tag; \ + } \ + } while(0) +#endif + +#ifdef __cplusplus +} +#endif diff --git a/firmware/echoear-platform-agent/components/esp-gmf/.codespellrc b/firmware/echoear-platform-agent/components/esp-gmf/.codespellrc new file mode 100644 index 0000000..957b32b --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/.codespellrc @@ -0,0 +1,4 @@ +[codespell] +skip = build,*.yuv,alice.txt,*.rgb +ignore-words-list = ser,dout,rsource,fram,inout,shs,ans,aci,unstall,unstalling,hart,wheight,wel,ot,afe,muxer,parm +write-changes = true diff --git a/firmware/echoear-platform-agent/components/esp-gmf/.github/workflows/sync-jira.yml b/firmware/echoear-platform-agent/components/esp-gmf/.github/workflows/sync-jira.yml new file mode 100644 index 0000000..65a5c7b --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/.github/workflows/sync-jira.yml @@ -0,0 +1,53 @@ +--- +# FILE: .github/workflows/sync-jira.yml + +# This GitHub Actions workflow synchronizes GitHub issues, comments, and pull requests with Jira. +# It triggers on new issues, issue comments, and on a scheduled basis. +# The workflow uses a custom action to perform the synchronization with Jira (espressif/sync-jira-actions). + +name: 🔷 Sync to Jira + +run-name: > + Sync to Jira - + ${{ github.event_name == 'issue_comment' && 'Issue Comment' || + github.event_name == 'schedule' && 'New Pull Requests' || + github.event_name == 'issues' && 'New Issue' || + github.event_name == 'workflow_dispatch' && 'Manual Sync' }} + +on: + issues: {types: [opened]} + issue_comment: {types: [created, edited, deleted]} + schedule: [cron: '0 * * * *'] + workflow_dispatch: + inputs: + action: {description: 'Action to be performed', required: true, default: 'mirror-issues'} + issue-numbers: {description: 'Issue numbers to sync (comma-separated)', required: true} + +jobs: + sync-to-jira: + name: > + Sync to Jira - + ${{ github.event_name == 'issue_comment' && 'Issue Comment' || + github.event_name == 'schedule' && 'New Pull Requests' || + github.event_name == 'issues' && 'New Issue' || + github.event_name == 'workflow_dispatch' && 'Manual Sync' }} + runs-on: ubuntu-latest + permissions: + contents: read + issues: write + pull-requests: write + steps: + - name: Check out + uses: actions/checkout@v4 + + - name: Run synchronization to Jira + uses: espressif/sync-jira-actions@v1 + with: + cron_job: ${{ github.event_name == 'schedule' && 'true' || '' }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + JIRA_PASS: ${{ secrets.JIRA_PASS }} + JIRA_PROJECT: AUD + JIRA_COMPONENT: GMF_GitHub + JIRA_URL: ${{ secrets.JIRA_URL }} + JIRA_USER: ${{ secrets.JIRA_USER }} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/.github/workflows/upload_component.yml b/firmware/echoear-platform-agent/components/esp-gmf/.github/workflows/upload_component.yml new file mode 100644 index 0000000..f5d41d7 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/.github/workflows/upload_component.yml @@ -0,0 +1,31 @@ +name: Push components to https://components.espressif.com +on: + push: + branches: + - main +jobs: + upload_components: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: "recursive" + - name: Upload components to the component registry + uses: espressif/upload-components-ci-action@v2 + with: + components: | + gmf_core:./gmf_core + gmf_audio:./elements/gmf_audio + gmf_io:./elements/gmf_io + gmf_misc:./elements/gmf_misc + gmf_ai_audio:./elements/gmf_ai_audio + gmf_video:./elements/gmf_video + gmf_loader:./packages/gmf_loader + esp_audio_simple_player:./packages/esp_audio_simple_player + esp_capture: ./packages/esp_capture + gmf_app_utils: ./packages/gmf_app_utils + esp_board_manager: ./packages/esp_board_manager + esp_audio_render: ./packages/esp_audio_render + gmf_examples: ./gmf_examples + namespace: "espressif" + api_token: ${{ secrets.IDF_COMPONENT_API_TOKEN }} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/.gitignore b/firmware/echoear-platform-agent/components/esp-gmf/.gitignore new file mode 100644 index 0000000..564f9d7 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/.gitignore @@ -0,0 +1,54 @@ +.config +*.o +*.pyc + +# gtags +GTAGS +GRTAGS +GPATH + +# emacs +.dir-locals.el + +# emacs temp file suffixes +*~ +.#* +\#*# +# MacOS directory files +.DS_Store + +# eclipse setting +.settings + +# Example project files + +**/sdkconfig +**/sdkconfig.old +**/build +**/managed_components +**/dependencies.lock +**/dist +**/gen_bmgr_codes/ + +# Doc build artifacts +gmf_core/docs/*/_build/ +gmf_core/docs/*/doxygen-warning-log.txt +gmf_core/docs/*/sphinx-warning-log.txt +gmf_core/docs/*/sphinx-warning-log-sanitized.txt +gmf_core/docs/*/xml/ +gmf_core/docs/*/xml_in/ +gmf_core/docs/*/man/ +gmf_core/docs/*/doxygen_sqlite3.db + +# Downloaded font files +gmf_core/docs/_static/DejaVuSans.ttf +gmf_core/docs/_static/NotoSansSC-Regular.otf + +# gcov coverage reports +*.gcda +*.gcno +coverage.info +coverage_report/ + +.vscode +.cache/ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/.gitlab-ci.yml b/firmware/echoear-platform-agent/components/esp-gmf/.gitlab-ci.yml new file mode 100644 index 0000000..5c86f6d --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/.gitlab-ci.yml @@ -0,0 +1,33 @@ +stages: + - pre_check + - build + - target_test + - deploy + +variables: + GIT_STRATEGY: clone + GET_SOURCES_ATTEMPTS: "10" + ARTIFACT_DOWNLOAD_ATTEMPTS: "10" + GIT_SUBMODULE_STRATEGY: none + IDF_SKIP_CHECK_SUBMODULES: 1 + GIT_DEPTH: 1 + IDF_PATH: "/root/.espressif/esp-idf" + PROJECT_PATH: "${CI_PROJECT_DIR}" + BASE_FRAMEWORK: $IDF_REPOSITORY + BASE_FRAMEWORK_PATH: "${IDF_PATH}" + TARGET_DIR_TYPE: "example" + EXAMPLES_PATH: "${CI_PROJECT_DIR}" + +before_script: + - source ${CI_PROJECT_DIR}/tools/ci/utils.sh + - add_gitlab_ssh_keys + +include: + - '.gitlab/ci/rules.yml' + - '.gitlab/ci/common.yml' + - '.gitlab/ci/pre_check.yml' + - '.gitlab/ci/build_examples.yml' + - '.gitlab/ci/build_ut.yml' + - '.gitlab/ci/target_ut.yml' + - '.gitlab/ci/target_examples.yml' + - '.gitlab/ci/deploy.yml' diff --git a/firmware/echoear-platform-agent/components/esp-gmf/.gitlab/ci/.build_test_rules.yml b/firmware/echoear-platform-agent/components/esp-gmf/.gitlab/ci/.build_test_rules.yml new file mode 100644 index 0000000..05c60d3 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/.gitlab/ci/.build_test_rules.yml @@ -0,0 +1,19 @@ +# The file mainly marks apps with special rules, while other apps are enabled by default + +.disable_test_apps: &disable_test_apps + disable: + - if: TARGET_DIR_TYPE == "test_apps" + +.disable_example: &disable_example + disable: + - if: TARGET_DIR_TYPE == "example" + +.disable_test_apps_esp32: &disable_test_apps_esp32 + disable: + - if: TARGET_DIR_TYPE == "test_apps" AND IDF_TARGET == "esp32" + +gmf_core/test_apps: + <<: *disable_example + +packages/esp_board_manager: + <<: *disable_test_apps_esp32 \ No newline at end of file diff --git a/firmware/echoear-platform-agent/components/esp-gmf/.gitlab/ci/build_examples.yml b/firmware/echoear-platform-agent/components/esp-gmf/.gitlab/ci/build_examples.yml new file mode 100644 index 0000000..7d0e237 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/.gitlab/ci/build_examples.yml @@ -0,0 +1,72 @@ +.build_cmake_template: &build_cmake_template + stage: build + image: ${IMAGE} + tags: + - multimedia_build + parallel: 2 + interruptible: true + dependencies: # set dependencies to null to avoid missing artifacts issue + variables: + DEEP_CLONE: "false" + IDF_FIND_APP_PATH_ARG: "-p" + SDKCFG_DEFAULTS: "sdkconfig.defaults.$IDF_TARGET" + BASE_FRAMEWORK_PATH: "${IDF_PATH}" + BASE_FRAMEWORK: $IDF_REPOSITORY + artifacts: + paths: + - "**/build*/size.json" + - "**/build*/build_log.txt" + - "**/build*/*.bin" + - "**/build*/*.elf" + - "**/build*/flasher_args.json" + - "**/build*/flash_project_args" + - "**/build*/config/sdkconfig.json" + - "**/build*/sdkconfig" + - "**/build*/bootloader/*.bin" + - "**/build*/partition_table/*.bin" + - "**/build*/srmodels/*.bin" + when: always + expire_in: 4 days + extends: + - .build_template + script: + - python $CI_PROJECT_DIR/tools/ci/esp_bmgr_pre_build.py --target $IDF_TARGET --board $AUDIO_BOARD --project-path ${EXAMPLES_PATH[@]} --target-dir-type ${TARGET_DIR_TYPE} + - run_cmd python $CI_PROJECT_DIR/tools/ci/build_apps.py ${EXAMPLES_PATH[@]} \ + -t $IDF_TARGET -vv \ + --target-dir-type ${TARGET_DIR_TYPE} \ + --build-dir ${IDF_VERSION_TAG}/build_@t_@w \ + --parallel-count ${parallel##*/} \ + --parallel-index ${parallel%%/*} + +.matrix_template: &matrix_template + parallel: + matrix: + - IDF_VERSION_TAG: ['v5.4.3'] + parallel: [ 1/1 ] + +build_examples_esp32_lyrat_mini_v1_1: + extends: + - .rules:build:protected-merge-requests-pipeline + - .build_cmake_template + - .matrix_template + variables: + IDF_TARGET: esp32 + AUDIO_BOARD: lyrat_mini_v1_1 + +.build_examples_esp32s2_kaluga_v1_2: + extends: + - .rules:build:protected-merge-requests-pipeline + - .build_cmake_template + - .matrix_template + variables: + IDF_TARGET: esp32s2 + AUDIO_BOARD: '' + +build_examples_esp32s3_korvo_v2: + extends: + - .rules:build:protected-merge-requests-pipeline + - .build_cmake_template + - .matrix_template + variables: + IDF_TARGET: esp32s3 + AUDIO_BOARD: esp32_s3_korvo2_v3 diff --git a/firmware/echoear-platform-agent/components/esp-gmf/.gitlab/ci/build_ut.yml b/firmware/echoear-platform-agent/components/esp-gmf/.gitlab/ci/build_ut.yml new file mode 100644 index 0000000..7117415 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/.gitlab/ci/build_ut.yml @@ -0,0 +1,115 @@ +variables: + BASE_FRAMEWORK_PATH: "${IDF_PATH}" + BASE_FRAMEWORK: $IDF_REPOSITORY + IDF_TAG_FLAG: true + +.build_gmf_component: + interruptible: true + variables: + TARGET_DIR_TYPE: "test_apps" + TEST_DIR: ${CI_PROJECT_DIR}/gmf_core + parallel: + matrix: + - IDF_VERSION_TAG: ['v5.4.3'] + parallel: [ 1/1 ] + extends: + - .rules:build:protected-merge-requests-pipeline + - .build_template + artifacts: + paths: + - "**/build*/size.json" + - "**/build*/build_log.txt" + - "**/build*/*.bin" + - "**/build*/*.elf" + - "**/build*/flasher_args.json" + - "**/build*/flash_project_args" + - "**/build*/config/sdkconfig.json" + - "**/build*/sdkconfig" + - "**/build*/bootloader/*.bin" + - "**/build*/partition_table/*.bin" + - "**/build*/srmodels/*.bin" + - "${TEST_DIR}/**/test_apps/*.py" + expire_in: 4 days + script: + - python $CI_PROJECT_DIR/tools/ci/esp_bmgr_pre_build.py --target $IDF_TARGET --board $AUDIO_BOARD --project-path ${TEST_DIR} --target-dir-type ${TARGET_DIR_TYPE} + - run_cmd python $CI_PROJECT_DIR/tools/ci/build_apps.py ${TEST_DIR} \ + -t $IDF_TARGET -vv \ + --target-dir-type ${TARGET_DIR_TYPE} \ + --build-dir ${IDF_VERSION_TAG}/build_@t_@w \ + --parallel-count ${parallel##*/} \ + --parallel-index ${parallel%%/*} + - python $CI_PROJECT_DIR/tools/ci/check_test_cxx_build.py + +.build_ut_gmf_packages: + extends: + - .build_gmf_component + variables: + TEST_DIR: ${CI_PROJECT_DIR}/packages + +.build_ut_gmf_elements: + extends: + - .build_gmf_component + variables: + TEST_DIR: ${CI_PROJECT_DIR}/elements + +build_ut_gmf_core_esp32: + extends: + - .build_gmf_component + variables: + IDF_TARGET: esp32 + AUDIO_BOARD: lyrat_mini_v1_1 + +.build_ut_gmf_core_esp32c3: + extends: + - .build_gmf_component + variables: + IDF_TARGET: esp32c3 + +build_ut_gmf_core_esp32s3: + extends: + - .build_gmf_component + variables: + IDF_TARGET: esp32s3 + AUDIO_BOARD: esp32_s3_korvo2_v3 + +.build_ut_gmf_core_esp32p4: + extends: + - .build_gmf_component + variables: + IDF_TARGET: esp32p4 + AUDIO_BOARD: esp32_p4_function_ev + +build_ut_gmf_elements_esp32: + extends: + - .build_ut_gmf_elements + variables: + IDF_TARGET: esp32 + AUDIO_BOARD: lyrat_mini_v1_1 + +build_ut_gmf_elements_esp32s3: + extends: + - .build_ut_gmf_elements + variables: + IDF_TARGET: esp32s3 + AUDIO_BOARD: esp32_s3_korvo2_v3 + +.build_ut_gmf_elements_esp32p4: + extends: + - .build_ut_gmf_elements + variables: + IDF_TARGET: esp32p4 + AUDIO_BOARD: esp32_p4_function_ev + +build_ut_gmf_packages_esp32: + extends: + - .build_ut_gmf_packages + variables: + IDF_TARGET: esp32 + AUDIO_BOARD: lyrat_mini_v1_1 + +build_ut_gmf_packages_esp32s3: + extends: + - .build_ut_gmf_packages + variables: + IDF_TARGET: esp32s3 + AUDIO_BOARD: esp32_s3_korvo2_v3 diff --git a/firmware/echoear-platform-agent/components/esp-gmf/.gitlab/ci/common.yml b/firmware/echoear-platform-agent/components/esp-gmf/.gitlab/ci/common.yml new file mode 100644 index 0000000..9895a18 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/.gitlab/ci/common.yml @@ -0,0 +1,47 @@ +variables: + IDF_TAG_FLAG: true + IDF_EXTRA_ACTIONS_PATH: ${CI_PROJECT_DIR}/packages/esp_board_manager + +.update_source: &update_source + - source ${CI_PROJECT_DIR}/tools/ci/utils.sh + - add_gitlab_ssh_keys + - git submodule update --init --recursive --depth 1 + - git clone --depth 1 -b $IDF_VERSION_TAG $IDF_REPOSITORY $IDF_PATH + - fetch_idf_branch $IDF_VERSION_TAG + - cd $BASE_FRAMEWORK_PATH + - git log -2 + - common_before_scripts + - setup_tools_and_idf_python_venv + - set_env_variable + - cd ${CI_PROJECT_DIR} + +.build_template: + stage: build + image: ${IMAGE} + interruptible: true + tags: + - multimedia_build + before_script: + - *update_source + after_script: + - find "${TEST_DIR}" -type d \( -name "managed_components" -o -name "build" \) -exec rm -rf {} + + +.target_test_template: + stage: target_test + image: "$CI_DOCKER_REGISTRY/target-test-env-v5.4:2" + interruptible: true + timeout: 1 hour + retry: 2 + cache: + # Usually do not need submodule-cache in target_test + - key: pip-cache + paths: + - .cache/pip + policy: pull + before_script: + - source ${CI_PROJECT_DIR}/tools/ci/utils.sh + - add_gitlab_ssh_keys + - git clone --depth 1 -b $IDF_VERSION_TAG $IDF_REPOSITORY $IDF_PATH + - check_idf_version $IDF_VERSION_TAG + - export PYTHONPATH="$IDF_PATH/tools:$IDF_PATH/tools/ci/python_packages:$PYTHONPATH" + - export PYTHONPATH="$CI_PROJECT_DIR/tools:$PYTHONPATH" diff --git a/firmware/echoear-platform-agent/components/esp-gmf/.gitlab/ci/deploy.yml b/firmware/echoear-platform-agent/components/esp-gmf/.gitlab/ci/deploy.yml new file mode 100644 index 0000000..218f07e --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/.gitlab/ci/deploy.yml @@ -0,0 +1,21 @@ +push_to_github: + stage: deploy + rules: + - if: + '($CI_COMMIT_REF_NAME == "main" || + $CI_COMMIT_BRANCH =~ /^release\/v/ || + $CI_COMMIT_TAG =~ /^v\d+\.\d+(\.\d+)?($|-)/) && + $CI_PIPELINE_SOURCE != "schedule"' + when: on_success + image: $CI_DOCKER_REGISTRY/esp32-ci-env + variables: + GIT_STRATEGY: clone + before_script: + - echo "skip default before_script" + script: + - source ${CI_PROJECT_DIR}/tools/ci/utils.sh + - add_github_ssh_keys + - git fetch --unshallow + - git remote remove github &>/dev/null || true + - git remote add github ${TARGET_GITHUB_REPO} + - push_to_github diff --git a/firmware/echoear-platform-agent/components/esp-gmf/.gitlab/ci/pre_check.yml b/firmware/echoear-platform-agent/components/esp-gmf/.gitlab/ci/pre_check.yml new file mode 100644 index 0000000..b217428 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/.gitlab/ci/pre_check.yml @@ -0,0 +1,102 @@ +.pre_check_template: + stage: pre_check + image: "${CI_DOCKER_REGISTRY}/esp-env-v5.3:1" + interruptible: true + tags: + - host_test + dependencies: [] + +.check_branch_script: &check_branch_script | + if [[ "$BOT_LABEL_SKIP_BRANCH_CHECK" = "true" || "$CI_MERGE_REQUEST_LABELS" =~ skip_branch_check ]]; then + echo "Skip branch name check." + else + ${CHECK_TOOLS_PATH}/check_commit_format branch-name + fi + +.check_commit_script: &check_commit_script | + if [[ "$BOT_LABEL_SKIP_COMMIT_CHECK" = "true" || "$CI_MERGE_REQUEST_LABELS" =~ skip_commit_check ]]; then + echo "Skip commit message check." + else + ${CHECK_TOOLS_PATH}/check_commit_format commit-message + fi + +.check_code_script: &check_code_script | + if [[ "$BOT_LABEL_SKIP_CODE_CHECK" = "true" || "$CI_MERGE_REQUEST_LABELS" =~ skip_code_check ]]; then + echo "Skip code style check." + else + ${CHECK_TOOLS_PATH}/check_commit_format --verbose --format code-style; + if [ -d "${CHECK_REPO_PATH}/formatted" ] && [ "$(ls -A ${CHECK_REPO_PATH}/formatted)" ]; then + git diff -- . ':(exclude).gitmodules' > ${CHECK_REPO_PATH}/patch/code_format.patch + exit 1 + fi + fi + +check_commit_format: + image: "$CI_DOCKER_IMAGE_REGISTRY/pre_commit:1" + interruptible: true + stage: pre_check + rules: + - if: '$CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_LABELS !~ /^(?:[^,\n\r]+,)*skip_check(?:,[^,\n\r]+)*$/i' + script: + - git clone --depth 1 $CHECK_FORMAT_TOOL_REPO + - cd check-format-tool/bin + - export CHECK_REPO_PATH=${CI_PROJECT_DIR} + - ./install.sh + - . ./export.sh + - cd ${CHECK_REPO_PATH}; + - *check_branch_script + - *check_commit_script + - *check_code_script + - check_project_root_dir_change + allow_failure: true + artifacts: + paths: + - ${CI_PROJECT_DIR}/patch + - ${CI_PROJECT_DIR}/formatted + when: always + expire_in: 4 days + +check_pre_commit: + extends: + - .rules:build:protected-merge-requests + - .pre_check_template + before_script: + - pip install pre-commit + script: + - | + # merged results pipelines, by default + if [[ -n $CI_MERGE_REQUEST_SOURCE_BRANCH_SHA ]]; then + git fetch origin $CI_MERGE_REQUEST_DIFF_BASE_SHA --depth=1 ${GIT_FETCH_EXTRA_FLAGS} + git fetch origin $CI_MERGE_REQUEST_SOURCE_BRANCH_SHA --depth=1 ${GIT_FETCH_EXTRA_FLAGS} + export GIT_DIFF_OUTPUT=$(git diff --name-only $CI_MERGE_REQUEST_DIFF_BASE_SHA $CI_MERGE_REQUEST_SOURCE_BRANCH_SHA) + # merge request pipelines, when the mr got conflicts + elif [[ -n $CI_MERGE_REQUEST_DIFF_BASE_SHA ]]; then + git fetch origin $CI_MERGE_REQUEST_DIFF_BASE_SHA --depth=1 ${GIT_FETCH_EXTRA_FLAGS} + git fetch origin $CI_COMMIT_SHA --depth=1 ${GIT_FETCH_EXTRA_FLAGS} + export GIT_DIFF_OUTPUT=$(git diff --name-only $CI_MERGE_REQUEST_DIFF_BASE_SHA $CI_COMMIT_SHA) + # other pipelines, like the protected branches pipelines + elif [[ "$CI_COMMIT_BEFORE_SHA" != "0000000000000000000000000000000000000000" ]]; then + git fetch origin $CI_COMMIT_BEFORE_SHA --depth=1 ${GIT_FETCH_EXTRA_FLAGS} + git fetch origin $CI_COMMIT_SHA --depth=1 ${GIT_FETCH_EXTRA_FLAGS} + export GIT_DIFF_OUTPUT=$(git diff --name-only $CI_COMMIT_BEFORE_SHA $CI_COMMIT_SHA) + else + # pipeline source could be web, scheduler, etc. + git fetch origin $CI_COMMIT_SHA --depth=2 ${GIT_FETCH_EXTRA_FLAGS} + export GIT_DIFF_OUTPUT=$(git diff --name-only $CI_COMMIT_SHA~1 $CI_COMMIT_SHA) + fi + - MODIFIED_FILES=$(echo $GIT_DIFF_OUTPUT | xargs) + - echo "MODIFIED_FILES=$MODIFIED_FILES" >> pipeline.env + - pre-commit install --allow-missing-config -t pre-commit -t commit-msg + - pre-commit run --files $MODIFIED_FILES + + after_script: + - git diff > fixed.patch + - cat fixed.patch + + artifacts: + reports: + dotenv: pipeline.env + paths: + - pipeline.env + expire_in: 1 week + when: always diff --git a/firmware/echoear-platform-agent/components/esp-gmf/.gitlab/ci/rules.yml b/firmware/echoear-platform-agent/components/esp-gmf/.gitlab/ci/rules.yml new file mode 100644 index 0000000..e6de546 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/.gitlab/ci/rules.yml @@ -0,0 +1,118 @@ +.patterns-build_components: &patterns-build_components + - ".gitlab/**/*" + - "tools/**/*" + - "elements/**/*" + - "gmf_core/**/*" + - "packages/**/*" + +############## +# if anchors # +############## +.if-web: &if-web + if: '$CI_PIPELINE_SOURCE == "web"' + +.if-ref-main: &if-ref-main + if: '$CI_COMMIT_REF_NAME == "main"' + +.if-trigger: &if-trigger + if: '$CI_PIPELINE_SOURCE == "trigger"' + +.if-pipeline: &if-pipeline + if: '$CI_PIPELINE_SOURCE == "pipeline"' + +.if-schedule: &if-schedule + if: '$CI_PIPELINE_SOURCE == "schedule"' + +.if-merge_request_event: &if-merge_request_event + if: '$CI_PIPELINE_SOURCE == "merge_request_event"' + +.if-open-merge-request: &if-open-merge-request + if: '$CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS && ($CI_PIPELINE_SOURCE == "push")' + +.if-dev-push: &if-dev-push + if: '$CI_COMMIT_REF_NAME != "main" && $CI_COMMIT_BRANCH !~ /^release\/v/ && $CI_COMMIT_TAG !~ /^v\d+\.\d+(\.\d+)?($|-)/ && ($CI_PIPELINE_SOURCE == "push")' + +.if-protected: &if-protected + if: '($CI_COMMIT_REF_NAME == "main" || $CI_COMMIT_BRANCH =~ /^release\/v/ || $CI_COMMIT_TAG =~ /^v\d+\.\d+(\.\d+)?($|-)/)' + +.if-label-skip_unit_test: &if-label-skip_unit_test + if: '$BOT_LABEL_SKIP_UNIT_TEST || $CI_MERGE_REQUEST_LABELS =~ /^(?:[^,\n\r]+,)*skip_unit_test(?:,[^,\n\r]+)*$/i' + +.if-label-skip_example_test: &if-label-skip_example_test + if: '$BOT_LABEL_SKIP_EXAMPLE_TEST || $CI_MERGE_REQUEST_LABELS =~ /^(?:[^,\n\r]+,)*skip_example_test(?:,[^,\n\r]+)*$/i' + +.if-label-skip_example: &if-label-skip_example + if: '$BOT_LABEL_SKIP_EXAMPLE_TEST || $CI_MERGE_REQUEST_LABELS =~ /^(?:[^,\n\r]+,)*skip_example(?:,[^,\n\r]+)*$/i' + +.idf-version-tag-v5-3: &idf-version-tag-v5-3 + if: '$IDF_VERSION_TAG == "v5.3"' + variables: + IMAGE: "$CI_DOCKER_IMAGE_REGISTRY/env_v5.3:1" + +.idf-version-tag-v5-4: &idf-version-tag-v5-4 + if: '$IDF_VERSION_TAG == "v5.4.3"' + variables: + IMAGE: "$CI_IDF_DOCKER_REGISTRY/esp-env-v5.4:2" + +.rules:ref:main-schedule: + rules: + - <<: *if-ref-main + - <<: *if-schedule + +.rules:ref:check-label: + rules: + - <<: *if-merge_request_event + +.rules:build:regular-board-idf-ver-tag: + rules: + - <<: *if-dev-push + when: never + - <<: *if-schedule + when: never + +.rules:build:non-regular-board-idf-ver-tag: + rules: + - <<: *if-dev-push + when: never + - <<: *if-merge_request_event + when: never + +.rules:build:protected-merge-requests-pipeline: + rules: + - <<: *if-dev-push + when: never + - <<: *idf-version-tag-v5-3 + - <<: *idf-version-tag-v5-4 + - <<: *if-web + - <<: *if-trigger + - <<: *if-pipeline + - <<: *if-protected + - <<: *if-merge_request_event + +.rules:build:protected-merge-requests: + rules: + - <<: *if-web + - <<: *if-protected + - <<: *if-merge_request_event + +.rules:build:enable-gmf-example-test: + rules: + - <<: *if-label-skip_example_test + when: never + - <<: *if-label-skip_example + when: never + - <<: *if-web + - <<: *if-protected + - <<: *if-merge_request_event + +.rules:build:enable-gmf-unit-test: + rules: + - <<: *if-dev-push + when: never + - <<: *if-label-skip_unit_test + when: never + - <<: *if-web + - <<: *if-protected + - <<: *if-trigger + - <<: *if-schedule + - changes: *patterns-build_components diff --git a/firmware/echoear-platform-agent/components/esp-gmf/.gitlab/ci/target_examples.yml b/firmware/echoear-platform-agent/components/esp-gmf/.gitlab/ci/target_examples.yml new file mode 100644 index 0000000..8db5735 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/.gitlab/ci/target_examples.yml @@ -0,0 +1,70 @@ +.target_example_test_template: + extends: + - .target_test_template + parallel: + matrix: + - IDF_VERSION_TAG: ['v5.4.3'] + variables: + GIT_DEPTH: 1 + SUBMODULES_TO_FETCH: "none" + IDF_TAG_FLAG: "true" + TEST_FW_PATH: "$CI_PROJECT_DIR/tools/tiny-test-fw" + LOG_PATH: "$CI_PROJECT_DIR/TEST_LOGS" + ENV_FILE: "${BASE_FRAMEWORK_PATH}/ci-test-runner-configs/$CI_RUNNER_DESCRIPTION/EnvConfig.yml" + artifacts: + when: always + paths: + - XUNIT_RESULT.xml + - "**/*.log" + - $LOG_PATH + - "$CI_PROJECT_DIR/**/output.wav" + reports: + junit: XUNIT_RESULT.xml + expire_in: 1 week + +.pytest_template: + extends: + - .target_example_test_template + script: + - pip install pytest pytest-embedded-idf pytest-embedded-serial-esp pytest-rerunfailures pytest-timeout idf_build_apps + - eval "$(python ${CI_PROJECT_DIR}/tools/ci/build_apps.py ${TEST_DIR} --find --target-dir-type ${TARGET_DIR_TYPE} --target ${IDF_TARGET} -vv)" + - pytest "${APP_LIST[@]}" --target ${IDF_TARGET} -m "${tags}" --junitxml=XUNIT_RESULT.xml --parallel-count 1 --parallel-index 1 + +.pytest_audio_loopback_template: + image: "$CI_DOCKER_IMAGE_REGISTRY/target_test_env_v5.3:2" + extends: + - .rules:build:enable-gmf-example-test + - .target_example_test_template + script: + - export PYTHONPATH="/usr/local/lib/python3.9/dist-packages:$PYTHONPATH" + - pip install pytest pytest-embedded-idf pytest-embedded-serial-esp pytest-rerunfailures pytest-timeout idf_build_apps + - arecord -l + - eval "$(python ${CI_PROJECT_DIR}/tools/ci/build_apps.py ${TEST_DIR} --find --target-dir-type ${TARGET_DIR_TYPE} --target ${IDF_TARGET} -vv)" + - pytest "${APP_LIST[@]}" --target ${IDF_TARGET} -m "${tags}" --junitxml=XUNIT_RESULT.xml --parallel-count 1 --parallel-index 1 + variables: + TEST_DIR: "${EXAMPLES_PATH}" + +.pytest_examples_dir_template: + extends: + - .rules:build:enable-gmf-example-test + - .pytest_template + variables: + TEST_DIR: "${EXAMPLES_PATH}" + +audio_playback_test_esp32: + extends: + - .pytest_audio_loopback_template + variables: + IDF_TARGET: esp32 + needs: + - 'build_examples_esp32_lyrat_mini_v1_1: [v5.4.3, 1/1]' + tags: [ esp32, AUDIO_LOOPBACK_ENV ] + +example_test_esp32s3: + extends: + - .pytest_examples_dir_template + variables: + IDF_TARGET: esp32s3 + needs: + - 'build_examples_esp32s3_korvo_v2: [v5.4.3, 1/1]' + tags: [ esp32s3 ] diff --git a/firmware/echoear-platform-agent/components/esp-gmf/.gitlab/ci/target_ut.yml b/firmware/echoear-platform-agent/components/esp-gmf/.gitlab/ci/target_ut.yml new file mode 100644 index 0000000..8666c19 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/.gitlab/ci/target_ut.yml @@ -0,0 +1,62 @@ +.pytest_ut_template: + extends: + - .target_test_template + variables: + IDF_VERSION_TAG: v5.4.3 + IDF_TAG_FLAG: true + TARGET_DIR_TYPE: "test_apps" + artifacts: + when: always + paths: + - XUNIT_RESULT.xml + - pytest-embedded/ + reports: + junit: XUNIT_RESULT.xml + expire_in: 1 week + script: + - cd $CI_PROJECT_DIR + - pip install pytest pytest-embedded-idf pytest-embedded-serial-esp pytest-rerunfailures pytest-timeout idf_build_apps + - eval "$(python ${CI_PROJECT_DIR}/tools/ci/build_apps.py ${TEST_DIR} --find --target-dir-type ${TARGET_DIR_TYPE} --target ${IDF_TARGET} -vv)" + - pytest "${APP_LIST[@]}" + --target ${IDF_TARGET} + --junitxml=XUNIT_RESULT.xml + --parallel-count ${CI_NODE_TOTAL:-1} + --parallel-index ${CI_NODE_INDEX:-1} + ${PYTEST_EXTRA_FLAGS} + +.pytest_components_dir_template: + extends: + - .rules:build:enable-gmf-unit-test + - .pytest_ut_template + variables: + TEST_DIR: ${CI_PROJECT_DIR}/gmf_core + BUILD_JOB_PREFIX: build_pytest_components_ + IDF_TARGET: esp32s3 + retry: 2 + +ut_gmf_core_esp32s3: + extends: + - .pytest_components_dir_template + needs: + - 'build_ut_gmf_core_esp32s3: [v5.4.3, 1/1]' + variables: + TEST_DIR: ${CI_PROJECT_DIR}/gmf_core + tags: [ ESP32S3, ESP_GMF_COMPONENT ] + +ut_gmf_elements_esp32s3: + extends: + - .pytest_components_dir_template + needs: + - 'build_ut_gmf_elements_esp32s3: [v5.4.3, 1/1]' + variables: + TEST_DIR: ${CI_PROJECT_DIR}/elements + tags: [ ESP32S3, ESP_GMF_COMPONENT ] + +ut_gmf_packages_esp32s3: + extends: + - .pytest_components_dir_template + needs: + - 'build_ut_gmf_packages_esp32s3: [v5.4.3, 1/1]' + variables: + TEST_DIR: ${CI_PROJECT_DIR}/packages + tags: [ ESP32S3, ESP_GMF_COMPONENT ] diff --git a/firmware/echoear-platform-agent/components/esp-gmf/.pre-commit-config.yaml b/firmware/echoear-platform-agent/components/esp-gmf/.pre-commit-config.yaml new file mode 100644 index 0000000..e7402bf --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/.pre-commit-config.yaml @@ -0,0 +1,28 @@ +# See https://pre-commit.com for more information +# See https://pre-commit.com/hooks.html for more hooks + +repos: + - repo: https://github.com/codespell-project/codespell + rev: v2.2.6 + hooks: + - id: codespell + + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.3.0 + hooks: + - id: check-executables-have-shebangs + - id: check-merge-conflict + - id: double-quote-string-fixer + - id: end-of-file-fixer + types_or: [c, c++] + - id: mixed-line-ending + args: ['-f=lf'] + types_or: [c, c++] + - id: no-commit-to-branch + name: Do not use more than one slash in the branch name + args: ['--pattern', '^[^/]*/[^/]*/'] + - id: no-commit-to-branch + name: Do not use uppercase letters in the branch name + args: ['--pattern', '^[^A-Z]*[A-Z]'] + - id: trailing-whitespace + types_or: [c, c++] diff --git a/firmware/echoear-platform-agent/components/esp-gmf/LICENSE b/firmware/echoear-platform-agent/components/esp-gmf/LICENSE new file mode 100644 index 0000000..95aa434 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/LICENSE @@ -0,0 +1,22 @@ +Espressif Modified MIT License + +Copyright (c) 2025 Espressif Systems (Shanghai) CO., LTD + +Permission is hereby granted for use EXCLUSIVELY with Espressif Systems products. +This includes the right to use, copy, modify, merge, publish, distribute, and sublicense +the Software, subject to the following conditions: + +1. This Software MUST BE USED IN CONJUNCTION WITH ESPRESSIF SYSTEMS PRODUCTS. +2. The above copyright notice and this permission notice shall be included in all copies + or substantial portions of the Software. +3. Redistribution of the Software in source or binary form FOR USE WITH NON-ESPRESSIF PRODUCTS + is strictly prohibited. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT diff --git a/firmware/echoear-platform-agent/components/esp-gmf/README.md b/firmware/echoear-platform-agent/components/esp-gmf/README.md new file mode 100644 index 0000000..2c18860 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/README.md @@ -0,0 +1,59 @@ +# ESP-GMF +[中文版](./README_CN.md) + +The Espressif General Multimedia Framework (ESP-GMF) is a lightweight and versatile software framework developed by Espressif specifically for IoT multimedia applications. It is highly flexible and scalable and tailored for IoT chips, with RAM usage as low as 7 KB. ESP-GMF supports applications in audio, image, and video processing, as well as any data-streaming product. + +ESP-GMF consists of four modules: GMF-Core, Elements, Packages, and GMF-Examples. + +- **GMF-Core** is the core of the ESP-GMF software framework and includes the main components such as GMF-Element, GMF-Pipeline, and GMF-Task. +- **Elements** implements various functional elements based on GMF-Core, such as codec algorithms, audio processing algorithms, and more. +- **Packages** implements high-level functional components based on GMF-Pipeline, such as the **ESP Audio Simple Player**, which is a basic audio decoding player. +- **GMF-Examples** provides examples of how to use GMF-Pipeline to implement simple functionalities, such as playing music from flash or an SD card. + +# ESP-GMF Component Overview + +ESP-GMF modules exist as components, which are categorized by functionality into **atomic components**, **basic components** and **advanced components**. When developing a project, it is recommended to use the elements and IOs components from the official repository. You can also create your own elements and IO components to extend its application scenarios. + +## Atomic Components + +Atomic components are essential, core building of ESP-GMF. + +| Component Name | Function | Dependent Components | +| :------------: | :------------: | :------------ | +| [gmf_core](./gmf_core) | Core framework for GMF | None | + +## Basic Components + +Basic components are intermediate-layer modules in ESP-GMF that handle data processing and core capabilities like audio/video stream encoding/decoding. These components have clear input/output interfaces, focus on specific tasks, and are reusable and composable. They can be used independently or commonly combined to build complex applications. + +| Component Name | Function | Dependent Components | +| :------------: | :------------: | :------------ | +| [gmf_audio](./elements/gmf_audio) | Elements for audio encoding, decoding,
    and sound effects processing | - `gmf_core`
    - `esp_audio_effects`
    - `esp_audio_codec` | +| [gmf_misc](./elements/gmf_misc) | Utility elements | None | +| [gmf_io](./elements/gmf_io) | Input/output for files, flash, and HTTP | - `gmf_core`
    - `esp_codec_dev` | +| [gmf_ai_audio](./elements/gmf_ai_audio) | Elements for intelligence audio processing
    and speech recognition | - `esp-sr`
    - `gmf_core` | +| [gmf_video](./elements/gmf_video) | Elements for video encoding, decoding,
    and video effects processing | - `gmf_core`
    - `esp_video_codec` | + +## Advanced Components + +Advanced components are encapsulation modules in ESP-GMF that target specific application scenarios, typically composed of multiple basic components or even atomic components. They encapsulate common multimedia business processes, hide the underlying pipeline construction and component configuration logic, provide simple and easy-to-use interfaces to help users quickly implement complex functions, simplify user development processes, and facilitate rapid integration. This category also includes utility modules and example collections. + +| Component Name | Function | Dependent Components | +| :------------: | :------------: | :------------ | +| [esp_audio_simple_player](./packages/esp_audio_simple_player) | Simple audio player | - `gmf_audio`
    - `gmf_io` | +| [gmf_loader](./packages/gmf_loader) | Set up the given GMF pool using the
    configuration selected via `Kconfig` | - `gmf_core`
    - `gmf_io`
    - `gmf_audio`
    - `gmf_misc`
    - `gmf_video`
    - `gmf_ai_audio`
    - `esp_codec_dev`
    - `esp_audio_codec`
    - `esp_video_codec` | +| [gmf_app_utils](./packages/gmf_app_utils) | Common peripheral configuration, unit
    testing tools Memory leak detection tools | - `gmf_core`
    - `protocol_examples_common`
    - `esp_board_manager`| +| [esp_capture](./packages/esp_capture) | High-level multimedia capture module | - `gmf_core`
    - `gmf-audio`
    - `gmf-video`
    - `esp_muxer`
    - `esp_codec_dev`
    - `esp-sr`
    - `esp_video`
    - `esp32-camera`| +| [esp_board_manager](./packages/esp_board_manager) | Intelligent, automated board configuration and management utilities with YAML-based setup | Dependencies vary based on selected board | +| [esp_audio_render](./packages/esp_audio_render) | Audio render module with mixer support | - `gmf_core`
    - `gmf-audio`
    | + + +# ESP-GMF Usage Guide + +For a basic example of GMF-Core API usage, see [test_apps](./gmf_core/test_apps/main/cases/gmf_pool_test.c). For practical application examples of elements, see the [ examples ](./gmf_examples/basic_examples/). + +# Frequently Asked Questions + +- **What is the difference between ESP-GMF and ESP-ADF?** + + ESP-ADF is a functional repository that includes many modules, such as `audio_pipeline`, `services`, `peripherals`, and `audio boards`. It is commonly used for more complex projects. ESP-GMF is an extension of the `audio_pipeline` module, designed to support applications for streaming audio, video, and image data. ESP-GMF offers a more modular structure than ESP-ADF's `audio_pipeline` by organizing functionality into different components, making it more flexible. For example, it is suitable for simple data streaming, such as playing audio from SD card/flash, or combining multiple components to provide more complex modules (e.g., audio player `esp_audio_simple_player`). Future versions of ESP-ADF will replace the `audio_pipeline` module with ESP-GMF. diff --git a/firmware/echoear-platform-agent/components/esp-gmf/README_CN.md b/firmware/echoear-platform-agent/components/esp-gmf/README_CN.md new file mode 100644 index 0000000..c019683 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/README_CN.md @@ -0,0 +1,58 @@ +# ESP-GMF +[English](./README.md) + +ESP-GMF 全称 Espressif General Multimedia Framework,是乐鑫开发的应用于 IoT 多媒体领域的轻量级通用软件框架。它灵活性高,可扩展性强,专为 IoT 芯片量身打造,RAM 资源占用只有 7 KB。ESP-GMF 可应用于音频、图像、视频等产品,还可应用于任何流式处理数据的产品中。 + +ESP-GMF 包含 GMF-Core、 Elements、Packages 和 GMF-Examples 四个模块。 + +- **GMF-Core** 是 ESP-GMF 软件框架的核心,有 GMF-Element、GMF-Pipeline、GMF-Task 等主要部件。 +- **Elements** 是基于 GMF-Core 实现的各种具体功能 element,比如音视频编解码算法、音效处理算法 和 AI 算法等。 +- **Packages** 基于 GMF-Pipeline 实现的 High Level 功能的组件示例,比如 **ESP Audio Simple Player** 是一个简单音频解码播放器。 +- **GMF-Examples** 提供如何使用 GMF-Pipeline 实现简单功能的示例,比如播放 flash 或 SD 卡中的音乐。 + +# ESP-GMF 组件介绍 + +ESP-GMF 各个模块以组件的形式存在,组件又按功能分为**原子组件**、**基础组件**和**高级组件**。在开发项目时,推荐使用官方仓库的 elements 和 IOs 组件进行开发,也可以自行创建 element 和 IO 组件来扩展其应用场景。 + +## 原子组件 + +原子组件是 ESP-GMF 不可或缺的、核心的基础构建单元。 + +| 组件名称 | 功能 | 依赖的组件 | +| :------------: | :------------:|:------------ | +| [gmf_core](./gmf_core) | GMF 基础框架 | 无 | + +## 基础组件 + +基础组件是 ESP-GMF 中的中间层模块,承担数据处理和音视频流编解码等核心能力。这些组件具备清晰的输入/输出接口,专注于一个具体的任务,具有复用性和可组合性。它们既可以单独使用,也常用于构建复杂应用程序。 + +| 组件名称 | 功能 | 依赖的组件 | +| :------------: | :------------:|:------------ | +| [gmf_audio](./elements/gmf_audio) | GMF 音频编解码和
    音效处理 element | - `gmf_core`
    - `esp_audio_effects`
    - `esp_audio_codec` | +| [gmf_misc](./elements/gmf_misc) | 工具类 element | 无 | +| [gmf_io](./elements/gmf_io) | 文件、flash、HTTP 输入输出 | - `gmf_core`
    - `esp_codec_dev` | +| [gmf_ai_audio](./elements/gmf_ai_audio) | 智能语音算法和
    语音识别 element | - `esp-sr`
    - `gmf_core` | +| [gmf_video](./elements/gmf_video) | GMF 视频编解码和
    视频效果处理 element | - `gmf_core`
    - `esp_video_codec` | + +## 高级组件 + +高级组件是 ESP-GMF 中面向特定应用场景的封装模块,通常由多个基础功能组件甚至原子组件组合而成。它们封装了常见的多媒体业务流程,隐藏了底层的 pipeline 构建和组件配置逻辑,提供简单易用的接口,帮助用户快速实现复杂的功能,以简化用户开发流程,便于快速集成。该分类还包括一些工具类模块和示例集合。 + +| 组件名称 | 功能 | 依赖的组件 | +| :------------: | :------------:|:------------ | +| [esp_audio_simple_player](./packages/esp_audio_simple_player) | 简单的音频播放器 | - `gmf_audio`
    - `gmf_io` | +| [gmf_loader](./packages/gmf_loader) | 使用 `Kconfig` 选择的配置
    设置给定的 GMF Pool | - `gmf_core`
    - `gmf_io`
    - `gmf_audio`
    - `gmf_misc`
    - `gmf_video`
    - `gmf_ai_audio`
    - `esp_codec_dev`
    - `esp_audio_codec`
    - `esp_video_codec` | +| [gmf_app_utils](./packages/gmf_app_utils) | 常用外设配置,单元测试工具
    内存泄漏检测工具 | - `gmf_core`
    - `protocol_examples_common`
    - `esp_board_manager` | +| [esp_capture](./packages/esp_capture) | 易用的音视频采集器 | - `gmf_core`
    - `gmf-audio`
    - `gmf-video`
    - `esp_muxer`
    - `esp_codec_dev`
    - `esp-sr`
    - `esp_video`
    - `esp32-camera`| +| [esp_board_manager](./packages/esp_board_manager) | 智能、自动化的板子配置和管理工具,支持基于 YAML 的设置 | 根据选择的板子变化依赖 | +| [esp_audio_render](./packages/esp_audio_render) | 支持混音的音频渲染器 | - `gmf_core`
    - `gmf-audio`
    | + +# ESP-GMF 使用说明 + +GMF-Core API 的简单示例代码请参考 [test_apps](./gmf_core/test_apps/main/cases/gmf_pool_test.c),Elements 实际应用示例请参考 [ examples ](./gmf_examples/basic_examples/)。 + +# 常见问题 + +- **ESP-GMF 和 ESP-ADF 有什么区别?** + + ESP-ADF 是一个包含很多模块的功能性仓库,比如 `audio_pipeline`、`services`、`peripherals` 和 `audio boards` 等,它常应用于比较复杂的项目中。ESP-GMF 是将 `audio_pipeline` 独立出来并进行功能扩展,使其支持音频、视频、图像等流式数据的应用场景。ESP-GMF 按功能分为不同的组件,灵活性优于 ESP-ADF 的 `audio_pipeline`,例如用于简单流式数据的处理,从 SD 卡/flash 播放一个音频,以及多个组件结合使用提供较复杂的功能模块(如音频播放器 `esp_audio_simple_player `)。ESP-ADF 的后期版本会使用 ESP-GMF 替代 `audio_pipeline` 模块。 diff --git a/firmware/echoear-platform-agent/components/esp-gmf/conftest.py b/firmware/echoear-platform-agent/components/esp-gmf/conftest.py new file mode 100644 index 0000000..be24f15 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/conftest.py @@ -0,0 +1,340 @@ +# SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: Apache-2.0 + +import logging +import os +import pathlib +import re +import sys +from datetime import datetime +from typing import Callable, List, Optional, Tuple + +import pytest +from _pytest.nodes import Item +from pytest import Config, FixtureRequest, Function, Session +from pytest_embedded.plugin import multi_dut_argument, multi_dut_fixture +from pytest_embedded.utils import to_list + +IDF_VERSION = os.environ.get('IDF_VERSION') +PYTEST_ROOT_DIR = str(pathlib.Path(__file__).parent) +logging.info(f'Pytest root dir: {PYTEST_ROOT_DIR}') + + +SUPPORTED_TARGETS = ['esp32', 'esp32s2', 'esp32c3', 'esp32s3', 'esp32c2', 'esp32c6', 'esp32h2', 'esp32p4'] +PREVIEW_TARGETS: List[str] = [] # this PREVIEW_TARGETS excludes 'linux' target +DEFAULT_SDKCONFIG = 'default' + +TARGET_MARKERS = { + 'esp32': 'support esp32 target', + 'esp32s2': 'support esp32s2 target', + 'esp32s3': 'support esp32s3 target', + 'esp32c3': 'support esp32c3 target', + 'esp32c2': 'support esp32c2 target', + 'esp32c6': 'support esp32c6 target', + 'esp32h2': 'support esp32h2 target', + 'esp32p4': 'support esp32p4 target', + 'linux': 'support linux target', +} + +SPECIAL_MARKERS = { + 'supported_targets': "support all officially announced supported targets ('esp32', 'esp32s2', 'esp32c3', 'esp32s3', 'esp32c2', 'esp32c6')", + 'preview_targets': "support all preview targets ('none')", + 'all_targets': 'support all targets, including supported ones and preview ones', + 'temp_skip_ci': 'temp skip tests for specified targets only in ci', + 'temp_skip': 'temp skip tests for specified targets both in ci and locally', + 'nightly_run': 'tests should be executed as part of the nightly trigger pipeline', + 'host_test': 'tests which should not be built at the build stage, and instead built in host_test stage', + 'qemu': 'build and test using qemu-system-xtensa, not real target', +} + +ENV_MARKERS = { + 'GMF_EXAMPLE_GENERIC': 'Tests should be run on GMF_EXAMPLE_GENERIC runners.', + 'ESP_GMF_COMPONENT': 'Tests should be run on ESP_GMF_COMPONENT runners.', + 'ESP_GMF_ELEMENT': 'Tests should be run on random runners.', + 'ESP_GMF_TASK': 'Tests should be run on random runners.', + 'ESP_GMF_IO': 'Tests should be run on random runners.', + 'ESP_GMF_RINGBUF': 'Tests should be run on random runners.', + 'ESP_GMF_PBUF': 'Tests should be run on random runners.', + 'ESP_GMF_BLOCK': 'Tests should be run on random runners.', + 'GMF_LOADER': 'Tests should be run on random runners.', + 'ELEMENT_POOL': 'Tests should be run on random runners.', + 'ELEMENT_PORT': 'Tests should be run on random runners.', + 'ESP_GMF_METHOD': 'Tests should be run on random runners.', + 'AUDIO_LOOPBACK_ENV': 'Audio loopback testing verifies the functionality by comparing the audio input signal with the output signal.', +} + +################## +# Help Functions # +################## +def format_case_id(target: Optional[str], config: Optional[str], case: str) -> str: + return f'{target}.{config}.{case}' + + +def item_marker_names(item: Item) -> List[str]: + return [marker.name for marker in item.iter_markers()] + + +def item_skip_targets(item: Item) -> List[str]: + def _get_temp_markers_disabled_targets(marker_name: str) -> List[str]: + temp_marker = item.get_closest_marker(marker_name) + + if not temp_marker: + return [] + + # temp markers should always use keyword arguments `targets` and `reason` + if not temp_marker.kwargs.get('targets') or not temp_marker.kwargs.get('reason'): + raise ValueError( + f'`{marker_name}` should always use keyword arguments `targets` and `reason`. ' + f'For example: ' + f'`@pytest.mark.{marker_name}(targets=["esp32"], reason="IDF-xxxx, will fix it ASAP")`' + ) + + return to_list(temp_marker.kwargs['targets']) # type: ignore + + temp_skip_ci_targets = _get_temp_markers_disabled_targets('temp_skip_ci') + temp_skip_targets = _get_temp_markers_disabled_targets('temp_skip') + + # in CI we skip the union of `temp_skip` and `temp_skip_ci` + if os.getenv('CI_JOB_ID'): + skip_targets = list(set(temp_skip_ci_targets).union(set(temp_skip_targets))) + else: # we use `temp_skip` locally + skip_targets = temp_skip_targets + + return skip_targets + + +@pytest.fixture(scope='session', autouse=True) +def idf_version() -> str: + if os.environ.get('IDF_VERSION'): + return os.environ.get('IDF_VERSION') + idf_path = os.environ.get('IDF_PATH') + if not idf_path: + logging.warning('Failed to get IDF_VERSION!') + return '' + version_path = os.path.join(os.environ['IDF_PATH'], 'tools/cmake/version.cmake') + regex = re.compile(r'^\s*set\s*\(\s*IDF_VERSION_([A-Z]{5})\s+(\d+)') + ver = {} + with open(version_path) as f: + for line in f: + m = regex.match(line) + if m: + ver[m.group(1)] = m.group(2) + return '{}.{}'.format(int(ver['MAJOR']), int(ver['MINOR'])) + + +@pytest.fixture(scope='session', autouse=True) +def session_tempdir() -> str: + _tmpdir = os.path.join( + os.path.dirname(__file__), + 'pytest_log', + datetime.now().strftime('%Y-%m-%d_%H-%M-%S'), + ) + os.makedirs(_tmpdir, exist_ok=True) + return _tmpdir + + +@pytest.fixture +@multi_dut_argument +def config(request: FixtureRequest) -> str: + config_marker = list(request.node.iter_markers(name='config')) + return config_marker[0].args[0] if config_marker else 'default' + + +@pytest.fixture +@multi_dut_argument +def app_path(request: FixtureRequest, test_file_path: str) -> str: + config_marker = list(request.node.iter_markers(name='app_path')) + if config_marker: + return config_marker[0].args[0] + else: + # compatible with old pytest-embedded parametrize --app_path + return request.config.getoption('app_path', None) or os.path.dirname(test_file_path) + + +@pytest.fixture +def test_case_name(request: FixtureRequest, target: str, config: str) -> str: + if not isinstance(target, str): + target = '|'.join(sorted(list(set(target)))) + if not isinstance(config, str): + config = '|'.join(sorted(list(config))) + return f'{target}.{config}.{request.node.originalname}' + + +@pytest.fixture +@multi_dut_fixture +def build_dir( + app_path: str, + target: Optional[str], + config: Optional[str], + idf_version: str +) -> Optional[str]: + """ + Check local build dir with the following priority: + + 1. /${IDF_VERSION}/build__ + 2. /${IDF_VERSION}/build_ + 3. /build__ + 4. /build + 5. + + Args: + app_path: app path + target: target + config: config + + Returns: + valid build directory + """ + + assert target + assert config + check_dirs = [] + if idf_version: + check_dirs.append(os.path.join(idf_version, f'build_{target}_{config}')) + check_dirs.append(os.path.join(idf_version, f'build_{target}')) + check_dirs.append(f'build_{target}_{config}') + check_dirs.append(f'build_{target}_{config}s') + check_dirs.append('build') + check_dirs.append('.') + for check_dir in check_dirs: + binary_path = os.path.join(app_path, check_dir) + if os.path.isdir(binary_path): + logging.info(f'find valid binary path: {binary_path}') + return check_dir + + logging.warning( + f'checking binary path: {binary_path} ... missing ... try another place') + + logging.error( + f'no build dir. Please build the binary "python tools/build_apps.py {app_path}" and run pytest again') + sys.exit(1) + + +@pytest.fixture(autouse=True) +@multi_dut_fixture +def junit_properties( + test_case_name: str, record_xml_attribute: Callable[[str, object], None] +) -> None: + """ + This fixture is autoused and will modify the junit report test case name to .. + """ + record_xml_attribute('name', test_case_name) + + +################## +# Hook functions # +################## +_idf_pytest_embedded_key = pytest.StashKey['IdfPytestEmbedded'] + + +def pytest_addoption(parser: pytest.Parser) -> None: + base_group = parser.getgroup('idf') + base_group.addoption( + '--env', + help='only run tests matching the environment NAME.', + ) + + +def pytest_configure(config: Config) -> None: + # Require cli option "--target" + help_commands = ['--help', '--fixtures', '--markers', '--version'] + for cmd in help_commands: + if cmd in config.invocation_params.args: + target = 'unneeded' + break + else: + target = config.getoption('target') + if not target: + raise ValueError('Please specify one target marker via "--target [TARGET]"') + + config.stash[_idf_pytest_embedded_key] = IdfPytestEmbedded( + target=target, + env_name=config.getoption('env'), + ) + config.pluginmanager.register(config.stash[_idf_pytest_embedded_key]) + + for name, description in {**TARGET_MARKERS, **ENV_MARKERS, **SPECIAL_MARKERS}.items(): + config.addinivalue_line('markers', f'{name}: {description}') + +def pytest_unconfigure(config: Config) -> None: + _pytest_embedded = config.stash.get(_idf_pytest_embedded_key, None) + if _pytest_embedded: + del config.stash[_idf_pytest_embedded_key] + config.pluginmanager.unregister(_pytest_embedded) + + +class IdfPytestEmbedded: + def __init__( + self, + target: Optional[str] = None, + env_name: Optional[str] = None, + ): + # CLI options to filter the test cases + self.target = target + self.env_name = env_name + + self._failed_cases: List[ + Tuple[str, bool, bool] + ] = [] # (test_case_name, is_known_failure_cases, is_xfail) + + @pytest.hookimpl(tryfirst=True) + def pytest_sessionstart(self, session: Session) -> None: + if self.target: + self.target = self.target.lower() + session.config.option.target = self.target + + @pytest.hookimpl(tryfirst=True) + def pytest_collection_modifyitems(self, items: List[Function]) -> None: + # sort by file path and callspec.config + # implement like this since this is a limitation of pytest, couldn't get fixture values while collecting + # https://github.com/pytest-dev/pytest/discussions/9689 + # after sort the test apps, the test may use the app cache to reduce the flash times. + def _get_param_config(_item: Function) -> str: + if hasattr(_item, 'callspec'): + return _item.callspec.params.get('config', DEFAULT_SDKCONFIG) # type: ignore + return DEFAULT_SDKCONFIG + + items.sort(key=lambda x: (os.path.dirname(x.path), _get_param_config(x))) + + # set default timeout 10 minutes for each case + for item in items: + if 'timeout' not in item.keywords: + item.add_marker(pytest.mark.timeout(10 * 60)) + + # add markers for special markers + for item in items: + if 'supported_targets' in item.keywords: + for _target in SUPPORTED_TARGETS: + item.add_marker(_target) + if 'preview_targets' in item.keywords: + for _target in PREVIEW_TARGETS: + item.add_marker(_target) + if 'all_targets' in item.keywords: + for _target in [*SUPPORTED_TARGETS, *PREVIEW_TARGETS]: + item.add_marker(_target) + + # add 'xtal_40mhz' tag as a default tag for esp32c2 target + # only add this marker for esp32c2 cases + if ( + self.target == 'esp32c2' + and 'esp32c2' in item_marker_names(item) + and 'xtal_26mhz' not in item_marker_names(item) + ): + item.add_marker('xtal_40mhz') + + # filter all the test cases with "nightly_run" marker + if os.getenv('INCLUDE_NIGHTLY_RUN') == '1': + # Do not filter nightly_run cases + pass + elif os.getenv('NIGHTLY_RUN') == '1': + items[:] = [item for item in items if 'nightly_run' in item_marker_names(item)] + else: + items[:] = [item for item in items if 'nightly_run' not in item_marker_names(item)] + + # filter all the test cases with target and skip_targets + items[:] = [ + item + for item in items + if self.target in item_marker_names(item) and self.target not in item_skip_targets(item) + ] diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/README.md b/firmware/echoear-platform-agent/components/esp-gmf/elements/README.md new file mode 100644 index 0000000..5fbd782 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/README.md @@ -0,0 +1,5 @@ +# ESP-GMF-Elements + +- [中文版](./README_CN.md) + +This is Espressif's official GMF elements component collection: [GMF Element Audio](./gmf_audio/README.md), [GMF Element Misc](./gmf_misc/README.md), GMF Element Image, [GMF IO](./gmf_io/README.md), and GMF AI Audio. Please refer to the README files of each component for detailed functionality and descriptions. diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/README_CN.md b/firmware/echoear-platform-agent/components/esp-gmf/elements/README_CN.md new file mode 100644 index 0000000..dbdc6c2 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/README_CN.md @@ -0,0 +1,5 @@ +# ESP-GMF-Elements + +- [English](./README.md) + +这是 Espressif 官方 GMF elements 组件集合:[GMF Element Audio](./gmf_audio/README_CN.md),[GMF Element Misc](./gmf_misc/README_CN.md),GMF Element Image, [GMF IO](./gmf_io/README_CN.md) 和 GMF AI Audio。请查看各个组件的 readme 了解具体功能和说明。 diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/CHANGELOG.md b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/CHANGELOG.md new file mode 100644 index 0000000..4d1cb56 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/CHANGELOG.md @@ -0,0 +1,67 @@ +# Changelog + +## v0.7.4 + +### Bug Fixes + +- Fixed a crash issue that occurred when the AFE manager was destroyed. + +## v0.7.3 + +### Features + +- Added default sdkconfig for esp32p4 in wwe example + +### Bug Fixes + +- Fixed build error for esp32p4 + +## v0.7.2 + +### Features + +- Added `esp_gmf_afe_trigger_wakeup` and `esp_gmf_afe_trigger_sleep` APIs to trigger wakeup and sleep events manually + +## v0.7.1 + +### Features + +- Increased `esp-sr` dependency to version 2.1.5 + +## v0.7.0 + +### Features + +- Added `esp_gmf_afe_set_event_cb` API to register AFE element event callbacks +- Replaced the interface for decoder reconfiguration +- Introduced `esp_gmf_wn` element to support wake word detection +- Corrected return value validation for *acq_write* and *acq_release* callback function implementations +- Added `esp_gmf_afe_keep_awake` API to enable/disable the keep awake feature +- Increased `esp-sr` dependency to version 2.1.4 + +### Bug Fixes + +- Fixed an issue where the output attribute of the `esp_gmf_afe` element was not set correctly +- Fixed a build error occurring when SPIRAM is disabled +- Integrated gmf_app_utils package for peripheral and system management +- Migrated common utilities to gmf_app_utils package +- Standardize TAG identifier format across all elements with `ai` prefix +- Fixed examples TAGs issue because of updating TAGs gmf_audio gmf_io + +## v0.6.2 + +### Features + +- Limit the version of `esp_audio_codec` and `esp_codec_dev` in `aec_rec` example + +## v0.6.1 + +### Bug Fixes + +- Fixed a build issue caused by a variable type mismatch + +## v0.6.0 + +### Features + +- Initial version of GMF AI Audio Elements diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/CMakeLists.txt b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/CMakeLists.txt new file mode 100644 index 0000000..a457585 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/CMakeLists.txt @@ -0,0 +1,12 @@ +set(srcs "./src/esp_gmf_wn.c") + +if((${IDF_TARGET} STREQUAL "esp32s3") OR (${IDF_TARGET} STREQUAL "esp32p4") OR (${IDF_TARGET} STREQUAL "esp32")) + list(APPEND srcs "./src/esp_gmf_afe_manager.c" "./src/esp_gmf_afe.c" "./src/esp_gmf_aec.c") +endif() + +idf_component_register( + SRCS "${srcs}" + INCLUDE_DIRS "./include" + PRIV_INCLUDE_DIRS "./private_include" + REQUIRES esp-sr esp_timer +) diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/LICENSE b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/LICENSE new file mode 100644 index 0000000..876250e --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/LICENSE @@ -0,0 +1,22 @@ +Espressif Modified MIT License + + Copyright (c) 2025 Espressif Systems (Shanghai) CO., LTD + + Permission is hereby granted for use EXCLUSIVELY with Espressif Systems products. + This includes the right to use, copy, modify, merge, publish, distribute, and sublicense + the Software, subject to the following conditions: + + 1. This Software MUST BE USED IN CONJUNCTION WITH ESPRESSIF SYSTEMS PRODUCTS. + 2. The above copyright notice and this permission notice shall be included in all copies + or substantial portions of the Software. + 3. Redistribution of the Software in source or binary form FOR USE WITH NON-ESPRESSIF PRODUCTS + is strictly prohibited. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE + FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + + SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/README.md b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/README.md new file mode 100644 index 0000000..8dd9c14 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/README.md @@ -0,0 +1,248 @@ +# GMF AI Audio + +- [![Component Registry](https://components.espressif.com/components/espressif/gmf_ai_audio/badge.svg)](https://components.espressif.com/components/espressif/gmf_ai_audio) + +- [中文版](./README_CN.md) + +`GMF AI Audio` is an artificial intelligence audio processing module that provides users with convenient and easy-to-use intelligent audio processing algorithms at the [GMF](https://github.com/espressif/esp-gmf) framework, such as voice wake-up, command word recognition, and echo cancellation. Currently, it offers the following modules based on `esp-sr`: + +- [esp_gmf_afe_manager](./src/esp_gmf_afe_manager.c): `audio front end(afe)` manager +- [esp_gmf_aec](./src/esp_gmf_aec.c): Echo Cancellation +- [esp_gmf_wn](./src/esp_gmf_wn.c): A standalone wake word detection module that can be used independently +- [esp_gmf_afe](./src/esp_gmf_afe.c): An easy-to-use interface based on the `audio front end (afe)` from `esp-sr`, providing functionalities such as voice wake-up, command word recognition, and speech detection + +| Name | Tag | Function | Method | Input Channel Number | Output Channel Number | Model Partition Dependency | Input Frame Length | Notes | +|:----:|:----:|:----:|:----:|:----:|:----:|:----:|:----:|:----| +| esp_gmf_afe | ai_afe | Audio front-end processing: Wake word detection, command word recognition, voice enhancement, echo cancellation, noise suppression, automatic gain control | `start_vcmd_det` | 1-4 | 1 | Yes | 256(sample) | Currently supports up to 2 microphone channels + 1 speaker reference signal, remaining channel selection marked as N, requires following voice command detection procedure | +| esp_gmf_aec | ai_aec | Echo cancellation: Eliminates echo interference in audio, improves voice quality | None | 1-4 | 1 | No | 256(sample) | Input channels can be set to multiple microphones, uses first microphone channel and reference channel for calculation, must include reference signal | +| esp_gmf_wn | ai_wn | Independent wake word detection: Lightweight wake word detection, independent of AFE, low resource consumption | None | 1-4 | 1 | Yes | 256(sample) | Supports up to 3 microphone channels, microphone channel count in input format must match working mode | + +## AFE Manager `esp_gmf_afe_manager` + +### Features + +- Manages the data path and task scheduling of the Audio Front-End (AFE) +- Supports dynamic enabling/disabling of features (e.g., wake-up, AEC, VAD) +- Provides multi-task coordination to ensure real-time audio stream processing + +### Key Characteristics + +- Task Management: Creates independent `feed_task` and `fetch_task` for audio data input and result processing +- Feature Control: Dynamically toggle algorithm modules via `esp_afe_manager_enable_features` +- Event-Driven: Supports suspend/resume operations (`esp_afe_manager_suspend`) for low-power scenarios + +### System Diagram + + ```mermaid + graph LR + subgraph AFE Manager + DATA_CB[Raw Data] + FEED_TASK[feed_task
    Audio Input Task] + FETCH_TASK[fetch_task
    Result Processing Task] + AFE_CORE[AFE Core
    esp_afe_sr_iface] + EVENT_CB[Result Callback] + CTRL_IF[Control Interface] + end + + FEED_TASK -->|Data Callback| DATA_CB + FEED_TASK -->|Raw Audio Data| AFE_CORE + AFE_CORE -->|Processing Results| FETCH_TASK + FETCH_TASK -->|Result Callback
    Data/Wake-Up/VAD/Command Words| EVENT_CB + CTRL_IF -->|Suspend/Resume Interface| FEED_TASK + CTRL_IF -->|Suspend/Resume Interface| FETCH_TASK + CTRL_IF -->|Feature Toggle Interface
    AEC/VAD/SE| AFE_CORE + ``` + +## Echo Cancellation `esp_gmf_aec` + +### Features + +- Eliminates echo interference in audio to improve voice quality +- Supports multi-channel input and single-channel output + +### Technical Specifications + +- Supported Hardware: `ESP32`, `ESP32-S3`, and `ESP32-P4` +- Input Format + - Sampling Rate: 8 kHz and 16 kHz + - Bit Width: 16-bit PCM +- Channel Configuration: String identifiers (e.g., `MMNR`) + - `M`: Microphone Channel + - `R`: Reference Signal Channel + - `N`: Invalid Signal +- Output Format: 16-bit single-channel PCM + +## Wake Word Detection `esp_gmf_wn` + +### Features + +- Runs independently without AFE dependency, low resource usage +- Supports multi-channel input and single-channel output +- Notifies wake word detection results via callback function + +### Technical Specifications + +- Supported Hardware: `ESP32`, `ESP32-S3`, `ESP32-C3`, `ESP32-C5` and `ESP32-P4` +- Input Format + - Sampling Rate: 16 kHz + - Bit Width: 16-bit PCM +- Channel Configuration: String identifiers (e.g., `MMNR`) + - `M`: Microphone Channel + - `R`: Reference Signal Channel + - `N`: Invalid Signal +- Output Format: 16-bit single-channel PCM + +## Speech Recognition `esp_gmf_afe` + +### Features + +- Voice Wake-Up (Wake Word Detection) +- Command Word Recognition +- Voice Activity Detection (VAD) and State Management + +### Key Characteristics + +- Supported Hardware: `ESP32`, `ESP32-S3`, and `ESP32-P4` +- Input and Output: Same as described in `esp_gmf_aec` +- Unified Event Interface: Notifies events (wake-up start/end, VAD state changes, command word triggers) via callback functions +- State Machine Management: Automatically maintains transitions between wake-up, speech, and silence states + +### System Diagram + + ```mermaid + graph TD + subgraph Audio Preprocessing + RAW[Audio Data] + end + + subgraph GMF_AFE + AFE_MGR[AFE Manager
    esp_gmf_afe_manager] + INPUT_RB[Input Buffer] + OUTPUT_RB[Output Buffer] + STATE_MACHINE[Wake-Up/VAD State Machine] + MN_DET[Command Word Detection] + DELAY[Delay Mechanism] + end + + subgraph Application Layer + EVENT_CB[Event Callback] + OUT_PORT[Data Output] + end + + RAW -->|Processed Audio Data| INPUT_RB + INPUT_RB -->|Data Callback| AFE_MGR + AFE_MGR -->|Result Callback
    Data| OUTPUT_RB + AFE_MGR -->|Result Callback
    Wake-Up/VAD Events| STATE_MACHINE + AFE_MGR -->|Result Callback
    Data| MN_DET + OUTPUT_RB --> DELAY --> OUT_PORT + STATE_MACHINE -->|Event Notification| EVENT_CB + MN_DET -->|Event Notification| EVENT_CB + ``` + +### `esp_gmf_afe_cfg_t` Advanced Configuration Parameters + + | Parameter | Description | Default Value | + | --- | --- | --- | + | **wakeup_time** | Time in milliseconds to trigger wake-up end event if no voice activity is detected after enabling wake-up | 10000 (ms) | + | **wakeup_end** | Time in milliseconds to trigger wake-up end event after detecting silence following voice activity | 2000 (ms) | + | **vcmd_timeout** | Timeout for command word detection. After timeout, the interface must be called again to start a new detection round | 5760 (ms) | + | **delay_samples** | Output data delay to compensate for VAD detection lag. The specified data length (in samples) should be greater than or equal to `afe_config_t.vad_min_speech_ms` configured during AFE manager initialization | 2048 (samples) | + +### Wake Word and VAD State Machine + +The following illustrates state transitions when features are enabled. The `/` character indicates the triggered user event + +- **Enable Wake Word Detection, Disable VAD** + + This scenario focuses on detecting wake words and can be configured (`wakeup_time`) to trigger an end event after a certain period + + Modify the configuration in the example [wwe](./examples/wwe/main/main.c) to use this scenario + + ```c + #define WAKENET_ENABLE (true) + #define VAD_ENABLE (false) + ``` + + ```mermaid + stateDiagram-v2 + [*] --> IDLE + + IDLE --> WAKEUP: Wake Word / (WAKEUP_START) + + WAKEUP --> IDLE: Timer (wakeup_time) Timeout / (WAKEUP_END) + ``` + +- **Enable VAD, Disable Wake Word Detection** + + This scenario focuses on detecting voice activity and triggers corresponding events at the start and end of speech + + Modify the configuration in the example [wwe](./examples/wwe/main/main.c) to use this scenario + + ```c + #define WAKENET_ENABLE (false) + #define VAD_ENABLE (true) + ``` + + ```mermaid + stateDiagram-v2 + [*] --> IDLE + + IDLE --> SPEECHING: Voice Detected / (VAD_START) + + SPEECHING --> IDLE: Noise or Silence Detected / (VAD_END) + ``` + +- **Enable Both Wake Word Detection and VAD** + + This scenario combines wake word detection with voice activity detection to avoid frequent voice activity events outside the wake word interval + + Modify the configuration in the example [wwe](./examples/wwe/main/main.c) to use this scenario + + ```c + #define WAKENET_ENABLE (true) + #define VAD_ENABLE (true) + ``` + + ```mermaid + stateDiagram-v2 + [*] --> IDLE + + IDLE --> WAKEUP: Wake Word / (WAKEUP_START) + + WAKEUP --> SPEECHING: Voice Detected / (VAD_START) + WAKEUP --> IDLE: Timer (wakeup_time) Timeout / (WAKEUP_END) + + SPEECHING --> WAIT_FOR_SLEEP: Noise or Silence Detected / (VAD_END) + + WAIT_FOR_SLEEP --> SPEECHING: Voice Detected / (VAD_START) + WAIT_FOR_SLEEP --> IDLE: Timer (wakeup_end) Timeout / (WAKEUP_END) + ``` + +### Command Word Detection + +Users need to decide when to start command word detection. A typical use case is to enable detection after the state machine pushes a (WAKEUP_START) event and determine the next operation based on the detected command word index in the callback function + +- Command word detection is independent of the wake word state machine +- Command word detection supports continuous detection until timeout + +## Usage + +For example code, refer to the [examples](./examples) folder for the Wake Word Detection and Audio Echo Cancellation demos. + +You can also create and compile a project using the following commands, taking the `aec_rec` project as an example. Before starting, make sure you have a working [ESP-IDF](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/get-started/index.html) environment. + +### 1. Create the Example Project + +Create the `aec_rec` example project based on the `gmf_ai_audio` component (using version v0.7.0 as an example; update the version as needed): + +```shell +idf.py create-project-from-example "espressif/gmf_ai_audio=0.7.0:aec_rec" +``` + +### 2. Build and Flash the Project Using an ESP32-S3 Board + +```shell +cd aec_rec +idf.py set-target esp32s3 +idf.py -p YOUR_PORT flash monitor +``` diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/README_CN.md b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/README_CN.md new file mode 100644 index 0000000..e7c3f7c --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/README_CN.md @@ -0,0 +1,248 @@ +# GMF AI Audio + +- [![Component Registry](https://components.espressif.com/components/espressif/gmf_ai_audio/badge.svg)](https://components.espressif.com/components/espressif/gmf_ai_audio) + +- [English](./README.md) + +`GMF AI Audio` 是一个人工智能语音处理模块,它在 [GMF](https://github.com/espressif/esp-gmf) 层面为用户提供方便、易用的语音唤醒、命令词识别和回声消除等常用智能语音处理算法。目前基于 `esp-sr` 提供以下模块: + +- [esp_gmf_afe_manager](./src/esp_gmf_afe_manager.c): `audio front end(afe)` 管理器 +- [esp_gmf_aec](./src/esp_gmf_aec.c): 回声消除 +- [esp_gmf_wn](./src/esp_gmf_wn.c): 独立的唤醒词检测模块,不依赖AFE +- [esp_gmf_afe](./src/esp_gmf_afe.c): 基于 `esp-sr` 中 `audio front end (afe)` 实现的易用接口,提供了语音唤醒,命令词识别,人声检测等语音识别的功能 + +| 名称 | 标签 | 功能 | 函数方法 | 输入通道 | 输出通道 | 依赖模型数据分区 | 输入帧长 | 备注 | +|:----:|:----:|:----:|:----:|:----:|:----:|:----:|:----:|:----| +| esp_gmf_afe | ai_afe | 语音前处理:唤醒词检测、命令词识别、人声增强、回声消除、降噪、自动增益控制 | `start_vcmd_det` | 1-4 | 1 | 是 | 256(sample) | 目前最多支持2通道麦克风+1个扬声器的回采信号,剩余通道选择记为N,同时需要按照命令词检测步骤操作 | +| esp_gmf_aec | ai_aec | 回声消除:消除音频中的回声干扰,提升语音质量 | 无 | 1-4 | 1 | 否 | 256(sample) | 输入通道可以设置多个麦克风,采用第一个麦克风通道和回采通道进行计算,且必须包含回采信号 | +| esp_gmf_wn | ai_wn | 独立唤醒词检测:轻量级唤醒词检测,不依赖AFE,资源占用小 | 无 | 1-4 | 1 | 是 | 256(sample) | 最高支持3通道麦克风,输入格式中的麦克风通道数需要与工作模式对应 | + +## AFE 管理器 `esp_gmf_afe_manager` + +### 功能 + +- 管理音频前端(Audio Front-End, AFE)的数据通路与任务调度 +- 支持动态启用/禁用特性(如唤醒、AEC、VAD) +- 提供多任务协同机制,确保实时音频流处理 + +### 关键特性 + +- 任务管理:创建独立的 `feed_task` 和 `fetch_task`,分别负责音频数据输入和结果处理 +- 特性控制:通过 `esp_afe_manager_enable_features` 动态开关算法模块 +- 事件驱动:支持挂起/恢复操作 `esp_afe_manager_suspend`,适配低功耗场景 + +### 系统框图 + + ```mermaid + graph LR + subgraph AFE管理器 + DATA_CB[原始数据] + FEED_TASK[feed_task
    音频输入任务] + FETCH_TASK[fetch_task
    结果处理任务] + AFE_CORE[AFE核心
    esp_afe_sr_iface] + EVENT_CB[结果回调] + CTRL_IF[控制接口] + end + + FEED_TASK -->|数据回调| DATA_CB + FEED_TASK -->|原始音频数据| AFE_CORE + AFE_CORE -->|处理结果| FETCH_TASK + FETCH_TASK -->|结果回调
    (数据/唤醒/VAD/命令词)| EVENT_CB + CTRL_IF -->|挂起/恢复接口| FEED_TASK + CTRL_IF -->|挂起/恢复接口| FETCH_TASK + CTRL_IF -->|特性使能接口
    (AEC/VAD/SE)| AFE_CORE + ``` + +## 回声消除 `esp_gmf_aec` + +### 功能 + +- 消除音频中的回声干扰,提升语音质量 +- 支持多通道输入,单通道输出 + +### 技术规格 + +- 支持硬件:`ESP32`、`ESP32-S3` 和 `ESP32-P4` +- 输入格式 + - 采样率:16 kHz + - 位宽:16-bit PCM +- 通道配置:字符串标识(如 `MMNR`) + - `M`:麦克风通道 + - `R`:回采信号通道 + - `N`:无效信号 +- 输出格式:16-bit 单通道 PCM + +## 唤醒词检测 `esp_gmf_wn` + +### 功能 + +- 独立运行,不依赖AFE,资源占用小 +- 支持多通道输入,单通道输出 +- 通过回调函数通知唤醒词检测结果 + +### 技术规格 + +- 支持硬件:`ESP32`、`ESP32-S3`、`ESP32-C3`、`ESP32-C5` 和 `ESP32-P4` +- 输入格式 + - 采样率:8 kHz, 16 kHz + - 位宽:16-bit PCM +- 通道配置:字符串标识(如 `MMNR`) + - `M`:麦克风通道 + - `R`:回采信号通道 + - `N`:无效信号 +- 输出格式:16-bit 单通道 PCM + +## 语音识别 `esp_gmf_afe` + +### 功能 + +- 语音唤醒(Wake Word Detection) +- 命令词识别(Voice Command Detection) +- 人声检测(VAD)与状态管理 + +### 关键特性 + +- 支持硬件: `esp32`, `esp32s3` 和 `esp32p4` +- 输入与输出同上文中的 `esp_gmf_aec` +- 统一事件接口:通过回调函数通知事件(唤醒开始/结束、VAD 状态变化、命令词触发) +- 状态机管理:自动维护唤醒、语音、静音等状态切换 + +### 系统框图 + + ```mermaid + graph TD + subgraph 音频前级处理 + RAW[音频数据] + end + + subgraph GMF_AFE + AFE_MGR[AFE管理器
    esp_gmf_afe_manager] + INPUT_RB[输入缓存] + OUTPUT_RB[输出缓存] + STATE_MACHINE[唤醒/VAD状态机] + MN_DET[命令词检测] + DELAY[延时机制] + end + + subgraph 应用层 + EVENT_CB[事件回调] + OUT_PORT[数据出口] + end + + RAW -->| 音频前级处理后的数据| INPUT_RB + INPUT_RB -->| 数据回调 | AFE_MGR + AFE_MGR -->| 结果回调
    数据 | OUTPUT_RB + AFE_MGR -->| 结果回调
    唤醒/VAD事件 | STATE_MACHINE + AFE_MGR -->| 结果回调
    数据 | MN_DET + OUTPUT_RB --> DELAY --> OUT_PORT + STATE_MACHINE -->| 事件通知 | EVENT_CB + MN_DET -->| 事件通知 | EVENT_CB + ``` + +### `esp_gmf_afe_cfg_t` 高级配置参数说明 + + | 参数 | 描述 | 默认值 | + | --- | --- | --- | + | **wakeup_time** | 唤醒功能启用后,若未检测到人声事件,则在等待 `wakeup_time` 毫秒后触发唤醒结束事件 | 10000(ms) | + | **wakeup_end** | 唤醒功能启用后,若依次检测到人声和静音,则在静音事件触发后等待 `wakeup_end` 毫秒,触发唤醒结束事件 | 2000(ms) | + | **vcmd_timeout** | 命令词检测的超时时间,超时后需重新调用接口以开启新一轮命令词检测 | 5760(ms) | + | **delay_samples** | 输出数据延迟,用于补偿 VAD 检测滞后导致的数据输出与事件回调不同步的问题。该参数指定的数据长度(以采样点为单位)换算为时间后,应大于等于初始化 AFE 管理器时配置的 `afe_config_t.vad_min_speech_ms` | 2048(samples) | + +### 唤醒与VAD状态机 + +以下列举了功能使能情况下的状态切换,`/` 字符之后为触发的用户事件 + +- **使能唤醒,不使能VAD** + + 该场景主要关注唤醒词的检测,同时可以配置(wakeup_time),在一段时间后触发结束事件 + + 修改示例 [wwe](./examples/wwe/main/main.c) 中的配置,从而使用该场景 + + ```c + #define WAKENET_ENABLE (true) + #define VAD_ENABLE (false) + ``` + + ```mermaid + stateDiagram-v2 + [*] --> IDLE + + IDLE --> WAKEUP: 唤醒词 / (WAKEUP_START) + + WAKEUP --> IDLE: 定时器(wakeup_time)超时 / (WAKEUP_END) + ``` + +- **使VAD,不使能唤醒词** + + 该场景主要关注人声的检测,在检测到人说话的前后节点触发相应事件 + + 修改示例 [wwe](./examples/wwe/main/main.c) 中的配置,从而使用该场景 + + ```c + #define WAKENET_ENABLE (false) + #define VAD_ENABLE (true) + ``` + + ```mermaid + stateDiagram-v2 + [*] --> IDLE + + IDLE --> SPEECHING: 检测到人声 / (VAD_START) + + SPEECHING --> IDLE: 检测到噪声或静音 / (VAD_END) + ``` + +- **使能唤醒和VAD** + + 该场景结合唤醒词与人声检测,避免在唤醒间隔之外频繁触发人声检测事件 + + 修改示例 [wwe](./examples/wwe/main/main.c) 中的配置,从而使用该场景 + + ```c + #define WAKENET_ENABLE (true) + #define VAD_ENABLE (true) + ``` + + ```mermaid + stateDiagram-v2 + [*] --> IDLE + + IDLE --> WAKEUP: 唤醒词 / (WAKEUP_START) + + WAKEUP --> SPEECHING: 检测到人声 / (VAD_START) + WAKEUP --> IDLE: 定时器(wakeup_time)超时 / (WAKEUP_END) + + SPEECHING --> WAIT_FOR_SLEEP: 检测到噪声或静音 / (VAD_END) + + WAIT_FOR_SLEEP --> SPEECHING: 检测到人声 / (VAD_START) + WAIT_FOR_SLEEP --> IDLE: 定时器(wakeup_end)超时 / (WAKEUP_END) + ``` + +### 命令词检测 + +用户需决定何时开始检测命令词,如典型的使用场景:在状态机推送(WAKEUP_START)事件之后,开启检测,并在回调函数中根据检测到的命令词序号决定下一步操作 + +- 命令词检测与唤醒状态机独立 +- 命令词支持连续检测,直到超时 + +## 示例 + +示例代码,请参阅 [示例](./examples) 文件夹中的唤醒词检测和音频回声消除程序。 + +此外,也可通过以下命令创建并编译工程,以 `aec_rec` 示例工程为例。开始之前需要有可运行的 [ESP-IDF](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/get-started/index.html) 环境。 + +### 1. 创建示例工程 + +基于 `gmf_ai_audio` 组件创建 `aec_rec` 的示例(以 v0.7.0 版本为例, 请根据实际使用更新版本参数) + +``` shell +idf.py create-project-from-example "espressif/gmf_ai_audio=0.7.0:aec_rec" +``` + +### 2. 基于 ESP32S3 编译和下载 + +```shell +cd aec_rec +idf.py set-target esp32s3` +idf.py -p YOUR_PORT flash monitor +``` diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/aec_rec/CMakeLists.txt b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/aec_rec/CMakeLists.txt new file mode 100644 index 0000000..23233bb --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/aec_rec/CMakeLists.txt @@ -0,0 +1,7 @@ +# The following lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) + +project(aec_rec) diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/aec_rec/README.md b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/aec_rec/README.md new file mode 100644 index 0000000..8a629a3 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/aec_rec/README.md @@ -0,0 +1,244 @@ +# Play Music from SDCard While Recording Audio with AEC + +- [中文版](./README_CN.md) + +## Example Brief + +This example uses two pipelines to demonstrate the audio recording effect of AEC. One pipeline reads MP3 files stored on the SD card using File IO, then decodes the data through a decoder element. After decoding, the data undergoes audio effect processing and is output as music through the CODEC_DEV_TX IO. Simultaneously, another pipeline uses CODEC_DEV_RX IO to read PCM data from the I2S device, processes it with AEC, encodes the audio, and finally saves it as a file using File IO + +This example supports compressing the audio processed by AEC into the `AAC` format, which can be enabled through the macro `ENCODER_ENABLE` in [main.c](./main/main.c): + +```c +#define ENCODER_ENABLE (false) +``` + +- `ENCODER_ENABLE`: Enabling this option will activate the `AAC` encoder in the recording pipeline and save the encoded data as `aec.aac` + +## GMF Pipeline + +Below is the diagram of the pipelines used in this example: + +```mermaid +graph TD + subgraph Path1 [MP3 Player] + A[File IO] --> B[Decoder: MP3] --> C[Audio Effects] --> D[CODEC_DEV_TX] + end + subgraph Path2 [AEC Recorder] + E[CODEC_DEV_RX] --> F[Rate_cvt] --> G[AEC] --> H[Encoder: AAC] --> I[File IO] + end +``` + +## AEC Element Initialization Example + +```c +esp_gmf_element_handle_t gmf_aec_handle = NULL; +esp_gmf_aec_cfg_t gmf_aec_cfg = { + .filter_len = 4, + .type = AFE_TYPE_VC, + .mode = AFE_MODE_HIGH_PERF, + .input_format = "RMNM", +}; +esp_gmf_aec_init(&gmf_aec_cfg, &gmf_aec_handle); +``` + +### Parameter Explanation + +- `filter_len`: Filter length. Higher values increase CPU load. For `esp32s3` and `esp32p4`, a filter length of 4 is recommended. For `esp32c5`, a filter length of 2 is recommended +- `type`: AEC type, with the following options: + - `AFE_TYPE_VC`: Suitable for voice communication echo cancellation + - `AFE_TYPE_SR`: Suitable for voice recognition echo cancellation +- `mode`: AEC mode, with the following options: + - `AFE_MODE_LOW_POWER`: Low-power mode, suitable for power-sensitive scenarios + - `AFE_MODE_HIGH_PERF`: High-performance mode, suitable for scenarios requiring high-quality echo cancellation +- `input_format`: Input data format, such as `RMNM`: + - `M`: Microphone channel + - `R`: Reference signal channel + - `N`: Invalid signal + +By configuring these parameters, you can adjust the performance and resource usage of AEC according to specific application scenarios. Once added to the `GMF Pool`, the AEC element can be used in the `GMF Pipeline` to process audio streams + +## Example Set Up + +### Default IDF Branch + +This example supports IDF release/v5.3 and later branches + +### Build and Flash + +Before compiling this example, ensure that the ESP-IDF environment is properly configured. If it is already set up, you can proceed to the next configuration step. If not, run the following script in the root directory of ESP-IDF to set up the build environment. For detailed steps on configuring and using ESP-IDF, please refer to the [ESP-IDF Programming Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/index.html) + +``` +./install.sh +. ./export.sh +``` + +Here are the summarized steps for compilation: + +- Enter the location where the test project is stored: + +``` +cd gmf_ai_audio/examples/aec_rec +``` + +- Select the target chip for compilation. For example, to use the ESP32S3: + +``` +idf.py set-target esp32s3 +``` +- Select the compilation board, taking ESP32-S3-Korvo V2 as an example: + +``` +idf.py menuconfig +In 'menuconfig', select 'GMF APP Configuration' -> 'Audio Board' -> 'ESP32-S3-Korvo V2', and then save and exit +``` + +- Build the example: + +``` +idf.py build +``` + +- Flash the program and run the monitor tool to view serial output (replace PORT with the port name): + +``` +idf.py -p PORT flash monitor +``` + +- Exit the debugging interface using ``Ctrl-]`` + +## How to Use the Example + +- This example requires an SDCard. Prepare an MP3 file and store it on the SDCard +- Before compiling the program, modify the value of `ENCODER_ENABLE` in `main.c` to enable or disable the `AAC` encoder +- Before running the example, ensure the SDCard is correctly installed on the development board +- During the example's execution, you can speak to the development board to verify the AEC effect later +- After the example finishes running, you can export the files from the SDCard and use software to check the recording content: + - If encoding is disabled, the recording file will be named `aec_16k_16bit_1ch.pcm` with a format of 16K sampling rate, 16-bit width, and 1 channel + - If encoding is enabled, the recording file will be named `aec.aac` + +### Example Functionality + +- Once the example starts running, it will read MP3 audio from the SDCard and play it while recording and saving to the SDCard. After 20 seconds, it will stop and exit, printing the following: + +```c +I (926) gpio: GPIO[15]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0 +I (935) gpio: GPIO[7]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0 +I (944) gpio: GPIO[4]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0 +I (995) gpio: GPIO[17]| InputEn: 1| OutputEn: 1| OpenDrain: 1| Pullup: 1| Pulldown: 0| Intr:0 +I (996) gpio: GPIO[18]| InputEn: 1| OutputEn: 1| OpenDrain: 1| Pullup: 1| Pulldown: 0| Intr:0 +I (1010) ES8311: Work in Slave mode +I (1013) gpio: GPIO[48]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0 +E (1017) i2s_common: i2s_channel_disable(1116): the channel has not been enabled yet +I (1025) I2S_IF: channel mode 0 bits:32/32 channel:2 mask:3 +I (1031) I2S_IF: STD Mode 1 bits:32/32 channel:2 sample_rate:48000 mask:3 +I (1053) Adev_Codec: Open codec device OK +I (1057) ES7210: Work in Slave mode +I (1063) ES7210: Enable ES7210_INPUT_MIC1 +I (1066) ES7210: Enable ES7210_INPUT_MIC2 +I (1069) ES7210: Enable ES7210_INPUT_MIC3 +I (1072) ES7210: Enable TDM mode +E (1077) i2s_common: i2s_channel_disable(1116): the channel has not been enabled yet +I (1078) I2S_IF: channel mode 0 bits:32/32 channel:2 mask:3 +I (1085) I2S_IF: STD Mode 0 bits:32/32 channel:2 sample_rate:48000 mask:3 +I (1093) ES7210: Bits 16 +I (1102) ES7210: Enable ES7210_INPUT_MIC1 +I (1105) ES7210: Enable ES7210_INPUT_MIC2 +I (1108) ES7210: Enable ES7210_INPUT_MIC3 +I (1112) ES7210: Enable TDM mode +I (1118) ES7210: Unmuted +I (1118) Adev_Codec: Open codec device OK +I (1123) ESP_GMF_POOL: Registered items on pool:0x3c23ddf8, app_main-142 +I (1129) ESP_GMF_POOL: IO, Item:0x3c23decc, H:0x3c23de0c, TAG:io_http +I (1135) ESP_GMF_POOL: IO, Item:0x3c23df9c, H:0x3c23dedc, TAG:io_http +I (1142) ESP_GMF_POOL: IO, Item:0x3c23e038, H:0x3c23dfac, TAG:io_file +I (1149) ESP_GMF_POOL: IO, Item:0x3c23e0d4, H:0x3c23e048, TAG:io_file +I (1156) ESP_GMF_POOL: IO, Item:0x3c23e204, H:0x3c23e170, TAG:io_embed_flash +I (1163) ESP_GMF_POOL: IO, Item:0x3c23ede0, H:0x3c23ed50, TAG:io_codec_dev +I (1171) ESP_GMF_POOL: IO, Item:0x3c23ee80, H:0x3c23edf0, TAG:io_codec_dev +I (1178) ESP_GMF_POOL: EL, Item:0x3c23e160, H:0x3c23e0e4, TAG:copier +I (1185) ESP_GMF_POOL: EL, Item:0x3c23e2e8, H:0x3c23e214, TAG:aud_enc +I (1193) ESP_GMF_POOL: EL, Item:0x3c23e3e8, H:0x3c23e2f8, TAG:aud_dec +I (1200) ESP_GMF_POOL: EL, Item:0x3c23e4bc, H:0x3c23e3f8, TAG:aud_alc +I (1207) ESP_GMF_POOL: EL, Item:0x3c23e570, H:0x3c23e4cc, TAG:aud_eq +I (1214) ESP_GMF_POOL: EL, Item:0x3c23e5a4, H:0x3c23e6d8, TAG:aud_ch_cvt +I (1221) ESP_GMF_POOL: EL, Item:0x3c23e5d4, H:0x3c23e774, TAG:aud_bit_cvt +I (1228) ESP_GMF_POOL: EL, Item:0x3c23e8ac, H:0x3c23e810, TAG:aud_rate_cvt +I (1235) ESP_GMF_POOL: EL, Item:0x3c23e988, H:0x3c23e8bc, TAG:aud_fade +I (1242) ESP_GMF_POOL: EL, Item:0x3c23ea84, H:0x3c23e998, TAG:aud_sonic +I (1249) ESP_GMF_POOL: EL, Item:0x3c23eb64, H:0x3c23ea94, TAG:aud_deintlv +I (1256) ESP_GMF_POOL: EL, Item:0x3c23ec40, H:0x3c23eb74, TAG:aud_intlv +I (1264) ESP_GMF_POOL: EL, Item:0x3c23ed40, H:0x3c23ec50, TAG:aud_mixer +I (1271) ESP_GMF_POOL: EL, Item:0x3c23ef60, H:0x3c23ee90, TAG:ai_aec +W (1278) ESP_GMF_PIPELINE: There is no thread for add jobs, pipe:0x3c23ef70, tsk:0x0, [el:aud_rate_cvt-0x3c23efa8] +I (1288) ESP_GMF_THREAD: The TSK_0x3fcb5404 created on internal memory +I (1288) ESP_GMF_TASK: Waiting to run... [tsk:TSK_0x3fcb5404-0x3fcb5404, wk:0x0, run:0] +I (1304) AEC_EL_2_FILE: CB: RECV Pipeline EVT: el:NULL-0x3c23ef70, type:8192, sub:ESP_GMF_EVENT_STATE_OPENING, payload:0x0, size:0,0x0 +I (1328) ESP_GMF_PORT: ACQ IN, new self payload:0x3c23f35c, port:0x3c23f2a8, el:0x3c23efa8-aud_rate_cvt +I (1329) ESP_GMF_PORT: ACQ OUT SET, new self payload:0x3c23fe68, p:0x3c23f1a8, el:0x3c23efa8-aud_rate_cvt +I (1358) GMF_AEC: GMF AEC open, frame_len: 2048, nch 4, chunksize 256 +I (1359) AEC_EL_2_FILE: CB: RECV Pipeline EVT: el:ai_aec-0x3c23f0c4, type:12288, sub:ESP_GMF_EVENT_STATE_INITIALIZED, payload:0x3fcb6790, size:12,0x0 +I (1371) AEC_EL_2_FILE: CB: RECV Pipeline EVT: el:ai_aec-0x3c23f0c4, type:8192, sub:ESP_GMF_EVENT_STATE_RUNNING, payload:0x0, size:0,0x0 +I (1383) ESP_GMF_TASK: One times job is complete, del[wk:0x3c23f3dc,ctx:0x3c23f0c4, label:aec_open] +I (1401) ESP_GMF_TASK: Waiting to run... [tsk:TSK_0x3fcca2f8-0x3fcca2f8, wk:0x0, run:0] +I (1401) ESP_GMF_THREAD: The TSK_0x3fcca2f8 created on internal memory +I (1432) ESP_GMF_TASK: Waiting to run... [tsk:TSK_0x3fcca2f8-0x3fcca2f8, wk:0x3c252ac0, run:0] +I (1433) ESP_GMF_FILE: Open, dir:1, uri:/sdcard/test.mp3 +I (1446) ESP_GMF_FILE: File size: 2994349 byte, file position: 0 +I (1447) AEC_EL_2_FILE: CB: RECV Pipeline EVT: el:NULL-0x3c25230c, type:8192 +I (1460) ESP_GMF_TASK: One times job is complete, del[wk:0x3c252ac0,ctx:0x3c252344, label:aud_simp_dec_open] +I (1469) ESP_GMF_PORT: ACQ IN, new self payload:0x3c252ac0, port:0x3c252964, el:0x3c252344-aud_dec +I (1482) ESP_GMF_PORT: ACQ OUT SET, new self payload:0x3c252d64, p:0x3c25252c, el:0x3c252344-aud_dec +W (1491) ESP_GMF_ASMP_DEC: Not enough memory for out, need:4608, old: 1024, new: 4608 +I (1505) ESP_GMF_ASMP_DEC: NOTIFY Info, rate: 0, bits: 0, ch: 0 --> rate: 44100, bits: 16, ch: 2 +Audio > +I (1589) ESP_GMF_TASK: One times job is complete, del[wk:0x3c253fa4,ctx:0x3c252420, label:rate_cvt_open] +I (1590) ESP_GMF_PORT: ACQ OUT SET, new self payload:0x3c253fa4, p:0x3c2526c4, el:0x3c252420-aud_rate_cvt +I (1603) ESP_GMF_TASK: One times job is complete, del[wk:0x3c253fec,ctx:0x3c2525ac, label:ch_cvt_open] +I (1615) AEC_EL_2_FILE: CB: RECV Pipeline EVT: el:aud_bit_cvt-0x3c252744, type:12288, sub:ESP_GMF_EVENT_STATE_INITIALIZED, payload:0x3fccb680, size:12,0x0 +I (1627) AEC_EL_2_FILE: CB: RECV Pipeline EVT: el:aud_bit_cvt-0x3c252744, type:8192, sub:ESP_GMF_EVENT_STATE_RUNNING, payload:0x0, size:0,0x0 +I (1639) ESP_GMF_TASK: One times job is complete, del[wk:0x3c255608,ctx:0x3c252744, label:bit_cvt_open] +I (1651) ESP_GMF_PORT: ACQ OUT, new self payload:0x3c255608, port:0x3c252a34, el:0x3c252744-aud_bit_cvt +I (21462) ESP_GMF_CODEC_DEV: CLose, 0x3c23f228, pos = 7716864/0 +I (21464) ESP_GMF_TASK: One times job is complete, del[wk:0x3c23f410,ctx:0x3c23efa8, label:rate_cvt_close] +I (21478) ESP_GMF_TASK: One times job is complete, del[wk:0x3c253fec,ctx:0x3c23f0c4, label:aec_close] +I (21479) AEC_EL_2_FILE: CB: RECV Pipeline EVT: el:NULL-0x3c23ef70, type:8192, sub:ESP_GMF_EVENT_STATE_STOPPED, payload:0x0, size:0,0x0 +I (21501) ESP_GMF_TASK: Waiting to run... [tsk:TSK_0x3fcb5404-0x3fcb5404, wk:0x0, run:0] +I (21502) ESP_GMF_TASK: Waiting to run... [tsk:TSK_0x3fcb5404-0x3fcb5404, wk:0x0, run:0] +I (21514) ESP_GMF_FILE: CLose, 0x3c2528d8, pos = 318464/2994349 +I (21525) ESP_GMF_CODEC_DEV: CLose, 0x3c2529a4, pos = 7633608/0 +I (21526) ESP_GMF_TASK: One times job is complete, del[wk:0x3c25562c,ctx:0x3c252344, label:aud_simp_dec_close] +I (21538) ESP_GMF_TASK: One times job is complete, del[wk:0x3c23f410,ctx:0x3c252420, label:rate_cvt_close] +I (21549) ESP_GMF_TASK: One times job is complete, del[wk:0x3c23f380,ctx:0x3c2525ac, label:ch_cvt_close] +I (21561) ESP_GMF_TASK: One times job is complete, del[wk:0x3c23f3b8,ctx:0x3c252744, label:bit_cvt_close] +I (21572) AEC_EL_2_FILE: CB: RECV Pipeline EVT: el:NULL-0x3c25230c, type:8192, sub:ESP_GMF_EVENT_STATE_STOPPED, payload:0x0, size:0,0x0 +I (21584) ESP_GMF_TASK: Waiting to run... [tsk:TSK_0x3fcca2f8-0x3fcca2f8, wk:0x0, run:0] +I (21595) ESP_GMF_TASK: Waiting to run... [tsk:TSK_0x3fcca2f8-0x3fcca2f8, wk:0x0, run:0] +E (25298) i2s_common: i2s_channel_disable(1116): the channel has not been enabled yet +E (25303) i2s_common: i2s_channel_disable(1116): the channel has not been enabled yet +I (25314) main_task: Returned from app_main() +``` + +## Troubleshooting + +### 1. SDCard Not Recognized + +- Ensure the SDCard is formatted as FAT32 +- Check if the SDCard is properly inserted into the development board + +### 2. MP3 File Not Playing + +- Ensure the MP3 file is stored in the root directory of the SDCard +- Verify that the MP3 file is not corrupted +- Ensure the MP3 file is named `test.mp3` + +### 3. No Audio Recorded + +- Verify that the microphone hardware on the development board is functioning correctly +- Ensure the audio channel configuration matches the hardware design + +### 4. High CPU Usage with AEC in the Pipeline + +Since the AEC algorithm requires significant CPU resources, consider the following steps if issues arise: + +- Identify CPU-intensive elements in the pipeline, such as `ai_aec`, `aud_enc`, or `io_file` +- Split the problematic pipeline into two separate pipelines, run them on different CPU cores, and connect them using `gmf port` diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/aec_rec/README_CN.md b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/aec_rec/README_CN.md new file mode 100644 index 0000000..c7438f4 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/aec_rec/README_CN.md @@ -0,0 +1,244 @@ +# 播放 SDCard 音乐同时 AEC 录制音频 + +- [English](./README.md) + +## 例程简介 + +本例程使用两路 pipeline 演示 AEC 录制音频的效果。一路 pipeline 使用 File IO 读取存储在 SDCard 中的 MP3 文件,然后经过 decoder 元素解码,解码后数据进行音频效果处理后用 CODEC_DEV_TX IO 输出音乐。同时,另外一路 pipeline 使用 CODEC_DEV_RX IO 从 I2S 设备读取 PCM 数据,经过 AEC 处理后进行音频编码,最终通过 File IO 保存为文件 + +本例程支持将 AEC 处理后的音频压缩为 `AAC` 格式,通过 [main.c](./main/main.c) 中的宏 `ENCODER_ENABLE` 使能 + +```c +#define ENCODER_ENABLE (false) +``` + +- `ENCODER_ENABLE`: 使能该选项将会在录音的 pipeline 中使能 `AAC` 编码器,并保存编码后的数据为 `aec.aac` + +## GMF Pipeline + +以下是本例程中使用的 pipeline 的示意图: + +```mermaid +graph TD + subgraph Path1 [MP3 Player] + A[File IO] --> B[Decoder: MP3] --> C[Audio Effects] --> D[CODEC_DEV_TX] + end + subgraph Path2 [AEC Recorder] + E[CODEC_DEV_RX] --> F[Rate_cvt] --> G[AEC] --> H[Encoder: AAC] --> I[File IO] + end +``` + +## AEC 元素初始化示例 + +```c +esp_gmf_element_handle_t gmf_aec_handle = NULL; +esp_gmf_aec_cfg_t gmf_aec_cfg = { + .filter_len = 4, + .type = AFE_TYPE_VC, + .mode = AFE_MODE_HIGH_PERF, + .input_format = "RMNM", +}; +esp_gmf_aec_init(&gmf_aec_cfg, &gmf_aec_handle); +``` + +#### 参数说明 + +- `filter_len`: 滤波器长度。数值越大,CPU 负载越高。对于 esp32s3 和 esp32p4,建议 filter_length = 4。对于 esp32c5,建议 filter_length = 2 +- `type`: AEC 类型,支持以下选项: + - `AFE_TYPE_VC`: 适用于语音通信的回声消除 + - `AFE_TYPE_SR`: 适用于语音识别的回声消除 +- `mode`: AEC 模式,支持以下选项: + - `AFE_MODE_LOW_POWER`: 低功耗模式,适用于功耗敏感的场景 + - `AFE_MODE_HIGH_PERF`: 高性能模式,适用于需要高质量回声消除的场景 +- `input_format`: 输入数据的格式, 如 `RMNM`: + - `M`:麦克风通道 + - `R`:回采信号通道 + - `N`:无效信号 + +通过配置这些参数,可以根据具体应用场景调整 AEC 的性能和资源使用。添加到 `GMF Pool` 中后,可在 `GMF Pipeline` 中使用 AEC 处理音频流 + +## 示例创建 + +### IDF 默认分支 + +本例程支持 IDF release/v5.3 及以后的分支 + +### 编译和下载 + +编译本例程前需要先确保已配置 ESP-IDF 的环境,如果已配置可跳到下一项配置,如果未配置需要先在 ESP-IDF 根目录运行下面脚本设置编译环境,有关配置和使用 ESP-IDF 完整步骤,请参阅 [《ESP-IDF 编程指南》](https://docs.espressif.com/projects/esp-idf/zh_CN/latest/esp32s3/index.html) + +``` +./install.sh +. ./export.sh +``` + +下面是简略编译步骤: + +- 进入示例工程的位置 + +``` +cd gmf_ai_audio/examples/aec_rec +``` + +- 选择编译芯片,以 esp32s3 为例: + +``` +idf.py set-target esp32s3 +``` +- 选择编译目标板,以 ESP32-S3-Korvo V2 为例: + +``` +idf.py menuconfig +在 `menuconfig` 中选择 `GMF APP Configuration` -> `Audio Board` -> `ESP32-S3-Korvo V2`,然后保存退出 +``` + +- 编译例子程序 + +``` +idf.py build +``` + +- 烧录程序并运行 monitor 工具来查看串口输出 (替换 PORT 为端口名称): + +``` +idf.py -p PORT flash monitor +``` + +- 退出调试界面使用 ``Ctrl-]`` + +## 如何使用例程 + +- 本例程需要 SDCard 和 MP3 文件,用户需准备一份 MP3 文件并存入 SDCard 中 +- 编译程序前,可以通过 `main.c` 中的 `ENCODER_ENABLE` 的值来决定是否启用 `AAC` 编码 +- 运行例程前,请确保 SDCard 已正确安装到开发板上 +- 运行例程时,用户可以对开发板说话,以便后续验证 AEC 效果 +- 运行结束后,用户可以将 SDCard 中的文件导出,用软件查看录音内容: + - 如果未启用编码,录音文件名为 `aec_16k_16bit_1ch.pcm`,格式为 16K 采样率,16 bit 位宽,单声道 + - 如果启用编码,录音文件名为 `aec.aac` + +### 功能和用法 + +- 例程运行后,将从 SDCard 中读取 MP3 音频进行播放,同时录音并保存到 SDCard。运行 20 秒后停止并退出,串口打印如下日志: + +```c +I (926) gpio: GPIO[15]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0 +I (935) gpio: GPIO[7]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0 +I (944) gpio: GPIO[4]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0 +I (995) gpio: GPIO[17]| InputEn: 1| OutputEn: 1| OpenDrain: 1| Pullup: 1| Pulldown: 0| Intr:0 +I (996) gpio: GPIO[18]| InputEn: 1| OutputEn: 1| OpenDrain: 1| Pullup: 1| Pulldown: 0| Intr:0 +I (1010) ES8311: Work in Slave mode +I (1013) gpio: GPIO[48]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0 +E (1017) i2s_common: i2s_channel_disable(1116): the channel has not been enabled yet +I (1025) I2S_IF: channel mode 0 bits:32/32 channel:2 mask:3 +I (1031) I2S_IF: STD Mode 1 bits:32/32 channel:2 sample_rate:48000 mask:3 +I (1053) Adev_Codec: Open codec device OK +I (1057) ES7210: Work in Slave mode +I (1063) ES7210: Enable ES7210_INPUT_MIC1 +I (1066) ES7210: Enable ES7210_INPUT_MIC2 +I (1069) ES7210: Enable ES7210_INPUT_MIC3 +I (1072) ES7210: Enable TDM mode +E (1077) i2s_common: i2s_channel_disable(1116): the channel has not been enabled yet +I (1078) I2S_IF: channel mode 0 bits:32/32 channel:2 mask:3 +I (1085) I2S_IF: STD Mode 0 bits:32/32 channel:2 sample_rate:48000 mask:3 +I (1093) ES7210: Bits 16 +I (1102) ES7210: Enable ES7210_INPUT_MIC1 +I (1105) ES7210: Enable ES7210_INPUT_MIC2 +I (1108) ES7210: Enable ES7210_INPUT_MIC3 +I (1112) ES7210: Enable TDM mode +I (1118) ES7210: Unmuted +I (1118) Adev_Codec: Open codec device OK +I (1123) ESP_GMF_POOL: Registered items on pool:0x3c23ddf8, app_main-142 +I (1129) ESP_GMF_POOL: IO, Item:0x3c23decc, H:0x3c23de0c, TAG:io_http +I (1135) ESP_GMF_POOL: IO, Item:0x3c23df9c, H:0x3c23dedc, TAG:io_http +I (1142) ESP_GMF_POOL: IO, Item:0x3c23e038, H:0x3c23dfac, TAG:io_file +I (1149) ESP_GMF_POOL: IO, Item:0x3c23e0d4, H:0x3c23e048, TAG:io_file +I (1156) ESP_GMF_POOL: IO, Item:0x3c23e204, H:0x3c23e170, TAG:io_embed_flash +I (1163) ESP_GMF_POOL: IO, Item:0x3c23ede0, H:0x3c23ed50, TAG:io_codec_dev +I (1171) ESP_GMF_POOL: IO, Item:0x3c23ee80, H:0x3c23edf0, TAG:io_codec_dev +I (1178) ESP_GMF_POOL: EL, Item:0x3c23e160, H:0x3c23e0e4, TAG:copier +I (1185) ESP_GMF_POOL: EL, Item:0x3c23e2e8, H:0x3c23e214, TAG:aud_enc +I (1193) ESP_GMF_POOL: EL, Item:0x3c23e3e8, H:0x3c23e2f8, TAG:aud_dec +I (1200) ESP_GMF_POOL: EL, Item:0x3c23e4bc, H:0x3c23e3f8, TAG:aud_alc +I (1207) ESP_GMF_POOL: EL, Item:0x3c23e570, H:0x3c23e4cc, TAG:aud_eq +I (1214) ESP_GMF_POOL: EL, Item:0x3c23e5a4, H:0x3c23e6d8, TAG:aud_ch_cvt +I (1221) ESP_GMF_POOL: EL, Item:0x3c23e5d4, H:0x3c23e774, TAG:aud_bit_cvt +I (1228) ESP_GMF_POOL: EL, Item:0x3c23e8ac, H:0x3c23e810, TAG:aud_rate_cvt +I (1235) ESP_GMF_POOL: EL, Item:0x3c23e988, H:0x3c23e8bc, TAG:aud_fade +I (1242) ESP_GMF_POOL: EL, Item:0x3c23ea84, H:0x3c23e998, TAG:aud_sonic +I (1249) ESP_GMF_POOL: EL, Item:0x3c23eb64, H:0x3c23ea94, TAG:aud_deintlv +I (1256) ESP_GMF_POOL: EL, Item:0x3c23ec40, H:0x3c23eb74, TAG:aud_intlv +I (1264) ESP_GMF_POOL: EL, Item:0x3c23ed40, H:0x3c23ec50, TAG:aud_mixer +I (1271) ESP_GMF_POOL: EL, Item:0x3c23ef60, H:0x3c23ee90, TAG:ai_aec +W (1278) ESP_GMF_PIPELINE: There is no thread for add jobs, pipe:0x3c23ef70, tsk:0x0, [el:aud_rate_cvt-0x3c23efa8] +I (1288) ESP_GMF_THREAD: The TSK_0x3fcb5404 created on internal memory +I (1288) ESP_GMF_TASK: Waiting to run... [tsk:TSK_0x3fcb5404-0x3fcb5404, wk:0x0, run:0] +I (1304) AEC_EL_2_FILE: CB: RECV Pipeline EVT: el:NULL-0x3c23ef70, type:8192, sub:ESP_GMF_EVENT_STATE_OPENING, payload:0x0, size:0,0x0 +I (1328) ESP_GMF_PORT: ACQ IN, new self payload:0x3c23f35c, port:0x3c23f2a8, el:0x3c23efa8-aud_rate_cvt +I (1329) ESP_GMF_PORT: ACQ OUT SET, new self payload:0x3c23fe68, p:0x3c23f1a8, el:0x3c23efa8-aud_rate_cvt +I (1358) GMF_AEC: GMF AEC open, frame_len: 2048, nch 4, chunksize 256 +I (1359) AEC_EL_2_FILE: CB: RECV Pipeline EVT: el:ai_aec-0x3c23f0c4, type:12288, sub:ESP_GMF_EVENT_STATE_INITIALIZED, payload:0x3fcb6790, size:12,0x0 +I (1371) AEC_EL_2_FILE: CB: RECV Pipeline EVT: el:ai_aec-0x3c23f0c4, type:8192, sub:ESP_GMF_EVENT_STATE_RUNNING, payload:0x0, size:0,0x0 +I (1383) ESP_GMF_TASK: One times job is complete, del[wk:0x3c23f3dc,ctx:0x3c23f0c4, label:aec_open] +I (1401) ESP_GMF_TASK: Waiting to run... [tsk:TSK_0x3fcca2f8-0x3fcca2f8, wk:0x0, run:0] +I (1401) ESP_GMF_THREAD: The TSK_0x3fcca2f8 created on internal memory +I (1432) ESP_GMF_TASK: Waiting to run... [tsk:TSK_0x3fcca2f8-0x3fcca2f8, wk:0x3c252ac0, run:0] +I (1433) ESP_GMF_FILE: Open, dir:1, uri:/sdcard/test.mp3 +I (1446) ESP_GMF_FILE: File size: 2994349 byte, io_file position: 0 +I (1447) AEC_EL_2_FILE: CB: RECV Pipeline EVT: el:NULL-0x3c25230c, type:8192 +I (1460) ESP_GMF_TASK: One times job is complete, del[wk:0x3c252ac0,ctx:0x3c252344, label:aud_simp_dec_open] +I (1469) ESP_GMF_PORT: ACQ IN, new self payload:0x3c252ac0, port:0x3c252964, el:0x3c252344-aud_dec +I (1482) ESP_GMF_PORT: ACQ OUT SET, new self payload:0x3c252d64, p:0x3c25252c, el:0x3c252344-aud_dec +W (1491) ESP_GMF_ASMP_DEC: Not enough memory for out, need:4608, old: 1024, new: 4608 +I (1505) ESP_GMF_ASMP_DEC: NOTIFY Info, rate: 0, bits: 0, ch: 0 --> rate: 44100, bits: 16, ch: 2 +Audio > +I (1589) ESP_GMF_TASK: One times job is complete, del[wk:0x3c253fa4,ctx:0x3c252420, label:rate_cvt_open] +I (1590) ESP_GMF_PORT: ACQ OUT SET, new self payload:0x3c253fa4, p:0x3c2526c4, el:0x3c252420-aud_rate_cvt +I (1603) ESP_GMF_TASK: One times job is complete, del[wk:0x3c253fec,ctx:0x3c2525ac, label:ch_cvt_open] +I (1615) AEC_EL_2_FILE: CB: RECV Pipeline EVT: el:aud_bit_cvt-0x3c252744, type:12288, sub:ESP_GMF_EVENT_STATE_INITIALIZED, payload:0x3fccb680, size:12,0x0 +I (1627) AEC_EL_2_FILE: CB: RECV Pipeline EVT: el:aud_bit_cvt-0x3c252744, type:8192, sub:ESP_GMF_EVENT_STATE_RUNNING, payload:0x0, size:0,0x0 +I (1639) ESP_GMF_TASK: One times job is complete, del[wk:0x3c255608,ctx:0x3c252744, label:bit_cvt_open] +I (1651) ESP_GMF_PORT: ACQ OUT, new self payload:0x3c255608, port:0x3c252a34, el:0x3c252744-aud_bit_cvt +I (21462) ESP_GMF_CODEC_DEV: CLose, 0x3c23f228, pos = 7716864/0 +I (21464) ESP_GMF_TASK: One times job is complete, del[wk:0x3c23f410,ctx:0x3c23efa8, label:rate_cvt_close] +I (21478) ESP_GMF_TASK: One times job is complete, del[wk:0x3c253fec,ctx:0x3c23f0c4, label:aec_close] +I (21479) AEC_EL_2_FILE: CB: RECV Pipeline EVT: el:NULL-0x3c23ef70, type:8192, sub:ESP_GMF_EVENT_STATE_STOPPED, payload:0x0, size:0,0x0 +I (21501) ESP_GMF_TASK: Waiting to run... [tsk:TSK_0x3fcb5404-0x3fcb5404, wk:0x0, run:0] +I (21502) ESP_GMF_TASK: Waiting to run... [tsk:TSK_0x3fcb5404-0x3fcb5404, wk:0x0, run:0] +I (21514) ESP_GMF_FILE: CLose, 0x3c2528d8, pos = 318464/2994349 +I (21525) ESP_GMF_CODEC_DEV: CLose, 0x3c2529a4, pos = 7633608/0 +I (21526) ESP_GMF_TASK: One times job is complete, del[wk:0x3c25562c,ctx:0x3c252344, label:aud_simp_dec_close] +I (21538) ESP_GMF_TASK: One times job is complete, del[wk:0x3c23f410,ctx:0x3c252420, label:rate_cvt_close] +I (21549) ESP_GMF_TASK: One times job is complete, del[wk:0x3c23f380,ctx:0x3c2525ac, label:ch_cvt_close] +I (21561) ESP_GMF_TASK: One times job is complete, del[wk:0x3c23f3b8,ctx:0x3c252744, label:bit_cvt_close] +I (21572) AEC_EL_2_FILE: CB: RECV Pipeline EVT: el:NULL-0x3c25230c, type:8192, sub:ESP_GMF_EVENT_STATE_STOPPED, payload:0x0, size:0,0x0 +I (21584) ESP_GMF_TASK: Waiting to run... [tsk:TSK_0x3fcca2f8-0x3fcca2f8, wk:0x0, run:0] +I (21595) ESP_GMF_TASK: Waiting to run... [tsk:TSK_0x3fcca2f8-0x3fcca2f8, wk:0x0, run:0] +E (25298) i2s_common: i2s_channel_disable(1116): the channel has not been enabled yet +E (25303) i2s_common: i2s_channel_disable(1116): the channel has not been enabled yet +I (25314) main_task: Returned from app_main() +``` + +## 常见问题 + +### 1. 无法识别 SDCard + +- 确保 SDCard 已正确格式化为 FAT32 文件系统 +- 检查 SDCard 是否正确插入开发板 + +### 2. 无法播放 MP3 文件 + +- 确保 MP3 文件存储在 SDCard 的根目录中 +- 确保 MP3 文件未损坏 +- 确保 MP3 文件名为 `test.mp3` + +### 3. 录制音频无声音 + +- 确保开发板的麦克风硬件正常工作 +- 确保语音通道配置符合硬件设计 + +### 4. 集成 AEC 的 Pipeline CPU 占用过高 + +由于 AEC 算法需要的 cpu 算力较多,所以组建包含 AEC 元素的 Pipeline 时,需合理分配资源,如遇到问题,可按如下方式处理: + +- 查看 Pipeline 有哪些需要 cpu 算力较多的元素,如 `ai_aec`, `aud_enc`, `io_file` 等 +- 将出现问题的 Pipeline 拆分为两个,分别运行到不同的 cpu 核心上,使用 `gmf port` 连接 diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/aec_rec/main/CMakeLists.txt b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/aec_rec/main/CMakeLists.txt new file mode 100644 index 0000000..cf2c455 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/aec_rec/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "main.c" + INCLUDE_DIRS ".") diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/aec_rec/main/idf_component.yml b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/aec_rec/main/idf_component.yml new file mode 100644 index 0000000..1e402e6 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/aec_rec/main/idf_component.yml @@ -0,0 +1,19 @@ +dependencies: + espressif/gmf_loader: + override_path: ../../../../../packages/gmf_loader + version: "^0.7" + espressif/gmf_app_utils: + override_path: ../../../../../packages/gmf_app_utils + version: "^0.7" + espressif/gmf_core: + override_path: ../../../../../gmf_core + espressif/gmf_io: + override_path: ../../../../gmf_io + espressif/gmf_audio: + override_path: ../../../../gmf_audio + espressif/gmf_ai_audio: + override_path: ../../../../gmf_ai_audio + espressif/gmf_video: + override_path: ../../../../gmf_video + espressif/gmf_misc: + override_path: ../../../../gmf_misc diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/aec_rec/main/main.c b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/aec_rec/main/main.c new file mode 100644 index 0000000..c55cf31 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/aec_rec/main/main.c @@ -0,0 +1,219 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include "esp_err.h" +#include "esp_log.h" + +#include "esp_gmf_err.h" +#include "esp_gmf_io_codec_dev.h" +#include "esp_gmf_obj.h" +#include "esp_gmf_payload.h" +#include "esp_gmf_port.h" +#include "esp_gmf_aec.h" +#include "esp_gmf_element.h" +#include "esp_gmf_pipeline.h" +#include "esp_gmf_pool.h" +#include "esp_gmf_rate_cvt.h" +#include "esp_gmf_bit_cvt.h" +#include "esp_gmf_ch_cvt.h" +#include "esp_gmf_audio_dec.h" +#include "esp_gmf_app_setup_peripheral.h" +#include "esp_gmf_app_cli.h" +#include "gmf_loader_setup_defaults.h" + +#define BOARD_LYRAT_MINI (0) +#define BOARD_KORVO_2 (1) + +#if defined CONFIG_IDF_TARGET_ESP32S3 +#define AUDIO_BOARD (BOARD_KORVO_2) +#elif defined CONFIG_IDF_TARGET_ESP32 +#define AUDIO_BOARD (BOARD_LYRAT_MINI) +#endif /* defined CONFIG_IDF_TARGET_ESP32S3 */ + +#if AUDIO_BOARD == BOARD_KORVO_2 +#define ADC_I2S_PORT (0) +#define ADC_I2S_CH (2) +#define ADC_I2S_BITS (32) +#define DAC_I2S_PORT (0) +#define DAC_I2S_CH (2) +#define DAC_I2S_BITS (32) +#define INPUT_CH_NUM (4) +#define INPUT_CH_BITS (16) /* For board `ESP32-S3-Korvo-2`, the es7210 is configured as 32-bit, + 2-channel mode to accommodate 16-bit, 4-channel data */ +#define INPUT_CH_ALLOCATION ("RMNM") +#elif AUDIO_BOARD == BOARD_LYRAT_MINI +#define ADC_I2S_PORT (1) +#define ADC_I2S_CH (2) +#define ADC_I2S_BITS (16) +#define DAC_I2S_PORT (0) +#define DAC_I2S_CH (1) +#define DAC_I2S_BITS (16) +#define INPUT_CH_NUM (ADC_I2S_CH) +#define INPUT_CH_BITS (ADC_I2S_BITS) +#define INPUT_CH_ALLOCATION ("RM") +#endif /* AUDIO_BOARD == BOARD_KORVO_2 */ + +#define ENCODER_ENABLE (false) + +static const char *TAG = "AEC_EL_2_FILE"; + +static uint8_t *pcm_buffer = NULL; +const uint32_t buf_size = 600 * 1024; +static size_t pcm_received = 0; + +static esp_err_t _pipeline_event(esp_gmf_event_pkt_t *event, void *ctx) +{ + ESP_LOGI(TAG, "CB: RECV Pipeline EVT: el:%s-%p, type:%d, sub:%s, payload:%p, size:%d,%p", + OBJ_GET_TAG(event->from), event->from, event->type, esp_gmf_event_get_state_str(event->sub), + event->payload, event->payload_size, ctx); + return ESP_OK; +} + +static esp_gmf_err_io_t pcm_buf_acq_write(void *handle, esp_gmf_payload_t *load, int wanted_size, int block_ticks) +{ + return ESP_GMF_IO_OK; +} + +static esp_gmf_err_io_t pcm_buf_release_write(void *handle, esp_gmf_payload_t *load, int block_ticks) +{ + if (load == NULL) { + return ESP_GMF_IO_FAIL; + } + if (load->valid_size && (pcm_received + load->valid_size <= buf_size)) { + memcpy(pcm_buffer + pcm_received, load->buf, load->valid_size); + pcm_received += load->valid_size; + } + return ESP_GMF_IO_OK; +} + +void app_main(void) +{ + esp_log_level_set("*", ESP_LOG_INFO); + + pcm_buffer = heap_caps_malloc(buf_size, MALLOC_CAP_SPIRAM); + if (!pcm_buffer) { + ESP_LOGE(TAG, "Failed to allocate SPIRAM buffer"); + return; + } + pcm_received = 0; + + esp_gmf_app_codec_info_t codec_info = ESP_GMF_APP_CODEC_INFO_DEFAULT(); + codec_info.play_info.sample_rate = 48000; + codec_info.play_info.channel = DAC_I2S_CH; + codec_info.play_info.bits_per_sample = DAC_I2S_BITS; + codec_info.record_info.sample_rate = codec_info.play_info.sample_rate; + codec_info.record_info.channel = ADC_I2S_CH; + codec_info.record_info.bits_per_sample = ADC_I2S_BITS; + esp_gmf_app_setup_codec_dev(&codec_info); + + void *sdcard_handle = NULL; + esp_gmf_app_setup_sdcard(&sdcard_handle); + + esp_gmf_pool_handle_t pool = NULL; + esp_gmf_pool_init(&pool); + gmf_loader_setup_all_defaults(pool); + + ESP_GMF_POOL_SHOW_ITEMS(pool); + esp_gmf_pipeline_handle_t read_pipe = NULL; + +#if ENCODER_ENABLE + const char *name[] = {"aud_rate_cvt", "ai_aec", "aud_enc"}; +#else + const char *name[] = {"aud_rate_cvt", "ai_aec"}; +#endif /* ENCODER_ENABLE */ + esp_gmf_pool_new_pipeline(pool, "io_codec_dev", name, sizeof(name) / sizeof(char *), NULL, &read_pipe); + if (read_pipe == NULL) { + ESP_LOGE(TAG, "There is no pipeline"); + return; + } + esp_gmf_io_codec_dev_set_dev(ESP_GMF_PIPELINE_GET_IN_INSTANCE(read_pipe), esp_gmf_app_get_record_handle()); + + esp_gmf_port_handle_t out_port = NEW_ESP_GMF_PORT_OUT_BYTE(pcm_buf_acq_write, pcm_buf_release_write, NULL, NULL, 1024, portMAX_DELAY); + esp_gmf_element_register_out_port(read_pipe->last_el, out_port); + esp_gmf_obj_handle_t rate_cvt = NULL; + esp_gmf_pipeline_get_el_by_name(read_pipe, "aud_rate_cvt", &rate_cvt); + esp_gmf_rate_cvt_set_dest_rate(rate_cvt, 16000); + + esp_gmf_info_sound_t info = { + .format_id = ESP_AUDIO_SIMPLE_DEC_TYPE_MP3, + .sample_rates = 48000, + .channels = INPUT_CH_NUM, + .bits = INPUT_CH_BITS, + }; + esp_gmf_pipeline_report_info(read_pipe, ESP_GMF_INFO_SOUND, &info, sizeof(info)); + esp_gmf_task_cfg_t cfg = DEFAULT_ESP_GMF_TASK_CONFIG(); + cfg.thread.core = 1; + cfg.thread.stack = 5120; + esp_gmf_task_handle_t read_task = NULL; + esp_gmf_task_init(&cfg, &read_task); + + esp_gmf_pipeline_bind_task(read_pipe, read_task); + esp_gmf_pipeline_loading_jobs(read_pipe); + esp_gmf_pipeline_set_event(read_pipe, _pipeline_event, NULL); + esp_gmf_pipeline_run(read_pipe); + + esp_gmf_app_cli_init("Audio >", NULL); + + // New pipeline to play 'test.mp3' + esp_gmf_pipeline_handle_t play_pipe = NULL; + const char *play_name[] = {"aud_dec", "aud_rate_cvt", "aud_ch_cvt", "aud_bit_cvt"}; + esp_gmf_pool_new_pipeline(pool, "io_file", play_name, sizeof(play_name) / sizeof(char *), "io_codec_dev", &play_pipe); + if (play_pipe == NULL) { + ESP_LOGE(TAG, "There is no play pipeline"); + return; + } + esp_gmf_io_codec_dev_set_dev(ESP_GMF_PIPELINE_GET_OUT_INSTANCE(play_pipe), esp_gmf_app_get_playback_handle()); + + esp_gmf_obj_handle_t bit_cvt = NULL; + esp_gmf_pipeline_get_el_by_name(play_pipe, "aud_bit_cvt", &bit_cvt); + esp_gmf_bit_cvt_set_dest_bits(bit_cvt, DAC_I2S_BITS); + esp_gmf_obj_handle_t ch_cvt = NULL; + esp_gmf_pipeline_get_el_by_name(play_pipe, "aud_ch_cvt", &ch_cvt); + esp_gmf_ch_cvt_set_dest_channel(ch_cvt, DAC_I2S_CH); + esp_gmf_obj_handle_t dec_el = NULL; + esp_gmf_pipeline_get_el_by_name(play_pipe, "aud_dec", &dec_el); + esp_gmf_audio_dec_reconfig_by_sound_info(dec_el, &info); + + esp_gmf_pipeline_set_in_uri(play_pipe, "/sdcard/test.mp3"); + + esp_gmf_task_handle_t play_task = NULL; + cfg.thread.core = 0; + esp_gmf_task_init(&cfg, &play_task); + + esp_gmf_pipeline_bind_task(play_pipe, play_task); + esp_gmf_pipeline_loading_jobs(play_pipe); + esp_gmf_pipeline_set_event(play_pipe, _pipeline_event, NULL); + esp_gmf_pipeline_run(play_pipe); + + vTaskDelay(20000 / portTICK_PERIOD_MS); + esp_gmf_pipeline_stop(read_pipe); + esp_gmf_pipeline_stop(play_pipe); + +#if ENCODER_ENABLE + FILE *file = fopen("/sdcard/aec.aac", "wb"); +#else + FILE *file = fopen("/sdcard/aec_16k_16bit_1ch.pcm", "wb"); +#endif /* ENCODER_ENABLE */ + if (file) { + fwrite(pcm_buffer, 1, pcm_received, file); + fclose(file); + } else { + ESP_LOGE(TAG, "Failed to open file for writing"); + } + heap_caps_free(pcm_buffer); + + esp_gmf_task_deinit(read_task); + esp_gmf_task_deinit(play_task); + esp_gmf_pipeline_destroy(read_pipe); + esp_gmf_pipeline_destroy(play_pipe); + gmf_loader_teardown_all_defaults(pool); + esp_gmf_pool_deinit(pool); + esp_gmf_app_teardown_codec_dev(); + esp_gmf_app_teardown_sdcard(sdcard_handle); +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/aec_rec/partitions.csv b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/aec_rec/partitions.csv new file mode 100644 index 0000000..fb45a7a --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/aec_rec/partitions.csv @@ -0,0 +1,4 @@ +# Espressif ESP32 Partition Table +# Name, Type, SubType, Offset, Size +nvs, data, nvs, 0x9000, 0x6000, +factory, app, factory, 0x010000, 2500k diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/aec_rec/pytest_aec_rec.py b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/aec_rec/pytest_aec_rec.py new file mode 100644 index 0000000..d9e650d --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/aec_rec/pytest_aec_rec.py @@ -0,0 +1,14 @@ +# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD +# +# SPDX-License-Identifier: Apache-2.0 + +import pytest +import os + +from pytest_embedded import Dut + +@pytest.mark.esp32 +@pytest.mark.esp32s3 +@pytest.mark.temp_skip_ci(targets=['esp32', 'esp32s3', 'esp32p4'], reason='No running in CI') +def test_aec_rec(dut: Dut)-> None: + dut.expect(r'Audio >', timeout=20) diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/aec_rec/sdkconfig.defaults b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/aec_rec/sdkconfig.defaults new file mode 100644 index 0000000..b4eecbc --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/aec_rec/sdkconfig.defaults @@ -0,0 +1,52 @@ +# +# Serial flasher config +# +CONFIG_ESPTOOLPY_FLASH_MODE_AUTO_DETECT=y +CONFIG_ESPTOOLPY_FLASHMODE_DIO=y +CONFIG_ESPTOOLPY_FLASH_SAMPLE_MODE_STR=y +CONFIG_ESPTOOLPY_FLASHMODE="dio" +CONFIG_ESPTOOLPY_FLASHFREQ_80M=y +CONFIG_ESPTOOLPY_FLASHFREQ="80m" +CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y +CONFIG_ESPTOOLPY_FLASHSIZE="8MB" + +# +# Partition Table +# +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" +CONFIG_PARTITION_TABLE_FILENAME="partitions.csv" +CONFIG_PARTITION_TABLE_OFFSET=0x8000 +CONFIG_PARTITION_TABLE_MD5=y +# end of Partition Table + + +# +# Compiler options +# +CONFIG_COMPILER_OPTIMIZATION_PERF=y +# end of Compiler options + +# +# ESP PSRAM +# +CONFIG_SPIRAM=y +CONFIG_SPIRAM_TYPE_AUTO=y +CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY=y +CONFIG_SPIRAM_SPEED_80M=y +CONFIG_SPIRAM_SPEED=80 + +# +# Audio Codec Device Configuration +# +# CONFIG_CODEC_I2C_BACKWARD_COMPATIBLE is not set +# end of Audio Codec Device Configuration + +CONFIG_FATFS_LFN_HEAP=y + +CONFIG_FREERTOS_HZ=1000 +CONFIG_FREERTOS_USE_TRACE_FACILITY=y +CONFIG_FREERTOS_USE_STATS_FORMATTING_FUNCTIONS=y +# CONFIG_FREERTOS_USE_LIST_DATA_INTEGRITY_CHECK_BYTES is not set +CONFIG_FREERTOS_VTASKLIST_INCLUDE_COREID=y +CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS=y \ No newline at end of file diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/aec_rec/sdkconfig.defaults.esp32 b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/aec_rec/sdkconfig.defaults.esp32 new file mode 100644 index 0000000..5593e48 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/aec_rec/sdkconfig.defaults.esp32 @@ -0,0 +1,15 @@ +CONFIG_IDF_CMAKE=y +CONFIG_IDF_TARGET_ARCH_XTENSA=y +CONFIG_IDF_TARGET="esp32" +CONFIG_IDF_TARGET_ESP32=y +CONFIG_IDF_FIRMWARE_CHIP_ID=0x0000 + +# +# ESP System Settings +# +# CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_80 is not set +# CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_160 is not set +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ=240 + +CONFIG_AUDIO_BOARD="LYRAT_MINI_V1" diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/aec_rec/sdkconfig.defaults.esp32s3 b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/aec_rec/sdkconfig.defaults.esp32s3 new file mode 100644 index 0000000..b96507c --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/aec_rec/sdkconfig.defaults.esp32s3 @@ -0,0 +1,15 @@ +CONFIG_IDF_CMAKE=y +CONFIG_IDF_TARGET_ARCH_XTENSA=y +CONFIG_IDF_TARGET="esp32s3" +CONFIG_IDF_TARGET_ESP32S3=y +CONFIG_IDF_FIRMWARE_CHIP_ID=0x0009 + +CONFIG_COMPILER_OPTIMIZATION_PERF=y + +CONFIG_SPIRAM_MODE_OCT=y + +CONFIG_ESP32S3_DEFAULT_CPU_FREQ_240=y +CONFIG_ESP32S3_INSTRUCTION_CACHE_32KB=y +CONFIG_ESP32S3_DATA_CACHE_LINE_64B=y + +CONFIG_AUDIO_BOARD="S3_Korvo_V2" diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/wwe/CMakeLists.txt b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/wwe/CMakeLists.txt new file mode 100644 index 0000000..810df38 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/wwe/CMakeLists.txt @@ -0,0 +1,7 @@ +# The following lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) + +project(gmf_wwe_demo) diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/wwe/README.md b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/wwe/README.md new file mode 100644 index 0000000..79816e1 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/wwe/README.md @@ -0,0 +1,150 @@ +# Speech Recognition + +- [中文版](./README_CN.md) + +## Example Brief + +This example demonstrates how to use the `AFE element` for wake word detection, voice activity detection (VAD), and command word recognition. It allows configuring features through the following macro definitions in [main.c](./main/main.c) to achieve different application combinations: + +```c +#define VOICE2FILE (true) +#define WAKENET_ENABLE (true) +#define VAD_ENABLE (true) +#define VCMD_ENABLE (true) +``` + +- `VOICE2FILE`: Save the audio between VAD start and VAD end as a file +- `WAKENET_ENABLE`: Enable wake word recognition +- `VAD_ENABLE`: Enable voice activity detection +- `VCMD_ENABLE`: Enable command word recognition + +## GMF Pipeline + +```mermaid +graph + subgraph WWE + E[CODEC_DEV_RX] --> F[GMF_AFE] --> G[GMF_PORT] + end +``` + +## Example Set Up + +### Default IDF Branch + +This example supports IDF release/v5.3 and later branches + +### Prerequisites + +The voice wake word and command word detection used in this example are derived from `esp-sr`. Please familiarize yourself with its configuration and usage: [README](https://github.com/espressif/esp-sr/blob/master/README.md) + +### Build and Flash + +Before compiling this example, ensure that the ESP-IDF environment is properly configured. If it is already set up, you can proceed to the next configuration step. If not, run the following script in the root directory of ESP-IDF to set up the build environment. For detailed steps on configuring and using ESP-IDF, please refer to the [ESP-IDF Programming Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/index.html) + +``` +./install.sh +. ./export.sh +``` + +Here are the summarized steps for compilation: + +- Enter the location where the example project is stored: + +``` +cd gmf_ai_audio/examples/wwe +``` + +- Select the target chip for compilation. For example, to use the ESP32S3: + +``` +idf.py set-target esp32s3 +``` +- Select the compilation board, taking ESP32-S3-Korvo V2 as an example: + +``` +idf.py menuconfig +In 'menuconfig', select 'GMF APP Configuration' -> 'Audio Board' -> 'ESP32-S3-Korvo V2', and then save and exit +``` + +- Build the example: + +``` +idf.py build +``` + +- Flash the program and run the monitor tool to view serial output (replace PORT with the port name): + +``` +idf.py -p PORT flash monitor +``` + +- Exit the debugging interface using `Ctrl-]` + +## How to use the Example + +- Before compiling the program, confirm whether wake word detection is enabled by checking the value of `WAKENET_ENABLE` in `main.c`. Also, confirm whether the `afe` output needs to be saved as a file by checking the value of `VOICE2FILE` +- If `VOICE2FILE` is set to `true`, ensure that the SDCard is correctly installed on the development board before running the example +- During the example's execution, if `WAKENET_ENABLE` is set to `true`, first say the wake word, then say the command word to verify the effects of wake word detection, voice activity detection, and command word detection. If `WAKENET_ENABLE` is set to `false`, you can directly use command words. Pay attention to the corresponding event prompts in the `log` +- If `VOICE2FILE` is set to `true`, after the example finishes running, you can export the files from the SDCard and use software to check the recording content. The recording file names are `16k_16bit_1ch_{idx}.pcm`, where `{idx}` increments. The PCM format is 16K sampling rate, 16-bit width, and 1 channel + +### Example Functionality + +- After the example starts running, you can say wake words and command words to the development board. The following will be printed: + +```c +I (1547) AFE: AFE Version: (2MIC_V250113) +I (1550) AFE: Input PCM Config: total 4 channels(2 microphone, 1 playback), sample rate:16000 +I (1560) AFE: AFE Pipeline: [input] -> |AEC(SR_HIGH_PERF)| -> |SE(BSS)| -> |VAD(WebRTC)| -> |WakeNet(wn9_hilexin,)| -> [output] +I (1572) AFE_manager: Feed task, ch 4, chunk 1024, buf size 8192 +I (1579) GMF_AFE: Create AFE, ai_afe-0x3c2dcf90 +I (1584) GMF_AFE: Create AFE, ai_afe-0x3c2dd0b8 +I (1589) GMF_AFE: New an object,ai_afe-0x3c2dd0b8 +I (1595) ESP_GMF_TASK: Waiting to run... [tsk:TSK_0x3fcc500c-0x3fcc500c, wk:0x0, run:0] +I (1603) ESP_GMF_THREAD: The TSK_0x3fcc500c created on internal memory +I (1610) ESP_GMF_TASK: Waiting to run... [tsk:TSK_0x3fcc500c-0x3fcc500c, wk:0x3c2dd17c, run:0] +I (1620) AI_AUDIO_WWE: CB: RECV Pipeline EVT: el:NULL-0x3c2dd080, type:8192, sub:ESP_GMF_EVENT_STATE_OPENING, payload:0x0, size:0,0x0 +I (1632) AFE_manager: AFE Ctrl [1, 0] +I (1637) AFE_manager: VAD ctrl ret 1 +Build fst from commands. +Quantized MultiNet6:rnnt_ctc_1.0, name:mn6_cn, (Feb 18 2025 12:00:53) +Quantized MultiNet6 search method: 2, time out:5.8 s +I (2639) NEW_DATA_BUS: New ringbuffer:0x3c6feeb4, num:2, item_cnt:8192, db:0x3c6e2b3c +I (2643) NEW_DATA_BUS: New ringbuffer:0x3c6fe4d0, num:1, item_cnt:20480, db:0x3c6fcbd8 +I (2651) AFE_manager: AFE manager suspend 1 +I (2656) AFE_manager: AFE manager suspend 0 +I (2661) AI_AUDIO_WWE: CB: RECV Pipeline EVT: el:ai_afe-0x3c2dd0b8, type:12288, sub:ESP_GMF_EVENT_STATE_INITIALIZED, payload:0x3fccd920, size:12,0x0 +I (2675) AI_AUDIO_WWE: CB: RECV Pipeline EVT: el:ai_afe-0x3c2dd0b8, type:8192, sub:ESP_GMF_EVENT_STATE_RUNNING, payload:0x0, size:0,0x0 +I (2688) ESP_GMF_TASK: One times job is complete, del[wk:0x3c2dd17c,ctx:0x3c2dd0b8, label:gmf_afe_open] +I (2698) ESP_GMF_PORT: ACQ IN, new self payload:0x3c2dd17c, port:0x3c2dd240, el:0x3c2dd0b8-ai_afe + +Type 'help' to get the list of commands. +Use UP/DOWN arrows to navigate through command history. +Press TAB when typing command name to auto-complete. +I (2893) ESP_GMF_PORT: ACQ OUT, new self payload:0x3c6fc548, port:0x3c2dd280, el:0x3c2dd0b8-ai_afe +Audio > I (5668) AFE_manager: AFE Ctrl [1, 1] +I (5669) AFE_manager: VAD ctrl ret 1 +I (5674) AI_AUDIO_WWE: WAKEUP_START [1 : 1] +I (6334) AI_AUDIO_WWE: VAD_START +W (7833) AI_AUDIO_WWE: Command 25, phrase_id 25, prob 0.998051, str: da kai kong tiao +I (8110) AI_AUDIO_WWE: VAD_END +I (8128) AI_AUDIO_WWE: File closed +I (10110) AFE_manager: AFE Ctrl [1, 0] +I (10111) AFE_manager: VAD ctrl ret 1 +I (10118) AI_AUDIO_WWE: WAKEUP_END +``` + +### Troubleshooting + +1. **Wake Word Not Detected**: + - Ensure `WAKENET_ENABLE` is set to `true` + - Verify that the model files are correctly loaded + - Check if the channel configuration matches the hardware setup + +2. **Recording File Not Generated**: + - Ensure the SD card is correctly installed + - Verify that `VOICE2FILE` is set to `true` + - Confirm that speech is detected after wakeup + +3. **Task Watchdog Timeout**: + - Ensure `esp_afe_manager_cfg_t.feed_task_setting.core` and `esp_afe_manager_cfg_t.fetch_task_setting.core` are configured on different CPU cores + - Properly allocate tasks on the core where the timeout occurs diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/wwe/README_CN.md b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/wwe/README_CN.md new file mode 100644 index 0000000..950cdae --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/wwe/README_CN.md @@ -0,0 +1,156 @@ +# 语音识别 + +- [English](./README.md) + +## 例程简介 + +本例程介绍使用 `AFE element` 进行唤醒词、人声检测和命令词检测的方法,并通过 [main.c](./main/main.c) 中的以下宏定义来配置功能,从而实现不同的应用组合: + +```c +#define VOICE2FILE (true) +#define WAKENET_ENABLE (true) +#define VAD_ENABLE (true) +#define VCMD_ENABLE (true) +``` + +- `VOICE2FILE`: 以文件的形式保存 VAD 开始到 VAD 结束之间的音频 +- `WAKENET_ENABLE`: 唤醒词识别使能 +- `VAD_ENABLE`: 人声检测使能 +- `VCMD_ENABLE`: 命令词检测使能 + +## GMF Pipeline + +```mermaid +graph + subgraph WWE + E[CODEC_DEV_RX] --> F[GMF_AFE] --> G[GMF_PORT] + end +``` + +## 示例创建 + +### IDF 默认分支 + +本例程支持 IDF release/v5.3 及以后的分支 + +### 预备知识 + +例程中使用的语音唤醒和命令词检测,源自于 `esp-sr`,请先了解其配置和使用: [README](https://github.com/espressif/esp-sr/blob/master/README.md) + +### 编译和下载 + +编译本例程前需要先确保已配置 ESP-IDF 的环境,如果已配置可跳到下一项配置,如果未配置需要先在 ESP-IDF 根目录运行下面脚本设置编译环境,有关配置和使用 ESP-IDF 完整步骤,请参阅 [《ESP-IDF 编程指南》](https://docs.espressif.com/projects/esp-idf/zh_CN/latest/esp32s3/index.html) + +``` +./install.sh +. ./export.sh +``` + +下面是简略编译步骤: + +- 进入示例工程的位置 + +``` +cd gmf_ai_audio/examples/wwe +``` + +- 选择编译芯片,以 esp32s3 为例: + +``` +idf.py set-target esp32s3 +``` +- 选择编译目标板,以 ESP32-S3-Korvo V2 为例: + +``` +idf.py menuconfig +在 `menuconfig` 中选择 `GMF APP Configuration` -> `Audio Board` -> `ESP32-S3-Korvo V2`,然后保存退出 +``` + +- 编译例子程序 + +``` +idf.py build +``` + +- 烧录程序并运行 monitor 工具来查看串口输出 (替换 PORT 为端口名称): + +``` +idf.py -p PORT flash monitor +``` + +- 退出调试界面使用 `Ctrl-]` + +## 如何使用例程 + +1. **配置功能**: 在 `main.c` 中通过上文中说明的宏定义启用或禁用功能来选择对应的应用场景 + +2. **运行要求**: + - 如果启用了 `VOICE2FILE`,请确保 SD 卡已正确安装到开发板上 + - 如果启用了 `WAKENET_ENABLE`,运行时需要先说出唤醒词,然后再说命令词 + - 如果关闭了 `WAKENET_ENABLE`,运行时可以直接说命令词 + +3. **查看结果**: + - 运行过程中,串口会打印检测到的事件日志 + - 如果启用了 `VOICE2FILE`,录音文件将保存到 SD 卡,文件名格式为 `16k_16bit_1ch_{idx}.pcm` + +### 功能和用法 + +运行后,可以对开发板说唤醒词和命令词,串口会打印如下日志: + +```c +I (1547) AFE: AFE Version: (2MIC_V250113) +I (1550) AFE: Input PCM Config: total 4 channels(2 microphone, 1 playback), sample rate:16000 +I (1560) AFE: AFE Pipeline: [input] -> |AEC(SR_HIGH_PERF)| -> |SE(BSS)| -> |VAD(WebRTC)| -> |WakeNet(wn9_hilexin,)| -> [output] +I (1572) AFE_manager: Feed task, ch 4, chunk 1024, buf size 8192 +I (1579) GMF_AFE: Create AFE, ai_afe-0x3c2dcf90 +I (1584) GMF_AFE: Create AFE, ai_afe-0x3c2dd0b8 +I (1589) GMF_AFE: New an object,ai_afe-0x3c2dd0b8 +I (1595) ESP_GMF_TASK: Waiting to run... [tsk:TSK_0x3fcc500c-0x3fcc500c, wk:0x0, run:0] +I (1603) ESP_GMF_THREAD: The TSK_0x3fcc500c created on internal memory +I (1610) ESP_GMF_TASK: Waiting to run... [tsk:TSK_0x3fcc500c-0x3fcc500c, wk:0x3c2dd17c, run:0] +I (1620) AI_AUDIO_WWE: CB: RECV Pipeline EVT: el:NULL-0x3c2dd080, type:8192, sub:ESP_GMF_EVENT_STATE_OPENING, payload:0x0, size:0,0x0 +I (1632) AFE_manager: AFE Ctrl [1, 0] +I (1637) AFE_manager: VAD ctrl ret 1 +Build fst from commands. +Quantized MultiNet6:rnnt_ctc_1.0, name:mn6_cn, (Feb 18 2025 12:00:53) +Quantized MultiNet6 search method: 2, time out:5.8 s +I (2639) NEW_DATA_BUS: New ringbuffer:0x3c6feeb4, num:2, item_cnt:8192, db:0x3c6e2b3c +I (2643) NEW_DATA_BUS: New ringbuffer:0x3c6fe4d0, num:1, item_cnt:20480, db:0x3c6fcbd8 +I (2651) AFE_manager: AFE manager suspend 1 +I (2656) AFE_manager: AFE manager suspend 0 +I (2661) AI_AUDIO_WWE: CB: RECV Pipeline EVT: el:ai_afe-0x3c2dd0b8, type:12288, sub:ESP_GMF_EVENT_STATE_INITIALIZED, payload:0x3fccd920, size:12,0x0 +I (2675) AI_AUDIO_WWE: CB: RECV Pipeline EVT: el:ai_afe-0x3c2dd0b8, type:8192, sub:ESP_GMF_EVENT_STATE_RUNNING, payload:0x0, size:0,0x0 +I (2688) ESP_GMF_TASK: One times job is complete, del[wk:0x3c2dd17c,ctx:0x3c2dd0b8, label:gmf_afe_open] +I (2698) ESP_GMF_PORT: ACQ IN, new self payload:0x3c2dd17c, port:0x3c2dd240, el:0x3c2dd0b8-ai_afe + +Type 'help' to get the list of commands. +Use UP/DOWN arrows to navigate through command history. +Press TAB when typing command name to auto-complete. +I (2893) ESP_GMF_PORT: ACQ OUT, new self payload:0x3c6fc548, port:0x3c2dd280, el:0x3c2dd0b8-ai_afe +Audio > I (5668) AFE_manager: AFE Ctrl [1, 1] +I (5669) AFE_manager: VAD ctrl ret 1 +I (5674) AI_AUDIO_WWE: WAKEUP_START [1 : 1] +I (6334) AI_AUDIO_WWE: VAD_START +W (7833) AI_AUDIO_WWE: Command 25, phrase_id 25, prob 0.998051, str: da kai kong tiao +I (8110) AI_AUDIO_WWE: VAD_END +I (8128) AI_AUDIO_WWE: File closed +I (10110) AFE_manager: AFE Ctrl [1, 0] +I (10111) AFE_manager: VAD ctrl ret 1 +I (10118) AI_AUDIO_WWE: WAKEUP_END +``` + +### 常见问题 + +1. **无法检测唤醒词**: + - 确保 `WAKENET_ENABLE` 设置为 `true` + - 检查模型文件是否正确加载 + - 配置的通道顺序是否符合硬件实际 + +2. **录音文件未生成**: + - 确保 SD 卡已正确安装。 + - 检查 `VOICE2FILE` 是否设置为 `true` + - 唤醒后是否有说话 + +3. **Task Watch Dog**: + - 确保 `esp_afe_manager_cfg_t.feed_task_setting.core` 和 `esp_afe_manager_cfg_t.fetch_task_setting.core` 配置在不同的 cpu 核心上 + - 将出现 `task wdt` 的核上任务合理分配 diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/wwe/main/CMakeLists.txt b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/wwe/main/CMakeLists.txt new file mode 100644 index 0000000..4c70261 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/wwe/main/CMakeLists.txt @@ -0,0 +1,3 @@ + +idf_component_register(SRC_DIRS "./" + INCLUDE_DIRS "./") diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/wwe/main/idf_component.yml b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/wwe/main/idf_component.yml new file mode 100644 index 0000000..1e402e6 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/wwe/main/idf_component.yml @@ -0,0 +1,19 @@ +dependencies: + espressif/gmf_loader: + override_path: ../../../../../packages/gmf_loader + version: "^0.7" + espressif/gmf_app_utils: + override_path: ../../../../../packages/gmf_app_utils + version: "^0.7" + espressif/gmf_core: + override_path: ../../../../../gmf_core + espressif/gmf_io: + override_path: ../../../../gmf_io + espressif/gmf_audio: + override_path: ../../../../gmf_audio + espressif/gmf_ai_audio: + override_path: ../../../../gmf_ai_audio + espressif/gmf_video: + override_path: ../../../../gmf_video + espressif/gmf_misc: + override_path: ../../../../gmf_misc diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/wwe/main/main.c b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/wwe/main/main.c new file mode 100644 index 0000000..634d86e --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/wwe/main/main.c @@ -0,0 +1,399 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#include "esp_err.h" +#include "esp_log.h" +#include "soc/soc_caps.h" +#include "esp_console.h" +#include "argtable3/argtable3.h" + +#include "esp_gmf_io.h" +#include "esp_gmf_pipeline.h" +#include "esp_gmf_pool.h" +#include "esp_gmf_app_setup_peripheral.h" + +#include "esp_gmf_io_codec_dev.h" +#include "esp_gmf_app_cli.h" +#include "gmf_loader_setup_defaults.h" + +#if SOC_SDMMC_HOST_SUPPORTED == 1 +#define VOICE2FILE (true) +#endif /* SOC_SDMMC_HOST_SUPPORTED == 1 */ +#ifdef CONFIG_GMF_AI_AUDIO_WAKEUP_ENABLE +#define WAKENET_ENABLE (true) +#else +#define WAKENET_ENABLE (false) +#endif /* CONFIG_GMF_AI_AUDIO_WAKEUP_ENABLE */ +#ifdef CONFIG_GMF_AI_AUDIO_VOICE_COMMAND_ENABLE +#define VCMD_ENABLE (true) +#else +#define VCMD_ENABLE (false) +#endif /* CONFIG_GMF_AI_AUDIO_VOICE_COMMAND_ENABLE */ +#define QUIT_CMD_FOUND (BIT0) + +#define BOARD_LYRAT_MINI (0) +#define BOARD_KORVO_2 (1) +#define BOARD_XD_AIOT_C3 (2) +#define BOARD_ESP_SPOT (3) +#define BOARD_P4_FUN_EV (4) + +#if defined CONFIG_IDF_TARGET_ESP32S3 +#define WITH_AFE (true) +#define AUDIO_BOARD (BOARD_KORVO_2) +#elif defined CONFIG_IDF_TARGET_ESP32 +#define WITH_AFE (true) +#define AUDIO_BOARD (BOARD_LYRAT_MINI) +#elif defined CONFIG_IDF_TARGET_ESP32C3 +#define WITH_AFE (false) +#define AUDIO_BOARD (BOARD_XD_AIOT_C3) +#elif defined CONFIG_IDF_TARGET_ESP32C5 +#define WITH_AFE (false) +#define AUDIO_BOARD (BOARD_ESP_SPOT) +#elif defined CONFIG_IDF_TARGET_ESP32P4 +#define WITH_AFE (true) +#define AUDIO_BOARD (BOARD_P4_FUN_EV) +#endif /* defined CONFIG_IDF_TARGET_ESP32S3 */ + +#if AUDIO_BOARD == BOARD_KORVO_2 +#define ADC_I2S_CH (2) +#define ADC_I2S_BITS (32) +#define INPUT_CH_NUM (4) +#define INPUT_CH_BITS (16) /* For board `ESP32-S3-Korvo-2`, the es7210 is configured as 32-bit, + 2-channel mode to accommodate 16-bit, 4-channel data */ +#elif AUDIO_BOARD == BOARD_LYRAT_MINI +#define ADC_I2S_CH (2) +#define ADC_I2S_BITS (16) +#define INPUT_CH_NUM (ADC_I2S_CH) +#define INPUT_CH_BITS (ADC_I2S_BITS) +#elif AUDIO_BOARD == BOARD_XD_AIOT_C3 +#define ADC_I2S_CH (2) +#define ADC_I2S_BITS (16) +#define INPUT_CH_NUM (ADC_I2S_CH) +#define INPUT_CH_BITS (ADC_I2S_BITS) +#elif AUDIO_BOARD == BOARD_ESP_SPOT +#define ADC_I2S_CH (2) +#define ADC_I2S_BITS (16) +#define INPUT_CH_NUM (ADC_I2S_CH) +#define INPUT_CH_BITS (ADC_I2S_BITS) +#elif AUDIO_BOARD == BOARD_P4_FUN_EV +#define ADC_I2S_CH (2) +#define ADC_I2S_BITS (16) +#define INPUT_CH_NUM (ADC_I2S_CH) +#define INPUT_CH_BITS (ADC_I2S_BITS) +#endif /* AUDIO_BOARD == BOARD_KORVO_2 */ + +#if WITH_AFE == true +#include "esp_gmf_afe.h" +#else +#include "esp_gmf_wn.h" +#endif /* WITH_AFE == true */ + +static const char *TAG = "AI_AUDIO_WWE"; + +#if WITH_AFE == true +static bool speeching = false; +static bool wakeup = false; +#endif /* WITH_AFE == true */ +static EventGroupHandle_t g_event_group = NULL; +#if WITH_AFE == true +static esp_gmf_element_handle_t g_afe = NULL; +#endif /* WITH_AFE == true */ + +static esp_err_t _pipeline_event(esp_gmf_event_pkt_t *event, void *ctx) +{ + ESP_LOGI(TAG, "CB: RECV Pipeline EVT: el:%s-%p, type:%d, sub:%s, payload:%p, size:%d,%p", + OBJ_GET_TAG(event->from), event->from, event->type, esp_gmf_event_get_state_str(event->sub), + event->payload, event->payload_size, ctx); + return 0; +} + +#if WITH_AFE == true +void esp_gmf_afe_event_cb(esp_gmf_obj_handle_t obj, esp_gmf_afe_evt_t *event, void *user_data) +{ + switch (event->type) { + case ESP_GMF_AFE_EVT_WAKEUP_START: { + wakeup = true; +#if WAKENET_ENABLE == true && VCMD_ENABLE == true + esp_gmf_afe_vcmd_detection_cancel(obj); + esp_gmf_afe_vcmd_detection_begin(obj); +#endif /* WAKENET_ENABLE == true && VCMD_ENABLE == true */ + if (event->event_data) { + esp_gmf_afe_wakeup_info_t *info = event->event_data; + ESP_LOGI(TAG, "WAKEUP_START [%d : %d]", info->wake_word_index, info->wakenet_model_index); + } else { + ESP_LOGI(TAG, "WAKEUP_START"); + } + break; + } + case ESP_GMF_AFE_EVT_WAKEUP_END: { + wakeup = false; +#if WAKENET_ENABLE == true && VCMD_ENABLE == true + esp_gmf_afe_vcmd_detection_cancel(obj); +#endif /* WAKENET_ENABLE == true && VCMD_ENABLE == true */ + ESP_LOGI(TAG, "WAKEUP_END"); + break; + } + case ESP_GMF_AFE_EVT_VAD_START: { +#if WAKENET_ENABLE != true && VCMD_ENABLE == true + esp_gmf_afe_vcmd_detection_cancel(obj); + esp_gmf_afe_vcmd_detection_begin(obj); +#endif /* WAKENET_ENABLE != true && VCMD_ENABLE == true */ + speeching = true; + ESP_LOGI(TAG, "VAD_START"); + break; + } + case ESP_GMF_AFE_EVT_VAD_END: { +#if WAKENET_ENABLE != true && VCMD_ENABLE == true + esp_gmf_afe_vcmd_detection_cancel(obj); +#endif /* WAKENET_ENABLE != true && VCMD_ENABLE == true */ + speeching = false; + ESP_LOGI(TAG, "VAD_END"); + break; + } + case ESP_GMF_AFE_EVT_VCMD_DECT_TIMEOUT: { + ESP_LOGI(TAG, "VCMD_DECT_TIMEOUT"); + break; + } + default: { + esp_gmf_afe_vcmd_info_t *info = event->event_data; + ESP_LOGW(TAG, "Command %d, phrase_id %d, prob %f, str: %s", + event->type, info->phrase_id, info->prob, info->str); + break; + } + } +} +#else +static void esp_gmf_wn_event_cb(esp_gmf_obj_handle_t obj, int32_t trigger_ch, void *user_ctx) +{ + static int32_t cnt = 1; + ESP_LOGI(TAG, "WWE detected on channel %" PRIi32 ", cnt: %" PRIi32, trigger_ch, cnt++); + if (cnt >= 10) { + xEventGroupSetBits(g_event_group, QUIT_CMD_FOUND); + } +} +#endif /* WITH_AFE == true */ + +static void voice_2_file(uint8_t *buffer, int len) +{ +#if VOICE2FILE == true && WITH_AFE == true +#define MAX_FNAME_LEN (50) + + static FILE *fp = NULL; + static int fcnt = 0; + + if (speeching) { + if (!fp) { + char fname[MAX_FNAME_LEN] = {0}; + snprintf(fname, MAX_FNAME_LEN - 1, "/sdcard/16k_16bit_1ch_%d.pcm", fcnt++); + fp = fopen(fname, "wb"); + if (!fp) { + ESP_LOGE(TAG, "File open failed"); + return; + } + } + if (len) { + fwrite(buffer, len, 1, fp); + } + } else { + if (fp) { + ESP_LOGI(TAG, "File closed"); + fclose(fp); + fp = NULL; + } + } +#endif /* VOICE2FILE == true && WITH_AFE == true */ +} + +static esp_gmf_err_io_t outport_acquire_write(void *handle, esp_gmf_payload_t *load, int wanted_size, int block_ticks) +{ + ESP_LOGD(TAG, "Acquire write"); + return ESP_GMF_IO_OK; +} + +static esp_gmf_err_io_t outport_release_write(void *handle, esp_gmf_payload_t *load, int block_ticks) +{ + ESP_LOGD(TAG, "Release write"); + voice_2_file(load->buf, load->valid_size); + return ESP_GMF_IO_OK; +} + +#if WITH_AFE == true +static struct { + struct arg_int *keep; + struct arg_end *end; +} keep_args; + +static int keep_awake(int argc, char **argv) +{ + int nerrors = arg_parse(argc, argv, (void **)&keep_args); + if (nerrors != 0) { + arg_print_errors(stderr, keep_args.end, argv[0]); + return ESP_FAIL; + } + if (g_afe) { + esp_gmf_afe_keep_awake(g_afe, keep_args.keep->ival[0]); + printf("Keep awake: %d\n", keep_args.keep->ival[0]); + } else { + ESP_LOGE(TAG, "AFE not found"); + } + return ESP_OK; +} + +static struct { + struct arg_str *cmd; + struct arg_end *end; +} trigger_args; + +static int trigger(int argc, char **argv) +{ + int nerrors = arg_parse(argc, argv, (void **)&trigger_args); + if (nerrors != 0) { + arg_print_errors(stderr, trigger_args.end, argv[0]); + return ESP_FAIL; + } + if (g_afe) { + if (strcmp(trigger_args.cmd->sval[0], "wakeup") == 0) { + esp_gmf_afe_trigger_wakeup(g_afe); + } else if (strcmp(trigger_args.cmd->sval[0], "sleep") == 0) { + esp_gmf_afe_trigger_sleep(g_afe); + } + } + return 0; +} +#endif /* WITH_AFE == true */ + +static int quit(int argc, char **argv) +{ + xEventGroupSetBits(g_event_group, QUIT_CMD_FOUND); + return 0; +} + +static void wwe_cmds_register(void) +{ +#if WITH_AFE == true + esp_console_cmd_t cmd_keep = { + .command = "keep", + .help = "keep awake", + .hint = NULL, + .func = &keep_awake, + .argtable = &keep_args, + }; + keep_args.keep = arg_int0(NULL, NULL, "<0 - 1>", "Keep awake"); + keep_args.end = arg_end(1); + esp_console_cmd_register(&cmd_keep); + + esp_console_cmd_t cmd_trigger_wakeup = { + .command = "trigger", + .help = "trigger wakeup or sleep", + .hint = NULL, + .func = &trigger, + .argtable = &trigger_args, + }; + trigger_args.cmd = arg_str0(NULL, NULL, "", "Trigger wakeup or sleep"); + trigger_args.end = arg_end(1); + esp_console_cmd_register(&cmd_trigger_wakeup); +#endif /* WITH_AFE == true */ + + esp_console_cmd_t cmd_quit = { + .command = "quit", + .help = "quit", + .hint = NULL, + .func = &quit, + .argtable = NULL, + }; + esp_console_cmd_register(&cmd_quit); +} + +void app_main(void) +{ + esp_log_level_set("*", ESP_LOG_INFO); + esp_gmf_app_codec_info_t codec_info = ESP_GMF_APP_CODEC_INFO_DEFAULT(); + codec_info.record_info.sample_rate = 16000; + codec_info.record_info.channel = ADC_I2S_CH; + codec_info.record_info.bits_per_sample = ADC_I2S_BITS; + codec_info.play_info.sample_rate = codec_info.record_info.sample_rate; + esp_gmf_app_setup_codec_dev(&codec_info); + void *sdcard_handle = NULL; + esp_gmf_app_setup_sdcard(&sdcard_handle); + g_event_group = xEventGroupCreate(); + + esp_gmf_pool_handle_t pool = NULL; + esp_gmf_pool_init(&pool); + gmf_loader_setup_all_defaults(pool); + + esp_gmf_pipeline_handle_t pipe = NULL; +#if WITH_AFE == true + const char *name[] = {"ai_afe"}; +#else + const char *name[] = {"ai_wn"}; +#endif /* WITH_AFE == true */ + esp_gmf_pool_new_pipeline(pool, "io_codec_dev", name, sizeof(name) / sizeof(char *), NULL, &pipe); + if (pipe == NULL) { + ESP_LOGE(TAG, "There is no pipeline"); + goto __quit; + } + esp_gmf_io_codec_dev_set_dev(ESP_GMF_PIPELINE_GET_IN_INSTANCE(pipe), esp_gmf_app_get_record_handle()); +#if WITH_AFE == true + esp_gmf_pipeline_get_el_by_name(pipe, "ai_afe", &g_afe); + esp_gmf_afe_set_event_cb(g_afe, esp_gmf_afe_event_cb, NULL); +#else + esp_gmf_element_handle_t wn = NULL; + esp_gmf_pipeline_get_el_by_name(pipe, "ai_wn", &wn); + esp_gmf_wn_set_detect_cb(wn, esp_gmf_wn_event_cb, NULL); +#endif /* WITH_AFE == true */ + esp_gmf_port_handle_t outport = NEW_ESP_GMF_PORT_OUT_BYTE(outport_acquire_write, + outport_release_write, + NULL, + NULL, + 2048, + 100); + esp_gmf_pipeline_reg_el_port(pipe, name[0], ESP_GMF_IO_DIR_WRITER, outport); + + esp_gmf_info_sound_t info = { + .sample_rates = 16000, + .channels = INPUT_CH_NUM, + .bits = INPUT_CH_BITS, + }; + esp_gmf_pipeline_report_info(pipe, ESP_GMF_INFO_SOUND, &info, sizeof(info)); + + esp_gmf_task_cfg_t cfg = DEFAULT_ESP_GMF_TASK_CONFIG(); + cfg.ctx = NULL; + cfg.cb = NULL; + cfg.thread.core = 0; + cfg.thread.prio = 5; + cfg.thread.stack = 5120; + esp_gmf_task_handle_t task = NULL; + esp_gmf_task_init(&cfg, &task); + esp_gmf_pipeline_bind_task(pipe, task); + esp_gmf_pipeline_loading_jobs(pipe); + esp_gmf_pipeline_set_event(pipe, _pipeline_event, NULL); + esp_gmf_pipeline_run(pipe); + + esp_gmf_app_cli_init("Audio >", wwe_cmds_register); + + while (1) { + EventBits_t bits = xEventGroupWaitBits(g_event_group, QUIT_CMD_FOUND, pdTRUE, pdFALSE, portMAX_DELAY); + if (bits & QUIT_CMD_FOUND) { + ESP_LOGI(TAG, "Quit command found, stopping pipeline"); + break; + } + } + +__quit: + esp_gmf_pipeline_stop(pipe); + esp_gmf_task_deinit(task); + esp_gmf_pipeline_destroy(pipe); + gmf_loader_teardown_all_defaults(pool); + esp_gmf_pool_deinit(pool); + esp_gmf_app_teardown_codec_dev(); + esp_gmf_app_teardown_sdcard(sdcard_handle); + vEventGroupDelete(g_event_group); + ESP_LOGW(TAG, "Wake word engine demo finished"); +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/wwe/partitions.csv b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/wwe/partitions.csv new file mode 100644 index 0000000..7604e71 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/wwe/partitions.csv @@ -0,0 +1,5 @@ +# Espressif ESP32 Partition Table +# Name, Type, SubType, Offset, Size +nvs, data, nvs, 0x9000, 0x6000, +factory, app, factory, 0x010000, 2500k +model, data, spiffs, , 5168K, diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/wwe/partitions_esp32.csv b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/wwe/partitions_esp32.csv new file mode 100644 index 0000000..642e92a --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/wwe/partitions_esp32.csv @@ -0,0 +1,5 @@ +# Espressif ESP32 Partition Table +# Name, Type, SubType, Offset, Size +nvs, data, nvs, 0x9000, 0x6000, +factory, app, factory, 0x010000, 2500k +model, data, spiffs, , 2M, diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/wwe/pytest_wwe.py b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/wwe/pytest_wwe.py new file mode 100644 index 0000000..440bcc8 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/wwe/pytest_wwe.py @@ -0,0 +1,14 @@ +# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD +# +# SPDX-License-Identifier: Apache-2.0 + +import pytest +import os + +from pytest_embedded import Dut + +@pytest.mark.esp32 +@pytest.mark.esp32s3 +@pytest.mark.temp_skip_ci(targets=['esp32', 'esp32s3', 'esp32p4'], reason='No running in CI') +def test_wwe(dut: Dut)-> None: + dut.expect(r'Audio >', timeout=20) diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/wwe/sdkconfig.defaults b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/wwe/sdkconfig.defaults new file mode 100644 index 0000000..32d2fad --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/wwe/sdkconfig.defaults @@ -0,0 +1,41 @@ +# +# Serial flasher config +# +CONFIG_ESPTOOLPY_FLASH_MODE_AUTO_DETECT=y +CONFIG_ESPTOOLPY_FLASHMODE_DIO=y +CONFIG_ESPTOOLPY_FLASH_SAMPLE_MODE_STR=y +CONFIG_ESPTOOLPY_FLASHMODE="dio" +CONFIG_ESPTOOLPY_FLASHFREQ_80M=y +CONFIG_ESPTOOLPY_FLASHFREQ="80m" +CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y +CONFIG_ESPTOOLPY_FLASHSIZE="8MB" + +# +# Compiler options +# +CONFIG_COMPILER_OPTIMIZATION_PERF=y +# end of Compiler options + +# +# ESP PSRAM +# +CONFIG_SPIRAM=y +CONFIG_SPIRAM_TYPE_AUTO=y +CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY=y +CONFIG_SPIRAM_SPEED_80M=y +CONFIG_SPIRAM_SPEED=80 + +# +# Audio Codec Device Configuration +# +# CONFIG_CODEC_I2C_BACKWARD_COMPATIBLE is not set +# end of Audio Codec Device Configuration + +CONFIG_FATFS_LFN_HEAP=y + +CONFIG_FREERTOS_HZ=1000 +CONFIG_FREERTOS_USE_TRACE_FACILITY=y +CONFIG_FREERTOS_USE_STATS_FORMATTING_FUNCTIONS=y +# CONFIG_FREERTOS_USE_LIST_DATA_INTEGRITY_CHECK_BYTES is not set +CONFIG_FREERTOS_VTASKLIST_INCLUDE_COREID=y +CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS=y \ No newline at end of file diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/wwe/sdkconfig.defaults.esp32 b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/wwe/sdkconfig.defaults.esp32 new file mode 100644 index 0000000..6305002 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/wwe/sdkconfig.defaults.esp32 @@ -0,0 +1,45 @@ +CONFIG_IDF_CMAKE=y +CONFIG_IDF_TARGET_ARCH_XTENSA=y +CONFIG_IDF_TARGET="esp32" +CONFIG_IDF_TARGET_ESP32=y +CONFIG_IDF_FIRMWARE_CHIP_ID=0x0000 + +# +# Partition Table +# +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_esp32.csv" +CONFIG_PARTITION_TABLE_FILENAME="partitions_esp32.csv" +CONFIG_PARTITION_TABLE_OFFSET=0x8000 +CONFIG_PARTITION_TABLE_MD5=y +# end of Partition Table + +# +# ESP Speech Recognition +# +CONFIG_AFE_INTERFACE_V1=y +CONFIG_SR_NSN_WEBRTC=y +CONFIG_SR_VADN_WEBRTC=y + +# +# Load Multiple Wake Words +# +CONFIG_SR_WN_WN9_HILEXIN=y + +CONFIG_SR_MN_CN_NONE=y +# CONFIG_SR_MN_CN_MULTINET2_SINGLE_RECOGNITION is not set +CONFIG_SR_MN_EN_NONE=y +# end of ESP Speech Recognition + +# +# GMF AI Audio +# +CONFIG_GMF_AI_AUDIO_INIT_AEC=y +CONFIG_GMF_AI_AUDIO_INIT_AFE=y + +CONFIG_GMF_AI_AUDIO_AFE_VAD_ENABLE=y +CONFIG_GMF_AI_AUDIO_VAD_MODE=3 +CONFIG_GMF_AI_AUDIO_WAKEUP_ENABLE=y +# end of GMF AI Audio + +CONFIG_AUDIO_BOARD="LYRAT_MINI_V1" diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/wwe/sdkconfig.defaults.esp32c3 b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/wwe/sdkconfig.defaults.esp32c3 new file mode 100644 index 0000000..2e51e56 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/wwe/sdkconfig.defaults.esp32c3 @@ -0,0 +1,32 @@ +# +# Partition Table +# +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" +CONFIG_PARTITION_TABLE_FILENAME="partitions.csv" +CONFIG_PARTITION_TABLE_OFFSET=0x8000 +CONFIG_PARTITION_TABLE_MD5=y +# end of Partition Table + +# +# ESP Speech Recognition +# +CONFIG_AFE_INTERFACE_V1=y +CONFIG_SR_NSN_WEBRTC=y +CONFIG_SR_VADN_WEBRTC=y + +# +# Load Multiple Wake Words +# +CONFIG_SR_WN_WN9S_HILEXIN=y +# CONFIG_SR_WN_WN9S_HIESP is not set +# CONFIG_SR_WN_WN9S_NIHAOXIAOZHI is not set +# CONFIG_SR_WN_WN9S_HIJASON is not set +# end of Load Multiple Wake Words + +CONFIG_SR_MN_CN_NONE=y +CONFIG_SR_MN_EN_NONE=y +# end of ESP Speech Recognition + +CONFIG_GMF_AI_AUDIO_INIT_WN=y +CONFIG_GMF_AI_AUDIO_WN_CH_ALLOCATION="MR" diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/wwe/sdkconfig.defaults.esp32c5 b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/wwe/sdkconfig.defaults.esp32c5 new file mode 100644 index 0000000..339a4ca --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/wwe/sdkconfig.defaults.esp32c5 @@ -0,0 +1,34 @@ +# +# Partition Table +# +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" +CONFIG_PARTITION_TABLE_FILENAME="partitions.csv" +CONFIG_PARTITION_TABLE_OFFSET=0x8000 +CONFIG_PARTITION_TABLE_MD5=y +# end of Partition Table + +# +# ESP Speech Recognition +# +CONFIG_AFE_INTERFACE_V1=y +CONFIG_SR_NSN_WEBRTC=y +CONFIG_SR_VADN_WEBRTC=y + +# +# Load Multiple Wake Words +# +CONFIG_SR_WN_WN9S_HILEXIN=y +# CONFIG_SR_WN_WN9S_HIESP is not set +# CONFIG_SR_WN_WN9S_NIHAOXIAOZHI is not set +# CONFIG_SR_WN_WN9S_HIJASON is not set +# end of Load Multiple Wake Words + +CONFIG_SR_MN_CN_NONE=y +CONFIG_SR_MN_EN_NONE=y +# end of ESP Speech Recognition + +CONFIG_GMF_AI_AUDIO_INIT_WN=y +CONFIG_GMF_AI_AUDIO_WN_CH_ALLOCATION="MR" + +CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG=y diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/wwe/sdkconfig.defaults.esp32p4 b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/wwe/sdkconfig.defaults.esp32p4 new file mode 100644 index 0000000..a27606e --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/wwe/sdkconfig.defaults.esp32p4 @@ -0,0 +1,59 @@ + +CONFIG_IDF_TARGET="esp32p4" +CONFIG_IDF_TARGET_ESP32P4=y + +CONFIG_IDF_EXPERIMENTAL_FEATURES=y + +# +# Partition Table +# +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" +CONFIG_PARTITION_TABLE_FILENAME="partitions.csv" +# end of Partition Table + +# +# ESP Speech Recognition +# +CONFIG_MODEL_IN_FLASH=y +CONFIG_AFE_INTERFACE_V1=y +CONFIG_SR_NSN_WEBRTC=y +CONFIG_SR_VADN_WEBRTC=y +CONFIG_SR_WN_WN9S_HILEXIN=y +CONFIG_SR_MN_CN_MULTINET7_QUANT=y +CONFIG_SR_MN_EN_NONE=y +# end of ESP Speech Recognition + +# +# ESP PSRAM +# +CONFIG_SPIRAM=y + +# +# PSRAM config +# +CONFIG_SPIRAM_MODE_HEX=y +CONFIG_SPIRAM_SPEED_200M=y +CONFIG_SPIRAM_SPEED=200 +CONFIG_SPIRAM_BOOT_INIT=y +CONFIG_SPIRAM_USE_MALLOC=y +CONFIG_SPIRAM_MEMTEST=y +CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=16384 +CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL=32768 + +# +# GMF AI Audio +# +CONFIG_GMF_AI_AUDIO_INIT_AEC=y +CONFIG_GMF_AI_AUDIO_INIT_AFE=y + +CONFIG_GMF_AI_AUDIO_AFE_VAD_ENABLE=y +CONFIG_GMF_AI_AUDIO_AFE_AEC_ENABLE=y +CONFIG_GMF_AI_AUDIO_AEC_CH_ALLOCATION="MR" +CONFIG_GMF_AI_AUDIO_AFE_CH_ALLOCATION="MR" + +CONFIG_GMF_AI_AUDIO_WAKEUP_ENABLE=y +CONFIG_GMF_AI_AUDIO_VOICE_COMMAND_ENABLE=y +# end of GMF AI Audio + +CONFIG_AUDIO_BOARD="ESP32_P4_DEV_V14" diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/wwe/sdkconfig.defaults.esp32s3 b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/wwe/sdkconfig.defaults.esp32s3 new file mode 100644 index 0000000..0dd7826 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/examples/wwe/sdkconfig.defaults.esp32s3 @@ -0,0 +1,84 @@ +CONFIG_IDF_CMAKE=y +CONFIG_IDF_TARGET_ARCH_XTENSA=y +CONFIG_IDF_TARGET="esp32s3" +CONFIG_IDF_TARGET_ESP32S3=y +CONFIG_IDF_FIRMWARE_CHIP_ID=0x0009 + +# +# Partition Table +# +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" +CONFIG_PARTITION_TABLE_FILENAME="partitions.csv" +CONFIG_PARTITION_TABLE_OFFSET=0x8000 +CONFIG_PARTITION_TABLE_MD5=y +# end of Partition Table + +# +# ESP Speech Recognition +# +CONFIG_MODEL_IN_FLASH=y +CONFIG_AFE_INTERFACE_V1=y +CONFIG_SR_WN_WN9_HILEXIN=y +CONFIG_SR_MN_CN_MULTINET6_QUANT=y +CONFIG_SR_MN_EN_NONE=y +# end of ESP Speech Recognition + +# +# ESP PSRAM +# +CONFIG_SPIRAM=y + +# +# SPI RAM config +# +CONFIG_SPIRAM_MODE_OCT=y +CONFIG_SPIRAM_TYPE_AUTO=y +CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY=y +CONFIG_SPIRAM_CLK_IO=30 +CONFIG_SPIRAM_CS_IO=26 +CONFIG_SPIRAM_SPEED_80M=y +CONFIG_SPIRAM_SPEED=80 +CONFIG_SPIRAM_BOOT_INIT=y +CONFIG_SPIRAM_USE_MALLOC=y +CONFIG_SPIRAM_MEMTEST=y +CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=16384 +CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL=32768 +# end of SPI RAM config +# end of ESP PSRAM + +# +# ESP System Settings +# +CONFIG_ESP32S3_DEFAULT_CPU_FREQ_240=y + +# +# Cache config +# +CONFIG_ESP32S3_INSTRUCTION_CACHE_32KB=y +CONFIG_ESP32S3_INSTRUCTION_CACHE_SIZE=0x8000 +CONFIG_ESP32S3_INSTRUCTION_CACHE_8WAYS=y +CONFIG_ESP32S3_ICACHE_ASSOCIATED_WAYS=8 +CONFIG_ESP32S3_INSTRUCTION_CACHE_LINE_32B=y +CONFIG_ESP32S3_INSTRUCTION_CACHE_LINE_SIZE=32 +CONFIG_ESP32S3_DATA_CACHE_64KB=y +CONFIG_ESP32S3_DATA_CACHE_SIZE=0x10000 +CONFIG_ESP32S3_DATA_CACHE_8WAYS=y +CONFIG_ESP32S3_DCACHE_ASSOCIATED_WAYS=8 +CONFIG_ESP32S3_DATA_CACHE_LINE_64B=y +CONFIG_ESP32S3_DATA_CACHE_LINE_SIZE=64 +# end of Cache config + +# +# GMF AI Audio +# +CONFIG_GMF_AI_AUDIO_INIT_AEC=y +CONFIG_GMF_AI_AUDIO_INIT_AFE=y + +CONFIG_GMF_AI_AUDIO_AFE_VAD_ENABLE=y +CONFIG_GMF_AI_AUDIO_AFE_AEC_ENABLE=y +CONFIG_GMF_AI_AUDIO_WAKEUP_ENABLE=y +CONFIG_GMF_AI_AUDIO_VOICE_COMMAND_ENABLE=y +# end of GMF AI Audio + +CONFIG_AUDIO_BOARD="S3_Korvo_V2" diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/idf_component.yml b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/idf_component.yml new file mode 100644 index 0000000..13b9da6 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/idf_component.yml @@ -0,0 +1,15 @@ +version: "0.7.4" +description: Espressif GMF AI Audio Elements Module +url: https://github.com/espressif/esp-gmf/tree/main/elements/gmf_ai_audio +documentation: "https://github.com/espressif/esp-gmf/blob/main/elements/gmf_ai_audio/README.md" +issues: "https://github.com/espressif/esp-gmf/issues" + +tags: + - "gmf" + +dependencies: + espressif/esp-sr: + version: "2.1.5" + espressif/gmf_core: + version: "^0.7" + require: public diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/include/esp_gmf_aec.h b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/include/esp_gmf_aec.h new file mode 100644 index 0000000..05e7024 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/include/esp_gmf_aec.h @@ -0,0 +1,53 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#include "esp_gmf_err.h" +#include "esp_gmf_element.h" +#include "esp_afe_config.h" + +/* This element can run with esp32, esp32s3, esp32c5 and esp32p4 */ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * @brief Configuration structure for AEC + * + * @note The input format, same as afe config: + * `M` to represent the microphone channel, + * `R` to represent the playback reference channel, + * `N` to represent an unknown or unused channel + * For example, input_format="MMNR" indicates that the input data consists of four channels, + * which are the microphone channel, the microphone channel, an unused channel, and the playback channel + */ +typedef struct { + uint8_t filter_len; /*!< The length of filter. The larger the filter, the higher the CPU loading + * Recommended filter_length = 4 for esp32s3 and esp32p4. Recommended filter_length = 2 for esp32c5 */ + afe_type_t type; /*!< AFE type */ + afe_mode_t mode; /*!< AFE mode */ + char *input_format; /*!< Input format */ +} esp_gmf_aec_cfg_t; + +/** + * @brief Initialize the Espressif AEC element + * + * @param[in] cfg Pointer to the configuration structure + * @param[out] out_handle Pointer to the handle to be created + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_MEMORY_LACK Memory allocation failed + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_aec_init(esp_gmf_aec_cfg_t *cfg, esp_gmf_obj_handle_t *out_handle); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/include/esp_gmf_afe.h b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/include/esp_gmf_afe.h new file mode 100644 index 0000000..e4359ef --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/include/esp_gmf_afe.h @@ -0,0 +1,223 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#include "esp_gmf_afe_manager.h" +#include "esp_gmf_err.h" +#include "esp_gmf_element.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* This element can run with esp32, esp32s3 and esp32p4 */ + +#define ESP_GMF_AFE_VCMD_MAX_LEN (256) + +#define ESP_GMF_AFE_DEFAULT_DELAY_SAMPLES (2048) +#define ESP_GMF_AFE_DEFAULT_WAKEUP_TIME_MS (10000) +#define ESP_GMF_AFE_DEFAULT_WAKEUP_END_MS (2000) +#define ESP_GMF_AFE_DEFAULT_VCMD_TIMEOUT_MS (5760) + +/** + * @brief Information when wakeup state detected, event data for "ESP_GMF_AFE_EVT_WAKEUP_START" + */ +typedef struct { + float data_volume; /*!< Volume of input audio, the unit is decibel(dB) */ + int wake_word_index; /*!< Wake word index which start from 1 */ + int wakenet_model_index; /*!< Wakenets index which start from 1 */ +} esp_gmf_afe_wakeup_info_t; + +/** + * @brief Information when voice command detected, event data for `ESP_GMF_AFE_EVT_VCMD_DECTECTED` + */ +typedef struct { + int phrase_id; /*!< Phrase ID */ + float prob; /*!< probability */ + char str[ESP_GMF_AFE_VCMD_MAX_LEN]; /*!< Command string */ +} esp_gmf_afe_vcmd_info_t; + +/** + * @brief AFE manager event type + */ +typedef enum { + ESP_GMF_AFE_EVT_WAKEUP_START = -100, /*!< Wakeup start */ + ESP_GMF_AFE_EVT_WAKEUP_END, /*!< Wakeup stop */ + ESP_GMF_AFE_EVT_VAD_START, /*!< Vad start */ + ESP_GMF_AFE_EVT_VAD_END, /*!< Vad stop */ + ESP_GMF_AFE_EVT_VCMD_DECT_TIMEOUT, /*!< Voice command detect timeout */ + ESP_GMF_AFE_EVT_VCMD_DECTECTED = 0 /*!< Form 0 is the id of the voice commands detected by Multinet*/ + /* DO NOT add items below this line */ +} esp_gmf_afe_event_t; + +/** + * @brief Event structure for GMF AFE + */ +typedef struct { + esp_gmf_afe_event_t type; /*!< Event type */ + void *event_data; /*!< Event data */ + size_t data_len; /*!< Length of event data */ +} esp_gmf_afe_evt_t; + +/** + * @brief Callback type for GMF AFE events + */ +typedef void (*esp_gmf_afe_event_cb_t)(esp_gmf_element_handle_t el, esp_gmf_afe_evt_t *event, void *user_data); + +/** + * @brief Configuration structure for GMF AFE wrapper + */ +typedef struct { + esp_gmf_afe_manager_handle_t afe_manager; /*!< AFE Manager handle */ + uint32_t delay_samples; /*!< Number of samples to delay + Note: If the user wants to using the output of AFE only after detecting the VAD start event, + the time corresponding to the value of this parameter should not be less than the `vad_min_speech_ms` in `afe_config_t` used when creating the afe_manager, + otherwise, a small portion of the data at the beginning of the voice may be lost */ + void *models; /*!< List of models */ + uint32_t wakeup_time; /*!< Unit:ms. The duration that the wakeup state remains when VAD is not triggered */ + uint32_t wakeup_end; /*!< Unit:ms. When the silence time after AUDIO_REC_VAD_END state exceeds this value, it is determined as AUDIO_REC_WAKEUP_END */ + bool vcmd_detect_en; /*!< Enable voice command detection */ + uint32_t vcmd_timeout; /*!< Timeout for voice command detection, units: ms*/ + const char *mn_language; /*!< Language for the multi-net model, `cn` or `en` */ + esp_gmf_afe_event_cb_t event_cb; /*!< Callback function for AI audio events */ + void *event_ctx; /*!< User context to be passed to the callback function */ +} esp_gmf_afe_cfg_t; + +#define DEFAULT_GMF_AFE_CFG(__afe_manager, __event_cb, __event_ctx, __models) { \ + .afe_manager = __afe_manager, /* */ \ + .models = __models, /* */ \ + .delay_samples = ESP_GMF_AFE_DEFAULT_DELAY_SAMPLES, /* Default delay samples, 64ms */ \ + .wakeup_time = ESP_GMF_AFE_DEFAULT_WAKEUP_TIME_MS, /* Default wakeup duration */ \ + .wakeup_end = ESP_GMF_AFE_DEFAULT_WAKEUP_END_MS, /* Default wakeup end time */ \ + .vcmd_detect_en = false, /* */ \ + .vcmd_timeout = ESP_GMF_AFE_DEFAULT_VCMD_TIMEOUT_MS, /* */ \ + .mn_language = "cn", /* Default language `cn` */ \ + .event_cb = __event_cb, /* */ \ + .event_ctx = __event_ctx, /* */ \ +} + +/** + * @brief Initialize the GMF AFE + * + * @param[in] config Pointer to the configuration structure + * @param[out] handle Pointer to the handle to be created + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_MEMORY_LACK Memory allocation failed + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_afe_init(void *config, esp_gmf_obj_handle_t *handle); + +/** + * @brief Begin voice command detection + * + * @param[in] handle Handle to the GMF object + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + * - ESP_GMF_ERR_INVALID_STATE Voice command not enabled + */ +esp_gmf_err_t esp_gmf_afe_vcmd_detection_begin(esp_gmf_element_handle_t handle); + +/** + * @brief Cancel voice command detection + * + * @note This function is used to clear the states of voice command detection process, + * the voice command detection will stay enabled, + * and the user can call `esp_gmf_afe_vcmd_detection_begin` to start the detection again + * + * @param[in] handle Handle to the GMF object + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + * - ESP_GMF_ERR_INVALID_STATE Voice command not enabled + */ +esp_gmf_err_t esp_gmf_afe_vcmd_detection_cancel(esp_gmf_element_handle_t handle); + +/** + * @brief Set the event callback for the AFE (Audio Front-End) element + * + * This function registers a callback function to handle events generated by the + * AFE element. The callback will be invoked with the specified context whenever + * an event occurs + * + * @param handle The handle to the AFE element + * @param cb The callback function to handle AFE events + * @param ctx User-defined context to be passed to the callback function + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + * - ESP_GMF_ERR_INVALID_STATE Config not exist + */ +esp_gmf_err_t esp_gmf_afe_set_event_cb(esp_gmf_element_handle_t handle, esp_gmf_afe_event_cb_t cb, void *ctx); + +/** + * @brief Enable or disable keep-awake mode + * + * When keep-awake mode is enabled, the system will remain in the wake state + * and prevent wakeup_end events from being triggered automatically + * This is useful for scenarios where you want to keep the system active + * without automatic timeout + * + * @param handle The handle to the AFE element + * @param enable True to enable keep-awake mode, false to disable + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + * - ESP_GMF_ERR_INVALID_STATE Config not exist + * - ESP_GMF_ERR_TIMEOUT Command send timeout + */ +esp_gmf_err_t esp_gmf_afe_keep_awake(esp_gmf_element_handle_t handle, bool enable); + +/** + * @brief Manually trigger wakeup state + * + * This function allows manual activation of the wakeup state without waiting + * for automatic wakeword detection. It is useful in the following scenarios: + * + * 1. **Button-triggered activation**: When users press a physical button to + * activate voice interaction, bypassing the need for wakewords + * 2. **External event-driven activation**: When the system needs to enter + * wakeup state based on external triggers (sensors, timers, network events) + * + * After calling this function, the AFE will enter wakeup state and begin + * listening for voice commands (if voice command detection is enabled). + * The system will generate ESP_GMF_AFE_EVT_WAKEUP_START event and remain + * active according to the configured wakeup_time duration. + * + * @param[in] handle Handle to the GMF object + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + * - ESP_GMF_ERR_INVALID_STATE Element not opened + * - ESP_GMF_ERR_TIMEOUT Command send timeout + */ +esp_gmf_err_t esp_gmf_afe_trigger_wakeup(esp_gmf_element_handle_t handle); + +/** + * @brief Manually trigger sleep of wakeup state + * + * @param[in] handle Handle to the GMF object + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + * - ESP_GMF_ERR_INVALID_STATE Element not opened + * - ESP_GMF_ERR_TIMEOUT Command send timeout + */ +esp_gmf_err_t esp_gmf_afe_trigger_sleep(esp_gmf_element_handle_t handle); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/include/esp_gmf_afe_manager.h b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/include/esp_gmf_afe_manager.h new file mode 100644 index 0000000..f4612e1 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/include/esp_gmf_afe_manager.h @@ -0,0 +1,243 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#include "esp_afe_sr_iface.h" +#include "esp_gmf_err.h" + +/** + * The AFE Manager aims to provide users with a simple interface for managing AFE (Audio front end) functions, + * including WakeNet, VAD, AEC, SE, and more + * This component will automatically create feed and fetch tasks, + * users only need to provide data read callback functions and result processing callback functions + * Users can configure `AFE` functions through the `afe_config_t` structure + * The data fed into `AFE` must be in 16-bit PCM format with a sampling rate of 16kHz, + * the number of channels and channel arrangement are determined by the configuration in the `afe_config_init` function, + * for details, please refer to the description of the `afe_config_init` function which provide by `esp-sr` + */ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define ESP_AFE_MANAGER_FEED_TASK_CORE (0) +#define ESP_AFE_MANAGER_FEED_TASK_PRIO (5) +#define ESP_AFE_MANAGER_FEED_TASK_STACK (3 * 1024) +#define ESP_AFE_MANAGER_FETCH_TASK_CORE (1) +#define ESP_AFE_MANAGER_FETCH_TASK_PRIO (5) +#define ESP_AFE_MANAGER_FETCH_TASK_STACK (3 * 1024) + +/** + * @enum + * @brief Enumeration of AFE functions + */ +typedef enum { + ESP_AFE_FEATURE_WAKENET, /*!< WakeNet function */ + ESP_AFE_FEATURE_VAD, /*!< Voice Activity Detection function */ + ESP_AFE_FEATURE_AEC, /*!< Acoustic Echo Cancellation function */ + ESP_AFE_FEATURE_SE, /*!< Speech Enhancement function */ +} esp_gmf_afe_feature_t; + +/** + * @brief Handle for the AFE manager + */ +typedef void *esp_gmf_afe_manager_handle_t; + +/** + * @brief Callback function type for processing AFE results + * + * @param[in] result Pointer to the result structure + * @param[in] user_ctx User context to be passed to the callback function + */ +typedef void (*esp_gmf_afe_manager_result_cb_t)(afe_fetch_result_t *result, void *user_ctx); + +/** + * @brief Callback type for reading data + * + * @param[in] buffer Pointer to the buffer to read data into + * @param[in] buf_sz Size of the buffer + * @param[in] user_ctx User context to be passed to the callback function + * @param[in] ticks Number of ticks to wait for data + * + * @return + */ +typedef int32_t (*esp_gmf_afe_manager_read_cb_t)(void *buffer, int buf_sz, void *user_ctx, uint32_t ticks); + +/** + * @brief Configuration structure for the task setting + */ +typedef struct { + uint32_t stack_size; /*!< Task stack size */ + uint8_t core; /*!< Task core id */ + uint8_t prio; /*!< Task priority */ +} esp_gmf_afe_manager_task_setting_t; + +/** + * @brief Configuration structure for the AFE manager + */ +typedef struct { + afe_config_t *afe_cfg; /*!< Configuration of ESP AFE */ + esp_gmf_afe_manager_task_setting_t feed_task_setting; /*!< Feed task setting */ + esp_gmf_afe_manager_task_setting_t fetch_task_setting; /*!< Fetch task setting */ + esp_gmf_afe_manager_read_cb_t read_cb; /*!< Callback function for reading audio data */ + void *read_ctx; /*!< Context for the read callback function */ + esp_gmf_afe_manager_result_cb_t result_cb; /*!< Callback function for processing AFE results */ + void *result_ctx; /*!< Context for the result callback function */ +} esp_gmf_afe_manager_cfg_t; + +#define DEFAULT_GMF_AFE_MANAGER_CFG(_afe_cfg, _read_cb, _read_ctx, _result_cb, _result_ctx) { \ + .afe_cfg = _afe_cfg, \ + .feed_task_setting = { \ + .stack_size = ESP_AFE_MANAGER_FEED_TASK_STACK, \ + .core = ESP_AFE_MANAGER_FEED_TASK_CORE, \ + .prio = ESP_AFE_MANAGER_FEED_TASK_PRIO \ + }, \ + .fetch_task_setting = { \ + .stack_size = ESP_AFE_MANAGER_FETCH_TASK_STACK, \ + .core = ESP_AFE_MANAGER_FETCH_TASK_CORE, \ + .prio = ESP_AFE_MANAGER_FETCH_TASK_PRIO \ + }, \ + .read_cb = _read_cb, \ + .read_ctx = _read_ctx, \ + .result_cb = _result_cb, \ + .result_ctx = _result_ctx \ +} + +/** + * @brief GMF AFE Manager Feature Configuration + * + * This structure defines the feature enable states for the AFE manager + * A value of `true` indicates that the feature is enabled, while `false` indicates it is disabled + */ +typedef struct { + bool wakeup; /*!< Wake-up detection */ + bool vad; /*!< Voice Activity Detection (VAD) */ + bool ns; /*!< Noise Suppression (NS) */ + bool aec; /*!< Acoustic Echo Cancellation (AEC) */ + bool se; /*!< Speech Enhancement (SE) */ +} esp_gmf_afe_manager_features_t; + +/** + * @brief Create an AFE Manager instance + * + * @param[in] cfg Pointer to the AFE manager configuration structure + * @param[out] handle Pointer to the created AFE manager handle + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_FAIL Failed to create the AFE manager + * - ESP_GMF_ERR_MEMORY_LACK Insufficient memory allocation + */ +esp_gmf_err_t esp_gmf_afe_manager_create(esp_gmf_afe_manager_cfg_t *cfg, esp_gmf_afe_manager_handle_t *handle); + +/** + * @brief Destroy an AFE Manager instance + * + * @param[in] handle AFE manager handle to be destroyed + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid handle + */ +esp_gmf_err_t esp_gmf_afe_manager_destroy(esp_gmf_afe_manager_handle_t handle); + +/** + * @brief Set the audio input read callback for the AFE Manager + * + * @note If the read callback is set to `NULL`, the AFE Manager will be suspended + * + * @param[in] handle AFE manager handle + * @param[in] read_cb Function pointer to the read callback + * @param[in] read_ctx User-defined context to be passed to the callback + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid arguments + */ +esp_gmf_err_t esp_gmf_afe_manager_set_read_cb(esp_gmf_afe_manager_handle_t handle, esp_gmf_afe_manager_read_cb_t read_cb, void *read_ctx); + +/** + * @brief Register a processing result callback for the AFE Manager + * + * @param[in] handle AFE manager handle + * @param[in] proc_cb Function pointer to the result callback + * @param[in] user_ctx User-defined context to be passed to the callback + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid arguments + */ +esp_gmf_err_t esp_gmf_afe_manager_set_result_cb(esp_gmf_afe_manager_handle_t handle, esp_gmf_afe_manager_result_cb_t proc_cb, void *user_ctx); + +/** + * @brief Suspend or resume the AFE Manager + * + * @param[in] handle AFE manager handle + * @param[in] suspend `true` to suspend, `false` to resume + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid arguments + */ +esp_gmf_err_t esp_gmf_afe_manager_suspend(esp_gmf_afe_manager_handle_t handle, bool suspend); + +/** + * @brief Enable or disable specific features in the AFE Manager + * + * @param[in] handle AFE manager handle + * @param[in] feature Feature to be configured (see `esp_gmf_afe_feature_t`) + * @param[in] enable `true` to enable, `false` to disable + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid arguments + */ +esp_gmf_err_t esp_gmf_afe_manager_enable_features(esp_gmf_afe_manager_handle_t handle, esp_gmf_afe_feature_t feature, bool enable); + +/** + * @brief Retrieve the current feature enable states of the AFE Manager + * + * @param[in] handle AFE manager handle + * @param[out] features Pointer to a structure to store the feature states + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid arguments + */ +esp_gmf_err_t esp_gmf_afe_manager_get_features(esp_gmf_afe_manager_handle_t handle, esp_gmf_afe_manager_features_t *features); + +/** + * @brief Get the processing chunk size for the AFE Manager + * + * @note The chunk size represents the number of audio samples per channel. + * The AFE Manager processes data in fixed-size chunks. + * + * @param[in] handle AFE manager handle + * @param[out] size Pointer to store the chunk size (unit: samples) + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid arguments + */ +esp_gmf_err_t esp_gmf_afe_manager_get_chunk_size(esp_gmf_afe_manager_handle_t handle, size_t *size); + +/** + * @brief Retrieve the number of input channels for the AFE Manager + * + * @param[in] handle AFE manager handle + * @param[out] ch_num Pointer to store the number of channels + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid arguments + */ +esp_gmf_err_t esp_gmf_afe_manager_get_input_ch_num(esp_gmf_afe_manager_handle_t handle, uint8_t *ch_num); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/include/esp_gmf_ai_audio_methods.h b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/include/esp_gmf_ai_audio_methods.h new file mode 100644 index 0000000..18622fc --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/include/esp_gmf_ai_audio_methods.h @@ -0,0 +1,20 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +// AFE Method +#define ESP_GMF_METHOD_AFE_START_VCMD_DET "start_vcmd_det" +#define ESP_GMF_METHOD_AFE_START_VCMD_DET_ARG_EN "vcmd_det" + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/include/esp_gmf_wn.h b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/include/esp_gmf_wn.h new file mode 100644 index 0000000..83b78e5 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/include/esp_gmf_wn.h @@ -0,0 +1,77 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#include "esp_gmf_element.h" +#include "esp_gmf_err.h" +#include "esp_wn_iface.h" +#include "model_path.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * @brief Callback type for WakeNet detection + * + * @param[in] handle Handle to the WakeNet object + * @param[in] trigger_ch The microphone channel that triggered the detection + * @param[in] user_ctx User context passed during initialization + */ +typedef void (*esp_wn_detect_cb_t)(esp_gmf_element_handle_t handle, int32_t trigger_ch, void *user_ctx); + +/** + * @brief Configuration structure for WakeNet + * + * @note The input format, same as afe config: + * `M` to represent the microphone channel, + * `R` to represent the playback reference channel, + * `N` to represent an unknown or unused channel + * For example, input_format="MMNR" indicates that the input data consists of four channels, + * which are the microphone channel, the microphone channel, an unused channel, and the playback channel + */ +typedef struct { + srmodel_list_t *models; /*!< Model list containing wake word models */ + det_mode_t det_mode; /*!< Detection mode */ + char *input_format; /*!< Input format */ + esp_wn_detect_cb_t detect_cb; /*!< Detection callback function */ + void *user_ctx; /*!< User context to be passed to the callback function */ +} esp_gmf_wn_cfg_t; + +/** + * @brief Initialize the WakeNet element + * + * @param[in] config Pointer to the configuration structure + * @param[out] handle Pointer to the handle to be initialized + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + * - ESP_GMF_ERR_MEMORY_LACK Memory allocation failed + * - ESP_GMF_ERR_FAIL Other failures + */ +esp_gmf_err_t esp_gmf_wn_init(esp_gmf_wn_cfg_t *config, esp_gmf_element_handle_t *handle); + +/** + * @brief Set the voice trigger detection callback for WakeNet + * This function registers a user-defined callback that will be invoked + * when WakeNet detects a wake word + * + * @param[in] handle Handle to the WakeNet element + * @param[in] detect_cb Callback function to be called on wake word detection + * @param[in] ctx User-defined context to be passed to the callback + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_wn_set_detect_cb(esp_gmf_element_handle_t handle, esp_wn_detect_cb_t detect_cb, void *ctx); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/private_include/esp_gmf_ch_sort.h b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/private_include/esp_gmf_ch_sort.h new file mode 100644 index 0000000..c4515b3 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/private_include/esp_gmf_ch_sort.h @@ -0,0 +1,56 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#include "esp_types.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * @brief Sort input data according to the input format and convert data layout + * + * @note This function performs two operations: + * 1. Channel sorting: Keeps microphone (M) channels in original order, removes unused (N) channels, + * and places reference (R) channels at the end + * 2. Layout conversion: Converts data from interleaved format (M0[0]R[0]M1[0]N[0]M0[1]R[1]M1[1]N[1]...) to block format (M0[0]M0[1]...M1[0]M1[1]...R[0]R[1]...) + * + * @param[in] input_data Pointer to the interleaved input data buffer + * @param[in] input_format Input format string (e.g., "MMNR" for 2 mic channels, 1 unused, 1 reference) + * @param[in] num_samples Number of samples per channel + * @param[in] num_channels Total number of channels in the input data + * @param[out] sorted_data Pointer to the output buffer for block-formatted sorted data + * Data will be arranged as: [mic_samples][ref_samples] + */ +static inline void esp_gmf_sort_with_format(int16_t *input_data, const char *input_format, int num_samples, int num_channels, int16_t *sorted_data) +{ + int mic_index = 0; + int ref_index = 0; + + for (int ch = 0; ch < num_channels; ++ch) { + if (input_format[ch] == 'M') { + for (int sample = 0; sample < num_samples; ++sample) { + sorted_data[mic_index++] = input_data[sample * num_channels + ch]; + } + } + } + + ref_index = mic_index; + for (int ch = 0; ch < num_channels; ++ch) { + if (input_format[ch] == 'R') { + for (int sample = 0; sample < num_samples; ++sample) { + sorted_data[ref_index++] = input_data[sample * num_channels + ch]; + } + } + } +} + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/src/esp_gmf_aec.c b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/src/esp_gmf_aec.c new file mode 100644 index 0000000..958772f --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/src/esp_gmf_aec.c @@ -0,0 +1,249 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#include +#include +#include +#include "esp_log.h" +#include "esp_afe_config.h" +#include "esp_afe_aec.h" +#include "esp_gmf_payload.h" +#include "esp_gmf_aec.h" +#include "esp_gmf_audio_element.h" +#include "esp_gmf_err.h" +#include "esp_gmf_job.h" +#include "esp_gmf_node.h" +#include "esp_gmf_oal_mem.h" +#include "esp_gmf_cache.h" +#include "esp_gmf_cap.h" +#include "esp_gmf_caps_def.h" + +#define ESP_AEC_BYTES_PER_SAMPLE (sizeof(uint16_t)) // ESP AEC processes data at 16 kHz, 16-bit, and single-channel + +typedef struct { + esp_gmf_audio_element_t parent; + afe_aec_handle_t *aec_handle; + uint32_t chunk_size; + uint32_t frame_len; + esp_gmf_cache_t *cache; + esp_gmf_payload_t *in_load; +} gmf_aec_t; + +static const char *TAG = "GMF_AEC"; + +static esp_gmf_err_t gmf_aec_new(void *cfg, esp_gmf_obj_handle_t *handle) +{ + ESP_GMF_NULL_CHECK(TAG, cfg, { return ESP_GMF_ERR_INVALID_ARG;}); + ESP_GMF_NULL_CHECK(TAG, handle, { return ESP_GMF_ERR_INVALID_ARG;}); + *handle = NULL; + esp_gmf_aec_cfg_t *aec_cfg = (esp_gmf_aec_cfg_t *)cfg; + esp_gmf_obj_handle_t new_obj = NULL; + esp_gmf_err_t ret = esp_gmf_aec_init(aec_cfg, &new_obj); + if (ret != ESP_GMF_ERR_OK) { + return ret; + } + *handle = (void *)new_obj; + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t gmf_aec_received_event_handler(esp_gmf_event_pkt_t *evt, void *ctx) +{ + ESP_GMF_NULL_CHECK(TAG, evt, { return ESP_GMF_ERR_INVALID_ARG;}); + ESP_GMF_NULL_CHECK(TAG, ctx, { return ESP_GMF_ERR_INVALID_ARG;}); + esp_gmf_element_handle_t self = (esp_gmf_element_handle_t)ctx; + esp_gmf_element_handle_t el = evt->from; + esp_gmf_event_state_t state = ESP_GMF_EVENT_STATE_NONE; + esp_gmf_element_get_state(self, &state); + esp_gmf_element_handle_t prev = NULL; + esp_gmf_element_get_prev_el(self, &prev); + if ((state == ESP_GMF_EVENT_STATE_NONE) || (prev == el)) { + if (evt->sub == ESP_GMF_INFO_SOUND) { + esp_gmf_info_sound_t info = {0}; + memcpy(&info, evt->payload, evt->payload_size); + ESP_LOGD(TAG, "RECV info, from: %s-%p, next: %p, self: %s-%p, type: %x, state: %s, rate: %d, ch: %d, bits: %d", + OBJ_GET_TAG(el), el, esp_gmf_node_for_next((esp_gmf_node_t *)el), OBJ_GET_TAG(self), self, evt->type, + esp_gmf_event_get_state_str(state), info.sample_rates, info.channels, info.bits); + if (info.sample_rates != 16000 || info.bits != 16) { + ESP_LOGE(TAG, "Unsupported format, rate: %d, bits: %d", info.sample_rates, info.bits); + return ESP_GMF_ERR_NOT_SUPPORT; + } + esp_gmf_element_set_state(self, ESP_GMF_EVENT_STATE_INITIALIZED); + } + } + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t gmf_aec_destroy(esp_gmf_audio_element_handle_t self) +{ + ESP_GMF_NULL_CHECK(TAG, self, { return ESP_GMF_ERR_INVALID_ARG;}); + + gmf_aec_t *gmf_aec = (gmf_aec_t *)self; + + if (OBJ_GET_CFG(self)) { + esp_gmf_oal_free(OBJ_GET_CFG(self)); + } + if (gmf_aec->aec_handle) { + afe_aec_destroy(gmf_aec->aec_handle); + gmf_aec->aec_handle = NULL; + } + if (gmf_aec->cache) { + esp_gmf_cache_delete(gmf_aec->cache); + gmf_aec->cache = NULL; + } + ESP_LOGD(TAG, "Destroyed"); + esp_gmf_audio_el_deinit(self); + esp_gmf_oal_free(self); + + return ESP_GMF_ERR_OK; +} + +static esp_gmf_job_err_t gmf_aec_open(esp_gmf_audio_element_handle_t self, void *para) +{ + gmf_aec_t *gmf_aec = (gmf_aec_t *)self; + esp_gmf_aec_cfg_t *cfg = OBJ_GET_CFG(self); + gmf_aec->aec_handle = afe_aec_create(cfg->input_format, cfg->filter_len, cfg->type, cfg->mode); + ESP_GMF_NULL_CHECK(TAG, gmf_aec->aec_handle, { return ESP_GMF_JOB_ERR_FAIL;}); + + gmf_aec->chunk_size = afe_aec_get_chunksize(gmf_aec->aec_handle); + gmf_aec->frame_len = gmf_aec->chunk_size * gmf_aec->aec_handle->pcm_config.total_ch_num * ESP_AEC_BYTES_PER_SAMPLE; + esp_gmf_cache_new(gmf_aec->frame_len, &gmf_aec->cache); + ESP_GMF_NULL_CHECK(TAG, gmf_aec->cache, { return ESP_GMF_JOB_ERR_FAIL;}); + + ESP_GMF_ELEMENT_IN_PORT_ATTR_SET(ESP_GMF_ELEMENT_GET(self)->in_attr, ESP_GMF_EL_PORT_CAP_SINGLE, 16, 0, + ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, gmf_aec->frame_len); + ESP_LOGI(TAG, "GMF AEC open, frame_len: %lu, nch %d, chunksize %lu", gmf_aec->frame_len, gmf_aec->aec_handle->pcm_config.total_ch_num, gmf_aec->chunk_size); + esp_gmf_info_sound_t snd_info = {0}; + snd_info.sample_rates = 16000; + snd_info.bits = 16; + snd_info.channels = 1; + esp_gmf_element_notify_snd_info(self, &snd_info); + return ESP_GMF_JOB_ERR_OK; +} + +static esp_gmf_job_err_t gmf_aec_process(esp_gmf_audio_element_handle_t self, void *para) +{ + int ret = ESP_GMF_JOB_ERR_OK; + gmf_aec_t *gmf_aec = (gmf_aec_t *)self; + esp_gmf_port_handle_t in_port = ESP_GMF_ELEMENT_GET(self)->in; + esp_gmf_port_handle_t out_port = ESP_GMF_ELEMENT_GET(self)->out; + esp_gmf_payload_t *out_load = NULL; + esp_gmf_payload_t *cache_load = NULL; + esp_gmf_err_io_t load_ret = 0; + bool need_load = false; + + esp_gmf_cache_ready_for_load(gmf_aec->cache, &need_load); + if (need_load) { + load_ret = esp_gmf_port_acquire_in(in_port, &gmf_aec->in_load, gmf_aec->frame_len, in_port->wait_ticks); + ESP_GMF_PORT_ACQUIRE_IN_CHECK(TAG, load_ret, ret, { goto __quit;}); + esp_gmf_cache_load(gmf_aec->cache, gmf_aec->in_load); + } + esp_gmf_cache_acquire(gmf_aec->cache, gmf_aec->frame_len, &cache_load); + if (cache_load->valid_size != gmf_aec->frame_len) { + if (cache_load->is_done == true) { + ret = ESP_GMF_JOB_ERR_DONE; + } else { + ret = ESP_GMF_JOB_ERR_CONTINUE; + } + ESP_LOGD(TAG, "Return Continue, size:%u", cache_load->valid_size); + goto __quit; + } + load_ret = esp_gmf_port_acquire_out(out_port, &out_load, gmf_aec->chunk_size * ESP_AEC_BYTES_PER_SAMPLE, ESP_GMF_MAX_DELAY); + ESP_GMF_PORT_ACQUIRE_OUT_CHECK(TAG, load_ret, ret, { goto __quit;}); + if (out_load->buf_length < gmf_aec->chunk_size * ESP_AEC_BYTES_PER_SAMPLE) { + ret = ESP_GMF_JOB_ERR_FAIL; + ESP_LOGE(TAG, "Output buffer is not enough"); + goto __quit; + } + afe_aec_process(gmf_aec->aec_handle, (int16_t *)cache_load->buf, (int16_t *)out_load->buf); + out_load->valid_size = gmf_aec->chunk_size * ESP_AEC_BYTES_PER_SAMPLE; + out_load->is_done = cache_load->is_done; + esp_gmf_cache_ready_for_load(gmf_aec->cache, &need_load); + if (need_load == false) { + ret = ESP_GMF_JOB_ERR_TRUNCATE; + } else { + ret = (out_load->is_done == true ? ESP_GMF_JOB_ERR_DONE : ESP_GMF_JOB_ERR_OK); + } + +__quit: + if (out_load) { + esp_gmf_port_release_out(out_port, out_load, ESP_GMF_MAX_DELAY); + } + if (gmf_aec->in_load && (ret != ESP_GMF_JOB_ERR_TRUNCATE)) { + esp_gmf_port_release_in(in_port, gmf_aec->in_load, ESP_GMF_MAX_DELAY); + gmf_aec->in_load = NULL; + } + if (cache_load) { + esp_gmf_cache_release(gmf_aec->cache, cache_load); + } + return ret; +} + +static esp_gmf_job_err_t gmf_aec_close(esp_gmf_audio_element_handle_t self, void *para) +{ + gmf_aec_t *gmf_aec = (gmf_aec_t *)self; + if (gmf_aec->aec_handle) { + afe_aec_destroy(gmf_aec->aec_handle); + gmf_aec->aec_handle = NULL; + } + if (gmf_aec->cache) { + esp_gmf_cache_delete(gmf_aec->cache); + gmf_aec->cache = NULL; + } + return ESP_GMF_JOB_ERR_OK; +} + +static esp_gmf_err_t _load_aec_caps_func(esp_gmf_element_handle_t handle) +{ + esp_gmf_cap_t *caps = NULL; + esp_gmf_cap_t aec_caps = {0}; + aec_caps.cap_eightcc = ESP_GMF_CAPS_AUDIO_AEC; + aec_caps.attr_fun = NULL; + int ret = esp_gmf_cap_append(&caps, &aec_caps); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, { return ret;}, "Failed to create capability"); + + esp_gmf_element_t *el = (esp_gmf_element_t *)handle; + el->caps = caps; + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_aec_init(esp_gmf_aec_cfg_t *config, esp_gmf_obj_handle_t *handle) +{ + ESP_GMF_NULL_CHECK(TAG, config, { return ESP_GMF_ERR_INVALID_ARG;}); + ESP_GMF_NULL_CHECK(TAG, handle, { return ESP_GMF_ERR_INVALID_ARG;}); + *handle = NULL; + gmf_aec_t *gmf_aec = esp_gmf_oal_calloc(1, sizeof(gmf_aec_t)); + ESP_GMF_MEM_VERIFY(TAG, gmf_aec, { return ESP_GMF_ERR_MEMORY_LACK;}, "aec", sizeof(gmf_aec_t)); + esp_gmf_obj_t *obj = (esp_gmf_obj_t *)gmf_aec; + obj->new_obj = gmf_aec_new; + obj->del_obj = gmf_aec_destroy; + + esp_gmf_aec_cfg_t *obj_cfg = esp_gmf_oal_calloc(1, sizeof(esp_gmf_aec_cfg_t)); + memcpy(obj_cfg, config, sizeof(esp_gmf_aec_cfg_t)); + esp_gmf_err_t ret = esp_gmf_obj_set_config(obj, obj_cfg, sizeof(esp_gmf_aec_cfg_t)); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto __failed, "Failed set OBJ configuration"); + ret = esp_gmf_obj_set_tag(obj, "ai_aec"); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto __failed, "Failed set OBJ tag"); + esp_gmf_element_cfg_t el_cfg = {0}; + ESP_GMF_ELEMENT_IN_PORT_ATTR_SET(el_cfg.in_attr, ESP_GMF_EL_PORT_CAP_SINGLE, 16, 0, + ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, 1024); + ESP_GMF_ELEMENT_OUT_PORT_ATTR_SET(el_cfg.out_attr, ESP_GMF_EL_PORT_CAP_SINGLE, 16, 0, + ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, 1024); + el_cfg.dependency = true; + esp_gmf_audio_el_init(gmf_aec, &el_cfg); + + ESP_GMF_ELEMENT_GET(gmf_aec)->ops.open = gmf_aec_open; + ESP_GMF_ELEMENT_GET(gmf_aec)->ops.process = gmf_aec_process; + ESP_GMF_ELEMENT_GET(gmf_aec)->ops.close = gmf_aec_close; + ESP_GMF_ELEMENT_GET(gmf_aec)->ops.event_receiver = gmf_aec_received_event_handler; + ESP_GMF_ELEMENT_GET(gmf_aec)->ops.load_caps = _load_aec_caps_func; + + *handle = obj; + return ESP_GMF_ERR_OK; +__failed: + esp_gmf_obj_delete(obj); + return ret; +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/src/esp_gmf_afe.c b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/src/esp_gmf_afe.c new file mode 100644 index 0000000..c8e6902 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/src/esp_gmf_afe.c @@ -0,0 +1,820 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#include +#include +#include +#include +#include "esp_gmf_obj.h" +#include "freertos/idf_additions.h" +#include "freertos/queue.h" +#include "esp_log.h" +#include "esp_timer.h" +#include "esp_gmf_audio_element.h" +#include "esp_gmf_err.h" +#include "esp_gmf_job.h" +#include "esp_gmf_new_databus.h" +#include "esp_gmf_oal_mem.h" +#include "esp_wn_iface.h" +#include "esp_mn_iface.h" +#include "esp_mn_models.h" +#include "esp_process_sdkconfig.h" +#include "esp_gmf_afe_manager.h" +#include "esp_gmf_afe.h" +#include "esp_gmf_cap.h" +#include "esp_gmf_caps_def.h" +#include "esp_gmf_method.h" +#include "esp_gmf_ai_audio_methods.h" + +#define AFE_DEFAULT_DATA_SIZE (2048) +#define WAKEUP_ST_SET(handle, st) (handle->wakeup_state = st) + +/** + * @brief Represents the various states of the wakeup process in the system + * + * This enumeration defines the different states that the system can be in + * during the wakeup and speech processing lifecycle. + */ +typedef enum { + ST_IDLE, + ST_WAKEUP, + ST_SPEECHING, + ST_WAIT_FOR_SLEEP, +} wakeup_state_t; + +/** + * @brief Enumeration representing various wakeup events in the system + * + * This enumeration defines the different types of events that can trigger + * the state changing in the system. Each event corresponds to a specific + * condition or state. + */ +typedef enum { + ET_NOISE_DECT, + ET_SPEECH_DECT, + ET_WWE_DECT, + ET_WAKEUP_TIMER_EXPIRED, + ET_KEEP_WAKE_MODIFIED, + ET_TRIGGER_WAKEUP, + ET_TRIGGER_SLEEP, + ET_UNKNOWN +} wakeup_event_t; + +typedef struct { + wakeup_event_t event; + union { + struct { + bool enable; + } keep_awake; + struct { + bool state; + } trigger; + } payload; +} afe_evt_msg_t; + +/** + * @brief Structure representing the ESP GMF AFE component + */ +typedef struct { + esp_gmf_audio_element_t parent; + esp_gmf_db_handle_t in_db; + esp_gmf_db_handle_t out_db; + wakeup_state_t wakeup_state; + wakeup_event_t last_event; + bool origin_vad_enable; + bool keep_wake; + esp_timer_handle_t wakeup_timer; + SemaphoreHandle_t wake_st_lock; + model_iface_data_t *mn_handle; + bool mn_detecting; + SemaphoreHandle_t mn_lock; + esp_mn_state_t mn_state; + esp_mn_iface_t *multinet; + QueueHandle_t msg_queue; +} esp_gmf_afe_t; + +static const char *TAG = "GMF_AFE"; + +static const char *state_str[] = { + "ST_IDLE", + "ST_WAKEUP", + "ST_SPEECHING", + "ST_WAIT_FOR_SLEEP", +}; +static const char *event_str[] = { + "ET_NOISE_DECT", + "ET_SPEECH_DECT", + "ET_WWE_DECT", + "ET_WAKEUP_TIMER_EXPIRED", + "ET_KEEP_WAKE_MODIFIED", + "ET_TRIGGER_WAKEUP", + "ET_TRIGGER_SLEEP", + "ET_UNKNOWN", +}; + +static wakeup_event_t result_2_event(afe_fetch_result_t *result) +{ + if (result->wakeup_state == WAKENET_DETECTED) { + return ET_WWE_DECT; + } + if (result->vad_state == VAD_SILENCE) { + return ET_NOISE_DECT; + } + if (result->vad_state == VAD_SPEECH) { + return ET_SPEECH_DECT; + } + return ET_UNKNOWN; +} + +static void event_2_user(esp_gmf_afe_t *gmf_afe, int event, void *event_data, size_t dlen) +{ + esp_gmf_afe_cfg_t *cfg = OBJ_GET_CFG(gmf_afe); + if (cfg->event_cb) { + esp_gmf_afe_evt_t afe_event = { + .type = event, + .event_data = event_data, + .data_len = dlen, + }; + cfg->event_cb(gmf_afe, &afe_event, cfg->event_ctx); + } +} + +static void afe_result_2_wakeup_info(afe_fetch_result_t *result, esp_gmf_afe_wakeup_info_t *info) +{ + info->wake_word_index = result->wake_word_index; + info->wakenet_model_index = result->wakenet_model_index; + info->data_volume = result->data_volume; +} + +static void wakeup_state_reset(esp_gmf_afe_t *gmf_afe) +{ + esp_timer_stop(gmf_afe->wakeup_timer); + WAKEUP_ST_SET(gmf_afe, ST_IDLE); + esp_gmf_afe_manager_features_t feat = { 0 }; + esp_gmf_afe_cfg_t *cfg = OBJ_GET_CFG(gmf_afe); + if (cfg && cfg->afe_manager) { + esp_gmf_afe_manager_get_features(cfg->afe_manager, &feat); + if (feat.wakeup && gmf_afe->origin_vad_enable) { + esp_gmf_afe_manager_enable_features(cfg->afe_manager, ESP_AFE_FEATURE_VAD, false); + } + } +} + +static void wakeup_timer_start(esp_gmf_afe_t *gmf_afe) +{ + esp_gmf_afe_cfg_t *cfg = OBJ_GET_CFG(gmf_afe); + int timeout = 0; + if (gmf_afe->wakeup_state == ST_WAKEUP) { + timeout = cfg->wakeup_time; + } else if (gmf_afe->wakeup_state == ST_WAIT_FOR_SLEEP) { + timeout = cfg->wakeup_end; + } + esp_timer_stop(gmf_afe->wakeup_timer); + if (timeout) { + esp_timer_start_once(gmf_afe->wakeup_timer, timeout * 1000); + } +} + +static void wakeup_state_update(esp_gmf_afe_t *gmf_afe, wakeup_event_t event, void *event_data, size_t len) +{ + esp_gmf_afe_cfg_t *cfg = OBJ_GET_CFG(gmf_afe); + esp_gmf_afe_manager_features_t afe_feat = {0}; + int32_t user_event = -1; + if (event == ET_KEEP_WAKE_MODIFIED || gmf_afe->last_event != event) { + ESP_LOGV(TAG, "Recorder update state, cur %s, event %s", state_str[gmf_afe->wakeup_state], event_str[event]); + gmf_afe->last_event = event; + } else { + return; + } + xSemaphoreTake(gmf_afe->wake_st_lock, portMAX_DELAY); + if ((event == ET_WWE_DECT || event == ET_TRIGGER_WAKEUP) && gmf_afe->wakeup_state != ST_IDLE) { + wakeup_state_reset(gmf_afe); + } + esp_gmf_afe_manager_get_features(cfg->afe_manager, &afe_feat); + switch (gmf_afe->wakeup_state) { + case ST_IDLE: { + if (event == ET_WWE_DECT || event == ET_TRIGGER_WAKEUP) { + WAKEUP_ST_SET(gmf_afe, ST_WAKEUP); + if (!gmf_afe->keep_wake) { + wakeup_timer_start(gmf_afe); + } + if (gmf_afe->origin_vad_enable) { + esp_gmf_afe_manager_enable_features(cfg->afe_manager, ESP_AFE_FEATURE_VAD, true); + } + user_event = ESP_GMF_AFE_EVT_WAKEUP_START; + } else if (event == ET_SPEECH_DECT && afe_feat.wakeup == false) { + WAKEUP_ST_SET(gmf_afe, ST_SPEECHING); + user_event = ESP_GMF_AFE_EVT_VAD_START; + } + break; + } + case ST_WAKEUP: { + if (event == ET_SPEECH_DECT) { + esp_timer_stop(gmf_afe->wakeup_timer); + WAKEUP_ST_SET(gmf_afe, ST_SPEECHING); + user_event = ESP_GMF_AFE_EVT_VAD_START; + } else if (event == ET_KEEP_WAKE_MODIFIED) { + if (gmf_afe->keep_wake) { + esp_timer_stop(gmf_afe->wakeup_timer); + } else { + wakeup_timer_start(gmf_afe); + } + } else if (event == ET_WAKEUP_TIMER_EXPIRED) { + WAKEUP_ST_SET(gmf_afe, ST_IDLE); + esp_timer_stop(gmf_afe->wakeup_timer); + user_event = ESP_GMF_AFE_EVT_WAKEUP_END; + } else if (event == ET_TRIGGER_SLEEP) { + WAKEUP_ST_SET(gmf_afe, ST_IDLE); + esp_timer_stop(gmf_afe->wakeup_timer); + if (gmf_afe->origin_vad_enable) { + esp_gmf_afe_manager_enable_features(cfg->afe_manager, ESP_AFE_FEATURE_VAD, false); + } + user_event = ESP_GMF_AFE_EVT_WAKEUP_END; + } + break; + } + case ST_SPEECHING: { + if (event == ET_NOISE_DECT) { + if (afe_feat.wakeup == true) { + if (!gmf_afe->keep_wake) { + WAKEUP_ST_SET(gmf_afe, ST_WAIT_FOR_SLEEP); + wakeup_timer_start(gmf_afe); + } else { + WAKEUP_ST_SET(gmf_afe, ST_WAKEUP); + } + } else { + WAKEUP_ST_SET(gmf_afe, ST_IDLE); + } + user_event = ESP_GMF_AFE_EVT_VAD_END; + } else if (event == ET_TRIGGER_SLEEP) { + WAKEUP_ST_SET(gmf_afe, ST_IDLE); + if (gmf_afe->origin_vad_enable) { + esp_gmf_afe_manager_enable_features(cfg->afe_manager, ESP_AFE_FEATURE_VAD, false); + } + user_event = ESP_GMF_AFE_EVT_WAKEUP_END; + } + break; + } + case ST_WAIT_FOR_SLEEP: { + if (event == ET_SPEECH_DECT) { + WAKEUP_ST_SET(gmf_afe, ST_SPEECHING); + user_event = ESP_GMF_AFE_EVT_VAD_START; + } else if (event == ET_WAKEUP_TIMER_EXPIRED) { + WAKEUP_ST_SET(gmf_afe, ST_IDLE); + if (gmf_afe->origin_vad_enable) { + esp_gmf_afe_manager_enable_features(cfg->afe_manager, ESP_AFE_FEATURE_VAD, false); + } + user_event = ESP_GMF_AFE_EVT_WAKEUP_END; + } else if (event == ET_KEEP_WAKE_MODIFIED) { + if (gmf_afe->keep_wake) { + esp_timer_stop(gmf_afe->wakeup_timer); + WAKEUP_ST_SET(gmf_afe, ST_WAKEUP); + } else { + wakeup_timer_start(gmf_afe); + } + } else if (event == ET_TRIGGER_SLEEP) { + WAKEUP_ST_SET(gmf_afe, ST_IDLE); + esp_timer_stop(gmf_afe->wakeup_timer); + if (gmf_afe->origin_vad_enable) { + esp_gmf_afe_manager_enable_features(cfg->afe_manager, ESP_AFE_FEATURE_VAD, false); + } + user_event = ESP_GMF_AFE_EVT_WAKEUP_END; + } + break; + } + default: + break; + } + xSemaphoreGive(gmf_afe->wake_st_lock); + if (user_event != -1) { + event_2_user(gmf_afe, user_event, event_data, len); + } +} + +static void wakeup_afe_monitor(afe_fetch_result_t *result, void *user_ctx) +{ + wakeup_event_t event = result_2_event(result); + if (event == ET_WWE_DECT) { + esp_gmf_afe_wakeup_info_t info = {0}; + afe_result_2_wakeup_info(result, &info); + wakeup_state_update(user_ctx, event, &info, sizeof(esp_gmf_afe_wakeup_info_t)); + } else { + wakeup_state_update(user_ctx, event, NULL, 0); + } +} + +static void wakeup_timer_expired(void *arg) +{ + esp_gmf_afe_t *gmf_afe = arg; + wakeup_state_update(gmf_afe, ET_WAKEUP_TIMER_EXPIRED, NULL, 0); +} + +static esp_err_t vcmd_det_begin(esp_gmf_afe_t *gmf_afe) +{ + if (!gmf_afe) { + return ESP_ERR_INVALID_ARG; + } + xSemaphoreTake(gmf_afe->mn_lock, portMAX_DELAY); + gmf_afe->mn_detecting = true; + gmf_afe->mn_state = ESP_MN_STATE_TIMEOUT; + xSemaphoreGive(gmf_afe->mn_lock); + return ESP_OK; +} + +static esp_err_t vcmd_det_cancel(esp_gmf_afe_t *gmf_afe) +{ + if (!gmf_afe) { + return ESP_ERR_INVALID_ARG; + } + xSemaphoreTake(gmf_afe->mn_lock, portMAX_DELAY); + gmf_afe->mn_detecting = false; + gmf_afe->mn_state = ESP_MN_STATE_TIMEOUT; + gmf_afe->multinet->clean(gmf_afe->mn_handle); + xSemaphoreGive(gmf_afe->mn_lock); + return ESP_OK; +} + +static void mn_afe_monitor(afe_fetch_result_t *result, void *user_ctx) +{ + esp_gmf_afe_t *gmf_afe = user_ctx; + + if (gmf_afe->mn_detecting) { + if ((gmf_afe->mn_state != ESP_MN_STATE_DETECTING) && result->vad_cache_size) { + uint8_t *cache = (uint8_t *)result->vad_cache; + while (cache < ((uint8_t *)result->vad_cache + result->vad_cache_size)) { + gmf_afe->mn_state = gmf_afe->multinet->detect(gmf_afe->mn_handle, (int16_t *)cache); + if (gmf_afe->mn_state == ESP_MN_STATE_DETECTED) { + goto __proc_state; + } + cache += result->data_size; + } + } + gmf_afe->mn_state = gmf_afe->multinet->detect(gmf_afe->mn_handle, result->data); + __proc_state: + switch (gmf_afe->mn_state) { + case ESP_MN_STATE_DETECTED: { + esp_mn_results_t *mn_result = gmf_afe->multinet->get_results(gmf_afe->mn_handle); + esp_gmf_afe_vcmd_info_t vcmd_info = { + .phrase_id = mn_result->phrase_id[0], + .prob = mn_result->prob[0]}; + strlcpy(vcmd_info.str, mn_result->string, sizeof(vcmd_info.str)); + event_2_user(gmf_afe, vcmd_info.phrase_id, &vcmd_info, sizeof(esp_gmf_afe_vcmd_info_t)); + + break; + } + case ESP_MN_STATE_TIMEOUT: { + xSemaphoreTake(gmf_afe->mn_lock, portMAX_DELAY); + gmf_afe->mn_detecting = false; + xSemaphoreGive(gmf_afe->mn_lock); + event_2_user(gmf_afe, ESP_GMF_AFE_EVT_VCMD_DECT_TIMEOUT, NULL, 0); + break; + } + default: + break; + } + } +} + +static void esp_gmf_afe_result_proc(afe_fetch_result_t *result, void *user_ctx) +{ + esp_gmf_afe_t *gmf_afe = (esp_gmf_afe_t *)user_ctx; + if (result->data_size) { + esp_gmf_afe_manager_features_t feat = {0}; + esp_gmf_afe_cfg_t *cfg = OBJ_GET_CFG(gmf_afe); + esp_gmf_afe_manager_get_features(cfg->afe_manager, &feat); + if (feat.wakeup || feat.vad) { + wakeup_afe_monitor(result, user_ctx); + } + if (cfg->vcmd_detect_en) { + mn_afe_monitor(result, user_ctx); + } + + uint32_t available_size = 0; + esp_gmf_db_get_available(gmf_afe->out_db, &available_size); + + if (available_size < result->data_size) { + esp_gmf_data_bus_block_t blk = {0}; + blk.buf_length = result->data_size - available_size; + ESP_GMF_RET_ON_FAIL(TAG, esp_gmf_db_acquire_read(gmf_afe->out_db, &blk, blk.buf_length, ESP_GMF_MAX_DELAY), { return;}, "DB failed to acquire read"); + ESP_GMF_RET_ON_FAIL(TAG, esp_gmf_db_release_read(gmf_afe->out_db, &blk, ESP_GMF_MAX_DELAY), { return;}, "DB failed to release read"); + } + esp_gmf_data_bus_block_t blk = {0}; + ESP_GMF_RET_ON_FAIL(TAG, esp_gmf_db_acquire_write(gmf_afe->out_db, &blk, result->data_size, ESP_GMF_MAX_DELAY), { return;}, "DB failed to acquire write"); + blk.buf = (uint8_t *)result->data; + blk.valid_size = result->data_size; + ESP_GMF_RET_ON_FAIL(TAG, esp_gmf_db_release_write(gmf_afe->out_db, &blk, ESP_GMF_MAX_DELAY), { return;}, "DB failed to release write"); + } +} + +static int32_t esp_gmf_afe_read_cb(void *buffer, int buf_sz, void *user_ctx, TickType_t ticks) +{ + esp_gmf_afe_t *gmf_afe = (esp_gmf_afe_t *)user_ctx; + esp_gmf_data_bus_block_t blk = {0}; + blk.buf = buffer; + blk.buf_length = buf_sz; + ESP_LOGD(TAG, "Feed %zu", blk.buf_length); + ESP_GMF_RET_ON_FAIL(TAG, esp_gmf_db_acquire_read(gmf_afe->in_db, &blk, buf_sz, ticks), { return 0;}, "DB failed to acquire read"); + ESP_GMF_RET_ON_FAIL(TAG, esp_gmf_db_release_read(gmf_afe->in_db, &blk, ticks), { return 0;}, "DB failed to release read"); + return buf_sz; +} + +static esp_gmf_err_t esp_gmf_afe_new(void *cfg, esp_gmf_obj_handle_t *handle) +{ + ESP_GMF_MEM_CHECK(TAG, cfg, { return ESP_GMF_ERR_INVALID_ARG;}); + ESP_GMF_MEM_CHECK(TAG, handle, { return ESP_GMF_ERR_INVALID_ARG;}); + esp_gmf_afe_cfg_t *gmf_afe_manager_cfg = (esp_gmf_afe_cfg_t *)cfg; + esp_gmf_obj_handle_t new_obj = NULL; + int ret = ESP_GMF_ERR_OK; + ret = esp_gmf_afe_init(gmf_afe_manager_cfg, &new_obj); + if (ret != ESP_GMF_ERR_OK) { + return ret; + } + *handle = (void *)new_obj; + ESP_LOGI(TAG, "New an object,%s-%p", OBJ_GET_TAG(new_obj), new_obj); + return ret; +} + +static esp_gmf_err_t esp_gmf_afe_destroy(esp_gmf_audio_element_handle_t self) +{ + if (self) { + if (OBJ_GET_CFG(self)) { + esp_gmf_oal_free(OBJ_GET_CFG(self)); + } + esp_gmf_audio_el_deinit(self); + esp_gmf_oal_free(self); + } + return ESP_GMF_ERR_OK; +} + +static esp_gmf_job_err_t esp_gmf_afe_open(esp_gmf_audio_element_handle_t self, void *para) +{ + esp_gmf_afe_t *gmf_afe = (esp_gmf_afe_t *)self; + esp_gmf_afe_cfg_t *cfg = OBJ_GET_CFG(self); + size_t buf_size = 0, chunk_size = 0; + uint8_t ch_num = 0; + esp_gmf_afe_manager_get_input_ch_num(cfg->afe_manager, &ch_num); + esp_gmf_afe_manager_get_chunk_size(cfg->afe_manager, &chunk_size); + buf_size = chunk_size * ch_num * sizeof(uint16_t); + ESP_GMF_ELEMENT_GET(self)->in_attr.data_size = buf_size; + ESP_GMF_ELEMENT_GET(self)->out_attr.data_size = chunk_size * 2; + esp_gmf_afe_manager_features_t feat = {0}; + esp_gmf_afe_manager_get_features(cfg->afe_manager, &feat); + if (feat.wakeup || feat.vad) { + gmf_afe->wake_st_lock = xSemaphoreCreateMutex(); + esp_timer_create_args_t wakeup_timer_cfg = { + .callback = wakeup_timer_expired, + .arg = gmf_afe, + .dispatch_method = ESP_TIMER_TASK, + .name = "wakeup_timer", + }; + esp_timer_create(&wakeup_timer_cfg, &gmf_afe->wakeup_timer); + if (feat.wakeup && feat.vad) { + esp_gmf_afe_manager_enable_features(cfg->afe_manager, ESP_AFE_FEATURE_VAD, false); + gmf_afe->origin_vad_enable = true; + } + } + if (cfg->vcmd_detect_en) { + char *mn_name = esp_srmodel_filter(cfg->models, ESP_MN_PREFIX, cfg->mn_language); + ESP_GMF_NULL_CHECK(TAG, mn_name, return ESP_GMF_JOB_ERR_FAIL); + gmf_afe->multinet = esp_mn_handle_from_name(mn_name); + + if (cfg->vcmd_timeout == 0) { + ESP_LOGW(TAG, "Voice command timeout configured as 0, reset to default: 5760 ms"); + cfg->vcmd_timeout = 5760; + } + gmf_afe->mn_handle = gmf_afe->multinet->create(mn_name, cfg->vcmd_timeout); + ESP_GMF_NULL_CHECK(TAG, gmf_afe->mn_handle, return ESP_GMF_JOB_ERR_FAIL); + gmf_afe->mn_lock = xSemaphoreCreateMutex(); + ESP_GMF_NULL_CHECK(TAG, gmf_afe->mn_lock, return ESP_GMF_JOB_ERR_FAIL); + gmf_afe->mn_state = ESP_MN_STATE_TIMEOUT; + + esp_mn_commands_update_from_sdkconfig((esp_mn_iface_t *)gmf_afe->multinet, gmf_afe->mn_handle); + } + esp_gmf_db_new_ringbuf(2, buf_size, &gmf_afe->in_db); + esp_gmf_db_new_ringbuf(1, buf_size * 2 + (cfg->delay_samples * sizeof(uint16_t)), &gmf_afe->out_db); + esp_gmf_afe_manager_set_result_cb(cfg->afe_manager, esp_gmf_afe_result_proc, self); + esp_gmf_afe_manager_set_read_cb(cfg->afe_manager, esp_gmf_afe_read_cb, self); + if (gmf_afe->msg_queue == NULL) { + gmf_afe->msg_queue = xQueueCreate(4, sizeof(afe_evt_msg_t)); + ESP_GMF_NULL_CHECK(TAG, gmf_afe->msg_queue, goto __failed); + } + esp_gmf_info_sound_t snd_info = {0}; + snd_info.sample_rates = 16000; + snd_info.bits = 16; + snd_info.channels = 1; + esp_gmf_element_notify_snd_info(self, &snd_info); + return ESP_GMF_JOB_ERR_OK; + +__failed: + esp_gmf_obj_delete(self); + return ESP_GMF_JOB_ERR_FAIL; +} + +static esp_gmf_job_err_t esp_gmf_afe_close(esp_gmf_audio_element_handle_t self, void *para) +{ + esp_gmf_afe_t *gmf_afe = (esp_gmf_afe_t *)self; + esp_gmf_afe_cfg_t *cfg = OBJ_GET_CFG(self); + if (cfg->vcmd_detect_en) { + vcmd_det_cancel(gmf_afe); + } + esp_gmf_afe_manager_set_read_cb(cfg->afe_manager, NULL, NULL); + esp_gmf_afe_manager_set_result_cb(cfg->afe_manager, NULL, NULL); + + if (gmf_afe->in_db) { + esp_gmf_db_deinit(gmf_afe->in_db); + gmf_afe->in_db = NULL; + } + if (gmf_afe->out_db) { + esp_gmf_db_deinit(gmf_afe->out_db); + gmf_afe->out_db = NULL; + } + if (gmf_afe->wake_st_lock) { + vSemaphoreDelete(gmf_afe->wake_st_lock); + gmf_afe->wake_st_lock = NULL; + } + if (gmf_afe->wakeup_timer) { + esp_timer_stop(gmf_afe->wakeup_timer); + esp_timer_delete(gmf_afe->wakeup_timer); + gmf_afe->wakeup_timer = NULL; + } + if (gmf_afe->mn_lock) { + vSemaphoreDelete(gmf_afe->mn_lock); + gmf_afe->mn_lock = NULL; + } + if (gmf_afe->mn_handle) { + gmf_afe->multinet->destroy(gmf_afe->mn_handle); + gmf_afe->mn_handle = NULL; + } + if (gmf_afe->msg_queue) { + vQueueDelete(gmf_afe->msg_queue); + gmf_afe->msg_queue = NULL; + } + return ESP_GMF_JOB_ERR_OK; +} + +static void esp_gmf_afe_cmd_handler(esp_gmf_audio_element_handle_t self) +{ + esp_gmf_afe_t *gmf_afe = (esp_gmf_afe_t *)self; + if (gmf_afe->msg_queue) { + afe_evt_msg_t msg; + while (xQueueReceive(gmf_afe->msg_queue, &msg, 0) == pdTRUE) { + switch (msg.event) { + case ET_KEEP_WAKE_MODIFIED: { + xSemaphoreTake(gmf_afe->wake_st_lock, portMAX_DELAY); + gmf_afe->keep_wake = msg.payload.keep_awake.enable; + xSemaphoreGive(gmf_afe->wake_st_lock); + wakeup_state_update(gmf_afe, msg.event, NULL, 0); + break; + } + case ET_TRIGGER_WAKEUP: + case ET_TRIGGER_SLEEP: { + wakeup_state_update(gmf_afe, msg.event, NULL, 0); + break; + } + default: + break; + } + } + } +} + +static esp_gmf_job_err_t esp_gmf_afe_proc(esp_gmf_audio_element_handle_t self, void *para) +{ + int ret = 0; + esp_gmf_port_handle_t in_port = ESP_GMF_ELEMENT_GET(self)->in; + esp_gmf_port_handle_t out_port = ESP_GMF_ELEMENT_GET(self)->out; + esp_gmf_payload_t *in_load = NULL; + esp_gmf_payload_t *out_load = NULL; + esp_gmf_afe_t *gmf_afe = (esp_gmf_afe_t *)self; + esp_gmf_afe_cfg_t *cfg = OBJ_GET_CFG(self); + + esp_gmf_afe_cmd_handler(self); + + ret = esp_gmf_port_acquire_in(in_port, &in_load, ESP_GMF_ELEMENT_GET(self)->in_attr.data_size, ESP_GMF_MAX_DELAY); + if (ret < 0) { + ESP_LOGE(TAG, "Read data error, ret:%d, line:%d", ret, __LINE__); + return ret == ESP_GMF_IO_ABORT ? ESP_GMF_JOB_ERR_OK : ESP_GMF_JOB_ERR_FAIL; + } + + esp_gmf_data_bus_block_t blk = {0}; + esp_gmf_db_acquire_write(gmf_afe->in_db, &blk, in_load->valid_size, ESP_GMF_MAX_DELAY); + blk.buf = in_load->buf; + blk.valid_size = in_load->valid_size; + esp_gmf_db_release_write(gmf_afe->in_db, &blk, ESP_GMF_MAX_DELAY); + + uint32_t filled_size = 0; + esp_gmf_db_get_filled_size(gmf_afe->out_db, &filled_size); + + if (filled_size > (cfg->delay_samples * sizeof(uint16_t))) { + uint32_t rsize = filled_size - (cfg->delay_samples * sizeof(uint16_t)); + esp_gmf_port_acquire_out(out_port, &out_load, rsize, ESP_GMF_MAX_DELAY); + esp_gmf_data_bus_block_t blk = {0}; + blk.buf = out_load->buf; + blk.buf_length = rsize; + esp_gmf_db_acquire_read(gmf_afe->out_db, &blk, rsize, ESP_GMF_MAX_DELAY); + esp_gmf_db_release_read(gmf_afe->out_db, &blk, ESP_GMF_MAX_DELAY); + out_load->valid_size = rsize; + esp_gmf_port_release_out(out_port, out_load, ESP_GMF_MAX_DELAY); + ret = ESP_GMF_JOB_ERR_OK; + } else { + ret = ESP_GMF_JOB_ERR_CONTINUE; + } + if (in_load->is_done) { + ret = ESP_GMF_JOB_ERR_DONE; + } + esp_gmf_port_release_in(in_port, in_load, ESP_GMF_MAX_DELAY); + return ret; +} + +static esp_gmf_err_t _load_afe_caps_func(esp_gmf_element_handle_t handle) +{ + esp_gmf_cap_t *caps = NULL; + esp_gmf_cap_t afe_caps = {0}; + afe_caps.cap_eightcc = ESP_GMF_CAPS_AUDIO_AEC; + afe_caps.attr_fun = NULL; + int ret = esp_gmf_cap_append(&caps, &afe_caps); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, { return ret;}, "Failed to create AEC capability"); + afe_caps.cap_eightcc = ESP_GMF_CAPS_AUDIO_AGC; + ret = esp_gmf_cap_append(&caps, &afe_caps); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, { return ret;}, "Failed to create AGC capability"); + afe_caps.cap_eightcc = ESP_GMF_CAPS_AUDIO_NS; + afe_caps.attr_fun = NULL; + ret = esp_gmf_cap_append(&caps, &afe_caps); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, { return ret;}, "Failed to create NS capability"); + afe_caps.cap_eightcc = ESP_GMF_CAPS_AUDIO_VAD; + afe_caps.attr_fun = NULL; + ret = esp_gmf_cap_append(&caps, &afe_caps); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, { return ret;}, "Failed to create VAD capability"); + afe_caps.cap_eightcc = ESP_GMF_CAPS_AUDIO_WWE; + afe_caps.attr_fun = NULL; + ret = esp_gmf_cap_append(&caps, &afe_caps); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, { return ret;}, "Failed to create WWE capability"); + afe_caps.cap_eightcc = ESP_GMF_CAPS_AUDIO_VCMD; + afe_caps.attr_fun = NULL; + ret = esp_gmf_cap_append(&caps, &afe_caps); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, { return ret;}, "Failed to create VCMD capability"); + + esp_gmf_element_t *el = (esp_gmf_element_t *)handle; + el->caps = caps; + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t __afe_set_vcmd_det(esp_gmf_audio_element_handle_t handle, esp_gmf_args_desc_t *arg_desc, + uint8_t *buf, int buf_len) +{ + ESP_GMF_NULL_CHECK(TAG, handle, {return ESP_GMF_ERR_INVALID_ARG;}); + ESP_GMF_NULL_CHECK(TAG, arg_desc, {return ESP_GMF_ERR_INVALID_ARG;}); + ESP_GMF_NULL_CHECK(TAG, buf, {return ESP_GMF_ERR_INVALID_ARG;}); + + esp_gmf_afe_t *gmf_afe = (esp_gmf_afe_t *)handle; + esp_gmf_afe_cfg_t *cfg = OBJ_GET_CFG(gmf_afe); + uint8_t vcmd_det_start = buf[0]; + if (cfg->vcmd_detect_en) { + if (vcmd_det_start) { + vcmd_det_begin(gmf_afe); + } else { + vcmd_det_cancel(gmf_afe); + } + return ESP_GMF_ERR_OK; + } + return ESP_GMF_ERR_INVALID_STATE; +} + +static esp_gmf_err_t _load_afe_methods_func(esp_gmf_element_handle_t handle) +{ + esp_gmf_method_t *method = NULL; + esp_gmf_args_desc_t *set_args = NULL; + esp_gmf_err_t ret = esp_gmf_args_desc_append(&set_args, ESP_GMF_METHOD_AFE_START_VCMD_DET_ARG_EN, + ESP_GMF_ARGS_TYPE_UINT8, sizeof(uint8_t), 0); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to append vcmd det argument"); + ret = esp_gmf_method_append(&method, ESP_GMF_METHOD_AFE_START_VCMD_DET, __afe_set_vcmd_det, set_args); + ESP_GMF_RET_ON_ERROR(TAG, ret, {return ret;}, "Failed to register %s method", ESP_GMF_METHOD_AFE_START_VCMD_DET); + + esp_gmf_element_t *el = (esp_gmf_element_t *)handle; + el->method = method; + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_afe_init(void *config, esp_gmf_obj_handle_t *handle) +{ + ESP_GMF_MEM_CHECK(TAG, config, { return ESP_GMF_ERR_INVALID_ARG;}); + ESP_GMF_MEM_CHECK(TAG, handle, { return ESP_GMF_ERR_INVALID_ARG;}); + ESP_GMF_NULL_CHECK(TAG, ((esp_gmf_afe_cfg_t *)config)->afe_manager, { return ESP_GMF_ERR_INVALID_ARG;}) + + esp_gmf_afe_t *gmf_afe = esp_gmf_oal_calloc(1, sizeof(esp_gmf_afe_t)); + ESP_GMF_MEM_CHECK(TAG, gmf_afe, { return ESP_GMF_ERR_MEMORY_LACK;}); + esp_gmf_obj_t *obj = (esp_gmf_obj_t *)gmf_afe; + obj->new_obj = esp_gmf_afe_new; + obj->del_obj = esp_gmf_afe_destroy; + + esp_gmf_afe_cfg_t *obj_cfg = esp_gmf_oal_calloc(1, sizeof(esp_gmf_afe_cfg_t)); + memcpy(obj_cfg, config, sizeof(esp_gmf_afe_cfg_t)); + esp_gmf_obj_set_config(obj, obj_cfg, sizeof(esp_gmf_afe_cfg_t)); + int ret = esp_gmf_obj_set_tag(obj, "ai_afe"); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto __failed, "Failed set OBJ tag"); + + esp_gmf_element_cfg_t el_cfg = {0}; + ESP_GMF_ELEMENT_IN_PORT_ATTR_SET(el_cfg.in_attr, ESP_GMF_EL_PORT_CAP_SINGLE, 16, 0, + ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, AFE_DEFAULT_DATA_SIZE); + ESP_GMF_ELEMENT_OUT_PORT_ATTR_SET(el_cfg.out_attr, ESP_GMF_EL_PORT_CAP_SINGLE, 16, 0, + ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, AFE_DEFAULT_DATA_SIZE); + el_cfg.dependency = false; + ret = esp_gmf_audio_el_init(gmf_afe, &el_cfg); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto __failed, "Failed to initialize audio element"); + gmf_afe->last_event = ET_UNKNOWN; + ESP_GMF_ELEMENT_GET(gmf_afe)->ops.open = esp_gmf_afe_open; + ESP_GMF_ELEMENT_GET(gmf_afe)->ops.process = esp_gmf_afe_proc; + ESP_GMF_ELEMENT_GET(gmf_afe)->ops.close = esp_gmf_afe_close; + ESP_GMF_ELEMENT_GET(gmf_afe)->ops.load_caps = _load_afe_caps_func; + ESP_GMF_ELEMENT_GET(gmf_afe)->ops.load_methods = _load_afe_methods_func; + + *handle = obj; + ESP_LOGI(TAG, "Create AFE, %s-%p", OBJ_GET_TAG(obj), obj); + + return ESP_GMF_ERR_OK; + +__failed: + esp_gmf_afe_destroy(gmf_afe); + return ESP_GMF_ERR_FAIL; +} + +static esp_gmf_err_t esp_gmf_afe_set_vcmd_detection(esp_gmf_element_handle_t handle, bool enable) +{ + ESP_GMF_NULL_CHECK(TAG, handle, {return ESP_GMF_ERR_INVALID_ARG;}); + const esp_gmf_method_t *method_head = NULL; + const esp_gmf_method_t *method = NULL; + esp_gmf_element_get_method((esp_gmf_element_handle_t)handle, &method_head); + esp_gmf_method_found(method_head, ESP_GMF_METHOD_AFE_START_VCMD_DET, &method); + uint8_t buf[1] = { enable }; + esp_gmf_args_set_value(method->args_desc, ESP_GMF_METHOD_AFE_START_VCMD_DET_ARG_EN, buf, buf, sizeof(uint8_t)); + return esp_gmf_element_exe_method((esp_gmf_element_handle_t)handle, ESP_GMF_METHOD_AFE_START_VCMD_DET, buf, sizeof(buf)); +} + +esp_gmf_err_t esp_gmf_afe_vcmd_detection_begin(esp_gmf_element_handle_t handle) +{ + return esp_gmf_afe_set_vcmd_detection(handle, true); +} + +esp_gmf_err_t esp_gmf_afe_vcmd_detection_cancel(esp_gmf_element_handle_t handle) +{ + return esp_gmf_afe_set_vcmd_detection(handle, false); +} + +esp_gmf_err_t esp_gmf_afe_set_event_cb(esp_gmf_element_handle_t handle, esp_gmf_afe_event_cb_t cb, void *ctx) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG;); + esp_gmf_afe_cfg_t *cfg = OBJ_GET_CFG(handle); + ESP_GMF_NULL_CHECK(TAG, cfg, return ESP_GMF_ERR_INVALID_STATE;) + cfg->event_cb = cb; + cfg->event_ctx = ctx; + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_afe_keep_awake(esp_gmf_element_handle_t handle, bool enable) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG;); + esp_gmf_afe_t *gmf_afe = (esp_gmf_afe_t *)handle; + if (gmf_afe->msg_queue == NULL) { + ESP_LOGE(TAG, "GMF AFE Message queue not initialized"); + return ESP_GMF_ERR_INVALID_STATE; + } + afe_evt_msg_t msg = { 0 }; + msg.event = ET_KEEP_WAKE_MODIFIED; + msg.payload.keep_awake.enable = enable; + BaseType_t ok = xQueueSend(gmf_afe->msg_queue, &msg, pdMS_TO_TICKS(1000)); + return ok == pdTRUE ? ESP_GMF_ERR_OK : ESP_GMF_ERR_TIMEOUT; +} + +esp_gmf_err_t esp_gmf_afe_trigger_wakeup(esp_gmf_element_handle_t handle) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG;); + esp_gmf_afe_t *gmf_afe = (esp_gmf_afe_t *)handle; + if (gmf_afe->msg_queue == NULL) { + ESP_LOGE(TAG, "GMF AFE Message queue not initialized"); + return ESP_GMF_ERR_INVALID_STATE; + } + afe_evt_msg_t msg = { 0 }; + msg.event = ET_TRIGGER_WAKEUP; + msg.payload.trigger.state = true; + BaseType_t ok = xQueueSend(gmf_afe->msg_queue, &msg, pdMS_TO_TICKS(1000)); + return ok == pdTRUE ? ESP_GMF_ERR_OK : ESP_GMF_ERR_TIMEOUT; +} + +esp_gmf_err_t esp_gmf_afe_trigger_sleep(esp_gmf_element_handle_t handle) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG;); + esp_gmf_afe_t *gmf_afe = (esp_gmf_afe_t *)handle; + if (gmf_afe->msg_queue == NULL) { + ESP_LOGE(TAG, "GMF AFE Message queue not initialized"); + return ESP_GMF_ERR_INVALID_STATE; + } + afe_evt_msg_t msg = { 0 }; + msg.event = ET_TRIGGER_SLEEP; + msg.payload.trigger.state = false; + BaseType_t ok = xQueueSend(gmf_afe->msg_queue, &msg, pdMS_TO_TICKS(1000)); + return ok == pdTRUE ? ESP_GMF_ERR_OK : ESP_GMF_ERR_TIMEOUT; +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/src/esp_gmf_afe_manager.c b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/src/esp_gmf_afe_manager.c new file mode 100644 index 0000000..d35696c --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/src/esp_gmf_afe_manager.c @@ -0,0 +1,372 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/event_groups.h" +#include "freertos/portmacro.h" +#include "freertos/task.h" +#include "freertos/idf_additions.h" +#include "freertos/semphr.h" +#include "esp_private/freertos_idf_additions_priv.h" +#include "esp_afe_sr_models.h" +#include "esp_check.h" +#include "esp_err.h" +#include "esp_heap_caps.h" +#include "esp_gmf_afe_manager.h" + +#define AFE_RUN_EVENT (BIT0) +#define AFE_DESTROYING_EVENT (BIT1) +#define FEED_TASK_DESTROYED (BIT2) +#define FETCH_TASK_DESTROYED (BIT3) + +/** + * @brief Information about an AFE (Audio Front-End) task. + */ +typedef struct { + TaskHandle_t task; /*!< Task handle */ + bool running; /*!< Task running status */ +} afe_task_info_t; + +/** + * @brief AFE (Audio Front-End) manager. + */ +typedef struct __afe { + afe_task_info_t feed; /*!< Feed task information */ + afe_task_info_t fetch; /*!< Fetch task information */ + esp_gmf_afe_manager_read_cb_t read_cb; /*!< Read callback function */ + void *read_ctx; /*!< Read callback context */ + esp_afe_sr_data_t *afe_data; /*!< AFE data */ + esp_gmf_afe_manager_result_cb_t result_proc; /*!< Result processing callback function */ + void *result_ctx; /*!< Result processing callback context */ + esp_gmf_afe_manager_features_t feat; /*!< AFE feature state */ + EventGroupHandle_t ctrl_events; /*!< Control events */ + SemaphoreHandle_t read_cb_lock; /*!< Mutex for read callback */ + SemaphoreHandle_t result_cb_lock;/*!< Mutex for result callback */ + esp_afe_sr_iface_t *esp_afe; /*!< AFE interface */ +} esp_gmf_afe_manager_t; + +static const char *TAG = "AFE_MANAGER"; + +static void feed_task(void *arg) +{ + esp_gmf_afe_manager_t *afe_manager = (esp_gmf_afe_manager_t *)arg; + int chan_num = afe_manager->esp_afe->get_feed_channel_num(afe_manager->afe_data); + int chunksize = afe_manager->esp_afe->get_feed_chunksize(afe_manager->afe_data); + const int buf_size = chunksize * chan_num * sizeof(uint16_t); + ESP_LOGI(TAG, "Feed task, ch %d, chunk %d, buf size %d", chan_num, chunksize, buf_size); + int16_t *buf = heap_caps_calloc_prefer(1, buf_size, 2, MALLOC_CAP_SPIRAM, MALLOC_CAP_INTERNAL); + if (buf == NULL) { + ESP_LOGE(TAG, "Feed task calloc failed, task quit"); + goto __quit; + } + afe_manager->feed.running = true; + + while (afe_manager->feed.running) { + EventBits_t bits = xEventGroupWaitBits(afe_manager->ctrl_events, AFE_RUN_EVENT | AFE_DESTROYING_EVENT, false, false, portMAX_DELAY); + if (bits & AFE_DESTROYING_EVENT) { + break; + } + int rlen = 0; + xSemaphoreTake(afe_manager->read_cb_lock, portMAX_DELAY); + if (afe_manager->read_cb) { + rlen = afe_manager->read_cb(buf, buf_size, afe_manager->read_ctx, pdMS_TO_TICKS(500)); + } + xSemaphoreGive(afe_manager->read_cb_lock); + if (rlen == buf_size) { + afe_manager->esp_afe->feed(afe_manager->afe_data, buf); + } else { + ESP_LOGE(TAG, "AFE read failed %d", rlen); + } + } + +__quit: + if (buf) { + heap_caps_free(buf); + } + xEventGroupSetBits(afe_manager->ctrl_events, FEED_TASK_DESTROYED); + vTaskDelete(NULL); +} + +static void fetch_task(void *arg) +{ + esp_gmf_afe_manager_t *afe_manager = (esp_gmf_afe_manager_t *)arg; + afe_manager->fetch.running = true; + + while (afe_manager->fetch.running) { + EventBits_t bits = xEventGroupWaitBits(afe_manager->ctrl_events, AFE_RUN_EVENT | AFE_DESTROYING_EVENT, false, false, portMAX_DELAY); + if (bits & AFE_DESTROYING_EVENT) { + break; + } + afe_fetch_result_t *result = afe_manager->esp_afe->fetch(afe_manager->afe_data); + xSemaphoreTake(afe_manager->result_cb_lock, portMAX_DELAY); + if (afe_manager->result_proc != NULL) { + afe_manager->result_proc(result, afe_manager->result_ctx); + } + xSemaphoreGive(afe_manager->result_cb_lock); + } + xEventGroupSetBits(afe_manager->ctrl_events, FETCH_TASK_DESTROYED); + vTaskDelete(NULL); +} + +esp_gmf_err_t esp_gmf_afe_manager_set_result_cb(esp_gmf_afe_manager_handle_t handle, esp_gmf_afe_manager_result_cb_t proc, void *ctx) +{ + esp_gmf_afe_manager_t *afe_manager = (esp_gmf_afe_manager_t *)handle; + ESP_RETURN_ON_FALSE(afe_manager, ESP_GMF_ERR_INVALID_ARG, TAG, "AFE set result cb invalid handle"); + xSemaphoreTake(afe_manager->result_cb_lock, portMAX_DELAY); + afe_manager->result_proc = proc; + afe_manager->result_ctx = ctx; + xSemaphoreGive(afe_manager->result_cb_lock); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_afe_manager_destroy(esp_gmf_afe_manager_handle_t handle) +{ + esp_gmf_afe_manager_t *afe_manager = (esp_gmf_afe_manager_t *)handle; + ESP_RETURN_ON_FALSE(afe_manager, ESP_GMF_ERR_INVALID_ARG, TAG, "AFE destroy: handle NULL"); + + xEventGroupSetBits(afe_manager->ctrl_events, AFE_DESTROYING_EVENT); + EventBits_t wait_bits = 0; + if (afe_manager->feed.task) { + afe_manager->feed.running = false; + wait_bits |= FEED_TASK_DESTROYED; + } + if (afe_manager->fetch.task) { + afe_manager->fetch.running = false; + wait_bits |= FETCH_TASK_DESTROYED; + } + EventBits_t bits = xEventGroupWaitBits(afe_manager->ctrl_events, wait_bits, true, true, portMAX_DELAY); + if ((bits & wait_bits) != wait_bits) { + ESP_LOGE(TAG, "AFE destroy wait bits timeout %" PRIu32, bits); + return ESP_GMF_ERR_TIMEOUT; + } + if (afe_manager->ctrl_events) { + vEventGroupDelete(afe_manager->ctrl_events); + } + if (afe_manager->read_cb_lock) { + vSemaphoreDelete(afe_manager->read_cb_lock); + } + if (afe_manager->result_cb_lock) { + vSemaphoreDelete(afe_manager->result_cb_lock); + } + if (afe_manager->afe_data) { + afe_manager->esp_afe->destroy(afe_manager->afe_data); + afe_manager->afe_data = NULL; + } + heap_caps_free(afe_manager); + ESP_LOGI(TAG, "AFE manager destroy"); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_afe_manager_create(esp_gmf_afe_manager_cfg_t *cfg, esp_gmf_afe_manager_handle_t *handle) +{ + esp_gmf_err_t ret = ESP_GMF_ERR_OK; + *handle = NULL; + + ESP_RETURN_ON_FALSE(cfg != NULL, ESP_GMF_ERR_INVALID_ARG, TAG, "AFE manager create: cfg NULL"); + ESP_RETURN_ON_FALSE(handle != NULL, ESP_GMF_ERR_INVALID_ARG, TAG, "AFE manager create: handle NULL"); + ESP_RETURN_ON_FALSE(cfg->afe_cfg != NULL, ESP_GMF_ERR_INVALID_ARG, TAG, "AFE manager create: cfg->afe_cfg NULL"); + + esp_gmf_afe_manager_t *afe_manager = heap_caps_calloc_prefer(1, sizeof(esp_gmf_afe_manager_t), 2, MALLOC_CAP_SPIRAM, MALLOC_CAP_INTERNAL); + ESP_GOTO_ON_FALSE(afe_manager, ESP_GMF_ERR_MEMORY_LACK, __err, TAG, "AFE manager create no memory"); + afe_manager->read_cb = cfg->read_cb; + afe_manager->read_ctx = cfg->read_ctx; + afe_config_print(cfg->afe_cfg); + afe_manager->esp_afe = esp_afe_handle_from_config(cfg->afe_cfg); + afe_manager->afe_data = afe_manager->esp_afe->create_from_config(cfg->afe_cfg); + ESP_GOTO_ON_FALSE(afe_manager->afe_data, ESP_GMF_ERR_FAIL, __err, TAG, "AFE manager create failed"); + + afe_manager->ctrl_events = xEventGroupCreate(); + ESP_GOTO_ON_FALSE(afe_manager->ctrl_events, ESP_GMF_ERR_MEMORY_LACK, __err, TAG, "AFE manager create events failed"); + + afe_manager->read_cb_lock = xSemaphoreCreateMutex(); + ESP_GOTO_ON_FALSE(afe_manager->read_cb_lock, ESP_GMF_ERR_MEMORY_LACK, __err, TAG, "AFE manager create mutex failed"); + afe_manager->result_cb_lock = xSemaphoreCreateMutex(); + ESP_GOTO_ON_FALSE(afe_manager->result_cb_lock, ESP_GMF_ERR_MEMORY_LACK, __err, TAG, "AFE manager create mutex failed"); + + if (afe_manager->read_cb) { + xEventGroupSetBits(afe_manager->ctrl_events, AFE_RUN_EVENT); + } + +#if (configSUPPORT_STATIC_ALLOCATION == 1) && defined(CONFIG_SPIRAM_BOOT_INIT) + prvTaskCreateDynamicPinnedToCoreWithCaps(feed_task, + "afe_feed", + cfg->feed_task_setting.stack_size, + afe_manager, + cfg->feed_task_setting.prio, + cfg->feed_task_setting.core, + (MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT), + &afe_manager->feed.task); + ESP_GOTO_ON_FALSE(afe_manager->feed.task, ESP_GMF_ERR_MEMORY_LACK, __err, TAG, "Create afe feed task failed"); + + prvTaskCreateDynamicPinnedToCoreWithCaps(fetch_task, + "afe_fetch", + cfg->fetch_task_setting.stack_size, + afe_manager, + cfg->fetch_task_setting.prio, + cfg->fetch_task_setting.core, + (MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT), + &afe_manager->fetch.task); + ESP_GOTO_ON_FALSE(afe_manager->fetch.task, ESP_GMF_ERR_MEMORY_LACK, __err, TAG, "Create afe fetch task failed"); +#else + ESP_LOGW(TAG, "The AFE Manager may not work when PSRAM is disabled"); + + xTaskCreatePinnedToCore(feed_task, + "afe_feed", + cfg->feed_task_setting.stack_size, + afe_manager, + cfg->feed_task_setting.prio, + &afe_manager->feed.task, + cfg->feed_task_setting.core); + ESP_GOTO_ON_FALSE(afe_manager->feed.task, ESP_GMF_ERR_MEMORY_LACK, __err, TAG, "Create afe feed task failed"); + + xTaskCreatePinnedToCore(fetch_task, + "afe_fetch", + cfg->fetch_task_setting.stack_size, + afe_manager, + cfg->fetch_task_setting.prio, + &afe_manager->fetch.task, + cfg->fetch_task_setting.core); + ESP_GOTO_ON_FALSE(afe_manager->fetch.task, ESP_GMF_ERR_MEMORY_LACK, __err, TAG, "Create afe fetch task failed"); +#endif /* (configSUPPORT_STATIC_ALLOCATION == 1) */ + + afe_manager->feat.wakeup = cfg->afe_cfg->wakenet_init; + afe_manager->feat.aec = cfg->afe_cfg->aec_init; + afe_manager->feat.se = cfg->afe_cfg->se_init; + afe_manager->feat.vad = (cfg->afe_cfg->vad_init); + + *handle = afe_manager; + return ret; +__err: + if (afe_manager) { + esp_gmf_afe_manager_destroy(afe_manager); + } + return ret; +} + +esp_gmf_err_t esp_gmf_afe_manager_get_features(esp_gmf_afe_manager_handle_t handle, esp_gmf_afe_manager_features_t *features) +{ + ESP_RETURN_ON_FALSE(handle, ESP_GMF_ERR_INVALID_ARG, TAG, "AFE get feature: handle NULL"); + ESP_RETURN_ON_FALSE(features, ESP_GMF_ERR_INVALID_ARG, TAG, "AFE get feature: features NULL"); + esp_gmf_afe_manager_t *afe_manager = (esp_gmf_afe_manager_t *)handle; + memcpy(features, &(afe_manager->feat), sizeof(esp_gmf_afe_manager_features_t)); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_afe_manager_suspend(esp_gmf_afe_manager_handle_t handle, bool suspend) +{ + esp_gmf_afe_manager_t *afe_manager = (esp_gmf_afe_manager_t *)handle; + ESP_RETURN_ON_FALSE(afe_manager, ESP_GMF_ERR_INVALID_ARG, TAG, "AFE suspend: handle NULL"); + ESP_LOGI(TAG, "AFE manager suspend %d", suspend); + if (suspend) { + xEventGroupClearBits(afe_manager->ctrl_events, AFE_RUN_EVENT); + } else { + xEventGroupSetBits(afe_manager->ctrl_events, AFE_RUN_EVENT); + } + + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_afe_manager_enable_features(esp_gmf_afe_manager_handle_t handle, esp_gmf_afe_feature_t feature, bool enable) +{ + esp_gmf_afe_manager_t *afe_manager = (esp_gmf_afe_manager_t *)handle; + ESP_RETURN_ON_FALSE(afe_manager, ESP_GMF_ERR_INVALID_ARG, TAG, "AFE suspend: handle NULL"); + esp_gmf_err_t ret = ESP_GMF_ERR_OK; + ESP_LOGD(TAG, "AFE Ctrl [%u, %d]", feature, enable); + switch (feature) { + case ESP_AFE_FEATURE_WAKENET: { + if (enable) { + ret = afe_manager->esp_afe->enable_wakenet(afe_manager->afe_data); + } else { + ret = afe_manager->esp_afe->disable_wakenet(afe_manager->afe_data); + } + ESP_LOGD(TAG, "Wakenet ctrl ret %d", ret); + if (ret >= 0) { + afe_manager->feat.wakeup = ret; + } + break; + } + case ESP_AFE_FEATURE_AEC: { + if (enable) { + ret = afe_manager->esp_afe->enable_aec(afe_manager->afe_data); + } else { + ret = afe_manager->esp_afe->disable_aec(afe_manager->afe_data); + } + ESP_LOGD(TAG, "AEC ctrl ret %d", ret); + if (ret >= 0) { + afe_manager->feat.aec = ret; + } + break; + } + case ESP_AFE_FEATURE_SE: { + if (enable) { + ret = afe_manager->esp_afe->enable_se(afe_manager->afe_data); + } else { + ret = afe_manager->esp_afe->disable_se(afe_manager->afe_data); + } + ESP_LOGD(TAG, "AE ctrl ret %d", ret); + if (ret >= 0) { + afe_manager->feat.se = ret; + } + break; + } + case ESP_AFE_FEATURE_VAD: { + if (enable) { + ret = afe_manager->esp_afe->enable_vad(afe_manager->afe_data); + } else { + ret = afe_manager->esp_afe->disable_vad(afe_manager->afe_data); + } + ESP_LOGD(TAG, "VAD ctrl ret %d", ret); + if (ret >= 0) { + afe_manager->feat.vad = ret; + } + ret = afe_manager->esp_afe->reset_vad(afe_manager->afe_data); + break; + } + default: + ESP_LOGW(TAG, "AFE manager feature ctrl: %u not support", feature); + ret = ESP_GMF_ERR_INVALID_ARG; + break; + } + + return ret; +} + +esp_gmf_err_t esp_gmf_afe_manager_set_read_cb(esp_gmf_afe_manager_handle_t handle, esp_gmf_afe_manager_read_cb_t read_cb, void *read_ctx) +{ + ESP_RETURN_ON_FALSE(handle, ESP_GMF_ERR_INVALID_ARG, TAG, "AFE Manager set read: handle NULL"); + esp_gmf_afe_manager_t *afe_manager = (esp_gmf_afe_manager_t *)handle; + esp_gmf_afe_manager_suspend(afe_manager, true); + xSemaphoreTake(afe_manager->read_cb_lock, portMAX_DELAY); + afe_manager->read_cb = read_cb; + afe_manager->read_ctx = read_ctx; + if (afe_manager->read_cb) { + esp_gmf_afe_manager_suspend(afe_manager, false); + } + xSemaphoreGive(afe_manager->read_cb_lock); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_afe_manager_get_chunk_size(esp_gmf_afe_manager_handle_t handle, size_t *size) +{ + ESP_RETURN_ON_FALSE(handle, ESP_GMF_ERR_INVALID_ARG, TAG, "AFE Manager get feed size: handle NULL"); + ESP_RETURN_ON_FALSE(size, ESP_GMF_ERR_INVALID_ARG, TAG, "AFE Manager get feed size: size NULL"); + esp_gmf_afe_manager_t *afe_manager = (esp_gmf_afe_manager_t *)handle; + *size = afe_manager->esp_afe->get_feed_chunksize(afe_manager->afe_data); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_afe_manager_get_input_ch_num(esp_gmf_afe_manager_handle_t handle, uint8_t *ch_num) +{ + ESP_RETURN_ON_FALSE(handle, ESP_GMF_ERR_INVALID_ARG, TAG, "AFE Manager get feed size: handle NULL"); + ESP_RETURN_ON_FALSE(ch_num, ESP_GMF_ERR_INVALID_ARG, TAG, "AFE Manager get feed size: ch_num NULL"); + esp_gmf_afe_manager_t *afe_manager = (esp_gmf_afe_manager_t *)handle; + *ch_num = afe_manager->esp_afe->get_feed_channel_num(afe_manager->afe_data); + return ESP_GMF_ERR_OK; +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/src/esp_gmf_wn.c b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/src/esp_gmf_wn.c new file mode 100644 index 0000000..64416a9 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_ai_audio/src/esp_gmf_wn.c @@ -0,0 +1,287 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#include +#include +#include + +#include "esp_log.h" +#include "esp_types.h" +#include "esp_gmf_audio_element.h" +#include "esp_gmf_cache.h" +#include "esp_gmf_cap.h" +#include "esp_gmf_caps_def.h" +#include "esp_gmf_err.h" +#include "esp_gmf_job.h" +#include "esp_gmf_node.h" +#include "esp_gmf_oal_mem.h" +#include "esp_gmf_payload.h" + +#include "esp_wn_models.h" +#include "esp_gmf_wn.h" +#include "esp_gmf_ch_sort.h" + +#define ESP_WN_BYTES_PER_SAMPLE (sizeof(uint16_t)) + +typedef struct { + esp_gmf_audio_element_t parent; + esp_wn_iface_t *wn_iface; + model_iface_data_t *model_data; + uint32_t sample_rate; + uint32_t ch; + uint32_t chunk_size; + uint32_t frame_len; + esp_gmf_cache_t *cache; + esp_gmf_payload_t *in_load; +} gmf_wn_t; + +static const char *TAG = "GMF_WN"; + +static esp_gmf_err_t gmf_wn_new(void *cfg, esp_gmf_obj_handle_t *handle) +{ + ESP_GMF_NULL_CHECK(TAG, cfg, { return ESP_GMF_ERR_INVALID_ARG; }); + ESP_GMF_NULL_CHECK(TAG, handle, { return ESP_GMF_ERR_INVALID_ARG; }); + *handle = NULL; + return esp_gmf_wn_init((esp_gmf_wn_cfg_t *)cfg, (esp_gmf_element_handle_t *)handle); +} + +static esp_gmf_err_t gmf_wn_received_event_handler(esp_gmf_event_pkt_t *evt, void *ctx) +{ + ESP_GMF_NULL_CHECK(TAG, evt, { return ESP_GMF_ERR_INVALID_ARG; }); + ESP_GMF_NULL_CHECK(TAG, ctx, { return ESP_GMF_ERR_INVALID_ARG; }); + esp_gmf_element_handle_t self = (esp_gmf_element_handle_t)ctx; + esp_gmf_element_handle_t el = evt->from; + esp_gmf_event_state_t state = ESP_GMF_EVENT_STATE_NONE; + esp_gmf_element_get_state(self, &state); + if (evt->sub == ESP_GMF_INFO_SOUND) { + esp_gmf_info_sound_t info = {0}; + memcpy(&info, evt->payload, evt->payload_size); + ESP_LOGD(TAG, "RECV info, from: %s-%p, next: %p, self: %s-%p, type: %x, state: %s, rate: %d, ch: %d, bits: %d", + OBJ_GET_TAG(el), el, esp_gmf_node_for_next((esp_gmf_node_t *)el), OBJ_GET_TAG(self), self, evt->type, + esp_gmf_event_get_state_str(state), info.sample_rates, info.channels, info.bits); + if (info.sample_rates != 16000 || info.bits != 16) { + ESP_LOGE(TAG, "Unsupported format, rate: %d, bits: %d", info.sample_rates, info.bits); + return ESP_GMF_ERR_NOT_SUPPORT; + } + } + if (state == ESP_GMF_EVENT_STATE_NONE) { + esp_gmf_element_set_state(self, ESP_GMF_EVENT_STATE_INITIALIZED); + } + + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t gmf_wn_destroy(esp_gmf_obj_handle_t self) +{ + ESP_GMF_NULL_CHECK(TAG, self, { return ESP_GMF_ERR_INVALID_ARG; }); + + gmf_wn_t *gmf_wn = (gmf_wn_t *)self; + + if (OBJ_GET_CFG(self)) { + esp_gmf_oal_free(OBJ_GET_CFG(self)); + } + if (gmf_wn->wn_iface && gmf_wn->model_data) { + gmf_wn->wn_iface->destroy(gmf_wn->model_data); + gmf_wn->model_data = NULL; + } + if (gmf_wn->cache) { + esp_gmf_cache_delete(gmf_wn->cache); + gmf_wn->cache = NULL; + } + ESP_LOGD(TAG, "Destroyed"); + esp_gmf_audio_el_deinit(self); + esp_gmf_oal_free(self); + + return ESP_GMF_ERR_OK; +} + +static esp_gmf_job_err_t gmf_wn_open(esp_gmf_audio_element_handle_t self, void *para) +{ + gmf_wn_t *gmf_wn = (gmf_wn_t *)self; + esp_gmf_wn_cfg_t *cfg = OBJ_GET_CFG(self); + char *model_name = esp_srmodel_filter(cfg->models, ESP_WN_PREFIX, NULL); + ESP_GMF_NULL_CHECK(TAG, model_name, { return ESP_GMF_JOB_ERR_FAIL; }); + gmf_wn->wn_iface = (esp_wn_iface_t *)esp_wn_handle_from_name(model_name); + ESP_GMF_NULL_CHECK(TAG, gmf_wn->wn_iface, { return ESP_GMF_JOB_ERR_FAIL; }); + gmf_wn->model_data = gmf_wn->wn_iface->create(model_name, cfg->det_mode); + ESP_GMF_NULL_CHECK(TAG, gmf_wn->model_data, { return ESP_GMF_JOB_ERR_FAIL; }); + + int algo_ch_num = gmf_wn->wn_iface->get_channel_num(gmf_wn->model_data); + gmf_wn->ch = strlen(cfg->input_format); + if (algo_ch_num > gmf_wn->ch) { + ESP_LOGE(TAG, "Input channel number is less than needed"); + return ESP_GMF_JOB_ERR_FAIL; + } + gmf_wn->sample_rate = gmf_wn->wn_iface->get_samp_rate(gmf_wn->model_data); + gmf_wn->chunk_size = gmf_wn->wn_iface->get_samp_chunksize(gmf_wn->model_data); + gmf_wn->frame_len = gmf_wn->chunk_size * gmf_wn->ch * ESP_WN_BYTES_PER_SAMPLE; + + esp_gmf_cache_new(gmf_wn->frame_len, &gmf_wn->cache); + ESP_GMF_NULL_CHECK(TAG, gmf_wn->cache, { return ESP_GMF_JOB_ERR_FAIL; }); + + ESP_GMF_ELEMENT_IN_PORT_ATTR_SET(ESP_GMF_ELEMENT_GET(self)->in_attr, ESP_GMF_EL_PORT_CAP_SINGLE, 16, 0, + ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, gmf_wn->frame_len); + ESP_GMF_ELEMENT_OUT_PORT_ATTR_SET(ESP_GMF_ELEMENT_GET(self)->out_attr, ESP_GMF_EL_PORT_CAP_SINGLE, 16, 0, + ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, gmf_wn->chunk_size * ESP_WN_BYTES_PER_SAMPLE); + ESP_LOGI(TAG, "Open, frame_len: %" PRIu32 ", ch %" PRIu32 ", chunksize %" PRIu32, gmf_wn->frame_len, gmf_wn->ch, gmf_wn->chunk_size); + esp_gmf_info_sound_t snd_info = {0}; + snd_info.sample_rates = 16000; + snd_info.bits = 16; + snd_info.channels = 1; + esp_gmf_element_notify_snd_info(self, &snd_info); + return ESP_GMF_JOB_ERR_OK; +} + +static esp_gmf_job_err_t gmf_wn_process(esp_gmf_audio_element_handle_t self, void *para) +{ + int ret = ESP_GMF_JOB_ERR_OK; + gmf_wn_t *gmf_wn = (gmf_wn_t *)self; + esp_gmf_port_handle_t in_port = ESP_GMF_ELEMENT_GET(self)->in; + esp_gmf_port_handle_t out_port = ESP_GMF_ELEMENT_GET(self)->out; + esp_gmf_payload_t *out_load = NULL; + esp_gmf_payload_t *cache_load = NULL; + esp_gmf_err_io_t load_ret = 0; + esp_gmf_wn_cfg_t *cfg = OBJ_GET_CFG(self); + bool need_load = false; + + esp_gmf_cache_ready_for_load(gmf_wn->cache, &need_load); + if (need_load) { + load_ret = esp_gmf_port_acquire_in(in_port, &gmf_wn->in_load, gmf_wn->frame_len, in_port->wait_ticks); + ESP_GMF_PORT_ACQUIRE_IN_CHECK(TAG, load_ret, ret, { goto __quit; }); + esp_gmf_cache_load(gmf_wn->cache, gmf_wn->in_load); + } + esp_gmf_cache_acquire(gmf_wn->cache, gmf_wn->frame_len, &cache_load); + if (cache_load->valid_size != gmf_wn->frame_len) { + if (cache_load->is_done == true) { + ret = ESP_GMF_JOB_ERR_DONE; + } else { + ret = ESP_GMF_JOB_ERR_CONTINUE; + ESP_LOGD(TAG, "Return Continue, size:%u", cache_load->valid_size); + } + goto __quit; + } + load_ret = esp_gmf_port_acquire_out(out_port, &out_load, gmf_wn->frame_len, ESP_GMF_MAX_DELAY); + out_load->is_done = cache_load->is_done; + ESP_GMF_PORT_ACQUIRE_OUT_CHECK(TAG, load_ret, ret, { goto __quit; }); + if (out_load->buf_length < gmf_wn->frame_len) { + ret = ESP_GMF_JOB_ERR_FAIL; + ESP_LOGE(TAG, "Output buffer is not enough"); + goto __quit; + } + esp_gmf_sort_with_format((int16_t *)cache_load->buf, + cfg->input_format, + gmf_wn->chunk_size, + gmf_wn->ch, + (int16_t *)out_load->buf); + int res = gmf_wn->wn_iface->detect(gmf_wn->model_data, (int16_t *)out_load->buf); + if (res > 0) { + int trigger_ch = gmf_wn->wn_iface->get_triggered_channel(gmf_wn->model_data); + if (cfg->detect_cb) { + cfg->detect_cb(self, trigger_ch, cfg->user_ctx); + } + } + + out_load->valid_size = gmf_wn->chunk_size * ESP_WN_BYTES_PER_SAMPLE; + esp_gmf_cache_ready_for_load(gmf_wn->cache, &need_load); + if (need_load == false) { + ret = ESP_GMF_JOB_ERR_TRUNCATE; + } else { + ret = (out_load->is_done == true ? ESP_GMF_JOB_ERR_DONE : ESP_GMF_JOB_ERR_OK); + } + +__quit: + if (out_load) { + esp_gmf_port_release_out(out_port, out_load, ESP_GMF_MAX_DELAY); + } + if (gmf_wn->in_load && (ret != ESP_GMF_JOB_ERR_TRUNCATE)) { + esp_gmf_port_release_in(in_port, gmf_wn->in_load, ESP_GMF_MAX_DELAY); + gmf_wn->in_load = NULL; + } + if (cache_load) { + esp_gmf_cache_release(gmf_wn->cache, cache_load); + } + return ret; +} + +static esp_gmf_job_err_t gmf_wn_close(esp_gmf_audio_element_handle_t self, void *para) +{ + gmf_wn_t *gmf_wn = (gmf_wn_t *)self; + if (gmf_wn->wn_iface && gmf_wn->model_data) { + gmf_wn->wn_iface->destroy(gmf_wn->model_data); + gmf_wn->model_data = NULL; + } + if (gmf_wn->cache) { + esp_gmf_cache_delete(gmf_wn->cache); + gmf_wn->cache = NULL; + } + return ESP_GMF_JOB_ERR_OK; +} + +static esp_gmf_err_t _load_wn_caps_func(esp_gmf_element_handle_t handle) +{ + esp_gmf_cap_t *caps = NULL; + esp_gmf_cap_t wn_caps = {0}; + wn_caps.cap_eightcc = ESP_GMF_CAPS_AUDIO_WWE; + wn_caps.attr_fun = NULL; + int ret = esp_gmf_cap_append(&caps, &wn_caps); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, { return ret; }, "Failed to create capability"); + + esp_gmf_element_t *el = (esp_gmf_element_t *)handle; + el->caps = caps; + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_wn_init(esp_gmf_wn_cfg_t *config, esp_gmf_element_handle_t *handle) +{ + esp_gmf_err_t ret = ESP_GMF_ERR_OK; + ESP_GMF_NULL_CHECK(TAG, config, { return ESP_GMF_ERR_INVALID_ARG; }); + ESP_GMF_NULL_CHECK(TAG, handle, { return ESP_GMF_ERR_INVALID_ARG; }); + *handle = NULL; + gmf_wn_t *gmf_wn = esp_gmf_oal_calloc(1, sizeof(gmf_wn_t)); + ESP_GMF_MEM_VERIFY(TAG, gmf_wn, { return ESP_GMF_ERR_MEMORY_LACK; }, "wn", sizeof(gmf_wn_t)); + esp_gmf_obj_t *obj = (esp_gmf_obj_t *)gmf_wn; + obj->new_obj = gmf_wn_new; + obj->del_obj = gmf_wn_destroy; + + esp_gmf_wn_cfg_t *obj_cfg = esp_gmf_oal_calloc(1, sizeof(esp_gmf_wn_cfg_t)); + ESP_GMF_NULL_CHECK(TAG, obj_cfg, { ret = ESP_GMF_ERR_MEMORY_LACK; goto __failed; }); + memcpy(obj_cfg, config, sizeof(esp_gmf_wn_cfg_t)); + ret = esp_gmf_obj_set_config(obj, obj_cfg, sizeof(esp_gmf_wn_cfg_t)); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto __failed, "Failed set OBJ configuration"); + ret = esp_gmf_obj_set_tag(obj, "ai_wn"); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto __failed, "Failed set OBJ tag"); + esp_gmf_element_cfg_t el_cfg = {0}; + ESP_GMF_ELEMENT_IN_PORT_ATTR_SET(el_cfg.in_attr, ESP_GMF_EL_PORT_CAP_SINGLE, 16, 0, + ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, 1024); + ESP_GMF_ELEMENT_OUT_PORT_ATTR_SET(el_cfg.out_attr, ESP_GMF_EL_PORT_CAP_SINGLE, 16, 0, + ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, 1024); + el_cfg.dependency = true; + ret = esp_gmf_audio_el_init(gmf_wn, &el_cfg); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto __failed, "Failed to initialize wn element"); + + ESP_GMF_ELEMENT_GET(gmf_wn)->ops.open = gmf_wn_open; + ESP_GMF_ELEMENT_GET(gmf_wn)->ops.process = gmf_wn_process; + ESP_GMF_ELEMENT_GET(gmf_wn)->ops.close = gmf_wn_close; + ESP_GMF_ELEMENT_GET(gmf_wn)->ops.event_receiver = gmf_wn_received_event_handler; + ESP_GMF_ELEMENT_GET(gmf_wn)->ops.load_caps = _load_wn_caps_func; + + *handle = obj; + return ESP_GMF_ERR_OK; +__failed: + esp_gmf_obj_delete(obj); + return ret; +} + +esp_gmf_err_t esp_gmf_wn_set_detect_cb(esp_gmf_element_handle_t self, esp_wn_detect_cb_t cb, void *ctx) +{ + ESP_GMF_NULL_CHECK(TAG, self, { return ESP_GMF_ERR_INVALID_ARG; }); + ESP_GMF_NULL_CHECK(TAG, cb, { return ESP_GMF_ERR_INVALID_ARG; }); + esp_gmf_wn_cfg_t *cfg = OBJ_GET_CFG(self); + cfg->detect_cb = cb; + cfg->user_ctx = ctx; + return ESP_GMF_ERR_OK; +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/CHANGELOG.md b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/CHANGELOG.md new file mode 100644 index 0000000..0e1907a --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/CHANGELOG.md @@ -0,0 +1,105 @@ +# Changelog + +## v0.7.2 + +### Features + +- Support C++ build + +## v0.7.1 + +### Bug Fixes +- Fixed sonic bypass failed for first payload + +### Features + +- Added audio param setting for `aud_alc`, `aud_fade`, `aud_sonic` + +## v0.7.0 + +### Features + +- Updated `esp_audio_codec` to version v2.3.0 +- Updated `esp_audio_effects` to version v1.1.0 +- Added `esp_gmf_audio_enc_get_frame_size`, `esp_gmf_audio_enc_set_bitrate`, `esp_gmf_audio_enc_get_bitrate`, `esp_gmf_audio_enc_reconfig` and `esp_gmf_audio_enc_reconfig_by_sound_info` functions to `gmf_audio_enc` +- Added `SBC` and `LC3` encoders +- Added `esp_gmf_audio_dec_reconfig` and `esp_gmf_audio_dec_reconfig_by_sound_info` functions to `gmf_audio_dec` +- Added `SBC` and `LC3` decoders +- Added `meta_flag` in `gmf_audio_dec` to support audio decoder recovery status tracking +- Added common audio parameter setting interface +- Redefined audio methods name +- Removed the audio encoder and decoder reconfig interface in `esp_gmf_audio_helper.c` +- Used the `esp_gmf_element_handle_t` type handle in the `gmf_audio` module +- Use default configuration for `esp_gmf_audio_xxx_init` internally if configuration set to NULL +- Added TRUNCATE support for SONIC element to enable its flexible placement at any position within the pipeline +- Supported scenarios where the input data for audio encoding is not a complete frame +- Added PTS (Presentation Time Stamp) calculation and propagation for the `aud_alc`, `aud_eq`, `aud_fade`, `aud_enc` +- Supported user-configurable input frame length and load acquisition timeout for the mixer + +### Bug Fixes + +- Fixed `gmf_audio_enc` process blocked due to forget release of in_load when truncate is returned +- Fixed parameter mismatch in `audio_dec_reconfig_dec_by_sound_info` +- Standardize TAG identifier format across all audio elements with `aud` prefix +- Fixed a crash caused by passing a NULL encoder config to the init function and subsequently reporting info +- Fixed bugs in the implementation of methods for sonic, fade, and mixer +- Fixed the issue where the encoder modified the configuration during runtime +- Fixed the issue where the mixer did not forward the out_load parameter to downstream components when all input stream were no data +- Fixed bugs that not check validation of in and out buffer for mixer which will cause crash +- Fixed bugs where setting parameters before opening had no effect for `aud_fade` and `aud_mixer` + +## v0.6.3 + +### Bug Fixes + +- Fixed decoder not releasing input port on failure +- Fixed missing input size check in gmf_audio_enc when input is insufficient to encode a frame and stream has ended +- Fixed missing zero size check for acquire_in in gmf_audio_dec +- Fixed unmatched sub_cfg when reconfigured by esp_gmf_audio_helper_reconfig_dec_by_uri + +## v0.6.2 + +### Features + +- Limit the version of `esp_audio_codec` and `esp_audio_effects` + + +## v0.6.1 + +- Added missing #include "esp_gmf_audio_element.h" across all audio elements +- Fixed out-of-range parameter handling for mixer and EQ elements +- Resolved rate/bit/channel converter bypass errors caused by asynchronous modification of obj->cfg between set/event callbacks and process functions +- Deleted one unreasonable log from esp_gmf_audio_helper.c + + +## v0.6.0 + +### Features +- Updated the logic for GMF capabilities and method handling +- Added support for NULL object configuration +- Added GMF port attribution for elements +- Added gmf_cache and optimized the loop path for the audio encoder and decoder +- Renamed component to `gmf_audio` +- Updated the License + + +## v0.5.2 + +### Bug Fixes + +- Fixed an issue where the decoder's output done flag was repeatedly set after the input was marked as done +- Fixed an issue where the input port was not released when the input data size was 0 + + +## v0.5.1 + +### Bug Fixes + +- Fixed the component requirements + + +## v0.5.0 + +### Features + +- Initial version of `esp-gmf-audio` diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/CMakeLists.txt b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/CMakeLists.txt new file mode 100644 index 0000000..5d490b5 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/CMakeLists.txt @@ -0,0 +1,4 @@ +idf_component_register(SRC_DIRS "./" + INCLUDE_DIRS "./include" + PRIV_INCLUDE_DIRS "./private_include" + REQUIRES esp_timer) diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/LICENSE b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/LICENSE new file mode 100644 index 0000000..95aa434 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/LICENSE @@ -0,0 +1,22 @@ +Espressif Modified MIT License + +Copyright (c) 2025 Espressif Systems (Shanghai) CO., LTD + +Permission is hereby granted for use EXCLUSIVELY with Espressif Systems products. +This includes the right to use, copy, modify, merge, publish, distribute, and sublicense +the Software, subject to the following conditions: + +1. This Software MUST BE USED IN CONJUNCTION WITH ESPRESSIF SYSTEMS PRODUCTS. +2. The above copyright notice and this permission notice shall be included in all copies + or substantial portions of the Software. +3. Redistribution of the Software in source or binary form FOR USE WITH NON-ESPRESSIF PRODUCTS + is strictly prohibited. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/README.md b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/README.md new file mode 100644 index 0000000..e06e640 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/README.md @@ -0,0 +1,43 @@ +# ESP-GMF-Audio + +- [![Component Registry](https://components.espressif.com/components/espressif/gmf_audio/badge.svg)](https://components.espressif.com/components/espressif/gmf_audio) + +- [中文版](./README_CN.md) + +ESP GMF Audio is a collection of GMF elements related to audio processing, including audio encoding, decoding, and audio effect processing algorithms. The currently supported audio modules are listed in the table below. + +|Name|TAG|Function|Method|Input Port|Output Port|Input blocking time|Output blocking time|Dependent on Audio Information| +|:----:|:----:|:-----:|:----:|:----:|:----:|:----:|:----:|:----| +| AUDIO_DEC |aud_dec |Audio decoder: MP3,AAC,AMRNB,
    AMRWB,FLAC,WAV,M4A,TS|Nil|Single|Single|User configurable, default value is maximum delay|User configurable, default value is maximum delay|No| +| AUDIO_ENC |aud_enc |Audio encoder: AAC,AMRNB,AMRWB,
    ADPCM,OPUS,PCM|Nil|Single|Single|User configurable, default value is maximum delay|User configurable, default value is maximum delay|Yes| +| RATE_CVT|aud_rate_cvt |Audio sampling rate adjustment|`set_dest_rate`|Single|Single|Maximum delay|Maximum delay|Yes| +| BIT_CVT |aud_bit_cvt |Audio bit-depth conversion|`set_dest_bits`|Single|Single|Maximum delay|Maximum delay|Yes| +| CH_CVT |aud_ch_cvt |Audio channel conversion|`set_dest_ch`|Single|Single|Maximum delay|Maximum delay|Yes| +| ALC |aud_alc |Audio volume adjustment|`set_gain`
    `get_gain`|Single|Single|Maximum delay|Maximum delay|Yes| +| EQ |aud_eq |Audio equalizer adjustment|`set_para`
    `get_para`
    `enable_filter`
    `disable_filter`|Single|Single|Maximum delay|Maximum delay|Yes| +| FADE |aud_fade |Audio fade-in and fade-out effects|`set_mode`
    `get_mode`
    `reset_weight`|Single|Single|Maximum delay|Maximum delay|Yes| +| SONIC |aud_sonic |Audio pitch and speed shifting effects|`set_speed`
    `get_speed`
    `set_pitch`
    `get_pitch`|Single|Single|Maximum delay|Maximum delay|Yes| +| MIXER |aud_mixer |Audio mixing effects|`set_info`
    `set_mode`|Multiple|Single|The blocking time for the first channel is 0, while the blocking time for other channels is maximum delay|Maximum delay|No| +|INTERLEAVE|aud_intlv|Data interleaving|Nil|Multiple|Single|User configurable, default value is maximum delay|Maximum delay|Yes| +|DEINTERLEAVE|aud_deintlv|Data de-interleaving|Nil|Single|Multiple|Maximum delay|User configurable, default value is maximum delay|Yes| + +## Usage +The ESP GMF Audio is often used in combination to form a pipeline. For example code, please refer to [test_app](../test_apps/main/elements/gmf_audio_play_el_test.c)。 + +You can also create and compile a project using the following commands, taking the `pipeline_play_embed_music` project as an example. Before starting, make sure you have a working [ESP-IDF](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/get-started/index.html) environment. + +### 1. Create the Example Project + +Create the `pipeline_play_embed_music` example project based on the `gmf_examples` component (using version v0.7.0 as an example; update the version as needed): + +```shell +idf.py create-project-from-example "espressif/gmf_examples=0.7.0:pipeline_play_embed_music" +``` + +### 2. Build and Flash the Project Using an ESP32-S3 Board + +```shell +cd pipeline_play_embed_music +idf.py set-target esp32s3 +idf.py -p YOUR_PORT flash monitor +``` diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/README_CN.md b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/README_CN.md new file mode 100644 index 0000000..cb33ac5 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/README_CN.md @@ -0,0 +1,43 @@ +# ESP-GMF-Audio + +- [![Component Registry](https://components.espressif.com/components/espressif/gmf_audio/badge.svg)](https://components.espressif.com/components/espressif/gmf_audio) + +- [English](./README.md) + +ESP GMF Audio 是 GMF 音频处理相关元素的集合,包括音频编码,解码和音频效果处理算法。目前已支持的 Audio 模块参见下表。 + +| 名称 | 标签 | 功能 | 函数方法 | 输入端口 | 输出端口 | 输入阻塞时间 | 输出阻塞时间 |依赖音频信息 | +|:----:| :-----: | :----: | :----: |:----: |:----: |:----: |:----: |:---- | +| AUDIO_DEC | aud_dec | 音频解码:MP3,AAC,AMRNB,
    AMRWB,FLAC,WAV,M4A,TS | 无 | 单个 | 单个 | 可用户配置,默认是最大延迟 |可用户配置,默认是最大延迟 |否 | +| AUDIO_ENC | aud_enc | 音频编码:AAC,AMRNB,AMRWB,
    ADPCM,OPUS,PCM | 无 | 单个 | 单个 | 可用户配置,默认是最大延迟 |可用户配置,默认是最大延迟 |是 | +| RATE_CVT| aud_rate_cvt | 音频采样率调节 | `set_dest_rate` | 单个 | 单个 |最大延迟 |最大延迟| 是 | +| BIT_CVT | aud_bit_cvt | 音频比特位转换 | `set_dest_bits`| 单个 | 单个 |最大延迟 |最大延迟| 是 | +| CH_CVT | aud_ch_cvt | 音频声道数转换 | `set_dest_ch`| 单个 | 单个 |最大延迟 |最大延迟| 是 | +| ALC | aud_alc | 音频音量调节 | `set_gain`
    `get_gain`| 单个 | 单个 |最大延迟 |最大延迟| 是 | +| EQ | aud_eq | 音频均衡器调节 |`set_para`
    `get_para`
    `enable_filter`
    `disable_filter` |单个 |单个|最大延迟 |最大延迟|是 | +| FADE | aud_fade | 音频淡入淡出效果 |`set_mode`
    `get_mode`
    `reset_weight` | 单个 | 单个 |最大延迟 |最大延迟 |是 | +| SONIC | aud_sonic | 音频变速变调效果 |`set_speed`
    `get_speed`
    `set_pitch`
    `get_pitch`| 单个 | 单个 |最大延迟 |最大延迟| 是 | +| MIXER | aud_mixer | 音频混音效果 |`set_info`
    `set_mode`| 多个 | 单个 | 第一路阻塞时间为0,其他路阻塞时间为最大延迟 |最大延迟| 否 | +|INTERLEAVE| aud_intlv | 数据交织 | 无 | 多个 | 单个 | 可用户配置,默认是最大延迟 |最大延迟| 是 | +|DEINTERLEAVE| aud_deintlv | 数据解交织 | 无| 单个 | 多个 |最大延迟|可用户配置,默认是最大延迟 |是 | + +## 示例 +ESP GMF Audio 常常组合成管道使用,示例代码请参考 [test_app](../test_apps/main/elements/gmf_audio_play_el_test.c)。 + +此外,也可通过以下命令创建并编译工程,以 `pipeline_play_embed_music` 示例工程为例。开始之前需要有可运行的 [ESP-IDF](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/get-started/index.html) 环境。 + +### 1. 创建示例工程 + +基于 `gmf_examples` 组件创建 `pipeline_play_embed_music` 的示例(以 v0.7.0 版本为例, 请根据实际使用更新版本参数) + +``` shell +idf.py create-project-from-example "espressif/gmf_examples=0.7.0:pipeline_play_embed_music" +``` + +### 2. 基于 ESP32S3 编译和下载 + +```shell +cd pipeline_play_embed_music +idf.py set-target esp32s3` +idf.py -p YOUR_PORT flash monitor +``` diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/esp_gmf_alc.c b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/esp_gmf_alc.c new file mode 100644 index 0000000..4e52820 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/esp_gmf_alc.c @@ -0,0 +1,353 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#include +#include "esp_log.h" +#include "esp_gmf_oal_mem.h" +#include "esp_gmf_oal_mutex.h" +#include "esp_gmf_node.h" +#include "esp_gmf_alc.h" +#include "esp_gmf_args_desc.h" +#include "gmf_audio_common.h" +#include "esp_gmf_audio_methods_def.h" +#include "esp_gmf_cap.h" +#include "esp_gmf_caps_def.h" +#include "esp_gmf_audio_element.h" + +#define GMF_ALC_DEFAULT_MAX_CHANNEL 2 +/** + * @brief Audio ALC context in GMF + */ +typedef struct { + esp_gmf_audio_element_t parent; /*!< The GMF alc handle */ + esp_ae_alc_handle_t alc_hd; /*!< The audio effects alc handle */ + uint8_t bytes_per_sample; /*!< Bytes number of per sampling point */ + int8_t *gain; /*!< The modified gain value of a certain channel number */ + int8_t max_ch; /*!< The maximum channel number */ + bool need_reopen : 1; /*!< Whether need to reopen. + True: Execute the close function first, then execute the open function + False: Do nothing */ +} esp_gmf_alc_t; + +static const char *TAG = "ESP_GMF_ALC"; + +static esp_gmf_err_t __alc_set_gain(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc, + uint8_t *buf, int buf_len) +{ + ESP_GMF_NULL_CHECK(TAG, arg_desc, { return ESP_GMF_ERR_INVALID_ARG;}); + ESP_GMF_NULL_CHECK(TAG, buf, { return ESP_GMF_ERR_INVALID_ARG;}); + esp_gmf_args_desc_t *alc_desc = arg_desc; + uint8_t idx = (uint8_t)(*buf); + alc_desc = alc_desc->next; + int8_t gain = (int8_t)(*(buf + alc_desc->offset)); + esp_gmf_err_t ret = ESP_GMF_ERR_OK; + if (idx == 0xFF) { + // Apply gain to all channels + esp_ae_alc_cfg_t *config = (esp_ae_alc_cfg_t *)OBJ_GET_CFG(handle); + if (config) { + for (uint8_t i = 0; i < config->channel; i++) { + ret |= esp_gmf_alc_set_gain(handle, i, gain); + } + } + } else { + ret = esp_gmf_alc_set_gain(handle, idx, gain); + } + return ret; +} + +static esp_gmf_err_t __alc_get_gain(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc, + uint8_t *buf, int buf_len) +{ + ESP_GMF_NULL_CHECK(TAG, arg_desc, {return ESP_GMF_ERR_INVALID_ARG;}); + ESP_GMF_NULL_CHECK(TAG, buf, {return ESP_GMF_ERR_INVALID_ARG;}); + esp_gmf_args_desc_t *alc_desc = arg_desc; + uint8_t idx = (uint8_t)(*buf); + alc_desc = alc_desc->next; + int8_t *gain = (int8_t *)(buf + alc_desc->offset); + return esp_gmf_alc_get_gain(handle, idx, gain); +} + +static esp_gmf_err_t esp_gmf_alc_new(void *cfg, esp_gmf_obj_handle_t *handle) +{ + return esp_gmf_alc_init(cfg, (esp_gmf_element_handle_t *)handle); +} + +static esp_gmf_job_err_t esp_gmf_alc_open(esp_gmf_element_handle_t self, void *para) +{ + esp_gmf_alc_t *alc = (esp_gmf_alc_t *)self; + esp_ae_alc_cfg_t *config = (esp_ae_alc_cfg_t *)OBJ_GET_CFG(self); + ESP_GMF_NULL_CHECK(TAG, config, { return ESP_GMF_JOB_ERR_FAIL;}); + alc->bytes_per_sample = (config->bits_per_sample >> 3) * config->channel; + esp_ae_alc_open(config, &alc->alc_hd); + ESP_GMF_CHECK(TAG, alc->alc_hd, { return ESP_GMF_JOB_ERR_FAIL;}, "Failed to create alc handle"); + GMF_AUDIO_UPDATE_SND_INFO(self, config->sample_rate, config->bits_per_sample, config->channel); + for (size_t i = 0; i < config->channel; i++) { + esp_ae_err_t ret = esp_ae_alc_set_gain(alc->alc_hd, i, alc->gain[i]); + if (ret != ESP_AE_ERR_OK) { + return ESP_GMF_JOB_ERR_FAIL; + } + } + alc->need_reopen = false; + ESP_LOGD(TAG, "Open, %p", self); + return ESP_GMF_JOB_ERR_OK; +} + +static esp_gmf_job_err_t esp_gmf_alc_close(esp_gmf_element_handle_t self, void *para) +{ + esp_gmf_alc_t *alc = (esp_gmf_alc_t *)self; + ESP_LOGD(TAG, "Closed, %p", self); + if (alc->alc_hd != NULL) { + esp_ae_alc_close(alc->alc_hd); + alc->alc_hd = NULL; + } + return ESP_GMF_ERR_OK; +} + +static esp_gmf_job_err_t esp_gmf_alc_process(esp_gmf_element_handle_t self, void *para) +{ + esp_gmf_alc_t *alc = (esp_gmf_alc_t *)self; + esp_gmf_job_err_t out_len = ESP_GMF_JOB_ERR_OK; + if (alc->need_reopen) { + esp_gmf_alc_close(self, NULL); + out_len = esp_gmf_alc_open(self, NULL); + if (out_len != ESP_GMF_JOB_ERR_OK) { + ESP_LOGE(TAG, "ALC reopen failed"); + return out_len; + } + } + esp_gmf_port_handle_t in_port = ESP_GMF_ELEMENT_GET(self)->in; + esp_gmf_port_handle_t out_port = ESP_GMF_ELEMENT_GET(self)->out; + esp_gmf_payload_t *in_load = NULL; + esp_gmf_payload_t *out_load = NULL; + int samples_num = ESP_GMF_ELEMENT_GET(alc)->in_attr.data_size / (alc->bytes_per_sample); + int bytes = samples_num * alc->bytes_per_sample; + esp_gmf_err_io_t load_ret = esp_gmf_port_acquire_in(in_port, &in_load, bytes, ESP_GMF_MAX_DELAY); + samples_num = in_load->valid_size / (alc->bytes_per_sample); + bytes = samples_num * alc->bytes_per_sample; + if ((bytes != in_load->valid_size) || (load_ret < ESP_GMF_IO_OK)) { + ESP_LOGE(TAG, "Invalid in load size %d, ret %d", in_load->valid_size, load_ret); + out_len = ESP_GMF_JOB_ERR_FAIL; + goto __alc_release; + } + if (in_port->is_shared == 1) { + out_load = in_load; + } + load_ret = esp_gmf_port_acquire_out(out_port, &out_load, samples_num ? bytes : in_load->buf_length, ESP_GMF_MAX_DELAY); + ESP_GMF_PORT_ACQUIRE_OUT_CHECK(TAG, load_ret, out_len, { goto __alc_release;}); + if (samples_num) { + esp_gmf_oal_mutex_lock(((esp_gmf_audio_element_t *)self)->lock); + esp_ae_err_t ret = esp_ae_alc_process(alc->alc_hd, samples_num, in_load->buf, out_load->buf); + esp_gmf_oal_mutex_unlock(((esp_gmf_audio_element_t *)self)->lock); + ESP_GMF_RET_ON_ERROR(TAG, ret, {out_len = ESP_GMF_JOB_ERR_FAIL; goto __alc_release;}, "ALC process error %d", ret); + } + ESP_LOGV(TAG, "Samples: %d, IN-PLD: %p-%p-%d-%d-%d, OUT-PLD: %p-%p-%d-%d-%d", + samples_num, in_load, in_load->buf, in_load->valid_size, in_load->buf_length, in_load->is_done, + out_load, out_load->buf, out_load->valid_size, out_load->buf_length, out_load->is_done); + out_load->valid_size = samples_num * alc->bytes_per_sample; + out_load->is_done = in_load->is_done; + out_load->pts = in_load->pts; + esp_gmf_audio_el_update_file_pos((esp_gmf_element_handle_t)self, out_load->valid_size); + if (in_load->is_done) { + out_len = ESP_GMF_JOB_ERR_DONE; + ESP_LOGD(TAG, "ALC done, out len: %d", out_load->valid_size); + } +__alc_release: + // Release in and out port + if (out_load != NULL) { + load_ret = esp_gmf_port_release_out(out_port, out_load, ESP_GMF_MAX_DELAY); + if ((load_ret < ESP_GMF_IO_OK) && (load_ret != ESP_GMF_IO_ABORT)) { + ESP_LOGE(TAG, "OUT port release error, ret:%d", load_ret); + out_len = ESP_GMF_JOB_ERR_FAIL; + } + } + if (in_load != NULL) { + load_ret = esp_gmf_port_release_in(in_port, in_load, ESP_GMF_MAX_DELAY); + if ((load_ret < ESP_GMF_IO_OK) && (load_ret != ESP_GMF_IO_ABORT)) { + ESP_LOGE(TAG, "IN port release error, ret:%d", load_ret); + out_len = ESP_GMF_JOB_ERR_FAIL; + } + } + return out_len; +} + +static esp_gmf_err_t alc_received_event_handler(esp_gmf_event_pkt_t *evt, void *ctx) +{ + ESP_GMF_NULL_CHECK(TAG, ctx, {return ESP_GMF_ERR_INVALID_ARG;}); + ESP_GMF_NULL_CHECK(TAG, evt, {return ESP_GMF_ERR_INVALID_ARG;}); + if ((evt->type != ESP_GMF_EVT_TYPE_REPORT_INFO) + || (evt->sub != ESP_GMF_INFO_SOUND) + || (evt->payload == NULL)) { + return ESP_GMF_ERR_OK; + } + esp_gmf_element_handle_t self = (esp_gmf_element_handle_t)ctx; + esp_gmf_element_handle_t el = evt->from; + esp_gmf_event_state_t state = ESP_GMF_EVENT_STATE_NONE; + esp_gmf_element_get_state(self, &state); + esp_gmf_info_sound_t *info = (esp_gmf_info_sound_t *)evt->payload; + esp_ae_alc_cfg_t *config = (esp_ae_alc_cfg_t *)OBJ_GET_CFG(self); + ESP_GMF_NULL_CHECK(TAG, config, return ESP_GMF_ERR_FAIL); + esp_gmf_alc_t *alc = (esp_gmf_alc_t *)self; + if (info->channels > alc->max_ch) { + alc->gain = esp_gmf_oal_realloc(alc->gain, config->channel * sizeof(*alc->gain)); + ESP_GMF_MEM_VERIFY(TAG, alc->gain, return ESP_GMF_ERR_MEMORY_LACK, "alc gain", config->channel * sizeof(*alc->gain)); + alc->max_ch = info->channels; + } + alc->need_reopen = (config->sample_rate != info->sample_rates) || (info->channels != config->channel) || (config->bits_per_sample != info->bits); + config->sample_rate = info->sample_rates; + config->channel = info->channels; + config->bits_per_sample = info->bits; + ESP_LOGD(TAG, "RECV element info, from: %s-%p, next: %p, self: %s-%p, type: %x, state: %s, rate: %d, ch: %d, bits: %d", + OBJ_GET_TAG(el), el, esp_gmf_node_for_next((esp_gmf_node_t *)el), OBJ_GET_TAG(self), self, evt->type, + esp_gmf_event_get_state_str(state), info->sample_rates, info->channels, info->bits); + if (state == ESP_GMF_EVENT_STATE_NONE) { + esp_gmf_element_set_state(self, ESP_GMF_EVENT_STATE_INITIALIZED); + } + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t esp_gmf_alc_destroy(esp_gmf_element_handle_t self) +{ + esp_gmf_alc_t *alc = (esp_gmf_alc_t *)self; + ESP_LOGD(TAG, "Destroyed, %p", self); + void *cfg = OBJ_GET_CFG(self); + if (cfg) { + esp_gmf_oal_free(cfg); + } + if (alc->gain) { + esp_gmf_oal_free(alc->gain); + alc->gain = NULL; + } + esp_gmf_audio_el_deinit(self); + esp_gmf_oal_free(alc); + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t _load_alc_caps_func(esp_gmf_element_handle_t handle) +{ + esp_gmf_cap_t *caps = NULL; + esp_gmf_cap_t dec_caps = {0}; + dec_caps.cap_eightcc = ESP_GMF_CAPS_AUDIO_ALC; + dec_caps.attr_fun = NULL; + int ret = esp_gmf_cap_append(&caps, &dec_caps); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to create capability"); + + esp_gmf_element_t *el = (esp_gmf_element_t *)handle; + el->caps = caps; + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t _load_alc_methods_func(esp_gmf_element_handle_t handle) +{ + esp_gmf_method_t *method = NULL; + esp_gmf_args_desc_t *set_args = NULL; + esp_gmf_args_desc_t *get_args = NULL; + esp_gmf_err_t ret = esp_gmf_args_desc_append(&set_args, AMETHOD_ARG(ALC, SET_GAIN, IDX), + ESP_GMF_ARGS_TYPE_UINT8, sizeof(uint8_t), 0); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to append index argument"); + ret = esp_gmf_args_desc_append(&set_args, AMETHOD_ARG(ALC, SET_GAIN, GAIN), ESP_GMF_ARGS_TYPE_INT8, + sizeof(int8_t), sizeof(uint8_t)); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to append gain argument"); + ret = esp_gmf_method_append(&method, AMETHOD(ALC, SET_GAIN), __alc_set_gain, set_args); + ESP_GMF_RET_ON_ERROR(TAG, ret, {return ret;}, "Failed to register %s method", AMETHOD(ALC, SET_GAIN)); + + ret = esp_gmf_args_desc_copy(set_args, &get_args); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to copy argument"); + ret = esp_gmf_method_append(&method, AMETHOD(ALC, GET_GAIN), __alc_get_gain, get_args); + ESP_GMF_RET_ON_ERROR(TAG, ret, {return ret;}, "Failed to register %s method", AMETHOD(ALC, GET_GAIN)); + + esp_gmf_element_t *el = (esp_gmf_element_t *)handle; + el->method = method; + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_alc_set_gain(esp_gmf_element_handle_t handle, uint8_t idx, int8_t gain) +{ + ESP_GMF_NULL_CHECK(TAG, handle, { return ESP_GMF_ERR_INVALID_ARG;}); + esp_gmf_alc_t *alc = (esp_gmf_alc_t *)handle; + if (idx >= alc->max_ch) { + ESP_LOGE(TAG, "Gain index %d is out of range", idx); + return ESP_GMF_ERR_INVALID_ARG; + } + if (alc->alc_hd) { + esp_gmf_oal_mutex_lock(((esp_gmf_audio_element_t *)handle)->lock); + esp_ae_err_t ret = esp_ae_alc_set_gain(alc->alc_hd, idx, gain); + esp_gmf_oal_mutex_unlock(((esp_gmf_audio_element_t *)handle)->lock); + if (ret != ESP_AE_ERR_OK) { + return ESP_GMF_JOB_ERR_FAIL; + } + } + alc->gain[idx] = gain; + return ESP_GMF_JOB_ERR_OK; +} + +esp_gmf_err_t esp_gmf_alc_get_gain(esp_gmf_element_handle_t handle, uint8_t idx, int8_t *gain) +{ + ESP_GMF_NULL_CHECK(TAG, handle, { return ESP_GMF_ERR_INVALID_ARG;}); + ESP_GMF_NULL_CHECK(TAG, gain, { return ESP_GMF_ERR_INVALID_ARG;}); + esp_gmf_alc_t *alc = (esp_gmf_alc_t *)handle; + if(idx > alc->max_ch) { + ESP_LOGE(TAG, "Gain index %d is out of range", idx); + return ESP_GMF_ERR_INVALID_ARG; + } + if (alc->alc_hd) { + esp_ae_err_t ret = esp_ae_alc_get_gain(alc->alc_hd, idx, gain); + if (ret != ESP_AE_ERR_OK) { + return ESP_GMF_JOB_ERR_FAIL; + } + return ESP_GMF_JOB_ERR_OK; + } + *gain = alc->gain[idx]; + return ESP_GMF_JOB_ERR_OK; +} + +esp_gmf_err_t esp_gmf_alc_init(esp_ae_alc_cfg_t *config, esp_gmf_element_handle_t *handle) +{ + ESP_GMF_NULL_CHECK(TAG, handle, {return ESP_GMF_ERR_INVALID_ARG;}); + *handle = NULL; + esp_gmf_err_t ret = ESP_GMF_ERR_OK; + esp_gmf_alc_t *alc = esp_gmf_oal_calloc(1, sizeof(esp_gmf_alc_t)); + ESP_GMF_MEM_VERIFY(TAG, alc, {return ESP_GMF_ERR_MEMORY_LACK;}, "ALC", sizeof(esp_gmf_alc_t)); + esp_gmf_obj_t *obj = (esp_gmf_obj_t *)alc; + obj->new_obj = esp_gmf_alc_new; + obj->del_obj = esp_gmf_alc_destroy; + alc->max_ch = GMF_ALC_DEFAULT_MAX_CHANNEL; + esp_ae_alc_cfg_t *cfg = esp_gmf_oal_calloc(1, sizeof(esp_ae_alc_cfg_t)); + ESP_GMF_MEM_VERIFY(TAG, cfg, ret = ESP_GMF_ERR_MEMORY_LACK; goto ALC_INIT_FAIL;, "alc configuration", sizeof(esp_ae_alc_cfg_t)); + esp_gmf_obj_set_config(obj, cfg, sizeof(esp_ae_alc_cfg_t)); + if (config) { + alc->max_ch = config->channel > 0 ? config->channel : GMF_ALC_DEFAULT_MAX_CHANNEL; + memcpy(cfg, config, sizeof(esp_ae_alc_cfg_t)); + } else { + esp_ae_alc_cfg_t dcfg = DEFAULT_ESP_GMF_ALC_CONFIG(); + alc->max_ch = dcfg.channel; + memcpy(cfg, &dcfg, sizeof(esp_ae_alc_cfg_t)); + } + alc->gain = esp_gmf_oal_calloc(1, alc->max_ch * sizeof(int8_t)); + ESP_GMF_MEM_VERIFY(TAG, alc->gain, goto ALC_INIT_FAIL, "alc gain", alc->max_ch * sizeof(int8_t)); + ret = esp_gmf_obj_set_tag(obj, "aud_alc"); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto ALC_INIT_FAIL, "Failed to set obj tag"); + esp_gmf_element_cfg_t el_cfg = {0}; + ESP_GMF_ELEMENT_IN_PORT_ATTR_SET(el_cfg.in_attr, ESP_GMF_EL_PORT_CAP_SINGLE, 0, 0, + ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, ESP_GMF_ELEMENT_PORT_DATA_SIZE_DEFAULT); + ESP_GMF_ELEMENT_OUT_PORT_ATTR_SET(el_cfg.out_attr, ESP_GMF_EL_PORT_CAP_SINGLE, 0, 0, + ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, ESP_GMF_ELEMENT_PORT_DATA_SIZE_DEFAULT); + el_cfg.dependency = true; + ret = esp_gmf_audio_el_init(alc, &el_cfg); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto ALC_INIT_FAIL, "Failed to initialize alc element"); + ESP_GMF_ELEMENT_GET(alc)->ops.open = esp_gmf_alc_open; + ESP_GMF_ELEMENT_GET(alc)->ops.process = esp_gmf_alc_process; + ESP_GMF_ELEMENT_GET(alc)->ops.close = esp_gmf_alc_close; + ESP_GMF_ELEMENT_GET(alc)->ops.event_receiver = alc_received_event_handler; + ESP_GMF_ELEMENT_GET(alc)->ops.load_caps = _load_alc_caps_func; + ESP_GMF_ELEMENT_GET(alc)->ops.load_methods = _load_alc_methods_func; + *handle = obj; + ESP_LOGD(TAG, "Initialization, %s-%p", OBJ_GET_TAG(obj), obj); + return ESP_GMF_ERR_OK; +ALC_INIT_FAIL: + esp_gmf_alc_destroy(obj); + return ret; +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/esp_gmf_audio_dec.c b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/esp_gmf_audio_dec.c new file mode 100644 index 0000000..41860cd --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/esp_gmf_audio_dec.c @@ -0,0 +1,517 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#include +#include "esp_log.h" +#include "esp_gmf_oal_mem.h" +#include "esp_gmf_err.h" +#include "esp_gmf_audio_dec.h" +#include "esp_audio_types.h" +#include "esp_audio_simple_dec_default.h" +#include "gmf_audio_common.h" +#include "esp_gmf_cap.h" +#include "esp_fourcc.h" +#include "esp_gmf_caps_def.h" +#include "esp_gmf_audio_methods_def.h" +#include "esp_gmf_audio_element.h" + +#define DEFAULT_DEC_OUTPUT_BUFFER_SIZE 1024 + +/** + * @brief Audio simple decoder context in GMF + */ +typedef struct { + esp_gmf_audio_element_t parent; /*!< The GMF audio decoder handle */ + esp_audio_simple_dec_handle_t dec_hd; /*!< The audio simple decoder handle */ + esp_audio_simple_dec_raw_t in_data; /*!< The audio simple decoder input data handle */ + esp_audio_simple_dec_out_t out_data; /*!< The audio simple decoder output data handle */ + int32_t buf_size; /*!< The size of decoder out buffer */ + esp_gmf_payload_t *in_load; /*!< The input payload */ + uint64_t pts; /*!< Audio pts */ +} esp_gmf_audio_dec_t; + +static const char *TAG = "ESP_GMF_ASMP_DEC"; + +static esp_gmf_err_t _dec_caps_iter_fun(uint32_t attr_index, esp_gmf_cap_attr_t *attr) +{ + switch (attr_index) { + case 0: { + const static uint32_t support_dec_type[] = {ESP_FOURCC_MP3, ESP_FOURCC_AAC, ESP_FOURCC_OPUS, ESP_FOURCC_FLAC, ESP_FOURCC_AMRNB, ESP_FOURCC_AMRWB, ESP_FOURCC_ALAC, + ESP_FOURCC_M4A, ESP_FOURCC_ALAW, ESP_FOURCC_ULAW, ESP_FOURCC_LC3, ESP_FOURCC_SBC, ESP_FOURCC_PCM}; + ESP_GMF_CAP_ATTR_SET_DISCRETE(attr, ESP_FOURCC_TO_INT('T', 'Y', 'P', 'E'), (uint32_t *) &support_dec_type, + sizeof(support_dec_type) / sizeof(uint32_t), sizeof(uint32_t)); + break; + } + default: + attr->prop_type = ESP_GMF_PROP_TYPE_NONE; + return ESP_GMF_ERR_NOT_SUPPORT; + } + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t __audio_dec_reconfig(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc, + uint8_t *buf, int buf_len) +{ + ESP_GMF_NULL_CHECK(TAG, buf, return ESP_GMF_ERR_INVALID_ARG); + esp_audio_simple_dec_cfg_t *config = (esp_audio_simple_dec_cfg_t *)buf; + return esp_gmf_audio_dec_reconfig(handle, config); +} + +static esp_gmf_err_t __audio_dec_reconfig_by_sound_info(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc, + uint8_t *buf, int buf_len) +{ + ESP_GMF_NULL_CHECK(TAG, buf, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_info_sound_t *snd_info = (esp_gmf_info_sound_t *)buf; + return esp_gmf_audio_dec_reconfig_by_sound_info(handle, snd_info); +} + +static inline esp_gmf_err_t dupl_esp_audio_simple_cfg(esp_audio_simple_dec_cfg_t *config, esp_audio_simple_dec_cfg_t **new_config) +{ + void *sub_cfg = NULL; + *new_config = esp_gmf_oal_calloc(1, sizeof(*config)); + ESP_GMF_MEM_VERIFY(TAG, *new_config, {return ESP_GMF_ERR_MEMORY_LACK;}, "audio simple decoder configuration", sizeof(*config)); + memcpy(*new_config, config, sizeof(*config)); + if (config->dec_cfg && (config->cfg_size > 0)) { + sub_cfg = esp_gmf_oal_calloc(1, config->cfg_size); + ESP_GMF_MEM_VERIFY(TAG, sub_cfg, {esp_gmf_oal_free(*new_config); return ESP_GMF_ERR_MEMORY_LACK;}, + "decoder configuration", config->cfg_size); + memcpy(sub_cfg, config->dec_cfg, config->cfg_size); + (*new_config)->dec_cfg = sub_cfg; + } + return ESP_GMF_JOB_ERR_OK; +} + +static esp_gmf_err_t audio_dec_set_subcfg(esp_audio_simple_dec_cfg_t *dec_cfg, void *sub_cfg, int32_t sub_cfg_sz) +{ + dec_cfg->dec_cfg = esp_gmf_oal_calloc(1, sub_cfg_sz); + ESP_GMF_MEM_CHECK(TAG, dec_cfg->dec_cfg, return ESP_GMF_ERR_MEMORY_LACK;); + dec_cfg->cfg_size = sub_cfg_sz; + memcpy(dec_cfg->dec_cfg, sub_cfg, sub_cfg_sz); + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t audio_dec_reconfig_dec_by_sound_info(esp_gmf_element_handle_t handle, esp_gmf_info_sound_t *info) +{ + esp_audio_simple_dec_cfg_t *dec_cfg = (esp_audio_simple_dec_cfg_t *)OBJ_GET_CFG(handle); + if (dec_cfg == NULL) { + dec_cfg = esp_gmf_oal_calloc(1, sizeof(esp_audio_simple_dec_cfg_t)); + ESP_GMF_MEM_VERIFY(TAG, dec_cfg, return ESP_GMF_ERR_MEMORY_LACK, "audio simple decoder configuration", sizeof(esp_audio_simple_dec_cfg_t)); + esp_gmf_obj_set_config(handle, dec_cfg, sizeof(esp_audio_simple_dec_cfg_t)); + } + esp_gmf_err_t ret = ESP_GMF_ERR_OK; + bool same_type = (info->format_id == dec_cfg->dec_type) ? (true) : (false); + dec_cfg->dec_type = info->format_id; + // free sub cfg first + if (dec_cfg->dec_cfg && (same_type == false)) { + esp_gmf_oal_free(dec_cfg->dec_cfg); + dec_cfg->dec_cfg = NULL; + dec_cfg->cfg_size = 0; + } + switch (info->format_id) { + case ESP_AUDIO_SIMPLE_DEC_TYPE_MP3: + case ESP_AUDIO_SIMPLE_DEC_TYPE_AMRWB: + case ESP_AUDIO_SIMPLE_DEC_TYPE_AMRNB: + case ESP_AUDIO_SIMPLE_DEC_TYPE_FLAC: + case ESP_AUDIO_SIMPLE_DEC_TYPE_WAV: + case ESP_AUDIO_SIMPLE_DEC_TYPE_M4A: + case ESP_AUDIO_SIMPLE_DEC_TYPE_TS: + break; + case ESP_AUDIO_SIMPLE_DEC_TYPE_AAC: + if (dec_cfg->dec_cfg == NULL) { + esp_aac_dec_cfg_t aac_cfg = { + .no_adts_header = false, + .aac_plus_enable = true, + }; + ret = audio_dec_set_subcfg(dec_cfg, &aac_cfg, sizeof(esp_aac_dec_cfg_t)); + } + esp_aac_dec_cfg_t *aac_cfg = (esp_aac_dec_cfg_t *)dec_cfg->dec_cfg; + aac_cfg->sample_rate = info->sample_rates; + aac_cfg->channel = info->channels; + aac_cfg->bits_per_sample = info->bits; + break; + case ESP_AUDIO_SIMPLE_DEC_TYPE_RAW_OPUS: + if (dec_cfg->dec_cfg == NULL) { + dec_cfg->dec_type = ESP_AUDIO_SIMPLE_DEC_TYPE_RAW_OPUS; + esp_opus_dec_cfg_t opus_cfg = ESP_OPUS_DEC_CONFIG_DEFAULT(); + opus_cfg.frame_duration = ESP_OPUS_DEC_FRAME_DURATION_20_MS; + opus_cfg.channel = info->channels; + opus_cfg.sample_rate = info->sample_rates; + ret = audio_dec_set_subcfg(dec_cfg, &opus_cfg, sizeof(esp_opus_dec_cfg_t)); + } else { + esp_opus_dec_cfg_t *opus_cfg = (esp_opus_dec_cfg_t *)dec_cfg->dec_cfg; + opus_cfg->channel = info->channels; + opus_cfg->sample_rate = info->sample_rates; + } + break; + case ESP_AUDIO_SIMPLE_DEC_TYPE_PCM: + esp_pcm_dec_cfg_t pcm_cfg = { + .sample_rate = info->sample_rates, + .channel = info->channels, + .bits_per_sample = info->bits, + }; + ret = audio_dec_set_subcfg(dec_cfg, &pcm_cfg, sizeof(esp_pcm_dec_cfg_t)); + break; + case ESP_AUDIO_SIMPLE_DEC_TYPE_G711A: + case ESP_AUDIO_SIMPLE_DEC_TYPE_G711U: + esp_g711_dec_cfg_t g711_cfg = { + .channel = info->channels, + }; + ret = audio_dec_set_subcfg(dec_cfg, &g711_cfg, sizeof(esp_g711_dec_cfg_t)); + break; + case ESP_AUDIO_SIMPLE_DEC_TYPE_ADPCM: + dec_cfg->dec_type = ESP_AUDIO_SIMPLE_DEC_TYPE_ADPCM; + esp_adpcm_dec_cfg_t adpcm_cfg = { + .bits_per_sample = 4, + .channel = info->channels, + .sample_rate = info->sample_rates, + }; + ret = audio_dec_set_subcfg(dec_cfg, &adpcm_cfg, sizeof(esp_adpcm_dec_cfg_t)); + break; + case ESP_AUDIO_SIMPLE_DEC_TYPE_SBC: + if (dec_cfg->dec_cfg == NULL) { + dec_cfg->dec_type = ESP_AUDIO_SIMPLE_DEC_TYPE_SBC; + esp_sbc_dec_cfg_t sbc_cfg = { + .sbc_mode = ESP_SBC_MODE_STD, + .ch_num = 2, + .enable_plc = false, + }; + ret = audio_dec_set_subcfg(dec_cfg, &sbc_cfg, sizeof(esp_sbc_dec_cfg_t)); + } + break; + case ESP_AUDIO_SIMPLE_DEC_TYPE_LC3: + if (dec_cfg->dec_cfg == NULL) { + dec_cfg->dec_type = ESP_AUDIO_SIMPLE_DEC_TYPE_LC3; + esp_lc3_dec_cfg_t lc3_cfg = ESP_LC3_DEC_CONFIG_DEFAULT(); + lc3_cfg.channel = info->channels; + lc3_cfg.sample_rate = info->sample_rates; + lc3_cfg.bits_per_sample = info->bits; + ret = audio_dec_set_subcfg(dec_cfg, &lc3_cfg, sizeof(esp_lc3_dec_cfg_t)); + } else { + esp_lc3_dec_cfg_t *lc3_cfg = (esp_lc3_dec_cfg_t *)dec_cfg->dec_cfg; + lc3_cfg->channel = info->channels; + lc3_cfg->sample_rate = info->sample_rates; + lc3_cfg->bits_per_sample = info->bits; + } + break; + default: + dec_cfg->dec_type = ESP_AUDIO_SIMPLE_DEC_TYPE_NONE; + ESP_LOGW(TAG, "Not support for simple decoder type %ld", info->format_id); + return ESP_GMF_ERR_NOT_SUPPORT; + } + ESP_LOGD(TAG, "The new dec type is %d", dec_cfg->dec_type); + return ((ret == ESP_GMF_ERR_OK) ? (ESP_GMF_ERR_OK) : (ret)); +} + +static inline void free_esp_audio_simple_cfg(esp_audio_simple_dec_cfg_t *config) +{ + if (config) { + if (config->dec_cfg) { + esp_gmf_oal_free(config->dec_cfg); + config->cfg_size = 0; + } + esp_gmf_oal_free(config); + } +} + +static esp_gmf_err_t esp_gmf_audio_dec_new(void *cfg, esp_gmf_obj_handle_t *handle) +{ + return esp_gmf_audio_dec_init((esp_audio_simple_dec_cfg_t *)cfg, (esp_gmf_element_handle_t *)handle); +} + +static esp_gmf_job_err_t esp_gmf_audio_dec_open(esp_gmf_element_handle_t self, void *para) +{ + esp_gmf_audio_dec_t *audio_dec = (esp_gmf_audio_dec_t *)self; + esp_audio_simple_dec_cfg_t *dec_cfg = (esp_audio_simple_dec_cfg_t *)OBJ_GET_CFG(self); + ESP_GMF_NULL_CHECK(TAG, dec_cfg, return ESP_GMF_ERR_FAIL); + esp_audio_simple_dec_open(dec_cfg, &audio_dec->dec_hd); + ESP_GMF_CHECK(TAG, audio_dec->dec_hd, {return ESP_GMF_JOB_ERR_FAIL;}, "Failed to create simple decoder handle"); + esp_gmf_port_enable_payload_share(ESP_GMF_ELEMENT_GET(self)->in, false); + audio_dec->buf_size = DEFAULT_DEC_OUTPUT_BUFFER_SIZE; + ESP_LOGD(TAG, "Open, el: %p, cfg: %p, type: %d", self, dec_cfg, dec_cfg->dec_type); + return ESP_GMF_JOB_ERR_OK; +} + +static esp_gmf_job_err_t esp_gmf_audio_dec_process(esp_gmf_element_handle_t self, void *para) +{ + esp_gmf_port_t *in_port = ESP_GMF_ELEMENT_GET(self)->in; + esp_gmf_port_t *out = ESP_GMF_ELEMENT_GET(self)->out; + esp_audio_err_t ret = ESP_AUDIO_ERR_OK; + esp_gmf_job_err_t out_len = ESP_GMF_JOB_ERR_OK; + esp_gmf_err_io_t load_ret = ESP_GMF_IO_OK; + esp_gmf_audio_dec_t *audio_dec = (esp_gmf_audio_dec_t *)self; + esp_gmf_payload_t *out_load = NULL; + esp_audio_simple_dec_info_t dec_info = {0}; + esp_gmf_info_sound_t snd_info = {0}; + if (audio_dec->in_data.len == 0) { + load_ret = esp_gmf_port_acquire_in(in_port, &audio_dec->in_load, ESP_GMF_ELEMENT_GET(audio_dec)->in_attr.data_size, in_port->wait_ticks); + ESP_GMF_PORT_ACQUIRE_IN_CHECK(TAG, load_ret, out_len, {goto __aud_proc_release;}); + audio_dec->in_data.buffer = audio_dec->in_load->buf; + audio_dec->in_data.len = audio_dec->in_load->valid_size; + audio_dec->in_data.consumed = 0; + audio_dec->in_data.eos = audio_dec->in_load->is_done; + audio_dec->in_data.frame_recover = (audio_dec->in_load->meta_flag & ESP_GMF_META_FLAG_AUD_RECOVERY_PLC) ? 1 : 0; + } + ESP_LOGV(TAG, "Read, in_len: %ld, done: %d\r\n", audio_dec->in_data.len, audio_dec->in_load ? audio_dec->in_load->is_done : -1); + load_ret = esp_gmf_port_acquire_out(out, &out_load, audio_dec->buf_size, ESP_GMF_MAX_DELAY); + ESP_GMF_PORT_ACQUIRE_OUT_CHECK(TAG, load_ret, out_len, {goto __aud_proc_release;}); + out_load->valid_size = 0; + audio_dec->out_data.buffer = out_load->buf; + audio_dec->out_data.len = out_load->buf_length; + if (audio_dec->in_data.len == 0) { + if (audio_dec->in_load->is_done == true) { + out_len = ESP_GMF_JOB_ERR_DONE; + out_load->is_done = audio_dec->in_load->is_done; + ESP_LOGD(TAG, "Return done, line:%d", __LINE__); + goto __aud_proc_release; + } else { + out_len = ESP_GMF_JOB_ERR_CONTINUE; + ESP_LOGD(TAG, "Return Continue, size:%d", audio_dec->in_load->valid_size); + goto __aud_proc_release; + } + } + while (1) { + ret = esp_audio_simple_dec_process(audio_dec->dec_hd, &audio_dec->in_data, &audio_dec->out_data); + if (ret != ESP_AUDIO_ERR_OK && ret != ESP_AUDIO_ERR_BUFF_NOT_ENOUGH) { + ESP_LOGE(TAG, "Failed to decode data, ret: %d", ret); + out_len = ESP_GMF_JOB_ERR_FAIL; + goto __aud_proc_release; + } + if (ret == ESP_AUDIO_ERR_BUFF_NOT_ENOUGH) { + load_ret = esp_gmf_port_release_out(out, out_load, out->wait_ticks); + ESP_GMF_PORT_RELEASE_OUT_CHECK(TAG, load_ret, out_len, {goto __aud_proc_release;}); + load_ret = esp_gmf_port_acquire_out(out, &out_load, audio_dec->out_data.needed_size, ESP_GMF_MAX_DELAY); + ESP_GMF_PORT_ACQUIRE_OUT_CHECK(TAG, load_ret, out_len, {goto __aud_proc_release;}); + ESP_LOGW(TAG, "Not enough memory for out, need:%d, old: %ld, new: %d", (int)audio_dec->out_data.needed_size, + audio_dec->out_data.len, out_load->buf_length); + audio_dec->out_data.buffer = out_load->buf; + audio_dec->out_data.len = out_load->buf_length; + audio_dec->buf_size = audio_dec->out_data.needed_size; + continue; + } + if (audio_dec->in_data.consumed <= audio_dec->in_data.len) { + audio_dec->in_data.buffer += audio_dec->in_data.consumed; + audio_dec->in_data.len -= audio_dec->in_data.consumed; + } + ESP_LOGV(TAG, "Dec, out len: %ld, need: %ld, in len: %ld, consumed: %ld, dec: %ld", + audio_dec->out_data.len, audio_dec->out_data.needed_size, + audio_dec->in_data.len, audio_dec->in_data.consumed, audio_dec->out_data.decoded_size); + ESP_LOGV(TAG, "buf: %p, sz: %d, dec: %ld", out_load->buf, out_load->valid_size, audio_dec->out_data.decoded_size); + if (audio_dec->out_data.decoded_size > 0) { + esp_audio_simple_dec_get_info(audio_dec->dec_hd, &dec_info); + esp_gmf_audio_el_get_snd_info(self, &snd_info); + if (snd_info.sample_rates != dec_info.sample_rate + || snd_info.channels != dec_info.channel + || snd_info.bits != dec_info.bits_per_sample) { + ESP_LOGD(TAG, "NOTIFY Info, rate: %d, bits: %d, ch: %d --> rate: %ld, bits: %d, ch: %d", + snd_info.sample_rates, snd_info.bits, snd_info.channels, dec_info.sample_rate, dec_info.bits_per_sample, dec_info.channel); + GMF_AUDIO_UPDATE_SND_INFO(self, dec_info.sample_rate, dec_info.bits_per_sample, dec_info.channel); + } + out_load->valid_size = audio_dec->out_data.decoded_size; + out_load->pts = audio_dec->pts; + audio_dec->pts += GMF_AUDIO_CALC_PTS(out_load->valid_size, dec_info.sample_rate, dec_info.channel, dec_info.bits_per_sample); + esp_gmf_audio_el_update_file_pos(self, out_load->valid_size); + if (audio_dec->in_load != NULL && audio_dec->in_data.len > 0) { + ESP_LOGD(TAG, "Return truncate, in len:%ld", audio_dec->in_data.len); + out_len = ESP_GMF_JOB_ERR_TRUNCATE; + } + } else { + if (audio_dec->in_data.len > 0) { + continue; + } + if (audio_dec->in_load && audio_dec->in_load->is_done) { + out_load->is_done = audio_dec->in_load->is_done; + ESP_LOGD(TAG, "Return done, line:%d", __LINE__); + out_len = ESP_GMF_JOB_ERR_DONE; + } else { + ESP_LOGD(TAG, "Return Continue, in len:%ld", audio_dec->in_data.len); + out_len = ESP_GMF_JOB_ERR_CONTINUE; + } + } + ESP_LOGV(TAG, "Release IN, in_len: %ld, done: %d, decoded_size: %ld", + audio_dec->in_data.len, audio_dec->in_load ? audio_dec->in_load->is_done : -1, audio_dec->out_data.decoded_size); + break; + } +__aud_proc_release: + if (out_load != NULL) { + load_ret = esp_gmf_port_release_out(out, out_load, out->wait_ticks); + if ((load_ret < ESP_GMF_IO_OK) && (load_ret != ESP_GMF_IO_ABORT)) { + ESP_LOGE(TAG, "OUT port release error, ret:%d", load_ret); + out_len = ESP_GMF_JOB_ERR_FAIL; + } + } + // If decoding fails or there is no data on input side, release input port + if ((out_len == ESP_GMF_JOB_ERR_FAIL) || (audio_dec->in_load && (audio_dec->in_data.len == 0))) { + load_ret = esp_gmf_port_release_in(in_port, audio_dec->in_load, ESP_GMF_MAX_DELAY); + if ((load_ret < ESP_GMF_IO_OK) && (load_ret != ESP_GMF_IO_ABORT)) { + ESP_LOGE(TAG, "IN port release error, ret:%d", load_ret); + out_len = ESP_GMF_JOB_ERR_FAIL; + } + audio_dec->in_load = NULL; + } + return out_len; +} + +static esp_gmf_job_err_t esp_gmf_audio_dec_close(esp_gmf_element_handle_t self, void *para) +{ + ESP_LOGD(TAG, "Closed, %p", self); + esp_gmf_audio_dec_t *audio_dec = (esp_gmf_audio_dec_t *)self; + if (audio_dec->dec_hd != NULL) { + esp_audio_simple_dec_close(audio_dec->dec_hd); + audio_dec->dec_hd = NULL; + } + audio_dec->pts = 0; + esp_gmf_info_sound_t snd_info = {0}; + audio_dec->in_load = NULL; + audio_dec->in_data.len = 0; + esp_gmf_audio_el_set_snd_info(self, &snd_info); + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t esp_gmf_audio_dec_destroy(esp_gmf_element_handle_t self) +{ + ESP_LOGD(TAG, "Destroyed, %p", self); + free_esp_audio_simple_cfg(OBJ_GET_CFG(self)); + esp_gmf_audio_el_deinit(self); + esp_gmf_oal_free(self); + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t _load_dec_caps_func(esp_gmf_element_handle_t handle) +{ + esp_gmf_cap_t *caps = NULL; + esp_gmf_cap_t dec_caps = {0}; + dec_caps.cap_eightcc = ESP_GMF_CAPS_AUDIO_DECODER; + dec_caps.attr_fun = _dec_caps_iter_fun; + int ret = esp_gmf_cap_append(&caps, &dec_caps); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to create capability"); + + esp_gmf_element_t *el = (esp_gmf_element_t *)handle; + el->caps = caps; + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t _load_dec_methods_func(esp_gmf_element_handle_t handle) +{ + esp_gmf_method_t *method = NULL; + esp_gmf_args_desc_t *set_args = NULL; + esp_gmf_args_desc_t *reconfig_args = NULL; + + esp_gmf_args_desc_t *sndinfo_args = NULL; + esp_gmf_err_t ret = esp_gmf_args_desc_append(&sndinfo_args, AMETHOD_ARG(DECODER, RECONFIG_BY_SND_INFO, INFO_TYPE), ESP_GMF_ARGS_TYPE_UINT32, + sizeof(uint32_t), offsetof(esp_gmf_info_sound_t, format_id)); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, return ret, "Failed to append type argument"); + ret = esp_gmf_args_desc_append(&sndinfo_args, AMETHOD_ARG(DECODER, RECONFIG_BY_SND_INFO, INFO_SAMPLERATE), ESP_GMF_ARGS_TYPE_INT32, + sizeof(int32_t), offsetof(esp_gmf_info_sound_t, sample_rates)); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, return ret, "Failed to append sample_rates argument"); + ret = esp_gmf_args_desc_append(&sndinfo_args, AMETHOD_ARG(DECODER, RECONFIG_BY_SND_INFO, INFO_CHANNEL), ESP_GMF_ARGS_TYPE_INT8, + sizeof(int8_t), 12); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, return ret, "Failed to append channels argument"); + ret = esp_gmf_args_desc_append(&sndinfo_args, AMETHOD_ARG(DECODER, RECONFIG_BY_SND_INFO, INFO_BITS), ESP_GMF_ARGS_TYPE_INT8, + sizeof(int8_t), 13); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, return ret, "Failed to append bits argument"); + ret = esp_gmf_args_desc_append_array(&set_args, AMETHOD_ARG(DECODER, RECONFIG_BY_SND_INFO, INFO), sndinfo_args, + sizeof(esp_gmf_info_sound_t), 0); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, return ret, "Failed to append sound info argument"); + ret = esp_gmf_method_append(&method, AMETHOD(DECODER, RECONFIG_BY_SND_INFO), __audio_dec_reconfig_by_sound_info, set_args); + ESP_GMF_RET_ON_ERROR(TAG, ret, return ret, "Failed to register %s method", AMETHOD(DECODER, RECONFIG_BY_SND_INFO)); + + set_args = NULL; + ret = esp_gmf_args_desc_append(&reconfig_args, AMETHOD_ARG(DECODER, RECONFIG, CFG_TYPE), ESP_GMF_ARGS_TYPE_INT32, + sizeof(int32_t), offsetof(esp_audio_simple_dec_cfg_t, dec_type)); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, return ret, "Failed to append type argument"); + ret = esp_gmf_args_desc_append(&reconfig_args, AMETHOD_ARG(DECODER, RECONFIG, CFG_SUBCFGPTR), ESP_GMF_ARGS_TYPE_INT32, + sizeof(int32_t), offsetof(esp_audio_simple_dec_cfg_t, dec_cfg)); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, return ret, "Failed to append cfg argument"); + ret = esp_gmf_args_desc_append(&reconfig_args, AMETHOD_ARG(DECODER, RECONFIG, CFG_SUBCFGSZ), ESP_GMF_ARGS_TYPE_UINT32, + sizeof(uint32_t), offsetof(esp_audio_simple_dec_cfg_t, cfg_size)); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, return ret, "Failed to append cfg_sz argument"); + ret = esp_gmf_args_desc_append_array(&set_args, AMETHOD_ARG(DECODER, RECONFIG, CFG), reconfig_args, + sizeof(esp_audio_simple_dec_cfg_t), 0); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, return ret, "Failed to append config argument"); + ret = esp_gmf_method_append(&method, AMETHOD(DECODER, RECONFIG), __audio_dec_reconfig, set_args); + ESP_GMF_RET_ON_ERROR(TAG, ret, return ret, "Failed to register %s method", AMETHOD(DECODER, RECONFIG)); + + esp_gmf_element_t *el = (esp_gmf_element_t *)handle; + el->method = method; + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_audio_dec_reconfig(esp_gmf_element_handle_t handle, esp_audio_simple_dec_cfg_t *config) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, config, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_event_state_t state = ESP_GMF_EVENT_STATE_NONE; + esp_gmf_element_get_state(handle, &state); + if (state < ESP_GMF_EVENT_STATE_OPENING) { + esp_audio_simple_dec_cfg_t *new_config = NULL; + esp_gmf_err_t ret = dupl_esp_audio_simple_cfg(config, &new_config); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, return ret, "Failed to duplicate audio decoder configuration"); + free_esp_audio_simple_cfg(OBJ_GET_CFG(handle)); + esp_gmf_obj_set_config(handle, new_config, sizeof(*config)); + return ESP_GMF_ERR_OK; + } else { + ESP_LOGE(TAG, "Failed to reconfig decoder due to invalid state: %s", esp_gmf_event_get_state_str(state)); + return ESP_GMF_ERR_FAIL; + } +} + +esp_gmf_err_t esp_gmf_audio_dec_reconfig_by_sound_info(esp_gmf_element_handle_t handle, esp_gmf_info_sound_t *info) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_event_state_t state = ESP_GMF_EVENT_STATE_NONE; + esp_gmf_element_get_state(handle, &state); + if (state < ESP_GMF_EVENT_STATE_OPENING) { + esp_gmf_err_t ret = audio_dec_reconfig_dec_by_sound_info(handle, info); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, return ret, "Failed to reconfig simple decoder by sound information"); + return ESP_GMF_ERR_OK; + } else { + ESP_LOGE(TAG, "Failed to reconfig decoder due to invalid state: %s", esp_gmf_event_get_state_str(state)); + return ESP_GMF_ERR_FAIL; + } +} + +esp_gmf_err_t esp_gmf_audio_dec_init(esp_audio_simple_dec_cfg_t *config, esp_gmf_element_handle_t *handle) +{ + ESP_GMF_NULL_CHECK(TAG, handle, {return ESP_GMF_ERR_INVALID_ARG;}); + *handle = NULL; + esp_gmf_err_t ret = ESP_GMF_ERR_OK; + esp_gmf_audio_dec_t *dec_hd = esp_gmf_oal_calloc(1, sizeof(esp_gmf_audio_dec_t)); + ESP_GMF_MEM_VERIFY(TAG, dec_hd, {return ESP_GMF_ERR_MEMORY_LACK;}, "audio decoder", sizeof(esp_gmf_audio_dec_t)); + esp_gmf_obj_t *obj = (esp_gmf_obj_t *)dec_hd; + obj->new_obj = esp_gmf_audio_dec_new; + obj->del_obj = esp_gmf_audio_dec_destroy; + esp_audio_simple_dec_cfg_t *cfg = NULL; + if (config) { + dupl_esp_audio_simple_cfg(config, &cfg); + } else { + esp_audio_simple_dec_cfg_t dcfg = DEFAULT_ESP_GMF_AUDIO_DEC_CONFIG(); + dupl_esp_audio_simple_cfg(&dcfg, &cfg); + } + ESP_GMF_CHECK(TAG, cfg, {ret = ESP_GMF_ERR_MEMORY_LACK; goto ES_DEC_FAIL;}, "Failed to allocate audio decoder configuration"); + esp_gmf_obj_set_config(obj, cfg, sizeof(esp_audio_simple_dec_cfg_t)); + ret = esp_gmf_obj_set_tag(obj, "aud_dec"); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto ES_DEC_FAIL, "Failed to set obj tag"); + esp_gmf_element_cfg_t el_cfg = {0}; + ESP_GMF_ELEMENT_IN_PORT_ATTR_SET(el_cfg.in_attr, ESP_GMF_EL_PORT_CAP_SINGLE, 0, 0, + ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, 512); + ESP_GMF_ELEMENT_OUT_PORT_ATTR_SET(el_cfg.out_attr, ESP_GMF_EL_PORT_CAP_SINGLE, 0, 0, + ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, 512); + el_cfg.dependency = false; + ret = esp_gmf_audio_el_init(dec_hd, &el_cfg); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto ES_DEC_FAIL, "Failed to initialize audio decoder element"); + ESP_LOGD(TAG, "Initialization, %s-%p", OBJ_GET_TAG(obj), obj); + ESP_GMF_ELEMENT_GET(dec_hd)->ops.open = esp_gmf_audio_dec_open; + ESP_GMF_ELEMENT_GET(dec_hd)->ops.process = esp_gmf_audio_dec_process; + ESP_GMF_ELEMENT_GET(dec_hd)->ops.close = esp_gmf_audio_dec_close; + ESP_GMF_ELEMENT_GET(dec_hd)->ops.load_caps = _load_dec_caps_func; + ESP_GMF_ELEMENT_GET(dec_hd)->ops.load_methods = _load_dec_methods_func; + *handle = obj; + return ESP_GMF_ERR_OK; +ES_DEC_FAIL: + esp_gmf_audio_dec_destroy(obj); + return ret; +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/esp_gmf_audio_enc.c b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/esp_gmf_audio_enc.c new file mode 100644 index 0000000..4d97138 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/esp_gmf_audio_enc.c @@ -0,0 +1,701 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#include +#include "esp_log.h" +#include "esp_gmf_node.h" +#include "esp_gmf_oal_mem.h" +#include "esp_gmf_oal_mutex.h" +#include "esp_gmf_audio_enc.h" +#include "esp_audio_enc_default.h" +#include "esp_gmf_cache.h" +#include "esp_gmf_cap.h" +#include "esp_gmf_caps_def.h" +#include "esp_gmf_audio_methods_def.h" +#include "esp_gmf_audio_element.h" +#include "gmf_audio_common.h" + +#define AUD_ENC_DEFAULT_INPUT_TIME_MS (20) +#define SET_ENC_BASIC_INFO(cfg, info) do { \ + (cfg)->sample_rate = (info)->sample_rates; \ + (cfg)->channel = (info)->channels; \ + (cfg)->bits_per_sample = (info)->bits; \ +} while (0) + +/** + * @brief Audio encoder context in GMF + */ +typedef struct { + esp_gmf_audio_element_t parent; /*!< The GMF audio encoder handle */ + esp_audio_enc_handle_t audio_enc_hd; /*!< The audio encoder handle */ + esp_gmf_cache_t *cached_payload; /*!< A Cached payload for data concatenation */ + uint32_t bitrate; /*!< The bitrate of the encoded data */ + esp_gmf_payload_t *origin_in_load; /*!< The original input payload */ + int64_t cur_pts; /*!< The audio Presentation Time Stamp(pts) */ +} esp_gmf_audio_enc_t; + +static const char *TAG = "ESP_GMF_AENC"; + +static esp_gmf_err_t __audio_enc_get_frame_size(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc, + uint8_t *buf, int buf_len) +{ + ESP_GMF_NULL_CHECK(TAG, arg_desc, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, buf, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_args_desc_t *enc_desc = arg_desc; + uint32_t *in_size = (uint32_t *)(buf); + enc_desc = enc_desc->next; + uint32_t *out_size = (uint32_t *)(buf + enc_desc->offset); + return esp_gmf_audio_enc_get_frame_size(handle, in_size, out_size); +} + +static esp_gmf_err_t __audio_enc_set_bitrate(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc, + uint8_t *buf, int buf_len) +{ + ESP_GMF_NULL_CHECK(TAG, arg_desc, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, buf, return ESP_GMF_ERR_INVALID_ARG); + uint32_t bitrate = *((uint32_t *)buf); + return esp_gmf_audio_enc_set_bitrate(handle, bitrate); +} + +static esp_gmf_err_t __audio_enc_get_bitrate(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc, + uint8_t *buf, int buf_len) +{ + ESP_GMF_NULL_CHECK(TAG, arg_desc, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, buf, return ESP_GMF_ERR_INVALID_ARG); + uint32_t *bitrate = (uint32_t *)buf; + return esp_gmf_audio_enc_get_bitrate(handle, bitrate); +} + +static esp_gmf_err_t __audio_enc_reconfig(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc, + uint8_t *buf, int buf_len) +{ +ESP_GMF_NULL_CHECK(TAG, arg_desc, return ESP_GMF_ERR_INVALID_ARG); +ESP_GMF_NULL_CHECK(TAG, buf, return ESP_GMF_ERR_INVALID_ARG); +esp_audio_enc_config_t *config = (esp_audio_enc_config_t *)buf; +return esp_gmf_audio_enc_reconfig(handle, config); +} + +static esp_gmf_err_t __audio_enc_reconfig_by_sound_info(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc, + uint8_t *buf, int buf_len) +{ + ESP_GMF_NULL_CHECK(TAG, arg_desc, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, buf, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_info_sound_t *snd_info = (esp_gmf_info_sound_t *)buf; + return esp_gmf_audio_enc_reconfig_by_sound_info(handle, snd_info); +} + +static inline esp_gmf_err_t dupl_esp_gmf_audio_enc_cfg(esp_audio_enc_config_t *config, esp_audio_enc_config_t **new_config) +{ + void *sub_cfg = NULL; + *new_config = esp_gmf_oal_calloc(1, sizeof(*config)); + ESP_GMF_MEM_VERIFY(TAG, *new_config, {return ESP_GMF_ERR_MEMORY_LACK;}, "audio encoder handle configuration", sizeof(*config)); + memcpy(*new_config, config, sizeof(*config)); + if (config->cfg && (config->cfg_sz > 0)) { + sub_cfg = esp_gmf_oal_calloc(1, config->cfg_sz); + ESP_GMF_MEM_VERIFY(TAG, sub_cfg, {esp_gmf_oal_free(*new_config); return ESP_GMF_ERR_MEMORY_LACK;}, + "audio encoder configuration", (int)config->cfg_sz); + memcpy(sub_cfg, config->cfg, config->cfg_sz); + (*new_config)->cfg = sub_cfg; + } + return ESP_GMF_JOB_ERR_OK; +} + +static inline void free_esp_gmf_audio_enc_cfg(esp_audio_enc_config_t *config) +{ + if (config) { + if (config->cfg) { + esp_gmf_oal_free(config->cfg); + config->cfg = NULL; + config->cfg_sz = 0; + } + esp_gmf_oal_free(config); + } +} + +static inline void audio_enc_change_audio_info(esp_audio_enc_config_t *enc_cfg, esp_gmf_info_sound_t *info) +{ + switch (enc_cfg->type) { + case ESP_AUDIO_TYPE_AAC: { + esp_aac_enc_config_t *aac_enc_cfg = (esp_aac_enc_config_t *)enc_cfg->cfg; + SET_ENC_BASIC_INFO(aac_enc_cfg, info); + break; + } + case ESP_AUDIO_TYPE_AMRNB: { + esp_amrnb_enc_config_t *amr_enc_cfg = (esp_amrnb_enc_config_t *)enc_cfg->cfg; + SET_ENC_BASIC_INFO(amr_enc_cfg, info); + break; + } + case ESP_AUDIO_TYPE_G711U: + case ESP_AUDIO_TYPE_G711A: { + esp_g711_enc_config_t *g711_enc_cfg = (esp_g711_enc_config_t *)enc_cfg->cfg; + SET_ENC_BASIC_INFO(g711_enc_cfg, info); + break; + } + case ESP_AUDIO_TYPE_AMRWB: { + esp_amrnb_enc_config_t *amr_enc_cfg = (esp_amrnb_enc_config_t *)enc_cfg->cfg; + SET_ENC_BASIC_INFO(amr_enc_cfg, info); + break; + } + case ESP_AUDIO_TYPE_ALAC: { + esp_alac_enc_config_t *alac_enc_cfg = (esp_alac_enc_config_t *)enc_cfg->cfg; + SET_ENC_BASIC_INFO(alac_enc_cfg, info); + break; + } + case ESP_AUDIO_TYPE_PCM: { + esp_pcm_enc_config_t *pcm_enc_cfg = (esp_pcm_enc_config_t *)enc_cfg->cfg; + SET_ENC_BASIC_INFO(pcm_enc_cfg, info); + break; + } + case ESP_AUDIO_TYPE_OPUS: { + esp_opus_enc_config_t *opus_enc_cfg = (esp_opus_enc_config_t *)enc_cfg->cfg; + SET_ENC_BASIC_INFO(opus_enc_cfg, info); + break; + } + case ESP_AUDIO_TYPE_ADPCM: { + esp_adpcm_enc_config_t *adpcm_enc_cfg = (esp_adpcm_enc_config_t *)enc_cfg->cfg; + SET_ENC_BASIC_INFO(adpcm_enc_cfg, info); + break; + } + case ESP_AUDIO_TYPE_LC3: { + esp_lc3_enc_config_t *lc3_enc_cfg = (esp_lc3_enc_config_t *)enc_cfg->cfg; + SET_ENC_BASIC_INFO(lc3_enc_cfg, info); + break; + } + case ESP_AUDIO_TYPE_SBC: { + esp_sbc_enc_config_t *sbc_enc_cfg = (esp_sbc_enc_config_t *)enc_cfg->cfg; + sbc_enc_cfg->sample_rate = info->sample_rates; + sbc_enc_cfg->bits_per_sample = info->bits; + if (info->channels == 1) { + sbc_enc_cfg->ch_mode = ESP_SBC_CH_MODE_MONO; + } else if (info->channels == 2) { + sbc_enc_cfg->ch_mode = ((sbc_enc_cfg->ch_mode > ESP_SBC_CH_MODE_MONO && sbc_enc_cfg->ch_mode <= ESP_SBC_CH_MODE_JOINT_STEREO) ? (sbc_enc_cfg->ch_mode) : (ESP_SBC_CH_MODE_DUAL)); + } else { + sbc_enc_cfg->ch_mode = ESP_SBC_CH_MODE_INVALID; + } + break; + } + default: + break; + } +} + +static esp_gmf_err_t audio_enc_set_subcfg(esp_audio_enc_config_t *enc_cfg, void *sub_cfg, int32_t sub_cfg_sz) +{ + enc_cfg->cfg = esp_gmf_oal_calloc(1, sub_cfg_sz); + ESP_GMF_MEM_CHECK(TAG, enc_cfg->cfg, return ESP_GMF_ERR_MEMORY_LACK;); + enc_cfg->cfg_sz = sub_cfg_sz; + memcpy(enc_cfg->cfg, sub_cfg, sub_cfg_sz); + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t audio_enc_reconfig_enc_by_sound_info(esp_gmf_element_handle_t handle, esp_gmf_info_sound_t *info) +{ + esp_audio_enc_config_t *cfg = (esp_audio_enc_config_t *)OBJ_GET_CFG(handle); + if (cfg == NULL) { + cfg = esp_gmf_oal_calloc(1, sizeof(esp_audio_enc_config_t)); + ESP_GMF_MEM_VERIFY(TAG, cfg, return ESP_GMF_ERR_MEMORY_LACK, "audio encoder configuration", sizeof(esp_audio_enc_config_t)); + esp_gmf_obj_set_config(handle, cfg, sizeof(esp_audio_enc_config_t)); + } + esp_gmf_err_t ret = ESP_GMF_ERR_OK; + bool same_type = (cfg->type == info->format_id) ? true : false; + // free sub cfg first + if (cfg->cfg && (same_type == false)) { + esp_gmf_oal_free(cfg->cfg); + cfg->cfg = NULL; + cfg->cfg_sz = 0; + } + cfg->type = info->format_id; + if (same_type == true && (cfg->cfg != NULL)) { + audio_enc_change_audio_info(cfg, info); + return ESP_GMF_ERR_OK; + } + switch (info->format_id) { + case ESP_AUDIO_TYPE_AAC: { + esp_aac_enc_config_t aac_enc_cfg = ESP_AAC_ENC_CONFIG_DEFAULT(); + SET_ENC_BASIC_INFO(&aac_enc_cfg, info); + aac_enc_cfg.bitrate = info->bitrate; + ret = audio_enc_set_subcfg(cfg, &aac_enc_cfg, sizeof(esp_aac_enc_config_t)); + break; + } + case ESP_AUDIO_TYPE_AMRNB: { + esp_amrnb_enc_config_t amr_enc_cfg = ESP_AMRNB_ENC_CONFIG_DEFAULT(); + SET_ENC_BASIC_INFO(&amr_enc_cfg, info); + amr_enc_cfg.bitrate_mode = info->bitrate; + ret = audio_enc_set_subcfg(cfg, &amr_enc_cfg, sizeof(esp_amrnb_enc_config_t)); + break; + } + case ESP_AUDIO_TYPE_AMRWB: { + esp_amrnb_enc_config_t amr_enc_cfg = ESP_AMRNB_ENC_CONFIG_DEFAULT(); + SET_ENC_BASIC_INFO(&amr_enc_cfg, info); + amr_enc_cfg.bitrate_mode = info->bitrate; + ret = audio_enc_set_subcfg(cfg, &amr_enc_cfg, sizeof(esp_amrnb_enc_config_t)); + break; + } + case ESP_AUDIO_TYPE_G711A: { + esp_g711_enc_config_t g711_enc_cfg = ESP_G711_ENC_CONFIG_DEFAULT(); + SET_ENC_BASIC_INFO(&g711_enc_cfg, info); + ret = audio_enc_set_subcfg(cfg, &g711_enc_cfg, sizeof(esp_g711_enc_config_t)); + break; + } + case ESP_AUDIO_TYPE_G711U: { + esp_g711_enc_config_t g711_enc_cfg = ESP_G711_ENC_CONFIG_DEFAULT(); + SET_ENC_BASIC_INFO(&g711_enc_cfg, info); + ret = audio_enc_set_subcfg(cfg, &g711_enc_cfg, sizeof(esp_g711_enc_config_t)); + break; + } + case ESP_AUDIO_TYPE_ALAC: { + esp_alac_enc_config_t alac_enc_cfg = ESP_ALAC_ENC_CONFIG_DEFAULT(); + SET_ENC_BASIC_INFO(&alac_enc_cfg, info); + ret = audio_enc_set_subcfg(cfg, &alac_enc_cfg, sizeof(esp_alac_enc_config_t)); + break; + } + case ESP_AUDIO_TYPE_PCM: { + esp_pcm_enc_config_t pcm_enc_cfg = ESP_PCM_ENC_CONFIG_DEFAULT(); + SET_ENC_BASIC_INFO(&pcm_enc_cfg, info); + ret = audio_enc_set_subcfg(cfg, &pcm_enc_cfg, sizeof(esp_pcm_enc_config_t)); + break; + } + case ESP_AUDIO_TYPE_OPUS: { + esp_opus_enc_config_t opus_enc_cfg = ESP_OPUS_ENC_CONFIG_DEFAULT(); + SET_ENC_BASIC_INFO(&opus_enc_cfg, info); + opus_enc_cfg.bitrate = info->bitrate; + ret = audio_enc_set_subcfg(cfg, &opus_enc_cfg, sizeof(esp_opus_enc_config_t)); + break; + } + case ESP_AUDIO_TYPE_ADPCM: { + esp_adpcm_enc_config_t adpcm_enc_cfg = ESP_ADPCM_ENC_CONFIG_DEFAULT(); + SET_ENC_BASIC_INFO(&adpcm_enc_cfg, info); + ret = audio_enc_set_subcfg(cfg, &adpcm_enc_cfg, sizeof(esp_adpcm_enc_config_t)); + break; + } + case ESP_AUDIO_TYPE_LC3: { + esp_lc3_enc_config_t lc3_enc_cfg = ESP_LC3_ENC_CONFIG_DEFAULT(); + SET_ENC_BASIC_INFO(&lc3_enc_cfg, info); + ret = audio_enc_set_subcfg(cfg, &lc3_enc_cfg, sizeof(esp_lc3_enc_config_t)); + break; + } + case ESP_AUDIO_TYPE_SBC: { + esp_sbc_enc_config_t sbc_enc_cfg = ESP_SBC_STD_ENC_CONFIG_DEFAULT(); + sbc_enc_cfg.sample_rate = info->sample_rates; + sbc_enc_cfg.bits_per_sample = info->bits; + if (info->channels == 1) { + sbc_enc_cfg.ch_mode = ESP_SBC_CH_MODE_MONO; + } else if (info->channels == 2) { + sbc_enc_cfg.ch_mode = ESP_SBC_CH_MODE_DUAL; + } else { + sbc_enc_cfg.ch_mode = ESP_SBC_CH_MODE_INVALID; + } + ret = audio_enc_set_subcfg(cfg, &sbc_enc_cfg, sizeof(esp_sbc_enc_config_t)); + break; + } + default: + ESP_LOGE(TAG, "Not support for encoder, %ld", info->format_id); + cfg->type = ESP_AUDIO_TYPE_UNSUPPORT; + return ESP_GMF_ERR_NOT_SUPPORT; + } + return ((ret == ESP_GMF_ERR_OK) ? (ESP_GMF_ERR_OK) : (ret)); +} + +static esp_gmf_job_err_t gmf_audio_enc_acquire_in(esp_gmf_audio_enc_t *audio_enc, esp_gmf_port_handle_t in_port, esp_gmf_payload_t **in_load) +{ + bool needed_load = false; + esp_gmf_job_err_t job_ret = ESP_GMF_JOB_ERR_OK; + esp_gmf_cache_ready_for_load(audio_enc->cached_payload, &needed_load); + if (needed_load) { + esp_gmf_err_io_t load_ret = esp_gmf_port_acquire_in(in_port, &audio_enc->origin_in_load, ESP_GMF_ELEMENT_GET(audio_enc)->in_attr.data_size, in_port->wait_ticks); + ESP_GMF_PORT_ACQUIRE_IN_CHECK(TAG, load_ret, job_ret, return job_ret); + int cache_size = 0; + esp_gmf_cache_get_cached_size(audio_enc->cached_payload, &cache_size); + audio_enc->cur_pts = audio_enc->origin_in_load->pts - GMF_AUDIO_CALC_PTS(cache_size, audio_enc->parent.snd_info.sample_rates, audio_enc->parent.snd_info.channels, audio_enc->parent.snd_info.bits); + esp_gmf_cache_load(audio_enc->cached_payload, audio_enc->origin_in_load); + } + esp_gmf_err_t ret = esp_gmf_cache_acquire(audio_enc->cached_payload, ESP_GMF_ELEMENT_GET(audio_enc)->in_attr.data_size, in_load); + if (ret != ESP_GMF_ERR_OK) { + job_ret = ((ret == ESP_GMF_ERR_NOT_ENOUGH) ? (ESP_GMF_JOB_ERR_CONTINUE) : (ESP_GMF_JOB_ERR_FAIL)); + } + return job_ret; +} + +static esp_gmf_err_t esp_gmf_audio_enc_new(void *cfg, esp_gmf_obj_handle_t *handle) +{ + return esp_gmf_audio_enc_init(cfg, (esp_gmf_element_handle_t *)handle); +} + +static esp_gmf_job_err_t esp_gmf_audio_enc_open(esp_gmf_element_handle_t self, void *para) +{ + esp_gmf_audio_enc_t *enc = (esp_gmf_audio_enc_t *)self; + esp_audio_enc_config_t *enc_cfg = (esp_audio_enc_config_t *)OBJ_GET_CFG(enc); + ESP_GMF_CHECK(TAG, enc_cfg, {return ESP_GMF_JOB_ERR_FAIL;}, "There is no encoder configuration"); + esp_audio_err_t ret = esp_audio_enc_open(enc_cfg, &enc->audio_enc_hd); + ESP_GMF_CHECK(TAG, enc->audio_enc_hd, {return ESP_GMF_JOB_ERR_FAIL;}, "Failed to create audio encoder handle"); + if (esp_audio_enc_get_frame_size(enc->audio_enc_hd, &ESP_GMF_ELEMENT_GET(enc)->in_attr.data_size, &ESP_GMF_ELEMENT_GET(enc)->out_attr.data_size) != ESP_AUDIO_ERR_OK) { + ESP_LOGE(TAG, "Failed to obtain frame size, ret: %d", ret); + return ESP_GMF_JOB_ERR_FAIL; + } + esp_gmf_port_enable_payload_share(ESP_GMF_ELEMENT_GET(self)->in, false); + esp_gmf_cache_new(ESP_GMF_ELEMENT_GET(enc)->in_attr.data_size, &enc->cached_payload); + ESP_GMF_CHECK(TAG, enc->cached_payload, {return ESP_GMF_JOB_ERR_FAIL;}, "Failed to new a cached payload on open"); + esp_audio_enc_info_t enc_info = {0}; + esp_audio_enc_get_info(enc->audio_enc_hd, &enc_info); + GMF_AUDIO_UPDATE_SND_INFO(self, enc_info.sample_rate, enc_info.bits_per_sample, enc_info.channel); + ESP_LOGI(TAG, "Open, type:%s, acquire in frame: %d, out frame: %d", esp_audio_codec_get_name(enc_cfg->type), ESP_GMF_ELEMENT_GET(enc)->in_attr.data_size, ESP_GMF_ELEMENT_GET(enc)->out_attr.data_size); + return ESP_GMF_JOB_ERR_OK; +} + +static esp_gmf_job_err_t esp_gmf_audio_enc_process(esp_gmf_element_handle_t self, void *para) +{ + // Get audio encoder instance and initialize variables + esp_gmf_audio_enc_t *audio_enc = (esp_gmf_audio_enc_t *)self; + esp_gmf_job_err_t out_len = ESP_GMF_JOB_ERR_OK; + esp_audio_err_t ret = ESP_AUDIO_ERR_OK; + esp_gmf_port_handle_t in_port = ESP_GMF_ELEMENT_GET(self)->in; + esp_gmf_port_handle_t out_port = ESP_GMF_ELEMENT_GET(self)->out; + esp_gmf_payload_t *out_load = NULL; + esp_audio_enc_in_frame_t enc_in_frame = {0}; + esp_audio_enc_out_frame_t enc_out_frame = {0}; + esp_gmf_err_io_t load_ret = 0; + bool needed_load = false; + esp_gmf_payload_t *in_load = NULL; + out_len = gmf_audio_enc_acquire_in(audio_enc, in_port, &in_load); + if (out_len != ESP_GMF_JOB_ERR_OK) { + goto __audio_enc_release; + } + ESP_LOGD(TAG, "Acq cache, buf:%p, vld:%d, len:%d, done:%d", in_load->buf, in_load->valid_size, in_load->buf_length, in_load->is_done); + // Acquire output buffer for encoded data and verify output buffer size + load_ret = esp_gmf_port_acquire_out(out_port, &out_load, ESP_GMF_ELEMENT_GET(audio_enc)->out_attr.data_size, ESP_GMF_MAX_DELAY); + ESP_GMF_PORT_ACQUIRE_OUT_CHECK(TAG, load_ret, out_len, {goto __audio_enc_release;}); + if (out_load->buf_length < (ESP_GMF_ELEMENT_GET(audio_enc)->out_attr.data_size)) { + ESP_LOGE(TAG, "The out payload valid size(%d) is smaller than wanted size(%d)", + out_load->buf_length, (ESP_GMF_ELEMENT_GET(audio_enc)->out_attr.data_size)); + out_len = ESP_GMF_JOB_ERR_FAIL; + goto __audio_enc_release; + } + // Check if have enough input data for processing + if (in_load->valid_size != ESP_GMF_ELEMENT_GET(audio_enc)->in_attr.data_size) { + if (in_load->is_done == true) { + out_len = ESP_GMF_JOB_ERR_DONE; + out_load->valid_size = 0; + out_load->is_done = in_load->is_done; + ESP_LOGD(TAG, "Return done, line:%d", __LINE__); + goto __audio_enc_release; + } else { + out_len = ESP_GMF_JOB_ERR_CONTINUE; + ESP_LOGD(TAG, "Return Continue, line:%d", __LINE__); + goto __audio_enc_release; + } + } + // Perform audio encoding with mutex protection + enc_in_frame.buffer = in_load->buf; + enc_in_frame.len = in_load->valid_size; + enc_out_frame.buffer = out_load->buf; + enc_out_frame.len = ESP_GMF_ELEMENT_GET(audio_enc)->out_attr.data_size; + esp_gmf_oal_mutex_lock(((esp_gmf_audio_element_t *)self)->lock); + ret = esp_audio_enc_process(audio_enc->audio_enc_hd, &enc_in_frame, &enc_out_frame); + esp_gmf_oal_mutex_unlock(((esp_gmf_audio_element_t *)self)->lock); + ESP_GMF_RET_ON_ERROR(TAG, ret, {out_len = ESP_GMF_JOB_ERR_FAIL; goto __audio_enc_release;}, "Audio encoder process error %d", ret); + out_load->valid_size = enc_out_frame.encoded_bytes; + out_load->is_done = in_load->is_done; + out_load->pts = audio_enc->cur_pts; + audio_enc->cur_pts += GMF_AUDIO_CALC_PTS(enc_in_frame.len, audio_enc->parent.snd_info.sample_rates, audio_enc->parent.snd_info.channels, audio_enc->parent.snd_info.bits); + // Handle end of stream + if (in_load->is_done) { + ESP_LOGW(TAG, "Got done, out size: %d", out_load->valid_size); + out_len = ESP_GMF_JOB_ERR_DONE; + } + // Check if need to truncate + esp_gmf_cache_ready_for_load(audio_enc->cached_payload, &needed_load); + if (needed_load == false) { + out_len = ESP_GMF_JOB_ERR_TRUNCATE; + int cached_size = 0; + esp_gmf_cache_get_cached_size(audio_enc->cached_payload, &cached_size); + ESP_LOGD(TAG, "Return TRUNCATE, reminder in size: %d", cached_size); + } +__audio_enc_release: + // Cleanup resources + esp_gmf_cache_release(audio_enc->cached_payload, in_load); + if (out_load != NULL) { + load_ret = esp_gmf_port_release_out(out_port, out_load, out_port->wait_ticks); + if ((load_ret < ESP_GMF_IO_OK) && (load_ret != ESP_GMF_IO_ABORT)) { + ESP_LOGE(TAG, "OUT port release error, ret:%d", load_ret); + out_len = ESP_GMF_JOB_ERR_FAIL; + } + } + if ((audio_enc->origin_in_load != NULL) && (out_len != ESP_GMF_JOB_ERR_TRUNCATE)) { + load_ret = esp_gmf_port_release_in(in_port, audio_enc->origin_in_load, in_port->wait_ticks); + if ((load_ret < ESP_GMF_IO_OK) && (load_ret != ESP_GMF_IO_ABORT)) { + ESP_LOGE(TAG, "IN port release error, ret:%d", load_ret); + out_len = ESP_GMF_JOB_ERR_FAIL; + } + audio_enc->origin_in_load = NULL; + } + return out_len; +} + +static esp_gmf_job_err_t esp_gmf_audio_enc_close(esp_gmf_element_handle_t self, void *para) +{ + ESP_LOGD(TAG, "Closed, %p", self); + esp_gmf_audio_enc_t *enc = (esp_gmf_audio_enc_t *)self; + if (enc->cached_payload) { + esp_gmf_cache_delete(enc->cached_payload); + enc->cached_payload = NULL; + } + if (enc->audio_enc_hd != NULL) { + esp_audio_enc_close(enc->audio_enc_hd); + enc->audio_enc_hd = NULL; + } + return ESP_GMF_JOB_ERR_OK; +} + +static esp_gmf_err_t audio_enc_received_event_handler(esp_gmf_event_pkt_t *evt, void *ctx) +{ + ESP_GMF_NULL_CHECK(TAG, ctx, { return ESP_GMF_ERR_INVALID_ARG;}); + ESP_GMF_NULL_CHECK(TAG, evt, { return ESP_GMF_ERR_INVALID_ARG;}); + if ((evt->type != ESP_GMF_EVT_TYPE_REPORT_INFO) + || (evt->sub != ESP_GMF_INFO_SOUND) + || (evt->payload == NULL)) { + return ESP_GMF_ERR_OK; + } + esp_gmf_element_handle_t self = (esp_gmf_element_handle_t)ctx; + esp_gmf_element_handle_t el = evt->from; + esp_gmf_event_state_t state = ESP_GMF_EVENT_STATE_NONE; + esp_gmf_element_get_state(self, &state); + if (state < ESP_GMF_EVENT_STATE_OPENING) { + esp_audio_enc_config_t *enc_cfg = (esp_audio_enc_config_t *)OBJ_GET_CFG(self); + ESP_GMF_NULL_CHECK(TAG, enc_cfg, return ESP_GMF_ERR_FAIL); + esp_gmf_info_sound_t *info = (esp_gmf_info_sound_t *)evt->payload; + audio_enc_change_audio_info(enc_cfg, info); + ESP_LOGD(TAG, "RECV info, from: %s-%p, next: %p, self: %s-%p, type: %x, state: %s, rate: %d, ch: %d, bits: %d", + OBJ_GET_TAG(el), el, esp_gmf_node_for_next((esp_gmf_node_t *)el), OBJ_GET_TAG(self), self, evt->type, + esp_gmf_event_get_state_str(state), info->sample_rates, info->channels, info->bits); + if (state == ESP_GMF_EVENT_STATE_NONE) { + esp_gmf_element_set_state(self, ESP_GMF_EVENT_STATE_INITIALIZED); + } + } + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t esp_gmf_audio_enc_destroy(esp_gmf_element_handle_t self) +{ + ESP_LOGD(TAG, "Destroyed, %p", self); + esp_gmf_audio_enc_t *enc = (esp_gmf_audio_enc_t *)self; + free_esp_gmf_audio_enc_cfg(OBJ_GET_CFG(self)); + esp_gmf_audio_el_deinit(self); + esp_gmf_oal_free(enc); + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t _load_enc_caps_func(esp_gmf_element_handle_t handle) +{ + esp_gmf_cap_t *caps = NULL; + esp_gmf_cap_t dec_caps = {0}; + dec_caps.cap_eightcc = ESP_GMF_CAPS_AUDIO_ENCODER; + dec_caps.attr_fun = NULL; + int ret = esp_gmf_cap_append(&caps, &dec_caps); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to create capability"); + + esp_gmf_element_t *el = (esp_gmf_element_t *)handle; + el->caps = caps; + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t _load_enc_methods_func(esp_gmf_element_handle_t handle) +{ + esp_gmf_method_t *method = NULL; + esp_gmf_args_desc_t *set_args = NULL; + esp_gmf_args_desc_t *get_args = NULL; + + esp_gmf_err_t ret = esp_gmf_args_desc_append(&set_args, AMETHOD_ARG(ENCODER, SET_BITRATE, BITRATE), + ESP_GMF_ARGS_TYPE_INT32, sizeof(uint32_t), 0); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, return ret, "Failed to append bitrate argument"); + ret = esp_gmf_method_append(&method, AMETHOD(ENCODER, SET_BITRATE), __audio_enc_set_bitrate, set_args); + ESP_GMF_RET_ON_ERROR(TAG, ret, return ret, "Failed to register %s method", AMETHOD(ENCODER, SET_BITRATE)); + ret = esp_gmf_args_desc_copy(set_args, &get_args); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, return ret, "Failed to copy argument"); + ret = esp_gmf_method_append(&method, AMETHOD(ENCODER, GET_BITRATE), __audio_enc_get_bitrate, get_args); + ESP_GMF_RET_ON_ERROR(TAG, ret, return ret, "Failed to register %s method", AMETHOD(ENCODER, GET_BITRATE)); + + get_args = NULL; + set_args = NULL; + ret = esp_gmf_args_desc_append(&get_args, AMETHOD_ARG(ENCODER, GET_FRAME_SZ, INSIZE), + ESP_GMF_ARGS_TYPE_INT32, sizeof(uint32_t), 0); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, return ret, "Failed to append insize argument"); + ret = esp_gmf_args_desc_append(&get_args, AMETHOD_ARG(ENCODER, GET_FRAME_SZ, OUTSIZE), ESP_GMF_ARGS_TYPE_INT32, + sizeof(uint32_t), sizeof(uint32_t)); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, return ret, "Failed to append outsize argument"); + ret = esp_gmf_method_append(&method, AMETHOD(ENCODER, GET_FRAME_SZ), __audio_enc_get_frame_size, get_args); + ESP_GMF_RET_ON_ERROR(TAG, ret, return ret, "Failed to register %s method", AMETHOD(ENCODER, GET_FRAME_SZ)); + + set_args = NULL; + esp_gmf_args_desc_t *sndinfo_args = NULL; + ret = esp_gmf_args_desc_append(&sndinfo_args, AMETHOD_ARG(ENCODER, RECONFIG_BY_SND_INFO, INFO_TYPE), ESP_GMF_ARGS_TYPE_UINT32, + sizeof(uint32_t), offsetof(esp_gmf_info_sound_t, format_id)); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, return ret, "Failed to append type argument"); + ret = esp_gmf_args_desc_append(&sndinfo_args, AMETHOD_ARG(ENCODER, RECONFIG_BY_SND_INFO, INFO_SAMPLERATE), ESP_GMF_ARGS_TYPE_INT32, + sizeof(int32_t), offsetof(esp_gmf_info_sound_t, sample_rates)); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, return ret, "Failed to append sample_rates argument"); + ret = esp_gmf_args_desc_append(&sndinfo_args, AMETHOD_ARG(ENCODER, RECONFIG_BY_SND_INFO, INFO_BITRATE), ESP_GMF_ARGS_TYPE_INT32, + sizeof(int32_t), offsetof(esp_gmf_info_sound_t, bitrate)); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, return ret, "Failed to append bitrate argument"); + ret = esp_gmf_args_desc_append(&sndinfo_args, AMETHOD_ARG(ENCODER, RECONFIG_BY_SND_INFO, INFO_CHANNEL), ESP_GMF_ARGS_TYPE_INT8, + sizeof(int8_t), 12); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, return ret, "Failed to append channels argument"); + ret = esp_gmf_args_desc_append(&sndinfo_args, AMETHOD_ARG(ENCODER, RECONFIG_BY_SND_INFO, INFO_BITS), ESP_GMF_ARGS_TYPE_INT8, + sizeof(int8_t), 13); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, return ret, "Failed to append bits argument"); + ret = esp_gmf_args_desc_append_array(&set_args, AMETHOD_ARG(ENCODER, RECONFIG_BY_SND_INFO, INFO), sndinfo_args, + sizeof(esp_gmf_info_sound_t), 0); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, return ret, "Failed to append sound info argument"); + ret = esp_gmf_method_append(&method, AMETHOD(ENCODER, RECONFIG_BY_SND_INFO), __audio_enc_reconfig_by_sound_info, set_args); + ESP_GMF_RET_ON_ERROR(TAG, ret, return ret, "Failed to register %s method", AMETHOD(ENCODER, RECONFIG_BY_SND_INFO)); + + set_args = NULL; + esp_gmf_args_desc_t *reconfig_args = NULL; + ret = esp_gmf_args_desc_append(&reconfig_args, AMETHOD_ARG(ENCODER, RECONFIG, CFG_TYPE), ESP_GMF_ARGS_TYPE_INT32, + sizeof(int32_t), offsetof(esp_audio_enc_config_t, type)); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, return ret, "Failed to append type argument"); + ret = esp_gmf_args_desc_append(&reconfig_args, AMETHOD_ARG(ENCODER, RECONFIG, CFG_SUBCFGPTR), ESP_GMF_ARGS_TYPE_INT32, + sizeof(int32_t), offsetof(esp_audio_enc_config_t, cfg)); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, return ret, "Failed to append cfg argument"); + ret = esp_gmf_args_desc_append(&reconfig_args, AMETHOD_ARG(ENCODER, RECONFIG, CFG_SUBCFGSZ), ESP_GMF_ARGS_TYPE_UINT32, + sizeof(uint32_t), offsetof(esp_audio_enc_config_t, cfg_sz)); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, return ret, "Failed to append cfg_sz argument"); + ret = esp_gmf_args_desc_append_array(&set_args, AMETHOD_ARG(ENCODER, RECONFIG, CFG), reconfig_args, + sizeof(esp_audio_enc_config_t), 0); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, return ret, "Failed to append argument"); + ret = esp_gmf_method_append(&method, AMETHOD(ENCODER, RECONFIG), __audio_enc_reconfig, set_args); + ESP_GMF_RET_ON_ERROR(TAG, ret, return ret, "Failed to register %s method", AMETHOD(ENCODER, RECONFIG)); + + esp_gmf_element_t *el = (esp_gmf_element_t *)handle; + el->method = method; + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_audio_enc_get_frame_size(esp_gmf_element_handle_t handle, uint32_t *in_size, uint32_t *out_size) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_audio_enc_config_t *cfg = (esp_audio_enc_config_t *)OBJ_GET_CFG(handle); + ESP_GMF_NULL_CHECK(TAG, cfg, return ESP_GMF_ERR_FAIL); + esp_gmf_audio_enc_t *enc = (esp_gmf_audio_enc_t *)handle; + esp_audio_err_t ret = -1; + if (enc->audio_enc_hd == NULL) { + esp_audio_enc_frame_info_t frame_info = {0}; + ret = esp_audio_enc_get_frame_info_by_cfg(cfg, &frame_info); + if (ret != ESP_AUDIO_ERR_OK) { + return ESP_GMF_ERR_FAIL; + } + *in_size = (uint32_t)frame_info.in_frame_size; + *out_size = (uint32_t)frame_info.out_frame_size; + } else { + ret = esp_audio_enc_get_frame_size(enc->audio_enc_hd, (int *)in_size, (int *)out_size); + } + return ((ret == ESP_AUDIO_ERR_OK) ? (ESP_GMF_ERR_OK) : (ESP_GMF_ERR_FAIL)); +} + +esp_gmf_err_t esp_gmf_audio_enc_set_bitrate(esp_gmf_element_handle_t handle, uint32_t bitrate) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_audio_enc_t *enc = (esp_gmf_audio_enc_t *)handle; + if (enc->audio_enc_hd) { + esp_gmf_oal_mutex_lock(((esp_gmf_audio_element_t *)handle)->lock); + esp_audio_err_t ret = esp_audio_enc_set_bitrate(enc->audio_enc_hd, (int)bitrate); + esp_gmf_oal_mutex_unlock(((esp_gmf_audio_element_t *)handle)->lock); + return ((ret == ESP_AUDIO_ERR_OK) ? (ESP_GMF_ERR_OK) : (ESP_GMF_ERR_FAIL)); + } + enc->bitrate = bitrate; + return ESP_GMF_JOB_ERR_OK; +} + +esp_gmf_err_t esp_gmf_audio_enc_get_bitrate(esp_gmf_element_handle_t handle, uint32_t *bitrate) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_audio_enc_t *enc = (esp_gmf_audio_enc_t *)handle; + if (enc->audio_enc_hd) { + esp_audio_enc_info_t enc_info = {0}; + esp_audio_err_t ret = esp_audio_enc_get_info(enc->audio_enc_hd, &enc_info); + *bitrate = enc_info.bitrate; + return ((ret == ESP_AUDIO_ERR_OK) ? (ESP_GMF_ERR_OK) : (ESP_GMF_ERR_FAIL)); + } + *bitrate = enc->bitrate; + return ESP_GMF_JOB_ERR_OK; +} + +esp_gmf_err_t esp_gmf_audio_enc_reconfig(esp_gmf_element_handle_t handle, esp_audio_enc_config_t *config) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, config, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_event_state_t state = ESP_GMF_EVENT_STATE_NONE; + esp_gmf_element_get_state(handle, &state); + if (state < ESP_GMF_EVENT_STATE_OPENING) { + esp_audio_enc_config_t *new_config = NULL; + esp_gmf_err_t ret = dupl_esp_gmf_audio_enc_cfg(config, &new_config); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to duplicate config"); + free_esp_gmf_audio_enc_cfg(OBJ_GET_CFG(handle)); + esp_gmf_obj_set_config(handle, new_config, sizeof(esp_audio_enc_config_t)); + return ESP_GMF_ERR_OK; + } else { + ESP_LOGE(TAG, "Failed to reconfig encoder due to invalid state: %s", esp_gmf_event_get_state_str(state)); + return ESP_GMF_ERR_FAIL; + } +} + +esp_gmf_err_t esp_gmf_audio_enc_reconfig_by_sound_info(esp_gmf_element_handle_t handle, esp_gmf_info_sound_t *info) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_event_state_t state = ESP_GMF_EVENT_STATE_NONE; + esp_gmf_element_get_state(handle, &state); + if (state < ESP_GMF_EVENT_STATE_OPENING) { + esp_gmf_err_t ret = audio_enc_reconfig_enc_by_sound_info(handle, info); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, return ret, "Failed to reconfig encoder by sound information"); + return ESP_GMF_ERR_OK; + } else { + ESP_LOGE(TAG, "Failed to reconfig encoder due to invalid state: %s", esp_gmf_event_get_state_str(state)); + return ESP_GMF_ERR_FAIL; + } +} + +esp_gmf_err_t esp_gmf_audio_enc_init(esp_audio_enc_config_t *config, esp_gmf_element_handle_t *handle) +{ + ESP_GMF_NULL_CHECK(TAG, handle, {return ESP_GMF_ERR_INVALID_ARG;}); + *handle = NULL; + esp_gmf_err_t ret = ESP_GMF_ERR_OK; + esp_gmf_audio_enc_t *audio_enc = esp_gmf_oal_calloc(1, sizeof(esp_gmf_audio_enc_t)); + ESP_GMF_MEM_VERIFY(TAG, audio_enc, {return ESP_GMF_ERR_MEMORY_LACK;}, "audio encoder", sizeof(esp_gmf_audio_enc_t)); + esp_gmf_obj_t *obj = (esp_gmf_obj_t *)audio_enc; + obj->new_obj = esp_gmf_audio_enc_new; + obj->del_obj = esp_gmf_audio_enc_destroy; + esp_audio_enc_config_t *cfg = NULL; + if (config) { + dupl_esp_gmf_audio_enc_cfg(config, &cfg); + } else { + esp_audio_enc_config_t dcfg = DEFAULT_ESP_GMF_AUDIO_ENC_CONFIG(); + dupl_esp_gmf_audio_enc_cfg(&dcfg, &cfg); + } + ESP_GMF_CHECK(TAG, cfg, ret = ESP_GMF_ERR_MEMORY_LACK; goto ES_ENC_FAIL;, "Failed to allocate audio encoder configuration"); + esp_gmf_obj_set_config(obj, cfg, sizeof(esp_audio_enc_config_t)); + ret = esp_gmf_obj_set_tag(obj, "aud_enc"); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto ES_ENC_FAIL, "Failed to set obj tag"); + esp_gmf_element_cfg_t el_cfg = {0}; + ESP_GMF_ELEMENT_IN_PORT_ATTR_SET(el_cfg.in_attr, ESP_GMF_EL_PORT_CAP_SINGLE, 0, 0, + ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, ESP_GMF_ELEMENT_PORT_DATA_SIZE_DEFAULT); + ESP_GMF_ELEMENT_OUT_PORT_ATTR_SET(el_cfg.out_attr, ESP_GMF_EL_PORT_CAP_SINGLE, 0, 0, + ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, ESP_GMF_ELEMENT_PORT_DATA_SIZE_DEFAULT); + el_cfg.dependency = true; + ret = esp_gmf_audio_el_init(audio_enc, &el_cfg); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto ES_ENC_FAIL, "Failed to initialize audio encoder element"); + audio_enc->parent.base.ops.open = esp_gmf_audio_enc_open; + audio_enc->parent.base.ops.process = esp_gmf_audio_enc_process; + audio_enc->parent.base.ops.close = esp_gmf_audio_enc_close; + audio_enc->parent.base.ops.event_receiver = audio_enc_received_event_handler; + audio_enc->parent.base.ops.load_caps = _load_enc_caps_func; + audio_enc->parent.base.ops.load_methods = _load_enc_methods_func; + *handle = obj; + ESP_LOGD(TAG, "Initialization, %s-%p", OBJ_GET_TAG(obj), obj); + return ret; +ES_ENC_FAIL: + esp_gmf_audio_enc_destroy(obj); + return ret; +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/esp_gmf_audio_helper.c b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/esp_gmf_audio_helper.c new file mode 100644 index 0000000..6dfc8be --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/esp_gmf_audio_helper.c @@ -0,0 +1,60 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#include +#include "esp_log.h" +#include "esp_gmf_audio_helper.h" +#include "esp_fourcc.h" + +static const char *TAG = "ESP_GMF_AUDIO_HELPER"; + +esp_gmf_err_t esp_gmf_audio_helper_get_audio_type_by_uri(const char *uri, uint32_t *format_id) +{ + const char *ext = strrchr(uri, '.'); + if (ext == NULL) { + return ESP_GMF_ERR_NOT_SUPPORT; + } + ext++; + if (strncasecmp(ext, "aac", 3) == 0) { + *format_id = ESP_FOURCC_AAC; + } else if (strncasecmp(ext, "g711a", 5) == 0) { + *format_id = ESP_FOURCC_ALAW; + } else if (strncasecmp(ext, "g711u", 5) == 0) { + *format_id = ESP_FOURCC_ULAW; + } else if (strncasecmp(ext, "amr", 3) == 0) { + *format_id = ESP_FOURCC_AMRNB; + } else if (strncasecmp(ext, "awb", 3) == 0) { + *format_id = ESP_FOURCC_AMRWB; + } else if (strncasecmp(ext, "alac", 4) == 0) { + *format_id = ESP_FOURCC_ALAC; + } else if (strncasecmp(ext, "pcm", 3) == 0) { + *format_id = ESP_FOURCC_PCM; + } else if (strncasecmp(ext, "opus", 4) == 0) { + *format_id = ESP_FOURCC_OPUS; + } else if (strncasecmp(ext, "adpcm", 5) == 0) { + *format_id = ESP_FOURCC_ADPCM; + } else if (strncasecmp(ext, "sbc", 3) == 0) { + *format_id = ESP_FOURCC_SBC; + } else if (strncasecmp(ext, "lc3", 3) == 0) { + *format_id = ESP_FOURCC_LC3; + } else if (strncasecmp(ext, "mp3", 3) == 0) { + *format_id = ESP_FOURCC_MP3; + } else if (strncasecmp(ext, "m4a", 3) == 0) { + *format_id = ESP_FOURCC_M4A; + } else if (strncasecmp(ext, "wav", 3) == 0) { + *format_id = ESP_FOURCC_WAV; + } else if (strncasecmp(ext, "ts", 2) == 0) { + *format_id = ESP_FOURCC_M2TS; + } else if (strncasecmp(ext, "flac", 4) == 0) { + *format_id = ESP_FOURCC_FLAC; + } else { + *format_id = 0; + ESP_LOGE(TAG, "Not support audio codec type, %s", uri); + return ESP_GMF_ERR_NOT_SUPPORT; + } + return ESP_GMF_ERR_OK; +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/esp_gmf_audio_param.c b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/esp_gmf_audio_param.c new file mode 100644 index 0000000..7f381b4 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/esp_gmf_audio_param.c @@ -0,0 +1,102 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#include "esp_gmf_method_helper.h" +#include "esp_gmf_audio_param.h" +#include "esp_gmf_audio_methods_def.h" +#define PREPARE_AMETHOD_SETTING() \ + esp_gmf_method_exec_ctx_t exec_ctx = {}; \ + const esp_gmf_method_t* method_head = NULL; \ + esp_gmf_element_get_method(self, &method_head); \ + esp_gmf_err_t ret = esp_gmf_method_prepare_exec_ctx(method_head, method_name, &exec_ctx); \ + if (ret != ESP_GMF_ERR_OK) { \ + return ret; \ + } + +#define SET_AMETHOD_ARG(arg_name, value) \ + esp_gmf_args_set_value(exec_ctx.method->args_desc, arg_name, exec_ctx.exec_buf, (uint8_t*) &value, sizeof(value)); + +#define RELEASE_AMETHOD_SETTING() \ + ret = exec_ctx.method->func(self, exec_ctx.method->args_desc, exec_ctx.exec_buf, exec_ctx.buf_size); \ + esp_gmf_method_release_exec_ctx(&exec_ctx); \ + return ret; + +esp_gmf_err_t esp_gmf_audio_param_set_dest_rate(esp_gmf_element_handle_t self, uint32_t dest_rate) +{ + const char *method_name = AMETHOD(RATE_CVT, SET_DEST_RATE); + PREPARE_AMETHOD_SETTING(); + + SET_AMETHOD_ARG(AMETHOD_ARG(RATE_CVT, SET_DEST_RATE, RATE), dest_rate); + + RELEASE_AMETHOD_SETTING(); +} + +esp_gmf_err_t esp_gmf_audio_param_set_dest_bits(esp_gmf_element_handle_t self, uint8_t dest_bits) +{ + const char *method_name = AMETHOD(BIT_CVT, SET_DEST_BITS); + PREPARE_AMETHOD_SETTING(); + + SET_AMETHOD_ARG(AMETHOD_ARG(BIT_CVT, SET_DEST_BITS, BITS), dest_bits); + + RELEASE_AMETHOD_SETTING(); +} + +esp_gmf_err_t esp_gmf_audio_param_set_dest_ch(esp_gmf_element_handle_t self, uint8_t dest_ch) +{ + const char *method_name = AMETHOD(CH_CVT, SET_DEST_CH); + PREPARE_AMETHOD_SETTING(); + + SET_AMETHOD_ARG(AMETHOD_ARG(CH_CVT, SET_DEST_CH, CH), dest_ch); + + RELEASE_AMETHOD_SETTING(); +} + +esp_gmf_err_t esp_gmf_audio_param_set_speed(esp_gmf_element_handle_t self, float speed) +{ + const char *method_name = AMETHOD(SONIC, SET_SPEED); + PREPARE_AMETHOD_SETTING(); + + SET_AMETHOD_ARG(AMETHOD_ARG(SONIC, SET_SPEED, SPEED), speed); + + RELEASE_AMETHOD_SETTING(); +} + +esp_gmf_err_t esp_gmf_audio_param_set_pitch(esp_gmf_element_handle_t self, float pitch) +{ + const char *method_name = AMETHOD(SONIC, SET_PITCH); + PREPARE_AMETHOD_SETTING(); + + SET_AMETHOD_ARG(AMETHOD_ARG(SONIC, SET_PITCH, PITCH), pitch); + + RELEASE_AMETHOD_SETTING(); +} + +esp_gmf_err_t esp_gmf_audio_param_set_alc_channel_gain(esp_gmf_element_handle_t self, uint8_t ch_idx, float db) +{ + const char *method_name = AMETHOD(ALC, SET_GAIN); + // ALC currently only support int8 db + int8_t gain = (int8_t) db; + PREPARE_AMETHOD_SETTING(); + + SET_AMETHOD_ARG(AMETHOD_ARG(ALC, SET_GAIN, IDX), ch_idx); + SET_AMETHOD_ARG(AMETHOD_ARG(ALC, SET_GAIN, GAIN), gain); + + RELEASE_AMETHOD_SETTING(); +} + +esp_gmf_err_t esp_gmf_audio_param_set_fade(esp_gmf_element_handle_t self, bool is_fade_in) +{ + const char *method_name = AMETHOD(FADE, SET_MODE); + // Aligned with ESP_AE_FADE_MODE_FADE_IN = 1, ESP_AE_FADE_MODE_FADE_OUT = 2 + int32_t mode = is_fade_in ? 1 : 2; + + PREPARE_AMETHOD_SETTING(); + + SET_AMETHOD_ARG(AMETHOD_ARG(FADE, SET_MODE, MODE), mode); + + RELEASE_AMETHOD_SETTING(); +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/esp_gmf_bit_cvt.c b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/esp_gmf_bit_cvt.c new file mode 100644 index 0000000..29c5625 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/esp_gmf_bit_cvt.c @@ -0,0 +1,280 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#include +#include "esp_log.h" +#include "esp_gmf_node.h" +#include "esp_gmf_oal_mem.h" +#include "esp_gmf_oal_mutex.h" +#include "esp_gmf_bit_cvt.h" +#include "gmf_audio_common.h" +#include "esp_gmf_audio_methods_def.h" +#include "esp_gmf_cap.h" +#include "esp_gmf_caps_def.h" +#include "esp_gmf_audio_element.h" + +/** + * @brief Audio bit conversion context in GMF + */ +typedef struct { + esp_gmf_audio_element_t parent; /*!< The GMF bit cvt handle */ + esp_ae_bit_cvt_handle_t bit_hd; /*!< The audio effects bit cvt handle */ + uint8_t in_bytes_per_sample; /*!< Source bytes number of per sampling point */ + uint8_t out_bytes_per_sample; /*!< Dest bytes number of per sampling point */ + bool need_reopen : 1; /*!< Whether need to reopen. + True: Execute the close function first, then execute the open function + False: Do nothing */ + bool bypass : 1; /*!< Whether bypass. True: need bypass. False: needn't bypass */ +} esp_gmf_bit_cvt_t; + +static const char *TAG = "ESP_GMF_BIT_CVT"; + +static esp_gmf_err_t __set_dest_bits(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc, + uint8_t *buf, int buf_len) +{ + ESP_GMF_NULL_CHECK(TAG, buf, {return ESP_GMF_ERR_INVALID_ARG;}); + uint8_t dest_bits = (uint8_t)(*buf); + return esp_gmf_bit_cvt_set_dest_bits(handle, dest_bits); +} + +static esp_gmf_err_t esp_gmf_bit_cvt_new(void *cfg, esp_gmf_obj_handle_t *handle) +{ + return esp_gmf_bit_cvt_init(cfg, (esp_gmf_element_handle_t *)handle); +} + +static esp_gmf_job_err_t esp_gmf_bit_cvt_open(esp_gmf_element_handle_t self, void *para) +{ + esp_gmf_bit_cvt_t *bit_cvt = (esp_gmf_bit_cvt_t *)self; + esp_ae_bit_cvt_cfg_t *bit_info = (esp_ae_bit_cvt_cfg_t *)OBJ_GET_CFG(self); + ESP_GMF_NULL_CHECK(TAG, bit_info, {return ESP_GMF_JOB_ERR_FAIL;}); + esp_ae_bit_cvt_open(bit_info, &bit_cvt->bit_hd); + ESP_GMF_CHECK(TAG, bit_cvt->bit_hd, {return ESP_GMF_JOB_ERR_FAIL;}, "Failed to create bit conversion handle"); + bit_cvt->in_bytes_per_sample = (bit_info->src_bits >> 3) * bit_info->channel; + bit_cvt->out_bytes_per_sample = (bit_info->dest_bits >> 3) * bit_info->channel; + GMF_AUDIO_UPDATE_SND_INFO(self, bit_info->sample_rate, bit_info->dest_bits, bit_info->channel); + ESP_LOGD(TAG, "Open, rate: %ld, channel: %d, src_bits: %d, dest_bits: %d", + bit_info->sample_rate, bit_info->channel, bit_info->src_bits, bit_info->dest_bits); + bit_cvt->need_reopen = false; + bit_cvt->bypass = (bit_info->src_bits == bit_info->dest_bits); + return ESP_GMF_JOB_ERR_OK; +} + +static esp_gmf_job_err_t esp_gmf_bit_cvt_close(esp_gmf_element_handle_t self, void *para) +{ + esp_gmf_bit_cvt_t *bit_cvt = (esp_gmf_bit_cvt_t *)self; + ESP_LOGD(TAG, "Closed, %p", self); + if (bit_cvt->bit_hd != NULL) { + esp_ae_bit_cvt_close(bit_cvt->bit_hd); + bit_cvt->bit_hd = NULL; + } + return ESP_GMF_ERR_OK; +} + +static esp_gmf_job_err_t esp_gmf_bit_cvt_process(esp_gmf_element_handle_t self, void *para) +{ + esp_gmf_bit_cvt_t *bit_cvt = (esp_gmf_bit_cvt_t *)self; + esp_gmf_job_err_t out_len = ESP_GMF_JOB_ERR_OK; + if (bit_cvt->need_reopen) { + esp_gmf_bit_cvt_close(self, NULL); + out_len = esp_gmf_bit_cvt_open(self, NULL); + if (out_len != ESP_GMF_JOB_ERR_OK) { + ESP_LOGE(TAG, "Bit conversion reopen failed"); + return out_len; + } + } + esp_gmf_port_handle_t in_port = ESP_GMF_ELEMENT_GET(self)->in; + esp_gmf_port_handle_t out_port = ESP_GMF_ELEMENT_GET(self)->out; + esp_gmf_payload_t *in_load = NULL; + esp_gmf_payload_t *out_load = NULL; + int samples_num = ESP_GMF_ELEMENT_GET(bit_cvt)->in_attr.data_size / (bit_cvt->in_bytes_per_sample); + int bytes = samples_num * bit_cvt->in_bytes_per_sample; + esp_gmf_err_io_t load_ret = esp_gmf_port_acquire_in(in_port, &in_load, bytes, ESP_GMF_MAX_DELAY); + if (load_ret < ESP_GMF_IO_OK) { + ESP_LOGE(TAG, "ACQ IN, ret: %d", load_ret); + out_len = ESP_GMF_JOB_ERR_FAIL; + goto __bit_release; + } + samples_num = in_load->valid_size / (bit_cvt->in_bytes_per_sample); + bytes = samples_num * bit_cvt->out_bytes_per_sample; + if ((bit_cvt->in_bytes_per_sample * samples_num != in_load->valid_size) || (load_ret < ESP_GMF_IO_OK)) { + ESP_LOGE(TAG, "Invalid in load size %d, ret %d", in_load->valid_size, load_ret); + out_len = ESP_GMF_JOB_ERR_FAIL; + goto __bit_release; + } + if ((bit_cvt->bypass) && (in_port->is_shared == 1)) { + // This case bit conversion is do bypass + out_load = in_load; + } + load_ret = esp_gmf_port_acquire_out(out_port, &out_load, samples_num ? bytes : in_load->buf_length, ESP_GMF_MAX_DELAY); + ESP_GMF_PORT_ACQUIRE_OUT_CHECK(TAG, load_ret, out_len, { goto __bit_release;}); + if (samples_num) { + esp_ae_err_t ret = esp_ae_bit_cvt_process(bit_cvt->bit_hd, samples_num, (unsigned char *)in_load->buf, (unsigned char *)out_load->buf); + ESP_GMF_RET_ON_ERROR(TAG, ret, {out_len = ESP_GMF_JOB_ERR_FAIL; goto __bit_release;}, "Bit conversion process error, ret: %d", ret); + } + ESP_LOGV(TAG, "Samples: %d, IN-PLD: %p-%p-%d-%d-%d, OUT-PLD: %p-%p-%d-%d-%d", + samples_num, in_load, in_load->buf, in_load->valid_size, in_load->buf_length, in_load->is_done, + out_load, out_load->buf, out_load->valid_size, out_load->buf_length, out_load->is_done); + out_load->valid_size = samples_num * bit_cvt->out_bytes_per_sample; + out_load->pts = in_load->pts; + out_load->is_done = in_load->is_done; + if (out_load->valid_size > 0) { + esp_gmf_audio_el_update_file_pos((esp_gmf_element_handle_t)self, out_load->valid_size); + } + if (in_load->is_done) { + out_len = ESP_GMF_JOB_ERR_DONE; + ESP_LOGD(TAG, "Bit conversion is done, out len: %d", out_load->valid_size); + } +__bit_release: + // Release in and out port + if (out_load != NULL) { + load_ret = esp_gmf_port_release_out(out_port, out_load, ESP_GMF_MAX_DELAY); + if ((load_ret < ESP_GMF_IO_OK) && (load_ret != ESP_GMF_IO_ABORT)) { + ESP_LOGE(TAG, "OUT port release error, ret:%d", load_ret); + out_len = ESP_GMF_JOB_ERR_FAIL; + } + } + if (in_load != NULL) { + load_ret = esp_gmf_port_release_in(in_port, in_load, ESP_GMF_MAX_DELAY); + if ((load_ret < ESP_GMF_IO_OK) && (load_ret != ESP_GMF_IO_ABORT)) { + ESP_LOGE(TAG, "IN port release error, ret:%d", load_ret); + out_len = ESP_GMF_JOB_ERR_FAIL; + } + } + return out_len; +} + +static esp_gmf_err_t bit_cvt_received_event_handler(esp_gmf_event_pkt_t *evt, void *ctx) +{ + ESP_GMF_NULL_CHECK(TAG, ctx, {return ESP_GMF_ERR_INVALID_ARG;}); + ESP_GMF_NULL_CHECK(TAG, evt, {return ESP_GMF_ERR_INVALID_ARG;}); + if ((evt->type != ESP_GMF_EVT_TYPE_REPORT_INFO) + || (evt->sub != ESP_GMF_INFO_SOUND) + || (evt->payload == NULL)) { + return ESP_GMF_ERR_OK; + } + esp_gmf_element_handle_t self = (esp_gmf_element_handle_t)ctx; + esp_gmf_element_handle_t el = evt->from; + esp_gmf_event_state_t state = ESP_GMF_EVENT_STATE_NONE; + esp_gmf_element_get_state(self, &state); + esp_gmf_info_sound_t *info = (esp_gmf_info_sound_t *)evt->payload; + esp_ae_bit_cvt_cfg_t *config = (esp_ae_bit_cvt_cfg_t *)OBJ_GET_CFG(self); + ESP_GMF_NULL_CHECK(TAG, config, return ESP_GMF_ERR_FAIL); + esp_gmf_bit_cvt_t *bit_cvt = (esp_gmf_bit_cvt_t *)self; + bit_cvt->need_reopen = (config->sample_rate != info->sample_rates) || (info->channels != config->channel) || (config->src_bits != info->bits); + config->sample_rate = info->sample_rates; + config->channel = info->channels; + config->src_bits = info->bits; + ESP_LOGD(TAG, "RECV info, from: %s-%p, next: %p, self: %s-%p, type: %x, state: %s, rate: %d, ch: %d, bits: %d", + OBJ_GET_TAG(el), el, esp_gmf_node_for_next((esp_gmf_node_t *)el), OBJ_GET_TAG(self), self, evt->type, + esp_gmf_event_get_state_str(state), info->sample_rates, info->channels, info->bits); + if (state == ESP_GMF_EVENT_STATE_NONE) { + esp_gmf_element_set_state(self, ESP_GMF_EVENT_STATE_INITIALIZED); + } + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t esp_gmf_bit_cvt_destroy(esp_gmf_element_handle_t self) +{ + esp_gmf_bit_cvt_t *bit_cvt = (esp_gmf_bit_cvt_t *)self; + ESP_LOGD(TAG, "Destroyed, %p", self); + void *cfg = OBJ_GET_CFG(self); + if (cfg) { + esp_gmf_oal_free(cfg); + } + esp_gmf_audio_el_deinit(self); + esp_gmf_oal_free(bit_cvt); + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t _load_bit_cvt_caps_func(esp_gmf_element_handle_t handle) +{ + esp_gmf_cap_t *caps = NULL; + esp_gmf_cap_t dec_caps = {0}; + dec_caps.cap_eightcc = ESP_GMF_CAPS_AUDIO_BIT_CONVERT; + dec_caps.attr_fun = NULL; + int ret = esp_gmf_cap_append(&caps, &dec_caps); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to create capability"); + + esp_gmf_element_t *el = (esp_gmf_element_t *)handle; + el->caps = caps; + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t _load_bit_cvt_methods_func(esp_gmf_element_handle_t handle) +{ + esp_gmf_method_t *method = NULL; + esp_gmf_args_desc_t *set_args = NULL; + esp_gmf_err_t ret = esp_gmf_args_desc_append(&set_args, AMETHOD_ARG(BIT_CVT, SET_DEST_BITS, BITS), + ESP_GMF_ARGS_TYPE_UINT8, sizeof(uint8_t), 0); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to append argument"); + ret = esp_gmf_method_append(&method, AMETHOD(BIT_CVT, SET_DEST_BITS), __set_dest_bits, set_args); + ESP_GMF_RET_ON_ERROR(TAG, ret, {return ret;}, "Failed to register %s method", AMETHOD(BIT_CVT, SET_DEST_BITS)); + + esp_gmf_element_t *el = (esp_gmf_element_t *)handle; + el->method = method; + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_bit_cvt_set_dest_bits(esp_gmf_element_handle_t handle, uint8_t dest_bits) +{ + ESP_GMF_NULL_CHECK(TAG, handle, { return ESP_GMF_ERR_INVALID_ARG;}); + esp_ae_bit_cvt_cfg_t *cfg = (esp_ae_bit_cvt_cfg_t *)OBJ_GET_CFG(handle); + if (cfg) { + if (cfg->dest_bits == dest_bits) { + return ESP_GMF_ERR_OK; + } + cfg->dest_bits = dest_bits; + esp_gmf_bit_cvt_t *bit_cvt = (esp_gmf_bit_cvt_t *)handle; + bit_cvt->need_reopen = true; + return ESP_GMF_ERR_OK; + } + ESP_LOGE(TAG, "Failed to set dest bits, cfg is NULL"); + return ESP_GMF_ERR_FAIL; +} + +esp_gmf_err_t esp_gmf_bit_cvt_init(esp_ae_bit_cvt_cfg_t *config, esp_gmf_element_handle_t *handle) +{ + ESP_GMF_NULL_CHECK(TAG, handle, {return ESP_GMF_ERR_INVALID_ARG;}); + *handle = NULL; + esp_gmf_err_t ret = ESP_GMF_ERR_OK; + esp_gmf_bit_cvt_t *bit_cvt = esp_gmf_oal_calloc(1, sizeof(esp_gmf_bit_cvt_t)); + ESP_GMF_MEM_VERIFY(TAG, bit_cvt, {return ESP_GMF_ERR_MEMORY_LACK;}, "bit conversion", sizeof(esp_gmf_bit_cvt_t)); + esp_gmf_obj_t *obj = (esp_gmf_obj_t *)bit_cvt; + obj->new_obj = esp_gmf_bit_cvt_new; + obj->del_obj = esp_gmf_bit_cvt_destroy; + esp_ae_bit_cvt_cfg_t *cfg = esp_gmf_oal_calloc(1, sizeof(esp_ae_bit_cvt_cfg_t)); + ESP_GMF_MEM_VERIFY(TAG, cfg, {ret = ESP_GMF_ERR_MEMORY_LACK; goto BIT_CVT_INIT_FAIL;}, "bit conversion configuration", sizeof(esp_ae_bit_cvt_cfg_t)); + esp_gmf_obj_set_config(obj, cfg, sizeof(esp_ae_bit_cvt_cfg_t)); + if (config) { + memcpy(cfg, config, sizeof(esp_ae_bit_cvt_cfg_t)); + } else { + esp_ae_bit_cvt_cfg_t dcfg = DEFAULT_ESP_GMF_BIT_CVT_CONFIG(); + memcpy(cfg, &dcfg, sizeof(esp_ae_bit_cvt_cfg_t)); + } + ret = esp_gmf_obj_set_tag(obj, "aud_bit_cvt"); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto BIT_CVT_INIT_FAIL, "Failed to set obj tag"); + esp_gmf_element_cfg_t el_cfg = {0}; + ESP_GMF_ELEMENT_IN_PORT_ATTR_SET(el_cfg.in_attr, ESP_GMF_EL_PORT_CAP_SINGLE, 0, 0, + ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, ESP_GMF_ELEMENT_PORT_DATA_SIZE_DEFAULT); + ESP_GMF_ELEMENT_IN_PORT_ATTR_SET(el_cfg.out_attr, ESP_GMF_EL_PORT_CAP_SINGLE, 0, 0, + ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, ESP_GMF_ELEMENT_PORT_DATA_SIZE_DEFAULT); + el_cfg.dependency = true; + ret = esp_gmf_audio_el_init(bit_cvt, &el_cfg); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto BIT_CVT_INIT_FAIL, "Failed to initialize bit conversion element"); + ESP_GMF_ELEMENT_GET(bit_cvt)->ops.open = esp_gmf_bit_cvt_open; + ESP_GMF_ELEMENT_GET(bit_cvt)->ops.process = esp_gmf_bit_cvt_process; + ESP_GMF_ELEMENT_GET(bit_cvt)->ops.close = esp_gmf_bit_cvt_close; + ESP_GMF_ELEMENT_GET(bit_cvt)->ops.event_receiver = bit_cvt_received_event_handler; + ESP_GMF_ELEMENT_GET(bit_cvt)->ops.load_caps = _load_bit_cvt_caps_func; + ESP_GMF_ELEMENT_GET(bit_cvt)->ops.load_methods = _load_bit_cvt_methods_func; + *handle = obj; + ESP_LOGD(TAG, "Initialization, %s-%p", OBJ_GET_TAG(obj), obj); + return ESP_GMF_ERR_OK; +BIT_CVT_INIT_FAIL: + esp_gmf_bit_cvt_destroy(obj); + return ret; +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/esp_gmf_ch_cvt.c b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/esp_gmf_ch_cvt.c new file mode 100644 index 0000000..9e65c5d --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/esp_gmf_ch_cvt.c @@ -0,0 +1,304 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#include +#include "esp_log.h" +#include "esp_gmf_node.h" +#include "esp_gmf_oal_mem.h" +#include "esp_gmf_oal_mutex.h" +#include "esp_gmf_ch_cvt.h" +#include "gmf_audio_common.h" +#include "esp_gmf_audio_methods_def.h" +#include "esp_gmf_cap.h" +#include "esp_gmf_caps_def.h" +#include "esp_gmf_audio_element.h" + +/** + * @brief Audio channel conversion context in GMF + */ +typedef struct { + esp_gmf_audio_element_t parent; /*!< The GMF channel cvt handle */ + esp_ae_ch_cvt_handle_t ch_hd; /*!< The audio effects channel cvt handle */ + uint8_t in_bytes_per_sample; /*!< Source bytes number of per sampling point */ + uint8_t out_bytes_per_sample; /*!< Dest bytes number of per sampling point */ + bool need_reopen : 1; /*!< Whether need to reopen. + True: Execute the close function first, then execute the open function + False: Do nothing */ + bool bypass : 1; /*!< Whether bypass. True: need bypass. False: needn't bypass */ +} esp_gmf_ch_cvt_t; + +static const char *TAG = "ESP_GMF_CH_CVT"; + +static inline esp_gmf_err_t dupl_esp_ae_ch_cvt_cfg(esp_ae_ch_cvt_cfg_t *config, esp_ae_ch_cvt_cfg_t **new_config) +{ + void *sub_cfg = NULL; + *new_config = esp_gmf_oal_calloc(1, sizeof(*config)); + ESP_GMF_MEM_VERIFY(TAG, *new_config, {return ESP_GMF_ERR_MEMORY_LACK;}, "channel conversion configuration", sizeof(*config)); + memcpy(*new_config, config, sizeof(*config)); + if (config->weight && (config->weight_len > 0)) { + sub_cfg = esp_gmf_oal_calloc(1, config->weight_len); + ESP_GMF_MEM_VERIFY(TAG, sub_cfg, {esp_gmf_oal_free(*new_config); return ESP_GMF_ERR_MEMORY_LACK;}, + "weight array", (int)config->weight_len); + memcpy(sub_cfg, config->weight, config->weight_len); + (*new_config)->weight = sub_cfg; + } + return ESP_GMF_JOB_ERR_OK; +} + +static inline void free_esp_ae_ch_cvt_cfg(esp_ae_ch_cvt_cfg_t *config) +{ + if (config) { + if (config->weight) { + esp_gmf_oal_free(config->weight); + config->weight = NULL; + config->weight_len = 0; + } + esp_gmf_oal_free(config); + } +} + +static esp_gmf_err_t __set_dest_ch(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc, + uint8_t *buf, int buf_len) +{ + ESP_GMF_NULL_CHECK(TAG, buf, {return ESP_GMF_ERR_INVALID_ARG;}); + uint8_t dest_ch = (uint8_t)(*buf); + return esp_gmf_ch_cvt_set_dest_channel(handle, dest_ch); +} + +static esp_gmf_err_t esp_gmf_ch_cvt_new(void *cfg, esp_gmf_obj_handle_t *handle) +{ + return esp_gmf_ch_cvt_init(cfg, (esp_gmf_element_handle_t *)handle); +} + +static esp_gmf_job_err_t esp_gmf_ch_cvt_open(esp_gmf_element_handle_t self, void *para) +{ + esp_gmf_ch_cvt_t *ch_cvt = (esp_gmf_ch_cvt_t *)self; + esp_ae_ch_cvt_cfg_t *ch_info = (esp_ae_ch_cvt_cfg_t *)OBJ_GET_CFG(self); + ESP_GMF_NULL_CHECK(TAG, ch_info, {return ESP_GMF_JOB_ERR_FAIL;}); + esp_ae_ch_cvt_open(ch_info, &ch_cvt->ch_hd); + ESP_GMF_CHECK(TAG, ch_cvt->ch_hd, {return ESP_GMF_JOB_ERR_FAIL;}, "Failed to create channel conversion handle"); + ch_cvt->in_bytes_per_sample = (ch_info->bits_per_sample >> 3) * ch_info->src_ch; + ch_cvt->out_bytes_per_sample = (ch_info->bits_per_sample >> 3) * ch_info->dest_ch; + GMF_AUDIO_UPDATE_SND_INFO(self, ch_info->sample_rate, ch_info->bits_per_sample, ch_info->dest_ch); + ESP_LOGD(TAG, "Open, rate: %ld, bits: %d, src_channel: %d, dest_channel: %d", + ch_info->sample_rate, ch_info->bits_per_sample, ch_info->src_ch, ch_info->dest_ch); + ch_cvt->need_reopen = false; + ch_cvt->bypass = ch_info->src_ch == ch_info->dest_ch; + return ESP_GMF_JOB_ERR_OK; +} + +static esp_gmf_job_err_t esp_gmf_ch_cvt_close(esp_gmf_element_handle_t self, void *para) +{ + esp_gmf_ch_cvt_t *ch_cvt = (esp_gmf_ch_cvt_t *)self; + ESP_LOGD(TAG, "Closed, %p", self); + if (ch_cvt->ch_hd != NULL) { + esp_ae_ch_cvt_close(ch_cvt->ch_hd); + ch_cvt->ch_hd = NULL; + } + return ESP_GMF_JOB_ERR_OK; +} + +static esp_gmf_job_err_t esp_gmf_ch_cvt_process(esp_gmf_element_handle_t self, void *para) +{ + esp_gmf_ch_cvt_t *ch_cvt = (esp_gmf_ch_cvt_t *)self; + esp_gmf_job_err_t out_len = ESP_GMF_JOB_ERR_OK; + if (ch_cvt->need_reopen) { + esp_gmf_ch_cvt_close(self, NULL); + out_len = esp_gmf_ch_cvt_open(self, NULL); + if (out_len != ESP_GMF_JOB_ERR_OK) { + ESP_LOGE(TAG, "Channel conversion reopen failed"); + return out_len; + } + } + esp_gmf_port_handle_t in_port = ESP_GMF_ELEMENT_GET(self)->in; + esp_gmf_port_handle_t out_port = ESP_GMF_ELEMENT_GET(self)->out; + esp_gmf_payload_t *in_load = NULL; + esp_gmf_payload_t *out_load = NULL; + int samples_num = ESP_GMF_ELEMENT_GET(ch_cvt)->in_attr.data_size / (ch_cvt->in_bytes_per_sample); + int bytes = samples_num * ch_cvt->in_bytes_per_sample; + esp_gmf_err_io_t load_ret = esp_gmf_port_acquire_in(in_port, &in_load, bytes, ESP_GMF_MAX_DELAY); + if (load_ret < ESP_GMF_IO_OK) { + ESP_LOGE(TAG, "ACQ IN, ret: %d", load_ret); + out_len = ESP_GMF_JOB_ERR_FAIL; + goto __ch_release; + } + samples_num = in_load->valid_size / (ch_cvt->in_bytes_per_sample); + bytes = samples_num * ch_cvt->out_bytes_per_sample; + if ((ch_cvt->in_bytes_per_sample * samples_num != in_load->valid_size) || (load_ret < ESP_GMF_IO_OK)) { + ESP_LOGE(TAG, "Invalid in load size %d, ret %d", in_load->valid_size, load_ret); + out_len = ESP_GMF_JOB_ERR_FAIL; + goto __ch_release; + } + if (ch_cvt->bypass && (in_port->is_shared == true)) { + // This case channel conversion is do bypass + out_load = in_load; + } + load_ret = esp_gmf_port_acquire_out(out_port, &out_load, samples_num ? bytes : in_load->buf_length, ESP_GMF_MAX_DELAY); + ESP_GMF_PORT_ACQUIRE_OUT_CHECK(TAG, load_ret, out_len, { goto __ch_release;}); + if (samples_num) { + esp_ae_err_t ret = esp_ae_ch_cvt_process(ch_cvt->ch_hd, samples_num, (unsigned char *)in_load->buf, (unsigned char *)out_load->buf); + ESP_GMF_RET_ON_ERROR(TAG, ret, {out_len = ESP_GMF_JOB_ERR_FAIL; goto __ch_release;}, "Channel conversion process error, ret: %d", ret); + } + out_load->valid_size = samples_num * ch_cvt->out_bytes_per_sample; + out_load->pts = in_load->pts; + out_load->is_done = in_load->is_done; + ESP_LOGV(TAG, "Samples: %d, IN-PLD: %p-%p-%d-%d-%d, OUT-PLD: %p-%p-%d-%d-%d", + samples_num, in_load, in_load->buf, in_load->valid_size, in_load->buf_length, in_load->is_done, + out_load, out_load->buf, out_load->valid_size, out_load->buf_length, out_load->is_done); + if (out_load->valid_size > 0) { + esp_gmf_audio_el_update_file_pos((esp_gmf_element_handle_t)self, out_load->valid_size); + } + if (in_load->is_done) { + out_len = ESP_GMF_JOB_ERR_DONE; + ESP_LOGD(TAG, "The channel cvt done, out len: %d", out_load->valid_size); + } +__ch_release: + // Release in and out port + if (out_load != NULL) { + load_ret = esp_gmf_port_release_out(out_port, out_load, ESP_GMF_MAX_DELAY); + if ((load_ret < ESP_GMF_IO_OK) && (load_ret != ESP_GMF_IO_ABORT)) { + ESP_LOGE(TAG, "OUT port release error, ret:%d", load_ret); + out_len = ESP_GMF_JOB_ERR_FAIL; + } + } + if (in_load != NULL) { + load_ret = esp_gmf_port_release_in(in_port, in_load, ESP_GMF_MAX_DELAY); + if ((load_ret < ESP_GMF_IO_OK) && (load_ret != ESP_GMF_IO_ABORT)) { + ESP_LOGE(TAG, "IN port release error, ret:%d", load_ret); + out_len = ESP_GMF_JOB_ERR_FAIL; + } + } + return out_len; +} + +static esp_gmf_err_t ch_cvt_received_event_handler(esp_gmf_event_pkt_t *evt, void *ctx) +{ + ESP_GMF_NULL_CHECK(TAG, ctx, {return ESP_GMF_ERR_INVALID_ARG;}); + ESP_GMF_NULL_CHECK(TAG, evt, {return ESP_GMF_ERR_INVALID_ARG;}); + if ((evt->type != ESP_GMF_EVT_TYPE_REPORT_INFO) + || (evt->sub != ESP_GMF_INFO_SOUND) + || (evt->payload == NULL)) { + return ESP_GMF_ERR_OK; + } + esp_gmf_element_handle_t self = (esp_gmf_element_handle_t)ctx; + esp_gmf_element_handle_t el = evt->from; + esp_gmf_event_state_t state = ESP_GMF_EVENT_STATE_NONE; + esp_gmf_element_get_state(self, &state); + esp_gmf_info_sound_t *info = (esp_gmf_info_sound_t *)evt->payload; + esp_ae_ch_cvt_cfg_t *config = (esp_ae_ch_cvt_cfg_t *)OBJ_GET_CFG(self); + ESP_GMF_NULL_CHECK(TAG, config, return ESP_GMF_ERR_FAIL); + esp_gmf_ch_cvt_t *ch_cvt = (esp_gmf_ch_cvt_t *)self; + ch_cvt->need_reopen = (config->sample_rate != info->sample_rates) || (info->channels != config->src_ch) || (config->bits_per_sample != info->bits); + config->sample_rate = info->sample_rates; + config->src_ch = info->channels; + config->bits_per_sample = info->bits; + ESP_LOGD(TAG, "RECV info, from: %s-%p, next: %p, self: %s-%p, type: %x, state: %s, rate: %d, ch: %d, bits: %d", + OBJ_GET_TAG(el), el, esp_gmf_node_for_next((esp_gmf_node_t *)el), OBJ_GET_TAG(self), self, evt->type, + esp_gmf_event_get_state_str(state), info->sample_rates, info->channels, info->bits); + if (state == ESP_GMF_EVENT_STATE_NONE) { + esp_gmf_element_set_state(self, ESP_GMF_EVENT_STATE_INITIALIZED); + } + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t esp_gmf_ch_cvt_destroy(esp_gmf_element_handle_t self) +{ + esp_gmf_ch_cvt_t *ch_cvt = (esp_gmf_ch_cvt_t *)self; + ESP_LOGD(TAG, "Destroyed, %p", self); + free_esp_ae_ch_cvt_cfg(OBJ_GET_CFG(self)); + esp_gmf_audio_el_deinit(self); + esp_gmf_oal_free(ch_cvt); + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t _load_channel_cvt_caps_func(esp_gmf_element_handle_t handle) +{ + esp_gmf_cap_t *caps = NULL; + esp_gmf_cap_t dec_caps = {0}; + dec_caps.cap_eightcc = ESP_GMF_CAPS_AUDIO_CHANNEL_CONVERT; + dec_caps.attr_fun = NULL; + int ret = esp_gmf_cap_append(&caps, &dec_caps); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to create capability"); + + esp_gmf_element_t *el = (esp_gmf_element_t *)handle; + el->caps = caps; + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t _load_channel_cvt_methods_func(esp_gmf_element_handle_t handle) +{ + esp_gmf_method_t *method = NULL; + esp_gmf_args_desc_t *set_args = NULL; + esp_gmf_err_t ret = esp_gmf_args_desc_append(&set_args, AMETHOD_ARG(CH_CVT, SET_DEST_CH, CH), ESP_GMF_ARGS_TYPE_UINT8, sizeof(uint8_t), 0); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to append argument"); + ret = esp_gmf_method_append(&method, AMETHOD(CH_CVT, SET_DEST_CH), __set_dest_ch, set_args); + ESP_GMF_RET_ON_ERROR(TAG, ret, {return ret;}, "Failed to register %s method", AMETHOD(CH_CVT, SET_DEST_CH)); + + esp_gmf_element_t *el = (esp_gmf_element_t *)handle; + el->method = method; + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_ch_cvt_set_dest_channel(esp_gmf_element_handle_t handle, uint8_t dest_ch) +{ + ESP_GMF_NULL_CHECK(TAG, handle, { return ESP_GMF_ERR_INVALID_ARG;}); + esp_ae_ch_cvt_cfg_t *cfg = (esp_ae_ch_cvt_cfg_t *)OBJ_GET_CFG(handle); + if (cfg) { + if (cfg->dest_ch == dest_ch) { + return ESP_GMF_ERR_OK; + } + cfg->dest_ch = dest_ch; + esp_gmf_ch_cvt_t *ch_cvt = (esp_gmf_ch_cvt_t *)handle; + ch_cvt->need_reopen = true; + return ESP_GMF_ERR_OK; + } + ESP_LOGE(TAG, "Failed to set dest channel, cfg is NULL"); + return ESP_GMF_ERR_FAIL; +} + +esp_gmf_err_t esp_gmf_ch_cvt_init(esp_ae_ch_cvt_cfg_t *config, esp_gmf_element_handle_t *handle) +{ + ESP_GMF_NULL_CHECK(TAG, handle, {return ESP_GMF_ERR_INVALID_ARG;}); + *handle = NULL; + esp_gmf_err_t ret = ESP_GMF_ERR_OK; + esp_gmf_ch_cvt_t *ch_cvt = esp_gmf_oal_calloc(1, sizeof(esp_gmf_ch_cvt_t)); + ESP_GMF_MEM_VERIFY(TAG, ch_cvt, {return ESP_GMF_ERR_MEMORY_LACK;}, "channel conversion", sizeof(esp_gmf_ch_cvt_t)); + esp_gmf_obj_t *obj = (esp_gmf_obj_t *)ch_cvt; + obj->new_obj = esp_gmf_ch_cvt_new; + obj->del_obj = esp_gmf_ch_cvt_destroy; + esp_ae_ch_cvt_cfg_t *cfg = NULL; + if (config) { + dupl_esp_ae_ch_cvt_cfg(config, &cfg); + } else { + esp_ae_ch_cvt_cfg_t dcfg = DEFAULT_ESP_GMF_CH_CVT_CONFIG(); + dupl_esp_ae_ch_cvt_cfg(&dcfg, &cfg); + } + ESP_GMF_CHECK(TAG, cfg, {ret = ESP_GMF_ERR_MEMORY_LACK; goto CH_CVT_INIT_FAIL;}, "Failed to allocate channel conversion configuration"); + esp_gmf_obj_set_config(obj, cfg, sizeof(esp_ae_ch_cvt_cfg_t)); + ret = esp_gmf_obj_set_tag(obj, "aud_ch_cvt"); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto CH_CVT_INIT_FAIL, "Failed to set obj tag"); + esp_gmf_element_cfg_t el_cfg = {0}; + ESP_GMF_ELEMENT_IN_PORT_ATTR_SET(el_cfg.in_attr, ESP_GMF_EL_PORT_CAP_SINGLE, 0, 0, + ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, ESP_GMF_ELEMENT_PORT_DATA_SIZE_DEFAULT); + ESP_GMF_ELEMENT_IN_PORT_ATTR_SET(el_cfg.out_attr, ESP_GMF_EL_PORT_CAP_SINGLE, 0, 0, + ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, ESP_GMF_ELEMENT_PORT_DATA_SIZE_DEFAULT); + el_cfg.dependency = true; + ret = esp_gmf_audio_el_init(ch_cvt, &el_cfg); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto CH_CVT_INIT_FAIL, "Failed to initialize channel conversion element"); + ESP_GMF_ELEMENT_GET(ch_cvt)->ops.open = esp_gmf_ch_cvt_open; + ESP_GMF_ELEMENT_GET(ch_cvt)->ops.process = esp_gmf_ch_cvt_process; + ESP_GMF_ELEMENT_GET(ch_cvt)->ops.close = esp_gmf_ch_cvt_close; + ESP_GMF_ELEMENT_GET(ch_cvt)->ops.event_receiver = ch_cvt_received_event_handler; + ESP_GMF_ELEMENT_GET(ch_cvt)->ops.load_caps = _load_channel_cvt_caps_func; + ESP_GMF_ELEMENT_GET(ch_cvt)->ops.load_methods = _load_channel_cvt_methods_func; + *handle = obj; + ESP_LOGD(TAG, "Initialization, %s-%p", OBJ_GET_TAG(obj), obj); + return ESP_GMF_ERR_OK; +CH_CVT_INIT_FAIL: + esp_gmf_ch_cvt_destroy(obj); + return ret; +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/esp_gmf_deinterleave.c b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/esp_gmf_deinterleave.c new file mode 100644 index 0000000..bb36603 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/esp_gmf_deinterleave.c @@ -0,0 +1,274 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#include +#include "esp_log.h" +#include "esp_gmf_oal_mem.h" +#include "esp_gmf_oal_mutex.h" +#include "esp_gmf_node.h" +#include "esp_gmf_deinterleave.h" +#include "esp_ae_data_weaver.h" +#include "esp_heap_caps.h" +#include "gmf_audio_common.h" +#include "esp_gmf_cap.h" +#include "esp_gmf_caps_def.h" +#include "esp_gmf_audio_element.h" + +#define ESP_GMF_PROCESS_SAMPLE (256) + +/** + * @brief Audio deinterleave context in GMF + */ +typedef struct { + esp_gmf_audio_element_t parent; /*!< The GMF deinterleave handle */ + uint8_t bytes_per_sample; /*!< Bytes number of per sampling point */ + esp_gmf_payload_t *in_load; /*!< The input payload */ + esp_gmf_payload_t **out_load; /*!< The array of output payload */ + uint8_t **out_arr; /*!< The array of output buffer pointer */ + uint8_t channel; /*!< The audio channel */ + uint8_t bits_per_sample; /*!< Bits number of per sampling point */ + bool need_reopen : 1; /*!< Whether need to reopen. + True: Execute the close function first, then execute the open function + False: Do nothing */ +} esp_gmf_deinterleave_t; + +static const char *TAG = "ESP_GMF_DEINTLV"; + +static inline esp_gmf_err_t dupl_esp_ae_deinterleave_cfg(esp_gmf_deinterleave_cfg *config, esp_gmf_deinterleave_cfg **new_config) +{ + *new_config = esp_gmf_oal_calloc(1, sizeof(*config)); + ESP_GMF_MEM_VERIFY(TAG, *new_config, {return ESP_GMF_ERR_MEMORY_LACK;}, "deinterleave configuration", sizeof(*config)); + memcpy(*new_config, config, sizeof(*config)); + return ESP_GMF_JOB_ERR_OK; +} + +static inline void free_esp_ae_deinterleave_cfg(esp_gmf_deinterleave_cfg *config) +{ + if (config) { + esp_gmf_oal_free(config); + } +} + +static esp_gmf_err_t esp_gmf_deinterleave_new(void *cfg, esp_gmf_obj_handle_t *handle) +{ + return esp_gmf_deinterleave_init(cfg, (esp_gmf_element_handle_t *)handle); +} + +static esp_gmf_job_err_t esp_gmf_deinterleave_open(esp_gmf_element_handle_t self, void *para) +{ + esp_gmf_deinterleave_t *deinterleave = (esp_gmf_deinterleave_t *)self; + esp_gmf_deinterleave_cfg *deinterleave_info = (esp_gmf_deinterleave_cfg *)OBJ_GET_CFG(self); + ESP_GMF_NULL_CHECK(TAG, deinterleave_info, {return ESP_GMF_JOB_ERR_FAIL;}) + deinterleave->bytes_per_sample = (deinterleave_info->bits_per_sample >> 3); + deinterleave->out_load = esp_gmf_oal_calloc(1, sizeof(esp_gmf_payload_t *) * deinterleave_info->channel); + ESP_GMF_MEM_VERIFY(TAG, deinterleave->out_load, {return ESP_GMF_JOB_ERR_FAIL;}, + "out load", sizeof(esp_gmf_payload_t *) * deinterleave_info->channel); + deinterleave->out_arr = esp_gmf_oal_calloc(1, sizeof(uint8_t *) * deinterleave_info->channel); + ESP_GMF_MEM_VERIFY(TAG, deinterleave->out_arr, {return ESP_GMF_JOB_ERR_FAIL;}, + "out buffer array", sizeof(uint8_t *) * deinterleave_info->channel); + GMF_AUDIO_UPDATE_SND_INFO(self, deinterleave_info->sample_rate, deinterleave_info->bits_per_sample, 1); + deinterleave->channel = deinterleave_info->channel; + deinterleave->bits_per_sample = deinterleave_info->bits_per_sample; + deinterleave->need_reopen = false; + ESP_LOGD(TAG, "Open, %p", self); + return ESP_GMF_ERR_OK; +} + +static esp_gmf_job_err_t esp_gmf_deinterleave_close(esp_gmf_element_handle_t self, void *para) +{ + esp_gmf_deinterleave_t *deinterleave = (esp_gmf_deinterleave_t *)self; + ESP_LOGD(TAG, "Closed, %p", self); + if (deinterleave->out_arr != NULL) { + esp_gmf_oal_free(deinterleave->out_arr); + deinterleave->out_arr = NULL; + } + if (deinterleave->out_load != NULL) { + esp_gmf_oal_free(deinterleave->out_load); + deinterleave->out_load = NULL; + } + return ESP_GMF_ERR_OK; +} + +static esp_gmf_job_err_t esp_gmf_deinterleave_process(esp_gmf_element_handle_t self, void *para) +{ + esp_gmf_deinterleave_t *deinterleave = (esp_gmf_deinterleave_t *)self; + esp_gmf_job_err_t out_len = ESP_GMF_JOB_ERR_OK; + int i = 0; + if (deinterleave->need_reopen) { + esp_gmf_deinterleave_close(self, NULL); + out_len = esp_gmf_deinterleave_open(self, NULL); + if (out_len != ESP_GMF_JOB_ERR_OK) { + ESP_LOGE(TAG, "deinterleave reopen failed"); + return out_len; + } + } + esp_gmf_port_handle_t in_port = ESP_GMF_ELEMENT_GET(self)->in; + esp_gmf_port_handle_t out = ESP_GMF_ELEMENT_GET(self)->out; + esp_gmf_port_handle_t out_port = out; + deinterleave->in_load = NULL; + memset(deinterleave->out_load, 0, sizeof(esp_gmf_payload_t *) * deinterleave->channel); + int samples_num = ESP_GMF_ELEMENT_GET(deinterleave)->in_attr.data_size / (deinterleave->bytes_per_sample * deinterleave->channel); + int bytes = samples_num * deinterleave->bytes_per_sample * deinterleave->channel; + esp_gmf_err_io_t load_ret = esp_gmf_port_acquire_in(in_port, &deinterleave->in_load, bytes, ESP_GMF_MAX_DELAY); + samples_num = deinterleave->in_load->valid_size / (deinterleave->bytes_per_sample * deinterleave->channel); + bytes = samples_num * deinterleave->bytes_per_sample; + if (((bytes * deinterleave->channel) != deinterleave->in_load->valid_size) || (load_ret < ESP_GMF_IO_OK)) { + ESP_LOGE(TAG, "Invalid in load size %d, ret %d", deinterleave->in_load->valid_size, load_ret); + out_len = ESP_GMF_JOB_ERR_FAIL; + goto __deintlv_release; + } + ESP_LOGV(TAG, "IN: load: %p, buf: %p, valid size: %d, buf length: %d, done: %d", + deinterleave->in_load, deinterleave->in_load->buf, deinterleave->in_load->valid_size, + deinterleave->in_load->buf_length, deinterleave->in_load->is_done); + // Not do deinterleave anymore if one channel failed + while (out_port != NULL) { + load_ret = esp_gmf_port_acquire_out(out_port, &(deinterleave->out_load[i]), + samples_num ? bytes : deinterleave->in_load->buf_length, ESP_GMF_MAX_DELAY); + ESP_GMF_PORT_CHECK(TAG, load_ret, out_len, {out_len = ESP_GMF_JOB_ERR_FAIL; goto __deintlv_release;}, "Failed to acquire out, idx:%d, ret: %d", i, load_ret); + deinterleave->out_arr[i] = deinterleave->out_load[i]->buf; + out_port = out_port->next; + i++; + } + if (samples_num > 0) { + esp_ae_err_t proc_ret = esp_ae_deintlv_process(deinterleave->channel, deinterleave->bits_per_sample, samples_num, + deinterleave->in_load->buf, (void **)deinterleave->out_arr); + ESP_GMF_RET_ON_ERROR(TAG, proc_ret, {out_len = ESP_GMF_JOB_ERR_FAIL; goto __deintlv_release;}, "Deinterleave process error, ret: %d", proc_ret); + } + out_port = out; + i = 0; + while (out_port != NULL) { + deinterleave->out_load[i]->pts = deinterleave->in_load->pts; + deinterleave->out_load[i]->is_done = deinterleave->in_load->is_done; + deinterleave->out_load[i]->valid_size = samples_num * deinterleave->bytes_per_sample; + ESP_LOGV(TAG, "OUT: idx: %d load: %p, buf: %p, valid size: %d, buf length: %d, done: %d", + i, deinterleave->out_load[i], deinterleave->out_load[i]->buf, deinterleave->out_load[i]->valid_size, + deinterleave->out_load[i]->buf_length, deinterleave->out_load[i]->is_done); + out_port = out_port->next; + i++; + } + if (deinterleave->in_load->is_done) { + ESP_LOGD(TAG, "Deinterleave is done"); + out_len = ESP_GMF_JOB_ERR_DONE; + } +__deintlv_release: + out_port = out; + i = 0; + while (out_port != NULL && deinterleave->out_load[i] != NULL) { + load_ret = esp_gmf_port_release_out(out_port, deinterleave->out_load[i], out_port->wait_ticks); + if ((load_ret < ESP_GMF_IO_OK) && (load_ret != ESP_GMF_IO_ABORT)) { + ESP_LOGE(TAG, "OUT port release error, ret:%d", load_ret); + out_len = ESP_GMF_JOB_ERR_FAIL; + } + out_port = out_port->next; + i++; + } + if (deinterleave->in_load != NULL) { + load_ret = esp_gmf_port_release_in(in_port, deinterleave->in_load, ESP_GMF_MAX_DELAY); + if ((load_ret < ESP_GMF_IO_OK) && (load_ret != ESP_GMF_IO_ABORT)) { + ESP_LOGE(TAG, "IN port release error, ret:%d", load_ret); + out_len = ESP_GMF_JOB_ERR_FAIL; + } + } + return out_len; +} + +static esp_gmf_err_t deinterleave_received_event_handler(esp_gmf_event_pkt_t *evt, void *ctx) +{ + ESP_GMF_NULL_CHECK(TAG, ctx, {return ESP_GMF_ERR_INVALID_ARG;}); + ESP_GMF_NULL_CHECK(TAG, evt, {return ESP_GMF_ERR_INVALID_ARG;}); + if ((evt->type != ESP_GMF_EVT_TYPE_REPORT_INFO) + || (evt->sub != ESP_GMF_INFO_SOUND) + || (evt->payload == NULL)) { + return ESP_GMF_ERR_OK; + } + esp_gmf_element_handle_t self = (esp_gmf_element_handle_t)ctx; + esp_gmf_element_handle_t el = evt->from; + esp_gmf_event_state_t state = ESP_GMF_EVENT_STATE_NONE; + esp_gmf_element_get_state(self, &state); + esp_gmf_info_sound_t *info = (esp_gmf_info_sound_t *)evt->payload; + ESP_GMF_NULL_CHECK(TAG, info, { return ESP_GMF_ERR_FAIL;}); + esp_gmf_deinterleave_cfg *config = (esp_gmf_deinterleave_cfg *)OBJ_GET_CFG(self); + ESP_GMF_NULL_CHECK(TAG, config, return ESP_GMF_ERR_FAIL); + esp_gmf_deinterleave_t *deinterleave = (esp_gmf_deinterleave_t *)self; + deinterleave->need_reopen = (config->sample_rate != info->sample_rates) || (info->channels != config->channel) || (config->bits_per_sample != info->bits); + config->sample_rate = info->sample_rates; + config->channel = info->channels; + config->bits_per_sample = info->bits; + ESP_LOGD(TAG, "RECV element info, from: %s-%p, next: %p, self: %s-%p, type: %x, state: %s, rate: %d, ch: %d, bits: %d", + OBJ_GET_TAG(el), el, esp_gmf_node_for_next((esp_gmf_node_t *)el), OBJ_GET_TAG(self), self, evt->type, + esp_gmf_event_get_state_str(state), info->sample_rates, info->channels, info->bits); + if (state == ESP_GMF_EVENT_STATE_NONE) { + esp_gmf_element_set_state(self, ESP_GMF_EVENT_STATE_INITIALIZED); + } + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t esp_gmf_deinterleave_destroy(esp_gmf_element_handle_t self) +{ + esp_gmf_deinterleave_t *deinterleave = (esp_gmf_deinterleave_t *)self; + ESP_LOGD(TAG, "Destroyed, %p", self); + free_esp_ae_deinterleave_cfg(OBJ_GET_CFG(self)); + esp_gmf_audio_el_deinit(self); + esp_gmf_oal_free(deinterleave); + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t _load_deinterleave_caps_func(esp_gmf_element_handle_t handle) +{ + esp_gmf_cap_t *caps = NULL; + esp_gmf_cap_t dec_caps = {0}; + dec_caps.cap_eightcc = ESP_GMF_CAPS_AUDIO_DEINTERLEAVE; + dec_caps.attr_fun = NULL; + int ret = esp_gmf_cap_append(&caps, &dec_caps); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to create capability"); + + esp_gmf_element_t *el = (esp_gmf_element_t *)handle; + el->caps = caps; + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_deinterleave_init(esp_gmf_deinterleave_cfg *config, esp_gmf_element_handle_t *handle) +{ + ESP_GMF_NULL_CHECK(TAG, handle, {return ESP_GMF_ERR_INVALID_ARG;}); + *handle = NULL; + esp_gmf_err_t ret = ESP_GMF_ERR_OK; + esp_gmf_deinterleave_t *deinterleave = esp_gmf_oal_calloc(1, sizeof(esp_gmf_deinterleave_t)); + ESP_GMF_MEM_VERIFY(TAG, deinterleave, {return ESP_GMF_ERR_MEMORY_LACK;}, "deinterleave", sizeof(esp_gmf_deinterleave_t)); + esp_gmf_obj_t *obj = (esp_gmf_obj_t *)deinterleave; + obj->new_obj = esp_gmf_deinterleave_new; + obj->del_obj = esp_gmf_deinterleave_destroy; + esp_gmf_deinterleave_cfg *cfg = NULL; + if (config) { + dupl_esp_ae_deinterleave_cfg(config, &cfg); + } else { + esp_gmf_deinterleave_cfg dcfg = DEFAULT_ESP_GMF_DEINTERLEAVE_CONFIG(); + dupl_esp_ae_deinterleave_cfg(&dcfg, &cfg); + } + ESP_GMF_CHECK(TAG, cfg, ret = ESP_GMF_ERR_MEMORY_LACK; goto DEINTLV_INIT_FAIL;, "Failed to allocate deinterleave configuration"); + esp_gmf_obj_set_config(obj, cfg, sizeof(esp_gmf_deinterleave_cfg)); + ret = esp_gmf_obj_set_tag(obj, "aud_deintlv"); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto DEINTLV_INIT_FAIL, "Failed to set obj tag"); + esp_gmf_element_cfg_t el_cfg = {0}; + ESP_GMF_ELEMENT_IN_PORT_ATTR_SET(el_cfg.in_attr, ESP_GMF_EL_PORT_CAP_SINGLE, 0, 0, + ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, ESP_GMF_ELEMENT_PORT_DATA_SIZE_DEFAULT); + ESP_GMF_ELEMENT_IN_PORT_ATTR_SET(el_cfg.out_attr, ESP_GMF_EL_PORT_CAP_MULTI, 0, 0, + ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, ESP_GMF_ELEMENT_PORT_DATA_SIZE_DEFAULT); + el_cfg.dependency = true; + ret = esp_gmf_audio_el_init(deinterleave, &el_cfg); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto DEINTLV_INIT_FAIL, "Failed to initialize deinterleave element"); + ESP_GMF_ELEMENT_GET(deinterleave)->ops.open = esp_gmf_deinterleave_open; + ESP_GMF_ELEMENT_GET(deinterleave)->ops.process = esp_gmf_deinterleave_process; + ESP_GMF_ELEMENT_GET(deinterleave)->ops.close = esp_gmf_deinterleave_close; + ESP_GMF_ELEMENT_GET(deinterleave)->ops.event_receiver = deinterleave_received_event_handler; + ESP_GMF_ELEMENT_GET(deinterleave)->ops.load_caps = _load_deinterleave_caps_func; + *handle = obj; + ESP_LOGD(TAG, "Initialization, %s-%p", OBJ_GET_TAG(obj), obj); + return ESP_GMF_ERR_OK; +DEINTLV_INIT_FAIL: + esp_gmf_deinterleave_destroy(obj); + return ret; +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/esp_gmf_eq.c b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/esp_gmf_eq.c new file mode 100644 index 0000000..41e5c63 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/esp_gmf_eq.c @@ -0,0 +1,446 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#include +#include "esp_log.h" +#include "esp_gmf_oal_mem.h" +#include "esp_gmf_oal_mutex.h" +#include "esp_gmf_node.h" +#include "esp_gmf_eq.h" +#include "esp_gmf_args_desc.h" +#include "gmf_audio_common.h" +#include "esp_gmf_audio_methods_def.h" +#include "esp_gmf_cap.h" +#include "esp_gmf_caps_def.h" +#include "esp_gmf_audio_element.h" + +/** + * @brief Audio equalizer context in GMF + */ +typedef struct { + esp_gmf_audio_element_t parent; /*!< The GMF eq handle */ + esp_ae_eq_handle_t eq_hd; /*!< The audio effects eq handle */ + uint8_t bytes_per_sample; /*!< Bytes number of per sampling point */ + bool *is_filter_enabled; /*!< The flag of whether eq filter is enabled */ + bool need_reopen : 1; /*!< Whether need to reopen. + True: Execute the close function first, then execute the open function + False: Do nothing */ +} esp_gmf_eq_t; + +static const char *TAG = "ESP_GMF_EQ"; + +const esp_ae_eq_filter_para_t esp_gmf_default_eq_paras[10] = { + {ESP_AE_EQ_FILTER_PEAK, 31, 1.0, 0.0}, + {ESP_AE_EQ_FILTER_PEAK, 62, 1.0, 0.0}, + {ESP_AE_EQ_FILTER_PEAK, 125, 1.0, 0.0}, + {ESP_AE_EQ_FILTER_PEAK, 250, 1.0, 1.0}, + {ESP_AE_EQ_FILTER_PEAK, 500, 1.0, 2.0}, + {ESP_AE_EQ_FILTER_PEAK, 1000, 1.0, 3.0}, + {ESP_AE_EQ_FILTER_PEAK, 2000, 1.0, 3.0}, + {ESP_AE_EQ_FILTER_PEAK, 4000, 1.0, 2.0}, + {ESP_AE_EQ_FILTER_PEAK, 8000, 1.0, 1.0}, + {ESP_AE_EQ_FILTER_PEAK, 16000, 1.0, 0.0}, +}; + +static inline esp_gmf_err_t dupl_esp_ae_eq_cfg(esp_ae_eq_cfg_t *config, esp_ae_eq_cfg_t **new_config) +{ + void *sub_cfg = NULL; + *new_config = esp_gmf_oal_calloc(1, sizeof(*config)); + ESP_GMF_MEM_VERIFY(TAG, *new_config, {return ESP_GMF_ERR_MEMORY_LACK;}, "eq configuration", sizeof(*config)); + memcpy(*new_config, config, sizeof(*config)); + if (config->para && (config->filter_num > 0)) { + sub_cfg = esp_gmf_oal_calloc(1, config->filter_num * sizeof(esp_ae_eq_filter_para_t)); + ESP_GMF_MEM_VERIFY(TAG, sub_cfg, {esp_gmf_oal_free(*new_config); return ESP_GMF_ERR_MEMORY_LACK;}, + "filter parameter", config->filter_num * sizeof(esp_ae_eq_filter_para_t)); + memcpy(sub_cfg, config->para, config->filter_num * sizeof(esp_ae_eq_filter_para_t)); + (*new_config)->para = sub_cfg; + } + return ESP_GMF_JOB_ERR_OK; +} + +static inline void free_esp_ae_eq_cfg(esp_ae_eq_cfg_t *config) +{ + if (config) { + if (config->para && (config->para != esp_gmf_default_eq_paras)) { + esp_gmf_oal_free(config->para); + } + esp_gmf_oal_free(config); + } +} + +static inline void eq_change_src_info(esp_gmf_element_handle_t self, uint32_t src_rate, uint8_t src_ch, uint8_t src_bits) +{ + esp_ae_eq_cfg_t *eq_info = (esp_ae_eq_cfg_t *)OBJ_GET_CFG(self); + eq_info->channel = src_ch; + eq_info->sample_rate = src_rate; + eq_info->bits_per_sample = src_bits; +} + +static esp_gmf_err_t __eq_set_para(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc, + uint8_t *buf, int buf_len) +{ + ESP_GMF_NULL_CHECK(TAG, handle, {return ESP_GMF_ERR_INVALID_ARG;}); + ESP_GMF_NULL_CHECK(TAG, arg_desc, {return ESP_GMF_ERR_INVALID_ARG;}); + ESP_GMF_NULL_CHECK(TAG, buf, {return ESP_GMF_ERR_INVALID_ARG;}); + uint8_t idx = (uint8_t)(*buf); + return esp_gmf_eq_set_para(handle, idx, (esp_ae_eq_filter_para_t *)(buf + arg_desc->next->offset)); +} + +static esp_gmf_err_t __eq_get_para(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc, + uint8_t *buf, int buf_len) +{ + ESP_GMF_NULL_CHECK(TAG, handle, {return ESP_GMF_ERR_INVALID_ARG;}); + ESP_GMF_NULL_CHECK(TAG, arg_desc, {return ESP_GMF_ERR_INVALID_ARG;}); + ESP_GMF_NULL_CHECK(TAG, buf, {return ESP_GMF_ERR_INVALID_ARG;}); + uint8_t idx = (uint8_t)(*buf); + esp_ae_eq_filter_para_t *para = (esp_ae_eq_filter_para_t *)(buf + arg_desc->next->offset); + return esp_gmf_eq_get_para(handle, idx, para); +} + +static esp_gmf_err_t __eq_enable_filter(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc, + uint8_t *buf, int buf_len) +{ + ESP_GMF_NULL_CHECK(TAG, handle, {return ESP_GMF_ERR_INVALID_ARG;}); + ESP_GMF_NULL_CHECK(TAG, arg_desc, {return ESP_GMF_ERR_INVALID_ARG;}); + ESP_GMF_NULL_CHECK(TAG, buf, {return ESP_GMF_ERR_INVALID_ARG;}); + esp_gmf_args_desc_t *filter_desc = arg_desc; + uint8_t idx = (uint8_t)(*buf); + filter_desc = filter_desc->next; + uint8_t is_enable = (uint8_t)(*(buf + filter_desc->offset)); + return esp_gmf_eq_enable_filter(handle, idx,is_enable); +} + +static esp_gmf_err_t esp_gmf_eq_new(void *cfg, esp_gmf_obj_handle_t *handle) +{ + return esp_gmf_eq_init(cfg, (esp_gmf_element_handle_t *)handle); +} + +static esp_gmf_job_err_t esp_gmf_eq_open(esp_gmf_element_handle_t self, void *para) +{ + esp_gmf_eq_t *eq = (esp_gmf_eq_t *)self; + esp_ae_eq_cfg_t *eq_info = (esp_ae_eq_cfg_t *)OBJ_GET_CFG(self); + ESP_GMF_NULL_CHECK(TAG, eq_info, {return ESP_GMF_JOB_ERR_FAIL;}); + eq->bytes_per_sample = (eq_info->bits_per_sample >> 3) * eq_info->channel; + esp_ae_eq_open(eq_info, &eq->eq_hd); + ESP_GMF_CHECK(TAG, eq->eq_hd, {return ESP_GMF_JOB_ERR_FAIL;}, "Failed to create eq handle"); + GMF_AUDIO_UPDATE_SND_INFO(self, eq_info->sample_rate, eq_info->bits_per_sample, eq_info->channel); + for (int i = 0; i < eq_info->filter_num; i++) { + if (eq->is_filter_enabled[i]) { + esp_ae_eq_enable_filter(eq->eq_hd, i); + } else { + esp_ae_eq_disable_filter(eq->eq_hd, i); + } + } + eq->need_reopen = false; + ESP_LOGD(TAG, "Open, %p", eq); + return ESP_GMF_ERR_OK; +} + +static esp_gmf_job_err_t esp_gmf_eq_close(esp_gmf_element_handle_t self, void *para) +{ + esp_gmf_eq_t *eq = (esp_gmf_eq_t *)self; + ESP_LOGD(TAG, "Closed, %p", self); + if (eq->eq_hd != NULL) { + esp_ae_eq_close(eq->eq_hd); + eq->eq_hd = NULL; + } + return ESP_GMF_ERR_OK; +} + +static esp_gmf_job_err_t esp_gmf_eq_process(esp_gmf_element_handle_t self, void *para) +{ + esp_gmf_eq_t *eq = (esp_gmf_eq_t *)self; + esp_gmf_job_err_t out_len = ESP_GMF_JOB_ERR_OK; + if (eq->need_reopen) { + esp_gmf_eq_close(self, NULL); + out_len = esp_gmf_eq_open(self, NULL); + if (out_len != ESP_GMF_JOB_ERR_OK) { + ESP_LOGE(TAG, "EQ reopen failed"); + return out_len; + } + } + esp_gmf_port_handle_t in_port = ESP_GMF_ELEMENT_GET(self)->in; + esp_gmf_port_handle_t out_port = ESP_GMF_ELEMENT_GET(self)->out; + esp_gmf_payload_t *in_load = NULL; + esp_gmf_payload_t *out_load = NULL; + int samples_num = ESP_GMF_ELEMENT_GET(eq)->in_attr.data_size / (eq->bytes_per_sample); + int bytes = samples_num * eq->bytes_per_sample; + esp_gmf_err_io_t load_ret = esp_gmf_port_acquire_in(in_port, &in_load, bytes, ESP_GMF_MAX_DELAY); + samples_num = in_load->valid_size / (eq->bytes_per_sample); + bytes = samples_num * eq->bytes_per_sample; + if ((bytes != in_load->valid_size) || (load_ret < ESP_GMF_IO_OK)) { + ESP_LOGE(TAG, "Invalid in load size %d, ret %d", in_load->valid_size, load_ret); + out_len = ESP_GMF_JOB_ERR_FAIL; + goto __eq_release; + } + if (in_port->is_shared == 1) { + out_load = in_load; + } + load_ret = esp_gmf_port_acquire_out(out_port, &out_load, samples_num ? bytes : in_load->buf_length, ESP_GMF_MAX_DELAY); + ESP_GMF_PORT_ACQUIRE_OUT_CHECK(TAG, load_ret, out_len, { goto __eq_release;}); + if (samples_num > 0) { + esp_gmf_oal_mutex_lock(((esp_gmf_audio_element_t *)self)->lock); + esp_ae_err_t ret = esp_ae_eq_process(eq->eq_hd, samples_num, in_load->buf, out_load->buf); + esp_gmf_oal_mutex_unlock(((esp_gmf_audio_element_t *)self)->lock); + ESP_GMF_RET_ON_ERROR(TAG, ret, {out_len = ESP_GMF_JOB_ERR_FAIL; goto __eq_release;}, "Equalize process error %d", ret); + } + ESP_LOGV(TAG, "Samples: %d, IN-PLD: %p-%p-%d-%d-%d, OUT-PLD: %p-%p-%d-%d-%d", + samples_num, in_load, in_load->buf, in_load->valid_size, in_load->buf_length, in_load->is_done, + out_load, out_load->buf, out_load->valid_size, out_load->buf_length, out_load->is_done); + out_load->valid_size = samples_num * eq->bytes_per_sample; + out_load->is_done = in_load->is_done; + out_load->pts = in_load->pts; + if (out_load->valid_size > 0) { + esp_gmf_audio_el_update_file_pos((esp_gmf_element_handle_t)self, out_load->valid_size); + } + if (in_load->is_done) { + out_len = ESP_GMF_JOB_ERR_DONE; + ESP_LOGD(TAG, "Equalize done, out len: %d", out_load->valid_size); + } +__eq_release: + // Release in and out port + if (out_load != NULL) { + load_ret = esp_gmf_port_release_out(out_port, out_load, ESP_GMF_MAX_DELAY); + if ((load_ret < ESP_GMF_IO_OK) && (load_ret != ESP_GMF_IO_ABORT)) { + ESP_LOGE(TAG, "OUT port release error, ret:%d", load_ret); + out_len = ESP_GMF_JOB_ERR_FAIL; + } + } + if (in_load != NULL) { + load_ret = esp_gmf_port_release_in(in_port, in_load, ESP_GMF_MAX_DELAY); + if ((load_ret < ESP_GMF_IO_OK) && (load_ret != ESP_GMF_IO_ABORT)) { + ESP_LOGE(TAG, "IN port release error, ret:%d", load_ret); + out_len = ESP_GMF_JOB_ERR_FAIL; + } + } + return out_len; +} + +static esp_gmf_err_t eq_received_event_handler(esp_gmf_event_pkt_t *evt, void *ctx) +{ + ESP_GMF_NULL_CHECK(TAG, ctx, {return ESP_GMF_ERR_INVALID_ARG;}); + ESP_GMF_NULL_CHECK(TAG, evt, {return ESP_GMF_ERR_INVALID_ARG;}); + if ((evt->type != ESP_GMF_EVT_TYPE_REPORT_INFO) + || (evt->sub != ESP_GMF_INFO_SOUND) + || (evt->payload == NULL)) { + return ESP_GMF_ERR_OK; + } + esp_gmf_element_handle_t self = (esp_gmf_element_handle_t)ctx; + esp_gmf_element_handle_t el = evt->from; + esp_gmf_event_state_t state = ESP_GMF_EVENT_STATE_NONE; + esp_gmf_element_get_state(self, &state); + esp_gmf_info_sound_t *info = (esp_gmf_info_sound_t *)evt->payload; + esp_ae_eq_cfg_t *config = (esp_ae_eq_cfg_t *)OBJ_GET_CFG(self); + ESP_GMF_NULL_CHECK(TAG, config, return ESP_GMF_ERR_FAIL); + esp_gmf_eq_t *eq = (esp_gmf_eq_t *)self; + eq->need_reopen = (config->sample_rate != info->sample_rates) || (info->channels != config->channel) || (config->bits_per_sample != info->bits); + config->sample_rate = info->sample_rates; + config->channel = info->channels; + config->bits_per_sample = info->bits; + ESP_LOGD(TAG, "RECV element info, from: %s-%p, next: %p, self: %s-%p, type: %x, state: %s, rate: %d, ch: %d, bits: %d", + OBJ_GET_TAG(el), el, esp_gmf_node_for_next((esp_gmf_node_t *)el), OBJ_GET_TAG(self), self, evt->type, + esp_gmf_event_get_state_str(state), info->sample_rates, info->channels, info->bits); + if (state == ESP_GMF_EVENT_STATE_NONE) { + esp_gmf_element_set_state(self, ESP_GMF_EVENT_STATE_INITIALIZED); + } + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t esp_gmf_eq_destroy(esp_gmf_element_handle_t self) +{ + esp_gmf_eq_t *eq = (esp_gmf_eq_t *)self; + ESP_LOGD(TAG, "Destroyed, %p", self); + free_esp_ae_eq_cfg(OBJ_GET_CFG(self)); + if(eq->is_filter_enabled){ + esp_gmf_oal_free(eq->is_filter_enabled); + } + esp_gmf_audio_el_deinit(self); + esp_gmf_oal_free(eq); + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t _load_eq_caps_func(esp_gmf_element_handle_t handle) +{ + esp_gmf_cap_t *caps = NULL; + esp_gmf_cap_t dec_caps = {0}; + dec_caps.cap_eightcc = ESP_GMF_CAPS_AUDIO_EQUALIZER; + dec_caps.attr_fun = NULL; + int ret = esp_gmf_cap_append(&caps, &dec_caps); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to create capability"); + + esp_gmf_element_t *el = (esp_gmf_element_t *)handle; + el->caps = caps; + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t _load_eq_methods_func(esp_gmf_element_handle_t handle) +{ + esp_gmf_method_t *method = NULL; + esp_gmf_args_desc_t *set_args = NULL; + esp_gmf_args_desc_t *get_args = NULL; + esp_gmf_args_desc_t *pointer_args = NULL; + esp_gmf_err_t ret = esp_gmf_args_desc_append(&pointer_args, AMETHOD_ARG(EQ, SET_PARA, PARA_FT), ESP_GMF_ARGS_TYPE_UINT32, + sizeof(uint32_t), offsetof(esp_ae_eq_filter_para_t, filter_type)); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to append FILTER argument"); + ret = esp_gmf_args_desc_append(&pointer_args, AMETHOD_ARG(EQ, SET_PARA, PARA_FC), ESP_GMF_ARGS_TYPE_UINT32, + sizeof(uint32_t), offsetof(esp_ae_eq_filter_para_t, fc)); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to append FC argument"); + ret = esp_gmf_args_desc_append(&pointer_args, AMETHOD_ARG(EQ, SET_PARA, PARA_Q), ESP_GMF_ARGS_TYPE_FLOAT, + sizeof(float), offsetof(esp_ae_eq_filter_para_t, q)); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to append Q argument"); + ret = esp_gmf_args_desc_append(&pointer_args, AMETHOD_ARG(EQ, SET_PARA, PARA_GAIN), ESP_GMF_ARGS_TYPE_FLOAT, + sizeof(float), offsetof(esp_ae_eq_filter_para_t, gain)); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to append GAIN argument"); + ret = esp_gmf_args_desc_append(&set_args, AMETHOD_ARG(EQ, SET_PARA, IDX), ESP_GMF_ARGS_TYPE_UINT8, sizeof(uint8_t), 0); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to append INDEX argument"); + ret = esp_gmf_args_desc_append_array(&set_args, AMETHOD_ARG(EQ, SET_PARA, PARA), pointer_args, + sizeof(esp_ae_eq_filter_para_t), sizeof(uint8_t)); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to append PARA argument"); + ret = esp_gmf_method_append(&method, AMETHOD(EQ, SET_PARA), __eq_set_para, set_args); + ESP_GMF_RET_ON_ERROR(TAG, ret, {return ret;}, "Failed to register %s method", AMETHOD(EQ, SET_PARA)); + + ret = esp_gmf_args_desc_copy(set_args, &get_args); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to copy PARA argument"); + ret = esp_gmf_method_append(&method, AMETHOD(EQ, GET_PARA), __eq_get_para, get_args); + ESP_GMF_RET_ON_ERROR(TAG, ret, {return ret;}, "Failed to register %s method", AMETHOD(EQ, GET_PARA)); + + set_args = NULL; + get_args = NULL; + ret = esp_gmf_args_desc_append(&set_args, AMETHOD_ARG(EQ, ENABLE_FILTER, IDX), ESP_GMF_ARGS_TYPE_UINT8, sizeof(uint8_t), 0); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to append INDEX argument"); + ret = esp_gmf_args_desc_append(&set_args, AMETHOD_ARG(EQ, ENABLE_FILTER, ENABLE), ESP_GMF_ARGS_TYPE_UINT8, + sizeof(uint8_t), sizeof(uint8_t)); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to append PARA argument"); + ret = esp_gmf_method_append(&method, AMETHOD(EQ, ENABLE_FILTER), __eq_enable_filter, set_args); + ESP_GMF_RET_ON_ERROR(TAG, ret, {return ret;}, "Failed to register %s method", AMETHOD(EQ, ENABLE_FILTER)); + + esp_gmf_element_t *el = (esp_gmf_element_t *)handle; + el->method = method; + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_eq_set_para(esp_gmf_element_handle_t handle, uint8_t idx, esp_ae_eq_filter_para_t *para) +{ + ESP_GMF_NULL_CHECK(TAG, handle, { return ESP_GMF_ERR_INVALID_ARG;}); + ESP_GMF_NULL_CHECK(TAG, para, { return ESP_GMF_ERR_INVALID_ARG;}); + esp_gmf_eq_t *eq = (esp_gmf_eq_t *)handle; + if (eq->eq_hd) { + esp_gmf_oal_mutex_lock(((esp_gmf_audio_element_t *)handle)->lock); + esp_ae_err_t ret = esp_ae_eq_set_filter_para(eq->eq_hd, idx, para); + esp_gmf_oal_mutex_unlock(((esp_gmf_audio_element_t *)handle)->lock); + ESP_GMF_RET_ON_ERROR(TAG, ret, return ESP_GMF_JOB_ERR_FAIL;, "Equalize set error %d", ret); + } + esp_ae_eq_cfg_t *cfg = (esp_ae_eq_cfg_t *)OBJ_GET_CFG(handle); + ESP_GMF_NULL_CHECK(TAG, cfg, return ESP_GMF_ERR_FAIL); + if (cfg->para) { + if (idx >= cfg->filter_num) { + ESP_LOGE(TAG, "Invalid idx %d", idx); + return ESP_GMF_ERR_FAIL; + } + memcpy(&cfg->para[idx], para, sizeof(esp_ae_eq_filter_para_t)); + return ESP_GMF_ERR_OK; + } + ESP_LOGE(TAG, "Failed to set EQ para, no para allocated"); + return ESP_GMF_ERR_FAIL; +} + +esp_gmf_err_t esp_gmf_eq_get_para(esp_gmf_element_handle_t handle, uint8_t idx, esp_ae_eq_filter_para_t *para) +{ + ESP_GMF_NULL_CHECK(TAG, handle, { return ESP_GMF_ERR_INVALID_ARG;}); + esp_gmf_eq_t *eq = (esp_gmf_eq_t *)handle; + if (eq->eq_hd) { + esp_ae_err_t ret = esp_ae_eq_get_filter_para(eq->eq_hd, idx, para); + ESP_GMF_RET_ON_ERROR(TAG, ret, return ESP_GMF_JOB_ERR_FAIL;, "Equalize set error %d", ret); + } else { + esp_ae_eq_cfg_t *cfg = (esp_ae_eq_cfg_t *)OBJ_GET_CFG(handle); + ESP_GMF_NULL_CHECK(TAG, cfg, return ESP_GMF_ERR_FAIL); + if (cfg->para) { + if (idx >= cfg->filter_num) { + ESP_LOGE(TAG, "Invalid idx %d", idx); + return ESP_GMF_ERR_FAIL; + } + memcpy(para, &cfg->para[idx], sizeof(esp_ae_eq_filter_para_t)); + } + } + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_eq_enable_filter(esp_gmf_element_handle_t handle, uint8_t idx, bool is_enable) +{ + ESP_GMF_NULL_CHECK(TAG, handle, { return ESP_GMF_ERR_INVALID_ARG;}); + esp_gmf_eq_t *eq = (esp_gmf_eq_t *)handle; + esp_ae_eq_cfg_t *cfg = (esp_ae_eq_cfg_t *)OBJ_GET_CFG(handle); + ESP_GMF_NULL_CHECK(TAG, cfg, return ESP_GMF_ERR_FAIL); + if (idx >= cfg->filter_num) { + ESP_LOGE(TAG, "Filter index %d overlimit %d hd:%p", idx, cfg->filter_num, eq); + return ESP_GMF_ERR_INVALID_ARG; + } + if (eq->eq_hd) { + esp_gmf_oal_mutex_lock(((esp_gmf_audio_element_t *)handle)->lock); + esp_ae_err_t ret = 0; + if (is_enable) { + ret = esp_ae_eq_enable_filter(eq->eq_hd, idx); + } else { + ret = esp_ae_eq_disable_filter(eq->eq_hd, idx); + } + esp_gmf_oal_mutex_unlock(((esp_gmf_audio_element_t *)handle)->lock); + ESP_GMF_RET_ON_ERROR(TAG, ret, return ESP_GMF_JOB_ERR_FAIL;, "Equalize set error %d", ret); + } + eq->is_filter_enabled[idx] = is_enable; + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_eq_init(esp_ae_eq_cfg_t *config, esp_gmf_element_handle_t *handle) +{ + ESP_GMF_NULL_CHECK(TAG, handle, {return ESP_GMF_ERR_INVALID_ARG;}); + *handle = NULL; + esp_gmf_err_t ret = ESP_GMF_ERR_OK; + esp_gmf_eq_t *eq = esp_gmf_oal_calloc(1, sizeof(esp_gmf_eq_t)); + ESP_GMF_MEM_VERIFY(TAG, eq, {return ESP_GMF_ERR_MEMORY_LACK;}, "eq", sizeof(esp_gmf_eq_t)); + esp_gmf_obj_t *obj = (esp_gmf_obj_t *)eq; + obj->new_obj = esp_gmf_eq_new; + obj->del_obj = esp_gmf_eq_destroy; + esp_ae_eq_cfg_t *cfg = NULL; + if (config) { + if (config->para == NULL) { + config->para = (esp_ae_eq_filter_para_t *)esp_gmf_default_eq_paras; + config->filter_num = sizeof(esp_gmf_default_eq_paras) / sizeof(esp_ae_eq_filter_para_t); + } + dupl_esp_ae_eq_cfg(config, &cfg); + } else { + esp_ae_eq_cfg_t dcfg = DEFAULT_ESP_GMF_EQ_CONFIG(); + dcfg.para = (esp_ae_eq_filter_para_t *)esp_gmf_default_eq_paras; + dcfg.filter_num = sizeof(esp_gmf_default_eq_paras) / sizeof(esp_ae_eq_filter_para_t); + dupl_esp_ae_eq_cfg(&dcfg, &cfg); + } + ESP_GMF_CHECK(TAG, cfg, {ret = ESP_GMF_ERR_MEMORY_LACK; goto EQ_INI_FAIL;}, "Failed to allocate eq configuration"); + esp_gmf_obj_set_config(obj, cfg, sizeof(esp_ae_eq_cfg_t)); + eq->is_filter_enabled = esp_gmf_oal_calloc(cfg->filter_num, sizeof(bool)); + ESP_GMF_MEM_VERIFY(TAG, eq->is_filter_enabled, ret = ESP_GMF_ERR_MEMORY_LACK; goto EQ_INI_FAIL, "Rellocation failed", cfg->filter_num); + ret = esp_gmf_obj_set_tag(obj, "aud_eq"); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto EQ_INI_FAIL, "Failed to set obj tag"); + esp_gmf_element_cfg_t el_cfg = {0}; + ESP_GMF_ELEMENT_IN_PORT_ATTR_SET(el_cfg.in_attr, ESP_GMF_EL_PORT_CAP_SINGLE, 0, 0, + ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, ESP_GMF_ELEMENT_PORT_DATA_SIZE_DEFAULT); + ESP_GMF_ELEMENT_IN_PORT_ATTR_SET(el_cfg.out_attr, ESP_GMF_EL_PORT_CAP_SINGLE, 0, 0, + ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, ESP_GMF_ELEMENT_PORT_DATA_SIZE_DEFAULT); + el_cfg.dependency = true; + ret = esp_gmf_audio_el_init(eq, &el_cfg); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto EQ_INI_FAIL, "Failed to initialize eq element"); + *handle = obj; + ESP_LOGD(TAG, "Initialization, %s-%p", OBJ_GET_TAG(obj), obj); + ESP_GMF_ELEMENT_GET(eq)->ops.open = esp_gmf_eq_open; + ESP_GMF_ELEMENT_GET(eq)->ops.process = esp_gmf_eq_process; + ESP_GMF_ELEMENT_GET(eq)->ops.close = esp_gmf_eq_close; + ESP_GMF_ELEMENT_GET(eq)->ops.event_receiver = eq_received_event_handler; + ESP_GMF_ELEMENT_GET(eq)->ops.load_caps = _load_eq_caps_func; + ESP_GMF_ELEMENT_GET(eq)->ops.load_methods = _load_eq_methods_func; + return ESP_GMF_ERR_OK; +EQ_INI_FAIL: + esp_gmf_eq_destroy(obj); + return ret; +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/esp_gmf_fade.c b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/esp_gmf_fade.c new file mode 100644 index 0000000..064cbbf --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/esp_gmf_fade.c @@ -0,0 +1,329 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#include +#include "esp_log.h" +#include "esp_gmf_oal_mem.h" +#include "esp_gmf_oal_mutex.h" +#include "esp_gmf_node.h" +#include "esp_gmf_fade.h" +#include "gmf_audio_common.h" +#include "esp_gmf_audio_methods_def.h" +#include "esp_gmf_cap.h" +#include "esp_gmf_caps_def.h" +#include "esp_gmf_audio_element.h" + +/** + * @brief Audio fade context in GMF + */ +typedef struct { + esp_gmf_audio_element_t parent; /*!< The GMF fade handle */ + esp_ae_fade_handle_t fade_hd; /*!< The audio effects fade handle */ + uint8_t bytes_per_sample; /*!< Bytes number of per sampling point */ + bool need_reopen : 1; /*!< Whether need to reopen. + True: Execute the close function first, then execute the open function + False: Do nothing */ +} esp_gmf_fade_t; + +static const char *TAG = "ESP_GMF_FADE"; + +static esp_gmf_err_t __fade_set_mode(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc, + uint8_t *buf, int buf_len) +{ + ESP_GMF_NULL_CHECK(TAG, arg_desc, {return ESP_GMF_ERR_INVALID_ARG;}); + ESP_GMF_NULL_CHECK(TAG, buf, {return ESP_GMF_ERR_INVALID_ARG;}); + return esp_gmf_fade_set_mode(handle, *((esp_ae_fade_mode_t *)buf)); +} + +static esp_gmf_err_t __fade_get_mode(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc, + uint8_t *buf, int buf_len) +{ + ESP_GMF_NULL_CHECK(TAG, arg_desc, {return ESP_GMF_ERR_INVALID_ARG;}); + return esp_gmf_fade_get_mode(handle, (esp_ae_fade_mode_t *)buf); +} + +static esp_gmf_err_t __fade_reset(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc, + uint8_t *buf, int buf_len) +{ + return esp_gmf_fade_reset_weight(handle); +} + +static esp_gmf_err_t esp_gmf_fade_new(void *cfg, esp_gmf_obj_handle_t *handle) +{ + return esp_gmf_fade_init(cfg, (esp_gmf_element_handle_t *)handle); +} + +static esp_gmf_job_err_t esp_gmf_fade_open(esp_gmf_element_handle_t self, void *para) +{ + esp_gmf_fade_t *fade = (esp_gmf_fade_t *)self; + esp_ae_fade_cfg_t *fade_info = (esp_ae_fade_cfg_t *)OBJ_GET_CFG(self); + ESP_GMF_NULL_CHECK(TAG, fade_info, {return ESP_GMF_JOB_ERR_FAIL;}); + fade->bytes_per_sample = (fade_info->bits_per_sample >> 3) * fade_info->channel; + esp_ae_fade_open(fade_info, &fade->fade_hd); + ESP_GMF_CHECK(TAG, fade->fade_hd, {return ESP_GMF_JOB_ERR_FAIL;}, "Failed to create fade handle"); + GMF_AUDIO_UPDATE_SND_INFO(self, fade_info->sample_rate, fade_info->bits_per_sample, fade_info->channel); + fade->need_reopen = false; + ESP_LOGD(TAG, "Open, %p", self); + return ESP_GMF_ERR_OK; +} + +static esp_gmf_job_err_t esp_gmf_fade_close(esp_gmf_element_handle_t self, void *para) +{ + esp_gmf_fade_t *fade = (esp_gmf_fade_t *)self; + ESP_LOGD(TAG, "Closed, %p", self); + if (fade->fade_hd != NULL) { + esp_ae_fade_close(fade->fade_hd); + fade->fade_hd = NULL; + } + return ESP_GMF_ERR_OK; +} + +static esp_gmf_job_err_t esp_gmf_fade_process(esp_gmf_element_handle_t self, void *para) +{ + esp_gmf_fade_t *fade = (esp_gmf_fade_t *)self; + esp_gmf_job_err_t out_len = ESP_GMF_JOB_ERR_OK; + if (fade->need_reopen) { + esp_gmf_fade_close(self, NULL); + out_len = esp_gmf_fade_open(self, NULL); + if (out_len != ESP_GMF_JOB_ERR_OK) { + ESP_LOGE(TAG, "Fade reopen failed"); + return out_len; + } + } + esp_gmf_port_handle_t in_port = ESP_GMF_ELEMENT_GET(self)->in; + esp_gmf_port_handle_t out_port = ESP_GMF_ELEMENT_GET(self)->out; + esp_gmf_payload_t *in_load = NULL; + esp_gmf_payload_t *out_load = NULL; + int samples_num = ESP_GMF_ELEMENT_GET(fade)->in_attr.data_size / (fade->bytes_per_sample); + int bytes = samples_num * fade->bytes_per_sample; + esp_gmf_err_io_t load_ret = esp_gmf_port_acquire_in(in_port, &in_load, bytes, ESP_GMF_MAX_DELAY); + samples_num = in_load->valid_size / (fade->bytes_per_sample); + bytes = samples_num * fade->bytes_per_sample; + if ((bytes != in_load->valid_size) || (load_ret < ESP_GMF_IO_OK)) { + ESP_LOGE(TAG, "Invalid in load size %d, ret %d", in_load->valid_size, load_ret); + out_len = ESP_GMF_JOB_ERR_FAIL; + goto __fade_release; + } + if (in_port->is_shared == 1) { + out_load = in_load; + } + load_ret = esp_gmf_port_acquire_out(out_port, &out_load, samples_num ? bytes : in_load->buf_length, ESP_GMF_MAX_DELAY); + ESP_GMF_PORT_ACQUIRE_OUT_CHECK(TAG, load_ret, out_len, { goto __fade_release;}); + if (samples_num > 0) { + esp_gmf_oal_mutex_lock(((esp_gmf_audio_element_t *)self)->lock); + esp_ae_err_t ret = esp_ae_fade_process(fade->fade_hd, samples_num, in_load->buf, out_load->buf); + esp_gmf_oal_mutex_unlock(((esp_gmf_audio_element_t *)self)->lock); + ESP_GMF_RET_ON_ERROR(TAG, ret, {out_len = ESP_GMF_JOB_ERR_FAIL; goto __fade_release;}, "Fade process error %d", ret); + } + ESP_LOGV(TAG, "Samples: %d, IN-PLD: %p-%p-%d-%d-%d, OUT-PLD: %p-%p-%d-%d-%d", + samples_num, in_load, in_load->buf, in_load->valid_size, in_load->buf_length, in_load->is_done, + out_load, out_load->buf, out_load->valid_size, out_load->buf_length, out_load->is_done); + out_load->valid_size = samples_num * fade->bytes_per_sample; + out_load->is_done = in_load->is_done; + out_load->pts = in_load->pts; + if (out_load->valid_size > 0) { + esp_gmf_audio_el_update_file_pos((esp_gmf_element_handle_t)self, out_load->valid_size); + } + if (in_load->is_done) { + out_len = ESP_GMF_JOB_ERR_DONE; + ESP_LOGD(TAG, "Fade done, out len: %d", out_load->valid_size); + } +__fade_release: + // Release in and out port + if (out_load != NULL) { + load_ret = esp_gmf_port_release_out(out_port, out_load, ESP_GMF_MAX_DELAY); + if ((load_ret < ESP_GMF_IO_OK) && (load_ret != ESP_GMF_IO_ABORT)) { + ESP_LOGE(TAG, "OUT port release error, ret:%d", load_ret); + out_len = ESP_GMF_JOB_ERR_FAIL; + } + } + if (in_load != NULL) { + load_ret = esp_gmf_port_release_in(in_port, in_load, ESP_GMF_MAX_DELAY); + if ((load_ret < ESP_GMF_IO_OK) && (load_ret != ESP_GMF_IO_ABORT)) { + ESP_LOGE(TAG, "IN port release error, ret:%d", load_ret); + out_len = ESP_GMF_JOB_ERR_FAIL; + } + } + return out_len; +} + +static esp_gmf_err_t fade_received_event_handler(esp_gmf_event_pkt_t *evt, void *ctx) +{ + ESP_GMF_NULL_CHECK(TAG, ctx, {return ESP_GMF_ERR_INVALID_ARG;}); + ESP_GMF_NULL_CHECK(TAG, evt, {return ESP_GMF_ERR_INVALID_ARG;}); + if ((evt->type != ESP_GMF_EVT_TYPE_REPORT_INFO) + || (evt->sub != ESP_GMF_INFO_SOUND) + || (evt->payload == NULL)) { + return ESP_GMF_ERR_OK; + } + esp_gmf_element_handle_t self = (esp_gmf_element_handle_t)ctx; + esp_gmf_element_handle_t el = evt->from; + esp_gmf_event_state_t state = ESP_GMF_EVENT_STATE_NONE; + esp_gmf_element_get_state(self, &state); + esp_gmf_info_sound_t *info = (esp_gmf_info_sound_t *)evt->payload; + esp_ae_fade_cfg_t *config = (esp_ae_fade_cfg_t *)OBJ_GET_CFG(self); + ESP_GMF_NULL_CHECK(TAG, config, return ESP_GMF_ERR_FAIL); + esp_gmf_fade_t *fade = (esp_gmf_fade_t *)self; + fade->need_reopen = (config->sample_rate != info->sample_rates) || (info->channels != config->channel) || (config->bits_per_sample != info->bits); + config->sample_rate = info->sample_rates; + config->channel = info->channels; + config->bits_per_sample = info->bits; + ESP_LOGD(TAG, "RECV element info, from: %s-%p, next: %p, self: %s-%p, type: %x, state: %s, rate: %d, ch: %d, bits: %d", + OBJ_GET_TAG(el), el, esp_gmf_node_for_next((esp_gmf_node_t *)el), OBJ_GET_TAG(self), self, evt->type, + esp_gmf_event_get_state_str(state), info->sample_rates, info->channels, info->bits); + if (state == ESP_GMF_EVENT_STATE_NONE) { + esp_gmf_element_set_state(self, ESP_GMF_EVENT_STATE_INITIALIZED); + } + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t esp_gmf_fade_destroy(esp_gmf_element_handle_t self) +{ + esp_gmf_fade_t *fade = (esp_gmf_fade_t *)self; + ESP_LOGD(TAG, "Destroyed, %p", self); + void *cfg = OBJ_GET_CFG(self); + if (cfg) { + esp_gmf_oal_free(cfg); + } + esp_gmf_audio_el_deinit(self); + esp_gmf_oal_free(fade); + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t _load_fade_caps_func(esp_gmf_element_handle_t handle) +{ + esp_gmf_cap_t *caps = NULL; + esp_gmf_cap_t dec_caps = {0}; + dec_caps.cap_eightcc = ESP_GMF_CAPS_AUDIO_FADE; + dec_caps.attr_fun = NULL; + int ret = esp_gmf_cap_append(&caps, &dec_caps); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to create capability"); + + esp_gmf_element_t *el = (esp_gmf_element_t *)handle; + el->caps = caps; + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t _load_fade_methods_func(esp_gmf_element_handle_t handle) +{ + esp_gmf_method_t *method = NULL; + esp_gmf_args_desc_t *set_args = NULL; + esp_gmf_args_desc_t *get_args = NULL; + esp_gmf_err_t ret = esp_gmf_args_desc_append(&set_args, AMETHOD_ARG(FADE, SET_MODE, MODE), ESP_GMF_ARGS_TYPE_INT32, sizeof(int32_t), 0); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to append MODE argument"); + ret = esp_gmf_method_append(&method, AMETHOD(FADE, SET_MODE), __fade_set_mode, set_args); + ESP_GMF_RET_ON_ERROR(TAG, ret, {return ret;}, "Failed to register %s method", AMETHOD(FADE, SET_MODE)); + + ret = esp_gmf_args_desc_copy(set_args, &get_args); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to copy argument"); + ret = esp_gmf_method_append(&method, AMETHOD(FADE, GET_MODE), __fade_get_mode, get_args); + ESP_GMF_RET_ON_ERROR(TAG, ret, {return ret;}, "Failed to register %s method", AMETHOD(FADE, GET_MODE)); + + ret = esp_gmf_method_append(&method, AMETHOD(FADE, RESET_WEIGHT), __fade_reset, NULL); + ESP_GMF_RET_ON_ERROR(TAG, ret, {return ret;}, "Failed to register %s method", AMETHOD(FADE, RESET_WEIGHT)); + + esp_gmf_element_t *el = (esp_gmf_element_t *)handle; + el->method = method; + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_fade_set_mode(esp_gmf_element_handle_t handle, esp_ae_fade_mode_t mode) +{ + ESP_GMF_NULL_CHECK(TAG, handle, { return ESP_GMF_ERR_INVALID_ARG;}); + esp_gmf_fade_t *fade = (esp_gmf_fade_t *)handle; + esp_ae_fade_cfg_t *cfg = (esp_ae_fade_cfg_t *)OBJ_GET_CFG(handle); + ESP_GMF_NULL_CHECK(TAG, cfg, return ESP_GMF_ERR_FAIL); + if (fade->fade_hd) { + esp_gmf_oal_mutex_lock(((esp_gmf_audio_element_t *)handle)->lock); + esp_ae_err_t ret = esp_ae_fade_set_mode(fade->fade_hd, mode); + esp_gmf_oal_mutex_unlock(((esp_gmf_audio_element_t *)handle)->lock); + if (ret != ESP_AE_ERR_OK) { + return ESP_GMF_JOB_ERR_FAIL; + } + } + cfg->mode = mode; + return ESP_GMF_JOB_ERR_OK; +} + +esp_gmf_err_t esp_gmf_fade_get_mode(esp_gmf_element_handle_t handle, esp_ae_fade_mode_t *mode) +{ + ESP_GMF_NULL_CHECK(TAG, handle, { return ESP_GMF_ERR_INVALID_ARG;}); + ESP_GMF_NULL_CHECK(TAG, mode, { return ESP_GMF_ERR_INVALID_ARG;}); + esp_ae_fade_cfg_t *cfg = (esp_ae_fade_cfg_t *)OBJ_GET_CFG(handle); + ESP_GMF_NULL_CHECK(TAG, cfg, return ESP_GMF_ERR_FAIL); + esp_gmf_fade_t *fade = (esp_gmf_fade_t *)handle; + if (fade->fade_hd) { + esp_ae_err_t ret = esp_ae_fade_get_mode(fade->fade_hd, mode); + if (ret != ESP_AE_ERR_OK) { + return ESP_GMF_JOB_ERR_FAIL; + } + } else { + *mode = cfg->mode; + } + return ESP_GMF_JOB_ERR_OK; +} + +esp_gmf_err_t esp_gmf_fade_reset_weight(esp_gmf_element_handle_t handle) +{ + ESP_GMF_NULL_CHECK(TAG, handle, { return ESP_GMF_ERR_INVALID_ARG;}); + esp_ae_fade_cfg_t *cfg = (esp_ae_fade_cfg_t *)OBJ_GET_CFG(handle); + ESP_GMF_NULL_CHECK(TAG, cfg, return ESP_GMF_ERR_FAIL); + esp_gmf_fade_t *fade = (esp_gmf_fade_t *)handle; + if(fade->fade_hd) { + esp_gmf_oal_mutex_lock(((esp_gmf_audio_element_t *)handle)->lock); + esp_ae_err_t ret = esp_ae_fade_reset_weight(fade->fade_hd); + esp_gmf_oal_mutex_unlock(((esp_gmf_audio_element_t *)handle)->lock); + if (ret != ESP_AE_ERR_OK) { + return ESP_GMF_JOB_ERR_FAIL; + } + esp_ae_fade_get_mode(fade->fade_hd, &cfg->mode); + } + return ESP_GMF_JOB_ERR_OK; +} + +esp_gmf_err_t esp_gmf_fade_init(esp_ae_fade_cfg_t *config, esp_gmf_element_handle_t *handle) +{ + ESP_GMF_NULL_CHECK(TAG, handle, {return ESP_GMF_ERR_INVALID_ARG;}); + *handle = NULL; + esp_gmf_err_t ret = ESP_GMF_ERR_OK; + esp_gmf_fade_t *fade = esp_gmf_oal_calloc(1, sizeof(esp_gmf_fade_t)); + ESP_GMF_MEM_VERIFY(TAG, fade, {return ESP_GMF_ERR_MEMORY_LACK;}, "fade", sizeof(esp_gmf_fade_t)); + esp_gmf_obj_t *obj = (esp_gmf_obj_t *)fade; + obj->new_obj = esp_gmf_fade_new; + obj->del_obj = esp_gmf_fade_destroy; + esp_ae_fade_cfg_t *cfg = esp_gmf_oal_calloc(1, sizeof(esp_ae_fade_cfg_t)); + ESP_GMF_MEM_VERIFY(TAG, cfg, {ret = ESP_GMF_ERR_MEMORY_LACK; goto FADE_INIT_FAIL;}, "fade configuration", sizeof(esp_ae_fade_cfg_t)); + esp_gmf_obj_set_config(obj, cfg, sizeof(esp_ae_fade_cfg_t)); + if (config) { + memcpy(cfg, config, sizeof(esp_ae_fade_cfg_t)); + } else { + esp_ae_fade_cfg_t dcfg = DEFAULT_ESP_GMF_FADE_CONFIG(); + memcpy(cfg, &dcfg, sizeof(esp_ae_fade_cfg_t)); + } + ret = esp_gmf_obj_set_tag(obj, "aud_fade"); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto FADE_INIT_FAIL, "Failed to set obj tag"); + esp_gmf_element_cfg_t el_cfg = {0}; + ESP_GMF_ELEMENT_IN_PORT_ATTR_SET(el_cfg.in_attr, ESP_GMF_EL_PORT_CAP_SINGLE, 0, 0, + ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, ESP_GMF_ELEMENT_PORT_DATA_SIZE_DEFAULT); + ESP_GMF_ELEMENT_IN_PORT_ATTR_SET(el_cfg.out_attr, ESP_GMF_EL_PORT_CAP_SINGLE, 0, 0, + ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, ESP_GMF_ELEMENT_PORT_DATA_SIZE_DEFAULT); + el_cfg.dependency = true; + ret = esp_gmf_audio_el_init(fade, &el_cfg); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto FADE_INIT_FAIL, "Failed to initialize fade element"); + ESP_GMF_ELEMENT_GET(fade)->ops.open = esp_gmf_fade_open; + ESP_GMF_ELEMENT_GET(fade)->ops.process = esp_gmf_fade_process; + ESP_GMF_ELEMENT_GET(fade)->ops.close = esp_gmf_fade_close; + ESP_GMF_ELEMENT_GET(fade)->ops.event_receiver = fade_received_event_handler; + ESP_GMF_ELEMENT_GET(fade)->ops.load_caps = _load_fade_caps_func; + ESP_GMF_ELEMENT_GET(fade)->ops.load_methods = _load_fade_methods_func; + *handle = obj; + ESP_LOGD(TAG, "Initialization, %s-%p", OBJ_GET_TAG(obj), obj); + return ESP_GMF_ERR_OK; +FADE_INIT_FAIL: + esp_gmf_fade_destroy(obj); + return ret; +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/esp_gmf_interleave.c b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/esp_gmf_interleave.c new file mode 100644 index 0000000..36125e7 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/esp_gmf_interleave.c @@ -0,0 +1,270 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#include +#include "esp_log.h" +#include "esp_gmf_oal_mem.h" +#include "esp_gmf_oal_mutex.h" +#include "esp_gmf_node.h" +#include "esp_gmf_interleave.h" +#include "esp_ae_data_weaver.h" +#include "esp_heap_caps.h" +#include "gmf_audio_common.h" +#include "esp_gmf_cap.h" +#include "esp_gmf_caps_def.h" +#include "esp_gmf_audio_element.h" + +#define ESP_GMF_PROCESS_SAMPLE (256) + +/** + * @brief Audio interleave context in GMF + */ +typedef struct { + esp_gmf_audio_element_t parent; /*!< The GMF interleave handle */ + uint8_t bytes_per_sample; /*!< Bytes number of per sampling point */ + esp_gmf_payload_t **in_load; /*!< The array of input payload */ + esp_gmf_payload_t *out_load; /*!< The output payload */ + uint8_t **in_arr; /*!< The array of input buffer pointer */ + uint8_t src_num; /*!< The number of input stream source */ + uint8_t bits_per_sample; /*!< Bits number of per sampling point */ + bool need_reopen : 1; /*!< Whether need to reopen. + True: Execute the close function first, then execute the open function + False: Do nothing */ +} esp_gmf_interleave_t; + +static const char *TAG = "ESP_GMF_INTLV"; + +static inline esp_gmf_err_t dupl_esp_ae_interleave_cfg(esp_gmf_interleave_cfg *config, esp_gmf_interleave_cfg **new_config) +{ + *new_config = esp_gmf_oal_calloc(1, sizeof(*config)); + ESP_GMF_MEM_VERIFY(TAG, *new_config, {return ESP_GMF_ERR_MEMORY_LACK;}, "interleave configuration", sizeof(*config)); + memcpy(*new_config, config, sizeof(*config)); + return ESP_GMF_JOB_ERR_OK; +} + +static inline void free_esp_ae_interleave_cfg(esp_gmf_interleave_cfg *config) +{ + if (config) { + esp_gmf_oal_free(config); + } +} + +static esp_gmf_err_t esp_gmf_interleave_new(void *cfg, esp_gmf_obj_handle_t *handle) +{ + return esp_gmf_interleave_init(cfg, (esp_gmf_element_handle_t *)handle); +} + +static esp_gmf_job_err_t esp_gmf_interleave_open(esp_gmf_element_handle_t self, void *para) +{ + esp_gmf_interleave_t *interleave = (esp_gmf_interleave_t *)self; + esp_gmf_interleave_cfg *interleave_info = (esp_gmf_interleave_cfg *)OBJ_GET_CFG(self); + ESP_GMF_NULL_CHECK(TAG, interleave_info, {return ESP_GMF_JOB_ERR_FAIL;}) + interleave->bytes_per_sample = (interleave_info->bits_per_sample >> 3); + interleave->in_load = esp_gmf_oal_calloc(1, sizeof(esp_gmf_payload_t *) * interleave_info->src_num); + ESP_GMF_MEM_VERIFY(TAG, interleave->in_load, {return ESP_GMF_JOB_ERR_FAIL;}, + "in load", sizeof(esp_gmf_payload_t *) * interleave_info->src_num); + interleave->in_arr = esp_gmf_oal_calloc(1, sizeof(int *) * interleave_info->src_num); + ESP_GMF_MEM_VERIFY(TAG, interleave->in_arr, {return ESP_GMF_JOB_ERR_FAIL;}, + "in buffer array", sizeof(int *) * interleave_info->src_num); + GMF_AUDIO_UPDATE_SND_INFO(self, interleave_info->sample_rate, interleave_info->bits_per_sample, interleave_info->src_num); + interleave->src_num = interleave_info->src_num; + interleave->bits_per_sample = interleave_info->bits_per_sample; + interleave->need_reopen = false; + ESP_LOGD(TAG, "Open, %p", self); + return ESP_GMF_JOB_ERR_OK; +} + +static esp_gmf_job_err_t esp_gmf_interleave_close(esp_gmf_element_handle_t self, void *para) +{ + esp_gmf_interleave_t *interleave = (esp_gmf_interleave_t *)self; + ESP_LOGD(TAG, "Closed, %p", self); + if (interleave->in_arr != NULL) { + esp_gmf_oal_free(interleave->in_arr); + interleave->in_arr = NULL; + } + if (interleave->in_load != NULL) { + esp_gmf_oal_free(interleave->in_load); + interleave->in_load = NULL; + } + return ESP_GMF_ERR_OK; +} + +static esp_gmf_job_err_t esp_gmf_interleave_process(esp_gmf_element_handle_t self, void *para) +{ + esp_gmf_interleave_t *interleave = (esp_gmf_interleave_t *)self; + esp_gmf_job_err_t out_len = ESP_GMF_JOB_ERR_OK; + if (interleave->need_reopen) { + esp_gmf_interleave_close(self, NULL); + out_len = esp_gmf_interleave_open(self, NULL); + if (out_len != ESP_GMF_JOB_ERR_OK) { + ESP_LOGE(TAG, "Interleave reopen failed"); + return out_len; + } + } + esp_gmf_port_handle_t in = ESP_GMF_ELEMENT_GET(self)->in; + esp_gmf_port_handle_t in_port = in; + esp_gmf_port_handle_t out_port = ESP_GMF_ELEMENT_GET(self)->out; + esp_gmf_interleave_cfg *interleave_info = (esp_gmf_interleave_cfg *)OBJ_GET_CFG(self); + ESP_GMF_NULL_CHECK(TAG, interleave_info, return ESP_GMF_JOB_ERR_FAIL); + int index = interleave_info->src_num; + int i = 0; + bool is_done = false; + esp_gmf_err_io_t load_ret = ESP_GMF_IO_OK; + memset(interleave->in_load, 0, sizeof(esp_gmf_payload_t *) * index); + interleave->out_load = NULL; + int samples_num = ESP_GMF_ELEMENT_GET(interleave)->in_attr.data_size / (interleave->bytes_per_sample); + int bytes = samples_num * interleave->bytes_per_sample; + while (in_port != NULL) { + load_ret = esp_gmf_port_acquire_in(in_port, &(interleave->in_load[i]), bytes, in_port->wait_ticks); + ESP_GMF_PORT_CHECK(TAG, load_ret, out_len, {out_len = ESP_GMF_JOB_ERR_FAIL; goto __intlv_release;}, "Failed to acquire in, idx:%d, ret: %d", i, load_ret); + interleave->in_arr[i] = interleave->in_load[i]->buf; + in_port = in_port->next; + // if one load is done means interleave need to done + is_done = (((is_done == 1) || (interleave->in_load[i]->is_done == 1)) ? true : false); + ESP_LOGV(TAG, "IN: idx: %d load: %p, buf: %p, valid size: %d, buf length: %d, done: %d", + i, interleave->in_load[i], interleave->in_load[i]->buf, interleave->in_load[i]->valid_size, + interleave->in_load[i]->buf_length, interleave->in_load[i]->is_done); + i++; + } + samples_num = interleave->in_load[0]->valid_size / (interleave->bytes_per_sample); + bytes = samples_num * interleave->bytes_per_sample; + load_ret = esp_gmf_port_acquire_out(out_port, &interleave->out_load, + samples_num ? bytes * interleave->src_num : interleave->in_load[0]->buf_length, ESP_GMF_MAX_DELAY); + ESP_GMF_PORT_ACQUIRE_OUT_CHECK(TAG, load_ret, out_len, { goto __intlv_release;}); + if (samples_num > 0) { + esp_ae_err_t ret = esp_ae_intlv_process(interleave->src_num, interleave->bits_per_sample, samples_num, + (void **)interleave->in_arr, interleave->out_load->buf); + ESP_GMF_RET_ON_ERROR(TAG, ret, {out_len = ESP_GMF_JOB_ERR_FAIL; goto __intlv_release;}, "Interleave process error, ret: %d", ret); + } + ESP_LOGV(TAG, "OUT: load: %p, buf: %p, valid size: %d, buf length: %d", + interleave->out_load, interleave->out_load->buf, interleave->out_load->valid_size, interleave->out_load->buf_length); + interleave->out_load->valid_size = samples_num * interleave->bytes_per_sample * interleave->src_num; + if (interleave->out_load->valid_size > 0) { + esp_gmf_audio_el_update_file_pos((esp_gmf_element_handle_t)self, interleave->out_load->valid_size); + } + // Interleave finished + if (is_done == true) { + ESP_LOGD(TAG, "Interleave is done."); + interleave->out_load->is_done = true; + out_len = ESP_GMF_JOB_ERR_DONE; + } +__intlv_release: + if (interleave->out_load != NULL) { + load_ret = esp_gmf_port_release_out(out_port, interleave->out_load, ESP_GMF_MAX_DELAY); + if ((load_ret < ESP_GMF_IO_OK) && (load_ret != ESP_GMF_IO_ABORT)) { + ESP_LOGE(TAG, "OUT port release error, ret:%d", load_ret); + out_len = ESP_GMF_JOB_ERR_FAIL; + } + } + in_port = in; + i = 0; + while (in_port != NULL && interleave->in_load[i] != NULL) { + load_ret = esp_gmf_port_release_in(in_port, interleave->in_load[i], ESP_GMF_MAX_DELAY); + if ((load_ret < ESP_GMF_IO_OK) && (load_ret != ESP_GMF_IO_ABORT)) { + ESP_LOGE(TAG, "IN port release error, ret:%d", load_ret); + out_len = ESP_GMF_JOB_ERR_FAIL; + } + in_port = in_port->next; + i++; + } + return out_len; +} + +static esp_gmf_err_t interleave_received_event_handler(esp_gmf_event_pkt_t *evt, void *ctx) +{ + ESP_GMF_NULL_CHECK(TAG, ctx, {return ESP_GMF_ERR_INVALID_ARG;}); + ESP_GMF_NULL_CHECK(TAG, evt, {return ESP_GMF_ERR_INVALID_ARG;}); + if ((evt->type != ESP_GMF_EVT_TYPE_REPORT_INFO) + || (evt->sub != ESP_GMF_INFO_SOUND) + || (evt->payload == NULL)) { + return ESP_GMF_ERR_OK; + } + esp_gmf_element_handle_t self = (esp_gmf_element_handle_t)ctx; + esp_gmf_element_handle_t el = evt->from; + esp_gmf_event_state_t state = ESP_GMF_EVENT_STATE_NONE; + esp_gmf_element_get_state(self, &state); + esp_gmf_info_sound_t *info = (esp_gmf_info_sound_t *)evt->payload; + esp_gmf_interleave_cfg *config = (esp_gmf_interleave_cfg *)OBJ_GET_CFG(self); + ESP_GMF_NULL_CHECK(TAG, config, return ESP_GMF_ERR_FAIL); + esp_gmf_interleave_t *interleave = (esp_gmf_interleave_t *)self; + interleave->need_reopen = (config->sample_rate != info->sample_rates) || (config->bits_per_sample != info->bits); + config->sample_rate = info->sample_rates; + config->bits_per_sample = info->bits; + ESP_LOGD(TAG, "RECV element info, from: %s-%p, next: %p, self: %s-%p, type: %x, state: %s, rate: %d, ch: %d, bits: %d", + OBJ_GET_TAG(el), el, esp_gmf_node_for_next((esp_gmf_node_t *)el), OBJ_GET_TAG(self), self, evt->type, + esp_gmf_event_get_state_str(state), info->sample_rates, info->channels, info->bits); + if (state == ESP_GMF_EVENT_STATE_NONE) { + esp_gmf_element_set_state(self, ESP_GMF_EVENT_STATE_INITIALIZED); + } + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t esp_gmf_interleave_destroy(esp_gmf_element_handle_t self) +{ + esp_gmf_interleave_t *interleave = (esp_gmf_interleave_t *)self; + ESP_LOGD(TAG, "Destroyed, %p", self); + free_esp_ae_interleave_cfg(OBJ_GET_CFG(self)); + esp_gmf_audio_el_deinit(self); + esp_gmf_oal_free(interleave); + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t _load_interleave_caps_func(esp_gmf_element_handle_t handle) +{ + esp_gmf_cap_t *caps = NULL; + esp_gmf_cap_t dec_caps = {0}; + dec_caps.cap_eightcc = ESP_GMF_CAPS_AUDIO_INTERLEAVE; + dec_caps.attr_fun = NULL; + int ret = esp_gmf_cap_append(&caps, &dec_caps); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to create capability"); + + esp_gmf_element_t *el = (esp_gmf_element_t *)handle; + el->caps = caps; + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_interleave_init(esp_gmf_interleave_cfg *config, esp_gmf_element_handle_t *handle) +{ + ESP_GMF_NULL_CHECK(TAG, handle, {return ESP_GMF_ERR_INVALID_ARG;}); + *handle = NULL; + esp_gmf_err_t ret = ESP_GMF_ERR_OK; + esp_gmf_interleave_t *interleave = esp_gmf_oal_calloc(1, sizeof(esp_gmf_interleave_t)); + ESP_GMF_MEM_VERIFY(TAG, interleave, {return ESP_GMF_ERR_MEMORY_LACK;}, "interleave", sizeof(esp_gmf_interleave_t)); + esp_gmf_obj_t *obj = (esp_gmf_obj_t *)interleave; + obj->new_obj = esp_gmf_interleave_new; + obj->del_obj = esp_gmf_interleave_destroy; + esp_gmf_interleave_cfg *cfg = esp_gmf_oal_calloc(1, sizeof(esp_gmf_interleave_cfg)); + ESP_GMF_MEM_VERIFY(TAG, cfg, {ret = ESP_GMF_ERR_MEMORY_LACK; goto INTLV_INIT_FAIL;}, "interleave configuration", sizeof(esp_gmf_interleave_cfg)); + esp_gmf_obj_set_config(obj, cfg, sizeof(esp_gmf_interleave_cfg)); + if (config) { + memcpy(cfg, config, sizeof(esp_gmf_interleave_cfg)); + } else { + esp_gmf_interleave_cfg dcfg = DEFAULT_ESP_GMF_INTERLEAVE_CONFIG(); + memcpy(cfg, &dcfg, sizeof(esp_gmf_interleave_cfg)); + } + ret = esp_gmf_obj_set_tag(obj, "aud_intlv"); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto INTLV_INIT_FAIL, "Failed to set obj tag"); + esp_gmf_element_cfg_t el_cfg = {0}; + ESP_GMF_ELEMENT_IN_PORT_ATTR_SET(el_cfg.in_attr, ESP_GMF_EL_PORT_CAP_MULTI, 0, 0, + ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, ESP_GMF_ELEMENT_PORT_DATA_SIZE_DEFAULT); + ESP_GMF_ELEMENT_IN_PORT_ATTR_SET(el_cfg.out_attr, ESP_GMF_EL_PORT_CAP_SINGLE, 0, 0, + ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, ESP_GMF_ELEMENT_PORT_DATA_SIZE_DEFAULT); + el_cfg.dependency = true; + ret = esp_gmf_audio_el_init(interleave, &el_cfg); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto INTLV_INIT_FAIL, "Failed to initialize interleave element"); + ESP_GMF_ELEMENT_GET(interleave)->ops.open = esp_gmf_interleave_open; + ESP_GMF_ELEMENT_GET(interleave)->ops.process = esp_gmf_interleave_process; + ESP_GMF_ELEMENT_GET(interleave)->ops.close = esp_gmf_interleave_close; + ESP_GMF_ELEMENT_GET(interleave)->ops.event_receiver = interleave_received_event_handler; + ESP_GMF_ELEMENT_GET(interleave)->ops.load_caps = _load_interleave_caps_func; + *handle = obj; + ESP_LOGD(TAG, "Initialization, %s-%p", OBJ_GET_TAG(obj), obj); + return ESP_GMF_ERR_OK; +INTLV_INIT_FAIL: + esp_gmf_interleave_destroy(obj); + return ret; +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/esp_gmf_mixer.c b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/esp_gmf_mixer.c new file mode 100644 index 0000000..cd4834e --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/esp_gmf_mixer.c @@ -0,0 +1,437 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#include +#include "esp_log.h" +#include "esp_gmf_oal_mem.h" +#include "esp_gmf_oal_mutex.h" +#include "esp_gmf_node.h" +#include "esp_gmf_mixer.h" +#include "esp_heap_caps.h" +#include "gmf_audio_common.h" +#include "esp_gmf_audio_methods_def.h" +#include "esp_gmf_cap.h" +#include "esp_gmf_caps_def.h" +#include "esp_gmf_audio_element.h" +#ifdef MIXER_DEBUG +#include "esp_timer.h" +#endif + +#define MIXER_DEFAULT_PROC_TIME_MS (10) +#define MIXER_GET_FRAME_BYTE_SIZE(info) (MIXER_DEFAULT_PROC_TIME_MS * info->sample_rate * info->channel * info->bits_per_sample / 8000) + +typedef struct { + esp_gmf_audio_element_t parent; /*!< The GMF mixer handle */ + esp_ae_mixer_handle_t mixer_hd; /*!< The audio effects mixer handle */ + uint8_t bytes_per_sample; /*!< Bytes number of per sampling point */ + uint32_t process_num; /*!< The data number of mixer processing */ + uint32_t frame_time; /*!< The time of one frame, unit: ms */ + esp_gmf_payload_t **in_load; /*!< The array of input payload */ + esp_gmf_payload_t *out_load; /*!< The output payload */ + uint8_t **in_arr; /*!< The input buffer pointer array of mixer */ + esp_ae_mixer_mode_t *mode; /*!< The mixer mode */ + uint8_t src_num; /*!< The number of source */ + bool need_reopen : 1; /*!< Whether need to reopen. + True: Execute the close function first, then execute the open function + False: Do nothing */ +} esp_gmf_mixer_t; + +static const char *TAG = "ESP_GMF_MIXER"; + +const esp_ae_mixer_info_t esp_gmf_default_mixer_src_info[] = { + {1.0, 0.5, 500}, + {0.5, 0.0, 500}, +}; + +static inline esp_gmf_err_t dupl_esp_ae_mixer_cfg(esp_ae_mixer_cfg_t *config, esp_ae_mixer_cfg_t **new_config) +{ + void *sub_cfg = NULL; + *new_config = esp_gmf_oal_calloc(1, sizeof(*config)); + ESP_GMF_MEM_VERIFY(TAG, *new_config, {return ESP_GMF_ERR_MEMORY_LACK;}, "mixer configuration", sizeof(*config)); + memcpy(*new_config, config, sizeof(*config)); + if (config->src_info && (config->src_num > 0)) { + sub_cfg = esp_gmf_oal_calloc(1, config->src_num * sizeof(esp_ae_mixer_info_t)); + ESP_GMF_MEM_VERIFY(TAG, sub_cfg, {esp_gmf_oal_free(*new_config); return ESP_GMF_ERR_MEMORY_LACK;}, + "source information", config->src_num * sizeof(esp_ae_mixer_info_t)); + memcpy(sub_cfg, config->src_info, config->src_num * sizeof(esp_ae_mixer_info_t)); + (*new_config)->src_info = sub_cfg; + } + return ESP_GMF_JOB_ERR_OK; +} + +static inline void free_esp_ae_mixer_cfg(esp_ae_mixer_cfg_t *config) +{ + if (config) { + if (config->src_info && (config->src_info != esp_gmf_default_mixer_src_info)) { + esp_gmf_oal_free(config->src_info); + } + esp_gmf_oal_free(config); + } +} + +static esp_gmf_err_t __mixer_set_mode(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc, + uint8_t *buf, int buf_len) +{ + ESP_GMF_NULL_CHECK(TAG, arg_desc, {return ESP_GMF_ERR_INVALID_ARG;}); + ESP_GMF_NULL_CHECK(TAG, buf, {return ESP_GMF_ERR_INVALID_ARG;}); + uint8_t src_idx = (uint8_t)(*buf); + return esp_gmf_mixer_set_mode(handle, src_idx, *((esp_ae_mixer_mode_t *)(buf + arg_desc->next->offset))); +} + +static esp_gmf_err_t __mixer_set_audio_info(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc, + uint8_t *buf, int buf_len) +{ + ESP_GMF_NULL_CHECK(TAG, handle, {return ESP_GMF_ERR_INVALID_ARG;}); + ESP_GMF_NULL_CHECK(TAG, arg_desc, {return ESP_GMF_ERR_INVALID_ARG;}); + ESP_GMF_NULL_CHECK(TAG, buf, {return ESP_GMF_ERR_INVALID_ARG;}); + esp_gmf_args_desc_t *mix_desc = arg_desc; + uint32_t rate = *((uint32_t *)buf); + mix_desc = mix_desc->next; + uint8_t ch = (uint8_t)(*(buf + mix_desc->offset)); + mix_desc = mix_desc->next; + uint8_t bits = (uint8_t)(*(buf + mix_desc->offset)); + return esp_gmf_mixer_set_audio_info(handle, rate, bits, ch); +} + +static esp_gmf_err_t esp_gmf_mixer_new(void *cfg, esp_gmf_obj_handle_t *handle) +{ + return esp_gmf_mixer_init(cfg, (esp_gmf_element_handle_t *)handle); +} + +static esp_gmf_job_err_t esp_gmf_mixer_open(esp_gmf_element_handle_t self, void *para) +{ + esp_gmf_mixer_t *mixer = (esp_gmf_mixer_t *)self; + esp_ae_mixer_cfg_t *mixer_info = (esp_ae_mixer_cfg_t *)OBJ_GET_CFG(self); + ESP_GMF_NULL_CHECK(TAG, mixer_info, {return ESP_GMF_JOB_ERR_FAIL;}) + mixer->bytes_per_sample = (mixer_info->bits_per_sample >> 3) * mixer_info->channel; + esp_ae_mixer_open(mixer_info, &mixer->mixer_hd); + ESP_GMF_CHECK(TAG, mixer->mixer_hd, {return ESP_GMF_JOB_ERR_FAIL;}, "Failed to create mixer handle"); + for (int i = 0; i < mixer_info->src_num; i++) { + esp_ae_mixer_set_mode(mixer->mixer_hd, i, mixer->mode[i]); + } + uint32_t process_num = MIXER_GET_FRAME_BYTE_SIZE(mixer_info); + mixer->process_num = (ESP_GMF_ELEMENT_GET(mixer)->in_attr.data_size == 0) ? (process_num) : (ESP_GMF_ELEMENT_GET(mixer)->in_attr.data_size); + mixer->frame_time = GMF_AUDIO_CALC_PTS(mixer->process_num, mixer_info->sample_rate, mixer_info->channel, mixer_info->bits_per_sample); + mixer->src_num = mixer_info->src_num; + mixer->in_load = esp_gmf_oal_calloc(1, sizeof(esp_gmf_payload_t *) * mixer_info->src_num); + ESP_GMF_MEM_VERIFY(TAG, mixer->in_load, {return ESP_GMF_JOB_ERR_FAIL;}, + "in load", sizeof(esp_gmf_payload_t *) * mixer_info->src_num); + mixer->in_arr = esp_gmf_oal_calloc(1, sizeof(int *) * mixer_info->src_num); + ESP_GMF_MEM_VERIFY(TAG, mixer->in_arr, {return ESP_GMF_JOB_ERR_FAIL;}, + "in buffer array", sizeof(int *) * mixer_info->src_num); + GMF_AUDIO_UPDATE_SND_INFO(self, mixer_info->sample_rate, mixer_info->bits_per_sample, mixer_info->channel); + + mixer->need_reopen = false; + ESP_LOGD(TAG, "Open, %p", self); + return ESP_GMF_JOB_ERR_OK; +} + +static esp_gmf_job_err_t esp_gmf_mixer_close(esp_gmf_element_handle_t self, void *para) +{ + esp_gmf_mixer_t *mixer = (esp_gmf_mixer_t *)self; + ESP_LOGD(TAG, "Closed, %p", self); + if (mixer->mixer_hd != NULL) { + esp_ae_mixer_close(mixer->mixer_hd); + mixer->mixer_hd = NULL; + } + if (mixer->in_arr != NULL) { + esp_gmf_oal_free(mixer->in_arr); + mixer->in_arr = NULL; + } + if (mixer->in_load != NULL) { + esp_gmf_oal_free(mixer->in_load); + mixer->in_load = NULL; + } + return ESP_GMF_ERR_OK; +} + +static esp_gmf_job_err_t esp_gmf_mixer_process(esp_gmf_element_handle_t self, void *para) +{ + esp_gmf_mixer_t *mixer = (esp_gmf_mixer_t *)self; + esp_gmf_job_err_t out_len = ESP_GMF_JOB_ERR_OK; + if (mixer->need_reopen) { + esp_gmf_mixer_close(self, NULL); + out_len = esp_gmf_mixer_open(self, NULL); + if (out_len != ESP_GMF_JOB_ERR_OK) { + ESP_LOGE(TAG, "Mixer reopen failed"); + return out_len; + } + } + int read_len = 0; + esp_gmf_err_io_t ret = ESP_GMF_IO_OK; + int status_end = 0; + ESP_GMF_RET_ON_NOT_OK(TAG, ret, { return ESP_GMF_JOB_ERR_FAIL;}, "Failed to apply mixer setting"); + esp_gmf_port_handle_t in = ESP_GMF_ELEMENT_GET(self)->in; + esp_gmf_port_handle_t in_port = in; + esp_gmf_port_handle_t out_port = ESP_GMF_ELEMENT_GET(self)->out; + int index = mixer->src_num; + memset(mixer->in_load, 0, sizeof(esp_gmf_payload_t *) * index); + mixer->out_load = NULL; + int i = 0; +#ifdef MIXER_DEBUG + uint64_t start = 0; + uint64_t end = 0; + uint64_t acquire_in_time = 0; + uint32_t frame_time = mixer->frame_time; +#endif + while (in_port != NULL) { +#ifdef MIXER_DEBUG + start = esp_timer_get_time(); +#endif + int wait_ticks = ((in_port->wait_ticks < (mixer->frame_time / index)) ? (mixer->frame_time / index) : (in_port->wait_ticks)); + ret = esp_gmf_port_acquire_in(in_port, &(mixer->in_load[i]), mixer->process_num, wait_ticks); +#ifdef MIXER_DEBUG + end = esp_timer_get_time(); + acquire_in_time += ((end - start) / 1000); + ESP_LOGD(TAG, "Port %d acquire in time: %lld ms, wait ticks: %d", i, (end - start) / 1000, wait_ticks); +#endif + if (ret == ESP_GMF_IO_FAIL || mixer->in_load[i]->buf == NULL) { + ESP_LOGE(TAG, "Acquire in failed, idx:%d, ret: %d", i, ret); + out_len = ESP_GMF_JOB_ERR_FAIL; + goto __mixer_release; + } + if (ret == ESP_GMF_IO_TIMEOUT || ret == ESP_GMF_IO_ABORT || mixer->in_load[i]->is_done) { + status_end++; + } + read_len = mixer->in_load[i]->valid_size; + mixer->in_arr[i] = mixer->in_load[i]->buf; + if (read_len < mixer->process_num) { + memset(mixer->in_arr[i] + read_len, 0, mixer->process_num - read_len); + } + in_port = in_port->next; + ESP_LOGV(TAG, "IN: idx: %d load: %p, buf: %p, valid size: %d, buf length: %d, done: %d", + i, mixer->in_load[i], mixer->in_load[i]->buf, mixer->in_load[i]->valid_size, + mixer->in_load[i]->buf_length, mixer->in_load[i]->is_done); + i++; + } +#ifdef MIXER_DEBUG + if (acquire_in_time > frame_time) { + ESP_LOGW(TAG, "Total acquire in time: %lld ms, frame time: %ld ms", acquire_in_time, frame_time); + } +#endif + ret = esp_gmf_port_acquire_out(out_port, &mixer->out_load, mixer->process_num, ESP_GMF_MAX_DELAY); + ESP_GMF_PORT_ACQUIRE_OUT_CHECK(TAG, ret, out_len, { goto __mixer_release;}); + // Down-mixer never stop in gmf, only user can set to stop + if (status_end == index) { + memset(mixer->out_load->buf, 0, mixer->process_num); + out_len = ESP_GMF_JOB_ERR_OK; + goto __mixer_release; + } + esp_gmf_oal_mutex_lock(((esp_gmf_audio_element_t *)self)->lock); + esp_ae_err_t porc_ret = esp_ae_mixer_process(mixer->mixer_hd, mixer->process_num / mixer->bytes_per_sample, + (void *)mixer->in_arr, mixer->out_load->buf); + esp_gmf_oal_mutex_unlock(((esp_gmf_audio_element_t *)self)->lock); + if (porc_ret != ESP_AE_ERR_OK) { + ESP_LOGE(TAG, "Mix process error %d.", porc_ret); + return ESP_GMF_JOB_ERR_FAIL; + } + ESP_LOGV(TAG, "OUT: load: %p, buf: %p, valid size: %d, buf length: %d", + mixer->out_load, mixer->out_load->buf, mixer->out_load->valid_size, mixer->out_load->buf_length); + mixer->out_load->valid_size = mixer->process_num; + if (mixer->out_load->valid_size > 0) { + esp_gmf_audio_el_update_file_pos((esp_gmf_element_handle_t)self, mixer->out_load->valid_size); + } +__mixer_release: + // Release in and out port + if (mixer->out_load != NULL) { + ret = esp_gmf_port_release_out(out_port, mixer->out_load, ESP_GMF_MAX_DELAY); + if ((ret < ESP_GMF_IO_OK) && (ret != ESP_GMF_IO_ABORT)) { + ESP_LOGE(TAG, "OUT port release error, ret:%d", ret); + out_len = ESP_GMF_JOB_ERR_FAIL; + } + } + in_port = in; + i = 0; + while (in_port != NULL && mixer->in_load[i] != NULL) { + ret = esp_gmf_port_release_in(in_port, mixer->in_load[i], ESP_GMF_MAX_DELAY); + if ((ret < ESP_GMF_IO_OK) && (ret != ESP_GMF_IO_ABORT)) { + ESP_LOGE(TAG, "IN port release error, ret:%d", ret); + out_len = ESP_GMF_JOB_ERR_FAIL; + } + in_port = in_port->next; + i++; + } + return out_len; +} + +static esp_gmf_err_t mixer_received_event_handler(esp_gmf_event_pkt_t *evt, void *ctx) +{ + ESP_GMF_NULL_CHECK(TAG, ctx, {return ESP_GMF_ERR_INVALID_ARG;}); + ESP_GMF_NULL_CHECK(TAG, evt, {return ESP_GMF_ERR_INVALID_ARG;}); + if ((evt->type != ESP_GMF_EVT_TYPE_REPORT_INFO) + || (evt->sub != ESP_GMF_INFO_SOUND) + || (evt->payload == NULL)) { + return ESP_GMF_ERR_OK; + } + esp_gmf_element_handle_t self = (esp_gmf_element_handle_t)ctx; + esp_gmf_element_handle_t el = evt->from; + esp_gmf_event_state_t state = ESP_GMF_EVENT_STATE_NONE; + esp_gmf_element_get_state(self, &state); + esp_gmf_info_sound_t *info = (esp_gmf_info_sound_t *)evt->payload; + esp_ae_mixer_cfg_t *config = (esp_ae_mixer_cfg_t *)OBJ_GET_CFG(self); + ESP_GMF_NULL_CHECK(TAG, config, return ESP_GMF_ERR_FAIL); + esp_gmf_mixer_t *mixer = (esp_gmf_mixer_t *)self; + mixer->need_reopen = (config->sample_rate != info->sample_rates) || (info->channels != config->channel) || (config->bits_per_sample != info->bits); + config->sample_rate = info->sample_rates; + config->channel = info->channels; + config->bits_per_sample = info->bits; + ESP_LOGD(TAG, "RECV element info, from: %s-%p, next: %p, self: %s-%p, type: %x, state: %s, rate: %d, ch: %d, bits: %d", + OBJ_GET_TAG(el), el, esp_gmf_node_for_next((esp_gmf_node_t *)el), OBJ_GET_TAG(self), self, evt->type, + esp_gmf_event_get_state_str(state), info->sample_rates, info->channels, info->bits); + if (state == ESP_GMF_EVENT_STATE_NONE) { + esp_gmf_element_set_state(self, ESP_GMF_EVENT_STATE_INITIALIZED); + } + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t esp_gmf_mixer_destroy(esp_gmf_element_handle_t self) +{ + esp_gmf_mixer_t *mixer = (esp_gmf_mixer_t *)self; + ESP_LOGD(TAG, "Destroyed, %p", self); + free_esp_ae_mixer_cfg(OBJ_GET_CFG(self)); + if (mixer->mode) { + esp_gmf_oal_free(mixer->mode); + } + esp_gmf_audio_el_deinit(self); + esp_gmf_oal_free(mixer); + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t _load_mixer_caps_func(esp_gmf_element_handle_t handle) +{ + esp_gmf_cap_t *caps = NULL; + esp_gmf_cap_t dec_caps = {0}; + dec_caps.cap_eightcc = ESP_GMF_CAPS_AUDIO_MIXER; + dec_caps.attr_fun = NULL; + int ret = esp_gmf_cap_append(&caps, &dec_caps); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to create capability"); + + esp_gmf_element_t *el = (esp_gmf_element_t *)handle; + el->caps = caps; + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t _load_mixer_methods_func(esp_gmf_element_handle_t handle) +{ + esp_gmf_method_t *method = NULL; + esp_gmf_args_desc_t *set_args = NULL; + esp_gmf_err_t ret = esp_gmf_args_desc_append(&set_args, AMETHOD_ARG(MIXER, SET_INFO, RATE), ESP_GMF_ARGS_TYPE_UINT32, sizeof(uint32_t), 0); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to append RATE argument"); + ret = esp_gmf_args_desc_append(&set_args, AMETHOD_ARG(MIXER, SET_INFO, CH), ESP_GMF_ARGS_TYPE_UINT8, + sizeof(uint8_t), sizeof(uint32_t)); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to append CHANNEL argument"); + ret = esp_gmf_args_desc_append(&set_args, AMETHOD_ARG(MIXER, SET_INFO, BITS), ESP_GMF_ARGS_TYPE_UINT8, + sizeof(uint8_t), sizeof(uint8_t) + sizeof(uint32_t)); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to append BITS argument"); + ret = esp_gmf_method_append(&method, AMETHOD(MIXER, SET_INFO), __mixer_set_audio_info, set_args); + ESP_GMF_RET_ON_ERROR(TAG, ret, {return ret;}, "Failed to register %s method", AMETHOD(MIXER, SET_INFO)); + + set_args = NULL; + ret = esp_gmf_args_desc_append(&set_args, AMETHOD_ARG(MIXER, SET_MODE, IDX), ESP_GMF_ARGS_TYPE_UINT8, sizeof(uint8_t), 0); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to append INDEX argument"); + ret = esp_gmf_args_desc_append(&set_args, AMETHOD_ARG(MIXER, SET_MODE, MODE), ESP_GMF_ARGS_TYPE_INT32, + sizeof(int32_t), sizeof(uint8_t)); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to append MODE argument"); + ret = esp_gmf_method_append(&method, AMETHOD(MIXER, SET_MODE), __mixer_set_mode, set_args); + ESP_GMF_RET_ON_ERROR(TAG, ret, {return ret;}, "Failed to register %s method", AMETHOD(MIXER, SET_MODE)); + + esp_gmf_element_t *el = (esp_gmf_element_t *)handle; + el->method = method; + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_mixer_set_mode(esp_gmf_element_handle_t handle, uint8_t src_idx, esp_ae_mixer_mode_t mode) +{ + ESP_GMF_NULL_CHECK(TAG, handle, { return ESP_GMF_ERR_INVALID_ARG;}); + esp_gmf_mixer_t *mixer = (esp_gmf_mixer_t *)handle; + esp_ae_mixer_cfg_t *cfg = (esp_ae_mixer_cfg_t *)OBJ_GET_CFG(handle); + ESP_GMF_NULL_CHECK(TAG, cfg, return ESP_GMF_ERR_FAIL); + if (src_idx >= cfg->src_num) { + ESP_LOGE(TAG, "Source index %d overlimit %d hd:%p", src_idx, cfg->src_num, mixer); + return ESP_GMF_ERR_INVALID_ARG; + } + if (mixer->mixer_hd) { + esp_gmf_oal_mutex_lock(((esp_gmf_audio_element_t *)handle)->lock); + esp_ae_err_t ret = esp_ae_mixer_set_mode(mixer->mixer_hd, src_idx, mode); + esp_gmf_oal_mutex_unlock(((esp_gmf_audio_element_t *)handle)->lock); + ESP_GMF_RET_ON_ERROR(TAG, ret, return ESP_GMF_JOB_ERR_FAIL;, "mixerualize set error %d", ret); + } + mixer->mode[src_idx] = mode; + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_mixer_set_audio_info(esp_gmf_element_handle_t handle, uint32_t sample_rate, + uint8_t bits, uint8_t channel) +{ + ESP_GMF_NULL_CHECK(TAG, handle, { return ESP_GMF_ERR_INVALID_ARG;}); + esp_ae_mixer_cfg_t *cfg = (esp_ae_mixer_cfg_t *)OBJ_GET_CFG(handle); + ESP_GMF_NULL_CHECK(TAG, cfg, return ESP_GMF_ERR_FAIL); + if (cfg->sample_rate == sample_rate && cfg->bits_per_sample == bits && cfg->channel == channel) { + return ESP_GMF_ERR_OK; + } + cfg->sample_rate = sample_rate; + cfg->bits_per_sample = bits; + cfg->channel = channel; + esp_gmf_mixer_t *mixer = (esp_gmf_mixer_t *)handle; + mixer->need_reopen = true; + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_mixer_init(esp_ae_mixer_cfg_t *config, esp_gmf_element_handle_t *handle) +{ + ESP_GMF_NULL_CHECK(TAG, handle, {return ESP_GMF_ERR_INVALID_ARG;}); + *handle = NULL; + esp_gmf_err_t ret = ESP_GMF_ERR_OK; + esp_gmf_mixer_t *mixer = esp_gmf_oal_calloc(1, sizeof(esp_gmf_mixer_t)); + ESP_GMF_MEM_VERIFY(TAG, mixer, {return ESP_GMF_ERR_MEMORY_LACK;}, "mixer", sizeof(esp_gmf_mixer_t)); + esp_gmf_obj_t *obj = (esp_gmf_obj_t *)mixer; + obj->new_obj = esp_gmf_mixer_new; + obj->del_obj = esp_gmf_mixer_destroy; + esp_ae_mixer_cfg_t *cfg = NULL; + if (config) { + if (config->src_info == NULL) { + config->src_info = (esp_ae_mixer_info_t *)esp_gmf_default_mixer_src_info; + config->src_num = sizeof(esp_gmf_default_mixer_src_info) / sizeof(esp_ae_mixer_info_t); + } + dupl_esp_ae_mixer_cfg(config, &cfg); + } else { + esp_ae_mixer_cfg_t dcfg = DEFAULT_ESP_GMF_MIXER_CONFIG(); + dcfg.src_info = (esp_ae_mixer_info_t *)esp_gmf_default_mixer_src_info; + dcfg.src_num = sizeof(esp_gmf_default_mixer_src_info) / sizeof(esp_ae_mixer_info_t); + dupl_esp_ae_mixer_cfg(&dcfg, &cfg); + } + ESP_GMF_CHECK(TAG, cfg, ret = ESP_GMF_ERR_MEMORY_LACK; goto MIXER_INIT_FAIL;, "Failed to allocate mixer configuration"); + esp_gmf_obj_set_config(obj, cfg, sizeof(esp_ae_mixer_cfg_t)); + mixer->mode = esp_gmf_oal_calloc(cfg->src_num, sizeof(esp_ae_mixer_mode_t)); + ESP_GMF_MEM_VERIFY(TAG, mixer->mode, {ret = ESP_GMF_ERR_MEMORY_LACK; goto MIXER_INIT_FAIL;}, "Allocate(%d) failed", cfg->src_num * sizeof(esp_ae_mixer_mode_t)); + for (int i = 0; i < cfg->src_num; i++) { + mixer->mode[i] = ESP_AE_MIXER_MODE_FADE_UPWARD; + } + ret = esp_gmf_obj_set_tag(obj, "aud_mixer"); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto MIXER_INIT_FAIL, "Failed to set obj tag"); + esp_gmf_element_cfg_t el_cfg = {0}; + mixer->process_num = MIXER_GET_FRAME_BYTE_SIZE(cfg); + ESP_GMF_ELEMENT_IN_PORT_ATTR_SET(el_cfg.in_attr, ESP_GMF_EL_PORT_CAP_MULTI, 0, 0, + ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, mixer->process_num); + ESP_GMF_ELEMENT_IN_PORT_ATTR_SET(el_cfg.out_attr, ESP_GMF_EL_PORT_CAP_SINGLE, 0, 0, + ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, mixer->process_num); + el_cfg.dependency = true; + ret = esp_gmf_audio_el_init(mixer, &el_cfg); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto MIXER_INIT_FAIL, "Failed to initialize mixer element"); + ESP_GMF_ELEMENT_GET(mixer)->ops.open = esp_gmf_mixer_open; + ESP_GMF_ELEMENT_GET(mixer)->ops.process = esp_gmf_mixer_process; + ESP_GMF_ELEMENT_GET(mixer)->ops.close = esp_gmf_mixer_close; + ESP_GMF_ELEMENT_GET(mixer)->ops.event_receiver = mixer_received_event_handler; + ESP_GMF_ELEMENT_GET(mixer)->ops.load_caps = _load_mixer_caps_func; + ESP_GMF_ELEMENT_GET(mixer)->ops.load_methods = _load_mixer_methods_func; + *handle = obj; + ESP_LOGD(TAG, "Initialization, %s-%p", OBJ_GET_TAG(obj), obj); + return ESP_GMF_ERR_OK; +MIXER_INIT_FAIL: + esp_gmf_mixer_destroy(obj); + return ret; +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/esp_gmf_rate_cvt.c b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/esp_gmf_rate_cvt.c new file mode 100644 index 0000000..2238ca0 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/esp_gmf_rate_cvt.c @@ -0,0 +1,276 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#include +#include "esp_log.h" +#include "esp_gmf_oal_mem.h" +#include "esp_gmf_oal_mutex.h" +#include "esp_gmf_node.h" +#include "esp_gmf_rate_cvt.h" +#include "gmf_audio_common.h" +#include "esp_gmf_audio_methods_def.h" +#include "esp_gmf_cap.h" +#include "esp_gmf_caps_def.h" +#include "esp_gmf_audio_element.h" + +/** + * @brief Audio rate conversion context in GMF + */ +typedef struct { + esp_gmf_audio_element_t parent; /*!< The GMF rate cvt handle */ + esp_ae_rate_cvt_handle_t rate_hd; /*!< The audio effects rate cvt handle */ + uint8_t bytes_per_sample; /*!< Bytes number of per sampling point */ + bool need_reopen : 1; /*!< Whether need to reopen. + True: Execute the close function first, then execute the open function + False: Do nothing */ + bool bypass : 1; /*!< Whether bypass. True: need bypass. False: needn't bypass */ +} esp_gmf_rate_cvt_t; + +static const char *TAG = "ESP_GMF_RATE_CVT"; + +static esp_gmf_err_t __rate_cvt_set_dest_rate(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc, + uint8_t *buf, int buf_len) +{ + ESP_GMF_NULL_CHECK(TAG, buf, {return ESP_GMF_ERR_INVALID_ARG;}); + uint32_t dest_rate = *((uint32_t *)buf); + return esp_gmf_rate_cvt_set_dest_rate(handle, dest_rate); +} + +static esp_gmf_err_t esp_gmf_rate_cvt_new(void *cfg, esp_gmf_obj_handle_t *handle) +{ + return esp_gmf_rate_cvt_init(cfg, (esp_gmf_element_handle_t *)handle); +} + +static esp_gmf_job_err_t esp_gmf_rate_cvt_open(esp_gmf_element_handle_t self, void *para) +{ + esp_gmf_rate_cvt_t *rate_cvt = (esp_gmf_rate_cvt_t *)self; + esp_ae_rate_cvt_cfg_t *rate_info = (esp_ae_rate_cvt_cfg_t *)OBJ_GET_CFG(self); + ESP_GMF_NULL_CHECK(TAG, rate_info, {return ESP_GMF_JOB_ERR_FAIL;}); + rate_cvt->bytes_per_sample = (rate_info->bits_per_sample >> 3) * rate_info->channel; + esp_ae_rate_cvt_open(rate_info, &rate_cvt->rate_hd); + ESP_GMF_CHECK(TAG, rate_cvt->rate_hd, {return ESP_GMF_JOB_ERR_FAIL;}, "Failed to create rate conversion handle"); + GMF_AUDIO_UPDATE_SND_INFO(self, rate_info->dest_rate, rate_info->bits_per_sample, rate_info->channel); + ESP_LOGD(TAG, "Open, src: %"PRIu32", dest: %"PRIu32", ch: %d, bits: %d", + rate_info->src_rate, rate_info->dest_rate, rate_info->channel, rate_info->bits_per_sample); + rate_cvt->need_reopen = false; + rate_cvt->bypass = rate_info->src_rate == rate_info->dest_rate; + return ESP_GMF_JOB_ERR_OK; +} + +static esp_gmf_job_err_t esp_gmf_rate_cvt_close(esp_gmf_element_handle_t self, void *para) +{ + esp_gmf_rate_cvt_t *rate_cvt = (esp_gmf_rate_cvt_t *)self; + ESP_LOGD(TAG, "Closed, %p", self); + if (rate_cvt->rate_hd != NULL) { + esp_ae_rate_cvt_close(rate_cvt->rate_hd); + rate_cvt->rate_hd = NULL; + } + return ESP_GMF_JOB_ERR_OK; +} + +static esp_gmf_job_err_t esp_gmf_rate_cvt_process(esp_gmf_element_handle_t self, void *para) +{ + esp_gmf_rate_cvt_t *rate_cvt = (esp_gmf_rate_cvt_t *)self; + esp_gmf_job_err_t out_len = ESP_GMF_JOB_ERR_OK; + if (rate_cvt->need_reopen) { + esp_gmf_rate_cvt_close(self, NULL); + out_len = esp_gmf_rate_cvt_open(self, NULL); + if (out_len != ESP_GMF_JOB_ERR_OK) { + ESP_LOGE(TAG, "Rate conversion reopen failed"); + return out_len; + } + } + esp_ae_err_t ret = ESP_AE_ERR_OK; + esp_gmf_port_handle_t in_port = ESP_GMF_ELEMENT_GET(self)->in; + esp_gmf_port_handle_t out_port = ESP_GMF_ELEMENT_GET(self)->out; + esp_gmf_payload_t *in_load = NULL; + esp_gmf_payload_t *out_load = NULL; + int samples_num = ESP_GMF_ELEMENT_GET(rate_cvt)->in_attr.data_size / (rate_cvt->bytes_per_sample); + int bytes = samples_num * rate_cvt->bytes_per_sample; + esp_gmf_err_io_t load_ret = esp_gmf_port_acquire_in(in_port, &in_load, bytes, ESP_GMF_MAX_DELAY); + samples_num = in_load->valid_size / (rate_cvt->bytes_per_sample); + bytes = samples_num * rate_cvt->bytes_per_sample; + if((bytes != in_load->valid_size) || (load_ret < ESP_GMF_IO_OK)) { + ESP_LOGE(TAG, "Invalid in load size %d, ret %d", in_load->valid_size, load_ret); + out_len = ESP_GMF_JOB_ERR_FAIL; + goto __rate_release; + } + uint32_t out_samples_num = 0; + if (samples_num) { + ret = esp_ae_rate_cvt_get_max_out_sample_num(rate_cvt->rate_hd, samples_num, &out_samples_num); + ESP_GMF_RET_ON_ERROR(TAG, ret, {out_len = ESP_GMF_JOB_ERR_FAIL; goto __rate_release;}, "Failed to get resample out size, ret: %d", ret); + } + int acq_out_size = out_samples_num == 0 ? in_load->buf_length : out_samples_num * rate_cvt->bytes_per_sample; + if (rate_cvt->bypass && (in_port->is_shared == true)) { + // This case rate conversion is do bypass + out_load = in_load; + } + load_ret = esp_gmf_port_acquire_out(out_port, &out_load, acq_out_size, ESP_GMF_MAX_DELAY); + ESP_GMF_PORT_ACQUIRE_OUT_CHECK(TAG, load_ret, out_len, {goto __rate_release;}); + if (samples_num) { + ret = esp_ae_rate_cvt_process(rate_cvt->rate_hd, (unsigned char *)in_load->buf, samples_num, + (unsigned char *)out_load->buf, &out_samples_num); + ESP_GMF_RET_ON_ERROR(TAG, ret, {out_len = ESP_GMF_JOB_ERR_FAIL; goto __rate_release;}, "Rate conversion process error, ret: %d", ret); + } + out_load->valid_size = out_samples_num * rate_cvt->bytes_per_sample; + out_load->pts = in_load->pts; + out_load->is_done = in_load->is_done; + ESP_LOGV(TAG, "Out Samples: %ld, IN-PLD: %p-%p-%d-%d-%d, OUT-PLD: %p-%p-%d-%d-%d", out_samples_num, in_load, in_load->buf, + in_load->valid_size, in_load->buf_length, in_load->is_done, out_load, + out_load->buf, out_load->valid_size, out_load->buf_length, out_load->is_done); + esp_gmf_audio_el_update_file_pos((esp_gmf_element_handle_t)self, out_load->valid_size); + if (in_load->is_done) { + out_len = ESP_GMF_JOB_ERR_DONE; + ESP_LOGD(TAG, "Rate convert done, out len: %d", out_load->valid_size); + } +__rate_release: + // Release in and out port + if (out_load != NULL) { + load_ret = esp_gmf_port_release_out(out_port, out_load, ESP_GMF_MAX_DELAY); + if ((load_ret < ESP_GMF_IO_OK) && (load_ret != ESP_GMF_IO_ABORT)) { + ESP_LOGE(TAG, "OUT port release error, ret:%d", load_ret); + out_len = ESP_GMF_JOB_ERR_FAIL; + } + } + if (in_load != NULL) { + load_ret = esp_gmf_port_release_in(in_port, in_load, ESP_GMF_MAX_DELAY); + if ((load_ret < ESP_GMF_IO_OK) && (load_ret != ESP_GMF_IO_ABORT)) { + ESP_LOGE(TAG, "IN port release error, ret:%d", load_ret); + out_len = ESP_GMF_JOB_ERR_FAIL; + } + } + return out_len; +} + +static esp_gmf_err_t rate_cvt_received_event_handler(esp_gmf_event_pkt_t *evt, void *ctx) +{ + ESP_GMF_NULL_CHECK(TAG, ctx, {return ESP_GMF_ERR_INVALID_ARG;}); + ESP_GMF_NULL_CHECK(TAG, evt, {return ESP_GMF_ERR_INVALID_ARG;}); + if ((evt->type != ESP_GMF_EVT_TYPE_REPORT_INFO) + || (evt->sub != ESP_GMF_INFO_SOUND) + || (evt->payload == NULL)) { + return ESP_GMF_ERR_OK; + } + esp_gmf_element_handle_t self = (esp_gmf_element_handle_t)ctx; + esp_gmf_element_handle_t el = evt->from; + esp_gmf_event_state_t state = ESP_GMF_EVENT_STATE_NONE; + esp_gmf_element_get_state(self, &state); + esp_gmf_info_sound_t *info = (esp_gmf_info_sound_t *)evt->payload; + esp_ae_rate_cvt_cfg_t *config = (esp_ae_rate_cvt_cfg_t *)OBJ_GET_CFG(self); + ESP_GMF_NULL_CHECK(TAG, config, return ESP_GMF_ERR_FAIL); + esp_gmf_rate_cvt_t *rate_cvt = (esp_gmf_rate_cvt_t *)self; + rate_cvt->need_reopen = (config->src_rate != info->sample_rates) || (info->channels != config->channel) || (config->bits_per_sample != info->bits); + config->src_rate = info->sample_rates; + config->channel = info->channels; + config->bits_per_sample = info->bits; + ESP_LOGD(TAG, "RECV element info, from: %s-%p, next: %p, self: %s-%p, type: %x, state: %s, rate: %d, ch: %d, bits: %d", + OBJ_GET_TAG(el), el, esp_gmf_node_for_next((esp_gmf_node_t *)el), OBJ_GET_TAG(self), self, evt->type, + esp_gmf_event_get_state_str(state), info->sample_rates, info->channels, info->bits); + if (state == ESP_GMF_EVENT_STATE_NONE) { + esp_gmf_element_set_state(self, ESP_GMF_EVENT_STATE_INITIALIZED); + } + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t esp_gmf_rate_cvt_destroy(esp_gmf_element_handle_t self) +{ + esp_gmf_rate_cvt_t *rate_cvt = (esp_gmf_rate_cvt_t *)self; + ESP_LOGD(TAG, "Destroyed, %p", self); + void *cfg = OBJ_GET_CFG(self); + if (cfg) { + esp_gmf_oal_free(cfg); + } + esp_gmf_audio_el_deinit(self); + esp_gmf_oal_free(rate_cvt); + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t _load_rate_cvt_caps_func(esp_gmf_element_handle_t handle) +{ + esp_gmf_cap_t *caps = NULL; + esp_gmf_cap_t dec_caps = {0}; + dec_caps.cap_eightcc = ESP_GMF_CAPS_AUDIO_RATE_CONVERT; + dec_caps.attr_fun = NULL; + int ret = esp_gmf_cap_append(&caps, &dec_caps); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to create capability"); + + esp_gmf_element_t *el = (esp_gmf_element_t *)handle; + el->caps = caps; + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t _load_rate_cvt_methods_func(esp_gmf_element_handle_t handle) +{ + esp_gmf_method_t *method = NULL; + esp_gmf_args_desc_t *set_args = NULL; + esp_gmf_err_t ret = esp_gmf_args_desc_append(&set_args, AMETHOD_ARG(RATE_CVT, SET_DEST_RATE, RATE), + ESP_GMF_ARGS_TYPE_UINT32, sizeof(uint32_t), 0); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to append RATE argument"); + ret = esp_gmf_method_append(&method, AMETHOD(RATE_CVT, SET_DEST_RATE), __rate_cvt_set_dest_rate, set_args); + ESP_GMF_RET_ON_ERROR(TAG, ret, {return ret;}, "Failed to register %s method", AMETHOD(RATE_CVT, SET_DEST_RATE)); + + esp_gmf_element_t *el = (esp_gmf_element_t *)handle; + el->method = method; + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_rate_cvt_set_dest_rate(esp_gmf_element_handle_t handle, uint32_t dest_rate) +{ + ESP_GMF_NULL_CHECK(TAG, handle, { return ESP_GMF_ERR_INVALID_ARG;}); + esp_ae_rate_cvt_cfg_t *cfg = (esp_ae_rate_cvt_cfg_t *)OBJ_GET_CFG(handle); + ESP_GMF_NULL_CHECK(TAG, cfg, return ESP_GMF_ERR_FAIL); + if (cfg->dest_rate == dest_rate) { + return ESP_GMF_ERR_OK; + } + cfg->dest_rate = dest_rate; + esp_gmf_rate_cvt_t *rate_cvt = (esp_gmf_rate_cvt_t *)handle; + rate_cvt->need_reopen = true; + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_rate_cvt_init(esp_ae_rate_cvt_cfg_t *config, esp_gmf_element_handle_t *handle) +{ + ESP_GMF_NULL_CHECK(TAG, handle, {return ESP_GMF_ERR_INVALID_ARG;}); + *handle = NULL; + esp_gmf_err_t ret = ESP_GMF_ERR_OK; + esp_gmf_rate_cvt_t *rate_cvt = esp_gmf_oal_calloc(1, sizeof(esp_gmf_rate_cvt_t)); + ESP_GMF_MEM_VERIFY(TAG, rate_cvt, {return ESP_GMF_ERR_MEMORY_LACK;}, "rate conversion", sizeof(esp_gmf_rate_cvt_t)); + esp_gmf_obj_t *obj = (esp_gmf_obj_t *)rate_cvt; + obj->new_obj = esp_gmf_rate_cvt_new; + obj->del_obj = esp_gmf_rate_cvt_destroy; + esp_ae_rate_cvt_cfg_t *cfg = esp_gmf_oal_calloc(1, sizeof(esp_ae_rate_cvt_cfg_t)); + ESP_GMF_MEM_VERIFY(TAG, cfg, {ret = ESP_GMF_ERR_MEMORY_LACK; goto RATE_CVT_INIT_FAIL;}, "Rate conversion configuration", sizeof(esp_ae_rate_cvt_cfg_t)); + esp_gmf_obj_set_config(obj, cfg, sizeof(esp_ae_rate_cvt_cfg_t)); + if (config) { + memcpy(cfg, config, sizeof(esp_ae_rate_cvt_cfg_t)); + } else { + esp_ae_rate_cvt_cfg_t dcfg = DEFAULT_ESP_GMF_RATE_CVT_CONFIG(); + memcpy(cfg, &dcfg, sizeof(esp_ae_rate_cvt_cfg_t)); + } + ret = esp_gmf_obj_set_tag(obj, "aud_rate_cvt"); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto RATE_CVT_INIT_FAIL, "Failed to set obj tag"); + esp_gmf_element_cfg_t el_cfg = {0}; + ESP_GMF_ELEMENT_IN_PORT_ATTR_SET(el_cfg.in_attr, ESP_GMF_EL_PORT_CAP_SINGLE, 0, 0, + ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, ESP_GMF_ELEMENT_PORT_DATA_SIZE_DEFAULT); + ESP_GMF_ELEMENT_IN_PORT_ATTR_SET(el_cfg.out_attr, ESP_GMF_EL_PORT_CAP_SINGLE, 0, 0, + ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, ESP_GMF_ELEMENT_PORT_DATA_SIZE_DEFAULT); + el_cfg.dependency = true; + ret = esp_gmf_audio_el_init(rate_cvt, &el_cfg); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto RATE_CVT_INIT_FAIL, "Failed to initialize rate conversion element"); + ESP_GMF_ELEMENT_GET(rate_cvt)->ops.open = esp_gmf_rate_cvt_open; + ESP_GMF_ELEMENT_GET(rate_cvt)->ops.process = esp_gmf_rate_cvt_process; + ESP_GMF_ELEMENT_GET(rate_cvt)->ops.close = esp_gmf_rate_cvt_close; + ESP_GMF_ELEMENT_GET(rate_cvt)->ops.event_receiver = rate_cvt_received_event_handler; + ESP_GMF_ELEMENT_GET(rate_cvt)->ops.load_caps = _load_rate_cvt_caps_func; + ESP_GMF_ELEMENT_GET(rate_cvt)->ops.load_methods = _load_rate_cvt_methods_func; + *handle = obj; + ESP_LOGD(TAG, "Initialization, %s-%p", OBJ_GET_TAG(obj), obj); + return ESP_GMF_ERR_OK; +RATE_CVT_INIT_FAIL: + esp_gmf_rate_cvt_destroy(obj); + return ret; +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/esp_gmf_sonic.c b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/esp_gmf_sonic.c new file mode 100644 index 0000000..582ae9f --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/esp_gmf_sonic.c @@ -0,0 +1,435 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#include +#include "esp_log.h" +#include "esp_gmf_oal_mem.h" +#include "esp_gmf_oal_mutex.h" +#include "esp_gmf_node.h" +#include "esp_gmf_sonic.h" +#include "gmf_audio_common.h" +#include "esp_gmf_audio_methods_def.h" +#include "esp_gmf_cap.h" +#include "esp_gmf_caps_def.h" +#include "esp_gmf_audio_element.h" + +#define SONIC_DEFAULT_OUTPUT_TIME_MS (10) + +/** + * @brief Audio sonic context in GMF + */ +typedef struct { + esp_gmf_audio_element_t parent; /*!< The GMF sonic handle */ + esp_ae_sonic_handle_t sonic_hd; /*!< The audio effects sonic handle */ + uint8_t bytes_per_sample; /*!< Bytes number of per sampling point */ + uint32_t sample_rate; /*!< The audio sample rate */ + uint8_t bits_per_sample; /*!< Bits number of per sampling point */ + uint8_t channel; /*!< The audio channel */ + esp_ae_sonic_in_data_t in_data_hd; /*!< The sonic input data handle */ + esp_ae_sonic_out_data_t out_data_hd; /*!< The sonic output data handle */ + float speed; /*!< The audio speed */ + float pitch; /*!< The audio pitch */ + int32_t out_size; /*!< The acquired out size */ + int64_t cur_pts; /*!< The audio Presentation Time Stamp(pts) */ + bool need_reopen : 1; /*!< Whether need to reopen. + True: Execute the close function first, then execute the open function + False: Do nothing */ + bool is_done : 1; /*!< Whether the input data is done */ +} esp_gmf_sonic_t; + +static const char *TAG = "ESP_GMF_SONIC"; + +static esp_gmf_err_t __sonic_set_speed(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc, + uint8_t *buf, int buf_len) +{ + ESP_GMF_NULL_CHECK(TAG, arg_desc, {return ESP_GMF_ERR_INVALID_ARG;}); + ESP_GMF_NULL_CHECK(TAG, buf, {return ESP_GMF_ERR_INVALID_ARG;}); + float *speed = (float *)buf; + return esp_gmf_sonic_set_speed(handle, *speed); +} + +static esp_gmf_err_t __sonic_get_speed(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc, + uint8_t *buf, int buf_len) +{ + ESP_GMF_NULL_CHECK(TAG, buf, {return ESP_GMF_ERR_INVALID_ARG;}); + float *speed = (float *)buf; + return esp_gmf_sonic_get_speed(handle, speed); +} + +static esp_gmf_err_t __sonic_set_pitch(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc, + uint8_t *buf, int buf_len) +{ + ESP_GMF_NULL_CHECK(TAG, arg_desc, {return ESP_GMF_ERR_INVALID_ARG;}); + ESP_GMF_NULL_CHECK(TAG, buf, {return ESP_GMF_ERR_INVALID_ARG;}); + float *pitch = (float *)buf; + return esp_gmf_sonic_set_pitch(handle, *pitch); +} + +static esp_gmf_err_t __sonic_get_pitch(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc, + uint8_t *buf, int buf_len) +{ + ESP_GMF_NULL_CHECK(TAG, buf, {return ESP_GMF_ERR_INVALID_ARG;}); + float *pitch = (float *)buf; + return esp_gmf_sonic_get_pitch(handle, pitch); +} + +static esp_gmf_err_t esp_gmf_sonic_new(void *cfg, esp_gmf_obj_handle_t *handle) +{ + return esp_gmf_sonic_init(cfg, (esp_gmf_element_handle_t *)handle); +} + +static esp_gmf_job_err_t esp_gmf_sonic_open(esp_gmf_element_handle_t self, void *para) +{ + esp_gmf_sonic_t *sonic = (esp_gmf_sonic_t *)self; + esp_ae_sonic_cfg_t *sonic_info = (esp_ae_sonic_cfg_t *)OBJ_GET_CFG(self); + ESP_GMF_NULL_CHECK(TAG, sonic_info, {return ESP_GMF_JOB_ERR_FAIL;}); + sonic->sample_rate = sonic_info->sample_rate; + sonic->channel = sonic_info->channel; + sonic->bits_per_sample = sonic_info->bits_per_sample; + sonic->bytes_per_sample = (sonic_info->bits_per_sample >> 3) * sonic_info->channel; + GMF_AUDIO_UPDATE_SND_INFO(self, sonic_info->sample_rate, sonic_info->bits_per_sample, sonic_info->channel); + sonic->out_size = SONIC_DEFAULT_OUTPUT_TIME_MS * sonic->sample_rate * sonic->bytes_per_sample / 1000; + esp_ae_sonic_open(sonic_info, &sonic->sonic_hd); + ESP_GMF_CHECK(TAG, sonic->sonic_hd, {return ESP_GMF_JOB_ERR_FAIL;}, "Failed to create sonic handle"); + esp_ae_sonic_set_speed(sonic->sonic_hd, sonic->speed); + esp_ae_sonic_set_pitch(sonic->sonic_hd, sonic->pitch); + sonic->need_reopen = false; + ESP_LOGD(TAG, "Open, %p", self); + return ESP_GMF_JOB_ERR_OK; +} + +static esp_gmf_job_err_t esp_gmf_sonic_close(esp_gmf_element_handle_t self, void *para) +{ + esp_gmf_sonic_t *sonic = (esp_gmf_sonic_t *)self; + ESP_LOGD(TAG, "Closed, %p", self); + if (sonic->sonic_hd != NULL) { + esp_ae_sonic_close(sonic->sonic_hd); + sonic->sonic_hd = NULL; + } + sonic->cur_pts = 0; + return ESP_GMF_ERR_OK; +} + +static esp_gmf_job_err_t gmf_sonic_bypass_process(esp_gmf_sonic_t *sonic, esp_gmf_port_handle_t in_port, esp_gmf_port_handle_t out_port, + esp_gmf_payload_t **in_load, esp_gmf_payload_t **out_load) +{ + esp_gmf_job_err_t out_len = ESP_GMF_JOB_ERR_OK; + int load_ret = esp_gmf_port_acquire_in(in_port, in_load, ESP_GMF_ELEMENT_GET(sonic)->in_attr.data_size, ESP_GMF_MAX_DELAY); + ESP_GMF_PORT_ACQUIRE_IN_CHECK(TAG, load_ret, out_len, return out_len); + sonic->is_done = (*in_load)->is_done; + if (!sonic->is_done && (*in_load)->valid_size == 0) { + return ESP_GMF_JOB_ERR_CONTINUE; + } + if (in_port->is_shared == 1) { + *out_load = *in_load; + } + size_t out_size = (*in_load)->valid_size ? (*in_load)->valid_size : ESP_GMF_ELEMENT_GET(sonic)->in_attr.data_size; + load_ret = esp_gmf_port_acquire_out(out_port, out_load, out_size, ESP_GMF_MAX_DELAY); + ESP_GMF_PORT_ACQUIRE_OUT_CHECK(TAG, load_ret, out_len, return out_len); + if (in_port->is_shared != 1) { + memcpy((*out_load)->buf, (*in_load)->buf, (*in_load)->valid_size); + (*out_load)->valid_size = (*in_load)->valid_size; + (*out_load)->is_done = (*in_load)->is_done; + (*out_load)->pts = (*in_load)->pts; + } + return sonic->is_done ? ESP_GMF_JOB_ERR_DONE : out_len; +} + +static esp_gmf_job_err_t esp_gmf_sonic_process(esp_gmf_element_handle_t self, void *para) +{ + esp_gmf_sonic_t *sonic = (esp_gmf_sonic_t *)self; + esp_gmf_job_err_t out_len = ESP_GMF_JOB_ERR_OK; + esp_gmf_err_io_t load_ret = ESP_GMF_IO_OK; + if (sonic->need_reopen) { + esp_gmf_sonic_close(self, NULL); + out_len = esp_gmf_sonic_open(self, NULL); + if (out_len != ESP_GMF_JOB_ERR_OK) { + ESP_LOGE(TAG, "Sonic reopen failed"); + return out_len; + } + } + esp_gmf_port_handle_t in_port = ESP_GMF_ELEMENT_GET(self)->in; + esp_gmf_port_handle_t out_port = ESP_GMF_ELEMENT_GET(self)->out; + esp_gmf_payload_t *in_load = NULL; + esp_gmf_payload_t *out_load = NULL; + bool is_done = false; + sonic->out_data_hd.needed_num = sonic->out_size / sonic->bytes_per_sample; + if (sonic->speed == 1.0f && sonic->pitch == 1.0f && (sonic->out_data_hd.out_num < sonic->out_data_hd.needed_num)) { + out_len = gmf_sonic_bypass_process(sonic, in_port, out_port, &in_load, &out_load); + goto __sonic_release; + } else { + if ((sonic->out_data_hd.out_num < sonic->out_data_hd.needed_num) && sonic->is_done == false) { + int samples_num = ESP_GMF_ELEMENT_GET(sonic)->in_attr.data_size / (sonic->bytes_per_sample); + int bytes = samples_num * sonic->bytes_per_sample; + load_ret = esp_gmf_port_acquire_in(in_port, &in_load, bytes, ESP_GMF_MAX_DELAY); + samples_num = in_load->valid_size / (sonic->bytes_per_sample); + bytes = samples_num * sonic->bytes_per_sample; + sonic->is_done = in_load->is_done; + if ((bytes != in_load->valid_size) || (load_ret < ESP_GMF_IO_OK)) { + ESP_LOGE(TAG, "Invalid in load size %d, ret %d", in_load->valid_size, load_ret); + out_len = ESP_GMF_JOB_ERR_FAIL; + goto __sonic_release; + } + sonic->in_data_hd.samples = in_load->buf; + sonic->in_data_hd.num = samples_num; + sonic->cur_pts = in_load->pts; + if (sonic->in_data_hd.num == 0) { + if (sonic->is_done == true) { + out_len = ESP_GMF_JOB_ERR_DONE; + is_done = true; + } else { + out_len = ESP_GMF_JOB_ERR_CONTINUE; + goto __sonic_release; + } + } + } + load_ret = esp_gmf_port_acquire_out(out_port, &out_load, sonic->out_size, ESP_GMF_MAX_DELAY); + ESP_GMF_PORT_ACQUIRE_OUT_CHECK(TAG, load_ret, out_len, goto __sonic_release); + sonic->out_data_hd.samples = out_load->buf; + out_load->valid_size = 0; + out_load->pts = sonic->cur_pts; + if (!is_done) { + esp_gmf_oal_mutex_lock(((esp_gmf_audio_element_t *)self)->lock); + esp_ae_err_t ret = esp_ae_sonic_process(sonic->sonic_hd, &sonic->in_data_hd, &sonic->out_data_hd); + esp_gmf_oal_mutex_unlock(((esp_gmf_audio_element_t *)self)->lock); + ESP_GMF_RET_ON_ERROR(TAG, ret, {out_len = ESP_GMF_JOB_ERR_FAIL; goto __sonic_release;}, "Sonic process error %d", ret); + sonic->in_data_hd.samples = ((uint8_t *)sonic->in_data_hd.samples) + sonic->in_data_hd.consume_num * sonic->bytes_per_sample; + sonic->in_data_hd.num -= sonic->in_data_hd.consume_num; + sonic->in_data_hd.consume_num = 0; + out_load->valid_size = sonic->out_data_hd.out_num * sonic->bytes_per_sample; + if (out_load->valid_size > 0) { + esp_gmf_audio_el_update_file_pos((esp_gmf_element_handle_t)self, out_load->valid_size); + } + out_load->is_done = false; + out_load->pts = sonic->cur_pts; + sonic->cur_pts += (uint64_t)((float)(GMF_AUDIO_CALC_PTS(out_load->valid_size, sonic->sample_rate, sonic->channel, sonic->bits_per_sample)) * sonic->speed); + ESP_LOGV(TAG, "%s, I: %p-buf: %p-sz: %d, O: %p-buf: %p-sz: %d, ret: %d", __func__, in_port, + in_load->buf, in_load->valid_size, out_port, + out_load->buf, out_load->buf_length, ret); + if (sonic->out_data_hd.out_num == sonic->out_data_hd.needed_num) { + out_len = ESP_GMF_JOB_ERR_TRUNCATE; + goto __sonic_release; + } + } + if (sonic->is_done == true) { + out_load->is_done = true; + out_len = ESP_GMF_JOB_ERR_DONE; + goto __sonic_release; + } + } +__sonic_release: + if (out_load != NULL) { + load_ret = esp_gmf_port_release_out(out_port, out_load, ESP_GMF_MAX_DELAY); + if ((load_ret < ESP_GMF_IO_OK) && (load_ret != ESP_GMF_IO_ABORT)) { + ESP_LOGE(TAG, "OUT port release error, ret:%d", load_ret); + out_len = ESP_GMF_JOB_ERR_FAIL; + } + } + if (in_load != NULL) { + load_ret = esp_gmf_port_release_in(in_port, in_load, ESP_GMF_MAX_DELAY); + if ((load_ret < ESP_GMF_IO_OK) && (load_ret != ESP_GMF_IO_ABORT)) { + ESP_LOGE(TAG, "IN port release error, ret:%d", load_ret); + out_len = ESP_GMF_JOB_ERR_FAIL; + } + } + return out_len; +} + +static esp_gmf_err_t sonic_received_event_handler(esp_gmf_event_pkt_t *evt, void *ctx) +{ + ESP_GMF_NULL_CHECK(TAG, ctx, {return ESP_GMF_ERR_INVALID_ARG;}); + ESP_GMF_NULL_CHECK(TAG, evt, {return ESP_GMF_ERR_INVALID_ARG;}); + if ((evt->type != ESP_GMF_EVT_TYPE_REPORT_INFO) + || (evt->sub != ESP_GMF_INFO_SOUND) + || (evt->payload == NULL)) { + return ESP_GMF_ERR_OK; + } + esp_gmf_element_handle_t self = (esp_gmf_element_handle_t)ctx; + esp_gmf_element_handle_t el = evt->from; + esp_gmf_event_state_t state = ESP_GMF_EVENT_STATE_NONE; + esp_gmf_element_get_state(self, &state); + esp_gmf_info_sound_t *info = (esp_gmf_info_sound_t *)evt->payload; + esp_ae_sonic_cfg_t *config = (esp_ae_sonic_cfg_t *)OBJ_GET_CFG(self); + ESP_GMF_NULL_CHECK(TAG, config, return ESP_GMF_ERR_FAIL); + esp_gmf_sonic_t *sonic = (esp_gmf_sonic_t *)self; + sonic->need_reopen = (config->sample_rate != info->sample_rates) || (info->channels != config->channel) || (config->bits_per_sample != info->bits); + config->sample_rate = info->sample_rates; + config->channel = info->channels; + config->bits_per_sample = info->bits; + ESP_LOGD(TAG, "RECV element info, from: %s-%p, next: %p, self: %s-%p, type: %x, state: %s, rate: %d, ch: %d, bits: %d", + OBJ_GET_TAG(el), el, esp_gmf_node_for_next((esp_gmf_node_t *)el), OBJ_GET_TAG(self), self, evt->type, + esp_gmf_event_get_state_str(state), info->sample_rates, info->channels, info->bits); + if (state == ESP_GMF_EVENT_STATE_NONE) { + esp_gmf_element_set_state(self, ESP_GMF_EVENT_STATE_INITIALIZED); + } + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t esp_gmf_sonic_destroy(esp_gmf_element_handle_t self) +{ + esp_gmf_sonic_t *sonic = (esp_gmf_sonic_t *)self; + ESP_LOGD(TAG, "Destroyed, %p", self); + void *cfg = OBJ_GET_CFG(self); + if (cfg) { + esp_gmf_oal_free(cfg); + } + esp_gmf_audio_el_deinit(self); + esp_gmf_oal_free(sonic); + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t _load_sonic_caps_func(esp_gmf_element_handle_t handle) +{ + esp_gmf_cap_t *caps = NULL; + esp_gmf_cap_t dec_caps = {0}; + dec_caps.cap_eightcc = ESP_GMF_CAPS_AUDIO_SONIC; + dec_caps.attr_fun = NULL; + int ret = esp_gmf_cap_append(&caps, &dec_caps); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to create capability"); + + esp_gmf_element_t *el = (esp_gmf_element_t *)handle; + el->caps = caps; + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t _load_sonic_methods_func(esp_gmf_element_handle_t handle) +{ + esp_gmf_method_t *method = NULL; + esp_gmf_args_desc_t *set_args = NULL; + esp_gmf_args_desc_t *get_args = NULL; + esp_gmf_err_t ret = esp_gmf_args_desc_append(&set_args, AMETHOD_ARG(SONIC, SET_SPEED, SPEED), + ESP_GMF_ARGS_TYPE_FLOAT, sizeof(float), 0); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to append argument"); + ret = esp_gmf_method_append(&method, AMETHOD(SONIC, SET_SPEED), __sonic_set_speed, set_args); + ESP_GMF_RET_ON_ERROR(TAG, ret, {return ret;}, "Failed to register %s method", AMETHOD(SONIC, SET_SPEED)); + + ret = esp_gmf_args_desc_copy(set_args, &get_args); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to copy argument"); + ret = esp_gmf_method_append(&method, AMETHOD(SONIC, GET_SPEED), __sonic_get_speed, get_args); + ESP_GMF_RET_ON_ERROR(TAG, ret, {return ret;}, "Failed to register %s method", AMETHOD(SONIC, GET_SPEED)); + + set_args = NULL; + get_args = NULL; + ret = esp_gmf_args_desc_append(&set_args, AMETHOD_ARG(SONIC, SET_PITCH, PITCH), ESP_GMF_ARGS_TYPE_FLOAT, sizeof(float), 0); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to append argument"); + ret = esp_gmf_method_append(&method, AMETHOD(SONIC, SET_PITCH), __sonic_set_pitch, set_args); + ESP_GMF_RET_ON_ERROR(TAG, ret, {return ret;}, "Failed to register %s method", AMETHOD(SONIC, SET_PITCH)); + + ret = esp_gmf_args_desc_copy(set_args, &get_args); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to copy argument"); + ret = esp_gmf_method_append(&method, AMETHOD(SONIC, GET_PITCH), __sonic_get_pitch, get_args); + ESP_GMF_RET_ON_ERROR(TAG, ret, {return ret;}, "Failed to register %s method", AMETHOD(SONIC, GET_PITCH)); + + esp_gmf_element_t *el = (esp_gmf_element_t *)handle; + el->method = method; + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_sonic_set_speed(esp_gmf_element_handle_t handle, float speed) +{ + ESP_GMF_NULL_CHECK(TAG, handle, { return ESP_GMF_ERR_INVALID_ARG;}); + esp_gmf_sonic_t *sonic = (esp_gmf_sonic_t *)handle; + if (sonic->sonic_hd) { + esp_gmf_oal_mutex_lock(((esp_gmf_audio_element_t *)handle)->lock); + esp_ae_err_t ret = esp_ae_sonic_set_speed(sonic->sonic_hd, speed); + esp_gmf_oal_mutex_unlock(((esp_gmf_audio_element_t *)handle)->lock); + ESP_GMF_RET_ON_ERROR(TAG, ret, return ESP_GMF_JOB_ERR_FAIL;, "sonicualize set error %d", ret); + } + sonic->speed = speed; + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_sonic_get_speed(esp_gmf_element_handle_t handle, float *speed) +{ + ESP_GMF_NULL_CHECK(TAG, handle, { return ESP_GMF_ERR_INVALID_ARG;}); + ESP_GMF_NULL_CHECK(TAG, speed, { return ESP_GMF_ERR_INVALID_ARG;}); + esp_gmf_sonic_t *sonic = (esp_gmf_sonic_t *)handle; + if (sonic->sonic_hd) { + esp_gmf_oal_mutex_lock(((esp_gmf_audio_element_t *)handle)->lock); + esp_ae_err_t ret = esp_ae_sonic_get_speed(sonic->sonic_hd, speed); + esp_gmf_oal_mutex_unlock(((esp_gmf_audio_element_t *)handle)->lock); + ESP_GMF_RET_ON_ERROR(TAG, ret, return ESP_GMF_JOB_ERR_FAIL;, "sonicualize set error %d", ret); + } else { + *speed = sonic->speed; + } + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_sonic_set_pitch(esp_gmf_element_handle_t handle, float pitch) +{ + ESP_GMF_NULL_CHECK(TAG, handle, { return ESP_GMF_ERR_INVALID_ARG;}); + esp_gmf_sonic_t *sonic = (esp_gmf_sonic_t *)handle; + if (sonic->sonic_hd) { + esp_gmf_oal_mutex_lock(((esp_gmf_audio_element_t *)handle)->lock); + esp_ae_err_t ret = esp_ae_sonic_set_pitch(sonic->sonic_hd, pitch); + esp_gmf_oal_mutex_unlock(((esp_gmf_audio_element_t *)handle)->lock); + ESP_GMF_RET_ON_ERROR(TAG, ret, return ESP_GMF_JOB_ERR_FAIL;, "sonicualize set error %d", ret); + } + sonic->pitch = pitch; + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_sonic_get_pitch(esp_gmf_element_handle_t handle, float *pitch) +{ + ESP_GMF_NULL_CHECK(TAG, handle, { return ESP_GMF_ERR_INVALID_ARG;}); + ESP_GMF_NULL_CHECK(TAG, pitch, { return ESP_GMF_ERR_INVALID_ARG;}); + esp_gmf_sonic_t *sonic = (esp_gmf_sonic_t *)handle; + if (sonic->sonic_hd) { + esp_gmf_oal_mutex_lock(((esp_gmf_audio_element_t *)handle)->lock); + esp_ae_err_t ret = esp_ae_sonic_get_pitch(sonic->sonic_hd, pitch); + esp_gmf_oal_mutex_unlock(((esp_gmf_audio_element_t *)handle)->lock); + ESP_GMF_RET_ON_ERROR(TAG, ret, return ESP_GMF_JOB_ERR_FAIL;, "sonicualize set error %d", ret); + } else { + *pitch = sonic->pitch; + } + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_sonic_init(esp_ae_sonic_cfg_t *config, esp_gmf_element_handle_t *handle) +{ + ESP_GMF_NULL_CHECK(TAG, handle, {return ESP_GMF_ERR_INVALID_ARG;}); + *handle = NULL; + esp_gmf_err_t ret = ESP_GMF_ERR_OK; + esp_gmf_sonic_t *sonic = esp_gmf_oal_calloc(1, sizeof(esp_gmf_sonic_t)); + ESP_GMF_MEM_VERIFY(TAG, sonic, {return ESP_GMF_ERR_MEMORY_LACK;}, "sonic", sizeof(esp_gmf_sonic_t)); + esp_gmf_obj_t *obj = (esp_gmf_obj_t *)sonic; + obj->new_obj = esp_gmf_sonic_new; + obj->del_obj = esp_gmf_sonic_destroy; + esp_ae_sonic_cfg_t *cfg = esp_gmf_oal_calloc(1, sizeof(esp_ae_sonic_cfg_t)); + ESP_GMF_MEM_VERIFY(TAG, cfg, ret = ESP_GMF_ERR_MEMORY_LACK; goto SONIC_INIT_FAIL;, "sonic configuration", sizeof(esp_ae_sonic_cfg_t)); + esp_gmf_obj_set_config(obj, cfg, sizeof(esp_ae_sonic_cfg_t)); + if (config) { + memcpy(cfg, config, sizeof(esp_ae_sonic_cfg_t)); + } else { + esp_ae_sonic_cfg_t dcfg = DEFAULT_ESP_GMF_SONIC_CONFIG(); + memcpy(cfg, &dcfg, sizeof(esp_ae_sonic_cfg_t)); + } + ret = esp_gmf_obj_set_tag(obj, "aud_sonic"); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto SONIC_INIT_FAIL, "Failed to set obj tag"); + sonic->speed = 1.0f; + sonic->pitch = 1.0f; + esp_gmf_element_cfg_t el_cfg = {0}; + ESP_GMF_ELEMENT_IN_PORT_ATTR_SET(el_cfg.in_attr, ESP_GMF_EL_PORT_CAP_SINGLE, 0, 0, + ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, ESP_GMF_ELEMENT_PORT_DATA_SIZE_DEFAULT); + ESP_GMF_ELEMENT_IN_PORT_ATTR_SET(el_cfg.out_attr, ESP_GMF_EL_PORT_CAP_SINGLE, 0, 0, + ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, ESP_GMF_ELEMENT_PORT_DATA_SIZE_DEFAULT); + el_cfg.dependency = true; + ret = esp_gmf_audio_el_init(sonic, &el_cfg); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto SONIC_INIT_FAIL, "Failed to initialize sonic element"); + ESP_GMF_ELEMENT_GET(sonic)->ops.open = esp_gmf_sonic_open; + ESP_GMF_ELEMENT_GET(sonic)->ops.process = esp_gmf_sonic_process; + ESP_GMF_ELEMENT_GET(sonic)->ops.close = esp_gmf_sonic_close; + ESP_GMF_ELEMENT_GET(sonic)->ops.event_receiver = sonic_received_event_handler; + ESP_GMF_ELEMENT_GET(sonic)->ops.load_caps = _load_sonic_caps_func; + ESP_GMF_ELEMENT_GET(sonic)->ops.load_methods = _load_sonic_methods_func; + *handle = obj; + ESP_LOGD(TAG, "Initialization, %s-%p", OBJ_GET_TAG(obj), obj); + return ESP_GMF_ERR_OK; +SONIC_INIT_FAIL: + esp_gmf_sonic_destroy(obj); + return ret; +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/idf_component.yml b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/idf_component.yml new file mode 100644 index 0000000..310a268 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/idf_component.yml @@ -0,0 +1,19 @@ +version: "0.7.2" +description: Espressif GMF Audio Elements Module +url: https://github.com/espressif/esp-gmf/tree/main/elements/gmf_audio +documentation: "https://github.com/espressif/esp-gmf/blob/main/elements/gmf_audio/README.md" +issues: "https://github.com/espressif/esp-gmf/issues" + +tags: + - "gmf" + +dependencies: + espressif/esp_audio_codec: + require: public + version: "~2.3" + espressif/esp_audio_effects: + require: public + version: "~1.1" + espressif/gmf_core: + require: public + version: "^0.7" diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/include/esp_gmf_alc.h b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/include/esp_gmf_alc.h new file mode 100644 index 0000000..2a4eb9a --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/include/esp_gmf_alc.h @@ -0,0 +1,72 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#include "esp_gmf_err.h" +#include "esp_gmf_element.h" +#include "esp_ae_alc.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define DEFAULT_ESP_GMF_ALC_CONFIG() { \ + .sample_rate = 48000, \ + .channel = 2, \ + .bits_per_sample = 16, \ +} + +/** + * @brief Initializes the GMF ALC with the provided configuration + * + * @param[in] config Pointer to the ALC configuration + * @param[out] handle Pointer to the ALC handle to be initialized + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid configuration provided + * - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory + */ +esp_gmf_err_t esp_gmf_alc_init(esp_ae_alc_cfg_t *config, esp_gmf_element_handle_t *handle); + +/** + * @brief Set specific channel gain of the ALC handle. + * Positive gain indicates an increase in volume, + * negative gain indicates a decrease in volume. + * 0 gain indicates the volume level remains unchanged. + * + * @param[in] handle The ALC handle + * @param[in] idx The channel index of the gain to retrieve. eg: 0 refers to the first channel + * @param[in] gain The gain value needs to conform to the following conditions: + * - Supported range [-64, 63] + * - Below -64 will set to mute + * - Higher than 63 not supported + * Unit: dB + * + * @return + * - ESP_GMF_ERR_OK Operation succeeded + * - ESP_GMF_ERR_INVALID_ARG Invalid input parameter + */ +esp_gmf_err_t esp_gmf_alc_set_gain(esp_gmf_element_handle_t handle, uint8_t idx, int8_t gain); + +/** + * @brief Get the gain for a specific channel from the ALC handle + * + * @param[in] handle The ALC handle + * @param[in] ch_idx The channel index of the gain to retrieve. eg: 0 refers to the first channel + * @param[out] gain Pointer to store the retrieved gain. Unit: dB + * + * @return + * - ESP_GMF_ERR_OK Operation succeeded + * - ESP_GMF_ERR_INVALID_ARG Invalid input parameter + */ +esp_gmf_err_t esp_gmf_alc_get_gain(esp_gmf_element_handle_t handle, uint8_t idx, int8_t *gain); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/include/esp_gmf_audio_dec.h b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/include/esp_gmf_audio_dec.h new file mode 100644 index 0000000..a2f341c --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/include/esp_gmf_audio_dec.h @@ -0,0 +1,75 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#include "esp_audio_simple_dec.h" +#include "esp_gmf_element.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define DEFAULT_ESP_GMF_AUDIO_DEC_CONFIG() { \ + .dec_type = ESP_AUDIO_SIMPLE_DEC_TYPE_NONE, \ + .dec_cfg = NULL, \ + .cfg_size = 0, \ +} + +/** + * @brief Initializes the GMF audio decoder with the provided configuration + * + * @param[in] config Pointer to the audio decoder configuration + * @param[out] handle Pointer to the audio decoder handle to be initialized + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid configuration provided + * - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory + */ +esp_gmf_err_t esp_gmf_audio_dec_init(esp_audio_simple_dec_cfg_t *config, esp_gmf_element_handle_t *handle); + +/** + * @brief Reconfigure the GMF audio decoder and filled into provided configuration + * + * @note Only allowed when not running, i.e., in the `ESP_GMF_EVENT_STATE_NONE` or `ESP_GMF_EVENT_STATE_INITIALIZED` state + * + * @param[in] handle Audio decoder handle to be reconfigured + * @param[in] config Pointer to the new simple decoder configuration + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid handle or configuration provided + * - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory + */ +esp_gmf_err_t esp_gmf_audio_dec_reconfig(esp_gmf_element_handle_t handle, esp_audio_simple_dec_cfg_t *config); + +/** + * @brief Reconfigures the GMF audio decoder with default configuration based on provided sound information + * + * @note 1. Only allowed when not running, i.e., in the `ESP_GMF_EVENT_STATE_NONE` or `ESP_GMF_EVENT_STATE_INITIALIZED` state + * 2. The actual reconfiguration behavior depends on the relationship between the provided `sound info` and + * the current decoder configuration (`cfg`): + * - If the `format_id` in `sound info` matches the decoder's current `type`, and the decoder's sub-config (`sub cfg`) is not NULL, + * only the basic fields in the sub-config will be updated with information from `sound info` (e.g., sample rate, channel count). + * - If the `format_id` differs from the current `type`, or if the sub-config is NULL, + * the decoder will be reconfigured using the **default configuration** for the specified `format_id`, + * and the current decoder `type` will be updated accordingly. + * + * @param[in] handle Audio decoder handle to be reconfigured + * @param[in] info Sound information to be configured + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid handle or decoder type provided + * - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory + */ +esp_gmf_err_t esp_gmf_audio_dec_reconfig_by_sound_info(esp_gmf_element_handle_t handle, esp_gmf_info_sound_t *info); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/include/esp_gmf_audio_enc.h b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/include/esp_gmf_audio_enc.h new file mode 100644 index 0000000..74899ce --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/include/esp_gmf_audio_enc.h @@ -0,0 +1,113 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#include "esp_gmf_err.h" +#include "esp_gmf_element.h" +#include "encoder/esp_audio_enc.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define DEFAULT_ESP_GMF_AUDIO_ENC_CONFIG() { \ + .type = ESP_AUDIO_TYPE_UNSUPPORT, \ + .cfg = NULL, \ + .cfg_sz = 0, \ +} + +/** + * @brief Initializes the GMF audio encoder with the provided configuration + * + * @param[in] config Pointer to the audio encoder configuration + * @param[out] handle Pointer to the audio encoder handle to be initialized + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid configuration provided + * - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory + */ +esp_gmf_err_t esp_gmf_audio_enc_init(esp_audio_enc_config_t *config, esp_gmf_element_handle_t *handle); + +/** + * @brief Get the input and output frame size information of the GMF audio encoder + * + * @param[in] handle Audio encoder handle + * @param[out] in_size Pointer to store input frame size + * @param[out] out_size Pointer to store suggested output frame size + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid handle or size pointers provided + */ +esp_gmf_err_t esp_gmf_audio_enc_get_frame_size(esp_gmf_element_handle_t handle, uint32_t *in_size, uint32_t *out_size); + +/** + * @brief Set the bitrate for the GMF audio encoder + * + * @param[in] handle Audio encoder handle + * @param[in] bitrate The bitrate of the encoder + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid handle or bitrate value provided + */ +esp_gmf_err_t esp_gmf_audio_enc_set_bitrate(esp_gmf_element_handle_t handle, uint32_t bitrate); + +/** + * @brief Get the current bitrate of the GMF audio encoder + * + * @param[in] handle Audio encoder handle + * @param[out] bitrate Pointer to store the current bitrate + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid handle or bitrate pointer provided + */ +esp_gmf_err_t esp_gmf_audio_enc_get_bitrate(esp_gmf_element_handle_t handle, uint32_t *bitrate); + +/** + * @brief Reconfigure the GMF audio encoder and filled into provided configuration + * + * @note Only allowed when not running, i.e., in the `ESP_GMF_EVENT_STATE_NONE` or `ESP_GMF_EVENT_STATE_INITIALIZED` state + * + * @param[in] handle Audio encoder handle to be reconfigured + * @param[in] config Pointer to the new audio encoder configuration + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid handle or configuration provided + * - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory + */ +esp_gmf_err_t esp_gmf_audio_enc_reconfig(esp_gmf_element_handle_t handle, esp_audio_enc_config_t *config); + +/** + * @brief Reconfigures the GMF audio encoder with default configuration for the sound information + * + * @note 1. Only allowed when not running, i.e., in the `ESP_GMF_EVENT_STATE_NONE` or `ESP_GMF_EVENT_STATE_INITIALIZED` state + * 2. The actual reconfiguration behavior depends on the relationship between the provided `sound info` and + * the current encoder configuration (`cfg`): + * - If the `format_id` in `sound info` matches the encoder's current `type`, and the encoder's sub-config (`sub cfg`) is not NULL, + * only the basic fields in the sub-config will be updated with information from `sound info` (e.g., sample rate, channel count). + * - If the `format_id` differs from the current `type`, or if the sub-config is NULL, + * the encoder will be reconfigured using the **default configuration** for the specified `format_id`, + * and the current encoder `type` will be updated accordingly. + * + * @param[in] handle Audio encoder handle to be reconfigured + * @param[in] info Sound information to be configured + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid handle or encoder type provided + * - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory + */ +esp_gmf_err_t esp_gmf_audio_enc_reconfig_by_sound_info(esp_gmf_element_handle_t handle, esp_gmf_info_sound_t *info); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/include/esp_gmf_audio_helper.h b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/include/esp_gmf_audio_helper.h new file mode 100644 index 0000000..6b1b5c9 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/include/esp_gmf_audio_helper.h @@ -0,0 +1,30 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#include "esp_gmf_err.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * @brief Get audio codec type by uri + * + * @param[in] uri URI of audio codec + * @param[out] format_id Type of audio codec(ESP_FourCC type) + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_NOT_SUPPORT Not supported audio codec type + */ +esp_gmf_err_t esp_gmf_audio_helper_get_audio_type_by_uri(const char *uri, uint32_t *format_id); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/include/esp_gmf_audio_methods_def.h b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/include/esp_gmf_audio_methods_def.h new file mode 100644 index 0000000..d2eb05e --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/include/esp_gmf_audio_methods_def.h @@ -0,0 +1,217 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#include "esp_gmf_method_helper.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * @brief Audio general method argument definition + */ +#define ESP_GMF_AUDIO_INDEX_ARG "index" +#define ESP_GMF_AUDIO_GAIN_ARG "gain" +#define ESP_GMF_AUDIO_BITS_ARG "bits" +#define ESP_GMF_AUDIO_CHANNEL_ARG "channel" +#define ESP_GMF_AUDIO_SAMPLE_RATE_ARG "sample_rate" +#define ESP_GMF_AUDIO_BITRATE_ARG "bitrate" +#define ESP_GMF_AUDIO_ENABLE_ARG "enable" +#define ESP_GMF_AUDIO_TYPE_ARG "type" +#define ESP_GMF_AUDIO_PARA_ARG "para" +#define ESP_GMF_AUDIO_MODE_ARG "mode" +#define ESP_GMF_AUDIO_SPEED_ARG "speed" +#define ESP_GMF_AUDIO_PITCH_ARG "pitch" +#define ESP_GMF_AUDIO_INSIZE_ARG "in_size" +#define ESP_GMF_AUDIO_OUTSIZE_ARG "out_size" +#define ESP_GMF_AUDIO_CFG_ARG "cfg" +#define ESP_GMF_AUDIO_INFO_ARG "info" + +/** + * @brief Audio EQ parameter argument definition + */ +#define ESP_GMF_AUDIO_EQ_PARA_ARG ESP_GMF_AUDIO_PARA_ARG +#define ESP_GMF_AUDIO_EQ_PARA_FT_ARG ESP_GMF_AUDIO_TYPE_ARG +#define ESP_GMF_AUDIO_EQ_PARA_FC_ARG "fc" +#define ESP_GMF_AUDIO_EQ_PARA_Q_ARG "q" +#define ESP_GMF_AUDIO_EQ_PARA_GAIN_ARG ESP_GMF_AUDIO_GAIN_ARG + +/** + * @brief Audio general config parameter argument definition + */ +#define ESP_GMF_AUDIO_CFG_TYPE_ARG ESP_GMF_AUDIO_TYPE_ARG +#define ESP_GMF_AUDIO_CFG_SUBCFGPTR_ARG "sub_cfg" +#define ESP_GMF_AUDIO_CFG_SUBCFGSZ_ARG "sub_cfg_sz" + +/** + * @brief Audio sound information parameter argument definition + */ +#define ESP_GMF_AUDIO_SND_INFO_ARG ESP_GMF_AUDIO_INFO_ARG +#define ESP_GMF_AUDIO_SND_INFO_TYPE_ARG ESP_GMF_AUDIO_TYPE_ARG +#define ESP_GMF_AUDIO_SND_INFO_SAMPLERATE_ARG ESP_GMF_AUDIO_SAMPLE_RATE_ARG +#define ESP_GMF_AUDIO_SND_INFO_BITRATE_ARG ESP_GMF_AUDIO_BITRATE_ARG +#define ESP_GMF_AUDIO_SND_INFO_CHANNEL_ARG ESP_GMF_AUDIO_CHANNEL_ARG +#define ESP_GMF_AUDIO_SND_INFO_BITS_ARG ESP_GMF_AUDIO_BITS_ARG + +/** + * @brief GMF audio method definition + */ +#define AMETHOD_DEF(module, method, str) ESP_GMF_METHOD_DEF(AUDIO, module, method, str) + +/** + * @brief Get GMF audio method string + */ +#define AMETHOD(module, method) ESP_GMF_METHOD_STR(AUDIO, module, method) + +/** + * @brief GMF audio method argument definition + */ +#define AMETHOD_ARG_DEF(module, method, arg, str) ESP_GMF_METHOD_ARG_DEF(AUDIO, module, method, arg, str) + +/** + * @brief Get GMF audio method argument string + */ +#define AMETHOD_ARG(module, method, arg) ESP_GMF_METHOD_ARG_STR(AUDIO, module, method, arg) + +/** + * @brief ALC method + */ +AMETHOD_DEF(ALC, SET_GAIN, "set_gain"); +AMETHOD_ARG_DEF(ALC, SET_GAIN, IDX, ESP_GMF_AUDIO_INDEX_ARG); +AMETHOD_ARG_DEF(ALC, SET_GAIN, GAIN, ESP_GMF_AUDIO_GAIN_ARG); + +AMETHOD_DEF(ALC, GET_GAIN, "get_gain"); +AMETHOD_ARG_DEF(ALC, GET_GAIN, IDX, ESP_GMF_AUDIO_INDEX_ARG); +AMETHOD_ARG_DEF(ALC, GET_GAIN, GAIN, ESP_GMF_AUDIO_GAIN_ARG); + +/** + * @brief BIT CVT method + */ +AMETHOD_DEF(BIT_CVT, SET_DEST_BITS, "set_dest_bits"); +AMETHOD_ARG_DEF(BIT_CVT, SET_DEST_BITS, BITS, ESP_GMF_AUDIO_BITS_ARG); + +/** + * @brief CH CVT method + */ +AMETHOD_DEF(CH_CVT, SET_DEST_CH, "set_dest_ch"); +AMETHOD_ARG_DEF(CH_CVT, SET_DEST_CH, CH, ESP_GMF_AUDIO_CHANNEL_ARG); + +/** + * @brief RATE CVT method + */ +AMETHOD_DEF(RATE_CVT, SET_DEST_RATE, "set_dest_rate"); +AMETHOD_ARG_DEF(RATE_CVT, SET_DEST_RATE, RATE, ESP_GMF_AUDIO_SAMPLE_RATE_ARG); + +/** + * @brief EQ method + */ +AMETHOD_DEF(EQ, SET_PARA, "set_para"); +AMETHOD_ARG_DEF(EQ, SET_PARA, IDX, ESP_GMF_AUDIO_INDEX_ARG); +AMETHOD_ARG_DEF(EQ, SET_PARA, PARA, ESP_GMF_AUDIO_EQ_PARA_ARG); +AMETHOD_ARG_DEF(EQ, SET_PARA, PARA_FT, ESP_GMF_AUDIO_EQ_PARA_FT_ARG); +AMETHOD_ARG_DEF(EQ, SET_PARA, PARA_FC, ESP_GMF_AUDIO_EQ_PARA_FC_ARG); +AMETHOD_ARG_DEF(EQ, SET_PARA, PARA_Q, ESP_GMF_AUDIO_EQ_PARA_Q_ARG); +AMETHOD_ARG_DEF(EQ, SET_PARA, PARA_GAIN, ESP_GMF_AUDIO_EQ_PARA_GAIN_ARG); + +AMETHOD_DEF(EQ, GET_PARA, "get_para"); +AMETHOD_ARG_DEF(EQ, GET_PARA, IDX, ESP_GMF_AUDIO_INDEX_ARG); +AMETHOD_ARG_DEF(EQ, GET_PARA, PARA, ESP_GMF_AUDIO_EQ_PARA_ARG); +AMETHOD_ARG_DEF(EQ, GET_PARA, PARA_FT, ESP_GMF_AUDIO_EQ_PARA_FT_ARG); +AMETHOD_ARG_DEF(EQ, GET_PARA, PARA_FC, ESP_GMF_AUDIO_EQ_PARA_FC_ARG); +AMETHOD_ARG_DEF(EQ, GET_PARA, PARA_Q, ESP_GMF_AUDIO_EQ_PARA_Q_ARG); +AMETHOD_ARG_DEF(EQ, GET_PARA, PARA_GAIN, ESP_GMF_AUDIO_EQ_PARA_GAIN_ARG); + +AMETHOD_DEF(EQ, ENABLE_FILTER, "enable_filter"); +AMETHOD_ARG_DEF(EQ, ENABLE_FILTER, IDX, ESP_GMF_AUDIO_INDEX_ARG); +AMETHOD_ARG_DEF(EQ, ENABLE_FILTER, ENABLE, ESP_GMF_AUDIO_ENABLE_ARG); + +/** + * @brief FADE method + */ +AMETHOD_DEF(FADE, SET_MODE, "set_mode"); +AMETHOD_ARG_DEF(FADE, SET_MODE, MODE, ESP_GMF_AUDIO_MODE_ARG); + +AMETHOD_DEF(FADE, GET_MODE, "get_mode"); +AMETHOD_ARG_DEF(FADE, GET_MODE, MODE, ESP_GMF_AUDIO_MODE_ARG); + +AMETHOD_DEF(FADE, RESET_WEIGHT, "reset_weight"); + +/** + * @brief MIXER method + */ +AMETHOD_DEF(MIXER, SET_MODE, "set_mode"); +AMETHOD_ARG_DEF(MIXER, SET_MODE, IDX, ESP_GMF_AUDIO_INDEX_ARG); +AMETHOD_ARG_DEF(MIXER, SET_MODE, MODE, ESP_GMF_AUDIO_MODE_ARG); + +AMETHOD_DEF(MIXER, SET_INFO, "set_info"); +AMETHOD_ARG_DEF(MIXER, SET_INFO, RATE, ESP_GMF_AUDIO_SAMPLE_RATE_ARG); +AMETHOD_ARG_DEF(MIXER, SET_INFO, CH, ESP_GMF_AUDIO_CHANNEL_ARG); +AMETHOD_ARG_DEF(MIXER, SET_INFO, BITS, ESP_GMF_AUDIO_BITS_ARG); + +/** + * @brief SONIC method + */ +AMETHOD_DEF(SONIC, SET_SPEED, "set_speed"); +AMETHOD_ARG_DEF(SONIC, SET_SPEED, SPEED, ESP_GMF_AUDIO_SPEED_ARG); + +AMETHOD_DEF(SONIC, GET_SPEED, "get_speed"); +AMETHOD_ARG_DEF(SONIC, GET_SPEED, SPEED, ESP_GMF_AUDIO_SPEED_ARG); + +AMETHOD_DEF(SONIC, SET_PITCH, "set_pitch"); +AMETHOD_ARG_DEF(SONIC, SET_PITCH, PITCH, ESP_GMF_AUDIO_PITCH_ARG); + +AMETHOD_DEF(SONIC, GET_PITCH, "get_pitch"); +AMETHOD_ARG_DEF(SONIC, GET_PITCH, PITCH, ESP_GMF_AUDIO_PITCH_ARG); + +/** + * @brief ENCODER method + */ +AMETHOD_DEF(ENCODER, GET_FRAME_SZ, "get_frame_sz"); +AMETHOD_ARG_DEF(ENCODER, GET_FRAME_SZ, INSIZE, ESP_GMF_AUDIO_INSIZE_ARG); +AMETHOD_ARG_DEF(ENCODER, GET_FRAME_SZ, OUTSIZE, ESP_GMF_AUDIO_OUTSIZE_ARG); + +AMETHOD_DEF(ENCODER, SET_BITRATE, "set_bitrate"); +AMETHOD_ARG_DEF(ENCODER, SET_BITRATE, BITRATE, ESP_GMF_AUDIO_BITRATE_ARG); + +AMETHOD_DEF(ENCODER, GET_BITRATE, "get_bitrate"); +AMETHOD_ARG_DEF(ENCODER, GET_BITRATE, BITRATE, ESP_GMF_AUDIO_BITRATE_ARG); + +AMETHOD_DEF(ENCODER, RECONFIG, "recfg"); +AMETHOD_ARG_DEF(ENCODER, RECONFIG, CFG, ESP_GMF_AUDIO_CFG_ARG); +AMETHOD_ARG_DEF(ENCODER, RECONFIG, CFG_TYPE, ESP_GMF_AUDIO_CFG_TYPE_ARG); +AMETHOD_ARG_DEF(ENCODER, RECONFIG, CFG_SUBCFGPTR, ESP_GMF_AUDIO_CFG_SUBCFGPTR_ARG); +AMETHOD_ARG_DEF(ENCODER, RECONFIG, CFG_SUBCFGSZ, ESP_GMF_AUDIO_CFG_SUBCFGSZ_ARG); + +AMETHOD_DEF(ENCODER, RECONFIG_BY_SND_INFO, "recfg_by_sndinfo"); +AMETHOD_ARG_DEF(ENCODER, RECONFIG_BY_SND_INFO, INFO, ESP_GMF_AUDIO_SND_INFO_ARG); +AMETHOD_ARG_DEF(ENCODER, RECONFIG_BY_SND_INFO, INFO_TYPE, ESP_GMF_AUDIO_SND_INFO_TYPE_ARG); +AMETHOD_ARG_DEF(ENCODER, RECONFIG_BY_SND_INFO, INFO_SAMPLERATE, ESP_GMF_AUDIO_SND_INFO_SAMPLERATE_ARG); +AMETHOD_ARG_DEF(ENCODER, RECONFIG_BY_SND_INFO, INFO_BITRATE, ESP_GMF_AUDIO_SND_INFO_BITRATE_ARG); +AMETHOD_ARG_DEF(ENCODER, RECONFIG_BY_SND_INFO, INFO_CHANNEL, ESP_GMF_AUDIO_SND_INFO_CHANNEL_ARG); +AMETHOD_ARG_DEF(ENCODER, RECONFIG_BY_SND_INFO, INFO_BITS, ESP_GMF_AUDIO_SND_INFO_BITS_ARG); + +/** + * @brief DECODER method + */ +AMETHOD_DEF(DECODER, RECONFIG, "recfg"); +AMETHOD_ARG_DEF(DECODER, RECONFIG, CFG, ESP_GMF_AUDIO_CFG_ARG); +AMETHOD_ARG_DEF(DECODER, RECONFIG, CFG_TYPE, ESP_GMF_AUDIO_CFG_TYPE_ARG); +AMETHOD_ARG_DEF(DECODER, RECONFIG, CFG_SUBCFGPTR, ESP_GMF_AUDIO_CFG_SUBCFGPTR_ARG); +AMETHOD_ARG_DEF(DECODER, RECONFIG, CFG_SUBCFGSZ, ESP_GMF_AUDIO_CFG_SUBCFGSZ_ARG); + +AMETHOD_DEF(DECODER, RECONFIG_BY_SND_INFO, "recfg_by_sndinfo"); +AMETHOD_ARG_DEF(DECODER, RECONFIG_BY_SND_INFO, INFO, ESP_GMF_AUDIO_SND_INFO_ARG); +AMETHOD_ARG_DEF(DECODER, RECONFIG_BY_SND_INFO, INFO_TYPE, ESP_GMF_AUDIO_SND_INFO_TYPE_ARG); +AMETHOD_ARG_DEF(DECODER, RECONFIG_BY_SND_INFO, INFO_SAMPLERATE, ESP_GMF_AUDIO_SND_INFO_SAMPLERATE_ARG); +AMETHOD_ARG_DEF(DECODER, RECONFIG_BY_SND_INFO, INFO_CHANNEL, ESP_GMF_AUDIO_SND_INFO_CHANNEL_ARG); +AMETHOD_ARG_DEF(DECODER, RECONFIG_BY_SND_INFO, INFO_BITS, ESP_GMF_AUDIO_SND_INFO_BITS_ARG); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/include/esp_gmf_audio_param.h b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/include/esp_gmf_audio_param.h new file mode 100644 index 0000000..e0b3fc8 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/include/esp_gmf_audio_param.h @@ -0,0 +1,128 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#include "esp_gmf_element.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Set frame destination sample rate for audio element + * + * @param[in] handle Audio element handle + * @param[in] dest_rate Destination sample rate + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_NOT_FOUND Not found the method + * - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory + * - Others Failed to apply method + */ +esp_gmf_err_t esp_gmf_audio_param_set_dest_rate(esp_gmf_element_handle_t self, uint32_t dest_rate); + +/** + * @brief Set frame destination bits for audio element + * + * @param[in] handle Audio element handle + * @param[in] dest_bits Destination bits + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_NOT_FOUND Not found the method + * - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory + * - Others Failed to apply method + */ +esp_gmf_err_t esp_gmf_audio_param_set_dest_bits(esp_gmf_element_handle_t self, uint8_t dest_bits); + +/** + * @brief Set frame destination channel for audio element + * + * @param[in] handle Audio element handle + * @param[in] dest_ch Destination channel + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_NOT_FOUND Not found the method + * - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory + * - Others Failed to apply method + */ +esp_gmf_err_t esp_gmf_audio_param_set_dest_ch(esp_gmf_element_handle_t self, uint8_t dest_ch); + +/** + * @brief Set speed for audio element + * + * @param[in] handle Audio element handle + * @param[in] speed Audio playback speed + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_NOT_FOUND Not found the method + * - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory + * - Others Failed to apply method + */ +esp_gmf_err_t esp_gmf_audio_param_set_speed(esp_gmf_element_handle_t self, float speed); + +/** + * @brief Set pitch for audio element + * + * @param[in] handle Audio element handle + * @param[in] pitch Audio pitch to set + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_NOT_FOUND Not found the method + * - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory + * - Others Failed to apply method + */ +esp_gmf_err_t esp_gmf_audio_param_set_pitch(esp_gmf_element_handle_t self, float pitch); + +/** + * @brief Set ALC certain channel gain for audio element + * + * @param[in] handle Audio element handle + * @param[in] ch_idx Channel index (0: Left channel 1: Right Channel 0xFF: All channels) + * @param[in] gain_db ALC gain (unit decibel) + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_NOT_FOUND Not found the method + * - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory + * - Others Failed to apply method + */ +esp_gmf_err_t esp_gmf_audio_param_set_alc_channel_gain(esp_gmf_element_handle_t self, uint8_t ch_idx, float gain_db); + +/** + * @brief Set ALC gain for all channels + */ +#define esp_gmf_audio_param_set_alc_gain(self, gain_db) esp_gmf_audio_param_set_alc_channel_gain(self, 0xFF, gain_db) + +/** + * @brief Set fade in/out direction for audio element + * + * @param[in] handle Audio element handle + * @param[in] is_fade_in Whether fade in or out + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_NOT_FOUND Not found the method + * - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory + * - Others Failed to apply method + */ +esp_gmf_err_t esp_gmf_audio_param_set_fade(esp_gmf_element_handle_t self, bool is_fade_in); + +/** + * @brief Helper wrapper for fade in and fade out setting + */ +#define esp_gmf_audio_param_set_fade_in(self) esp_gmf_audio_param_set_fade(self, true); +#define esp_gmf_audio_param_set_fade_out(self) esp_gmf_audio_param_set_fade(self, false); + +#ifdef __cplusplus +} +#endif diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/include/esp_gmf_bit_cvt.h b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/include/esp_gmf_bit_cvt.h new file mode 100644 index 0000000..2b4bce7 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/include/esp_gmf_bit_cvt.h @@ -0,0 +1,53 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#include "esp_gmf_err.h" +#include "esp_ae_bit_cvt.h" +#include "esp_gmf_element.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define DEFAULT_ESP_GMF_BIT_CVT_CONFIG() { \ + .sample_rate = 48000, \ + .channel = 2, \ + .src_bits = 16, \ + .dest_bits = 16, \ +} + +/** + * @brief Initializes the GMF bit conversion with the provided configuration + * + * @param[in] config Pointer to the bit conversion configuration + * @param[out] handle Pointer to the bit conversion handle to be initialized + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid configuration provided + * - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory + */ +esp_gmf_err_t esp_gmf_bit_cvt_init(esp_ae_bit_cvt_cfg_t *config, esp_gmf_element_handle_t *handle); + +/** + * @brief Set destination bits for the bit conversion handle + * + * @param[in] handle The channel bit handle + * @param[in] dest_bits The destination bits + * + * @return + * - ESP_GMF_ERR_OK Operation succeeded + * - ESP_GMF_ERR_INVALID_ARG Invalid input parameter + * - ESP_GMF_ERR_FAIL Failed to set configuration + */ +esp_gmf_err_t esp_gmf_bit_cvt_set_dest_bits(esp_gmf_element_handle_t handle, uint8_t dest_bits); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/include/esp_gmf_ch_cvt.h b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/include/esp_gmf_ch_cvt.h new file mode 100644 index 0000000..cb350c7 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/include/esp_gmf_ch_cvt.h @@ -0,0 +1,55 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#include "esp_gmf_err.h" +#include "esp_ae_ch_cvt.h" +#include "esp_gmf_element.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define DEFAULT_ESP_GMF_CH_CVT_CONFIG() { \ + .sample_rate = 48000, \ + .bits_per_sample = 16, \ + .src_ch = 2, \ + .dest_ch = 2, \ + .weight = NULL, \ + .weight_len = 0, \ +} + +/** + * @brief Initializes the GMF channel conversion with the provided configuration + * + * @param[in] config Pointer to the channel conversion configuration + * @param[out] handle Pointer to the channel conversion handle to be initialized + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid configuration provided + * - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory + */ +esp_gmf_err_t esp_gmf_ch_cvt_init(esp_ae_ch_cvt_cfg_t *config, esp_gmf_element_handle_t *handle); + +/** + * @brief Set dest channel in the channel conversion handle + * + * @param[in] handle The channel conversion handle + * @param[in] dest_ch The dest channel + * + * @return + * - ESP_GMF_ERR_OK Operation succeeded + * - ESP_GMF_ERR_INVALID_ARG Invalid input parameter + * - ESP_GMF_ERR_FAIL Failed to set configuration + */ +esp_gmf_err_t esp_gmf_ch_cvt_set_dest_channel(esp_gmf_element_handle_t handle, uint8_t dest_ch); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/include/esp_gmf_deinterleave.h b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/include/esp_gmf_deinterleave.h new file mode 100644 index 0000000..508cfea --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/include/esp_gmf_deinterleave.h @@ -0,0 +1,47 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#include "esp_gmf_err.h" +#include "esp_gmf_element.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define DEFAULT_ESP_GMF_DEINTERLEAVE_CONFIG() { \ + .sample_rate = 48000, \ + .bits_per_sample = 16, \ + .channel = 2, \ +} + +/** + * @brief Configuration structure for deinterleave + */ +typedef struct { + uint32_t sample_rate; /*!< The audio sample rate */ + uint8_t bits_per_sample; /*!< The audio bits per sample, supports 16, 24, 32 bits */ + uint8_t channel; /*!< The audio channel */ +} esp_gmf_deinterleave_cfg; + +/** + * @brief Initializes the GMF deinterleave with the provided configuration + * + * @param[in] config Pointer to the deinterleave configuration + * @param[out] handle Pointer to the deinterleave handle to be initialized + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid configuration provided + * - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory + */ +esp_gmf_err_t esp_gmf_deinterleave_init(esp_gmf_deinterleave_cfg *config, esp_gmf_element_handle_t *handle); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/include/esp_gmf_eq.h b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/include/esp_gmf_eq.h new file mode 100644 index 0000000..f8523c0 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/include/esp_gmf_eq.h @@ -0,0 +1,87 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#include "esp_gmf_err.h" +#include "esp_ae_eq.h" +#include "esp_gmf_element.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * @brief Default EQ parameters + * If `para` is NULL in the `esp_ae_eq_cfg_t` , the EQ will use `esp_gmf_default_eq_paras` as default + * And the `filter_num` will be set to sizeof(esp_gmf_default_eq_paras) / sizeof(esp_ae_eq_filter_para_t) + */ +#define DEFAULT_ESP_GMF_EQ_CONFIG() { \ + .sample_rate = 48000, \ + .channel = 2, \ + .bits_per_sample = 16, \ + .filter_num = 0, \ + .para = NULL, \ +} + +/** + * @brief Initializes the GMF EQ with the provided configuration + * + * @param[in] config Pointer to the EQ configuration + * @param[out] handle Pointer to the EQ handle to be initialized + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid configuration provided + * - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory + */ +esp_gmf_err_t esp_gmf_eq_init(esp_ae_eq_cfg_t *config, esp_gmf_element_handle_t *handle); + +/** + * @brief Set the filter parameters for a specific filter identified by 'idx' + * + * @param[in] handle The EQ handle + * @param[in] idx The index of a specific filter for which the parameters are to be set. + * eg: 0 refers to the first filter + * @param[in] para The filter setup parameter + * + * @return + * - ESP_GMF_ERR_OK Operation succeeded + * - ESP_GMF_ERR_INVALID_ARG Invalid input parameter + */ +esp_gmf_err_t esp_gmf_eq_set_para(esp_gmf_element_handle_t handle, uint8_t idx, esp_ae_eq_filter_para_t *para); + +/** + * @brief Get the filter parameters for a specific filter identified by 'idx' + * + * @param[in] handle The EQ handle + * @param[in] idx The index of a specific filter for which the parameters are to be retrieved. + * eg: 0 refers to first filter + * @param[out] para The filter setup parameter + * + * @return + * - ESP_GMF_ERR_OK Operation succeeded + * - ESP_GMF_ERR_INVALID_ARG Invalid input parameter + */ +esp_gmf_err_t esp_gmf_eq_get_para(esp_gmf_element_handle_t handle, uint8_t idx, esp_ae_eq_filter_para_t *para); + +/** + * @brief Choose to enable or disable filter processing for a specific filter identified by 'idx' in the equalizer + * + * @param[in] handle The EQ handle + * @param[in] idx The index of a specific filter to be enabled + * @param[in] is_enable The flag of whether to enable band filter processing + * + * @return + * - ESP_GMF_ERR_OK Operation succeeded + * - ESP_GMF_ERR_INVALID_ARG Invalid input parameter + */ +esp_gmf_err_t esp_gmf_eq_enable_filter(esp_gmf_element_handle_t handle, uint8_t idx, bool is_enable); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/include/esp_gmf_fade.h b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/include/esp_gmf_fade.h new file mode 100644 index 0000000..a36f80c --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/include/esp_gmf_fade.h @@ -0,0 +1,81 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#include "esp_gmf_err.h" +#include "esp_ae_fade.h" +#include "esp_gmf_element.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define DEFAULT_ESP_GMF_FADE_CONFIG() { \ + .mode = ESP_AE_FADE_MODE_FADE_IN, \ + .curve = ESP_AE_FADE_CURVE_LINE, \ + .transit_time = 500, \ + .sample_rate = 48000, \ + .channel = 2, \ + .bits_per_sample = 16, \ +} + +/** + * @brief Initializes the GMF fade with the provided configuration + * + * @param[in] config Pointer to the fade configuration + * @param[out] handle Pointer to the fade handle to be initialized + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid configuration provided + * - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory + */ +esp_gmf_err_t esp_gmf_fade_init(esp_ae_fade_cfg_t *config, esp_gmf_element_handle_t *handle); + +/** + * @brief Set the fade process mode + * + * @param[in] handle The fade handle + * @param[in] mode The mode of fade + * + * @return + * - ESP_GMF_ERR_OK Operation succeeded + * - ESP_GMF_ERR_INVALID_ARG Invalid input parameter + */ +esp_gmf_err_t esp_gmf_fade_set_mode(esp_gmf_element_handle_t handle, esp_ae_fade_mode_t mode); + +/** + * @brief Get the fade process mode + * + * @param[in] handle The fade handle + * @param[out] mode The mode of fade + * + * @return + * - ESP_GMF_ERR_OK Operation succeeded + * - ESP_GMF_ERR_INVALID_ARG Invalid input parameter + */ +esp_gmf_err_t esp_gmf_fade_get_mode(esp_gmf_element_handle_t handle, esp_ae_fade_mode_t *mode); + +/** + * @brief Reset the fade process to the initial configuration state. + * If the initial configuration mode is fade in, the current weight is set to 0. + * If the initial configuration mode is fade out, the current weight is set to 1. + * This function helps the user to restart fade in or fade out without + * reopening and closing. + * + * @param[in] handle The fade handle + * + * @return + * - ESP_GMF_ERR_OK Operation succeeded + * - ESP_GMF_ERR_INVALID_ARG Invalid input parameter + */ +esp_gmf_err_t esp_gmf_fade_reset_weight(esp_gmf_element_handle_t handle); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/include/esp_gmf_interleave.h b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/include/esp_gmf_interleave.h new file mode 100644 index 0000000..412dee3 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/include/esp_gmf_interleave.h @@ -0,0 +1,47 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#include "esp_gmf_err.h" +#include "esp_gmf_element.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define DEFAULT_ESP_GMF_INTERLEAVE_CONFIG() { \ + .sample_rate = 48000, \ + .bits_per_sample = 16, \ + .src_num = 2, \ +} + +/** + * @brief Configuration structure for interleave + */ +typedef struct { + uint32_t sample_rate; /*!< The audio sample rate */ + uint8_t bits_per_sample; /*!< The audio bits per sample, supports 16, 24, 32 bits */ + uint8_t src_num; /*!< The number of input source num */ +} esp_gmf_interleave_cfg; + +/** + * @brief Initializes the GMF interleave with the provided configuration + * + * @param[in] config Pointer to the interleave configuration + * @param[out] handle Pointer to the interleave handle to be initialized + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid configuration provided + * - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory + */ +esp_gmf_err_t esp_gmf_interleave_init(esp_gmf_interleave_cfg *config, esp_gmf_element_handle_t *handle); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/include/esp_gmf_mixer.h b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/include/esp_gmf_mixer.h new file mode 100644 index 0000000..022e14d --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/include/esp_gmf_mixer.h @@ -0,0 +1,76 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#include "esp_gmf_err.h" +#include "esp_ae_mixer.h" +#include "esp_gmf_element.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * @brief Default mixer source info + * If `src_info` is NULL in `esp_ae_mixer_cfg_t`, the mixer will use esp_gmf_default_mixer_src_info as default + * And `src_num` will set to `sizeof(esp_gmf_default_mixer_src_info) / sizeof(esp_ae_mixer_info_t)`; + */ +#define DEFAULT_ESP_GMF_MIXER_CONFIG() { \ + .sample_rate = 48000, \ + .channel = 2, \ + .bits_per_sample = 16, \ + .src_num = 0, \ + .src_info = NULL, \ +} + +/** + * @brief Initializes the GMF mixer with the provided configuration + * + * @param[in] config Pointer to the mixer configuration + * @param[out] handle Pointer to the mixer handle to be initialized + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid configuration provided + * - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory + */ +esp_gmf_err_t esp_gmf_mixer_init(esp_ae_mixer_cfg_t *config, esp_gmf_element_handle_t *handle); + +/** + * @brief Set the transit mode of a certain stream according to src_idx + * + * @param[in] handle The mixer handle + * @param[in] src_idx The index of a certain source stream which want to set transit mode. + * eg: 0 refer to first source stream + * @param[in] mode The transit mode of source stream + * + * @return + * - ESP_GMF_ERR_OK Operation succeeded + * - ESP_GMF_ERR_INVALID_ARG Invalid input parameter + */ +esp_gmf_err_t esp_gmf_mixer_set_mode(esp_gmf_element_handle_t handle, uint8_t src_idx, esp_ae_mixer_mode_t mode); + +/** + * @brief Set audio information to the mixer handle + * + * @param[in] handle The mixer handle + * @param[in] sample_rate The audio sample rate + * @param[in] bits The audio bits per sample + * @param[in] channel The audio channel + * + * @return + * - ESP_GMF_ERR_OK Operation succeeded + * - ESP_GMF_ERR_INVALID_ARG Invalid input parameter + * - ESP_GMF_ERR_FAIL Failed to set configuration + */ +esp_gmf_err_t esp_gmf_mixer_set_audio_info(esp_gmf_element_handle_t handle, uint32_t sample_rate, + uint8_t bits, uint8_t channel); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/include/esp_gmf_rate_cvt.h b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/include/esp_gmf_rate_cvt.h new file mode 100644 index 0000000..247ae7c --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/include/esp_gmf_rate_cvt.h @@ -0,0 +1,55 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#include "esp_gmf_err.h" +#include "esp_ae_rate_cvt.h" +#include "esp_gmf_element.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define DEFAULT_ESP_GMF_RATE_CVT_CONFIG() { \ + .src_rate = 44100, \ + .dest_rate = 48000, \ + .channel = 2, \ + .bits_per_sample = 16, \ + .complexity = 2, \ + .perf_type = ESP_AE_RATE_CVT_PERF_TYPE_SPEED, \ +} + +/** + * @brief Initializes the GMF rate conversion with the provided configuration + * + * @param[in] config Pointer to the rate conversion configuration + * @param[out] handle Pointer to the rate conversion handle to be initialized + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid configuration provided + * - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory + */ +esp_gmf_err_t esp_gmf_rate_cvt_init(esp_ae_rate_cvt_cfg_t *config, esp_gmf_element_handle_t *handle); + +/** + * @brief Set dest rate in the rate conversion handle + * + * @param[in] handle The rate conversion handle + * @param[in] dest_ch The dest rate + * + * @return + * - ESP_GMF_ERR_OK Operation succeeded + * - ESP_GMF_ERR_INVALID_ARG Invalid input parameter + * - ESP_GMF_ERR_FAIL Failed to set configuration + */ +esp_gmf_err_t esp_gmf_rate_cvt_set_dest_rate(esp_gmf_element_handle_t handle, uint32_t dest_rate); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/include/esp_gmf_sonic.h b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/include/esp_gmf_sonic.h new file mode 100644 index 0000000..cd06e8f --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/include/esp_gmf_sonic.h @@ -0,0 +1,92 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#include "esp_gmf_err.h" +#include "esp_ae_sonic.h" +#include "esp_gmf_element.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define DEFAULT_ESP_GMF_SONIC_CONFIG() { \ + .sample_rate = 48000, \ + .channel = 2, \ + .bits_per_sample = 16, \ +} + +/** + * @brief Initializes the GMF sonic with the provided configuration + * + * @param[in] config Pointer to the sonic configuration + * @param[out] handle Pointer to the sonic handle to be initialized + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_ERR_INVALID_ARG Invalid configuration provided + * - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory + */ +esp_gmf_err_t esp_gmf_sonic_init(esp_ae_sonic_cfg_t *config, esp_gmf_element_handle_t *handle); + +/** + * @brief Set the audio speed + * + * @param[in] handle The handle of the sonic + * @param[in] speed The scaling factor of audio speed. + * The range of speed is [0.5, 2.0] + * + * @return + * - ESP_GMF_ERR_OK Operation succeeded + * - ESP_GMF_ERR_INVALID_ARG Invalid input parameter + */ +esp_gmf_err_t esp_gmf_sonic_set_speed(esp_gmf_element_handle_t handle, float speed); + +/** + * @brief Get the audio speed + * + * @param[in] handle The handle of the sonic + * @param[out] speed The scaling factor of audio speed + * + * @return + * - ESP_GMF_ERR_OK Operation succeeded + * - ESP_GMF_ERR_INVALID_ARG Invalid input parameter + */ +esp_gmf_err_t esp_gmf_sonic_get_speed(esp_gmf_element_handle_t handle, float *speed); + +/** + * @brief Set the audio pitch + * + * @param[in] handle The handle of the sonic + * @param[in] pitch The scaling factor of audio pitch. + * The range of pitch is [0.5, 2.0]. + * If the pitch value is smaller than 1.0, the sound is deep voice; + * if the pitch value is equal to 1.0, the sound is no change; + * if the pitch value is gather than 1.0, the sound is sharp voice; + * + * @return + * - ESP_GMF_ERR_OK Operation succeeded + * - ESP_GMF_ERR_INVALID_ARG Invalid input parameter + */ +esp_gmf_err_t esp_gmf_sonic_set_pitch(esp_gmf_element_handle_t handle, float pitch); + +/** + * @brief Get the audio pitch + * + * @param[in] handle The handle of the sonic + * @param[out] pitch The scaling factor of audio pitch + * + * @return + * - ESP_GMF_ERR_OK Operation succeeded + * - ESP_GMF_ERR_INVALID_ARG Invalid input parameter + */ +esp_gmf_err_t esp_gmf_sonic_get_pitch(esp_gmf_element_handle_t handle, float *pitch); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/private_include/gmf_audio_common.h b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/private_include/gmf_audio_common.h new file mode 100644 index 0000000..bb7b089 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_audio/private_include/gmf_audio_common.h @@ -0,0 +1,35 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#include "esp_err.h" +#include "esp_gmf_info.h" +#include "esp_gmf_audio_element.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GMF_AUDIO_UPDATE_SND_INFO(self, sample_rate, bits, channel) gmf_audio_update_snd_info(self, sample_rate, bits, channel) +#define GMF_AUDIO_INPUT_SAMPLE_NUM (256) +#define GMF_AUDIO_CALC_PTS(out_len, sample_rate, ch, bits) ((out_len) * 8000 / ((sample_rate) * (ch) * (bits))) + +static inline void gmf_audio_update_snd_info(void *self, uint32_t sample_rate, uint8_t bits, uint8_t channel) +{ + esp_gmf_info_sound_t snd_info = {0}; + esp_gmf_audio_el_get_snd_info(self, &snd_info); + snd_info.sample_rates = sample_rate; + snd_info.channels = channel; + snd_info.bits = bits; + esp_gmf_audio_el_set_snd_info(self, &snd_info); + esp_gmf_element_notify_snd_info(self, &snd_info); +} + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_io/CHANGELOG.md b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_io/CHANGELOG.md new file mode 100644 index 0000000..0033cbc --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_io/CHANGELOG.md @@ -0,0 +1,97 @@ +# Changelog + +## v0.7.6 + +### Features + +- Support C++ build + +## v0.7.5 + +### Bug Fixes + +- Fixed the issue where idf_build_set_property is not a valid function for component + +## v0.7.4 + +### Bug Fixes + +- Fixed data bus init when direction is write in `io_http` + +## v0.7.3 + +### Bug Fixes + +- Fixed cache caps issues for esp32 in `io_file` + +## v0.7.2 + +### Features + +- Added io_file cache size configuration to enhance read and write performance + +### Bug Fixes + +- Delay task creation to open stage in `io_http` to avoid task resource waste if no used + +## v0.7.1 + +- Updated `esp-codec-dev` dependency to v1.4.0 + +## v0.7.0 + +### Features + +- Added `esp_gmf_io_codec_dev_set_dev` API to set the audio codec device handle +- Added `esp_gmf_io_http_set_event_callback` API to set an HTTP event callback +- Added basic reset functionality to each IO to properly clean up resources and reset state + +### Bug Fixes +- Fixed an issue where HTTP IO did not reset the thread and databus properly +- Fixed file open check for treat return error as valid fd +- Corrected return value validation for *acq_write/read and *acq_release_write/read callback function implementations +- Fixed HTTP connection timeout handling during _http_close operations +- Standardize TAG identifier format across all I/O elements with `io` prefix + + +## v0.6.3 + +### Bug Fixes + +- Changed the tag for embedded flash IO from `embed_flash` to `embed` +- Refactor the script to make the generated files follow our coding style + +## v0.6.2 + +### Features + +- Limit the version of `esp_codec_dev` + +## v0.6.1 + +- Fixed memory leaks in I/O flash destroy API +- Corrected default values for I/O file and I/O I2S elements to ensure stable initialization +- Enforced esp_gmf_err_t return type for all element initialization functions (I/O flash, HTTP) + +## v0.6.0 + +### Features +- Added support for NULL object configuration +- Renamed component to `gmf_io` +- Updated the License + +### Bug Fixes +- Standardized return values of port and data bus acquire/release APIs to esp_gmf_err_io_t only + +## v0.5.1 + +### Bug Fixes + +- Fixed the component requirements + + +## v0.5.0 + +### Features + +- Initial version of `esp-gmf-io` diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_io/CMakeLists.txt b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_io/CMakeLists.txt new file mode 100644 index 0000000..75a4a2a --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_io/CMakeLists.txt @@ -0,0 +1,23 @@ +idf_build_get_property(build_components BUILD_COMPONENTS) +set(search_pattern "esp_codec_dev") +set(index -1) +foreach(item IN LISTS build_components) + if("${item}" MATCHES "${search_pattern}.*") + set(index ${item}) + break() + endif() +endforeach() + +list(APPEND io_srcs "esp_gmf_io_file.c" "esp_gmf_io_embed_flash.c" "esp_gmf_io_i2s_pdm.c" "esp_gmf_io_http.c" "http_lib/gzip/gzip_miniz.c") +if(index EQUAL -1) + set(io_inc "") +else() + message(STATUS "The esp_codec_dev is installed") + list(APPEND io_srcs "codec_dev_io/esp_gmf_io_codec_dev.c") + set(io_inc "codec_dev_io") +endif() + +idf_component_register(SRCS ${io_srcs} + INCLUDE_DIRS ./include ${io_inc} "http_lib/gzip/" "http_lib/gzip/include" + PRIV_INCLUDE_DIRS "" + REQUIRES "driver" "esp_http_client") diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_io/LICENSE b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_io/LICENSE new file mode 100644 index 0000000..95aa434 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_io/LICENSE @@ -0,0 +1,22 @@ +Espressif Modified MIT License + +Copyright (c) 2025 Espressif Systems (Shanghai) CO., LTD + +Permission is hereby granted for use EXCLUSIVELY with Espressif Systems products. +This includes the right to use, copy, modify, merge, publish, distribute, and sublicense +the Software, subject to the following conditions: + +1. This Software MUST BE USED IN CONJUNCTION WITH ESPRESSIF SYSTEMS PRODUCTS. +2. The above copyright notice and this permission notice shall be included in all copies + or substantial portions of the Software. +3. Redistribution of the Software in source or binary form FOR USE WITH NON-ESPRESSIF PRODUCTS + is strictly prohibited. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_io/README.md b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_io/README.md new file mode 100644 index 0000000..a596138 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_io/README.md @@ -0,0 +1,36 @@ +# ESP-GMF-IO + +- [![Component Registry](https://components.espressif.com/components/espressif/gmf_io/badge.svg)](https://components.espressif.com/components/espressif/gmf_io) + +- [中文版](./README_CN.md) + +ESP GMF IO is a collection of GMF input and output streams. IO streams have two data flow directions: reading and writing. The input type is readable, and the output type is writable. IO streams classify data transmission types into byte access and block access types. In general, byte access involves copying the source data before transferring, while block access only transfers the source data address without copy overhead. Additionally, IO streams provide optional thread configuration, allowing some IO streams to establish their own independent threads. The currently supported IO streams are listed in the table below: + +| Name | TAG | Data flow direction | Thread | Data Type| Dependent Components | Notes | +| :----: | :----: | :----: | :----: | :----: | :----: |:----: | +| File | io_file | RW | NO | Byte |NA | NA | +| HTTP | io_http | RW | YES | Block | NA | Not support HTTP Live Stream | +| Codec Dev IO | io_codec_dev | RW | NO | Byte | [ESP codec dev](https://components.espressif.com/components/espressif/esp_codec_dev) | NA | +| Embed Flash | io_embed_flash | R | NO | Byte | NA | NA | +| I2S PDM | io_i2s_pdm | RW | NO | Byte | NA | NA | + +## Usage +ESP GMF IO is typically used in combination with GMF Elements to form a pipeline, but it can also be used independently. In a pipeline, the IO stream is connected to the GMF Element's Port, and when used independently, the IO stream is accessed through the ESP_GMF_IO interface. For example code, please refer to [test_app](../test_apps/main/elements/gmf_audio_play_el_test.c)。 + +You can also create and compile a project using the following commands, taking the `pipeline_play_embed_music` project as an example. Before starting, make sure you have a working [ESP-IDF](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/get-started/index.html) environment. + +### 1. Create the Example Project + +Create the `pipeline_play_embed_music` example project based on the `gmf_examples` component (using version v0.7.0 as an example; update the version as needed): + +```shell +idf.py create-project-from-example "espressif/gmf_examples=0.7.0:pipeline_play_embed_music" +``` + +### 2. Build and Flash the Project Using an ESP32-S3 Board + +```shell +cd pipeline_play_embed_music +idf.py set-target esp32s3 +idf.py -p YOUR_PORT flash monitor +``` diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_io/README_CN.md b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_io/README_CN.md new file mode 100644 index 0000000..2c8508a --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_io/README_CN.md @@ -0,0 +1,36 @@ +# ESP-GMF-IO + +- [![Component Registry](https://components.espressif.com/components/espressif/gmf_io/badge.svg)](https://components.espressif.com/components/espressif/gmf_io) + +- [English](./README.md) + +ESP GMF IO 是 GMF 输入和输出流的集合。IO 流有读写两个数据流方向,输入类型为可读,输出类型为可写。IO 流对数据传输类型进行了分类,分别是字节访问和块访问类型,一般来说,字节访问对源数据进行拷贝后传递,块访问仅传递源数据地址无拷贝开销。另外,IO 流还提供了线程的配置可选项,使一些 IO Stream 可以建立自己独立的线程。目前已支持的 IO 流参见下表: + +| 名称 | TAG | 数据流方向 | 作为线程 | 数据类型| 依赖的组件 | 备注 | +| :----: | :----: | :----: | :----: | :----: | :----: |:----: | +| File | io_file | RW | NO | Byte |NA | NA | +| HTTP | io_http | RW | YES | Block | NA | Not support HTTP Live Stream | +| Codec Dev IO | io_codec_dev | RW | NO | Byte | [ESP codec dev](https://components.espressif.com/components/espressif/esp_codec_dev) | NA | +| Embed Flash | io_embed_flash | R | NO | Byte | NA | NA | +| I2S PDM | io_i2s_pdm | RW | NO | Byte | NA | NA | + +## 示例 +ESP GMF IO 一般与 GMF 元素组成管道进行使用,也可以独立使用。在管道中,IO stream 与 GMF 元素的端口连接,独立使用时调用 IO stream 使用 ESP_GMF_IO 接口。 示例代码请参考 [test_app](../test_apps/main/elements/gmf_audio_play_el_test.c)。 + +此外,也可通过以下命令创建并编译工程,以 `pipeline_play_embed_music` 示例工程为例。开始之前需要有可运行的 [ESP-IDF](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/get-started/index.html) 环境。 + +### 1. 创建示例工程 + +基于 `gmf_examples` 组件创建 `pipeline_play_embed_music` 的示例(以 v0.7.0 版本为例, 请根据实际使用更新版本参数) + +``` shell +idf.py create-project-from-example "espressif/gmf_examples=0.7.0:pipeline_play_embed_music" +``` + +### 2. 基于 ESP32S3 编译和下载 + +```shell +cd pipeline_play_embed_music +idf.py set-target esp32s3` +idf.py -p YOUR_PORT flash monitor +``` diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_io/codec_dev_io/esp_gmf_io_codec_dev.c b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_io/codec_dev_io/esp_gmf_io_codec_dev.c new file mode 100644 index 0000000..80a0aa5 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_io/codec_dev_io/esp_gmf_io_codec_dev.c @@ -0,0 +1,174 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#include +#include +#include "esp_gmf_io_codec_dev.h" +#include "esp_gmf_oal_mem.h" +#include "esp_log.h" + +/** + * @brief Codec device io context in GMF + */ +typedef struct { + esp_gmf_io_t base; /*!< The GMF codec dev io handle */ + bool is_open; /*!< The flag of whether opened */ +} codec_dev_io_stream_t; + +static const char *TAG = "ESP_GMF_CODEC_DEV"; + +static esp_gmf_err_t esp_gmf_io_codec_dev_new(void *cfg, esp_gmf_obj_handle_t *io) +{ + return esp_gmf_io_codec_dev_init(cfg, io); +} + +static esp_gmf_err_t _codec_dev_open(esp_gmf_io_handle_t io) +{ + codec_dev_io_stream_t *codec_dev_io = (codec_dev_io_stream_t *)io; + codec_dev_io_cfg_t *cfg = (codec_dev_io_cfg_t *)OBJ_GET_CFG(codec_dev_io); + ESP_GMF_NULL_CHECK(TAG, cfg, return ESP_GMF_ERR_FAIL;); + ESP_GMF_CHECK(TAG, cfg->dev, {return ESP_GMF_ERR_FAIL;}, "There is no activated I2S driver handle"); + codec_dev_io->is_open = true; + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_io_t _codec_dev_acquire_read(esp_gmf_io_handle_t handle, void *payload, uint32_t wanted_size, int block_ticks) +{ + codec_dev_io_stream_t *codec_dev_io = (codec_dev_io_stream_t *)handle; + esp_gmf_payload_t *pload = (esp_gmf_payload_t *)payload; + codec_dev_io_cfg_t *cfg = (codec_dev_io_cfg_t *)OBJ_GET_CFG(codec_dev_io); + ESP_GMF_NULL_CHECK(TAG, cfg, return ESP_GMF_IO_FAIL;); + if (esp_codec_dev_read(cfg->dev, pload->buf, wanted_size) != ESP_GMF_IO_OK) { + ESP_LOGE(TAG, "Read failed, wanted: %ld", wanted_size); + return ESP_GMF_IO_FAIL; + } + pload->valid_size = wanted_size; + return ESP_GMF_IO_OK; +} + +static esp_gmf_err_io_t _codec_dev_release_read(esp_gmf_io_handle_t handle, void *payload, int block_ticks) +{ + codec_dev_io_stream_t *codec_dev_io = (codec_dev_io_stream_t *)handle; + esp_gmf_payload_t *pload = (esp_gmf_payload_t *)payload; + esp_gmf_info_file_t info = {0}; + esp_gmf_io_get_info((esp_gmf_io_handle_t)codec_dev_io, &info); + ESP_LOGD(TAG, "Update len = %d, pos = %d/%d", pload->valid_size, (int)info.pos, (int)info.size); + esp_gmf_io_update_pos((esp_gmf_io_handle_t)handle, pload->valid_size); + return ESP_GMF_IO_OK; +} + +static esp_gmf_err_io_t _codec_dev_acquire_write(esp_gmf_io_handle_t handle, void *payload, uint32_t wanted_size, int block_ticks) +{ + return ESP_GMF_IO_OK; +} + +static esp_gmf_err_io_t _codec_dev_release_write(esp_gmf_io_handle_t handle, void *payload, int block_ticks) +{ + codec_dev_io_stream_t *codec_dev_io = (codec_dev_io_stream_t *)handle; + esp_gmf_payload_t *pload = (esp_gmf_payload_t *)payload; + codec_dev_io_cfg_t *cfg = (codec_dev_io_cfg_t *)OBJ_GET_CFG(codec_dev_io); + ESP_GMF_NULL_CHECK(TAG, cfg, return ESP_GMF_IO_FAIL;); + size_t wlen = pload->valid_size; + if (esp_codec_dev_write(cfg->dev, pload->buf, pload->valid_size) != ESP_GMF_ERR_OK) { + ESP_LOGE(TAG, "Write failed, valid: %d", pload->valid_size); + return ESP_GMF_IO_FAIL; + } + esp_gmf_info_file_t info = {0}; + if (wlen > 0) { + esp_gmf_io_update_pos((esp_gmf_io_handle_t)handle, wlen); + } + esp_gmf_io_get_info((esp_gmf_io_handle_t)codec_dev_io, &info); + ESP_LOGD(TAG, "Write len: %d, pos: %d/%d", pload->valid_size, (int)info.pos, (int)info.size); + return ESP_GMF_IO_OK; +} + +static esp_gmf_err_t _codec_dev_seek(esp_gmf_io_handle_t io, uint64_t seek_byte_pos) +{ + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t _codec_dev_close(esp_gmf_io_handle_t io) +{ + codec_dev_io_stream_t *codec_dev_io = (codec_dev_io_stream_t *)io; + esp_gmf_info_file_t info = {0}; + esp_gmf_io_get_info((esp_gmf_io_handle_t)codec_dev_io, &info); + ESP_LOGI(TAG, "CLose, %p, pos = %d/%d", codec_dev_io, (int)info.pos, (int)info.size); + if (codec_dev_io->is_open) { + codec_dev_io->is_open = false; + } + esp_gmf_io_set_pos((esp_gmf_io_handle_t)io, 0); + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t _codec_dev_delete(esp_gmf_io_handle_t io) +{ + if (io != NULL) { + codec_dev_io_stream_t *codec_dev_io = (codec_dev_io_stream_t *)io; + ESP_LOGD(TAG, "Delete, %s-%p", OBJ_GET_TAG(codec_dev_io), codec_dev_io); + esp_gmf_oal_free(OBJ_GET_CFG(codec_dev_io)); + esp_gmf_io_deinit(io); + esp_gmf_oal_free(codec_dev_io); + } + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_io_codec_dev_init(codec_dev_io_cfg_t *config, esp_gmf_io_handle_t *io) +{ + ESP_GMF_NULL_CHECK(TAG, config, {return ESP_GMF_ERR_INVALID_ARG;}); + ESP_GMF_NULL_CHECK(TAG, io, {return ESP_GMF_ERR_INVALID_ARG;}); + *io = NULL; + esp_gmf_err_t ret = ESP_GMF_ERR_OK; + codec_dev_io_stream_t *codec_dev_io = esp_gmf_oal_calloc(1, sizeof(codec_dev_io_stream_t)); + ESP_GMF_MEM_VERIFY(TAG, codec_dev_io, return ESP_GMF_ERR_MEMORY_LACK, + "codec device", sizeof(codec_dev_io_stream_t)); + codec_dev_io->base.dir = config->dir; + codec_dev_io->base.type = ESP_GMF_IO_TYPE_BYTE; + esp_gmf_obj_t *obj = (esp_gmf_obj_t *)codec_dev_io; + obj->new_obj = esp_gmf_io_codec_dev_new; + obj->del_obj = _codec_dev_delete; + codec_dev_io_cfg_t *cfg = esp_gmf_oal_calloc(1, sizeof(*config)); + ESP_GMF_MEM_VERIFY(TAG, cfg, {ret = ESP_GMF_ERR_MEMORY_LACK; goto _codec_dev_fail;}, + "codec device configuration", sizeof(*config)); + memcpy(cfg, config, sizeof(*config)); + esp_gmf_obj_set_config(obj, cfg, sizeof(*config)); + ret = esp_gmf_obj_set_tag(obj, (config->name == NULL ? "io_codec_dev" : config->name)); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto _codec_dev_fail, "Failed to set obj tag"); + codec_dev_io->base.close = _codec_dev_close; + codec_dev_io->base.open = _codec_dev_open; + codec_dev_io->base.seek = _codec_dev_seek; + codec_dev_io->base.reset = NULL; + esp_gmf_io_init(obj, NULL); + if (codec_dev_io->base.dir == ESP_GMF_IO_DIR_WRITER) { + codec_dev_io->base.acquire_write = _codec_dev_acquire_write; + codec_dev_io->base.release_write = _codec_dev_release_write; + } else if (codec_dev_io->base.dir == ESP_GMF_IO_DIR_READER) { + codec_dev_io->base.acquire_read = _codec_dev_acquire_read; + codec_dev_io->base.release_read = _codec_dev_release_read; + } else { + ESP_LOGE(TAG, "Does not set read or write function"); + ret = ESP_GMF_ERR_NOT_SUPPORT; + goto _codec_dev_fail; + } + *io = obj; + ESP_LOGD(TAG, "Initialization, %s-%p", OBJ_GET_TAG(obj), codec_dev_io); + return ESP_GMF_ERR_OK; +_codec_dev_fail: + esp_gmf_obj_delete(obj); + return ret; +} + +esp_gmf_err_t esp_gmf_io_codec_dev_set_dev(esp_gmf_io_handle_t io, esp_codec_dev_handle_t dev) +{ + codec_dev_io_stream_t *codec_dev_io = (codec_dev_io_stream_t *)io; + ESP_GMF_NULL_CHECK(TAG, codec_dev_io, return ESP_GMF_ERR_INVALID_ARG;); + + codec_dev_io_cfg_t *cfg = (codec_dev_io_cfg_t *)OBJ_GET_CFG(codec_dev_io); + ESP_GMF_NULL_CHECK(TAG, cfg, return ESP_GMF_ERR_INVALID_STATE;); + + cfg->dev = dev; + return ESP_GMF_ERR_OK; +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_io/codec_dev_io/esp_gmf_io_codec_dev.h b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_io/codec_dev_io/esp_gmf_io_codec_dev.h new file mode 100644 index 0000000..2bc1b19 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_io/codec_dev_io/esp_gmf_io_codec_dev.h @@ -0,0 +1,60 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#include "esp_codec_dev.h" +#include "esp_gmf_io.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * @brief Audio Codec Device IO configurations, if any entry is zero then the configuration will be set to default values + */ +typedef struct { + esp_codec_dev_handle_t dev; /*!< Audio Codec Device handler */ + esp_gmf_io_dir_t dir; /*!< IO direction, reader or writer */ + const char *name; /*!< Name for this instance */ +} codec_dev_io_cfg_t; + +#define ESP_GMF_IO_CODEC_DEV_CFG_DEFAULT() { \ + .dev = NULL, \ + .dir = ESP_GMF_IO_DIR_NONE, \ + .name = NULL, \ +} + +/** + * @brief Initializes the Audio Codec Device I/O with the provided configuration + * + * @param[in] config Audio codec device io configuration + * @param[out] io Audio codec device io handle + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid configuration provided + * - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory + */ +esp_gmf_err_t esp_gmf_io_codec_dev_init(codec_dev_io_cfg_t *config, esp_gmf_io_handle_t *io); + +/** + * @brief Set or update the audio codec device handle for an existing I/O instance + * + * @param[in] io Audio codec device I/O handle + * @param[in] dev New audio codec device handle to set + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid arguments provided + * - ESP_GMF_ERR_INVALID_STATE Invalid state + */ +esp_gmf_err_t esp_gmf_io_codec_dev_set_dev(esp_gmf_io_handle_t io, esp_codec_dev_handle_t dev); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_io/esp_gmf_io_embed_flash.c b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_io/esp_gmf_io_embed_flash.c new file mode 100644 index 0000000..7a3b30d --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_io/esp_gmf_io_embed_flash.c @@ -0,0 +1,182 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#include "esp_gmf_io_embed_flash.h" +#include "esp_gmf_oal_mem.h" +#include "esp_log.h" + +#define MAX_FILES_INDEX 1000 + +/** + * @brief Embed flash io context in GMF + */ +typedef struct { + esp_gmf_io_t base; /*!< The GMF embed flash io handle */ + int cur; /*!< The current stream pos */ + int max_files; /*!< The max file number */ + embed_item_info_t *items; /*!< The embed flash stream item */ +} embed_flash_io_t; + +static const char *const TAG = "ESP_GMF_EMBED_FLASH"; + +static esp_gmf_err_t _embed_flash_new(void *cfg, esp_gmf_obj_handle_t *io) +{ + return esp_gmf_io_embed_flash_init(cfg, io); +} + +static esp_gmf_err_t _embed_flash_open(esp_gmf_io_handle_t io) +{ + embed_flash_io_t *embed_flash = (embed_flash_io_t *)io; + if (embed_flash->items == NULL) { + ESP_LOGE(TAG, "There is no embedded items, please call embed_flash_io_set_context first"); + return ESP_GMF_ERR_FAIL; + } + char *uri = NULL; + esp_gmf_io_get_uri((esp_gmf_io_handle_t)embed_flash, &uri); + if (uri == NULL) { + ESP_LOGE(TAG, "The file URI is NULL!"); + return ESP_GMF_ERR_FAIL; + } + // The embedded file path like "embed://tone/0_alarm.mp3" + char *temp = strchr(uri, '_'); + if (temp == NULL) { + ESP_LOGE(TAG, "No _ in file name, %s", uri); + return ESP_GMF_ERR_FAIL; + } + *temp = 0; + temp = strrchr(uri, '/'); + if (temp == NULL) { + ESP_LOGE(TAG, "The file name is incorrect!, %s", uri); + return ESP_GMF_ERR_FAIL; + } + temp++; + int file_index = strtoul(temp, 0, 10); + ESP_LOGI(TAG, "The read item is %d, %s", file_index, uri); + if (embed_flash->max_files < file_index) { + ESP_LOGE(TAG, "The file index is out of range, %d", file_index); + return ESP_GMF_ERR_FAIL; + } + + embed_flash->cur = file_index; + uint64_t total_bytes = embed_flash->items[embed_flash->cur].size; + esp_gmf_io_set_size(io, total_bytes); + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_io_t _embed_flash_acquire_read(esp_gmf_io_handle_t io, void *payload, uint32_t wanted_size, int block_ticks) +{ + embed_flash_io_t *embed_flash = (embed_flash_io_t *)io; + esp_gmf_payload_t *pload = (esp_gmf_payload_t *)payload; + esp_gmf_info_file_t info = {0}; + esp_gmf_io_get_info((esp_gmf_io_handle_t)embed_flash, &info); + if ((info.pos + wanted_size) > info.size) { + wanted_size = info.size - info.pos; + } + ESP_LOGD(TAG, "Embed read data, ret:%ld, pos: %llu/%llu", wanted_size, info.pos, info.size); + if (wanted_size == 0) { + ESP_LOGW(TAG, "No more data, ret:%ld, pos: %llu/%llu", wanted_size, info.pos, info.size); + pload->is_done = true; + } + memcpy(pload->buf, embed_flash->items[embed_flash->cur].address + info.pos, wanted_size); + pload->valid_size = wanted_size; + return ESP_GMF_IO_OK; +} + +static esp_gmf_err_io_t _embed_flash_release_read(esp_gmf_io_handle_t io, void *payload, int block_ticks) +{ + embed_flash_io_t *embed_flash = (embed_flash_io_t *)io; + esp_gmf_payload_t *pload = (esp_gmf_payload_t *)payload; + esp_gmf_info_file_t info = {0}; + esp_gmf_io_get_info((esp_gmf_io_handle_t)embed_flash, &info); + ESP_LOGD(TAG, "Update len = %d, pos = %d/%d", pload->valid_size, (int)info.pos, (int)info.size); + esp_gmf_io_update_pos((esp_gmf_io_handle_t)embed_flash, pload->valid_size); + return ESP_GMF_IO_OK; +} + +static esp_gmf_err_t _embed_flash_close(esp_gmf_io_handle_t io) +{ + esp_gmf_info_file_t info = {0}; + esp_gmf_io_get_info((esp_gmf_io_handle_t)io, &info); + ESP_LOGI(TAG, "Closed, pos: %llu/%llu", info.pos, info.size); + esp_gmf_io_set_pos(io, 0); + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t _embed_flash_destroy(esp_gmf_io_handle_t io) +{ + embed_flash_io_t *embed_flash = (embed_flash_io_t *)io; + ESP_LOGD(TAG, "Delete, %s-%p", OBJ_GET_TAG(embed_flash), embed_flash); + void *cfg = OBJ_GET_CFG(io); + if (cfg) { + esp_gmf_oal_free(cfg); + } + if (embed_flash->items) { + esp_gmf_oal_free(embed_flash->items); + } + esp_gmf_io_deinit(io); + esp_gmf_oal_free(embed_flash); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_io_embed_flash_init(embed_flash_io_cfg_t *config, esp_gmf_io_handle_t *io) +{ + ESP_GMF_NULL_CHECK(TAG, config, return ESP_GMF_ERR_INVALID_ARG;); + ESP_GMF_NULL_CHECK(TAG, io, return ESP_GMF_ERR_INVALID_ARG;); + *io = NULL; + esp_gmf_err_t ret = ESP_GMF_ERR_OK; + embed_flash_io_t *embed_flash = esp_gmf_oal_calloc(1, sizeof(embed_flash_io_t)); + ESP_GMF_MEM_VERIFY(TAG, embed_flash, return ESP_GMF_ERR_MEMORY_LACK, + "embed flash stream", sizeof(embed_flash_io_t)); + embed_flash->base.dir = ESP_GMF_IO_DIR_READER; + embed_flash->base.type = ESP_GMF_IO_TYPE_BYTE; + embed_flash->max_files = config->max_files; + esp_gmf_obj_t *obj = (esp_gmf_obj_t *)embed_flash; + obj->new_obj = _embed_flash_new; + obj->del_obj = _embed_flash_destroy; + embed_flash_io_cfg_t *cfg = esp_gmf_oal_calloc(1, sizeof(*config)); + ESP_GMF_MEM_VERIFY(TAG, cfg, {ret = ESP_GMF_ERR_MEMORY_LACK; goto _embed_fail;}, + "embed flash stream configuration", sizeof(*config)); + memcpy(cfg, config, sizeof(*config)); + esp_gmf_obj_set_config(obj, cfg, sizeof(*config)); + ret = esp_gmf_obj_set_tag(obj, (config->name == NULL ? "io_embed_flash" : config->name)); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto _embed_fail, "Failed to set obj tag"); + embed_flash->base.open = _embed_flash_open; + embed_flash->base.close = _embed_flash_close; + embed_flash->base.seek = NULL; + embed_flash->base.reset = NULL; + esp_gmf_io_init(obj, NULL); + if (embed_flash->base.dir == ESP_GMF_IO_DIR_READER) { + embed_flash->base.acquire_read = _embed_flash_acquire_read; + embed_flash->base.release_read = _embed_flash_release_read; + } else { + ESP_LOGE(TAG, "Does not support this operation, %d", embed_flash->base.dir); + ret = ESP_GMF_ERR_NOT_SUPPORT; + goto _embed_fail; + } + *io = obj; + ESP_LOGD(TAG, "Initialization, %s-%p", OBJ_GET_TAG(obj), embed_flash); + return ESP_GMF_ERR_OK; +_embed_fail: + esp_gmf_obj_delete(obj); + return ret; +} + +esp_gmf_err_t esp_gmf_io_embed_flash_set_context(esp_gmf_io_handle_t io, const embed_item_info_t *context, int max_num) +{ + ESP_GMF_NULL_CHECK(TAG, io, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, context, return ESP_GMF_ERR_INVALID_ARG); + embed_flash_io_t *embed_flash = (embed_flash_io_t *)io; + if (embed_flash->items) { + esp_gmf_oal_free(embed_flash->items); + } + embed_flash->items = esp_gmf_oal_calloc(max_num, sizeof(embed_item_info_t)); + ESP_GMF_MEM_VERIFY(TAG, embed_flash->items, return ESP_GMF_ERR_MEMORY_LACK, + "embed item information", sizeof(embed_item_info_t)); + embed_flash->max_files = max_num; + memcpy(embed_flash->items, context, sizeof(embed_item_info_t) * max_num); + return ESP_GMF_ERR_OK; +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_io/esp_gmf_io_file.c b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_io/esp_gmf_io_file.c new file mode 100644 index 0000000..0f89e0f --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_io/esp_gmf_io_file.c @@ -0,0 +1,298 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#include +#include +#include +#include +#include "errno.h" + +#include "esp_heap_caps.h" +#include "esp_gmf_io_file.h" +#include "esp_gmf_oal_mem.h" +#include "fcntl.h" +#include "esp_log.h" + +#define IO_FILE_ALIGN_UP(num, align) (((num) + ((align) - 1)) & ~((align) - 1)) +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define IO_FILE_DEFAULT_CACHE_ALIGN (512) + +/** + * @brief File io context in GMF + */ +typedef struct { + esp_gmf_io_t base; /*!< The GMF file io handle */ + bool is_open; /*!< The flag of whether opened */ + FILE *file; /*!< The handle of file stream */ + uint8_t *cache; /*!< The cache buffer */ + int cache_size; /*!< The cache size */ + int cache_caps; /*!< The cache memory capabilities */ +} file_io_stream_t; + +static const char *TAG = "ESP_GMF_FILE"; + +static char *get_mount_path(char *uri) +{ + /* support format: /sdcard, /spiffs, /storage etc ... */ + if (uri[0] == '/') { + return uri; + } + /* support format: scheme://basepath... */ + char *skip_scheme = strstr(uri, "://"); + if (skip_scheme == NULL) { + return NULL; + } + skip_scheme += 2; + /* support format: scheme:///basepath... */ + if (skip_scheme[1] == '/') { + skip_scheme++; + } + return skip_scheme; +} + +static esp_gmf_err_t _file_new(void *cfg, esp_gmf_obj_handle_t *io) +{ + return esp_gmf_io_file_init(cfg, io); +} + +static esp_gmf_err_t _file_open(esp_gmf_io_handle_t io) +{ + file_io_stream_t *file_io = (file_io_stream_t *)io; + char *uri = NULL; + esp_gmf_io_get_uri((esp_gmf_io_handle_t)file_io, &uri); + if (uri == NULL) { + ESP_LOGE(TAG, "Error, uri is not set, handle: %p", io); + return ESP_GMF_ERR_FAIL; + } + ESP_LOGI(TAG, "Open, dir:%d, uri:%s", ((file_io_cfg_t *)file_io->base.parent.cfg)->dir, uri); + char *path = get_mount_path(uri); + if (path == NULL) { + ESP_LOGE(TAG, "Invalid URI (%s).", uri); + return ESP_GMF_ERR_FAIL; + } + if (file_io->is_open) { + ESP_LOGE(TAG, "Already opened, p: %p, path: %s", file_io, path); + return ESP_GMF_ERR_FAIL; + } + if (((file_io_cfg_t *)file_io->base.parent.cfg)->dir == ESP_GMF_IO_DIR_READER) { + file_io->file = fopen(path, "rb"); + if (file_io->file == NULL) { + ESP_LOGE(TAG, "Failed to open on read, path: %s, err: %s", path, strerror(errno)); + return ESP_GMF_ERR_FAIL; + } + struct stat sz = {0}; + stat(path, &sz); + esp_gmf_io_set_size((esp_gmf_io_handle_t)file_io, sz.st_size); + esp_gmf_info_file_t info = {0}; + esp_gmf_io_get_info((esp_gmf_io_handle_t)file_io, &info); + ESP_LOGI(TAG, "File size: %d byte, file position: %lld", (int)sz.st_size, info.pos); + if (info.pos > 0) { + if (fseek(file_io->file, info.pos, SEEK_SET) != 0) { + ESP_LOGE(TAG, "Seek to %lld failed, err: %s", info.pos, strerror(errno)); + return ESP_GMF_ERR_FAIL; + } + } + } else if (((file_io_cfg_t *)file_io->base.parent.cfg)->dir == ESP_GMF_IO_DIR_WRITER) { + file_io->file = fopen(path, "wb"); + if (file_io->file == NULL) { + ESP_LOGE(TAG, "Failed to open on write, path: %s, err: %s", path, strerror(errno)); + return ESP_GMF_ERR_FAIL; + } + } else { + ESP_LOGE(TAG, "The type must be reader or writer"); + return ESP_GMF_ERR_FAIL; + } + if (file_io->cache_size > 0) { + file_io->cache = heap_caps_malloc(file_io->cache_size, file_io->cache_caps); + ESP_GMF_MEM_VERIFY(TAG, file_io->cache, { fclose(file_io->file); return ESP_GMF_ERR_MEMORY_LACK;}, + "file stream cache", file_io->cache_size); + setvbuf(file_io->file, (char *)file_io->cache, _IOFBF, file_io->cache_size); + ESP_LOGD(TAG, "File_io cache: %p, size: %d, caps: 0x%x", file_io->cache, file_io->cache_size, file_io->cache_caps); + } + + file_io->is_open = true; + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_io_t _file_acquire_read(esp_gmf_io_handle_t handle, void *payload, uint32_t wanted_size, int block_ticks) +{ + file_io_stream_t *file_io = (file_io_stream_t *)handle; + esp_gmf_payload_t *pload = (esp_gmf_payload_t *)payload; + size_t rlen = fread(pload->buf, 1, wanted_size, file_io->file); + ESP_LOGD(TAG, "Read len: %zu-%ld", rlen, wanted_size); + if (rlen == 0 && ferror(file_io->file)) { + ESP_LOGE(TAG, "The error is happened in reading data, error msg: %s", strerror(errno)); + return ESP_GMF_IO_FAIL; + } + pload->valid_size = rlen; + if (rlen < wanted_size) { + rlen = fread(pload->buf + rlen, 1, (wanted_size - rlen), file_io->file); + if (rlen == 0) { + pload->is_done = true; + ESP_LOGI(TAG, "No more data, ret: %zu", rlen); + return ESP_GMF_IO_OK; + } + pload->valid_size += rlen; + } + return ESP_GMF_IO_OK; +} + +static esp_gmf_err_io_t _file_release_read(esp_gmf_io_handle_t handle, void *payload, int block_ticks) +{ + file_io_stream_t *file_io = (file_io_stream_t *)handle; + esp_gmf_payload_t *pload = (esp_gmf_payload_t *)payload; + esp_gmf_info_file_t info = {0}; + esp_gmf_io_get_info((esp_gmf_io_handle_t)file_io, &info); + ESP_LOGD(TAG, "Update len = %d, pos = %d/%d", pload->valid_size, (int)info.pos, (int)info.size); + esp_gmf_io_update_pos((esp_gmf_io_handle_t)handle, pload->valid_size); + return ESP_GMF_IO_OK; +} + +static esp_gmf_err_io_t _file_acquire_write(esp_gmf_io_handle_t handle, void *payload, uint32_t wanted_size, int block_ticks) +{ + return ESP_GMF_IO_OK; +} + +static esp_gmf_err_io_t _file_release_write(esp_gmf_io_handle_t handle, void *payload, int block_ticks) +{ + file_io_stream_t *file_io = (file_io_stream_t *)handle; + esp_gmf_payload_t *pload = (esp_gmf_payload_t *)payload; + size_t wlen_total = 0; + uint8_t *pbuf = pload->buf; + if (file_io->cache_size > 0) { + while (pload->valid_size > wlen_total) { + int size = MIN(pload->valid_size - wlen_total, file_io->cache_size - 1); + size_t wlen = fwrite(pbuf, 1, size, file_io->file); + if (wlen == 0) { + break; + } + pbuf += wlen; + wlen_total += wlen; + } + } else { + wlen_total = fwrite(pload->buf, 1, pload->valid_size, file_io->file); + } + if (wlen_total != pload->valid_size) { + ESP_LOGE(TAG, "The error is happened in writing data, error msg:%s", strerror(errno)); + return ESP_GMF_IO_FAIL; + } + esp_gmf_info_file_t info = {0}; + esp_gmf_io_get_info((esp_gmf_io_handle_t)file_io, &info); + ESP_LOGD(TAG, "Write len = %zu, pos = %d/%d", wlen_total, (int)info.pos, (int)info.size); + esp_gmf_io_update_pos((esp_gmf_io_handle_t)handle, wlen_total); + return ESP_GMF_IO_OK; +} + +static esp_gmf_err_t _file_seek(esp_gmf_io_handle_t io, uint64_t seek_byte_pos) +{ + file_io_stream_t *file_io = (file_io_stream_t *)io; + esp_gmf_info_file_t info = {0}; + esp_gmf_io_get_info((esp_gmf_io_handle_t)file_io, &info); + ESP_LOGI(TAG, "Seek position, total_bytes: %lld, seek: %lld", + info.size, seek_byte_pos); + if (seek_byte_pos > info.size) { + ESP_LOGE(TAG, "Seek position is out of range, total_bytes: %lld, seek: %lld", + info.size, seek_byte_pos); + return ESP_GMF_ERR_OUT_OF_RANGE; + } + if (fseek(file_io->file, seek_byte_pos, SEEK_SET) != 0) { + ESP_LOGE(TAG, "Error seek file, error message: %s, line: %d", strerror(errno), __LINE__); + return ESP_GMF_ERR_FAIL; + } + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t _file_reset(esp_gmf_io_handle_t io) +{ + file_io_stream_t *file_io = (file_io_stream_t *)io; + if (file_io->file != NULL) { + fseek(file_io->file, 0, SEEK_SET); + } + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t _file_close(esp_gmf_io_handle_t io) +{ + file_io_stream_t *file_io = (file_io_stream_t *)io; + esp_gmf_info_file_t info = {0}; + esp_gmf_io_get_info((esp_gmf_io_handle_t)file_io, &info); + ESP_LOGI(TAG, "CLose, %p, pos = %d/%d", file_io, (int)info.pos, (int)info.size); + if (file_io->is_open) { + fclose(file_io->file); + file_io->file = NULL; + file_io->is_open = false; + } + esp_gmf_io_set_pos((esp_gmf_io_handle_t)io, 0); + if (file_io->cache) { + esp_gmf_oal_free(file_io->cache); + file_io->cache = NULL; + } + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t _file_delete(esp_gmf_io_handle_t io) +{ + file_io_stream_t *file_io = (file_io_stream_t *)io; + ESP_LOGD(TAG, "Delete, %s-%p", OBJ_GET_TAG(file_io), file_io); + void *cfg = OBJ_GET_CFG(io); + if (cfg) { + esp_gmf_oal_free(cfg); + } + esp_gmf_io_deinit(io); + esp_gmf_oal_free(file_io); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_io_file_init(file_io_cfg_t *config, esp_gmf_io_handle_t *io) +{ + ESP_GMF_NULL_CHECK(TAG, config, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, io, return ESP_GMF_ERR_INVALID_ARG); + *io = NULL; + esp_gmf_err_t ret = ESP_GMF_ERR_OK; + file_io_stream_t *file_io = esp_gmf_oal_calloc(1, sizeof(file_io_stream_t)); + ESP_GMF_MEM_VERIFY(TAG, file_io, return ESP_GMF_ERR_MEMORY_LACK, + "file stream", sizeof(file_io_stream_t)); + file_io->base.dir = config->dir; + file_io->base.type = ESP_GMF_IO_TYPE_BYTE; + esp_gmf_obj_t *obj = (esp_gmf_obj_t *)file_io; + obj->new_obj = _file_new; + obj->del_obj = _file_delete; + file_io_cfg_t *cfg = esp_gmf_oal_calloc(1, sizeof(*config)); + ESP_GMF_MEM_VERIFY(TAG, cfg, {ret = ESP_GMF_ERR_MEMORY_LACK; goto _file_fail;}, "file_io cfg", sizeof(*config)); + memcpy(cfg, config, sizeof(*config)); + esp_gmf_obj_set_config(obj, cfg, sizeof(*config)); + ret = esp_gmf_obj_set_tag(obj, (config->name == NULL ? "io_file" : config->name)); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto _file_fail, "Failed to set obj tag"); + file_io->base.close = _file_close; + file_io->base.open = _file_open; + file_io->base.seek = _file_seek; + file_io->base.reset = _file_reset; + file_io_cfg_t *fat_cfg = (file_io_cfg_t *)config; + esp_gmf_io_init(obj, NULL); + if (fat_cfg->dir == ESP_GMF_IO_DIR_WRITER) { + file_io->base.acquire_write = _file_acquire_write; + file_io->base.release_write = _file_release_write; + } else if (fat_cfg->dir == ESP_GMF_IO_DIR_READER) { + file_io->base.acquire_read = _file_acquire_read; + file_io->base.release_read = _file_release_read; + } else { + ESP_LOGW(TAG, "Does not set read or write function"); + ret = ESP_GMF_ERR_NOT_SUPPORT; + goto _file_fail; + } + *io = obj; + if (cfg->cache_size <= IO_FILE_DEFAULT_CACHE_ALIGN) { + file_io->cache_size = 0; + } else { + file_io->cache_caps = (cfg->cache_caps == 0) ? (MALLOC_CAP_DMA) : cfg->cache_caps; + file_io->cache_size = IO_FILE_ALIGN_UP(cfg->cache_size, IO_FILE_DEFAULT_CACHE_ALIGN); + } + return ESP_GMF_ERR_OK; +_file_fail: + esp_gmf_obj_delete(obj); + return ret; +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_io/esp_gmf_io_http.c b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_io/esp_gmf_io_http.c new file mode 100644 index 0000000..5c16f42 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_io/esp_gmf_io_http.c @@ -0,0 +1,534 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#include +#include +#include "esp_log.h" +#include "esp_http_client.h" +#include "gzip_miniz.h" + +#include "esp_gmf_new_databus.h" +#include "esp_gmf_io_http.h" +#include "esp_gmf_oal_mem.h" + +#define HTTP_STREAM_BUFFER_SIZE (3 * 1024) +#define HTTP_MAX_CONNECT_TIMES (5) + +/** + * @brief Http io context in GMF + */ +typedef struct http_io { + esp_gmf_io_t base; /*!< The GMF http io handle */ + bool is_open; /*!< The flag of whether opened */ + esp_http_client_handle_t client; /*!< The http client handle */ + int _errno; /*!< Errno code for http */ + int connect_times; /*!< Max reconnect times */ + bool gzip_encoding; /*!< Content is encoded */ + gzip_miniz_handle_t gzip; /*!< GZIP instance */ + esp_gmf_db_handle_t data_bus; /*!< The data bus handle */ +} http_stream_t; + +static const char *TAG = "ESP_GMF_HTTP"; + +static esp_gmf_err_t _http_destroy(esp_gmf_io_handle_t self); + +static int _gzip_read_data(uint8_t *data, int size, void *ctx) +{ + http_stream_t *http = (http_stream_t *)ctx; + return esp_http_client_read(http->client, (char *)data, size); +} + +static esp_gmf_err_t _http_event_handle(esp_http_client_event_t *evt) +{ + http_stream_t *http = (http_stream_t *)evt->user_data; + if (evt->event_id != HTTP_EVENT_ON_HEADER) { + return ESP_GMF_ERR_OK; + } + if (strcasecmp(evt->header_key, "Content-Encoding") == 0) { + http->gzip_encoding = true; + if (strcasecmp(evt->header_value, "gzip") == 0) { + gzip_miniz_cfg_t cfg = { + .chunk_size = 1024, + .ctx = http, + .read_cb = _gzip_read_data, + }; + http->gzip = gzip_miniz_init(&cfg); + } + if (http->gzip == NULL) { + ESP_LOGE(TAG, "Content-Encoding %s not supported", evt->header_value); + return ESP_GMF_ERR_FAIL; + } + } + return ESP_GMF_ERR_OK; +} + +static int dispatch_hook(esp_gmf_io_handle_t self, http_stream_event_id_t type, void *buffer, int buffer_len) +{ + http_stream_t *http_stream = (http_stream_t *)self; + http_io_cfg_t *http_io_cfg = (http_io_cfg_t *)OBJ_GET_CFG(http_stream); + http_stream_event_msg_t msg; + msg.event_id = type; + msg.http_client = http_stream->client; + msg.user_data = http_io_cfg->user_data; + msg.buffer = buffer; + msg.buffer_len = buffer_len; + if (http_io_cfg->event_handle) { + return http_io_cfg->event_handle(&msg); + } + return ESP_GMF_ERR_OK; +} + +static int _http_read_data(http_stream_t *http, char *buffer, int len) +{ + if (http->gzip_encoding == false) { + return esp_http_client_read(http->client, buffer, len); + } + // use gzip to uncompress data + return gzip_miniz_read(http->gzip, (uint8_t *)buffer, len); +} + +static esp_gmf_err_t _http_new(void *cfg, esp_gmf_obj_handle_t *io) +{ + return esp_gmf_io_http_init(cfg, io); +} + +static esp_gmf_err_t _http_open(esp_gmf_io_handle_t self) +{ + http_stream_t *http = (http_stream_t *)self; + esp_gmf_err_t err; + if (http->is_open) { + ESP_LOGW(TAG, "The HTTP already opened, %p", http); + return ESP_GMF_ERR_OK; + } + http->_errno = 0; + esp_gmf_info_file_t info = {0}; + esp_gmf_io_get_info((esp_gmf_io_handle_t)http, &info); + char *uri = (char *)info.uri; + if (uri == NULL) { + ESP_LOGE(TAG, "Error open connection, uri = NULL"); + return ESP_GMF_ERR_FAIL; + } + char *hls_type = strrchr(uri, '/'); + if (hls_type && strstr(hls_type, ".m3u")) { + ESP_LOGE(TAG, "The HTTP stream does not support HTTP Live Streaming. URI:%s", uri); + return ESP_GMF_ERR_FAIL; + } + + esp_gmf_io_get_info((esp_gmf_io_handle_t)http, &info); + http_io_cfg_t *http_io_cfg = (http_io_cfg_t *)OBJ_GET_CFG(http); + ESP_LOGI(TAG, "HTTP Open, URI = %s", uri); + if (http->client == NULL) { + esp_http_client_config_t http_cfg = { + .url = uri, + .event_handler = _http_event_handle, + .user_data = self, + .timeout_ms = 30 * 1000, + .buffer_size = HTTP_STREAM_BUFFER_SIZE, + .buffer_size_tx = 1024, + .cert_pem = http_io_cfg->cert_pem, +#ifdef CONFIG_MBEDTLS_CERTIFICATE_BUNDLE + .crt_bundle_attach = http_io_cfg->crt_bundle_attach, +#endif /* CONFIG_MBEDTLS_CERTIFICATE_BUNDLE */ + }; + http->client = esp_http_client_init(&http_cfg); + ESP_GMF_CHECK(TAG, http->client, return ESP_GMF_ERR_MEMORY_LACK, "Failed to initialize http client"); + } else { + esp_http_client_set_url(http->client, uri); + } + + if (info.pos) { + char rang_header[32]; + snprintf(rang_header, 32, "bytes=%d-", (int)info.pos); + esp_http_client_set_header(http->client, "Range", rang_header); + } else { + esp_http_client_delete_header(http->client, "Range"); + } + + if (dispatch_hook(self, HTTP_STREAM_PRE_REQUEST, NULL, 0) != ESP_GMF_ERR_OK) { + ESP_LOGE(TAG, "Failed to process user callback:%d", __LINE__); + return ESP_GMF_ERR_FAIL; + } + + if (http->data_bus == NULL) { + err = esp_gmf_db_new_block(1, http_io_cfg->out_buf_size, &http->data_bus); + if (err != ESP_GMF_ERR_OK) { + ESP_LOGE(TAG, "Failed to create the buffer for %d, sz: %d, %s-%p", http_io_cfg->dir, http_io_cfg->out_buf_size, OBJ_GET_TAG(http), http); + return err; + } + esp_gmf_data_bus_type_t db_type = 0; + esp_gmf_db_get_type(http->data_bus, &db_type); + http->base.type = db_type; + } + + if (http_io_cfg->dir == ESP_GMF_IO_DIR_WRITER) { + err = esp_http_client_open(http->client, -1); + if (err == ESP_GMF_ERR_OK) { + http->is_open = true; + } + return err; + } + + char *buffer = NULL; + int post_len = esp_http_client_get_post_field(http->client, &buffer); +_stream_redirect: + if (http->gzip_encoding) { + gzip_miniz_deinit(http->gzip); + http->gzip = NULL; + http->gzip_encoding = false; + } + if ((err = esp_http_client_open(http->client, post_len)) != ESP_GMF_ERR_OK) { + ESP_LOGE(TAG, "Failed to open http stream"); + return err; + } + + int wrlen = dispatch_hook(self, HTTP_STREAM_ON_REQUEST, buffer, post_len); + if (wrlen < 0) { + ESP_LOGE(TAG, "Failed to process user callback:%d", __LINE__); + return ESP_GMF_ERR_FAIL; + } + if (post_len && buffer && wrlen == 0) { + if (esp_http_client_write(http->client, buffer, post_len) <= 0) { + ESP_LOGE(TAG, "Failed to write data to http stream"); + return ESP_GMF_ERR_FAIL; + } + ESP_LOGD(TAG, "len=%d, data=%s", post_len, buffer); + } + + if (dispatch_hook(self, HTTP_STREAM_POST_REQUEST, NULL, 0) < 0) { + esp_http_client_close(http->client); + return ESP_GMF_ERR_FAIL; + } + /** + * Due to the total byte of content has been changed after seek, set info.total_bytes at beginning only. + */ + int64_t cur_pos = esp_http_client_fetch_headers(http->client); + esp_gmf_io_get_info((esp_gmf_io_handle_t)http, &info); + if (info.pos <= 0) { + info.size = cur_pos; + } + + ESP_LOGI(TAG, "The total size is %d bytes", (int)info.size); + int status_code = esp_http_client_get_status_code(http->client); + if (status_code == 301 || status_code == 302) { + esp_http_client_set_redirection(http->client); + goto _stream_redirect; + } + if (status_code != 200 + && (esp_http_client_get_status_code(http->client) != 206)) { + ESP_LOGE(TAG, "Invalid HTTP stream, status code = %d", status_code); + return ESP_GMF_ERR_FAIL; + } + esp_gmf_io_set_size(self, info.size); + + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t _http_prev_close(esp_gmf_io_handle_t self) +{ + http_stream_t *http = (http_stream_t *)self; + esp_gmf_db_abort(http->data_bus); + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t _http_close(esp_gmf_io_handle_t self) +{ + http_stream_t *http = (http_stream_t *)self; + ESP_LOGD(TAG, "_http_close, %p", http); + http_io_cfg_t *http_io_cfg = (http_io_cfg_t *)OBJ_GET_CFG(http); + if (http->is_open && (http_io_cfg->dir == ESP_GMF_IO_DIR_WRITER)) { + do { + if (dispatch_hook(self, HTTP_STREAM_POST_REQUEST, NULL, 0) < 0) { + break; + } + if (esp_http_client_fetch_headers(http->client) < 0) { + break; + } + if (dispatch_hook(self, HTTP_STREAM_FINISH_REQUEST, NULL, 0) < 0) { + break; + } + } while (0); + } + http->is_open = false; + if (http->gzip) { + gzip_miniz_deinit(http->gzip); + http->gzip = NULL; + } + if (http->client) { + esp_http_client_close(http->client); + esp_http_client_cleanup(http->client); + http->client = NULL; + } + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t _http_reconnect(esp_gmf_io_handle_t self) +{ + esp_gmf_err_t err = ESP_GMF_ERR_OK; + ESP_GMF_NULL_CHECK(TAG, self, return ESP_GMF_ERR_FAIL); + esp_gmf_info_file_t info = {0}; + err |= esp_gmf_io_get_info((esp_gmf_io_handle_t)self, &info); + err |= _http_close(self); + err |= esp_gmf_io_update_pos(self, info.pos); + err |= _http_open(self); + return err; +} + +static int _http_read(esp_gmf_io_handle_t self, char *buffer, int len, TickType_t ticks_to_wait, void *context) +{ + http_stream_t *http = (http_stream_t *)self; + esp_gmf_info_file_t info = {0}; + esp_gmf_io_get_info((esp_gmf_io_handle_t)http, &info); + int wrlen = dispatch_hook(self, HTTP_STREAM_ON_RESPONSE, buffer, len); + int rlen = wrlen; + if (rlen == 0) { + rlen = _http_read_data(http, buffer, len); + } + if (rlen <= 0) { + http->_errno = esp_http_client_get_errno(http->client); + ESP_LOGW(TAG, "No more data, errno: %d, read bytes: %llu, rlen = %d", http->_errno, info.pos, rlen); + if (http->_errno != 0) { // Error occuered, reset connection + ESP_LOGW(TAG, "Got %d errno(%s)", http->_errno, strerror(http->_errno)); + return http->_errno; + } + return ESP_GMF_ERR_OK; + } else { + esp_gmf_io_update_pos(self, rlen); + } + ESP_LOGD(TAG, "req length = %d, read = %d, pos = %d/%d", len, rlen, (int)info.pos, (int)info.size); + return rlen; +} + +static int _http_write(esp_gmf_io_handle_t self, char *buffer, int len, TickType_t ticks_to_wait, void *context) +{ + http_stream_t *http = (http_stream_t *)self; + int wrlen = dispatch_hook(self, HTTP_STREAM_ON_REQUEST, buffer, len); + if (wrlen < 0) { + ESP_LOGE(TAG, "Failed to process user callback:%d", __LINE__); + return ESP_GMF_ERR_FAIL; + } + if (wrlen > 0) { + return wrlen; + } + if ((wrlen = esp_http_client_write(http->client, buffer, len)) <= 0) { + http->_errno = esp_http_client_get_errno(http->client); + ESP_LOGE(TAG, "Failed to write data to http stream, wrlen = %d, errno = %d(%s)", wrlen, http->_errno, strerror(http->_errno)); + } + return wrlen; +} + +static esp_gmf_job_err_t _http_process(esp_gmf_io_handle_t self, void *params) +{ + int r_size = 0; + http_stream_t *http = (http_stream_t *)self; + esp_gmf_data_bus_block_t blk = {0}; + esp_gmf_job_err_t job_err = ESP_GMF_JOB_ERR_OK; + http->is_open = true; + http_io_cfg_t *http_io_cfg = (http_io_cfg_t *)OBJ_GET_CFG(http); + if (http_io_cfg->dir == ESP_GMF_IO_DIR_READER) { + esp_gmf_db_acquire_write(http->data_bus, &blk, HTTP_STREAM_BUFFER_SIZE, portMAX_DELAY); + r_size = _http_read(self, (char *)blk.buf, blk.buf_length, portMAX_DELAY, NULL); + blk.valid_size = r_size; + ESP_LOGD(TAG, "Read: %d, len: %d", r_size, blk.buf_length); + if (r_size > 0) { + if (http->_errno != 0) { + esp_gmf_err_t ret = ESP_GMF_ERR_OK; + if (http->connect_times > HTTP_MAX_CONNECT_TIMES) { + ESP_LOGE(TAG, "Reconnect times more than %d, disconnect http stream", HTTP_MAX_CONNECT_TIMES); + return ESP_GMF_ERR_FAIL; + }; + http->connect_times++; + ret = _http_reconnect(self); + if (ret != ESP_GMF_ERR_OK) { + ESP_LOGE(TAG, "Failed to reset connection"); + return ret; + } + ESP_LOGW(TAG, "Reconnect to peer successful"); + return ESP_GMF_ERR_INVALID_STATE; + } else { + http->connect_times = 0; + if (http_io_cfg->dir == ESP_GMF_IO_DIR_READER) { + esp_gmf_db_release_write(http->data_bus, &blk, portMAX_DELAY); + } + } + } else if (r_size == 0) { + esp_gmf_db_done_write(http->data_bus); + esp_gmf_db_release_write(http->data_bus, &blk, portMAX_DELAY); + job_err = ESP_GMF_JOB_ERR_DONE; + } else { + job_err = r_size; + esp_gmf_db_abort(http->data_bus); + } + } else { + r_size = esp_gmf_db_acquire_read(http->data_bus, &blk, HTTP_STREAM_BUFFER_SIZE, portMAX_DELAY); + ESP_LOGD(TAG, "ACQ, read: %d, vld: %d, buf_len: %d", r_size, blk.valid_size, blk.buf_length); + if (blk.valid_size > 0) { + int w_size = _http_write(self, (char *)blk.buf, blk.valid_size, portMAX_DELAY, NULL); + if (w_size <= 0) { + job_err = ESP_GMF_JOB_ERR_FAIL; + } + } else if (r_size == ESP_GMF_IO_OK || r_size == ESP_GMF_IO_ABORT) { + job_err = ESP_GMF_JOB_ERR_DONE; + } else { + job_err = r_size; + } + esp_gmf_db_release_read(http->data_bus, &blk, portMAX_DELAY); + } + return job_err; +} + +static esp_gmf_err_t _http_destroy(esp_gmf_io_handle_t self) +{ + http_stream_t *http = (http_stream_t *)self; + ESP_LOGD(TAG, "%s-%p", __FUNCTION__, http); + if (http->data_bus) { + esp_gmf_db_deinit(http->data_bus); + http->data_bus = NULL; + } + void *cfg = OBJ_GET_CFG(self); + if (cfg) { + esp_gmf_oal_free(cfg); + } + esp_gmf_io_deinit(http); + esp_gmf_oal_free(http); + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t _http_seek(esp_gmf_io_handle_t handle, uint64_t pos) +{ + http_stream_t *http = (http_stream_t *)handle; + esp_gmf_info_file_t info = {0}; + esp_gmf_io_get_info((esp_gmf_io_handle_t)handle, &info); + if (pos > info.size) { + ESP_LOGE(TAG, "The seek position is out of range, pos %llu > %llu, http: %p", pos, info.size, http); + return ESP_GMF_ERR_OUT_OF_RANGE; + } + ESP_LOGD(TAG, "HTTP Seek to: %lld, %p", pos, http); + _http_close(handle); + + esp_gmf_io_set_pos(http, pos); + esp_gmf_db_reset(http->data_bus); + _http_open(handle); + + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_io_t _http_acquire_read(esp_gmf_io_handle_t handle, void *payload, uint32_t wanted_size, int block_ticks) +{ + http_stream_t *http = (http_stream_t *)handle; + esp_gmf_data_bus_block_t *blk = (esp_gmf_data_bus_block_t *)payload; + esp_gmf_err_io_t ret = esp_gmf_db_acquire_read(http->data_bus, payload, wanted_size, block_ticks); + ESP_LOGD(TAG, "acq_rd: %ld, vld: %d, done: %d, %p, %d", wanted_size, blk->valid_size, blk->is_last, blk->buf, blk->buf_length); + return ret; +} + +static esp_gmf_err_io_t _http_release_read(esp_gmf_io_handle_t handle, void *payload, int block_ticks) +{ + http_stream_t *http = (http_stream_t *)handle; + esp_gmf_data_bus_block_t *blk = (esp_gmf_data_bus_block_t *)payload; + ESP_LOGD(TAG, "rel_rd: %p, vld: %d, len: %d done: %d", blk->buf, blk->valid_size, blk->buf_length, blk->is_last); + return esp_gmf_db_release_read(http->data_bus, payload, block_ticks); +} + +static esp_gmf_err_io_t _http_acquire_write(esp_gmf_io_handle_t handle, void *payload, uint32_t wanted_size, int block_ticks) +{ + http_stream_t *http = (http_stream_t *)handle; + esp_gmf_data_bus_block_t *blk = (esp_gmf_data_bus_block_t *)payload; + ESP_LOGD(TAG, "acq_wr: %ld, vld: %d, done: %d, %p, %d", wanted_size, blk->valid_size, blk->is_last, blk->buf, blk->buf_length); + return esp_gmf_db_acquire_write(http->data_bus, payload, wanted_size, block_ticks); +} + +static esp_gmf_err_io_t _http_release_write(esp_gmf_io_handle_t handle, void *payload, int block_ticks) +{ + http_stream_t *http = (http_stream_t *)handle; + esp_gmf_data_bus_block_t *blk = (esp_gmf_data_bus_block_t *)payload; + ESP_LOGD(TAG, "rel_wr: %p, vld: %d, len: %d, done: %d", blk->buf, blk->valid_size, blk->buf_length, blk->is_last); + return esp_gmf_db_release_write(http->data_bus, payload, block_ticks); +} + +esp_gmf_err_t esp_gmf_io_http_reset(esp_gmf_io_handle_t handle) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG;); + http_stream_t *http = (http_stream_t *)handle; + if (http->data_bus) { + esp_gmf_db_reset(http->data_bus); + } + ESP_LOGD(TAG, "Reset, %p", http); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_io_http_set_server_cert(esp_gmf_io_handle_t handle, const char *cert) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG;); + ESP_GMF_NULL_CHECK(TAG, cert, return ESP_GMF_ERR_INVALID_ARG); + http_io_cfg_t *http_io_cfg = (http_io_cfg_t *)OBJ_GET_CFG(handle); + http_io_cfg->cert_pem = cert; + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_io_http_set_event_callback(esp_gmf_io_handle_t handle, http_io_event_handle_t event_callback) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG;); + http_io_cfg_t *http_io_cfg = (http_io_cfg_t *)OBJ_GET_CFG(handle); + http_io_cfg->event_handle = event_callback; + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_io_http_init(http_io_cfg_t *config, esp_gmf_io_handle_t *io) +{ + ESP_GMF_NULL_CHECK(TAG, config, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, io, return ESP_GMF_ERR_INVALID_ARG); + *io = NULL; + esp_gmf_err_t ret = ESP_GMF_ERR_OK; + http_stream_t *http = esp_gmf_oal_calloc(1, sizeof(http_stream_t)); + ESP_GMF_MEM_VERIFY(TAG, http, return ESP_GMF_ERR_MEMORY_LACK, + "http stream", sizeof(http_stream_t)); + esp_gmf_obj_t *obj = (esp_gmf_obj_t *)http; + obj->new_obj = _http_new; + obj->del_obj = _http_destroy; + http_io_cfg_t *cfg = esp_gmf_oal_calloc(1, sizeof(*config)); + ESP_GMF_MEM_VERIFY(TAG, cfg, {ret = ESP_GMF_ERR_MEMORY_LACK; goto _http_init_fail;}, + "http stream configuration", sizeof(*config)); + memcpy(cfg, config, sizeof(*config)); + esp_gmf_obj_set_config(obj, cfg, sizeof(*config)); + ret = esp_gmf_obj_set_tag(obj, "io_http"); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto _http_init_fail, "Failed to set obj tag"); + http->base.dir = config->dir; + http->base.type = ESP_GMF_IO_TYPE_BLOCK; + http->base.open = _http_open; + http->base.process = _http_process; + http->base.seek = _http_seek; + http->base.prev_close = _http_prev_close; + http->base.close = _http_close; + http->base.reset = esp_gmf_io_http_reset; + if (config->dir == ESP_GMF_IO_DIR_WRITER) { + http->base.acquire_write = _http_acquire_write; + http->base.release_write = _http_release_write; + } else if (config->dir == ESP_GMF_IO_DIR_READER) { + http->base.acquire_read = _http_acquire_read; + http->base.release_read = _http_release_read; + } else { + ESP_LOGE(TAG, "Does not set read or write function"); + ret = ESP_GMF_ERR_NOT_SUPPORT; + goto _http_init_fail; + } + esp_gmf_io_cfg_t io_cfg = { + .thread.stack = config->task_stack, + .thread.prio = config->task_prio, + .thread.core = config->task_core, + .thread.stack_in_ext = config->stack_in_ext, + }; + ret = esp_gmf_io_init(&http->base, &io_cfg); + if(ret != ESP_GMF_ERR_OK) { + goto _http_init_fail; + } + *io = obj; + ESP_LOGD(TAG, "Initialization, %s-%p", OBJ_GET_TAG(http), http); + return ESP_GMF_ERR_OK; +_http_init_fail: + esp_gmf_obj_delete(obj); + return ret; +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_io/esp_gmf_io_i2s_pdm.c b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_io/esp_gmf_io_i2s_pdm.c new file mode 100644 index 0000000..2c33d5b --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_io/esp_gmf_io_i2s_pdm.c @@ -0,0 +1,212 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#include +#include +#include "esp_gmf_io_i2s_pdm.h" +#include "esp_gmf_oal_mem.h" +#include "esp_log.h" +#include "freertos/FreeRTOS.h" +#include "freertos/event_groups.h" + +#define PDM_TX_DONE_BIT BIT(0) + +/** + * @brief I2S pdm io context in GMF + */ +typedef struct { + esp_gmf_io_t base; /*!< The GMF i2s pdm io handle */ + bool is_open; /*!< The flag of whether opened */ + EventGroupHandle_t pdm_event; /*!< The event group handle of i2s pdm */ +} i2s_pdm_io_stream_t; + +static const char *TAG = "ESP_GMF_IIS_PDM"; + +bool _i2s_pdm_tx_done_callback(i2s_chan_handle_t handle, i2s_event_data_t *event, void *user_ctx) +{ + EventGroupHandle_t evt = (EventGroupHandle_t)user_ctx; + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + BaseType_t xResult = xEventGroupSetBitsFromISR(evt, PDM_TX_DONE_BIT, &xHigherPriorityTaskWoken); + return xResult; +} + +static esp_gmf_err_t _i2s_pdm_new(void *cfg, esp_gmf_obj_handle_t *io) +{ + return esp_gmf_io_i2s_pdm_init(cfg, io); +} + +static esp_gmf_err_t _i2s_pdm_open(esp_gmf_io_handle_t io) +{ + i2s_pdm_io_stream_t *i2s_pdm_io = (i2s_pdm_io_stream_t *)io; + i2s_pdm_io_cfg_t *cfg = (i2s_pdm_io_cfg_t *)OBJ_GET_CFG(i2s_pdm_io); + ESP_GMF_NULL_CHECK(TAG, cfg, return ESP_GMF_ERR_FAIL;); + if (cfg->pdm_chan == NULL) { + ESP_LOGE(TAG, "There is no activated I2S driver handle"); + return ESP_GMF_ERR_FAIL; + } + if (cfg->dir == ESP_GMF_IO_DIR_WRITER) { + i2s_pdm_io->pdm_event = xEventGroupCreate(); + ESP_GMF_CHECK(TAG, i2s_pdm_io->pdm_event, return ESP_GMF_ERR_MEMORY_LACK, "Failed to create i2s pdm event"); + i2s_event_callbacks_t cbs = { + .on_recv = NULL, + .on_recv_q_ovf = NULL, + .on_sent = NULL, + .on_send_q_ovf = _i2s_pdm_tx_done_callback, + }; + i2s_channel_register_event_callback(cfg->pdm_chan, &cbs, i2s_pdm_io->pdm_event); + } + i2s_channel_enable(cfg->pdm_chan); + i2s_pdm_io->is_open = true; + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_io_t _i2s_pdm_acquire_read(esp_gmf_io_handle_t handle, void *payload, uint32_t wanted_size, int block_ticks) +{ + i2s_pdm_io_stream_t *i2s_pdm_io = (i2s_pdm_io_stream_t *)handle; + esp_gmf_payload_t *pload = (esp_gmf_payload_t *)payload; + size_t rlen = 0; + i2s_pdm_io_cfg_t *cfg = (i2s_pdm_io_cfg_t *)OBJ_GET_CFG(i2s_pdm_io); + ESP_GMF_NULL_CHECK(TAG, cfg, return ESP_GMF_IO_FAIL;); + if (i2s_channel_read(cfg->pdm_chan, pload->buf, wanted_size, &rlen, ESP_GMF_MAX_DELAY) != ESP_GMF_ERR_OK) { + ESP_LOGE(TAG, "Read I2S data error, wanted: %ld", wanted_size); + return ESP_GMF_IO_FAIL; + } + pload->valid_size = rlen; + ESP_LOGD(TAG, "Read len: %d", rlen); + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_io_t _i2s_pdm_release_read(esp_gmf_io_handle_t handle, void *payload, int block_ticks) +{ + i2s_pdm_io_stream_t *i2s_pdm_io = (i2s_pdm_io_stream_t *)handle; + esp_gmf_payload_t *pload = (esp_gmf_payload_t *)payload; + esp_gmf_info_file_t info = {0}; + esp_gmf_io_get_info((esp_gmf_io_handle_t)i2s_pdm_io, &info); + ESP_LOGD(TAG, "Update len = %d, pos = %d/%d", pload->valid_size, (int)info.pos, (int)info.size); + esp_gmf_io_update_pos((esp_gmf_io_handle_t)handle, pload->valid_size); + return ESP_GMF_IO_OK; +} + +static esp_gmf_err_io_t _i2s_pdm_acquire_write(esp_gmf_io_handle_t handle, void *payload, uint32_t wanted_size, int block_ticks) +{ + return ESP_GMF_IO_OK; +} + +static esp_gmf_err_io_t _i2s_pdm_release_write(esp_gmf_io_handle_t handle, void *payload, int block_ticks) +{ + i2s_pdm_io_stream_t *i2s_pdm_io = (i2s_pdm_io_stream_t *)handle; + esp_gmf_payload_t *pload = (esp_gmf_payload_t *)payload; + size_t wlen = 0; + i2s_pdm_io_cfg_t *cfg = (i2s_pdm_io_cfg_t *)OBJ_GET_CFG(i2s_pdm_io); + ESP_GMF_NULL_CHECK(TAG, cfg, return ESP_GMF_IO_FAIL;); + if (i2s_channel_write(cfg->pdm_chan, pload->buf, pload->valid_size, &wlen, ESP_GMF_MAX_DELAY) != ESP_GMF_ERR_OK) { + ESP_LOGE(TAG, "I2S write failed, valid: %d", pload->valid_size); + return ESP_GMF_IO_FAIL; + } + esp_gmf_info_file_t info = {0}; + if (pload->is_done && i2s_pdm_io->pdm_event) { + ESP_LOGE(TAG, "Clear the PDM_TX_DONE_BIT, len = %d", pload->valid_size); + xEventGroupClearBits(i2s_pdm_io->pdm_event, PDM_TX_DONE_BIT); + } + if (wlen > 0) { + esp_gmf_io_update_pos((esp_gmf_io_handle_t)handle, wlen); + } + esp_gmf_io_get_info((esp_gmf_io_handle_t)i2s_pdm_io, &info); + ESP_LOGD(TAG, "Write len = %d, pos = %d/%d", pload->valid_size, (int)info.pos, (int)info.size); + return ESP_GMF_IO_OK; +} + +static esp_gmf_err_t _i2s_pdm_seek(esp_gmf_io_handle_t io, uint64_t seek_byte_pos) +{ + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t _i2s_pdm_reset(esp_gmf_io_handle_t io) +{ + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t _i2s_pdm_close(esp_gmf_io_handle_t io) +{ + i2s_pdm_io_stream_t *i2s_pdm_io = (i2s_pdm_io_stream_t *)io; + esp_gmf_info_file_t info = {0}; + i2s_pdm_io_cfg_t *cfg = (i2s_pdm_io_cfg_t *)OBJ_GET_CFG(i2s_pdm_io); + ESP_GMF_NULL_CHECK(TAG, cfg, return ESP_GMF_IO_FAIL;); + esp_gmf_io_get_info((esp_gmf_io_handle_t)i2s_pdm_io, &info); + ESP_LOGI(TAG, "Try to close, %p, pos = %d/%d, pdm_event: %p", i2s_pdm_io, (int)info.pos, (int)info.size, i2s_pdm_io->pdm_event); + if (i2s_pdm_io->pdm_event) { + xEventGroupWaitBits(i2s_pdm_io->pdm_event, PDM_TX_DONE_BIT, pdTRUE, pdFALSE, ESP_GMF_MAX_DELAY); + } + ESP_LOGI(TAG, "CLose, %p, pos = %d/%d", i2s_pdm_io, (int)info.pos, (int)info.size); + if (i2s_pdm_io->is_open) { + i2s_pdm_io->is_open = false; + } + esp_gmf_io_set_pos((esp_gmf_io_handle_t)io, 0); + i2s_channel_disable(cfg->pdm_chan); + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t _i2s_pdm_delete(esp_gmf_io_handle_t io) +{ + i2s_pdm_io_stream_t *i2s_pdm_io = (i2s_pdm_io_stream_t *)io; + ESP_LOGD(TAG, "Delete, %s-%p", OBJ_GET_TAG(i2s_pdm_io), i2s_pdm_io); + if (i2s_pdm_io->pdm_event) { + vEventGroupDelete(i2s_pdm_io->pdm_event); + } + void *cfg = OBJ_GET_CFG(io); + if (cfg) { + esp_gmf_oal_free(cfg); + } + esp_gmf_io_deinit(io); + esp_gmf_oal_free(i2s_pdm_io); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_io_i2s_pdm_init(i2s_pdm_io_cfg_t *config, esp_gmf_io_handle_t *io) +{ + ESP_GMF_NULL_CHECK(TAG, config, {return ESP_GMF_ERR_INVALID_ARG;}); + ESP_GMF_NULL_CHECK(TAG, io, {return ESP_GMF_ERR_INVALID_ARG;}); + *io = NULL; + esp_gmf_err_t ret = ESP_GMF_ERR_OK; + i2s_pdm_io_stream_t *i2s_pdm_io = esp_gmf_oal_calloc(1, sizeof(i2s_pdm_io_stream_t)); + ESP_GMF_MEM_VERIFY(TAG, i2s_pdm_io, {return ESP_GMF_ERR_MEMORY_LACK;}, + "I2s pdm stream", sizeof(i2s_pdm_io_stream_t)); + i2s_pdm_io->base.dir = config->dir; + i2s_pdm_io->base.type = ESP_GMF_IO_TYPE_BYTE; + esp_gmf_obj_t *obj = (esp_gmf_obj_t *)i2s_pdm_io; + obj->new_obj = _i2s_pdm_new; + obj->del_obj = _i2s_pdm_delete; + i2s_pdm_io_cfg_t *cfg = esp_gmf_oal_calloc(1, sizeof(*config)); + ESP_GMF_MEM_VERIFY(TAG, cfg, {ret = ESP_GMF_ERR_MEMORY_LACK; goto _i2s_pdm_fail;}, + "I2s pdm stream configuration", sizeof(*config)); + memcpy(cfg, config, sizeof(*config)); + esp_gmf_obj_set_config(obj, cfg, sizeof(*config)); + ret = esp_gmf_obj_set_tag(obj, (config->name == NULL ? "io_i2s_pdm" : config->name)); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto _i2s_pdm_fail, "Failed to set obj tag"); + i2s_pdm_io->base.close = _i2s_pdm_close; + i2s_pdm_io->base.open = _i2s_pdm_open; + i2s_pdm_io->base.seek = _i2s_pdm_seek; + i2s_pdm_io->base.reset = _i2s_pdm_reset; + esp_gmf_io_init(obj, NULL); + if (cfg->dir == ESP_GMF_IO_DIR_WRITER) { + i2s_pdm_io->base.acquire_write = _i2s_pdm_acquire_write; + i2s_pdm_io->base.release_write = _i2s_pdm_release_write; + } else if (cfg->dir == ESP_GMF_IO_DIR_READER) { + i2s_pdm_io->base.acquire_read = _i2s_pdm_acquire_read; + i2s_pdm_io->base.release_read = _i2s_pdm_release_read; + } else { + ESP_LOGW(TAG, "Does not set read or write function"); + ret = ESP_GMF_ERR_NOT_SUPPORT; + goto _i2s_pdm_fail; + } + *io = obj; + ESP_LOGD(TAG, "Initialization, %s-%p", OBJ_GET_TAG(obj), i2s_pdm_io); + return ESP_GMF_ERR_OK; +_i2s_pdm_fail: + esp_gmf_obj_delete(obj); + return ret; +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_io/http_lib/gzip/gzip_miniz.c b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_io/http_lib/gzip/gzip_miniz.c new file mode 100644 index 0000000..f3e7490 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_io/http_lib/gzip/gzip_miniz.c @@ -0,0 +1,310 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ +#include +#include +#include +#include +#include "esp_log.h" +#include "gzip_miniz.h" +#include "miniz_inflate.h" + +#define TAG "GZIP_MINIZ" +#define GZIP_HEADER_SIZE (10) + +typedef struct { + gzip_miniz_cfg_t cfg; + uint8_t *chunk_ptr; + int chunk_filled; + int chunk_consumed; + bool unzip_error; + bool first_data; + bool final_data; + int head_flag; + int extra_len; + int head_filled; + mz_stream s; +} gzip_miniz_t; + +static bool verify_gzip_header(gzip_miniz_t *zip, uint8_t *data, int size) +{ + if (size < GZIP_HEADER_SIZE) { + return false; + } + if (data[0] != 0x1F || data[1] != 0x8B || data[2] != 0x8) { + return false; + } + zip->head_flag = data[3]; + return true; +} + +static int gzip_miniz_skip_head(gzip_miniz_t *zip, uint8_t *data, int size) +{ + if (zip->head_flag == 0) { + return 0; + } + int org_size = size; + if (zip->head_flag & 4) { + // 2 bytes extra len + if (zip->head_filled > 2) { + return -1; + } + if (zip->head_filled + size >= 2) { + int used = 2 - zip->head_filled; + if (used) { + size -= used; + while (used--) { + zip->extra_len = zip->extra_len + (*data << (zip->head_filled * 8)); + zip->head_filled++; + data++; + } + } + if (size >= zip->extra_len) { + size -= zip->extra_len; + data += zip->extra_len; + zip->extra_len = 0; + zip->head_flag &= ~4; + zip->head_filled = 0; + } else { + zip->extra_len -= size; + return org_size; + } + } else { + for (int i = 0; i < size; i++) { + zip->extra_len = zip->extra_len + (*data << (zip->head_filled * 8)); + zip->head_filled++; + data++; + } + zip->head_filled += size; + return org_size; + } + } + if (zip->head_flag & 8) { + // name + while (size) { + size--; + data++; + if (*(data - 1) == '\0') { + zip->head_flag &= ~8; + break; + } + } + if (size == 0) { + return org_size; + } + } + if (zip->head_flag & 0x10) { + // comment + while (size) { + size--; + data++; + if (*(data - 1) == '\0') { + zip->head_flag &= ~0x10; + break; + } + } + if (size == 0) { + return org_size; + } + } + if (zip->head_flag & 0x2) { + // CRC16 + if (zip->head_filled > 2) { + return -1; + } + if (zip->head_filled + size >= 2) { + int used = 2 - zip->head_filled; + size -= used; + data += used; + zip->head_flag &= ~0x2; + } else { + zip->head_filled += size; + return org_size; + } + } + return org_size - size; +} + +gzip_miniz_handle_t gzip_miniz_init(gzip_miniz_cfg_t *cfg) +{ + if (cfg->read_cb == NULL) { + ESP_LOGE(TAG, "Read callback must be provided"); + return NULL; + } + gzip_miniz_t *zip = (gzip_miniz_t *) calloc(1, sizeof(gzip_miniz_t)); + if (zip == NULL) { + ESP_LOGE(TAG, "No memory for instance"); + return NULL; + } + zip->cfg = *cfg; + int chunk_size = cfg->chunk_size ? cfg->chunk_size : 32; + zip->chunk_ptr = (uint8_t *) malloc(chunk_size); + if (zip->chunk_ptr == NULL) { + free(zip); + ESP_LOGE(TAG, "No memory for chunk"); + return NULL; + } + zip->cfg.chunk_size = chunk_size; + zip->first_data = true; + mz_inflateInit2(&zip->s, -MZ_DEFAULT_WINDOW_BITS); + return (gzip_miniz_handle_t)zip; +} + +int gzip_miniz_read(gzip_miniz_handle_t h, uint8_t *out, int out_size) +{ + gzip_miniz_t *zip = (gzip_miniz_t *) h; + if (zip == NULL) { + return -1; + } + if (zip->unzip_error) { + return -2; + } + if (zip->final_data) { + return 0; + } + int size = 0; + if (zip->first_data == true) { + zip->first_data = false; + int size = zip->cfg.read_cb(zip->chunk_ptr, zip->cfg.chunk_size, zip->cfg.ctx); + if (size < 0) { + zip->unzip_error = true; + return -1; + } + zip->chunk_filled = size; + if (verify_gzip_header(zip, zip->chunk_ptr, zip->chunk_filled) == false) { + zip->unzip_error = true; + ESP_LOGE(TAG, "Wrong data not match gzip header"); + return -1; + } + zip->chunk_consumed = GZIP_HEADER_SIZE; + } + + while (1) { + uint8_t *data = zip->chunk_ptr + zip->chunk_consumed; + size = zip->chunk_filled - zip->chunk_consumed; + if (size) { + int skip = gzip_miniz_skip_head(zip, data, size); + if (skip == 0) { + break; + } + zip->chunk_consumed += skip; + if (size > skip) { + break; + } + } + size = zip->cfg.read_cb(zip->chunk_ptr, zip->cfg.chunk_size, zip->cfg.ctx); + if (size < 0) { + zip->unzip_error = true; + ESP_LOGE(TAG, "Fail to read data"); + return -1; + } + if (size == 0) { + zip->final_data = true; + return 0; + } + zip->chunk_filled = size; + zip->chunk_consumed = 0; + } + + int org_size = out_size; + // set input buffer + mz_stream *s = &zip->s; + s->next_in = zip->chunk_ptr + zip->chunk_consumed; + s->avail_in = zip->chunk_filled - zip->chunk_consumed; + s->next_out = out; + s->avail_out = out_size; + while (1) { + int ret = mz_inflate(s, MZ_SYNC_FLUSH); + if (ret < 0) { + zip->unzip_error = true; + ESP_LOGE(TAG, "Fail to inflate ret %d", ret); + break; + } + int out_consume = out_size - s->avail_out; + out += out_consume; + out_size -= out_consume; + if (ret == MZ_STREAM_END) { + zip->final_data = true; + break; + } + if (s->avail_out == 0) { + // All output consumed + int org_size = zip->chunk_filled - zip->chunk_consumed; + zip->chunk_consumed += (org_size - s->avail_in); + break; + } + if (s->avail_in == 0) { + size = zip->cfg.read_cb(zip->chunk_ptr, zip->cfg.chunk_size, zip->cfg.ctx); + if (size < 0) { + zip->unzip_error = true; + ESP_LOGE(TAG, "Fail to read data"); + return -1; + } + zip->chunk_filled = size; + zip->chunk_consumed = 0; + // Update pointer + s->next_in = zip->chunk_ptr; + s->avail_in = zip->chunk_filled - zip->chunk_consumed; + s->next_out = out; + s->avail_out = out_size; + + } else { + // Impossible case + break; + } + } + return org_size - out_size; +} + +int gzip_miniz_deinit(gzip_miniz_handle_t h) +{ + gzip_miniz_t *zip = (gzip_miniz_t *) h; + if (zip == NULL) { + return -1; + } + mz_inflateEnd(&zip->s); + free(zip->chunk_ptr); + free(zip); + return 0; +} + +int gzip_miniz_zip(const uint8_t *input, size_t input_size, uint8_t *out, int out_size) +{ + int pos = 0; + const uint8_t header[10] = {0x1F, 0x8B, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03}; + memcpy(out, header, 10); + pos += 10; + + mz_stream stream = {0}; + if (mz_deflateInit2(&stream, MZ_BEST_COMPRESSION, MZ_DEFLATED, -MZ_DEFAULT_WINDOW_BITS, 1, MZ_DEFAULT_STRATEGY) != MZ_OK) { + ESP_LOGE(TAG, "Failed to init deflate"); + return -1; + } + + stream.next_in = (const unsigned char *)input; + stream.avail_in = input_size; + int ret; + do { + stream.next_out = out + pos; + stream.avail_out = out_size - pos; + ret = mz_deflate(&stream, MZ_FINISH); + if (ret != MZ_OK && ret != MZ_STREAM_END) { + ESP_LOGE(TAG, "Failed to deflate ret %d", ret); + mz_deflateEnd(&stream); + return -2; + } + pos += (out_size - pos) - stream.avail_out; + } while (ret != MZ_STREAM_END); + // Clean up compression stream + mz_deflateEnd(&stream); + + uint32_t crc_value = mz_crc32(0, input, input_size); + memcpy(out + pos, &crc_value, 4); + pos += 4; + memcpy(out + pos, &input_size, 4); + pos += 4; + return pos; +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_io/http_lib/gzip/include/gzip_miniz.h b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_io/http_lib/gzip/include/gzip_miniz.h new file mode 100644 index 0000000..2ff046f --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_io/http_lib/gzip/include/gzip_miniz.h @@ -0,0 +1,71 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ +#ifndef _GZIP_MINIZ_H_ +#define _GZIP_MINIZ_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Configuration for gzip using miniz library + */ +typedef struct { + int (*read_cb)(uint8_t *data, int size, void *ctx); /*!< Read callback return size being read */ + int chunk_size; /*!< Chunk size default 32 if set to 0 */ + void *ctx; /*!< Read context */ +} gzip_miniz_cfg_t; + +/** + * @brief Handle for gzip + */ +typedef void *gzip_miniz_handle_t; + +/** + * @brief Initialize for gzip using miniz + * @param cfg: Configuration for gzip using miniz + * @return NULL: Input parameter wrong or no memory + * Others: Handle for gzip inflate operation + */ +gzip_miniz_handle_t gzip_miniz_init(gzip_miniz_cfg_t *cfg); + +/** + * @brief Inflate and read data + * @param out: Data to read after inflated + * @param out_size: Data size to read + * @return >= 0: Data size being read + * -1: Wrong input parameter or wrong data + * -2: Inflate error by miniz + */ +int gzip_miniz_read(gzip_miniz_handle_t zip, uint8_t *out, int out_size); + +/** + * @brief Deinitialize gzip using miniz + * @param zip: Handle for gzip + * @return 0: On success + * -1: Input parameter wrong + */ +int gzip_miniz_deinit(gzip_miniz_handle_t zip); + +/** + * @brief Zip data into gzip format + * @param in: Data to be zipped + * @param in_size: Data size to be zipped + * @param in_size: Data size to be zipped + * @param out: Store output gzip format data + * @param out_size: Zipped data size + * @return >= 0: On success + * -1: Fail to init gzip deflate + * -2: Fail to deflate + */ +int gzip_miniz_zip(const uint8_t* in, size_t in_size, uint8_t *out, int out_size); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_io/http_lib/gzip/miniz_inflate.h b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_io/http_lib/gzip/miniz_inflate.h new file mode 100644 index 0000000..b075c71 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_io/http_lib/gzip/miniz_inflate.h @@ -0,0 +1,365 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ +#ifndef _MINIZ_INFLATE_H +#define _MINIZ_INFLATE_H + +#include +#include "esp_idf_version.h" +#if (ESP_IDF_VERSION_MAJOR == 4) && (ESP_IDF_VERSION_MINOR < 3) +#include "esp32/rom/miniz.h" +#elif (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 0, 0) && ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 3, 0)) +#include "rom/miniz.h" +#else +#include "miniz.h" +#endif + +// Add the API missing in miniz of ROM code +// Original code can be gotten from: https://github.com/richgel999/miniz/blob/master/miniz.c +#ifdef MINIZ_NO_ZLIB_APIS + +// Flush values. For typical usage you only need MZ_NO_FLUSH and MZ_FINISH. The other values are for advanced use (refer +// to the zlib docs). +enum { MZ_NO_FLUSH = 0, MZ_PARTIAL_FLUSH = 1, MZ_SYNC_FLUSH = 2, MZ_FULL_FLUSH = 3, MZ_FINISH = 4, MZ_BLOCK = 5 }; + +// Return status codes. MZ_PARAM_ERROR is non-standard. +enum { + MZ_OK = 0, + MZ_STREAM_END = 1, + MZ_NEED_DICT = 2, + MZ_ERRNO = -1, + MZ_STREAM_ERROR = -2, + MZ_DATA_ERROR = -3, + MZ_MEM_ERROR = -4, + MZ_BUF_ERROR = -5, + MZ_VERSION_ERROR = -6, + MZ_PARAM_ERROR = -10000 +}; + +// Compression levels: 0-9 are the standard zlib-style levels, 10 is best possible compression (not zlib compatible, and +// may be very slow), MZ_DEFAULT_COMPRESSION=MZ_DEFAULT_LEVEL. +enum { + MZ_NO_COMPRESSION = 0, + MZ_BEST_SPEED = 1, + MZ_BEST_COMPRESSION = 9, + MZ_UBER_COMPRESSION = 10, + MZ_DEFAULT_LEVEL = 6, + MZ_DEFAULT_COMPRESSION = -1 +}; + +// Window bits +#define MZ_DEFAULT_WINDOW_BITS 15 +#define MZ_MIN(a, b) (a > b ? b : a) + +struct mz_internal_state; + +// Compression/decompression stream struct. +typedef struct mz_stream_s { + const unsigned char *next_in; // pointer to next byte to read + unsigned int avail_in; // number of bytes available at next_in + mz_ulong total_in; // total number of bytes consumed so far + + unsigned char *next_out; // pointer to next byte to write + unsigned int avail_out; // number of bytes that can be written to next_out + mz_ulong total_out; // total number of bytes produced so far + + char *msg; // error msg (unused) + struct mz_internal_state *state; // internal state, allocated by zalloc/zfree + + int data_type; // data_type (unused) + mz_ulong adler; // adler32 of the source or uncompressed data + mz_ulong crc32; // crc32 of the source or uncompressed data +} mz_stream; + +typedef mz_stream *mz_streamp; + +typedef struct { + tinfl_decompressor m_decomp; + mz_uint m_dict_ofs, m_dict_avail, m_first_call, m_has_flushed; + int m_window_bits; + mz_uint8 m_dict[TINFL_LZ_DICT_SIZE]; + tinfl_status m_last_status; +} inflate_state; + +static int mz_inflateInit2(mz_streamp pStream, int window_bits) +{ + inflate_state *pDecomp; + if (!pStream) + return MZ_STREAM_ERROR; + if ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS)) + return MZ_PARAM_ERROR; + + pStream->data_type = 0; + pStream->adler = 0; + pStream->crc32 = 0; + pStream->msg = NULL; + pStream->total_in = 0; + pStream->total_out = 0; + + pDecomp = (inflate_state *) calloc(1, sizeof(inflate_state)); + if (!pDecomp) + return MZ_MEM_ERROR; + + pStream->state = (struct mz_internal_state *) pDecomp; + + tinfl_init(&pDecomp->m_decomp); + pDecomp->m_dict_ofs = 0; + pDecomp->m_dict_avail = 0; + pDecomp->m_last_status = TINFL_STATUS_NEEDS_MORE_INPUT; + pDecomp->m_first_call = 1; + pDecomp->m_has_flushed = 0; + pDecomp->m_window_bits = window_bits; + + return MZ_OK; +} + +static int mz_inflate(mz_streamp pStream, int flush) +{ + inflate_state *pState; + mz_uint n, first_call, decomp_flags = TINFL_FLAG_COMPUTE_ADLER32; + size_t in_bytes, out_bytes, orig_avail_in; + tinfl_status status; + + if ((!pStream) || (!pStream->state)) + return MZ_STREAM_ERROR; + if (flush == MZ_PARTIAL_FLUSH) + flush = MZ_SYNC_FLUSH; + if ((flush) && (flush != MZ_SYNC_FLUSH) && (flush != MZ_FINISH)) + return MZ_STREAM_ERROR; + + pState = (inflate_state *) pStream->state; + if (pState->m_window_bits > 0) + decomp_flags |= TINFL_FLAG_PARSE_ZLIB_HEADER; + orig_avail_in = pStream->avail_in; + + first_call = pState->m_first_call; + pState->m_first_call = 0; + if (pState->m_last_status < 0) + return MZ_DATA_ERROR; + + if (pState->m_has_flushed && (flush != MZ_FINISH)) + return MZ_STREAM_ERROR; + pState->m_has_flushed |= (flush == MZ_FINISH); + + if ((flush == MZ_FINISH) && (first_call)) { + // MZ_FINISH on the first call implies that the input and output buffers are large enough to hold the entire + // compressed/decompressed file. + decomp_flags |= TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF; + in_bytes = pStream->avail_in; + out_bytes = pStream->avail_out; + status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pStream->next_out, pStream->next_out, + &out_bytes, decomp_flags); + pState->m_last_status = status; + pStream->next_in += (mz_uint) in_bytes; + pStream->avail_in -= (mz_uint) in_bytes; + pStream->total_in += (mz_uint) in_bytes; + pStream->adler = tinfl_get_adler32(&pState->m_decomp); + pStream->next_out += (mz_uint) out_bytes; + pStream->avail_out -= (mz_uint) out_bytes; + pStream->total_out += (mz_uint) out_bytes; + + if (status < 0) + return MZ_DATA_ERROR; + else if (status != TINFL_STATUS_DONE) { + pState->m_last_status = TINFL_STATUS_FAILED; + return MZ_BUF_ERROR; + } + return MZ_STREAM_END; + } + // flush != MZ_FINISH then we must assume there's more input. + if (flush != MZ_FINISH) + decomp_flags |= TINFL_FLAG_HAS_MORE_INPUT; + + if (pState->m_dict_avail) { + n = MZ_MIN(pState->m_dict_avail, pStream->avail_out); + memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n); + pStream->next_out += n; + pStream->avail_out -= n; + pStream->total_out += n; + pState->m_dict_avail -= n; + pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1); + return ((pState->m_last_status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK; + } + + for (;;) { + in_bytes = pStream->avail_in; + out_bytes = TINFL_LZ_DICT_SIZE - pState->m_dict_ofs; + + status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pState->m_dict, + pState->m_dict + pState->m_dict_ofs, &out_bytes, decomp_flags); + pState->m_last_status = status; + + pStream->next_in += (mz_uint) in_bytes; + pStream->avail_in -= (mz_uint) in_bytes; + pStream->total_in += (mz_uint) in_bytes; + pStream->adler = tinfl_get_adler32(&pState->m_decomp); + + pState->m_dict_avail = (mz_uint) out_bytes; + + n = MZ_MIN(pState->m_dict_avail, pStream->avail_out); + memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n); + pStream->next_out += n; + pStream->avail_out -= n; + pStream->total_out += n; + pState->m_dict_avail -= n; + pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1); + + if (status < 0) + return MZ_DATA_ERROR; // Stream is corrupted (there could be some uncompressed data left in the output + // dictionary - oh well). + else if ((status == TINFL_STATUS_NEEDS_MORE_INPUT) && (!orig_avail_in)) + return MZ_BUF_ERROR; // Signal caller that we can't make forward progress without supplying more input or by + // setting flush to MZ_FINISH. + else if (flush == MZ_FINISH) { + // The output buffer MUST be large to hold the remaining uncompressed data when flush==MZ_FINISH. + if (status == TINFL_STATUS_DONE) + return pState->m_dict_avail ? MZ_BUF_ERROR : MZ_STREAM_END; + // status here must be TINFL_STATUS_HAS_MORE_OUTPUT, which means there's at least 1 more byte on the way. If + // there's no more room left in the output buffer then something is wrong. + else if (!pStream->avail_out) + return MZ_BUF_ERROR; + } else if ((status == TINFL_STATUS_DONE) || (!pStream->avail_in) || (!pStream->avail_out) || + (pState->m_dict_avail)) + break; + } + + return ((status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK; +} + +static int mz_inflateEnd(mz_streamp pStream) +{ + if (!pStream) + return MZ_STREAM_ERROR; + if (pStream->state) { + free(pStream->state); + pStream->state = NULL; + } + return MZ_OK; +} + +static int mz_deflateEnd(mz_streamp pStream) +{ + if (!pStream) + return MZ_STREAM_ERROR; + if (pStream->state) + { + free(pStream->state); + pStream->state = NULL; + } + return MZ_OK; +} + +static int tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy) +{ + static const mz_uint s_tdefl_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 }; + mz_uint comp_flags = s_tdefl_num_probes[(level >= 0) ? MZ_MIN(10, level) : MZ_DEFAULT_LEVEL] | ((level <= 3) ? TDEFL_GREEDY_PARSING_FLAG : 0); + if (window_bits > 0) + comp_flags |= TDEFL_WRITE_ZLIB_HEADER; + + if (!level) + comp_flags |= TDEFL_FORCE_ALL_RAW_BLOCKS; + else if (strategy == MZ_FILTERED) + comp_flags |= TDEFL_FILTER_MATCHES; + else if (strategy == MZ_HUFFMAN_ONLY) + comp_flags &= ~TDEFL_MAX_PROBES_MASK; + else if (strategy == MZ_FIXED) + comp_flags |= TDEFL_FORCE_ALL_STATIC_BLOCKS; + else if (strategy == MZ_RLE) + comp_flags |= TDEFL_RLE_MATCHES; + + return comp_flags; +} + +static int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy) +{ + tdefl_compressor *pComp; + mz_uint comp_flags = TDEFL_COMPUTE_ADLER32 | tdefl_create_comp_flags_from_zip_params(level, window_bits, strategy); + + if (!pStream) + return MZ_STREAM_ERROR; + if ((method != MZ_DEFLATED) || ((mem_level < 1) || (mem_level > 9)) || ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS))) + return MZ_PARAM_ERROR; + + pStream->data_type = 0; + pStream->adler = MZ_ADLER32_INIT; + pStream->msg = NULL; + pStream->total_in = 0; + pStream->total_out = 0; + pComp = (tdefl_compressor *)calloc(1, sizeof(tdefl_compressor)); + if (!pComp) + return MZ_MEM_ERROR; + + pStream->state = (struct mz_internal_state *)pComp; + + if (tdefl_init(pComp, NULL, NULL, comp_flags) != TDEFL_STATUS_OKAY) + { + mz_deflateEnd(pStream); + return MZ_PARAM_ERROR; + } + + return MZ_OK; +} + +static int mz_deflate(mz_streamp pStream, int flush) +{ + size_t in_bytes, out_bytes; + mz_ulong orig_total_in, orig_total_out; + int mz_status = MZ_OK; + + if ((!pStream) || (!pStream->state) || (flush < 0) || (flush > MZ_FINISH) || (!pStream->next_out)) + return MZ_STREAM_ERROR; + if (!pStream->avail_out) + return MZ_BUF_ERROR; + + if (flush == MZ_PARTIAL_FLUSH) + flush = MZ_SYNC_FLUSH; + + if (((tdefl_compressor *)pStream->state)->m_prev_return_status == TDEFL_STATUS_DONE) + return (flush == MZ_FINISH) ? MZ_STREAM_END : MZ_BUF_ERROR; + + orig_total_in = pStream->total_in; + orig_total_out = pStream->total_out; + for (;;) + { + tdefl_status defl_status; + in_bytes = pStream->avail_in; + out_bytes = pStream->avail_out; + + defl_status = tdefl_compress((tdefl_compressor *)pStream->state, pStream->next_in, &in_bytes, pStream->next_out, &out_bytes, (tdefl_flush)flush); + pStream->next_in += (mz_uint)in_bytes; + pStream->avail_in -= (mz_uint)in_bytes; + pStream->total_in += (mz_uint)in_bytes; + pStream->adler = tdefl_get_adler32((tdefl_compressor *)pStream->state); + + pStream->next_out += (mz_uint)out_bytes; + pStream->avail_out -= (mz_uint)out_bytes; + pStream->total_out += (mz_uint)out_bytes; + + if (defl_status < 0) + { + mz_status = MZ_STREAM_ERROR; + break; + } + else if (defl_status == TDEFL_STATUS_DONE) + { + mz_status = MZ_STREAM_END; + break; + } + else if (!pStream->avail_out) + break; + else if ((!pStream->avail_in) && (flush != MZ_FINISH)) + { + if ((flush) || (pStream->total_in != orig_total_in) || (pStream->total_out != orig_total_out)) + break; + return MZ_BUF_ERROR; /* Can't make forward progress without some input. + */ + } + } + return mz_status; +} + +#endif +#endif diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_io/idf_component.yml b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_io/idf_component.yml new file mode 100644 index 0000000..9087afa --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_io/idf_component.yml @@ -0,0 +1,16 @@ +version: "0.7.6" +description: Espressif GMF IO Module +url: https://github.com/espressif/esp-gmf/tree/main/elements/gmf_io +documentation: "https://github.com/espressif/esp-gmf/blob/main/elements/gmf_io/README.md" +issues: "https://github.com/espressif/esp-gmf/issues" + +tags: + - "gmf" + +dependencies: + espressif/esp_codec_dev: + require: "public" + version: "^1.4" + espressif/gmf_core: + require: public + version: "^0.7" diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_io/include/esp_gmf_io_embed_flash.h b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_io/include/esp_gmf_io_embed_flash.h new file mode 100644 index 0000000..f190512 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_io/include/esp_gmf_io_embed_flash.h @@ -0,0 +1,66 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#include "esp_gmf_io.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * @brief Espressif embed flash configurations, if any entry is zero then the configuration will be set to default values + */ +typedef struct { + int max_files; /*!< IO direction, reader or writer */ + const char *name; /*!< Name for this instance */ +} embed_flash_io_cfg_t; + +#define EMBED_FLASH_CFG_DEFAULT() { \ + .max_files = 200, \ + .name = NULL, \ +} + +/** + * @brief Embed information in flash + */ +typedef struct embed_item_info { + const uint8_t *address; /*!< The corresponding address in flash */ + int size; /*!< Size of corresponding data */ +} embed_item_info_t; + +/** + * @brief Initializes the embed flash stream I/O with the provided configuration + * + * @param[in] config Pointer to the embed flash IO configuration + * @param[out] io Pointer to the embed flash IO handle to be initialized + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid configuration provided + * - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory + */ +esp_gmf_err_t esp_gmf_io_embed_flash_init(embed_flash_io_cfg_t *config, esp_gmf_io_handle_t *io); + +/** + * @brief Set the embed flash context + * This function mainly provides information about embed flash data + * + * @param[in] io The embed flash element handle + * @param[in] context The embed flash context + * @param[in] max_num The number of embed flash context + * + * @return + * - ESP_GMF_ERR_OK + * - ESP_GMF_ERR_FAIL + */ +esp_gmf_err_t esp_gmf_io_embed_flash_set_context(esp_gmf_io_handle_t io, const embed_item_info_t *context, int max_num); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_io/include/esp_gmf_io_file.h b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_io/include/esp_gmf_io_file.h new file mode 100644 index 0000000..9428b99 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_io/include/esp_gmf_io_file.h @@ -0,0 +1,52 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#include "esp_gmf_io.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * @brief File IO configurations, if any entry is zero then the configuration will be set to default values + */ +typedef struct { + int dir; /*!< IO direction, reader or writer */ + const char *name; /*!< Name for this instance */ + int cache_size; /*!< Cache size for file IO operations in bytes. If size <= 512, it will be set to 0. + Note: Larger cache size will improve read and write performance but consume more memory */ + int cache_caps; /*!< Cache memory capabilities, if zero then it will be set to MALLOC_CAP_DMA. + Note: + 1. If chips have SOC_SDMMC_PSRAM_DMA_CAPABLE capability(such as ESP32P4), + then you can set (MALLOC_CAP_SPIRAM | MALLOC_CAP_DMA) to save SRAM + 2. For ESP32, should use (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT) or MALLOC_CAP_DMA caps to malloc cache + 3. For ESP32Sxx and ESP32Cxx, can also use MALLOC_CAP_INTERNAL caps to malloc cache */ +} file_io_cfg_t; + +#define FILE_IO_CFG_DEFAULT() { \ + .dir = ESP_GMF_IO_DIR_READER, \ + .name = NULL, \ +} + +/** + * @brief Initializes the file stream I/O with the provided configuration + * + * @param[in] config Pointer to the file IO configuration + * @param[out] io Pointer to the file IO handle to be initialized + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid configuration provided + * - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory + */ +esp_gmf_err_t esp_gmf_io_file_init(file_io_cfg_t *config, esp_gmf_io_handle_t *io); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_io/include/esp_gmf_io_http.h b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_io/include/esp_gmf_io_http.h new file mode 100644 index 0000000..6200536 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_io/include/esp_gmf_io_http.h @@ -0,0 +1,141 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#include "esp_gmf_io.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define HTTP_STREAM_TASK_STACK (6 * 1024) +#define HTTP_STREAM_TASK_CORE (0) +#define HTTP_STREAM_TASK_PRIO (10) +#define HTTP_STREAM_RINGBUFFER_SIZE (20 * 1024) + +/** + * @brief HTTP Stream hook type + */ +typedef enum { + HTTP_STREAM_PRE_REQUEST = 0x01, /*!< The event handler will be called before HTTP Client making the connection to the server */ + HTTP_STREAM_ON_REQUEST, /*!< The event handler will be called when HTTP Client is requesting data, + * If the function return the value (-1: ESP_GMF_ERR_FAIL), HTTP Client will be stopped + * If the function return the value > 0, HTTP Stream will ignore the post_field + * If the function return the value = 0, HTTP Stream continue send data from post_field (if any) + */ + HTTP_STREAM_ON_RESPONSE, /*!< The event handler will be called when HTTP Client is receiving data + * If the function return the value (-1: ESP_GMF_ERR_FAIL), HTTP Client will be stopped + * If the function return the value > 0, HTTP Stream will ignore the read function + * If the function return the value = 0, HTTP Stream continue read data from HTTP Server + */ + HTTP_STREAM_POST_REQUEST, /*!< The event handler will be called after HTTP Client send header and body to the server, before fetching the headers */ + HTTP_STREAM_FINISH_REQUEST, /*!< The event handler will be called after HTTP Client fetch the header and ready to read HTTP body */ +} http_stream_event_id_t; + +/** + * @brief Stream event message + */ +typedef struct { + http_stream_event_id_t event_id; /*!< Event ID */ + void *http_client; /*!< Reference to HTTP Client using by this HTTP Stream */ + void *buffer; /*!< Reference to Buffer using by the IO */ + int buffer_len; /*!< Length of buffer */ + void *user_data; /*!< User data context, from `http_io_cfg_t` */ +} http_stream_event_msg_t; + +typedef int (*http_io_event_handle_t)(http_stream_event_msg_t *msg); + +/** + * @brief HTTP Stream configurations + * Default value will be used if any entry is zero + */ +typedef struct { + int task_stack; /*!< Task stack size */ + int task_core; /*!< Task running in core (0 or 1) */ + int task_prio; /*!< Task priority (based on freeRTOS priority) */ + bool stack_in_ext; /*!< Try to allocate stack in external memory */ + int dir; /*!< Type of stream */ + int out_buf_size; /*!< Size of output buffer */ + http_io_event_handle_t event_handle; /*!< The hook function for HTTP Stream */ + void *user_data; /*!< User data context */ + const char *cert_pem; /*!< SSL server certification, PEM format as string, if the client requires to verify server */ + esp_err_t (*crt_bundle_attach)(void *conf); /*!< Function pointer to esp_crt_bundle_attach. Enables the use of certification + bundle for server verification, must be enabled in menuconfig */ +} http_io_cfg_t; + +#define HTTP_STREAM_CFG_DEFAULT() { \ + .task_stack = HTTP_STREAM_TASK_STACK, \ + .task_core = HTTP_STREAM_TASK_CORE, \ + .task_prio = HTTP_STREAM_TASK_PRIO, \ + .stack_in_ext = true, \ + .dir = ESP_GMF_IO_DIR_READER, \ + .out_buf_size = HTTP_STREAM_RINGBUFFER_SIZE, \ + .event_handle = NULL, \ + .user_data = NULL, \ + .cert_pem = NULL, \ + .crt_bundle_attach = NULL, \ +} + +/** + * @brief Initialize the HTTP stream I/O with the specified configuration + * + * @param[in] config Pointer to an `http_io_cfg_t` structure containing the configuration + * settings for the HTTP I/O + * @param[out] io Pointer to a `esp_gmf_io_handle_t` where the initialized I/O object + * handle will be stored + * + * @return + * - ESP_GMF_ERR_OK Initialization successful + * - ESP_GMF_ERR_INVALID_ARG Invalid argument(s) + * - ESP_GMF_ERR_MEMORY_LACK Insufficient memory for initialization + */ +esp_gmf_err_t esp_gmf_io_http_init(http_io_cfg_t *config, esp_gmf_io_handle_t *io); + +/** + * @brief Reset http information. + * + * This function can be used in event_handler of http IO. + * User can call this function to connect to next track in playlist when he/she gets `HTTP_STREAM_FINISH_TRACK` event + * + * @param handle The http IO handle + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_io_http_reset(esp_gmf_io_handle_t handle); + +/** + * @brief Set SSL server certification + * + * @note PEM format as string, if the client requires to verify server + * + * @param handle The http IO handle + * @param cert Server certification + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_io_http_set_server_cert(esp_gmf_io_handle_t handle, const char *cert); + +/** + * @brief Set the event handle for the HTTP IO + * + * @param handle The http IO handle + * @param event_callback The event callback handle + * + * @return + * - ESP_GMF_ERR_OK on success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_io_http_set_event_callback(esp_gmf_io_handle_t handle, http_io_event_handle_t event_callback); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_io/include/esp_gmf_io_i2s_pdm.h b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_io/include/esp_gmf_io_i2s_pdm.h new file mode 100644 index 0000000..076defb --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_io/include/esp_gmf_io_i2s_pdm.h @@ -0,0 +1,47 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#include "driver/i2s_pdm.h" +#include "esp_gmf_io.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * @brief I2S PDM IO configurations, if any entry is zero then the configuration will be set to default values + */ +typedef struct { + i2s_chan_handle_t pdm_chan; /*!< I2S tx channel handler */ + int dir; /*!< IO direction, reader or writer */ + const char *name; /*!< Name for this instance */ +} i2s_pdm_io_cfg_t; + +#define ESP_GMF_IO_I2S_PDM_CFG_DEFAULT() { \ + .pdm_chan = NULL, \ + .dir = ESP_GMF_IO_DIR_READER, \ + .name = NULL, \ +} + +/** + * @brief Initializes the I2S PDM I/O with the provided configuration + * + * @param[in] config Pointer to the file IO configuration + * @param[out] io Pointer to the file IO handle to be initialized + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid configuration provided + * - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory + */ +esp_gmf_err_t esp_gmf_io_i2s_pdm_init(i2s_pdm_io_cfg_t *config, esp_gmf_io_handle_t *io); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_io/mk_flash_embed_tone.py b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_io/mk_flash_embed_tone.py new file mode 100644 index 0000000..2615365 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_io/mk_flash_embed_tone.py @@ -0,0 +1,215 @@ +#!/user/bin/env python3 + +# Espressif Modified MIT License +# +# Copyright (c) 2025 Espressif Systems (Shanghai) CO., LTD +# +# Permission is hereby granted for use EXCLUSIVELY with Espressif Systems products. +# This includes the right to use, copy, modify, merge, publish, distribute, and sublicense +# the Software, subject to the following conditions: +# +# 1. This Software MUST BE USED IN CONJUNCTION WITH ESPRESSIF SYSTEMS PRODUCTS. +# 2. The above copyright notice and this permission notice shall be included in all copies +# or substantial portions of the Software. +# 3. Redistribution of the Software in source or binary form FOR USE WITH NON-ESPRESSIF PRODUCTS +# is strictly prohibited. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +# PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +# FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# DEALINGS IN THE SOFTWARE. +# +# SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + +import sys +import os +import time +import argparse +import logging +from typing import Dict, Tuple, List +from datetime import datetime + +# Configure logging +logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(levelname)s - %(message)s' +) +logger = logging.getLogger(__name__) + +# Constants +LICENSE_STR = f'''/* + * SPDX-FileCopyrightText: {datetime.now().year} Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ + +''' + +GEN_HEAD_FILE_NAME = 'esp_embed_tone.h' +GEN_CMAKE_FILE_NAME = 'esp_embed_tone.cmake' +SUPPORTED_EXTENSIONS = ('.wav', '.mp3', '.aac', '.m4a') + +def sanitize_filename(filename: str) -> str: + # Replace invalid characters with underscore + sanitized = filename.replace('-', '_').replace('.', '_').replace(' ', '_') + # Ensure it starts with a letter + if sanitized[0].isdigit(): + sanitized = 'tone_' + sanitized + return sanitized + +def get_file_info(path: str) -> Dict[str, int]: + try: + file_list = [x for x in os.listdir(path) if x.lower().endswith(SUPPORTED_EXTENSIONS)] + file_list.sort() + if not file_list: + logger.warning(f'No supported audio files found in {path}') + return {} + + file_info = {} + for filename in file_list: + try: + size = os.path.getsize(os.path.join(path, filename)) + file_info[filename] = size + logger.info(f'Found audio file: {filename} ({size} bytes)') + except OSError as e: + logger.error(f'Error getting size for {filename}: {e}') + continue + return file_info + except OSError as e: + logger.error(f'Error scanning directory {path}: {e}') + return {} + +def gen_h_file(file_info: Dict[str, int]) -> Tuple[str, str]: + if not file_info: + logger.error('No file information provided') + return '', '' + + # Header file content + h_file = [ + '#pragma once', + '', + '/**', + ' * @brief Structure for embedding tone information', + ' */', + 'typedef struct {', + ' const uint8_t *address; /*!< Pointer to the embedded tone data */', + ' int size; /*!< Size of the tone data in bytes */', + '} esp_embed_tone_t;', + '' + ] + # CMake file content + cmake_file = ['set(COMPONENT_EMBED_TXTFILES'] + # Generate content for each file + enum_entries = [] + url_entries = [] + tone_entries = [] + for idx, (filename, size) in enumerate(file_info.items()): + sanitized = sanitize_filename(filename) + # Add external reference + h_file.extend([ + '/**', + f' * @brief External reference to embedded tone data: {filename}', + ' */', + f'extern const uint8_t {sanitized}[] asm("_binary_{sanitized}_start");', + '' + ]) + # Add to CMake file + cmake_file.append(f' {filename}') + # Add tone entry + tone_entries.append( + f' [{idx}] = {{\n' + f' .address = {sanitized},\n' + f' .size = {size},\n' + f' }},' + ) + # Add enum entry + enum_entries.append( + f' ESP_EMBED_TONE_{sanitized.upper()} = {idx},' + ) + # Add URL entry with hyphens replaced by underscores + url_filename = filename.replace('-', '_') + url_entries.append( + f' "embed://tone/{idx}_{url_filename}",' + ) + # Complete header file + h_file.extend([ + '/**', + ' * @brief Array of embedded tone information', + ' */', + 'esp_embed_tone_t g_esp_embed_tone[] = {', + *tone_entries, + '};', + '', + '/**', + ' * @brief Enumeration for tone URLs', + ' */', + 'enum esp_embed_tone_index {', + *enum_entries, + f' ESP_EMBED_TONE_URL_MAX = {len(file_info)}', + '};', + '', + '/**', + ' * @brief Array of tone URLs', + ' */', + 'const char * esp_embed_tone_url[] = {', + *url_entries, + '};', + '' + ]) + # Complete CMake file + cmake_file.append(')') + return '\n'.join(h_file), ''.join(cmake_file) + +def write_file(content: str, filepath: str, is_header: bool = False) -> bool: + try: + if os.path.exists(filepath): + os.remove(filepath) + with open(filepath, 'w+') as f: + if is_header: + f.write(LICENSE_STR) + f.write(content) + logger.info(f'Successfully wrote {filepath}') + return True + except OSError as e: + logger.error(f'Error writing to {filepath}: {e}') + return False + +def main(): + parser = argparse.ArgumentParser( + description='Generate embedded audio tone header and CMake files', + formatter_class=argparse.ArgumentDefaultsHelpFormatter + ) + parser.add_argument( + '-p', '--path', + type=str, + required=True, + help='Base folder containing audio files' + ) + parser.add_argument( + '-v', '--verbose', + action='store_true', + help='Enable verbose output' + ) + args = parser.parse_args() + if args.verbose: + logger.setLevel(logging.DEBUG) + # Get file information + file_info = get_file_info(args.path) + if not file_info: + logger.error('No valid audio files found') + sys.exit(1) + # Generate files + h_content, cmake_content = gen_h_file(file_info) + # Write files + h_path = os.path.join(args.path, GEN_HEAD_FILE_NAME) + cmake_path = os.path.join(args.path, GEN_CMAKE_FILE_NAME) + if not write_file(h_content, h_path, is_header=True): + sys.exit(1) + if not write_file(cmake_content, cmake_path): + sys.exit(1) + logger.info('Successfully generated embedded tone files') + +if __name__ == '__main__': + main() diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_misc/CHANGELOG.md b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_misc/CHANGELOG.md new file mode 100644 index 0000000..9b79c38 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_misc/CHANGELOG.md @@ -0,0 +1,32 @@ +# Changelog + +## v0.7.0 + +No further changes. Version updated to align with other components. + +## v0.6.1 + +### Bug Fixes + +- Fixed a bug where the required read size was incorrectly altered by the payload's valid size + +## v0.6.0 + +### Features +- Added GMF port attribution for elements +- Renamed component to `gmf_misc` +- Updated the License + + +## v0.5.1 + +### Bug Fixes + +- Fixed the component requirements + + +## v0.5.0 + +### Features + +- Initial version of `esp-gmf-misc` diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_misc/CMakeLists.txt b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_misc/CMakeLists.txt new file mode 100644 index 0000000..0d4ca7d --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_misc/CMakeLists.txt @@ -0,0 +1,3 @@ +idf_component_register(SRC_DIRS "." + INCLUDE_DIRS "./include" + REQUIRES) diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_misc/LICENSE b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_misc/LICENSE new file mode 100644 index 0000000..95aa434 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_misc/LICENSE @@ -0,0 +1,22 @@ +Espressif Modified MIT License + +Copyright (c) 2025 Espressif Systems (Shanghai) CO., LTD + +Permission is hereby granted for use EXCLUSIVELY with Espressif Systems products. +This includes the right to use, copy, modify, merge, publish, distribute, and sublicense +the Software, subject to the following conditions: + +1. This Software MUST BE USED IN CONJUNCTION WITH ESPRESSIF SYSTEMS PRODUCTS. +2. The above copyright notice and this permission notice shall be included in all copies + or substantial portions of the Software. +3. Redistribution of the Software in source or binary form FOR USE WITH NON-ESPRESSIF PRODUCTS + is strictly prohibited. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_misc/README.md b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_misc/README.md new file mode 100644 index 0000000..412e692 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_misc/README.md @@ -0,0 +1,13 @@ +# ESP-GMF-Misc + +- [![Component Registry](https://components.espressif.com/components/espressif/gmf_misc/badge.svg)](https://components.espressif.com/components/espressif/gmf_misc) + +- [中文版](./README_CN.md) + +ESP GMF Miscellaneous gathers various GMF elements with single-purpose functionalities. The currently supported elements are listed below: +| Name | TAG | Function Description | Input Port | Output port |Input blocking time|Output blocking time| +|:----:|:----:|:----:|:----:|:----:|:----:|:----:| +| esp_gmf_copier | copier | Copies input data to multiple output ports | Single | Multiple | Maximum delay | User configurable, default value is maximum delay | + +## Usage +ESP GMF Miscellaneous is often combined with other GMF elements to form a pipeline. For example code, please refer to [test_app](../test_apps/main/elements/gmf_audio_play_el_test.c)。 diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_misc/README_CN.md b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_misc/README_CN.md new file mode 100644 index 0000000..da51eae --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_misc/README_CN.md @@ -0,0 +1,13 @@ +# ESP-GMF-Misc + +- [![Component Registry](https://components.espressif.com/components/espressif/gmf_misc/badge.svg)](https://components.espressif.com/components/espressif/gmf_misc) + +- [English](./README.md) + +ESP GMF Miscellaneous 汇集了 GMF 各种各样功能单一的元素,目前支持的元素列表如下: +| 名称 | 标签 | 功能说明 | 输入端口 | 输出端口 |输入阻塞时间 | 输出阻塞时间 | +|:----:|:----:|:----:|:----:|:----:|:----:|:----:| +| esp_gmf_copier | copier | 将输入数据复制到多个端口输出 | 单个 | 多个 | 最大延迟 | 可用户配置,默认是最大延迟 | + +## 示例 +ESP GMF Miscellaneous 常与其他 GMF 元素组合成管道使用,示例代码请参考 [test_app](../test_apps/main/elements/gmf_audio_play_el_test.c)。 diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_misc/esp_gmf_copier.c b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_misc/esp_gmf_copier.c new file mode 100644 index 0000000..b12af7d --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_misc/esp_gmf_copier.c @@ -0,0 +1,146 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#include +#include "esp_log.h" +#include "esp_gmf_oal_mem.h" +#include "esp_gmf_element.h" +#include "esp_gmf_err.h" +#include "esp_gmf_copier.h" + +/** + * @brief Copier context in GMF + */ +typedef struct esp_gmf_copier { + struct esp_gmf_element parent; /*!< The GMF copier handle */ +} esp_gmf_copier_t; + +static const char *TAG = "ESP_GMF_COPIER"; + +static esp_gmf_err_t esp_gmf_copier_new(void *cfg, esp_gmf_obj_handle_t *handle) +{ + return esp_gmf_copier_init(cfg, handle); +} + +static esp_gmf_job_err_t esp_gmf_copier_open(esp_gmf_element_handle_t self, void *para) +{ + ESP_LOGD(TAG, "%s", __func__); + return ESP_GMF_ERR_OK; +} + +static esp_gmf_job_err_t esp_gmf_copier_process(esp_gmf_element_handle_t self, void *para) +{ + esp_gmf_element_handle_t hd = (esp_gmf_element_handle_t)self; + esp_gmf_port_t *in = ESP_GMF_ELEMENT_GET(hd)->in; + esp_gmf_port_t *out = ESP_GMF_ELEMENT_GET(hd)->out; + esp_gmf_payload_t *in_load = NULL; + esp_gmf_payload_t *out_load = NULL; + esp_gmf_err_io_t out_len = ESP_GMF_IO_OK; + int idx = 0; + esp_gmf_err_io_t load_ret = esp_gmf_port_acquire_in(in, &in_load, in->data_length, ESP_GMF_MAX_DELAY); + ESP_GMF_PORT_ACQUIRE_IN_CHECK(TAG, load_ret, out_len, { goto __copy_release;}); + int out_data_length = in_load->valid_size > 0 ? in_load->valid_size : in_load->buf_length; + while (out) { + out_load = NULL; + if (out != ESP_GMF_ELEMENT_GET(hd)->out) { + load_ret = esp_gmf_port_acquire_out(out, &out_load, out_data_length, 0); + ESP_GMF_PORT_ACQUIRE_OUT_CHECK(TAG, load_ret, out_len, { goto __copy_release;}); + esp_gmf_payload_copy_data(in_load, out_load); + } else { + out_load = in_load; + load_ret = esp_gmf_port_acquire_out(out, &out_load, out_data_length, 0); + ESP_GMF_PORT_ACQUIRE_OUT_CHECK(TAG, load_ret, out_len, { goto __copy_release;}); + out_load->is_done = in_load->is_done; + out_load->valid_size = in_load->valid_size; + } + load_ret = esp_gmf_port_release_out(out, out_load, out->wait_ticks); + if (load_ret < ESP_GMF_IO_OK) { + if (load_ret == ESP_GMF_IO_FAIL) { + ESP_LOGE(TAG, "Failed to release out, idx: %d, ret: %d.", idx, load_ret); + out_len = load_ret; + goto __copy_release; + } else { + ESP_LOGW(TAG, "Something error on release out, idx: %d, ret: %d.", idx, load_ret); + } + } + ESP_LOGV(TAG, "OUT: %p, rd: %s, I: %p, b: %p, sz: %d, O: %p, b: %p, sz: %d, done: %d, t: %d", out, OBJ_GET_TAG(out->reader), + in_load, in_load->buf, in_load->valid_size, out_load, + out_load != NULL ? out_load->buf : NULL, + out_load != NULL ? out_load->valid_size : -1, + out_load != NULL ? out_load->is_done : -1, + out->wait_ticks); + out = out->next; + idx++; + } + out_len = in_load->valid_size; + if (in_load->is_done) { + out_len = ESP_GMF_JOB_ERR_DONE; + } +__copy_release: + if (in_load != NULL) { + load_ret = esp_gmf_port_release_in(in, in_load, 0); + if ((load_ret < ESP_GMF_IO_OK) && (load_ret != ESP_GMF_IO_ABORT)) { + ESP_LOGE(TAG, "Payload release error, ret:%d, line %d ", load_ret, __LINE__); + out_len = ESP_GMF_JOB_ERR_FAIL; + } + } + return out_len; +} + +static esp_gmf_job_err_t esp_gmf_copier_close(esp_gmf_element_handle_t self, void *para) +{ + ESP_LOGD(TAG, "%s", __func__); + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t esp_gmf_copier_destroy(esp_gmf_element_handle_t self) +{ + ESP_LOGD(TAG, "Destroyed"); + void *cfg = OBJ_GET_CFG(self); + if (cfg) { + esp_gmf_oal_free(cfg); + } + esp_gmf_element_deinit(self); + esp_gmf_oal_free(self); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_copier_init(esp_gmf_copier_cfg_t *config, esp_gmf_obj_handle_t *handle) +{ + ESP_GMF_NULL_CHECK(TAG, handle, {return ESP_GMF_ERR_INVALID_ARG;}); + *handle = NULL; + esp_gmf_copier_t *copier = esp_gmf_oal_calloc(1, sizeof(esp_gmf_copier_t)); + ESP_GMF_MEM_VERIFY(TAG, copier, {return ESP_GMF_ERR_MEMORY_LACK;}, "copier", sizeof(esp_gmf_copier_t)); + esp_gmf_obj_t *obj = (esp_gmf_obj_t *)copier; + obj->new_obj = esp_gmf_copier_new; + obj->del_obj = esp_gmf_copier_destroy; + esp_gmf_err_t ret = ESP_GMF_ERR_OK; + if (config) { + esp_gmf_copier_cfg_t *cfg = esp_gmf_oal_calloc(1, sizeof(*config)); + ESP_GMF_MEM_VERIFY(TAG, cfg, {ret = ESP_GMF_ERR_MEMORY_LACK; goto _copier_init_fail;}, "copier configuration", sizeof(*config)); + memcpy(cfg, config, sizeof(*config)); + esp_gmf_obj_set_config(obj, cfg, sizeof(*config)); + } + ret = esp_gmf_obj_set_tag(obj, "copier"); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto _copier_init_fail, "Failed set OBJ tag"); + ESP_GMF_ELEMENT_GET(copier)->ops.open = esp_gmf_copier_open; + ESP_GMF_ELEMENT_GET(copier)->ops.process = esp_gmf_copier_process; + ESP_GMF_ELEMENT_GET(copier)->ops.close = esp_gmf_copier_close; + esp_gmf_element_cfg_t el_cfg = {0}; + ESP_GMF_ELEMENT_IN_PORT_ATTR_SET(el_cfg.in_attr, ESP_GMF_EL_PORT_CAP_SINGLE, 0, 0, + ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, ESP_GMF_ELEMENT_PORT_DATA_SIZE_DEFAULT); + ESP_GMF_ELEMENT_OUT_PORT_ATTR_SET(el_cfg.out_attr, ESP_GMF_EL_PORT_CAP_MULTI, 0, 0, + ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, ESP_GMF_ELEMENT_PORT_DATA_SIZE_DEFAULT); + el_cfg.dependency = false; + ret = esp_gmf_element_init(copier, &el_cfg); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto _copier_init_fail, "Failed init copier"); + *handle = obj; + return ESP_GMF_ERR_OK; +_copier_init_fail: + esp_gmf_obj_delete(obj); + return ret; +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_misc/idf_component.yml b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_misc/idf_component.yml new file mode 100644 index 0000000..13a5c2a --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_misc/idf_component.yml @@ -0,0 +1,13 @@ +version: "0.7.0" +description: Espressif GMF Misc Elements Module +url: https://github.com/espressif/esp-gmf/tree/main/elements/gmf_misc +documentation: "https://github.com/espressif/esp-gmf/blob/main/elements/gmf_misc/README.md" +issues: "https://github.com/espressif/esp-gmf/issues" + +tags: + - "gmf" + +dependencies: + espressif/gmf_core: + require: public + version: ">=0.7" diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_misc/include/esp_gmf_copier.h b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_misc/include/esp_gmf_copier.h new file mode 100644 index 0000000..96c4c12 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_misc/include/esp_gmf_copier.h @@ -0,0 +1,38 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#include "esp_gmf_element.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * @brief Configuration for the GMF copier. + */ +typedef struct { + uint8_t copy_num; /*!< Number of copies */ +} esp_gmf_copier_cfg_t; + +/** + * @brief Initializes the GMF copier using copier configuration. + * + * @param[in] config Pointer to the copier configuration + * @param[out] handle Pointer to the copier handle to be initialized + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid configuration provided + * - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory + */ +esp_gmf_err_t esp_gmf_copier_init(esp_gmf_copier_cfg_t *config, esp_gmf_obj_handle_t *handle); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/CHANGELOG.md b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/CHANGELOG.md new file mode 100644 index 0000000..7b94c1c --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/CHANGELOG.md @@ -0,0 +1,16 @@ +# Changelog + +## v0.7.1 + +### Features + +- Add QP and GOP setting support for video encoder element + +## v0.7.0 + +### Features + +This is the initial release of the `gmf_video` component, introducing the following features: + +* Core video processing elements: decoder, encoder, frame rate converter, overlay mixer, and hardware-accelerated Pixel Processing Accelerator (PPA) +* Software-based video effects: cropper, scaler, color converter, and rotator diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/CMakeLists.txt b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/CMakeLists.txt new file mode 100644 index 0000000..ead4307 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/CMakeLists.txt @@ -0,0 +1,8 @@ +if (${IDF_TARGET} STREQUAL "esp32p4") +list(APPEND COMPONENT_REQUIRES esp_driver_ppa esp_hw_support esp_mm) +endif() + +idf_component_register(SRC_DIRS "./" + INCLUDE_DIRS "./include" + REQUIRES ${COMPONENT_REQUIRES} + PRIV_INCLUDE_DIRS "./private_include") diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/LICENSE b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/LICENSE new file mode 100644 index 0000000..95aa434 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/LICENSE @@ -0,0 +1,22 @@ +Espressif Modified MIT License + +Copyright (c) 2025 Espressif Systems (Shanghai) CO., LTD + +Permission is hereby granted for use EXCLUSIVELY with Espressif Systems products. +This includes the right to use, copy, modify, merge, publish, distribute, and sublicense +the Software, subject to the following conditions: + +1. This Software MUST BE USED IN CONJUNCTION WITH ESPRESSIF SYSTEMS PRODUCTS. +2. The above copyright notice and this permission notice shall be included in all copies + or substantial portions of the Software. +3. Redistribution of the Software in source or binary form FOR USE WITH NON-ESPRESSIF PRODUCTS + is strictly prohibited. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/README.md b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/README.md new file mode 100644 index 0000000..ec3794b --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/README.md @@ -0,0 +1,93 @@ +# ESP-GMF-Video + +- [![Component Registry](https://components.espressif.com/components/espressif/gmf_video/badge.svg)](https://components.espressif.com/components/espressif/gmf_video) + +- [中文版](./README_CN.md) + +ESP GMF Video is a suite of video processing elements designed for a variety of tasks such as video encoding, and applying video effects. These elements can be combined to form a complete video processing pipeline. + +## Supported Video Modules + +The following table lists the currently supported video processing modules and their detailed information: + +|Name|TAG|Function|Method|Input Port|Output Port|Hardware Acceleration|Dependent on Video Information| +|:----:|:----:|:-----:|:----:|:----:|:----:|:----:|:----| +| VIDEO_ENC | vid_enc | Video encoder: H264, MJPEG | `set_bitrate`
    `set_dst_codec`
    `get_src_fmts`
    `preset`
    `get_frame_size` | Single | Single | Yes | Yes | +| VIDEO_DEC | vid_dec | Video decoder: H264, MJPEG | `set_dst_fmt`
    `set_src_codec`
    `get_dst_fmts` | Single | Single | No | No | +| VIDEO_PPA | vid_ppa | Pixel processing accelerator: Color conversion,
    Scaling, Cropping, Rotation | `set_dst_format`
    `set_dst_resolution`
    `set_rotation`
    `set_cropped_rgn` | Single | Single | Yes | Yes | +| FPS_CVT | vid_fps_cvt | Frame rate conversion | `set_fps` | Single | Single | No | Yes | +| OVERLAY_MIXER | vid_overlay | Video overlay mixer | `overlay_enable`
    `set_rgn`
    `set_port`
    `set_alpha` | Multiple | Single | No | Yes | +| COLOR_CVT | vid_color_cvt | Software color conversion | `set_dst_fmt` | Single | Single | No | Yes | +| CROP | vid_crop | Software video cropping | `set_crop_rgn` | Single | Single | No | Yes | +| SCALE | vid_scale | Software video scaling | `set_dst_res` | Single | Single | No | Yes | +| ROTATE | vid_rotate | Software video rotation | `set_angle` | Single | Single | No | Yes | + +## Elements + +### Video Encoder +The Video Encoder element uses the `esp_video_codec` to convert raw video frames into compressed video streams. It currently supports two codecs: +- **H264** +- **MJPEG** + +### Video Decoder +The video decoder element use the `esp_video_codec` to convert compressed video streams into raw video frames. It currently supports two codecs: +- **H264** +- **MJPEG** + +### Video PPA (Pixel Processing Accelerator) +The Video PPA element is a compact element, support multiple functions. It is currently available on the ESP32P4 board and includes following functionalities: +- **Color Conversion:** + The ESP32P4 supports color conversion through two hardware modules: + - **2D-DMA:** Automatically selected for better efficiency if supported. + - **PPA:** Used as a fallback when 2D-DMA not supported +- **Resizing:** + Resizing functionality is provided by the PPA module. +- **Cropping:** + Cropping functionality is provided by the PPA module. +- **Rotation:** + Supports rotations at 0°, 90°, 180°, and 270°. + +### Video FPS Converter +This module adjusts the frame rate of the video. It decreases the input frame rate to a specified output rate, using the Presentation Time Stamp (PTS) embedded in the input data to accurately schedule frames. + +### Video Overlay Mixer +The Video Overlay Mixer module allows users to overlay additional graphics onto a video frame. By receiving overlay data via a user-defined port, it can blend elements such as timestamps, watermarks, or other images into a designated region of the original video frame. + +### Video Pixel Processor Elements +Following elements are wrapped for [Video Pixel Processor](https://github.com/espressif/esp-adf-libs/tree/master/esp_image_effects) which implemented software video processing. + +#### Video Color Converter +Element to do software color conversion for video image + +#### Video Cropper +Element to do software video cropper for video image + +#### Video Scaler +Element to do software video scaler for video image + +#### Video Rotator +Element to do software video rotator for video image + +## ESP-GMF-Video Release and SoC Compatibility + +The following table summarizes the support for ESP-GMF-Video elements across Espressif SoCs in the current release. +A check mark (✔) indicates that the element is supported, while a cross mark (✖) indicates it is not supported. + +| Element | ESP32 | ESP32-S2 | ESP32-S3 | ESP32-P4 | +|-----------------|:-----------:|:-----------:|:-----------:|:-----------:| +| Video PPA | ✖ | ✖ | ✖ | ✔ | +| FPS Converter | ✔ | ✔ | ✔ | ✔ | +| Overlay Mixer | ✔ | ✔ | ✔ | ✔ | +| Video Decoder | MJPEG only | MJPEG only | ✔ | ✔ | +| Video Encoder | MJPEG only | MJPEG only | ✔ | ✔ | +| Video Pixel Processor | ✔ | ✔ | ✔ | ✔ | + +## Notes + +- **Video PPA** is currently supported **only** on the ESP32-P4. +- **FPS Converter** and **Overlay Mixer** are supported on **all SoCs**. +- **Video Decoder** supports MJPEG decoding on ESP32 and ESP32-S2 +- **Video Encoder** supports MJPEG encoding on ESP32 and ESP32-S2 + +## Usage +ESP GMF Video modules are often used together to build a complete video processing pipeline. For example, you might first convert its colors or size, adjust the frame rate, and overlay and finally output through video encoder. For a practical implementation, please refer to the example in [test_app](../test_apps/main/elements/gmf_video_el_test.c). diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/README_CN.md b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/README_CN.md new file mode 100644 index 0000000..82ffb06 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/README_CN.md @@ -0,0 +1,93 @@ +# ESP-GMF-Video + +- [![Component Registry](https://components.espressif.com/components/espressif/gmf_video/badge.svg)](https://components.espressif.com/components/espressif/gmf_video) + +- [English Version](./README.md) + +ESP GMF Video 是一套专为视频编解码和视频转换等设计的视频处理模块。这些模块可以组合在一起,形成完整的视频处理流水线。 + +## 支持的视频模块 + +下表列出了当前支持的视频处理模块及其详细信息: + +| 名称 | 标签 | 功能 | 函数方法 | 输入端口 | 输出端口 | 硬件加速 |依赖视频信息 | +|:----:|:-----:|:----:|:----:|:----:|:----:|:----:|:----| +| VIDEO_ENC | vid_enc | 视频编码器:H264, MJPEG | `set_bitrate`
    `set_dst_codec`
    `get_src_fmts`
    `preset`
    `get_frame_size` | 单个 | 单个 | 是 | 是 | +| VIDEO_DEC | vid_dec | 视频解码器:H264, MJPEG | `set_dst_fmt`
    `set_src_codec`
    `get_dst_fmts` | 单个 | 单个 | 否 | 否 | +| VIDEO_PPA | vid_ppa | 像素处理加速器:颜色转换,缩放,
    裁剪,旋转 | `set_dst_format`
    `set_dst_resolution`
    `set_rotation`
    `set_cropped_rgn` | 单个 | 单个 | 是 | 是 | +| FPS_CVT | vid_fps_cvt | 帧率转换 | `set_fps` | 单个 | 单个 | 否 | 是 | +| OVERLAY_MIXER | vid_overlay | 视频叠加混合器 | `overlay_enable`
    `set_rgn`
    `set_port`
    `set_alpha` | 多个 | 单个 | 否 | 是 | +| COLOR_CVT | vid_color_cvt | 软件颜色转换 | `set_dst_fmt` | 单个 | 单个 | 否 | 是 | +| CROP | vid_crop | 软件视频裁剪 | `set_crop_rgn` | 单个 | 单个 | 否 | 是 | +| SCALE | vid_scale | 软件视频缩放 | `set_dst_res` | 单个 | 单个 | 否 | 是 | +| ROTATE | vid_rotate | 软件视频旋转 | `set_angle` | 单个 | 单个 | 否 | 是 | + +## 元素 + +### 视频编码器 +视频编码器模块使用 `esp_video_codec` 将原始视频帧转换为压缩视频流。目前支持以下两种编码格式: +- **H264** +- **MJPEG** + +### 视频解码器 +视频编码器模块使用 `esp_video_codec` 将压缩视频流解码为原始视频帧。目前支持以下两种解码格式: +- **H264** +- **MJPEG** + +### 视频像素加速器 (PPA) +视频像素加速器模块是一个复合模块,目前仅适用于 ESP32P4 ,包含以下功能: +- **颜色转换:** + ESP32P4 通过两个硬件模块支持颜色转换: + - **2D-DMA:** 如果支持,将自动选择以提高效率。 + - **PPA:** 当 2D-DMA 不支持时作为备用方案。 +- **缩放:** + 由 PPA 模块提供缩放功能。 +- **裁剪:** + 由 PPA 模块提供裁剪功能。 +- **旋转:** + 支持 0°、90°、180° 和 270° 旋转。 + +### 视频帧率转换 +此模块用于调整视频的帧率。它会根据输入数据中嵌入的演示时间戳(PTS)来准确地调整视频帧,从而将输入帧率降低到指定的输出帧率。 + +### 视频叠加混合器 +视频叠加混合器模块允许用户在视频帧上叠加额外的图像。它通过用户定义的端口接收叠加数据,并将诸如时间戳、水印或其他图像等元素混合到原始视频帧的指定区域。 + +### 视频像素处理器元素 +以下元素是为[视频像素处理器](https://github.com/espressif/esp-adf-libs/tree/master/esp_image_effects)包装的,它实现了软件视频处理。 + +#### 视频彩色转换器 +软件实现视频图像不同颜色转换 + +#### 视频剪辑器 +软件实现视频图像任意位置及大小的裁剪 + +#### 视频缩放器 +软件实现视频图像放大和缩小 + +#### 视频旋转器 +软件实现视频图像任意角度的旋转 + +# ESP-GMF-Video SoC 兼容性 + +下表总结了当前版本中 ESP-GMF-Video 元素在乐鑫各 SoC 上的支持情况。 +✔ 表示支持,✖ 表示不支持。 + +| 元素 | ESP32 | ESP32-S2 | ESP32-S3 | ESP32-P4 | +|----------------|:-----------:|:-----------:|:-----------:|:-----------:| +| 视频像素加速器 | ✖ | ✖ | ✖ | ✔ | +| 帧率转换器 | ✔ | ✔ | ✔ | ✔ | +| 叠加混合器 | ✔ | ✔ | ✔ | ✔ | +| 视频解码器 | 仅支持 MJPEG | 仅支持 MJPEG | ✔ | ✔ | +| 视频编码器 | 仅支持 MJPEG | 仅支持 MJPEG | ✔ | ✔ | +| 视频像素处理器 | ✔ | ✔ | ✔ | ✔ | + +## 备注 + +- **视频像素加速器** 目前仅在 **ESP32-P4** 上支持。 +- **帧率转换器** 和 **叠加混合器** 在**所有 SoC**上均支持。 +- **视频解码器** 在 ESP32 和 ESP32-S2 上仅支持 MJPEG 解码。 +- **视频编码器** 在 ESP32 和 ESP32-S2 上仅支持 MJPEG 编码。 + +## 使用方法 +ESP GMF Video 模块通常组合使用,以构建完整的视频处理流水线。例如,您可以先调整帧率,然后转换颜色或调整大小,叠加特效,最终通过视频编码器输出。有关具体用法,请参考 [test_app](../test_apps/main/elements/gmf_video_el_test.c) 示例。 diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/esp_gmf_video_color_convert.c b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/esp_gmf_video_color_convert.c new file mode 100644 index 0000000..bab1e01 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/esp_gmf_video_color_convert.c @@ -0,0 +1,314 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#include +#include +#include "esp_log.h" +#include "esp_gmf_oal_mutex.h" +#include "esp_gmf_oal_mem.h" +#include "esp_gmf_err.h" +#include "esp_gmf_node.h" +#include "esp_gmf_video_element.h" +#include "esp_gmf_video_color_convert.h" +#include "esp_gmf_video_methods_def.h" +#include "gmf_video_common.h" +#include "esp_gmf_caps_def.h" + +static const char *TAG = "IMGFX_CLRCVT_EL"; + +typedef struct _gmf_imgfx_color_convert_t { + esp_gmf_video_element_t parent; + esp_imgfx_color_convert_handle_t hd; + bool need_recfg; +} esp_gmf_color_convert_hd_t; + +static esp_gmf_job_err_t video_cc_el_open(esp_gmf_element_handle_t self, void *para) +{ + esp_gmf_color_convert_hd_t *video_el = (esp_gmf_color_convert_hd_t *)self; + // Get and check configuration + esp_imgfx_color_convert_cfg_t *cfg = (esp_imgfx_color_convert_cfg_t *)OBJ_GET_CFG(self); + ESP_GMF_CHECK(TAG, cfg, return ESP_GMF_JOB_ERR_FAIL, "Failed to get color convert config"); + // Open color convert module + esp_imgfx_color_convert_open(cfg, &video_el->hd); + ESP_GMF_CHECK(TAG, video_el->hd, return ESP_GMF_JOB_ERR_FAIL, "Failed to create color convert handle"); + // Get video size + esp_imgfx_get_image_size(cfg->in_pixel_fmt, &cfg->in_res, (uint32_t *)&(ESP_GMF_ELEMENT_GET(video_el)->in_attr.data_size)); + esp_imgfx_get_image_size(cfg->out_pixel_fmt, &cfg->in_res, (uint32_t *)&(ESP_GMF_ELEMENT_GET(video_el)->out_attr.data_size)); + // Report information to the next element, the next element will use this information to configure + gmf_video_update_info(self, cfg->in_res.width, cfg->in_res.height, cfg->out_pixel_fmt); + // The video_el->hd has been opened using newest configuration, so it can set the need_recfg to false + video_el->need_recfg = false; + ESP_LOGD(TAG, "Open, %p", self); + return ESP_GMF_JOB_ERR_OK; +} + +static esp_gmf_job_err_t video_cc_el_process(esp_gmf_element_handle_t self, void *para) +{ + esp_gmf_color_convert_hd_t *video_el = (esp_gmf_color_convert_hd_t *)self; + esp_imgfx_data_t in_image; + esp_imgfx_data_t out_image; + esp_imgfx_color_convert_cfg_t *cfg = (esp_imgfx_color_convert_cfg_t *)OBJ_GET_CFG(self); + ESP_GMF_NULL_CHECK(TAG, cfg, return ESP_GMF_JOB_ERR_FAIL); + bool bypass = cfg->in_pixel_fmt == cfg->out_pixel_fmt; + if (video_el->need_recfg) { + // reset color convert config + esp_imgfx_err_t imgfx_ret = esp_imgfx_color_convert_set_cfg(video_el->hd, cfg); + if (imgfx_ret != ESP_IMGFX_ERR_OK) { + return ESP_GMF_JOB_ERR_FAIL; + } + // Get video size + esp_imgfx_get_image_size(cfg->in_pixel_fmt, &cfg->in_res, (uint32_t *)&ESP_GMF_ELEMENT_GET(video_el)->in_attr.data_size); + esp_imgfx_get_image_size(cfg->out_pixel_fmt, &cfg->in_res, (uint32_t *)&ESP_GMF_ELEMENT_GET(video_el)->out_attr.data_size); + video_el->need_recfg = false; + } + esp_gmf_port_handle_t in_port = ESP_GMF_ELEMENT_GET(self)->in; + esp_gmf_port_handle_t out_port = ESP_GMF_ELEMENT_GET(self)->out; + esp_gmf_payload_t *in_load = NULL; + esp_gmf_payload_t *out_load = NULL; + esp_gmf_job_err_t ret = ESP_GMF_JOB_ERR_OK; + esp_gmf_err_io_t load_ret = ESP_GMF_IO_OK; + ret = video_el_acquire_payload(in_port, out_port, &in_load, &out_load, ESP_GMF_ELEMENT_GET(video_el)->in_attr.data_size, + ESP_GMF_ELEMENT_GET(video_el)->out_attr.data_size, bypass); + if (ret != ESP_GMF_JOB_ERR_OK) { + goto __release; + } + /* It is for bypass */ + if (in_load == out_load) { + goto __release; + } + /* finished */ + if (in_load->is_done) { + out_load->is_done = in_load->is_done; + out_load->pts = in_load->pts; + ret = ESP_GMF_JOB_ERR_DONE; + ESP_LOGD(TAG, "It's done, out: %d", in_load->valid_size); + goto __release; + } + in_image.data = in_load->buf; + in_image.data_len = in_load->valid_size; + out_image.data = out_load->buf; + out_image.data_len = out_load->buf_length; + esp_imgfx_err_t imgfx_ret = esp_imgfx_color_convert_process(video_el->hd, &in_image, &out_image); + if (imgfx_ret != ESP_IMGFX_ERR_OK) { + ESP_LOGE(TAG, "Image effects color convert process failed, ret: %d-%p", imgfx_ret, video_el); + ret = ESP_GMF_JOB_ERR_FAIL; + goto __release; + } + // Copy the information to the output payload. The next element will use this information to process + out_load->is_done = in_load->is_done; + out_load->valid_size = ESP_GMF_ELEMENT_GET(video_el)->out_attr.data_size; + out_load->pts = in_load->pts; +__release: + // Release in and out port + if (out_load != NULL) { + load_ret = esp_gmf_port_release_out(out_port, out_load, ESP_GMF_MAX_DELAY); + if ((load_ret < ESP_GMF_IO_OK) && (load_ret != ESP_GMF_IO_ABORT)) { + ESP_LOGE(TAG, "OUT port release error, ret:%d", load_ret); + ret = ESP_GMF_JOB_ERR_FAIL; + } + } + if (in_load != NULL) { + load_ret = esp_gmf_port_release_in(in_port, in_load, ESP_GMF_MAX_DELAY); + if ((load_ret < ESP_GMF_IO_OK) && (load_ret != ESP_GMF_IO_ABORT)) { + ESP_LOGE(TAG, "IN port release error, ret:%d", load_ret); + ret = ESP_GMF_JOB_ERR_FAIL; + } + } + return ret; +} + +static esp_gmf_job_err_t video_cc_el_close(esp_gmf_element_handle_t self, void *para) +{ + ESP_LOGD(TAG, "Closed, %p", self); + esp_gmf_color_convert_hd_t *video_el = (esp_gmf_color_convert_hd_t *)self; + if (video_el) { + if (video_el->hd) { + esp_imgfx_color_convert_close(video_el->hd); + video_el->hd = NULL; + } + } + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t video_cc_el_delete(esp_gmf_obj_handle_t handle) +{ + ESP_LOGD(TAG, "Deleted, %p", handle); + void *cfg = OBJ_GET_CFG(handle); + if (cfg) { + esp_gmf_oal_free(cfg); + } + esp_gmf_video_el_deinit(handle); + esp_gmf_oal_free(handle); + return ESP_GMF_ERR_OK; +} + +static inline esp_gmf_err_t esp_gmf_video_color_convert_set_cfg(esp_gmf_element_handle_t self, esp_imgfx_color_convert_cfg_t *config) +{ + ESP_GMF_NULL_CHECK(TAG, self, return ESP_GMF_JOB_ERR_FAIL); + ESP_GMF_NULL_CHECK(TAG, config, return ESP_GMF_JOB_ERR_FAIL); + esp_gmf_color_convert_hd_t *video_el = (esp_gmf_color_convert_hd_t *)self; + esp_gmf_obj_t *obj = (esp_gmf_obj_t *)video_el; + // reset obj config + if (obj->cfg) { + memcpy(obj->cfg, config, sizeof(esp_imgfx_color_convert_cfg_t)); + video_el->need_recfg = true; + return ESP_GMF_JOB_ERR_OK; + } + ESP_LOGE(TAG, " %s-%p is no configured yet", __func__, self); + return ESP_GMF_JOB_ERR_FAIL; +} + +static inline esp_gmf_err_t esp_gmf_video_color_convert_get_cfg(esp_gmf_element_handle_t self, esp_imgfx_color_convert_cfg_t *config) +{ + ESP_GMF_NULL_CHECK(TAG, self, return ESP_GMF_JOB_ERR_FAIL); + esp_gmf_color_convert_hd_t *video_el = (esp_gmf_color_convert_hd_t *)self; + // If IMGFX color converter opened, get it from it or-else get from object + if (video_el->hd) { + esp_imgfx_err_t imgfx_ret = esp_imgfx_color_convert_get_cfg(video_el->hd, config); + if (imgfx_ret != ESP_IMGFX_ERR_OK) { + ESP_LOGE(TAG, "Get video effects color convert cfg failed, hd:%p, ret: %d", self, imgfx_ret); + return ESP_GMF_JOB_ERR_FAIL; + } + return ESP_GMF_JOB_ERR_OK; + } else { + esp_gmf_obj_t *obj = (esp_gmf_obj_t *)video_el; + // Get obj config + if (obj->cfg) { + memcpy(config, obj->cfg, sizeof(esp_imgfx_color_convert_cfg_t)); + return ESP_GMF_JOB_ERR_OK; + } + } + ESP_LOGE(TAG, " %s-%p is no configured yet", __func__, self); + return ESP_GMF_JOB_ERR_FAIL; +} + +static esp_gmf_err_t video_set_dst_format(void *handle, esp_gmf_args_desc_t *arg_desc, uint8_t *buf, int buf_len) +{ + ESP_GMF_NULL_CHECK(TAG, buf, return ESP_GMF_ERR_INVALID_ARG); + return esp_gmf_video_color_convert_dst_format(handle, *(esp_imgfx_pixel_fmt_t *)buf); +} + +static esp_gmf_err_t video_cc_el_load_caps(esp_gmf_element_handle_t handle) +{ + esp_gmf_cap_t *caps = NULL; + esp_gmf_cap_t dec_caps = {0}; + dec_caps.cap_eightcc = ESP_GMF_CAPS_VIDEO_COLOR_CONVERT; + dec_caps.attr_fun = NULL; + int ret = esp_gmf_cap_append(&caps, &dec_caps); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, return ret, "Failed to create capability"); + esp_gmf_element_t *el = (esp_gmf_element_t *)handle; + el->caps = caps; + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t video_cc_el_load_methods(esp_gmf_element_handle_t handle) +{ + esp_gmf_method_t *method = NULL; + esp_gmf_err_t ret = ESP_GMF_ERR_OK; + esp_gmf_args_desc_t *set_args = NULL; + ret = esp_gmf_args_desc_append(&set_args, VMETHOD_ARG(CLR_CVT, SET_DST_FMT, FMT), ESP_GMF_ARGS_TYPE_INT8, sizeof(esp_imgfx_pixel_fmt_t), 0); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, return ret, "Failed to append color convert destination format"); + ret = esp_gmf_method_append(&method, VMETHOD(CLR_CVT, SET_DST_FMT), video_set_dst_format, set_args); + ESP_GMF_RET_ON_ERROR(TAG, ret, return ret, "Failed to register %s method", VMETHOD(CLR_CVT, SET_DST_FMT)); + esp_gmf_element_t *el = (esp_gmf_element_t *)handle; + el->method = method; + return ret; +} + +static esp_gmf_err_t video_cc_el_received_event_handler(esp_gmf_event_pkt_t *evt, void *ctx) +{ + ESP_GMF_NULL_CHECK(TAG, ctx, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, evt, return ESP_GMF_ERR_INVALID_ARG); + if ((evt->type != ESP_GMF_EVT_TYPE_REPORT_INFO) + || (evt->sub != ESP_GMF_INFO_VIDEO) + || (evt->payload == NULL)) { + return ESP_GMF_ERR_OK; + } + esp_gmf_element_handle_t self = (esp_gmf_element_handle_t)ctx; + esp_gmf_element_handle_t el = evt->from; + esp_gmf_event_state_t state = ESP_GMF_EVENT_STATE_NONE; + esp_gmf_element_get_state(self, &state); + esp_gmf_info_video_t *info = (esp_gmf_info_video_t *)evt->payload; + esp_gmf_video_el_set_src_info(self, info); + esp_imgfx_color_convert_cfg_t *config = (esp_imgfx_color_convert_cfg_t *)OBJ_GET_CFG(self); + ESP_GMF_NULL_CHECK(TAG, config, return ESP_GMF_ERR_FAIL); + esp_gmf_color_convert_hd_t *video_el = (esp_gmf_color_convert_hd_t *)self; + GMF_VIDEO_UPDATE_CONFIG(config, info, video_el->need_recfg); + ESP_LOGD(TAG, "RECV element info, from: %s-%p, next: %p, self: %s-%p, type: %x, state: %s, width: %d, height: %d, pixel format: %lx", + OBJ_GET_TAG(el), el, esp_gmf_node_for_next((esp_gmf_node_t *)el), OBJ_GET_TAG(self), self, evt->type, + esp_gmf_event_get_state_str(state), info->width, info->height, info->format_id); + // It is for the first time to receive event from previous element + if (state == ESP_GMF_EVENT_STATE_NONE) { + esp_gmf_element_set_state(self, ESP_GMF_EVENT_STATE_INITIALIZED); + } + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t video_cc_el_new(void *config, esp_gmf_obj_handle_t *handle) +{ + return esp_gmf_video_color_convert_init((esp_imgfx_color_convert_cfg_t *)config, handle); +} + +esp_gmf_err_t esp_gmf_video_color_convert_init(esp_imgfx_color_convert_cfg_t *config, esp_gmf_obj_handle_t *handle) +{ + ESP_GMF_MEM_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + *handle = NULL; + esp_gmf_color_convert_hd_t *video_el = esp_gmf_oal_calloc(1, sizeof(esp_gmf_color_convert_hd_t)); + ESP_GMF_MEM_CHECK(TAG, video_el, return ESP_GMF_ERR_MEMORY_LACK); + esp_gmf_err_t ret = ESP_GMF_ERR_OK; + esp_gmf_obj_t *obj = (esp_gmf_obj_t *)video_el; + // Set element tag + ret = esp_gmf_obj_set_tag(video_el, "vid_color_cvt"); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto __init_exit, "Failed set OBJ tag"); + // Configure object callbacks + obj->new_obj = video_cc_el_new; + obj->del_obj = video_cc_el_delete; + // Set element config + esp_imgfx_color_convert_cfg_t *cfg = esp_gmf_oal_calloc(1, sizeof(esp_imgfx_color_convert_cfg_t)); + ESP_GMF_MEM_VERIFY(TAG, cfg, {ret = ESP_GMF_ERR_MEMORY_LACK; goto __init_exit;}, "color convert configuration", sizeof(esp_imgfx_color_convert_cfg_t)); + esp_gmf_obj_set_config(obj, cfg, sizeof(esp_imgfx_color_convert_cfg_t)); + if (config) { + memcpy(cfg, config, sizeof(esp_imgfx_color_convert_cfg_t)); + } else { + esp_imgfx_color_convert_cfg_t dcfg = DEFAULT_ESP_GMF_COLOR_CONVERT_CONFIG(); + memcpy(cfg, &dcfg, sizeof(esp_imgfx_color_convert_cfg_t)); + } + // Configure element + esp_gmf_element_cfg_t el_cfg = { + .dependency = true, + }; + ESP_GMF_ELEMENT_IN_PORT_ATTR_SET(el_cfg.in_attr, ESP_GMF_EL_PORT_CAP_SINGLE, 0, 0, + ESP_GMF_PORT_TYPE_BLOCK, ESP_GMF_ELEMENT_PORT_DATA_SIZE_DEFAULT); + ESP_GMF_ELEMENT_OUT_PORT_ATTR_SET(el_cfg.out_attr, ESP_GMF_EL_PORT_CAP_SINGLE, 0, 0, + ESP_GMF_PORT_TYPE_BLOCK, ESP_GMF_ELEMENT_PORT_DATA_SIZE_DEFAULT); + // Initialize element + ret = esp_gmf_video_el_init(video_el, &el_cfg); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto __init_exit, "Failed initialize video element"); + // Register element's callbacks + esp_gmf_video_element_t *parent = (esp_gmf_video_element_t *)video_el; + parent->base.ops.open = video_cc_el_open; + parent->base.ops.process = video_cc_el_process; + parent->base.ops.close = video_cc_el_close; + parent->base.ops.event_receiver = video_cc_el_received_event_handler; + parent->base.ops.load_caps = video_cc_el_load_caps; + parent->base.ops.load_methods = video_cc_el_load_methods; + *handle = (esp_gmf_obj_handle_t)video_el; + ESP_LOGD(TAG, "Initialization, %s-%p", OBJ_GET_TAG(obj), obj); + return ret; +__init_exit: + video_cc_el_delete(video_el); + return ret; +} + +esp_gmf_err_t esp_gmf_video_color_convert_dst_format(esp_gmf_element_handle_t handle, uint32_t format) +{ + esp_imgfx_color_convert_cfg_t config; + esp_gmf_err_t ret = esp_gmf_video_color_convert_get_cfg(handle, &config); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, return ret, "Failed to set destination pixel format"); + config.out_pixel_fmt = format; + return esp_gmf_video_color_convert_set_cfg(handle, &config); +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/esp_gmf_video_crop.c b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/esp_gmf_video_crop.c new file mode 100644 index 0000000..b9a8243 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/esp_gmf_video_crop.c @@ -0,0 +1,324 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#include +#include +#include "esp_log.h" +#include "esp_gmf_oal_mutex.h" +#include "esp_gmf_oal_mem.h" +#include "esp_gmf_err.h" +#include "esp_gmf_node.h" +#include "esp_gmf_video_crop.h" +#include "esp_gmf_video_methods_def.h" +#include "esp_gmf_video_element.h" +#include "gmf_video_common.h" +#include "esp_gmf_video_types.h" +#include "esp_gmf_caps_def.h" + +static const char *TAG = "IMGFX_CROP_EL"; + +typedef struct _gmf_imgfx_crop_t { + esp_gmf_video_element_t parent; + esp_imgfx_crop_handle_t hd; + bool need_recfg; +} esp_gmf_crop_hd_t; + +static esp_gmf_job_err_t video_crop_el_open(esp_gmf_element_handle_t self, void *para) +{ + esp_gmf_crop_hd_t *video_el = (esp_gmf_crop_hd_t *)self; + // Get and check config + esp_imgfx_crop_cfg_t *cfg = (esp_imgfx_crop_cfg_t *)OBJ_GET_CFG(self); + ESP_GMF_CHECK(TAG, cfg, return ESP_GMF_JOB_ERR_FAIL, "Failed to get crop config"); + // Open crop module + esp_imgfx_crop_open(cfg, &video_el->hd); + ESP_GMF_CHECK(TAG, video_el->hd, return ESP_GMF_JOB_ERR_FAIL, "Failed to create crop handle"); + // Get video size + esp_imgfx_get_image_size(cfg->in_pixel_fmt, &cfg->in_res, (uint32_t *)&(ESP_GMF_ELEMENT_GET(video_el)->in_attr.data_size)); + esp_imgfx_get_image_size(cfg->in_pixel_fmt, &cfg->cropped_res, (uint32_t *)&(ESP_GMF_ELEMENT_GET(video_el)->out_attr.data_size)); + // Report information to the next element, the next element will use this information to configure + gmf_video_update_info(self, cfg->cropped_res.width, cfg->cropped_res.height, cfg->in_pixel_fmt); + // The video_el->hd has been opened using newest configuration, so it can set the need_recfg to false + video_el->need_recfg = false; + ESP_LOGD(TAG, "Open, %p", self); + return ESP_GMF_JOB_ERR_OK; +} + +static esp_gmf_job_err_t video_crop_el_process(esp_gmf_element_handle_t self, void *para) +{ + esp_gmf_crop_hd_t *video_el = (esp_gmf_crop_hd_t *)self; + esp_imgfx_data_t in_image; + esp_imgfx_data_t out_image; + if (video_el->need_recfg) { + esp_imgfx_crop_cfg_t *cfg = (esp_imgfx_crop_cfg_t *)OBJ_GET_CFG(self); + // Reset crop configuration + esp_imgfx_err_t imgfx_ret = esp_imgfx_crop_set_cfg(video_el->hd, cfg); + if (imgfx_ret != ESP_IMGFX_ERR_OK) { + return ESP_GMF_JOB_ERR_FAIL; + } + // Get video size + esp_imgfx_get_image_size(cfg->in_pixel_fmt, &cfg->in_res, (uint32_t *)&(ESP_GMF_ELEMENT_GET(video_el)->in_attr.data_size)); + esp_imgfx_get_image_size(cfg->in_pixel_fmt, &cfg->cropped_res, (uint32_t *)&(ESP_GMF_ELEMENT_GET(video_el)->out_attr.data_size)); + video_el->need_recfg = false; + } + esp_gmf_port_handle_t in_port = ESP_GMF_ELEMENT_GET(self)->in; + esp_gmf_port_handle_t out_port = ESP_GMF_ELEMENT_GET(self)->out; + esp_gmf_payload_t *in_load = NULL; + esp_gmf_payload_t *out_load = NULL; + esp_gmf_job_err_t ret = ESP_GMF_JOB_ERR_OK; + esp_gmf_err_io_t load_ret = ESP_GMF_IO_OK; + ret = video_el_acquire_payload(in_port, out_port, &in_load, &out_load, (ESP_GMF_ELEMENT_GET(video_el)->in_attr.data_size), (ESP_GMF_ELEMENT_GET(video_el)->out_attr.data_size), + ((ESP_GMF_ELEMENT_GET(video_el)->in_attr.data_size) == (ESP_GMF_ELEMENT_GET(video_el)->out_attr.data_size))); + if (ret != ESP_GMF_JOB_ERR_OK) { + goto __release; + } + /* It is for bypass */ + if (in_load == out_load) { + goto __release; + } + if (in_load->is_done) { + out_load->is_done = in_load->is_done; + out_load->pts = in_load->pts; + ret = ESP_GMF_JOB_ERR_DONE; + ESP_LOGD(TAG, "It's done, out: %d", in_load->valid_size); + goto __release; + } + in_image.data = in_load->buf; + in_image.data_len = in_load->valid_size; + out_image.data = out_load->buf; + out_image.data_len = out_load->buf_length; + esp_imgfx_err_t imgfx_ret = esp_imgfx_crop_process(video_el->hd, &in_image, &out_image); + if (imgfx_ret != ESP_IMGFX_ERR_OK) { + ESP_LOGE(TAG, "Image effects crop process failed, ret: %d-%p", imgfx_ret, video_el); + ret = ESP_GMF_JOB_ERR_FAIL; + goto __release; + } + // Copy the information to the output payload. The next element will use this information to process + out_load->is_done = in_load->is_done; + out_load->valid_size = (ESP_GMF_ELEMENT_GET(video_el)->out_attr.data_size); + out_load->pts = in_load->pts; +__release: + // Release in and out port + if (out_load != NULL) { + load_ret = esp_gmf_port_release_out(out_port, out_load, ESP_GMF_MAX_DELAY); + if ((load_ret < ESP_GMF_IO_OK) && (load_ret != ESP_GMF_IO_ABORT)) { + ESP_LOGE(TAG, "OUT port release error, ret:%d", load_ret); + ret = ESP_GMF_JOB_ERR_FAIL; + } + } + if (in_load != NULL) { + load_ret = esp_gmf_port_release_in(in_port, in_load, ESP_GMF_MAX_DELAY); + if ((load_ret < ESP_GMF_IO_OK) && (load_ret != ESP_GMF_IO_ABORT)) { + ESP_LOGE(TAG, "IN port release error, ret:%d", load_ret); + ret = ESP_GMF_JOB_ERR_FAIL; + } + } + return ret; +} + +static esp_gmf_job_err_t video_crop_el_close(esp_gmf_element_handle_t self, void *para) +{ + ESP_LOGD(TAG, "Closed, %p", self); + esp_gmf_crop_hd_t *video_el = (esp_gmf_crop_hd_t *)self; + if (video_el) { + if (video_el->hd) { + esp_imgfx_crop_close(video_el->hd); + video_el->hd = NULL; + } + } + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t video_crop_el_delete(esp_gmf_obj_handle_t handle) +{ + ESP_LOGD(TAG, "Deleted, %p", handle); + void *cfg = OBJ_GET_CFG(handle); + if (cfg) { + esp_gmf_oal_free(cfg); + } + esp_gmf_video_el_deinit(handle); + esp_gmf_oal_free(handle); + return ESP_GMF_ERR_OK; +} + +static inline esp_gmf_err_t esp_gmf_video_crop_set_cfg(esp_gmf_element_handle_t self, esp_imgfx_crop_cfg_t *config) +{ + ESP_GMF_NULL_CHECK(TAG, self, return ESP_GMF_JOB_ERR_FAIL); + ESP_GMF_NULL_CHECK(TAG, config, return ESP_GMF_JOB_ERR_FAIL); + esp_gmf_crop_hd_t *video_el = (esp_gmf_crop_hd_t *)self; + esp_gmf_obj_t *obj = (esp_gmf_obj_t *)video_el; + // reset obj config + if (obj->cfg) { + memcpy(obj->cfg, config, sizeof(esp_imgfx_crop_cfg_t)); + video_el->need_recfg = true; + return ESP_GMF_JOB_ERR_OK; + } + ESP_LOGE(TAG, " %s-%p is no configured yet", __func__, self); + return ESP_GMF_JOB_ERR_FAIL; +} + +static inline esp_gmf_err_t esp_gmf_video_crop_get_cfg(esp_gmf_element_handle_t self, esp_imgfx_crop_cfg_t *config) +{ + ESP_GMF_NULL_CHECK(TAG, self, return ESP_GMF_JOB_ERR_FAIL); + esp_gmf_crop_hd_t *video_el = (esp_gmf_crop_hd_t *)self; + if (video_el->hd) { + esp_imgfx_err_t imgfx_ret = esp_imgfx_crop_get_cfg(video_el->hd, config); + if (imgfx_ret != ESP_IMGFX_ERR_OK) { + ESP_LOGE(TAG, "Failed to get video crop cfg, hd:%p, ret: %d", self, imgfx_ret); + return ESP_GMF_JOB_ERR_FAIL; + } + return ESP_GMF_JOB_ERR_OK; + } else { + esp_gmf_obj_t *obj = (esp_gmf_obj_t *)video_el; + // Get obj config + if (obj->cfg) { + memcpy(config, obj->cfg, sizeof(esp_imgfx_crop_cfg_t)); + return ESP_GMF_JOB_ERR_OK; + } + } + ESP_LOGE(TAG, " %s-%p is no configured yet", __func__, self); + return ESP_GMF_JOB_ERR_FAIL; +} + +static esp_gmf_err_t video_set_dst_rgn(void *handle, esp_gmf_args_desc_t *arg_desc, uint8_t *buf, int buf_len) +{ + ESP_GMF_MEM_CHECK(TAG, buf, return ESP_GMF_ERR_INVALID_ARG); + return esp_gmf_video_crop_rgn(handle, (esp_gmf_video_rgn_t *)buf); +} + +static esp_gmf_err_t video_crop_el_load_caps(esp_gmf_element_handle_t handle) +{ + esp_gmf_cap_t *caps = NULL; + esp_gmf_cap_t dec_caps = {0}; + dec_caps.cap_eightcc = ESP_GMF_CAPS_VIDEO_CROP; + dec_caps.attr_fun = NULL; + int ret = esp_gmf_cap_append(&caps, &dec_caps); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, return ret, "Failed to create capability"); + esp_gmf_element_t *el = (esp_gmf_element_t *)handle; + el->caps = caps; + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t video_crop_el_load_methods(esp_gmf_element_handle_t handle) +{ + esp_gmf_method_t *method = NULL; + esp_gmf_err_t ret = ESP_GMF_ERR_OK; + esp_gmf_args_desc_t *set_args = NULL; + ret = esp_gmf_args_desc_append(&set_args, VMETHOD_ARG(CROP, SET_CROP_RGN, X), ESP_GMF_ARGS_TYPE_UINT16, + sizeof(uint16_t), offsetof(esp_gmf_video_rgn_t, x)); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, return ret, "Failed to append x of cropper region"); + ret = esp_gmf_args_desc_append(&set_args, VMETHOD_ARG(CROP, SET_CROP_RGN, Y), ESP_GMF_ARGS_TYPE_UINT16, + sizeof(uint16_t), offsetof(esp_gmf_video_rgn_t, y)); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, return ret, "Failed to append y of cropper region"); + ret = esp_gmf_args_desc_append(&set_args, VMETHOD_ARG(CROP, SET_CROP_RGN, WIDTH), ESP_GMF_ARGS_TYPE_UINT16, + sizeof(uint16_t), offsetof(esp_gmf_video_rgn_t, width)); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, return ret, "Failed to append width of cropper region"); + ret = esp_gmf_args_desc_append(&set_args, VMETHOD_ARG(CROP, SET_CROP_RGN, HEIGHT), ESP_GMF_ARGS_TYPE_UINT16, + sizeof(uint16_t), offsetof(esp_gmf_video_rgn_t, height)); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, return ret, "Failed to append height of cropper region"); + ret = esp_gmf_method_append(&method, VMETHOD(CROP, SET_CROP_RGN), video_set_dst_rgn, set_args); + ESP_GMF_RET_ON_ERROR(TAG, ret, return ret, "Failed to register %s method", VMETHOD(CROP, SET_CROP_RGN)); + esp_gmf_element_t *el = (esp_gmf_element_t *)handle; + el->method = method; + return ret; +} + +static esp_gmf_err_t video_crop_el_received_event_handler(esp_gmf_event_pkt_t *evt, void *ctx) +{ + ESP_GMF_NULL_CHECK(TAG, ctx, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, evt, return ESP_GMF_ERR_INVALID_ARG); + if ((evt->type != ESP_GMF_EVT_TYPE_REPORT_INFO) + || (evt->sub != ESP_GMF_INFO_VIDEO) + || (evt->payload == NULL)) { + return ESP_GMF_ERR_OK; + } + esp_gmf_element_handle_t self = (esp_gmf_element_handle_t)ctx; + esp_gmf_element_handle_t el = evt->from; + esp_gmf_event_state_t state = ESP_GMF_EVENT_STATE_NONE; + esp_gmf_element_get_state(self, &state); + esp_gmf_info_video_t *info = (esp_gmf_info_video_t *)evt->payload; + esp_gmf_video_el_set_src_info(self, info); + esp_imgfx_crop_cfg_t *config = (esp_imgfx_crop_cfg_t *)OBJ_GET_CFG(self); + ESP_GMF_NULL_CHECK(TAG, config, return ESP_GMF_ERR_FAIL); + esp_gmf_crop_hd_t *video_el = (esp_gmf_crop_hd_t *)self; + GMF_VIDEO_UPDATE_CONFIG(config, info, video_el->need_recfg); + ESP_LOGD(TAG, "RECV element info, from: %s-%p, next: %p, self: %s-%p, type: %x, state: %s, width: %d, height: %d, pixel format: %lx", + OBJ_GET_TAG(el), el, esp_gmf_node_for_next((esp_gmf_node_t *)el), OBJ_GET_TAG(self), self, evt->type, + esp_gmf_event_get_state_str(state), info->width, info->height, info->format_id); + // It is for the first time to receive event from previous element + if (state == ESP_GMF_EVENT_STATE_NONE) { + esp_gmf_element_set_state(self, ESP_GMF_EVENT_STATE_INITIALIZED); + } + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t video_crop_el_new(void *config, esp_gmf_obj_handle_t *handle) +{ + return esp_gmf_video_crop_init((esp_imgfx_crop_cfg_t *)config, handle); +} + +esp_gmf_err_t esp_gmf_video_crop_init(esp_imgfx_crop_cfg_t *config, esp_gmf_obj_handle_t *handle) +{ + ESP_GMF_MEM_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + *handle = NULL; + esp_gmf_crop_hd_t *video_el = esp_gmf_oal_calloc(1, sizeof(esp_gmf_crop_hd_t)); + ESP_GMF_MEM_CHECK(TAG, video_el, return ESP_GMF_ERR_MEMORY_LACK); + esp_gmf_err_t ret = ESP_GMF_ERR_OK; + esp_gmf_obj_t *obj = (esp_gmf_obj_t *)video_el; + // Set element tag + ret = esp_gmf_obj_set_tag(video_el, "vid_crop"); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto __init_exit, "Failed set OBJ tag"); + // Configure object callbacks + obj->new_obj = video_crop_el_new; + obj->del_obj = video_crop_el_delete; + // Set element config + esp_imgfx_crop_cfg_t *cfg = esp_gmf_oal_calloc(1, sizeof(esp_imgfx_crop_cfg_t)); + ESP_GMF_MEM_VERIFY(TAG, cfg, {ret = ESP_GMF_ERR_MEMORY_LACK; goto __init_exit;}, "crop configuration", sizeof(esp_imgfx_crop_cfg_t)); + esp_gmf_obj_set_config(obj, cfg, sizeof(esp_imgfx_crop_cfg_t)); + if (config) { + memcpy(cfg, config, sizeof(esp_imgfx_crop_cfg_t)); + } else { + esp_imgfx_crop_cfg_t dcfg = DEFAULT_ESP_GMF_CROP_CONFIG(); + memcpy(cfg, &dcfg, sizeof(esp_imgfx_crop_cfg_t)); + } + // Configure element + esp_gmf_element_cfg_t el_cfg = { + .dependency = true, + }; + ESP_GMF_ELEMENT_IN_PORT_ATTR_SET(el_cfg.in_attr, ESP_GMF_EL_PORT_CAP_SINGLE, 0, 0, + ESP_GMF_PORT_TYPE_BLOCK, ESP_GMF_ELEMENT_PORT_DATA_SIZE_DEFAULT); + ESP_GMF_ELEMENT_OUT_PORT_ATTR_SET(el_cfg.out_attr, ESP_GMF_EL_PORT_CAP_SINGLE, 0, 0, + ESP_GMF_PORT_TYPE_BLOCK, ESP_GMF_ELEMENT_PORT_DATA_SIZE_DEFAULT); + // Initialize element + ret = esp_gmf_video_el_init(video_el, &el_cfg); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto __init_exit, "Failed initialize video element"); + // Register element's callbacks + esp_gmf_video_element_t *parent = (esp_gmf_video_element_t *)video_el; + parent->base.ops.open = video_crop_el_open; + parent->base.ops.process = video_crop_el_process; + parent->base.ops.close = video_crop_el_close; + parent->base.ops.event_receiver = video_crop_el_received_event_handler; + parent->base.ops.load_caps = video_crop_el_load_caps; + parent->base.ops.load_methods = video_crop_el_load_methods; + *handle = (esp_gmf_obj_handle_t)video_el; + ESP_LOGD(TAG, "Initialization, %s-%p", OBJ_GET_TAG(obj), obj); + return ret; +__init_exit: + video_crop_el_delete(video_el); + return ret; +} + +esp_gmf_err_t esp_gmf_video_crop_rgn(esp_gmf_element_handle_t handle, esp_gmf_video_rgn_t *rgn) +{ + esp_imgfx_crop_cfg_t config; + esp_gmf_err_t ret = esp_gmf_video_crop_get_cfg(handle, &config); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, return ret, "Failed to get crop configuration"); + config.x_pos = rgn->x; + config.y_pos = rgn->y; + config.cropped_res.width = rgn->width; + config.cropped_res.height = rgn->height; + return esp_gmf_video_crop_set_cfg(handle, &config); +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/esp_gmf_video_dec.c b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/esp_gmf_video_dec.c new file mode 100644 index 0000000..0d8fd7f --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/esp_gmf_video_dec.c @@ -0,0 +1,434 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#include "esp_gmf_video_dec.h" +#include "esp_gmf_caps_def.h" +#include "esp_gmf_video_element.h" +#include "esp_video_dec.h" +#include "esp_video_codec_utils.h" +#include "esp_gmf_video_element.h" +#include "gmf_video_common.h" +#include "esp_gmf_video_methods_def.h" +#include "esp_log.h" + +#define TAG "VDEC_EL" + +#define COPY_ARG(dst, src, offset, size, total_len) \ + if (offset + size <= total_len) { \ + memcpy(&(dst), src + offset, size); \ + offset += size; \ + } + +#define ADD_ARG_DESC(desc, type, size, offset) \ + ret = esp_gmf_args_desc_append(&set_args, desc, type, size, offset); \ + GMF_VIDEO_BREAK_ON_FAIL(ret); \ + offset += size; + +/** + * @brief Video decoder definition + */ +typedef struct { + esp_gmf_video_element_t parent; /*!< Video element parent */ + uint32_t out_format; /*!< Video decoder output codec */ + uint32_t codec_cc; /*!< Codec FourCC used to find decoder if user set it */ + bool vdec_bypass; /*!< Whether decoder is bypassed or not */ + bool header_parsed; /*!< Whether video header parsed or not */ + esp_video_dec_handle_t dec_handle; /*!< Video decoder handle */ +} vdec_t; + +static inline uint32_t get_prefer_codec(vdec_t *vdec) +{ + esp_gmf_video_dec_cfg_t *cfg = (esp_gmf_video_dec_cfg_t *)OBJ_GET_CFG(vdec); + return cfg ? cfg->codec_cc : 0; +} + +static int vdec_get_out_fmts(vdec_t *vdec, uint32_t src_codec, const uint32_t **fmts, uint8_t *num) +{ + esp_video_dec_caps_t caps = {}; + esp_video_codec_query_t query = { + .codec_type = (esp_video_codec_type_t)src_codec, + .codec_cc = get_prefer_codec(vdec), + }; + esp_vc_err_t ret = esp_video_dec_query_caps(&query, &caps); + if (ret != ESP_VC_ERR_OK) { + return ESP_GMF_ERR_NOT_SUPPORT; + } + *fmts = (const uint32_t *)caps.out_fmts; + *num = caps.out_fmt_num; + return ESP_GMF_ERR_OK; +} + +static bool vdec_is_codec_supported(vdec_t *vdec, uint32_t in_codec, uint32_t out_format) +{ + esp_video_dec_caps_t caps = {}; + esp_video_codec_query_t query = { + .codec_type = (esp_video_codec_type_t)in_codec, + .codec_cc = get_prefer_codec(vdec), + }; + esp_vc_err_t ret = esp_video_dec_query_caps(&query, &caps); + if (ret != ESP_VC_ERR_OK) { + return false; + } + for (uint8_t i = 0; i < caps.out_fmt_num; i++) { + if (out_format == (uint32_t)caps.out_fmts[i]) { + return true; + } + } + return false; +} + +static esp_gmf_job_err_t vdec_el_open(esp_gmf_video_element_handle_t self, void *para) +{ + vdec_t *vdec = (vdec_t *)self; + esp_gmf_info_video_t *src_info = &vdec->parent.src_info; + vdec->vdec_bypass = (src_info->format_id == vdec->out_format); + if (vdec->vdec_bypass) { + // Report video info to next element directly + esp_gmf_element_notify_vid_info(self, src_info); + return ESP_GMF_JOB_ERR_OK; + } + // Open video decoder now + esp_video_dec_cfg_t dec_cfg = { + .codec_type = (esp_video_codec_type_t)src_info->format_id, + .codec_cc = get_prefer_codec(vdec), + .out_fmt = (esp_video_codec_pixel_fmt_t)vdec->out_format, + }; + bool supported = vdec_is_codec_supported(vdec, src_info->format_id, vdec->out_format); + if (supported == false) { + ESP_LOGE(TAG, "Format not supported in:%s out:%s", + esp_gmf_video_get_format_string(src_info->format_id), + esp_gmf_video_get_format_string(vdec->out_format)); + return ESP_GMF_JOB_ERR_FAIL; + } + int ret = esp_video_dec_open(&dec_cfg, &vdec->dec_handle); + if (ret != ESP_VC_ERR_OK) { + return ESP_GMF_JOB_ERR_FAIL; + } + // Get alignment request + uint8_t in_frame_align = 0; + uint8_t out_frame_align = 0; + esp_video_dec_get_frame_align(vdec->dec_handle, &in_frame_align, &out_frame_align); + ESP_GMF_ELEMENT_GET(vdec)->in_attr.port.buf_addr_aligned = in_frame_align; + ESP_GMF_ELEMENT_GET(vdec)->out_attr.port.buf_addr_aligned = out_frame_align; + esp_gmf_info_video_t out_info = vdec->parent.src_info; + out_info.format_id = vdec->out_format; + esp_gmf_element_notify_vid_info(self, &out_info); + return ESP_GMF_JOB_ERR_OK; +} + +static int vdec_bypass(vdec_t *vdec, esp_gmf_port_t *in, esp_gmf_port_t *out) +{ + esp_gmf_payload_t *in_load = NULL; + esp_gmf_payload_t *out_load = NULL; + esp_gmf_err_io_t ret = esp_gmf_port_acquire_in(in, &in_load, ESP_GMF_ELEMENT_GET(vdec)->in_attr.data_size, ESP_GMF_MAX_DELAY); + if (ret < 0) { + ESP_LOGE(TAG, "Acquire on in port, ret:%d", ret); + return ret == ESP_GMF_IO_ABORT ? ESP_GMF_JOB_ERR_OK : ESP_GMF_JOB_ERR_FAIL; + } + bool is_done = in_load->is_done; + out_load = in_load; + ret = esp_gmf_port_acquire_out(out, &out_load, out_load->valid_size, -1); + // Not release out if error + if (ret < 0) { + esp_gmf_port_release_in(in, in_load, 0); + return ret; + } + esp_gmf_port_release_out(out, out_load, 0); + esp_gmf_port_release_in(in, in_load, 0); + if (is_done) { + ret = ESP_GMF_JOB_ERR_DONE; + } + return ret; +} + +static esp_gmf_job_err_t vdec_el_process(esp_gmf_video_element_handle_t self, void *para) +{ + esp_gmf_element_handle_t hd = (esp_gmf_element_handle_t)self; + vdec_t *vdec = (vdec_t *)self; + esp_gmf_port_t *in = ESP_GMF_ELEMENT_GET(hd)->in; + esp_gmf_port_t *out = ESP_GMF_ELEMENT_GET(hd)->out; + esp_gmf_payload_t *in_load = NULL; + esp_gmf_payload_t *out_load = NULL; + if (vdec->vdec_bypass) { + // This code is added to bypass raw input data only + return vdec_bypass(vdec, in, out); + } + // TODO make sure input frame alignment meet request + int ret = esp_gmf_port_acquire_in(in, &in_load, ESP_GMF_ELEMENT_GET(vdec)->in_attr.data_size, -1); + if (ret < 0) { + ESP_LOGE(TAG, "Acquire in port error ret:%d", ret); + return ret == ESP_GMF_IO_ABORT ? ESP_GMF_JOB_ERR_OK : ESP_GMF_JOB_ERR_FAIL; + } + + // Suppose input data must be frame boundary + esp_video_dec_in_frame_t in_frame = { + .pts = in_load->pts, + .data = in_load->buf, + .size = in_load->valid_size, + }; + esp_video_dec_out_frame_t decoded_frame = {}; + ret = ESP_GMF_JOB_ERR_FAIL; + do { + // Check whether done + if (in_load->valid_size == 0 && in_load->is_done) { + ret = ESP_GMF_JOB_ERR_DONE; + break; + } + // Check alignment + if ((intptr_t)in_load->buf & (ESP_GMF_ELEMENT_GET(vdec)->in_attr.port.buf_addr_aligned - 1)) { + ESP_LOGE(TAG, "Input alignment not meet %d", ESP_GMF_ELEMENT_GET(vdec)->in_attr.port.buf_addr_aligned); + ret = ESP_GMF_JOB_ERR_FAIL; + break; + } + if (vdec->header_parsed == false) { + // Workaround use small buffer, so that can get frame info + uint32_t out_size = 32; + uint8_t out_frame_align = ESP_GMF_ELEMENT_GET(vdec)->out_attr.port.buf_addr_aligned; + uint8_t *out_data = esp_video_codec_align_alloc(out_frame_align, out_size, &out_size); + if (out_data == NULL) { + ESP_LOGE(TAG, "No enough memory for parse header"); + ret = ESP_GMF_JOB_ERR_FAIL; + break; + } + decoded_frame.data = out_data; + decoded_frame.size = out_size; + ret = esp_video_dec_process(vdec->dec_handle, &in_frame, &decoded_frame); + esp_video_codec_free(out_data); + esp_video_codec_frame_info_t frame_info = {}; + ret = esp_video_dec_get_frame_info(vdec->dec_handle, &frame_info); + if ((ret != ESP_VC_ERR_OK) || (frame_info.res.width == 0) || (frame_info.res.height) == 0) { + ESP_LOGE(TAG, "Fail to get frame info%d", ret); + ret = ESP_GMF_JOB_ERR_CONTINUE; + break; + } + ESP_LOGI(TAG, "Dec frame size %dx%d", (int)frame_info.res.width, (int)frame_info.res.height); + // Update output frame size + uint32_t out_frame_size = esp_video_codec_get_image_size(vdec->out_format, &frame_info.res); + out_frame_size = GMF_VIDEO_ALIGN_UP(out_frame_size, out_frame_align); + ESP_GMF_ELEMENT_GET(vdec)->out_attr.data_size = out_frame_size; + // Report info to next element when parse header finished + esp_gmf_info_video_t out_info = { + .format_id = vdec->out_format, + .width = frame_info.res.width, + .height = frame_info.res.height, + .fps = frame_info.fps, + }; + esp_gmf_element_notify_vid_info(self, &out_info); + vdec->header_parsed = true; + } + // We do not allow decode change resolution in middle currently + ret = esp_gmf_port_acquire_out(out, &out_load, ESP_GMF_ELEMENT_GET(vdec)->out_attr.data_size, ESP_GMF_MAX_DELAY); + if (ret < 0) { + ESP_LOGE(TAG, "Write data error, ret:%d, line:%d", ret, __LINE__); + ret = (ret == ESP_GMF_IO_ABORT) ? ESP_GMF_JOB_ERR_OK : ESP_GMF_JOB_ERR_FAIL; + break; + } + decoded_frame.data = out_load->buf; + decoded_frame.size = ESP_GMF_ELEMENT_GET(vdec)->out_attr.data_size; + ret = esp_video_dec_process(vdec->dec_handle, &in_frame, &decoded_frame); + // Skip data if decode failed + if (ret != ESP_VC_ERR_OK) { + ESP_LOGE(TAG, "Fail to decode ret %d", ret); + ret = ESP_GMF_JOB_ERR_CONTINUE; + break; + } + out_load->valid_size = decoded_frame.decoded_size; + out_load->pts = in_load->pts; + ret = ESP_GMF_JOB_ERR_OK; + } while (0); + if (out_load) { + esp_gmf_port_release_out(out, out_load, 0); + } + esp_gmf_port_release_in(in, in_load, 0); + return ret; +} + +static esp_gmf_job_err_t vdec_el_close(esp_gmf_video_element_handle_t self, void *para) +{ + vdec_t *vdec = (vdec_t *)self; + if (vdec->dec_handle) { + esp_video_dec_close(vdec->dec_handle); + vdec->dec_handle = NULL; + } + vdec->header_parsed = false; + ESP_LOGI(TAG, "Closed, %p", self); + return ESP_OK; +} + +static esp_gmf_err_t vdec_el_new(void *cfg, esp_gmf_obj_handle_t *handle) +{ + return esp_gmf_video_dec_init((esp_gmf_video_dec_cfg_t *)cfg, (esp_gmf_element_handle_t*)handle); +} + +static esp_gmf_err_t vdec_el_destroy(esp_gmf_obj_handle_t self) +{ + esp_gmf_video_el_deinit(self); + void *cfg = OBJ_GET_CFG(self); + if (cfg) { + esp_gmf_oal_free(cfg); + } + esp_gmf_oal_free(self); + return ESP_OK; +} + +static esp_gmf_err_t set_out_format(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc, + uint8_t *buf, int buf_len) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, arg_desc, return ESP_GMF_ERR_INVALID_ARG); + vdec_t *vdec = (vdec_t *)handle; + vdec->out_format = *((uint32_t*) buf); + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t set_src_codec(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc, + uint8_t *buf, int buf_len) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, arg_desc, return ESP_GMF_ERR_INVALID_ARG); + vdec_t *vdec = (vdec_t *)handle; + vdec->parent.src_info.format_id = *((uint32_t*) buf); + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t get_out_formats(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc, + uint8_t *buf, int buf_len) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, arg_desc, return ESP_GMF_ERR_INVALID_ARG); + vdec_t *vdec = (vdec_t *)handle; + int offset = 0; + uint32_t in_codec = 0; + const uint32_t **out_fmts = NULL; + uint8_t *out_fmt_num = NULL; + COPY_ARG(in_codec, buf, offset, sizeof(uint32_t), buf_len); + COPY_ARG(out_fmts, buf, offset, sizeof(void*), buf_len); + COPY_ARG(out_fmt_num, buf, offset, sizeof(void*), buf_len); + return vdec_get_out_fmts(vdec, in_codec, out_fmts, out_fmt_num); +} + +static esp_gmf_err_t vdec_el_load_caps(esp_gmf_element_handle_t handle) +{ + esp_gmf_cap_t *caps = NULL; + esp_gmf_cap_t cap = {0}; + esp_gmf_err_t ret = ESP_GMF_ERR_OK; + do { + cap.cap_eightcc = ESP_GMF_CAPS_VIDEO_DECODER; + cap.attr_fun = NULL; + ret = esp_gmf_cap_append(&caps, &cap); + GMF_VIDEO_BREAK_ON_FAIL(ret); + + ((esp_gmf_element_t *) handle)->caps = caps; + return ret; + } while (0); + if (caps) { + esp_gmf_cap_destroy(caps); + } + return ret; +} + +static esp_gmf_err_t vdec_load_methods(esp_gmf_element_handle_t handle) +{ + esp_gmf_args_desc_t *set_args = NULL; + esp_gmf_method_t *methods = NULL; + esp_gmf_err_t ret = ESP_GMF_ERR_OK; + int offset = 0; + do { + ret = esp_gmf_args_desc_append(&set_args, VMETHOD_ARG(CLR_CVT, SET_DST_FMT, FMT), ESP_GMF_ARGS_TYPE_UINT32, sizeof(uint32_t), 0); + GMF_VIDEO_BREAK_ON_FAIL(ret); + ret = esp_gmf_method_append(&methods, VMETHOD(CLR_CVT, SET_DST_FMT), set_out_format, set_args); + GMF_VIDEO_BREAK_ON_FAIL(ret); + + set_args = NULL; + offset = 0; + ADD_ARG_DESC(VMETHOD_ARG(DECODER, SET_SRC_CODEC, CODEC), ESP_GMF_ARGS_TYPE_UINT32, sizeof(uint32_t), offset); + ret = esp_gmf_method_append(&methods, VMETHOD(DECODER, SET_SRC_CODEC), set_src_codec, set_args); + GMF_VIDEO_BREAK_ON_FAIL(ret); + + set_args = NULL; + offset = 0; + ADD_ARG_DESC(VMETHOD_ARG(DECODER, GET_DST_FMTS, SRC_CODEC), ESP_GMF_ARGS_TYPE_UINT32, sizeof(uint32_t), offset); + ADD_ARG_DESC(VMETHOD_ARG(DECODER, GET_DST_FMTS, DST_FMTS_PTR), ESP_GMF_ARGS_TYPE_UINT32, sizeof(void *), offset); + ADD_ARG_DESC(VMETHOD_ARG(DECODER, GET_DST_FMTS, DST_FMTS_NUM_PTR), ESP_GMF_ARGS_TYPE_UINT32, sizeof(void *), offset); + ret = esp_gmf_method_append(&methods, VMETHOD(DECODER, GET_DST_FMTS), get_out_formats, set_args); + GMF_VIDEO_BREAK_ON_FAIL(ret); + + ((esp_gmf_element_t *) handle)->method = methods; + return ESP_GMF_ERR_OK; + } while (0); + ESP_LOGE(TAG, "Fail to load methods"); + if (set_args) { + esp_gmf_args_desc_destroy(set_args); + } + if (methods) { + esp_gmf_method_destroy(methods); + } + return ESP_GMF_ERR_MEMORY_LACK; +} + +esp_gmf_err_t esp_gmf_video_dec_init(esp_gmf_video_dec_cfg_t *cfg, esp_gmf_element_handle_t *handle) +{ + ESP_GMF_MEM_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + vdec_t *vdec = esp_gmf_oal_calloc(1, sizeof(vdec_t)); + ESP_GMF_MEM_CHECK(TAG, vdec, return ESP_GMF_ERR_MEMORY_LACK); + + esp_gmf_obj_t *obj = (esp_gmf_obj_t *)vdec; + obj->new_obj = vdec_el_new; + obj->del_obj = vdec_el_destroy; + if (cfg) { + esp_gmf_video_dec_cfg_t *dec_cfg = (esp_gmf_video_dec_cfg_t *) calloc(1, sizeof(esp_gmf_video_dec_cfg_t)); + ESP_GMF_MEM_CHECK(TAG, vdec, { goto VDEC_FAIL; }); + *dec_cfg = *cfg; + esp_gmf_obj_set_config(obj, dec_cfg, sizeof(esp_gmf_video_dec_cfg_t)); + } + + esp_gmf_err_t ret = esp_gmf_obj_set_tag(obj, "vid_dec"); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto VDEC_FAIL, "Failed set OBJ tag"); + + uint8_t align = esp_gmf_oal_get_spiram_cache_align(); + esp_gmf_element_cfg_t el_cfg = { + .dependency = true, + }; + ESP_GMF_ELEMENT_IN_PORT_ATTR_SET(el_cfg.in_attr, ESP_GMF_EL_PORT_CAP_SINGLE, align, align, + ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, ESP_GMF_ELEMENT_PORT_DATA_SIZE_DEFAULT); + ESP_GMF_ELEMENT_OUT_PORT_ATTR_SET(el_cfg.out_attr, ESP_GMF_EL_PORT_CAP_SINGLE, align, align, + ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, ESP_GMF_ELEMENT_PORT_DATA_SIZE_DEFAULT); + ret = esp_gmf_video_el_init(vdec, &el_cfg); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto VDEC_FAIL, "Failed to init video decoder element"); + ESP_GMF_ELEMENT_GET(vdec)->ops.open = vdec_el_open; + ESP_GMF_ELEMENT_GET(vdec)->ops.process = vdec_el_process; + ESP_GMF_ELEMENT_GET(vdec)->ops.close = vdec_el_close; + ESP_GMF_ELEMENT_GET(vdec)->ops.event_receiver = esp_gmf_video_handle_events; + ESP_GMF_ELEMENT_GET(vdec)->ops.load_caps = vdec_el_load_caps; + ESP_GMF_ELEMENT_GET(vdec)->ops.load_methods = vdec_load_methods; + + *handle = (esp_gmf_element_handle_t) obj; + ESP_LOGD(TAG, "Create %s-%p", OBJ_GET_TAG(obj), obj); + return ESP_GMF_ERR_OK; + +VDEC_FAIL: + esp_gmf_obj_delete(obj); + return ret; +} + +esp_gmf_err_t esp_gmf_video_dec_set_dst_format(esp_gmf_element_handle_t handle, uint32_t dst_fmt) +{ + ESP_GMF_MEM_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + vdec_t *vdec = (vdec_t *)handle; + vdec->out_format = dst_fmt; + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_video_dec_get_dst_formats(esp_gmf_element_handle_t handle, + uint32_t in_codec, + const uint32_t **dst_fmts, + uint8_t *dst_fmts_num) +{ + ESP_GMF_MEM_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + vdec_t *vdec = (vdec_t *)handle; + return vdec_get_out_fmts(vdec, in_codec, dst_fmts, dst_fmts_num); +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/esp_gmf_video_enc.c b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/esp_gmf_video_enc.c new file mode 100644 index 0000000..7c8dd32 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/esp_gmf_video_enc.c @@ -0,0 +1,578 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#include +#include "esp_log.h" +#include "esp_gmf_info.h" +#include "esp_gmf_oal_mem.h" +#include "esp_gmf_err.h" +#include "esp_gmf_video_element.h" +#include "esp_video_enc.h" +#include "esp_video_enc_reg.h" +#include "esp_video_codec_utils.h" +#include "esp_gmf_video_enc.h" +#include "gmf_video_common.h" +#include "esp_fourcc.h" +#include "esp_gmf_video_methods_def.h" +#include "esp_gmf_caps_def.h" + +#define MAX_ENC_TYPE_SUPPORT 4 +// TODO this is estimated value so that each frame not over the size +#define MJPEG_ENC_MIN_COMPRESS_RATIO 10 +#define H264_ENC_MIN_COMPRESS_RATIO 2 + +#define COPY_ARG(dst, src, offset, size, total_len) \ + if (offset + size <= total_len) { \ + memcpy(dst, src + offset, size); \ + offset += size; \ + } + +#define ADD_ARG_DESC(desc, type, size, offset) \ + ret = esp_gmf_args_desc_append(&set_args, desc, type, size, offset); \ + GMF_VIDEO_BREAK_ON_FAIL(ret); \ + offset += size; + +static const char *TAG = "VENC_EL"; + +/** + * @brief Video encoder extra setting mask + */ +typedef enum { + VENC_EXTRA_SET_MASK_NONE = 0, + VENC_EXTRA_SET_MASK_BITRATE = (1 << 0), + VENC_EXTRA_SET_MASK_QP = (1 << 1), + VENC_EXTRA_SET_MASK_GOP = (1 << 2), + VENC_EXTRA_SET_MASK_ALL = (0xFF), +} venc_extra_set_mask_t; + +/** + * @brief Video encoder extra setting + */ +typedef struct { + uint32_t bitrate; + uint32_t min_qp; + uint32_t max_qp; + uint32_t gop; + venc_extra_set_mask_t mask; +} venc_extra_set_t; + +/** + * @brief Video encoder definition + */ +typedef struct { + esp_gmf_video_element_t parent; /*!< Video element parent */ + esp_video_codec_type_t dst_codec; /*!< Video encoder destination codec */ + bool venc_bypass; /*!< Whether video encoder is bypassed or not */ + uint32_t codec_cc; /*!< FourCC used to find encoder if set */ + esp_video_enc_handle_t enc_handle; /*!< Video encoder handle */ + venc_extra_set_t extra_set; /*!< Video encoder extra setting */ +} venc_t; + +static int venc_get_input_codecs(venc_t *venc, uint32_t dst_codec, const uint32_t **codecs, uint8_t *num) +{ + esp_video_enc_caps_t caps = {}; + esp_video_codec_query_t query = { + .codec_type = (esp_video_codec_type_t)dst_codec, + .codec_cc = venc->codec_cc, + }; + int ret = esp_video_enc_query_caps(&query, &caps); + if (ret != ESP_VC_ERR_OK) { + return ESP_GMF_ERR_NOT_SUPPORT; + } + *codecs = (const uint32_t *)caps.in_fmts; + *num = caps.in_fmt_num; + return ESP_GMF_ERR_OK; +} + +static bool venc_is_codec_supported(venc_t *venc, uint32_t src_codec, uint32_t dst_codec) +{ + esp_video_enc_caps_t caps = {}; + esp_video_codec_query_t query = { + .codec_type = (esp_video_codec_type_t)dst_codec, + .codec_cc = venc->codec_cc, + }; + int ret = esp_video_enc_query_caps(&query, &caps); + if (ret != ESP_VC_ERR_OK) { + return false; + } + for (uint8_t i = 0; i < caps.in_fmt_num; i++) { + if (src_codec == (uint32_t)caps.in_fmts[i]) { + return true; + } + } + return false; +} + +static int venc_get_frame_size(venc_t *venc, int *in_frame_size, int *out_frame_size) +{ + esp_gmf_info_video_t *src_info = &venc->parent.src_info; + esp_video_codec_resolution_t res = { + .width = src_info->width, + .height = src_info->height, + }; + *in_frame_size = (int)esp_video_codec_get_image_size( + (esp_video_codec_pixel_fmt_t)src_info->format_id, &res); + if (venc->dst_codec == ESP_FOURCC_MJPG) { + *out_frame_size = *in_frame_size / MJPEG_ENC_MIN_COMPRESS_RATIO; + } else if (venc->dst_codec == ESP_FOURCC_H264) { + uint8_t align = esp_gmf_oal_get_spiram_cache_align(); + *out_frame_size = *in_frame_size / H264_ENC_MIN_COMPRESS_RATIO; + *out_frame_size = GMF_VIDEO_ALIGN_UP(*out_frame_size, align); + } else { + return ESP_GMF_ERR_NOT_SUPPORT; + } + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t venc_el_apply_settings(venc_t *venc, venc_extra_set_mask_t mask) +{ + venc_extra_set_t *extra_set = &venc->extra_set; + if (venc->enc_handle == NULL || + extra_set->mask == VENC_EXTRA_SET_MASK_NONE) { + return ESP_GMF_ERR_OK; + } + esp_vc_err_t ret = ESP_VC_ERR_OK; + if (extra_set->mask & (mask & VENC_EXTRA_SET_MASK_BITRATE)) { + ret |= esp_video_enc_set_bitrate(venc->enc_handle, extra_set->bitrate); + } + if (extra_set->mask & (mask & VENC_EXTRA_SET_MASK_QP)) { + ret |= esp_video_enc_set_qp(venc->enc_handle, extra_set->min_qp, extra_set->max_qp); + } + if (extra_set->mask & (mask & VENC_EXTRA_SET_MASK_GOP)) { + ret |= esp_video_enc_set_gop(venc->enc_handle, extra_set->gop); + } + return ret == ESP_VC_ERR_OK ? ESP_GMF_ERR_OK : ESP_GMF_ERR_NOT_SUPPORT; +} + +static esp_gmf_job_err_t venc_el_open(esp_gmf_video_element_handle_t self, void *para) +{ + venc_t *venc = (venc_t *)self; + esp_gmf_video_enc_cfg_t *cfg = (esp_gmf_video_enc_cfg_t *)OBJ_GET_CFG(self); + esp_gmf_info_video_t *src_info = &venc->parent.src_info; + venc->venc_bypass = (src_info->format_id == venc->dst_codec); + if (venc->venc_bypass == false) { + esp_video_enc_cfg_t enc_cfg = { + .codec_type = venc->dst_codec, + .resolution = { + .width = src_info->width, + .height = src_info->height, + }, + .in_fmt = (esp_video_codec_pixel_fmt_t)src_info->format_id, + .fps = src_info->fps, + }; + if (cfg) { + enc_cfg.codec_cc = cfg->codec_cc; + venc->codec_cc = cfg->codec_cc; + } + esp_vc_err_t ret = esp_video_enc_open(&enc_cfg, &venc->enc_handle); + if (ret != ESP_VC_ERR_OK) { + ESP_LOGE(TAG, "Fail to open encoder ret %d", ret); + return ESP_GMF_ERR_NOT_SUPPORT; + } + int in_frame_size = 0; + int out_frame_size = 0; + venc_get_frame_size(venc, &in_frame_size, &out_frame_size); + ESP_GMF_ELEMENT_GET(venc)->in_attr.data_size = in_frame_size; + ESP_GMF_ELEMENT_GET(venc)->out_attr.data_size = out_frame_size; + venc_el_apply_settings(venc, VENC_EXTRA_SET_MASK_ALL); + } + // Report info to next element + esp_gmf_info_video_t out_info = venc->parent.src_info; + out_info.format_id = venc->dst_codec; + esp_gmf_element_notify_vid_info(self, &out_info); + return ESP_GMF_JOB_ERR_OK; +} + +static esp_gmf_job_err_t venc_el_process(esp_gmf_video_element_handle_t self, void *para) +{ + esp_gmf_element_handle_t hd = (esp_gmf_element_handle_t)self; + venc_t *venc = (venc_t *)self; + esp_gmf_port_t *in = ESP_GMF_ELEMENT_GET(hd)->in; + esp_gmf_port_t *out = ESP_GMF_ELEMENT_GET(hd)->out; + esp_gmf_payload_t *in_load = NULL; + esp_gmf_payload_t *out_load = NULL; + + int ret = esp_gmf_port_acquire_in(in, &in_load, ESP_GMF_ELEMENT_GET(venc)->in_attr.data_size, ESP_GMF_MAX_DELAY); + if (ret < 0) { + ESP_LOGE(TAG, "Acquire size:%d on in port, ret:%d", ESP_GMF_ELEMENT_GET(venc)->in_attr.data_size, ret); + return (ret == ESP_GMF_IO_ABORT) ? ESP_GMF_JOB_ERR_OK : ESP_GMF_JOB_ERR_FAIL; + } + if (in_load->is_done && in_load->valid_size == 0) { + esp_gmf_port_release_in(in, in_load, 0); + return ESP_GMF_JOB_ERR_DONE; + } + if (venc->venc_bypass) { + out_load = in_load; + } + int out_frame_size = ESP_GMF_ELEMENT_GET(venc)->out_attr.data_size; + ret = esp_gmf_port_acquire_out(out, &out_load, out_frame_size, -1); + if (ret < 0) { + esp_gmf_port_release_in(in, in_load, 0); + ESP_LOGE(TAG, "Acquire size:%d on out port, ret:%d", out_frame_size, ret); + return ret; + } + do { + if (venc->venc_bypass) { + break; + } + esp_video_enc_in_frame_t in_frame = { + .pts = in_load->pts, + .data = in_load->buf, + .size = in_load->valid_size, + }; + esp_video_enc_out_frame_t out_frame = { + .data = out_load->buf, + .size = out_frame_size, + }; + ret = esp_video_enc_process(venc->enc_handle, &in_frame, &out_frame); + ESP_LOGD(TAG, "I-b:%p,I-sz:%d, O-b:%p,O-sz:%d, in_sz:%d", in_load->buf, in_load->valid_size, + out_load->buf, out_load->valid_size, out_frame_size); + // Handle buffer not enough case + if (ret == ESP_VC_ERR_BUF_NOT_ENOUGH) { + ret = 0; + break; + } + if (ret != ESP_VC_ERR_OK) { + ESP_LOGE(TAG, "Video encoder encode frame error, ret:%d", ret); + ret = ESP_GMF_JOB_ERR_FAIL; + break; + } + out_load->pts = in_load->pts; + out_load->valid_size = out_frame.encoded_size; + ret = out_load->valid_size; + } while (0); + esp_gmf_port_release_in(in, in_load, 0); + esp_gmf_port_release_out(out, out_load, 0); + if (in_load->is_done) { + ret = ESP_GMF_JOB_ERR_DONE; + } + return ret; +} + +static esp_gmf_job_err_t venc_el_close(esp_gmf_video_element_handle_t self, void *para) +{ + venc_t *venc = (venc_t *)self; + if (venc->enc_handle) { + esp_video_enc_close(venc->enc_handle); + venc->enc_handle = NULL; + } + return ESP_GMF_JOB_ERR_OK; +} + +static esp_gmf_err_t venc_el_destroy(esp_gmf_video_element_handle_t self) +{ + venc_t *venc = (venc_t *)self; + if (venc->enc_handle) { + esp_video_enc_close(venc->enc_handle); + venc->enc_handle = NULL; + } + esp_gmf_video_el_deinit(self); + void *cfg = OBJ_GET_CFG(self); + if (cfg) { + esp_gmf_oal_free(cfg); + } + esp_gmf_oal_free(self); + return ESP_OK; +} + +static esp_gmf_err_t venc_el_load_caps(esp_gmf_element_handle_t handle) +{ + esp_gmf_cap_t *caps = NULL; + esp_gmf_cap_t cap = {0}; + esp_gmf_err_t ret = ESP_GMF_ERR_OK; + do { + cap.cap_eightcc = ESP_GMF_CAPS_VIDEO_ENCODER; + cap.attr_fun = NULL; + ret = esp_gmf_cap_append(&caps, &cap); + GMF_VIDEO_BREAK_ON_FAIL(ret); + + ((esp_gmf_element_t *) handle)->caps = caps; + return ret; + } while (0); + if (caps) { + esp_gmf_cap_destroy(caps); + } + return ret; +} + +static esp_gmf_err_t venc_el_new(void *cfg, esp_gmf_obj_handle_t *obj) +{ + return esp_gmf_video_enc_init((esp_gmf_video_enc_cfg_t *)cfg, (esp_gmf_element_handle_t*)obj); +} + +static esp_gmf_err_t set_bitrate(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc, + uint8_t *buf, int buf_len) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, arg_desc, return ESP_GMF_ERR_INVALID_ARG); + venc_t *venc = (venc_t *)handle; + int offset = 0; + COPY_ARG(&venc->extra_set.bitrate, buf, offset, sizeof(uint32_t), buf_len); + venc->extra_set.mask |= VENC_EXTRA_SET_MASK_BITRATE; + return venc_el_apply_settings(venc, VENC_EXTRA_SET_MASK_BITRATE); +} + +static esp_gmf_err_t get_in_formats(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc, + uint8_t *buf, int buf_len) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, arg_desc, return ESP_GMF_ERR_INVALID_ARG); + venc_t *venc = (venc_t *)handle; + int offset = 0; + uint32_t dst_codec = 0; + const uint32_t **in_fmts = NULL; + uint8_t *in_fmts_num = NULL; + COPY_ARG(&dst_codec, buf, offset, sizeof(uint32_t), buf_len); + COPY_ARG(&in_fmts, buf, offset, sizeof(void *), buf_len); + COPY_ARG(&in_fmts_num, buf, offset, sizeof(void *), buf_len); + return venc_get_input_codecs(venc, dst_codec, in_fmts, in_fmts_num); +} + +static esp_gmf_err_t set_format(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc, + uint8_t *buf, int buf_len) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, arg_desc, return ESP_GMF_ERR_INVALID_ARG); + venc_t *venc = (venc_t *)handle; + esp_gmf_info_video_t video_info = {}; + uint32_t dst_codec = 0; + int offset = 0; + COPY_ARG(&video_info.format_id, buf, offset, sizeof(uint32_t), buf_len); + COPY_ARG(&video_info.width, buf, offset, sizeof(uint16_t), buf_len); + COPY_ARG(&video_info.height, buf, offset, sizeof(uint16_t), buf_len); + COPY_ARG(&video_info.fps, buf, offset, sizeof(uint8_t), buf_len); + COPY_ARG(&video_info.bitrate, buf, offset, sizeof(uint32_t), buf_len); + COPY_ARG(&dst_codec, buf, offset, sizeof(uint32_t), buf_len); + uint32_t in_fmt = video_info.format_id; + if (venc_is_codec_supported(venc, in_fmt, dst_codec) == false) { + ESP_LOGE(TAG, "Not support encode from %s to %s", + esp_gmf_video_get_format_string(video_info.format_id), + esp_gmf_video_get_format_string(dst_codec)); + return ESP_GMF_ERR_NOT_SUPPORT; + } + venc->parent.src_info = video_info; + venc->dst_codec = (esp_video_codec_type_t)(dst_codec); + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t set_dst_codec(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc, + uint8_t *buf, int buf_len) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, arg_desc, return ESP_GMF_ERR_INVALID_ARG); + venc_t *venc = (venc_t *)handle; + venc->dst_codec = *(uint32_t *) buf; + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t get_frame_size(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc, + uint8_t *buf, int buf_len) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, arg_desc, return ESP_GMF_ERR_INVALID_ARG); + venc_t *venc = (venc_t *)handle; + uint32_t *frame_size = NULL; + int offset = 0; + COPY_ARG(&frame_size, buf, offset, sizeof(void *), buf_len); + if (frame_size == NULL) { + return ESP_GMF_ERR_INVALID_ARG; + } + *frame_size = 0; + if (venc->parent.src_info.format_id == 0 || venc->dst_codec == ESP_VIDEO_CODEC_TYPE_NONE) { + return ESP_GMF_ERR_NOT_SUPPORT; + } + if (venc->venc_bypass) { + return ESP_GMF_ERR_OK; + } + int in_frame_size = 0, out_frame_size = 0; + venc_get_frame_size(venc, &in_frame_size, &out_frame_size); + *frame_size = out_frame_size; + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t venc_el_load_methods(esp_gmf_element_handle_t handle) +{ + esp_gmf_args_desc_t *set_args = NULL; + esp_gmf_method_t *methods = NULL; + esp_gmf_err_t ret = ESP_GMF_ERR_OK; + int offset = 0; + do { + ret = esp_gmf_args_desc_append(&set_args, VMETHOD_ARG(ENCODER, SET_BITRATE, BITRATE), ESP_GMF_ARGS_TYPE_UINT32, sizeof(uint32_t), 0); + GMF_VIDEO_BREAK_ON_FAIL(ret); + ret = esp_gmf_method_append(&methods, VMETHOD(ENCODER, SET_BITRATE), set_bitrate, set_args); + GMF_VIDEO_BREAK_ON_FAIL(ret); + + set_args = NULL; + offset = 0; + ADD_ARG_DESC(VMETHOD_ARG(ENCODER, SET_DST_CODEC, CODEC), ESP_GMF_ARGS_TYPE_UINT32, sizeof(void *), offset); + ret = esp_gmf_method_append(&methods, VMETHOD(ENCODER, SET_DST_CODEC), set_dst_codec, set_args); + GMF_VIDEO_BREAK_ON_FAIL(ret); + + set_args = NULL; + offset = 0; + ADD_ARG_DESC(VMETHOD_ARG(ENCODER, GET_SRC_FMTS, DST_CODEC), ESP_GMF_ARGS_TYPE_UINT32, sizeof(uint32_t), offset); + ADD_ARG_DESC(VMETHOD_ARG(ENCODER, GET_SRC_FMTS, SRC_FMTS_PTR), ESP_GMF_ARGS_TYPE_UINT32, sizeof(void *), offset); + ADD_ARG_DESC(VMETHOD_ARG(ENCODER, GET_SRC_FMTS, SRC_FMTS_NUM_PTR), ESP_GMF_ARGS_TYPE_UINT32, sizeof(void *), offset); + ret = esp_gmf_method_append(&methods, VMETHOD(ENCODER, GET_SRC_FMTS), get_in_formats, set_args); + GMF_VIDEO_BREAK_ON_FAIL(ret); + + set_args = NULL; + offset = 0; + ADD_ARG_DESC(VMETHOD_ARG(ENCODER, PRESET, SRC_FMT), ESP_GMF_ARGS_TYPE_UINT32, sizeof(uint32_t), offset); + ADD_ARG_DESC(VMETHOD_ARG(ENCODER, PRESET, SRC_WIDTH), ESP_GMF_ARGS_TYPE_UINT16, sizeof(uint16_t), offset); + ADD_ARG_DESC(VMETHOD_ARG(ENCODER, PRESET, SRC_HEIGHT), ESP_GMF_ARGS_TYPE_UINT16, sizeof(uint16_t), offset); + ADD_ARG_DESC(VMETHOD_ARG(ENCODER, PRESET, SRC_FPS), ESP_GMF_ARGS_TYPE_UINT8, sizeof(uint8_t), offset); + ADD_ARG_DESC(VMETHOD_ARG(ENCODER, PRESET, SRC_BITRATE), ESP_GMF_ARGS_TYPE_UINT32, sizeof(uint32_t), offset); + ADD_ARG_DESC(VMETHOD_ARG(ENCODER, PRESET, DST_CODEC), ESP_GMF_ARGS_TYPE_UINT32, sizeof(uint32_t), offset); + ret = esp_gmf_method_append(&methods, VMETHOD(ENCODER, PRESET), set_format, set_args); + GMF_VIDEO_BREAK_ON_FAIL(ret); + + set_args = NULL; + offset = 0; + ADD_ARG_DESC(VMETHOD_ARG(ENCODER, GET_FRAME_SIZE, DST_FRM_SIZE_PTR), ESP_GMF_ARGS_TYPE_UINT32, sizeof(void *), offset); + ret = esp_gmf_method_append(&methods, VMETHOD(ENCODER, GET_FRAME_SIZE), get_frame_size, set_args); + GMF_VIDEO_BREAK_ON_FAIL(ret); + + ((esp_gmf_element_t *) handle)->method = methods; + return ESP_GMF_ERR_OK; + } while (0); + ESP_LOGE(TAG, "Fail to load methods"); + if (set_args) { + esp_gmf_args_desc_destroy(set_args); + } + if (methods) { + esp_gmf_method_destroy(methods); + } + return ESP_GMF_ERR_MEMORY_LACK; +} + +esp_gmf_err_t esp_gmf_video_enc_get_out_size(esp_gmf_element_handle_t self, uint32_t *frame_size) +{ + ESP_GMF_NULL_CHECK(TAG, self, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, frame_size, return ESP_GMF_ERR_INVALID_ARG); + venc_t *venc = (venc_t *)self; + *frame_size = 0; + if (venc->parent.src_info.format_id == 0 || venc->dst_codec == ESP_VIDEO_CODEC_TYPE_NONE) { + return ESP_GMF_ERR_NOT_SUPPORT; + } + if (venc->venc_bypass) { + return ESP_GMF_ERR_OK; + } + int in_frame_size = 0, out_frame_size = 0; + venc_get_frame_size(venc, &in_frame_size, &out_frame_size); + *frame_size = out_frame_size; + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_video_enc_set_bitrate(esp_gmf_element_handle_t self, uint32_t bitrate) +{ + ESP_GMF_NULL_CHECK(TAG, self, return ESP_GMF_ERR_INVALID_ARG); + venc_t *venc = (venc_t *)self; + venc->extra_set.bitrate = bitrate; + venc->extra_set.mask |= VENC_EXTRA_SET_MASK_BITRATE; + return venc_el_apply_settings(venc, VENC_EXTRA_SET_MASK_BITRATE); +} + +esp_gmf_err_t esp_gmf_video_enc_set_gop(esp_gmf_element_handle_t self, uint32_t gop) +{ + ESP_GMF_NULL_CHECK(TAG, self, return ESP_GMF_ERR_INVALID_ARG); + venc_t *venc = (venc_t *)self; + venc->extra_set.gop = gop; + venc->extra_set.mask |= VENC_EXTRA_SET_MASK_GOP; + return venc_el_apply_settings(venc, VENC_EXTRA_SET_MASK_GOP); +} + +esp_gmf_err_t esp_gmf_video_enc_set_qp(esp_gmf_element_handle_t self, uint32_t min_qp, uint32_t max_qp) +{ + ESP_GMF_NULL_CHECK(TAG, self, return ESP_GMF_ERR_INVALID_ARG); + venc_t *venc = (venc_t *)self; + venc->extra_set.min_qp = min_qp; + venc->extra_set.max_qp = max_qp; + venc->extra_set.mask |= VENC_EXTRA_SET_MASK_QP; + venc_el_apply_settings(venc, VENC_EXTRA_SET_MASK_QP); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_video_enc_set_dst_codec(esp_gmf_element_handle_t handle, uint32_t dst_codec) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + venc_t *venc = (venc_t *)handle; + if (venc->enc_handle) { + ESP_LOGE(TAG, "Not support set encoder handle during run"); + return ESP_GMF_ERR_NOT_SUPPORT; + } + venc->dst_codec = (esp_video_codec_type_t)dst_codec; + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_video_enc_preset(esp_gmf_element_handle_t self, esp_gmf_info_video_t *video_info, uint32_t dst_codec) +{ + ESP_GMF_NULL_CHECK(TAG, self, return ESP_GMF_ERR_INVALID_ARG); + venc_t *venc = (venc_t *)self; + if (venc_is_codec_supported(venc, video_info->format_id, dst_codec) == false) { + ESP_LOGE(TAG, "Not support encode from %s to %s", + esp_gmf_video_get_format_string(video_info->format_id), esp_gmf_video_get_format_string(dst_codec)); + return ESP_GMF_ERR_NOT_SUPPORT; + } + venc->parent.src_info = *video_info; + venc->dst_codec = (esp_video_codec_type_t)(dst_codec); + return 0; +} + +esp_gmf_err_t esp_gmf_video_enc_get_src_formats(esp_gmf_element_handle_t self, + uint32_t dst_codec, + const uint32_t **input_codecs, + uint8_t *input_codec_num) +{ + ESP_GMF_NULL_CHECK(TAG, self, return ESP_GMF_ERR_INVALID_ARG); + venc_t *venc = (venc_t *)self; + return venc_get_input_codecs(venc, dst_codec, input_codecs, input_codec_num); +} + +esp_gmf_err_t esp_gmf_video_enc_init(esp_gmf_video_enc_cfg_t *cfg, esp_gmf_element_handle_t *handle) +{ + ESP_GMF_MEM_CHECK(TAG, handle, return ESP_ERR_INVALID_ARG); + venc_t *venc = esp_gmf_oal_calloc(1, sizeof(venc_t)); + ESP_GMF_MEM_CHECK(TAG, venc, return ESP_ERR_NO_MEM); + esp_gmf_obj_t *obj = (esp_gmf_obj_t *)venc; + obj->new_obj = venc_el_new; + obj->del_obj = venc_el_destroy; + + int ret = esp_gmf_obj_set_tag(obj, "vid_enc"); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto VENC_FAIL, "Failed set OBJ tag"); + + uint8_t align = esp_gmf_oal_get_spiram_cache_align(); + esp_gmf_element_cfg_t el_cfg = { + .dependency = true, + }; + ESP_GMF_ELEMENT_IN_PORT_ATTR_SET(el_cfg.in_attr, ESP_GMF_EL_PORT_CAP_SINGLE, align, align, + ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, ESP_GMF_ELEMENT_PORT_DATA_SIZE_DEFAULT); + ESP_GMF_ELEMENT_OUT_PORT_ATTR_SET(el_cfg.out_attr, ESP_GMF_EL_PORT_CAP_SINGLE, align, align, + ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, ESP_GMF_ELEMENT_PORT_DATA_SIZE_DEFAULT); + ret = esp_gmf_video_el_init(venc, &el_cfg); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto VENC_FAIL, "Failed init video encoder el"); + if (cfg) { + esp_gmf_video_enc_cfg_t *enc_cfg = (esp_gmf_video_enc_cfg_t *) calloc(1, sizeof(esp_gmf_video_enc_cfg_t)); + ESP_GMF_MEM_CHECK(TAG, enc_cfg, { goto VENC_FAIL; }); + memcpy(enc_cfg, cfg, sizeof(esp_gmf_video_enc_cfg_t)); + esp_gmf_obj_set_config(obj, enc_cfg, sizeof(esp_gmf_video_enc_cfg_t)); + } + ESP_GMF_ELEMENT_GET(venc)->ops.open = venc_el_open; + ESP_GMF_ELEMENT_GET(venc)->ops.process = venc_el_process; + ESP_GMF_ELEMENT_GET(venc)->ops.close = venc_el_close; + ESP_GMF_ELEMENT_GET(venc)->ops.event_receiver = esp_gmf_video_handle_events; + ESP_GMF_ELEMENT_GET(venc)->ops.load_caps = venc_el_load_caps; + ESP_GMF_ELEMENT_GET(venc)->ops.load_methods = venc_el_load_methods; + + ESP_LOGI(TAG, "Create %s-%p", OBJ_GET_TAG(obj), obj); + *handle = (esp_gmf_element_handle_t) obj; + return ESP_OK; + +VENC_FAIL: + esp_gmf_obj_delete(obj); + return ret; +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/esp_gmf_video_fps_cvt.c b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/esp_gmf_video_fps_cvt.c new file mode 100644 index 0000000..76716c2 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/esp_gmf_video_fps_cvt.c @@ -0,0 +1,213 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#include +#include "esp_log.h" +#include "esp_gmf_err.h" +#include "esp_gmf_node.h" +#include "esp_gmf_oal_mem.h" +#include "esp_gmf_video_fps_cvt.h" +#include "esp_gmf_video_methods_def.h" +#include "esp_gmf_caps_def.h" +#include "esp_gmf_element.h" +#include "esp_gmf_video_element.h" +#include "esp_gmf_info.h" +#include "gmf_video_common.h" + +static const char *TAG = "VID_FPS_CVT"; + +/** + * @brief Video frame rate convert definition + */ +typedef struct { + esp_gmf_video_element_t parent; /*!< Video element parent */ + uint16_t dst_fps; /*!< Destination frame rate */ + uint32_t frame_num; /*!< Accumulated frame number that received */ + uint64_t start_pts; /*!< Started PTS for first received frame */ +} gmf_vid_rate_cvt_t; + +static esp_gmf_job_err_t gmf_vid_rate_cvt_open(esp_gmf_element_handle_t self, void *para) +{ + gmf_vid_rate_cvt_t *rate_cvt = (gmf_vid_rate_cvt_t *)self; + esp_gmf_info_video_t *src_info = &rate_cvt->parent.src_info; + if (rate_cvt->dst_fps == 0 || rate_cvt->dst_fps > src_info->fps) { + ESP_LOGE(TAG, "dst fps %d > src fps %d", rate_cvt->dst_fps, src_info->fps); + return ESP_GMF_JOB_ERR_FAIL; + } + esp_gmf_info_video_t vid_info = *src_info; + vid_info.fps = rate_cvt->dst_fps; + esp_gmf_element_notify_vid_info(self, &vid_info); + rate_cvt->frame_num = 0; + rate_cvt->start_pts = 0; + return ESP_GMF_JOB_ERR_OK; +} + +static bool rate_control_need_drop(gmf_vid_rate_cvt_t *rate_cvt, esp_gmf_payload_t *in_load) +{ + esp_gmf_info_video_t *src_info = &rate_cvt->parent.src_info; + if (rate_cvt->dst_fps == src_info->fps) { + return false; + } + if (rate_cvt->frame_num == 0) { + rate_cvt->start_pts = in_load->pts; + rate_cvt->frame_num++; + return false; + } + uint64_t expected_pts = rate_cvt->start_pts + (rate_cvt->frame_num) * 1000 / rate_cvt->dst_fps; + if (in_load->pts >= expected_pts) { + rate_cvt->frame_num++; + return false; + } + return true; +} + +static esp_gmf_job_err_t gmf_vid_rate_cvt_process(esp_gmf_element_handle_t self, void *para) +{ + gmf_vid_rate_cvt_t *rate_cvt = (gmf_vid_rate_cvt_t *)self; + int ret = 0; + esp_gmf_port_handle_t in_port = ESP_GMF_ELEMENT_GET(self)->in; + esp_gmf_port_handle_t out_port = ESP_GMF_ELEMENT_GET(self)->out; + esp_gmf_payload_t *in_load = NULL; + esp_gmf_payload_t *out_load = NULL; + ret = esp_gmf_port_acquire_in(in_port, &in_load, ESP_GMF_ELEMENT_GET(self)->in_attr.data_size, ESP_GMF_MAX_DELAY); + if (ret < 0) { + ESP_LOGE(TAG, "Read data error, ret:%d, line:%d", ret, __LINE__); + return ret == ESP_GMF_IO_ABORT ? ESP_GMF_JOB_ERR_OK : ESP_GMF_JOB_ERR_FAIL; + } + if (in_load->is_done && in_load->valid_size == 0) { + esp_gmf_port_release_in(in_port, in_load, 0); + return ESP_GMF_JOB_ERR_DONE; + } + // Handle drop logic + if (rate_control_need_drop(rate_cvt, in_load)) { + esp_gmf_port_release_in(in_port, in_load, ESP_GMF_MAX_DELAY); + return ESP_GMF_JOB_ERR_CONTINUE; + } + out_load = in_load; + ret = esp_gmf_port_acquire_out(out_port, &out_load, in_load->valid_size, ESP_GMF_MAX_DELAY); + if (ret < 0) { + ESP_LOGE(TAG, "Write data error, ret:%d, line:%d", ret, __LINE__); + return ret == ESP_GMF_IO_ABORT ? ESP_GMF_JOB_ERR_OK : ESP_GMF_JOB_ERR_FAIL; + } + esp_gmf_port_release_out(out_port, out_load, ESP_GMF_MAX_DELAY); + esp_gmf_port_release_in(in_port, in_load, ESP_GMF_MAX_DELAY); + return ret; +} + +static esp_gmf_job_err_t gmf_vid_rate_cvt_close(esp_gmf_element_handle_t self, void *para) +{ + gmf_vid_rate_cvt_t *rate_cvt = (gmf_vid_rate_cvt_t *)self; + rate_cvt->frame_num = 0; + rate_cvt->start_pts = 0; + return ESP_GMF_JOB_ERR_OK; +} + +static esp_gmf_err_t set_dst_fps(esp_gmf_element_handle_t self, esp_gmf_args_desc_t *arg_desc, uint8_t *buf, int buf_len) +{ + gmf_vid_rate_cvt_t *rate_cvt = (gmf_vid_rate_cvt_t *)self; + rate_cvt->dst_fps = *(uint16_t *)buf; + // After change fps reset frame number + rate_cvt->frame_num = 0; + return ESP_OK; +} + +static esp_gmf_err_t gmf_vid_rate_cvt_load_methods(esp_gmf_element_handle_t handle) +{ + esp_gmf_args_desc_t *set_args = NULL; + esp_gmf_method_t *methods = NULL; + esp_gmf_err_t ret = ESP_GMF_ERR_OK; + do { + ret = esp_gmf_args_desc_append(&set_args, VMETHOD_ARG(FPS_CVT, SET_FPS, FPS), ESP_GMF_ARGS_TYPE_UINT16, sizeof(uint16_t), 0); + GMF_VIDEO_BREAK_ON_FAIL(ret); + ret = esp_gmf_method_append(&methods, VMETHOD(FPS_CVT, SET_FPS), set_dst_fps, set_args); + ((esp_gmf_element_t *) handle)->method = methods; + return ESP_GMF_ERR_OK; + } while (0); + ESP_LOGE(TAG, "Fail to load methods"); + if (set_args) { + esp_gmf_args_desc_destroy(set_args); + } + if (methods) { + esp_gmf_method_destroy(methods); + } + return ESP_GMF_ERR_MEMORY_LACK; +} + +static esp_gmf_err_t gmf_vid_rate_cvt_load_caps(esp_gmf_element_handle_t handle) +{ + esp_gmf_cap_t *caps = NULL; + esp_gmf_cap_t cap = { + .cap_eightcc = ESP_GMF_CAPS_VIDEO_FPS_CVT + }; + esp_gmf_err_t ret = esp_gmf_cap_append(&caps, &cap); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, return ret, "Failed to append caps"); + ((esp_gmf_element_t *) handle)->caps = caps; + return ret; +} + +static esp_gmf_err_t gmf_vid_rate_cvt_new(void *cfg, esp_gmf_obj_handle_t *handle) +{ + return esp_gmf_video_fps_cvt_init(cfg, (esp_gmf_element_handle_t*)handle); +} + +static esp_gmf_err_t gmf_vid_rate_cvt_destroy(esp_gmf_element_handle_t self) +{ + gmf_vid_rate_cvt_t *rate_cvt = (gmf_vid_rate_cvt_t *)self; + esp_gmf_video_el_deinit(self); + if (rate_cvt) { + esp_gmf_oal_free(rate_cvt); + } + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_video_fps_cvt_init(void *config, esp_gmf_element_handle_t *handle) +{ + ESP_GMF_MEM_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + gmf_vid_rate_cvt_t *vid_rate_cvt = esp_gmf_oal_calloc(1, sizeof(gmf_vid_rate_cvt_t)); + ESP_GMF_MEM_CHECK(TAG, vid_rate_cvt, return ESP_GMF_ERR_MEMORY_LACK); + esp_gmf_obj_t *obj = (esp_gmf_obj_t *)vid_rate_cvt; + obj->new_obj = gmf_vid_rate_cvt_new; + obj->del_obj = gmf_vid_rate_cvt_destroy; + + esp_gmf_err_t ret = esp_gmf_obj_set_tag(obj, "vid_fps_cvt"); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto _cvt_init_fail, "Failed set OBJ tag"); + + esp_gmf_element_cfg_t el_cfg = { 0 }; + ESP_GMF_ELEMENT_IN_PORT_ATTR_SET(el_cfg.in_attr, ESP_GMF_EL_PORT_CAP_SINGLE, 0, 0, + ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, ESP_GMF_ELEMENT_PORT_DATA_SIZE_DEFAULT); + ESP_GMF_ELEMENT_OUT_PORT_ATTR_SET(el_cfg.out_attr, ESP_GMF_EL_PORT_CAP_SINGLE, 0, 0, + ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, ESP_GMF_ELEMENT_PORT_DATA_SIZE_DEFAULT); + el_cfg.dependency = true; + ret = esp_gmf_video_el_init(obj, &el_cfg); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto _cvt_init_fail, "Failed init video rate convert"); + + vid_rate_cvt->parent.base.ops.open = gmf_vid_rate_cvt_open; + vid_rate_cvt->parent.base.ops.process = gmf_vid_rate_cvt_process; + vid_rate_cvt->parent.base.ops.close = gmf_vid_rate_cvt_close; + vid_rate_cvt->parent.base.ops.event_receiver = esp_gmf_video_handle_events; + vid_rate_cvt->parent.base.ops.load_methods = gmf_vid_rate_cvt_load_methods; + vid_rate_cvt->parent.base.ops.load_caps = gmf_vid_rate_cvt_load_caps; + + *handle = obj; + return ESP_GMF_ERR_OK; + +_cvt_init_fail: + esp_gmf_obj_delete(obj); + return ret; +} + +esp_gmf_err_t esp_gmf_video_fps_cvt_set_fps(esp_gmf_element_handle_t handle, uint16_t fps) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + const esp_gmf_method_t *method_head = NULL; + const esp_gmf_method_t *method = NULL; + esp_gmf_element_get_method((esp_gmf_element_handle_t)handle, &method_head); + esp_gmf_method_found(method_head, VMETHOD(FPS_CVT, SET_FPS), &method); + uint8_t buf[2] = { 0 }; + esp_gmf_args_set_value(method->args_desc, VMETHOD_ARG(FPS_CVT, SET_FPS, FPS), buf, (uint8_t *)&fps, sizeof(fps)); + return esp_gmf_element_exe_method((esp_gmf_element_handle_t)handle, VMETHOD(FPS_CVT, SET_FPS), buf, sizeof(buf)); +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/esp_gmf_video_overlay.c b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/esp_gmf_video_overlay.c new file mode 100644 index 0000000..18f3fb8 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/esp_gmf_video_overlay.c @@ -0,0 +1,442 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#include +#include "esp_log.h" +#include "esp_gmf_err.h" +#include "esp_fourcc.h" +#include "esp_gmf_node.h" +#include "esp_gmf_oal_mem.h" +#include "esp_gmf_video_element.h" +#include "esp_gmf_info.h" +#include "esp_gmf_video_overlay.h" +#include "esp_gmf_video_methods_def.h" +#include "esp_gmf_caps_def.h" +#include "gmf_video_common.h" + +static const char *TAG = "OVERLAY_MIXER"; + +/** + * @brief Video pixel data definition + */ +typedef struct { + uint8_t *data; /*!< Video pixel data */ + uint32_t size; /*!< Video pixel data size */ +} esp_gmf_video_pixel_data_t; + +/** + * @brief Video overlay definition + */ +typedef struct { + esp_gmf_video_element_t parent; /*!< Video element parent */ + esp_gmf_port_handle_t overlay_port; /*!< GMF port for overlay */ + bool enable; /*!< Overlay enable setting from user */ + bool overlay_enabled; /*!< Whether overlay enabled or not */ + esp_gmf_overlay_rgn_info_t overlay_rgn; /*!< Overlay region info */ + uint8_t window_alpha; /*!< Overlay window alpha */ + bool is_open; /*!< Whether element is open or not */ +} gmf_vid_overlay_t; + +static esp_gmf_err_t sw_mixer_open(gmf_vid_overlay_t *mixer) +{ + // Currently only support RGB565 + if (mixer->parent.src_info.format_id != ESP_FOURCC_RGB16) { + ESP_LOGE(TAG, "Only support RGB565"); + return ESP_GMF_ERR_NOT_SUPPORT; + } + return ESP_GMF_ERR_OK; +} + +static inline uint16_t sw_mix_rgb565(uint16_t pixel_a, uint16_t pixel_b, uint8_t alpha) +{ + uint8_t inv_alpha = 255 - alpha; + uint8_t r_a = (pixel_a >> 11) & 0x1F; + uint8_t g_a = (pixel_a >> 5) & 0x3F; + uint8_t b_a = pixel_a & 0x1F; + + uint8_t r_b = (pixel_b >> 11) & 0x1F; + uint8_t g_b = (pixel_b >> 5) & 0x3F; + uint8_t b_b = pixel_b & 0x1F; + + uint8_t r = (r_a * inv_alpha + r_b * alpha) >> 8; + uint8_t g = (g_a * inv_alpha + g_b * alpha) >> 8; + uint8_t b = (b_a * inv_alpha + b_b * alpha) >> 8; + + return (r << 11) | (g << 5) | b; +} + +static esp_gmf_err_t sw_mixer_process(gmf_vid_overlay_t *mixer, esp_gmf_video_pixel_data_t *dst, + esp_gmf_video_pixel_data_t *window_data) +{ + if (mixer->window_alpha == 0) { + return ESP_GMF_ERR_OK; + } + esp_gmf_video_rgn_t *window = &mixer->overlay_rgn.dst_rgn; + esp_gmf_info_video_t *src_info = &mixer->parent.src_info; + + if (mixer->overlay_rgn.format_id == ESP_FOURCC_RGB16) { + // size check + int dst_size = src_info->width * src_info->height * 2; + int window_size = window->width * window->height * 2; + if (dst->size < dst_size || window_data->size < window_size) { + return ESP_GMF_ERR_INVALID_ARG; + } + uint16_t *dst_pixel = ((uint16_t *)dst->data) + window->y * src_info->width; + dst_pixel += window->x; + uint16_t *window_pixel = (uint16_t *)window_data->data; + if (mixer->window_alpha == 255) { + for (int i = 0; i < window->height; i++) { + memcpy(dst_pixel, window_pixel, window->width * 2); + dst_pixel += src_info->width; + window_pixel += window->width; + } + return ESP_GMF_ERR_OK; + } else { + for (int i = 0; i < window->height; i++) { + uint16_t *cur_dst = dst_pixel; + uint16_t *cur_window = window_pixel; + uint16_t *line_end = cur_window + window->width; + while (cur_window < line_end) { + *cur_dst = sw_mix_rgb565(*cur_dst, *cur_window, mixer->window_alpha); + cur_dst++; + cur_window++; + } + dst_pixel += src_info->width; + window_pixel = line_end; + } + return ESP_GMF_ERR_OK; + } + } + return ESP_GMF_ERR_NOT_SUPPORT; +} + +static esp_gmf_err_t overlay_enable(gmf_vid_overlay_t *overlay_mixer) +{ + esp_gmf_info_video_t *src_info = &overlay_mixer->parent.src_info; + // Element not open or no overlay set + if (overlay_mixer->overlay_port == NULL || overlay_mixer->enable == false || overlay_mixer->is_open == false) { + return ESP_GMF_ERR_OK; + } + if (overlay_mixer->overlay_enabled) { + return ESP_GMF_ERR_OK; + } + esp_gmf_video_rgn_t *rgn = &overlay_mixer->overlay_rgn.dst_rgn; + if (overlay_mixer->overlay_rgn.format_id != src_info->format_id || + rgn->x + rgn->width > src_info->width || + rgn->y + rgn->height > src_info->height) { + ESP_LOGE(TAG, "Wrong overlay region or codec settings"); + return ESP_GMF_ERR_NOT_SUPPORT; + } + esp_gmf_err_t ret = sw_mixer_open(overlay_mixer); + if (ret != ESP_GMF_ERR_OK) { + return ret; + } + overlay_mixer->overlay_enabled = true; + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t overlay_disable(gmf_vid_overlay_t *overlay_mixer) +{ + if (overlay_mixer->overlay_enabled == false) { + return ESP_GMF_ERR_INVALID_STATE; + } + overlay_mixer->overlay_enabled = false; + return ESP_GMF_ERR_OK; +} + +static esp_gmf_job_err_t gmf_vid_overlay_open(esp_gmf_element_handle_t self, void *para) +{ + gmf_vid_overlay_t *overlay_mixer = (gmf_vid_overlay_t *)self; + overlay_mixer->is_open = true; + esp_gmf_err_t ret = overlay_enable(overlay_mixer); + if (ret != ESP_GMF_ERR_OK) { + return ESP_GMF_JOB_ERR_FAIL; + } + esp_gmf_element_notify_vid_info(self, &overlay_mixer->parent.src_info); + return ESP_GMF_JOB_ERR_OK; +} + +static esp_gmf_job_err_t gmf_vid_overlay_process(esp_gmf_element_handle_t self, void *para) +{ + gmf_vid_overlay_t *overlay_mixer = (gmf_vid_overlay_t *)self; + int ret = 0; + esp_gmf_port_handle_t in_port = ESP_GMF_ELEMENT_GET(self)->in; + esp_gmf_port_handle_t out_port = ESP_GMF_ELEMENT_GET(self)->out; + esp_gmf_payload_t *in_load = NULL; + esp_gmf_payload_t *out_load = NULL; + ret = esp_gmf_port_acquire_in(in_port, &in_load, ESP_GMF_ELEMENT_GET(self)->in_attr.data_size, ESP_GMF_MAX_DELAY); + if (ret < 0) { + ESP_LOGE(TAG, "Read data error, ret:%d, line:%d", ret, __LINE__); + return ret == ESP_GMF_IO_ABORT ? ESP_GMF_JOB_ERR_OK : ESP_GMF_JOB_ERR_FAIL; + } + if (in_load->is_done && in_load->valid_size == 0) { + esp_gmf_port_release_in(in_port, in_load, 0); + return ESP_GMF_JOB_ERR_DONE; + } + out_load = in_load; + if (overlay_mixer->overlay_enabled && overlay_mixer->overlay_port) { + // Fetch overlay data + esp_gmf_payload_t *overlay_load = NULL; + ret = esp_gmf_port_acquire_in(overlay_mixer->overlay_port, &overlay_load, ESP_GMF_ELEMENT_GET(self)->in_attr.data_size, + ESP_GMF_MAX_DELAY); + if (ret >= 0) { + // TODO workaround use PTS to store alpha + uint8_t alpha = (uint8_t)overlay_load->pts; + overlay_mixer->window_alpha = alpha; + // Do mixer + esp_gmf_video_pixel_data_t dst_frame = { + .data = in_load->buf, + .size = in_load->valid_size, + }; + esp_gmf_video_pixel_data_t overlay_frame = { + .data = overlay_load->buf, + .size = overlay_load->valid_size, + }; + ret = sw_mixer_process(overlay_mixer, &dst_frame, &overlay_frame); + esp_gmf_port_release_in(overlay_mixer->overlay_port, overlay_load, ESP_GMF_MAX_DELAY); + } else { + ESP_LOGE(TAG, "Fail to fetch overlay data ret %d", ret); + } + } + ret = esp_gmf_port_acquire_out(out_port, &out_load, in_load->valid_size, ESP_GMF_MAX_DELAY); + if (ret < 0) { + ESP_LOGE(TAG, "Write data error, ret:%d, line:%d", ret, __LINE__); + return ret == ESP_GMF_IO_ABORT ? ESP_GMF_JOB_ERR_OK : ESP_GMF_JOB_ERR_FAIL; + } + esp_gmf_port_release_out(out_port, out_load, ESP_GMF_MAX_DELAY); + esp_gmf_port_release_in(in_port, in_load, ESP_GMF_MAX_DELAY); + return ret; +} + +static esp_gmf_job_err_t gmf_vid_overlay_close(esp_gmf_element_handle_t self, void *para) +{ + gmf_vid_overlay_t *overlay_mixer = (gmf_vid_overlay_t *)self; + overlay_disable(overlay_mixer); + overlay_mixer->is_open = false; + return ESP_GMF_JOB_ERR_OK; +} + +static esp_gmf_err_t gmf_vid_overlay_destroy(esp_gmf_element_handle_t self) +{ + gmf_vid_overlay_t *overlay_mixer = (gmf_vid_overlay_t *)self; + esp_gmf_video_el_deinit(self); + if (overlay_mixer != NULL) { + esp_gmf_oal_free(overlay_mixer); + } + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_video_overlay_set_overlay_port(esp_gmf_element_handle_t self, esp_gmf_port_handle_t port) +{ + ESP_GMF_NULL_CHECK(TAG, self, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, port, return ESP_GMF_ERR_INVALID_ARG); + const esp_gmf_method_t *method_head = NULL; + const esp_gmf_method_t *method = NULL; + esp_gmf_element_get_method(self, &method_head); + esp_gmf_method_found(method_head, VMETHOD(OVERLAY, SET_PORT), &method); + uint8_t buf[sizeof(esp_gmf_port_handle_t)]; + esp_gmf_args_set_value(method->args_desc, VMETHOD_ARG(OVERLAY, SET_PORT, PORT), buf, (uint8_t *)&port, sizeof(esp_gmf_port_handle_t)); + return esp_gmf_element_exe_method(self, VMETHOD(OVERLAY, SET_PORT), buf, sizeof(buf)); +} + +esp_gmf_err_t esp_gmf_video_overlay_set_alpha(esp_gmf_element_handle_t self, uint8_t alpha) +{ + ESP_GMF_NULL_CHECK(TAG, self, return ESP_GMF_ERR_INVALID_ARG); + const esp_gmf_method_t *method_head = NULL; + const esp_gmf_method_t *method = NULL; + esp_gmf_element_get_method(self, &method_head); + esp_gmf_method_found(method_head, VMETHOD(OVERLAY, SET_ALPHA), &method); + uint8_t buf[sizeof(uint8_t)]; + esp_gmf_args_set_value(method->args_desc, VMETHOD_ARG(OVERLAY, SET_ALPHA, ALPHA), buf, (uint8_t *)&alpha, sizeof(esp_gmf_port_handle_t)); + return esp_gmf_element_exe_method(self, VMETHOD(OVERLAY, SET_ALPHA), buf, sizeof(buf)); +} + +esp_gmf_err_t esp_gmf_video_overlay_set_rgn(esp_gmf_element_handle_t self, esp_gmf_overlay_rgn_info_t *rgn_info) +{ + ESP_GMF_NULL_CHECK(TAG, self, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, rgn_info, return ESP_GMF_ERR_INVALID_ARG); + const esp_gmf_method_t *method_head = NULL; + const esp_gmf_method_t *method = NULL; + esp_gmf_element_get_method(self, &method_head); + esp_gmf_method_found(method_head, VMETHOD(OVERLAY, SET_RGN), &method); + uint8_t buf[sizeof(esp_gmf_overlay_rgn_info_t)]; + esp_gmf_args_set_value(method->args_desc, VMETHOD_ARG(OVERLAY, SET_RGN, FMT), buf, (uint8_t *)&rgn_info->format_id, sizeof(uint32_t)); + esp_gmf_args_set_value(method->args_desc, VMETHOD_ARG(OVERLAY, SET_RGN, X), buf, (uint8_t *)&rgn_info->dst_rgn.x, sizeof(uint16_t)); + esp_gmf_args_set_value(method->args_desc, VMETHOD_ARG(OVERLAY, SET_RGN, Y), buf, (uint8_t *)&rgn_info->dst_rgn.y, sizeof(uint16_t)); + esp_gmf_args_set_value(method->args_desc, VMETHOD_ARG(OVERLAY, SET_RGN, WIDTH), buf, (uint8_t *)&rgn_info->dst_rgn.width, sizeof(uint16_t)); + esp_gmf_args_set_value(method->args_desc, VMETHOD_ARG(OVERLAY, SET_RGN, HEIGHT), buf, (uint8_t *)&rgn_info->dst_rgn.height, sizeof(uint16_t)); + return esp_gmf_element_exe_method(self, VMETHOD(OVERLAY, SET_RGN), buf, sizeof(buf)); +} + +esp_gmf_err_t esp_gmf_video_overlay_enable(esp_gmf_element_handle_t self, bool enable) +{ + ESP_GMF_NULL_CHECK(TAG, self, return ESP_GMF_ERR_INVALID_ARG); + const esp_gmf_method_t *method_head = NULL; + const esp_gmf_method_t *method = NULL; + esp_gmf_element_get_method(self, &method_head); + esp_gmf_method_found(method_head, VMETHOD(OVERLAY, OVERLAY_ENABLE), &method); + uint8_t buf[sizeof(bool)]; + esp_gmf_args_set_value(method->args_desc, VMETHOD_ARG(OVERLAY, OVERLAY_ENABLE, ENABLE), buf, (uint8_t *)&enable, sizeof(bool)); + return esp_gmf_element_exe_method(self, VMETHOD(OVERLAY, OVERLAY_ENABLE), buf, sizeof(buf)); +} + +static esp_gmf_err_t set_mixer_enable(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc, + uint8_t *buf, int buf_len) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, arg_desc, return ESP_GMF_ERR_INVALID_ARG); + gmf_vid_overlay_t *overlay_mixer = (gmf_vid_overlay_t *)handle; + overlay_mixer->enable = *(bool *)buf; + return overlay_mixer->enable ? overlay_enable(overlay_mixer) : overlay_disable(overlay_mixer); +} + +static esp_gmf_err_t set_mixer_rgn(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc, + uint8_t *buf, int buf_len) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, arg_desc, return ESP_GMF_ERR_INVALID_ARG); + gmf_vid_overlay_t *overlay_mixer = (gmf_vid_overlay_t *)handle; + overlay_mixer->overlay_rgn.format_id = *(uint32_t *)buf; + buf += sizeof(uint32_t); + overlay_mixer->overlay_rgn.dst_rgn.x = *(uint16_t *)(buf); + overlay_mixer->overlay_rgn.dst_rgn.y = *(uint16_t *)(buf + 2); + overlay_mixer->overlay_rgn.dst_rgn.width = *(uint16_t *)(buf + 4); + overlay_mixer->overlay_rgn.dst_rgn.height = *(uint16_t *)(buf + 6); + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t set_mixer_port(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc, + uint8_t *buf, int buf_len) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, arg_desc, return ESP_GMF_ERR_INVALID_ARG); + gmf_vid_overlay_t *overlay_mixer = (gmf_vid_overlay_t *)handle; + overlay_mixer->overlay_port = *(esp_gmf_port_handle_t *)buf; + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t set_mixer_alpha(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc, + uint8_t *buf, int buf_len) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, arg_desc, return ESP_GMF_ERR_INVALID_ARG); + gmf_vid_overlay_t *overlay_mixer = (gmf_vid_overlay_t *)handle; + overlay_mixer->window_alpha = *(uint8_t *)buf; + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t gmf_vid_overlay_load_methods(esp_gmf_element_handle_t handle) +{ + esp_gmf_args_desc_t *set_args = NULL; + esp_gmf_method_t *methods = NULL; + esp_gmf_err_t ret = ESP_GMF_ERR_OK; + do { + ret = esp_gmf_args_desc_append(&set_args, VMETHOD_ARG(OVERLAY, OVERLAY_ENABLE, ENABLE), ESP_GMF_ARGS_TYPE_UINT8, sizeof(uint8_t), 0); + GMF_VIDEO_BREAK_ON_FAIL(ret); + ret = esp_gmf_method_append(&methods, VMETHOD(OVERLAY, OVERLAY_ENABLE), set_mixer_enable, set_args); + GMF_VIDEO_BREAK_ON_FAIL(ret); + + set_args = NULL; + ret = esp_gmf_args_desc_append(&set_args, VMETHOD_ARG(OVERLAY, SET_RGN, FMT), ESP_GMF_ARGS_TYPE_UINT32, sizeof(uint32_t), 0); + GMF_VIDEO_BREAK_ON_FAIL(ret); + ret = esp_gmf_args_desc_append(&set_args, VMETHOD_ARG(OVERLAY, SET_RGN, X), ESP_GMF_ARGS_TYPE_UINT16, sizeof(uint16_t), 4); + GMF_VIDEO_BREAK_ON_FAIL(ret); + ret = esp_gmf_args_desc_append(&set_args, VMETHOD_ARG(OVERLAY, SET_RGN, Y), ESP_GMF_ARGS_TYPE_UINT16, sizeof(uint16_t), 6); + GMF_VIDEO_BREAK_ON_FAIL(ret); + ret = esp_gmf_args_desc_append(&set_args, VMETHOD_ARG(OVERLAY, SET_RGN, WIDTH), ESP_GMF_ARGS_TYPE_UINT16, sizeof(uint16_t), 8); + GMF_VIDEO_BREAK_ON_FAIL(ret); + ret = esp_gmf_args_desc_append(&set_args, VMETHOD_ARG(OVERLAY, SET_RGN, HEIGHT), ESP_GMF_ARGS_TYPE_UINT16, sizeof(uint16_t), 10); + GMF_VIDEO_BREAK_ON_FAIL(ret); + ret = esp_gmf_method_append(&methods, VMETHOD(OVERLAY, SET_RGN), set_mixer_rgn, set_args); + GMF_VIDEO_BREAK_ON_FAIL(ret); + + set_args = NULL; + ret = esp_gmf_args_desc_append(&set_args, VMETHOD_ARG(OVERLAY, SET_PORT, PORT), ESP_GMF_ARGS_TYPE_UINT32, sizeof(intptr_t), 0); + GMF_VIDEO_BREAK_ON_FAIL(ret); + ret = esp_gmf_method_append(&methods, VMETHOD(OVERLAY, SET_PORT), set_mixer_port, set_args); + GMF_VIDEO_BREAK_ON_FAIL(ret); + + set_args = NULL; + ret = esp_gmf_args_desc_append(&set_args, VMETHOD_ARG(OVERLAY, SET_ALPHA, ALPHA), ESP_GMF_ARGS_TYPE_UINT8, sizeof(uint8_t), 0); + GMF_VIDEO_BREAK_ON_FAIL(ret); + ret = esp_gmf_method_append(&methods, VMETHOD(OVERLAY, SET_ALPHA), set_mixer_alpha, set_args); + GMF_VIDEO_BREAK_ON_FAIL(ret); + + ((esp_gmf_element_t *) handle)->method = methods; + return ESP_GMF_ERR_OK; + } while (0); + ESP_LOGE(TAG, "Fail to load methods"); + if (set_args) { + esp_gmf_args_desc_destroy(set_args); + } + if (methods) { + esp_gmf_method_destroy(methods); + } + return ESP_GMF_ERR_MEMORY_LACK; +} + +static esp_gmf_err_t gmf_vid_overlay_load_caps(esp_gmf_element_handle_t handle) +{ + esp_gmf_cap_t *caps = NULL; + esp_gmf_cap_t cap = {0}; + esp_gmf_err_t ret = ESP_GMF_ERR_OK; + do { + cap.cap_eightcc = ESP_GMF_CAPS_VIDEO_OVERLAY; + cap.attr_fun = NULL; + ret = esp_gmf_cap_append(&caps, &cap); + GMF_VIDEO_BREAK_ON_FAIL(ret); + + ((esp_gmf_element_t *) handle)->caps = caps; + return ret; + } while (0); + if (caps) { + esp_gmf_cap_destroy(caps); + } + return ret; +} + +static esp_gmf_err_t gmf_vid_overlay_new(void *cfg, esp_gmf_obj_handle_t *handle) +{ + return esp_gmf_video_overlay_init(cfg, (esp_gmf_element_handle_t *)handle); +} + +esp_gmf_err_t esp_gmf_video_overlay_init(void *config, esp_gmf_element_handle_t *handle) +{ + ESP_GMF_MEM_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + gmf_vid_overlay_t *overlay_mixer = esp_gmf_oal_calloc(1, sizeof(gmf_vid_overlay_t)); + ESP_GMF_MEM_CHECK(TAG, overlay_mixer, return ESP_GMF_ERR_MEMORY_LACK); + esp_gmf_obj_t *obj = (esp_gmf_obj_t *)overlay_mixer; + obj->new_obj = gmf_vid_overlay_new; + obj->del_obj = gmf_vid_overlay_destroy; + + int ret = esp_gmf_obj_set_tag(obj, "vid_overlay"); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto _overlay_init_fail, "Failed set OBJ tag"); + + esp_gmf_element_cfg_t el_cfg = { 0 }; + ESP_GMF_ELEMENT_IN_PORT_ATTR_SET(el_cfg.in_attr, ESP_GMF_EL_PORT_CAP_MULTI, 0, 0, + ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, ESP_GMF_ELEMENT_PORT_DATA_SIZE_DEFAULT); + ESP_GMF_ELEMENT_OUT_PORT_ATTR_SET(el_cfg.out_attr, ESP_GMF_EL_PORT_CAP_SINGLE, 0, 0, + ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, ESP_GMF_ELEMENT_PORT_DATA_SIZE_DEFAULT); + el_cfg.dependency = true; + ret = esp_gmf_video_el_init(obj, &el_cfg); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto _overlay_init_fail, "Failed init video rate convert"); + + overlay_mixer->parent.base.ops.open = gmf_vid_overlay_open; + overlay_mixer->parent.base.ops.process = gmf_vid_overlay_process; + overlay_mixer->parent.base.ops.close = gmf_vid_overlay_close; + overlay_mixer->parent.base.ops.event_receiver = esp_gmf_video_handle_events; + overlay_mixer->parent.base.ops.load_methods = gmf_vid_overlay_load_methods; + overlay_mixer->parent.base.ops.load_caps = gmf_vid_overlay_load_caps; + + *handle = obj; + ESP_LOGI(TAG, "Create video overlay, %s-%p", OBJ_GET_TAG(obj), obj); + return ESP_GMF_ERR_OK; + +_overlay_init_fail: + esp_gmf_obj_delete(obj); + return ret; +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/esp_gmf_video_param.c b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/esp_gmf_video_param.c new file mode 100644 index 0000000..b327eed --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/esp_gmf_video_param.c @@ -0,0 +1,162 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#include "esp_gmf_video_param.h" + +#define PREPARE_VMETHOD_SETTING() \ + esp_gmf_method_exec_ctx_t exec_ctx = {}; \ + const esp_gmf_method_t* method_head = NULL; \ + esp_gmf_element_get_method(self, &method_head); \ + esp_gmf_err_t ret = esp_gmf_method_prepare_exec_ctx(method_head, method_name, &exec_ctx); \ + if (ret != ESP_GMF_ERR_OK) { \ + return ret; \ + } + +#define SET_METHOD_ARG(arg_name, value) \ + esp_gmf_args_set_value(exec_ctx.method->args_desc, arg_name, exec_ctx.exec_buf, (uint8_t*) &value, sizeof(value)); + +#define RELEASE_VMETHOD_SETTING() \ + ret = exec_ctx.method->func(self, exec_ctx.method->args_desc, exec_ctx.exec_buf, exec_ctx.buf_size); \ + esp_gmf_method_release_exec_ctx(&exec_ctx); \ + return ret; + +esp_gmf_err_t esp_gmf_video_param_set_dst_format(esp_gmf_element_handle_t self, uint32_t dst_fmt) +{ + const char *method_name = VMETHOD(CLR_CVT, SET_DST_FMT); + PREPARE_VMETHOD_SETTING(); + + SET_METHOD_ARG(VMETHOD_ARG(CLR_CVT, SET_DST_FMT, FMT), dst_fmt); + + RELEASE_VMETHOD_SETTING(); +} + +esp_gmf_err_t esp_gmf_video_param_set_fps(esp_gmf_element_handle_t self, uint16_t fps) +{ + const char *method_name = VMETHOD(FPS_CVT, SET_FPS); + PREPARE_VMETHOD_SETTING(); + + SET_METHOD_ARG(VMETHOD_ARG(FPS_CVT, SET_FPS, FPS), fps); + + RELEASE_VMETHOD_SETTING(); +} + +esp_gmf_err_t esp_gmf_video_param_set_dst_resolution(esp_gmf_element_handle_t self, esp_gmf_video_resolution_t *res) +{ + const char *method_name = VMETHOD(SCALER, SET_DST_RES); + PREPARE_VMETHOD_SETTING(); + + SET_METHOD_ARG(VMETHOD_ARG(SCALER, SET_DST_RES, WIDTH), res->width); + SET_METHOD_ARG(VMETHOD_ARG(SCALER, SET_DST_RES, HEIGHT), res->height); + + RELEASE_VMETHOD_SETTING(); +} + +esp_gmf_err_t esp_gmf_video_param_set_dst_codec(esp_gmf_element_handle_t self, uint32_t dst_codec) +{ + const char *method_name = VMETHOD(ENCODER, SET_DST_CODEC); + PREPARE_VMETHOD_SETTING(); + + SET_METHOD_ARG(VMETHOD_ARG(ENCODER, SET_DST_CODEC, CODEC), dst_codec); + + RELEASE_VMETHOD_SETTING(); +} + +esp_gmf_err_t esp_gmf_video_param_venc_preset(esp_gmf_element_handle_t self, esp_gmf_info_video_t *vid_info, + uint32_t dst_codec) +{ + const char *method_name = VMETHOD(ENCODER, PRESET); + PREPARE_VMETHOD_SETTING(); + + SET_METHOD_ARG(VMETHOD_ARG(ENCODER, PRESET, SRC_FMT), vid_info->format_id); + SET_METHOD_ARG(VMETHOD_ARG(ENCODER, PRESET, SRC_WIDTH), vid_info->width); + SET_METHOD_ARG(VMETHOD_ARG(ENCODER, PRESET, SRC_HEIGHT), vid_info->height); + SET_METHOD_ARG(VMETHOD_ARG(ENCODER, PRESET, SRC_FPS), vid_info->fps); + SET_METHOD_ARG(VMETHOD_ARG(ENCODER, PRESET, SRC_BITRATE), vid_info->bitrate); + SET_METHOD_ARG(VMETHOD_ARG(ENCODER, PRESET, DST_CODEC), dst_codec); + + RELEASE_VMETHOD_SETTING(); +} + +esp_gmf_err_t esp_gmf_video_param_get_src_fmts_by_codec(esp_gmf_element_handle_t self, uint32_t dst_codec, + const uint32_t **src_fmts, uint8_t *src_fmts_num) +{ + const char *method_name = VMETHOD(ENCODER, GET_SRC_FMTS); + PREPARE_VMETHOD_SETTING(); + + SET_METHOD_ARG(VMETHOD_ARG(ENCODER, GET_SRC_FMTS, DST_CODEC), dst_codec); + SET_METHOD_ARG(VMETHOD_ARG(ENCODER, GET_SRC_FMTS, SRC_FMTS_PTR), src_fmts); + SET_METHOD_ARG(VMETHOD_ARG(ENCODER, GET_SRC_FMTS, SRC_FMTS_NUM_PTR), src_fmts_num); + + RELEASE_VMETHOD_SETTING(); +} + +esp_gmf_err_t esp_gmf_video_param_set_src_codec(esp_gmf_element_handle_t self, uint32_t src_codec) +{ + const char *method_name = VMETHOD(DECODER, SET_SRC_CODEC); + PREPARE_VMETHOD_SETTING(); + + SET_METHOD_ARG(VMETHOD_ARG(DECODER, SET_SRC_CODEC, CODEC), src_codec); + + RELEASE_VMETHOD_SETTING(); +} + +esp_gmf_err_t esp_gmf_video_param_set_rotate_angle(esp_gmf_element_handle_t self, uint16_t degree) +{ + const char *method_name = VMETHOD(ROTATOR, SET_ANGLE); + PREPARE_VMETHOD_SETTING(); + + SET_METHOD_ARG(VMETHOD_ARG(ROTATOR, SET_ANGLE, DEGREE), degree); + + RELEASE_VMETHOD_SETTING(); +} + +esp_gmf_err_t esp_gmf_video_param_set_cropped_region(esp_gmf_element_handle_t self, esp_gmf_video_rgn_t *rgn) +{ + const char *method_name = VMETHOD(CROP, SET_CROP_RGN); + PREPARE_VMETHOD_SETTING(); + + SET_METHOD_ARG(VMETHOD_ARG(CROP, SET_CROP_RGN, X), rgn->x); + SET_METHOD_ARG(VMETHOD_ARG(CROP, SET_CROP_RGN, Y), rgn->y); + SET_METHOD_ARG(VMETHOD_ARG(CROP, SET_CROP_RGN, WIDTH), rgn->width); + SET_METHOD_ARG(VMETHOD_ARG(CROP, SET_CROP_RGN, HEIGHT), rgn->height); + + RELEASE_VMETHOD_SETTING(); +} + +esp_gmf_err_t esp_gmf_video_param_overlay_enable(esp_gmf_element_handle_t self, bool enable) +{ + const char *method_name = VMETHOD(OVERLAY, OVERLAY_ENABLE); + PREPARE_VMETHOD_SETTING(); + + SET_METHOD_ARG(VMETHOD_ARG(OVERLAY, OVERLAY_ENABLE, ENABLE), enable); + + RELEASE_VMETHOD_SETTING(); +} + +esp_gmf_err_t esp_gmf_video_param_set_overlay_port(esp_gmf_element_handle_t self, void *port) +{ + const char *method_name = VMETHOD(OVERLAY, SET_PORT); + PREPARE_VMETHOD_SETTING(); + + SET_METHOD_ARG(VMETHOD_ARG(OVERLAY, SET_PORT, PORT), port); + + RELEASE_VMETHOD_SETTING(); +} + +esp_gmf_err_t esp_gmf_video_param_set_overlay_rgn(esp_gmf_element_handle_t self, esp_gmf_overlay_rgn_info_t *rgn) +{ + const char *method_name = VMETHOD(OVERLAY, SET_RGN); + PREPARE_VMETHOD_SETTING(); + + SET_METHOD_ARG(VMETHOD_ARG(OVERLAY, SET_RGN, FMT), rgn->format_id); + SET_METHOD_ARG(VMETHOD_ARG(OVERLAY, SET_RGN, X), rgn->dst_rgn.x); + SET_METHOD_ARG(VMETHOD_ARG(OVERLAY, SET_RGN, Y), rgn->dst_rgn.y); + SET_METHOD_ARG(VMETHOD_ARG(OVERLAY, SET_RGN, WIDTH), rgn->dst_rgn.width); + SET_METHOD_ARG(VMETHOD_ARG(OVERLAY, SET_RGN, HEIGHT), rgn->dst_rgn.height); + + RELEASE_VMETHOD_SETTING(); +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/esp_gmf_video_ppa.c b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/esp_gmf_video_ppa.c new file mode 100644 index 0000000..892bc3c --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/esp_gmf_video_ppa.c @@ -0,0 +1,926 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#include +#include "esp_log.h" +#include "esp_gmf_err.h" +#include "esp_gmf_node.h" +#include "esp_gmf_oal_mem.h" +#include "esp_gmf_video_ppa.h" +#include "esp_gmf_video_methods_def.h" +#include "esp_gmf_caps_def.h" +#include "esp_gmf_element.h" +#include "esp_gmf_video_element.h" +#include "esp_gmf_info.h" +#include "gmf_video_common.h" +#include "esp_heap_caps.h" +#include "esp_fourcc.h" +#include "esp_cache.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "esp_idf_version.h" + +#if CONFIG_IDF_TARGET_ESP32P4 +#include "driver/ppa.h" +#include "esp_private/dma2d.h" +#include "hal/dma2d_types.h" +#include "hal/color_types.h" +#include "soc/dma2d_channel.h" +#endif + +static const char *TAG = "VCVT_EL"; + +#if CONFIG_IDF_TARGET_ESP32P4 + +/** + * @brief 2D-DMA EOF callback + */ +typedef bool (*dma2d_m2m_trans_eof_callback_t)(void *user_data); + +/** + * @brief 2D-DMA M2M transport configuration + */ +typedef struct { + intptr_t tx_desc_base_addr; /*!< 2D-DMA TX descriptor address */ + intptr_t rx_desc_base_addr; /*!< 2D-DMA RX descriptor address */ + dma2d_m2m_trans_eof_callback_t trans_eof_cb; /*!< Transfer EOF callback */ + void *user_data; /*!< User registered data to be passed into `trans_eof_cb` callback */ + dma2d_transfer_ability_t transfer_ability; /*!< Transfer ability */ + dma2d_strategy_config_t *tx_strategy_config; /*!< Pointer to a collection of 2D-DMA TX strategy configuration */ + dma2d_strategy_config_t *rx_strategy_config; /*!< Pointer to a collection of 2D-DMA RX strategy configuration */ + dma2d_csc_config_t *tx_csc_config; /*!< Pointer to a collection of 2D-DMA TX color space conversion configuration */ + dma2d_csc_config_t *rx_csc_config; /*!< Pointer to a collection of 2D-DMA RX color space conversion configuration */ +} dma2d_m2m_trans_config_t; + +/** + * @brief 2D-DMA M2M transaction definition + */ +typedef struct { + dma2d_m2m_trans_config_t m2m_trans_desc; /*!< M2M transport description configuration */ + dma2d_trans_config_t dma_chan_desc; /*!< 2D-DMA transport configuration */ + uint8_t dma_trans_placeholder_head[64]; /*!< DMA transport description place holder */ +} dma2d_m2m_transaction_t; + +/** + * @brief 2D-DMA information definition + */ +typedef struct { + dma2d_descriptor_t *rx_desc; /*!< Pointer to 2D-DMA RX description */ + dma2d_descriptor_t *tx_desc; /*!< Pointer to 2D-DMA TX description */ + dma2d_pool_handle_t handle; /*!< 2D-DMA pool handle */ + SemaphoreHandle_t sema; /*!< Semaphore for wait transfer done */ + dma2d_m2m_transaction_t trans; /*!< 2D-DMA M2M transaction */ + dma2d_csc_config_t tx_cvt; /*!< 2D-DMA TX color space conversion configuration */ +} dma2d_info_t; +#endif + +/** + * @brief Video PPA (Pixel Processing Accelerator) definition + */ +typedef struct { + esp_gmf_video_element_t parent; /*!< Video element parent */ + uint32_t dst_format; /*!< Color converter destination format */ + uint16_t dst_width; /*!< Scale destination width */ + uint16_t dst_height; /*!< Scale destination height */ + uint16_t rotate_degree; /*!< Rotation angle setting */ + esp_gmf_video_rgn_t crop_rgn; /*!< Cropped region setting */ + uint32_t out_frame_size; /*!< Output frame size of PPA */ + bool bypass; /*!< Whether PPA is bypassed or not */ +#if CONFIG_IDF_TARGET_ESP32P4 + ppa_client_handle_t ppa_handle; /*!< PPA client handle */ + ppa_srm_oper_config_t ppa_config; /*!< PPA SRM operation configuration */ + bool supported; /*!< Whether setting supported or not */ + bool use_ppa; /*!< Whether use PPA or 2D-DMA */ + dma2d_info_t dma2d_info; /*!< 2D-DMA information */ +#endif +} gmf_video_ppa_t; + +#if CONFIG_IDF_TARGET_ESP32P4 +static uint32_t get_frame_size(gmf_video_ppa_t *vid_cvt, uint32_t codec) +{ + switch (codec) { + case ESP_FOURCC_RGB24: + case ESP_FOURCC_BGR24: + return vid_cvt->dst_width * vid_cvt->dst_height * 3; + case ESP_FOURCC_RGB16: + case ESP_FOURCC_RGB16_BE: + return vid_cvt->dst_width * vid_cvt->dst_height * 2; + case ESP_FOURCC_YUV420P: + case ESP_FOURCC_OUYY_EVYY: + return vid_cvt->dst_width * vid_cvt->dst_height * 3 / 2; + case ESP_FOURCC_YUYV: + return vid_cvt->dst_width * vid_cvt->dst_height * 2; + default: + return 0; + } +} + +static ppa_srm_color_mode_t get_ppa_clr_mode(uint32_t codec) +{ + switch (codec) { + case ESP_FOURCC_RGB24: + case ESP_FOURCC_BGR24: + return PPA_SRM_COLOR_MODE_RGB888; + case ESP_FOURCC_RGB16_BE: + case ESP_FOURCC_RGB16: + return PPA_SRM_COLOR_MODE_RGB565; + case ESP_FOURCC_YUV420P: + case ESP_FOURCC_OUYY_EVYY: + return PPA_SRM_COLOR_MODE_YUV420; + default: + return (ppa_srm_color_mode_t)0; + } +} + +static bool check_2ddma_supported(gmf_video_ppa_t *vid_cvt) +{ + dma2d_info_t *dma2d = &vid_cvt->dma2d_info; + esp_gmf_info_video_t *src_info = &vid_cvt->parent.src_info; + if (src_info->format_id == ESP_FOURCC_RGB16_BE && vid_cvt->dst_format == ESP_FOURCC_BGR24) { + dma2d->tx_cvt.pre_scramble = 1; + } else if ((src_info->format_id == ESP_FOURCC_RGB16 && vid_cvt->dst_format == ESP_FOURCC_BGR24) || (src_info->format_id == ESP_FOURCC_BGR24 && vid_cvt->dst_format == ESP_FOURCC_RGB16)) { + dma2d->tx_cvt.pre_scramble = 0; + } else if ((src_info->format_id == ESP_FOURCC_RGB24 && vid_cvt->dst_format == ESP_FOURCC_RGB16)) { + dma2d->tx_cvt.pre_scramble = 5; + } else { + return false; + } + return true; +} + +static bool check_ppa_supported(gmf_video_ppa_t *vid_cvt) +{ + esp_gmf_info_video_t *src_info = &vid_cvt->parent.src_info; + if (src_info->format_id == vid_cvt->dst_format) { + return true; + } + if ((src_info->format_id == ESP_FOURCC_RGB16_BE && vid_cvt->dst_format == ESP_FOURCC_RGB16) || + (src_info->format_id == ESP_FOURCC_RGB16 && vid_cvt->dst_format == ESP_FOURCC_RGB16_BE)) { + vid_cvt->ppa_config.byte_swap = 1; + return true; + } + if ((src_info->format_id == ESP_FOURCC_RGB16 && vid_cvt->dst_format == ESP_FOURCC_BGR24) || + (src_info->format_id == ESP_FOURCC_OUYY_EVYY && vid_cvt->dst_format == ESP_FOURCC_RGB16) || + (src_info->format_id == ESP_FOURCC_OUYY_EVYY && vid_cvt->dst_format == ESP_FOURCC_BGR24) || + (src_info->format_id == ESP_FOURCC_RGB16 && vid_cvt->dst_format == ESP_FOURCC_OUYY_EVYY) || + (src_info->format_id == ESP_FOURCC_RGB16_BE && vid_cvt->dst_format == ESP_FOURCC_OUYY_EVYY)) { + return true; + } + if ((src_info->format_id == ESP_FOURCC_RGB24 && vid_cvt->dst_format == ESP_FOURCC_RGB16) || + (src_info->format_id == ESP_FOURCC_RGB16 && vid_cvt->dst_format == ESP_FOURCC_RGB24) || + (src_info->format_id == ESP_FOURCC_RGB16 && vid_cvt->dst_format == ESP_FOURCC_OUYY_EVYY) || + (src_info->format_id == ESP_FOURCC_RGB24 && vid_cvt->dst_format == ESP_FOURCC_BGR24) || + (src_info->format_id == ESP_FOURCC_BGR24 && vid_cvt->dst_format == ESP_FOURCC_RGB24)) { + vid_cvt->ppa_config.rgb_swap = 1; + return true; + } + return false; +} + +static bool check_rotation_angle(uint16_t degree, ppa_srm_rotation_angle_t *angle) +{ + switch (degree) { + case 0: + *angle = PPA_SRM_ROTATION_ANGLE_0; + break; + case 90: + *angle = PPA_SRM_ROTATION_ANGLE_90; + break; + case 180: + *angle = PPA_SRM_ROTATION_ANGLE_180; + break; + case 270: + *angle = PPA_SRM_ROTATION_ANGLE_270; + break; + default: + return false; + } + return true; +} + +static int open_ppa(gmf_video_ppa_t *vid_cvt) +{ + esp_gmf_info_video_t *src_info = &vid_cvt->parent.src_info; + ppa_client_config_t ppa_client_config = { + .oper_type = PPA_OPERATION_SRM, + .max_pending_trans_num = 1, + }; + ppa_register_client(&ppa_client_config, &vid_cvt->ppa_handle); + ESP_GMF_MEM_CHECK(TAG, vid_cvt->ppa_handle, return ESP_GMF_ERR_NOT_ENOUGH); + memset(&vid_cvt->ppa_config, 0, sizeof(ppa_srm_oper_config_t)); + uint32_t in_block_w = src_info->width; + uint32_t in_block_h = src_info->height; + float scale_x, scale_y; + if (vid_cvt->crop_rgn.width) { + in_block_w = vid_cvt->crop_rgn.width; + in_block_h = vid_cvt->crop_rgn.height; + vid_cvt->ppa_config.in.block_offset_x = vid_cvt->crop_rgn.x; + vid_cvt->ppa_config.in.block_offset_y = vid_cvt->crop_rgn.y; + } + if (vid_cvt->rotate_degree == 0 || vid_cvt->rotate_degree == 180) { + scale_x = 1.0 * vid_cvt->dst_width / in_block_w; + scale_y = 1.0 * vid_cvt->dst_height / in_block_h; + } else { + scale_x = 1.0 * vid_cvt->dst_height / in_block_w; + scale_y = 1.0 * vid_cvt->dst_width / in_block_h; + } + vid_cvt->ppa_config.in.pic_w = src_info->width; + vid_cvt->ppa_config.in.pic_h = src_info->height; + vid_cvt->ppa_config.in.block_w = in_block_w; + vid_cvt->ppa_config.in.block_h = in_block_h; + vid_cvt->ppa_config.in.srm_cm = get_ppa_clr_mode(src_info->format_id); + + vid_cvt->ppa_config.out.pic_w = vid_cvt->dst_width; + vid_cvt->ppa_config.out.pic_h = vid_cvt->dst_height; + vid_cvt->ppa_config.out.srm_cm = get_ppa_clr_mode(vid_cvt->dst_format); + + vid_cvt->ppa_config.rgb_swap = 0; + vid_cvt->ppa_config.byte_swap = 0; + check_ppa_supported(vid_cvt); + vid_cvt->ppa_config.mode = PPA_TRANS_MODE_BLOCKING; + + vid_cvt->ppa_config.scale_x = scale_x; + vid_cvt->ppa_config.scale_y = scale_y; + if (check_rotation_angle(vid_cvt->rotate_degree, &vid_cvt->ppa_config.rotation_angle) == false) { + return ESP_GMF_ERR_NOT_SUPPORT; + } + return ESP_GMF_ERR_OK; +} + +static int close_ppa(gmf_video_ppa_t *vid_cvt) +{ + if (vid_cvt->ppa_handle) { + ppa_unregister_client(vid_cvt->ppa_handle); + vid_cvt->ppa_handle = NULL; + } + return 0; +} + +static int ppa_convert(gmf_video_ppa_t *vid_cvt, esp_gmf_payload_t *in_load, esp_gmf_payload_t *out_load) +{ + if (vid_cvt->supported == false) { + return ESP_GMF_ERR_NOT_SUPPORT; + } + ESP_GMF_NULL_CHECK(TAG, vid_cvt->ppa_handle, return ESP_GMF_ERR_NOT_SUPPORT); + vid_cvt->ppa_config.in.buffer = in_load->buf; + vid_cvt->ppa_config.out.buffer = out_load->buf; + vid_cvt->ppa_config.out.buffer_size = out_load->buf_length; + int err = ppa_do_scale_rotate_mirror(vid_cvt->ppa_handle, &vid_cvt->ppa_config); + return err; +} + +static bool need_ppa(gmf_video_ppa_t *vid_cvt) +{ + esp_gmf_info_video_t *src_info = &vid_cvt->parent.src_info; + if (src_info->width != vid_cvt->dst_width || src_info->height != vid_cvt->dst_height) { + return true; + } + if (src_info->format_id == ESP_FOURCC_YUV420P || + vid_cvt->dst_format == ESP_FOURCC_YUV420P || + src_info->format_id == ESP_FOURCC_OUYY_EVYY || + vid_cvt->dst_format == ESP_FOURCC_OUYY_EVYY) { + return true; + } + if (check_2ddma_supported(vid_cvt)) { + return false; + } + return true; +} + +static bool dma2d_m2m_transaction_done_cb(dma2d_channel_handle_t dma2d_chan, dma2d_event_data_t *event_data, void *user_data) +{ + bool need_yield = false; + dma2d_m2m_transaction_t *trans_config = (dma2d_m2m_transaction_t*) user_data; + dma2d_m2m_trans_config_t *m2m_trans_desc = &trans_config->m2m_trans_desc; + if (m2m_trans_desc->trans_eof_cb) { + need_yield |= m2m_trans_desc->trans_eof_cb(m2m_trans_desc->user_data); + } + return need_yield; +} + +static bool dma2d_m2m_transaction_on_picked(uint32_t channel_num, const dma2d_trans_channel_info_t *dma2d_chans, void *user_config) +{ + dma2d_m2m_transaction_t *trans_config = (dma2d_m2m_transaction_t*) user_config; + dma2d_m2m_trans_config_t *m2m_trans_desc = &trans_config->m2m_trans_desc; + // Get the required 2D-DMA channel handles + uint32_t dma_tx_chan_idx = 0; + uint32_t dma_rx_chan_idx = 1; + if (dma2d_chans[0].dir == DMA2D_CHANNEL_DIRECTION_RX) { + dma_tx_chan_idx = 1; + dma_rx_chan_idx = 0; + } + + dma2d_channel_handle_t dma_tx_chan = dma2d_chans[dma_tx_chan_idx].chan; + dma2d_channel_handle_t dma_rx_chan = dma2d_chans[dma_rx_chan_idx].chan; + dma2d_trigger_t trig_periph = { + .periph = DMA2D_TRIG_PERIPH_M2M, + .periph_sel_id = SOC_DMA2D_TRIG_PERIPH_M2M_TX, + }; + dma2d_connect(dma_tx_chan, &trig_periph); + trig_periph.periph_sel_id = SOC_DMA2D_TRIG_PERIPH_M2M_RX; + dma2d_connect(dma_rx_chan, &trig_periph); + + dma2d_set_transfer_ability(dma_tx_chan, &m2m_trans_desc->transfer_ability); + dma2d_set_transfer_ability(dma_rx_chan, &m2m_trans_desc->transfer_ability); + + if (m2m_trans_desc->tx_strategy_config) { + dma2d_apply_strategy(dma_tx_chan, m2m_trans_desc->tx_strategy_config); + } + if (m2m_trans_desc->rx_strategy_config) { + dma2d_apply_strategy(dma_rx_chan, m2m_trans_desc->rx_strategy_config); + } + + if (m2m_trans_desc->tx_csc_config) { + dma2d_configure_color_space_conversion(dma_tx_chan, m2m_trans_desc->tx_csc_config); + } + if (m2m_trans_desc->rx_csc_config) { + dma2d_configure_color_space_conversion(dma_rx_chan, m2m_trans_desc->rx_csc_config); + } + dma2d_rx_event_callbacks_t dma_cbs = { + .on_recv_eof = dma2d_m2m_transaction_done_cb, + }; + dma2d_register_rx_event_callbacks(dma_rx_chan, &dma_cbs, (void *)trans_config); + dma2d_set_desc_addr(dma_tx_chan, m2m_trans_desc->tx_desc_base_addr); + dma2d_set_desc_addr(dma_rx_chan, m2m_trans_desc->rx_desc_base_addr); + dma2d_start(dma_tx_chan); + dma2d_start(dma_rx_chan); + return false; +} + +static bool IRAM_ATTR dma2d_m2m_suc_eof_event_cb(void *user_data) +{ + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + SemaphoreHandle_t sem = (SemaphoreHandle_t) user_data; + xSemaphoreGiveFromISR(sem, &xHigherPriorityTaskWoken); + return (xHigherPriorityTaskWoken == pdTRUE); +} + +static void dma2d_link_dscr_init(dma2d_descriptor_t *dma2d, uint32_t *next, void *buf_ptr, uint32_t ha, uint32_t va, + uint32_t hb, uint32_t vb, uint32_t eof, uint32_t en_2d, uint32_t pbyte, uint32_t mod, + uint32_t bias_x, uint32_t bias_y) +{ + dma2d->owner = DMA2D_DESCRIPTOR_BUFFER_OWNER_DMA; + dma2d->suc_eof = eof; + dma2d->dma2d_en = en_2d; + dma2d->err_eof = 0; + dma2d->hb_length = hb; + dma2d->vb_size = vb; + dma2d->pbyte = pbyte; + dma2d->ha_length = ha; + dma2d->va_size = va; + dma2d->mode = mod; + dma2d->y = bias_y; + dma2d->x = bias_x; + dma2d->buffer = buf_ptr; + dma2d->next = (dma2d_descriptor_t*) next; +} + +static int open_dma2d(gmf_video_ppa_t *vid_cvt) +{ + esp_gmf_info_video_t *src_info = &vid_cvt->parent.src_info; + dma2d_pool_config_t pool_config = { + .pool_id = 0, + }; + dma2d_info_t *dma2d = &vid_cvt->dma2d_info; + int ret = dma2d_acquire_pool(&pool_config, &dma2d->handle); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Fail to allocate for DMA2D"); + return ret; + } + uint8_t align = esp_gmf_oal_get_spiram_cache_align(); + dma2d->tx_desc = (dma2d_descriptor_t *)heap_caps_aligned_calloc(align, 1, 64, MALLOC_CAP_SPIRAM); + dma2d->rx_desc = (dma2d_descriptor_t *)heap_caps_aligned_calloc(align, 1, 64, MALLOC_CAP_SPIRAM); + dma2d->sema = xSemaphoreCreateCounting(1, 0); + if (dma2d->sema == NULL || dma2d->rx_desc == NULL || dma2d->tx_desc == NULL) { + ESP_GMF_MEM_CHECK(TAG, NULL, return ESP_GMF_ERR_MEMORY_LACK); + } + dma2d->trans.dma_chan_desc.tx_channel_num = 1; + dma2d->trans.dma_chan_desc.rx_channel_num = 1; + dma2d->trans.dma_chan_desc.channel_flags = DMA2D_CHANNEL_FUNCTION_FLAG_SIBLING; + dma2d->trans.dma_chan_desc.specified_tx_channel_mask = 0; + dma2d->trans.dma_chan_desc.specified_rx_channel_mask = 0; + dma2d->trans.dma_chan_desc.user_config = (void*) &dma2d->trans; + dma2d->trans.dma_chan_desc.on_job_picked = dma2d_m2m_transaction_on_picked; + if ((src_info->format_id == ESP_FOURCC_RGB24 || src_info->format_id == ESP_FOURCC_BGR24) && + (vid_cvt->dst_format == ESP_FOURCC_RGB16 || vid_cvt->dst_format == ESP_FOURCC_RGB16_BE)) { + dma2d->tx_cvt.tx_csc_option = DMA2D_CSC_TX_RGB888_TO_RGB565; + dma2d->tx_cvt.pre_scramble = DMA2D_SCRAMBLE_ORDER_BYTE0_1_2; + dma2d->trans.m2m_trans_desc.tx_csc_config = &dma2d->tx_cvt; + } else if ((src_info->format_id == ESP_FOURCC_RGB16_BE || src_info->format_id == ESP_FOURCC_RGB16) && + (vid_cvt->dst_format == ESP_FOURCC_RGB24 || vid_cvt->dst_format == ESP_FOURCC_BGR24)) { + dma2d->tx_cvt.tx_csc_option = DMA2D_CSC_TX_RGB565_TO_RGB888; + dma2d->tx_cvt.pre_scramble = DMA2D_SCRAMBLE_ORDER_BYTE2_1_0; + dma2d->trans.m2m_trans_desc.tx_csc_config = &dma2d->tx_cvt; + } + check_2ddma_supported(vid_cvt); + if (dma2d->trans.m2m_trans_desc.tx_csc_config) { + dma2d->trans.dma_chan_desc.channel_flags |= DMA2D_CHANNEL_FUNCTION_FLAG_TX_CSC; + } + if (dma2d->trans.m2m_trans_desc.rx_csc_config) { + dma2d->trans.dma_chan_desc.channel_flags |= DMA2D_CHANNEL_FUNCTION_FLAG_RX_CSC; + } + // TODO support other color conversion + dma2d->trans.m2m_trans_desc.tx_desc_base_addr = (intptr_t) dma2d->tx_desc; + dma2d->trans.m2m_trans_desc.rx_desc_base_addr = (intptr_t) dma2d->rx_desc; + + dma2d->trans.m2m_trans_desc.trans_eof_cb = dma2d_m2m_suc_eof_event_cb; + dma2d->trans.m2m_trans_desc.user_data = (void*) dma2d->sema; + dma2d_transfer_ability_t* trans_ability = &dma2d->trans.m2m_trans_desc.transfer_ability; + trans_ability->data_burst_length = DMA2D_DATA_BURST_LENGTH_128, trans_ability->desc_burst_en = true, + trans_ability->mb_size = DMA2D_MACRO_BLOCK_SIZE_NONE; + int src_size = get_frame_size(vid_cvt, src_info->format_id); + int dst_size = get_frame_size(vid_cvt, vid_cvt->dst_format); + + dma2d_link_dscr_init(dma2d->tx_desc, NULL, NULL, + src_size >> 14, src_size >> 14, + src_size & 0x3FFF, src_size & 0x3FFF, + 1, 0, DMA2D_DESCRIPTOR_PBYTE_1B0_PER_PIXEL, + DMA2D_DESCRIPTOR_BLOCK_RW_MODE_SINGLE, 0, 0); + dma2d_link_dscr_init(dma2d->rx_desc, NULL, NULL, + 0, dst_size >> 14, + 0, dst_size & 0x3FFF, + 0, 0, DMA2D_DESCRIPTOR_PBYTE_1B0_PER_PIXEL, + DMA2D_DESCRIPTOR_BLOCK_RW_MODE_SINGLE, 0, 0); + return ESP_GMF_ERR_OK; +} + +static void flush_src_dst_data(gmf_video_ppa_t *vid_cvt, esp_gmf_payload_t *in_load, esp_gmf_payload_t *out_load) +{ + esp_cache_msync((void *)in_load->buf, in_load->buf_length, ESP_CACHE_MSYNC_FLAG_DIR_C2M); + esp_cache_msync((void *)out_load->buf, out_load->buf_length, ESP_CACHE_MSYNC_FLAG_DIR_C2M); +} + +static void invalid_dst_data(gmf_video_ppa_t *vid_cvt, esp_gmf_payload_t *out_load) +{ + esp_cache_msync(out_load->buf, out_load->buf_length, ESP_CACHE_MSYNC_FLAG_DIR_M2C); +} + +static int dm2d_convert(gmf_video_ppa_t *vid_cvt, esp_gmf_payload_t *in_load, esp_gmf_payload_t *out_load) +{ + if (vid_cvt->supported == false) { + return ESP_GMF_ERR_NOT_SUPPORT; + } + dma2d_info_t *dma2d = &vid_cvt->dma2d_info; + flush_src_dst_data(vid_cvt, in_load, out_load); + // Set buffer for TX, RX desc + dma2d->tx_desc->buffer = (void *)in_load->buf; + dma2d->rx_desc->buffer = (void *)out_load->buf; + esp_cache_msync(dma2d->tx_desc, 64, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED); + esp_cache_msync(dma2d->rx_desc, 64, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED); + int ret = dma2d_enqueue(dma2d->handle, &dma2d->trans.dma_chan_desc, + (dma2d_trans_t *)dma2d->trans.dma_trans_placeholder_head); + if (ret != ESP_OK) { + return ret; + } + // Wait for DMA transfer to complete + xSemaphoreTake(dma2d->sema, portMAX_DELAY); + invalid_dst_data(vid_cvt, out_load); + return 0; +} + +static void close_dma2d(gmf_video_ppa_t *vid_cvt) +{ + dma2d_info_t *dma2d = &vid_cvt->dma2d_info; + if (dma2d->handle) { + dma2d_release_pool(dma2d->handle); + dma2d->handle = NULL; + } + if (dma2d->sema) { + vSemaphoreDelete(dma2d->sema); + dma2d->sema = NULL; + } + if (dma2d->tx_desc) { + heap_caps_free(dma2d->tx_desc); + dma2d->tx_desc = NULL; + } + if (dma2d->rx_desc) { + heap_caps_free(dma2d->rx_desc); + dma2d->rx_desc = NULL; + } +} +#endif + +static esp_gmf_job_err_t gmf_video_ppa_open(esp_gmf_element_handle_t self, void *para) +{ + gmf_video_ppa_t *vid_cvt = (gmf_video_ppa_t *)self; + esp_gmf_info_video_t *src_info = &vid_cvt->parent.src_info; + esp_gmf_info_video_t vid_info = vid_cvt->parent.src_info; + // Set Default info + if (vid_cvt->dst_width == 0) { + vid_cvt->dst_width = src_info->width; + if (vid_cvt->rotate_degree != 0 && vid_cvt->rotate_degree != 180) { + vid_cvt->dst_width = src_info->height; + } + } + if (vid_cvt->dst_height == 0) { + vid_cvt->dst_height = src_info->height; + if (vid_cvt->rotate_degree != 0 && vid_cvt->rotate_degree != 180) { + vid_cvt->dst_height = src_info->width; + } + } + if (vid_cvt->dst_format == 0) { + vid_cvt->dst_format = src_info->format_id; + } + if (vid_cvt->crop_rgn.width) { + if ((vid_cvt->crop_rgn.x + vid_cvt->crop_rgn.width > src_info->width) || (vid_cvt->crop_rgn.y + vid_cvt->crop_rgn.height > src_info->height)) { + ESP_LOGE(TAG, "Crop over limited w: %d + %d > %d h: %d + %d > %d", + vid_cvt->crop_rgn.x, vid_cvt->crop_rgn.width, src_info->width, + vid_cvt->crop_rgn.y, vid_cvt->crop_rgn.height, src_info->height); + return ESP_GMF_JOB_ERR_FAIL; + } + } + vid_cvt->bypass = false; + if (vid_cvt->dst_width == src_info->width && vid_cvt->dst_height == src_info->height && vid_cvt->dst_format == src_info->format_id) { + vid_cvt->bypass = true; + } + int ret = 0; + if (vid_cvt->bypass == false) { +#if CONFIG_IDF_TARGET_ESP32P4 + vid_cvt->supported = check_ppa_supported(vid_cvt) || check_2ddma_supported(vid_cvt); + if (vid_cvt->supported == false) { + ESP_LOGE(TAG, "Not support convert from %s to %s", + esp_gmf_video_get_format_string(src_info->format_id), esp_gmf_video_get_format_string(vid_cvt->dst_format)); + return ESP_GMF_ERR_NOT_SUPPORT; + } + // TODO decide to use DMA2D or PPA + vid_cvt->out_frame_size = get_frame_size(vid_cvt, vid_cvt->dst_format); + // Allocate memory for output frame + int cache_line_size = CONFIG_CACHE_L2_CACHE_LINE_SIZE; + ESP_GMF_ELEMENT_GET(vid_cvt)->out_attr.data_size = GMF_VIDEO_ALIGN_UP(vid_cvt->out_frame_size, cache_line_size); + vid_cvt->use_ppa = need_ppa(vid_cvt); + if (vid_cvt->use_ppa) { + ret = open_ppa(vid_cvt); + } else { + ret = open_dma2d(vid_cvt); + } + vid_info.format_id = vid_cvt->dst_format; + vid_info.width = vid_cvt->dst_width; + vid_info.height = vid_cvt->dst_height; + ESP_LOGI(TAG, "Convert in %s %dx%d to %s %dx%d ppa:%d", + esp_gmf_video_get_format_string(src_info->format_id), (int)src_info->width, (int)src_info->height, + esp_gmf_video_get_format_string(vid_info.format_id), (int)vid_info.width, (int)vid_info.height, + vid_cvt->use_ppa); +#else + ESP_LOGE(TAG, "Not support video convert except esp32p4"); + return ESP_GMF_JOB_ERR_FAIL; +#endif + } + esp_gmf_element_notify_vid_info(self, &vid_info); + return ret; +} + +static esp_gmf_job_err_t gmf_video_ppa_process(esp_gmf_element_handle_t self, void *para) +{ + gmf_video_ppa_t *vid_cvt = (gmf_video_ppa_t *)self; + int ret = 0; + esp_gmf_port_handle_t in_port = ESP_GMF_ELEMENT_GET(self)->in; + esp_gmf_port_handle_t out_port = ESP_GMF_ELEMENT_GET(self)->out; + esp_gmf_payload_t *in_load = NULL; + esp_gmf_payload_t *out_load = NULL; + ret = esp_gmf_port_acquire_in(in_port, &in_load, ESP_GMF_ELEMENT_GET(self)->in_attr.data_size, ESP_GMF_MAX_DELAY); + if (ret < 0) { + ESP_LOGE(TAG, "Read data error, ret:%d, line:%d", ret, __LINE__); + return ret == ESP_GMF_IO_ABORT ? ESP_GMF_JOB_ERR_OK : ESP_GMF_JOB_ERR_FAIL; + } + if (in_load->is_done && in_load->valid_size == 0) { + esp_gmf_port_release_in(in_port, in_load, 0); + return ESP_GMF_JOB_ERR_DONE; + } + uint32_t wanted_size = 0; + if (vid_cvt->bypass) { + out_load = in_load; + wanted_size = in_load->valid_size; + } else { + out_load = NULL; + wanted_size = ESP_GMF_ELEMENT_GET(vid_cvt)->out_attr.data_size; + } + ret = esp_gmf_port_acquire_out(out_port, &out_load, wanted_size, ESP_GMF_MAX_DELAY); + if (ret < 0) { + ESP_LOGE(TAG, "Write data error, ret:%d, line:%d", ret, __LINE__); + return ret == ESP_GMF_IO_ABORT ? ESP_GMF_JOB_ERR_OK : ESP_GMF_JOB_ERR_FAIL; + } + if (vid_cvt->bypass == false) { +#if CONFIG_IDF_TARGET_ESP32P4 + if (vid_cvt->use_ppa) { + ret = ppa_convert(vid_cvt, in_load, out_load); + } else { + ret = dm2d_convert(vid_cvt, in_load, out_load); + } + if (ret == 0) { + out_load->valid_size = vid_cvt->out_frame_size; + out_load->pts = in_load->pts; + } +#else + ret = ESP_GMF_JOB_ERR_FAIL; +#endif + } + esp_gmf_port_release_out(out_port, out_load, ESP_GMF_MAX_DELAY); + esp_gmf_port_release_in(in_port, in_load, ESP_GMF_MAX_DELAY); + return ret; +} + +static esp_gmf_job_err_t gmf_video_ppa_close(esp_gmf_element_handle_t self, void *para) +{ +#if CONFIG_IDF_TARGET_ESP32P4 + gmf_video_ppa_t *vid_cvt = (gmf_video_ppa_t *)self; + if (vid_cvt->use_ppa) { + close_ppa(vid_cvt); + } else { + close_dma2d(vid_cvt); + } +#endif + return ESP_GMF_JOB_ERR_OK; +} + +static esp_gmf_err_t gmf_video_ppa_destroy(esp_gmf_element_handle_t self) +{ + gmf_video_ppa_t *vid_cvt = (gmf_video_ppa_t *)self; + esp_gmf_video_el_deinit(self); + if (vid_cvt != NULL) { + esp_gmf_oal_free(vid_cvt); + } + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t set_dst_format(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc, + uint8_t *buf, int buf_len) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, arg_desc, return ESP_GMF_ERR_INVALID_ARG); + gmf_video_ppa_t *vid_cvt = (gmf_video_ppa_t *)handle; + vid_cvt->dst_format = *(uint32_t *)buf; + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t set_dst_resolution(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc, + uint8_t *buf, int buf_len) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, arg_desc, return ESP_GMF_ERR_INVALID_ARG); + gmf_video_ppa_t *vid_cvt = (gmf_video_ppa_t *)handle; + uint16_t *v = (uint16_t *)buf; + vid_cvt->dst_width = *(v++); + vid_cvt->dst_height = *v; + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t set_rotation(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc, + uint8_t *buf, int buf_len) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, arg_desc, return ESP_GMF_ERR_INVALID_ARG); + gmf_video_ppa_t *vid_cvt = (gmf_video_ppa_t *)handle; + uint16_t *v = (uint16_t *)buf; + vid_cvt->rotate_degree = *v; + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t set_crop(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc, + uint8_t *buf, int buf_len) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, arg_desc, return ESP_GMF_ERR_INVALID_ARG); + gmf_video_ppa_t *vid_cvt = (gmf_video_ppa_t *)handle; + uint16_t *v = (uint16_t *)buf; + vid_cvt->crop_rgn.x = *(v++); + vid_cvt->crop_rgn.y = *(v++); + vid_cvt->crop_rgn.width = *(v++); + vid_cvt->crop_rgn.height = *(v++); + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t gmf_video_ppa_new(void *cfg, esp_gmf_obj_handle_t *handle) +{ + return esp_gmf_video_ppa_init(cfg, (esp_gmf_element_handle_t*)handle); +} + +static esp_gmf_err_t gmf_video_ppa_load_methods(esp_gmf_element_handle_t handle) +{ + esp_gmf_args_desc_t *set_args = NULL; + esp_gmf_method_t *methods = NULL; + esp_gmf_err_t ret = ESP_GMF_ERR_OK; + do { + ret = esp_gmf_args_desc_append(&set_args, VMETHOD_ARG(CLR_CVT, SET_DST_FMT, FMT), ESP_GMF_ARGS_TYPE_UINT32, sizeof(uint32_t), 0); + GMF_VIDEO_BREAK_ON_FAIL(ret); + + ret = esp_gmf_method_append(&methods, VMETHOD(CLR_CVT, SET_DST_FMT), set_dst_format, set_args); + GMF_VIDEO_BREAK_ON_FAIL(ret); + + set_args = NULL; + ret = esp_gmf_args_desc_append(&set_args, VMETHOD_ARG(SCALER, SET_DST_RES, WIDTH), ESP_GMF_ARGS_TYPE_UINT16, sizeof(uint16_t), 0); + GMF_VIDEO_BREAK_ON_FAIL(ret); + ret = esp_gmf_args_desc_append(&set_args, VMETHOD_ARG(SCALER, SET_DST_RES, HEIGHT), ESP_GMF_ARGS_TYPE_UINT16, sizeof(uint16_t), sizeof(uint16_t)); + GMF_VIDEO_BREAK_ON_FAIL(ret); + ret = esp_gmf_method_append(&methods, VMETHOD(SCALER, SET_DST_RES), set_dst_resolution, set_args); + GMF_VIDEO_BREAK_ON_FAIL(ret); + + set_args = NULL; + ret = esp_gmf_args_desc_append(&set_args, VMETHOD_ARG(ROTATOR, SET_ANGLE, DEGREE), ESP_GMF_ARGS_TYPE_UINT16, sizeof(uint16_t), 0); + GMF_VIDEO_BREAK_ON_FAIL(ret); + ret = esp_gmf_method_append(&methods, VMETHOD(ROTATOR, SET_ANGLE), set_rotation, set_args); + GMF_VIDEO_BREAK_ON_FAIL(ret); + + set_args = NULL; + ret = esp_gmf_args_desc_append(&set_args, VMETHOD_ARG(CROP, SET_CROP_RGN, X), ESP_GMF_ARGS_TYPE_UINT16, sizeof(uint16_t), 0); + GMF_VIDEO_BREAK_ON_FAIL(ret); + ret = esp_gmf_args_desc_append(&set_args, VMETHOD_ARG(CROP, SET_CROP_RGN, Y), ESP_GMF_ARGS_TYPE_UINT16, sizeof(uint16_t), 2); + GMF_VIDEO_BREAK_ON_FAIL(ret); + ret = esp_gmf_args_desc_append(&set_args, VMETHOD_ARG(CROP, SET_CROP_RGN, WIDTH), ESP_GMF_ARGS_TYPE_UINT16, sizeof(uint16_t), 4); + GMF_VIDEO_BREAK_ON_FAIL(ret); + ret = esp_gmf_args_desc_append(&set_args, VMETHOD_ARG(CROP, SET_CROP_RGN, HEIGHT), ESP_GMF_ARGS_TYPE_UINT16, sizeof(uint16_t), 6); + GMF_VIDEO_BREAK_ON_FAIL(ret); + ret = esp_gmf_method_append(&methods, VMETHOD(CROP, SET_CROP_RGN), set_crop, set_args); + GMF_VIDEO_BREAK_ON_FAIL(ret); + ((esp_gmf_element_t *) handle)->method = methods; + return ESP_GMF_ERR_OK; + } while (0); + ESP_LOGE(TAG, "Fail to load methods"); + if (set_args) { + esp_gmf_args_desc_destroy(set_args); + } + if (methods) { + esp_gmf_method_destroy(methods); + } + return ESP_GMF_ERR_MEMORY_LACK; +} + +static esp_gmf_err_t gmf_video_ppa_load_caps(esp_gmf_element_handle_t handle) +{ + esp_gmf_cap_t *caps = NULL; + esp_gmf_cap_t cap = {0}; + esp_gmf_err_t ret = ESP_GMF_ERR_OK; + do { + cap.cap_eightcc = ESP_GMF_CAPS_VIDEO_COLOR_CONVERT; + cap.attr_fun = NULL; + ret = esp_gmf_cap_append(&caps, &cap); + GMF_VIDEO_BREAK_ON_FAIL(ret); + + cap.cap_eightcc = ESP_GMF_CAPS_VIDEO_SCALE; + ret = esp_gmf_cap_append(&caps, &cap); + GMF_VIDEO_BREAK_ON_FAIL(ret); + + cap.cap_eightcc = ESP_GMF_CAPS_VIDEO_CROP; + ret = esp_gmf_cap_append(&caps, &cap); + GMF_VIDEO_BREAK_ON_FAIL(ret); + + cap.cap_eightcc = ESP_GMF_CAPS_VIDEO_ROTATE; + ret = esp_gmf_cap_append(&caps, &cap); + GMF_VIDEO_BREAK_ON_FAIL(ret); + + ((esp_gmf_element_t *) handle)->caps = caps; + return ret; + } while (0); + if (caps) { + esp_gmf_cap_destroy(caps); + } + return ret; +} + +esp_gmf_err_t esp_gmf_video_ppa_init(void *config, esp_gmf_element_handle_t *handle) +{ + ESP_GMF_MEM_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + gmf_video_ppa_t *video_cvt = esp_gmf_oal_calloc(1, sizeof(gmf_video_ppa_t)); + ESP_GMF_MEM_CHECK(TAG, video_cvt, return ESP_GMF_ERR_MEMORY_LACK); + esp_gmf_obj_t *obj = (esp_gmf_obj_t *)video_cvt; + obj->new_obj = gmf_video_ppa_new; + obj->del_obj = gmf_video_ppa_destroy; + esp_gmf_err_t ret = esp_gmf_obj_set_tag(obj, "vid_ppa"); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto _cvt_init_fail, "Failed set OBJ tag"); + + uint8_t align = esp_gmf_oal_get_spiram_cache_align(); + esp_gmf_element_cfg_t el_cfg = { + .dependency = true, + }; + ESP_GMF_ELEMENT_IN_PORT_ATTR_SET(el_cfg.in_attr, ESP_GMF_EL_PORT_CAP_SINGLE, align, align, + ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, ESP_GMF_ELEMENT_PORT_DATA_SIZE_DEFAULT); + ESP_GMF_ELEMENT_OUT_PORT_ATTR_SET(el_cfg.out_attr, ESP_GMF_EL_PORT_CAP_SINGLE, align, align, + ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, ESP_GMF_ELEMENT_PORT_DATA_SIZE_DEFAULT); + ret = esp_gmf_video_el_init(obj, &el_cfg); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto _cvt_init_fail, "Failed to init video convert"); + + // Bind API + gmf_video_ppa_t *rate_cvt = (gmf_video_ppa_t *)obj; + rate_cvt->parent.base.ops.open = gmf_video_ppa_open; + rate_cvt->parent.base.ops.process = gmf_video_ppa_process; + rate_cvt->parent.base.ops.close = gmf_video_ppa_close; + rate_cvt->parent.base.ops.event_receiver = esp_gmf_video_handle_events; + rate_cvt->parent.base.ops.load_methods = gmf_video_ppa_load_methods; + rate_cvt->parent.base.ops.load_caps = gmf_video_ppa_load_caps; + + *handle = (esp_gmf_element_handle_t) rate_cvt; + return ESP_GMF_ERR_OK; + +_cvt_init_fail: + esp_gmf_obj_delete(obj); + return ret; +} + +esp_gmf_err_t esp_gmf_video_ppa_set_dst_format(esp_gmf_element_handle_t handle, uint32_t codec) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + const esp_gmf_method_t *method_head = NULL; + const esp_gmf_method_t *method = NULL; + esp_gmf_element_get_method((esp_gmf_element_handle_t)handle, &method_head); + esp_gmf_method_found(method_head, VMETHOD(CLR_CVT, SET_DST_FMT), &method); + ESP_GMF_NULL_CHECK(TAG, method_head, return ESP_GMF_ERR_NOT_SUPPORT); + uint8_t buf[4] = { 0 }; + esp_gmf_args_set_value(method->args_desc, VMETHOD_ARG(CLR_CVT, SET_DST_FMT, FMT), buf, (uint8_t *)&codec, sizeof(uint32_t)); + return esp_gmf_element_exe_method((esp_gmf_element_handle_t)handle, VMETHOD(CLR_CVT, SET_DST_FMT), buf, sizeof(buf)); +} + +esp_gmf_err_t esp_gmf_video_ppa_set_cropped_rgn(esp_gmf_element_handle_t handle, esp_gmf_video_rgn_t *rgn) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + const esp_gmf_method_t *method_head = NULL; + const esp_gmf_method_t *method = NULL; + esp_gmf_element_get_method((esp_gmf_element_handle_t)handle, &method_head); + esp_gmf_method_found(method_head, VMETHOD(CROP, SET_CROP_RGN), &method); + ESP_GMF_NULL_CHECK(TAG, method_head, return ESP_GMF_ERR_NOT_SUPPORT); + uint8_t buf[8]; + esp_gmf_args_set_value(method->args_desc, VMETHOD_ARG(CROP, SET_CROP_RGN, X), buf, (uint8_t *)&rgn->x, sizeof(uint16_t)); + esp_gmf_args_set_value(method->args_desc, VMETHOD_ARG(CROP, SET_CROP_RGN, Y), buf, (uint8_t *)&rgn->y, sizeof(uint16_t)); + esp_gmf_args_set_value(method->args_desc, VMETHOD_ARG(CROP, SET_CROP_RGN, WIDTH), buf, (uint8_t *)&rgn->width, sizeof(uint16_t)); + esp_gmf_args_set_value(method->args_desc, VMETHOD_ARG(CROP, SET_CROP_RGN, HEIGHT), buf, (uint8_t *)&rgn->height, sizeof(uint16_t)); + return esp_gmf_element_exe_method((esp_gmf_element_handle_t)handle, VMETHOD(CROP, SET_CROP_RGN), buf, sizeof(buf)); +} + +esp_gmf_err_t esp_gmf_video_ppa_set_rotation(esp_gmf_element_handle_t handle, uint16_t degree) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + const esp_gmf_method_t *method_head = NULL; + const esp_gmf_method_t *method = NULL; + esp_gmf_element_get_method((esp_gmf_element_handle_t)handle, &method_head); + esp_gmf_method_found(method_head, VMETHOD(ROTATOR, SET_ANGLE), &method); + ESP_GMF_NULL_CHECK(TAG, method_head, return ESP_GMF_ERR_NOT_SUPPORT); + uint8_t buf[2] = { 0 }; + esp_gmf_args_set_value(method->args_desc, VMETHOD_ARG(ROTATOR, SET_ANGLE, DEGREE), buf, (uint8_t *)°ree, sizeof(uint16_t)); + return esp_gmf_element_exe_method((esp_gmf_element_handle_t)handle, VMETHOD(ROTATOR, SET_ANGLE), buf, sizeof(buf)); +} + +esp_gmf_err_t esp_gmf_video_ppa_set_dst_resolution(esp_gmf_element_handle_t handle, esp_gmf_video_resolution_t *res) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, res, return ESP_GMF_ERR_INVALID_ARG); + const esp_gmf_method_t *method_head = NULL; + const esp_gmf_method_t *method = NULL; + esp_gmf_element_get_method((esp_gmf_element_handle_t)handle, &method_head); + esp_gmf_method_found(method_head, VMETHOD(SCALER, SET_DST_RES), &method); + ESP_GMF_NULL_CHECK(TAG, method_head, return ESP_GMF_ERR_NOT_SUPPORT); + uint8_t buf[4] = { 0 }; + esp_gmf_args_set_value(method->args_desc, VMETHOD_ARG(SCALER, SET_DST_RES, WIDTH), buf, + (uint8_t *)&res->width, sizeof(uint16_t)); + esp_gmf_args_set_value(method->args_desc, VMETHOD_ARG(SCALER, SET_DST_RES, HEIGHT), + buf, (uint8_t *)&res->height, sizeof(uint16_t)); + return esp_gmf_element_exe_method((esp_gmf_element_handle_t)handle, VMETHOD(SCALER, SET_DST_RES), buf, sizeof(buf)); +} + +/** + * @brief This API is for debug only + */ +int gmf_video_ppa_test(uint32_t from_codec, int32_t to_codec, uint32_t width, uint32_t height, uint8_t *src, uint8_t *dst, int v) +{ + esp_gmf_obj_handle_t cvt = NULL; + gmf_video_ppa_new(NULL, &cvt); + gmf_video_ppa_t *vid_cvt = (gmf_video_ppa_t *)cvt; + esp_gmf_info_video_t vid_info = { + .format_id = from_codec, + .width = width, + .height = height, + }; + vid_cvt->parent.src_info = vid_info; + vid_cvt->dst_format = to_codec; + vid_cvt->dst_width = width; + vid_cvt->dst_height = height; + gmf_video_ppa_open(cvt, NULL); + int ret = 0; +#if CONFIG_IDF_TARGET_ESP32P4 + esp_gmf_payload_t in_load = { + .buf = src, + .buf_length = get_frame_size(vid_cvt, from_codec), + }; + esp_gmf_payload_t out_load = { + .buf = dst, + .buf_length = get_frame_size(vid_cvt, to_codec), + }; + if (v != -1) { + vid_cvt->ppa_config.rgb_swap = v & 0x1; + vid_cvt->ppa_config.byte_swap = v & 0x2; + dma2d_info_t *dma2d = &vid_cvt->dma2d_info; + dma2d->tx_cvt.pre_scramble = v; + } + ESP_LOGI(TAG, "RGB swap:%d byteswap:%d scramble:%d", vid_cvt->ppa_config.rgb_swap, vid_cvt->ppa_config.byte_swap, v); + if (vid_cvt->use_ppa) { + ret = ppa_convert(vid_cvt, &in_load, &out_load); + } else { + ret = dm2d_convert(vid_cvt, &in_load, &out_load); + } +#endif + gmf_video_ppa_close(cvt, NULL); + gmf_video_ppa_destroy(cvt); + return ret; +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/esp_gmf_video_rotate.c b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/esp_gmf_video_rotate.c new file mode 100644 index 0000000..179ff24 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/esp_gmf_video_rotate.c @@ -0,0 +1,316 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#include +#include +#include "esp_log.h" +#include "esp_gmf_oal_mutex.h" +#include "esp_gmf_oal_mem.h" +#include "esp_gmf_err.h" +#include "esp_gmf_node.h" +#include "esp_gmf_video_rotate.h" +#include "esp_gmf_video_methods_def.h" +#include "esp_gmf_video_element.h" +#include "gmf_video_common.h" +#include "esp_gmf_caps_def.h" + +static const char *TAG = "IMGFX_ROTATE_EL"; + +typedef struct _gmf_imgfx_rotate_t { + esp_gmf_video_element_t parent; + esp_imgfx_rotate_handle_t hd; + bool need_recfg; +} esp_gmf_rotate_hd_t; + +static esp_gmf_job_err_t video_rotate_el_open(esp_gmf_element_handle_t self, void *para) +{ + esp_gmf_rotate_hd_t *video_el = (esp_gmf_rotate_hd_t *)self; + // Get and check config + esp_imgfx_rotate_cfg_t *cfg = (esp_imgfx_rotate_cfg_t *)OBJ_GET_CFG(self); + ESP_GMF_CHECK(TAG, cfg, return ESP_GMF_JOB_ERR_FAIL, "Failed to get rotate config"); + // Open rotate module + esp_imgfx_rotate_open(cfg, &video_el->hd); + ESP_GMF_CHECK(TAG, video_el->hd, return ESP_GMF_JOB_ERR_FAIL, "Failed to create rotate handle"); + // Get video size + esp_imgfx_get_image_size(cfg->in_pixel_fmt, &cfg->in_res, (uint32_t *)&(ESP_GMF_ELEMENT_GET(video_el)->in_attr.data_size)); + esp_imgfx_resolution_t res; + esp_imgfx_rotate_get_rotated_resolution(video_el->hd, &res); + esp_imgfx_get_image_size(cfg->in_pixel_fmt, &res, (uint32_t *)&(ESP_GMF_ELEMENT_GET(video_el)->out_attr.data_size)); + // Report information to the next element, the next element will use this information to configure + gmf_video_update_info(self, res.width, res.height, cfg->in_pixel_fmt); + // The video_el->hd has been opened using newest configuration, so it can set the need_recfg to false + video_el->need_recfg = false; + ESP_LOGD(TAG, "Open, %p", self); + return ESP_GMF_JOB_ERR_OK; +} + +static esp_gmf_job_err_t video_rotate_el_process(esp_gmf_element_handle_t self, void *para) +{ + esp_gmf_rotate_hd_t *video_el = (esp_gmf_rotate_hd_t *)self; + esp_imgfx_data_t in_image; + esp_imgfx_data_t out_image; + esp_imgfx_rotate_cfg_t *cfg = (esp_imgfx_rotate_cfg_t *)OBJ_GET_CFG(self); + ESP_GMF_MEM_CHECK(TAG, cfg, return ESP_GMF_JOB_ERR_FAIL); + bool bypass = cfg->degree % 360 == 0; + if (video_el->need_recfg) { + // Reset rotate configuration + esp_imgfx_err_t imgfx_ret = esp_imgfx_rotate_set_cfg(video_el->hd, cfg); + if (imgfx_ret != ESP_IMGFX_ERR_OK) { + return ESP_GMF_JOB_ERR_FAIL; + } + // Get video size + esp_imgfx_get_image_size(cfg->in_pixel_fmt, &cfg->in_res, (uint32_t *)&(ESP_GMF_ELEMENT_GET(video_el)->in_attr.data_size)); + esp_imgfx_resolution_t res; + esp_imgfx_rotate_get_rotated_resolution(video_el->hd, &res); + esp_imgfx_get_image_size(cfg->in_pixel_fmt, &res, (uint32_t *)&(ESP_GMF_ELEMENT_GET(video_el)->out_attr.data_size)); + video_el->need_recfg = false; + } + esp_gmf_port_handle_t in_port = ESP_GMF_ELEMENT_GET(self)->in; + esp_gmf_port_handle_t out_port = ESP_GMF_ELEMENT_GET(self)->out; + esp_gmf_payload_t *in_load = NULL; + esp_gmf_payload_t *out_load = NULL; + esp_gmf_job_err_t ret = ESP_GMF_JOB_ERR_OK; + esp_gmf_err_io_t load_ret = ESP_GMF_IO_OK; + ret = video_el_acquire_payload(in_port, out_port, &in_load, &out_load, (ESP_GMF_ELEMENT_GET(video_el)->in_attr.data_size), + (ESP_GMF_ELEMENT_GET(video_el)->out_attr.data_size), bypass); + if (ret != ESP_GMF_JOB_ERR_OK) { + goto __release; + } + /* It is for bypass */ + if (in_load == out_load) { + goto __release; + } + if (in_load->is_done) { + out_load->is_done = in_load->is_done; + out_load->pts = in_load->pts; + ret = ESP_GMF_JOB_ERR_DONE; + ESP_LOGD(TAG, "It's done, out: %d", in_load->valid_size); + goto __release; + } + in_image.data = in_load->buf; + in_image.data_len = in_load->valid_size; + out_image.data = out_load->buf; + out_image.data_len = out_load->buf_length; + esp_imgfx_err_t imgfx_ret = esp_imgfx_rotate_process(video_el->hd, &in_image, &out_image); + if (imgfx_ret != ESP_IMGFX_ERR_OK) { + ESP_LOGE(TAG, "Image effects rotate process failed, ret: %d-%p", imgfx_ret, video_el); + ret = ESP_GMF_JOB_ERR_FAIL; + goto __release; + } + // Copy the information to the output payload. The next element will use this information to process + out_load->is_done = in_load->is_done; + out_load->valid_size = (ESP_GMF_ELEMENT_GET(video_el)->out_attr.data_size); + out_load->pts = in_load->pts; +__release: + // Release in and out port + if (out_load != NULL) { + load_ret = esp_gmf_port_release_out(out_port, out_load, ESP_GMF_MAX_DELAY); + if ((load_ret < ESP_GMF_IO_OK) && (load_ret != ESP_GMF_IO_ABORT)) { + ESP_LOGE(TAG, "OUT port release error, ret:%d", load_ret); + ret = ESP_GMF_JOB_ERR_FAIL; + } + } + if (in_load != NULL) { + load_ret = esp_gmf_port_release_in(in_port, in_load, ESP_GMF_MAX_DELAY); + if ((load_ret < ESP_GMF_IO_OK) && (load_ret != ESP_GMF_IO_ABORT)) { + ESP_LOGE(TAG, "IN port release error, ret:%d", load_ret); + ret = ESP_GMF_JOB_ERR_FAIL; + } + } + return ret; +} + +static esp_gmf_job_err_t video_rotate_el_close(esp_gmf_element_handle_t self, void *para) +{ + ESP_LOGD(TAG, "Closed, %p", self); + esp_gmf_rotate_hd_t *video_el = (esp_gmf_rotate_hd_t *)self; + if (video_el) { + if (video_el->hd) { + esp_imgfx_rotate_close(video_el->hd); + video_el->hd = NULL; + } + } + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t video_rotate_el_delete(esp_gmf_obj_handle_t handle) +{ + ESP_LOGD(TAG, "Deleted, %p", handle); + void *cfg = OBJ_GET_CFG(handle); + if (cfg) { + esp_gmf_oal_free(cfg); + } + esp_gmf_video_el_deinit(handle); + esp_gmf_oal_free(handle); + return ESP_GMF_ERR_OK; +} + +static inline esp_gmf_err_t esp_gmf_video_rotate_set_cfg(esp_gmf_element_handle_t self, esp_imgfx_rotate_cfg_t *config) +{ + ESP_GMF_NULL_CHECK(TAG, self, return ESP_GMF_JOB_ERR_FAIL); + ESP_GMF_NULL_CHECK(TAG, config, return ESP_GMF_JOB_ERR_FAIL); + esp_gmf_rotate_hd_t *video_el = (esp_gmf_rotate_hd_t *)self; + esp_gmf_obj_t *obj = (esp_gmf_obj_t *)video_el; + // reset obj config + if (obj->cfg) { + memcpy(obj->cfg, config, sizeof(esp_imgfx_rotate_cfg_t)); + video_el->need_recfg = true; + return ESP_GMF_JOB_ERR_OK; + } + ESP_LOGE(TAG, " %s-%p is no configured yet", __func__, self); + return ESP_GMF_JOB_ERR_FAIL; +} + +static inline esp_gmf_err_t esp_gmf_video_rotate_get_cfg(esp_gmf_element_handle_t self, esp_imgfx_rotate_cfg_t *config) +{ + ESP_GMF_NULL_CHECK(TAG, self, return ESP_GMF_JOB_ERR_FAIL); + esp_gmf_rotate_hd_t *video_el = (esp_gmf_rotate_hd_t *)self; + // Get cfg from rotate handle if handle valid or-else get from object + if (video_el->hd) { + esp_imgfx_err_t imgfx_ret = esp_imgfx_rotate_get_cfg(video_el->hd, config); + if (imgfx_ret != ESP_IMGFX_ERR_OK) { + ESP_LOGE(TAG, "Get video effects rotate cfg failed, hd:%p, ret: %d", self, imgfx_ret); + return ESP_GMF_JOB_ERR_FAIL; + } + return ESP_GMF_JOB_ERR_OK; + } else { + esp_gmf_obj_t *obj = (esp_gmf_obj_t *)video_el; + // Get obj config + if (obj->cfg) { + memcpy(config, obj->cfg, sizeof(esp_imgfx_rotate_cfg_t)); + return ESP_GMF_JOB_ERR_OK; + } + } + ESP_LOGE(TAG, " %s-%p is no configured yet", __func__, self); + return ESP_GMF_JOB_ERR_FAIL; +} + +static esp_gmf_err_t video_set_dst_degree(void *handle, esp_gmf_args_desc_t *arg_desc, uint8_t *buf, int buf_len) +{ + ESP_GMF_MEM_CHECK(TAG, buf, return ESP_GMF_ERR_INVALID_ARG); + return esp_gmf_video_rotate_set_rotation(handle, *(uint16_t *)buf); +} + +static esp_gmf_err_t video_rotate_el_load_caps(esp_gmf_element_handle_t handle) +{ + esp_gmf_cap_t *caps = NULL; + esp_gmf_cap_t dec_caps = {0}; + dec_caps.cap_eightcc = ESP_GMF_CAPS_VIDEO_ROTATE; + dec_caps.attr_fun = NULL; + int ret = esp_gmf_cap_append(&caps, &dec_caps); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, return ret, "Failed to create capability"); + esp_gmf_element_t *el = (esp_gmf_element_t *)handle; + el->caps = caps; + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t video_rotate_el_load_methods(esp_gmf_element_handle_t handle) +{ + esp_gmf_method_t *method = NULL; + esp_gmf_err_t ret = ESP_GMF_ERR_OK; + esp_gmf_args_desc_t *set_args = NULL; + ret = esp_gmf_args_desc_append(&set_args, VMETHOD_ARG(ROTATOR, SET_ANGLE, DEGREE), ESP_GMF_ARGS_TYPE_UINT16, sizeof(uint16_t), 0); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, return ret, "Failed to append rotation degree"); + ret = esp_gmf_method_append(&method, VMETHOD(ROTATOR, SET_ANGLE), video_set_dst_degree, set_args); + ESP_GMF_RET_ON_ERROR(TAG, ret, return ret, "Failed to register %s method", VMETHOD(ROTATOR, SET_ANGLE)); + esp_gmf_element_t *el = (esp_gmf_element_t *)handle; + el->method = method; + return ret; +} + +static esp_gmf_err_t video_rotate_el_received_event_handler(esp_gmf_event_pkt_t *evt, void *ctx) +{ + ESP_GMF_NULL_CHECK(TAG, ctx, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, evt, return ESP_GMF_ERR_INVALID_ARG); + if ((evt->type != ESP_GMF_EVT_TYPE_REPORT_INFO) + || (evt->sub != ESP_GMF_INFO_VIDEO) + || (evt->payload == NULL)) { + return ESP_GMF_ERR_OK; + } + esp_gmf_element_handle_t self = (esp_gmf_element_handle_t)ctx; + esp_gmf_element_handle_t el = evt->from; + esp_gmf_event_state_t state = ESP_GMF_EVENT_STATE_NONE; + esp_gmf_element_get_state(self, &state); + esp_gmf_info_video_t *info = (esp_gmf_info_video_t *)evt->payload; + esp_gmf_video_el_set_src_info(self, info); + esp_imgfx_rotate_cfg_t *config = (esp_imgfx_rotate_cfg_t *)OBJ_GET_CFG(self); + ESP_GMF_NULL_CHECK(TAG, config, return ESP_GMF_ERR_FAIL); + esp_gmf_rotate_hd_t *video_el = (esp_gmf_rotate_hd_t *)self; + GMF_VIDEO_UPDATE_CONFIG(config, info, video_el->need_recfg); + ESP_LOGD(TAG, "RECV element info, from: %s-%p, next: %p, self: %s-%p, type: %x, state: %s, width: %d, height: %d, pixel format: %lx", + OBJ_GET_TAG(el), el, esp_gmf_node_for_next((esp_gmf_node_t *)el), OBJ_GET_TAG(self), self, evt->type, + esp_gmf_event_get_state_str(state), info->width, info->height, info->format_id); + // It is for the first time to receive event from previous element + if (state == ESP_GMF_EVENT_STATE_NONE) { + esp_gmf_element_set_state(self, ESP_GMF_EVENT_STATE_INITIALIZED); + } + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t video_rotate_el_new(void *config, esp_gmf_obj_handle_t *handle) +{ + return esp_gmf_video_rotate_init((esp_imgfx_rotate_cfg_t *)config, handle); +} + +esp_gmf_err_t esp_gmf_video_rotate_init(esp_imgfx_rotate_cfg_t *config, esp_gmf_obj_handle_t *handle) +{ + ESP_GMF_MEM_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + *handle = NULL; + esp_gmf_rotate_hd_t *video_el = esp_gmf_oal_calloc(1, sizeof(esp_gmf_rotate_hd_t)); + ESP_GMF_MEM_CHECK(TAG, video_el, return ESP_GMF_ERR_MEMORY_LACK); + esp_gmf_err_t ret = ESP_GMF_ERR_OK; + esp_gmf_obj_t *obj = (esp_gmf_obj_t *)video_el; + // Set element tag + ret = esp_gmf_obj_set_tag(video_el, "vid_rotate"); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto __init_exit, "Failed set OBJ tag"); + // Configure object callbacks + obj->new_obj = video_rotate_el_new; + obj->del_obj = video_rotate_el_delete; + esp_imgfx_rotate_cfg_t *cfg = esp_gmf_oal_calloc(1, sizeof(esp_imgfx_rotate_cfg_t)); + ESP_GMF_MEM_VERIFY(TAG, cfg, {ret = ESP_GMF_ERR_MEMORY_LACK; goto __init_exit;}, "rotate configuration", sizeof(esp_imgfx_rotate_cfg_t)); + esp_gmf_obj_set_config(obj, cfg, sizeof(esp_imgfx_rotate_cfg_t)); + if (config) { + memcpy(cfg, config, sizeof(esp_imgfx_rotate_cfg_t)); + } else { + esp_imgfx_rotate_cfg_t dcfg = DEFAULT_ESP_GMF_ROTATE_CONFIG(); + memcpy(cfg, &dcfg, sizeof(esp_imgfx_rotate_cfg_t)); + } + // Configure element + esp_gmf_element_cfg_t el_cfg = { + .dependency = true, + }; + ESP_GMF_ELEMENT_IN_PORT_ATTR_SET(el_cfg.in_attr, ESP_GMF_EL_PORT_CAP_SINGLE, 0, 0, + ESP_GMF_PORT_TYPE_BLOCK, ESP_GMF_ELEMENT_PORT_DATA_SIZE_DEFAULT); + ESP_GMF_ELEMENT_OUT_PORT_ATTR_SET(el_cfg.out_attr, ESP_GMF_EL_PORT_CAP_SINGLE, 0, 0, + ESP_GMF_PORT_TYPE_BLOCK, ESP_GMF_ELEMENT_PORT_DATA_SIZE_DEFAULT); + // Initialize element + ret = esp_gmf_video_el_init(video_el, &el_cfg); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto __init_exit, "Failed initialize video element"); + // Register element's callbacks + esp_gmf_video_element_t *parent = (esp_gmf_video_element_t *)video_el; + parent->base.ops.open = video_rotate_el_open; + parent->base.ops.process = video_rotate_el_process; + parent->base.ops.close = video_rotate_el_close; + parent->base.ops.event_receiver = video_rotate_el_received_event_handler; + parent->base.ops.load_caps = video_rotate_el_load_caps; + parent->base.ops.load_methods = video_rotate_el_load_methods; + *handle = (esp_gmf_obj_handle_t)video_el; + ESP_LOGD(TAG, "Initialization, %s-%p", OBJ_GET_TAG(obj), obj); + return ret; +__init_exit: + video_rotate_el_delete(video_el); + return ret; +} + +esp_gmf_err_t esp_gmf_video_rotate_set_rotation(esp_gmf_element_handle_t handle, uint16_t degree) +{ + esp_imgfx_rotate_cfg_t config; + esp_gmf_err_t ret = esp_gmf_video_rotate_get_cfg(handle, &config); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, return ret, "Failed to set destination degree"); + config.degree = degree; + return esp_gmf_video_rotate_set_cfg(handle, &config); +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/esp_gmf_video_scale.c b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/esp_gmf_video_scale.c new file mode 100644 index 0000000..97f8607 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/esp_gmf_video_scale.c @@ -0,0 +1,317 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#include +#include +#include "esp_log.h" +#include "esp_gmf_oal_mutex.h" +#include "esp_gmf_oal_mem.h" +#include "esp_gmf_err.h" +#include "esp_gmf_node.h" +#include "esp_gmf_video_scale.h" +#include "esp_gmf_video_methods_def.h" +#include "gmf_video_common.h" +#include "esp_gmf_video_element.h" +#include "esp_gmf_video_types.h" +#include "esp_gmf_caps_def.h" + +static const char *TAG = "IMGFX_SCALE_EL"; + +typedef struct _gmf_imgfx_scale_t { + esp_gmf_video_element_t parent; + esp_imgfx_scale_handle_t hd; + bool need_recfg; +} esp_gmf_scale_hd_t; + +static esp_gmf_job_err_t video_scale_el_open(esp_gmf_element_handle_t self, void *para) +{ + esp_gmf_scale_hd_t *video_el = (esp_gmf_scale_hd_t *)self; + // Get and check config + esp_imgfx_scale_cfg_t *cfg = (esp_imgfx_scale_cfg_t *)OBJ_GET_CFG(self); + ESP_GMF_CHECK(TAG, cfg, return ESP_GMF_JOB_ERR_FAIL, "Failed to get scale config"); + // Open scale module + esp_imgfx_scale_open(cfg, &video_el->hd); + ESP_GMF_CHECK(TAG, video_el->hd, return ESP_GMF_JOB_ERR_FAIL, "Failed to create scale handle"); + // Get video size + esp_imgfx_get_image_size(cfg->in_pixel_fmt, &cfg->in_res, (uint32_t *)&(ESP_GMF_ELEMENT_GET(video_el)->in_attr.data_size)); + esp_imgfx_get_image_size(cfg->in_pixel_fmt, &cfg->scale_res, (uint32_t *)&(ESP_GMF_ELEMENT_GET(video_el)->out_attr.data_size)); + // Report information to the next element, the next element will use this information to configure + gmf_video_update_info(self, cfg->scale_res.width, cfg->scale_res.height, cfg->in_pixel_fmt); + // The video_el->hd has been opened using newest configuration, so it can set the need_recfg to false + video_el->need_recfg = false; + ESP_LOGD(TAG, "Open, %p", self); + return ESP_GMF_JOB_ERR_OK; +} + +static esp_gmf_job_err_t video_scale_el_process(esp_gmf_element_handle_t self, void *para) +{ + esp_gmf_scale_hd_t *video_el = (esp_gmf_scale_hd_t *)self; + esp_imgfx_data_t in_image; + esp_imgfx_data_t out_image; + if (video_el->need_recfg) { + esp_imgfx_scale_cfg_t *cfg = (esp_imgfx_scale_cfg_t *)OBJ_GET_CFG(self); + // Reset scale configuration + esp_imgfx_err_t imgfx_ret = esp_imgfx_scale_set_cfg(video_el->hd, cfg); + if (imgfx_ret != ESP_IMGFX_ERR_OK) { + ESP_LOGE(TAG, "Image effects color convert set cfg failed, ret: %d", imgfx_ret); + return ESP_GMF_JOB_ERR_FAIL; + } + // Get video size + esp_imgfx_get_image_size(cfg->in_pixel_fmt, &cfg->in_res, (uint32_t *)&(ESP_GMF_ELEMENT_GET(video_el)->in_attr.data_size)); + esp_imgfx_get_image_size(cfg->in_pixel_fmt, &cfg->scale_res, (uint32_t *)&(ESP_GMF_ELEMENT_GET(video_el)->out_attr.data_size)); + video_el->need_recfg = false; + } + esp_gmf_port_handle_t in_port = ESP_GMF_ELEMENT_GET(self)->in; + esp_gmf_port_handle_t out_port = ESP_GMF_ELEMENT_GET(self)->out; + esp_gmf_payload_t *in_load = NULL; + esp_gmf_payload_t *out_load = NULL; + esp_gmf_job_err_t ret = ESP_GMF_JOB_ERR_OK; + esp_gmf_err_io_t load_ret = ESP_GMF_IO_OK; + ret = video_el_acquire_payload(in_port, out_port, &in_load, &out_load, (ESP_GMF_ELEMENT_GET(video_el)->in_attr.data_size), (ESP_GMF_ELEMENT_GET(video_el)->out_attr.data_size), + ((ESP_GMF_ELEMENT_GET(video_el)->in_attr.data_size) == (ESP_GMF_ELEMENT_GET(video_el)->out_attr.data_size))); + if (ret != ESP_GMF_JOB_ERR_OK) { + goto __release; + } + /* It is for bypass */ + if (in_load == out_load) { + goto __release; + } + /* finished */ + if (in_load->is_done) { + out_load->is_done = in_load->is_done; + out_load->pts = in_load->pts; + ret = ESP_GMF_JOB_ERR_DONE; + ESP_LOGD(TAG, "It's done, out: %d", in_load->valid_size); + goto __release; + } + in_image.data = in_load->buf; + in_image.data_len = in_load->valid_size; + out_image.data = out_load->buf; + out_image.data_len = out_load->buf_length; + esp_imgfx_err_t imgfx_ret = esp_imgfx_scale_process(video_el->hd, &in_image, &out_image); + if (imgfx_ret != ESP_IMGFX_ERR_OK) { + ESP_LOGE(TAG, "Image effects scale process failed, ret: %d-%p", imgfx_ret, video_el); + ret = ESP_GMF_JOB_ERR_FAIL; + goto __release; + } + // Copy the information to the output payload. The next element will use this information to process + out_load->is_done = in_load->is_done; + out_load->valid_size = (ESP_GMF_ELEMENT_GET(video_el)->out_attr.data_size); + out_load->pts = in_load->pts; +__release: + // Release in and out port + if (out_load != NULL) { + load_ret = esp_gmf_port_release_out(out_port, out_load, ESP_GMF_MAX_DELAY); + if ((load_ret < ESP_GMF_IO_OK) && (load_ret != ESP_GMF_IO_ABORT)) { + ESP_LOGE(TAG, "OUT port release error, ret:%d", load_ret); + ret = ESP_GMF_JOB_ERR_FAIL; + } + } + if (in_load != NULL) { + load_ret = esp_gmf_port_release_in(in_port, in_load, ESP_GMF_MAX_DELAY); + if ((load_ret < ESP_GMF_IO_OK) && (load_ret != ESP_GMF_IO_ABORT)) { + ESP_LOGE(TAG, "IN port release error, ret:%d", load_ret); + ret = ESP_GMF_JOB_ERR_FAIL; + } + } + return ret; +} + +static esp_gmf_job_err_t video_scale_el_close(esp_gmf_element_handle_t self, void *para) +{ + ESP_LOGD(TAG, "Closed, %p", self); + esp_gmf_scale_hd_t *video_el = (esp_gmf_scale_hd_t *)self; + if (video_el) { + if (video_el->hd) { + esp_imgfx_scale_close(video_el->hd); + video_el->hd = NULL; + } + } + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t video_scale_el_delete(esp_gmf_obj_handle_t handle) +{ + ESP_LOGD(TAG, "Deleted, %p", handle); + void *cfg = OBJ_GET_CFG(handle); + if (cfg) { + esp_gmf_oal_free(cfg); + } + esp_gmf_video_el_deinit(handle); + esp_gmf_oal_free(handle); + return ESP_GMF_ERR_OK; +} + +static inline esp_gmf_err_t esp_gmf_video_scale_set_cfg(esp_gmf_element_handle_t self, esp_imgfx_scale_cfg_t *config) +{ + ESP_GMF_NULL_CHECK(TAG, self, return ESP_GMF_JOB_ERR_FAIL); + ESP_GMF_NULL_CHECK(TAG, config, return ESP_GMF_JOB_ERR_FAIL); + esp_gmf_scale_hd_t *video_el = (esp_gmf_scale_hd_t *)self; + esp_gmf_obj_t *obj = (esp_gmf_obj_t *)video_el; + // reset obj config + if (obj->cfg) { + memcpy(obj->cfg, config, sizeof(esp_imgfx_scale_cfg_t)); + video_el->need_recfg = true; + return ESP_GMF_JOB_ERR_OK; + } + ESP_LOGE(TAG, " %s-%p is no configured yet", __func__, self); + return ESP_GMF_JOB_ERR_FAIL; +} + +static inline esp_gmf_err_t esp_gmf_video_scale_get_cfg(esp_gmf_element_handle_t self, esp_imgfx_scale_cfg_t *config) +{ + ESP_GMF_NULL_CHECK(TAG, self, return ESP_GMF_JOB_ERR_FAIL); + esp_gmf_scale_hd_t *video_el = (esp_gmf_scale_hd_t *)self; + // first get cfg from scale handle. If it is NULL, get cfg from obj + if (video_el->hd) { + esp_imgfx_err_t imgfx_ret = esp_imgfx_scale_get_cfg(video_el->hd, config); + if (imgfx_ret != ESP_IMGFX_ERR_OK) { + ESP_LOGE(TAG, "Get video effects scale cfg failed, hd:%p, ret: %d", self, imgfx_ret); + return ESP_GMF_JOB_ERR_FAIL; + } + return ESP_GMF_JOB_ERR_OK; + } else { + esp_gmf_obj_t *obj = (esp_gmf_obj_t *)video_el; + // Get obj config + if (obj->cfg) { + memcpy(config, obj->cfg, sizeof(esp_imgfx_scale_cfg_t)); + return ESP_GMF_JOB_ERR_OK; + } + } + ESP_LOGE(TAG, " %s-%p is no configured yet", __func__, self); + return ESP_GMF_JOB_ERR_FAIL; +} + +static esp_gmf_err_t video_set_dst_res(void *handle, esp_gmf_args_desc_t *arg_desc, uint8_t *buf, int buf_len) +{ + ESP_GMF_MEM_CHECK(TAG, buf, return ESP_GMF_ERR_INVALID_ARG); + return esp_gmf_video_scale_dst_resolution(handle, (esp_gmf_video_resolution_t *)buf); +} + +static esp_gmf_err_t video_scale_el_load_caps(esp_gmf_element_handle_t handle) +{ + esp_gmf_cap_t *caps = NULL; + esp_gmf_cap_t dec_caps = {0}; + dec_caps.cap_eightcc = ESP_GMF_CAPS_VIDEO_SCALE; + dec_caps.attr_fun = NULL; + int ret = esp_gmf_cap_append(&caps, &dec_caps); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, return ret, "Failed to create capability"); + esp_gmf_element_t *el = (esp_gmf_element_t *)handle; + el->caps = caps; + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t video_scale_el_load_methods(esp_gmf_element_handle_t handle) +{ + esp_gmf_method_t *method = NULL; + esp_gmf_err_t ret = ESP_GMF_ERR_OK; + esp_gmf_args_desc_t *set_args = NULL; + ret = esp_gmf_args_desc_append(&set_args, VMETHOD_ARG(SCALER, SET_DST_RES, WIDTH), ESP_GMF_ARGS_TYPE_UINT16, sizeof(uint16_t), offsetof(esp_gmf_video_resolution_t, width)); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, return ret, "Failed to append scale destination width of resolution"); + ret = esp_gmf_args_desc_append(&set_args, VMETHOD_ARG(SCALER, SET_DST_RES, HEIGHT), ESP_GMF_ARGS_TYPE_UINT16, sizeof(uint16_t), offsetof(esp_gmf_video_resolution_t, height)); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, return ret, "Failed to append scale destination height of resolution"); + ret = esp_gmf_method_append(&method, VMETHOD(SCALER, SET_DST_RES), video_set_dst_res, set_args); + ESP_GMF_RET_ON_ERROR(TAG, ret, return ret, "Failed to register %s method", VMETHOD(SCALER, SET_DST_RES)); + esp_gmf_element_t *el = (esp_gmf_element_t *)handle; + el->method = method; + return ret; +} + +static esp_gmf_err_t video_scale_el_received_event_handler(esp_gmf_event_pkt_t *evt, void *ctx) +{ + ESP_GMF_NULL_CHECK(TAG, ctx, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, evt, return ESP_GMF_ERR_INVALID_ARG); + if ((evt->type != ESP_GMF_EVT_TYPE_REPORT_INFO) + || (evt->sub != ESP_GMF_INFO_VIDEO) + || (evt->payload == NULL)) { + return ESP_GMF_ERR_OK; + } + esp_gmf_element_handle_t self = (esp_gmf_element_handle_t)ctx; + esp_gmf_element_handle_t el = evt->from; + esp_gmf_event_state_t state = ESP_GMF_EVENT_STATE_NONE; + esp_gmf_element_get_state(self, &state); + esp_gmf_info_video_t *info = (esp_gmf_info_video_t *)evt->payload; + esp_gmf_video_el_set_src_info(self, info); + esp_imgfx_scale_cfg_t *config = (esp_imgfx_scale_cfg_t *)OBJ_GET_CFG(self); + ESP_GMF_NULL_CHECK(TAG, config, return ESP_GMF_ERR_FAIL); + esp_gmf_scale_hd_t *video_el = (esp_gmf_scale_hd_t *)self; + GMF_VIDEO_UPDATE_CONFIG(config, info, video_el->need_recfg); + ESP_LOGD(TAG, "RECV element info, from: %s-%p, next: %p, self: %s-%p, type: %x, state: %s, width: %d, height: %d, pixel format: %lx", + OBJ_GET_TAG(el), el, esp_gmf_node_for_next((esp_gmf_node_t *)el), OBJ_GET_TAG(self), self, evt->type, + esp_gmf_event_get_state_str(state), info->width, info->height, info->format_id); + // It is for the first time to receive event from previous element + if (state == ESP_GMF_EVENT_STATE_NONE) { + esp_gmf_element_set_state(self, ESP_GMF_EVENT_STATE_INITIALIZED); + } + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t video_scale_el_new(void *config, esp_gmf_obj_handle_t *handle) +{ + return esp_gmf_video_scale_init((esp_imgfx_scale_cfg_t *)config, handle); +} + +esp_gmf_err_t esp_gmf_video_scale_init(esp_imgfx_scale_cfg_t *config, esp_gmf_obj_handle_t *handle) +{ + ESP_GMF_MEM_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + *handle = NULL; + esp_gmf_scale_hd_t *video_el = esp_gmf_oal_calloc(1, sizeof(esp_gmf_scale_hd_t)); + ESP_GMF_MEM_CHECK(TAG, video_el, return ESP_GMF_ERR_MEMORY_LACK); + esp_gmf_err_t ret = ESP_GMF_ERR_OK; + esp_gmf_obj_t *obj = (esp_gmf_obj_t *)video_el; + // Set element tag + ret = esp_gmf_obj_set_tag(video_el, "vid_scale"); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto __init_exit, "Failed set OBJ tag"); + // Configure object callbacks + obj->new_obj = video_scale_el_new; + obj->del_obj = video_scale_el_delete; + // Set element config + esp_imgfx_scale_cfg_t *cfg = esp_gmf_oal_calloc(1, sizeof(esp_imgfx_scale_cfg_t)); + ESP_GMF_MEM_VERIFY(TAG, cfg, {ret = ESP_GMF_ERR_MEMORY_LACK; goto __init_exit;}, "scale configuration", sizeof(esp_imgfx_scale_cfg_t)); + esp_gmf_obj_set_config(obj, cfg, sizeof(esp_imgfx_scale_cfg_t)); + if (config) { + memcpy(cfg, config, sizeof(esp_imgfx_scale_cfg_t)); + } else { + esp_imgfx_scale_cfg_t dcfg = DEFAULT_ESP_GMF_SCALE_CONFIG(); + memcpy(cfg, &dcfg, sizeof(esp_imgfx_scale_cfg_t)); + } + // Configure element + esp_gmf_element_cfg_t el_cfg = { + .dependency = true, + }; + ESP_GMF_ELEMENT_IN_PORT_ATTR_SET(el_cfg.in_attr, ESP_GMF_EL_PORT_CAP_SINGLE, 0, 0, + ESP_GMF_PORT_TYPE_BLOCK, ESP_GMF_ELEMENT_PORT_DATA_SIZE_DEFAULT); + ESP_GMF_ELEMENT_OUT_PORT_ATTR_SET(el_cfg.out_attr, ESP_GMF_EL_PORT_CAP_SINGLE, 0, 0, + ESP_GMF_PORT_TYPE_BLOCK, ESP_GMF_ELEMENT_PORT_DATA_SIZE_DEFAULT); + // Initialize element + ret = esp_gmf_video_el_init(video_el, &el_cfg); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto __init_exit, "Failed initialize video element"); + // Register element's callbacks + esp_gmf_video_element_t *parent = (esp_gmf_video_element_t *)video_el; + parent->base.ops.open = video_scale_el_open; + parent->base.ops.process = video_scale_el_process; + parent->base.ops.close = video_scale_el_close; + parent->base.ops.event_receiver = video_scale_el_received_event_handler; + parent->base.ops.load_caps = video_scale_el_load_caps; + parent->base.ops.load_methods = video_scale_el_load_methods; + *handle = (esp_gmf_obj_handle_t)video_el; + ESP_LOGD(TAG, "Initialization, %s-%p", OBJ_GET_TAG(obj), obj); + return ret; +__init_exit: + video_scale_el_delete(video_el); + return ret; +} + +esp_gmf_err_t esp_gmf_video_scale_dst_resolution(esp_gmf_element_handle_t handle, esp_gmf_video_resolution_t *res) +{ + esp_imgfx_scale_cfg_t config; + esp_gmf_err_t ret = esp_gmf_video_scale_get_cfg(handle, &config); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, return ret, "Failed to set destination pixel format"); + config.scale_res.height = res->height; + config.scale_res.width = res->width; + return esp_gmf_video_scale_set_cfg(handle, &config); +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/gmf_video_common.c b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/gmf_video_common.c new file mode 100644 index 0000000..db1f466 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/gmf_video_common.c @@ -0,0 +1,67 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#include "esp_fourcc.h" +#include "gmf_video_common.h" +#include "esp_gmf_video_element.h" +#include "esp_log.h" + +#define TAG "VIDEO_COMM" + +const char *esp_gmf_video_get_format_string(uint32_t codec) +{ + switch (codec) { + case ESP_FOURCC_H264: + return "h264"; + case ESP_FOURCC_MJPG: + return "mjpeg"; + case ESP_FOURCC_RGB16: + return "rgb565"; + case ESP_FOURCC_RGB16_BE: + return "rgb565_be"; + case ESP_FOURCC_RGB24: + return "rgb888"; + case ESP_FOURCC_BGR24: + return "bgr888"; + case ESP_FOURCC_YUV420P: + return "yuv420p"; + case ESP_FOURCC_YUV422P: + return "yuv422p"; + case ESP_FOURCC_YUYV: + return "yuv422"; + case ESP_FOURCC_OUYY_EVYY: + return "o_uyy_e_vyy"; + default: + return "none"; + } +} + +esp_gmf_err_t esp_gmf_video_handle_events(esp_gmf_event_pkt_t *evt, void *ctx) +{ + esp_gmf_err_t ret = ESP_GMF_ERR_OK; + esp_gmf_element_handle_t self = (esp_gmf_element_handle_t) ctx; + // Only process report info currently + if ((evt->type != ESP_GMF_EVT_TYPE_REPORT_INFO) + || (evt->sub != ESP_GMF_INFO_VIDEO) + || (evt->payload == NULL)) { + return ESP_GMF_ERR_OK; + } + esp_gmf_event_state_t state = -1; + esp_gmf_element_get_state(self, &state); + // State must matched + esp_gmf_info_video_t *vid_info = evt->payload; + esp_gmf_video_element_t *video_el = (esp_gmf_video_element_t *)self; + video_el->src_info = *vid_info; + ESP_LOGI(TAG, "Video info for %s-%p format:%s %dx%d %dfps", + OBJ_GET_TAG(self), self, + esp_gmf_video_get_format_string(vid_info->format_id), + (int)vid_info->width, (int)vid_info->height, (int)vid_info->fps); + if (state == ESP_GMF_EVENT_STATE_NONE) { + esp_gmf_element_set_state(self, ESP_GMF_EVENT_STATE_INITIALIZED); + } + return ret; +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/idf_component.yml b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/idf_component.yml new file mode 100644 index 0000000..6585b7f --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/idf_component.yml @@ -0,0 +1,17 @@ +version: "0.7.1" +description: Espressif GMF Video Elements Module +url: https://github.com/espressif/esp-gmf/tree/main/elements/gmf_video +documentation: "https://github.com/espressif/esp-gmf/blob/main/elements/gmf_video/README.md" +issues: "https://github.com/espressif/esp-gmf/issues" +tags: + - "gmf" + - "video" + +dependencies: + espressif/esp_video_codec: "~0.5.2" + espressif/esp_image_effects: + require: public + version: "~1.0.0" + espressif/gmf_core: + version: "~0.7.0" + require: public diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/include/esp_gmf_video_color_convert.h b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/include/esp_gmf_video_color_convert.h new file mode 100644 index 0000000..589e80c --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/include/esp_gmf_video_color_convert.h @@ -0,0 +1,57 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#include "esp_gmf_element.h" +#include "esp_imgfx_color_convert.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define DEFAULT_ESP_GMF_COLOR_CONVERT_CONFIG() { \ + .in_res = { \ + .width = 320, \ + .height = 240 , \ + }, \ + .in_pixel_fmt = ESP_IMGFX_PIXEL_FMT_YUYV, \ + .out_pixel_fmt = ESP_IMGFX_PIXEL_FMT_RGB565_LE, \ + .color_space_std = ESP_IMGFX_COLOR_SPACE_STD_BT601, \ +} + +/** + * @brief Initialize the video color convert element with the specified configuration + * + * @param[in] config Pointer to a color convert configuration + * @param[out] handle Pointer to a `esp_gmf_element_handle_t` where the initialized video color convert + * handle will be stored + * + * @return + * - ESP_GMF_ERR_OK Initialization successful + * - ESP_GMF_ERR_INVALID_ARG Invalid arguments + * - ESP_GMF_ERR_MEMORY_LACK No memory for video color convert element + */ +esp_gmf_err_t esp_gmf_video_color_convert_init(esp_imgfx_color_convert_cfg_t *config, esp_gmf_element_handle_t *handle); + +/** + * @brief Set video color convert destination format + * + * @note This API have no special timing request, user can call it freely + * + * @param[in] handle Video color convert handle + * @param[in] format Video color convert destination format (GMF FourCC representation of video format) + * + * @return + * - ESP_GMF_ERR_OK Successful + * - ESP_GMF_ERR_INVALID_ARG Invalid arguments + */ +esp_gmf_err_t esp_gmf_video_color_convert_dst_format(esp_gmf_element_handle_t handle, uint32_t format); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/include/esp_gmf_video_crop.h b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/include/esp_gmf_video_crop.h new file mode 100644 index 0000000..909c213 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/include/esp_gmf_video_crop.h @@ -0,0 +1,63 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#include "esp_gmf_element.h" +#include "esp_gmf_video_types.h" +#include "esp_imgfx_crop.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define DEFAULT_ESP_GMF_CROP_CONFIG() { \ + .in_res = { \ + .width = 320, \ + .height = 240, \ + }, \ + .in_pixel_fmt = ESP_IMGFX_PIXEL_FMT_RGB565_LE, \ + .cropped_res = { \ + .width = 160, \ + .height = 120, \ + }, \ + .x_pos = 0, \ + .y_pos = 0, \ +} + +/** + * @brief Initialize the video crop element with the specified configuration + * + * @param[in] config Pointer to a `esp_imgfx_crop_cfg_t` structure that contains the + * configuration settings for the video crop + * @param[out] handle Pointer to a `esp_gmf_element_handle_t` where the initialized video crop + * handle will be stored + * + * @return + * - ESP_GMF_ERR_OK Initialization successful + * - ESP_GMF_ERR_INVALID_ARG Invalid arguments + * - ESP_GMF_ERR_MEMORY_LACK No memory for video crop element + */ +esp_gmf_err_t esp_gmf_video_crop_init(esp_imgfx_crop_cfg_t *config, esp_gmf_element_handle_t *handle); + +/** + * @brief Set video cropped region + * + * @note This API have no special timing request, user can call it freely + * + * @param[in] handle Video video crop handle + * @param[in] rgn Region to be kept in original video frame + * + * @return + * - ESP_GMF_ERR_OK Successful + * - ESP_GMF_ERR_INVALID_ARG Invalid arguments + */ +esp_gmf_err_t esp_gmf_video_crop_rgn(esp_gmf_element_handle_t handle, esp_gmf_video_rgn_t *rgn); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/include/esp_gmf_video_dec.h b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/include/esp_gmf_video_dec.h new file mode 100644 index 0000000..ca97af0 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/include/esp_gmf_video_dec.h @@ -0,0 +1,87 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#include "esp_gmf_element.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Video decoder configuration (optional) + */ +typedef struct { + uint32_t codec_cc; /*!< FourCC code to force a specific decoder implementation + See `esp_video_codec_desc_t` in `esp_video_codec_types.h` for details + If zero, the system will select a suitable decoder (HW preferred) */ +} esp_gmf_video_dec_cfg_t; + +/** + * @brief Initializes the GMF Video decoder with the provided configuration + * + * @param[in] cfg Pointer to the Video decoder configuration (optional) + * @param[out] handle Pointer to the Video decoder handle to be initialized + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid configuration provided + * - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory + */ +esp_gmf_err_t esp_gmf_video_dec_init(esp_gmf_video_dec_cfg_t *cfg, esp_gmf_element_handle_t *handle); + +/** + * @brief Get Video decoder output frame size + * + * @note This API should only called after element running or `esp_gmf_video_dec_set_dst_format` is called + * + * @param[in] handle Video decoder element handle + * @param[out] frame_size Output frame size to store + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid configuration provided + */ +esp_gmf_err_t esp_gmf_video_dec_get_out_size(esp_gmf_element_handle_t handle, uint32_t *frame_size); + +/** + * @brief Setting for video decoder destination format (e.g., YUV420P, RGB565LE) + * + * @note This API should only called before element running + * + * @param[in] handle Video decoder element handle + * @param[in] dst_fmt Destination format (GMF FourCC representation of video codec) + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid configuration provided + */ +esp_gmf_err_t esp_gmf_video_dec_set_dst_format(esp_gmf_element_handle_t handle, uint32_t dst_fmt); + +/** + * @brief Get supported output formats for a given video decoder type + * This function retrieves the list of output formats (e.g., YUV420P, RGB565LE) that the decoder can produce + * when decoding from the specified source codec + * + * @param[in] handle Video decoder element handle + * @param[in] src_codec Decode type (GMF FourCC representation of video codec) + * @param[out] dst_fmts Output video formats to store (GMF FourCC representation of video format) + * @param[out] dst_fmts_num Output video format number to store + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid configuration provided + */ +esp_gmf_err_t esp_gmf_video_dec_get_dst_formats(esp_gmf_element_handle_t handle, + uint32_t src_codec, + const uint32_t **dst_fmts, + uint8_t *dst_fmts_num); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/include/esp_gmf_video_enc.h b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/include/esp_gmf_video_enc.h new file mode 100644 index 0000000..8a6909e --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/include/esp_gmf_video_enc.h @@ -0,0 +1,153 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#include "esp_gmf_element.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Video encoder configuration + */ +typedef struct { + uint32_t codec_cc; /*!< FourCC code to force a specific encoder implementation + See `esp_video_codec_desc_t` in `esp_video_codec_types.h` for details + If zero, the system will select a suitable encoder (HW preferred) */ +} esp_gmf_video_enc_cfg_t; + +/** + * @brief Initializes the GMF video encoder with the provided configuration + * + * @param[in] cfg Pointer to the video encoder configuration + * @param[out] handle Pointer to the video encoder handle to be initialized + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid configuration provided + * - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory + */ +esp_gmf_err_t esp_gmf_video_enc_init(esp_gmf_video_enc_cfg_t *cfg, esp_gmf_element_handle_t *handle); + +/** + * @brief Setting for video encoder destination codec (e.g., H.264, MJPEG) + * + * @note This API should only called before element running + * + * @param[in] handle Video encoder element handle + * @param[in] dst_codec Video encode destination codec (use GMF FourCC presentation) + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid configuration provided + */ +esp_gmf_err_t esp_gmf_video_enc_set_dst_codec(esp_gmf_element_handle_t handle, uint32_t dst_codec); + +/** + * @brief Do presetting for video encoder + * + * @note This function pre-configures the video encoder with both source and destination information + * This allows querying encoder capabilities before runtime + * Normally, source information is reported from dependent element in the running state + * Call this function only before element running + * + * @param[in] handle Video encoder element handle + * @param[in] src_info Video encode input video information + * @param[in] dst_codec Destination video codec (use GMF FourCC presentation) + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid configuration provided + */ +esp_gmf_err_t esp_gmf_video_enc_preset(esp_gmf_element_handle_t handle, esp_gmf_info_video_t *src_info, + uint32_t dst_codec); + +/** + * @brief Get video encoder output frame size + * + * @note This API should only be called after `esp_gmf_video_enc_preset` set or after element running + * + * @param[in] handle Video encoder element handle + * @param[out] frame_size Output frame size to store + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid configuration provided + */ +esp_gmf_err_t esp_gmf_video_enc_get_out_size(esp_gmf_element_handle_t handle, uint32_t *frame_size); + +/** + * @brief Get supported source formats (e.g., YUV420P, RGB565LE etc) for a given video encoder type + * This function retrieves the list of supported input formats that can be encoded + * into the specified destination codec + * + * @param[in] handle Video encoder element handle + * @param[in] dst_codec Video encode destination codec (GMF FourCC representation of video codec) + * @param[out] src_fmts Source formats to stored (GMF FourCC representation of video format) + * @param[out] src_fmts_num Source format number to store + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid configuration provided + */ +esp_gmf_err_t esp_gmf_video_enc_get_src_formats(esp_gmf_element_handle_t handle, + uint32_t dst_codec, + const uint32_t **src_fmts, + uint8_t *src_fmts_num); + +/** + * @brief Setting for video encoder bitrate + * + * @note Support setting both before and after element running + * + * @param[in] handle Video encoder element handle + * @param[in] bitrate Output bitrate + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid configuration provided + */ +esp_gmf_err_t esp_gmf_video_enc_set_bitrate(esp_gmf_element_handle_t handle, uint32_t bitrate); + +/** + * @brief Set the GOP (Group of Pictures) for video encoder. + * + * @note This setting applies specifically to the H.264 encoder + * The H.264 encoder generates an I-frame at the start of each GOP + * Can be configured both before and after the encoder element is running + * + * @param[in] handle Handle to the video encoder element + * @param[in] gop GOP size for H.264 (in number of frames) + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid configuration provided + */ +esp_gmf_err_t esp_gmf_video_enc_set_gop(esp_gmf_element_handle_t handle, uint32_t gop); + +/** + * @brief Set the QP (Quantization Parameter) range for video encoder + * + * @note This setting applies specifically to the H.264 encoder + * Higher QP values generally result in lower image quality + * Can be configured both before and after the encoder element is running + * + * @param[in] handle Handle to the video encoder element + * @param[in] min_qp Minimum QP value + * @param[in] max_qp Maximum QP value + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid configuration provided + */ +esp_gmf_err_t esp_gmf_video_enc_set_qp(esp_gmf_element_handle_t handle, uint32_t min_qp, uint32_t max_qp); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/include/esp_gmf_video_fps_cvt.h b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/include/esp_gmf_video_fps_cvt.h new file mode 100644 index 0000000..50436fa --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/include/esp_gmf_video_fps_cvt.h @@ -0,0 +1,45 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#include "esp_gmf_element.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initializes the GMF video frame rate convert + * + * @param[in] config No need to set + * @param[out] handle Pointer to the video frame rate convert handle to be initialized + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid configuration provided + * - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory + */ +esp_gmf_err_t esp_gmf_video_fps_cvt_init(void *config, esp_gmf_element_handle_t *handle); + +/** + * @brief Set GMF video frame rate converter output frame rate + * + * @note This API should only called before element running + * + * @param[in] handle Video frame rate converter handle + * @param[out] fps Destination frame rate to set (unit frame per second) + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid configuration provided + */ +esp_gmf_err_t esp_gmf_video_fps_cvt_set_fps(esp_gmf_element_handle_t handle, uint16_t fps); + +#ifdef __cplusplus +} +#endif diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/include/esp_gmf_video_methods_def.h b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/include/esp_gmf_video_methods_def.h new file mode 100644 index 0000000..22f4161 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/include/esp_gmf_video_methods_def.h @@ -0,0 +1,176 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#include "esp_gmf_method_helper.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * @brief Video general method argument definition + */ +#define ESP_GMF_VIDEO_FORMAT_ARG "format" +#define ESP_GMF_VIDEO_WIDTH_ARG "width" +#define ESP_GMF_VIDEO_HEIGHT_ARG "height" +#define ESP_GMF_VIDEO_FPS_ARG "fps" +#define ESP_GMF_VIDEO_BITRATE_ARG "bitrate" +#define ESP_GMF_VIDEO_CODEC_ARG "codec" + +/** + * @brief Video general region argument definition + */ +#define ESP_GMF_VIDEO_RGN_ARG_X "x" +#define ESP_GMF_VIDEO_RGN_ARG_Y "y" +#define ESP_GMF_VIDEO_RGN_ARG_WIDTH ESP_GMF_VIDEO_WIDTH_ARG +#define ESP_GMF_VIDEO_RGN_ARG_HEIGHT ESP_GMF_VIDEO_HEIGHT_ARG + +/** + * @brief GMF video method definition + */ +#define VMETHOD_DEF(module, method, str) ESP_GMF_METHOD_DEF(VIDEO, module, method, str) + +/** + * @brief Get GMF video method string + */ +#define VMETHOD(module, method) ESP_GMF_METHOD_STR(VIDEO, module, method) + +/** + * @brief GMF video method argument definition + */ +#define VMETHOD_ARG_DEF(module, method, arg, str) ESP_GMF_METHOD_ARG_DEF(VIDEO, module, method, arg, str) + +/** + * @brief Get GMF video method argument string + */ +#define VMETHOD_ARG(module, method, arg) ESP_GMF_METHOD_ARG_STR(VIDEO, module, method, arg) + +/** + * @brief Video FPS converter method definition for set frame per second + */ +VMETHOD_DEF(FPS_CVT, SET_FPS, "set_fps"); +VMETHOD_ARG_DEF(FPS_CVT, SET_FPS, FPS, ESP_GMF_VIDEO_FPS_ARG); + +/** + * @brief Video overlay mixer method definition for enable overlay + */ +VMETHOD_DEF(OVERLAY, OVERLAY_ENABLE, "overlay_enable"); +VMETHOD_ARG_DEF(OVERLAY, OVERLAY_ENABLE, ENABLE, "enable"); + +/** + * @brief Video overlay mixer method definition for set overlay port + */ +VMETHOD_DEF(OVERLAY, SET_PORT, "overlay_port"); +VMETHOD_ARG_DEF(OVERLAY, SET_PORT, PORT, "port"); + +/** + * @brief Video overlay mixer method definition for set overlay region information + */ +VMETHOD_DEF(OVERLAY, SET_RGN, "overlay_rgn"); +VMETHOD_ARG_DEF(OVERLAY, SET_RGN, FMT, ESP_GMF_VIDEO_FORMAT_ARG); +VMETHOD_ARG_DEF(OVERLAY, SET_RGN, X, ESP_GMF_VIDEO_RGN_ARG_X); +VMETHOD_ARG_DEF(OVERLAY, SET_RGN, Y, ESP_GMF_VIDEO_RGN_ARG_Y); +VMETHOD_ARG_DEF(OVERLAY, SET_RGN, WIDTH, ESP_GMF_VIDEO_RGN_ARG_WIDTH); +VMETHOD_ARG_DEF(OVERLAY, SET_RGN, HEIGHT, ESP_GMF_VIDEO_RGN_ARG_HEIGHT); + +/** + * @brief Video overlay mixer method definition for set overlay region alpha + */ +VMETHOD_DEF(OVERLAY, SET_ALPHA, "set_alpha"); +VMETHOD_ARG_DEF(OVERLAY, SET_ALPHA, ALPHA, "alpha"); + +/** + * @brief Video color convert method definition for set destination format + */ +VMETHOD_DEF(CLR_CVT, SET_DST_FMT, "set_dst_fmt"); +VMETHOD_ARG_DEF(CLR_CVT, SET_DST_FMT, FMT, ESP_GMF_VIDEO_FORMAT_ARG); + +/** + * @brief Video scaler method definition for set destination resolution + */ +VMETHOD_DEF(SCALER, SET_DST_RES, "set_dst_res"); +VMETHOD_ARG_DEF(SCALER, SET_DST_RES, WIDTH, ESP_GMF_VIDEO_RGN_ARG_WIDTH); +VMETHOD_ARG_DEF(SCALER, SET_DST_RES, HEIGHT, ESP_GMF_VIDEO_RGN_ARG_HEIGHT); + +/** + * @brief Video cropper method definition for set cropped region + */ +VMETHOD_DEF(CROP, SET_CROP_RGN, "set_crop_rgn"); +VMETHOD_ARG_DEF(CROP, SET_CROP_RGN, X, ESP_GMF_VIDEO_RGN_ARG_X); +VMETHOD_ARG_DEF(CROP, SET_CROP_RGN, Y, ESP_GMF_VIDEO_RGN_ARG_Y); +VMETHOD_ARG_DEF(CROP, SET_CROP_RGN, WIDTH, ESP_GMF_VIDEO_RGN_ARG_WIDTH); +VMETHOD_ARG_DEF(CROP, SET_CROP_RGN, HEIGHT, ESP_GMF_VIDEO_RGN_ARG_HEIGHT); + +/** + * @brief Video rotator method definition for set rotate degree + */ +VMETHOD_DEF(ROTATOR, SET_ANGLE, "set_angle"); +VMETHOD_ARG_DEF(ROTATOR, SET_ANGLE, DEGREE, "degree"); + +/** + * @brief Video encoder method definition for set output codec (e.g., H.264, MJPEG) + */ +VMETHOD_DEF(ENCODER, SET_DST_CODEC, "set_dst_codec"); +VMETHOD_ARG_DEF(ENCODER, SET_DST_CODEC, CODEC, ESP_GMF_VIDEO_CODEC_ARG); + +/** + * @brief Video encoder method definition for set output bitrate + */ +VMETHOD_DEF(ENCODER, SET_BITRATE, "set_bitrate"); +VMETHOD_ARG_DEF(ENCODER, SET_BITRATE, BITRATE, ESP_GMF_VIDEO_BITRATE_ARG); + +/** + * @brief Video encoder method definition for get input formats according output codec + */ +VMETHOD_DEF(ENCODER, GET_SRC_FMTS, "get_src_fmts"); +VMETHOD_ARG_DEF(ENCODER, GET_SRC_FMTS, DST_CODEC, "dst_codec"); +VMETHOD_ARG_DEF(ENCODER, GET_SRC_FMTS, SRC_FMTS_PTR, "src_fmts"); +VMETHOD_ARG_DEF(ENCODER, GET_SRC_FMTS, SRC_FMTS_NUM_PTR, "src_fmts_num"); + +/** + * @brief Video encoder method definition for presetting including input video information and output codec + * + * @note This method is specially used when user want to query things from encoder and provide encode basic settings + */ +VMETHOD_DEF(ENCODER, PRESET, "venc_preset"); +VMETHOD_ARG_DEF(ENCODER, PRESET, SRC_FMT, "src_fmt"); +VMETHOD_ARG_DEF(ENCODER, PRESET, SRC_WIDTH, ESP_GMF_VIDEO_WIDTH_ARG); +VMETHOD_ARG_DEF(ENCODER, PRESET, SRC_HEIGHT, ESP_GMF_VIDEO_HEIGHT_ARG); +VMETHOD_ARG_DEF(ENCODER, PRESET, SRC_FPS, ESP_GMF_VIDEO_FPS_ARG); +VMETHOD_ARG_DEF(ENCODER, PRESET, SRC_BITRATE, ESP_GMF_VIDEO_BITRATE_ARG); +VMETHOD_ARG_DEF(ENCODER, PRESET, DST_CODEC, "dst_codec"); + +/** + * @brief Video encoder method definition for get encode frame size + */ +VMETHOD_DEF(ENCODER, GET_FRAME_SIZE, "get_frm_size"); +VMETHOD_ARG_DEF(ENCODER, GET_FRAME_SIZE, DST_FRM_SIZE_PTR, "dst_frm_size"); + +/** + * @brief Video decoder method definition for set source codec (e.g., H.264, MJPEG) + */ +VMETHOD_DEF(DECODER, SET_SRC_CODEC, "set_src_codec"); +VMETHOD_ARG_DEF(DECODER, SET_SRC_CODEC, CODEC, ESP_GMF_VIDEO_CODEC_ARG); + +/** + * @brief Video decoder method definition for set destination format (e.g., YUV420P, RGB565LE) + * Reuse `VMETHOD_DEF(CLR_CVT, SET_DST_FMT, "set_dst_fmt")` for same function + */ + +/** + * @brief Video decoder method definition for get output formats according input codec + */ +VMETHOD_DEF(DECODER, GET_DST_FMTS, "get_dst_fmts"); +VMETHOD_ARG_DEF(DECODER, GET_DST_FMTS, SRC_CODEC, "src_codec"); +VMETHOD_ARG_DEF(DECODER, GET_DST_FMTS, DST_FMTS_PTR, "dst_fmts"); +VMETHOD_ARG_DEF(DECODER, GET_DST_FMTS, DST_FMTS_NUM_PTR, "dst_fmts_num"); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/include/esp_gmf_video_overlay.h b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/include/esp_gmf_video_overlay.h new file mode 100644 index 0000000..c0c0103 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/include/esp_gmf_video_overlay.h @@ -0,0 +1,111 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#include "esp_gmf_element.h" +#include "esp_gmf_video_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initializes the GMF overlay mixer with the provided configuration + * + * @note This module mixes the overlay plane (foreground) into the original plane (background) using alpha blending + * The formula is: + * Output_Plane_Pixel = Original_Plane_Pixel * (255 - alpha) + Overlay_Plane_Pixel * alpha + * where `alpha` is the transparency level (0 = fully transparent, 255 = fully opaque) + * + * Diagram: + * +----------------------------+ + * | Original Plane | + * | (Background) | + * | +---------------------+ | + * | | Overlay Plane | | + * | | (Foreground) | | + * | +---------------------+ | + * +----------------------------+ + * + * @param[in] config No need to set + * @param[out] handle Pointer to the overlay mixer handle to be initialized + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid configuration provided + * - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory + */ +esp_gmf_err_t esp_gmf_video_overlay_init(void *config, esp_gmf_element_handle_t *handle); + +/** + * @brief Set the output region for video overlay + * + * @note This API should only called before element running + * + * @param[in] handle Video overlay element handle + * @param[out] rgn_info Region information + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid configuration provided + * - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory + */ +esp_gmf_err_t esp_gmf_video_overlay_set_rgn(esp_gmf_element_handle_t handle, esp_gmf_overlay_rgn_info_t *rgn_info); + +/** + * @brief Set the GMF port for video overlay input + * + * @note The video overlay element will use this port to acquire overlay data, + * mix it into the original video plane, and then release the port + * The port is managed by the user, the overlay element only accesses it + * This API can be called before element running and allow reset + * + * @param[in] handle Video overlay element handle + * @param[in] port GMF port for video overlay data + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid configuration provided + * - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory + */ +esp_gmf_err_t esp_gmf_video_overlay_set_overlay_port(esp_gmf_element_handle_t handle, esp_gmf_port_handle_t port); + +/** + * @brief Set the alpha value of the overlay window + * + * @note Allow to set both before and after element running + * + * @param[in] handle Video overlay element handle + * @param[in] alpha Overlay window alpha setting: + * 0 means fully transparent, 255 means fully opaque + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid configuration provided + */ +esp_gmf_err_t esp_gmf_video_overlay_set_alpha(esp_gmf_element_handle_t handle, uint8_t alpha); + +/** + * @brief Enable overlay mixer or not + * + * @note When overlay mixer is disabled will not acquire data from overlay port and do mixer anymore + * Original plane data leave unchanged + * Allow to set both before and after element running + * + * @param[in] handle Video overlay mixer handle + * @param[in] enable Enable mixer or not + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid configuration provided + */ +esp_gmf_err_t esp_gmf_video_overlay_enable(esp_gmf_element_handle_t handle, bool enable); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/include/esp_gmf_video_param.h b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/include/esp_gmf_video_param.h new file mode 100644 index 0000000..490f113 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/include/esp_gmf_video_param.h @@ -0,0 +1,204 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#include "esp_gmf_element.h" +#include "esp_gmf_video_types.h" +#include "esp_gmf_video_methods_def.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief This file provides general parameter setting functions for common video processing using GMF method + * + * These functions simplify setting parameters for video processing elements, + * handling the underlying method calls. They do not enforce a specific order + * or argument count, but apply the basic settings where supported. Refer to + * the individual element APIs for details on supported settings and timing. + */ + +/** + * @brief Set destination format for video element (e.g., color converter, video decoder) + * + * @param[in] handle Video element handle + * @param[in] dst_fmt Destination format + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_NOT_FOUND Not found the method + * - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory + * - Others Failed to apply method + */ +esp_gmf_err_t esp_gmf_video_param_set_dst_format(esp_gmf_element_handle_t handle, uint32_t dst_fmt); + +/** + * @brief Set frame rate for video element (mainly for FPS converter) + * + * @param[in] handle Video element handle + * @param[in] fps Destination frame rate + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_NOT_FOUND Not found the method + * - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory + * - Others Failed to apply method + */ +esp_gmf_err_t esp_gmf_video_param_set_fps(esp_gmf_element_handle_t handle, uint16_t fps); + +/** + * @brief Set destination resolution for video element (mainly for video scaler) + * + * @param[in] handle Video element handle + * @param[in] res Destination resolution + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_NOT_FOUND Not found the method + * - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory + * - Others Failed to apply method + */ +esp_gmf_err_t esp_gmf_video_param_set_dst_resolution(esp_gmf_element_handle_t handle, esp_gmf_video_resolution_t *res); + +/** + * @brief Set destination codec for video element (e.g., video encoder) + * + * @param[in] handle Video element handle + * @param[in] dst_codec Destination codec + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_NOT_FOUND Not found the method + * - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory + * - Others Failed to apply method + */ +esp_gmf_err_t esp_gmf_video_param_set_dst_codec(esp_gmf_element_handle_t handle, uint32_t dst_codec); + +/** + * @brief Preset source video information and destination codec for video encoder + * + * @note In special cases call this API then query some information from video encoder before element running + * + * @param[in] handle Video element handle + * @param[in] vid_info Source video information + * @param[in] dst_codec Destination codec + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_NOT_FOUND Not found the method + * - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory + * - Others Failed to apply method + */ +esp_gmf_err_t esp_gmf_video_param_venc_preset(esp_gmf_element_handle_t handle, esp_gmf_info_video_t *vid_info, + uint32_t dst_codec); + +/** + * @brief Get supported source formats by codec for video element (mainly for video encoder) + * + * @param[in] handle Video element handle + * @param[in] dst_codec Destination codec + * @param[in] src_fmts Pointer to store source formats array + * @param[in] src_fmts_num Pointer to store source formats number + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_NOT_FOUND Not found the method + * - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory + * - Others Failed to apply method + */ +esp_gmf_err_t esp_gmf_video_param_get_src_fmts_by_codec(esp_gmf_element_handle_t handle, uint32_t dst_codec, + const uint32_t **src_fmts, uint8_t *src_fmts_num); + +/** + * @brief Set source codec for video element (e.g., video decoder) + * + * @param[in] handle Video element handle + * @param[in] src_codec Source codec + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_NOT_FOUND Not found the method + * - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory + * - Others Failed to apply method + */ +esp_gmf_err_t esp_gmf_video_param_set_src_codec(esp_gmf_element_handle_t handle, uint32_t src_codec); + +/** + * @brief Set the rotation angle for the video element (mainly for video rotator) + * + * @param[in] handle Video element handle + * @param[in] degree Rotation angle in degrees (e.g., 0, 90, 180, 270) + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_NOT_FOUND Not found the method + * - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory + * - Others Failed to apply method + */ +esp_gmf_err_t esp_gmf_video_param_set_rotate_angle(esp_gmf_element_handle_t handle, uint16_t degree); + +/** + * @brief Set the cropped region for video element (mainly for video cropper) + * + * @param[in] handle Video element handle + * @param[in] rgn Kept region after crop + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_NOT_FOUND Not found the method + * - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory + * - Others Failed to apply method + */ +esp_gmf_err_t esp_gmf_video_param_set_cropped_region(esp_gmf_element_handle_t handle, esp_gmf_video_rgn_t *rgn); + +/** + * @brief Enable overlay for video element (mainly for video overlay) + * + * @param[in] handle Video element handle + * @param[in] enable Whether enable video overlay or not + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_NOT_FOUND Not found the method + * - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory + * - Others Failed to apply method + */ +esp_gmf_err_t esp_gmf_video_param_overlay_enable(esp_gmf_element_handle_t self, bool enable); + +/** + * @brief Set overlay port for video element (mainly for video overlay) + * + * @param[in] handle Video element handle + * @param[in] port Handle to the input GMF port providing overlay data + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_NOT_FOUND Not found the method + * - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory + * - Others Failed to apply method + */ +esp_gmf_err_t esp_gmf_video_param_set_overlay_port(esp_gmf_element_handle_t self, void *port); + +/** + * @brief Set overlay region information for video element (mainly for video overlay) + * + * @param[in] handle Video element handle + * @param[in] rgn Overlay region information + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_NOT_FOUND Not found the method + * - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory + * - Others Failed to apply method + */ +esp_gmf_err_t esp_gmf_video_param_set_overlay_rgn(esp_gmf_element_handle_t self, esp_gmf_overlay_rgn_info_t *rgn); + +#ifdef __cplusplus +} +#endif diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/include/esp_gmf_video_ppa.h b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/include/esp_gmf_video_ppa.h new file mode 100644 index 0000000..49d5ba5 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/include/esp_gmf_video_ppa.h @@ -0,0 +1,99 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#include "esp_gmf_element.h" +#include "esp_gmf_video_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Video PPA (Pixel Processing Accelerator) is a compact module + * Currently it only support on ESP32P4 and including following video processing: + * 1) Color convert + * 2) Scale + * 3) Rotate + * 5) Crop + * These operations can be mixed so that can do them all together for better performance + */ + +/** + * @brief Initializes the GMF Video PPA + * + * @param[in] config No need to set + * @param[out] handle Video PPA handle to store + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid configuration provided + * - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory + */ +esp_gmf_err_t esp_gmf_video_ppa_init(void *config, esp_gmf_element_handle_t *handle); + +/** + * @brief Set video PPA destination resolution + * + * @note This API should only called before element running + * + * @param[in] handle Video PPA handle + * @param[in] res Video PPA output resolution + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid configuration provided + * - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory + */ +esp_gmf_err_t esp_gmf_video_ppa_set_dst_resolution(esp_gmf_element_handle_t handle, esp_gmf_video_resolution_t *res); + +/** + * @brief Set video PPA destination format + * + * @note This API should only called before element running + * + * @param[in] handle Video PPA handle + * @param[in] format Video PPA destination format (GMF FourCC representation of video format) + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid configuration provided + * - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory + */ +esp_gmf_err_t esp_gmf_video_ppa_set_dst_format(esp_gmf_element_handle_t handle, uint32_t format); + +/** + * @brief Set video PPA cropped region + * + * @note This API should only called before element running + * + * @param[in] handle Video PPA handle + * @param[in] rgn Region to be kept in original video frame + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid configuration provided + * - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory + */ +esp_gmf_err_t esp_gmf_video_ppa_set_cropped_rgn(esp_gmf_element_handle_t handle, esp_gmf_video_rgn_t *rgn); + +/** + * @brief Set video PPA rotation degree + * + * @note This API should only called before element running + * + * @param[in] handle Video PPA handle + * @param[in] degree Currently only support (0, 90, 180, 270) unit(one degree) + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid configuration provided + */ +esp_gmf_err_t esp_gmf_video_ppa_set_rotation(esp_gmf_element_handle_t handle, uint16_t degree); + +#ifdef __cplusplus +} +#endif diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/include/esp_gmf_video_rotate.h b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/include/esp_gmf_video_rotate.h new file mode 100644 index 0000000..b418bc9 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/include/esp_gmf_video_rotate.h @@ -0,0 +1,57 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#include "esp_gmf_element.h" +#include "esp_imgfx_rotate.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define DEFAULT_ESP_GMF_ROTATE_CONFIG() { \ + .in_res = { \ + .width = 320, \ + .height = 240, \ + }, \ + .in_pixel_fmt = ESP_IMGFX_PIXEL_FMT_RGB565_LE, \ + .degree = 0, \ +} + +/** + * @brief Initialize the video rotate element with the specified configuration + * + * @param[in] config Pointer to a `esp_imgfx_rotate_cfg_t` structure that contains the + * configuration settings for the video rotate + * @param[out] handle Pointer to a `esp_gmf_element_handle_t` where the initialized video rotate + * handle will be stored + * + * @return + * - ESP_GMF_ERR_OK Initialization successful + * - ESP_GMF_ERR_INVALID_ARG Invalid arguments + * - ESP_GMF_ERR_MEMORY_LACK No memory for video rotate element + */ +esp_gmf_err_t esp_gmf_video_rotate_init(esp_imgfx_rotate_cfg_t *config, esp_gmf_element_handle_t *handle); + +/** + * @brief Set video rotate rotation degree + * + * @note This API have no special timing request, user can call it freely + * + * @param[in] handle Video video rotate handle + * @param[in] degree Any angle, unit(one degree) + * + * @return + * - ESP_GMF_ERR_OK Successful + * - ESP_GMF_ERR_INVALID_ARG Invalid arguments + */ +esp_gmf_err_t esp_gmf_video_rotate_set_rotation(esp_gmf_element_handle_t handle, uint16_t degree); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/include/esp_gmf_video_scale.h b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/include/esp_gmf_video_scale.h new file mode 100644 index 0000000..afcf234 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/include/esp_gmf_video_scale.h @@ -0,0 +1,62 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#include "esp_gmf_element.h" +#include "esp_gmf_video_types.h" +#include "esp_imgfx_scale.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define DEFAULT_ESP_GMF_SCALE_CONFIG() { \ + .in_res = { \ + .width = 320, \ + .height = 240, \ + }, \ + .in_pixel_fmt = ESP_IMGFX_PIXEL_FMT_RGB565_LE, \ + .scale_res = { \ + .width = 160, \ + .height = 120, \ + }, \ + .filter_type = ESP_IMGFX_SCALE_FILTER_TYPE_DOWN_RESAMPLE \ +} + +/** + * @brief Initialize the video scale element with the specified configuration + * + * @param[in] config Pointer to a `esp_imgfx_scale_cfg_t` structure that contains the + * configuration settings for the video scale + * @param[out] handle Pointer to a `esp_gmf_element_handle_t` where the initialized video scale + * handle will be stored + * + * @return + * - ESP_GMF_ERR_OK Initialization successful + * - ESP_GMF_ERR_INVALID_ARG Invalid arguments + * - ESP_GMF_ERR_MEMORY_LACK No memory for video scale element + */ +esp_gmf_err_t esp_gmf_video_scale_init(esp_imgfx_scale_cfg_t *config, esp_gmf_element_handle_t *handle); + +/** + * @brief Set video scale destination resolution + * + * @note This API have no special timing request, user can call it freely + * + * @param[in] handle Video video scale handle + * @param[in] res Video video scale output resolution + * + * @return + * - ESP_GMF_ERR_OK Successful + * - ESP_GMF_ERR_INVALID_ARG Invalid arguments + */ +esp_gmf_err_t esp_gmf_video_scale_dst_resolution(esp_gmf_element_handle_t handle, esp_gmf_video_resolution_t *res); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/include/esp_gmf_video_types.h b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/include/esp_gmf_video_types.h new file mode 100644 index 0000000..093fbb3 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/include/esp_gmf_video_types.h @@ -0,0 +1,47 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#include "esp_gmf_element.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Video resolution definition + */ +typedef struct { + uint16_t width; /*!< Width of the video in pixels */ + uint16_t height; /*!< Height of the video in pixels */ +} esp_gmf_video_resolution_t; + +/** + * @brief Video region definition + * Represents a rectangular region of the video frame + */ +typedef struct { + uint16_t x; /*!< X-coordinate of the top-left corner */ + uint16_t y; /*!< Y-coordinate of the top-left corner */ + uint16_t width; /*!< Width of the region in pixels */ + uint16_t height; /*!< Height of the region in pixels */ +} esp_gmf_video_rgn_t; + +/** + * @brief Specifies the format and destination region for a video overlay + * + * @note This structure defines the parameters needed to position and format an overlay on a video frame + */ +typedef struct { + uint32_t format_id; /*!< FourCC representation of overlay format */ + esp_gmf_video_rgn_t dst_rgn; /*!< Region position to put the overlay */ +} esp_gmf_overlay_rgn_info_t; + +#ifdef __cplusplus +} +#endif diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/private_include/gmf_video_common.h b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/private_include/gmf_video_common.h new file mode 100644 index 0000000..3d8a29b --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/gmf_video/private_include/gmf_video_common.h @@ -0,0 +1,147 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#include +#include +#include "esp_gmf_event.h" +#include "esp_gmf_video_element.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * @brief Atomic declare and operations + */ +#define ATOM_VAR atomic_int +#define GET_ATOM(v) atomic_load(&v); +#define SET_ATOM(atom, v) atomic_store(&atom, v) +#define ATOM_SET_BITS(atom, bits) atomic_fetch_or(&atom, bits) +#define ATOM_CLEAR_BITS(atom, bits) atomic_fetch_and(&atom, ~bits) + +#define GMF_VIDEO_BREAK_ON_FAIL(ret) if (ret != ESP_GMF_ERR_OK) { \ + break; \ +} + +#define GMF_VIDEO_ALIGN_UP(size, align) (((size) + ((align) - 1)) & ~((align) - 1)) + +#define GMF_VIDEO_UPDATE_CONFIG(config, info, need_recfg) do { \ + (need_recfg) = ((config)->in_pixel_fmt != (info)->format_id) \ + || ((config)->in_res.width != (info)->width) \ + || ((config)->in_res.height != (info)->height); \ + (config)->in_res.width = (info)->width; \ + (config)->in_res.height = (info)->height; \ + (config)->in_pixel_fmt = (info)->format_id; \ +} while (0); + +/** + * @brief Get video format string representation + * + * @param[in] format Video format FourCC representation + * + * @return + * - "none" Not valid codec type + * - Others String representation of codec + */ +const char *esp_gmf_video_get_format_string(uint32_t format); + +/** + * @brief Common video event handler + * + * @note This API implement basic handler of GMF report info event, element should derive from `esp_gmf_video_element_t` + * + * @param[in] evt GMT event packet + * @param[in] ctx Event context + * + * @return + * - ESP_GMF_ERR_OK On success + */ +esp_gmf_err_t esp_gmf_video_handle_events(esp_gmf_event_pkt_t *evt, void *ctx); + +/** + * @brief Update basic information of video element + * This function updates the width, height, and pixel format of a video element. + * Typically called when video stream configuration changes (e.g., resolution adjustment + * or format switching) to synchronize the latest parameters + * + * @param[in] self Video element handle pointing to the target video element object. + * Must be initialized and non-NULL. + * @param[in] width New video width in pixels + * @param[in] height New video height in pixels + * @param[in] pixel_fmt Pixel format + * + */ +static inline void gmf_video_update_info(esp_gmf_video_element_handle_t self, uint16_t width, uint16_t height, uint32_t pixel_fmt) +{ + esp_gmf_info_video_t vid_info = {0}; + esp_gmf_video_el_get_src_info(self, &vid_info); + vid_info.height = height; + vid_info.width = width; + vid_info.format_id = pixel_fmt; + esp_gmf_element_notify_vid_info(self, &vid_info); +} + +/** + * @brief Acquire input/output payload for processing + * + * @param[in] in_port Handle to the input port + * @param[in] out_port Handle to the output port + * @param[out] in_load Double pointer to receive the acquired input payload buffer + * The buffer will be allocated/reserved according to in_wanted_size + * @param[out] out_load Double pointer to receive the acquired output payload buffer + * The buffer will be allocated/reserved according to out_wanted_size + * @param[in] in_wanted_size Desired minimum size for input payload buffer (in bytes) + * @param[in] out_wanted_size Desired minimum size for output payload buffer (in bytes) + * @param[in] is_bypass Bypass flag indicating whether to use zero-copy optimization (true) + * or allow data modification (false) + * + * @return + * - ESP_GMF_JOB_ERR_OK On success + * - ESP_GMF_JOB_ERR_FAIL On failure + * + */ +static inline esp_gmf_job_err_t video_el_acquire_payload(esp_gmf_port_handle_t in_port, esp_gmf_port_handle_t out_port, esp_gmf_payload_t **in_load, + esp_gmf_payload_t **out_load, uint32_t in_wanted_size, uint32_t out_wanted_size, bool is_bypass) +{ + esp_gmf_err_io_t load_ret = ESP_GMF_IO_OK; + load_ret = esp_gmf_port_acquire_in(in_port, in_load, in_wanted_size, ESP_GMF_MAX_DELAY); + if (load_ret < ESP_GMF_IO_OK) { + if (load_ret == ESP_GMF_IO_ABORT) { + return ESP_GMF_JOB_ERR_OK; + } + ESP_LOGE("Imgfx_el", "Acquire size: %ld on in port, ret: %d", in_wanted_size, load_ret); + return ESP_GMF_JOB_ERR_FAIL; + } + if (((*in_load)->valid_size) < (in_wanted_size)) { + ESP_LOGE("Imgfx_el", "Acquire size %ld-%d on in port is not enough", in_wanted_size, (*in_load)->valid_size); + return ESP_GMF_JOB_ERR_FAIL; + } + if ((in_port->is_shared == 1) + && (is_bypass)) { + /* If it is share buffer and is_bypass is true, just copy it */ + *out_load = (*in_load); + } + load_ret = esp_gmf_port_acquire_out(out_port, out_load, out_wanted_size, ESP_GMF_MAX_DELAY); + if (load_ret < ESP_GMF_IO_OK) { + if (load_ret == ESP_GMF_IO_ABORT) { + return ESP_GMF_JOB_ERR_OK; + } + ESP_LOGE("Imgfx_el", "Acquire size: %ld on out port, ret: %d", out_wanted_size, load_ret); + return ESP_GMF_JOB_ERR_FAIL; + } + if ((*out_load)->buf_length < out_wanted_size) { + ESP_LOGE("Imgfx_el", "Acquire size %ld-%d on out port is not enough", out_wanted_size, (*out_load)->buf_length); + return ESP_GMF_JOB_ERR_FAIL; + } + return ESP_GMF_JOB_ERR_OK; +} + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/CMakeLists.txt b/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/CMakeLists.txt new file mode 100644 index 0000000..19a34a3 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/CMakeLists.txt @@ -0,0 +1,5 @@ +cmake_minimum_required(VERSION 3.16) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) + +project(test_esp_gmf_core) diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/main/CMakeLists.txt b/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/main/CMakeLists.txt new file mode 100644 index 0000000..48bd065 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/main/CMakeLists.txt @@ -0,0 +1,5 @@ +idf_component_register(SRC_DIRS "." "elements" + INCLUDE_DIRS "." "elements" + REQUIRES unity esp_codec_dev esp_gdbstub + EMBED_FILES "hi_lexin.pcm" "test_48000hz_16bit_2ch_5000ms.flac" + WHOLE_ARCHIVE) diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/main/elements/gmf_ai_audio_test.c b/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/main/elements/gmf_ai_audio_test.c new file mode 100644 index 0000000..d668f84 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/main/elements/gmf_ai_audio_test.c @@ -0,0 +1,597 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "unity.h" +#include +#include +#include +#include +#include +#include +#include + +#include "esp_err.h" +#include "esp_log.h" +#include "esp_bit_defs.h" +#include "freertos/FreeRTOS.h" +#include "freertos/event_groups.h" + +#include "esp_gmf_element.h" +#include "esp_gmf_oal_mem.h" +#include "esp_gmf_obj.h" +#include "esp_gmf_port.h" +#include "esp_gmf_job.h" +#include "esp_gmf_cap.h" +#include "esp_gmf_caps_def.h" + +#include "esp_gmf_wn.h" +#if defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32P4) +#include "esp_afe_config.h" +#include "esp_gmf_afe_manager.h" +#include "esp_gmf_afe.h" +#include "esp_gmf_aec.h" +#include "esp_dsp.h" +#endif /* defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32P4) */ +#include "esp_gmf_app_setup_peripheral.h" + +#define DEBUG2FILE (false) +#define FS 16000 +#define DURATION 1 +#define SIGNAL_LEN (FS * DURATION) +#define STEREO_LEN (SIGNAL_LEN * 2) +#define DELAY 0 +#define ATTENUATION 0.1 + +#define WAKEUP_DETECTED (BIT0) +#define VAD_DETECTED (BIT1) +#define VCMD_FOUND (BIT2) +#ifdef CONFIG_IDF_TARGET_ESP32 +#define AEC_ENABLE (false) +#define MN_ENABLE (false) +#define EVENTS_2_WAIT (WAKEUP_DETECTED | VAD_DETECTED) +#elif defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32P4) +#define AEC_ENABLE (true) +#define MN_ENABLE (true) +#define EVENTS_2_WAIT (WAKEUP_DETECTED | VAD_DETECTED | VCMD_FOUND) +#else +#define AEC_ENABLE (false) +#define MN_ENABLE (false) +#define EVENTS_2_WAIT (WAKEUP_DETECTED) +#endif /* CONFIG_IDF_TARGET_ESP32 */ + +static const char *TAG = "AI_AUDIO_TEST"; +static uint32_t out_count = 0; +static EventGroupHandle_t g_event_group = NULL; +extern const uint8_t hi_lexin_pcm_start[] asm("_binary_hi_lexin_pcm_start"); +extern const uint8_t hi_lexin_pcm_end[] asm("_binary_hi_lexin_pcm_end"); + +#if defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32P4) +void generate_reference_signal(int16_t *signal, int len) +{ + for (int i = 0; i < len; i++) { + double t = (double)i / FS; + double value = sin(2 * M_PI * 1000 * t); + signal[i] = (int16_t)(value * 32767); + } +} + +void generate_noise(int16_t *noise_buffer, int len) +{ + for (int i = 0; i < len; i++) { + double t = (double)i / FS; + double value = sin(2 * M_PI * 2000 * t); + noise_buffer[i] = (int16_t)(value * 32767 * ATTENUATION); + } +} + +void generate_echo_signal(int16_t *reference, int16_t *noise_buffer, int16_t *echo, int len) +{ + for (int i = 0; i < len; i++) { + int idx = (i - DELAY) >= 0 ? (i - DELAY) : 0; + double echo_sample = reference[idx] * ATTENUATION; + echo_sample += noise_buffer[i]; + if (echo_sample > 32767) { + echo_sample = 32767; + } + if (echo_sample < -32768) { + echo_sample = -32768; + } + + echo[i] = (int16_t)echo_sample; + } +} + +void create_stereo_pcm(int16_t *reference, int16_t *echo, int length, int16_t *stereo) +{ + for (int i = 0; i < length; i++) { + stereo[2 * i] = reference[i]; + stereo[2 * i + 1] = echo[i]; + } +} + +void analyze_frequency(int16_t *pcm_data, uint32_t n_samples) +{ + float *real = (float *)esp_gmf_oal_malloc_align(16, n_samples * 2 * sizeof(float)); + float *hanning_win = (float *)esp_gmf_oal_malloc_align(16, n_samples * sizeof(float)); + TEST_ASSERT_NOT_NULL(real); + TEST_ASSERT_NOT_NULL(hanning_win); + + // Perform FFT + TEST_ASSERT_EQUAL(ESP_OK, dsps_fft2r_init_fc32(NULL, CONFIG_DSP_MAX_FFT_SIZE)); + dsps_wind_hann_f32(hanning_win, n_samples); + for (int i = 0; i < n_samples; i++) { + real[i * 2 + 0] = ((float)pcm_data[i] / 32768.0f) * hanning_win[i]; + real[i * 2 + 1] = 0; + } + + dsps_fft2r_fc32(real, n_samples); + dsps_bit_rev_fc32(real, n_samples); + dsps_cplx2reC_fc32(real, n_samples); + + for (int i = 0; i < n_samples >> 1; i++) { + real[i] = 10 * log10f((real[i * 2 + 0] * real[i * 2 + 0] + real[i * 2 + 1] * real[i * 2 + 1]) / n_samples); + } + + // dsps_view(real, n_samples / 2, 64, 10, -60, 40, '|'); + float max_energy = -INFINITY; + int max_index = 0; + for (int i = 0; i < n_samples >> 1; i++) { + if (real[i] > max_energy) { + max_energy = real[i]; + max_index = i; + } + } + float max_frequency = (float)max_index * FS / n_samples; + if (max_energy > -20) { + TEST_ASSERT_EQUAL(2000, (int)max_frequency); + } + + esp_gmf_oal_free(real); + esp_gmf_oal_free(hanning_win); + dsps_fft2r_deinit_fc32(); +} + +static esp_gmf_err_io_t aec_acquire_read(void *handle, esp_gmf_payload_t *load, int wanted_size, int block_ticks) +{ + uint8_t *src = (uint8_t *)handle; + static int count = 0; + if (load->buf == NULL) { + return ESP_GMF_IO_FAIL; + } + esp_gmf_err_io_t ret = ESP_GMF_IO_OK; + if (count < STEREO_LEN * 2) { + if (count + wanted_size > STEREO_LEN * 2) { + wanted_size = STEREO_LEN * 2 - count; + } + memcpy(load->buf, &src[count], wanted_size); + count += wanted_size; + load->valid_size = wanted_size; + + if (count == STEREO_LEN * 2) { + load->is_done = true; + } + } else { + load->valid_size = 0; + load->is_done = true; + } + return ret; +} + +static esp_gmf_err_io_t aec_release_read(void *handle, esp_gmf_payload_t *load, int block_ticks) +{ + load->valid_size = 0; + return ESP_GMF_IO_OK; +} + +static esp_gmf_err_io_t aec_acquire_write(void *handle, esp_gmf_payload_t *load, int wanted_size, int block_ticks) +{ + if (load->buf == NULL) { + return ESP_FAIL; + } + return ESP_GMF_IO_OK; +} + +static esp_gmf_err_io_t aec_release_write(void *handle, esp_gmf_payload_t *load, int block_ticks) +{ + uint8_t *dest = (uint8_t *)handle; +#ifdef CONFIG_IDF_TARGET_ESP32S3 + analyze_frequency((int16_t *)load->buf, load->valid_size / 2); +#endif /* CONFIG_IDF_TARGET_ESP32S3 */ + if (out_count < SIGNAL_LEN * 2) { + if (out_count + load->valid_size > SIGNAL_LEN * 2) { + load->valid_size = SIGNAL_LEN * 2 - out_count; + } + memcpy(&dest[out_count], load->buf, load->valid_size); + out_count += load->valid_size; + if (out_count == SIGNAL_LEN * 2) { + load->is_done = true; + } + } else { + load->valid_size = 0; + load->is_done = true; + } + return ESP_GMF_IO_OK; +} + +TEST_CASE("Test gmf aec process", "[ESP_GMF_AEC]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + ESP_GMF_MEM_SHOW(TAG); + + printf("\r\n///////////////////// AEC /////////////////////\r\n"); +#if DEBUG2FILE == true + void *sdcard_handle = NULL; + esp_gmf_app_setup_sdcard(&sdcard_handle); +#endif /* DEBUG2FILE == true */ + int16_t *reference_signal = (int16_t *)esp_gmf_oal_malloc_align(16, SIGNAL_LEN * sizeof(int16_t)); + TEST_ASSERT_NOT_NULL(reference_signal); + int16_t *echo_signal = (int16_t *)esp_gmf_oal_malloc_align(16, SIGNAL_LEN * sizeof(int16_t)); + TEST_ASSERT_NOT_NULL(echo_signal); + int16_t *stereo_signal = (int16_t *)esp_gmf_oal_malloc_align(16, STEREO_LEN * sizeof(int16_t)); + TEST_ASSERT_NOT_NULL(stereo_signal); + int16_t *output_signal = (int16_t *)esp_gmf_oal_malloc_align(16, SIGNAL_LEN * sizeof(int16_t)); + TEST_ASSERT_NOT_NULL(output_signal); + int16_t *noise_buffer = (int16_t *)esp_gmf_oal_malloc_align(16, SIGNAL_LEN * sizeof(int16_t)); + TEST_ASSERT_NOT_NULL(noise_buffer); + generate_reference_signal(reference_signal, SIGNAL_LEN); + generate_noise(noise_buffer, SIGNAL_LEN); + generate_echo_signal(reference_signal, noise_buffer, echo_signal, SIGNAL_LEN); + create_stereo_pcm(reference_signal, echo_signal, SIGNAL_LEN, stereo_signal); + + esp_gmf_port_handle_t in_port = NEW_ESP_GMF_PORT_IN_BYTE(aec_acquire_read, aec_release_read, NULL, stereo_signal, 1024, 100); + esp_gmf_port_handle_t out_port = NEW_ESP_GMF_PORT_OUT_BYTE(aec_acquire_write, aec_release_write, NULL, output_signal, 1024, 100); + + esp_gmf_element_handle_t gmf_aec_handle = NULL; + esp_gmf_aec_cfg_t gmf_aec_cfg = { + .filter_len = 4, + .type = AFE_TYPE_VC, + .mode = AFE_MODE_HIGH_PERF, + .input_format = (char *)"RM", + }; + esp_gmf_aec_init(&gmf_aec_cfg, &gmf_aec_handle); + esp_gmf_cap_t *caps = NULL; + esp_gmf_cap_t *out_caps = NULL; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_element_get_caps(gmf_aec_handle, (const esp_gmf_cap_t **)&caps)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_cap_fetch_node(caps, ESP_GMF_CAPS_AUDIO_AEC, &out_caps)); + + esp_gmf_element_register_in_port(gmf_aec_handle, in_port); + esp_gmf_element_register_out_port(gmf_aec_handle, out_port); + esp_gmf_element_process_open(gmf_aec_handle, NULL); + esp_gmf_job_err_t ret = ESP_GMF_JOB_ERR_OK; + do { + ret = esp_gmf_element_process_running(gmf_aec_handle, NULL); + if (ret == ESP_GMF_JOB_ERR_FAIL) { + ESP_LOGE(TAG, "AEC process failed"); + break; + } else if (ret == ESP_GMF_JOB_ERR_DONE) { + ESP_LOGI(TAG, "AEC process done"); + break; + } + } while (true); + esp_gmf_element_process_close(gmf_aec_handle, NULL); + esp_gmf_obj_delete(gmf_aec_handle); + +#if DEBUG2FILE == true + FILE *fp = fopen("/sdcard/reference_signal.pcm", "wb"); + if (fp) { + fwrite(reference_signal, sizeof(int16_t), SIGNAL_LEN, fp); + fclose(fp); + } else { + ESP_LOGE(TAG, "Failed to open reference_signal.pcm for writing"); + } + fp = fopen("/sdcard/echo_signal.pcm", "wb"); + if (fp) { + fwrite(echo_signal, sizeof(int16_t), SIGNAL_LEN, fp); + fclose(fp); + } else { + ESP_LOGE(TAG, "Failed to open echo_signal.pcm for writing"); + } + fp = fopen("/sdcard/output_signal.pcm", "wb"); + if (fp) { + fwrite(output_signal, 1, out_count, fp); + fclose(fp); + } else { + ESP_LOGE(TAG, "Failed to open output_signal.pcm for writing"); + } + fp = fopen("/sdcard/stereo_signal.pcm", "wb"); + if (fp) { + fwrite(stereo_signal, sizeof(int16_t), STEREO_LEN, fp); + fclose(fp); + } else { + ESP_LOGE(TAG, "Failed to open stereo_signal.pcm for writing"); + } + fp = fopen("/sdcard/noise_buffer.pcm", "wb"); + if (fp) { + fwrite(noise_buffer, sizeof(int16_t), SIGNAL_LEN, fp); + fclose(fp); + } else { + ESP_LOGE(TAG, "Failed to open noise_buffer.pcm for writing"); + } + esp_gmf_app_teardown_sdcard(sdcard_handle); +#endif /* DEBUG2FILE == true */ + out_count = 0; + esp_gmf_oal_free(noise_buffer); + esp_gmf_oal_free(reference_signal); + esp_gmf_oal_free(echo_signal); + esp_gmf_oal_free(stereo_signal); + esp_gmf_oal_free(output_signal); +} + +TEST_CASE("Test gmf afe manager create", "[ESP_GMF_AFE_MANAGER]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + ESP_GMF_MEM_SHOW(TAG); + + printf("\r\n///////////////////// AFE MANAGER CREATE /////////////////////\r\n"); + + esp_gmf_afe_manager_handle_t afe_manager = NULL; + srmodel_list_t *models = esp_srmodel_init("model"); + const char *ch_format = "MR"; + afe_config_t *afe_cfg = afe_config_init(ch_format, models, AFE_TYPE_SR, AFE_MODE_HIGH_PERF); + afe_cfg->vad_init = true; + afe_cfg->vad_mode = VAD_MODE_2; + afe_cfg->vad_min_speech_ms = 64; + afe_cfg->vad_min_noise_ms = 100; + afe_cfg->wakenet_init = true; + afe_cfg->aec_init = true; + esp_gmf_afe_manager_cfg_t afe_manager_cfg = DEFAULT_GMF_AFE_MANAGER_CFG(afe_cfg, NULL, NULL, NULL, NULL); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_afe_manager_create(&afe_manager_cfg, &afe_manager)); + afe_config_free(afe_cfg); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_afe_manager_destroy(afe_manager)); + esp_srmodel_deinit(models); +} + +static esp_gmf_err_io_t afe_acquire_read(void *handle, esp_gmf_payload_t *load, int wanted_size, int block_ticks) +{ + static int offset = 0; + const uint8_t *src = hi_lexin_pcm_start; + int total_size = hi_lexin_pcm_end - hi_lexin_pcm_start; + + if (offset < total_size) { + if (offset + wanted_size > total_size) { + wanted_size = total_size - offset; + } + memcpy(load->buf, &src[offset], wanted_size); + offset += wanted_size; + load->valid_size = wanted_size; + + if (offset == total_size) { + offset = 0; + load->is_done = true; + } + } else { + load->valid_size = 0; + load->is_done = true; + } + return ESP_GMF_IO_OK; +} + +static esp_gmf_err_io_t afe_release_read(void *handle, esp_gmf_payload_t *load, int block_ticks) +{ + load->valid_size = 0; + return ESP_GMF_IO_OK; +} + +static esp_gmf_err_io_t afe_acquire_write(void *handle, esp_gmf_payload_t *load, int wanted_size, int block_ticks) +{ + if (load->buf == NULL) { + return ESP_GMF_ERR_FAIL; + } + return ESP_GMF_IO_OK; +} + +static esp_gmf_err_io_t afe_release_write(void *handle, esp_gmf_payload_t *load, int block_ticks) +{ + return ESP_GMF_IO_OK; +} + +void esp_gmf_afe_event_cb(esp_gmf_obj_handle_t obj, esp_gmf_afe_evt_t *event, void *user_data) +{ + switch (event->type) { + case ESP_GMF_AFE_EVT_WAKEUP_START: { + xEventGroupSetBits(g_event_group, WAKEUP_DETECTED); +#if MN_ENABLE == true + esp_gmf_afe_vcmd_detection_cancel(obj); + esp_gmf_afe_vcmd_detection_begin(obj); +#endif /* MN_ENABLE == true */ + esp_gmf_afe_wakeup_info_t *info = event->event_data; + ESP_LOGI(TAG, "WAKEUP_START [%d : %d]", info->wake_word_index, info->wakenet_model_index); + break; + } + case ESP_GMF_AFE_EVT_WAKEUP_END: { +#if MN_ENABLE == true + esp_gmf_afe_vcmd_detection_cancel(obj); +#endif /* MN_ENABLE == true */ + ESP_LOGI(TAG, "WAKEUP_END"); + break; + } + case ESP_GMF_AFE_EVT_VAD_START: { + xEventGroupSetBits(g_event_group, VAD_DETECTED); + ESP_LOGI(TAG, "VAD_START"); + break; + } + case ESP_GMF_AFE_EVT_VAD_END: { + ESP_LOGI(TAG, "VAD_END"); + break; + } + case ESP_GMF_AFE_EVT_VCMD_DECT_TIMEOUT: { + ESP_LOGI(TAG, "VCMD_DECT_TIMEOUT"); + break; + } + default: { + esp_gmf_afe_vcmd_info_t *info = event->event_data; + ESP_LOGW(TAG, "Command %d, phrase_id %d, prob %f, str: %s", + event->type, info->phrase_id, info->prob, info->str); + if (event->type == 25 || event->type == 216) { + // 25: "da kai kong tiao" + // 216: "kai kong tiao" + xEventGroupSetBits(g_event_group, VCMD_FOUND); + } + break; + } + } +} + +TEST_CASE("Test gmf afe process", "[ESP_GMF_AFE]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + ESP_GMF_MEM_SHOW(TAG); + + printf("\r\n///////////////////// AFE /////////////////////\r\n"); + g_event_group = xEventGroupCreate(); + TEST_ASSERT_NOT_NULL(g_event_group); + + esp_gmf_port_handle_t in_port = NEW_ESP_GMF_PORT_IN_BYTE(afe_acquire_read, afe_release_read, NULL, NULL, 1024, 100); + esp_gmf_port_handle_t out_port = NEW_ESP_GMF_PORT_OUT_BYTE(afe_acquire_write, afe_release_write, NULL, NULL, 1024, 100); + + esp_gmf_afe_manager_handle_t afe_manager = NULL; + srmodel_list_t *models = esp_srmodel_init("model"); + const char *ch_format = "MR"; + afe_config_t *afe_cfg = afe_config_init(ch_format, models, AFE_TYPE_SR, AFE_MODE_HIGH_PERF); + afe_cfg->vad_init = true; + afe_cfg->vad_mode = VAD_MODE_2; + afe_cfg->vad_min_speech_ms = 64; + afe_cfg->vad_min_noise_ms = 100; + afe_cfg->wakenet_init = true; + afe_cfg->aec_init = AEC_ENABLE; + esp_gmf_afe_manager_cfg_t afe_manager_cfg = DEFAULT_GMF_AFE_MANAGER_CFG(afe_cfg, NULL, NULL, NULL, NULL); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_afe_manager_create(&afe_manager_cfg, &afe_manager)); + esp_gmf_element_handle_t gmf_afe = NULL; + esp_gmf_afe_cfg_t gmf_afe_cfg = DEFAULT_GMF_AFE_CFG(afe_manager, esp_gmf_afe_event_cb, NULL, models); + gmf_afe_cfg.vcmd_detect_en = MN_ENABLE; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_afe_init(&gmf_afe_cfg, &gmf_afe)); + esp_gmf_cap_t *caps = NULL; + esp_gmf_cap_t *out_caps = {0}; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_element_get_caps(gmf_afe, (const esp_gmf_cap_t **)&caps)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_cap_fetch_node(caps, ESP_GMF_CAPS_AUDIO_AEC, &out_caps)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_cap_fetch_node(caps, ESP_GMF_CAPS_AUDIO_NS, &out_caps)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_cap_fetch_node(caps, ESP_GMF_CAPS_AUDIO_AGC, &out_caps)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_cap_fetch_node(caps, ESP_GMF_CAPS_AUDIO_VAD, &out_caps)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_cap_fetch_node(caps, ESP_GMF_CAPS_AUDIO_WWE, &out_caps)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_cap_fetch_node(caps, ESP_GMF_CAPS_AUDIO_VCMD, &out_caps)); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_element_register_in_port(gmf_afe, in_port)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_element_register_out_port(gmf_afe, out_port)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_element_process_open(gmf_afe, NULL)); + esp_gmf_job_err_t ret = ESP_GMF_JOB_ERR_OK; + do { + ret = esp_gmf_element_process_running(gmf_afe, NULL); + if (ret == ESP_GMF_JOB_ERR_FAIL) { + ESP_LOGE(TAG, "AFE process failed"); + break; + } else if (ret == ESP_GMF_JOB_ERR_DONE) { + ESP_LOGI(TAG, "AFE process done"); + break; + } + vTaskDelay(pdMS_TO_TICKS(20)); + } while (true); + TEST_ASSERT_EQUAL(EVENTS_2_WAIT, xEventGroupWaitBits(g_event_group, EVENTS_2_WAIT, pdTRUE, pdTRUE, pdMS_TO_TICKS(10 * 1000))); + esp_gmf_element_process_close(gmf_afe, NULL); + esp_gmf_obj_delete(gmf_afe); + afe_config_free(afe_cfg); + esp_gmf_afe_manager_destroy(afe_manager); + esp_srmodel_deinit(models); + vEventGroupDelete(g_event_group); + g_event_group = NULL; +} +#endif /* CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32P4 */ + +static esp_gmf_err_io_t wn_acquire_read(void *handle, esp_gmf_payload_t *load, int wanted_size, int block_ticks) +{ + static int offset = 0; + const uint8_t *src = hi_lexin_pcm_start; + int total_size = hi_lexin_pcm_end - hi_lexin_pcm_start; + + if (offset < total_size) { + if (offset + wanted_size > total_size) { + wanted_size = total_size - offset; + } + memcpy(load->buf, &src[offset], wanted_size); + offset += wanted_size; + load->valid_size = wanted_size; + + if (offset == total_size) { + offset = 0; + load->is_done = true; + } + } else { + load->valid_size = 0; + load->is_done = true; + } + return 0; +} + +static esp_gmf_err_io_t wn_release_read(void *handle, esp_gmf_payload_t *load, int block_ticks) +{ + load->valid_size = 0; + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_io_t wn_acquire_write(void *handle, esp_gmf_payload_t *load, int wanted_size, int block_ticks) +{ + if (load->buf == NULL) { + return ESP_GMF_ERR_FAIL; + } + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_io_t wn_release_write(void *handle, esp_gmf_payload_t *load, int block_ticks) +{ + return ESP_GMF_ERR_OK; +} + +static void esp_gmf_wn_event_cb(esp_gmf_obj_handle_t obj, int32_t trigger_ch, void *user_ctx) +{ + static int32_t cnt = 1; + ESP_LOGI(TAG, "WWE detected on channel %" PRIu32 ", cnt: %" PRIi32, trigger_ch, cnt++); + xEventGroupSetBits(g_event_group, WAKEUP_DETECTED); +} + +TEST_CASE("Test gmf wakenet process", "[ESP_GMF_WN]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + ESP_GMF_MEM_SHOW(TAG); + + printf("\r\n///////////////////// Wakenet /////////////////////\r\n"); + g_event_group = xEventGroupCreate(); + TEST_ASSERT_NOT_NULL(g_event_group); + + esp_gmf_port_handle_t in_port = NEW_ESP_GMF_PORT_IN_BYTE(wn_acquire_read, wn_release_read, NULL, NULL, 1024, 100); + esp_gmf_port_handle_t out_port = NEW_ESP_GMF_PORT_OUT_BYTE(wn_acquire_write, wn_release_write, NULL, NULL, 1024, 100); + + srmodel_list_t *models = esp_srmodel_init("model"); + const char *ch_format = "MR"; + esp_gmf_wn_cfg_t wn_cfg = { + .input_format = (char *)ch_format, + .det_mode = DET_MODE_95, + .models = models, + .detect_cb = esp_gmf_wn_event_cb, + .user_ctx = NULL, + }; + esp_gmf_element_handle_t gmf_wn = NULL; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_wn_init(&wn_cfg, &gmf_wn)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_element_register_in_port(gmf_wn, in_port)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_element_register_out_port(gmf_wn, out_port)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_element_process_open(gmf_wn, NULL)); + esp_gmf_job_err_t ret = ESP_GMF_JOB_ERR_OK; + do { + ret = esp_gmf_element_process_running(gmf_wn, NULL); + if (ret == ESP_GMF_JOB_ERR_FAIL) { + ESP_LOGE(TAG, "WN process failed"); + break; + } else if (ret == ESP_GMF_JOB_ERR_DONE) { + ESP_LOGI(TAG, "WN process done"); + break; + } + vTaskDelay(pdMS_TO_TICKS(20)); + } while (true); + TEST_ASSERT_EQUAL(WAKEUP_DETECTED, xEventGroupWaitBits(g_event_group, WAKEUP_DETECTED, pdTRUE, pdTRUE, pdMS_TO_TICKS(10 * 1000))); + esp_gmf_element_process_close(gmf_wn, NULL); + esp_gmf_obj_delete(gmf_wn); + esp_srmodel_deinit(models); + vEventGroupDelete(g_event_group); + g_event_group = NULL; +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/main/elements/gmf_audio_effects_test.c b/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/main/elements/gmf_audio_effects_test.c new file mode 100644 index 0000000..a7519d6 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/main/elements/gmf_audio_effects_test.c @@ -0,0 +1,530 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "unity.h" +#include +#include "esp_err.h" +#include "freertos/FreeRTOS.h" +#include "freertos/event_groups.h" +#include "esp_log.h" + +#include "esp_gmf_element.h" +#include "esp_gmf_pipeline.h" +#include "esp_gmf_pool.h" +#include "esp_gmf_oal_mem.h" +#include "esp_gmf_oal_thread.h" +#include "esp_gmf_port.h" +#include "esp_gmf_obj.h" +#include "esp_gmf_data_bus.h" +#include "esp_gmf_new_databus.h" + +#include "esp_gmf_bit_cvt.h" +#include "esp_gmf_ch_cvt.h" +#include "esp_gmf_eq.h" +#include "esp_gmf_alc.h" +#include "esp_gmf_fade.h" +#include "esp_gmf_mixer.h" +#include "esp_gmf_rate_cvt.h" +#include "esp_gmf_sonic.h" +#include "esp_gmf_interleave.h" +#include "esp_gmf_deinterleave.h" +#include "esp_gmf_audio_enc.h" +#include "esp_gmf_audio_dec.h" +#include "esp_audio_simple_dec_default.h" +#include "esp_audio_enc_reg.h" +#include "esp_audio_dec_reg.h" + +#include "esp_gmf_app_setup_peripheral.h" +#include "esp_audio_enc_default.h" +#include "esp_audio_dec_default.h" +#include "esp_audio_dec_reg.h" +#include "esp_gmf_audio_helper.h" +#include "esp_gmf_audio_methods_def.h" +#include "esp_gmf_method.h" +#include "esp_gmf_audio_param.h" +#include "gmf_audio_play_com.h" + +#ifdef MEDIA_LIB_MEM_TEST +#include "media_lib_adapter.h" +#include "media_lib_mem_trace.h" +#endif /* MEDIA_LIB_MEM_TEST */ + +static const char *TAG = "AUDIO_EFFECTS_ELEMENT_TEST"; + +#define PIPELINE_BLOCK_BIT BIT(0) +#define PIPELINE_BLOCK_RUN_BIT BIT(1) + +#define ESP_GMF_PORT_PAYLOAD_LEN_DEFAULT (4096) + +static esp_err_t _pipeline_event(esp_gmf_event_pkt_t *event, void *ctx) +{ + // The warning messages are used to make the content more noticeable. + ESP_LOGW(TAG, "CB: RECV Pipeline EVT: el:%s-%p, type:%x, sub:%s, payload:%p, size:%d,%p", + "OBJ_GET_TAG(event->from)", event->from, event->type, esp_gmf_event_get_state_str(event->sub), + event->payload, event->payload_size, ctx); + if ((event->sub == ESP_GMF_EVENT_STATE_STOPPED) + || (event->sub == ESP_GMF_EVENT_STATE_FINISHED) + || (event->sub == ESP_GMF_EVENT_STATE_ERROR)) { + if (ctx) { + xEventGroupSetBits((EventGroupHandle_t)ctx, PIPELINE_BLOCK_BIT); + } + } + return 0; +} + +esp_err_t _pipeline_event2(esp_gmf_event_pkt_t *event, void *ctx) +{ + ESP_LOGW(TAG, "CB: RECV Pipeline2 EVT: el:%s-%p, type:%d, sub:%s, payload:%p, size:%d,%p", + "OBJ_GET_TAG(event->from)", event->from, event->type, esp_gmf_event_get_state_str(event->sub), + event->payload, event->payload_size, ctx); + if ((event->sub == ESP_GMF_EVENT_STATE_STOPPED) + || (event->sub == ESP_GMF_EVENT_STATE_FINISHED) + || (event->sub == ESP_GMF_EVENT_STATE_ERROR)) { + xEventGroupSetBits((EventGroupHandle_t)ctx, PIPELINE_BLOCK_BIT); + } + return 0; +} + +esp_err_t _pipeline_event3(esp_gmf_event_pkt_t *event, void *ctx) +{ + ESP_LOGW(TAG, "CB: RECV Pipeline3 EVT: el:%s-%p, type:%d, sub:%s, payload:%p, size:%d,%p", + "OBJ_GET_TAG(event->from)", event->from, event->type, esp_gmf_event_get_state_str(event->sub), + event->payload, event->payload_size, ctx); + if ((event->sub == ESP_GMF_EVENT_STATE_STOPPED) + || (event->sub == ESP_GMF_EVENT_STATE_FINISHED) + || (event->sub == ESP_GMF_EVENT_STATE_ERROR)) { + xEventGroupSetBits((EventGroupHandle_t)ctx, PIPELINE_BLOCK_BIT); + } + if (event->sub == ESP_GMF_EVENT_STATE_RUNNING) { + if (ctx) { + xEventGroupSetBits((EventGroupHandle_t)ctx, PIPELINE_BLOCK_RUN_BIT); + } + } + return 0; +} + +esp_err_t _pipeline_event4(esp_gmf_event_pkt_t *event, void *ctx) +{ + ESP_LOGW(TAG, "CB: RECV Pipeline4 EVT: el:%s-%p, type:%d, sub:%s, payload:%p, size:%d,%p", + "OBJ_GET_TAG(event->from)", event->from, event->type, esp_gmf_event_get_state_str(event->sub), + event->payload, event->payload_size, ctx); + if ((event->sub == ESP_GMF_EVENT_STATE_STOPPED) + || (event->sub == ESP_GMF_EVENT_STATE_FINISHED) + || (event->sub == ESP_GMF_EVENT_STATE_ERROR)) { + xEventGroupSetBits((EventGroupHandle_t)ctx, PIPELINE_BLOCK_BIT); + } + return 0; +} + +TEST_CASE("Audio Effects Play, [io_file->aud_dec->aud_rate_cvt->aud_ch_cvt->aud_bit_cvt->aud_sonic->aud_alc->aud_eq->aud_fade->io_codec_dev]", "[ESP_GMF_Effects]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + esp_log_level_set("ESP_GMF_PIPELINE", ESP_LOG_DEBUG); + esp_log_level_set("ESP_GMF_POOL", ESP_LOG_DEBUG); + ESP_GMF_MEM_SHOW(TAG); + esp_gmf_app_setup_codec_dev(NULL); + void *sdcard_handle = NULL; + esp_gmf_app_setup_sdcard(&sdcard_handle); + EventGroupHandle_t pipe_sync_evt = xEventGroupCreate(); + ESP_GMF_NULL_CHECK(TAG, pipe_sync_evt, return); + +#ifdef MEDIA_LIB_MEM_TEST + media_lib_add_default_adapter(); +#endif /* MEDIA_LIB_MEM_TEST */ + esp_gmf_pool_handle_t pool = NULL; + esp_gmf_pool_init(&pool); + TEST_ASSERT_NOT_NULL(pool); + + gmf_register_audio_all(pool); + + ESP_GMF_POOL_SHOW_ITEMS(pool); + esp_gmf_pipeline_handle_t pipe = NULL; + + const char *name[] = {"aud_dec", "aud_rate_cvt", "aud_ch_cvt", "aud_bit_cvt", "aud_sonic", "aud_alc", "aud_eq", "aud_fade"}; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pool_new_pipeline(pool, "io_file", name, sizeof(name) / sizeof(char *), "io_codec_dev", &pipe)); + TEST_ASSERT_NOT_NULL(pipe); + + gmf_setup_pipeline_out_dev(pipe); + esp_gmf_task_cfg_t cfg = DEFAULT_ESP_GMF_TASK_CONFIG(); + cfg.ctx = NULL; + cfg.cb = NULL; + esp_gmf_task_handle_t work_task = NULL; + esp_gmf_task_init(&cfg, &work_task); + TEST_ASSERT_NOT_NULL(work_task); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_bind_task(pipe, work_task)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_set_event(pipe, _pipeline_event, pipe_sync_evt)); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_reset(pipe)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_loading_jobs(pipe)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_set_in_uri(pipe, "/sdcard/test.mp3")); + esp_gmf_element_handle_t dec_el = NULL; + esp_gmf_element_handle_t sonic_el = NULL; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_get_el_by_name(pipe, "aud_dec", &dec_el)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_get_el_by_name(pipe, "aud_sonic", &sonic_el)); + esp_gmf_info_sound_t info = { + .format_id = ESP_AUDIO_SIMPLE_DEC_TYPE_MP3, + }; + esp_gmf_audio_dec_reconfig_by_sound_info(dec_el, &info); + esp_gmf_sonic_set_speed(sonic_el, 1.5f); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_run(pipe)); + + vTaskDelay(2000 / portTICK_RATE_MS); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_pause(pipe)); + vTaskDelay(1000 / portTICK_RATE_MS); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_resume(pipe)); + esp_gmf_sonic_set_pitch(sonic_el, 1.5f); + xEventGroupWaitBits(pipe_sync_evt, PIPELINE_BLOCK_BIT, pdTRUE, pdFALSE, portMAX_DELAY); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_stop(pipe)); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_deinit(work_task)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_destroy(pipe)); + gmf_unregister_audio_all(pool); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pool_deinit(pool)); + vEventGroupDelete(pipe_sync_evt); +#ifdef MEDIA_LIB_MEM_TEST + media_lib_stop_mem_trace(); +#endif /* MEDIA_LIB_MEM_TEST */ + esp_gmf_app_teardown_sdcard(sdcard_handle); + esp_gmf_app_teardown_codec_dev(); + vTaskDelay(1000 / portTICK_RATE_MS); + ESP_GMF_MEM_SHOW(TAG); +} + +/*** +// Test deinterleave with two pipeline + +-----+ + +- RB1 ->+ alc +-> RB3 -+ ++-------------------------------------+ | +-----+ | +-----------------------+ +| Pipe: file-->dec-->deinterleave |-- + +--| interleave-->rate_cvt | ++---------------------+-------------- + | +-----+ | +-----------------------+ + +- RB2 ->+ alc +-> RB4 -+ + +-----+ +***/ +TEST_CASE("Audio Effects Data Weaver test", "[ESP_GMF_Effects]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + esp_log_level_set("ESP_GMF_PIPELINE", ESP_LOG_DEBUG); + ESP_GMF_MEM_SHOW(TAG); + esp_gmf_app_setup_codec_dev(NULL); + void *sdcard_handle = NULL; + esp_gmf_app_setup_sdcard(&sdcard_handle); + EventGroupHandle_t pipe_sync_evt1 = xEventGroupCreate(); + ESP_GMF_NULL_CHECK(TAG, pipe_sync_evt1, return); + EventGroupHandle_t pipe_sync_evt2 = xEventGroupCreate(); + ESP_GMF_NULL_CHECK(TAG, pipe_sync_evt2, return); + EventGroupHandle_t pipe_sync_evt3 = xEventGroupCreate(); + ESP_GMF_NULL_CHECK(TAG, pipe_sync_evt3, return); + EventGroupHandle_t pipe_sync_evt4 = xEventGroupCreate(); + ESP_GMF_NULL_CHECK(TAG, pipe_sync_evt4, return); +#ifdef MEDIA_LIB_MEM_TEST + media_lib_add_default_adapter(); +#endif /* MEDIA_LIB_MEM_TEST */ + esp_gmf_pool_handle_t pool = NULL; + esp_gmf_pool_init(&pool); + TEST_ASSERT_NOT_NULL(pool); + + gmf_register_audio_all(pool); + + ESP_GMF_POOL_SHOW_ITEMS(pool); + esp_gmf_pipeline_handle_t pipe1 = NULL; + const char *name1[] = {"aud_dec", "aud_deintlv"}; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pool_new_pipeline(pool, "io_file", name1, sizeof(name1) / sizeof(char *), NULL, &pipe1)); + TEST_ASSERT_NOT_NULL(pipe1); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_set_in_uri(pipe1, "/sdcard/test.mp3")); + esp_gmf_element_handle_t dec_el = NULL; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_get_el_by_name(pipe1, "aud_dec", &dec_el)); + esp_gmf_info_sound_t info = { + .format_id = ESP_AUDIO_SIMPLE_DEC_TYPE_MP3, + }; + esp_gmf_audio_dec_reconfig_by_sound_info(dec_el, &info); + + esp_gmf_pipeline_handle_t pipe2 = NULL; + const char *name2[] = {"aud_alc"}; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pool_new_pipeline(pool, NULL, name2, sizeof(name2) / sizeof(char *), NULL, &pipe2)); + TEST_ASSERT_NOT_NULL(pipe2); + + esp_gmf_pipeline_handle_t pipe3 = NULL; + const char *name3[] = {"aud_alc"}; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pool_new_pipeline(pool, NULL, name3, sizeof(name3) / sizeof(char *), NULL, &pipe3)); + TEST_ASSERT_NOT_NULL(pipe3); + + esp_gmf_pipeline_handle_t pipe4 = NULL; + const char *name4[] = {"aud_intlv", "aud_rate_cvt"}; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pool_new_pipeline(pool, NULL, name4, sizeof(name4) / sizeof(char *), "io_codec_dev", &pipe4)); + TEST_ASSERT_NOT_NULL(pipe4); + + gmf_setup_pipeline_out_dev(pipe4); + // create rb + esp_gmf_db_handle_t db1 = NULL; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_db_new_ringbuf(10, 1024, &db1)); + esp_gmf_port_handle_t out_port = NEW_ESP_GMF_PORT_OUT_BYTE(esp_gmf_db_acquire_write, esp_gmf_db_release_write, esp_gmf_db_deinit, db1, + ESP_GMF_PORT_PAYLOAD_LEN_DEFAULT, 300); + esp_gmf_port_handle_t in_port = NEW_ESP_GMF_PORT_IN_BYTE(esp_gmf_db_acquire_read, esp_gmf_db_release_read, esp_gmf_db_deinit, db1, + ESP_GMF_PORT_PAYLOAD_LEN_DEFAULT, 300); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_connect_pipe(pipe1, "aud_deintlv", out_port, pipe2, "aud_alc", in_port)); + esp_gmf_db_handle_t db2 = NULL; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_db_new_ringbuf(10, 1024, &db2)); + out_port = NEW_ESP_GMF_PORT_OUT_BYTE(esp_gmf_db_acquire_write, esp_gmf_db_release_write, esp_gmf_db_deinit, db2, + ESP_GMF_PORT_PAYLOAD_LEN_DEFAULT, 3000); + in_port = NEW_ESP_GMF_PORT_IN_BYTE(esp_gmf_db_acquire_read, esp_gmf_db_release_read, esp_gmf_db_deinit, db2, + ESP_GMF_PORT_PAYLOAD_LEN_DEFAULT, 3000); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_connect_pipe(pipe1, "aud_deintlv", out_port, pipe3, "aud_alc", in_port)); + esp_gmf_db_handle_t db3 = NULL; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_db_new_ringbuf(10, 1024, &db3)); + out_port = NEW_ESP_GMF_PORT_OUT_BYTE(esp_gmf_db_acquire_write, esp_gmf_db_release_write, esp_gmf_db_deinit, db3, + ESP_GMF_PORT_PAYLOAD_LEN_DEFAULT, 3000); + in_port = NEW_ESP_GMF_PORT_IN_BYTE(esp_gmf_db_acquire_read, esp_gmf_db_release_read, esp_gmf_db_deinit, db3, + ESP_GMF_PORT_PAYLOAD_LEN_DEFAULT, 3000); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_connect_pipe(pipe2, "aud_alc", out_port, pipe4, "aud_intlv", in_port)); + esp_gmf_db_handle_t db4 = NULL; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_db_new_ringbuf(10, 1024, &db4)); + out_port = NEW_ESP_GMF_PORT_OUT_BYTE(esp_gmf_db_acquire_write, esp_gmf_db_release_write, esp_gmf_db_deinit, db4, + ESP_GMF_PORT_PAYLOAD_LEN_DEFAULT, 3000); + in_port = NEW_ESP_GMF_PORT_IN_BYTE(esp_gmf_db_acquire_read, esp_gmf_db_release_read, esp_gmf_db_deinit, db4, + ESP_GMF_PORT_PAYLOAD_LEN_DEFAULT, 3000); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_connect_pipe(pipe3, "aud_alc", out_port, pipe4, "aud_intlv", in_port)); + + esp_gmf_task_cfg_t cfg1 = DEFAULT_ESP_GMF_TASK_CONFIG(); + cfg1.ctx = NULL; + cfg1.cb = NULL; + cfg1.thread.core = 0; + cfg1.thread.prio = 3; + cfg1.name = "aud_deintlv"; + esp_gmf_task_handle_t work_task1 = NULL; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_init(&cfg1, &work_task1)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_bind_task(pipe1, work_task1)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_loading_jobs(pipe1)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_set_event(pipe1, _pipeline_event, pipe_sync_evt1)); + + esp_gmf_task_cfg_t cfg2 = DEFAULT_ESP_GMF_TASK_CONFIG(); + cfg2.thread.core = 0; + cfg2.thread.prio = 3; + cfg2.name = "channel1"; + esp_gmf_task_handle_t work_task2 = NULL; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_init(&cfg2, &work_task2)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_bind_task(pipe2, work_task2)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_loading_jobs(pipe2)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_set_event(pipe2, _pipeline_event2, pipe_sync_evt2)); + + esp_gmf_task_cfg_t cfg3 = DEFAULT_ESP_GMF_TASK_CONFIG(); + cfg3.ctx = NULL; + cfg3.cb = NULL; + cfg3.thread.core = 1; + cfg3.thread.prio = 3; + cfg3.name = "channel2"; + esp_gmf_task_handle_t work_task3 = NULL; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_init(&cfg3, &work_task3)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_bind_task(pipe3, work_task3)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_loading_jobs(pipe3)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_set_event(pipe3, _pipeline_event3, pipe_sync_evt3)); + + esp_gmf_task_cfg_t cfg4 = DEFAULT_ESP_GMF_TASK_CONFIG(); + cfg4.ctx = NULL; + cfg4.cb = NULL; + cfg4.thread.core = 0; + cfg4.thread.prio = 3; + cfg4.name = "aud_intlv"; + esp_gmf_task_handle_t work_task4 = NULL; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_init(&cfg4, &work_task4)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_bind_task(pipe4, work_task4)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_loading_jobs(pipe4)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_set_event(pipe4, _pipeline_event4, pipe_sync_evt4)); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_run(pipe1)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_run(pipe2)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_run(pipe3)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_run(pipe4)); + + vTaskDelay(2000 / portTICK_RATE_MS); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_pause(pipe4)); + + vTaskDelay(1000 / portTICK_RATE_MS); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_resume(pipe4)); + + xEventGroupWaitBits(pipe_sync_evt1, PIPELINE_BLOCK_BIT, pdTRUE, pdFALSE, portMAX_DELAY); + xEventGroupWaitBits(pipe_sync_evt2, PIPELINE_BLOCK_BIT, pdTRUE, pdFALSE, portMAX_DELAY); + xEventGroupWaitBits(pipe_sync_evt3, PIPELINE_BLOCK_BIT, pdTRUE, pdFALSE, portMAX_DELAY); + xEventGroupWaitBits(pipe_sync_evt4, PIPELINE_BLOCK_BIT, pdTRUE, pdFALSE, portMAX_DELAY); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_stop(pipe1)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_stop(pipe2)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_stop(pipe3)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_stop(pipe4)); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_deinit(work_task1)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_deinit(work_task2)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_deinit(work_task3)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_deinit(work_task4)); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_destroy(pipe1)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_destroy(pipe2)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_destroy(pipe3)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_destroy(pipe4)); + gmf_unregister_audio_all(pool); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pool_deinit(pool)); + vEventGroupDelete(pipe_sync_evt1); + vEventGroupDelete(pipe_sync_evt2); + vEventGroupDelete(pipe_sync_evt3); + vEventGroupDelete(pipe_sync_evt4); +#ifdef MEDIA_LIB_MEM_TEST + media_lib_stop_mem_trace(); +#endif /* MEDIA_LIB_MEM_TEST */ + esp_gmf_app_teardown_sdcard(sdcard_handle); + esp_gmf_app_teardown_codec_dev(); + vTaskDelay(1000 / portTICK_RATE_MS); + ESP_GMF_MEM_SHOW(TAG); +} + +/*** +// Test mix with two pipeline + ++-------------------------------------+ +| Pipe1: file-->dec-->deinterleave |-+- RB1 -+ ++---------------------+-------------- + | + | +-------+ + +-- | mixer | + | +-------+ ++-------------------------------------+ | +| Pipe1: file-->dec-->deinterleave |-+- RB2 -+ ++---------------------+-------------- + + +***/ +TEST_CASE("Audio mixer Play", "[ESP_GMF_Effects]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + esp_log_level_set("ESP_GMF_PIPELINE", ESP_LOG_DEBUG); + esp_log_level_set("ESP_GMF_POOL", ESP_LOG_DEBUG); + ESP_GMF_MEM_SHOW(TAG); +#ifdef MEDIA_LIB_MEM_TEST + media_lib_add_default_adapter(); +#endif /* MEDIA_LIB_MEM_TEST */ + esp_gmf_app_setup_codec_dev(NULL); + void *sdcard_handle = NULL; + esp_gmf_app_setup_sdcard(&sdcard_handle); + EventGroupHandle_t pipe_sync_evt1 = xEventGroupCreate(); + ESP_GMF_NULL_CHECK(TAG, pipe_sync_evt1, return); + + EventGroupHandle_t pipe_sync_evt2 = xEventGroupCreate(); + ESP_GMF_NULL_CHECK(TAG, pipe_sync_evt2, return); + + EventGroupHandle_t pipe_sync_evt3 = xEventGroupCreate(); + ESP_GMF_NULL_CHECK(TAG, pipe_sync_evt3, return); + + esp_gmf_pool_handle_t pool = NULL; + esp_gmf_pool_init(&pool); + TEST_ASSERT_NOT_NULL(pool); + gmf_register_audio_all(pool); + + ESP_GMF_POOL_SHOW_ITEMS(pool); + esp_gmf_pipeline_handle_t pipe1 = NULL; + const char *name1[] = {"aud_dec", "aud_rate_cvt", "aud_ch_cvt", "aud_bit_cvt"}; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pool_new_pipeline(pool, "io_file", name1, sizeof(name1) / sizeof(char *), NULL, &pipe1)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_set_in_uri(pipe1, "/sdcard/test1.mp3")); + esp_gmf_element_handle_t dec_el = NULL; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_get_el_by_name(pipe1, "aud_dec", &dec_el)); + esp_gmf_info_sound_t info = { + .format_id = ESP_AUDIO_SIMPLE_DEC_TYPE_MP3, + }; + esp_gmf_audio_dec_reconfig_by_sound_info(dec_el, &info); + + esp_gmf_pipeline_handle_t pipe2 = NULL; + const char *name2[] = {"aud_dec", "aud_rate_cvt", "aud_ch_cvt", "aud_bit_cvt"}; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pool_new_pipeline(pool, "io_file", name2, sizeof(name2) / sizeof(char *), NULL, &pipe2)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_set_in_uri(pipe2, "/sdcard/test.mp3")); + dec_el = NULL; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_get_el_by_name(pipe2, "aud_dec", &dec_el)); + esp_gmf_audio_dec_reconfig_by_sound_info(dec_el, &info); + + esp_gmf_pipeline_handle_t pipe3 = NULL; + const char *name3[] = {"aud_mixer"}; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pool_new_pipeline(pool, NULL, name3, sizeof(name3) / sizeof(char *), "io_codec_dev", &pipe3)); + gmf_setup_pipeline_out_dev(pipe3); + // create rb + esp_gmf_db_handle_t db = NULL; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_db_new_ringbuf(10, 1024, &db)); + esp_gmf_db_handle_t db2 = NULL; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_db_new_ringbuf(10, 1024, &db2)); + + esp_gmf_port_handle_t out_port = NEW_ESP_GMF_PORT_OUT_BYTE(esp_gmf_db_acquire_write, esp_gmf_db_release_write, esp_gmf_db_deinit, db, + ESP_GMF_PORT_PAYLOAD_LEN_DEFAULT, 300); + esp_gmf_port_handle_t in_port = NEW_ESP_GMF_PORT_IN_BYTE(esp_gmf_db_acquire_read, esp_gmf_db_release_read, esp_gmf_db_deinit, db, + ESP_GMF_PORT_PAYLOAD_LEN_DEFAULT, 300); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_connect_pipe(pipe1, "aud_bit_cvt", out_port, pipe3, "aud_mixer", in_port)); + + out_port = NEW_ESP_GMF_PORT_OUT_BYTE(esp_gmf_db_acquire_write, esp_gmf_db_release_write, esp_gmf_db_deinit, db2, + ESP_GMF_PORT_PAYLOAD_LEN_DEFAULT, 300); + in_port = NEW_ESP_GMF_PORT_IN_BYTE(esp_gmf_db_acquire_read, esp_gmf_db_release_read, esp_gmf_db_deinit, db2, + ESP_GMF_PORT_PAYLOAD_LEN_DEFAULT, 300); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_connect_pipe(pipe2, "aud_bit_cvt", out_port, pipe3, "aud_mixer", in_port)); + + esp_gmf_task_cfg_t cfg1 = DEFAULT_ESP_GMF_TASK_CONFIG(); + cfg1.ctx = NULL; + cfg1.cb = NULL; + cfg1.thread.core = 1; + cfg1.thread.prio = 10; + cfg1.name = "stream1"; + esp_gmf_task_handle_t work_task1 = NULL; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_init(&cfg1, &work_task1)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_bind_task(pipe1, work_task1)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_loading_jobs(pipe1)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_set_event(pipe1, _pipeline_event, pipe_sync_evt1)); + + esp_gmf_task_cfg_t cfg2 = DEFAULT_ESP_GMF_TASK_CONFIG(); + cfg2.thread.core = 0; + cfg2.thread.prio = 10; + cfg2.name = "stream2"; + esp_gmf_task_handle_t work_task2 = NULL; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_init(&cfg2, &work_task2)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_bind_task(pipe2, work_task2)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_loading_jobs(pipe2)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_set_event(pipe2, _pipeline_event2, pipe_sync_evt2)); + + esp_gmf_task_cfg_t cfg3 = DEFAULT_ESP_GMF_TASK_CONFIG(); + cfg3.ctx = NULL; + cfg3.cb = NULL; + cfg3.thread.core = 0; + cfg3.thread.prio = 5; + cfg3.name = "mix_process"; + esp_gmf_task_handle_t work_task3 = NULL; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_init(&cfg3, &work_task3)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_bind_task(pipe3, work_task3)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_loading_jobs(pipe3)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_set_event(pipe3, _pipeline_event3, pipe_sync_evt3)); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_run(pipe1)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_run(pipe2)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_run(pipe3)); + esp_gmf_element_handle_t mixer_hd = NULL; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_get_el_by_name(pipe3, "aud_mixer", &mixer_hd)); + xEventGroupWaitBits(pipe_sync_evt3, PIPELINE_BLOCK_RUN_BIT, pdTRUE, pdFALSE, portMAX_DELAY); + // set mode + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_mixer_set_mode(mixer_hd, 0, ESP_AE_MIXER_MODE_FADE_UPWARD)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_mixer_set_mode(mixer_hd, 1, ESP_AE_MIXER_MODE_FADE_DOWNWARD)); + + xEventGroupWaitBits(pipe_sync_evt1, PIPELINE_BLOCK_BIT, pdTRUE, pdFALSE, portMAX_DELAY); + xEventGroupWaitBits(pipe_sync_evt2, PIPELINE_BLOCK_BIT, pdTRUE, pdFALSE, portMAX_DELAY); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_stop(pipe2)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_stop(pipe1)); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_stop(pipe3)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_deinit(work_task1)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_destroy(pipe1)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_deinit(work_task2)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_destroy(pipe2)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_deinit(work_task3)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_destroy(pipe3)); + gmf_unregister_audio_all(pool); + esp_gmf_pool_deinit(pool); + vEventGroupDelete(pipe_sync_evt1); + vEventGroupDelete(pipe_sync_evt2); + vEventGroupDelete(pipe_sync_evt3); +#ifdef MEDIA_LIB_MEM_TEST + media_lib_stop_mem_trace(); +#endif /* MEDIA_LIB_MEM_TEST */ + esp_gmf_app_teardown_sdcard(sdcard_handle); + esp_gmf_app_teardown_codec_dev(); + vTaskDelay(1000 / portTICK_RATE_MS); + ESP_GMF_MEM_SHOW(TAG); +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/main/elements/gmf_audio_el_com.c b/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/main/elements/gmf_audio_el_com.c new file mode 100644 index 0000000..abbacc0 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/main/elements/gmf_audio_el_com.c @@ -0,0 +1,487 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "unity.h" +#include "esp_gmf_audio_methods_def.h" +#include "esp_gmf_method_helper.h" +#include "esp_gmf_eq.h" +#include "esp_gmf_fade.h" +#include "esp_gmf_mixer.h" +#include "esp_gmf_audio_enc.h" +#include "esp_gmf_audio_dec.h" +#include "esp_gmf_audio_param.h" +#include "esp_gmf_args_desc.h" +#include "gmf_audio_el_com.h" +#include "esp_audio_enc_default.h" +#include "esp_audio_dec_default.h" +#include "esp_audio_simple_dec_default.h" + +#define PREPARE_METHOD() \ + esp_gmf_method_exec_ctx_t exec_ctx = {}; \ + const esp_gmf_method_t *method_head = NULL; \ + esp_gmf_element_get_method(self, &method_head); \ + esp_gmf_err_t ret = esp_gmf_method_prepare_exec_ctx(method_head, method_name, &exec_ctx); \ + if (ret != ESP_GMF_ERR_OK) { \ + return ret; \ + } + +#define SET_METHOD_ARG(arg_name, value) \ + esp_gmf_args_set_value(exec_ctx.method->args_desc, arg_name, exec_ctx.exec_buf, (uint8_t *)&value, sizeof(value)); + +#define EXEC_METHOD() \ + ret = exec_ctx.method->func(self, exec_ctx.method->args_desc, exec_ctx.exec_buf, exec_ctx.buf_size); + +#define GET_METHOD_ARG(arg_name, value) \ + esp_gmf_args_extract_value(exec_ctx.method->args_desc, arg_name, exec_ctx.exec_buf, exec_ctx.buf_size, (uint32_t *)value); + +#define RELEASE_METHOD() \ + esp_gmf_method_release_exec_ctx(&exec_ctx); + +// Audio effect parameter setting methods +static esp_gmf_err_t audio_el_test_set_eq_filter_para(esp_gmf_element_handle_t self, uint8_t idx, esp_ae_eq_filter_para_t *para) +{ + const char *method_name = AMETHOD(EQ, SET_PARA); + PREPARE_METHOD(); + + SET_METHOD_ARG(AMETHOD_ARG(EQ, SET_PARA, IDX), idx); + SET_METHOD_ARG(AMETHOD_ARG(EQ, SET_PARA, PARA), *para); + EXEC_METHOD(); + RELEASE_METHOD(); + return ret; +} + +static esp_gmf_err_t audio_el_test_get_eq_filter_para(esp_gmf_element_handle_t self, uint8_t idx, esp_ae_eq_filter_para_t *para) +{ + const char *method_name = AMETHOD(EQ, GET_PARA); + PREPARE_METHOD(); + + SET_METHOD_ARG(AMETHOD_ARG(EQ, GET_PARA, IDX), idx); + EXEC_METHOD(); + GET_METHOD_ARG(AMETHOD_ARG(EQ, GET_PARA, PARA), para); + RELEASE_METHOD(); + return ret; +} + +static esp_gmf_err_t audio_el_test_enable_eq_filter(esp_gmf_element_handle_t self, uint8_t idx, bool enable) +{ + const char *method_name = AMETHOD(EQ, ENABLE_FILTER); + PREPARE_METHOD(); + + SET_METHOD_ARG(AMETHOD_ARG(EQ, ENABLE_FILTER, IDX), idx); + SET_METHOD_ARG(AMETHOD_ARG(EQ, ENABLE_FILTER, ENABLE), enable); + EXEC_METHOD(); + RELEASE_METHOD(); + return ret; +} + +static esp_gmf_err_t audio_el_test_get_encoder_frame_size(esp_gmf_element_handle_t self, uint32_t *in_size, uint32_t *out_size) +{ + const char *method_name = AMETHOD(ENCODER, GET_FRAME_SZ); + PREPARE_METHOD(); + EXEC_METHOD(); + GET_METHOD_ARG(AMETHOD_ARG(ENCODER, GET_FRAME_SZ, INSIZE), in_size); + GET_METHOD_ARG(AMETHOD_ARG(ENCODER, GET_FRAME_SZ, OUTSIZE), out_size); + RELEASE_METHOD(); + return ret; +} + +static esp_gmf_err_t audio_el_test_set_encoder_bitrate(esp_gmf_element_handle_t self, uint32_t bitrate) +{ + const char *method_name = AMETHOD(ENCODER, SET_BITRATE); + PREPARE_METHOD(); + SET_METHOD_ARG(AMETHOD_ARG(ENCODER, SET_BITRATE, BITRATE), bitrate); + EXEC_METHOD(); + RELEASE_METHOD(); + return ret; +} + +static esp_gmf_err_t audio_el_test_get_encoder_bitrate(esp_gmf_element_handle_t self, uint32_t *bitrate) +{ + const char *method_name = AMETHOD(ENCODER, GET_BITRATE); + PREPARE_METHOD(); + EXEC_METHOD(); + GET_METHOD_ARG(AMETHOD_ARG(ENCODER, GET_BITRATE, BITRATE), bitrate); + RELEASE_METHOD(); + return ret; +} + +static esp_gmf_err_t audio_el_test_reconfig_encoder(esp_gmf_element_handle_t self, esp_audio_enc_config_t *cfg) +{ + const char *method_name = AMETHOD(ENCODER, RECONFIG); + PREPARE_METHOD(); + SET_METHOD_ARG(AMETHOD_ARG(ENCODER, RECONFIG, CFG), *cfg); + EXEC_METHOD(); + RELEASE_METHOD(); + return ret; +} + +static esp_gmf_err_t audio_el_test_reconfig_encoder_by_sound_info(esp_gmf_element_handle_t self, esp_gmf_info_sound_t *info) +{ + const char *method_name = AMETHOD(ENCODER, RECONFIG_BY_SND_INFO); + PREPARE_METHOD(); + SET_METHOD_ARG(AMETHOD_ARG(ENCODER, RECONFIG_BY_SND_INFO, INFO), *info); + EXEC_METHOD(); + RELEASE_METHOD(); + return ret; +} + +static esp_gmf_err_t audio_el_test_reconfig_decoder(esp_gmf_element_handle_t self, esp_audio_simple_dec_cfg_t *cfg) +{ + const char *method_name = AMETHOD(DECODER, RECONFIG); + PREPARE_METHOD(); + SET_METHOD_ARG(AMETHOD_ARG(DECODER, RECONFIG, CFG), *cfg); + EXEC_METHOD(); + RELEASE_METHOD(); + return ret; +} + +static esp_gmf_err_t audio_el_test_reconfig_decoder_by_sound_info(esp_gmf_element_handle_t self, esp_gmf_info_sound_t *info) +{ + const char *method_name = AMETHOD(DECODER, RECONFIG_BY_SND_INFO); + PREPARE_METHOD(); + SET_METHOD_ARG(AMETHOD_ARG(DECODER, RECONFIG_BY_SND_INFO, INFO), *info); + EXEC_METHOD(); + RELEASE_METHOD(); + return ret; +} + +static esp_gmf_err_t audio_el_test_set_fade_mode(esp_gmf_element_handle_t self, esp_ae_fade_mode_t mode) +{ + const char *method_name = AMETHOD(FADE, SET_MODE); + PREPARE_METHOD(); + SET_METHOD_ARG(AMETHOD_ARG(FADE, SET_MODE, MODE), mode); + EXEC_METHOD(); + RELEASE_METHOD(); + return ret; +} + +static esp_gmf_err_t audio_el_test_get_fade_mode(esp_gmf_element_handle_t self, esp_ae_fade_mode_t *mode) +{ + const char *method_name = AMETHOD(FADE, GET_MODE); + PREPARE_METHOD(); + EXEC_METHOD(); + GET_METHOD_ARG(AMETHOD_ARG(FADE, GET_MODE, MODE), mode); + RELEASE_METHOD(); + return ret; +} + +static esp_gmf_err_t audio_el_test_reset_fade_weight(esp_gmf_element_handle_t self) +{ + const char *method_name = AMETHOD(FADE, RESET_WEIGHT); + PREPARE_METHOD(); + EXEC_METHOD(); + RELEASE_METHOD(); + return ret; +} + +static esp_gmf_err_t audio_el_test_set_mixer_mode(esp_gmf_element_handle_t self, uint8_t idx, esp_ae_mixer_mode_t mode) +{ + const char *method_name = AMETHOD(MIXER, SET_MODE); + PREPARE_METHOD(); + SET_METHOD_ARG(AMETHOD_ARG(MIXER, SET_MODE, IDX), idx); + SET_METHOD_ARG(AMETHOD_ARG(MIXER, SET_MODE, MODE), mode); + EXEC_METHOD(); + RELEASE_METHOD(); + return ret; +} + +static esp_gmf_err_t audio_el_test_set_mixer_info(esp_gmf_element_handle_t self, uint32_t sample_rate, uint32_t channels, uint32_t bits) +{ + const char *method_name = AMETHOD(MIXER, SET_INFO); + PREPARE_METHOD(); + SET_METHOD_ARG(AMETHOD_ARG(MIXER, SET_INFO, RATE), sample_rate); + SET_METHOD_ARG(AMETHOD_ARG(MIXER, SET_INFO, CH), channels); + SET_METHOD_ARG(AMETHOD_ARG(MIXER, SET_INFO, BITS), bits); + EXEC_METHOD(); + RELEASE_METHOD(); + return ret; +} + +static esp_gmf_err_t audio_el_test_set_sonic_speed(esp_gmf_element_handle_t self, float speed) +{ + const char *method_name = AMETHOD(SONIC, SET_SPEED); + PREPARE_METHOD(); + SET_METHOD_ARG(AMETHOD_ARG(SONIC, SET_SPEED, SPEED), speed); + EXEC_METHOD(); + RELEASE_METHOD(); + return ret; +} + +static esp_gmf_err_t audio_el_test_get_sonic_speed(esp_gmf_element_handle_t self, float *speed) +{ + const char *method_name = AMETHOD(SONIC, GET_SPEED); + PREPARE_METHOD(); + EXEC_METHOD(); + GET_METHOD_ARG(AMETHOD_ARG(SONIC, GET_SPEED, SPEED), speed); + RELEASE_METHOD(); + return ret; +} + +static esp_gmf_err_t audio_el_test_set_sonic_pitch(esp_gmf_element_handle_t self, float pitch) +{ + const char *method_name = AMETHOD(SONIC, SET_PITCH); + PREPARE_METHOD(); + SET_METHOD_ARG(AMETHOD_ARG(SONIC, SET_PITCH, PITCH), pitch); + EXEC_METHOD(); + RELEASE_METHOD(); + return ret; +} + +static esp_gmf_err_t audio_el_test_get_sonic_pitch(esp_gmf_element_handle_t self, float *pitch) +{ + const char *method_name = AMETHOD(SONIC, GET_PITCH); + PREPARE_METHOD(); + EXEC_METHOD(); + GET_METHOD_ARG(AMETHOD_ARG(SONIC, GET_PITCH, PITCH), pitch); + RELEASE_METHOD(); + return ret; +} + +static esp_gmf_err_t audio_el_test_set_alc_gain(esp_gmf_element_handle_t self, uint8_t idx, int8_t gain) +{ + const char *method_name = AMETHOD(ALC, SET_GAIN); + PREPARE_METHOD(); + SET_METHOD_ARG(AMETHOD_ARG(ALC, SET_GAIN, IDX), idx); + SET_METHOD_ARG(AMETHOD_ARG(ALC, SET_GAIN, GAIN), gain); + EXEC_METHOD(); + RELEASE_METHOD(); + return ret; +} + +static esp_gmf_err_t audio_el_test_get_alc_gain(esp_gmf_element_handle_t self, uint8_t idx, int8_t *gain) +{ + const char *method_name = AMETHOD(ALC, GET_GAIN); + PREPARE_METHOD(); + SET_METHOD_ARG(AMETHOD_ARG(ALC, GET_GAIN, IDX), idx); + EXEC_METHOD(); + GET_METHOD_ARG(AMETHOD_ARG(ALC, GET_GAIN, GAIN), gain); + RELEASE_METHOD(); + return ret; +} + +void encoder_config_callback(esp_gmf_element_handle_t el, void *ctx) +{ + audio_el_res_t *res = (audio_el_res_t *)ctx; + esp_audio_enc_config_t enc_cfg = {0}; + uint32_t bitrate = 0; + esp_gmf_event_state_t event_state = 0; + esp_gmf_element_get_state(el, &event_state); + uint32_t in_size = 0, out_size = 0; + esp_gmf_err_t ret = ESP_GMF_ERR_OK; + if (event_state >= ESP_GMF_EVENT_STATE_OPENING) { + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, audio_el_test_get_encoder_frame_size(el, &in_size, &out_size)); + ret = esp_gmf_audio_enc_reconfig(el, &enc_cfg); + esp_gmf_element_get_state(el, &event_state); + if (event_state >= ESP_GMF_EVENT_STATE_OPENING) { + TEST_ASSERT_EQUAL(ESP_GMF_ERR_FAIL, ret); + } + ret = esp_gmf_audio_enc_reconfig_by_sound_info(el, &res->in_inst[0].src_info); + esp_gmf_element_get_state(el, &event_state); + if (event_state >= ESP_GMF_EVENT_STATE_OPENING) { + TEST_ASSERT_EQUAL(ESP_GMF_ERR_FAIL, ret); + } + return; + } + // Test AMRNB encoder configuration + esp_amrnb_enc_config_t new_amrnb_cfg = { + .sample_rate = 8000, + .channel = 1, + .bits_per_sample = 16, + .dtx_enable = true, + .bitrate_mode = ESP_AMRNB_ENC_BITRATE_MR122, + .no_file_header = false}; + enc_cfg.type = ESP_AUDIO_TYPE_AMRNB; + enc_cfg.cfg = &new_amrnb_cfg; + enc_cfg.cfg_sz = sizeof(esp_amrnb_enc_config_t); + ret = audio_el_test_reconfig_encoder(el, &enc_cfg); + esp_gmf_element_get_state(el, &event_state); + if (event_state < ESP_GMF_EVENT_STATE_OPENING) { + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, ret); + // Verify AMRNB configuration + esp_audio_enc_config_t *get_cfg = OBJ_GET_CFG(el); + TEST_ASSERT_EQUAL(ESP_AUDIO_TYPE_AMRNB, get_cfg->type); + esp_amrnb_enc_config_t *amrnb_cfg = (esp_amrnb_enc_config_t *)get_cfg->cfg; + TEST_ASSERT_EQUAL(new_amrnb_cfg.sample_rate, amrnb_cfg->sample_rate); + TEST_ASSERT_EQUAL(new_amrnb_cfg.channel, amrnb_cfg->channel); + TEST_ASSERT_EQUAL(new_amrnb_cfg.bits_per_sample, amrnb_cfg->bits_per_sample); + TEST_ASSERT_EQUAL(new_amrnb_cfg.dtx_enable, amrnb_cfg->dtx_enable); + TEST_ASSERT_EQUAL(new_amrnb_cfg.bitrate_mode, amrnb_cfg->bitrate_mode); + TEST_ASSERT_EQUAL(new_amrnb_cfg.no_file_header, amrnb_cfg->no_file_header); + // Test bitrate interface + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, audio_el_test_set_encoder_bitrate(el, 12200)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, audio_el_test_get_encoder_bitrate(el, &bitrate)); + TEST_ASSERT_EQUAL(12200, bitrate); + } + // Test reconfiguration using sound info + ret = audio_el_test_reconfig_encoder_by_sound_info(el, &res->in_inst[0].src_info); + esp_gmf_element_get_state(el, &event_state); + if (event_state < ESP_GMF_EVENT_STATE_OPENING) { + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, ret); + // Verify AMRNB configuration + esp_audio_enc_config_t *get_cfg = OBJ_GET_CFG(el); + TEST_ASSERT_EQUAL(res->in_inst[0].src_info.format_id, get_cfg->type); + } + // Verify frame size after reconfiguration + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, audio_el_test_get_encoder_frame_size(el, &in_size, &out_size)); +} + +void decoder_config_callback(esp_gmf_element_handle_t el, void *ctx) +{ + audio_el_res_t *res = (audio_el_res_t *)ctx; + esp_audio_simple_dec_cfg_t dec_cfg = {0}; + esp_gmf_event_state_t event_state = 0; + esp_gmf_element_get_state(el, &event_state); + esp_gmf_err_t ret = ESP_GMF_ERR_OK; + if (event_state >= ESP_GMF_EVENT_STATE_OPENING) { + ret = audio_el_test_reconfig_decoder(el, &dec_cfg); + esp_gmf_element_get_state(el, &event_state); + if (event_state >= ESP_GMF_EVENT_STATE_OPENING) { + TEST_ASSERT_EQUAL(ESP_GMF_ERR_FAIL, ret); + } + ret = audio_el_test_reconfig_decoder_by_sound_info(el, &res->in_inst[0].src_info); + esp_gmf_element_get_state(el, &event_state); + if (event_state >= ESP_GMF_EVENT_STATE_OPENING) { + TEST_ASSERT_EQUAL(ESP_GMF_ERR_FAIL, ret); + } + return; + } + // Test LC3 decoder configuration + esp_lc3_dec_cfg_t new_lc3_cfg = { + .sample_rate = 48000, + .channel = 2, + .bits_per_sample = 16, + .frame_dms = 100, + .is_cbr = 1, + .len_prefixed = 1, + .enable_plc = 1, + .nbyte = 120}; + dec_cfg.dec_type = ESP_AUDIO_TYPE_LC3; + dec_cfg.dec_cfg = &new_lc3_cfg; + dec_cfg.cfg_size = sizeof(esp_lc3_dec_cfg_t); + ret = audio_el_test_reconfig_decoder(el, &dec_cfg); + esp_gmf_element_get_state(el, &event_state); + if (event_state < ESP_GMF_EVENT_STATE_OPENING) { + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, ret); + // Verify LC3 configuration + esp_audio_simple_dec_cfg_t *get_cfg = OBJ_GET_CFG(el); + TEST_ASSERT_EQUAL(ESP_AUDIO_TYPE_LC3, get_cfg->dec_type); + esp_lc3_dec_cfg_t *lc3_cfg = (esp_lc3_dec_cfg_t *)get_cfg->dec_cfg; + TEST_ASSERT_EQUAL(new_lc3_cfg.sample_rate, lc3_cfg->sample_rate); + TEST_ASSERT_EQUAL(new_lc3_cfg.channel, lc3_cfg->channel); + TEST_ASSERT_EQUAL(new_lc3_cfg.bits_per_sample, lc3_cfg->bits_per_sample); + TEST_ASSERT_EQUAL(new_lc3_cfg.frame_dms, lc3_cfg->frame_dms); + TEST_ASSERT_EQUAL(new_lc3_cfg.is_cbr, lc3_cfg->is_cbr); + TEST_ASSERT_EQUAL(new_lc3_cfg.len_prefixed, lc3_cfg->len_prefixed); + TEST_ASSERT_EQUAL(new_lc3_cfg.enable_plc, lc3_cfg->enable_plc); + } + // Test FLAC decoder configuration using sound info + esp_gmf_info_sound_t dec_info = { + .format_id = ESP_AUDIO_SIMPLE_DEC_TYPE_FLAC, + .sample_rates = 48000, + .channels = 2, + .bits = 16, + .bitrate = 0}; + ret = audio_el_test_reconfig_decoder_by_sound_info(el, &dec_info); + esp_gmf_element_get_state(el, &event_state); + if (event_state < ESP_GMF_EVENT_STATE_OPENING) { + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, ret); + esp_audio_simple_dec_cfg_t *get_cfg = OBJ_GET_CFG(el); + TEST_ASSERT_EQUAL(ESP_AUDIO_SIMPLE_DEC_TYPE_FLAC, get_cfg->dec_type); + } +} + +void eq_config_callback(esp_gmf_element_handle_t self, void *ctx) +{ + esp_ae_eq_filter_para_t filter_para[] = { + {.fc = 100, + .gain = 6.0f, + .q = 1.0f, + .filter_type = ESP_AE_EQ_FILTER_LOW_SHELF}, + {.fc = 1000, + .gain = -3.0f, + .q = 2.0f, + .filter_type = ESP_AE_EQ_FILTER_PEAK}, + {.fc = 10000, + .gain = 3.0f, + .q = 1.0f, + .filter_type = ESP_AE_EQ_FILTER_HIGH_SHELF}}; + esp_ae_eq_filter_para_t read_para = {0}; + for (int i = 0; i < sizeof(filter_para) / sizeof(filter_para[0]); i++) { + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, audio_el_test_set_eq_filter_para(self, i, &filter_para[i])); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, audio_el_test_get_eq_filter_para(self, i, &read_para)); + TEST_ASSERT_EQUAL(filter_para[i].fc, read_para.fc); + TEST_ASSERT_EQUAL_FLOAT(filter_para[i].gain, read_para.gain); + TEST_ASSERT_EQUAL_FLOAT(filter_para[i].q, read_para.q); + TEST_ASSERT_EQUAL(filter_para[i].filter_type, read_para.filter_type); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, audio_el_test_enable_eq_filter(self, i, true)); + } +} + +void fade_config_callback(esp_gmf_element_handle_t self, void *ctx) +{ + esp_ae_fade_mode_t mode = 0; + // Test fade mode setting and getting + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, audio_el_test_set_fade_mode(self, ESP_AE_FADE_MODE_FADE_IN)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, audio_el_test_get_fade_mode(self, &mode)); + TEST_ASSERT_EQUAL(ESP_AE_FADE_MODE_FADE_IN, mode); + // Test fade weight reset + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, audio_el_test_reset_fade_weight(self)); +} + +void mixer_config_callback(esp_gmf_element_handle_t self, void *ctx) +{ + // Test mixer mode setting for each channel + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, audio_el_test_set_mixer_mode(self, 0, ESP_AE_MIXER_MODE_FADE_UPWARD)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, audio_el_test_set_mixer_mode(self, 1, ESP_AE_MIXER_MODE_FADE_DOWNWARD)); + // Test mixer info setting + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, audio_el_test_set_mixer_info(self, 48000, 2, 16)); +} + +void sonic_config_callback(esp_gmf_element_handle_t self, void *ctx) +{ + float speed = 0.0f; + float pitch = 0.0f; + // Test speed setting and getting + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, audio_el_test_set_sonic_speed(self, 1.5f)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, audio_el_test_get_sonic_speed(self, &speed)); + TEST_ASSERT_EQUAL_FLOAT(1.5f, speed); + // Test pitch setting and getting + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, audio_el_test_set_sonic_pitch(self, 1.2f)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, audio_el_test_get_sonic_pitch(self, &pitch)); + TEST_ASSERT_EQUAL_FLOAT(1.2f, pitch); +} + +void alc_config_callback(esp_gmf_element_handle_t self, void *ctx) +{ + audio_el_res_t *res = (audio_el_res_t *)ctx; + int8_t gain = 0; + for (int i = 0; i < res->in_inst[0].src_info.channels; i++) { + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, audio_el_test_set_alc_gain(self, i, 2)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, audio_el_test_get_alc_gain(self, i, &gain)); + TEST_ASSERT_EQUAL(2, gain); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, audio_el_test_set_alc_gain(self, i, 1)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, audio_el_test_get_alc_gain(self, i, &gain)); + TEST_ASSERT_EQUAL(1, gain); + } +} + +void bit_cvt_config_callback(esp_gmf_element_handle_t self, void *ctx) +{ + audio_el_res_t *res = (audio_el_res_t *)ctx; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_audio_param_set_dest_bits(self, res->out_inst[0].out_info.bits)); +} + +void ch_cvt_config_callback(esp_gmf_element_handle_t self, void *ctx) +{ + audio_el_res_t *res = (audio_el_res_t *)ctx; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_audio_param_set_dest_ch(self, res->out_inst[0].out_info.channels)); +} + +void rate_cvt_config_callback(esp_gmf_element_handle_t self, void *ctx) +{ + audio_el_res_t *res = (audio_el_res_t *)ctx; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_audio_param_set_dest_rate(self, res->out_inst[0].out_info.sample_rates)); +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/main/elements/gmf_audio_el_com.h b/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/main/elements/gmf_audio_el_com.h new file mode 100644 index 0000000..76382f7 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/main/elements/gmf_audio_el_com.h @@ -0,0 +1,99 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _GMF_AUDIO_EL_COM_H_ +#define _GMF_AUDIO_EL_COM_H_ + +#include "esp_gmf_element.h" +#include "esp_gmf_pool.h" +#include "esp_gmf_audio_param.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/queue.h" +#include "freertos/event_groups.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +typedef struct { + uint8_t *data; + size_t size; + size_t buf_length; + bool is_released; + bool is_done; +} sweep_data_t; + +typedef struct { + uint32_t in_frame_count; + bool is_done; + uint32_t src_codec; + esp_gmf_info_sound_t src_info; + uint8_t *src_data; + uint32_t src_size; + uint32_t in_acquire_count; + uint32_t in_release_count; + QueueHandle_t sweep_queue1; + QueueHandle_t sweep_queue2; + TaskHandle_t task_hd; + esp_gmf_pipeline_handle_t pipeline; + bool change_info; + bool is_sweep_signal; +} audio_el_in_test_t; + +typedef struct { + uint32_t out_codec; + esp_gmf_info_sound_t out_info; + uint8_t *out_data; + uint32_t out_size; + uint32_t out_frame_count; + uint32_t out_max_size; + bool no_need_free; + uint32_t out_acquire_count; + uint32_t out_release_count; +} audio_el_out_test_t; + +typedef struct { + esp_gmf_pool_handle_t pool; + esp_gmf_pipeline_handle_t pipe; + esp_gmf_task_handle_t task; + esp_gmf_element_handle_t current_hd[5]; + int32_t el_cnt; + audio_el_in_test_t *in_inst; + audio_el_out_test_t *out_inst; + uint32_t in_port_num; + uint32_t out_port_num; + TaskHandle_t cfg_task_hd; + EventGroupHandle_t pipe_sync_evt; + void (*config_func)(esp_gmf_element_handle_t, void *); + bool is_end; +} audio_el_res_t; + +void eq_config_callback(esp_gmf_element_handle_t self, void *ctx); + +void fade_config_callback(esp_gmf_element_handle_t self, void *ctx); + +void mixer_config_callback(esp_gmf_element_handle_t self, void *ctx); + +void encoder_config_callback(esp_gmf_element_handle_t self, void *ctx); + +void decoder_config_callback(esp_gmf_element_handle_t self, void *ctx); + +void sonic_config_callback(esp_gmf_element_handle_t self, void *ctx); + +void alc_config_callback(esp_gmf_element_handle_t self, void *ctx); + +void bit_cvt_config_callback(esp_gmf_element_handle_t self, void *ctx); + +void ch_cvt_config_callback(esp_gmf_element_handle_t self, void *ctx); + +void rate_cvt_config_callback(esp_gmf_element_handle_t self, void *ctx); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* _GMF_AUDIO_EL_COM_H_ */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/main/elements/gmf_audio_el_test.c b/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/main/elements/gmf_audio_el_test.c new file mode 100644 index 0000000..8a027b3 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/main/elements/gmf_audio_el_test.c @@ -0,0 +1,1040 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @brief Common Test Items for Audio Elements + * + * This test suite covers the following common test scenarios for audio elements: + * 1. Element Run State Testing: + * - Tests element behavior in different runtime scenarios such as: + * - normal run to finish + * - normal run to stop + * - error on open + * - error on process + * - run with multiple tasks + * + * 2. Method Interface Testing: + * - Tests element method interfaces in different elementstates + * + * 3. Lifecycle Interface Testing: + * - Tests init, open, process, close, and destroy interfaces + * - Verifies memory leak prevention + * + * 4. Acquire/Release Consistency Check: + * - Ensures that every acquire call is matched with a corresponding release, preventing resource leaks or unexpected deadlocks + * + * 5. Capability Testing: + * - Tests retrieving element tags through capabilities + * + * 6. Runtime Report Info Update Test: + * - Tests the functionality of updating sound info dynamically during runtime via report info + * + * 7. Decoder Testing: + * - Tests different input sizes to ensure coverage of all process branches + * + * 8. Encoder Testing: + * - Tests various input sizes and types to ensures complete coverage of encoder process branches + */ + +#include +#include +#include +#include "unity.h" +#include "esp_log.h" +#include "esp_heap_caps.h" +#include "esp_gmf_caps_def.h" +#include "esp_gmf_method_helper.h" +#include "gmf_loader_setup_defaults.h" +#include "esp_gmf_audio_enc.h" +#include "esp_gmf_audio_dec.h" +#include "esp_gmf_bit_cvt.h" +#include "esp_gmf_ch_cvt.h" +#include "esp_gmf_eq.h" +#include "esp_gmf_alc.h" +#include "esp_gmf_fade.h" +#include "esp_gmf_mixer.h" +#include "esp_gmf_rate_cvt.h" +#include "esp_gmf_sonic.h" +#include "esp_gmf_interleave.h" +#include "esp_gmf_deinterleave.h" +#include "esp_gmf_audio_enc.h" +#include "esp_gmf_audio_dec.h" +#include "esp_gmf_audio_methods_def.h" +#include "gmf_audio_el_com.h" +#include "gmf_audio_play_com.h" +#include "gmf_audio_pattern.h" + +#define TAG "AUD_EL_TEST" +#define TEST_AUDIO_SAMPLE_RATE (48000) +#define TEST_AUDIO_CHANNELS (2) +#define TEST_AUDIO_BITS (16) +#define TEST_AUDIO_BITRATE (96000) +#define TEST_AUDIO_ALIGNMENT (16) +#define TEST_AUDIO_FRAME_SIZE (1024) +#define GET_EL_NUM(arr) (sizeof(arr) / sizeof(char *)) +#define AUDIO_EL_MAX_STACK_SIZE (40 * 1024) +#define SWEEP_QUEUE_SIZE 4 +#define PIPELINE_BLOCK_BIT BIT(0) + +extern const uint8_t test_flac_start[] asm("_binary_test_48000hz_16bit_2ch_5000ms_flac_start"); +extern const uint8_t test_flac_end[] asm("_binary_test_48000hz_16bit_2ch_5000ms_flac_end"); + +#define DEFAULT_SINGLE_IN_SINGLE_OUT_CONFIG() { \ + .in_port_num = 1, \ + .out_port_num = 1, \ + .el_cnt = 1, \ + .src_size = 1024, \ +} + +#define DEFAULT_SINGLE_IN_MULTI_OUT_CONFIG() { \ + .in_port_num = 1, \ + .out_port_num = 2, \ + .el_cnt = 1, \ + .src_size = 1024, \ +} + +#define DEFAULT_MULTI_IN_SINGLE_OUT_CONFIG() { \ + .in_port_num = 2, \ + .out_port_num = 1, \ + .el_cnt = 1, \ + .src_size = 1024, \ +} + +typedef struct { + uint32_t in_port_num; + uint32_t out_port_num; + uint64_t *caps_cc; + uint8_t el_cnt; + uint32_t src_size; + void (*config_func)(esp_gmf_element_handle_t, void *); +} audio_el_res_cfg_t; + +static void SAFE_FREE(void *ptr) +{ + if (ptr) { + free(ptr); + ptr = NULL; + } +} + +static esp_err_t audio_el_pipeline_event(esp_gmf_event_pkt_t *event, void *ctx) +{ + // The warning messages are used to make the content more noticeable. + ESP_LOGW(TAG, "CB: RECV Pipeline EVT: el:%s-%p, type:%x, sub:%s, payload:%p, size:%d,%p", + "OBJ_GET_TAG(event->from)", event->from, event->type, esp_gmf_event_get_state_str(event->sub), + event->payload, event->payload_size, ctx); + if ((event->sub == ESP_GMF_EVENT_STATE_STOPPED) + || (event->sub == ESP_GMF_EVENT_STATE_FINISHED) + || (event->sub == ESP_GMF_EVENT_STATE_ERROR)) { + if (ctx) { + xEventGroupSetBits((EventGroupHandle_t)ctx, PIPELINE_BLOCK_BIT); + } + } + return 0; +} + +// Test different audio elements configuration in different states +static void audio_el_cfg_task(void *pvParameters) +{ + audio_el_res_t *res = (audio_el_res_t *)pvParameters; + while (!res->is_end) { + if (res->config_func) { + res->config_func(res->current_hd[0], res); + } + vTaskDelay(500 / portTICK_RATE_MS); + } + ESP_LOGI(TAG, "Cfg task end"); + res->cfg_task_hd = NULL; + vTaskDelete(NULL); +} + +static void audio_el_data_gene_task(void *pvParameters) +{ + audio_el_in_test_t *in_inst = pvParameters; + bool change_params = in_inst->change_info; + esp_gmf_info_sound_t *sound_info = &(in_inst->src_info); + bool is_done = false; + sweep_data_t data_packet = {0}; + data_packet.data = in_inst->src_data; + data_packet.buf_length = in_inst->src_size; + if (in_inst->is_sweep_signal == false) { + const uint8_t *flac_data = test_flac_start; + const size_t flac_size = test_flac_end - test_flac_start; + size_t current_pos = 0; + while (!is_done) { + size_t remaining = flac_size - current_pos; + size_t copy_size = (remaining < in_inst->src_size) ? remaining : in_inst->src_size; + if (copy_size == 0) { + is_done = true; + break; + } + memcpy(data_packet.data, flac_data + current_pos, copy_size); + current_pos += copy_size; + data_packet.size = copy_size; + data_packet.is_released = false; + data_packet.is_done = (current_pos >= flac_size); + if (xQueueSend(in_inst->sweep_queue1, &data_packet, portMAX_DELAY) != pdPASS) { + ESP_LOGE(TAG, "Failed to send FLAC data to queue"); + continue; + } + sweep_data_t release_signal; + if (xQueueReceive(in_inst->sweep_queue2, &release_signal, portMAX_DELAY) == pdPASS) { + if (release_signal.is_released) { + memset(in_inst->src_data, 0, in_inst->src_size); + } + } + } + } else { + const float duration = SWEEP_DURATION_MS / 1000.0f; + uint32_t total_samples = sound_info->sample_rates * duration; + uint32_t samples_per_chunk = in_inst->src_size / (sound_info->channels * (sound_info->bits / 8)); + uint32_t current_sample = 0; + while (!is_done) { + audio_generate_sweep_signal(&data_packet, sound_info, current_sample, total_samples, samples_per_chunk, &is_done); + if (change_params && current_sample == total_samples / 2) { + xQueueSend(in_inst->sweep_queue1, &data_packet, portMAX_DELAY); + current_sample += data_packet.size / (sound_info->channels * (sound_info->bits / 8)); + sweep_data_t release_signal; + xQueueReceive(in_inst->sweep_queue2, &release_signal, portMAX_DELAY); + if (release_signal.is_released) { + memset(in_inst->src_data, 0, in_inst->src_size); + } + sound_info->sample_rates = 44100; + sound_info->channels = 2; + sound_info->bits = 16; + sound_info->bitrate = 80000; + esp_gmf_pipeline_report_info(in_inst->pipeline, ESP_GMF_INFO_SOUND, sound_info, sizeof(*sound_info)); + total_samples = sound_info->sample_rates * duration; + samples_per_chunk = in_inst->src_size / (sound_info->channels * (sound_info->bits / 8)); + ESP_LOGI(TAG, "Changed params: sr=%d, ch=%d, bits=%d", sound_info->sample_rates, sound_info->channels, sound_info->bits); + continue; + } + xQueueSend(in_inst->sweep_queue1, &data_packet, portMAX_DELAY); + sweep_data_t release_signal; + xQueueReceive(in_inst->sweep_queue2, &release_signal, portMAX_DELAY); + if (release_signal.is_released) { + memset(in_inst->src_data, 0, in_inst->src_size); + } + current_sample += data_packet.size / (sound_info->channels * (sound_info->bits / 8)); + } + } + in_inst->is_done = true; + ESP_LOGI(TAG, "Data gene task end"); + in_inst->task_hd = NULL; + vTaskDelete(NULL); +} + +static esp_gmf_err_io_t audio_el_in_acquire_check_proc_err(void *handle, esp_gmf_payload_t *load, uint32_t wanted_size, int wait_ticks) +{ + return ESP_GMF_IO_FAIL; +} + +static esp_gmf_err_io_t audio_el_in_release_check_proc_err(void *handle, esp_gmf_payload_t *load, uint32_t wanted_size, int wait_ticks) +{ + return ESP_GMF_IO_OK; +} + +static esp_gmf_err_io_t audio_el_in_acquire(void *handle, esp_gmf_payload_t *load, uint32_t wanted_size, int wait_ticks) +{ + audio_el_in_test_t *in_inst = (audio_el_in_test_t *)handle; + load->pts = in_inst->in_frame_count * 100; + sweep_data_t sweep_data; + esp_gmf_err_io_t ret = ESP_GMF_IO_OK; + if (in_inst->is_done) { + in_inst->in_acquire_count++; + goto __acq_exit; + } + if (xQueueReceive(in_inst->sweep_queue1, &sweep_data, pdMS_TO_TICKS(wait_ticks)) == pdPASS) { + in_inst->in_acquire_count++; + ESP_LOGD(TAG, "In frame %p size %d", sweep_data.data, sweep_data.size); + load->buf = sweep_data.data; + load->valid_size = sweep_data.size; + load->buf_length = sweep_data.buf_length; + load->is_done = sweep_data.is_done; + return ESP_GMF_IO_OK; + } + ret = ESP_GMF_IO_TIMEOUT; +__acq_exit: + load->buf = in_inst->src_data; + load->valid_size = 0; + load->buf_length = sweep_data.buf_length; + return ret; +} + +static esp_gmf_err_io_t audio_el_in_release(void *handle, esp_gmf_payload_t *load, uint32_t wanted_size, int wait_ticks) +{ + audio_el_in_test_t *in_inst = (audio_el_in_test_t *)handle; + in_inst->in_release_count++; + in_inst->in_frame_count++; + ESP_LOGD(TAG, "In frame %ld size %d", in_inst->in_release_count, load->valid_size); + sweep_data_t release_signal; + release_signal.data = load->buf; + release_signal.is_released = true; + xQueueSend(in_inst->sweep_queue2, &release_signal, 300); + return ESP_GMF_IO_OK; +} + +static esp_gmf_err_io_t audio_el_out_acquire(void *handle, esp_gmf_payload_t *load, uint32_t wanted_size, int wait_ticks) +{ + audio_el_out_test_t *out_inst = (audio_el_out_test_t *)handle; + out_inst->out_acquire_count++; + if (load->buf) { + out_inst->no_need_free = true; + } else { + if (wanted_size > out_inst->out_max_size) { + SAFE_FREE(out_inst->out_data); + out_inst->out_data = NULL; + uint8_t *new_buf = esp_gmf_oal_malloc_align(TEST_AUDIO_ALIGNMENT, wanted_size + TEST_AUDIO_ALIGNMENT); + if (new_buf == NULL) { + ESP_LOGE(TAG, "Fail to allocate %d bytes for output buffer", (int)wanted_size + TEST_AUDIO_ALIGNMENT); + return ESP_GMF_IO_FAIL; + } + out_inst->out_data = new_buf; + out_inst->out_max_size = wanted_size + TEST_AUDIO_ALIGNMENT; + } + load->buf = out_inst->out_data; + load->buf_length = out_inst->out_max_size; + } + return ESP_GMF_IO_OK; +} + +static esp_gmf_err_io_t audio_el_out_release(void *handle, esp_gmf_payload_t *load, uint32_t wanted_size, int wait_ticks) +{ + audio_el_out_test_t *out_inst = (audio_el_out_test_t *)handle; + out_inst->out_release_count++; + ESP_LOGD(TAG, "Out frame %ld size %ld", out_inst->out_release_count, out_inst->out_size); + out_inst->out_frame_count++; + if (out_inst->no_need_free == false) { + load->buf = NULL; + } + return ESP_GMF_IO_OK; +} + +static esp_gmf_element_handle_t audio_el_get_element_by_caps_from_pool(esp_gmf_pool_handle_t pool, uint64_t caps_cc) +{ + const void *iter = NULL; + esp_gmf_element_handle_t element = NULL; + while (esp_gmf_pool_iterate_element(pool, &iter, &element) == ESP_GMF_ERR_OK) { + const esp_gmf_cap_t *caps = NULL; + esp_gmf_element_get_caps(element, &caps); + while (caps) { + if (caps->cap_eightcc == caps_cc) { + return element; + } + caps = caps->next; + } + } + return NULL; +} + +static const char *audio_el_get_element_name_by_caps(audio_el_res_t *res, uint64_t caps_cc) +{ + // Test get element name by caps + esp_gmf_element_handle_t element; + element = audio_el_get_element_by_caps_from_pool(res->pool, caps_cc); + const char *name = OBJ_GET_TAG(element); + return name; +} + +static void audio_el_set_audio_info(audio_el_res_t *res) +{ + for (uint32_t i = 0; i < res->in_port_num; i++) { + res->in_inst[i].src_info.sample_rates = TEST_AUDIO_SAMPLE_RATE; + res->in_inst[i].src_info.channels = TEST_AUDIO_CHANNELS; + res->in_inst[i].src_info.bits = TEST_AUDIO_BITS; + res->in_inst[i].src_info.format_id = ESP_AUDIO_TYPE_PCM; + res->in_inst[i].src_info.bitrate = TEST_AUDIO_BITRATE; + res->in_inst[i].src_size = 1024; + res->in_inst[i].is_done = false; + res->in_inst[i].change_info = true; + res->in_inst[i].is_sweep_signal = true; + } +} + +static void audio_el_pool_init(audio_el_res_t *res) +{ + esp_gmf_pool_init(&res->pool); + TEST_ASSERT_NOT_EQUAL(NULL, res->pool); + gmf_register_audio_all(res->pool); + ESP_GMF_POOL_SHOW_ITEMS(res->pool); +} + +static void audio_el_pool_deinit(audio_el_res_t *res) +{ + if (res->pool) { + gmf_unregister_audio_all(res->pool); + esp_gmf_pool_deinit(res->pool); + res->pool = NULL; + } +} + +static void audio_el_prepare_audio_pipeline(audio_el_res_t *res, char **elements) +{ + res->pipe_sync_evt = xEventGroupCreate(); + TEST_ASSERT_NOT_NULL(res->pipe_sync_evt); + esp_gmf_pool_new_pipeline(res->pool, NULL, (const char **)elements, res->el_cnt, NULL, &res->pipe); + TEST_ASSERT_NOT_EQUAL(NULL, res->pipe); + // Create and register for each input port + for (uint32_t i = 0; i < res->in_port_num; i++) { + esp_gmf_port_handle_t in_port = NEW_ESP_GMF_PORT_IN_BLOCK(audio_el_in_acquire, audio_el_in_release, NULL, &res->in_inst[i], i, 300); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_reg_el_port(res->pipe, elements[0], ESP_GMF_IO_DIR_READER, in_port)); + } + // Create and register output ports + for (uint32_t i = 0; i < res->out_port_num; i++) { + esp_gmf_port_handle_t out_port = NEW_ESP_GMF_PORT_OUT_BLOCK(audio_el_out_acquire, audio_el_out_release, NULL, &res->out_inst[i], i, 300); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_reg_el_port(res->pipe, elements[res->el_cnt - 1], ESP_GMF_IO_DIR_WRITER, out_port)); + } + esp_gmf_task_cfg_t cfg = DEFAULT_ESP_GMF_TASK_CONFIG(); + cfg.thread.stack_in_ext = true; + cfg.thread.stack = AUDIO_EL_MAX_STACK_SIZE; + cfg.thread.core = 1; + esp_gmf_task_init(&cfg, &res->task); + TEST_ASSERT_NOT_EQUAL(NULL, res->task); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_bind_task(res->pipe, res->task)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_set_event(res->pipe, audio_el_pipeline_event, res->pipe_sync_evt)); + for (int i = 0; i < res->el_cnt; i++) { + esp_gmf_pipeline_get_el_by_name(res->pipe, elements[i], &res->current_hd[i]); + } +} + +static void audio_el_release_audio_pipeline(audio_el_res_t *res) +{ + if (res->pipe) { + esp_gmf_pipeline_stop(res->pipe); + } + if (res->task) { + esp_gmf_task_deinit(res->task); + res->task = NULL; + } + if (res->pipe) { + esp_gmf_pipeline_destroy(res->pipe); + res->pipe = NULL; + } + if (res->pipe_sync_evt) { + vEventGroupDelete(res->pipe_sync_evt); + res->pipe_sync_evt = NULL; + } +} + +static void audio_el_res_init(audio_el_res_cfg_t *cfg, audio_el_res_t **res) +{ + *res = (audio_el_res_t *)heap_caps_calloc(1, sizeof(audio_el_res_t), MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + TEST_ASSERT_NOT_EQUAL(NULL, *res); + (*res)->in_port_num = cfg->in_port_num; + (*res)->out_port_num = cfg->out_port_num; + (*res)->el_cnt = cfg->el_cnt; + audio_el_pool_init(*res); + char **elements = NULL; + if (cfg->caps_cc) { + elements = (char **)heap_caps_calloc(cfg->el_cnt, sizeof(char *), MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + TEST_ASSERT_NOT_EQUAL(NULL, elements); + for (int i = 0; i < cfg->el_cnt; i++) { + elements[i] = audio_el_get_element_name_by_caps(*res, cfg->caps_cc[i]); + TEST_ASSERT_NOT_EQUAL(NULL, elements[i]); + } + } + (*res)->in_inst = (audio_el_in_test_t *)heap_caps_calloc((*res)->in_port_num, sizeof(audio_el_in_test_t), MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + TEST_ASSERT_NOT_EQUAL(NULL, (*res)->in_inst); + (*res)->out_inst = (audio_el_out_test_t *)heap_caps_calloc((*res)->out_port_num, sizeof(audio_el_out_test_t), MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + TEST_ASSERT_NOT_EQUAL(NULL, (*res)->out_inst); + audio_el_prepare_audio_pipeline(*res, elements); + SAFE_FREE(elements); +} + +static void audio_el_res_deinit(audio_el_res_t *res) +{ + SAFE_FREE(res->in_inst); + res->in_inst = NULL; + SAFE_FREE(res->out_inst); + res->out_inst = NULL; + audio_el_release_audio_pipeline(res); + audio_el_pool_deinit(res); + SAFE_FREE(res); +} + +static void audio_el_inst_init(audio_el_res_t *res) +{ + TaskHandle_t current_task = xTaskGetCurrentTaskHandle(); + UBaseType_t priority = uxTaskPriorityGet(current_task); + for (uint32_t i = 0; i < res->in_port_num; i++) { + res->in_inst[i].pipeline = res->pipe; + res->in_inst[i].sweep_queue1 = xQueueCreate(SWEEP_QUEUE_SIZE, sizeof(sweep_data_t)); + TEST_ASSERT_NOT_EQUAL(NULL, res->in_inst[i].sweep_queue1); + res->in_inst[i].sweep_queue2 = xQueueCreate(SWEEP_QUEUE_SIZE, sizeof(sweep_data_t)); + TEST_ASSERT_NOT_EQUAL(NULL, res->in_inst[i].sweep_queue2); + res->in_inst[i].src_data = heap_caps_malloc(res->in_inst[i].src_size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + TEST_ASSERT_NOT_EQUAL(NULL, res->in_inst[i].src_data); + char task_name[32] = {0}; + sprintf(task_name, "sweep_task_%ld", i); + xTaskCreatePinnedToCore(audio_el_data_gene_task, task_name, 10240, &res->in_inst[i], priority + i + 3, &(res->in_inst[i].task_hd), 1); + TEST_ASSERT_NOT_EQUAL(NULL, res->in_inst[i].task_hd); + } + res->is_end = false; +} + +static void audio_el_inst_deinit(audio_el_res_t *res) +{ + if (res->in_inst) { + for (uint32_t i = 0; i < res->in_port_num; i++) { + if (res->in_inst[i].task_hd) { + vTaskDelete(res->in_inst[i].task_hd); + res->in_inst[i].task_hd = NULL; + } + if (res->in_inst[i].sweep_queue1) { + vQueueDelete(res->in_inst[i].sweep_queue1); + res->in_inst[i].sweep_queue1 = NULL; + } + if (res->in_inst[i].sweep_queue2) { + vQueueDelete(res->in_inst[i].sweep_queue2); + res->in_inst[i].sweep_queue2 = NULL; + } + SAFE_FREE(res->in_inst[i].src_data); + res->in_inst[i].src_data = NULL; + } + memset(res->in_inst, 0, sizeof(audio_el_in_test_t) * res->in_port_num); + } + if (res->out_inst) { + for (uint32_t i = 0; i < res->out_port_num; i++) { + SAFE_FREE(res->out_inst[i].out_data); + res->out_inst[i].out_data = NULL; + } + memset(res->out_inst, 0, sizeof(audio_el_out_test_t) * res->out_port_num); + } + if (res->cfg_task_hd) { + vTaskDelete(res->cfg_task_hd); + res->cfg_task_hd = NULL; + } +} + +static void test_element_run_stop(audio_el_res_t *res) +{ + ESP_LOGI(TAG, "Test_element_run_stop"); + audio_el_inst_init(res); + xTaskCreate(audio_el_cfg_task, "cfg_task", 10240, res, 6, &(res->cfg_task_hd)); + TEST_ASSERT_NOT_EQUAL(NULL, res->cfg_task_hd); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_reset(res->pipe)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_loading_jobs(res->pipe)); + for (uint32_t i = 0; i < res->in_port_num; i++) { + esp_gmf_pipeline_report_info(res->pipe, ESP_GMF_INFO_SOUND, &res->in_inst[i].src_info, sizeof(res->in_inst[i].src_info)); + } + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_run(res->pipe)); + vTaskDelay(2000 / portTICK_RATE_MS); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_stop(res->pipe)); + xEventGroupWaitBits(res->pipe_sync_evt, PIPELINE_BLOCK_BIT, pdTRUE, pdFALSE, portMAX_DELAY); + res->is_end = true; + // Test in acquire and in release count should be equal + for (uint32_t i = 0; i < res->in_port_num; i++) { + ESP_LOGI(TAG, "In port %ld acquire count %ld, release count %ld", i, res->in_inst[i].in_acquire_count, res->in_inst[i].in_release_count); + TEST_ASSERT_EQUAL(res->in_inst[i].in_acquire_count, res->in_inst[i].in_release_count); + } + // Test out acquire and out release count should be equal + for (uint32_t i = 0; i < res->out_port_num; i++) { + ESP_LOGI(TAG, "Out port %ld acquire count %ld, release count %ld", i, res->out_inst[i].out_acquire_count, res->out_inst[i].out_release_count); + TEST_ASSERT_EQUAL(res->out_inst[i].out_acquire_count, res->out_inst[i].out_release_count); + } + audio_el_inst_deinit(res); +} + +static void test_element_run_finish(audio_el_res_t *res) +{ + ESP_LOGI(TAG, "Test_element_run_finish"); + audio_el_inst_init(res); + xTaskCreate(audio_el_cfg_task, "cfg_task", 10240, res, 6, &(res->cfg_task_hd)); + TEST_ASSERT_NOT_EQUAL(NULL, res->cfg_task_hd); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_reset(res->pipe)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_loading_jobs(res->pipe)); + for (uint32_t i = 0; i < res->in_port_num; i++) { + esp_gmf_pipeline_report_info(res->pipe, ESP_GMF_INFO_SOUND, &res->in_inst[i].src_info, sizeof(res->in_inst[i].src_info)); + } + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_run(res->pipe)); + char *el_name = OBJ_GET_TAG(res->current_hd[0]); + // Mixer need to stop by user + if (strcmp(el_name, "aud_mixer") == 0) { + vTaskDelay(6000 / portTICK_RATE_MS); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_stop(res->pipe)); + } else { + xEventGroupWaitBits(res->pipe_sync_evt, PIPELINE_BLOCK_BIT, pdTRUE, pdFALSE, portMAX_DELAY); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_stop(res->pipe)); + } + res->is_end = true; + // Test in acquire and in release count should be equal + for (uint32_t i = 0; i < res->in_port_num; i++) { + ESP_LOGI(TAG, "In port %ld acquire count %ld, release count %ld", i, res->in_inst[i].in_acquire_count, res->in_inst[i].in_release_count); + TEST_ASSERT_EQUAL(res->in_inst[i].in_acquire_count, res->in_inst[i].in_release_count); + } + // Test out acquire and out release count should be equal + for (uint32_t i = 0; i < res->out_port_num; i++) { + ESP_LOGI(TAG, "Out port %ld acquire count %ld, release count %ld", i, res->out_inst[i].out_acquire_count, res->out_inst[i].out_release_count); + TEST_ASSERT_EQUAL(res->out_inst[i].out_acquire_count, res->out_inst[i].out_release_count); + } + audio_el_inst_deinit(res); +} + +static void test_element_run_error_open(audio_el_res_t *res) +{ + ESP_LOGI(TAG, "Test_element_run_error_open"); + audio_el_set_audio_info(res); + audio_el_inst_init(res); + xTaskCreate(audio_el_cfg_task, "cfg_task", 10240, res, 6, &(res->cfg_task_hd)); + TEST_ASSERT_NOT_EQUAL(NULL, res->cfg_task_hd); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_reset(res->pipe)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_loading_jobs(res->pipe)); + for (uint32_t i = 0; i < res->in_port_num; i++) { + res->in_inst[i].src_info.bits = 20; + res->in_inst[i].src_info.channels = 0; + esp_gmf_pipeline_report_info(res->pipe, ESP_GMF_INFO_SOUND, &res->in_inst[i].src_info, sizeof(res->in_inst[i].src_info)); + } + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_run(res->pipe)); + vTaskDelay(2000 / portTICK_RATE_MS); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_stop(res->pipe)); + res->is_end = true; + audio_el_inst_deinit(res); +} + +static void test_element_run_error_process(audio_el_res_t *res) +{ + ESP_LOGI(TAG, "Test_element_run_error_process"); + esp_gmf_element_unregister_in_port(res->pipe->head_el, NULL); + // Create and register for each input port + for (uint32_t i = 0; i < res->in_port_num; i++) { + esp_gmf_port_handle_t in_port = NEW_ESP_GMF_PORT_IN_BLOCK(audio_el_in_acquire_check_proc_err, audio_el_in_release_check_proc_err, NULL, &res->in_inst[i], i, 300); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_reg_el_port(res->pipe, OBJ_GET_TAG(res->current_hd[0]), ESP_GMF_IO_DIR_READER, in_port)); + } + audio_el_set_audio_info(res); + audio_el_inst_init(res); + xTaskCreate(audio_el_cfg_task, "cfg_task", 10240, res, 6, &(res->cfg_task_hd)); + TEST_ASSERT_NOT_EQUAL(NULL, res->cfg_task_hd); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_reset(res->pipe)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_loading_jobs(res->pipe)); + for (uint32_t i = 0; i < res->in_port_num; i++) { + esp_gmf_pipeline_report_info(res->pipe, ESP_GMF_INFO_SOUND, &res->in_inst[i].src_info, sizeof(res->in_inst[i].src_info)); + } + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_run(res->pipe)); + vTaskDelay(2000 / portTICK_RATE_MS); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_stop(res->pipe)); + res->is_end = true; + audio_el_inst_deinit(res); +} + +static void test_element_run_with_multi_task(audio_el_res_cfg_t *cfg, void (*config_func)(esp_gmf_element_handle_t, void *)) +{ + ESP_LOGI(TAG, "Test_element_run_with_multi_task"); + audio_el_res_t *res1 = NULL; + audio_el_res_t *res2 = NULL; + audio_el_res_init(cfg, &res1); + audio_el_res_init(cfg, &res2); + res1->config_func = config_func; + res2->config_func = config_func; + audio_el_set_audio_info(res1); + audio_el_set_audio_info(res2); + for (uint32_t i = 0; i < res1->in_port_num; i++) { + res1->in_inst[i].src_size = cfg->src_size; + } + for (uint32_t i = 0; i < res2->in_port_num; i++) { + res2->in_inst[i].src_size = cfg->src_size; + } + audio_el_inst_init(res1); + audio_el_inst_init(res2); + xTaskCreate(audio_el_cfg_task, "cfg_task1", 10240, res1, 6, &(res1->cfg_task_hd)); + xTaskCreate(audio_el_cfg_task, "cfg_task2", 10240, res2, 7, &(res2->cfg_task_hd)); + TEST_ASSERT_NOT_EQUAL(NULL, res1->cfg_task_hd); + TEST_ASSERT_NOT_EQUAL(NULL, res2->cfg_task_hd); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_reset(res1->pipe)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_reset(res2->pipe)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_loading_jobs(res1->pipe)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_loading_jobs(res2->pipe)); + for (uint32_t i = 0; i < res1->in_port_num; i++) { + esp_gmf_pipeline_report_info(res1->pipe, ESP_GMF_INFO_SOUND, &res1->in_inst[i].src_info, sizeof(res1->in_inst[i].src_info)); + } + for (uint32_t i = 0; i < res2->in_port_num; i++) { + esp_gmf_pipeline_report_info(res2->pipe, ESP_GMF_INFO_SOUND, &res2->in_inst[i].src_info, sizeof(res2->in_inst[i].src_info)); + } + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_run(res1->pipe)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_run(res2->pipe)); + vTaskDelay(2000 / portTICK_RATE_MS); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_stop(res1->pipe)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_stop(res2->pipe)); + res1->is_end = true; + res2->is_end = true; + audio_el_inst_deinit(res1); + audio_el_inst_deinit(res2); + audio_el_res_deinit(res1); + audio_el_res_deinit(res2); +} + +TEST_CASE("Audio ENCODER Element Test", "[ESP_GMF_AUDIO]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + ESP_GMF_MEM_SHOW(TAG); + audio_el_res_cfg_t cfg = DEFAULT_SINGLE_IN_SINGLE_OUT_CONFIG(); + cfg.caps_cc = (uint64_t[]) {ESP_GMF_CAPS_AUDIO_ENCODER}; + audio_el_res_t *res = NULL; + audio_el_res_init(&cfg, &res); + res->config_func = encoder_config_callback; + // Test encoder with different types + uint32_t enc_types[] = {ESP_AUDIO_TYPE_AAC, ESP_AUDIO_TYPE_LC3, ESP_AUDIO_TYPE_OPUS, ESP_AUDIO_TYPE_SBC}; + // Test different input sizes + uint32_t test_sizes[] = {120, 256, 512, 4096}; + // Test for run and stop + for (int i = 0; i < sizeof(enc_types) / sizeof(enc_types[0]); i++) { + for (int j = 0; j < sizeof(test_sizes) / sizeof(test_sizes[0]); j++) { + audio_el_set_audio_info(res); + res->in_inst[0].src_info.format_id = enc_types[i]; + res->in_inst[0].src_size = test_sizes[j]; + test_element_run_stop(res); + } + } + // Test for run and finish + for (int i = 0; i < sizeof(enc_types) / sizeof(enc_types[0]); i++) { + for (int j = 0; j < sizeof(test_sizes) / sizeof(test_sizes[0]); j++) { + audio_el_set_audio_info(res); + res->in_inst[0].src_info.format_id = enc_types[i]; + res->in_inst[0].src_size = test_sizes[j]; + test_element_run_finish(res); + } + } + // Test for run on process error + test_element_run_error_process(res); + // Test for run on open error + audio_el_set_audio_info(res); + res->config_func = NULL; + esp_audio_enc_config_t *enc_cfg = OBJ_GET_CFG(res->current_hd[0]); + enc_cfg->type = ESP_AUDIO_TYPE_UNSUPPORT; + test_element_run_error_open(res); + audio_el_res_deinit(res); + // Test for run with multi task + test_element_run_with_multi_task(&cfg, encoder_config_callback); + ESP_GMF_MEM_SHOW(TAG); +} + +TEST_CASE("Audio DECODER Element Test", "[ESP_GMF_AUDIO]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + ESP_GMF_MEM_SHOW(TAG); + audio_el_res_cfg_t cfg = DEFAULT_SINGLE_IN_SINGLE_OUT_CONFIG(); + cfg.caps_cc = (uint64_t[]) {ESP_GMF_CAPS_AUDIO_DECODER}; + audio_el_res_t *res = NULL; + audio_el_res_init(&cfg, &res); + res->config_func = decoder_config_callback; + // Test different source sizes + uint32_t test_sizes[] = {119, 256, 512, 4096}; + // Test for run and stop + for (int i = 0; i < sizeof(test_sizes) / sizeof(test_sizes[0]); i++) { + audio_el_set_audio_info(res); + res->in_inst[0].src_size = test_sizes[i]; + res->in_inst[0].is_sweep_signal = false; + test_element_run_stop(res); + } + // Test for run and finish + for (int i = 0; i < sizeof(test_sizes) / sizeof(test_sizes[0]); i++) { + audio_el_set_audio_info(res); + res->in_inst[0].src_size = test_sizes[i]; + res->in_inst[0].is_sweep_signal = false; + test_element_run_finish(res); + } + // Test for run on process error + test_element_run_error_process(res); + // Test for run on open error + res->config_func = NULL; + esp_audio_simple_dec_cfg_t *dec_cfg = OBJ_GET_CFG(res->current_hd[0]); + dec_cfg->dec_type = ESP_AUDIO_SIMPLE_DEC_TYPE_NONE; + test_element_run_error_open(res); + audio_el_res_deinit(res); + // Test for run with multi task + test_element_run_with_multi_task(&cfg, decoder_config_callback); + ESP_GMF_MEM_SHOW(TAG); +} + +TEST_CASE("Audio ALC Element Test", "[ESP_GMF_AUDIO]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + ESP_GMF_MEM_SHOW(TAG); + audio_el_res_cfg_t cfg = DEFAULT_SINGLE_IN_SINGLE_OUT_CONFIG(); + cfg.caps_cc = (uint64_t[]) {ESP_GMF_CAPS_AUDIO_ALC}; + audio_el_res_t *res = NULL; + audio_el_res_init(&cfg, &res); + res->config_func = alc_config_callback; + // Test for run and stop + audio_el_set_audio_info(res); + test_element_run_stop(res); + // Test for run and finish + audio_el_set_audio_info(res); + test_element_run_finish(res); + // Test for run on open error + test_element_run_error_open(res); + // Test for run on process error + test_element_run_error_process(res); + audio_el_res_deinit(res); + // Test for run with multi task + test_element_run_with_multi_task(&cfg, alc_config_callback); + ESP_GMF_MEM_SHOW(TAG); +} + +TEST_CASE("Audio EQ Element Test", "[ESP_GMF_AUDIO]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + ESP_GMF_MEM_SHOW(TAG); + audio_el_res_cfg_t cfg = DEFAULT_SINGLE_IN_SINGLE_OUT_CONFIG(); + cfg.caps_cc = (uint64_t[]) {ESP_GMF_CAPS_AUDIO_EQUALIZER}; + audio_el_res_t *res = NULL; + audio_el_res_init(&cfg, &res); + res->config_func = eq_config_callback; + // Test for run and stop + audio_el_set_audio_info(res); + test_element_run_stop(res); + // Test for run and finish + audio_el_set_audio_info(res); + test_element_run_finish(res); + // Test for run on open error + test_element_run_error_open(res); + // Test for run on process error + test_element_run_error_process(res); + audio_el_res_deinit(res); + // Test for run with multi task + test_element_run_with_multi_task(&cfg, eq_config_callback); + ESP_GMF_MEM_SHOW(TAG); +} + +TEST_CASE("Audio FADE Element Test", "[ESP_GMF_AUDIO]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + ESP_GMF_MEM_SHOW(TAG); + audio_el_res_cfg_t cfg = DEFAULT_SINGLE_IN_SINGLE_OUT_CONFIG(); + cfg.caps_cc = (uint64_t[]) {ESP_GMF_CAPS_AUDIO_FADE}; + audio_el_res_t *res = NULL; + audio_el_res_init(&cfg, &res); + res->config_func = fade_config_callback; + // Test for run and stop + audio_el_set_audio_info(res); + test_element_run_stop(res); + // Test for run and finish + audio_el_set_audio_info(res); + test_element_run_finish(res); + // Test for run on open error + test_element_run_error_open(res); + // Test for run on process error + test_element_run_error_process(res); + audio_el_res_deinit(res); + // Test for run with multi task + test_element_run_with_multi_task(&cfg, fade_config_callback); + ESP_GMF_MEM_SHOW(TAG); +} + +TEST_CASE("Audio SONIC Element Test", "[ESP_GMF_AUDIO]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + ESP_GMF_MEM_SHOW(TAG); + audio_el_res_cfg_t cfg = DEFAULT_SINGLE_IN_SINGLE_OUT_CONFIG(); + cfg.caps_cc = (uint64_t[]) {ESP_GMF_CAPS_AUDIO_SONIC}; + audio_el_res_t *res = NULL; + audio_el_res_init(&cfg, &res); + res->config_func = sonic_config_callback; + // Test for run and stop + audio_el_set_audio_info(res); + test_element_run_stop(res); + // Test for run and finish + audio_el_set_audio_info(res); + test_element_run_finish(res); + // Test for run on open error + test_element_run_error_open(res); + // Test for run on process error + test_element_run_error_process(res); + audio_el_res_deinit(res); + // Test for run with multi task + test_element_run_with_multi_task(&cfg, sonic_config_callback); + ESP_GMF_MEM_SHOW(TAG); +} + +TEST_CASE("Audio BIT_CVT Element Test", "[ESP_GMF_AUDIO]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + ESP_GMF_MEM_SHOW(TAG); + audio_el_res_cfg_t cfg = DEFAULT_SINGLE_IN_SINGLE_OUT_CONFIG(); + cfg.caps_cc = (uint64_t[]) {ESP_GMF_CAPS_AUDIO_BIT_CONVERT}; + audio_el_res_t *res = NULL; + audio_el_res_init(&cfg, &res); + res->config_func = bit_cvt_config_callback; + // Test different source and destination bits + uint32_t bit_pairs[][2] = {{16, 24}, {24, 16}, {16, 32}, {32, 16}}; + // Test for run and stop + for (int i = 0; i < sizeof(bit_pairs) / sizeof(bit_pairs[0]); i++) { + audio_el_set_audio_info(res); + res->in_inst[0].src_info.bits = bit_pairs[i][0]; + res->out_inst[0].out_info.bits = bit_pairs[i][1]; + test_element_run_stop(res); + } + // Test for run and finish + for (int i = 0; i < sizeof(bit_pairs) / sizeof(bit_pairs[0]); i++) { + audio_el_set_audio_info(res); + res->in_inst[0].src_info.bits = bit_pairs[i][0]; + res->out_inst[0].out_info.bits = bit_pairs[i][1]; + test_element_run_finish(res); + } + // Test for run on open error + test_element_run_error_open(res); + // Test for run on process error + test_element_run_error_process(res); + audio_el_res_deinit(res); + // Test for run with multi task + test_element_run_with_multi_task(&cfg, bit_cvt_config_callback); + ESP_GMF_MEM_SHOW(TAG); +} + +TEST_CASE("Audio CH_CVT Element Test", "[ESP_GMF_AUDIO]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + ESP_GMF_MEM_SHOW(TAG); + audio_el_res_cfg_t cfg = DEFAULT_SINGLE_IN_SINGLE_OUT_CONFIG(); + cfg.caps_cc = (uint64_t[]) {ESP_GMF_CAPS_AUDIO_CHANNEL_CONVERT}; + audio_el_res_t *res = NULL; + audio_el_res_init(&cfg, &res); + res->config_func = ch_cvt_config_callback; + // Test different source and destination channels + uint32_t channel_pairs[][2] = {{1, 2}, {2, 1}, {2, 4}, {4, 2}}; + // Test for run and stop + for (int i = 0; i < sizeof(channel_pairs) / sizeof(channel_pairs[0]); i++) { + audio_el_set_audio_info(res); + res->in_inst[0].src_info.channels = channel_pairs[i][0]; + res->out_inst[0].out_info.channels = channel_pairs[i][1]; + test_element_run_stop(res); + } + // Test for run and finish + for (int i = 0; i < sizeof(channel_pairs) / sizeof(channel_pairs[0]); i++) { + audio_el_set_audio_info(res); + res->in_inst[0].src_info.channels = channel_pairs[i][0]; + res->out_inst[0].out_info.channels = channel_pairs[i][1]; + test_element_run_finish(res); + } + // Test for run on open error + test_element_run_error_open(res); + // Test for run on process error + test_element_run_error_process(res); + audio_el_res_deinit(res); + // Test for run with multi task + test_element_run_with_multi_task(&cfg, ch_cvt_config_callback); + ESP_GMF_MEM_SHOW(TAG); +} + +TEST_CASE("Audio RATE_CVT Element Test", "[ESP_GMF_AUDIO]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + ESP_GMF_MEM_SHOW(TAG); + audio_el_res_cfg_t cfg = DEFAULT_SINGLE_IN_SINGLE_OUT_CONFIG(); + cfg.caps_cc = (uint64_t[]) {ESP_GMF_CAPS_AUDIO_RATE_CONVERT}; + audio_el_res_t *res = NULL; + audio_el_res_init(&cfg, &res); + res->config_func = rate_cvt_config_callback; + // Test different source and destination rates + uint32_t rate_pairs[][2] = {{44100, 48000}, {48000, 44100}, {44100, 16000}, {16000, 44100}}; + // Test for run and stop + for (int i = 0; i < sizeof(rate_pairs) / sizeof(rate_pairs[0]); i++) { + audio_el_set_audio_info(res); + res->in_inst[0].src_info.sample_rates = rate_pairs[i][0]; + res->out_inst[0].out_info.sample_rates = rate_pairs[i][1]; + test_element_run_stop(res); + } + // Test for run and finish + for (int i = 0; i < sizeof(rate_pairs) / sizeof(rate_pairs[0]); i++) { + audio_el_set_audio_info(res); + res->in_inst[0].src_info.sample_rates = rate_pairs[i][0]; + res->out_inst[0].out_info.sample_rates = rate_pairs[i][1]; + test_element_run_finish(res); + } + // Test for run on open error + test_element_run_error_open(res); + // Test for run on process error + test_element_run_error_process(res); + audio_el_res_deinit(res); + // Test for run with multi task + test_element_run_with_multi_task(&cfg, rate_cvt_config_callback); + ESP_GMF_MEM_SHOW(TAG); +} + +TEST_CASE("Audio MIXER Element Test", "[ESP_GMF_AUDIO]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + ESP_GMF_MEM_SHOW(TAG); + audio_el_res_cfg_t cfg = DEFAULT_MULTI_IN_SINGLE_OUT_CONFIG(); + cfg.caps_cc = (uint64_t[]) {ESP_GMF_CAPS_AUDIO_MIXER}; + audio_el_res_t *res = NULL; + audio_el_res_init(&cfg, &res); + uint32_t src_size = 10 * TEST_AUDIO_SAMPLE_RATE * TEST_AUDIO_BITS * TEST_AUDIO_CHANNELS / 8000; + res->config_func = mixer_config_callback; + // Test for run and stop + audio_el_set_audio_info(res); + res->in_inst[0].src_size = src_size; + res->in_inst[1].src_size = src_size; + test_element_run_stop(res); + // Test for run and finish + audio_el_set_audio_info(res); + res->in_inst[0].src_size = src_size; + res->in_inst[1].src_size = src_size; + test_element_run_finish(res); + // Test for run on process error + test_element_run_error_process(res); + // Test for run on open error + res->config_func = NULL; + esp_ae_mixer_cfg_t *mix_cfg = OBJ_GET_CFG(res->current_hd[0]); + mix_cfg->src_num = 0; + test_element_run_error_open(res); + audio_el_res_deinit(res); + // Test for run with multi task + cfg.src_size = src_size; + test_element_run_with_multi_task(&cfg, mixer_config_callback); + ESP_GMF_MEM_SHOW(TAG); +} + +TEST_CASE("Audio INTERLEAVE Element Test", "[ESP_GMF_AUDIO]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + ESP_GMF_MEM_SHOW(TAG); + audio_el_res_cfg_t cfg = DEFAULT_MULTI_IN_SINGLE_OUT_CONFIG(); + cfg.caps_cc = (uint64_t[]) {ESP_GMF_CAPS_AUDIO_INTERLEAVE}; + audio_el_res_t *res = NULL; + audio_el_res_init(&cfg, &res); + res->config_func = NULL; + // Test for run and stop + audio_el_set_audio_info(res); + test_element_run_stop(res); + // Test for run and finish + audio_el_set_audio_info(res); + test_element_run_finish(res); + // Test for run on open error + esp_gmf_interleave_cfg *interleave_cfg = OBJ_GET_CFG(res->current_hd[0]); + interleave_cfg->src_num = 0; + test_element_run_error_open(res); + // Test for run on process error + test_element_run_error_process(res); + audio_el_res_deinit(res); + // Test for run with multi task + test_element_run_with_multi_task(&cfg, NULL); + ESP_GMF_MEM_SHOW(TAG); +} + +TEST_CASE("Audio DEINTERLEAVE Element Test", "[ESP_GMF_AUDIO]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + ESP_GMF_MEM_SHOW(TAG); + audio_el_res_cfg_t cfg = DEFAULT_SINGLE_IN_MULTI_OUT_CONFIG(); + cfg.caps_cc = (uint64_t[]) {ESP_GMF_CAPS_AUDIO_DEINTERLEAVE}; + audio_el_res_t *res = NULL; + audio_el_res_init(&cfg, &res); + res->config_func = NULL; + // Test for run and stop + audio_el_set_audio_info(res); + test_element_run_stop(res); + // Test for run and finish + audio_el_set_audio_info(res); + test_element_run_finish(res); + // Test for run on open error + test_element_run_error_open(res); + // Test for run on process error + test_element_run_error_process(res); + audio_el_res_deinit(res); + // Test for run with multi task + test_element_run_with_multi_task(&cfg, NULL); + ESP_GMF_MEM_SHOW(TAG); +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/main/elements/gmf_audio_pattern.h b/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/main/elements/gmf_audio_pattern.h new file mode 100644 index 0000000..139ccb5 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/main/elements/gmf_audio_pattern.h @@ -0,0 +1,71 @@ +/** + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include +#include +#include +#include "gmf_audio_el_com.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define SWEEP_DURATION_MS (5000) + +void audio_generate_sweep_signal(sweep_data_t *sweep_data, esp_gmf_info_sound_t *sound_info, uint32_t current_sample, + uint32_t total_samples, uint32_t samples_per_chunk, bool *is_done) +{ + const float start_freq = 20.0f; + const float end_freq = 20000.0f; + const float duration = SWEEP_DURATION_MS / 1000.0f; + uint32_t sample_count = 0; + for (uint32_t i = 0; i < samples_per_chunk; i++) { + if (current_sample >= total_samples) { + *is_done = true; + break; + } + float t = (float)current_sample / sound_info->sample_rates; + float freq = start_freq + (end_freq - start_freq) * t / duration; + float phase = 2.0f * M_PI * freq * t; + float sample_value = sinf(phase); + for (int ch = 0; ch < sound_info->channels; ch++) { + switch (sound_info->bits) { + case 16: { + int16_t sample = (int16_t)(32767.0f * sample_value); + ((int16_t *)sweep_data->data)[i * sound_info->channels + ch] = sample; + break; + } + case 24: { + int32_t sample = (int32_t)(8388607.0f * sample_value); + uint8_t *ptr = (uint8_t *)sweep_data->data + (i * sound_info->channels + ch) * 3; + ptr[0] = sample & 0xFF; + ptr[1] = (sample >> 8) & 0xFF; + ptr[2] = (sample >> 16) & 0xFF; + break; + } + case 32: { + int32_t sample = (int32_t)(2147483647.0f * sample_value); + ((int32_t *)sweep_data->data)[i * sound_info->channels + ch] = sample; + break; + } + default: + ESP_LOGE("AUD_PATTERN", "Unsupported bit depth: %d", sound_info->bits); + *is_done = true; + return; + } + } + current_sample++; + sample_count++; + } + sweep_data->size = sample_count * sound_info->channels * (sound_info->bits / 8); + sweep_data->is_released = false; + sweep_data->is_done = *is_done; +} + +#ifdef __cplusplus +} +#endif diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/main/elements/gmf_audio_play_com.c b/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/main/elements/gmf_audio_play_com.c new file mode 100644 index 0000000..94a6fcd --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/main/elements/gmf_audio_play_com.c @@ -0,0 +1,131 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "unity.h" +#include +#include "esp_log.h" +#include "freertos/FreeRTOS.h" +#include "freertos/event_groups.h" +#include "esp_err.h" + +#include "esp_gmf_element.h" +#include "esp_gmf_pool.h" +#include "esp_gmf_oal_mem.h" +#include "esp_gmf_oal_thread.h" + +#include "esp_gmf_audio_dec.h" +#include "esp_audio_dec_default.h" +#include "esp_audio_dec_reg.h" +#include "esp_gmf_new_databus.h" +#include "esp_gmf_app_setup_peripheral.h" +#include "esp_gmf_audio_helper.h" +#include "esp_gmf_audio_enc.h" +#include "esp_audio_dec_default.h" +#include "gmf_loader_setup_defaults.h" +#include "gmf_audio_play_com.h" + +static const char *TAG = "GMF_AUDIO_PLAY_COM"; + +void play_pause_single_file( esp_gmf_pipeline_handle_t pipe, const char *uri) +{ + ESP_LOGW(TAG, "play_single_file: %s\r\n", uri); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_reset(pipe)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_loading_jobs(pipe)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_set_in_uri(pipe, uri)); + + esp_gmf_element_handle_t dec_el = NULL; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_get_el_by_name(pipe, "aud_dec", &dec_el)); + esp_audio_simple_dec_cfg_t *simple_cfg = (esp_audio_simple_dec_cfg_t *)OBJ_GET_CFG(dec_el); + TEST_ASSERT_NOT_NULL(simple_cfg); + uint32_t type = ESP_AUDIO_TYPE_UNSUPPORT; + esp_gmf_audio_helper_get_audio_type_by_uri(uri, &type); + esp_gmf_info_sound_t info = { + .sample_rates = 48000, + .channels = 2, + .bits = 16, + .format_id = type, + }; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_audio_dec_reconfig_by_sound_info(dec_el, &info)); + ESP_GMF_MEM_SHOW(TAG); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_run(pipe)); + + vTaskDelay(2000 / portTICK_RATE_MS); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_pause(pipe)); + vTaskDelay(1000 / portTICK_RATE_MS); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_resume(pipe)); +} + +void play_loop_single_file(esp_gmf_pipeline_handle_t pipe, esp_audio_type_t type) +{ + ESP_LOGW(TAG, "play_single_file: %d\r\n", type); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_reset(pipe)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_loading_jobs(pipe)); + + // enc reconfig + esp_gmf_element_handle_t enc_handle = NULL; + esp_gmf_pipeline_get_el_by_name(pipe, "aud_enc", &enc_handle); + esp_gmf_info_sound_t info = {0}; + info.format_id = type; + if (type == ESP_AUDIO_TYPE_G711A || type == ESP_AUDIO_TYPE_G711U) { + info.sample_rates = 8000; + info.channels = 2; + info.bits = 16; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_set_in_uri(pipe, "/sdcard/test_8000hz_16bit_2ch_10000ms.pcm")); + } else { + info.sample_rates = 48000; + info.channels = 2; + info.bits = 16; + info.bitrate = 90000; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_set_in_uri(pipe, "/sdcard/test_48000hz_16bit_2ch_10000ms.pcm")); + } + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_audio_enc_reconfig_by_sound_info(enc_handle, &info)); + esp_gmf_pipeline_report_info(pipe, ESP_GMF_INFO_SOUND, &info, sizeof(info)); + + // dec reconfig + esp_gmf_element_handle_t dec_el = NULL; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_get_el_by_name(pipe, "aud_dec", &dec_el)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_audio_dec_reconfig_by_sound_info(dec_el, &info)); + + ESP_GMF_MEM_SHOW(TAG); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_run(pipe)); + + vTaskDelay(2000 / portTICK_RATE_MS); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_pause(pipe)); + vTaskDelay(1000 / portTICK_RATE_MS); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_resume(pipe)); +} + +void gmf_register_audio_all(esp_gmf_pool_handle_t pool) +{ + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, gmf_loader_setup_io_default(pool)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, gmf_loader_setup_audio_codec_default(pool)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, gmf_loader_setup_audio_effects_default(pool)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, gmf_loader_setup_misc_default(pool)); +} + +void gmf_unregister_audio_all(esp_gmf_pool_handle_t pool) +{ + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, gmf_loader_teardown_audio_effects_default(pool)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, gmf_loader_teardown_audio_codec_default(pool)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, gmf_loader_teardown_io_default(pool)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, gmf_loader_teardown_misc_default(pool)); +} + +void gmf_setup_pipeline_out_dev(esp_gmf_pipeline_handle_t pipe) +{ + esp_gmf_io_handle_t io_handle = NULL; + esp_gmf_pipeline_get_out(pipe, &io_handle); + TEST_ASSERT_NOT_NULL(io_handle); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_io_codec_dev_set_dev(io_handle, esp_gmf_app_get_playback_handle())); +} + +void gmf_setup_pipeline_in_dev(esp_gmf_pipeline_handle_t pipe) +{ + esp_gmf_io_handle_t io_handle = NULL; + esp_gmf_pipeline_get_in(pipe, &io_handle); + TEST_ASSERT_NOT_NULL(io_handle); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_io_codec_dev_set_dev(io_handle, esp_gmf_app_get_record_handle())); +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/main/elements/gmf_audio_play_com.h b/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/main/elements/gmf_audio_play_com.h new file mode 100644 index 0000000..3a42698 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/main/elements/gmf_audio_play_com.h @@ -0,0 +1,27 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "esp_gmf_pipeline.h" +#include "esp_gmf_io_codec_dev.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +void play_pause_single_file( esp_gmf_pipeline_handle_t pipe, const char *uri); + +void play_loop_single_file(esp_gmf_pipeline_handle_t pipe, esp_audio_type_t type); +void gmf_register_audio_all(esp_gmf_pool_handle_t pool); +void gmf_unregister_audio_all(esp_gmf_pool_handle_t pool); + +void gmf_setup_pipeline_out_dev(esp_gmf_pipeline_handle_t pipe); +void gmf_setup_pipeline_in_dev(esp_gmf_pipeline_handle_t pipe); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/main/elements/gmf_audio_play_el_test.c b/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/main/elements/gmf_audio_play_el_test.c new file mode 100644 index 0000000..4ca521b --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/main/elements/gmf_audio_play_el_test.c @@ -0,0 +1,1129 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "unity.h" +#include +#include "esp_log.h" +#include "freertos/FreeRTOS.h" +#include "freertos/event_groups.h" +#include "esp_err.h" +#include "esp_gmf_element.h" +#include "esp_gmf_pipeline.h" +#include "esp_gmf_pool.h" +#include "esp_gmf_oal_mem.h" +#include "esp_gmf_oal_thread.h" + +#include "esp_gmf_audio_dec.h" +#include "esp_audio_dec_default.h" +#include "esp_audio_dec_reg.h" +#include "esp_gmf_new_databus.h" +#include "esp_gmf_app_setup_peripheral.h" +#include "esp_gmf_app_unit_test.h" +#include "esp_gmf_audio_helper.h" +#include "gmf_audio_play_com.h" +#include "esp_gmf_io_http.h" + +#ifdef MEDIA_LIB_MEM_TEST +#include "media_lib_adapter.h" +#include "media_lib_mem_trace.h" +#endif /* MEDIA_LIB_MEM_TEST */ + +#define PIPELINE_BLOCK_BIT BIT(0) +#define PIPELINE_BLOCK_BIT2 BIT(1) +#define PIPELINE_BLOCK_BIT3 BIT(2) + +static const char *TAG = "AUDIO_PLAY_ELEMENT_TEST"; + +static const char *file_name = "/sdcard/test.mp3"; +static const char *file_name1 = "/sdcard/test_2.wav"; + +static const char *wav_file_path[] = { + "/sdcard/test_1.wav", + "/sdcard/test_2.wav", + "/sdcard/test_3.wav", +}; + +static const char *dec_file_path[] = { + // Two same ts file to test the reset function + "/sdcard/test.ts", + "/sdcard/test.ts", + "/sdcard/test.aac", + "/sdcard/test.mp3", + "/sdcard/test.amr", + "/sdcard/test.flac", + "/sdcard/test.wav", + "/sdcard/test.m4a", +}; + +#define ESP_GMF_PORT_PAYLOAD_LEN_DEFAULT (4096) + +static esp_err_t _pipeline_event(esp_gmf_event_pkt_t *event, void *ctx) +{ + // The warning messages are used to make the content more noticeable. + ESP_LOGW(TAG, "CB: RECV Pipeline EVT: el:%s-%p, type:%x, sub:%s, payload:%p, size:%d,%p", + "OBJ_GET_TAG(event->from)", event->from, event->type, esp_gmf_event_get_state_str(event->sub), + event->payload, event->payload_size, ctx); + if ((event->sub == ESP_GMF_EVENT_STATE_STOPPED) + || (event->sub == ESP_GMF_EVENT_STATE_FINISHED) + || (event->sub == ESP_GMF_EVENT_STATE_ERROR)) { + if (ctx) { + xEventGroupSetBits((EventGroupHandle_t)ctx, PIPELINE_BLOCK_BIT); + } + } + return 0; +} + +static esp_err_t _pipeline_event2(esp_gmf_event_pkt_t *event, void *ctx) +{ + // The warning messages are used to make the content more noticeable. + ESP_LOGW(TAG, "CB: RECV Pipeline 2 EVT: el:%s-%p, type:%d, sub:%s, payload:%p, size:%d,%p", + "OBJ_GET_TAG(event->from)", event->from, event->type, esp_gmf_event_get_state_str(event->sub), + event->payload, event->payload_size, ctx); + if ((event->sub == ESP_GMF_EVENT_STATE_STOPPED) + || (event->sub == ESP_GMF_EVENT_STATE_FINISHED) + || (event->sub == ESP_GMF_EVENT_STATE_ERROR)) { + if (ctx) { + xEventGroupSetBits((EventGroupHandle_t)ctx, PIPELINE_BLOCK_BIT2); + } + } + return 0; +} + +static esp_err_t _pipeline_event3(esp_gmf_event_pkt_t *event, void *ctx) +{ + // The warning messages are used to make the content more noticeable. + ESP_LOGW(TAG, "CB: RECV Pipeline 3 EVT: el:%s-%p, type:%d, sub:%s, payload:%p, size:%d,%p", + "OBJ_GET_TAG(event->from)", event->from, event->type, esp_gmf_event_get_state_str(event->sub), + event->payload, event->payload_size, ctx); + if ((event->sub == ESP_GMF_EVENT_STATE_STOPPED) + || (event->sub == ESP_GMF_EVENT_STATE_FINISHED) + || (event->sub == ESP_GMF_EVENT_STATE_ERROR)) { + if (ctx) { + xEventGroupSetBits((EventGroupHandle_t)ctx, PIPELINE_BLOCK_BIT3); + } + } + return 0; +} + +TEST_CASE("Create and destroy pipeline", "[ESP_GMF_POOL]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + esp_log_level_set("ESP_GMF_PIPELINE", ESP_LOG_DEBUG); + ESP_GMF_MEM_SHOW(TAG); +#ifdef MEDIA_LIB_MEM_TEST + media_lib_add_default_adapter(); +#endif /* MEDIA_LIB_MEM_TEST */ + esp_gmf_app_setup_codec_dev(NULL); + esp_gmf_pool_handle_t pool = NULL; + esp_gmf_pool_init(&pool); + TEST_ASSERT_NOT_NULL(pool); + gmf_register_audio_all(pool); + ESP_GMF_POOL_SHOW_ITEMS(pool); + // Create the new pipeline + esp_gmf_pipeline_handle_t pipe = NULL; + const char *name[] = {"aud_dec", "aud_rate_cvt"}; + esp_gmf_pool_new_pipeline(pool, "io_file", name, sizeof(name) / sizeof(char *), "io_codec_dev", &pipe); + TEST_ASSERT_NOT_NULL(pipe); + esp_gmf_pipeline_destroy(pipe); + gmf_unregister_audio_all(pool); + esp_gmf_pool_deinit(pool); + esp_gmf_app_teardown_codec_dev(); + ESP_GMF_MEM_SHOW(TAG); +} + +TEST_CASE("Audio Play, ENC and DEC Loop TEST, [FILE->enc->dec->IIS]", "[ESP_GMF_POOL]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + esp_log_level_set("ESP_GMF_PIPELINE", ESP_LOG_DEBUG); + esp_log_level_set("ESP_GMF_POOL", ESP_LOG_DEBUG); + ESP_GMF_MEM_SHOW(TAG); + esp_audio_type_t type_list[] = {ESP_AUDIO_TYPE_AAC, ESP_AUDIO_TYPE_G711A, ESP_AUDIO_TYPE_G711U, + ESP_AUDIO_TYPE_OPUS, ESP_AUDIO_TYPE_ADPCM, ESP_AUDIO_TYPE_PCM, + ESP_AUDIO_TYPE_SBC, ESP_AUDIO_TYPE_LC3}; + esp_gmf_app_setup_codec_dev(NULL); + void *sdcard_handle = NULL; + esp_gmf_app_setup_sdcard(&sdcard_handle); + + EventGroupHandle_t pipe_sync_evt = xEventGroupCreate(); + ESP_GMF_NULL_CHECK(TAG, pipe_sync_evt, return); +#ifdef MEDIA_LIB_MEM_TEST + media_lib_add_default_adapter(); +#endif /* MEDIA_LIB_MEM_TEST */ + esp_gmf_pool_handle_t pool = NULL; + esp_gmf_pool_init(&pool); + TEST_ASSERT_NOT_NULL(pool); + gmf_register_audio_all(pool); + ESP_GMF_MEM_SHOW(TAG); + ESP_GMF_POOL_SHOW_ITEMS(pool); + + esp_gmf_pipeline_handle_t pipe = NULL; + const char *name[] = {"aud_enc", "aud_dec", "aud_rate_cvt", "aud_ch_cvt"}; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pool_new_pipeline(pool, "io_file", name, sizeof(name) / sizeof(char *), "io_codec_dev", &pipe)); + TEST_ASSERT_NOT_NULL(pipe); + gmf_setup_pipeline_out_dev(pipe); + esp_gmf_task_cfg_t cfg = DEFAULT_ESP_GMF_TASK_CONFIG(); + cfg.ctx = NULL; + cfg.cb = NULL; + cfg.thread.stack = 50 * 1024; + esp_gmf_task_handle_t work_task = NULL; + esp_gmf_task_init(&cfg, &work_task); + TEST_ASSERT_NOT_NULL(work_task); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_bind_task(pipe, work_task)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_set_event(pipe, _pipeline_event, pipe_sync_evt)); + ESP_GMF_MEM_SHOW(TAG); + for (int i = 0; i < sizeof(type_list) / sizeof(esp_audio_type_t); ++i) { + play_loop_single_file(pipe, type_list[i]); + xEventGroupWaitBits(pipe_sync_evt, PIPELINE_BLOCK_BIT, pdTRUE, pdFALSE, portMAX_DELAY); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_stop(pipe)); + } + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_deinit(work_task)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_destroy(pipe)); + gmf_unregister_audio_all(pool); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pool_deinit(pool)); + vEventGroupDelete(pipe_sync_evt); +#ifdef MEDIA_LIB_MEM_TEST + media_lib_stop_mem_trace(); +#endif /* MEDIA_LIB_MEM_TEST */ + vTaskDelay(1000 / portTICK_RATE_MS); + esp_gmf_app_teardown_sdcard(sdcard_handle); + esp_gmf_app_teardown_codec_dev(); + ESP_GMF_MEM_SHOW(TAG); +} + +TEST_CASE("Audio Play, One Pipe, [FILE->dec->resample->IIS]", "[ESP_GMF_POOL]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + esp_log_level_set("ESP_GMF_PIPELINE", ESP_LOG_DEBUG); + esp_log_level_set("ESP_GMF_ELEMENT", ESP_LOG_DEBUG); + esp_log_level_set("ESP_GMF_IO", ESP_LOG_DEBUG); + ESP_GMF_MEM_SHOW(TAG); + esp_gmf_app_setup_codec_dev(NULL); + void *sdcard_handle = NULL; + esp_gmf_app_setup_sdcard(&sdcard_handle); +#ifdef MEDIA_LIB_MEM_TEST + media_lib_add_default_adapter(); +#endif /* MEDIA_LIB_MEM_TEST */ + EventGroupHandle_t pipe_sync_evt = xEventGroupCreate(); + ESP_GMF_NULL_CHECK(TAG, pipe_sync_evt, return); + + esp_gmf_pool_handle_t pool = NULL; + esp_gmf_pool_init(&pool); + TEST_ASSERT_NOT_NULL(pool); + gmf_register_audio_all(pool); + ESP_GMF_POOL_SHOW_ITEMS(pool); + + esp_gmf_pipeline_handle_t pipe = NULL; + const char *name[] = {"aud_dec", "aud_rate_cvt"}; + esp_gmf_pool_new_pipeline(pool, "io_file", name, sizeof(name) / sizeof(char *), "io_codec_dev", &pipe); + TEST_ASSERT_NOT_NULL(pipe); + gmf_setup_pipeline_out_dev(pipe); + esp_gmf_element_handle_t dec_el = NULL; + esp_gmf_pipeline_get_el_by_name(pipe, "aud_dec", &dec_el); + esp_gmf_info_sound_t info = { + .format_id = ESP_AUDIO_SIMPLE_DEC_TYPE_MP3, + }; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_audio_dec_reconfig_by_sound_info(dec_el, &info)); + + esp_gmf_task_cfg_t cfg = DEFAULT_ESP_GMF_TASK_CONFIG(); + cfg.ctx = NULL; + cfg.cb = NULL; + esp_gmf_task_handle_t work_task = NULL; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_init(&cfg, &work_task)); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_bind_task(pipe, work_task)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_loading_jobs(pipe)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_set_event(pipe, _pipeline_event, pipe_sync_evt)); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_set_in_uri(pipe, file_name)); + ESP_GMF_MEM_SHOW(TAG); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_run(pipe)); + + vTaskDelay(2000 / portTICK_RATE_MS); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_pause(pipe)); + vTaskDelay(1000 / portTICK_RATE_MS); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_resume(pipe)); + + // Wait to finished or got error + xEventGroupWaitBits(pipe_sync_evt, PIPELINE_BLOCK_BIT, pdTRUE, pdFALSE, portMAX_DELAY); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_stop(pipe)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_deinit(work_task)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_destroy(pipe)); + gmf_unregister_audio_all(pool); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pool_deinit(pool)); + vEventGroupDelete(pipe_sync_evt); + +#ifdef MEDIA_LIB_MEM_TEST + media_lib_stop_mem_trace(); +#endif /* MEDIA_LIB_MEM_TEST */ + esp_gmf_app_teardown_sdcard(sdcard_handle); + esp_gmf_app_teardown_codec_dev(); + vTaskDelay(1000 / portTICK_RATE_MS); + ESP_GMF_MEM_SHOW(TAG); +} + +TEST_CASE("Audio Play, multiple file with One Pipe, [FILE->dec->resample->IIS]", "[ESP_GMF_POOL]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + esp_log_level_set("ESP_GMF_PIPELINE", ESP_LOG_DEBUG); + esp_log_level_set("ESP_GMF_POOL", ESP_LOG_DEBUG); + ESP_GMF_MEM_SHOW(TAG); + esp_gmf_app_setup_codec_dev(NULL); + void *sdcard_handle = NULL; + esp_gmf_app_setup_sdcard(&sdcard_handle); + EventGroupHandle_t pipe_sync_evt = xEventGroupCreate(); + ESP_GMF_NULL_CHECK(TAG, pipe_sync_evt, return); +#ifdef MEDIA_LIB_MEM_TEST + media_lib_add_default_adapter(); +#endif /* MEDIA_LIB_MEM_TEST */ + esp_gmf_pool_handle_t pool = NULL; + esp_gmf_pool_init(&pool); + TEST_ASSERT_NOT_NULL(pool); + gmf_register_audio_all(pool); + + ESP_GMF_MEM_SHOW(TAG); + ESP_GMF_POOL_SHOW_ITEMS(pool); + + esp_gmf_pipeline_handle_t pipe = NULL; + const char *name[] = {"aud_dec", "aud_rate_cvt", "aud_ch_cvt"}; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pool_new_pipeline(pool, "io_file", name, sizeof(name) / sizeof(char *), "io_codec_dev", &pipe)); + TEST_ASSERT_NOT_NULL(pipe); + gmf_setup_pipeline_out_dev(pipe); + esp_gmf_task_cfg_t cfg = DEFAULT_ESP_GMF_TASK_CONFIG(); + cfg.ctx = NULL; + cfg.cb = NULL; + esp_gmf_task_handle_t work_task = NULL; + esp_gmf_task_init(&cfg, &work_task); + TEST_ASSERT_NOT_NULL(work_task); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_bind_task(pipe, work_task)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_set_event(pipe, _pipeline_event, pipe_sync_evt)); + ESP_GMF_MEM_SHOW(TAG); + for (int i = 0; i < sizeof(dec_file_path) / sizeof(char *); ++i) { + play_pause_single_file(pipe, dec_file_path[i]); + xEventGroupWaitBits(pipe_sync_evt, PIPELINE_BLOCK_BIT, pdTRUE, pdFALSE, portMAX_DELAY); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_stop(pipe)); + } + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_deinit(work_task)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_destroy(pipe)); + gmf_unregister_audio_all(pool); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pool_deinit(pool)); + vEventGroupDelete(pipe_sync_evt); +#ifdef MEDIA_LIB_MEM_TEST + media_lib_stop_mem_trace(); +#endif /* MEDIA_LIB_MEM_TEST */ + vTaskDelay(1000 / portTICK_RATE_MS); + esp_gmf_app_teardown_sdcard(sdcard_handle); + esp_gmf_app_teardown_codec_dev(); + ESP_GMF_MEM_SHOW(TAG); +} + +TEST_CASE("Audio Play, two in pipe use same task, [file->dec]->rb->[resample+IIS]", "[ESP_GMF_POOL]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + esp_log_level_set("ESP_GMF_PIPELINE", ESP_LOG_DEBUG); + esp_log_level_set("ESP_GMF_POOL", ESP_LOG_DEBUG); + + ESP_GMF_MEM_SHOW(TAG); + esp_gmf_app_setup_codec_dev(NULL); + void *sdcard_handle = NULL; + esp_gmf_app_setup_sdcard(&sdcard_handle); + EventGroupHandle_t pipe_sync_evt = xEventGroupCreate(); + ESP_GMF_NULL_CHECK(TAG, pipe_sync_evt, return); +#ifdef MEDIA_LIB_MEM_TEST + media_lib_add_default_adapter(); +#endif /* MEDIA_LIB_MEM_TEST */ + esp_gmf_pool_handle_t pool = NULL; + esp_gmf_pool_init(&pool); + TEST_ASSERT_NOT_NULL(pool); + gmf_register_audio_all(pool); + ESP_GMF_MEM_SHOW(TAG); + ESP_GMF_POOL_SHOW_ITEMS(pool); + + esp_gmf_pipeline_handle_t pipe_in1, pipe_in2; + const char *name[] = {"aud_dec"}; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pool_new_pipeline(pool, "io_file", name, sizeof(name) / sizeof(char *), NULL, &pipe_in1)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pool_new_pipeline(pool, "io_file", name, sizeof(name) / sizeof(char *), NULL, &pipe_in2)); + TEST_ASSERT_NOT_NULL(pipe_in1); + TEST_ASSERT_NOT_NULL(pipe_in2); + + const char *name2[] = {"aud_rate_cvt"}; + esp_gmf_pipeline_handle_t pipe_out = NULL; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pool_new_pipeline(pool, NULL, name2, sizeof(name2) / sizeof(char *), "io_codec_dev", &pipe_out)); + TEST_ASSERT_NOT_NULL(pipe_out); + gmf_setup_pipeline_out_dev(pipe_out); + esp_gmf_db_handle_t db = NULL; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_db_new_ringbuf(20, 1024, &db)); + esp_gmf_port_handle_t out_port1 = NEW_ESP_GMF_PORT_OUT_BYTE(esp_gmf_db_acquire_write, esp_gmf_db_release_write, NULL, db, + ESP_GMF_PORT_PAYLOAD_LEN_DEFAULT, portMAX_DELAY); + + esp_gmf_port_handle_t out_port2 = NEW_ESP_GMF_PORT_OUT_BYTE(esp_gmf_db_acquire_write, esp_gmf_db_release_write, NULL, db, + ESP_GMF_PORT_PAYLOAD_LEN_DEFAULT, portMAX_DELAY); + + esp_gmf_port_handle_t in_port = NEW_ESP_GMF_PORT_IN_BYTE(esp_gmf_db_acquire_read, esp_gmf_db_release_read, NULL, db, + ESP_GMF_PORT_PAYLOAD_LEN_DEFAULT, portMAX_DELAY); + esp_gmf_pipeline_connect_pipe(pipe_in1, "aud_dec", out_port1, pipe_out, "aud_rate_cvt", in_port); + + ESP_LOGW(TAG, "Starting play the FIRST file."); + esp_gmf_task_cfg_t cfg = DEFAULT_ESP_GMF_TASK_CONFIG(); + cfg.ctx = NULL; + cfg.cb = NULL; + cfg.name = "aud_dec"; + esp_gmf_task_handle_t work_task_in1 = NULL; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_init(&cfg, &work_task_in1)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_bind_task(pipe_in1, work_task_in1)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_set_event(pipe_in1, _pipeline_event, pipe_sync_evt)); + + cfg.name = "resample"; + cfg.thread.core = 1; + cfg.thread.prio = 6; + esp_gmf_task_handle_t work_task2 = NULL; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_init(&cfg, &work_task2)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_bind_task(pipe_out, work_task2)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_set_event(pipe_out, _pipeline_event2, pipe_sync_evt)); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_set_in_uri(pipe_in1, (char *)wav_file_path[0])); + + esp_gmf_element_handle_t dec_el = NULL; + esp_gmf_pipeline_get_el_by_name(pipe_in1, "aud_dec", &dec_el); + uint32_t type = ESP_AUDIO_TYPE_UNSUPPORT; + esp_gmf_audio_helper_get_audio_type_by_uri(wav_file_path[0], &type); + esp_gmf_info_sound_t info = { + .format_id = type, + }; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_audio_dec_reconfig_by_sound_info(dec_el, &info)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_loading_jobs(pipe_in1)); + ESP_GMF_MEM_SHOW(TAG); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_run(pipe_in1)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_run(pipe_out)); + + ESP_LOGW(TAG, "Waiting for the FIRST file to finish..."); + xEventGroupWaitBits(pipe_sync_evt, PIPELINE_BLOCK_BIT2, pdTRUE, pdFALSE, portMAX_DELAY); + + ESP_LOGW(TAG, "Starting play the SECOND file."); + esp_gmf_audio_element_handle_t el = NULL; + esp_gmf_pipeline_get_el_by_name(pipe_in2, "aud_dec", &el); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_element_register_out_port(el, out_port2)); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_reset(work_task_in1)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_bind_task(pipe_in2, work_task_in1)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_set_event(pipe_in2, _pipeline_event, pipe_sync_evt)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_set_in_uri(pipe_in2, (char *)wav_file_path[1])); + dec_el = NULL; + esp_gmf_pipeline_get_el_by_name(pipe_in2, "aud_dec", &dec_el); + esp_gmf_audio_helper_get_audio_type_by_uri(wav_file_path[1], &info.format_id); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_audio_dec_reconfig_by_sound_info(dec_el, &info)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_loading_jobs(pipe_in2)); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_reg_event_recipient(pipe_in2, pipe_out)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_db_reset(db)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_reset(pipe_out)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_loading_jobs(pipe_out)); + + ESP_GMF_MEM_SHOW(TAG); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_run(pipe_in2)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_run(pipe_out)); + + ESP_LOGW(TAG, "Waiting for the SECOND file to finish..."); + xEventGroupWaitBits(pipe_sync_evt, PIPELINE_BLOCK_BIT2, pdTRUE, pdFALSE, portMAX_DELAY); + ESP_LOGE(TAG, "It's finished,%s-%d", __func__, __LINE__); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_stop(pipe_in1)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_stop(pipe_in2)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_stop(pipe_out)); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_deinit(work_task_in1)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_deinit(work_task2)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_destroy(pipe_out)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_destroy(pipe_in1)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_destroy(pipe_in2)); + gmf_unregister_audio_all(pool); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pool_deinit(pool)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_db_deinit(db)); + vEventGroupDelete(pipe_sync_evt); + ESP_GMF_MEM_SHOW(TAG); + vTaskDelay(1000 / portTICK_RATE_MS); + esp_gmf_app_teardown_sdcard(sdcard_handle); + esp_gmf_app_teardown_codec_dev(); +#ifdef MEDIA_LIB_MEM_TEST + media_lib_stop_mem_trace(); +#endif /* MEDIA_LIB_MEM_TEST */ + vTaskDelay(1000 / portTICK_RATE_MS); + ESP_GMF_MEM_SHOW(TAG); +} + +TEST_CASE("Audio Play, One Pipe, [HTTP->dec->resample->IIS]", "[ESP_GMF_POOL][leaks=10000]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + esp_log_level_set("ESP_GMF_PIPELINE", ESP_LOG_DEBUG); + ESP_GMF_MEM_SHOW(TAG); + esp_gmf_app_test_case_uses_tcpip(); + esp_gmf_app_wifi_connect(); + esp_gmf_app_setup_codec_dev(NULL); +#ifdef MEDIA_LIB_MEM_TEST + media_lib_add_default_adapter(); + void *sdcard_handle = NULL; + esp_gmf_app_setup_sdcard(&sdcard_handle); +#endif /* MEDIA_LIB_MEM_TEST */ + ESP_GMF_MEM_SHOW(TAG); + esp_gmf_pool_handle_t pool = NULL; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pool_init(&pool)); + TEST_ASSERT_NOT_NULL(pool); + gmf_register_audio_all(pool); + ESP_GMF_MEM_SHOW(TAG); + ESP_GMF_POOL_SHOW_ITEMS(pool); + esp_gmf_info_sound_t info = {0}; + + ESP_LOGI(TAG, "---- Test 1 for HTTP pipeline reset playing ----"); + // Create the new elements + esp_gmf_pipeline_handle_t pipe = NULL; + const char *uri = "https://dl.espressif.com/dl/audio/gs-16b-2c-44100hz.mp3"; + const char *name[] = {"aud_dec", "aud_rate_cvt", "aud_ch_cvt"}; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pool_new_pipeline(pool, "io_http", name, sizeof(name) / sizeof(char *), "io_codec_dev", &pipe)); + TEST_ASSERT_NOT_NULL(pipe); + gmf_setup_pipeline_out_dev(pipe); + EventGroupHandle_t pipe_sync_evt = xEventGroupCreate(); + ESP_GMF_NULL_CHECK(TAG, pipe_sync_evt, return); + esp_gmf_task_cfg_t cfg = DEFAULT_ESP_GMF_TASK_CONFIG(); + cfg.ctx = NULL; + cfg.cb = NULL; + esp_gmf_task_handle_t work_task = NULL; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_init(&cfg, &work_task)); + TEST_ASSERT_NOT_NULL(pipe); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_bind_task(pipe, work_task)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_set_event(pipe, _pipeline_event, pipe_sync_evt)); + + ESP_GMF_MEM_SHOW(TAG); + + for (int i = 0; i < 3; ++i) { + printf("\r\nIndex:%d\r\n", i); + ESP_GMF_MEM_SHOW(TAG); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_loading_jobs(pipe)); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_set_in_uri(pipe, uri)); + esp_gmf_element_handle_t dec_el = NULL; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_get_el_by_name(pipe, "aud_dec", &dec_el)); + esp_gmf_audio_helper_get_audio_type_by_uri(uri, &info.format_id); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_audio_dec_reconfig_by_sound_info(dec_el, &info)); + + ESP_GMF_MEM_SHOW(TAG); + esp_gmf_task_set_timeout(pipe->thread, 5000); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_run(pipe)); + esp_gmf_pipeline_list_el(pipe); + vTaskDelay(2000 / portTICK_RATE_MS); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_pause(pipe)); + vTaskDelay(1000 / portTICK_RATE_MS); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_resume(pipe)); + + // Wait to finished or got error + xEventGroupWaitBits(pipe_sync_evt, PIPELINE_BLOCK_BIT, pdTRUE, pdFALSE, portMAX_DELAY); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_stop(pipe)); + + esp_gmf_pipeline_reset(pipe); + } + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_deinit(work_task)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_destroy(pipe)); + vEventGroupDelete(pipe_sync_evt); + ESP_GMF_MEM_SHOW(TAG); + + ESP_LOGI(TAG, "---- Test 2 for create destroy HTTP pipeline multiple times ----"); + + for (int i = 0; i < 3; ++i) { + printf("\r\nIndex:%d\r\n", i); + ESP_GMF_MEM_SHOW(TAG); + EventGroupHandle_t pipe_sync_evt = xEventGroupCreate(); + ESP_GMF_NULL_CHECK(TAG, pipe_sync_evt, return); + + // Create the new elements + esp_gmf_pipeline_handle_t pipe = NULL; + const char *uri = "https://dl.espressif.com/dl/audio/gs-16b-2c-44100hz.mp3"; + const char *name[] = {"aud_dec", "aud_rate_cvt", "aud_ch_cvt"}; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pool_new_pipeline(pool, "io_http", name, sizeof(name) / sizeof(char *), "io_codec_dev", &pipe)); + TEST_ASSERT_NOT_NULL(pipe); + gmf_setup_pipeline_out_dev(pipe); + esp_gmf_task_cfg_t cfg = DEFAULT_ESP_GMF_TASK_CONFIG(); + cfg.ctx = NULL; + cfg.cb = NULL; + esp_gmf_task_handle_t work_task = NULL; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_init(&cfg, &work_task)); + TEST_ASSERT_NOT_NULL(pipe); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_bind_task(pipe, work_task)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_loading_jobs(pipe)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_set_event(pipe, _pipeline_event, pipe_sync_evt)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_set_in_uri(pipe, uri)); + esp_gmf_element_handle_t dec_el = NULL; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_get_el_by_name(pipe, "aud_dec", &dec_el)); + info.format_id = ESP_AUDIO_SIMPLE_DEC_TYPE_MP3; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_audio_dec_reconfig_by_sound_info(dec_el, &info)); + + ESP_GMF_MEM_SHOW(TAG); + esp_gmf_task_set_timeout(pipe->thread, 5000); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_run(pipe)); + esp_gmf_pipeline_list_el(pipe); + vTaskDelay(2000 / portTICK_RATE_MS); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_pause(pipe)); + vTaskDelay(1000 / portTICK_RATE_MS); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_resume(pipe)); + + // Wait to finished or got error + xEventGroupWaitBits(pipe_sync_evt, PIPELINE_BLOCK_BIT, pdTRUE, pdFALSE, portMAX_DELAY); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_stop(pipe)); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_deinit(work_task)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_destroy(pipe)); + + vEventGroupDelete(pipe_sync_evt); + } + +#ifdef MEDIA_LIB_MEM_TEST + media_lib_stop_mem_trace(); + esp_gmf_app_teardown_sdcard(sdcard_handle); +#endif /* MEDIA_LIB_MEM_TEST */ + esp_gmf_app_teardown_codec_dev(); + gmf_unregister_audio_all(pool); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pool_deinit(pool)); + esp_gmf_app_wifi_disconnect(); + vTaskDelay(1000 / portTICK_RATE_MS); + ESP_GMF_MEM_SHOW(TAG); +} + +TEST_CASE("Audio Play, Two Pipe, [HTTP->dec]--RB-->[resample->IIS]", "[ESP_GMF_POOL][leaks=10000]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + esp_log_level_set("ESP_GMF_PIPELINE", ESP_LOG_DEBUG); + esp_log_level_set("ESP_GMF_POOL", ESP_LOG_DEBUG); + esp_gmf_app_test_case_uses_tcpip(); + ESP_GMF_MEM_SHOW(TAG); + esp_gmf_app_wifi_connect(); + esp_gmf_app_setup_codec_dev(NULL); + ESP_GMF_MEM_SHOW(TAG); +#ifdef MEDIA_LIB_MEM_TEST + media_lib_add_default_adapter(); +#endif /* MEDIA_LIB_MEM_TEST */ + EventGroupHandle_t pipe_sync_evt = xEventGroupCreate(); + ESP_GMF_NULL_CHECK(TAG, pipe_sync_evt, return); + + esp_gmf_pool_handle_t pool = NULL; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pool_init(&pool)); + TEST_ASSERT_NOT_NULL(pool); + gmf_register_audio_all(pool); + ESP_GMF_MEM_SHOW(TAG); + ESP_GMF_POOL_SHOW_ITEMS(pool); + + // Create the new elements + esp_gmf_pipeline_handle_t pipe_in = NULL; + esp_gmf_pipeline_handle_t pipe_out = NULL; + const char *uri = "https://dl.espressif.com/dl/audio/ff-16b-2c-16000hz.mp3"; + const char *name_in[] = {"aud_dec"}; + const char *name_out[] = {"aud_rate_cvt"}; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pool_new_pipeline(pool, "io_http", name_in, sizeof(name_in) / sizeof(char *), NULL, &pipe_in)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pool_new_pipeline(pool, NULL, name_out, sizeof(name_out) / sizeof(char *), "io_codec_dev", &pipe_out)); + TEST_ASSERT_NOT_NULL(pipe_in); + TEST_ASSERT_NOT_NULL(pipe_out); + gmf_setup_pipeline_out_dev(pipe_out); + esp_gmf_db_handle_t db = NULL; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_db_new_ringbuf(15, 1024, &db)); + TEST_ASSERT_NOT_NULL(db); + esp_gmf_port_handle_t out_port = NEW_ESP_GMF_PORT_OUT_BYTE(esp_gmf_db_acquire_write, esp_gmf_db_release_write, esp_gmf_db_deinit, db, + ESP_GMF_PORT_PAYLOAD_LEN_DEFAULT, portMAX_DELAY); + esp_gmf_port_handle_t in_port = NEW_ESP_GMF_PORT_IN_BYTE(esp_gmf_db_acquire_read, esp_gmf_db_release_read, NULL, db, + ESP_GMF_PORT_PAYLOAD_LEN_DEFAULT, portMAX_DELAY); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_connect_pipe(pipe_in, "aud_dec", out_port, pipe_out, "aud_rate_cvt", in_port)); + + esp_gmf_task_cfg_t cfg = DEFAULT_ESP_GMF_TASK_CONFIG(); + cfg.ctx = NULL; + cfg.cb = NULL; + cfg.name = "DECODER"; + esp_gmf_task_handle_t work_task_in = NULL; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_init(&cfg, &work_task_in)); + TEST_ASSERT_NOT_NULL(work_task_in); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_bind_task(pipe_in, work_task_in)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_loading_jobs(pipe_in)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_set_event(pipe_in, _pipeline_event, pipe_sync_evt)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_set_in_uri(pipe_in, uri)); + esp_gmf_element_handle_t dec_el = NULL; + esp_gmf_pipeline_get_el_by_name(pipe_in, "aud_dec", &dec_el); + esp_gmf_info_sound_t info = { + .format_id = ESP_AUDIO_SIMPLE_DEC_TYPE_MP3, + }; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_audio_dec_reconfig_by_sound_info(dec_el, &info)); + + esp_gmf_task_handle_t work_task_out = NULL; + cfg.name = "RES_IIS"; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_init(&cfg, &work_task_out)); + TEST_ASSERT_NOT_NULL(work_task_out); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_bind_task(pipe_out, work_task_out)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_loading_jobs(pipe_out)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_set_event(pipe_out, _pipeline_event2, pipe_sync_evt)); + + ESP_GMF_MEM_SHOW(TAG); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_run(pipe_in)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_run(pipe_out)); + + vTaskDelay(2000 / portTICK_RATE_MS); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_pause(pipe_in)); + vTaskDelay(1000 / portTICK_RATE_MS); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_resume(pipe_in)); + + vTaskDelay(10000 / portTICK_RATE_MS); + xEventGroupWaitBits(pipe_sync_evt, PIPELINE_BLOCK_BIT, pdTRUE, pdFALSE, portMAX_DELAY); + esp_gmf_db_done_write(db); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_stop(pipe_in)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_stop(pipe_out)); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_deinit(work_task_in)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_deinit(work_task_out)); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_destroy(pipe_in)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_destroy(pipe_out)); + gmf_unregister_audio_all(pool); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pool_deinit(pool)); + vEventGroupDelete(pipe_sync_evt); +#ifdef MEDIA_LIB_MEM_TEST + media_lib_stop_mem_trace(); +#endif /* MEDIA_LIB_MEM_TEST */ + esp_gmf_app_teardown_codec_dev(); + esp_gmf_app_wifi_disconnect(); + vTaskDelay(1000 / portTICK_RATE_MS); + ESP_GMF_MEM_SHOW(TAG); +} + +/*** +// No gap loop playing case +// The pipeline diagram as follow + ++----------------------+ +| PipeIn1: File+decoder +---+ ++----------------------+ | +--------------------------+ + +-- RB --> Pipe out: Resample + IIS | ++----------------------+ | +--------------------------+ +| PipeIn2: File+decoder +---+ ++----------------------+ + +***/ +esp_gmf_pipeline_handle_t pipe_in1, pipe_in2; +esp_err_t _loop_play_event(esp_gmf_event_pkt_t *event, void *ctx) +{ + ESP_LOGW(TAG, "CB:Loop Play, Pipeline EVT: %s-%p, type:%d, sub:%s, payload:%p, size:%d,%p", + OBJ_GET_TAG(event->from), event->from, event->type, esp_gmf_event_get_state_str(event->sub), + event->payload, event->payload_size, ctx); + if (event->sub == ESP_GMF_EVENT_STATE_FINISHED) { + if (event->from == pipe_in1) { + xEventGroupSetBits((EventGroupHandle_t)ctx, PIPELINE_BLOCK_BIT); + } else if (event->from == pipe_in2) { + xEventGroupSetBits((EventGroupHandle_t)ctx, PIPELINE_BLOCK_BIT2); + } + } + return 0; +} + +TEST_CASE("Audio Play, loop with no gap, [file->dec]->rb->[resample+IIS]", "[ESP_GMF_POOL]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + ESP_GMF_MEM_SHOW(TAG); + esp_gmf_app_setup_codec_dev(NULL); + void *sdcard_handle = NULL; + esp_gmf_app_setup_sdcard(&sdcard_handle); + +#ifdef MEDIA_LIB_MEM_TEST + media_lib_add_default_adapter(); +#endif /* MEDIA_LIB_MEM_TEST */ + EventGroupHandle_t pipe_sync_evt = xEventGroupCreate(); + ESP_GMF_NULL_CHECK(TAG, pipe_sync_evt, return); + esp_gmf_port_handle_t out_port1, out_port2; + esp_gmf_db_handle_t db = NULL; + esp_gmf_pool_handle_t pool = NULL; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pool_init(&pool)); + TEST_ASSERT_NOT_NULL(pool); + gmf_register_audio_all(pool); + ESP_GMF_POOL_SHOW_ITEMS(pool); + + const char *name[] = {"aud_dec"}; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pool_new_pipeline(pool, "io_file", name, sizeof(name) / sizeof(char *), NULL, &pipe_in1)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pool_new_pipeline(pool, "io_file", name, sizeof(name) / sizeof(char *), NULL, &pipe_in2)); + TEST_ASSERT_NOT_NULL(pipe_in1); + TEST_ASSERT_NOT_NULL(pipe_in2); + + const char *name2[] = {"aud_rate_cvt"}; + esp_gmf_pipeline_handle_t pipe_out = NULL; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pool_new_pipeline(pool, NULL, name2, sizeof(name2) / sizeof(char *), "io_codec_dev", &pipe_out)); + TEST_ASSERT_NOT_NULL(pipe_out); + gmf_setup_pipeline_out_dev(pipe_out); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_db_new_ringbuf(20, 1024, &db)); + TEST_ASSERT_NOT_NULL(db); + out_port1 = NEW_ESP_GMF_PORT_OUT_BYTE(esp_gmf_db_acquire_write, esp_gmf_db_release_write, NULL, db, + ESP_GMF_PORT_PAYLOAD_LEN_DEFAULT, portMAX_DELAY); + out_port2 = NEW_ESP_GMF_PORT_OUT_BYTE(esp_gmf_db_acquire_write, esp_gmf_db_release_write, NULL, db, + ESP_GMF_PORT_PAYLOAD_LEN_DEFAULT, portMAX_DELAY); + + esp_gmf_port_handle_t in_port = NEW_ESP_GMF_PORT_IN_BYTE(esp_gmf_db_acquire_read, esp_gmf_db_release_read, NULL, db, + ESP_GMF_PORT_PAYLOAD_LEN_DEFAULT, portMAX_DELAY); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_connect_pipe(pipe_in1, "aud_dec", out_port1, pipe_out, "aud_rate_cvt", in_port)); + + esp_gmf_task_cfg_t cfg = DEFAULT_ESP_GMF_TASK_CONFIG(); + cfg.ctx = NULL; + cfg.cb = NULL; + cfg.name = "aud_dec"; + esp_gmf_task_handle_t work_task_in1 = NULL; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_init(&cfg, &work_task_in1)); + TEST_ASSERT_NOT_NULL(work_task_in1); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_bind_task(pipe_in1, work_task_in1)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_set_event(pipe_in1, _loop_play_event, pipe_sync_evt)); + + esp_gmf_task_handle_t work_task_in2 = NULL; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_init(&cfg, &work_task_in2)); + TEST_ASSERT_NOT_NULL(work_task_in2); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_bind_task(pipe_in2, work_task_in2)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_set_event(pipe_in2, _loop_play_event, pipe_sync_evt)); + + esp_gmf_audio_element_handle_t el = NULL; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_get_el_by_name(pipe_in2, "aud_dec", &el)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_element_register_out_port(el, out_port2)); + + cfg.name = "res_iis"; + cfg.thread.core = 1; + cfg.thread.prio = 1; + esp_gmf_task_handle_t work_task2 = NULL; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_init(&cfg, &work_task2)); + TEST_ASSERT_NOT_NULL(work_task2); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_bind_task(pipe_out, work_task2)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_set_event(pipe_out, _pipeline_event2, NULL)); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_set_in_uri(pipe_in1, file_name)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_set_in_uri(pipe_in2, file_name1)); + + esp_gmf_element_handle_t dec_el = NULL; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_get_el_by_name(pipe_in1, "aud_dec", &dec_el)); + esp_gmf_info_sound_t info = {0}; + esp_gmf_audio_helper_get_audio_type_by_uri(file_name, &info.format_id); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_audio_dec_reconfig_by_sound_info(dec_el, &info)); + + dec_el = NULL; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_get_el_by_name(pipe_in2, "aud_dec", &dec_el)); + esp_gmf_audio_helper_get_audio_type_by_uri(file_name1, &info.format_id); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_audio_dec_reconfig_by_sound_info(dec_el, &info)); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_loading_jobs(pipe_in1)); + ESP_GMF_MEM_SHOW(TAG); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_run(pipe_in1)); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_loading_jobs(pipe_out)); + ESP_GMF_MEM_SHOW(TAG); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_run(pipe_out)); + + int loop_play_times = 0; + while (1) { + // Wait for pipeline auto stopped event after done then auto start next input pipeline (pipe_in2/pipe_in1) + // Loop 2 cycles and quit + int bits = xEventGroupWaitBits(pipe_sync_evt, PIPELINE_BLOCK_BIT | PIPELINE_BLOCK_BIT2, + pdTRUE, pdFALSE, portMAX_DELAY); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_db_reset_done_write(db)); + if (bits & PIPELINE_BLOCK_BIT) { + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_port_clean_payload_done(out_port1)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_reset(pipe_in2)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_loading_jobs(pipe_in2)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_run(pipe_in2)); + } + if (bits & PIPELINE_BLOCK_BIT2) { + loop_play_times++; + if (loop_play_times >= 2) { + break; + } + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_port_clean_payload_done(out_port2)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_reset(pipe_in1)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_loading_jobs(pipe_in1)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_run(pipe_in1)); + } + } + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_stop(pipe_in1)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_stop(pipe_in2)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_stop(pipe_out)); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_deinit(work_task_in1)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_deinit(work_task_in2)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_deinit(work_task2)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_destroy(pipe_out)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_destroy(pipe_in1)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_destroy(pipe_in2)); + gmf_unregister_audio_all(pool); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pool_deinit(pool)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_db_deinit(db)); + vEventGroupDelete(pipe_sync_evt); +#ifdef MEDIA_LIB_MEM_TEST + media_lib_stop_mem_trace(); +#endif /* MEDIA_LIB_MEM_TEST */ + esp_gmf_app_teardown_sdcard(sdcard_handle); + esp_gmf_app_teardown_codec_dev(); + vTaskDelay(1000 / portTICK_RATE_MS); + ESP_GMF_MEM_SHOW(TAG); +} + +/*** +// Test copier with two pipeline + ++-------------------------------------------------------+ +| Pipe: file-->dec-->copier-->resample-->i2s | ++---------------------+---------------------------------+ + | + | +-------------------------+ + +- RB ->+ Pipe2: resample-->file | + +-------------------------+ +***/ +TEST_CASE("Copier, 2 pipeline test, One pipeline play file to I2S, another save to file", "[ESP_GMF_POOL]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + // esp_log_level_set("ESP_GMF_PIPELINE", ESP_LOG_DEBUG); + esp_log_level_set("ESP_GMF_POOL", ESP_LOG_DEBUG); + ESP_GMF_MEM_SHOW(TAG); + esp_gmf_app_setup_codec_dev(NULL); + void *sdcard_handle = NULL; + esp_gmf_app_setup_sdcard(&sdcard_handle); + +#ifdef MEDIA_LIB_MEM_TEST + media_lib_add_default_adapter(); +#endif /* MEDIA_LIB_MEM_TEST */ + EventGroupHandle_t pipe_sync_evt = xEventGroupCreate(); + ESP_GMF_NULL_CHECK(TAG, pipe_sync_evt, return); + + esp_gmf_pool_handle_t pool = NULL; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pool_init(&pool)); + TEST_ASSERT_NOT_NULL(pool); + gmf_register_audio_all(pool); + ESP_GMF_POOL_SHOW_ITEMS(pool); + + esp_gmf_pipeline_handle_t pipe = NULL; + const char *name[] = {"aud_dec", "copier", "aud_rate_cvt"}; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pool_new_pipeline(pool, "io_file", name, sizeof(name) / sizeof(char *), "io_codec_dev", &pipe)); + TEST_ASSERT_NOT_NULL(pipe); + gmf_setup_pipeline_out_dev(pipe); + ESP_LOGE(TAG, "%d, Copier, 2 pipeline test", __LINE__); + esp_gmf_pipeline_handle_t pipe2 = NULL; + const char *name2[] = {"aud_rate_cvt"}; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pool_new_pipeline(pool, NULL, name2, sizeof(name2) / sizeof(char *), "io_file", &pipe2)); + TEST_ASSERT_NOT_NULL(pipe2); + + esp_gmf_db_handle_t db = NULL; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_db_new_ringbuf(20, 1024, &db)); + TEST_ASSERT_NOT_NULL(db); + esp_gmf_port_handle_t out_port = NEW_ESP_GMF_PORT_OUT_BYTE(esp_gmf_db_acquire_write, esp_gmf_db_release_write, esp_gmf_db_deinit, db, + ESP_GMF_PORT_PAYLOAD_LEN_DEFAULT, 100); + esp_gmf_port_handle_t in_port = NEW_ESP_GMF_PORT_IN_BYTE(esp_gmf_db_acquire_read, esp_gmf_db_release_read, esp_gmf_db_deinit, db, + ESP_GMF_PORT_PAYLOAD_LEN_DEFAULT, 100); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_connect_pipe(pipe, "copier", out_port, pipe2, "aud_rate_cvt", in_port)); + + esp_gmf_task_cfg_t cfg = DEFAULT_ESP_GMF_TASK_CONFIG(); + cfg.ctx = NULL; + cfg.cb = NULL; + cfg.thread.core = 0; + cfg.thread.prio = 6; + cfg.name = "P_file"; + esp_gmf_task_handle_t work_task = NULL; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_init(&cfg, &work_task)); + TEST_ASSERT_NOT_NULL(work_task); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_bind_task(pipe, work_task)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_loading_jobs(pipe)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_set_event(pipe, _pipeline_event, pipe_sync_evt)); + + cfg.name = "S_file"; + cfg.thread.core = 1; + cfg.thread.prio = 6; + esp_gmf_task_handle_t work_task2 = NULL; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_init(&cfg, &work_task2)); + TEST_ASSERT_NOT_NULL(work_task2); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_bind_task(pipe2, work_task2)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_loading_jobs(pipe2)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_set_event(pipe2, _pipeline_event2, NULL)); + ESP_LOGE(TAG, "%d, Copier, 2 pipeline test", __LINE__); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_set_in_uri(pipe, file_name)); + + ESP_GMF_MEM_SHOW(TAG); + esp_gmf_element_handle_t dec_el = NULL; + esp_gmf_info_sound_t info = {0}; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_get_el_by_name(pipe, "aud_dec", &dec_el)); + esp_gmf_audio_helper_get_audio_type_by_uri(file_name, &info.format_id); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_audio_dec_reconfig_by_sound_info(dec_el, &info)); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_run(pipe)); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_set_out_uri(pipe2, "/sdcard/test_save1.wav")); + + ESP_GMF_MEM_SHOW(TAG); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_run(pipe2)); + + xEventGroupWaitBits(pipe_sync_evt, PIPELINE_BLOCK_BIT, pdTRUE, pdFALSE, portMAX_DELAY); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_stop(pipe2)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_stop(pipe)); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_deinit(work_task)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_deinit(work_task2)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_destroy(pipe)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_destroy(pipe2)); + gmf_unregister_audio_all(pool); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pool_deinit(pool)); + vEventGroupDelete(pipe_sync_evt); +#ifdef MEDIA_LIB_MEM_TEST + media_lib_stop_mem_trace(); +#endif /* MEDIA_LIB_MEM_TEST */ + esp_gmf_app_teardown_sdcard(sdcard_handle); + esp_gmf_app_teardown_codec_dev(); + vTaskDelay(1000 / portTICK_RATE_MS); + ESP_GMF_MEM_SHOW(TAG); +} + +/*** +// 3 Pipeline testing +// The copier via ringbuffer connect to two pipelines + + +------------------------+ + +-- RB1 --> Pipe2: Resample + FILE | ++------------------------------+ | +------------------------+ +| Pipe: File + decoder + Copier +-------+ ++------------------------------+ | +------------------------+ + +-- RB2 --> Pipe3: Resample + IIS | + +------------------------+ +***/ +TEST_CASE("Copier, 3 pipeline test, One pipeline decoding file, one is play to I2S, last one save to file", "[ESP_GMF_POOL]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + // esp_log_level_set("ESP_GMF_PIPELINE", ESP_LOG_DEBUG); + esp_log_level_set("ESP_GMF_POOL", ESP_LOG_DEBUG); + ESP_GMF_MEM_SHOW(TAG); + esp_gmf_app_setup_codec_dev(NULL); + void *sdcard_handle = NULL; + esp_gmf_app_setup_sdcard(&sdcard_handle); + +#ifdef MEDIA_LIB_MEM_TEST + media_lib_add_default_adapter(); +#endif /* MEDIA_LIB_MEM_TEST */ + EventGroupHandle_t pipe_sync_evt = xEventGroupCreate(); + ESP_GMF_NULL_CHECK(TAG, pipe_sync_evt, return); + + esp_gmf_pool_handle_t pool = NULL; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pool_init(&pool)); + TEST_ASSERT_NOT_NULL(pool); + gmf_register_audio_all(pool); + ESP_GMF_POOL_SHOW_ITEMS(pool); + + esp_gmf_pipeline_handle_t pipe = NULL; + const char *name[] = {"aud_dec", "copier"}; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pool_new_pipeline(pool, "io_file", name, sizeof(name) / sizeof(char *), NULL, &pipe)); + TEST_ASSERT_NOT_NULL(pipe); + + esp_gmf_pipeline_handle_t pipe2 = NULL; + const char *name2[] = {"aud_rate_cvt"}; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pool_new_pipeline(pool, NULL, name2, sizeof(name2) / sizeof(char *), "io_codec_dev", &pipe2)); + TEST_ASSERT_NOT_NULL(pipe2); + gmf_setup_pipeline_out_dev(pipe2); + esp_gmf_pipeline_handle_t pipe3 = NULL; + const char *name3[] = {"aud_rate_cvt"}; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pool_new_pipeline(pool, NULL, name3, sizeof(name3) / sizeof(char *), "io_file", &pipe3)); + TEST_ASSERT_NOT_NULL(pipe3); + esp_gmf_element_handle_t rate_hd = NULL; + esp_gmf_pipeline_get_el_by_name(pipe2, "aud_rate_cvt", &rate_hd); + + esp_gmf_db_handle_t db = NULL; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_db_new_ringbuf(10, 1024, &db)); + esp_gmf_db_handle_t db2 = NULL; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_db_new_ringbuf(10, 1024, &db2)); + esp_gmf_port_handle_t out_port = NEW_ESP_GMF_PORT_OUT_BYTE(esp_gmf_db_acquire_write, esp_gmf_db_release_write, esp_gmf_db_deinit, db, + ESP_GMF_PORT_PAYLOAD_LEN_DEFAULT, 10); + esp_gmf_port_handle_t in_port = NEW_ESP_GMF_PORT_IN_BYTE(esp_gmf_db_acquire_read, esp_gmf_db_release_read, esp_gmf_db_deinit, db, + ESP_GMF_PORT_PAYLOAD_LEN_DEFAULT, 10); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_connect_pipe(pipe, "copier", out_port, pipe2, "aud_rate_cvt", in_port)); + + out_port = NEW_ESP_GMF_PORT_OUT_BYTE(esp_gmf_db_acquire_write, esp_gmf_db_release_write, esp_gmf_db_deinit, db2, + ESP_GMF_PORT_PAYLOAD_LEN_DEFAULT, 3000); + in_port = NEW_ESP_GMF_PORT_IN_BYTE(esp_gmf_db_acquire_read, esp_gmf_db_release_read, esp_gmf_db_deinit, db2, + ESP_GMF_PORT_PAYLOAD_LEN_DEFAULT, 3000); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_connect_pipe(pipe, "copier", out_port, pipe3, "aud_rate_cvt", in_port)); + + esp_gmf_task_cfg_t cfg = DEFAULT_ESP_GMF_TASK_CONFIG(); + cfg.ctx = NULL; + cfg.cb = NULL; + cfg.name = "P_file"; + cfg.thread.core = 0; + cfg.thread.prio = 8; + esp_gmf_task_handle_t work_task = NULL; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_init(&cfg, &work_task)); + TEST_ASSERT_NOT_NULL(work_task); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_bind_task(pipe, work_task)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_loading_jobs(pipe)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_set_event(pipe, _pipeline_event, NULL)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_set_in_uri(pipe, file_name)); + esp_gmf_element_handle_t dec_el = NULL; + esp_gmf_info_sound_t info = {0}; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_get_el_by_name(pipe, "aud_dec", &dec_el)); + esp_gmf_audio_helper_get_audio_type_by_uri(file_name, &info.format_id); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_audio_dec_reconfig_by_sound_info(dec_el, &info)); + + cfg.name = "IIS"; + cfg.thread.core = 0; + cfg.thread.prio = 6; + cfg.thread.stack = 8192; + esp_gmf_task_handle_t work_task2 = NULL; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_init(&cfg, &work_task2)); + TEST_ASSERT_NOT_NULL(work_task2); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_bind_task(pipe2, work_task2)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_loading_jobs(pipe2)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_set_event(pipe2, _pipeline_event2, NULL)); + + cfg.name = "O_file"; + cfg.thread.core = 1; + cfg.thread.prio = 3; + esp_gmf_task_handle_t work_task3 = NULL; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_init(&cfg, &work_task3)); + TEST_ASSERT_NOT_NULL(work_task3); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_bind_task(pipe3, work_task3)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_loading_jobs(pipe3)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_set_event(pipe3, _pipeline_event3, pipe_sync_evt)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_set_out_uri(pipe3, "/sdcard/test_copier2.wav")); + + ESP_GMF_MEM_SHOW(TAG); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_run(pipe)); + ESP_GMF_MEM_SHOW(TAG); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_run(pipe2)); + ESP_GMF_MEM_SHOW(TAG); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_run(pipe3)); + ESP_GMF_MEM_SHOW(TAG); + + xEventGroupWaitBits(pipe_sync_evt, PIPELINE_BLOCK_BIT3, pdTRUE, pdFALSE, portMAX_DELAY); + ESP_GMF_MEM_SHOW(TAG); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_stop(pipe3)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_stop(pipe2)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_stop(pipe)); + + vTaskDelay(1000 / portTICK_RATE_MS); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_deinit(work_task)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_deinit(work_task2)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_deinit(work_task3)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_destroy(pipe)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_destroy(pipe2)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_destroy(pipe3)); + gmf_unregister_audio_all(pool); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pool_deinit(pool)); + vEventGroupDelete(pipe_sync_evt); +#ifdef MEDIA_LIB_MEM_TEST + media_lib_stop_mem_trace(); +#endif /* MEDIA_LIB_MEM_TEST */ + esp_gmf_app_teardown_sdcard(sdcard_handle); + esp_gmf_app_teardown_codec_dev(); + vTaskDelay(1000 / portTICK_RATE_MS); + ESP_GMF_MEM_SHOW(TAG); +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/main/elements/gmf_audio_rec_el_test.c b/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/main/elements/gmf_audio_rec_el_test.c new file mode 100644 index 0000000..f42059a --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/main/elements/gmf_audio_rec_el_test.c @@ -0,0 +1,516 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "unity.h" +#include + +#include "freertos/FreeRTOS.h" +#include "freertos/event_groups.h" +#include "esp_bit_defs.h" + +#include "esp_log.h" +#include "esp_err.h" +#include "driver/sdmmc_host.h" +#include "esp_http_client.h" +#include "esp_gmf_element.h" +#include "esp_gmf_pipeline.h" +#include "esp_gmf_pool.h" +#include "esp_gmf_oal_mem.h" +#include "esp_gmf_oal_thread.h" +#include "esp_gmf_io_http.h" +#include "esp_gmf_rate_cvt.h" +#include "esp_gmf_audio_enc.h" +#include "esp_gmf_app_setup_peripheral.h" +#include "esp_gmf_app_unit_test.h" +#include "esp_gmf_audio_helper.h" +#include "gmf_audio_play_com.h" + +#ifdef MEDIA_LIB_MEM_TEST +#include "media_lib_adapter.h" +#include "media_lib_mem_trace.h" +#endif /* MEDIA_LIB_MEM_TEST */ + +#define SETUP_AUDIO_SAMPLE_RATE 16000 +#define SETUP_AUDIO_BITS 16 +#define SETUP_AUDIO_CHANNELS 1 +#define PIPELINE_BLOCK_BIT BIT(0) + +static const char *TAG = "AUDIO_REC_ELEMENT_TEST"; + +static const char *test_enc_format[] = { + "aac", + "amr", + "awb", + "pcm", + "opus", + "adpcm", + "g711a", + "g711u", + "alac", +}; + +static const char *header_type[] = { + "audio/aac", + "audio/opus", + "audio/pcm", +}; + +static esp_err_t _pipeline_event(esp_gmf_event_pkt_t *event, void *ctx) +{ + // The warning messages are used to make the content more noticeable. + ESP_LOGW(TAG, "CB: RECV Pipeline EVT: el:%s-%p, type:%x, sub:%s, payload:%p, size:%d,%p", + "OBJ_GET_TAG(event->from)", event->from, event->type, esp_gmf_event_get_state_str(event->sub), + event->payload, event->payload_size, ctx); + if ((event->sub == ESP_GMF_EVENT_STATE_STOPPED) + || (event->sub == ESP_GMF_EVENT_STATE_FINISHED) + || (event->sub == ESP_GMF_EVENT_STATE_ERROR)) { + if (ctx) { + xEventGroupSetBits((EventGroupHandle_t)ctx, PIPELINE_BLOCK_BIT); + } + } + return 0; +} + +static esp_gmf_err_t _http_set_content_type_by_format(esp_http_client_handle_t http, uint32_t format) +{ + switch (format) { + case ESP_AUDIO_TYPE_AAC: + esp_http_client_set_header(http, "Content-Type", header_type[0]); + break; + case ESP_AUDIO_TYPE_OPUS: + esp_http_client_set_header(http, "Content-Type", header_type[1]); + break; + case ESP_AUDIO_TYPE_PCM: + esp_http_client_set_header(http, "Content-Type", header_type[2]); + break; + default: + return ESP_GMF_ERR_FAIL; + } + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t _http_stream_event_handle(http_stream_event_msg_t *msg) +{ + esp_http_client_handle_t http = (esp_http_client_handle_t)msg->http_client; + char len_buf[16]; + if (msg->event_id == HTTP_STREAM_PRE_REQUEST) { + // set header + ESP_LOGW(TAG, "[ + ] HTTP client HTTP_STREAM_PRE_REQUEST, length=%d, format:%s", msg->buffer_len, (char *)msg->user_data); + esp_http_client_set_method(http, HTTP_METHOD_POST); + char dat[10] = {0}; + snprintf(dat, sizeof(dat), "%d", SETUP_AUDIO_SAMPLE_RATE); + esp_http_client_set_header(http, "x-audio-sample-rates", dat); + uint32_t fmt = 0; + esp_gmf_audio_helper_get_audio_type_by_uri((char *)msg->user_data, &fmt); + esp_gmf_err_t ret = _http_set_content_type_by_format(http, fmt); + if (ret != ESP_GMF_ERR_OK) { + ESP_LOGE(TAG, "Failed to set content type for format: %lx", fmt); + return ret; + } + memset(dat, 0, sizeof(dat)); + snprintf(dat, sizeof(dat), "%d", SETUP_AUDIO_BITS); + esp_http_client_set_header(http, "x-audio-bits", dat); + memset(dat, 0, sizeof(dat)); + snprintf(dat, sizeof(dat), "%d", SETUP_AUDIO_CHANNELS); + esp_http_client_set_header(http, "x-audio-channel", dat); + return ESP_GMF_ERR_OK; + } + + if (msg->event_id == HTTP_STREAM_ON_REQUEST) { + // write data + int wlen = sprintf(len_buf, "%x\r\n", msg->buffer_len); + if (esp_http_client_write(http, len_buf, wlen) <= 0) { + return ESP_GMF_ERR_FAIL; + } + if (esp_http_client_write(http, msg->buffer, msg->buffer_len) <= 0) { + return ESP_GMF_ERR_FAIL; + } + if (esp_http_client_write(http, "\r\n", 2) <= 0) { + return ESP_GMF_ERR_FAIL; + } + return msg->buffer_len; + } + + if (msg->event_id == HTTP_STREAM_POST_REQUEST) { + ESP_LOGW(TAG, "[ + ] HTTP client HTTP_STREAM_POST_REQUEST, write end chunked marker"); + if (esp_http_client_write(http, "0\r\n\r\n", 5) <= 0) { + return ESP_GMF_ERR_FAIL; + } + return ESP_GMF_ERR_OK; + } + + if (msg->event_id == HTTP_STREAM_FINISH_REQUEST) { + ESP_LOGW(TAG, "[ + ] HTTP client HTTP_STREAM_FINISH_REQUEST"); + char *buf = (char *)calloc(1, 64); + assert(buf); + int read_len = esp_http_client_read(http, buf, 64); + if (read_len <= 0) { + free(buf); + return ESP_GMF_ERR_FAIL; + } + buf[read_len] = 0; + ESP_LOGI(TAG, "Got HTTP Response = %s", (char *)buf); + free(buf); + return ESP_GMF_ERR_OK; + } + ESP_LOGD(TAG, "Unknown event: %d", msg->event_id); + return ESP_GMF_ERR_OK; +} + +TEST_CASE("Recorder, One Pipe, [IIS->ENC->FILE]", "[ESP_GMF_POOL]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + esp_log_level_set("ESP_GMF_PIPELINE", ESP_LOG_DEBUG); + // esp_log_level_set("GMF_CACHE", ESP_LOG_DEBUG); + // esp_log_level_set("ESP_GMF_AENC", ESP_LOG_DEBUG); + ESP_GMF_MEM_SHOW(TAG); + uint32_t I2S_REC_ENCODER_SAMPLE_RATE = 48000; + esp_gmf_app_codec_info_t codec_info = ESP_GMF_APP_CODEC_INFO_DEFAULT(); + codec_info.record_info.sample_rate = SETUP_AUDIO_SAMPLE_RATE; + codec_info.record_info.channel = SETUP_AUDIO_CHANNELS; + codec_info.record_info.bits_per_sample = SETUP_AUDIO_BITS; + codec_info.play_info.sample_rate = codec_info.record_info.sample_rate; + esp_gmf_app_setup_codec_dev(&codec_info); + void *sdcard_handle = NULL; + esp_gmf_app_setup_sdcard(&sdcard_handle); + +#ifdef MEDIA_LIB_MEM_TEST + media_lib_add_default_adapter(); +#endif /* MEDIA_LIB_MEM_TEST */ + esp_gmf_pool_handle_t pool = NULL; + esp_gmf_pool_init(&pool); + TEST_ASSERT_NOT_NULL(pool); + gmf_register_audio_all(pool); + + ESP_GMF_POOL_SHOW_ITEMS(pool); + // Create the new elements + esp_gmf_pipeline_handle_t pipe = NULL; + const char *uri = "/sdcard/esp_gmf_rec1.aac"; + + const char *name[] = {"aud_rate_cvt", "aud_ch_cvt", "aud_enc"}; + esp_gmf_pool_new_pipeline(pool, "io_codec_dev", name, sizeof(name) / sizeof(char *), "io_file", &pipe); + TEST_ASSERT_NOT_NULL(pipe); + gmf_setup_pipeline_in_dev(pipe); + esp_gmf_task_cfg_t cfg = DEFAULT_ESP_GMF_TASK_CONFIG(); + cfg.ctx = NULL; + cfg.cb = NULL; + esp_gmf_task_handle_t work_task = NULL; + esp_gmf_task_init(&cfg, &work_task); + TEST_ASSERT_NOT_NULL(work_task); + + esp_gmf_pipeline_bind_task(pipe, work_task); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_loading_jobs(pipe)); + esp_gmf_pipeline_set_event(pipe, _pipeline_event, NULL); + + esp_gmf_pipeline_set_out_uri(pipe, uri); + esp_gmf_element_handle_t enc_handle = NULL; + esp_gmf_pipeline_get_el_by_name(pipe, "aud_enc", &enc_handle); + uint32_t audio_type = 0; + esp_gmf_audio_helper_get_audio_type_by_uri(uri, &audio_type); + esp_gmf_info_sound_t info = { + .sample_rates = SETUP_AUDIO_SAMPLE_RATE, + .channels = SETUP_AUDIO_CHANNELS, + .bits = SETUP_AUDIO_BITS, + .bitrate = 90000, + .format_id = audio_type, + }; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_audio_enc_reconfig_by_sound_info(enc_handle, &info)); + esp_gmf_element_handle_t resp = NULL; + esp_gmf_pipeline_get_el_by_name(pipe, "aud_rate_cvt", &resp); + esp_ae_rate_cvt_cfg_t *resp_cfg = (esp_ae_rate_cvt_cfg_t *)OBJ_GET_CFG(resp); + resp_cfg->src_rate = SETUP_AUDIO_SAMPLE_RATE; + if (audio_type == ESP_AUDIO_TYPE_AMRNB) { + resp_cfg->dest_rate = 8000; + } else if (audio_type == ESP_AUDIO_TYPE_G711U) { + resp_cfg->dest_rate = 8000; + } else if (audio_type == ESP_AUDIO_TYPE_G711A) { + resp_cfg->dest_rate = 8000; + } else if (audio_type == ESP_AUDIO_TYPE_AMRWB) { + resp_cfg->dest_rate = 16000; + } else { + resp_cfg->dest_rate = I2S_REC_ENCODER_SAMPLE_RATE; + } + esp_gmf_pipeline_report_info(pipe, ESP_GMF_INFO_SOUND, &info, sizeof(info)); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_run(pipe)); + + vTaskDelay(10000 / portTICK_RATE_MS); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_stop(pipe)); + + esp_gmf_task_deinit(work_task); + esp_gmf_pipeline_destroy(pipe); + gmf_unregister_audio_all(pool); + esp_gmf_pool_deinit(pool); +#ifdef MEDIA_LIB_MEM_TEST + media_lib_stop_mem_trace(); +#endif /* MEDIA_LIB_MEM_TEST */ + esp_gmf_app_teardown_codec_dev(); + esp_gmf_app_teardown_sdcard(sdcard_handle); + vTaskDelay(1000 / portTICK_RATE_MS); + ESP_GMF_MEM_SHOW(TAG); +} + +TEST_CASE("Recorder, One Pipe recoding multiple format, [IIS->ENC->FILE]", "[ESP_GMF_POOL]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + esp_log_level_set("ESP_GMF_PIPELINE", ESP_LOG_DEBUG); + // esp_log_level_set("ESP_GMF_RATE_CVT", ESP_LOG_DEBUG); + // esp_log_level_set("ESP_GMF_AENC", ESP_LOG_DEBUG); + ESP_GMF_MEM_SHOW(TAG); + + esp_gmf_app_codec_info_t codec_info = ESP_GMF_APP_CODEC_INFO_DEFAULT(); + codec_info.record_info.sample_rate = SETUP_AUDIO_SAMPLE_RATE; + codec_info.record_info.channel = SETUP_AUDIO_CHANNELS; + codec_info.record_info.bits_per_sample = SETUP_AUDIO_BITS; + codec_info.play_info.sample_rate = codec_info.record_info.sample_rate; + esp_gmf_app_setup_codec_dev(&codec_info); + void *sdcard_handle = NULL; + esp_gmf_app_setup_sdcard(&sdcard_handle); +#ifdef MEDIA_LIB_MEM_TEST + media_lib_add_default_adapter(); +#endif /* MEDIA_LIB_MEM_TEST */ + esp_gmf_pool_handle_t pool = NULL; + esp_gmf_pool_init(&pool); + TEST_ASSERT_NOT_NULL(pool); + gmf_register_audio_all(pool); + ESP_GMF_POOL_SHOW_ITEMS(pool); + + EventGroupHandle_t pipe_sync_evt = xEventGroupCreate(); + ESP_GMF_NULL_CHECK(TAG, pipe_sync_evt, return); + + esp_gmf_pipeline_handle_t pipe = NULL; + const char *name[] = {"aud_rate_cvt", "aud_enc"}; + esp_gmf_pool_new_pipeline(pool, "io_codec_dev", name, sizeof(name) / sizeof(char *), "io_file", &pipe); + TEST_ASSERT_NOT_NULL(pipe); + gmf_setup_pipeline_in_dev(pipe); + esp_gmf_task_cfg_t cfg = DEFAULT_ESP_GMF_TASK_CONFIG(); + cfg.ctx = NULL; + cfg.cb = NULL; + cfg.thread.stack = 40 * 1024; + esp_gmf_task_handle_t work_task = NULL; + esp_gmf_task_init(&cfg, &work_task); + TEST_ASSERT_NOT_NULL(work_task); + + esp_gmf_pipeline_bind_task(pipe, work_task); + esp_gmf_pipeline_loading_jobs(pipe); + esp_gmf_pipeline_set_event(pipe, _pipeline_event, pipe_sync_evt); + + char uri[128] = {0}; + uint32_t I2S_REC_ENCODER_SAMPLE_RATE = 48000; + + for (int i = 0; i < sizeof(test_enc_format) / sizeof(char *); ++i) { + snprintf(uri, 127, "/sdcard/esp_gmf_rec_%02d.%s", i, test_enc_format[i]); + esp_gmf_element_handle_t enc_handle = NULL; + esp_gmf_pipeline_get_el_by_name(pipe, "aud_enc", &enc_handle); + esp_audio_enc_config_t *esp_gmf_enc_cfg = (esp_audio_enc_config_t *)OBJ_GET_CFG(enc_handle); + uint32_t audio_type = 0; + esp_gmf_audio_helper_get_audio_type_by_uri(uri, &audio_type); + esp_gmf_info_sound_t info = { + .sample_rates = SETUP_AUDIO_SAMPLE_RATE, + .channels = SETUP_AUDIO_CHANNELS, + .bits = SETUP_AUDIO_BITS, + .format_id = audio_type, + }; + if (audio_type == ESP_AUDIO_TYPE_AMRNB) { + info.bitrate = 12200; + } else if (audio_type == ESP_AUDIO_TYPE_AMRWB) { + info.bitrate = 19850; + } else { + info.bitrate = 90000; + } + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_audio_enc_reconfig_by_sound_info(enc_handle, &info)); + esp_gmf_pipeline_report_info(pipe, ESP_GMF_INFO_SOUND, &info, sizeof(info)); + + esp_gmf_element_handle_t resp = NULL; + esp_gmf_pipeline_get_el_by_name(pipe, "aud_rate_cvt", &resp); + esp_ae_rate_cvt_cfg_t *resp_cfg = (esp_ae_rate_cvt_cfg_t *)OBJ_GET_CFG(resp); + resp_cfg->src_rate = SETUP_AUDIO_SAMPLE_RATE; + if (audio_type == ESP_AUDIO_TYPE_AMRNB) { + resp_cfg->dest_rate = 8000; + } else if (audio_type == ESP_AUDIO_TYPE_G711U) { + resp_cfg->dest_rate = 8000; + } else if (audio_type == ESP_AUDIO_TYPE_G711A) { + resp_cfg->dest_rate = 8000; + } else if (audio_type == ESP_AUDIO_TYPE_AMRWB) { + resp_cfg->dest_rate = 16000; + } else { + resp_cfg->dest_rate = I2S_REC_ENCODER_SAMPLE_RATE; + } + + ESP_LOGW(TAG, "Recoding file, %s, type:%d\r\n", uri, esp_gmf_enc_cfg->type); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_set_out_uri(pipe, uri)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_run(pipe)); + int cnt = 10; + xEventGroupClearBits(pipe_sync_evt, PIPELINE_BLOCK_BIT); + while (cnt) { + printf("."); + EventBits_t ret = xEventGroupWaitBits(pipe_sync_evt, PIPELINE_BLOCK_BIT, pdTRUE, pdFALSE, 1000 / portTICK_RATE_MS); + if ((ret & PIPELINE_BLOCK_BIT) != 0) { + cnt = 0; + break; + } + fflush(stdout); + cnt--; + } + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_stop(pipe)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_reset(pipe)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_loading_jobs(pipe)); + } + + esp_gmf_task_deinit(work_task); + esp_gmf_pipeline_destroy(pipe); + gmf_unregister_audio_all(pool); + esp_gmf_pool_deinit(pool); +#ifdef MEDIA_LIB_MEM_TEST + media_lib_stop_mem_trace(); +#endif /* MEDIA_LIB_MEM_TEST */ + vEventGroupDelete(pipe_sync_evt); + esp_gmf_app_teardown_codec_dev(); + esp_gmf_app_teardown_sdcard(sdcard_handle); + vTaskDelay(1000 / portTICK_RATE_MS); + ESP_GMF_MEM_SHOW(TAG); +} + +// Refer the 'esp_gmf_audio_rec_el_test.c' test_enc_format +static const char *recoding_file_path[] = { + "/sdcard/esp_gmf_rec_00.aac", + "/sdcard/esp_gmf_rec_01.amr", + "/sdcard/esp_gmf_rec_02.awb", + "/sdcard/esp_gmf_rec_03.pcm", +}; + +TEST_CASE("Record file for playback, multiple files with One Pipe, [FILE->dec->resample->IIS]", "[ESP_GMF_POOL]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + esp_log_level_set("ESP_GMF_PIPELINE", ESP_LOG_DEBUG); + esp_log_level_set("ESP_GMF_POOL", ESP_LOG_DEBUG); + ESP_GMF_MEM_SHOW(TAG); + esp_gmf_app_setup_codec_dev(NULL); + void *sdcard_handle = NULL; + esp_gmf_app_setup_sdcard(&sdcard_handle); + EventGroupHandle_t pipe_sync_evt = xEventGroupCreate(); + ESP_GMF_NULL_CHECK(TAG, pipe_sync_evt, return); +#ifdef MEDIA_LIB_MEM_TEST + media_lib_add_default_adapter(); +#endif /* MEDIA_LIB_MEM_TEST */ + esp_gmf_pool_handle_t pool = NULL; + esp_gmf_pool_init(&pool); + TEST_ASSERT_NOT_NULL(pool); + gmf_register_audio_all(pool); + ESP_GMF_POOL_SHOW_ITEMS(pool); + + esp_gmf_pipeline_handle_t pipe = NULL; + const char *name[] = {"aud_dec", "aud_rate_cvt", "aud_ch_cvt"}; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pool_new_pipeline(pool, "io_file", name, sizeof(name) / sizeof(char *), "io_codec_dev", &pipe)); + TEST_ASSERT_NOT_NULL(pipe); + gmf_setup_pipeline_out_dev(pipe); + esp_gmf_task_cfg_t cfg = DEFAULT_ESP_GMF_TASK_CONFIG(); + cfg.thread.stack = 30 * 1024; + esp_gmf_task_handle_t work_task = NULL; + esp_gmf_task_init(&cfg, &work_task); + TEST_ASSERT_NOT_NULL(work_task); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_bind_task(pipe, work_task)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_set_event(pipe, _pipeline_event, pipe_sync_evt)); + + for (int i = 0; i < sizeof(recoding_file_path) / sizeof(char *); ++i) { + play_pause_single_file(pipe, recoding_file_path[i]); + xEventGroupWaitBits(pipe_sync_evt, PIPELINE_BLOCK_BIT, pdTRUE, pdFALSE, portMAX_DELAY); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_stop(pipe)); + } + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_deinit(work_task)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_destroy(pipe)); + gmf_unregister_audio_all(pool); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pool_deinit(pool)); + vEventGroupDelete(pipe_sync_evt); +#ifdef MEDIA_LIB_MEM_TEST + media_lib_stop_mem_trace(); +#endif /* MEDIA_LIB_MEM_TEST */ + esp_gmf_app_teardown_codec_dev(); + esp_gmf_app_teardown_sdcard(sdcard_handle); + vTaskDelay(1000 / portTICK_RATE_MS); + ESP_GMF_MEM_SHOW(TAG); +} + +TEST_CASE("Recorder, One Pipe, [IIS->ENC->HTTP]", "[ESP_GMF_POOL][ignore][leaks=10000]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + esp_log_level_set("ESP_GMF_PIPELINE", ESP_LOG_DEBUG); + esp_log_level_set("ESP_GMF_POOL", ESP_LOG_DEBUG); + esp_gmf_app_test_case_uses_tcpip(); + ESP_GMF_MEM_SHOW(TAG); + esp_gmf_app_wifi_connect(); + esp_gmf_app_codec_info_t codec_info = ESP_GMF_APP_CODEC_INFO_DEFAULT(); + codec_info.record_info.sample_rate = SETUP_AUDIO_SAMPLE_RATE; + codec_info.record_info.channel = SETUP_AUDIO_CHANNELS; + codec_info.record_info.bits_per_sample = SETUP_AUDIO_BITS; + codec_info.play_info.sample_rate = codec_info.record_info.sample_rate; + esp_gmf_app_setup_codec_dev(&codec_info); + +#ifdef MEDIA_LIB_MEM_TEST + media_lib_add_default_adapter(); +#endif /* MEDIA_LIB_MEM_TEST */ + esp_gmf_pool_handle_t pool = NULL; + esp_gmf_pool_init(&pool); + TEST_ASSERT_NOT_NULL(pool); + gmf_register_audio_all(pool); + ESP_GMF_POOL_SHOW_ITEMS(pool); + + // Create the new elements + esp_gmf_pipeline_handle_t pipe = NULL; + const char *name[] = {"aud_enc"}; + esp_gmf_pool_new_pipeline(pool, "io_codec_dev", name, sizeof(name) / sizeof(char *), "io_http", &pipe); + TEST_ASSERT_NOT_NULL(pipe); + gmf_setup_pipeline_in_dev(pipe); + const char *rec_type = ".aac"; + + esp_gmf_element_handle_t enc_handle = NULL; + esp_gmf_pipeline_get_el_by_name(pipe, "aud_enc", &enc_handle); + uint32_t audio_type = ESP_AUDIO_TYPE_AAC; + esp_gmf_audio_helper_get_audio_type_by_uri(rec_type, &audio_type); + esp_gmf_info_sound_t info = { + .sample_rates = SETUP_AUDIO_SAMPLE_RATE, + .channels = SETUP_AUDIO_CHANNELS, + .bits = SETUP_AUDIO_BITS, + .bitrate = 90000, + .format_id = audio_type, + }; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_audio_enc_reconfig_by_sound_info(enc_handle, &info)); + esp_gmf_pipeline_report_info(pipe, ESP_GMF_INFO_SOUND, &info, sizeof(info)); + + esp_gmf_io_handle_t http_out = NULL; + esp_gmf_pipeline_get_out(pipe, &http_out); + http_io_cfg_t *http_cfg = (http_io_cfg_t *)OBJ_GET_CFG(http_out); + http_cfg->user_data = (void *)rec_type; + http_cfg->event_handle = _http_stream_event_handle; + + esp_gmf_task_cfg_t cfg = DEFAULT_ESP_GMF_TASK_CONFIG(); + cfg.ctx = NULL; + cfg.cb = NULL; + esp_gmf_task_handle_t work_task = NULL; + esp_gmf_task_init(&cfg, &work_task); + TEST_ASSERT_NOT_NULL(work_task); + + esp_gmf_pipeline_bind_task(pipe, work_task); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_loading_jobs(pipe)); + esp_gmf_pipeline_set_event(pipe, _pipeline_event, NULL); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_set_out_uri(pipe, "http://192.168.31.28:8000/upload")); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_run(pipe)); + + vTaskDelay(15000 / portTICK_RATE_MS); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_stop(pipe)); + esp_gmf_task_deinit(work_task); + esp_gmf_pipeline_destroy(pipe); + gmf_unregister_audio_all(pool); + esp_gmf_pool_deinit(pool); +#ifdef MEDIA_LIB_MEM_TEST + media_lib_stop_mem_trace(); +#endif /* MEDIA_LIB_MEM_TEST */ + esp_gmf_app_teardown_codec_dev(); + esp_gmf_app_wifi_disconnect(); + vTaskDelay(1000 / portTICK_RATE_MS); + ESP_GMF_MEM_SHOW(TAG); +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/main/elements/gmf_el_if_test.c b/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/main/elements/gmf_el_if_test.c new file mode 100644 index 0000000..f725992 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/main/elements/gmf_el_if_test.c @@ -0,0 +1,368 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "unity.h" +#include + +#include "esp_gmf_bit_cvt.h" +#include "esp_gmf_ch_cvt.h" +#include "esp_gmf_eq.h" +#include "esp_gmf_alc.h" +#include "esp_gmf_fade.h" +#include "esp_gmf_mixer.h" +#include "esp_gmf_rate_cvt.h" +#include "esp_gmf_sonic.h" +#include "esp_gmf_interleave.h" +#include "esp_gmf_deinterleave.h" +#include "esp_gmf_audio_enc.h" +#include "esp_gmf_audio_dec.h" + +#include "esp_gmf_io_embed_flash.h" +#include "esp_gmf_io_http.h" +#include "esp_gmf_io_file.h" +#include "esp_gmf_io_i2s_pdm.h" + +#include "esp_gmf_copier.h" + +void test_esp_gmf_alc_if() +{ + esp_ae_alc_cfg_t config = {.channel = 2}; + esp_gmf_obj_handle_t handle; + // Initialize function test + TEST_ASSERT_EQUAL(esp_gmf_alc_init(&config, NULL), ESP_GMF_ERR_INVALID_ARG); + TEST_ASSERT_EQUAL(esp_gmf_alc_init(&config, &handle), ESP_GMF_ERR_OK); + // Set gain function test + TEST_ASSERT_EQUAL(esp_gmf_alc_set_gain(NULL, 0, 10), ESP_GMF_ERR_INVALID_ARG); + TEST_ASSERT_EQUAL(esp_gmf_alc_set_gain(handle, config.channel + 1, 10), ESP_GMF_ERR_INVALID_ARG); + TEST_ASSERT_EQUAL(esp_gmf_alc_set_gain(handle, config.channel - 1, -10), ESP_GMF_ERR_OK); + // Get gain function test + int8_t gain = 0; + TEST_ASSERT_EQUAL(esp_gmf_alc_get_gain(NULL, 0, &gain), ESP_GMF_ERR_INVALID_ARG); + TEST_ASSERT_EQUAL(esp_gmf_alc_get_gain(handle, 0, NULL), ESP_GMF_ERR_INVALID_ARG); + TEST_ASSERT_EQUAL(esp_gmf_alc_get_gain(handle, config.channel + 1, &gain), ESP_GMF_ERR_INVALID_ARG); + // Deinitialize function test + TEST_ASSERT_EQUAL(esp_gmf_obj_delete(handle), ESP_GMF_ERR_OK); + // Test for config is NULL, will create a default config + TEST_ASSERT_EQUAL(esp_gmf_alc_init(NULL, &handle), ESP_GMF_ERR_OK); + TEST_ASSERT_NOT_EQUAL(NULL, OBJ_GET_CFG(handle)); + TEST_ASSERT_EQUAL(esp_gmf_obj_delete(handle), ESP_GMF_ERR_OK); +} + +void test_esp_gmf_bit_cvt_if() +{ + esp_ae_bit_cvt_cfg_t config = DEFAULT_ESP_GMF_BIT_CVT_CONFIG(); + esp_gmf_obj_handle_t handle; + // Initialize function test + TEST_ASSERT_EQUAL(esp_gmf_bit_cvt_init(&config, NULL), ESP_GMF_ERR_INVALID_ARG); + TEST_ASSERT_EQUAL(esp_gmf_bit_cvt_init(&config, &handle), ESP_GMF_ERR_OK); + // Deinitialize function test + TEST_ASSERT_EQUAL(esp_gmf_obj_delete(handle), ESP_GMF_ERR_OK); + // Test for config is NULL, will create a default config + TEST_ASSERT_EQUAL(esp_gmf_bit_cvt_init(NULL, &handle), ESP_GMF_ERR_OK); + TEST_ASSERT_NOT_EQUAL(NULL, OBJ_GET_CFG(handle)); + TEST_ASSERT_EQUAL(esp_gmf_obj_delete(handle), ESP_GMF_ERR_OK); +} + +void test_esp_gmf_ch_cvt_if() +{ + esp_ae_ch_cvt_cfg_t config = DEFAULT_ESP_GMF_CH_CVT_CONFIG(); + esp_gmf_obj_handle_t handle; + // Initialize function test + TEST_ASSERT_EQUAL(esp_gmf_ch_cvt_init(&config, NULL), ESP_GMF_ERR_INVALID_ARG); + TEST_ASSERT_EQUAL(esp_gmf_ch_cvt_init(&config, &handle), ESP_GMF_ERR_OK); + // Set dest channel function test + TEST_ASSERT_EQUAL(esp_gmf_ch_cvt_set_dest_channel(NULL, 1), ESP_GMF_ERR_INVALID_ARG); + TEST_ASSERT_EQUAL(esp_gmf_ch_cvt_set_dest_channel(handle, 1), ESP_GMF_ERR_OK); + // Deinitialize function test + TEST_ASSERT_EQUAL(esp_gmf_obj_delete(handle), ESP_GMF_ERR_OK); + // Test for config is NULL, will create a default config + TEST_ASSERT_EQUAL(esp_gmf_ch_cvt_init(NULL, &handle), ESP_GMF_ERR_OK); + TEST_ASSERT_NOT_EQUAL(NULL, OBJ_GET_CFG(handle)); + TEST_ASSERT_EQUAL(esp_gmf_obj_delete(handle), ESP_GMF_ERR_OK); +} + +void test_esp_gmf_deinterleave_if() +{ + esp_gmf_deinterleave_cfg config = DEFAULT_ESP_GMF_DEINTERLEAVE_CONFIG(); + esp_gmf_obj_handle_t handle; + // Initialize function test + TEST_ASSERT_EQUAL(esp_gmf_deinterleave_init(&config, NULL), ESP_GMF_ERR_INVALID_ARG); + TEST_ASSERT_EQUAL(esp_gmf_deinterleave_init(&config, &handle), ESP_GMF_ERR_OK); + // Deinitialize function test + TEST_ASSERT_EQUAL(esp_gmf_obj_delete(handle), ESP_GMF_ERR_OK); + // Test for config is NULL, will create a default config + TEST_ASSERT_EQUAL(esp_gmf_deinterleave_init(NULL, &handle), ESP_GMF_ERR_OK); + TEST_ASSERT_NOT_EQUAL(NULL, OBJ_GET_CFG(handle)); + TEST_ASSERT_EQUAL(esp_gmf_obj_delete(handle), ESP_GMF_ERR_OK); +} + +void test_esp_gmf_eq_if() +{ + esp_ae_eq_cfg_t config = DEFAULT_ESP_GMF_EQ_CONFIG(); + esp_gmf_obj_handle_t handle; + esp_ae_eq_filter_para_t para = {0}; + // Initialize function test + TEST_ASSERT_EQUAL(esp_gmf_eq_init(&config, NULL), ESP_GMF_ERR_INVALID_ARG); + TEST_ASSERT_EQUAL(esp_gmf_eq_init(&config, &handle), ESP_GMF_ERR_OK); + // Set para function test + TEST_ASSERT_EQUAL(esp_gmf_eq_set_para(NULL, 0, ¶), ESP_GMF_ERR_INVALID_ARG); + // Deinitialize function test + TEST_ASSERT_EQUAL(esp_gmf_obj_delete(handle), ESP_GMF_ERR_OK); + // Test for config is NULL, will create a default config + TEST_ASSERT_EQUAL(esp_gmf_eq_init(NULL, &handle), ESP_GMF_ERR_OK); + TEST_ASSERT_NOT_EQUAL(NULL, OBJ_GET_CFG(handle)); + TEST_ASSERT_EQUAL(esp_gmf_obj_delete(handle), ESP_GMF_ERR_OK); +} + +void test_esp_gmf_fade_if() +{ + esp_ae_fade_cfg_t config = DEFAULT_ESP_GMF_FADE_CONFIG(); + esp_gmf_obj_handle_t handle; + esp_ae_fade_mode_t mode = ESP_AE_FADE_MODE_INVALID; + // Initialize function test + TEST_ASSERT_EQUAL(esp_gmf_fade_init(&config, NULL), ESP_GMF_ERR_INVALID_ARG); + TEST_ASSERT_EQUAL(esp_gmf_fade_init(&config, &handle), ESP_GMF_ERR_OK); + // Set mode function test + TEST_ASSERT_EQUAL(esp_gmf_fade_set_mode(NULL, 0), ESP_GMF_ERR_INVALID_ARG); + // Get mode function test + TEST_ASSERT_EQUAL(esp_gmf_fade_get_mode(NULL, &mode), ESP_GMF_ERR_INVALID_ARG); + TEST_ASSERT_EQUAL(esp_gmf_fade_get_mode(handle, NULL), ESP_GMF_ERR_INVALID_ARG); + TEST_ASSERT_EQUAL(esp_gmf_fade_get_mode(handle, &mode), ESP_GMF_ERR_OK); + // Set weight function test + TEST_ASSERT_EQUAL(esp_gmf_fade_reset_weight(NULL), ESP_GMF_ERR_INVALID_ARG); + TEST_ASSERT_EQUAL(esp_gmf_fade_reset_weight(handle), ESP_GMF_ERR_OK); + // Deinitialize function test + TEST_ASSERT_EQUAL(esp_gmf_obj_delete(handle), ESP_GMF_ERR_OK); + // Test for config is NULL, will create a default config + TEST_ASSERT_EQUAL(esp_gmf_fade_init(NULL, &handle), ESP_GMF_ERR_OK); + TEST_ASSERT_NOT_EQUAL(NULL, OBJ_GET_CFG(handle)); + TEST_ASSERT_EQUAL(esp_gmf_obj_delete(handle), ESP_GMF_ERR_OK); +} + +void test_esp_gmf_interleave_if() +{ + esp_gmf_interleave_cfg config = DEFAULT_ESP_GMF_INTERLEAVE_CONFIG(); + esp_gmf_obj_handle_t handle; + // Initialize function test + TEST_ASSERT_EQUAL(esp_gmf_interleave_init(&config, NULL), ESP_GMF_ERR_INVALID_ARG); + TEST_ASSERT_EQUAL(esp_gmf_interleave_init(&config, &handle), ESP_GMF_ERR_OK); + // Deinitialize function test + TEST_ASSERT_EQUAL(esp_gmf_obj_delete(handle), ESP_GMF_ERR_OK); + // Test for config is NULL, will create a default config + TEST_ASSERT_EQUAL(esp_gmf_interleave_init(NULL, &handle), ESP_GMF_ERR_OK); + TEST_ASSERT_NOT_EQUAL(NULL, OBJ_GET_CFG(handle)); + TEST_ASSERT_EQUAL(esp_gmf_obj_delete(handle), ESP_GMF_ERR_OK); +} + +void test_esp_gmf_mixer_if() +{ + esp_ae_mixer_cfg_t config = DEFAULT_ESP_GMF_MIXER_CONFIG(); + esp_gmf_obj_handle_t handle; + esp_ae_mixer_mode_t mode = ESP_AE_MIXER_MODE_INVALID; + uint32_t sample_rate = 48000; + uint8_t channel = 2; + uint8_t bits = 16; + // Initialize function test + TEST_ASSERT_EQUAL(esp_gmf_mixer_init(&config, NULL), ESP_GMF_ERR_INVALID_ARG); + TEST_ASSERT_EQUAL(esp_gmf_mixer_init(&config, &handle), ESP_GMF_ERR_OK); + // Set mode function test + TEST_ASSERT_EQUAL(esp_gmf_mixer_set_mode(NULL, 0, mode), ESP_GMF_ERR_INVALID_ARG); + TEST_ASSERT_EQUAL(esp_gmf_mixer_set_mode(handle, config.src_num + 1, mode), ESP_GMF_ERR_INVALID_ARG); + TEST_ASSERT_EQUAL(esp_gmf_mixer_set_mode(handle, 0, mode), ESP_GMF_ERR_OK); + // Set audio info function test + TEST_ASSERT_EQUAL(esp_gmf_mixer_set_audio_info(NULL, sample_rate, channel, bits), ESP_GMF_ERR_INVALID_ARG); + TEST_ASSERT_EQUAL(esp_gmf_mixer_set_audio_info(handle, sample_rate, channel, bits), ESP_GMF_ERR_OK); + // Deinitialize function test + TEST_ASSERT_EQUAL(esp_gmf_obj_delete(handle), ESP_GMF_ERR_OK); + // Test for config is NULL, will create a default config + TEST_ASSERT_EQUAL(esp_gmf_mixer_init(NULL, &handle), ESP_GMF_ERR_OK); + TEST_ASSERT_NOT_EQUAL(NULL, OBJ_GET_CFG(handle)); + TEST_ASSERT_EQUAL(esp_gmf_obj_delete(handle), ESP_GMF_ERR_OK); +} + +void test_esp_gmf_rate_cvt_if() +{ + esp_ae_rate_cvt_cfg_t config = DEFAULT_ESP_GMF_RATE_CVT_CONFIG(); + esp_gmf_obj_handle_t handle; + uint32_t sample_rate = 48000; + // Initialize function test + TEST_ASSERT_EQUAL(esp_gmf_rate_cvt_init(&config, NULL), ESP_GMF_ERR_INVALID_ARG); + TEST_ASSERT_EQUAL(esp_gmf_rate_cvt_init(&config, &handle), ESP_GMF_ERR_OK); + // Set mode function test + TEST_ASSERT_EQUAL(esp_gmf_rate_cvt_set_dest_rate(NULL, sample_rate), ESP_GMF_ERR_INVALID_ARG); + TEST_ASSERT_EQUAL(esp_gmf_rate_cvt_set_dest_rate(handle, sample_rate), ESP_GMF_ERR_OK); + // Deinitialize function test + TEST_ASSERT_EQUAL(esp_gmf_obj_delete(handle), ESP_GMF_ERR_OK); + // Test for config is NULL, will create a default config + TEST_ASSERT_EQUAL(esp_gmf_rate_cvt_init(NULL, &handle), ESP_GMF_ERR_OK); + TEST_ASSERT_NOT_EQUAL(NULL, OBJ_GET_CFG(handle)); + TEST_ASSERT_EQUAL(esp_gmf_obj_delete(handle), ESP_GMF_ERR_OK); +} + +void test_esp_gmf_sonic_if() +{ + esp_ae_sonic_cfg_t config = DEFAULT_ESP_GMF_SONIC_CONFIG(); + esp_gmf_obj_handle_t handle; + float speed = 1.0; + float pitch = 1.0; + // Initialize function test + TEST_ASSERT_EQUAL(esp_gmf_sonic_init(&config, NULL), ESP_GMF_ERR_INVALID_ARG); + TEST_ASSERT_EQUAL(esp_gmf_sonic_init(&config, &handle), ESP_GMF_ERR_OK); + // Set speed function test + TEST_ASSERT_EQUAL(esp_gmf_sonic_set_speed(NULL, speed), ESP_GMF_ERR_INVALID_ARG); + TEST_ASSERT_EQUAL(esp_gmf_sonic_set_speed(handle, speed), ESP_GMF_ERR_OK); + // Get speed function test + TEST_ASSERT_EQUAL(esp_gmf_sonic_get_speed(NULL, &speed), ESP_GMF_ERR_INVALID_ARG); + TEST_ASSERT_EQUAL(esp_gmf_sonic_get_speed(handle, &speed), ESP_GMF_ERR_OK); + // Set pitch function test + TEST_ASSERT_EQUAL(esp_gmf_sonic_set_pitch(NULL, pitch), ESP_GMF_ERR_INVALID_ARG); + TEST_ASSERT_EQUAL(esp_gmf_sonic_set_pitch(handle, pitch), ESP_GMF_ERR_OK); + // Get pitch function test + TEST_ASSERT_EQUAL(esp_gmf_sonic_get_pitch(NULL, &pitch), ESP_GMF_ERR_INVALID_ARG); + TEST_ASSERT_EQUAL(esp_gmf_sonic_get_pitch(handle, &pitch), ESP_GMF_ERR_OK); + // Deinitialize function test + TEST_ASSERT_EQUAL(esp_gmf_obj_delete(handle), ESP_GMF_ERR_OK); + // Test for config is NULL, will create a default config + TEST_ASSERT_EQUAL(esp_gmf_sonic_init(NULL, &handle), ESP_GMF_ERR_OK); + TEST_ASSERT_NOT_EQUAL(NULL, OBJ_GET_CFG(handle)); + TEST_ASSERT_EQUAL(esp_gmf_obj_delete(handle), ESP_GMF_ERR_OK); +} + +void test_esp_gmf_dec_if() +{ + esp_audio_simple_dec_cfg_t config = DEFAULT_ESP_GMF_AUDIO_DEC_CONFIG(); + esp_gmf_obj_handle_t handle; + // Initialize function test + TEST_ASSERT_EQUAL(esp_gmf_audio_dec_init(&config, NULL), ESP_GMF_ERR_INVALID_ARG); + TEST_ASSERT_EQUAL(esp_gmf_audio_dec_init(&config, &handle), ESP_GMF_ERR_OK); + // Deinitialize function test + TEST_ASSERT_EQUAL(esp_gmf_obj_delete(handle), ESP_GMF_ERR_OK); + // Test for config is NULL, will create a default config + TEST_ASSERT_EQUAL(esp_gmf_audio_dec_init(NULL, &handle), ESP_GMF_ERR_OK); + esp_audio_simple_dec_cfg_t *cfg = OBJ_GET_CFG(handle); + TEST_ASSERT_NOT_EQUAL(NULL, cfg); + TEST_ASSERT_EQUAL(cfg->dec_type, ESP_AUDIO_SIMPLE_DEC_TYPE_NONE); + TEST_ASSERT_EQUAL(cfg->dec_cfg, NULL); + TEST_ASSERT_EQUAL(cfg->cfg_size, 0); + TEST_ASSERT_EQUAL(esp_gmf_obj_delete(handle), ESP_GMF_ERR_OK); +} + +void test_esp_gmf_enc_if() +{ + esp_audio_enc_config_t config = DEFAULT_ESP_GMF_AUDIO_ENC_CONFIG(); + esp_gmf_obj_handle_t handle; + // Initialize function test + TEST_ASSERT_EQUAL(esp_gmf_audio_enc_init(&config, NULL), ESP_GMF_ERR_INVALID_ARG); + TEST_ASSERT_EQUAL(esp_gmf_audio_enc_init(&config, &handle), ESP_GMF_ERR_OK); + // Deinitialize function test + TEST_ASSERT_EQUAL(esp_gmf_obj_delete(handle), ESP_GMF_ERR_OK); + // Test for config is NULL, will create a default config + TEST_ASSERT_EQUAL(esp_gmf_audio_enc_init(NULL, &handle), ESP_GMF_ERR_OK); + esp_audio_enc_config_t *cfg = OBJ_GET_CFG(handle); + TEST_ASSERT_NOT_EQUAL(NULL, cfg); + TEST_ASSERT_EQUAL(cfg->type, ESP_AUDIO_TYPE_UNSUPPORT); + TEST_ASSERT_EQUAL(cfg->cfg, NULL); + TEST_ASSERT_EQUAL(cfg->cfg_sz, 0); + TEST_ASSERT_EQUAL(esp_gmf_obj_delete(handle), ESP_GMF_ERR_OK); +} + +void test_esp_gmf_io_embed_flash_if() +{ + embed_flash_io_cfg_t config = EMBED_FLASH_CFG_DEFAULT(); + esp_gmf_obj_handle_t handle; + const embed_item_info_t context = {0}; + int max_num = 3; + // Initialize function test + TEST_ASSERT_EQUAL(esp_gmf_io_embed_flash_init(&config, NULL), ESP_GMF_ERR_INVALID_ARG); + TEST_ASSERT_EQUAL(esp_gmf_io_embed_flash_init(&config, &handle), ESP_GMF_ERR_OK); + // Set context function test + TEST_ASSERT_EQUAL(esp_gmf_io_embed_flash_set_context(NULL, &context, max_num), ESP_GMF_ERR_INVALID_ARG); + TEST_ASSERT_EQUAL(esp_gmf_io_embed_flash_set_context(handle, NULL, max_num), ESP_GMF_ERR_INVALID_ARG); + TEST_ASSERT_EQUAL(esp_gmf_io_embed_flash_set_context(handle, &context, max_num), ESP_GMF_ERR_OK); + // Deinitialize function test + TEST_ASSERT_EQUAL(esp_gmf_obj_delete(handle), ESP_GMF_ERR_OK); +} + +void test_esp_gmf_io_file_if() +{ + file_io_cfg_t config = FILE_IO_CFG_DEFAULT(); + esp_gmf_obj_handle_t handle; + // Initialize function test + TEST_ASSERT_EQUAL(esp_gmf_io_file_init(&config, NULL), ESP_GMF_ERR_INVALID_ARG); + config.dir = ESP_GMF_IO_DIR_NONE; + TEST_ASSERT_EQUAL(esp_gmf_io_file_init(&config, &handle), ESP_GMF_ERR_NOT_SUPPORT); + config.dir = ESP_GMF_IO_DIR_READER; + TEST_ASSERT_EQUAL(esp_gmf_io_file_init(&config, &handle), ESP_GMF_ERR_OK); + // Deinitialize function test + TEST_ASSERT_EQUAL(esp_gmf_obj_delete(handle), ESP_GMF_ERR_OK); +} + +void test_esp_gmf_io_http_if() +{ + http_io_cfg_t config = HTTP_STREAM_CFG_DEFAULT(); + esp_gmf_obj_handle_t handle; + const char *cert = "test"; + // Initialize function test + TEST_ASSERT_EQUAL(esp_gmf_io_http_init(&config, NULL), ESP_GMF_ERR_INVALID_ARG); + config.dir = ESP_GMF_IO_DIR_NONE; + TEST_ASSERT_EQUAL(esp_gmf_io_http_init(&config, &handle), ESP_GMF_ERR_NOT_SUPPORT); + config.dir = ESP_GMF_IO_DIR_READER; + TEST_ASSERT_EQUAL(esp_gmf_io_http_init(&config, &handle), ESP_GMF_ERR_OK); + // Reset function test + TEST_ASSERT_EQUAL(esp_gmf_io_http_reset(NULL), ESP_GMF_ERR_INVALID_ARG); + TEST_ASSERT_EQUAL(esp_gmf_io_http_reset(handle), ESP_GMF_ERR_OK); + // Set server cert function test + TEST_ASSERT_EQUAL(esp_gmf_io_http_set_server_cert(NULL, cert), ESP_GMF_ERR_INVALID_ARG); + TEST_ASSERT_EQUAL(esp_gmf_io_http_set_server_cert(handle, cert), ESP_GMF_ERR_OK); + // Deinitialize function test + TEST_ASSERT_EQUAL(esp_gmf_obj_delete(handle), ESP_GMF_ERR_OK); +} + +void test_esp_gmf_io_i2s_if() +{ + i2s_pdm_io_cfg_t config = ESP_GMF_IO_I2S_PDM_CFG_DEFAULT(); + esp_gmf_obj_handle_t handle; + // Initialize function test + TEST_ASSERT_EQUAL(esp_gmf_io_i2s_pdm_init(&config, NULL), ESP_GMF_ERR_INVALID_ARG); + config.dir = ESP_GMF_IO_DIR_NONE; + TEST_ASSERT_EQUAL(esp_gmf_io_i2s_pdm_init(&config, &handle), ESP_GMF_ERR_NOT_SUPPORT); + config.dir = ESP_GMF_IO_DIR_READER; + TEST_ASSERT_EQUAL(esp_gmf_io_i2s_pdm_init(&config, &handle), ESP_GMF_ERR_OK); + // Deinitialize function test + TEST_ASSERT_EQUAL(esp_gmf_obj_delete(handle), ESP_GMF_ERR_OK); +} + +void test_esp_gmf_copier_if() +{ + esp_gmf_copier_cfg_t config; + esp_gmf_obj_handle_t handle; + // Initialize function test + TEST_ASSERT_EQUAL(esp_gmf_copier_init(&config, NULL), ESP_GMF_ERR_INVALID_ARG); + TEST_ASSERT_EQUAL(esp_gmf_copier_init(&config, &handle), ESP_GMF_ERR_OK); + // Deinitialize function test + TEST_ASSERT_EQUAL(esp_gmf_obj_delete(handle), ESP_GMF_ERR_OK); +} + +TEST_CASE("Test element if check", "[ESP_GMF_IF_CHECK]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + test_esp_gmf_alc_if(); + test_esp_gmf_bit_cvt_if(); + test_esp_gmf_ch_cvt_if(); + test_esp_gmf_deinterleave_if(); + test_esp_gmf_eq_if(); + test_esp_gmf_fade_if(); + test_esp_gmf_interleave_if(); + test_esp_gmf_mixer_if(); + test_esp_gmf_rate_cvt_if(); + test_esp_gmf_sonic_if(); + test_esp_gmf_dec_if(); + test_esp_gmf_enc_if(); + test_esp_gmf_io_embed_flash_if(); + test_esp_gmf_io_file_if(); + test_esp_gmf_io_http_if(); + test_esp_gmf_io_i2s_if(); + test_esp_gmf_copier_if(); +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/main/elements/gmf_image_effects_test.c b/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/main/elements/gmf_image_effects_test.c new file mode 100644 index 0000000..fd0b332 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/main/elements/gmf_image_effects_test.c @@ -0,0 +1,265 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "unity.h" +#include +#include "freertos/FreeRTOS.h" +#include "freertos/event_groups.h" +#include "esp_gmf_data_bus.h" +#include "esp_gmf_video_element.h" +#include "esp_gmf_video_scale.h" +#include "esp_gmf_video_crop.h" +#include "esp_gmf_video_rotate.h" +#include "esp_gmf_video_color_convert.h" + +#define TEST_MAX_EL_NUM (4) +#define TEST_MAX_IMG_LENGTH (640 * 480 * 3) +#define TEST_TICK_DELAY (0) +static esp_gmf_obj_handle_t obj_hd_pool[TEST_MAX_EL_NUM] = {NULL}; +static uint8_t *test_buffer = NULL; +static uint8_t *test_buffer1 = NULL; + +static const char *TAG = "IMGFX_SCALE_TEST"; + +void esp_gmf_destory_obj_cfg_pool() +{ + for (size_t i = 0; i < TEST_MAX_EL_NUM; i++) { + esp_gmf_obj_delete(obj_hd_pool[i]); + obj_hd_pool[i] = NULL; + } +} + +static esp_gmf_err_t esp_gmf_create_obj_cfg_pool() +{ + esp_gmf_job_err_t ret = ESP_GMF_JOB_ERR_OK; + esp_imgfx_scale_cfg_t scale_cfg = { + .in_pixel_fmt = ESP_IMGFX_PIXEL_FMT_RGB888, + .in_res = {640, 480}, + .scale_res = {320, 240}, + .filter_type = ESP_IMGFX_SCALE_FILTER_TYPE_BILINEAR}; + ret = esp_gmf_video_scale_init(&scale_cfg, &obj_hd_pool[0]); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, { goto _cfg_free;}, "Failed to append scale obj to pool"); + esp_imgfx_crop_cfg_t crop_cfg = { + .in_res = {100, 100}, + .in_pixel_fmt = ESP_IMGFX_PIXEL_FMT_RGB888, + .cropped_res = {50, 50}, + .x_pos = 25, + .y_pos = 25}; + ret = esp_gmf_video_crop_init(&crop_cfg, &obj_hd_pool[1]); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, { goto _cfg_free;}, "Failed to append crop obj to pool"); + esp_imgfx_rotate_cfg_t rotate_cfg = { + .in_pixel_fmt = ESP_IMGFX_PIXEL_FMT_RGB888, + .in_res = {.width = 256, .height = 256}, + .degree = 90}; + ret = esp_gmf_video_rotate_init(&rotate_cfg, &obj_hd_pool[2]); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, { goto _cfg_free;}, "Failed to append rotate obj to pool"); + esp_imgfx_color_convert_cfg_t color_convert_cfg = { + .in_res = {640, 480}, + .in_pixel_fmt = ESP_IMGFX_PIXEL_FMT_RGB565_LE, + .out_pixel_fmt = ESP_IMGFX_PIXEL_FMT_RGB888, + .color_space_std = ESP_IMGFX_COLOR_SPACE_STD_BT601}; + ret = esp_gmf_video_color_convert_init(&color_convert_cfg, &obj_hd_pool[3]); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, { goto _cfg_free;}, "Failed to append color convert obj to pool"); + return ret; +_cfg_free: + esp_gmf_destory_obj_cfg_pool(); + return ret; +} + +static esp_gmf_err_t event_cb(esp_gmf_event_pkt_t *evt, void *ctx) +{ + ESP_GMF_NULL_CHECK(TAG, ctx, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, evt, return ESP_GMF_ERR_INVALID_ARG); + if ((evt->type != ESP_GMF_EVT_TYPE_REPORT_INFO) + || (evt->sub != ESP_GMF_INFO_VIDEO) + || (evt->payload == NULL)) { + return ESP_GMF_ERR_OK; + } + esp_gmf_info_video_t *info = (esp_gmf_info_video_t *)evt->payload; + // Check received info + esp_gmf_info_video_t get_info = {0}; + esp_gmf_video_el_get_src_info(ctx, &get_info); + TEST_ASSERT_EQUAL(info->fps, get_info.fps); + TEST_ASSERT_EQUAL(info->bitrate, get_info.bitrate); + const char *tag = OBJ_GET_TAG(ctx); + if (strcmp(tag, "vid_scale") == 0) { + esp_imgfx_scale_cfg_t *cfg = (esp_imgfx_scale_cfg_t *)OBJ_GET_CFG(ctx); + TEST_ASSERT_EQUAL(info->width, cfg->scale_res.width); + TEST_ASSERT_EQUAL(info->height, cfg->scale_res.height); + TEST_ASSERT_EQUAL(info->format_id, cfg->in_pixel_fmt); + } else if (strcmp(tag, "vid_crop") == 0) { + esp_imgfx_crop_cfg_t *cfg = (esp_imgfx_crop_cfg_t *)OBJ_GET_CFG(ctx); + TEST_ASSERT_EQUAL(info->width, cfg->cropped_res.width); + TEST_ASSERT_EQUAL(info->height, cfg->cropped_res.height); + TEST_ASSERT_EQUAL(info->format_id, cfg->in_pixel_fmt); + } else if (strcmp(tag, "vid_rotate") == 0) { + esp_imgfx_rotate_cfg_t *cfg = (esp_imgfx_rotate_cfg_t *)OBJ_GET_CFG(ctx); + if (cfg->degree % 90 == 0 + || cfg->degree % 270 == 0) { + TEST_ASSERT_EQUAL(info->width, cfg->in_res.height); + TEST_ASSERT_EQUAL(info->height, cfg->in_res.width); + } else { + TEST_ASSERT_EQUAL(info->width, cfg->in_res.width); + TEST_ASSERT_EQUAL(info->height, cfg->in_res.height); + } + TEST_ASSERT_EQUAL(info->format_id, cfg->in_pixel_fmt); + } else if (strcmp(tag, "vid_color_cvt") == 0) { + esp_imgfx_color_convert_cfg_t *cfg = (esp_imgfx_color_convert_cfg_t *)OBJ_GET_CFG(ctx); + TEST_ASSERT_EQUAL(info->width, cfg->in_res.width); + TEST_ASSERT_EQUAL(info->height, cfg->in_res.height); + TEST_ASSERT_EQUAL(info->format_id, cfg->out_pixel_fmt); + } else { + return ESP_GMF_ERR_FAIL; + } + ESP_LOGI(TAG, "Event callback"); + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_io_t imgfx_acquire_read(void *handle, esp_gmf_data_bus_block_t *blk, int wanted_size, int block_ticks) +{ + blk->buf = test_buffer; + blk->buf_length = TEST_MAX_IMG_LENGTH; + blk->valid_size = wanted_size; + memset(blk->buf, 1, wanted_size); + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_io_t imgfx_release_read(void *handle, esp_gmf_data_bus_block_t *blk, int block_ticks) +{ + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_io_t imgfx_acquire_write(void *handle, esp_gmf_data_bus_block_t *blk, int wanted_size, int block_ticks) +{ + blk->buf = test_buffer1; + blk->buf_length = TEST_MAX_IMG_LENGTH; + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_io_t imgfx_release_write(void *handle, esp_gmf_data_bus_block_t *blk, int block_ticks) +{ + ESP_LOGI(TAG, "Output size %d", blk->valid_size); + return ESP_GMF_ERR_OK; +} + +TEST_CASE("Test for all software video effects", "[ESP_GMF_Effects]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + ESP_GMF_MEM_SHOW(TAG); + esp_gmf_port_handle_t in_port = NULL; + esp_gmf_port_handle_t out_port = NULL; + esp_gmf_obj_handle_t obj_hd = {NULL}; + esp_gmf_info_video_t info = { + .fps = 30, + .width = 640, + .height = 480, + .format_id = ESP_IMGFX_PIXEL_FMT_RGB888, + .bitrate = 1000000, + }; + esp_gmf_event_pkt_t event_pkt = { + .from = NULL, + .type = ESP_GMF_EVT_TYPE_REPORT_INFO, + .sub = ESP_GMF_INFO_VIDEO, + .payload = &info, + .payload_size = sizeof(info), + }; + // Create obj handle and cfg pool + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_create_obj_cfg_pool()); + test_buffer = (uint8_t *)malloc(TEST_MAX_IMG_LENGTH); + TEST_ASSERT_NOT_EQUAL(NULL, test_buffer); + test_buffer1 = (uint8_t *)malloc(TEST_MAX_IMG_LENGTH); + TEST_ASSERT_NOT_EQUAL(NULL, test_buffer1); + + for (size_t test_count = 0; test_count < 5; test_count++) { + for (size_t el_index = 0; el_index < TEST_MAX_EL_NUM; el_index++) { + // New obj + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_obj_dupl(obj_hd_pool[el_index], &obj_hd)); + // Print obj tag + ESP_LOGE(TAG, "%s-%d,obj_hd:%p", (OBJ_GET_TAG(obj_hd)), __LINE__, obj_hd); + // Create in/out port + in_port = NEW_ESP_GMF_PORT_IN_BLOCK(imgfx_acquire_read, imgfx_release_read, NULL, NULL, TEST_MAX_IMG_LENGTH, TEST_TICK_DELAY); + out_port = NEW_ESP_GMF_PORT_OUT_BLOCK(imgfx_acquire_write, imgfx_release_write, NULL, NULL, TEST_MAX_IMG_LENGTH, TEST_TICK_DELAY); + // Register in/out port + esp_gmf_element_register_in_port(obj_hd, in_port); + esp_gmf_element_register_out_port(obj_hd, out_port); + for (size_t i = 0; i < 4; i++) { + // set event callback + esp_gmf_element_set_event_func(obj_hd, event_cb, obj_hd); + // report info + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_element_receive_event(obj_hd, &event_pkt, NULL)); + // Open obj + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_element_process_open(obj_hd, NULL)); + // Check received info + esp_gmf_info_video_t get_info = {0}; + esp_gmf_video_el_get_src_info(obj_hd, &get_info); + TEST_ASSERT_EQUAL(info.fps, get_info.fps); + TEST_ASSERT_EQUAL(info.width, get_info.width); + TEST_ASSERT_EQUAL(info.height, get_info.height); + TEST_ASSERT_EQUAL(info.format_id, get_info.format_id); + TEST_ASSERT_EQUAL(info.bitrate, get_info.bitrate); + // Run + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_element_process_running(obj_hd, NULL)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_element_process_running(obj_hd, NULL)); + // Close obj + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_element_process_close(obj_hd, NULL)); + } + // Delete obj: port will be deleted when obj handle pool is destroyed + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_obj_delete(obj_hd)); + } + } + // free buffer + free(test_buffer); + free(test_buffer1); + // Destroy obj handle and cfg pool + esp_gmf_destory_obj_cfg_pool(); + ESP_GMF_MEM_SHOW(TAG); +} + +TEST_CASE("ERROR: Input load is NULL", "[ESP_GMF_Effects]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + ESP_GMF_MEM_SHOW(TAG); + esp_gmf_port_handle_t in_port = NULL; + esp_gmf_port_handle_t out_port = NULL; + esp_gmf_obj_handle_t obj_hd = {NULL}; + // Create obj handle and cfg pool + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_create_obj_cfg_pool()); + test_buffer = (uint8_t *)malloc(TEST_MAX_IMG_LENGTH); + TEST_ASSERT_NOT_EQUAL(NULL, test_buffer); + test_buffer1 = (uint8_t *)malloc(TEST_MAX_IMG_LENGTH); + TEST_ASSERT_NOT_EQUAL(NULL, test_buffer1); + + for (size_t el_index = 0; el_index < TEST_MAX_EL_NUM; el_index++) { + // New obj + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_obj_dupl(obj_hd_pool[el_index], &obj_hd)); + // Print obj tag + ESP_LOGE(TAG, "%s-%d,obj_hd:%p", (OBJ_GET_TAG(obj_hd)), __LINE__, obj_hd); + // Create in/out port + in_port = NEW_ESP_GMF_PORT_IN_BLOCK(NULL, NULL, NULL, NULL, TEST_MAX_IMG_LENGTH, TEST_TICK_DELAY); + out_port = NEW_ESP_GMF_PORT_OUT_BLOCK(NULL, NULL, NULL, NULL, TEST_MAX_IMG_LENGTH, TEST_TICK_DELAY); + + uint32_t fake_reader_handle; + esp_gmf_port_set_writer(in_port, (void *)&fake_reader_handle); + // Register in/out port + esp_gmf_element_register_in_port(obj_hd, in_port); + esp_gmf_element_register_out_port(obj_hd, out_port); + for (size_t i = 0; i < 4; i++) { + // Open obj + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_element_process_open(obj_hd, NULL)); + // Run obj + TEST_ASSERT_EQUAL(ESP_GMF_ERR_FAIL, esp_gmf_element_process_running(obj_hd, NULL)); + // Close obj + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_element_process_close(obj_hd, NULL)); + } + // Delete obj: port will be deleted when obj handle pool is destroyed + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_obj_delete(obj_hd)); + } + // free buffer + free(test_buffer); + free(test_buffer1); + // Destroy obj handle and cfg pool + esp_gmf_destory_obj_cfg_pool(); + ESP_GMF_MEM_SHOW(TAG); +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/main/elements/gmf_video_el_test.c b/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/main/elements/gmf_video_el_test.c new file mode 100644 index 0000000..52b9d10 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/main/elements/gmf_video_el_test.c @@ -0,0 +1,955 @@ + +/** + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "unity.h" +#include "esp_gmf_video_ppa.h" +#include "esp_gmf_video_enc.h" +#include "esp_gmf_video_dec.h" +#include "esp_gmf_video_fps_cvt.h" +#include "esp_gmf_video_overlay.h" +#include "esp_gmf_pool.h" +#include "esp_video_enc_default.h" +#include "esp_video_dec_default.h" +#include "esp_video_codec_utils.h" +#include "gmf_video_pattern.h" +#include "esp_gmf_video_param.h" +#include "esp_gmf_caps_def.h" +#include "freertos/FreeRTOS.h" +#include "esp_log.h" +#include "esp_heap_caps.h" +#include "esp_gmf_video_scale.h" +#include "esp_gmf_video_crop.h" +#include "esp_gmf_video_rotate.h" +#include "esp_gmf_video_color_convert.h" +#include "gmf_loader_setup_defaults.h" + +#define TAG "VID_EL_TEST" + +#if CONFIG_IDF_TARGET_ESP32P4 +#define TEST_PATTERN_WIDTH (1280) +#define TEST_PATTERN_HEIGHT (720) +#else +#define TEST_PATTERN_WIDTH (320) +#define TEST_PATTERN_HEIGHT (240) +#endif +#define TEST_PATTERN_VERTICAL (false) +#define TEST_PATTERN_BAR_COUNT (8) +#define TEST_VIDEO_ALIGNMENT (128) + +#define SAFE_FREE(ptr) if (ptr) { \ + esp_gmf_oal_free(ptr); \ + ptr = NULL; \ +} + +#define ELEMS(arr) (sizeof(arr) / sizeof((arr)[0])) +#define VIDEO_EL_MAX_STACK_SIZE (40 * 1024) + +typedef struct { + // Src information + uint32_t in_frame_count; + uint32_t src_codec; + esp_gmf_video_resolution_t src_res; + uint8_t *src_pixel; + uint32_t src_size; + uint8_t *src_copy; + + // Overlay information + uint8_t *overlay_data; + uint32_t overlay_size; + esp_gmf_video_resolution_t overlay_res; + + // Out information + uint16_t rotate_degree; + uint32_t out_codec; + esp_gmf_video_resolution_t out_res; + uint8_t *out_pixel; + uint32_t out_size; + uint32_t out_frame_count; + uint32_t out_max_size; + bool no_need_free; +} video_el_test_t; + +typedef struct { + esp_gmf_pool_handle_t pool; + esp_gmf_pipeline_handle_t pipe; + esp_gmf_task_handle_t task; + esp_gmf_element_handle_t convert_hd; + esp_gmf_element_handle_t enc_hd; + esp_gmf_element_handle_t rate_hd; + esp_gmf_element_handle_t overlay_hd; + esp_gmf_element_handle_t dec_hd; +} convert_res_t; + +static video_el_test_t video_el_inst; + +static esp_gmf_element_handle_t get_element_by_caps_from_pool(esp_gmf_pool_handle_t pool, uint64_t caps_cc) +{ + const void *iter = NULL; + esp_gmf_element_handle_t element = NULL; + while (esp_gmf_pool_iterate_element(pool, &iter, &element) == ESP_GMF_ERR_OK) { + const esp_gmf_cap_t *caps = NULL; + esp_gmf_element_get_caps(element, &caps); + while (caps) { + if (caps->cap_eightcc == caps_cc) { + return element; + } + caps = caps->next; + } + } + return NULL; +} + +static esp_gmf_element_handle_t get_element_by_caps_from_pipe(esp_gmf_pipeline_handle_t pipe, uint64_t caps_cc) +{ + const esp_gmf_cap_t *caps = NULL; + esp_gmf_element_handle_t element = NULL; + esp_gmf_pipeline_get_head_el(pipe, &element); + + for (; element; esp_gmf_pipeline_get_next_el(pipe, element, &element)) { + esp_gmf_element_get_caps(element, &caps); + for (; caps; caps = caps->next) { + if (caps->cap_eightcc == caps_cc) { + return element; + } + } + } + return NULL; +} + +static void get_pattern_info(pattern_info_t *info, bool is_out) +{ + if (is_out == false) { + info->format_id = video_el_inst.src_codec; + info->res = video_el_inst.src_res; + info->pixel = video_el_inst.src_pixel; + info->data_size = video_el_inst.src_size; + info->bar_count = TEST_PATTERN_BAR_COUNT; + info->vertical = TEST_PATTERN_VERTICAL; + } else { + info->format_id = video_el_inst.out_codec; + info->res = video_el_inst.out_res; + info->pixel = video_el_inst.out_pixel; + info->data_size = video_el_inst.out_size; + info->bar_count = TEST_PATTERN_BAR_COUNT; + if (video_el_inst.rotate_degree == 90 || video_el_inst.rotate_degree == 270) { + info->vertical = !TEST_PATTERN_VERTICAL; + } + } +} + +static int allocate_src_pattern(uint32_t src_codec, bool copy) +{ + video_el_inst.src_res.width = TEST_PATTERN_WIDTH; + video_el_inst.src_res.height = TEST_PATTERN_HEIGHT; + video_el_inst.src_codec = src_codec; + esp_video_codec_resolution_t res = { + .width = video_el_inst.src_res.width, + .height = video_el_inst.src_res.height, + }; + video_el_inst.src_size = esp_video_codec_get_image_size((esp_video_codec_pixel_fmt_t)src_codec, &res); + video_el_inst.src_pixel = esp_gmf_oal_malloc_align(TEST_VIDEO_ALIGNMENT, video_el_inst.src_size); + if (video_el_inst.src_pixel == NULL) { + return -1; + } + pattern_info_t pattern_info = {}; + get_pattern_info(&pattern_info, false); + gen_pattern_color_bar(&pattern_info); + return 0; +} + +static void show_result_pattern(void) +{ + pattern_info_t src_info = {}; + pattern_info_t out_info = {}; + get_pattern_info(&src_info, false); + get_pattern_info(&out_info, true); + draw_convert_result(&src_info, &out_info); +} + +static void free_video_el_inst(void) +{ + SAFE_FREE(video_el_inst.src_pixel); + SAFE_FREE(video_el_inst.overlay_data); + if (video_el_inst.no_need_free == false) { + SAFE_FREE(video_el_inst.out_pixel); + } + video_el_inst.out_max_size = 0; + video_el_inst.out_pixel = NULL; +} + +static esp_gmf_err_io_t in_acquire(void *handle, esp_gmf_payload_t *load, uint32_t wanted_size, int wait_ticks) +{ + load->pts = video_el_inst.in_frame_count * 100; + load->buf = video_el_inst.src_pixel; + load->valid_size = video_el_inst.src_size; + load->buf_length = load->valid_size; + return ESP_GMF_IO_OK; +} + +static esp_gmf_err_io_t in_release(void *handle, esp_gmf_payload_t *load, uint32_t wanted_size, int wait_ticks) +{ + video_el_inst.in_frame_count++; + vTaskDelay(10 / portTICK_RATE_MS); + return ESP_GMF_IO_OK; +} + +static esp_gmf_err_io_t overlay_acquire(void *handle, esp_gmf_payload_t *load, uint32_t wanted_size, int wait_ticks) +{ + load->pts = 255; + load->buf = video_el_inst.overlay_data; + load->valid_size = video_el_inst.overlay_size; + load->buf_length = video_el_inst.overlay_size; + return ESP_GMF_IO_OK; +} + +static esp_gmf_err_io_t overlay_release(void *handle, esp_gmf_payload_t *load, uint32_t wanted_size, int wait_ticks) +{ + return ESP_GMF_IO_OK; +} + +static esp_gmf_err_io_t out_acquire(void *handle, esp_gmf_payload_t *load, uint32_t wanted_size, int wait_ticks) +{ + if (load->buf) { + video_el_inst.no_need_free = true; + } else { + if (wanted_size > video_el_inst.out_max_size) { + SAFE_FREE(video_el_inst.out_pixel); + uint8_t *new_buf = esp_gmf_oal_malloc_align(TEST_VIDEO_ALIGNMENT, wanted_size + TEST_VIDEO_ALIGNMENT); + if (new_buf == NULL) { + ESP_LOGE(TAG, "Fail to allocate %d bytes for output buffer", (int)wanted_size + TEST_VIDEO_ALIGNMENT); + return -1; + } + video_el_inst.out_pixel = new_buf; + video_el_inst.out_max_size = wanted_size; + } + load->buf = video_el_inst.out_pixel; + load->buf_length = video_el_inst.out_max_size; + } + return ESP_GMF_IO_OK; +} + +static esp_gmf_err_io_t out_release(void *handle, esp_gmf_payload_t *load, uint32_t wanted_size, int wait_ticks) +{ + video_el_inst.out_pixel = load->buf; + video_el_inst.out_size = load->valid_size; + ESP_LOGI(TAG, "Out frame %d size %d", (int)video_el_inst.out_frame_count, (int)video_el_inst.out_size); + video_el_inst.out_frame_count++; + // FIXME: why add this ugly code + if (video_el_inst.no_need_free == false) { + load->buf = NULL; + } + return ESP_GMF_IO_OK; +} + +static int prepare_pool(convert_res_t *res) +{ + memset(res, 0, sizeof(*res)); + esp_gmf_pool_init(&res->pool); + if (res->pool == NULL) { + return -1; + } + gmf_loader_setup_video_codec_default(res->pool); + gmf_loader_setup_video_effects_default(res->pool); + ESP_GMF_POOL_SHOW_ITEMS(res->pool); + return 0; +} + +static int prepare_convert_pipeline(convert_res_t *res, const char **elements) +{ + int n = 0; + const char **header = elements; + while (*header) { + n++; + header++; + } + esp_gmf_pool_new_pipeline(res->pool, NULL, elements, n, NULL, &res->pipe); + if (res->pipe == NULL) { + return -1; + } + + // Set input and output buffer + esp_gmf_port_handle_t in_port = NEW_ESP_GMF_PORT_IN_BLOCK(in_acquire, in_release, NULL, NULL, 0, ESP_GMF_MAX_DELAY); + esp_gmf_pipeline_reg_el_port(res->pipe, elements[0], ESP_GMF_IO_DIR_READER, in_port); + // Use byte block to avoid malloc again + esp_gmf_port_handle_t out_port = NEW_ESP_GMF_PORT_OUT_BLOCK(out_acquire, out_release, NULL, NULL, 0, ESP_GMF_MAX_DELAY); + esp_gmf_pipeline_reg_el_port(res->pipe, elements[n - 1], ESP_GMF_IO_DIR_WRITER, out_port); + + esp_gmf_task_cfg_t cfg = DEFAULT_ESP_GMF_TASK_CONFIG(); + cfg.thread.stack_in_ext = true; + cfg.thread.stack = VIDEO_EL_MAX_STACK_SIZE; + esp_gmf_task_init(&cfg, &res->task); + if (res->task == NULL) { + return -1; + } + + esp_gmf_pipeline_bind_task(res->pipe, res->task); + esp_gmf_pipeline_loading_jobs(res->pipe); + + esp_gmf_pipeline_get_el_by_name(res->pipe, "vid_ppa", &res->convert_hd); + esp_gmf_pipeline_get_el_by_name(res->pipe, "vid_enc", &res->enc_hd); + esp_gmf_pipeline_get_el_by_name(res->pipe, "vid_fps_cvt", &res->rate_hd); + esp_gmf_pipeline_get_el_by_name(res->pipe, "vid_overlay", &res->overlay_hd); + esp_gmf_pipeline_get_el_by_name(res->pipe, "vid_dec", &res->dec_hd); + + return 0; +} + +static void release_convert_pipeline(convert_res_t *res) +{ + if (res->pipe) { + esp_gmf_pipeline_stop(res->pipe); + } + if (res->task) { + esp_gmf_task_deinit(res->task); + } + if (res->pipe) { + esp_gmf_pipeline_destroy(res->pipe); + } + gmf_loader_teardown_video_codec_default(res->pool); + gmf_loader_teardown_video_effects_default(res->pool); + esp_gmf_pool_deinit(res->pool); +} + +static int test_color_convert(convert_res_t *res, uint32_t convert_pair[][2], int n) +{ + for (int i = 0; i < n; i++) { + // Gen pattern + allocate_src_pattern(convert_pair[i][0], false); + video_el_inst.out_res = video_el_inst.src_res; + video_el_inst.out_codec = convert_pair[i][1]; + esp_gmf_info_video_t info = { + .format_id = convert_pair[i][0], + .width = TEST_PATTERN_WIDTH, + .height = TEST_PATTERN_HEIGHT, + }; + esp_gmf_element_handle_t convert_hd; + convert_hd = get_element_by_caps_from_pipe(res->pipe, ESP_GMF_CAPS_VIDEO_COLOR_CONVERT); + esp_gmf_video_param_set_dst_format(convert_hd, convert_pair[i][1]); + esp_gmf_pipeline_report_info(res->pipe, ESP_GMF_INFO_VIDEO, &info, sizeof(info)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_run(res->pipe)); + vTaskDelay(1000 / portTICK_RATE_MS); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_stop(res->pipe)); + show_result_pattern(); + free_video_el_inst(); + esp_gmf_pipeline_reset(res->pipe); + esp_gmf_pipeline_loading_jobs(res->pipe); + } + return 0; +} + +#ifdef CONFIG_IDF_TARGET_ESP32P4 +TEST_CASE("Color convert HW", "[ESP_GMF_VIDEO]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + ESP_GMF_MEM_SHOW(TAG); +#ifdef MEDIA_LIB_MEM_TEST + media_lib_add_default_adapter(); +#endif /* MEDIA_LIB_MEM_TEST */ + convert_res_t res; + memset(&video_el_inst, 0, sizeof(video_el_test_t)); + prepare_pool(&res); + const char *name[] = {"vid_ppa", NULL}; + TEST_ASSERT_EQUAL(0, prepare_convert_pipeline(&res, (const char **)name)); + uint32_t convert_pair[][2] = { + {ESP_FOURCC_RGB16, ESP_FOURCC_OUYY_EVYY}, + {ESP_FOURCC_OUYY_EVYY, ESP_FOURCC_RGB16}, + }; + test_color_convert(&res, convert_pair, ELEMS(convert_pair)); + + // Clear up resources + release_convert_pipeline(&res); + +#ifdef MEDIA_LIB_MEM_TEST + media_lib_stop_mem_trace(); +#endif /* MEDIA_LIB_MEM_TEST */ + ESP_GMF_MEM_SHOW(TAG); +} +#endif + +TEST_CASE("Color convert SW", "[ESP_GMF_VIDEO]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + ESP_GMF_MEM_SHOW(TAG); +#ifdef MEDIA_LIB_MEM_TEST + media_lib_add_default_adapter(); +#endif /* MEDIA_LIB_MEM_TEST */ + convert_res_t res; + memset(&video_el_inst, 0, sizeof(video_el_test_t)); + prepare_pool(&res); + const char *name[] = {"vid_color_cvt", NULL}; + TEST_ASSERT_EQUAL(0, prepare_convert_pipeline(&res, (const char **)name)); + + uint32_t convert_pair[][2] = { + {ESP_FOURCC_RGB16, ESP_FOURCC_OUYY_EVYY}, + {ESP_FOURCC_OUYY_EVYY, ESP_FOURCC_RGB16}, + {ESP_FOURCC_RGB16, ESP_FOURCC_BGR16}, + {ESP_FOURCC_RGB16, ESP_FOURCC_RGB24}, + }; + test_color_convert(&res, convert_pair, ELEMS(convert_pair)); + + // Clear up resources + release_convert_pipeline(&res); + +#ifdef MEDIA_LIB_MEM_TEST + media_lib_stop_mem_trace(); +#endif /* MEDIA_LIB_MEM_TEST */ + ESP_GMF_MEM_SHOW(TAG); +} + +TEST_CASE("Color convert by caps", "[ESP_GMF_VIDEO]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + ESP_GMF_MEM_SHOW(TAG); +#ifdef MEDIA_LIB_MEM_TEST + media_lib_add_default_adapter(); +#endif /* MEDIA_LIB_MEM_TEST */ + convert_res_t res; + memset(&video_el_inst, 0, sizeof(video_el_test_t)); + prepare_pool(&res); + esp_gmf_element_handle_t element; + element = get_element_by_caps_from_pool(res.pool, ESP_GMF_CAPS_VIDEO_COLOR_CONVERT); + const char *name[] = {OBJ_GET_TAG(element), NULL}; + printf("Get element name %s\n", OBJ_GET_TAG(element)); + TEST_ASSERT_EQUAL(0, prepare_convert_pipeline(&res, (const char **)name)); + + uint32_t convert_pair[][2] = { + {ESP_FOURCC_RGB16, ESP_FOURCC_OUYY_EVYY}, + {ESP_FOURCC_OUYY_EVYY, ESP_FOURCC_RGB16}, + }; + test_color_convert(&res, convert_pair, ELEMS(convert_pair)); + + // Clear up resources + release_convert_pipeline(&res); + +#ifdef MEDIA_LIB_MEM_TEST + media_lib_stop_mem_trace(); +#endif /* MEDIA_LIB_MEM_TEST */ + ESP_GMF_MEM_SHOW(TAG); +} + +static int test_scale(convert_res_t *res, uint32_t convert_pair[][2], int n) +{ + for (int i = 0; i < n; i++) { + // Gen pattern + allocate_src_pattern(convert_pair[i][0], false); + video_el_inst.out_res.width = video_el_inst.src_res.width >> 1; + video_el_inst.out_res.height = video_el_inst.src_res.height >> 1; + video_el_inst.out_codec = convert_pair[i][1]; + esp_gmf_element_handle_t convert_hd; + convert_hd = get_element_by_caps_from_pipe(res->pipe, ESP_GMF_CAPS_VIDEO_COLOR_CONVERT); + esp_gmf_video_param_set_dst_format(convert_hd, convert_pair[i][1]); + esp_gmf_element_handle_t scale_hd; + scale_hd = get_element_by_caps_from_pipe(res->pipe, ESP_GMF_CAPS_VIDEO_SCALE); + esp_gmf_video_param_set_dst_resolution(scale_hd, &video_el_inst.out_res); + + esp_gmf_info_video_t info = { + .format_id = convert_pair[i][0], + .width = TEST_PATTERN_WIDTH, + .height = TEST_PATTERN_HEIGHT, + }; + + esp_gmf_pipeline_report_info(res->pipe, ESP_GMF_INFO_VIDEO, &info, sizeof(info)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_run(res->pipe)); + vTaskDelay(1000 / portTICK_RATE_MS); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_stop(res->pipe)); + show_result_pattern(); + free_video_el_inst(); + esp_gmf_pipeline_reset(res->pipe); + esp_gmf_pipeline_loading_jobs(res->pipe); + esp_cpu_clear_watchpoint(0); + } + return 0; +} + +#ifdef CONFIG_IDF_TARGET_ESP32P4 +TEST_CASE("Scale HW", "[ESP_GMF_VIDEO]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + ESP_GMF_MEM_SHOW(TAG); +#ifdef MEDIA_LIB_MEM_TEST + media_lib_add_default_adapter(); +#endif /* MEDIA_LIB_MEM_TEST */ + convert_res_t res; + memset(&video_el_inst, 0, sizeof(video_el_test_t)); + prepare_pool(&res); + const char *name[] = {"vid_ppa", NULL}; + TEST_ASSERT_EQUAL(0, prepare_convert_pipeline(&res, (const char **)name)); + + uint32_t convert_pair[][2] = { + {ESP_FOURCC_RGB16, ESP_FOURCC_OUYY_EVYY}, + {ESP_FOURCC_OUYY_EVYY, ESP_FOURCC_RGB16}, + }; + test_scale(&res, convert_pair, ELEMS(convert_pair)); + + // Clearup resources + release_convert_pipeline(&res); + +#ifdef MEDIA_LIB_MEM_TEST + media_lib_stop_mem_trace(); +#endif /* MEDIA_LIB_MEM_TEST */ + ESP_GMF_MEM_SHOW(TAG); +} +#endif + +TEST_CASE("Scale SW", "[ESP_GMF_VIDEO]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + ESP_GMF_MEM_SHOW(TAG); +#ifdef MEDIA_LIB_MEM_TEST + media_lib_add_default_adapter(); +#endif /* MEDIA_LIB_MEM_TEST */ + convert_res_t res; + memset(&video_el_inst, 0, sizeof(video_el_test_t)); + prepare_pool(&res); + const char *name[] = {"vid_scale", NULL}; + TEST_ASSERT_EQUAL(0, prepare_convert_pipeline(&res, (const char **)name)); + uint32_t convert_pair[][2] = { + {ESP_FOURCC_RGB16, ESP_FOURCC_RGB16}, + {ESP_FOURCC_RGB24, ESP_FOURCC_RGB24}, + }; + test_scale(&res, convert_pair, ELEMS(convert_pair)); + + // Clearup resources + release_convert_pipeline(&res); + +#ifdef MEDIA_LIB_MEM_TEST + media_lib_stop_mem_trace(); +#endif /* MEDIA_LIB_MEM_TEST */ + ESP_GMF_MEM_SHOW(TAG); +} + +static int test_rotate(convert_res_t *res, uint32_t convert_pair[][2], int n) +{ + for (int i = 0; i < n; i++) { + // Gen pattern + allocate_src_pattern(convert_pair[i][0], false); + video_el_inst.out_res.width = video_el_inst.src_res.height; + video_el_inst.out_res.height = video_el_inst.src_res.width; + video_el_inst.out_codec = convert_pair[i][1]; + video_el_inst.rotate_degree = i & 1 ? 90 : 270; + + esp_gmf_element_handle_t convert_hd; + convert_hd = get_element_by_caps_from_pipe(res->pipe, ESP_GMF_CAPS_VIDEO_COLOR_CONVERT); + esp_gmf_video_param_set_dst_format(convert_hd, convert_pair[i][1]); + esp_gmf_element_handle_t rotate_hd; + rotate_hd = get_element_by_caps_from_pipe(res->pipe, ESP_GMF_CAPS_VIDEO_ROTATE); + esp_gmf_video_param_set_rotate_angle(rotate_hd, video_el_inst.rotate_degree); + esp_gmf_video_param_set_dst_resolution(rotate_hd, &video_el_inst.out_res); + + esp_gmf_info_video_t info = { + .format_id = convert_pair[i][0], + .width = TEST_PATTERN_WIDTH, + .height = TEST_PATTERN_HEIGHT, + }; + + esp_gmf_pipeline_report_info(res->pipe, ESP_GMF_INFO_VIDEO, &info, sizeof(info)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_run(res->pipe)); + vTaskDelay(1000 / portTICK_RATE_MS); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_stop(res->pipe)); + show_result_pattern(); + free_video_el_inst(); + esp_gmf_pipeline_reset(res->pipe); + esp_gmf_pipeline_loading_jobs(res->pipe); + } + return 0; +} + +#ifdef CONFIG_IDF_TARGET_ESP32P4 +TEST_CASE("Rotate HW", "[ESP_GMF_VIDEO]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + ESP_GMF_MEM_SHOW(TAG); +#ifdef MEDIA_LIB_MEM_TEST + media_lib_add_default_adapter(); +#endif /* MEDIA_LIB_MEM_TEST */ + convert_res_t res; + memset(&video_el_inst, 0, sizeof(video_el_test_t)); + prepare_pool(&res); + const char *name[] = {"vid_ppa", NULL}; + TEST_ASSERT_EQUAL(0, prepare_convert_pipeline(&res, name)); + uint32_t convert_pair[][2] = { + {ESP_FOURCC_RGB16, ESP_FOURCC_OUYY_EVYY}, + {ESP_FOURCC_OUYY_EVYY, ESP_FOURCC_RGB16}, + }; + test_rotate(&res, convert_pair, ELEMS(convert_pair)); + + // Clear up resources + release_convert_pipeline(&res); + +#ifdef MEDIA_LIB_MEM_TEST + media_lib_stop_mem_trace(); +#endif /* MEDIA_LIB_MEM_TEST */ + ESP_GMF_MEM_SHOW(TAG); +} +#endif + +TEST_CASE("Rotate SW", "[ESP_GMF_VIDEO]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + ESP_GMF_MEM_SHOW(TAG); +#ifdef MEDIA_LIB_MEM_TEST + media_lib_add_default_adapter(); +#endif /* MEDIA_LIB_MEM_TEST */ + convert_res_t res; + memset(&video_el_inst, 0, sizeof(video_el_test_t)); + prepare_pool(&res); + const char *name[] = {"vid_rotate", NULL}; + TEST_ASSERT_EQUAL(0, prepare_convert_pipeline(&res, (const char **)name)); + uint32_t convert_pair[][2] = { + {ESP_FOURCC_RGB16, ESP_FOURCC_RGB16}, + }; + test_rotate(&res, convert_pair, ELEMS(convert_pair)); + + // Clear up resources + release_convert_pipeline(&res); + +#ifdef MEDIA_LIB_MEM_TEST + media_lib_stop_mem_trace(); +#endif /* MEDIA_LIB_MEM_TEST */ + ESP_GMF_MEM_SHOW(TAG); +} + +static int test_crop(convert_res_t *res, uint32_t convert_pair[][2], int n) +{ + for (int i = 0; i < n; i++) { + // Gen pattern + allocate_src_pattern(convert_pair[i][0], false); + video_el_inst.out_res.width = video_el_inst.src_res.width >> 1; + video_el_inst.out_res.height = video_el_inst.src_res.height >> 1; + video_el_inst.out_codec = convert_pair[i][1]; + + esp_gmf_video_rgn_t crop_rgn = { + .x = video_el_inst.src_res.width >> 2, + .y = video_el_inst.src_res.height >> 2, + .width = video_el_inst.out_res.width, + .height = video_el_inst.out_res.height, + }; + esp_gmf_element_handle_t convert_hd; + convert_hd = get_element_by_caps_from_pipe(res->pipe, ESP_GMF_CAPS_VIDEO_COLOR_CONVERT); + esp_gmf_video_param_set_dst_format(convert_hd, convert_pair[i][1]); + + esp_gmf_element_handle_t crop_hd; + crop_hd = get_element_by_caps_from_pipe(res->pipe, ESP_GMF_CAPS_VIDEO_CROP); + esp_gmf_video_param_set_cropped_region(crop_hd, &crop_rgn); + esp_gmf_video_param_set_dst_resolution(crop_hd, &video_el_inst.out_res); + + esp_gmf_info_video_t info = { + .format_id = convert_pair[i][0], + .width = TEST_PATTERN_WIDTH, + .height = TEST_PATTERN_HEIGHT, + }; + + esp_gmf_pipeline_report_info(res->pipe, ESP_GMF_INFO_VIDEO, &info, sizeof(info)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_run(res->pipe)); + vTaskDelay(1000 / portTICK_RATE_MS); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_stop(res->pipe)); + show_result_pattern(); + free_video_el_inst(); + esp_gmf_pipeline_reset(res->pipe); + esp_gmf_pipeline_loading_jobs(res->pipe); + } + return 0; +} + +#ifdef CONFIG_IDF_TARGET_ESP32P4 +TEST_CASE("Crop HW", "[ESP_GMF_VIDEO]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + ESP_GMF_MEM_SHOW(TAG); +#ifdef MEDIA_LIB_MEM_TEST + media_lib_add_default_adapter(); +#endif /* MEDIA_LIB_MEM_TEST */ + convert_res_t res; + memset(&video_el_inst, 0, sizeof(video_el_test_t)); + prepare_pool(&res); + const char *name[] = {"vid_ppa", NULL}; + TEST_ASSERT_EQUAL(0, prepare_convert_pipeline(&res, name)); + uint32_t convert_pair[][2] = { + {ESP_FOURCC_RGB16, ESP_FOURCC_OUYY_EVYY}, + {ESP_FOURCC_OUYY_EVYY, ESP_FOURCC_RGB16}, + }; + test_crop(&res, convert_pair, ELEMS(convert_pair)); + // Clear up resources + release_convert_pipeline(&res); + +#ifdef MEDIA_LIB_MEM_TEST + media_lib_stop_mem_trace(); +#endif /* MEDIA_LIB_MEM_TEST */ + ESP_GMF_MEM_SHOW(TAG); +} +#endif + +TEST_CASE("Crop only SW", "[ESP_GMF_VIDEO]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + ESP_GMF_MEM_SHOW(TAG); +#ifdef MEDIA_LIB_MEM_TEST + media_lib_add_default_adapter(); +#endif /* MEDIA_LIB_MEM_TEST */ + convert_res_t res; + memset(&video_el_inst, 0, sizeof(video_el_test_t)); + prepare_pool(&res); + + const char *name[] = {"vid_crop", NULL}; + + TEST_ASSERT_EQUAL(0, prepare_convert_pipeline(&res, name)); + + uint32_t convert_pair[][2] = { + {ESP_FOURCC_RGB16, ESP_FOURCC_RGB16}, + // { ESP_FOURCC_RGB24, ESP_FOURCC_RGB16 }, + }; + test_crop(&res, convert_pair, ELEMS(convert_pair)); + // Clear up resources + release_convert_pipeline(&res); + +#ifdef MEDIA_LIB_MEM_TEST + media_lib_stop_mem_trace(); +#endif /* MEDIA_LIB_MEM_TEST */ + ESP_GMF_MEM_SHOW(TAG); +} + +TEST_CASE("Encoder only", "[ESP_GMF_VIDEO]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + ESP_GMF_MEM_SHOW(TAG); +#ifdef MEDIA_LIB_MEM_TEST + media_lib_add_default_adapter(); +#endif /* MEDIA_LIB_MEM_TEST */ + convert_res_t res; + memset(&video_el_inst, 0, sizeof(video_el_test_t)); + prepare_pool(&res); + const char *name[] = {"vid_enc", NULL}; + TEST_ASSERT_EQUAL(0, prepare_convert_pipeline(&res, name)); + + uint32_t convert_pair[][2] = { +#if CONFIG_IDF_TARGET_ESP32P4 + {ESP_FOURCC_OUYY_EVYY, ESP_FOURCC_H264}, + {ESP_FOURCC_RGB16, ESP_FOURCC_MJPG}, +#else + {ESP_FOURCC_RGB24, ESP_FOURCC_MJPG}, +#if CONFIG_IDF_TARGET_ESP32S3 + {ESP_FOURCC_YUV420P, ESP_FOURCC_H264}, +#endif /* CON FIG_IDF_TARGET_ESP32S3 */ +#endif /* CONFIG_IDF_TARGET_ESP32P4 */ + }; + for (int i = 0; i < ELEMS(convert_pair); i++) { + // Gen pattern + allocate_src_pattern(convert_pair[i][0], false); + video_el_inst.out_res = video_el_inst.src_res; + video_el_inst.out_codec = convert_pair[i][1]; + + esp_gmf_info_video_t info = { + .format_id = convert_pair[i][0], + .width = TEST_PATTERN_WIDTH, + .height = TEST_PATTERN_HEIGHT, + .fps = 10, + }; + // esp_gmf_video_enc_set_dst_codec(res.enc_hd, convert_pair[i][1]); + esp_gmf_video_param_set_dst_codec(res.enc_hd, convert_pair[i][1]); + esp_gmf_pipeline_report_info(res.pipe, ESP_GMF_INFO_VIDEO, &info, sizeof(info)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_run(res.pipe)); + vTaskDelay(1000 / portTICK_RATE_MS); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_stop(res.pipe)); + free_video_el_inst(); + esp_gmf_pipeline_reset(res.pipe); + esp_gmf_pipeline_loading_jobs(res.pipe); + } + // Clear up resources + release_convert_pipeline(&res); + +#ifdef MEDIA_LIB_MEM_TEST + media_lib_stop_mem_trace(); +#endif /* MEDIA_LIB_MEM_TEST */ + ESP_GMF_MEM_SHOW(TAG); +} + +TEST_CASE("FPS convert only", "[ESP_GMF_VIDEO]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + ESP_GMF_MEM_SHOW(TAG); +#ifdef MEDIA_LIB_MEM_TEST + media_lib_add_default_adapter(); +#endif /* MEDIA_LIB_MEM_TEST */ + convert_res_t res; + memset(&video_el_inst, 0, sizeof(video_el_test_t)); + prepare_pool(&res); + const char *name[] = {"vid_fps_cvt", NULL}; + TEST_ASSERT_EQUAL(0, prepare_convert_pipeline(&res, name)); + uint32_t convert_pair[][2] = { + {ESP_FOURCC_RGB16, ESP_FOURCC_MJPG}, + }; + for (int i = 0; i < ELEMS(convert_pair); i++) { + // Gen pattern + allocate_src_pattern(convert_pair[i][0], false); + video_el_inst.out_res = video_el_inst.src_res; + video_el_inst.out_codec = convert_pair[i][1]; + esp_gmf_info_video_t info = { + .format_id = convert_pair[i][0], + .width = TEST_PATTERN_WIDTH, + .height = TEST_PATTERN_HEIGHT, + .fps = 10, + }; + // esp_gmf_video_fps_cvt_set_fps(res.rate_hd, 5); + esp_gmf_video_param_set_fps(res.rate_hd, 5); + esp_gmf_pipeline_report_info(res.pipe, ESP_GMF_INFO_VIDEO, &info, sizeof(info)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_run(res.pipe)); + vTaskDelay(100 / portTICK_RATE_MS); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_stop(res.pipe)); + free_video_el_inst(); + esp_gmf_pipeline_reset(res.pipe); + esp_gmf_pipeline_loading_jobs(res.pipe); + } + // Clear up resources + release_convert_pipeline(&res); + +#ifdef MEDIA_LIB_MEM_TEST + media_lib_stop_mem_trace(); +#endif /* MEDIA_LIB_MEM_TEST */ + ESP_GMF_MEM_SHOW(TAG); +} + +TEST_CASE("Overlay Test", "[ESP_GMF_VIDEO]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + ESP_GMF_MEM_SHOW(TAG); +#ifdef MEDIA_LIB_MEM_TEST + media_lib_add_default_adapter(); +#endif /* MEDIA_LIB_MEM_TEST */ + convert_res_t res; + memset(&video_el_inst, 0, sizeof(video_el_test_t)); + prepare_pool(&res); + const char *name[] = {"vid_overlay", NULL}; + TEST_ASSERT_EQUAL(0, prepare_convert_pipeline(&res, name)); + + allocate_src_pattern(ESP_FOURCC_RGB16, false); + + // Create overlay buffer + video_el_inst.overlay_res.width = video_el_inst.src_res.width / 2; + video_el_inst.overlay_res.height = video_el_inst.src_res.height / 2; + esp_video_codec_resolution_t overlay_res = { + .width = video_el_inst.overlay_res.width, + .height = video_el_inst.overlay_res.height, + }; + video_el_inst.overlay_size = esp_video_codec_get_image_size((esp_video_codec_pixel_fmt_t)ESP_FOURCC_RGB16, + (esp_video_codec_resolution_t *)&overlay_res); + video_el_inst.overlay_data = esp_gmf_oal_malloc_align(TEST_VIDEO_ALIGNMENT, video_el_inst.overlay_size); + TEST_ASSERT_NOT_NULL(video_el_inst.src_pixel); + pattern_info_t pattern_info = { + .format_id = ESP_FOURCC_RGB16, + .res = video_el_inst.overlay_res, + .pixel = video_el_inst.overlay_data, + .data_size = video_el_inst.overlay_size, + .bar_count = TEST_PATTERN_BAR_COUNT / 2, + .vertical = !TEST_PATTERN_VERTICAL, + }; + gen_pattern_color_bar(&pattern_info); + + video_el_inst.out_res = video_el_inst.src_res; + video_el_inst.out_codec = ESP_FOURCC_RGB16; + + esp_gmf_overlay_rgn_info_t rgn = { + .format_id = ESP_FOURCC_RGB16, + .dst_rgn = { + .x = video_el_inst.src_res.width / 4, + .y = video_el_inst.src_res.height / 4, + .width = video_el_inst.overlay_res.width, + .height = video_el_inst.overlay_res.height, + }, + }; + // esp_gmf_video_overlay_set_rgn(res.overlay_hd, &rgn); + esp_gmf_video_param_set_overlay_rgn(res.overlay_hd, &rgn); + + esp_gmf_port_handle_t overlay_port = NEW_ESP_GMF_PORT_IN_BLOCK(overlay_acquire, overlay_release, NULL, NULL, 0, ESP_GMF_MAX_DELAY); + // esp_gmf_video_overlay_set_overlay_port(res.overlay_hd, overlay_port); + esp_gmf_video_param_set_overlay_port(res.overlay_hd, overlay_port); + // esp_gmf_video_overlay_enable(res.overlay_hd, true); + esp_gmf_video_param_overlay_enable(res.overlay_hd, true); + + esp_gmf_info_video_t info = { + .format_id = ESP_FOURCC_RGB16, + .width = TEST_PATTERN_WIDTH, + .height = TEST_PATTERN_HEIGHT, + .fps = 10, + }; + esp_gmf_pipeline_report_info(res.pipe, ESP_GMF_INFO_VIDEO, &info, sizeof(info)); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_run(res.pipe)); + vTaskDelay(100 / portTICK_RATE_MS); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_stop(res.pipe)); + show_result_pattern(); + + free_video_el_inst(); + // Clear up resources + release_convert_pipeline(&res); + if (overlay_port) { + esp_gmf_port_deinit(overlay_port); + } + +#ifdef MEDIA_LIB_MEM_TEST + media_lib_stop_mem_trace(); +#endif /* MEDIA_LIB_MEM_TEST */ + ESP_GMF_MEM_SHOW(TAG); +} + +TEST_CASE("Encoder to Decode", "[ESP_GMF_VIDEO]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + ESP_GMF_MEM_SHOW(TAG); +#ifdef MEDIA_LIB_MEM_TEST + media_lib_add_default_adapter(); +#endif /* MEDIA_LIB_MEM_TEST */ + convert_res_t res; + memset(&video_el_inst, 0, sizeof(video_el_test_t)); + prepare_pool(&res); + const char *name[] = {"vid_enc", "vid_dec", NULL}; + TEST_ASSERT_EQUAL(0, prepare_convert_pipeline(&res, name)); + + uint32_t convert_pair[][3] = { +#if CONFIG_IDF_TARGET_ESP32P4 + {ESP_FOURCC_OUYY_EVYY, ESP_FOURCC_H264, ESP_FOURCC_YUV420P}, + {ESP_FOURCC_RGB16, ESP_FOURCC_MJPG, ESP_FOURCC_RGB16}, +#else + {ESP_FOURCC_RGB24, ESP_FOURCC_MJPG, ESP_FOURCC_RGB16}, +#if CONFIG_IDF_TARGET_ESP32S3 + {ESP_FOURCC_YUV420P, ESP_FOURCC_H264, ESP_FOURCC_YUV420P}, +#endif /* CON FIG_IDF_TARGET_ESP32S3 */ +#endif /* CONFIG_IDF_TARGET_ESP32P4 */ + }; + for (int i = 0; i < ELEMS(convert_pair); i++) { + // Gen pattern + allocate_src_pattern(convert_pair[i][0], false); + video_el_inst.out_res = video_el_inst.src_res; + video_el_inst.out_codec = convert_pair[i][2]; + + esp_gmf_info_video_t info = { + .format_id = convert_pair[i][0], + .width = TEST_PATTERN_WIDTH, + .height = TEST_PATTERN_HEIGHT, + .fps = 10, + }; + // esp_gmf_video_enc_set_dst_codec(res.enc_hd, convert_pair[i][1]); + esp_gmf_video_param_set_dst_codec(res.enc_hd, convert_pair[i][1]); + esp_gmf_pipeline_report_info(res.pipe, ESP_GMF_INFO_VIDEO, &info, sizeof(info)); + // Set decoder information + // esp_gmf_video_dec_set_dst_format(res.dec_hd, convert_pair[i][2]); + esp_gmf_video_param_set_dst_format(res.dec_hd, convert_pair[i][2]); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_run(res.pipe)); + vTaskDelay(1000 / portTICK_RATE_MS); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_stop(res.pipe)); + show_result_pattern(); + free_video_el_inst(); + esp_gmf_pipeline_reset(res.pipe); + esp_gmf_pipeline_loading_jobs(res.pipe); + } + // Clear up resources + release_convert_pipeline(&res); + +#ifdef MEDIA_LIB_MEM_TEST + media_lib_stop_mem_trace(); +#endif /* MEDIA_LIB_MEM_TEST */ + ESP_GMF_MEM_SHOW(TAG); +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/main/elements/gmf_video_pattern.h b/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/main/elements/gmf_video_pattern.h new file mode 100644 index 0000000..50d3269 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/main/elements/gmf_video_pattern.h @@ -0,0 +1,558 @@ + +/** + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include "esp_fourcc.h" +#include "esp_gmf_video_types.h" +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define GET_RGB565_R(x) (((((x) >> 11) & 0x1F) << 3) | (((x) >> 8) & 0x07)) +#define GET_RGB565_G(x) (((((x) >> 5) & 0x3F) << 2) | (((x) >> 3) & 0x03)) +#define GET_RGB565_B(x) ((((x) & 0x1F) << 3) | (((x) & 0x1C) >> 2)) +#define SWAP_EDIAN(x) (((x) << 8) | ((x) >> 8)) +#define CLAMP(x) ((x) < 0 ? 0 : ((x) > 255 ? 255 : (x))) + +typedef struct { + uint32_t format_id; + esp_gmf_video_resolution_t res; + uint8_t *pixel; + uint32_t data_size; + bool vertical; + uint8_t bar_count; +} pattern_info_t; + +typedef struct { + uint8_t y; + uint8_t u; + uint8_t v; +} yuv_pixel_t; + +typedef struct { + uint8_t r; + uint8_t g; + uint8_t b; +} rgb888_pixel_t; + +static int gen_pattern_color_bar(pattern_info_t *info) +{ + uint8_t *pixel = info->pixel; + bool vertical = info->vertical; + uint8_t n = info->bar_count; + + switch (info->format_id) { + case ESP_FOURCC_BGR24: { + rgb888_pixel_t *color = (rgb888_pixel_t *)malloc(n * sizeof(rgb888_pixel_t)); + if (color == NULL) { + return -1; + } + for (int i = 0; i < n; i++) { + color[i].r = (uint16_t)(rand() & 0xFF); + color[i].g = (uint16_t)(rand() & 0xFF); + color[i].g = (uint16_t)(rand() & 0xFF); + } + if (vertical) { + uint32_t bar_w = info->res.width / n; + uint32_t last_bar_w = info->res.width - bar_w * (n - 1); + for (int y = 0; y < info->res.height; y++) { + for (int i = 0; i < n; i++) { + int points = (i == n - 1 ? last_bar_w : bar_w); + for (int x = 0; x < points; x++) { + *(pixel++) = color[i].b; + *(pixel++) = color[i].g; + *(pixel++) = color[i].r; + } + } + } + } else { + uint32_t bar_h = info->res.height / n; + uint32_t last_bar_h = info->res.height - bar_h * (n - 1); + for (int i = 0; i < n; i++) { + int points = (i == n - 1 ? last_bar_h : bar_h) * info->res.width; + for (int x = 0; x < points; x++) { + *(pixel++) = color[i].b; + *(pixel++) = color[i].g; + *(pixel++) = color[i].r; + } + } + } + free(color); + } break; + case ESP_FOURCC_RGB24: { + rgb888_pixel_t *color = (rgb888_pixel_t *)malloc(n * sizeof(rgb888_pixel_t)); + if (color == NULL) { + return -1; + } + for (int i = 0; i < n; i++) { + color[i].r = (uint8_t)(rand() & 0xFF); + color[i].g = (uint8_t)(rand() & 0xFF); + color[i].g = (uint8_t)(rand() & 0xFF); + } + if (vertical) { + uint32_t bar_w = info->res.width / n; + uint32_t last_bar_w = info->res.width - bar_w * (n - 1); + for (int y = 0; y < info->res.height; y++) { + for (int i = 0; i < n; i++) { + int points = (i == n - 1 ? last_bar_w : bar_w); + for (int x = 0; x < points; x++) { + *(pixel++) = color[i].r; + *(pixel++) = color[i].g; + *(pixel++) = color[i].b; + } + } + } + } else { + uint32_t bar_h = info->res.height / n; + uint32_t last_bar_h = info->res.height - bar_h * (n - 1); + for (int i = 0; i < n; i++) { + int points = (i == n - 1 ? last_bar_h : bar_h) * info->res.width; + for (int x = 0; x < points; x++) { + *(pixel++) = color[i].r; + *(pixel++) = color[i].g; + *(pixel++) = color[i].b; + } + } + } + free(color); + } break; + + case ESP_FOURCC_RGB16: + case ESP_FOURCC_RGB16_BE: { + uint16_t *color = (uint16_t *)malloc(n * sizeof(uint16_t)); + if (color == NULL) { + return -1; + } + for (int i = 0; i < n; i++) { + color[i] = (uint16_t)(rand() & 0xFFFF); + } + uint16_t *pixel16 = (uint16_t *)pixel; + if (vertical) { + uint32_t bar_w = info->res.width / n; + uint32_t last_bar_w = info->res.width - bar_w * (n - 1); + for (int y = 0; y < info->res.height; y++) { + for (int i = 0; i < n; i++) { + int points = (i == n - 1 ? last_bar_w : bar_w); + for (int x = 0; x < points; x++) { + *(pixel16++) = color[i]; + } + } + } + } else { + uint32_t bar_h = info->res.height / n; + uint32_t last_bar_h = info->res.height - bar_h * (n - 1); + for (int i = 0; i < n; i++) { + int points = (i == n - 1 ? last_bar_h : bar_h) * info->res.width; + for (int x = 0; x < points; x++) { + *(pixel16++) = color[i]; + } + } + } + free(color); + } break; + case ESP_FOURCC_YUV420P: { + yuv_pixel_t *color = (yuv_pixel_t *)malloc(n * sizeof(yuv_pixel_t)); + if (color == NULL) { + return -1; + } + for (int i = 0; i < n; i++) { + color[i].y = (uint8_t)(rand() & 0xFF); + color[i].u = (uint8_t)(rand() & 0xFF); + color[i].v = (uint8_t)(rand() & 0xFF); + } + if (vertical) { + uint32_t bar_w = (info->res.width / n) >> 1 << 1; + uint32_t last_bar_w = info->res.width - bar_w * (n - 1); + // Fill Y firstly + for (int y = 0; y < info->res.height; y++) { + for (int i = 0; i < n; i++) { + uint32_t bytes = (i == n - 1 ? last_bar_w : bar_w); + memset(pixel, color[i].y, bytes); + pixel += bytes; + } + } + // Fill U + for (int y = 0; y < info->res.height >> 1; y++) { + for (int i = 0; i < n; i++) { + uint32_t bytes = (i == n - 1 ? last_bar_w : bar_w) >> 1; + memset(pixel, color[i].u, bytes); + pixel += bytes; + } + } + // Fill V + for (int y = 0; y < info->res.height >> 1; y++) { + for (int i = 0; i < n; i++) { + uint32_t bytes = (i == n - 1 ? last_bar_w : bar_w) >> 1; + memset(pixel, color[i].v, bytes); + pixel += bytes; + } + } + } else { + uint32_t bar_h = (info->res.height / n) >> 1 << 1; + uint32_t last_bar_h = info->res.height - bar_h * (n - 1); + // Fill Y firstly + for (int i = 0; i < n; i++) { + uint32_t bytes = (i == n - 1 ? last_bar_h : bar_h) * info->res.width; + memset(pixel, color[i].y, bytes); + pixel += bytes; + } + // Fill U + for (int i = 0; i < n; i++) { + uint32_t bytes = (i == n - 1 ? last_bar_h : bar_h) * info->res.width >> 2; + memset(pixel, color[i].u, bytes); + pixel += bytes; + } + // Fill V + for (int i = 0; i < n; i++) { + uint32_t bytes = (i == n - 1 ? last_bar_h : bar_h) * info->res.width >> 2; + memset(pixel, color[i].v, bytes); + pixel += bytes; + } + } + free(color); + } break; + case ESP_FOURCC_UYVY: { + yuv_pixel_t *color = (yuv_pixel_t *)malloc(n * sizeof(yuv_pixel_t)); + if (color == NULL) { + return -1; + } + for (int i = 0; i < n; i++) { + color[i].y = (uint8_t)(rand() & 0xFF); + color[i].u = (uint8_t)(rand() & 0xFF); + color[i].v = (uint8_t)(rand() & 0xFF); + } + if (vertical) { + uint32_t bar_w = (info->res.width / n) >> 1 << 1; + // Fill Y firstly + for (int y = 0; y < info->res.height; y++) { + int bar_filled = 0; + int i = 0; + for (int x = 0; x < (info->res.width >> 1); x++) { + *pixel++ = color[i].u; + *pixel++ = color[i].y; + *pixel++ = color[i].v; + *pixel++ = color[i].y; + bar_filled += 2; + if (bar_filled >= bar_w) { + bar_filled = 0; + if (i < n - 1) { + i++; + } + } + } + } + } else { + uint32_t bar_h = (info->res.height / n) >> 1 << 1; + uint32_t last_bar_h = info->res.height - bar_h * (n - 1); + // Fill Y firstly + for (int i = 0; i < n; i++) { + uint32_t bytes = (i == n - 1 ? last_bar_h : bar_h) * info->res.width * 3 / 2; + while (bytes > 0) { + *pixel++ = color[i].u; + *pixel++ = color[i].y; + *pixel++ = color[i].v; + *pixel++ = color[i].y; + bytes -= 3; + } + } + } + free(color); + } break; + case ESP_FOURCC_YUYV: { + yuv_pixel_t *color = (yuv_pixel_t *)malloc(n * sizeof(yuv_pixel_t)); + if (color == NULL) { + return -1; + } + for (int i = 0; i < n; i++) { + color[i].y = (uint8_t)(rand() & 0xFF); + color[i].u = (uint8_t)(rand() & 0xFF); + color[i].v = (uint8_t)(rand() & 0xFF); + } + if (vertical) { + uint32_t bar_w = (info->res.width / n) >> 1 << 1; + // Fill Y firstly + for (int y = 0; y < info->res.height; y++) { + int bar_filled = 0; + int i = 0; + for (int x = 0; x < (info->res.width >> 1); x++) { + *pixel++ = color[i].y; + *pixel++ = color[i].u; + *pixel++ = color[i].y; + *pixel++ = color[i].v; + bar_filled += 2; + if (bar_filled >= bar_w) { + bar_filled = 0; + if (i < n - 1) { + i++; + } + } + } + } + } else { + uint32_t bar_h = (info->res.height / n) >> 1 << 1; + uint32_t last_bar_h = info->res.height - bar_h * (n - 1); + // Fill Y firstly + for (int i = 0; i < n; i++) { + uint32_t bytes = (i == n - 1 ? last_bar_h : bar_h) * info->res.width * 3 / 2; + while (bytes > 0) { + *pixel++ = color[i].y; + *pixel++ = color[i].u; + *pixel++ = color[i].y; + *pixel++ = color[i].v; + bytes -= 3; + } + } + } + free(color); + } break; + case ESP_FOURCC_OUYY_EVYY: { + yuv_pixel_t *color = (yuv_pixel_t *)malloc(n * sizeof(yuv_pixel_t)); + if (color == NULL) { + return -1; + } + for (int i = 0; i < n; i++) { + color[i].y = (uint8_t)(rand() & 0xFF); + color[i].u = (uint8_t)(rand() & 0xFF); + color[i].v = (uint8_t)(rand() & 0xFF); + } + if (vertical) { + uint32_t bar_w = (info->res.width / n) >> 1 << 1; + // Fill Y firstly + for (int y = 0; y < (info->res.height >> 1); y++) { + int bar_filled = 0; + int i = 0; + for (int x = 0; x < (info->res.width >> 1); x++) { + *pixel++ = color[i].u; + *pixel++ = color[i].y; + *pixel++ = color[i].y; + bar_filled += 2; + if (bar_filled >= bar_w) { + bar_filled = 0; + if (i < n - 1) { + i++; + } + } + } + bar_filled = 0; + i = 0; + for (int x = 0; x < (info->res.width >> 1); x++) { + *pixel++ = color[i].v; + *pixel++ = color[i].y; + *pixel++ = color[i].y; + bar_filled += 2; + if (bar_filled >= bar_w) { + bar_filled = 0; + if (i < n - 1) { + i++; + } + } + } + } + } else { + uint32_t bar_h = (info->res.height / n) >> 1 << 1; + uint32_t last_bar_h = info->res.height - bar_h * (n - 1); + // Fill Y firstly + for (int i = 0; i < n; i++) { + uint32_t height = (i == n - 1 ? last_bar_h : bar_h); + uint32_t width = info->res.width >> 1; + for (int y = 0; y < (height >> 1); y++) { + for (int x = 0; x < width; x++) { + *pixel++ = color[i].u; + *pixel++ = color[i].y; + *pixel++ = color[i].y; + } + for (int x = 0; x < width; x++) { + *pixel++ = color[i].v; + *pixel++ = color[i].y; + *pixel++ = color[i].y; + } + } + } + } + free(color); + } + default: + break; + } + return 0; +} + +static void yuv_to_rgb(uint8_t y, uint8_t u, uint8_t v, rgb888_pixel_t *pixel) +{ + int c = y - 16; + int d = u - 128; + int e = v - 128; + int r_temp = (298 * c + 409 * e + 128) >> 8; // R = Y + 1.403 * (V-128) + int g_temp = (298 * c - 100 * d - 208 * e + 128) >> 8; // G = Y - 0.344 * (U-128) - 0.714 * (V-128) + int b_temp = (298 * c + 516 * d + 128) >> 8; // B = Y + 1.770 * (U-128) + pixel->r = CLAMP(r_temp); + pixel->g = CLAMP(g_temp); + pixel->b = CLAMP(b_temp); +} + +static void get_pixel(pattern_info_t *info, rgb888_pixel_t *pixel, int x, int y) +{ + uint8_t *data = info->pixel; + switch (info->format_id) { + case ESP_FOURCC_BGR24: { + rgb888_pixel_t *src = (rgb888_pixel_t *)data; + src += y * info->res.width + x; + pixel->b = src->r; + pixel->g = src->g; + pixel->r = src->b; + break; + } + case ESP_FOURCC_RGB24: { + rgb888_pixel_t *src = (rgb888_pixel_t *)data; + src += y * info->res.width + x; + *pixel = *src; + break; + } + case ESP_FOURCC_RGB16: { + uint16_t *src = (uint16_t *)data; + src += y * info->res.width + x; + pixel->r = GET_RGB565_R(*src); + pixel->g = GET_RGB565_G(*src); + pixel->b = GET_RGB565_B(*src); + break; + } + case ESP_FOURCC_RGB16_BE: { + uint16_t *src = (uint16_t *)data; + src += y * info->res.width + x; + uint16_t v = SWAP_EDIAN(*src); + pixel->r = GET_RGB565_R(v); + pixel->g = GET_RGB565_G(v); + pixel->b = GET_RGB565_B(v); + break; + } + case ESP_FOURCC_UYVY: { + x = (x >> 1 << 1); + uint8_t *yuyv = data + y * info->res.width * 2 + x * 2; + yuv_to_rgb(yuyv[3], yuyv[0], yuyv[2], pixel); + break; + } + case ESP_FOURCC_YUYV: { + x = (x >> 1 << 1); + uint8_t *yuyv = data + y * info->res.width * 2 + x * 2; + yuv_to_rgb(yuyv[0], yuyv[1], yuyv[3], pixel); + break; + } + case ESP_FOURCC_YUV420P: { + uint8_t *py = data + y * info->res.width + x; + y >>= 1; + x >>= 1; + uint8_t *pu = data + info->res.height * info->res.width + y * info->res.width / 2 + x; + uint8_t *pv = data + info->res.height * info->res.width * 5 / 4 + y * info->res.width / 2 + x; + yuv_to_rgb(py[0], pu[0], pv[0], pixel); + break; + } + case ESP_FOURCC_OUYY_EVYY: { + uint8_t *uyy = data + (y >> 1) * info->res.width * 3 + (x >> 1)* 3; + uint8_t *vyy = uyy + info->res.width * 3 / 2; + uint8_t y_pixel = (y & 1) ? vyy[1 + (x & 1)] : uyy[1 + (x & 1)] ; + yuv_to_rgb(y_pixel, uyy[0], vyy[0], pixel); + break; + } + default: + break; + } +} + +static void draw_convert_result(pattern_info_t *a, pattern_info_t *b) +{ + if (a->bar_count == 0) { + return; + } + printf("\n"); + int y = 0; + rgb888_pixel_t block_start = {}; + rgb888_pixel_t block_end = {}; + + if (a->bar_count == b->bar_count) { + int n = a->bar_count; + uint32_t bar_w = (a->res.width / n) >> 1 << 1; + uint32_t last_bar_w = a->res.width - bar_w * (n - 1); + uint32_t bar_h = a->res.height / n; + + uint32_t bar_w_b = (b->res.width / n) >> 1 << 1; + uint32_t last_bar_w_b = b->res.width - bar_w_b * (n - 1); + uint32_t bar_h_b = b->res.height / n; + int y_b = 0; + + for (int col = 0; col < n; col++) { + int x = 0; + for (int row = 0; row < n; row++) { + get_pixel(a, &block_start, x, y); + x += (row == n - 1 ? last_bar_w : bar_w); + get_pixel(a, &block_end, x - 1, y); + printf("\033[48;2;%d;%d;%dm%c\033[0m", block_start.r, block_start.g, block_start.b, ' '); + printf("\033[48;2;%d;%d;%dm%c\033[0m", block_end.r, block_end.g, block_end.b, ' '); + } + if (b->pixel) { + printf(" | "); + x = 0; + for (int row = 0; row < n; row++) { + get_pixel(b, &block_start, x, y_b); + x += (row == n - 1 ? last_bar_w_b : bar_w_b); + get_pixel(b, &block_end, x - 1, y_b); + printf("\033[48;2;%d;%d;%dm%c\033[0m", block_start.r, block_start.g, block_start.b, ' '); + printf("\033[48;2;%d;%d;%dm%c\033[0m", block_end.r, block_end.g, block_end.b, ' '); + } + y_b += bar_h_b; + } + y += bar_h; + printf("\n"); + } + } else { + // Draw image + int n = a->bar_count; + uint32_t bar_w = (a->res.width / n) >> 1 << 1; + uint32_t last_bar_w = a->res.width - bar_w * (n - 1); + uint32_t bar_h = a->res.height / n; + + printf("A:\n"); + for (int col = 0, y = 0; col < n; col++, y += bar_h) { + int x = 0; + printf("║ "); + for (int row = 0; row < n; row++) { + get_pixel(a, &block_start, x, y); + x += (row == n - 1 ? last_bar_w : bar_w); + get_pixel(a, &block_end, x - 1, y); + printf("\033[48;2;%d;%d;%dm%c\033[0m", block_start.r, block_start.g, block_start.b, ' '); + printf("\033[48;2;%d;%d;%dm%c\033[0m", block_end.r, block_end.g, block_end.b, ' '); + } + printf("\n"); + } + if (b->pixel) { + uint32_t bar_w = (b->res.width / n) >> 1 << 1; + uint32_t last_bar_w = b->res.width - bar_w * (n - 1); + uint32_t bar_h = b->res.height / n; + + printf("B:\n"); + for (int col = 0, y = 0; col < n; col++, y += bar_h) { + int x = 0; + printf("║ "); + for (int row = 0; row < n; row++) { + get_pixel(b, &block_start, x, y); + x += (row == n - 1 ? last_bar_w : bar_w); + get_pixel(b, &block_end, x - 1, y); + printf("\033[48;2;%d;%d;%dm%c\033[0m", block_start.r, block_start.g, block_start.b, ' '); + printf("\033[48;2;%d;%d;%dm%c\033[0m", block_end.r, block_end.g, block_end.b, ' '); + } + printf("\n"); + } + printf("\n"); + } + } +} + +#ifdef __cplusplus +} +#endif diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/main/hi_lexin.pcm b/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/main/hi_lexin.pcm new file mode 100644 index 0000000..ace8ce3 Binary files /dev/null and b/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/main/hi_lexin.pcm differ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/main/idf_component.yml b/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/main/idf_component.yml new file mode 100644 index 0000000..d5e8e92 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/main/idf_component.yml @@ -0,0 +1,27 @@ +dependencies: + espressif/gmf_audio: + override_path: ../../gmf_audio + espressif/gmf_io: + override_path: ../../gmf_io + espressif/gmf_misc: + override_path: ../../gmf_misc + espressif/gmf_ai_audio: + override_path: ../../gmf_ai_audio + espressif/gmf_core: + override_path: ../../../gmf_core + gmf_video: + override_path: ../../gmf_video + espressif/esp-dsp: <=1.6.0 + espressif/esp_video_codec: "~0.5.2" + espressif/esp_h264: + version: "1.0.4" + rules: + - if: "target in [esp32s3, esp32p4]" + espressif/esp_wifi_remote: + rules: + - if: "target in [esp32p4]" + espressif/gmf_loader: + override_path: ../../../packages/gmf_loader + espressif/gmf_app_utils: + override_path: ../../../packages/gmf_app_utils + diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/main/test_48000hz_16bit_2ch_5000ms.flac b/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/main/test_48000hz_16bit_2ch_5000ms.flac new file mode 100644 index 0000000..e0ce727 Binary files /dev/null and b/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/main/test_48000hz_16bit_2ch_5000ms.flac differ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/main/test_cxx_build.cpp b/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/main/test_cxx_build.cpp new file mode 100644 index 0000000..6c13a42 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/main/test_cxx_build.cpp @@ -0,0 +1,28 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +/** + * Test API C++ compilation only, not as a example reference + */ +#include "unity.h" +#include "esp_gmf_io_file.h" +#include "esp_gmf_io_http.h" +#include "esp_gmf_io_embed_flash.h" +#include "esp_gmf_io_i2s_pdm.h" + +extern "C" void test_cxx_build(void) +{ + file_io_cfg_t file_config = FILE_IO_CFG_DEFAULT(); + TEST_ASSERT_EQUAL(esp_gmf_io_file_init(&file_config, NULL), ESP_GMF_ERR_INVALID_ARG); + + http_io_cfg_t http_config = HTTP_STREAM_CFG_DEFAULT(); + TEST_ASSERT_EQUAL(esp_gmf_io_http_init(&http_config, NULL), ESP_GMF_ERR_INVALID_ARG); + + embed_flash_io_cfg_t embed_flash_config = EMBED_FLASH_CFG_DEFAULT(); + TEST_ASSERT_EQUAL(esp_gmf_io_embed_flash_init(&embed_flash_config, NULL), ESP_GMF_ERR_INVALID_ARG); + + i2s_pdm_io_cfg_t i2s_pdm_config = ESP_GMF_IO_I2S_PDM_CFG_DEFAULT(); + TEST_ASSERT_EQUAL(esp_gmf_io_i2s_pdm_init(&i2s_pdm_config, NULL), ESP_GMF_ERR_INVALID_ARG); +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/main/test_gmf_core_main.c b/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/main/test_gmf_core_main.c new file mode 100644 index 0000000..bff83cb --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/main/test_gmf_core_main.c @@ -0,0 +1,13 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "unity.h" +#include "esp_gmf_app_unit_test.h" + +void app_main(void) +{ + esp_gmf_app_test_main(); +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/partitions_4m.csv b/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/partitions_4m.csv new file mode 100644 index 0000000..5301937 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/partitions_4m.csv @@ -0,0 +1,4 @@ +# Name, Type, SubType, Offset, Size, Flags +nvs, data, nvs, 0x9000, 0x6000 +factory, app, factory, 0x10000, 3M, +model, data, spiffs, , 528k, diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/partitions_8m.csv b/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/partitions_8m.csv new file mode 100644 index 0000000..f19b70b --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/partitions_8m.csv @@ -0,0 +1,5 @@ +# Espressif ESP32 Partition Table +# Name, Type, SubType, Offset, Size +nvs, data, nvs, 0x9000, 0x6000, +factory, app, factory, 0x010000, 0x400000 +model, data, spiffs, , 0x3F0000, diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/pytest_gmf_elements.py b/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/pytest_gmf_elements.py new file mode 100644 index 0000000..3c87616 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/pytest_gmf_elements.py @@ -0,0 +1,21 @@ +# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD +# +# SPDX-License-Identifier: Apache-2.0 + +import pytest +from pytest_embedded import Dut + +@pytest.mark.esp32 +@pytest.mark.esp32c3 +@pytest.mark.esp32s3 +@pytest.mark.esp32p4 +@pytest.mark.timeout(8000) +@pytest.mark.parametrize( + 'config', + [ + 'default', + ], + indirect=True, +) +def test_gmf_elements(dut: Dut) -> None: + dut.run_all_single_board_cases(timeout=2000) diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/sdkconfig.ci b/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/sdkconfig.ci new file mode 100644 index 0000000..45ef635 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/sdkconfig.ci @@ -0,0 +1,6 @@ +# +# WiFi Configuration +# +CONFIG_EXAMPLE_WIFI_SSID="${CI_WIFI_SSID}" +CONFIG_EXAMPLE_WIFI_PASSWORD="${CI_WIFI_PASSWORD}" +# end of WiFi Configuration diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/sdkconfig.defaults b/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/sdkconfig.defaults new file mode 100644 index 0000000..8e69f2e --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/sdkconfig.defaults @@ -0,0 +1,238 @@ +# General options for additional checks +CONFIG_HEAP_POISONING_COMPREHENSIVE=y +CONFIG_COMPILER_WARN_WRITE_STRINGS=y +CONFIG_BOOTLOADER_LOG_LEVEL_WARN=y +CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK=y +CONFIG_COMPILER_STACK_CHECK_MODE_STRONG=y +CONFIG_COMPILER_STACK_CHECK=y + +# Serial flasher config +CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y +CONFIG_ESPTOOLPY_FLASHSIZE="8MB" + +# disable task watchdog since this app uses an interactive menu +CONFIG_ESP_TASK_WDT_INIT=n + +# some tests verify file name encoding +CONFIG_FATFS_LFN_HEAP=y +CONFIG_FATFS_API_ENCODING_UTF_8=y +CONFIG_FATFS_CODEPAGE_936=y + +# some of the tests verify concurrent operation of FAT partitions in +# an SD card and in Flash, so need to use a custom partition table. +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_4m.csv" + +CONFIG_ESP_GDBSTUB_ENABLED=y + +CONFIG_ESP_WIFI_STATIC_RX_BUFFER_NUM=20 +CONFIG_ESP_WIFI_DYNAMIC_RX_BUFFER_NUM=128 +CONFIG_ESP_WIFI_DYNAMIC_TX_BUFFER_NUM=128 +CONFIG_ESP_WIFI_AMPDU_TX_ENABLED=y +CONFIG_ESP_WIFI_TX_BA_WIN=32 +CONFIG_ESP_WIFI_AMPDU_RX_ENABLED=y +CONFIG_ESP_WIFI_RX_BA_WIN=32 + +# CONFIG_ESP_WIFI_IRAM_OPT is not set +# CONFIG_ESP_WIFI_EXTRA_IRAM_OPT is not set +# CONFIG_ESP_WIFI_RX_IRAM_OPT is not set +# CONFIG_ESP_WIFI_SLP_IRAM_OPT is not set +# CONFIG_LWIP_IRAM_OPTIMIZATION=y + +CONFIG_LWIP_TCP_SND_BUF_DEFAULT=65535 +CONFIG_LWIP_TCP_WND_DEFAULT=65535 +CONFIG_LWIP_TCP_RECVMBOX_SIZE=64 +CONFIG_LWIP_UDP_RECVMBOX_SIZE=64 +CONFIG_LWIP_TCPIP_RECVMBOX_SIZE=64 + +CONFIG_FREERTOS_HZ=1000 +CONFIG_COMPILER_OPTIMIZATION_PERF=y + +CONFIG_ESPTOOLPY_FLASHMODE_QIO=y +CONFIG_ESPTOOLPY_FLASHFREQ_80M=y + +CONFIG_SPIRAM=y +CONFIG_FREERTOS_ENABLE_BACKWARD_COMPATIBILITY=y + +CONFIG_CODEC_I2C_BACKWARD_COMPATIBLE=n + +CONFIG_LOG_DEFAULT_LEVEL_DEBUG=y +CONFIG_LOG_DEFAULT_LEVEL=4 +CONFIG_UNITY_ENABLE_64BIT=y + +# +# GMF Video Configurations +# + +# +# GMF Video Codec +# +CONFIG_GMF_VIDEO_CODEC_INIT_DECODER=y +CONFIG_GMF_VIDEO_DEC_CODEC_TYPE_AUTO=y +CONFIG_GMF_VIDEO_DEC_CODEC_TYPE=0x00000000 +CONFIG_GMF_VIDEO_CODEC_INIT_ENCODER=y +CONFIG_GMF_VIDEO_ENC_CODEC_TYPE_AUTO=y +CONFIG_GMF_VIDEO_ENC_CODEC_TYPE=0x00000000 +# end of GMF Video Codec + +# +# GMF Video Effects +# +CONFIG_GMF_VIDEO_EFFECTS_INIT_FPS_CONVERT=y +CONFIG_GMF_VIDEO_EFFECTS_INIT_OVERLAY=y +CONFIG_GMF_VIDEO_EFFECTS_INIT_COLOR_CONVERT=y + +# +# Video effects color convert Configuration +# +CONFIG_GMF_VIDEO_EFFECTS_CC_INPUT_WIDTH=320 +CONFIG_GMF_VIDEO_EFFECTS_CC_INPUT_HEIGHT=240 +CONFIG_GMF_VIDEO_EFFECTS_CC_INPUT_FMT_GREY=y +CONFIG_GMF_VIDEO_EFFECTS_CC_INPUT_PIXEL_FORMAT=0x59455247 +CONFIG_GMF_VIDEO_EFFECTS_CC_OUTPUT_FMT_RGB565_LE=y +CONFIG_GMF_VIDEO_EFFECTS_CC_OUTPUT_PIXEL_FORMAT=0x4C424752 +CONFIG_GMF_VIDEO_EFFECTS_CC_COLOR_SPACE_BT601=y +CONFIG_GMF_VIDEO_EFFECTS_CC_COLOR_SPACE=0 +# end of Video effects color convert Configuration + +CONFIG_GMF_VIDEO_EFFECTS_INIT_SCALE=y + +# +# Video effects scale Configuration +# +CONFIG_GMF_VIDEO_EFFECTS_SCALE_INPUT_WIDTH=320 +CONFIG_GMF_VIDEO_EFFECTS_SCALE_INPUT_HEIGHT=240 +CONFIG_GMF_VIDEO_EFFECTS_SCALE_INPUT_FMT_RGB565_LE=y +CONFIG_GMF_VIDEO_EFFECTS_SCALE_INPUT_PIXEL_FORMAT=0x4C424752 +CONFIG_GMF_VIDEO_EFFECTS_SCALE_OUTPUT_WIDTH=160 +CONFIG_GMF_VIDEO_EFFECTS_SCALE_OUTPUT_HEIGHT=120 +CONFIG_GMF_VIDEO_EFFECTS_SCALE_FILTER_DOWN_RESAMPLE=y +CONFIG_GMF_VIDEO_EFFECTS_SCALE_FILTER_TYPE=0 +# end of Video effects scale Configuration + +CONFIG_GMF_VIDEO_EFFECTS_INIT_CROP=y + +# +# Video effects crop Configuration +# +CONFIG_GMF_VIDEO_EFFECTS_CROP_INPUT_WIDTH=320 +CONFIG_GMF_VIDEO_EFFECTS_CROP_INPUT_HEIGHT=240 +CONFIG_GMF_VIDEO_EFFECTS_CROP_INPUT_FMT_RGB565_LE=y +CONFIG_GMF_VIDEO_EFFECTS_CROP_INPUT_PIXEL_FORMAT=0x4C424752 +CONFIG_GMF_VIDEO_EFFECTS_CROP_X_POS=0 +CONFIG_GMF_VIDEO_EFFECTS_CROP_Y_POS=0 +CONFIG_GMF_VIDEO_EFFECTS_CROP_OUTPUT_WIDTH=160 +CONFIG_GMF_VIDEO_EFFECTS_CROP_OUTPUT_HEIGHT=120 +# end of Video effects crop Configuration + +CONFIG_GMF_VIDEO_EFFECTS_INIT_ROTATE=y + +# +# Video effects rotate Configuration +# +CONFIG_GMF_VIDEO_EFFECTS_ROTATE_INPUT_WIDTH=320 +CONFIG_GMF_VIDEO_EFFECTS_ROTATE_INPUT_HEIGHT=240 +CONFIG_GMF_VIDEO_EFFECTS_ROTATE_INPUT_FMT_RGB565_LE=y +CONFIG_GMF_VIDEO_EFFECTS_ROTATE_INPUT_PIXEL_FORMAT=0x4C424752 +CONFIG_GMF_VIDEO_EFFECTS_ROTATE_DEGREE=0 +# end of Video effects rotate Configuration +# end of GMF Video Effects +# end of GMF Video Configurations +# end of ESP GMF Loader +# ESP GMF Loader +# + +# +# GMF IO Reader +# +CONFIG_GMF_IO_INIT_CODEC_DEV_RX=y +CONFIG_GMF_IO_INIT_FILE_READER=y +CONFIG_GMF_IO_INIT_HTTP_READER=y + +# +# HTTP Reader Configuration +# +CONFIG_GMF_IO_HTTP_READER_TASK_STACK=3072 +CONFIG_GMF_IO_HTTP_READER_TASK_CORE=0 +CONFIG_GMF_IO_HTTP_READER_TASK_PRIORITY=5 +CONFIG_GMF_IO_HTTP_READER_OUT_BUF_SIZE=8192 +CONFIG_GMF_IO_HTTP_READER_CERT_PEM="" +# end of HTTP Reader Configuration + +CONFIG_GMF_IO_INIT_FLASH_READER=y + +# +# GMF IO Writer +# +CONFIG_GMF_IO_INIT_CODEC_DEV_TX=y +CONFIG_GMF_IO_INIT_FILE_WRITER=y +CONFIG_GMF_IO_INIT_HTTP_WRITER=y + +# +# HTTP Writer Configuration +# +CONFIG_GMF_IO_HTTP_WRITER_TASK_STACK=3072 +CONFIG_GMF_IO_HTTP_WRITER_TASK_CORE=0 +CONFIG_GMF_IO_HTTP_WRITER_TASK_PRIORITY=5 +CONFIG_GMF_IO_HTTP_WRITER_OUT_BUF_SIZE=8192 +CONFIG_GMF_IO_HTTP_WRITER_CERT_PEM="" +# end of HTTP Writer Configuration +# end of GMF IO Writer +# end of GMF IO Configurations + +# +# GMF Audio Codec +# +CONFIG_GMF_AUDIO_CODEC_INIT_DECODER=y +CONFIG_GMF_AUDIO_CODEC_DECODER_TYPE_PCM=y +# end of GMF Audio Codec + +# +# PCM Decoder Configuration +# +CONFIG_GMF_AUDIO_CODEC_DEC_PCM_SAMPLE_RATE=44100 +CONFIG_GMF_AUDIO_CODEC_DEC_PCM_CHANNEL=2 +CONFIG_GMF_AUDIO_CODEC_DEC_PCM_BITS_PER_SAMPLE=16 +# end of PCM Decoder Configuration + +# +# AAC Encoder Configuration +# +CONFIG_GMF_AUDIO_CODEC_INIT_ENCODER=y +CONFIG_GMF_AUDIO_CODEC_ENCODER_TYPE_PCM=y +# end of AAC Encoder Configuration + +# +# GMF Audio Effects +# +CONFIG_GMF_AUDIO_EFFECT_INIT_ALC=y +CONFIG_GMF_AUDIO_EFFECT_INIT_CH_CVT=y +CONFIG_GMF_AUDIO_EFFECT_CH_CVT_DEST_CH=2 +CONFIG_GMF_AUDIO_EFFECT_INIT_BIT_CVT=y +CONFIG_GMF_AUDIO_EFFECT_BIT_CVT_DEST_BITS=16 +CONFIG_GMF_AUDIO_EFFECT_INIT_RATE_CVT=y +CONFIG_GMF_AUDIO_EFFECT_RATE_CVT_DEST_RATE=48000 +CONFIG_GMF_AUDIO_EFFECT_RATE_CVT_COMPLEXITY=1 +CONFIG_GMF_AUDIO_EFFECT_RATE_CVT_PERF_TYPE_MEMORY=y +CONFIG_GMF_AUDIO_EFFECT_RATE_CVT_PERF_TYPE=0 +CONFIG_GMF_AUDIO_EFFECT_INIT_DEINTERLEAVE=y +CONFIG_GMF_AUDIO_EFFECT_INIT_INTERLEAVE=y +CONFIG_GMF_AUDIO_EFFECT_INIT_MIXER=y +CONFIG_GMF_AUDIO_EFFECT_INIT_EQ=y +CONFIG_GMF_AUDIO_EFFECT_INIT_SONIC=y +CONFIG_GMF_AUDIO_EFFECT_INIT_FADE=y +# end of GMF Audio Effects + +# +# GMF AI Audio +# +CONFIG_GMF_AI_AUDIO_INIT_AEC=y +CONFIG_GMF_AI_AUDIO_AEC_FILTER_LEN=4 +CONFIG_GMF_AI_AUDIO_AEC_CH_ALLOCATION="RMNM" +# end of GMF AI Audio + +# +# GMF Miscellaneous +# +CONFIG_GMF_MISC_INIT_COPIER=y +# end of GMF Miscellaneous diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/sdkconfig.defaults.esp32 b/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/sdkconfig.defaults.esp32 new file mode 100644 index 0000000..f178daf --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/sdkconfig.defaults.esp32 @@ -0,0 +1,12 @@ +# +# Partition Table +# +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_8m.csv" +CONFIG_PARTITION_TABLE_FILENAME="partitions_8m.csv" +CONFIG_PARTITION_TABLE_OFFSET=0x8000 +CONFIG_PARTITION_TABLE_MD5=y +# end of Partition Table + +CONFIG_SR_WN_WN9_HILEXIN=y +CONFIG_AUDIO_BOARD="LYRAT_MINI_V1" diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/sdkconfig.defaults.esp32c3 b/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/sdkconfig.defaults.esp32c3 new file mode 100644 index 0000000..0ea7ce8 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/sdkconfig.defaults.esp32c3 @@ -0,0 +1,38 @@ +CONFIG_ESPTOOLPY_FLASHFREQ_80M=y +CONFIG_ESPTOOLPY_FLASHFREQ_80M_DEFAULT=y +CONFIG_ESPTOOLPY_FLASHFREQ="80m" +CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y +CONFIG_ESPTOOLPY_FLASHSIZE="8MB" + +CONFIG_ESPTOOLPY_FLASHMODE_DIO=y +CONFIG_ESPTOOLPY_FLASHMODE="dio" + +# +# Partition Table +# +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_8m.csv" +CONFIG_PARTITION_TABLE_FILENAME="partitions_8m.csv" +CONFIG_PARTITION_TABLE_OFFSET=0x8000 +CONFIG_PARTITION_TABLE_MD5=y +# end of Partition Table + +# +# ESP Speech Recognition +# +CONFIG_AFE_INTERFACE_V1=y +CONFIG_SR_NSN_WEBRTC=y +CONFIG_SR_VADN_WEBRTC=y + +# +# Load Multiple Wake Words +# +CONFIG_SR_WN_WN9S_HILEXIN=y +# CONFIG_SR_WN_WN9S_HIESP is not set +# CONFIG_SR_WN_WN9S_NIHAOXIAOZHI is not set +# CONFIG_SR_WN_WN9S_HIJASON is not set +# end of Load Multiple Wake Words + +CONFIG_SR_MN_CN_NONE=y +CONFIG_SR_MN_EN_NONE=y +# end of ESP Speech Recognition diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/sdkconfig.defaults.esp32c5 b/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/sdkconfig.defaults.esp32c5 new file mode 100644 index 0000000..9a329af --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/sdkconfig.defaults.esp32c5 @@ -0,0 +1,42 @@ +CONFIG_ESPTOOLPY_FLASHFREQ_80M=y +CONFIG_ESPTOOLPY_FLASHFREQ_80M_DEFAULT=y +CONFIG_ESPTOOLPY_FLASHFREQ="80m" +CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y +CONFIG_ESPTOOLPY_FLASHSIZE="8MB" + +CONFIG_ESPTOOLPY_FLASHMODE_DIO=y +CONFIG_ESPTOOLPY_FLASHMODE="dio" + +# +# Partition Table +# +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_8m.csv" +CONFIG_PARTITION_TABLE_FILENAME="partitions_8m.csv" +CONFIG_PARTITION_TABLE_OFFSET=0x8000 +CONFIG_PARTITION_TABLE_MD5=y +# end of Partition Table + +# +# ESP Speech Recognition +# +CONFIG_AFE_INTERFACE_V1=y +CONFIG_SR_NSN_WEBRTC=y +CONFIG_SR_VADN_WEBRTC=y + +# +# Load Multiple Wake Words +# +CONFIG_SR_WN_WN9S_HILEXIN=y +# CONFIG_SR_WN_WN9S_HIESP is not set +# CONFIG_SR_WN_WN9S_NIHAOXIAOZHI is not set +# CONFIG_SR_WN_WN9S_HIJASON is not set +# end of Load Multiple Wake Words + +CONFIG_SR_MN_CN_NONE=y +CONFIG_SR_MN_EN_NONE=y +# end of ESP Speech Recognition + +CONFIG_GMF_AI_AUDIO_INIT_WN=y + +CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG=y diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/sdkconfig.defaults.esp32p4 b/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/sdkconfig.defaults.esp32p4 new file mode 100644 index 0000000..621012d --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/sdkconfig.defaults.esp32p4 @@ -0,0 +1,46 @@ +# Flash setting +CONFIG_FLASHMODE_QIO=y +CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y + +CONFIG_SPIRAM_SPEED_200M=y + +# If you use serial JTAG turn on this option +#CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG=y + +# Use camera SC2336 +CONFIG_CAMERA_SC2336=y +CONFIG_CAMERA_SC2336_MIPI_RAW8_1024x600_30FPS=y + +# Enable ISP pipelines +CONFIG_ESP_VIDEO_ENABLE_ISP_PIPELINE_CONTROLLER=y + +# Set Slave target type +CONFIG_IDF_SLAVE_TARGET="esp32c6" +CONFIG_SLAVE_IDF_TARGET_ESP32C6=y + +# +# Partition Table +# +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_8m.csv" +CONFIG_PARTITION_TABLE_FILENAME="partitions_8m.csv" +CONFIG_PARTITION_TABLE_OFFSET=0x8000 +CONFIG_PARTITION_TABLE_MD5=y +# end of Partition Table + +CONFIG_HEAP_POISONING_COMPREHENSIVE=n +CONFIG_HEAP_POISONING_LIGHT=y + +CONFIG_GMF_VIDEO_EFFECTS_INIT_PPA=y +# +# ESP Speech Recognition +# +CONFIG_MODEL_IN_FLASH=y +CONFIG_AFE_INTERFACE_V1=y +CONFIG_SR_NSN_WEBRTC=y +CONFIG_SR_VADN_WEBRTC=y +CONFIG_SR_WN_WN9_HILEXIN=y +CONFIG_SR_MN_CN_MULTINET7_QUANT=y +CONFIG_SR_MN_EN_NONE=y + +CONFIG_AUDIO_BOARD="ESP32_P4_DEV_V14" diff --git a/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/sdkconfig.defaults.esp32s3 b/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/sdkconfig.defaults.esp32s3 new file mode 100644 index 0000000..a9e48b5 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/elements/test_apps/sdkconfig.defaults.esp32s3 @@ -0,0 +1,46 @@ +CONFIG_IDF_CMAKE=y +CONFIG_IDF_TARGET_ARCH_XTENSA=y +CONFIG_IDF_TARGET="esp32s3" +CONFIG_IDF_TARGET_ESP32S3=y +CONFIG_IDF_FIRMWARE_CHIP_ID=0x0009 + +CONFIG_ESPTOOLPY_FLASHFREQ_80M=y +CONFIG_ESPTOOLPY_FLASHFREQ="80m" +CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y +CONFIG_ESPTOOLPY_FLASHSIZE="8MB" + +# +# Partition Table +# +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_8m.csv" +CONFIG_PARTITION_TABLE_FILENAME="partitions_8m.csv" +CONFIG_PARTITION_TABLE_OFFSET=0x8000 +CONFIG_PARTITION_TABLE_MD5=y +# end of Partition Table + +# +# ESP Speech Recognition +# +CONFIG_MODEL_IN_FLASH=y +CONFIG_AFE_INTERFACE_V1=y +CONFIG_SR_WN_WN9_HILEXIN=y +CONFIG_SR_MN_CN_MULTINET6_QUANT=y +CONFIG_SR_MN_EN_NONE=y + +CONFIG_SPIRAM_SPEED_80M=y +CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP=y +CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY=y +CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=1024 +CONFIG_SPIRAM_MODE_OCT=y +CONFIG_SPIRAM_TYPE_AUTO=y + +CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC=y +CONFIG_ESP_TLS_INSECURE=y +CONFIG_ESP_TLS_SKIP_SERVER_CERT_VERIFY=y + +CONFIG_ESP32S3_DEFAULT_CPU_FREQ_240=y +CONFIG_ESP32S3_INSTRUCTION_CACHE_32KB=y +CONFIG_ESP32S3_DATA_CACHE_LINE_64B=y + +CONFIG_AUDIO_BOARD="S3_Korvo_V2" diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/CHANGELOG.md b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/CHANGELOG.md new file mode 100644 index 0000000..b93eeea --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/CHANGELOG.md @@ -0,0 +1,121 @@ +# Changelog + +## v0.7.7 + +### Bug Fixes + +- Fixed abort exit correct for data_bus +- Unified API `esp_gmf_db_done_write` call order +- Unified write done usage and judgment logic +- Fixed give read semaphore when write done +- Fixed create fifo data_bus use wrong handle +- Added unit test for data_bus control API + +## v0.7.6 + +### Bug Fixes + +- Fixed racing condition when load method and capability +- Fixed dependency and non-dependency element wrong open order +- Refine some warning log output +- Correct logic for API `esp_gmf_pipeline_get_next_el` + +## v0.7.5 + +### Features + +- Support C++ build + +## v0.7.4 + +### Bug Fixes + +- Fixed incorrect bit shift in the macro converting FourCC code to a string + +## v0.7.3 + +### Bug Fixes + +- Fixed possible dead lock for released port after close +- Fixed pipeline previous action state not cleared after stopped +- Fixed incorrect task stack information when using `esp_gmf_oal_sys_get_real_time_stats` + +## v0.7.2 + +### Bug Fixes + +- Fixed an issue where the pipeline overwrote the event callback state when both input and output IO operations failed + +## v0.7.1 + +### Bug Fixes + +- Fixed a bug where the pause and stop operations lagged and caused the run API call to fail + +## v0.7.0 + +### Features +- Added `esp_gmf_pool_iterate_element` to support iteration over elements in the GMF pool +- Added FourCC codes to represent video element caps +- Optimized GMF argument and method name handling to avoid unnecessary copying +- Added `esp_gmf_oal_get_spiram_cache_align` for retrieving SPIRAM cache alignment +- Added helper macros for defining and retrieving GMF method and argument identifiers +- Added helper function for GMF method execution +- Added `esp_gmf_io_reset` API to reset the IO thread and reload jobs +- Added `meta_flag` field to `esp_gmf_payload_t` to support audio decoder recovery status tracking +- Added raw_pcm in `esp_fourcc.h` +- Added `esp_gmf_pool_register_element_at_head` for insertion of elements at the head of the pool +- Enhanced `esp_gmf_oal_thread_delete` to accept NULL handle as a valid input +- Enhanced GMF task to avoid race condition when stop + +### Bug Fixes + +- Fixed pause timeout caused by missing sync event when pause producer entered an error state +- Fixed a thread safety issue with the gmf_task `running` flag +- Fixed parameter type mismatch in memory transfer operations to ensure data integrity +- Corrected return value validation for *acq_write/read and *acq_release_write/read callback function which in esp_gmf_ring_buffer.c +- Fixed issue where element name renaming in the pool was lost when duplicate new one from it +- Fixed `esp_gmf_cache_acquire` still report OK even not cache enough data +- Fixed stop timeout occurred due to an element keep on report CONTINUE +- Fixed method helper to support method without argument +- Fixed used after free when once job return TRUNCATE +- Removed unused configuration buffer for GMF task +- Fixed memory leakage when clear-up when `esp_gmf_task_init` failed + +## v0.6.1 + +### Bug Fixes + +- Fixed an issue where gmf_task failed to wait for event bits +- Fixed compilation failure when building for P4 + + +## v0.6.0 + +### Features +- Added GMF element capabilities +- Added GMF port attribution functionality +- Added gmf_cache APIs for GMF payload caching +- Added truncated loop path handling for gmf_task execution +- Added memory alignment support for GMF fifo bus +- Renamed component to `gmf_core` + +### Bug Fixes + +- Fixed support for NULL configuration in GMF object initialization +- Standardized return values of port and data bus acquire/release APIs to esp_gmf_err_io_t only +- Improved task list output formatting + +## v0.5.1 + +### Bug Fixes + +- Fixed an issue where the block data bus returns a valid size when the done flag is set +- Fixed an issue where the block unit test contained incorrect code + + +## v0.5.0 + +### Features + +- Initial version of `esp-gmf-core` diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/CMakeLists.txt b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/CMakeLists.txt new file mode 100644 index 0000000..8551d4f --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/CMakeLists.txt @@ -0,0 +1,6 @@ +set(COMPONENT_ADD_INCLUDEDIRS "oal/include" "include" "data_bus/include" "helpers/include") +set(COMPONENT_SRCDIRS src oal data_bus helpers) + +set(COMPONENT_REQUIRES esp_mm) + +register_component() \ No newline at end of file diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/LICENSE b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/LICENSE new file mode 100644 index 0000000..95aa434 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/LICENSE @@ -0,0 +1,22 @@ +Espressif Modified MIT License + +Copyright (c) 2025 Espressif Systems (Shanghai) CO., LTD + +Permission is hereby granted for use EXCLUSIVELY with Espressif Systems products. +This includes the right to use, copy, modify, merge, publish, distribute, and sublicense +the Software, subject to the following conditions: + +1. This Software MUST BE USED IN CONJUNCTION WITH ESPRESSIF SYSTEMS PRODUCTS. +2. The above copyright notice and this permission notice shall be included in all copies + or substantial portions of the Software. +3. Redistribution of the Software in source or binary form FOR USE WITH NON-ESPRESSIF PRODUCTS + is strictly prohibited. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/README.md b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/README.md new file mode 100644 index 0000000..a7c9385 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/README.md @@ -0,0 +1,151 @@ +# Overview of the GMF-Core Framework +[中文版](./README_CN.md) + +The following diagram illustrates the class relationships within the GMF-Core framework. +```mermaid +classDiagram + GMF-Obj <|-- GMF-IO + GMF-Obj <|-- GMF-Task + GMF-Obj <|-- GMF-Element + GMF-Obj: +new_obj() + GMF-Obj: +del_obj() + GMF-Task --* GMF-Pipeline + GMF-Port --* GMF-Element + GMF-Element --* GMF-Pipeline + GMF-Element --o GMF-Pool + GMF-IO --* GMF-Pipeline + GMF-IO --o GMF-Pool +``` + +`GMF-Obj` is the core class, providing a unified interface for object creation and destruction to manage derived class objects easily. `GMF-IO`, `GMF-Task`, and `GMF-Element` all inherit from it. + +GMF-Core primarily comprises `GMF-Element`, `GMF-Pipeline`, and `GMF-Task`. It operates around a unit called a "job," which serves as the smallest processing unit. Each job is loaded and executed in real time. +- **GMF-Element** is the job provider and offers three fundamental jobs: open, process, and close. Typically, open is the first job to load, process follows after open completes, and when all jobs are finished or if an error occurs, the close job loads. +- **GMF-Pipeline** manages and loads the jobs of connected elements, controls the start and stop of `GMF-Task`, and opens and closes the IN and OUT IO interfaces if any. +- **GMF-Task** handles the workflow of jobs as shown below. The task operates in three phases: **opening** (calls each element's open and process jobs once), **running** (repeatedly calls each element's process job based on connection order), and **cleanup** (when jobs complete or an error occurs, it calls the close job for resource release). + +```mermaid +graph LR + Opening -.->open1[open1] --> proc1[proc1] --> open2[open2] + open2--> proc2[proc2]-->open3[open3] --> proc3[proc3] + + Running -.-> proc4[proc1] --> proc5[proc2] --> proc6[proc3] + proc6-->proc4 + + Cleanup -.->|Finished, Stopped, Error| close1 --> close2 --> close3 + + style Opening fill:none,stroke:#333,stroke-width:1 + style Running fill:none,stroke:#333,stroke-width:1 + style Cleanup fill:none,stroke:#333,stroke-width:1 +``` + +## GMF-Pool +`GMF-Pool` is the management unit for `GMF-Element` and `GMF-IO`. It enables the creation of pipelines based on a list of strings, creating object replicas first, then connecting them to form a pipeline. If a new pipeline requires a specific element or IO configuration changes, these adjustments can be made after pipeline creation using `esp_gmf_pipeline_get_el_by_name` and `OBJ_GET_CFG`. + +## GMF-Element +`GMF-Element` is a base class designed for implementing specific functional elements. Its derived categories include audio elements, picture elements, and others. + +- An **audio element** class may include an encoder, a decoder, and audio processing algorithms like EQ, mixer, and resample. +- A **picture element** class may include a JPEG encoder/decoder, a PNG decoder, etc. + +The input and output ports of GMF-Element are represented by GMF-Port. GMF-Port manages the payload buffer based on the connection status of the elements and the requested data size, facilitating the transfer of payload data between elements. GMF-Element is responsible for managing GMF-Port's connection capabilities and the number of connections. + +## GMF-DataBus +`GMF-DataBus` is GMF-Core's data access module, which employs an Acquire-Release method for data access. `GMF-DataBus` supports both zero-copy and copy-based data transfer, as well as blocking and non-blocking access modes. Currently, GMF-Core supports four buffer types: `Ringbuffer`, `PBuffer`, `FIFO`, and `BlockBuffer`. `PBuffer`, `FIFO`, and `BlockBuffer` use zero-copy transfer, while `Ringbuffer`, `FIFO`, and `BlockBuffer` provide blocking interfaces. + +## GMF-Task +`GMF-Task` is the thread that executes jobs, taking jobs from a list and running them in serial. Once all jobs in the list have been processed, it enters an idle state until new jobs are added. Jobs are the smallest unit of work and are categorized into single-use jobs or continuous jobs. + +## GMF-Pipeline +A `GMF-Pipeline` is composed of elements, tasks, and IO. Each pipeline requires at least one task to manage and schedule jobs. `GMF-Pipeline` supports sequentially cascading pipelines, allowing one pipeline to connect to another and cascade events. + +The pipeline also enables ordered startup for elements with event dependencies. For example, if a pipeline consists of a decoder and a resampler, and the resampler requires music data parsed by the decoder, `GMF-Pipeline` will first start only the decoder. Once the decoder has parsed the data, the pipeline will then initiate the resampler. This flow simplifies operations and avoids unnecessary noise. + +`GMF-Pipeline` is an independently operable module within GMF and provides users with control interfaces for pipeline operations, such as run, stop, pause, resume, and reset. `GMF-Pipeline` manages the elements and tasks connected to it, as well as the opening and closing of IN and OUT ports. During operation, the pipeline registers and manages jobs for connected elements. A `GMF-Pipeline` typically consists of one GMF-Task, at least one element, a maximum of one IN port, and a maximum of one OUT port. The IN port connects to the input of the first element, and the OUT port connects to the output of the last element. A pipeline can contain one or multiple elements, with no limit on the number of elements, constrained only by physical resource availability. + +Below are four basic pipeline application examples. + +**Application 1:** A single pipeline decodes audio from a file and plays it through I2S. +```mermaid +graph LR + File((File)) --> Decoder1[decoder] --> Resample1[Resample] --> I2S((I2S)) +``` + +**Application 2:** Input and output ports of the pipeline are replaced by callback functions to fetch user-supplied data. +```mermaid +graph LR + CB1((CB)) --> Decoder2[decoder] --> Resample2[Resample] --> CB2((CB)) +``` + +**Application 3:** A single playback pipeline is split into two, connected via `Ringbuffer`. +```mermaid +graph LR + subgraph P1 + File3((File)) --> MP3_1[MP3] + end + MP3_1 --> Resample3[Resample] + subgraph P2 + Resample3 --> OUT3((OUT)) + end +``` + +**Application 4:** Two pipelines are merged into a mixer pipeline for multi-channel mixing. +```mermaid +graph LR + subgraph P1 + File((File)) --> MP3_1[MP3] + end + subgraph P2 + HTTP((HTTP)) --> MP3_2[MP3] + end + MP3_1 --> Mixer[Mixer Pipeline] + MP3_2 --> Mixer[Mixer Pipeline] +``` + +Complex pipelines are valuable in data distribution scenarios, such as providing audio data to acoustic echo cancellation (AEC) for reference while playing music. The following examples employ a `copier` element with multi-output capabilities, enabling connections to multiple pipelines or outputs. + +**Application 1:** Use two pipelines to achieve multi-output from a single input source. The decoded data is forwarded by a copier to an additional pipeline. + +```mermaid +graph LR + subgraph P1 + File1((File)) --> Decoder1[Decoder] --> Copier1{Copier} --> Resample1[Resample] --> OUT1((OUT)) + end + Copier1 --> P2Resample + subgraph P2 + P2Resample[Resample] --> P2File((File)) + end +``` + +**Application 2:** Use two pipelines to achieve multi-output from a single input source. One output is directed through a pipeline, and the other through the port. +```mermaid +graph LR + subgraph P1 + File((File)) --> Decoder[Decoder] --> Copier{Copier} + end + Copier --> P2Resample[Resample] + Copier --> File2((File)) + subgraph P2 + P2Resample[Resample] --> I2S((I2S)) + end +``` + +**Application 3:** Use three pipelines to achieve multi-output from a single input source. The decoded data is distributed to each subsequent pipeline via `ringbuffer`. +```mermaid +graph LR + subgraph P1 + P1File((File)) --> P1Decoder[Decoder] --> P1Copier{Copier} + end + P1Copier --> P2Resample[Resample] + P1Copier --> P3Resample[Resample] + subgraph P2 + P2Resample[Resample] --> P2I2S((I2S)) + end + subgraph P3 + P3Resample[Resample] --> P3File((File)) + end +``` + +## Usage Instructions + +For a simple example of the GMF-Core API, please refer to [test_apps](./test_apps/main/cases/gmf_pool_test.c). For additional practical application examples, check the examples provided in the GMF-Elements. diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/README_CN.md b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/README_CN.md new file mode 100644 index 0000000..3dffa0e --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/README_CN.md @@ -0,0 +1,151 @@ +# GMF-Core 框架介绍 +[English](./README.md) + +下图展示了 GMF-Core 框架的类图关系。 +```mermaid +classDiagram + GMF-Obj <|-- GMF-IO + GMF-Obj <|-- GMF-Task + GMF-Obj <|-- GMF-Element + GMF-Obj: +new_obj() + GMF-Obj: +del_obj() + GMF-Task --* GMF-Pipeline + GMF-Port --* GMF-Element + GMF-Element --* GMF-Pipeline + GMF-Element --o GMF-Pool + GMF-IO --* GMF-Pipeline + GMF-IO --o GMF-Pool +``` + +GMF-Obj 是基础类,它为其他组件创建和销毁提供统一接口,便于对继承类对象进行管理操作:创建、复制、销毁。GMF-IO、GMF-Task 和 GMF-Element 都继承于它。 + +GMF-Core 主要由 GMF-Element、GMF-Pipeline、GMF-Task 组成,它的工作原理是以一个称为 Job 的工作片作为最小的处理单元,并实时加载这些工作片执行。 +- GMF-Element 是工作片的提供者,为每个 element 提供三个基础的工作片,即 open,process 和 close。其中 open 一般是最先加载的工作片,process 会在 open 处理完后被加载,当所有 element 的工作片都处理完或者发生错误的时,close 工作片会被加载。 +- GMF-Pipeline 负责管理和加载被连接的 element 的工作片,同时负责对 GMF-Task 的开始和停止进行控制。当 GMF-Pipeline 有 IN 和 OUT 的 IO 接口时,还负责对其进行打开和关闭操作。 +- GMF-Task 处理工作片的流程参见下图。整个过程分为三个阶段,第一个阶段是 opening,把每个 element 的 open 和 process 工作片都调用一次,然后进入 running 阶段,这个过程是按 element 连接顺序循环执行每个 element 的 process 工作片,当工作片处理完或者出错则进入最后一个阶段 cleanup,它会调用每个 element 的 close 工作片来回收 element 资源。 + +```mermaid +graph LR + Opening -.->open1[open1] --> proc1[proc1] --> open2[open2] + open2--> proc2[proc2]-->open3[open3] --> proc3[proc3] + + Running -.-> proc4[proc1] --> proc5[proc2] --> proc6[proc3] + proc6-->proc4 + + Cleanup -.->|Finished, Stopped, Error| close1 --> close2 --> close3 + + style Opening fill:none,stroke:#333,stroke-width:1 + style Running fill:none,stroke:#333,stroke-width:1 + style Cleanup fill:none,stroke:#333,stroke-width:1 +``` + +## GMF-Pool +GMF-Pool 是 GMF-Element 和 GMF-IO 的管理单元。GMF-Pool 根据字符串列表创建 pipeline,先创建存储对象的复制品,然后连接成 pipeline。如果新 pipeline 需要调整 element 或 IO 初始配置参数,可以在 pipeline 创建成功后,通过 `esp_gmf_pipeline_get_el_by_name` 和 `OBJ_GET_CFG` 来修改。 + +## GMF-Element +GMF-Element 是一个基础类,用于实现具体功能的 element。它派生的类别有 audio element、picture element 等。 + +- audio element 实例化的类包含 encoder、decoder,以及 EQ、mixer、resample 等音频算法。 +- picture element 实例化的类包含 JPEG encoder/decoder、PNG decoder 等。 + +GMF-Element 的输入输出端口是 GMF-Port, +它根据 element 的连接情况和请求的数据大小管理 payload 缓冲区,并让 payload 数据在 element 之间传递。GMF-Element 负责管理 GMF-Port 的连接能力和连接个数。 + +## GMF-DataBus +GMF-DataBus 是 GMF-Core 存取数据的模块,它使用 Acquire 再 Release 的方式访问数据。 GMF-DataBus 数据传输支持零拷贝和拷贝两种传输方式,也支持阻塞和非阻塞的访问方式。 +目前 GMF-Core 支持了三种类型,分别是 Ringbuffer、PBuffer、FIFO 和 BlockBuffer,其中 PBuffer、FIFO 和 BlockBuffer 数据传输是零拷贝,Ringbuffer、FIFO 和 BlockBuffer 提供阻塞接口。 + +## GMF-Task +GMF-Task 是执行 job 的线程,它从工作列表中取出 job 顺序运行。当工作列表的 job 执行完即进入空闲状态,直到有新的 job 加入。job 是执行工作的最小单位,job 分为一次性和无限次的两种。 + +## GMF-Pipeline +GMF-Pipeline 由 element、task、IO 组成,一个 pipeline 必须有一个 task 来处理和调度任务。GMF-Pipeline 支持 pipeline 之间的顺序级联,即一个 pipeline 可以连接另外一个 pipeline,pipeline 的 event 也可以级联到后级的 pipeline 上。 +Pipeline 还支持一些有事件依赖的 element 的顺序启动,比如一个 pipeline 由 decoder 和 resample 两个 element 组成,其中 resample 需要 decoder 解析出来的音乐信息才能开始工作,那么 GMF-Pipeline 会先只启动 decoder,直到解析到音乐信息后,pipeline 才启动 resample。这样的流程即简化了操作也不会带来噪音。 + +GMF-Pipeline 是 GMF 中可以独立运行的模块,为用户提供 pipeline 的控制接口,如 Pipeline 的 run\stop\pause\resume\reset 等。GMF-Pipeline 负责处理连接的 element 和 task 事件,还负责打开和关闭 IN 和 OUT port。在运行过程中 pipeline 还负责注册连接的 element job。GMF-Pipeline 一般由一个 GMF-Task、至少一个 element、最多一个 IN port 和最多一个 OUT port 组成。IN port 连接的是第一级 element 的输入,OUT port 连接的是最后一级 element 的输出。一个 pipeline 可以包含一个 element 或多个 element,支持的 element 数量没有上限,只受物理资源限制。 + +GMF-Pipeline 的灵活性很高,本文介绍 4 个基本的 pipeline 应用。 + +应用 1:一路 pipeline 从文件解码音频到 I2S 播放 +```mermaid +graph LR + File((File)) --> Decoder1[decoder] --> Resample1[Resample] --> I2S((I2S)) +``` + +应用 2:pipeline 输入输出不连接 port,而是使用回调函数,让用户去填充数据 +```mermaid +graph LR + CB1((CB)) --> Decoder2[decoder] --> Resample2[Resample] --> CB2((CB)) +``` + +应用 3:一路播放文件的 pipeline 拆成两个,通过 Ringbuffer 连接 +```mermaid +graph LR + subgraph P1 + File3((File)) --> MP3_1[MP3] + end + MP3_1 --> Resample3[Resample] + subgraph P2 + Resample3 --> OUT3((OUT)) + end +``` +应用 4:两个 pipeline 接入一个 mixer pipeline,应用于多路进一路出的混音场景 +```mermaid +graph LR + subgraph P1 + File((File)) --> MP3_1[MP3] + end + subgraph P2 + HTTP((HTTP)) --> MP3_2[MP3] + end + MP3_1 --> Mixer[Mixer Pipeline] + MP3_2 --> Mixer[Mixer Pipeline] +``` + +下面介绍的 pipeline 复杂度较高,应用在一些分流数据的复杂场景中,比如音乐播放的同时需要把音频数据给到 AEC 做参考。下图中都用了一个 copier 的 element,它具有一进多出的功能,输出可连接多路 pipeline 和一些其他输出形式。 + +应用 1:两路 pipeline 实现从单路输入多路输出的场景,解码的数据通过 copier 输出到另外一路 pipeline + +```mermaid +graph LR + subgraph P1 + File1((File)) --> Decoder1[Decoder] --> Copier1{Copier} --> Resample1[Resample] --> OUT1((OUT)) + end + Copier1 --> P2Resample + subgraph P2 + P2Resample[Resample] --> P2File((File)) + end +``` + +应用 2:两路 pipeline 实现从单路输入多路输出的场景,一路通过 pipeline 连接输出,一路通过 port 输出 +```mermaid +graph LR + subgraph P1 + File((File)) --> Decoder[Decoder] --> Copier{Copier} + end + Copier --> P2Resample[Resample] + Copier --> File2((File)) + subgraph P2 + P2Resample[Resample] --> I2S((I2S)) + end +``` + +应用 3:三路 pipeline 实现从单路输入多路输出的场景,解码的数据可通过 ringbuffer 连接每路 pipeline +```mermaid +graph LR + subgraph P1 + P1File((File)) --> P1Decoder[Decoder] --> P1Copier{Copier} + end + P1Copier --> P2Resample[Resample] + P1Copier --> P3Resample[Resample] + subgraph P2 + P2Resample[Resample] --> P2I2S((I2S)) + end + subgraph P3 + P3Resample[Resample] --> P3File((File)) + end +``` + +## 使用说明 + +GMF-Core API 的简单示例代码请参考 [test_apps](./test_apps/main/cases/gmf_pool_test.c),更多实际应用示例请参考 GMF-Elements 的 examples。 diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/data_bus/esp_gmf_block.c b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/data_bus/esp_gmf_block.c new file mode 100644 index 0000000..aae0a74 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/data_bus/esp_gmf_block.c @@ -0,0 +1,398 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "esp_gmf_oal_mutex.h" +#include "esp_gmf_block.h" +#include "esp_log.h" +#include "esp_gmf_oal_mem.h" + +static const char *TAG = "ESP_GMF_BLOCK"; + +/** + * @brief Structure representing a block buffer + */ +typedef struct { + SemaphoreHandle_t can_read; /*!< Semaphore for blocking read operations */ + SemaphoreHandle_t can_write; /*!< Semaphore for blocking write operations */ + SemaphoreHandle_t lock; /*!< Semaphore for thread-safe access */ + uint32_t b_cnt; /*!< Number of blocks */ + uint32_t b_size; /*!< Size of each block */ + uint32_t total_size; /*!< Total size of the buffer */ + uint8_t *buf; /*!< Pointer to the start of the buffer */ + uint8_t *buf_end; /*!< Pointer to the end of the buffer */ + uint8_t *p_rd; /*!< Pointer to the read position */ + uint8_t *p_wr; /*!< Pointer to the write position */ + uint8_t *p_wr_end; /*!< Pointer to the write position */ + uint32_t fill_size; /*!< Number of bytes filled in the buffer */ + uint8_t _is_write_done : 1; /*!< Flag indicating if writing is done */ + uint8_t _is_abort : 1; /*!< Flag indicating if an abort signal has been received */ +} esp_gmf_block_t; + +static inline void _block_handle_free(esp_gmf_block_handle_t handle) +{ + esp_gmf_block_t *hd = (esp_gmf_block_t *)handle; + if (hd->buf) { + esp_gmf_oal_free(hd->buf); + } + if (hd->can_read) { + vSemaphoreDelete(hd->can_read); + } + if (hd->can_write) { + vSemaphoreDelete(hd->can_write); + } + if (hd->lock) { + esp_gmf_oal_mutex_destroy(hd->lock); + } + esp_gmf_oal_free(hd); +} + +static inline uint32_t get_fill_size(esp_gmf_block_t *hd) +{ + uint32_t fill_len = 0; + if (hd->p_wr > hd->p_rd) { + fill_len = hd->p_wr - hd->p_rd; + } else if (hd->p_wr < hd->p_rd) { + if (hd->p_wr_end == hd->p_rd) { + fill_len = hd->p_wr - hd->buf; + } else { + fill_len = hd->p_wr_end - hd->p_rd; + } + } else { + // p_wr == p_rd + if (hd->p_wr_end == hd->buf) { + fill_len = hd->p_wr - hd->p_rd; + } else { + fill_len = hd->p_wr_end - hd->p_wr; + } + } + ESP_LOGV(TAG, "F:%p,%p,%p,%ld", hd->p_rd, hd->p_wr, hd->p_wr_end, fill_len); + return fill_len; +} + +static inline uint32_t get_empty_size(esp_gmf_block_t *hd) +{ + uint32_t emtpy_len = 0; + if (hd->p_wr > hd->p_rd) { + emtpy_len = hd->buf_end - hd->p_wr; + } else if (hd->p_wr < hd->p_rd) { + if (hd->p_wr_end == hd->p_rd) { + emtpy_len = hd->buf_end - hd->p_wr; + } else { + emtpy_len = hd->p_rd - hd->p_wr; + } + } else { + // p_wr == p_rd + if (hd->p_wr_end == hd->buf) { + emtpy_len = hd->buf_end - hd->p_wr; + } else { + emtpy_len = hd->p_rd - hd->p_wr; + } + } + ESP_LOGV(TAG, "E:%p,%p,%p,%ld", hd->p_rd, hd->p_wr, hd->p_wr_end, emtpy_len); + return emtpy_len; +} + +esp_gmf_err_t esp_gmf_block_create(int block_size, int block_cnt, esp_gmf_block_handle_t *handle) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + if ((block_cnt < 1) + || (block_size < 1)) { + ESP_LOGE(TAG, "Invalid parameters,cnt:%d, size:%d", block_cnt, block_size); + return ESP_GMF_ERR_INVALID_ARG; + } + *handle = NULL; + esp_gmf_block_t *blk = esp_gmf_oal_calloc(1, sizeof(esp_gmf_block_t)); + int ret = ESP_GMF_ERR_OK; + ESP_GMF_MEM_CHECK(TAG, blk, return ESP_GMF_ERR_MEMORY_LACK;); + blk->buf = (uint8_t *)esp_gmf_oal_calloc(block_size, block_cnt); + ESP_GMF_MEM_CHECK(TAG, blk->buf, {ret = ESP_GMF_ERR_MEMORY_LACK; goto esp_gmf_blk_err;}); + blk->total_size = block_size * block_cnt; + blk->b_cnt = block_cnt; + blk->b_size = block_size; + blk->buf_end = blk->buf + blk->total_size; + blk->p_rd = blk->buf; + blk->p_wr = blk->buf; + blk->p_wr_end = blk->buf; + + blk->can_read = xSemaphoreCreateBinary(); + ESP_GMF_MEM_CHECK(TAG, blk->can_read, {ret = ESP_GMF_ERR_MEMORY_LACK; goto esp_gmf_blk_err;}); + blk->can_write = xSemaphoreCreateBinary(); + ESP_GMF_MEM_CHECK(TAG, blk->can_write, {ret = ESP_GMF_ERR_MEMORY_LACK; goto esp_gmf_blk_err;}); + blk->lock = (SemaphoreHandle_t)esp_gmf_oal_mutex_create(); + ESP_GMF_MEM_CHECK(TAG, blk->lock, {ret = ESP_GMF_ERR_MEMORY_LACK; goto esp_gmf_blk_err;}); + blk->fill_size = 0; + blk->_is_write_done = 0; + blk->_is_abort = 0; + *handle = blk; + ESP_LOGI(TAG, "The block buf:%p, end:%p", blk->buf, blk->buf_end); + return ESP_GMF_ERR_OK; + +esp_gmf_blk_err: + _block_handle_free(blk); + return ret; +} + +esp_gmf_err_t esp_gmf_block_destroy(esp_gmf_block_handle_t handle) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + _block_handle_free(handle); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_io_t esp_gmf_block_acquire_read(esp_gmf_block_handle_t handle, esp_gmf_data_bus_block_t *blk, uint32_t wanted_size, int block_ticks) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_IO_FAIL); + ESP_GMF_NULL_CHECK(TAG, blk, return ESP_GMF_IO_FAIL); + esp_gmf_block_t *hd = (esp_gmf_block_t *)handle; + if (wanted_size > hd->total_size || wanted_size == 0) { + ESP_LOGE(TAG, "ACQ_R, out of range, total:%ld, wanted:%ld", hd->total_size, wanted_size); + return ESP_GMF_IO_FAIL; + } + ESP_LOGD(TAG, "ACQ_R+, rd:%p, wr:%p, wr_e:%p, f:%ld, done:%d, wanted:%ld", hd->p_rd, hd->p_wr, hd->p_wr_end, hd->fill_size, hd->_is_write_done, wanted_size); + esp_gmf_oal_mutex_lock(hd->lock); + if (hd->fill_size == 0 && hd->_is_write_done) { + esp_gmf_oal_mutex_unlock(hd->lock); + blk->is_last = 1; + blk->valid_size = 0; + ESP_LOGW(TAG, "Done set on read, h:%p, rd:%p, wr:%p, wr_e:%p", hd, hd->p_rd, hd->p_wr, hd->p_wr_end); + return ESP_GMF_IO_OK; + } + blk->is_last = 0; + while (get_fill_size(hd) < wanted_size) { + if (hd->buf < hd->p_wr_end) { + if (hd->p_wr_end != hd->p_rd) { + wanted_size = hd->p_wr_end - hd->p_rd; + ESP_LOGV(TAG, "Read tail data is not enough for wanted:%ld, h:%p, r:%p, w:%p, we:%p", wanted_size, hd, hd->p_rd, hd->p_wr, hd->p_wr_end); + break; + } else { + hd->p_wr_end = hd->buf; + hd->p_rd = hd->buf; + ESP_LOGV(TAG, "Read tail data is not enough for read, wanted:%ld, h:%p, r:%p, w:%p, we:%p", wanted_size, hd, hd->p_rd, hd->p_wr, hd->p_wr_end); + if (get_fill_size(hd) >= wanted_size) { + break; + } + } + } + if (hd->_is_write_done) { + wanted_size = get_fill_size(hd); + blk->is_last = 1; + ESP_LOGI(TAG, "Done on read, wanted:%ld, h:%p, r:%p, w:%p, we:%p", wanted_size, hd, hd->p_rd, hd->p_wr, hd->p_wr_end); + break; + } + esp_gmf_oal_mutex_unlock(hd->lock); + if (hd->_is_abort) { + return ESP_GMF_IO_ABORT; + } + ESP_LOGV(TAG, "R-T:%p, %p, %p, wanted:%ld, fill:%ld", hd->p_rd, hd->p_wr, hd->p_wr_end, wanted_size, get_fill_size(hd)); + if (xSemaphoreTake(hd->can_read, block_ticks) != pdPASS) { + ESP_LOGE(TAG, "Read timeout"); + return ESP_GMF_IO_TIMEOUT; + } + if (hd->_is_abort) { + return ESP_GMF_IO_ABORT; + } + esp_gmf_oal_mutex_lock(hd->lock); + } + if (hd->_is_abort) { + esp_gmf_oal_mutex_unlock(hd->lock); + return ESP_GMF_IO_ABORT; + } + uint32_t act_sz = wanted_size; + if ((hd->p_rd + act_sz) > hd->buf_end) { + act_sz = hd->buf_end - hd->p_rd; + blk->valid_size = act_sz; + blk->buf = hd->p_rd; + blk->buf_length = act_sz; + } else { + blk->valid_size = wanted_size; + blk->buf = hd->p_rd; + blk->buf_length = wanted_size; + } + esp_gmf_oal_mutex_unlock(hd->lock); + return ESP_GMF_IO_OK; +} + +esp_gmf_err_io_t esp_gmf_block_release_read(esp_gmf_block_handle_t handle, esp_gmf_data_bus_block_t *blk, int block_ticks) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_IO_FAIL); + ESP_GMF_NULL_CHECK(TAG, blk, return ESP_GMF_IO_FAIL); + esp_gmf_block_t *hd = (esp_gmf_block_t *)handle; + if ((hd->p_rd + blk->valid_size) > hd->buf_end) { + ESP_LOGE(TAG, "The block pointer is invalid, rd:%p, end:%p", hd->p_rd, hd->buf_end); + return ESP_GMF_IO_FAIL; + } + esp_gmf_oal_mutex_lock(hd->lock); + hd->p_rd += blk->valid_size; + hd->fill_size -= blk->valid_size; + ESP_LOGD(TAG, "ACQ_R-, rd:%p, wr:%p, wr_e:%p, f:%ld, done:%d, vld:%d", hd->p_rd, hd->p_wr, hd->p_wr_end, hd->fill_size, hd->_is_write_done, blk->valid_size); + if ((hd->p_rd == hd->buf_end) || (hd->p_rd == hd->p_wr_end)) { + hd->p_rd = hd->buf; + hd->p_wr_end = hd->buf; + } + esp_gmf_oal_mutex_unlock(hd->lock); + xSemaphoreGive(hd->can_write); + + return ESP_GMF_IO_OK; +} + +esp_gmf_err_io_t esp_gmf_block_acquire_write(esp_gmf_block_handle_t handle, esp_gmf_data_bus_block_t *blk, uint32_t wanted_size, int block_ticks) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_IO_FAIL); + ESP_GMF_NULL_CHECK(TAG, blk, return ESP_GMF_IO_FAIL); + esp_gmf_block_t *hd = (esp_gmf_block_t *)handle; + if ((wanted_size > hd->total_size) || wanted_size == 0) { + ESP_LOGE(TAG, "ACQ_WR, out of range, total:%ld, wanted:%ld", hd->total_size, wanted_size); + return ESP_GMF_IO_FAIL; + } + esp_gmf_oal_mutex_lock(hd->lock); + uint32_t emtpy_size = get_empty_size(hd); + ESP_LOGD(TAG, "ACQ_W+, f:%ld, emt:%ld, rd:%p, wr:%p, wr_e:%p, done:%d, wanted:%ld", hd->fill_size, emtpy_size, hd->p_rd, hd->p_wr, hd->p_wr_end, hd->_is_write_done, wanted_size); + if ((emtpy_size == 0) && hd->_is_write_done) { + ESP_LOGW(TAG, "Done set on write, h:%p, rd:%p, wr:%p, wr_e:%p", hd, hd->p_rd, hd->p_wr, hd->p_wr_end); + blk->is_last = 1; + blk->valid_size = 0; + esp_gmf_oal_mutex_unlock(hd->lock); + return ESP_GMF_IO_OK; + } + blk->is_last = 0; + while ((emtpy_size = get_empty_size(hd)) < wanted_size) { + if (hd->p_wr >= hd->p_rd) { + if (((hd->buf_end - hd->p_wr) < wanted_size) + && ((hd->p_rd - hd->buf) >= wanted_size) + && (hd->buf == hd->p_wr_end)) { + ESP_LOGV(TAG, "Move WR to head, r:%p, w:%p, e:%p, empt:%ld, wanted:%ld", hd->p_rd, hd->p_wr, hd->p_wr_end, emtpy_size, wanted_size); + hd->p_wr_end = hd->p_wr; + hd->p_wr = hd->buf; + break; + } + } + if (hd->_is_write_done) { + wanted_size = hd->fill_size; + blk->is_last = 1; + ESP_LOGI(TAG, "Done on write"); + break; + } + esp_gmf_oal_mutex_unlock(hd->lock); + if (hd->_is_abort) { + return ESP_GMF_IO_ABORT; + } + ESP_LOGV(TAG, "W-T:%p,%p,%p,%ld, empt:%ld\r\n", hd->p_rd, hd->p_wr, hd->p_wr_end, wanted_size, get_empty_size(hd)); + if (xSemaphoreTake(hd->can_write, block_ticks) != pdPASS) { + ESP_LOGE(TAG, "Write timeout"); + return ESP_GMF_IO_TIMEOUT; + } + if (hd->_is_abort) { + return ESP_GMF_IO_ABORT; + } + esp_gmf_oal_mutex_lock(hd->lock); + } + if (hd->_is_abort) { + esp_gmf_oal_mutex_unlock(hd->lock); + return ESP_GMF_IO_ABORT; + } + blk->buf_length = wanted_size; + blk->buf = hd->p_wr; + blk->valid_size = 0; + + esp_gmf_oal_mutex_unlock(hd->lock); + return ESP_GMF_IO_OK; +} + +esp_gmf_err_io_t esp_gmf_block_release_write(esp_gmf_block_handle_t handle, esp_gmf_data_bus_block_t *blk, int block_ticks) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_IO_FAIL); + ESP_GMF_NULL_CHECK(TAG, blk, return ESP_GMF_IO_FAIL); + esp_gmf_block_t *hd = (esp_gmf_block_t *)handle; + if ((hd->p_wr + blk->valid_size) > hd->buf_end) { + ESP_LOGE(TAG, "The buffer is out of range, wr:%p, vld:%d, end:%p", hd->p_wr, blk->valid_size, hd->buf_end); + return ESP_GMF_IO_FAIL; + } + esp_gmf_oal_mutex_lock(hd->lock); + hd->p_wr += blk->valid_size; + hd->fill_size += blk->valid_size; + if (hd->p_wr == hd->buf_end) { + hd->p_wr = hd->buf; + hd->p_wr_end = hd->buf_end; + } + ESP_LOGD(TAG, "ACQ_W-, f:%ld, emt:%ld, rd:%p, wr:%p,wr_e:%p, done:%d, vld:%d", hd->fill_size, get_empty_size(hd), hd->p_rd, hd->p_wr, hd->p_wr_end, hd->_is_write_done, blk->valid_size); + esp_gmf_oal_mutex_unlock(hd->lock); + xSemaphoreGive(hd->can_read); + return ESP_GMF_IO_OK; +} + +esp_gmf_err_t esp_gmf_block_done_write(esp_gmf_block_handle_t handle) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_block_t *hd = (esp_gmf_block_t *)handle; + hd->_is_write_done = 1; + xSemaphoreGive(hd->can_read); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_block_abort(esp_gmf_block_handle_t handle) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_block_t *hd = (esp_gmf_block_t *)handle; + hd->_is_abort = 1; + xSemaphoreGive(hd->can_read); + xSemaphoreGive(hd->can_write); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_block_reset(esp_gmf_block_handle_t handle) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_block_t *hd = (esp_gmf_block_t *)handle; + esp_gmf_oal_mutex_lock(hd->lock); + hd->p_rd = hd->buf; + hd->p_wr = hd->buf; + hd->p_wr_end = hd->buf; + hd->fill_size = 0; + hd->_is_write_done = 0; + hd->_is_abort = 0; + ESP_LOGD(TAG, "esp_gmf_block_reset, %p", hd); + esp_gmf_oal_mutex_unlock(hd->lock); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_block_get_free_size(esp_gmf_block_handle_t handle, uint32_t *free_size) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, free_size, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_block_t *hd = (esp_gmf_block_t *)handle; + esp_gmf_oal_mutex_lock(hd->lock); + *free_size = hd->total_size - hd->fill_size; + esp_gmf_oal_mutex_unlock(hd->lock); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_block_get_filled_size(esp_gmf_block_handle_t handle, uint32_t *filled_size) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, filled_size, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_block_t *hd = (esp_gmf_block_t *)handle; + esp_gmf_oal_mutex_lock(hd->lock); + *filled_size = hd->fill_size; + esp_gmf_oal_mutex_unlock(hd->lock); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_block_get_total_size(esp_gmf_block_handle_t handle, uint32_t *total_size) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, total_size, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_block_t *hd = (esp_gmf_block_t *)handle; + esp_gmf_oal_mutex_lock(hd->lock); + *total_size = hd->total_size; + esp_gmf_oal_mutex_unlock(hd->lock); + return ESP_GMF_ERR_OK; +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/data_bus/esp_gmf_data_bus.c b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/data_bus/esp_gmf_data_bus.c new file mode 100644 index 0000000..98a0c83 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/data_bus/esp_gmf_data_bus.c @@ -0,0 +1,254 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#include +#include +#include "esp_log.h" +#include "esp_gmf_oal_mem.h" +#include "esp_gmf_data_bus.h" + +static const char *TAG = "ESP_GMF_DATA_BUS"; + +esp_gmf_err_t esp_gmf_db_init(esp_gmf_db_config_t *db_config, esp_gmf_db_handle_t *hd) +{ + ESP_GMF_NULL_CHECK(TAG, db_config, return ESP_GMF_ERR_INVALID_ARG;); + ESP_GMF_NULL_CHECK(TAG, hd, return ESP_GMF_ERR_INVALID_ARG;); + esp_gmf_data_bus_t *db = esp_gmf_oal_calloc(1, sizeof(esp_gmf_data_bus_t)); + ESP_GMF_NULL_CHECK(TAG, db, return ESP_GMF_ERR_MEMORY_LACK;); + db->name = esp_gmf_oal_strdup(db_config->name == NULL ? "NULL" : db_config->name); + ESP_GMF_MEM_CHECK(TAG, db->name, goto __init_fail); + db->type = db_config->type; + db->max_item_num = db_config->max_item_num; + db->max_size = db_config->max_size; + db->child = db_config->child; + *hd = db; + return ESP_GMF_ERR_OK; + +__init_fail: + if (db) { + esp_gmf_oal_free(db); + db = NULL; + } + return ESP_GMF_ERR_FAIL; +} + +esp_gmf_err_t esp_gmf_db_deinit(esp_gmf_db_handle_t handle) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_data_bus_t *db = (esp_gmf_data_bus_t *)handle; + if (db->op.deinit) { + db->op.deinit(db->child); + } + if (db->name) { + esp_gmf_oal_free((void *)db->name); + db->name = NULL; + } + if (db) { + esp_gmf_oal_free(db); + db = NULL; + } + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_db_read(esp_gmf_db_handle_t handle, void *buffer, int buf_len, int block_ticks) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_data_bus_t *db = (esp_gmf_data_bus_t *)handle; + esp_gmf_err_t ret = ESP_GMF_ERR_OK; + if (db->op.read) { + ret = db->op.read(db->child, buffer, buf_len, block_ticks); + } + return ret; +} + +esp_gmf_err_t esp_gmf_db_write(esp_gmf_db_handle_t handle, void *buffer, int buf_len, int block_ticks) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_data_bus_t *db = (esp_gmf_data_bus_t *)handle; + esp_gmf_err_t ret = ESP_GMF_ERR_OK; + if (db->op.write) { + ret = db->op.write(db->child, buffer, buf_len, block_ticks); + } + return ret; +} + +esp_gmf_err_io_t esp_gmf_db_acquire_read(esp_gmf_db_handle_t handle, esp_gmf_data_bus_block_t *blk, uint32_t wanted_size, int block_ticks) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_data_bus_t *db = (esp_gmf_data_bus_t *)handle; + esp_gmf_err_io_t ret = ESP_GMF_ERR_OK; + if (db->op.acquire_read) { + ret = db->op.acquire_read(db->child, blk, wanted_size, block_ticks); + } + return ret; +} + +esp_gmf_err_io_t esp_gmf_db_release_read(esp_gmf_db_handle_t handle, esp_gmf_data_bus_block_t *blk, int block_ticks) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_data_bus_t *db = (esp_gmf_data_bus_t *)handle; + esp_gmf_err_io_t ret = ESP_GMF_ERR_OK; + if (db->op.release_read) { + ret = db->op.release_read(db->child, blk, block_ticks); + } + return ret; +} + +esp_gmf_err_io_t esp_gmf_db_acquire_write(esp_gmf_db_handle_t handle, esp_gmf_data_bus_block_t *blk, uint32_t wanted_size, int block_ticks) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_data_bus_t *db = (esp_gmf_data_bus_t *)handle; + esp_gmf_err_io_t ret = ESP_GMF_ERR_OK; + if (db->op.acquire_write) { + ret = db->op.acquire_write(db->child, blk, wanted_size, block_ticks); + } + return ret; +} + +esp_gmf_err_io_t esp_gmf_db_release_write(esp_gmf_db_handle_t handle, esp_gmf_data_bus_block_t *blk, int block_ticks) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_data_bus_t *db = (esp_gmf_data_bus_t *)handle; + esp_gmf_err_io_t ret = ESP_GMF_ERR_OK; + if (db->op.release_write) { + ret = db->op.release_write(db->child, blk, block_ticks); + } + return ret; +} + +esp_gmf_err_t esp_gmf_db_done_write(esp_gmf_db_handle_t handle) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_data_bus_t *db = (esp_gmf_data_bus_t *)handle; + esp_gmf_err_t ret = ESP_GMF_ERR_OK; + if (db->op.done_write) { + ret = db->op.done_write(db->child); + } + return ret; +} + +esp_gmf_err_t esp_gmf_db_reset_done_write(esp_gmf_db_handle_t handle) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_data_bus_t *db = (esp_gmf_data_bus_t *)handle; + esp_gmf_err_t ret = ESP_GMF_ERR_OK; + if (db->op.reset_done_write) { + ret = db->op.reset_done_write(db->child); + } + return ret; +} + +esp_gmf_err_t esp_gmf_db_reset(esp_gmf_db_handle_t handle) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_data_bus_t *db = (esp_gmf_data_bus_t *)handle; + esp_gmf_err_t ret = ESP_GMF_ERR_OK; + if (db->op.reset) { + ret = db->op.reset(db->child); + } + return ret; +} + +esp_gmf_err_t esp_gmf_db_abort(esp_gmf_db_handle_t handle) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_data_bus_t *db = (esp_gmf_data_bus_t *)handle; + esp_gmf_err_t ret = ESP_GMF_ERR_OK; + if (db->op.abort) { + ret = db->op.abort(db->child); + } + return ret; +} + +esp_gmf_err_t esp_gmf_db_get_total_size(esp_gmf_db_handle_t handle, uint32_t *buff_size) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_data_bus_t *db = (esp_gmf_data_bus_t *)handle; + esp_gmf_err_t ret = ESP_GMF_ERR_OK; + if (db->op.get_total_size) { + ret = db->op.get_total_size(db->child, buff_size); + } + return ret; +} + +esp_gmf_err_t esp_gmf_db_get_filled_size(esp_gmf_db_handle_t handle, uint32_t *filled_size) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_data_bus_t *db = (esp_gmf_data_bus_t *)handle; + esp_gmf_err_t ret = ESP_GMF_ERR_OK; + if (db->op.get_filled_size) { + ret = db->op.get_filled_size(db->child, filled_size); + } + return ret; +} + +esp_gmf_err_t esp_gmf_db_get_available(esp_gmf_db_handle_t handle, uint32_t *available_size) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_data_bus_t *db = (esp_gmf_data_bus_t *)handle; + esp_gmf_err_t ret = ESP_GMF_ERR_OK; + if (db->op.get_available) { + ret = db->op.get_available(db->child, available_size); + } + return ret; +} + +esp_gmf_err_t esp_gmf_db_set_writer(esp_gmf_db_handle_t handle, void *holder) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_data_bus_t *db = (esp_gmf_data_bus_t *)handle; + esp_gmf_err_t ret = ESP_GMF_ERR_OK; + db->writer = holder; + return ret; +} + +esp_gmf_err_t esp_gmf_db_get_writer(esp_gmf_db_handle_t handle, void **holder) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_data_bus_t *db = (esp_gmf_data_bus_t *)handle; + esp_gmf_err_t ret = ESP_GMF_ERR_OK; + if (holder) { + *holder = db->writer; + } + return ret; +} + +esp_gmf_err_t esp_gmf_db_set_reader(esp_gmf_db_handle_t handle, void *holder) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_data_bus_t *db = (esp_gmf_data_bus_t *)handle; + esp_gmf_err_t ret = ESP_GMF_ERR_OK; + db->reader = holder; + return ret; +} + +esp_gmf_err_t esp_gmf_db_get_reader(esp_gmf_db_handle_t handle, void **holder) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_data_bus_t *db = (esp_gmf_data_bus_t *)handle; + esp_gmf_err_t ret = ESP_GMF_ERR_OK; + if (holder) { + *holder = db->reader; + } + return ret; +} + +esp_gmf_err_t esp_gmf_db_get_type(esp_gmf_db_handle_t handle, esp_gmf_data_bus_type_t *db_type) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_data_bus_t *db = (esp_gmf_data_bus_t *)handle; + esp_gmf_err_t ret = ESP_GMF_ERR_OK; + *db_type = db->type; + return ret; +} + +const char *esp_gmf_db_get_name(esp_gmf_db_handle_t handle) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return NULL); + esp_gmf_data_bus_t *db = (esp_gmf_data_bus_t *)handle; + return db->name; +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/data_bus/esp_gmf_fifo.c b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/data_bus/esp_gmf_fifo.c new file mode 100644 index 0000000..a11a55a --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/data_bus/esp_gmf_fifo.c @@ -0,0 +1,427 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "esp_log.h" +#include "esp_gmf_oal_mem.h" +#include "esp_gmf_oal_mutex.h" +#include "esp_gmf_err.h" +#include "esp_gmf_fifo.h" + +static const char *TAG = "ESP_GMF_FIFO"; + +#define GMF_FIFO_DEFAULT_ALIGNMENT (16) + +typedef struct esp_esp_gmf_fifo_node { + struct esp_esp_gmf_fifo_node *next; + void *buffer; + size_t buf_length; + size_t valid_size; + bool is_done; +} esp_gmf_fifo_node_t; + +/** + * @brief Structure representing a FIFO (First-In-First-Out) buffer + */ +typedef struct { + uint32_t node_cnt; /*!< Maximum number of buffer nodes in the FIFO */ + uint32_t capacity; /*!< Current number of buffers in the FIFO */ + SemaphoreHandle_t can_read; /*!< Semaphore for indicating available data for reading. It blocks reading operations when no data is available */ + SemaphoreHandle_t can_write; /*!< Semaphore for indicating available space for writing. It blocks writing operations when the FIFO is full */ + SemaphoreHandle_t lock; /*!< Semaphore for controlling exclusive access to the FIFO to ensure thread safety */ + esp_gmf_fifo_node_t *empty_head; /*!< Pointer to the head of the list of empty buffer nodes that can be used for writing */ + esp_gmf_fifo_node_t *fill_head; /*!< Pointer to the head of the list of filled buffer nodes that can be read */ + uint8_t _is_write_done : 1; /*!< Flag indicating if all writing operations to the FIFO have been completed. Set to 1 when writing is finished */ + uint8_t _is_abort : 1; /*!< Flag indicating if an abort operation has been requested. Set to 1 to signal that FIFO operations should be aborted */ + uint8_t align; /*!< Alignment for the request buffer */ +} esp_gmf_fifo_t; + +static inline esp_gmf_fifo_node_t *esp_gmf_fifo_node_create(void) +{ + esp_gmf_fifo_node_t *node = (esp_gmf_fifo_node_t *)esp_gmf_oal_calloc(1, sizeof(esp_gmf_fifo_node_t)); + if (!node) { + return NULL; + } + return node; +} + +static inline esp_gmf_fifo_node_t *esp_gmf_fifo_node_with_buf_create(size_t buf_size, uint8_t align) +{ + esp_gmf_fifo_node_t *node = esp_gmf_fifo_node_create(); + if (!node) { + return NULL; + } + if (buf_size > 0) { + node->buffer = esp_gmf_oal_malloc_align(align, buf_size); + if (!node->buffer) { + esp_gmf_oal_free(node); + return NULL; + } + memset(node->buffer, 0, buf_size); + node->buf_length = buf_size; + } + node->is_done = false; + return node; +} + +static inline int esp_gmf_fifo_node_get_cnt(esp_gmf_fifo_node_t *node) +{ + int cnt = 0; + while (node) { + cnt++; + node = node->next; + } + return cnt; +} + +static inline void esp_gmf_fifo_node_destroy(esp_gmf_fifo_node_t *node) +{ + if (!node) { + return; + } + if (node->buffer) { + esp_gmf_oal_free(node->buffer); + node->buffer = NULL; + } + esp_gmf_oal_free(node); +} + +static inline void _gmf_fifo_handle_free(esp_gmf_fifo_handle_t handle) +{ + esp_gmf_fifo_t *fifo = (esp_gmf_fifo_t *)handle; + while (fifo->empty_head) { + esp_gmf_fifo_node_t *node = fifo->empty_head; + fifo->empty_head = fifo->empty_head->next; + esp_gmf_fifo_node_destroy(node); + } + + while (fifo->fill_head) { + esp_gmf_fifo_node_t *node = fifo->fill_head; + fifo->fill_head = fifo->fill_head->next; + esp_gmf_fifo_node_destroy(node); + } + + if (fifo->can_read) { + vSemaphoreDelete(fifo->can_read); + } + if (fifo->can_write) { + vSemaphoreDelete(fifo->can_write); + } + if (fifo->lock) { + esp_gmf_oal_mutex_destroy(fifo->lock); + } + esp_gmf_oal_free(fifo); +} + +esp_gmf_err_t esp_gmf_fifo_create(int block_cnt, int block_size, esp_gmf_fifo_handle_t *handle) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + if (block_cnt < 1) { + return ESP_GMF_ERR_INVALID_ARG; + } + *handle = NULL; + esp_gmf_fifo_t *fifo = esp_gmf_oal_calloc(1, sizeof(esp_gmf_fifo_t)); + ESP_GMF_MEM_CHECK(TAG, fifo, return ESP_GMF_ERR_MEMORY_LACK;); + fifo->can_read = xSemaphoreCreateBinary(); + ESP_GMF_MEM_CHECK(TAG, fifo->can_read, goto esp_gmf_fifo_err;); + fifo->can_write = xSemaphoreCreateBinary(); + ESP_GMF_MEM_CHECK(TAG, fifo->can_write, goto esp_gmf_fifo_err;); + fifo->lock = (SemaphoreHandle_t)esp_gmf_oal_mutex_create(); + ESP_GMF_MEM_CHECK(TAG, fifo->lock, goto esp_gmf_fifo_err;); + + fifo->capacity = block_cnt; + fifo->node_cnt = 0; + fifo->_is_write_done = 0; + fifo->align = GMF_FIFO_DEFAULT_ALIGNMENT; + *handle = fifo; + return ESP_GMF_ERR_OK; + +esp_gmf_fifo_err: + _gmf_fifo_handle_free(fifo); + return ESP_GMF_ERR_FAIL; +} + +esp_gmf_err_t esp_gmf_fifo_set_align(esp_gmf_fifo_handle_t handle, uint8_t align) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_fifo_t *fifo = (esp_gmf_fifo_t *)handle; + fifo->align = (align == 0) ? GMF_FIFO_DEFAULT_ALIGNMENT : align; + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_fifo_destroy(esp_gmf_fifo_handle_t handle) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + _gmf_fifo_handle_free(handle); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_io_t esp_gmf_fifo_acquire_read(esp_gmf_fifo_handle_t handle, esp_gmf_data_bus_block_t *blk, uint32_t wanted_size, int block_ticks) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_IO_FAIL); + ESP_GMF_NULL_CHECK(TAG, blk, return ESP_GMF_IO_FAIL); + esp_gmf_fifo_t *fifo = (esp_gmf_fifo_t *)handle; + ESP_LOGD(TAG, "RD_ACQ+, hd:%p, wanted:%ld, ticks:%d", handle, wanted_size, block_ticks); + if (fifo->fill_head == NULL) { + while (fifo->fill_head == NULL) { + if (fifo->_is_abort) { + return ESP_GMF_IO_ABORT; + } + if (fifo->_is_write_done) { + blk->is_last = 1; + blk->valid_size = 0; + return ESP_GMF_IO_OK; + } + if (xSemaphoreTake(fifo->can_read, block_ticks) != pdTRUE) { + ESP_LOGE(TAG, "FIFO acquire read timeout"); + return ESP_GMF_IO_TIMEOUT; + } + if (fifo->_is_abort) { + return ESP_GMF_IO_ABORT; + } + } + } + if (fifo->_is_abort) { + return ESP_GMF_IO_ABORT; + } + esp_gmf_fifo_node_t *node = fifo->fill_head; + blk->buf = node->buffer; + blk->buf_length = node->buf_length; + blk->valid_size = node->valid_size; + blk->is_last = node->is_done; + ESP_LOGD(TAG, "RD_ACQ-, hd:%p, b:%p, l:%d, valid:%d, n:%ld, e:%d, f:%d", handle, blk->buf, blk->buf_length, blk->valid_size, fifo->node_cnt, + esp_gmf_fifo_node_get_cnt(fifo->empty_head), esp_gmf_fifo_node_get_cnt(fifo->fill_head)); + return ESP_GMF_IO_OK; +} + +esp_gmf_err_io_t esp_gmf_fifo_release_read(esp_gmf_fifo_handle_t handle, esp_gmf_data_bus_block_t *blk, int block_ticks) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_IO_FAIL); + ESP_GMF_NULL_CHECK(TAG, blk, return ESP_GMF_IO_FAIL); + esp_gmf_fifo_t *fifo = (esp_gmf_fifo_t *)handle; + + ESP_LOGD(TAG, "RD_RLS+, hd:%p, b:%p, l:%d", handle, blk->buf, blk->buf_length); + if (fifo->_is_write_done && fifo->fill_head == NULL) { + ESP_LOGE(TAG, "RLS_RD, write done, fill head is empty, p:%p", fifo); + return ESP_GMF_IO_OK; + } + esp_gmf_oal_mutex_lock(fifo->lock); + esp_gmf_fifo_node_t *node = fifo->fill_head; + fifo->fill_head = node->next; + if (node->buffer != blk->buf) { + ESP_LOGE(TAG, "Release read error, buffer not match"); + esp_gmf_oal_mutex_unlock(fifo->lock); + return ESP_GMF_IO_FAIL; + } + + node->is_done = false; + node->valid_size = 0; + node->next = NULL; + if (fifo->empty_head == NULL) { + fifo->empty_head = node; + } else { + esp_gmf_fifo_node_t *tmp = fifo->empty_head; + while (tmp->next != NULL) { + tmp = tmp->next; + } + tmp->next = node; + } + xSemaphoreGive(fifo->can_write); + esp_gmf_oal_mutex_unlock(fifo->lock); + ESP_LOGD(TAG, "RD_RLS-, hd:%p, b:%p, l:%d, n:%ld, e:%d, f:%d", handle, blk->buf, blk->buf_length, fifo->node_cnt, + esp_gmf_fifo_node_get_cnt(fifo->empty_head), esp_gmf_fifo_node_get_cnt(fifo->fill_head)); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_io_t esp_gmf_fifo_acquire_write(esp_gmf_fifo_handle_t handle, esp_gmf_data_bus_block_t *blk, uint32_t wanted_size, int block_ticks) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_IO_FAIL); + ESP_GMF_NULL_CHECK(TAG, blk, return ESP_GMF_IO_FAIL); + esp_gmf_fifo_t *fifo = (esp_gmf_fifo_t *)handle; + + ESP_LOGD(TAG, "WR_ACQ+, hd:%p, wanted:%ld, ticks:%d", handle, wanted_size, block_ticks); + esp_gmf_fifo_node_t *node = NULL; + esp_gmf_oal_mutex_lock(fifo->lock); + if (fifo->empty_head == NULL) { + if (fifo->node_cnt < fifo->capacity) { + node = esp_gmf_fifo_node_with_buf_create(wanted_size, fifo->align); + ESP_GMF_NULL_CHECK(TAG, node, {esp_gmf_oal_mutex_unlock(fifo->lock); return ESP_GMF_ERR_MEMORY_LACK;}); + fifo->empty_head = node; + fifo->node_cnt++; + ESP_LOGD(TAG, "New an empty node:%p, addr:%p, n:%ld, e:%d, f:%d", node, node->buffer, fifo->node_cnt, + esp_gmf_fifo_node_get_cnt(fifo->empty_head), esp_gmf_fifo_node_get_cnt(fifo->fill_head)); + } else { + esp_gmf_oal_mutex_unlock(fifo->lock); + while (fifo->empty_head == NULL) { + if (fifo->_is_abort) { + ESP_LOGW(TAG, "FIFO acquire write abort"); + return ESP_GMF_IO_ABORT; + } + if (xSemaphoreTake(fifo->can_write, block_ticks) != pdTRUE) { + return ESP_GMF_IO_FAIL; + } + if (fifo->_is_abort) { + return ESP_GMF_IO_ABORT; + } + } + esp_gmf_oal_mutex_lock(fifo->lock); + } + } + if (fifo->_is_abort) { + esp_gmf_oal_mutex_unlock(fifo->lock); + return ESP_GMF_IO_ABORT; + } + node = fifo->empty_head; + if (node->buf_length < wanted_size) { + esp_gmf_oal_free(node->buffer); + node->buffer = esp_gmf_oal_malloc(wanted_size); + ESP_GMF_NULL_CHECK(TAG, node->buffer, {fifo->empty_head = node->next; esp_gmf_fifo_node_destroy(node); + fifo->node_cnt--; esp_gmf_oal_mutex_unlock(fifo->lock); return ESP_GMF_ERR_MEMORY_LACK;}); + node->buf_length = wanted_size; + } + + blk->buf = node->buffer; + blk->buf_length = node->buf_length; + blk->is_last = node->is_done; + esp_gmf_oal_mutex_unlock(fifo->lock); + ESP_LOGD(TAG, "WR_ACQ-, hd:%p, b:%p, l:%d, n:%ld, e:%d, f:%d", handle, blk->buf, blk->buf_length, fifo->node_cnt, + esp_gmf_fifo_node_get_cnt(fifo->empty_head), esp_gmf_fifo_node_get_cnt(fifo->fill_head)); + return ESP_GMF_IO_OK; +} + +esp_gmf_err_io_t esp_gmf_fifo_release_write(esp_gmf_fifo_handle_t handle, esp_gmf_data_bus_block_t *blk, int block_ticks) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_IO_FAIL); + ESP_GMF_NULL_CHECK(TAG, blk, return ESP_GMF_IO_FAIL); + esp_gmf_fifo_t *fifo = (esp_gmf_fifo_t *)handle; + ESP_LOGD(TAG, "WR_RLS+, hd:%p, b:%p, l:%d, valid:%d, %d, %d", handle, blk->buf, blk->buf_length, blk->valid_size, esp_gmf_fifo_node_get_cnt(fifo->empty_head), + esp_gmf_fifo_node_get_cnt(fifo->fill_head)); + esp_gmf_oal_mutex_lock(fifo->lock); + esp_gmf_fifo_node_t *node = fifo->empty_head; + if (node == NULL) { + ESP_LOGE(TAG, "%s,%d, empty_head is NULL", __func__, __LINE__); + esp_gmf_oal_mutex_unlock(fifo->lock); + if (fifo->_is_abort) { + return ESP_GMF_IO_ABORT; + } + return ESP_GMF_IO_FAIL; + } + fifo->empty_head = node->next; + + node->next = NULL; + if (fifo->fill_head == NULL) { + fifo->fill_head = node; + } else { + esp_gmf_fifo_node_t *tmp = fifo->fill_head; + while (tmp->next != NULL) { + tmp = tmp->next; + } + tmp->next = node; + } + node->buffer = blk->buf; + node->buf_length = blk->buf_length; + node->valid_size = blk->valid_size; + node->is_done = blk->is_last; + + xSemaphoreGive(fifo->can_read); + esp_gmf_oal_mutex_unlock(fifo->lock); + ESP_LOGD(TAG, "WR_RLS-, hd:%p, b:%p, l:%d, valid:%d, n:%ld, e:%d, f:%d", handle, blk->buf, blk->buf_length, blk->valid_size, + fifo->node_cnt, esp_gmf_fifo_node_get_cnt(fifo->empty_head), esp_gmf_fifo_node_get_cnt(fifo->fill_head)); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_fifo_done_write(esp_gmf_fifo_handle_t handle) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_fifo_t *fifo = (esp_gmf_fifo_t *)handle; + fifo->_is_write_done = 1; + xSemaphoreGive(fifo->can_read); + xSemaphoreGive(fifo->can_write); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_fifo_abort(esp_gmf_fifo_handle_t handle) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_fifo_t *fifo = (esp_gmf_fifo_t *)handle; + fifo->_is_abort = 1; + xSemaphoreGive(fifo->can_read); + xSemaphoreGive(fifo->can_write); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_fifo_reset(esp_gmf_fifo_handle_t handle) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_fifo_t *fifo = (esp_gmf_fifo_t *)handle; + esp_gmf_fifo_node_t *node = fifo->fill_head; + while (node) { + node->is_done = 0; + node->valid_size = 0; + node = node->next; + } + node = fifo->empty_head; + while (node) { + node->is_done = 0; + node->valid_size = 0; + node = node->next; + } + fifo->_is_write_done = 0; + fifo->_is_abort = 0; + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_fifo_get_free_size(esp_gmf_fifo_handle_t handle, uint32_t *free_size) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, free_size, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_fifo_t *fifo = (esp_gmf_fifo_t *)handle; + *free_size = 0; + esp_gmf_fifo_node_t *node = fifo->empty_head; + while (node) { + *free_size += node->buf_length; + node = node->next; + } + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_fifo_get_total_size(esp_gmf_fifo_handle_t handle, uint32_t *total_size) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, total_size, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_fifo_t *fifo = (esp_gmf_fifo_t *)handle; + *total_size = 0; + esp_gmf_fifo_node_t *node = fifo->empty_head; + while (node) { + *total_size += node->buf_length; + node = node->next; + } + node = fifo->fill_head; + while (node) { + *total_size += node->buf_length; + node = node->next; + } + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_fifo_get_filled_size(esp_gmf_fifo_handle_t handle, uint32_t *filled_size) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, filled_size, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_fifo_t *fifo = (esp_gmf_fifo_t *)handle; + *filled_size = 0; + esp_gmf_fifo_node_t *node = fifo->fill_head; + while (node) { + *filled_size += node->buf_length; + node = node->next; + } + return ESP_GMF_ERR_OK; +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/data_bus/esp_gmf_new_databus.c b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/data_bus/esp_gmf_new_databus.c new file mode 100644 index 0000000..78f31a6 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/data_bus/esp_gmf_new_databus.c @@ -0,0 +1,182 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#include +#include +#include "esp_log.h" +#include "esp_gmf_oal_mem.h" +#include "esp_gmf_err.h" + +#include "esp_gmf_data_bus.h" +#include "esp_gmf_ringbuffer.h" +#include "esp_gmf_block.h" +#include "esp_gmf_pbuf.h" +#include "esp_gmf_fifo.h" + +static const char *TAG = "NEW_DATA_BUS"; + +int esp_gmf_db_new_ringbuf(int num, int item_cnt, esp_gmf_db_handle_t *h) +{ + ESP_GMF_NULL_CHECK(TAG, h, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_rb_handle_t rb = NULL; + esp_gmf_rb_create(num, item_cnt, &rb); + ESP_GMF_NULL_CHECK(TAG, rb, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_db_config_t db_config = { + .name = "ringbuffer", + .type = DATA_BUS_TYPE_BYTE, + .max_size = (item_cnt * num), + .max_item_num = num, + .child = rb, + }; + esp_gmf_data_bus_t *db = NULL; + if (ESP_GMF_ERR_OK != esp_gmf_db_init(&db_config, (esp_gmf_db_handle_t)&db)) { + if (rb) { + esp_gmf_rb_destroy(rb); + } + return ESP_GMF_ERR_MEMORY_LACK; + } + if (db == NULL) { + ESP_LOGE(TAG, "DATA BUS is NULL"); + return ESP_GMF_ERR_FAIL; + } + db->op.deinit = esp_gmf_rb_destroy; + db->op.acquire_read = esp_gmf_rb_acquire_read; + db->op.release_read = esp_gmf_rb_release_read; + db->op.acquire_write = esp_gmf_rb_acquire_write; + db->op.release_write = esp_gmf_rb_release_write; + db->op.done_write = esp_gmf_rb_done_write; + db->op.reset_done_write = esp_gmf_rb_reset_done_write; + db->op.reset = esp_gmf_rb_reset; + db->op.abort = esp_gmf_rb_abort; + db->op.get_total_size = esp_gmf_rb_get_size; + db->op.get_filled_size = esp_gmf_rb_bytes_filled; + db->op.get_available = esp_gmf_rb_bytes_available; + ESP_LOGI(TAG, "New ringbuffer:%p, num:%d, item_cnt:%d, db:%p", rb, num, item_cnt, db); + *h = db; + return ESP_GMF_ERR_OK; +} + +int esp_gmf_db_new_block(int num, int item_cnt, esp_gmf_db_handle_t *h) +{ + ESP_GMF_NULL_CHECK(TAG, h, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_block_handle_t handle = NULL; + esp_gmf_block_create(num, item_cnt, &handle); + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_db_config_t db_config = { + .name = "block", + .type = DATA_BUS_TYPE_BLOCK, + .max_size = (item_cnt * num), + .max_item_num = num, + .child = handle, + }; + esp_gmf_data_bus_t *db = NULL; + if (ESP_GMF_ERR_OK != esp_gmf_db_init(&db_config, (esp_gmf_db_handle_t)&db)) { + if (handle) { + esp_gmf_block_destroy(handle); + } + return ESP_GMF_ERR_MEMORY_LACK; + } + if (db == NULL) { + ESP_LOGE(TAG, "DATA BUS is NULL"); + return ESP_GMF_ERR_FAIL; + } + db->op.deinit = esp_gmf_block_destroy; + db->op.acquire_read = esp_gmf_block_acquire_read; + db->op.release_read = esp_gmf_block_release_read; + db->op.acquire_write = esp_gmf_block_acquire_write; + db->op.release_write = esp_gmf_block_release_write; + db->op.done_write = esp_gmf_block_done_write; + db->op.reset = esp_gmf_block_reset; + db->op.abort = esp_gmf_block_abort; + db->op.get_total_size = esp_gmf_block_get_total_size; + db->op.get_filled_size = esp_gmf_block_get_filled_size; + db->op.get_available = esp_gmf_block_get_free_size; + ESP_LOGI(TAG, "New block buf, num:%d, item_cnt:%d, db:%p", num, item_cnt, db); + *h = db; + return ESP_GMF_ERR_OK; +} + +int esp_gmf_db_new_pbuf(int num, int item_cnt, esp_gmf_db_handle_t *h) +{ + ESP_GMF_NULL_CHECK(TAG, h, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_pbuf_handle_t handle = NULL; + esp_gmf_pbuf_create(num, &handle); + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_db_config_t db_config = { + .name = "pbuf", + .type = DATA_BUS_TYPE_BLOCK, + .max_size = (1 * num), + .max_item_num = num, + .child = handle, + }; + esp_gmf_data_bus_t *db = NULL; + if (ESP_GMF_ERR_OK != esp_gmf_db_init(&db_config, (esp_gmf_db_handle_t)&db)) { + if (handle) { + esp_gmf_pbuf_destroy(handle); + } + return ESP_GMF_ERR_MEMORY_LACK; + } + if (db == NULL) { + ESP_LOGE(TAG, "DATA BUS is NULL"); + return ESP_GMF_ERR_FAIL; + } + db->op.deinit = esp_gmf_pbuf_destroy; + db->op.acquire_read = esp_gmf_pbuf_acquire_read; + db->op.release_read = esp_gmf_pbuf_release_read; + db->op.acquire_write = esp_gmf_pbuf_acquire_write; + db->op.release_write = esp_gmf_pbuf_release_write; + db->op.done_write = esp_gmf_pbuf_done_write; + db->op.reset = esp_gmf_pbuf_reset; + db->op.abort = esp_gmf_pbuf_abort; + db->op.get_total_size = NULL; + db->op.get_filled_size = NULL; + db->op.get_available = NULL; + ESP_LOGI(TAG, "New pbuf, num:%d, item_cnt:%d, db:%p", num, item_cnt, db); + *h = db; + return ESP_GMF_ERR_OK; +} + + +int esp_gmf_db_new_fifo(int num, int item_cnt, esp_gmf_db_handle_t *h) +{ + ESP_GMF_NULL_CHECK(TAG, h, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_fifo_handle_t handle = NULL; + esp_gmf_fifo_create(num, item_cnt, &handle); + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_db_config_t db_config = { + .name = "fifo", + .type = DATA_BUS_TYPE_BLOCK, + .max_size = (1 * num), + .max_item_num = num, + .child = handle, + }; + esp_gmf_data_bus_t *db = NULL; + if (ESP_GMF_ERR_OK != esp_gmf_db_init(&db_config, (esp_gmf_db_handle_t)&db)) { + if (handle) { + esp_gmf_fifo_destroy(handle); + } + return ESP_GMF_ERR_MEMORY_LACK; + } + if (db == NULL) { + ESP_LOGE(TAG, "DATA BUS is NULL"); + return ESP_GMF_ERR_FAIL; + } + db->op.deinit = esp_gmf_fifo_destroy; + db->op.acquire_read = esp_gmf_fifo_acquire_read; + db->op.release_read = esp_gmf_fifo_release_read; + db->op.acquire_write = esp_gmf_fifo_acquire_write; + db->op.release_write = esp_gmf_fifo_release_write; + db->op.done_write = esp_gmf_fifo_done_write; + db->op.reset = esp_gmf_fifo_reset; + db->op.abort = esp_gmf_fifo_abort; + db->op.get_total_size = esp_gmf_fifo_get_total_size; + db->op.get_filled_size = esp_gmf_fifo_get_filled_size; + db->op.get_available = esp_gmf_fifo_get_free_size; + ESP_LOGI(TAG, "New pbuf, num:%d, item_cnt:%d, db:%p", num, item_cnt, db); + *h = db; + return ESP_GMF_ERR_OK; +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/data_bus/esp_gmf_pbuf.c b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/data_bus/esp_gmf_pbuf.c new file mode 100644 index 0000000..7f49349 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/data_bus/esp_gmf_pbuf.c @@ -0,0 +1,363 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "esp_gmf_oal_mutex.h" +#include "esp_gmf_pbuf.h" +#include "esp_log.h" +#include "esp_gmf_oal_mem.h" +#include "esp_gmf_err.h" + +static const char *TAG = "ESP_GMF_PBUF"; + +#define THREAD_SAFE + +#ifdef THREAD_SAFE +#define pbuf_lock(x) esp_gmf_oal_mutex_lock(x) +#define pbuf_unlock(x) esp_gmf_oal_mutex_unlock(x) + +#else +#define pbuf_lock(x) +#define pbuf_unlock(x) + +#endif /* THREAD_SAFE */ + +/** + * @brief Structure representing a list node in the point buffer + */ +typedef struct pbuf_list_ { + struct pbuf_list_ *next; /*!< Pointer to the next node in the list */ + esp_gmf_data_bus_block_t block; /*!< Data bus block associated with the node */ +} pbuf_list_t; + +/** + * @brief Structure representing a point buffer + */ +typedef struct _pbuf_ { + pbuf_list_t *empty_head; /*!< Pointer to the head of the list of empty blocks */ + pbuf_list_t *empty_tail; /*!< Pointer to the tail of the list of empty blocks */ + pbuf_list_t *fill_head; /*!< Pointer to the head of the list of filled blocks */ + pbuf_list_t *fill_tail; /*!< Pointer to the tail of the list of filled blocks */ + void *lock; /*!< Pointer to the lock used for synchronization */ + int buf_cnt; /*!< Number of blocks in the buffer */ + uint32_t capacity; /*!< Total capacity of the buffer */ + uint8_t _is_write_done : 1; /*!< Flag indicating if writing is complete */ + uint8_t _is_abort : 1; /*!< Flag indicating if an abort operation has been requested */ +} esp_gmf_pbuf_t; + +static inline void _pbuf_handle_free(esp_gmf_pbuf_handle_t handle) +{ + esp_gmf_pbuf_t *pbuf = (esp_gmf_pbuf_t *)handle; + if (pbuf->lock) { + esp_gmf_oal_mutex_destroy(pbuf->lock); + } + esp_gmf_oal_free(pbuf); +} + +esp_gmf_err_t esp_gmf_pbuf_create(int capacity, esp_gmf_pbuf_handle_t *handle) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + if (capacity < 1) { + ESP_LOGE(TAG, "The capacity[%d] is not support", capacity); + return ESP_GMF_ERR_INVALID_ARG; + } + *handle = NULL; + esp_gmf_pbuf_t *pbuf = esp_gmf_oal_calloc(1, sizeof(esp_gmf_pbuf_t)); + ESP_GMF_MEM_CHECK(TAG, pbuf, return ESP_GMF_ERR_MEMORY_LACK;); + pbuf->capacity = capacity; + int ret = ESP_GMF_ERR_OK; + pbuf->lock = (SemaphoreHandle_t)esp_gmf_oal_mutex_create(); + ESP_GMF_MEM_CHECK(TAG, pbuf->lock, {ret = ESP_GMF_ERR_MEMORY_LACK; goto esp_gmf_pbuf_err;}); + pbuf->_is_write_done = 0; + pbuf->_is_abort = 0; + pbuf->buf_cnt = 0; + *handle = pbuf; + return ESP_GMF_ERR_OK; + +esp_gmf_pbuf_err: + _pbuf_handle_free(pbuf); + return ret; +} + +esp_gmf_err_t esp_gmf_pbuf_destroy(esp_gmf_pbuf_handle_t handle) +{ + ESP_GMF_MEM_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG;); + esp_gmf_pbuf_t *pbuf = (esp_gmf_pbuf_t *)handle; + pbuf_lock(pbuf->lock); + while (pbuf->empty_head) { + pbuf_list_t *tmp = pbuf->empty_head->next; + if (pbuf->empty_head->block.buf) { + esp_gmf_oal_free(pbuf->empty_head->block.buf); + } + esp_gmf_oal_free(pbuf->empty_head); + pbuf->empty_head = tmp; + } + while (pbuf->fill_head) { + pbuf_list_t *tmp = pbuf->fill_head->next; + if (pbuf->fill_head->block.buf) { + esp_gmf_oal_free(pbuf->fill_head->block.buf); + } + esp_gmf_oal_free(pbuf->fill_head); + pbuf->fill_head = tmp; + } + pbuf_unlock(pbuf->lock); + _pbuf_handle_free(pbuf); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_io_t esp_gmf_pbuf_acquire_read(esp_gmf_pbuf_handle_t handle, esp_gmf_data_bus_block_t *blk, uint32_t wanted_size, int block_ticks) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_IO_FAIL); + ESP_GMF_NULL_CHECK(TAG, blk, return ESP_GMF_IO_FAIL); + esp_gmf_pbuf_t *pbuf = (esp_gmf_pbuf_t *)handle; + if (pbuf->_is_write_done && pbuf->fill_head == NULL) { + blk->valid_size = 0; + blk->is_last = 1; + ESP_LOGE(TAG, "ACQ_RD, write done, fill head is empty, p:%p", pbuf); + return ESP_GMF_IO_OK; + } + if (pbuf->_is_abort) { + return ESP_GMF_IO_ABORT; + } + if (pbuf->fill_head == NULL) { + ESP_LOGD(TAG, "ACQ_RD, fill head is empty, p:%p", pbuf); + return ESP_GMF_IO_FAIL; + } + pbuf_lock(pbuf->lock); + + blk->buf = pbuf->fill_head->block.buf; + blk->buf_length = pbuf->fill_head->block.buf_length; + blk->valid_size = pbuf->fill_head->block.valid_size; + blk->is_last = pbuf->fill_head->block.is_last; + + pbuf_unlock(pbuf->lock); + ESP_LOGD(TAG, "ACQ_RD, p:%p, h:%p b:%p, l:%d, vld:%d,last:%d, c:%d", pbuf, + pbuf->fill_head, blk->buf, blk->buf_length, blk->valid_size, blk->is_last, pbuf->buf_cnt); + return ESP_GMF_IO_OK; +} + +esp_gmf_err_io_t esp_gmf_pbuf_release_read(esp_gmf_pbuf_handle_t handle, esp_gmf_data_bus_block_t *blk, int block_ticks) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_IO_FAIL); + ESP_GMF_NULL_CHECK(TAG, blk, return ESP_GMF_IO_FAIL); + esp_gmf_pbuf_t *pbuf = (esp_gmf_pbuf_t *)handle; + if (pbuf->_is_write_done && pbuf->fill_head == NULL) { + ESP_LOGE(TAG, "RLS_RD, write done, fill head is empty, p:%p", pbuf); + return ESP_GMF_IO_OK; + } + pbuf_lock(pbuf->lock); + if (pbuf->fill_head->block.buf == blk->buf) { + pbuf->fill_head->block.buf_length = blk->buf_length; + pbuf->fill_head->block.valid_size = 0; + pbuf->fill_head->block.is_last = 0; + pbuf_list_t *tmp = pbuf->fill_head->next; + pbuf_list_t *empty = pbuf->fill_head; + empty->next = NULL; + pbuf->fill_head = tmp; + + if (pbuf->empty_head == NULL) { + pbuf->empty_tail = empty; + pbuf->empty_head = empty; + } else { + pbuf->empty_tail->next = empty; + pbuf->empty_tail = empty; + } + blk->buf = NULL; + blk->is_last = 0; + blk->valid_size = 0; + pbuf->buf_cnt--; + } else { + ESP_LOGE(TAG, "RLS_RD, the buffer is not belong to filled buffer, p:%p, buf:%p", pbuf, blk->buf); + pbuf_unlock(pbuf->lock); + return ESP_GMF_IO_FAIL; + } + ESP_LOGD(TAG, "RLS_RD, p:%p, head:%p, e:%p, b:%p, l:%d, vld:%d, c:%d", pbuf, pbuf->fill_head, pbuf->empty_head, blk->buf, blk->buf_length, blk->valid_size, pbuf->buf_cnt); + pbuf_unlock(pbuf->lock); + return ESP_GMF_IO_OK; +} + +esp_gmf_err_io_t esp_gmf_pbuf_acquire_write(esp_gmf_pbuf_handle_t handle, esp_gmf_data_bus_block_t *blk, uint32_t wanted_size, int block_ticks) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_IO_FAIL); + ESP_GMF_NULL_CHECK(TAG, blk, return ESP_GMF_IO_FAIL); + esp_gmf_pbuf_t *pbuf = (esp_gmf_pbuf_t *)handle; + if (wanted_size == 0) { + ESP_LOGE(TAG, "ACQ_WR, the wanted size is not correct, p:%p, size:%ld", pbuf, wanted_size); + return ESP_GMF_IO_FAIL; + } + if (pbuf->_is_write_done) { + return ESP_GMF_IO_OK; + } + if (pbuf->_is_abort) { + return ESP_GMF_IO_ABORT; + } + pbuf_lock(pbuf->lock); + if (pbuf->empty_head == NULL) { + if (pbuf->capacity < pbuf->buf_cnt) { + ESP_LOGE(TAG, "ACQ_WR, the buffer size is out of range, %p, cnt:%d, cap:%ld", pbuf, pbuf->buf_cnt, pbuf->capacity); + pbuf_unlock(pbuf->lock); + return ESP_GMF_IO_FAIL; + } + pbuf_list_t *new_pbuf = (pbuf_list_t *)esp_gmf_oal_calloc(1, sizeof(pbuf_list_t)); + ESP_GMF_MEM_CHECK(TAG, new_pbuf, { pbuf_unlock(pbuf->lock); return ESP_GMF_IO_FAIL;}); + new_pbuf->block.buf = (uint8_t *)esp_gmf_oal_calloc(1, wanted_size); + ESP_GMF_MEM_CHECK(TAG, new_pbuf->block.buf, { pbuf_unlock(pbuf->lock); esp_gmf_oal_free(new_pbuf); return ESP_GMF_IO_FAIL;}); + new_pbuf->block.buf_length = wanted_size; + new_pbuf->block.valid_size = wanted_size; + new_pbuf->block.is_last = 0; + pbuf->empty_head = new_pbuf; + pbuf->empty_tail = new_pbuf; + ESP_LOGD(TAG, "ACQ_WR, w:%ld, new buf:%p, l:%d, self:%p, next:%p", wanted_size, new_pbuf->block.buf, + new_pbuf->block.buf_length, new_pbuf, new_pbuf->next); + } + if (pbuf->empty_head->block.buf_length < wanted_size) { + if (pbuf->empty_head->block.buf) { + esp_gmf_oal_free(pbuf->empty_head->block.buf); + } + pbuf->empty_head->block.buf = esp_gmf_oal_calloc(1, wanted_size); + ESP_GMF_MEM_CHECK(TAG, pbuf->empty_head->block.buf, { pbuf_unlock(pbuf->lock); return ESP_GMF_IO_FAIL;}); + pbuf->empty_head->block.buf_length = wanted_size; + pbuf->empty_head->block.valid_size = wanted_size; + } + blk->buf = pbuf->empty_head->block.buf; + blk->buf_length = pbuf->empty_head->block.buf_length; + blk->valid_size = pbuf->empty_head->block.valid_size; + if (wanted_size <= blk->buf_length) { + blk->valid_size = wanted_size; + } + pbuf->buf_cnt++; + ESP_LOGD(TAG, "ACQ_WR, w:%ld, b:%p, l:%d, vld:%d self:%p, nxt:%p, c:%d", wanted_size, blk->buf, + blk->buf_length, blk->valid_size, pbuf->empty_head, pbuf->empty_head->next, pbuf->buf_cnt); + pbuf_unlock(pbuf->lock); + return ESP_GMF_IO_OK; +} + +esp_gmf_err_io_t esp_gmf_pbuf_release_write(esp_gmf_pbuf_handle_t handle, esp_gmf_data_bus_block_t *blk, int block_ticks) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_IO_FAIL); + ESP_GMF_NULL_CHECK(TAG, blk, return ESP_GMF_IO_FAIL); + esp_gmf_pbuf_t *pbuf = (esp_gmf_pbuf_t *)handle; + if (pbuf->_is_write_done) { + return ESP_GMF_IO_OK; + } + pbuf_lock(pbuf->lock); + if (pbuf->empty_head->block.buf == blk->buf) { + pbuf->empty_head->block.buf_length = blk->buf_length; + pbuf->empty_head->block.valid_size = blk->valid_size; + pbuf->empty_head->block.is_last = blk->is_last; + pbuf_list_t *tmp = pbuf->empty_head->next; + pbuf_list_t *filled = pbuf->empty_head; + filled->next = NULL; + pbuf->empty_head = tmp; + + if (pbuf->fill_head == NULL) { + pbuf->fill_tail = filled; + pbuf->fill_head = filled; + } else { + pbuf->fill_tail->next = filled; + pbuf->fill_tail = filled; + } + blk->buf = NULL; + blk->is_last = 0; + blk->valid_size = 0; + } else { + ESP_LOGE(TAG, "RLS_WR, buffer is not belong to empty header, p:%p, buf:%p", pbuf, blk->buf); + pbuf_unlock(pbuf->lock); + return ESP_GMF_IO_FAIL; + } + ESP_LOGD(TAG, "RLS_WR, p:%p, h:%p, b:%p, l:%d, vld:%d,last:%d, c:%d", pbuf, pbuf->empty_head, blk->buf, + blk->buf_length, blk->valid_size, blk->is_last, pbuf->buf_cnt); + pbuf_unlock(pbuf->lock); + return ESP_GMF_IO_OK; +} + +esp_gmf_err_t esp_gmf_pbuf_done_write(esp_gmf_pbuf_handle_t handle) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_pbuf_t *pbuf = (esp_gmf_pbuf_t *)handle; + pbuf->_is_write_done = 1; + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_pbuf_abort(esp_gmf_pbuf_handle_t handle) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_pbuf_t *pbuf = (esp_gmf_pbuf_t *)handle; + pbuf->_is_abort = 1; + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_pbuf_reset(esp_gmf_pbuf_handle_t handle) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_pbuf_t *pbuf = (esp_gmf_pbuf_t *)handle; + pbuf_lock(pbuf->lock); + pbuf->_is_write_done = 0; + pbuf->_is_abort = 0; + pbuf_unlock(pbuf->lock); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_pbuf_get_free_size(esp_gmf_pbuf_handle_t handle, uint32_t *free_size) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, free_size, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_pbuf_t *pbuf = (esp_gmf_pbuf_t *)handle; + pbuf_lock(pbuf->lock); + pbuf_list_t *tmp = pbuf->empty_head; + int sz = 0; + while (tmp) { + sz += tmp->block.buf_length; + tmp = tmp->next; + } + *free_size = sz; + pbuf_unlock(pbuf->lock); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_pbuf_get_filled_size(esp_gmf_pbuf_handle_t handle, uint32_t *filled_size) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, filled_size, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_pbuf_t *pbuf = (esp_gmf_pbuf_t *)handle; + pbuf_lock(pbuf->lock); + pbuf_list_t *tmp = pbuf->fill_head; + int sz = 0; + while (tmp) { + sz += tmp->block.valid_size; + tmp = tmp->next; + } + *filled_size = sz; + pbuf_unlock(pbuf->lock); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_pbuf_get_total_size(esp_gmf_pbuf_handle_t handle, uint32_t *total_size) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, total_size, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_pbuf_t *pbuf = (esp_gmf_pbuf_t *)handle; + pbuf_lock(pbuf->lock); + pbuf_list_t *tmp = pbuf->fill_head; + int sz = 0; + while (tmp) { + sz += tmp->block.buf_length; + tmp = tmp->next; + } + tmp = pbuf->empty_head; + while (tmp) { + sz += tmp->block.buf_length; + tmp = tmp->next; + } + *total_size = sz; + pbuf_unlock(pbuf->lock); + return ESP_GMF_ERR_OK; +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/data_bus/esp_gmf_ringbuffer.c b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/data_bus/esp_gmf_ringbuffer.c new file mode 100644 index 0000000..2f1fca6 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/data_bus/esp_gmf_ringbuffer.c @@ -0,0 +1,375 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "esp_gmf_ringbuffer.h" +#include "esp_log.h" +#include "esp_gmf_oal_mem.h" + +static const char *TAG = "ESP_GMF_RB"; + +/** + * @brief Structure representing a ring buffer + */ +struct esp_gmf_ringbuffer { + char *p_o; /*!< Original pointer */ + char *volatile p_r; /*!< Read pointer */ + char *volatile p_w; /*!< Write pointer */ + volatile uint32_t fill_cnt; /*!< Number of filled size */ + uint32_t size; /*!< Buffer size */ + SemaphoreHandle_t can_read; /*!< Semaphore to control reading */ + SemaphoreHandle_t can_write; /*!< Semaphore to control writing */ + SemaphoreHandle_t lock; /*!< Semaphore to protect critical sections */ + uint8_t abort_read : 1; /*!< Flag to indicate read abort */ + uint8_t abort_write : 1; /*!< Flag to indicate write abort */ + uint8_t is_done_write : 1; /*!< Flag to signal completion of writing */ +}; + +esp_gmf_err_t esp_gmf_rb_create(int block_size, int n_blocks, esp_gmf_rb_handle_t *handle) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + if ((n_blocks < 1) + || (block_size < 1)) { + ESP_LOGE(TAG, "Invalid parameters,cnt:%d, size:%d", n_blocks, block_size); + return ESP_GMF_ERR_INVALID_ARG; + } + struct esp_gmf_ringbuffer *rb = NULL; + *handle = NULL; + bool _success = ( + (rb = esp_gmf_oal_malloc(sizeof(struct esp_gmf_ringbuffer))) && + (rb->p_o = esp_gmf_oal_calloc(n_blocks, block_size)) && + (rb->can_read = xSemaphoreCreateBinary()) && + (rb->lock = xSemaphoreCreateMutex()) && + (rb->can_write = xSemaphoreCreateBinary()) + ); + + ESP_GMF_MEM_CHECK(TAG, _success, goto _esp_gmf_rb_init_failed); + rb->p_r = rb->p_w = rb->p_o; + rb->fill_cnt = 0; + rb->size = block_size * n_blocks; + rb->is_done_write = 0; + rb->abort_read = 0; + rb->abort_write = 0; + *handle = rb; + return ESP_GMF_ERR_OK; +_esp_gmf_rb_init_failed: + esp_gmf_rb_destroy(rb); + return ESP_GMF_ERR_FAIL; +} + +esp_gmf_err_t esp_gmf_rb_destroy(esp_gmf_rb_handle_t handle) +{ + struct esp_gmf_ringbuffer *rb = (struct esp_gmf_ringbuffer *)handle; + if (rb == NULL) { + return ESP_GMF_ERR_INVALID_ARG; + } + if (rb->p_o) { + esp_gmf_oal_free(rb->p_o); + rb->p_o = NULL; + } + if (rb->can_read) { + vSemaphoreDelete(rb->can_read); + rb->can_read = NULL; + } + if (rb->can_write) { + vSemaphoreDelete(rb->can_write); + rb->can_write = NULL; + } + if (rb->lock) { + vSemaphoreDelete(rb->lock); + rb->lock = NULL; + } + esp_gmf_oal_free(rb); + rb = NULL; + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_rb_reset(esp_gmf_rb_handle_t handle) +{ + struct esp_gmf_ringbuffer *rb = (struct esp_gmf_ringbuffer *)handle; + if (rb == NULL) { + return ESP_GMF_ERR_INVALID_ARG; + } + rb->p_r = rb->p_w = rb->p_o; + rb->fill_cnt = 0; + rb->is_done_write = 0; + rb->abort_read = 0; + rb->abort_write = 0; + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_io_t esp_gmf_rb_acquire_read(esp_gmf_rb_handle_t handle, esp_gmf_data_bus_block_t *blk, uint32_t wanted_size, int ticks_to_wait) +{ + struct esp_gmf_ringbuffer *rb = (struct esp_gmf_ringbuffer *)handle; + int read_size = 0; + int total_read_size = 0; + int ret_val = ESP_GMF_IO_OK; + + if (rb == NULL || blk == NULL) { + ESP_LOGE(TAG, "Invalid parameters on acquire read, rb:%p, blk:%p", rb, blk); + return ESP_GMF_IO_FAIL; + } + uint32_t buf_len = wanted_size; + uint8_t *buf = blk->buf; + ESP_LOGV(TAG, "ACQ_RD+:%p, b:%p, l:%d, s:%ld", rb, buf, blk->buf_length, wanted_size); + while (buf_len) { + // take buffer lock + if (xSemaphoreTake(rb->lock, portMAX_DELAY) != pdTRUE) { + ret_val = ESP_GMF_IO_TIMEOUT; + goto read_err; + } + + if (rb->fill_cnt < buf_len) { + read_size = rb->fill_cnt; + /** + * When non-multiple of 4(word size) bytes are written to I2S, there is noise. + * Below is the kind of workaround to read only in multiple of 4. Avoids noise when rb is read in small chunks. + * Note that, when we have buf_len bytes available in rb, we still read those irrespective of if it's multiple of 4. + */ + read_size = read_size & 0xfffffffc; + if ((read_size == 0) && rb->is_done_write) { + read_size = rb->fill_cnt; + } + } else { + read_size = buf_len; + } + if (read_size == 0) { + // no data to read, release thread block to allow other threads to write data + if (rb->is_done_write) { + blk->is_last = 1; + xSemaphoreGive(rb->lock); + goto read_err; + } + if (rb->abort_read) { + ret_val = ESP_GMF_IO_ABORT; + xSemaphoreGive(rb->lock); + goto read_err; + } + + xSemaphoreGive(rb->lock); + xSemaphoreGive(rb->can_write); + // wait till some data available to read + if (xSemaphoreTake(rb->can_read, ticks_to_wait) != pdTRUE) { + ret_val = ESP_GMF_IO_TIMEOUT; + goto read_err; + } + if (rb->abort_read) { + ret_val = ESP_GMF_IO_ABORT; + goto read_err; + } + continue; + } + if (rb->abort_read) { + ret_val = ESP_GMF_IO_ABORT; + xSemaphoreGive(rb->lock); + goto read_err; + } + if ((rb->p_r + read_size) > (rb->p_o + rb->size)) { + int rlen1 = rb->p_o + rb->size - rb->p_r; + int rlen2 = read_size - rlen1; + if (buf) { + memcpy(buf, rb->p_r, rlen1); + memcpy(buf + rlen1, rb->p_o, rlen2); + } + rb->p_r = rb->p_o + rlen2; + } else { + if (buf) { + memcpy(buf, rb->p_r, read_size); + } + rb->p_r = rb->p_r + read_size; + } + + buf_len -= read_size; + rb->fill_cnt -= read_size; + total_read_size += read_size; + buf += read_size; + xSemaphoreGive(rb->lock); + if (buf_len == 0) { + break; + } + } +read_err: + if (total_read_size > 0) { + xSemaphoreGive(rb->can_write); + } + ESP_LOGV(TAG, "ACQ_RD-:%p, ret:%d", rb, ret_val); + blk->valid_size = total_read_size; + return ret_val; +} + +esp_gmf_err_io_t esp_gmf_rb_release_read(esp_gmf_rb_handle_t handle, esp_gmf_data_bus_block_t *blk, int block_ticks) +{ + // Do nothing + return ESP_GMF_IO_OK; +} + +esp_gmf_err_io_t esp_gmf_rb_acquire_write(esp_gmf_rb_handle_t handle, esp_gmf_data_bus_block_t *blk, uint32_t wanted_size, int ticks_to_wait) +{ + return ESP_GMF_IO_OK; +} + +esp_gmf_err_io_t esp_gmf_rb_release_write(esp_gmf_rb_handle_t handle, esp_gmf_data_bus_block_t *blk, int block_ticks) +{ + struct esp_gmf_ringbuffer *rb = (struct esp_gmf_ringbuffer *)handle; + uint32_t write_size; + int total_write_size = 0; + esp_gmf_err_io_t ret_val = ESP_GMF_IO_OK; + if (rb == NULL || blk == NULL) { + ESP_LOGE(TAG, "Invalid parameters on release write, rb:%p, blk:%p", rb, blk); + return ESP_GMF_IO_FAIL; + } + uint8_t *buf = blk->buf; + int buf_len = blk->valid_size; + esp_gmf_rb_bytes_available(rb, &write_size); + ESP_LOGV(TAG, "RLS_WR+:%p, blk:%p, vld_sz:%d, wanted:%ld, time:%d", rb, blk, blk->valid_size, write_size, block_ticks); + while (buf_len) { + // take buffer lock + if (xSemaphoreTake(rb->lock, portMAX_DELAY) != pdTRUE) { + ret_val = ESP_GMF_IO_TIMEOUT; + goto write_err; + } + esp_gmf_rb_bytes_available(rb, &write_size); + if (buf_len < write_size) { + write_size = buf_len; + } + if (write_size == 0) { + // no space to write, release thread block to allow other to read data + if (rb->is_done_write) { + xSemaphoreGive(rb->lock); + ESP_LOGD(TAG, "WR:%p, done\r\n", rb); + goto write_err; + } + if (rb->abort_write) { + ret_val = ESP_GMF_IO_ABORT; + xSemaphoreGive(rb->lock); + ESP_LOGD(TAG, "WR:%p, abort\r\n", rb); + goto write_err; + } + xSemaphoreGive(rb->lock); + xSemaphoreGive(rb->can_read); + // wait till we have some empty space to write + if (xSemaphoreTake(rb->can_write, block_ticks) != pdTRUE) { + ret_val = ESP_GMF_IO_TIMEOUT; + ESP_LOGD(TAG, "WR:%p, timeout:%d\r\n", rb, block_ticks); + goto write_err; + } + if (rb->abort_write) { + ret_val = ESP_GMF_IO_ABORT; + ESP_LOGD(TAG, "WR:%p, abort\r\n", rb); + goto write_err; + } + continue; + } + if (rb->abort_write) { + ret_val = ESP_GMF_IO_ABORT; + xSemaphoreGive(rb->lock); + ESP_LOGD(TAG, "WR:%p, abort\r\n", rb); + goto write_err; + } + if ((rb->p_w + write_size) > (rb->p_o + rb->size)) { + int wlen1 = rb->p_o + rb->size - rb->p_w; + int wlen2 = write_size - wlen1; + memcpy(rb->p_w, buf, wlen1); + memcpy(rb->p_o, buf + wlen1, wlen2); + rb->p_w = rb->p_o + wlen2; + } else { + memcpy(rb->p_w, buf, write_size); + rb->p_w = rb->p_w + write_size; + } + + buf_len -= write_size; + rb->fill_cnt += write_size; + total_write_size += write_size; + buf += write_size; + xSemaphoreGive(rb->lock); + if (buf_len == 0) { + break; + } + } +write_err: + if (total_write_size > 0) { + xSemaphoreGive(rb->can_read); + ret_val = ESP_GMF_IO_OK; + } + ESP_LOGV(TAG, "RLS_WR-:%p, ret:%d, ws:%d, fill:%ld", rb, ret_val, total_write_size, rb->fill_cnt); + if (blk->is_last) { + esp_gmf_rb_done_write(rb); + ret_val = ESP_GMF_IO_OK; + } + return ret_val; +} + +esp_gmf_err_t esp_gmf_rb_abort(esp_gmf_rb_handle_t handle) +{ + struct esp_gmf_ringbuffer *rb = (struct esp_gmf_ringbuffer *)handle; + if (rb == NULL) { + return ESP_GMF_ERR_INVALID_ARG; + } + ESP_LOGD(TAG, "Abort, rb:%p", rb); + rb->abort_read = 1; + xSemaphoreGive(rb->can_read); + rb->abort_write = 1; + xSemaphoreGive(rb->can_write); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_rb_done_write(esp_gmf_rb_handle_t handle) +{ + struct esp_gmf_ringbuffer *rb = (struct esp_gmf_ringbuffer *)handle; + if (rb == NULL) { + return ESP_GMF_ERR_INVALID_ARG; + } + rb->is_done_write = 1; + ESP_LOGD(TAG, "Set done write, rb:%p", rb); + xSemaphoreGive(rb->can_read); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_rb_reset_done_write(esp_gmf_rb_handle_t handle) +{ + struct esp_gmf_ringbuffer *rb = (struct esp_gmf_ringbuffer *)handle; + if (rb == NULL) { + return ESP_GMF_ERR_INVALID_ARG; + } + rb->is_done_write = 0; + ESP_LOGD(TAG, "Reset done write, rb:%p", rb); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_rb_bytes_available(esp_gmf_rb_handle_t handle, uint32_t *available_size) +{ + struct esp_gmf_ringbuffer *rb = (struct esp_gmf_ringbuffer *)handle; + if (rb && available_size) { + *available_size = (rb->size - rb->fill_cnt); + return ESP_GMF_ERR_OK; + } + return ESP_GMF_ERR_INVALID_ARG; +} + +esp_gmf_err_t esp_gmf_rb_bytes_filled(esp_gmf_rb_handle_t handle, uint32_t *filled_size) +{ + struct esp_gmf_ringbuffer *rb = (struct esp_gmf_ringbuffer *)handle; + if (rb && filled_size) { + *filled_size = rb->fill_cnt; + return ESP_GMF_ERR_OK; + } + return ESP_GMF_ERR_INVALID_ARG; +} + +esp_gmf_err_t esp_gmf_rb_get_size(esp_gmf_rb_handle_t handle, uint32_t *valid_size) +{ + struct esp_gmf_ringbuffer *rb = (struct esp_gmf_ringbuffer *)handle; + if (rb == NULL || valid_size == NULL) { + return ESP_GMF_ERR_INVALID_ARG; + } + *valid_size = rb->size; + return ESP_GMF_ERR_OK; +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/data_bus/include/esp_gmf_block.h b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/data_bus/include/esp_gmf_block.h new file mode 100644 index 0000000..4b6e0c1 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/data_bus/include/esp_gmf_block.h @@ -0,0 +1,214 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#include "esp_gmf_data_bus.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * @brief GMF block buffer is an interface for passing buffer addresses without generating any copies. + * It first allocates a memory block of size block_size * block_cnt. Each time the Acquire API is called, + * it returns the starting address of each block, and each Acquire size recommendation is divisible by the total cache size. + * Acquire size also supports any value within the total size, and the recommended Acquire size that is divisible + * by the total has the best performance. After esp_gmf_block_acquire_write and esp_gmf_block_release_write, + * esp_gmf_block_acquire_read can obtain the written data. + * + * These interfaces are thread-safe, and esp_gmf_block_acquire_write and esp_gmf_block_acquire_read also have blocking functionality. + */ + +/** + * @brief Represents a handle to a block buffer + */ +typedef void *esp_gmf_block_handle_t; + +/** + * @brief Create a block buffer with total size = block_size * block_cnt + * + * @param[in] block_size Size of each block + * @param[in] block_cnt Number of blocks + * @param[out] handle Pointer to store the handle to the created block buffer + * + * @return + * - ESP_GMF_ERR_OK Operation successful + * - ESP_GMF_ERR_INVALID_ARG Invalid argument provided + * - ESP_GMF_ERR_MEMORY_LACK Memory allocation failed + */ +esp_gmf_err_t esp_gmf_block_create(int block_size, int block_cnt, esp_gmf_block_handle_t *handle); + +/** + * @brief Cleanup and free all memory created by esp_gmf_block_create + * + * @param[in] handle The block handle + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_block_destroy(esp_gmf_block_handle_t handle); + +/** + * @brief Retrieve the address of a valid data buffer from the internal buffer for reading, without performing any copying. + * If there is insufficient data and block_ticks is greater than 0, the function will block until enough data becomes available. + * + * @note 1. It's recommended to set `wanted_size` either equal to `block_size` or to ensure + * that the total size divided by `wanted_size` results in no remainder. This ensures expected + * behavior, particularly when reaching the end of the buffer, where the return value may not + * match the expected value due to the buffer's inability to wrap from the tail to the head. + * 2. The obtained buffer address is internal and should not be freed externally + * 3. The `wanted_size` can't be greater than block_size * block_cnt + * 4. The actual valid size is stored in `blk->valid_size` + * + * @param[in] handle Handle to the block buffer + * @param[out] blk Pointer to the data bus block structure to store the acquired data + * @param[in] wanted_size Desired size of data to be acquired + * @param[in] block_ticks Ticks to wait for the operation + * + * @return + * - > 0 The specific length of data being read + * - ESP_GMF_IO_OK Operation succeeded + * - ESP_GMF_IO_FAIL Invalid arguments, or wanted_size > total size + * - ESP_GMF_IO_TIMEOUT Operation timed out + */ +esp_gmf_err_io_t esp_gmf_block_acquire_read(esp_gmf_block_handle_t handle, esp_gmf_data_bus_block_t *blk, uint32_t wanted_size, int block_ticks); + +/** + * @brief Returned acquired buffer address to the specific handle + * + * @note 1. The returned buffer MUST be acquired from `esp_gmf_block_acquire_read` to ensure proper synchronization + * 2. `esp_gmf_block_acquire_read` and `esp_gmf_block_release_read` must be called in pairs, and consecutive acquisition + * followed by consecutive release is not allowed + * 3. If the buffer address reaches the end of the internal buffer, the read pointer will be reset to the beginning of the internal buffer + * + * @param[in] handle Handle to the block buffer + * @param[in] blk Pointer to the data bus block structure containing the data to be released + * @param[in] block_ticks Ticks to wait for the operation + * + * @return + * - ESP_GMF_IO_OK Operation succeeded + * - ESP_GMF_IO_FAIL Invalid arguments, or the buffer does not belong to the provided handle + */ +esp_gmf_err_io_t esp_gmf_block_release_read(esp_gmf_block_handle_t handle, esp_gmf_data_bus_block_t *blk, int block_ticks); + +/** + * @brief Acquire space corresponding to the desired size within a specific handle + * If there is insufficient space and block_ticks is greater than 0, the function will block until enough space becomes available. + * + * @note 1. `esp_gmf_block_acquire_write` and `esp_gmf_block_release_write` must be called in pairs, and consecutive acquisition + * followed by consecutive release is not allowed + * 2. The obtained buffer address is internal and should not be freed externally + * 3. The `wanted_size` can't be greater than block_size * block_cnt + * + * @param[in] handle Handle to the block buffer + * @param[out] blk Pointer to the data bus block structure to store the acquired space + * @param[in] wanted_size Desired size of space to be acquired + * @param[in] block_ticks Ticks to wait for the operation + * + * @return + * - > 0 The specific length of space can be write + * - ESP_GMF_IO_OK Operation succeeded, or it's done to write + * - ESP_GMF_IO_FAIL Invalid arguments, or no filled data + * - ESP_GMF_IO_TIMEOUT Operation timed out + */ +esp_gmf_err_io_t esp_gmf_block_acquire_write(esp_gmf_block_handle_t handle, esp_gmf_data_bus_block_t *blk, uint32_t wanted_size, int block_ticks); + +/** + * @brief Returned acquired buffer address to the specific handle + * + * @note 1. The returned buffer MUST be acquired from `esp_gmf_block_acquire_write` to ensure proper synchronization + * 2. `esp_gmf_block_acquire_write` and `esp_gmf_block_release_write` must be called in pairs, and consecutive acquisition + * followed by consecutive release is not allowed + * 3. If the buffer address reaches the end of the internal buffer, the write pointer will be reset to the beginning of the internal buffer + * + * @param[in] handle Handle to the block buffer + * @param[in] blk Pointer to the data bus block structure containing the space to be released + * @param[in] block_ticks Ticks to wait for the operation + * + * @return + * - ESP_GMF_IO_OK Operation succeeded + * - ESP_GMF_IO_FAIL Invalid arguments, or the buffer does not belong to the provided handle + */ +esp_gmf_err_io_t esp_gmf_block_release_write(esp_gmf_block_handle_t handle, esp_gmf_data_bus_block_t *blk, int block_ticks); + +/** + * @brief Set status of writing to is done + * + * @note This function just mark the done flag, which is actually used in esp_gmf_block_release_write to ensure that the + * last frame of data can be read. So it will be called in esp_gmf_block_release_write only. + * + * @param[in] handle The block handle + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_block_done_write(esp_gmf_block_handle_t handle); + +/** + * @brief Abort waiting if reading or writing is blocking + * + * @param[in] handle The block handle + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_block_abort(esp_gmf_block_handle_t handle); + +/** + * @brief Reset, clear all values as initial state + * + * @param[in] handle The block handle + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_block_reset(esp_gmf_block_handle_t handle); + +/** + * @brief Get the free size of a GMF block + * + * @param[in] handle The handle to the GMF block. + * @param[out] free_size Pointer to a variable where the free size will be stored. + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_block_get_free_size(esp_gmf_block_handle_t handle, uint32_t *free_size); + +/** + * @brief Get the total size of a GMF block + * + * @param[in] handle The handle to the GMF block + * @param[out] total_size Pointer to a variable where the total size of the block will be stored + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_block_get_total_size(esp_gmf_block_handle_t handle, uint32_t *total_size); + +/** + * @brief Get the filled size of a GMF block + * + * @param[in] handle The handle to the GMF block + * @param[out] filled_size Pointer to a variable where the filled size will be stored + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_block_get_filled_size(esp_gmf_block_handle_t handle, uint32_t *filled_size); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/data_bus/include/esp_gmf_data_bus.h b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/data_bus/include/esp_gmf_data_bus.h new file mode 100644 index 0000000..ff86cd4 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/data_bus/include/esp_gmf_data_bus.h @@ -0,0 +1,358 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#include "esp_gmf_err.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * @brief Handle for the data bus interface + */ +typedef void *esp_gmf_db_handle_t; + +/** + * @brief Type of data bus + */ +typedef enum { + DATA_BUS_TYPE_BYTE = 1, /*!< Data bus type for byte-oriented data */ + DATA_BUS_TYPE_BLOCK = 2, /*!< Data bus type for block-oriented data */ +} esp_gmf_data_bus_type_t; + +/** + * @brief Structure representing a block of data for block-oriented data buses + */ +typedef struct { + uint8_t *buf; /*!< Pointer to the buffer */ + size_t buf_length; /*!< Length of the buffer */ + size_t valid_size; /*!< Valid data size in the buffer */ + bool is_last; /*!< Flag indicating if the buffer is the last */ +} esp_gmf_data_bus_block_t; + +/** + * @brief Structure representing the operations of a data bus + */ +struct data_bus_op_t { + + esp_gmf_err_t (*deinit)(esp_gmf_db_handle_t handle); /*!< Deinitialize the data bus */ + esp_gmf_err_t (*read)(esp_gmf_db_handle_t handle, uint8_t *buffer, int buf_len, int block_ticks); /*!< Read data from the data bus */ + esp_gmf_err_t (*write)(esp_gmf_db_handle_t handle, uint8_t *buffer, int written_size, int block_ticks); /*!< Write data to the data bus */ + + esp_gmf_err_io_t (*acquire_read)(esp_gmf_db_handle_t handle, esp_gmf_data_bus_block_t *blk, uint32_t wanted_size, int block_ticks); /*!< Acquire a block of data for reading */ + esp_gmf_err_io_t (*release_read)(esp_gmf_db_handle_t handle, esp_gmf_data_bus_block_t *blk, int block_ticks); /*!< Release a block of data after reading */ + + esp_gmf_err_io_t (*acquire_write)(esp_gmf_db_handle_t handle, esp_gmf_data_bus_block_t *blk, uint32_t wanted_size, int block_ticks); /*!< Acquire a block of data for writing */ + esp_gmf_err_io_t (*release_write)(esp_gmf_db_handle_t handle, esp_gmf_data_bus_block_t *blk, int block_ticks); /*!< Release a block of data after writing */ + + esp_gmf_err_t (*done_write)(esp_gmf_db_handle_t handle); /*!< Signal that writing to the data bus is done */ + esp_gmf_err_t (*reset_done_write)(esp_gmf_db_handle_t handle); /*!< Reset the "done writing" signal */ + esp_gmf_err_t (*reset)(esp_gmf_db_handle_t handle); /*!< Reset the data bus */ + esp_gmf_err_t (*abort)(esp_gmf_db_handle_t handle); /*!< Abort ongoing operations */ + esp_gmf_err_t (*get_total_size)(esp_gmf_db_handle_t handle, uint32_t *size); /*!< Get the total size of the data bus */ + esp_gmf_err_t (*get_filled_size)(esp_gmf_db_handle_t handle, uint32_t *filled_size); /*!< Get the filled size of the data bus */ + esp_gmf_err_t (*get_available)(esp_gmf_db_handle_t handle, uint32_t *available_size); /*!< Get the available size of the data bus */ +}; + +/** + * @brief Structure representing a data bus + */ +typedef struct { + struct data_bus_op_t op; /*!< Operations of the data bus */ + void *child; /*!< Pointer to the child */ + void *writer; /*!< Pointer to the writer who call the write associated functions */ + void *reader; /*!< Pointer to the reader who call the read associated functions*/ + const char *name; /*!< Name of the data bus */ + esp_gmf_data_bus_type_t type; /*!< Type of the data bus */ + int max_item_num; /*!< Maximum number of items */ + int max_size; /*!< Maximum size */ +} esp_gmf_data_bus_t; + +/** + * @brief Configuration for the database + */ +typedef struct { + void *child; /*!< Pointer to the child */ + const char *name; /*!< Name of the database */ + esp_gmf_data_bus_type_t type; /*!< Type of the data bus */ + int max_item_num; /*!< Maximum number of items */ + int max_size; /*!< Maximum size */ + bool safeguard; /*!< Flag indicating if the safeguard is enabled */ +} esp_gmf_db_config_t; + +/** + * @brief Initialize data bus with the given configuration + * + * @param[in] db_config Pointer to the configuration structure + * @param[out] hd Pointer to store the data bus handle after initialization + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid arguments + * - ESP_GMF_ERR_MEMORY_LACK Memory allocation failed + */ +esp_gmf_err_t esp_gmf_db_init(esp_gmf_db_config_t *db_config, esp_gmf_db_handle_t *hd); + +/** + * @brief Deinitialize data bus, freeing associated resources + * + * @param[in] handle data bus handle to deinitialize + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_db_deinit(esp_gmf_db_handle_t handle); + +/** + * @brief Read data from data bus + * + * @param[in] handle data bus handle + * @param[out] buffer Pointer to the buffer to store the read data + * @param[in] buf_len Length of the buffer + * @param[in] block_ticks Maximum time to wait for the read operation + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_db_read(esp_gmf_db_handle_t handle, void *buffer, int buf_len, int block_ticks); + +/** + * @brief Write data to data bus + * + * @param[in] handle data bus handle + * @param[in] buffer Pointer to the data buffer to be written + * @param[in] buf_len Length of the data buffer + * @param[in] block_ticks Maximum time to wait for the write operation + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_db_write(esp_gmf_db_handle_t handle, void *buffer, int buf_len, int block_ticks); + +/** + * @brief Acquire expected data on data bus + * The actual valid size is stored in `blk->valid_size` + * + * @param[in] handle data bus handle + * @param[out] blk Pointer to the data bus block structure + * @param[in] wanted_size Size of data to acquire + * @param[in] block_ticks Maximum time to wait for the acquire operation + * + * @return + * - > 0 The specific length of data being read + * - ESP_GMF_IO_OK Operation successful + * - ESP_GMF_IO_FAIL Operation failed + * - ESP_GMF_IO_ABORT Operation aborted + * - ESP_GMF_IO_TIMEOUT Operation timed out + */ +esp_gmf_err_io_t esp_gmf_db_acquire_read(esp_gmf_db_handle_t handle, esp_gmf_data_bus_block_t *blk, uint32_t wanted_size, int block_ticks); + +/** + * @brief Release a read operation on data bus + * + * @param[in] handle data bus handle + * @param[in] blk Pointer to the data bus block structure + * @param[in] block_ticks Maximum time to wait for the release operation + * + * @return + * - ESP_GMF_IO_OK Operation successful + * - ESP_GMF_IO_FAIL Operation failed + * - ESP_GMF_IO_ABORT Operation aborted + * - ESP_GMF_IO_TIMEOUT Operation timed out + */ +esp_gmf_err_io_t esp_gmf_db_release_read(esp_gmf_db_handle_t handle, esp_gmf_data_bus_block_t *blk, int block_ticks); + +/** + * @brief Acquire space to write operation on data bus + * + * @param[in] handle data bus handle + * @param[out] blk Pointer to the data bus block structure + * @param[in] wanted_size Size of data to acquire + * @param[in] block_ticks Maximum time to wait for the acquire operation + * + * @return + * - > 0 The specific length of space can be write + * - ESP_GMF_IO_OK Operation successful + * - ESP_GMF_IO_FAIL Operation failed + * - ESP_GMF_IO_ABORT Operation aborted + * - ESP_GMF_IO_TIMEOUT Operation timed out + */ +esp_gmf_err_io_t esp_gmf_db_acquire_write(esp_gmf_db_handle_t handle, esp_gmf_data_bus_block_t *blk, uint32_t wanted_size, int block_ticks); + +/** + * @brief Release a write operation on data bus + * + * @param[in] handle data bus handle + * @param[in] blk Pointer to the data bus block structure + * @param[in] block_ticks Maximum time to wait for the release operation + * + * @return + * - ESP_GMF_IO_OK Operation successful + * - ESP_GMF_IO_FAIL Operation failed + * - ESP_GMF_IO_ABORT Operation aborted + * - ESP_GMF_IO_TIMEOUT Operation timed out + */ +esp_gmf_err_io_t esp_gmf_db_release_write(esp_gmf_db_handle_t handle, esp_gmf_data_bus_block_t *blk, int block_ticks); + +/** + * @brief Mark a write operation as done on data bus + * + * @param[in] handle data bus handle + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_db_done_write(esp_gmf_db_handle_t handle); + +/** + * @brief Reset the status of a completed write operation on data bus + * + * @param[in] handle data bus handle + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_db_reset_done_write(esp_gmf_db_handle_t handle); + +/** + * @brief Reset data bus, clearing any pending operations and resetting status + * + * @param[in] handle data bus handle + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_db_reset(esp_gmf_db_handle_t handle); + +/** + * @brief Abort exit the data bus + * + * @param[in] handle data bus handle + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_db_abort(esp_gmf_db_handle_t handle); + +/** + * @brief Get the total size of data bus buffer + * + * @param[in] handle data bus handle + * @param[out] buff_size Pointer to store the total buffer size + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_db_get_total_size(esp_gmf_db_handle_t handle, uint32_t *buff_size); + +/** + * @brief Get the filled size of data bus buffer + * + * @param[in] handle data bus handle + * @param[out] filled_size Pointer to store the filled buffer size + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_db_get_filled_size(esp_gmf_db_handle_t handle, uint32_t *filled_size); + +/** + * @brief Get the available size in data bus buffer + * + * @param[in] handle data bus handle + * @param[out] available_size Pointer to store the available buffer size + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_db_get_available(esp_gmf_db_handle_t handle, uint32_t *available_size); + +/** + * @brief Set the writer holder for data bus + * + * @param[in] handle data bus handle + * @param[in] holder Pointer to the writer holder + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_db_set_writer(esp_gmf_db_handle_t handle, void *holder); + +/** + * @brief Get the writer holder from data bus + * + * @param[in] handle data bus handle + * @param[out] holder Pointer to store the writer holder + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_db_get_writer(esp_gmf_db_handle_t handle, void **holder); + +/** + * @brief Set the reader holder for data bus + * + * @param[in] handle data bus handle + * @param[in] holder Pointer to the reader holder + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_db_set_reader(esp_gmf_db_handle_t handle, void *holder); + +/** + * @brief Get the reader holder from data bus + * + * @param[in] handle data bus handle + * @param[out] holder Pointer to store the reader holder + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_db_get_reader(esp_gmf_db_handle_t handle, void **holder); + +/** + * @brief Get the data bus type of data bus + * + * @param[in] handle data bus handle + * @param[out] db_type Pointer to store the data bus type + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_db_get_type(esp_gmf_db_handle_t handle, esp_gmf_data_bus_type_t *db_type); + +/** + * @brief Get the name of data bus + * + * @param[in] handle data bus handle + * + * @return + * - Pointer to the name of the data bus + */ +const char *esp_gmf_db_get_name(esp_gmf_db_handle_t handle); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/data_bus/include/esp_gmf_fifo.h b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/data_bus/include/esp_gmf_fifo.h new file mode 100644 index 0000000..43f407f --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/data_bus/include/esp_gmf_fifo.h @@ -0,0 +1,221 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#include "esp_gmf_data_bus.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * @brief The GMF FIFO Buffer is an interface designed for passing buffer addresses without generating any copies. + * It maintains a list whose maximum number of items is specified when creating the FIFO handle. The FIFO buffer + * is not allocated during initialization; instead, it is allocated when `esp_gmf_fifo_acquire_write` is called. + * This interface provides blocking operations, meaning that `esp_gmf_fifo_acquire_read` and `esp_gmf_fifo_acquire_write` + * will block until a buffer becomes available or the timeout occurs. The functions `esp_gmf_fifo_release_read` and + * `esp_gmf_fifo_release_write` must be used in pairs and cannot be invoked recursively. Additionally, the GMF FIFO + * buffer is thread-safe, ensuring safe concurrent access in multi-threaded environments. + */ + +/** + * @brief Handle for the GMF FIFO + */ +typedef void *esp_gmf_fifo_handle_t; + +/** + * @brief Create a FIFO buffer for data blocks + * + * @param[in] block_cnt Number of blocks in the FIFO + * @param[in] block_size Size of each block in bytes + * @param[out] handle Pointer to the FIFO handle to be created + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_MEMORY_LACK Memory allocation failed + * - ESP_GMF_ERR_INVALID_ARG Invalid arguments + */ +esp_gmf_err_t esp_gmf_fifo_create(int block_cnt, int block_size, esp_gmf_fifo_handle_t *handle); + +/** + * @brief Set alignment for FIFO buffer + * + * @note Should call this API before `esp_gmf_fifo_acquire_write` to get aligned buffer + * + * @param[in] handle FIFO handle + * @param[in] align Alignment for FIFO buffer to get (power of 2, 0=default) + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid arguments + */ +esp_gmf_err_t esp_gmf_fifo_set_align(esp_gmf_fifo_handle_t handle, uint8_t align); + +/** + * @brief Destroy the FIFO buffer and release resources + * + * @param[in] handle FIFO handle to be destroyed + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid handle + */ +esp_gmf_err_t esp_gmf_fifo_destroy(esp_gmf_fifo_handle_t handle); + +/** + * @brief Acquire a filled block from the FIFO, if there is insufficient block and block_ticks is greater than 0, + * the function will block until enough block becomes available + * + * @note 1. The `wanted_size` parameter is not used because the acquired block comes from the filled blocks. + * 2. The obtained buffer address is internal and should not be freed externally. + * 3. The obtained blocks must be released by `esp_gmf_fifo_release_read` in pairs. + * 4. The actual valid size is stored in `blk->valid_size` + * + * @param[in] handle FIFO handle + * @param[out] blk Pointer to the data block structure to be filled + * @param[in] wanted_size Desired size to read in bytes + * @param[in] block_ticks Maximum ticks to wait if the block is not available + * + * @return + * - > 0 The specific length of data being read + * - ESP_GMF_IO_OK Operation succeeded + * - ESP_GMF_IO_FAIL Invalid arguments, or wanted_size > total size + * - ESP_GMF_IO_TIMEOUT Operation timed out + */ +esp_gmf_err_io_t esp_gmf_fifo_acquire_read(esp_gmf_fifo_handle_t handle, esp_gmf_data_bus_block_t *blk, uint32_t wanted_size, int block_ticks); + +/** + * @brief Returned acquired block address to the specific handle + * + * @note 1. The returned block MUST be acquired from `esp_gmf_block_acquire_read` to ensure proper synchronization + * 2. `esp_gmf_block_acquire_read` and `esp_gmf_block_release_read` must be called in pairs + * 3. Calling this function once makes one block available for the write operation + * + * @param[in] handle FIFO handle + * @param[in] blk Pointer to the data block structure to be released + * @param[in] block_ticks Maximum ticks to wait if necessary + * + * @return + * - ESP_GMF_IO_OK Operation succeeded + * - ESP_GMF_IO_FAIL Invalid arguments, or the buffer does not belong to the provided handle + */ +esp_gmf_err_io_t esp_gmf_fifo_release_read(esp_gmf_fifo_handle_t handle, esp_gmf_data_bus_block_t *blk, int block_ticks); + +/** + * @brief Acquire freed block to the desired size within a specific handle + * If the number of the list is reach the maximum and block_ticks is greater than 0, the function will block + * until enough space becomes available. + * + * @note 1. `esp_gmf_fifo_acquire_write` and `esp_gmf_fifo_release_write` must be called in pairs + * 2. The obtained buffer address is internal and should not be freed externally + * 3. The `wanted_size` parameter is used to specify the size of the block that can be written + * 4. If the freed block is not ready and the number of items in the list is below the maximum limit, a new node and buffer will be allocated for the list + * 5. If the obtained buffer is smaller than the `wanted_size`, the buffer will be reallocated to match the requested size + * + * @param[in] handle FIFO handle + * @param[out] blk Pointer to the data block structure to be filled + * @param[in] wanted_size Desired size to write in bytes + * @param[in] block_ticks Maximum ticks to wait if the block is not available + * + * @return + * - > 0 The specific length of space can be write + * - ESP_GMF_IO_OK Operation succeeded, or it's done to write + * - ESP_GMF_IO_FAIL Invalid arguments, or no filled data + * - ESP_GMF_IO_TIMEOUT Operation timed out + */ +esp_gmf_err_io_t esp_gmf_fifo_acquire_write(esp_gmf_fifo_handle_t handle, esp_gmf_data_bus_block_t *blk, uint32_t wanted_size, int block_ticks); + +/** + * @brief Returned a previously acquired write block + * + * @note 1. The returned buffer MUST be acquired from `esp_gmf_fifo_acquire_write` to ensure proper synchronization + * 2. `esp_gmf_fifo_acquire_write` and `esp_gmf_fifo_release_write` must be called in pairs + * 3. Calling this function once makes one block available for read operations, and the count of freed blocks will be decremented by one + * + * @param[in] handle FIFO handle + * @param[in] blk Pointer to the data block structure to be released + * @param[in] block_ticks Maximum ticks to wait if necessary + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_io_t esp_gmf_fifo_release_write(esp_gmf_fifo_handle_t handle, esp_gmf_data_bus_block_t *blk, int block_ticks); + +/** + * @brief Indicate that writing to the FIFO is complete + * + * @param[in] handle FIFO handle + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid handle + */ +esp_gmf_err_t esp_gmf_fifo_done_write(esp_gmf_fifo_handle_t handle); + +/** + * @brief Abort all operations on the FIFO + * + * @param[in] handle FIFO handle + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid handle + */ +esp_gmf_err_t esp_gmf_fifo_abort(esp_gmf_fifo_handle_t handle); + +/** + * @brief Reset the FIFO to its initial state + * + * @param[in] handle FIFO handle + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid handle + */ +esp_gmf_err_t esp_gmf_fifo_reset(esp_gmf_fifo_handle_t handle); + +/** + * @brief Get the amount of free space in the FIFO + * + * @param[in] handle FIFO handle + * @param[out] free_size Pointer to store the free size in bytes + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid arguments + */ +esp_gmf_err_t esp_gmf_fifo_get_free_size(esp_gmf_fifo_handle_t handle, uint32_t *free_size); + +/** + * @brief Get the amount of filled space in the FIFO + * + * @param[in] handle FIFO handle + * @param[out] filled_size Pointer to store the filled size in bytes + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid arguments + */ +esp_gmf_err_t esp_gmf_fifo_get_filled_size(esp_gmf_fifo_handle_t handle, uint32_t *filled_size); + +/** + * @brief Get the total capacity of the FIFO + * + * @param[in] handle FIFO handle + * @param[out] total_size Pointer to store the total size in bytes + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid arguments + */ +esp_gmf_err_t esp_gmf_fifo_get_total_size(esp_gmf_fifo_handle_t handle, uint32_t *total_size); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/data_bus/include/esp_gmf_new_databus.h b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/data_bus/include/esp_gmf_new_databus.h new file mode 100644 index 0000000..f8de752 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/data_bus/include/esp_gmf_new_databus.h @@ -0,0 +1,70 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#include "esp_gmf_data_bus.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * @brief Create a new ring buffer with the specified item count and size + * + * @param[in] num Size of each item + * @param[in] item_cnt Number of items + * @param[out] h Pointer to store the handle of the GMF data bus + * + * @return + * - 0 On success + * - < 0 Negative value if an error occurs + */ +int esp_gmf_db_new_ringbuf(int num, int item_cnt, esp_gmf_db_handle_t *h); + +/** + * @brief Create a new block buffer with the specified item count and size + * + * @param[in] num Size of each item + * @param[in] item_cnt Number of items + * @param[out] h Pointer to store the handle of the GMF data bus + * + * @return + * - 0 On success + * - < 0 Negative value if an error occurs + */ +int esp_gmf_db_new_block(int num, int item_cnt, esp_gmf_db_handle_t *h); + +/** + * @brief Create a new pointer buffer (pbuf) with the specified item count and size + * + * @param[in] num Size of each item + * @param[in] item_cnt Number of items + * @param[out] h Pointer to store the handle of the GMF data bus + * + * @return + * - 0 On success + * - < 0 Negative value if an error occurs + */ +int esp_gmf_db_new_pbuf(int num, int item_cnt, esp_gmf_db_handle_t *h); + +/** + * @brief Create a new FIFO buffer with the specified item count and size + * + * @param[in] num num Maximum number of items + * @param[in] item_cnt Reserved for future use (currently unused) + * @param[out] h Pointer to store the handle of the GMF data bus + * + * @return + * - 0 On success + * - < 0 Negative value if an error occurs + */ +int esp_gmf_db_new_fifo(int num, int item_cnt, esp_gmf_db_handle_t *h); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/data_bus/include/esp_gmf_pbuf.h b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/data_bus/include/esp_gmf_pbuf.h new file mode 100644 index 0000000..354fbfd --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/data_bus/include/esp_gmf_pbuf.h @@ -0,0 +1,201 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#include "esp_gmf_data_bus.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * @brief GMF Pointer Buffer is an interface for passing buffer addresses without generating any copies. + * esp_gmf_pbuf_acquire_read is used to retrieve a filled buffer. esp_gmf_pbuf_acquire_write indicates the + * address of an available buffer. If no available buffer exists, a request will also be made. The + * maximum number of requests is specified during initialization. The esp_gmf_pbuf_acquire_read/write and + * esp_gmf_pbuf_release_read/writer must be used in pairs and cannot be invoked recursively. + * + * Its interfaces do not possess blocking conditions, but they can be called safely in a multithreaded environment. + */ + +/** + * @brief Represents a handle to a pointer buffer + */ +typedef void *esp_gmf_pbuf_handle_t; + +/** + * @brief Creates a new pointer buffer with the specified capacity + * It simply allocates an instance without any buffer + * + * @param[in] capacity The capacity of the maximum pointer buffer + * @param[out] handle Pointer to store the handle to the created pointer buffer + * + * @return + * - ESP_GMF_ERR_OK Operation successful + * - ESP_GMF_ERR_INVALID_ARG Invalid argument provided + * - ESP_GMF_ERR_MEMORY_LACK Memory allocation failed + */ +esp_gmf_err_t esp_gmf_pbuf_create(int capacity, esp_gmf_pbuf_handle_t *handle); + +/** + * @brief Destroy a pointer buffer and release all associated resources + * + * @param[in] handle Handle to the pointer buffer to be destroyed + * + * @return + * - ESP_GMF_ERR_OK Operation successful + * - ESP_GMF_ERR_INVALID_ARG Invalid argument provided + */ +esp_gmf_err_t esp_gmf_pbuf_destroy(esp_gmf_pbuf_handle_t handle); + +/** + * @brief Acquire valid data from the pointer buffer for reading, it consistently retrieves the filled header data + * + * @note 1. If called before any write operation, it will return ESP_GMF_IO_FAIL + * 2. `esp_gmf_pbuf_acquire_read` and `esp_gmf_pbuf_release_read` must be called in pairs, and consecutive acquisition + * followed by consecutive release is not allowed + * 3. The actual valid size is stored in `blk->valid_size` + * + * @param[in] handle Handle to the pointer buffer + * @param[out] blk Pointer to the data bus block structure to store the acquired data + * @param[in] wanted_size Desired size of data to be acquired + * @param[in] block_ticks Ticks to wait for the operation + * + * @return + * - > 0 The specific length of data being read + * - ESP_GMF_IO_OK Operation succeeded + * - ESP_GMF_IO_FAIL Invalid arguments, or no filled data + */ +esp_gmf_err_io_t esp_gmf_pbuf_acquire_read(esp_gmf_pbuf_handle_t handle, esp_gmf_data_bus_block_t *blk, uint32_t wanted_size, int block_ticks); + +/** + * @brief Returned acquired buffer to the specific pointer buffer + * + * @note 1. The returned buffer MUST be acquired from `esp_gmf_pbuf_acquire_read` to ensure proper synchronization + * 2. `esp_gmf_pbuf_acquire_read` and `esp_gmf_pbuf_release_read` must be called in pairs, and consecutive acquisition + * followed by consecutive release is not allowed + * + * @param[in] handle Handle to the pointer buffer + * @param[in] blk Pointer to the data bus block structure containing the data to be released + * @param[in] block_ticks Ticks to wait for the operation + * + * @return + * - ESP_GMF_IO_OK Operation succeeded + * - ESP_GMF_IO_FAIL Invalid arguments, or the buffer does not belong to the provided handle + */ +esp_gmf_err_io_t esp_gmf_pbuf_release_read(esp_gmf_pbuf_handle_t handle, esp_gmf_data_bus_block_t *blk, int block_ticks); + +/** + * @brief Acquire space corresponding to the desired size within a specific handle + * The first call requests the desired buffer, or it does so when no buffer is available. + * + * @note 1. The allocated number of buffer is greater than given capacity, it will return ESP_GMF_IO_FAIL + * 2. `esp_gmf_pbuf_acquire_write` and `esp_gmf_pbuf_release_write` must be called in pairs, and consecutive acquisition + * followed by consecutive release is not allowed + * + * @param[in] handle Handle to the pointer buffer + * @param[out] blk Pointer to the data bus block structure to store the acquired space + * @param[in] wanted_size Desired size of space to be acquired + * @param[in] block_ticks Ticks to wait for the operation + * + * @return + * - > 0 The specific length of space can be write + * - ESP_GMF_IO_OK Operation succeeded, or it's done to write + * - ESP_GMF_IO_FAIL Invalid arguments, or no filled data + */ +esp_gmf_err_io_t esp_gmf_pbuf_acquire_write(esp_gmf_pbuf_handle_t handle, esp_gmf_data_bus_block_t *blk, uint32_t wanted_size, int block_ticks); + +/** + * @brief Store the given buffer to filled list by the specific pointer buffer + * + * @note 1. The stored buffer MUST be acquired from `esp_gmf_pbuf_acquire_write` to ensure proper synchronization + * 2. `esp_gmf_pbuf_acquire_write` and `esp_gmf_pbuf_release_write` must be called in pairs, and consecutive acquisition + * followed by consecutive release is not allowed + * + * @param[in] handle Handle to the pointer buffer + * @param[in] blk Pointer to the data bus block structure containing the space to be released + * @param[in] block_ticks Ticks to wait for the operation + * + * @return + * - ESP_GMF_IO_OK Operation succeeded + * - ESP_GMF_IO_FAIL Invalid arguments, or the buffer does not belong to the provided handle + */ +esp_gmf_err_io_t esp_gmf_pbuf_release_write(esp_gmf_pbuf_handle_t handle, esp_gmf_data_bus_block_t *blk, int block_ticks); + +/** + * @brief Set the status of writing to the pointer buffer as done + * + * @param[in] handle Handle to the pointer buffer + * + * @return + * - ESP_GMF_ERR_OK Operation successful + * - ESP_GMF_ERR_INVALID_ARG Invalid argument provided + */ +esp_gmf_err_t esp_gmf_pbuf_done_write(esp_gmf_pbuf_handle_t handle); + +/** + * @brief Abort waiting until there is space for reading or writing of the pointer buffer + * + * @param[in] handle Handle to the pointer buffer + * + * @return + * - ESP_GMF_ERR_OK Operation successful + * - ESP_GMF_ERR_INVALID_ARG Invalid argument provided + */ +esp_gmf_err_t esp_gmf_pbuf_abort(esp_gmf_pbuf_handle_t handle); + +/** + * @brief Reset the pointer buffer, clearing all values to initial state + * + * @param[in] handle Handle to the pointer buffer + * + * @return + * - ESP_GMF_ERR_OK Operation successful + * - ESP_GMF_ERR_INVALID_ARG Invalid argument provided + */ +esp_gmf_err_t esp_gmf_pbuf_reset(esp_gmf_pbuf_handle_t handle); + +/** + * @brief Get the total free size of the pointer buffer + * + * @param[in] handle Handle to the pointer buffer + * @param[out] free_size Pointer to store the total free size + * + * @return + * - ESP_GMF_ERR_OK Operation successful + * - ESP_GMF_ERR_INVALID_ARG Invalid argument provided + */ +esp_gmf_err_t esp_gmf_pbuf_get_free_size(esp_gmf_pbuf_handle_t handle, uint32_t *free_size); + +/** + * @brief Get the number of bytes that have filled the pointer buffer + * + * @param[in] handle Handle to the pointer buffer + * @param[out] filled_size Pointer to store the filled size + * + * @return + * - ESP_GMF_ERR_OK Operation successful + * - ESP_GMF_ERR_INVALID_ARG Invalid argument provided + */ +esp_gmf_err_t esp_gmf_pbuf_get_filled_size(esp_gmf_pbuf_handle_t handle, uint32_t *filled_size); + +/** + * @brief Get the total size of the pointer buffer (in bytes) + * + * @param[in] handle Handle to the pointer buffer + * @param[out] total_size Pointer to store the total size + * + * @return + * - ESP_GMF_ERR_OK Operation successful + * - ESP_GMF_ERR_INVALID_ARG Invalid argument provided + */ +esp_gmf_err_t esp_gmf_pbuf_get_total_size(esp_gmf_pbuf_handle_t handle, uint32_t *total_size); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/data_bus/include/esp_gmf_ringbuffer.h b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/data_bus/include/esp_gmf_ringbuffer.h new file mode 100644 index 0000000..e8b8272 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/data_bus/include/esp_gmf_ringbuffer.h @@ -0,0 +1,217 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#include "esp_gmf_data_bus.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * @brief GMF ringbuffer represents a ring buffer. The buffer size is determined by + * multiplying the block size with the number of blocks.Read and write operations + * occur on a byte-by-byte basis and do not offer direct access to the internal + * buffer address. Its access is thread-safe. The 'esp_gmf_rb_acquire_read' and + * 'esp_gmf_rb_release_write' perform read and write operations as copies, respectively. + * `esp_gmf_rb_release_read` and `esp_gmf_rb_acquire_write` have no practical significance. + * + * The `esp_gmf_rb_release_write` and `esp_gmf_rb_acquire_read` have blocking functionality. + */ + +/** + * @brief Handle to the ring buffer + */ +typedef void *esp_gmf_rb_handle_t; + +/** + * @brief Create a ring buffer with total size = block_size * n_blocks + * + * @param[in] block_size Size of each block + * @param[in] n_blocks Number of blocks + * @param[out] handle Pointer to store the handle to the created ringbufer buffer + * + * @return + * - ESP_GMF_ERR_OK Operation successful + * - ESP_GMF_ERR_INVALID_ARG Invalid argument provided + */ +esp_gmf_err_t esp_gmf_rb_create(int block_size, int n_blocks, esp_gmf_rb_handle_t *handle); + +/** + * @brief Cleanup and free all memory allocated for the ring buffer + * + * @param[in] handle The Ringbuffer handle + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid arguments + */ +esp_gmf_err_t esp_gmf_rb_destroy(esp_gmf_rb_handle_t handle); + +/** + * @brief Reset the ring buffer, clearing all values to the initial state + * + * @param[in] handle The Ringbuffer handle + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid arguments + */ +esp_gmf_err_t esp_gmf_rb_reset(esp_gmf_rb_handle_t handle); + +/** + * @brief Copy valid data from ring buffer to the given buffer with specific handle + * When the ring buffer handle cannot provide enough size, it will block for the duration specified by block_ticks. + * The actual valid size is stored in `blk->valid_size` + * + * @param[in] handle The Ringbuffer handle + * @param[out] blk Pointer to the data block structure to be filled + * @param[in] wanted_size Desired size to read + * @param[in] ticks_to_wait Maximum duration to wait for the operation to complete + * + * @return + * - > 0 The specific length of data being read + * - ESP_GMF_IO_OK Operation succeeded + * - ESP_GMF_IO_FAIL Invalid arguments + * - ESP_GMF_IO_TIMEOUT Operation timed out + * - ESP_GMF_IO_ABORT Operation aborted + */ +esp_gmf_err_io_t esp_gmf_rb_acquire_read(esp_gmf_rb_handle_t handle, esp_gmf_data_bus_block_t *blk, uint32_t wanted_size, int ticks_to_wait); + +/** + * @brief Release the read operation + * + * @note It's do nothing due to read acquire is a copy operation + * + * @param[in] handle The Ringbuffer handle + * @param[in] blk Pointer to the data block structure to release + * @param[in] block_ticks Maximum duration to wait for the operation to complete + * + * @return + * - ESP_GMF_IO_OK Operation succeeded + */ +esp_gmf_err_io_t esp_gmf_rb_release_read(esp_gmf_rb_handle_t handle, esp_gmf_data_bus_block_t *blk, int block_ticks); + +/** + * @brief Acquire space for write + * + * @note It's do nothing due to write acquire is a copy operation + * + * @param[in] handle The Ringbuffer handle + * @param[out] blk Pointer to the data block structure to be filled + * @param[in] wanted_size Desired size to write + * @param[in] ticks_to_wait Maximum duration to wait for the operation to complete + * + * @return + * - > 0 The specific length of space can be write + * - ESP_GMF_IO_OK Operation succeeded + * - ESP_GMF_IO_FAIL Invalid arguments + * - ESP_GMF_IO_TIMEOUT Operation timed out + * - ESP_GMF_IO_ABORT Operation aborted + */ +esp_gmf_err_io_t esp_gmf_rb_acquire_write(esp_gmf_rb_handle_t handle, esp_gmf_data_bus_block_t *blk, uint32_t wanted_size, int ticks_to_wait); + +/** + * @brief Copy the given buffer to the ring buffer with specific handle + * When the ring buffer handle cannot accommodate the given buffer size, it will block for the duration specified by block_ticks. + * + * @param[in] handle The Ringbuffer handle + * @param[in] blk Pointer to the data block structure to release + * @param[in] block_ticks Maximum duration to wait for the operation to complete + * + * @return + * - ESP_GMF_IO_OK Operation succeeded + * - ESP_GMF_IO_FAIL Invalid arguments + * - ESP_GMF_IO_TIMEOUT Operation timed out + * - ESP_GMF_IO_ABORT Operation aborted + */ +esp_gmf_err_io_t esp_gmf_rb_release_write(esp_gmf_rb_handle_t handle, esp_gmf_data_bus_block_t *blk, int block_ticks); + +/** + * @brief Abort any pending operations on the ring buffer + * + * @param[in] handle The Ringbuffer handle + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid arguments + */ +esp_gmf_err_t esp_gmf_rb_abort(esp_gmf_rb_handle_t handle); + +/** + * @brief Set the status of writing to the ring buffer as done + * + * @param[in] handle The Ringbuffer handle + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid arguments + */ +esp_gmf_err_t esp_gmf_rb_done_write(esp_gmf_rb_handle_t handle); + +/** + * @brief Reset the status of writing to the ring buffer as not done + * + * @param[in] handle The Ringbuffer handle + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid arguments + */ +esp_gmf_err_t esp_gmf_rb_reset_done_write(esp_gmf_rb_handle_t handle); + +/** + * @brief Unblock the reader of the ring buffer + * + * @param[in] handle The Ringbuffer handle + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid arguments + */ +esp_gmf_err_t esp_gmf_rb_unblock_reader(esp_gmf_rb_handle_t handle); + +/** + * @brief Get the number of bytes available for reading from the ring buffer + * + * @param[in] handle The Ringbuffer handle + * @param[out] available_size Pointer to store the available size + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid arguments + */ +esp_gmf_err_t esp_gmf_rb_bytes_available(esp_gmf_rb_handle_t handle, uint32_t *available_size); + +/** + * @brief Get the number of bytes filled in the ring buffer + * + * @param[in] handle The Ringbuffer handle + * @param[out] filled_size Pointer to store the filled size + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid arguments + */ +esp_gmf_err_t esp_gmf_rb_bytes_filled(esp_gmf_rb_handle_t handle, uint32_t *filled_size); + +/** + * @brief Get the total size of the ring buffer + * + * @param[in] handle The Ringbuffer handle + * @param[out] valid_size Pointer to store the total size + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid arguments + */ +esp_gmf_err_t esp_gmf_rb_get_size(esp_gmf_rb_handle_t handle, uint32_t *valid_size); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/docs/Doxyfile b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/docs/Doxyfile new file mode 100644 index 0000000..eb24044 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/docs/Doxyfile @@ -0,0 +1,68 @@ +# This is Doxygen configuration file +# +# Doxygen provides over 260 configuration statements +# To make this file easier to follow, +# it contains only statements that are non-default +# +# NOTE: +# It is recommended not to change defaults unless specifically required +# Test any changes how they affect generated documentation +# Make sure that correct warnings are generated to flag issues with documented code +# +# For the complete list of configuration statements see: +# https://www.stack.nl/~dimitri/doxygen/manual/config.html + + +PROJECT_NAME = "Espressif Audio Development Framework (ESP-GMF-Core)" + +## The 'INPUT' statement below is used as input by script 'gen-df-input.py' +## to automatically generate API reference list files heder_file.inc +## These files are placed in '_inc' directory +## and used to include in API reference documentation + +INPUT = ./include ./data_bus/include ./oal/include + + + +## Get warnings for functions that have no documentation for their parameters or return value +## +WARN_NO_PARAMDOC = YES + +## Enable preprocessing and remove __attribute__(...) expressions from the INPUT files +## +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = YES +EXPAND_ONLY_PREDEF = YES +PREDEFINED = \ + __attribute__(x)= \ + IRAM_ATTR= \ + configSUPPORT_DYNAMIC_ALLOCATION=1 \ + configSUPPORT_STATIC_ALLOCATION=1 \ + configQUEUE_REGISTRY_SIZE=1 \ + configUSE_RECURSIVE_MUTEXES=1 \ + configTHREAD_LOCAL_STORAGE_DELETE_CALLBACKS=1 \ + configNUM_THREAD_LOCAL_STORAGE_POINTERS=1 \ + configUSE_APPLICATION_TASK_TAG=1 + +## Do not complain about not having dot +## +HAVE_DOT = NO + +## Generate XML that is required for Breathe +## +GENERATE_XML = YES +XML_OUTPUT = xml + +GENERATE_HTML = NO +HAVE_DOT = NO +GENERATE_LATEX = NO +GENERATE_MAN = YES +GENERATE_RTF = NO + +## Skip distracting progress messages +## +QUIET = YES +## Log warnings in a file for further review +## +WARN_LOGFILE = "doxygen-warning-log.txt" + diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/helpers/esp_gmf_uri_parser.c b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/helpers/esp_gmf_uri_parser.c new file mode 100644 index 0000000..689d56c --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/helpers/esp_gmf_uri_parser.c @@ -0,0 +1,132 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#include +#include +#include +#include "esp_gmf_uri_parser.h" + +void esp_gmf_uri_free(esp_gmf_uri_t *uri) +{ + if (uri->scheme) { + free(uri->scheme); + } + if (uri->userinfo) { + free(uri->userinfo); + } + if (uri->username) { + free(uri->username); + } + if (uri->password) { + free(uri->password); + } + if (uri->host) { + free(uri->host); + } + if (uri->path) { + free(uri->path); + } + if (uri->query) { + free(uri->query); + } + if (uri->fragment) { + free(uri->fragment); + } + free(uri); +} + +int esp_gmf_uri_parse(const char *uri_str, esp_gmf_uri_t **uri_out) +{ + if (!uri_str || !uri_out) { + printf("Invalid parameters, str:%p, out:%p\r\n", uri_str, uri_out); + return -1; + } + const size_t len = strlen(uri_str); + if (len == 0) { + return -1; + } + + esp_gmf_uri_t *uri = calloc(1, sizeof(esp_gmf_uri_t)); + if (!uri) { + return -1; + } + + const char *curr = uri_str; + + // Parsing scheme + const char *scheme_end = strstr(curr, "://"); + if (!scheme_end) { + esp_gmf_uri_free(uri); + return -1; + } + size_t scheme_len = scheme_end - curr; + uri->scheme = strndup(curr, scheme_len); + curr = scheme_end + 3; // Move past "://" + + // Parsing userinfo (if present) + const char *host_start = strchr(curr, '@'); + if (host_start) { + size_t userinfo_len = host_start - curr; + uri->userinfo = strndup(curr, userinfo_len); + curr = host_start + 1; + + // Split userinfo into username and password + const char *password_sep = strchr(uri->userinfo, ':'); + if (password_sep) { + size_t username_len = password_sep - uri->userinfo; + uri->username = strndup(uri->userinfo, username_len); + uri->password = strndup(password_sep + 1, userinfo_len - username_len - 1); + } else { + uri->username = strdup(uri->userinfo); + } + } + // Parsing host + const char *port_start = strchr(curr, ':'); + const char *path_start = strchr(curr, '/'); + const char *host_end = path_start ? path_start : curr + strlen(curr); + + if (port_start && port_start < host_end) { + uri->host = strndup(curr, port_start - curr); + uri->port = atoi(port_start + 1); + curr = port_start + 1; + } else { + uri->host = strndup(curr, host_end - curr); + } + if (path_start) { + curr = path_start; + // Parsing path + const char *query_start = strchr(curr, '?'); + const char *fragment_start = strchr(curr, '#'); + + if (query_start) { + uri->path = strndup(curr, query_start - curr); + curr = query_start + 1; + } else if (fragment_start) { + uri->path = strndup(curr, fragment_start - curr); + curr = fragment_start; + } else { + uri->path = strdup(curr); + curr += strlen(curr); + } + // Parsing query + if (query_start) { + if (fragment_start) { + uri->query = strndup(query_start + 1, fragment_start - query_start - 1); + curr = fragment_start; + } else { + uri->query = strdup(query_start + 1); + curr += strlen(curr); + } + } + // Parsing fragment + if (fragment_start) { + uri->fragment = strdup(fragment_start + 1); + } + } + *uri_out = uri; + return 0; +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/helpers/fourcc_check.py b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/helpers/fourcc_check.py new file mode 100644 index 0000000..911eeb8 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/helpers/fourcc_check.py @@ -0,0 +1,62 @@ +import re +from collections import Counter +import sys + +def extract_fourcc_definitions(file_path): + """ + Extract FOURCC characters from a header file. + Args: + file_path (str): Path to the header file. + Returns: + list: A list of tuples containing FOURCC strings and their corresponding lines. + """ + fourcc_pattern = re.compile(r"ESP_FOURCC_TO_INT\('(.{1})', '(.{1})', '(.{1})', '(.{1})'\)") + fourcc_list = [] + + with open(file_path, 'r') as f: + for line_number, line in enumerate(f, 1): + match = fourcc_pattern.search(line) + if match: + fourcc = ''.join(match.groups()) # Combine the matched characters + fourcc_list.append((fourcc, line_number, line.strip())) + + return fourcc_list + +def check_duplicates(fourcc_list): + """ + Check for duplicates in the FOURCC list. + Args: + fourcc_list (list): A list of tuples containing FOURCC strings and their corresponding lines. + Returns: + dict: A dictionary with duplicate FOURCC strings and their occurrences (line numbers and lines). + """ + counter = Counter([fourcc for fourcc, _, _ in fourcc_list]) + duplicates = { + fourcc: [(line_number, line) for fcc, line_number, line in fourcc_list if fcc == fourcc] + for fourcc, count in counter.items() if count > 1 + } + return duplicates + +if __name__ == '__main__': + if len(sys.argv) != 2: + print('Usage: python fourcc_check.py ') + sys.exit(1) + header_file_path = sys.argv[1] + try: + fourcc_list = extract_fourcc_definitions(header_file_path) + duplicates = check_duplicates(fourcc_list) + + if duplicates: + print('Duplicate FOURCC definitions found:') + for fourcc, occurrences in duplicates.items(): + print(f'{fourcc}: {len(occurrences)} times') + for line_number, line in occurrences: + print(f' Line {line_number}: {line}') + else: + print('No duplicate FOURCC definitions found.') + except FileNotFoundError: + print(f'Error: File not found: {header_file_path}') + sys.exit(1) + except Exception as e: + print(f'An error occurred: {e}') + sys.exit(1) diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/helpers/include/esp_fourcc.h b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/helpers/include/esp_fourcc.h new file mode 100644 index 0000000..730180f --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/helpers/include/esp_fourcc.h @@ -0,0 +1,802 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#include + +#define ESP_FOURCC_VERSION ("v1.0.1") + +/** +# Changelog + +## [v1.0.1] + - Fixed incorrect bit shift in the macro converting FourCC code to a string + +## [v1.0.0] + - Add audio, video and pixel format with fourcc(four character code) +*/ + +typedef uint32_t esp_fourcc_t; // 32-bit FOURCC code + +#define ESP_FOURCC_TO_INT(a, b, c, d) ((uint32_t)(a) | ((uint32_t)(b) << 8) | ((uint32_t)(c) << 16) | ((uint32_t)(d) << 24)) + +// Convert 32-bit FOURCC to string +static inline void gmf_fourcc_to_str(uint32_t fourcc, char out[5]) { + for (int i = 0; i < 4; i++) { + out[i] = (char)((fourcc >> (i * 8)) & 0xFF); + } + out[4] = '\0'; +} + +// Macro to convert an FOURCC code to a string +#define ESP_FOURCC_TO_STR(fourcc) ({ \ + static char fourcc_str[5]; \ + fourcc_str[0] = fourcc_str[1] = fourcc_str[2] = fourcc_str[3] = fourcc_str[4] = '\0'; \ + gmf_fourcc_to_str(fourcc, fourcc_str); \ + fourcc_str; \ +}) + +/***************************************************************/ +/* Video codec */ +/***************************************************************/ +#define ESP_FOURCC_H263 ESP_FOURCC_TO_INT('H', '2', '6', '3') /* H263 */ +#define ESP_FOURCC_H264 ESP_FOURCC_TO_INT('H', '2', '6', '4') /* H264 with start code */ +#define ESP_FOURCC_AVC1 ESP_FOURCC_TO_INT('A', 'V', 'C', '1') /* H264 without start code */ +#define ESP_FOURCC_H265 ESP_FOURCC_TO_INT('H', '2', '6', '5') /* HEVC */ +#define ESP_FOURCC_VP8 ESP_FOURCC_TO_INT('V', 'P', '8', ' ') /* VP8 */ +#define ESP_FOURCC_MJPG ESP_FOURCC_TO_INT('M', 'J', 'P', 'G') /* Motion-JPEG */ + + +/***************************************************************/ +/* Container */ +/***************************************************************/ +#define ESP_FOURCC_WAV ESP_FOURCC_TO_INT('W', 'A', 'V', ' ') /* WAV container */ +#define ESP_FOURCC_MP4 ESP_FOURCC_TO_INT('M', 'P', '4', ' ') /* MPEG-4 container */ +#define ESP_FOURCC_M2TS ESP_FOURCC_TO_INT('M', '2', 'T', 'S') /* MPEG-2 Transport Stream */ +#define ESP_FOURCC_FLV ESP_FOURCC_TO_INT('F', 'L', 'V', '1') /* Flash Video */ +#define ESP_FOURCC_AVI ESP_FOURCC_TO_INT('A', 'V', 'I', ' ') /* Audio Video Interleave */ +#define ESP_FOURCC_OGG ESP_FOURCC_TO_INT('O', 'G', 'G', ' ') /* Ogg container */ +#define ESP_FOURCC_WEBM ESP_FOURCC_TO_INT('W', 'E', 'B', 'M') /* WebM container */ + + +/***************************************************************/ +/* Image codec */ +/***************************************************************/ +#define ESP_FOURCC_PNG ESP_FOURCC_TO_INT('P', 'N', 'G', ' ') /* Portable Network Graphics */ +#define ESP_FOURCC_JPEG ESP_FOURCC_TO_INT('J', 'P', 'E', 'G') /* JPEG File Interchange Format (JFIF) */ +#define ESP_FOURCC_GIF ESP_FOURCC_TO_INT('G', 'I', 'F', ' ') /* Graphics Interchange Format */ +#define ESP_FOURCC_WEBP ESP_FOURCC_TO_INT('W', 'E', 'B', 'P') /* WebP */ +#define ESP_FOURCC_BMP ESP_FOURCC_TO_INT('B', 'M', 'P', ' ') /* Bitmap */ + +/***************************************************************/ +/* Audio codec */ +/***************************************************************/ +#define ESP_FOURCC_MP2 ESP_FOURCC_TO_INT('M', 'P', '2', ' ') /* MPEG Layer II */ +#define ESP_FOURCC_MP3 ESP_FOURCC_TO_INT('M', 'P', '3', ' ') /* MPEG Layer III */ +#define ESP_FOURCC_AAC ESP_FOURCC_TO_INT('A', 'A', 'C', ' ') /* Advanced Audio Codec */ +#define ESP_FOURCC_FLAC ESP_FOURCC_TO_INT('F', 'L', 'A', 'C') /* Free Lossless Audio Codec */ +#define ESP_FOURCC_M4A ESP_FOURCC_TO_INT('M', '4', 'A', 'A') /* MPEG-4 Audio */ +#define ESP_FOURCC_VORBIS ESP_FOURCC_TO_INT('V', 'O', 'B', 'S') /* Vorbis */ +#define ESP_FOURCC_AMRNB ESP_FOURCC_TO_INT('A', 'M', 'R', 'N') /* Adaptive Multi-Rate Narrowband */ +#define ESP_FOURCC_AMRWB ESP_FOURCC_TO_INT('A', 'M', 'R', 'W') /* Adaptive Multi-Rate Wideband */ +#define ESP_FOURCC_ALAC ESP_FOURCC_TO_INT('A', 'L', 'A', 'C') /* Apple Lossless Audio Codec */ +#define ESP_FOURCC_ALAW ESP_FOURCC_TO_INT('A', 'L', 'A', 'W') /* G.711 Audio Compressed */ +#define ESP_FOURCC_ULAW ESP_FOURCC_TO_INT('U', 'L', 'A', 'W') /* G.711 Audio Compressed */ +#define ESP_FOURCC_ADPCM ESP_FOURCC_TO_INT('A', 'D', 'P', 'C') /* IMA-ADPCM Audio */ +#define ESP_FOURCC_PCM_U08 ESP_FOURCC_TO_INT('U', '0', '8', 'L') /* 8-bit PCM Audio */ +#define ESP_FOURCC_PCM_S16 ESP_FOURCC_TO_INT('S', '1', '6', 'L') /* 16-bit PCM Audio little endian */ +#define ESP_FOURCC_PCM_S24 ESP_FOURCC_TO_INT('S', '2', '4', 'L') /* 24-bit PCM Audio little endian */ +#define ESP_FOURCC_PCM_S32 ESP_FOURCC_TO_INT('S', '3', '2', 'L') /* 32-bit PCM Audio little endian */ +#define ESP_FOURCC_LYRA ESP_FOURCC_TO_INT('L', 'Y', 'R', 'A') /* Lyra Audio */ +#define ESP_FOURCC_LC3 ESP_FOURCC_TO_INT('L', 'C', '3', '0') /* Low Complexity Communication Codec */ +#define ESP_FOURCC_SBC ESP_FOURCC_TO_INT('S', 'B', 'C', ' ') /* Sub-Band Codec */ +#define ESP_FOURCC_OPUS ESP_FOURCC_TO_INT('O', 'P', 'U', 'S') /* Raw Opus */ +#define ESP_FOURCC_SPEEX ESP_FOURCC_TO_INT('S', 'P', 'E', 'X') /* Speex Audio */ +#define ESP_FOURCC_PCM ESP_FOURCC_TO_INT('P', 'C', 'M', ' ') /* PCM Audio Type*/ + + +/***************************************************************/ +/* RGB Pixel formats */ +/***************************************************************/ + +/** + * RGB555 + * Memory Layout: + * | bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0 | + * Byte 1: | 0 R4 R3 R2 R1 R0 | G4 G3 | + * Byte 0: | G2 G1 G0 | B4 B3 B2 B1 B0 | + */ +#define ESP_FOURCC_RGB15 ESP_FOURCC_TO_INT('R', 'G', '1', '5') /* 16 bpp(bits per pixel), RGB-5-5-5 */ + +/** + * BGR555 + * Memory Layout: + * | bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0 | + * Byte 1: | 0 B4 B3 B2 B1 B0 | G4 G3 | + * Byte 0: | G2 G1 G0 | R4 R3 R2 R1 R0 | + */ +#define ESP_FOURCC_BGR15 ESP_FOURCC_TO_INT('B', 'G', '1', '5') /* 16 bpp BGR-5-5-5 */ + +/** + * RGB565 + * Memory Layout: + * | bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0 | + * Byte 1: | R4 R3 R2 R1 R0 | G5 G4 G3 | + * Byte 0: | G2 G1 G0 | B4 B3 B2 B1 B0 | + */ +#define ESP_FOURCC_RGB16 ESP_FOURCC_TO_INT('R', 'G', 'B', 'L') /* 16 bpp RGB-5-6-5, little endian */ + +/** + * BGR565 + * Memory Layout: + * | bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0 | + * Byte 1: | B4 B3 B2 B1 B0 | G5 G4 G3 | + * Byte 0: | G2 G1 G0 | R4 R3 R2 R1 R0 | + */ +#define ESP_FOURCC_BGR16 ESP_FOURCC_TO_INT('B', 'G', 'R', 'L') /* 16 bpp BGR-5-6-5, little endian */ + +/** + * RGB565_BE + * Memory Layout: + * | bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0 | + * Byte 1: | G2 G1 G0 | B4 B3 B2 B1 B0 | + * Byte 0: | R4 R3 R2 R1 R0 | G5 G4 G3 | + */ +#define ESP_FOURCC_RGB16_BE ESP_FOURCC_TO_INT('R', 'G', 'B', 'B') /* 16 bpp RGB-5-6-5, big endian */ + +/** + * BGR565_BE + * Memory Layout: + * | bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0 | + * Byte 1: | G2 G1 G0 | R4 R3 R2 R1 R0 | + * Byte 0: | B4 B3 B2 B1 B0 | G5 G4 G3 | + */ +#define ESP_FOURCC_BGR16_BE ESP_FOURCC_TO_INT('B', 'G', 'R', 'B') /* 16 bpp BGR-5-6-5, big endian */ + +/** + * RGB24 + * Memory Layout: + * | bit7 - bit0 | + * Byte 2: | B7 - B0 | + * Byte 1: | G7 - G0 | + * Byte 0: | R7 - R0 | + */ +#define ESP_FOURCC_RGB24 ESP_FOURCC_TO_INT('R', 'G', 'B', '3') /* 24 bpp RGB-8-8-8 */ + +/** + * BGR24 + * Memory Layout: + * | bit7 - bit0 | + * Byte 2: | R7 - R0 | + * Byte 1: | G7 - G0 | + * Byte 0: | B7 - B0 | + */ +#define ESP_FOURCC_BGR24 ESP_FOURCC_TO_INT('B', 'G', 'R', '3') /* 24 bpp BGR-8-8-8 */ + +/** + * RGBA32 + * Memory Layout: + * | bit7 - bit0 | + * Byte 3: | A7 - A0 | + * Byte 2: | B7 - B0 | + * Byte 1: | G7 - G0 | + * Byte 0: | R7 - R0 | + */ +#define ESP_FOURCC_RGBA32 ESP_FOURCC_TO_INT('R', 'A', '2', '4') /* 32 bpp RGBA-8-8-8-8 */ + +/** + * RGBX32 + * Memory Layout: + * | bit7 - bit0 | + * Byte 3: | X7 - X0 | + * Byte 2: | B7 - B0 | + * Byte 1: | G7 - G0 | + * Byte 0: | R7 - R0 | + */ +#define ESP_FOURCC_RGBX32 ESP_FOURCC_TO_INT('R', 'X', '2', '4') /* 32 bpp RGBX-8-8-8-8 */ + +/** + * ARGB32 + * Memory Layout: + * | bit7 - bit0 | + * Byte 3: | B7 - B0 | + * Byte 2: | G7 - G0 | + * Byte 1: | R7 - R0 | +* Byte 0: | A7 - A0 | + */ +#define ESP_FOURCC_ARGB32 ESP_FOURCC_TO_INT('A', 'B', '2', '4') /* 32 bpp ARGB-8-8-8-8 */ + +/** + * XRGB32 + * Memory Layout: + * | bit7 - bit0 | + * Byte 3: | B7 - B0 | + * Byte 2: | G7 - G0 | + * Byte 1: | R7 - R0 | +* Byte 0: | X7 - X0 | + */ +#define ESP_FOURCC_XRGB32 ESP_FOURCC_TO_INT('X', 'B', '2', '4') /* 32 bpp XRGB-8-8-8-8 */ + +/** + * ABGR32 + * Memory Layout: + * | bit7 - bit0 | + * Byte 3: | R7 - R0 | + * Byte 2: | G7 - G0 | + * Byte 1: | B7 - B0 | + * Byte 0: | A7 - A0 | + */ +#define ESP_FOURCC_ABGR32 ESP_FOURCC_TO_INT('A', 'R', '2', '4') /* 32 bpp ABGR-8-8-8-8 */ + +/** + * XBGR32 + * Memory Layout: + * | bit7 - bit0 | + * Byte 3: | R7 - R0 | + * Byte 2: | G7 - G0 | + * Byte 1: | B7 - B0 | + * Byte 0: | X7 - X0 | + */ +#define ESP_FOURCC_XBGR32 ESP_FOURCC_TO_INT('X', 'R', '2', '4') /* 32 bpp XBGR-8-8-8-8 */ + +/** + * BGRA32 + * Memory Layout: + * | bit7 - bit0 | + * Byte 3: | A7 - A0 | + * Byte 2: | R7 - R0 | + * Byte 1: | G7 - G0 | + * Byte 0: | B7 - B0 | + */ +#define ESP_FOURCC_BGRA32 ESP_FOURCC_TO_INT('B', 'A', '2', '4') /* 32 bpp BGRA-8-8-8-8 */ + +/** + * BGRX32 + * Memory Layout: + * | bit7 - bit0 | + * Byte 3: | X7 - X0 | + * Byte 2: | R7 - R0 | + * Byte 1: | G7 - G0 | + * Byte 0: | B7 - B0 | + */ +#define ESP_FOURCC_BGRX32 ESP_FOURCC_TO_INT('B', 'X', '2', '4') /* 32 bpp BGRX-8-8-8-8 */ + + +/***************************************************************/ +/* Grey Pixel formats */ +/***************************************************************/ +/** + * Grey8 + * Memory Layout: + * | bit7 - bit0 | + * Byte 0: | G7 - G0 | + */ +#define ESP_FOURCC_GREY ESP_FOURCC_TO_INT('G', 'R', 'E', 'Y') /* 8 bpp Greyscale */ + +/** + * Grey16 + * Memory Layout: + * | bit7 - bit0 | + * Byte 1: | G15 - G8 | + * Byte 0: | G7 - G0 | + */ +#define ESP_FOURCC_Y16 ESP_FOURCC_TO_INT('Y', '1', '6', 'L') /* 16 bpp Greyscale */ + +/** + * Grey16_BE + * Memory Layout: + * | bit7 - bit0 | + * Byte 1: | G7 - G0 | + * Byte 0: | G15 - G8 | + */ +#define ESP_FOURCC_Y16_BE ESP_FOURCC_TO_INT('Y', '1', '6', 'B') /* 16 bpp Greyscale, big endian */ + + +/***************************************************************/ +/* YCbCr-YUV packed format */ +/***************************************************************/ +/** + * YUYV422 + * Memory Layout: + * +--+--+--+--+ +--+--+--+--+ + * |Y0|U0|Y1|V0| |Y2|U2|Y3|V2| + * +--+--+--+--+ +--+--+--+--+ + */ +#define ESP_FOURCC_YUYV ESP_FOURCC_TO_INT('Y', 'U', 'Y', 'V') /* 16 bpp(bits per pixel), YUYV 4:2:2 */ + +/** + * YVYU422 + * Memory Layout: + * +--+--+--+--+ +--+--+--+--+ + * |Y0|V0|Y1|U0| |Y2|V2|Y3|U2| + * +--+--+--+--+ +--+--+--+--+ + */ +#define ESP_FOURCC_YVYU ESP_FOURCC_TO_INT('Y', 'V', 'Y', 'U') /* 16 bpp, YVYU 4:2:2 */ + +/** + * YYUV422 + * Memory Layout: + * +--+--+--+--+ +--+--+--+--+ + * |Y0|Y1|U0|V0| |Y2|Y3|U2|V2| + * +--+--+--+--+ +--+--+--+--+ + */ +#define ESP_FOURCC_YYUV ESP_FOURCC_TO_INT('Y', 'Y', 'U', 'V') /* 16 bpp, YYUV 4:2:2 */ + +/** + * UYVY422 + * Memory Layout: + * +--+--+--+--+ +--+--+--+--+ + * |U0|Y0|V0|Y1| |U2|Y2|V2|Y3| + * +--+--+--+--+ +--+--+--+--+ + */ +#define ESP_FOURCC_UYVY ESP_FOURCC_TO_INT('U', 'Y', 'V', 'Y') /* 16 bpp, UYVY 4:2:2 */ + +/** + * VYUY422 + * Memory Layout: + * +--+--+--+--+ +--+--+--+--+ + * |V0|Y0|U0|Y1| |V2|Y2|U2|Y3| + * +--+--+--+--+ +--+--+--+--+ + */ +#define ESP_FOURCC_VYUY ESP_FOURCC_TO_INT('V', 'Y', 'U', 'Y') /* 16 bpp, VYUY 4:2:2 */ + +/** + * YUV444 + * Memory Layout: + * +--+--+--+ +--+--+--+ + * |Y0|U0|V0| |Y1|U1|V1| + * +--+--+--+ +--+--+--+ + */ +#define ESP_FOURCC_YUV ESP_FOURCC_TO_INT('V', '3', '0', '8') /* 24 bpp, Y-U-V 4:4:4 */ + +/** + * UYV444 + * Memory Layout: + * +--+--+--+ +--+--+--+ + * |U0|Y0|V0| |U1|Y1|V1| + * +--+--+--+ +--+--+--+ + */ +#define ESP_FOURCC_UYV ESP_FOURCC_TO_INT('I', 'Y', 'U', '2') /* 24 bpp, U-Y-V 4:4:4 */ + +/** + * Espressif YUV420, U00 V10 shared Y00 Y01 Y10 Y11, U02 V12 shared Y02 Y03 Y12 Y13 + * Memory Layout: + * +---+---+---+ +---+---+---+ + * Line1 |U00|Y00|Y01| |U02|Y02|Y03| + * +---+---+---+ +---+---+---+ + * Line2 |V10|Y10|Y11| |V12|Y12|Y13| + * +---+---+---+ +---+---+---+ + */ +#define ESP_FOURCC_OUYY_EVYY ESP_FOURCC_TO_INT('O', 'U', 'E', 'V') /* 12 bpp, Espressif Y-U-V 4:2:0 */ + +/** + * Espressif YUV420, U0 V4 shared Y0 Y1 Y2 Y3, U2 V6 shared Y4 Y5 Y6 Y7 + * Memory Layout: + * Y, Cb, Y, Y, Cb, Y, Y, Cr, Y, Y, Cr, Y + * +--+--+--+ +--+--+--+ +--+--+--+ +--+--+--+ + * |Y0|U0|Y1| |Y2|U2|Y3| |Y4|V4|Y5| |Y6|V6|Y7| + * +--+--+--+ +--+--+--+ +--+--+--+ +--+--+--+ + */ +#define ESP_FOURCC_YUY2YVY2 ESP_FOURCC_TO_INT('Y', 'U', 'Y', '2') /* 12 bpp, Espressif LCD_CAM Y-U-V 4:2:0 */ + +/** + * Espressif YUV411, U0 V2 shared Y0 Y1 Y2 Y3, U4 V6 shared Y4 Y5 Y6 Y7 + * Memory Layout: + * Y, Cb, Y, Y, Cb, Y, Y, Cr, Y, Y, Cr, Y + * +--+--+--+ +--+--+--+ +--+--+--+ +--+--+--+ + * |Y0|U0|Y1| |Y2|V2|Y3| |Y4|U4|Y5| |Y6|V6|Y7| + * +--+--+--+ +--+--+--+ +--+--+--+ +--+--+--+ + */ +#define ESP_FOURCC_YUYYVY ESP_FOURCC_TO_INT('Y', 'U', 'Y', 'Y') /* 12 bpp, Espressif LCD_CAM Y-U-V 4:1:1 */ + + +/***************************************************************/ +/* YUV(YCbCr) Semi-planar format */ +/* Two contiguous planes, one Y, one Cr + Cb interleaved */ +/***************************************************************/ +/** + * NV12 (YUV 4:2:0), 1 shared U(Cb), V(Cr) value for 2x2 block pixels + * Memory Layout: + * | Y00 | Y01 | Y02 | Y03 | + * | Y10 | Y11 | Y12 | Y13 | + * | Y20 | Y21 | Y22 | Y23 | + * | Y30 | Y31 | Y32 | Y33 | + * | U00 | V00 | U02 | V02 | + * | U20 | V20 | U22 | V22 | + */ +#define ESP_FOURCC_NV12 ESP_FOURCC_TO_INT('N', 'V', '1', '2') /* 12 bpp, YUV 4:2:0 */ + +/** + * NV21 (YVU 4:2:0), 1 shared U(Cb), V(Cr) value for 2x2 block pixels + * Memory Layout: + * | Y00 | Y01 | Y02 | Y03 | + * | Y10 | Y11 | Y12 | Y13 | + * | Y20 | Y21 | Y22 | Y23 | + * | Y30 | Y31 | Y32 | Y33 | + * | V00 | U00 | V02 | U02 | + * | V20 | U20 | V22 | U22 | + */ +#define ESP_FOURCC_NV21 ESP_FOURCC_TO_INT('N', 'V', '2', '1') /* 12 bpp, YVU 4:2:0 */ + +/** + * NV16 (YUV 4:2:2), 1 shared U(Cb), V(Cr) value for 2 pixels + * Memory Layout: + * | Y00 | Y01 | Y02 | Y03 | + * | Y10 | Y11 | Y12 | Y13 | + * | Y20 | Y21 | Y22 | Y23 | + * | Y30 | Y31 | Y32 | Y33 | + * | U00 | V00 | U02 | V02 | + * | U10 | V10 | U12 | V12 | + * | U20 | V20 | U22 | V22 | + * | U30 | V30 | U32 | V32 | + */ +#define ESP_FOURCC_NV16 ESP_FOURCC_TO_INT('N', 'V', '1', '6') /* 16 bpp, YUV 4:2:2 */ + +/** + * NV61 (YVU 4:2:2), 1 shared U(Cb), V(Cr) value for 2 pixels + * Memory Layout: + * | Y00 | Y01 | Y02 | Y03 | + * | Y10 | Y11 | Y12 | Y13 | + * | Y20 | Y21 | Y22 | Y23 | + * | Y30 | Y31 | Y32 | Y33 | + * | V00 | U00 | V02 | U02 | + * | V10 | U10 | V12 | U12 | + * | V20 | U20 | V22 | U22 | + * | V30 | U30 | V32 | U32 | + */ +#define ESP_FOURCC_NV61 ESP_FOURCC_TO_INT('N', 'V', '6', '1') /* 16 bpp, YVU 4:2:2 */ + +/** + * NV24 (YUV 4:4:4) + * Memory Layout: + * | Y00 | Y01 | Y02 | Y03 | + * | Y10 | Y11 | Y12 | Y13 | + * | Y20 | Y21 | Y22 | Y23 | + * | Y30 | Y31 | Y32 | Y33 | + * | U00 | V00 | U01 | V01 | U02 | V02 | U03 | V03 | + * | U10 | V10 | U11 | V11 | U12 | V12 | U13 | V13 | + * | U20 | V20 | U21 | V21 | U22 | V22 | U23 | V23 | + * | U30 | V30 | U31 | V31 | U32 | V32 | U33 | V33 | + */ +#define ESP_FOURCC_NV24 ESP_FOURCC_TO_INT('N', 'V', '2', '4') /* 24 bpp, YUV 4:4:4 */ + +/** + * NV42 (YVU 4:4:4) + * Memory Layout: + * | Y00 | Y01 | Y02 | Y03 | + * | Y10 | Y11 | Y12 | Y13 | + * | Y20 | Y21 | Y22 | Y23 | + * | Y30 | Y31 | Y32 | Y33 | + * | V00 | U00 | V01 | U01 | V02 | U02 | V03 | U03 | + * | V10 | U10 | V11 | U11 | V12 | U12 | V13 | U13 | + * | V20 | U20 | V21 | U21 | V22 | U22 | V23 | U23 | + * | V30 | U30 | V31 | U31 | V32 | U32 | V33 | U33 | + */ +#define ESP_FOURCC_NV42 ESP_FOURCC_TO_INT('N', 'V', '4', '2') /* 24 bpp, YVU 4:4:4 */ + + +/*******************************************************************/ +/* YCbCr-YUV Semi-planar format */ +/* Two non contiguous planes - one Y, one Cr + Cb interleaved */ +/*******************************************************************/ +/** + * NV12M (YUV 4:2:0), 1 shared U(Cb), V(Cr) value for 2x2 block pixels + * Memory Layout: + * | Y00 | Y01 | Y02 | Y03 | + * | Y10 | Y11 | Y12 | Y13 | + * | Y20 | Y21 | Y22 | Y23 | + * | Y30 | Y31 | Y32 | Y33 | + * + * | U00 | V00 | U02 | V02 | + * | U20 | V20 | U22 | V22 | + */ +#define ESP_FOURCC_NV12M ESP_FOURCC_TO_INT('N', 'M', '1', '2') /* 12 YUV 4:2:0 */ + +/** + * NV21M (YVU 4:2:0), 1 shared U(Cb), V(Cr) value for 2x2 block pixels + * Memory Layout: + * | Y00 | Y01 | Y02 | Y03 | + * | Y10 | Y11 | Y12 | Y13 | + * | Y20 | Y21 | Y22 | Y23 | + * | Y30 | Y31 | Y32 | Y33 | + * + * | V00 | U00 | V02 | U02 | + * | V20 | U20 | V22 | U22 | + */ +#define ESP_FOURCC_NV21M ESP_FOURCC_TO_INT('N', 'M', '2', '1') /* 12 YVU 4:2:0 */ + +/** + * NV16M (YUV 4:2:2), 1 shared U(Cb), V(Cr) value for 2 pixels + * Memory Layout: + * | Y00 | Y01 | Y02 | Y03 | + * | Y10 | Y11 | Y12 | Y13 | + * | Y20 | Y21 | Y22 | Y23 | + * | Y30 | Y31 | Y32 | Y33 | + * + * | U00 | V00 | U02 | V02 | + * | U10 | V10 | U12 | V12 | + * | U20 | V20 | U22 | V22 | + * | U30 | V30 | U32 | V32 | + */ +#define ESP_FOURCC_NV16M ESP_FOURCC_TO_INT('N', 'M', '1', '6') /* 16 YUV 4:2:2 */ + +/** + * NV61M (YVU 4:2:2), 1 shared U(Cb), V(Cr) value for 2 pixels + * Memory Layout: + * | Y00 | Y01 | Y02 | Y03 | + * | Y10 | Y11 | Y12 | Y13 | + * | Y20 | Y21 | Y22 | Y23 | + * | Y30 | Y31 | Y32 | Y33 | + * + * | V00 | U00 | V02 | U02 | + * | V10 | U10 | V12 | U12 | + * | V20 | U20 | V22 | U22 | + * | V30 | U30 | V32 | U32 | + */ +#define ESP_FOURCC_NV61M ESP_FOURCC_TO_INT('N', 'M', '6', '1') /* 16 YVU 4:2:2 */ + + +/*******************************************************************/ +/* YCbCr-YUV planar format */ +/* Three contiguous planes, Y, Cb(U), Cr(V), U V is un-interleaved */ +/*******************************************************************/ +/** + * YUV410P (YUV 4:1:0), 1 shared U(Cb), V(Cr) value for 16(4x4) pixels + * Memory Layout: + * | Y00 | Y01 | Y02 | Y03 | + * | Y10 | Y11 | Y12 | Y13 | + * | Y20 | Y21 | Y22 | Y23 | + * | Y30 | Y31 | Y32 | Y33 | + * | U00 | + * | V00 | + */ +#define ESP_FOURCC_YUV410P ESP_FOURCC_TO_INT('Y', 'U', 'V', '9') /* 9 bpp, YUV 4:1:0, 1 shared U(Cb), V(Cr) value for 16 pixels */ + +/** + * YVU410P (YVU 4:1:0), 1 shared U(Cb), V(Cr) value for 16(4x4) pixels + * Memory Layout: + * | Y00 | Y01 | Y02 | Y03 | + * | Y10 | Y11 | Y12 | Y13 | + * | Y20 | Y21 | Y22 | Y23 | + * | Y30 | Y31 | Y32 | Y33 | + * | V00 | + * | U00 | + */ +#define ESP_FOURCC_YVU410P ESP_FOURCC_TO_INT('Y', 'V', 'U', '9') /* 9 bpp, YVU 4:1:0, 1 shared U(Cb), V(Cr) value for 16 pixels */ + +/** + * YUV411P (YUV 4:1:1), 1 shared U(Cb), V(Cr) value for 4 horizontally pixels + * Memory Layout: + * | Y00 | Y01 | Y02 | Y03 | + * | Y10 | Y11 | Y12 | Y13 | + * | U00 | + * | U10 | + * | V00 | + * | V10 | + */ +#define ESP_FOURCC_YUV411P ESP_FOURCC_TO_INT('4', '1', '1', 'P') /* 12 bpp, YVU411 planar, 1 shared U(Cb), V(Cr) value for 4 horizontally pixels */ + +/** + * YUV420P (YUV 4:2:0) + * Memory Layout: + * | Y00 | Y01 | Y02 | Y03 | + * | Y10 | Y11 | Y12 | Y13 | + * | Y20 | Y21 | Y22 | Y23 | + * | Y30 | Y31 | Y32 | Y33 | + * | U00 | U02 | + * | U20 | U22 | + * | V00 | V02 | + * | V20 | V22 | + */ +#define ESP_FOURCC_YUV420P ESP_FOURCC_TO_INT('Y', 'U', '1', '2') /* 12 bpp, YUV 4:2:0, 1 shared U(Cb), V(Cr) value for 2x2 block pixels */ + +/** + * YVU420P (YVU 4:2:0) + * Memory Layout: + * | Y00 | Y01 | Y02 | Y03 | + * | Y10 | Y11 | Y12 | Y13 | + * | Y20 | Y21 | Y22 | Y23 | + * | Y30 | Y31 | Y32 | Y33 | + * | V00 | V02 | + * | V20 | V22 | + * | U00 | U02 | + * | U20 | U22 | + */ +#define ESP_FOURCC_YVU420P ESP_FOURCC_TO_INT('Y', 'V', '1', '2') /* 12 bpp, YVU 4:2:0, 1 shared U(Cb), V(Cr) value for 2x2 block pixels */ + +/** + * YUV422P (YUV 4:2:2) + * Memory Layout: + * | Y00 | Y01 | Y02 | Y03 | + * | Y10 | Y11 | Y12 | Y13 | + * | U00 | U02 | + * | U10 | U12 | + * | V00 | V02 | + * | V10 | V12 | + */ +#define ESP_FOURCC_YUV422P ESP_FOURCC_TO_INT('4', '2', '2', 'P') /* 16 bpp, YVU422 planar */ + +/** + * YUV444P (YUV 4:4:4) + * Memory Layout: + * | Y00 | Y01 | Y02 | Y03 | + * | Y10 | Y11 | Y12 | Y13 | + * | U00 | U01 | U02 | U03 | + * | U10 | U11 | U12 | U13 | + * | V00 | V01 | V02 | V03 | + * | V10 | V11 | V12 | V13 | + */ +#define ESP_FOURCC_YUV444P ESP_FOURCC_TO_INT('4', '4', '4', 'P') /* 24 bpp, YVU444 planar */ + + +/***********************************************************************/ +/* YCbCr-YUV planar format */ +/* Three non contiguous planes, Y, Cb(U), Cr(V), U V is un-interleaved */ +/***********************************************************************/ + +/** + * YUV420M (YUV 4:2:0), 1 shared U(Cb), V(Cr) value for 2x2 block pixels + * Memory Layout: + * | Y00 | Y01 | Y02 | Y03 | + * | Y10 | Y11 | Y12 | Y13 | + * | Y20 | Y21 | Y22 | Y23 | + * | Y30 | Y31 | Y32 | Y33 | + * + * | U00 | U02 | + * | U20 | U22 | + * + * | V00 | V02 | + * | V20 | V22 | + */ +#define ESP_FOURCC_YUV420M ESP_FOURCC_TO_INT('Y', 'M', '1', '2') /* 12 bpp, YUV420 planar */ + +/** + * YVU420M (YUV 4:2:0), 1 shared U(Cb), V(Cr) value for 2x2 block pixels + * Memory Layout: + * | Y00 | Y01 | Y02 | Y03 | + * | Y10 | Y11 | Y12 | Y13 | + * | Y20 | Y21 | Y22 | Y23 | + * | Y30 | Y31 | Y32 | Y33 | + * + * | V00 | V02 | + * | V20 | V22 | + * + * | U00 | U02 | + * | U20 | U22 | + */ +#define ESP_FOURCC_YVU420M ESP_FOURCC_TO_INT('Y', 'M', '2', '1') /* 12 bpp, YVU420 planar */ + +/** + * YUV422M (YUV 4:2:2), 1 shared U(Cb), V(Cr) value for 2 pixels + * Memory Layout: + * | Y00 | Y01 | Y02 | Y03 | + * | Y10 | Y11 | Y12 | Y13 | + * + * | U00 | U02 | + * | U10 | U12 | + * + * | V00 | V02 | + * | V10 | V12 | + */ +#define ESP_FOURCC_YUV422M ESP_FOURCC_TO_INT('Y', 'M', '1', '6') /* 16 bpp, YUV422 planar */ + +/** + * YVU422M (YUV 4:2:2), 1 shared U(Cb), V(Cr) value for 2 pixels + * Memory Layout: + * | Y00 | Y01 | Y02 | Y03 | + * | Y10 | Y11 | Y12 | Y13 | + * + * | V00 | V02 | + * | V10 | V12 | + * + * | U00 | U02 | + * | U10 | U12 | + */ +#define ESP_FOURCC_YVU422M ESP_FOURCC_TO_INT('Y', 'M', '6', '1') /* 16 bpp, YVU422 planar */ + +/** + * YUV444M (YUV 4:4:4) + * Memory Layout: + * | Y00 | Y01 | Y02 | Y03 | + * | Y10 | Y11 | Y12 | Y13 | + * + * | U00 | U01 | U02 | U03 | + * | U10 | U11 | U12 | U13 | + * + * | V00 | V01 | V02 | V03 | + * | V10 | V11 | V12 | V13 | + */ +#define ESP_FOURCC_YUV444M ESP_FOURCC_TO_INT('Y', 'M', '2', '4') /* 24 bpp, YUV444 planar */ + +/** + * YUV444M (YUV 4:4:4) + * Memory Layout: + * | Y00 | Y01 | Y02 | Y03 | + * | Y10 | Y11 | Y12 | Y13 | + * + * | V00 | V01 | V02 | V03 | + * | V10 | V11 | V12 | V13 | + * + * | U00 | U01 | U02 | U03 | + * | U10 | U11 | U12 | U13 | + */ +#define ESP_FOURCC_YVU444M ESP_FOURCC_TO_INT('Y', 'M', '4', '2') /* 24 bpp, YVU444 planar */ + + +/**********************************************************************/ +/* CMYK format */ +/**********************************************************************/ +#define ESP_FOURCC_CMYK ESP_FOURCC_TO_INT('C', 'M', 'Y', 'K') /* 32 bpp, CMYK */ + + +/**********************************************************************/ +/* RAW format */ +/**********************************************************************/ +/** + * RAW8 + * Memory Layout: + * Addr0 Addr1 Addr2 Addr3 + * R0 R1 R2 R3 + */ +#define ESP_FOURCC_RAW8 ESP_FOURCC_TO_INT('R', 'A', 'W', '8') /* 8 bpp, raw8 */ + +/** + * RAW10 + * Memory Layout: + * Addr0 Addr1 Addr2 Addr3 + * R0[7:0] R0[1:0] R1[7:0] R1[1:0] + */ +#define ESP_FOURCC_RAW10 ESP_FOURCC_TO_INT('R', 'A', 'W', 'A') /* 10 bpp, raw10 */ + +/** + * RAW12 + * Memory Layout: + * Addr0 Addr1 Addr2 Addr3 + * R0[7:0] R0[3:0] R1[7:0] R1[3:0] + */ +#define ESP_FOURCC_RAW12 ESP_FOURCC_TO_INT('R', 'A', 'W', 'C') /* 12 bpp, raw12 */ + +/** + * RAW16 + * Memory Layout: + * Addr0 Addr1 Addr2 Addr3 + * R0[7:0] R0[7:0] R1[7:0] R1[7:0] + */ +#define ESP_FOURCC_RAW16 ESP_FOURCC_TO_INT('R', 'A', 'W', 'G') /* 16 bpp, raw16 */ + +/** + * LUM4 (luminance 4 bits) + * Memory Layout: + * Addr0 Addr1 Addr2 Addr3 + * L0L1 L2L3 L4L5 L6L7 + */ +#define ESP_FOURCC_LUM4 ESP_FOURCC_TO_INT('L', 'U', 'M', '4') /* 4 bpp, an 4-bit luminance-only format */ + +/** + * LUM8 (luminance 8 bits) + * Memory Layout: + * Addr0 Addr1 Addr2 Addr3 + * L0 L1 L2 L3 + */ +#define ESP_FOURCC_LUM8 ESP_FOURCC_TO_INT('L', 'U', 'M', '8') /* 8 bpp, an 8-bit luminance-only format */ + +/** + * ALPHA4 (Alpha 4 bits) + * Memory Layout: + * Addr0 Addr1 Addr2 Addr3 + * A0A1 A2A3 A4A5 A6A7 + */ +#define ESP_FOURCC_ALPHA4 ESP_FOURCC_TO_INT('A', 'L', 'P', '4') /* 4 bpp, an 4-bit alpha-only format */ + +/** + * ALPHA8 (Alpha 8 bits) + * Memory Layout: + * Addr0 Addr1 Addr2 Addr3 + * A0 A1 A2 A3 + */ +#define ESP_FOURCC_ALPHA8 ESP_FOURCC_TO_INT('A', 'L', 'P', '8') /* 8 bpp, an 8-bit alpha-only format */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/helpers/include/esp_gmf_caps_def.h b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/helpers/include/esp_gmf_caps_def.h new file mode 100644 index 0000000..d099770 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/helpers/include/esp_gmf_caps_def.h @@ -0,0 +1,84 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +// Convert string to character code +static inline uint64_t gmf_str_to_cc(const char *str, int max_len) +{ + uint64_t result = 0; + if (str && strlen(str) <= max_len) { + memcpy(&result, str, strlen(str)); + } + return result; +} + +// Convert 64-bit EIGHTCC to string +static inline void gmf_eightcc_to_str(uint64_t eightcc, char out[9]) +{ + for (int i = 0; i < 8; i++) { + out[i] = (char)((eightcc >> (i * 8)) & 0xFF); + } + out[8] = '\0'; // Null-terminate the string +} + +// Macro to convert a string to an 8-byte identifier (EIGHTCC) +#define STR_2_EIGHTCC(str) gmf_str_to_cc(str, 8) + +// Macro to convert a string to an 4-byte identifier (FOURCC) +#define STR_2_FOURTCC(str) gmf_str_to_cc(str, 4) + +// Macro to convert an EIGHTCC code to a string +#define EIGHTCC_2_STR(eightcc) ({ \ + static char eightcc_str[9]; \ + gmf_eightcc_to_str(eightcc, eightcc_str); \ + eightcc_str; \ +}) + +/***************************************************************************/ +/* Definition of Audio Capabilities */ +/***************************************************************************/ +#define ESP_GMF_CAPS_AUDIO_DECODER STR_2_EIGHTCC("AUDDEC") +#define ESP_GMF_CAPS_AUDIO_ENCODER STR_2_EIGHTCC("AUDENC") +#define ESP_GMF_CAPS_AUDIO_ALC STR_2_EIGHTCC("AUDALC") +#define ESP_GMF_CAPS_AUDIO_BIT_CONVERT STR_2_EIGHTCC("AUDBTCVT") +#define ESP_GMF_CAPS_AUDIO_CHANNEL_CONVERT STR_2_EIGHTCC("AUDCHCVT") +#define ESP_GMF_CAPS_AUDIO_RATE_CONVERT STR_2_EIGHTCC("AUDRTCVT") +#define ESP_GMF_CAPS_AUDIO_MIXER STR_2_EIGHTCC("AUDMIXER") +#define ESP_GMF_CAPS_AUDIO_EQUALIZER STR_2_EIGHTCC("AUDEQ") +#define ESP_GMF_CAPS_AUDIO_SONIC STR_2_EIGHTCC("AUDSONIC") +#define ESP_GMF_CAPS_AUDIO_FADE STR_2_EIGHTCC("AUDFADE") +#define ESP_GMF_CAPS_AUDIO_DEINTERLEAVE STR_2_EIGHTCC("AUDDITLV") +#define ESP_GMF_CAPS_AUDIO_INTERLEAVE STR_2_EIGHTCC("AUDINTLV") +#define ESP_GMF_CAPS_AUDIO_AEC STR_2_EIGHTCC("AUDAEC") +#define ESP_GMF_CAPS_AUDIO_NS STR_2_EIGHTCC("AUDNS") +#define ESP_GMF_CAPS_AUDIO_AGC STR_2_EIGHTCC("AUDAGC") +#define ESP_GMF_CAPS_AUDIO_VAD STR_2_EIGHTCC("AUDVAD") +#define ESP_GMF_CAPS_AUDIO_WWE STR_2_EIGHTCC("AUDWWE") +#define ESP_GMF_CAPS_AUDIO_VCMD STR_2_EIGHTCC("AUDVCMD") + + +/***************************************************************************/ +/* Definition of Video Capabilities */ +/***************************************************************************/ +#define ESP_GMF_CAPS_VIDEO_DECODER STR_2_EIGHTCC("VIDDEC") +#define ESP_GMF_CAPS_VIDEO_ENCODER STR_2_EIGHTCC("VIDENC") +#define ESP_GMF_CAPS_VIDEO_COLOR_CONVERT STR_2_EIGHTCC("VIDCCVT") +#define ESP_GMF_CAPS_VIDEO_CROP STR_2_EIGHTCC("VIDCROP") +#define ESP_GMF_CAPS_VIDEO_ROTATE STR_2_EIGHTCC("VIDRTATE") +#define ESP_GMF_CAPS_VIDEO_SCALE STR_2_EIGHTCC("VIDSCALE") +#define ESP_GMF_CAPS_VIDEO_OVERLAY STR_2_EIGHTCC("VIDOVLY") +#define ESP_GMF_CAPS_VIDEO_FPS_CVT STR_2_EIGHTCC("VIDFPS") + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/helpers/include/esp_gmf_uri_parser.h b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/helpers/include/esp_gmf_uri_parser.h new file mode 100644 index 0000000..c94dfcc --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/helpers/include/esp_gmf_uri_parser.h @@ -0,0 +1,60 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * @brief Structure representing a parsed URI + * + * This structure holds different components of a URI such as scheme, userinfo, + * host, port, path, query, etc. Each field corresponds to a specific part of the URI + */ +typedef struct { + char *scheme; /*!< The scheme of the URI (e.g., "http", "https") */ + char *userinfo; /*!< The userinfo component (optional, may include username and password) */ + char *username; /*!< The username part of the userinfo, if present */ + char *password; /*!< The password part of the userinfo, if present */ + char *host; /*!< The host (e.g., domain name or IP address) */ + int port; /*!< The port number (optional, if not specified, defaults to scheme's standard port) */ + char *path; /*!< The path component of the URI */ + char *query; /*!< The query string of the URI (optional, starts after '?' in URL) */ + char *fragment; /*!< The fragment identifier (optional, starts after '#' in URL) */ +} esp_gmf_uri_t; + +/** + * @brief Parses a URI string and populates a esp_gmf_uri_t structure with its components + * + * This function takes a URI string as input, parses it, and allocates and populates a + * `esp_gmf_uri_t` structure with the parsed components. The caller is responsible for + * freeing the allocated memory using `esp_gmf_uri_free()`. + * + * @param[in] uri_str The URI string to be parsed + * @param[out] uri_out Pointer to the `esp_gmf_uri_t` structure where the parsed URI components will be stored + * + * @return + * - 0 On success + * - Non-zero Error code on failure + */ +int esp_gmf_uri_parse(const char *uri_str, esp_gmf_uri_t **uri_out); + +/** + * @brief Frees the memory allocated for a esp_gmf_uri_t object + * + * This function releases all dynamically allocated memory associated with the URI components + * within the given `esp_gmf_uri_t` structure. After calling this function, the URI pointer will be invalid. + * + * @param[in] uri Pointer to the `esp_gmf_uri_t` object to be freed + */ +void esp_gmf_uri_free(esp_gmf_uri_t *uri); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/idf_component.yml b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/idf_component.yml new file mode 100644 index 0000000..111e971 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/idf_component.yml @@ -0,0 +1,9 @@ +version: "0.7.7" +description: Espressif General Multimedia Framework (gmf-core) module +url: https://github.com/espressif/esp-gmf/tree/main/gmf_core +documentation: "https://github.com/espressif/esp-gmf/blob/main/gmf_core/README.md" +issues: "https://github.com/espressif/esp-gmf/issues" + +tags: + - "gmf" + - "multimedia" diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/include/esp_gmf_args_desc.h b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/include/esp_gmf_args_desc.h new file mode 100644 index 0000000..b67d79f --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/include/esp_gmf_args_desc.h @@ -0,0 +1,577 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#include +#include "esp_gmf_err.h" +#include "esp_log.h" +#include "esp_gmf_oal_mem.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * @brief GMF argument types enumeration + */ +typedef enum { + ESP_GMF_ARGS_TYPE_NIL = 0x00, /*!< No type or null value */ + ESP_GMF_ARGS_TYPE_UINT8 = 0x01, /*!< Unsigned 8-bit integer */ + ESP_GMF_ARGS_TYPE_INT8 = 0x02, /*!< Signed 8-bit integer */ + ESP_GMF_ARGS_TYPE_UINT16 = 0x03, /*!< Unsigned 16-bit integer */ + ESP_GMF_ARGS_TYPE_INT16 = 0x04, /*!< Signed 16-bit integer */ + ESP_GMF_ARGS_TYPE_UINT32 = 0x05, /*!< Unsigned 32-bit integer */ + ESP_GMF_ARGS_TYPE_INT32 = 0x06, /*!< Signed 32-bit integer */ + ESP_GMF_ARGS_TYPE_UINT64 = 0x07, /*!< Unsigned 64-bit integer */ + ESP_GMF_ARGS_TYPE_INT64 = 0x08, /*!< Signed 64-bit integer */ + ESP_GMF_ARGS_TYPE_FLOAT = 0x09, /*!< Single-precision floating point */ + ESP_GMF_ARGS_TYPE_DOUBLE = 0x0a, /*!< Double-precision floating point */ + ESP_GMF_ARGS_TYPE_ARRAY = 0x0b, /*!< Array data type */ +} esp_gmf_args_type_t; + +/** + * @brief GMF argument description structure + */ +typedef struct esp_gmf_args_desc { + struct esp_gmf_args_desc *next; /*!< Pointer to the next argument in the list */ + esp_gmf_args_type_t type; /*!< Data type of the argument */ + uint16_t offset; /*!< Byte offset from the previous argument */ + const char *name; /*!< Name of the argument */ + struct esp_gmf_args_desc *val; /*!< Pointer to nested value (used for arrays) */ + uint32_t size; /*!< Size of the argument in bytes */ +} esp_gmf_args_desc_t; + +/** + * @brief Create a new argument description node + * + * @note The `name` is not copied, so it must have global or static scope to remain valid throughout the method's operation + * + * @param[in] name Name of the argument + * @param[in] type Type of the argument + * @param[in] val Nested value for arrays or sublists + * @param[in] size Size of the argument + * @param[out] handle Pointer to the created node + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_MEMORY_LACK Memory allocation failure + */ +static inline esp_gmf_err_t esp_gmf_args_desc_create(const char *name, esp_gmf_args_type_t type, esp_gmf_args_desc_t *val, + uint32_t size, esp_gmf_args_desc_t **handle) +{ + esp_gmf_args_desc_t *node = (esp_gmf_args_desc_t *)esp_gmf_oal_calloc(1, sizeof(esp_gmf_args_desc_t)); + if (node) { + if (name) { + node->name = name; + } + node->type = type; + node->val = val; + node->size = size; + node->next = NULL; + *handle = node; + return ESP_GMF_ERR_OK; + } + return ESP_GMF_ERR_MEMORY_LACK; +} + +/** + * @brief Destroy a esp_gmf_args_desc node and all its successors + * + * @param[in] head Pointer to the head of the list to be destroyed + */ +static inline void esp_gmf_args_desc_destroy(esp_gmf_args_desc_t *head) +{ + esp_gmf_args_desc_t *current = head; + esp_gmf_args_desc_t *next; + + while (current != NULL) { + next = current->next; + current->next = NULL; + if (current->val != NULL) { + esp_gmf_args_desc_destroy(current->val); + current->val = NULL; + } + esp_gmf_oal_free(current); + current = next; + } +} + +/** + * @brief Calculate the total size of all argument values in a linked list of GMF argument descriptions + * + * @param[in] head Pointer to the head of the linked list of argument descriptions + * @param[out] total_value_size Pointer to the variable where the total size will be stored + * + * @return + * - ESP_GMF_ERR_OK Success, the total size is stored in `total_value_size` + * - ESP_GMF_ERR_INVALID_ARG If either the `head` or `total_value_size` pointer is `NULL` + */ +static inline esp_gmf_err_t esp_gmf_args_desc_get_total_size(esp_gmf_args_desc_t *head, size_t *total_value_size) +{ + if (head == NULL || total_value_size == NULL) { + return ESP_GMF_ERR_INVALID_ARG; + } + esp_gmf_args_desc_t *current = head; + while (current != NULL) { + *total_value_size += current->size; + ESP_LOGD("GMF_ARG", "Get total size %d, name:%s", *total_value_size, current->name); + current = current->next; + } + return ESP_GMF_ERR_OK; +} + +/** + * @brief Append a new argument description to the linked list of argument descriptions. + * This function creates a new argument description node and appends it to the end of the linked list. + * The new node is initialized with the specified `name`, `type`, `size`, `offset`, and a pointer to + * a `val` structure that contains additional argument-specific data. + * + * @note The `name` is not copied, so it must have global or static scope to remain valid throughout the method's operation + * + * @param[in,out] head Pointer to the head of the linked list. This is a pointer to the first node + * in the list, and it will be updated if the new node is added at the head + * @param[in] name The name of the argument, which will be stored in the new node + * @param[in] type The type of the argument (e.g., integer, array, etc.) + * @param[in] size The size of the argument value + * @param[in] offset The offset of the argument in the structure or buffer + * @param[in] val Pointer to a structure containing additional data related to the argument + * + * @return + * - ESP_GMF_ERR_OK Success, the new argument description is appended to the list + * - ESP_GMF_ERR_INVALID_ARG If `head` or `name` is `NULL` + */ +static inline esp_gmf_err_t esp_gmf_args_desc_append_base(esp_gmf_args_desc_t **head, const char *name, esp_gmf_args_type_t type, + uint32_t size, uint32_t offset, esp_gmf_args_desc_t *val) +{ + esp_gmf_args_desc_t *new_args = NULL; + esp_gmf_args_desc_create(name, type, val, size, &new_args); + if (new_args == NULL) { + return ESP_GMF_ERR_MEMORY_LACK; + } + if (*head == NULL) { + *head = new_args; + return ESP_GMF_ERR_OK; + } + esp_gmf_args_desc_t *current = *head; + while (current) { + if (current->next == NULL) { + break; + } + current = current->next; + } + if (type == ESP_GMF_ARGS_TYPE_ARRAY) { + size_t sz = 0; + esp_gmf_args_desc_get_total_size(*head, &sz); + offset = sz; + esp_gmf_args_desc_t *tmp = val; + while (tmp) { + tmp->offset += offset; + if (tmp->next == NULL) { + break; + } + tmp = tmp->next; + } + } + new_args->offset = offset; + current->next = new_args; + return ESP_GMF_ERR_OK; +} + +/** + * @brief Append a new argument description for a non-array argument to the linked list + * + * @note The `name` is not copied, so it must have global or static scope to remain valid throughout the method's operation + * + * @param[in,out] head Pointer to the head of the linked list. This is a pointer to the first node + * in the list, and it will be updated if the new node is added at the head + * @param[in] name The name of the argument, which will be stored in the new node + * @param[in] type The type of the argument (e.g., integer, array, etc.) + * @param[in] size The size of the argument value + * @param[in] offset The offset of the argument in the structure or buffer + * + * @return + * - ESP_GMF_ERR_OK Success, the new argument description is appended to the list + * - ESP_GMF_ERR_INVALID_ARG If `head` or `name` is `NULL` + */ +static inline esp_gmf_err_t esp_gmf_args_desc_append(esp_gmf_args_desc_t **head, const char *name, esp_gmf_args_type_t type, + uint32_t size, uint32_t offset) +{ + return esp_gmf_args_desc_append_base(head, name, type, size, offset, NULL); +} + +/** + * @brief Append a new argument description for an array argument to the linked list + * + * This function wraps the `esp_gmf_args_desc_append_base` function to append a new argument description + * for an array argument (where `val` is a pointer to the array's value structure) to the linked list + * It sets the argument type to `ESP_GMF_ARGS_TYPE_ARRAY` and uses the provided `name`, `size`, and `offset` + * to create the new argument description + * + * @param[in,out] head Pointer to the head of the linked list. This is a pointer to the first node + * in the list, and it will be updated if the new node is added at the head + * @param[in] name The name of the argument, which will be stored in the new node + * @param[in] val Pointer to a structure containing the array data associated with the argument + * @param[in] size The size of the array (e.g., the total number of elements) + * @param[in] offset The offset of the argument in the structure or buffer + * + * @return + * - ESP_GMF_ERR_OK Success, the new argument description is appended to the list + * - ESP_GMF_ERR_INVALID_ARG If `head` or `name` is `NULL` + */ +static inline esp_gmf_err_t esp_gmf_args_desc_append_array(esp_gmf_args_desc_t **head, const char *name, esp_gmf_args_desc_t *val, + uint32_t size, uint32_t offset) +{ + return esp_gmf_args_desc_append_base(head, name, ESP_GMF_ARGS_TYPE_ARRAY, size, offset, val); +} + +/** + * @brief Copy an entire GMF argument description list + * + * @param[in] head Pointer to the head of the list to be copied + * @param[out] new_args Pointer to the head of the newly copied list + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_MEMORY_LACK Memory allocation failure + * - ESP_GMF_ERR_INVALID_ARG Invalid input arguments + */ +static inline esp_gmf_err_t esp_gmf_args_desc_copy(const esp_gmf_args_desc_t *head, esp_gmf_args_desc_t **new_args) +{ + if (head == NULL) { + return ESP_GMF_ERR_INVALID_ARG; + } + esp_gmf_args_desc_t *new_head = NULL; + esp_gmf_args_desc_create(head->name, head->type, head->val, head->size, &new_head); + if (new_head == NULL) { + return ESP_GMF_ERR_MEMORY_LACK; + } + new_head->offset = head->offset; + // Recursively copy the value (for array or nested structures) + if (head->val) { + esp_gmf_args_desc_copy(head->val, &new_head->val); + if (!new_head->val) { + esp_gmf_oal_free(new_head); + return ESP_GMF_ERR_MEMORY_LACK; + } + } else { + new_head->val = NULL; + } + // Recursively copy the next node + if (head->next) { + esp_gmf_args_desc_copy(head->next, &new_head->next); + if (!new_head->next) { + if (new_head->val) { + esp_gmf_oal_free(new_head->val); + } + esp_gmf_oal_free(new_head); + return ESP_GMF_ERR_MEMORY_LACK; + } + } else { + new_head->next = NULL; + } + *new_args = new_head; + return ESP_GMF_ERR_OK; +} + +/** + * @brief Count the number of nodes in a GMF argument description list, including nested lists + * + * @param[in] head Pointer to the head of the list + * + * @return + * - The total number of nodes in the list, including nested lists + */ +static inline size_t esp_gmf_args_desc_count(esp_gmf_args_desc_t *head) +{ + size_t count = 0; + const esp_gmf_args_desc_t *current = head; + while (current != NULL) { + count++; + current = current->next; + } + return count; +} + +/** + * @brief Find an argument description by its name in the linked list + * + * @param[in] head Pointer to the head of the linked list + * @param[in] wanted_name The name of the argument that is being searched for + * @param[out] wanted_arg A pointer to a pointer where the found argument description will be stored + * If the argument is found, `wanted_arg` will point to the corresponding node + * @return + * - ESP_GMF_ERR_OK Success, the argument description was found, and `wanted_arg` is updated + * - ESP_GMF_ERR_NOT_FOUND If no argument with the specified `wanted_name` is found + * - ESP_GMF_ERR_INVALID_ARG If `head`, `wanted_name`, or `wanted_arg` is `NULL` + */ +static inline esp_gmf_err_t esp_gmf_args_desc_found(esp_gmf_args_desc_t *head, const char *wanted_name, esp_gmf_args_desc_t **wanted_arg) +{ + esp_gmf_args_desc_t *current = head; + esp_gmf_args_desc_t *next; + while (current != NULL) { + next = current->next; + ESP_LOGV("GMF_ARG", "Desc_Find, %s, want:%s, offset:%d", current->name, wanted_name, current->offset); + if ((current->name == wanted_name) || (strcasecmp(current->name, wanted_name) == 0)) { + *wanted_arg = current; + ESP_LOGD("GMF_ARG", "Found %s, want:%s, offset:%d\r\n", current->name, wanted_name, current->offset); + return ESP_GMF_ERR_OK; + } else if (current->val) { + esp_gmf_args_desc_found(current->val, wanted_name, wanted_arg); + } + current = next; + } + return ESP_GMF_ERR_NOT_FOUND; +} + +/** + * @brief Extracts a specific type value from the raw data buffer + * + * @param[in] head Pointer to the argument descriptor list + * @param[in] name The name of the argument whose value needs to be extracted + * @param[in] buf The buffer containing raw data from which the value will be extracted + * @param[in] buf_len The size of the buffer `buf` + * @param[out] value Pointer to store the extracted value + * + * @return + * - ESP_OK If the value was successfully extracted from the buffer + * - ESP_ERR_NOT_FOUND If the specified argument name was not found + */ +static inline esp_gmf_err_t esp_gmf_args_extract_value(esp_gmf_args_desc_t *head, const char *name, uint8_t *buf, uint32_t buf_len, + uint32_t *value) +{ + esp_gmf_args_desc_t *args = NULL; + esp_gmf_args_desc_found(head, name, &args); + if (args) { + ESP_LOGD("GMF_ARG", "extract:%s-%p, offset:%d, sz:%ld\r\n", args->name, args, args->offset, args->size); + memcpy(value, buf + args->offset, args->size); + return ESP_GMF_ERR_OK; + } + return ESP_GMF_ERR_NOT_FOUND; +} + +/** + * @brief Sets a specific value into the raw data buffer + * + * @param[in] head Pointer to the argument descriptor list + * @param[in] name The name of the argument where the value needs to be set + * @param[in] buf The buffer where the value will be set, it stores raw data + * @param[in] value The value that will be written into the `buf` + * @param[in] value_len The size of the value in bytes + * + * @return + * - ESP_OK If the value was successfully extracted from the buffer + * - ESP_ERR_NOT_FOUND If the specified argument name was not found + */ +static inline esp_gmf_err_t esp_gmf_args_set_value(esp_gmf_args_desc_t *head, const char *name, uint8_t *buf, uint8_t *value, + uint32_t value_len) +{ + esp_gmf_args_desc_t *set_args = NULL; + esp_gmf_args_desc_found(head, name, &set_args); + ESP_LOGD("GMF_ARG", "Set, %p", set_args); + if (set_args) { + ESP_LOGD("GMF_ARG", "Set value %s, offset:%d, sz:%ld", set_args->name, set_args->offset, set_args->size); + memcpy(buf + set_args->offset, value, set_args->size); + return ESP_GMF_ERR_OK; + } + return ESP_GMF_ERR_NOT_FOUND; +} + +/** + * @brief Extract a uint8_t value from the GMF argument description list + * + * @param[in] head Pointer to the head of the argument list + * @param[in] name The name of the argument to extract + * @param[in] buf Pointer to the buffer containing argument values + * @param[in] buf_len Length of the buffer (not used in this function) + * @param[out] value Pointer to store the extracted uint64_t value + * + * @return + * - ESP_GMF_ERR_OK Value extracted successfully + * - ESP_GMF_ERR_NOT_FOUND Argument not found in the list + */ +static inline esp_gmf_err_t esp_gmf_args_extract_uint8(esp_gmf_args_desc_t *head, const char *name, uint8_t *buf, uint8_t *buf_len, + uint8_t *value) +{ + esp_gmf_args_desc_t *args = NULL; + esp_gmf_args_desc_found(head, name, &args); + if (args) { + memcpy(value, buf + args->offset, args->size); + return ESP_GMF_ERR_OK; + } + return ESP_GMF_ERR_NOT_FOUND; +} + +/** + * @brief Extract a uint16_t value from the GMF argument description list + * + * @param[in] head Pointer to the head of the argument list + * @param[in] name The name of the argument to extract + * @param[in] buf Pointer to the buffer containing argument values + * @param[in] buf_len Length of the buffer (not used in this function) + * @param[out] value Pointer to store the extracted uint64_t value + * + * @return + * - ESP_GMF_ERR_OK Value extracted successfully + * - ESP_GMF_ERR_NOT_FOUND Argument not found in the list + */ +static inline esp_gmf_err_t esp_gmf_args_extract_uint16(esp_gmf_args_desc_t *head, const char *name, uint8_t *buf, uint8_t *buf_len, + uint16_t *value) +{ + esp_gmf_args_desc_t *args = NULL; + esp_gmf_args_desc_found(head, name, &args); + if (args) { + memcpy(value, buf + args->offset, args->size); + return ESP_GMF_ERR_OK; + } + return ESP_GMF_ERR_NOT_FOUND; +} + +/** + * @brief Extract a uint32_t value from the GMF argument description list + * + * @param[in] head Pointer to the head of the argument list + * @param[in] name The name of the argument to extract + * @param[in] buf Pointer to the buffer containing argument values + * @param[in] buf_len Length of the buffer (not used in this function) + * @param[out] value Pointer to store the extracted uint64_t value + * + * @return + * - ESP_GMF_ERR_OK Value extracted successfully + * - ESP_GMF_ERR_NOT_FOUND Argument not found in the list + */ +static inline esp_gmf_err_t esp_gmf_args_extract_uint32(esp_gmf_args_desc_t *head, const char *name, uint8_t *buf, uint8_t *buf_len, + uint32_t *value) +{ + esp_gmf_args_desc_t *args = NULL; + esp_gmf_args_desc_found(head, name, &args); + if (args) { + memcpy(value, buf + args->offset, args->size); + return ESP_GMF_ERR_OK; + } + return ESP_GMF_ERR_NOT_FOUND; +} + +/** + * @brief Extract a uint64_t value from the GMF argument description list + * + * @param[in] head Pointer to the head of the argument list + * @param[in] name The name of the argument to extract + * @param[in] buf Pointer to the buffer containing argument values + * @param[in] buf_len Length of the buffer (not used in this function) + * @param[out] value Pointer to store the extracted uint64_t value + * + * @return + * - ESP_GMF_ERR_OK Value extracted successfully + * - ESP_GMF_ERR_NOT_FOUND Argument not found in the list + */ +static inline esp_gmf_err_t esp_gmf_args_extract_uint64(esp_gmf_args_desc_t *head, const char *name, uint8_t *buf, uint8_t *buf_len, + uint64_t *value) +{ + esp_gmf_args_desc_t *args = NULL; + esp_gmf_args_desc_found(head, name, &args); + if (args) { + memcpy(value, buf + args->offset, args->size); + return ESP_GMF_ERR_OK; + } + return ESP_GMF_ERR_NOT_FOUND; +} + +/** + * @brief Extract a float value from the GMF argument description list + * + * @param[in] head Pointer to the head of the argument list + * @param[in] name The name of the argument to extract + * @param[in] buf Pointer to the buffer containing argument values + * @param[in] buf_len Length of the buffer (not used in this function) + * @param[out] value Pointer to store the extracted float value + * + * @return + * - ESP_GMF_ERR_OK Value extracted successfully + * - ESP_GMF_ERR_NOT_FOUND Argument not found in the list + */ +static inline esp_gmf_err_t esp_gmf_args_extract_float(esp_gmf_args_desc_t *head, const char *name, uint8_t *buf, uint8_t *buf_len, + uint64_t *value) +{ + esp_gmf_args_desc_t *args = NULL; + esp_gmf_args_desc_found(head, name, &args); + if (args) { + memcpy(value, buf + args->offset, args->size); + return ESP_GMF_ERR_OK; + } + return ESP_GMF_ERR_NOT_FOUND; +} + +/** + * @brief Extract a double value from the GMF argument description list + * + * @param[in] head Pointer to the head of the argument list + * @param[in] name The name of the argument to extract + * @param[in] buf Pointer to the buffer containing argument values + * @param[in] buf_len Length of the buffer (not used in this function) + * @param[out] value Pointer to store the extracted double value + * + * @return + * - ESP_GMF_ERR_OK Value extracted successfully + * - ESP_GMF_ERR_NOT_FOUND Argument not found in the list + */ +static inline esp_gmf_err_t esp_gmf_args_extract_double(esp_gmf_args_desc_t *head, const char *name, uint8_t *buf, uint8_t *buf_len, + uint64_t *value) +{ + esp_gmf_args_desc_t *args = NULL; + esp_gmf_args_desc_found(head, name, &args); + if (args) { + memcpy(value, buf + args->offset, args->size); + return ESP_GMF_ERR_OK; + } + return ESP_GMF_ERR_NOT_FOUND; +} + +/** + * @brief Display the argument descriptions in the linked list + * The function uses indentation ("--") to visually represent the depth of any nested structures or arrays + * + * @param[in] head Pointer to the head of the linked list containing argument descriptions + * @param[in] func The name of the function from which this logging function is called + * @param[in] line The line number in the source code from which this function is called + */ +static void inline esp_gmf_args_desc_show(esp_gmf_args_desc_t *head, const char *func, size_t line) +{ + esp_gmf_args_desc_t *current = head; + esp_gmf_args_desc_t *next; + static uint8_t k = 0; + if (k == 0) { + printf("ARGS DESC on [%s,line:%d]\n", func, line); + } + int n = 16 - (k * 2); + while (current != NULL) { + next = current->next; + for (int i = 0; i < k; ++i) { + printf("--"); + } + printf("name:%-*s offset:%-8d sz:%-8ld\r\n", n, current->name, current->offset, current->size); + if (current->val != NULL) { + k++; + esp_gmf_args_desc_show(current->val, func, line); + } + current = next; + } + if (k) { + k--; + } else { + printf("\n"); + } +} + +/** + * @brief A macro to print the argument descriptor details + */ +#define ESP_GMF_ARGS_DESC_PRINT(x) esp_gmf_args_desc_show((x), __func__, __LINE__) + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/include/esp_gmf_audio_element.h b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/include/esp_gmf_audio_element.h new file mode 100644 index 0000000..654ccd6 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/include/esp_gmf_audio_element.h @@ -0,0 +1,127 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#include "esp_gmf_element.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * @brief GMF audio element structure + */ +typedef struct _esp_gmf_audio_element { + struct esp_gmf_element base; /*!< Base element structure */ + esp_gmf_info_sound_t snd_info; /*!< Sound information */ + esp_gmf_info_file_t file_info; /*!< File information */ + void *lock; /*!< Lock for thread safety */ +} esp_gmf_audio_element_t; + +/** GMF audio element handle */ +typedef void *esp_gmf_audio_element_handle_t; + +/** + * @brief Initialize a GMF audio element with the given configuration + * + * @param[in] handle GMF audio element handle to initialize + * @param[in] config Pointer to the configuration structure + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + * - ESP_GMF_ERR_MEMORY_LACK Memory allocate failed + */ +esp_gmf_err_t esp_gmf_audio_el_init(esp_gmf_audio_element_handle_t handle, esp_gmf_element_cfg_t *config); + +/** + * @brief Get sound information from a GMF audio element + * + * @param[in] handle GMF audio element handle + * @param[out] info Pointer to store the sound information + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_audio_el_get_snd_info(esp_gmf_audio_element_handle_t handle, esp_gmf_info_sound_t *info); + +/** + * @brief Set sound information for a GMF audio element + * + * @param[in] handle GMF audio element handle + * @param[in] info Pointer to the sound information to set + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_audio_el_set_snd_info(esp_gmf_audio_element_handle_t handle, esp_gmf_info_sound_t *info); + +/** + * @brief Get file information from a GMF audio element + * + * @param[in] handle GMF audio element handle + * @param[out] info Pointer to store the file information + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_audio_el_get_file_info(esp_gmf_audio_element_handle_t handle, esp_gmf_info_file_t *info); + +/** + * @brief Set file information for a GMF audio element + * + * @param[in] handle GMF audio element handle + * @param[in] info Pointer to the file information to set + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_audio_el_set_file_info(esp_gmf_audio_element_handle_t handle, esp_gmf_info_file_t *info); + +/** + * @brief Set the file size for a GMF audio element + * + * @param[in] handle GMF audio element handle + * @param[in] size New file size to update + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_audio_el_set_file_size(esp_gmf_audio_element_handle_t handle, uint64_t size); + +/** + * @brief Update the file position for a GMF audio element + * + * @param[in] handle GMF audio element handle + * @param[in] pos New file position to update + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_audio_el_update_file_pos(esp_gmf_audio_element_handle_t handle, uint64_t pos); + +/** + * @brief Deinitialize a GMF audio element, freeing associated resources + * + * @param[in] handle GMF audio element handle to deinitialize + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_audio_el_deinit(esp_gmf_audio_element_handle_t handle); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/include/esp_gmf_cache.h b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/include/esp_gmf_cache.h new file mode 100644 index 0000000..4c05f14 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/include/esp_gmf_cache.h @@ -0,0 +1,266 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#include +#include "esp_gmf_oal_mem.h" +#include "esp_gmf_payload.h" + +/** + * @brief This GMF payload cache module is designed to cache a data block of a specified size. Each call to acquire returns this + * fixed-size block. If the acquired data is smaller than the expected size, new data must be loaded. When sufficient data + * is available, release clears the cached block. + * It is commonly used in scenarios where an element can only process a fixed data size, but the input data length is variable. + * For example, if an element requires exactly 1350 bytes per processing cycle but receives input of varying lengths, you can + * create a cache with `esp_gmf_cache_new(1350, out_handle)`, and the element can then fetch data using `esp_gmf_cache_acquire` + */ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * @brief Structure representing a cache for GMF payloads + */ +typedef struct { + uint8_t *buf; /*!< Pointer to the cache buffer */ + uint32_t buf_len; /*!< Total allocated size of the cache buffer */ + uint32_t buf_filled; /*!< Amount of data currently stored in the buffer */ + esp_gmf_payload_t origin_load; /*!< Original payload from which data is copied */ + esp_gmf_payload_t load; /*!< Current payload structure for acquiring data */ +} esp_gmf_cache_t; + +/** + * @brief Create a new GMF cache instance + * + * @param[in] len The size of the cache buffer to allocate + * @param[out] handle Pointer to the cache handle to be initialized + * + * @return + * - ESP_GMF_ERR_OK Success, cache is initialized + * - ESP_GMF_ERR_INVALID_ARG If `handle` is NULL + * - ESP_GMF_ERR_MEMORY_LACK If memory allocation fails + */ +static inline esp_gmf_err_t esp_gmf_cache_new(uint32_t len, esp_gmf_cache_t **handle) +{ + if (handle == NULL) { + return ESP_GMF_ERR_INVALID_ARG; + } + *handle = (esp_gmf_cache_t *)esp_gmf_oal_calloc(1, sizeof(esp_gmf_cache_t)); + if (*handle == NULL) { + ESP_LOGE("GMF_CACHE", "Failed to allocate the cache instance"); + return ESP_GMF_ERR_MEMORY_LACK; + } + (*handle)->buf = (uint8_t *)esp_gmf_oal_calloc(1, len); + if ((*handle)->buf == NULL) { + esp_gmf_oal_free(*handle); + ESP_LOGE("GMF_CACHE", "Failed to allocate the cache buffer, size:%ld", len); + *handle = NULL; + return ESP_GMF_ERR_MEMORY_LACK; + } + (*handle)->buf_len = len; + (*handle)->buf_filled = 0; + return ESP_GMF_ERR_OK; +} + +/** + * @brief Delete a GMF cache instance and free associated memory + * + * @param[in] handle Pointer to the cache handle + * + * @return + * - ESP_GMF_ERR_OK Success, cache is deleted + * - ESP_GMF_ERR_INVALID_ARG If `handle` is NULL + */ +static inline esp_gmf_err_t esp_gmf_cache_delete(esp_gmf_cache_t *handle) +{ + if (handle == NULL) { + return ESP_GMF_ERR_INVALID_ARG; + } + if (handle->buf) { + esp_gmf_oal_free(handle->buf); + } + esp_gmf_oal_free(handle); + return ESP_GMF_ERR_OK; +} + +/** + * @brief Check if the cache is ready to load new data + * If the return `is_ready` is true, `esp_gmf_cache_load` can be called to load a new payload + * + * @param[in] handle Pointer to the cache handle + * @param[out] is_ready Pointer to a boolean flag indicating whether new data can be loaded + * + * @return + * - ESP_GMF_ERR_OK Success, `is_ready` is set + * - ESP_GMF_ERR_INVALID_ARG If `handle` or `is_ready` is NULL + */ +static inline esp_gmf_err_t esp_gmf_cache_ready_for_load(esp_gmf_cache_t *handle, bool *is_ready) +{ + if ((handle == NULL) || (is_ready == NULL)) { + return ESP_GMF_ERR_INVALID_ARG; + } + *is_ready = false; + if (handle->origin_load.valid_size == 0) { + *is_ready = true; + } + return ESP_GMF_ERR_OK; +} + +/** + * @brief Load new payload data into the cache + * Call `esp_gmf_cache_ready_for_load` before use to check if it is ready to load a new payload + * + * @param[in] handle Pointer to the cache handle + * @param[in] load_in Pointer to the payload data to be loaded + * + * @return + * - ESP_GMF_ERR_OK Success, payload is loaded + * - ESP_GMF_ERR_INVALID_ARG If `handle` or `load_in` is NULL + * - ESP_GMF_ERR_INVALID_STATE If previous data has not been consumed + */ +static inline esp_gmf_err_t esp_gmf_cache_load(esp_gmf_cache_t *handle, const esp_gmf_payload_t *load_in) +{ + if ((handle == NULL) || (load_in == NULL)) { + return ESP_GMF_ERR_INVALID_ARG; + } + if (handle->origin_load.valid_size != 0) { + ESP_LOGE("GMF_CACHE", "Reloading while previous load underuse, call esp_gmf_cache_ready_for_load check firstly, filled: %ld,\ + orig_valid: %d", handle->buf_filled, handle->origin_load.valid_size); + return ESP_GMF_ERR_INVALID_STATE; + } + memcpy(&handle->origin_load, load_in, sizeof(esp_gmf_payload_t)); + return ESP_GMF_ERR_OK; +} + +/** + * @brief Acquire a data chunk of the expected size from the cache + * + * @note + * - This function must be used in conjunction with `esp_gmf_cache_release` + * - The `expected_size` should match the internal buffer length + * - If `expected_size` exceeds the current buffer length, the buffer will be reallocated + * + * @param[in] handle Pointer to the cache handle + * @param[in] expected_size The requested data size in bytes + * @param[out] load_out Pointer to store the acquired payload data + * + * @return + * - ESP_GMF_ERR_OK Data successfully acquired + * - ESP_GMF_ERR_INVALID_ARG If `handle` or `load_out` is NULL + * - ESP_GMF_ERR_MEMORY_LACK Insufficient memory to allocate a new buffer + * - ESP_GMF_ERR_NOT_ENOUGH Cached data size less than expected size + */ +static inline esp_gmf_err_t esp_gmf_cache_acquire(esp_gmf_cache_t *handle, uint32_t expected_size, esp_gmf_payload_t **load_out) +{ + if ((handle == NULL) || (load_out == NULL)) { + return ESP_GMF_ERR_INVALID_ARG; + } + if (expected_size > handle->buf_len) { + uint8_t *buf = (uint8_t *)esp_gmf_oal_realloc(handle->buf, expected_size); + ESP_GMF_MEM_CHECK("GMF_CACHE", buf, return ESP_GMF_ERR_MEMORY_LACK;); + handle->buf = buf; + ESP_LOGI("GMF_CACHE", "Reallocate the cache buffer from %ld to %ld bytes, %p", handle->buf_len, expected_size, handle->buf); + handle->buf_len = expected_size; + } + + *load_out = &handle->load; + ESP_LOGD("GMF_CACHE", "ACQ, filled: %ld, Origin_valid_size: %d", handle->buf_filled, handle->origin_load.valid_size); + + /** + * 1. If the original buffer has sufficient data, return its address directly to the user + * 2. Copy the remaining data from the original buffer to the cache buffer + * 3. If the original buffer does not have enough data for the user, provide the cached buffer address instead + */ + bool cache_enough = false; + if (handle->buf_filled == 0) { + if (handle->origin_load.valid_size >= expected_size) { + (*load_out)->buf = handle->origin_load.buf; + (*load_out)->buf_length = expected_size; + (*load_out)->valid_size = expected_size; + (*load_out)->is_done = (handle->origin_load.valid_size == expected_size) ? handle->origin_load.is_done : false; + (*load_out)->pts = handle->origin_load.pts; + handle->origin_load.valid_size -= expected_size; + handle->origin_load.buf += expected_size; + cache_enough = true; + } + } + if (handle->buf_filled || (handle->origin_load.valid_size < expected_size)) { + int n = (handle->buf_len - handle->buf_filled) > handle->origin_load.valid_size + ? handle->origin_load.valid_size + : (handle->buf_len - handle->buf_filled); + memcpy(handle->buf + handle->buf_filled, handle->origin_load.buf, n); + handle->origin_load.buf += n; + handle->origin_load.valid_size -= n; + handle->buf_filled += n; + if (handle->buf_filled >= expected_size) { + cache_enough = true; + } + ESP_LOGD("GMF_CACHE", "ACQ, filled: %ld, used size: %d, origin_left_size: %d", handle->buf_filled, n, handle->origin_load.valid_size); + } + if ((cache_enough == false) && (handle->origin_load.is_done == false)) { + return ESP_GMF_ERR_NOT_ENOUGH; + } + if ((*load_out)->valid_size == 0) { + (*load_out)->buf = handle->buf; + (*load_out)->buf_length = handle->buf_len; + (*load_out)->valid_size = handle->buf_filled; + // FIXME If the data is split into several pieces, the pts remains the same. + (*load_out)->pts = handle->origin_load.pts; + (*load_out)->is_done = ((handle->origin_load.valid_size == 0) && handle->origin_load.is_done); + } + return ESP_GMF_ERR_OK; +} + +/** + * @brief Release the payload data previously acquired with `esp_gmf_cache_acquire` + * + * @note The filled portion of the buffer is cleared only if its size equals the buffer length + * + * @param[in] handle Pointer to the cache handle + * @param[in] load Pointer to the payload data to be released + * + * @return + * - ESP_GMF_ERR_OK Payload data successfully released + * - ESP_GMF_ERR_INVALID_ARG If either `handle` or `load` is NULL + */ +static inline esp_gmf_err_t esp_gmf_cache_release(esp_gmf_cache_t *handle, esp_gmf_payload_t *load) +{ + if ((handle == NULL) || (load == NULL)) { + return ESP_GMF_ERR_INVALID_ARG; + } + ESP_LOGD("GMF_CACHE", "RLS, buf:%p-%p, filled: %ld, origin_valid_size: %d", load->buf, handle->buf, handle->buf_filled, handle->origin_load.valid_size); + if ((load->buf == handle->buf) && (handle->buf_filled == handle->buf_len)) { + handle->buf_filled = 0; + } + memset(&handle->load, 0, sizeof(esp_gmf_payload_t)); + return ESP_GMF_ERR_OK; +} + +/** + * @brief Get the total amount of cached data + * + * @param[in] handle Pointer to the cache handle + * @param[out] filled Pointer to store the filled data size + * + * @return + * - ESP_GMF_ERR_OK Success, `filled` is set + * - ESP_GMF_ERR_INVALID_ARG If `handle` or `filled` is NULL + */ +static inline esp_gmf_err_t esp_gmf_cache_get_cached_size(esp_gmf_cache_t *handle, int *filled) +{ + if ((handle == NULL) || (filled == NULL)) { + return ESP_GMF_ERR_INVALID_ARG; + } + *filled = handle->buf_filled + handle->origin_load.valid_size; + return ESP_GMF_ERR_OK; +} + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/include/esp_gmf_cap.h b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/include/esp_gmf_cap.h new file mode 100644 index 0000000..7438f03 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/include/esp_gmf_cap.h @@ -0,0 +1,257 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#include +#include "stdbool.h" +#include "esp_gmf_err.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * @brief Enumeration for property types in capabilities + */ +typedef enum { + ESP_GMF_PROP_TYPE_NONE = 0, /*!< No property type specified */ + ESP_GMF_PROP_TYPE_DISCRETE = 1, /*!< Discrete property with distinct values */ + ESP_GMF_PROP_TYPE_STEPWISE = 2, /*!< Stepwise property with specified min, max, and step */ + ESP_GMF_PROP_TYPE_MULTIPLE = 3, /*!< Multiple property with min, max, and multiplicative factor */ + ESP_GMF_PROP_TYPE_CONSTANT = 4, /*!< Constant property with a single value */ +} esp_gmf_prop_type; + +/** + * @brief Structure for discrete properties in capabilities + */ +typedef struct { + void *collection; /*!< Pointer to the collection of discrete values */ + uint16_t item_num; /*!< Number of items in the collection */ + uint16_t item_size; /*!< Size of each item in bytes (1, 2, or 4) */ +} esp_gmf_prop_discrete_t; + +/** + * @brief Structure for stepwise properties in capabilities, e.g. An = min + (n-1) * step + */ +typedef struct { + uint32_t min; /*!< Minimum value of the property */ + uint32_t step; /*!< Step size between values */ + uint32_t max; /*!< Maximum value of the property */ +} esp_gmf_prop_stepwise_t; + +/** + * @brief Structure for multiple properties in capabilities, e.g. M = factor * n, n:1~N + */ +typedef struct { + uint32_t min; /*!< Minimum value of the property */ + uint16_t factor; /*!< Multiplicative factor between values */ + uint32_t max; /*!< Maximum value of the property */ +} esp_gmf_prop_multiple_t; + +/** + * @brief Structure for constant properties in capabilities + */ +typedef struct { + uint32_t data; /*!< The constant value of the property */ +} esp_gmf_prop_constant_t; + +/** + * @brief Structure for attributes of a capability + */ +typedef struct esp_gmf_cap_attr { + uint32_t fourcc; /*!< Unique 4-character code identifying the attribute */ + uint8_t index; /*!< Index of the discrete attribute only */ + uint8_t prop_type; /*!< Type of property (discrete, stepwise, or multiple) */ + union { + esp_gmf_prop_discrete_t d; /*!< Discrete property data */ + esp_gmf_prop_stepwise_t s; /*!< Stepwise property data */ + esp_gmf_prop_multiple_t m; /*!< Multiple property data */ + esp_gmf_prop_constant_t c; /*!< Constant property data */ + } value; /*!< Union holding the property value based on prop_type */ +} esp_gmf_cap_attr_t; + +/** + * @brief Function pointer type for iterating over capability attributes + */ +typedef esp_gmf_err_t (*cap_attr_iter_fun)(uint32_t index, esp_gmf_cap_attr_t *attr); + +/** + * @brief Structure for performance metrics of a capability + */ +typedef struct { + uint32_t oper_per_sec; /*!< Number of operations per second (or a percentage) */ +} esp_gmf_cap_perf_t; + +/** + * @brief Structure representing a capability with performance metrics and attributes + */ +typedef struct esp_gmf_cap { + struct esp_gmf_cap *next; /*!< Pointer to the next capability in a linked list */ + uint64_t cap_eightcc; /*!< Unique 8-character code for identifying the capability */ + esp_gmf_cap_perf_t perf; /*!< Performance metrics associated with the capability */ + cap_attr_iter_fun attr_fun; /*!< Function pointer for iterating over attributes in the capability */ + void *attr_fun_ctx; /*!< Function pointer for iterating over attributes in the capability */ +} esp_gmf_cap_t; + +/** + * @brief Macro to set discrete attribute parameters in esp_gmf_cap_attr_t + */ +#define ESP_GMF_CAP_ATTR_SET_DISCRETE(attr, code, coll, num, size) do { \ + (attr)->fourcc = (code); \ + (attr)->index = 0; \ + (attr)->prop_type = ESP_GMF_PROP_TYPE_DISCRETE; \ + (attr)->value.d.collection = (void *)(coll); \ + (attr)->value.d.item_num = (num); \ + (attr)->value.d.item_size = (size); \ +} while (0) + +/** + * @brief Macro to set stepwise attribute parameters in esp_gmf_cap_attr_t + */ +#define ESP_GMF_CAP_ATTR_SET_STEPWISE(attr, code, min_val, step_size, max_val) do { \ + (attr)->fourcc = (code); \ + (attr)->index = 0; \ + (attr)->prop_type = ESP_GMF_PROP_TYPE_STEPWISE; \ + (attr)->value.s.min = (min_val); \ + (attr)->value.s.step = (step_size); \ + (attr)->value.s.max = (max_val); \ +} while (0) + +/** + * @brief Macro to set multiple attribute parameters in esp_gmf_cap_attr_t + */ +#define ESP_GMF_CAP_ATTR_SET_MULTIPLE(attr, code, min_val, factor_val, max_val) do { \ + (attr)->fourcc = (code); \ + (attr)->index = 0; \ + (attr)->prop_type = ESP_GMF_PROP_TYPE_MULTIPLE; \ + (attr)->value.m.min = (min_val); \ + (attr)->value.m.factor = (factor_val); \ + (attr)->value.m.max = (max_val); \ +} while (0) + +/** + * @brief Macro to set constant attribute parameters in esp_gmf_cap_attr_t + */ +#define ESP_GMF_CAP_ATTR_SET_CONSTANT(attr, code, factor_val) do { \ + (attr)->fourcc = (code); \ + (attr)->index = 0; \ + (attr)->prop_type = ESP_GMF_PROP_TYPE_CONSTANT; \ + (attr)->value.c.data = (factor_val); \ +} while (0) + +/** + * @brief Create a new capability node based on the given cap_value and append it to the capability linked list + * + * @param[in, out] caps Pointer to the head of the capability linked list. If *caps is NULL, a new list will be created + * @param[in] cap_value Pointer to the capability data to be appended + * + * @return + * - ESP_GMF_ERR_OK Successfully appended the capability + * - ESP_GMF_ERR_MEMORY_LACK Insufficient memory to create a new capability node + * - ESP_GMF_ERR_INVALID_ARG Invalid argument (e.g., NULL pointer) + */ +esp_gmf_err_t esp_gmf_cap_append(esp_gmf_cap_t **caps, esp_gmf_cap_t *cap_value); + +/** + * @brief Destroy a linked list of capabilities + * + * @param[in] caps Pointer to the head of the capability linked list + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG The argument is invalid + */ +esp_gmf_err_t esp_gmf_cap_destroy(esp_gmf_cap_t *caps); + +/** + * @brief Fetch a capability node from a capability list using an 8-character code + * + * @param[in] caps Pointer to the head of the capability list to search + * @param[in] eight_cc 8-character code representing the capability to find (as uint64_t) + * @param[out] out_caps Pointer to the variable that will receive the found capability node + * + * @return + * - ESP_GMF_ERR_OK The capability was found successfully + * - ESP_GMF_ERR_INVALID_ARG One or more arguments are invalid (e.g., NULL pointers) + * - ESP_GMF_ERR_NOT_FOUND No matching capability was found; `*out_caps` will be set to NULL + */ +esp_gmf_err_t esp_gmf_cap_fetch_node(esp_gmf_cap_t *caps, uint64_t eight_cc, esp_gmf_cap_t **out_caps); + +/** + * @brief Iterate over capability attributes by index + * + * @param[in] caps Pointer to the capability being iterated + * @param[in] attr_index Index of the attribute to retrieve + * @param[out] out_attr Pointer to store the retrieved attribute + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG The argument isinvalid + * - ESP_GMF_ERR_NOT_SUPPORT The cap_attr_iter_fun is invalid + * - ESP_GMF_ERR_NOT_FOUND The attr_index is out of range + */ +esp_gmf_err_t esp_gmf_cap_iterate_attr(esp_gmf_cap_t *caps, uint32_t attr_index, esp_gmf_cap_attr_t *out_attr); + +/** + * @brief Find an attribute from a given capability using a 4-character code + * + * @param[in] caps Pointer to the capability to search + * @param[in] cc Character code of the attribute to find + * @param[out] out_attr Pointer to store the found attribute + * + * @return + * - ESP_GMF_ERR_OK The attribute is found + * - ESP_GMF_ERR_INVALID_ARG The argument is invalid + * - ESP_GMF_ERR_NOT_FOUND The attribute is not found + */ +esp_gmf_err_t esp_gmf_cap_find_attr(esp_gmf_cap_t *caps, uint32_t cc, esp_gmf_cap_attr_t *out_attr); + +/** + * @brief Check if a given value is supported by an attribute + * + * @param[in] attr Pointer to the attribute to check + * @param[in] val Value to check + * @param[out] is_support Pointer to boolean indicating if the value is supported + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG The argument is invalid + * - ESP_GMF_ERR_NOT_SUPPORT If the value or attribute type is not support + */ +esp_gmf_err_t esp_gmf_cap_attr_check_value(esp_gmf_cap_attr_t *attr, uint32_t val, bool *is_support); + +/** + * @brief Iterate through values of an attribute + * + * @param[in,out] attr Pointer to the attribute iterator + * @param[out] val Pointer to store the current value + * @param[out] is_last Pointer to boolean indicating if this is the last value + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG The argument is invalid + * - ESP_GMF_ERR_NOT_SUPPORT If the value or attribute type is not support + */ +esp_gmf_err_t esp_gmf_cap_attr_iterator_value(esp_gmf_cap_attr_t *attr, uint32_t *val, bool *is_last); + +/** + * @brief Retrieve the first valid value of an attribute + * + * @param[in,out] iter Pointer to the attribute + * @param[out] val Pointer to store the retrieved value + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument (e.g., `iter` or `val` is NULL) + * - ESP_GMF_ERR_NOT_SUPPORT The attribute type does not support this operation + */ +esp_gmf_err_t esp_gmf_cap_attr_get_first_value(esp_gmf_cap_attr_t *attr, uint32_t *val); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/include/esp_gmf_element.h b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/include/esp_gmf_element.h new file mode 100644 index 0000000..9f903fc --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/include/esp_gmf_element.h @@ -0,0 +1,496 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#include "esp_gmf_err.h" +#include "esp_gmf_obj.h" +#include "esp_gmf_job.h" +#include "esp_gmf_event.h" +#include "esp_gmf_info.h" +#include "esp_gmf_port.h" +#include "esp_gmf_method.h" +#include "esp_gmf_cap.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define ESP_GMF_ELEMENT_JOB_OPEN BIT(0) +#define ESP_GMF_ELEMENT_JOB_PROCESS BIT(1) +#define ESP_GMF_ELEMENT_JOB_CLOSE BIT(2) +#define ESP_GMF_ELEMENT_PORT_DATA_SIZE_DEFAULT (768) +#define ESP_GMF_ELEMENT_PORT_ADDR_ALIGNED_DEFAULT (16) // 16 bytes aligned + +#define ESP_GMF_MAX_DELAY (0xFFFFFFFFUL) + +#define ESP_GMF_ELEMENT_GET(x) ((esp_gmf_element_t *)x) +#define ESP_GMF_ELEMENT_GET_IN_PORT(x) (((esp_gmf_element_t *)x)->in) +#define ESP_GMF_ELEMENT_GET_OUT_PORT(x) (((esp_gmf_element_t *)x)->out) +#define ESP_GMF_ELEMENT_GET_DEPENDENCY(x) (((esp_gmf_element_t *)x)->dependency) + +#define ESP_GMF_ELEMENT_IN_PORT_ATTR_SET(attr, caps, addr_aligned, size_aligned, port_type, acq_data_size) do { \ + (attr).cap = (uint8_t)(caps); \ + (attr).port.buf_addr_aligned = (uint8_t)(addr_aligned); \ + (attr).port.buf_size_aligned = (uint8_t)(size_aligned); \ + (attr).port.dir = (ESP_GMF_PORT_DIR_IN); \ + (attr).port.type = (uint8_t)(port_type); \ + (attr).data_size = (int)(acq_data_size); \ +} while (0) + +#define ESP_GMF_ELEMENT_OUT_PORT_ATTR_SET(attr, caps, addr_aligned, size_aligned, port_type, acq_data_size) do { \ + (attr).cap = (uint8_t)(caps); \ + (attr).port.buf_addr_aligned = (uint8_t)(addr_aligned); \ + (attr).port.buf_size_aligned = (uint8_t)(size_aligned); \ + (attr).port.dir = (ESP_GMF_PORT_DIR_OUT); \ + (attr).port.type = (uint8_t)(port_type); \ + (attr).data_size = (int)(acq_data_size); \ +} while (0) + +/** + * @brief Defining the bit mask for an element's port capabilities + */ +#define ESP_GMF_EL_PORT_CAP_SINGLE (1) /*!< Bit0 for single port capability */ +#define ESP_GMF_EL_PORT_CAP_MULTI (2) /*!< Bit1 for multi-port capability */ + +/** + * @brief The GMF element handle + */ +typedef void *esp_gmf_element_handle_t; + +/** + * @brief Structure defining the attributes of an element's port + */ +typedef struct { + uint8_t cap; /*!< An element can connect to one or more capability ports */ + esp_gmf_port_attr_t port; /*!< Port attributes */ + int data_size; /*!< A minimum data size for element acquisition operations, + recommended for all elements, even those without specific processing requirements */ +} esp_gmf_element_port_attr_t; + +/** + * @brief Function pointer type for load element capability + */ +typedef esp_gmf_err_t (*esp_gmf_load_caps_func)(esp_gmf_element_handle_t handle); + +/** + * @brief Function pointer type for load element method + */ +typedef esp_gmf_err_t (*esp_gmf_load_method_func)(esp_gmf_element_handle_t handle); +/** + * @brief Structure defining the operations of an element + */ +typedef struct { + esp_gmf_job_func open; /*!< Function to open the element */ + esp_gmf_job_func process; /*!< Function to process the element */ + esp_gmf_job_func close; /*!< Function to close the element */ + esp_gmf_load_caps_func load_caps; /*!< Function to load element capability description */ + esp_gmf_load_method_func load_methods; /*!< Function to load element methods */ + esp_gmf_event_cb event_receiver; /*!< Event receiver function */ +} esp_gmf_element_ops_t; + +/** + * @brief Structure representing a GMF element + */ +typedef struct esp_gmf_element { + esp_gmf_obj_t base; /*!< Base object */ + esp_gmf_element_ops_t ops; /*!< Operations */ + uint8_t job_mask; /*!< Job mask */ + + esp_gmf_port_t *in; /*!< Input port */ + esp_gmf_element_port_attr_t in_attr; /*!< Input port attributes */ + + esp_gmf_port_t *out; /*!< Output port */ + esp_gmf_element_port_attr_t out_attr; /*!< Output port attributes */ + + /* Properties */ + esp_gmf_event_state_t init_state; /*!< Initial state */ + esp_gmf_event_state_t cur_state; /*!< Current state */ + esp_gmf_event_cb event_func; /*!< Event function */ + esp_gmf_method_t *method; /*!< It can access the data members and member functions of the objects */ + esp_gmf_cap_t *caps; /*!< Element capabilities */ + + /* Protect */ + void *ctx; /*!< User Context */ + uint8_t dependency : 1; /*!< Indicates if the element depends on other information to open */ +} esp_gmf_element_t; + +/** + * @brief Configuration structure for a GMF element + */ +typedef struct { + void *ctx; /*!< User context */ + esp_gmf_event_cb cb; /*!< Callback function */ + esp_gmf_element_port_attr_t in_attr; /*!< Input port attributes */ + esp_gmf_element_port_attr_t out_attr; /*!< Output port attributes */ + bool dependency; /*!< Indicates if the element depends on other information to open */ +} esp_gmf_element_cfg_t; + +/** + * @brief Initialize the given element with the configuration + * + * @param[in] handle GMF element handle to initialize + * @param[in] config Pointer to the configuration structure + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_element_init(esp_gmf_element_handle_t handle, esp_gmf_element_cfg_t *config); + +/** + * @brief Deinitialize the specific element, freeing associated resources + * + * @param[in] handle GMF element handle to deinitialize + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_element_deinit(esp_gmf_element_handle_t handle); + +/** + * @brief Set the event callback function for the specific element + * + * @param[in] handle GMF element handle + * @param[in] cb Event callback function + * @param[in] ctx Context for the callback function + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_element_set_event_func(esp_gmf_element_handle_t handle, esp_gmf_event_cb cb, void *ctx); + +/** + * @brief Register an input port for the specific element + * + * @note The registered port will be destroyed when `esp_gmf_element_unregister_out_port` is called + * + * @param[in] handle GMF element handle + * @param[in] io_inst port handle to register + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid arguments + * - ESP_GMF_ERR_NOT_SUPPORT The specified port type is not supported, or the element can't connect more port + */ +esp_gmf_err_t esp_gmf_element_register_in_port(esp_gmf_element_handle_t handle, esp_gmf_port_handle_t io_inst); + +/** + * @brief Unregister an input port from the specific element + * If `io_inst` is NULL, it unregisters all ports of the element + * + * @param[in] handle GMF element handle + * @param[in] io_inst Input port handle to unregister + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + * - ESP_GMF_ERR_NOT_FOUND The specified port was not found + */ +esp_gmf_err_t esp_gmf_element_unregister_in_port(esp_gmf_element_handle_t handle, esp_gmf_port_handle_t io_inst); + +/** + * @brief Register an output port for the specific element + * + * @note The registered port will be destroyed when `esp_gmf_element_unregister_out_port` is called + * + * @param[in] handle GMF element handle + * @param[in] io_inst Output port handle to register + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid arguments + * - ESP_GMF_ERR_NOT_SUPPORT The specified port type is not supported, or the element can't connect more port + */ +esp_gmf_err_t esp_gmf_element_register_out_port(esp_gmf_element_handle_t handle, esp_gmf_port_handle_t io_inst); + +/** + * @brief Unregister an output port from the specific element + * If `io_inst` is NULL, it unregisters all ports of the element + * + * @param[in] handle GMF element handle + * @param[in] io_inst Output port handle to unregister + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + * - ESP_GMF_ERR_NOT_FOUND The specified port was not found + */ +esp_gmf_err_t esp_gmf_element_unregister_out_port(esp_gmf_element_handle_t handle, esp_gmf_port_handle_t io_inst); + +/** + * @brief Link a new element to the given element + * + * @param[in] handle Given element handle + * @param[in] new_el New element handle to link + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_element_link_el(esp_gmf_element_handle_t handle, esp_gmf_element_handle_t new_el); + +/** + * @brief Get the next linked element by the given element + * + * @param[in] handle Given element handle + * @param[in] next_el Next element handle + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_element_get_next_el(esp_gmf_element_handle_t handle, esp_gmf_element_handle_t *next_el); + +/** + * @brief Get the previous linked element by the given element + * + * @param[in] handle Given element handle + * @param[in] prev_el Previous element handle + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_element_get_prev_el(esp_gmf_element_handle_t handle, esp_gmf_element_handle_t *prev_el); + +/** + * @brief Process the open phase for the specific element + * + * @param[in] handle GMF element handle + * @param[in] para Pointer to the parameters for processing + * + * @return + * - ESP_GMF_JOB_ERR_DONE Indicating the job has been completed + * - ESP_GMF_JOB_ERR_CONTINUE Indicating the job should continue + * - ESP_GMF_JOB_ERR_OK Indicating the job has executed successfully + * - ESP_GMF_JOB_ERR_FAIL Indicating the job has failed to execute + */ +esp_gmf_job_err_t esp_gmf_element_process_open(esp_gmf_element_handle_t handle, void *para); + +/** + * @brief Process the close phase for the specific element + * + * @param[in] handle GMF element handle + * @param[in] para Pointer to the parameters for processing + * + * @return + * - ESP_GMF_JOB_ERR_DONE Indicating the job has been completed + * - ESP_GMF_JOB_ERR_CONTINUE Indicating the job should continue + * - ESP_GMF_JOB_ERR_OK Indicating the job has executed successfully + * - ESP_GMF_JOB_ERR_FAIL Indicating the job has failed to execute + */ +esp_gmf_job_err_t esp_gmf_element_process_close(esp_gmf_element_handle_t handle, void *para); + +/** + * @brief Process the running phase for the specific element + * + * @param[in] handle GMF element handle + * @param[in] para Pointer to the parameters for processing + * + * @return + * - ESP_GMF_JOB_ERR_DONE Indicating the job has been completed + * - ESP_GMF_JOB_ERR_CONTINUE Indicating the job should continue + * - ESP_GMF_JOB_ERR_OK Indicating the job has executed successfully + * - ESP_GMF_JOB_ERR_FAIL Indicating the job has failed to execute + */ +esp_gmf_job_err_t esp_gmf_element_process_running(esp_gmf_element_handle_t handle, void *para); + +/** + * @brief Set the state of the specific element + * + * @param[in] handle GMF element handle + * @param[in] new_state New state to set + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG If the handle is invalid + */ +esp_gmf_err_t esp_gmf_element_set_state(esp_gmf_element_handle_t handle, esp_gmf_event_state_t new_state); + +/** + * @brief Get the state of the specific element + * + * @param[in] handle GMF element handle + * @param[out] state Pointer to store the current state + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG If the handle is invalid + */ +esp_gmf_err_t esp_gmf_element_get_state(esp_gmf_element_handle_t handle, esp_gmf_event_state_t *state); + +/** + * @brief Reset the state of the specific element to its initial state and clean the `job_mask` + * + * @param[in] handle GMF element handle + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG If the handle is invalid + */ +esp_gmf_err_t esp_gmf_element_reset_state(esp_gmf_element_handle_t handle); + +/** + * @brief Reset the ports of the specific element to their initial state + * + * @param[in] handle GMF element handle + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG If the handle is invalid + */ +esp_gmf_err_t esp_gmf_element_reset_port(esp_gmf_element_handle_t handle); + +/** + * @brief Receive an event packet for the specific element + * + * @param[in] handle GMF element handle + * @param[in] event Pointer to the event packet + * @param[in] ctx Context for event processing + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG If the handle is invalid + */ +esp_gmf_err_t esp_gmf_element_receive_event(esp_gmf_element_handle_t handle, esp_gmf_event_pkt_t *event, void *ctx); + +/** + * @brief Set the job mask of the GMF element to the given mask value + * + * @param[in] handle GMF element handle + * @param[in] mask Job mask to set + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG If the handle is invalid + */ +esp_gmf_err_t esp_gmf_element_set_job_mask(esp_gmf_element_handle_t handle, uint16_t mask); + +/** + * @brief Update the job mask for the specific element by performing a bitwise OR operation with the given mask value + * + * @param[in] handle GMF element handle + * @param[in] mask Job mask to apply + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG If the handle is invalid + */ +esp_gmf_err_t esp_gmf_element_change_job_mask(esp_gmf_element_handle_t handle, uint16_t mask); + +/** + * @brief Get the job mask for the specific element + * + * @param[in] handle GMF element handle + * @param[out] mask Pointer to store the job mask + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG If the handle is invalid + */ +esp_gmf_err_t esp_gmf_element_get_job_mask(esp_gmf_element_handle_t handle, uint16_t *mask); + +/** + * @brief Notify the specific element about sound information + * + * @param[in] handle GMF element handle + * @param[in] info Pointer to sound information + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_FAIL No event callback function + * - ESP_GMF_ERR_INVALID_ARG If the handle is invalid + * - Others Failed + */ +esp_gmf_err_t esp_gmf_element_notify_snd_info(esp_gmf_element_handle_t handle, esp_gmf_info_sound_t *info); + +/** + * @brief Notify the specific element about video information + * + * @param[in] handle GMF element handle + * @param[in] info Pointer to video information + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_FAIL No event callback function + * - ESP_GMF_ERR_INVALID_ARG If the handle is invalid + * - Others Failed + */ +esp_gmf_err_t esp_gmf_element_notify_vid_info(esp_gmf_element_handle_t handle, esp_gmf_info_video_t *info); + +/** + * @brief Register a method for a GMF element + * + * This function registers a method identified by `name` with the GMF element specified by `handle` + * The method is associated with the function pointer `func` and will be executed when called + * + * @note The registered method and associated resources will be destroyed when the element is destroyed + * via `esp_gmf_element_destroy` + * + * @param[in] handle Handle to the GMF element where the method will be registered + * @param[in] name Name of the method to be registered + * @param[in] func Function pointer to the method implementation + * @param[in] args_desc A pointer to the argument description structure for the method + * + * @return + * - ESP_GMF_ERR_OK Method registered successfully + * - ESP_GMF_ERR_MEMORY_LACK Insufficient memory to register the method + * - ESP_GMF_ERR_INVALID_ARG Invalid argument, such as a NULL handle or function pointer + */ +esp_gmf_err_t esp_gmf_element_register_method(esp_gmf_element_handle_t handle, const char *name, + esp_gmf_method_func func, esp_gmf_args_desc_t *args_desc); + +/** + * @brief Execute methods of GMF element by argument list + * + * @param[in] handle Pointer to the handle of the GMF element + * @param[in] name The name of the method to be executed + * @param[in] buf Pointer to the buffer containing the arguments for the method + * @param[in] buf_len The length of the buffer (`buf`) + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_NOT_SUPPORT No executable methods found for the given name + * - ESP_GMF_ERR_INVALID_ARG Invalid handle or arguments + */ +esp_gmf_err_t esp_gmf_element_exe_method(esp_gmf_element_handle_t handle, const char *name, + uint8_t *buf, int buf_len); + +/** + * @brief Retrieve the method structure associated with a given ESP-GMF element + * + * @param[in] handle Pointer to the ESP-GMF element handle whose method structure is to be retrieved + * @param[out] methods Pointer to a pointer where the address of the method structure will be stored upon successful retrieval + * + * @return + * - ESP_GMF_OK Method structure retrieved successfully + * - ESP_GMF_ERR_INVALID_ARG Invalid argument, such as a NULL handle or output pointer + */ +esp_gmf_err_t esp_gmf_element_get_method(esp_gmf_element_handle_t handle, const esp_gmf_method_t **methods); + +/** + * @brief Retrieve the capability structure associated with a given ESP-GMF element + * On the first call, if the element's esp_gmf_load_caps_func is valid, it will load + * the capability structure by calling esp_gmf_load_caps_func. The created capability + * structure will be destroyed when the element is destroyed via esp_gmf_element_destroy + * + * @param[in] handle Pointer to the ESP-GMF element handle + * @param[out] caps Pointer to a pointer where the address of the capability structure + * will be stored upon successful retrieval + * + * @return + * - ESP_GMF_OK Capability structure successfully retrieved + * - ESP_GMF_ERR_INVALID_ARG Invalid argument, such as a NULL handle or output pointer + */ +esp_gmf_err_t esp_gmf_element_get_caps(esp_gmf_element_handle_t handle, const esp_gmf_cap_t **caps); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/include/esp_gmf_err.h b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/include/esp_gmf_err.h new file mode 100644 index 0000000..f0cd62b --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/include/esp_gmf_err.h @@ -0,0 +1,82 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#include "esp_err.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define ESP_GMF_ERR_BASE (-0x2000) +#define ESP_GMF_ERR_CORE_BASE (ESP_GMF_ERR_BASE + 0x0) + +/** + * @brief Error codes for GMF IO operations + */ +typedef enum { + ESP_GMF_IO_OK = ESP_OK, /*!< Operation successful */ + ESP_GMF_IO_FAIL = ESP_FAIL, /*!< Operation failed */ + ESP_GMF_IO_TIMEOUT = -2, /*!< Operation timed out */ + ESP_GMF_IO_ABORT = -3, /*!< Operation aborted */ +} esp_gmf_err_io_t; + +typedef enum { + ESP_GMF_ERR_OK = ESP_OK, + ESP_GMF_ERR_FAIL = ESP_FAIL, + ESP_GMF_ERR_TIMEOUT = ESP_GMF_IO_TIMEOUT, + ESP_GMF_ERR_UNKNOWN = ESP_GMF_ERR_CORE_BASE - 0, + ESP_GMF_ERR_ALREADY_EXISTS = ESP_GMF_ERR_CORE_BASE - 1, + ESP_GMF_ERR_MEMORY_LACK = ESP_GMF_ERR_CORE_BASE - 2, + ESP_GMF_ERR_INVALID_URI = ESP_GMF_ERR_CORE_BASE - 3, + ESP_GMF_ERR_INVALID_PATH = ESP_GMF_ERR_CORE_BASE - 4, + ESP_GMF_ERR_INVALID_ARG = ESP_GMF_ERR_CORE_BASE - 5, + ESP_GMF_ERR_INVALID_STATE = ESP_GMF_ERR_CORE_BASE - 6, + ESP_GMF_ERR_OUT_OF_RANGE = ESP_GMF_ERR_CORE_BASE - 7, + ESP_GMF_ERR_NOT_READY = ESP_GMF_ERR_CORE_BASE - 8, + ESP_GMF_ERR_NOT_SUPPORT = ESP_GMF_ERR_CORE_BASE - 9, + ESP_GMF_ERR_NOT_FOUND = ESP_GMF_ERR_CORE_BASE - 10, + ESP_GMF_ERR_NOT_ENOUGH = ESP_GMF_ERR_CORE_BASE - 12, + ESP_GMF_ERR_NO_DATA = ESP_GMF_ERR_CORE_BASE - 13, +} esp_gmf_err_t; + +#define ESP_GMF_CHECK(TAG, a, action, msg) if (!(a)) { \ + ESP_LOGE(TAG, "%s:%d (%s): %s", __FILENAME__, __LINE__, __FUNCTION__, msg); \ + action; \ +} + +#define ESP_GMF_RET_ON_FAIL(TAG, a, action, msg) if (unlikely(a == ESP_FAIL)) { \ + ESP_LOGE(TAG, "%s:%d (%s): %s", __FILENAME__, __LINE__, __FUNCTION__, msg); \ + action; \ +} + +#define ESP_GMF_RET_ON_NOT_OK(TAG, a, action, msg) if (unlikely(a != ESP_OK)) { \ + ESP_LOGE(TAG, "%s:%d (%s): %s", __FILENAME__, __LINE__, __FUNCTION__, msg); \ + action; \ +} + +#define ESP_GMF_RET_ON_ERROR(log_tag, a, action, format, ...) do { \ + esp_err_t err_rc_ = (a); \ + if (unlikely(err_rc_ != ESP_OK)) { \ + ESP_LOGE(log_tag, "%s(%d): " format, __FUNCTION__, __LINE__ __VA_OPT__(, ) __VA_ARGS__); \ + action; \ + } \ +} while (0) + +#define ESP_GMF_MEM_VERIFY(TAG, a, action, name, size) if (!(a)) { \ + ESP_LOGE(TAG, "%s(%d): Failed to allocate memory for %s(%d).", __FUNCTION__, __LINE__, name, size); \ + action; \ +} + +#define ESP_GMF_MEM_CHECK(TAG, a, action) ESP_GMF_CHECK(TAG, a, action, "Memory exhausted") + +#define ESP_GMF_NULL_CHECK(TAG, a, action) ESP_GMF_CHECK(TAG, a, action, "Got NULL Pointer") + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/include/esp_gmf_event.h b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/include/esp_gmf_event.h new file mode 100644 index 0000000..ac1351e --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/include/esp_gmf_event.h @@ -0,0 +1,83 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#include "esp_gmf_err.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * @brief Type of events in GMF + */ +typedef enum { + ESP_GMF_EVT_TYPE_LOADING_JOB = 0x1000, /*!< Loading job event */ + ESP_GMF_EVT_TYPE_CHANGE_STATE = 0x2000, /*!< State change event */ + ESP_GMF_EVT_TYPE_REPORT_INFO = 0x3000, /*!< Information reporting event */ +} esp_gmf_event_type_t; + +/** + * @brief States of GMF events + */ +typedef enum { + ESP_GMF_EVENT_STATE_NONE = 0, /*!< No specific state */ + ESP_GMF_EVENT_STATE_INITIALIZED = 1, /*!< Initialized state */ + ESP_GMF_EVENT_STATE_OPENING = 2, /*!< Opening state */ + ESP_GMF_EVENT_STATE_RUNNING = 3, /*!< Running state */ + ESP_GMF_EVENT_STATE_PAUSED = 4, /*!< Paused state */ + ESP_GMF_EVENT_STATE_STOPPED = 5, /*!< Stopped state */ + ESP_GMF_EVENT_STATE_FINISHED = 6, /*!< Finished state */ + ESP_GMF_EVENT_STATE_ERROR = 7, /*!< Error state */ +} esp_gmf_event_state_t; + +/** + * @brief Packet containing information about a GMF event + */ +typedef struct { + void *from; /*!< Pointer to the object sending the event */ + esp_gmf_event_type_t type; /*!< Type of the event */ + int sub; /*!< Additional data or subtype */ + void *payload; /*!< Pointer to the payload data */ + int payload_size; /*!< Size of the payload data */ +} esp_gmf_event_pkt_t; + +/** + * @brief Callback function for handling GMF events + * + * @param[in] pkt Pointer to the packet containing information about the event + * @param[in] ctx Context pointer + * + * @return + * - esp_gmf_err_t error code Indicating success or failure + */ +typedef esp_gmf_err_t (*esp_gmf_event_cb)(esp_gmf_event_pkt_t *pkt, void *ctx); + +/** + * @brief Structure representing an item in the event queue + */ +typedef struct _esp_gmf_event_item_ { + struct _esp_gmf_event_item_ *next; /*!< Pointer to the next event item */ + esp_gmf_event_cb cb; /*!< Callback function for the event */ + void *ctx; /*!< Context pointer */ +} esp_gmf_event_item_t; + +/** + * @brief Get the string representation of a GMF event state + * + * @param[in] st GMF event state + * + * @return + * - NULL Out of range + * - Others Const String representation of the state + */ +const char *esp_gmf_event_get_state_str(esp_gmf_event_state_t st); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/include/esp_gmf_info.h b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/include/esp_gmf_info.h new file mode 100644 index 0000000..775d7cd --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/include/esp_gmf_info.h @@ -0,0 +1,77 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#include "stdint.h" +#include "esp_gmf_err.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * @brief Structure representing metadata information for a GMF element + */ +typedef struct { + void *content; /*!< Pointer to the content */ + int length; /*!< Length of the content */ + void *ctx; /*!< User context */ +} esp_gmf_info_metadata_t; + +/** + * @brief Structure representing sound-related information for a GMF element + */ +typedef struct { + uint32_t format_id; /*!< Audio format FourCC representation refer to `esp_fourcc.h` for more details */ + int sample_rates; /*!< Sample rate */ + int bitrate; /*!< Bits per second */ + uint16_t channels : 8; /*!< Number of channels */ + uint16_t bits : 8; /*!< Bit depth */ +} esp_gmf_info_sound_t; + +/** + * @brief Structure representing sound-related information for a GMF element + */ +typedef struct { + uint32_t format_id; /*!< Video format FourCC representation refer to `esp_fourcc.h` for more details */ + uint16_t height; /*!< Height of the picture */ + uint16_t width; /*!< Width of the picture */ + uint16_t fps; /*!< Frame per sample */ + uint32_t bitrate; /*!< Bits per second */ +} esp_gmf_info_video_t; + +/** + * @brief Structure representing picture-related information for a GMF element + */ +typedef struct { + uint16_t height; /*!< Height of the picture */ + uint16_t width; /*!< Width of the picture */ +} esp_gmf_info_pic_t; + +/** + * @brief Structure representing file-related information for a GMF element + */ +typedef struct { + const char *uri; /*!< Uniform Resource Identifier (URI) of the file */ + uint64_t size; /*!< Total size of the file */ + uint64_t pos; /*!< Byte position in the file */ +} esp_gmf_info_file_t; + +/** + * @brief Enum representing the type of information for a GMF element + */ +typedef enum { + ESP_GMF_INFO_SOUND = 1, /*!< Sound-related information */ + ESP_GMF_INFO_VIDEO = 2, /*!< Video-related information */ + ESP_GMF_INFO_FILE = 3, /*!< File-related information */ + ESP_GMF_INFO_PIC = 4, /*!< Picture-related information */ +} esp_gmf_info_type_t; + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/include/esp_gmf_info_file.h b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/include/esp_gmf_info_file.h new file mode 100644 index 0000000..40f909f --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/include/esp_gmf_info_file.h @@ -0,0 +1,194 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#include "esp_gmf_err.h" +#include "esp_gmf_info.h" +#include "esp_gmf_oal_mem.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * @brief Tag of esp_gmf_info_file + */ +static const char *INFO_TAG = "ESP_GMF_INFO_FILE"; + +/** + * @brief Initialize the file information by given handle + * + * @param[in] handle Pointer to the file information handle to initialize + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid configuration provided + */ +static inline esp_gmf_err_t esp_gmf_info_file_init(esp_gmf_info_file_t *handle) +{ + ESP_GMF_NULL_CHECK(INFO_TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + if (handle->uri) { + esp_gmf_oal_free((char *)handle->uri); + handle->uri = NULL; + } + handle->pos = 0; + handle->size = 0; + return ESP_GMF_ERR_OK; +} + +/** + * @brief Deinitialize the file information by given handle + * + * @param[in] handle Pointer to the file information handle to deinitialize + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid configuration provided + */ +static inline esp_gmf_err_t esp_gmf_info_file_deinit(esp_gmf_info_file_t *handle) +{ + ESP_GMF_NULL_CHECK(INFO_TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + if (handle->uri) { + esp_gmf_oal_free((char *)handle->uri); + handle->uri = NULL; + } + return ESP_GMF_ERR_OK; +} + +/** + * @brief Update the file position of the specific handle + * + * @param[in] handle Pointer to the file information handle + * @param[in] byte_pos Byte position to update by + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid configuration provided + */ +static inline esp_gmf_err_t esp_gmf_info_file_update_pos(esp_gmf_info_file_t *handle, uint64_t byte_pos) +{ + ESP_GMF_NULL_CHECK(INFO_TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + handle->pos += byte_pos; + return ESP_GMF_ERR_OK; +} + +/** + * @brief Set the file position of the specific handle + * + * @param[in] handle Pointer to the file information handle + * @param[in] byte_pos Byte position to set + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid configuration provided + */ +static inline esp_gmf_err_t esp_gmf_info_file_set_pos(esp_gmf_info_file_t *handle, uint64_t byte_pos) +{ + ESP_GMF_NULL_CHECK(INFO_TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + handle->pos = byte_pos; + return ESP_GMF_ERR_OK; +} + +/** + * @brief Get the position of the specific handle + * + * @param[in] handle Pointer to the file information handle + * @param[out] byte_pos Pointer to store the byte position + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid configuration provided + */ +static inline esp_gmf_err_t esp_gmf_info_file_get_pos(esp_gmf_info_file_t *handle, uint64_t *byte_pos) +{ + ESP_GMF_NULL_CHECK(INFO_TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + *byte_pos = handle->pos; + return ESP_GMF_ERR_OK; +} + +/** + * @brief Set the total size of the specific handle + * + * @param[in] handle Pointer to the file information handle + * @param[in] total_size Total size to set + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid configuration provided + */ +static inline esp_gmf_err_t esp_gmf_info_file_set_size(esp_gmf_info_file_t *handle, uint64_t total_size) +{ + ESP_GMF_NULL_CHECK(INFO_TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + handle->size = total_size; + return ESP_GMF_ERR_OK; +} + +/** + * @brief Get the total size of the specific handle + * + * @param[in] handle Pointer to the file information handle + * @param[out] total_size Pointer to store the total size + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid configuration provided + */ +static inline esp_gmf_err_t esp_gmf_info_file_get_size(esp_gmf_info_file_t *handle, uint64_t *total_size) +{ + ESP_GMF_NULL_CHECK(INFO_TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + *total_size = handle->size; + return ESP_GMF_ERR_OK; +} + +/** + * @brief Set the URI of the specific handle + * + * @param[in] handle Pointer to the file information handle + * @param[in] uri URI to set + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_MEMORY_LACK Memory allocation failed + * - ESP_GMF_ERR_INVALID_ARG Invalid configuration provided + */ +static inline esp_gmf_err_t esp_gmf_info_file_set_uri(esp_gmf_info_file_t *handle, const char *uri) +{ + ESP_GMF_NULL_CHECK(INFO_TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + if (handle->uri) { + esp_gmf_oal_free((void *)handle->uri); + handle->uri = NULL; + } + if (uri) { + handle->uri = esp_gmf_oal_strdup(uri); + if (handle->uri == NULL) { + return ESP_GMF_ERR_MEMORY_LACK; + } + } + return ESP_GMF_ERR_OK; +} + +/** + * @brief Get the URI of the specific handle + * + * @param[in] handle Pointer to the file information handle + * @param[out] uri Pointer to store the URI + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid configuration provided + */ +static inline esp_gmf_err_t esp_gmf_info_file_get_uri(esp_gmf_info_file_t *handle, char **uri) +{ + ESP_GMF_NULL_CHECK(INFO_TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + *uri = (char *)handle->uri; + return ESP_GMF_ERR_OK; +} + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/include/esp_gmf_io.h b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/include/esp_gmf_io.h new file mode 100644 index 0000000..259c2cd --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/include/esp_gmf_io.h @@ -0,0 +1,345 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#include "esp_gmf_err.h" +#include "esp_gmf_obj.h" +#include "esp_gmf_info.h" +#include "esp_gmf_payload.h" +#include "esp_gmf_task.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * @brief Handle to a GMF I/O + */ +typedef void *esp_gmf_io_handle_t; + +/** + * @brief Enumeration for the direction of a GMF I/O (none, reader, writer) + */ +typedef enum { + ESP_GMF_IO_DIR_NONE = 0, /*!< No direction */ + ESP_GMF_IO_DIR_READER = 1, /*!< Reader direction */ + ESP_GMF_IO_DIR_WRITER = 2, /*!< Writer direction */ +} esp_gmf_io_dir_t; + +/** + * @brief Enumeration for the type of data handled by a GMF I/O (byte or block) + */ +typedef enum { + ESP_GMF_IO_TYPE_BYTE = 1, /*!< Byte type */ + ESP_GMF_IO_TYPE_BLOCK = 2, /*!< Block type */ +} esp_gmf_io_type_t; + +/** + * @brief Configuration structure for a GMF I/O + */ +typedef struct { + esp_gmf_task_config_t thread; /*!< Task configuration */ +} esp_gmf_io_cfg_t; + +/** + * @brief Structure representing a GMF I/O + */ +typedef struct esp_gmf_io_ { + struct esp_gmf_obj_ parent; /*!< Parent object */ + esp_gmf_err_t (*open)(esp_gmf_io_handle_t obj); /*!< Open callback function */ + esp_gmf_err_t (*seek)(esp_gmf_io_handle_t obj, uint64_t data); /*!< Seek callback function */ + esp_gmf_err_t (*close)(esp_gmf_io_handle_t obj); /*!< Close callback function */ + esp_gmf_err_t (*reset)(esp_gmf_io_handle_t obj); /*!< Reset callback function */ + + /*!< Previous close callback function + * For some block IO instances, this function can be called before the `close` operation + */ + esp_gmf_err_t (*prev_close)(esp_gmf_io_handle_t handle); + + /*!< Process callback function + * If the thread is valid, this function should be implemented to register the GMF task + */ + esp_gmf_job_err_t (*process)(esp_gmf_io_handle_t handle, void *params); /*!< Process function for GMF task calling if it's exist */ + + esp_gmf_err_io_t (*acquire_read)(esp_gmf_io_handle_t handle, void *payload, uint32_t wanted_size, int block_ticks); /*!< Acquire read callback function */ + esp_gmf_err_io_t (*release_read)(esp_gmf_io_handle_t handle, void *payload, int block_ticks); /*!< Release read callback function */ + esp_gmf_err_io_t (*acquire_write)(esp_gmf_io_handle_t handle, void *payload, uint32_t wanted_size, int block_ticks); /*!< Acquire write callback function */ + esp_gmf_err_io_t (*release_write)(esp_gmf_io_handle_t handle, void *payload, int block_ticks); /*!< Release write callback function */ + + esp_gmf_io_cfg_t task_cfg; + esp_gmf_task_handle_t task_hd; /*!< Task handle */ + esp_gmf_io_dir_t dir; /*!< I/O direction */ + esp_gmf_io_type_t type; /*!< I/O type */ + esp_gmf_info_file_t attr; /*!< File attribute */ +} esp_gmf_io_t; + +/** + * @brief Initialize a I/O handle with the given configuration + * If the stack size is greater than 0, a GMF task is created with the process job registered + * + * @param[in] handle GMF I/O handle to initialize + * @param[in] cfg Pointer to the configuration structure + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + * - ESP_GMF_ERR_MEMORY_LACK Insufficient memory to perform the job registration + * - ESP_GMF_ERR_FAIL Failed to create thread + */ +esp_gmf_err_t esp_gmf_io_init(esp_gmf_io_handle_t handle, esp_gmf_io_cfg_t *cfg); + +/** + * @brief Deinitialize a GMF I/O handle, freeing associated resources + * + * @param[in] handle GMF I/O handle to deinitialize + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_io_deinit(esp_gmf_io_handle_t handle); + +/** + * @brief Open the specific I/O handle, run the thread if it is valid + * + * @param[in] handle GMF I/O handle to open + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_io_open(esp_gmf_io_handle_t handle); + +/** + * @brief Seek to a specific byte position in the speoific I/O handle + * If the IO thread is invalid, only the IO's seek function is called. + * If the IO thread is valid, the seek procedure is as follows: first, + * perform a previous close, then pause the thread, seek to the specified position, reopen if necessary. + * Finally, resume the thread if it was paused; otherwise, re-register the process job + * + * @param[in] handle GMF I/O handle + * @param[in] seek_byte_pos Byte position to seek to + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + * - Others Indicating failure + */ +esp_gmf_err_t esp_gmf_io_seek(esp_gmf_io_handle_t handle, uint64_t seek_byte_pos); + +/** + * @brief Close a GMF I/O handle + * If both prev_close and thread are valid, they will be called before executing the IO close operation + * + * @param[in] handle GMF I/O handle to close + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_io_close(esp_gmf_io_handle_t handle); + +/** + * @brief Acquire read access to the specific I/O handle + * + * @param[in] handle GMF I/O handle + * @param[out] load Pointer to store the acquired payload + * @param[in] wanted_size Desired payload size + * @param[in] wait_ticks Timeout value in ticks, in milliseconds + * + * @return + * - > 0 The specific length of data being read + * - ESP_GMF_IO_OK Operation successful + * - ESP_GMF_IO_FAIL Operation failed or invalid arguments + * - ESP_GMF_IO_ABORT Operation aborted + * - ESP_GMF_IO_TIMEOUT Operation timed out + */ +esp_gmf_err_io_t esp_gmf_io_acquire_read(esp_gmf_io_handle_t handle, esp_gmf_payload_t *load, uint32_t wanted_size, int wait_ticks); + +/** + * @brief Release read access to the specific I/O handle + * + * @param[in] handle GMF I/O handle + * @param[in] load Pointer to the payload to release + * @param[in] wait_ticks Timeout value in ticks + * + * @return + * - ESP_GMF_IO_OK Operation successful + * - ESP_GMF_IO_FAIL Operation failed or invalid arguments + * - ESP_GMF_IO_ABORT Operation aborted + * - ESP_GMF_IO_TIMEOUT Operation timed out + */ +esp_gmf_err_io_t esp_gmf_io_release_read(esp_gmf_io_handle_t handle, esp_gmf_payload_t *load, int wait_ticks); + +/** + * @brief Acquire write access to the specific I/O handle + * + * @param[in] handle GMF I/O handle + * @param[out] load Pointer to store the acquired payload + * @param[in] wanted_size Desired payload size + * @param[in] wait_ticks Timeout value in ticks, in milliseconds + * + * @return + * - > 0 The specific length of space can be write + * - ESP_GMF_IO_OK Operation successful + * - ESP_GMF_IO_FAIL Operation failed or invalid arguments + * - ESP_GMF_IO_ABORT Operation aborted + * - ESP_GMF_IO_TIMEOUT Operation timed out + */ +esp_gmf_err_io_t esp_gmf_io_acquire_write(esp_gmf_io_handle_t handle, esp_gmf_payload_t *load, uint32_t wanted_size, int wait_ticks); + +/** + * @brief Release write access to the specific I/O handle + * + * @param[in] handle GMF I/O handle + * @param[in] load Pointer to the payload to release + * @param[in] wait_ticks Timeout value in ticks + * + * @return + * - ESP_GMF_IO_OK Operation successful + * - ESP_GMF_IO_FAIL Operation failed or invalid arguments + * - ESP_GMF_IO_ABORT Operation aborted + * - ESP_GMF_IO_TIMEOUT Operation timed out + */ +esp_gmf_err_io_t esp_gmf_io_release_write(esp_gmf_io_handle_t handle, esp_gmf_payload_t *load, int wait_ticks); + +/** + * @brief Set information for the specific I/O handle + * + * @param[in] handle GMF I/O handle + * @param[in] info Pointer to the file information structure + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_io_set_info(esp_gmf_io_handle_t handle, esp_gmf_info_file_t *info); + +/** + * @brief Get information from the specific I/O handle + * + * @param[in] handle GMF I/O handle + * @param[out] info Pointer to store the file information + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_io_get_info(esp_gmf_io_handle_t handle, esp_gmf_info_file_t *info); + +/** + * @brief Set the URI for the specific I/O handle + * + * @param[in] handle GMF I/O handle + * @param[in] uri Pointer to the URI string + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + * - ESP_GMF_ERR_MEMORY_LACK Memory allocation failed + */ +esp_gmf_err_t esp_gmf_io_set_uri(esp_gmf_io_handle_t handle, const char *uri); + +/** + * @brief Get the URI from the specific I/O handle + * + * @param[in] handle GMF I/O handle + * @param[out] uri Pointer to store the URI string + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_io_get_uri(esp_gmf_io_handle_t handle, char **uri); + +/** + * @brief Set the byte position for the specific I/O handle + * + * @param[in] handle GMF I/O handle + * @param[in] byte_pos Byte position to set + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_io_set_pos(esp_gmf_io_handle_t handle, uint64_t byte_pos); + +/** + * @brief Update the byte position for the specific I/O handle + * + * @param[in] handle GMF I/O handle + * @param[in] byte_pos Byte position to update + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_io_update_pos(esp_gmf_io_handle_t handle, uint64_t byte_pos); + +/** + * @brief Get the current byte position from the specific I/O handle + * + * @param[in] handle GMF I/O handle + * @param[out] byte_pos Pointer to store the current byte position + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_io_get_pos(esp_gmf_io_handle_t handle, uint64_t *byte_pos); + +/** + * @brief Set the total size for the specific I/O handle + * + * @param[in] handle GMF I/O handle + * @param[in] total_size Total size to set + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_io_set_size(esp_gmf_io_handle_t handle, uint64_t total_size); + +/** + * @brief Get the total size from the specific I/O handle + * + * @param[in] handle GMF I/O handle + * @param[out] total_size Pointer to store the total size + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_io_get_size(esp_gmf_io_handle_t handle, uint64_t *total_size); + +/** + * @brief Get the I/O type of the specific I/O handle + * + * @param[in] handle GMF I/O handle + * @param[out] type Pointer to store the I/O type + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_io_get_type(esp_gmf_io_handle_t handle, esp_gmf_io_type_t *type); + +/** + * @brief Reset IO will do reset the position and size, call the reset function if it is valid, then reset the task and reload IO process job + * + * @param[in] handle GMF I/O handle + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_io_reset(esp_gmf_io_handle_t handle); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/include/esp_gmf_job.h b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/include/esp_gmf_job.h new file mode 100644 index 0000000..6daa785 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/include/esp_gmf_job.h @@ -0,0 +1,219 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once +#include +#include "esp_gmf_err.h" +#include "esp_gmf_oal_mem.h" +#include "esp_log.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define ESP_GMF_JOB_LABLE_MAX_LEN (64) // The maximum length of tag including the '\0' is 64 byte. +#define ESP_GMF_JOB_STR_OPEN ("_open") +#define ESP_GMF_JOB_STR_PROCESS ("_proc") +#define ESP_GMF_JOB_STR_CLOSE ("_close") + +/** + * @brief This enumeration defines different states that a GMF job can be in + */ +typedef enum { + ESP_GMF_JOB_STATUS_SUSPENDED = 0, /*!< The job is suspended */ + ESP_GMF_JOB_STATUS_READY = 1, /*!< The job is ready */ + ESP_GMF_JOB_STATUS_RUNNING = 2 /*!< The job is running */ +} esp_gmf_job_status_t; + +/** + * @brief This enumeration specifies how many times a GMF job should be executed + */ +typedef enum { + ESP_GMF_JOB_TIMES_NONE = 0, /*!< The job should not be executed */ + ESP_GMF_JOB_TIMES_ONCE = 1, /*!< The job should be executed once */ + ESP_GMF_JOB_TIMES_INFINITE = 2 /*!< The job should be executed indefinitely */ +} esp_gmf_job_times_t; + +/** + * @brief This enumeration specifies the error status of a GMF job + */ +typedef enum { + ESP_GMF_JOB_ERR_TRUNCATE = 3, /*!< The job has been truncated */ + ESP_GMF_JOB_ERR_DONE = 2, /*!< The job has been completed */ + ESP_GMF_JOB_ERR_CONTINUE = 1, /*!< The job should continue */ + ESP_GMF_JOB_ERR_OK = ESP_OK, /*!< The job has executed successfully */ + ESP_GMF_JOB_ERR_FAIL = ESP_FAIL /*!< The job has failed to execute */ +} esp_gmf_job_err_t; + +/** + * @brief Function pointer type for GMF job functions + * + * @param[in] self A pointer to the job itself, allowing the function to access its internal data if needed + * @param[in] para A pointer to additional parameters needed by the job function + * + * @return + * - A value of type esp_gmf_job_err_t indicating the execution status of the job function + */ +typedef esp_gmf_job_err_t (*esp_gmf_job_func)(void *self, void *para); + +/** + * @brief This structure represents a job in the GMF + * A job encapsulates a function to be executed, along with its context and other properties + */ +typedef struct _esp_gmf_job_t { + struct _esp_gmf_job_t *prev; /*!< Pointer to the previous job in the linked list */ + struct _esp_gmf_job_t *next; /*!< Pointer to the next job in the linked list */ + const char *label; /*!< Label identifying the job */ + esp_gmf_job_func func; /*!< Function pointer to the job's function */ + void *ctx; /*!< Context pointer to be passed to the job's function */ + void *para; /*!< Parameter pointer to be passed to the job's function */ + esp_gmf_job_times_t times; /*!< Times the job should be executed */ + esp_gmf_job_err_t ret; /*!< Return value of the job function */ +} esp_gmf_job_t; + +/** + * @brief Concatenate two strings into the target string + * + * @param[in,out] target Pointer to the target buffer where the concatenated string will be stored + * @param[in] target_size Size of the target buffer + * @param[in] src1 Pointer to the null-terminated string to be concatenated from the beginning + * @param[in] src2 Pointer to the string to be concatenated + * @param[in] src2_len Length of the src2 string to be concatenated + */ +static inline void esp_gmf_job_str_cat(char *target, int target_size, + const char *src1, const char *src2, int src2_len) +{ + memset(target, 0, target_size); + snprintf(target, target_size - 1 - src2_len, "%s", src1); + strcat(target, src2); +} + +typedef struct esp_gmf_job_node { + struct esp_gmf_job_node *next; + uint32_t node_addr; +} esp_gmf_job_node_t; + +typedef struct { + esp_gmf_job_node_t *top; +} esp_gmf_job_stack_t; + +static inline esp_gmf_err_t esp_gmf_job_stack_create(esp_gmf_job_stack_t **stack) +{ + *stack = (esp_gmf_job_stack_t *)esp_gmf_oal_calloc(1, sizeof(esp_gmf_job_stack_t)); + ESP_GMF_MEM_CHECK("GMF_JOB_STACK", *stack, return ESP_GMF_ERR_MEMORY_LACK); + return ESP_GMF_ERR_OK; +} + +static inline esp_gmf_err_t esp_gmf_job_stack_push(esp_gmf_job_stack_t *stack, uint32_t node_addr) +{ + ESP_GMF_NULL_CHECK("GMF_JOB_STACK", stack, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_job_node_t *node = (esp_gmf_job_node_t *)esp_gmf_oal_calloc(1, sizeof(esp_gmf_job_node_t)); + ESP_GMF_MEM_CHECK("GMF_JOB_STACK", node, return ESP_GMF_ERR_MEMORY_LACK); + node->node_addr = node_addr; + node->next = stack->top; + stack->top = node; + return ESP_GMF_ERR_OK; +} + +static inline esp_gmf_err_t esp_gmf_job_stack_pop(esp_gmf_job_stack_t *stack, uint32_t *node_addr) +{ + ESP_GMF_NULL_CHECK("GMF_JOB_STACK", stack, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK("GMF_JOB_STACK", node_addr, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_CHECK("GMF_JOB_STACK", stack->top, return ESP_GMF_ERR_NOT_READY, "Job stack is empty"); + esp_gmf_job_node_t *node = stack->top; + if (node->next == NULL) { + *node_addr = node->node_addr; + return ESP_GMF_ERR_OK; + } + stack->top = node->next; + node->next = NULL; + *node_addr = node->node_addr; + esp_gmf_oal_free(node); + return ESP_GMF_ERR_OK; +} + +static inline void esp_gmf_job_stack_clear(esp_gmf_job_stack_t *stack) +{ + if (!stack) { + return; + } + esp_gmf_job_node_t *curr = stack->top; + while (curr) { + esp_gmf_job_node_t *temp = curr; + curr = curr->next; + esp_gmf_oal_free(temp); + } + stack->top = NULL; +} + +static inline esp_gmf_err_t esp_gmf_job_stack_remove(esp_gmf_job_stack_t *stack, uint32_t node_addr) +{ + ESP_GMF_NULL_CHECK("GMF_JOB_STACK", stack, return ESP_GMF_ERR_INVALID_ARG); + if (stack->top == NULL) { + return ESP_GMF_ERR_NOT_READY; + } + if (stack->top->node_addr == node_addr) { + esp_gmf_job_node_t *removed = stack->top; + stack->top = removed->next; + removed->next = NULL; + esp_gmf_oal_free(removed); + return ESP_GMF_ERR_OK; + } + esp_gmf_job_node_t *prev = stack->top; + esp_gmf_job_node_t *curr = stack->top->next; + while (curr) { + if (curr->node_addr == node_addr) { + prev->next = curr->next; + curr->next = NULL; + esp_gmf_oal_free(curr); + return ESP_GMF_ERR_OK; + } + prev = curr; + curr = curr->next; + } + return ESP_GMF_ERR_NOT_FOUND; +} + +static inline esp_gmf_err_t esp_gmf_job_stack_is_empty(const esp_gmf_job_stack_t *stack, bool *empty) +{ + ESP_GMF_NULL_CHECK("GMF_JOB_STACK", stack, return ESP_GMF_ERR_INVALID_ARG); + *empty = false; + if (stack->top == NULL) { + *empty = true; + } + return ESP_GMF_ERR_OK; +} + +static inline void esp_gmf_job_stack_destroy(esp_gmf_job_stack_t *stack) +{ + ESP_GMF_NULL_CHECK("GMF_JOB_STACK", stack, return); + esp_gmf_job_node_t *curr = stack->top; + while (curr) { + esp_gmf_job_node_t *temp = curr; + curr = curr->next; + esp_gmf_oal_free(temp); + } + esp_gmf_oal_free(stack); +} + +static inline void esp_gmf_job_stack_show(const esp_gmf_job_stack_t *stack, int line) +{ + const esp_gmf_job_node_t *cur = stack ? stack->top : NULL; + printf("Job Stack [line:%d] (top -> bottom): ", line); + while (cur) { + esp_gmf_job_t *job = (esp_gmf_job_t *)cur->node_addr; + printf("%s(%p) ", job->label, job->func); + cur = cur->next; + } + printf("\n"); +} + +#define ESP_GMF_JOB_STACK_SHOW(stack) esp_gmf_job_stack_show(stack, __LINE__) + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/include/esp_gmf_method.h b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/include/esp_gmf_method.h new file mode 100644 index 0000000..293f7ec --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/include/esp_gmf_method.h @@ -0,0 +1,191 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#include "esp_gmf_err.h" +#include "esp_gmf_oal_mem.h" +#include "esp_gmf_args_desc.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * @brief Function pointer type for GMF method functions + * This typedef defines the function pointer type for GMF method functions + */ +typedef esp_gmf_err_t (*esp_gmf_method_func)(void *handle, esp_gmf_args_desc_t *arg_desc, uint8_t *buf, int buf_len); + +/** + * @brief Structure for GMF methods + * This structure defines a linked list node for storing GMF methods + */ +typedef struct esp_gmf_method { + struct esp_gmf_method *next; /*!< Pointer to the next method node */ + const char *name; /*!< Name of the method */ + esp_gmf_method_func func; /*!< Function pointer to the method implementation */ + uint16_t args_cnt; /*!< Number of the argument description */ + esp_gmf_args_desc_t *args_desc; /*!< A pointer to argument description structure */ +} esp_gmf_method_t; + +/** + * @brief Create a new GMF method + * This function creates a new GMF method and initializes its members + * + * @note The `name` argument must have global or static scope to ensure validity throughout the method's operation + * + * @param[in] name Name of the method + * @param[in] func Function pointer to the method implementation + * @param[in] args The arguments description list + * @param[out] handle Pointer to the handle for the newly created method + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_MEMORY_LACK Memory allocation failure + */ +static inline esp_gmf_err_t esp_gmf_method_create(const char *name, esp_gmf_method_func func, esp_gmf_args_desc_t *args, esp_gmf_method_t **handle) +{ + esp_gmf_method_t *new_method = (esp_gmf_method_t *)esp_gmf_oal_calloc(1, sizeof(esp_gmf_method_t)); + if (new_method) { + if (name) { + new_method->name = name; + } + new_method->args_cnt = esp_gmf_args_desc_count(args); + new_method->func = func; + new_method->args_desc = args; + new_method->next = NULL; + *handle = new_method; + return ESP_GMF_ERR_OK; + } + return ESP_GMF_ERR_MEMORY_LACK; +} + +/** + * @brief Append a new GMF method to the method list + * This function creates a new GMF method and appends it to the provided method list + * + * @note The `name` argument must have global or static scope to ensure validity throughout the method's operation + * + * @param[in,out] head Pointer to the head of the method list + * @param[in] name Name of the method + * @param[in] func Function pointer to the method implementation + * @param[in] args The arguments description list + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_MEMORY_LACK Memory allocation failure + */ +static inline esp_gmf_err_t esp_gmf_method_append(esp_gmf_method_t **head, const char *name, esp_gmf_method_func func, esp_gmf_args_desc_t *args) +{ + esp_gmf_method_t *new_method = NULL; + esp_gmf_method_create(name, func, args, &new_method); + if (new_method == NULL) { + return ESP_GMF_ERR_MEMORY_LACK; + } + if (*head == NULL) { + *head = new_method; + } else { + esp_gmf_method_t *current = *head; + while (current->next != NULL) { + current = current->next; + } + current->next = new_method; + } + return ESP_GMF_ERR_OK; +} + +/** + * @brief Destroy a GMF method list + * + * @param[in] head Pointer to the head of the method list + */ +static inline void esp_gmf_method_destroy(esp_gmf_method_t *head) +{ + esp_gmf_method_t *current = head; + esp_gmf_method_t *next; + while (current != NULL) { + next = current->next; + if (current->args_desc) { + esp_gmf_args_desc_destroy(current->args_desc); + current->args_desc = NULL; + } + esp_gmf_oal_free(current); + current = next; + } +} + +/** + * @brief Display the list of GMF methods and their argument descriptions + * + * @param[in] head Pointer to the head of the method list + */ +static inline void esp_gmf_method_show(esp_gmf_method_t *head) +{ + esp_gmf_method_t *current = head; + while (current != NULL) { + printf("Method:%s\n", current->name); + if (current->args_desc) { + ESP_GMF_ARGS_DESC_PRINT(current->args_desc); + } + current = current->next; + } +} + +/** + * @brief Find a method by name in a linked list of methods + * + * @param[in] head Pointer to the head of the linked list of methods + * @param[in] wanted_name The name of the method to search for + * @param[out] wanted_method Pointer to store the found method, if any. Set to NULL if not found. + * + * @return + * - ESP_GMF_ERR_OK The method was found successfully + * - ESP_GMF_ERR_NOT_FOUND The method with the specified name was not found + */ +static inline esp_gmf_err_t esp_gmf_method_found(const esp_gmf_method_t *head, const char *wanted_name, const esp_gmf_method_t **wanted_method) +{ + if ((head == NULL) || (wanted_method == NULL)) { + return ESP_GMF_ERR_INVALID_ARG; + } + const esp_gmf_method_t *current = head; + while (current != NULL) { + ESP_LOGD("GMF_Method", "name:%s, want:%s", current->name, wanted_name); + if ((current->name == wanted_name) || (strcasecmp(current->name, wanted_name) == 0)) { + *wanted_method = current; + return ESP_GMF_ERR_OK; + } + current = current->next; + } + *wanted_method = NULL; + return ESP_GMF_ERR_NOT_FOUND; +} + +/** + * @brief Query the argument descriptors of a method + * + * @param[in] head Pointer to the method structure from which arguments will be queried + * @param[out] args Pointer to store the retrieved argument descriptors + * Set to NULL if the method has no arguments + * + * @return + * - ESP_GMF_ERR_OK The argument descriptors were successfully retrieved + * - ESP_GMF_ERR_INVALID_ARG The `head` or `args` pointer is invalid (e.g., NULL) + */ +static inline esp_gmf_err_t esp_gmf_method_query_args(esp_gmf_method_t *head, esp_gmf_args_desc_t **args) +{ + if (head) { + *args = head->args_desc; + return ESP_GMF_ERR_OK; + } + *args = NULL; + return ESP_GMF_ERR_INVALID_ARG; +} + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/include/esp_gmf_method_helper.h b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/include/esp_gmf_method_helper.h new file mode 100644 index 0000000..c60f41e --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/include/esp_gmf_method_helper.h @@ -0,0 +1,119 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#include "esp_gmf_method.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * @brief Define a GMF method identifier + * + * This macro defines a string identifier for a GMF method, + * making the code more readable and understandable. The identifier is composed of + * three parts: + * 1. Category: The method's category (e.g., VIDEO) + * 2. Module: The module to which the method belongs (e.g., ENCODER) + * 3. Method: The method name itself (e.g., SET_DST_CODEC) + * + * Example usage: + * ESP_GMF_METHOD_DEF(VIDEO, ENCODER, SET_DST_CODEC, "set_dst_codec") + */ +#define ESP_GMF_METHOD_DEF(category, module, method, str) \ + static const char* const ESP_GMF_METHOD_##category##_##module##_##method = str + +/** + * @brief Retrieve the string representation of a method defined with `ESP_GMF_METHOD_DEF`. + */ +#define ESP_GMF_METHOD_STR(category, module, method) \ + (ESP_GMF_METHOD_##category##_##module##_##method) + +/** + * @brief Define a GMF method argument identifier + * + * This macro defines a string identifier for a method argument, improving code readability. + * The identifier is composed of four parts: + * 1. Category: The method's category (e.g., VIDEO) + * 2. Module: The module to which the method belongs (e.g., ENCODER) + * 3. Method: The method name (e.g., SET_DST_CODEC) + * 4. Argument: The argument name (e.g., CODEC) + * + * Example usage: + * ESP_GMF_METHOD_ARG_DEF(VIDEO, ENCODER, SET_DST_CODEC, CODEC, "codec") + */ +#define ESP_GMF_METHOD_ARG_DEF(category, module, method, arg, str) \ + static const char* const ESP_GMF_METHOD_##category##_##module##_##method##_##arg = str + +/** + * @brief Retrieve the string representation of a method argument defined with `ESP_GMF_METHOD_ARG_DEF` + */ +#define ESP_GMF_METHOD_ARG_STR(category, module, method, arg) \ + (ESP_GMF_METHOD_##category##_##module##_##method##_##arg) + +/** + * @brief Structure for GMF method execution context + * This structure holds the necessary information to execute a GMF method + * Including the method pointer and buffer to store execution settings + */ +typedef struct esp_gmf_method_exec_ctx { + const esp_gmf_method_t *method; /*!< Pointer to the matched method to be executed */ + uint8_t *exec_buf; /*!< Buffer to store executed settings */ + size_t buf_size; /*!< Executed buffer size */ +} esp_gmf_method_exec_ctx_t; + +/** + * @brief Prepare for method execution context by method name + * + * @param[in] method_head Header of GMF methods + * @param[in] method_name Method name to be searched + * @param[out] exec_ctx Pointer to method execution context + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_NOT_FOUND Not found method for the method name + * - ESP_GMF_ERR_MEMORY_LACK Not enough memory for method execution buffer + */ +static inline esp_gmf_err_t esp_gmf_method_prepare_exec_ctx(const esp_gmf_method_t *method_head, const char *method_name, + esp_gmf_method_exec_ctx_t *exec_ctx) +{ + if (method_head) { + esp_gmf_method_found(method_head, method_name, &exec_ctx->method); + } + if (exec_ctx->method == NULL) { + return ESP_GMF_ERR_NOT_FOUND; + } + exec_ctx->exec_buf = NULL; + // Allocate for execute buffer + esp_gmf_args_desc_get_total_size(exec_ctx->method->args_desc, &exec_ctx->buf_size); + if (exec_ctx->buf_size > 0) { + exec_ctx->exec_buf = esp_gmf_oal_calloc(1, exec_ctx->buf_size); + if (exec_ctx->exec_buf == NULL) { + return ESP_GMF_ERR_MEMORY_LACK; + } + } + return ESP_GMF_ERR_OK; +} + +/** + * @brief Release for method execution context + * + * @param[in] exec_ctx Pointer to method execution context + */ +static inline void esp_gmf_method_release_exec_ctx(esp_gmf_method_exec_ctx_t *exec_ctx) +{ + if (exec_ctx && exec_ctx->exec_buf) { + esp_gmf_oal_free(exec_ctx->exec_buf); + exec_ctx->exec_buf = NULL; + } +} + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/include/esp_gmf_node.h b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/include/esp_gmf_node.h new file mode 100644 index 0000000..f306bed --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/include/esp_gmf_node.h @@ -0,0 +1,117 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * @brief Doubly linked list node structure + */ +typedef struct esp_gmf_node_t { + struct esp_gmf_node_t *prev; /*!< Pointer to the previous node in the linked list */ + struct esp_gmf_node_t *next; /*!< Pointer to the next node in the linked list */ +} esp_gmf_node_t; + +/** + * @brief Function pointer type for node cleanup + */ +typedef int (*node_free)(void *p); + +/** + * @brief Get the head node of a linked list + * + * @param[in] node The node of the linked list + * @param[in] num The number of the node start from given 'node' + * + * @return + * - NULL If the linked list is empty + * - Others Pointer to the head node of the linked list + */ +esp_gmf_node_t *esp_gmf_node_get_head(esp_gmf_node_t *node, int *num); + +/** + * @brief Get the tail node of a linked list + * + * @param[in] root Root node of the linked list + * + * @return + * - NULL If the linked list is empty + * - Others Pointer to the tail node of the linked list + */ +esp_gmf_node_t *esp_gmf_node_get_tail(esp_gmf_node_t *root); + +/** + * @brief Add a node to the end of a linked list + * + * @param[in] root Root node of the linked list + * @param[in] new_node Node to be added to the end of the linked list + */ +void esp_gmf_node_add_last(esp_gmf_node_t *root, esp_gmf_node_t *new_node); + +/** + * @brief Clear a linked list, freeing associated resources + * + * @param[in,out] root Pointer to the root node of the linked list + * @param[in] del Function pointer for freeing node resources + */ +void esp_gmf_node_clear(esp_gmf_node_t **root, node_free del); + +/** + * @brief Get the size of a linked list + * + * @param[in] root Root node of the linked list + * + * @return + * - > 0 Number of nodes in the linked list + * - 0 Invalid argument provided or the list is empty + */ +int esp_gmf_node_get_size(esp_gmf_node_t *root); + +/** + * @brief Insert a new node after a specified node in a linked list + * + * @param[in] prev Node after which the new node will be inserted + * @param[in] new Node to be inserted + */ +void esp_gmf_node_insert_after(esp_gmf_node_t *prev, esp_gmf_node_t *new); + +/** + * @brief Delete a specified node from a linked list, freeing associated resources + * + * @param[in,out] root Pointer to the root node of the linked list + * @param[in] del Node to be deleted + */ +void esp_gmf_node_del_at(esp_gmf_node_t **root, esp_gmf_node_t *del); + +/** + * @brief Get the next node in a linked list + * + * @param[in] node Current node in the linked list + * + * @return + * - NULL If the current node is the tail + * - Others Pointer to the next node + */ +esp_gmf_node_t *esp_gmf_node_for_next(esp_gmf_node_t *node); + +/** + * @brief Get the previous node in a linked list + * + * @param[in] node Current node in the linked list + * + * @return + * - NULL If the current node is the head + * - Others Pointer to the previous node + */ +esp_gmf_node_t *esp_gmf_node_for_prev(esp_gmf_node_t *node); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/include/esp_gmf_obj.h b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/include/esp_gmf_obj.h new file mode 100644 index 0000000..d2a6cc6 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/include/esp_gmf_obj.h @@ -0,0 +1,119 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#include "esp_gmf_err.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define ESP_GMF_TAG_MAX_LEN (32) // The maximum length of tag including the '\0' is 32 byte. + +typedef void *esp_gmf_obj_handle_t; + +/** + * @brief GMF object structure + * + * This structure represents a generic GMF object with basic functionalities, + * such as creating a new object, deleting an object, and storing object-related + * information. + */ +typedef struct esp_gmf_obj_ { + struct esp_gmf_obj_ *prev; /*!< Pointer to the previous GMF object in the list */ + struct esp_gmf_obj_ *next; /*!< Pointer to the next GMF object in the list */ + esp_gmf_err_t (*new_obj)(void *cfg, esp_gmf_obj_handle_t *new_obj); /*!< Function pointer to create a new object */ + esp_gmf_err_t (*del_obj)(esp_gmf_obj_handle_t obj); /*!< Function pointer to delete an object */ + const char *tag; /*!< Pointer to a tag or identifier associated with the object */ + void *cfg; /*!< Pointer to the configuration data for the object */ +} esp_gmf_obj_t; + +#define OBJ_GET_TAG(x) ((x) ? ((((esp_gmf_obj_t *)x)->tag) ? ((esp_gmf_obj_t *)x)->tag : "NULL") : "NULL") + +#define OBJ_GET_CFG(x) ((x) ? ((((esp_gmf_obj_t *)x)->cfg) ? ((esp_gmf_obj_t *)x)->cfg : NULL) : NULL) + +/** + * @brief Duplicate a GMF object + * + * @param[in] old_obj Handle of the old object to duplicate + * @param[out] new_obj Pointer to store the handle of the new duplicated object + * + * @return + * - ESP_GMF_ERR_OK On success (new_obj contains the duplicated object handle) + * - ESP_GMF_ERR_INVALID_ARG If old_obj is invalid or new_obj is NULL + * - ESP_GMF_ERR_MEMORY_LACK Insufficient memory for operation + */ +esp_gmf_err_t esp_gmf_obj_dupl(esp_gmf_obj_handle_t old_obj, esp_gmf_obj_handle_t *new_obj); + +/** + * @brief Create a new GMF object based on an existing object with additional configuration + * + * @param[in] old_obj Handle of the existing object to use as a base + * @param[in] cfg Pointer to the additional configuration for the new object + * @param[out] new_obj Pointer to store the handle of the newly created object + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument provided + * - ESP_GMF_ERR_MEMORY_LACK Insufficient memory for operation + */ +esp_gmf_err_t esp_gmf_obj_new(esp_gmf_obj_handle_t old_obj, void *cfg, esp_gmf_obj_handle_t *new_obj); + +/** + * @brief Delete a GMF object + * + * @param[in] obj Handle of the object to delete + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument provided + */ +esp_gmf_err_t esp_gmf_obj_delete(esp_gmf_obj_handle_t obj); + +/** + * @brief Set configuration for a GMF object + * + * @param[in] obj Handle of the object to set the configuration for + * @param[in] cfg Pointer to the configuration data + * @param[in] cfg_size Size of the configuration data + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument provided + * - ESP_GMF_ERR_MEMORY_LACK Insufficient memory for operation + */ +esp_gmf_err_t esp_gmf_obj_set_config(esp_gmf_obj_handle_t obj, void *cfg, int cfg_size); + +/** + * @brief Set a tag for a GMF object + * + * @param[in] obj Handle of the object to set the tag for + * @param[in] tag Pointer to the tag string + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument provided + * - ESP_GMF_ERR_MEMORY_LACK Insufficient memory for operation + */ +esp_gmf_err_t esp_gmf_obj_set_tag(esp_gmf_obj_handle_t obj, const char *tag); + +/** + * @brief Get the tag associated with a GMF object + * + * @param[in] obj Handle of the object to get the tag from + * @param[out] tag Pointer to store the tag string + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument provided + */ +esp_gmf_err_t esp_gmf_obj_get_tag(esp_gmf_obj_handle_t obj, char **tag); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/include/esp_gmf_payload.h b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/include/esp_gmf_payload.h new file mode 100644 index 0000000..c172e84 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/include/esp_gmf_payload.h @@ -0,0 +1,133 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#include +#include "esp_gmf_err.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * @brief The meta flag for the current payload + */ +#define ESP_GMF_META_FLAG_AUD_RECOVERY_PLC (1 << 0) /*!< The current frame is recovered through the packet loss concealment (PLC) mechanism */ + +/** + * @brief Structure representing a payload in GMF + */ +typedef struct { + uint8_t *buf; /*!< Pointer to the payload buffer */ + size_t buf_length; /*!< Length of the payload buffer */ + size_t valid_size; /*!< Size of valid data in the payload buffer */ + bool is_done; /*!< Flag indicating if this payload buffer marks the end of the stream */ + uint64_t pts; /*!< Presentation time stamp */ + uint8_t needs_free : 1; /*!< Flag indicating if the payload buffer needs to be freed by esp_gmf_payload_delete or not*/ + uint8_t meta_flag : 7; /*!< Meta flag for the payload */ +} esp_gmf_payload_t; + +/** + * @brief Create a new payload instance without buffer + * + * @param[out] instance Pointer to store the newly created payload instance + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_MEMORY_LACK Not enough memory to create the payload instance + * - ESP_GMF_ERR_INVALID_ARG Invalid argument provided + */ +esp_gmf_err_t esp_gmf_payload_new(esp_gmf_payload_t **instance); + +/** + * @brief Create a new payload instance and buffer by specified buffer length + * + * @param[in] buf_length Length of the payload buffer + * @param[out] instance Pointer to store the newly created payload instance + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_MEMORY_LACK Not enough memory to create the payload instance + * - ESP_GMF_ERR_INVALID_ARG Invalid argument provided + */ +esp_gmf_err_t esp_gmf_payload_new_with_len(uint32_t buf_length, esp_gmf_payload_t **instance); + +/** + * @brief Copy data and done flag from one payload instance to another + * + * @param[in] src Source payload instance + * @param[out] dest Destination payload instance + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument provided + */ +esp_gmf_err_t esp_gmf_payload_copy_data(esp_gmf_payload_t *src, esp_gmf_payload_t *dest); + +/** + * @brief Reallocate the buffer of a payload instance to the specified length + * Unlike `realloc`,the original valid data in the buffer was not copied to the new buffer + * + * @param[in] instance Payload instance to reallocate the buffer for + * @param[in] new_length New length for the payload buffer + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_MEMORY_LACK Not enough memory to reallocate the buffer + * - ESP_GMF_ERR_INVALID_ARG Invalid instance or new_length is zero + */ +esp_gmf_err_t esp_gmf_payload_realloc_buf(esp_gmf_payload_t *instance, uint32_t new_length); + +/** + * @brief Reallocate the buffer of a payload instance to the specified length with specified byte alignment + * Behavior same as `esp_gmf_payload_realloc_buf` with an additional alignment request + * Unlike `realloc`,the original valid data in the buffer was not copied to the new buffer + * + * @param[in] instance Payload instance to reallocate the buffer for + * @param[in] align Byte alignment for the new payload buffer + * @param[in] new_length New length for the payload buffer + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_MEMORY_LACK Not enough memory to reallocate the buffer + * - ESP_GMF_ERR_INVALID_ARG Invalid instance or new_length is zero + */ +esp_gmf_err_t esp_gmf_payload_realloc_aligned_buf(esp_gmf_payload_t *instance, uint8_t align, uint32_t new_length); + +/** + * @brief Set the done flag for a payload instance + * + * @param[in] instance Payload instance to set as done + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument provided + */ +esp_gmf_err_t esp_gmf_payload_set_done(esp_gmf_payload_t *instance); + +/** + * @brief Clean the done flag for a payload instance + * + * @param[in] instance Payload instance to clean the done status for + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument provided + */ +esp_gmf_err_t esp_gmf_payload_clean_done(esp_gmf_payload_t *instance); + +/** + * @brief Delete a payload instance, if needs_free is set free associated resources + * + * @param[in] instance Payload instance to delete + */ +void esp_gmf_payload_delete(esp_gmf_payload_t *instance); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/include/esp_gmf_pic_element.h b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/include/esp_gmf_pic_element.h new file mode 100644 index 0000000..76d63f3 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/include/esp_gmf_pic_element.h @@ -0,0 +1,154 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#include "esp_gmf_element.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * @brief GMF picture element structure + */ +typedef struct _esp_gmf_pic_element { + struct esp_gmf_element base; /*!< Base element structure */ + esp_gmf_info_metadata_t meta_info; /*!< Picture metadata information */ + esp_gmf_info_pic_t pic_info; /*!< Picture information */ + esp_gmf_info_file_t file_info; /*!< File information */ + void *lock; /*!< Lock for thread safety */ +} esp_gmf_pic_element_t; + +/** GMF picture element handle */ +typedef void *esp_gmf_pic_element_handle_t; + +/** + * @brief Initialize a GMF picture element with the given configuration + * + * @param[in] handle GMF picture element handle to initialize + * @param[in] config Pointer to the configuration structure + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + * - ESP_GMF_ERR_MEMORY_LACK Memory allocate failed + */ +esp_gmf_err_t esp_gmf_pic_el_init(esp_gmf_pic_element_handle_t handle, esp_gmf_element_cfg_t *config); + +/** + * @brief Get metadata from a GMF picture element + * + * @param[in] handle GMF picture element handle + * @param[out] meta Pointer to store the metadata information + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_pic_el_get_metadata(esp_gmf_pic_element_handle_t handle, esp_gmf_info_metadata_t *meta); + +/** + * @brief Set metadata for a GMF picture element + * + * @param[in] handle GMF picture element handle + * @param[in] value Pointer to the metadata value + * @param[in] len Length of the metadata value + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + * - ESP_GMF_ERR_MEMORY_LACK Memory allocate failed + */ +esp_gmf_err_t esp_gmf_pic_el_set_metadata(esp_gmf_pic_element_handle_t handle, void *value, int len); + +/** + * @brief Get file information from a GMF picture element + * + * @param[in] handle GMF picture element handle + * @param[out] info Pointer to store the file information + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_pic_el_get_file_info(esp_gmf_pic_element_handle_t handle, esp_gmf_info_file_t *info); + +/** + * @brief Set file information for a GMF picture element + * + * @param[in] handle GMF picture element handle + * @param[in] info Pointer to the file information + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_pic_el_set_file_info(esp_gmf_pic_element_handle_t handle, esp_gmf_info_file_t *info); + +/** + * @brief Get picture information from a GMF picture element + * + * @param[in] handle GMF picture element handle + * @param[out] info Pointer to store the picture information + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_pic_el_get_pic_info(esp_gmf_pic_element_handle_t handle, esp_gmf_info_pic_t *info); + +/** + * @brief Set picture information for a GMF picture element + * + * @param[in] handle GMF picture element handle + * @param[in] info Pointer to the picture information + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_pic_el_set_pic_info(esp_gmf_pic_element_handle_t handle, esp_gmf_info_pic_t *info); + +/** + * @brief Set the file size for a GMF picture element + * + * @param[in] handle GMF picture element handle + * @param[in] size New file size to update + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_pic_el_set_file_size(esp_gmf_pic_element_handle_t handle, uint64_t size); + +/** + * @brief Update the file position for a GMF picture element + * + * @param[in] handle GMF picture element handle + * @param[in] pos New file position to update + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_pic_el_update_file_pos(esp_gmf_pic_element_handle_t handle, uint64_t pos); + +/** + * @brief Deinitialize a GMF picture element, freeing associated resources + * + * @param[in] handle GMF picture element handle to deinitialize + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_pic_el_deinit(esp_gmf_pic_element_handle_t handle); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/include/esp_gmf_pipeline.h b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/include/esp_gmf_pipeline.h new file mode 100644 index 0000000..fbf291f --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/include/esp_gmf_pipeline.h @@ -0,0 +1,519 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#include "esp_gmf_element.h" +#include "esp_gmf_io.h" +#include "esp_gmf_task.h" +#include "esp_gmf_event.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define ESP_GMF_PIPELINE_GET_IN_INSTANCE(p) (p->in) +#define ESP_GMF_PIPELINE_GET_OUT_INSTANCE(p) (p->out) +#define ESP_GMF_PIPELINE_GET_FIRST_ELEMENT(p) (p->head_el) +#define ESP_GMF_PIPELINE_GET_LAST_ELEMENT(p) (p->last_el) + +/** + * @brief Pointer to a GMF pipeline + */ +typedef struct esp_gmf_pipeline *esp_gmf_pipeline_handle_t; + +/** + * @brief Function pointer type for pipeline previous action callback + */ +typedef esp_gmf_err_t (*esp_gmf_pipeline_prev_act)(void *handle); /*!< */ + +/** + * @brief Structure representing a pipeline in GMF + */ +typedef struct esp_gmf_pipeline { + esp_gmf_element_handle_t head_el; /*!< Handle of the first element in the pipeline */ + esp_gmf_element_handle_t last_el; /*!< Handle of the last element in the pipeline */ + esp_gmf_io_handle_t in; /*!< Handle of the input I/O port */ + esp_gmf_io_handle_t out; /*!< Handle of the output I/O port */ + esp_gmf_event_item_t *evt_conveyor; /*!< Event conveyor list */ + esp_gmf_event_cb evt_acceptor; /*!< Event acceptor callback function */ + esp_gmf_event_cb user_cb; /*!< User callback function */ + void *user_ctx; /*!< User context */ + esp_gmf_event_state_t state; /*!< Current state of the pipeline */ + esp_gmf_task_handle_t thread; /*!< Handle of the task associated with the pipeline */ + esp_gmf_pipeline_prev_act prev_run; /*!< A pointer to the previous run callback */ + esp_gmf_pipeline_prev_act prev_stop; /*!< A pointer to the previous stop callback */ + void *prev_run_ctx; /*!< The previous run context */ + void *prev_stop_ctx; /*!< The previous stop context */ + uint8_t prev_state; /*!< The previous action state */ + void *lock; /*!< Lock for thread synchronization */ +} esp_gmf_pipeline_t; + +/** + * @brief The GMF pipeline configuration structure + */ +typedef struct esp_gmf_pipeline_cfg { + esp_gmf_event_cb event; /*!< Event callback function */ +} esp_gmf_pipeline_cfg_t; + +/** + * @brief Create a new GMF pipeline + * + * @param[out] pipeline Pointer to store the newly created GMF pipeline handle + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG If the pipeline handle is invalid + * - ESP_GMF_ERR_MEMORY_LACK Memory allocation failed + */ +esp_gmf_err_t esp_gmf_pipeline_create(esp_gmf_pipeline_handle_t *pipeline); + +/** + * @brief Destroy a GMF pipeline, freeing associated resources + * + * @param[in] pipeline GMF pipeline handle to destroy + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG If the pipeline handle is invalid + */ +esp_gmf_err_t esp_gmf_pipeline_destroy(esp_gmf_pipeline_handle_t pipeline); + +/** + * @brief Set the I/O handle for a GMF pipeline + * + * @param[in] pipeline GMF pipeline handle + * @param[in] io GMF I/O handle + * @param[in] dir I/O direction (input or output) + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG If the pipeline handle or I/O port is invalid + */ +esp_gmf_err_t esp_gmf_pipeline_set_io(esp_gmf_pipeline_handle_t pipeline, esp_gmf_io_handle_t io, int dir); + +/** + * @brief Register a GMF element with a GMF pipeline + * + * @param[in] pipeline GMF pipeline handle + * @param[in] el GMF element handle to register + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG if the pipeline handle or element is invalid + */ +esp_gmf_err_t esp_gmf_pipeline_register_el(esp_gmf_pipeline_handle_t pipeline, esp_gmf_element_handle_t el); + +/** + * @brief List all GMF elements in the pipeline + * + * @param[in] pipeline GMF pipeline handle + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG If the pipeline handle is invalid + */ +esp_gmf_err_t esp_gmf_pipeline_list_el(esp_gmf_pipeline_handle_t pipeline); + +/** + * @brief Set the event callback function for a GMF pipeline + * + * @param[in] pipeline GMF pipeline handle + * @param[in] cb Event callback function + * @param[in] ctx Context to be passed to the callback function + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG If the pipeline handle is invalid + */ +esp_gmf_err_t esp_gmf_pipeline_set_event(esp_gmf_pipeline_handle_t pipeline, esp_gmf_event_cb cb, void *ctx); + +/** + * @brief Bind a given task to the pipeline + * + * @param[in] pipeline GMF pipeline handle + * @param[in] task GMF task handle + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG If the pipeline or task handle is invalid + */ +esp_gmf_err_t esp_gmf_pipeline_bind_task(esp_gmf_pipeline_handle_t pipeline, esp_gmf_task_handle_t task); + +/** + * @brief Load linked element jobs to the bind task on the specific pipeline + * + * @param[in] pipeline GMF pipeline handle + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG If the pipeline or task handle is invalid + */ +esp_gmf_err_t esp_gmf_pipeline_loading_jobs(esp_gmf_pipeline_handle_t pipeline); + +/** + * @brief Set the input URI for the specific pipeline + * + * @param[in] pipeline GMF pipeline handle + * @param[in] uri Input URI + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG If the pipeline handle is invalid + * - ESP_GMF_ERR_MEMORY_LACK Memory allocation for URI failed + */ +esp_gmf_err_t esp_gmf_pipeline_set_in_uri(esp_gmf_pipeline_handle_t pipeline, const char *uri); + +/** + * @brief Set the output URI for specific pipeline + * + * @param[in] pipeline GMF pipeline handle + * @param[in] uri Output URI + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG If the pipeline handle is invalid + * - ESP_GMF_ERR_MEMORY_LACK Memory allocation for URI failed + */ +esp_gmf_err_t esp_gmf_pipeline_set_out_uri(esp_gmf_pipeline_handle_t pipeline, const char *uri); + +/** + * @brief Register an event recipient for specific pipeline + * + * @param[in] connector GMF pipeline handle of the connector + * @param[in] connectee GMF pipeline handle of the connectee + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG If the pipeline handles are invalid + * - ESP_GMF_ERR_MEMORY_LACK Memory allocation failed + */ +esp_gmf_err_t esp_gmf_pipeline_reg_event_recipient(esp_gmf_pipeline_handle_t connector, esp_gmf_pipeline_handle_t connectee); + +/** + * @brief Connect two GMF pipelines + * + * @param[in] connector GMF pipeline handle of the connector + * @param[in] connector_name Name of the connector element + * @param[in] connector_port Port handle of the connector element + * @param[in] connectee GMF pipeline handle of the connectee + * @param[in] connectee_name Name of the connectee element + * @param[in] connectee_port Port handle of the connectee element + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG If the handles are invalid + * - ESP_GMF_ERR_NOT_FOUND The connector_name is not in the connector pipeline, or connectee_name is not in the connectee pipeline + * - ESP_GMF_ERR_NOT_SUPPORT The given port is not support + */ +esp_gmf_err_t esp_gmf_pipeline_connect_pipe(esp_gmf_pipeline_handle_t connector, const char *connector_name, esp_gmf_port_handle_t connector_port, + esp_gmf_pipeline_handle_t connectee, const char *connectee_name, esp_gmf_port_handle_t connectee_port); + +/** + * @brief Set action callback before do `esp_gmf_pipeline_run` + * If `prev_run` is NULL, no previous run function will be invoked + * + * @param[in] pipeline Handle to the GMF pipeline + * @param[in] prev_run Callback function to execute before run the pipeline + * @param[in] ctx Context to be passed to the previous action function + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid pipeline or callback argument + */ +esp_gmf_err_t esp_gmf_pipeline_set_prev_run_cb(esp_gmf_pipeline_handle_t pipeline, esp_gmf_pipeline_prev_act prev_run, void *ctx); + +/** + * @brief Set action callback before do `esp_gmf_pipeline_stop` + * If `prev_stop` is NULL, no previous stop function will be invoked + * + * @param[in] pipeline Handle to the GMF pipeline + * @param[in] prev_stop Callback function to execute before stop the pipeline + * @param[in] ctx Context to be passed to the previous action function + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid pipeline or callback argument + */ +esp_gmf_err_t esp_gmf_pipeline_set_prev_stop_cb(esp_gmf_pipeline_handle_t pipeline, esp_gmf_pipeline_prev_act prev_stop, void *ctx); + +/** + * @brief Manually trigger the previous run action for a pipeline + * + * @note Typically used to perform setup operations before dependency pipelines run + * + * @param[in] pipeline Handle to the GMF pipeline + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid pipeline + */ +esp_gmf_err_t esp_gmf_pipeline_prev_run(esp_gmf_pipeline_handle_t pipeline); + +/** + * @brief Manually trigger the previous stop action for a pipeline + * + * @note Typically used to perform cleanup operations before dependency pipelines stop + * + * @param[in] pipeline Handle to the GMF pipeline + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid pipeline + */ +esp_gmf_err_t esp_gmf_pipeline_prev_stop(esp_gmf_pipeline_handle_t pipeline); + +/** + * @brief Run the specific pipeline using `esp_gmf_task_run`, which blocks by default for `DEFAULT_TASK_OPT_MAX_TIME_MS` + * To change the waiting time, use `esp_gmf_task_set_timeout` + * + * @note This API will automatically trigger the `prev_run` action if configured and not manually triggered yet + * + * + * @param[in] pipeline GMF pipeline handle + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG If the pipeline or thread handle is invalid + * - ESP_GMF_ERR_NOT_SUPPORT Indicating the state of task is ESP_GMF_EVENT_STATE_PAUSED or ESP_GMF_EVENT_STATE_RUNNING + * - ESP_GMF_ERR_INVALID_STATE The task is not running + * - ESP_GMF_ERR_TIMEOUT Indicating that the synchronization operation has timed out + */ +esp_gmf_err_t esp_gmf_pipeline_run(esp_gmf_pipeline_handle_t pipeline); + +/** + * @brief Stop a running pipeline using `esp_gmf_task_stop`, which blocks by default for `DEFAULT_TASK_OPT_MAX_TIME_MS` + * To change the waiting time, use `esp_gmf_task_set_timeout` + * + * @note This API will automatically trigger the `prev_stop` action if configured and not manually triggered yet + * + * @param[in] pipeline GMF pipeline handle + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG If the pipeline or thread handle is invalid + * - ESP_GMF_ERR_NOT_SUPPORT The state of task is ESP_GMF_EVENT_STATE_NONE + * - ESP_GMF_ERR_INVALID_STATE The task is not running + * - ESP_GMF_ERR_TIMEOUT Indicating that the synchronization operation has timed out + */ +esp_gmf_err_t esp_gmf_pipeline_stop(esp_gmf_pipeline_handle_t pipeline); + +/** + * @brief Pause a running GMF pipeline using `esp_gmf_task_pause`, which blocks by default for `DEFAULT_TASK_OPT_MAX_TIME_MS` + * To change the waiting time, use `esp_gmf_task_set_timeout` + * + * @param[in] pipeline GMF pipeline handle + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG If the pipeline or thread handle is invalid + * - ESP_GMF_ERR_NOT_SUPPORT The state of task is not ESP_GMF_EVENT_STATE_RUNNING + * - ESP_GMF_ERR_TIMEOUT Indicating that the synchronization operation has timed out + */ +esp_gmf_err_t esp_gmf_pipeline_pause(esp_gmf_pipeline_handle_t pipeline); + +/** + * @brief Resume a paused GMF pipeline using `esp_gmf_task_resume`, which blocks by default for `DEFAULT_TASK_OPT_MAX_TIME_MS` + * To change the waiting time, use `esp_gmf_task_set_timeout` + * + * @param[in] pipeline GMF pipeline handle + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG If the pipeline or thread handle is invalid + * - ESP_GMF_ERR_NOT_SUPPORT The state of task is not ESP_GMF_EVENT_STATE_PAUSED + * - ESP_GMF_ERR_TIMEOUT Indicating that the synchronization operation has timed out + */ +esp_gmf_err_t esp_gmf_pipeline_resume(esp_gmf_pipeline_handle_t pipeline); + +/** + * @brief Reset the GMF pipeline to its initial state, including job lists, port states, and element states + * To run the pipeline again, `esp_gmf_pipeline_loading_jobs` must be called + * + * @param[in] pipeline GMF pipeline handle + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG If the pipeline handle is invalid + */ +esp_gmf_err_t esp_gmf_pipeline_reset(esp_gmf_pipeline_handle_t pipeline); + +/** + * @brief Seeking to a specific position in the pipeline calls `io_seek`, which means it only + * supports streaming audio formats like MP3, AAC, and TS, where each frame can be decoded independently + * + * @param[in] pipeline GMF pipeline handle + * @param[in] pos Position to seek to + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG If the pipeline handle is invalid + * - ESP_GMF_ERR_INVALID_STATE + */ +esp_gmf_err_t esp_gmf_pipeline_seek(esp_gmf_pipeline_handle_t pipeline, uint64_t pos); + +/** + * @brief Retrieve the linked pipeline from given pipeline + * + * @param[in] connector Handle to the GMF connector pipeline + * @param[out] iterator A void pointer to the iterative pipeline + * @param[out] connectee Handle to the connected pipeline + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid connector or connectee handle + * - ESP_GMF_ERR_NOT_FOUND No linked pipeline found + * - ESP_GMF_ERR_INVALID_STATE Connector is not in a valid state for linking + */ +esp_gmf_err_t esp_gmf_pipeline_get_linked_pipeline(esp_gmf_pipeline_handle_t connector, const void **iterator, esp_gmf_pipeline_handle_t *connectee); + +/** + * @brief Get the input I/O of a GMF pipeline + * + * @param[in] pipeline GMF pipeline handle + * @param[out] io_handle Pointer to store the input I/O handle + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG If the pipeline handle is invalid + */ +esp_gmf_err_t esp_gmf_pipeline_get_in(esp_gmf_pipeline_handle_t pipeline, esp_gmf_io_handle_t *io_handle); + +/** + * @brief Get the output I/O of a GMF pipeline + * + * @param[in] pipeline GMF pipeline handle + * @param[out] io_handle Pointer to store the output I/O handle + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG If the pipeline or thread handle is invalid + */ +esp_gmf_err_t esp_gmf_pipeline_get_out(esp_gmf_pipeline_handle_t pipeline, esp_gmf_io_handle_t *io_handle); + +/** + * @brief Replace the input I/O of a GMF pipeline with a new one + * + * @note Call esp_gmf_pipeline_get_in function before you call this function + * Because of the pineline in will be overwritten and the replaced MUST release by user + * + * @param[in] pipeline GMF pipeline handle + * @param[in] new_one New input I/O handle + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG If the pipeline handle or new I/O handle is invalid + * - ESP_GMF_ERR_INVALID_STATE The pipeline is running, replace is not safe + */ +esp_gmf_err_t esp_gmf_pipeline_replace_in(esp_gmf_pipeline_handle_t pipeline, esp_gmf_io_handle_t new_one); + +/** + * @brief Replace the output I/O of a GMF pipeline with a new one + * + * @note Call esp_gmf_pipeline_get_out function before you call this function + * Because of the pineline in will be overwritten and the replaced MUST release by user + * + * @param[in] pipeline GMF pipeline handle + * @param[in] new_one New output I/O handle + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG If the pipeline handle or new I/O port handle is invalid + * - ESP_GMF_ERR_INVALID_STATE The pipeline is running, replace is not safe + */ +esp_gmf_err_t esp_gmf_pipeline_replace_out(esp_gmf_pipeline_handle_t pipeline, esp_gmf_io_handle_t new_one); + +/** + * @brief Get the header GMF element in the pipeline + * + * @param[in] pipeline GMF pipeline handle + * @param[out] head Head element of the pipeline + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG If the pipeline handle or head element is invalid + */ +esp_gmf_err_t esp_gmf_pipeline_get_head_el(esp_gmf_pipeline_handle_t pipeline, esp_gmf_element_handle_t *head); + +/** + * @brief Get the next GMF element in the pipeline + * + * @param[in] pipeline GMF pipeline handle + * @param[in] head Head element of the pipeline + * @param[out] next Pointer to store the next GMF element handle + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG If the pipeline handle or head element is invalid + */ +esp_gmf_err_t esp_gmf_pipeline_get_next_el(esp_gmf_pipeline_handle_t pipeline, esp_gmf_element_handle_t head, esp_gmf_element_handle_t *next); + +/** + * @brief Get a element in specific pipeline by its name + * + * @param[in] pipeline GMF pipeline handle + * @param[in] tag Name of the wanted element + * @param[out] out_handle Pointer to store the element handle + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG If the pipeline handle or tag is invalid + * - ESP_GMF_ERR_NOT_FOUND It's not found the element by given tag + */ +esp_gmf_err_t esp_gmf_pipeline_get_el_by_name(esp_gmf_pipeline_handle_t pipeline, const char *tag, esp_gmf_element_handle_t *out_handle); + +/** + * @brief Register an I/O port for an element within the pipeline + * + * @param[in] pipeline GMF pipeline handle + * @param[in] tag Name of the GMF element + * @param[in] io_dir I/O direction type (input or output) + * @param[in] port I/O port handle to register + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG If the pipeline handle, tag, or port handle is invalid + * - ESP_GMF_ERR_NOT_SUPPORT The specified port type is not supported, + * or the element can't connect more port, or the given direction is not supported + * - ESP_GMF_ERR_NOT_FOUND It's not found the element by given tag + */ +esp_gmf_err_t esp_gmf_pipeline_reg_el_port(esp_gmf_pipeline_handle_t pipeline, const char *tag, esp_gmf_io_dir_t io_dir, esp_gmf_io_handle_t port); + +/** + * @brief Report information from first element of pipeline + * + * @note This function is only for the GMF's first element, which does not report music information. However, this information is used by the linked elements. + * For example, if the pipeline is `rate conversion -> channel conversion -> encoder -> file`, the rate conversion and channel conversion elements + * require the music information to trigger their loading jobs. Therefore, this API is called to report the music information to the first element. + * + * @param[in] pipeline The handle to the GMF pipeline + * @param[in] info_type The type of information to report, specified by `esp_gmf_info_type_t` + * @param[in] value Pointer to the buffer where the information will be stored + * @param[in] len Length of the buffer pointed to by `value`, in bytes + * + * @return + * - ESP_GMF_ERR_OK Success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument(s) provided + * - ESP_GMF_ERR_MEMORY_LACK Insufficient memory to store the information + * - ESP_GMF_ERR_NOT_SUPPORTED The requested information type is not supported + */ +esp_gmf_err_t esp_gmf_pipeline_report_info(esp_gmf_pipeline_handle_t pipeline, esp_gmf_info_type_t info_type, void *value, int len); + +/** + * @brief Print information about a GMF pipeline + * + * @param[in] handle GMF pipeline handle + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG If the pipeline handle is invalid + */ +esp_gmf_err_t esp_gmf_pipeline_show(esp_gmf_pipeline_handle_t handle); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/include/esp_gmf_pool.h b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/include/esp_gmf_pool.h new file mode 100644 index 0000000..d433fb1 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/include/esp_gmf_pool.h @@ -0,0 +1,176 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#include "esp_gmf_audio_element.h" +#include "esp_gmf_io.h" +#include "esp_gmf_pipeline.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +typedef struct esp_gmf_pool *esp_gmf_pool_handle_t; + +/** + * @brief Initialize a GMF pool + * + * @param[out] handle Pointer to store the handle of the initialized GMF pool + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + * - ESP_GMF_ERR_MEMORY_LACK Memory allocation failed + */ +esp_gmf_err_t esp_gmf_pool_init(esp_gmf_pool_handle_t *handle); + +/** + * @brief Deinitialize a GMF pool, freeing registered elements and I/O instances + * + * @param[in] handle GMF pool handle to deinitialize + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_pool_deinit(esp_gmf_pool_handle_t handle); + +/** + * @brief Register a GMF element to specific pool + * + * @note Once an element is registered to the pool, the pool takes ownership of it + * The user should avoid destroying the element manually to prevent issues + * caused by the pool accessing an invalid element + * + * @param[in] handle GMF pool handle + * @param[in] el GMF element handle to register + * @param[in] tag Tag associated with the element + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid arguments + * - ESP_GMF_ERR_MEMORY_LACK Memory allocation failed + */ +esp_gmf_err_t esp_gmf_pool_register_element(esp_gmf_pool_handle_t handle, esp_gmf_element_handle_t el, const char *tag); + +/** + * @brief Insert a GMF element at the head of specific pool + * + * @note This API functions like `esp_gmf_pool_register_element`, except that it + * inserts the element at the head of the pool to ensure higher search priority + * + * @param[in] handle GMF pool handle + * @param[in] el GMF element handle to register + * @param[in] tag Tag associated with the element + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid arguments + * - ESP_GMF_ERR_MEMORY_LACK Memory allocation failed + */ +esp_gmf_err_t esp_gmf_pool_register_element_at_head(esp_gmf_pool_handle_t handle, esp_gmf_element_handle_t el, const char *tag); + +/** + * @brief Register a GMF I/O instance with a GMF pool + * + * @param[in] handle GMF pool handle + * @param[in] port GMF I/O handle to register + * @param[in] tag Tag associated with the I/O port + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid arguments + * - ESP_GMF_ERR_MEMORY_LACK Memory allocation failed + */ +esp_gmf_err_t esp_gmf_pool_register_io(esp_gmf_pool_handle_t handle, esp_gmf_io_handle_t port, const char *tag); + +/** + * @brief Create a new GMF pipeline from the specific pool + * It checks if the element is already registered. If so, it duplicates them, links them together + * Similarly to the element, it checks the I/O ports, duplicates them, and then adds input and output ports to the pipeline + * If either `in_name` or `out_name` is NULL, the corresponding port will be ignored + * + * @param[in] handle GMF pool handle + * @param[in] in_name Name of the input element + * @param[in] el_name Array of names for intermediate elements + * @param[in] num_of_el_name Number of elements in the el_name array + * @param[in] out_name Name of the output element + * @param[out] pipeline Pointer to store the created GMF pipeline handle + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_MEMORY_LACK Memory allocation failed + * - ESP_GMF_ERR_INVALID_ARG Invalid argument or the number of names is incorrect + * - ESP_GMF_ERR_NOT_SUPPORT Not support port type + * - ESP_GMF_ERR_NOT_FOUND Not found the specific instance + * - ESP_GMF_ERR_FAIL Others failed + */ +esp_gmf_err_t esp_gmf_pool_new_pipeline(esp_gmf_pool_handle_t handle, const char *in_name, + const char *el_name[], int num_of_el_name, + const char *out_name, + esp_gmf_pipeline_handle_t *pipeline); + +/** + * @brief Create a new I/O instance from the GMF pool by given name + * + * @param[in] handle GMF pool handle + * @param[in] name Name of the I/O handle + * @param[in] dir Direction of the I/O (reader or writer) + * @param[out] new_io Pointer to the new I/O handle to be created + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid arguments + * - ESP_GMF_ERR_MEMORY_LACK Memory allocation failed + * - ESP_GMF_ERR_NOT_FOUND Not found the specific I/O instance + */ +esp_gmf_err_t esp_gmf_pool_new_io(esp_gmf_pool_handle_t handle, const char *name, esp_gmf_io_dir_t dir, esp_gmf_io_handle_t *new_io); + +/** + * @brief Create a new element from the GMF pool by given name + * + * @param[in] handle GMF pool handle + * @param[in] el_name Name of the element + * @param[out] new_el Pointer to the new element handle to be created + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid arguments + * - ESP_GMF_ERR_MEMORY_LACK Memory allocation failed + * - ESP_GMF_ERR_NOT_FOUND Not found the specific element instance + */ +esp_gmf_err_t esp_gmf_pool_new_element(esp_gmf_pool_handle_t handle, const char *el_name, esp_gmf_element_handle_t *new_el); + +/** + * @brief Iterate pool to retrieve element + * + * @param[in] handle GMF pool handle + * @param[out] iterator To retrieve first element set `*iterator = NULL`, after that do not modify `*iterator` any more + * @param[out] el To store retrieved element handle + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid arguments + * - ESP_GMF_ERR_NOT_FOUND Iterate finished, no more elements found + */ +esp_gmf_err_t esp_gmf_pool_iterate_element(esp_gmf_pool_handle_t handle, const void **iterator, esp_gmf_element_handle_t *el); + +/** + * @brief Print information about elements and IOs registered in a GMF pool + * + * @param[in] handle GMF pool handle + * @param[in] line Line of log + * @param[in] func Function name of log + */ +void esp_gmf_pool_show_lists(esp_gmf_pool_handle_t handle, int line, const char *func); + +#define ESP_GMF_POOL_SHOW_ITEMS(x) esp_gmf_pool_show_lists(x, __LINE__, __FUNCTION__) + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/include/esp_gmf_port.h b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/include/esp_gmf_port.h new file mode 100644 index 0000000..9f37a36 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/include/esp_gmf_port.h @@ -0,0 +1,378 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#include +#include "sys/queue.h" +#include "esp_gmf_err.h" +#include "esp_gmf_payload.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define ESP_GMF_PORT_CHECK(log_tag, ret, ret_value, action, format, ...) { \ + if (unlikely(ret < ESP_GMF_IO_OK)) { \ + if (ret != ESP_GMF_IO_ABORT) { \ + ESP_LOGE(log_tag, "%s(%d): " format, __FUNCTION__, __LINE__ __VA_OPT__(, ) __VA_ARGS__); \ + ret_value = ESP_GMF_ERR_FAIL; \ + } else { \ + ret_value = ESP_GMF_ERR_OK; \ + } \ + action; \ + } \ +} + +#define ESP_GMF_PORT_ACQUIRE_IN_CHECK(TAG, ret, ret_value, action) ESP_GMF_PORT_CHECK(TAG, ret, ret_value, action, "Failed to acquire in, ret: %d", ret) + +#define ESP_GMF_PORT_ACQUIRE_OUT_CHECK(TAG, ret, ret_value, action) ESP_GMF_PORT_CHECK(TAG, ret, ret_value, action, "Failed to acquire out, ret: %d", ret) + +#define ESP_GMF_PORT_RELEASE_IN_CHECK(TAG, ret, ret_value, action) ESP_GMF_PORT_CHECK(TAG, ret, ret_value, action, "Failed to release in, ret: %d", ret) + +#define ESP_GMF_PORT_RELEASE_OUT_CHECK(TAG, ret, ret_value, action) ESP_GMF_PORT_CHECK(TAG, ret, ret_value, action, "Failed to release out, ret: %d", ret) + +/** + * @brief Definition the direction of a GMF port (input or output) + */ +#define ESP_GMF_PORT_DIR_IN (0) /*!< Input port */ +#define ESP_GMF_PORT_DIR_OUT (1) /*!< Output port */ + +/** + * @brief Definition the bit mask for the type of data handled by a GMF port (byte or block) + * - A byte-type port transfers values by copying data byte by byte. Users must allocate memory, + * which means data transmission involves copying. This type of port allows convenient access to arbitrary byte lengths + * - A block-type port transfers data by passing memory addresses. The memory used by the user is provided by another source, + * so data transmission does not involve copying. However, accessing arbitrary byte lengths is less flexible and may require data concatenation + */ +#define ESP_GMF_PORT_TYPE_BYTE (0x01) /*!< Bit0 for the byte type of GMF port */ +#define ESP_GMF_PORT_TYPE_BLOCK (0x02) /*!< Bit1 for the block type of GMF port */ + +/** + * @brief Handle to a GMF port + */ +typedef struct esp_gmf_port_ *esp_gmf_port_handle_t; + +/** + * @brief Function pointer type for acquiring data from a port + */ +typedef esp_gmf_err_io_t (*port_acquire)(void *handle, esp_gmf_payload_t *load, uint32_t wanted_size, int wait_ticks); + +/** + * @brief Function pointer type for releasing data from a port + */ +typedef esp_gmf_err_io_t (*port_release)(void *handle, esp_gmf_payload_t *load, int wait_ticks); + +/** + * @brief Function pointer type for freeing a port + */ +typedef void (*port_free)(void *p); + +/** + * @brief Structure defining the I/O operations of a GMF port + */ +typedef struct { + port_acquire acquire; /*!< Function pointer for acquiring data */ + port_release release; /*!< Function pointer for releasing data */ + port_free del; /*!< Function pointer for freeing the port */ +} esp_gmf_port_io_ops_t; + +/** + * @brief Structure defining the attributes of a GMF port + */ +typedef struct { + uint8_t buf_addr_aligned; /*!< Byte-align the address of the buffer */ + uint8_t buf_size_aligned; /*!< Byte-align the length of the buffer */ + uint8_t dir; /*!< Port direction */ + uint8_t type; /*!< Port type */ +} esp_gmf_port_attr_t; + +/** + * @brief Structure representing a GMF port + * The usage of the port in linked elements is as follows + * + * +---------+ +---------------+ +----------+ + * | In Port +-----> First Element +----> Out Port | + * +---------+ +-------+-------+ +----------+ + * | + * v + * +---------+ +-------+-------+ +----------+ + * | In Port +-----> More Element +----> Out Port | + * +---------+ +-------+-------+ +----------+ + * | + * v + * +---------+ +-------+-------+ +----------+ + * | In Port +-----> Last Element +----> Out Port | + * +---------+ +---------------+ +----------+ + */ +typedef struct esp_gmf_port_ { + struct esp_gmf_port_ *next; /*!< Pointer to the next port */ + void *writer; /*!< Acquire out functions caller with the port */ + void *reader; /*!< Acquire in functions caller with the port */ + esp_gmf_port_io_ops_t ops; /*!< I/O operations of the port */ + esp_gmf_port_attr_t attr; /*!< Port attributes */ + int data_length; /*!< Data length of the payload */ + void *ctx; /*!< User context for the port */ + int wait_ticks; /*!< Timeout for port operations */ + esp_gmf_payload_t *payload; /*!< Payload pointer to be set */ + uint8_t is_shared : 1; /*!< Payload is shared to the next element port or not, 1 for shared (default), 0 for dedicated */ + esp_gmf_payload_t *self_payload; /*!< Self payload of the port */ + struct esp_gmf_port_ *ref_port; /*!< Pointer to the reference port */ + int8_t ref_count; /*!< Reference count indicating the number of active references */ +} esp_gmf_port_t; + +/** + * @brief Structure defining the configuration of a GMF port + */ +typedef struct esp_gmf_port_config_ { + uint8_t dir; /*!< Direction of the port */ + uint8_t type; /*!< Type of data handled by the port */ + esp_gmf_port_io_ops_t ops; /*!< I/O operations of the port */ + void *ctx; /*!< User context associated with the port */ + int data_length; /*!< Data length of the port */ + int wait_ticks; /*!< Timeout for port operations */ +} esp_gmf_port_config_t; + +/** + * @brief Initialize a GMF port with the given configuration + * + * @param[in] cfg Pointer to the configuration structure + * @param[out] out_result Pointer to store the result handle after initialization + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + * - ESP_GMF_ERR_MEMORY_LACK Memory allocate failed + */ +esp_gmf_err_t esp_gmf_port_init(esp_gmf_port_config_t *cfg, esp_gmf_port_handle_t *out_result); + +/** + * @brief Deinitialize a GMF port + * + * @param[in] handle The handle of the port to be deinitialized + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_port_deinit(esp_gmf_port_handle_t handle); + +/** + * @brief Set the self payload for the specific port + * + * @param[in] handle The handle of the port + * @param[in] load Pointer to the payload structure + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_port_set_payload(esp_gmf_port_handle_t handle, esp_gmf_payload_t *load); + +/** + * @brief Clean the payload done status of a GMF port + * + * @param[in] handle The handle of the port + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_port_clean_payload_done(esp_gmf_port_handle_t handle); + +/** + * @brief Enables or disables shared payload for the specified port + * + * @param[in] handle The port handle to enable or disable payload sharing on + * @param[in] enable Set to true to enable payload sharing, or false to disable it + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument provided + */ +esp_gmf_err_t esp_gmf_port_enable_payload_share(esp_gmf_port_handle_t handle, bool enable); + +/** + * @brief Reset the port payload and variable of self payload + * + * @param[in] handle The handle of the port + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_port_reset(esp_gmf_port_handle_t handle); + +/** + * @brief Set the wait ticks for the specific port + * + * @param[in] handle The handle of the port + * @param[in] wait_ticks_ms Number of milliseconds to wait + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_port_set_wait_ticks(esp_gmf_port_handle_t handle, int wait_ticks_ms); + +/** + * @brief Set the esp_gmf_port_acquire_in and esp_gmf_port_release_in caller for the specific port + * + * @param[in] handle The handle of the port + * @param[in] reader Pointer to the reader + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_port_set_reader(esp_gmf_port_handle_t handle, void *reader); + +/** + * @brief Set the esp_gmf_port_acquire_out and esp_gmf_port_acquire_out caller for the specific port + * + * @param[in] handle The handle of the port + * @param[in] writer Pointer to the writer + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_port_set_writer(esp_gmf_port_handle_t handle, void *writer); + +/** + * @brief Add a GMF port to the end of the list + * + * @param[in] head The head of the port list + * @param[in] io_inst The port instance to be added + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_port_add_last(esp_gmf_port_handle_t head, esp_gmf_port_handle_t io_inst); + +/** + * @brief Delete a GMF port from the list + * + * @param[in,out] head A pointer to the head of the port list + * @param[in] io_inst The port instance to be deleted + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_port_del_at(esp_gmf_port_handle_t *head, esp_gmf_port_handle_t io_inst); + +/** + * @brief Acquire the expected valid data into the specified payload, regardless of whether the payload is NULL or not + * If writer of port is valid, the payload from the previous element stored on the port `payload` pointer is fetched + * If the `*load` pointer is NULL, a new payload will be allocated before calling the acquire operation + * The actual valid size is stored in `load->valid_size` + * + * @param[in] handle The handle of the port + * @param[in,out] load Pointer to store the acquired payload + * @param[in] wanted_size Size of the payload to be acquired + * @param[in] wait_ticks Number of ticks to wait, in milliseconds + * + * @return + * - ESP_GMF_IO_OK Operation successful + * - ESP_GMF_IO_FAIL Operation failed or invalid arguments + * - ESP_GMF_IO_ABORT Operation aborted + * - ESP_GMF_IO_TIMEOUT Operation timed out + */ +esp_gmf_err_io_t esp_gmf_port_acquire_in(esp_gmf_port_handle_t handle, esp_gmf_payload_t **load, uint32_t wanted_size, int wait_ticks); + +/** + * @brief Call the release operation or clean the payload pointer of the port + * + * @param[in] handle The handle of the port + * @param[in] load Pointer to the payload to be released + * @param[in] wait_ticks Number of ticks to wait + * + * @return + * - ESP_GMF_IO_OK Operation successful + * - ESP_GMF_IO_FAIL Operation failed or invalid arguments + * - ESP_GMF_IO_ABORT Operation aborted + * - ESP_GMF_IO_TIMEOUT Operation timed out + */ +esp_gmf_err_io_t esp_gmf_port_release_in(esp_gmf_port_handle_t handle, esp_gmf_payload_t *load, int wait_ticks); + +/** + * @brief Acquire the buffer of the expected size into the specified payload, + * If the reader of the port is valid, store the provided or allocated payload to the input port of the next element + * If reader pointer is NULL, prepare a payload if `*load` is invalid before calling the acquire operation + * The actual valid size is stored in `load->valid_size` + * + * @param[in] handle The handle of the port + * @param[in,out] load Pointer to store the acquired payload + * @param[in] wanted_size Size of the payload to be acquired + * @param[in] wait_ticks Number of ticks to wait, in milliseconds + * + * @return + * - ESP_GMF_IO_OK Operation successful + * - ESP_GMF_IO_FAIL Operation failed or invalid arguments + * - ESP_GMF_IO_ABORT Operation aborted + * - ESP_GMF_IO_TIMEOUT Operation timed out + */ +esp_gmf_err_io_t esp_gmf_port_acquire_out(esp_gmf_port_handle_t handle, esp_gmf_payload_t **load, uint32_t wanted_size, int wait_ticks); + +/** + * @brief Call the release operation or clean the payload pointer of the port + * + * @param[in] handle The handle of the port + * @param[in] load Pointer to the payload to be released + * @param[in] wait_ticks Number of ticks to wait + * + * @return + * - ESP_GMF_IO_OK Operation successful + * - ESP_GMF_IO_FAIL Operation failed or invalid arguments + * - ESP_GMF_IO_ABORT Operation aborted + * - ESP_GMF_IO_TIMEOUT Operation timed out + */ +esp_gmf_err_io_t esp_gmf_port_release_out(esp_gmf_port_handle_t handle, esp_gmf_payload_t *load, int wait_ticks); + +static inline void *NEW_ESP_GMF_PORT(uint8_t dir, uint8_t type, void *acq, void *release, + void *del, void *ctx, int length, int ticks_ms) +{ + esp_gmf_port_config_t port_config = { + .dir = (uint8_t)dir, + .type = (uint8_t)type, + .ops = { + .acquire = (port_acquire)acq, + .release = (port_release)release, + .del = (port_free)del, + }, + .ctx = ctx, + .data_length = length, + .wait_ticks = ticks_ms, + }; + esp_gmf_port_handle_t new_port = NULL; + esp_gmf_port_init(&port_config, &new_port); + return new_port; +} + +static inline void *NEW_ESP_GMF_PORT_IN_BYTE(void *acq, void *release, void *del, void *ctx, int length, int ticks_ms) +{ + return NEW_ESP_GMF_PORT(ESP_GMF_PORT_DIR_IN, ESP_GMF_PORT_TYPE_BYTE, acq, release, del, ctx, length, ticks_ms); +} +static inline void *NEW_ESP_GMF_PORT_OUT_BYTE(void *acq, void *release, void *del, void *ctx, int length, int ticks_ms) +{ + return NEW_ESP_GMF_PORT(ESP_GMF_PORT_DIR_OUT, ESP_GMF_PORT_TYPE_BYTE, acq, release, del, ctx, length, ticks_ms); +} + +static inline void *NEW_ESP_GMF_PORT_IN_BLOCK(void *acq, void *release, void *del, void *ctx, int length, int ticks_ms) +{ + return NEW_ESP_GMF_PORT(ESP_GMF_PORT_DIR_IN, ESP_GMF_PORT_TYPE_BLOCK, acq, release, del, ctx, length, ticks_ms); +} +static inline void *NEW_ESP_GMF_PORT_OUT_BLOCK(void *acq, void *release, void *del, void *ctx, int length, int ticks_ms) +{ + return NEW_ESP_GMF_PORT(ESP_GMF_PORT_DIR_OUT, ESP_GMF_PORT_TYPE_BLOCK, acq, release, del, ctx, length, ticks_ms); +} + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/include/esp_gmf_queue.h b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/include/esp_gmf_queue.h new file mode 100644 index 0000000..49d7450 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/include/esp_gmf_queue.h @@ -0,0 +1,180 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)queue.h 8.5 (Berkeley) 8/20/94 + * $FreeBSD$ + */ + +#ifdef QUEUE_MACRO_DEBUG +/* Store the last 2 places the queue element or head was altered */ +struct qm_trace { + unsigned long lastline; + unsigned long prevline; + const char *lastfile; + const char *prevfile; +}; + +#define TRACEBUF struct qm_trace trace; +#define TRACEBUF_INITIALIZER {__FILE__, __LINE__, NULL, 0}, +#define TRASHIT(x) do { \ + (x) = (void *)-1; \ +} while (0) +#define QMD_SAVELINK(name, link) void **name = (void *)&(link) + +#define QMD_TRACE_HEAD(head) do { \ + (head)->trace.prevline = (head)->trace.lastline; \ + (head)->trace.prevfile = (head)->trace.lastfile; \ + (head)->trace.lastline = __LINE__; \ + (head)->trace.lastfile = __FILE__; \ +} while (0) + +#define QMD_TRACE_ELEM(elem) do { \ + (elem)->trace.prevline = (elem)->trace.lastline; \ + (elem)->trace.prevfile = (elem)->trace.lastfile; \ + (elem)->trace.lastline = __LINE__; \ + (elem)->trace.lastfile = __FILE__; \ +} while (0) + +#else +#define QMD_TRACE_ELEM(elem) +#define QMD_TRACE_HEAD(head) +#define QMD_SAVELINK(name, link) +#define TRACEBUF +#define TRACEBUF_INITIALIZER +#define TRASHIT(x) +#endif /* QUEUE_MACRO_DEBUG */ + +/** + * Singly-linked Tail queue declarations. + */ +#define STAILQ_HEAD(name, type) struct name { \ + struct type *stqh_first; /* first element */ \ + struct type **stqh_last; /* addr of last next element */ \ +} + +#define STAILQ_HEAD_INITIALIZER(head) { \ + NULL, &(head).stqh_first \ +} + +#define STAILQ_ENTRY(type) struct { \ + struct type *stqe_next; /* next element */ \ +} + +/** + * Singly-linked Tail queue functions. + */ +#define STAILQ_CONCAT(head1, head2) do { \ + if (!STAILQ_EMPTY((head2))) { \ + *(head1)->stqh_last = (head2)->stqh_first; \ + (head1)->stqh_last = (head2)->stqh_last; \ + STAILQ_INIT((head2)); \ + } \ +} while (0) + +#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL) + +#define STAILQ_FIRST(head) ((head)->stqh_first) + +#define STAILQ_FOREACH(var, head, field) \ + for ((var) = STAILQ_FIRST((head)); \ + (var); \ + (var) = STAILQ_NEXT((var), field)) + +#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = STAILQ_FIRST((head)); \ + (var) && ((tvar) = STAILQ_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define STAILQ_INIT(head) do { \ + STAILQ_FIRST((head)) = NULL; \ + (head)->stqh_last = &STAILQ_FIRST((head)); \ +} while (0) + +#define STAILQ_INSERT_AFTER(head, tqelm, elm, field) do { \ + if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL) \ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ + STAILQ_NEXT((tqelm), field) = (elm); \ +} while (0) + +#define STAILQ_INSERT_HEAD(head, elm, field) do { \ + if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ + STAILQ_FIRST((head)) = (elm); \ +} while (0) + +#define STAILQ_INSERT_TAIL(head, elm, field) do { \ + STAILQ_NEXT((elm), field) = NULL; \ + *(head)->stqh_last = (elm); \ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ +} while (0) + +#define STAILQ_LAST(head, type, field) \ + (STAILQ_EMPTY((head)) ? NULL : __containerof((head)->stqh_last, struct type, field.stqe_next)) + +#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next) + +#define STAILQ_REMOVE(head, elm, type, field) do { \ + QMD_SAVELINK(oldnext, (elm)->field.stqe_next); \ + if (STAILQ_FIRST((head)) == (elm)) { \ + STAILQ_REMOVE_HEAD((head), field); \ + } else { \ + struct type *curelm = STAILQ_FIRST((head)); \ + while (STAILQ_NEXT(curelm, field) != (elm)) \ + curelm = STAILQ_NEXT(curelm, field); \ + STAILQ_REMOVE_AFTER(head, curelm, field); \ + } \ + TRASHIT(*oldnext); \ +} while (0) + +#define STAILQ_REMOVE_AFTER(head, elm, field) do { \ + if ((STAILQ_NEXT(elm, field) = STAILQ_NEXT(STAILQ_NEXT(elm, field), field)) == NULL) \ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ +} while (0) + +#define STAILQ_REMOVE_HEAD(head, field) do { \ + if ((STAILQ_FIRST((head)) = STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL) \ + (head)->stqh_last = &STAILQ_FIRST((head)); \ +} while (0) + +#define STAILQ_REMOVE_HEAD_UNTIL(head, elm, field) do { \ + if ((STAILQ_FIRST((head)) = STAILQ_NEXT((elm), field)) == NULL) \ + (head)->stqh_last = &STAILQ_FIRST((head)); \ +} while (0) + +#define STAILQ_SWAP(head1, head2, type) do { \ + struct type *swap_first = STAILQ_FIRST(head1); \ + struct type **swap_last = (head1)->stqh_last; \ + STAILQ_FIRST(head1) = STAILQ_FIRST(head2); \ + (head1)->stqh_last = (head2)->stqh_last; \ + STAILQ_FIRST(head2) = swap_first; \ + (head2)->stqh_last = swap_last; \ + if (STAILQ_EMPTY(head1)) \ + (head1)->stqh_last = &STAILQ_FIRST(head1); \ + if (STAILQ_EMPTY(head2)) \ + (head2)->stqh_last = &STAILQ_FIRST(head2); \ +} while (0) diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/include/esp_gmf_single_node.h b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/include/esp_gmf_single_node.h new file mode 100644 index 0000000..bcbb691 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/include/esp_gmf_single_node.h @@ -0,0 +1,112 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once +#include "esp_gmf_oal_mem.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * @brief Single node structure for a singly linked list + */ +typedef struct esp_gmf_single_node { + struct esp_gmf_single_node *next; /*!< Pointer to the next node in the list */ +} esp_gmf_single_node_t; + +/** + * @brief Function pointer type for freeing a node, with context passed as a parameter + */ +typedef void (*single_node_free)(void *ctx); + +/** + * @brief Append a node to the end of a singly linked list + * + * @param[in,out] head Pointer to the head of the linked list + * @param[in] node Node to append to the list + */ +static inline void esp_gmf_single_node_append(esp_gmf_single_node_t **head, esp_gmf_single_node_t *node) +{ + if (*head == NULL) { + *head = node; + } else { + esp_gmf_single_node_t *temp = *head; + while (temp->next) { + temp = temp->next; + } + temp->next = node; + } +} + +/** + * @brief Remove a specified node from the singly linked list + * If the node is not found, the function does nothing + * + * @param[in,out] head Pointer to the head of the linked list + * @param[in] del Node to remove from the list + * + */ +static inline void esp_gmf_single_node_remove(esp_gmf_single_node_t **head, esp_gmf_single_node_t *del) +{ + if (*head == NULL || del == NULL) { + return; + } + if (*head == del) { + *head = del->next; + esp_gmf_oal_free(del); + return; + } + esp_gmf_single_node_t *temp = *head; + while (temp != NULL && temp->next != del) { + temp = temp->next; + } + if (temp == NULL) { + return; + } + temp->next = del->next; + esp_gmf_oal_free(del); +} + +/** + * @brief Get the count of nodes in the singly linked list + * + * @param[in] head Pointer to the head of the linked list + * @return + * - > 0 Number of nodes in the list + */ +static inline int esp_gmf_single_node_get_count(esp_gmf_single_node_t *head) +{ + int count = 0; + esp_gmf_single_node_t *current = head; + while (current != NULL) { + count++; + current = current->next; + } + return count; +} + +/** + * @brief Destroy the entire singly linked list, freeing each node using the provided callback function + * + * @param[in] head Pointer to the head of the linked list + * @param[in] free_cb Callback function to free each node in the list + * + */ +static inline void esp_gmf_single_node_destroy(esp_gmf_single_node_t *head, single_node_free free_cb) +{ + esp_gmf_single_node_t *temp = head; + while (temp) { + head = temp->next; + free_cb(temp); + temp = head; + } +} + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/include/esp_gmf_task.h b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/include/esp_gmf_task.h new file mode 100644 index 0000000..51547d5 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/include/esp_gmf_task.h @@ -0,0 +1,254 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#include "stdbool.h" +#include "esp_gmf_err.h" +#include "esp_gmf_event.h" +#include "esp_gmf_job.h" +#include "esp_gmf_obj.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define ESP_GMF_MAX_DELAY (0xFFFFFFFFUL) + +/** + * @brief GMF Task Handle + */ +typedef void *esp_gmf_task_handle_t; + +/** + * @brief GMF task configuration + * + * Configuration structure for GMF tasks, specifying parameters such as stack size, + * priority, CPU core affinity, and whether the stack is allocated in external memory. + */ +typedef struct esp_gmf_task_config { + int stack; /*!< Size of the task stack */ + int prio; /*!< Priority of the task */ + uint32_t core : 4; /*!< CPU core affinity for the task */ + uint32_t stack_in_ext : 4; /*!< Flag indicating if the stack is in external memory */ +} esp_gmf_task_config_t; + +/** + * @brief GMF task structure + * + * Represents a GMF task, including its properties, configuration, and internal state. + */ +typedef struct _esp_gmf_task { + struct esp_gmf_obj_ base; /*!< Base object for GMF tasks */ + esp_gmf_job_t *working; /*!< Currently executing job in the task */ + esp_gmf_job_stack_t *start_stack; /*!< Stack for the start job */ + + /* Properties */ + esp_gmf_event_cb event_func; /*!< Callback function for task events */ + esp_gmf_event_state_t state; /*!< Current state of the task */ + + /* Protect */ + esp_gmf_task_config_t thread; /*!< Configuration settings for the task */ + void *ctx; /*!< Context associated with the task */ + + /* Private */ + void *oal_thread; /*!< Handle to the thread */ + void *lock; /*!< Mutex lock for task synchronization */ + void *event_group; /*!< Event group for wait events */ + void *block_sem; /*!< Semaphore for blocking tasks */ + void *wait_sem; /*!< Semaphore for task waiting */ + int api_sync_time; /*!< Timeout for synchronization */ + + uint8_t _task_run : 1; /*!< Internal flag for task execution */ + uint8_t _running : 1; /*!< Internal flag for task running state */ + uint8_t _run : 1; /*!< Internal flag for task run API flag */ + uint8_t _pause : 1; /*!< Internal flag for task pause API flag */ + uint8_t _stop : 1; /*!< Internal flag for task stop API flag */ + uint8_t _destroy : 1; /*!< Internal flag for task destruction API flag */ +} esp_gmf_task_t; + +/** + * @brief GMF Task configuration + * + * Configuration structure for GMF tasks, specifying parameters such as thread configuration, + * task name, user context, and callback function. + */ +typedef struct { + esp_gmf_task_config_t thread; /*!< Configuration settings for the task thread */ + const char *name; /*!< Name of the task */ + void *ctx; /*!< User context */ + esp_gmf_event_cb cb; /*!< Callback function for task events */ +} esp_gmf_task_cfg_t; + +#define DEFAULT_ESP_GMF_STACK_SIZE (4 * 1024) +#define DEFAULT_ESP_GMF_TASK_PRIO (5) +#define DEFAULT_ESP_GMF_TASK_CORE (0) + +#define DEFAULT_ESP_GMF_TASK_CONFIG() { \ + .thread = { \ + .stack = DEFAULT_ESP_GMF_STACK_SIZE, \ + .prio = DEFAULT_ESP_GMF_TASK_PRIO, \ + .core = DEFAULT_ESP_GMF_TASK_CORE, \ + .stack_in_ext = false, \ + }, \ + .name = NULL, \ + .ctx = NULL, \ + .cb = NULL, \ +} + +/** + * @brief Initialize a GMF task + * + * @param[in] config Configuration for the GMF task + * @param[out] tsk_hd Pointer to store the GMF task handle after initialization + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG If the configuration or handle is invalid + * - ESP_GMF_ERR_MEMORY_LACK If there is insufficient memory to perform the initialization + * - Others Indicating failure + */ +esp_gmf_err_t esp_gmf_task_init(esp_gmf_task_cfg_t *config, esp_gmf_task_handle_t *tsk_hd); + +/** + * @brief Deinitialize a GMF task, freeing associated resources + * + * @param[in] handle GMF task handle to deinitialize + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG If the configuration or handle is invalid + */ +esp_gmf_err_t esp_gmf_task_deinit(esp_gmf_task_handle_t handle); + +/** + * @brief Register a ready job to the specific GMF task + * + * @param[in] handle GMF task handle + * @param[in] label Label for the job + * @param[in] job Job function to register + * @param[in] times Job execution times configuration + * @param[in] ctx Context to be passed to the job function + * @param[in] done Flag indicating whether the job is done + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Indicating the handle is invalid + * - ESP_GMF_ERR_MEMORY_LACK Insufficient memory to perform the registration + */ +esp_gmf_err_t esp_gmf_task_register_ready_job(esp_gmf_task_handle_t handle, const char *label, esp_gmf_job_func job, esp_gmf_job_times_t times, void *ctx, bool done); + +/** + * @brief Set the event callback function for a GMF task + * + * @param[in] handle GMF task handle + * @param[in] cb Event callback function + * @param[in] ctx Context to be passed to the callback function + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Indicating the handle is invalid + */ +esp_gmf_err_t esp_gmf_task_set_event_func(esp_gmf_task_handle_t handle, esp_gmf_event_cb cb, void *ctx); + +/** + * @brief Run the specific GMF task + * This function may block for either the DEFAULT_TASK_OPT_MAX_TIME_MS or the time set by the set esp_gmf_task_set_timeout + * + * @param[in] handle GMF task handle + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Indicating the handle is invalid + * - ESP_GMF_ERR_NOT_SUPPORT Indicating the state of task is ESP_GMF_EVENT_STATE_PAUSED or ESP_GMF_EVENT_STATE_RUNNING + * - ESP_GMF_ERR_INVALID_STATE The task is not running + * - ESP_GMF_ERR_TIMEOUT Indicating that the synchronization operation has timed out + */ +esp_gmf_err_t esp_gmf_task_run(esp_gmf_task_handle_t handle); + +/** + * @brief Stop a running or paused GMF task + * This function may block for either the DEFAULT_TASK_OPT_MAX_TIME_MS or the time set by the set esp_gmf_task_set_timeout + * + * + * @param[in] handle GMF task handle + * + * @return + * - ESP_GMF_ERR_OK On success or the task already stopped + * - ESP_GMF_ERR_INVALID_ARG Indicating the handle is invalid + * - ESP_GMF_ERR_NOT_SUPPORT The state of task is ESP_GMF_EVENT_STATE_NONE + * - ESP_GMF_ERR_INVALID_STATE The task is not running + * - ESP_GMF_ERR_TIMEOUT Indicating that the synchronization operation has timed out + */ +esp_gmf_err_t esp_gmf_task_stop(esp_gmf_task_handle_t handle); + +/** + * @brief Pause a running GMF task + * This function may block for either the DEFAULT_TASK_OPT_MAX_TIME_MS or the time set by the set esp_gmf_task_set_timeout + * + * @param[in] handle GMF task handle + * + * @return + * - ESP_GMF_ERR_OK On success or the task already paused + * - ESP_GMF_ERR_INVALID_ARG Indicating the handle is invalid + * - ESP_GMF_ERR_NOT_SUPPORT The state of task is not ESP_GMF_EVENT_STATE_RUNNING + * - ESP_GMF_ERR_TIMEOUT Indicating that the synchronization operation has timed out + */ +esp_gmf_err_t esp_gmf_task_pause(esp_gmf_task_handle_t handle); + +/** + * @brief Resume a paused GMF task only + * This function may block for either the DEFAULT_TASK_OPT_MAX_TIME_MS or the time set by the set esp_gmf_task_set_timeout + * + * @param[in] handle GMF task handle + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Indicating the handle is invalid + * - ESP_GMF_ERR_NOT_SUPPORT The state of task is not ESP_GMF_EVENT_STATE_PAUSED + * - ESP_GMF_ERR_TIMEOUT Indicating that the synchronization operation has timed out + */ +esp_gmf_err_t esp_gmf_task_resume(esp_gmf_task_handle_t handle); + +/** + * @brief Reset a GMF task to its initial state + * + * @param[in] handle GMF task handle + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Indicating the handle is invalid + */ +esp_gmf_err_t esp_gmf_task_reset(esp_gmf_task_handle_t handle); + +/** + * @brief Set the synchronization timeout for run, stop, pause, and resume operations + * + * @param[in] handle GMF task handle + * @param[in] wait_ms Timeout duration in milliseconds + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Indicating the handle is invalid + */ +esp_gmf_err_t esp_gmf_task_set_timeout(esp_gmf_task_handle_t handle, int wait_ms); + +/** + * @brief Get the state of the specific task + * + * @param[in] handle GMF task handle + * @param[out] state Pointer to store the GMF event state + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Indicating the handle is invalid + */ +esp_gmf_err_t esp_gmf_task_get_state(esp_gmf_task_handle_t handle, esp_gmf_event_state_t *state); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/include/esp_gmf_video_element.h b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/include/esp_gmf_video_element.h new file mode 100644 index 0000000..e228205 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/include/esp_gmf_video_element.h @@ -0,0 +1,79 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#include "esp_gmf_element.h" +#include "esp_gmf_info.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * @brief GMF video element structure + */ +typedef struct _esp_gmf_video_element { + struct esp_gmf_element base; /*!< Base element structure */ + esp_gmf_info_video_t src_info; /*!< Video input information */ + void *lock; /*!< Lock for thread safety */ +} esp_gmf_video_element_t; + +/** GMF video element handle */ +typedef void *esp_gmf_video_element_handle_t; + +/** + * @brief Initialize a GMF video element with the given configuration + * + * @param[in] handle GMF video element handle to initialize + * @param[in] config Pointer to the configuration structure + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + * - ESP_GMF_ERR_MEMORY_LACK Memory allocate failed + */ +esp_gmf_err_t esp_gmf_video_el_init(esp_gmf_video_element_handle_t handle, esp_gmf_element_cfg_t *config); + +/** + * @brief Get video source information from a GMF video element + * + * @param[in] handle GMF video element handle + * @param[out] info Pointer to store the video source information + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_video_el_get_src_info(esp_gmf_video_element_handle_t handle, esp_gmf_info_video_t *info); + +/** + * @brief Set video source information from a GMF video element + * + * @param[in] handle GMF video element handle + * @param[out] info Pointer to the video source information to set + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_video_el_set_src_info(esp_gmf_video_element_handle_t handle, esp_gmf_info_video_t *info); + +/** + * @brief Deinitialize a GMF video element, freeing associated resources + * + * @param[in] handle GMF video element handle to deinitialize + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_INVALID_ARG Invalid argument + */ +esp_gmf_err_t esp_gmf_video_el_deinit(esp_gmf_video_element_handle_t handle); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/oal/esp_gmf_oal_mem.c b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/oal/esp_gmf_oal_mem.c new file mode 100644 index 0000000..e8bbb63 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/oal/esp_gmf_oal_mem.c @@ -0,0 +1,179 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#include +#include "string.h" +#include "sdkconfig.h" +#include "esp_log.h" +#include "esp_gmf_oal_mem.h" +#include "esp_heap_caps.h" +#include "hal/efuse_hal.h" +#include "esp_private/esp_cache_private.h" + +// #define ENABLE_AUDIO_MEM_TRACE +#define MALLOC_RAM_FLAG 1 + + +#ifdef ENABLE_AUDIO_MEM_TRACE +int __attribute__((weak)) media_lib_add_trace_mem(const char *module, void *addr, int size, uint8_t flag) +{ + return 0; +} + +void __attribute__((weak)) media_lib_remove_trace_mem(void *addr) +{ +} +#endif /* ENABLE_AUDIO_MEM_TRACE */ + +void *esp_gmf_oal_malloc(size_t size) +{ + void *data = NULL; +#if CONFIG_SPIRAM_BOOT_INIT + data = heap_caps_malloc(size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); +#else + data = malloc(size); +#endif /* CONFIG_SPIRAM_BOOT_INIT */ + // ESP_LOGI("ESP_GMF_MEM", "malloc:%p, size:%d, called:0x%08x", data, size, (intptr_t)__builtin_return_address(0) - 2); +#ifdef ENABLE_AUDIO_MEM_TRACE + media_lib_add_trace_mem(NULL, data, size, 0); +#endif /* ENABLE_AUDIO_MEM_TRACE */ + return data; +} + +void *esp_gmf_oal_malloc_align(uint8_t align, size_t size) +{ + void *data = NULL; +#if CONFIG_SPIRAM_BOOT_INIT + data = heap_caps_aligned_alloc(align, size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); +#else + data = heap_caps_aligned_alloc(align, size, MALLOC_CAP_DEFAULT); +#endif /* CONFIG_SPIRAM_BOOT_INIT */ + // ESP_LOGI("ESP_GMF_MEM", "malloc:%p, size:%d, called:0x%08x", data, size, (intptr_t)__builtin_return_address(0) - 2); +#ifdef ENABLE_AUDIO_MEM_TRACE + media_lib_add_trace_mem(NULL, data, size, 0); +#endif /* ENABLE_AUDIO_MEM_TRACE */ + return data; +} + +void esp_gmf_oal_free(void *ptr) +{ +#ifdef ENABLE_AUDIO_MEM_TRACE + media_lib_remove_trace_mem(ptr); +#endif /* ENABLE_AUDIO_MEM_TRACE */ + // ESP_LOGI("ESP_GMF_MEM", "free:%p, called:0x%08x", ptr, (intptr_t)__builtin_return_address(0) - 2); + + free(ptr); +} + +void *esp_gmf_oal_calloc(size_t nmemb, size_t size) +{ + void *data = NULL; +#if CONFIG_SPIRAM_BOOT_INIT + data = heap_caps_malloc(nmemb * size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + if (data) { + memset(data, 0, nmemb * size); + } +#else + data = calloc(nmemb, size); +#endif /* CONFIG_SPIRAM_BOOT_INIT */ + // ESP_LOGI("ESP_GMF_MEM", "calloc:%p, size:%d, called:0x%08x", data, size, (intptr_t)__builtin_return_address(0) - 2); + +#ifdef ENABLE_AUDIO_MEM_TRACE + media_lib_add_trace_mem(NULL, data, nmemb * size, 0); +#endif /* ENABLE_AUDIO_MEM_TRACE */ + return data; +} + +void *esp_gmf_oal_realloc(void *ptr, size_t size) +{ + void *p = NULL; +#ifdef ENABLE_AUDIO_MEM_TRACE + media_lib_remove_trace_mem(ptr); +#endif /* ENABLE_AUDIO_MEM_TRACE */ + +#if CONFIG_SPIRAM_BOOT_INIT + p = heap_caps_realloc(ptr, size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); +#else + p = heap_caps_realloc(ptr, size, MALLOC_CAP_8BIT); +#endif /* CONFIG_SPIRAM_BOOT_INIT */ + // ESP_LOGI("ESP_GMF_MEM", "realloc:%p, size:%d, called:0x%08x", p, size, (intptr_t)__builtin_return_address(0) - 2); + +#ifdef ENABLE_AUDIO_MEM_TRACE + media_lib_add_trace_mem(NULL, p, size, 0); +#endif /* ENABLE_AUDIO_MEM_TRACE */ + return p; +} + +char *esp_gmf_oal_strdup(const char *str) +{ + int size = strlen(str) + 1; +#if CONFIG_SPIRAM_BOOT_INIT + char *copy = heap_caps_malloc(size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); +#else + char *copy = malloc(size); +#endif /* CONFIG_SPIRAM_BOOT_INIT */ + if (copy) { + strcpy(copy, str); +#ifdef ENABLE_AUDIO_MEM_TRACE + media_lib_add_trace_mem(NULL, copy, size, 0); +#endif /* ENABLE_AUDIO_MEM_TRACE */ + } + // ESP_LOGI("ESP_GMF_MEM", "strdup:%p, size:%d, called:0x%08x", copy, size, (intptr_t)__builtin_return_address(0) - 2); + + return copy; +} + +void *esp_gmf_oal_calloc_inner(size_t n, size_t size) +{ + void *data = NULL; +#if CONFIG_SPIRAM_BOOT_INIT + data = heap_caps_calloc_prefer(n, size, 2, MALLOC_CAP_DEFAULT | MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT, MALLOC_CAP_DEFAULT | MALLOC_CAP_SPIRAM); +#else + data = heap_caps_calloc(n, size, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); +#endif /* CONFIG_SPIRAM_BOOT_INIT */ +#ifdef ENABLE_AUDIO_MEM_TRACE + media_lib_add_trace_mem(NULL, data, n * size, MALLOC_RAM_FLAG); +#endif /* ENABLE_AUDIO_MEM_TRACE */ + // ESP_LOGI("ESP_GMF_MEM", "inner:%p, size:%d, called:0x%08x", data, size, (intptr_t)__builtin_return_address(0) - 2); + + return data; +} + +void esp_gmf_oal_mem_print(const char *tag, int line, const char *func) +{ +#ifdef CONFIG_SPIRAM_BOOT_INIT + ESP_LOGI(tag, "Func:%s, Line:%d, MEM Total:%d Bytes, Inter:%d Bytes, Dram:%d Bytes\r\n", func, line, (int)heap_caps_get_free_size(MALLOC_CAP_DEFAULT), + (int)heap_caps_get_free_size(MALLOC_CAP_INTERNAL), (int)heap_caps_get_free_size(MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)); +#else + ESP_LOGI(tag, "Func:%s, Line:%d, MEM Total:%d Bytes\r\n", func, line, (int)heap_caps_get_free_size(MALLOC_CAP_DEFAULT)); +#endif /* CONFIG_SPIRAM_BOOT_INIT */ +} + +bool esp_gmf_oal_mem_spiram_stack_is_enabled(void) +{ +#if defined(CONFIG_SPIRAM_BOOT_INIT) && (CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY) + bool ret = true; +#if defined(CONFIG_IDF_TARGET_ESP32) + uint32_t chip_ver = 0; + chip_ver = efuse_hal_chip_revision(); + if (chip_ver < 3) { + ESP_LOGW("ESP_GMF_MEM", "Can't support stack on external memory due to ESP32 chip is %d", (int)chip_ver); + ret = false; + } +#endif // defined(CONFIG_IDF_TARGET_ESP32) + return ret; +#else // defined(CONFIG_SPIRAM_BOOT_INIT) + return false; +#endif /* defined(CONFIG_SPIRAM_BOOT_INIT) && (CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY) */ +} + +uint8_t esp_gmf_oal_get_spiram_cache_align(void) +{ + size_t mem_alignment = 0; + esp_cache_get_alignment(MALLOC_CAP_SPIRAM, (size_t *)&mem_alignment); + return (uint8_t)mem_alignment; +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/oal/esp_gmf_oal_mutex.c b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/oal/esp_gmf_oal_mutex.c new file mode 100644 index 0000000..36658ef --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/oal/esp_gmf_oal_mutex.c @@ -0,0 +1,39 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" +#include "freertos/task.h" + +#include "esp_gmf_oal_mutex.h" +#include "esp_log.h" + +void *esp_gmf_oal_mutex_create(void) +{ + void *handle = NULL; + handle = xSemaphoreCreateMutex(); + return (void *)handle; +} + +int esp_gmf_oal_mutex_destroy(void *mutex) +{ + vSemaphoreDelete((QueueHandle_t)mutex); + return 0; +} + +int esp_gmf_oal_mutex_lock(void *mutex) +{ + while (xSemaphoreTake((QueueHandle_t)mutex, portMAX_DELAY) != pdPASS); + return 0; +} + +int esp_gmf_oal_mutex_unlock(void *mutex) +{ + int ret = 0; + ret = xSemaphoreGive((QueueHandle_t)mutex); + return ret; +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/oal/esp_gmf_oal_sys.c b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/oal/esp_gmf_oal_sys.c new file mode 100644 index 0000000..f2bf008 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/oal/esp_gmf_oal_sys.c @@ -0,0 +1,206 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_log.h" +#include "esp_gmf_oal_mem.h" +#include "esp_gmf_oal_sys.h" +#include "esp_memory_utils.h" + +static const char *TAG = "ESP_GMF_OAL_SYS"; + +#define ARRAY_SIZE_OFFSET 8 // Increase this if esp_gmf_oal_sys_get_real_time_stats returns ESP_GMF_ERR_NOT_ENOUGH + +#ifndef configRUN_TIME_COUNTER_TYPE +#define configRUN_TIME_COUNTER_TYPE uint32_t +#endif + +const char *task_state[] = { + "Running", + "Ready", + "Blocked", + "Suspended", + "Deleted", + "Invalid state" +}; + +/** @brief Task stack location + * - "Extr": Allocated task stack from psram + * - "Intr": Allocated task stack from internal + */ +const char *task_stack[] = {"Extr", "Intr"}; + +int esp_gmf_oal_sys_get_tick_by_time_ms(int ms) +{ + return (ms / portTICK_PERIOD_MS); +} + +int64_t esp_gmf_oal_sys_get_time_ms(void) +{ + struct timeval t; + gettimeofday(&t, NULL); + int64_t milliseconds = t.tv_sec * 1000LL + t.tv_usec / 1000; + return milliseconds; +} + +#if (CONFIG_FREERTOS_VTASKLIST_INCLUDE_COREID && CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS) +static TaskStatus_t *matched_status; + +static esp_gmf_err_t esp_gmf_get_cur_task_status(TaskStatus_t **status, UBaseType_t *num, configRUN_TIME_COUNTER_TYPE *cur_time) +{ + // Reserve some space for task created during get state running + *num = uxTaskGetNumberOfTasks() + ARRAY_SIZE_OFFSET; + TaskStatus_t *task_status = esp_gmf_oal_malloc(sizeof(TaskStatus_t) * (*num)); + if (task_status == NULL) { + return ESP_GMF_ERR_MEMORY_LACK; + } + *num = uxTaskGetSystemState(task_status, *num, cur_time); + if (*num == 0) { + ESP_LOGE(TAG, "Insufficient array size for uxTaskGetSystemState. Trying increasing ARRAY_SIZE_OFFSET"); + esp_gmf_oal_free(task_status); + return ESP_GMF_ERR_NOT_ENOUGH; + } + *status = task_status; + return ESP_GMF_ERR_OK; +} + +static int compare_tasks(const void *a, const void *b) +{ + const TaskStatus_t *task_a = &matched_status[*(uint8_t *)a]; + const TaskStatus_t *task_b = &matched_status[*(uint8_t *)b]; + + if (task_a->xCoreID != task_b->xCoreID) { + return task_a->xCoreID - task_b->xCoreID; + } + return (task_b->ulRunTimeCounter > task_a->ulRunTimeCounter) ? 1 : -1; +} + +esp_gmf_err_t esp_gmf_oal_sys_get_real_time_stats(int elapsed_time_ms, bool markdown) +{ + TaskStatus_t *start_array = NULL, *end_array = NULL; + uint8_t *matched_arr = NULL; + UBaseType_t start_array_size = 0, end_array_size = 0; + configRUN_TIME_COUNTER_TYPE start_run_time, end_run_time; + + esp_gmf_err_t ret = esp_gmf_get_cur_task_status(&start_array, &start_array_size, &start_run_time); + ESP_GMF_MEM_CHECK(TAG, start_array, goto exit); + + // Delay some time to get cpu usage + vTaskDelay(pdMS_TO_TICKS(elapsed_time_ms)); + + ret = esp_gmf_get_cur_task_status(&end_array, &end_array_size, &end_run_time); + ESP_GMF_MEM_CHECK(TAG, end_array, goto exit); + + // Match each task in start_array to those in the end_array + int min_count = start_array_size < end_array_size ? start_array_size : end_array_size; + matched_arr = malloc(min_count * sizeof(uint8_t)); + ESP_GMF_MEM_CHECK(TAG, matched_arr, goto exit); + + uint8_t matched_count = 0; + for (int i = 0; i < start_array_size; i++) { + for (int j = 0; j < end_array_size; j++) { + if (start_array[i].xHandle != end_array[j].xHandle) { + continue; + } + matched_arr[matched_count++] = j; + start_array[i].xHandle = NULL; + end_array[j].ulRunTimeCounter -= start_array[i].ulRunTimeCounter; + break; + } + } + + // Sort tasks by core ID and CPU usage + matched_status = end_array; + qsort(matched_arr, matched_count, sizeof(uint8_t), compare_tasks); + if (markdown) { + printf("| Task | Core ID | Run Time | CPU | Priority | Stack HWM | State | Stack |\n"); + printf("|-------------------|----------|-------------|---------|----------|-----------|------------|-------|\n"); + } else { + ESP_LOGI("", "┌───────────────────┬──────────┬─────────────┬─────────┬──────────┬───────────┬────────────┬───────┐"); + ESP_LOGI("", "│ Task │ Core ID │ Run Time │ CPU │ Priority │ Stack HWM │ State │ Stack │"); + } + BaseType_t current_core = -1; + // Calculate total_elapsed_time in units of run time stats clock period. + int total_elapsed_time = (int)(end_run_time - start_run_time) * portNUM_PROCESSORS; + for (int i = 0; i < matched_count; i++) { + TaskStatus_t *cur = &matched_status[matched_arr[i]]; + float percentage_time = ((float)cur->ulRunTimeCounter * 100UL) / total_elapsed_time; + + if (current_core != cur->xCoreID && markdown == false) { + current_core = cur->xCoreID; + ESP_LOGI("", "├───────────────────┼──────────┼─────────────┼─────────┼──────────┼───────────┼────────────┼───────┤"); + } + if (markdown) { + printf("| %-17s | %-8x | %-11d | %6.2f%% | %-8u | %-9u | %-10s | %-5s |\n", + cur->pcTaskName, + cur->xCoreID, + (int)cur->ulRunTimeCounter, + percentage_time, + cur->uxCurrentPriority, + (int)cur->usStackHighWaterMark, + task_state[(cur->eCurrentState)], + task_stack[esp_ptr_internal(pxTaskGetStackStart(cur->xHandle))]); + } else { + ESP_LOGI("", "│ %-17s │ %-8x │ %-11d │ %6.2f%% │ %-8u │ %-9u │ %-10s │ %-5s │", + cur->pcTaskName, + cur->xCoreID, + (int)cur->ulRunTimeCounter, + percentage_time, + cur->uxCurrentPriority, + (int)cur->usStackHighWaterMark, + task_state[(cur->eCurrentState)], + task_stack[esp_ptr_internal(pxTaskGetStackStart(cur->xHandle))]); + } + } + if (markdown == false) { + ESP_LOGI("", "└───────────────────┴──────────┴─────────────┴─────────┴──────────┴───────────┴────────────┴───────┘"); + } + if (matched_count != start_array_size) { + ESP_LOGI("", "Deleted Tasks:"); + for (int i = 0; i < start_array_size; i++) { + if (start_array[i].xHandle != NULL) { + ESP_LOGI(TAG, " %s", start_array[i].pcTaskName); + } + } + } + if (matched_count != end_array_size) { + ESP_LOGI("", "Created Tasks:"); + for (int i = 0; i < end_array_size; i++) { + if (end_array[i].xHandle != NULL) { + ESP_LOGI("", " %s", end_array[i].pcTaskName); + } + } + } + ret = ESP_GMF_ERR_OK; + +exit: // Common return path + if (matched_arr) { + esp_gmf_oal_free(matched_arr); + } + if (start_array) { + esp_gmf_oal_free(start_array); + start_array = NULL; + } + if (end_array) { + esp_gmf_oal_free(end_array); + end_array = NULL; + } + return ret; +} + +#else + +esp_gmf_err_t esp_gmf_oal_sys_get_real_time_stats(int elapsed_time_ms, bool markdown) +{ + ESP_LOGW(TAG, "Please enable `CONFIG_FREERTOS_VTASKLIST_INCLUDE_COREID` and `CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS` in menuconfig"); + return ESP_GMF_ERR_FAIL; +} + +#endif diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/oal/esp_gmf_oal_thread.c b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/oal/esp_gmf_oal_thread.c new file mode 100644 index 0000000..fbe8d2d --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/oal/esp_gmf_oal_thread.c @@ -0,0 +1,61 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" +#include "freertos/task.h" + +#include "esp_log.h" +#include "esp_gmf_oal_mem.h" +#include "esp_gmf_oal_thread.h" +#include "esp_memory_utils.h" + +static const char *TAG = "ESP_GMF_THREAD"; + +esp_gmf_err_t esp_gmf_oal_thread_create(esp_gmf_oal_thread_t *p_handle, const char *name, void (*main_func)(void *arg), void *arg, + uint32_t stack, int prio, bool stack_in_ext, int core_id) +{ + + TaskHandle_t task_handle = NULL; + + // When only support one core force no affinity + #if CONFIG_FREERTOS_UNICORE + core_id = tskNO_AFFINITY; +#endif + + BaseType_t ret; + if (stack_in_ext && esp_gmf_oal_mem_spiram_stack_is_enabled()) { + ret = xTaskCreatePinnedToCoreWithCaps(main_func, name, stack, arg, prio, &task_handle, + core_id, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + } else { + if (stack_in_ext) { + ESP_LOGW(TAG, "Make sure selected the `CONFIG_SPIRAM_BOOT_INIT` and `CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY` by `make menuconfig`"); + } + ret = xTaskCreatePinnedToCore(main_func, name, stack, arg, prio, &task_handle, core_id); + } + if (ret != pdPASS) { + ESP_LOGE(TAG, "Error create task %s in %s", name, stack_in_ext ? "PSRAM" : "RAM"); + return ESP_GMF_ERR_FAIL; + } + if (p_handle) { + *p_handle = task_handle; + } + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_oal_thread_delete(esp_gmf_oal_thread_t p_handle) +{ + TaskHandle_t task_handle = (TaskHandle_t) p_handle; + uint8_t *task_stack = pxTaskGetStackStart(task_handle); + ESP_GMF_NULL_CHECK(TAG, task_stack, return ESP_GMF_ERR_INVALID_ARG); + if (esp_ptr_internal(task_stack) == false) { + vTaskDeleteWithCaps(task_handle); + } else { + vTaskDelete(task_handle); + } + return ESP_GMF_ERR_OK; /* Control never reach here if this is self delete */ +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/oal/include/esp_gmf_oal_mem.h b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/oal/include/esp_gmf_oal_mem.h new file mode 100644 index 0000000..49cf62c --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/oal/include/esp_gmf_oal_mem.h @@ -0,0 +1,134 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * @brief Allocate memory of a specified size + * + * @param[in] size Size of memory to allocate, in bytes + * + * @return + * - Pointer to allocated memory on success + * - NULL if an error occurs + */ +void *esp_gmf_oal_malloc(size_t size); + +/** + * @brief Allocate memory with specified alignment + * + * @param[in] align Memory alignment in bytes (must be a power of 2 or 0) + * @param[in] size Size of memory to allocate, in bytes + * + * @return + * - Pointer to aligned memory on success + * - NULL if an error occurs + */ +void *esp_gmf_oal_malloc_align(uint8_t align, size_t size); + +/** + * @brief Free allocated memory + * + * @param[in] ptr Pointer to the memory to free + * + */ +void esp_gmf_oal_free(void *ptr); + +/** + * @brief Allocate zero-initialized memory, if SPI RAM is enabled, memory may be allocated there + * + * @param[in] nmemb Number of elements to allocate + * @param[in] size Size of each element, in bytes + * + * @return + * - Pointer to allocated and zero-initialized memory on success + * - NULL if an error occurs + */ +void *esp_gmf_oal_calloc(size_t nmemb, size_t size); + +/** + * @brief Allocate zero-initialized memory in internal memory + * + * @param[in] nmemb Number of elements to allocate + * @param[in] size Size of each element, in bytes + * + * @return + * - Pointer to allocated and zero-initialized memory on success + * - NULL if an error occurs + */ +void *esp_gmf_oal_calloc_inner(size_t nmemb, size_t size); + +/** + * @brief Print the current heap memory status + * + * @param[in] tag Log tag identifier + * @param[in] line Line number where the function is called + * @param[in] func Name of the function making the call + * + */ +void esp_gmf_oal_mem_print(const char *tag, int line, const char *func); + +/** + * @brief Reallocate memory to a new size, if SPI RAM is enabled, the memory may be allocated there + * + * @param[in] ptr Pointer to previously allocated memory + * @param[in] size New size of memory block, in bytes + * + * @return + * - Pointer to the reallocated memory block on success + * - NULL if an error occurs + */ +void *esp_gmf_oal_realloc(void *ptr, size_t size); + +/** + * @brief Duplicate a string in newly allocated memory + * + * @param[in] str String to duplicate + * + * @return + * - Pointer to the duplicated string in allocated memory + * - NULL if an error occurs + */ +char *esp_gmf_oal_strdup(const char *str); + +/** + * @brief Check if SPI RAM is enabled + * + * @return + * - true if SPI RAM is enabled + * - false if SPI RAM is not enabled + */ +bool esp_gmf_oal_mem_spiram_is_enabled(void); + +/** + * @brief Check if stack allocation on external SPI RAM is enabled + * + * @return + * - true if stack allocation on SPI RAM is enabled + * - false if stack allocation on SPI RAM is not enabled + */ +bool esp_gmf_oal_mem_spiram_stack_is_enabled(void); + +#define ESP_GMF_MEM_SHOW(x) esp_gmf_oal_mem_print(x, __LINE__, __func__) + +/** + * @brief Get SPI RAM cache alignment + * + * @return + * - Alignment SPI RAM cache alignment + */ +uint8_t esp_gmf_oal_get_spiram_cache_align(void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/oal/include/esp_gmf_oal_mutex.h b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/oal/include/esp_gmf_oal_mutex.h new file mode 100644 index 0000000..449820a --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/oal/include/esp_gmf_oal_mutex.h @@ -0,0 +1,62 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" +#include "freertos/task.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * @brief Allocates and initializes a new mutex object for synchronization + * + * @return + * - Pointer to the newly created mutex on success + * - NULL if the mutex creation fails + */ +void *esp_gmf_oal_mutex_create(void); + +/** + * @brief Destroy a mutex + * + * @param[in] mutex Pointer to the mutex to destroy + * + * @return + * - 0 on success + * - Negative value if an error occurs + */ +int esp_gmf_oal_mutex_destroy(void *mutex); + +/** + * @brief Acquires a lock on the specified mutex, blocking if necessary until the lock becomes available + * + * @param[in] mutex Pointer to the mutex to lock + * + * @return + * - 0 on success + * - Negative value if an error occurs + */ +int esp_gmf_oal_mutex_lock(void *mutex); + +/** + * @brief Releases the lock held on the specified mutex, allowing other threads to acquire the lock + * + * @param[in] mutex Pointer to the mutex to unlock + * + * @return + * - 0 on success + * - Negative value if an error occurs + */ +int esp_gmf_oal_mutex_unlock(void *mutex); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/oal/include/esp_gmf_oal_sys.h b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/oal/include/esp_gmf_oal_sys.h new file mode 100644 index 0000000..a96d72a --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/oal/include/esp_gmf_oal_sys.h @@ -0,0 +1,66 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#include "esp_gmf_err.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#if defined(__GNUC__) && (__GNUC__ >= 7) +#define FALL_THROUGH __attribute__((fallthrough)) +#else +#define FALL_THROUGH ((void)0) +#endif /* defined(__GNUC__) && (__GNUC__ >= 7) */ + +/** + * @brief Get system ticks based on the given millisecond value + * + * @param[in] ms Time in milliseconds + * + * @return + * - The corresponding system ticks + */ +int esp_gmf_oal_sys_get_tick_by_time_ms(int ms); + +/** + * @brief Retrieve the current system time in milliseconds + * + * @return + * - The system time in milliseconds + */ +int64_t esp_gmf_oal_sys_get_time_ms(void); + +/** + * @brief Print CPU usage statistics of tasks over a specified time period + * + * This function measures and prints the CPU usage of tasks over a given + * period (in milliseconds) by calling `uxTaskGetSystemState()` twice, + * with a delay in between. The CPU usage is then calculated based on the + * difference in task run times before and after the delay. + * + * @note + * - If tasks are added or removed during the delay, their statistics will not be included in the final report + * - To minimize inaccuracies caused by delays, this function should be called from a high-priority task + * - In dual-core mode, each core will account for up to 50% of the total runtime + * + * @param[in] elapsed_time_ms Time period for the CPU usage measurement in milliseconds + * @param[in] markdown Whether print use markdown format + * + * @return + * - ESP_GMF_ERR_OK On success + * - ESP_GMF_ERR_MEMORY_LACK No memory for operation + * - ESP_GMF_ERR_NOT_ENOUGH More memory is needed for uxTaskGetSystemState + * - ESP_GMF_ERR_FAIL On failure + */ +esp_gmf_err_t esp_gmf_oal_sys_get_real_time_stats(int elapsed_time_ms, bool markdown); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/oal/include/esp_gmf_oal_thread.h b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/oal/include/esp_gmf_oal_thread.h new file mode 100644 index 0000000..dc3da7a --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/oal/include/esp_gmf_oal_thread.h @@ -0,0 +1,55 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#pragma once + +#include "esp_gmf_err.h" +#include "stdbool.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +typedef void *esp_gmf_oal_thread_t; + +/** + * @brief This function creates a new thread, specifying its properties like name, priority, stack size, and the core to which it should be pinned + * + * @note + * - Please apply the ./idf_patches/idf_v3.3/4.x_freertos.patch first + * - Please enable support for external RAM and `Allow external memory as an argument to xTaskCreateStatic` + * to be able to use external memory for task stack, namely `CONFIG_SPIRAM_BOOT_INIT=y` and `CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY=y` + * + * @param[out] p_handle Pointer to the thread handle that will be created + * @param[in] name The name of the thread + * @param[in] main_func The main function that the thread will execute + * @param[in] arg Argument to pass to the thread's main function + * @param[in] stack The size of the stack (in bytes) to allocate for the thread + * @param[in] prio The priority of the thread + * @param[in] stack_in_ext If true, the stack will be allocated in external memory + * @param[in] core_id The core to pin the thread to (-1 for no pinning) + * + * @return + * - ESP_GMF_ERR_OK Operation successful + * - ESP_GMF_ERR_FAIL Operation failed, usually due to insufficient memory for creating a new thread + */ +esp_gmf_err_t esp_gmf_oal_thread_create(esp_gmf_oal_thread_t *p_handle, const char *name, void (*main_func)(void *arg), void *arg, + uint32_t stack, int prio, bool stack_in_ext, int core_id); + +/** + * @brief Delete an existing GMF OAL thread + * + * @param[in] p_handle Pointer to the thread handle to be deleted + * + * @return + * - ESP_GMR_ERR_OK Operation successful + */ +esp_gmf_err_t esp_gmf_oal_thread_delete(esp_gmf_oal_thread_t p_handle); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/src/esp_gmf_audio_element.c b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/src/esp_gmf_audio_element.c new file mode 100644 index 0000000..359139e --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/src/esp_gmf_audio_element.c @@ -0,0 +1,107 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#include +#include "esp_gmf_audio_element.h" +#include "esp_gmf_oal_mem.h" +#include "esp_gmf_oal_mutex.h" +#include "esp_gmf_info_file.h" +#include "esp_log.h" + +static const char *TAG = "ESP_GMF_AUD_ELEMENT"; + +esp_gmf_err_t esp_gmf_audio_el_init(esp_gmf_audio_element_handle_t handle, esp_gmf_element_cfg_t *config) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, config, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_audio_element_t *aud = (esp_gmf_audio_element_t *)handle; + config->ctx = (void *)aud; + esp_gmf_element_init(&aud->base, config); + esp_gmf_info_file_init(&aud->file_info); + aud->lock = esp_gmf_oal_mutex_create(); + ESP_GMF_MEM_CHECK(TAG, aud->lock, { + esp_gmf_element_deinit(&aud->base); + esp_gmf_oal_free(aud); + return ESP_GMF_ERR_MEMORY_LACK; + }); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_audio_el_get_snd_info(esp_gmf_audio_element_handle_t handle, esp_gmf_info_sound_t *info) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, info, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_audio_element_t *aud = (esp_gmf_audio_element_t *)handle; + esp_gmf_oal_mutex_lock(aud->lock); + memcpy(info, &aud->snd_info, sizeof(aud->snd_info)); + esp_gmf_oal_mutex_unlock(aud->lock); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_audio_el_set_snd_info(esp_gmf_audio_element_handle_t handle, esp_gmf_info_sound_t *info) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, info, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_audio_element_t *aud = (esp_gmf_audio_element_t *)handle; + esp_gmf_oal_mutex_lock(aud->lock); + memcpy(&aud->snd_info, info, sizeof(aud->snd_info)); + esp_gmf_oal_mutex_unlock(aud->lock); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_audio_el_get_file_info(esp_gmf_audio_element_handle_t handle, esp_gmf_info_file_t *info) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, info, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_audio_element_t *aud = (esp_gmf_audio_element_t *)handle; + esp_gmf_oal_mutex_lock(aud->lock); + memcpy(info, &aud->file_info, sizeof(aud->file_info)); + esp_gmf_oal_mutex_unlock(aud->lock); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_audio_el_set_file_info(esp_gmf_audio_element_handle_t handle, esp_gmf_info_file_t *info) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, info, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_audio_element_t *aud = (esp_gmf_audio_element_t *)handle; + esp_gmf_oal_mutex_lock(aud->lock); + memcpy(&aud->file_info, info, sizeof(aud->file_info)); + esp_gmf_oal_mutex_unlock(aud->lock); + return ESP_GMF_ERR_OK; +} +esp_gmf_err_t esp_gmf_audio_el_set_file_size(esp_gmf_audio_element_handle_t handle, uint64_t size) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_audio_element_t *aud = (esp_gmf_audio_element_t *)handle; + esp_gmf_oal_mutex_lock(aud->lock); + int ret = esp_gmf_info_file_set_size(&aud->file_info, size); + esp_gmf_oal_mutex_unlock(aud->lock); + return ret; +} + +esp_gmf_err_t esp_gmf_audio_el_update_file_pos(esp_gmf_audio_element_handle_t handle, uint64_t pos) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_audio_element_t *aud = (esp_gmf_audio_element_t *)handle; + esp_gmf_oal_mutex_lock(aud->lock); + int ret = esp_gmf_info_file_update_pos(&aud->file_info, pos); + esp_gmf_oal_mutex_unlock(aud->lock); + return ret; +} + +esp_gmf_err_t esp_gmf_audio_el_deinit(esp_gmf_audio_element_handle_t handle) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_audio_element_t *aud = (esp_gmf_audio_element_t *)handle; + esp_gmf_oal_mutex_lock(aud->lock); + esp_gmf_element_deinit(&aud->base); + esp_gmf_info_file_deinit(&aud->file_info); + esp_gmf_oal_mutex_unlock(aud->lock); + esp_gmf_oal_mutex_destroy(aud->lock); + return ESP_GMF_ERR_OK; +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/src/esp_gmf_cap.c b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/src/esp_gmf_cap.c new file mode 100644 index 0000000..92daf4e --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/src/esp_gmf_cap.c @@ -0,0 +1,219 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#include +#include "esp_log.h" +#include "esp_gmf_cap.h" +#include "esp_gmf_single_node.h" +#include "esp_gmf_oal_mem.h" +#include "esp_fourcc.h" +#include "esp_gmf_caps_def.h" + +static const char *TAG = "ESP_GMF_CAPS"; + +static void esp_gmf_cap_free(void *handle) +{ + esp_gmf_oal_free(handle); +} + +esp_gmf_err_t esp_gmf_cap_append(esp_gmf_cap_t **caps, esp_gmf_cap_t *cap_value) +{ + ESP_GMF_NULL_CHECK(TAG, caps, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, cap_value, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_cap_t *node = (esp_gmf_cap_t *)esp_gmf_oal_calloc(1, sizeof(esp_gmf_cap_t)); + ESP_GMF_MEM_CHECK(TAG, node, return ESP_GMF_ERR_MEMORY_LACK); + node->cap_eightcc = cap_value->cap_eightcc; + node->attr_fun = cap_value->attr_fun; + esp_gmf_single_node_append((esp_gmf_single_node_t **)caps, (esp_gmf_single_node_t *)node); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_cap_destroy(esp_gmf_cap_t *caps) +{ + ESP_GMF_NULL_CHECK(TAG, caps, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_single_node_destroy((esp_gmf_single_node_t *)caps, esp_gmf_cap_free); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_cap_iterate_attr(esp_gmf_cap_t *caps, uint32_t attr_index, esp_gmf_cap_attr_t *out_attr) +{ + ESP_GMF_NULL_CHECK(TAG, caps, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, out_attr, return ESP_GMF_ERR_INVALID_ARG); + if (caps->attr_fun) { + return caps->attr_fun(attr_index, out_attr); + } + return ESP_GMF_ERR_NOT_SUPPORT; +} + +esp_gmf_err_t esp_gmf_cap_fetch_node(esp_gmf_cap_t *caps, uint64_t eight_cc, esp_gmf_cap_t **out_caps) +{ + ESP_GMF_NULL_CHECK(TAG, caps, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, out_caps, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_cap_t *current = caps; + *out_caps = NULL; + while (current != NULL) { + if (current->cap_eightcc == eight_cc) { + *out_caps = current; + return ESP_GMF_ERR_OK; + } + current = current->next; + } + return ESP_GMF_ERR_NOT_FOUND; +} + +esp_gmf_err_t esp_gmf_cap_find_attr(esp_gmf_cap_t *caps, uint32_t cc, esp_gmf_cap_attr_t *out_attr) +{ + ESP_GMF_NULL_CHECK(TAG, caps, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, out_attr, return ESP_GMF_ERR_INVALID_ARG); + if (caps->attr_fun == NULL) { + ESP_LOGW(TAG, "There is no attribute iteration function, %p", caps); + return ESP_GMF_ERR_NOT_SUPPORT; + } + esp_gmf_cap_attr_t tmp = {0}; + uint32_t idx = 0; + int ret = ESP_GMF_ERR_FAIL; + do { + ret = esp_gmf_cap_iterate_attr(caps, idx++, &tmp); + if (ret == ESP_GMF_ERR_OK) { + ESP_LOGD(TAG, "Find dest:%s, src:%s", ESP_FOURCC_TO_STR(cc), ESP_FOURCC_TO_STR(tmp.fourcc)); + if (tmp.fourcc == cc) { + memcpy(out_attr, &tmp, sizeof(*out_attr)); + return ESP_GMF_ERR_OK; + } + } + } while (ret == ESP_GMF_ERR_OK); + return ESP_GMF_ERR_NOT_FOUND; +} + +esp_gmf_err_t esp_gmf_cap_attr_check_value(esp_gmf_cap_attr_t *attr, uint32_t val, bool *is_support) +{ + ESP_GMF_NULL_CHECK(TAG, attr, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, is_support, return ESP_GMF_ERR_INVALID_ARG); + switch (attr->prop_type) { + case ESP_GMF_PROP_TYPE_DISCRETE: + *is_support = false; + for (int i = 0; i < attr->value.d.item_num; ++i) { + void *p = attr->value.d.collection; + if (attr->value.d.item_size == 1) { + if (val == ((uint8_t *)p)[i]) { + *is_support = true; + return ESP_GMF_ERR_OK; + } + } else if (attr->value.d.item_size == 2) { + if (val == ((uint16_t *)p)[i]) { + *is_support = true; + return ESP_GMF_ERR_OK; + } + } else if (attr->value.d.item_size == 4) { + if (val == ((uint32_t *)p)[i]) { + *is_support = true; + return ESP_GMF_ERR_OK; + } + } else { + ESP_LOGE(TAG, "Value check failed: invalid item size, %d", attr->value.d.item_size); + return ESP_GMF_ERR_NOT_SUPPORT; + } + } + return ESP_GMF_ERR_NOT_SUPPORT; + case ESP_GMF_PROP_TYPE_STEPWISE: + ESP_LOGD(TAG, "Check value:%s, type:%d", ESP_FOURCC_TO_STR(attr->fourcc), attr->prop_type); + if (((attr->value.s.min > val) || (attr->value.s.max < val)) || ((val - attr->value.s.min) % attr->value.s.step)) { + *is_support = false; + return ESP_GMF_ERR_NOT_SUPPORT; + } + *is_support = true; + return ESP_GMF_ERR_OK; + case ESP_GMF_PROP_TYPE_MULTIPLE: + ESP_LOGD(TAG, "Check MUL:%s, min:%ld, fac:%d, max:%ld", ESP_FOURCC_TO_STR(attr->fourcc), attr->value.m.min, attr->value.m.factor, attr->value.m.max); + if (((attr->value.m.min > val) || (attr->value.m.max < val)) || (val % attr->value.m.factor)) { + *is_support = false; + return ESP_GMF_ERR_NOT_SUPPORT; + } + *is_support = true; + return ESP_GMF_ERR_OK; + case ESP_GMF_PROP_TYPE_CONSTANT: + ESP_LOGD(TAG, "Check MUL:%s, min:%ld, fac:%d, max:%ld", ESP_FOURCC_TO_STR(attr->fourcc), attr->value.m.min, attr->value.m.factor, attr->value.m.max); + if (val != attr->value.c.data) { + *is_support = false; + return ESP_GMF_ERR_NOT_SUPPORT; + } + *is_support = true; + return ESP_GMF_ERR_OK; + } + return ESP_GMF_ERR_NOT_SUPPORT; +} + +esp_gmf_err_t esp_gmf_cap_attr_iterator_value(esp_gmf_cap_attr_t *attr, uint32_t *val, bool *is_last) +{ + ESP_GMF_NULL_CHECK(TAG, attr, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, val, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, is_last, return ESP_GMF_ERR_INVALID_ARG); + *is_last = false; + *val = 0; + switch (attr->prop_type) { + case ESP_GMF_PROP_TYPE_DISCRETE: + ESP_LOGD(TAG, "Iterate value DIS, sz:%d, num:%d", attr->value.d.item_size, attr->value.d.item_num); + if (attr->value.d.item_size == 1) { + *val = ((uint8_t *)attr->value.d.collection)[attr->index++]; + } else if (attr->value.d.item_size == 2) { + *val = ((uint16_t *)attr->value.d.collection)[attr->index++]; + } else if (attr->value.d.item_size == 4) { + *val = ((uint32_t *)attr->value.d.collection)[attr->index++]; + } else { + ESP_LOGE(TAG, "Iterate value failed: invalid item size, %d", attr->value.d.item_size); + return ESP_GMF_ERR_NOT_SUPPORT; + } + if (attr->index == attr->value.d.item_num) { + *is_last = true; + attr->index = 0; + } + return ESP_GMF_ERR_OK; + case ESP_GMF_PROP_TYPE_STEPWISE: + *val = attr->value.s.min + attr->value.s.step * attr->index++; + if (*val >= attr->value.s.max) { + *is_last = true; + attr->index = 0; + } + return ESP_GMF_ERR_OK; + case ESP_GMF_PROP_TYPE_MULTIPLE: + attr->index++; + *val = attr->value.m.factor * attr->index; + if (*val >= attr->value.m.max) { + *is_last = true; + attr->index = 0; + } + return ESP_GMF_ERR_OK; + case ESP_GMF_PROP_TYPE_CONSTANT: + attr->index++; + *val = attr->value.c.data; + *is_last = true; + return ESP_GMF_ERR_OK; + } + return ESP_GMF_ERR_NOT_SUPPORT; +} + +esp_gmf_err_t esp_gmf_cap_attr_get_first_value(esp_gmf_cap_attr_t *attr, uint32_t *val) +{ + ESP_GMF_NULL_CHECK(TAG, attr, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, val, return ESP_GMF_ERR_INVALID_ARG); + *val = 0; + switch (attr->prop_type) { + case ESP_GMF_PROP_TYPE_DISCRETE: + *val = ((uint32_t *)attr->value.d.collection)[0]; + return ESP_GMF_ERR_OK; + case ESP_GMF_PROP_TYPE_STEPWISE: + *val = attr->value.s.min; + return ESP_GMF_ERR_OK; + case ESP_GMF_PROP_TYPE_MULTIPLE: + *val = attr->value.m.min; + return ESP_GMF_ERR_OK; + case ESP_GMF_PROP_TYPE_CONSTANT: + *val = attr->value.c.data; + return ESP_GMF_ERR_OK; + } + return ESP_GMF_ERR_NOT_SUPPORT; +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/src/esp_gmf_element.c b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/src/esp_gmf_element.c new file mode 100644 index 0000000..3f580a8 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/src/esp_gmf_element.c @@ -0,0 +1,524 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#include +#include +#include "esp_log.h" +#include "esp_gmf_element.h" + +#include "esp_gmf_oal_mutex.h" +#include "esp_gmf_oal_thread.h" +#include "esp_gmf_oal_mem.h" +#include "esp_gmf_node.h" + +static const char *TAG = "ESP_GMF_ELEMENT"; + +static void *s_load_mutex = NULL; // Static mutex for protecting caps and methods + +static inline esp_gmf_err_t _ensure_load_mutex(void) +{ + if (s_load_mutex == NULL) { + void *new_mutex = esp_gmf_oal_mutex_create(); + if (new_mutex != NULL && s_load_mutex == NULL) { + s_load_mutex = new_mutex; + } else if (new_mutex != NULL) { + // Another thread created it first, destroy duplicate + esp_gmf_oal_mutex_destroy(new_mutex); + } else { + ESP_LOGE(TAG, "Failed to create load mutex"); + return ESP_GMF_ERR_MEMORY_LACK; + } + } + return ESP_GMF_ERR_OK; +} + +static inline int _get_port_cnt(esp_gmf_port_handle_t port) +{ + int k = 0; + esp_gmf_port_handle_t tmp = port; + while (tmp) { + k++; + tmp = tmp->next; + } + return k; +} + +static inline int _del_port_from_el(esp_gmf_port_handle_t *head, esp_gmf_port_handle_t io_inst) +{ + int ret = esp_gmf_port_del_at(head, io_inst); + ESP_GMF_RET_ON_ERROR(TAG, ret, return ESP_GMF_ERR_NOT_FOUND, "Not found port, head:%p, port:%p", head, io_inst); + esp_gmf_port_deinit(io_inst); + return ret; +} + +static inline esp_gmf_err_t esp_gmf_notify_state_changed(esp_gmf_element_handle_t handle, esp_gmf_event_state_t st) +{ + esp_gmf_element_t *el = (esp_gmf_element_t *)handle; + esp_gmf_event_pkt_t evt = { + .from = el, + .type = ESP_GMF_EVT_TYPE_CHANGE_STATE, + .sub = st, + .payload = NULL, + .payload_size = 0, + }; + el->cur_state = st; + if (el->event_func == NULL) { + ESP_LOGW(TAG, "[%s,%p] element not has register event callback", OBJ_GET_TAG(el), el); + return ESP_GMF_ERR_FAIL; + } + return el->event_func(&evt, el->ctx); +} + +esp_gmf_err_t esp_gmf_element_init(esp_gmf_element_handle_t handle, esp_gmf_element_cfg_t *config) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, config, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_element_t *el = (esp_gmf_element_t *)handle; + if (config->dependency == true) { + el->init_state = ESP_GMF_EVENT_STATE_NONE; + } else { + el->init_state = ESP_GMF_EVENT_STATE_INITIALIZED; + } + el->cur_state = el->init_state; + el->event_func = config->cb; + el->dependency = config->dependency; + el->in_attr.cap = config->in_attr.cap == 0 ? ESP_GMF_EL_PORT_CAP_SINGLE : config->in_attr.cap; + el->out_attr.cap = config->out_attr.cap == 0 ? ESP_GMF_EL_PORT_CAP_SINGLE : config->out_attr.cap; + el->in_attr.port.type = config->in_attr.port.type == 0 ? ESP_GMF_PORT_TYPE_BYTE : config->in_attr.port.type; + el->out_attr.port.type = config->out_attr.port.type == 0 ? ESP_GMF_PORT_TYPE_BYTE : config->out_attr.port.type; + el->in_attr.data_size = config->in_attr.data_size == 0 ? ESP_GMF_ELEMENT_PORT_DATA_SIZE_DEFAULT : config->in_attr.data_size; + el->out_attr.data_size = config->out_attr.data_size == 0 ? ESP_GMF_ELEMENT_PORT_DATA_SIZE_DEFAULT : config->out_attr.data_size; + + el->in_attr.port.buf_addr_aligned = config->in_attr.port.buf_addr_aligned == 0 ? ESP_GMF_ELEMENT_PORT_ADDR_ALIGNED_DEFAULT : config->in_attr.port.buf_addr_aligned; + el->out_attr.port.buf_addr_aligned = config->out_attr.port.buf_addr_aligned == 0 ? ESP_GMF_ELEMENT_PORT_ADDR_ALIGNED_DEFAULT : config->out_attr.port.buf_addr_aligned; + el->in_attr.port.buf_size_aligned = config->in_attr.port.buf_size_aligned == 0 ? 1 : config->in_attr.port.buf_size_aligned; + el->out_attr.port.buf_size_aligned = config->out_attr.port.buf_size_aligned == 0 ? 1 : config->out_attr.port.buf_size_aligned; + + el->ctx = config->ctx; + el->job_mask = 0; + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_element_deinit(esp_gmf_element_handle_t handle) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_element_t *el = (esp_gmf_element_t *)handle; + esp_gmf_method_destroy(el->method); + if (el->caps) { + esp_gmf_cap_destroy(el->caps); + } + esp_gmf_port_handle_t port = el->in; + while (port) { + esp_gmf_port_handle_t tmp = port->next; + esp_gmf_port_deinit(port); + port = tmp; + } + port = el->out; + while (port) { + esp_gmf_port_handle_t tmp = port->next; + esp_gmf_port_deinit(port); + port = tmp; + } + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_element_set_event_func(esp_gmf_element_handle_t handle, esp_gmf_event_cb cb, void *ctx) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_element_t *el = (esp_gmf_element_t *)handle; + el->event_func = cb; + el->ctx = ctx; + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_element_register_in_port(esp_gmf_element_handle_t handle, esp_gmf_port_handle_t io_inst) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, io_inst, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_element_t *el = (esp_gmf_element_t *)handle; + if ((el->in_attr.port.type & io_inst->attr.type) == 0) { + ESP_LOGE(TAG, "The in port type[%x] is unsupported, expected:%x,[%p-%s]", io_inst->attr.type, el->in_attr.port.type, handle, OBJ_GET_TAG(handle)); + return ESP_GMF_ERR_NOT_SUPPORT; + } + int cnt = _get_port_cnt(el->in); + if ((el->in_attr.cap & ESP_GMF_EL_PORT_CAP_SINGLE) && cnt) { + ESP_LOGE(TAG, "Can't register more in ports for an element that is only support single port, cnt:%x,[%p-%s]", cnt, handle, OBJ_GET_TAG(handle)); + return ESP_GMF_ERR_NOT_SUPPORT; + } + io_inst->attr.buf_size_aligned = el->in_attr.port.buf_size_aligned; + io_inst->attr.buf_addr_aligned = el->in_attr.port.buf_addr_aligned; + if (el->in == NULL) { + el->in = io_inst; + io_inst->next = NULL; + } else { + esp_gmf_port_add_last(el->in, io_inst); + } + esp_gmf_port_set_reader(io_inst, (void *)handle); + ESP_LOGD(TAG, "REG in port, el:%p-%s, head:%p, new:%p", el, OBJ_GET_TAG(el), el->in, io_inst); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_element_unregister_in_port(esp_gmf_element_handle_t handle, esp_gmf_port_handle_t io_inst) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_element_t *el = (esp_gmf_element_t *)handle; + int ret = ESP_GMF_ERR_OK; + if (io_inst) { + ESP_LOGD(TAG, "UNREG in port, el:%p-%s, head:%p, del:%p, cnt:%d", el, OBJ_GET_TAG(el), el->in, io_inst, _get_port_cnt(el->in)); + ret = _del_port_from_el(&el->in, io_inst); + ESP_GMF_RET_ON_ERROR(TAG, ret, return ESP_GMF_ERR_NOT_FOUND, "Unregister in port failed, del:%p, el:%p-%s", io_inst, el, OBJ_GET_TAG(el)); + } else { + esp_gmf_port_handle_t tmp = el->in; + while (tmp) { + esp_gmf_port_handle_t next = tmp->next; + ESP_LOGD(TAG, "UNREG in port, el:%p-%s, head:%p, del:%p, cnt:%d", el, OBJ_GET_TAG(el), el->in, tmp, _get_port_cnt(el->in)); + ret = _del_port_from_el(&el->in, tmp); + ESP_GMF_RET_ON_ERROR(TAG, ret, return ESP_GMF_ERR_NOT_FOUND, "Unregister all in port failed, del:%p, el:%p-%s", tmp, el, OBJ_GET_TAG(el)); + tmp = next; + } + } + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_element_register_out_port(esp_gmf_element_handle_t handle, esp_gmf_port_handle_t io_inst) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, io_inst, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_element_t *el = (esp_gmf_element_t *)handle; + if ((el->out_attr.port.type & io_inst->attr.type) == 0) { + ESP_LOGE(TAG, "The out port type[%x] is unsupported, expected:%x, [%p-%s]", io_inst->attr.type, el->out_attr.port.type, handle, OBJ_GET_TAG(handle)); + return ESP_GMF_ERR_NOT_SUPPORT; + } + int cnt = _get_port_cnt(el->out); + if ((el->out_attr.cap & ESP_GMF_EL_PORT_CAP_SINGLE) && cnt) { + ESP_LOGE(TAG, "Can't register more out ports for an element that is only support single port, cnt:%x, [%p-%s]", cnt, handle, OBJ_GET_TAG(handle)); + return ESP_GMF_ERR_NOT_SUPPORT; + } + io_inst->attr.buf_size_aligned = el->out_attr.port.buf_size_aligned; + io_inst->attr.buf_addr_aligned = el->out_attr.port.buf_addr_aligned; + if (el->out == NULL) { + el->out = io_inst; + io_inst->next = NULL; + } else { + esp_gmf_port_add_last(el->out, io_inst); + } + esp_gmf_port_set_writer(io_inst, (void *)handle); + ESP_LOGD(TAG, "REG out port, el:%p-%s, head:%p, new:%p", el, OBJ_GET_TAG(el), el->out, io_inst); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_element_unregister_out_port(esp_gmf_element_handle_t handle, esp_gmf_port_handle_t io_inst) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_element_t *el = (esp_gmf_element_t *)handle; + int ret = ESP_GMF_ERR_OK; + if (io_inst) { + ESP_LOGD(TAG, "UNREG out port, el:%p-%s, head:%p, del:%p, cnt:%d", el, OBJ_GET_TAG(el), el->out, io_inst, _get_port_cnt(el->out)); + ret = _del_port_from_el(&el->out, io_inst); + ESP_GMF_RET_ON_ERROR(TAG, ret, return ESP_GMF_ERR_NOT_FOUND, "Unregister in port failed, del:%p, el:%p-%s", io_inst, el, OBJ_GET_TAG(el)); + } else { + esp_gmf_port_handle_t tmp = el->out; + while (tmp) { + esp_gmf_port_handle_t next = tmp->next; + ESP_LOGD(TAG, "UNREG out port, el:%p-%s, head:%p, del:%p, cnt:%d", el, OBJ_GET_TAG(el), el->out, tmp, _get_port_cnt(el->out)); + ret = _del_port_from_el(&el->out, tmp); + ESP_GMF_RET_ON_ERROR(TAG, ret, return ESP_GMF_ERR_NOT_FOUND, "Unregister all in port failed, del:%p, el:%p-%s", tmp, el, OBJ_GET_TAG(el)); + tmp = next; + } + } + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_element_link_el(esp_gmf_element_handle_t handle, esp_gmf_element_handle_t new_el) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, new_el, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_element_t *header_el = (esp_gmf_element_t *)handle; + esp_gmf_element_t *tail_el = (esp_gmf_element_t *)esp_gmf_node_get_tail((esp_gmf_node_t *)header_el); + esp_gmf_node_add_last((esp_gmf_node_t *)header_el, (esp_gmf_node_t *)new_el); + + esp_gmf_port_set_reader(tail_el->out, new_el); + esp_gmf_port_set_writer(ESP_GMF_ELEMENT_GET(new_el)->in, tail_el); + + ESP_LOGD(TAG, "EL:%p-%s, NEXT EL:%p-%s", header_el, OBJ_GET_TAG(header_el), new_el, OBJ_GET_TAG(new_el)); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_element_get_next_el(esp_gmf_element_handle_t handle, esp_gmf_element_handle_t *next_el) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, next_el, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_element_t *el = (esp_gmf_element_t *)handle; + *next_el = (esp_gmf_element_t *)esp_gmf_node_for_next((esp_gmf_node_t *)el); + ESP_LOGD(TAG, "Get next, EL:%p-%s, NEXT EL:%p-%s", el, OBJ_GET_TAG(el), *next_el, OBJ_GET_TAG(*next_el)); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_element_get_prev_el(esp_gmf_element_handle_t handle, esp_gmf_element_handle_t *prev_el) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, prev_el, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_element_t *el = (esp_gmf_element_t *)handle; + *prev_el = (esp_gmf_element_t *)esp_gmf_node_for_prev((esp_gmf_node_t *)el); + ESP_LOGD(TAG, "Get previous, EL:%p-%s, PREV EL:%p-%s", el, OBJ_GET_TAG(el), *prev_el, OBJ_GET_TAG(*prev_el)); + return ESP_GMF_ERR_OK; +} + +esp_gmf_job_err_t esp_gmf_element_process_open(esp_gmf_element_handle_t handle, void *para) +{ + esp_gmf_element_t *el = (esp_gmf_element_t *)handle; + if ((el == NULL) || (el->ops.open == NULL)) { + ESP_LOGE(TAG, "There is no open function [%p-%s]", handle, OBJ_GET_TAG(handle)); + return ESP_GMF_ERR_FAIL; + } + // Last element outport allowed to be NULL + bool last_element = (el->base.next == NULL); + if ((el->in == NULL) || ((last_element == false) && (el->out == NULL))) { + ESP_LOGE(TAG, "There is no in or out port,in:%p,out:%p [%p-%s]", el->in, el->out, handle, OBJ_GET_TAG(handle)); + return ESP_GMF_JOB_ERR_FAIL; + } + esp_gmf_job_err_t ret = ESP_GMF_JOB_ERR_OK; + ret = el->ops.open(el, NULL); + if (ret == ESP_GMF_JOB_ERR_OK) { + esp_gmf_notify_state_changed(handle, ESP_GMF_EVENT_STATE_RUNNING); + } + return ret; +} + +esp_gmf_job_err_t esp_gmf_element_process_running(esp_gmf_element_handle_t handle, void *para) +{ + esp_gmf_element_t *el = (esp_gmf_element_t *)handle; + if ((el == NULL) || (el->ops.process == NULL)) { + ESP_LOGE(TAG, "There is no process function [%p-%s]", handle, OBJ_GET_TAG(handle)); + return ESP_GMF_ERR_FAIL; + } + return el->ops.process(el, NULL); +} + +esp_gmf_job_err_t esp_gmf_element_process_close(esp_gmf_element_handle_t handle, void *para) +{ + esp_gmf_element_t *el = (esp_gmf_element_t *)handle; + if ((el == NULL) || (el->ops.close == NULL)) { + ESP_LOGE(TAG, "There is no close function [%p-%s]", handle, OBJ_GET_TAG(handle)); + return ESP_GMF_ERR_FAIL; + } + // Release port still have reference + esp_gmf_port_t *in_port = ESP_GMF_ELEMENT_GET_IN_PORT(el); + if (in_port && in_port->ref_count) { + if (in_port->ops.release) { + in_port->ops.release(in_port->ctx, in_port->self_payload, 0); + } + in_port->ref_count = 0; + } + esp_gmf_job_err_t ret = el->ops.close(el, NULL); + return ret; +} + +esp_gmf_err_t esp_gmf_element_set_state(esp_gmf_element_handle_t handle, esp_gmf_event_state_t new_state) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_element_t *el = (esp_gmf_element_t *)handle; + el->cur_state = new_state; + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_element_get_state(esp_gmf_element_handle_t handle, esp_gmf_event_state_t *state) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_element_t *el = (esp_gmf_element_t *)handle; + if (state) { + *state = el->cur_state; + return ESP_GMF_ERR_OK; + } + return ESP_GMF_ERR_INVALID_ARG; +} + +esp_gmf_err_t esp_gmf_element_reset_state(esp_gmf_element_handle_t handle) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_element_t *el = (esp_gmf_element_t *)handle; + el->cur_state = el->init_state; + el->job_mask = 0; + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_element_reset_port(esp_gmf_element_handle_t handle) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_element_t *el = (esp_gmf_element_t *)handle; + esp_gmf_port_t *tmp = el->in; + while (tmp) { + esp_gmf_port_reset(tmp); + tmp = tmp->next; + } + tmp = el->out; + while (tmp) { + esp_gmf_port_reset(tmp); + tmp = tmp->next; + } + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_element_receive_event(esp_gmf_element_handle_t handle, esp_gmf_event_pkt_t *event, void *ctx) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_element_t *el = (esp_gmf_element_t *)handle; + if (el->ops.event_receiver) { + return el->ops.event_receiver(event, el); + } + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_element_set_job_mask(esp_gmf_element_handle_t handle, uint16_t mask) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_element_t *el = (esp_gmf_element_t *)handle; + el->job_mask = mask; + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_element_change_job_mask(esp_gmf_element_handle_t handle, uint16_t mask) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_element_t *el = (esp_gmf_element_t *)handle; + el->job_mask |= mask; + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_element_get_job_mask(esp_gmf_element_handle_t handle, uint16_t *mask) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_element_t *el = (esp_gmf_element_t *)handle; + *mask = el->job_mask; + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_element_notify_snd_info(esp_gmf_element_handle_t handle, esp_gmf_info_sound_t *info) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_element_t *el = (esp_gmf_element_t *)handle; + esp_gmf_event_pkt_t evt = { + .from = el, + .type = ESP_GMF_EVT_TYPE_REPORT_INFO, + .sub = ESP_GMF_INFO_SOUND, + .payload = info, + .payload_size = sizeof(*info), + }; + if (el->event_func == NULL) { + ESP_LOGW(TAG, "[%p-%s] element not has register event callback", el, OBJ_GET_TAG(el)); + return ESP_GMF_ERR_FAIL; + } + return el->event_func(&evt, el->ctx); +} + +esp_gmf_err_t esp_gmf_element_notify_vid_info(esp_gmf_element_handle_t handle, esp_gmf_info_video_t *info) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_element_t *el = (esp_gmf_element_t *)handle; + esp_gmf_event_pkt_t evt = { + .from = el, + .type = ESP_GMF_EVT_TYPE_REPORT_INFO, + .sub = ESP_GMF_INFO_VIDEO, + .payload = info, + .payload_size = sizeof(*info), + }; + if (el->event_func == NULL) { + ESP_LOGW(TAG, "[%p-%s] element not has register event callback", el, OBJ_GET_TAG(el)); + return ESP_GMF_ERR_FAIL; + } + return el->event_func(&evt, el->ctx); +} + +esp_gmf_err_t esp_gmf_element_register_method(esp_gmf_element_handle_t handle, const char *name, esp_gmf_method_func func, esp_gmf_args_desc_t *args_desc) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, func, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_element_t *el = (esp_gmf_element_t *)handle; + int ret = esp_gmf_method_append(&el->method, name, func, args_desc); + return ret; +} + +esp_gmf_err_t esp_gmf_element_exe_method(esp_gmf_element_handle_t handle, const char *name, uint8_t *buf, int buf_len) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, name, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, buf, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_element_t *el = (esp_gmf_element_t *)handle; + esp_gmf_method_t *mthd = el->method; + if (mthd == NULL) { + ESP_LOGE(TAG, "There are no executable methods, [%p-%s]", el, OBJ_GET_TAG(el)); + return ESP_GMF_ERR_NOT_SUPPORT; + } + int ret = 0; + while (mthd) { + if (strcasecmp(mthd->name, name) == 0) { + ret = mthd->func(handle, mthd->args_desc, buf, buf_len); + return ret; + } + ESP_LOGD(TAG, "Method[%p-%s], ret:%x, [%p-%s]\r\n", mthd, mthd->name, ret, el, OBJ_GET_TAG(el)); + mthd = mthd->next; + } + return ret; +} + +esp_gmf_err_t esp_gmf_element_get_method(esp_gmf_element_handle_t handle, const esp_gmf_method_t **mthd) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, mthd, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_element_t *el = (esp_gmf_element_t *)handle; + if (el->method != NULL) { + *mthd = el->method; + return ESP_GMF_ERR_OK; + } + if (el->ops.load_methods) { + esp_gmf_err_t ret = _ensure_load_mutex(); + if (ret != ESP_GMF_ERR_OK) { + return ret; + } + esp_gmf_oal_mutex_lock(s_load_mutex); + if (el->method == NULL) { + ret = el->ops.load_methods(handle); + if (ret != ESP_GMF_ERR_OK) { + esp_gmf_oal_mutex_unlock(s_load_mutex); + ESP_LOGE(TAG, "Load method failed, ret:%x, [%p-%s]\r\n", ret, el, OBJ_GET_TAG(el)); + return ret; + } + } + esp_gmf_oal_mutex_unlock(s_load_mutex); + } + *mthd = el->method; + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_element_get_caps(esp_gmf_element_handle_t handle, const esp_gmf_cap_t **caps) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, caps, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_element_t *el = (esp_gmf_element_t *)handle; + if (el->caps != NULL) { + *caps = el->caps; + return ESP_GMF_ERR_OK; + } + if (el->ops.load_caps) { + esp_gmf_err_t ret = _ensure_load_mutex(); + if (ret != ESP_GMF_ERR_OK) { + return ret; + } + esp_gmf_oal_mutex_lock(s_load_mutex); + if (el->caps == NULL) { + ret = el->ops.load_caps(handle); + if (ret != ESP_GMF_ERR_OK) { + esp_gmf_oal_mutex_unlock(s_load_mutex); + ESP_LOGE(TAG, "Load caps failed, ret:%x, [%p-%s]\r\n", ret, el, OBJ_GET_TAG(el)); + return ret; + } + } + esp_gmf_oal_mutex_unlock(s_load_mutex); + } + *caps = el->caps; + return ESP_GMF_ERR_OK; +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/src/esp_gmf_event.c b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/src/esp_gmf_event.c new file mode 100644 index 0000000..6ebe748 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/src/esp_gmf_event.c @@ -0,0 +1,26 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#include "stdlib.h" +#include "esp_gmf_event.h" + +static const char *event_state_string[] = { + "ESP_GMF_EVENT_STATE_NONE", + "ESP_GMF_EVENT_STATE_INITIALIZED", + "ESP_GMF_EVENT_STATE_OPENING", + "ESP_GMF_EVENT_STATE_RUNNING", + "ESP_GMF_EVENT_STATE_PAUSED", + "ESP_GMF_EVENT_STATE_STOPPED", + "ESP_GMF_EVENT_STATE_FINISHED", + "ESP_GMF_EVENT_STATE_ERROR", + "", +}; + +const char *esp_gmf_event_get_state_str(esp_gmf_event_state_t st) +{ + return event_state_string[st]; +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/src/esp_gmf_io.c b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/src/esp_gmf_io.c new file mode 100644 index 0000000..fc6b04a --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/src/esp_gmf_io.c @@ -0,0 +1,270 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#include +#include +#include "esp_gmf_io.h" +#include "esp_log.h" +#include "esp_gmf_info_file.h" +#include "esp_gmf_oal_mem.h" +#include "esp_gmf_job.h" + +static const char *TAG = "ESP_GMF_IO"; + +esp_gmf_err_t esp_gmf_io_init(esp_gmf_io_handle_t handle, esp_gmf_io_cfg_t *io_cfg) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_io_t *io = (esp_gmf_io_t *)handle; + if (io_cfg) { + memcpy(&io->task_cfg, io_cfg, sizeof(esp_gmf_io_cfg_t)); + } else { + memset(&io->task_cfg, 0, sizeof(esp_gmf_io_cfg_t)); + } + return esp_gmf_info_file_init(&io->attr); +} + +esp_gmf_err_t esp_gmf_io_deinit(esp_gmf_io_handle_t handle) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_io_t *io = (esp_gmf_io_t *)handle; + if (io->task_hd) { + esp_gmf_task_deinit(io->task_hd); + io->task_hd = NULL; + } + return esp_gmf_info_file_deinit(&io->attr); +} + +esp_gmf_err_t esp_gmf_io_open(esp_gmf_io_handle_t handle) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_io_t *io = (esp_gmf_io_t *)handle; + if (io->open == NULL) { + return ESP_GMF_ERR_NOT_READY; + } + + int ret = ESP_GMF_ERR_OK; + esp_gmf_io_cfg_t *io_cfg = &io->task_cfg; + if (io->task_hd == NULL && io_cfg && io_cfg->thread.stack > 0) { + esp_gmf_task_cfg_t cfg = DEFAULT_ESP_GMF_TASK_CONFIG(); + cfg.thread.stack = io_cfg->thread.stack; + cfg.thread.prio = io_cfg->thread.prio; + cfg.thread.core = io_cfg->thread.core; + cfg.thread.stack_in_ext = io_cfg->thread.stack_in_ext; + cfg.name = OBJ_GET_TAG(io); + cfg.ctx = handle; + cfg.cb = NULL; + if (esp_gmf_task_init(&cfg, &io->task_hd) != ESP_GMF_ERR_OK) { + ESP_LOGE(TAG, "Failed to create new IO task, [%p-%s]", io, OBJ_GET_TAG(io)); + return ESP_GMF_ERR_FAIL; + } + char name[ESP_GMF_JOB_LABLE_MAX_LEN] = ""; + esp_gmf_job_str_cat(name, ESP_GMF_JOB_LABLE_MAX_LEN, OBJ_GET_TAG(io), ESP_GMF_JOB_STR_PROCESS, strlen(ESP_GMF_JOB_STR_PROCESS)); + ret = esp_gmf_task_register_ready_job(io->task_hd, name, io->process, ESP_GMF_JOB_TIMES_INFINITE, handle, true); + ESP_LOGD(TAG, "Initialize a GMF IO[%p-%s], stack:%d, thread:%p-%s", handle, OBJ_GET_TAG(io), + io_cfg == NULL ? -1 : io_cfg->thread.stack, io->task_hd, OBJ_GET_TAG(io->task_hd)); + } + ret = io->open(io); + ESP_GMF_RET_ON_ERROR(TAG, ret, return ret, "esp_gmf_io_open failed"); + if (io->task_hd) { + ret = esp_gmf_task_run(io->task_hd); + } + return ret; +} + +esp_gmf_err_t esp_gmf_io_seek(esp_gmf_io_handle_t handle, uint64_t seek_byte_pos) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_io_t *io = (esp_gmf_io_t *)handle; + if (io->seek == NULL) { + return ESP_GMF_ERR_NOT_READY; + } + int ret = ESP_GMF_ERR_OK; + if (io->prev_close) { + io->prev_close(io); + } + if (io->task_hd) { + ret = esp_gmf_task_pause(io->task_hd); + } + if (ret == ESP_GMF_ERR_OK) { + ret = io->seek(io, seek_byte_pos); + } + if (io->task_hd) { + esp_gmf_event_state_t st = 0; + esp_gmf_task_get_state(io->task_hd, &st); + if (st == ESP_GMF_EVENT_STATE_PAUSED) { + ret = esp_gmf_task_resume(io->task_hd); + } else { + char name[ESP_GMF_JOB_LABLE_MAX_LEN] = ""; + esp_gmf_job_str_cat(name, ESP_GMF_JOB_LABLE_MAX_LEN, OBJ_GET_TAG(io), ESP_GMF_JOB_STR_PROCESS, strlen(ESP_GMF_JOB_STR_PROCESS)); + ret = esp_gmf_task_register_ready_job(io->task_hd, "name", io->process, ESP_GMF_JOB_TIMES_INFINITE, handle, true); + ret = esp_gmf_task_run(io->task_hd); + } + } + return ret; +} + +esp_gmf_err_t esp_gmf_io_close(esp_gmf_io_handle_t handle) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_io_t *io = (esp_gmf_io_t *)handle; + if (io->close == NULL) { + return ESP_GMF_ERR_NOT_READY; + } + ESP_LOGD(TAG, "%s, [%p-%s]", __func__, io, OBJ_GET_TAG(io)); + if (io->prev_close) { + io->prev_close(io); + } + if (io->task_hd) { + esp_gmf_task_stop(io->task_hd); + } + return io->close(io); +} + +esp_gmf_err_io_t esp_gmf_io_acquire_read(esp_gmf_io_handle_t handle, esp_gmf_payload_t *load, uint32_t wanted_size, int wait_ticks) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_IO_FAIL); + ESP_GMF_NULL_CHECK(TAG, load, return ESP_GMF_IO_FAIL); + esp_gmf_io_t *io = (esp_gmf_io_t *)handle; + if (io->acquire_read == NULL) { + return ESP_GMF_IO_FAIL; + } + return io->acquire_read(io, load, wanted_size, wait_ticks); +} + +esp_gmf_err_io_t esp_gmf_io_release_read(esp_gmf_io_handle_t handle, esp_gmf_payload_t *load, int wait_ticks) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_IO_FAIL); + ESP_GMF_NULL_CHECK(TAG, load, return ESP_GMF_IO_FAIL); + esp_gmf_io_t *io = (esp_gmf_io_t *)handle; + if (io->release_read == NULL) { + return ESP_GMF_IO_FAIL; + } + return io->release_read(io, load, wait_ticks); +} + +esp_gmf_err_io_t esp_gmf_io_acquire_write(esp_gmf_io_handle_t handle, esp_gmf_payload_t *load, uint32_t wanted_size, int wait_ticks) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_IO_FAIL); + ESP_GMF_NULL_CHECK(TAG, load, return ESP_GMF_IO_FAIL); + esp_gmf_io_t *io = (esp_gmf_io_t *)handle; + if (io->acquire_write == NULL) { + return ESP_GMF_IO_FAIL; + } + return io->acquire_write(io, load, wanted_size, wait_ticks); +} + +esp_gmf_err_io_t esp_gmf_io_release_write(esp_gmf_io_handle_t handle, esp_gmf_payload_t *load, int wait_ticks) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_IO_FAIL); + ESP_GMF_NULL_CHECK(TAG, load, return ESP_GMF_IO_FAIL); + esp_gmf_io_t *io = (esp_gmf_io_t *)handle; + if (io->release_write == NULL) { + return ESP_GMF_IO_FAIL; + } + return io->release_write(io, load, wait_ticks); +} + +esp_gmf_err_t esp_gmf_io_set_info(esp_gmf_io_handle_t handle, esp_gmf_info_file_t *info) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, info, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_io_t *io = (esp_gmf_io_t *)handle; + memcpy(&io->attr, info, sizeof(esp_gmf_info_file_t)); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_io_get_info(esp_gmf_io_handle_t handle, esp_gmf_info_file_t *info) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, info, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_io_t *io = (esp_gmf_io_t *)handle; + memcpy(info, &io->attr, sizeof(esp_gmf_info_file_t)); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_io_set_uri(esp_gmf_io_handle_t handle, const char *uri) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_io_t *io = (esp_gmf_io_t *)handle; + int ret = esp_gmf_info_file_set_uri(&io->attr, uri); + return ret; +} +esp_gmf_err_t esp_gmf_io_get_uri(esp_gmf_io_handle_t handle, char **uri) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, uri, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_io_t *io = (esp_gmf_io_t *)handle; + return esp_gmf_info_file_get_uri(&io->attr, uri); +} + +esp_gmf_err_t esp_gmf_io_set_pos(esp_gmf_io_handle_t handle, uint64_t byte_pos) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_io_t *io = (esp_gmf_io_t *)handle; + return esp_gmf_info_file_set_pos(&io->attr, byte_pos); +} +esp_gmf_err_t esp_gmf_io_update_pos(esp_gmf_io_handle_t handle, uint64_t byte_pos) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_io_t *io = (esp_gmf_io_t *)handle; + return esp_gmf_info_file_update_pos(&io->attr, byte_pos); +} + +esp_gmf_err_t esp_gmf_io_get_pos(esp_gmf_io_handle_t handle, uint64_t *byte_pos) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, byte_pos, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_io_t *io = (esp_gmf_io_t *)handle; + return esp_gmf_info_file_get_pos(&io->attr, byte_pos); +} + +esp_gmf_err_t esp_gmf_io_set_size(esp_gmf_io_handle_t handle, uint64_t total_size) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_io_t *io = (esp_gmf_io_t *)handle; + return esp_gmf_info_file_set_size(&io->attr, total_size); +} + +esp_gmf_err_t esp_gmf_io_get_size(esp_gmf_io_handle_t handle, uint64_t *total_size) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, total_size, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_io_t *io = (esp_gmf_io_t *)handle; + return esp_gmf_info_file_get_size(&io->attr, total_size); +} + +esp_gmf_err_t esp_gmf_io_get_type(esp_gmf_io_handle_t handle, esp_gmf_io_type_t *type) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + if (type) { + esp_gmf_io_t *io = (esp_gmf_io_t *)handle; + *type = io->type; + return ESP_GMF_ERR_OK; + } + return ESP_GMF_ERR_INVALID_ARG; +} + +esp_gmf_err_t esp_gmf_io_reset(esp_gmf_io_handle_t handle) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_io_t *io = (esp_gmf_io_t *)handle; + + esp_gmf_io_set_pos(handle, 0); + esp_gmf_io_set_size(handle, 0); + + if (io->reset) { + io->reset(io); + } + int ret = ESP_GMF_ERR_OK; + if (io->task_hd) { + esp_gmf_task_reset(io->task_hd); + char name[ESP_GMF_JOB_LABLE_MAX_LEN] = ""; + esp_gmf_job_str_cat(name, ESP_GMF_JOB_LABLE_MAX_LEN, OBJ_GET_TAG(io), ESP_GMF_JOB_STR_PROCESS, strlen(ESP_GMF_JOB_STR_PROCESS)); + ret = esp_gmf_task_register_ready_job(io->task_hd, name, io->process, ESP_GMF_JOB_TIMES_INFINITE, handle, true); + } + return ret; +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/src/esp_gmf_node.c b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/src/esp_gmf_node.c new file mode 100644 index 0000000..1eb0c3d --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/src/esp_gmf_node.c @@ -0,0 +1,128 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#include "esp_gmf_node.h" +#include "esp_gmf_oal_mem.h" +#include "esp_log.h" +#include + +esp_gmf_node_t *esp_gmf_node_get_head(esp_gmf_node_t *node, int *num) +{ + esp_gmf_node_t *head; + if (node == NULL) { + return NULL; + } + head = node; + int cnt = 1; + while (head->prev != NULL) { + head = head->prev; + cnt++; + } + *num = cnt; + return head; +} + +esp_gmf_node_t *esp_gmf_node_get_tail(esp_gmf_node_t *root) +{ + esp_gmf_node_t *last; + if (root == NULL) { + return NULL; + } + last = root; + while (last->next != NULL) { + last = last->next; + } + return last; +} + +void esp_gmf_node_add_last(esp_gmf_node_t *root, esp_gmf_node_t *new_node) +{ + esp_gmf_node_t *last = esp_gmf_node_get_tail(root); + if (last != NULL) { + last->next = new_node; + new_node->prev = last; + new_node->next = NULL; + } else { + ESP_LOGE("NODE", "The root is NULL, %s, %p, %p", __func__, root, new_node); + } +} + +void esp_gmf_node_clear(esp_gmf_node_t **root, node_free del) +{ + while (*root) { + esp_gmf_node_t *next = (*root)->next; + if (del) { + del(*root); + } + *root = next; + } +} + +int esp_gmf_node_get_size(esp_gmf_node_t *root) +{ + if (root == NULL) { + return 0; + } + int count = 0; + while (root->next != NULL) { + count++; + root = root->next; + } + return count; +} + +void esp_gmf_node_insert_after(esp_gmf_node_t *prev, esp_gmf_node_t *new) +{ + if (prev == NULL) { + return; + } + new->next = prev->next; + prev->next = new; + new->prev = prev; + + if (new->next) { + new->next->prev = new; + } +} + +void esp_gmf_node_del_at(esp_gmf_node_t **root, esp_gmf_node_t *del) +{ + if (*root == NULL || del == NULL) { + return; + } + if (*root == del) { + *root = del->next; + return; + } + if (del->next) { + del->next->prev = del->prev; + } else { + del->prev->next = NULL; + } + + if (del->prev) { + del->prev->next = del->next; + } + del->next = NULL; + del->prev = NULL; +} + +esp_gmf_node_t *esp_gmf_node_for_next(esp_gmf_node_t *node) +{ + if (node == NULL) { + return NULL; + } + return node->next; +} + +esp_gmf_node_t *esp_gmf_node_for_prev(esp_gmf_node_t *node) +{ + if (node == NULL) { + return NULL; + } + return node->prev; +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/src/esp_gmf_obj.c b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/src/esp_gmf_obj.c new file mode 100644 index 0000000..91ca9fe --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/src/esp_gmf_obj.c @@ -0,0 +1,101 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#include +#include +#include "esp_gmf_obj.h" +#include "esp_gmf_err.h" +#include "esp_log.h" +#include "esp_gmf_oal_mem.h" + +static const char *TAG = "ESP_GMF_OBJ"; + +esp_gmf_err_t esp_gmf_obj_dupl(esp_gmf_obj_handle_t old_obj, esp_gmf_obj_handle_t *new_obj) +{ + ESP_GMF_NULL_CHECK(TAG, old_obj, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, new_obj, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_obj_t *tmp = (esp_gmf_obj_t *)old_obj; + esp_gmf_err_t ret = ESP_GMF_ERR_FAIL; + if (tmp->new_obj) { + ret = tmp->new_obj(tmp->cfg, new_obj); + if (ret == ESP_GMF_ERR_OK) { + // Duplicate TAG also + ret = esp_gmf_obj_set_tag(*new_obj, OBJ_GET_TAG(old_obj)); + if (ret != ESP_GMF_ERR_OK) { + esp_gmf_obj_delete(*new_obj); + *new_obj = NULL; + } + } + } else { + ESP_LOGE(TAG, "%s is no new function [%p-%s], ", __func__, old_obj, OBJ_GET_TAG(old_obj)); + } + return ret; +} + +esp_gmf_err_t esp_gmf_obj_new(esp_gmf_obj_handle_t old_obj, void *cfg, esp_gmf_obj_handle_t *new_obj) +{ + ESP_GMF_NULL_CHECK(TAG, old_obj, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, new_obj, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_obj_t *tmp = (esp_gmf_obj_t *)old_obj; + if (tmp->new_obj) { + return tmp->new_obj(cfg, new_obj); + } + ESP_LOGE(TAG, "%s is no new function [%p-%s], ", __func__, old_obj, OBJ_GET_TAG(old_obj)); + return ESP_GMF_ERR_FAIL; +} + +esp_gmf_err_t esp_gmf_obj_delete(esp_gmf_obj_handle_t obj) +{ + ESP_GMF_NULL_CHECK(TAG, obj, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_obj_t *h = (esp_gmf_obj_t *)obj; + ESP_LOGD(TAG, "esp_gmf_obj_delete:%p", obj); + if (h->tag) { + esp_gmf_oal_free((void *)h->tag); + h->tag = NULL; + } + if (h->del_obj) { + return h->del_obj(h); + } + ESP_LOGE(TAG, "There is no delete function [%p-%s]", obj, OBJ_GET_TAG(obj)); + return ESP_GMF_ERR_FAIL; +} + +esp_gmf_err_t esp_gmf_obj_set_config(esp_gmf_obj_handle_t obj, void *cfg, int cfg_size) +{ + ESP_GMF_NULL_CHECK(TAG, obj, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_obj_t *h = (esp_gmf_obj_t *)obj; + h->cfg = cfg; + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_obj_set_tag(esp_gmf_obj_handle_t obj, const char *tag) +{ + ESP_GMF_NULL_CHECK(TAG, obj, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_obj_t *h = (esp_gmf_obj_t *)obj; + if (h->tag) { + esp_gmf_oal_free((void *)h->tag); + h->tag = NULL; + } + if (tag) { + if (strlen(tag) >= ESP_GMF_TAG_MAX_LEN) { + ESP_LOGE(TAG, "The length of tag is out of range, len:%d", strlen(tag)); + return ESP_GMF_ERR_INVALID_ARG; + } + h->tag = esp_gmf_oal_strdup(tag); + ESP_GMF_MEM_CHECK(TAG, h->tag, return ESP_GMF_ERR_MEMORY_LACK); + } + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_obj_get_tag(esp_gmf_obj_handle_t obj, char **tag) +{ + ESP_GMF_NULL_CHECK(TAG, obj, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, tag, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_obj_t *h = (esp_gmf_obj_t *)obj; + *tag = (char *)h->tag; + return ESP_GMF_ERR_OK; +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/src/esp_gmf_payload.c b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/src/esp_gmf_payload.c new file mode 100644 index 0000000..17a028f --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/src/esp_gmf_payload.c @@ -0,0 +1,122 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#include "stdlib.h" +#include "esp_gmf_oal_mem.h" +#include "esp_gmf_payload.h" +#include "esp_log.h" +#include "string.h" + +static const char *TAG = "ESP_GMF_PAYLOAD"; + +esp_gmf_err_t esp_gmf_payload_new(esp_gmf_payload_t **instance) +{ + if (instance == NULL) { + ESP_LOGE(TAG, "Invalid parameters on %s, h:%p", __func__, instance); + return ESP_GMF_ERR_INVALID_ARG; + } + *instance = (esp_gmf_payload_t *)esp_gmf_oal_calloc(1, sizeof(esp_gmf_payload_t)); + ESP_LOGD(TAG, "New a payload, h:%p, called:0x%08x", *instance, (intptr_t)__builtin_return_address(0) - 2); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_payload_new_with_len(uint32_t buf_length, esp_gmf_payload_t **instance) +{ + if ((instance == NULL) || (buf_length == 0)) { + ESP_LOGE(TAG, "Invalid parameters on %s, h:%p, l:%ld", __func__, instance, buf_length); + return ESP_GMF_ERR_INVALID_ARG; + } + *instance = (esp_gmf_payload_t *)esp_gmf_oal_calloc(1, sizeof(esp_gmf_payload_t)); + ESP_GMF_NULL_CHECK(TAG, *instance, return ESP_GMF_ERR_MEMORY_LACK); + (*instance)->buf = esp_gmf_oal_calloc(1, buf_length); + ESP_GMF_NULL_CHECK(TAG, (*instance)->buf, { + esp_gmf_oal_free(*instance); + return ESP_GMF_ERR_MEMORY_LACK; + }); + (*instance)->buf_length = buf_length; + (*instance)->needs_free = 1; + ESP_LOGD(TAG, "New a payload with buffer, h:%p, buf:%p, l:%ld, called:0x%08x", *instance, (*instance)->buf, buf_length, (intptr_t)__builtin_return_address(0) - 2); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_payload_copy_data(esp_gmf_payload_t *src, esp_gmf_payload_t *dest) +{ + if ((src == NULL) || (dest == 0) || (src->buf == NULL) || (dest->buf == 0)) { + ESP_LOGE(TAG, "Invalid parameters, src:%p-buf:%p, dest:%p-buf:%p", src, src->buf, dest, dest == NULL ? NULL : dest->buf); + return ESP_GMF_ERR_INVALID_ARG; + } + if (src->valid_size > dest->buf_length) { + ESP_LOGE(TAG, "The valid size large than target buffer length, src:%p-l:%d, dest:%p-l:%d", + src, src->valid_size, dest, dest->buf_length); + return ESP_GMF_ERR_OUT_OF_RANGE; + } + if (src->valid_size > 0) { + memcpy(dest->buf, src->buf, src->valid_size); + } + dest->valid_size = src->valid_size; + dest->is_done = src->is_done; + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_payload_realloc_aligned_buf(esp_gmf_payload_t *instance, uint8_t align, uint32_t new_length) +{ + if ((instance == NULL) || (new_length == 0)) { + ESP_LOGE(TAG, "Invalid parameters on %s, h:%p, l:%ld", __func__, instance, new_length); + return ESP_GMF_ERR_INVALID_ARG; + } + // `buf` allocates by others + if (instance->buf && (instance->needs_free == 0)) { + ESP_LOGW(TAG, "Does not support reallocation of payload buffer that were allocated externally, p:%p, buf:%p, l:%d, new_l:%ld", instance, instance->buf, instance->buf_length, new_length); + return ESP_GMF_ERR_NOT_SUPPORT; + } + uint8_t *buf = esp_gmf_oal_malloc_align(align, new_length); + ESP_GMF_NULL_CHECK(TAG, buf, { return ESP_GMF_ERR_MEMORY_LACK;}); + if (instance->buf) { + // There was no operation of memcpy valid size. Because this function is to expand the buffer size. + ESP_LOGD(TAG, "Free payload:%p, buf:%p-%d, needs_free:%d", instance, instance->buf, instance->buf_length, instance->needs_free); + esp_gmf_oal_free(instance->buf); + } + instance->needs_free = 1; + instance->buf = buf; + instance->buf_length = new_length; + ESP_LOGD(TAG, "Realloc payload, h:%p, new_buf:%p-%ld, called:0x%08x", instance, instance->buf, new_length, (intptr_t)__builtin_return_address(0) - 2); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_payload_realloc_buf(esp_gmf_payload_t *instance, uint32_t new_length) +{ + return esp_gmf_payload_realloc_aligned_buf(instance, 1, new_length); +} + +esp_gmf_err_t esp_gmf_payload_set_done(esp_gmf_payload_t *instance) +{ + ESP_GMF_NULL_CHECK(TAG, instance, return ESP_GMF_ERR_INVALID_ARG); + instance->is_done = true; + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_payload_clean_done(esp_gmf_payload_t *instance) +{ + ESP_GMF_NULL_CHECK(TAG, instance, return ESP_GMF_ERR_INVALID_ARG); + instance->is_done = false; + return ESP_GMF_ERR_OK; +} + +void esp_gmf_payload_delete(esp_gmf_payload_t *instance) +{ + ESP_LOGD(TAG, "Delete a payload, h:%p, needs_free:%d, buf:%p, l:%d", instance, instance != NULL ? instance->needs_free : -1, + instance != NULL ? instance->buf : NULL, instance != NULL ? instance->buf_length : -1); + if (instance) { + if (instance->needs_free) { + esp_gmf_oal_free(instance->buf); + instance->needs_free = 0; + } + instance->buf = NULL; + instance->buf_length = 0; + esp_gmf_oal_free(instance); + } +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/src/esp_gmf_pic_element.c b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/src/esp_gmf_pic_element.c new file mode 100644 index 0000000..2f9fb35 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/src/esp_gmf_pic_element.c @@ -0,0 +1,140 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#include +#include "esp_gmf_pic_element.h" +#include "esp_gmf_oal_mem.h" +#include "esp_gmf_oal_mutex.h" +#include "esp_log.h" +#include "esp_gmf_info_file.h" + +static const char *TAG = "ESP_GMF_PIC_EL"; + +esp_gmf_err_t esp_gmf_pic_el_init(esp_gmf_pic_element_handle_t handle, esp_gmf_element_cfg_t *config) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, config, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_pic_element_t *pic = (esp_gmf_pic_element_t *)handle; + config->ctx = (void *)pic; + esp_gmf_element_init(&pic->base, config); + esp_gmf_info_file_init(&pic->file_info); + pic->lock = esp_gmf_oal_mutex_create(); + ESP_GMF_MEM_CHECK(TAG, pic->lock, { + esp_gmf_element_deinit(&pic->base); + esp_gmf_oal_free(pic); + return ESP_GMF_ERR_MEMORY_LACK; + }); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_pic_el_get_metadata(esp_gmf_pic_element_handle_t handle, esp_gmf_info_metadata_t *meta) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, meta, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_pic_element_t *pic = (esp_gmf_pic_element_t *)handle; + esp_gmf_oal_mutex_lock(pic->lock); + memcpy(meta, &pic->meta_info, sizeof(pic->meta_info)); + esp_gmf_oal_mutex_unlock(pic->lock); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_pic_el_set_metadata(esp_gmf_pic_element_handle_t handle, void *value, int len) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, value, return ESP_GMF_ERR_INVALID_ARG); + + esp_gmf_pic_element_t *pic = (esp_gmf_pic_element_t *)handle; + esp_gmf_oal_mutex_lock(pic->lock); + pic->meta_info.content = (void *)esp_gmf_oal_calloc(1, len); + pic->meta_info.length = 0; + ESP_GMF_MEM_CHECK(TAG, pic->meta_info.content, esp_gmf_oal_mutex_unlock(pic->lock); return ESP_GMF_ERR_MEMORY_LACK); + pic->meta_info.length = len; + memcpy(pic->meta_info.content, value, len); + esp_gmf_oal_mutex_unlock(pic->lock); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_pic_el_get_file_info(esp_gmf_pic_element_handle_t handle, esp_gmf_info_file_t *info) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, info, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_pic_element_t *pic = (esp_gmf_pic_element_t *)handle; + esp_gmf_oal_mutex_lock(pic->lock); + memcpy(info, &pic->file_info, sizeof(pic->file_info)); + esp_gmf_oal_mutex_unlock(pic->lock); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_pic_el_set_file_info(esp_gmf_pic_element_handle_t handle, esp_gmf_info_file_t *info) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, info, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_pic_element_t *pic = (esp_gmf_pic_element_t *)handle; + esp_gmf_oal_mutex_lock(pic->lock); + memcpy(&pic->file_info, info, sizeof(pic->file_info)); + esp_gmf_oal_mutex_unlock(pic->lock); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_pic_el_get_pic_info(esp_gmf_pic_element_handle_t handle, esp_gmf_info_pic_t *info) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, info, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_pic_element_t *pic = (esp_gmf_pic_element_t *)handle; + esp_gmf_oal_mutex_lock(pic->lock); + memcpy(info, &pic->pic_info, sizeof(pic->pic_info)); + esp_gmf_oal_mutex_unlock(pic->lock); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_pic_el_set_pic_info(esp_gmf_pic_element_handle_t handle, esp_gmf_info_pic_t *info) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, info, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_pic_element_t *pic = (esp_gmf_pic_element_t *)handle; + esp_gmf_oal_mutex_lock(pic->lock); + memcpy(&pic->pic_info, info, sizeof(pic->pic_info)); + esp_gmf_oal_mutex_unlock(pic->lock); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_pic_el_set_file_size(esp_gmf_pic_element_handle_t handle, uint64_t size) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_pic_element_t *pic = (esp_gmf_pic_element_t *)handle; + esp_gmf_oal_mutex_lock(pic->lock); + esp_gmf_info_file_set_size(&pic->file_info, size); + esp_gmf_oal_mutex_unlock(pic->lock); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_pic_el_update_file_pos(esp_gmf_pic_element_handle_t handle, uint64_t pos) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_pic_element_t *pic = (esp_gmf_pic_element_t *)handle; + esp_gmf_oal_mutex_lock(pic->lock); + esp_gmf_info_file_update_pos(&pic->file_info, pos); + esp_gmf_oal_mutex_unlock(pic->lock); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_pic_el_deinit(esp_gmf_pic_element_handle_t handle) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_pic_element_t *pic = (esp_gmf_pic_element_t *)handle; + esp_gmf_oal_mutex_lock(pic->lock); + + if (pic->meta_info.content) { + esp_gmf_oal_free(pic->meta_info.content); + pic->meta_info.length = 0; + } + esp_gmf_element_deinit(&pic->base); + esp_gmf_info_file_deinit(&pic->file_info); + esp_gmf_oal_mutex_unlock(pic->lock); + esp_gmf_oal_mutex_destroy(pic->lock); + return ESP_GMF_ERR_OK; +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/src/esp_gmf_pipeline.c b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/src/esp_gmf_pipeline.c new file mode 100644 index 0000000..810ef12 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/src/esp_gmf_pipeline.c @@ -0,0 +1,724 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#include +#include "esp_log.h" +#include "esp_gmf_oal_mem.h" +#include "esp_gmf_oal_mutex.h" +#include "esp_gmf_element.h" +#include "esp_gmf_pipeline.h" +#include "esp_gmf_node.h" + +#define PIPELINE_PRE_STATE_NONE (0) +#define PIPELINE_PRE_RUN_STATE (1 << 0) +#define PIPELINE_PRE_STOP_STATE (1 << 1) + +static const char *TAG = "ESP_GMF_PIPELINE"; + +static inline void register_close_jobs_to_task(esp_gmf_pipeline_handle_t pipeline) +{ + esp_gmf_node_t *node = (esp_gmf_node_t *)pipeline->head_el; + esp_gmf_element_handle_t next_el = pipeline->head_el; + do { + ESP_LOGD(TAG, "Add close job, p:%p, tsk:%p, [el:%s-%p]", pipeline, pipeline->thread, OBJ_GET_TAG(next_el), next_el); + esp_gmf_element_change_job_mask(next_el, ESP_GMF_ELEMENT_JOB_CLOSE); + char name[ESP_GMF_JOB_LABLE_MAX_LEN] = ""; + esp_gmf_job_str_cat(name, ESP_GMF_JOB_LABLE_MAX_LEN, OBJ_GET_TAG(next_el), ESP_GMF_JOB_STR_CLOSE, strlen(ESP_GMF_JOB_STR_CLOSE)); + esp_gmf_task_register_ready_job(pipeline->thread, name, esp_gmf_element_process_close, ESP_GMF_JOB_TIMES_ONCE, next_el, true); + node = (esp_gmf_node_t *)next_el; + } while ((next_el = (esp_gmf_element_handle_t)esp_gmf_node_for_next(node))); +} + +static inline esp_gmf_err_t register_working_jobs_to_task(esp_gmf_pipeline_handle_t pipeline, esp_gmf_element_handle_t el) +{ + esp_gmf_oal_mutex_lock(pipeline->lock); + uint16_t job_mask = 0; + esp_gmf_event_state_t st = ESP_GMF_EVENT_STATE_NONE; + esp_gmf_element_get_state(el, &st); + if (st != ESP_GMF_EVENT_STATE_INITIALIZED) { + ESP_LOGD(TAG, "The element state is not INITIALIZED, %s, pipe:%p, [el:%s-%p]", esp_gmf_event_get_state_str(st), pipeline, OBJ_GET_TAG(el), el); + esp_gmf_oal_mutex_unlock(pipeline->lock); + return ESP_GMF_ERR_NOT_READY; + } + if (pipeline->thread == NULL) { + ESP_LOGD(TAG, "There is no thread for add jobs, pipe:%p, tsk:%p, [el:%s-%p]", pipeline, pipeline->thread, OBJ_GET_TAG(el), el); + esp_gmf_oal_mutex_unlock(pipeline->lock); + return ESP_GMF_ERR_NOT_SUPPORT; + } + esp_gmf_element_get_job_mask(el, &job_mask); + if ((job_mask & (ESP_GMF_ELEMENT_JOB_OPEN | ESP_GMF_ELEMENT_JOB_PROCESS)) == 0) { + ESP_LOGD(TAG, "Add open and process jobs, p:%p, tsk:%p, [el:%s-%p]", pipeline, pipeline->thread, OBJ_GET_TAG(el), el); + esp_gmf_element_change_job_mask(el, ESP_GMF_ELEMENT_JOB_OPEN); + esp_gmf_element_change_job_mask(el, ESP_GMF_ELEMENT_JOB_PROCESS); + char name[ESP_GMF_JOB_LABLE_MAX_LEN] = ""; + esp_gmf_job_str_cat(name, ESP_GMF_JOB_LABLE_MAX_LEN, OBJ_GET_TAG(el), ESP_GMF_JOB_STR_OPEN, strlen(ESP_GMF_JOB_STR_OPEN)); + esp_gmf_task_register_ready_job(pipeline->thread, name, esp_gmf_element_process_open, ESP_GMF_JOB_TIMES_ONCE, el, false); + esp_gmf_job_str_cat(name, ESP_GMF_JOB_LABLE_MAX_LEN, OBJ_GET_TAG(el), ESP_GMF_JOB_STR_PROCESS, strlen(ESP_GMF_JOB_STR_PROCESS)); + esp_gmf_task_register_ready_job(pipeline->thread, name, esp_gmf_element_process_running, ESP_GMF_JOB_TIMES_INFINITE, el, true); + } + esp_gmf_oal_mutex_unlock(pipeline->lock); + return ESP_GMF_ERR_OK; +} + +static inline void _set_pipe_linked_el_state(esp_gmf_pipeline_handle_t pipeline, esp_gmf_event_state_t event) +{ + esp_gmf_element_handle_t next_el = (esp_gmf_element_handle_t)pipeline->head_el; + while (next_el) { + esp_gmf_event_state_t st = ESP_GMF_EVENT_STATE_NONE; + esp_gmf_element_get_state(next_el, &st); + if (st == ESP_GMF_EVENT_STATE_INITIALIZED) { + if (event == ESP_GMF_EVENT_STATE_OPENING) { + // Never force opening + break; + } + esp_gmf_element_set_state(next_el, event); + } + next_el = (esp_gmf_element_handle_t)esp_gmf_node_for_next((esp_gmf_node_t *)next_el); + } +} + +static esp_gmf_err_t esp_gmf_task_evt(esp_gmf_event_pkt_t *evt, void *ctx) +{ + esp_gmf_pipeline_handle_t pipeline = (esp_gmf_pipeline_handle_t)ctx; + esp_gmf_task_handle_t tsk = evt->from; + ESP_LOGD(TAG, "TASK EVT, p:%p tsk:%s-%p, t:%x, sub:%s, pld:%p, sz:%d", pipeline, + OBJ_GET_TAG(tsk), evt->from, evt->type, esp_gmf_event_get_state_str(evt->sub), evt->payload, evt->payload_size); + int ret_val = ESP_GMF_ERR_OK; + if (evt->type == ESP_GMF_EVT_TYPE_LOADING_JOB) { + switch (evt->sub) { + case ESP_GMF_EVENT_STATE_ERROR: + case ESP_GMF_EVENT_STATE_STOPPED: + case ESP_GMF_EVENT_STATE_FINISHED: { + register_close_jobs_to_task(pipeline); + if (pipeline->in) { + esp_gmf_io_close(pipeline->in); + } + if (pipeline->out) { + esp_gmf_io_close(pipeline->out); + } + } break; + } + } else if (evt->type == ESP_GMF_EVT_TYPE_CHANGE_STATE) { + switch (evt->sub) { + case ESP_GMF_EVENT_STATE_ERROR: + case ESP_GMF_EVENT_STATE_STOPPED: + case ESP_GMF_EVENT_STATE_FINISHED: + _set_pipe_linked_el_state(pipeline, evt->sub); + if (pipeline->user_cb) { + evt->from = pipeline; + pipeline->user_cb(evt, pipeline->user_ctx); + } + pipeline->state = evt->sub; + break; + case ESP_GMF_EVENT_STATE_PAUSED: + _set_pipe_linked_el_state(pipeline, evt->sub); + if (pipeline->user_cb) { + evt->from = pipeline; + pipeline->user_cb(evt, pipeline->user_ctx); + } + pipeline->state = evt->sub; + break; + case ESP_GMF_EVENT_STATE_RUNNING: { + esp_gmf_event_state_t st = 0; + esp_gmf_task_get_state(tsk, &st); + if (st != ESP_GMF_EVENT_STATE_PAUSED) { + evt->sub = ESP_GMF_EVENT_STATE_OPENING; + if (pipeline->in) { + ret_val = esp_gmf_io_open(pipeline->in); + if (ret_val != ESP_GMF_ERR_OK) { + evt->sub = ESP_GMF_EVENT_STATE_ERROR; + ESP_LOGE(TAG, "Failed to open the in port, ret:%d,[%p-%s]", ret_val, tsk, OBJ_GET_TAG(tsk)); + } + } + if ((ret_val == ESP_GMF_ERR_OK) && pipeline->out) { + ret_val = esp_gmf_io_open(pipeline->out); + if (ret_val != ESP_GMF_ERR_OK) { + evt->sub = ESP_GMF_EVENT_STATE_ERROR; + ESP_LOGE(TAG, "Failed to open the out port, ret:%d,[%p-%s]", ret_val, tsk, OBJ_GET_TAG(tsk)); + } + } + } + evt->from = pipeline; + _set_pipe_linked_el_state(pipeline, evt->sub); + if (pipeline->user_cb) { + pipeline->user_cb(evt, pipeline->user_ctx); + } + pipeline->state = evt->sub; + break; + } + default: + break; + } + } else { + ESP_LOGW(TAG, "Not supported event type(%d), [p:%p, tsk:%s-%p]", evt->type, pipeline, OBJ_GET_TAG(tsk), tsk); + } + return ret_val; +} + +static esp_gmf_err_t pipeline_element_events(esp_gmf_event_pkt_t *evt, void *ctx) +{ + esp_gmf_pipeline_handle_t pipeline = (esp_gmf_pipeline_handle_t)ctx; + if (pipeline == NULL) { + return ESP_GMF_ERR_INVALID_ARG; + } + esp_gmf_element_handle_t el = evt->from; + esp_gmf_element_handle_t next_el = (esp_gmf_element_handle_t)pipeline->head_el; + + // 0. Confirm whether the notification target is the first element of another pipeline or + // the element of the current pipeline. + while (next_el) { + if (next_el == el) { + break; + } + next_el = (esp_gmf_element_handle_t)esp_gmf_node_for_next((esp_gmf_node_t *)next_el); + } + if (next_el) { + next_el = (esp_gmf_element_handle_t)esp_gmf_node_for_next((esp_gmf_node_t *)next_el); + } else { + next_el = (esp_gmf_element_handle_t)pipeline->head_el; + } + + ESP_LOGD(TAG, "EL EVT Start, from:%s-%p, p:%p, next_el:%s-%p", OBJ_GET_TAG(el), el, pipeline, OBJ_GET_TAG(next_el), next_el); + + // 1.Notify the element event + while (next_el) { + if ((ESP_GMF_ELEMENT_GET_DEPENDENCY(next_el)) == 0) { + next_el = (esp_gmf_element_handle_t)esp_gmf_node_for_next((esp_gmf_node_t *)next_el); + } else { + break; + } + } + ESP_LOGD(TAG, "EL EVT, p:%p, el:%s-%p, type:%x, sub:%s, payload:%p, size:%d", pipeline, + OBJ_GET_TAG(el), el, evt->type, esp_gmf_event_get_state_str(evt->sub), evt->payload, evt->payload_size); + if (next_el) { + // Notify the element event to next one only + int ret = esp_gmf_element_receive_event(next_el, evt, ctx); + if (ret != ESP_GMF_ERR_OK) { + ESP_LOGE(TAG, "Error notifying event,p:%p, el:%s-%p", pipeline, OBJ_GET_TAG(next_el), next_el); + return ESP_GMF_ERR_FAIL; + } + } else { + // Notify the element event to other pipeline + esp_gmf_event_item_t *item = pipeline->evt_conveyor; + while (item && item->cb) { + item->cb(evt, item->ctx); + item = item->next; + } + } + + // 2.Add the INITIALIZED jobs to working list + next_el = (esp_gmf_element_handle_t)pipeline->head_el; + if (evt->type == ESP_GMF_EVT_TYPE_REPORT_INFO) { + if (next_el) { + esp_gmf_element_handle_t tmp = next_el; + // Get first non-opening element + while (tmp) { + esp_gmf_event_state_t st = ESP_GMF_EVENT_STATE_NONE; + esp_gmf_element_get_state(tmp, &st); + if ((st == ESP_GMF_EVENT_STATE_INITIALIZED) && (tmp != el)) { + break; + } + tmp = (esp_gmf_element_handle_t)esp_gmf_node_for_next((esp_gmf_node_t *)tmp); + } + if (tmp == NULL) { + // The event emit by others pipeline + tmp = pipeline->head_el; + } + next_el = tmp; + while (next_el) { + int ret = register_working_jobs_to_task(pipeline, next_el); + if (ret != ESP_GMF_ERR_OK) { + break; + } + next_el = (esp_gmf_element_handle_t)esp_gmf_node_for_next((esp_gmf_node_t *)next_el); + } + } + if (el == pipeline->last_el && pipeline->user_cb) { + pipeline->user_cb(evt, pipeline->user_ctx); + } + ESP_LOGD(TAG, "ESP_GMF_EVT_TYPE_REPORT_INFO, [p:%p, el:%s-%p]", pipeline, OBJ_GET_TAG(el), el); + } else if (evt->type == ESP_GMF_EVT_TYPE_CHANGE_STATE) { + // Notify the ESP_GMF_EVENT_STATE_RUNNING event to user + if (el == pipeline->last_el && pipeline->user_cb) { + pipeline->user_cb(evt, pipeline->user_ctx); + } + } else { + ESP_LOGE(TAG, "Not supported event type(%d), [p:%p, el:%s-%p]", evt->type, pipeline, OBJ_GET_TAG(el), el); + } + ESP_LOGD(TAG, "EL EVT END, from:%s-%p, p:%p", OBJ_GET_TAG(el), el, pipeline); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_pipeline_create(esp_gmf_pipeline_handle_t *pipeline) +{ + ESP_GMF_NULL_CHECK(TAG, pipeline, return ESP_GMF_ERR_INVALID_ARG); + *pipeline = esp_gmf_oal_calloc(1, sizeof(esp_gmf_pipeline_t)); + ESP_GMF_MEM_CHECK(TAG, *pipeline, return ESP_GMF_ERR_MEMORY_LACK); + (*pipeline)->lock = esp_gmf_oal_mutex_create(); + ESP_GMF_MEM_CHECK(TAG, (*pipeline)->lock, {esp_gmf_oal_free(*pipeline); return ESP_GMF_ERR_MEMORY_LACK;}); + (*pipeline)->evt_acceptor = pipeline_element_events; + (*pipeline)->state = ESP_GMF_EVENT_STATE_NONE; + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_pipeline_destroy(esp_gmf_pipeline_handle_t pipeline) +{ + ESP_GMF_NULL_CHECK(TAG, pipeline, return ESP_GMF_ERR_INVALID_ARG); + ESP_LOGD(TAG, "Pipeline destroying, %p", pipeline); + esp_gmf_oal_mutex_lock(pipeline->lock); + if (pipeline->in) { + esp_gmf_element_unregister_in_port(pipeline->head_el, NULL); + esp_gmf_obj_delete((esp_gmf_obj_handle_t)pipeline->in); + } + if (pipeline->out) { + esp_gmf_element_unregister_out_port(pipeline->last_el, NULL); + esp_gmf_obj_delete((esp_gmf_obj_handle_t)pipeline->out); + } + + esp_gmf_event_item_t *item = pipeline->evt_conveyor; + while (item) { + esp_gmf_event_item_t *tmp = item->next; + esp_gmf_oal_free(item); + item = tmp; + } + esp_gmf_node_clear((esp_gmf_node_t **)&pipeline->head_el, (void *)esp_gmf_obj_delete); + esp_gmf_oal_mutex_unlock(pipeline->lock); + esp_gmf_oal_mutex_destroy(pipeline->lock); + esp_gmf_oal_free(pipeline); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_pipeline_set_io(esp_gmf_pipeline_handle_t pipeline, esp_gmf_io_handle_t io, int dir) +{ + ESP_GMF_NULL_CHECK(TAG, pipeline, return ESP_GMF_ERR_INVALID_ARG); + if (dir == ESP_GMF_IO_DIR_READER) { + pipeline->in = io; + } else if (dir == ESP_GMF_IO_DIR_WRITER) { + pipeline->out = io; + } + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_pipeline_register_el(esp_gmf_pipeline_handle_t pipeline, esp_gmf_element_handle_t el) +{ + ESP_GMF_NULL_CHECK(TAG, pipeline, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_element_set_event_func(el, pipeline_element_events, pipeline); + if (pipeline->head_el == NULL) { + pipeline->head_el = el; + pipeline->last_el = el; + } else { + pipeline->last_el = el; + esp_gmf_element_link_el(pipeline->head_el, el); + } + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_pipeline_list_el(esp_gmf_pipeline_handle_t pipeline) +{ + ESP_GMF_NULL_CHECK(TAG, pipeline, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_node_t *head = (esp_gmf_node_t *)pipeline->head_el; + while (head) { + printf("%p-%s, prev:%p-%s, next:%p-%s \r\n", head, OBJ_GET_TAG(head), head->prev, OBJ_GET_TAG(head->prev), head->next, OBJ_GET_TAG(head->next)); + head = head->next; + } + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_pipeline_set_event(esp_gmf_pipeline_handle_t pipeline, esp_gmf_event_cb cb, void *ctx) +{ + ESP_GMF_NULL_CHECK(TAG, pipeline, return ESP_GMF_ERR_INVALID_ARG); + pipeline->user_cb = cb; + pipeline->user_ctx = ctx; + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_pipeline_bind_task(esp_gmf_pipeline_handle_t pipeline, esp_gmf_task_handle_t task) +{ + ESP_GMF_NULL_CHECK(TAG, pipeline, return ESP_GMF_ERR_INVALID_ARG); + if (task == NULL) { + // Allow clear bind task, so that can recreate task and bind + pipeline->thread = NULL; + return ESP_GMF_ERR_OK; + } + ESP_GMF_NULL_CHECK(TAG, task, return ESP_GMF_ERR_INVALID_ARG); + pipeline->thread = task; + esp_gmf_task_set_event_func(task, esp_gmf_task_evt, pipeline); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_pipeline_loading_jobs(esp_gmf_pipeline_handle_t pipeline) +{ + ESP_GMF_NULL_CHECK(TAG, pipeline, return ESP_GMF_ERR_INVALID_ARG); + if (pipeline->thread == NULL) { + ESP_LOGE(TAG, "No task for pipeline, %p", pipeline); + return ESP_GMF_ERR_INVALID_ARG; + } + esp_gmf_node_t *node = (esp_gmf_node_t *)pipeline->head_el; + esp_gmf_element_handle_t el = pipeline->head_el; + do { + int ret = register_working_jobs_to_task(pipeline, el); + if (ret != ESP_GMF_ERR_OK) { + // Not load all following element job if current is not ready yet + if (el == pipeline->head_el) { + ESP_LOGW(TAG, "Element[%s-%p] not ready to register job, ret:0x%x", OBJ_GET_TAG(el), el, ret); + } + break; + } + node = (esp_gmf_node_t *)el; + } while ((el = (esp_gmf_element_handle_t)esp_gmf_node_for_next(node))); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_pipeline_set_in_uri(esp_gmf_pipeline_handle_t pipeline, const char *uri) +{ + ESP_GMF_NULL_CHECK(TAG, pipeline, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, uri, return ESP_GMF_ERR_INVALID_ARG); + int ret = ESP_GMF_ERR_OK; + if (pipeline->in) { + ret = esp_gmf_io_set_uri(pipeline->in, uri); + } + return ret; +} + +esp_gmf_err_t esp_gmf_pipeline_set_out_uri(esp_gmf_pipeline_handle_t pipeline, const char *uri) +{ + ESP_GMF_NULL_CHECK(TAG, pipeline, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, uri, return ESP_GMF_ERR_INVALID_ARG); + int ret = ESP_GMF_ERR_OK; + if (pipeline->out) { + ret = esp_gmf_io_set_uri(pipeline->out, uri); + } + return ret; +} + +esp_gmf_err_t esp_gmf_pipeline_reg_event_recipient(esp_gmf_pipeline_handle_t connector, esp_gmf_pipeline_handle_t connectee) +{ + ESP_GMF_NULL_CHECK(TAG, connector, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_event_item_t *item = connector->evt_conveyor; + esp_gmf_event_item_t *new_item = esp_gmf_oal_calloc(1, sizeof(esp_gmf_event_item_t)); + ESP_GMF_MEM_CHECK(TAG, new_item, return ESP_GMF_ERR_MEMORY_LACK); + if (item == NULL) { + new_item->cb = connectee->evt_acceptor; + new_item->ctx = connectee; + new_item->next = NULL; + connector->evt_conveyor = new_item; + } else { + while (item->next) { + item = item->next; + } + new_item->cb = connectee->evt_acceptor; + new_item->ctx = connectee; + new_item->next = NULL; + item->next = new_item; + } + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_pipeline_connect_pipe(esp_gmf_pipeline_handle_t connector, const char *connector_name, esp_gmf_port_handle_t connector_port, + esp_gmf_pipeline_handle_t connectee, const char *connectee_name, esp_gmf_port_handle_t connectee_port) +{ + ESP_GMF_NULL_CHECK(TAG, connector, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, connector_name, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, connectee, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, connectee_port, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, connectee_name, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_err_t ret; + if (connector_port) { + esp_gmf_element_handle_t connector_el = NULL; + ret = esp_gmf_pipeline_get_el_by_name(connector, connector_name, &connector_el); + ESP_GMF_RET_ON_ERROR(TAG, ret, return ret, "The connector[%s] is not found", connector_name); + + ret = esp_gmf_element_register_out_port(connector_el, connector_port); + ESP_GMF_RET_ON_ERROR(TAG, ret, return ret, "Register connector out port failed, [%s]", connectee_name); + } + esp_gmf_element_handle_t connectee_el = NULL; + ret = esp_gmf_pipeline_get_el_by_name(connectee, connectee_name, &connectee_el); + ESP_GMF_RET_ON_ERROR(TAG, ret, return ret, "The connectee[%s] is not found", connectee_name); + + ret = esp_gmf_element_register_in_port(connectee_el, connectee_port); + ESP_GMF_RET_ON_ERROR(TAG, ret, return ret, "Register connectee in port failed, [%s]", connectee_name); + + return esp_gmf_pipeline_reg_event_recipient(connector, connectee); +} + +esp_gmf_err_t esp_gmf_pipeline_set_prev_run_cb(esp_gmf_pipeline_handle_t pipeline, esp_gmf_pipeline_prev_act prev_run, void *ctx) +{ + ESP_GMF_NULL_CHECK(TAG, pipeline, return ESP_GMF_ERR_INVALID_ARG); + ESP_LOGD(TAG, "Set prev run:%p pipeline:%p", prev_run, pipeline); + pipeline->prev_run = prev_run; + pipeline->prev_run_ctx = ctx; + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_pipeline_set_prev_stop_cb(esp_gmf_pipeline_handle_t pipeline, esp_gmf_pipeline_prev_act prev_stop, void *ctx) +{ + ESP_GMF_NULL_CHECK(TAG, pipeline, return ESP_GMF_ERR_INVALID_ARG); + ESP_LOGD(TAG, "Set prev stop:%p pipeline:%p", prev_stop, pipeline); + pipeline->prev_stop = prev_stop; + pipeline->prev_stop_ctx = ctx; + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_pipeline_prev_run(esp_gmf_pipeline_handle_t pipeline) +{ + ESP_GMF_NULL_CHECK(TAG, pipeline, return ESP_GMF_ERR_INVALID_ARG); + if (pipeline->prev_run == NULL) { + return ESP_GMF_ERR_OK; + } + if (pipeline->prev_state & PIPELINE_PRE_RUN_STATE) { + return ESP_GMF_ERR_OK; + } + pipeline->prev_run(pipeline->prev_run_ctx); + pipeline->prev_state |= PIPELINE_PRE_RUN_STATE; + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_pipeline_prev_stop(esp_gmf_pipeline_handle_t pipeline) +{ + ESP_GMF_NULL_CHECK(TAG, pipeline, return ESP_GMF_ERR_INVALID_ARG); + if (pipeline->prev_stop == NULL) { + return ESP_GMF_ERR_OK; + } + if (pipeline->prev_state & PIPELINE_PRE_STOP_STATE) { + return ESP_GMF_ERR_OK; + } + pipeline->prev_stop(pipeline->prev_stop_ctx); + pipeline->prev_state |= PIPELINE_PRE_STOP_STATE; + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_pipeline_run(esp_gmf_pipeline_handle_t pipeline) +{ + ESP_GMF_NULL_CHECK(TAG, pipeline, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_err_t ret = esp_gmf_pipeline_prev_run(pipeline); + ESP_GMF_RET_ON_ERROR(TAG, ret, return ret, "Fail to prev run for %p", pipeline); + return esp_gmf_task_run(pipeline->thread); +} + +esp_gmf_err_t esp_gmf_pipeline_stop(esp_gmf_pipeline_handle_t pipeline) +{ + ESP_GMF_NULL_CHECK(TAG, pipeline, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_err_t ret = ESP_GMF_ERR_OK; + ESP_LOGD(TAG, "Pipeline going to stop, %p", pipeline); + ret = esp_gmf_pipeline_prev_stop(pipeline); + ESP_GMF_RET_ON_ERROR(TAG, ret, return ret, "Fail to prev stop for %p", pipeline); + ret = esp_gmf_task_stop(pipeline->thread); + pipeline->prev_state = PIPELINE_PRE_STATE_NONE; + return ret; +} + +esp_gmf_err_t esp_gmf_pipeline_pause(esp_gmf_pipeline_handle_t pipeline) +{ + ESP_GMF_NULL_CHECK(TAG, pipeline, return ESP_GMF_ERR_INVALID_ARG); + int ret = ESP_GMF_ERR_OK; + ret = esp_gmf_task_pause(pipeline->thread); + return ret; +} + +esp_gmf_err_t esp_gmf_pipeline_resume(esp_gmf_pipeline_handle_t pipeline) +{ + ESP_GMF_NULL_CHECK(TAG, pipeline, return ESP_GMF_ERR_INVALID_ARG); + int ret = ESP_GMF_ERR_OK; + ret = esp_gmf_task_resume(pipeline->thread); + return ret; +} + +esp_gmf_err_t esp_gmf_pipeline_reset(esp_gmf_pipeline_handle_t pipeline) +{ + ESP_GMF_NULL_CHECK(TAG, pipeline, return ESP_GMF_ERR_INVALID_ARG); + int ret = ESP_GMF_ERR_OK; + pipeline->state = ESP_GMF_EVENT_STATE_NONE; + ret = esp_gmf_task_reset(pipeline->thread); + if (pipeline->in) { + esp_gmf_io_reset(pipeline->in); + } + if (pipeline->out) { + esp_gmf_io_reset(pipeline->out); + } + esp_gmf_element_handle_t next_el = pipeline->head_el; + do { + esp_gmf_element_reset_state(next_el); + esp_gmf_element_reset_port(next_el); + esp_gmf_element_set_job_mask(next_el, 0); + ESP_LOGD(TAG, "Pipeline reset, %p, %p-%s", pipeline, next_el, OBJ_GET_TAG(next_el)); + } while ((next_el = (esp_gmf_element_handle_t)esp_gmf_node_for_next(next_el))); + return ret; +} + +esp_gmf_err_t esp_gmf_pipeline_seek(esp_gmf_pipeline_handle_t pipeline, uint64_t pos) +{ + ESP_GMF_NULL_CHECK(TAG, pipeline, return ESP_GMF_ERR_INVALID_ARG); + if ((pipeline->state != ESP_GMF_EVENT_STATE_PAUSED) + && (pipeline->state != ESP_GMF_EVENT_STATE_STOPPED) + && (pipeline->state != ESP_GMF_EVENT_STATE_FINISHED)) { + ESP_LOGE(TAG, "The pipeline status is %s, can't be seek.", esp_gmf_event_get_state_str(pipeline->state)); + return ESP_GMF_ERR_INVALID_STATE; + } + if (pipeline->in == NULL) { + ESP_LOGE(TAG, "This pipeline no in port, can't be seek"); + return ESP_GMF_ERR_NOT_SUPPORT; + } + int ret = ESP_GMF_ERR_OK; + ret = esp_gmf_io_seek(pipeline->in, pos); + ESP_LOGD(TAG, "Seek to %lld, ret:%d", pos, ret); + return ret; +} + +esp_gmf_err_t esp_gmf_pipeline_get_linked_pipeline(esp_gmf_pipeline_handle_t connector, const void **link, esp_gmf_pipeline_handle_t *connectee) +{ + ESP_GMF_NULL_CHECK(TAG, connector, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, link, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, connectee, return ESP_GMF_ERR_INVALID_ARG); + + esp_gmf_event_item_t *item; + if (*link == NULL) { + item = connector->evt_conveyor; + } else { + item = (esp_gmf_event_item_t *)*link; + item = item->next; + } + if (item) { + *connectee = (esp_gmf_pipeline_handle_t)item->ctx; + *link = item; + return ESP_GMF_ERR_OK; + } + *connectee = NULL; + return ESP_GMF_ERR_NOT_FOUND; +} + +esp_gmf_err_t esp_gmf_pipeline_get_in(esp_gmf_pipeline_handle_t pipeline, esp_gmf_io_handle_t *io_handle) +{ + ESP_GMF_NULL_CHECK(TAG, pipeline, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, io_handle, return ESP_GMF_ERR_INVALID_ARG); + *io_handle = pipeline->in; + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_pipeline_get_out(esp_gmf_pipeline_handle_t pipeline, esp_gmf_io_handle_t *io_handle) +{ + ESP_GMF_NULL_CHECK(TAG, pipeline, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, io_handle, return ESP_GMF_ERR_INVALID_ARG); + *io_handle = pipeline->out; + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_pipeline_replace_in(esp_gmf_pipeline_handle_t pipeline, esp_gmf_io_handle_t new) +{ + ESP_GMF_NULL_CHECK(TAG, pipeline, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, new, return ESP_GMF_ERR_INVALID_ARG); + if ((pipeline->state == ESP_GMF_EVENT_STATE_RUNNING) + || (pipeline->state == ESP_GMF_EVENT_STATE_INITIALIZED)) { + ESP_LOGE(TAG, "Can't replace in port, st:%s, new:%p", + esp_gmf_event_get_state_str(pipeline->state), new); + return ESP_GMF_ERR_INVALID_STATE; + } + pipeline->in = new; + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_pipeline_replace_out(esp_gmf_pipeline_handle_t pipeline, esp_gmf_io_handle_t new) +{ + ESP_GMF_NULL_CHECK(TAG, pipeline, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, new, return ESP_GMF_ERR_INVALID_ARG); + if ((pipeline->state != ESP_GMF_EVENT_STATE_RUNNING) + || (pipeline->state != ESP_GMF_EVENT_STATE_INITIALIZED)) { + ESP_LOGE(TAG, "Can't replace out port, st:%s, new:%p", + esp_gmf_event_get_state_str(pipeline->state), new); + return ESP_GMF_ERR_INVALID_STATE; + } + pipeline->out = new; + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_pipeline_get_head_el(esp_gmf_pipeline_handle_t pipeline, esp_gmf_element_handle_t *head) +{ + ESP_GMF_NULL_CHECK(TAG, pipeline, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, head, return ESP_GMF_ERR_INVALID_ARG); + *head = pipeline->head_el; + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_pipeline_get_next_el(esp_gmf_pipeline_handle_t pipeline, esp_gmf_element_handle_t head, esp_gmf_element_handle_t *next) +{ + ESP_GMF_NULL_CHECK(TAG, pipeline, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, head, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, next, return ESP_GMF_ERR_INVALID_ARG); + *next = (esp_gmf_element_handle_t)esp_gmf_node_for_next((esp_gmf_node_t *)head); + return *next ? ESP_GMF_ERR_OK : ESP_GMF_ERR_NOT_FOUND; +} + +esp_gmf_err_t esp_gmf_pipeline_get_el_by_name(esp_gmf_pipeline_handle_t pipeline, const char *tag, esp_gmf_element_handle_t *out_handle) +{ + ESP_GMF_NULL_CHECK(TAG, pipeline, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, tag, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, out_handle, return ESP_GMF_ERR_INVALID_ARG); + + esp_gmf_node_t *node = (esp_gmf_node_t *)pipeline->head_el; + esp_gmf_element_handle_t next_el = node; + do { + if (strncasecmp(tag, OBJ_GET_TAG(next_el), ESP_GMF_TAG_MAX_LEN) == 0) { + ESP_LOGD(TAG, "Find EL %s-%p", OBJ_GET_TAG(next_el), next_el); + *out_handle = next_el; + return ESP_GMF_ERR_OK; + } + node = (esp_gmf_node_t *)next_el; + } while ((next_el = (esp_gmf_element_handle_t)esp_gmf_node_for_next(node))); + return ESP_GMF_ERR_NOT_FOUND; +} + +esp_gmf_err_t esp_gmf_pipeline_reg_el_port(esp_gmf_pipeline_handle_t pipeline, + const char *tag, esp_gmf_io_dir_t io_dir, esp_gmf_io_handle_t port) +{ + ESP_GMF_NULL_CHECK(TAG, pipeline, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, tag, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, port, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_element_handle_t target_el = NULL; + int ret = esp_gmf_pipeline_get_el_by_name(pipeline, tag, &target_el); + ESP_GMF_RET_ON_ERROR(TAG, ret, return ret, "Find the element error, p:%p, tag:%s, ret:%d", pipeline, tag, ret); + if (io_dir == ESP_GMF_IO_DIR_READER) { + ret = esp_gmf_element_register_in_port((esp_gmf_element_handle_t)target_el, port); + } else if (io_dir == ESP_GMF_IO_DIR_WRITER) { + ret = esp_gmf_element_register_out_port((esp_gmf_element_handle_t)target_el, port); + } else { + ESP_LOGE(TAG, "Unsupported IO type,%d, [%p]", io_dir, pipeline); + return ESP_GMF_ERR_NOT_SUPPORT; + } + return ret; +} + +esp_gmf_err_t esp_gmf_pipeline_report_info(esp_gmf_pipeline_handle_t pipeline, esp_gmf_info_type_t info_type, void *value, int len) +{ + ESP_GMF_NULL_CHECK(TAG, pipeline, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_element_t *el = (esp_gmf_element_t *)pipeline->head_el; + esp_gmf_event_pkt_t evt = { + .from = NULL, + .type = ESP_GMF_EVT_TYPE_REPORT_INFO, + .sub = info_type, + .payload = value, + .payload_size = len, + }; + if (el->event_func == NULL) { + ESP_LOGW(TAG, "Report info failed[pipe:%p], due to [el:%p-%s] no registered callback", pipeline, el, OBJ_GET_TAG(el)); + return ESP_GMF_ERR_FAIL; + } + return el->event_func(&evt, el->ctx); +} + +esp_gmf_err_t esp_gmf_pipeline_show(esp_gmf_pipeline_handle_t pipeline) +{ + ESP_GMF_NULL_CHECK(TAG, pipeline, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_node_t *node = (esp_gmf_node_t *)pipeline->head_el; + esp_gmf_element_handle_t next_el = node; + ESP_LOGI(TAG, "SHOW PIPELINE MEMBERS:"); + ESP_LOGI(TAG, "The IN port, [%p-%s]", pipeline->in, OBJ_GET_TAG(pipeline->in)); + do { + ESP_LOGI(TAG, "The EL, [%p-%s]", next_el, OBJ_GET_TAG(next_el)); + node = (esp_gmf_node_t *)next_el; + } while ((next_el = (esp_gmf_element_handle_t)esp_gmf_node_for_next(node))); + ESP_LOGI(TAG, "The OUT port, [%p-%s]", pipeline->out, OBJ_GET_TAG(pipeline->out)); + return ESP_GMF_ERR_OK; +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/src/esp_gmf_pool.c b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/src/esp_gmf_pool.c new file mode 100644 index 0000000..104c2e7 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/src/esp_gmf_pool.c @@ -0,0 +1,332 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#include +#include "sys/queue.h" +#include "esp_log.h" +#include "esp_gmf_element.h" +#include "esp_gmf_pipeline.h" +#include "esp_gmf_data_bus.h" +#include "esp_gmf_new_databus.h" +#include "esp_gmf_pool.h" +#include "esp_gmf_io.h" +#include "esp_gmf_oal_mem.h" +#include "esp_gmf_err.h" + +static const char *TAG = "ESP_GMF_POOL"; + +typedef struct gmp_pool_io_item { + STAILQ_ENTRY(gmp_pool_io_item) next; + esp_gmf_io_handle_t instance; +} esp_gmf_io_item_t; +typedef STAILQ_HEAD(gmp_io_list, gmp_pool_io_item) gmp_io_list_t; + +typedef struct esp_gmf_element_item { + STAILQ_ENTRY(esp_gmf_element_item) next; + esp_gmf_element_handle_t instance; +} esp_gmf_element_item_t; +typedef STAILQ_HEAD(esp_gmf_element_list, esp_gmf_element_item) esp_gmf_element_list_t; + +struct esp_gmf_pool { + esp_gmf_element_list_t el_list; + gmp_io_list_t io_list; +} esp_gmf_pool_t; + +static inline esp_gmf_element_item_t *__get_element_item_by_tag(esp_gmf_pool_handle_t handle, const char *tag) +{ + esp_gmf_element_item_t *item, *tmp; + STAILQ_FOREACH_SAFE(item, &handle->el_list, next, tmp) { + char *el_tag = (char *)"NULL"; + esp_gmf_obj_get_tag((esp_gmf_obj_handle_t)item->instance, &el_tag); + ESP_LOGD(TAG, "Get EL items:%p-%s", item->instance, el_tag); + if (el_tag && strcasecmp(el_tag, tag) == 0) { + return item; + } + } + return NULL; +} + +static inline esp_gmf_io_item_t *_get_io_item_by_tag(esp_gmf_pool_handle_t handle, const char *tag, esp_gmf_io_dir_t dir) +{ + esp_gmf_io_item_t *item, *tmp; + STAILQ_FOREACH_SAFE(item, &handle->io_list, next, tmp) { + char *io_tag = (char *)"NULL"; + esp_gmf_obj_get_tag((esp_gmf_obj_handle_t)item->instance, &io_tag); + ESP_LOGD(TAG, "Get IO items: %p-%s, dir:%d", item->instance, io_tag, ((esp_gmf_io_t *)item->instance)->dir); + if (io_tag && (strcasecmp(io_tag, tag) == 0) && (((esp_gmf_io_t *)item->instance)->dir == dir)) { + return item; + } + } + return NULL; +} + +esp_gmf_err_t esp_gmf_pool_init(esp_gmf_pool_handle_t *handle) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_pool_handle_t ph = esp_gmf_oal_calloc(1, sizeof(struct esp_gmf_pool)); + ESP_GMF_MEM_CHECK(TAG, ph, return ESP_GMF_ERR_MEMORY_LACK); + STAILQ_INIT(&ph->el_list); + STAILQ_INIT(&ph->io_list); + *handle = ph; + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_pool_deinit(esp_gmf_pool_handle_t handle) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + /// Unregister all of element and io objects + esp_gmf_element_item_t *el_item, *tmp; + STAILQ_FOREACH_SAFE(el_item, &handle->el_list, next, tmp) { + STAILQ_REMOVE(&handle->el_list, el_item, esp_gmf_element_item, next); + ESP_LOGD(TAG, "%s, el:[%p-%s]", __FUNCTION__, el_item->instance, OBJ_GET_TAG(el_item->instance)); + esp_gmf_obj_delete(el_item->instance); + esp_gmf_oal_free(el_item); + } + esp_gmf_io_item_t *io_item, *tmp1; + STAILQ_FOREACH_SAFE(io_item, &handle->io_list, next, tmp1) { + STAILQ_REMOVE(&handle->io_list, io_item, gmp_pool_io_item, next); + ESP_LOGD(TAG, "%s, io:[%p-%s]", __FUNCTION__, io_item->instance, OBJ_GET_TAG(io_item->instance)); + esp_gmf_obj_delete(io_item->instance); + esp_gmf_oal_free(io_item); + } + esp_gmf_oal_free(handle); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_pool_register_element(esp_gmf_pool_handle_t handle, esp_gmf_element_handle_t el, const char *tag) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, el, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_element_item_t *el_item = (esp_gmf_element_item_t *)esp_gmf_oal_calloc(1, sizeof(esp_gmf_element_item_t)); + ESP_GMF_MEM_CHECK(TAG, el_item, return ESP_GMF_ERR_MEMORY_LACK); + if (tag) { + int ret = esp_gmf_obj_set_tag((esp_gmf_obj_t *)el, tag); + ESP_GMF_RET_ON_ERROR(TAG, ret, return ret, "Set EL tag failed, obj:%p, tag:%s", el, OBJ_GET_TAG((esp_gmf_obj_t *)el)); + } + el_item->instance = el; + STAILQ_INSERT_TAIL(&handle->el_list, el_item, next); + ESP_LOGD(TAG, "REG el:[%p-%s], item:%p", el, OBJ_GET_TAG(el), el_item); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_pool_register_element_at_head(esp_gmf_pool_handle_t handle, esp_gmf_element_handle_t el, const char *tag) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, el, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_element_item_t *el_item = (esp_gmf_element_item_t *)esp_gmf_oal_calloc(1, sizeof(esp_gmf_element_item_t)); + ESP_GMF_MEM_CHECK(TAG, el_item, return ESP_GMF_ERR_MEMORY_LACK); + if (tag) { + int ret = esp_gmf_obj_set_tag((esp_gmf_obj_t *)el, tag); + ESP_GMF_RET_ON_ERROR(TAG, ret, return ret, "Set EL tag failed, obj:%p, tag:%s", el, OBJ_GET_TAG((esp_gmf_obj_t *)el)); + } + el_item->instance = el; + STAILQ_INSERT_HEAD(&handle->el_list, el_item, next); + ESP_LOGD(TAG, "REG at head el:[%p-%s], item:%p", el, OBJ_GET_TAG(el), el_item); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_pool_register_io(esp_gmf_pool_handle_t handle, esp_gmf_io_handle_t io, const char *tag) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, io, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_io_item_t *io_item = (esp_gmf_io_item_t *)esp_gmf_oal_calloc(1, sizeof(esp_gmf_io_item_t)); + ESP_GMF_MEM_CHECK(TAG, io_item, return ESP_GMF_ERR_MEMORY_LACK); + if (tag) { + int ret = esp_gmf_obj_set_tag((esp_gmf_obj_t *)io, tag); + ESP_GMF_RET_ON_ERROR(TAG, ret, return ret, "Set IO tag failed, IO:%p, tag:%s", io, OBJ_GET_TAG((esp_gmf_obj_t *)io)); + } + io_item->instance = io; + STAILQ_INSERT_TAIL(&handle->io_list, io_item, next); + ESP_LOGD(TAG, "REG IO:[%p-%s], item:%p, pool:%p", io, OBJ_GET_TAG(io), io_item, handle); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_pool_iterate_element(esp_gmf_pool_handle_t handle, const void **iterator, esp_gmf_element_handle_t *el) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, iterator, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, el, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_element_item_t *cur = (esp_gmf_element_item_t *) *iterator; + + if (cur == NULL) { + cur = STAILQ_FIRST(&handle->el_list); + } else { + cur = STAILQ_NEXT(cur, next); + } + if (cur) { + *el = cur->instance; + *iterator = cur; + return ESP_GMF_ERR_OK; + } + *el = NULL; + return ESP_GMF_ERR_NOT_FOUND; +} + +esp_gmf_err_t esp_gmf_pool_new_io(esp_gmf_pool_handle_t handle, const char *name, esp_gmf_io_dir_t dir, esp_gmf_io_handle_t *new_io) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, name, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, new_io, return ESP_GMF_ERR_INVALID_ARG); + if (dir < ESP_GMF_IO_DIR_READER) { + ESP_LOGD(TAG, "Invalid direction, dir:%x, name:%s, pool:%p", dir, name, handle); + return ESP_GMF_ERR_INVALID_ARG; + } + esp_gmf_io_item_t *io_item = _get_io_item_by_tag(handle, name, dir); + if (io_item == NULL) { + ESP_LOGE(TAG, "Not found %s port, name:%s, pool:%p", dir > ESP_GMF_IO_DIR_READER ? "WRITER" : "READER", name, handle); + return ESP_GMF_ERR_NOT_FOUND; + } + esp_gmf_obj_handle_t new_io_obj = NULL; + int ret = esp_gmf_obj_dupl((esp_gmf_obj_handle_t)io_item->instance, (void *)&new_io_obj); + ESP_GMF_RET_ON_ERROR(TAG, ret, return ESP_GMF_ERR_MEMORY_LACK, "Failed to create %s IO object, name:%s, [%p-%s]", + dir > ESP_GMF_IO_DIR_READER ? "WRITER" : "READER", name, io_item->instance, OBJ_GET_TAG(io_item->instance)); + *new_io = new_io_obj; + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_pool_new_element(esp_gmf_pool_handle_t handle, const char *el_name, esp_gmf_element_handle_t *new_el) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, el_name, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, new_el, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_element_item_t *el_item = __get_element_item_by_tag(handle, el_name); + if (el_item == NULL) { + ESP_LOGE(TAG, "Can't found the element[%s]", el_name); + return ESP_GMF_ERR_NOT_FOUND; + } + esp_gmf_obj_handle_t new_el_obj = NULL; + int ret = esp_gmf_obj_dupl(el_item->instance, (void *)&new_el_obj); + ESP_GMF_RET_ON_ERROR(TAG, ret, return ESP_GMF_ERR_MEMORY_LACK, "Failed to create element object, [%p-%s]", el_item->instance, OBJ_GET_TAG(el_item->instance)); + *new_el = new_el_obj; + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_pool_new_pipeline(esp_gmf_pool_handle_t handle, const char *in_name, + const char *el_name[], int num_of_el_name, + const char *out_name, esp_gmf_pipeline_handle_t *pipeline) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, el_name, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, pipeline, return ESP_GMF_ERR_INVALID_ARG); + if (num_of_el_name < 1) { + ESP_LOGE(TAG, "The number of name is too short, %d", num_of_el_name); + return ESP_GMF_ERR_INVALID_ARG; + } + esp_gmf_pipeline_t *pl = NULL; + esp_gmf_pipeline_create(&pl); + ESP_GMF_MEM_CHECK(TAG, pl, return ESP_GMF_ERR_MEMORY_LACK); + *pipeline = pl; + int el_cnt = 0; + esp_gmf_obj_handle_t new_last_el_obj = NULL; + esp_gmf_obj_handle_t new_prev_el_obj = NULL; + esp_gmf_obj_handle_t new_first_el_obj = NULL; + int ret = ESP_GMF_ERR_OK; + // Link the elements + for (int i = 0; i < num_of_el_name; ++i) { + esp_gmf_element_handle_t new_el = NULL; + ret = esp_gmf_pool_new_element(handle, el_name[i], &new_el); + if (ret != ESP_GMF_ERR_OK) { + goto NEW_PIPE_FAIL; + } + ESP_LOGD(TAG, "TO link elements, [%p-%s]", new_el, OBJ_GET_TAG(new_el)); + el_cnt++; + if (el_cnt == 1) { + new_first_el_obj = new_el; + new_prev_el_obj = new_el; + new_last_el_obj = new_el; + } + if ((num_of_el_name > 1) && (el_cnt > 1)) { + esp_gmf_port_handle_t out_port = NULL; + esp_gmf_port_handle_t in_port = NULL; + out_port = NEW_ESP_GMF_PORT_OUT_BLOCK(NULL, NULL, NULL, NULL, (ESP_GMF_ELEMENT_GET(new_first_el_obj)->out_attr.data_size), ESP_GMF_MAX_DELAY); + ESP_GMF_NULL_CHECK(TAG, out_port, {ret = ESP_GMF_ERR_MEMORY_LACK; goto NEW_PIPE_FAIL;}); + in_port = NEW_ESP_GMF_PORT_IN_BLOCK(NULL, NULL, NULL, NULL, (ESP_GMF_ELEMENT_GET(new_first_el_obj)->in_attr.data_size), ESP_GMF_MAX_DELAY); + ESP_GMF_NULL_CHECK(TAG, in_port, {ret = ESP_GMF_ERR_MEMORY_LACK; goto NEW_PIPE_FAIL;}); + esp_gmf_element_register_out_port((esp_gmf_element_handle_t)new_prev_el_obj, out_port); + esp_gmf_element_register_in_port(new_el, in_port); + new_prev_el_obj = new_el; + new_last_el_obj = new_el; + } + esp_gmf_pipeline_register_el(pl, new_el); + } + // Found the input io if already registered + if (in_name) { + esp_gmf_io_handle_t new_in = NULL; + ret = esp_gmf_pool_new_io(handle, in_name, ESP_GMF_IO_DIR_READER, &new_in); + if (ret != ESP_GMF_ERR_OK) { + goto NEW_PIPE_FAIL; + } + esp_gmf_pipeline_set_io(pl, new_in, ESP_GMF_IO_DIR_READER); + + esp_gmf_io_type_t io_type = 0; + esp_gmf_io_get_type(new_in, &io_type); + esp_gmf_port_handle_t in_port = NULL; + + if (io_type == ESP_GMF_IO_TYPE_BYTE) { + in_port = NEW_ESP_GMF_PORT_IN_BYTE(esp_gmf_io_acquire_read, esp_gmf_io_release_read, NULL, new_in, + (ESP_GMF_ELEMENT_GET(new_first_el_obj)->in_attr.data_size), ESP_GMF_MAX_DELAY); + } else if (io_type == ESP_GMF_IO_TYPE_BLOCK) { + in_port = NEW_ESP_GMF_PORT_IN_BLOCK(esp_gmf_io_acquire_read, esp_gmf_io_release_read, NULL, new_in, + (ESP_GMF_ELEMENT_GET(new_first_el_obj)->in_attr.data_size), ESP_GMF_MAX_DELAY); + } else { + ESP_LOGE(TAG, "The IN type is incorrect,%d, [%p-%s]", io_type, new_in, OBJ_GET_TAG(new_in)); + ret = ESP_GMF_ERR_NOT_SUPPORT; + goto NEW_PIPE_FAIL; + } + ESP_GMF_NULL_CHECK(TAG, in_port, {ret = ESP_GMF_ERR_MEMORY_LACK; goto NEW_PIPE_FAIL;}); + esp_gmf_element_register_in_port((esp_gmf_element_handle_t)new_first_el_obj, in_port); + ESP_LOGD(TAG, "TO link IN port, [%p-%s],new:%p", new_in, OBJ_GET_TAG(new_in), in_port); + } + + // Found the output io if already registered + if (out_name) { + esp_gmf_io_handle_t new_out = NULL; + ret = esp_gmf_pool_new_io(handle, out_name, ESP_GMF_IO_DIR_WRITER, &new_out); + if (ret != ESP_GMF_ERR_OK) { + goto NEW_PIPE_FAIL; + } + esp_gmf_pipeline_set_io(pl, new_out, ESP_GMF_IO_DIR_WRITER); + + esp_gmf_io_type_t io_type = 0; + esp_gmf_io_get_type(new_out, &io_type); + esp_gmf_port_handle_t out_port = NULL; + + if (io_type == ESP_GMF_IO_TYPE_BYTE) { + out_port = NEW_ESP_GMF_PORT_OUT_BYTE(esp_gmf_io_acquire_write, esp_gmf_io_release_write, NULL, new_out, + (ESP_GMF_ELEMENT_GET(new_last_el_obj)->out_attr.data_size), ESP_GMF_MAX_DELAY); + } else if (io_type == ESP_GMF_IO_TYPE_BLOCK) { + out_port = NEW_ESP_GMF_PORT_OUT_BLOCK(esp_gmf_io_acquire_write, esp_gmf_io_release_write, NULL, new_out, + (ESP_GMF_ELEMENT_GET(new_last_el_obj)->out_attr.data_size), ESP_GMF_MAX_DELAY); + } else { + ESP_LOGE(TAG, "The OUT type is incorrect, %d, [%p-%s]", io_type, new_out, OBJ_GET_TAG(new_out)); + ret = ESP_GMF_ERR_NOT_SUPPORT; + goto NEW_PIPE_FAIL; + } + ESP_GMF_NULL_CHECK(TAG, out_port, {ret = ESP_GMF_ERR_MEMORY_LACK; goto NEW_PIPE_FAIL;}); + esp_gmf_element_register_out_port((esp_gmf_element_handle_t)new_last_el_obj, out_port); + ESP_LOGD(TAG, "TO link OUT port, [%p-%s], new:%p, sz:%d", new_out, OBJ_GET_TAG(new_out), out_port, (ESP_GMF_ELEMENT_GET(new_last_el_obj)->out_attr.data_size)); + } + return ESP_GMF_ERR_OK; + +NEW_PIPE_FAIL: + esp_gmf_pipeline_destroy(pl); + *pipeline = NULL; + return ret; +} + +void esp_gmf_pool_show_lists(esp_gmf_pool_handle_t handle, int line, const char *func) +{ + esp_gmf_io_item_t *item, *tmp; + ESP_LOGI(TAG, "Registered items on pool:%p, %s-%d", handle, func, line); + STAILQ_FOREACH_SAFE(item, &handle->io_list, next, tmp) { + ESP_LOGI(TAG, "IO, Item:%p, H:%p, TAG:%s", item, item->instance, OBJ_GET_TAG(item->instance)); + } + esp_gmf_element_item_t *el_item, *el_tmp; + STAILQ_FOREACH_SAFE(el_item, &handle->el_list, next, el_tmp) { + ESP_LOGI(TAG, "EL, Item:%p, H:%p, TAG:%s", el_item, el_item->instance, OBJ_GET_TAG(el_item->instance)); + } +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/src/esp_gmf_port.c b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/src/esp_gmf_port.c new file mode 100644 index 0000000..bb669f6 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/src/esp_gmf_port.c @@ -0,0 +1,363 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#include +#include +#include "esp_log.h" +#include "esp_gmf_oal_mem.h" +#include "esp_gmf_port.h" +#include "esp_gmf_element.h" +#include "esp_gmf_node.h" + +static const char *TAG = "ESP_GMF_PORT"; + +static inline esp_gmf_err_io_t esp_gmf_port_dec_ref(esp_gmf_port_handle_t port, esp_gmf_payload_t *load, int wait_ticks) +{ + if (load == NULL) { + load = port->self_payload; + } + if (port->ref_count > 0) { + port->ref_count--; + if ((port->ref_count == 0) && port->ops.release) { + return port->ops.release(port->ctx, load, wait_ticks); + } + } + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_port_init(esp_gmf_port_config_t *cfg, esp_gmf_port_handle_t *out_result) +{ + ESP_GMF_NULL_CHECK(TAG, cfg, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, out_result, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_port_t *port = esp_gmf_oal_calloc(1, sizeof(esp_gmf_port_t)); + ESP_GMF_MEM_CHECK(TAG, port, return ESP_GMF_ERR_MEMORY_LACK); + port->attr.type = cfg->type; + port->data_length = cfg->data_length; + port->attr.dir = cfg->dir; + memcpy(&port->ops, &cfg->ops, sizeof(port->ops)); + port->ctx = cfg->ctx; + port->wait_ticks = cfg->wait_ticks; + port->is_shared = 1; // Shared the payload for other port by default + *out_result = (esp_gmf_port_handle_t)port; + ESP_LOGD(TAG, "Create a port:%p, t:%d, dir:%d, sub:%p, len:%d", port, + port->attr.type, port->attr.dir, port->ctx, port->data_length); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_port_deinit(esp_gmf_port_handle_t handle) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_port_t *port = (esp_gmf_port_t *)handle; + ESP_LOGD(TAG, "Delete a port:%p, t:%d, dir:%d, self_payload:%p, ptr:%p", port, + port->attr.type, port->attr.dir, port->self_payload, port->payload); + esp_gmf_payload_delete(port->self_payload); + if (port->ops.del && (port->attr.dir == ESP_GMF_PORT_DIR_OUT)) { + port->ops.del(port->ctx); + port->ops.del = NULL; + port->ctx = NULL; + } + esp_gmf_oal_free(port); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_port_set_payload(esp_gmf_port_handle_t handle, esp_gmf_payload_t *load) +{ + esp_gmf_port_t *port = (esp_gmf_port_t *)handle; + ESP_GMF_NULL_CHECK(TAG, port, return ESP_GMF_ERR_INVALID_ARG); + ESP_LOGD(TAG, "Set payload, cur:%p, new load:%p-b:%p-l:%d, port:%p", port->payload, load, + load->buf ? load->buf : NULL, load->buf_length ? load->buf_length : 0, port); + if (port->self_payload) { + esp_gmf_payload_delete(port->self_payload); + port->self_payload = NULL; + } + port->self_payload = load; + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_port_clean_payload_done(esp_gmf_port_handle_t handle) +{ + esp_gmf_port_t *port = (esp_gmf_port_t *)handle; + ESP_GMF_NULL_CHECK(TAG, port, return ESP_GMF_ERR_INVALID_ARG); + if (port->self_payload) { + esp_gmf_payload_clean_done(port->self_payload); + } + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_port_enable_payload_share(esp_gmf_port_handle_t handle, bool enable) +{ + esp_gmf_port_t *port = (esp_gmf_port_t *)handle; + ESP_GMF_NULL_CHECK(TAG, port, return ESP_GMF_ERR_INVALID_ARG); + port->is_shared = enable == true ? 1 : 0; + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_port_reset(esp_gmf_port_handle_t handle) +{ + esp_gmf_port_t *port = (esp_gmf_port_t *)handle; + ESP_GMF_NULL_CHECK(TAG, port, return ESP_GMF_ERR_INVALID_ARG); + port->payload = NULL; + if (port->self_payload) { + esp_gmf_payload_clean_done(port->self_payload); + port->self_payload->valid_size = 0; + } + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_port_set_wait_ticks(esp_gmf_port_handle_t handle, int wait_ticks_ms) +{ + esp_gmf_port_t *port = (esp_gmf_port_t *)handle; + ESP_GMF_NULL_CHECK(TAG, port, return ESP_GMF_ERR_INVALID_ARG); + ESP_LOGD(TAG, "P:%p, change wait ticks from %d to %d", port, port->wait_ticks, wait_ticks_ms); + port->wait_ticks = wait_ticks_ms; + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_port_set_reader(esp_gmf_port_handle_t handle, void *reader) +{ + esp_gmf_port_t *port = (esp_gmf_port_t *)handle; + ESP_GMF_NULL_CHECK(TAG, port, return ESP_GMF_ERR_INVALID_ARG); + port->reader = reader; + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_port_set_writer(esp_gmf_port_handle_t handle, void *writer) +{ + esp_gmf_port_t *port = (esp_gmf_port_t *)handle; + ESP_GMF_NULL_CHECK(TAG, port, return ESP_GMF_ERR_INVALID_ARG); + port->writer = writer; + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_port_add_last(esp_gmf_port_handle_t head, esp_gmf_port_handle_t io_inst) +{ + ESP_GMF_NULL_CHECK(TAG, head, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, io_inst, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_port_t *tmp = (esp_gmf_port_t *)head; + while (tmp && tmp->next) { + tmp = tmp->next; + } + tmp->next = io_inst; + io_inst->next = NULL; + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_port_del_at(esp_gmf_port_handle_t *head, esp_gmf_port_handle_t io_inst) +{ + ESP_GMF_NULL_CHECK(TAG, head, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, io_inst, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_port_t *tmp = (esp_gmf_port_t *)*head; + esp_gmf_port_t *prev = (esp_gmf_port_t *)*head; + while (tmp) { + if (io_inst == tmp) { + if (*head == tmp) { + *head = tmp->next; + } else { + prev->next = tmp->next; + } + return ESP_GMF_ERR_OK; + } + prev = tmp; + tmp = tmp->next; + } + return ESP_GMF_ERR_FAIL; +} + +esp_gmf_err_io_t esp_gmf_port_acquire_in(esp_gmf_port_handle_t handle, esp_gmf_payload_t **load, uint32_t wanted_size, int wait_ticks) +{ + esp_gmf_port_t *port = (esp_gmf_port_t *)handle; + ESP_GMF_NULL_CHECK(TAG, port, return ESP_GMF_IO_FAIL); + ESP_GMF_NULL_CHECK(TAG, load, return ESP_GMF_IO_FAIL); + if (port->attr.dir != ESP_GMF_PORT_DIR_IN) { + ESP_LOGE(TAG, "Wrong port direction! %s, p:%p-dir:%d", __func__, port, port->attr.dir); + return ESP_GMF_IO_FAIL; + } + esp_gmf_err_io_t ret = ESP_GMF_ERR_OK; + esp_gmf_element_handle_t el = (esp_gmf_element_handle_t)port->reader; + // Both reader and writer existed + if (el && port->writer) { + // Not first element + ESP_LOGD(TAG, "ACQ IN, GET, port:%p-%d, el:%p-%s, PLD[h:%p, b:%p, v:%d]", + port, port->attr.type, el, OBJ_GET_TAG(el), port->payload, port->payload ? port->payload->buf : NULL, port->payload ? port->payload->valid_size : 0); + if (port->payload) { + *load = port->payload; + if (ESP_GMF_ELEMENT_GET(((esp_gmf_node_t *)el)->next)) { + if ((port->payload->needs_free) && (port->is_shared) && ESP_GMF_ELEMENT_GET(((esp_gmf_node_t *)el)->next)->out) { + ESP_GMF_ELEMENT_GET(((esp_gmf_node_t *)el)->next)->out->payload = port->payload; + } + } + } else { + ESP_LOGE(TAG, "ACQ IN, there is no payload, p:%p, el:%p-%s ---->", port, el, OBJ_GET_TAG(el)); + ret = ESP_GMF_IO_FAIL; + } + } else { + if (*load == NULL) { + if (port->self_payload == NULL) { + esp_gmf_payload_new(&port->self_payload); + ESP_GMF_MEM_CHECK(TAG, port->self_payload, return ESP_GMF_IO_FAIL); + ESP_LOGI(TAG, "ACQ IN, new self payload:%p, port:%p, el:%p-%s", port->self_payload, port, el, OBJ_GET_TAG(el)); + } + port->payload = port->self_payload; + *load = port->self_payload; + } else { + port->payload = *load; + } + if ((port->attr.type == ESP_GMF_PORT_TYPE_BYTE) && ((*load)->buf_length < wanted_size)) { + // Check whether the buffer length is sufficient for use; if not, reallocate it. + ret = esp_gmf_payload_realloc_aligned_buf(*load, port->attr.buf_addr_aligned, wanted_size); + ESP_GMF_RET_ON_ERROR(TAG, ret, return ESP_GMF_IO_FAIL, "ACQ IN, reallocate payload buffer failed, ret:%d, %s, p:%p, new_sz:%ld", + ret, __func__, port, wanted_size); + } + esp_gmf_element_t *nxt_el = el ? ESP_GMF_ELEMENT_GET(((esp_gmf_node_t *)el)->next) : NULL; + ESP_LOGD(TAG, "ACQ IN, port:%p-%d, el:%p-%s, PLD[p:%p, h:%p, b:%p, l:%d], nxt_el:%p-%s", port, port->attr.type, el, OBJ_GET_TAG(el), port->payload, + *load, (*load)->buf, (*load)->buf_length, nxt_el, OBJ_GET_TAG(nxt_el)); + if ((port->payload->needs_free) && (port->attr.type != ESP_GMF_PORT_TYPE_BLOCK) && (port->is_shared) + && nxt_el && nxt_el->out) { + nxt_el->out->payload = port->payload; + } + if (port->ops.acquire) { + ret = port->ops.acquire(port->ctx, *load, wanted_size, wait_ticks); + if (ret >= ESP_GMF_IO_OK) { + port->ref_count = 1; + } + } + } + return ret; +} + +esp_gmf_err_io_t esp_gmf_port_release_in(esp_gmf_port_handle_t handle, esp_gmf_payload_t *load, int wait_ticks) +{ + esp_gmf_port_t *port = (esp_gmf_port_t *)handle; + ESP_GMF_NULL_CHECK(TAG, port, return ESP_GMF_IO_FAIL); + ESP_GMF_NULL_CHECK(TAG, load, return ESP_GMF_IO_FAIL); + + if (port->attr.dir != ESP_GMF_PORT_DIR_IN) { + ESP_LOGE(TAG, "Wrong port direction! %s, p:%p, pld:%p, buf_len:%d", __func__, port, + port->payload, port->data_length); + return ESP_GMF_IO_FAIL; + } + int ret = ESP_GMF_ERR_OK; + esp_gmf_element_handle_t el = (esp_gmf_element_handle_t)port->reader; + ESP_LOGD(TAG, "%s, p:%p, el:%s, PLD[p:%p, h:%p, b:%p, l:%d]", __func__, port, OBJ_GET_TAG(el), port->payload, load, load->buf, load->buf_length); + if (el && port->writer) { + if (port->ref_port) { + ret = esp_gmf_port_dec_ref(port->ref_port, load, wait_ticks); + } + if (port->payload && port->is_shared) { + port->payload = NULL; + } + } else { + ret = esp_gmf_port_dec_ref(port, load, wait_ticks); + } + return ret; +} + +esp_gmf_err_io_t esp_gmf_port_acquire_out(esp_gmf_port_handle_t handle, esp_gmf_payload_t **load, uint32_t wanted_size, int wait_ticks) +{ + esp_gmf_port_t *port = (esp_gmf_port_t *)handle; + ESP_GMF_NULL_CHECK(TAG, port, return ESP_GMF_IO_FAIL); + ESP_GMF_NULL_CHECK(TAG, load, return ESP_GMF_IO_FAIL); + if (port->attr.dir != ESP_GMF_PORT_DIR_OUT) { + ESP_LOGE(TAG, "Wrong port direction! %s, p:%p, pld:%p, buf_len:%d, want:%ld", __func__, port, + port->payload, port->data_length, wanted_size); + return ESP_GMF_IO_FAIL; + } + int ret = ESP_GMF_ERR_OK; + esp_gmf_element_handle_t el = (esp_gmf_element_handle_t)port->writer; + esp_gmf_payload_t *in_original_load = *load; + if ((*load) && ((*load) == ESP_GMF_ELEMENT_GET(el)->in->payload)) { + if (wanted_size > ESP_GMF_ELEMENT_GET(el)->in->payload->buf_length) { + ESP_LOGE(TAG, "Input and output use the same payload, but the acquired length is too large. I:%p-%d, O:%p-%ld", + ESP_GMF_ELEMENT_GET(el)->in->payload, ESP_GMF_ELEMENT_GET(el)->in->payload->buf_length, (*load), wanted_size); + return ESP_GMF_IO_FAIL; + } + // When in and out use same payload, clear the next element out payload which is set by acquire in + if (ESP_GMF_ELEMENT_GET(((esp_gmf_node_t *)el)->next) && ESP_GMF_ELEMENT_GET(((esp_gmf_node_t *)el)->next)->out) { + ESP_GMF_ELEMENT_GET(((esp_gmf_node_t *)el)->next)->out->payload = NULL; + } + } + if (*load == NULL) { + if (port->payload && port->ops.acquire == NULL) { + *load = port->payload; + } else { + if (port->self_payload == NULL) { + esp_gmf_payload_new(&port->self_payload); + ESP_GMF_MEM_CHECK(TAG, port->self_payload, return ESP_GMF_IO_FAIL); + ESP_LOGD(TAG, "ACQ OUT, new self payload:%p, port:%p, el:%p-%s", port->self_payload, port, el, OBJ_GET_TAG(el)); + } + port->payload = port->self_payload; + *load = port->self_payload; + } + } + if (el && port->reader) { + if ((*load)->buf_length < wanted_size) { + ret = esp_gmf_payload_realloc_aligned_buf(*load, port->attr.buf_addr_aligned, wanted_size); + ESP_GMF_RET_ON_ERROR(TAG, ret, return ESP_GMF_IO_FAIL, "ACQ OUT, SET NEXT, reallocate payload buffer failed, el:%s, p:%p, sz:%d, new_sz:%ld", + OBJ_GET_TAG(el), port, port->data_length, wanted_size); + } + if (in_original_load) { + if (ESP_GMF_ELEMENT_GET(((esp_gmf_node_t *)el)->next)->in->payload) { + ESP_LOGD(TAG, "ACQ OUT, COPY DATA TO NEXT[%p], port:%p-%d, el:%p-%s", + ESP_GMF_ELEMENT_GET(((esp_gmf_node_t *)el)->next)->in->payload, port, port->attr.type, el, OBJ_GET_TAG(el)); + esp_gmf_payload_copy_data(ESP_GMF_ELEMENT_GET(((esp_gmf_node_t *)el)->next)->in->payload, *load); + } else { + esp_gmf_port_t *next_in = ESP_GMF_ELEMENT_GET(((esp_gmf_node_t *)el)->next)->in; + next_in->payload = *load; + esp_gmf_port_t *ref_in = ESP_GMF_ELEMENT_GET(el)->in; + ref_in = ref_in->ref_port ? ref_in->ref_port : ref_in; + next_in->ref_port = ref_in; + ref_in->ref_count++; + } + } else { + esp_gmf_port_t *next_in = ESP_GMF_ELEMENT_GET(((esp_gmf_node_t *)el)->next)->in; + next_in->payload = *load; + } + ESP_LOGD(TAG, "ACQ OUT, SET NEXT, port:%p-%d, el:%p-%s, PLD[in:%p-done:%d, nxt:%p]", port, port->attr.type, el, + OBJ_GET_TAG(el), *load, (*load)->is_done, ESP_GMF_ELEMENT_GET(((esp_gmf_node_t *)el)->next)->in->payload); + } else { + port->payload = *load; + if (port->attr.type == ESP_GMF_PORT_TYPE_BYTE) { + if ((*load)->buf_length < wanted_size) { + ret = esp_gmf_payload_realloc_aligned_buf(*load, port->attr.buf_addr_aligned, wanted_size); + } + ESP_GMF_RET_ON_ERROR(TAG, ret, return ESP_GMF_IO_FAIL, "ACQ OUT, reallocate payload buffer failed, el:%s, p:%p, ld:%p, sz:%d, new_sz:%ld", + OBJ_GET_TAG(el), port, *load, port->data_length, wanted_size); + } + ESP_LOGD(TAG, "ACQ OUT, port:%p-%d, el:%p-%s, PLD[p:%p, h:%p, b:%p, v:%d, l:%d]", port, port->attr.type, el, OBJ_GET_TAG(el), + port->payload, *load, (*load)->buf, (*load)->valid_size, (*load)->buf_length); + if (el && ESP_GMF_ELEMENT_GET(((esp_gmf_node_t *)el)->next)) { + if ((port->payload->needs_free) && ESP_GMF_ELEMENT_GET(((esp_gmf_node_t *)el)->next)->in) { + ESP_GMF_ELEMENT_GET(((esp_gmf_node_t *)el)->next)->in->payload = port->payload; + } + } + if (port->ops.acquire) { + ret = port->ops.acquire(port->ctx, *load, wanted_size, wait_ticks); + } + } + return ret; +} + +esp_gmf_err_io_t esp_gmf_port_release_out(esp_gmf_port_handle_t handle, esp_gmf_payload_t *load, int wait_ticks) +{ + esp_gmf_port_t *port = (esp_gmf_port_t *)handle; + ESP_GMF_NULL_CHECK(TAG, port, return ESP_GMF_IO_FAIL); + ESP_GMF_NULL_CHECK(TAG, load, return ESP_GMF_IO_FAIL); + if (port->attr.dir != ESP_GMF_PORT_DIR_OUT) { + ESP_LOGE(TAG, "Wrong port direction! %s, p:%p, pld:%p, buf_len:%d", __func__, port, + port->payload, port->data_length); + return ESP_GMF_IO_FAIL; + } + esp_gmf_element_handle_t el = (esp_gmf_element_handle_t)port->writer; + esp_gmf_err_io_t ret = ESP_GMF_ERR_OK; + ESP_LOGD(TAG, "%s, p:%p, el:%s,reader:%p, PLD[h:%p, b:%p, l:%d]", __func__, port, OBJ_GET_TAG(el), port->reader, load, load->buf, load->buf_length); + if (el && port->reader) { + port->payload = NULL; + } else { + ret = port->ops.release(port->ctx, load, wait_ticks); + } + return ret; +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/src/esp_gmf_task.c b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/src/esp_gmf_task.c new file mode 100644 index 0000000..df8d759 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/src/esp_gmf_task.c @@ -0,0 +1,603 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#include +#include +#include + +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" +#include "sys/queue.h" + +#include "esp_gmf_oal_mutex.h" +#include "esp_gmf_oal_thread.h" +#include "esp_gmf_oal_mem.h" +#include "esp_gmf_node.h" +#include "esp_gmf_task.h" +#include "esp_log.h" + +static const char *TAG = "ESP_GMF_TASK"; + +#define DEFAULT_TASK_OPT_MAX_TIME_MS (2000 / portTICK_PERIOD_MS) + +#define GMF_TASK_RUN_BIT (1 << 0) +#define GMF_TASK_PAUSE_BIT (1 << 1) +#define GMF_TASK_RESUME_BIT (1 << 2) +#define GMF_TASK_STOP_BIT (1 << 3) +#define GMF_TASK_EXIT_BIT (1 << 4) + +#define GMF_TASK_WAIT_FOR_STATE_BITS(event_group, bits, timeout) \ + (bits == (bits & xEventGroupWaitBits((EventGroupHandle_t)event_group, bits, true, true, timeout))) + +#define GMF_TASK_WAIT_FOR_MULTI_STATE_BITS(event_group, bits, timeout) \ + (0 != ((bits) & xEventGroupWaitBits((EventGroupHandle_t)event_group, (bits), false, false, timeout))) + +#define GMF_TASK_SET_STATE_BITS(event_group, bits) \ + xEventGroupSetBits((EventGroupHandle_t)event_group, bits); + +#define GMF_TASK_CLR_STATE_BITS(event_group, bits) \ + xEventGroupClearBits((EventGroupHandle_t)event_group, bits); + +static inline esp_gmf_err_t esp_gmf_event_state_notify(esp_gmf_task_handle_t handle, esp_gmf_event_type_t type, esp_gmf_event_state_t st) +{ + esp_gmf_task_t *tsk = (esp_gmf_task_t *)handle; + esp_gmf_event_pkt_t evt = { + .from = tsk, + .type = type, + .sub = st, + .payload = NULL, + .payload_size = 0, + }; + if (tsk->event_func == NULL) { + return ESP_GMF_ERR_OK; + } + return tsk->event_func(&evt, tsk->ctx); +} + +static inline esp_gmf_err_t esp_gmf_task_event_state_change_and_notify(esp_gmf_task_handle_t handle, esp_gmf_event_state_t new_st) +{ + int ret = ESP_GMF_ERR_OK; + esp_gmf_task_t *tsk = (esp_gmf_task_t *)handle; + if (tsk->state != new_st) { + // Notification first then change the state to keep the previous state in callback function + ret = esp_gmf_event_state_notify(tsk, ESP_GMF_EVT_TYPE_CHANGE_STATE, new_st); + if (ret == ESP_GMF_ERR_OK) { + tsk->state = new_st; + } + } + return ret; +} + +static inline esp_gmf_err_t esp_gmf_task_event_loading_job(esp_gmf_task_handle_t handle, esp_gmf_event_state_t new_st) +{ + int ret = ESP_GMF_ERR_OK; + esp_gmf_task_t *tsk = (esp_gmf_task_t *)handle; + if (tsk->state != new_st) { + ret = esp_gmf_event_state_notify(tsk, ESP_GMF_EVT_TYPE_LOADING_JOB, new_st); + if (ret == ESP_GMF_ERR_OK) { + tsk->state = new_st; + } + } + return ret; +} + +static inline int esp_gmf_task_acquire_signal(esp_gmf_task_handle_t handle, int ticks) +{ + esp_gmf_task_t *tsk = (esp_gmf_task_t *)handle; + if (xSemaphoreTake(tsk->wait_sem, ticks) != pdPASS) { + return ESP_GMF_ERR_FAIL; + } + return ESP_GMF_ERR_OK; +} + +static inline int esp_gmf_task_release_signal(esp_gmf_task_handle_t handle, int ticks) +{ + esp_gmf_task_t *tsk = (esp_gmf_task_t *)handle; + if (xSemaphoreGive(tsk->wait_sem) != pdTRUE) { + return ESP_GMF_ERR_FAIL; + } + return ESP_GMF_ERR_OK; +} + +static int get_jobs_num(esp_gmf_job_t *job) +{ + int k = 1; + esp_gmf_job_t *tmp = job; + while (tmp && (tmp = tmp->next)) { + k++; + } + return k; +} + +static inline void __esp_gmf_task_free(esp_gmf_task_handle_t handle) +{ + esp_gmf_task_t *tsk = (esp_gmf_task_t *)handle; + if (tsk->lock) { + esp_gmf_oal_mutex_destroy(tsk->lock); + } + if (tsk->event_group) { + vEventGroupDelete((EventGroupHandle_t)tsk->event_group); + } + if (tsk->block_sem) { + vSemaphoreDelete(tsk->block_sem); + } + if (tsk->wait_sem) { + vSemaphoreDelete(tsk->wait_sem); + } + if (tsk->start_stack) { + esp_gmf_job_stack_destroy(tsk->start_stack); + } + esp_gmf_obj_set_tag((esp_gmf_obj_handle_t)tsk, NULL); + esp_gmf_oal_free(tsk); +} + +static int esp_gmf_job_item_free(void *ptr) +{ + esp_gmf_job_t *job = (esp_gmf_job_t *)ptr; + esp_gmf_oal_free((void *)job->label); + esp_gmf_oal_free(job); + return ESP_GMF_ERR_OK; +} + +static inline void __esp_gmf_task_delete_jobs(esp_gmf_task_handle_t handle) +{ + esp_gmf_task_t *tsk = (esp_gmf_task_t *)handle; + esp_gmf_node_clear((esp_gmf_node_t **)&tsk->working, esp_gmf_job_item_free); +} + +static inline esp_gmf_job_t *_esp_gmf_get_next_job(esp_gmf_task_t *tsk, esp_gmf_job_t *worker) +{ + esp_gmf_job_t *next_job = worker->next; + if (worker->times == ESP_GMF_JOB_TIMES_ONCE) { + ESP_LOGI(TAG, "One times job is complete, del[wk:%p, ctx:%p, label:%s]", worker, worker->ctx, worker->label); + esp_gmf_node_del_at((esp_gmf_node_t **)&tsk->working, (esp_gmf_node_t *)worker); + esp_gmf_job_item_free(worker); + } + if (next_job) { + return next_job; + } + bool is_empty = false; + esp_gmf_job_stack_is_empty(tsk->start_stack, &is_empty); + if (is_empty == false) { + esp_gmf_job_stack_pop(tsk->start_stack, (uint32_t *)&next_job); + } + return next_job; +} + +static inline int process_func(esp_gmf_task_handle_t handle, void *para) +{ + esp_gmf_task_t *tsk = (esp_gmf_task_t *)handle; + esp_gmf_job_t *worker = tsk->working; + if ((worker == NULL) || (worker->func == NULL)) { + ESP_LOGE(TAG, "Jobs list are invalid[%p, %p]", tsk, worker); + return ESP_GMF_ERR_INVALID_ARG; + } + int result = ESP_GMF_ERR_OK; + GMF_TASK_CLR_STATE_BITS(tsk->event_group, GMF_TASK_STOP_BIT); + esp_gmf_event_state_t quit_state = ESP_GMF_EVENT_STATE_STOPPED; + while (worker && worker->func) { + ESP_LOGD(TAG, "Running, job:%p, ctx:%p", worker->func, worker->ctx); + worker->ret = worker->func(worker->ctx, NULL); + ESP_LOGV(TAG, "Job ret:%d, [tsk:%s-%p:%p-%p-%s]", worker->ret, OBJ_GET_TAG((esp_gmf_obj_handle_t)tsk), tsk, worker, worker->ctx, worker->label); + if (worker->ret == ESP_GMF_JOB_ERR_CONTINUE) { + // The means need more loops + worker = tsk->working; + // When receive command need process command + if (tsk->_pause == false && tsk->_stop == false) { + continue; + } + } else if (worker->ret == ESP_GMF_JOB_ERR_TRUNCATE) { + if (worker->times == ESP_GMF_JOB_TIMES_ONCE) { + // Once job and truncated is conflicted, enter into error state when detected + ESP_LOGE(TAG, "Once job not support truncated"); + __esp_gmf_task_delete_jobs(tsk); + quit_state = ESP_GMF_EVENT_STATE_ERROR; + break; + } else { + ESP_LOGD(TAG, "Job truncated [tsk:%s-%p:%p-%p-%s], st:%s", OBJ_GET_TAG((esp_gmf_obj_handle_t)tsk), tsk, worker, worker->ctx, worker->label, + esp_gmf_event_get_state_str(tsk->state)); + esp_gmf_job_stack_push(tsk->start_stack, (uint32_t)worker); + } + } else if (worker->ret == ESP_GMF_JOB_ERR_DONE) { + ESP_LOGI(TAG, "Job is done, [tsk:%s-%p, wk:%p, job:%p-%s]", OBJ_GET_TAG((esp_gmf_obj_handle_t)tsk), tsk, worker, worker->ctx, worker->label); + esp_gmf_job_t *tmp = worker->next; + esp_gmf_node_del_at((esp_gmf_node_t **)&tsk->working, (esp_gmf_node_t *)worker); + esp_gmf_job_stack_remove(tsk->start_stack, (uint32_t)worker); + esp_gmf_job_item_free(worker); + worker = tmp; + if (worker == NULL) { + ESP_LOGD(TAG, "All jobs are finished, [tsk:%s-%p]", OBJ_GET_TAG((esp_gmf_obj_handle_t)tsk), tsk); + quit_state = ESP_GMF_EVENT_STATE_FINISHED; + break; + } + continue; + } else if (worker->ret == ESP_GMF_JOB_ERR_FAIL) { + ESP_LOGE(TAG, "Job failed[tsk:%s-%p:%p-%p-%s], ret:%d, st:%s", OBJ_GET_TAG((esp_gmf_obj_handle_t)tsk), tsk, worker, worker->ctx, worker->label, worker->ret, + esp_gmf_event_get_state_str(tsk->state)); + if (tsk->state != ESP_GMF_EVENT_STATE_STOPPED) { + __esp_gmf_task_delete_jobs(tsk); + quit_state = ESP_GMF_EVENT_STATE_ERROR; + break; + } + } + if (tsk->_pause) { + if (tsk->state != ESP_GMF_EVENT_STATE_ERROR) { + ESP_LOGI(TAG, "Pause job, [%s-%p, wk:%p, job:%p-%s],st:%s", OBJ_GET_TAG((esp_gmf_obj_handle_t)tsk), tsk, worker, worker->ctx, worker->label, + esp_gmf_event_get_state_str(tsk->state)); + esp_gmf_task_event_state_change_and_notify(tsk, ESP_GMF_EVENT_STATE_PAUSED); + GMF_TASK_SET_STATE_BITS(tsk->event_group, GMF_TASK_PAUSE_BIT); + esp_gmf_task_acquire_signal(tsk, portMAX_DELAY); + ESP_LOGI(TAG, "Resume job, [%s-%p, wk:%p, job:%p-%s]", OBJ_GET_TAG((esp_gmf_obj_handle_t)tsk), tsk, worker, worker->ctx, worker->label); + esp_gmf_task_event_state_change_and_notify(tsk, ESP_GMF_EVENT_STATE_RUNNING); + GMF_TASK_SET_STATE_BITS(tsk->event_group, GMF_TASK_RESUME_BIT); + } else { + ESP_LOGI(TAG, "Skip pause by error state, [%s-%p, wk:%p, job:%p-%s],st:%s", OBJ_GET_TAG((esp_gmf_obj_handle_t)tsk), tsk, worker, worker->ctx, worker->label, + esp_gmf_event_get_state_str(tsk->state)); + GMF_TASK_SET_STATE_BITS(tsk->event_group, GMF_TASK_PAUSE_BIT); + } + } + if (tsk->_stop && (tsk->state != ESP_GMF_EVENT_STATE_ERROR)) { + ESP_LOGV(TAG, "Stop job, [%s-%p, wk:%p, job:%p-%s]", OBJ_GET_TAG((esp_gmf_obj_handle_t)tsk), tsk, worker, worker->ctx, worker->label); + __esp_gmf_task_delete_jobs(tsk); + quit_state = ESP_GMF_EVENT_STATE_STOPPED; + break; + } + ESP_LOGD(TAG, "Find next job to process, [%s-%p, cur:%p-%p-%s]", OBJ_GET_TAG((esp_gmf_obj_handle_t)tsk), tsk, worker, worker->ctx, worker->label); + worker = _esp_gmf_get_next_job(tsk, worker); + ESP_LOGD(TAG, "Found next job[%p] to process", worker); + } + // Load quit jobs and run + esp_gmf_job_stack_clear(tsk->start_stack); + esp_gmf_task_event_loading_job(tsk, quit_state); + ESP_LOGV(TAG, "Worker exit, [%p-%s], st:%s", tsk, OBJ_GET_TAG((esp_gmf_obj_handle_t)tsk), esp_gmf_event_get_state_str(tsk->state)); + worker = tsk->working; + while (worker && worker->func) { + worker->ret = worker->func(worker->ctx, NULL); + // Failed when do clear up set state to error, continue to clear up for other jobs + if (worker->ret != ESP_GMF_JOB_ERR_OK) { + quit_state = ESP_GMF_EVENT_STATE_ERROR; + } + worker = _esp_gmf_get_next_job(tsk, worker); + } + tsk->state = quit_state; + esp_gmf_event_state_notify(tsk, ESP_GMF_EVT_TYPE_CHANGE_STATE, tsk->state); + GMF_TASK_SET_STATE_BITS(tsk->event_group, GMF_TASK_STOP_BIT); + return result; +} + +static void esp_gmf_thread_fun(void *pv) +{ + esp_gmf_task_t *tsk = (esp_gmf_task_t *)pv; + tsk->_destroy = 0; + while (tsk->_task_run) { + while ((tsk->working == NULL) || (tsk->_running == 0)) { + ESP_LOGI(TAG, "Waiting to run... [tsk:%s-%p, wk:%p, run:%d]", OBJ_GET_TAG((esp_gmf_obj_handle_t)tsk), tsk, tsk->working, tsk->_running); + xSemaphoreTake(tsk->block_sem, portMAX_DELAY); + if (tsk->_destroy) { + tsk->_destroy = 0; + ESP_LOGD(TAG, "Thread will be destroyed, [%s,%p]", OBJ_GET_TAG((esp_gmf_obj_handle_t)tsk), tsk); + goto ESP_GMF_THREAD_EXIT; + } + if (tsk->_run == 1) { + tsk->_running = 1; + tsk->_run = 0; + } + } + int ret = esp_gmf_task_event_state_change_and_notify(tsk, ESP_GMF_EVENT_STATE_RUNNING); + GMF_TASK_SET_STATE_BITS(tsk->event_group, GMF_TASK_RUN_BIT); + if (ret != ESP_GMF_ERR_OK) { + tsk->_running = 0; + ESP_LOGE(TAG, "Failed on prepare, [%s,%p],ret:%d", OBJ_GET_TAG((esp_gmf_obj_handle_t)tsk), tsk, ret); + continue; + } + // Loop jobs until done or error + process_func(tsk, tsk->ctx); + tsk->_running = 0; + } +ESP_GMF_THREAD_EXIT: + tsk->state = ESP_GMF_EVENT_STATE_NONE; + void *oal_thread = tsk->oal_thread; + GMF_TASK_SET_STATE_BITS(tsk->event_group, GMF_TASK_EXIT_BIT); + ESP_LOGD(TAG, "Thread destroyed! [%s,%p]", OBJ_GET_TAG((esp_gmf_obj_handle_t)tsk), tsk); + esp_gmf_oal_thread_delete(oal_thread); +} + +static esp_gmf_err_t _task_new(void *cfg, esp_gmf_obj_handle_t *handle) +{ + return esp_gmf_task_init((esp_gmf_task_cfg_t *)cfg, handle); +} + +esp_gmf_err_t esp_gmf_task_init(esp_gmf_task_cfg_t *config, esp_gmf_task_handle_t *tsk_hd) +{ + ESP_GMF_NULL_CHECK(TAG, tsk_hd, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, config, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_task_t *handle = calloc(1, sizeof(struct _esp_gmf_task)); + ESP_GMF_MEM_CHECK(TAG, handle, return ESP_GMF_ERR_MEMORY_LACK); + handle->lock = esp_gmf_oal_mutex_create(); + ESP_GMF_MEM_CHECK(TAG, handle->lock, goto _tsk_init_failed); + handle->event_group = xEventGroupCreate(); + ESP_GMF_MEM_CHECK(TAG, handle->event_group, goto _tsk_init_failed); + handle->block_sem = xSemaphoreCreateBinary(); + ESP_GMF_MEM_CHECK(TAG, handle->block_sem, goto _tsk_init_failed); + handle->wait_sem = xSemaphoreCreateBinary(); + ESP_GMF_MEM_CHECK(TAG, handle->wait_sem, goto _tsk_init_failed); + esp_gmf_task_cfg_t *cfg = (esp_gmf_task_cfg_t *)config; + handle->event_func = cfg->cb; + handle->ctx = cfg->ctx; + handle->api_sync_time = DEFAULT_TASK_OPT_MAX_TIME_MS; + + esp_gmf_job_stack_create(&handle->start_stack); + ESP_GMF_MEM_CHECK(TAG, handle->start_stack, goto _tsk_init_failed); + + char tag[ESP_GMF_TAG_MAX_LEN] = {0}; + if (cfg->name) { + // User original name without modify + snprintf(tag, ESP_GMF_TAG_MAX_LEN, "%s", cfg->name); + } else { + snprintf(tag, ESP_GMF_TAG_MAX_LEN, "TSK_%p", handle); + } + + esp_gmf_obj_t *obj = (esp_gmf_obj_t *)handle; + esp_gmf_err_t ret = esp_gmf_obj_set_tag(obj, tag); + ESP_GMF_RET_ON_ERROR(TAG, ret, goto _tsk_init_failed, "Failed set OBJ tag"); + obj->new_obj = _task_new; + obj->del_obj = esp_gmf_task_deinit; + + if (cfg->thread.stack > 0) { + handle->thread.stack = cfg->thread.stack; + handle->thread.stack_in_ext = cfg->thread.stack_in_ext; + } + if (cfg->thread.prio) { + handle->thread.prio = cfg->thread.prio; + } else { + handle->thread.prio = DEFAULT_ESP_GMF_TASK_PRIO; + } + if (cfg->thread.core) { + handle->thread.core = cfg->thread.core; + } else { + handle->thread.core = DEFAULT_ESP_GMF_TASK_CORE; + } + handle->_task_run = true; + if (handle->thread.stack > 0) { + ret = esp_gmf_oal_thread_create(&handle->oal_thread, OBJ_GET_TAG(obj), esp_gmf_thread_fun, handle, handle->thread.stack, + handle->thread.prio, handle->thread.stack_in_ext, handle->thread.core); + if (ret == ESP_GMF_ERR_FAIL) { + handle->_task_run = false; + ESP_LOGE(TAG, "Create thread failed, [%s]", OBJ_GET_TAG((esp_gmf_obj_handle_t)handle)); + goto _tsk_init_failed; + } + } + handle->state = ESP_GMF_EVENT_STATE_INITIALIZED; + *tsk_hd = handle; + return ESP_GMF_ERR_OK; +_tsk_init_failed: + __esp_gmf_task_free(handle); + return ESP_GMF_ERR_FAIL; +} + +esp_gmf_err_t esp_gmf_task_deinit(esp_gmf_task_handle_t handle) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_task_t *tsk = (esp_gmf_task_t *)handle; + // Wait for thread quit if already run + if (tsk->_task_run) { + esp_gmf_oal_mutex_lock(tsk->lock); + if ((tsk->state == ESP_GMF_EVENT_STATE_RUNNING) + || (tsk->state == ESP_GMF_EVENT_STATE_PAUSED)) { + tsk->_stop = 1; + } + if (tsk->state == ESP_GMF_EVENT_STATE_PAUSED) { + esp_gmf_task_release_signal(tsk, portMAX_DELAY); + } + tsk->_task_run = 0; + tsk->_destroy = 1; + xSemaphoreGive(tsk->block_sem); + // Wait for task exit + if (GMF_TASK_WAIT_FOR_STATE_BITS(tsk->event_group, GMF_TASK_EXIT_BIT, portMAX_DELAY) == false) { + ESP_LOGE(TAG, "Failed to wait task %p to exit", tsk); + } + ESP_LOGD(TAG, "%s, %s", __func__, OBJ_GET_TAG(tsk)); + __esp_gmf_task_delete_jobs(tsk); + esp_gmf_oal_mutex_unlock(tsk->lock); + } + // Clear-up all related resources + __esp_gmf_task_free(tsk); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_task_register_ready_job(esp_gmf_task_handle_t handle, const char *label, esp_gmf_job_func job, esp_gmf_job_times_t times, void *ctx, bool done) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_task_t *tsk = (esp_gmf_task_t *)handle; + esp_gmf_job_t *new_job = NULL; + new_job = esp_gmf_oal_calloc(1, sizeof(esp_gmf_job_t)); + ESP_GMF_MEM_CHECK(TAG, new_job, return ESP_GMF_ERR_MEMORY_LACK;); + new_job->label = esp_gmf_oal_strdup(label == NULL ? "NULL" : label); + ESP_GMF_MEM_CHECK(TAG, new_job->label, { esp_gmf_oal_free(new_job); return ESP_GMF_ERR_MEMORY_LACK;}); + new_job->func = job; + new_job->ctx = ctx; + new_job->times = times; + + // Add the first infinite processing job to the stack + bool is_empty = false; + esp_gmf_job_stack_is_empty(tsk->start_stack, &is_empty); + if ((times == ESP_GMF_JOB_TIMES_INFINITE) && (is_empty == true)) { + esp_gmf_job_stack_push(tsk->start_stack, (uint32_t)new_job); + } + + if (tsk->working == NULL) { + tsk->working = new_job; + } else { + esp_gmf_node_add_last((esp_gmf_node_t *)tsk->working, (esp_gmf_node_t *)new_job); + } + ESP_LOGD(TAG, "Reg new job to task:%p, item:%p, label:%s, func:%p, ctx:%p cnt:%d", tsk, new_job, new_job->label, job, ctx, get_jobs_num(tsk->working)); + if (done) { + xSemaphoreGive(tsk->block_sem); + } + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_task_set_event_func(esp_gmf_task_handle_t handle, esp_gmf_event_cb cb, void *ctx) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_task_t *tsk = (esp_gmf_task_t *)handle; + esp_gmf_oal_mutex_lock(tsk->lock); + tsk->event_func = cb; + tsk->ctx = ctx; + esp_gmf_oal_mutex_unlock(tsk->lock); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_task_run(esp_gmf_task_handle_t handle) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_task_t *tsk = (esp_gmf_task_t *)handle; + esp_gmf_oal_mutex_lock(tsk->lock); + ESP_LOGD(TAG, "%s, %s-%p,st:%s", __func__, OBJ_GET_TAG((esp_gmf_obj_handle_t)tsk), tsk, esp_gmf_event_get_state_str(tsk->state)); + if (tsk->state == ESP_GMF_EVENT_STATE_PAUSED || tsk->state == ESP_GMF_EVENT_STATE_RUNNING) { + esp_gmf_oal_mutex_unlock(tsk->lock); + ESP_LOGW(TAG, "Can't run on %s, [%s,%p]", esp_gmf_event_get_state_str(tsk->state), OBJ_GET_TAG((esp_gmf_obj_handle_t)tsk), tsk); + return ESP_GMF_ERR_NOT_SUPPORT; + } + if (tsk->_task_run == false) { + esp_gmf_oal_mutex_unlock(tsk->lock); + ESP_LOGW(TAG, "No task for run, %s, [%s,%p]", esp_gmf_event_get_state_str(tsk->state), OBJ_GET_TAG((esp_gmf_obj_handle_t)tsk), tsk); + return ESP_GMF_ERR_INVALID_STATE; + } + tsk->_run = 1; + xSemaphoreGive(tsk->block_sem); + // Wait for run finished + if (GMF_TASK_WAIT_FOR_STATE_BITS(tsk->event_group, GMF_TASK_RUN_BIT, tsk->api_sync_time) == false) { + ESP_LOGE(TAG, "Run timeout,[%s,%p]", OBJ_GET_TAG((esp_gmf_obj_handle_t)tsk), tsk); + esp_gmf_oal_mutex_unlock(tsk->lock); + return ESP_GMF_ERR_TIMEOUT; + } + esp_gmf_oal_mutex_unlock(tsk->lock); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_task_stop(esp_gmf_task_handle_t handle) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_task_t *tsk = (esp_gmf_task_t *)handle; + esp_gmf_oal_mutex_lock(tsk->lock); + ESP_LOGD(TAG, "%s, %s-%p, st:%s", __func__, OBJ_GET_TAG((esp_gmf_obj_handle_t)tsk), tsk, esp_gmf_event_get_state_str(tsk->state)); + if ((tsk->state != ESP_GMF_EVENT_STATE_RUNNING) + && (tsk->state != ESP_GMF_EVENT_STATE_PAUSED)) { + esp_gmf_oal_mutex_unlock(tsk->lock); + ESP_LOGD(TAG, "Already stopped, %s, [%s,%p]", esp_gmf_event_get_state_str(tsk->state), OBJ_GET_TAG((esp_gmf_obj_handle_t)tsk), tsk); + return ESP_GMF_ERR_OK; + } + if ((tsk->_task_run == false)) { + esp_gmf_oal_mutex_unlock(tsk->lock); + ESP_LOGW(TAG, "The task is not running, %s, [%s,%p]", esp_gmf_event_get_state_str(tsk->state), OBJ_GET_TAG((esp_gmf_obj_handle_t)tsk), tsk); + return ESP_GMF_ERR_INVALID_STATE; + } + if ((tsk->state == ESP_GMF_EVENT_STATE_NONE)) { + esp_gmf_oal_mutex_unlock(tsk->lock); + ESP_LOGW(TAG, "Can't stop on %s, [%s,%p]", esp_gmf_event_get_state_str(tsk->state), OBJ_GET_TAG((esp_gmf_obj_handle_t)tsk), tsk); + return ESP_GMF_ERR_NOT_SUPPORT; + } + tsk->_stop = 1; + if (tsk->state == ESP_GMF_EVENT_STATE_PAUSED) { + esp_gmf_task_release_signal(tsk, portMAX_DELAY); + } + if (GMF_TASK_WAIT_FOR_STATE_BITS(tsk->event_group, GMF_TASK_STOP_BIT, tsk->api_sync_time) == false) { + ESP_LOGE(TAG, "Stop timeout,[%s,%p]", OBJ_GET_TAG((esp_gmf_obj_handle_t)tsk), tsk); + esp_gmf_oal_mutex_unlock(tsk->lock); + tsk->_stop = 0; + return ESP_GMF_ERR_TIMEOUT; + } + tsk->_stop = 0; + esp_gmf_oal_mutex_unlock(tsk->lock); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_task_pause(esp_gmf_task_handle_t handle) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_task_t *tsk = (esp_gmf_task_t *)handle; + + esp_gmf_oal_mutex_lock(tsk->lock); + ESP_LOGD(TAG, "%s, task:%s-%p, st:%s", __func__, OBJ_GET_TAG((esp_gmf_obj_handle_t)tsk), tsk, esp_gmf_event_get_state_str(tsk->state)); + if ((tsk->state == ESP_GMF_EVENT_STATE_STOPPED) + || (tsk->state == ESP_GMF_EVENT_STATE_PAUSED) + || (tsk->state == ESP_GMF_EVENT_STATE_FINISHED) + || (tsk->state == ESP_GMF_EVENT_STATE_ERROR)) { + esp_gmf_oal_mutex_unlock(tsk->lock); + ESP_LOGW(TAG, "Without pause on %s, [%s,%p]", esp_gmf_event_get_state_str(tsk->state), OBJ_GET_TAG((esp_gmf_obj_handle_t)tsk), tsk); + return ESP_GMF_ERR_OK; + } + + if ((tsk->state != ESP_GMF_EVENT_STATE_RUNNING)) { + esp_gmf_oal_mutex_unlock(tsk->lock); + ESP_LOGW(TAG, "Can't pause on %s, [%s,%p]", esp_gmf_event_get_state_str(tsk->state), OBJ_GET_TAG((esp_gmf_obj_handle_t)tsk), tsk); + return ESP_GMF_ERR_NOT_SUPPORT; + } + tsk->_pause = 1; + if (GMF_TASK_WAIT_FOR_MULTI_STATE_BITS(tsk->event_group, GMF_TASK_PAUSE_BIT | GMF_TASK_STOP_BIT, tsk->api_sync_time) == false) { + ESP_LOGE(TAG, "Pause timeout,[%s,%p]", OBJ_GET_TAG((esp_gmf_obj_handle_t)tsk), tsk); + esp_gmf_oal_mutex_unlock(tsk->lock); + tsk->_pause = 0; + return ESP_GMF_ERR_TIMEOUT; + } + tsk->_pause = 0; + // Only clear pause bits + GMF_TASK_CLR_STATE_BITS(tsk->event_group, GMF_TASK_PAUSE_BIT); + esp_gmf_oal_mutex_unlock(tsk->lock); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_task_resume(esp_gmf_task_handle_t handle) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_task_t *tsk = (esp_gmf_task_t *)handle; + esp_gmf_oal_mutex_lock(tsk->lock); + ESP_LOGD(TAG, "%s, task:%s-%p,st:%s", __func__, OBJ_GET_TAG((esp_gmf_obj_handle_t)tsk), tsk, esp_gmf_event_get_state_str(tsk->state)); + if ((tsk->state != ESP_GMF_EVENT_STATE_PAUSED)) { + esp_gmf_oal_mutex_unlock(tsk->lock); + ESP_LOGW(TAG, "Can't resume on %s, [%s,%p]", esp_gmf_event_get_state_str(tsk->state), OBJ_GET_TAG((esp_gmf_obj_handle_t)tsk), tsk); + return ESP_GMF_ERR_NOT_SUPPORT; + } + tsk->_pause = 0; + esp_gmf_task_release_signal(tsk, portMAX_DELAY); + if (GMF_TASK_WAIT_FOR_MULTI_STATE_BITS(tsk->event_group, GMF_TASK_RESUME_BIT | GMF_TASK_STOP_BIT, tsk->api_sync_time) == false) { + ESP_LOGE(TAG, "Resume timeout,[%s,%p]", OBJ_GET_TAG((esp_gmf_obj_handle_t)tsk), tsk); + esp_gmf_oal_mutex_unlock(tsk->lock); + return ESP_GMF_ERR_TIMEOUT; + } + GMF_TASK_CLR_STATE_BITS(tsk->event_group, GMF_TASK_RESUME_BIT); + esp_gmf_oal_mutex_unlock(tsk->lock); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_task_reset(esp_gmf_task_handle_t handle) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_task_t *tsk = (esp_gmf_task_t *)handle; + tsk->state = ESP_GMF_EVENT_STATE_INITIALIZED; + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_task_set_timeout(esp_gmf_task_handle_t handle, int wait_ms) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_task_t *tsk = (esp_gmf_task_t *)handle; + tsk->api_sync_time = wait_ms / portTICK_PERIOD_MS; + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_task_get_state(esp_gmf_task_handle_t handle, esp_gmf_event_state_t *state) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_task_t *tsk = (esp_gmf_task_t *)handle; + if (state) { + *state = tsk->state; + return ESP_GMF_ERR_OK; + } + return ESP_GMF_ERR_INVALID_ARG; +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/src/esp_gmf_video_element.c b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/src/esp_gmf_video_element.c new file mode 100644 index 0000000..88170de --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/src/esp_gmf_video_element.c @@ -0,0 +1,63 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * See LICENSE file for details. + */ + +#include +#include "esp_gmf_video_element.h" +#include "esp_gmf_oal_mem.h" +#include "esp_gmf_oal_mutex.h" +#include "esp_log.h" + +static const char *TAG = "ESP_GMF_VID_ELEMENT"; + +esp_gmf_err_t esp_gmf_video_el_init(esp_gmf_video_element_handle_t handle, esp_gmf_element_cfg_t *config) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, config, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_video_element_t *vid = (esp_gmf_video_element_t *)handle; + config->ctx = (void *)vid; + esp_gmf_element_init(&vid->base, config); + vid->lock = esp_gmf_oal_mutex_create(); + ESP_GMF_MEM_CHECK(TAG, vid->lock, { + esp_gmf_element_deinit(&vid->base); + esp_gmf_oal_free(vid); + return ESP_GMF_ERR_MEMORY_LACK; + }); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_video_el_get_src_info(esp_gmf_video_element_handle_t handle, esp_gmf_info_video_t *info) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, info, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_video_element_t *vid = (esp_gmf_video_element_t *)handle; + esp_gmf_oal_mutex_lock(vid->lock); + memcpy(info, &vid->src_info, sizeof(esp_gmf_info_video_t)); + esp_gmf_oal_mutex_unlock(vid->lock); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_video_el_set_src_info(esp_gmf_video_element_handle_t handle, esp_gmf_info_video_t *info) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + ESP_GMF_NULL_CHECK(TAG, info, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_video_element_t *vid = (esp_gmf_video_element_t *)handle; + esp_gmf_oal_mutex_lock(vid->lock); + memcpy(&vid->src_info, info, sizeof(esp_gmf_info_video_t)); + esp_gmf_oal_mutex_unlock(vid->lock); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_t esp_gmf_video_el_deinit(esp_gmf_video_element_handle_t handle) +{ + ESP_GMF_NULL_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG); + esp_gmf_video_element_t *vid = (esp_gmf_video_element_t *)handle; + esp_gmf_oal_mutex_lock(vid->lock); + esp_gmf_element_deinit(&vid->base); + esp_gmf_oal_mutex_unlock(vid->lock); + esp_gmf_oal_mutex_destroy(vid->lock); + return ESP_GMF_ERR_OK; +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/CMakeLists.txt b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/CMakeLists.txt new file mode 100644 index 0000000..4dd0914 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/CMakeLists.txt @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 3.16) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) + +set(EXTRA_COMPONENT_DIRS ${EXTRA_COMPONENT_DIRS} "../../gmf_core") + +project(test_gmf_core) diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/README.md b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/README.md new file mode 100644 index 0000000..bae980c --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/README.md @@ -0,0 +1 @@ +This is ESP GMF unit test diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/components/test_utils/CMakeLists.txt b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/components/test_utils/CMakeLists.txt new file mode 100644 index 0000000..8bf1f0e --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/components/test_utils/CMakeLists.txt @@ -0,0 +1,9 @@ +set(srcs "memory_checks.c" + "test_runner.c" + "test_utils.c") + +idf_component_register(SRCS ${srcs} + INCLUDE_DIRS include + REQUIRES esp_partition cmock newlib + PRIV_REQUIRES perfmon esp_netif) +target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format") diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/components/test_utils/Kconfig b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/components/test_utils/Kconfig new file mode 100644 index 0000000..1b68b3e --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/components/test_utils/Kconfig @@ -0,0 +1,36 @@ +menu "IDF unit test" + + config UNITY_FREERTOS_PRIORITY + int "Priority of Unity test task" + default 5 + + config UNITY_FREERTOS_CPU + int "CPU to run Unity test task on" + default 0 + + config UNITY_FREERTOS_STACK_SIZE + int "Stack size of Unity test task, in bytes" + default 8192 + + config UNITY_WARN_LEAK_LEVEL_GENERAL + int "Leak warning level" + default 255 + + config UNITY_CRITICAL_LEAK_LEVEL_GENERAL + int "Critical leak" + default 1200 + + config UNITY_CRITICAL_LEAK_LEVEL_LWIP + int "Critical leak for UT which use LWIP component" + default 4095 + + config UNITY_IGNORE_PERFORMANCE_TESTS + bool "Ignore performance test results" + default y if IDF_ENV_FPGA + default n + help + If set, performance tests that use TEST_PERFORMANCE_LESS_THAN and + TEST_PERFORMANCE_GREATER_THAN macros will log the performance value + but not fail the test if the threshold is not met. + +endmenu diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/components/test_utils/include/memory_checks.h b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/components/test_utils/include/memory_checks.h new file mode 100644 index 0000000..ef8904e --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/components/test_utils/include/memory_checks.h @@ -0,0 +1,91 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include "esp_err.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * @brief Leak for components + */ +typedef enum { + ESP_COMP_LEAK_GENERAL = 0, /**< Leak by default */ + ESP_COMP_LEAK_LWIP, /**< Leak for LWIP */ + ESP_COMP_LEAK_NVS, /**< Leak for NVS */ + ESP_COMP_LEAK_ALL, /**< Use for getting the summary leak level */ +} esp_comp_leak_t; + +/** + * @brief Type of a leak threshold + */ +typedef enum { + ESP_LEAK_TYPE_WARNING = 0, /**< Warning level of leak */ + ESP_LEAK_TYPE_CRITICAL, /**< Critical level of leak */ + ESP_LEAK_TYPE_MAX, /**< Max number of leak levels for all components/levels */ +} esp_type_leak_t; + +/** + * @brief Adjust the memory leak thresholds for unit tests. + * + * Usually, unit tests will check if memory is leaked. Some functionality used by unit tests may unavoidably + * leak memory. This is why there is a default threshold for memory leaks (currently 1200 bytes). + * Within this range, the number of bytes leaked will be visually reported on the terminal, but no test failure will + * be triggered. Any memory leak above the default threshold will trigger a unit test failure. + * This function allows to adjust that memory leak threshold. + * + * @param leak_level Maximum allowed memory leak which will not trigger a unit test failure. + * @param type_of_leak There are two types of leak thresholds: critical and warning. Only the + * critical threshold will trigger a unit test failure if exceeded. + * @param component Thresholds can be set in general or for specific components. Note that this argument + * is not checked. + * + * @return + */ +esp_err_t test_utils_set_leak_level(size_t leak_level, esp_type_leak_t type_of_leak, esp_comp_leak_t component); + +/** + * @brief Return the memory leak thresholds for unit tests for a leak type and component. + * + * For more information, see \c test_utils_set_leak_level above. + * + * @param type_of_leak Warning or Critical + * @param component The component for which to return the leak threshold. + */ +size_t test_utils_get_leak_level(esp_type_leak_t type_of_leak, esp_comp_leak_t component); + +/** + * @brief Start/Restart memory leak checking. + * + * Records the current free memory values at time of calling. After the test case, it may be checked with + * \c test_utils_finish_and_evaluate_leaks. + * + * If this function is called repeatedly, only the free memory values at the last time of calling will prevail + * as reference. + */ +void test_utils_record_free_mem(void); + +/** + * @brief Evaluate memory leak checking according to the provided thresholds. + * + * If the current memory leak level (counted from the last time calling \c test_utils_record_free_mem() ) exceeds + * \c critical_threshold, a unit test failure will be triggered. If it exceeds only the warning, + * a warning message will be issued. + */ +void test_utils_finish_and_evaluate_leaks(size_t warn_threshold, size_t critical_threshold); + +/** + * @brief Helper function to setup and initialize heap tracing. + */ +void setup_heap_record(void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/components/test_utils/include/test_utils.h b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/components/test_utils/include/test_utils.h new file mode 100644 index 0000000..9fc629d --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/components/test_utils/include/test_utils.h @@ -0,0 +1,235 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +// Utilities for esp-idf unit tests + +#include +#include +#include "sdkconfig.h" +#include "unity.h" +#include "soc/soc_caps.h" +/* include performance pass standards header file */ +// #include "idf_performance.h" +// #include "idf_performance_target.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* For performance check with unity test on IDF */ +/* These macros should only be used with ESP-IDF. + * To use performance check, we need to first define pass standard in idf_performance.h. + */ + +// macros call this to expand an argument instead of directly converting into str +#define PERFORMANCE_STR(s) #s +// macros call this to contact strings after expanding them +#define PERFORMANCE_CON(a, b) _PERFORMANCE_CON(a, b) +#define _PERFORMANCE_CON(a, b) a##b + +#if !CONFIG_UNITY_IGNORE_PERFORMANCE_TESTS +#define _TEST_PERFORMANCE_ASSERT TEST_ASSERT +#else +#define _TEST_PERFORMANCE_ASSERT(ARG) printf("Ignoring performance test [%s]\n", PERFORMANCE_STR(ARG)) +#endif /* !CONFIG_UNITY_IGNORE_PERFORMANCE_TESTS */ + +#define TEST_PERFORMANCE_LESS_THAN(name, value_fmt, value, ...) do { \ + IDF_LOG_PERFORMANCE(#name, value_fmt, value, ##__VA_ARGS__); \ + _TEST_PERFORMANCE_ASSERT(value < PERFORMANCE_CON(IDF_PERFORMANCE_MAX_, name)); \ +} while (0) + +#define TEST_PERFORMANCE_GREATER_THAN(name, value_fmt, value, ...) do { \ + IDF_LOG_PERFORMANCE(#name, value_fmt, value, ##__VA_ARGS__); \ + _TEST_PERFORMANCE_ASSERT(value > PERFORMANCE_CON(IDF_PERFORMANCE_MIN_, name)); \ +} while (0) + +/* Macros to be used when performance is calculated using the cache compensated timer + will not assert if ccomp not supported */ +#if SOC_CCOMP_TIMER_SUPPORTED +#define TEST_PERFORMANCE_CCOMP_GREATER_THAN(name, value_fmt, value, ...) \ + TEST_PERFORMANCE_GREATER_THAN(name, value_fmt, value, ##__VA_ARGS__) +#define TEST_PERFORMANCE_CCOMP_LESS_THAN(name, value_fmt, value, ...) \ + TEST_PERFORMANCE_LESS_THAN(name, value_fmt, value, ##__VA_ARGS__) +#else +#define TEST_PERFORMANCE_CCOMP_GREATER_THAN(name, value_fmt, value, ...) \ + IDF_LOG_PERFORMANCE(#name, value_fmt, value, ##__VA_ARGS__) +#define TEST_PERFORMANCE_CCOMP_LESS_THAN(name, value_fmt, value, ...) \ + IDF_LOG_PERFORMANCE(#name, value_fmt, value, ##__VA_ARGS__) +#endif // SOC_CCOMP_TIMER_SUPPORTED + +/* @brief macro to print IDF performance + * @param item : performance item name. a string pointer. + * @param value_fmt: print format and unit of the value, for example: "%02fms", "%dKB" + * @param value : the performance value. + */ +#define IDF_LOG_PERFORMANCE(item, value_fmt, value, ...) \ + printf("[Performance][%s]: " value_fmt "\n", item, value, ##__VA_ARGS__) + +/* Some definitions applicable to Unity running in FreeRTOS */ +#define UNITY_FREERTOS_PRIORITY CONFIG_UNITY_FREERTOS_PRIORITY +#define UNITY_FREERTOS_CPU CONFIG_UNITY_FREERTOS_CPU +#define UNITY_FREERTOS_STACK_SIZE CONFIG_UNITY_FREERTOS_STACK_SIZE + +/* Return the 'flash_test' custom data partition (type 0x55) + defined in the custom partition table. +*/ +const esp_partition_t *get_test_data_partition(void); + +/** + * @brief Initialize reference clock + * + * Reference clock provides timestamps at constant 1 MHz frequency, even when + * the APB frequency is changing. + */ +void ref_clock_init(void); + +/** + * @brief Deinitialize reference clock + */ +void ref_clock_deinit(void); + +/** + * @brief Get reference clock timestamp + * @return + */ +uint64_t ref_clock_get(void); + +/** + * @brief Entry point of the test application + * + * Starts Unity test runner in a separate task and returns. + */ +void test_main(void); + +void unity_reset_leak_checks(void); + +/** + * @brief Call this function from a test case which requires TCP/IP or + * LWIP functionality. + * + * @note This should be the first function the test case calls, as it will + * allocate memory on first use (and also reset the test case leak checker). + */ +void test_case_uses_tcpip(void); + +/** + * @brief wait for signals with parameters. + * + * for multiple devices test cases, DUT might need to wait for other DUTs before continue testing. + * As all DUTs are independent, need user (or test script) interaction to make test synchronized. + * + * Here we provide signal functions for this. + * For example, we're testing GPIO, DUT1 has one pin connect to with DUT2. + * DUT2 will output high level and then DUT1 will read input. + * DUT1 should call `unity_wait_for_signal("output high level");` before it reads input. + * DUT2 should call `unity_send_signal("output high level");` after it finished setting output high level. + * According to the console logs: + * + * DUT1 console: + * + * ``` + * Waiting for signal: [output high level]! + * Please press "Enter" key to once any board send this signal. + * ``` + * + * DUT2 console: + * + * ``` + * Send signal: [output high level]! + * ``` + * + * Then we press Enter key on DUT1's console, DUT1 starts to read input and then test success. + * + * Another example, we have 2 DUTs in multiple devices test, and DUT1 need to get DUT2's mac address to perform BT connection. + * DUT1 should call `unity_wait_for_signal_param("dut2 mac address", mac, 19);` to wait for DUT2's mac address. + * DUT2 should call `unity_send_signal_param("dut2 mac address", "10:20:30:40:50:60");` to send to DUT1 its mac address. + * According to the console logs: + * + * DUT1 console: + * + * ``` + * Waiting for signal: [dut2 mac address]! + * Please input parameter value from any board send this signal and press "Enter" key. + * ``` + * + * DUT2 console: + * + * ``` + * Send signal: [dut2 mac address][10:20:30:40:50:60]! + * ``` + * + * @param signal_name signal name which DUT expected to wait before proceed testing + * @param parameter_buf buffer to receive parameter + * @param buf_len length of parameter_buf. + * Currently we have a limitation that it will write 1 extra byte at the end of string. + * We need to use a buffer with 2 bytes longer than actual string length. + */ +void unity_wait_for_signal_param(const char *signal_name, char *parameter_buf, uint8_t buf_len); + +/** + * @brief wait for signals. + * + * @param signal_name signal name which DUT expected to wait before proceed testing + */ +static inline void unity_wait_for_signal(const char *signal_name) +{ + unity_wait_for_signal_param(signal_name, NULL, 0); +} + +/** + * @brief DUT send signal and pass parameter to other devices. + * + * @param signal_name signal name which DUT send once it finished preparing. + * @param parameter a string to let remote device to receive. + */ +void unity_send_signal_param(const char *signal_name, const char *parameter); + +/** + * @brief DUT send signal with parameter. + * + * @param signal_name signal name which DUT send once it finished preparing. + */ +static inline void unity_send_signal(const char *signal_name) +{ + unity_send_signal_param(signal_name, NULL); +} + +/** + * @brief convert mac string to mac address + * + * @param mac_str MAC address string with format "xx:xx:xx:xx:xx:xx" + * @param[out] mac_addr store converted MAC address + */ +bool unity_util_convert_mac_from_string(const char *mac_str, uint8_t *mac_addr); + +typedef struct test_utils_exhaust_memory_record_s *test_utils_exhaust_memory_rec; + +/** + * Limit the largest free block of memory with a particular capability set to + * 'limit' bytes (meaning an allocation of 'limit' should succeed at least once, + * but any allocation of more bytes will fail.) + * + * Returns a record pointer which needs to be passed back in to test_utils_free_exhausted_memory + * before the test completes, to avoid a major memory leak. + * + * @param caps Capabilities of memory to exhause + * @param limit The size to limit largest free block to + * @return + */ +test_utils_exhaust_memory_rec test_utils_exhaust_memory(uint32_t caps, size_t limit); + +/** + * Call to free memory which was taken up by test_utils_exhaust_memory() call + * + * @param rec Result previously returned from test_utils_exhaust_memory() + */ +void test_utils_free_exhausted_memory(test_utils_exhaust_memory_rec rec); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/components/test_utils/memory_checks.c b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/components/test_utils/memory_checks.c new file mode 100644 index 0000000..7baf467 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/components/test_utils/memory_checks.c @@ -0,0 +1,104 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "esp_heap_caps.h" +#include "unity.h" +#include "memory_checks.h" +#ifdef CONFIG_HEAP_TRACING +#include "esp_heap_trace.h" +#endif /* CONFIG_HEAP_TRACING */ + +static size_t before_free_8bit; +static size_t before_free_32bit; + +static size_t test_unity_leak_level[ESP_LEAK_TYPE_MAX][ESP_COMP_LEAK_ALL] = {0}; + +esp_err_t test_utils_set_leak_level(size_t leak_level, esp_type_leak_t type_of_leak, esp_comp_leak_t component) +{ + if (type_of_leak >= ESP_LEAK_TYPE_MAX || component >= ESP_COMP_LEAK_ALL) { + return ESP_ERR_INVALID_ARG; + } + test_unity_leak_level[type_of_leak][component] = leak_level; + return ESP_OK; +} + +size_t test_utils_get_leak_level(esp_type_leak_t type_of_leak, esp_comp_leak_t component) +{ + size_t leak_level = 0; + if (type_of_leak >= ESP_LEAK_TYPE_MAX || component > ESP_COMP_LEAK_ALL) { + leak_level = 0; + } else { + if (component == ESP_COMP_LEAK_ALL) { + for (int comp = 0; comp < ESP_COMP_LEAK_ALL; ++comp) { + leak_level += test_unity_leak_level[type_of_leak][comp]; + } + } else { + leak_level = test_unity_leak_level[type_of_leak][component]; + } + } + return leak_level; +} + +void test_utils_record_free_mem(void) +{ + before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT); + before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT); +} + +void setup_heap_record(void) +{ +#ifdef CONFIG_HEAP_TRACING + const size_t num_heap_records = 80; + static heap_trace_record_t *record_buffer; + if (!record_buffer) { + record_buffer = malloc(sizeof(heap_trace_record_t) * num_heap_records); + assert(record_buffer); + heap_trace_init_standalone(record_buffer, num_heap_records); + } +#endif /* CONFIG_HEAP_TRACING */ +} + +static void check_leak(size_t before_free, + size_t after_free, + const char *type, + size_t warn_threshold, + size_t critical_threshold) +{ + int free_delta = (int)after_free - (int)before_free; + printf("MALLOC_CAP_%s usage: Free memory delta: %d Leak threshold: -%u \n", + type, + free_delta, + critical_threshold); + + if (free_delta > 0) { + return; // free memory went up somehow + } + + size_t leaked = (size_t)(free_delta * -1); + if (leaked <= warn_threshold) { + return; + } else { + printf("The test leaked more memory than warn threshold: Leaked: %d Warn threshold: -%u \n", + leaked, + warn_threshold); + } + + printf("MALLOC_CAP_%s %s leak: Before %u bytes free, After %u bytes free (delta %u)\n", + type, + leaked <= critical_threshold ? "potential" : "critical", + before_free, after_free, leaked); + fflush(stdout); + TEST_ASSERT_MESSAGE(leaked <= critical_threshold, "The test leaked too much memory"); +} + +void test_utils_finish_and_evaluate_leaks(size_t warn_threshold, size_t critical_threshold) +{ + size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT); + size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT); + check_leak(before_free_8bit, after_free_8bit, "8BIT", warn_threshold, critical_threshold); + check_leak(before_free_32bit, after_free_32bit, "32BIT", warn_threshold, critical_threshold); +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/components/test_utils/test/CMakeLists.txt b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/components/test_utils/test/CMakeLists.txt new file mode 100644 index 0000000..df857ed --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/components/test_utils/test/CMakeLists.txt @@ -0,0 +1,3 @@ +idf_component_register(SRC_DIRS "." + PRIV_INCLUDE_DIRS "." + PRIV_REQUIRES cmock test_utils perfmon esp_timer) diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/components/test_utils/test/leak_test.c b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/components/test_utils/test/leak_test.c new file mode 100644 index 0000000..be03a75 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/components/test_utils/test/leak_test.c @@ -0,0 +1,64 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +/** + Tests for a leak tag + */ + +#include +#include "unity.h" +#include "esp_heap_caps_init.h" +#include "esp_system.h" +#include + +static char *check_calloc(int size) +{ + char *arr = calloc(size, sizeof(char)); + TEST_ASSERT_NOT_NULL(arr); + return arr; +} + +TEST_CASE("Check for leaks (no leak)", "[test_utils]") +{ + char *arr = check_calloc(1000); + free(arr); +} + +TEST_CASE("Check for leaks (leak)", "[test_utils][ignore]") +{ + check_calloc(1000); +} + +TEST_CASE("Not check for leaks", "[test_utils][leaks]") +{ + check_calloc(1000); +} + +TEST_CASE("Set a leak level = 7016", "[test_utils][leaks=7016]") +{ + check_calloc(7000); +} + +static void test_fn(void) +{ + check_calloc(1000); +} + +TEST_CASE_MULTIPLE_STAGES("Not check for leaks in MULTIPLE_STAGES mode", "[test_utils][leaks]", test_fn, test_fn, test_fn); + +TEST_CASE_MULTIPLE_STAGES("Check for leaks in MULTIPLE_STAGES mode (leak)", "[test_utils][ignore]", test_fn, test_fn, test_fn); + +static void test_fn2(void) +{ + check_calloc(1000); + esp_restart(); +} + +static void test_fn3(void) +{ + check_calloc(1000); +} + +TEST_CASE_MULTIPLE_STAGES("Check for leaks in MULTIPLE_STAGES mode (manual reset)", "[test_utils][leaks][reset=SW_CPU_RESET, SW_CPU_RESET]", test_fn2, test_fn2, test_fn3); diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/components/test_utils/test_runner.c b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/components/test_utils/test_runner.c new file mode 100644 index 0000000..c6a138f --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/components/test_utils/test_runner.c @@ -0,0 +1,138 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "string.h" +#include "esp_heap_caps.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "unity.h" +#include "unity_test_runner.h" +#include "test_utils.h" +#include "esp_newlib.h" +#include "memory_checks.h" +#include "esp_log.h" + +#ifdef CONFIG_HEAP_TRACING +#include "esp_heap_trace.h" +#endif /* CONFIG_HEAP_TRACING */ + +static void unity_task(void *pvParameters) +{ + // Delay a bit to let the main task and any other startup tasks be deleted + vTaskDelay(pdMS_TO_TICKS(50)); + // Start running unity (prints test menu and doesn't return) + unity_run_menu(); +} + +void test_main(void) +{ + // Note: if unpinning this task, change the way run times are calculated in + // unity_port_esp32.c + xTaskCreatePinnedToCore(unity_task, "unityTask", UNITY_FREERTOS_STACK_SIZE, NULL, + UNITY_FREERTOS_PRIORITY, NULL, UNITY_FREERTOS_CPU); +} + +/* setUp runs before every test */ +void setUp(void) +{ +// If heap tracing is enabled in kconfig, leak trace the test +#ifdef CONFIG_HEAP_TRACING + setup_heap_record(); +#endif /* CONFIG_HEAP_TRACING */ + + printf("%s", ""); /* sneakily lazy-allocate the reent structure for this test task */ + +#ifdef CONFIG_APP_BUILD_USE_FLASH_SECTIONS + /* TODO: add sufficient startup code in case of building an ELF file, so that + * flash cache is initialized and can work in such mode. + * For now this is disabled to allow running unit tests which don't require + * flash cache related operations. + */ + // get_test_data_partition(); /* allocate persistent partition table structures */ +#endif // CONFIG_APP_BUILD_USE_FLASH_SECTIONS +#ifdef CONFIG_HEAP_TRACING + heap_trace_start(HEAP_TRACE_LEAKS); +#endif /* CONFIG_HEAP_TRACING */ + test_utils_record_free_mem(); + test_utils_set_leak_level(CONFIG_UNITY_CRITICAL_LEAK_LEVEL_GENERAL, ESP_LEAK_TYPE_CRITICAL, ESP_COMP_LEAK_GENERAL); + test_utils_set_leak_level(CONFIG_UNITY_WARN_LEAK_LEVEL_GENERAL, ESP_LEAK_TYPE_WARNING, ESP_COMP_LEAK_GENERAL); + test_utils_set_leak_level(0, ESP_LEAK_TYPE_CRITICAL, ESP_COMP_LEAK_LWIP); +} + +typedef enum { + NO_LEAK_CHECK, + DEFAULT_LEAK_CHECK, + SPECIAL_LEAK_CHECK +} leak_check_type_t; + +/** + * It is possible to specify the maximum allowed memory leak level directly in the test case + * or disable leak checking for a test case. + * This function checks if this is the case and return the appropriate return value. + * If a custom leak level has been specified, that custom threshold is written to the value pointed by threshold. + */ +static leak_check_type_t leak_check_required(size_t *threshold) +{ + if (Unity.CurrentDetail1 != NULL) { + const char *leaks = "[leaks"; + const int len_leaks = strlen(leaks); + const char *sub_leaks = strstr(Unity.CurrentDetail1, leaks); + if (sub_leaks != NULL) { + if (sub_leaks[len_leaks] == ']') { + return NO_LEAK_CHECK; + } else if (sub_leaks[len_leaks] == '=') { + *threshold = strtol(&sub_leaks[len_leaks + 1], NULL, 10); + return SPECIAL_LEAK_CHECK; + } + } + } + return DEFAULT_LEAK_CHECK; +} + +/* tearDown runs after every test */ +void tearDown(void) +{ + /* some FreeRTOS stuff is cleaned up by idle task */ + vTaskDelay(5); + + /* clean up some of the newlib's lazy allocations */ + esp_reent_cleanup(); + + /* We want the teardown to have this file in the printout if TEST_ASSERT fails */ + const char *real_testfile = Unity.TestFile; + Unity.TestFile = __FILE__; + + /* check if unit test has caused heap corruption in any heap */ + TEST_ASSERT_MESSAGE(heap_caps_check_integrity(MALLOC_CAP_INVALID, true), "The test has corrupted the heap"); + + /* check for leaks */ +#ifdef CONFIG_HEAP_TRACING + heap_trace_stop(); + heap_trace_dump(); +#endif /* CONFIG_HEAP_TRACING */ + + size_t leak_threshold_critical = 0; + size_t leak_threshold_warning = 0; + leak_check_type_t check_type = leak_check_required(&leak_threshold_critical); + + // In the "special case", only one level can be passed directly from the test case. + // Hence, we set both warning and critical leak levels to that same value here + leak_threshold_warning = leak_threshold_critical; + + if (check_type == NO_LEAK_CHECK) { + // do not check + } else if (check_type == SPECIAL_LEAK_CHECK) { + test_utils_finish_and_evaluate_leaks(leak_threshold_warning, leak_threshold_critical); + } else if (check_type == DEFAULT_LEAK_CHECK) { + test_utils_finish_and_evaluate_leaks(test_utils_get_leak_level(ESP_LEAK_TYPE_WARNING, ESP_COMP_LEAK_ALL), + test_utils_get_leak_level(ESP_LEAK_TYPE_CRITICAL, ESP_COMP_LEAK_ALL)); + } else { + assert(false); // coding error + } + + Unity.TestFile = real_testfile; // go back to the real filename +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/components/test_utils/test_utils.c b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/components/test_utils/test_utils.c new file mode 100644 index 0000000..27aa090 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/components/test_utils/test_utils.c @@ -0,0 +1,145 @@ +/* + * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "unity.h" +#include "test_utils.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_netif.h" +#include "lwip/sockets.h" +#include "sdkconfig.h" +#include "memory_checks.h" + +const esp_partition_t *get_test_data_partition(void) +{ + /* This finds "flash_test" partition defined in partition_table_unit_test_app.csv */ + const esp_partition_t *result = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, + ESP_PARTITION_SUBTYPE_ANY, "flash_test"); + TEST_ASSERT_NOT_NULL(result); /* means partition table set wrong */ + return result; +} + +void test_case_uses_tcpip(void) +{ + // Can be called more than once, does nothing on subsequent calls + esp_netif_init(); + + // Allocate all sockets then free them + // (First time each socket is allocated some one-time allocations happen.) + int sockets[CONFIG_LWIP_MAX_SOCKETS]; + for (int i = 0; i < CONFIG_LWIP_MAX_SOCKETS; i++) { + int type = (i % 2 == 0) ? SOCK_DGRAM : SOCK_STREAM; + int family = (i % 3 == 0) ? PF_INET6 : PF_INET; + sockets[i] = socket(family, type, IPPROTO_IP); + } + for (int i = 0; i < CONFIG_LWIP_MAX_SOCKETS; i++) { + close(sockets[i]); + } + + // Allow LWIP tasks to finish initialising themselves + vTaskDelay(25 / portTICK_PERIOD_MS); + + printf("Note: esp_netif_init() has been called. Until next reset, TCP/IP task will periodicially allocate memory and consume CPU time.\n"); + + // Reset the leak checker as LWIP allocates a lot of memory on first run + test_utils_record_free_mem(); + test_utils_set_leak_level(0, ESP_LEAK_TYPE_CRITICAL, ESP_COMP_LEAK_GENERAL); + test_utils_set_leak_level(CONFIG_UNITY_CRITICAL_LEAK_LEVEL_LWIP, ESP_LEAK_TYPE_CRITICAL, ESP_COMP_LEAK_LWIP); +} + +// wait user to send "Enter" key or input parameter +static void wait_user_control(char *parameter_buf, uint8_t buf_len) +{ + char *buffer = parameter_buf; + char sign[5]; + uint8_t buffer_len = buf_len - 1; + + if (parameter_buf == NULL) { + buffer = sign; + buffer_len = sizeof(sign) - 1; + } + // workaround that unity_gets (esp_rom_uart_rx_string) will not set '\0' correctly + bzero(buffer, buffer_len); + + unity_gets(buffer, buffer_len); +} + +// signal functions, used for sync between unity DUTs for multiple devices cases +void unity_wait_for_signal_param(const char *signal_name, char *parameter_buf, uint8_t buf_len) +{ + printf("Waiting for signal: [%s]!\n", signal_name); + if (parameter_buf == NULL) { + printf("Please press \"Enter\" key once any board send this signal.\n"); + } else { + printf("Please input parameter value from any board send this signal and press \"Enter\" key.\n"); + } + wait_user_control(parameter_buf, buf_len); +} + +void unity_send_signal_param(const char *signal_name, const char *parameter) +{ + if (parameter == NULL) { + printf("Send signal: [%s]!\n", signal_name); + } else { + printf("Send signal: [%s][%s]!\n", signal_name, parameter); + } +} + +bool unity_util_convert_mac_from_string(const char *mac_str, uint8_t *mac_addr) +{ + uint8_t loop = 0; + uint8_t tmp = 0; + const char *start; + char *stop; + + for (loop = 0; loop < 6; loop++) { + start = mac_str + loop * 3; + tmp = strtol(start, &stop, 16); + if (stop - start == 2 && (*stop == ':' || (*stop == 0 && loop == 5))) { + mac_addr[loop] = tmp; + } else { + return false; + } + } + return true; +} + +#define EXHAUST_MEMORY_ENTRIES 100 + +struct test_utils_exhaust_memory_record_s { + int *entries[EXHAUST_MEMORY_ENTRIES]; +}; + +test_utils_exhaust_memory_rec test_utils_exhaust_memory(uint32_t caps, size_t limit) +{ + int idx = 0; + test_utils_exhaust_memory_rec rec = calloc(1, sizeof(struct test_utils_exhaust_memory_record_s)); + TEST_ASSERT_NOT_NULL_MESSAGE(rec, "test_utils_exhaust_memory: not enough free memory to allocate record structure!"); + + while (idx < EXHAUST_MEMORY_ENTRIES) { + size_t free_caps = heap_caps_get_largest_free_block(caps); + if (free_caps <= limit) { + return rec; // done! + } + rec->entries[idx] = heap_caps_malloc(free_caps - limit, caps); + TEST_ASSERT_NOT_NULL_MESSAGE(rec->entries[idx], + "test_utils_exhaust_memory: something went wrong while freeing up memory, is another task using heap?"); + heap_caps_check_integrity_all(true); + idx++; + } + + TEST_FAIL_MESSAGE("test_utils_exhaust_memory: The heap with the requested caps is too fragmented, increase EXHAUST_MEMORY_ENTRIES or defrag the heap!"); + abort(); +} + +void test_utils_free_exhausted_memory(test_utils_exhaust_memory_rec rec) +{ + for (int i = 0; i < EXHAUST_MEMORY_ENTRIES; i++) { + free(rec->entries[i]); + } + free(rec); +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/main/CMakeLists.txt b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/main/CMakeLists.txt new file mode 100644 index 0000000..38b62cc --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/main/CMakeLists.txt @@ -0,0 +1,22 @@ +idf_component_register(SRCS "test_gmf_core_main.c" + "test_cxx_build.cpp" + "./cases/gmf_element_test.c" + "./cases/gmf_task_test.c" + "./cases/gmf_io_test.c" + "./cases/gmf_ringbuf_test.c" + "./cases/gmf_pbuf_test.c" + "./cases/gmf_fifo_test.c" + "./cases/gmf_block_test.c" + "./cases/gmf_pool_test.c" + "./cases/gmf_method_test.c" + "./cases/gmf_cache_test.c" + "./cases/gmf_uri_test.c" + "./cases/gmf_caps_test.c" + "./cases/gmf_pipeline_test.c" + "./common/gmf_ut_common.c" + "./common/gmf_fake_dec.c" + "./common/gmf_fake_io.c" + "./common/mock_dec.c" + INCLUDE_DIRS "common" + PRIV_REQUIRES unity soc esp_hw_support fatfs vfs sdmmc driver gmf_core test_utils + WHOLE_ARCHIVE) diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/main/cases/gmf_block_test.c b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/main/cases/gmf_block_test.c new file mode 100644 index 0000000..6e2f034 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/main/cases/gmf_block_test.c @@ -0,0 +1,455 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "unity.h" +#include "string.h" +#include "freertos/FreeRTOS.h" +#include "esp_private/esp_clk.h" +#include "driver/sdmmc_host.h" +#include "esp_log.h" + +#include "esp_random.h" +#include "esp_gmf_oal_mem.h" +#include "gmf_ut_common.h" +#include "esp_gmf_block.h" + +static const char *TAG = "TEST_ESP_GMF_BLOCK"; +static bool is_done; +static bool is_abort_read; +static bool is_abort_write; +static uint8_t block_size_type; // 1 for random size; 2 for specific size; others is fixed size +static bool use_random_delay; +uint32_t specific_block_size = 0; + +static const char *file_name = "/sdcard/gmf_ut_test2.mp3"; +static const char *file2_name = "/sdcard/gmf_ut_test_out.mp3"; +int file2_indx = 0; +char file2_path[100] = ""; + +static void acquire_write_task(void *param) +{ + esp_gmf_block_handle_t bk = (esp_gmf_block_handle_t)param; + FILE *f = fopen(file_name, "rb"); + if (f == NULL) { + ESP_LOGE(TAG, "Open file failed on %s, %d", file_name, __LINE__); + is_done = true; + vTaskDelete(NULL); + } + uint8_t *buf = NULL; + uint32_t start_cnt = 0; + uint64_t total_cnt = 0; + uint32_t wanted_size = 2000; + esp_gmf_data_bus_block_t blk_buf = {0}; + int result = 0; + uint32_t file_sz = 0; + bool run = true; + + fseek(f, 0, SEEK_END); + file_sz = ftell(f); + fseek(f, 0, SEEK_SET); + ESP_LOGI(TAG, "Going to read file, para:%p, file size:%ld,", param, file_sz); + + while (run) { + uint32_t start_time = 0; + do { + start_time = (esp_random() & 0x1F); + } while (start_time == 0); + switch (block_size_type) { + case 1: + wanted_size = start_time * 70 > 4000 ? 4000 : start_time * 20; + break; + case 2: + wanted_size = specific_block_size; + break; + default: + break; + } + + start_cnt = esp_clk_rtc_time(); + result = esp_gmf_block_acquire_write(bk, &blk_buf, wanted_size, portMAX_DELAY); + + total_cnt += (esp_clk_rtc_time() - start_cnt); + if (result >= ESP_OK) { + buf = blk_buf.buf; + } else { + if (result == ESP_GMF_IO_ABORT) { + is_abort_write = true; + } + ESP_LOGE(TAG, "Acquire write quit,ret:%d", result); + break; + } + if (use_random_delay) { + vTaskDelay(start_time * 4 / portTICK_PERIOD_MS); + } + int ret = fread(buf, 1, blk_buf.buf_length, f); + + file_sz += ret; + blk_buf.valid_size = ret; + esp_gmf_block_release_write(bk, &blk_buf, 0); + + if (ret == 0 || (blk_buf.buf_length != ret)) { + ESP_LOGI(TAG, "File read finished, size:%ld ret:%d", file_sz, ret); + esp_gmf_block_done_write(bk); + run = false; + } + if (ret != wanted_size) { + ESP_LOGD(TAG, "W2:%ld, ret:%d,sz:%ld,%p-%d-%d", wanted_size, ret, file_sz, blk_buf.buf, blk_buf.valid_size, blk_buf.is_last); + blk_buf.is_last = true; + } else { + ESP_LOGD(TAG, "W:%ld, ret:%d, file:%ld,%p-%d-%d", wanted_size, ret, file_sz, blk_buf.buf, blk_buf.valid_size, blk_buf.is_last); + } + } + ESP_LOGI(TAG, "Done to acquire write, ticks:%lld us,file_sz:%ld", total_cnt, file_sz); + fclose(f); + vTaskDelete(NULL); +} + +static void acquire_read_task(void *param) +{ + esp_gmf_block_handle_t bk = (esp_gmf_block_handle_t)param; + sprintf(file2_path, "%s%x", file2_name, file2_indx); + ESP_LOGI(TAG, "Going to write file, para:%p, path:%s", param, file2_path); + FILE *f = fopen(file2_path, "wb+"); + if (f == NULL) { + ESP_LOGE(TAG, "Open file failed on %s, %d", file2_path, __LINE__); + is_done = true; + vTaskDelete(NULL); + } + int wanted_size = 2000; + uint8_t *buf = NULL; + uint32_t start_cnt = 0; + uint64_t total_cnt = 0; + esp_gmf_data_bus_block_t blk_buf = {0}; + uint32_t file_sz = 0; + bool run = true; + while (run) { + uint32_t start_time = 0; + do { + start_time = (esp_random() & 0x3F); + } while (start_time == 0); + switch (block_size_type) { + case 1: + wanted_size = (start_time * 40) > 3000 ? 3000 : start_time * 20; + break; + case 2: + wanted_size = specific_block_size; + break; + default: + break; + } + + start_cnt = esp_clk_rtc_time(); + int ret = esp_gmf_block_acquire_read(bk, &blk_buf, wanted_size, portMAX_DELAY); + total_cnt += (esp_clk_rtc_time() - start_cnt); + + if (use_random_delay) { + vTaskDelay(start_time * 1 / portTICK_PERIOD_MS); + } + + if (ret < ESP_OK) { + if (ret == ESP_GMF_IO_ABORT) { + is_abort_read = true; + } + ESP_LOGE(TAG, "Acquire read quit,ret:%d", ret); + break; + } + file_sz += blk_buf.valid_size; + buf = blk_buf.buf; + ret = fwrite(buf, 1, blk_buf.valid_size, f); + + if (blk_buf.is_last == true) { + run = false; + } + // printf("%02x ", buf[0]); + esp_gmf_block_release_read(bk, &blk_buf, 0); + blk_buf.valid_size = 0; + // ESP_LOGI(TAG, "R:%d, ret:%d, file:%ld, %p-%d-%d", wanted_size, ret, file_sz, blk_buf.buf, blk_buf.valid_size, blk_buf.is_last); + } + fclose(f); + ESP_LOGI(TAG, "Done to acquire read, ticks:%lld us,file_sz:%ld", total_cnt, file_sz); + is_done = true; + vTaskDelete(NULL); +} + +TEST_CASE("Read and write with RANDOM SIZE + NO DELAY on different task", "[ESP_GMF_BLOCK]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + // esp_log_level_set("ESP_GMF_BLOCK", ESP_LOG_DEBUG); + + sdmmc_card_t *card = NULL; + esp_gmf_ut_setup_sdmmc(&card); + + is_done = false; + esp_gmf_block_handle_t bk = NULL; + esp_gmf_block_create(2 * 1000, 4, &bk); + TEST_ASSERT_NOT_NULL(bk); + uint32_t start_cnt = 0; + uint32_t total_cnt = 0; + start_cnt = 0; + total_cnt = 0; + block_size_type = 1; + use_random_delay = false; + + + uint32_t task_prio[3][2] = {{0, 2}, {0, 2}, {0, 5}}; + + uint32_t loop_times = 12; + for (int i = 0; i < loop_times; ++i) { + file2_indx = i; + printf("\r\n\r\n ---------- RANDOM SIZE + NO DELAY, %d, priority:%ld, %ld ---------- \r\n", i, task_prio[i % 3][0], task_prio[i % 3][1]); + start_cnt = esp_clk_rtc_time(); + xTaskCreate(acquire_read_task, "acq_read", 4096, bk, task_prio[i % 3][0], NULL); + xTaskCreate(acquire_write_task, "acq_write", 4096, bk, task_prio[i % 3][1], NULL); + while (1) { + vTaskDelay(1 / portTICK_PERIOD_MS); + if (is_done) { + total_cnt += (esp_clk_rtc_time() - start_cnt); + break; + } + } + ESP_LOGI(TAG, "Elapsed time: %ld us \r\n", total_cnt); + TEST_ASSERT_EQUAL(verify_two_files(file_name, file2_path), ESP_OK); + esp_gmf_block_reset(bk); + is_done = false; + vTaskDelay(100 / portTICK_PERIOD_MS); + } + esp_gmf_block_destroy(bk); + esp_gmf_ut_teardown_sdmmc(card); +} + +TEST_CASE("Read and write with FIXED SIZE + NO DELAY on different task", "[ESP_GMF_BLOCK]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + // esp_log_level_set("ESP_GMF_BLOCK", ESP_LOG_DEBUG); + + sdmmc_card_t *card = NULL; + esp_gmf_ut_setup_sdmmc(&card); + + is_done = false; + esp_gmf_block_handle_t bk = NULL; + esp_gmf_block_create(2 * 1000, 4, &bk); + TEST_ASSERT_NOT_NULL(bk); + uint32_t start_cnt = 0; + uint32_t total_cnt = 0; + start_cnt = 0; + total_cnt = 0; + block_size_type = 0; + use_random_delay = false; + ESP_GMF_MEM_SHOW(TAG); + uint32_t task_prio[3][2] = {{5, 2}, {2, 2}, {2, 5}}; + uint32_t loop_times = 10; + file2_indx = 0; + for (int i = 0; i < loop_times; ++i) { + printf("\r\n\r\n ---------- FIXED SIZE + NO DELAY, %d, priority:%ld, %ld ---------- \r\n", i, task_prio[i % 3][0], task_prio[i % 3][1]); + + start_cnt = esp_clk_rtc_time(); + xTaskCreate(acquire_read_task, "acq_read", 4096, bk, task_prio[i % 3][0], NULL); + xTaskCreate(acquire_write_task, "acq_write", 4096, bk, task_prio[i % 3][1], NULL); + while (1) { + vTaskDelay(1 / portTICK_PERIOD_MS); + if (is_done) { + total_cnt += (esp_clk_rtc_time() - start_cnt); + break; + } + } + ESP_LOGI(TAG, "Elapsed time: %ld us \r\n", total_cnt); + TEST_ASSERT_EQUAL(verify_two_files(file_name, file2_path), ESP_OK); + esp_gmf_block_reset(bk); + is_done = false; + } + esp_gmf_block_destroy(bk); + esp_gmf_ut_teardown_sdmmc(card); +} + +TEST_CASE("Read and write with RANDOM SIZE + RANDOM DELAY on different task", "[ESP_GMF_BLOCK]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + // esp_log_level_set("ESP_GMF_BLOCK", ESP_LOG_DEBUG); + + sdmmc_card_t *card = NULL; + esp_gmf_ut_setup_sdmmc(&card); + + is_done = false; + esp_gmf_block_handle_t bk = NULL; + esp_gmf_block_create(2 * 1000, 4, &bk); + TEST_ASSERT_NOT_NULL(bk); + uint32_t start_cnt = 0; + uint32_t total_cnt = 0; + start_cnt = 0; + total_cnt = 0; + block_size_type = 1; + use_random_delay = true; + + uint32_t task_prio[3][2] = {{5, 2}, {2, 2}, {2, 5}}; + + uint32_t loop_times = 10; + file2_indx = 0; + for (int i = 0; i < loop_times; ++i) { + printf("\r\n\r\n ---------- RANDOM SIZE + RANDOM DELAY, %d, priority:%ld, %ld ---------- \r\n", i, task_prio[i % 3][0], task_prio[i % 3][1]); + start_cnt = esp_clk_rtc_time(); + xTaskCreate(acquire_read_task, "acq_read", 4096, bk, task_prio[i % 3][0], NULL); + xTaskCreate(acquire_write_task, "acq_write", 4096, bk, task_prio[i % 3][1], NULL); + while (1) { + vTaskDelay(1 / portTICK_PERIOD_MS); + if (is_done) { + total_cnt += (esp_clk_rtc_time() - start_cnt); + break; + } + } + ESP_LOGI(TAG, "Elapsed time: %ld us \r\n", total_cnt); + TEST_ASSERT_EQUAL(verify_two_files(file_name, file2_path), ESP_OK); + esp_gmf_block_reset(bk); + is_done = false; + vTaskDelay(100 / portTICK_PERIOD_MS); + } + esp_gmf_block_destroy(bk); + esp_gmf_ut_teardown_sdmmc(card); +} + +TEST_CASE("Read and write with FIXED SIZE + RANDOM DELAY", "[ESP_GMF_BLOCK]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + // esp_log_level_set("ESP_GMF_BLOCK", ESP_LOG_DEBUG); + + sdmmc_card_t *card = NULL; + esp_gmf_ut_setup_sdmmc(&card); + + is_done = false; + esp_gmf_block_handle_t bk = NULL; + esp_gmf_block_create(2 * 1000, 4, &bk); + TEST_ASSERT_NOT_NULL(bk); + uint32_t start_cnt = 0; + uint32_t total_cnt = 0; + start_cnt = 0; + total_cnt = 0; + block_size_type = 0; + use_random_delay = true; + ESP_GMF_MEM_SHOW(TAG); + uint32_t task_prio[3][2] = {{5, 2}, {2, 2}, {2, 5}}; + + uint32_t loop_times = 10; + file2_indx = 0; + for (int i = 0; i < loop_times; ++i) { + printf("\r\n\r\n ---------- FIXED SIZE + RANDOM DELAY %d, priority:%ld, %ld ---------- \r\n", i, task_prio[i % 3][0], task_prio[i % 3][1]); + + start_cnt = esp_clk_rtc_time(); + xTaskCreate(acquire_read_task, "acq_read", 4096, bk, task_prio[i % 3][0], NULL); + xTaskCreate(acquire_write_task, "acq_write", 4096, bk, task_prio[i % 3][1], NULL); + while (1) { + vTaskDelay(1 / portTICK_PERIOD_MS); + if (is_done) { + total_cnt += (esp_clk_rtc_time() - start_cnt); + break; + } + } + ESP_LOGI(TAG, "Elapsed time: %ld us \r\n", total_cnt); + TEST_ASSERT_EQUAL(verify_two_files(file_name, file2_path), ESP_OK); + esp_gmf_block_reset(bk); + is_done = false; + } + esp_gmf_block_destroy(bk); + esp_gmf_ut_teardown_sdmmc(card); +} + +TEST_CASE("Read and write with SPECIFIC SIZE + NO DELAY", "[ESP_GMF_BLOCK]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + // esp_log_level_set("ESP_GMF_BLOCK", ESP_LOG_DEBUG); + + sdmmc_card_t *card = NULL; + esp_gmf_ut_setup_sdmmc(&card); + + is_done = false; + esp_gmf_block_handle_t bk = NULL; + esp_gmf_block_create(2 * 1000, 4, &bk); + TEST_ASSERT_NOT_NULL(bk); + uint32_t start_cnt = 0; + uint32_t total_cnt = 0; + start_cnt = 0; + total_cnt = 0; + block_size_type = 2; + use_random_delay = false; + ESP_GMF_MEM_SHOW(TAG); + + uint32_t task_prio[3][2] = {{5, 2}, {2, 2}, {2, 5}}; + uint32_t size_percent[10] = {1, 2, 10, 30, 50, 60, 70, 80, 99, 100}; + uint32_t loop_times = sizeof(size_percent) / sizeof(uint32_t); + file2_indx = 0; + for (int i = 0; i < loop_times; ++i) { + uint32_t total_size = 0; + esp_gmf_block_get_total_size(bk, &total_size); + specific_block_size = total_size * size_percent[i] / 100; + + printf("\r\n\r\n ---------- SPECIFIC SIZE, %d, priority:%ld, %ld, blk_sz:%ld ---------- \r\n", + i, task_prio[i % 3][0], task_prio[i % 3][1], specific_block_size); + + start_cnt = esp_clk_rtc_time(); + xTaskCreate(acquire_read_task, "acq_read", 4096, bk, task_prio[i % 3][0], NULL); + xTaskCreate(acquire_write_task, "acq_write", 4096, bk, task_prio[i % 3][1], NULL); + while (1) { + vTaskDelay(1 / portTICK_PERIOD_MS); + if (is_done) { + total_cnt += (esp_clk_rtc_time() - start_cnt); + break; + } + } + ESP_LOGI(TAG, "Elapsed time: %ld us \r\n", total_cnt); + TEST_ASSERT_EQUAL(verify_two_files(file_name, file2_path), ESP_OK); + esp_gmf_block_reset(bk); + is_done = false; + } + esp_gmf_block_destroy(bk); + esp_gmf_ut_teardown_sdmmc(card); +} + +TEST_CASE("Abort when read and write with FIXED SIZE + RANDOM DELAY", "[ESP_GMF_BLOCK]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + + sdmmc_card_t *card = NULL; + esp_gmf_ut_setup_sdmmc(&card); + + is_done = false; + is_abort_read = false; + is_abort_write = false; + esp_gmf_block_handle_t bk = NULL; + esp_gmf_block_create(2 * 1000, 4, &bk); + TEST_ASSERT_NOT_NULL(bk); + block_size_type = 0; + use_random_delay = true; + ESP_GMF_MEM_SHOW(TAG); + uint32_t task_prio[3][2] = {{5, 2}, {2, 2}, {2, 5}}; + + uint32_t loop_times = 10; + file2_indx = 0; + for (int i = 0; i < loop_times; ++i) { + printf("\r\n\r\n ----------ABORT with FIXED SIZE + RANDOM DELAY %d, priority:%ld, %ld ---------- \r\n", i, task_prio[i % 3][0], task_prio[i % 3][1]); + + xTaskCreate(acquire_read_task, "acq_read", 4096, bk, task_prio[i % 3][0], NULL); + xTaskCreate(acquire_write_task, "acq_write", 4096, bk, task_prio[i % 3][1], NULL); + int timeout_ms = 100; + while (1) { + vTaskDelay(1 / portTICK_PERIOD_MS); + timeout_ms -= 1; + if (timeout_ms == 0) { + ESP_LOGI(TAG, "Calling abort after 100ms"); + esp_gmf_block_abort(bk); + vTaskDelay(50 / portTICK_PERIOD_MS); + } + if (is_done) { + break; + } + } + TEST_ASSERT_TRUE(is_abort_read); + TEST_ASSERT_TRUE(is_abort_write); + esp_gmf_block_reset(bk); + is_abort_read = false; + is_abort_write = false; + is_done = false; + } + esp_gmf_block_destroy(bk); + esp_gmf_ut_teardown_sdmmc(card); +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/main/cases/gmf_cache_test.c b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/main/cases/gmf_cache_test.c new file mode 100644 index 0000000..96effc2 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/main/cases/gmf_cache_test.c @@ -0,0 +1,312 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include +#include +#include "unity.h" +#include "esp_log.h" +#include "esp_system.h" +#include "esp_gmf_cache.h" + +#include "freertos/FreeRTOS.h" +#include "esp_private/esp_clk.h" +#include "driver/sdmmc_host.h" + +#include "esp_gmf_oal_mem.h" +#include "gmf_ut_common.h" + +#define CACHE_SIZE 1024 +#define TEST_DATA_SIZE 256 +static const char *TAG = "TEST_ESP_GMF_CACHE"; + +static const char *file_name = "/sdcard/gmf_ut_test.mp3"; + +TEST_CASE("Test esp_gmf_cache creation and deletion", "[esp_gmf_cache]") +{ + esp_gmf_cache_t *cache = NULL; + + // Test NULL input + TEST_ASSERT_EQUAL(ESP_GMF_ERR_INVALID_ARG, esp_gmf_cache_new(CACHE_SIZE, NULL)); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_cache_new(CACHE_SIZE, &cache)); + TEST_ASSERT_NOT_NULL(cache); + TEST_ASSERT_NOT_NULL(cache->buf); + TEST_ASSERT_EQUAL_UINT32(CACHE_SIZE, cache->buf_len); + TEST_ASSERT_EQUAL_UINT32(0, cache->buf_filled); + + // Delete cache + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_cache_delete(cache)); + + // Test NULL deletion + TEST_ASSERT_EQUAL(ESP_GMF_ERR_INVALID_ARG, esp_gmf_cache_delete(NULL)); +} + +TEST_CASE("Test esp_gmf_cache acquire and release", "[esp_gmf_cache]") +{ + esp_gmf_cache_t *cache = NULL; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_cache_new(CACHE_SIZE, &cache)); + + esp_gmf_payload_t load_in, *load_out = NULL; + + // Test NULL input + TEST_ASSERT_EQUAL(ESP_GMF_ERR_INVALID_ARG, esp_gmf_cache_acquire(cache, TEST_DATA_SIZE, NULL)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_INVALID_ARG, esp_gmf_cache_load(cache, NULL)); + + // Prepare load_in + load_in.buf = (uint8_t *)malloc(TEST_DATA_SIZE); + load_in.buf_length = TEST_DATA_SIZE; + load_in.valid_size = TEST_DATA_SIZE; + memset(load_in.buf, 0xAB, TEST_DATA_SIZE); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_cache_load(cache, &load_in)); + + // Acquire data + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_cache_acquire(cache, TEST_DATA_SIZE, &load_out)); + TEST_ASSERT_EQUAL_UINT32(TEST_DATA_SIZE, load_out->valid_size); + + // Release data + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_cache_release(cache, load_out)); + + // Free resources + free(load_in.buf); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_cache_delete(cache)); +} + +TEST_CASE("Test esp_gmf_cache get cached size", "[esp_gmf_cache]") +{ + esp_gmf_cache_t *cache = NULL; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_cache_new(CACHE_SIZE, &cache)); + + int filled = 0; + + // Test NULL input + TEST_ASSERT_EQUAL(ESP_GMF_ERR_INVALID_ARG, esp_gmf_cache_get_cached_size(NULL, &filled)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_INVALID_ARG, esp_gmf_cache_get_cached_size(cache, NULL)); + + // Initially empty cache + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_cache_get_cached_size(cache, &filled)); + TEST_ASSERT_EQUAL_UINT32(0, filled); + + // Fill cache with test data + memset(cache->buf, 0x55, TEST_DATA_SIZE); + cache->buf_filled = TEST_DATA_SIZE; + + // Check cached size + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_cache_get_cached_size(cache, &filled)); + TEST_ASSERT_EQUAL_UINT32(TEST_DATA_SIZE, filled); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_cache_delete(cache)); +} + +int random_fluctuate_int(int value) +{ + int range = value / 2; // 计算浮动范围(±50%) + int random_offset = rand() % (2 * range + 1) - range; // 生成 [-range, range] 的随机偏移 + return value + random_offset; +} + +static void read_write_test1(const char *wr_name, int payload_size, int cache_size) +{ + esp_gmf_payload_t *read_payload = NULL; + esp_gmf_cache_t *cache = NULL; + esp_gmf_cache_new(cache_size, &cache); + TEST_ASSERT_NOT_NULL(cache); + esp_gmf_payload_new_with_len(payload_size*2, &read_payload); + TEST_ASSERT_NOT_NULL(read_payload); + + FILE *f_rd = fopen(file_name, "rb"); + if (f_rd == NULL) { + ESP_LOGE(TAG, "Open file failed"); + TEST_ASSERT_NOT_NULL(f_rd); + goto read_task_err; + } + FILE *f_wr = fopen(wr_name, "wb+"); + if (f_wr == NULL) { + ESP_LOGE(TAG, "Open file failed"); + TEST_ASSERT_NOT_NULL(f_wr); + goto read_task_err; + } + struct stat sz = {0}; + stat(file_name, &sz); + ESP_LOGI(TAG, "Read file size: %ld, IN Payload:%d, Cache size:%d", sz.st_size, payload_size, cache_size); + bool read_run = true; + uint32_t read_file_size = 0; + uint32_t file_size = 0; + esp_gmf_payload_t *out_payload = NULL; + int expect_sz = cache_size + 512; + while (read_run) { + int read_len = random_fluctuate_int(payload_size); + // int read_len = (payload_size); + + int ret = fread(read_payload->buf, 1, read_len, f_rd); + read_payload->valid_size = ret; + if (ret != read_len) { + read_payload->is_done = true; + read_run = false; + } + read_file_size += read_payload->valid_size; + ESP_LOGI(TAG, "Payload, buf:%p, vld:%d, len:%d, done:%d", read_payload->buf,read_payload->valid_size, + read_payload->buf_length, read_payload->is_done); + esp_gmf_cache_load(cache, read_payload); + while (1) { + esp_gmf_cache_acquire(cache, expect_sz, &out_payload); + ESP_LOGW(TAG, "Cache out, expect:%d, buf:%p, vld:%d, len:%d, done:%d, file w:%ld(r:%ld)", expect_sz, out_payload->buf,out_payload->valid_size, + out_payload->buf_length, out_payload->is_done, file_size, read_file_size); + if (out_payload->valid_size == expect_sz || out_payload->is_done) { + ret = fwrite(out_payload->buf, 1, out_payload->valid_size, f_wr); + file_size += out_payload->valid_size; + } + if (out_payload->is_done || out_payload->valid_size != expect_sz) { + esp_gmf_cache_release(cache, out_payload); + break; + } + esp_gmf_cache_release(cache, out_payload); + } + printf("\r\n"); + } + + ESP_LOGW(TAG, "Done to read, read size:%ld", file_size); +read_task_err: + if (cache) { + esp_gmf_cache_delete(cache); + } + if (read_payload) { + esp_gmf_payload_delete(read_payload); + } + if (f_rd) { + fclose(f_rd); + } + if (f_wr) { + fclose(f_wr); + } +} + +static void read_write_test2(const char *wr_name, int payload_size, int cache_size) +{ + esp_gmf_payload_t *read_payload = NULL; + esp_gmf_cache_t *cache = NULL; + esp_gmf_cache_new(cache_size, &cache); + TEST_ASSERT_NOT_NULL(cache); + esp_gmf_payload_new_with_len(payload_size*2, &read_payload); + TEST_ASSERT_NOT_NULL(read_payload); + + FILE *f_rd = fopen(file_name, "rb"); + if (f_rd == NULL) { + ESP_LOGE(TAG, "Open file failed"); + TEST_ASSERT_NOT_NULL(f_rd); + goto read_task_err; + } + FILE *f_wr = fopen(wr_name, "wb+"); + if (f_wr == NULL) { + ESP_LOGE(TAG, "Open file failed"); + TEST_ASSERT_NOT_NULL(f_wr); + goto read_task_err; + } + struct stat sz = {0}; + stat(file_name, &sz); + ESP_LOGI(TAG, "Read file size: %ld, IN Payload:%d, Cache size:%d", sz.st_size, payload_size, cache_size); + bool read_run = true; + uint32_t read_file_size = 0; + uint32_t file_size = 0; + esp_gmf_payload_t *out_payload = NULL; + int ret = 0; + int expect_sz = cache_size + 1024; + while (read_run) { + bool needed_load = false; + esp_gmf_cache_ready_for_load(cache, &needed_load); + if (needed_load) { + int read_len = random_fluctuate_int(payload_size); + // int read_len = (payload_size); + ret = fread(read_payload->buf, 1, read_len, f_rd); + read_payload->valid_size = ret; + if (ret != read_len) { + read_payload->is_done = true; + } + read_file_size += read_payload->valid_size; + printf("\r\n"); + ESP_LOGI(TAG, "Loading, buf:%p, vld:%d, len:%d, done:%d", read_payload->buf,read_payload->valid_size, + read_payload->buf_length, read_payload->is_done); + esp_gmf_cache_load(cache, read_payload); + } + + esp_gmf_cache_acquire(cache, expect_sz, &out_payload); + + ESP_LOGW(TAG, "Cache out, expect:%d, buf:%p, vld:%d, len:%d, done:%d, file:%ld(%ld)", expect_sz, out_payload->buf,out_payload->valid_size, + out_payload->buf_length, out_payload->is_done, file_size, read_file_size); + if ((!out_payload->is_done) && (out_payload->valid_size != expect_sz)) { + esp_gmf_cache_release(cache, out_payload); + continue; + } + if (out_payload->valid_size == expect_sz || out_payload->is_done) { + ret = fwrite(out_payload->buf, 1, out_payload->valid_size, f_wr); + file_size += out_payload->valid_size; + } + if (out_payload->is_done || out_payload->valid_size != expect_sz) { + read_run = false; + } + esp_gmf_cache_release(cache, out_payload); + } + + ESP_LOGW(TAG, "Done to read, read size:%ld", file_size); +read_task_err: + if (cache) { + esp_gmf_cache_delete(cache); + } + if (read_payload) { + esp_gmf_payload_delete(read_payload); + } + if (f_rd) { + fclose(f_rd); + } + if (f_wr) { + fclose(f_wr); + } +} + +TEST_CASE("Test Cache with a file - case 1", "[esp_gmf_cache]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + esp_log_level_set("GMF_CACHE", ESP_LOG_VERBOSE); + + sdmmc_card_t *card = NULL; + esp_gmf_ut_setup_sdmmc(&card); + + int payload_size[][2] = { {1536, 2048}, {2000, 1500},{1111, 1300}, {2000, 500}, {1000, 3000}}; + char wr_name[128] = {0}; + for (size_t i = 0; i < sizeof(payload_size) / sizeof(payload_size[0]); i++){ + ESP_LOGW(TAG, "Test Cache with payload_size %d, %d\r\n\r\n", payload_size[i][0], payload_size[i][1]); + snprintf(wr_name, 127, "/sdcard/esp_gmf_test_cache_%02d.txt", i); + read_write_test1(wr_name, payload_size[i][0], payload_size[i][1]); + int ret = verify_two_files(file_name, wr_name); + TEST_ASSERT_EQUAL(ret, ESP_OK); + } + + esp_gmf_ut_teardown_sdmmc(card); + vTaskDelay(10 / portTICK_PERIOD_MS); +} + +TEST_CASE("Test cache with file - case 2", "[esp_gmf_cache]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + esp_log_level_set("GMF_CACHE", ESP_LOG_VERBOSE); + + sdmmc_card_t *card = NULL; + esp_gmf_ut_setup_sdmmc(&card); + + int payload_size[][2] = { {1536, 2048},{2000, 1500}, {1111, 1300}, {2000, 500}, {1000, 3000}}; + char wr_name[128] = {0}; + for (size_t i = 0; i < sizeof(payload_size) / sizeof(payload_size[0]); i++){ + ESP_LOGW(TAG, "Test Cache with payload_size %d, %d\r\n\r\n", payload_size[i][0], payload_size[i][1]); + snprintf(wr_name, 127, "/sdcard/esp_gmf_test_cache_%02d.txt", i); + read_write_test2(wr_name, payload_size[i][0], payload_size[i][1]); + int ret = verify_two_files(file_name, wr_name); + TEST_ASSERT_EQUAL(ret, ESP_OK); + } + + esp_gmf_ut_teardown_sdmmc(card); + vTaskDelay(10 / portTICK_PERIOD_MS); +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/main/cases/gmf_caps_test.c b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/main/cases/gmf_caps_test.c new file mode 100644 index 0000000..f8b87a6 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/main/cases/gmf_caps_test.c @@ -0,0 +1,489 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "unity.h" +#include "freertos/FreeRTOS.h" + +#include "esp_log.h" +#include "esp_gmf_oal_mem.h" +#include "esp_gmf_cap.h" +#include "gmf_fake_dec.h" +#include "esp_gmf_caps_def.h" + +static const char *TAG = "TEST_ESP_GMF_CAPS"; + +static esp_gmf_err_t audio_attr_iter_fun(uint32_t attr_index, esp_gmf_cap_attr_t *attr) +{ + switch (attr_index) { + case 0: { + ESP_GMF_CAP_ATTR_SET_MULTIPLE(attr, STR_2_EIGHTCC("BITS"), 8, 8, 32); + break; + } + case 1: { + ESP_GMF_CAP_ATTR_SET_MULTIPLE(attr, STR_2_EIGHTCC("RATE"), 8000, 8000, 192000); + break; + } + case 2: { + ESP_GMF_CAP_ATTR_SET_MULTIPLE(attr, STR_2_EIGHTCC("RATE"), 8000, 11025, 192000); + break; + } + case 3: { + ESP_GMF_CAP_ATTR_SET_STEPWISE(attr, STR_2_EIGHTCC("TEST"), 8000, 3000, 22000); + break; + } + case 4: { + const static uint8_t support_chan[] = {1, 2, 5, 11, 29, 88}; + ESP_GMF_CAP_ATTR_SET_DISCRETE(attr, STR_2_EIGHTCC("CHAN"), (uint8_t *) &support_chan, + sizeof(support_chan) / sizeof(uint8_t), sizeof(uint8_t)); + break; + } + case 5: { + ESP_GMF_CAP_ATTR_SET_CONSTANT(attr, STR_2_EIGHTCC("VALUE"), 2000); + break; + } + case 6: { + ESP_GMF_CAP_ATTR_SET_STEPWISE(attr, STR_2_EIGHTCC("TET1"), 8000, 8000, 8000); + break; + } + case 7: { + ESP_GMF_CAP_ATTR_SET_MULTIPLE(attr, STR_2_EIGHTCC("TET2"), 8000, 8000, 8000); + break; + } + default: + attr->prop_type = ESP_GMF_PROP_TYPE_NONE; + return ESP_GMF_ERR_NOT_FOUND; + } + return ESP_GMF_ERR_OK; +} + +TEST_CASE("GMF CAPS Create and Destroy", "[ESP_GMF_CAPS]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + esp_log_level_set("ESP_GMF_CAPS", ESP_LOG_DEBUG); + + esp_gmf_cap_t *caps_list = NULL; + + esp_gmf_cap_t alc_caps = {0}; + alc_caps.cap_eightcc = STR_2_EIGHTCC("AUDALC"); + alc_caps.perf.oper_per_sec = 100; + alc_caps.attr_fun = audio_attr_iter_fun; + + esp_gmf_cap_t alc_caps1 = {0}; + alc_caps1.cap_eightcc = STR_2_EIGHTCC("AUDALC1"); + alc_caps1.perf.oper_per_sec = 101; + alc_caps1.attr_fun = audio_attr_iter_fun; + + esp_gmf_cap_t alc_caps2 = {0}; + alc_caps2.cap_eightcc = STR_2_EIGHTCC("AUDALC2"); + alc_caps2.perf.oper_per_sec = 102; + alc_caps2.attr_fun = audio_attr_iter_fun; + + ESP_GMF_MEM_SHOW(TAG); + esp_gmf_cap_append(&caps_list, &alc_caps); + esp_gmf_cap_append(&caps_list, &alc_caps1); + esp_gmf_cap_append(&caps_list, &alc_caps2); + esp_gmf_cap_t *tmp = caps_list; + while (tmp) { + ESP_LOGI(TAG, "%s,%ld,%p", EIGHTCC_2_STR(tmp->cap_eightcc), tmp->perf.oper_per_sec, tmp->attr_fun); + tmp = tmp->next; + } + esp_gmf_cap_destroy(caps_list); + caps_list = NULL; + for (int i = 0; i < 10; ++i) { + esp_gmf_cap_append(&caps_list, &alc_caps); + esp_gmf_cap_append(&caps_list, &alc_caps1); + esp_gmf_cap_append(&caps_list, &alc_caps2); + esp_gmf_cap_destroy(caps_list); + caps_list = NULL; + } + ESP_GMF_MEM_SHOW(TAG); +} + +TEST_CASE("GMF CAPS Iterate test", "[ESP_GMF_CAPS]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + esp_log_level_set("ESP_GMF_CAPS", ESP_LOG_DEBUG); + + esp_gmf_cap_t alc_caps = {0}; + alc_caps.cap_eightcc = STR_2_EIGHTCC("AUDALC"); + alc_caps.perf.oper_per_sec = 100; + alc_caps.attr_fun = audio_attr_iter_fun; + + ESP_LOGI(TAG, "--- MULTIPLE PROPERTY TEST ---"); + esp_gmf_cap_attr_t out_attr = {0}; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_cap_find_attr(&alc_caps, STR_2_EIGHTCC("RATE"), &out_attr)); + bool is_support = false; + esp_gmf_cap_attr_check_value(&out_attr, 16000, &is_support); + TEST_ASSERT_TRUE(is_support); + esp_gmf_cap_attr_check_value(&out_attr, 23000, &is_support); + TEST_ASSERT_FALSE(is_support); + + uint32_t support_sample_rate1[] = {8000, 16000, 24000, + 32000, 48000, 64000, 96000, 192000 + }; + // uint32_t support_sample_rate2[] = {11025, 22050, 44100, 88200}; + + uint32_t support_sample_rate3[] = {8000, 11025, 16000, 22050, 24000, + 32000, 44100, 48000, 64000, 88200, 96000, 192000 + }; + + uint32_t unsupport_sample_rate[] = {1000, 4000, 16008, 22051, 44000, 39000, 199000}; + for (int i = 0; i < sizeof(support_sample_rate1) / sizeof(uint32_t); ++i) { + esp_gmf_cap_attr_check_value(&out_attr, support_sample_rate1[i], &is_support); + TEST_ASSERT_TRUE(is_support); + } + + for (int i = 0; i < sizeof(support_sample_rate3) / sizeof(uint32_t); ++i) { + esp_gmf_cap_t *tmp_caps = &alc_caps; + int ret = esp_gmf_cap_find_attr(tmp_caps, STR_2_EIGHTCC("RATE"), &out_attr); + + while (ret == ESP_GMF_ERR_OK) { + esp_gmf_cap_attr_check_value(&out_attr, support_sample_rate3[i], &is_support); + if (is_support) { + ESP_LOGI(TAG, "%ld rate is supported", support_sample_rate3[i]); + break; + } else { + tmp_caps = tmp_caps->next; + if (tmp_caps == NULL) { + break; + } + ret = esp_gmf_cap_find_attr(tmp_caps, STR_2_EIGHTCC("RATE"), &out_attr); + } + } + } + + for (int i = 0; i < sizeof(unsupport_sample_rate) / sizeof(uint32_t); ++i) { + esp_gmf_cap_attr_check_value(&out_attr, unsupport_sample_rate[i], &is_support); + ESP_LOGI(TAG, "MULTI,check value, %d", is_support); + TEST_ASSERT_FALSE(is_support); + } + + bool is_last = false; + uint8_t cnt = 0; + + while (is_last == false) { + uint32_t value = 0; + esp_gmf_cap_attr_iterator_value(&out_attr, &value, &is_last); + ESP_LOGI(TAG, "MULTI,iterate value, %s, %d", EIGHTCC_2_STR(out_attr.fourcc), is_support); + for (int i = 0; i < sizeof(support_sample_rate1) / sizeof(uint32_t); ++i) { + if (value == support_sample_rate1[i]) { + ESP_LOGI(TAG, "MULTI:%ld is supported", value); + cnt++; + break; + } + } + } + TEST_ASSERT_EQUAL(cnt, sizeof(support_sample_rate1) / sizeof(uint32_t)); + + cnt = 0; + while (is_last == false) { + uint32_t value = 0; + esp_gmf_cap_attr_iterator_value(&out_attr, &value, &is_last); + for (int i = 0; i < sizeof(unsupport_sample_rate) / sizeof(uint32_t); ++i) { + if (value == unsupport_sample_rate[i]) { + ESP_LOGI(TAG, "MULTI: %ld is not supported", value); + cnt++; + } + } + } + TEST_ASSERT_EQUAL(cnt, 0); + ESP_LOGI(TAG, "--- MULTIPLE PROPERTY TEST END ---\r\n"); + + + ESP_LOGI(TAG, "--- STEPWISE PROPERTY TEST ---"); + + uint32_t support_stepwise_test[] = {8000, 11000, 14000, 17000, 20000}; + uint32_t unsupport_stepwise_test[] = {7000, 11900, 13000, 19000, 21000, 25000}; + esp_gmf_cap_find_attr(&alc_caps, STR_2_EIGHTCC("TEST"), &out_attr); + is_last = false; + cnt = 0; + while (is_last == false) { + uint32_t value = 0; + esp_gmf_cap_attr_iterator_value(&out_attr, &value, &is_last); + for (int i = 0; i < sizeof(support_stepwise_test) / sizeof(uint32_t); ++i) { + if (value == support_stepwise_test[i]) { + ESP_LOGI(TAG, "STEP: %ld is supported", value); + cnt++; + } + } + } + TEST_ASSERT_EQUAL(cnt, sizeof(support_stepwise_test) / sizeof(uint32_t)); + + cnt = 0; + while (is_last == false) { + uint32_t value = 0; + esp_gmf_cap_attr_iterator_value(&out_attr, &value, &is_last); + for (int i = 0; i < sizeof(unsupport_stepwise_test) / sizeof(uint32_t); ++i) { + if (value == unsupport_stepwise_test[i]) { + ESP_LOGI(TAG, "STEP: %ld is not supported", value); + cnt++; + } + } + } + TEST_ASSERT_EQUAL(cnt, 0); + + is_support = false; + for (int i = 0; i < sizeof(support_stepwise_test) / sizeof(uint32_t); ++i) { + esp_gmf_cap_attr_check_value(&out_attr, support_stepwise_test[i], &is_support); + + TEST_ASSERT_TRUE(is_support); + } + + is_support = false; + for (int i = 0; i < sizeof(unsupport_stepwise_test) / sizeof(uint32_t); ++i) { + esp_gmf_cap_attr_check_value(&out_attr, unsupport_stepwise_test[i], &is_support); + ESP_LOGI(TAG, "STEP: %ld, support:%d", unsupport_stepwise_test[i], is_support); + TEST_ASSERT_FALSE(is_support); + } + ESP_LOGI(TAG, "--- STEPWISE PROPERTY TEST END ---\r\n"); + + + ESP_LOGI(TAG, "--- DISCRETE PROPERTY TEST ---"); + uint32_t support_discrete_test[] = {1, 2, 5, 11, 29, 88}; + uint32_t unsupport_discrete_test[] = {0, 4, 8, 30, 100}; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_cap_find_attr(&alc_caps, STR_2_EIGHTCC("CHAN"), &out_attr)); + is_last = false; + cnt = 0; + while (is_last == false) { + uint32_t value = 0; + esp_gmf_cap_attr_iterator_value(&out_attr, &value, &is_last); + for (int i = 0; i < sizeof(support_discrete_test) / sizeof(uint32_t); ++i) { + if (value == support_discrete_test[i]) { + cnt++; + } + ESP_LOGI(TAG, "DISCRETE: val: %ld, src:%ld", value, support_discrete_test[i]); + } + } + TEST_ASSERT_EQUAL(cnt, sizeof(support_discrete_test) / sizeof(uint32_t)); + + cnt = 0; + while (is_last == false) { + uint32_t value = 0; + esp_gmf_cap_attr_iterator_value(&out_attr, &value, &is_last); + for (int i = 0; i < sizeof(unsupport_discrete_test) / sizeof(uint32_t); ++i) { + if (value == unsupport_discrete_test[i]) { + ESP_LOGI(TAG, "DISCRETE: %ld is not supported", value); + cnt++; + } + } + } + TEST_ASSERT_EQUAL(cnt, 0); + + is_support = false; + for (int i = 0; i < sizeof(support_discrete_test) / sizeof(uint32_t); ++i) { + esp_gmf_cap_attr_check_value(&out_attr, support_discrete_test[i], &is_support); + TEST_ASSERT_TRUE(is_support); + } + + is_support = false; + for (int i = 0; i < sizeof(unsupport_discrete_test) / sizeof(uint32_t); ++i) { + esp_gmf_cap_attr_check_value(&out_attr, unsupport_discrete_test[i], &is_support); + TEST_ASSERT_FALSE(is_support); + } + ESP_LOGI(TAG, "--- DISCRETE PROPERTY TEST END ---\r\n"); + + + ESP_LOGI(TAG, "--- CONSTANT PROPERTY TEST ---"); + uint32_t support_constant_test[] = {2000}; + uint32_t unsupport_constant_test[] = {0, 1999, 3000}; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_cap_find_attr(&alc_caps, STR_2_EIGHTCC("VALUE"), &out_attr)); + is_last = false; + cnt = 0; + while (is_last == false) { + uint32_t value = 0; + esp_gmf_cap_attr_iterator_value(&out_attr, &value, &is_last); + for (int i = 0; i < sizeof(support_constant_test) / sizeof(uint32_t); ++i) { + if (value == support_constant_test[i]) { + cnt++; + } + ESP_LOGI(TAG, "CONSTANT: val: %ld, src:%ld", value, support_constant_test[i]); + } + } + TEST_ASSERT_EQUAL(cnt, sizeof(support_constant_test) / sizeof(uint32_t)); + + cnt = 0; + while (is_last == false) { + uint32_t value = 0; + esp_gmf_cap_attr_iterator_value(&out_attr, &value, &is_last); + for (int i = 0; i < sizeof(unsupport_constant_test) / sizeof(uint32_t); ++i) { + if (value == unsupport_constant_test[i]) { + ESP_LOGI(TAG, "CONSTANT: %ld is not supported", value); + cnt++; + } + } + } + TEST_ASSERT_EQUAL(cnt, 0); + + is_support = false; + for (int i = 0; i < sizeof(support_constant_test) / sizeof(uint32_t); ++i) { + esp_gmf_cap_attr_check_value(&out_attr, support_constant_test[i], &is_support); + TEST_ASSERT_TRUE(is_support); + } + + is_support = false; + for (int i = 0; i < sizeof(unsupport_constant_test) / sizeof(uint32_t); ++i) { + esp_gmf_cap_attr_check_value(&out_attr, unsupport_constant_test[i], &is_support); + TEST_ASSERT_FALSE(is_support); + } + + out_attr.value.c.data = 3000; + esp_gmf_cap_attr_check_value(&out_attr, support_constant_test[0], &is_support); + TEST_ASSERT_FALSE(is_support); + esp_gmf_cap_attr_check_value(&out_attr, 3000, &is_support); + TEST_ASSERT_TRUE(is_support); + + ESP_LOGI(TAG, "--- CONSTANT PROPERTY TEST END ---\r\n"); +} + +TEST_CASE("GMF CAPS Boundary Value test", "[ESP_GMF_CAPS]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + esp_log_level_set("ESP_GMF_CAPS", ESP_LOG_DEBUG); + + esp_gmf_cap_t alc_caps = {0}; + alc_caps.cap_eightcc = STR_2_EIGHTCC("AUDALC"); + alc_caps.perf.oper_per_sec = 100; + alc_caps.attr_fun = audio_attr_iter_fun; + + ESP_LOGI(TAG, "--- STEPWISE PROPERTY TEST ---"); + esp_gmf_cap_attr_t out_attr = {0}; + bool is_support = false; + bool is_last = false; + uint8_t cnt = 0; + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_cap_find_attr(&alc_caps, STR_2_EIGHTCC("TET1"), &out_attr)); + esp_gmf_cap_attr_check_value(&out_attr, 8000, &is_support); + TEST_ASSERT_TRUE(is_support); + esp_gmf_cap_attr_check_value(&out_attr, 16000, &is_support); + TEST_ASSERT_FALSE(is_support); + + uint32_t sample_rate1[] = {8000, 16000, 24000, + 32000, 48000, 64000, 96000, 192000}; + for (int i = 0; i < sizeof(sample_rate1) / sizeof(uint32_t); ++i) { + esp_gmf_cap_attr_check_value(&out_attr, sample_rate1[i], &is_support); + ESP_LOGI(TAG, "STEP, iterate value, %ld, support:%s", sample_rate1[i], is_support ? "TRUE" : "FALSE"); + if (i == 0) { + TEST_ASSERT_TRUE(is_support); + } else { + TEST_ASSERT_FALSE(is_support); + } + } + while (is_last == false) { + uint32_t value = 0; + esp_gmf_cap_attr_iterator_value(&out_attr, &value, &is_last); + ESP_LOGI(TAG, "STEP,iterate value, %s, %d", EIGHTCC_2_STR(out_attr.fourcc), is_support); + for (int i = 0; i < sizeof(sample_rate1) / sizeof(uint32_t); ++i) { + if (value == sample_rate1[i]) { + ESP_LOGI(TAG, "STEP:%ld is supported", value); + cnt++; + break; + } + } + } + TEST_ASSERT_EQUAL(cnt, 1); + ESP_LOGI(TAG, "--- STEPWISE PROPERTY TEST END ---\r\n"); + + + ESP_LOGI(TAG, "--- MULTIPLE PROPERTY TEST ---"); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_cap_find_attr(&alc_caps, STR_2_EIGHTCC("TET2"), &out_attr)); + esp_gmf_cap_attr_check_value(&out_attr, 8000, &is_support); + TEST_ASSERT_TRUE(is_support); + esp_gmf_cap_attr_check_value(&out_attr, 16000, &is_support); + TEST_ASSERT_FALSE(is_support); + for (int i = 0; i < sizeof(sample_rate1) / sizeof(uint32_t); ++i) { + esp_gmf_cap_attr_check_value(&out_attr, sample_rate1[i], &is_support); + ESP_LOGI(TAG, "MULTI, iterate value, %ld, support:%s", sample_rate1[i], is_support ? "TRUE" : "FALSE"); + if (i == 0) { + TEST_ASSERT_TRUE(is_support); + } else { + TEST_ASSERT_FALSE(is_support); + } + } + is_last = false; + cnt = 0; + while (is_last == false) { + uint32_t value = 0; + esp_gmf_cap_attr_iterator_value(&out_attr, &value, &is_last); + ESP_LOGI(TAG, "MULTI, iterate %s, value:%ld", EIGHTCC_2_STR(out_attr.fourcc), value); + for (int i = 0; i < sizeof(sample_rate1) / sizeof(uint32_t); ++i) { + if (value == sample_rate1[i]) { + ESP_LOGI(TAG, "MULTI:%ld is supported", value); + cnt++; + break; + } + } + } + TEST_ASSERT_EQUAL(cnt, 1); + ESP_LOGI(TAG, "--- MULTIPLE PROPERTY TEST END ---\r\n"); +} + +static esp_err_t element_event(esp_gmf_event_pkt_t *event, void *ctx) +{ + ESP_LOGI(TAG, "Get event,from:%p, type:%d, sub:%d, payload:%p, size:%d", + event->from, event->type, event->sub, event->payload, event->payload_size); + return ESP_OK; +} + +TEST_CASE("GMF Element CAPS test", "[ESP_GMF_CAPS]") +{ + ESP_GMF_MEM_SHOW(TAG); + fake_dec_cfg_t fake_cfg = DEFAULT_FAKE_DEC_CONFIG(); + fake_cfg.cb = element_event; + esp_gmf_element_handle_t fake_dec_el = NULL; + fake_dec_init(&fake_cfg, &fake_dec_el); + + const esp_gmf_cap_t *caps = NULL; + esp_gmf_element_get_caps(fake_dec_el, &caps); + printf("caps:%p\r\n", caps); + ESP_LOGI(TAG, "--- STEPWISE PROPERTY TEST ---"); + esp_gmf_cap_attr_t out_attr; + uint32_t support_stepwise_test[] = {8000, 11000, 14000, 17000, 20000}; + uint32_t unsupport_stepwise_test[] = {7000, 11900, 13000, 19000, 21000, 25000}; + esp_gmf_cap_find_attr((esp_gmf_cap_t*)caps, STR_2_EIGHTCC("TEST"), &out_attr); + bool is_last = false; + int cnt = 0; + bool is_support = false; + while (is_last == false) { + uint32_t value = 0; + esp_gmf_cap_attr_iterator_value(&out_attr, &value, &is_last); + for (int i = 0; i < sizeof(support_stepwise_test) / sizeof(uint32_t); ++i) { + if (value == support_stepwise_test[i]) { + ESP_LOGI(TAG, "STEP: %ld is supported", value); + cnt++; + } + } + } + TEST_ASSERT_EQUAL(cnt, sizeof(support_stepwise_test) / sizeof(uint32_t)); + + cnt = 0; + while (is_last == false) { + uint32_t value = 0; + esp_gmf_cap_attr_iterator_value(&out_attr, &value, &is_last); + for (int i = 0; i < sizeof(unsupport_stepwise_test) / sizeof(uint32_t); ++i) { + if (value == unsupport_stepwise_test[i]) { + ESP_LOGI(TAG, "STEP: %ld is not supported", value); + cnt++; + } + } + } + TEST_ASSERT_EQUAL(cnt, 0); + + is_support = false; + for (int i = 0; i < sizeof(support_stepwise_test) / sizeof(uint32_t); ++i) { + esp_gmf_cap_attr_check_value(&out_attr, support_stepwise_test[i], &is_support); + + TEST_ASSERT_TRUE(is_support); + } + + is_support = false; + for (int i = 0; i < sizeof(unsupport_stepwise_test) / sizeof(uint32_t); ++i) { + esp_gmf_cap_attr_check_value(&out_attr, unsupport_stepwise_test[i], &is_support); + ESP_LOGI(TAG, "STEP: %ld, support:%d", unsupport_stepwise_test[i], is_support); + TEST_ASSERT_FALSE(is_support); + } + ESP_LOGI(TAG, "--- STEPWISE PROPERTY TEST END ---\r\n"); + esp_gmf_obj_delete(fake_dec_el); +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/main/cases/gmf_element_test.c b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/main/cases/gmf_element_test.c new file mode 100644 index 0000000..7f52240 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/main/cases/gmf_element_test.c @@ -0,0 +1,115 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "unity.h" +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" +#include "esp_log.h" +#include "esp_gmf_oal_mem.h" +#include "esp_gmf_port.h" +#include "esp_gmf_data_bus.h" +#include "esp_gmf_new_databus.h" + +#include "gmf_fake_dec.h" +#include "gmf_fake_io.h" + +static const char *TAG = "TEST_GMF_ELEMENT"; + +#define ESP_GMF_PORT_PAYLOAD_LEN_DEFAULT (4096) + +esp_err_t element_event(esp_gmf_event_pkt_t *event, void *ctx) +{ + ESP_LOGI(TAG, "Get event,from:%p, type:%d, sub:%d, payload:%p, size:%d", + event->from, event->type, event->sub, event->payload, event->payload_size); + return ESP_OK; +} + +TEST_CASE("Register and unregister port for GMF ELEMENT", "[ESP_GMF_ELEMENT]") +{ + esp_log_level_set("*", ESP_LOG_DEBUG); + esp_log_level_set("ESP_GMF_ELEMENT", ESP_LOG_VERBOSE); + + ESP_GMF_MEM_SHOW(TAG); + fake_io_cfg_t cfg = FAKE_IO_CFG_DEFAULT(); + cfg.dir = ESP_GMF_IO_DIR_READER; + esp_gmf_io_handle_t reader = NULL; + fake_io_init(&cfg, &reader); + TEST_ASSERT_NOT_NULL(reader); + esp_gmf_io_set_uri(reader, "test.mp3"); + ESP_GMF_MEM_SHOW(TAG); + + esp_gmf_io_handle_t writer = NULL; + cfg.dir = ESP_GMF_IO_DIR_WRITER; + fake_io_init(&cfg, &writer); + TEST_ASSERT_NOT_NULL(writer); + + fake_dec_cfg_t fake_cfg = DEFAULT_FAKE_DEC_CONFIG(); + fake_cfg.cb = element_event; + esp_gmf_element_handle_t fake_dec_el = NULL; + fake_dec_init(&fake_cfg, &fake_dec_el); + + ESP_LOGW(TAG, "Register the in port, %d", __LINE__); + esp_gmf_db_handle_t db = NULL; + esp_gmf_db_new_ringbuf(10, 1024, &db); + esp_gmf_port_handle_t in_port = NEW_ESP_GMF_PORT_IN_BYTE(esp_gmf_db_acquire_read, esp_gmf_db_release_read, NULL, db, + ESP_GMF_PORT_PAYLOAD_LEN_DEFAULT, portMAX_DELAY); + TEST_ASSERT_NOT_NULL(in_port); + TEST_ASSERT_EQUAL(esp_gmf_element_register_in_port(fake_dec_el, in_port), ESP_GMF_ERR_OK); + esp_gmf_port_handle_t in_port1 = NEW_ESP_GMF_PORT_IN_BYTE(esp_gmf_db_acquire_read, esp_gmf_db_release_read, NULL, db, + ESP_GMF_PORT_PAYLOAD_LEN_DEFAULT, portMAX_DELAY); + TEST_ASSERT_NOT_NULL(in_port1); + TEST_ASSERT_EQUAL(esp_gmf_element_register_in_port(fake_dec_el, in_port1), ESP_GMF_ERR_NOT_SUPPORT); + + esp_gmf_port_handle_t out_port = NEW_ESP_GMF_PORT_OUT_BYTE(esp_gmf_db_acquire_write, esp_gmf_db_release_write, NULL, db, + ESP_GMF_PORT_PAYLOAD_LEN_DEFAULT, portMAX_DELAY); + TEST_ASSERT_NOT_NULL(out_port); + TEST_ASSERT_EQUAL(esp_gmf_element_register_out_port(fake_dec_el, out_port), ESP_GMF_ERR_OK); + esp_gmf_port_handle_t out_port1 = NEW_ESP_GMF_PORT_OUT_BYTE(esp_gmf_db_acquire_write, esp_gmf_db_release_write, NULL, db, + ESP_GMF_PORT_PAYLOAD_LEN_DEFAULT, portMAX_DELAY); + TEST_ASSERT_NOT_NULL(out_port1); + TEST_ASSERT_EQUAL(esp_gmf_element_register_out_port(fake_dec_el, out_port1), ESP_GMF_ERR_NOT_SUPPORT); + + esp_gmf_port_t *el_in_port = ESP_GMF_ELEMENT_GET(fake_dec_el)->in; + esp_gmf_port_t *el_out_port = ESP_GMF_ELEMENT_GET(fake_dec_el)->out; + uint8_t k = 0; + while (el_in_port) { + if (k == 0) { + ESP_LOGI(TAG, "Compare IN port, %d, %p,%p", __LINE__, in_port, el_in_port); + TEST_ASSERT_EQUAL((uint32_t)in_port, (uint32_t)el_in_port); + } else if (k == 1) { + ESP_LOGI(TAG, "Compare IN port, %d, %p,%p", __LINE__, in_port, el_in_port); + TEST_ASSERT_EQUAL((uint32_t)in_port1, (uint32_t)el_in_port); + } + k++; + el_in_port = el_in_port->next; + } + k = 0; + while (el_out_port) { + if (k == 0) { + ESP_LOGI(TAG, "Compare OUT port, %d, %p,%p", __LINE__, out_port, el_out_port); + TEST_ASSERT_EQUAL((uint32_t)out_port, (uint32_t)el_out_port); + } else if (k == 1) { + ESP_LOGI(TAG, "Compare OUT port, %d, %p,%p", __LINE__, out_port1, el_out_port); + TEST_ASSERT_EQUAL((uint32_t)out_port1, (uint32_t)el_out_port); + } + k++; + el_out_port = el_out_port->next; + } + + ESP_LOGW(TAG, "Unregister the in port, %d", __LINE__); + esp_gmf_element_unregister_in_port(fake_dec_el, in_port); + esp_gmf_element_unregister_in_port(fake_dec_el, in_port1); + esp_gmf_element_unregister_out_port(fake_dec_el, out_port); + esp_gmf_element_unregister_out_port(fake_dec_el, out_port1); + esp_gmf_port_deinit(in_port1); + esp_gmf_port_deinit(out_port1); + esp_gmf_obj_delete(reader); + esp_gmf_obj_delete(writer); + esp_gmf_obj_delete(fake_dec_el); + esp_gmf_db_deinit(db); + + ESP_GMF_MEM_SHOW(TAG); +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/main/cases/gmf_fifo_test.c b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/main/cases/gmf_fifo_test.c new file mode 100644 index 0000000..ddb50bc --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/main/cases/gmf_fifo_test.c @@ -0,0 +1,204 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "unity.h" +#include "freertos/FreeRTOS.h" +#include "esp_private/esp_clk.h" +#include "driver/sdmmc_host.h" +#include "esp_log.h" + +#include "esp_gmf_oal_mem.h" +#include "esp_gmf_fifo.h" +#include "gmf_ut_common.h" + +static const char *TAG = "TEST_ESP_GMF_FIFO"; +static bool read_is_done; +static bool write_is_done; +static bool is_abort_read; +static bool is_abort_write; +static bool read_run; +static bool write_run; + +static const char *file_name = "/sdcard/gmf_ut_test.mp3"; +static const char *file2_name = "/sdcard/gmf_ut_test2.mp3"; + +static void read_task(void *param) +{ + esp_gmf_fifo_handle_t fifo = (esp_gmf_fifo_handle_t)param; + esp_gmf_data_bus_block_t blk = {0}; + FILE *f = fopen(file_name, "rb"); + if (f == NULL) { + ESP_LOGE(TAG, "Open file failed"); + TEST_ASSERT_NOT_NULL(f); + goto read_task_err; + } + + int len = 4096; + struct stat sz = {0}; + stat(file_name, &sz); + ESP_LOGI(TAG, "Going to read, %p, file size %ld", param, sz.st_size); + read_run = true; + uint32_t start_cnt = 0; + uint32_t total_cnt = 0; + uint32_t file_size = 0; + while (read_run) { + int ret = esp_gmf_fifo_acquire_write(fifo, &blk, len, portMAX_DELAY); + if (ret != ESP_GMF_ERR_OK) { + if (ret == ESP_GMF_IO_ABORT) { + is_abort_write = true; + } + ESP_LOGE(TAG, "Acquire write failed, ret:%d", ret); + break; + } + ret = fread(blk.buf, 1, len, f); + blk.valid_size = ret; + file_size +=blk.valid_size; + start_cnt = esp_clk_rtc_time(); + esp_gmf_fifo_release_write(fifo, &blk, portMAX_DELAY); + total_cnt += (esp_clk_rtc_time() - start_cnt); + if (ret == 0) { + esp_gmf_fifo_done_write(fifo); + blk.is_last = true; + read_run = false; + } + } + ESP_LOGW(TAG, "Done to read, consumed time:%ld, read size:%ld", total_cnt, file_size); +read_task_err: + if (f) { + fclose(f); + } + read_is_done = true; + vTaskDelete(NULL); +} + +static void write_task(void *param) +{ + ESP_LOGI(TAG, "Going to write, %p", param); + esp_gmf_fifo_handle_t fifo = (esp_gmf_fifo_handle_t)param; + esp_gmf_data_bus_block_t blk = {0}; + FILE *f = fopen(file2_name, "wb+"); + if (f == NULL) { + ESP_LOGE(TAG, "Open file failed"); + TEST_ASSERT_NOT_NULL(f); + goto write_task_err; + } + int len = 4096; + write_run = true; + uint32_t start_cnt = 0; + uint32_t total_cnt = 0; + uint32_t file_size = 0; + while (write_run) { + start_cnt = esp_clk_rtc_time(); + int ret = esp_gmf_fifo_acquire_read(fifo, &blk, len, portMAX_DELAY); + if (ret != ESP_GMF_ERR_OK) { + if (ret == ESP_GMF_IO_ABORT) { + is_abort_read = true; + } + ESP_LOGE(TAG, "Acquire read failed, ret:%d", ret); + break; + } + total_cnt += (esp_clk_rtc_time() - start_cnt); + file_size += blk.valid_size; + + ret = fwrite(blk.buf, 1, blk.valid_size, f); + fflush(f); + esp_gmf_fifo_release_read(fifo, &blk, 0); + if (blk.is_last) { + break; + } + } + ESP_LOGW(TAG, "Done to write, consumed time:%ld, file size:%ld", total_cnt, file_size); +write_task_err: + if (f) { + fclose(f); + } + write_is_done = true; + vTaskDelete(NULL); +} + +TEST_CASE("FIFO read and write on different task", "[ESP_GMF_FIFO]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + esp_log_level_set("ESP_GMF_FIFO", ESP_LOG_VERBOSE); + + sdmmc_card_t *card = NULL; + esp_gmf_ut_setup_sdmmc(&card); + + esp_gmf_fifo_handle_t fifo = NULL; + esp_gmf_fifo_create(10, 1, &fifo); + ESP_LOGI(TAG, "TEST Create GMF FIFO, %p", fifo); + TEST_ASSERT_NOT_NULL(fifo); + uint8_t priority[][2] = { {5, 5}, {0,10,}, {10, 0}}; + + for (size_t i = 0; i < sizeof(priority) / sizeof(priority[0]); i++) { + ESP_LOGW(TAG, "Test FIFO with priority %d, %d\r\n", priority[i][0], priority[i][1]); + read_is_done = false; + write_is_done = false; + xTaskCreate(read_task, "read", 4096, fifo, priority[i][0], NULL); + xTaskCreate(write_task, "write", 4096, fifo, priority[i][1], NULL); + while (1) { + vTaskDelay(10 / portTICK_PERIOD_MS); + if (read_is_done && write_is_done) { + break; + } + } + int ret = verify_two_files(file_name, file2_name); + TEST_ASSERT_EQUAL(ret, ESP_OK); + esp_gmf_fifo_reset(fifo); + } + + esp_gmf_fifo_destroy(fifo); + + esp_gmf_ut_teardown_sdmmc(card); + vTaskDelay(10 / portTICK_PERIOD_MS); +} + +TEST_CASE("Abort when FIFO read and write on different task", "[ESP_GMF_FIFO]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + esp_log_level_set("ESP_GMF_FIFO", ESP_LOG_VERBOSE); + + sdmmc_card_t *card = NULL; + esp_gmf_ut_setup_sdmmc(&card); + + esp_gmf_fifo_handle_t fifo = NULL; + esp_gmf_fifo_create(10, 1, &fifo); + ESP_LOGI(TAG, "TEST Create GMF FIFO, %p", fifo); + TEST_ASSERT_NOT_NULL(fifo); + uint8_t priority[][2] = { {5, 5}, {0,10,}, {10, 0}}; + + for (size_t i = 0; i < sizeof(priority) / sizeof(priority[0]); i++) { + ESP_LOGW(TAG, "Test FIFO with priority %d, %d\r\n", priority[i][0], priority[i][1]); + read_is_done = false; + write_is_done = false; + is_abort_read = false; + is_abort_write = false; + xTaskCreate(read_task, "read", 4096, fifo, priority[i][0], NULL); + xTaskCreate(write_task, "write", 4096, fifo, priority[i][1], NULL); + int timeout_ms = 100; + while (1) { + vTaskDelay(10 / portTICK_PERIOD_MS); + timeout_ms -= 10; + if (timeout_ms == 0) { + ESP_LOGI(TAG, "Calling abort after 100ms"); + esp_gmf_fifo_abort(fifo); + vTaskDelay(50 / portTICK_PERIOD_MS); + } + if (read_is_done && write_is_done) { + break; + } + } + TEST_ASSERT_TRUE(is_abort_read); + TEST_ASSERT_TRUE(is_abort_write); + esp_gmf_fifo_reset(fifo); + } + + esp_gmf_fifo_destroy(fifo); + + esp_gmf_ut_teardown_sdmmc(card); + vTaskDelay(10 / portTICK_PERIOD_MS); +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/main/cases/gmf_io_test.c b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/main/cases/gmf_io_test.c new file mode 100644 index 0000000..49f6a29 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/main/cases/gmf_io_test.c @@ -0,0 +1,81 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "unity.h" +#include "esp_log.h" +#include "esp_gmf_oal_mem.h" +#include "gmf_fake_io.h" + +static const char *TAG = "TEST_GMF_FAKE_IO"; + +TEST_CASE("GMF IO read and write", "[ESP_GMF_IO]") +{ + esp_log_level_set("*", ESP_LOG_DEBUG); + + ESP_GMF_MEM_SHOW(TAG); + fake_io_cfg_t cfg = FAKE_IO_CFG_DEFAULT(); + cfg.dir = ESP_GMF_IO_DIR_READER; + esp_gmf_io_handle_t reader = NULL; + fake_io_init(&cfg, &reader); + TEST_ASSERT_NOT_NULL(reader); + ESP_GMF_MEM_SHOW(TAG); + + esp_gmf_io_set_uri(reader, "test.mp3"); + char *rd_uri = NULL; + esp_gmf_io_get_uri(reader, &rd_uri); + + TEST_ASSERT_EQUAL_STRING_LEN("test.mp3", rd_uri, strlen("test.mp3")); + int r_ret = esp_gmf_io_open(reader); + + ESP_GMF_MEM_SHOW(TAG); + uint64_t reader_total_bytes = 0; + uint64_t pos = 0; + esp_gmf_io_get_size(reader, &reader_total_bytes); + ESP_LOGI(TAG, "READER reader_total_bytes:%lld", reader_total_bytes); + + cfg.dir = ESP_GMF_IO_DIR_WRITER; + esp_gmf_io_handle_t writer = NULL; + fake_io_init(&cfg, &writer); + TEST_ASSERT_NOT_NULL(writer); + + esp_gmf_io_set_uri(writer, "test1.mp3"); + char *wr_uri = NULL; + esp_gmf_io_get_uri(writer, &wr_uri); + TEST_ASSERT_EQUAL_STRING_LEN("test1.mp3", wr_uri, strlen("test1.mp3")); + + int w_ret = esp_gmf_io_open(writer); + TEST_ASSERT_EQUAL(w_ret, ESP_GMF_ERR_OK); + + int read_len = 4 * 1024; + int k = 0; + while (k++ < 4) { + esp_gmf_payload_t in_load = {0}; + esp_gmf_payload_t out_load = {0}; + r_ret = esp_gmf_io_acquire_read(reader, &in_load, read_len, 0); + if (r_ret == 0) { + ESP_LOGI(TAG, "Read DONE"); + uint64_t total_bytes = 0; + esp_gmf_io_get_size(reader, &total_bytes); + ESP_LOGI(TAG, "w_total:%lld", total_bytes); + break; + } + w_ret = esp_gmf_io_acquire_write(writer, &out_load, r_ret, 0); + out_load.valid_size = in_load.valid_size; + esp_gmf_io_release_read(reader, &in_load, 0); + esp_gmf_io_release_write(writer, &out_load, 0); + + esp_gmf_io_get_pos(reader, &pos); + ESP_LOGI(TAG, "RD pos:%lld", pos); + esp_gmf_io_get_pos(writer, &pos); + ESP_LOGI(TAG, "WR pos:%lld", pos); + } + esp_gmf_io_close(reader); + esp_gmf_obj_delete(reader); + + esp_gmf_io_close(writer); + esp_gmf_obj_delete(writer); + ESP_GMF_MEM_SHOW(TAG); +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/main/cases/gmf_method_test.c b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/main/cases/gmf_method_test.c new file mode 100644 index 0000000..e7f28d5 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/main/cases/gmf_method_test.c @@ -0,0 +1,288 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "unity.h" +#include "freertos/FreeRTOS.h" + +#include "esp_log.h" +#include "esp_gmf_oal_mem.h" +#include "esp_gmf_method.h" +#include "gmf_ut_common.h" +#include "gmf_fake_dec.h" +#include "gmf_fake_io.h" + +static const char *TAG = "TEST_ESP_GMF_METHOD"; + +esp_gmf_err_t esp_gmf_method_func1(void *handle, esp_gmf_args_desc_t *arg_desc, uint8_t *buf, int buf_len) +{ + printf("%s\r\n", __func__); + return ESP_OK; +} +esp_gmf_err_t esp_gmf_method_func2(void *handle, esp_gmf_args_desc_t *arg_desc, uint8_t *buf, int buf_len) +{ + printf("%s\r\n", __func__); + return ESP_OK; +} +esp_gmf_err_t esp_gmf_method_func3(void *handle, esp_gmf_args_desc_t *arg_desc, uint8_t *buf, int buf_len) +{ + printf("%s\r\n", __func__); + return ESP_OK; +} + +TEST_CASE("Method create and destroy test", "[ESP_GMF_METHOD]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + esp_gmf_method_t *method = NULL; + ESP_GMF_MEM_SHOW(TAG); + esp_gmf_method_create("test1", esp_gmf_method_func1, NULL, &method); + esp_gmf_method_show(method); + esp_gmf_method_destroy(method); + method = NULL; + + esp_gmf_method_append(&method, "test1", esp_gmf_method_func1, NULL); + esp_gmf_method_append(&method, "test2", esp_gmf_method_func2, NULL); + esp_gmf_method_append(&method, "test3", esp_gmf_method_func3, NULL); + + esp_gmf_args_desc_t *args_desc = NULL; + esp_gmf_method_query_args(method, &args_desc); + + esp_gmf_method_show(method); + esp_gmf_method_destroy(method); + ESP_GMF_MEM_SHOW(TAG); +} + +TEST_CASE("Test basic arithmetic type arguments description", "[ESP_GMF_METHOD]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + ESP_GMF_MEM_SHOW(TAG); + + fake_dec_cfg_t cfg = DEFAULT_FAKE_DEC_CONFIG(); + esp_gmf_obj_handle_t dec = NULL; + fake_dec_init(&cfg, &dec); + + esp_gmf_port_handle_t out_port = NEW_ESP_GMF_PORT_OUT_BYTE(NULL, NULL, NULL, NULL, 1000, ESP_GMF_MAX_DELAY); + esp_gmf_element_register_out_port((esp_gmf_element_handle_t)dec, out_port); + esp_gmf_port_handle_t in_port = NEW_ESP_GMF_PORT_OUT_BYTE(NULL, NULL, NULL, NULL, 1000, ESP_GMF_MAX_DELAY); + esp_gmf_element_register_in_port((esp_gmf_element_handle_t)dec, in_port); + esp_gmf_element_process_open((esp_gmf_element_handle_t)dec, NULL); + + /// Check uint32_t + const esp_gmf_method_t *method_head = NULL; + const esp_gmf_method_t *method1 = NULL; + esp_gmf_element_get_method((esp_gmf_element_handle_t)dec, &method_head); + esp_gmf_method_found(method_head, "set_info", &method1); + size_t cnt = 0; + esp_gmf_args_desc_get_total_size(method1->args_desc, &cnt); + ESP_LOGI(TAG, "Total size %d", cnt); + uint8_t *buf = NULL; + buf = esp_gmf_oal_malloc(cnt); + + uint32_t rate = 48000; + uint32_t channel = 3; + uint32_t bits = 24; + esp_gmf_args_set_value(method1->args_desc, "rate", buf, (uint8_t *)&rate, sizeof(rate)); + esp_gmf_args_set_value(method1->args_desc, "ch", buf, (uint8_t *)&channel, sizeof(channel)); + esp_gmf_args_set_value(method1->args_desc, "bits", buf, (uint8_t *)&bits, sizeof(bits)); + esp_gmf_element_exe_method((esp_gmf_element_handle_t)dec, "set_info", buf, cnt); + + memset(buf, 0, cnt); + esp_gmf_element_exe_method((esp_gmf_element_handle_t)dec, "get_info", buf, cnt); + uint32_t get_rate = 0; + uint32_t get_channel = 0; + uint32_t get_bits = 0; + esp_gmf_method_found(method_head, "get_info", &method1); + esp_gmf_args_extract_value(method1->args_desc, "bits", buf, cnt, &get_bits); + esp_gmf_args_extract_value(method1->args_desc, "ch", buf, cnt, &get_channel); + esp_gmf_args_extract_value(method1->args_desc, "rate", buf, cnt, &get_rate); + + TEST_ASSERT_EQUAL(rate, get_rate); + TEST_ASSERT_EQUAL(bits, get_bits); + TEST_ASSERT_EQUAL(channel, get_channel); + + // Check string + cnt = 0; + esp_gmf_method_found(method_head, "set_name", &method1); + esp_gmf_args_desc_get_total_size(method1->args_desc, &cnt); + esp_gmf_oal_free(buf); + buf = esp_gmf_oal_malloc(cnt); + const char *name = "1234567890abcdefghijklmnopqrstvu"; + esp_gmf_args_set_value(method1->args_desc, "dec_name", buf, (uint8_t *)name, strlen(name)); + esp_gmf_element_exe_method((esp_gmf_element_handle_t)dec, "set_name", buf, cnt); + + memset(buf, 0, cnt); + esp_gmf_element_exe_method((esp_gmf_element_handle_t)dec, "get_name", buf, cnt); + printf("%s\n", buf); + TEST_ASSERT_NOT_EQUAL(0, strcasecmp(name, (char *)buf)); + + // Check uint64_t + cnt = 0; + esp_gmf_method_found(method_head, "set_size", &method1); + esp_gmf_args_desc_get_total_size(method1->args_desc, &cnt); + esp_gmf_oal_free(buf); + buf = esp_gmf_oal_malloc(cnt); + uint64_t x = 0xFFFFFFF99998888; + esp_gmf_args_set_value(method1->args_desc, "size", buf, (uint8_t *)&x, sizeof(x)); + esp_gmf_element_exe_method((esp_gmf_element_handle_t)dec, "set_size", buf, cnt); + + memset(buf, 0, cnt); + uint64_t x_out = 0; + esp_gmf_element_exe_method((esp_gmf_element_handle_t)dec, "get_size", buf, cnt); + TEST_ASSERT_NOT_EQUAL(x, x_out); + + // Check uint64_t and uint8_t + cnt = 0; + esp_gmf_method_found(method_head, "set_filter", &method1); + esp_gmf_args_desc_get_total_size(method1->args_desc, &cnt); + esp_gmf_oal_free(buf); + buf = esp_gmf_oal_malloc(cnt); + uint8_t filter_idx = 1; + esp_gmf_args_set_value(method1->args_desc, "index", buf, (uint8_t *)&filter_idx, sizeof(filter_idx)); + uint64_t filter = 0x1122334455667788; + esp_gmf_args_set_value(method1->args_desc, "filter", buf, (uint8_t *)&filter, sizeof(filter)); + esp_gmf_element_exe_method((esp_gmf_element_handle_t)dec, "set_filter", buf, cnt); + + filter_idx = 0; + esp_gmf_args_set_value(method1->args_desc, "index", buf, (uint8_t *)&filter_idx, sizeof(filter_idx)); + filter = 0xAABBCCDDEEFF0011; + esp_gmf_args_set_value(method1->args_desc, "filter", buf, (uint8_t *)&filter, sizeof(filter)); + esp_gmf_element_exe_method((esp_gmf_element_handle_t)dec, "set_filter", buf, cnt); + + memset(buf, 0, cnt); + esp_gmf_element_exe_method((esp_gmf_element_handle_t)dec, "get_filter", buf, cnt); + x_out = 0; + TEST_ASSERT_NOT_EQUAL(filter, x_out); + + esp_gmf_element_process_close((esp_gmf_element_handle_t)dec, NULL); + esp_gmf_obj_delete(dec); + esp_gmf_oal_free(buf); + ESP_GMF_MEM_SHOW(TAG); +} + +TEST_CASE("Test structure description", "[ESP_GMF_METHOD]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + ESP_GMF_MEM_SHOW(TAG); + + fake_dec_cfg_t cfg = DEFAULT_FAKE_DEC_CONFIG(); + esp_gmf_obj_handle_t dec = NULL; + fake_dec_init(&cfg, &dec); + + esp_gmf_port_handle_t out_port = NEW_ESP_GMF_PORT_OUT_BYTE(NULL, NULL, NULL, NULL, 1000, ESP_GMF_MAX_DELAY); + esp_gmf_element_register_out_port((esp_gmf_element_handle_t)dec, out_port); + esp_gmf_port_handle_t in_port = NEW_ESP_GMF_PORT_OUT_BYTE(NULL, NULL, NULL, NULL, 1000, ESP_GMF_MAX_DELAY); + esp_gmf_element_register_in_port((esp_gmf_element_handle_t)dec, in_port); + esp_gmf_element_process_open((esp_gmf_element_handle_t)dec, NULL); + + const esp_gmf_method_t *method_head = NULL; + const esp_gmf_method_t *method1 = NULL; + esp_gmf_element_get_method((esp_gmf_element_handle_t)dec, &method_head); + size_t cnt = 0; + uint8_t *buf = NULL; + + /// Check Structure + cnt = 0; + esp_gmf_method_found(method_head, "set_para", &method1); + + esp_gmf_args_desc_get_total_size(method1->args_desc, &cnt); + buf = esp_gmf_oal_malloc(cnt); + uint8_t idx = 1; + uint32_t type = 9; + uint32_t fc = 100; + float q = 4.0; + float gain = 3.5; + + esp_gmf_args_set_value(method1->args_desc, "index", buf, (uint8_t *)&idx, sizeof(idx)); + esp_gmf_args_set_value(method1->args_desc, "filter_type", buf, (uint8_t *)&type, sizeof(type)); + esp_gmf_args_set_value(method1->args_desc, "fc", buf, (uint8_t *)&fc, sizeof(fc)); + esp_gmf_args_set_value(method1->args_desc, "q", buf, (uint8_t *)&q, sizeof(q)); + esp_gmf_args_set_value(method1->args_desc, "gain", buf, (uint8_t *)&gain, sizeof(gain)); + esp_gmf_element_exe_method((esp_gmf_element_handle_t)dec, "set_para", buf, cnt); + memset(buf, 0, cnt); + idx = 3; + esp_gmf_method_found(method_head, "get_para", &method1); + esp_gmf_args_set_value(method1->args_desc, "index", buf, (uint8_t *)&idx, sizeof(idx)); + esp_gmf_element_exe_method((esp_gmf_element_handle_t)dec, "get_para", buf, cnt); + uint32_t type2 = 0; + uint32_t fc2 = 0; + float q2 = 0.0; + float gain2 = 0.0; + esp_gmf_args_extract_value(method1->args_desc, "filter_type", buf, cnt, &type2); + esp_gmf_args_extract_value(method1->args_desc, "fc", buf, cnt, &fc2); + esp_gmf_args_extract_value(method1->args_desc, "q", buf, cnt, (uint32_t *)&q2); + esp_gmf_args_extract_value(method1->args_desc, "gain", buf, cnt, (uint32_t *)&gain2); + TEST_ASSERT_EQUAL(type, type2); + TEST_ASSERT_EQUAL(fc, fc2); + TEST_ASSERT_EQUAL_FLOAT(q, q2); + TEST_ASSERT_EQUAL_FLOAT(gain, gain2); + + /// Check nested Structure + ESP_LOGI(TAG, ">>>>>> %s <<<<<<\r\n", "Check nested Structure"); + cnt = 0; + esp_gmf_method_found(method_head, "set_args", &method1); + esp_gmf_args_desc_get_total_size(method1->args_desc, &cnt); + ESP_LOGI(TAG, "The total size :%d", cnt); + esp_gmf_oal_free(buf); + buf = esp_gmf_oal_malloc(cnt); + + const char *name = "1234567890abcdef"; + uint32_t a = 0x99; + uint32_t b = 0x33; + uint32_t c = 0xbb; + uint32_t d = 0x98; + uint32_t e = 0x32; + uint32_t f = 0xba; + uint32_t val = 0xdd; + + esp_gmf_args_set_value(method1->args_desc, "a", buf, (uint8_t *)&a, sizeof(a)); + esp_gmf_args_set_value(method1->args_desc, "b", buf, (uint8_t *)&b, sizeof(b)); + esp_gmf_args_set_value(method1->args_desc, "c", buf, (uint8_t *)&c, sizeof(c)); + + esp_gmf_args_set_value(method1->args_desc, "d", buf, (uint8_t *)&d, sizeof(d)); + esp_gmf_args_set_value(method1->args_desc, "e", buf, (uint8_t *)&e, sizeof(e)); + esp_gmf_args_set_value(method1->args_desc, "f", buf, (uint8_t *)&f, sizeof(f)); + + esp_gmf_args_set_value(method1->args_desc, "value", buf, (uint8_t *)&val, sizeof(val)); + esp_gmf_args_set_value(method1->args_desc, "label", buf, (uint8_t *)name, strlen(name)); + + esp_gmf_element_exe_method((esp_gmf_element_handle_t)dec, "set_args", buf, cnt); + + memset(buf, 0, cnt); + esp_gmf_method_found(method_head, "get_args", &method1); + esp_gmf_element_exe_method((esp_gmf_element_handle_t)dec, "get_args", buf, cnt); + + uint32_t a2 = 0; + uint32_t b2 = 0; + uint32_t c2 = 0; + uint32_t d2 = 0; + uint32_t e2 = 0; + uint32_t f2 = 0; + uint32_t val2 = 0; + char out_name[16] = {0}; + + esp_gmf_args_extract_value(method1->args_desc, "a", buf, cnt, &a2); + esp_gmf_args_extract_value(method1->args_desc, "b", buf, cnt, &b2); + esp_gmf_args_extract_value(method1->args_desc, "c", buf, cnt, &c2); + esp_gmf_args_extract_value(method1->args_desc, "d", buf, cnt, &d2); + esp_gmf_args_extract_value(method1->args_desc, "e", buf, cnt, &e2); + esp_gmf_args_extract_value(method1->args_desc, "f", buf, cnt, &f2); + + esp_gmf_args_extract_value(method1->args_desc, "value", buf, cnt, &val2); + esp_gmf_args_extract_value(method1->args_desc, "label", buf, cnt, (uint32_t *)&out_name); + + TEST_ASSERT_EQUAL(a, a2); + TEST_ASSERT_EQUAL(b, b2); + TEST_ASSERT_EQUAL(c, c2); + TEST_ASSERT_EQUAL(d, d2); + TEST_ASSERT_EQUAL(e, e2); + TEST_ASSERT_EQUAL(f, f2); + TEST_ASSERT_EQUAL(val, val2); + TEST_ASSERT_NOT_EQUAL(0, strcasecmp(name, (char *)out_name)); + + esp_gmf_element_process_close((esp_gmf_element_handle_t)dec, NULL); + esp_gmf_obj_delete(dec); + esp_gmf_oal_free(buf); + ESP_GMF_MEM_SHOW(TAG); +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/main/cases/gmf_pbuf_test.c b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/main/cases/gmf_pbuf_test.c new file mode 100644 index 0000000..8aab799 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/main/cases/gmf_pbuf_test.c @@ -0,0 +1,371 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "unity.h" +#include "freertos/FreeRTOS.h" +#include "esp_private/esp_clk.h" +#include "driver/sdmmc_host.h" +#include "esp_log.h" + +#include "esp_gmf_oal_mem.h" +#include "gmf_ut_common.h" +#include "esp_gmf_pbuf.h" + +static const char *TAG = "TEST_ESP_GMF_PBUF"; +static bool task_is_done; +static bool is_abort_read; +static bool is_abort_write; +static bool read_run; +static bool write_run; + +static const char *file_name = "/sdcard/gmf_ut_test.mp3"; +static const char *file2_name = "/sdcard/gmf_ut_test_out.mp3"; +extern uint32_t mem_start; + +static void read_task(void *param) +{ + ESP_LOGI(TAG, "Going to read, %p", param); + esp_gmf_pbuf_handle_t pbuf = (esp_gmf_pbuf_handle_t)param; + FILE *f = fopen(file_name, "rb"); + if (f == NULL) { + ESP_LOGE(TAG, "Open file failed on %s", __func__); + goto read_task_err; + } + int read_len = 4 * 1024; + char *buf = esp_gmf_oal_malloc(read_len); + ESP_GMF_MEM_CHECK(TAG, buf, goto read_task_err); + read_run = true; + uint32_t start_cnt = 0; + uint32_t total_cnt = 0; + int mem_start = 0; + esp_gmf_data_bus_block_t blk = {0}; + while (read_run) { + int ret = esp_gmf_pbuf_acquire_write(pbuf, &blk, read_len, portMAX_DELAY); + if (ret == ESP_GMF_IO_FAIL) { + // Pbuf is full, waiting the buffer to be read. + vTaskDelay(1); + continue; + } + if (ret == ESP_GMF_IO_ABORT) { + is_abort_write = true; + break; + } + ret = fread(blk.buf, 1, blk.buf_length, f); + ESP_LOGI(TAG, "Reading from file, ret:%d, buf:%p, len:%d", ret, blk.buf, blk.buf_length); + blk.valid_size = ret; + start_cnt = esp_clk_rtc_time(); + esp_gmf_pbuf_release_write(pbuf, &blk, 0); + total_cnt += (esp_clk_rtc_time() - start_cnt); + if (ret != blk.buf_length) { + esp_gmf_pbuf_done_write(pbuf); + blk.is_last = true; + read_run = false; + } + } + fclose(f); + esp_gmf_oal_free(buf); + ESP_LOGI(TAG, "Done to read, %" PRIu32 ", memory time:%d", total_cnt, mem_start); +read_task_err: + vTaskDelete(NULL); +} + +static void write_task(void *param) +{ + ESP_LOGI(TAG, "Going to write, %p", param); + esp_gmf_pbuf_handle_t pbuf = (esp_gmf_pbuf_handle_t)param; + FILE *f = fopen(file2_name, "wb+"); + if (f == NULL) { + ESP_LOGE(TAG, "Open file failed"); + goto write_task_err; + } + write_run = true; + uint32_t start_cnt = 0; + uint32_t total_cnt = 0; + esp_gmf_data_bus_block_t blk = {0}; + while (write_run) { + start_cnt = esp_clk_rtc_time(); + int ret = esp_gmf_pbuf_acquire_read(pbuf, &blk, 0, portMAX_DELAY); + if (ret == ESP_GMF_IO_FAIL) { + // Pbuf is empty, waiting the buffer to be fill. + vTaskDelay(1); + continue; + } + if (ret == ESP_GMF_IO_ABORT) { + is_abort_read = true; + break; + } + total_cnt += (esp_clk_rtc_time() - start_cnt); + ESP_LOGI(TAG, "Writing to file, ret:%d, buf:%p, len:%d", ret, blk.buf, blk.valid_size); + ret = fwrite(blk.buf, 1, blk.valid_size, f); + if (blk.is_last) { + fclose(f); + write_run = false; + task_is_done = true; + } + esp_gmf_pbuf_release_read(pbuf, &blk, 0); + ESP_GMF_MEM_SHOW(TAG); + } + ESP_LOGI(TAG, "Done to write, %" PRIu32 "", total_cnt); +write_task_err: + task_is_done = true; + vTaskDelete(NULL); +} + +static void wr_rd_task(void *param) +{ + ESP_LOGI(TAG, "Going to write and read, %p", param); + esp_gmf_pbuf_handle_t pbuf = (esp_gmf_pbuf_handle_t)param; + FILE *f = fopen(file_name, "rb"); + if (f == NULL) { + ESP_LOGE(TAG, "Open file failed"); + goto wr_task_err; + } + FILE *fw = fopen(file2_name, "wb+"); + if (fw == NULL) { + ESP_LOGE(TAG, "Open file failed"); + goto wr_task_err; + } + int read_len = 4 * 1024; + read_run = true; + uint32_t start_cnt = 0; + uint32_t total_cnt = 0; + int mem_start = 0; + esp_gmf_data_bus_block_t blk = {0}; + while (read_run) { + esp_gmf_pbuf_acquire_write(pbuf, &blk, read_len, portMAX_DELAY); + int ret = fread(blk.buf, 1, blk.buf_length, f); + blk.valid_size = ret; + start_cnt = esp_clk_rtc_time(); + esp_gmf_pbuf_release_write(pbuf, &blk, 0); + total_cnt += (esp_clk_rtc_time() - start_cnt); + if (ret != read_len) { + esp_gmf_pbuf_done_write(pbuf); + blk.is_last = true; + ESP_LOGE(TAG, "Reading, buf:%p,vld:%d, len:%d", blk.buf, blk.valid_size, blk.buf_length); + } else { + ESP_LOGI(TAG, "Reading, buf:%p,vld:%d, len:%d", blk.buf, blk.valid_size, blk.buf_length); + } + + esp_gmf_data_bus_block_t rd_blk = {0}; + start_cnt = esp_clk_rtc_time(); + ret = esp_gmf_pbuf_acquire_read(pbuf, &rd_blk, 0, portMAX_DELAY); + total_cnt += (esp_clk_rtc_time() - start_cnt); + ESP_LOGI(TAG, "Witing, ret:%d, buf:%p, buf:%d, last:%d", ret, rd_blk.buf, rd_blk.valid_size, rd_blk.is_last); + ret = fwrite(rd_blk.buf, 1, rd_blk.valid_size, fw); + if (rd_blk.is_last == true) { + task_is_done = true; + read_run = 0; + } + esp_gmf_pbuf_release_read(pbuf, &rd_blk, 0); + } + fclose(f); + fclose(fw); + // esp_gmf_oal_free(buf); + ESP_LOGI(TAG, "Done to read, %ld, memory time:%d", total_cnt, mem_start); +wr_task_err: + vTaskDelete(NULL); +} + +// Multiple acquire write one times +// And check the capacity +static void wr_rd_task2(void *param) +{ + ESP_LOGI(TAG, "Going to write and read, %p", param); + esp_gmf_pbuf_handle_t pbuf = (esp_gmf_pbuf_handle_t)param; + FILE *f = fopen(file_name, "rb"); + if (f == NULL) { + ESP_LOGE(TAG, "Open file failed"); + goto wr_task_err; + } + FILE *fw = fopen(file2_name, "wb+"); + if (fw == NULL) { + ESP_LOGE(TAG, "Open file failed"); + goto wr_task_err; + } + int read_len = 4 * 1024; + read_run = true; + uint32_t start_cnt = 0; + uint32_t total_cnt = 0; + int mem_start = 0; + esp_gmf_data_bus_block_t blk = {0}; + while (read_run) { + for (int i = 0; i < 3; ++i) { + esp_gmf_pbuf_acquire_write(pbuf, &blk, read_len, portMAX_DELAY); + int ret = fread(blk.buf, 1, blk.buf_length, f); + blk.valid_size = ret; + start_cnt = esp_clk_rtc_time(); + esp_gmf_pbuf_release_write(pbuf, &blk, 0); + total_cnt += (esp_clk_rtc_time() - start_cnt); + if (ret != read_len) { + ESP_LOGW(TAG, "Write, buf:%p,vld:%d, len:%d,%d", blk.buf, blk.valid_size, blk.buf_length, blk.is_last); + esp_gmf_pbuf_done_write(pbuf); + blk.is_last = true; + break; + } else { + ESP_LOGI(TAG, "Write, buf:%p,vld:%d, len:%d", blk.buf, blk.valid_size, blk.buf_length); + } + } + + esp_gmf_data_bus_block_t rd_blk = {0}; + start_cnt = esp_clk_rtc_time(); + while (1) { + int ret = esp_gmf_pbuf_acquire_read(pbuf, &rd_blk, 0, portMAX_DELAY); + if (ret == ESP_GMF_IO_FAIL) { + break; + } + total_cnt += (esp_clk_rtc_time() - start_cnt); + ret = fwrite(rd_blk.buf, 1, rd_blk.valid_size, fw); + if (rd_blk.is_last == true) { + task_is_done = true; + read_run = 0; + ESP_LOGW(TAG, "Read, %p, buf_len:%d, last:%d", rd_blk.buf, rd_blk.valid_size, rd_blk.is_last); + break; + } else { + ESP_LOGI(TAG, "Read, %p, buf_len:%d, last:%d", rd_blk.buf, rd_blk.valid_size, rd_blk.is_last); + } + esp_gmf_pbuf_release_read(pbuf, &rd_blk, 0); + } + } + fclose(f); + fclose(fw); + ESP_LOGI(TAG, "Done to read, %ld, memory time:%d", total_cnt, mem_start); +wr_task_err: + vTaskDelete(NULL); +} + +TEST_CASE("One task for read write test", "[ESP_GMF_PBUF]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + esp_log_level_set("ESP_GMF_PBUF", ESP_LOG_INFO); + ESP_LOGI(TAG, "TEST Create GMF Pbuf"); + + sdmmc_card_t *card = NULL; + esp_gmf_ut_setup_sdmmc(&card); + + task_is_done = false; + esp_gmf_pbuf_handle_t pbuf = NULL; + esp_gmf_pbuf_create(10, &pbuf); + ESP_LOGI(TAG, "TEST Create GMF, %p", pbuf); + TEST_ASSERT_NOT_NULL(pbuf); + uint32_t start_cnt = esp_clk_rtc_time(); + xTaskCreate(wr_rd_task, "one_rw", 4096, pbuf, 5, NULL); + while (1) { + vTaskDelay(2 / portTICK_PERIOD_MS); + if (task_is_done) { + break; + } + } + ESP_LOGW(TAG, "Taken %lld ms to copy file, start to verify the files", (esp_clk_rtc_time() - start_cnt) / 1000); + int ret = verify_two_files(file_name, file2_name); + TEST_ASSERT_EQUAL(ESP_OK, ret); + + esp_gmf_pbuf_destroy(pbuf); + esp_gmf_ut_teardown_sdmmc(card); + vTaskDelay(100); + ESP_LOGE(TAG, "%s,%d", __func__, __LINE__); +} + +TEST_CASE("One task for multiple read write test", "[ESP_GMF_PBUF]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + esp_log_level_set("ESP_GMF_PBUF", ESP_LOG_INFO); + ESP_LOGI(TAG, "TEST Create GMF Pbuf"); + + sdmmc_card_t *card = NULL; + esp_gmf_ut_setup_sdmmc(&card); + + task_is_done = false; + esp_gmf_pbuf_handle_t pbuf = NULL; + esp_gmf_pbuf_create(10, &pbuf); + ESP_LOGI(TAG, "TEST Create GMF, %p", pbuf); + TEST_ASSERT_NOT_NULL(pbuf); + uint32_t start_cnt = esp_clk_rtc_time(); + xTaskCreate(wr_rd_task2, "MULT_RW", 4096, pbuf, 5, NULL); + while (1) { + vTaskDelay(2 / portTICK_PERIOD_MS); + if (task_is_done) { + break; + } + } + ESP_LOGW(TAG, "Taken %lld ms to copy file.Start verify the files", (esp_clk_rtc_time() - start_cnt) / 1000); + int ret = verify_two_files(file_name, file2_name); + TEST_ASSERT_EQUAL(ESP_OK, ret); + + esp_gmf_pbuf_destroy(pbuf); + esp_gmf_ut_teardown_sdmmc(card); + vTaskDelay(100); + ESP_LOGE(TAG, "%s,%d", __func__, __LINE__); +} + +TEST_CASE("Read task and write task thread safe test", "[ESP_GMF_PBUF]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + esp_log_level_set("ESP_GMF_PBUF", ESP_LOG_INFO); + ESP_LOGI(TAG, "TEST Create GMF Pbuf"); + + sdmmc_card_t *card = NULL; + esp_gmf_ut_setup_sdmmc(&card); + + task_is_done = false; + esp_gmf_pbuf_handle_t pbuf = NULL; + esp_gmf_pbuf_create(10, &pbuf); + ESP_LOGI(TAG, "TEST Create GMF, %p", pbuf); + TEST_ASSERT_NOT_NULL(pbuf); + uint32_t start_cnt = esp_clk_rtc_time(); + xTaskCreate(read_task, "read", 4096, pbuf, 3, NULL); + xTaskCreate(write_task, "wr_to_file", 4096, pbuf, 3, NULL); + while (1) { + vTaskDelay(2 / portTICK_PERIOD_MS); + if (task_is_done) { + break; + } + } + ESP_LOGW(TAG, "Taken %lld ms to copy file.Start verify the files", (esp_clk_rtc_time() - start_cnt) / 1000); + int ret = verify_two_files(file_name, file2_name); + TEST_ASSERT_EQUAL(ESP_OK, ret); + + esp_gmf_pbuf_destroy(pbuf); + esp_gmf_ut_teardown_sdmmc(card); + ESP_LOGE(TAG, "%s,%d", __func__, __LINE__); +} + +TEST_CASE("Abort when read task and write task thread safe test", "[ESP_GMF_PBUF]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + esp_log_level_set("ESP_GMF_PBUF", ESP_LOG_INFO); + ESP_LOGI(TAG, "TEST Create GMF Pbuf"); + + sdmmc_card_t *card = NULL; + esp_gmf_ut_setup_sdmmc(&card); + + task_is_done = false; + is_abort_read = false; + is_abort_write = false; + esp_gmf_pbuf_handle_t pbuf = NULL; + esp_gmf_pbuf_create(10, &pbuf); + ESP_LOGI(TAG, "TEST Create GMF, %p", pbuf); + TEST_ASSERT_NOT_NULL(pbuf); + xTaskCreate(read_task, "read", 4096, pbuf, 3, NULL); + xTaskCreate(write_task, "wr_to_file", 4096, pbuf, 3, NULL); + int timeout_ms = 100; + while (1) { + vTaskDelay(2 / portTICK_PERIOD_MS); + timeout_ms -= 2; + if (timeout_ms == 0) { + ESP_LOGI(TAG, "Calling abort after 100ms"); + esp_gmf_pbuf_abort(pbuf); + vTaskDelay(50 / portTICK_PERIOD_MS); + } + if (task_is_done) { + break; + } + } + TEST_ASSERT_TRUE(is_abort_read); + TEST_ASSERT_TRUE(is_abort_write); + esp_gmf_pbuf_destroy(pbuf); + esp_gmf_ut_teardown_sdmmc(card); + ESP_LOGE(TAG, "%s,%d", __func__, __LINE__); +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/main/cases/gmf_pipeline_test.c b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/main/cases/gmf_pipeline_test.c new file mode 100644 index 0000000..4425f42 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/main/cases/gmf_pipeline_test.c @@ -0,0 +1,398 @@ +/** + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "unity.h" +#include "freertos/FreeRTOS.h" +#include "esp_log.h" +#include "esp_gmf_oal_mem.h" +#include "esp_gmf_pipeline.h" +#include "esp_gmf_task.h" +#include "esp_gmf_audio_element.h" +#include "esp_gmf_pool.h" +#include "esp_fourcc.h" +#include "gmf_fake_io.h" + +static const char *TAG = "TEST_ESP_GMF_PIPELINE"; + +#define DEFAULT_RUN_LOOPS (200) + +typedef bool (*dependency_res_checker)(esp_gmf_pipeline_handle_t pipeline); + +typedef struct { + uint8_t open_order; +} pipeline_state_t; + +typedef struct { + esp_gmf_audio_element_t parent; + uint8_t open_order; + uint8_t open_count; + uint8_t close_count; + int running_count; + esp_gmf_info_sound_t snd_info; +} general_el_t; + +typedef struct { + bool is_dependent; + bool report_in_process; + int report_pos; + pipeline_state_t *state; +} general_el_cfg_t; + +static esp_gmf_err_t general_el_init(general_el_cfg_t *config, esp_gmf_element_handle_t *handle); + +static esp_gmf_err_t general_el_new(void *cfg, esp_gmf_obj_handle_t *handle) +{ + return general_el_init(cfg, (esp_gmf_element_handle_t *)handle); +} + +static esp_gmf_job_err_t general_el_open(esp_gmf_element_handle_t self, void *para) +{ + general_el_t *el = (general_el_t *)self; + general_el_cfg_t *cfg = (general_el_cfg_t *)OBJ_GET_CFG(self); + el->open_order = cfg->state->open_order++; + el->open_count++; + if (cfg->is_dependent && cfg->report_in_process == false) { + // Notify dependency to next + esp_gmf_element_notify_snd_info(self, &el->snd_info); + } + return ESP_GMF_JOB_ERR_OK; +} + +static esp_gmf_job_err_t general_el_close(esp_gmf_element_handle_t self, void *para) +{ + general_el_t *el = (general_el_t *)self; + el->close_count++; + return ESP_GMF_ERR_OK; +} + +static esp_gmf_job_err_t general_el_process(esp_gmf_element_handle_t self, void *para) +{ + general_el_t *el = (general_el_t *)self; + general_el_cfg_t *cfg = (general_el_cfg_t *)OBJ_GET_CFG(self); + esp_gmf_job_err_t out_len = ESP_GMF_JOB_ERR_OK; + esp_gmf_port_handle_t in_port = ESP_GMF_ELEMENT_GET(self)->in; + esp_gmf_port_handle_t out_port = ESP_GMF_ELEMENT_GET(self)->out; + esp_gmf_payload_t *in_load = NULL; + esp_gmf_payload_t *out_load = NULL; + int bytes = ESP_GMF_ELEMENT_GET(el)->in_attr.data_size; + esp_gmf_err_io_t load_ret = esp_gmf_port_acquire_in(in_port, &in_load, bytes, ESP_GMF_MAX_DELAY); + do { + ESP_GMF_PORT_CHECK(TAG, load_ret, out_len, break, "Failed to acquire in, ret: %d", load_ret); + + int running_count = el->running_count; + el->running_count++; + if ((el->open_order == 0) && (el->running_count >= DEFAULT_RUN_LOOPS)) { + in_load->is_done = true; + } + if (cfg->is_dependent && cfg->report_in_process) { + if (running_count < cfg->report_pos) { + // No need report for all data consumed + out_len = ESP_GMF_JOB_ERR_CONTINUE; + break; + } + if (running_count == cfg->report_pos) { + // Report info to next + esp_gmf_element_notify_snd_info(self, &el->snd_info); + } + } + load_ret = esp_gmf_port_acquire_out(out_port, &out_load, in_load->buf_length, ESP_GMF_MAX_DELAY); + ESP_GMF_PORT_CHECK(TAG, load_ret, out_len, break, "Failed to acquire out, ret: %d", load_ret); + out_load->pts = in_load->pts; + out_load->is_done = in_load->is_done; + if (in_load->is_done) { + out_len = ESP_GMF_JOB_ERR_DONE; + } + } while (0); + if (out_load != NULL) { + esp_gmf_port_release_out(out_port, out_load, ESP_GMF_MAX_DELAY); + } + if (in_load != NULL) { + esp_gmf_port_release_in(in_port, in_load, ESP_GMF_MAX_DELAY); + } + return out_len; +} + +static esp_gmf_err_t general_el_event_handler(esp_gmf_event_pkt_t *evt, void *ctx) +{ + ESP_GMF_NULL_CHECK(TAG, ctx, { return ESP_GMF_ERR_INVALID_ARG; }); + ESP_GMF_NULL_CHECK(TAG, evt, { return ESP_GMF_ERR_INVALID_ARG; }); + if ((evt->type != ESP_GMF_EVT_TYPE_REPORT_INFO) + || (evt->sub != ESP_GMF_INFO_SOUND) + || (evt->payload == NULL)) { + return ESP_GMF_ERR_OK; + } + esp_gmf_element_handle_t self = (esp_gmf_element_handle_t)ctx; + esp_gmf_event_state_t state = ESP_GMF_EVENT_STATE_NONE; + esp_gmf_element_get_state(self, &state); + general_el_t *el = (general_el_t *)self; + memcpy(&el->snd_info, evt->payload, sizeof(esp_gmf_info_sound_t)); + if (state == ESP_GMF_EVENT_STATE_NONE) { + esp_gmf_element_set_state(self, ESP_GMF_EVENT_STATE_INITIALIZED); + } + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t general_el_destroy(esp_gmf_element_handle_t self) +{ + void *cfg = OBJ_GET_CFG(self); + if (cfg) { + esp_gmf_oal_free(cfg); + } + esp_gmf_audio_el_deinit(self); + esp_gmf_oal_free(self); + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t general_el_init(general_el_cfg_t *config, esp_gmf_element_handle_t *handle) +{ + ESP_GMF_NULL_CHECK(TAG, handle, { return ESP_GMF_ERR_INVALID_ARG; }); + *handle = NULL; + esp_gmf_err_t ret = ESP_GMF_ERR_OK; + general_el_t *el = esp_gmf_oal_calloc(1, sizeof(general_el_t)); + ESP_GMF_MEM_VERIFY( + TAG, el, { return ESP_GMF_ERR_MEMORY_LACK; }, "General element", sizeof(general_el_t)); + esp_gmf_obj_t *obj = (esp_gmf_obj_t *)el; + obj->new_obj = general_el_new; + obj->del_obj = general_el_destroy; + esp_gmf_obj_set_tag(obj, "general"); + esp_gmf_element_cfg_t el_cfg = { 0 }; + ESP_GMF_ELEMENT_IN_PORT_ATTR_SET(el_cfg.in_attr, ESP_GMF_EL_PORT_CAP_SINGLE, 0, 0, + ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, ESP_GMF_ELEMENT_PORT_DATA_SIZE_DEFAULT); + ESP_GMF_ELEMENT_IN_PORT_ATTR_SET(el_cfg.out_attr, ESP_GMF_EL_PORT_CAP_SINGLE, 0, 0, + ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, ESP_GMF_ELEMENT_PORT_DATA_SIZE_DEFAULT); + el_cfg.dependency = config->is_dependent; + general_el_cfg_t *cfg = esp_gmf_oal_calloc(1, sizeof(general_el_cfg_t)); + ESP_GMF_MEM_VERIFY( + TAG, cfg, {ret = ESP_GMF_ERR_MEMORY_LACK; goto GENERAL_EL_FAIL; }, "general element configuration", sizeof(general_el_cfg_t)); + esp_gmf_obj_set_config(obj, cfg, sizeof(general_el_cfg_t)); + if (config) { + memcpy(cfg, config, sizeof(general_el_cfg_t)); + } + ret = esp_gmf_audio_el_init(el, &el_cfg); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto GENERAL_EL_FAIL, "Failed to initialize general element"); + ESP_GMF_ELEMENT_GET(el)->ops.open = general_el_open; + ESP_GMF_ELEMENT_GET(el)->ops.process = general_el_process; + ESP_GMF_ELEMENT_GET(el)->ops.close = general_el_close; + ESP_GMF_ELEMENT_GET(el)->ops.event_receiver = general_el_event_handler; + *handle = obj; + return ESP_GMF_ERR_OK; +GENERAL_EL_FAIL: + general_el_destroy(obj); + return ret; +} + +esp_gmf_err_t pipeline_event(esp_gmf_event_pkt_t *pkt, void *ctx) +{ + if (pkt->type == ESP_GMF_EVT_TYPE_CHANGE_STATE && (pkt->sub == ESP_GMF_EVENT_STATE_FINISHED || pkt->sub == ESP_GMF_EVENT_STATE_ERROR)) { + bool *running = (bool *)ctx; + *running = false; + } + return ESP_GMF_ERR_OK; +} + +static int pipeline_dependency_test(general_el_cfg_t *cfg, int n, dependency_res_checker checker) +{ + esp_log_level_set("*", ESP_LOG_INFO); + ESP_GMF_MEM_SHOW(TAG); + + // Init for pool + esp_gmf_pool_handle_t pool = NULL; + esp_gmf_pool_init(&pool); + TEST_ASSERT_NOT_NULL(pool); + char el_name[n][2]; + for (int i = 0; i < n; i++) { + esp_gmf_element_handle_t el = NULL; + general_el_init(cfg + i, &el); + TEST_ASSERT_NOT_NULL(el); + snprintf(el_name[i], 2, "%c", 'A' + i); + esp_gmf_pool_register_element(pool, el, el_name[i]); + } + fake_io_cfg_t io_cfg = FAKE_IO_CFG_DEFAULT(); + io_cfg.dir = ESP_GMF_IO_DIR_READER; + esp_gmf_io_handle_t reader = NULL; + fake_io_init(&io_cfg, &reader); + TEST_ASSERT_NOT_NULL(reader); + esp_gmf_pool_register_io(pool, reader, "io_in"); + + esp_gmf_io_handle_t writer = NULL; + io_cfg.dir = ESP_GMF_IO_DIR_WRITER; + fake_io_init(&io_cfg, &writer); + TEST_ASSERT_NOT_NULL(writer); + esp_gmf_pool_register_io(pool, writer, "io_out"); + + esp_gmf_pipeline_handle_t pipe = NULL; + const char *el_name_array[n]; + for (int i = 0; i < n; i++) { + el_name_array[i] = el_name[i]; + } + esp_gmf_pool_new_pipeline(pool, "io_in", el_name_array, n, "io_out", &pipe); + bool running = true; + + esp_gmf_pipeline_set_event(pipe, pipeline_event, &running); + + esp_gmf_task_cfg_t task_cfg = DEFAULT_ESP_GMF_TASK_CONFIG(); + task_cfg.ctx = NULL; + task_cfg.cb = NULL; + esp_gmf_task_handle_t work_task = NULL; + esp_gmf_task_init(&task_cfg, &work_task); + TEST_ASSERT_NOT_NULL(work_task); + esp_gmf_pipeline_bind_task(pipe, work_task); + + esp_gmf_info_sound_t info = { + .sample_rates = 16000, + .channels = 2, + .bits = 16, + .format_id = ESP_FOURCC_PCM, + }; + esp_gmf_pipeline_report_info(pipe, ESP_GMF_INFO_SOUND, &info, sizeof(info)); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_run(pipe)); + int timeout = 10; + while (timeout-- > 0 && running) { + vTaskDelay(1000 / portTICK_RATE_MS); + } + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_stop(pipe)); + if (checker) { + TEST_ASSERT_TRUE(checker(pipe)); + } + esp_gmf_task_deinit(work_task); + esp_gmf_pipeline_destroy(pipe); + esp_gmf_pool_deinit(pool); + ESP_GMF_MEM_SHOW(TAG); + return 0; +} + +static bool general_dependency_check(esp_gmf_pipeline_handle_t pipeline) +{ + esp_gmf_element_handle_t iter = NULL; + esp_gmf_pipeline_get_head_el(pipeline, &iter); + general_el_t *head = (general_el_t *)iter; + esp_gmf_element_handle_t next = NULL; + uint8_t open_order = 0; + if (head->open_order != open_order) { + ESP_LOGE(TAG, "Head order wrong as %d", head->open_order); + return false; + } + general_el_cfg_t *cfg = (general_el_cfg_t *)OBJ_GET_CFG(head); + ESP_LOGI(TAG, "%s open_count %d close_count %d running_count %d order %d", OBJ_GET_TAG(head), + head->open_count, head->close_count, head->running_count, + head->open_order); + int skip_count = cfg->report_pos; + while (esp_gmf_pipeline_get_next_el(pipeline, iter, &next) == ESP_GMF_ERR_OK) { + // Verify results all same + general_el_t *el = (general_el_t *)next; + cfg = (general_el_cfg_t *)OBJ_GET_CFG(next); + ESP_LOGI(TAG, "%s open_count %d close_count %d running_count %d order %d", OBJ_GET_TAG(el), + el->open_count, el->close_count, el->running_count, + el->open_order); + open_order++; + general_el_t *next_el = (general_el_t *)next; + if (next_el->open_order != open_order) { + ESP_LOGE(TAG, "%s order wrong as %d", OBJ_GET_TAG(next), next_el->open_order); + return false; + } + if ((el->open_count != 1) || + (el->close_count != 1) || + (el->running_count + skip_count != head->running_count)) { + ESP_LOGE(TAG, "Failed check for %s", OBJ_GET_TAG(el)); + return false; + } + skip_count += cfg->report_pos; + iter = next; + } + return true; +} + +TEST_CASE("A - B - C - D (no dependency)", "[ESP_GMF_PIPELINE]") +{ + pipeline_state_t state = {}; + general_el_cfg_t cfg[4] = {}; + for (int i = 0; i < 4; i++) { + cfg[i].state = &state; + } + pipeline_dependency_test(cfg, 4, general_dependency_check); + ESP_GMF_MEM_SHOW(TAG); +} + +TEST_CASE("A - B - C - D (all has dependency)", "[ESP_GMF_PIPELINE]") +{ + pipeline_state_t state = {}; + general_el_cfg_t cfg[4] = {}; + for (int i = 0; i < 4; i++) { + cfg[i].is_dependent = true; + cfg[i].state = &state; + } + pipeline_dependency_test(cfg, 4, general_dependency_check); + ESP_GMF_MEM_SHOW(TAG); +} + +TEST_CASE("A(Y) - B(Y) - C(N) - D(Y) (C no dependency)", "[ESP_GMF_PIPELINE]") +{ + pipeline_state_t state = {}; + general_el_cfg_t cfg[4] = {}; + for (int i = 0; i < 4; i++) { + if ('A' + i == 'C') { + cfg[i].is_dependent = false; + } else { + cfg[i].is_dependent = true; + } + cfg[i].state = &state; + } + pipeline_dependency_test(cfg, 4, general_dependency_check); + ESP_GMF_MEM_SHOW(TAG); +} + +TEST_CASE("A(Y) - B(N) - C(Y) - D(N) (B,D no dependency)", "[ESP_GMF_PIPELINE]") +{ + pipeline_state_t state = {}; + general_el_cfg_t cfg[4] = {}; + for (int i = 0; i < 4; i++) { + if ('A' + i == 'B' || 'A' + i == 'D') { + cfg[i].is_dependent = false; + } else { + cfg[i].is_dependent = true; + } + cfg[i].state = &state; + } + pipeline_dependency_test(cfg, 4, general_dependency_check); + ESP_GMF_MEM_SHOW(TAG); +} + +TEST_CASE("A(N) - B(Y) - C(N) - D(Y) (A,C no dependency)", "[ESP_GMF_PIPELINE]") +{ + pipeline_state_t state = {}; + general_el_cfg_t cfg[4] = {}; + for (int i = 0; i < 4; i++) { + if ('A' + i == 'B' || 'A' + i == 'D') { + cfg[i].is_dependent = true; + } else { + cfg[i].is_dependent = false; + } + cfg[i].state = &state; + } + pipeline_dependency_test(cfg, 4, general_dependency_check); + ESP_GMF_MEM_SHOW(TAG); +} + +TEST_CASE("A(Y) - B(N) - C(Y) - D(N) (Report in middle)", "[ESP_GMF_PIPELINE]") +{ + pipeline_state_t state = {}; + general_el_cfg_t cfg[4] = {}; + for (int i = 0; i < 4; i++) { + if ('A' + i == 'A') { + cfg[i].is_dependent = true; + cfg[i].report_in_process = true; + cfg[i].report_pos = 2; + } else if ('A' + i == 'C') { + cfg[i].is_dependent = true; + cfg[i].report_in_process = true; + cfg[i].report_pos = 3; + } + cfg[i].state = &state; + } + pipeline_dependency_test(cfg, 4, general_dependency_check); + ESP_GMF_MEM_SHOW(TAG); +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/main/cases/gmf_pool_test.c b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/main/cases/gmf_pool_test.c new file mode 100644 index 0000000..112b92e --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/main/cases/gmf_pool_test.c @@ -0,0 +1,722 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "unity.h" +#include "freertos/FreeRTOS.h" +#include "freertos/event_groups.h" + +#include "esp_log.h" + +#include "esp_gmf_element.h" +#include "esp_gmf_pipeline.h" +#include "esp_gmf_pool.h" +#include "esp_gmf_data_bus.h" + +#include "esp_gmf_oal_mem.h" +#include "esp_gmf_new_databus.h" +#include "gmf_fake_io.h" +#include "gmf_fake_dec.h" +#include "gmf_ut_common.h" + +static const char *TAG = "TEST_ESP_GMF_POOL"; +static const char *test_file_uri = "/sdcard/gmf_ut_test1.mp3"; + +#define PIPELINE_BLOCK_BIT BIT(0) + +esp_err_t _pipeline_event(esp_gmf_event_pkt_t *event, void *ctx) +{ + ESP_LOGE(TAG, "CB: RECV Pipeline EVT: el:%s-%p, type:%d, sub:%s, payload:%p, size:%d,%p", + "OBJ_GET_TAG(event->from)", event->from, event->type, esp_gmf_event_get_state_str(event->sub), + event->payload, event->payload_size, ctx); + if ((event->sub == ESP_GMF_EVENT_STATE_STOPPED) + || (event->sub == ESP_GMF_EVENT_STATE_FINISHED) + || (event->sub == ESP_GMF_EVENT_STATE_ERROR)) { + if (ctx) { + xEventGroupSetBits((EventGroupHandle_t)ctx, PIPELINE_BLOCK_BIT); + } + } + return 0; +} + +static inline void pool_register_io_func(esp_gmf_pool_handle_t pool) +{ + fake_io_cfg_t io_cfg = FAKE_IO_CFG_DEFAULT(); + io_cfg.dir = ESP_GMF_IO_DIR_READER; + esp_gmf_io_handle_t fs = NULL; + fake_io_init(&io_cfg, &fs); + TEST_ASSERT_EQUAL(ESP_OK, esp_gmf_pool_register_io(pool, fs, NULL)); + + io_cfg.dir = ESP_GMF_IO_DIR_WRITER; + fake_io_init(&io_cfg, &fs); + TEST_ASSERT_EQUAL(ESP_OK, esp_gmf_pool_register_io(pool, fs, NULL)); +} + +static inline void pool_register_dec_func(esp_gmf_pool_handle_t pool) +{ + fake_dec_cfg_t fake_dec_cfg = DEFAULT_FAKE_DEC_CONFIG(); + fake_dec_cfg.cb = NULL; + fake_dec_cfg.name = "dec1"; + esp_gmf_element_handle_t fake_dec = NULL; + fake_dec_init(&fake_dec_cfg, &fake_dec); + TEST_ASSERT_EQUAL(ESP_OK, esp_gmf_pool_register_element(pool, fake_dec, NULL)); + + fake_dec_cfg.name = "dec2"; + fake_dec_cfg.in_buf_size = 5 * 1024; + fake_dec_cfg.out_buf_size = 8 * 1024; + fake_dec_cfg.cb = NULL; + fake_dec_init(&fake_dec_cfg, &fake_dec); + TEST_ASSERT_EQUAL(ESP_OK, esp_gmf_pool_register_element(pool, fake_dec, NULL)); + + fake_dec_cfg.name = "dec3"; + fake_dec_cfg.cb = NULL; + fake_dec_cfg.in_buf_size = 8 * 1024; + fake_dec_cfg.out_buf_size = 10 * 1024; + fake_dec_init(&fake_dec_cfg, &fake_dec); + TEST_ASSERT_EQUAL(ESP_OK, esp_gmf_pool_register_element(pool, fake_dec, NULL)); + + fake_dec_cfg.cb = NULL; + fake_dec_cfg.name = "dec4"; + fake_dec_cfg.in_buf_size = 12 * 1024; + fake_dec_cfg.out_buf_size = 12 * 1024; + fake_dec_init(&fake_dec_cfg, &fake_dec); + TEST_ASSERT_EQUAL(ESP_OK, esp_gmf_pool_register_element(pool, fake_dec, NULL)); +} + +static inline void pool_register_dec_func2(esp_gmf_pool_handle_t pool) +{ + // All element use same payload for IN and OUT + fake_dec_cfg_t fake_dec_cfg = DEFAULT_FAKE_DEC_CONFIG(); + fake_dec_cfg.cb = NULL; + fake_dec_cfg.is_pass = true; + fake_dec_cfg.in_buf_size = 10 * 1024; + fake_dec_cfg.out_buf_size = 10 * 1024; + fake_dec_cfg.name = "dec1"; + esp_gmf_element_handle_t fake_dec = NULL; + fake_dec_init(&fake_dec_cfg, &fake_dec); + TEST_ASSERT_EQUAL(ESP_OK, esp_gmf_pool_register_element(pool, fake_dec, NULL)); + + fake_dec_cfg.name = "dec2"; + fake_dec_cfg.in_buf_size = 10 * 1024; + fake_dec_cfg.out_buf_size = 10 * 1024; + fake_dec_cfg.cb = NULL; + fake_dec_init(&fake_dec_cfg, &fake_dec); + TEST_ASSERT_EQUAL(ESP_OK, esp_gmf_pool_register_element(pool, fake_dec, NULL)); + + fake_dec_cfg.name = "dec3"; + fake_dec_cfg.cb = NULL; + fake_dec_cfg.in_buf_size = 10 * 1024; + fake_dec_cfg.out_buf_size = 10 * 1024; + fake_dec_init(&fake_dec_cfg, &fake_dec); + TEST_ASSERT_EQUAL(ESP_OK, esp_gmf_pool_register_element(pool, fake_dec, NULL)); + + fake_dec_cfg.cb = NULL; + fake_dec_cfg.name = "dec4"; + fake_dec_cfg.in_buf_size = 10 * 1024; + fake_dec_cfg.out_buf_size = 10 * 1024; + fake_dec_init(&fake_dec_cfg, &fake_dec); + TEST_ASSERT_EQUAL(ESP_OK, esp_gmf_pool_register_element(pool, fake_dec, NULL)); +} + +static inline void pool_register_dec_func3(esp_gmf_pool_handle_t pool) +{ + // The middle element use same payload for IN and OUT + fake_dec_cfg_t fake_dec_cfg = DEFAULT_FAKE_DEC_CONFIG(); + fake_dec_cfg.cb = NULL; + fake_dec_cfg.name = "dec1"; + esp_gmf_element_handle_t fake_dec = NULL; + fake_dec_init(&fake_dec_cfg, &fake_dec); + TEST_ASSERT_EQUAL(ESP_OK, esp_gmf_pool_register_element(pool, fake_dec, NULL)); + + fake_dec_cfg.name = "dec2"; + fake_dec_cfg.is_pass = true; // The element IN and OUT use same payload + fake_dec_cfg.in_buf_size = 5 * 1024; + fake_dec_cfg.out_buf_size = 5 * 1024; + fake_dec_cfg.cb = NULL; + fake_dec_init(&fake_dec_cfg, &fake_dec); + TEST_ASSERT_EQUAL(ESP_OK, esp_gmf_pool_register_element(pool, fake_dec, NULL)); + + fake_dec_cfg.name = "dec3"; + fake_dec_cfg.cb = NULL; + fake_dec_cfg.is_pass = false; + fake_dec_cfg.in_buf_size = 8 * 1024; + fake_dec_cfg.out_buf_size = 10 * 1024; + fake_dec_init(&fake_dec_cfg, &fake_dec); + TEST_ASSERT_EQUAL(ESP_OK, esp_gmf_pool_register_element(pool, fake_dec, NULL)); + + fake_dec_cfg.cb = NULL; + fake_dec_cfg.name = "dec4"; + fake_dec_cfg.in_buf_size = 10 * 1024; + fake_dec_cfg.out_buf_size = 12 * 1024; + fake_dec_init(&fake_dec_cfg, &fake_dec); + TEST_ASSERT_EQUAL(ESP_OK, esp_gmf_pool_register_element(pool, fake_dec, NULL)); +} + +static inline void pool_register_dec_func4(esp_gmf_pool_handle_t pool) +{ + // The middle element use same payload for IN and OUT + fake_dec_cfg_t fake_dec_cfg = DEFAULT_FAKE_DEC_CONFIG(); + fake_dec_cfg.cb = NULL; + fake_dec_cfg.name = "dec1"; + esp_gmf_element_handle_t fake_dec = NULL; + fake_dec_init(&fake_dec_cfg, &fake_dec); + TEST_ASSERT_EQUAL(ESP_OK, esp_gmf_pool_register_element(pool, fake_dec, NULL)); + + fake_dec_cfg.name = "dec2"; + fake_dec_cfg.in_buf_size = 6 * 1024; + fake_dec_cfg.out_buf_size = 7 * 1024; + fake_dec_cfg.cb = NULL; + fake_dec_cfg.is_pass = false; + fake_dec_init(&fake_dec_cfg, &fake_dec); + TEST_ASSERT_EQUAL(ESP_OK, esp_gmf_pool_register_element(pool, fake_dec, NULL)); + + fake_dec_cfg.name = "dec3"; + fake_dec_cfg.is_pass = true; + fake_dec_cfg.cb = NULL; + fake_dec_cfg.in_buf_size = 7 * 1024; + fake_dec_cfg.out_buf_size = 7 * 1024; + fake_dec_init(&fake_dec_cfg, &fake_dec); + TEST_ASSERT_EQUAL(ESP_OK, esp_gmf_pool_register_element(pool, fake_dec, NULL)); + + fake_dec_cfg.cb = NULL; + fake_dec_cfg.name = "dec4"; + fake_dec_cfg.in_buf_size = 7 * 1024; + fake_dec_cfg.out_buf_size = 7 * 1024; + fake_dec_init(&fake_dec_cfg, &fake_dec); + TEST_ASSERT_EQUAL(ESP_OK, esp_gmf_pool_register_element(pool, fake_dec, NULL)); +} + +static inline void pool_register_dec_func5(esp_gmf_pool_handle_t pool) +{ + // The middle element use same payload for IN and OUT + fake_dec_cfg_t fake_dec_cfg = DEFAULT_FAKE_DEC_CONFIG(); + fake_dec_cfg.cb = NULL; + fake_dec_cfg.name = "dec1"; + fake_dec_cfg.is_shared = false; + esp_gmf_element_handle_t fake_dec = NULL; + fake_dec_init(&fake_dec_cfg, &fake_dec); + TEST_ASSERT_EQUAL(ESP_OK, esp_gmf_pool_register_element(pool, fake_dec, NULL)); + + fake_dec_cfg.name = "dec2"; + fake_dec_cfg.in_buf_size = 6 * 1024; + fake_dec_cfg.out_buf_size = 7 * 1024; + fake_dec_cfg.cb = NULL; + fake_dec_cfg.is_pass = false; + fake_dec_cfg.is_shared = true; + fake_dec_init(&fake_dec_cfg, &fake_dec); + TEST_ASSERT_EQUAL(ESP_OK, esp_gmf_pool_register_element(pool, fake_dec, NULL)); + + fake_dec_cfg.name = "dec3"; + fake_dec_cfg.is_pass = true; + fake_dec_cfg.cb = NULL; + fake_dec_cfg.in_buf_size = 7 * 1024; + fake_dec_cfg.out_buf_size = 7 * 1024; + fake_dec_init(&fake_dec_cfg, &fake_dec); + TEST_ASSERT_EQUAL(ESP_OK, esp_gmf_pool_register_element(pool, fake_dec, NULL)); + + fake_dec_cfg.cb = NULL; + fake_dec_cfg.name = "dec4"; + fake_dec_cfg.in_buf_size = 7 * 1024; + fake_dec_cfg.out_buf_size = 7 * 1024; + fake_dec_init(&fake_dec_cfg, &fake_dec); + TEST_ASSERT_EQUAL(ESP_OK, esp_gmf_pool_register_element(pool, fake_dec, NULL)); +} + +TEST_CASE("Create and destroy pipeline", "[ELEMENT_POOL]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + esp_log_level_set("ESP_GMF_PIPELINE", ESP_LOG_DEBUG); + esp_log_level_set("ESP_GMF_ELEMENT", ESP_LOG_DEBUG); + esp_log_level_set("ESP_GMF_POOL", ESP_LOG_DEBUG); + + esp_gmf_pool_handle_t pool = NULL; + esp_gmf_pool_init(&pool); + TEST_ASSERT_NOT_NULL(pool); + pool_register_io_func(pool); + pool_register_dec_func(pool); + ESP_GMF_POOL_SHOW_ITEMS(pool); + + // Create the new pipeline + esp_gmf_pipeline_handle_t pipe = NULL; + const char *name[] = {"dec1", "dec1", "dec1"}; + esp_gmf_pool_new_pipeline(pool, "file", name, sizeof(name) / sizeof(char *), "file", &pipe); + TEST_ASSERT_NOT_NULL(pipe); + + esp_gmf_pipeline_destroy(pipe); + esp_gmf_pool_deinit(pool); +} + +TEST_CASE("One Pipe, [FILE->dec->dec->dec->FILE]", "[ELEMENT_POOL]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + esp_log_level_set("ESP_GMF_PIPELINE", ESP_LOG_DEBUG); + esp_log_level_set("FAKE_DEC", ESP_LOG_DEBUG); + esp_log_level_set("ESP_GMF_POOL", ESP_LOG_DEBUG); + + esp_gmf_pool_handle_t pool = NULL; + esp_gmf_pool_init(&pool); + TEST_ASSERT_NOT_NULL(pool); + pool_register_io_func(pool); + pool_register_dec_func(pool); + ESP_GMF_POOL_SHOW_ITEMS(pool); + + // Create the new elements + esp_gmf_pipeline_handle_t pipe = NULL; + const char *name[] = {"dec1", "dec1", "dec1"}; + esp_gmf_pool_new_pipeline(pool, "file", name, sizeof(name) / sizeof(char *), "file", &pipe); + TEST_ASSERT_NOT_NULL(pipe); + + esp_gmf_task_cfg_t cfg = DEFAULT_ESP_GMF_TASK_CONFIG(); + cfg.ctx = NULL; + cfg.cb = NULL; + esp_gmf_task_handle_t work_task = NULL; + esp_gmf_task_init(&cfg, &work_task); + TEST_ASSERT_NOT_NULL(work_task); + + esp_gmf_pipeline_bind_task(pipe, work_task); + esp_gmf_pipeline_loading_jobs(pipe); + esp_gmf_pipeline_set_event(pipe, _pipeline_event, NULL); + esp_gmf_pipeline_set_in_uri(pipe, test_file_uri); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_run(pipe)); + vTaskDelay(300 / portTICK_PERIOD_MS); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_pause(pipe)); + vTaskDelay(800 / portTICK_PERIOD_MS); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_resume(pipe)); + vTaskDelay(300 / portTICK_PERIOD_MS); + + ESP_LOGE(TAG, "%s-%d", __func__, __LINE__); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_stop(pipe)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_deinit(work_task)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_destroy(pipe)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pool_deinit(pool)); +} + +TEST_CASE("One Pipe, [FILE->dec->FILE]", "[ELEMENT_POOL]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + esp_log_level_set("ESP_GMF_PIPELINE", ESP_LOG_DEBUG); + esp_log_level_set("ESP_GMF_ELEMENT", ESP_LOG_DEBUG); + esp_log_level_set("ESP_GMF_POOL", ESP_LOG_DEBUG); + + esp_gmf_pool_handle_t pool = NULL; + esp_gmf_pool_init(&pool); + TEST_ASSERT_NOT_NULL(pool); + pool_register_io_func(pool); + pool_register_dec_func(pool); + ESP_GMF_POOL_SHOW_ITEMS(pool); + + // Create the new elements + esp_gmf_pipeline_handle_t pipe = NULL; + const char *name[] = {"dec1"}; + esp_gmf_pool_new_pipeline(pool, "file", name, sizeof(name) / sizeof(char *), "file", &pipe); + TEST_ASSERT_NOT_NULL(pipe); + + esp_gmf_task_cfg_t cfg = DEFAULT_ESP_GMF_TASK_CONFIG(); + cfg.ctx = NULL; + cfg.cb = NULL; + esp_gmf_task_handle_t work_task = NULL; + esp_gmf_task_init(&cfg, &work_task); + TEST_ASSERT_NOT_NULL(work_task); + + esp_gmf_pipeline_bind_task(pipe, work_task); + esp_gmf_pipeline_loading_jobs(pipe); + esp_gmf_pipeline_set_event(pipe, _pipeline_event, NULL); + esp_gmf_pipeline_set_in_uri(pipe, test_file_uri); + esp_gmf_pipeline_set_out_uri(pipe, "/sdcard/esp_gmf_ut_test_out.mp3"); + + TEST_ASSERT_EQUAL(esp_gmf_pipeline_run(pipe), ESP_GMF_ERR_OK); + // Wait to finished or got error + vTaskDelay(2000 / portTICK_PERIOD_MS); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_stop(pipe)); + + ESP_LOGE(TAG, "%s-%d", __func__, __LINE__); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_deinit(work_task)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_destroy(pipe)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pool_deinit(pool)); +} + +TEST_CASE("IN-OUT Different payload, [FILE->dec->FILE]", "[ELEMENT_PORT]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + esp_log_level_set("ESP_GMF_PIPELINE", ESP_LOG_DEBUG); + esp_log_level_set("ESP_GMF_ELEMENT", ESP_LOG_DEBUG); + esp_log_level_set("ESP_GMF_PORT", ESP_LOG_DEBUG); + ESP_GMF_MEM_SHOW(TAG); + + esp_gmf_pool_handle_t pool = NULL; + esp_gmf_pool_init(&pool); + TEST_ASSERT_NOT_NULL(pool); + pool_register_io_func(pool); + pool_register_dec_func(pool); + ESP_GMF_POOL_SHOW_ITEMS(pool); + + // Create the new elements + esp_gmf_pipeline_handle_t pipe = NULL; + const char *name[] = {"dec1", "dec2", "dec3", "dec4"}; + esp_gmf_pool_new_pipeline(pool, "file", name, sizeof(name) / sizeof(char *), "file", &pipe); + TEST_ASSERT_NOT_NULL(pipe); + + esp_gmf_task_cfg_t cfg = DEFAULT_ESP_GMF_TASK_CONFIG(); + cfg.ctx = NULL; + cfg.cb = NULL; + esp_gmf_task_handle_t work_task = NULL; + esp_gmf_task_init(&cfg, &work_task); + TEST_ASSERT_NOT_NULL(work_task); + + esp_gmf_pipeline_bind_task(pipe, work_task); + esp_gmf_pipeline_loading_jobs(pipe); + esp_gmf_pipeline_set_event(pipe, _pipeline_event, NULL); + esp_gmf_pipeline_set_in_uri(pipe, test_file_uri); + esp_gmf_pipeline_set_out_uri(pipe, "/sdcard/esp_gmf_ut_test_out.mp3"); + + ESP_GMF_MEM_SHOW(TAG); + TEST_ASSERT_EQUAL(esp_gmf_pipeline_run(pipe), ESP_GMF_ERR_OK); + vTaskDelay(300 / portTICK_PERIOD_MS); + ESP_GMF_MEM_SHOW(TAG); + vTaskDelay(300 / portTICK_PERIOD_MS); + ESP_GMF_MEM_SHOW(TAG); + // Wait to finished or got error + vTaskDelay(300 / portTICK_PERIOD_MS); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_stop(pipe)); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_deinit(work_task)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_destroy(pipe)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pool_deinit(pool)); +} + +TEST_CASE("ALL element IN-OUT SAME, [FILE->dec->FILE]", "[ELEMENT_PORT]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + esp_log_level_set("ESP_GMF_PIPELINE", ESP_LOG_DEBUG); + esp_log_level_set("ESP_GMF_ELEMENT", ESP_LOG_DEBUG); + esp_log_level_set("ESP_GMF_PORT", ESP_LOG_DEBUG); + ESP_GMF_MEM_SHOW(TAG); + + esp_gmf_pool_handle_t pool = NULL; + esp_gmf_pool_init(&pool); + TEST_ASSERT_NOT_NULL(pool); + pool_register_io_func(pool); + pool_register_dec_func2(pool); + ESP_GMF_POOL_SHOW_ITEMS(pool); + + // Create the new elements + esp_gmf_pipeline_handle_t pipe = NULL; + const char *name[] = {"dec1", "dec2", "dec3", "dec4"}; + esp_gmf_pool_new_pipeline(pool, "file", name, sizeof(name) / sizeof(char *), "file", &pipe); + TEST_ASSERT_NOT_NULL(pipe); + + esp_gmf_task_cfg_t cfg = DEFAULT_ESP_GMF_TASK_CONFIG(); + cfg.ctx = NULL; + cfg.cb = NULL; + esp_gmf_task_handle_t work_task = NULL; + esp_gmf_task_init(&cfg, &work_task); + TEST_ASSERT_NOT_NULL(work_task); + + esp_gmf_pipeline_bind_task(pipe, work_task); + esp_gmf_pipeline_loading_jobs(pipe); + esp_gmf_pipeline_set_event(pipe, _pipeline_event, NULL); + esp_gmf_pipeline_set_in_uri(pipe, test_file_uri); + esp_gmf_pipeline_set_out_uri(pipe, "/sdcard/esp_gmf_ut_test_out.mp3"); + ESP_GMF_MEM_SHOW(TAG); + + TEST_ASSERT_EQUAL(esp_gmf_pipeline_run(pipe), ESP_GMF_ERR_OK); + vTaskDelay(300 / portTICK_PERIOD_MS); + ESP_GMF_MEM_SHOW(TAG); + vTaskDelay(300 / portTICK_PERIOD_MS); + ESP_GMF_MEM_SHOW(TAG); + + // Wait to finished or got error + vTaskDelay(300 / portTICK_PERIOD_MS); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_stop(pipe)); + + ESP_LOGE(TAG, "%s-%d", __func__, __LINE__); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_deinit(work_task)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_destroy(pipe)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pool_deinit(pool)); +} + +TEST_CASE("The middle element IN-OUT SAME, [FILE->dec->FILE]", "[ELEMENT_PORT]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + esp_log_level_set("ESP_GMF_PIPELINE", ESP_LOG_DEBUG); + esp_log_level_set("ESP_GMF_ELEMENT", ESP_LOG_DEBUG); + esp_log_level_set("ESP_GMF_PORT", ESP_LOG_DEBUG); + ESP_GMF_MEM_SHOW(TAG); + + esp_gmf_pool_handle_t pool = NULL; + esp_gmf_pool_init(&pool); + TEST_ASSERT_NOT_NULL(pool); + pool_register_io_func(pool); + pool_register_dec_func3(pool); + ESP_GMF_POOL_SHOW_ITEMS(pool); + + // Create the new elements + esp_gmf_pipeline_handle_t pipe = NULL; + const char *name[] = {"dec1", "dec2", "dec3", "dec4"}; + esp_gmf_pool_new_pipeline(pool, "file", name, sizeof(name) / sizeof(char *), "file", &pipe); + TEST_ASSERT_NOT_NULL(pipe); + + esp_gmf_task_cfg_t cfg = DEFAULT_ESP_GMF_TASK_CONFIG(); + cfg.ctx = NULL; + cfg.cb = NULL; + esp_gmf_task_handle_t work_task = NULL; + esp_gmf_task_init(&cfg, &work_task); + TEST_ASSERT_NOT_NULL(work_task); + + esp_gmf_pipeline_bind_task(pipe, work_task); + esp_gmf_pipeline_loading_jobs(pipe); + esp_gmf_pipeline_set_event(pipe, _pipeline_event, NULL); + esp_gmf_pipeline_set_in_uri(pipe, test_file_uri); + esp_gmf_pipeline_set_out_uri(pipe, "/sdcard/esp_gmf_ut_test_out.mp3"); + ESP_GMF_MEM_SHOW(TAG); + + TEST_ASSERT_EQUAL(esp_gmf_pipeline_run(pipe), ESP_GMF_ERR_OK); + vTaskDelay(300 / portTICK_PERIOD_MS); + ESP_GMF_MEM_SHOW(TAG); + vTaskDelay(300 / portTICK_PERIOD_MS); + ESP_GMF_MEM_SHOW(TAG); + + // Wait to finished or got error + vTaskDelay(300 / portTICK_PERIOD_MS); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_stop(pipe)); + + ESP_LOGE(TAG, "%s-%d", __func__, __LINE__); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_deinit(work_task)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_destroy(pipe)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pool_deinit(pool)); +} + +TEST_CASE("The element IN-OUT SAME, [FILE->dec->FILE]", "[ELEMENT_PORT]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + esp_log_level_set("ESP_GMF_PIPELINE", ESP_LOG_DEBUG); + esp_log_level_set("ESP_GMF_ELEMENT", ESP_LOG_DEBUG); + esp_log_level_set("ESP_GMF_PORT", ESP_LOG_DEBUG); + ESP_GMF_MEM_SHOW(TAG); + + esp_gmf_pool_handle_t pool = NULL; + esp_gmf_pool_init(&pool); + TEST_ASSERT_NOT_NULL(pool); + pool_register_io_func(pool); + pool_register_dec_func4(pool); + ESP_GMF_POOL_SHOW_ITEMS(pool); + + // Create the new elements + esp_gmf_pipeline_handle_t pipe = NULL; + const char *name[] = {"dec1", "dec2", "dec3", "dec4"}; + esp_gmf_pool_new_pipeline(pool, "file", name, sizeof(name) / sizeof(char *), "file", &pipe); + TEST_ASSERT_NOT_NULL(pipe); + + esp_gmf_task_cfg_t cfg = DEFAULT_ESP_GMF_TASK_CONFIG(); + cfg.ctx = NULL; + cfg.cb = NULL; + esp_gmf_task_handle_t work_task = NULL; + esp_gmf_task_init(&cfg, &work_task); + TEST_ASSERT_NOT_NULL(work_task); + + esp_gmf_pipeline_bind_task(pipe, work_task); + esp_gmf_pipeline_loading_jobs(pipe); + esp_gmf_pipeline_set_event(pipe, _pipeline_event, NULL); + esp_gmf_pipeline_set_in_uri(pipe, test_file_uri); + esp_gmf_pipeline_set_out_uri(pipe, "/sdcard/esp_gmf_ut_test_out.mp3"); + ESP_GMF_MEM_SHOW(TAG); + + TEST_ASSERT_EQUAL(esp_gmf_pipeline_run(pipe), ESP_GMF_ERR_OK); + vTaskDelay(300 / portTICK_PERIOD_MS); + ESP_GMF_MEM_SHOW(TAG); + vTaskDelay(300 / portTICK_PERIOD_MS); + ESP_GMF_MEM_SHOW(TAG); + + // Wait to finished or got error + vTaskDelay(300 / portTICK_PERIOD_MS); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_stop(pipe)); + + ESP_LOGE(TAG, "%s-%d", __func__, __LINE__); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_deinit(work_task)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_destroy(pipe)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pool_deinit(pool)); +} + +TEST_CASE("Un-Shared port, Same payload, [FILE->dec->FILE]", "[ELEMENT_PORT]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + esp_log_level_set("ESP_GMF_PIPELINE", ESP_LOG_DEBUG); + esp_log_level_set("ESP_GMF_ELEMENT", ESP_LOG_DEBUG); + esp_log_level_set("ESP_GMF_PORT", ESP_LOG_DEBUG); + ESP_GMF_MEM_SHOW(TAG); + + esp_gmf_pool_handle_t pool = NULL; + esp_gmf_pool_init(&pool); + TEST_ASSERT_NOT_NULL(pool); + pool_register_io_func(pool); + pool_register_dec_func5(pool); + ESP_GMF_POOL_SHOW_ITEMS(pool); + + // Create the new elements + esp_gmf_pipeline_handle_t pipe = NULL; + const char *name[] = {"dec1", "dec2", "dec3", "dec4"}; + esp_gmf_pool_new_pipeline(pool, "file", name, sizeof(name) / sizeof(char *), "file", &pipe); + TEST_ASSERT_NOT_NULL(pipe); + + esp_gmf_task_cfg_t cfg = DEFAULT_ESP_GMF_TASK_CONFIG(); + cfg.ctx = NULL; + cfg.cb = NULL; + esp_gmf_task_handle_t work_task = NULL; + esp_gmf_task_init(&cfg, &work_task); + TEST_ASSERT_NOT_NULL(work_task); + + esp_gmf_pipeline_bind_task(pipe, work_task); + esp_gmf_pipeline_loading_jobs(pipe); + esp_gmf_pipeline_set_event(pipe, _pipeline_event, NULL); + esp_gmf_pipeline_set_in_uri(pipe, test_file_uri); + esp_gmf_pipeline_set_out_uri(pipe, "/sdcard/esp_gmf_ut_test_out.mp3"); + ESP_GMF_MEM_SHOW(TAG); + + TEST_ASSERT_EQUAL(esp_gmf_pipeline_run(pipe), ESP_GMF_ERR_OK); + vTaskDelay(300 / portTICK_PERIOD_MS); + ESP_GMF_MEM_SHOW(TAG); + vTaskDelay(300 / portTICK_PERIOD_MS); + ESP_GMF_MEM_SHOW(TAG); + + // Wait to finished or got error + vTaskDelay(300 / portTICK_PERIOD_MS); + ESP_GMF_MEM_SHOW(TAG); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_stop(pipe)); + + ESP_LOGE(TAG, "%s-%d", __func__, __LINE__); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_deinit(work_task)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pipeline_destroy(pipe)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_pool_deinit(pool)); +} + +#define TEST_LENGTH (3 * 1024) +static uint8_t *test_buffer = NULL; + +static esp_gmf_err_io_t _acquire_read(esp_gmf_io_handle_t handle, void *payload, uint32_t wanted_size, int block_ticks) +{ + esp_gmf_payload_t *pload = (esp_gmf_payload_t *)payload; + pload->buf = test_buffer; + pload->buf_length = TEST_LENGTH; + pload->valid_size = wanted_size > TEST_LENGTH ? TEST_LENGTH : wanted_size; + printf("acquire_read %d\n", pload->valid_size); + // memset(pload->buf, 1, pload->valid_size); + return ESP_GMF_IO_OK; +} + +static esp_gmf_err_io_t _release_read(esp_gmf_io_handle_t handle, void *payload, int block_ticks) +{ + return ESP_GMF_IO_OK; +} + +static esp_gmf_err_io_t _acquire_write(esp_gmf_io_handle_t handle, void *payload, uint32_t wanted_size, int block_ticks) +{ + esp_gmf_payload_t *pload = (esp_gmf_payload_t *)payload; + pload->valid_size = wanted_size; + return ESP_GMF_IO_OK; +} +static esp_gmf_err_io_t _acquire_write_fail(esp_gmf_io_handle_t handle, void *payload, uint32_t wanted_size, int block_ticks) +{ + esp_gmf_payload_t *pload = (esp_gmf_payload_t *)payload; + pload->valid_size = wanted_size > TEST_LENGTH ? TEST_LENGTH : wanted_size; + if(wanted_size > TEST_LENGTH) { + pload->valid_size = TEST_LENGTH; + return ESP_GMF_IO_FAIL; + } + pload->valid_size = wanted_size; + return ESP_GMF_IO_OK; +} + +static esp_gmf_err_io_t _release_write(esp_gmf_io_handle_t handle, void *payload, int block_ticks) +{ + return ESP_GMF_IO_OK; +} + +TEST_CASE("Un-shared port, [port callback -> dec -> port callback]", "[ELEMENT_POOL]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + esp_log_level_set("ESP_GMF_PIPELINE", ESP_LOG_DEBUG); + esp_log_level_set("FAKE_DEC", ESP_LOG_DEBUG); + esp_log_level_set("ESP_GMF_POOL", ESP_LOG_DEBUG); + + ESP_GMF_MEM_SHOW(TAG); + esp_gmf_port_handle_t in_port = NULL; + esp_gmf_port_handle_t out_port = NULL; + esp_gmf_obj_handle_t obj_hd = {NULL}; + + test_buffer = (uint8_t *)malloc(TEST_LENGTH); + TEST_ASSERT_NOT_EQUAL(NULL, test_buffer); + // New obj + fake_dec_cfg_t fake_dec_cfg = DEFAULT_FAKE_DEC_CONFIG(); + fake_dec_cfg.cb = NULL; + fake_dec_cfg.name = "dec1"; + esp_gmf_element_handle_t fake_dec = NULL; + fake_dec_init(&fake_dec_cfg, &fake_dec); + obj_hd = fake_dec; + TEST_ASSERT_NOT_NULL(fake_dec); + // Print obj tag + ESP_LOGE(TAG, "%s-%d,obj_hd:%p", (OBJ_GET_TAG(obj_hd)), __LINE__, obj_hd); + // Create in/out port + in_port = NEW_ESP_GMF_PORT_IN_BLOCK(_acquire_read, _release_read, NULL, NULL, TEST_LENGTH, 100); + out_port = NEW_ESP_GMF_PORT_OUT_BLOCK(_acquire_write, _release_write, NULL, NULL, TEST_LENGTH, 100); + // Register in/out port + esp_gmf_element_register_in_port(obj_hd, in_port); + esp_gmf_element_register_out_port(obj_hd, out_port); + // Open obj + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_element_process_open(obj_hd, NULL)); + // Run obj + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_element_process_running(obj_hd, NULL)); + // Close obj + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_element_process_close(obj_hd, NULL)); + // Delete obj: port will be deleted when obj handle pool is destroyed + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_obj_delete(obj_hd)); + free(test_buffer); +} + +TEST_CASE("Shared port, [port callback -> dec -> port callback]", "[ELEMENT_POOL]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + esp_log_level_set("ESP_GMF_PIPELINE", ESP_LOG_DEBUG); + esp_log_level_set("FAKE_DEC", ESP_LOG_DEBUG); + esp_log_level_set("ESP_GMF_POOL", ESP_LOG_DEBUG); + + ESP_GMF_MEM_SHOW(TAG); + esp_gmf_port_handle_t in_port = NULL; + esp_gmf_port_handle_t out_port = NULL; + esp_gmf_obj_handle_t obj_hd = {NULL}; + + test_buffer = (uint8_t *)malloc(TEST_LENGTH); + TEST_ASSERT_NOT_EQUAL(NULL, test_buffer); + // New obj + fake_dec_cfg_t fake_dec_cfg = DEFAULT_FAKE_DEC_CONFIG(); + fake_dec_cfg.cb = NULL; + fake_dec_cfg.name = "dec1"; + fake_dec_cfg.is_shared = true; + fake_dec_cfg.is_pass = true; + + esp_gmf_element_handle_t fake_dec = NULL; + fake_dec_init(&fake_dec_cfg, &fake_dec); + obj_hd = fake_dec; + TEST_ASSERT_NOT_NULL(fake_dec); + // Print obj tag + ESP_LOGE(TAG, "%s-%d,obj_hd:%p", (OBJ_GET_TAG(obj_hd)), __LINE__, obj_hd); + // Create in/out port + in_port = NEW_ESP_GMF_PORT_IN_BLOCK(_acquire_read, _release_read, NULL, NULL, TEST_LENGTH, 100); + out_port = NEW_ESP_GMF_PORT_OUT_BLOCK(_acquire_write_fail, _release_write, NULL, NULL, TEST_LENGTH, 100); + // Register in/out port + esp_gmf_element_register_in_port(obj_hd, in_port); + esp_gmf_element_register_out_port(obj_hd, out_port); + // Open obj + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_element_process_open(obj_hd, NULL)); + // Run obj + TEST_ASSERT_EQUAL(ESP_GMF_ERR_FAIL, esp_gmf_element_process_running(obj_hd, NULL)); + // Close obj + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_element_process_close(obj_hd, NULL)); + // Delete obj: port will be deleted when obj handle pool is destroyed + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_obj_delete(obj_hd)); + free(test_buffer); +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/main/cases/gmf_ringbuf_test.c b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/main/cases/gmf_ringbuf_test.c new file mode 100644 index 0000000..63e177b --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/main/cases/gmf_ringbuf_test.c @@ -0,0 +1,191 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "unity.h" +#include "freertos/FreeRTOS.h" +#include "esp_private/esp_clk.h" +#include "driver/sdmmc_host.h" +#include "esp_log.h" + +#include "esp_gmf_oal_mem.h" +#include "esp_gmf_ringbuffer.h" +#include "gmf_ut_common.h" + +static const char *TAG = "TEST_ESP_GMF_RINGBUF"; +static bool is_done; +static bool is_abort_read; +static bool is_abort_write; +static bool read_run; +static bool write_run; + +static const char *file_name = "/sdcard/gmf_ut_test.mp3"; +static const char *file2_name = "/sdcard/gmf_ut_test_out.mp3"; + +static void read_task(void *param) +{ + ESP_LOGI(TAG, "Going to read, %p", param); + esp_gmf_rb_handle_t rb = (esp_gmf_rb_handle_t)param; + esp_gmf_data_bus_block_t blk = {0}; + FILE *f = fopen(file_name, "rb"); + if (f == NULL) { + ESP_LOGE(TAG, "Open file failed"); + TEST_ASSERT_NOT_NULL(f); + goto read_task_err; + } + int len = 4096; + blk.buf = esp_gmf_oal_malloc(len); + blk.valid_size = len; + ESP_GMF_MEM_CHECK(TAG, blk.buf, goto read_task_err); + // TEST_ASSERT_NOT_NULL(blk.buf); + read_run = true; + uint32_t start_cnt = 0; + uint32_t total_cnt = 0; + while (read_run) { + esp_gmf_rb_acquire_write(rb, &blk, len, portMAX_DELAY); + int ret = fread(blk.buf, 1, len, f); + blk.valid_size = ret; + if (ret == 0) { + esp_gmf_rb_done_write(rb); + blk.is_last = true; + read_run = false; + } + start_cnt = esp_clk_rtc_time(); + ret = esp_gmf_rb_release_write(rb, &blk, portMAX_DELAY); + if (ret != ESP_GMF_ERR_OK) { + if (ret == ESP_GMF_IO_ABORT) { + is_abort_write = true; + } + ESP_LOGE(TAG, "Acquire write failed, ret:%d", ret); + break; + } + total_cnt += (esp_clk_rtc_time() - start_cnt); + } + ESP_LOGW(TAG, "Done to read, %ld", total_cnt); +read_task_err: + if (f) { + fclose(f); + } + if (blk.buf) { + esp_gmf_oal_free(blk.buf); + } + vTaskDelete(NULL); +} + +static void write_task(void *param) +{ + ESP_LOGI(TAG, "Going to write, %p", param); + esp_gmf_rb_handle_t rb = (esp_gmf_rb_handle_t)param; + esp_gmf_data_bus_block_t blk = {0}; + FILE *f = fopen(file2_name, "wb+"); + if (f == NULL) { + ESP_LOGE(TAG, "Open file failed"); + TEST_ASSERT_NOT_NULL(f); + goto write_task_err; + } + int len = 4096; + blk.buf = esp_gmf_oal_malloc(len); + blk.valid_size = len; + ESP_GMF_MEM_CHECK(TAG, blk.buf, goto write_task_err); + // TEST_ASSERT_NOT_NULL(blk.buf); + write_run = true; + uint32_t start_cnt = 0; + uint32_t total_cnt = 0; + while (write_run) { + start_cnt = esp_clk_rtc_time(); + int ret = esp_gmf_rb_acquire_read(rb, &blk, len, portMAX_DELAY); + if (ret != ESP_GMF_ERR_OK) { + if (ret == ESP_GMF_IO_ABORT) { + is_abort_read = true; + } + ESP_LOGE(TAG, "Acquire read failed, ret:%d", ret); + break; + } + total_cnt += (esp_clk_rtc_time() - start_cnt); + + ret = fwrite(blk.buf, 1, blk.valid_size, f); + esp_gmf_rb_release_read(rb, &blk, 0); + if (blk.is_last) { + break; + } + } + ESP_LOGW(TAG, "Done to write, %ld", total_cnt); +write_task_err: + if (f) { + fclose(f); + } + if (blk.buf) { + esp_gmf_oal_free(blk.buf); + } + is_done = true; + vTaskDelete(NULL); +} + +TEST_CASE("Ringbuffer read and write on different task", "[ESP_GMF_RINGBUF]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + esp_log_level_set("ESP_GMF_RINGBUF", ESP_LOG_VERBOSE); + + sdmmc_card_t *card = NULL; + esp_gmf_ut_setup_sdmmc(&card); + + is_done = false; + esp_gmf_rb_handle_t rb = NULL; + esp_gmf_rb_create(2, 8 * 1024, &rb); + ESP_LOGI(TAG, "TEST Create GMF ringbuffer, %p", rb); + TEST_ASSERT_NOT_NULL(rb); + xTaskCreate(read_task, "read", 4096, rb, 5, NULL); + xTaskCreate(write_task, "write", 4096, rb, 5, NULL); + while (1) { + vTaskDelay(10 / portTICK_PERIOD_MS); + if (is_done) { + break; + } + } + int ret = verify_two_files(file_name, file2_name); + TEST_ASSERT_EQUAL(ret, ESP_OK); + + esp_gmf_rb_destroy(rb); + + esp_gmf_ut_teardown_sdmmc(card); + vTaskDelay(10 / portTICK_PERIOD_MS); +} + +TEST_CASE("Abort when Ringbuffer read and write on different task", "[ESP_GMF_RINGBUF]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + esp_log_level_set("ESP_GMF_RINGBUF", ESP_LOG_VERBOSE); + + sdmmc_card_t *card = NULL; + esp_gmf_ut_setup_sdmmc(&card); + + is_done = false; + is_abort_read = false; + is_abort_write = false; + esp_gmf_rb_handle_t rb = NULL; + esp_gmf_rb_create(2, 8 * 1024, &rb); + ESP_LOGI(TAG, "TEST Create GMF ringbuffer, %p", rb); + TEST_ASSERT_NOT_NULL(rb); + xTaskCreate(read_task, "read", 4096, rb, 5, NULL); + xTaskCreate(write_task, "write", 4096, rb, 5, NULL); + int timeout_ms = 100; + while (1) { + vTaskDelay(10 / portTICK_PERIOD_MS); + timeout_ms -= 10; + if (timeout_ms == 0) { + ESP_LOGI(TAG, "Calling abort after 100ms"); + esp_gmf_rb_abort(rb); + vTaskDelay(50 / portTICK_PERIOD_MS); + } + if (is_done) { + break; + } + } + TEST_ASSERT_TRUE(is_abort_read); + TEST_ASSERT_TRUE(is_abort_write); + esp_gmf_rb_destroy(rb); + esp_gmf_ut_teardown_sdmmc(card); + vTaskDelay(10 / portTICK_PERIOD_MS); +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/main/cases/gmf_task_test.c b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/main/cases/gmf_task_test.c new file mode 100644 index 0000000..cf59493 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/main/cases/gmf_task_test.c @@ -0,0 +1,897 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "unity.h" +#include "freertos/FreeRTOS.h" +#include "esp_log.h" +#include "esp_gmf_oal_mem.h" +#include "esp_gmf_task.h" + +static const char *TAG = "TEST_ESP_GMF_TASK"; + +typedef struct { + uint32_t prepare : 8; + uint32_t working : 16; + uint32_t cleanup : 8; + esp_gmf_job_err_t prepare_return; + esp_gmf_job_err_t working_return; + esp_gmf_job_err_t cleanup_return; +}test_gmf_task_count_t; + +test_gmf_task_count_t test_gmf_task1_count = {0}; +test_gmf_task_count_t test_gmf_task2_count = {0}; +test_gmf_task_count_t test_gmf_task3_count = {0}; +test_gmf_task_count_t test_gmf_task4_count = {0}; + +esp_gmf_job_err_t prepare1(void *self, void *para) +{ + printf("%s,%p,%p\r\n", __func__, self, para); + test_gmf_task1_count.prepare++; + return test_gmf_task1_count.prepare_return; +} +esp_gmf_job_err_t prepare2(void *self, void *para) +{ + printf("%s,%p,%p\r\n", __func__, self, para); + test_gmf_task2_count.prepare++; + return test_gmf_task2_count.prepare_return;; +} +esp_gmf_job_err_t prepare3(void *self, void *para) +{ + printf("%s,%p,%p\r\n", __func__, self, para); + test_gmf_task3_count.prepare++; + return test_gmf_task3_count.prepare_return;; +} +esp_gmf_job_err_t prepare4(void *self, void *para) +{ + printf("%s,%p,%p\r\n", __func__, self, para); + test_gmf_task4_count.prepare++; + return test_gmf_task4_count.prepare_return;; +} + +esp_gmf_job_err_t working1(void *self, void *para) +{ + vTaskDelay(50 / portTICK_PERIOD_MS); + printf("%s,%p,%p\r\n", __func__, self, para); + test_gmf_task1_count.working++; + return test_gmf_task1_count.working_return; +} +esp_gmf_job_err_t working2(void *self, void *para) +{ + vTaskDelay(150 / portTICK_PERIOD_MS); + printf("%s,%p,%p\r\n", __func__, self, para); + test_gmf_task2_count.working++; + return test_gmf_task2_count.working_return; +} +esp_gmf_job_err_t working3(void *self, void *para) +{ + vTaskDelay(100 / portTICK_PERIOD_MS); + printf("%s,%p,%p\r\n", __func__, self, para); + test_gmf_task3_count.working++; + return test_gmf_task3_count.working_return; +} +esp_gmf_job_err_t working4(void *self, void *para) +{ + vTaskDelay(50 / portTICK_PERIOD_MS); + printf("%s,%p,%p\r\n", __func__, self, para); + test_gmf_task4_count.working++; + return test_gmf_task4_count.working_return; +} + +esp_gmf_job_err_t cleanup1(void *self, void *para) +{ + vTaskDelay(200 / portTICK_PERIOD_MS); + printf("%s,%p,%p\r\n", __func__, self, para); + test_gmf_task1_count.cleanup++; + return test_gmf_task1_count.cleanup_return; +} +esp_gmf_job_err_t cleanup2(void *self, void *para) +{ + vTaskDelay(200 / portTICK_PERIOD_MS); + printf("%s,%p,%p\r\n", __func__, self, para); + test_gmf_task2_count.cleanup++; + return test_gmf_task2_count.cleanup_return; +} +esp_gmf_job_err_t cleanup3(void *self, void *para) +{ + vTaskDelay(100 / portTICK_PERIOD_MS); + printf("%s,%p,%p\r\n", __func__, self, para); + test_gmf_task3_count.cleanup++; + return test_gmf_task3_count.cleanup_return; +} +esp_gmf_job_err_t cleanup4(void *self, void *para) +{ + vTaskDelay(100 / portTICK_PERIOD_MS); + printf("%s,%p,%p\r\n", __func__, self, para); + test_gmf_task4_count.cleanup++; + return test_gmf_task4_count.cleanup_return; +} +static void clear_test_gmf_task_count(void) +{ + memset(&test_gmf_task4_count, 0, sizeof(test_gmf_task4_count)); + memset(&test_gmf_task3_count, 0, sizeof(test_gmf_task3_count)); + memset(&test_gmf_task2_count, 0, sizeof(test_gmf_task2_count)); + memset(&test_gmf_task1_count, 0, sizeof(test_gmf_task1_count)); +} + +static esp_gmf_err_t esp_gmf_task_evt(esp_gmf_event_pkt_t *evt, void *ctx) +{ + esp_gmf_task_handle_t tsk = evt->from; + ESP_LOGI(TAG, "TASK EVT, tsk:%s-%p, t:%x, sub:%s, pld:%p, sz:%d", + OBJ_GET_TAG(tsk), evt->from, evt->type, esp_gmf_event_get_state_str(evt->sub), evt->payload, evt->payload_size); + if (evt->type == ESP_GMF_EVT_TYPE_LOADING_JOB) { + switch (evt->sub) { + case ESP_GMF_EVENT_STATE_ERROR: + case ESP_GMF_EVENT_STATE_STOPPED: + case ESP_GMF_EVENT_STATE_FINISHED: + esp_gmf_task_register_ready_job(tsk, NULL, cleanup1, ESP_GMF_JOB_TIMES_ONCE, NULL, true); + esp_gmf_task_register_ready_job(tsk, NULL, cleanup2, ESP_GMF_JOB_TIMES_ONCE, NULL, true); + esp_gmf_task_register_ready_job(tsk, NULL, cleanup3, ESP_GMF_JOB_TIMES_ONCE, NULL, true); + esp_gmf_task_register_ready_job(tsk, NULL, cleanup4, ESP_GMF_JOB_TIMES_ONCE, NULL, true); + break; + } + } + return ESP_GMF_ERR_OK; +} + +TEST_CASE("Deinit after init directly", "[ESP_GMF_TASK]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + esp_gmf_task_cfg_t cfg = DEFAULT_ESP_GMF_TASK_CONFIG(); + esp_gmf_task_handle_t hd = NULL; + esp_gmf_task_init(&cfg, &hd); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_NOT_SUPPORT, esp_gmf_task_pause(hd)); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_NOT_SUPPORT, esp_gmf_task_resume(hd)); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_stop(hd)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_deinit(hd)); + + ESP_GMF_MEM_SHOW(TAG); +} + +TEST_CASE("Working to done with manual register cleanup", "[ESP_GMF_TASK]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + clear_test_gmf_task_count(); + + esp_gmf_task_cfg_t cfg = DEFAULT_ESP_GMF_TASK_CONFIG(); + cfg.ctx = NULL; + cfg.cb = NULL; + esp_gmf_task_handle_t hd = NULL; + + esp_gmf_task_init(&cfg, &hd); + esp_gmf_task_register_ready_job(hd, NULL, prepare1, ESP_GMF_JOB_TIMES_ONCE, NULL, false); + esp_gmf_task_register_ready_job(hd, NULL, prepare2, ESP_GMF_JOB_TIMES_ONCE, NULL, false); + esp_gmf_task_register_ready_job(hd, NULL, prepare3, ESP_GMF_JOB_TIMES_ONCE, NULL, false); + esp_gmf_task_register_ready_job(hd, NULL, prepare4, ESP_GMF_JOB_TIMES_ONCE, NULL, false); + + esp_gmf_task_register_ready_job(hd, NULL, working1, ESP_GMF_JOB_TIMES_INFINITE, NULL, false); + esp_gmf_task_register_ready_job(hd, NULL, working2, ESP_GMF_JOB_TIMES_INFINITE, NULL, false); + esp_gmf_task_register_ready_job(hd, NULL, working3, ESP_GMF_JOB_TIMES_INFINITE, NULL, false); + esp_gmf_task_register_ready_job(hd, NULL, working4, ESP_GMF_JOB_TIMES_INFINITE, NULL, false); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_run(hd)); + vTaskDelay(2000 / portTICK_PERIOD_MS); + + ESP_LOGW(TAG, "SET working1-return DONE"); + esp_gmf_task_register_ready_job(hd, NULL, cleanup1, ESP_GMF_JOB_TIMES_ONCE, NULL, true); + test_gmf_task1_count.working_return = ESP_GMF_JOB_ERR_DONE; + vTaskDelay(200 / portTICK_PERIOD_MS); + + ESP_LOGW(TAG, "SET work2ing-return DONE"); + esp_gmf_task_register_ready_job(hd, NULL, cleanup2, ESP_GMF_JOB_TIMES_ONCE, NULL, true); + test_gmf_task2_count.working_return = ESP_GMF_JOB_ERR_DONE; + vTaskDelay(300 / portTICK_PERIOD_MS); + + ESP_LOGW(TAG, "SET working3-return DONE"); + esp_gmf_task_register_ready_job(hd, NULL, cleanup3, ESP_GMF_JOB_TIMES_ONCE, NULL, true); + test_gmf_task3_count.working_return = ESP_GMF_JOB_ERR_DONE; + vTaskDelay(1000 / portTICK_PERIOD_MS); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_deinit(hd)); + ESP_GMF_MEM_SHOW(TAG); + + TEST_ASSERT_EQUAL(1, test_gmf_task1_count.cleanup); + TEST_ASSERT_EQUAL(1, test_gmf_task2_count.cleanup); + TEST_ASSERT_EQUAL(1, test_gmf_task3_count.cleanup); + + TEST_ASSERT_EQUAL(1, test_gmf_task1_count.prepare); + TEST_ASSERT_EQUAL(1, test_gmf_task2_count.prepare); + TEST_ASSERT_EQUAL(1, test_gmf_task3_count.prepare); + + TEST_ASSERT_NOT_EQUAL(0, test_gmf_task1_count.working); + TEST_ASSERT_NOT_EQUAL(0, test_gmf_task2_count.working); + TEST_ASSERT_NOT_EQUAL(0, test_gmf_task3_count.working); + + ESP_LOGI(TAG, "task1: %d, task2: %d, task3: %d, task4: %d", test_gmf_task1_count.working, + test_gmf_task2_count.working, test_gmf_task3_count.working, test_gmf_task4_count.working); + if ((test_gmf_task1_count.working == test_gmf_task2_count.working) && test_gmf_task1_count.working == test_gmf_task3_count.working) { + // OK + TEST_ASSERT_TRUE(true); + } else { + TEST_ASSERT_FALSE(true); + } +} + +TEST_CASE("Working to done with auto register cleanup", "[ESP_GMF_TASK]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + + clear_test_gmf_task_count(); + esp_gmf_task_cfg_t cfg = DEFAULT_ESP_GMF_TASK_CONFIG(); + cfg.ctx = NULL; + cfg.cb = NULL; + esp_gmf_task_handle_t hd = NULL; + + esp_gmf_task_init(&cfg, &hd); + esp_gmf_task_set_event_func(hd, esp_gmf_task_evt, NULL); + esp_gmf_task_register_ready_job(hd, NULL, prepare1, ESP_GMF_JOB_TIMES_ONCE, NULL, false); + esp_gmf_task_register_ready_job(hd, NULL, prepare2, ESP_GMF_JOB_TIMES_ONCE, NULL, false); + esp_gmf_task_register_ready_job(hd, NULL, prepare3, ESP_GMF_JOB_TIMES_ONCE, NULL, false); + esp_gmf_task_register_ready_job(hd, NULL, prepare4, ESP_GMF_JOB_TIMES_ONCE, NULL, false); + + esp_gmf_task_register_ready_job(hd, NULL, working1, ESP_GMF_JOB_TIMES_INFINITE, NULL, false); + esp_gmf_task_register_ready_job(hd, NULL, working2, ESP_GMF_JOB_TIMES_INFINITE, NULL, false); + esp_gmf_task_register_ready_job(hd, NULL, working3, ESP_GMF_JOB_TIMES_INFINITE, NULL, false); + esp_gmf_task_register_ready_job(hd, NULL, working4, ESP_GMF_JOB_TIMES_INFINITE, NULL, false); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_run(hd)); + vTaskDelay(1000 / portTICK_PERIOD_MS); + + ESP_LOGW(TAG, "SET working1_return DONE"); + test_gmf_task1_count.working_return = ESP_GMF_JOB_ERR_DONE; + ESP_LOGW(TAG, "SET test_gmf_task2_count.working_return DONE"); + test_gmf_task2_count.working_return = ESP_GMF_JOB_ERR_DONE; + ESP_LOGW(TAG, "SET test_gmf_task3_count.working_return DONE"); + test_gmf_task3_count.working_return = ESP_GMF_JOB_ERR_DONE; + ESP_LOGW(TAG, "SET test_gmf_task4_count.working_return DONE"); + test_gmf_task4_count.working_return = ESP_GMF_JOB_ERR_DONE; + vTaskDelay(1000 / portTICK_PERIOD_MS); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_deinit(hd)); + ESP_GMF_MEM_SHOW(TAG); + + TEST_ASSERT_EQUAL(1, test_gmf_task1_count.cleanup); + TEST_ASSERT_EQUAL(1, test_gmf_task2_count.cleanup); + TEST_ASSERT_EQUAL(1, test_gmf_task3_count.cleanup); + TEST_ASSERT_EQUAL(1, test_gmf_task4_count.cleanup); + + TEST_ASSERT_EQUAL(1, test_gmf_task1_count.prepare); + TEST_ASSERT_EQUAL(1, test_gmf_task2_count.prepare); + TEST_ASSERT_EQUAL(1, test_gmf_task3_count.prepare); + TEST_ASSERT_EQUAL(1, test_gmf_task4_count.prepare); + + TEST_ASSERT_NOT_EQUAL(0, test_gmf_task1_count.working); + TEST_ASSERT_NOT_EQUAL(0, test_gmf_task2_count.working); + TEST_ASSERT_NOT_EQUAL(0, test_gmf_task3_count.working); + TEST_ASSERT_NOT_EQUAL(0, test_gmf_task4_count.working); + + ESP_LOGI(TAG, "task1: %d, task2: %d, task3: %d, task4: %d", test_gmf_task1_count.working, + test_gmf_task2_count.working, test_gmf_task3_count.working, test_gmf_task4_count.working); +} + +TEST_CASE("Working with CONTINUE", "[ESP_GMF_TASK]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + + /* Test following cases: + * 1. A->B->C->D + * 2. A->B->C + * 3. A->B + * 4. A->B->C->D + * 5. To Done + */ + clear_test_gmf_task_count(); + esp_gmf_task_cfg_t cfg = DEFAULT_ESP_GMF_TASK_CONFIG(); + cfg.ctx = NULL; + cfg.cb = NULL; + esp_gmf_task_handle_t hd = NULL; + + esp_gmf_task_init(&cfg, &hd); + esp_gmf_task_set_event_func(hd, esp_gmf_task_evt, NULL); + esp_gmf_task_register_ready_job(hd, NULL, prepare1, ESP_GMF_JOB_TIMES_ONCE, NULL, false); + esp_gmf_task_register_ready_job(hd, NULL, prepare2, ESP_GMF_JOB_TIMES_ONCE, NULL, false); + esp_gmf_task_register_ready_job(hd, NULL, prepare3, ESP_GMF_JOB_TIMES_ONCE, NULL, false); + esp_gmf_task_register_ready_job(hd, NULL, prepare4, ESP_GMF_JOB_TIMES_ONCE, NULL, false); + + esp_gmf_task_register_ready_job(hd, NULL, working1, ESP_GMF_JOB_TIMES_INFINITE, NULL, false); + esp_gmf_task_register_ready_job(hd, NULL, working2, ESP_GMF_JOB_TIMES_INFINITE, NULL, false); + esp_gmf_task_register_ready_job(hd, NULL, working3, ESP_GMF_JOB_TIMES_INFINITE, NULL, false); + esp_gmf_task_register_ready_job(hd, NULL, working4, ESP_GMF_JOB_TIMES_INFINITE, NULL, false); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_run(hd)); + vTaskDelay(500 / portTICK_PERIOD_MS); + + ESP_LOGW(TAG, "SET task3 return, ESP_GMF_JOB_ERR_CONTINUE"); + test_gmf_task3_count.working_return = ESP_GMF_JOB_ERR_CONTINUE; + vTaskDelay(1000 / portTICK_PERIOD_MS); + + ESP_LOGW(TAG, "SET task2 return, ESP_GMF_JOB_ERR_CONTINUE"); + test_gmf_task2_count.working_return = ESP_GMF_JOB_ERR_CONTINUE; + vTaskDelay(500 / portTICK_PERIOD_MS); + + ESP_LOGW(TAG, "SET task2 return, ESP_GMF_JOB_ERR_OK"); + test_gmf_task2_count.working_return = ESP_GMF_JOB_ERR_OK; + vTaskDelay(500 / portTICK_PERIOD_MS); + + ESP_LOGW(TAG, "SET return, ESP_GMF_JOB_ERR_DONE"); + test_gmf_task1_count.working_return = ESP_GMF_JOB_ERR_DONE; + test_gmf_task2_count.working_return = ESP_GMF_JOB_ERR_DONE; + test_gmf_task3_count.working_return = ESP_GMF_JOB_ERR_DONE; + test_gmf_task4_count.working_return = ESP_GMF_JOB_ERR_DONE; + vTaskDelay(1000 / portTICK_PERIOD_MS); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_deinit(hd)); + ESP_GMF_MEM_SHOW(TAG); + + TEST_ASSERT_EQUAL(1, test_gmf_task1_count.cleanup); + TEST_ASSERT_EQUAL(1, test_gmf_task2_count.cleanup); + TEST_ASSERT_EQUAL(1, test_gmf_task3_count.cleanup); + TEST_ASSERT_EQUAL(1, test_gmf_task4_count.cleanup); + + TEST_ASSERT_EQUAL(1, test_gmf_task1_count.prepare); + TEST_ASSERT_EQUAL(1, test_gmf_task2_count.prepare); + TEST_ASSERT_EQUAL(1, test_gmf_task3_count.prepare); + TEST_ASSERT_EQUAL(1, test_gmf_task4_count.prepare); + + TEST_ASSERT_NOT_EQUAL(0, test_gmf_task1_count.working); + TEST_ASSERT_NOT_EQUAL(0, test_gmf_task2_count.working); + TEST_ASSERT_NOT_EQUAL(0, test_gmf_task3_count.working); + TEST_ASSERT_NOT_EQUAL(0, test_gmf_task4_count.working); + + ESP_LOGI(TAG, "task1: %d, task2: %d, task3: %d, task4: %d", test_gmf_task1_count.working, + test_gmf_task2_count.working, test_gmf_task3_count.working, test_gmf_task4_count.working); + if ((test_gmf_task1_count.working >= test_gmf_task2_count.working) + && (test_gmf_task2_count.working > test_gmf_task3_count.working) + && (test_gmf_task3_count.working > test_gmf_task4_count.working)) { + // OK + TEST_ASSERT_TRUE(true); + } else { + TEST_ASSERT_FALSE(true); + } +} + +TEST_CASE("Working with TRUNCATE", "[ESP_GMF_TASK]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + + /* Test following cases: + * 1. A->B->C->D + * 2. B->C->D + * 3. C->D + * 4. A->B->C->D + * 5. To Done + */ + clear_test_gmf_task_count(); + + esp_gmf_task_cfg_t cfg = DEFAULT_ESP_GMF_TASK_CONFIG(); + cfg.ctx = NULL; + cfg.cb = NULL; + esp_gmf_task_handle_t hd = NULL; + + esp_gmf_task_init(&cfg, &hd); + esp_gmf_task_set_event_func(hd, esp_gmf_task_evt, NULL); + esp_gmf_task_register_ready_job(hd, NULL, prepare1, ESP_GMF_JOB_TIMES_ONCE, NULL, false); + esp_gmf_task_register_ready_job(hd, NULL, prepare2, ESP_GMF_JOB_TIMES_ONCE, NULL, false); + esp_gmf_task_register_ready_job(hd, NULL, prepare3, ESP_GMF_JOB_TIMES_ONCE, NULL, false); + esp_gmf_task_register_ready_job(hd, NULL, prepare4, ESP_GMF_JOB_TIMES_ONCE, NULL, false); + + esp_gmf_task_register_ready_job(hd, NULL, working1, ESP_GMF_JOB_TIMES_INFINITE, NULL, false); + esp_gmf_task_register_ready_job(hd, NULL, working2, ESP_GMF_JOB_TIMES_INFINITE, NULL, false); + esp_gmf_task_register_ready_job(hd, NULL, working3, ESP_GMF_JOB_TIMES_INFINITE, NULL, false); + esp_gmf_task_register_ready_job(hd, NULL, working4, ESP_GMF_JOB_TIMES_INFINITE, NULL, false); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_run(hd)); + vTaskDelay(500 / portTICK_PERIOD_MS); + + ESP_LOGW(TAG, "SET task2 return, ESP_GMF_JOB_ERR_TRUNCATE"); + test_gmf_task2_count.working_return = ESP_GMF_JOB_ERR_TRUNCATE; + vTaskDelay(1000 / portTICK_PERIOD_MS); + + ESP_LOGW(TAG, "SET task3 return, ESP_GMF_JOB_ERR_TRUNCATE"); + test_gmf_task3_count.working_return = ESP_GMF_JOB_ERR_TRUNCATE; + vTaskDelay(500 / portTICK_PERIOD_MS); + + ESP_LOGW(TAG, "SET task3 return, ESP_GMF_JOB_ERR_OK"); + test_gmf_task3_count.working_return = ESP_GMF_JOB_ERR_OK; + vTaskDelay(500 / portTICK_PERIOD_MS); + + ESP_LOGW(TAG, "SET return, ESP_GMF_JOB_ERR_DONE"); + test_gmf_task1_count.working_return = ESP_GMF_JOB_ERR_DONE; + test_gmf_task2_count.working_return = ESP_GMF_JOB_ERR_DONE; + test_gmf_task3_count.working_return = ESP_GMF_JOB_ERR_DONE; + test_gmf_task4_count.working_return = ESP_GMF_JOB_ERR_DONE; + vTaskDelay(1000 / portTICK_PERIOD_MS); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_deinit(hd)); + ESP_GMF_MEM_SHOW(TAG); + + TEST_ASSERT_EQUAL(1, test_gmf_task1_count.cleanup); + TEST_ASSERT_EQUAL(1, test_gmf_task2_count.cleanup); + TEST_ASSERT_EQUAL(1, test_gmf_task3_count.cleanup); + TEST_ASSERT_EQUAL(1, test_gmf_task4_count.cleanup); + + TEST_ASSERT_EQUAL(1, test_gmf_task1_count.prepare); + TEST_ASSERT_EQUAL(1, test_gmf_task2_count.prepare); + TEST_ASSERT_EQUAL(1, test_gmf_task3_count.prepare); + TEST_ASSERT_EQUAL(1, test_gmf_task4_count.prepare); + + TEST_ASSERT_NOT_EQUAL(0, test_gmf_task1_count.working); + TEST_ASSERT_NOT_EQUAL(0, test_gmf_task2_count.working); + TEST_ASSERT_NOT_EQUAL(0, test_gmf_task3_count.working); + TEST_ASSERT_NOT_EQUAL(0, test_gmf_task4_count.working); + + ESP_LOGI(TAG, "task1: %d, task2: %d, task3: %d, task4: %d", test_gmf_task1_count.working, + test_gmf_task2_count.working, test_gmf_task3_count.working, test_gmf_task4_count.working); + if ((test_gmf_task1_count.working < test_gmf_task2_count.working) + && (test_gmf_task2_count.working < test_gmf_task3_count.working) + && (test_gmf_task3_count.working <= test_gmf_task4_count.working)) { + // OK + TEST_ASSERT_TRUE(true); + } else { + TEST_ASSERT_FALSE(true); + } + +} + + +TEST_CASE("Stopped by stop API", "[ESP_GMF_TASK]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + clear_test_gmf_task_count(); + esp_gmf_task_cfg_t cfg = DEFAULT_ESP_GMF_TASK_CONFIG(); + cfg.ctx = NULL; + cfg.cb = NULL; + esp_gmf_task_handle_t hd = NULL; + ESP_GMF_MEM_SHOW(TAG); + esp_gmf_task_init(&cfg, &hd); + + esp_gmf_task_set_event_func(hd, esp_gmf_task_evt, NULL); + + esp_gmf_task_register_ready_job(hd, NULL, prepare1, ESP_GMF_JOB_TIMES_ONCE, NULL, false); + esp_gmf_task_register_ready_job(hd, NULL, prepare2, ESP_GMF_JOB_TIMES_ONCE, NULL, false); + esp_gmf_task_register_ready_job(hd, NULL, prepare3, ESP_GMF_JOB_TIMES_ONCE, NULL, false); + esp_gmf_task_register_ready_job(hd, NULL, prepare4, ESP_GMF_JOB_TIMES_ONCE, NULL, false); + + esp_gmf_task_register_ready_job(hd, NULL, working1, ESP_GMF_JOB_TIMES_INFINITE, NULL, false); + esp_gmf_task_register_ready_job(hd, NULL, working2, ESP_GMF_JOB_TIMES_INFINITE, NULL, false); + esp_gmf_task_register_ready_job(hd, NULL, working3, ESP_GMF_JOB_TIMES_INFINITE, NULL, false); + esp_gmf_task_register_ready_job(hd, NULL, working4, ESP_GMF_JOB_TIMES_INFINITE, NULL, false); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_run(hd)); + vTaskDelay(200 / portTICK_PERIOD_MS); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_pause(hd)); + vTaskDelay(200 / portTICK_PERIOD_MS); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_resume(hd)); + vTaskDelay(200 / portTICK_PERIOD_MS); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_stop(hd)); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_deinit(hd)); + ESP_GMF_MEM_SHOW(TAG); + + TEST_ASSERT_EQUAL(1, test_gmf_task1_count.cleanup); + TEST_ASSERT_EQUAL(1, test_gmf_task2_count.cleanup); + TEST_ASSERT_EQUAL(1, test_gmf_task3_count.cleanup); + TEST_ASSERT_EQUAL(1, test_gmf_task4_count.cleanup); + + TEST_ASSERT_EQUAL(1, test_gmf_task1_count.prepare); + TEST_ASSERT_EQUAL(1, test_gmf_task2_count.prepare); + TEST_ASSERT_EQUAL(1, test_gmf_task3_count.prepare); + TEST_ASSERT_EQUAL(1, test_gmf_task4_count.prepare); + + TEST_ASSERT_NOT_EQUAL(0, test_gmf_task1_count.working); + TEST_ASSERT_NOT_EQUAL(0, test_gmf_task2_count.working); + TEST_ASSERT_NOT_EQUAL(0, test_gmf_task3_count.working); + TEST_ASSERT_NOT_EQUAL(0, test_gmf_task4_count.working); + + ESP_LOGI(TAG, "task1: %d, task2: %d, task3: %d, task4: %d", test_gmf_task1_count.working, + test_gmf_task2_count.working, test_gmf_task3_count.working, test_gmf_task4_count.working); + // if ((test_gmf_task1_count.working == test_gmf_task2_count.working) + // && (test_gmf_task2_count.working > test_gmf_task3_count.working) + // && (test_gmf_task3_count.working >= test_gmf_task4_count.working)) { + // // OK + // TEST_ASSERT_TRUE(true); + // } else { + // TEST_ASSERT_FALSE(true); + // } +} + +TEST_CASE("Return error on the PREPARE stage", "[ESP_GMF_TASK]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + + clear_test_gmf_task_count(); + + esp_gmf_task_cfg_t cfg = DEFAULT_ESP_GMF_TASK_CONFIG(); + cfg.ctx = NULL; + cfg.cb = NULL; + esp_gmf_task_handle_t hd = NULL; + + esp_gmf_task_init(&cfg, &hd); + esp_gmf_task_set_event_func(hd, esp_gmf_task_evt, NULL); + + esp_gmf_task_register_ready_job(hd, NULL, prepare1, ESP_GMF_JOB_TIMES_ONCE, NULL, false); + esp_gmf_task_register_ready_job(hd, NULL, prepare2, ESP_GMF_JOB_TIMES_ONCE, NULL, false); + esp_gmf_task_register_ready_job(hd, NULL, prepare3, ESP_GMF_JOB_TIMES_ONCE, NULL, false); + esp_gmf_task_register_ready_job(hd, NULL, prepare4, ESP_GMF_JOB_TIMES_ONCE, NULL, false); + + esp_gmf_task_register_ready_job(hd, NULL, working1, ESP_GMF_JOB_TIMES_INFINITE, NULL, false); + esp_gmf_task_register_ready_job(hd, NULL, working2, ESP_GMF_JOB_TIMES_INFINITE, NULL, false); + esp_gmf_task_register_ready_job(hd, NULL, working3, ESP_GMF_JOB_TIMES_INFINITE, NULL, false); + esp_gmf_task_register_ready_job(hd, NULL, working4, ESP_GMF_JOB_TIMES_INFINITE, NULL, false); + + test_gmf_task3_count.prepare_return = ESP_GMF_JOB_ERR_FAIL; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_run(hd)); + + vTaskDelay(200 / portTICK_PERIOD_MS); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_pause(hd)); + vTaskDelay(100 / portTICK_PERIOD_MS); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_NOT_SUPPORT, esp_gmf_task_resume(hd)); + vTaskDelay(200 / portTICK_PERIOD_MS); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_stop(hd)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_deinit(hd)); + + TEST_ASSERT_EQUAL(1, test_gmf_task1_count.cleanup); + TEST_ASSERT_EQUAL(1, test_gmf_task2_count.cleanup); + TEST_ASSERT_EQUAL(1, test_gmf_task3_count.cleanup); + TEST_ASSERT_EQUAL(1, test_gmf_task4_count.cleanup); + + TEST_ASSERT_EQUAL(1, test_gmf_task1_count.prepare); + TEST_ASSERT_EQUAL(1, test_gmf_task2_count.prepare); + TEST_ASSERT_EQUAL(1, test_gmf_task3_count.prepare); + TEST_ASSERT_EQUAL(0, test_gmf_task4_count.prepare); + + TEST_ASSERT_EQUAL(0, test_gmf_task1_count.working); + TEST_ASSERT_EQUAL(0, test_gmf_task2_count.working); + TEST_ASSERT_EQUAL(0, test_gmf_task3_count.working); + TEST_ASSERT_EQUAL(0, test_gmf_task4_count.working); + + ESP_LOGI(TAG, "task1: %d, task2: %d, task3: %d, task4: %d", test_gmf_task1_count.working, + test_gmf_task2_count.working, test_gmf_task3_count.working, test_gmf_task4_count.working); + ESP_GMF_MEM_SHOW(TAG); +} + +TEST_CASE("Return error on the WORKING stage", "[ESP_GMF_TASK]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + // esp_log_level_set("ESP_GMF_TASK", ESP_LOG_DEBUG); + clear_test_gmf_task_count(); + esp_gmf_task_cfg_t cfg = DEFAULT_ESP_GMF_TASK_CONFIG(); + cfg.ctx = NULL; + cfg.cb = NULL; + esp_gmf_task_handle_t hd = NULL; + + esp_gmf_task_init(&cfg, &hd); + esp_gmf_task_set_event_func(hd, esp_gmf_task_evt, NULL); + + esp_gmf_task_register_ready_job(hd, NULL, prepare1, ESP_GMF_JOB_TIMES_ONCE, NULL, false); + esp_gmf_task_register_ready_job(hd, NULL, prepare2, ESP_GMF_JOB_TIMES_ONCE, NULL, false); + esp_gmf_task_register_ready_job(hd, NULL, prepare3, ESP_GMF_JOB_TIMES_ONCE, NULL, false); + esp_gmf_task_register_ready_job(hd, NULL, prepare4, ESP_GMF_JOB_TIMES_ONCE, NULL, false); + + esp_gmf_task_register_ready_job(hd, NULL, working1, ESP_GMF_JOB_TIMES_INFINITE, NULL, false); + esp_gmf_task_register_ready_job(hd, NULL, working2, ESP_GMF_JOB_TIMES_INFINITE, NULL, false); + esp_gmf_task_register_ready_job(hd, NULL, working3, ESP_GMF_JOB_TIMES_INFINITE, NULL, false); + esp_gmf_task_register_ready_job(hd, NULL, working4, ESP_GMF_JOB_TIMES_INFINITE, NULL, false); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_run(hd)); + vTaskDelay(200 / portTICK_PERIOD_MS); + + test_gmf_task2_count.working_return = ESP_GMF_JOB_ERR_FAIL; + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_pause(hd)); + vTaskDelay(100 / portTICK_PERIOD_MS); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_NOT_SUPPORT, esp_gmf_task_resume(hd)); + vTaskDelay(200 / portTICK_PERIOD_MS); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_stop(hd)); + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_deinit(hd)); + + + TEST_ASSERT_EQUAL(1, test_gmf_task1_count.cleanup); + TEST_ASSERT_EQUAL(1, test_gmf_task2_count.cleanup); + TEST_ASSERT_EQUAL(1, test_gmf_task3_count.cleanup); + TEST_ASSERT_EQUAL(1, test_gmf_task4_count.cleanup); + + TEST_ASSERT_EQUAL(1, test_gmf_task1_count.prepare); + TEST_ASSERT_EQUAL(1, test_gmf_task2_count.prepare); + TEST_ASSERT_EQUAL(1, test_gmf_task3_count.prepare); + TEST_ASSERT_EQUAL(1, test_gmf_task4_count.prepare); + + TEST_ASSERT_NOT_EQUAL(0, test_gmf_task1_count.working); + TEST_ASSERT_NOT_EQUAL(0, test_gmf_task2_count.working); + TEST_ASSERT_EQUAL(0, test_gmf_task3_count.working); + TEST_ASSERT_EQUAL(0, test_gmf_task4_count.working); + + ESP_LOGI(TAG, "task1: %d, task2: %d, task3: %d, task4: %d", test_gmf_task1_count.working, + test_gmf_task2_count.working, test_gmf_task3_count.working, test_gmf_task4_count.working); + + ESP_GMF_MEM_SHOW(TAG); +} + +TEST_CASE("Return error on the CLEANUP stage", "[ESP_GMF_TASK]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + + clear_test_gmf_task_count(); + esp_gmf_task_cfg_t cfg = DEFAULT_ESP_GMF_TASK_CONFIG(); + cfg.ctx = NULL; + cfg.cb = NULL; + esp_gmf_task_handle_t hd = NULL; + + esp_gmf_task_init(&cfg, &hd); + esp_gmf_task_set_event_func(hd, esp_gmf_task_evt, NULL); + + esp_gmf_task_register_ready_job(hd, NULL, prepare1, ESP_GMF_JOB_TIMES_ONCE, NULL, false); + esp_gmf_task_register_ready_job(hd, NULL, prepare2, ESP_GMF_JOB_TIMES_ONCE, NULL, false); + esp_gmf_task_register_ready_job(hd, NULL, prepare3, ESP_GMF_JOB_TIMES_ONCE, NULL, false); + esp_gmf_task_register_ready_job(hd, NULL, prepare4, ESP_GMF_JOB_TIMES_ONCE, NULL, false); + + esp_gmf_task_register_ready_job(hd, NULL, working1, ESP_GMF_JOB_TIMES_INFINITE, NULL, false); + esp_gmf_task_register_ready_job(hd, NULL, working2, ESP_GMF_JOB_TIMES_INFINITE, NULL, false); + esp_gmf_task_register_ready_job(hd, NULL, working3, ESP_GMF_JOB_TIMES_INFINITE, NULL, false); + esp_gmf_task_register_ready_job(hd, NULL, working4, ESP_GMF_JOB_TIMES_INFINITE, NULL, false); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_run(hd)); + vTaskDelay(200 / portTICK_PERIOD_MS); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_pause(hd)); + vTaskDelay(100 / portTICK_PERIOD_MS); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_resume(hd)); + + test_gmf_task2_count.cleanup_return = ESP_GMF_JOB_ERR_FAIL; + vTaskDelay(200 / portTICK_PERIOD_MS); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_stop(hd)); + + // Make sure enter error state + esp_gmf_event_state_t state = ESP_GMF_EVENT_STATE_NONE; + esp_gmf_task_get_state(hd, &state); + TEST_ASSERT_EQUAL(ESP_GMF_EVENT_STATE_ERROR, state); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_deinit(hd)); + + TEST_ASSERT_EQUAL(1, test_gmf_task1_count.cleanup); + TEST_ASSERT_EQUAL(1, test_gmf_task2_count.cleanup); + TEST_ASSERT_EQUAL(1, test_gmf_task3_count.cleanup); + TEST_ASSERT_EQUAL(1, test_gmf_task4_count.cleanup); + + TEST_ASSERT_EQUAL(1, test_gmf_task1_count.prepare); + TEST_ASSERT_EQUAL(1, test_gmf_task2_count.prepare); + TEST_ASSERT_EQUAL(1, test_gmf_task3_count.prepare); + TEST_ASSERT_EQUAL(1, test_gmf_task4_count.prepare); + + TEST_ASSERT_NOT_EQUAL(0, test_gmf_task1_count.working); + TEST_ASSERT_NOT_EQUAL(0, test_gmf_task2_count.working); + TEST_ASSERT_NOT_EQUAL(0, test_gmf_task3_count.working); + TEST_ASSERT_NOT_EQUAL(0, test_gmf_task4_count.working); + + ESP_LOGI(TAG, "task1: %d, task2: %d, task3: %d, task4: %d", test_gmf_task1_count.working, + test_gmf_task2_count.working, test_gmf_task3_count.working, test_gmf_task4_count.working); + if ((test_gmf_task1_count.working > test_gmf_task2_count.working) + && (test_gmf_task2_count.working == test_gmf_task3_count.working) + && (test_gmf_task3_count.working == test_gmf_task4_count.working)) { + // OK + TEST_ASSERT_TRUE(true); + } else { + TEST_ASSERT_FALSE(true); + } + + ESP_GMF_MEM_SHOW(TAG); +} + +TEST_CASE("Finished state return error on the CLEANUP", "[ESP_GMF_TASK]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + + clear_test_gmf_task_count(); + esp_gmf_task_cfg_t cfg = DEFAULT_ESP_GMF_TASK_CONFIG(); + cfg.ctx = NULL; + cfg.cb = NULL; + esp_gmf_task_handle_t hd = NULL; + + esp_gmf_task_init(&cfg, &hd); + esp_gmf_task_set_event_func(hd, esp_gmf_task_evt, NULL); + + esp_gmf_task_register_ready_job(hd, NULL, prepare1, ESP_GMF_JOB_TIMES_ONCE, NULL, false); + esp_gmf_task_register_ready_job(hd, NULL, prepare2, ESP_GMF_JOB_TIMES_ONCE, NULL, false); + esp_gmf_task_register_ready_job(hd, NULL, prepare3, ESP_GMF_JOB_TIMES_ONCE, NULL, false); + esp_gmf_task_register_ready_job(hd, NULL, prepare4, ESP_GMF_JOB_TIMES_ONCE, NULL, false); + + esp_gmf_task_register_ready_job(hd, NULL, working1, ESP_GMF_JOB_TIMES_INFINITE, NULL, false); + esp_gmf_task_register_ready_job(hd, NULL, working2, ESP_GMF_JOB_TIMES_INFINITE, NULL, false); + esp_gmf_task_register_ready_job(hd, NULL, working3, ESP_GMF_JOB_TIMES_INFINITE, NULL, false); + esp_gmf_task_register_ready_job(hd, NULL, working4, ESP_GMF_JOB_TIMES_INFINITE, NULL, false); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_run(hd)); + // Let all job to be finished + test_gmf_task1_count.working_return = ESP_GMF_JOB_ERR_DONE; + test_gmf_task2_count.working_return = ESP_GMF_JOB_ERR_DONE; + test_gmf_task3_count.working_return = ESP_GMF_JOB_ERR_DONE; + test_gmf_task4_count.working_return = ESP_GMF_JOB_ERR_DONE; + test_gmf_task2_count.cleanup_return = ESP_GMF_JOB_ERR_FAIL; + vTaskDelay(200 / portTICK_PERIOD_MS); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_pause(hd)); + vTaskDelay(100 / portTICK_PERIOD_MS); + + // When resume for finished return not supported + TEST_ASSERT_EQUAL(ESP_GMF_ERR_NOT_SUPPORT, esp_gmf_task_resume(hd)); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_stop(hd)); + // Even stopped we keep the state to be error + esp_gmf_event_state_t state = ESP_GMF_EVENT_STATE_NONE; + esp_gmf_task_get_state(hd, &state); + TEST_ASSERT_EQUAL(ESP_GMF_EVENT_STATE_ERROR, state); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_deinit(hd)); + + TEST_ASSERT_EQUAL(1, test_gmf_task1_count.cleanup); + TEST_ASSERT_EQUAL(1, test_gmf_task2_count.cleanup); + TEST_ASSERT_EQUAL(1, test_gmf_task3_count.cleanup); + TEST_ASSERT_EQUAL(1, test_gmf_task4_count.cleanup); + + TEST_ASSERT_EQUAL(1, test_gmf_task1_count.prepare); + TEST_ASSERT_EQUAL(1, test_gmf_task2_count.prepare); + TEST_ASSERT_EQUAL(1, test_gmf_task3_count.prepare); + TEST_ASSERT_EQUAL(1, test_gmf_task4_count.prepare); + + TEST_ASSERT_NOT_EQUAL(0, test_gmf_task1_count.working); + TEST_ASSERT_NOT_EQUAL(0, test_gmf_task2_count.working); + TEST_ASSERT_NOT_EQUAL(0, test_gmf_task3_count.working); + TEST_ASSERT_NOT_EQUAL(0, test_gmf_task4_count.working); + + ESP_LOGI(TAG, "task1: %d, task2: %d, task3: %d, task4: %d", test_gmf_task1_count.working, + test_gmf_task2_count.working, test_gmf_task3_count.working, test_gmf_task4_count.working); + if ((test_gmf_task1_count.working == test_gmf_task2_count.working) && test_gmf_task1_count.working == test_gmf_task3_count.working) { + // OK + TEST_ASSERT_TRUE(true); + } else { + TEST_ASSERT_FALSE(true); + } + + ESP_GMF_MEM_SHOW(TAG); +} + +TEST_CASE("Return error after call STOP", "[ESP_GMF_TASK]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + // esp_log_level_set("ESP_GMF_TASK", ESP_LOG_DEBUG); + clear_test_gmf_task_count(); + esp_gmf_task_cfg_t cfg = DEFAULT_ESP_GMF_TASK_CONFIG(); + cfg.ctx = NULL; + cfg.cb = NULL; + esp_gmf_task_handle_t hd = NULL; + + esp_gmf_task_init(&cfg, &hd); + esp_gmf_task_set_event_func(hd, esp_gmf_task_evt, NULL); + + esp_gmf_task_register_ready_job(hd, NULL, prepare1, ESP_GMF_JOB_TIMES_ONCE, NULL, false); + esp_gmf_task_register_ready_job(hd, NULL, prepare2, ESP_GMF_JOB_TIMES_ONCE, NULL, false); + esp_gmf_task_register_ready_job(hd, NULL, prepare3, ESP_GMF_JOB_TIMES_ONCE, NULL, false); + esp_gmf_task_register_ready_job(hd, NULL, prepare4, ESP_GMF_JOB_TIMES_ONCE, NULL, false); + + esp_gmf_task_register_ready_job(hd, NULL, working1, ESP_GMF_JOB_TIMES_INFINITE, NULL, false); + esp_gmf_task_register_ready_job(hd, NULL, working2, ESP_GMF_JOB_TIMES_INFINITE, NULL, false); + esp_gmf_task_register_ready_job(hd, NULL, working3, ESP_GMF_JOB_TIMES_INFINITE, NULL, false); + esp_gmf_task_register_ready_job(hd, NULL, working4, ESP_GMF_JOB_TIMES_INFINITE, NULL, false); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_run(hd)); + vTaskDelay(200 / portTICK_PERIOD_MS); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_pause(hd)); + vTaskDelay(100 / portTICK_PERIOD_MS); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_resume(hd)); + vTaskDelay(200 / portTICK_PERIOD_MS); + + test_gmf_task2_count.cleanup_return = ESP_GMF_JOB_ERR_FAIL; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_stop(hd)); + + esp_gmf_event_state_t state = ESP_GMF_EVENT_STATE_NONE; + esp_gmf_task_get_state(hd, &state); + TEST_ASSERT_EQUAL(ESP_GMF_EVENT_STATE_ERROR, state); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_deinit(hd)); + + TEST_ASSERT_EQUAL(1, test_gmf_task1_count.cleanup); + TEST_ASSERT_EQUAL(1, test_gmf_task2_count.cleanup); + TEST_ASSERT_EQUAL(1, test_gmf_task3_count.cleanup); + TEST_ASSERT_EQUAL(1, test_gmf_task4_count.cleanup); + + TEST_ASSERT_EQUAL(1, test_gmf_task1_count.prepare); + TEST_ASSERT_EQUAL(1, test_gmf_task2_count.prepare); + TEST_ASSERT_EQUAL(1, test_gmf_task3_count.prepare); + TEST_ASSERT_EQUAL(1, test_gmf_task4_count.prepare); + + TEST_ASSERT_NOT_EQUAL(0, test_gmf_task1_count.working); + TEST_ASSERT_NOT_EQUAL(0, test_gmf_task2_count.working); + TEST_ASSERT_NOT_EQUAL(0, test_gmf_task3_count.working); + TEST_ASSERT_NOT_EQUAL(0, test_gmf_task4_count.working); + + ESP_LOGI(TAG, "task1: %d, task2: %d, task3: %d, task4: %d", test_gmf_task1_count.working, + test_gmf_task2_count.working, test_gmf_task3_count.working, test_gmf_task4_count.working); + // if ((test_gmf_task1_count.working > test_gmf_task2_count.working) + // && (test_gmf_task2_count.working > test_gmf_task3_count.working) + // && (test_gmf_task3_count.working >= test_gmf_task4_count.working)) { + // // OK + // TEST_ASSERT_TRUE(true); + // } else { + // TEST_ASSERT_FALSE(true); + // } + + ESP_GMF_MEM_SHOW(TAG); +} + +TEST_CASE("Once job return TRUNCATE", "[ESP_GMF_TASK]") +{ + esp_log_level_set("*", ESP_LOG_INFO); + + clear_test_gmf_task_count(); + esp_gmf_task_cfg_t cfg = DEFAULT_ESP_GMF_TASK_CONFIG(); + cfg.ctx = NULL; + cfg.cb = NULL; + esp_gmf_task_handle_t hd = NULL; + + esp_gmf_task_init(&cfg, &hd); + esp_gmf_task_set_event_func(hd, esp_gmf_task_evt, NULL); + + esp_gmf_task_register_ready_job(hd, NULL, prepare1, ESP_GMF_JOB_TIMES_ONCE, NULL, false); + esp_gmf_task_register_ready_job(hd, NULL, prepare2, ESP_GMF_JOB_TIMES_ONCE, NULL, false); + esp_gmf_task_register_ready_job(hd, NULL, prepare3, ESP_GMF_JOB_TIMES_ONCE, NULL, false); + esp_gmf_task_register_ready_job(hd, NULL, prepare4, ESP_GMF_JOB_TIMES_ONCE, NULL, false); + + esp_gmf_task_register_ready_job(hd, NULL, working1, ESP_GMF_JOB_TIMES_INFINITE, NULL, false); + esp_gmf_task_register_ready_job(hd, NULL, working2, ESP_GMF_JOB_TIMES_INFINITE, NULL, false); + // Let working3 run once only + esp_gmf_task_register_ready_job(hd, NULL, working3, ESP_GMF_JOB_TIMES_ONCE, NULL, false); + esp_gmf_task_register_ready_job(hd, NULL, working4, ESP_GMF_JOB_TIMES_INFINITE, NULL, false); + // Let working3 run TRUNCATE + test_gmf_task3_count.working_return = ESP_GMF_JOB_ERR_TRUNCATE; + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_run(hd)); + vTaskDelay(100 / portTICK_PERIOD_MS); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_pause(hd)); + vTaskDelay(100 / portTICK_PERIOD_MS); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_resume(hd)); + vTaskDelay(100 / portTICK_PERIOD_MS); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_stop(hd)); + + esp_gmf_event_state_t state = ESP_GMF_EVENT_STATE_NONE; + esp_gmf_task_get_state(hd, &state); + TEST_ASSERT_EQUAL(ESP_GMF_EVENT_STATE_ERROR, state); + + TEST_ASSERT_EQUAL(ESP_GMF_ERR_OK, esp_gmf_task_deinit(hd)); + + TEST_ASSERT_EQUAL(1, test_gmf_task1_count.cleanup); + TEST_ASSERT_EQUAL(1, test_gmf_task2_count.cleanup); + TEST_ASSERT_EQUAL(1, test_gmf_task3_count.cleanup); + TEST_ASSERT_EQUAL(1, test_gmf_task4_count.cleanup); + + TEST_ASSERT_EQUAL(1, test_gmf_task1_count.prepare); + TEST_ASSERT_EQUAL(1, test_gmf_task2_count.prepare); + TEST_ASSERT_EQUAL(1, test_gmf_task3_count.prepare); + TEST_ASSERT_EQUAL(1, test_gmf_task4_count.prepare); + + TEST_ASSERT_NOT_EQUAL(0, test_gmf_task1_count.working); + TEST_ASSERT_NOT_EQUAL(0, test_gmf_task2_count.working); + TEST_ASSERT_NOT_EQUAL(0, test_gmf_task3_count.working); + // Test4 should not run for task3 error + TEST_ASSERT_EQUAL(0, test_gmf_task4_count.working); + + ESP_GMF_MEM_SHOW(TAG); +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/main/cases/gmf_uri_test.c b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/main/cases/gmf_uri_test.c new file mode 100644 index 0000000..c465688 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/main/cases/gmf_uri_test.c @@ -0,0 +1,132 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "unity.h" +#include "esp_gmf_uri_parser.h" + +TEST_CASE("URI, Parser test", "[ESP_GMF_URI]") +{ + esp_gmf_uri_t *uri = NULL; + + TEST_ASSERT_EQUAL(0, esp_gmf_uri_parse("http://username:password@www.example.com:8080/path/to/resource?query=param#fragment", &uri)); + TEST_ASSERT_EQUAL_STRING("http", uri->scheme); + TEST_ASSERT_EQUAL_STRING("username", uri->username); + TEST_ASSERT_EQUAL_STRING("password", uri->password); + TEST_ASSERT_EQUAL_STRING("www.example.com", uri->host); + TEST_ASSERT_EQUAL_INT(8080, uri->port); + TEST_ASSERT_EQUAL_STRING("/path/to/resource", uri->path); + TEST_ASSERT_EQUAL_STRING("query=param", uri->query); + TEST_ASSERT_EQUAL_STRING("fragment", uri->fragment); + esp_gmf_uri_free(uri); + + TEST_ASSERT_EQUAL(0, esp_gmf_uri_parse("http://www.example.com", &uri)); + TEST_ASSERT_EQUAL_STRING("http", uri->scheme); + TEST_ASSERT_EQUAL_STRING("www.example.com", uri->host); + TEST_ASSERT_NULL(uri->path); + TEST_ASSERT_NULL(uri->query); + TEST_ASSERT_NULL(uri->fragment); + esp_gmf_uri_free(uri); + + TEST_ASSERT_EQUAL(0, esp_gmf_uri_parse("http://www.example.com/path/to/resource", &uri)); + TEST_ASSERT_EQUAL_STRING("http", uri->scheme); + TEST_ASSERT_EQUAL_STRING("www.example.com", uri->host); + TEST_ASSERT_EQUAL_STRING("/path/to/resource", uri->path); + TEST_ASSERT_NULL(uri->query); + TEST_ASSERT_NULL(uri->fragment); + esp_gmf_uri_free(uri); + + TEST_ASSERT_EQUAL(0, esp_gmf_uri_parse("http://www.example.com/path?query=param", &uri)); + TEST_ASSERT_EQUAL_STRING("http", uri->scheme); + TEST_ASSERT_EQUAL_STRING("www.example.com", uri->host); + TEST_ASSERT_EQUAL_STRING("/path", uri->path); + TEST_ASSERT_EQUAL_STRING("query=param", uri->query); + TEST_ASSERT_NULL(uri->fragment); + esp_gmf_uri_free(uri); + + TEST_ASSERT_EQUAL(0, esp_gmf_uri_parse("http://www.example.com/path#fragment", &uri)); + TEST_ASSERT_EQUAL_STRING("http", uri->scheme); + TEST_ASSERT_EQUAL_STRING("www.example.com", uri->host); + TEST_ASSERT_EQUAL_STRING("/path", uri->path); + TEST_ASSERT_NULL(uri->query); + TEST_ASSERT_EQUAL_STRING("fragment", uri->fragment); + esp_gmf_uri_free(uri); + + TEST_ASSERT_EQUAL(0, esp_gmf_uri_parse("http://www.example.com/path?query=param#fragment", &uri)); + TEST_ASSERT_EQUAL_STRING("http", uri->scheme); + TEST_ASSERT_EQUAL_STRING("www.example.com", uri->host); + TEST_ASSERT_EQUAL_STRING("/path", uri->path); + TEST_ASSERT_EQUAL_STRING("query=param", uri->query); + TEST_ASSERT_EQUAL_STRING("fragment", uri->fragment); + esp_gmf_uri_free(uri); + + TEST_ASSERT_EQUAL(0, esp_gmf_uri_parse("http://www.example.com:8080", &uri)); + TEST_ASSERT_EQUAL_STRING("http", uri->scheme); + TEST_ASSERT_EQUAL_STRING("www.example.com", uri->host); + TEST_ASSERT_EQUAL_INT(8080, uri->port); + TEST_ASSERT_NULL(uri->path); + TEST_ASSERT_NULL(uri->query); + TEST_ASSERT_NULL(uri->fragment); + esp_gmf_uri_free(uri); + + TEST_ASSERT_EQUAL(0, esp_gmf_uri_parse("http://username:password@www.example.com", &uri)); + TEST_ASSERT_EQUAL_STRING("http", uri->scheme); + TEST_ASSERT_EQUAL_STRING("username", uri->username); + TEST_ASSERT_EQUAL_STRING("password", uri->password); + TEST_ASSERT_EQUAL_STRING("www.example.com", uri->host); + esp_gmf_uri_free(uri); + + TEST_ASSERT_EQUAL(0, esp_gmf_uri_parse("http://username:password@www.example.com:8080/path?query=param#fragment", &uri)); + TEST_ASSERT_EQUAL_STRING("http", uri->scheme); + TEST_ASSERT_EQUAL_STRING("username", uri->username); + TEST_ASSERT_EQUAL_STRING("password", uri->password); + TEST_ASSERT_EQUAL_STRING("www.example.com", uri->host); + TEST_ASSERT_EQUAL_INT(8080, uri->port); + TEST_ASSERT_EQUAL_STRING("/path", uri->path); + TEST_ASSERT_EQUAL_STRING("query=param", uri->query); + TEST_ASSERT_EQUAL_STRING("fragment", uri->fragment); + esp_gmf_uri_free(uri); + + TEST_ASSERT_EQUAL(0, esp_gmf_uri_parse("https://www.secure.com", &uri)); + TEST_ASSERT_EQUAL_STRING("https", uri->scheme); + TEST_ASSERT_EQUAL_STRING("www.secure.com", uri->host); + esp_gmf_uri_free(uri); + + TEST_ASSERT_EQUAL(0, esp_gmf_uri_parse("ftp://ftp.example.com/resource", &uri)); + TEST_ASSERT_EQUAL_STRING("ftp", uri->scheme); + TEST_ASSERT_EQUAL_STRING("ftp.example.com", uri->host); + TEST_ASSERT_EQUAL_STRING("/resource", uri->path); + esp_gmf_uri_free(uri); + + TEST_ASSERT_EQUAL(0, esp_gmf_uri_parse("http:///test.mp3", &uri)); + TEST_ASSERT_EQUAL_STRING("http", uri->scheme); + TEST_ASSERT_EQUAL_STRING("", uri->host); + TEST_ASSERT_EQUAL_STRING("/test.mp3", uri->path); + esp_gmf_uri_free(uri); + + TEST_ASSERT_EQUAL(0, esp_gmf_uri_parse("http://test.mp3", &uri)); + TEST_ASSERT_EQUAL_STRING("http", uri->scheme); + TEST_ASSERT_EQUAL_STRING("test.mp3", uri->host); + esp_gmf_uri_free(uri); + + TEST_ASSERT_EQUAL(-1, esp_gmf_uri_parse("mailto:user@example.com", &uri)); + TEST_ASSERT_EQUAL(-1, esp_gmf_uri_parse("data:text/plain;base64,SGVsbG8sIFdvcmxkIQ==", &uri)); + TEST_ASSERT_EQUAL(-1, esp_gmf_uri_parse("www.example.com/path", &uri)); + TEST_ASSERT_EQUAL(-1, esp_gmf_uri_parse("", &uri)); + TEST_ASSERT_EQUAL(-1, esp_gmf_uri_parse("data:application//octet-stream;base64,SGVsbG8sIFdvcmxkIQ==", &uri)); + + // TEST_ASSERT_EQUAL(-1, esp_gmf_uri_parse("http://www.example.com/path/%E0%A4%A", &uri)); + // TEST_ASSERT_EQUAL(-1, esp_gmf_uri_parse("http://username:pass word@www.exa mple.com:80a0/path/to/resource?key1=&=value2#fragment#anotherfragment", &uri)); + // TEST_ASSERT_EQUAL(-1, esp_gmf_uri_parse("ht@tp://www.example.com", &uri)); + // TEST_ASSERT_EQUAL(-1, esp_gmf_uri_parse("http://www.exa mple.com", &uri)); + // TEST_ASSERT_EQUAL(-1, esp_gmf_uri_parse("http://www.example.com:80a0", &uri)); + // TEST_ASSERT_EQUAL(-1, esp_gmf_uri_parse("http://www.example.com/pa th", &uri)); + // TEST_ASSERT_EQUAL(-1, esp_gmf_uri_parse("http://www.example.com//path/to//resource", &uri)); + // TEST_ASSERT_EQUAL(-1, esp_gmf_uri_parse("http://www.example.com/path#fragment#anotherfragment", &uri)); + // TEST_ASSERT_EQUAL(-1, esp_gmf_uri_parse("http://www.example.com/path?key1=&=value2", &uri)); + // TEST_ASSERT_EQUAL(-1, esp_gmf_uri_parse("http://[2001:db8::1g]:8080/path", &uri)); + // TEST_ASSERT_EQUAL(-1, esp_gmf_uri_parse("http://[2001:db8::1:8080/path", &uri)); +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/main/common/gmf_fake_dec.c b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/main/common/gmf_fake_dec.c new file mode 100644 index 0000000..94ed4cd --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/main/common/gmf_fake_dec.c @@ -0,0 +1,429 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "freertos/FreeRTOS.h" +#include "esp_log.h" +#include "esp_gmf_oal_mem.h" +#include "esp_gmf_info.h" +#include "gmf_fake_dec.h" +#include "mock_dec.h" +#include "esp_gmf_caps_def.h" + +static const char *TAG = "FAKE_DEC"; + +typedef struct { + esp_gmf_audio_element_t parent; + bool is_opened; + uint64_t data_size; + uint64_t filter[2]; + mock_dec_handle_t mock_hd; + char fake_name[32]; + mock_dec_el_args_t args; +} fake_decoder_t; + +static esp_gmf_err_t audio_attr_iter_fun(uint32_t attr_index, esp_gmf_cap_attr_t *attr) +{ + switch (attr_index) { + case 0: { + ESP_GMF_CAP_ATTR_SET_STEPWISE(attr, STR_2_EIGHTCC("TEST"), 8000, 3000, 22000); + break; + } + default: + attr->prop_type = ESP_GMF_PROP_TYPE_NONE; + return ESP_GMF_ERR_NOT_FOUND; + } + return ESP_GMF_ERR_OK; +} + +static esp_gmf_job_err_t fake_dec_open(esp_gmf_audio_element_handle_t self, void *para) +{ + ESP_LOGW(TAG, "fake_dec_open, %p-%s", self, OBJ_GET_TAG(self)); + fake_decoder_t *dec = (fake_decoder_t *)self; + mock_dec_open(&dec->mock_hd); + return ESP_GMF_JOB_ERR_OK; +} + +static esp_gmf_job_err_t fake_dec_process(esp_gmf_audio_element_handle_t self, void *para) +{ + esp_gmf_element_handle_t hd = (esp_gmf_element_handle_t)self; + esp_gmf_port_t *in_port = ESP_GMF_ELEMENT_GET(hd)->in; + esp_gmf_port_t *out_port = ESP_GMF_ELEMENT_GET(hd)->out; + esp_gmf_payload_t *in_load = NULL; + esp_gmf_payload_t *out_load = NULL; + fake_dec_cfg_t *cfg = OBJ_GET_CFG(hd); + if (cfg->is_shared == false) { + esp_gmf_port_enable_payload_share(in_port, false); + } + esp_gmf_err_io_t ret = esp_gmf_port_acquire_in(in_port, &in_load, ESP_GMF_ELEMENT_GET(self)->in_attr.data_size, portMAX_DELAY); + if (ret < ESP_GMF_IO_OK) { + ESP_LOGE(TAG, "Read data error, port:%p, ret:%d", in_port, ret); + return ret == ESP_GMF_IO_ABORT ? ESP_GMF_JOB_ERR_OK : ESP_GMF_JOB_ERR_FAIL; + } + if (cfg->is_pass && (in_port->is_shared == 1)) { + out_load = in_load; + } + ret = esp_gmf_port_acquire_out(out_port, &out_load, ESP_GMF_ELEMENT_GET(self)->out_attr.data_size, portMAX_DELAY); + if (ret < ESP_GMF_IO_OK) { + ESP_LOGE(TAG, "Out port get error, %p, ret:%d", out_port, ret); + return ret == ESP_GMF_IO_ABORT ? ESP_GMF_JOB_ERR_OK : ESP_GMF_JOB_ERR_FAIL; + } + vTaskDelay(10 / portTICK_PERIOD_MS); + ret = esp_gmf_port_release_out(out_port, out_load, portMAX_DELAY); + if (ret < ESP_GMF_IO_OK) { + ESP_LOGE(TAG, "Out port get error, %p, ret:%d", out_port, ret); + return ret == ESP_GMF_IO_ABORT ? ESP_GMF_JOB_ERR_OK : ESP_GMF_JOB_ERR_FAIL; + } + ESP_LOGD(TAG, "[%p-%s]I:%p,b:%p,s:%d, done:%d; O:%p,b:%p,s:%d, done:%d", hd, OBJ_GET_TAG(hd), in_port, in_load->buf, in_load->valid_size, in_load->is_done, + out_port, out_load->buf, out_load->valid_size, out_load->is_done); + ret = esp_gmf_port_release_in(in_port, in_load, portMAX_DELAY); + if (ret < ESP_GMF_IO_OK) { + ESP_LOGE(TAG, "In port get error, %p,ret:%d", out_port, ret); + return ret == ESP_GMF_IO_ABORT ? ESP_GMF_JOB_ERR_OK : ESP_GMF_JOB_ERR_FAIL; + } + if (in_load->valid_size > 0) { + esp_gmf_audio_el_update_file_pos(hd, in_load->valid_size); + } + return in_load->is_done ? ESP_GMF_JOB_ERR_DONE : ret; +} + +static esp_gmf_job_err_t fake_dec_close(esp_gmf_audio_element_handle_t self, void *para) +{ + ESP_LOGW(TAG, "Closed, %p", self); + fake_decoder_t *dec = (fake_decoder_t *)self; + dec->is_opened = false; + mock_dec_close(dec->mock_hd); + return ESP_OK; +} + +static esp_err_t fake_dec_destroy(esp_gmf_audio_element_handle_t self) +{ + ESP_LOGW(TAG, "Destroyed, %p", self); + void *cfg = OBJ_GET_CFG(self); + if (cfg) { + esp_gmf_oal_free(cfg); + } + esp_gmf_audio_el_deinit(self); + esp_gmf_oal_free(self); + return ESP_OK; +} + +static esp_err_t fake_dec_new(void *cfg, esp_gmf_obj_handle_t *handle) +{ + ESP_GMF_MEM_CHECK(TAG, handle, return ESP_ERR_INVALID_ARG); + fake_dec_cfg_t *fake_cfg = (fake_dec_cfg_t *)cfg; + esp_gmf_obj_handle_t new_obj = NULL; + int ret = fake_dec_init(fake_cfg, &new_obj); + if (ret != ESP_OK) { + return ret; + } + *handle = (void *)new_obj; + ESP_LOGI(TAG, "New an object,%s-%p", OBJ_GET_TAG(new_obj), new_obj); + + return ESP_OK; +} + +static esp_gmf_err_t __set_para(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc, uint8_t *buf, int buf_len) +{ + fake_decoder_t *el = (fake_decoder_t *)handle; + esp_gmf_args_desc_t *para_desc = arg_desc; + uint8_t idx = 0; + memcpy(&idx, buf + para_desc->offset, para_desc->size); + para_desc = para_desc->next; + mock_para_t para = {0}; + memcpy(¶, buf + para_desc->offset, sizeof(para)); + ESP_LOGI(TAG, "%s, idx:%d, fc:%ld, type:%ld, %f, %f", __func__, idx, para.fc, para.type, para.q, para.gain); + int ret = mock_dec_set_para(el->mock_hd, idx, ¶); + return ret; +} + +static esp_gmf_err_t __get_para(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc, uint8_t *buf, int buf_len) +{ + fake_decoder_t *el = (fake_decoder_t *)handle; + esp_gmf_args_desc_t *para_desc = arg_desc; + + uint8_t *idx = (uint8_t *)(buf + para_desc->offset); + para_desc = para_desc->next; + mock_para_t *para = (mock_para_t *)(buf + para_desc->offset); + int ret = mock_dec_get_para(el->mock_hd, *idx, para); + ESP_LOGI(TAG, "%s, idx:%d, fc:%ld, type:%ld, %f, %f", __func__, *idx, para->fc, para->type, para->q, para->gain); + return ret; +} + +static esp_gmf_err_t __set_args(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc, uint8_t *buf, int buf_len) +{ + fake_decoder_t *el = (fake_decoder_t *)handle; + esp_gmf_args_desc_t *args_desc = arg_desc; + memcpy(&el->args.desc, buf + args_desc->offset, args_desc->size); + args_desc = args_desc->next; + memcpy(&el->args.label, buf + args_desc->offset, args_desc->size); + ESP_LOGI(TAG, "%s, f[a:%x,b:%lx,c:%x],s[a:%x,b:%lx,c:%x],val:%x, name:%s", __func__, el->args.desc.first.a, el->args.desc.first.b, el->args.desc.first.c, el->args.desc.second.d, el->args.desc.second.e, el->args.desc.second.f, el->args.desc.value, el->args.label); + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t __get_args(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc, uint8_t *buf, int buf_len) +{ + fake_decoder_t *el = (fake_decoder_t *)handle; + esp_gmf_args_desc_t *args_desc = arg_desc; + + memcpy(buf + args_desc->offset, &el->args.desc, args_desc->size); + args_desc = args_desc->next; + memcpy(buf + args_desc->offset, &el->args.label, args_desc->size); + ESP_LOGI(TAG, "%s, f[a:%x,b:%lx,c:%x],s[a:%x,b:%lx,c:%x],val:%x, name:%s", __func__, el->args.desc.first.a, el->args.desc.first.b, el->args.desc.first.c, el->args.desc.second.d, el->args.desc.second.e, el->args.desc.second.f, el->args.desc.value, el->args.label); + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t __set_info(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc, uint8_t *buf, int buf_len) +{ + fake_decoder_t *el = (fake_decoder_t *)handle; + esp_gmf_args_desc_t *info_desc = arg_desc; + + uint32_t sample_rate = 0; + memcpy(&sample_rate, buf + info_desc->offset, info_desc->size); + uint16_t channel = 0; + info_desc = info_desc->next; + memcpy(&channel, buf + info_desc->offset, info_desc->size); + uint16_t bits = 0; + info_desc = info_desc->next; + memcpy(&bits, buf + info_desc->offset, info_desc->size); + ESP_LOGI(TAG, "%s, rate:%ld, ch:%d, bit:%d", __func__, sample_rate, channel, bits); + int ret = mock_dec_set_info(el->mock_hd, sample_rate, channel, bits); + return ret; +} + +static esp_gmf_err_t __get_info(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc, uint8_t *buf, int buf_len) +{ + fake_decoder_t *el = (fake_decoder_t *)handle; + esp_gmf_args_desc_t *info_desc = arg_desc; + + uint32_t *sample_rate = (uint32_t *)(buf + info_desc->offset); + info_desc = info_desc->next; + uint16_t *channel = (uint16_t *)(buf + info_desc->offset); + info_desc = info_desc->next; + uint16_t *bits = (uint16_t *)(buf + info_desc->offset); + int ret = mock_dec_get_info(el->mock_hd, sample_rate, channel, bits); + ESP_LOGI(TAG, "%s, rate:%ld, ch:%d, bit:%d", __func__, *sample_rate, *channel, *bits); + return ret; +} + +static esp_gmf_err_t __set_name(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc, uint8_t *buf, int buf_len) +{ + fake_decoder_t *el = (fake_decoder_t *)handle; + esp_gmf_args_desc_t *name_desc = arg_desc; + strlcpy(el->fake_name, (char *)buf, name_desc->size); + ESP_LOGI(TAG, "%s, name:%s", __func__, el->fake_name); + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t __get_name(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc, uint8_t *buf, int buf_len) +{ + fake_decoder_t *el = (fake_decoder_t *)handle; + esp_gmf_args_desc_t *name_desc = arg_desc; + strncpy((char *)buf, el->fake_name, name_desc->size); + ESP_LOGI(TAG, "%s, name:%s", __func__, el->fake_name); + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t __set_size(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc, uint8_t *buf, int buf_len) +{ + fake_decoder_t *el = (fake_decoder_t *)handle; + esp_gmf_args_desc_t *size_desc = arg_desc; + + memcpy(&el->data_size, buf + size_desc->offset, size_desc->size); + ESP_LOGI(TAG, "%s, data_size:%llx", __func__, el->data_size); + return ESP_OK; +} + +static esp_gmf_err_t __get_size(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc, uint8_t *buf, int buf_len) +{ + fake_decoder_t *el = (fake_decoder_t *)handle; + esp_gmf_args_desc_t *size_desc = arg_desc; + + memcpy(buf + size_desc->offset, &el->data_size, sizeof(size_desc->size)); + ESP_LOGI(TAG, "%s, data_size:%llx", __func__, el->data_size); + return ESP_OK; +} + +static esp_gmf_err_t __set_filter(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc, uint8_t *buf, int buf_len) +{ + fake_decoder_t *el = (fake_decoder_t *)handle; + esp_gmf_args_desc_t *filter_desc = arg_desc; + + uint8_t idx = 0; + memcpy(&idx, buf + filter_desc->offset, filter_desc->size); + filter_desc = filter_desc->next; + memcpy(&el->filter[idx], buf + filter_desc->offset, filter_desc->size); + ESP_LOGI(TAG, "%s, idx:%d, filter:%llx", __func__, idx, el->filter[idx]); + + return ESP_OK; +} + +static esp_gmf_err_t __get_filter(esp_gmf_element_handle_t handle, esp_gmf_args_desc_t *arg_desc, uint8_t *buf, int buf_len) +{ + fake_decoder_t *el = (fake_decoder_t *)handle; + esp_gmf_args_desc_t *filter_desc = arg_desc; + + uint8_t idx = 0; + memcpy(&idx, buf + filter_desc->offset, filter_desc->size); + filter_desc = filter_desc->next; + memcpy(buf + filter_desc->offset, &el->filter[idx], filter_desc->size); + ESP_LOGI(TAG, "%s, idx:%d, filter:%llx", __func__, idx, el->filter[idx]); + return ESP_OK; +} + +static esp_gmf_err_t _load_caps_func(esp_gmf_element_handle_t handle) +{ + esp_gmf_cap_t *caps = NULL; + esp_gmf_cap_t dec_caps = {0}; + dec_caps.cap_eightcc = STR_2_EIGHTCC("FAKEDEC"); + dec_caps.attr_fun = audio_attr_iter_fun; + int ret = esp_gmf_cap_append(&caps, &dec_caps); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, {return ret;}, "Failed to create capability"); + + esp_gmf_element_t *el = (esp_gmf_element_t *)handle; + el->caps = caps; + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t _load_methods_func(esp_gmf_element_handle_t handle) +{ + esp_gmf_method_t *method = NULL; + esp_gmf_args_desc_t *set_args = NULL; + esp_gmf_args_desc_t *pointer_args = NULL; + esp_gmf_args_desc_t *get_args = NULL; + + //// Register structure, two parameters, one for in, one for out + esp_gmf_args_desc_append(&pointer_args, "filter_type", ESP_GMF_ARGS_TYPE_UINT32, sizeof(uint32_t), offsetof(mock_para_t, type)); + esp_gmf_args_desc_append(&pointer_args, "fc", ESP_GMF_ARGS_TYPE_UINT32, sizeof(uint32_t), offsetof(mock_para_t, fc)); + esp_gmf_args_desc_append(&pointer_args, "q", ESP_GMF_ARGS_TYPE_FLOAT, sizeof(float), offsetof(mock_para_t, q)); + esp_gmf_args_desc_append(&pointer_args, "gain", ESP_GMF_ARGS_TYPE_FLOAT, sizeof(float), offsetof(mock_para_t, gain)); + ESP_GMF_ARGS_DESC_PRINT(pointer_args); + + esp_gmf_args_desc_append(&set_args, "index", ESP_GMF_ARGS_TYPE_UINT8, sizeof(uint8_t), 0); + esp_gmf_args_desc_append_array(&set_args, "para", pointer_args, sizeof(mock_para_t), sizeof(uint8_t)); + int ret = esp_gmf_method_append(&method, "set_para", __set_para, set_args); + ESP_GMF_RET_ON_ERROR(TAG, ret, {return ret;}, "Failed to register %s method", "set_para"); + ESP_GMF_ARGS_DESC_PRINT(set_args); + + esp_gmf_args_desc_copy(set_args, &get_args); + ret = esp_gmf_method_append(&method, "get_para", __get_para, get_args); + ESP_GMF_ARGS_DESC_PRINT(get_args); + + //// Register nested structure + pointer_args = NULL; + set_args = NULL; + esp_gmf_args_desc_t *nested_args = NULL; + + esp_gmf_args_desc_append(&pointer_args, "a", ESP_GMF_ARGS_TYPE_UINT8, sizeof(uint8_t), offsetof(mock_args_ldata_t, a)); + esp_gmf_args_desc_append(&pointer_args, "b", ESP_GMF_ARGS_TYPE_UINT32, sizeof(uint32_t), offsetof(mock_args_ldata_t, b)); + esp_gmf_args_desc_append(&pointer_args, "c", ESP_GMF_ARGS_TYPE_UINT16, sizeof(uint16_t), offsetof(mock_args_ldata_t, c)); + esp_gmf_args_desc_append_array(&nested_args, "first", pointer_args, sizeof(mock_args_ldata_t), offsetof(mock_dec_desc_t, first)); + ESP_GMF_ARGS_DESC_PRINT(nested_args); + + pointer_args = NULL; + esp_gmf_args_desc_append(&pointer_args, "d", ESP_GMF_ARGS_TYPE_UINT8, sizeof(uint8_t), offsetof(mock_args_hdata_t, d)); + esp_gmf_args_desc_append(&pointer_args, "e", ESP_GMF_ARGS_TYPE_UINT32, sizeof(uint32_t), offsetof(mock_args_hdata_t, e)); + esp_gmf_args_desc_append(&pointer_args, "f", ESP_GMF_ARGS_TYPE_UINT16, sizeof(uint16_t), offsetof(mock_args_hdata_t, f)); + esp_gmf_args_desc_append_array(&nested_args, "second", pointer_args, sizeof(mock_args_hdata_t), offsetof(mock_dec_desc_t, second)); + esp_gmf_args_desc_append(&nested_args, "value", ESP_GMF_ARGS_TYPE_UINT16, sizeof(uint16_t), offsetof(mock_dec_desc_t, value)); + ESP_GMF_ARGS_DESC_PRINT(nested_args); + + esp_gmf_args_desc_append_array(&set_args, "desc", nested_args, sizeof(mock_dec_desc_t), offsetof(mock_dec_el_args_t, desc)); + esp_gmf_args_desc_append(&set_args, "label", ESP_GMF_ARGS_TYPE_INT8, 16, offsetof(mock_dec_el_args_t, label)); + ret = esp_gmf_method_append(&method, "set_args", __set_args, set_args); + ESP_GMF_ARGS_DESC_PRINT(set_args); + + esp_gmf_args_desc_copy(set_args, &get_args); + ret = esp_gmf_method_append(&method, "get_args", __get_args, get_args); + ESP_GMF_ARGS_DESC_PRINT(get_args); + + // Register integer parameters + set_args = NULL; + esp_gmf_args_desc_append(&set_args, "rate", ESP_GMF_ARGS_TYPE_UINT32, sizeof(uint32_t), 0); + esp_gmf_args_desc_append(&set_args, "ch", ESP_GMF_ARGS_TYPE_UINT16, sizeof(uint16_t), sizeof(uint32_t)); + esp_gmf_args_desc_append(&set_args, "bits", ESP_GMF_ARGS_TYPE_UINT16, sizeof(uint16_t), sizeof(uint16_t) + sizeof(uint32_t)); + esp_gmf_args_desc_copy(set_args, &get_args); + ret = esp_gmf_method_append(&method, "set_info", __set_info, set_args); + ESP_GMF_ARGS_DESC_PRINT(get_args); + ret = esp_gmf_method_append(&method, "get_info", __get_info, get_args); + + // Register string parameters + set_args = NULL; + esp_gmf_args_desc_append(&set_args, "dec_name", ESP_GMF_ARGS_TYPE_INT8, 32, 0); + esp_gmf_args_desc_copy(set_args, &get_args); + ret = esp_gmf_method_append(&method, "set_name", __set_name, set_args); + ESP_GMF_ARGS_DESC_PRINT(get_args); + ret = esp_gmf_method_append(&method, "get_name", __get_name, get_args); + + // Register 64bits integer parameters + set_args = NULL; + esp_gmf_args_desc_append(&set_args, "size", ESP_GMF_ARGS_TYPE_UINT64, 8, 0); + esp_gmf_args_desc_copy(set_args, &get_args); + ESP_GMF_ARGS_DESC_PRINT(set_args); + ret = esp_gmf_method_append(&method, "set_size", __set_size, set_args); + ret = esp_gmf_method_append(&method, "get_size", __get_size, get_args); + + // Register two parameters, one for in, one for out + set_args = NULL; + esp_gmf_args_desc_append(&set_args, "index", ESP_GMF_ARGS_TYPE_UINT8, sizeof(uint8_t), 0); + esp_gmf_args_desc_append(&set_args, "filter", ESP_GMF_ARGS_TYPE_UINT64, 8, sizeof(uint8_t)); + esp_gmf_args_desc_copy(set_args, &get_args); + ESP_GMF_ARGS_DESC_PRINT(set_args); + ret = esp_gmf_method_append(&method, "set_filter", __set_filter, set_args); + ret = esp_gmf_method_append(&method, "get_filter", __get_filter, get_args); + + esp_gmf_element_t *el = (esp_gmf_element_t *)handle; + el->method = method; + return ESP_GMF_ERR_OK; +} + +esp_err_t fake_dec_init(fake_dec_cfg_t *config, esp_gmf_obj_handle_t *handle) +{ + ESP_GMF_MEM_CHECK(TAG, handle, return ESP_ERR_INVALID_ARG); + fake_decoder_t *fake = esp_gmf_oal_calloc(1, sizeof(fake_decoder_t)); + ESP_GMF_MEM_CHECK(TAG, fake, return ESP_ERR_NO_MEM); + esp_gmf_obj_t *obj = (esp_gmf_obj_t *)fake; + esp_gmf_err_t ret = ESP_GMF_ERR_OK; + ret = esp_gmf_obj_set_tag(obj, "fake_dec"); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto FAKE_DEC_FAIL, "Failed set OBJ tag"); + if (config) { + fake_dec_cfg_t *cfg = esp_gmf_oal_calloc(1, sizeof(*config)); + ESP_GMF_MEM_CHECK(TAG, cfg, {esp_gmf_oal_free(fake); return ESP_GMF_ERR_MEMORY_LACK;}); + memcpy(cfg, config, sizeof(*config)); + esp_gmf_obj_set_config(obj, cfg, sizeof(*cfg)); + if (config->name) { + ret = esp_gmf_obj_set_tag(obj, cfg->name); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto FAKE_DEC_FAIL, "Failed set OBJ tag"); + } + } + + obj->new_obj = fake_dec_new; + obj->del_obj = fake_dec_destroy; + + esp_gmf_element_cfg_t el_cfg = { + .cb = config->cb, + .in_attr.cap = ESP_GMF_EL_PORT_CAP_SINGLE, + .out_attr.cap = ESP_GMF_EL_PORT_CAP_SINGLE, + .in_attr.port.type = ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, + .out_attr.port.type = ESP_GMF_PORT_TYPE_BLOCK | ESP_GMF_PORT_TYPE_BYTE, + .in_attr.data_size = config->in_buf_size, + .out_attr.data_size = config->out_buf_size, + }; + ret = esp_gmf_audio_el_init(fake, &el_cfg); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto FAKE_DEC_FAIL, "Failed Initialize audio el"); + ESP_GMF_ELEMENT_GET(fake)->ops.open = fake_dec_open; + ESP_GMF_ELEMENT_GET(fake)->ops.process = fake_dec_process; + ESP_GMF_ELEMENT_GET(fake)->ops.close = fake_dec_close; + ESP_GMF_ELEMENT_GET(fake)->ops.load_caps = _load_caps_func; + ESP_GMF_ELEMENT_GET(fake)->ops.load_methods = _load_methods_func; + *handle = obj; + ESP_LOGI(TAG, "Create fake dec,%s-%p, in:%d, out:%d", OBJ_GET_TAG(obj), obj, config->in_buf_size, config->out_buf_size); + return ESP_OK; + +FAKE_DEC_FAIL: + fake_dec_destroy(obj); + return ret; +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/main/common/gmf_fake_dec.h b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/main/common/gmf_fake_dec.h new file mode 100644 index 0000000..c9b69b3 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/main/common/gmf_fake_dec.h @@ -0,0 +1,43 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "mock_dec.h" +#include "esp_gmf_audio_element.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * @brief Fake Decoder configurations + */ +typedef struct { + int in_buf_size; /*!< Size of output ringbuffer */ + int out_buf_size; + esp_gmf_event_cb cb; + const char *name; + bool is_pass; + bool is_shared; +} fake_dec_cfg_t; + +#define FAKE_DEC_BUFFER_SIZE (5 * 1024) + +#define DEFAULT_FAKE_DEC_CONFIG() { \ + .in_buf_size = FAKE_DEC_BUFFER_SIZE, \ + .out_buf_size = FAKE_DEC_BUFFER_SIZE, \ + .cb = NULL, \ + .name = NULL, \ + .is_pass = false, \ + .is_shared = true, \ +} + +esp_err_t fake_dec_init(fake_dec_cfg_t *config, esp_gmf_obj_handle_t *handle); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/main/common/gmf_fake_io.c b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/main/common/gmf_fake_io.c new file mode 100644 index 0000000..08e679a --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/main/common/gmf_fake_io.c @@ -0,0 +1,151 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "freertos/FreeRTOS.h" +#include +#include "esp_gmf_oal_mem.h" +#include "gmf_fake_io.h" +#include "esp_log.h" + +static const char *TAG = "FAKE_IO"; + +typedef struct { + esp_gmf_io_t base; +} fake_io_t; + +static esp_gmf_err_t _file_open(esp_gmf_io_handle_t io) +{ + fake_io_t *file_io = (fake_io_t *)io; + ESP_LOGI(TAG, "%s, %s-%p", __func__, OBJ_GET_TAG(file_io), file_io); + return ESP_GMF_ERR_OK; +} + +esp_gmf_err_io_t _file_acquire_read(esp_gmf_io_handle_t handle, void *payload, uint32_t wanted_size, int block_ticks) +{ + fake_io_t *file_io = (fake_io_t *)handle; + ESP_LOGD(TAG, "%s, %s-%p", __func__, OBJ_GET_TAG(file_io), file_io); + esp_gmf_payload_t *pload = (esp_gmf_payload_t *)payload; + pload->valid_size = wanted_size; + vTaskDelay(3 / portTICK_PERIOD_MS); + return wanted_size; +} + +esp_gmf_err_io_t _file_release_read(esp_gmf_io_handle_t handle, void *payload, int block_ticks) +{ + fake_io_t *file_io = (fake_io_t *)handle; + ESP_LOGD(TAG, "%s, %s-%p", __func__, OBJ_GET_TAG(file_io), file_io); + esp_gmf_payload_t *pload = (esp_gmf_payload_t *)payload; + esp_gmf_io_update_pos((esp_gmf_io_handle_t)handle, pload->valid_size); + vTaskDelay(2 / portTICK_PERIOD_MS); + return ESP_GMF_IO_OK; +} + +esp_gmf_err_io_t _file_acquire_write(esp_gmf_io_handle_t handle, void *payload, uint32_t wanted_size, int block_ticks) +{ + fake_io_t *file_io = (fake_io_t *)handle; + ESP_LOGD(TAG, "%s, %s-%p", __func__, OBJ_GET_TAG(file_io), file_io); + vTaskDelay(2 / portTICK_PERIOD_MS); + return wanted_size; +} + +esp_gmf_err_io_t _file_release_write(esp_gmf_io_handle_t handle, void *payload, int block_ticks) +{ + fake_io_t *file_io = (fake_io_t *)handle; + ESP_LOGD(TAG, "%s, %s-%p", __func__, OBJ_GET_TAG(file_io), file_io); + esp_gmf_payload_t *pload = (esp_gmf_payload_t *)payload; + esp_gmf_io_update_pos((esp_gmf_io_handle_t)handle, pload->valid_size); + vTaskDelay(2 / portTICK_PERIOD_MS); + return 1; +} + +esp_gmf_err_t _file_seek(esp_gmf_io_handle_t io, uint64_t seek_byte_pos) +{ + fake_io_t *file_io = (fake_io_t *)io; + ESP_LOGI(TAG, "%s, %s-%p", __func__, OBJ_GET_TAG(file_io), file_io); + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t _file_close(esp_gmf_io_handle_t io) +{ + fake_io_t *file_io = (fake_io_t *)io; + ESP_LOGI(TAG, "%s, %s-%p", __func__, OBJ_GET_TAG(file_io), file_io); + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t _file_delete(esp_gmf_io_handle_t io) +{ + fake_io_t *file_io = (fake_io_t *)io; + ESP_LOGI(TAG, "%s, %s-%p", __func__, OBJ_GET_TAG(file_io), file_io); + void *cfg = OBJ_GET_CFG(io); + if (cfg) { + esp_gmf_oal_free(cfg); + } + esp_gmf_io_deinit(io); + esp_gmf_oal_free(file_io); + return ESP_GMF_ERR_OK; +} + +static esp_gmf_err_t fake_io_new(void *cfg, esp_gmf_obj_handle_t *io) +{ + ESP_GMF_MEM_CHECK(TAG, cfg, return ESP_ERR_INVALID_ARG); + ESP_GMF_MEM_CHECK(TAG, io, return ESP_ERR_INVALID_ARG); + esp_gmf_obj_handle_t new_io = NULL; + fake_io_cfg_t *config = (fake_io_cfg_t *)cfg; + int ret = fake_io_init(config, &new_io); + if (ret != ESP_GMF_ERR_OK) { + return ret; + } + *io = new_io; + ESP_LOGI(TAG, "New an object,%s-%p", OBJ_GET_TAG(new_io), new_io); + return ret; +} + +esp_gmf_err_t fake_io_init(fake_io_cfg_t *config, esp_gmf_io_handle_t *io) +{ + ESP_GMF_MEM_CHECK(TAG, config, return ESP_ERR_INVALID_ARG); + ESP_GMF_MEM_CHECK(TAG, io, return ESP_ERR_INVALID_ARG); + fake_io_t *file_io = esp_gmf_oal_calloc(1, sizeof(fake_io_t)); + ESP_GMF_MEM_CHECK(TAG, file_io, return ESP_ERR_NO_MEM); + file_io->base.dir = config->dir; + file_io->base.type = ESP_GMF_IO_TYPE_BYTE; + + esp_gmf_obj_t *obj = (esp_gmf_obj_t *)file_io; + fake_io_cfg_t *cfg = esp_gmf_oal_calloc(1, sizeof(*config)); + ESP_GMF_MEM_CHECK(TAG, cfg, {esp_gmf_oal_free(file_io); return ESP_GMF_ERR_MEMORY_LACK;}); + memcpy(cfg, config, sizeof(*config)); + esp_gmf_obj_set_config(obj, cfg, sizeof(*cfg)); + + int ret = esp_gmf_obj_set_tag(obj, (config->name == NULL ? "file" : config->name)); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto _file_fail, "Failed set OBJ tag"); + obj->new_obj = fake_io_new; + obj->del_obj = _file_delete; + + file_io->base.close = _file_close; + file_io->base.open = _file_open; + file_io->base.seek = _file_seek; + fake_io_cfg_t *fat_cfg = (fake_io_cfg_t *)config; + esp_gmf_io_init(obj, NULL); + if (fat_cfg->dir == ESP_GMF_IO_DIR_WRITER) { + file_io->base.acquire_write = _file_acquire_write; + file_io->base.release_write = _file_release_write; + } else if (fat_cfg->dir == ESP_GMF_IO_DIR_READER) { + file_io->base.acquire_read = _file_acquire_read; + file_io->base.release_read = _file_release_read; + } else { + ESP_LOGW(TAG, "Does not set read or write function,%x", fat_cfg->dir); + ret = ESP_GMF_ERR_NOT_SUPPORT; + goto _file_fail; + } + + *io = obj; + ESP_LOGI(TAG, "Init Fake IO,%s-%p", OBJ_GET_TAG(obj), file_io); + return ESP_GMF_ERR_OK; + +_file_fail: + _file_delete(obj); + return ret; +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/main/common/gmf_fake_io.h b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/main/common/gmf_fake_io.h new file mode 100644 index 0000000..0a6498a --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/main/common/gmf_fake_io.h @@ -0,0 +1,42 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "esp_gmf_io.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * @brief Fake IO configurations + */ +typedef struct { + int dir; /*!< IO direction, reader or writer */ + const char *name; /*!< Name for this instance */ +} fake_io_cfg_t; + +#define FAKE_IO_CFG_DEFAULT() { \ + .dir = ESP_GMF_IO_DIR_NONE, \ + .name = NULL, \ +} + +/** + * @brief Initializes the fake stream I/O with the provided configuration + * + * @param[in] config Pointer to the fake IO configuration + * @param[out] io Pointer to the fake IO handle to be initialized + * + * @return + * - ESP_GMF_ERR_OK Success + * - other error codes Initialization failed + */ +esp_gmf_err_t fake_io_init(fake_io_cfg_t *config, esp_gmf_io_handle_t *io); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/main/common/gmf_ut_common.c b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/main/common/gmf_ut_common.c new file mode 100644 index 0000000..ac5298c --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/main/common/gmf_ut_common.c @@ -0,0 +1,153 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "unity.h" +#include +#include "esp_log.h" +#include "esp_gmf_oal_mem.h" +#include "driver/sdmmc_host.h" +#include "vfs_fat_internal.h" + +static const char *TAG = "ESP_GMF_UT_COM"; + +#ifdef CONFIG_IDF_TARGET_ESP32P4 +// ESP32p4_Function_EV_Board +// Sdmmc +#define ESP_SD_PIN_CLK (GPIO_NUM_43) +#define ESP_SD_PIN_CMD (GPIO_NUM_44) +#define ESP_SD_PIN_D0 (GPIO_NUM_39) +#define ESP_SD_PIN_D1 (GPIO_NUM_40) +#define ESP_SD_PIN_D2 (GPIO_NUM_41) +#define ESP_SD_PIN_D3 (GPIO_NUM_42) +#define ESP_SD_PIN_D4 (GPIO_NUM_NC) +#define ESP_SD_PIN_D5 (GPIO_NUM_NC) +#define ESP_SD_PIN_D6 (GPIO_NUM_NC) +#define ESP_SD_PIN_D7 (GPIO_NUM_NC) +#define ESP_SD_PIN_CD (GPIO_NUM_NC) +#define ESP_SD_PIN_WP (GPIO_NUM_NC) +#define ESP_WIDTH (4) +#else +#define ESP_SD_PIN_CLK GPIO_NUM_15 +#define ESP_SD_PIN_CMD GPIO_NUM_7 +#define ESP_SD_PIN_D0 GPIO_NUM_4 +#define ESP_SD_PIN_D1 -1 +#define ESP_SD_PIN_D2 -1 +#define ESP_SD_PIN_D3 -1 +#define ESP_SD_PIN_D4 -1 +#define ESP_SD_PIN_D5 -1 +#define ESP_SD_PIN_D6 -1 +#define ESP_SD_PIN_D7 -1 +#define ESP_SD_PIN_CD -1 +#define ESP_SD_PIN_WP -1 +#endif +int verify_two_files(const char *src_path, const char *dest_path) +{ + int len = 4096; + struct stat src_siz = {0}; + stat(src_path, &src_siz); + FILE *src_file = fopen(src_path, "rb"); + TEST_ASSERT_NOT_NULL(src_file); + char *src_buf = esp_gmf_oal_calloc(1, len); + TEST_ASSERT_NOT_NULL(src_buf); + ESP_LOGI(TAG, "The source file size is %ld, path:%s", src_siz.st_size, src_path); + + FILE *dest_file = fopen(dest_path, "rb"); + TEST_ASSERT_NOT_NULL(dest_file); + struct stat dest_siz = {0}; + char *dest_buf = esp_gmf_oal_calloc(1, len); + TEST_ASSERT_NOT_NULL(dest_buf); + stat(dest_path, &dest_siz); + ESP_LOGI(TAG, "The destination file size is %ld, path:%s", dest_siz.st_size, dest_path); + + TEST_ASSERT_EQUAL(src_siz.st_size, dest_siz.st_size); + int result = ESP_OK; + uint32_t pos = 0; + uint32_t max_len = (dest_siz.st_size > src_siz.st_size ? dest_siz.st_size : src_siz.st_size); + bool run = true; + while (run) { + fread(src_buf, 1, len, src_file); + fread(dest_buf, 1, len, dest_file); + for (int i = 0; i < len; ++i) { + if (src_buf[i] != dest_buf[i]) { + ESP_LOGE(TAG, "Unexepect data at:%ld, src:%x, dest:%x, max_len:%ld", pos, src_buf[i], dest_buf[i], max_len); + result = ESP_FAIL; + break; + } + pos++; + if (pos == max_len) { + run = false; + break; + } + } + } + fclose(src_file); + fclose(dest_file); + esp_gmf_oal_free(src_buf); + esp_gmf_oal_free(dest_buf); + return result; +} + +#if SOC_SDMMC_IO_POWER_EXTERNAL +#include "sd_pwr_ctrl_by_on_chip_ldo.h" +#endif /* SOC_SDMMC_IO_POWER_EXTERNAL */ + +void esp_gmf_ut_setup_sdmmc(sdmmc_card_t **out_card) +{ + sdmmc_card_t *card = NULL; + sdmmc_host_t host = SDMMC_HOST_DEFAULT(); + sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT(); + +#if SOC_SDMMC_IO_POWER_EXTERNAL + host.slot = SDMMC_HOST_SLOT_0; + host.max_freq_khz = SDMMC_FREQ_HIGHSPEED; + sd_pwr_ctrl_ldo_config_t ldo_config = { + .ldo_chan_id = 4, + }; + static sd_pwr_ctrl_handle_t pwr_ctrl_handle = NULL; + esp_err_t ret = sd_pwr_ctrl_new_on_chip_ldo(&ldo_config, &pwr_ctrl_handle); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to create a new on-chip LDO power control driver"); + return; + } + host.pwr_ctrl_handle = pwr_ctrl_handle; +#endif /* SOC_SDMMC_IO_POWER_EXTERNAL */ + + slot_config.width = 1; // 1-bits + esp_vfs_fat_sdmmc_mount_config_t mount_config = { + .format_if_mount_failed = true, + .max_files = 5, + .allocation_unit_size = 16 * 1024}; +#if SOC_SDMMC_USE_GPIO_MATRIX + slot_config.clk = ESP_SD_PIN_CLK; + slot_config.cmd = ESP_SD_PIN_CMD; + slot_config.d0 = ESP_SD_PIN_D0; + slot_config.d1 = ESP_SD_PIN_D1; + slot_config.d2 = ESP_SD_PIN_D2; + slot_config.d3 = ESP_SD_PIN_D3; + slot_config.d4 = ESP_SD_PIN_D4; + slot_config.d5 = ESP_SD_PIN_D5; + slot_config.d6 = ESP_SD_PIN_D6; + slot_config.d7 = ESP_SD_PIN_D7; + slot_config.cd = ESP_SD_PIN_CD; + slot_config.wp = ESP_SD_PIN_WP; +#endif /* SOC_SDMMC_USE_GPIO_MATRIX */ +#if defined CONFIG_IDF_TARGET_ESP32P4 + slot_config.flags |= SDMMC_SLOT_FLAG_INTERNAL_PULLUP; +#endif /* defined CONFIG_IDF_TARGET_ESP32P4 */ + TEST_ESP_OK(esp_vfs_fat_sdmmc_mount("/sdcard", &host, &slot_config, &mount_config, &card)); + *out_card = card; +} + +void esp_gmf_ut_teardown_sdmmc(sdmmc_card_t *card) +{ + TEST_ESP_OK(esp_vfs_fat_sdcard_unmount("/sdcard", card)); +#if SOC_SDMMC_IO_POWER_EXTERNAL + int ret = sd_pwr_ctrl_del_on_chip_ldo(card->host.pwr_ctrl_handle); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to delete the on-chip LDO power control driver"); + } +#endif /* SOC_SDMMC_IO_POWER_EXTERNAL */ +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/main/common/gmf_ut_common.h b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/main/common/gmf_ut_common.h new file mode 100644 index 0000000..05a2934 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/main/common/gmf_ut_common.h @@ -0,0 +1,23 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "driver/sdmmc_host.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +int verify_two_files(const char *src_path, const char *dest_path); + +void esp_gmf_ut_setup_sdmmc(sdmmc_card_t **out_card); + +void esp_gmf_ut_teardown_sdmmc(sdmmc_card_t *card); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/main/common/mock_dec.c b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/main/common/mock_dec.c new file mode 100644 index 0000000..9ad1591 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/main/common/mock_dec.c @@ -0,0 +1,76 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "esp_log.h" +#include "esp_gmf_oal_mem.h" +#include "mock_dec.h" +#include "esp_gmf_err.h" + +static const char *TAG = "MOCK_DEC"; + +typedef struct mock_decoder { + uint32_t sample_rate; + uint16_t channel; + uint16_t bits; + uint32_t para_size; + mock_para_t para; +} mock_decoder_t; + +esp_err_t mock_dec_open(mock_dec_handle_t *handle) +{ + ESP_LOGI(TAG, "Open, %p", handle); + *handle = esp_gmf_oal_calloc(1, sizeof(mock_decoder_t)); + ESP_GMF_MEM_CHECK(TAG, handle, return ESP_ERR_NO_MEM); + return ESP_OK; +} + +esp_err_t mock_dec_process(mock_dec_handle_t handle) +{ + ESP_LOGI(TAG, "Process, %p", handle); + return ESP_OK; +} + +esp_err_t mock_dec_close(mock_dec_handle_t handle) +{ + ESP_LOGW(TAG, "Closed, %p", handle); + esp_gmf_oal_free(handle); + return ESP_OK; +} + +esp_err_t mock_dec_set_para(mock_dec_handle_t handle, uint8_t para_size, mock_para_t *para) +{ + mock_decoder_t *dec = (mock_decoder_t *)handle; + dec->para_size = para_size; + memcpy(&dec->para, para, sizeof(mock_para_t)); + return ESP_OK; +} + +esp_err_t mock_dec_get_para(mock_dec_handle_t handle, uint8_t para_size, mock_para_t *para) +{ + mock_decoder_t *dec = (mock_decoder_t *)handle; + dec->para_size = para_size; + memcpy(para, &dec->para, sizeof(mock_para_t)); + return ESP_OK; +} + +esp_err_t mock_dec_set_info(mock_dec_handle_t handle, uint32_t sample_rate, uint16_t ch, uint16_t bits) +{ + mock_decoder_t *dec = (mock_decoder_t *)handle; + dec->sample_rate = sample_rate; + dec->channel = ch; + dec->bits = bits; + return ESP_OK; +} + +esp_err_t mock_dec_get_info(mock_dec_handle_t handle, uint32_t *sample_rate, uint16_t *ch, uint16_t *bits) +{ + mock_decoder_t *dec = (mock_decoder_t *)handle; + *sample_rate = dec->sample_rate; + *ch = dec->channel; + *bits = dec->bits; + return ESP_OK; +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/main/common/mock_dec.h b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/main/common/mock_dec.h new file mode 100644 index 0000000..8912f5c --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/main/common/mock_dec.h @@ -0,0 +1,63 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "esp_err.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +typedef void *mock_dec_handle_t; + +typedef struct { + uint8_t a; + uint32_t b; + uint16_t c; +} mock_args_ldata_t; + +#pragma pack(push, 1) +typedef struct { + uint8_t d; + uint32_t e; + uint16_t f; +} mock_args_hdata_t; +#pragma pack(pop) + +typedef struct { + mock_args_ldata_t first; + mock_args_hdata_t second; + uint16_t value; +} mock_dec_desc_t; + +typedef struct { + mock_dec_desc_t desc; + char label[16]; +} mock_dec_el_args_t; + +typedef struct { + uint32_t type; + uint32_t fc; + float q; + float gain; +} mock_para_t; + +esp_err_t mock_dec_open(mock_dec_handle_t *handle); +esp_err_t mock_dec_process(mock_dec_handle_t handle); +esp_err_t mock_dec_close(mock_dec_handle_t handle); + +esp_err_t mock_dec_set_para(mock_dec_handle_t handle, uint8_t index, mock_para_t *para); + +esp_err_t mock_dec_get_para(mock_dec_handle_t handle, uint8_t index, mock_para_t *para); + +esp_err_t mock_dec_set_info(mock_dec_handle_t handle, uint32_t sample_rate, uint16_t ch, uint16_t bits); + +esp_err_t mock_dec_get_info(mock_dec_handle_t handle, uint32_t *sample_rate, uint16_t *ch, uint16_t *bits); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/main/test_cxx_build.cpp b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/main/test_cxx_build.cpp new file mode 100644 index 0000000..3835e5e --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/main/test_cxx_build.cpp @@ -0,0 +1,73 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +/** + * Test API C++ compilation only, not as a example reference + */ +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/event_groups.h" +#include "esp_log.h" +#include "esp_err.h" +#include "esp_gmf_element.h" +#include "esp_gmf_pipeline.h" +#include "esp_gmf_pool.h" + +static const char *TAG = "CXX_BUILD_TEST"; + +static esp_gmf_err_t _pipeline_event(esp_gmf_event_pkt_t *event, void *ctx) +{ + return ESP_GMF_ERR_OK; +} + +extern "C" void test_cxx_build(void) +{ + esp_log_level_set("*", ESP_LOG_INFO); + int ret = 0; + + esp_gmf_pool_handle_t pool = NULL; + esp_gmf_pool_init(&pool); + ESP_GMF_POOL_SHOW_ITEMS(pool); + + esp_gmf_pipeline_handle_t pipe = NULL; + const char *name[] = {"aud_enc"}; + ret = esp_gmf_pool_new_pipeline(pool, "io_codec_dev", name, sizeof(name) / sizeof(char *), "io_file", &pipe); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, { return; }, "Failed to new pipeline"); + + esp_gmf_pipeline_set_out_uri(pipe, "/sdcard/test.aac"); + + esp_gmf_element_handle_t enc_el = NULL; + esp_gmf_pipeline_get_el_by_name(pipe, "aud_enc", &enc_el); + + esp_gmf_info_sound_t info = { + .format_id = 0, + .sample_rates = 16000, + .bitrate = 0, + .channels = 1, + .bits = 16, + }; + esp_gmf_pipeline_report_info(pipe, ESP_GMF_INFO_SOUND, &info, sizeof(info)); + + esp_gmf_task_cfg_t cfg = DEFAULT_ESP_GMF_TASK_CONFIG(); + cfg.ctx = NULL; + cfg.cb = NULL; + esp_gmf_task_handle_t work_task = NULL; + ret = esp_gmf_task_init(&cfg, &work_task); + ESP_GMF_RET_ON_NOT_OK(TAG, ret, { return;}, "Failed to create pipeline task"); + esp_gmf_pipeline_bind_task(pipe, work_task); + esp_gmf_pipeline_loading_jobs(pipe); + + esp_gmf_pipeline_set_event(pipe, _pipeline_event, NULL); + + esp_gmf_pipeline_run(pipe); + + vTaskDelay(10000 / portTICK_PERIOD_MS); + esp_gmf_pipeline_stop(pipe); + + esp_gmf_task_deinit(work_task); + esp_gmf_pipeline_destroy(pipe); + esp_gmf_pool_deinit(pool); +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/main/test_gmf_core_main.c b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/main/test_gmf_core_main.c new file mode 100644 index 0000000..ed0552c --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/main/test_gmf_core_main.c @@ -0,0 +1,13 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "unity.h" +#include "test_utils.h" + +void app_main(void) +{ + test_main(); +} diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/partitions.csv b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/partitions.csv new file mode 100644 index 0000000..c851199 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/partitions.csv @@ -0,0 +1,3 @@ +# Name, Type, SubType, Offset, Size, Flags +factory, app, factory, 0x10000, 2M, +storage, data, fat, , 528k, diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/pytest_gmf_core.py b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/pytest_gmf_core.py new file mode 100644 index 0000000..110c6a2 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/pytest_gmf_core.py @@ -0,0 +1,30 @@ +# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD +# +# SPDX-License-Identifier: Apache-2.0 +import pytest +from pytest_embedded import Dut + + +@pytest.mark.esp32 +@pytest.mark.esp32c3 +@pytest.mark.esp32s3 +@pytest.mark.esp32p4 +@pytest.mark.timeout(8000) +@pytest.mark.ESP_GMF_ELEMENT +@pytest.mark.ESP_GMF_TASK +@pytest.mark.ESP_GMF_IO +@pytest.mark.ESP_GMF_RINGBUF +@pytest.mark.ESP_GMF_PBUF +@pytest.mark.ESP_GMF_BLOCK +@pytest.mark.ELEMENT_POOL +@pytest.mark.ELEMENT_PORT +@pytest.mark.ESP_GMF_METHOD +@pytest.mark.parametrize( + 'config', + [ + 'default', + ], + indirect=True, +) +def test_gmf_core(dut: Dut) -> None: + dut.run_all_single_board_cases(timeout=2000) diff --git a/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/sdkconfig.defaults b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/sdkconfig.defaults new file mode 100644 index 0000000..c8d1298 --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/gmf_core/test_apps/sdkconfig.defaults @@ -0,0 +1,81 @@ +# General options for additional checks +CONFIG_COMPILER_WARN_WRITE_STRINGS=y +CONFIG_BOOTLOADER_LOG_LEVEL_WARN=y +CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK=y +CONFIG_COMPILER_STACK_CHECK_MODE_STRONG=y +CONFIG_COMPILER_STACK_CHECK=y +CONFIG_ESP_GDBSTUB_ENABLED=y + +# Serial flasher config +CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y +CONFIG_ESPTOOLPY_FLASHSIZE="4MB" + + +# +# Heap memory debugging +# +CONFIG_HEAP_POISONING_COMPREHENSIVE=y +CONFIG_HEAP_TRACING_STANDALONE=y +CONFIG_HEAP_TRACING=y +CONFIG_HEAP_TRACING_STACK_DEPTH=10 +CONFIG_HEAP_TASK_TRACKING=y +CONFIG_ESP_SYSTEM_PANIC_GDBSTUB=y + +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ=240 + + +# disable task watchdog since this app uses an interactive menu +CONFIG_ESP_TASK_WDT_INIT=n + +# FAT Filesystem support +CONFIG_FATFS_API_ENCODING_UTF_8=y +CONFIG_FATFS_SECTOR_4096=y +CONFIG_FATFS_CODEPAGE_936=y +CONFIG_FATFS_TIMEOUT_MS=10000 +CONFIG_FATFS_PER_FILE_CACHE=y +CONFIG_FATFS_MAX_LFN=255 +CONFIG_FATFS_FS_LOCK=10 + + +# some of the tests verify concurrent operation of FAT partitions in +# an SD card and in Flash, so need to use a custom partition table. +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" + + +# +# FreeRTOS Kernel +# +CONFIG_FREERTOS_ENABLE_BACKWARD_COMPATIBILITY=y +CONFIG_FREERTOS_USE_TRACE_FACILITY=y +CONFIG_FREERTOS_USE_STATS_FORMATTING_FUNCTIONS=y +CONFIG_FREERTOS_USE_LIST_DATA_INTEGRITY_CHECK_BYTES=y +CONFIG_FREERTOS_VTASKLIST_INCLUDE_COREID=y +CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS=y +CONFIG_FREERTOS_RUN_TIME_COUNTER_TYPE_U64=y + + +# +# Log output +# +CONFIG_LOG_DEFAULT_LEVEL_DEBUG=y +# CONFIG_LOG_DEFAULT_LEVEL_VERBOSE is not set +CONFIG_LOG_DEFAULT_LEVEL=4 +CONFIG_LOG_MAXIMUM_EQUALS_DEFAULT=y + + +# +# FAT Filesystem support +# +CONFIG_FATFS_VOLUME_COUNT=2 +# CONFIG_FATFS_LFN_NONE is not set +CONFIG_FATFS_LFN_HEAP=y +# CONFIG_FATFS_LFN_STACK is not set +# end of FAT Filesystem support + + +CONFIG_ESP_INT_WDT=y +CONFIG_ESP_INT_WDT_TIMEOUT_MS=400 +CONFIG_ESP_INT_WDT_CHECK_CPU1=y +CONFIG_ESP_TASK_WDT_EN=y diff --git a/firmware/echoear-platform-agent/components/esp-gmf/pytest.ini b/firmware/echoear-platform-agent/components/esp-gmf/pytest.ini new file mode 100644 index 0000000..069d1cf --- /dev/null +++ b/firmware/echoear-platform-agent/components/esp-gmf/pytest.ini @@ -0,0 +1,35 @@ +[pytest] +# only the files with prefix `pytest_` would be recognized as pytest test scripts. +python_files = pytest_*.py + +# ignore PytestExperimentalApiWarning for record_xml_attribute +# set traceback to "short" to prevent the overwhelming tracebacks +addopts = + -s -vv + --embedded-services esp,idf + --tb short + --strict-markers + --skip-check-coredump y + --logfile-extension ".txt" + --check-duplicates y + --ignore-glob */managed_components/* + --ignore pytest-embedded + +# ignore DeprecationWarning +filterwarnings = + ignore::DeprecationWarning:matplotlib.*: + ignore::DeprecationWarning:google.protobuf.*: + ignore::_pytest.warning_types.PytestExperimentalApiWarning + +# log related +log_cli = True +log_cli_level = INFO +log_cli_format = %(asctime)s %(levelname)s %(message)s +log_cli_date_format = %Y-%m-%d %H:%M:%S + +# junit related +junit_family = xunit1 + +## log all to `system-out` when case fail +junit_logging = stdout +junit_log_passing_tests = False diff --git a/firmware/echoear-platform-agent/components/gen_bmgr_codes/CMakeLists.txt b/firmware/echoear-platform-agent/components/gen_bmgr_codes/CMakeLists.txt new file mode 100644 index 0000000..27817e0 --- /dev/null +++ b/firmware/echoear-platform-agent/components/gen_bmgr_codes/CMakeLists.txt @@ -0,0 +1,12 @@ +# Board information output +message(STATUS "Selected Board: echoear_core_board_v1_2") +message(STATUS "Board Path: E:/Codex/story/platform/firmware/echoear-platform-agent/managed_components/espressif__esp_board_manager/boards/echoear_core_board_v1_2") + +idf_component_register( + SRC_DIRS "." "../../managed_components/espressif__esp_board_manager/boards/echoear_core_board_v1_2" + INCLUDE_DIRS "." "../../managed_components/espressif__esp_board_manager/boards/echoear_core_board_v1_2" + REQUIRES esp_board_manager +) + +# This is equivalent to adding WHOLE_ARCHIVE option to the idf_component_register call above: +idf_component_set_property(${COMPONENT_NAME} WHOLE_ARCHIVE TRUE) diff --git a/firmware/echoear-platform-agent/components/gen_bmgr_codes/gen_board_device_config.c b/firmware/echoear-platform-agent/components/gen_bmgr_codes/gen_board_device_config.c new file mode 100644 index 0000000..30f1ce5 --- /dev/null +++ b/firmware/echoear-platform-agent/components/gen_bmgr_codes/gen_board_device_config.c @@ -0,0 +1,299 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * Auto-generated device configuration file + * DO NOT MODIFY THIS FILE MANUALLY + * + * See LICENSE file for details. + */ + +#include +#include "esp_board_device.h" +#include "dev_audio_codec.h" +#include "dev_display_lcd_spi.h" +#include "dev_fatfs_sdcard.h" +#include "dev_gpio_ctrl.h" +#include "dev_lcd_touch_i2c.h" +#include "dev_ledc_ctrl.h" +#include "driver/sdmmc_host.h" +#include "driver/sdmmc_types.h" + +// Device configuration structures +const static dev_gpio_ctrl_config_t esp_bmgr_audio_power_cfg = { + .name = "audio_power", + .type = "gpio_ctrl", + .gpio_name = "gpio_power_audio", + .active_level = 1, + .default_level = 0, +}; + +const static dev_gpio_ctrl_config_t esp_bmgr_lcd_power_cfg = { + .name = "lcd_power", + .type = "gpio_ctrl", + .gpio_name = "gpio_power_lcd", + .active_level = 0, + .default_level = 1, +}; + +const static dev_audio_codec_config_t esp_bmgr_audio_dac_cfg = { + .name = "audio_dac", + .chip = "es8311", + .type = "audio_codec", + .adc_enabled = false, + .adc_max_channel = 0, + .adc_channel_mask = 0x3, + .adc_channel_labels = "", + .adc_init_gain = 0, + .dac_enabled = true, + .dac_max_channel = 1, + .dac_channel_mask = 0x1, + .dac_init_gain = 0, + .pa_cfg = { + .name = "gpio_pa_control", + .port = 15, + .active_level = 1, + .gain = 6.0, + }, + .i2c_cfg = { + .name = "i2c_master", + .port = 0, + .address = 48, + .frequency = 400000, + }, + .i2s_cfg = { + .name = "i2s_audio_out", + .port = 0, + }, + .metadata = NULL, + .metadata_size = 0, + .mclk_enabled = true, + .aec_enabled = false, + .eq_enabled = false, + .alc_enabled = false, +}; + +const static dev_audio_codec_config_t esp_bmgr_audio_adc_cfg = { + .name = "audio_adc", + .chip = "es7210", + .type = "audio_codec", + .adc_enabled = true, + .adc_max_channel = 4, + .adc_channel_mask = 0x7, + .adc_channel_labels = "NA,RE,FR,FL", + .adc_init_gain = 0, + .dac_enabled = false, + .dac_max_channel = 0, + .dac_channel_mask = 0x0, + .dac_init_gain = 0, + .pa_cfg = { + .name = "none", + .port = -1, + .active_level = 0, + .gain = 0.0, + }, + .i2c_cfg = { + .name = "i2c_master", + .port = 0, + .address = 128, + .frequency = 400000, + }, + .i2s_cfg = { + .name = "i2s_audio_in", + .port = 0, + }, + .metadata = NULL, + .metadata_size = 0, + .mclk_enabled = false, + .aec_enabled = false, + .eq_enabled = false, + .alc_enabled = false, +}; + +const static dev_fatfs_sdcard_config_t esp_bmgr_fs_sdcard_cfg = { + .name = "fs_sdcard", + .mount_point = "/sdcard", + .vfs_config = { + .format_if_mount_failed = true, + .max_files = 5, + .allocation_unit_size = 16384, + }, + .frequency = SDMMC_FREQ_HIGHSPEED, + .slot = SDMMC_HOST_SLOT_1, + .bus_width = 1, + .slot_flags = SDMMC_SLOT_FLAG_INTERNAL_PULLUP, + .pins = { + .clk = 16, + .cmd = 38, + .d0 = 17, + .d1 = -1, + .d2 = -1, + .d3 = -1, + .d4 = -1, + .d5 = -1, + .d6 = -1, + .d7 = -1, + .cd = -1, + .wp = -1, + }, + .ldo_chan_id = -1, +}; + +const static dev_display_lcd_spi_config_t esp_bmgr_display_lcd_cfg = { + .name = "display_lcd", + .chip = "st77916", + .type = "display_lcd_spi", + .spi_name = "spi_display", + .io_spi_config = { + .cs_gpio_num = 14, + .dc_gpio_num = 45, + .spi_mode = 0, + .pclk_hz = 20000000, + .trans_queue_depth = 1, + .on_color_trans_done = NULL, + .user_ctx = NULL, + .lcd_cmd_bits = 32, + .lcd_param_bits = 8, + .cs_ena_pretrans = 0, + .cs_ena_posttrans = 0, + .flags = { + .dc_high_on_cmd = false, + .dc_low_on_data = false, + .dc_low_on_param = false, + .octal_mode = false, + .quad_mode = true, + .sio_mode = false, + .lsb_first = false, + .cs_high_active = false, + }, + }, + .panel_config = { + .reset_gpio_num = 47, + .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB, + .data_endian = LCD_RGB_DATA_ENDIAN_BIG, + .bits_per_pixel = 16, + .flags = { + .reset_active_high = true, + }, + .vendor_config = NULL, + }, + .swap_xy = false, + .mirror_x = false, + .mirror_y = false, + .x_max = 360, + .y_max = 360, +}; + +const static dev_lcd_touch_i2c_config_t esp_bmgr_lcd_touch_cfg = { + .name = "lcd_touch", + .chip = "cst816s", + .type = "lcd_touch_i2c", + .i2c_name = "i2c_master", + .i2c_addr = {0x15, 0x00}, + .io_i2c_config = { + .dev_addr = 21, + .control_phase_bytes = 1, + .dc_bit_offset = 0, + .lcd_cmd_bits = 8, + .lcd_param_bits = 0, + .scl_speed_hz = 100000, + .flags = { + .dc_low_on_data = false, + .disable_control_phase = true, + }, + }, + .touch_config = { + .x_max = 360, + .y_max = 360, + .rst_gpio_num = -1, + .int_gpio_num = 10, + .levels = { + .reset = 0, + .interrupt = 0, + }, + .flags = { + .swap_xy = false, + .mirror_x = false, + .mirror_y = false, + }, + .process_coordinates = NULL, + .interrupt_callback = NULL, + .user_data = NULL, + .driver_data = NULL, + }, +}; + +const static dev_ledc_ctrl_config_t esp_bmgr_lcd_brightness_cfg = { + .name = "lcd_brightness", + .type = "ledc_ctrl", + .ledc_name = "ledc_backlight", + .default_percent = 100, +}; + +// Device descriptor array +const esp_board_device_desc_t g_esp_board_devices[] = { + { + .next = &g_esp_board_devices[1], + .name = "audio_power", + .type = "gpio_ctrl", + .cfg = &esp_bmgr_audio_power_cfg, + .cfg_size = sizeof(esp_bmgr_audio_power_cfg), + .init_skip = false, + }, + { + .next = &g_esp_board_devices[2], + .name = "lcd_power", + .type = "gpio_ctrl", + .cfg = &esp_bmgr_lcd_power_cfg, + .cfg_size = sizeof(esp_bmgr_lcd_power_cfg), + .init_skip = false, + }, + { + .next = &g_esp_board_devices[3], + .name = "audio_dac", + .type = "audio_codec", + .cfg = &esp_bmgr_audio_dac_cfg, + .cfg_size = sizeof(esp_bmgr_audio_dac_cfg), + .init_skip = false, + }, + { + .next = &g_esp_board_devices[4], + .name = "audio_adc", + .type = "audio_codec", + .cfg = &esp_bmgr_audio_adc_cfg, + .cfg_size = sizeof(esp_bmgr_audio_adc_cfg), + .init_skip = false, + }, + { + .next = &g_esp_board_devices[5], + .name = "fs_sdcard", + .type = "fatfs_sdcard", + .cfg = &esp_bmgr_fs_sdcard_cfg, + .cfg_size = sizeof(esp_bmgr_fs_sdcard_cfg), + .init_skip = false, + }, + { + .next = &g_esp_board_devices[6], + .name = "display_lcd", + .type = "display_lcd_spi", + .cfg = &esp_bmgr_display_lcd_cfg, + .cfg_size = sizeof(esp_bmgr_display_lcd_cfg), + .init_skip = false, + }, + { + .next = &g_esp_board_devices[7], + .name = "lcd_touch", + .type = "lcd_touch_i2c", + .cfg = &esp_bmgr_lcd_touch_cfg, + .cfg_size = sizeof(esp_bmgr_lcd_touch_cfg), + .init_skip = false, + }, + { + .next = NULL, + .name = "lcd_brightness", + .type = "ledc_ctrl", + .cfg = &esp_bmgr_lcd_brightness_cfg, + .cfg_size = sizeof(esp_bmgr_lcd_brightness_cfg), + .init_skip = false, + }, +}; diff --git a/firmware/echoear-platform-agent/components/gen_bmgr_codes/gen_board_device_custom.h b/firmware/echoear-platform-agent/components/gen_bmgr_codes/gen_board_device_custom.h new file mode 100644 index 0000000..da3056a --- /dev/null +++ b/firmware/echoear-platform-agent/components/gen_bmgr_codes/gen_board_device_custom.h @@ -0,0 +1,13 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * Auto-generated custom device structure definitions + * DO NOT MODIFY THIS FILE MANUALLY + * + * See LICENSE file for details. + */ + +#pragma once + +// No custom device structures defined diff --git a/firmware/echoear-platform-agent/components/gen_bmgr_codes/gen_board_device_handles.c b/firmware/echoear-platform-agent/components/gen_bmgr_codes/gen_board_device_handles.c new file mode 100644 index 0000000..f502cad --- /dev/null +++ b/firmware/echoear-platform-agent/components/gen_bmgr_codes/gen_board_device_handles.c @@ -0,0 +1,86 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * Auto-generated device handle definition file + * DO NOT MODIFY THIS FILE MANUALLY + * + * See LICENSE file for details. + */ + +#include +#include "esp_board_device.h" +#include "dev_audio_codec.h" +#include "dev_display_lcd_spi.h" +#include "dev_fatfs_sdcard.h" +#include "dev_gpio_ctrl.h" +#include "dev_lcd_touch_i2c.h" +#include "dev_ledc_ctrl.h" + +// Device handle array +esp_board_device_handle_t g_esp_board_device_handles[] = { + { + .next = &g_esp_board_device_handles[1], + .name = "audio_power", + .type = "gpio_ctrl", + .device_handle = NULL, + .init = dev_gpio_ctrl_init, + .deinit = dev_gpio_ctrl_deinit + }, + { + .next = &g_esp_board_device_handles[2], + .name = "lcd_power", + .type = "gpio_ctrl", + .device_handle = NULL, + .init = dev_gpio_ctrl_init, + .deinit = dev_gpio_ctrl_deinit + }, + { + .next = &g_esp_board_device_handles[3], + .name = "audio_dac", + .type = "audio_codec", + .device_handle = NULL, + .init = dev_audio_codec_init, + .deinit = dev_audio_codec_deinit + }, + { + .next = &g_esp_board_device_handles[4], + .name = "audio_adc", + .type = "audio_codec", + .device_handle = NULL, + .init = dev_audio_codec_init, + .deinit = dev_audio_codec_deinit + }, + { + .next = &g_esp_board_device_handles[5], + .name = "fs_sdcard", + .type = "fatfs_sdcard", + .device_handle = NULL, + .init = dev_fatfs_sdcard_init, + .deinit = dev_fatfs_sdcard_deinit + }, + { + .next = &g_esp_board_device_handles[6], + .name = "display_lcd", + .type = "display_lcd_spi", + .device_handle = NULL, + .init = dev_display_lcd_spi_init, + .deinit = dev_display_lcd_spi_deinit + }, + { + .next = &g_esp_board_device_handles[7], + .name = "lcd_touch", + .type = "lcd_touch_i2c", + .device_handle = NULL, + .init = dev_lcd_touch_i2c_init, + .deinit = dev_lcd_touch_i2c_deinit + }, + { + .next = NULL, + .name = "lcd_brightness", + .type = "ledc_ctrl", + .device_handle = NULL, + .init = dev_ledc_ctrl_init, + .deinit = dev_ledc_ctrl_deinit + }, +}; diff --git a/firmware/echoear-platform-agent/components/gen_bmgr_codes/gen_board_info.c b/firmware/echoear-platform-agent/components/gen_bmgr_codes/gen_board_info.c new file mode 100644 index 0000000..fa3f7e2 --- /dev/null +++ b/firmware/echoear-platform-agent/components/gen_bmgr_codes/gen_board_info.c @@ -0,0 +1,20 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * Auto-generated board information file + * DO NOT MODIFY THIS FILE MANUALLY + * + * See LICENSE file for details. + */ + +#include "esp_board_manager.h" + +// Board information +const esp_board_info_t g_esp_board_info = { + .name = "echoear_core_board_v1_2", + .chip = "esp32s3", + .version = "1.2.0", + .description = "EchoEar Core Board V1.2- AI Pet Companion Development Board", + .manufacturer = "ESPRESSIF", +}; diff --git a/firmware/echoear-platform-agent/components/gen_bmgr_codes/gen_board_periph_config.c b/firmware/echoear-platform-agent/components/gen_bmgr_codes/gen_board_periph_config.c new file mode 100644 index 0000000..ccfc65b --- /dev/null +++ b/firmware/echoear-platform-agent/components/gen_bmgr_codes/gen_board_periph_config.c @@ -0,0 +1,266 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * Auto-generated peripheral configuration file + * DO NOT MODIFY THIS FILE MANUALLY + * + * See LICENSE file for details. + */ + +#include +#include "esp_board_periph.h" +#include "driver/i2c_master.h" +#include "driver/i2c_types.h" +#include "hal/gpio_types.h" +#include "periph_gpio.h" +#include "periph_i2s.h" +#include "periph_ledc.h" +#include "periph_spi.h" + +// Peripheral configuration structures +const static i2c_master_bus_config_t esp_bmgr_i2c_master_cfg = { + .i2c_port = I2C_NUM_0, + .sda_io_num = 2, + .scl_io_num = 1, + .clk_source = I2C_CLK_SRC_DEFAULT, + .glitch_ignore_cnt = 7, + .intr_priority = 1, + .trans_queue_depth = 0, + .flags = { + .enable_internal_pullup = true, + }, +}; + +const static periph_i2s_config_t esp_bmgr_i2s_audio_out_cfg = { + .port = I2S_NUM_0, + .role = I2S_ROLE_MASTER, + .mode = I2S_COMM_MODE_STD, + .direction = I2S_DIR_TX, + .i2s_cfg = { + .std = { + .clk_cfg = { + .sample_rate_hz = 48000, + .clk_src = I2S_CLK_SRC_DEFAULT, + .ext_clk_freq_hz = 0, + .mclk_multiple = I2S_MCLK_MULTIPLE_256, + }, + .slot_cfg = { + .data_bit_width = I2S_DATA_BIT_WIDTH_16BIT, + .slot_bit_width = I2S_SLOT_BIT_WIDTH_AUTO, + .slot_mode = I2S_SLOT_MODE_STEREO, + .slot_mask = I2S_STD_SLOT_BOTH, + .ws_width = 16, + .ws_pol = false, + .bit_shift = true, + .left_align = true, + .big_endian = false, + .bit_order_lsb = false, + }, + .gpio_cfg = { + .mclk = 42, + .bclk = 40, + .ws = 39, + .dout = 41, + .din = 3, + .invert_flags = { + .mclk_inv = false, + .bclk_inv = false, + .ws_inv = false, + }, + }, + }, + }, +}; + +const static periph_i2s_config_t esp_bmgr_i2s_audio_in_cfg = { + .port = I2S_NUM_0, + .role = I2S_ROLE_MASTER, + .mode = I2S_COMM_MODE_STD, + .direction = I2S_DIR_RX, + .i2s_cfg = { + .std = { + .clk_cfg = { + .sample_rate_hz = 48000, + .clk_src = I2S_CLK_SRC_DEFAULT, + .ext_clk_freq_hz = 0, + .mclk_multiple = I2S_MCLK_MULTIPLE_256, + }, + .slot_cfg = { + .data_bit_width = I2S_DATA_BIT_WIDTH_16BIT, + .slot_bit_width = I2S_SLOT_BIT_WIDTH_AUTO, + .slot_mode = I2S_SLOT_MODE_STEREO, + .slot_mask = I2S_STD_SLOT_BOTH, + .ws_width = 16, + .ws_pol = false, + .bit_shift = true, + .left_align = true, + .big_endian = false, + .bit_order_lsb = false, + }, + .gpio_cfg = { + .mclk = 42, + .bclk = 40, + .ws = 39, + .dout = 41, + .din = 3, + .invert_flags = { + .mclk_inv = false, + .bclk_inv = false, + .ws_inv = false, + }, + }, + }, + }, +}; + +static periph_spi_config_t esp_bmgr_spi_display_cfg = { + .spi_port = SPI2_HOST, + .spi_bus_config = { + .mosi_io_num = 46, + .miso_io_num = 13, + .sclk_io_num = 18, + .quadwp_io_num = 11, + .quadhd_io_num = 12, + .data4_io_num = -1, + .data5_io_num = -1, + .data6_io_num = -1, + .data7_io_num = -1, + .data_io_default_level = false, + .max_transfer_sz = 72000, + .flags = 0, + .isr_cpu_id = ESP_INTR_CPU_AFFINITY_AUTO, + .intr_flags = 0, + }, +}; + +const static periph_ledc_config_t esp_bmgr_ledc_backlight_cfg = { + .handle = { + .channel = LEDC_CHANNEL_0, + .speed_mode = LEDC_LOW_SPEED_MODE, + }, + .gpio_num = 44, + .duty = 2048, + .freq_hz = 2000, + .duty_resolution = LEDC_TIMER_10_BIT, + .timer_sel = LEDC_TIMER_0, + .sleep_mode = LEDC_SLEEP_MODE_NO_ALIVE_NO_PD, + .output_invert = false, +}; + +const static periph_gpio_config_t esp_bmgr_gpio_power_lcd_cfg = { + .gpio_config = { + .pin_bit_mask = BIT64(9), + .mode = GPIO_MODE_OUTPUT, + .pull_up_en = GPIO_PULLUP_DISABLE, + .pull_down_en = GPIO_PULLDOWN_DISABLE, + .intr_type = GPIO_INTR_DISABLE, + }, + .default_level = 0, +}; + +const static periph_gpio_config_t esp_bmgr_gpio_power_audio_cfg = { + .gpio_config = { + .pin_bit_mask = BIT64(48), + .mode = GPIO_MODE_OUTPUT, + .pull_up_en = GPIO_PULLUP_DISABLE, + .pull_down_en = GPIO_PULLDOWN_DISABLE, + .intr_type = GPIO_INTR_DISABLE, + }, + .default_level = 1, +}; + +const static periph_gpio_config_t esp_bmgr_gpio_pa_control_cfg = { + .gpio_config = { + .pin_bit_mask = BIT64(15), + .mode = GPIO_MODE_OUTPUT, + .pull_up_en = GPIO_PULLUP_DISABLE, + .pull_down_en = GPIO_PULLDOWN_DISABLE, + .intr_type = GPIO_INTR_DISABLE, + }, + .default_level = 0, +}; + +// Peripheral descriptor array +const esp_board_periph_desc_t g_esp_board_peripherals[] = { + { + .next = &g_esp_board_peripherals[1], + .name = "i2c_master", + .type = "i2c", + .format = NULL, + .role = "master", + .cfg = &esp_bmgr_i2c_master_cfg, + .cfg_size = sizeof(esp_bmgr_i2c_master_cfg), + .id = 0, + }, + { + .next = &g_esp_board_peripherals[2], + .name = "i2s_audio_out", + .type = "i2s", + .format = "std-out", + .role = "master", + .cfg = &esp_bmgr_i2s_audio_out_cfg, + .cfg_size = sizeof(esp_bmgr_i2s_audio_out_cfg), + .id = 0, + }, + { + .next = &g_esp_board_peripherals[3], + .name = "i2s_audio_in", + .type = "i2s", + .format = "std-in", + .role = "slave", + .cfg = &esp_bmgr_i2s_audio_in_cfg, + .cfg_size = sizeof(esp_bmgr_i2s_audio_in_cfg), + .id = 0, + }, + { + .next = &g_esp_board_peripherals[4], + .name = "spi_display", + .type = "spi", + .format = NULL, + .role = "master", + .cfg = &esp_bmgr_spi_display_cfg, + .cfg_size = sizeof(esp_bmgr_spi_display_cfg), + .id = 0, + }, + { + .next = &g_esp_board_peripherals[5], + .name = "ledc_backlight", + .type = "ledc", + .format = NULL, + .role = "none", + .cfg = &esp_bmgr_ledc_backlight_cfg, + .cfg_size = sizeof(esp_bmgr_ledc_backlight_cfg), + .id = 0, + }, + { + .next = &g_esp_board_peripherals[6], + .name = "gpio_power_lcd", + .type = "gpio", + .format = NULL, + .role = "none", + .cfg = &esp_bmgr_gpio_power_lcd_cfg, + .cfg_size = sizeof(esp_bmgr_gpio_power_lcd_cfg), + .id = 0, + }, + { + .next = &g_esp_board_peripherals[7], + .name = "gpio_power_audio", + .type = "gpio", + .format = NULL, + .role = "none", + .cfg = &esp_bmgr_gpio_power_audio_cfg, + .cfg_size = sizeof(esp_bmgr_gpio_power_audio_cfg), + .id = 0, + }, + { + .next = NULL, + .name = "gpio_pa_control", + .type = "gpio", + .format = NULL, + .role = "none", + .cfg = &esp_bmgr_gpio_pa_control_cfg, + .cfg_size = sizeof(esp_bmgr_gpio_pa_control_cfg), + .id = 0, + }, +}; diff --git a/firmware/echoear-platform-agent/components/gen_bmgr_codes/gen_board_periph_handles.c b/firmware/echoear-platform-agent/components/gen_bmgr_codes/gen_board_periph_handles.c new file mode 100644 index 0000000..282bfd6 --- /dev/null +++ b/firmware/echoear-platform-agent/components/gen_bmgr_codes/gen_board_periph_handles.c @@ -0,0 +1,64 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD + * SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT + * + * Auto-generated peripheral handle definition file + * DO NOT MODIFY THIS FILE MANUALLY + * + * See LICENSE file for details. + */ + +#include +#include "esp_board_periph.h" +#include "periph_gpio.h" +#include "periph_i2c.h" +#include "periph_i2s.h" +#include "periph_i2s.h" +#include "periph_ledc.h" +#include "periph_spi.h" + +// Peripheral handle array +esp_board_periph_entry_t g_esp_board_periph_handles[] = { + { + .next = &g_esp_board_periph_handles[1], + .type = "gpio", + .role = "none", + .init = periph_gpio_init, + .deinit = periph_gpio_deinit + }, + { + .next = &g_esp_board_periph_handles[2], + .type = "i2c", + .role = "master", + .init = periph_i2c_init, + .deinit = periph_i2c_deinit + }, + { + .next = &g_esp_board_periph_handles[3], + .type = "i2s", + .role = "master", + .init = periph_i2s_init, + .deinit = periph_i2s_deinit + }, + { + .next = &g_esp_board_periph_handles[4], + .type = "i2s", + .role = "slave", + .init = periph_i2s_init, + .deinit = periph_i2s_deinit + }, + { + .next = &g_esp_board_periph_handles[5], + .type = "ledc", + .role = "none", + .init = periph_ledc_init, + .deinit = periph_ledc_deinit + }, + { + .next = NULL, + .type = "spi", + .role = "master", + .init = periph_spi_init, + .deinit = periph_spi_deinit + }, +}; diff --git a/firmware/echoear-platform-agent/components/gen_bmgr_codes/idf_component.yml b/firmware/echoear-platform-agent/components/gen_bmgr_codes/idf_component.yml new file mode 100644 index 0000000..fb7a530 --- /dev/null +++ b/firmware/echoear-platform-agent/components/gen_bmgr_codes/idf_component.yml @@ -0,0 +1,3 @@ +dependencies: + espressif/esp_lcd_st77916: '*' + espressif/esp_lcd_touch_cst816s: '*' diff --git a/firmware/echoear-platform-agent/components/hw_init/CMakeLists.txt b/firmware/echoear-platform-agent/components/hw_init/CMakeLists.txt new file mode 100644 index 0000000..288b7a7 --- /dev/null +++ b/firmware/echoear-platform-agent/components/hw_init/CMakeLists.txt @@ -0,0 +1,76 @@ +set(srcs + "hw_init.c" + "input/touch_init.c" + "input/touch_rotation_helper.c" + "input/knob_init.c" + "helpers/lcd_orientation_helper.c" +) + +if(CONFIG_EXAMPLE_LCD_INTERFACE_MIPI_DSI) + list(APPEND srcs "lcd/lcd_init_mipi_dsi.c") +elseif(CONFIG_EXAMPLE_LCD_INTERFACE_QSPI) + list(APPEND srcs "lcd/lcd_init_qspi.c") +elseif(CONFIG_EXAMPLE_LCD_INTERFACE_RGB) + list(APPEND srcs "lcd/lcd_init_rgb.c") +elseif(CONFIG_EXAMPLE_LCD_INTERFACE_SPI_WITH_PSRAM) + list(APPEND srcs "lcd/lcd_init_spi_with_psram.c") +elseif(CONFIG_EXAMPLE_LCD_INTERFACE_SPI_WITHOUT_PSRAM) + list(APPEND srcs "lcd/lcd_init_spi_without_psram.c") +else() + list(APPEND srcs "lcd/lcd_init_spi_with_psram.c") +endif() + +set(include_dirs "." "lcd" "input" "helpers") + +set(component_requires driver;esp_lcd;esp_lvgl_adapter) + +if(CONFIG_EXAMPLE_LCD_INTERFACE_MIPI_DSI) + list(APPEND component_requires esp_lcd_ek79007) + list(APPEND component_requires espressif__esp_lcd_touch_gt911) +endif() + +if(CONFIG_EXAMPLE_LCD_INTERFACE_QSPI) + list(APPEND component_requires esp_lcd_st77916) + list(APPEND component_requires espressif__esp_lcd_touch_cst816s) +endif() + +if(CONFIG_EXAMPLE_LCD_INTERFACE_RGB) + list(APPEND component_requires espressif__esp_lcd_touch_gt1151) +endif() + +if(CONFIG_EXAMPLE_LCD_INTERFACE_SPI_WITH_PSRAM) + list(APPEND component_requires espressif__esp_lcd_ili9341) + list(APPEND component_requires espressif__esp_lcd_touch_gt911) +endif() + +if(CONFIG_EXAMPLE_LCD_INTERFACE_SPI_WITHOUT_PSRAM) + list(APPEND component_requires espressif__esp_lcd_gc9a01) + if("espressif__knob" IN_LIST build_components) + list(APPEND component_requires espressif__knob) + elseif("knob" IN_LIST build_components) + list(APPEND component_requires knob) + endif() + if("espressif__button" IN_LIST build_components) + list(APPEND component_requires espressif__button) + elseif("button" IN_LIST build_components) + list(APPEND component_requires button) + endif() +endif() + +idf_component_register( + SRCS ${srcs} + INCLUDE_DIRS ${include_dirs} + REQUIRES ${component_requires} +) + +if(CONFIG_EXAMPLE_LCD_INTERFACE_MIPI_DSI) + message(STATUS "hw_init: MIPI DSI + Touch") +elseif(CONFIG_EXAMPLE_LCD_INTERFACE_QSPI) + message(STATUS "hw_init: QSPI + Touch") +elseif(CONFIG_EXAMPLE_LCD_INTERFACE_RGB) + message(STATUS "hw_init: RGB + Touch") +elseif(CONFIG_EXAMPLE_LCD_INTERFACE_SPI_WITH_PSRAM) + message(STATUS "hw_init: SPI (PSRAM) + Touch") +elseif(CONFIG_EXAMPLE_LCD_INTERFACE_SPI_WITHOUT_PSRAM) + message(STATUS "hw_init: SPI (No PSRAM) + Encoder") +endif() diff --git a/firmware/echoear-platform-agent/components/hw_init/Kconfig.projbuild b/firmware/echoear-platform-agent/components/hw_init/Kconfig.projbuild new file mode 100644 index 0000000..023e807 --- /dev/null +++ b/firmware/echoear-platform-agent/components/hw_init/Kconfig.projbuild @@ -0,0 +1,79 @@ +menu "Hardware Configuration" + +choice EXAMPLE_LCD_INTERFACE + prompt "LCD Interface Type" + default EXAMPLE_LCD_INTERFACE_MIPI_DSI if IDF_TARGET_ESP32P4 + default EXAMPLE_LCD_INTERFACE_SPI_WITH_PSRAM if IDF_TARGET_ESP32S3 + default EXAMPLE_LCD_INTERFACE_SPI_WITHOUT_PSRAM if IDF_TARGET_ESP32C3 + default EXAMPLE_LCD_INTERFACE_SPI_WITH_PSRAM + help + Select the LCD interface type for your display. + Resolution and input device will be set automatically based on the interface. + + config EXAMPLE_LCD_INTERFACE_MIPI_DSI + bool "MIPI DSI Interface (1024x600, Touch)" + depends on IDF_TARGET_ESP32P4 + help + MIPI DSI interface for high-resolution displays. + Resolution: 1024x600 + Input: Touch panel + Only available on ESP32-P4. + + config EXAMPLE_LCD_INTERFACE_QSPI + bool "QSPI Interface (360x360, Touch)" + depends on !IDF_TARGET_ESP32C3 + help + QSPI (Quad SPI) interface for medium-resolution displays. + Resolution: 360x360 + Input: Touch panel + Requires PSRAM. Not available on ESP32-C3. + + config EXAMPLE_LCD_INTERFACE_RGB + bool "RGB Interface (800x480, Touch)" + depends on IDF_TARGET_ESP32S3 || IDF_TARGET_ESP32P4 + help + RGB parallel interface for medium to high-resolution displays. + Resolution: 800x480 + Input: Touch panel + Requires PSRAM. Available on ESP32-S3 and ESP32-P4. + + config EXAMPLE_LCD_INTERFACE_SPI_WITH_PSRAM + bool "SPI Interface with PSRAM (320x240, Touch)" + depends on !IDF_TARGET_ESP32C3 + help + Standard SPI interface with PSRAM support. + Resolution: 320x240 + Input: Touch panel + Not available on ESP32-C3. + + config EXAMPLE_LCD_INTERFACE_SPI_WITHOUT_PSRAM + bool "SPI Interface without PSRAM (240x240, Encoder)" + help + Standard SPI interface without PSRAM. + Resolution: 240x240 + Input: Encoder/Knob + Suitable for resource-constrained chips. +endchoice + +choice EXAMPLE_DISPLAY_ROTATION + prompt "Display Rotation" + default EXAMPLE_DISPLAY_ROTATION_0 + help + Select the rotation applied to both the LCD panel and the touch controller + so that LVGL coordinates match the screen orientation. + +config EXAMPLE_DISPLAY_ROTATION_0 + bool "Rotate 0 degrees" + +config EXAMPLE_DISPLAY_ROTATION_90 + bool "Rotate 90 degrees clockwise" + +config EXAMPLE_DISPLAY_ROTATION_180 + bool "Rotate 180 degrees" + +config EXAMPLE_DISPLAY_ROTATION_270 + bool "Rotate 270 degrees clockwise" + +endchoice + +endmenu diff --git a/firmware/echoear-platform-agent/components/hw_init/helpers/lcd_orientation_helper.c b/firmware/echoear-platform-agent/components/hw_init/helpers/lcd_orientation_helper.c new file mode 100644 index 0000000..21e9883 --- /dev/null +++ b/firmware/echoear-platform-agent/components/hw_init/helpers/lcd_orientation_helper.c @@ -0,0 +1,116 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "lcd_orientation_helper.h" + +typedef struct { + int m00; + int m01; + int m10; + int m11; +} lcd_orientation_matrix_t; + +static lcd_orientation_matrix_t matrix_from_flags(bool swap_xy, bool mirror_x, bool mirror_y) +{ + int axis_x_x = 1; + int axis_x_y = 0; + int axis_y_x = 0; + int axis_y_y = 1; + + if (swap_xy) { + int tmp_x = axis_x_x; + int tmp_y = axis_x_y; + axis_x_x = axis_y_x; + axis_x_y = axis_y_y; + axis_y_x = tmp_x; + axis_y_y = tmp_y; + } + + if (mirror_x) { + axis_x_x = -axis_x_x; + axis_x_y = -axis_x_y; + } + if (mirror_y) { + axis_y_x = -axis_y_x; + axis_y_y = -axis_y_y; + } + + lcd_orientation_matrix_t matrix = { + .m00 = axis_x_x, + .m01 = axis_y_x, + .m10 = axis_x_y, + .m11 = axis_y_y, + }; + return matrix; +} + +static lcd_orientation_matrix_t matrix_multiply(lcd_orientation_matrix_t lhs, lcd_orientation_matrix_t rhs) +{ + lcd_orientation_matrix_t result = { + .m00 = lhs.m00 * rhs.m00 + lhs.m01 * rhs.m10, + .m01 = lhs.m00 * rhs.m01 + lhs.m01 * rhs.m11, + .m10 = lhs.m10 * rhs.m00 + lhs.m11 * rhs.m10, + .m11 = lhs.m10 * rhs.m01 + lhs.m11 * rhs.m11, + }; + return result; +} + +static lcd_orientation_matrix_t matrix_from_rotation(esp_lv_adapter_rotation_t rotation) +{ + switch (rotation) { + case ESP_LV_ADAPTER_ROTATE_90: + return matrix_from_flags(true, true, false); + case ESP_LV_ADAPTER_ROTATE_180: + return matrix_from_flags(false, true, true); + case ESP_LV_ADAPTER_ROTATE_270: + return matrix_from_flags(true, false, true); + case ESP_LV_ADAPTER_ROTATE_0: + default: + return matrix_from_flags(false, false, false); + } +} + +void lcd_get_orientation_flags(bool base_swap_xy, bool base_mirror_x, bool base_mirror_y, + esp_lv_adapter_rotation_t rotation, + bool *swap_xy, bool *mirror_x, bool *mirror_y) +{ + lcd_orientation_matrix_t base = matrix_from_flags(base_swap_xy, base_mirror_x, base_mirror_y); + lcd_orientation_matrix_t rotation_matrix = matrix_from_rotation(rotation); + lcd_orientation_matrix_t desired = matrix_multiply(rotation_matrix, base); + + for (int test_swap = 0; test_swap <= 1; ++test_swap) { + for (int test_mirror_x = 0; test_mirror_x <= 1; ++test_mirror_x) { + for (int test_mirror_y = 0; test_mirror_y <= 1; ++test_mirror_y) { + lcd_orientation_matrix_t candidate = matrix_from_flags(test_swap, test_mirror_x, test_mirror_y); + if (candidate.m00 == desired.m00 && + candidate.m01 == desired.m01 && + candidate.m10 == desired.m10 && + candidate.m11 == desired.m11) { + if (swap_xy) { + *swap_xy = (bool)test_swap; + } + if (mirror_x) { + *mirror_x = (bool)test_mirror_x; + } + if (mirror_y) { + *mirror_y = (bool)test_mirror_y; + } + return; + } + } + } + } + + if (swap_xy) { + *swap_xy = base_swap_xy; + } + if (mirror_x) { + *mirror_x = base_mirror_x; + } + if (mirror_y) { + *mirror_y = base_mirror_y; + } +} diff --git a/firmware/echoear-platform-agent/components/hw_init/helpers/lcd_orientation_helper.h b/firmware/echoear-platform-agent/components/hw_init/helpers/lcd_orientation_helper.h new file mode 100644 index 0000000..baa16f5 --- /dev/null +++ b/firmware/echoear-platform-agent/components/hw_init/helpers/lcd_orientation_helper.h @@ -0,0 +1,22 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include "esp_lv_adapter_display.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void lcd_get_orientation_flags(bool base_swap_xy, bool base_mirror_x, bool base_mirror_y, + esp_lv_adapter_rotation_t rotation, + bool *swap_xy, bool *mirror_x, bool *mirror_y); + +#ifdef __cplusplus +} +#endif diff --git a/firmware/echoear-platform-agent/components/hw_init/hw_init.c b/firmware/echoear-platform-agent/components/hw_init/hw_init.c new file mode 100644 index 0000000..8c4f67f --- /dev/null +++ b/firmware/echoear-platform-agent/components/hw_init/hw_init.c @@ -0,0 +1,18 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "hw_init.h" +#if !CONFIG_EXAMPLE_LCD_INTERFACE_MIPI_DSI && \ + !CONFIG_EXAMPLE_LCD_INTERFACE_QSPI && \ + !CONFIG_EXAMPLE_LCD_INTERFACE_RGB && \ + !CONFIG_EXAMPLE_LCD_INTERFACE_SPI_WITH_PSRAM && \ + !CONFIG_EXAMPLE_LCD_INTERFACE_SPI_WITHOUT_PSRAM +#error "No LCD interface selected! Please select one in menuconfig." +#endif + +#if !HW_USE_TOUCH && !HW_USE_ENCODER +#error "No input device configured!" +#endif diff --git a/firmware/echoear-platform-agent/components/hw_init/hw_init.h b/firmware/echoear-platform-agent/components/hw_init/hw_init.h new file mode 100644 index 0000000..da60b1e --- /dev/null +++ b/firmware/echoear-platform-agent/components/hw_init/hw_init.h @@ -0,0 +1,75 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "esp_log.h" +#include "esp_lcd_panel_ops.h" +#include "esp_lcd_panel_io.h" +#include "esp_lv_adapter_display.h" +#include "sdkconfig.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if CONFIG_EXAMPLE_LCD_INTERFACE_MIPI_DSI +#define HW_LCD_H_RES 1024 +#define HW_LCD_V_RES 600 +#define HW_USE_TOUCH 1 +#define HW_USE_ENCODER 0 +#elif CONFIG_EXAMPLE_LCD_INTERFACE_QSPI +#define HW_LCD_H_RES 360 +#define HW_LCD_V_RES 360 +#define HW_USE_TOUCH 0 +#define HW_USE_ENCODER 1 +#elif CONFIG_EXAMPLE_LCD_INTERFACE_RGB +#define HW_LCD_H_RES 800 +#define HW_LCD_V_RES 480 +#define HW_USE_TOUCH 1 +#define HW_USE_ENCODER 0 +#elif CONFIG_EXAMPLE_LCD_INTERFACE_SPI_WITH_PSRAM +#define HW_LCD_H_RES 320 +#define HW_LCD_V_RES 240 +#define HW_USE_TOUCH 1 +#define HW_USE_ENCODER 0 +#elif CONFIG_EXAMPLE_LCD_INTERFACE_SPI_WITHOUT_PSRAM +#define HW_LCD_H_RES 240 +#define HW_LCD_V_RES 240 +#define HW_USE_TOUCH 0 +#define HW_USE_ENCODER 1 +#else +#define HW_LCD_H_RES 320 +#define HW_LCD_V_RES 240 +#define HW_USE_TOUCH 1 +#define HW_USE_ENCODER 0 +#endif + +esp_err_t hw_lcd_init(esp_lcd_panel_handle_t *panel_handle, + esp_lcd_panel_io_handle_t *io_handle, + esp_lv_adapter_tear_avoid_mode_t tear_avoid_mode, + esp_lv_adapter_rotation_t rotation); + +#if HW_USE_TOUCH +#include "esp_lcd_touch.h" + +esp_err_t hw_touch_init(esp_lcd_touch_handle_t *touch_handle, + esp_lv_adapter_rotation_t rotation); + +#endif + +#if HW_USE_ENCODER +#include "iot_knob.h" +#include "iot_button.h" + +const knob_config_t *hw_knob_get_config(void); +button_handle_t hw_knob_get_button(void); + +#endif + +#ifdef __cplusplus +} +#endif diff --git a/firmware/echoear-platform-agent/components/hw_init/idf_component.yml b/firmware/echoear-platform-agent/components/hw_init/idf_component.yml new file mode 100644 index 0000000..6e803a5 --- /dev/null +++ b/firmware/echoear-platform-agent/components/hw_init/idf_component.yml @@ -0,0 +1,31 @@ +version: "1.0.0" + +dependencies: + idf: + version: ">=5.5.0" + + esp_lvgl_adapter: + version: "==0.1.0-beta.1" + + esp_lcd_ek79007: + version: "*" + rules: + - if: "target == esp32p4" + + esp_lcd_st77916: + version: "*" + + esp_lcd_ili9341: + version: "^2" + + esp_lcd_gc9a01: + version: "^2" + + espressif/esp_lcd_touch_gt911: + version: "^1" + + espressif/esp_lcd_touch_gt1151: + version: "^1" + + espressif/esp_lcd_touch_cst816s: + version: "^1" diff --git a/firmware/echoear-platform-agent/components/hw_init/input/knob_init.c b/firmware/echoear-platform-agent/components/hw_init/input/knob_init.c new file mode 100644 index 0000000..c728701 --- /dev/null +++ b/firmware/echoear-platform-agent/components/hw_init/input/knob_init.c @@ -0,0 +1,66 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "hw_init.h" + +#if HW_USE_ENCODER + +#include +#include "esp_log.h" +#include "iot_knob.h" +#include "iot_button.h" +#include "button_gpio.h" + +#define HW_ENCODER_A (GPIO_NUM_10) +#define HW_ENCODER_B (GPIO_NUM_6) +#define HW_ENCODER_ENTER (GPIO_NUM_9) + +static const char *TAG = "hw_knob_init"; + +static const knob_config_t hw_knob_encoder_config = { + .default_direction = 0, + .gpio_encoder_a = HW_ENCODER_A, + .gpio_encoder_b = HW_ENCODER_B, + .enable_power_save = false, +}; + +static button_handle_t s_encoder_button_handle = NULL; + +const knob_config_t *hw_knob_get_config(void) +{ + return &hw_knob_encoder_config; +} + +button_handle_t hw_knob_get_button(void) +{ + if (s_encoder_button_handle == NULL) { + ESP_LOGI(TAG, "Initializing encoder button on GPIO %d", HW_ENCODER_ENTER); + + const button_config_t btn_cfg = { + .long_press_time = 0, + .short_press_time = 0, + }; + + const button_gpio_config_t btn_gpio_cfg = { + .gpio_num = HW_ENCODER_ENTER, + .active_level = 0, + .enable_power_save = false, + .disable_pull = false, + }; + + esp_err_t ret = iot_button_new_gpio_device(&btn_cfg, &btn_gpio_cfg, &s_encoder_button_handle); + if (ret != ESP_OK || s_encoder_button_handle == NULL) { + ESP_LOGE(TAG, "Failed to create encoder button: %d", ret); + return NULL; + } + + ESP_LOGI(TAG, "Encoder button initialized successfully"); + } + + return s_encoder_button_handle; +} + +#endif diff --git a/firmware/echoear-platform-agent/components/hw_init/input/touch_init.c b/firmware/echoear-platform-agent/components/hw_init/input/touch_init.c new file mode 100644 index 0000000..641fe2a --- /dev/null +++ b/firmware/echoear-platform-agent/components/hw_init/input/touch_init.c @@ -0,0 +1,142 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "driver/i2c_master.h" +#include +#include "esp_log.h" +#include "hw_init.h" +#include "input/touch_rotation_helper.h" + +#if HW_USE_TOUCH + +#if CONFIG_EXAMPLE_LCD_INTERFACE_RGB +#include "esp_lcd_touch_gt1151.h" +#define HW_LCD_TOUCH_RST (GPIO_NUM_NC) +#define HW_LCD_TOUCH_INT (GPIO_NUM_NC) +#define HW_I2C_SDA (GPIO_NUM_47) +#define HW_I2C_SCL (GPIO_NUM_48) +#define TOUCH_CONTROLLER_NAME "GT1151" +#define TOUCH_ROTATION_TYPE TOUCH_ROTATION_STANDARD +#define TOUCH_CONTROLLER_TYPE_GT1151 1 + +#elif CONFIG_EXAMPLE_LCD_INTERFACE_MIPI_DSI +#include "esp_lcd_touch_gt911.h" +#define HW_LCD_TOUCH_RST (GPIO_NUM_NC) +#define HW_LCD_TOUCH_INT (GPIO_NUM_NC) +#define HW_I2C_SDA (GPIO_NUM_7) +#define HW_I2C_SCL (GPIO_NUM_8) +#define TOUCH_CONTROLLER_NAME "GT911" +#define TOUCH_ROTATION_TYPE TOUCH_ROTATION_MIPI_DSI +#define TOUCH_CONTROLLER_TYPE_GT911 1 + +#elif CONFIG_EXAMPLE_LCD_INTERFACE_QSPI +#include "esp_lcd_touch_cst816s.h" +#define HW_LCD_TOUCH_RST (GPIO_NUM_NC) +#define HW_LCD_TOUCH_INT (GPIO_NUM_10) +#define HW_I2C_SDA (GPIO_NUM_2) +#define HW_I2C_SCL (GPIO_NUM_1) +#define TOUCH_CONTROLLER_NAME "CST816S" +#define TOUCH_ROTATION_TYPE TOUCH_ROTATION_STANDARD +#define TOUCH_CONTROLLER_TYPE_CST816S 1 + +#elif CONFIG_EXAMPLE_LCD_INTERFACE_SPI_WITH_PSRAM +#include "esp_lcd_touch_gt911.h" +#define HW_LCD_TOUCH_RST (GPIO_NUM_NC) +#define HW_LCD_TOUCH_INT (GPIO_NUM_3) +#define HW_I2C_SDA (GPIO_NUM_8) +#define HW_I2C_SCL (GPIO_NUM_18) +#define TOUCH_CONTROLLER_NAME "GT911" +#define TOUCH_ROTATION_TYPE TOUCH_ROTATION_STANDARD +#define TOUCH_CONTROLLER_TYPE_GT911 1 + +#else +#include "esp_lcd_touch_gt911.h" +#define HW_LCD_TOUCH_RST (GPIO_NUM_NC) +#define HW_LCD_TOUCH_INT (GPIO_NUM_NC) +#define HW_I2C_SDA (GPIO_NUM_7) +#define HW_I2C_SCL (GPIO_NUM_8) +#define TOUCH_CONTROLLER_NAME "GT911" +#define TOUCH_ROTATION_TYPE TOUCH_ROTATION_STANDARD +#define TOUCH_CONTROLLER_TYPE_GT911 1 +#endif + +#define HW_I2C_NUM (I2C_NUM_0) +#define HW_I2C_CLK_SPEED_HZ (400000) + +static const char *TAG = "hw_touch_init"; +static i2c_master_bus_handle_t s_touch_i2c_bus = NULL; + +static esp_err_t hw_touch_i2c_init(void) +{ + if (s_touch_i2c_bus) { + return ESP_OK; + } + + i2c_master_bus_config_t i2c_bus_conf = { + .clk_source = I2C_CLK_SRC_DEFAULT, + .sda_io_num = HW_I2C_SDA, + .scl_io_num = HW_I2C_SCL, + .i2c_port = HW_I2C_NUM, + }; + ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_bus_conf, &s_touch_i2c_bus)); + + return ESP_OK; +} + +esp_err_t hw_touch_init(esp_lcd_touch_handle_t *ret_touch, esp_lv_adapter_rotation_t rotation) +{ + ESP_LOGI(TAG, "Initializing touch controller (%s)", TOUCH_CONTROLLER_NAME); + + ESP_ERROR_CHECK(hw_touch_i2c_init()); + + bool swap_xy = false; + bool mirror_x = false; + bool mirror_y = false; + touch_get_rotation_flags(TOUCH_ROTATION_TYPE, rotation, &swap_xy, &mirror_x, &mirror_y); + + const esp_lcd_touch_config_t tp_cfg = { + .x_max = HW_LCD_H_RES, + .y_max = HW_LCD_V_RES, + .rst_gpio_num = HW_LCD_TOUCH_RST, + .int_gpio_num = HW_LCD_TOUCH_INT, + .levels = { + .reset = 0, + .interrupt = 0, + }, + .flags = { + .swap_xy = swap_xy, + .mirror_x = mirror_x, + .mirror_y = mirror_y, + }, + }; + + esp_lcd_panel_io_handle_t tp_io_handle = NULL; + + /* Initialize touch controller based on type */ +#if defined(TOUCH_CONTROLLER_TYPE_GT1151) + esp_lcd_panel_io_i2c_config_t tp_io_config = ESP_LCD_TOUCH_IO_I2C_GT1151_CONFIG(); + tp_io_config.scl_speed_hz = HW_I2C_CLK_SPEED_HZ; + ESP_ERROR_CHECK(esp_lcd_new_panel_io_i2c(s_touch_i2c_bus, &tp_io_config, &tp_io_handle)); + return esp_lcd_touch_new_i2c_gt1151(tp_io_handle, &tp_cfg, ret_touch); + +#elif defined(TOUCH_CONTROLLER_TYPE_CST816S) + esp_lcd_panel_io_i2c_config_t tp_io_config = ESP_LCD_TOUCH_IO_I2C_CST816S_CONFIG(); + tp_io_config.scl_speed_hz = HW_I2C_CLK_SPEED_HZ; + ESP_ERROR_CHECK(esp_lcd_new_panel_io_i2c(s_touch_i2c_bus, &tp_io_config, &tp_io_handle)); + return esp_lcd_touch_new_i2c_cst816s(tp_io_handle, &tp_cfg, ret_touch); + +#elif defined(TOUCH_CONTROLLER_TYPE_GT911) + esp_lcd_panel_io_i2c_config_t tp_io_config = ESP_LCD_TOUCH_IO_I2C_GT911_CONFIG(); + tp_io_config.scl_speed_hz = HW_I2C_CLK_SPEED_HZ; + ESP_ERROR_CHECK(esp_lcd_new_panel_io_i2c(s_touch_i2c_bus, &tp_io_config, &tp_io_handle)); + return esp_lcd_touch_new_i2c_gt911(tp_io_handle, &tp_cfg, ret_touch); + +#else +#error "No touch controller type defined" +#endif +} + +#endif diff --git a/firmware/echoear-platform-agent/components/hw_init/input/touch_rotation_helper.c b/firmware/echoear-platform-agent/components/hw_init/input/touch_rotation_helper.c new file mode 100644 index 0000000..eb5b43b --- /dev/null +++ b/firmware/echoear-platform-agent/components/hw_init/input/touch_rotation_helper.c @@ -0,0 +1,78 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "touch_rotation_helper.h" + +void touch_get_rotation_flags(touch_rotation_type_t type, esp_lv_adapter_rotation_t rotation, + bool *swap_xy, bool *mirror_x, bool *mirror_y) +{ + bool swap = false; + bool x_mirror, y_mirror; + + if (type == TOUCH_ROTATION_MIPI_DSI) { + x_mirror = true; + y_mirror = true; + switch (rotation) { + case ESP_LV_ADAPTER_ROTATE_90: + swap = true; + x_mirror = false; + y_mirror = true; + break; + case ESP_LV_ADAPTER_ROTATE_180: + swap = false; + x_mirror = false; + y_mirror = false; + break; + case ESP_LV_ADAPTER_ROTATE_270: + swap = true; + x_mirror = true; + y_mirror = false; + break; + case ESP_LV_ADAPTER_ROTATE_0: + default: + swap = false; + x_mirror = true; + y_mirror = true; + break; + } + } else { + x_mirror = false; + y_mirror = false; + switch (rotation) { + case ESP_LV_ADAPTER_ROTATE_90: + swap = true; + x_mirror = true; + y_mirror = false; + break; + case ESP_LV_ADAPTER_ROTATE_180: + swap = false; + x_mirror = true; + y_mirror = true; + break; + case ESP_LV_ADAPTER_ROTATE_270: + swap = true; + x_mirror = false; + y_mirror = true; + break; + case ESP_LV_ADAPTER_ROTATE_0: + default: + swap = false; + x_mirror = false; + y_mirror = false; + break; + } + } + + if (swap_xy) { + *swap_xy = swap; + } + if (mirror_x) { + *mirror_x = x_mirror; + } + if (mirror_y) { + *mirror_y = y_mirror; + } +} diff --git a/firmware/echoear-platform-agent/components/hw_init/input/touch_rotation_helper.h b/firmware/echoear-platform-agent/components/hw_init/input/touch_rotation_helper.h new file mode 100644 index 0000000..a0d26a7 --- /dev/null +++ b/firmware/echoear-platform-agent/components/hw_init/input/touch_rotation_helper.h @@ -0,0 +1,26 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include "esp_lv_adapter_display.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + TOUCH_ROTATION_STANDARD, + TOUCH_ROTATION_MIPI_DSI, +} touch_rotation_type_t; + +void touch_get_rotation_flags(touch_rotation_type_t type, esp_lv_adapter_rotation_t rotation, + bool *swap_xy, bool *mirror_x, bool *mirror_y); + +#ifdef __cplusplus +} +#endif diff --git a/firmware/echoear-platform-agent/components/hw_init/lcd/lcd_init_mipi_dsi.c b/firmware/echoear-platform-agent/components/hw_init/lcd/lcd_init_mipi_dsi.c new file mode 100644 index 0000000..e009926 --- /dev/null +++ b/firmware/echoear-platform-agent/components/hw_init/lcd/lcd_init_mipi_dsi.c @@ -0,0 +1,78 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: CC0-1.0 + */ + +#include "hw_init.h" + +#if CONFIG_EXAMPLE_LCD_INTERFACE_MIPI_DSI + +#include "esp_ldo_regulator.h" +#include "esp_lcd_mipi_dsi.h" +#include "esp_lcd_panel_ops.h" +#include "esp_lcd_ek79007.h" +#include "driver/gpio.h" + +static const char *TAG = "hw_lcd_init"; + +#define HW_LDO_MIPI_CHAN (3) +#define HW_LDO_MIPI_VOLTAGE_MV (2500) +#define HW_LCD_BIT_PER_PIXEL (16) +#define HW_MIPI_DPI_PX_FORMAT (LCD_COLOR_PIXEL_FORMAT_RGB565) +#define HW_PIN_NUM_LCD_RST (27) + +static esp_lcd_dsi_bus_handle_t s_mipi_dsi_bus; +static esp_lcd_panel_io_handle_t s_mipi_dbi_io; +static esp_lcd_panel_handle_t s_panel_handle; + +static void lcd_ldo_power_on(void) +{ + esp_ldo_channel_handle_t ldo_mipi_phy = NULL; + esp_ldo_channel_config_t ldo_mipi_phy_config = { + .chan_id = HW_LDO_MIPI_CHAN, + .voltage_mv = HW_LDO_MIPI_VOLTAGE_MV, + }; + ESP_ERROR_CHECK(esp_ldo_acquire_channel(&ldo_mipi_phy_config, &ldo_mipi_phy)); +} + +esp_err_t hw_lcd_init(esp_lcd_panel_handle_t *panel_handle, esp_lcd_panel_io_handle_t *io_handle, esp_lv_adapter_tear_avoid_mode_t tear_avoid_mode, esp_lv_adapter_rotation_t rotation) +{ + lcd_ldo_power_on(); + + ESP_LOGD(TAG, "Install LCD driver"); + esp_lcd_dsi_bus_config_t bus_config = EK79007_PANEL_BUS_DSI_2CH_CONFIG(); + ESP_ERROR_CHECK(esp_lcd_new_dsi_bus(&bus_config, &s_mipi_dsi_bus)); + + ESP_LOGI(TAG, "Install panel IO"); + esp_lcd_dbi_io_config_t dbi_config = EK79007_PANEL_IO_DBI_CONFIG(); + ESP_ERROR_CHECK(esp_lcd_new_panel_io_dbi(s_mipi_dsi_bus, &dbi_config, &s_mipi_dbi_io)); + + ESP_LOGI(TAG, "Install LCD driver of ek79007"); + esp_lcd_dpi_panel_config_t dpi_config = EK79007_1024_600_PANEL_60HZ_CONFIG(HW_MIPI_DPI_PX_FORMAT); + dpi_config.num_fbs = esp_lv_adapter_get_required_frame_buffer_count(tear_avoid_mode, rotation); + ek79007_vendor_config_t vendor_config = { + .mipi_config = { + .dsi_bus = s_mipi_dsi_bus, + .dpi_config = &dpi_config, + }, + }; + const esp_lcd_panel_dev_config_t panel_config = { + .reset_gpio_num = HW_PIN_NUM_LCD_RST, + .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB, + .bits_per_pixel = HW_LCD_BIT_PER_PIXEL, + .vendor_config = &vendor_config, + }; + ESP_ERROR_CHECK(esp_lcd_new_panel_ek79007(s_mipi_dbi_io, &panel_config, &s_panel_handle)); + ESP_ERROR_CHECK(esp_lcd_panel_reset(s_panel_handle)); + ESP_ERROR_CHECK(esp_lcd_panel_init(s_panel_handle)); + + *panel_handle = s_panel_handle; + if (io_handle) { + *io_handle = s_mipi_dbi_io; + } + + return ESP_OK; +} + +#endif diff --git a/firmware/echoear-platform-agent/components/hw_init/lcd/lcd_init_qspi.c b/firmware/echoear-platform-agent/components/hw_init/lcd/lcd_init_qspi.c new file mode 100644 index 0000000..27727dd --- /dev/null +++ b/firmware/echoear-platform-agent/components/hw_init/lcd/lcd_init_qspi.c @@ -0,0 +1,314 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: CC0-1.0 + */ + +#include "esp_system.h" +#include "esp_lcd_st77916.h" +#include "driver/gpio.h" +#include "hw_init.h" +#include "helpers/lcd_orientation_helper.h" + +#if CONFIG_EXAMPLE_LCD_INTERFACE_QSPI + +static const char *TAG = "hw_lcd_init"; + +#define HW_PIN_NUM_BK_LIGHT (GPIO_NUM_44) +#define HW_LCD_BK_LIGHT_ON_LEVEL (1) +#define HW_LCD_BASE_SWAP_XY (false) +#define HW_LCD_BASE_MIRROR_X (false) +#define HW_LCD_BASE_MIRROR_Y (false) + +#define HW_LCD_DATA3 (GPIO_NUM_12) +#define HW_LCD_DATA2 (GPIO_NUM_11) +#define HW_LCD_DATA1 (GPIO_NUM_13) +#define HW_LCD_DATA0 (GPIO_NUM_46) +#define HW_LCD_PCLK (GPIO_NUM_18) +#define HW_LCD_CS (GPIO_NUM_14) +#define HW_LCD_DC (GPIO_NUM_45) +#define HW_LCD_RST (GPIO_NUM_47) +#define HW_LCD_POWER_OFF (GPIO_NUM_9) + +#define HW_LCD_PIXEL_CLOCK_HZ (80 * 1000 * 1000) +#define HW_LCD_COLOR_SPACE (LCD_RGB_ELEMENT_ORDER_RGB) +#define HW_LCD_BITS_PER_PIXEL (16) +#define HW_LCD_CMD_BITS (32) +#define HW_LCD_PARAM_BITS (8) +#define HW_LCD_MAX_TRANSFER_SZ (HW_LCD_H_RES * HW_LCD_V_RES * 2) + +static esp_lcd_panel_io_handle_t s_panel_io_handle; +static esp_lcd_panel_handle_t s_panel_handle; + +static const st77916_lcd_init_cmd_t st77916_qspi_init[] = { + {0xF0, (uint8_t []){0x28}, 1, 0}, + {0xF2, (uint8_t []){0x28}, 1, 0}, + {0x73, (uint8_t []){0xF0}, 1, 0}, + {0x7C, (uint8_t []){0xD1}, 1, 0}, + {0x83, (uint8_t []){0xE0}, 1, 0}, + {0x84, (uint8_t []){0x61}, 1, 0}, + {0xF2, (uint8_t []){0x82}, 1, 0}, + {0xF0, (uint8_t []){0x00}, 1, 0}, + {0xF0, (uint8_t []){0x01}, 1, 0}, + {0xF1, (uint8_t []){0x01}, 1, 0}, + {0xB0, (uint8_t []){0x56}, 1, 0}, + {0xB1, (uint8_t []){0x4D}, 1, 0}, + {0xB2, (uint8_t []){0x24}, 1, 0}, + {0xB4, (uint8_t []){0x87}, 1, 0}, + {0xB5, (uint8_t []){0x44}, 1, 0}, + {0xB6, (uint8_t []){0x8B}, 1, 0}, + {0xB7, (uint8_t []){0x40}, 1, 0}, + {0xB8, (uint8_t []){0x86}, 1, 0}, + {0xBA, (uint8_t []){0x00}, 1, 0}, + {0xBB, (uint8_t []){0x08}, 1, 0}, + {0xBC, (uint8_t []){0x08}, 1, 0}, + {0xBD, (uint8_t []){0x00}, 1, 0}, + {0xC0, (uint8_t []){0x80}, 1, 0}, + {0xC1, (uint8_t []){0x10}, 1, 0}, + {0xC2, (uint8_t []){0x37}, 1, 0}, + {0xC3, (uint8_t []){0x80}, 1, 0}, + {0xC4, (uint8_t []){0x10}, 1, 0}, + {0xC5, (uint8_t []){0x37}, 1, 0}, + {0xC6, (uint8_t []){0xA9}, 1, 0}, + {0xC7, (uint8_t []){0x41}, 1, 0}, + {0xC8, (uint8_t []){0x01}, 1, 0}, + {0xC9, (uint8_t []){0xA9}, 1, 0}, + {0xCA, (uint8_t []){0x41}, 1, 0}, + {0xCB, (uint8_t []){0x01}, 1, 0}, + {0xD0, (uint8_t []){0x91}, 1, 0}, + {0xD1, (uint8_t []){0x68}, 1, 0}, + {0xD2, (uint8_t []){0x68}, 1, 0}, + {0xF5, (uint8_t []){0x00, 0xA5}, 2, 0}, + {0xDD, (uint8_t []){0x4F}, 1, 0}, + {0xDE, (uint8_t []){0x4F}, 1, 0}, + {0xF1, (uint8_t []){0x10}, 1, 0}, + {0xF0, (uint8_t []){0x00}, 1, 0}, + {0xF0, (uint8_t []){0x02}, 1, 0}, + {0xE0, (uint8_t []){0xF0, 0x0A, 0x10, 0x09, 0x09, 0x36, 0x35, 0x33, 0x4A, 0x29, 0x15, 0x15, 0x2E, 0x34}, 14, 0}, + {0xE1, (uint8_t []){0xF0, 0x0A, 0x0F, 0x08, 0x08, 0x05, 0x34, 0x33, 0x4A, 0x39, 0x15, 0x15, 0x2D, 0x33}, 14, 0}, + {0xF0, (uint8_t []){0x10}, 1, 0}, + {0xF3, (uint8_t []){0x10}, 1, 0}, + {0xE0, (uint8_t []){0x07}, 1, 0}, + {0xE1, (uint8_t []){0x00}, 1, 0}, + {0xE2, (uint8_t []){0x00}, 1, 0}, + {0xE3, (uint8_t []){0x00}, 1, 0}, + {0xE4, (uint8_t []){0xE0}, 1, 0}, + {0xE5, (uint8_t []){0x06}, 1, 0}, + {0xE6, (uint8_t []){0x21}, 1, 0}, + {0xE7, (uint8_t []){0x01}, 1, 0}, + {0xE8, (uint8_t []){0x05}, 1, 0}, + {0xE9, (uint8_t []){0x02}, 1, 0}, + {0xEA, (uint8_t []){0xDA}, 1, 0}, + {0xEB, (uint8_t []){0x00}, 1, 0}, + {0xEC, (uint8_t []){0x00}, 1, 0}, + {0xED, (uint8_t []){0x0F}, 1, 0}, + {0xEE, (uint8_t []){0x00}, 1, 0}, + {0xEF, (uint8_t []){0x00}, 1, 0}, + {0xF8, (uint8_t []){0x00}, 1, 0}, + {0xF9, (uint8_t []){0x00}, 1, 0}, + {0xFA, (uint8_t []){0x00}, 1, 0}, + {0xFB, (uint8_t []){0x00}, 1, 0}, + {0xFC, (uint8_t []){0x00}, 1, 0}, + {0xFD, (uint8_t []){0x00}, 1, 0}, + {0xFE, (uint8_t []){0x00}, 1, 0}, + {0xFF, (uint8_t []){0x00}, 1, 0}, + {0x60, (uint8_t []){0x40}, 1, 0}, + {0x61, (uint8_t []){0x04}, 1, 0}, + {0x62, (uint8_t []){0x00}, 1, 0}, + {0x63, (uint8_t []){0x42}, 1, 0}, + {0x64, (uint8_t []){0xD9}, 1, 0}, + {0x65, (uint8_t []){0x00}, 1, 0}, + {0x66, (uint8_t []){0x00}, 1, 0}, + {0x67, (uint8_t []){0x00}, 1, 0}, + {0x68, (uint8_t []){0x00}, 1, 0}, + {0x69, (uint8_t []){0x00}, 1, 0}, + {0x6A, (uint8_t []){0x00}, 1, 0}, + {0x6B, (uint8_t []){0x00}, 1, 0}, + {0x70, (uint8_t []){0x40}, 1, 0}, + {0x71, (uint8_t []){0x03}, 1, 0}, + {0x72, (uint8_t []){0x00}, 1, 0}, + {0x73, (uint8_t []){0x42}, 1, 0}, + {0x74, (uint8_t []){0xD8}, 1, 0}, + {0x75, (uint8_t []){0x00}, 1, 0}, + {0x76, (uint8_t []){0x00}, 1, 0}, + {0x77, (uint8_t []){0x00}, 1, 0}, + {0x78, (uint8_t []){0x00}, 1, 0}, + {0x79, (uint8_t []){0x00}, 1, 0}, + {0x7A, (uint8_t []){0x00}, 1, 0}, + {0x7B, (uint8_t []){0x00}, 1, 0}, + {0x80, (uint8_t []){0x48}, 1, 0}, + {0x81, (uint8_t []){0x00}, 1, 0}, + {0x82, (uint8_t []){0x06}, 1, 0}, + {0x83, (uint8_t []){0x02}, 1, 0}, + {0x84, (uint8_t []){0xD6}, 1, 0}, + {0x85, (uint8_t []){0x04}, 1, 0}, + {0x86, (uint8_t []){0x00}, 1, 0}, + {0x87, (uint8_t []){0x00}, 1, 0}, + {0x88, (uint8_t []){0x48}, 1, 0}, + {0x89, (uint8_t []){0x00}, 1, 0}, + {0x8A, (uint8_t []){0x08}, 1, 0}, + {0x8B, (uint8_t []){0x02}, 1, 0}, + {0x8C, (uint8_t []){0xD8}, 1, 0}, + {0x8D, (uint8_t []){0x04}, 1, 0}, + {0x8E, (uint8_t []){0x00}, 1, 0}, + {0x8F, (uint8_t []){0x00}, 1, 0}, + {0x90, (uint8_t []){0x48}, 1, 0}, + {0x91, (uint8_t []){0x00}, 1, 0}, + {0x92, (uint8_t []){0x0A}, 1, 0}, + {0x93, (uint8_t []){0x02}, 1, 0}, + {0x94, (uint8_t []){0xDA}, 1, 0}, + {0x95, (uint8_t []){0x04}, 1, 0}, + {0x96, (uint8_t []){0x00}, 1, 0}, + {0x97, (uint8_t []){0x00}, 1, 0}, + {0x98, (uint8_t []){0x48}, 1, 0}, + {0x99, (uint8_t []){0x00}, 1, 0}, + {0x9A, (uint8_t []){0x0C}, 1, 0}, + {0x9B, (uint8_t []){0x02}, 1, 0}, + {0x9C, (uint8_t []){0xDC}, 1, 0}, + {0x9D, (uint8_t []){0x04}, 1, 0}, + {0x9E, (uint8_t []){0x00}, 1, 0}, + {0x9F, (uint8_t []){0x00}, 1, 0}, + {0xA0, (uint8_t []){0x48}, 1, 0}, + {0xA1, (uint8_t []){0x00}, 1, 0}, + {0xA2, (uint8_t []){0x05}, 1, 0}, + {0xA3, (uint8_t []){0x02}, 1, 0}, + {0xA4, (uint8_t []){0xD5}, 1, 0}, + {0xA5, (uint8_t []){0x04}, 1, 0}, + {0xA6, (uint8_t []){0x00}, 1, 0}, + {0xA7, (uint8_t []){0x00}, 1, 0}, + {0xA8, (uint8_t []){0x48}, 1, 0}, + {0xA9, (uint8_t []){0x00}, 1, 0}, + {0xAA, (uint8_t []){0x07}, 1, 0}, + {0xAB, (uint8_t []){0x02}, 1, 0}, + {0xAC, (uint8_t []){0xD7}, 1, 0}, + {0xAD, (uint8_t []){0x04}, 1, 0}, + {0xAE, (uint8_t []){0x00}, 1, 0}, + {0xAF, (uint8_t []){0x00}, 1, 0}, + {0xB0, (uint8_t []){0x48}, 1, 0}, + {0xB1, (uint8_t []){0x00}, 1, 0}, + {0xB2, (uint8_t []){0x09}, 1, 0}, + {0xB3, (uint8_t []){0x02}, 1, 0}, + {0xB4, (uint8_t []){0xD9}, 1, 0}, + {0xB5, (uint8_t []){0x04}, 1, 0}, + {0xB6, (uint8_t []){0x00}, 1, 0}, + {0xB7, (uint8_t []){0x00}, 1, 0}, + {0xB8, (uint8_t []){0x48}, 1, 0}, + {0xB9, (uint8_t []){0x00}, 1, 0}, + {0xBA, (uint8_t []){0x0B}, 1, 0}, + {0xBB, (uint8_t []){0x02}, 1, 0}, + {0xBC, (uint8_t []){0xDB}, 1, 0}, + {0xBD, (uint8_t []){0x04}, 1, 0}, + {0xBE, (uint8_t []){0x00}, 1, 0}, + {0xBF, (uint8_t []){0x00}, 1, 0}, + {0xC0, (uint8_t []){0x10}, 1, 0}, + {0xC1, (uint8_t []){0x47}, 1, 0}, + {0xC2, (uint8_t []){0x56}, 1, 0}, + {0xC3, (uint8_t []){0x65}, 1, 0}, + {0xC4, (uint8_t []){0x74}, 1, 0}, + {0xC5, (uint8_t []){0x88}, 1, 0}, + {0xC6, (uint8_t []){0x99}, 1, 0}, + {0xC7, (uint8_t []){0x01}, 1, 0}, + {0xC8, (uint8_t []){0xBB}, 1, 0}, + {0xC9, (uint8_t []){0xAA}, 1, 0}, + {0xD0, (uint8_t []){0x10}, 1, 0}, + {0xD1, (uint8_t []){0x47}, 1, 0}, + {0xD2, (uint8_t []){0x56}, 1, 0}, + {0xD3, (uint8_t []){0x65}, 1, 0}, + {0xD4, (uint8_t []){0x74}, 1, 0}, + {0xD5, (uint8_t []){0x88}, 1, 0}, + {0xD6, (uint8_t []){0x99}, 1, 0}, + {0xD7, (uint8_t []){0x01}, 1, 0}, + {0xD8, (uint8_t []){0xBB}, 1, 0}, + {0xD9, (uint8_t []){0xAA}, 1, 0}, + {0xF3, (uint8_t []){0x01}, 1, 0}, + {0xF0, (uint8_t []){0x00}, 1, 0}, + {0x21, (uint8_t []){}, 0, 0}, + {0x11, (uint8_t []){}, 0, 0}, + {0x00, (uint8_t []){}, 0, 120}, +}; + +esp_err_t hw_lcd_init(esp_lcd_panel_handle_t *panel_handle, esp_lcd_panel_io_handle_t *io_handle, esp_lv_adapter_tear_avoid_mode_t tear_avoid_mode, esp_lv_adapter_rotation_t rotation) +{ + ESP_LOGD(TAG, "Initialize QSPI bus"); + const spi_bus_config_t buscfg = { + .sclk_io_num = HW_LCD_PCLK, + .data0_io_num = HW_LCD_DATA0, + .data1_io_num = HW_LCD_DATA1, + .data2_io_num = HW_LCD_DATA2, + .data3_io_num = HW_LCD_DATA3, + .max_transfer_sz = HW_LCD_MAX_TRANSFER_SZ, + .flags = SPICOMMON_BUSFLAG_QUAD, + }; + ESP_ERROR_CHECK(spi_bus_initialize(SPI2_HOST, &buscfg, SPI_DMA_CH_AUTO)); + + ESP_LOGD(TAG, "Install panel IO"); + esp_lcd_panel_io_spi_config_t io_config = { + .dc_gpio_num = HW_LCD_DC, + .cs_gpio_num = HW_LCD_CS, + .pclk_hz = HW_LCD_PIXEL_CLOCK_HZ, + .lcd_cmd_bits = HW_LCD_CMD_BITS, + .lcd_param_bits = HW_LCD_PARAM_BITS, + .spi_mode = 0, + .trans_queue_depth = 1, + .flags = { + .quad_mode = 1, + }, + }; + ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)SPI2_HOST, &io_config, &s_panel_io_handle)); + + ESP_LOGD(TAG, "Power on LCD"); + gpio_config_t power_gpio_config = { + .mode = GPIO_MODE_OUTPUT, + .pin_bit_mask = 1ULL << HW_LCD_POWER_OFF + }; + ESP_ERROR_CHECK(gpio_config(&power_gpio_config)); + gpio_set_level(HW_LCD_POWER_OFF, 0); + + ESP_LOGD(TAG, "Install LCD driver"); + const st77916_vendor_config_t vendor_config = { + .init_cmds = st77916_qspi_init, + .init_cmds_size = sizeof(st77916_qspi_init) / sizeof(st77916_lcd_init_cmd_t), + .flags = { + .use_qspi_interface = 1, + }, + }; + esp_lcd_panel_dev_config_t panel_config = { + .reset_gpio_num = HW_LCD_RST, + .rgb_ele_order = HW_LCD_COLOR_SPACE, + .bits_per_pixel = HW_LCD_BITS_PER_PIXEL, + .vendor_config = (void *) &vendor_config, + .flags = { + .reset_active_high = true, + }, + }; + + ESP_LOGI(TAG, "Install st77916 panel driver"); + ESP_ERROR_CHECK(esp_lcd_new_panel_st77916(s_panel_io_handle, (const esp_lcd_panel_dev_config_t *)&panel_config, &s_panel_handle)); + + esp_lcd_panel_reset(s_panel_handle); + esp_lcd_panel_init(s_panel_handle); + + bool swap_xy = false; + bool mirror_x = false; + bool mirror_y = false; + lcd_get_orientation_flags(HW_LCD_BASE_SWAP_XY, HW_LCD_BASE_MIRROR_X, HW_LCD_BASE_MIRROR_Y, + rotation, &swap_xy, &mirror_x, &mirror_y); + ESP_ERROR_CHECK(esp_lcd_panel_swap_xy(s_panel_handle, swap_xy)); + ESP_ERROR_CHECK(esp_lcd_panel_mirror(s_panel_handle, mirror_x, mirror_y)); + esp_lcd_panel_disp_on_off(s_panel_handle, true); + + *io_handle = s_panel_io_handle; + *panel_handle = s_panel_handle; + + gpio_config_t bk_gpio_config = { + .mode = GPIO_MODE_OUTPUT, + .pin_bit_mask = 1ULL << HW_PIN_NUM_BK_LIGHT + }; + ESP_ERROR_CHECK(gpio_config(&bk_gpio_config)); + ESP_LOGI(TAG, "Turning on LCD backlight"); + gpio_set_level(HW_PIN_NUM_BK_LIGHT, HW_LCD_BK_LIGHT_ON_LEVEL); + + return ESP_OK; +} + +#endif diff --git a/firmware/echoear-platform-agent/components/hw_init/lcd/lcd_init_rgb.c b/firmware/echoear-platform-agent/components/hw_init/lcd/lcd_init_rgb.c new file mode 100644 index 0000000..783292d --- /dev/null +++ b/firmware/echoear-platform-agent/components/hw_init/lcd/lcd_init_rgb.c @@ -0,0 +1,113 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: CC0-1.0 + */ + +#include "hw_init.h" + +#if CONFIG_EXAMPLE_LCD_INTERFACE_RGB + +#include "esp_lcd_panel_rgb.h" +#include "driver/gpio.h" + +static const char *TAG = "hw_lcd_init"; + +#define HW_LCD_PIXEL_CLOCK_HZ (18 * 1000 * 1000) +#define HW_LCD_HSYNC (40) +#define HW_LCD_HBP (40) +#define HW_LCD_HFP (48) +#define HW_LCD_VSYNC (23) +#define HW_LCD_VBP (32) +#define HW_LCD_VFP (13) + +#define HW_LCD_RGB_VSYNC (GPIO_NUM_3) +#define HW_LCD_RGB_HSYNC (GPIO_NUM_46) +#define HW_LCD_RGB_DE (GPIO_NUM_17) +#define HW_LCD_RGB_PCLK (GPIO_NUM_9) +#define HW_LCD_RGB_DISP (GPIO_NUM_NC) +#define HW_LCD_RGB_DATA0 (GPIO_NUM_10) +#define HW_LCD_RGB_DATA1 (GPIO_NUM_11) +#define HW_LCD_RGB_DATA2 (GPIO_NUM_12) +#define HW_LCD_RGB_DATA3 (GPIO_NUM_13) +#define HW_LCD_RGB_DATA4 (GPIO_NUM_14) +#define HW_LCD_RGB_DATA5 (GPIO_NUM_21) +#define HW_LCD_RGB_DATA6 (GPIO_NUM_8) +#define HW_LCD_RGB_DATA7 (GPIO_NUM_18) +#define HW_LCD_RGB_DATA8 (GPIO_NUM_45) +#define HW_LCD_RGB_DATA9 (GPIO_NUM_38) +#define HW_LCD_RGB_DATA10 (GPIO_NUM_39) +#define HW_LCD_RGB_DATA11 (GPIO_NUM_40) +#define HW_LCD_RGB_DATA12 (GPIO_NUM_41) +#define HW_LCD_RGB_DATA13 (GPIO_NUM_42) +#define HW_LCD_RGB_DATA14 (GPIO_NUM_2) +#define HW_LCD_RGB_DATA15 (GPIO_NUM_1) + +#define HW_LCD_DATA_WIDTH (16) +#define HW_LCD_BIT_PER_PIXEL (16) +#define HW_LCD_BOUNCE_BUFFER_HEIGHT (20) + +static esp_lcd_panel_handle_t s_panel_handle; + +esp_err_t hw_lcd_init(esp_lcd_panel_handle_t *panel_handle, esp_lcd_panel_io_handle_t *io_handle, esp_lv_adapter_tear_avoid_mode_t tear_avoid_mode, esp_lv_adapter_rotation_t rotation) +{ + ESP_LOGI(TAG, "Initialize RGB panel"); + esp_lcd_rgb_panel_config_t panel_conf = { + .clk_src = LCD_CLK_SRC_DEFAULT, + .dma_burst_size = 64, + .data_width = HW_LCD_DATA_WIDTH, + .bits_per_pixel = HW_LCD_BIT_PER_PIXEL, + .de_gpio_num = HW_LCD_RGB_DE, + .pclk_gpio_num = HW_LCD_RGB_PCLK, + .vsync_gpio_num = HW_LCD_RGB_VSYNC, + .hsync_gpio_num = HW_LCD_RGB_HSYNC, + .disp_gpio_num = HW_LCD_RGB_DISP, + .data_gpio_nums = { + HW_LCD_RGB_DATA0, + HW_LCD_RGB_DATA1, + HW_LCD_RGB_DATA2, + HW_LCD_RGB_DATA3, + HW_LCD_RGB_DATA4, + HW_LCD_RGB_DATA5, + HW_LCD_RGB_DATA6, + HW_LCD_RGB_DATA7, + HW_LCD_RGB_DATA8, + HW_LCD_RGB_DATA9, + HW_LCD_RGB_DATA10, + HW_LCD_RGB_DATA11, + HW_LCD_RGB_DATA12, + HW_LCD_RGB_DATA13, + HW_LCD_RGB_DATA14, + HW_LCD_RGB_DATA15, + }, + .timings = { + .pclk_hz = HW_LCD_PIXEL_CLOCK_HZ, + .h_res = HW_LCD_H_RES, + .v_res = HW_LCD_V_RES, + .hsync_back_porch = HW_LCD_HBP, + .hsync_front_porch = HW_LCD_HFP, + .hsync_pulse_width = HW_LCD_HSYNC, + .vsync_back_porch = HW_LCD_VBP, + .vsync_front_porch = HW_LCD_VFP, + .vsync_pulse_width = HW_LCD_VSYNC, + .flags = { + .pclk_active_neg = true, + }, + }, + .flags.fb_in_psram = 1, + .num_fbs = esp_lv_adapter_get_required_frame_buffer_count(tear_avoid_mode, rotation), + .bounce_buffer_size_px = HW_LCD_H_RES * HW_LCD_BOUNCE_BUFFER_HEIGHT, + }; + ESP_ERROR_CHECK(esp_lcd_new_rgb_panel(&panel_conf, &s_panel_handle)); + ESP_ERROR_CHECK(esp_lcd_panel_reset(s_panel_handle)); + ESP_ERROR_CHECK(esp_lcd_panel_init(s_panel_handle)); + + *panel_handle = s_panel_handle; + if (io_handle) { + *io_handle = NULL; + } + + return ESP_OK; +} + +#endif diff --git a/firmware/echoear-platform-agent/components/hw_init/lcd/lcd_init_spi_with_psram.c b/firmware/echoear-platform-agent/components/hw_init/lcd/lcd_init_spi_with_psram.c new file mode 100644 index 0000000..08311da --- /dev/null +++ b/firmware/echoear-platform-agent/components/hw_init/lcd/lcd_init_spi_with_psram.c @@ -0,0 +1,123 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: CC0-1.0 + */ + +#include "esp_system.h" +#include "esp_lcd_ili9341.h" +#include "driver/gpio.h" +#include "hw_init.h" +#include "helpers/lcd_orientation_helper.h" + +#if CONFIG_EXAMPLE_LCD_INTERFACE_SPI_WITH_PSRAM + +static const char *TAG = "hw_lcd_init"; + +#define HW_PIN_NUM_BK_LIGHT (GPIO_NUM_47) +#define HW_LCD_BK_LIGHT_ON_LEVEL (1) +#define HW_LCD_BASE_SWAP_XY (false) +#define HW_LCD_BASE_MIRROR_X (true) +#define HW_LCD_BASE_MIRROR_Y (true) + +#define HW_LCD_DATA0 (GPIO_NUM_6) +#define HW_LCD_PCLK (GPIO_NUM_7) +#define HW_LCD_CS (GPIO_NUM_5) +#define HW_LCD_DC (GPIO_NUM_4) +#define HW_LCD_RST (GPIO_NUM_48) + +#define HW_LCD_PIXEL_CLOCK_HZ (40 * 1000 * 1000) +#define HW_LCD_COLOR_SPACE (LCD_RGB_ELEMENT_ORDER_RGB) +#define HW_LCD_BITS_PER_PIXEL (16) +#define HW_LCD_CMD_BITS (8) +#define HW_LCD_PARAM_BITS (8) +#define HW_LCD_MAX_TRANSFER_SZ (HW_LCD_H_RES * HW_LCD_V_RES * 2) + +static esp_lcd_panel_io_handle_t s_panel_io_handle; +static esp_lcd_panel_handle_t s_panel_handle; + +static const ili9341_lcd_init_cmd_t vendor_specific_init_default[] = { + {0xC8, (uint8_t []){0xFF, 0x93, 0x42}, 3, 0}, + {0xC0, (uint8_t []){0x0E, 0x0E}, 2, 0}, + {0xC5, (uint8_t []){0xD0}, 1, 0}, + {0xC1, (uint8_t []){0x02}, 1, 0}, + {0xB4, (uint8_t []){0x02}, 1, 0}, + {0xE0, (uint8_t []){0x00, 0x03, 0x08, 0x06, 0x13, 0x09, 0x39, 0x39, 0x48, 0x02, 0x0a, 0x08, 0x17, 0x17, 0x0F}, 15, 0}, + {0xE1, (uint8_t []){0x00, 0x28, 0x29, 0x01, 0x0d, 0x03, 0x3f, 0x33, 0x52, 0x04, 0x0f, 0x0e, 0x37, 0x38, 0x0F}, 15, 0}, + {0xB1, (uint8_t []){00, 0x1B}, 2, 0}, + {0x36, (uint8_t []){0x08}, 1, 0}, + {0x3A, (uint8_t []){0x55}, 1, 0}, + {0xB7, (uint8_t []){0x06}, 1, 0}, + {0x11, (uint8_t []){0}, 0x80, 0}, + {0x29, (uint8_t []){0}, 0x80, 0}, + {0, (uint8_t []){0}, 0xff, 0}, +}; + +esp_err_t hw_lcd_init(esp_lcd_panel_handle_t *panel_handle, esp_lcd_panel_io_handle_t *io_handle, esp_lv_adapter_tear_avoid_mode_t tear_avoid_mode, esp_lv_adapter_rotation_t rotation) +{ + ESP_LOGD(TAG, "Initialize SPI bus"); + const spi_bus_config_t buscfg = { + .sclk_io_num = HW_LCD_PCLK, + .mosi_io_num = HW_LCD_DATA0, + .miso_io_num = GPIO_NUM_NC, + .quadwp_io_num = GPIO_NUM_NC, + .quadhd_io_num = GPIO_NUM_NC, + .max_transfer_sz = HW_LCD_MAX_TRANSFER_SZ, + }; + ESP_ERROR_CHECK(spi_bus_initialize(SPI2_HOST, &buscfg, SPI_DMA_CH_AUTO)); + + ESP_LOGD(TAG, "Install panel IO"); + esp_lcd_panel_io_spi_config_t io_config = { + .dc_gpio_num = HW_LCD_DC, + .cs_gpio_num = HW_LCD_CS, + .pclk_hz = HW_LCD_PIXEL_CLOCK_HZ, + .lcd_cmd_bits = HW_LCD_CMD_BITS, + .lcd_param_bits = HW_LCD_PARAM_BITS, + .spi_mode = 0, + .trans_queue_depth = 1, + }; + ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)SPI2_HOST, &io_config, &s_panel_io_handle)); + + ESP_LOGD(TAG, "Install LCD driver"); + const ili9341_vendor_config_t vendor_config = { + .init_cmds = vendor_specific_init_default, + .init_cmds_size = sizeof(vendor_specific_init_default) / sizeof(vendor_specific_init_default[0]), + }; + esp_lcd_panel_dev_config_t panel_config = { + .reset_gpio_num = HW_LCD_RST, + .flags.reset_active_high = true, + .rgb_ele_order = HW_LCD_COLOR_SPACE, + .bits_per_pixel = HW_LCD_BITS_PER_PIXEL, + .vendor_config = (void *) &vendor_config, + }; + + ESP_LOGI(TAG, "Install ili9341 panel driver"); + ESP_ERROR_CHECK(esp_lcd_new_panel_ili9341(s_panel_io_handle, (const esp_lcd_panel_dev_config_t *)&panel_config, &s_panel_handle)); + + esp_lcd_panel_reset(s_panel_handle); + esp_lcd_panel_init(s_panel_handle); + + bool swap_xy = false; + bool mirror_x = true; + bool mirror_y = true; + lcd_get_orientation_flags(HW_LCD_BASE_SWAP_XY, HW_LCD_BASE_MIRROR_X, HW_LCD_BASE_MIRROR_Y, + rotation, &swap_xy, &mirror_x, &mirror_y); + ESP_ERROR_CHECK(esp_lcd_panel_swap_xy(s_panel_handle, swap_xy)); + ESP_ERROR_CHECK(esp_lcd_panel_mirror(s_panel_handle, mirror_x, mirror_y)); + esp_lcd_panel_disp_on_off(s_panel_handle, true); + + *io_handle = s_panel_io_handle; + *panel_handle = s_panel_handle; + + gpio_config_t bk_gpio_config = { + .mode = GPIO_MODE_OUTPUT, + .pin_bit_mask = 1ULL << HW_PIN_NUM_BK_LIGHT + }; + ESP_ERROR_CHECK(gpio_config(&bk_gpio_config)); + ESP_LOGI(TAG, "Turning on LCD backlight"); + gpio_set_level(HW_PIN_NUM_BK_LIGHT, HW_LCD_BK_LIGHT_ON_LEVEL); + + return ESP_OK; +} + +#endif diff --git a/firmware/echoear-platform-agent/components/hw_init/lcd/lcd_init_spi_without_psram.c b/firmware/echoear-platform-agent/components/hw_init/lcd/lcd_init_spi_without_psram.c new file mode 100644 index 0000000..f060e30 --- /dev/null +++ b/firmware/echoear-platform-agent/components/hw_init/lcd/lcd_init_spi_without_psram.c @@ -0,0 +1,90 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: CC0-1.0 + */ + +#include "esp_system.h" +#include "esp_lcd_gc9a01.h" +#include "hw_init.h" +#include "helpers/lcd_orientation_helper.h" + +#if CONFIG_EXAMPLE_LCD_INTERFACE_SPI_WITHOUT_PSRAM + +static const char *TAG = "hw_lcd_init"; + +#define HW_LCD_BASE_SWAP_XY (false) +#define HW_LCD_BASE_MIRROR_X (true) +#define HW_LCD_BASE_MIRROR_Y (false) + +#define HW_LCD_DATA0 (GPIO_NUM_0) +#define HW_LCD_PCLK (GPIO_NUM_1) +#define HW_LCD_CS (GPIO_NUM_7) +#define HW_LCD_DC (GPIO_NUM_2) +#define HW_LCD_RST (GPIO_NUM_NC) + +#define HW_LCD_PIXEL_CLOCK_HZ (80 * 1000 * 1000) +#define HW_LCD_COLOR_SPACE (LCD_RGB_ELEMENT_ORDER_BGR) +#define HW_LCD_BITS_PER_PIXEL (16) +#define HW_LCD_CMD_BITS (8) +#define HW_LCD_PARAM_BITS (8) +#define HW_LCD_MAX_TRANSFER_SZ (HW_LCD_H_RES * 80 * 2) + +static esp_lcd_panel_io_handle_t s_panel_io_handle; +static esp_lcd_panel_handle_t s_panel_handle; + +esp_err_t hw_lcd_init(esp_lcd_panel_handle_t *panel_handle, esp_lcd_panel_io_handle_t *io_handle, esp_lv_adapter_tear_avoid_mode_t tear_avoid_mode, esp_lv_adapter_rotation_t rotation) +{ + ESP_LOGD(TAG, "Initialize SPI bus"); + const spi_bus_config_t buscfg = { + .sclk_io_num = HW_LCD_PCLK, + .mosi_io_num = HW_LCD_DATA0, + .miso_io_num = GPIO_NUM_NC, + .quadwp_io_num = GPIO_NUM_NC, + .quadhd_io_num = GPIO_NUM_NC, + .max_transfer_sz = HW_LCD_MAX_TRANSFER_SZ, + }; + ESP_ERROR_CHECK(spi_bus_initialize(SPI2_HOST, &buscfg, SPI_DMA_CH_AUTO)); + + ESP_LOGD(TAG, "Install panel IO"); + esp_lcd_panel_io_spi_config_t io_config = { + .dc_gpio_num = HW_LCD_DC, + .cs_gpio_num = HW_LCD_CS, + .pclk_hz = HW_LCD_PIXEL_CLOCK_HZ, + .lcd_cmd_bits = HW_LCD_CMD_BITS, + .lcd_param_bits = HW_LCD_PARAM_BITS, + .spi_mode = 0, + .trans_queue_depth = 10, + }; + ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)SPI2_HOST, &io_config, &s_panel_io_handle)); + + ESP_LOGD(TAG, "Install LCD driver"); + esp_lcd_panel_dev_config_t panel_config = { + .reset_gpio_num = HW_LCD_RST, + .rgb_ele_order = HW_LCD_COLOR_SPACE, + .bits_per_pixel = HW_LCD_BITS_PER_PIXEL, + }; + + ESP_LOGI(TAG, "Install gc9a01 panel driver"); + ESP_ERROR_CHECK(esp_lcd_new_panel_gc9a01(s_panel_io_handle, &panel_config, &s_panel_handle)); + + esp_lcd_panel_reset(s_panel_handle); + esp_lcd_panel_init(s_panel_handle); + + bool swap_xy = false; + bool mirror_x = true; + bool mirror_y = false; + lcd_get_orientation_flags(HW_LCD_BASE_SWAP_XY, HW_LCD_BASE_MIRROR_X, HW_LCD_BASE_MIRROR_Y, + rotation, &swap_xy, &mirror_x, &mirror_y); + ESP_ERROR_CHECK(esp_lcd_panel_swap_xy(s_panel_handle, swap_xy)); + ESP_ERROR_CHECK(esp_lcd_panel_mirror(s_panel_handle, mirror_x, mirror_y)); + ESP_ERROR_CHECK(esp_lcd_panel_invert_color(s_panel_handle, true)); + esp_lcd_panel_disp_on_off(s_panel_handle, true); + + *io_handle = s_panel_io_handle; + *panel_handle = s_panel_handle; + + return ESP_OK; +} + +#endif diff --git a/firmware/echoear-platform-agent/components/volc_conv_ai/CMakeLists.txt b/firmware/echoear-platform-agent/components/volc_conv_ai/CMakeLists.txt new file mode 100644 index 0000000..41992b4 --- /dev/null +++ b/firmware/echoear-platform-agent/components/volc_conv_ai/CMakeLists.txt @@ -0,0 +1,49 @@ +cmake_minimum_required(VERSION 3.16) + +message(STATUS "................. ${CMAKE_SYSTEM_NAME}") +if("${CMAKE_SYSTEM_NAME}" MATCHES "(Rtos|rtos|RTOS)+") + set(PLATFORM_RTOS TRUE) + add_compile_definitions(PLATFORM_RTOS) +elseif("${CMAKE_SYSTEM_NAME}" MATCHES "(Linux|linux|LINUX)+") + set(PLATFORM_LINUX TRUE) + add_compile_definitions(PLATFORM_LINUX) +elseif("${CMAKE_SYSTEM_NAME}" MATCHES "(android|Android|ANDROID)+") + set(PLATFORM_ANDROID TRUE) + add_compile_definitions(PLATFORM_ANDROID) +elseif("${CMAKE_SYSTEM_NAME}" MATCHES "(Darwin|darwin|MACOSX|macosx|MACOS|macos)+") + set(PLATFORM_MACOS TRUE) + add_compile_definitions(PLATFORM_MACOS) + set(VOLC_CONV_AI_PLATFORM_SRCS "osal/src/macos/volc_osal.c" + CACHE INTERNAL "ConversationalAI-Embedded-Kit-2.0 platform src file") + message(STATUS "VOLC_CONV_AI_PLATFORM_SRCS: ${VOLC_CONV_AI_PLATFORM_SRCS}") +endif() + +include(${CMAKE_CURRENT_LIST_DIR}/cmake/common.cmake) +if(ESP_PLATFORM) + message(STATUS "ESP_PLATFORM is defined, including esp.cmake") + include(${CMAKE_CURRENT_LIST_DIR}/cmake/espressif.cmake) + return() +elseif(PLATFORM_LINUX) + message(STATUS "PLATFORM_LINUX is defined, including linux.cmake") + include(${CMAKE_CURRENT_LIST_DIR}/cmake/linux.cmake) +elseif(PLATFORM_MACOS) + message(STATUS "PLATFORM_MACOS is defined, including macos.cmake") + include(${CMAKE_CURRENT_LIST_DIR}/cmake/macos.cmake) + return() +endif() + +if(PLATFORM_LINUX AND ENABLE_SAMPLE) + message(STATUS "Linux平台的test_sample") + add_executable( + test_sample + sample/sample.c + ) + target_include_directories( + test_sample + PRIVATE + ${VOLC_CONV_AI_INCS} + ) + target_link_libraries(test_sample PRIVATE + volc_conv_ai_a + ) +endif() diff --git a/firmware/echoear-platform-agent/components/volc_conv_ai/README.md b/firmware/echoear-platform-agent/components/volc_conv_ai/README.md new file mode 100644 index 0000000..8e8ef4a --- /dev/null +++ b/firmware/echoear-platform-agent/components/volc_conv_ai/README.md @@ -0,0 +1,44 @@ +本目录聚焦于与云端智能体建立网络连接的代码实现,如果你想快速与云端建联并且移植到新的平台上,请参考以下教程: + +### 1 工程依赖与前期准备简介 +与云端建联的方案(高性能方案),依赖于以下基础库: + 1. mbedtls(第三方开源库) + 2. cjson(第三方开源库,但是本仓库在`src/util/cJSON`放置了一份源码) + 3. 系统抽象层代码(需要自己实现,接口头文件在`osal/inc`,已经实现的平台代码在`src`,供其他平台参考) + 4. 火山引擎volc_rtc_engine库(放置在`src/transports/high_quality/third_party/volc_rtc_engine_lite`目录下) + 5. 其他连接实现层代码,我们在`common.cmake`里已经归类好了,对于平台移植而言,直接使用即可,无需关注细节 + +### 2 构建自己的cmake脚本 +参考`linux.cmake`脚本,逐一引用上述5个部分,构建一个静态的`volc_conv_ai_a`库即可;脚本内有注释,可供参考。 +> 注意:对于具体的平台,需要修改使用平台自己的toolchain + +### 3 sample编译运行简介(方便验证) +对于transport层,我们有简单的sample做以验证。 +1. 修改`sample.c`中这一组账号: + ```c + #define CONFIG_VOLC_BOT_ID "xxxx" + #define CONFIG_VOLC_INSTANCE_ID "xxxx" + #define CONFIG_VOLC_PRODUCT_KEY "xxxx" + #define CONFIG_VOLC_PRODUCT_SECRET "xxxx" + #define CONFIG_VOLC_DEVICE_NAME "xxxx" +2. 编译 (Linux 环境下) 运行: + ```bash + # 编译 + mkdir build + cd build + cmake .. + make + + # 运行 + ./test_sample +如果运行成功有如下打印: + ```text + Volc Engine connected + [INF|volc_rtc.c:184]remote user joined xxxxx elapsed 0 ms + Volc Engine event: 0 + 火山引擎测试。 + 火山引擎测试。 + 火山引擎测试。 + 火山引擎测试。 + ``` +对于其他平台,可以参考修改CMakeLists.txt 中test_sample相关部分,进行编译运行; \ No newline at end of file diff --git a/firmware/echoear-platform-agent/components/volc_conv_ai/cmake/common.cmake b/firmware/echoear-platform-agent/components/volc_conv_ai/cmake/common.cmake new file mode 100644 index 0000000..c2f3003 --- /dev/null +++ b/firmware/echoear-platform-agent/components/volc_conv_ai/cmake/common.cmake @@ -0,0 +1,33 @@ +set(VOLC_CONV_AI_SRCS "${CMAKE_CURRENT_LIST_DIR}/../src/volc_conv_ai.c" + "${CMAKE_CURRENT_LIST_DIR}/../src/base/volc_device_manager.c" + "${CMAKE_CURRENT_LIST_DIR}/../src/util/volc_auth.c" + "${CMAKE_CURRENT_LIST_DIR}/../src/util/volc_base64.c" + "${CMAKE_CURRENT_LIST_DIR}/../src/util/volc_http.c" + "${CMAKE_CURRENT_LIST_DIR}/../src/util/volc_json.c" + "${CMAKE_CURRENT_LIST_DIR}/../third_party/mbedtls_port/tls_certificate.c" + "${CMAKE_CURRENT_LIST_DIR}/../third_party/mbedtls_port/tls_client.c" + "${CMAKE_CURRENT_LIST_DIR}/../third_party/webclient/src/webclient.c" + CACHE INTERNAL "ConversationalAI-Embedded-Kit-2.0 common src file") + +set(VOLC_CONV_AI_INCS "${CMAKE_CURRENT_LIST_DIR}/../inc" + "${CMAKE_CURRENT_LIST_DIR}/../src" + "${CMAKE_CURRENT_LIST_DIR}/../third_party/mbedtls_port" + "${CMAKE_CURRENT_LIST_DIR}/../third_party/webclient/inc" + CACHE INTERNAL "ConversationalAI-Embedded-Kit-2.0 common include dir") + +set(VOLC_CONV_AI_LOW_LOAD_SRCS "${CMAKE_CURRENT_LIST_DIR}/../src/transports/low_load/src/volc_ws.c" + "${CMAKE_CURRENT_LIST_DIR}/../src/transports/low_load/third_party/websocket/websocket.c" + CACHE INTERNAL "ConversationalAI-Embedded-Kit-2.0 low load solution src file") +set(VOLC_CONV_AI_LOW_LOAD_INCS "${CMAKE_CURRENT_LIST_DIR}/../src/transports/low_load/inc" + "${CMAKE_CURRENT_LIST_DIR}/../src/transports/low_load/third_party/websocket" + CACHE INTERNAL "ConversationalAI-Embedded-Kit-2.0 low load solution include dir") + +set(VOLC_CONV_AI_HIGH_QUALITY_SRCS "${CMAKE_CURRENT_LIST_DIR}/../src/transports/high_quality/src/volc_rtc.c" + CACHE INTERNAL "ConversationalAI-Embedded-Kit-2.0 high quality solution src file") +set(VOLC_CONV_AI_HIGH_QUALITY_INCS "${CMAKE_CURRENT_LIST_DIR}/../src/transports/high_quality/inc" + "${CMAKE_CURRENT_LIST_DIR}/../src/transports/high_quality/third_party/volc_rtc_engine_lite/inc" + CACHE INTERNAL "ConversationalAI-Embedded-Kit-2.0 high quality solution include dir") + +set(VOLC_CONV_AI_PLATFORM_INCS "${CMAKE_CURRENT_LIST_DIR}/../osal/inc" + CACHE INTERNAL "ConversationalAI-Embedded-Kit-2.0 platform include dir") + diff --git a/firmware/echoear-platform-agent/components/volc_conv_ai/cmake/espressif.cmake b/firmware/echoear-platform-agent/components/volc_conv_ai/cmake/espressif.cmake new file mode 100644 index 0000000..bb9ba1e --- /dev/null +++ b/firmware/echoear-platform-agent/components/volc_conv_ai/cmake/espressif.cmake @@ -0,0 +1,66 @@ +# Copyright (2025) Beijing Volcano Engine Technology Ltd. +# SPDX-License-Identifier: Apache-2.0 + +# ConversationalAI SDK ESP-IDF component configuration +# Converted from standard CMake to ESP-IDF component format + +# Source files +set(VOLC_SRCS ${VOLC_CONV_AI_SRCS} + "${CMAKE_CURRENT_LIST_DIR}/../osal/src/espressif/volc_osal.c" +) + +set(VOLC_INCS ${VOLC_CONV_AI_INCS} + "${CMAKE_CURRENT_LIST_DIR}/../osal/inc" +) + +if(CONFIG_VOLC_RTC_MODE) + message(STATUS "CONFIG_VOLC_RTC_MODE: ${CONFIG_VOLC_RTC_MODE}") + list(APPEND VOLC_SRCS + ${VOLC_CONV_AI_HIGH_QUALITY_SRCS} + ) + list(APPEND VOLC_INCS + ${VOLC_CONV_AI_HIGH_QUALITY_INCS} + ) +endif() + +if(CONFIG_VOLC_WS_MODE) + message(STATUS "CONFIG_VOLC_WS_MODE: ${CONFIG_VOLC_WS_MODE}") + list(APPEND VOLC_SRCS + ${VOLC_CONV_AI_LOW_LOAD_SRCS} + ) + list(APPEND VOLC_INCS + ${VOLC_CONV_AI_LOW_LOAD_INCS} + ) +endif() + +idf_component_register( + SRCS ${VOLC_SRCS} + INCLUDE_DIRS ${VOLC_INCS} + REQUIRES mbedtls json lwip esp_netif +) + +if(CONFIG_VOLC_RTC_MODE) + target_compile_definitions(${COMPONENT_LIB} PRIVATE ENABLE_RTC_MODE) + + # Add prebuilt library for RTC mode + add_prebuilt_library(volc_engine_rtc_lite + "${CMAKE_CURRENT_LIST_DIR}/../src/transports/high_quality/third_party/volc_rtc_engine_lite/libs/${CONFIG_IDF_TARGET}/libVolcEngineRTCLite.a" + REQUIRES mbedtls espressif__zlib json lwip) + target_link_libraries(${COMPONENT_LIB} INTERFACE volc_engine_rtc_lite) +endif() + +if(CONFIG_VOLC_WS_MODE) + target_compile_definitions(${COMPONENT_LIB} PRIVATE ENABLE_WS_MODE) +endif() + +# Compiler flags +target_compile_options(${COMPONENT_LIB} PRIVATE + -w + -Os + -ffunction-sections + -fdata-sections +) + +# Export the library for use by other components +set(VOLC_CONV_AI_SDK_LIBRARIES ${COMPONENT_LIB} PARENT_SCOPE) +set(VOLC_CONV_AI_SDK_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/../inc PARENT_SCOPE) diff --git a/firmware/echoear-platform-agent/components/volc_conv_ai/cmake/linux.cmake b/firmware/echoear-platform-agent/components/volc_conv_ai/cmake/linux.cmake new file mode 100644 index 0000000..566bf19 --- /dev/null +++ b/firmware/echoear-platform-agent/components/volc_conv_ai/cmake/linux.cmake @@ -0,0 +1,116 @@ +message(STATUS "配置Linux平台的volc_conv_ai库") + +option(ENABLE_RTC_MODE "Enable Conv AI RTC mode" ON) +option(ENABLE_WS_MODE "Enable Conv AI WS mode" OFF) +option(ENABLE_CJSON "Enable cJSON" ON) +option(ENABLE_MBEDTLS "Enable Mbedtls" ON) +option(ENABLE_SAMPLE "Enable sample" ON) + +# 系统抽象层代码 +if(NOT DEFINED VOLC_CONV_AI_PLATFORM_SRCS) + set(VOLC_CONV_AI_PLATFORM_SRCS + "${CMAKE_CURRENT_LIST_DIR}/../osal/src/linux/volc_osal.c" + CACHE INTERNAL "ConversationalAI-Embedded-Kit-2.0 platform src file") +endif() + +# mbedtls构建 +set(MBEDTLS_PREBUILT_DIR ${CMAKE_CURRENT_LIST_DIR}/../third_party/prebuilt/mbedtls) +set(MBEDTLS_LIBRARY_FILE "${MBEDTLS_PREBUILT_DIR}/lib/libmbedtls.a") +set(MBEDTLS_SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/../third_party/mbedtls) + +# 使用第三方开源库构建 +if(NOT EXISTS "${MBEDTLS_LIBRARY_FILE}" AND NOT EXISTS "${MBEDTLS_SOURCE_DIR}/CMakeLists.txt") + message(STATUS "start download and compile mbedtls...") + include(ExternalProject) + ExternalProject_Add( + mbedtls + GIT_REPOSITORY https://github.com/ARMmbed/mbedtls.git + GIT_TAG v3.6.3 + PREFIX ${CMAKE_CURRENT_BINARY_DIR}/build + SOURCE_DIR ${MBEDTLS_SOURCE_DIR} + CMAKE_ARGS + -DCMAKE_INSTALL_PREFIX=${MBEDTLS_PREBUILT_DIR} + -DUSE_SHARED_MBEDTLS_LIBRARY=${BUILD_SHARED} + -DCMAKE_BUILD_TYPE=Debug + -DCMAKE_MACOSX_RPATH=${CMAKE_MACOSX_RPATH} + -DENABLE_TESTING=OFF + -DENABLE_PROGRAMS=OFF + -DCMAKE_C_FLAGS=${CMAKE_C_FLAGS} -Wno-unused-but-set-parameter + -DMBEDTLS_FATAL_WARNINGS=OFF + + BUILD_ALWAYS FALSE + TEST_COMMAND "" + ) +else() + message(STATUS "mbedtls library already exists, skip download and compile") +endif() + +# volc_conv_ai_a库的构建,注意此处用源码的形式包括了 cJSON库 +add_library(volc_conv_ai_a STATIC + ${VOLC_CONV_AI_SRCS} + ${VOLC_CONV_AI_PLATFORM_SRCS} + "${CMAKE_CURRENT_LIST_DIR}/../src/util/cJSON/cJSON.c" +) + +# cJSON库 头文件目录 +if(ENABLE_CJSON) + target_include_directories(volc_conv_ai_a PUBLIC + "${CMAKE_CURRENT_LIST_DIR}/../src/util/cJSON/" + ) +endif() + +add_library(mbedtls_static STATIC IMPORTED GLOBAL) +add_library(mbedx509_static STATIC IMPORTED GLOBAL) +add_library(zlib_static STATIC IMPORTED GLOBAL) +add_library(mbedcrypto_static STATIC IMPORTED GLOBAL) + +set_target_properties(zlib_static PROPERTIES + IMPORTED_LOCATION ${CMAKE_CURRENT_LIST_DIR}/../third_party/prebuilt/zlib/lib/libz.a +) +set_target_properties(mbedtls_static PROPERTIES + IMPORTED_LOCATION ${MBEDTLS_PREBUILT_DIR}/lib/libmbedtls.a +) +set_target_properties(mbedx509_static PROPERTIES + IMPORTED_LOCATION ${MBEDTLS_PREBUILT_DIR}/lib/libmbedx509.a +) +set_target_properties(mbedcrypto_static PROPERTIES + IMPORTED_LOCATION ${MBEDTLS_PREBUILT_DIR}/lib/libmbedcrypto.a +) + +# 包括 火山引擎RTC Engine Lite库 +if(ENABLE_RTC_MODE) + add_definitions(-DENABLE_RTC_MODE) + target_sources(volc_conv_ai_a PRIVATE + ${VOLC_CONV_AI_HIGH_QUALITY_SRCS} + ) + message(STATUS "use high quality RTC mode") + set(RTC_LIB_DIR ${CMAKE_CURRENT_LIST_DIR}/../src/transports/high_quality/third_party/volc_rtc_engine_lite/libs/linux_x64) + message(STATUS "RTC_LIB_DIR: ${RTC_LIB_DIR}") + find_library(RTC_LIBRARIES NAMES VolcEngineRTCLite REQUIRED NO_CMAKE_FIND_ROOT_PATH PATHS ${RTC_LIB_DIR}) + target_link_libraries(volc_conv_ai_a PRIVATE + ${RTC_LIBRARIES} + ) + target_include_directories(volc_conv_ai_a PUBLIC + ${VOLC_CONV_AI_HIGH_QUALITY_INCS} + ) +endif() + +if(ENABLE_MBEDTLS) + target_include_directories(volc_conv_ai_a PUBLIC ${MBEDTLS_PREBUILT_DIR}/include) + + target_link_libraries(volc_conv_ai_a PRIVATE + mbedtls_static + mbedx509_static + mbedcrypto_static + zlib_static + ) + + target_compile_definitions(volc_conv_ai_a PRIVATE ENABLE_MBEDTLS) +endif() + +target_link_libraries(volc_conv_ai_a PUBLIC pthread) + +target_include_directories(volc_conv_ai_a PUBLIC + ${VOLC_CONV_AI_INCS} + ${VOLC_CONV_AI_PLATFORM_INCS} +) \ No newline at end of file diff --git a/firmware/echoear-platform-agent/components/volc_conv_ai/cmake/macos.cmake b/firmware/echoear-platform-agent/components/volc_conv_ai/cmake/macos.cmake new file mode 100644 index 0000000..4f9a732 --- /dev/null +++ b/firmware/echoear-platform-agent/components/volc_conv_ai/cmake/macos.cmake @@ -0,0 +1,133 @@ +message(STATUS "配置macOS平台的volc_conv_ai库") + +option(ENABLE_RTC_MODE "Enable Conv AI RTC mode" OFF) +option(ENABLE_WS_MODE "Enable Conv AI WS mode" OFF) +option(ENABLE_CJSON "Enable cJSON" ON) +option(ENABLE_MBEDTLS "Enable Mbedtls" ON) + +if(NOT DEFINED VOLC_CONV_AI_PLATFORM_SRCS) + set(VOLC_CONV_AI_PLATFORM_SRCS + "${CMAKE_CURRENT_LIST_DIR}/../osal/src/macos/volc_osal.c" + CACHE INTERNAL "ConversationalAI-Embedded-Kit-2.0 platform src file") +endif() + +set(MBEDTLS_PREBUILT_DIR ${CMAKE_CURRENT_LIST_DIR}/../third_party/prebuilt/mbedtls) +set(MBEDTLS_LIBRARY_FILE "${MBEDTLS_PREBUILT_DIR}/lib/libmbedtls.a") +set(MBEDTLS_SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/../third_party/mbedtls) + +if(NOT EXISTS "${MBEDTLS_LIBRARY_FILE}" AND NOT EXISTS "${MBEDTLS_SOURCE_DIR}/CMakeLists.txt") + message(STATUS "start download and compile mbedtls...") + include(ExternalProject) + ExternalProject_Add( + mbedtls + GIT_REPOSITORY https://github.com/ARMmbed/mbedtls.git + GIT_TAG v3.6.3 + PREFIX ${CMAKE_CURRENT_BINARY_DIR}/build + SOURCE_DIR ${MBEDTLS_SOURCE_DIR} + CMAKE_ARGS + -DCMAKE_INSTALL_PREFIX=${MBEDTLS_PREBUILT_DIR} + -DUSE_SHARED_MBEDTLS_LIBRARY=${BUILD_SHARED} + -DCMAKE_BUILD_TYPE=Debug + -DCMAKE_MACOSX_RPATH=${CMAKE_MACOSX_RPATH} + -DENABLE_TESTING=OFF + -DENABLE_PROGRAMS=OFF + -DCMAKE_C_FLAGS=${CMAKE_C_FLAGS} -Wno-unused-but-set-parameter + -DMBEDTLS_FATAL_WARNINGS=OFF + + BUILD_ALWAYS FALSE + TEST_COMMAND "" + ) +else() + message(STATUS "mbedtls library already exists, skip download and compile") +endif() + +add_library(volc_conv_ai_a STATIC + ${VOLC_CONV_AI_SRCS} + ${VOLC_CONV_AI_PLATFORM_SRCS} +) + +if(ENABLE_WS_MODE) + message(STATUS "use low load WS mode") + target_sources(volc_conv_ai_a PRIVATE + ${VOLC_CONV_AI_LOW_LOAD_SRCS} + ) +endif() + +add_library(mbedtls_static STATIC IMPORTED GLOBAL) +add_library(mbedx509_static STATIC IMPORTED GLOBAL) +add_library(zlib_static STATIC IMPORTED GLOBAL) +add_library(mbedcrypto_static STATIC IMPORTED GLOBAL) + +set_target_properties(zlib_static PROPERTIES + IMPORTED_LOCATION ${CMAKE_CURRENT_LIST_DIR}/../third_party/prebuilt/zlib/lib/libz.a +) +set_target_properties(mbedtls_static PROPERTIES + IMPORTED_LOCATION ${MBEDTLS_PREBUILT_DIR}/lib/libmbedtls.a +) +set_target_properties(mbedx509_static PROPERTIES + IMPORTED_LOCATION ${MBEDTLS_PREBUILT_DIR}/lib/libmbedx509.a +) +set_target_properties(mbedcrypto_static PROPERTIES + IMPORTED_LOCATION ${MBEDTLS_PREBUILT_DIR}/lib/libmbedcrypto.a +) + +if(ENABLE_RTC_MODE) + target_sources(volc_conv_ai_a PRIVATE + ${VOLC_CONV_AI_HIGH_QUALITY_SRCS} + ) + message(STATUS "use high quality RTC mode") + set(RTC_LIB_DIR ${CMAKE_CURRENT_LIST_DIR}/../src/transports/high_quality/third_party/volc_rtc_engine_lite/libs/macos) + message(STATUS "RTC_LIB_DIR: ${RTC_LIB_DIR}") + find_library(RTC_LIBRARIES NAMES VolcEngineRTCLite REQUIRED NO_CMAKE_FIND_ROOT_PATH PATHS ${RTC_LIB_DIR}) + + target_link_libraries(volc_conv_ai_a PRIVATE + ${RTC_LIBRARIES} + ) +endif() + +if(ENABLE_WS_MODE) + target_compile_definitions(volc_conv_ai_a PRIVATE ENABLE_WS_MODE) +endif() + +if(ENABLE_RTC_MODE) + target_compile_definitions(volc_conv_ai_a PRIVATE ENABLE_RTC_MODE) +endif() + +if(ENABLE_CJSON) + target_compile_definitions(volc_conv_ai_a PRIVATE ENABLE_CJSON) +endif() + +if(ENABLE_MBEDTLS) + target_include_directories(volc_conv_ai_a PUBLIC ${MBEDTLS_PREBUILT_DIR}/include) + + target_link_libraries(volc_conv_ai_a PRIVATE + mbedtls_static + mbedx509_static + mbedcrypto_static + zlib_static + ) + + target_compile_definitions(volc_conv_ai_a PRIVATE ENABLE_MBEDTLS) +endif() + +target_include_directories(volc_conv_ai_a PUBLIC + ${VOLC_CONV_AI_INCS} + ${VOLC_CONV_AI_PLATFORM_INCS} +) + +if(ENABLE_WS_MODE) + target_include_directories(volc_conv_ai_a PUBLIC + ${VOLC_CONV_AI_LOW_LOAD_INCS} + ) +endif() + +if(ENABLE_RTC_MODE) + target_include_directories(volc_conv_ai_a PUBLIC + ${VOLC_CONV_AI_HIGH_QUALITY_INCS} + ) +endif() + +set_target_properties(volc_conv_ai_a PROPERTIES + ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib" + LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib" +) diff --git a/firmware/echoear-platform-agent/components/volc_conv_ai/inc/volc_conv_ai.h b/firmware/echoear-platform-agent/components/volc_conv_ai/inc/volc_conv_ai.h new file mode 100644 index 0000000..4a82b93 --- /dev/null +++ b/firmware/echoear-platform-agent/components/volc_conv_ai/inc/volc_conv_ai.h @@ -0,0 +1,170 @@ +// Copyright (2025) Beijing Volcano Engine Technology Ltd. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef __VOLC_CONV_AI_H__ +#define __VOLC_CONV_AI_H__ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define __volc_rt_api__ __attribute__((visibility("default"))) + +#define VOLC_VERSION_MAJOR 1 +#define VOLC_VERSION_MINOR 0 +#define VOLC_VERSION_PATCH 0 +#define VOLC_VERSION_CHECK(major, minor, patch) ((major * 10000U) + (minor * 100U) + patch) +#define VOLC_VERSION VOLC_VERSION_CHECK(VOLC_VERSION_MAJOR, VOLC_VERSION_MINOR, VOLC_VERSION_PATCH) + +typedef enum { + VOLC_ERR_NO_ERROR = 0, + VOLC_ERR_FAILED = -1, + VOLC_ERR_LICENSE_EXHAUSTED = -10, + VOLC_ERR_LICENSE_EXPIRED = -11, +} volc_error_code_e; + +typedef enum { + VOLC_AUDIO_DATA_TYPE_UNKNOWN = 0, + VOLC_AUDIO_DATA_TYPE_OPUS = 1, + VOLC_AUDIO_DATA_TYPE_G722 = 2, + VOLC_AUDIO_DATA_TYPE_AACLC = 3, + VOLC_AUDIO_DATA_TYPE_G711A = 4, + VOLC_AUDIO_DATA_TYPE_PCM = 5, + VOLC_AUDIO_DATA_TYPE_G711U = 6, +} volc_audio_data_type_e; + +typedef enum { + VOLC_AUDIO_CODEC_TYPE_UNKNOWN = 0, + VOLC_AUDIO_CODEC_TYPE_OPUS = 1, + VOLC_AUDIO_CODEC_TYPE_G722 = 2, + VOLC_AUDIO_CODEC_TYPE_G711A = 3, + VOLC_AUDIO_CODEC_TYPE_PCM = 4, + VOLC_AUDIO_CODEC_TYPE_G711U = 5, +} volc_audio_codec_type_e; + +typedef struct { + volc_audio_data_type_e data_type; + // uint16_t sent_ts; + bool commit; +} volc_audio_frame_info_t; + +typedef enum { + VOLC_VIDEO_CODEC_TYPE_UNKNOWN = 0, + VOLC_VIDEO_CODEC_TYPE_H264 = 1, + VOLC_VIDEO_CODEC_TYPE_H265 = 2, + VOLC_VIDEO_CODEC_TYPE_JPEG = 3, +} volc_video_codec_type_e; + +typedef enum { + VOLC_VIDEO_DATA_TYPE_UNKNOWN = 0, + VOLC_VIDEO_DATA_TYPE_H264 = 1, + VOLC_VIDEO_DATA_TYPE_H265 = 2, + VOLC_VIDEO_DATA_TYPE_JPEG = 3, +} volc_video_data_type_e; + +typedef struct { + volc_video_data_type_e data_type; +} volc_video_frame_info_t; + +typedef enum { + VOLC_CONV_STATUS_LISTENING = 1, + VOLC_CONV_STATUS_THINKING, + VOLC_CONV_STATUS_ANSWERING, + VOLC_CONV_STATUS_INTERRUPTED, + VOLC_CONV_STATUS_ANSWER_FINISH, +} volc_conv_status_e; + +typedef struct { + // place holder + bool is_binary; +} volc_message_info_t; + +typedef enum { + VOLC_DATA_TYPE_AUDIO = 0, + VOLC_DATA_TYPE_VIDEO, + VOLC_DATA_TYPE_MESSAGE, + VOLC_DATA_TYPE_CNT, +} volc_data_type_e; + +typedef struct { + volc_data_type_e type; + union { + volc_audio_frame_info_t audio; + volc_video_frame_info_t video; + volc_message_info_t message; + } info; +} volc_data_info_t; + +typedef enum { + VOLC_EV_UNKNOWN = 0, // 未知事件 + VOLC_EV_CONNECTED, // 成功连接 + VOLC_EV_DISCONNECTED, // 断开连接 + VOLC_EV_QUOTA_EXCEEDED, +} volc_event_code_e; + +typedef struct { + volc_event_code_e code; // 包含错误码、告警码、关键事件码等 + union { + int placeholder; + } data; // 事件数据,具体内容根据event_code而定 +} volc_event_t; + +typedef enum { + VOLC_MODE_RTC = 0, + VOLC_MODE_WS = 1, + VOLC_MODE_UNKNOWN, +} volc_mode_e; +typedef struct { + volc_mode_e mode; + char* bot_id; + char* params; +} volc_opt_t; + +typedef enum { + VOLC_AGENT_TYPE_TTS = 0, + VOLC_AGENT_TYPE_LLM = 1, + VOLC_AGENT_TYPE_UNKNOW, +} volc_agent_type_e; + +typedef void* volc_engine_t; + +typedef struct { + void (*on_volc_event)(volc_engine_t handle, volc_event_t* event, void* user_data); + void (*on_volc_conversation_status)(volc_engine_t handle, volc_conv_status_e status, void* user_data); + void (*on_volc_audio_data)(volc_engine_t handle, const void* data_ptr, size_t data_len, volc_audio_frame_info_t* info_ptr, void* user_data); + void (*on_volc_video_data)(volc_engine_t handle, const void* data_ptr, size_t data_len, volc_video_frame_info_t* info_ptr, void* user_data); + void (*on_volc_message_data)(volc_engine_t handle, const void* data_ptr, size_t data_len, volc_message_info_t* info_ptr, void* user_data); +} volc_event_handler_t; + +__volc_rt_api__ const char* volc_get_version(void); + +__volc_rt_api__ const char* volc_err_2_str(int err_code); + +__volc_rt_api__ int volc_create(volc_engine_t* handle, const char* config_json, volc_event_handler_t* event_handler, void* user_data); + +__volc_rt_api__ void volc_destroy(volc_engine_t handle); + +__volc_rt_api__ int volc_start(volc_engine_t handle, volc_opt_t* opt); + +__volc_rt_api__ int volc_stop(volc_engine_t handle); + +__volc_rt_api__ int volc_update(volc_engine_t handle, const void* data_ptr, size_t data_len); + +__volc_rt_api__ int volc_send_audio_data(volc_engine_t handle, const void* data_ptr, size_t data_len, volc_audio_frame_info_t* info_ptr); + +__volc_rt_api__ int volc_send_video_data(volc_engine_t handle, const void* data_ptr, size_t data_len, volc_video_frame_info_t* info_ptr); + +__volc_rt_api__ int volc_send_message(volc_engine_t handle, const void* data_ptr, size_t data_len, volc_message_info_t* info_ptr); + +__volc_rt_api__ int volc_send_text_to_agent(volc_engine_t handle, const char* text, volc_agent_type_e type, int interrupt_mode); + +__volc_rt_api__ int volc_interrupt(volc_engine_t handle); + +#ifdef __cplusplus +} +#endif +#endif /* __VOLC_CONV_AI_H__ */ diff --git a/firmware/echoear-platform-agent/components/volc_conv_ai/osal/inc/volc_osal.h b/firmware/echoear-platform-agent/components/volc_conv_ai/osal/inc/volc_osal.h new file mode 100644 index 0000000..35f4237 --- /dev/null +++ b/firmware/echoear-platform-agent/components/volc_conv_ai/osal/inc/volc_osal.h @@ -0,0 +1,62 @@ +// Copyright (2025) Beijing Volcano Engine Technology Ltd. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef __CONV_AI_OSAL_VOLC_OSAL_H__ +#define __CONV_AI_OSAL_VOLC_OSAL_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +void* volc_osal_malloc(size_t size); +void* volc_osal_calloc(size_t num, size_t size); +void* volc_osal_realloc(void* ptr, size_t new_size); +void volc_osal_free(void* ptr); +#define HAL_SAFE_FREE(ptr) do { if (ptr) { volc_osal_free(ptr); ptr = NULL; } } while (0) + +typedef void* volc_osal_mutex_t; +volc_osal_mutex_t volc_osal_mutex_create(void); +void volc_osal_mutex_lock(volc_osal_mutex_t mutex); +void volc_osal_mutex_unlock(volc_osal_mutex_t mutex); +void volc_osal_mutex_destroy(volc_osal_mutex_t mutex); + +typedef void* volc_osal_sem_t; +volc_osal_sem_t volc_osal_sem_create(void); +void volc_osal_sem_wait(volc_osal_sem_t sem); +void volc_osal_sem_post(volc_osal_sem_t sem); +void volc_osal_sem_destroy(volc_osal_sem_t sem); + +uint64_t volc_osal_get_time_ms(void); + +int volc_osal_get_uuid(char* uuid, size_t size); + +#define THREAD_NAME_MAX_LEN 16 +typedef void* volc_osal_tid_t; +typedef struct { + char name[THREAD_NAME_MAX_LEN]; + int priority; + int stack_size; + int bind_cpu; + int stack_in_ext; +} volc_osal_thread_param_t; + +#if defined(PLATFORM_MACOS)|| (PLATFORM_LINUX) +int volc_osal_thread_create(volc_osal_tid_t* thread, const volc_osal_thread_param_t* param, void* (*start_routine)(void *), void* args); +#else +int volc_osal_thread_create(volc_osal_tid_t* thread, const volc_osal_thread_param_t* param, void (*start_routine)(void *), void* args); +#endif +int volc_osal_thread_detach(volc_osal_tid_t thread); +void volc_osal_thread_exit(volc_osal_tid_t thread); +void volc_osal_thread_sleep(int time_ms); +void volc_osal_thread_destroy(volc_osal_tid_t thread); + +int volc_osal_get_platform_info(char* info, size_t size); +int volc_osal_fill_random(uint8_t* data, size_t size); + +#ifdef __cplusplus +} +#endif +#endif /* __CONV_AI_OSAL_VOLC_OSAL_H__ */ diff --git a/firmware/echoear-platform-agent/components/volc_conv_ai/osal/src/espressif/volc_osal.c b/firmware/echoear-platform-agent/components/volc_conv_ai/osal/src/espressif/volc_osal.c new file mode 100644 index 0000000..dcd2cc6 --- /dev/null +++ b/firmware/echoear-platform-agent/components/volc_conv_ai/osal/src/espressif/volc_osal.c @@ -0,0 +1,184 @@ +// Copyright (2025) Beijing Volcano Engine Technology Ltd. +// SPDX-License-Identifier: Apache-2.0 + +#include "volc_osal.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" + +void* volc_osal_malloc(size_t size) { + return heap_caps_malloc(size,MALLOC_CAP_SPIRAM | MALLOC_CAP_DEFAULT); +} + +void* volc_osal_calloc(size_t num, size_t size) { + return heap_caps_calloc(num,size,MALLOC_CAP_SPIRAM | MALLOC_CAP_DEFAULT); +} + +void* volc_osal_realloc(void* ptr, size_t new_size) { + return heap_caps_realloc(ptr,new_size,MALLOC_CAP_SPIRAM | MALLOC_CAP_DEFAULT); +} + +void volc_osal_free(void* ptr) { + heap_caps_free(ptr); +} + +volc_osal_mutex_t volc_osal_mutex_create(void) { + pthread_mutex_t* p_mutex = NULL; + pthread_mutexattr_t attr; + + p_mutex = (pthread_mutex_t *)volc_osal_calloc(1, sizeof(pthread_mutex_t)); + if (NULL == p_mutex) { + return NULL; + } + + if (0 != pthread_mutexattr_init(&attr) || + 0 != pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL) || + 0 != pthread_mutex_init(p_mutex, &attr)) { + volc_osal_free(p_mutex); + return NULL; + } + + return (volc_osal_mutex_t)p_mutex; +} +void volc_osal_mutex_lock(volc_osal_mutex_t mutex) { + pthread_mutex_lock((pthread_mutex_t *)mutex); +} + +void volc_osal_mutex_unlock(volc_osal_mutex_t mutex) { + pthread_mutex_unlock((pthread_mutex_t *)mutex); +} + +void volc_osal_mutex_destroy(volc_osal_mutex_t mutex) { + pthread_mutex_t* p_mutex = (pthread_mutex_t *)mutex; + if (NULL == p_mutex) { + return; + } + pthread_mutex_destroy(p_mutex); + volc_osal_free(p_mutex); +} + +volc_osal_sem_t volc_osal_sem_create() { + SemaphoreHandle_t sem = xSemaphoreCreateBinary(); + if (NULL == sem) { + return NULL; + } + return (volc_osal_sem_t)sem; +} + +void volc_osal_sem_wait(volc_osal_sem_t sem) { + if (NULL == sem) { + return; + } + xSemaphoreTake((SemaphoreHandle_t)sem, portMAX_DELAY); +} + +void volc_osal_sem_post(volc_osal_sem_t sem) { + if (NULL == sem) { + return; + } + xSemaphoreGive((SemaphoreHandle_t)sem); +} + +void volc_osal_sem_destroy(volc_osal_sem_t sem) { + if (NULL == sem) { + return; + } + vSemaphoreDelete((SemaphoreHandle_t)sem); +} + +uint64_t volc_osal_get_time_ms(void) { + struct timespec now_time; + clock_gettime(CLOCK_REALTIME, &now_time); + return (uint64_t)now_time.tv_sec * 1000 + (uint64_t)now_time.tv_nsec / 1000000; +} + +int volc_osal_get_uuid(char* uuid, size_t size) { + esp_netif_t *netif = NULL; + + while ((netif = esp_netif_next_unsafe(netif)) != NULL) { + uint8_t hwaddr[6] = {0}; + esp_netif_get_mac(netif, hwaddr); + snprintf(uuid, size,"%02X%02X%02X%02X%02X%02X",hwaddr[0], hwaddr[1], hwaddr[2], hwaddr[3], hwaddr[4], hwaddr[5]); + } + return 0; +} + +int volc_osal_thread_create(volc_osal_tid_t* thread, const volc_osal_thread_param_t* param, void (*start_routine)(void *), void* args) { + int ret = 0; + int stack_size = 0; + int priority = 0; + BaseType_t core_id = 0; + TaskHandle_t* handle = NULL; + if (NULL == thread || NULL == start_routine) { + return -1; + } + + if (NULL != param) { + stack_size = param->stack_size <= 0 ? 8192 : param->stack_size; + priority = param->priority <= 0 ? 3 : param->priority; + core_id = 1; + } else { + stack_size = 8192; + priority = 3; + core_id = tskNO_AFFINITY; + } + handle = (TaskHandle_t *)volc_osal_calloc(1, sizeof(TaskHandle_t)); + if (NULL == handle) { + return -1; + } + *thread = (volc_osal_tid_t *)handle; + + if(param && param->stack_in_ext) { + ret = xTaskCreatePinnedToCoreWithCaps(start_routine, param->name, stack_size, args, priority, handle, core_id, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + } else { + ret = xTaskCreatePinnedToCore(start_routine, param->name, stack_size, args, priority, handle, core_id); + } + if (pdPASS != ret) { + volc_osal_free(handle); + *thread = NULL; + return -1; + } + return 0; +} +int volc_osal_thread_detach(volc_osal_tid_t thread) { + return pthread_detach((pthread_t)thread); +} + +void volc_osal_thread_exit(volc_osal_tid_t thread) { + vTaskDelete(NULL); +} + +void volc_osal_thread_sleep(int time_ms) { + vTaskDelay(pdMS_TO_TICKS(time_ms)); +} + +void volc_osal_thread_destroy(volc_osal_tid_t thread) { + if (NULL == thread) { + return; + } + volc_osal_free(thread); +} + +int volc_osal_get_platform_info(char* info, size_t size) { + if (NULL == info || size <= 0) { + return -1; + } + snprintf(info, size, "esp32"); + return 0; +} + +int volc_osal_fill_random(uint8_t* data, size_t size) { + if (NULL == data || size <= 0) { + return -1; + } + esp_fill_random((void *)data, size); + return 0; +} diff --git a/firmware/echoear-platform-agent/components/volc_conv_ai/osal/src/linux/volc_osal.c b/firmware/echoear-platform-agent/components/volc_conv_ai/osal/src/linux/volc_osal.c new file mode 100644 index 0000000..e6671be --- /dev/null +++ b/firmware/echoear-platform-agent/components/volc_conv_ai/osal/src/linux/volc_osal.c @@ -0,0 +1,168 @@ +// Copyright (2025) Beijing Volcano Engine Technology Ltd. +// SPDX-License-Identifier: MIT + +#include "volc_osal.h" + +#include +#include +#include +#include +#include +#include +#include +#include +// #include +#include +#include +#include + +#include + +void* volc_osal_malloc(size_t size) { + return malloc(size); +} + +void* volc_osal_calloc(size_t num, size_t size) { + return calloc(num, size); +} + +void* volc_osal_realloc(void* ptr, size_t new_size) { + return realloc(ptr, new_size); +} + +void volc_osal_free(void* ptr) { + free(ptr); +} + +volc_osal_mutex_t volc_osal_mutex_create(void) { + pthread_mutex_t* p_mutex = NULL; + pthread_mutexattr_t attr; + + p_mutex = (pthread_mutex_t *)volc_osal_calloc(1, sizeof(pthread_mutex_t)); + if (NULL == p_mutex) { + return NULL; + } + + if (0 != pthread_mutexattr_init(&attr) || + 0 != pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL) || + 0 != pthread_mutex_init(p_mutex, &attr)) { + volc_osal_free(p_mutex); + return NULL; + } + + return (volc_osal_mutex_t)p_mutex; +} +void volc_osal_mutex_lock(volc_osal_mutex_t mutex) { + pthread_mutex_lock((pthread_mutex_t *)mutex); +} + +void volc_osal_mutex_unlock(volc_osal_mutex_t mutex) { + pthread_mutex_unlock((pthread_mutex_t *)mutex); +} + +void volc_osal_mutex_destroy(volc_osal_mutex_t mutex) { + pthread_mutex_t* p_mutex = (pthread_mutex_t *)mutex; + if (NULL == p_mutex) { + return; + } + pthread_mutex_destroy(p_mutex); + volc_osal_free(p_mutex); +} + +uint64_t volc_osal_get_time_ms(void) { + struct timespec now_time; + clock_gettime(CLOCK_REALTIME, &now_time); + return (uint64_t)now_time.tv_sec * 1000 + (uint64_t)now_time.tv_nsec / 1000000; +} + +int volc_osal_get_uuid(char* uuid, size_t size) { +int ret = -1; + struct ifaddrs* ifa = NULL; + struct ifaddrs* ifa_temp = NULL; + + if (uuid == NULL || size < 13) { + errno = EINVAL; + goto err_out_label; + } + + if (getifaddrs(&ifa) != 0) { + perror("getifaddrs failed"); + goto err_out_label; + } + + for (ifa_temp = ifa; ifa_temp != NULL; ifa_temp = ifa_temp->ifa_next) { + if (ifa_temp->ifa_addr == NULL) continue; + + if (ifa_temp->ifa_addr->sa_family != AF_PACKET) continue; + struct sockaddr_ll* sll = (struct sockaddr_ll*)ifa_temp->ifa_addr; + if (sll->sll_halen != 6 || (ifa_temp->ifa_flags & IFF_LOOPBACK)) continue; + unsigned char* mac = sll->sll_addr; + + + snprintf(uuid, size, "%02X%02X%02X%02X%02X%02X", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + ret = 0; + break; + } + + if (ret != 0) { + fprintf(stderr, "no valid non-loopback MAC address found\n"); + errno = ENODEV; + } + +err_out_label: + if (ifa != NULL) freeifaddrs(ifa); + return ret; +} +int volc_osal_thread_create(volc_osal_tid_t* thread, const volc_osal_thread_param_t* param, void* (*start_routine)(void *), void* args) { + int ret = 0; + pthread_t pthread; + if (NULL == thread || NULL == start_routine) { + return -1; + } + ret = pthread_create(&pthread, NULL, start_routine, args); + if (0 != ret) { + goto err_out_label; + } + *thread = (volc_osal_tid_t)pthread; + return 0; +err_out_label: + return -1; +} +int volc_osal_thread_detach(volc_osal_tid_t thread) { + return pthread_detach((pthread_t)thread); +} + +void volc_osal_thread_exit(volc_osal_tid_t thread) { + pthread_exit((pthread_t)thread); +} + +void volc_osal_thread_sleep(int time_ms) { + usleep(time_ms * 1000); +} + +void volc_osal_thread_destroy(volc_osal_tid_t thread) { + (void)thread; +} + +int volc_osal_get_platform_info(char* info, size_t size) { + if (NULL == info || size <= 0) { + return -1; + } + snprintf(info, size, "macos"); + return 0; +} + +int volc_osal_fill_random(uint8_t* data, size_t size) { + if (NULL == data || size <= 0) { + return -1; + } + while (size > 0) { + uint32_t word = (uint32_t) volc_osal_get_time_ms(); + uint32_t to_copy = sizeof(word) < size ? sizeof(word) : size; + memcpy(data, &word, to_copy); + data += to_copy; + size -= to_copy; + } + return 0; +} diff --git a/firmware/echoear-platform-agent/components/volc_conv_ai/osal/src/macos/volc_osal.c b/firmware/echoear-platform-agent/components/volc_conv_ai/osal/src/macos/volc_osal.c new file mode 100644 index 0000000..8e5c380 --- /dev/null +++ b/firmware/echoear-platform-agent/components/volc_conv_ai/osal/src/macos/volc_osal.c @@ -0,0 +1,146 @@ +// Copyright (2025) Beijing Volcano Engine Technology Ltd. +// SPDX-License-Identifier: MIT + +#include "volc_osal.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void* volc_osal_malloc(size_t size) { + return malloc(size); +} + +void* volc_osal_calloc(size_t num, size_t size) { + return calloc(num, size); +} + +void* volc_osal_realloc(void* ptr, size_t new_size) { + return realloc(ptr, new_size); +} + +void volc_osal_free(void* ptr) { + free(ptr); +} + +volc_osal_mutex_t volc_osal_mutex_create(void) { + pthread_mutex_t* p_mutex = NULL; + pthread_mutexattr_t attr; + + p_mutex = (pthread_mutex_t *)volc_osal_calloc(1, sizeof(pthread_mutex_t)); + if (NULL == p_mutex) { + return NULL; + } + + if (0 != pthread_mutexattr_init(&attr) || + 0 != pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL) || + 0 != pthread_mutex_init(p_mutex, &attr)) { + volc_osal_free(p_mutex); + return NULL; + } + + return (volc_osal_mutex_t)p_mutex; +} +void volc_osal_mutex_lock(volc_osal_mutex_t mutex) { + pthread_mutex_lock((pthread_mutex_t *)mutex); +} + +void volc_osal_mutex_unlock(volc_osal_mutex_t mutex) { + pthread_mutex_unlock((pthread_mutex_t *)mutex); +} + +void volc_osal_mutex_destroy(volc_osal_mutex_t mutex) { + pthread_mutex_t* p_mutex = (pthread_mutex_t *)mutex; + if (NULL == p_mutex) { + return; + } + pthread_mutex_destroy(p_mutex); + volc_osal_free(p_mutex); +} + +uint64_t volc_osal_get_time_ms(void) { + struct timespec now_time; + clock_gettime(CLOCK_REALTIME, &now_time); + return (uint64_t)now_time.tv_sec * 1000 + (uint64_t)now_time.tv_nsec / 1000000; +} + +int volc_osal_get_uuid(char* uuid, size_t size) { + int ret = 0; + struct ifaddrs* ifa = NULL; + struct ifaddrs* ifa_temp = NULL; + + getifaddrs(&ifa); + for (ifa_temp = ifa; ifa_temp != NULL; ifa_temp = ifa_temp->ifa_next) { + if (ifa_temp->ifa_addr != NULL && ifa_temp->ifa_addr->sa_family == AF_LINK) { + struct sockaddr_dl* sdl = (struct sockaddr_dl*)ifa_temp->ifa_addr; + if (sdl->sdl_alen == 6) { + unsigned char* mac = (unsigned char*)LLADDR(sdl); + if (strncmp(ifa_temp->ifa_name, "lo", 2) != 0) { + snprintf(uuid, size, "%02X%02X%02X%02X%02X%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + break; + } + } + } + } + freeifaddrs(ifa); +err_out_label: + return ret; +} +int volc_osal_thread_create(volc_osal_tid_t* thread, const volc_osal_thread_param_t* param, void* (*start_routine)(void *), void* args) { + int ret = 0; + pthread_t pthread; + if (NULL == thread || NULL == start_routine) { + return -1; + } + ret = pthread_create(&pthread, NULL, start_routine, args); + if (0 != ret) { + goto err_out_label; + } + *thread = (volc_osal_tid_t)pthread; + return 0; +err_out_label: + return -1; +} +int volc_osal_thread_detach(volc_osal_tid_t thread) { + return pthread_detach((pthread_t)thread); +} + +void volc_osal_thread_exit(volc_osal_tid_t thread) { + pthread_exit((pthread_t)thread); +} + +void volc_osal_thread_sleep(int time_ms) { + usleep(time_ms * 1000); +} + +void volc_osal_thread_destroy(volc_osal_tid_t thread) { + (void)thread; +} + +int volc_osal_get_platform_info(char* info, size_t size) { + if (NULL == info || size <= 0) { + return -1; + } + snprintf(info, size, "macos"); + return 0; +} + +int volc_osal_fill_random(uint8_t* data, size_t size) { + if (NULL == data || size <= 0) { + return -1; + } + while (size > 0) { + uint32_t word = (uint32_t) volc_osal_get_time_ms(); + uint32_t to_copy = sizeof(word) < size ? sizeof(word) : size; + memcpy(data, &word, to_copy); + data += to_copy; + size -= to_copy; + } + return 0; +} diff --git a/firmware/echoear-platform-agent/components/volc_conv_ai/sample/sample.c b/firmware/echoear-platform-agent/components/volc_conv_ai/sample/sample.c new file mode 100644 index 0000000..a21217e --- /dev/null +++ b/firmware/echoear-platform-agent/components/volc_conv_ai/sample/sample.c @@ -0,0 +1,153 @@ +#include "volc_conv_ai.h" +#include +#include +#include + +#define CONFIG_VOLC_BOT_ID "xxxx" +#define CONFIG_VOLC_INSTANCE_ID "xxxx" +#define CONFIG_VOLC_PRODUCT_KEY "xxxx" +#define CONFIG_VOLC_PRODUCT_SECRET "xxxx" +#define CONFIG_VOLC_DEVICE_NAME "xxxx" + +#define CONV_AI_CONFIG_FORMAT "{\ + \"ver\": 1,\ + \"iot\": {\ + \"instance_id\": \"%s\",\ + \"product_key\": \"%s\",\ + \"product_secret\": \"%s\",\ + \"device_name\": \"%s\"\ + },\ + \"rtc\": {\ + \"log_level\": 3,\ + \"audio\": {\ + \"publish\": true,\ + \"subscribe\": true,\ + \"codec\": 3\ + },\ + \"video\": {\ + \"publish\": false,\ + \"subscribe\": false,\ + \"codec\": 1\ + },\ + \"params\":[\ + \"{\\\"debug\\\":{\\\"log_to_console\\\":1}}\",\ + \"{\\\"rtc\\\":{\\\"access\\\":{\\\"concurrent_requests\\\":1}}}\",\ + \"{\\\"rtc\\\":{\\\"ice\\\":{\\\"concurrent_agents\\\":1}}}\",\ + \"{\\\"audio\\\":{\\\"codec\\\":{\\\"pcma\\\":{\\\"s_samples_per_frame\\\":480}}}}\"\ + ]\ + }\ +}" + +volc_engine_t engine; + +static void __on_volc_event(volc_engine_t handle, volc_event_t *event, void *user_data) +{ + switch (event->code) + { + case VOLC_EV_CONNECTED: + printf("Volc Engine connected \n"); + break; + case VOLC_EV_DISCONNECTED: + printf( "Volc Engine disconnected \n"); + break; + case VOLC_EV_QUOTA_EXCEEDED: + printf("Volc Engine quota exceeded\n"); + default: + printf("Volc Engine event: %d \n", event->code); + break; + } +} + +static void __on_volc_audio_data(volc_engine_t handle, const void *data_ptr, size_t data_len, volc_audio_frame_info_t *info_ptr, void *user_data) +{ + printf("__on_volc_audio_data \n"); + // play_audio_data(player_handle, data_ptr, data_len); +} + +static void __on_subtitle_message_received(const cJSON* root) { + /* + { + "data" : + [ + { + "definite" : false, + "language" : "zh", + "mode" : 1, + "paragraph" : false, + "sequence" : 0, + "text" : "\\u4f60\\u597d", + "userId" : "voiceChat_xxxxx" + } + ], + "type" : "subtitle" + } + */ + cJSON * type_obj = cJSON_GetObjectItem(root, "type"); + if (type_obj != NULL && strcmp("subtitle", cJSON_GetStringValue(type_obj)) == 0) { + cJSON* data_obj_arr = cJSON_GetObjectItem(root, "data"); + cJSON* obji = NULL; + cJSON_ArrayForEach(obji, data_obj_arr) { + cJSON* user_id_obj = cJSON_GetObjectItem(obji, "userId"); + cJSON* text_obj = cJSON_GetObjectItem(obji, "text"); + cJSON* definite_obj = cJSON_GetObjectItem(obji, "definite"); + char* sub = cJSON_GetStringValue(text_obj); + + // print_string(sub + sub_offset -1); + if(cJSON_IsTrue(definite_obj)){ + printf(" %s \n",sub); + } + } + } +} + +static void __on_volc_message_data(volc_engine_t handle, const void *message, size_t size, volc_message_info_t *info_ptr, void *user_data) +{ + static char message_buffer[4096]; + if (size > 8 && size < 4096) { + memcpy(message_buffer, message, size); + message_buffer[size] = 0; + message_buffer[size + 1] = 0; + cJSON *root = cJSON_Parse(message_buffer + 8); + if (root != NULL) { + if (message_buffer[0] == 's' && message_buffer[1] == 'u' && message_buffer[2] == 'b' && message_buffer[3] == 'v') { + __on_subtitle_message_received(root); + } + cJSON_Delete(root); + } + } +} + +int main(){ + static char config_buf[1024] = {0}; + + snprintf(config_buf, sizeof(config_buf), CONV_AI_CONFIG_FORMAT, + CONFIG_VOLC_INSTANCE_ID, + CONFIG_VOLC_PRODUCT_KEY, + CONFIG_VOLC_PRODUCT_SECRET, + CONFIG_VOLC_DEVICE_NAME); + + volc_event_handler_t volc_event_handler = (volc_event_handler_t){ + .on_volc_event = __on_volc_event, + .on_volc_audio_data = __on_volc_audio_data, + .on_volc_message_data = __on_volc_message_data, + }; + + int error = volc_create(&engine, config_buf, &volc_event_handler, NULL); + + volc_opt_t opt = { + .mode = VOLC_MODE_RTC, + .bot_id = CONFIG_VOLC_BOT_ID, + }; + + error = volc_start(engine, &opt); + + for(int i = 0;i<5;i++){ + // capture_audio_data(); + // volc_send_audio_data(engine, data, len, &info); + sleep(5); + volc_send_text_to_agent(engine, "火山引擎测试", VOLC_AGENT_TYPE_TTS, 1); + } + volc_stop(engine); + volc_destroy(engine); + return 0; +} \ No newline at end of file diff --git a/firmware/echoear-platform-agent/components/volc_conv_ai/src/base/volc_base.h b/firmware/echoear-platform-agent/components/volc_conv_ai/src/base/volc_base.h new file mode 100644 index 0000000..517f89c --- /dev/null +++ b/firmware/echoear-platform-agent/components/volc_conv_ai/src/base/volc_base.h @@ -0,0 +1,51 @@ +// Copyright (2025) Beijing Volcano Engine Technology Ltd. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef __CONV_AI_BASE_VOLC_BASE_H__ +#define __CONV_AI_BASE_VOLC_BASE_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#include "util/volc_list.h" +#include "volc_conv_ai.h" + +// TODO: internal or external +typedef enum { + VOLC_MSG_CONNECTED = 0, // 成功连接 + VOLC_MSG_DISCONNECTED, // 断开连接 + VOLC_MSG_USER_JOINED, // 用户加入 + VOLC_MSG_USER_OFFLINE, // 用户离开 + VOLC_MSG_APP_ID_INVALID, + VOLC_MSG_TOKEN_INVALID, + VOLC_MSG_TOKEN_EXPIRED, + VOLC_MSG_LICENSE_EXPIRED, + VOLC_MSG_KEY_FRAME_REQ, // 关键帧请求 + VOLC_MSG_TARGET_BITRATE_CHANGED, // 目标码率变化 + VOLC_MSG_CONV_STATUS, // 会话状态 + VOLC_MSG_QUOTA_EXCEEDED, // 资源耗尽 +} volc_msg_e; + +typedef struct { + volc_msg_e code; + union { + uint32_t target_bitrate; + uint32_t conv_status; + char* msg; + } data; +} volc_msg_t; + +typedef void(*volc_msg_cb)(void* context, volc_msg_t *msg); +typedef void(*volc_data_cb)(void* context, const void* buffer, size_t len, volc_data_info_t *info); + + +#ifdef __cplusplus +} +#endif +#endif /* __CONV_AI_BASE_VOLC_BASE_H__ */ diff --git a/firmware/echoear-platform-agent/components/volc_conv_ai/src/base/volc_device_manager.c b/firmware/echoear-platform-agent/components/volc_conv_ai/src/base/volc_device_manager.c new file mode 100644 index 0000000..d2aaaaf --- /dev/null +++ b/firmware/echoear-platform-agent/components/volc_conv_ai/src/base/volc_device_manager.c @@ -0,0 +1,211 @@ +// Copyright (2025) Beijing Volcano Engine Technology Ltd. +// SPDX-License-Identifier: Apache-2.0 + +#include "volc_device_manager.h" + +#include +#include +#include + +#include "cJSON.h" +#include "volc_osal.h" +#include "util/volc_http.h" +#include "util/volc_json.h" +#include "util/volc_base64.h" +#include "util/volc_auth.h" +#include "util/volc_list.h" +#include "util/volc_log.h" + +#include "volc_conv_ai.h" + +#define VOLC_IOT_HOST "https://iot-cn-shanghai.iot.volces.com" + +#define VOLC_DYNAMIC_REGISTER_PATH "/2021-12-14/DynamicRegister" + +#define VOLC_API_VERSION "2021-12-14" +#define VOLC_API_VERSION_QUERY_PARAM "Version=2021-12-14" +#define VOLC_API_ACTION_DYNAMIC_REGISTER "Action=DynamicRegister" + +char* volc_generate_signature(const char* secret_key, const char* product_key, const char* device_name, int rnd, uint64_t timestamp, int auth_type) +{ + char input_str[256] = {0}; + uint8_t hmac_result[32] = {0}; + int hmac_result_len = sizeof(hmac_result); + int base64_encoded_len = 0; + size_t olen = 0; + + snprintf(input_str, sizeof(input_str), "auth_type=%d&device_name=%s&random_num=%d&product_key=%s×tamp=%" PRIu64, auth_type, device_name, rnd, product_key, timestamp); + volc_sha256_hmac((const unsigned char*)secret_key, strlen(secret_key), (const unsigned char*)input_str, strlen(input_str), hmac_result, &hmac_result_len); + base64_encoded_len = volc_base64_encoded_length(hmac_result_len); + unsigned char* base64_encoded = (unsigned char*)volc_osal_malloc(base64_encoded_len); + if (!base64_encoded) { + LOGE("Failed to allocate memory for base64 encoded string"); + return NULL; + } + + volc_base64_encode(base64_encoded, base64_encoded_len, &olen, (const unsigned char*)hmac_result, sizeof(hmac_result)); + return (char*)base64_encoded; +} + +char* volc_generate_signature_ws(const char* secret_key, const char* product_key, const char* device_name, const char* instance_id, int rnd, uint64_t timestamp, int auth_type) { + char input_str[256] = {0}; + uint8_t hmac_result[32] = {0}; + int hmac_result_len = sizeof(hmac_result); + int base64_encoded_len = 0; + size_t olen = 0; + + snprintf(input_str, sizeof(input_str), "auth_type=%d&device_name=%s&random_num=%d&product_key=%s×tamp=%" PRIu64 "&instance_id=%s", auth_type, device_name, rnd, product_key, timestamp, instance_id); + volc_sha256_hmac((const unsigned char*)secret_key, strlen(secret_key), (const unsigned char*)input_str, strlen(input_str), hmac_result, &hmac_result_len); + base64_encoded_len = volc_base64_encoded_length(hmac_result_len); + unsigned char* base64_encoded = (unsigned char*)volc_osal_malloc(base64_encoded_len); + if (!base64_encoded) { + LOGE("Failed to allocate memory for base64 encoded string"); + return NULL; + } + + volc_base64_encode(base64_encoded, base64_encoded_len, &olen, (const unsigned char*)hmac_result, sizeof(hmac_result)); + return (char*)base64_encoded; +} + +volc_error_code_e volc_inter_err_2_ext_err(int code) { + switch (code) { + case ERROR_LICENSE_EXHAUSTED: + return VOLC_ERR_LICENSE_EXHAUSTED; + case ERROR_LICENSE_EXPIRED: + return VOLC_ERR_LICENSE_EXPIRED; + default: + return VOLC_ERR_FAILED; + } +} + +int volc_device_register(volc_iot_info_t* info, char** output) +{ + int ret = 0; + uint64_t current_time = volc_osal_get_time_ms(); + int32_t random_num = (int32_t)current_time; + char url[256] = {0}; + char* payload = NULL; + char* signature = volc_generate_signature(info->product_secret, info->product_key, info->device_name, random_num, current_time, 1); + cJSON* response_json = NULL; + cJSON* root = cJSON_CreateObject(); + cJSON_AddStringToObject(root, "InstanceID", info->instance_id); + cJSON_AddStringToObject(root, "product_key", info->product_key); + cJSON_AddStringToObject(root, "device_name", info->device_name); + cJSON_AddNumberToObject(root, "random_num", random_num); + cJSON_AddNumberToObject(root, "timestamp", (double)current_time); + cJSON_AddNumberToObject(root, "auth_type", 1); + cJSON_AddStringToObject(root, "signature", signature); + char* json_str = cJSON_PrintUnformatted(root); + + snprintf(url, sizeof(url), "%s%s?%s&%s", VOLC_IOT_HOST, VOLC_DYNAMIC_REGISTER_PATH, VOLC_API_ACTION_DYNAMIC_REGISTER, VOLC_API_VERSION_QUERY_PARAM); + LOGD("url: %s, body: %s", url, json_str); + + char* response = volc_http_post(url, json_str, strlen(json_str)); + if (response == NULL) { + LOGE("Failed to get response from server"); + ret = -1; + goto err_out_label; + } + + response_json = cJSON_Parse(response); + if (response_json == NULL) { + LOGE("Failed to parse response JSON"); + ret = -1; + goto err_out_label; + } + int code = 0; + ret = volc_json_read_int(response_json, "ResponseMetadata.Error.CodeN", &code); + if (0 == ret) { + ret = volc_inter_err_2_ext_err(code); + LOGE("register device failed, ret: %d, code: %d", ret, code); + goto err_out_label; + } + ret = volc_json_read_string(response_json, "Result.payload", &payload); + if (ret != 0) { + LOGE("Failed to read payload from response JSON"); + ret = -1; + goto err_out_label; + } + ret = volc_json_read_string(response_json, "Result.RTCAppID", &info->rtc_app_id); + if (ret != 0) { + LOGE("Failed to read rtc app id from response JSON"); + ret = -1; + goto err_out_label; + } + LOGD("rtc app id: %s", info->rtc_app_id); + + // TODO: device secret, should be freed by caller + *output = volc_aes_decode(info->product_secret, payload, true); + +err_out_label: + HAL_SAFE_FREE(payload); + HAL_SAFE_FREE(response); + HAL_SAFE_FREE(root); + HAL_SAFE_FREE(response_json); + HAL_SAFE_FREE(signature); + HAL_SAFE_FREE(json_str); + return ret; +} + +#define VOLC_GET_RTC_CONFIG_PATH "/2021-12-14/GetRTCConfig" +#define VOLC_API_ACTION_GET_RTC_CONFIG "Action=GetRTCConfig" +int volc_get_rtc_config(volc_iot_info_t* info, int audio_codec, const char* bot_id, const char* task_id, volc_room_info_t* room_info, const char* params) { + int ret = 0; + uint64_t current_time = volc_osal_get_time_ms(); + int32_t random_num = (int32_t)current_time; + char url[256] = {0}; + char* signature = volc_generate_signature(info->device_secret, info->product_key, info->device_name, random_num, current_time, 0); + cJSON* response_json = NULL; + cJSON* root = cJSON_CreateObject(); + cJSON_AddStringToObject(root, "InstanceID", info->instance_id); + cJSON_AddStringToObject(root, "product_key", info->product_key); + cJSON_AddStringToObject(root, "device_name", info->device_name); + cJSON_AddNumberToObject(root, "random_num", random_num); + cJSON_AddNumberToObject(root, "timestamp", (double)current_time); + cJSON_AddStringToObject(root, "signature", signature); + cJSON_AddStringToObject(root, "bot_id", bot_id); + cJSON_AddNumberToObject(root, "audio_codec", audio_codec); + cJSON_AddStringToObject(root, "task_id", task_id); + + if(params){ + cJSON* params_config = cJSON_Parse(params); + + cJSON* agent_config = cJSON_CreateObject(); + volc_json_read_object(params_config,"AgentConfig",&agent_config); + cJSON_AddItemToObject(root, "agent_config", agent_config); + + cJSON* config = cJSON_CreateObject(); + volc_json_read_object(params_config,"Config",&config); + cJSON_AddItemToObject(root, "config", config); + } + char* json_str = cJSON_PrintUnformatted(root); + snprintf(url, sizeof(url), "%s%s?%s&%s", VOLC_IOT_HOST, VOLC_GET_RTC_CONFIG_PATH, VOLC_API_ACTION_GET_RTC_CONFIG, VOLC_API_VERSION_QUERY_PARAM); + LOGI("url: %s, body: %s", url, json_str); + char* response = volc_http_post(url, json_str, strlen(json_str)); + if (response == NULL) { + LOGE("Failed to get response from server"); + ret = -1; + goto err_out_label; + } + response_json = cJSON_Parse(response); + volc_json_read_string(response_json, "Result.RoomID", &room_info->rtc_opt.p_channel_name); + volc_json_read_string(response_json, "Result.UserID", &room_info->rtc_opt.p_uid); + volc_json_read_string(response_json, "Result.Token", &room_info->rtc_opt.p_token); + volc_json_read_string(response_json, "Result.TaskID", &room_info->task_id); + if (room_info->rtc_opt.p_channel_name == NULL || room_info->rtc_opt.p_uid == NULL || room_info->rtc_opt.p_token == NULL || room_info->task_id == NULL) { + LOGE("Failed to get RTC config from server: %s", response); + ret = -1; + goto err_out_label; + } +err_out_label: + if (root) { + cJSON_Delete(root); + } + if (response_json) { + cJSON_Delete(response_json); + } + HAL_SAFE_FREE(signature); + HAL_SAFE_FREE(json_str); + HAL_SAFE_FREE(response); + return ret; +} diff --git a/firmware/echoear-platform-agent/components/volc_conv_ai/src/base/volc_device_manager.h b/firmware/echoear-platform-agent/components/volc_conv_ai/src/base/volc_device_manager.h new file mode 100644 index 0000000..b3c44c4 --- /dev/null +++ b/firmware/echoear-platform-agent/components/volc_conv_ai/src/base/volc_device_manager.h @@ -0,0 +1,46 @@ +// Copyright (2025) Beijing Volcano Engine Technology Ltd. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef __CONV_AI_SRC_VOLC_DEVICE_MANAGER__ +#define __CONV_AI_SRC_VOLC_DEVICE_MANAGER__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define HARDWARE_ID "a2:c8:2c:89:6e:46" + +#define ERROR_LICENSE_EXHAUSTED 12000130 +#define ERROR_LICENSE_EXPIRED 12000140 + +typedef struct { + char* instance_id; + char* product_key; + char* product_secret; + char* device_name; + char* device_secret; + char* rtc_app_id; +} volc_iot_info_t; + +typedef struct { + char *p_channel_name; + char *p_uid; + char *p_token; +} volc_rtc_option_t; + +typedef struct { + volc_rtc_option_t rtc_opt; + char* task_id; // Task ID for the room +} volc_room_info_t; + +int volc_device_register(volc_iot_info_t* info, char** output); +int volc_get_rtc_config(volc_iot_info_t* info, int audio_codec, const char* bot_id, const char* task_id, volc_room_info_t* room_info, const char* params); +char* volc_generate_signature(const char* secret_key, const char* product_key, const char* device_name, int rnd, uint64_t timestamp, int auth_type); +char* volc_generate_signature_ws(const char* secret_key, const char* product_key, const char* device_name, const char* instance_id, int rnd, uint64_t timestamp, int auth_type); + +#ifdef __cplusplus +} +#endif +#endif /* __CONV_AI_SRC_VOLC_DEVICE_MANAGER__ */ diff --git a/firmware/echoear-platform-agent/components/volc_conv_ai/src/transports/high_quality/inc/volc_rtc.h b/firmware/echoear-platform-agent/components/volc_conv_ai/src/transports/high_quality/inc/volc_rtc.h new file mode 100644 index 0000000..2da8010 --- /dev/null +++ b/firmware/echoear-platform-agent/components/volc_conv_ai/src/transports/high_quality/inc/volc_rtc.h @@ -0,0 +1,43 @@ +// Copyright (2025) Beijing Volcano Engine Technology Ltd. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef __CONV_AI_NODE_VOLC_RTC_H__ +#define __CONV_AI_NODE_VOLC_RTC_H__ +#if defined(ENABLE_RTC_MODE) +#include + +#include "cJSON.h" + +#include "VolcEngineRTCLite.h" + +#include "base/volc_base.h" +#include "base/volc_device_manager.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void* volc_rtc_t; + +volc_rtc_t volc_rtc_create(const char* appid, void* context, cJSON* p_config, volc_msg_cb message_callback, volc_data_cb data_callback); + +void volc_rtc_destroy(volc_rtc_t rtc); + +int volc_rtc_start(volc_rtc_t rtc, const char* bot_id, volc_iot_info_t* iot_info, const char* params); + +int volc_rtc_stop(volc_rtc_t rtc); + +int volc_rtc_send(volc_rtc_t rtc, const void* data, int size, volc_data_info_t* data_info); + +int volc_rtc_interrupt(volc_rtc_t rtc); + +int volc_rtc_send_jpg(volc_rtc_t rtc, void* data, int size); + +int volc_rtc_send_text_to_agent(volc_rtc_t rtc, const char* text, volc_agent_type_e type, int interrupt_mode); + +#endif + +#ifdef __cplusplus +} +#endif +#endif /* __CONV_AI_NODE_VOLC_RTC_H__ */ diff --git a/firmware/echoear-platform-agent/components/volc_conv_ai/src/transports/high_quality/src/volc_rtc.c b/firmware/echoear-platform-agent/components/volc_conv_ai/src/transports/high_quality/src/volc_rtc.c new file mode 100644 index 0000000..331351d --- /dev/null +++ b/firmware/echoear-platform-agent/components/volc_conv_ai/src/transports/high_quality/src/volc_rtc.c @@ -0,0 +1,643 @@ +// Copyright (2025) Beijing Volcano Engine Technology Ltd. +// SPDX-License-Identifier: Apache-2.0 + +#include "volc_rtc.h" + +#include +#include +#include + +#include "volc_osal.h" +#include "util/volc_list.h" +#include "util/volc_log.h" +#include "util/volc_json.h" + +#define MAGIC_CONTROL "ctrl" +#define MAGIC_CONV "conv" +#define MAGIC_LENGTH 4 +#define MAGIC_OFFSET 8 +const char* interrupt_str = "{\"Command\":\"interrupt\"}"; + +typedef struct { + bool b_pipeline_started; + bool b_user_joined; + bool b_channel_joined; + bool b_first_keyframe_received; + bool b_fini; + bool b_audio_publish; + bool b_video_publish; + bool b_audio_subscribe; + bool b_video_subscribe; + char* p_appid; + char* p_channel_name; + char* p_user_id; + char* p_remote_user_id; + char* p_token; + void* context; + int audio_codec; + volc_room_info_t info; + volc_msg_cb message_callback; + volc_data_cb data_callback; + byte_rtc_engine_t rtc; + byte_rtc_event_handler_t event_handler; +} rtc_impl_t; + +static bool __is_first_keyframe_not_received(rtc_impl_t* rtc, int is_key_frame) +{ + return (!rtc->b_first_keyframe_received && !is_key_frame); +} + +static int _build_binary_message(const char* magic, const char* message, + uint8_t** out_buf, size_t* out_len) { + size_t magic_len = strlen(magic); + size_t msg_len = strlen(message); + + // 分配内存:魔术字 + 4字节长度 + 消息内容 + *out_len = magic_len + 4 + msg_len; + *out_buf = (uint8_t*)volc_osal_malloc(*out_len); + if (!*out_buf) { + LOGE("volc_osal_malloc failed"); + return -1; + } + + // 填充魔术字 + memcpy(*out_buf, magic, magic_len); + + // 以大端序填充长度 + uint8_t* len_ptr = *out_buf + magic_len; + len_ptr[0] = (uint8_t)((msg_len >> 24) & 0xFF); // 最高位字节 + len_ptr[1] = (uint8_t)((msg_len >> 16) & 0xFF); + len_ptr[2] = (uint8_t)((msg_len >> 8) & 0xFF); + len_ptr[3] = (uint8_t)(msg_len & 0xFF); // 最低位字节 + + // 填充消息内容 + memcpy(*out_buf + magic_len + 4, message, msg_len); + + return 0; +} + +static int __rtc_start(rtc_impl_t* rtc, volc_rtc_option_t* option) +{ + volc_opt_t* p_opt = NULL; + byte_rtc_room_options_t room_opt = {0}; + if (!rtc || !option) { + LOGE("rtc or option is NULL"); + return -1; + } + + room_opt.auto_publish_audio = rtc->b_audio_publish; + room_opt.auto_publish_video = rtc->b_video_publish; + room_opt.auto_subscribe_audio = rtc->b_audio_subscribe; + room_opt.auto_subscribe_video = rtc->b_video_subscribe; + LOGI("Joining channel: %s, uid: %s, token: %s, vpub: %d, vsub: %d, apub: %d, asub: %d", option->p_channel_name, option->p_uid, option->p_token, (int)room_opt.auto_publish_video, (int)room_opt.auto_subscribe_video, (int)room_opt.auto_publish_audio, (int)room_opt.auto_subscribe_audio); + int ret = byte_rtc_join_room(rtc->rtc, option->p_channel_name, option->p_uid, option->p_token, &room_opt); + if (ret != 0) { + LOGE("Failed to join room: %d", ret); + return ret; + } + rtc->b_pipeline_started = true; + rtc->p_channel_name = strdup(option->p_channel_name); + rtc->p_user_id = strdup(option->p_uid); + + return 0; +} + +static void __rtc_stop(rtc_impl_t* rtc) +{ + if (!rtc) { + LOGE("rtc instance is NULL"); + return; + } + if (!rtc->b_pipeline_started) { + LOGI("pipeline not started"); + return; + } + + int ret = byte_rtc_leave_room(rtc->rtc, rtc->p_channel_name); + if (ret != 0) { + LOGE("Failed to leave room: %d", ret); + return; + } + rtc->b_pipeline_started = false; + HAL_SAFE_FREE(rtc->p_channel_name); + + return; +} + +static void __send_data_2_user(rtc_impl_t* rtc, const void* data, int data_len, volc_data_info_t* info) { + if (rtc->data_callback) { + rtc->data_callback(rtc->context, data, data_len, info); + } +} + +static void _register_message_router(rtc_impl_t* rtc, volc_msg_cb callback) +{ + rtc->message_callback = callback; +} + +static void _send_message_2_user(rtc_impl_t* rtc, volc_msg_t* msg) +{ + if (rtc->message_callback) { + rtc->message_callback(rtc->context, msg); + } +} + +static bool _is_target_message(const uint8_t* message, const char* target) { + if (message == NULL || target == NULL) { + return false; + } + // Check if the first 4 bytes match the magic number for "subv" + if (*(const uint32_t*)message != *(const uint32_t*)target) { + return false; + } + return true; +} + +static int _on_conversion_status_message_parsed(uint8_t* message, rtc_impl_t* rtc) { + int c = -1; + char* error_reason = NULL; + volc_msg_t msg = { 0 }; + cJSON *root = cJSON_Parse((const char*)message); + if (root == NULL) { + return c; + } + volc_json_read_int(root, "Stage.Code", &c); + volc_json_read_string(root, "ErrorInfo.Reason", &error_reason); + if(error_reason != NULL) { + LOGW("ErrorInfo.Reason %s",error_reason); + if(strcmp(error_reason, "preParamCheck:AI license duration quota exceeded") == 0) { + msg.code = VOLC_MSG_QUOTA_EXCEEDED; + msg.data.conv_status = 0; + _send_message_2_user(rtc, &msg); + } + HAL_SAFE_FREE(error_reason); + return -1; + } + if(root != NULL) cJSON_Delete(root); + return c; +} + +static void _on_join_channel_success(byte_rtc_engine_t engine, const char* channel, int elapsed_ms, bool rejoin) +{ + volc_msg_t msg = {0}; + LOGI("join channel success %s elapsed %d ms\n", channel, elapsed_ms); + rtc_impl_t* rtc = (rtc_impl_t*) byte_rtc_get_user_data(engine); + + rtc->b_first_keyframe_received = false; + rtc->b_channel_joined = true; + + msg.code = VOLC_MSG_CONNECTED; + _send_message_2_user(rtc, &msg); +}; + +static void _on_user_joined(byte_rtc_engine_t engine, const char* channel, const char* user_name, int elapsed_ms) +{ + rtc_impl_t* rtc = (rtc_impl_t*) byte_rtc_get_user_data(engine); + volc_msg_t msg = {0}; + LOGI("remote user joined %s:%s elapsed %d ms\n", channel, user_name, elapsed_ms); + + rtc->b_user_joined = true; + rtc->p_remote_user_id = strdup(user_name); + + msg.code = VOLC_MSG_USER_JOINED; + _send_message_2_user(rtc, &msg); +}; + +static void _on_user_offline(byte_rtc_engine_t engine, const char* channel, const char* user_name, int reason) +{ + rtc_impl_t* rtc = (rtc_impl_t*) byte_rtc_get_user_data(engine); + volc_msg_t msg = {0}; + LOGI("remote user offline %s:%s reason %d\n", channel, user_name, reason); + rtc->b_user_joined = false; + + msg.code = VOLC_MSG_USER_OFFLINE; + _send_message_2_user(rtc, &msg); +}; + +static void _on_user_mute_audio(byte_rtc_engine_t engine, const char* channel, const char* user_name, int muted) +{ + LOGD("remote user mute audio %s:%s %d\n", channel, user_name, muted); +}; + +static void _on_user_mute_video(byte_rtc_engine_t engine, const char* channel, const char* user_name, int muted) +{ + LOGD("remote user mute video %s:%s %d\n", channel, user_name, muted); +}; + +static void _on_audio_data(byte_rtc_engine_t engine, const char* channel, const char* user_name, uint16_t sent_ts, audio_data_type_e data_type, + const void* data_ptr, size_t data_len, const uint8_t* extra_info, size_t extra_info_size) +{ + volc_data_info_t info = {0}; + rtc_impl_t* rtc = (rtc_impl_t*)byte_rtc_get_user_data(engine); + + if (NULL == rtc || !rtc->b_pipeline_started || !rtc->b_channel_joined || !rtc->b_user_joined) { + LOGE("pipeline not started or channel not joined or user not joined"); + return; + } + info.type = VOLC_DATA_TYPE_AUDIO; + info.info.audio.data_type = (volc_audio_data_type_e) data_type; + // info.info.audio.sent_ts = sent_ts; + __send_data_2_user(rtc, data_ptr, data_len, &info); +}; + +static void _on_video_data(byte_rtc_engine_t engine, const char* channel, const char* user_name, uint16_t sent_ts, video_data_type_e codec, + int is_key_frame, const void* data_ptr, size_t data_len) +{ + volc_data_info_t info = {0}; + rtc_impl_t* rtc = (rtc_impl_t*) byte_rtc_get_user_data(engine); + + if (NULL == rtc || !rtc->b_pipeline_started || !rtc->b_channel_joined || !rtc->b_user_joined) { + LOGD("pipeline not started or channel not joined or user not joined"); + return; + } + + if (__is_first_keyframe_not_received(rtc, is_key_frame)) { + LOGD("first keyframe not received, request key frame"); + byte_rtc_request_video_key_frame(rtc->rtc, channel, user_name); + return; + } + + rtc->b_first_keyframe_received = true; + + info.type = VOLC_DATA_TYPE_VIDEO; + info.info.video.data_type = (volc_video_data_type_e) codec; + __send_data_2_user(rtc, data_ptr, data_len, &info); +}; + +static void _on_channel_error(byte_rtc_engine_t engine, const char* channel, int code, const char* msg) +{ + volc_msg_t msg_data = {0}; + rtc_impl_t* rtc = (rtc_impl_t*) byte_rtc_get_user_data(engine); + LOGE("channel error %s:%d %s\n", channel, code, msg ? msg : ""); + msg_data.code = code; + msg_data.data.msg = (char *)msg; + _send_message_2_user(rtc, &msg_data); +}; + +static void _on_global_error(byte_rtc_engine_t engine, int code, const char* message) +{ + volc_msg_t msg_data = {0}; + rtc_impl_t* rtc = (rtc_impl_t*) byte_rtc_get_user_data(engine); + rtc->b_channel_joined = false; + + rtc->b_first_keyframe_received = false; + LOGI("global error %d %s\n", code, message ? message : ""); + msg_data.code = VOLC_MSG_DISCONNECTED; + _send_message_2_user(rtc, &msg_data); +}; + +static void _on_key_frame_gen_req(byte_rtc_engine_t engine, const char* channel, const char* user_name) +{ + LOGI("remote req key frame %s:%s\n", channel, user_name); + rtc_impl_t* rtc = (rtc_impl_t*) byte_rtc_get_user_data(engine); + volc_msg_t msg_data = {0}; + msg_data.code = VOLC_MSG_KEY_FRAME_REQ; + _send_message_2_user(rtc, &msg_data); +}; + +static void _on_target_bitrate_changed(byte_rtc_engine_t engine, const char* channel, uint32_t target_bps) +{ + rtc_impl_t* rtc = (rtc_impl_t*) byte_rtc_get_user_data(engine); + volc_msg_t msg_data = {0}; + + LOGD("target bitrate changed %s %d bps\n", channel, (int)target_bps); + // TODO: do not send it to user + msg_data.code = VOLC_MSG_TARGET_BITRATE_CHANGED; + msg_data.data.target_bitrate = target_bps; + _send_message_2_user(rtc, &msg_data); +}; + +static void _on_token_privilege_will_expire(byte_rtc_engine_t engine, const char* token) +{ + LOGI("\ntoken privilege will expire %s", token ? token : ""); + rtc_impl_t* rtc = (rtc_impl_t*) byte_rtc_get_user_data(engine); + volc_msg_t msg_data = {0}; + msg_data.code = VOLC_MSG_TOKEN_EXPIRED; + // TODO: token? + _send_message_2_user(rtc, &msg_data); +}; + +static void _on_message_received(byte_rtc_engine_t engine, const char* channel_name, const char* src, const uint8_t* message, int size, bool binary) +{ + + int ret = 0; + volc_msg_t msg = { 0 }; + rtc_impl_t* rtc = (rtc_impl_t*) byte_rtc_get_user_data(engine); + volc_data_info_t info = {0}; + info.type = VOLC_DATA_TYPE_MESSAGE; + info.info.message.is_binary = binary; + + if (NULL == rtc || !rtc->b_pipeline_started || !rtc->b_channel_joined || !rtc->b_user_joined) { + LOGE("pipeline not started or channel not joined or user not joined"); + return; + } + + if (_is_target_message(message, MAGIC_CONV)) { + ret = _on_conversion_status_message_parsed((uint8_t *)message + MAGIC_OFFSET,rtc); + if(ret != -1){ + msg.code = VOLC_MSG_CONV_STATUS; + msg.data.conv_status = ret; + _send_message_2_user(rtc, &msg); + } + return; + } + __send_data_2_user(rtc, message, size, &info); +}; + +static void _on_message_send_result(byte_rtc_engine_t engine, const char* channel_name, int64_t msgid, int error, const char* extencontent) +{ + LOGD("----------------------->MessageSendResult msg id %" PRId64 ", error %d, extencontent %s \n", msgid, error, extencontent ? extencontent : ""); +}; + +static void _on_license_will_expire(byte_rtc_engine_t engine, int daysleft) +{ + LOGI("----------------------->license will expire in %d days \n", daysleft); + volc_msg_t msg_data = {0}; + rtc_impl_t* rtc = (rtc_impl_t*) byte_rtc_get_user_data(engine); + msg_data.code = VOLC_MSG_LICENSE_EXPIRED; + _send_message_2_user(rtc, &msg_data); +}; + +static void _on_fini_notify(byte_rtc_engine_t engine) +{ + rtc_impl_t* rtc = (rtc_impl_t*) byte_rtc_get_user_data(engine); + rtc->b_fini = true; +} + +/* + VOLC_AUDIO_CODEC_TYPE_UNKNOWN = 0, + VOLC_AUDIO_CODEC_TYPE_OPUS = 1, + VOLC_AUDIO_CODEC_TYPE_G722 = 2, + VOLC_AUDIO_CODEC_TYPE_G711A = 3, + VOLC_AUDIO_CODEC_TYPE_PCM = 4, + VOLC_AUDIO_CODEC_TYPE_G711U = 5, +*/ +static int __volc_to_rtc_audio_codec(int volc_codec) +{ + switch (volc_codec) { + case VOLC_AUDIO_CODEC_TYPE_OPUS: + return AUDIO_CODEC_TYPE_OPUS; + case VOLC_AUDIO_CODEC_TYPE_G722: + return AUDIO_CODEC_TYPE_G722; + case VOLC_AUDIO_CODEC_TYPE_G711A: + return AUDIO_CODEC_TYPE_G711A; + case VOLC_AUDIO_CODEC_TYPE_G711U: + return AUDIO_CODEC_TYPE_G711U; + default: + LOGW("volc audio codec %d not supported, use opus instead", volc_codec); + return AUDIO_CODEC_TYPE_OPUS; + } +} + +static int __rtc_init(rtc_impl_t* engine, cJSON* p_config) +{ + int ret = 0; + int video_codec = 0; + int log_level = 0; + int params_cnt = 0; + cJSON* p_params = NULL; + byte_rtc_event_handler_t rtc_event_handler = {.on_global_error = _on_global_error, + .on_join_room_success = _on_join_channel_success, + .on_room_error = _on_channel_error, + .on_user_joined = _on_user_joined, + .on_user_offline = _on_user_offline, + .on_user_mute_audio = _on_user_mute_audio, + .on_user_mute_video = _on_user_mute_video, + .on_audio_data = _on_audio_data, + .on_video_data = _on_video_data, + .on_key_frame_gen_req = _on_key_frame_gen_req, + .on_target_bitrate_changed = _on_target_bitrate_changed, + .on_token_privilege_will_expire = _on_token_privilege_will_expire, + .on_message_received = _on_message_received, + .on_message_send_result = _on_message_send_result, + .on_license_expire_warning = _on_license_will_expire, + .on_fini_notify = _on_fini_notify}; + ret = volc_json_read_int(p_config, "audio.codec", &engine->audio_codec); + if (ret != 0 || engine->audio_codec < 0 || engine->audio_codec > AUDIO_CODEC_TYPE_G711U) { + LOGE("volc_rtc_create: read audio_codec failed"); + return -1; + } + ret = volc_json_read_int(p_config, "video.codec", &video_codec); + if (ret != 0 || video_codec < 0 || video_codec > VIDEO_DATA_TYPE_MJPEG) { + LOGE("volc_rtc_create: read video_codec failed"); + return -1; + } + ret = volc_json_read_int(p_config, "log_level", &log_level); + if (ret != 0) { + log_level = BYTE_RTC_LOG_LEVEL_WARN; // default log level + } + ret = volc_json_read_bool(p_config, "audio.publish", &engine->b_audio_publish); + if (ret != 0) { + engine->b_audio_publish = true; // default to publish audio + } + ret = volc_json_read_bool(p_config, "video.publish", &engine->b_video_publish); + if (ret != 0) { + engine->b_video_publish = false; // default to no publish video + } + ret = volc_json_read_bool(p_config, "audio.subscribe", &engine->b_audio_subscribe); + if (ret != 0) { + engine->b_audio_subscribe = true; // default to subscribe audio + } + ret = volc_json_read_bool(p_config, "video.subscribe", &engine->b_video_subscribe); + if (ret != 0) { + engine->b_video_subscribe = false; // default to no subscribe video + } + + engine->rtc = byte_rtc_create(engine->p_appid, &rtc_event_handler); + byte_rtc_set_user_data(engine->rtc, (void*) engine); + byte_rtc_set_log_level(engine->rtc, log_level); + ret = volc_json_read_object(p_config, "params", &p_params); + if (0 == ret) { + params_cnt = cJSON_GetArraySize(p_params); + if (params_cnt > 0) { + for (int i = 0; i < params_cnt; i++) { + cJSON* p_param = cJSON_GetArrayItem(p_params, i); + if (p_param && cJSON_IsString(p_param)) { + const char* param_str = cJSON_GetStringValue(p_param); + if (param_str && strlen(param_str) > 0) { + byte_rtc_set_params(engine->rtc, param_str); + LOGI("volc_rtc_create: set param[%d]: %s", i, param_str); + } + } + } + } + cJSON_Delete(p_params); + } + byte_rtc_init(engine->rtc); + byte_rtc_set_audio_codec(engine->rtc, __volc_to_rtc_audio_codec(engine->audio_codec)); + byte_rtc_set_video_codec(engine->rtc, video_codec - 1); // -1 for default codec + return 0; +} + +volc_rtc_t volc_rtc_create(const char* appid, void* context, cJSON* p_config, volc_msg_cb message_callback, volc_data_cb data_callback) +{ + rtc_impl_t* rtc = (rtc_impl_t*) volc_osal_calloc(1, sizeof(rtc_impl_t)); + if (!rtc) { + LOGE("volc_rtc_create: malloc rtc failed"); + return NULL; + } + rtc->message_callback = message_callback; + rtc->data_callback = data_callback; + rtc->context = context; + rtc->p_appid = strdup(appid); + if (NULL == rtc->p_appid) { + LOGE("malloc appid memory failed"); + goto err_out_label; + } + + if (__rtc_init(rtc, p_config) != 0) { + LOGE("volc_rtc_create: rtc init failed"); + goto err_out_label; + } + + LOGD("rtc create success"); + return (volc_rtc_t)rtc; +err_out_label: + HAL_SAFE_FREE(rtc->p_appid); + HAL_SAFE_FREE(rtc); + return NULL; +} + +void volc_rtc_destroy(volc_rtc_t handle) +{ + rtc_impl_t* rtc = (rtc_impl_t*)handle; + if (!rtc) { + LOGE("rtc instance is NULL"); + return; + } + __rtc_stop(rtc); + + byte_rtc_fini(rtc->rtc); + while (!rtc->b_fini) { + usleep(1000 * 10); + } + byte_rtc_destroy(rtc->rtc); + HAL_SAFE_FREE(rtc->p_channel_name); + HAL_SAFE_FREE(rtc->p_remote_user_id); + HAL_SAFE_FREE(rtc->p_token); + HAL_SAFE_FREE(rtc); + LOGD("rtc destroy success"); +} + +int volc_rtc_start(volc_rtc_t rtc, const char* bot_id, volc_iot_info_t* iot_info, const char* params) { + int ret = 0; + volc_rtc_option_t *opt = NULL; + rtc_impl_t* rtc_impl = (rtc_impl_t*) rtc; + if (!rtc_impl) { + LOGE("rtc instance is NULL"); + return -1; + } + char* task_id = "test"; + if (volc_get_rtc_config(iot_info, __volc_to_rtc_audio_codec(rtc_impl->audio_codec), bot_id, task_id, &rtc_impl->info, params)) { + LOGE("get rtc config failed"); + return -1; + } + opt = &rtc_impl->info.rtc_opt; + return __rtc_start(rtc_impl, opt); +} + +int volc_rtc_stop(volc_rtc_t handle) { + rtc_impl_t* rtc = (rtc_impl_t *)handle; + if (!rtc) { + LOGE("rtc instance is NULL"); + return -1; + } + __rtc_stop(rtc); + return 0; +} + +int volc_rtc_send(volc_rtc_t handle, const void* data, int size, volc_data_info_t* data_info) { + rtc_impl_t* rtc = (rtc_impl_t *)handle; + audio_frame_info_t audio_info = {0}; + video_frame_info_t video_info = {0}; + if (NULL == rtc || NULL == data || NULL == data_info) { + LOGE("input args is invalid, rtc(%p), data(%p), data_info(%p)", rtc, data, data_info); + return -1; + } + switch (data_info->type) { + case VOLC_DATA_TYPE_AUDIO: { + audio_info.data_type = (audio_data_type_e) data_info->info.audio.data_type; + byte_rtc_send_audio_data(rtc->rtc, rtc->p_channel_name, data, size, &audio_info); + break; + } + case VOLC_DATA_TYPE_VIDEO: { + video_info.data_type = (video_data_type_e) data_info->info.video.data_type; + video_info.stream_type = VIDEO_STREAM_HIGH; + video_info.frame_type = (video_info.data_type == VIDEO_DATA_TYPE_MJPEG) ? VIDEO_FRAME_KEY : VIDEO_FRAME_AUTO_DETECT; + LOGD("Sending video to channel: %s, data length: %d, data type: %d", rtc->p_channel_name, size, video_info.data_type); + byte_rtc_send_video_data(rtc->rtc, rtc->p_channel_name, data, size, &video_info); + break; + } + case VOLC_DATA_TYPE_MESSAGE: { + LOGD("Sending message to channel: %s, remote user: %s, data length: %d, is_binary: %d", + rtc->p_channel_name, rtc->p_remote_user_id, size, data_info->info.message.is_binary); + byte_rtc_rts_send_message(rtc->rtc, rtc->p_channel_name, rtc->p_remote_user_id, data, size, data_info->info.message.is_binary, + RTS_MESSAGE_RELIABLE); + break; + } + default: + LOGW("unsupported data type: %d", data_info->type); + return -1; + } + return 0; +} + +int volc_rtc_interrupt(volc_rtc_t rtc) { + int ret = 0; + uint8_t* msg_ctrl = NULL; + size_t msg_len = 0; + rtc_impl_t* rtc_impl = (rtc_impl_t*) rtc; + volc_data_info_t data_info = {0}; + if (!rtc_impl) { + LOGE("rtc instance is NULL"); + return -1; + } + if (_build_binary_message(MAGIC_CONTROL, interrupt_str, &msg_ctrl, &msg_len) != 0) { + LOGE("build control message failed"); + return -1; + } + data_info.type = VOLC_DATA_TYPE_MESSAGE; + data_info.info.message.is_binary = true; + if ((ret = volc_rtc_send(rtc, msg_ctrl, msg_len, &data_info)) != 0) { + LOGE("send interrupt message failed"); + } + HAL_SAFE_FREE(msg_ctrl); + return ret; +} + +int volc_rtc_send_text_to_agent(volc_rtc_t rtc, const char* text, volc_agent_type_e type, int interrupt_mode){ + int ret = 0; + cJSON * pJsn = cJSON_CreateObject(); + uint8_t* msg_ctrl = NULL; + size_t msg_len = 0; + rtc_impl_t* rtc_impl = (rtc_impl_t*) rtc; + volc_data_info_t data_info = {0}; + if (!rtc_impl) { + LOGE("rtc instance is NULL"); + return -1; + } + if(type == VOLC_AGENT_TYPE_TTS){ + cJSON_AddStringToObject(pJsn,"Command","ExternalTextToSpeech"); + } else if(type == VOLC_AGENT_TYPE_LLM){ + cJSON_AddStringToObject(pJsn,"Command","ExternalTextToLLM"); + } + cJSON_AddStringToObject(pJsn,"Message",text); + cJSON_AddNumberToObject(pJsn,"InterruptMode",interrupt_mode); + char* text_str = cJSON_Print(pJsn); + if (_build_binary_message(MAGIC_CONTROL, text_str, &msg_ctrl, &msg_len) != 0) { + LOGE("build control message failed"); + return -1; + } + data_info.type = VOLC_DATA_TYPE_MESSAGE; + data_info.info.message.is_binary = true; + if ((ret = volc_rtc_send(rtc, msg_ctrl, msg_len, &data_info)) != 0) { + LOGE("send interrupt message failed"); + } + HAL_SAFE_FREE(msg_ctrl); + cJSON_Delete(pJsn); + return ret; +} + +int volc_rtc_send_jpg(volc_rtc_t rtc, void* data, int size) { + return 0; +} diff --git a/firmware/echoear-platform-agent/components/volc_conv_ai/src/transports/high_quality/third_party/volc_rtc_engine_lite/inc/VolcEngineRTCLite.h b/firmware/echoear-platform-agent/components/volc_conv_ai/src/transports/high_quality/third_party/volc_rtc_engine_lite/inc/VolcEngineRTCLite.h new file mode 100644 index 0000000..5040f5f --- /dev/null +++ b/firmware/echoear-platform-agent/components/volc_conv_ai/src/transports/high_quality/third_party/volc_rtc_engine_lite/inc/VolcEngineRTCLite.h @@ -0,0 +1,1194 @@ +// Copyright (2025) Beijing Volcano Engine Technology Ltd. +// SPDX-License-Identifier: MIT + +#ifndef __BYTE_RTC_API_H__ +#define __BYTE_RTC_API_H__ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(_MSC_VER) +#if defined(__BUILDING_BYTE_RTC_SDK__) +#define __byte_rtc_api__ __declspec(dllexport) +#else +#define __byte_rtc_api__ __declspec(dllimport) +#endif +#else +#define __byte_rtc_api__ __attribute__((visibility("default"))) +#endif + +#define BYTE_RTC_API_VERSION "1.1.0" +#define BYTE_RTC_API_VERSION_NUM 0x1100 + + +/** + * @locale zh + * @type errorcode + * @brief 回调错误码。
    + * SDK 内部遇到不可恢复的错误时,会通过 `on_global_error` 或 `on_room_error` 回调通知用户。 + */ + +typedef enum { + /** + * @locale zh + * @brief Token 无效。 + * 加入房间时使用的 Token 无效或过期失效。需要用户重新获取 Token,并调用 `byte_rtc_renew_token` 方法更新 Token。 + */ + ERR_INVALID_TOKEN = -1000, + /** + * @locale zh + * @brief 加入房间错误。 + * 加入房间时发生未知错误导致加入房间失败。需要用户重新加入房间。 + */ + ERR_JOIN_ROOM = -1001, + /** + * @locale zh + * @brief 没有发布音视频流权限。 + * 用户在所在房间中发布音视频流失败,失败原因为用户没有发布流的权限。 + */ + ERR_NO_PUBLISH_PERMISSION = -1002, + /** + * @locale zh + * @brief 没有订阅音视频流权限。 + * 用户订阅所在房间中的音视频流失败,失败原因为用户没有订阅流的权限。 + */ + ERR_NO_SUBSCRIBE_PERMISSION = -1003, + /** + * @locale zh + * @brief 相同用户 ID 的用户加入本房间,当前用户被踢出房间 + */ + ERR_NO_DUPLICATE_LOGIN = -1004, + /** + * @locale zh + * @brief 服务端调用 OpenAPI 将当前用户踢出房间 + */ + ERR_KICKED_OUT = -1006, + /** + * @locale zh + * @brief 当调用 `byte_rtc_join_room` ,如果room 非法,会返回null,并抛出该error + */ + ERR_ROOMID_ILLEGAL = -1007, + /** + * @locale zh + * @brief Token 过期。调用 `byte_rtc_join_room` 使用新的 Token 重新加入房间。 + */ + ERR_ROOM_TOKEN_EXPIRED = -1009, + /** + * @locale zh + * @brief 调用 `updateToken` 传入的 Token 无效 + */ + ERR_UPDATE_TOKEN_WITH_INVALID_TOKEN = -1010, + /** + * @locale zh + * @brief 服务端调用 OpenAPI 解散房间,所有用户被移出房间。 + */ + ERR_ROOM_DISMISS = -1011, + /** + * @locale zh + * @brief 加入房间错误。
    + * 加入房间时, LICENSE 计费账号未使用 LICENSE_AUTHENTICATE SDK,加入房间错误。 + */ + ERR_JOIN_ROOM_WITHOUT_LICENSE_AUTHENTICATE_SDK = -1012, + /** + * @locale zh + * @brief 通话回路检测已经存在同样 room 的房间了 + */ + ERR_ROOM_ALREADY_EXIST = -1013, + /** + * @locale zh + * @brief 加入多个房间时使用了不同的 uid。
    + * 同一个引擎实例实例中,用户需使用同一个 uid 加入不同的房间。 + */ + ERR_USERID_DIFFERENT = -1014, + /** + * @locale zh + * @brief 服务端license过期,拒绝加入房间。
    + */ + ERR_JOIN_ROOM_SERVER_LICENSE_EXPIRED = -1017, + /** + * @locale zh + * @brief 超过服务端license许可的并发量上限,拒绝加入房间。
    + */ + ERR_JOIN_ROOM_EXCEEDS_THE_UPPER_LIMIT = -1018, + /** + * @locale zh + * @brief license参数错误,拒绝加入房间。
    + */ + ERR_JOIN_ROOM_LICENSE_PARAMETER_ERROR = -1019, + /** + * @locale zh + * @brief license 证书路径错误。
    + */ + ERR_JOIN_ROOM_LICENSE_FILE_PATH = -1020, + /** + * @locale zh + * @brief license 证书不合法。
    + */ + ERR_JOIN_ROOM_LICENSE_ILLEGAL = -1021, + /** + * @locale zh + * @brief license 证书已经过期,拒绝加入房间。
    + */ + ERR_JOIN_ROOM_LICENSE_EXPIRED = -1022, + /** + * @locale zh + * @brief license 证书内容不匹配。
    + */ + ERR_JOIN_ROOM_LICENSE_INFORMATION_NOT_MATCH = -1023, + /** + * @locale zh + * @brief license 当前证书与缓存证书不匹配。
    + */ + ERR_JOIN_ROOM_LICENSE_NOT_MATCH_WITH_CACHE = -1024, + /** + * @locale zh + * @brief 房间被封禁。
    + */ + ERR_JOIN_ROOM_FORBIDDEN = -1025, + /** + * @locale zh + * @brief 用户被封禁。
    + */ + ERR_JOIN_ROOM_USER_FORBIDDEN = -1026, + /** + * @locale zh + * @brief 订阅音视频流失败,订阅音视频流总数超过上限。 + */ + ERR_OVER_STREAM_SUBSCRIBE_LIMIT = -1070, + /** + * @locale zh + * @brief 发布流失败,发布流总数超过上限。 + * RTC 系统会限制单个房间内发布的总流数,总流数包括视频流、音频流和屏幕流。如果房间内发布流数已达上限时,本地用户再向房间中发布流时会失败,同时会收到此错误通知。 + */ + ERR_OVER_STREAM_PUBLISH_LIMIT = -1080, + /** + * @locale zh + * @brief 服务端异常状态导致退出房间。
    + * SDK与信令服务器断开,并不再自动重连,可联系技术支持。
    + */ + ERR_ABNORMAL_SERVER_STATUS = -1084, + +} error_code_e; + +/** Error code. */ + +/** + * @locale zh + * @type keytype + * @brief 用户离线原因。 + */ +typedef enum { + /** + * @locale zh + * @brief 用户主动离线 + */ + USER_OFFLINE_QUIT = 0, + /** + * @locale zh + * @brief 用户超时掉线 + */ + USER_OFFLINE_DROPPED = 1, + +} user_offline_reason_e; + +/** + * @locale zh + * @type keytype + * @brief 视频数据类型。 + */ +typedef enum { + /** + * @locale zh + + * @brief 未知视频数据类型 + */ + VIDEO_DATA_TYPE_UNKNOWN = 0, + + /** + * @locale zh + + * @brief H264 + */ + VIDEO_DATA_TYPE_H264 = 1, + /** + * @locale zh + * @brief BYTEVC1 + */ + VIDEO_DATA_TYPE_BYTEVC1 = 2, + + /** + * @locale zh + * @brief MJPEG + */ + VIDEO_DATA_TYPE_MJPEG = 3, + + +} video_data_type_e; + +/** + * @locale zh + * @type keytype + * @brief 视频帧类型。 + */ +typedef enum { + /** + * @locale zh + * @brief 未知类型
    + * 如果设置为 `VIDEO_FRAME_AUTO_DETECT`,SDK 会自行判断视频帧类型。 + */ + VIDEO_FRAME_AUTO_DETECT = 0, + /** + * @locale zh + * @brief 关键帧 + */ + VIDEO_FRAME_KEY = 1, + /** + * @locale zh + * @brief P 帧 + */ + VIDEO_FRAME_DELTA = 2, +} video_frame_type_e; + + +/** + * @locale zh + * @type keytype + * @brief 视频流类型。 + */ +typedef enum { + /** + * @locale zh + * @brief 主流 + */ + VIDEO_STREAM_HIGH = 0, + /** + * @locale zh + * @brief 辅流 + */ + VIDEO_STREAM_LOW = 1, + +} video_stream_type_e; + +/** + * @locale zh + * @type keytype + * @brief 视频帧信息。 + */ +typedef struct { + /** + * @locale zh + * @brief 视频数据类型,参看 video_data_type_e{@link #video_data_type_e}。 + */ + video_data_type_e data_type; + /** + * @locale zh + * @brief 视频流类型,参看 video_data_type_e{@link #video_data_type_e}。 + */ + video_stream_type_e stream_type; + /** + * @locale zh + * @brief 视频帧类型,参看 video_frame_type_e{@link #video_frame_type_e}。 + */ + video_frame_type_e frame_type; + /** + * @locale zh + * @brief 视频帧率 + */ + int frame_rate; + +} video_frame_info_t; + +/** + * @locale zh + * @type keytype + * @brief 音频编码类型。 + */ +typedef enum { + /** + * @locale zh + * @brief OPUS + */ + AUDIO_CODEC_TYPE_OPUS = 1, + /** + * @locale zh + * @brief G722 + */ + AUDIO_CODEC_TYPE_G722 = 2, + /** + * @locale zh + * @brief AACLC + */ + AUDIO_CODEC_TYPE_AACLC = 3, + + /** + * @locale zh + * @brief G711A + */ + AUDIO_CODEC_TYPE_G711A = 4, + + /** + * @locale zh + * @brief G711U + */ + AUDIO_CODEC_TYPE_G711U = 5, + +} audio_codec_type_e; + + +/** + * @locale zh + * @type keytype + * @brief 视频编码类型。 + */ +/** + * @locale en + * @type keytype + * @brief video codec type list. + */ +typedef enum { + /** + * @locale zh + * @brief 编码类型H264 + */ + /** + * @locale en + * @brief codec type H264 + */ + VIDEO_CODEC_TYPE_H264 = 0, + + /** + * @locale zh + * @brief 编码类型ByteVC1 + */ + /** + * @locale en + * @brief codec type ByteVC1 + */ + VIDEO_CODEC_TYPE_BYTEVC1 = 1, + + + /** + * @locale zh + * @brief 编码类型MJPEG + */ + /** + * @locale en + * @brief codec type MJPEG + */ + VIDEO_CODEC_TYPE_MJPEG = 2, + +} video_codec_type_e; + +/** + * @locale zh + * @type keytype + * @brief 音频数据类型。 + */ +typedef enum { + /** + * @locale zh + * @brief OPUS + */ + AUDIO_DATA_TYPE_UNKNOWN = 0, + /** + * @locale zh + * @brief OPUS + */ + AUDIO_DATA_TYPE_OPUS = 1, + /** + * @locale zh + * @brief G722 + */ + AUDIO_DATA_TYPE_G722 = 2, + /** + * @locale zh + * @brief AACLC + */ + AUDIO_DATA_TYPE_AACLC = 3, + + /** + * @locale zh + * @brief PCMA + */ + AUDIO_DATA_TYPE_PCMA = 4, + + /** + * @locale zh + * @brief PCM + */ + AUDIO_DATA_TYPE_PCM = 5, + + /** + * @locale zh + * @brief PCMU + */ + AUDIO_DATA_TYPE_PCMU = 6, +} audio_data_type_e; + +/** + * @locale zh + * @type keytype + * @brief 音频帧信息。 + */ +typedef struct { + /** + * @locale zh + * @brief 音频数据类型,参看 audio_data_type_e{@link #audio_data_type_e}。 + */ + audio_data_type_e data_type; + +} audio_frame_info_t; + +/** + * @locale zh + * @type keytype + * @brief SDK 日志等级。 + */ +typedef enum { + /** + * @locale zh + * @brief 调试级别。 + */ + BYTE_RTC_LOG_LEVEL_DEBUG = 0, + /** + * @locale zh + * @brief 信息级别。 + */ + BYTE_RTC_LOG_LEVEL_INFO = 1, + /** + * @locale zh + * @brief 警告级别。 + */ + BYTE_RTC_LOG_LEVEL_WARN = 2, + /** + * @locale zh + * @brief 错误级别。 + */ + BYTE_RTC_LOG_LEVEL_ERROR = 3, + /** + * @locale zh + * @brief 严重错误级别。 + */ + BYTE_RTC_LOG_LEVEL_FATAL = 4 + +} byte_rtc_log_level_e; + +/** + * @locale zh + * @type keytype + * @brief 房间音视频自动订阅选项。 + */ +typedef struct { + /** + * @locale zh + * @brief 是否自动订阅远端用户的音频流。
    + * - true: 是 + * - false:否 + */ + bool auto_subscribe_audio; + /** + * @locale zh + * @brief 是否自动订阅远端用户的视频流。
    + * - true: 是 + * - false:否 + */ + bool auto_subscribe_video; + + /** + * @locale zh + * @brief 是否自动发布本端用户的音频流。
    + * - true: 是 + * - false:否 + */ + + bool auto_publish_audio; + + /** + * @locale zh + * @brief 是否自动发布本端用户的视频流。
    + * - true: 是 + * - false:否 + */ + bool auto_publish_video; + +} byte_rtc_room_options_t; + +/** + * @locale zh + * @type keytype + * @brief 网络事件类型。 + * @hidden + */ +typedef enum { + NETWORK_EVENT_DOWN = 0, + NETWORK_EVENT_UP, + NETWORK_EVENT_CHANGE, +} network_event_type_e; + +/** + * @locale zh + * @type keytype + * @brief 实时信令消息类型。 + */ +typedef enum { + /** + * @locale zh + * @brief 可靠消息 + */ + RTS_MESSAGE_RELIABLE = 0, + /** + * @locale zh + * @brief 不可靠有序消息 + */ + RTS_MESSAGE_UNRELIABLE_ORDERED = 1, + /** + * @locale zh + * @brief 不可靠无序消息 + */ + RTS_MESSAGE_UNRELIABLE_UNORDERED = 2 +} rts_message_type; + +/** + * @locale zh + * @type keytype + * @brief 流状态。 + */ +typedef enum { + /** + * @locale zh + * @brief 流发布能力已具备,本地用户可以发布流。 + */ + STREAM_STATUS_PUBLISHED = 0, + /** + * @locale zh + * @brief 流发布能力已释放,本地用户不能发布流。 + */ + STREAM_STATUS_UNPUBLISHED = 1, + /** + * @locale zh + * @brief 流订阅能力已具备,本地用户可以订阅流。 + */ + STREAM_STATUS_SUBSCRIBED = 2, + /** + * @locale zh + * @brief 流订阅能力已释放,本地用户不能订阅流。 + */ + STREAM_STATUS_UNSUBSCRIBED = 3, +} stream_status_e; + + + +typedef void * byte_rtc_engine_t; + + +/** + * @locale zh + * @type callback + * @brief SDK 事件回调结构体 + * @note 回调函数是在 SDK 内部线程同步抛出来的,请不要做耗时操作,否则可能导致 SDK 运行异常。 + */ +typedef struct { + +/** + * @locale zh + * @type callback + * @list 回调 + * @order 0 + * @brief SDK 错误信息回调
    + * @param engine 通过byte_rtc_create{@link #byte_rtc_create}创建的引擎实例 + * @param code 错误码,参看 error_code_e{@link #error_code_e} + * @param msg 错误信息 + */ +void (*on_global_error)(byte_rtc_engine_t engine, int code, const char* msg); + +/** + * @locale zh + * @type callback + * @list 回调 + * @order 2 + * @brief 加入房间成功回调。 + * @param engine 通过byte_rtc_create{@link #byte_rtc_create}创建的引擎实例 + * @param room 房间名 + * @param elapsed_ms 从开始加入房间到加入房间成功的耗时,单位:毫秒 + * @param rejoin 当网络断开时,重连后自动触发重新加入房间 + * - True:重新加入房间 + * - False:首次加入房间 + */ +void (*on_join_room_success)(byte_rtc_engine_t engine, const char* room, int elapsed_ms, bool rejoin); + + +/** + * @locale zh + * @type callback + * @list 回调 + * @order 1 + * @brief 加入房间失败或异常退出房间回调 + * @param engine 通过byte_rtc_create{@link #byte_rtc_create}创建的引擎实例 + * @param room 房间名 + * @param code 错误码,参看 error_code_e{@link #error_code_e} + * @param msg 错误信息 + */ +void (*on_room_error)(byte_rtc_engine_t engine, const char* room, int code, const char* msg); + + +/** + * @locale zh + * @type callback + * @list 回调 + * @order 3 + * @brief 远端用户加入房间回调
    + 远端用户断网后重新连入房间时,房间内其他用户将收到该事件
    + 新加入房间用户也会收到加入房间前已在房间内的用户的入房间回调通知 + * @param engine 通过byte_rtc_create{@link #byte_rtc_create}创建的引擎实例 + * @param room 房间名 + * @param uid 远端用户名 + * @param elapsed_ms 加入房间耗时(保留字段) + */ +void (*on_user_joined)(byte_rtc_engine_t engine, const char* room, const char* uid, int elapsed_ms); + +/** + * @locale zh + * @type callback + * @list 回调 + * @order 4 + * @brief 远端用户离开房间
    + * 房间内其他用户会收到此事件 + * @param engine 通过byte_rtc_create{@link #byte_rtc_create}创建的引擎实例 + * @param room 房间名 + * @param uid 远端用户名 + * @param reason 用户离开房间的原因, 参看 user_offline_reason_e{@link #user_offline_reason_e} + */ +void (*on_user_offline)(byte_rtc_engine_t engine, const char* room, const char* uid, int reason); + +/** + * @locale zh + * @type callback + * @list 回调 + * @order 8 + * @brief 房间内用户暂停/恢复发送音频流时,房间内其他用户会收到此回调 + * @param engine 通过byte_rtc_create{@link #byte_rtc_create}创建的引擎实例 + * @param room 房间名 + * @param uid 远端用户名 + * @param muted 发送状态
    +* - true(1):不发送
    + * - false(0):发送 + */ +void (*on_user_mute_audio)(byte_rtc_engine_t engine, const char* room, const char* uid, int muted); + +/** + * @locale zh + * @type callback + * @list 回调 + * @order 7 + * @brief 房间内用户暂停/恢复发送视频流时,房间内其他用户会收到此回调 + * @param engine 通过byte_rtc_create{@link #byte_rtc_create}创建的引擎实例 + * @param room 房间名 + * @param uid 远端用户名 + * @param muted 发送状态
    +* - true(1):不发送
    + * - false(0):发送 + */ +void (*on_user_mute_video)(byte_rtc_engine_t engine, const char* room, const char* uid, int muted); + +/** + * @locale zh + * @type callback + * @list 回调 + * @order 9 + * @brief 提示流发布端需重新生成关键帧的回调 + * @param engine 通过byte_rtc_create{@link #byte_rtc_create}创建的引擎实例 + * @param room 房间名 + * @param uid 远端用户名 + */ +void (*on_key_frame_gen_req)(byte_rtc_engine_t engine, const char* room, const char* uid); + +/** + * @locale zh + * @type callback + * @list 回调 + * @order 6 + * @brief 返回远端单个用户的音频数据 + * @param engine 通过byte_rtc_create{@link #byte_rtc_create}创建的引擎实例 + * @param room 房间名 + * @param uid 远端用户名 + * @param sent_ts 发送时间 (暂不支持) + * @param data 音频数据类型,参看 audio_data_type_e{@link #audio_data_type_e} + * @param data_ptr 音频数据 + * @param data_len 音频数据长度,单位字节 + * @param extra_info extra_info数据 + * @param extra_info_size extra_info长度,单位字节 + */ +void (*on_audio_data)(byte_rtc_engine_t engine, const char* room, const char* uid, uint16_t sent_ts, + audio_data_type_e codec, const void * data_ptr, size_t data_len,const uint8_t* extra_info, size_t extra_info_size); + +/** + * @locale zh + * @type callback + * @list 回调 + * @order 5 + * @brief 返回远端单个用户的视频数据 + * @param engine 通过byte_rtc_create{@link #byte_rtc_create}创建的引擎实例 + * @param room 房间名 + * @param uid 远端用户名 + * @param sent_ts 发送时间(暂不支持) + * @param codec 视频编码类型,参看 video_data_type_e{@link #video_data_type_e} + * @param is_key_frame 帧类型 + * - 0: 非关键帧 + * - !0: 关键帧 + * @param data_ptr 视频数据 + * @param data_len 视频数据长度,单位字节 + */ +void (*on_video_data)(byte_rtc_engine_t engine, const char* room, const char* uid, uint16_t sent_ts, + video_data_type_e codec, int is_key_frame, + const void * data_ptr, size_t data_len); + +/** + * @locale zh + * @type callback + * @list 回调 + * @order 10 + * @brief 当带宽估计码率发生变化时,触发该回调。
    + * 此时你需要将编码器码率调至目标码率 + * @param engine 通过byte_rtc_create{@link #byte_rtc_create}创建的引擎实例 + * @param room 房间名 + * @param target_bps 目标码率,单位 bps + */ +void (*on_target_bitrate_changed)(byte_rtc_engine_t engine, const char* room, uint32_t target_bps); + + +/** + * @locale zh + * @type callback + * @list 回调 + * @order 12 + * @brief 返回远端用户发送的实时信令消息 + * @param engine 通过byte_rtc_create{@link #byte_rtc_create}创建的引擎实例 + * @param room 房间名 + * @param src 远端用户名 + * @param message 实时信令消息 + * @param size 实时信令消息长度 + * @param binary 是否未二进制消息 + */ + +void (*on_message_received)(byte_rtc_engine_t engine, const char* room, const char* src, const uint8_t* message, int size, bool binary); + + +/** + * @locale zh + * @type callback + * @list 回调 + * @order 11 + * @brief 实时信令消息发送结果通知 + * @param engine 通过byte_rtc_create{@link #byte_rtc_create}创建的引擎实例 + * @param room 房间名 + * @param msgid 发送消息的id,用来和发送的消息匹配 + * @param error 发送消息错误码,0表示发送成功 + * @param extencontent 扩展信息,暂时未使用 + */ +void (*on_message_send_result)(byte_rtc_engine_t engine, const char* room, int64_t msgid, int error, const char* extencontent); + +/** + * @locale zh + * @type callback + * @list 回调 + * @order 13 + * @brief Token 加入房间权限过期前 30 秒,触发该回调。
    + * 收到该回调后,你需调用 `byte_rtc_renew_token` 更新 Token 加入房间权限 + * @param engine 通过byte_rtc_create{@link #byte_rtc_create}创建的引擎实例 + * @param room 房间名 + */ +void (*on_token_privilege_will_expire)(byte_rtc_engine_t engine, const char* room); + +/** + * @locale zh + * @type callback + * @list 回调 + * @order 14 + * @brief license 过期提醒。在剩余天数低于 30 天时,收到此回调 + * @param engine 通过byte_rtc_create{@link #byte_rtc_create}创建的引擎实例 + * @param daysleft license 剩余有效天数 + */ + +void (*on_license_expire_warning)(byte_rtc_engine_t engine, int daysleft); + +/** + * @locale zh + * @type callback + * @list 回调 + * @order 15 + * @brief engine 实例清理(byte_rtc_fini)结束通知,只有收到该通知之后,重新创建实例(byte_rtc_init)才是安全的 + * @param engine 通过byte_rtc_create{@link #byte_rtc_create}创建的引擎实例 + */ + +void (*on_fini_notify)(byte_rtc_engine_t engine); + + +/** + * @locale zh + * @type callback + * @list 回调 + * @order 16 + * @brief agic场景下使用license模式,当配额用尽时,触发该回调。当收到该回调时,请及时续费。当配额用尽的时候,智能体会自动离房,会话已不能继续进行,应用层应该调用反初始化接口,待完成充值之后再重新初始化。 + * @param engine 通过byte_rtc_create{@link #byte_rtc_create}创建的引擎实例 + * @param message 配额即将用尽的提示信息 + * @param extra 扩展信息,暂未使用 + + */ +void (*on_quota_exceeded)(byte_rtc_engine_t engine, const char* message, const char* extra); + + +/** + * @locale zh + * @type callback + * @list 回调 + * @order 17 + * @brief 当本地用户发布或者订阅流状态发生变化时,触发该回调。 + * @param engine 通过byte_rtc_create{@link #byte_rtc_create}创建的引擎实例 + * @param room 房间名 + * @param uid 用户id + * @param status 流状态,参看 stream_status_e{@link #stream_status_e} + * @param extra 扩展信息,暂未使用 + */ +void (*on_stream_status_changed)(byte_rtc_engine_t engine, const char* room, const char* uid, stream_status_e status, const char* extra); + +/** +* @locale zh +* @type callback +* @list 回调 +* @order 18 +* @brief 远端用户加入房间且表明自己会发送complete sub信令时
    +* 房间内其他用户会收到此事件 +* @param engine 通过byte_rtc_create{@link #byte_rtc_create}创建的引擎实例 +* @param room 房间名 +* @param uid 远端用户名 +* @param enable_audio 是否发送complete sub Audio +* @param enable_video 是否发送complete sub Video +*/ +void (*on_user_subscribe_completed)(byte_rtc_engine_t engine, const char* room, const char* uid,bool enable_audio,bool enable_video); +} byte_rtc_event_handler_t; + + +/** + * @locale zh + * @type api + * @list 方法 + * @order 1 + * @brief 获取 SDK 版本号 + * @return SDK 版本号 + */ +extern __byte_rtc_api__ const char* byte_rtc_get_version(void); + +/** + * @locale zh + * @type api + * @hidden + * @brief 错误码转对应字符串 + * @note 不必释放此字符串 + * @param err 错误码 + * @return 错误信息 + */ +extern __byte_rtc_api__ const char* byte_rtc_err_2_str(int err); + +/** + * @locale zh + * @type api + * @list 方法 + * @order 7 + * @brief 设置 SDK 日志等级 + * @param engine 通过byte_rtc_create{@link #byte_rtc_create}创建的引擎实例 + * @param level 日志等级,参看 byte_rtc_log_level_e{@link #byte_rtc_log_level_e} + */ +extern __byte_rtc_api__ void byte_rtc_set_log_level(byte_rtc_engine_t engine, int level); + +/** + * @locale zh + * @type api + * @list 方法 + * @order 5 + * @brief 设置 SDK 日志文件路径、大小和数目 + * @param engine 通过byte_rtc_create{@link #byte_rtc_create}创建的引擎实例 + * @param log_path 日志文件存放路径 + * @param size_per_file 单个日志文件大小,单位字节 + * @param max_file_count 日志文件最大个数 + * @return 方法调用结果:
    + * - 0:成功 + * - -1:失败,路径参数无效 + */ +extern __byte_rtc_api__ int byte_rtc_config_log(byte_rtc_engine_t engine, const char* log_path, int size_per_file, int max_file_count); + +/** + * @locale zh + * @type api + * @list 方法 + * @order 3 + * @brief 创建引擎实例,该方法是整个SDK调用的第一个方法 + * @param app_id 应用 ID + * @param event_handler 回调方法,参看 byte_rtc_event_handler_t{@link #byte_rtc_event_handler_t} + * @return 引擎实例 + */ +extern __byte_rtc_api__ byte_rtc_engine_t byte_rtc_create(const char* app_id, const byte_rtc_event_handler_t * event_handler); + +/** + * @locale zh + * @type api + * @list 方法 + * @order 13 + * @brief 初始化引擎实例 + * @note 仅能被初始化一次 + * @param engine 通过byte_rtc_create{@link #byte_rtc_create}创建的引擎实例 + * @return 方法调用结果:
    + * - 0:成功
    + * - -1: appid 或 event_handler 为空
    + * - -2:引擎实例已被初始化
    + * - -3:引擎实例创建失败,请检查是否有可用内存 + */ +extern __byte_rtc_api__ int byte_rtc_init(byte_rtc_engine_t engine); + +/** + * @locale zh + * @type api + * @list 方法 + * @order 35 + * @brief 销毁引擎实例,VolcEngineRTCLite内部完成销毁操作之后,通过回调(on_fini_notify)通知上层 参看 byte_rtc_event_handler_t{@link #byte_rtc_event_handler_t} + * @param engine 通过byte_rtc_create{@link #byte_rtc_create}创建的引擎实例 + * @return 方法调用结果: + * - 0:成功
    + * - -1: 引擎实例不存在 + */ +extern __byte_rtc_api__ int byte_rtc_fini(byte_rtc_engine_t engine); + +/** + * @locale zh + * @type api + * @list 方法 + * @order 37 + * @brief 销毁引擎实例,只有在收到on_fini_notify的回调之后,调用此方法才是安全的 + * @param engine 通过byte_rtc_create{@link #byte_rtc_create}创建的引擎实例 + */ +extern __byte_rtc_api__ void byte_rtc_destroy(byte_rtc_engine_t engine); + + +/** + * @locale zh + * @type api + * @list 方法 + * @order 9 + * @brief 将自定义的数据与引擎实例关联起来 + * @param engine 通过byte_rtc_create{@link #byte_rtc_create}创建的引擎实例 + * @param user_data 设置用户自定义数据 + */ +extern __byte_rtc_api__ void byte_rtc_set_user_data(byte_rtc_engine_t engine, void * user_data); + + +/** + * @locale zh + * @type api + * @list 方法 + * @order 11 + * @brief 获取与引擎实例相关联的自定义数据 + * @param engine 通过byte_rtc_create{@link #byte_rtc_create}创建的引擎实例 + */ +extern __byte_rtc_api__ void * byte_rtc_get_user_data(byte_rtc_engine_t engine); + +/** + * @locale zh + * @type api + * @list 方法 + * @order 15 + * @brief 设置音频编码格式 + * @param engine 通过byte_rtc_create{@link #byte_rtc_create}创建的引擎实例 + * @param audio_codec_type 音频编码类型,参看 audio_codec_type_e{@link #audio_codec_type_e} + * @return 方法调用结果:
    + * - 0:成功
    + * - -1: 引擎实例不存在
    + * - -2:编码格式暂不被支持 + */ +extern __byte_rtc_api__ int byte_rtc_set_audio_codec(byte_rtc_engine_t engine, audio_codec_type_e audio_codec_type); + +/** + * @locale zh + * @type api + * @list 方法 + * @order 17 + * @brief 设置视频编码格式,暂仅支持 `VIDEO_CODEC_TYPE_H264`{@link #VIDEO_CODEC_TYPE_H264} 和 `VIDEO_CODEC_TYPE_BYTEVC1`{@link #VIDEO_CODEC_TYPE_BYTEVC1} + * @param engine 通过byte_rtc_create{@link #byte_rtc_create}创建的引擎实例 + * @param video_codec_type 视频编码类型,参看 video_codec_type{@link #video_codec_type} + * @return 方法调用结果:
    + * - 0:成功。
    + * - -1: 引擎不存在。
    + * - -2:编码格式暂不被支持。 + */ +extern __byte_rtc_api__ int byte_rtc_set_video_codec(byte_rtc_engine_t engine, video_codec_type_e video_codec_type); + +/** + * @locale zh + * @type api + * @list 方法 + * @order 19 + * @brief 加入房间 + * @param engine 通过byte_rtc_create{@link #byte_rtc_create}创建的引擎实例 + * @param room 房间名 + * @param uid 用户名 + * @param token 动态密钥,用于对加入房间用户进行鉴权验证
    + * @param options 房间音视频自动订阅和发布设置,参看 byte_rtc_room_options_t{@link #byte_rtc_room_options_t}
    + * @return 方法调用结果:
    + * - 0:成功
    + * - -1:引擎实例不存在
    + * - -2:输入参数为空
    + * - -3:已加入过房间 + */ +extern __byte_rtc_api__ int byte_rtc_join_room(byte_rtc_engine_t engine, const char* room, const char* uid, + const char* token, byte_rtc_room_options_t* options); + + +/** + * @locale zh + * @type api + * @order 33 + * @list 方法 + * @brief 退出房间 + * @param engine 通过byte_rtc_create{@link #byte_rtc_create}创建的引擎实例 + * @param room 房间名 + * @return 方法调用结果:
    + * - 0:成功
    + * - -1:引擎实例不存在
    + * - -2:输入参数为空 + */ +extern __byte_rtc_api__ int byte_rtc_leave_room(byte_rtc_engine_t engine, const char* room); + +/** + * @locale zh + * @type api + * @list 方法 + * @order 31 + * @brief 更新 Token
    + * 收到 on_token_privilege_will_expire{@link #byte_rtc_event_handler_t#on_token_privilege_will_expire} 时,必须重新获取 Token,调用此方法更新 Token,以保证通话的正常进行 + * @param engine 通过byte_rtc_create{@link #byte_rtc_create}创建的引擎实例 + * @param room 房间名 + * @param token 动态密钥。用于对加入房间用户进行鉴权验证
    + * @return 方法调用结果:
    + * - 0:成功
    + * - -1:引擎实例不存在
    + * - -2:输入参数为空 + */ +extern __byte_rtc_api__ int byte_rtc_renew_token(byte_rtc_engine_t engine, const char* room, const char* token); + +/** + * @locale zh + * @type api + * @list 方法 + * @order 27 + * @brief 控制媒体流(本端 &远端)(音频 & 视频)流状态 + * @param engine 通过byte_rtc_create{@link #byte_rtc_create}创建的引擎实例 + * @param room 房间名 + * @param uid 用户Id,非空控制的是远端的用户,空值控制的是本端用户 + * @param video 媒体类型,true:视频流,false:音频流 + * @param mute 接收状态 + * @return 方法调用结果:
    + * - 0:成功
    + * - -1:引擎实例不存在
    + * - -2:输入参数为空 + */ +extern __byte_rtc_api__ int byte_rtc_mute(byte_rtc_engine_t engine, const char* room, const char* uid, bool video, bool mute); + +/** + * @locale zh + * @type api + * @list 方法 + * @order 29 + * @brief 请求远端用户重编关键帧 + * @param engine 通过byte_rtc_create{@link #byte_rtc_create}创建的引擎实例 + * @param room 房间名 + * @param remote_uid 远端用户名 + * @return 方法调用结果:
    + * - 0:成功
    + * - -1:引擎实例不存在
    + * - -2:输入参数为空 + */ +extern __byte_rtc_api__ int byte_rtc_request_video_key_frame(byte_rtc_engine_t engine, const char* room, const char* remote_uid); + +/** + * @locale zh + * @type api + * @list 方法 + * @order 21 + * @brief 发送音频帧 + * @param engine 通过byte_rtc_create{@link #byte_rtc_create}创建的引擎实例 + * @param room 房间名 + * @param data_ptr 音频数据 + * @param data_len 数据长度,单位字节 + * @param info_ptr 音频帧信息,参看 audio_frame_info_t{@link #audio_frame_info_t} + * @return 方法调用结果:
    + * - 0:成功
    + * - -1:引擎实例不存在
    + * - -2:输入参数为空 + */ +extern __byte_rtc_api__ int byte_rtc_send_audio_data(byte_rtc_engine_t engine, const char* room, const void * data_ptr, size_t data_len, + audio_frame_info_t * info_ptr); + +/** + * @locale zh + * @type api + * @list 方法 + * @order 23 + * @brief 发送视频帧 + * @param engine 通过byte_rtc_create{@link #byte_rtc_create}创建的引擎实例 + * @param room 房间名 + * @param data_ptr 视频数据 + * @param data_len 数据长度 + * @param info_ptr 视频帧信息,参看 video_frame_info_t{@link #video_frame_info_t} + * @note - 仅支持 `VIDEO_DATA_TYPE_H264`
    + * - 每个用户仅支持一路流,仅使用 `VIDEO_STREAM_HIGH` + * @return 方法调用结果:
    + * - 0:成功
    + * - -1:引擎实例不存在
    + * - -2:输入参数为空 + */ +extern __byte_rtc_api__ int byte_rtc_send_video_data(byte_rtc_engine_t engine, const char* room, const void *data_ptr, size_t data_len, + video_frame_info_t * info_ptr); + + +/** + * @locale zh + * @type api + * @list 方法 + * @order 25 + * @brief 发送实时信令消息 + * @param engine 通过byte_rtc_create{@link #byte_rtc_create}创建的引擎实例 + * @param room 房间名 + * @param target 接收消息的目标用户,如果target传NULL表示发送的是房间内广播消息 + * @param data_ptr 实时信令消息数据 + * @param data_len 实时信令消息长度 + * @param binary 指定消息是否是二进制消息 + * @param type 用于指定实时信令消息类型,对于房间内广播消息,仅支持RTS_MESSAGE_RELIABLE + + * @return 方法调用结果:
    + * - < 0:失败
    + * - >= 0:消息id
    + */ + +extern __byte_rtc_api__ int64_t byte_rtc_rts_send_message(byte_rtc_engine_t engine, const char* room, const char* target, const void * data_ptr, + size_t data_len, bool binary, rts_message_type type); + +/** + * @locale zh + * @type api + * @hidden + * @brief 设置 SDK 参数 + * @param engine 通过byte_rtc_create{@link #byte_rtc_create}创建的引擎实例 + * @param params json 格式参数 + * @return 方法调用结果:
    + * - 0:成功
    + * - <0:失败 + */ +extern __byte_rtc_api__ int byte_rtc_set_params(byte_rtc_engine_t engine, const char* params); + +#ifdef __cplusplus +} +#endif +#endif /* __BYTE_RTC_API_H__ */ diff --git a/firmware/echoear-platform-agent/components/volc_conv_ai/src/transports/high_quality/third_party/volc_rtc_engine_lite/libs/esp32p4/libVolcEngineRTCLite.a b/firmware/echoear-platform-agent/components/volc_conv_ai/src/transports/high_quality/third_party/volc_rtc_engine_lite/libs/esp32p4/libVolcEngineRTCLite.a new file mode 100644 index 0000000..b4d7b03 Binary files /dev/null and b/firmware/echoear-platform-agent/components/volc_conv_ai/src/transports/high_quality/third_party/volc_rtc_engine_lite/libs/esp32p4/libVolcEngineRTCLite.a differ diff --git a/firmware/echoear-platform-agent/components/volc_conv_ai/src/transports/high_quality/third_party/volc_rtc_engine_lite/libs/esp32s3/libVolcEngineRTCLite.a b/firmware/echoear-platform-agent/components/volc_conv_ai/src/transports/high_quality/third_party/volc_rtc_engine_lite/libs/esp32s3/libVolcEngineRTCLite.a new file mode 100644 index 0000000..70cc99e Binary files /dev/null and b/firmware/echoear-platform-agent/components/volc_conv_ai/src/transports/high_quality/third_party/volc_rtc_engine_lite/libs/esp32s3/libVolcEngineRTCLite.a differ diff --git a/firmware/echoear-platform-agent/components/volc_conv_ai/src/transports/high_quality/third_party/volc_rtc_engine_lite/libs/linux_x64/libVolcEngineRTCLite.so b/firmware/echoear-platform-agent/components/volc_conv_ai/src/transports/high_quality/third_party/volc_rtc_engine_lite/libs/linux_x64/libVolcEngineRTCLite.so new file mode 100644 index 0000000..1d9fe6c Binary files /dev/null and b/firmware/echoear-platform-agent/components/volc_conv_ai/src/transports/high_quality/third_party/volc_rtc_engine_lite/libs/linux_x64/libVolcEngineRTCLite.so differ diff --git a/firmware/echoear-platform-agent/components/volc_conv_ai/src/transports/high_quality/third_party/volc_rtc_engine_lite/libs/macos/libVolcEngineRTCHal.a b/firmware/echoear-platform-agent/components/volc_conv_ai/src/transports/high_quality/third_party/volc_rtc_engine_lite/libs/macos/libVolcEngineRTCHal.a new file mode 100644 index 0000000..bf5e049 Binary files /dev/null and b/firmware/echoear-platform-agent/components/volc_conv_ai/src/transports/high_quality/third_party/volc_rtc_engine_lite/libs/macos/libVolcEngineRTCHal.a differ diff --git a/firmware/echoear-platform-agent/components/volc_conv_ai/src/transports/high_quality/third_party/volc_rtc_engine_lite/libs/macos/libVolcEngineRTCLite.dylib b/firmware/echoear-platform-agent/components/volc_conv_ai/src/transports/high_quality/third_party/volc_rtc_engine_lite/libs/macos/libVolcEngineRTCLite.dylib new file mode 100644 index 0000000..9da1943 Binary files /dev/null and b/firmware/echoear-platform-agent/components/volc_conv_ai/src/transports/high_quality/third_party/volc_rtc_engine_lite/libs/macos/libVolcEngineRTCLite.dylib differ diff --git a/firmware/echoear-platform-agent/components/volc_conv_ai/src/transports/high_quality/third_party/volc_rtc_engine_lite/libs/macos/libVolcEngineRTCLite_a.a b/firmware/echoear-platform-agent/components/volc_conv_ai/src/transports/high_quality/third_party/volc_rtc_engine_lite/libs/macos/libVolcEngineRTCLite_a.a new file mode 100644 index 0000000..de07afb Binary files /dev/null and b/firmware/echoear-platform-agent/components/volc_conv_ai/src/transports/high_quality/third_party/volc_rtc_engine_lite/libs/macos/libVolcEngineRTCLite_a.a differ diff --git a/firmware/echoear-platform-agent/components/volc_conv_ai/src/transports/low_load/inc/volc_ws.h b/firmware/echoear-platform-agent/components/volc_conv_ai/src/transports/low_load/inc/volc_ws.h new file mode 100644 index 0000000..c93cefd --- /dev/null +++ b/firmware/echoear-platform-agent/components/volc_conv_ai/src/transports/low_load/inc/volc_ws.h @@ -0,0 +1,35 @@ +// Copyright (2025) Beijing Volcano Engine Technology Ltd. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef __CONV_AI_NODE_VOLC_WS_H__ +#define __CONV_AI_NODE_VOLC_WS_H__ + +#include + +#include "cJSON.h" + +#include "base/volc_base.h" +#include "base/volc_device_manager.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void* volc_ws_t; + +volc_ws_t volc_ws_create(void* context, cJSON* p_config, volc_msg_cb message_callback, volc_data_cb data_callback); + +void volc_ws_destroy(volc_ws_t ws); + +int volc_ws_start(volc_ws_t ws, const char* bot_id, volc_iot_info_t* iot_info, const char* params); + +int volc_ws_send(volc_ws_t ws, const void* data, int size, volc_data_info_t* data_info); + +int volc_ws_stop(volc_ws_t ws); + +int volc_ws_interrupt(volc_ws_t ws); + +#ifdef __cplusplus +} +#endif +#endif /* __CONV_AI_NODE_VOLC_WS_H__ */ diff --git a/firmware/echoear-platform-agent/components/volc_conv_ai/src/transports/low_load/src/volc_ws.c b/firmware/echoear-platform-agent/components/volc_conv_ai/src/transports/low_load/src/volc_ws.c new file mode 100644 index 0000000..a9a5aad --- /dev/null +++ b/firmware/echoear-platform-agent/components/volc_conv_ai/src/transports/low_load/src/volc_ws.c @@ -0,0 +1,688 @@ +// Copyright (2025) Beijing Volcano Engine Technology Ltd. +// SPDX-License-Identifier: Apache-2.0 + +#include "volc_ws.h" + +#include "volc_osal.h" +#include "base/volc_base.h" +#include "util/volc_list.h" +#include "util/volc_log.h" +#include "util/volc_json.h" +#include "util/volc_base64.h" +#include "websocket.h" + +#define WS_AIGC_URI "wss://ai-gateway.vei.volces.com" +#define WS_AIGC_PATH "/v1/realtime" + +const char* ws_interrupt_str = "{\"type\": \"response.cancel\"}"; + +typedef struct { + uint8_t opcode; + uint8_t* buffer; + int size; + int capacity; + int in_progress; +} ws_assembler_t; + +typedef struct ws_params { + volc_audio_codec_type_e audio_codec_type; + char* session_update_json; +} ws_params_t; + +typedef struct { + bool b_pipeline_started; + bool b_connected; + bool b_interrupted; + char* p_data_buf; + char* p_bot_id; + char* p_last_response_id; + char headers[1024]; + char uri[256]; + char event_id[64]; + int event_id_index; + size_t data_buf_size; + void* context; + volatile volc_conv_status_e conv_status; + volc_msg_cb message_callback; + volc_data_cb data_callback; + char hardware_id[32]; + ws_params_t params; + ws_assembler_t assembler; + volc_ws_client_t* client; +} ws_impl_t; + +static int __ws_init(ws_impl_t* ws, cJSON* p_config) +{ + int ret = volc_json_read_int(p_config, "audio.codec", (int*)&ws->params.audio_codec_type); + if (ret != 0) { + ws->params.audio_codec_type = VOLC_AUDIO_CODEC_TYPE_PCM; + } + return 0; +} + +static bool __ws_wait_for_session_update(ws_impl_t* ws) { + (void)ws; + return true; +} + +static int __build_ws_message(const char* msg, uint8_t** out_buf, size_t* out_len) { + size_t msg_len = strlen(msg) + 1; + *out_len = msg_len - 1; + *out_buf = (uint8_t*)volc_osal_malloc(msg_len); + if (!*out_buf) { + LOGE("volc_osal_malloc failed"); + return -1; + } + memcpy(*out_buf, msg, msg_len); + (*out_buf)[msg_len - 1] = 0; + return 0; +} + +static void __send_message_2_user(ws_impl_t* ws, volc_msg_t* msg) +{ + if (ws->message_callback) { + ws->message_callback(ws->context, msg); + } +} + +static void __send_data_2_user(ws_impl_t* ws, const char* data, int data_len, volc_data_info_t* info) { + if (ws->data_callback) { + ws->data_callback(ws->context, (const void*)data, data_len, info); + } +} + +static bool __ws_drop_for_interrupted(ws_impl_t* ws, const char* p_response_id) { + if (ws->b_interrupted && p_response_id) { + ws->b_interrupted = false; + if (ws->p_last_response_id) { + volc_osal_free(ws->p_last_response_id); + ws->p_last_response_id = NULL; + } + ws->p_last_response_id = strdup(p_response_id); + LOGI("drop for interrupted, response_id: %s", p_response_id); + } + if (ws->p_last_response_id && p_response_id && strcmp(ws->p_last_response_id, p_response_id) == 0) { + return true; + } + return false; +} + +static void __ws_recv_data(ws_impl_t* ws, const char* data, int data_len) +{ + cJSON* p_json = NULL; + char* p_type = NULL; + char* p_delta = NULL; + char* p_status = NULL; + void* p_data = NULL; + char* p_response_id = NULL; + size_t len = 0; + volc_data_info_t info = { 0 }; + volc_msg_t msg = { 0 }; + + if (NULL == data || 0 == data_len) { + return; + } + + p_json = cJSON_ParseWithLength(data, data_len); + if (!p_json) { + LOGE("Failed to parse json, data_len: %d, data: %s", data_len, data); + return; + } + // LOGI("json: %s", data); + volc_json_read_string(p_json, "type", &p_type); + volc_json_read_string(p_json, "delta", &p_delta); + volc_json_read_string(p_json, "response.status", &p_status); + if (strcmp(p_type, "response.audio.delta") == 0 && p_delta) { + if (strlen(p_delta) == 0) { + LOGE("delta is empty, data: %s", data); + goto err_out_label; + } + if (NULL == ws || !ws->b_pipeline_started) { + LOGD("pipeline not started"); + goto err_out_label; + } + volc_json_read_string(p_json, "response_id", &p_response_id); + if (__ws_drop_for_interrupted(ws, p_response_id)) { + goto err_out_label; + } + len = volc_base64_decoded_length((const uint8_t*)p_delta, strlen(p_delta)); + p_data = volc_osal_malloc(len + 1); + if (NULL == p_data) { + LOGE("Failed to alloc memory"); + goto err_out_label; + } + if (ws->conv_status == VOLC_CONV_STATUS_THINKING) { + ws->conv_status = VOLC_CONV_STATUS_ANSWERING; + msg.code = VOLC_MSG_CONV_STATUS; + msg.data.conv_status = ws->conv_status; + __send_message_2_user(ws, &msg); + } + volc_base64_decode((unsigned char *)p_data, len, &len, (const unsigned char *)p_delta, strlen(p_delta)); + info.type = VOLC_DATA_TYPE_AUDIO; + info.info.audio.data_type = VOLC_AUDIO_DATA_TYPE_PCM; + // info.info.audio.sent_ts = volc_get_time(); // TODO + __send_data_2_user(ws, p_data, len, &info); + HAL_SAFE_FREE(p_data); + } else if (strcmp(p_type, "input_audio_buffer.speech_started") == 0) { + ws->conv_status = VOLC_CONV_STATUS_LISTENING; + msg.code = VOLC_MSG_CONV_STATUS; + msg.data.conv_status = VOLC_CONV_STATUS_LISTENING; + __send_message_2_user(ws, &msg); + } else if (strcmp(p_type, "input_audio_buffer.speech_stopped") == 0) { + ws->conv_status = VOLC_CONV_STATUS_THINKING; + msg.code = VOLC_MSG_CONV_STATUS; + msg.data.conv_status = VOLC_CONV_STATUS_THINKING; + __send_message_2_user(ws, &msg); + } else if (strcmp(p_type, "response.done") == 0 && p_status) { + msg.code = VOLC_MSG_CONV_STATUS; + if (strcmp(p_status, "completed") == 0) { + ws->conv_status = VOLC_CONV_STATUS_ANSWER_FINISH; + msg.data.conv_status = VOLC_CONV_STATUS_ANSWER_FINISH; + } else if (strcmp(p_status, "cancelled") == 0) { + ws->conv_status = VOLC_CONV_STATUS_INTERRUPTED; + msg.data.conv_status = VOLC_CONV_STATUS_INTERRUPTED; + } + LOGI("data: %s", data); + __send_message_2_user(ws, &msg); + } else { + if (strcmp(p_type, "session.created") == 0) { + LOGI("%s", data); + } else if (strcmp(p_type, "response.audio_transcript.delta") == 0 || strcmp(p_type, "response.audio_transcript.done") == 0 || strcmp(p_type, "response.audio.done") == 0) { + volc_json_read_string(p_json, "response_id", &p_response_id); + if (__ws_drop_for_interrupted(ws, p_response_id)) { + goto err_out_label; + } + } + info.type = VOLC_DATA_TYPE_MESSAGE; + info.info.message.is_binary = false; + __send_data_2_user(ws, data, data_len, &info); + } +err_out_label: + HAL_SAFE_FREE(p_type); + HAL_SAFE_FREE(p_delta); + HAL_SAFE_FREE(p_status); + HAL_SAFE_FREE(p_response_id); + cJSON_Delete(p_json); +} + +static void __ws_assembler_free(ws_assembler_t* a) { + HAL_SAFE_FREE(a->buffer); + a->size = 0; + a->capacity = 0; + a->in_progress = 0; +} + +static int __ws_generate_event_id(ws_impl_t* ws) { + if (!ws) { + LOGE("ws instance is NULL"); + return -1; + } + memset(ws->event_id, 0, sizeof(ws->event_id)); + snprintf(ws->event_id, sizeof(ws->event_id), "event_id_%llu", ws->event_id_index++); + return 0; +} + +static char* __ws_audio_codec_to_string(ws_impl_t* ws) { + switch (ws->params.audio_codec_type) { + case VOLC_AUDIO_CODEC_TYPE_OPUS: + return "opus16"; + case VOLC_AUDIO_CODEC_TYPE_G722: + return "g722"; + case VOLC_AUDIO_CODEC_TYPE_G711A: + return "g711_alaw"; + case VOLC_AUDIO_CODEC_TYPE_G711U: + return "g711u_ulaw"; + default: + return "pcm16"; + } +} + +static char* __ws_generate_session_update(ws_impl_t* ws) { + char* json = NULL; + cJSON* root = NULL; + cJSON* session = NULL; + if (!ws) { + LOGE("ws instance is NULL"); + return NULL; + } + if (ws->params.session_update_json != NULL) { + return strdup(ws->params.session_update_json); + } + __ws_generate_event_id(ws); + root = cJSON_CreateObject(); + cJSON_AddStringToObject(root, "event_id", ws->event_id); + cJSON_AddStringToObject(root, "type", "session.update"); + session = cJSON_CreateObject(); + cJSON_AddItemToObject(root, "session", session); + cJSON_AddStringToObject(session, "object", "realtime.session"); + cJSON_AddStringToObject(session, "model", ""); + cJSON_AddStringToObject(session, "input_audio_format", __ws_audio_codec_to_string(ws)); + json = cJSON_PrintUnformatted(root); + cJSON_Delete(root); + return json; +} + +static void __ws_append_data(ws_impl_t* ws, volc_ws_event_data_t* data) { + int new_capacity = 0; + uint8_t* new_buffer = NULL; + if (data->op_code == VOLC_WS_OPCODES_TEXT || data->op_code == VOLC_WS_OPCODES_BINARY) { + ws->assembler.in_progress = 1; + ws->assembler.opcode = data->op_code; + } else if (data->op_code == VOLC_WS_OPCODES_CONT) { + if (!ws->assembler.in_progress) { + LOGW("append data, but assembler not in progress"); + return; + } + } else { + LOGD("append data, skip opcode: %d", data->op_code); + return; + } + + if (ws->assembler.in_progress) { + new_capacity = ws->assembler.size + data->data_len + 1; + if (new_capacity > ws->assembler.capacity) { + LOGI("append data, new_capacity: %d", new_capacity); + new_capacity = new_capacity < 1024 ? 1024 : new_capacity * 2; + new_buffer = volc_osal_realloc(ws->assembler.buffer, new_capacity); + if (new_buffer == NULL) { + LOGE("Failed to alloc memory"); + __ws_assembler_free(&ws->assembler); + return; + } + ws->assembler.buffer = new_buffer; + ws->assembler.capacity = new_capacity; + } + memcpy(ws->assembler.buffer + ws->assembler.size, data->data_ptr, data->data_len); + ws->assembler.size += data->data_len; + + if (data->fin && (data->payload_len == data->payload_offset + data->data_len)) { + LOGD("append data, fin, len: %d", ws->assembler.size); + ws->assembler.buffer[ws->assembler.size] = 0; + __ws_recv_data(ws, (const char*)ws->assembler.buffer, ws->assembler.size); + memset(ws->assembler.buffer, 0, ws->assembler.capacity); + ws->assembler.size = 0; + ws->assembler.in_progress = 0; + } + } +} + +static void __ws_stop(ws_impl_t* ws); +static void __ws_event_handler(void* context, int32_t event_id, void* event_data) { + ws_impl_t* ws = (ws_impl_t*)context; + volc_msg_t msg = { 0 }; + volc_ws_event_data_t *data = (volc_ws_event_data_t *)event_data; + if (NULL == ws) { + LOGW("ws is NULL"); + return; + } + switch (event_id) { + case VOLC_WS_EVENT_CONNECTED: + ws->b_connected = true; + msg.code = VOLC_MSG_CONNECTED; + __send_message_2_user(ws, &msg); + break; + case VOLC_WS_EVENT_DISCONNECTED: + ws->b_connected = false; + msg.code = VOLC_MSG_DISCONNECTED; + __send_message_2_user(ws, &msg); + break; + case VOLC_WS_EVENT_DATA: + __ws_append_data(ws, data); + break; + case VOLC_WS_EVENT_CLOSED: + LOGW("receive close event"); + ws->b_connected = false; + msg.code = VOLC_MSG_DISCONNECTED; + __send_message_2_user(ws, &msg); + /* The owner joins this worker; never join from its own callback. */ + break; + default: + break; + } +} + +static int __ws_send_message(ws_impl_t* ws, const void* data_ptr, size_t data_len) { + if (!ws || !data_ptr || !data_len) { + LOGE("ws or data or info is NULL"); + return -1; + } + return volc_ws_client_send_text(ws->client, (const char*)data_ptr, data_len, 1000); +} + +static int __ws_start(ws_impl_t* ws, volc_iot_info_t* iot_info) +{ + /* The standard-protocol signature requires Unix seconds, not milliseconds. */ + uint64_t current_time = volc_osal_get_time_ms() / 1000; + bool b_wait_for_session_update = __ws_wait_for_session_update(ws); + int random_num = (int)current_time;//volc_get_random_num(); + char time_str[32] = { 0 }; + char num_str[32] = { 0 }; + char platform_info[16] = { 0 }; + char user_agent[64] = { 0 }; + if (!ws || !iot_info) { + LOGE("ws or option is NULL"); + return -1; + } + + /* This instance is reused across conversations; readiness is per socket. */ + __ws_stop(ws); + ws->b_connected = false; + ws->b_interrupted = false; + ws->conv_status = VOLC_CONV_STATUS_ANSWER_FINISH; + HAL_SAFE_FREE(ws->p_last_response_id); + __ws_assembler_free(&ws->assembler); + volc_osal_get_platform_info(platform_info, sizeof(platform_info)); + snprintf(user_agent, sizeof(user_agent), "%s(%s)", volc_get_version(), platform_info); + snprintf(time_str, sizeof(time_str), "%llu", current_time); + snprintf(num_str, sizeof(num_str), "%d", random_num); + char* signature = volc_generate_signature_ws(iot_info->device_secret, iot_info->product_key, iot_info->device_name, iot_info->instance_id, random_num, current_time, 1); + volc_ws_config_t ws_cfg = { 0 }; + snprintf(ws->uri, sizeof(ws->uri), "%s%s?bot=%s&wait_for_session_update=%s", WS_AIGC_URI, WS_AIGC_PATH, ws->p_bot_id, b_wait_for_session_update ? "true" : "false"); + ws_cfg.uri = ws->uri; + ws_cfg.user_agent = user_agent; + snprintf(ws->headers, sizeof(ws->headers), + "X-Signature: %s\r\n" + "X-Auth-Type: 1\r\n" + "X-Device-Name: %s\r\n" + "X-Product-Key: %s\r\n" + "X-Random-Num: %s\r\n" + "X-Timestamp: %s\r\n" + "X-Hardware-Id: %s\r\n" + "X-Instance-Id: %s\r\n", signature, iot_info->device_name, iot_info->product_key, num_str, time_str, ws->hardware_id, iot_info->instance_id); + ws_cfg.headers = ws->headers; + LOGI("WS URL: %s", ws_cfg.uri); + ws_cfg.user_context = ws; + ws_cfg.buffer_size = 1024 * 5; + ws_cfg.ws_event_handler = __ws_event_handler; + ws->client = volc_ws_client_init(&ws_cfg); + if(volc_ws_client_start(ws->client)) { + LOGE("Failed to start websocket client"); + __ws_stop(ws); + return -1; + } + if (b_wait_for_session_update) { + uint64_t connect_deadline = volc_osal_get_time_ms() + 15000; + while (!ws->b_connected) { + if (ws->client->exit || volc_osal_get_time_ms() >= connect_deadline) { + LOGE("WebSocket connection did not become ready"); + __ws_stop(ws); + return -1; + } + volc_osal_thread_sleep(10); + } + char* session_update = __ws_generate_session_update(ws); + int sent = session_update ? __ws_send_message(ws, session_update, strlen(session_update)) : -1; + bool sent_complete = session_update && sent == (int)strlen(session_update); + HAL_SAFE_FREE(session_update); + if (!sent_complete) { + LOGE("Initial session.update failed; audio remains disabled"); + __ws_stop(ws); + return -1; + } + LOGI("Initial session.update sent after WebSocket connected"); + } + ws->b_pipeline_started = true; + return 0; +} + +static void __ws_stop(ws_impl_t* ws) +{ + if (!ws) { + LOGE("ws instance is NULL"); + return; + } + ws->b_connected = false; + ws->b_pipeline_started = false; + if (ws->client) volc_ws_client_destroy(ws->client); + ws->client = NULL; + ws->b_pipeline_started = false; +} + +static int __ws_input_audio_buffer_append(ws_impl_t* ws, const char* data_ptr, size_t data_len) { + int ret = 0; + cJSON* root = NULL; + if (!ws || !data_ptr || data_len == 0) { + LOGE("ws or data or data_len is NULL"); + return -1; + } + + root = cJSON_CreateObject(); + cJSON_AddStringToObject(root, "type", "input_audio_buffer.append"); + cJSON_AddStringToObject(root, "audio", data_ptr); + char* json = cJSON_PrintUnformatted(root); + cJSON_Delete(root); + ret = volc_ws_client_send_text(ws->client, json, strlen(json), 1000); + if (ret >= 0) { + ret = 0; + } else { + LOGW("failed to send audio buffer"); + } + HAL_SAFE_FREE(json); + return ret; +} + +static int __ws_input_audio_buffer_commit(ws_impl_t* ws) { + int ret = 0; + cJSON* root = NULL; + if (!ws) { + LOGE("ws instance is NULL"); + return -1; + } + root = cJSON_CreateObject(); + cJSON_AddStringToObject(root, "type", "input_audio_buffer.commit"); + char* json = cJSON_PrintUnformatted(root); + LOGD("json: %s", json); + cJSON_Delete(root); + ret = volc_ws_client_send_text(ws->client, json, strlen(json), 1000); + if (ret >= 0) { + ret = 0; + } + HAL_SAFE_FREE(json); + return ret; +} + +static int __ws_response_create(ws_impl_t* ws) { + int ret = 0; + cJSON* root = NULL; + if (!ws) { + LOGE("ws instance is NULL"); + return -1; + } + root = cJSON_CreateObject(); + cJSON_AddStringToObject(root, "type", "response.create"); + // 创建 "response" 对象 + cJSON* response_obj = cJSON_CreateObject(); + cJSON_AddItemToObject(root, "response", response_obj); + // 添加 “modalities” 字段 + cJSON* modalities_array = cJSON_CreateArray(); + cJSON_AddItemToArray(modalities_array, cJSON_CreateString("text")); + cJSON_AddItemToArray(modalities_array, cJSON_CreateString("audio")); + cJSON_AddItemToObject(response_obj, "modalities", modalities_array); + // 生成 JSON 字符串 + const char* json = cJSON_PrintUnformatted(root); + LOGD("json: %s", json); + // 释放内存 + cJSON_Delete(root); + ret = volc_ws_client_send_text(ws->client, json, strlen(json), 1000); + HAL_SAFE_FREE(json); + return ret; +} + +static int __ws_send_audio(ws_impl_t* ws, const void* data_ptr, size_t data_len, bool commit) { + int ret = 0; + size_t len = 0; + if (!ws || !data_ptr || !data_len) { + LOGE("ws or data or info is NULL"); + return -1; + } + len = volc_base64_encoded_length(data_len); + if (ws->data_buf_size < len) { + HAL_SAFE_FREE(ws->p_data_buf); + ws->p_data_buf = (char*)volc_osal_malloc(len); + if (!ws->p_data_buf) { + LOGE("failed to malloc data buf"); + ws->data_buf_size = 0; + return -1; + } + ws->data_buf_size = len; + } + volc_base64_encode((unsigned char*) ws->p_data_buf, len, &len, (const unsigned char*) data_ptr, data_len); + if (ret != 0) { + LOGE("failed to encode audio"); + return -1; + } + ret = __ws_input_audio_buffer_append(ws, ws->p_data_buf, len); + if (ret != 0) { + LOGE("failed to append audio buffer"); + return -1; + } + if (commit) { + ret = __ws_input_audio_buffer_commit(ws); + if (ret != 0) { + LOGE("failed to commit audio buffer"); + return -1; + } + ret = __ws_response_create(ws); + } + return ret; +} + +static void __ws_parse_params(const char* params, ws_params_t* out_params) { + cJSON* p_json = NULL; + if (!params || !out_params) { + LOGW("params or out_params is NULL"); + return; + } + p_json = cJSON_Parse(params); + if (!p_json) { + LOGW("Failed to parse json params: %s", params); + return; + } + volc_json_read_int(p_json, "audio.codec", (int*)&out_params->audio_codec_type); + cJSON* session_update = cJSON_GetObjectItem(p_json, "session_update"); + if (cJSON_IsObject(session_update)) { + HAL_SAFE_FREE(out_params->session_update_json); + out_params->session_update_json = cJSON_PrintUnformatted(session_update); + } + cJSON_Delete(p_json); +} + +volc_ws_t volc_ws_create(void* context, cJSON* p_config, volc_msg_cb message_callback, volc_data_cb data_callback) { + ws_impl_t* ws = (ws_impl_t*)volc_osal_calloc(1, sizeof(ws_impl_t)); + if (!ws) { + LOGE("volc_ws_create: malloc ws failed"); + return NULL; + } + ws->message_callback = message_callback; + ws->data_callback = data_callback; + ws->context = context; + ws->conv_status = VOLC_CONV_STATUS_ANSWER_FINISH; + ws->params.audio_codec_type = VOLC_AUDIO_CODEC_TYPE_PCM; + + volc_osal_get_uuid(ws->hardware_id, sizeof(ws->hardware_id)); + + if (__ws_init(ws, p_config) != 0) { + HAL_SAFE_FREE(ws); + LOGE("volc_ws_create: ws init failed"); + return NULL; + } + + LOGI("ws create success, hardware id: %s", ws->hardware_id); + return (volc_ws_t) ws; +} + +void volc_ws_destroy(volc_ws_t ws) { + ws_impl_t* ws_impl = (ws_impl_t*) ws; + if (!ws_impl) { + LOGE("ws instance is NULL"); + return; + } + + __ws_stop(ws_impl); + HAL_SAFE_FREE(ws_impl->p_data_buf); + HAL_SAFE_FREE(ws_impl->p_bot_id); + HAL_SAFE_FREE(ws_impl->p_last_response_id); + HAL_SAFE_FREE(ws_impl->params.session_update_json); + HAL_SAFE_FREE(ws_impl); +} + +int volc_ws_start(volc_ws_t ws, const char* bot_id, volc_iot_info_t* iot_info, const char* params) { + ws_impl_t* ws_impl = (ws_impl_t*) ws; + if (!ws_impl) { + LOGE("ws instance is NULL"); + return -1; + } + HAL_SAFE_FREE(ws_impl->p_bot_id); + ws_impl->p_bot_id = strdup(bot_id); + __ws_parse_params(params, &ws_impl->params); + return __ws_start(ws_impl, iot_info); +} + +int volc_ws_send(volc_ws_t ws, const void* data, int size, volc_data_info_t* data_info) { + ws_impl_t* ws_impl = (ws_impl_t*) ws; + if (!ws_impl) { + LOGE("ws instance is NULL"); + return -1; + } + + if (!ws_impl->b_pipeline_started || !ws_impl->b_connected) { + LOGD("pipeline started[%d], connected[%d], cannot process data", (int) ws_impl->b_pipeline_started, (int) ws_impl->b_connected); + return -1; + } + + switch (data_info->type) { + case VOLC_DATA_TYPE_AUDIO: { + return __ws_send_audio(ws_impl, data, size, data_info->info.audio.commit); + } + case VOLC_DATA_TYPE_VIDEO: { + LOGW("video data not supported"); + break; + } + case VOLC_DATA_TYPE_MESSAGE: { + return __ws_send_message(ws, data, size); + } + default: + LOGW("unsupported data type: %d", data_info->type); + return -1; + } + return 0; +} + +int volc_ws_stop(volc_ws_t ws) { + ws_impl_t* ws_impl = (ws_impl_t*) ws; + if (!ws_impl) { + LOGE("ws instance is NULL"); + return -1; + } + __ws_stop(ws_impl); + return 0; +} + +int volc_ws_interrupt(volc_ws_t ws) { + int ret = 0; + uint8_t* msg = NULL; + size_t msg_len = 0; + ws_impl_t* ws_impl = (ws_impl_t*) ws; + if (!ws_impl) { + LOGE("ws instance is NULL"); + return -1; + } + if (ws_impl->conv_status != VOLC_CONV_STATUS_LISTENING && ws_impl->conv_status != VOLC_CONV_STATUS_THINKING && + ws_impl->conv_status != VOLC_CONV_STATUS_ANSWERING) { + LOGW("interrupt failed, conv status is not listening or thinking"); + return -1; + } + if (__build_ws_message(ws_interrupt_str, &msg, &msg_len) != 0) { + LOGE("build control message failed"); + return -1; + } + if (ws_impl->conv_status == VOLC_CONV_STATUS_ANSWERING) { + ws_impl->b_interrupted = true; + } + if ((__ws_send_message(ws_impl, msg, msg_len)) <= 0) { + LOGE("send control message failed"); + ret = -1; + } + HAL_SAFE_FREE(msg); + return ret; +} diff --git a/firmware/echoear-platform-agent/components/volc_conv_ai/src/transports/low_load/third_party/websocket/LICENSE b/firmware/echoear-platform-agent/components/volc_conv_ai/src/transports/low_load/third_party/websocket/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/firmware/echoear-platform-agent/components/volc_conv_ai/src/transports/low_load/third_party/websocket/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/firmware/echoear-platform-agent/components/volc_conv_ai/src/transports/low_load/third_party/websocket/websocket.c b/firmware/echoear-platform-agent/components/volc_conv_ai/src/transports/low_load/third_party/websocket/websocket.c new file mode 100644 index 0000000..d9ce09f --- /dev/null +++ b/firmware/echoear-platform-agent/components/volc_conv_ai/src/transports/low_load/third_party/websocket/websocket.c @@ -0,0 +1,1562 @@ +// Copyright 2021-2022 Beken +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This file may have been modified by Volcano and/or its affiliates. + +#include "websocket.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "volc_osal.h" +#include "util/volc_list.h" +#include "util/volc_log.h" +#include "util/volc_base64.h" +#include "mbedtls/sha1.h" +#ifdef ESP_PLATFORM +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_heap_caps.h" +#endif + +#define WEBSOCKET_SSL_DEFAULT_PORT 443 +#define WEBSOCKET_TCP_DEFAULT_PORT 80 +#define WEBSOCKET_BUFFER_SIZE_BYTE (4 * 1024) +#define WS_BUFFER_SIZE (1 * 1600) +#define MAX_WEBSOCKET_HEADER_SIZE 16 + +#define WS_SIZE64 127 +#define WS_MASK 0x80 +#define WS_SIZE16 126 + +#define WEBSOCKET_TASK_PRIORITY (4) +#define WEBSOCKET_TASK_STACK (12 * 1024) +#define WEBSOCKET_NETWORK_TIMEOUT_MS (10 * 1000) +#define WEBSOCKET_PINGPONG_TIMEOUT_SEC (540) +#define WEBSOCKET_PING_INTERVAL_SEC (3) +#define WEBSOCKET_RECONNECT_TIMEOUT_MS (5 * 1000) +#define WEBSOCKET_RX_RETRY_COUNT (10) + +#ifndef MIN +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#endif + +static void volc_ws_client_dispatch_event(volc_ws_client_t* client, int32_t event, char* data, int data_len, int opcode) +{ + volc_ws_event_data_t event_data; + event_data.data_ptr = data; + event_data.data_len = data_len; + event_data.fin = client->last_fin; + event_data.op_code = opcode; + event_data.payload_len = client->payload_len; + event_data.payload_offset = client->payload_offset; + LOGD("volc_ws_client_dispatch_event, fin: %d, opcode: %d, payload_len: %d, payload_offset: %d, data_len: %d", event_data.fin, event_data.op_code, + event_data.payload_len, event_data.payload_offset, event_data.data_len); + + if (client->ws_event_handler) + (client->ws_event_handler)(client->user_context, event, (void*) &event_data); +} + +struct timeval* utils_ms_to_timeval(int timeout_ms, struct timeval* tv); + +#define CO_BIT(pos) (1U << (pos)) +const static int PING_SENT_BIT = CO_BIT(1); +// const static int TEXT_SENT_BIT = CO_BIT(2); +const static int CLOSE_SENT_BIT = CO_BIT(3); +int status_bits = PING_SENT_BIT; + +static char* trimwhitespace(const char* str); +static char* get_http_header(const char* buffer, const char* key); +static int ws_tcp_close(volc_ws_client_t* client); +static int ws_tcp_poll_read(volc_ws_client_t* client, int timeout_ms); +static int ws_tcp_poll_write(volc_ws_client_t* client, int timeout_ms); +static int ws_tcp_read(volc_ws_client_t* client, char* buffer, int len, int timeout_ms); +static int ws_tcp_write(volc_ws_client_t* client, const char* buffer, int len, int timeout_ms); +static int ws_read_payload(volc_ws_client_t* client, char* buffer, int len, int timeout_ms); +static int ws_read_header(volc_ws_client_t* client, char* buffer, int len, int timeout_ms); +static int ws_write(volc_ws_client_t* client, int opcode, int mask_flag, const char* b, int len, int timeout_ms); +static int ws_read(volc_ws_client_t* client, char* buffer, int len, int timeout_ms); +static int ws_poll_connection_closed(int* sockfd, int timeout_ms); +static int ws_client_recv(volc_ws_client_t* client); +static int set_socket_non_blocking(int fd, bool non_blocking); +static int hostname_to_fd(const char* host, size_t hostlen, int port, struct sockaddr_storage* address, int* fd); +static int _tcp_connect(int* sockfd, const char* host, int hostlen, int port, int timeout_ms); +static int ws_tcp_connect(volc_ws_client_t* client, const char* host, int port, int timeout_ms); +static int ws_disconnect(volc_ws_client_t* client); +static int ws_connect(volc_ws_client_t* client, const char* host, int port, int timeout_ms); +static int volc_ws_client_destory_config(volc_ws_client_t* client); + +static char* trimwhitespace(const char* str) +{ + char* end; + + // Trim leading space + while (isspace((unsigned char) *str)) + str++; + + if (*str == 0) { + return (char*) str; + } + + // Trim trailing space + end = (char*) (str + strlen(str) - 1); + while (end > str && isspace((unsigned char) *end)) + end--; + + // Write new null terminator + *(end + 1) = 0; + + return (char*) str; +} + +static char* get_http_header(const char* buffer, const char* key) +{ + char* found = strcasestr(buffer, key); + if (found) { + found += strlen(key); + char* found_end = strstr(found, "\r\n"); + if (found_end) { + found_end[0] = 0; + + return trimwhitespace(found); + } + } + return NULL; +} + +static int _tcp_close(volc_ws_client_t* client) +{ + int ret = -1; + if (client->sockfd >= 0) { + LOGE("%s, sockfd used, closing", __func__); + ret = close(client->sockfd); + client->sockfd = -1; + } + return ret; +} + +static int _tcp_poll_read(int* sockfd, int timeout_ms) +{ + int ret = -1; + struct timeval timeout; + fd_set readset; + fd_set errset; + FD_ZERO(&readset); + FD_ZERO(&errset); + FD_SET(*sockfd, &readset); + FD_SET(*sockfd, &errset); + + ret = select((*sockfd) + 1, &readset, NULL, &errset, utils_ms_to_timeval(timeout_ms, &timeout)); + if (ret > 0 && FD_ISSET(*sockfd, &errset)) { + int sock_errno = 0; + uint32_t optlen = sizeof(sock_errno); + getsockopt(*sockfd, SOL_SOCKET, SO_ERROR, &sock_errno, &optlen); + LOGE("poll_read select error %d, errno = %s, fd = %d", sock_errno, strerror(sock_errno), *sockfd); + ret = -1; + } + return ret; +} + +static int _tcp_poll_write(int* sockfd, int timeout_ms) +{ + int ret = -1; + struct timeval timeout; + fd_set writeset; + fd_set errset; + FD_ZERO(&writeset); + FD_ZERO(&errset); + FD_SET(*sockfd, &writeset); + FD_SET(*sockfd, &errset); + + ret = select((*sockfd) + 1, NULL, &writeset, &errset, utils_ms_to_timeval(timeout_ms, &timeout)); + if (ret > 0 && FD_ISSET(*sockfd, &errset)) { + int sock_errno = 0; + uint32_t optlen = sizeof(sock_errno); + getsockopt(*sockfd, SOL_SOCKET, SO_ERROR, &sock_errno, &optlen); + LOGE("poll_write select error %d, errno = %s, fd = %d\r\n", sock_errno, strerror(sock_errno), *sockfd); + ret = -1; + } + return ret; +} + +static int _tcp_read(int* sockfd, char* buffer, int len, int timeout_ms) +{ + int poll; + + if ((poll = _tcp_poll_read(sockfd, timeout_ms)) <= 0) { + return poll; + } + + int ret = recv(*sockfd, (unsigned char*) buffer, len, 0); + if (ret < 0) { + LOGE("tcp_read error, errno=%s", strerror(errno)); + } + if (ret == 0) { + if (poll > 0) { + // no error + LOGE("%s select > 0, ret = 0 \r\n", __func__); + } + ret = -1; + } + return ret; +} + +static int _tcp_write(int* sockfd, const char* buffer, int len, int timeout_ms) +{ + int poll; + if ((poll = _tcp_poll_write(sockfd, timeout_ms)) <= 0) { + LOGE("Poll timeout or error, errno=%s, fd=%d, timeout_ms=%d\r\n", strerror(errno), *sockfd, timeout_ms); + return poll; + } + + int ret = send((*sockfd), (const unsigned char*) buffer, len, 0); + if (ret < 0) { + LOGE("tcp_write error, errno=%s", strerror(errno)); + } + return ret; +} + +static int ws_read_payload(volc_ws_client_t* client, char* buffer, int len, int timeout_ms) +{ + int bytes_to_read; + int rlen = 0; + transport_ws_t* ws = client->ws_transport; + if (ws->frame_state.bytes_remaining > len) { + LOGE("Actual data to receive (%d) are longer than ws buffer (%d)\r\n", ws->frame_state.bytes_remaining, len); + bytes_to_read = len; + + } else { + bytes_to_read = ws->frame_state.bytes_remaining; + } + + if (bytes_to_read != 0 && (rlen = ws_tcp_read(client, buffer, bytes_to_read, timeout_ms)) <= 0) { + LOGE("Error read payload data\r\n"); + return rlen; + } + ws->frame_state.bytes_remaining -= rlen; + + // if (ws->frame_state.mask_key) { + for (int i = 0; i < bytes_to_read; i++) { + buffer[i] = (buffer[i] ^ ws->frame_state.mask_key[i % 4]); + } + // } + return rlen; +} + +static int _tcp_read_completely(volc_ws_client_t* client, char* buffer, int len, int timeout_ms) { + int rlen = 0; + while (rlen < len) { + int ret = ws_tcp_read(client, buffer + rlen, len - rlen, timeout_ms); + if (ret < 0) { + return ret; + } + rlen += ret; + } + return rlen; +} + +/* Read and parse the WS header, determine length of payload */ +static int ws_read_header(volc_ws_client_t* client, char* buffer, int len, int timeout_ms) +{ + int payload_len; + transport_ws_t* ws = client->ws_transport; + char ws_header[MAX_WEBSOCKET_HEADER_SIZE]; + char *data_ptr = ws_header, mask; + int rlen; + int poll_read; + ws->frame_state.header_received = false; + /* Polling is waiting for the network, not accessing TLS state. */ + bool connected = client->state == VOLC_WS_STATE_CONNECTED; + if (connected) volc_osal_mutex_unlock(client->mutex); + poll_read = ws_tcp_poll_read(client, timeout_ms); + if (connected) volc_osal_mutex_lock(client->mutex); + if (poll_read <= 0) { + LOGE("error poll read data\r\n"); + return poll_read; + } + + int header = 2; + int mask_len = 4; + if ((rlen = _tcp_read_completely(client, data_ptr, header, timeout_ms)) <= 0) { + LOGE("first header, Error read data\r\n"); + return rlen; + } + ws->frame_state.header_received = true; + ws->frame_state.fin = (*data_ptr & 0x80) != 0; + ws->frame_state.opcode = (*data_ptr & 0x0F); + data_ptr++; + mask = ((*data_ptr >> 7) & 0x01); + payload_len = (*data_ptr & 0x7F); + data_ptr++; + LOGD("%s, Opcode: %d, mask: %d, fin: %d, payload len: %d", __func__, ws->frame_state.opcode, mask, (int) ws->frame_state.fin, payload_len); + if (payload_len == WS_SIZE16) { + if ((rlen = _tcp_read_completely(client, data_ptr, header, timeout_ms)) <= 0) { + LOGE("126 read: Error read data\r\n"); + return rlen; + } + payload_len = (uint8_t) data_ptr[0] << 8 | (uint8_t) data_ptr[1]; + } else if (payload_len == WS_SIZE64) { + header = 8; + if ((rlen = _tcp_read_completely(client, data_ptr, header, timeout_ms)) <= 0) { + LOGE("127 read: Error read data\r\n"); + return rlen; + } + + if (data_ptr[0] != 0 || data_ptr[1] != 0 || data_ptr[2] != 0 || data_ptr[3] != 0) { + // really too big! + payload_len = 0xFFFFFFFF; + LOGW("127, payload_len:%d", payload_len); + } else { + payload_len = (uint8_t) data_ptr[4] << 24 | (uint8_t) data_ptr[5] << 16 | (uint8_t) data_ptr[6] << 8 | (uint8_t) data_ptr[7]; + LOGW("127, payload_len:%d, data_ptr[4]:%d, data_ptr[5]:%d, data_ptr[6]:%d, data_ptr[7]:%d", payload_len, data_ptr[4], data_ptr[5], + data_ptr[6], data_ptr[7]); + } + } + LOGD("ws_read_header, payload_len:%d", payload_len); + if (mask) { + LOGD("mask: %d, payload_len: %d", mask, payload_len); + // Read and store mask + if (payload_len != 0 && (rlen = _tcp_read_completely(client, buffer, mask_len, timeout_ms)) <= 0) { + LOGE("mask error read data\r\n"); + return rlen; + } + memcpy(ws->frame_state.mask_key, buffer, mask_len); + } else { + memset(ws->frame_state.mask_key, 0, mask_len); + } + + ws->frame_state.payload_len = payload_len; + ws->frame_state.bytes_remaining = payload_len; + + return payload_len; +} + +static int ws_write(volc_ws_client_t* client, int opcode, int mask_flag, const char* b, int len, int timeout_ms) +{ + char ws_header[MAX_WEBSOCKET_HEADER_SIZE]; + char* mask; + int header_len = 0, i; + int poll_write; + if (len < 0 || len > client->buffer_size || (len && !b)) { + return -1; + } + + /* Connected writers wait before taking the TLS mutex. Control frames must + * not hold that mutex waiting for TCP space either. */ + if ((poll_write = ws_tcp_poll_write(client, client->state == VOLC_WS_STATE_CONNECTED ? 0 : timeout_ms)) <= 0) { + LOGE("Error ws_tcp_poll_write\r\n"); + return -1; + } + + ws_header[header_len++] = opcode; + if (len <= 125) { + ws_header[header_len++] = (uint8_t) (len | mask_flag); + } else if (len < 65536) { + ws_header[header_len++] = WS_SIZE16 | mask_flag; + ws_header[header_len++] = (uint8_t) (len >> 8); + ws_header[header_len++] = (uint8_t) (len & 0xFF); + } else { + ws_header[header_len++] = WS_SIZE64 | mask_flag; + /* Support maximum 4 bytes length */ + ws_header[header_len++] = 0; //(uint8_t)((len >> 56) & 0xFF); + ws_header[header_len++] = 0; //(uint8_t)((len >> 48) & 0xFF); + ws_header[header_len++] = 0; //(uint8_t)((len >> 40) & 0xFF); + ws_header[header_len++] = 0; //(uint8_t)((len >> 32) & 0xFF); + ws_header[header_len++] = (uint8_t) ((len >> 24) & 0xFF); + ws_header[header_len++] = (uint8_t) ((len >> 16) & 0xFF); + ws_header[header_len++] = (uint8_t) ((len >> 8) & 0xFF); + ws_header[header_len++] = (uint8_t) ((len >> 0) & 0xFF); + } + + if (mask_flag) { + mask = &ws_header[header_len]; + volc_osal_fill_random((uint8_t *)ws_header + header_len, 4); + header_len += 4; + + } + /* Submit a complete frame in one TLS record. Separate tiny header writes + * otherwise delay the real-time stream and consume TCP send queue slots. */ + if (len) { + memmove(client->tx_buffer + header_len, b, len); + } + memcpy(client->tx_buffer, ws_header, header_len); + if (mask_flag) { + mask = &ws_header[header_len - 4]; + for (i = 0; i < len; ++i) { + client->tx_buffer[header_len + i] ^= mask[i % 4]; + } + } + int ret = ws_tcp_write(client, client->tx_buffer, header_len + len, timeout_ms); + return ret == header_len + len ? len : -1; +} + +static int ws_read(volc_ws_client_t* client, char* buffer, int len, int timeout_ms) +{ + int rlen = 0; + transport_ws_t* ws = client->ws_transport; + if (ws->frame_state.bytes_remaining <= 0) { + if ((rlen = ws_read_header(client, buffer, len, timeout_ms)) < 0) { + LOGE("Error reading header %d, %s\r\n", rlen, buffer); + ws->frame_state.bytes_remaining = 0; + return rlen; + } + + if (rlen == 0) { + ws->frame_state.bytes_remaining = 0; + return 0; + } + } + + if (ws->frame_state.payload_len) { + if ((rlen = ws_read_payload(client, buffer, len, timeout_ms)) <= 0) { + LOGE("Error reading payload data\r\n"); + ws->frame_state.bytes_remaining = 0; + return rlen; + } + LOGD("%s, payload len:%d\r\n", __func__, rlen); + } + + return rlen; +} + +struct timeval* utils_ms_to_timeval(int timeout_ms, struct timeval* tv) +{ + if (timeout_ms == -1) { + return NULL; + } + tv->tv_sec = timeout_ms / 1000; + tv->tv_usec = (timeout_ms - (tv->tv_sec * 1000)) * 1000; + return tv; +} + +static void ms_to_timeval(int timeout_ms, struct timeval* tv) +{ + tv->tv_sec = timeout_ms / 1000; + tv->tv_usec = (timeout_ms % 1000) * 1000; +} + +static int set_socket_non_blocking(int fd, bool non_blocking) +{ + int flags; + if ((flags = fcntl(fd, F_GETFL, 0)) < 0) { + LOGE("[sock=%d] get file flags error: %s", fd, strerror(errno)); + return -1; + } + + if (non_blocking) { + flags |= O_NONBLOCK; + } else { + flags &= ~O_NONBLOCK; + } + + if (fcntl(fd, F_SETFL, flags) < 0) { + LOGE("[sock=%d] set blocking/nonblocking error: %s", fd, strerror(errno)); + return -1; + } + LOGD("[sock=%d] non_block:%d\r\n", fd, non_blocking); + return 0; +} + +static int hostname_to_fd(const char* host, size_t hostlen, int port, struct sockaddr_storage* address, int* fd) +{ + struct addrinfo* address_info; + struct addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + + LOGD("host:%s: strlen %lu\r\n", host, (unsigned long) hostlen); + int res = getaddrinfo(host, NULL, &hints, &address_info); + if (res != 0 || address_info == NULL) { + LOGE("couldn't get hostname for :%s: " + "getaddrinfo() returns %d, addrinfo=%p", + host, res, address_info); + return -1; + } + *fd = socket(address_info->ai_family, address_info->ai_socktype, address_info->ai_protocol); + if (*fd < 0) { + LOGE("Failed to create socket (family %d socktype %d protocol %d)", address_info->ai_family, address_info->ai_socktype, + address_info->ai_protocol); + freeaddrinfo(address_info); + return -1; + } + + if (address_info->ai_family == AF_INET) { + struct sockaddr_in* p = (struct sockaddr_in*) address_info->ai_addr; + p->sin_port = htons(port); + // LOGE("[sock=%d] Resolved IPv4 address: %s\r\n", *fd, ipaddr_ntoa((const ip_addr_t*)&p->sin_addr.s_addr)); + memcpy(address, p, sizeof(struct sockaddr)); + } +#if LWIP_IPV6 + else if (address_info->ai_family == AF_INET6) { + struct sockaddr_in6* p = (struct sockaddr_in6*) address_info->ai_addr; + p->sin6_port = htons(port); + p->sin6_family = AF_INET6; + LOGE("[sock=%d] Resolved IPv6 address: %s", *fd, ip6addr_ntoa((const ip6_addr_t*) &p->sin6_addr)); + memcpy(address, p, sizeof(struct sockaddr_in6)); + } +#endif + else { + LOGE("Unsupported protocol family %d", address_info->ai_family); + close(*fd); + freeaddrinfo(address_info); + return -1; + } + + freeaddrinfo(address_info); + return 0; +} + +static int _tcp_connect(int* sockfd, const char* host, int hostlen, int port, int timeout_ms) +{ + struct sockaddr_storage address; + int fd; + int ret = hostname_to_fd(host, hostlen, port, &address, &fd); + if (ret != 0) { + LOGE("%s error fd\r\n", __func__); + return ret; + } + + if (timeout_ms) { + struct timeval tv; + ms_to_timeval(timeout_ms, &tv); + if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) != 0) { + LOGE("Fail to setsockopt SO_RCVTIMEO"); + return -1; + } + if (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) != 0) { + LOGE("Fail to setsockopt SO_SNDTIMEO"); + return -1; + } + } + + // Set to non block before connecting to better control connection timeout + ret = set_socket_non_blocking(fd, true); + if (ret != 0) { + LOGE("%s error nb", __func__); + goto err; + } + + if (connect(fd, (struct sockaddr*) &address, sizeof(struct sockaddr)) < 0) { + if (errno == EINPROGRESS) { + fd_set fdset; + struct timeval tv = {.tv_usec = 0, .tv_sec = 10}; // Default connection timeout is 10 s + + if (timeout_ms > 0) { + ms_to_timeval(timeout_ms, &tv); + } + FD_ZERO(&fdset); + FD_SET(fd, &fdset); + + int res = select(fd + 1, NULL, &fdset, NULL, &tv); + if (res < 0) { + LOGE("[sock=%d] select() error: %s\r\n", fd, strerror(errno)); + ret = -1; + goto err; + } else if (res == 0) { + LOGE("[sock=%d] select() timeout\r\n", fd); + ret = -1; + goto err; + } else { + int sockerr; + socklen_t len = (socklen_t) sizeof(int); + LOGD("[sock=%d] select() occur", fd); + if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (void*) (&sockerr), &len) < 0) { + LOGE("[sock=%d] getsockopt() error: %s\r\n", fd, strerror(errno)); + ret = -1; + goto err; + } else if (sockerr) { + LOGE("[sock=%d] delayed connect error: %s\r\n", fd, strerror(sockerr)); + ret = -1; + goto err; + } else if (!sockerr) { + LOGD("[sock=%d] no blocking connected", fd); + } + } + } else { + LOGE("[sock=%d] connect() error: %s\r\n", fd, strerror(errno)); + ret = -1; + goto err; + } + } + // reset back to blocking mode (unless non_block configured) + ret = set_socket_non_blocking(fd, false); + if (ret != 0) { + LOGE("%s error nb\r\n", __func__); + goto err; + } + *sockfd = fd; + return 0; + +err: + close(fd); + return ret; +} + +static int _ssl_init(volc_ws_client_t* client) { + int tls_ret = 0; + const char* pers = "websocket"; + if (NULL == client) { + LOGE("invalid input args"); + return -1; + } + client->ssl = volc_osal_calloc(1, sizeof(MbedTLSSession)); + if (NULL == client->ssl) { + LOGE("malloc ssl failed"); + goto _websocket_init_fail; + } + client->ssl->buffer_len = WEBSOCKET_BUFFER_SIZE_BYTE; + client->ssl->buffer = volc_osal_malloc(client->ssl->buffer_len); + if (NULL == client->ssl->buffer) { + LOGE("malloc ssl buffer fail"); + goto _websocket_init_fail; + } + if ((tls_ret = mbedtls_client_init(client->ssl, (void*) pers, strlen(pers))) < 0) { + LOGE("initialize https client failed return: -0x%x.", -tls_ret); + goto _websocket_init_fail; + } + client->ssl->host = strdup(client->host); + char port[8] = {0}; + if (NULL == client->ssl->host) { + LOGE("malloc host memory failed"); + goto _websocket_init_fail; + } + snprintf(port, sizeof(port), "%d", client->port); + client->ssl->port = strdup(port); + LOGD("websocket set port:%d init ssl %p", client->port, client->ssl); + return 0; +_websocket_init_fail: + mbedtls_client_close(client->ssl); + return -1; +} + +static int ws_tcp_connect(volc_ws_client_t* client, const char* host, int port, int timeout_ms) +{ + int err = 0; +#if defined(CONFIG_WEBSOCKET_TLS) + if (client->is_tls == 1) { + int tls_ret = 0; + if ((tls_ret = _ssl_init(client)) < 0) { + LOGE("ssl init failed"); + return -1; + } + if ((tls_ret = mbedtls_client_context(client->ssl)) < 0) { + LOGE("mbedtls_client_connect failed return: -0x%x.\r\n", -tls_ret); + return -1; + } + if ((tls_ret = mbedtls_client_connect(client->ssl)) < 0) { + LOGE("mbedtls_client_connect failed return: -0x%x.\r\n", -tls_ret); + return -1; + } + client->sockfd = client->ssl->server_fd.fd; + } else +#endif + err = _tcp_connect(&client->sockfd, host, strlen(host), port, timeout_ms); + if (err != 0) { + LOGE("%s failed\r\n", __func__); + return -1; + } + + int nodelay = 1; + setsockopt(client->sockfd, IPPROTO_TCP, TCP_NODELAY, &nodelay, sizeof(nodelay)); + return 0; +} + +static int _ssl_write(volc_ws_client_t* client, const char* buffer, int len) { + size_t written = 0; + size_t write_len = len; + while (written < len) { + if (write_len > MBEDTLS_SSL_OUT_CONTENT_LEN) { + write_len = MBEDTLS_SSL_OUT_CONTENT_LEN; + } + if (len > MBEDTLS_SSL_OUT_CONTENT_LEN) { + LOGE("Fragmenting data of excessive size :%d, offset: %d, size %d\r\n", len, written, write_len); + } + ssize_t ret = mbedtls_ssl_write(&client->ssl->ssl, (unsigned char*) buffer + written, write_len); + if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE) { + volc_osal_thread_sleep(1); + continue; + } + if (ret <= 0) { + LOGE("write error :-0x%04X:\r\n", -ret); + return -1; + } + else + LOGD("mbedtls_ssl_write, ret:%d\r\n", ret); + written += ret; + write_len = len - written; + } + return written; +} + +static int ws_tcp_write(volc_ws_client_t* client, const char* buffer, int len, int timeout_ms) +{ + int err = 0; +#if defined(CONFIG_WEBSOCKET_TLS) + if (client->is_tls == 1) + // err = mbedtls_client_write(client->ssl, (const unsigned char*)buffer, len); + err = _ssl_write(client, buffer, len); + else +#endif + err = _tcp_write(&client->sockfd, buffer, len, timeout_ms); + return err; +} + +static int ws_tcp_read(volc_ws_client_t* client, char* buffer, int len, int timeout_ms) +{ + int err = 0; +#if defined(CONFIG_WEBSOCKET_TLS) + if (client->is_tls == 1) { + if (client->state != VOLC_WS_STATE_CONNECTED) { + return mbedtls_client_read(client->ssl, (unsigned char*)buffer, len); + } + uint64_t deadline = volc_osal_get_time_ms() + timeout_ms; + /* The caller owns mutex. A TLS read on a blocking TCP socket can wait + * for an entire record and starve the audio sender for seconds. Keep + * TLS calls serialized, but release the mutex while waiting for bytes. */ + do { + err = mbedtls_ssl_read(&client->ssl->ssl, (unsigned char*)buffer, len); + if (err != MBEDTLS_ERR_SSL_WANT_READ && err != MBEDTLS_ERR_SSL_WANT_WRITE) { + return err > 0 ? err : -1; + } + volc_osal_mutex_unlock(client->mutex); + if (err == MBEDTLS_ERR_SSL_WANT_READ) _tcp_poll_read(&client->sockfd, 20); + else _tcp_poll_write(&client->sockfd, 20); + volc_osal_mutex_lock(client->mutex); + } while (client->run && volc_osal_get_time_ms() < deadline); + LOGE("TLS read timeout while awaiting frame data"); + return -1; + } + else +#endif + err = _tcp_read(&client->sockfd, buffer, len, timeout_ms); + return err; +} + +static int ws_tcp_poll_read(volc_ws_client_t* client, int timeout_ms) +{ +#if defined(CONFIG_WEBSOCKET_TLS) + if (client->is_tls && mbedtls_ssl_get_bytes_avail(&client->ssl->ssl) > 0) { + return 1; + } +#endif + int err = 0; + err = _tcp_poll_read(&client->sockfd, timeout_ms); + return err; +} + +static int ws_tcp_poll_write(volc_ws_client_t* client, int timeout_ms) +{ + int err = 0; + err = _tcp_poll_write(&client->sockfd, timeout_ms); + return err; +} + +static int ws_tcp_close(volc_ws_client_t* client) +{ + int err = 0; +#if defined(CONFIG_WEBSOCKET_TLS) + if (client->is_tls == 1) { + err = mbedtls_client_close(client->ssl); + client->ssl = NULL; + } else +#endif + err = _tcp_close(client); + if (err != 0) { + LOGE("%s failed", __func__); + return -1; + } + + return 0; +} + +static int ws_disconnect(volc_ws_client_t* client) +{ + if (client == NULL) { + LOGE("client aleady null\r\n"); + return -1; + } + ws_tcp_close(client); + if (client->auto_reconnect) { + client->reconnect_tick_ms = volc_osal_get_time_ms(); + } + client->state = VOLC_WS_STATE_WAIT_TIMEOUT; + volc_ws_client_dispatch_event(client, VOLC_WS_EVENT_DISCONNECTED, NULL, 0, -1); + return 0; +} + +static int ws_connect(volc_ws_client_t* client, const char* host, int port, int timeout_ms) +{ + transport_ws_t* ws = client->ws_transport; + + if (ws_tcp_connect(client, host, port, timeout_ms) < 0) { + return -1; + } + unsigned char random_key[16]; + volc_osal_fill_random(random_key, sizeof(random_key)); + + unsigned char client_key[28] = {0}; + + const char* user_agent_ptr = (ws->user_agent) ? (ws->user_agent) : "Volc Websocket Client"; + + size_t outlen = 0; + + volc_base64_encode(client_key, sizeof(client_key), &outlen, random_key, sizeof(random_key)); + + int len = snprintf(ws->buffer, WS_BUFFER_SIZE, + "GET %s HTTP/1.1\r\n" + "Pragma: no-cache\r\n" + "Cache-Control: no-cache\r\n" + "Host: %s\r\n" + "Origin: http://%s\r\n" + "User-Agent: %s\r\n" + "Upgrade: websocket\r\n" + "Connection: Upgrade\r\n" + "Sec-WebSocket-Key: %s\r\n" + "Sec-WebSocket-Version: 13\r\n", + ws->path, host, host, user_agent_ptr, client_key); + LOGD("http request: %s", ws->buffer); + + if (len <= 0 || len >= WS_BUFFER_SIZE) { + LOGE("Error in request generation, desired request len: %d, buffer size: %d\r\n", len, WS_BUFFER_SIZE); + return -1; + } + + if (ws->sub_protocol) { + LOGE("sub_protocol: %s\r\n", ws->sub_protocol); + int r = snprintf(ws->buffer + len, WS_BUFFER_SIZE - len, "Sec-WebSocket-Protocol: %s\r\n", ws->sub_protocol); + len += r; + if (r <= 0 || len >= WS_BUFFER_SIZE) { + LOGE("Error in request generation" + "(snprintf of subprotocol returned %d, desired request len: %d, buffer size: %d\r\n", + r, len, WS_BUFFER_SIZE); + return -1; + } + } + if (ws->headers) { + int r = snprintf(ws->buffer + len, WS_BUFFER_SIZE - len, "%s", ws->headers); + len += r; + if (r <= 0 || len >= WS_BUFFER_SIZE) { + LOGE("Error in request generation" + "(strncpy of headers returned %d, desired request len: %d, buffer size: %d\r\n", + r, len, WS_BUFFER_SIZE); + return -1; + } + } + int r = snprintf(ws->buffer + len, WS_BUFFER_SIZE - len, "\r\n"); + len += r; + if (r <= 0 || len >= WS_BUFFER_SIZE) { + LOGE("Error in request generation" + "(snprintf of header terminal returned %d, desired request len: %d, buffer size: %d\r\n", + r, len, WS_BUFFER_SIZE); + return -1; + } + LOGD("ws->buffer:\r\n%s", ws->buffer); + if (ws_tcp_write(client, ws->buffer, len, timeout_ms) < 0) { + LOGE("Write FAIL"); + } + + int header_len = 0; + do { + if ((len = ws_tcp_read(client, ws->buffer + header_len, WS_BUFFER_SIZE - header_len, timeout_ms)) <= 0) { + LOGE("read FAIL for upgrade header:%s\r\n", ws->buffer); + return -1; + } + header_len += len; + ws->buffer[header_len] = '\0'; + LOGD("Read header chunk %d, current header size: %d, header: %s\r\n", len, header_len, ws->buffer); + } while (NULL == strstr(ws->buffer, "\r\n\r\n") && header_len < WS_BUFFER_SIZE); + + char* server_key = get_http_header(ws->buffer, "Sec-WebSocket-Accept:"); + if (server_key == NULL) { + LOGE("Sec-WebSocket-Accept not found\r\n"); + return -1; + } + + unsigned char expected_server_sha1[20]; + unsigned char expected_server_key[33] = {0}; + // If you are interested, see https://tools.ietf.org/html/rfc6455 + const char expected_server_magic[] = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; + unsigned char expected_server_text[sizeof(client_key) + sizeof(expected_server_magic) + 1]; + strncpy((char*) expected_server_text, (char*) client_key, sizeof(client_key)); + strncat((char*) expected_server_text, expected_server_magic, sizeof(expected_server_text) - sizeof(client_key) - 1); + + size_t key_len = strlen((char*) expected_server_text); +#if 0 + int ret = mbedtls_sha1_ret(expected_server_text, key_len, expected_server_sha1); +#else + int ret = mbedtls_sha1(expected_server_text, key_len, expected_server_sha1); +#endif + if (ret != 0) { + LOGE("Error in calculating sha1 sum , Returned 0x%02X\r\n", ret); + return ret; + } + volc_base64_encode(expected_server_key, sizeof(expected_server_key), &outlen, expected_server_sha1, sizeof(expected_server_sha1)); + expected_server_key[(outlen < sizeof(expected_server_key)) ? outlen : (sizeof(expected_server_key) - 1)] = 0; + LOGD("server key=%s\r\n\r\n", (char*) server_key); + LOGD("send_key=%s\r\n\r\n", (char*) client_key); + LOGD("expected_server_key=%s\r\n\r\n", expected_server_key); + if (strcmp((char*) expected_server_key, (char*) server_key) != 0) { + LOGE("Invalid websocket key\r\n"); + return -1; + } + return 0; +} + +int ws_poll_connection_closed(int* sockfd, int timeout_ms) +{ + struct timeval timeout; + fd_set readset; + fd_set errset; + FD_ZERO(&readset); + FD_ZERO(&errset); + FD_SET(*sockfd, &readset); + FD_SET(*sockfd, &errset); + + int ret = select(*sockfd + 1, &readset, NULL, &errset, utils_ms_to_timeval(timeout_ms, &timeout)); + if (ret > 0) { + if (FD_ISSET(*sockfd, &readset)) { + uint8_t buffer; + if (recv(*sockfd, &buffer, 1, MSG_PEEK) <= 0) { + // socket is readable, but reads zero bytes -- connection cleanly closed by FIN flag + return 0; + } + LOGW("ws_poll_connection_closed: unexpected data readable on socket=%d", *sockfd); + } else if (FD_ISSET(*sockfd, &errset)) { + int sock_errno = 0; + uint32_t optlen = sizeof(sock_errno); + getsockopt(*sockfd, SOL_SOCKET, SO_ERROR, &sock_errno, &optlen); + LOGD("ws_poll_connection_closed select error %d, errno = %s, fd = %d", sock_errno, strerror(sock_errno), *sockfd); + if (sock_errno == ENOTCONN || sock_errno == ECONNRESET || sock_errno == ECONNABORTED) { + return 0; + } + LOGE("ws_poll_connection_closed: unexpected errno=%d on socket=%d", sock_errno, *sockfd); + } + return -1; + } + return ret; +} + +static int ws_client_recv(volc_ws_client_t* client) +{ + int rlen; + client->payload_offset = 0; + transport_ws_t* ws = client->ws_transport; + do { + LOGD("----------begin receive--------------\r\n"); + memset(client->rx_buffer, 0, client->buffer_size); + rlen = ws_read(client, client->rx_buffer, client->buffer_size, WEBSOCKET_NETWORK_TIMEOUT_MS); + if (rlen < 0) { + LOGE("Error read data\r\n"); + return -1; + } + + client->payload_len = ws->frame_state.payload_len; + client->last_fin = ws->frame_state.fin; + client->last_opcode = (volc_ws_opcode_e) ws->frame_state.opcode; + + if (rlen == 0 && client->last_opcode == VOLC_WS_OPCODES_NONE) { + LOGE("ws read timeouts\r\n"); + return 0; + } + /* Only TLS I/O needs the socket mutex. Application callbacks may + * update the display or queue audio and must not stall the uplink. */ + volc_osal_mutex_unlock(client->mutex); + volc_ws_client_dispatch_event(client, VOLC_WS_EVENT_DATA, client->rx_buffer, rlen, client->last_opcode); + volc_osal_mutex_lock(client->mutex); + client->payload_offset += rlen; + } while (client->payload_offset < client->payload_len); + + if (client->last_opcode == VOLC_WS_OPCODES_PING) { + const char* data = (client->payload_len == 0) ? NULL : client->rx_buffer; + LOGD("Received ping, Sending PONG with payload len=%d\r\n", client->payload_len); + ws_write(client, VOLC_WS_OPCODES_PONG | VOLC_WS_OPCODES_FIN, WS_MASK, data, client->payload_len, WEBSOCKET_NETWORK_TIMEOUT_MS); + } else if (client->last_opcode == VOLC_WS_OPCODES_PONG) { + client->wait_for_pong_resp = false; + } else if (client->last_opcode == VOLC_WS_OPCODES_CLOSE) { + LOGI("Received close frame\r\n"); + client->state = VOLC_WS_STATE_CLOSING; + } else if (client->last_opcode == VOLC_WS_OPCODES_TEXT) { + LOGD("Received text frame: \r\n"); + } + + return 0; +} + +static int volc_ws_client_destory_config(volc_ws_client_t* client) +{ + if (client == NULL) { + LOGE("%s, client already null, return", __func__); + return -1; + } + + HAL_SAFE_FREE(client->host); + HAL_SAFE_FREE(client->path); + HAL_SAFE_FREE(client->scheme); + +#if defined(CONFIG_WEBSOCKET_TLS) + client->is_tls = 0; +#endif + return 0; +} + +bool volc_ws_client_is_connected(volc_ws_client_t* client) +{ + if (client == NULL) { + return false; + } + return client->state == VOLC_WS_STATE_CONNECTED; +} + +static int volc_ws_client_send_with_opcode(volc_ws_client_t* client, volc_ws_opcode_e opcode, const uint8_t* data, int len, int timeout) +{ + int need_write = len; + int wlen = 0, widx = 0; + int ret = -1; + LOGD("%s: begin send opcode: %d\r\n", __func__, opcode); + if (client == NULL || len < 0 || (opcode != VOLC_WS_OPCODES_CLOSE && (data == NULL || len <= 0))) { + LOGE("Invalid arguments\r\n"); + return -1; + } + + if (!volc_ws_client_is_connected(client)) { + LOGE("Websocket client is not connected\r\n"); + return -1; + } + + /* Serialize whole outgoing messages separately from TLS state, so a full + * TCP send buffer cannot stop the receiver from draining incoming audio. */ + volc_osal_mutex_lock(client->send_mutex); + uint32_t current_opcode = opcode; + while (widx < len || current_opcode) { + if (ws_tcp_poll_write(client, timeout) <= 0) { + LOGW("UPLINK TCP backpressure timeout=%d ms; receiver remains active", timeout); + goto unlock_send_and_return; + } + volc_osal_mutex_lock(client->mutex); + if (!client->run || !volc_ws_client_is_connected(client)) { + volc_osal_mutex_unlock(client->mutex); + goto unlock_send_and_return; + } + if (need_write > client->buffer_size) { + need_write = client->buffer_size; + } else { + current_opcode |= VOLC_WS_OPCODES_FIN; + } + if (need_write) memcpy(client->tx_buffer, data + widx, need_write); + + wlen = ws_write(client, current_opcode, WS_MASK, (char*) client->tx_buffer, need_write, timeout); + volc_osal_mutex_unlock(client->mutex); + if (wlen < 0 || (wlen == 0 && need_write != 0)) { + ret = -1; + LOGE("Network error: ws_write() returned %d, errno=%d\r\n", ret, errno); + if (errno == EAGAIN) + LOGE("Network error: ws_write() EAGAIN, drop\r\n"); + goto unlock_send_and_return; + } + current_opcode = 0; + widx += wlen; + need_write = len - widx; + } + ret = widx; + +unlock_send_and_return: + volc_osal_mutex_unlock(client->send_mutex); + return ret; +} + +int volc_ws_client_send_text(volc_ws_client_t* client, const char* data, int len, int timeout) +{ + return volc_ws_client_send_with_opcode(client, VOLC_WS_OPCODES_TEXT, (const uint8_t*) data, len, timeout); +} + +int volc_ws_client_send_binary(volc_ws_client_t* client, const char* data, int len, int timeout) +{ + return volc_ws_client_send_with_opcode(client, VOLC_WS_OPCODES_BINARY, (const uint8_t*) data, len, timeout); +} + +int volc_ws_client_send_close(volc_ws_client_t* client, const char* data, int len, int timeout) +{ + if (volc_ws_client_send_with_opcode(client, VOLC_WS_OPCODES_CLOSE, (const uint8_t*) data, len, timeout) < 0) { + LOGE("Error send close frame\r\n"); + return -1; + } + status_bits = 0; + status_bits |= CLOSE_SENT_BIT; + return 0; +} + +static int __parse_url(volc_ws_client_t* client, char* url) { + bool is_ipv6 = false; + char* pos = url; + char* url_end = NULL; + char* filed_end = NULL; + int len = 0; + if (NULL == client || NULL == url) { + LOGE("invalid input args"); + return -1; + } + url_end = url + strlen(url); + LOGD("url: %s", url); + + // 1. parse scheme + filed_end = strstr(pos, "://"); + if (filed_end) { + len = filed_end - pos; + client->scheme = (char *)volc_osal_malloc(len + 1); + if (NULL == client->scheme) { + LOGE("malloc scheme memory failed"); + return -1; + } + strncpy(client->scheme, pos, len); + client->scheme[len] = 0; + pos = filed_end+ 3; + } else { + client->scheme = strdup("http"); + if (NULL == client->scheme) { + LOGE("malloc scheme memory failed"); + return -1; + } + } + + // 2. parse host + filed_end = pos; + while (filed_end < url_end) { + if (*filed_end == '/' || *filed_end == '?' || + *filed_end == '#' || *filed_end == ':') { + break; + } + filed_end++; + } + + if (*pos == '[') { + is_ipv6 = true; + pos++; // 跳过 '[' + filed_end = strchr(pos, ']'); + if (!filed_end) { + // IPv6 地址未闭合 + return -1; + } + } + + len = filed_end - pos; + client->host = (char *)volc_osal_malloc(len + 1); + if (NULL == client->host) { + LOGE("malloc host memory failed"); + return -1; + } + strncpy(client->host, pos, len); + client->host[len] = 0; + pos = filed_end; + + // 3. parse port + if (*pos == ':') { + pos++; // 跳过 ':' + const char *port_end = pos; + while (port_end < url_end && isdigit((int)*port_end)) port_end++; + + if (port_end == pos) { + // 端口号缺失 + return -1; + } + + char port_str[16]; + size_t port_len = port_end - pos; + if (port_len >= sizeof(port_str)) return -1; + + strncpy(port_str, pos, port_len); + port_str[port_len] = '\0'; + client->port = atoi(port_str); + pos = (char *)port_end; + LOGE("port: %d", (int)client->port); + } + + // 4. parse path + if (*pos == '/') { + len = url_end - pos; + client->path = (char *)volc_osal_malloc(len + 1); + if (NULL == client->path) { + LOGE("malloc path memory failed"); + return -1; + } + strncpy(client->path, pos, len); + client->path[len] = 0; + } else { + client->path = strdup("/"); + if (NULL == client->path) { + LOGE("malloc path memory failed"); + return -1; + } + } + + LOGD("scheme: %s, host: %s, port: %d, path: %s", client->scheme, client->host, (int)client->port, client->path); + return 0; +} + +volc_ws_client_t* volc_ws_client_init(const volc_ws_config_t* input) +{ + int tls_ret = 0; + const char* pers = "websocket"; + volc_ws_client_t* client = (volc_ws_client_t*) volc_osal_malloc(sizeof(volc_ws_client_t)); + memset(client, 0, sizeof(volc_ws_client_t)); + // parse websocket uri to websocket config + if (input->uri) { + if (__parse_url(client, (char *)input->uri) != 0) { + LOGE("set uri fail, client destory\r\n"); + goto _websocket_init_fail; + } + } + if ((strncmp(input->uri, "ws://", 5) == 0 || strncmp(input->uri, "http://", 7) == 0) && client->port == 0) { + client->port = WEBSOCKET_TCP_DEFAULT_PORT; + LOGI("websocket set default port"); + } else if ((strncmp(input->uri, "wss://", 6) == 0 || strncmp(input->uri, "https://", 8) == 0)) { +#if defined(CONFIG_WEBSOCKET_TLS) + if (client->port == 0) { + client->port = WEBSOCKET_SSL_DEFAULT_PORT; + } + client->is_tls = 1; + LOGD("websocket set port:%d", client->port); +#else + LOGE("websocket not open tls"); + goto _websocket_init_fail; +#endif + } + + client->user_context = input->user_context; + + // set event callback + client->ws_event_handler = input->ws_event_handler; + + // set autoreconnect + client->auto_reconnect = true; + + // init lock + client->mutex = volc_osal_mutex_create(); + client->send_mutex = volc_osal_mutex_create(); + + // set ws_transport + client->ws_transport = (transport_ws_t*) volc_osal_malloc(sizeof(transport_ws_t)); + memset(client->ws_transport, 0, sizeof(transport_ws_t)); + if (!client->ws_transport) { + LOGE("alloc ws_transport fail\r\n"); + goto _websocket_init_fail; + } + + if (client->path) { + HAL_SAFE_FREE(client->ws_transport->path); + client->ws_transport->path = strdup(client->path); + } else { + HAL_SAFE_FREE(client->ws_transport->path); + client->ws_transport->path = strdup("/"); + } + client->ws_transport->buffer = volc_osal_malloc(WS_BUFFER_SIZE); + if (!client->ws_transport->buffer) { + LOGE("alloc ws_transport buffer fail\r\n"); + goto _websocket_init_fail; + } + + if (input->subprotocol) { + HAL_SAFE_FREE(client->ws_transport->sub_protocol); + client->ws_transport->sub_protocol = strdup(input->subprotocol); + } + if (input->user_agent) { + HAL_SAFE_FREE(client->ws_transport->user_agent); + client->ws_transport->user_agent = strdup(input->user_agent); + } + if (input->headers) { + HAL_SAFE_FREE(client->ws_transport->headers); + client->ws_transport->headers = strdup(input->headers); + } + client->ws_transport->frame_state.bytes_remaining = 0; + + // tick... + client->reconnect_tick_ms = volc_osal_get_time_ms(); + client->ping_tick_ms = volc_osal_get_time_ms(); + client->wait_for_pong_resp = false; + + // rx retry + if (input->rx_retry <= 0) + client->rx_retry = WEBSOCKET_RX_RETRY_COUNT; + else + client->rx_retry = input->rx_retry; + + // buf malloc + int buffer_size = input->buffer_size; + if (buffer_size <= 0) { + buffer_size = WEBSOCKET_BUFFER_SIZE_BYTE; + } + client->buffer_size = buffer_size; + if (NULL == (client->rx_buffer = (char*) volc_osal_malloc(buffer_size))) { + LOGE("alloc rx_buffer fail\r\n"); + goto _websocket_init_fail; + } + + if (NULL == (client->tx_buffer = (char*) volc_osal_malloc(buffer_size + MAX_WEBSOCKET_HEADER_SIZE))) { + LOGE("alloc tx_buffer fail\r\n"); + goto _websocket_init_fail; + } + + // sockfd + client->sockfd = -1; + + return client; + +_websocket_init_fail: +#if defined(CONFIG_WEBSOCKET_TLS) + if (client->ssl) { + mbedtls_client_close(client->ssl); + client->ssl = NULL; + } +#endif + volc_ws_client_destroy(client); + return NULL; +} + +int volc_ws_client_destroy(volc_ws_client_t* client) +{ + if (client == NULL) { + return -1; + } + if (client->run && client->state == VOLC_WS_STATE_CONNECTED) { + /* Cleanup must still happen if the peer has already gone away. */ + (void)volc_ws_client_send_close(client, NULL, 0, 1000); + } + return volc_ws_client_stop(client); +} + +static void free_client(volc_ws_client_t* client) +{ + if (client == NULL) + return; + + volc_osal_mutex_destroy(client->mutex); + client->mutex = NULL; + volc_osal_mutex_destroy(client->send_mutex); + client->send_mutex = NULL; + client->ws_event_handler = NULL; + + HAL_SAFE_FREE(client->tx_buffer); + HAL_SAFE_FREE(client->rx_buffer); + + if (client->ws_transport) { + HAL_SAFE_FREE(client->ws_transport->buffer); + HAL_SAFE_FREE(client->ws_transport->path); + HAL_SAFE_FREE(client->ws_transport->sub_protocol); + HAL_SAFE_FREE(client->ws_transport->headers); + HAL_SAFE_FREE(client->ws_transport->user_agent); + HAL_SAFE_FREE(client->ws_transport); + } + HAL_SAFE_FREE(client); +} + +#if defined(PLATFORM_MACOS) +void* volc_ws_client_task(void* thread_param) +#else +void volc_ws_client_task(void* thread_param) +#endif +{ + volc_ws_client_t* client = (volc_ws_client_t*) thread_param; + + client->state = VOLC_WS_STATE_INIT; + + status_bits = PING_SENT_BIT; + int read_select = 0; + while (client->run) { + switch ((int) client->state) { + case VOLC_WS_STATE_INIT: + LOGI("websocket connecting to %s://%s:%d", client->scheme, client->host, client->port); + if (ws_connect(client, client->host, client->port, WEBSOCKET_NETWORK_TIMEOUT_MS) < 0) { + LOGE("Error websocket connect"); + ws_disconnect(client); + break; + } + LOGI("websocket connected to %s://%s:%d", client->scheme, client->host, client->port); + if (set_socket_non_blocking(client->sockfd, true) != 0) { + ws_disconnect(client); + break; + } + client->state = VOLC_WS_STATE_CONNECTED; + client->wait_for_pong_resp = false; + volc_ws_client_dispatch_event(client, VOLC_WS_EVENT_CONNECTED, NULL, 0, -1); + break; + case VOLC_WS_STATE_CONNECTED: + LOGD("%s, status:%02x %llu %llu\r\n", __func__, status_bits, volc_osal_get_time_ms(), client->ping_tick_ms); + if (volc_osal_get_time_ms() - client->ping_tick_ms > WEBSOCKET_PING_INTERVAL_SEC * 1000) { + client->ping_tick_ms = volc_osal_get_time_ms(); + + if (status_bits & PING_SENT_BIT) { + volc_osal_mutex_lock(client->mutex); + ws_write(client, VOLC_WS_OPCODES_PING | VOLC_WS_OPCODES_FIN, WS_MASK, NULL, 0, WEBSOCKET_NETWORK_TIMEOUT_MS); + volc_osal_mutex_unlock(client->mutex); + } + if (!client->wait_for_pong_resp) { + client->pingpong_tick_ms = volc_osal_get_time_ms(); + client->wait_for_pong_resp = true; + } + } + if (volc_osal_get_time_ms() - client->pingpong_tick_ms > WEBSOCKET_PINGPONG_TIMEOUT_SEC * 1000) { + if (client->wait_for_pong_resp) { + LOGW("Error, no PONG received for more than %" PRIu64 " seconds after PING\r\n", client->pingpong_tick_ms); + break; + } + } + + if (read_select == 0) { + LOGD("Read poll timeout: skipping read()..."); + break; + } + // client->ping_tick_ms = volc_osal_get_time_ms(); + volc_osal_mutex_lock(client->mutex); + if (ws_client_recv(client) == -1) { + LOGE("Error receive data"); + ws_disconnect(client); + volc_osal_mutex_unlock(client->mutex); + break; + } + volc_osal_mutex_unlock(client->mutex); + break; + + case VOLC_WS_STATE_WAIT_TIMEOUT: + if (!client->auto_reconnect) { + client->run = false; + break; + } + if (volc_osal_get_time_ms() - client->reconnect_tick_ms > WEBSOCKET_RECONNECT_TIMEOUT_MS) { + client->state = VOLC_WS_STATE_INIT; + client->reconnect_tick_ms = volc_osal_get_time_ms(); + LOGE("Reconnecting..."); + } + break; + case VOLC_WS_STATE_CLOSING: + LOGE("Closing initiated by the server, sending close frame"); + ws_write(client, VOLC_WS_OPCODES_CLOSE | VOLC_WS_OPCODES_FIN, WS_MASK, NULL, 0, WEBSOCKET_NETWORK_TIMEOUT_MS); + break; + default: + LOGE("Client run iteration in a default state: %d", client->state); + break; + } + + if (VOLC_WS_STATE_CONNECTED == client->state) { + read_select = ws_tcp_poll_read(client, 1000); // Poll every 1000ms + if (read_select < 0) { + LOGE("Network error: ws_tcp_poll_read() returned %d, errno=%d", read_select, errno); + ws_disconnect(client); + } + } else if (VOLC_WS_STATE_WAIT_TIMEOUT == client->state) { + if (client->auto_reconnect) + volc_osal_thread_sleep(WEBSOCKET_RECONNECT_TIMEOUT_MS); + } else if (VOLC_WS_STATE_CLOSING == client->state) { + LOGW(" Waiting for TCP connection to be closed by the server"); + int ret = ws_poll_connection_closed(&(client->sockfd), 1000); + if (ret == 0) { + // still waiting + break; + } + if (ret < 0) { + LOGE("Connection terminated while waiting for clean TCP close"); + } + client->run = false; + client->state = VOLC_WS_STATE_UNKNOW; + volc_ws_client_dispatch_event(client, VOLC_WS_EVENT_CLOSED, NULL, 0, -1); + break; + } + } + LOGW("close connection..."); + volc_ws_client_dispatch_event(client, VOLC_WS_EVENT_DISCONNECTED, NULL, 0, -1); + volc_osal_mutex_lock(client->mutex); + ws_tcp_close(client); + volc_osal_mutex_unlock(client->mutex); + client->state = VOLC_WS_STATE_UNKNOW; + + if (volc_ws_client_destory_config(client)) { + LOGE("client config already volc_osal_free"); + } + if (client->tid) { + volc_osal_thread_destroy(client->tid); + } + client->exit = true; +#ifdef ESP_PLATFORM + vTaskDeleteWithCaps(NULL); +#else + volc_osal_thread_exit(NULL); +#endif +#if defined(PLATFORM_MACOS) + return NULL; +#else + return; +#endif +} + +int volc_ws_client_start(volc_ws_client_t* client) +{ + int ret; + + if (client == NULL) { + LOGE("The client has not be initialized"); + return -1; + } + if (client->state >= VOLC_WS_STATE_INIT) { + LOGE("The client has started"); + return -1; + } + volc_osal_thread_param_t param = {0}; + client->run = true; + client->exit = false; + snprintf(param.name, sizeof(param.name), "%s", "websocket"); + param.stack_size = WEBSOCKET_TASK_STACK; + param.priority = WEBSOCKET_TASK_PRIORITY; +#ifdef ESP_PLATFORM + /* Keep the 12 KiB TLS stack out of scarce internal RAM after Wi-Fi/AFE + * startup. This task does not perform NVS writes or OTA operations. */ + param.stack_in_ext = 1; + LOGI("WS task memory: internal_free=%u internal_largest=%u psram_free=%u", + (unsigned)heap_caps_get_free_size(MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT), + (unsigned)heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT), + (unsigned)heap_caps_get_free_size(MALLOC_CAP_SPIRAM)); +#endif + ret = volc_osal_thread_create(&client->tid, ¶m, volc_ws_client_task, (void*) client); + if (ret != 0) { + client->run = false; + client->exit = true; + LOGE("create volc_ws_client_task fail"); + return -1; + } + + return ret; +} + +int volc_ws_client_stop(volc_ws_client_t* client) +{ + if (client == NULL) { + LOGW("Client null"); + return -1; + } + client->run = false; + client->state = VOLC_WS_STATE_UNKNOW; + while(client->tid && !client->exit) { + volc_osal_thread_sleep(10); + } + free_client(client); + client = NULL; + return 0; +} diff --git a/firmware/echoear-platform-agent/components/volc_conv_ai/src/transports/low_load/third_party/websocket/websocket.h b/firmware/echoear-platform-agent/components/volc_conv_ai/src/transports/low_load/third_party/websocket/websocket.h new file mode 100644 index 0000000..ca039ca --- /dev/null +++ b/firmware/echoear-platform-agent/components/volc_conv_ai/src/transports/low_load/third_party/websocket/websocket.h @@ -0,0 +1,152 @@ +#ifndef __WEBSOCKET_H__ +#define __WEBSOCKET_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +#include "volc_osal.h" +#include "tls_client.h" +#define CONFIG_WEBSOCKET_TLS + +typedef enum { + VOLC_WS_STATE_ERROR = -1, + VOLC_WS_STATE_UNKNOW = 0, + VOLC_WS_STATE_INIT, + VOLC_WS_STATE_CONNECTED, + VOLC_WS_STATE_WAIT_TIMEOUT, + VOLC_WS_STATE_CLOSING, +} volc_ws_state_e; + +typedef enum { + VOLC_WS_OPCODES_CONT = 0x00, // 附加数据帧 + VOLC_WS_OPCODES_TEXT = 0x01, // 文本数据帧 + VOLC_WS_OPCODES_BINARY = 0x02, // 二进制数据帧 + VOLC_WS_OPCODES_CLOSE = 0x08, // 连接关闭 + VOLC_WS_OPCODES_PING = 0x09, // 心跳 + VOLC_WS_OPCODES_PONG = 0x0a, + VOLC_WS_OPCODES_FIN = 0x80, + VOLC_WS_OPCODES_NONE = 0x100, +} volc_ws_opcode_e; + +typedef enum { + VOLC_WS_EVENT_ANY = -1, + VOLC_WS_EVENT_ERROR = 0, + VOLC_WS_EVENT_CONNECTED, + VOLC_WS_EVENT_DISCONNECTED, + VOLC_WS_EVENT_DATA, + VOLC_WS_EVENT_CLOSED, + VOLC_WS_EVENT_MAX +} volc_ws_event_id_e; + +typedef struct { + uint8_t opcode; + bool fin; + char mask_key[4]; + int payload_len; + int bytes_remaining; + bool header_received; +} volc_ws_frame_state_t; + +typedef struct { + char* path; + char* buffer; + char* sub_protocol; + char* user_agent; + char* headers; + bool propagate_control_frames; + volc_ws_frame_state_t frame_state; +} transport_ws_t; + +typedef struct { + uint64_t last_print_ms; + int sent; + int received; +} ws_stats_t; + +typedef void (*volc_ws_event_handler_t)(void* user_context, int32_t event_id, void* event_data); + +typedef struct { + char* host; + char* path; + char* scheme; + char* signature; + int port; + volc_ws_state_e state; + // uint64_t keepalive_tick_ms; + uint64_t reconnect_tick_ms; + uint64_t ping_tick_ms; + uint64_t pingpong_tick_ms; + int auto_reconnect; + volatile bool run; + volatile bool exit; + bool wait_for_pong_resp; + char* rx_buffer; + char* tx_buffer; + int buffer_size; + int rx_retry; + bool last_fin; + volc_ws_opcode_e last_opcode; + int payload_len; + int payload_offset; + transport_ws_t* ws_transport; + int sockfd; + int is_tls; +#if defined(CONFIG_WEBSOCKET_TLS) + MbedTLSSession* ssl; +#endif + void* user_context; + volc_ws_event_handler_t ws_event_handler; + volc_osal_mutex_t mutex; + volc_osal_mutex_t send_mutex; + volc_osal_tid_t tid; + ws_stats_t stats; +} volc_ws_client_t; + +typedef struct { + const char* uri; + // bool disable_auto_reconnect; + int rx_retry; + void* user_context; + int buffer_size; + const char* subprotocol; + const char* user_agent; + const char* headers; + // bool disable_pingpong_discon; + volc_ws_event_handler_t ws_event_handler; +} volc_ws_config_t; + +/** + * @brief Websocket event data + */ +typedef struct { + char* data_ptr; + int data_len; + bool fin; + uint8_t op_code; + int payload_len; + int payload_offset; +} volc_ws_event_data_t; + +volc_ws_client_t* volc_ws_client_init(const volc_ws_config_t* input); +int volc_ws_client_destroy(volc_ws_client_t* client); +#if defined(PLATFORM_MACOS) +void* volc_ws_client_task(void* thread_param); +#else +void volc_ws_client_task(void* thread_param); +#endif +int volc_ws_client_start(volc_ws_client_t* client); +int volc_ws_client_stop(volc_ws_client_t* client); + +int volc_ws_client_send_text(volc_ws_client_t* client, const char* data, int len, int timeout); + +#ifdef __cplusplus +} +#endif +#endif /* __WEBSOCKET_H__ */ diff --git a/firmware/echoear-platform-agent/components/volc_conv_ai/src/util/cJSON/cJSON.c b/firmware/echoear-platform-agent/components/volc_conv_ai/src/util/cJSON/cJSON.c new file mode 100644 index 0000000..a5d3987 --- /dev/null +++ b/firmware/echoear-platform-agent/components/volc_conv_ai/src/util/cJSON/cJSON.c @@ -0,0 +1,3074 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +/* cJSON */ +/* JSON parser in C. */ + +/* disable warnings about old C89 functions in MSVC */ +#if !defined(_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) +#define _CRT_SECURE_NO_DEPRECATE +#endif + +#ifdef __GNUC__ +#pragma GCC visibility push(default) +#endif +#if defined(_MSC_VER) +#pragma warning (push) +/* disable warning about single line comments in system headers */ +#pragma warning (disable : 4001) +#endif + +#include +#include +#include +#include +#include +#include +#include + +#ifdef ENABLE_LOCALES +#include +#endif + +#if defined(_MSC_VER) +#pragma warning (pop) +#endif +#ifdef __GNUC__ +#pragma GCC visibility pop +#endif + +#include "cJSON.h" + +/* define our own boolean type */ +#ifdef true +#undef true +#endif +#define true ((cJSON_bool)1) + +#ifdef false +#undef false +#endif +#define false ((cJSON_bool)0) + +/* define isnan and isinf for ANSI C, if in C99 or above, isnan and isinf has been defined in math.h */ +#ifndef isinf +#define isinf(d) (isnan((d - d)) && !isnan(d)) +#endif +#ifndef isnan +#define isnan(d) (d != d) +#endif + +#ifndef NAN +#define NAN 0.0/0.0 +#endif + +typedef struct { + const unsigned char *json; + size_t position; +} error; +static error global_error = { NULL, 0 }; + +CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void) +{ + return (const char*) (global_error.json + global_error.position); +} + +CJSON_PUBLIC(char *) cJSON_GetStringValue(cJSON *item) +{ + if (!cJSON_IsString(item)) + { + return NULL; + } + + return item->valuestring; +} + +CJSON_PUBLIC(double) cJSON_GetNumberValue(cJSON *item) +{ + if (!cJSON_IsNumber(item)) + { + return NAN; + } + + return item->valuedouble; +} + +/* This is a safeguard to prevent copy-pasters from using incompatible C and header files */ +#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 13) + #error cJSON.h and cJSON.c have different versions. Make sure that both have the same. +#endif + +CJSON_PUBLIC(const char*) cJSON_Version(void) +{ + static char version[15]; + sprintf(version, "%i.%i.%i", CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR, CJSON_VERSION_PATCH); + + return version; +} + +/* Case insensitive string comparison, doesn't consider two NULL pointers equal though */ +static int case_insensitive_strcmp(const unsigned char *string1, const unsigned char *string2) +{ + if ((string1 == NULL) || (string2 == NULL)) + { + return 1; + } + + if (string1 == string2) + { + return 0; + } + + for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++) + { + if (*string1 == '\0') + { + return 0; + } + } + + return tolower(*string1) - tolower(*string2); +} + +typedef struct internal_hooks +{ + void *(CJSON_CDECL *allocate)(size_t size); + void (CJSON_CDECL *deallocate)(void *pointer); + void *(CJSON_CDECL *reallocate)(void *pointer, size_t size); +} internal_hooks; + +#if defined(_MSC_VER) +/* work around MSVC error C2322: '...' address of dllimport '...' is not static */ +static void * CJSON_CDECL internal_malloc(size_t size) +{ + return malloc(size); +} +static void CJSON_CDECL internal_free(void *pointer) +{ + free(pointer); +} +static void * CJSON_CDECL internal_realloc(void *pointer, size_t size) +{ + return realloc(pointer, size); +} +#else +#define internal_malloc malloc +#define internal_free free +#define internal_realloc realloc +#endif + +/* strlen of character literals resolved at compile time */ +#define static_strlen(string_literal) (sizeof(string_literal) - sizeof("")) + +static internal_hooks global_hooks = { internal_malloc, internal_free, internal_realloc }; + +static unsigned char* cJSON_strdup(const unsigned char* string, const internal_hooks * const hooks) +{ + size_t length = 0; + unsigned char *copy = NULL; + + if (string == NULL) + { + return NULL; + } + + length = strlen((const char*)string) + sizeof(""); + copy = (unsigned char*)hooks->allocate(length); + if (copy == NULL) + { + return NULL; + } + memcpy(copy, string, length); + + return copy; +} + +CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks) +{ + if (hooks == NULL) + { + /* Reset hooks */ + global_hooks.allocate = malloc; + global_hooks.deallocate = free; + global_hooks.reallocate = realloc; + return; + } + + global_hooks.allocate = malloc; + if (hooks->malloc_fn != NULL) + { + global_hooks.allocate = hooks->malloc_fn; + } + + global_hooks.deallocate = free; + if (hooks->free_fn != NULL) + { + global_hooks.deallocate = hooks->free_fn; + } + + /* use realloc only if both free and malloc are used */ + global_hooks.reallocate = NULL; + if ((global_hooks.allocate == malloc) && (global_hooks.deallocate == free)) + { + global_hooks.reallocate = realloc; + } +} + +/* Internal constructor. */ +static cJSON *cJSON_New_Item(const internal_hooks * const hooks) +{ + cJSON* node = (cJSON*)hooks->allocate(sizeof(cJSON)); + if (node) + { + memset(node, '\0', sizeof(cJSON)); + } + + return node; +} + +/* Delete a cJSON structure. */ +CJSON_PUBLIC(void) cJSON_Delete(cJSON *item) +{ + cJSON *next = NULL; + while (item != NULL) + { + next = item->next; + if (!(item->type & cJSON_IsReference) && (item->child != NULL)) + { + cJSON_Delete(item->child); + } + if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL)) + { + global_hooks.deallocate(item->valuestring); + } + if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) + { + global_hooks.deallocate(item->string); + } + global_hooks.deallocate(item); + item = next; + } +} + +/* get the decimal point character of the current locale */ +static unsigned char get_decimal_point(void) +{ +#ifdef ENABLE_LOCALES + struct lconv *lconv = localeconv(); + return (unsigned char) lconv->decimal_point[0]; +#else + return '.'; +#endif +} + +typedef struct +{ + const unsigned char *content; + size_t length; + size_t offset; + size_t depth; /* How deeply nested (in arrays/objects) is the input at the current offset. */ + internal_hooks hooks; +} parse_buffer; + +/* check if the given size is left to read in a given parse buffer (starting with 1) */ +#define can_read(buffer, size) ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length)) +/* check if the buffer can be accessed at the given index (starting with 0) */ +#define can_access_at_index(buffer, index) ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length)) +#define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index)) +/* get a pointer to the buffer at the position */ +#define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset) + +/* Parse the input text to generate a number, and populate the result into item. */ +static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_buffer) +{ + double number = 0; + unsigned char *after_end = NULL; + unsigned char number_c_string[64]; + unsigned char decimal_point = get_decimal_point(); + size_t i = 0; + + if ((input_buffer == NULL) || (input_buffer->content == NULL)) + { + return false; + } + + /* copy the number into a temporary buffer and replace '.' with the decimal point + * of the current locale (for strtod) + * This also takes care of '\0' not necessarily being available for marking the end of the input */ + for (i = 0; (i < (sizeof(number_c_string) - 1)) && can_access_at_index(input_buffer, i); i++) + { + switch (buffer_at_offset(input_buffer)[i]) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '+': + case '-': + case 'e': + case 'E': + number_c_string[i] = buffer_at_offset(input_buffer)[i]; + break; + + case '.': + number_c_string[i] = decimal_point; + break; + + default: + goto loop_end; + } + } +loop_end: + number_c_string[i] = '\0'; + + number = strtod((const char*)number_c_string, (char**)&after_end); + if (number_c_string == after_end) + { + return false; /* parse_error */ + } + + item->valuedouble = number; + + /* use saturation in case of overflow */ + if (number >= INT_MAX) + { + item->valueint = INT_MAX; + } + else if (number <= (double)INT_MIN) + { + item->valueint = INT_MIN; + } + else + { + item->valueint = (int)number; + } + + item->type = cJSON_Number; + + input_buffer->offset += (size_t)(after_end - number_c_string); + return true; +} + +/* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */ +CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number) +{ + if (number >= INT_MAX) + { + object->valueint = INT_MAX; + } + else if (number <= (double)INT_MIN) + { + object->valueint = INT_MIN; + } + else + { + object->valueint = (int)number; + } + + return object->valuedouble = number; +} + +CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring) +{ + char *copy = NULL; + /* if object's type is not cJSON_String or is cJSON_IsReference, it should not set valuestring */ + if (!(object->type & cJSON_String) || (object->type & cJSON_IsReference)) + { + return NULL; + } + if (strlen(valuestring) <= strlen(object->valuestring)) + { + strcpy(object->valuestring, valuestring); + return object->valuestring; + } + copy = (char*) cJSON_strdup((const unsigned char*)valuestring, &global_hooks); + if (copy == NULL) + { + return NULL; + } + if (object->valuestring != NULL) + { + cJSON_free(object->valuestring); + } + object->valuestring = copy; + + return copy; +} + +typedef struct +{ + unsigned char *buffer; + size_t length; + size_t offset; + size_t depth; /* current nesting depth (for formatted printing) */ + cJSON_bool noalloc; + cJSON_bool format; /* is this print a formatted print */ + internal_hooks hooks; +} printbuffer; + +/* realloc printbuffer if necessary to have at least "needed" bytes more */ +static unsigned char* ensure(printbuffer * const p, size_t needed) +{ + unsigned char *newbuffer = NULL; + size_t newsize = 0; + + if ((p == NULL) || (p->buffer == NULL)) + { + return NULL; + } + + if ((p->length > 0) && (p->offset >= p->length)) + { + /* make sure that offset is valid */ + return NULL; + } + + if (needed > INT_MAX) + { + /* sizes bigger than INT_MAX are currently not supported */ + return NULL; + } + + needed += p->offset + 1; + if (needed <= p->length) + { + return p->buffer + p->offset; + } + + if (p->noalloc) { + return NULL; + } + + /* calculate new buffer size */ + if (needed > (INT_MAX / 2)) + { + /* overflow of int, use INT_MAX if possible */ + if (needed <= INT_MAX) + { + newsize = INT_MAX; + } + else + { + return NULL; + } + } + else + { + newsize = needed * 2; + } + + if (p->hooks.reallocate != NULL) + { + /* reallocate with realloc if available */ + newbuffer = (unsigned char*)p->hooks.reallocate(p->buffer, newsize); + if (newbuffer == NULL) + { + p->hooks.deallocate(p->buffer); + p->length = 0; + p->buffer = NULL; + + return NULL; + } + } + else + { + /* otherwise reallocate manually */ + newbuffer = (unsigned char*)p->hooks.allocate(newsize); + if (!newbuffer) + { + p->hooks.deallocate(p->buffer); + p->length = 0; + p->buffer = NULL; + + return NULL; + } + if (newbuffer) + { + memcpy(newbuffer, p->buffer, p->offset + 1); + } + p->hooks.deallocate(p->buffer); + } + p->length = newsize; + p->buffer = newbuffer; + + return newbuffer + p->offset; +} + +/* calculate the new length of the string in a printbuffer and update the offset */ +static void update_offset(printbuffer * const buffer) +{ + const unsigned char *buffer_pointer = NULL; + if ((buffer == NULL) || (buffer->buffer == NULL)) + { + return; + } + buffer_pointer = buffer->buffer + buffer->offset; + + buffer->offset += strlen((const char*)buffer_pointer); +} + +/* securely comparison of floating-point variables */ +static cJSON_bool compare_double(double a, double b) +{ + double maxVal = fabs(a) > fabs(b) ? fabs(a) : fabs(b); + return (fabs(a - b) <= maxVal * DBL_EPSILON); +} + +/* Render the number nicely from the given item into a string. */ +static cJSON_bool print_number(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output_pointer = NULL; + double d = item->valuedouble; + int length = 0; + size_t i = 0; + unsigned char number_buffer[26] = {0}; /* temporary buffer to print the number into */ + unsigned char decimal_point = get_decimal_point(); + double test = 0.0; + + if (output_buffer == NULL) + { + return false; + } + + /* This checks for NaN and Infinity */ + if (isnan(d) || isinf(d)) + { + length = sprintf((char*)number_buffer, "null"); + } + else + { + /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */ + length = sprintf((char*)number_buffer, "%1.15g", d); + + /* Check whether the original double can be recovered */ + if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || !compare_double((double)test, d)) + { + /* If not, print with 17 decimal places of precision */ + length = sprintf((char*)number_buffer, "%1.17g", d); + } + } + + /* sprintf failed or buffer overrun occurred */ + if ((length < 0) || (length > (int)(sizeof(number_buffer) - 1))) + { + return false; + } + + /* reserve appropriate space in the output */ + output_pointer = ensure(output_buffer, (size_t)length + sizeof("")); + if (output_pointer == NULL) + { + return false; + } + + /* copy the printed number to the output and replace locale + * dependent decimal point with '.' */ + for (i = 0; i < ((size_t)length); i++) + { + if (number_buffer[i] == decimal_point) + { + output_pointer[i] = '.'; + continue; + } + + output_pointer[i] = number_buffer[i]; + } + output_pointer[i] = '\0'; + + output_buffer->offset += (size_t)length; + + return true; +} + +/* parse 4 digit hexadecimal number */ +static unsigned parse_hex4(const unsigned char * const input) +{ + unsigned int h = 0; + size_t i = 0; + + for (i = 0; i < 4; i++) + { + /* parse digit */ + if ((input[i] >= '0') && (input[i] <= '9')) + { + h += (unsigned int) input[i] - '0'; + } + else if ((input[i] >= 'A') && (input[i] <= 'F')) + { + h += (unsigned int) 10 + input[i] - 'A'; + } + else if ((input[i] >= 'a') && (input[i] <= 'f')) + { + h += (unsigned int) 10 + input[i] - 'a'; + } + else /* invalid */ + { + return 0; + } + + if (i < 3) + { + /* shift left to make place for the next nibble */ + h = h << 4; + } + } + + return h; +} + +/* converts a UTF-16 literal to UTF-8 + * A literal can be one or two sequences of the form \uXXXX */ +static unsigned char utf16_literal_to_utf8(const unsigned char * const input_pointer, const unsigned char * const input_end, unsigned char **output_pointer) +{ + long unsigned int codepoint = 0; + unsigned int first_code = 0; + const unsigned char *first_sequence = input_pointer; + unsigned char utf8_length = 0; + unsigned char utf8_position = 0; + unsigned char sequence_length = 0; + unsigned char first_byte_mark = 0; + + if ((input_end - first_sequence) < 6) + { + /* input ends unexpectedly */ + goto fail; + } + + /* get the first utf16 sequence */ + first_code = parse_hex4(first_sequence + 2); + + /* check that the code is valid */ + if (((first_code >= 0xDC00) && (first_code <= 0xDFFF))) + { + goto fail; + } + + /* UTF16 surrogate pair */ + if ((first_code >= 0xD800) && (first_code <= 0xDBFF)) + { + const unsigned char *second_sequence = first_sequence + 6; + unsigned int second_code = 0; + sequence_length = 12; /* \uXXXX\uXXXX */ + + if ((input_end - second_sequence) < 6) + { + /* input ends unexpectedly */ + goto fail; + } + + if ((second_sequence[0] != '\\') || (second_sequence[1] != 'u')) + { + /* missing second half of the surrogate pair */ + goto fail; + } + + /* get the second utf16 sequence */ + second_code = parse_hex4(second_sequence + 2); + /* check that the code is valid */ + if ((second_code < 0xDC00) || (second_code > 0xDFFF)) + { + /* invalid second half of the surrogate pair */ + goto fail; + } + + + /* calculate the unicode codepoint from the surrogate pair */ + codepoint = 0x10000 + (((first_code & 0x3FF) << 10) | (second_code & 0x3FF)); + } + else + { + sequence_length = 6; /* \uXXXX */ + codepoint = first_code; + } + + /* encode as UTF-8 + * takes at maximum 4 bytes to encode: + * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ + if (codepoint < 0x80) + { + /* normal ascii, encoding 0xxxxxxx */ + utf8_length = 1; + } + else if (codepoint < 0x800) + { + /* two bytes, encoding 110xxxxx 10xxxxxx */ + utf8_length = 2; + first_byte_mark = 0xC0; /* 11000000 */ + } + else if (codepoint < 0x10000) + { + /* three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx */ + utf8_length = 3; + first_byte_mark = 0xE0; /* 11100000 */ + } + else if (codepoint <= 0x10FFFF) + { + /* four bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx */ + utf8_length = 4; + first_byte_mark = 0xF0; /* 11110000 */ + } + else + { + /* invalid unicode codepoint */ + goto fail; + } + + /* encode as utf8 */ + for (utf8_position = (unsigned char)(utf8_length - 1); utf8_position > 0; utf8_position--) + { + /* 10xxxxxx */ + (*output_pointer)[utf8_position] = (unsigned char)((codepoint | 0x80) & 0xBF); + codepoint >>= 6; + } + /* encode first byte */ + if (utf8_length > 1) + { + (*output_pointer)[0] = (unsigned char)((codepoint | first_byte_mark) & 0xFF); + } + else + { + (*output_pointer)[0] = (unsigned char)(codepoint & 0x7F); + } + + *output_pointer += utf8_length; + + return sequence_length; + +fail: + return 0; +} + +/* Parse the input text into an unescaped cinput, and populate item. */ +static cJSON_bool parse_string(cJSON * const item, parse_buffer * const input_buffer) +{ + const unsigned char *input_pointer = buffer_at_offset(input_buffer) + 1; + const unsigned char *input_end = buffer_at_offset(input_buffer) + 1; + unsigned char *output_pointer = NULL; + unsigned char *output = NULL; + + /* not a string */ + if (buffer_at_offset(input_buffer)[0] != '\"') + { + goto fail; + } + + { + /* calculate approximate size of the output (overestimate) */ + size_t allocation_length = 0; + size_t skipped_bytes = 0; + while (((size_t)(input_end - input_buffer->content) < input_buffer->length) && (*input_end != '\"')) + { + /* is escape sequence */ + if (input_end[0] == '\\') + { + if ((size_t)(input_end + 1 - input_buffer->content) >= input_buffer->length) + { + /* prevent buffer overflow when last input character is a backslash */ + goto fail; + } + skipped_bytes++; + input_end++; + } + input_end++; + } + if (((size_t)(input_end - input_buffer->content) >= input_buffer->length) || (*input_end != '\"')) + { + goto fail; /* string ended unexpectedly */ + } + + /* This is at most how much we need for the output */ + allocation_length = (size_t) (input_end - buffer_at_offset(input_buffer)) - skipped_bytes; + output = (unsigned char*)input_buffer->hooks.allocate(allocation_length + sizeof("")); + if (output == NULL) + { + goto fail; /* allocation failure */ + } + } + + output_pointer = output; + /* loop through the string literal */ + while (input_pointer < input_end) + { + if (*input_pointer != '\\') + { + *output_pointer++ = *input_pointer++; + } + /* escape sequence */ + else + { + unsigned char sequence_length = 2; + if ((input_end - input_pointer) < 1) + { + goto fail; + } + + switch (input_pointer[1]) + { + case 'b': + *output_pointer++ = '\b'; + break; + case 'f': + *output_pointer++ = '\f'; + break; + case 'n': + *output_pointer++ = '\n'; + break; + case 'r': + *output_pointer++ = '\r'; + break; + case 't': + *output_pointer++ = '\t'; + break; + case '\"': + case '\\': + case '/': + *output_pointer++ = input_pointer[1]; + break; + + /* UTF-16 literal */ + case 'u': + sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer); + if (sequence_length == 0) + { + /* failed to convert UTF16-literal to UTF-8 */ + goto fail; + } + break; + + default: + goto fail; + } + input_pointer += sequence_length; + } + } + + /* zero terminate the output */ + *output_pointer = '\0'; + + item->type = cJSON_String; + item->valuestring = (char*)output; + + input_buffer->offset = (size_t) (input_end - input_buffer->content); + input_buffer->offset++; + + return true; + +fail: + if (output != NULL) + { + input_buffer->hooks.deallocate(output); + } + + if (input_pointer != NULL) + { + input_buffer->offset = (size_t)(input_pointer - input_buffer->content); + } + + return false; +} + +/* Render the cstring provided to an escaped version that can be printed. */ +static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffer * const output_buffer) +{ + const unsigned char *input_pointer = NULL; + unsigned char *output = NULL; + unsigned char *output_pointer = NULL; + size_t output_length = 0; + /* numbers of additional characters needed for escaping */ + size_t escape_characters = 0; + + if (output_buffer == NULL) + { + return false; + } + + /* empty string */ + if (input == NULL) + { + output = ensure(output_buffer, sizeof("\"\"")); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "\"\""); + + return true; + } + + /* set "flag" to 1 if something needs to be escaped */ + for (input_pointer = input; *input_pointer; input_pointer++) + { + switch (*input_pointer) + { + case '\"': + case '\\': + case '\b': + case '\f': + case '\n': + case '\r': + case '\t': + /* one character escape sequence */ + escape_characters++; + break; + default: + if (*input_pointer < 32) + { + /* UTF-16 escape sequence uXXXX */ + escape_characters += 5; + } + break; + } + } + output_length = (size_t)(input_pointer - input) + escape_characters; + + output = ensure(output_buffer, output_length + sizeof("\"\"")); + if (output == NULL) + { + return false; + } + + /* no characters have to be escaped */ + if (escape_characters == 0) + { + output[0] = '\"'; + memcpy(output + 1, input, output_length); + output[output_length + 1] = '\"'; + output[output_length + 2] = '\0'; + + return true; + } + + output[0] = '\"'; + output_pointer = output + 1; + /* copy the string */ + for (input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++) + { + if ((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\')) + { + /* normal character, copy */ + *output_pointer = *input_pointer; + } + else + { + /* character needs to be escaped */ + *output_pointer++ = '\\'; + switch (*input_pointer) + { + case '\\': + *output_pointer = '\\'; + break; + case '\"': + *output_pointer = '\"'; + break; + case '\b': + *output_pointer = 'b'; + break; + case '\f': + *output_pointer = 'f'; + break; + case '\n': + *output_pointer = 'n'; + break; + case '\r': + *output_pointer = 'r'; + break; + case '\t': + *output_pointer = 't'; + break; + default: + /* escape and print as unicode codepoint */ + sprintf((char*)output_pointer, "u%04x", *input_pointer); + output_pointer += 4; + break; + } + } + } + output[output_length + 1] = '\"'; + output[output_length + 2] = '\0'; + + return true; +} + +/* Invoke print_string_ptr (which is useful) on an item. */ +static cJSON_bool print_string(const cJSON * const item, printbuffer * const p) +{ + return print_string_ptr((unsigned char*)item->valuestring, p); +} + +/* Predeclare these prototypes. */ +static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer); +static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer); +static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer); +static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer); +static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer); +static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer); + +/* Utility to jump whitespace and cr/lf */ +static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer) +{ + if ((buffer == NULL) || (buffer->content == NULL)) + { + return NULL; + } + + if (cannot_access_at_index(buffer, 0)) + { + return buffer; + } + + while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32)) + { + buffer->offset++; + } + + if (buffer->offset == buffer->length) + { + buffer->offset--; + } + + return buffer; +} + +/* skip the UTF-8 BOM (byte order mark) if it is at the beginning of a buffer */ +static parse_buffer *skip_utf8_bom(parse_buffer * const buffer) +{ + if ((buffer == NULL) || (buffer->content == NULL) || (buffer->offset != 0)) + { + return NULL; + } + + if (can_access_at_index(buffer, 4) && (strncmp((const char*)buffer_at_offset(buffer), "\xEF\xBB\xBF", 3) == 0)) + { + buffer->offset += 3; + } + + return buffer; +} + +CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated) +{ + size_t buffer_length; + + if (NULL == value) + { + return NULL; + } + + /* Adding null character size due to require_null_terminated. */ + buffer_length = strlen(value) + sizeof(""); + + return cJSON_ParseWithLengthOpts(value, buffer_length, return_parse_end, require_null_terminated); +} + +/* Parse an object - create a new root, and populate. */ +CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated) +{ + parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } }; + cJSON *item = NULL; + + /* reset error position */ + global_error.json = NULL; + global_error.position = 0; + + if (value == NULL || 0 == buffer_length) + { + goto fail; + } + + buffer.content = (const unsigned char*)value; + buffer.length = buffer_length; + buffer.offset = 0; + buffer.hooks = global_hooks; + + item = cJSON_New_Item(&global_hooks); + if (item == NULL) /* memory fail */ + { + goto fail; + } + + if (!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer)))) + { + /* parse failure. ep is set. */ + goto fail; + } + + /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */ + if (require_null_terminated) + { + buffer_skip_whitespace(&buffer); + if ((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0') + { + goto fail; + } + } + if (return_parse_end) + { + *return_parse_end = (const char*)buffer_at_offset(&buffer); + } + + return item; + +fail: + if (item != NULL) + { + cJSON_Delete(item); + } + + if (value != NULL) + { + error local_error; + local_error.json = (const unsigned char*)value; + local_error.position = 0; + + if (buffer.offset < buffer.length) + { + local_error.position = buffer.offset; + } + else if (buffer.length > 0) + { + local_error.position = buffer.length - 1; + } + + if (return_parse_end != NULL) + { + *return_parse_end = (const char*)local_error.json + local_error.position; + } + + global_error = local_error; + } + + return NULL; +} + +/* Default options for cJSON_Parse */ +CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value) +{ + return cJSON_ParseWithOpts(value, 0, 0); +} + +CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length) +{ + return cJSON_ParseWithLengthOpts(value, buffer_length, 0, 0); +} + +#define cjson_min(a, b) (((a) < (b)) ? (a) : (b)) + +static unsigned char *print(const cJSON * const item, cJSON_bool format, const internal_hooks * const hooks) +{ + static const size_t default_buffer_size = 256; + printbuffer buffer[1]; + unsigned char *printed = NULL; + + memset(buffer, 0, sizeof(buffer)); + + /* create buffer */ + buffer->buffer = (unsigned char*) hooks->allocate(default_buffer_size); + buffer->length = default_buffer_size; + buffer->format = format; + buffer->hooks = *hooks; + if (buffer->buffer == NULL) + { + goto fail; + } + + /* print the value */ + if (!print_value(item, buffer)) + { + goto fail; + } + update_offset(buffer); + + /* check if reallocate is available */ + if (hooks->reallocate != NULL) + { + printed = (unsigned char*) hooks->reallocate(buffer->buffer, buffer->offset + 1); + if (printed == NULL) { + goto fail; + } + buffer->buffer = NULL; + } + else /* otherwise copy the JSON over to a new buffer */ + { + printed = (unsigned char*) hooks->allocate(buffer->offset + 1); + if (printed == NULL) + { + goto fail; + } + memcpy(printed, buffer->buffer, cjson_min(buffer->length, buffer->offset + 1)); + printed[buffer->offset] = '\0'; /* just to be sure */ + + /* free the buffer */ + hooks->deallocate(buffer->buffer); + } + + return printed; + +fail: + if (buffer->buffer != NULL) + { + hooks->deallocate(buffer->buffer); + } + + if (printed != NULL) + { + hooks->deallocate(printed); + } + + return NULL; +} + +/* Render a cJSON item/entity/structure to text. */ +CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item) +{ + return (char*)print(item, true, &global_hooks); +} + +CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item) +{ + return (char*)print(item, false, &global_hooks); +} + +CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt) +{ + printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; + + if (prebuffer < 0) + { + return NULL; + } + + p.buffer = (unsigned char*)global_hooks.allocate((size_t)prebuffer); + if (!p.buffer) + { + return NULL; + } + + p.length = (size_t)prebuffer; + p.offset = 0; + p.noalloc = false; + p.format = fmt; + p.hooks = global_hooks; + + if (!print_value(item, &p)) + { + global_hooks.deallocate(p.buffer); + return NULL; + } + + return (char*)p.buffer; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format) +{ + printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; + + if ((length < 0) || (buffer == NULL)) + { + return false; + } + + p.buffer = (unsigned char*)buffer; + p.length = (size_t)length; + p.offset = 0; + p.noalloc = true; + p.format = format; + p.hooks = global_hooks; + + return print_value(item, &p); +} + +/* Parser core - when encountering text, process appropriately. */ +static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer) +{ + if ((input_buffer == NULL) || (input_buffer->content == NULL)) + { + return false; /* no input */ + } + + /* parse the different types of values */ + /* null */ + if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "null", 4) == 0)) + { + item->type = cJSON_NULL; + input_buffer->offset += 4; + return true; + } + /* false */ + if (can_read(input_buffer, 5) && (strncmp((const char*)buffer_at_offset(input_buffer), "false", 5) == 0)) + { + item->type = cJSON_False; + input_buffer->offset += 5; + return true; + } + /* true */ + if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "true", 4) == 0)) + { + item->type = cJSON_True; + item->valueint = 1; + input_buffer->offset += 4; + return true; + } + /* string */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"')) + { + return parse_string(item, input_buffer); + } + /* number */ + if (can_access_at_index(input_buffer, 0) && ((buffer_at_offset(input_buffer)[0] == '-') || ((buffer_at_offset(input_buffer)[0] >= '0') && (buffer_at_offset(input_buffer)[0] <= '9')))) + { + return parse_number(item, input_buffer); + } + /* array */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '[')) + { + return parse_array(item, input_buffer); + } + /* object */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{')) + { + return parse_object(item, input_buffer); + } + + return false; +} + +/* Render a value to text. */ +static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output = NULL; + + if ((item == NULL) || (output_buffer == NULL)) + { + return false; + } + + switch ((item->type) & 0xFF) + { + case cJSON_NULL: + output = ensure(output_buffer, 5); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "null"); + return true; + + case cJSON_False: + output = ensure(output_buffer, 6); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "false"); + return true; + + case cJSON_True: + output = ensure(output_buffer, 5); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "true"); + return true; + + case cJSON_Number: + return print_number(item, output_buffer); + + case cJSON_Raw: + { + size_t raw_length = 0; + if (item->valuestring == NULL) + { + return false; + } + + raw_length = strlen(item->valuestring) + sizeof(""); + output = ensure(output_buffer, raw_length); + if (output == NULL) + { + return false; + } + memcpy(output, item->valuestring, raw_length); + return true; + } + + case cJSON_String: + return print_string(item, output_buffer); + + case cJSON_Array: + return print_array(item, output_buffer); + + case cJSON_Object: + return print_object(item, output_buffer); + + default: + return false; + } +} + +/* Build an array from input text. */ +static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer) +{ + cJSON *head = NULL; /* head of the linked list */ + cJSON *current_item = NULL; + + if (input_buffer->depth >= CJSON_NESTING_LIMIT) + { + return false; /* to deeply nested */ + } + input_buffer->depth++; + + if (buffer_at_offset(input_buffer)[0] != '[') + { + /* not an array */ + goto fail; + } + + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ']')) + { + /* empty array */ + goto success; + } + + /* check if we skipped to the end of the buffer */ + if (cannot_access_at_index(input_buffer, 0)) + { + input_buffer->offset--; + goto fail; + } + + /* step back to character in front of the first element */ + input_buffer->offset--; + /* loop through the comma separated array elements */ + do + { + /* allocate next item */ + cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); + if (new_item == NULL) + { + goto fail; /* allocation failure */ + } + + /* attach next item to list */ + if (head == NULL) + { + /* start the linked list */ + current_item = head = new_item; + } + else + { + /* add to the end and advance */ + current_item->next = new_item; + new_item->prev = current_item; + current_item = new_item; + } + + /* parse next value */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_value(current_item, input_buffer)) + { + goto fail; /* failed to parse value */ + } + buffer_skip_whitespace(input_buffer); + } + while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); + + if (cannot_access_at_index(input_buffer, 0) || buffer_at_offset(input_buffer)[0] != ']') + { + goto fail; /* expected end of array */ + } + +success: + input_buffer->depth--; + + item->type = cJSON_Array; + item->child = head; + + input_buffer->offset++; + + return true; + +fail: + if (head != NULL) + { + cJSON_Delete(head); + } + + return false; +} + +/* Render an array to text */ +static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output_pointer = NULL; + size_t length = 0; + cJSON *current_element = item->child; + + if (output_buffer == NULL) + { + return false; + } + + /* Compose the output array. */ + /* opening square bracket */ + output_pointer = ensure(output_buffer, 1); + if (output_pointer == NULL) + { + return false; + } + + *output_pointer = '['; + output_buffer->offset++; + output_buffer->depth++; + + while (current_element != NULL) + { + if (!print_value(current_element, output_buffer)) + { + return false; + } + update_offset(output_buffer); + if (current_element->next) + { + length = (size_t) (output_buffer->format ? 2 : 1); + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) + { + return false; + } + *output_pointer++ = ','; + if(output_buffer->format) + { + *output_pointer++ = ' '; + } + *output_pointer = '\0'; + output_buffer->offset += length; + } + current_element = current_element->next; + } + + output_pointer = ensure(output_buffer, 2); + if (output_pointer == NULL) + { + return false; + } + *output_pointer++ = ']'; + *output_pointer = '\0'; + output_buffer->depth--; + + return true; +} + +/* Build an object from the text. */ +static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer) +{ + cJSON *head = NULL; /* linked list head */ + cJSON *current_item = NULL; + + if (input_buffer->depth >= CJSON_NESTING_LIMIT) + { + return false; /* to deeply nested */ + } + input_buffer->depth++; + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '{')) + { + goto fail; /* not an object */ + } + + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '}')) + { + goto success; /* empty object */ + } + + /* check if we skipped to the end of the buffer */ + if (cannot_access_at_index(input_buffer, 0)) + { + input_buffer->offset--; + goto fail; + } + + /* step back to character in front of the first element */ + input_buffer->offset--; + /* loop through the comma separated array elements */ + do + { + /* allocate next item */ + cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); + if (new_item == NULL) + { + goto fail; /* allocation failure */ + } + + /* attach next item to list */ + if (head == NULL) + { + /* start the linked list */ + current_item = head = new_item; + } + else + { + /* add to the end and advance */ + current_item->next = new_item; + new_item->prev = current_item; + current_item = new_item; + } + + /* parse the name of the child */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_string(current_item, input_buffer)) + { + goto fail; /* failed to parse name */ + } + buffer_skip_whitespace(input_buffer); + + /* swap valuestring and string, because we parsed the name */ + current_item->string = current_item->valuestring; + current_item->valuestring = NULL; + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != ':')) + { + goto fail; /* invalid object */ + } + + /* parse the value */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_value(current_item, input_buffer)) + { + goto fail; /* failed to parse value */ + } + buffer_skip_whitespace(input_buffer); + } + while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '}')) + { + goto fail; /* expected end of object */ + } + +success: + input_buffer->depth--; + + item->type = cJSON_Object; + item->child = head; + + input_buffer->offset++; + return true; + +fail: + if (head != NULL) + { + cJSON_Delete(head); + } + + return false; +} + +/* Render an object to text. */ +static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output_pointer = NULL; + size_t length = 0; + cJSON *current_item = item->child; + + if (output_buffer == NULL) + { + return false; + } + + /* Compose the output: */ + length = (size_t) (output_buffer->format ? 2 : 1); /* fmt: {\n */ + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) + { + return false; + } + + *output_pointer++ = '{'; + output_buffer->depth++; + if (output_buffer->format) + { + *output_pointer++ = '\n'; + } + output_buffer->offset += length; + + while (current_item) + { + if (output_buffer->format) + { + size_t i; + output_pointer = ensure(output_buffer, output_buffer->depth); + if (output_pointer == NULL) + { + return false; + } + for (i = 0; i < output_buffer->depth; i++) + { + *output_pointer++ = '\t'; + } + output_buffer->offset += output_buffer->depth; + } + + /* print key */ + if (!print_string_ptr((unsigned char*)current_item->string, output_buffer)) + { + return false; + } + update_offset(output_buffer); + + length = (size_t) (output_buffer->format ? 2 : 1); + output_pointer = ensure(output_buffer, length); + if (output_pointer == NULL) + { + return false; + } + *output_pointer++ = ':'; + if (output_buffer->format) + { + *output_pointer++ = '\t'; + } + output_buffer->offset += length; + + /* print value */ + if (!print_value(current_item, output_buffer)) + { + return false; + } + update_offset(output_buffer); + + /* print comma if not last */ + length = ((size_t)(output_buffer->format ? 1 : 0) + (size_t)(current_item->next ? 1 : 0)); + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) + { + return false; + } + if (current_item->next) + { + *output_pointer++ = ','; + } + + if (output_buffer->format) + { + *output_pointer++ = '\n'; + } + *output_pointer = '\0'; + output_buffer->offset += length; + + current_item = current_item->next; + } + + output_pointer = ensure(output_buffer, output_buffer->format ? (output_buffer->depth + 1) : 2); + if (output_pointer == NULL) + { + return false; + } + if (output_buffer->format) + { + size_t i; + for (i = 0; i < (output_buffer->depth - 1); i++) + { + *output_pointer++ = '\t'; + } + } + *output_pointer++ = '}'; + *output_pointer = '\0'; + output_buffer->depth--; + + return true; +} + +/* Get Array size/item / object item. */ +CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array) +{ + cJSON *child = NULL; + size_t size = 0; + + if (array == NULL) + { + return 0; + } + + child = array->child; + + while(child != NULL) + { + size++; + child = child->next; + } + + /* FIXME: Can overflow here. Cannot be fixed without breaking the API */ + + return (int)size; +} + +static cJSON* get_array_item(const cJSON *array, size_t index) +{ + cJSON *current_child = NULL; + + if (array == NULL) + { + return NULL; + } + + current_child = array->child; + while ((current_child != NULL) && (index > 0)) + { + index--; + current_child = current_child->next; + } + + return current_child; +} + +CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index) +{ + if (index < 0) + { + return NULL; + } + + return get_array_item(array, (size_t)index); +} + +static cJSON *get_object_item(const cJSON * const object, const char * const name, const cJSON_bool case_sensitive) +{ + cJSON *current_element = NULL; + + if ((object == NULL) || (name == NULL)) + { + return NULL; + } + + current_element = object->child; + if (case_sensitive) + { + while ((current_element != NULL) && (current_element->string != NULL) && (strcmp(name, current_element->string) != 0)) + { + current_element = current_element->next; + } + } + else + { + while ((current_element != NULL) && (case_insensitive_strcmp((const unsigned char*)name, (const unsigned char*)(current_element->string)) != 0)) + { + current_element = current_element->next; + } + } + + if ((current_element == NULL) || (current_element->string == NULL)) { + return NULL; + } + + return current_element; +} + +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string) +{ + return get_object_item(object, string, false); +} + +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string) +{ + return get_object_item(object, string, true); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string) +{ + return cJSON_GetObjectItem(object, string) ? 1 : 0; +} + +/* Utility for array list handling. */ +static void suffix_object(cJSON *prev, cJSON *item) +{ + prev->next = item; + item->prev = prev; +} + +/* Utility for handling references. */ +static cJSON *create_reference(const cJSON *item, const internal_hooks * const hooks) +{ + cJSON *reference = NULL; + if (item == NULL) + { + return NULL; + } + + reference = cJSON_New_Item(hooks); + if (reference == NULL) + { + return NULL; + } + + memcpy(reference, item, sizeof(cJSON)); + reference->string = NULL; + reference->type |= cJSON_IsReference; + reference->next = reference->prev = NULL; + return reference; +} + +static cJSON_bool add_item_to_array(cJSON *array, cJSON *item) +{ + cJSON *child = NULL; + + if ((item == NULL) || (array == NULL) || (array == item)) + { + return false; + } + + child = array->child; + /* + * To find the last item in array quickly, we use prev in array + */ + if (child == NULL) + { + /* list is empty, start new one */ + array->child = item; + item->prev = item; + item->next = NULL; + } + else + { + /* append to the end */ + if (child->prev) + { + suffix_object(child->prev, item); + array->child->prev = item; + } + else + { + while (child->next) + { + child = child->next; + } + suffix_object(child, item); + array->child->prev = item; + } + } + + return true; +} + +/* Add item to array/object. */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item) +{ + return add_item_to_array(array, item); +} + +#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) + #pragma GCC diagnostic push +#endif +#ifdef __GNUC__ +#pragma GCC diagnostic ignored "-Wcast-qual" +#endif +/* helper function to cast away const */ +static void* cast_away_const(const void* string) +{ + return (void*)string; +} +#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) + #pragma GCC diagnostic pop +#endif + + +static cJSON_bool add_item_to_object(cJSON * const object, const char * const string, cJSON * const item, const internal_hooks * const hooks, const cJSON_bool constant_key) +{ + char *new_key = NULL; + int new_type = cJSON_Invalid; + + if ((object == NULL) || (string == NULL) || (item == NULL) || (object == item)) + { + return false; + } + + if (constant_key) + { + new_key = (char*)cast_away_const(string); + new_type = item->type | cJSON_StringIsConst; + } + else + { + new_key = (char*)cJSON_strdup((const unsigned char*)string, hooks); + if (new_key == NULL) + { + return false; + } + + new_type = item->type & ~cJSON_StringIsConst; + } + + if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) + { + hooks->deallocate(item->string); + } + + item->string = new_key; + item->type = new_type; + + return add_item_to_array(object, item); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item) +{ + return add_item_to_object(object, string, item, &global_hooks, false); +} + +/* Add an item to an object with constant string as key */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item) +{ + return add_item_to_object(object, string, item, &global_hooks, true); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) +{ + if (array == NULL) + { + return false; + } + + return add_item_to_array(array, create_reference(item, &global_hooks)); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item) +{ + if ((object == NULL) || (string == NULL)) + { + return false; + } + + return add_item_to_object(object, string, create_reference(item, &global_hooks), &global_hooks, false); +} + +CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name) +{ + cJSON *null = cJSON_CreateNull(); + if (add_item_to_object(object, name, null, &global_hooks, false)) + { + return null; + } + + cJSON_Delete(null); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name) +{ + cJSON *true_item = cJSON_CreateTrue(); + if (add_item_to_object(object, name, true_item, &global_hooks, false)) + { + return true_item; + } + + cJSON_Delete(true_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name) +{ + cJSON *false_item = cJSON_CreateFalse(); + if (add_item_to_object(object, name, false_item, &global_hooks, false)) + { + return false_item; + } + + cJSON_Delete(false_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean) +{ + cJSON *bool_item = cJSON_CreateBool(boolean); + if (add_item_to_object(object, name, bool_item, &global_hooks, false)) + { + return bool_item; + } + + cJSON_Delete(bool_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number) +{ + cJSON *number_item = cJSON_CreateNumber(number); + if (add_item_to_object(object, name, number_item, &global_hooks, false)) + { + return number_item; + } + + cJSON_Delete(number_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string) +{ + cJSON *string_item = cJSON_CreateString(string); + if (add_item_to_object(object, name, string_item, &global_hooks, false)) + { + return string_item; + } + + cJSON_Delete(string_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw) +{ + cJSON *raw_item = cJSON_CreateRaw(raw); + if (add_item_to_object(object, name, raw_item, &global_hooks, false)) + { + return raw_item; + } + + cJSON_Delete(raw_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name) +{ + cJSON *object_item = cJSON_CreateObject(); + if (add_item_to_object(object, name, object_item, &global_hooks, false)) + { + return object_item; + } + + cJSON_Delete(object_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name) +{ + cJSON *array = cJSON_CreateArray(); + if (add_item_to_object(object, name, array, &global_hooks, false)) + { + return array; + } + + cJSON_Delete(array); + return NULL; +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item) +{ + if ((parent == NULL) || (item == NULL)) + { + return NULL; + } + + if (item != parent->child) + { + /* not the first element */ + item->prev->next = item->next; + } + if (item->next != NULL) + { + /* not the last element */ + item->next->prev = item->prev; + } + + if (item == parent->child) + { + /* first element */ + parent->child = item->next; + } + /* make sure the detached item doesn't point anywhere anymore */ + item->prev = NULL; + item->next = NULL; + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which) +{ + if (which < 0) + { + return NULL; + } + + return cJSON_DetachItemViaPointer(array, get_array_item(array, (size_t)which)); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which) +{ + cJSON_Delete(cJSON_DetachItemFromArray(array, which)); +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string) +{ + cJSON *to_detach = cJSON_GetObjectItem(object, string); + + return cJSON_DetachItemViaPointer(object, to_detach); +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string) +{ + cJSON *to_detach = cJSON_GetObjectItemCaseSensitive(object, string); + + return cJSON_DetachItemViaPointer(object, to_detach); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string) +{ + cJSON_Delete(cJSON_DetachItemFromObject(object, string)); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string) +{ + cJSON_Delete(cJSON_DetachItemFromObjectCaseSensitive(object, string)); +} + +/* Replace array/object items with new ones. */ +CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem) +{ + cJSON *after_inserted = NULL; + + if (which < 0) + { + return false; + } + + after_inserted = get_array_item(array, (size_t)which); + if (after_inserted == NULL) + { + return add_item_to_array(array, newitem); + } + + newitem->next = after_inserted; + newitem->prev = after_inserted->prev; + after_inserted->prev = newitem; + if (after_inserted == array->child) + { + array->child = newitem; + } + else + { + newitem->prev->next = newitem; + } + return true; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement) +{ + if ((parent == NULL) || (replacement == NULL) || (item == NULL)) + { + return false; + } + + if (replacement == item) + { + return true; + } + + replacement->next = item->next; + replacement->prev = item->prev; + + if (replacement->next != NULL) + { + replacement->next->prev = replacement; + } + if (parent->child == item) + { + parent->child = replacement; + } + else + { /* + * To find the last item in array quickly, we use prev in array. + * We can't modify the last item's next pointer where this item was the parent's child + */ + if (replacement->prev != NULL) + { + replacement->prev->next = replacement; + } + } + + item->next = NULL; + item->prev = NULL; + cJSON_Delete(item); + + return true; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem) +{ + if (which < 0) + { + return false; + } + + return cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem); +} + +static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSON *replacement, cJSON_bool case_sensitive) +{ + if ((replacement == NULL) || (string == NULL)) + { + return false; + } + + /* replace the name in the replacement */ + if (!(replacement->type & cJSON_StringIsConst) && (replacement->string != NULL)) + { + cJSON_free(replacement->string); + } + replacement->string = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); + replacement->type &= ~cJSON_StringIsConst; + + return cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, case_sensitive), replacement); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem) +{ + return replace_item_in_object(object, string, newitem, false); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem) +{ + return replace_item_in_object(object, string, newitem, true); +} + +/* Create basic types: */ +CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_NULL; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_True; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_False; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = boolean ? cJSON_True : cJSON_False; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_Number; + item->valuedouble = num; + + /* use saturation in case of overflow */ + if (num >= INT_MAX) + { + item->valueint = INT_MAX; + } + else if (num <= (double)INT_MIN) + { + item->valueint = INT_MIN; + } + else + { + item->valueint = (int)num; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_String; + item->valuestring = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); + if(!item->valuestring) + { + cJSON_Delete(item); + return NULL; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if (item != NULL) + { + item->type = cJSON_String | cJSON_IsReference; + item->valuestring = (char*)cast_away_const(string); + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if (item != NULL) { + item->type = cJSON_Object | cJSON_IsReference; + item->child = (cJSON*)cast_away_const(child); + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child) { + cJSON *item = cJSON_New_Item(&global_hooks); + if (item != NULL) { + item->type = cJSON_Array | cJSON_IsReference; + item->child = (cJSON*)cast_away_const(child); + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_Raw; + item->valuestring = (char*)cJSON_strdup((const unsigned char*)raw, &global_hooks); + if(!item->valuestring) + { + cJSON_Delete(item); + return NULL; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type=cJSON_Array; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if (item) + { + item->type = cJSON_Object; + } + + return item; +} + +/* Create Arrays: */ +CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (numbers == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + for(i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber(numbers[i]); + if (!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + p = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (numbers == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for(i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber((double)numbers[i]); + if(!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + p = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (numbers == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for(i = 0;a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber(numbers[i]); + if(!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + p = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (strings == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for (i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateString(strings[i]); + if(!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p,n); + } + p = n; + } + + return a; +} + +/* Duplication */ +CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse) +{ + cJSON *newitem = NULL; + cJSON *child = NULL; + cJSON *next = NULL; + cJSON *newchild = NULL; + + /* Bail on bad ptr */ + if (!item) + { + goto fail; + } + /* Create new item */ + newitem = cJSON_New_Item(&global_hooks); + if (!newitem) + { + goto fail; + } + /* Copy over all vars */ + newitem->type = item->type & (~cJSON_IsReference); + newitem->valueint = item->valueint; + newitem->valuedouble = item->valuedouble; + if (item->valuestring) + { + newitem->valuestring = (char*)cJSON_strdup((unsigned char*)item->valuestring, &global_hooks); + if (!newitem->valuestring) + { + goto fail; + } + } + if (item->string) + { + newitem->string = (item->type&cJSON_StringIsConst) ? item->string : (char*)cJSON_strdup((unsigned char*)item->string, &global_hooks); + if (!newitem->string) + { + goto fail; + } + } + /* If non-recursive, then we're done! */ + if (!recurse) + { + return newitem; + } + /* Walk the ->next chain for the child. */ + child = item->child; + while (child != NULL) + { + newchild = cJSON_Duplicate(child, true); /* Duplicate (with recurse) each item in the ->next chain */ + if (!newchild) + { + goto fail; + } + if (next != NULL) + { + /* If newitem->child already set, then crosswire ->prev and ->next and move on */ + next->next = newchild; + newchild->prev = next; + next = newchild; + } + else + { + /* Set newitem->child and move to it */ + newitem->child = newchild; + next = newchild; + } + child = child->next; + } + + return newitem; + +fail: + if (newitem != NULL) + { + cJSON_Delete(newitem); + } + + return NULL; +} + +static void skip_oneline_comment(char **input) +{ + *input += static_strlen("//"); + + for (; (*input)[0] != '\0'; ++(*input)) + { + if ((*input)[0] == '\n') { + *input += static_strlen("\n"); + return; + } + } +} + +static void skip_multiline_comment(char **input) +{ + *input += static_strlen("/*"); + + for (; (*input)[0] != '\0'; ++(*input)) + { + if (((*input)[0] == '*') && ((*input)[1] == '/')) + { + *input += static_strlen("*/"); + return; + } + } +} + +static void minify_string(char **input, char **output) { + (*output)[0] = (*input)[0]; + *input += static_strlen("\""); + *output += static_strlen("\""); + + + for (; (*input)[0] != '\0'; (void)++(*input), ++(*output)) { + (*output)[0] = (*input)[0]; + + if ((*input)[0] == '\"') { + (*output)[0] = '\"'; + *input += static_strlen("\""); + *output += static_strlen("\""); + return; + } else if (((*input)[0] == '\\') && ((*input)[1] == '\"')) { + (*output)[1] = (*input)[1]; + *input += static_strlen("\""); + *output += static_strlen("\""); + } + } +} + +CJSON_PUBLIC(void) cJSON_Minify(char *json) +{ + char *into = json; + + if (json == NULL) + { + return; + } + + while (json[0] != '\0') + { + switch (json[0]) + { + case ' ': + case '\t': + case '\r': + case '\n': + json++; + break; + + case '/': + if (json[1] == '/') + { + skip_oneline_comment(&json); + } + else if (json[1] == '*') + { + skip_multiline_comment(&json); + } else { + json++; + } + break; + + case '\"': + minify_string(&json, (char**)&into); + break; + + default: + into[0] = json[0]; + json++; + into++; + } + } + + /* and null-terminate. */ + *into = '\0'; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Invalid; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_False; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xff) == cJSON_True; +} + + +CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & (cJSON_True | cJSON_False)) != 0; +} +CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_NULL; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Number; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_String; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Array; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Object; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Raw; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive) +{ + if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF)) || cJSON_IsInvalid(a)) + { + return false; + } + + /* check if type is valid */ + switch (a->type & 0xFF) + { + case cJSON_False: + case cJSON_True: + case cJSON_NULL: + case cJSON_Number: + case cJSON_String: + case cJSON_Raw: + case cJSON_Array: + case cJSON_Object: + break; + + default: + return false; + } + + /* identical objects are equal */ + if (a == b) + { + return true; + } + + switch (a->type & 0xFF) + { + /* in these cases and equal type is enough */ + case cJSON_False: + case cJSON_True: + case cJSON_NULL: + return true; + + case cJSON_Number: + if (compare_double(a->valuedouble, b->valuedouble)) + { + return true; + } + return false; + + case cJSON_String: + case cJSON_Raw: + if ((a->valuestring == NULL) || (b->valuestring == NULL)) + { + return false; + } + if (strcmp(a->valuestring, b->valuestring) == 0) + { + return true; + } + + return false; + + case cJSON_Array: + { + cJSON *a_element = a->child; + cJSON *b_element = b->child; + + for (; (a_element != NULL) && (b_element != NULL);) + { + if (!cJSON_Compare(a_element, b_element, case_sensitive)) + { + return false; + } + + a_element = a_element->next; + b_element = b_element->next; + } + + /* one of the arrays is longer than the other */ + if (a_element != b_element) { + return false; + } + + return true; + } + + case cJSON_Object: + { + cJSON *a_element = NULL; + cJSON *b_element = NULL; + cJSON_ArrayForEach(a_element, a) + { + /* TODO This has O(n^2) runtime, which is horrible! */ + b_element = get_object_item(b, a_element->string, case_sensitive); + if (b_element == NULL) + { + return false; + } + + if (!cJSON_Compare(a_element, b_element, case_sensitive)) + { + return false; + } + } + + /* doing this twice, once on a and b to prevent true comparison if a subset of b + * TODO: Do this the proper way, this is just a fix for now */ + cJSON_ArrayForEach(b_element, b) + { + a_element = get_object_item(a, b_element->string, case_sensitive); + if (a_element == NULL) + { + return false; + } + + if (!cJSON_Compare(b_element, a_element, case_sensitive)) + { + return false; + } + } + + return true; + } + + default: + return false; + } +} + +CJSON_PUBLIC(void *) cJSON_malloc(size_t size) +{ + return global_hooks.allocate(size); +} + +CJSON_PUBLIC(void) cJSON_free(void *object) +{ + global_hooks.deallocate(object); +} diff --git a/firmware/echoear-platform-agent/components/volc_conv_ai/src/util/cJSON/cJSON.h b/firmware/echoear-platform-agent/components/volc_conv_ai/src/util/cJSON/cJSON.h new file mode 100644 index 0000000..0c6c8e0 --- /dev/null +++ b/firmware/echoear-platform-agent/components/volc_conv_ai/src/util/cJSON/cJSON.h @@ -0,0 +1,293 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#ifndef cJSON__h +#define cJSON__h + +#ifdef __cplusplus +extern "C" +{ +#endif + +#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32)) +#define __WINDOWS__ +#endif + +#ifdef __WINDOWS__ + +/* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention. For windows you have 3 define options: + +CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols +CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default) +CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol + +For *nix builds that support visibility attribute, you can define similar behavior by + +setting default visibility to hidden by adding +-fvisibility=hidden (for gcc) +or +-xldscope=hidden (for sun cc) +to CFLAGS + +then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does + +*/ + +#define CJSON_CDECL __cdecl +#define CJSON_STDCALL __stdcall + +/* export symbols by default, this is necessary for copy pasting the C and header file */ +#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS) +#define CJSON_EXPORT_SYMBOLS +#endif + +#if defined(CJSON_HIDE_SYMBOLS) +#define CJSON_PUBLIC(type) type CJSON_STDCALL +#elif defined(CJSON_EXPORT_SYMBOLS) +#define CJSON_PUBLIC(type) __declspec(dllexport) type CJSON_STDCALL +#elif defined(CJSON_IMPORT_SYMBOLS) +#define CJSON_PUBLIC(type) __declspec(dllimport) type CJSON_STDCALL +#endif +#else /* !__WINDOWS__ */ +#define CJSON_CDECL +#define CJSON_STDCALL + +#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY) +#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type +#else +#define CJSON_PUBLIC(type) type +#endif +#endif + +/* project version */ +#define CJSON_VERSION_MAJOR 1 +#define CJSON_VERSION_MINOR 7 +#define CJSON_VERSION_PATCH 13 + +#include + +/* cJSON Types: */ +#define cJSON_Invalid (0) +#define cJSON_False (1 << 0) +#define cJSON_True (1 << 1) +#define cJSON_NULL (1 << 2) +#define cJSON_Number (1 << 3) +#define cJSON_String (1 << 4) +#define cJSON_Array (1 << 5) +#define cJSON_Object (1 << 6) +#define cJSON_Raw (1 << 7) /* raw json */ + +#define cJSON_IsReference 256 +#define cJSON_StringIsConst 512 + +/* The cJSON structure: */ +typedef struct cJSON +{ + /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ + struct cJSON *next; + struct cJSON *prev; + /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ + struct cJSON *child; + + /* The type of the item, as above. */ + int type; + + /* The item's string, if type==cJSON_String and type == cJSON_Raw */ + char *valuestring; + /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */ + int valueint; + /* The item's number, if type==cJSON_Number */ + double valuedouble; + + /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ + char *string; +} cJSON; + +typedef struct cJSON_Hooks +{ + /* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */ + void *(CJSON_CDECL *malloc_fn)(size_t sz); + void (CJSON_CDECL *free_fn)(void *ptr); +} cJSON_Hooks; + +typedef int cJSON_bool; + +/* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them. + * This is to prevent stack overflows. */ +#ifndef CJSON_NESTING_LIMIT +#define CJSON_NESTING_LIMIT 1000 +#endif + +/* returns the version of cJSON as a string */ +CJSON_PUBLIC(const char*) cJSON_Version(void); + +/* Supply malloc, realloc and free functions to cJSON */ +CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks); + +/* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */ +/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */ +CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value); +CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length); +/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */ +/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */ +CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated); +CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated); + +/* Render a cJSON entity to text for transfer/storage. */ +CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item); +/* Render a cJSON entity to text for transfer/storage without any formatting. */ +CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item); +/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */ +CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt); +/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */ +/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */ +CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format); +/* Delete a cJSON entity and all subentities. */ +CJSON_PUBLIC(void) cJSON_Delete(cJSON *item); + +/* Returns the number of items in an array (or object). */ +CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array); +/* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */ +CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index); +/* Get item "string" from object. Case insensitive. */ +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string); +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string); +CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string); +/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */ +CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void); + +/* Check item type and return its value */ +CJSON_PUBLIC(char *) cJSON_GetStringValue(cJSON *item); +CJSON_PUBLIC(double) cJSON_GetNumberValue(cJSON *item); + +/* These functions check the type of an item */ +CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item); + +/* These calls create a cJSON item of the appropriate type. */ +CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean); +CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num); +CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string); +/* raw json */ +CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw); +CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void); + +/* Create a string where valuestring references a string so + * it will not be freed by cJSON_Delete */ +CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string); +/* Create an object/array that only references it's elements so + * they will not be freed by cJSON_Delete */ +CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child); +CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child); + +/* These utilities create an Array of count items. + * The parameter count cannot be greater than the number of elements in the number array, otherwise array access will be out of bounds.*/ +CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count); + +/* Append item to the specified array/object. */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item); +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item); +/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object. + * WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before + * writing to `item->string` */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item); +/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item); + +/* Remove/Detach items from Arrays/Objects. */ +CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which); +CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string); +CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string); +CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string); + +/* Update array items. */ +CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */ +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement); +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem); +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem); + +/* Duplicate a cJSON item */ +CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse); +/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will + * need to be released. With recurse!=0, it will duplicate any children connected to the item. + * The item->next and ->prev pointers are always zero on return from Duplicate. */ +/* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal. + * case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */ +CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive); + +/* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from strings. + * The input pointer json cannot point to a read-only address area, such as a string constant, + * but should point to a readable and writable adress area. */ +CJSON_PUBLIC(void) cJSON_Minify(char *json); + +/* Helper functions for creating and adding items to an object at the same time. + * They return the added item or NULL on failure. */ +CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean); +CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number); +CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string); +CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw); +CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name); + +/* When assigning an integer value, it needs to be propagated to valuedouble too. */ +#define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number)) +/* helper for the cJSON_SetNumberValue macro */ +CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number); +#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number)) +/* Change the valuestring of a cJSON_String object, only takes effect when type of object is cJSON_String */ +CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring); + +/* Macro for iterating over an array or object */ +#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next) + +/* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */ +CJSON_PUBLIC(void *) cJSON_malloc(size_t size); +CJSON_PUBLIC(void) cJSON_free(void *object); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/firmware/echoear-platform-agent/components/volc_conv_ai/src/util/volc_auth.c b/firmware/echoear-platform-agent/components/volc_conv_ai/src/util/volc_auth.c new file mode 100644 index 0000000..0469212 --- /dev/null +++ b/firmware/echoear-platform-agent/components/volc_conv_ai/src/util/volc_auth.c @@ -0,0 +1,118 @@ +// Copyright (2025) Beijing Volcano Engine Technology Ltd. +// SPDX-License-Identifier: Apache-2.0 + +#include "util/volc_auth.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "volc_osal.h" +#include "util/volc_base64.h" +#include "util/volc_list.h" +#include "util/volc_log.h" + +typedef bool(predicate_func)(unsigned char c); + +static bool _url_trim_pred(unsigned char ch) { + if (ch < 32) { + return true; + } + switch (ch) { + case 0x20: /* ' ' - space */ + case 0x09: /* '\t' - horizontal tab */ + case 0x0A: /* '\n' - line feed */ + case 0x0B: /* '\v' - vertical tab */ + case 0x0C: /* '\f' - form feed */ + case 0x0D: /* '\r' - carriage return */ + case '\a': /* '\r' - carriage return */ + return true; + default: + return false; + } +} +static int _right_trim_pred(unsigned char* str, predicate_func* pred) { + int len = strlen((const char*)str); + + while(len > 0 && pred(str[len - 1])) { + --len; + } + + str[len] = '\0'; + return len; +} + +static int __aes_cbc(bool encrypt, const unsigned char* key, unsigned char* iv, const unsigned char* input, uint32_t ilen, int key_bits, unsigned char* output) { + int mode = encrypt ? MBEDTLS_AES_ENCRYPT : MBEDTLS_AES_DECRYPT; + mbedtls_aes_context aes_ctx; + mbedtls_aes_init(&aes_ctx); + mbedtls_aes_setkey_dec(&aes_ctx, key, key_bits); + mbedtls_aes_crypt_cbc(&aes_ctx, mode, ilen, iv, (const unsigned char *)input, output); + mbedtls_aes_free(&aes_ctx); + + return 0; +} + +#define AES_KEY_SIZE 16 // AES key size in bytes +#define AES_KEY_SIZE_24 24 +char* volc_aes_decode(const char* secret, const char* input, const bool partial_secret) { + size_t decoded_len = 0; + size_t secret_len = 0; + int key_bits = 0; + char* key = NULL; + unsigned char iv[16] = {0}; + unsigned char* decoded_payload = NULL; + if (input == NULL || strlen(input) == 0) { + LOGE("Input is NULL or empty"); + return NULL; + } + + if (partial_secret) { + // If partial secret is used, we only use the first 8 bytes of the key and iv + key = (char *)volc_osal_calloc(AES_KEY_SIZE, 1); + memcpy(key, secret, AES_KEY_SIZE); + key_bits = 128; // AES-128 + } else { + secret_len = strlen(secret); + if (secret_len < AES_KEY_SIZE_24) { + LOGE("Secret length is less than 24, secret: %s", secret); + return NULL; + } + key = (char *)volc_osal_calloc(AES_KEY_SIZE_24, 1); // +1 for null terminator + memcpy(key, secret, AES_KEY_SIZE_24); + key_bits = 192; // AES-192 + } + + decoded_len = volc_base64_decoded_length((const uint8_t*)input, strlen(input)); + decoded_payload = (unsigned char *)volc_osal_calloc(decoded_len + 1, 1); + if (!decoded_payload) { + LOGE("Failed to allocate memory for decoded payload"); + return NULL; + } + volc_base64_decode((unsigned char *)decoded_payload, decoded_len, &decoded_len, (const unsigned char *)input, strlen(input)); + LOGV("decoded payload: %s, len: %zu", decoded_payload, decoded_len); + + uint8_t *output = volc_osal_calloc(decoded_len + 16, 1); // +1 for null terminator + + memcpy(iv, secret, AES_KEY_SIZE); // Use the same secret for IV + __aes_cbc(false, (const unsigned char *)key, (unsigned char *)iv, (const unsigned char *)decoded_payload, decoded_len, key_bits, output); + + _right_trim_pred(output, _url_trim_pred); + + LOGV("trimmed decrypted payload: %s, len: %d", output, (int)strlen((const char*)output)); + HAL_SAFE_FREE(decoded_payload); + HAL_SAFE_FREE(key); + return (char *)output; +} + +void volc_sha256_hmac(const unsigned char* key, int keylen, const unsigned char* input, int ilen, unsigned char* output, int* plen) { + mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), key, keylen, input, ilen, output); + *(plen) = mbedtls_md_get_size(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256)); +} diff --git a/firmware/echoear-platform-agent/components/volc_conv_ai/src/util/volc_auth.h b/firmware/echoear-platform-agent/components/volc_conv_ai/src/util/volc_auth.h new file mode 100644 index 0000000..b444bf2 --- /dev/null +++ b/firmware/echoear-platform-agent/components/volc_conv_ai/src/util/volc_auth.h @@ -0,0 +1,19 @@ +// Copyright (2025) Beijing Volcano Engine Technology Ltd. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef __CONV_AI_SRC_UTIL_VOLC_AUTH_H__ +#define __CONV_AI_SRC_UTIL_VOLC_AUTH_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +char* volc_aes_decode(const char* secret, const char* input, const bool partial_secret); +void volc_sha256_hmac(const unsigned char* key, int keylen, const unsigned char* input, int ilen, unsigned char* output, int* plen); + +#ifdef __cplusplus +} +#endif +#endif /* __CONV_AI_SRC_UTIL_VOLC_AUTH_H__ */ diff --git a/firmware/echoear-platform-agent/components/volc_conv_ai/src/util/volc_base64.c b/firmware/echoear-platform-agent/components/volc_conv_ai/src/util/volc_base64.c new file mode 100644 index 0000000..aa56695 --- /dev/null +++ b/firmware/echoear-platform-agent/components/volc_conv_ai/src/util/volc_base64.c @@ -0,0 +1,34 @@ +// Copyright (2025) Beijing Volcano Engine Technology Ltd. +// SPDX-License-Identifier: Apache-2.0 + +#include "util/volc_base64.h" + +#include + +int volc_base64_encoded_length(int len) { + return (len + 2) / 3 * 4 + 1; // +1 for null terminator +} + +int volc_base64_decoded_length(const uint8_t* to_decode, int len) { + int padding = 0; + if (len >= 2 && to_decode[len - 1] == '=' && to_decode[len - 2] == '=') { /*last two chars are = */ + padding = 2; + } else if (to_decode[len - 1] == '=') { /*last char is = */ + padding = 1; + } + return (len * 3) / 4 - padding; // Calculate the decoded length +} + +void volc_base64_encode(unsigned char *dst, size_t dlen, size_t* olen, const unsigned char *src, size_t slen) { + int ret = mbedtls_base64_encode(dst, dlen, olen, src, slen); + if (ret != 0) { + *olen = 0; // If encoding fails, set output length to 0 + } +} + +void volc_base64_decode(unsigned char *dst, size_t dlen, size_t* olen, const unsigned char *src, size_t slen) { + int ret = mbedtls_base64_decode(dst, dlen, olen, src, slen); + if (ret != 0) { + *olen = 0; // If decoding fails, set output length to 0 + } +} \ No newline at end of file diff --git a/firmware/echoear-platform-agent/components/volc_conv_ai/src/util/volc_base64.h b/firmware/echoear-platform-agent/components/volc_conv_ai/src/util/volc_base64.h new file mode 100644 index 0000000..8aec923 --- /dev/null +++ b/firmware/echoear-platform-agent/components/volc_conv_ai/src/util/volc_base64.h @@ -0,0 +1,24 @@ +// Copyright (2025) Beijing Volcano Engine Technology Ltd. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef __CONV_AI_SRC_UTIL_VOLC_BASE64_H__ +#define __CONV_AI_SRC_UTIL_VOLC_BASE64_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +int volc_base64_encoded_length(int len); +int volc_base64_decoded_length(const uint8_t* to_decode, int len); + +void volc_base64_encode(unsigned char *dst, size_t dlen, size_t* olen, const unsigned char *src, size_t slen); + +void volc_base64_decode(unsigned char *dst, size_t dlen, size_t* olen, const unsigned char *src, size_t slen); + +#ifdef __cplusplus +} +#endif +#endif /* __CONV_AI_SRC_UTIL_VOLC_BASE64_H__ */ diff --git a/firmware/echoear-platform-agent/components/volc_conv_ai/src/util/volc_http.c b/firmware/echoear-platform-agent/components/volc_conv_ai/src/util/volc_http.c new file mode 100644 index 0000000..8624388 --- /dev/null +++ b/firmware/echoear-platform-agent/components/volc_conv_ai/src/util/volc_http.c @@ -0,0 +1,40 @@ +// Copyright (2025) Beijing Volcano Engine Technology Ltd. +// SPDX-License-Identifier: Apache-2.0 + +#include "volc_http.h" + +#include "webclient.h" +#include "tls_certificate.h" +#include "util/volc_list.h" +#include "util/volc_log.h" + +char* volc_http_post(const char* uri, const char* post_data, int data_len) +{ + struct webclient_session* session = NULL; + char* buffer = NULL; + int resp_status; + size_t res_len = 0; + + /* create webclient session and set header response size */ + session = webclient_session_create(2048, GLOBAL_ROOT_CERT, GLOBAL_ROOT_CERT_LEN); + if (session == NULL) { + goto err_out_label; + } + + webclient_header_fields_add(session, "Content-Type: application/json\r\n"); + webclient_header_fields_add(session, "Content-Length: %d\r\n", strlen(post_data)); + + /* send POST request by default header */ + if ((resp_status = webclient_post(session, uri, post_data, data_len)) != 200) { + LOGE("webclient POST request failed, response(%d) error.\n", resp_status); + } + + webclient_response(session, (void**) &buffer, &res_len); + LOGD("url: %s, request: %s, response: %s", uri, post_data, buffer); +err_out_label: + if (session) { + webclient_close(session); + } + + return buffer; +} diff --git a/firmware/echoear-platform-agent/components/volc_conv_ai/src/util/volc_http.h b/firmware/echoear-platform-agent/components/volc_conv_ai/src/util/volc_http.h new file mode 100644 index 0000000..6ce1f95 --- /dev/null +++ b/firmware/echoear-platform-agent/components/volc_conv_ai/src/util/volc_http.h @@ -0,0 +1,16 @@ +// Copyright (2025) Beijing Volcano Engine Technology Ltd. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef __CONV_AI_SRC_UTIL_VOLC_HTTP_H__ +#define __CONV_AI_SRC_UTIL_VOLC_HTTP_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +char* volc_http_post(const char* uri, const char* post_data, int data_len); + +#ifdef __cplusplus +} +#endif +#endif /* __CONV_AI_SRC_UTIL_VOLC_HTTP_H__ */ diff --git a/firmware/echoear-platform-agent/components/volc_conv_ai/src/util/volc_json.c b/firmware/echoear-platform-agent/components/volc_conv_ai/src/util/volc_json.c new file mode 100644 index 0000000..d301801 --- /dev/null +++ b/firmware/echoear-platform-agent/components/volc_conv_ai/src/util/volc_json.c @@ -0,0 +1,208 @@ +// Copyright (2025) Beijing Volcano Engine Technology Ltd. +// SPDX-License-Identifier: Apache-2.0 + +#include "volc_json.h" + +#include +#include +#include + +#include "volc_osal.h" +#include "util/volc_list.h" +#include "util/volc_log.h" + +static int _get_array_index(char *fmt) +{ + char *start, *end; + int num; + start = strchr(fmt, '['); + if (NULL != start) { + end = strchr(fmt, ']'); + if (NULL != end) { + *start = *end = '\0'; + start++; + num = atoi(start); + if (num >= 0) { + return num; + } + } + } + return -1; +} + +static cJSON *_read_item_object(cJSON *root, const char *fmt) +{ + cJSON *parent = root; + cJSON *child = NULL; + int fmt_size = strlen(fmt) + 1; + char *buf = (char *)volc_osal_malloc(fmt_size); + char *buf_p = buf; + int offset = 0; + int arr_index; + int i = 0; + if (NULL == buf) { + LOGE("malloc failed !"); + return NULL; + } + /* 1. split */ + memcpy(buf, fmt, fmt_size); + for (; i < fmt_size; i++) { + if ('.' == buf[i]) { + buf[i] = '\0'; + } + } + /* 2. parse */ + while (fmt_size > 0) { + offset = strlen(buf) + 1; + arr_index = _get_array_index(buf); + child = cJSON_GetObjectItem(parent, buf); + if (NULL == child) { + goto L_ERROR; + } + if (arr_index >= 0) { + child = cJSON_GetArrayItem(child, arr_index); + if (NULL == child) { + goto L_ERROR; + } + } + parent = child; + buf = buf + offset; + fmt_size -= offset; + } + +L_ERROR: + HAL_SAFE_FREE(buf_p); + return child; +} + +static int _read_item_int(cJSON *root, const char *fmt, int *dst) +{ + if (NULL == root || NULL == fmt) { + LOGW("invalid input root %p fmt %p", root, fmt); + return -1; + } + cJSON *obj = _read_item_object(root, fmt); + if (!cJSON_IsNumber(obj)) { + LOGD("the value of the key(%s) is not the INT type", fmt); + return -1; + } + if (NULL != dst) { + *dst = (int)cJSON_GetNumberValue(obj); + } + return 0; +} + +static int _read_item_double(cJSON *root, const char *fmt, double *dst) +{ + if (NULL == root || NULL == fmt) { + LOGW("invalid input root %p fmt %p", root, fmt); + return -1; + } + cJSON *obj = _read_item_object(root, fmt); + if (!cJSON_IsNumber(obj)) { + LOGD("the value of the key(%s) is not the DOUBLE type", fmt); + return -1; + } + if (NULL != dst) { + *dst = obj->valuedouble; + } + return 0; +} + +static int _read_item_string(cJSON *root, const char *fmt, char **dst) +{ + if (NULL == root || NULL == fmt) { + LOGW("invalid input root %p fmt %p", root, fmt); + return -1; + } + cJSON *obj = _read_item_object(root, fmt); + if (!cJSON_IsString(obj)) { + LOGD("the value of the key(%s) is not the STRING type", fmt); + return -1; + } + if (NULL != dst) { + *dst = (char *)volc_osal_malloc(strlen(obj->valuestring) + 1); + if (NULL == *dst) { + LOGE("memory alloc failed"); + return -1; + } + strncpy(*dst, obj->valuestring, strlen(obj->valuestring)); + (*dst)[strlen(obj->valuestring)] = '\0'; + } + return 0; +} + +static int _read_item_bool(cJSON *root, const char *fmt, bool *dst) +{ + if (!root || !fmt) { + LOGW("invalid arguments, root: %p, fmt: %p", root, fmt); + return -1; + } + cJSON *obj = _read_item_object(root, fmt); + if (!cJSON_IsBool(obj)) { + LOGW("the value of the key(%s) is not the BOOL type", fmt); + return -1; + } + if (NULL != dst) { + *dst = cJSON_IsTrue(obj) ? true: false; + } + return 0; +} + +int volc_json_read_int(cJSON *root, const char *fmt, int *dst) { + return _read_item_int(root, fmt, dst); +} + +int volc_json_read_double(cJSON *root, const char *fmt, double *dst) { + return _read_item_double(root, fmt, dst); +} + +int volc_json_read_string(cJSON *root, const char *fmt, char **dst) { + return _read_item_string(root, fmt, dst); +} + +int volc_json_read_bool(cJSON *root, const char *fmt, bool *dst) +{ + return _read_item_bool(root, fmt, dst); +} + +int volc_json_read_object(cJSON *root, const char *fmt, cJSON **dst) { + cJSON *obj; + if (NULL == root || NULL == fmt) { + LOGW("invalid input root %p fmt %p", root, fmt); + return -1; + } + obj = _read_item_object(root, fmt); + if (NULL == obj) { + LOGD("parse error, fmt=%s", fmt); + return -1; + } + if (NULL != dst) { + *dst = cJSON_Duplicate(obj, 1); + if (NULL == *dst) { + LOGE("memory alloc failed"); + return -1; + } + } + return 0; +} + +int volc_json_check_int(cJSON *root, const char *fmt) +{ + return _read_item_int(root, fmt, NULL); +} + +int volc_json_check_double(cJSON *root, const char *fmt) +{ + return _read_item_double(root, fmt, NULL); +} + +int volc_json_check_string(cJSON *root, const char *fmt) +{ + return _read_item_string(root, fmt, NULL); +} + +int volc_json_check_bool(cJSON *root, const char *fmt) +{ + return _read_item_bool(root, fmt, NULL); +} diff --git a/firmware/echoear-platform-agent/components/volc_conv_ai/src/util/volc_json.h b/firmware/echoear-platform-agent/components/volc_conv_ai/src/util/volc_json.h new file mode 100644 index 0000000..daa05e0 --- /dev/null +++ b/firmware/echoear-platform-agent/components/volc_conv_ai/src/util/volc_json.h @@ -0,0 +1,48 @@ +// Copyright (2025) Beijing Volcano Engine Technology Ltd. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef __CONV_AI_SRC_UTIL_VOLC_JSON_H__ +#define __CONV_AI_SRC_UTIL_VOLC_JSON_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include "cJSON.h" + +#define JSON_KEY_LEN_MAX (64) + +/** + * @brief + * + * @param root + * @param fmt the string of the key, support the multi-level keys, such as: [key1.key2.key3] + * @param dst + * @return 0: success. + * -1: failure. + */ +int volc_json_read_int(cJSON *root, const char *fmt, int *dst); +int volc_json_read_double(cJSON *root, const char *fmt, double *dst); +int volc_json_read_string(cJSON *root, const char *fmt, char **dst); +int volc_json_read_object(cJSON *root, const char *fmt, cJSON **dst); +int volc_json_read_bool(cJSON *root, const char *fmt, bool *dst); + +/** + * @brief + * + * @param root + * @param fmt + * @return 0: the type of the item is same as the suffix of the function name. + * -1: different. + */ +int volc_json_check_int(cJSON *root, const char *fmt); +int volc_json_check_double(cJSON *root, const char *fmt); +int volc_json_check_string(cJSON *root, const char *fmt); +int volc_json_check_bool(cJSON *root, const char *fmt); + +#ifdef __cplusplus +} +#endif +#endif // __CONV_AI_SRC_UTIL_VOLC_JSON_H__ diff --git a/firmware/echoear-platform-agent/components/volc_conv_ai/src/util/volc_list.h b/firmware/echoear-platform-agent/components/volc_conv_ai/src/util/volc_list.h new file mode 100644 index 0000000..b7a4af2 --- /dev/null +++ b/firmware/echoear-platform-agent/components/volc_conv_ai/src/util/volc_list.h @@ -0,0 +1,79 @@ +// Copyright (2025) Beijing Volcano Engine Technology Ltd. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef __CONV_AI_SRC_UTIL_VOLC_LIST_H__ +#define __CONV_AI_SRC_UTIL_VOLC_LIST_H__ + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + + typedef struct volc_list_head_t + { + struct volc_list_head_t *next, *prev; + } volc_list_head_t; + +#define __LIST_ADD(entry, before, after) \ + { \ + volc_list_head_t *new_ = (entry), *prev = (before), *next = (after); \ + (next)->prev = (new_); \ + (new_)->next = (next); \ + (new_)->prev = (prev); \ + (prev)->next = (new_); \ + } +#define volc_list_init(entry) \ + do \ + { \ + (entry)->next = (entry); \ + (entry)->prev = (entry); \ + } while (0) +#define volc_list_add(entry, base) \ + do \ + { \ + __LIST_ADD((entry), (base), (base)->next); \ + } while (0) +#define volc_list_add_after(entry, base) \ + do \ + { \ + __LIST_ADD((entry), (base), (base)->next); \ + } while (0) +#define volc_list_add_before(entry, base) \ + do \ + { \ + __LIST_ADD((entry), (base)->prev, (base)); \ + } while (0) +#define volc_list_add_head(entry, head) volc_list_add_after(entry, head) +#define volc_list_add_tail(entry, head) volc_list_add_before(entry, head) +#define volc_list_del(entry) \ + do \ + { \ + (entry)->prev->next = (entry)->next; \ + (entry)->next->prev = (entry)->prev; \ + (entry)->next = (entry)->prev = (entry); \ + } while (0) +#define volc_list_empty(head) ((head)->next == (head)) +#define volc_list_get_head(head) (volc_list_empty(head) ? (volc_list_head_t *)NULL : (head)->next) +#define volc_list_get_tail(head) (volc_list_empty(head) ? (volc_list_head_t *)NULL : (head)->prev) +#define volc_list_is_head(entry, head) ((entry)->prev == head) +#define volc_list_is_tail(entry, head) ((entry)->next == head) +#define volc_list_entry(ptr, type, member) ((type *)((char *)(ptr) - (unsigned long)(&((type *)0)->member))) +#define volc_list_for_each(h, head) for (h = (head)->next; h != (head); h = h->next) +#define volc_list_for_each_safe(h, n, head) for (h = (head)->next, n = h->next; h != (head); h = n, n = h->next) +#define volc_list_for_each_entry(p, head, type, member) \ + for (p = volc_list_entry((head)->next, type, member); &p->member != (head); \ + p = volc_list_entry(p->member.next, type, member)) +#define volc_list_for_each_entry_safe(p, t, head, type, member) \ + for (p = volc_list_entry((head)->next, type, member), t = volc_list_entry(p->member.next, type, member); \ + &p->member != (head); p = t, t = volc_list_entry(t->member.next, type, member)) +#define volc_list_get_head_entry(head, type, member) \ + (volc_list_empty(head) ? (type *)NULL : volc_list_entry(((head)->next), type, member)) +#define volc_list_get_tail_entry(head, type, member) \ + (volc_list_empty(head) ? (type *)NULL : volc_list_entry(((head)->prev), type, member)) + +#ifdef __cplusplus +} +#endif +#endif /* #define __CONV_AI_SRC_UTIL_VOLC_LIST_H__ */ diff --git a/firmware/echoear-platform-agent/components/volc_conv_ai/src/util/volc_log.h b/firmware/echoear-platform-agent/components/volc_conv_ai/src/util/volc_log.h new file mode 100644 index 0000000..fab4fe8 --- /dev/null +++ b/firmware/echoear-platform-agent/components/volc_conv_ai/src/util/volc_log.h @@ -0,0 +1,58 @@ +// Copyright (2025) Beijing Volcano Engine Technology Ltd. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef __CONV_AI_SRC_UTIL_VOLC_LOG_H__ +#define __CONV_AI_SRC_UTIL_VOLC_LOG_H__ + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + + typedef enum + { + VOLC_LOG_LEVEL_ERROR, + VOLC_LOG_LEVEL_WARN, + VOLC_LOG_LEVEL_INFO, + VOLC_LOG_LEVEL_DEBUG, + VOLC_LOG_LEVEL_VERBOSE, + VOLC_LOG_LEVEL_NONE, // 不输出日志 + } volc_log_level_e; + +#define VOLC_LOG_LEVEL_DEFAULT VOLC_LOG_LEVEL_INFO + +// 日志级别颜色定义 +#define LOG_COLOR_RESET "\033[0m" +#define LOG_COLOR_RED "\033[31m" +#define LOG_COLOR_GREEN "\033[32m" +#define LOG_COLOR_YELLOW "\033[33m" +#define LOG_COLOR_BLUE "\033[34m" +#define LOG_COLOR_PRUPLE "\033[35m" + +#ifndef __FILENAME__ +#define __FILENAME__ (__builtin_strrchr(__FILE__, '/') ? __builtin_strrchr(__FILE__, '/') + 1 : __FILE__) +#endif +// 通用日志宏 +#define LOG(level, tag, color, format, ...) \ + do \ + { \ + if (level <= VOLC_LOG_LEVEL_DEFAULT) \ + { \ + printf(color "[%s|%s:%d]" format LOG_COLOR_RESET "\n", tag, __FILENAME__, __LINE__, ##__VA_ARGS__); \ + fflush(stdout); \ + } \ + } while (0) + +// 分级日志实现 +#define LOGV(format, ...) LOG(VOLC_LOG_LEVEL_VERBOSE, "VRB", LOG_COLOR_PRUPLE, format, ##__VA_ARGS__) +#define LOGD(format, ...) LOG(VOLC_LOG_LEVEL_DEBUG, "DBG", LOG_COLOR_BLUE, format, ##__VA_ARGS__) +#define LOGI(format, ...) LOG(VOLC_LOG_LEVEL_INFO, "INF", LOG_COLOR_GREEN, format, ##__VA_ARGS__) +#define LOGW(format, ...) LOG(VOLC_LOG_LEVEL_WARN, "WRN", LOG_COLOR_YELLOW, format, ##__VA_ARGS__) +#define LOGE(format, ...) LOG(VOLC_LOG_LEVEL_ERROR, "ERR", LOG_COLOR_RED, format, ##__VA_ARGS__) + +#ifdef __cplusplus +} +#endif +#endif /* __CONV_AI_SRC_UTIL_VOLC_LOG_H__ */ diff --git a/firmware/echoear-platform-agent/components/volc_conv_ai/src/volc_conv_ai.c b/firmware/echoear-platform-agent/components/volc_conv_ai/src/volc_conv_ai.c new file mode 100644 index 0000000..6a5e67b --- /dev/null +++ b/firmware/echoear-platform-agent/components/volc_conv_ai/src/volc_conv_ai.c @@ -0,0 +1,604 @@ +// Copyright (2025) Beijing Volcano Engine Technology Ltd. +// SPDX-License-Identifier: Apache-2.0 + +#include "volc_conv_ai.h" + +#include +#include +#include "volc_osal.h" +#include "util/volc_json.h" +#include "util/volc_log.h" +#include "base/volc_device_manager.h" +#include "base/volc_base.h" + +#if defined(ENABLE_RTC_MODE) +#include "VolcEngineRTCLite.h" +#include "volc_rtc.h" +#elif defined(ENABLE_WS_MODE) +#include "volc_ws.h" +#endif + +#include "cJSON.h" + +#define MAGIC_CONTROL "ctrl" +#define MAGIC_CONV "conv" +#define MAGIC_LENGTH 4 +#define MAGIC_OFFSET 8 + +typedef enum { + VOLC_RT_STATE_NONE = 0, // Initial state + VOLC_RT_STATE_CREATED, // engine created + VOLC_RT_STATE_STARTED, // engine started + // VOLC_RT_STATE_STARTED, // Joined a channel + VOLC_RT_STATE_UNJOINED, // Connection lost + VOLC_RT_STATE_STOPPED, // engine stopped + VOLC_RT_STATE_DESTROYED, // engine destroyed + VOLC_RT_STATE_ERROR // Error state +} volc_rt_state_e; + +typedef struct { + int bytes; + uint64_t last_time_ms; +} media_stats_t; + +typedef struct { + volatile size_t status; + bool b_key_frame_request; + int target_kbps; + void* user_data; + + volc_mode_e mode; +#if defined(ENABLE_WS_MODE) + volc_ws_t ws; +#endif +#if defined(ENABLE_RTC_MODE) + volc_rtc_t rtc; +#endif + + media_stats_t audio_recv_stats; + media_stats_t audio_send_stats; + volc_iot_info_t info; + // volc_room_info_t room_info; + volc_event_handler_t event_handler; +} volc_engine_impl_t; + +static void _iot_info_free(volc_iot_info_t* info) { + if (info) { + HAL_SAFE_FREE(info->device_name); + HAL_SAFE_FREE(info->device_secret); + HAL_SAFE_FREE(info->instance_id); + HAL_SAFE_FREE(info->product_key); + HAL_SAFE_FREE(info->product_secret); + } +} + +static void __realtime_conv_status_to_user(volc_engine_impl_t* impl, volc_conv_status_e status) { + if (impl->event_handler.on_volc_conversation_status) { + impl->event_handler.on_volc_conversation_status(impl, status, impl->user_data); + } +} + +static void __realtime_event_2_user_event(void* context, volc_msg_t* msg) { + volc_event_t event = { 0 }; + if (context == NULL || msg == NULL) { + LOGE("context or message is NULL"); + return; + } + + volc_engine_impl_t* impl = (volc_engine_impl_t*)context; + switch (msg->code) { + case VOLC_MSG_CONNECTED: + event.code = VOLC_EV_CONNECTED; + break; + case VOLC_MSG_DISCONNECTED: + event.code = VOLC_EV_DISCONNECTED; + break; + case VOLC_MSG_USER_JOINED: + case VOLC_MSG_USER_OFFLINE: + break; + case VOLC_MSG_TOKEN_EXPIRED: + break; + case VOLC_MSG_KEY_FRAME_REQ: + impl->b_key_frame_request = true; + return; + case VOLC_MSG_TARGET_BITRATE_CHANGED: + impl->target_kbps = msg->data.target_bitrate / 1000; + return; + case VOLC_MSG_CONV_STATUS: + __realtime_conv_status_to_user(impl, msg->data.conv_status); + return; + case VOLC_MSG_QUOTA_EXCEEDED: + event.code = VOLC_EV_QUOTA_EXCEEDED; + break; + default: + LOGW("Unknown message type: %d", msg->code); + return; // Ignore unknown messages + } + + if (impl->event_handler.on_volc_event) { + impl->event_handler.on_volc_event(impl, &event, impl->user_data); + } +} + +static void __realtime_user_event_router(void* context, volc_msg_t* msg) { + __realtime_event_2_user_event(context, msg); +} + +static void __record_media_bytes(media_stats_t* stats, size_t len, const char* tag) { +#if defined(ENABLE_WS_MODE) + uint64_t now_ms = 0; + stats->bytes += len; + now_ms = volc_osal_get_time_ms(); + if (stats->last_time_ms == 0) { + stats->last_time_ms = now_ms; + } + if (now_ms - stats->last_time_ms >= 2000) { + LOGI("%s bps: %d", tag, (int)(stats->bytes * 8 / (now_ms - stats->last_time_ms))); + stats->last_time_ms = now_ms; + stats->bytes = 0; + } +#else + (void)stats; + (void)len; + (void)tag; +#endif +} + +static void __realtime_audio_router(volc_engine_impl_t* impl, const void* data, size_t len, volc_data_info_t* info) { + if (impl->event_handler.on_volc_audio_data) { + impl->event_handler.on_volc_audio_data(impl, data, len, &info->info.audio, impl->user_data); + } + __record_media_bytes(&impl->audio_recv_stats, len, "audio recv"); +} + +static void __realtime_video_router(volc_engine_impl_t* impl, const void* data, size_t len, volc_data_info_t* info) { + if (impl->event_handler.on_volc_video_data) { + impl->event_handler.on_volc_video_data(impl, data, len, &info->info.video, impl->user_data); + } +} + +static void __realtime_message_router(volc_engine_impl_t* impl, const void* data, size_t len, volc_data_info_t* info) { + int ret = 0; + if (impl->event_handler.on_volc_message_data) { + impl->event_handler.on_volc_message_data(impl, data, len, &info->info.message, impl->user_data); + } +} + +static void __realtime_data_router(void* context, const void* data, size_t len, volc_data_info_t* info) { + volc_engine_impl_t* impl = (volc_engine_impl_t*)context; + if (info) { + switch(info->type) { + case VOLC_DATA_TYPE_AUDIO: + __realtime_audio_router(impl, data, len, info); + break; + case VOLC_DATA_TYPE_VIDEO: + __realtime_video_router(impl, data, len, info); + break; + case VOLC_DATA_TYPE_MESSAGE: + __realtime_message_router(impl, data, len, info); + break; + default: + break; + } + } +} + +static int __config_iot_parse(cJSON* iot_config, volc_engine_impl_t* engine) { + int ret = 0; + + ret |= volc_json_read_string(iot_config, "instance_id", &engine->info.instance_id); + ret |= volc_json_read_string(iot_config, "product_key", &engine->info.product_key); + ret |= volc_json_read_string(iot_config, "device_name", &engine->info.device_name); + + /* + * Production devices receive a per-device Device Secret from our SaaS. + * Product Secret is deliberately not accepted by the firmware path. + */ + ret |= volc_json_read_string(iot_config, "device_secret", &engine->info.device_secret); + + return ret == 0 ? 0 : -1; +} + +const char* volc_get_version(void) { + static char version[16] = {0}; + snprintf(version, sizeof(version), "%d.%d.%d", VOLC_VERSION_MAJOR, VOLC_VERSION_MINOR, VOLC_VERSION_PATCH); + return version; +} + +const char* volc_err_2_str(int err_code) { + switch(err_code) { + case VOLC_ERR_NO_ERROR: + return "Success"; + case VOLC_ERR_FAILED: + return "Failed"; + case VOLC_ERR_LICENSE_EXHAUSTED: + return "License exhausted"; + case VOLC_ERR_LICENSE_EXPIRED: + return "License expired"; + default: + break; + } + return "Unknown error"; +} + +int volc_create(volc_engine_t* handle, const char* config_json, volc_event_handler_t* event_handler, void* user_data) { + int ret = 0; + volc_engine_impl_t* engine = NULL; + cJSON* config = NULL; + if (config_json == NULL || event_handler == NULL || handle == NULL) { + LOGE("Configuration JSON is NULL"); + return VOLC_ERR_FAILED; + } + + engine = (volc_engine_impl_t*)volc_osal_malloc(sizeof(volc_engine_impl_t)); + if (engine == NULL) { + LOGE("Failed to allocate memory for engine"); + return VOLC_ERR_FAILED; + } + memset(engine, 0, sizeof(volc_engine_impl_t)); + + engine->event_handler = *event_handler; + engine->user_data = user_data; + engine->mode = VOLC_MODE_UNKNOWN; + + config = cJSON_Parse(config_json); + + cJSON* iot_config = cJSON_GetObjectItem(config, "iot"); + if (iot_config) { + ret = __config_iot_parse(iot_config, engine); + if (ret != 0) { + LOGE("Failed to parse IoT configuration"); + goto err_out_label; + } + if (engine->info.device_secret == NULL || strlen(engine->info.device_secret) < 16) { + LOGE("Device Secret is missing or invalid"); + ret = VOLC_ERR_FAILED; + goto err_out_label; + } + } else { + LOGE("IoT configuration is NULL"); + goto err_out_label; + } + + cJSON* rtc_cfg = cJSON_GetObjectItem(config, "rtc"); + if (rtc_cfg) { +#if defined(ENABLE_RTC_MODE) + engine->rtc = volc_rtc_create(engine->info.rtc_app_id, engine, rtc_cfg, __realtime_user_event_router, __realtime_data_router); +#else + LOGW("RTC mode is not enabled"); +#endif + } else { + LOGI("RTC configuration is NULL"); + } + +#if defined(ENABLE_WS_MODE) + cJSON* ws_cfg = cJSON_GetObjectItem(config, "ws"); + engine->ws = volc_ws_create(engine, ws_cfg, __realtime_user_event_router, __realtime_data_router); +#else + LOGW("WS mode is not enabled"); +#endif + + engine->status = VOLC_RT_STATE_CREATED; + *handle = (volc_engine_t)engine; + cJSON_Delete(config); + LOGI("Engine created successfully at: %llu ms", volc_osal_get_time_ms()); + return 0; +err_out_label: + _iot_info_free(&engine->info); + HAL_SAFE_FREE(engine); + cJSON_Delete(config); + return ret; +} + +void volc_destroy(volc_engine_t handle) { + volc_engine_impl_t* engine = (volc_engine_impl_t *)handle; + if (NULL == engine) { + return; + } + // if (engine->status == VOLC_RT_STATE_STARTED) { + // volc_stop(handle); + // } + switch(engine->mode) { + case VOLC_MODE_WS: +#if defined(ENABLE_WS_MODE) + volc_ws_destroy(engine->ws); +#else + LOGW("WS mode is not enabled"); +#endif + break; + case VOLC_MODE_RTC: +#if defined(ENABLE_RTC_MODE) + volc_rtc_destroy(engine->rtc); +#else + LOGW("RTC mode is not enabled"); +#endif + break; + default: + break; + } + _iot_info_free(&engine->info); + HAL_SAFE_FREE(engine); +} + +int volc_start(volc_engine_t handle, volc_opt_t* opt) { + int ret = 0; + volc_engine_impl_t* engine = (volc_engine_impl_t*)handle; + if (engine == NULL || opt == NULL) { + LOGE("engine handle(%p) or bot id(%p) is NULL", handle, opt); + return -1; + } + + if (NULL == opt->bot_id || strlen(opt->bot_id) <= 0) { + LOGE("bot id is invalid"); + return -1; + } + + if (engine->status != VOLC_RT_STATE_CREATED && engine->status != VOLC_RT_STATE_STOPPED) { + LOGE("engine is not in CREATED state"); + return -1; + } + + engine->mode = opt->mode; + if (opt->mode == VOLC_MODE_WS) { +#if defined(ENABLE_WS_MODE) + ret = volc_ws_start(engine->ws, opt->bot_id, &engine->info, opt->params); +#else + LOGE("WS mode is not enabled"); + ret = -1; +#endif + } else if (opt->mode == VOLC_MODE_RTC) { +#if defined(ENABLE_RTC_MODE) + ret = volc_rtc_start(engine->rtc, opt->bot_id, &engine->info, opt->params); +#else + LOGE("RTC mode is not enabled"); + ret = -1; +#endif + } + + if (ret != 0) { + engine->status = VOLC_RT_STATE_STOPPED; + LOGE("engine start failed: %d", ret); + return ret; + } + engine->status = VOLC_RT_STATE_STARTED; + LOGI("engine started successfully"); + return ret; +} + +int volc_stop(volc_engine_t handle) { + int ret = 0; + volc_engine_impl_t* engine = (volc_engine_impl_t*)handle; + if (engine == NULL) { + LOGE("engine handle is NULL"); + return -1; + } + if (engine->status != VOLC_RT_STATE_STARTED) { + LOGW("engine is not in STARTED state"); + return -1; + } + + switch (engine->mode) + { + case VOLC_MODE_WS: +#if defined(ENABLE_WS_MODE) + ret = volc_ws_stop(engine->ws); +#else + LOGE("WS mode is not enabled"); + ret = -1; +#endif + break; + case VOLC_MODE_RTC: +#if defined(ENABLE_RTC_MODE) + ret = volc_rtc_stop(engine->rtc); +#else + LOGE("RTC mode is not enabled"); + ret = -1; +#endif + break; + default: + break; + } + + engine->status = VOLC_RT_STATE_STOPPED; + LOGI("engine stopped successfully"); + return ret; +} + +int volc_update(volc_engine_t handle, const void* data_ptr, size_t data_len) { + int ret = 0; + volc_message_info_t info = { 0 }; + volc_engine_impl_t* engine = (volc_engine_impl_t*)handle; + if (engine == NULL) { + LOGE("engine handle is NULL"); + return -1; + } + if (engine->status != VOLC_RT_STATE_STARTED) { + LOGE("engine is not in STARTED state"); + return -1; + } + + info.is_binary = true; + return volc_send_message(handle, data_ptr, data_len, &info); +} + +int volc_send_audio_data(volc_engine_t handle, const void* data_ptr, size_t data_len, volc_audio_frame_info_t* info_ptr) { + int ret = 0; + volc_data_info_t info = { 0 }; + volc_engine_impl_t* engine = (volc_engine_impl_t*)handle; + if (engine == NULL || data_ptr == NULL || data_len == 0) { + LOGE("engine %p, data_ptr %p, or data_len %zu is NULL or zero", engine, data_ptr, data_len); + return -1; + } + if (engine->status != VOLC_RT_STATE_STARTED) { + LOGE("engine is not in STARTED state"); + return -1; + } + + info.type = VOLC_DATA_TYPE_AUDIO; + info.info.audio.data_type = info_ptr->data_type; + info.info.audio.commit = info_ptr->commit; + switch (engine->mode) { + case VOLC_MODE_RTC: +#if defined(ENABLE_RTC_MODE) + ret = volc_rtc_send(engine->rtc, data_ptr, data_len, &info); +#else + LOGE("RTC mode is not enabled"); + ret = -1; +#endif + break; + case VOLC_MODE_WS: +#if defined(ENABLE_WS_MODE) + ret = volc_ws_send(engine->ws, data_ptr, data_len, &info); +#else + LOGE("WS mode is not enabled"); + ret = -1; +#endif + break; + default: + break; + } + __record_media_bytes(&engine->audio_send_stats, data_len, "audio send"); + + return ret; +} + +int volc_send_video_data(volc_engine_t handle, const void* data_ptr, size_t data_len, volc_video_frame_info_t* info_ptr) { + int ret = 0; + volc_data_info_t info = { 0 }; + volc_engine_impl_t* engine = (volc_engine_impl_t*)handle; + if (engine == NULL || data_ptr == NULL || data_len == 0) { + LOGE("engine %p, data_ptr %p, or data_len %zu is NULL or zero", engine, data_ptr, data_len); + return -1; + } + if (engine->status != VOLC_RT_STATE_STARTED) { + LOGE("engine is not in STARTED state"); + return -1; + } + + info.type = VOLC_DATA_TYPE_VIDEO; + info.info.video.data_type = info_ptr->data_type; + switch (engine->mode) { + case VOLC_MODE_RTC: +#if defined(ENABLE_RTC_MODE) + ret = volc_rtc_send(engine->rtc, data_ptr, data_len, &info); +#else + LOGE("RTC mode is not enabled"); + ret = -1; +#endif + break; + case VOLC_MODE_WS: +#if defined(ENABLE_WS_MODE) + ret = volc_ws_send(engine->ws, data_ptr, data_len, &info); +#else + LOGE("WS mode is not enabled"); + ret = -1; +#endif + break; + default: + break; + } + return 0; +} + +int volc_send_message(volc_engine_t handle, const void* data_ptr, size_t data_len, volc_message_info_t* info_ptr) { + int ret = 0; + volc_data_info_t info = { 0 }; + if (handle == NULL || data_ptr == NULL || data_len == 0) { + LOGE("engine handle %p, data_ptr %p, or data_len %zu is NULL or zero", handle, data_ptr, data_len); + return -1; + } + + volc_engine_impl_t* engine = (volc_engine_impl_t*)handle; + if (engine->status != VOLC_RT_STATE_STARTED) { + LOGE("engine is not in started state"); + return -1; + } + + info.type = VOLC_DATA_TYPE_MESSAGE; + info.info.message.is_binary = info_ptr ? info_ptr->is_binary : false; + switch (engine->mode) { + case VOLC_MODE_RTC: +#if defined(ENABLE_RTC_MODE) + ret = volc_rtc_send(engine->rtc, data_ptr, data_len, &info); +#else + LOGE("RTC mode is not enabled"); + ret = -1; +#endif + break; + case VOLC_MODE_WS: +#if defined(ENABLE_WS_MODE) + ret = volc_ws_send(engine->ws, data_ptr, data_len, &info); +#else + LOGE("WS mode is not enabled"); + ret = -1; +#endif + break; + default: + break; + } + return ret; +} + +int volc_send_text_to_agent(volc_engine_t handle, const char* text, volc_agent_type_e type, int interrupt_mode) { + int ret = 0; + volc_engine_impl_t* engine = (volc_engine_impl_t*)handle; + if (handle == NULL) { + LOGE("engine handle is NULL"); + return -1; + } + LOGD("interrupt: %d", engine->mode); + switch (engine->mode) { + case VOLC_MODE_RTC: +#if defined(ENABLE_RTC_MODE) + ret = volc_rtc_send_text_to_agent(engine->rtc,text,type,interrupt_mode); +#else + LOGE("RTC mode is not enabled"); + ret = -1; +#endif + break; + case VOLC_MODE_WS: +#if defined(ENABLE_WS_MODE) + ret = -2; + LOGE("WS mode is not support send_text"); +#else + LOGE("WS mode is not enabled"); + ret = -1; +#endif + break; + default: + break; + } + return ret; +} + +int volc_interrupt(volc_engine_t handle) { + int ret = 0; + volc_engine_impl_t* engine = (volc_engine_impl_t*)handle; + if (handle == NULL) { + LOGE("engine handle is NULL"); + return -1; + } + LOGI("interrupt: %d, at: %" PRIu64"ms", engine->mode, volc_osal_get_time_ms()); + switch (engine->mode) { + case VOLC_MODE_RTC: +#if defined(ENABLE_RTC_MODE) + ret = volc_rtc_interrupt(engine->rtc); +#else + LOGE("RTC mode is not enabled"); + ret = -1; +#endif + break; + case VOLC_MODE_WS: +#if defined(ENABLE_WS_MODE) + ret = volc_ws_interrupt(engine->ws); +#else + LOGE("WS mode is not enabled"); + ret = -1; +#endif + break; + default: + break; + } + + return ret; +} diff --git a/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/mbedtls_port/tls_certificate.c b/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/mbedtls_port/tls_certificate.c new file mode 100644 index 0000000..40d2e07 --- /dev/null +++ b/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/mbedtls_port/tls_certificate.c @@ -0,0 +1,46 @@ + +/* + * Copyright (c) 2006-2018 RT-Thread Development Team. All rights reserved. + * License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +const char GLOBAL_ROOT_CERT[] = +"-----BEGIN CERTIFICATE-----\n" +"MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBh\n" +"MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\n" +"d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH\n" +"MjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVT\n" +"MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j\n" +"b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkqhkiG\n" +"9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI\n" +"2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx\n" +"1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQ\n" +"q2EGnI/yuum06ZIya7XzV+hdG82MHauVBJVJ8zUtluNJbd134/tJS7SsVQepj5Wz\n" +"tCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQ\n" +"vIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQABo0IwQDAP\n" +"BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV\n" +"5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY\n" +"1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4\n" +"NeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NG\n" +"Fdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ91\n" +"8rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTe\n" +"pLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl\n" +"MrY=\n" +"-----END CERTIFICATE-----\n"; + +size_t GLOBAL_ROOT_CERT_LEN = sizeof(GLOBAL_ROOT_CERT); diff --git a/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/mbedtls_port/tls_certificate.h b/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/mbedtls_port/tls_certificate.h new file mode 100644 index 0000000..048c4b8 --- /dev/null +++ b/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/mbedtls_port/tls_certificate.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2006-2015, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is part of mbed TLS (https://tls.mbed.org) + */ + +#ifndef MBEDTLS_CERTIFICATE_H +#define MBEDTLS_CERTIFICATE_H + +extern const char GLOBAL_ROOT_CERT[]; +extern size_t GLOBAL_ROOT_CERT_LEN; + +#endif diff --git a/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/mbedtls_port/tls_client.c b/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/mbedtls_port/tls_client.c new file mode 100644 index 0000000..106c610 --- /dev/null +++ b/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/mbedtls_port/tls_client.c @@ -0,0 +1,237 @@ +/* + * Copyright (C) 2006-2015, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is part of mbed TLS (https://tls.mbed.org) + */ +#include +#include +#include + +#if defined(MBEDTLS_USER_CONFIG_FILE) +#include MBEDTLS_USER_CONFIG_FILE +#endif + +#define TAG "tls_client" + +#include "tls_certificate.h" +#include "tls_client.h" + +#include "volc_osal.h" +#include "util/volc_list.h" +#include "util/volc_log.h" + +#ifdef MBEDTLS_DEBUG_C +#define DEBUG_LEVEL (2) +#endif + +static char error_buf[100]; + +static void _ssl_debug(void *ctx, int level, const char *file, int line, const char *str) +{ + ((void)level); + + // LOGD("%s:%04d: %s", file, line, str); +} + +static int mbedtls_ssl_certificate_verify(MbedTLSSession *session) +{ + int ret = 0; + ret = mbedtls_ssl_get_verify_result(&session->ssl); + if (ret != 0) { + LOGE("verify peer certificate fail...."); + memset(session->buffer, 0x00, session->buffer_len); + mbedtls_x509_crt_verify_info((char *)session->buffer, session->buffer_len, " ! ", ret); + LOGE("verification info: %s", session->buffer); + return -1; + } + return 0; +} + +int mbedtls_client_init(MbedTLSSession *session, void *entropy, size_t entropyLen) +{ + int ret = 0; + +#if defined(MBEDTLS_DEBUG_C) + LOGD("Set debug level (%d)", (int)DEBUG_LEVEL); + mbedtls_debug_set_threshold((int)DEBUG_LEVEL); +#endif + + mbedtls_net_init(&session->server_fd); + mbedtls_ssl_init(&session->ssl); + mbedtls_ssl_config_init(&session->conf); + mbedtls_ctr_drbg_init(&session->ctr_drbg); + mbedtls_entropy_init(&session->entropy); + mbedtls_x509_crt_init(&session->cacert); + + ret = mbedtls_ctr_drbg_seed(&session->ctr_drbg, mbedtls_entropy_func, &session->entropy, + (unsigned char *)entropy, entropyLen); + if (ret != 0) { + LOGE("mbedtls_ctr_drbg_seed error, return -0x%x\n", -ret); + return ret; + } + LOGD("mbedtls client struct init success..."); + + return 0; +} + +int mbedtls_client_close(MbedTLSSession *session) +{ + if (session == NULL) { + return -1; + } + + mbedtls_ssl_close_notify(&session->ssl); + mbedtls_net_free(&session->server_fd); + mbedtls_x509_crt_free(&session->cacert); + mbedtls_entropy_free(&session->entropy); + mbedtls_ctr_drbg_free(&session->ctr_drbg); + mbedtls_ssl_config_free(&session->conf); + mbedtls_ssl_free(&session->ssl); + + HAL_SAFE_FREE(session->buffer); + HAL_SAFE_FREE(session->host); + HAL_SAFE_FREE(session->port); + HAL_SAFE_FREE(session); + + return 0; +} + +int mbedtls_client_context(MbedTLSSession *session) +{ + int ret = 0; + + ret = mbedtls_x509_crt_parse(&session->cacert, (const unsigned char *)GLOBAL_ROOT_CERT, GLOBAL_ROOT_CERT_LEN); + if (ret < 0) { + LOGE("mbedtls_x509_crt_parse error, return -0x%x", -ret); + return ret; + } + + LOGD("Loading the CA root certificate success..."); + + /* Hostname set here should match CN in server certificate */ + if (session->host) { + ret = mbedtls_ssl_set_hostname(&session->ssl, session->host); + if (ret != 0) { + LOGE("mbedtls_ssl_set_hostname error, return -0x%x", -ret); + return ret; + } + } + + ret = mbedtls_ssl_config_defaults(&session->conf, MBEDTLS_SSL_IS_CLIENT, + MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT); + if (ret != 0) { + LOGE("mbedtls_ssl_config_defaults error, return -0x%x", -ret); + return ret; + } + + mbedtls_ssl_conf_authmode(&session->conf, MBEDTLS_SSL_VERIFY_REQUIRED); + mbedtls_ssl_conf_ca_chain(&session->conf, &session->cacert, NULL); + mbedtls_ssl_conf_rng(&session->conf, mbedtls_ctr_drbg_random, &session->ctr_drbg); + + mbedtls_ssl_conf_dbg(&session->conf, _ssl_debug, NULL); + + mbedtls_ssl_conf_read_timeout(&session->conf, 1000); + + ret = mbedtls_ssl_setup(&session->ssl, &session->conf); + if (ret != 0) { + LOGE("mbedtls_ssl_setup error, return -0x%x", -ret); + return ret; + } + LOGD("mbedtls client context init success..."); + + return 0; +} + +int mbedtls_client_connect(MbedTLSSession *session) +{ + int ret = 0; + + ret = mbedtls_net_connect(&session->server_fd, session->host, session->port, + MBEDTLS_NET_PROTO_TCP); + if (ret != 0) { + LOGE("mbedtls_net_connect error, return -0x%x", -ret); + return ret; + } + + LOGD("Connected %s:%s success...", session->host, session->port); + + mbedtls_ssl_set_bio(&session->ssl, &session->server_fd, mbedtls_net_send, mbedtls_net_recv, NULL); + + while ((ret = mbedtls_ssl_handshake(&session->ssl)) != 0) { + if (0 != mbedtls_ssl_certificate_verify(session)) { + return -1; + } + if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) { + LOGE("mbedtls_ssl_handshake error, return -0x%x", -ret); + return ret; + } + } + + if (0 != mbedtls_ssl_certificate_verify(session)) { + return -1; + } + + LOGD("Certificate verified success..."); + + return 0; +} + +int mbedtls_client_read(MbedTLSSession *session, unsigned char *buf, size_t len) +{ + int ret = 0; + + if (!session || !buf || (len == 0)) { + LOGW("invalid arguments: session(%p), buf(%p), len(%d)", + session, buf, (int)len); + return -1; + } + + do { + ret = mbedtls_ssl_read(&session->ssl, (unsigned char *)buf, len); + } while (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE); + if (ret <= 0) { + switch (ret) { + case MBEDTLS_ERR_SSL_TIMEOUT: + LOGE("timeout"); + break; + case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY: + LOGE("connection was close"); + break; + default: + mbedtls_strerror(ret, error_buf, 100); + LOGE("mbedtls_ssl_read failed: %d, %s", ret, error_buf); + break; + } + } + + return ret; +} + +int mbedtls_client_write(MbedTLSSession *session, const unsigned char *buf, size_t len) +{ + int ret = 0; + + if (session == NULL || buf == NULL) { + return -1; + } + + ret = mbedtls_ssl_write(&session->ssl, (unsigned char *)buf, len); + if (ret < 0 && ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) { + LOGE("mbedtls_client_write data error, return -0x%x", -ret); + } + + return ret; +} diff --git a/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/mbedtls_port/tls_client.h b/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/mbedtls_port/tls_client.h new file mode 100644 index 0000000..236cb84 --- /dev/null +++ b/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/mbedtls_port/tls_client.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2006-2015, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is part of mbed TLS (https://tls.mbed.org) + */ + +#ifndef MBEDTLS_CLIENT_H +#define MBEDTLS_CLIENT_H + +#include "mbedtls/x509_crt.h" +#include "mbedtls/net_sockets.h" +#include "mbedtls/debug.h" +#include "mbedtls/ssl.h" +#include "mbedtls/entropy.h" +#include "mbedtls/ctr_drbg.h" +#include "mbedtls/error.h" +#include "mbedtls/timing.h" +#include "mbedtls/platform.h" + +typedef struct MbedTLSSession { + char *host; + char *port; + + unsigned char *buffer; + size_t buffer_len; + + mbedtls_ssl_context ssl; + mbedtls_ssl_config conf; + mbedtls_entropy_context entropy; + mbedtls_ctr_drbg_context ctr_drbg; + mbedtls_net_context server_fd; + mbedtls_x509_crt cacert; +} MbedTLSSession; + +extern int mbedtls_client_init(MbedTLSSession *session, void *entropy, size_t entropyLen); +extern int mbedtls_client_close(MbedTLSSession *session); +extern int mbedtls_client_context(MbedTLSSession *session); +extern int mbedtls_client_connect(MbedTLSSession *session); +extern int mbedtls_client_read(MbedTLSSession *session, unsigned char *buf, size_t len); +extern int mbedtls_client_write(MbedTLSSession *session, const unsigned char *buf, size_t len); + +#endif diff --git a/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/prebuilt/zlib/inc/zconf.h b/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/prebuilt/zlib/inc/zconf.h new file mode 100644 index 0000000..995ad4f --- /dev/null +++ b/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/prebuilt/zlib/inc/zconf.h @@ -0,0 +1,536 @@ +/* zconf.h -- configuration of the zlib compression library + * Copyright (C) 1995-2016 Jean-loup Gailly, Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#ifndef ZCONF_H +#define ZCONF_H +/* #undef Z_PREFIX */ +#define Z_HAVE_UNISTD_H + +/* + * If you *really* need a unique prefix for all types and library functions, + * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. + * Even better than compiling with -DZ_PREFIX would be to use configure to set + * this permanently in zconf.h using "./configure --zprefix". + */ +#ifdef Z_PREFIX /* may be set to #if 1 by ./configure */ +# define Z_PREFIX_SET + +/* all linked symbols and init macros */ +# define _dist_code z__dist_code +# define _length_code z__length_code +# define _tr_align z__tr_align +# define _tr_flush_bits z__tr_flush_bits +# define _tr_flush_block z__tr_flush_block +# define _tr_init z__tr_init +# define _tr_stored_block z__tr_stored_block +# define _tr_tally z__tr_tally +# define adler32 z_adler32 +# define adler32_combine z_adler32_combine +# define adler32_combine64 z_adler32_combine64 +# define adler32_z z_adler32_z +# ifndef Z_SOLO +# define compress z_compress +# define compress2 z_compress2 +# define compressBound z_compressBound +# endif +# define crc32 z_crc32 +# define crc32_combine z_crc32_combine +# define crc32_combine64 z_crc32_combine64 +# define crc32_z z_crc32_z +# define deflate z_deflate +# define deflateBound z_deflateBound +# define deflateCopy z_deflateCopy +# define deflateEnd z_deflateEnd +# define deflateGetDictionary z_deflateGetDictionary +# define deflateInit z_deflateInit +# define deflateInit2 z_deflateInit2 +# define deflateInit2_ z_deflateInit2_ +# define deflateInit_ z_deflateInit_ +# define deflateParams z_deflateParams +# define deflatePending z_deflatePending +# define deflatePrime z_deflatePrime +# define deflateReset z_deflateReset +# define deflateResetKeep z_deflateResetKeep +# define deflateSetDictionary z_deflateSetDictionary +# define deflateSetHeader z_deflateSetHeader +# define deflateTune z_deflateTune +# define deflate_copyright z_deflate_copyright +# define get_crc_table z_get_crc_table +# ifndef Z_SOLO +# define gz_error z_gz_error +# define gz_intmax z_gz_intmax +# define gz_strwinerror z_gz_strwinerror +# define gzbuffer z_gzbuffer +# define gzclearerr z_gzclearerr +# define gzclose z_gzclose +# define gzclose_r z_gzclose_r +# define gzclose_w z_gzclose_w +# define gzdirect z_gzdirect +# define gzdopen z_gzdopen +# define gzeof z_gzeof +# define gzerror z_gzerror +# define gzflush z_gzflush +# define gzfread z_gzfread +# define gzfwrite z_gzfwrite +# define gzgetc z_gzgetc +# define gzgetc_ z_gzgetc_ +# define gzgets z_gzgets +# define gzoffset z_gzoffset +# define gzoffset64 z_gzoffset64 +# define gzopen z_gzopen +# define gzopen64 z_gzopen64 +# ifdef _WIN32 +# define gzopen_w z_gzopen_w +# endif +# define gzprintf z_gzprintf +# define gzputc z_gzputc +# define gzputs z_gzputs +# define gzread z_gzread +# define gzrewind z_gzrewind +# define gzseek z_gzseek +# define gzseek64 z_gzseek64 +# define gzsetparams z_gzsetparams +# define gztell z_gztell +# define gztell64 z_gztell64 +# define gzungetc z_gzungetc +# define gzvprintf z_gzvprintf +# define gzwrite z_gzwrite +# endif +# define inflate z_inflate +# define inflateBack z_inflateBack +# define inflateBackEnd z_inflateBackEnd +# define inflateBackInit z_inflateBackInit +# define inflateBackInit_ z_inflateBackInit_ +# define inflateCodesUsed z_inflateCodesUsed +# define inflateCopy z_inflateCopy +# define inflateEnd z_inflateEnd +# define inflateGetDictionary z_inflateGetDictionary +# define inflateGetHeader z_inflateGetHeader +# define inflateInit z_inflateInit +# define inflateInit2 z_inflateInit2 +# define inflateInit2_ z_inflateInit2_ +# define inflateInit_ z_inflateInit_ +# define inflateMark z_inflateMark +# define inflatePrime z_inflatePrime +# define inflateReset z_inflateReset +# define inflateReset2 z_inflateReset2 +# define inflateResetKeep z_inflateResetKeep +# define inflateSetDictionary z_inflateSetDictionary +# define inflateSync z_inflateSync +# define inflateSyncPoint z_inflateSyncPoint +# define inflateUndermine z_inflateUndermine +# define inflateValidate z_inflateValidate +# define inflate_copyright z_inflate_copyright +# define inflate_fast z_inflate_fast +# define inflate_table z_inflate_table +# ifndef Z_SOLO +# define uncompress z_uncompress +# define uncompress2 z_uncompress2 +# endif +# define zError z_zError +# ifndef Z_SOLO +# define zcalloc z_zcalloc +# define zcfree z_zcfree +# endif +# define zlibCompileFlags z_zlibCompileFlags +# define zlibVersion z_zlibVersion + +/* all zlib typedefs in zlib.h and zconf.h */ +# define Byte z_Byte +# define Bytef z_Bytef +# define alloc_func z_alloc_func +# define charf z_charf +# define free_func z_free_func +# ifndef Z_SOLO +# define gzFile z_gzFile +# endif +# define gz_header z_gz_header +# define gz_headerp z_gz_headerp +# define in_func z_in_func +# define intf z_intf +# define out_func z_out_func +# define uInt z_uInt +# define uIntf z_uIntf +# define uLong z_uLong +# define uLongf z_uLongf +# define voidp z_voidp +# define voidpc z_voidpc +# define voidpf z_voidpf + +/* all zlib structs in zlib.h and zconf.h */ +# define gz_header_s z_gz_header_s +# define internal_state z_internal_state + +#endif + +#if defined(__MSDOS__) && !defined(MSDOS) +# define MSDOS +#endif +#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2) +# define OS2 +#endif +#if defined(_WINDOWS) && !defined(WINDOWS) +# define WINDOWS +#endif +#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__) +# ifndef WIN32 +# define WIN32 +# endif +#endif +#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32) +# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__) +# ifndef SYS16BIT +# define SYS16BIT +# endif +# endif +#endif + +/* + * Compile with -DMAXSEG_64K if the alloc function cannot allocate more + * than 64k bytes at a time (needed on systems with 16-bit int). + */ +#ifdef SYS16BIT +# define MAXSEG_64K +#endif +#ifdef MSDOS +# define UNALIGNED_OK +#endif + +#ifdef __STDC_VERSION__ +# ifndef STDC +# define STDC +# endif +# if __STDC_VERSION__ >= 199901L +# ifndef STDC99 +# define STDC99 +# endif +# endif +#endif +#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus)) +# define STDC +#endif +#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__)) +# define STDC +#endif +#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32)) +# define STDC +#endif +#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__)) +# define STDC +#endif + +#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */ +# define STDC +#endif + +#ifndef STDC +# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ +# define const /* note: need a more gentle solution here */ +# endif +#endif + +#if defined(ZLIB_CONST) && !defined(z_const) +# define z_const const +#else +# define z_const +#endif + +#ifdef Z_SOLO + typedef unsigned long z_size_t; +#else +# define z_longlong long long +# if defined(NO_SIZE_T) + typedef unsigned NO_SIZE_T z_size_t; +# elif defined(STDC) +# include + typedef size_t z_size_t; +# else + typedef unsigned long z_size_t; +# endif +# undef z_longlong +#endif + +/* Maximum value for memLevel in deflateInit2 */ +#ifndef MAX_MEM_LEVEL +# ifdef MAXSEG_64K +# define MAX_MEM_LEVEL 8 +# else +# define MAX_MEM_LEVEL 9 +# endif +#endif + +/* Maximum value for windowBits in deflateInit2 and inflateInit2. + * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files + * created by gzip. (Files created by minigzip can still be extracted by + * gzip.) + */ +#ifndef MAX_WBITS +# define MAX_WBITS 15 /* 32K LZ77 window */ +#endif + +/* The memory requirements for deflate are (in bytes): + (1 << (windowBits+2)) + (1 << (memLevel+9)) + that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + plus a few kilobytes for small objects. For example, if you want to reduce + the default memory requirements from 256K to 128K, compile with + make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + Of course this will generally degrade compression (there's no free lunch). + + The memory requirements for inflate are (in bytes) 1 << windowBits + that is, 32K for windowBits=15 (default value) plus about 7 kilobytes + for small objects. +*/ + + /* Type declarations */ + +#ifndef OF /* function prototypes */ +# ifdef STDC +# define OF(args) args +# else +# define OF(args) () +# endif +#endif + +#ifndef Z_ARG /* function prototypes for stdarg */ +# if defined(STDC) || defined(Z_HAVE_STDARG_H) +# define Z_ARG(args) args +# else +# define Z_ARG(args) () +# endif +#endif + +/* The following definitions for FAR are needed only for MSDOS mixed + * model programming (small or medium model with some far allocations). + * This was tested only with MSC; for other MSDOS compilers you may have + * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, + * just define FAR to be empty. + */ +#ifdef SYS16BIT +# if defined(M_I86SM) || defined(M_I86MM) + /* MSC small or medium model */ +# define SMALL_MEDIUM +# ifdef _MSC_VER +# define FAR _far +# else +# define FAR far +# endif +# endif +# if (defined(__SMALL__) || defined(__MEDIUM__)) + /* Turbo C small or medium model */ +# define SMALL_MEDIUM +# ifdef __BORLANDC__ +# define FAR _far +# else +# define FAR far +# endif +# endif +#endif + +#if defined(WINDOWS) || defined(WIN32) + /* If building or using zlib as a DLL, define ZLIB_DLL. + * This is not mandatory, but it offers a little performance increase. + */ +# ifdef ZLIB_DLL +# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500)) +# ifdef ZLIB_INTERNAL +# define ZEXTERN extern __declspec(dllexport) +# else +# define ZEXTERN extern __declspec(dllimport) +# endif +# endif +# endif /* ZLIB_DLL */ + /* If building or using zlib with the WINAPI/WINAPIV calling convention, + * define ZLIB_WINAPI. + * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI. + */ +# ifdef ZLIB_WINAPI +# ifdef FAR +# undef FAR +# endif +# include + /* No need for _export, use ZLIB.DEF instead. */ + /* For complete Windows compatibility, use WINAPI, not __stdcall. */ +# define ZEXPORT WINAPI +# ifdef WIN32 +# define ZEXPORTVA WINAPIV +# else +# define ZEXPORTVA FAR CDECL +# endif +# endif +#endif + +#if defined (__BEOS__) +# ifdef ZLIB_DLL +# ifdef ZLIB_INTERNAL +# define ZEXPORT __declspec(dllexport) +# define ZEXPORTVA __declspec(dllexport) +# else +# define ZEXPORT __declspec(dllimport) +# define ZEXPORTVA __declspec(dllimport) +# endif +# endif +#endif + +#ifndef ZEXTERN +# define ZEXTERN extern +#endif +#ifndef ZEXPORT +# define ZEXPORT +#endif +#ifndef ZEXPORTVA +# define ZEXPORTVA +#endif + +#ifndef FAR +# define FAR +#endif + +#if !defined(__MACTYPES__) +typedef unsigned char Byte; /* 8 bits */ +#endif +typedef unsigned int uInt; /* 16 bits or more */ +typedef unsigned long uLong; /* 32 bits or more */ + +#ifdef SMALL_MEDIUM + /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ +# define Bytef Byte FAR +#else + typedef Byte FAR Bytef; +#endif +typedef char FAR charf; +typedef int FAR intf; +typedef uInt FAR uIntf; +typedef uLong FAR uLongf; + +#ifdef STDC + typedef void const *voidpc; + typedef void FAR *voidpf; + typedef void *voidp; +#else + typedef Byte const *voidpc; + typedef Byte FAR *voidpf; + typedef Byte *voidp; +#endif + +#if !defined(Z_U4) && !defined(Z_SOLO) && defined(STDC) +# include +# if (UINT_MAX == 0xffffffffUL) +# define Z_U4 unsigned +# elif (ULONG_MAX == 0xffffffffUL) +# define Z_U4 unsigned long +# elif (USHRT_MAX == 0xffffffffUL) +# define Z_U4 unsigned short +# endif +#endif + +#ifdef Z_U4 + typedef Z_U4 z_crc_t; +#else + typedef unsigned long z_crc_t; +#endif + +#ifdef HAVE_UNISTD_H /* may be set to #if 1 by ./configure */ +# define Z_HAVE_UNISTD_H +#endif + +#ifdef HAVE_STDARG_H /* may be set to #if 1 by ./configure */ +# define Z_HAVE_STDARG_H +#endif + +#ifdef STDC +# ifndef Z_SOLO +# include /* for off_t */ +# endif +#endif + +#if defined(STDC) || defined(Z_HAVE_STDARG_H) +# ifndef Z_SOLO +# include /* for va_list */ +# endif +#endif + +#ifdef _WIN32 +# ifndef Z_SOLO +# include /* for wchar_t */ +# endif +#endif + +/* a little trick to accommodate both "#define _LARGEFILE64_SOURCE" and + * "#define _LARGEFILE64_SOURCE 1" as requesting 64-bit operations, (even + * though the former does not conform to the LFS document), but considering + * both "#undef _LARGEFILE64_SOURCE" and "#define _LARGEFILE64_SOURCE 0" as + * equivalently requesting no 64-bit operations + */ +#if defined(_LARGEFILE64_SOURCE) && -_LARGEFILE64_SOURCE - -1 == 1 +# undef _LARGEFILE64_SOURCE +#endif + +#if defined(__WATCOMC__) && !defined(Z_HAVE_UNISTD_H) +# define Z_HAVE_UNISTD_H +#endif +#ifndef Z_SOLO +# if defined(Z_HAVE_UNISTD_H) || defined(_LARGEFILE64_SOURCE) +# include /* for SEEK_*, off_t, and _LFS64_LARGEFILE */ +# ifdef VMS +# include /* for off_t */ +# endif +# ifndef z_off_t +# define z_off_t off_t +# endif +# endif +#endif + +#if defined(_LFS64_LARGEFILE) && _LFS64_LARGEFILE-0 +# define Z_LFS64 +#endif + +#if defined(_LARGEFILE64_SOURCE) && defined(Z_LFS64) +# define Z_LARGE64 +#endif + +#if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS-0 == 64 && defined(Z_LFS64) +# define Z_WANT64 +#endif + +#if !defined(SEEK_SET) && !defined(Z_SOLO) +# define SEEK_SET 0 /* Seek from beginning of file. */ +# define SEEK_CUR 1 /* Seek from current position. */ +# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ +#endif + +#ifndef z_off_t +# define z_off_t long +#endif + +#if !defined(_WIN32) && defined(Z_LARGE64) +# define z_off64_t off64_t +#else +# if defined(_WIN32) && !defined(__GNUC__) && !defined(Z_SOLO) +# define z_off64_t __int64 +# else +# define z_off64_t z_off_t +# endif +#endif + +/* MVS linker does not support external names larger than 8 bytes */ +#if defined(__MVS__) + #pragma map(deflateInit_,"DEIN") + #pragma map(deflateInit2_,"DEIN2") + #pragma map(deflateEnd,"DEEND") + #pragma map(deflateBound,"DEBND") + #pragma map(inflateInit_,"ININ") + #pragma map(inflateInit2_,"ININ2") + #pragma map(inflateEnd,"INEND") + #pragma map(inflateSync,"INSY") + #pragma map(inflateSetDictionary,"INSEDI") + #pragma map(compressBound,"CMBND") + #pragma map(inflate_table,"INTABL") + #pragma map(inflate_fast,"INFA") + #pragma map(inflate_copyright,"INCOPY") +#endif + +#endif /* ZCONF_H */ diff --git a/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/prebuilt/zlib/inc/zlib.h b/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/prebuilt/zlib/inc/zlib.h new file mode 100644 index 0000000..f09cdaf --- /dev/null +++ b/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/prebuilt/zlib/inc/zlib.h @@ -0,0 +1,1912 @@ +/* zlib.h -- interface of the 'zlib' general purpose compression library + version 1.2.11, January 15th, 2017 + + Copyright (C) 1995-2017 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + + + The data format used by the zlib library is described by RFCs (Request for + Comments) 1950 to 1952 in the files http://tools.ietf.org/html/rfc1950 + (zlib format), rfc1951 (deflate format) and rfc1952 (gzip format). +*/ + +#ifndef ZLIB_H +#define ZLIB_H + +#include "zconf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ZLIB_VERSION "1.2.11" +#define ZLIB_VERNUM 0x12b0 +#define ZLIB_VER_MAJOR 1 +#define ZLIB_VER_MINOR 2 +#define ZLIB_VER_REVISION 11 +#define ZLIB_VER_SUBREVISION 0 + +/* + The 'zlib' compression library provides in-memory compression and + decompression functions, including integrity checks of the uncompressed data. + This version of the library supports only one compression method (deflation) + but other algorithms will be added later and will have the same stream + interface. + + Compression can be done in a single step if the buffers are large enough, + or can be done by repeated calls of the compression function. In the latter + case, the application must provide more input and/or consume the output + (providing more output space) before each call. + + The compressed data format used by default by the in-memory functions is + the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped + around a deflate stream, which is itself documented in RFC 1951. + + The library also supports reading and writing files in gzip (.gz) format + with an interface similar to that of stdio using the functions that start + with "gz". The gzip format is different from the zlib format. gzip is a + gzip wrapper, documented in RFC 1952, wrapped around a deflate stream. + + This library can optionally read and write gzip and raw deflate streams in + memory as well. + + The zlib format was designed to be compact and fast for use in memory + and on communications channels. The gzip format was designed for single- + file compression on file systems, has a larger header than zlib to maintain + directory information, and uses a different, slower check method than zlib. + + The library does not install any signal handler. The decoder checks + the consistency of the compressed data, so the library should never crash + even in the case of corrupted input. +*/ + +typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); +typedef void (*free_func) OF((voidpf opaque, voidpf address)); + +struct internal_state; + +typedef struct z_stream_s { + z_const Bytef *next_in; /* next input byte */ + uInt avail_in; /* number of bytes available at next_in */ + uLong total_in; /* total number of input bytes read so far */ + + Bytef *next_out; /* next output byte will go here */ + uInt avail_out; /* remaining free space at next_out */ + uLong total_out; /* total number of bytes output so far */ + + z_const char *msg; /* last error message, NULL if no error */ + struct internal_state FAR *state; /* not visible by applications */ + + alloc_func zalloc; /* used to allocate the internal state */ + free_func zfree; /* used to free the internal state */ + voidpf opaque; /* private data object passed to zalloc and zfree */ + + int data_type; /* best guess about the data type: binary or text + for deflate, or the decoding state for inflate */ + uLong adler; /* Adler-32 or CRC-32 value of the uncompressed data */ + uLong reserved; /* reserved for future use */ +} z_stream; + +typedef z_stream FAR *z_streamp; + +/* + gzip header information passed to and from zlib routines. See RFC 1952 + for more details on the meanings of these fields. +*/ +typedef struct gz_header_s { + int text; /* true if compressed data believed to be text */ + uLong time; /* modification time */ + int xflags; /* extra flags (not used when writing a gzip file) */ + int os; /* operating system */ + Bytef *extra; /* pointer to extra field or Z_NULL if none */ + uInt extra_len; /* extra field length (valid if extra != Z_NULL) */ + uInt extra_max; /* space at extra (only when reading header) */ + Bytef *name; /* pointer to zero-terminated file name or Z_NULL */ + uInt name_max; /* space at name (only when reading header) */ + Bytef *comment; /* pointer to zero-terminated comment or Z_NULL */ + uInt comm_max; /* space at comment (only when reading header) */ + int hcrc; /* true if there was or will be a header crc */ + int done; /* true when done reading gzip header (not used + when writing a gzip file) */ +} gz_header; + +typedef gz_header FAR *gz_headerp; + +/* + The application must update next_in and avail_in when avail_in has dropped + to zero. It must update next_out and avail_out when avail_out has dropped + to zero. The application must initialize zalloc, zfree and opaque before + calling the init function. All other fields are set by the compression + library and must not be updated by the application. + + The opaque value provided by the application will be passed as the first + parameter for calls of zalloc and zfree. This can be useful for custom + memory management. The compression library attaches no meaning to the + opaque value. + + zalloc must return Z_NULL if there is not enough memory for the object. + If zlib is used in a multi-threaded application, zalloc and zfree must be + thread safe. In that case, zlib is thread-safe. When zalloc and zfree are + Z_NULL on entry to the initialization function, they are set to internal + routines that use the standard library functions malloc() and free(). + + On 16-bit systems, the functions zalloc and zfree must be able to allocate + exactly 65536 bytes, but will not be required to allocate more than this if + the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, pointers + returned by zalloc for objects of exactly 65536 bytes *must* have their + offset normalized to zero. The default allocation function provided by this + library ensures this (see zutil.c). To reduce memory requirements and avoid + any allocation of 64K objects, at the expense of compression ratio, compile + the library with -DMAX_WBITS=14 (see zconf.h). + + The fields total_in and total_out can be used for statistics or progress + reports. After compression, total_in holds the total size of the + uncompressed data and may be saved for use by the decompressor (particularly + if the decompressor wants to decompress everything in a single step). +*/ + + /* constants */ + +#define Z_NO_FLUSH 0 +#define Z_PARTIAL_FLUSH 1 +#define Z_SYNC_FLUSH 2 +#define Z_FULL_FLUSH 3 +#define Z_FINISH 4 +#define Z_BLOCK 5 +#define Z_TREES 6 +/* Allowed flush values; see deflate() and inflate() below for details */ + +#define Z_OK 0 +#define Z_STREAM_END 1 +#define Z_NEED_DICT 2 +#define Z_ERRNO (-1) +#define Z_STREAM_ERROR (-2) +#define Z_DATA_ERROR (-3) +#define Z_MEM_ERROR (-4) +#define Z_BUF_ERROR (-5) +#define Z_VERSION_ERROR (-6) +/* Return codes for the compression/decompression functions. Negative values + * are errors, positive values are used for special but normal events. + */ + +#define Z_NO_COMPRESSION 0 +#define Z_BEST_SPEED 1 +#define Z_BEST_COMPRESSION 9 +#define Z_DEFAULT_COMPRESSION (-1) +/* compression levels */ + +#define Z_FILTERED 1 +#define Z_HUFFMAN_ONLY 2 +#define Z_RLE 3 +#define Z_FIXED 4 +#define Z_DEFAULT_STRATEGY 0 +/* compression strategy; see deflateInit2() below for details */ + +#define Z_BINARY 0 +#define Z_TEXT 1 +#define Z_ASCII Z_TEXT /* for compatibility with 1.2.2 and earlier */ +#define Z_UNKNOWN 2 +/* Possible values of the data_type field for deflate() */ + +#define Z_DEFLATED 8 +/* The deflate compression method (the only one supported in this version) */ + +#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ + +#define zlib_version zlibVersion() +/* for compatibility with versions < 1.0.2 */ + + + /* basic functions */ + +ZEXTERN const char * ZEXPORT zlibVersion OF((void)); +/* The application can compare zlibVersion and ZLIB_VERSION for consistency. + If the first character differs, the library code actually used is not + compatible with the zlib.h header file used by the application. This check + is automatically made by deflateInit and inflateInit. + */ + +/* +ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level)); + + Initializes the internal stream state for compression. The fields + zalloc, zfree and opaque must be initialized before by the caller. If + zalloc and zfree are set to Z_NULL, deflateInit updates them to use default + allocation functions. + + The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: + 1 gives best speed, 9 gives best compression, 0 gives no compression at all + (the input data is simply copied a block at a time). Z_DEFAULT_COMPRESSION + requests a default compromise between speed and compression (currently + equivalent to level 6). + + deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if level is not a valid compression level, or + Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible + with the version assumed by the caller (ZLIB_VERSION). msg is set to null + if there is no error message. deflateInit does not perform any compression: + this will be done by deflate(). +*/ + + +ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); +/* + deflate compresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce + some output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. deflate performs one or both of the + following actions: + + - Compress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in and avail_in are updated and + processing will resume at this point for the next call of deflate(). + + - Generate more output starting at next_out and update next_out and avail_out + accordingly. This action is forced if the parameter flush is non zero. + Forcing flush frequently degrades the compression ratio, so this parameter + should be set only when necessary. Some output may be provided even if + flush is zero. + + Before the call of deflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming more + output, and updating avail_in or avail_out accordingly; avail_out should + never be zero before the call. The application can consume the compressed + output when it wants, for example when the output buffer is full (avail_out + == 0), or after each call of deflate(). If deflate returns Z_OK and with + zero avail_out, it must be called again after making room in the output + buffer because there might be more output pending. See deflatePending(), + which can be used if desired to determine whether or not there is more ouput + in that case. + + Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to + decide how much data to accumulate before producing output, in order to + maximize compression. + + If the parameter flush is set to Z_SYNC_FLUSH, all pending output is + flushed to the output buffer and the output is aligned on a byte boundary, so + that the decompressor can get all input data available so far. (In + particular avail_in is zero after the call if enough output space has been + provided before the call.) Flushing may degrade compression for some + compression algorithms and so it should be used only when necessary. This + completes the current deflate block and follows it with an empty stored block + that is three bits plus filler bits to the next byte, followed by four bytes + (00 00 ff ff). + + If flush is set to Z_PARTIAL_FLUSH, all pending output is flushed to the + output buffer, but the output is not aligned to a byte boundary. All of the + input data so far will be available to the decompressor, as for Z_SYNC_FLUSH. + This completes the current deflate block and follows it with an empty fixed + codes block that is 10 bits long. This assures that enough bytes are output + in order for the decompressor to finish the block before the empty fixed + codes block. + + If flush is set to Z_BLOCK, a deflate block is completed and emitted, as + for Z_SYNC_FLUSH, but the output is not aligned on a byte boundary, and up to + seven bits of the current block are held to be written as the next byte after + the next deflate block is completed. In this case, the decompressor may not + be provided enough bits at this point in order to complete decompression of + the data provided so far to the compressor. It may need to wait for the next + block to be emitted. This is for advanced applications that need to control + the emission of deflate blocks. + + If flush is set to Z_FULL_FLUSH, all output is flushed as with + Z_SYNC_FLUSH, and the compression state is reset so that decompression can + restart from this point if previous compressed data has been damaged or if + random access is desired. Using Z_FULL_FLUSH too often can seriously degrade + compression. + + If deflate returns with avail_out == 0, this function must be called again + with the same value of the flush parameter and more output space (updated + avail_out), until the flush is complete (deflate returns with non-zero + avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that + avail_out is greater than six to avoid repeated flush markers due to + avail_out == 0 on return. + + If the parameter flush is set to Z_FINISH, pending input is processed, + pending output is flushed and deflate returns with Z_STREAM_END if there was + enough output space. If deflate returns with Z_OK or Z_BUF_ERROR, this + function must be called again with Z_FINISH and more output space (updated + avail_out) but no more input data, until it returns with Z_STREAM_END or an + error. After deflate has returned Z_STREAM_END, the only possible operations + on the stream are deflateReset or deflateEnd. + + Z_FINISH can be used in the first deflate call after deflateInit if all the + compression is to be done in a single step. In order to complete in one + call, avail_out must be at least the value returned by deflateBound (see + below). Then deflate is guaranteed to return Z_STREAM_END. If not enough + output space is provided, deflate will not return Z_STREAM_END, and it must + be called again as described above. + + deflate() sets strm->adler to the Adler-32 checksum of all input read + so far (that is, total_in bytes). If a gzip stream is being generated, then + strm->adler will be the CRC-32 checksum of the input read so far. (See + deflateInit2 below.) + + deflate() may update strm->data_type if it can make a good guess about + the input data type (Z_BINARY or Z_TEXT). If in doubt, the data is + considered binary. This field is only for information purposes and does not + affect the compression algorithm in any manner. + + deflate() returns Z_OK if some progress has been made (more input + processed or more output produced), Z_STREAM_END if all input has been + consumed and all output has been produced (only when flush is set to + Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example + if next_in or next_out was Z_NULL or the state was inadvertently written over + by the application), or Z_BUF_ERROR if no progress is possible (for example + avail_in or avail_out was zero). Note that Z_BUF_ERROR is not fatal, and + deflate() can be called again with more input and more output space to + continue compressing. +*/ + + +ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any pending + output. + + deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the + stream state was inconsistent, Z_DATA_ERROR if the stream was freed + prematurely (some input or output was discarded). In the error case, msg + may be set but then points to a static string (which must not be + deallocated). +*/ + + +/* +ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm)); + + Initializes the internal stream state for decompression. The fields + next_in, avail_in, zalloc, zfree and opaque must be initialized before by + the caller. In the current version of inflate, the provided input is not + read or consumed. The allocation of a sliding window will be deferred to + the first call of inflate (if the decompression does not complete on the + first call). If zalloc and zfree are set to Z_NULL, inflateInit updates + them to use default allocation functions. + + inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_VERSION_ERROR if the zlib library version is incompatible with the + version assumed by the caller, or Z_STREAM_ERROR if the parameters are + invalid, such as a null pointer to the structure. msg is set to null if + there is no error message. inflateInit does not perform any decompression. + Actual decompression will be done by inflate(). So next_in, and avail_in, + next_out, and avail_out are unused and unchanged. The current + implementation of inflateInit() does not process any header information -- + that is deferred until inflate() is called. +*/ + + +ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); +/* + inflate decompresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce + some output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. inflate performs one or both of the + following actions: + + - Decompress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), then next_in and avail_in are updated + accordingly, and processing will resume at this point for the next call of + inflate(). + + - Generate more output starting at next_out and update next_out and avail_out + accordingly. inflate() provides as much output as possible, until there is + no more input data or no more space in the output buffer (see below about + the flush parameter). + + Before the call of inflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming more + output, and updating the next_* and avail_* values accordingly. If the + caller of inflate() does not provide both available input and available + output space, it is possible that there will be no progress made. The + application can consume the uncompressed output when it wants, for example + when the output buffer is full (avail_out == 0), or after each call of + inflate(). If inflate returns Z_OK and with zero avail_out, it must be + called again after making room in the output buffer because there might be + more output pending. + + The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, Z_FINISH, + Z_BLOCK, or Z_TREES. Z_SYNC_FLUSH requests that inflate() flush as much + output as possible to the output buffer. Z_BLOCK requests that inflate() + stop if and when it gets to the next deflate block boundary. When decoding + the zlib or gzip format, this will cause inflate() to return immediately + after the header and before the first block. When doing a raw inflate, + inflate() will go ahead and process the first block, and will return when it + gets to the end of that block, or when it runs out of data. + + The Z_BLOCK option assists in appending to or combining deflate streams. + To assist in this, on return inflate() always sets strm->data_type to the + number of unused bits in the last byte taken from strm->next_in, plus 64 if + inflate() is currently decoding the last block in the deflate stream, plus + 128 if inflate() returned immediately after decoding an end-of-block code or + decoding the complete header up to just before the first byte of the deflate + stream. The end-of-block will not be indicated until all of the uncompressed + data from that block has been written to strm->next_out. The number of + unused bits may in general be greater than seven, except when bit 7 of + data_type is set, in which case the number of unused bits will be less than + eight. data_type is set as noted here every time inflate() returns for all + flush options, and so can be used to determine the amount of currently + consumed input in bits. + + The Z_TREES option behaves as Z_BLOCK does, but it also returns when the + end of each deflate block header is reached, before any actual data in that + block is decoded. This allows the caller to determine the length of the + deflate block header for later use in random access within a deflate block. + 256 is added to the value of strm->data_type when inflate() returns + immediately after reaching the end of the deflate block header. + + inflate() should normally be called until it returns Z_STREAM_END or an + error. However if all decompression is to be performed in a single step (a + single call of inflate), the parameter flush should be set to Z_FINISH. In + this case all pending input is processed and all pending output is flushed; + avail_out must be large enough to hold all of the uncompressed data for the + operation to complete. (The size of the uncompressed data may have been + saved by the compressor for this purpose.) The use of Z_FINISH is not + required to perform an inflation in one step. However it may be used to + inform inflate that a faster approach can be used for the single inflate() + call. Z_FINISH also informs inflate to not maintain a sliding window if the + stream completes, which reduces inflate's memory footprint. If the stream + does not complete, either because not all of the stream is provided or not + enough output space is provided, then a sliding window will be allocated and + inflate() can be called again to continue the operation as if Z_NO_FLUSH had + been used. + + In this implementation, inflate() always flushes as much output as + possible to the output buffer, and always uses the faster approach on the + first call. So the effects of the flush parameter in this implementation are + on the return value of inflate() as noted below, when inflate() returns early + when Z_BLOCK or Z_TREES is used, and when inflate() avoids the allocation of + memory for a sliding window when Z_FINISH is used. + + If a preset dictionary is needed after this call (see inflateSetDictionary + below), inflate sets strm->adler to the Adler-32 checksum of the dictionary + chosen by the compressor and returns Z_NEED_DICT; otherwise it sets + strm->adler to the Adler-32 checksum of all output produced so far (that is, + total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described + below. At the end of the stream, inflate() checks that its computed Adler-32 + checksum is equal to that saved by the compressor and returns Z_STREAM_END + only if the checksum is correct. + + inflate() can decompress and check either zlib-wrapped or gzip-wrapped + deflate data. The header type is detected automatically, if requested when + initializing with inflateInit2(). Any information contained in the gzip + header is not retained unless inflateGetHeader() is used. When processing + gzip-wrapped deflate data, strm->adler32 is set to the CRC-32 of the output + produced so far. The CRC-32 is checked against the gzip trailer, as is the + uncompressed length, modulo 2^32. + + inflate() returns Z_OK if some progress has been made (more input processed + or more output produced), Z_STREAM_END if the end of the compressed data has + been reached and all uncompressed output has been produced, Z_NEED_DICT if a + preset dictionary is needed at this point, Z_DATA_ERROR if the input data was + corrupted (input stream not conforming to the zlib format or incorrect check + value, in which case strm->msg points to a string with a more specific + error), Z_STREAM_ERROR if the stream structure was inconsistent (for example + next_in or next_out was Z_NULL, or the state was inadvertently written over + by the application), Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR + if no progress was possible or if there was not enough room in the output + buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and + inflate() can be called again with more input and more output space to + continue decompressing. If Z_DATA_ERROR is returned, the application may + then call inflateSync() to look for a good compression block if a partial + recovery of the data is to be attempted. +*/ + + +ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any pending + output. + + inflateEnd returns Z_OK if success, or Z_STREAM_ERROR if the stream state + was inconsistent. +*/ + + + /* Advanced functions */ + +/* + The following functions are needed only in some special applications. +*/ + +/* +ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm, + int level, + int method, + int windowBits, + int memLevel, + int strategy)); + + This is another version of deflateInit with more compression options. The + fields next_in, zalloc, zfree and opaque must be initialized before by the + caller. + + The method parameter is the compression method. It must be Z_DEFLATED in + this version of the library. + + The windowBits parameter is the base two logarithm of the window size + (the size of the history buffer). It should be in the range 8..15 for this + version of the library. Larger values of this parameter result in better + compression at the expense of memory usage. The default value is 15 if + deflateInit is used instead. + + For the current implementation of deflate(), a windowBits value of 8 (a + window size of 256 bytes) is not supported. As a result, a request for 8 + will result in 9 (a 512-byte window). In that case, providing 8 to + inflateInit2() will result in an error when the zlib header with 9 is + checked against the initialization of inflate(). The remedy is to not use 8 + with deflateInit2() with this initialization, or at least in that case use 9 + with inflateInit2(). + + windowBits can also be -8..-15 for raw deflate. In this case, -windowBits + determines the window size. deflate() will then generate raw deflate data + with no zlib header or trailer, and will not compute a check value. + + windowBits can also be greater than 15 for optional gzip encoding. Add + 16 to windowBits to write a simple gzip header and trailer around the + compressed data instead of a zlib wrapper. The gzip header will have no + file name, no extra data, no comment, no modification time (set to zero), no + header crc, and the operating system will be set to the appropriate value, + if the operating system was determined at compile time. If a gzip stream is + being written, strm->adler is a CRC-32 instead of an Adler-32. + + For raw deflate or gzip encoding, a request for a 256-byte window is + rejected as invalid, since only the zlib header provides a means of + transmitting the window size to the decompressor. + + The memLevel parameter specifies how much memory should be allocated + for the internal compression state. memLevel=1 uses minimum memory but is + slow and reduces compression ratio; memLevel=9 uses maximum memory for + optimal speed. The default value is 8. See zconf.h for total memory usage + as a function of windowBits and memLevel. + + The strategy parameter is used to tune the compression algorithm. Use the + value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a + filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no + string match), or Z_RLE to limit match distances to one (run-length + encoding). Filtered data consists mostly of small values with a somewhat + random distribution. In this case, the compression algorithm is tuned to + compress them better. The effect of Z_FILTERED is to force more Huffman + coding and less string matching; it is somewhat intermediate between + Z_DEFAULT_STRATEGY and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as + fast as Z_HUFFMAN_ONLY, but give better compression for PNG image data. The + strategy parameter only affects the compression ratio but not the + correctness of the compressed output even if it is not set appropriately. + Z_FIXED prevents the use of dynamic Huffman codes, allowing for a simpler + decoder for special applications. + + deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if any parameter is invalid (such as an invalid + method), or Z_VERSION_ERROR if the zlib library version (zlib_version) is + incompatible with the version assumed by the caller (ZLIB_VERSION). msg is + set to null if there is no error message. deflateInit2 does not perform any + compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the compression dictionary from the given byte sequence + without producing any compressed output. When using the zlib format, this + function must be called immediately after deflateInit, deflateInit2 or + deflateReset, and before any call of deflate. When doing raw deflate, this + function must be called either before any call of deflate, or immediately + after the completion of a deflate block, i.e. after all input has been + consumed and all output has been delivered when using any of the flush + options Z_BLOCK, Z_PARTIAL_FLUSH, Z_SYNC_FLUSH, or Z_FULL_FLUSH. The + compressor and decompressor must use exactly the same dictionary (see + inflateSetDictionary). + + The dictionary should consist of strings (byte sequences) that are likely + to be encountered later in the data to be compressed, with the most commonly + used strings preferably put towards the end of the dictionary. Using a + dictionary is most useful when the data to be compressed is short and can be + predicted with good accuracy; the data can then be compressed better than + with the default empty dictionary. + + Depending on the size of the compression data structures selected by + deflateInit or deflateInit2, a part of the dictionary may in effect be + discarded, for example if the dictionary is larger than the window size + provided in deflateInit or deflateInit2. Thus the strings most likely to be + useful should be put at the end of the dictionary, not at the front. In + addition, the current implementation of deflate will use at most the window + size minus 262 bytes of the provided dictionary. + + Upon return of this function, strm->adler is set to the Adler-32 value + of the dictionary; the decompressor may later use this value to determine + which dictionary has been used by the compressor. (The Adler-32 value + applies to the whole dictionary even if only a subset of the dictionary is + actually used by the compressor.) If a raw deflate was requested, then the + Adler-32 value is not computed and strm->adler is not set. + + deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a + parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is + inconsistent (for example if deflate has already been called for this stream + or if not at a block boundary for raw deflate). deflateSetDictionary does + not perform any compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateGetDictionary OF((z_streamp strm, + Bytef *dictionary, + uInt *dictLength)); +/* + Returns the sliding dictionary being maintained by deflate. dictLength is + set to the number of bytes in the dictionary, and that many bytes are copied + to dictionary. dictionary must have enough space, where 32768 bytes is + always enough. If deflateGetDictionary() is called with dictionary equal to + Z_NULL, then only the dictionary length is returned, and nothing is copied. + Similary, if dictLength is Z_NULL, then it is not set. + + deflateGetDictionary() may return a length less than the window size, even + when more than the window size in input has been provided. It may return up + to 258 bytes less in that case, due to how zlib's implementation of deflate + manages the sliding window and lookahead for matches, where matches can be + up to 258 bytes long. If the application needs the last window-size bytes of + input, then that would need to be saved by the application outside of zlib. + + deflateGetDictionary returns Z_OK on success, or Z_STREAM_ERROR if the + stream state is inconsistent. +*/ + +ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when several compression strategies will be + tried, for example when there are several ways of pre-processing the input + data with a filter. The streams that will be discarded should then be freed + by calling deflateEnd. Note that deflateCopy duplicates the internal + compression state which can be quite large, so this strategy is slow and can + consume lots of memory. + + deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being Z_NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm)); +/* + This function is equivalent to deflateEnd followed by deflateInit, but + does not free and reallocate the internal compression state. The stream + will leave the compression level and any other attributes that may have been + set unchanged. + + deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL). +*/ + +ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm, + int level, + int strategy)); +/* + Dynamically update the compression level and compression strategy. The + interpretation of level and strategy is as in deflateInit2(). This can be + used to switch between compression and straight copy of the input data, or + to switch to a different kind of input data requiring a different strategy. + If the compression approach (which is a function of the level) or the + strategy is changed, and if any input has been consumed in a previous + deflate() call, then the input available so far is compressed with the old + level and strategy using deflate(strm, Z_BLOCK). There are three approaches + for the compression levels 0, 1..3, and 4..9 respectively. The new level + and strategy will take effect at the next call of deflate(). + + If a deflate(strm, Z_BLOCK) is performed by deflateParams(), and it does + not have enough output space to complete, then the parameter change will not + take effect. In this case, deflateParams() can be called again with the + same parameters and more output space to try again. + + In order to assure a change in the parameters on the first try, the + deflate stream should be flushed using deflate() with Z_BLOCK or other flush + request until strm.avail_out is not zero, before calling deflateParams(). + Then no more input data should be provided before the deflateParams() call. + If this is done, the old level and strategy will be applied to the data + compressed before deflateParams(), and the new level and strategy will be + applied to the the data compressed after deflateParams(). + + deflateParams returns Z_OK on success, Z_STREAM_ERROR if the source stream + state was inconsistent or if a parameter was invalid, or Z_BUF_ERROR if + there was not enough output space to complete the compression of the + available input data before a change in the strategy or approach. Note that + in the case of a Z_BUF_ERROR, the parameters are not changed. A return + value of Z_BUF_ERROR is not fatal, in which case deflateParams() can be + retried with more output space. +*/ + +ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm, + int good_length, + int max_lazy, + int nice_length, + int max_chain)); +/* + Fine tune deflate's internal compression parameters. This should only be + used by someone who understands the algorithm used by zlib's deflate for + searching for the best matching string, and even then only by the most + fanatic optimizer trying to squeeze out the last compressed bit for their + specific input data. Read the deflate.c source code for the meaning of the + max_lazy, good_length, nice_length, and max_chain parameters. + + deflateTune() can be called after deflateInit() or deflateInit2(), and + returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream. + */ + +ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm, + uLong sourceLen)); +/* + deflateBound() returns an upper bound on the compressed size after + deflation of sourceLen bytes. It must be called after deflateInit() or + deflateInit2(), and after deflateSetHeader(), if used. This would be used + to allocate an output buffer for deflation in a single pass, and so would be + called before deflate(). If that first deflate() call is provided the + sourceLen input bytes, an output buffer allocated to the size returned by + deflateBound(), and the flush value Z_FINISH, then deflate() is guaranteed + to return Z_STREAM_END. Note that it is possible for the compressed size to + be larger than the value returned by deflateBound() if flush options other + than Z_FINISH or Z_NO_FLUSH are used. +*/ + +ZEXTERN int ZEXPORT deflatePending OF((z_streamp strm, + unsigned *pending, + int *bits)); +/* + deflatePending() returns the number of bytes and bits of output that have + been generated, but not yet provided in the available output. The bytes not + provided would be due to the available output space having being consumed. + The number of bits of output not provided are between 0 and 7, where they + await more bits to join them in order to fill out a full byte. If pending + or bits are Z_NULL, then those values are not set. + + deflatePending returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. + */ + +ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + deflatePrime() inserts bits in the deflate output stream. The intent + is that this function is used to start off the deflate output with the bits + leftover from a previous deflate stream when appending to it. As such, this + function can only be used for raw deflate, and must be used before the first + deflate() call after a deflateInit2() or deflateReset(). bits must be less + than or equal to 16, and that many of the least significant bits of value + will be inserted in the output. + + deflatePrime returns Z_OK if success, Z_BUF_ERROR if there was not enough + room in the internal buffer to insert the bits, or Z_STREAM_ERROR if the + source stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm, + gz_headerp head)); +/* + deflateSetHeader() provides gzip header information for when a gzip + stream is requested by deflateInit2(). deflateSetHeader() may be called + after deflateInit2() or deflateReset() and before the first call of + deflate(). The text, time, os, extra field, name, and comment information + in the provided gz_header structure are written to the gzip header (xflag is + ignored -- the extra flags are set according to the compression level). The + caller must assure that, if not Z_NULL, name and comment are terminated with + a zero byte, and that if extra is not Z_NULL, that extra_len bytes are + available there. If hcrc is true, a gzip header crc is included. Note that + the current versions of the command-line version of gzip (up through version + 1.3.x) do not support header crc's, and will report that it is a "multi-part + gzip file" and give up. + + If deflateSetHeader is not used, the default gzip header has text false, + the time set to zero, and os set to 255, with no extra, name, or comment + fields. The gzip header is returned to the default state by deflateReset(). + + deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm, + int windowBits)); + + This is another version of inflateInit with an extra parameter. The + fields next_in, avail_in, zalloc, zfree and opaque must be initialized + before by the caller. + + The windowBits parameter is the base two logarithm of the maximum window + size (the size of the history buffer). It should be in the range 8..15 for + this version of the library. The default value is 15 if inflateInit is used + instead. windowBits must be greater than or equal to the windowBits value + provided to deflateInit2() while compressing, or it must be equal to 15 if + deflateInit2() was not used. If a compressed stream with a larger window + size is given as input, inflate() will return with the error code + Z_DATA_ERROR instead of trying to allocate a larger window. + + windowBits can also be zero to request that inflate use the window size in + the zlib header of the compressed stream. + + windowBits can also be -8..-15 for raw inflate. In this case, -windowBits + determines the window size. inflate() will then process raw deflate data, + not looking for a zlib or gzip header, not generating a check value, and not + looking for any check values for comparison at the end of the stream. This + is for use with other formats that use the deflate compressed data format + such as zip. Those formats provide their own check values. If a custom + format is developed using the raw deflate format for compressed data, it is + recommended that a check value such as an Adler-32 or a CRC-32 be applied to + the uncompressed data as is done in the zlib, gzip, and zip formats. For + most applications, the zlib format should be used as is. Note that comments + above on the use in deflateInit2() applies to the magnitude of windowBits. + + windowBits can also be greater than 15 for optional gzip decoding. Add + 32 to windowBits to enable zlib and gzip decoding with automatic header + detection, or add 16 to decode only the gzip format (the zlib format will + return a Z_DATA_ERROR). If a gzip stream is being decoded, strm->adler is a + CRC-32 instead of an Adler-32. Unlike the gunzip utility and gzread() (see + below), inflate() will not automatically decode concatenated gzip streams. + inflate() will return Z_STREAM_END at the end of the gzip stream. The state + would need to be reset to continue decoding a subsequent gzip stream. + + inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_VERSION_ERROR if the zlib library version is incompatible with the + version assumed by the caller, or Z_STREAM_ERROR if the parameters are + invalid, such as a null pointer to the structure. msg is set to null if + there is no error message. inflateInit2 does not perform any decompression + apart from possibly reading the zlib header if present: actual decompression + will be done by inflate(). (So next_in and avail_in may be modified, but + next_out and avail_out are unused and unchanged.) The current implementation + of inflateInit2() does not process any header information -- that is + deferred until inflate() is called. +*/ + +ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the decompression dictionary from the given uncompressed byte + sequence. This function must be called immediately after a call of inflate, + if that call returned Z_NEED_DICT. The dictionary chosen by the compressor + can be determined from the Adler-32 value returned by that call of inflate. + The compressor and decompressor must use exactly the same dictionary (see + deflateSetDictionary). For raw inflate, this function can be called at any + time to set the dictionary. If the provided dictionary is smaller than the + window and there is already data in the window, then the provided dictionary + will amend what's there. The application must insure that the dictionary + that was used for compression is provided. + + inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a + parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is + inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the + expected one (incorrect Adler-32 value). inflateSetDictionary does not + perform any decompression: this will be done by subsequent calls of + inflate(). +*/ + +ZEXTERN int ZEXPORT inflateGetDictionary OF((z_streamp strm, + Bytef *dictionary, + uInt *dictLength)); +/* + Returns the sliding dictionary being maintained by inflate. dictLength is + set to the number of bytes in the dictionary, and that many bytes are copied + to dictionary. dictionary must have enough space, where 32768 bytes is + always enough. If inflateGetDictionary() is called with dictionary equal to + Z_NULL, then only the dictionary length is returned, and nothing is copied. + Similary, if dictLength is Z_NULL, then it is not set. + + inflateGetDictionary returns Z_OK on success, or Z_STREAM_ERROR if the + stream state is inconsistent. +*/ + +ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm)); +/* + Skips invalid compressed data until a possible full flush point (see above + for the description of deflate with Z_FULL_FLUSH) can be found, or until all + available input is skipped. No output is provided. + + inflateSync searches for a 00 00 FF FF pattern in the compressed data. + All full flush points have this pattern, but not all occurrences of this + pattern are full flush points. + + inflateSync returns Z_OK if a possible full flush point has been found, + Z_BUF_ERROR if no more input was provided, Z_DATA_ERROR if no flush point + has been found, or Z_STREAM_ERROR if the stream structure was inconsistent. + In the success case, the application may save the current current value of + total_in which indicates where valid compressed data was found. In the + error case, the application may repeatedly call inflateSync, providing more + input each time, until success or end of the input data. +*/ + +ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when randomly accessing a large stream. The + first pass through the stream can periodically record the inflate state, + allowing restarting inflate at those points when randomly accessing the + stream. + + inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being Z_NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm)); +/* + This function is equivalent to inflateEnd followed by inflateInit, + but does not free and reallocate the internal decompression state. The + stream will keep attributes that may have been set by inflateInit2. + + inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL). +*/ + +ZEXTERN int ZEXPORT inflateReset2 OF((z_streamp strm, + int windowBits)); +/* + This function is the same as inflateReset, but it also permits changing + the wrap and window size requests. The windowBits parameter is interpreted + the same as it is for inflateInit2. If the window size is changed, then the + memory allocated for the window is freed, and the window will be reallocated + by inflate() if needed. + + inflateReset2 returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL), or if + the windowBits parameter is invalid. +*/ + +ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + This function inserts bits in the inflate input stream. The intent is + that this function is used to start inflating at a bit position in the + middle of a byte. The provided bits will be used before any bytes are used + from next_in. This function should only be used with raw inflate, and + should be used before the first inflate() call after inflateInit2() or + inflateReset(). bits must be less than or equal to 16, and that many of the + least significant bits of value will be inserted in the input. + + If bits is negative, then the input stream bit buffer is emptied. Then + inflatePrime() can be called again to put bits in the buffer. This is used + to clear out bits leftover after feeding inflate a block description prior + to feeding inflate codes. + + inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +ZEXTERN long ZEXPORT inflateMark OF((z_streamp strm)); +/* + This function returns two values, one in the lower 16 bits of the return + value, and the other in the remaining upper bits, obtained by shifting the + return value down 16 bits. If the upper value is -1 and the lower value is + zero, then inflate() is currently decoding information outside of a block. + If the upper value is -1 and the lower value is non-zero, then inflate is in + the middle of a stored block, with the lower value equaling the number of + bytes from the input remaining to copy. If the upper value is not -1, then + it is the number of bits back from the current bit position in the input of + the code (literal or length/distance pair) currently being processed. In + that case the lower value is the number of bytes already emitted for that + code. + + A code is being processed if inflate is waiting for more input to complete + decoding of the code, or if it has completed decoding but is waiting for + more output space to write the literal or match data. + + inflateMark() is used to mark locations in the input data for random + access, which may be at bit positions, and to note those cases where the + output of a code may span boundaries of random access blocks. The current + location in the input stream can be determined from avail_in and data_type + as noted in the description for the Z_BLOCK flush parameter for inflate. + + inflateMark returns the value noted above, or -65536 if the provided + source stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm, + gz_headerp head)); +/* + inflateGetHeader() requests that gzip header information be stored in the + provided gz_header structure. inflateGetHeader() may be called after + inflateInit2() or inflateReset(), and before the first call of inflate(). + As inflate() processes the gzip stream, head->done is zero until the header + is completed, at which time head->done is set to one. If a zlib stream is + being decoded, then head->done is set to -1 to indicate that there will be + no gzip header information forthcoming. Note that Z_BLOCK or Z_TREES can be + used to force inflate() to return immediately after header processing is + complete and before any actual data is decompressed. + + The text, time, xflags, and os fields are filled in with the gzip header + contents. hcrc is set to true if there is a header CRC. (The header CRC + was valid if done is set to one.) If extra is not Z_NULL, then extra_max + contains the maximum number of bytes to write to extra. Once done is true, + extra_len contains the actual extra field length, and extra contains the + extra field, or that field truncated if extra_max is less than extra_len. + If name is not Z_NULL, then up to name_max characters are written there, + terminated with a zero unless the length is greater than name_max. If + comment is not Z_NULL, then up to comm_max characters are written there, + terminated with a zero unless the length is greater than comm_max. When any + of extra, name, or comment are not Z_NULL and the respective field is not + present in the header, then that field is set to Z_NULL to signal its + absence. This allows the use of deflateSetHeader() with the returned + structure to duplicate the header. However if those fields are set to + allocated memory, then the application will need to save those pointers + elsewhere so that they can be eventually freed. + + If inflateGetHeader is not used, then the header information is simply + discarded. The header is always checked for validity, including the header + CRC if present. inflateReset() will reset the process to discard the header + information. The application would need to call inflateGetHeader() again to + retrieve the header from the next gzip stream. + + inflateGetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits, + unsigned char FAR *window)); + + Initialize the internal stream state for decompression using inflateBack() + calls. The fields zalloc, zfree and opaque in strm must be initialized + before the call. If zalloc and zfree are Z_NULL, then the default library- + derived memory allocation routines are used. windowBits is the base two + logarithm of the window size, in the range 8..15. window is a caller + supplied buffer of that size. Except for special applications where it is + assured that deflate was used with small window sizes, windowBits must be 15 + and a 32K byte window must be supplied to be able to decompress general + deflate streams. + + See inflateBack() for the usage of these routines. + + inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of + the parameters are invalid, Z_MEM_ERROR if the internal state could not be + allocated, or Z_VERSION_ERROR if the version of the library does not match + the version of the header file. +*/ + +typedef unsigned (*in_func) OF((void FAR *, + z_const unsigned char FAR * FAR *)); +typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned)); + +ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm, + in_func in, void FAR *in_desc, + out_func out, void FAR *out_desc)); +/* + inflateBack() does a raw inflate with a single call using a call-back + interface for input and output. This is potentially more efficient than + inflate() for file i/o applications, in that it avoids copying between the + output and the sliding window by simply making the window itself the output + buffer. inflate() can be faster on modern CPUs when used with large + buffers. inflateBack() trusts the application to not change the output + buffer passed by the output function, at least until inflateBack() returns. + + inflateBackInit() must be called first to allocate the internal state + and to initialize the state with the user-provided window buffer. + inflateBack() may then be used multiple times to inflate a complete, raw + deflate stream with each call. inflateBackEnd() is then called to free the + allocated state. + + A raw deflate stream is one with no zlib or gzip header or trailer. + This routine would normally be used in a utility that reads zip or gzip + files and writes out uncompressed files. The utility would decode the + header and process the trailer on its own, hence this routine expects only + the raw deflate stream to decompress. This is different from the default + behavior of inflate(), which expects a zlib header and trailer around the + deflate stream. + + inflateBack() uses two subroutines supplied by the caller that are then + called by inflateBack() for input and output. inflateBack() calls those + routines until it reads a complete deflate stream and writes out all of the + uncompressed data, or until it encounters an error. The function's + parameters and return types are defined above in the in_func and out_func + typedefs. inflateBack() will call in(in_desc, &buf) which should return the + number of bytes of provided input, and a pointer to that input in buf. If + there is no input available, in() must return zero -- buf is ignored in that + case -- and inflateBack() will return a buffer error. inflateBack() will + call out(out_desc, buf, len) to write the uncompressed data buf[0..len-1]. + out() should return zero on success, or non-zero on failure. If out() + returns non-zero, inflateBack() will return with an error. Neither in() nor + out() are permitted to change the contents of the window provided to + inflateBackInit(), which is also the buffer that out() uses to write from. + The length written by out() will be at most the window size. Any non-zero + amount of input may be provided by in(). + + For convenience, inflateBack() can be provided input on the first call by + setting strm->next_in and strm->avail_in. If that input is exhausted, then + in() will be called. Therefore strm->next_in must be initialized before + calling inflateBack(). If strm->next_in is Z_NULL, then in() will be called + immediately for input. If strm->next_in is not Z_NULL, then strm->avail_in + must also be initialized, and then if strm->avail_in is not zero, input will + initially be taken from strm->next_in[0 .. strm->avail_in - 1]. + + The in_desc and out_desc parameters of inflateBack() is passed as the + first parameter of in() and out() respectively when they are called. These + descriptors can be optionally used to pass any information that the caller- + supplied in() and out() functions need to do their job. + + On return, inflateBack() will set strm->next_in and strm->avail_in to + pass back any unused input that was provided by the last in() call. The + return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR + if in() or out() returned an error, Z_DATA_ERROR if there was a format error + in the deflate stream (in which case strm->msg is set to indicate the nature + of the error), or Z_STREAM_ERROR if the stream was not properly initialized. + In the case of Z_BUF_ERROR, an input or output error can be distinguished + using strm->next_in which will be Z_NULL only if in() returned an error. If + strm->next_in is not Z_NULL, then the Z_BUF_ERROR was due to out() returning + non-zero. (in() will always be called before out(), so strm->next_in is + assured to be defined if out() returns non-zero.) Note that inflateBack() + cannot return Z_OK. +*/ + +ZEXTERN int ZEXPORT inflateBackEnd OF((z_streamp strm)); +/* + All memory allocated by inflateBackInit() is freed. + + inflateBackEnd() returns Z_OK on success, or Z_STREAM_ERROR if the stream + state was inconsistent. +*/ + +ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void)); +/* Return flags indicating compile-time options. + + Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other: + 1.0: size of uInt + 3.2: size of uLong + 5.4: size of voidpf (pointer) + 7.6: size of z_off_t + + Compiler, assembler, and debug options: + 8: ZLIB_DEBUG + 9: ASMV or ASMINF -- use ASM code + 10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention + 11: 0 (reserved) + + One-time table building (smaller code, but not thread-safe if true): + 12: BUILDFIXED -- build static block decoding tables when needed + 13: DYNAMIC_CRC_TABLE -- build CRC calculation tables when needed + 14,15: 0 (reserved) + + Library content (indicates missing functionality): + 16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking + deflate code when not needed) + 17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect + and decode gzip streams (to avoid linking crc code) + 18-19: 0 (reserved) + + Operation variations (changes in library functionality): + 20: PKZIP_BUG_WORKAROUND -- slightly more permissive inflate + 21: FASTEST -- deflate algorithm with only one, lowest compression level + 22,23: 0 (reserved) + + The sprintf variant used by gzprintf (zero is best): + 24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format + 25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure! + 26: 0 = returns value, 1 = void -- 1 means inferred string length returned + + Remainder: + 27-31: 0 (reserved) + */ + +#ifndef Z_SOLO + + /* utility functions */ + +/* + The following utility functions are implemented on top of the basic + stream-oriented functions. To simplify the interface, some default options + are assumed (compression level and memory usage, standard memory allocation + functions). The source code of these utility functions can be modified if + you need special options. +*/ + +ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Compresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total size + of the destination buffer, which must be at least the value returned by + compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed data. compress() is equivalent to compress2() with a level + parameter of Z_DEFAULT_COMPRESSION. + + compress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer. +*/ + +ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen, + int level)); +/* + Compresses the source buffer into the destination buffer. The level + parameter has the same meaning as in deflateInit. sourceLen is the byte + length of the source buffer. Upon entry, destLen is the total size of the + destination buffer, which must be at least the value returned by + compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed data. + + compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, + Z_STREAM_ERROR if the level parameter is invalid. +*/ + +ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen)); +/* + compressBound() returns an upper bound on the compressed size after + compress() or compress2() on sourceLen bytes. It would be used before a + compress() or compress2() call to allocate the destination buffer. +*/ + +ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Decompresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total size + of the destination buffer, which must be large enough to hold the entire + uncompressed data. (The size of the uncompressed data must have been saved + previously by the compressor and transmitted to the decompressor by some + mechanism outside the scope of this compression library.) Upon exit, destLen + is the actual size of the uncompressed data. + + uncompress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete. In + the case where there is not enough room, uncompress() will fill the output + buffer with the uncompressed data up to that point. +*/ + +ZEXTERN int ZEXPORT uncompress2 OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong *sourceLen)); +/* + Same as uncompress, except that sourceLen is a pointer, where the + length of the source is *sourceLen. On return, *sourceLen is the number of + source bytes consumed. +*/ + + /* gzip file access functions */ + +/* + This library supports reading and writing files in gzip (.gz) format with + an interface similar to that of stdio, using the functions that start with + "gz". The gzip format is different from the zlib format. gzip is a gzip + wrapper, documented in RFC 1952, wrapped around a deflate stream. +*/ + +typedef struct gzFile_s *gzFile; /* semi-opaque gzip file descriptor */ + +/* +ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode)); + + Opens a gzip (.gz) file for reading or writing. The mode parameter is as + in fopen ("rb" or "wb") but can also include a compression level ("wb9") or + a strategy: 'f' for filtered data as in "wb6f", 'h' for Huffman-only + compression as in "wb1h", 'R' for run-length encoding as in "wb1R", or 'F' + for fixed code compression as in "wb9F". (See the description of + deflateInit2 for more information about the strategy parameter.) 'T' will + request transparent writing or appending with no compression and not using + the gzip format. + + "a" can be used instead of "w" to request that the gzip stream that will + be written be appended to the file. "+" will result in an error, since + reading and writing to the same gzip file is not supported. The addition of + "x" when writing will create the file exclusively, which fails if the file + already exists. On systems that support it, the addition of "e" when + reading or writing will set the flag to close the file on an execve() call. + + These functions, as well as gzip, will read and decode a sequence of gzip + streams in a file. The append function of gzopen() can be used to create + such a file. (Also see gzflush() for another way to do this.) When + appending, gzopen does not test whether the file begins with a gzip stream, + nor does it look for the end of the gzip streams to begin appending. gzopen + will simply append a gzip stream to the existing file. + + gzopen can be used to read a file which is not in gzip format; in this + case gzread will directly read from the file without decompression. When + reading, this will be detected automatically by looking for the magic two- + byte gzip header. + + gzopen returns NULL if the file could not be opened, if there was + insufficient memory to allocate the gzFile state, or if an invalid mode was + specified (an 'r', 'w', or 'a' was not provided, or '+' was provided). + errno can be checked to determine if the reason gzopen failed was that the + file could not be opened. +*/ + +ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode)); +/* + gzdopen associates a gzFile with the file descriptor fd. File descriptors + are obtained from calls like open, dup, creat, pipe or fileno (if the file + has been previously opened with fopen). The mode parameter is as in gzopen. + + The next call of gzclose on the returned gzFile will also close the file + descriptor fd, just like fclose(fdopen(fd, mode)) closes the file descriptor + fd. If you want to keep fd open, use fd = dup(fd_keep); gz = gzdopen(fd, + mode);. The duplicated descriptor should be saved to avoid a leak, since + gzdopen does not close fd if it fails. If you are using fileno() to get the + file descriptor from a FILE *, then you will have to use dup() to avoid + double-close()ing the file descriptor. Both gzclose() and fclose() will + close the associated file descriptor, so they need to have different file + descriptors. + + gzdopen returns NULL if there was insufficient memory to allocate the + gzFile state, if an invalid mode was specified (an 'r', 'w', or 'a' was not + provided, or '+' was provided), or if fd is -1. The file descriptor is not + used until the next gz* read, write, seek, or close operation, so gzdopen + will not detect if fd is invalid (unless fd is -1). +*/ + +ZEXTERN int ZEXPORT gzbuffer OF((gzFile file, unsigned size)); +/* + Set the internal buffer size used by this library's functions. The + default buffer size is 8192 bytes. This function must be called after + gzopen() or gzdopen(), and before any other calls that read or write the + file. The buffer memory allocation is always deferred to the first read or + write. Three times that size in buffer space is allocated. A larger buffer + size of, for example, 64K or 128K bytes will noticeably increase the speed + of decompression (reading). + + The new buffer size also affects the maximum length for gzprintf(). + + gzbuffer() returns 0 on success, or -1 on failure, such as being called + too late. +*/ + +ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy)); +/* + Dynamically update the compression level or strategy. See the description + of deflateInit2 for the meaning of these parameters. Previously provided + data is flushed before the parameter change. + + gzsetparams returns Z_OK if success, Z_STREAM_ERROR if the file was not + opened for writing, Z_ERRNO if there is an error writing the flushed data, + or Z_MEM_ERROR if there is a memory allocation error. +*/ + +ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len)); +/* + Reads the given number of uncompressed bytes from the compressed file. If + the input file is not in gzip format, gzread copies the given number of + bytes into the buffer directly from the file. + + After reaching the end of a gzip stream in the input, gzread will continue + to read, looking for another gzip stream. Any number of gzip streams may be + concatenated in the input file, and will all be decompressed by gzread(). + If something other than a gzip stream is encountered after a gzip stream, + that remaining trailing garbage is ignored (and no error is returned). + + gzread can be used to read a gzip file that is being concurrently written. + Upon reaching the end of the input, gzread will return with the available + data. If the error code returned by gzerror is Z_OK or Z_BUF_ERROR, then + gzclearerr can be used to clear the end of file indicator in order to permit + gzread to be tried again. Z_OK indicates that a gzip stream was completed + on the last gzread. Z_BUF_ERROR indicates that the input file ended in the + middle of a gzip stream. Note that gzread does not return -1 in the event + of an incomplete gzip stream. This error is deferred until gzclose(), which + will return Z_BUF_ERROR if the last gzread ended in the middle of a gzip + stream. Alternatively, gzerror can be used before gzclose to detect this + case. + + gzread returns the number of uncompressed bytes actually read, less than + len for end of file, or -1 for error. If len is too large to fit in an int, + then nothing is read, -1 is returned, and the error state is set to + Z_STREAM_ERROR. +*/ + +ZEXTERN z_size_t ZEXPORT gzfread OF((voidp buf, z_size_t size, z_size_t nitems, + gzFile file)); +/* + Read up to nitems items of size size from file to buf, otherwise operating + as gzread() does. This duplicates the interface of stdio's fread(), with + size_t request and return types. If the library defines size_t, then + z_size_t is identical to size_t. If not, then z_size_t is an unsigned + integer type that can contain a pointer. + + gzfread() returns the number of full items read of size size, or zero if + the end of the file was reached and a full item could not be read, or if + there was an error. gzerror() must be consulted if zero is returned in + order to determine if there was an error. If the multiplication of size and + nitems overflows, i.e. the product does not fit in a z_size_t, then nothing + is read, zero is returned, and the error state is set to Z_STREAM_ERROR. + + In the event that the end of file is reached and only a partial item is + available at the end, i.e. the remaining uncompressed data length is not a + multiple of size, then the final partial item is nevetheless read into buf + and the end-of-file flag is set. The length of the partial item read is not + provided, but could be inferred from the result of gztell(). This behavior + is the same as the behavior of fread() implementations in common libraries, + but it prevents the direct use of gzfread() to read a concurrently written + file, reseting and retrying on end-of-file, when size is not 1. +*/ + +ZEXTERN int ZEXPORT gzwrite OF((gzFile file, + voidpc buf, unsigned len)); +/* + Writes the given number of uncompressed bytes into the compressed file. + gzwrite returns the number of uncompressed bytes written or 0 in case of + error. +*/ + +ZEXTERN z_size_t ZEXPORT gzfwrite OF((voidpc buf, z_size_t size, + z_size_t nitems, gzFile file)); +/* + gzfwrite() writes nitems items of size size from buf to file, duplicating + the interface of stdio's fwrite(), with size_t request and return types. If + the library defines size_t, then z_size_t is identical to size_t. If not, + then z_size_t is an unsigned integer type that can contain a pointer. + + gzfwrite() returns the number of full items written of size size, or zero + if there was an error. If the multiplication of size and nitems overflows, + i.e. the product does not fit in a z_size_t, then nothing is written, zero + is returned, and the error state is set to Z_STREAM_ERROR. +*/ + +ZEXTERN int ZEXPORTVA gzprintf Z_ARG((gzFile file, const char *format, ...)); +/* + Converts, formats, and writes the arguments to the compressed file under + control of the format string, as in fprintf. gzprintf returns the number of + uncompressed bytes actually written, or a negative zlib error code in case + of error. The number of uncompressed bytes written is limited to 8191, or + one less than the buffer size given to gzbuffer(). The caller should assure + that this limit is not exceeded. If it is exceeded, then gzprintf() will + return an error (0) with nothing written. In this case, there may also be a + buffer overflow with unpredictable consequences, which is possible only if + zlib was compiled with the insecure functions sprintf() or vsprintf() + because the secure snprintf() or vsnprintf() functions were not available. + This can be determined using zlibCompileFlags(). +*/ + +ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s)); +/* + Writes the given null-terminated string to the compressed file, excluding + the terminating null character. + + gzputs returns the number of characters written, or -1 in case of error. +*/ + +ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len)); +/* + Reads bytes from the compressed file until len-1 characters are read, or a + newline character is read and transferred to buf, or an end-of-file + condition is encountered. If any characters are read or if len == 1, the + string is terminated with a null character. If no characters are read due + to an end-of-file or len < 1, then the buffer is left untouched. + + gzgets returns buf which is a null-terminated string, or it returns NULL + for end-of-file or in case of error. If there was an error, the contents at + buf are indeterminate. +*/ + +ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c)); +/* + Writes c, converted to an unsigned char, into the compressed file. gzputc + returns the value that was written, or -1 in case of error. +*/ + +ZEXTERN int ZEXPORT gzgetc OF((gzFile file)); +/* + Reads one byte from the compressed file. gzgetc returns this byte or -1 + in case of end of file or error. This is implemented as a macro for speed. + As such, it does not do all of the checking the other functions do. I.e. + it does not check to see if file is NULL, nor whether the structure file + points to has been clobbered or not. +*/ + +ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file)); +/* + Push one character back onto the stream to be read as the first character + on the next read. At least one character of push-back is allowed. + gzungetc() returns the character pushed, or -1 on failure. gzungetc() will + fail if c is -1, and may fail if a character has been pushed but not read + yet. If gzungetc is used immediately after gzopen or gzdopen, at least the + output buffer size of pushed characters is allowed. (See gzbuffer above.) + The pushed character will be discarded if the stream is repositioned with + gzseek() or gzrewind(). +*/ + +ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush)); +/* + Flushes all pending output into the compressed file. The parameter flush + is as in the deflate() function. The return value is the zlib error number + (see function gzerror below). gzflush is only permitted when writing. + + If the flush parameter is Z_FINISH, the remaining data is written and the + gzip stream is completed in the output. If gzwrite() is called again, a new + gzip stream will be started in the output. gzread() is able to read such + concatenated gzip streams. + + gzflush should be called only when strictly necessary because it will + degrade compression if called too often. +*/ + +/* +ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file, + z_off_t offset, int whence)); + + Sets the starting position for the next gzread or gzwrite on the given + compressed file. The offset represents a number of bytes in the + uncompressed data stream. The whence parameter is defined as in lseek(2); + the value SEEK_END is not supported. + + If the file is opened for reading, this function is emulated but can be + extremely slow. If the file is opened for writing, only forward seeks are + supported; gzseek then compresses a sequence of zeroes up to the new + starting position. + + gzseek returns the resulting offset location as measured in bytes from + the beginning of the uncompressed stream, or -1 in case of error, in + particular if the file is opened for writing and the new starting position + would be before the current position. +*/ + +ZEXTERN int ZEXPORT gzrewind OF((gzFile file)); +/* + Rewinds the given file. This function is supported only for reading. + + gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET) +*/ + +/* +ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file)); + + Returns the starting position for the next gzread or gzwrite on the given + compressed file. This position represents a number of bytes in the + uncompressed data stream, and is zero when starting, even if appending or + reading a gzip stream from the middle of a file using gzdopen(). + + gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR) +*/ + +/* +ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile file)); + + Returns the current offset in the file being read or written. This offset + includes the count of bytes that precede the gzip stream, for example when + appending or when using gzdopen() for reading. When reading, the offset + does not include as yet unused buffered input. This information can be used + for a progress indicator. On error, gzoffset() returns -1. +*/ + +ZEXTERN int ZEXPORT gzeof OF((gzFile file)); +/* + Returns true (1) if the end-of-file indicator has been set while reading, + false (0) otherwise. Note that the end-of-file indicator is set only if the + read tried to go past the end of the input, but came up short. Therefore, + just like feof(), gzeof() may return false even if there is no more data to + read, in the event that the last read request was for the exact number of + bytes remaining in the input file. This will happen if the input file size + is an exact multiple of the buffer size. + + If gzeof() returns true, then the read functions will return no more data, + unless the end-of-file indicator is reset by gzclearerr() and the input file + has grown since the previous end of file was detected. +*/ + +ZEXTERN int ZEXPORT gzdirect OF((gzFile file)); +/* + Returns true (1) if file is being copied directly while reading, or false + (0) if file is a gzip stream being decompressed. + + If the input file is empty, gzdirect() will return true, since the input + does not contain a gzip stream. + + If gzdirect() is used immediately after gzopen() or gzdopen() it will + cause buffers to be allocated to allow reading the file to determine if it + is a gzip file. Therefore if gzbuffer() is used, it should be called before + gzdirect(). + + When writing, gzdirect() returns true (1) if transparent writing was + requested ("wT" for the gzopen() mode), or false (0) otherwise. (Note: + gzdirect() is not needed when writing. Transparent writing must be + explicitly requested, so the application already knows the answer. When + linking statically, using gzdirect() will include all of the zlib code for + gzip file reading and decompression, which may not be desired.) +*/ + +ZEXTERN int ZEXPORT gzclose OF((gzFile file)); +/* + Flushes all pending output if necessary, closes the compressed file and + deallocates the (de)compression state. Note that once file is closed, you + cannot call gzerror with file, since its structures have been deallocated. + gzclose must not be called more than once on the same file, just as free + must not be called more than once on the same allocation. + + gzclose will return Z_STREAM_ERROR if file is not valid, Z_ERRNO on a + file operation error, Z_MEM_ERROR if out of memory, Z_BUF_ERROR if the + last read ended in the middle of a gzip stream, or Z_OK on success. +*/ + +ZEXTERN int ZEXPORT gzclose_r OF((gzFile file)); +ZEXTERN int ZEXPORT gzclose_w OF((gzFile file)); +/* + Same as gzclose(), but gzclose_r() is only for use when reading, and + gzclose_w() is only for use when writing or appending. The advantage to + using these instead of gzclose() is that they avoid linking in zlib + compression or decompression code that is not used when only reading or only + writing respectively. If gzclose() is used, then both compression and + decompression code will be included the application when linking to a static + zlib library. +*/ + +ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum)); +/* + Returns the error message for the last error which occurred on the given + compressed file. errnum is set to zlib error number. If an error occurred + in the file system and not in the compression library, errnum is set to + Z_ERRNO and the application may consult errno to get the exact error code. + + The application must not modify the returned string. Future calls to + this function may invalidate the previously returned string. If file is + closed, then the string previously returned by gzerror will no longer be + available. + + gzerror() should be used to distinguish errors from end-of-file for those + functions above that do not distinguish those cases in their return values. +*/ + +ZEXTERN void ZEXPORT gzclearerr OF((gzFile file)); +/* + Clears the error and end-of-file flags for file. This is analogous to the + clearerr() function in stdio. This is useful for continuing to read a gzip + file that is being written concurrently. +*/ + +#endif /* !Z_SOLO */ + + /* checksum functions */ + +/* + These functions are not related to compression but are exported + anyway because they might be useful in applications using the compression + library. +*/ + +ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len)); +/* + Update a running Adler-32 checksum with the bytes buf[0..len-1] and + return the updated checksum. If buf is Z_NULL, this function returns the + required initial value for the checksum. + + An Adler-32 checksum is almost as reliable as a CRC-32 but can be computed + much faster. + + Usage example: + + uLong adler = adler32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + adler = adler32(adler, buffer, length); + } + if (adler != original_adler) error(); +*/ + +ZEXTERN uLong ZEXPORT adler32_z OF((uLong adler, const Bytef *buf, + z_size_t len)); +/* + Same as adler32(), but with a size_t length. +*/ + +/* +ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2, + z_off_t len2)); + + Combine two Adler-32 checksums into one. For two sequences of bytes, seq1 + and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for + each, adler1 and adler2. adler32_combine() returns the Adler-32 checksum of + seq1 and seq2 concatenated, requiring only adler1, adler2, and len2. Note + that the z_off_t type (like off_t) is a signed integer. If len2 is + negative, the result has no meaning or utility. +*/ + +ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len)); +/* + Update a running CRC-32 with the bytes buf[0..len-1] and return the + updated CRC-32. If buf is Z_NULL, this function returns the required + initial value for the crc. Pre- and post-conditioning (one's complement) is + performed within this function so it shouldn't be done by the application. + + Usage example: + + uLong crc = crc32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + crc = crc32(crc, buffer, length); + } + if (crc != original_crc) error(); +*/ + +ZEXTERN uLong ZEXPORT crc32_z OF((uLong adler, const Bytef *buf, + z_size_t len)); +/* + Same as crc32(), but with a size_t length. +*/ + +/* +ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2)); + + Combine two CRC-32 check values into one. For two sequences of bytes, + seq1 and seq2 with lengths len1 and len2, CRC-32 check values were + calculated for each, crc1 and crc2. crc32_combine() returns the CRC-32 + check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and + len2. +*/ + + + /* various hacks, don't look :) */ + +/* deflateInit and inflateInit are macros to allow checking the zlib version + * and the compiler's view of z_stream: + */ +ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method, + int windowBits, int memLevel, + int strategy, const char *version, + int stream_size)); +ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits, + unsigned char FAR *window, + const char *version, + int stream_size)); +#ifdef Z_PREFIX_SET +# define z_deflateInit(strm, level) \ + deflateInit_((strm), (level), ZLIB_VERSION, (int)sizeof(z_stream)) +# define z_inflateInit(strm) \ + inflateInit_((strm), ZLIB_VERSION, (int)sizeof(z_stream)) +# define z_deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ + deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ + (strategy), ZLIB_VERSION, (int)sizeof(z_stream)) +# define z_inflateInit2(strm, windowBits) \ + inflateInit2_((strm), (windowBits), ZLIB_VERSION, \ + (int)sizeof(z_stream)) +# define z_inflateBackInit(strm, windowBits, window) \ + inflateBackInit_((strm), (windowBits), (window), \ + ZLIB_VERSION, (int)sizeof(z_stream)) +#else +# define deflateInit(strm, level) \ + deflateInit_((strm), (level), ZLIB_VERSION, (int)sizeof(z_stream)) +# define inflateInit(strm) \ + inflateInit_((strm), ZLIB_VERSION, (int)sizeof(z_stream)) +# define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ + deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ + (strategy), ZLIB_VERSION, (int)sizeof(z_stream)) +# define inflateInit2(strm, windowBits) \ + inflateInit2_((strm), (windowBits), ZLIB_VERSION, \ + (int)sizeof(z_stream)) +# define inflateBackInit(strm, windowBits, window) \ + inflateBackInit_((strm), (windowBits), (window), \ + ZLIB_VERSION, (int)sizeof(z_stream)) +#endif + +#ifndef Z_SOLO + +/* gzgetc() macro and its supporting function and exposed data structure. Note + * that the real internal state is much larger than the exposed structure. + * This abbreviated structure exposes just enough for the gzgetc() macro. The + * user should not mess with these exposed elements, since their names or + * behavior could change in the future, perhaps even capriciously. They can + * only be used by the gzgetc() macro. You have been warned. + */ +struct gzFile_s { + unsigned have; + unsigned char *next; + z_off64_t pos; +}; +ZEXTERN int ZEXPORT gzgetc_ OF((gzFile file)); /* backward compatibility */ +#ifdef Z_PREFIX_SET +# undef z_gzgetc +# define z_gzgetc(g) \ + ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : (gzgetc)(g)) +#else +# define gzgetc(g) \ + ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : (gzgetc)(g)) +#endif + +/* provide 64-bit offset functions if _LARGEFILE64_SOURCE defined, and/or + * change the regular functions to 64 bits if _FILE_OFFSET_BITS is 64 (if + * both are true, the application gets the *64 functions, and the regular + * functions are changed to 64 bits) -- in case these are set on systems + * without large file support, _LFS64_LARGEFILE must also be true + */ +#ifdef Z_LARGE64 + ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); + ZEXTERN z_off64_t ZEXPORT gzseek64 OF((gzFile, z_off64_t, int)); + ZEXTERN z_off64_t ZEXPORT gztell64 OF((gzFile)); + ZEXTERN z_off64_t ZEXPORT gzoffset64 OF((gzFile)); + ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off64_t)); + ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off64_t)); +#endif + +#if !defined(ZLIB_INTERNAL) && defined(Z_WANT64) +# ifdef Z_PREFIX_SET +# define z_gzopen z_gzopen64 +# define z_gzseek z_gzseek64 +# define z_gztell z_gztell64 +# define z_gzoffset z_gzoffset64 +# define z_adler32_combine z_adler32_combine64 +# define z_crc32_combine z_crc32_combine64 +# else +# define gzopen gzopen64 +# define gzseek gzseek64 +# define gztell gztell64 +# define gzoffset gzoffset64 +# define adler32_combine adler32_combine64 +# define crc32_combine crc32_combine64 +# endif +# ifndef Z_LARGE64 + ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); + ZEXTERN z_off_t ZEXPORT gzseek64 OF((gzFile, z_off_t, int)); + ZEXTERN z_off_t ZEXPORT gztell64 OF((gzFile)); + ZEXTERN z_off_t ZEXPORT gzoffset64 OF((gzFile)); + ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t)); +# endif +#else + ZEXTERN gzFile ZEXPORT gzopen OF((const char *, const char *)); + ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile, z_off_t, int)); + ZEXTERN z_off_t ZEXPORT gztell OF((gzFile)); + ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile)); + ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t)); +#endif + +#else /* Z_SOLO */ + + ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t)); + +#endif /* !Z_SOLO */ + +/* undocumented functions */ +ZEXTERN const char * ZEXPORT zError OF((int)); +ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp)); +ZEXTERN const z_crc_t FAR * ZEXPORT get_crc_table OF((void)); +ZEXTERN int ZEXPORT inflateUndermine OF((z_streamp, int)); +ZEXTERN int ZEXPORT inflateValidate OF((z_streamp, int)); +ZEXTERN unsigned long ZEXPORT inflateCodesUsed OF ((z_streamp)); +ZEXTERN int ZEXPORT inflateResetKeep OF((z_streamp)); +ZEXTERN int ZEXPORT deflateResetKeep OF((z_streamp)); +#if (defined(_WIN32) || defined(__CYGWIN__)) && !defined(Z_SOLO) +ZEXTERN gzFile ZEXPORT gzopen_w OF((const wchar_t *path, + const char *mode)); +#endif +#if defined(STDC) || defined(Z_HAVE_STDARG_H) +# ifndef Z_SOLO +ZEXTERN int ZEXPORTVA gzvprintf Z_ARG((gzFile file, + const char *format, + va_list va)); +# endif +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* ZLIB_H */ diff --git a/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/prebuilt/zlib/lib/libz.a b/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/prebuilt/zlib/lib/libz.a new file mode 100644 index 0000000..eea211a Binary files /dev/null and b/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/prebuilt/zlib/lib/libz.a differ diff --git a/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/webclient/.gitignore b/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/webclient/.gitignore new file mode 100644 index 0000000..c6127b3 --- /dev/null +++ b/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/webclient/.gitignore @@ -0,0 +1,52 @@ +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf diff --git a/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/webclient/LICENSE b/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/webclient/LICENSE new file mode 100644 index 0000000..8f71f43 --- /dev/null +++ b/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/webclient/LICENSE @@ -0,0 +1,202 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/webclient/README.md b/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/webclient/README.md new file mode 100644 index 0000000..5d31a1e --- /dev/null +++ b/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/webclient/README.md @@ -0,0 +1,99 @@ +# WebClient + +[Chinese](README_ZH.md) | English + +## 1. Introduction + +The WebClient software package is independently developed by RT-Thread and is based on the implementation of the HTTP protocol client. It provides the basic functions of communication between the device and the HTTP Server. + +The features of the WebClient software package are as follows: + +- Support IPV4/IPV6 address; +- Support GET/POST request method; +- Support file upload and download function; +- Support HTTPS encrypted transmission; +- Complete header data addition and processing methods. + +For more software package introduction, please refer to [Detailed introduction](docs/introduction.md). + +### 1.1 Directory structure + +The directory structure of the WebClient software package is as follows: + +``` +webclient +├───docs +│ └───figures // Documents use pictures +│ │ api.md // API instructions +│ │ introduction.md // Introduction document +│ │ principle.md // Implementation principle +│ │ README.md // Document structure description +│ │ samples.md // package sample +│ │ user-guide.md // Instructions +│ └───version.md // version +├───inc // header file +├───src // source file +├───samples // sample code +| | webclient_get_sample // GET request sample code +│ └───webclient_post_sample // POST request sample code +│ LICENSE // package license +│ README.md // Software package instructions +└───SConscript // RT-Thread default build script +``` + +### 1.2 License + +The WebClient software package complies with the Apache-2.0 license, see the LICENSE file for details. + +### 1.3 Dependency + +- RT_Thread 3.0+ + +- [mbedtls package](https://github.com/RT-Thread-packages/mbedtls) (if HTTPS support is enabled) + +## 2. Get the software package + +To use the WebClient software package, you need to select it in the RT-Thread package management. The specific path is as follows: + +``` +RT-Thread online packages + IoT-internet of things ---> + [*] WebClient: A HTTP/HTTPS Client for RT-Thread + [ ] Enable debug log output + [ ] Enable webclient GET/POST/SHARD samples + [ ] Enable file download feature support + Select TLS mode (Not support) ---> + (x) Not support + () SAL TLS support + () MbedTLS support + Version (latest) ---> +``` + +**Enable webclient GET/POST samples**: add sample code; +**Select TLS mode**: Configure to enable HTTPS support and select the supported mode; +- **Not support**: Does not support TLS function; +- **SAL TLS support**: Configure the TLS function support in the SAL component, and abstract the TLS operation in the SAL component. Users also need to **manually configure the type of TLS software package used** (currently only supports the MbedTLS package); +- **MbedTLS support**: configure MbedTLS function support; +**Version**: Configure the software package version. + +After the configuration is complete, let the RT-Thread package manager automatically update, or use the pkgs --update command to update the package to the BSP. + +## 3. Use WebClient software package +- For detailed description of the software package, please refer to [Package Introduction](docs/introduction.md) +- For detailed sample introduction, please refer to [Sample Document](docs/samples.md) +- How to use from scratch, please refer to [User Guide](docs/user-guide.md) +- For complete API documentation, please refer to [API Manual](docs/api.md) +- The working principle of the software package, please refer to [Working Principle](docs/principle.md) + +- More **Detailed introduction documents** are located in the [`/docs`](/docs) folder, **Please check before using the package for development**. + +## 4. Matters needing attention + + + - When the WebClient software package connects to the HTTPS server, you need to enable the TLS function support in WebClient. + - After the WebClient software package version update (`V1.0.0 -> the current latest version V2.0.0`), the function interface and usage process in the software package have changed. If the previous interface is used in the developer code, the latest version interface can be adapted , Or select the `V1.0.0` version in the version number configuration, the specific modification method can refer to the software package [migration guide](docs/migration-guide.md). + +## 5. Contact & Thanks + +- Maintenance: RT-Thread development team +- Homepage: https://github.com/RT-Thread-packages/webclient \ No newline at end of file diff --git a/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/webclient/README_ZH.md b/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/webclient/README_ZH.md new file mode 100644 index 0000000..70199e6 --- /dev/null +++ b/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/webclient/README_ZH.md @@ -0,0 +1,104 @@ +# WebClient + +中文页 | [英文页](README.md) + +## 1、介绍 + +WebClient 软件包是 RT-Thread 自主研发的,基于 HTTP 协议的客户端的实现,它提供设备与 HTTP Server 的通讯的基本功能。 + +WebClient 软件包功能特点如下: + +- 支持 IPV4/IPV6 地址; +- 支持 GET/POST 请求方法; +- 支持文件的上传和下载功能; +- 支持 HTTPS 加密传输; +- 完善的头部数据添加和处理方式。 + +更多软件包介绍请查看 [详细介绍](docs/introduction.md)。 + +### 1.1 目录结构 + +WebClient 软件包目录结构如下所示: + +``` +webclient +├───docs +│ └───figures // 文档使用图片 +│ │ api.md // API 使用说明 +│ │ introduction.md // 介绍文档 +│ │ principle.md // 实现原理 +│ │ README.md // 文档结构说明 +│ │ samples.md // 软件包示例 +│ │ user-guide.md // 使用说明 +│ └───version.md // 版本 +├───inc // 头文件 +├───src // 源文件 +├───samples // 示例代码 +| | webclient_get_sample // GET 请求示例代码 +│ └───webclient_post_sample // POST 请求示例代码 +│ LICENSE // 软件包许可证 +│ README.md // 软件包使用说明 +└───SConscript // RT-Thread 默认的构建脚本 +``` + +### 1.2 许可证 + +WebClient 软件包遵循 Apache-2.0 许可,详见 LICENSE 文件。 + +### 1.3 依赖 + +- RT_Thread 3.0+ + +- [mbedtls 软件包](https://github.com/RT-Thread-packages/mbedtls)(如果开启 HTTPS 支持) + +## 2、获取软件包 + +使用 WebClient 软件包需要在 RT-Thread 的包管理中选中它,具体路径如下: + +``` +RT-Thread online packages + IoT - internet of things ---> + [*] WebClient: A HTTP/HTTPS Client for RT-Thread + [ ] Enable webclient GET/POST/SHARD samples + Select TLS mode (Not support) ---> + (x) Not support + ( ) SAL TLS support + ( ) MbedTLS support + Version (latest) ---> +``` + +**Enable webclient GET/POST samples** :添加示例代码; + +**Select TLS mode** :配置开启 HTTPS 支持,选择支持的模式; + +- **Not support**:不支持 TLS 功能; +- **SAL TLS support**:配置 SAL 组件中 TLS 功能支持,SAL 组件中抽象 TLS 操作,用户还需要**手动配置开启使用的 TLS 软件包类型**(目前只支持 MbedTLS 软件包); +- **MbedTLS support**:配置 MbedTLS 功能支持; + +**Version** :配置软件包版本。 + +配置完成后让 RT-Thread 的包管理器自动更新,或者使用 pkgs --update 命令更新包到 BSP 中。 + +## 3、使用 WebClient 软件包 +- 软件包详细介绍,请参考 [软件包介绍](docs/introduction.md) + +- 详细的示例介绍,请参考 [示例文档](docs/samples.md) + +- 如何从零开始使用,请参考 [用户指南](docs/user-guide.md) + +- 完整的 API 文档,请参考 [API 手册](docs/api.md) + +- 软件包工作原理,请参考 [工作原理](docs/principle.md) + +- 更多**详细介绍文档**位于 [`/docs`](/docs) 文件夹下,**使用软件包进行开发前请务必查看**。 + +## 4、注意事项 + + - WebClient 软件包连接 HTTPS 服务器时需要开启 WebClient 中对 TLS 功能的支持。 + - WebClient 软件包版本更新(`V1.0.0 -> 当前最新版 V2.0.0`)后软件包中函数接口和使用流程都有所变化,若开发者代码中使用之前接口,可以适配最新版本接口,或者在版本号配置中选择 `V1.0.0` 版本,具体改动方式可参考软件包 [迁移指南](docs/migration-guide.md)。 + + +## 5、联系方式 & 感谢 + +- 维护:RT-Thread 开发团队 +- 主页:https://github.com/RT-Thread-packages/webclient \ No newline at end of file diff --git a/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/webclient/SConscript b/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/webclient/SConscript new file mode 100644 index 0000000..ae8dbd4 --- /dev/null +++ b/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/webclient/SConscript @@ -0,0 +1,16 @@ +from building import * + +cwd = GetCurrentDir() +path = [cwd + '/inc'] + +src = Glob('src/webclient.c') + +if GetDepend(['WEBCLIENT_USING_FILE_DOWMLOAD']): + src += Glob('src/webclient_file.c') + +if GetDepend(['WEBCLIENT_USING_SAMPLES']): + src += Glob('samples/*.c') + +group = DefineGroup('WebClient', src, depend = ['PKG_USING_WEBCLIENT'], CPPPATH = path) + +Return('group') diff --git a/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/webclient/docs/README.md b/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/webclient/docs/README.md new file mode 100644 index 0000000..fab6dbe --- /dev/null +++ b/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/webclient/docs/README.md @@ -0,0 +1,18 @@ +# 文档 + +## 软件包地址 + +- https://github.com/RT-Thread-packages/webclient + +## 文档列表 + +|文件名 |描述| +|:----- |:----| +|[version.md](version.md) |版本信息| +|[introduction.md](introduction.md) |详细介绍| +|[principle.md](principle.md) |工作原理| +|[user-guide.md](user-guide.md) |使用指南| +|[api.md](api.md) |API 说明| +|[samples.md](samples.md) |示例说明| +|[migration-guide.md](migration-guide.md) |迁移指南| + diff --git a/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/webclient/docs/api.md b/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/webclient/docs/api.md new file mode 100644 index 0000000..ac98d88 --- /dev/null +++ b/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/webclient/docs/api.md @@ -0,0 +1,287 @@ +# API 说明 + +## 创建会话 + +```c +struct webclient_session *webclient_session_create(size_t header_sz); +``` + +创建客户端会话结构体。 + +| 参数 | 描述 | +|:------------------|:-----------------------------------| +|header_sz | 最大支持的头部长度 | +| **返回** | **描述** | +|!= NULL | webclient 会话结构体指针 | +|= NULL | 创建失败 | + +## 关闭会话连接 + +```c +int webclient_close(struct webclient_session *session); +``` + +关闭传入的客户端会话连接,并释放内存。 + +| 参数 | 描述 | +|:------------------|:-----------------------------------| +|session | 当前连接会话结构体指针 | +| **返回** | **描述** | +|=0 | 成功 | + + +## 发送 GET 请求 + +```c +int webclient_get(struct webclient_session *session, const char *URI); +``` + +发送 HTTP GET 请求命令。 + +| 参数 | 描述 | +|:------------------|:-----------------------------------| +|session | 当前连接会话结构体指针 | +|URI | 连接的 HTTP 服务器地址 | +| **返回** | **描述** | +|`>0` | HTTP 响应状态码 | +|<0 | 发送请求失败 | + +## 获取指定数据大小的 HEAD / GET 请求 + +```c +int webclient_shard_position_function(struct webclient_session *session, const char *URI, int size); +``` + +发送带有 Range 头信息的 HTTP GET/HEAD 请求命令,多用于断点续传 / 分片下载功能。 + +| 参数 | 描述 | +|:------------------|:-----------------------------------| +|session | 当前连接会话结构体指针 | +|URI | 连接的 HTTP 服务器地址 | +|size | 设定的接收空间 | +| **返回** | **描述** | +|`>0` | HTTP 响应状态码 | +|<0 | 发送请求失败 | + +## 发送 POST 请求 + +```c +int webclient_post(struct webclient_session *session, const char *URI, const void *post_data, size_t data_len); +``` + +发送 HTTP POST 请求命令,上传数据到 HTTP 服务器。 + +| 参数 | 描述 | +|:------------------|:-----------------------------------| +|session | 当前连接会话结构体指针 | +|URI | 连接的 HTTP 服务器地址 | +|post_data | 需要上传的数据地址 | +|data_len | 需要上传数据的长度 | +| **返回** | **描述** | +|`>0` | HTTP 响应状态码 | +|<0 | 发送请求失败 | + +## 发送数据 + +```c +int webclient_write(struct webclient_session *session, const void *buffer, size_t size); +``` + +发送数据到连接的服务器。 + +| 参数 | 描述 | +|:------------------|:-----------------------------------| +|session | 当前连接会话结构体指针 | +|buffer | 发送数据的地址 | +|size | 发送数据的长度 | +| **返回** | **描述** | +|`>0` | 成功发送数据的长度 | +|=0 | 连接关闭 | +|<0 | 发送数据失败 | + +## 接收数据 + +```c +int webclient_read(struct webclient_session *session, void *buffer, size_t size); +``` + +从连接的服务器接收数据。 + +| 参数 | 描述 | +|:------------------|:-----------------------------------| +|session | 当前连接会话结构体指针 | +|buffer | 接收数据的存放地址 | +|size | 最大接收数据的长度 | +| **返回** | **描述** | +|`>0` | 成功接收数据的长度 | +|=0 | 连接关闭 | +|<0 | 接收数据失败 | + + +## 设置接收和发送数据超时时间 + +```c +int webclient_set_timeout(struct webclient_session *session, int millisecond); +``` + +设置连接的接收和发送数据超时时间。 + +| 参数 | 描述 | +|:------------------|:-----------------------------------| +|session | 当前连接会话结构体指针 | +|millisecond | 设置的超时时间,单位毫秒 | +| **返回** | **描述** | +|=0 | 设置超时成功 | + +## 在请求头中添加字段数据 + +```c +int webclient_header_fields_add(struct webclient_session *session, const char *fmt, ...); +``` + +该函数用于创建会话之后和发送 GET 或 POST 请求之前,用于添加请求头字段数据。 + +| 参数 | 描述 | +|:------------------|:-----------------------------------| +|session | 当前连接会话结构体指针 | +|fmt | 添加字段数据的表达式 | +|... | 添加的字段数据,为可变参数 | +| **返回** | **描述** | +|`>0` | 成功添加的字段数据的长度 | +| <=0 | 添加失败或者头部数据长度超出 | + +## 通过字段名获取字段值数据 + +```c +const char *webclient_header_fields_get(struct webclient_session *session, const char *fields); +``` + +该函数用于发送 GET 或 POST 请求之后,可以通过传入的字段名称获取对应的字段数据。 + +| 参数 | 描述 | +|:------------------|:-----------------------------------| +|session | 当前连接会话结构体指针 | +|fields | HTTP 字段名称 | +| **返回** | **描述** | +|= NULL | 获取数据失败 | +|!= NULL | 成功获取的字段数据 | + + +## 接收响应数据到指定地址 + +```c +int webclient_response(struct webclient_session *session, void **response, size_t *resp_len); +``` + +该函数用于发送 GET 或 POST 请求之后, 可以接收响应数据到指定地址。 + +| 参数 | 描述 | +|:------------------|:-----------------------------------| +|session | 当前连接会话结构体指针 | +|response | 存放接收数据的字符串地址 | +|resp_len | 接收数据的长度的指针 | +| **返回** | **描述** | +| `>0` | 成功接收数据的长度 | +| <=0 | 接收数据失败 | + +## 发送 GET/POST 请求并接收响应数据 + +```c +int webclient_request(const char *URI, const char *header, const void *post_data, size_t data_len, void **response, size_t *resp_len); +``` + +| 参数 | 描述 | +|:------------------|:-----------------------------------| +|URI | 连接的 HTTP 服务器地址 | +|header | 需要发送的头部数据 | +| | = NULL,发送默认头数据信息,可用于发送 GET/POST请求 | +| | != NULL,发送指定头数据信息,可用于发送 GET/POST请求 | +|post_data | 发送到服务器的数据 | +| | = NULL,该发送请求为 GET 请求 | +| | != NULL,该发送请求为 POST 请求 | +| data_len | 发送数据的长度 | +|response | 存放接收数据的字符串地址 | +|resp_len | 接收数据长度的指针 | +| **返回** | **描述** | +| `>0` | 成功接收数据的长度 | +| <=0 | 接收数据失败 | + +## 拼接请求头部数据 + +```c +int webclient_request_header_add(char **request_header, const char *fmt, ...); +``` + +该函数适用于 webclient_request 函数发送请求之前,头部数据的拼接和添加。 + +| 参数 | 描述 | +|:------------------|:-----------------------------------| +|request_header | 请求头部数据缓冲区地址 | +|fmt | 添加字段数据的表达式 | +|... | 添加的字段数据,为可变参数 | +| **返回** | **描述** | +| `>0` | 成功添加的字段数据的长度 | +| <=0 | 头部数据添加失败或内存不足 | + + +## 获取 HTTP 响应状态码 + +```c +int webclient_resp_status_get(struct webclient_session *session); +``` + +该函数用于发送 GET 或 POST 请求之后,用于获取返回的响应状态码。 + +| 参数 | 描述 | +|:------------------|:-----------------------------------| +|session | 当前连接会话结构体指针 | +| **返回** | **描述** | +| `>0` | HTTP 响应状态码 | + +## 获取 Content-Length 字段数据 + +```c +int webclient_content_length_get(struct webclient_session *session); +``` + +该函数用于发送 GET 或 POST 请求之后,用于获取返回的 Content-Length 字段数据。 + +| 参数 | 描述 | +|:------------------|:-----------------------------------| +|session | 当前连接会话结构体指针 | +| **返回** | **描述** | +| `>0` | Content-Length 字段数据 | +| <0 | 获取失败 | + +## 下载文件到本地 + +```c +int webclient_get_file(const char *URI, const char *filename); +``` + +从 HTTP 服务器下载文件并存放到本地。 + +| 参数 | 描述 | +|:------------------|:-----------------------------------| +|URI | 连接的 HTTP 服务器地址 | +|filename | 存放文件位置、名称 | +| **返回** | **描述** | +|=0 | 下载文件成功 | +|<0 | 下载文件失败 | + +## 上传文件到服务器 + +```c +int webclient_post_file(const char *URI, const char *filename, const char *form_data); +``` + +从 HTTP 服务器下载文件并存放到本地。 + +| 参数 | 描述 | +|:------------------|:-----------------------------------| +|URI | 连接的 HTTP 服务器地址 | +|filename | 需要上传的文件位置、名称 | +|form_data | 附加选项 | +| **返回** | **描述** | +|=0 | 上传文件成功 | +|<0 | 上传文件失败 | \ No newline at end of file diff --git a/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/webclient/docs/figures/principle.jpg b/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/webclient/docs/figures/principle.jpg new file mode 100644 index 0000000..4e6c083 Binary files /dev/null and b/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/webclient/docs/figures/principle.jpg differ diff --git a/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/webclient/docs/figures/request.jpg b/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/webclient/docs/figures/request.jpg new file mode 100644 index 0000000..820900d Binary files /dev/null and b/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/webclient/docs/figures/request.jpg differ diff --git a/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/webclient/docs/figures/response.jpg b/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/webclient/docs/figures/response.jpg new file mode 100644 index 0000000..8b125c1 Binary files /dev/null and b/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/webclient/docs/figures/response.jpg differ diff --git a/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/webclient/docs/figures/webclient_cfg.jpg b/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/webclient/docs/figures/webclient_cfg.jpg new file mode 100644 index 0000000..363c84e Binary files /dev/null and b/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/webclient/docs/figures/webclient_cfg.jpg differ diff --git a/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/webclient/docs/introduction.md b/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/webclient/docs/introduction.md new file mode 100644 index 0000000..d978b13 --- /dev/null +++ b/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/webclient/docs/introduction.md @@ -0,0 +1,140 @@ +# 软件包介绍 + +WebClient 软件包是 RT-Thread 自主研发的,基于 HTTP 协议的客户端的实现,它提供设备与 HTTP Server 的通讯的基本功能。 + +## 软件包目录结构 + +WebClient 软件包目录结构如下所示: + +```shell +webclient ++---docs +│ +---figures // 文档使用图片 +│ │ api.md // API 使用说明 +│ │ introduction.md // 介绍文档 +│ │ principle.md // 实现原理 +│ │ README.md // 文档结构说明 +│ │ samples.md // 软件包示例 +│ │ user-guide.md // 使用说明 +│ +---version.md // 版本 ++---inc // 头文件 ++---src // 源文件 ++---samples // 示例代码 +│ │ webclient_get_sample // GET 请求示例代码 +│ +---webclient_post_sample // POST 请求示例代码 +│ LICENSE // 软件包许可证 +│ README.md // 软件包使用说明 ++---SConscript // RT-Thread 默认的构建脚本 +``` + +## 软件包功能特点 + +WebClient 软件包功能特点: + +- 支持 IPV4/IPV6 地址 + + WebClient 软件包会自动根据传入的 URI 地址的格式判断是 IPV4 地址或 IPV6 地址,并且从其中解析出连接服务器需要的信息,提高代码兼容性。 + +- 支持 GET/POST 请求方法 + + HTTP 有多种请求方法(GET、POST、PUT、DELETE等),目前 WebClient 软件包支持 GET 和 POST 请求方法,这也是嵌入式设备最常用到的两个命令类型,满足设备开发需求。 + +- 支持文件的上传和下载功能 + + WebClient 软件包提供文件上传和下载的接口函数,方便用户直接通过 GET/POST 请求方法上传本地文件到服务器或者下载服务器文件到本地,文件操作需要文件系统支持,使用前需开启并完成文件系统的移植。 + +- 支持 HTTPS 加密传输 + + HTTPS 协议(HyperText Transfer Protocol over Secure Socket Layer)和 HTTP 协议一样是基于 TCP 实现的,实际上是在原有的 HTTP 数据外部添加一层 TLS 加密的封装,从而达到加密传输数据的目的。HTTPS 协议地址区别于 HTTP 地址,是以 `https` 开头的。WebClient 软件包中的 TLS 加密方式依赖 [mbedtls 软件包](https://github.com/RT-Thread-packages/mbedtls) 实现。 + +- 完善的头部数据添加和处理方式 + + HTTP 头部信息用于确定当前请求或响应的数据和状态信息,在发送 GET/POST 请求时头部的拼接成为用户操作的一大难题,正常的做法是手动逐行输入或使用字符串拼接方式,WebClient 软件包中提供简单的添加发送请求头部信息的方式,方便用户使用。对于请求返回的头部信息,往往用户需要获取头部字段数据,WebClient 软件包同样提供了 `通过字段名获取字段数据的方式`,方便获取需要的数据。 + +## HTTP 协议介绍 + +### HTTP 协议简述 + +HTTP (Hypertext Transfer Protocol)协议, 即超文本传输协议,是互联网上应用最为广泛的一种网络协议,由于其简捷、快速的方式,适用于分布式和合作式超媒体信息系统。HTTP 协议是基于 TCP/IP 协议的网络应用层协议。默认端口为 80 端口。协议最新版本是 HTTP 2.0,目前是用最广泛的是 HTTP 1.1。 + +HTTP 协议是一种请求/响应式的协议。一个客户端与服务器建立连接之后,发送一个请求给服务器。服务器接收到请求之后,通过接收到的信息判断响应方式,并且给予客户端相应的响应,完成整个 HTTP 数据交互流程。 + +浏览器网页是 HTTP 的主要应用方式,但这不代表 HTTP 只能应用于网页,实际上只要通信的双方遵循 HTTP 协议数据传输合适就可以进行数据交互,比如嵌入式领域设备通过 HTTP 协议与服务器连接。 + +### HTTP 协议特点 + +- 无状态协议 + + HTTP 协议是`无状态协议`。无状态是指协议对于事件的处理没有记忆能力。这意味着如果后续处理需要前面的信息,则必须重传,这可能导致每次连接传送的数据量增大。这样的好处在于,在服务器不需要先前信息时它的应答就较快。 + +- 灵活的数据传输 + + HTTP 允许传输任意类型的数据对象,传输的数据类型由 Content-Type 加以标记区分。 + +- 简单快速 + + 客户端向服务器发送请求时,只需要传送请求方式和路径。由于HTTP协议简单,使得HTTP服务器的程序规模小,因而通信速度很快。 + +- 支持 B/S 和 C/S 模式 + + C/S 结构,即 Client/Server (客户机/服务器)结构。B/S 结构,即 Browser/Server (浏览器/服务器)结构。 + +### HTTP 协议请求信息 Request + + 客户端发送一个HTTP请求到服务器的请求消息包括: **请求行**、**请求头部**、**空行**和**请求数据**四个部分组成。 + + - 请求行:用于说明请求类型,用来说明请求类型,要访问的资源以及所使用的HTTP版本; + + - 请求头部:紧接着请求行(即第一行)之后的部分,用来说明服务器要使用的附加信息; + + - 空行:请求头部后面的空行是必须的,区分头部和请求数据; + + - 请求数据:请求数据也叫主体,可以添加任意的其他数据。 + + 如下图是一个 POST 请求的信息: + +![HTTP 协议请求信息](figures/request.jpg) + +### HTTP 协议响应信息 Response + +一般情况下,服务器接收并处理客户端发过来的请求后会返回一个HTTP的响应消息。HTTP响应也由四个部分组成,分别是:**状态行**、**消息报头**、**空行**和**响应数据**。 + +- 状态行:由 HTTP 协议号(HTTP 1.1),响应状态码(200),状态信息(OK)三部分组成; + +- 消息报头:用来说明客户端要使用的一些附加信息(日期,数据长度等); + +- 空行:消息报头后面的空行是必须的,用于区分消息报头和响应数据; + +- 响应数据:服务器返回给客户端的文本信息。 + +如下图是一个 POST 请求的响应的信息: + +![HTTP 协议响应信息](figures/response.jpg) + +### HTTP 协议状态码 + +HTTP 协议中通过返回的状态码信息,判断当前响应状态,WebClient 软件包中也有对状态的获取和判断方式,这里主要介绍常见的状态码的意义。 + +状态代码有三位数字组成,第一个数字定义了响应的类别,共分五种类别: + +- 1xx:指示信息--表示请求已接收,继续处理 + +- 2xx:成功--表示请求已被成功接收、理解、接受 + +- 3xx:重定向--要完成请求必须进行更进一步的操作 + +- 4xx:客户端错误--请求有语法错误或请求无法实现 + +- 5xx:服务器端错误--服务器未能实现合法的请求 + +常见的状态码有以下几种: + +```c +200 OK //客户端请求成功 +206 Partial Content //服务器已经成功处理了部分 GET 请求 +400 Bad Request //客户端请求有语法错误,不能被服务器所理解 +403 Forbidden //服务器收到请求,但是拒绝提供服务 +404 Not Found //请求资源不存在,eg:输入了错误的URL +500 Internal Server Error //服务器发生不可预期的错误 +503 Server Unavailable //服务器当前不能处理客户端的请求 +``` \ No newline at end of file diff --git a/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/webclient/docs/migration-guide.md b/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/webclient/docs/migration-guide.md new file mode 100644 index 0000000..a3c088f --- /dev/null +++ b/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/webclient/docs/migration-guide.md @@ -0,0 +1,180 @@ +# 迁移指南 + +## 1、V1.0.0 -> V2.0.0 版本改动 + +本节主要介绍 WebClient 软件包版本升级之后(`V1.0.0` -> 最近版本 `V2.0.0`),软件包的改动和适配最新版本的方式。 + +### 函数改动 + +1. 添加 `webclient_session_create()` 创建客户端结构体函数 + + V1.0.0 版本客户端会话结构体不需要手动创建,V2.0.0 版本改为手动创建,用户可自定义传入的头文件长度,减少资源占用。 + +2. `webclient_open` -> `wenclient_get` + + V1.0.0 发送 GET 命令的函数 `webclient_open` 改为 `wenclient_get`,两个函数区别在于 `webclient_open` 函数只能发送默认头部信息的 GET 请求, `wenclient_get` 函数既可以发送默认也可以发送自定义头部数据的 GET 请求。 + +3. `webclient_open_position` -> `wenclient_get_position` + + 同上述 `webclient_open` 函数改动类似。 + +4. 添加 `webclient_post` 发送 POST 请求函数 + + V1.0.0 版本若要发送 POST 请求需要用户完成创建会话、连接、发送头部数据等整个流程,V2.0.0 版本封装了 `webclient_post` 函数完成整个 POST 请求发送流程,具体使用方式可参考 [API 手册](api.md) 和 [用户指南](user-guide.md)。 + +5. 添加头部信息处理方式 + + V1.0.0 版本中没有对头部数据处理的方式,用户只能通过字符串拼接的方式来添加头部数据。V2.0.0 版本就这一问题提出相应改进方式,增加了在请求头中添加字段数据的函数(`webclient_header_fields_add`),以及通过字段名获取字段数据的函数(`webclient_header_fields_get`),方便用户添加请求头数据和获取响应头数据中信息。 + +6. 其他功能函数添加 + +- 添加获取当前响应状态码函数(`webclient_resp_status_get` ); +- 添加获取当前响应 Content-Length 数据函数(`webclient_content_length_get`)。 + +### 流程改动 + +#### GET 请求流程改动 + +下面以发送自定义头部数据的 GET 请求方式为例,介绍新版本软件包中 GET 流程改动。 + +V1.0.0 版本 GET 请求流如下: + +- 拼接头部数据(字符串拼接方式) +- 创建客户端会话结构体 +- 客户端与服务器建立连接 +- 发送 GET 请求头部数据 +- 接收并解析服务器下发头部数据 +- 接收服务器发送主体数据 +- 关闭连接 + +```c +/* 字符串处理方式拼接头部数据 */ +const char header = "xxx"; +struct webclient_session *session = RT_NULL; + +session = web_malloc(sizeof(struct webclient_session)) + +webclient_connect(session, URI); + +webclient_send_header(session, WEBCLIENT_GET, header, strlen(header)); + +webclient_handle_response(session); + +while(1) +{ + webclient_read(session, post_data ,strlen(post_data)); + ... +} + +webclient_close(session); +``` + +V2.0.0 版本 GET 请求流如下: + +- 创建会话结构体 +- 拼接头部数据(函数拼接方式) +- 发送 GET 请求头部并接收响应 +- 接收服务器下发数据 +- 关闭连接 + +```c +struct webclient_session *session = RT_NULL; + +session = webclient_session_create(1024) + +webclient_header_fields_add(session, "HOST: %s", URI); + +webclient_get(session, URI); + +while(1) +{ + webclient_read(session, buffer ,buf_sz); + ... +} + +webclient_close(session); +``` + +#### POST 请求流程改动 + +下面以发送自定义头部数据的 POST 请求方式为例,介绍新版本软件包中的 POST 流程改动。 + +V1.0.0 版本 POST 请求流如下: + +- 拼接头部数据(字符串拼接方式) +- 创建客户端会话结构体 +- 客户端与服务器建立连接 +- 发送 POST 请求头部数据 +- 接收并解析服务器下发头部数据 +- 上传数据到服务器 +- 关闭连接 + +```c +/* 字符串处理方式拼接头部数据 */ +const char header = "xxx"; +struct webclient_session *session = RT_NULL; + +session = web_malloc(sizeof(struct webclient_session)) + +webclient_connect(session, URI); + +webclient_send_header(session, WEBCLIENT_POST, header, strlen(header)); + +webclient_handle_response(session); + +while(1) +{ + webclient_write(session, post_data ,strlen(post_data)); + ... +} + +webclient_close(session); +``` + +V2.0.0 版本 POST 请求流如下: + +- 创建会话结构体 +- 拼接头部数据(函数拼接方式) +- 发送头部数据和主体数据到服务器,并接收服务器响应 +- 关闭连接 + +```c +struct webclient_session *session = RT_NULL; + +session = webclient_session_create(1024) + +webclient_header_fields_add(session, "Content-Length: %s", post_data_sz); + +webclient_post(session, URI, post_data, rt_strlen(post_data)); + +webclient_close(session); +``` + +上述介绍 WebClient 常用 GET/POST 请求方法改动,更多流程介绍请查看软件包 [使用指南](user-guide.md)。 + +## 2、V2.1.0 -> V2.2.0 版本改动 + +V2.2.0 版本主要改动部分为函数接口参数定义,目的是为了适配非字符串格式数据的读写,其他函数的使用流程未产生明显改动。 + +### 函数改动 + +- `webclient_post` 函数参数改动 + + 该函数入参 `const char *post_data` 修改为 `const void *post_data`,并添加`size_t data_len` 参数,用于确定需要发送的数据长度。 + +- `webclient_read` 函数参数改动 + + 该函数入参 `unsigned char *buffer` 修改为 `void *buffer`,用于适配非字符串数据接收。 + +- `webclient_write` 函数参数改动 + + 该函数入参 `const unsigned char *buffer` 修改为 `const void *buffer`,用于适配非字符串数据发送。 + +- `webclient_response` 函数参数改动 + + 该函数入参 `unsigned char **response` 修改为 `void **response`,并且添加参数 `size_t *resp_len`,用于获取接收数据长度信息,适配非字符串数据接收。 + +- `webclient_request` 函数参数改动 + + 该函数入参 `const char *post_data` 修改为 `const void *post_data`,入参 `unsigned char **response` 修改为 `void **response`,并且添加入参 `size_t data_len` 和 `size_t *resp_len`,用于适配非字符串数据接收和发送。 + diff --git a/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/webclient/docs/principle.md b/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/webclient/docs/principle.md new file mode 100644 index 0000000..8c8bb43 --- /dev/null +++ b/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/webclient/docs/principle.md @@ -0,0 +1,30 @@ +# 工作原理 + + WebClient 软件包主要用于在嵌入式设备上实现 HTTP 协议,软件包的主要工作原理基于 HTTP 协议实现,如下图所示: + +![WebClient 软件包工作原理](figures/principle.jpg) + +HTTP 协议定义了客户端如何从服务器请求数据,以及服务器如何把数据传送给客户端的方式。HTTP 协议采用了`请求/响应模型`。 客户端向服务器发送一个请求报文,请求报文包含请求的方法、URL、协议版本、请求头部和请求数据。服务器以一个状态行作为响应,响应的内容包括协议的版本、成功或者错误代码、服务器信息、响应头部和响应数据。 + +在 HTTP 协议的实际使用过程中,一般遵循以下流程: + +1. 客户端连接到服务器 + + 通常是通过 TCP 三次握手建立 TCP 连接,默认 HTTP 端口号为 80。 + +2. 客户端发送 HTTP 请求(GET/POST) + + 通过TCP套接字,客户端向Web服务器发送一个文本的请求报文,一个请求报文由请求行、请求头部、空行和请求数据四部分组成 + +3. 服务器接受请求并返回 HTTP 响应 + + 服务器解析请求,定位请求资源。服务器将需要发送的资源写到 TCP 套接字,由客户端读取。一个响应由状态行、响应头部、空行和响应数据四部分组成。 + +4. 客户端和服务器断开连接 + + 若客户端和服务器之间连接模式为普通模式,则服务器主动关闭 TCP 连接,客户端被动关闭连接,释放 TCP 连接。若连接模式为 keepalive 模式,则该连接保持一段时间,在该时间内可以继续接收数据。 + +5. 客户端解析响应的数据内容 + + 客户端获取数据后应该先解析响应状态码,判断请求是否成功,然后逐行解析响应报头,获取响应数据信息,最后读取响应数据,完成整个 HTTP 数据收发流程。 + diff --git a/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/webclient/docs/samples.md b/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/webclient/docs/samples.md new file mode 100644 index 0000000..a3563e5 --- /dev/null +++ b/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/webclient/docs/samples.md @@ -0,0 +1,137 @@ +# 示例程序 + +WebClient 软件包提供三个 HTTP Client 示例程序, 分别用于演示软件包支持的 GET 和 POST 功能,完成数据的上传与下载;以及一个完整的分片下载的功能。 + +**示例文件** + +| 示例程序路径 | 说明 | +| ---- | ---- | +| samples/webclient_get_sample.c | GET 请求测试例程 | +| samples/webclient_post_sample.c | POST 请求测试例程 | +| samples/webclient_shard_download_sample.c | 分片下载测试例程 | + +## 准备工作 + +### 获取软件包 + +- menuconfig 配置获取软件包和示例代码 + + 打开 RT-Thread 提供的 ENV 工具,使用 **menuconfig** 配置软件包。 + + 启用 WebClient 软件包,并配置使能测试例程(Enable webclient GET/POST/SHARD samples),如下所示: + +```shell +RT-Thread online packages + IoT - internet of things ---> + [*] WebClient: A HTTP/HTTPS Client for RT-Thread + [ ] Enable debug log output + [*] Enable webclient GET/POST/SHARD samples # 开启 WebClient 测试例程 + [ ] Enable file download feature support + Select TLS mode (Not support) ---> + Version (latest) ---> # 开启使用最新版本软件包 +``` + +- 使用 `pkgs --update` 命令下载软件包 +- 编译下载 + + +## 启动例程 + +本例程使用的测试网站是 RT-Thread 系统的官方网站。GET 请求示例可以从网站中获取并打印显示文件内容;POST 请求示例可以上传数据到测试网站,测试网站会响应相同的数据。 + +> HTTP 收发数据包括头部数据和正文数据两部分,以下称头部数据为 `header 数据`,正文数据为 `body 数据`。 + +### GET 请求示例 + +GET 请求示例流程: + +- 创建 client 会话结构体 +- client 发送 GET 请求 header 数据(使用默认header 数据) +- server 响应 header 数据和 body 数据 +- 打印 server 响应 body 数据 +- GET 请求测试完成/失败 + +GET 请求示例使用方式有如下两种: + +- 在 MSH 中使用命令 `web_get_test` 执行 GET 请求示例程序,可以获取并打印显示默认网址下载的文件信息;在 MSH 中使用命令 `web_get_test -s` 执行 POST 请求示例程序,使用简化接口(webclient_request 接口)发送 GET请求,适用于简短数据的收发。如下图 LOG 显示: + +```c +msh />web_get_test +webclient get response data: +RT-Thread is an open source IoT operating system from China, which has strong scalability: from a tiny kernel running on a tiny core, for example ARM Cortex-M0, or Cortex-M3/4/7, to a rich feature system running on MIPS32, ARM Cortex-A8, ARM Cortex-A9 DualCore etc. + +msh />web_get_test -s +webclient send get request by simplify request interface. +webclient get response data: +RT-Thread is an open source IoT operating system from China, which has strong scalability: from a tiny kernel running on a tiny core, for example ARM Cortex-M0, or Cortex-M3/4/7, to a rich feature system running on MIPS32, ARM Cortex-A8, ARM Cortex-A9 DualCore etc. + +msh /> +``` + +- 在 MSH 中使用命令 `web_get_test [URI]` 或 `web_get_test -s [URI]` 格式命令执行 GET 请求示例程序,其中 URI 为用户自定义的支持 GET 请求的地址。 + +### POST 请求示例 + +POST 请求示例流程如下: + +- 创建 client 会话结构体 +- 拼接 POST 请求需要的 header 数据 +- client 发送拼接的 header 数据和 body 数据 +- server 响应 header 数据和 body 数据 +- 打印 server 响应 body 数据 +- POST 请求测试完成/失败 + +POST 请求示例使用方式有如下两种: + +- 在 MSH 中使用命令 `web_post_test` 执行 POST 请求示例程序,可以获取并打印显示响应数据(默认 POST 请求的地址是类似于回显的地址,会返回上传的数据);在 MSH 中使用命令 `web_post_test -s` 执行 POST 请求示例程序,使用简化接口(webclient_request 接口)发送 POST 请求,适用于简短数据的收发。如下图 LOG 显示: + +```c +msh />web_post_test +webclient post response data : +RT-Thread is an open source IoT operating system from China! +msh /> +msh />web_post_test -s +webclient send post request by simplify request interface. +webclient post response data: +RT-Thread is an open source IoT operating system from China! +msh /> +``` + +- 在 MSH 中使用命令 `web_post_test [URI]` 或者 `web_post_test -s [URI]` 格式命令执行 POST 请求示例程序,其中 URI 为用户自定义的支持 POST 请求的地址。 + +### 分片下载示例 + +分片下载示例流程: + +- 创建 client 会话结构体 +- client 发送 HEAD 请求 header 数据 +- server 响应 header 数据 +- client 发送 GET 请求 header 数据,包含 Range 字段 +- server 响应 header 数据和指定长度的 body 数据 +- 循环发送请求直到接收完成所有数据 +- 分片下载测试完成/失败 + +GET 请求示例使用方式有如下两种: + +- 在 MSH 中使用命令 `web_get_test -l [size]` 执行分片下载示例程序;指定允许接收的最大 body 数据长度,可以获取并打印显示默认网址下载的文件信息; +```c +msh >web_shard_test -l 115 +Receive, len[0115]: +0000 - 0059: RT-Thread is an open source IoT operating system from China, +0060 - 0114: which has strong scalability: from a tiny kernel runni +Total: [0115]Bytes + +Receive, len[0115]: +0000 - 0059: ng on a tiny core, for example ARM Cortex-M0, or Cortex-M3/4 +0060 - 0114: /7, to a rich feature system running on MIPS32, ARM Cor +Total: [0115]Bytes + +Receive, len[0037]: +0000 - 0036: tex-A8, ARM Cortex-A9 DualCore etc. + +Total: [0037]Bytes +msh /> +``` +分多次下载得到的数据,与通用的 GET 请求获取的数据完全一致,分片下载功能正常。 + +- 在 MSH 中使用命令 `web_get_test -u [URI]` 格式命令执行 GET 请求示例程序,其中 URI 为用户自定义的支持 GET 请求的地址。 \ No newline at end of file diff --git a/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/webclient/docs/user-guide.md b/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/webclient/docs/user-guide.md new file mode 100644 index 0000000..f37b76d --- /dev/null +++ b/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/webclient/docs/user-guide.md @@ -0,0 +1,380 @@ +# 使用指南 + +本节主要介绍 WebClient 软包的基本使用流程, 并针对使用过程中经常涉及到的结构体和重要 API 进行简要说明。 + +## 准备工作 + +首先需要下载 WebClient 软件包,并将软件包加入到项目中。在 BSP 目录下使用 menuconfig 命令打开 env 配置界面,在 `RT-Thread online packages → IoT - internet of things` 中选择 WebClient 软件包,操作界面如下图所示: + +![WebClient 软件包配置](figures/webclient_cfg.jpg) + +详细配置介绍如下所示: + +```shell +RT-Thread online packages + IoT - internet of things ---> + [*] WebClient: A HTTP/HTTPS Client for RT-Thread + [ ] Enable debug log output + [ ] Enable webclient GET/POST samples + Select TLS mode (Not support) ---> + (x) Not support + ( ) SAL TLS support + ( ) MbedTLS support + Version (latest) ---> +``` + +**Enable debug log output**:开启调试日志显示,可以用于查看请求和响应的头部数据信息; + +**Enable webclient GET/POST samples** :添加示例代码; + +**Select TLS mode** :配置开启 HTTPS 支持,选择支持的模式; + +- **Not support**:不支持 TLS 功能; +- **SAL TLS support**:配置 SAL 组件中 TLS 功能支持,SAL 组件中抽象 TLS 操作,用户还需要**手动配置开启使用的 TLS 软件包类型**(目前只支持 MbedTLS 软件包); +- **MbedTLS support**:配置 MbedTLS 功能支持; + +**Version** :配置软件包版本号。 + +选择合适的配置项后,使用 `pkgs --update` 命令下载软件包并更新用户配置。 + + +## 使用流程 + +使用 WebClient 软件包发送 GET/POST 请求一般需要完成如下基本流程: + +(1) **创建客户端会话结构体** + +```c +struct webclient_header +{ + char *buffer; //添加或者获取的头部数据 + size_t length; //存放当前头部数据长度 + + size_t size; //存放最大支持的头部数据长度 +}; + +struct webclient_session +{ + struct webclient_header *header; //保存头部信息结构体 + int socket; //当前连接套接字 + int resp_status; //响应状态码 + + char *host; //连接服务器地址 + char *req_url; //连接的请求地址 + + int chunk_sz; //chunk 模式下一块数据大小 + int chunk_offset; //chunk 模式剩余数据大小 + + int content_length; //当前接收数据长度(非 chunk 模式) + size_t content_remainder; //当前剩余接收数据长度 + + rt_bool_t is_tls; //当前连接是否是 HTTPS 连接 +#ifdef WEBCLIENT_USING_MBED_TLS + MbedTLSSession *tls_session; // HTTPS 协议相关会话结构体 +#endif +}; +``` + +`webclient_session` 结构体用于存放当前建立的 HTTP 连接的部分信息,可用与 HTTP 数据交互整个流程。建立 HTTP 连接前需要创建并初始化该结构体,创建的方式示例如下: + +```c +struct webclient_session *session = RT_NULL; + +/* create webclient session and set header response size */ +session = webclient_session_create(1024); +if (session == RT_NULL) +{ + ret = -RT_ENOMEM; + goto __exit; +} +``` + +(2) **拼接头部数据** + + WebClient 软件包提供两种请求头部发送方式: + + - 默认头部数据 + + 如果要使用默认的头部信息,则不需要拼接任何头部数据,可直接调用 GET 发送命令。默认头部数据一般只用于 GET 请求。 + + - 自定义头部数据 + +自定义头部数据使用 `webclient_header_fields_add` 函数添加头部信息,添加的头部信息位于客户端会话结构体中,在发送 GET/POST 请求时发送。 + +添加示例代码如下: + +```c +/* 拼接头部信息 */ +webclient_header_fields_add(session, "Content-Length: %d\r\n", strlen(post_data)); + +webclient_header_fields_add(session, "Content-Type: application/octet-stream\r\n"); +``` + +(3) **发送 GET/POST 请求** + +头部信息添加完成之后,就可以调用 `webclient_get` 函数或者 `webclient_post` 函数发送 GET/POST 请求命令了,函数中主要操作如下: + +- 通过传入的 URI 获取信息,建立 TCP 连接; + +- 发送默认或者拼接的头部信息; + +- 接收并解析响应数据的头部信息; + +- 返回错误或者响应状态码。 + +发送 GET 请求示例代码如下: + +```c +int resp_status = 0; + +/* send GET request by default header */ +if ((resp_status = webclient_get(session, URI)) != 200) +{ + LOG_E("webclient GET request failed, response(%d) error.", resp_status); + ret = -RT_ERROR; + goto __exit; +} +``` + +(4) **接收响应的数据** + +发送 GET/POST 请求之后,可以使用 `webclient_read` 函数接收响应的实际数据。因为响应的实际数据可能比较长,所以往往我们需要循环接收响应数据,直到数据接收完毕。 + +如下所示为循环接收并打印响应数据方式: + +```c +int content_pos = 0; +/* 获取接收的响应数据长度 */ +int content_length = webclient_content_length_get(session); + +/* 循环接收响应数据直到数据接收完毕 */ +do +{ + bytes_read = webclient_read(session, buffer, 1024); + if (bytes_read <= 0) + { + break; + } + + /* 打印响应数据 */ + for (index = 0; index < bytes_read; index++) + { + rt_kprintf("%c", buffer[index]); + } + + content_pos += bytes_read; +} while (content_pos < content_length); +``` + +(5) **关闭并释放客户端会话结构体** + +请求发送并接收完成之后,需要使用 `webclient_close` 函数关闭并释放客户端会话结构体,完成整个 HTTP 数据交互流程。 + +使用方式如下: + +```c +if (session) +{ + webclient_close(session); +} +``` + +## 使用方式 + +WenClient 软件包对于 GET/POST 请求,分别提供了几种不同的使用方式,用于不同的情况。 + +### GET 请求方式 + +- 使用默认头部发送 GET 请求 + +```c +struct webclient_session *session = NULL; + +session = webclient_create(1024); + +if(webclient_get(session, URI) != 200) +{ + LOG_E("error!"); +} + +while(1) +{ + webclient_read(session, buffer, bfsz); + ... +} + +webclient_close(session); +``` + +- 使用自定义头部发送 GET 请求 + +```c +struct webclient_session *session = NULL; + +session = webclient_create(1024); + +webclient_header_fields_add(session, "User-Agent: RT-Thread HTTP Agent\r\n"); + +if(webclient_get(session, URI) != 200) +{ + LOG_E("error!"); +} + +while(1) +{ + webclient_read(session, buffer, bfsz); + ... +} + +webclient_close(session); +``` + +- 发送获取部分数据的 GET 请求(多用于断点续传/分片下载) + +```c +struct webclient_session *session = NULL; + +session = webclient_create(1024); + +webclient_connect(session, URI); +webclient_header_fields_add(session, "Range: bytes=%d-%d\r\n", 0, 99); +webclient_send_header(session, WEBCLIENT_GET); + +while(1) +{ + webclient_read(session, buffer, bfsz); + ... +} + +webclient_close(session); +``` + +- 使用 `webclient_response` 接收 GET 数据 + + 多用于接收数据长度较小的 GET 请求。 + +```c +struct webclient_session *session = NULL; +size_t length = 0; +char *result; + +session = webclient_create(1024); + +if(webclient_get(session, URI) != 200) +{ + LOG_E("error!"); +} + +webclient_response(session, &result, &length); + +web_free(result); +webclient_close(session); +``` + +- 使用 `webclient_request` 函数发送并接收 GET 请求 + + 多用于接收数据长度较小,且头部信息已经拼接给出的 GET 请求。 + +```c +size_t length = 0; +char *result, *header = RT_NULL; + +/* 拼接自定义头部数据 */ +webclient_request_header_add(&header, "User-Agent: RT-Thread HTTP Agent\r\n"); + +webclient_request(URI, header, NULL, 0, &result, &length); + +web_free(result); +``` + +### POST 请求方式 + +- 分段数据 POST 请求 + + 多用于上传数据量较大的 POST 请求,如:上传文件到服务器。 + +```c +struct webclient_session *session = NULL; + +session = webclient_create(1024); + +/* 拼接必要的头部信息 */ +webclient_header_fields_add(session, "Content-Length: %d\r\n", post_data_sz); +webclient_header_fields_add(session, "Content-Type: application/octet-stream\r\n"); + +/* 分段数据上传 webclient_post 第三个传输上传数据为 NULL,改为下面循环上传数据*/ +if( webclient_post(session, URI, NULL, 0) != 200) +{ + LOG_E("error!"); +} + +while(1) +{ + webclient_write(session, post_data, 1024); + ... +} + +if( webclient_handle_response(session) != 200) +{ + LOG_E("error!"); +} + +webclient_close(session); +``` + +- 整段数据 POST 请求 + + 多用于上传数据量较小的 POST 请求。 + +```c +char *post_data = "abcdefg"; + +session = webclient_create(1024); + +/* 拼接必要的头部信息 */ +webclient_header_fields_add(session, "Content-Length: %d\r\n", strlen(post_data)); +webclient_header_fields_add(session, "Content-Type: application/octet-stream\r\n"); + +if(webclient_post(session, URI, post_data, rt_strlen(post_data)) != 200); +{ + LOG_E("error!"); +} +webclient_close(session); +``` + +- 使用 `webclient_request` 函数发送 POST 请求 + + 多用于上传文件较小且头头部信息已经拼接给出的 POST 请求。 + +```c +char *post_data = "abcdefg"; +char *header = RT_NULL; + +/* 拼接自定义头部数据 */ +webclient_request_header_add(&header, "Content-Length: %d\r\n", strlen(post_data)); +webclient_request_header_add(&header, "Content-Type: application/octet-stream\r\n"); + +webclient_request(URI, header, post_data, rt_strlen(post_data), NULL, NULL); +``` + +## 常见问题 + +### HTTPS 地址不支持 + +```c +[E/WEB]not support https connect, please enable webclient https configure! +``` + +- 原因:使用 HTTPS 地址但是没有开启 HTTPS 支持。 + +- 解决方法:在 WebClient 软件包 menuconfig 配置选项中 选择 `Select TLS mode` 选项为 `MbedTLS support` 或者 `SAL TLS support`。 + +### 头部数据长度超出 + +```c +[E/WEB]not enough header buffer size(xxx)! +``` + +- 原因:添加的头部数据长度超过了最大支持的头部数据长度。 + +- 解决方法:在创建客户端会话结构体的时候,增大传入的最大支持的头部数据长度。 diff --git a/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/webclient/docs/version.md b/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/webclient/docs/version.md new file mode 100644 index 0000000..1a57821 --- /dev/null +++ b/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/webclient/docs/version.md @@ -0,0 +1,6 @@ +# 版本和修订 + +| Date | Version | Author | Note | +| -------- | :-----: | :---- | :---- | +| 2013-05-05 | v1.0.0 | bernard | 初始版本 | +| 2018-08-06 | v2.0.0 | chenyong | 版本更新 | \ No newline at end of file diff --git a/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/webclient/inc/webclient.h b/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/webclient/inc/webclient.h new file mode 100644 index 0000000..2e5d319 --- /dev/null +++ b/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/webclient/inc/webclient.h @@ -0,0 +1,277 @@ +/* + * Copyright (c) 2006-2018, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2013-05-05 Bernard the first version + * 2013-06-10 Bernard fix the slow speed issue when download file. + * 2015-11-14 aozima add content_length_remainder. + * 2017-12-23 aozima update gethostbyname to getaddrinfo. + * 2018-01-04 aozima add ipv6 address support. + * 2018-07-26 chenyong modify log information + * 2018-08-07 chenyong modify header processing + */ + +#ifndef __WEBCLIENT_H__ +#define __WEBCLIENT_H__ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define CONFIG_WEBCLIENT_HTTPS_SUPPORTED +#ifdef CONFIG_WEBCLIENT_HTTPS_SUPPORTED +#include "tls_client.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef CONFIG_TARGET_PLATFORM_RTTHREAD +// RT_Thread +#ifndef web_malloc +#define web_malloc rt_malloc +#endif + +#ifndef web_calloc +#define web_calloc rt_calloc +#endif + +#ifndef web_realloc +#define web_realloc rt_realloc +#endif + +#ifndef web_free +#define web_free rt_free +#endif + +#ifndef web_strdup +#define web_strdup rt_strdup +#endif +#else +// Linux X86_64 +#define RT_NULL NULL +#define RT_FALSE false +#define rt_int32_t int32_t +#define rt_uint32_t uint32_t + +#define RT_ASSERT assert +#define rt_calloc calloc +#define rt_strlen strlen +#define rt_strcpy strcpy +#define rt_strncpy strncpy +#define rt_strcmp strcmp +#define rt_strstr strstr +#define rt_memset memset +#define rt_vsnprintf vsnprintf +#define rt_snprintf snprintf +#define LOG_E printf +#define LOG_D printf +#define rt_kprintf printf + +#define web_malloc malloc +#define web_calloc calloc +#define web_realloc realloc +#define web_free free +#define web_strdup strdup + +/* RT-Thread error code definitions */ +#define RT_EOK 0 /**< There is no error */ +#define RT_ERROR 1 /**< A generic error happens */ +#define RT_ETIMEOUT 2 /**< Timed out */ +#define RT_EFULL 3 /**< The resource is full */ +#define RT_EEMPTY 4 /**< The resource is empty */ +#define RT_ENOMEM 5 /**< No memory */ +#define RT_ENOSYS 6 /**< No system */ +#define RT_EBUSY 7 /**< Busy */ +#define RT_EIO 8 /**< IO error */ +#define RT_EINTR 9 /**< Interrupted system call */ +#define RT_EINVAL 10 /**< Invalid argument */ +#endif // CONFIG_TARGET_PLATFORM_RTTHREAD + +#define WEBCLIENT_SW_VERSION "2.2.0" +#define WEBCLIENT_SW_VERSION_NUM 0x20200 + +#define WEBCLIENT_HEADER_BUFSZ 4096 +#define WEBCLIENT_RESPONSE_BUFSZ 4096 + +enum WEBCLIENT_STATUS { + WEBCLIENT_OK, + WEBCLIENT_ERROR, + WEBCLIENT_TIMEOUT, + WEBCLIENT_NOMEM, + WEBCLIENT_NOSOCKET, + WEBCLIENT_NOBUFFER, + WEBCLIENT_CONNECT_FAILED, + WEBCLIENT_DISCONNECT, + WEBCLIENT_FILE_ERROR, +}; + +enum WEBCLIENT_METHOD { WEBCLIENT_USER_METHOD, WEBCLIENT_GET, WEBCLIENT_POST, WEBCLIENT_HEAD, WEBCLIENT_PUT }; + +struct webclient_header { + char *buffer; + size_t length; /* content header buffer size */ + + size_t size; /* maximum support header size */ +}; + +typedef struct webclient_session { + struct webclient_header *header; /* webclient response header information */ + int socket; + int resp_status; + + char *host; /* server host */ + int port; /* server port */ + char *req_url; /* HTTP request address*/ + + int chunk_sz; + int chunk_offset; + + int content_length; + size_t content_remainder; /* remainder of content length */ + int (*handle_function)(char *buffer, int size); /* handle function */ + + bool is_tls; /* HTTPS connect */ + +#ifdef CONFIG_WEBCLIENT_HTTPS_SUPPORTED + MbedTLSSession *tls_session; /* mbedtls connect session */ + char *cert_buf; /* the certificate of Server if URL uses HTTPS to connect */ + size_t cert_len; /* the length of certificate, include the terminal character '\0' */ +#endif +} webclient_session; + +/** + * @brief create webclient session and set header response size + * + * @param header_sz the max length of header supported + * @return struct webclient_session* + */ +struct webclient_session *webclient_session_create(size_t header_sz, const char *cert_buf, size_t cert_len); + +/** + * @brief close and release wenclient session + * + * @param session the webclient session which is already created successfully + * @return 0: close successfully + */ +int webclient_close(struct webclient_session *session); + +/** + * @brief send HTTP GET request + * + * @param session the webclient session + * @param URI the address of the server connected by HTTP + * @return >0: the code of HTTP response; + * <0: failure to send GET request. + */ +int webclient_get(struct webclient_session *session, const char *URI); + +/* send HTTP HEAD request */ +int webclient_shard_head_function(struct webclient_session *session, const char *URI, int *length); + +/* send HTTP Range parameter, shard download */ +int webclient_shard_position_function(struct webclient_session *session, const char *URI, int start, + int length, int mem_size); +int *webclient_register_shard_position_function(struct webclient_session *session, + int (*handle_function)(char *buffer, int size)); + +/** + * @brief send POST request to server and get response header data. + * + * @param session webclient session + * @param URI input server URI address + * @param post_data data send to the server + * = NULL: just connect server and send header + * != NULL: send header and body data, resolve response data + * @param data_len the length of send data + * + * @return <0: send POST request failed + * =0: send POST header success + * >0: response http status code + */ +int webclient_post(struct webclient_session *session, const char *URI, const void *post_data, + size_t data_len); + +/** + * @brief send PUT request to server and get response header data. + * + * @param session webclient session + * @param URI input server URI address + * @param put_data data send to the server + * = NULL: just connect server and send header + * != NULL: send header and body data, resolve response data + * @param data_len the length of send data + * + * @return <0: send POST request failed + * =0: send POST header success + * >0: response http status code + */ +int webclient_put(struct webclient_session *session, const char *URI, const void *put_data, + size_t data_len); + +int webclient_set_timeout(struct webclient_session *session, int millisecond); + +/* send or receive data from server */ +int webclient_read(struct webclient_session *session, void *buffer, size_t size); +int webclient_write(struct webclient_session *session, const void *buffer, size_t size); + +/* webclient GET/POST header buffer operate by the header fields */ +int webclient_header_fields_add(struct webclient_session *session, const char *fmt, ...); +const char *webclient_header_fields_get(struct webclient_session *session, const char *fields); + +/** + * @brief get wenclient request response data. + * + * @param session wenclient session + * @param response response buffer address + * @param resp_len response buffer length + * + * @return response data size + */ +int webclient_response(struct webclient_session *session, void **response, size_t *resp_len); + +/** + * @brief send request(GET/POST) to server and get response data. + * + * @param URI input server address + * @param header send header data + * = NULL: use default header data, must be GET request + * != NULL: user custom header data, GET or POST request + * @param post_data data sent to the server + * = NULL: it is GET request + * != NULL: it is POST request + * @param data_len send data length + * @param response response buffer address + * @param resp_len response buffer length + * @param cert_buf the certificate of Server if URL uses HTTPS to connect + * @param cert_len the length of certificate, include the terminal character '\0' + * + * @return <0: request failed + * >=0: response buffer size + */ +int webclient_request(const char *URI, const char *header, const void *post_data, size_t data_len, + void **response, size_t *resp_len, const char *cert_buf, size_t cert_len); +int webclient_request_header_add(char **request_header, const char *fmt, ...); +int webclient_resp_status_get(struct webclient_session *session); +int webclient_content_length_get(struct webclient_session *session); + +#ifdef RT_USING_DFS +/* file related operations */ +int webclient_get_file(const char *URI, const char *filename); +int webclient_post_file(const char *URI, const char *filename, const char *form_data); +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/webclient/samples/webclient_get_sample.c b/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/webclient/samples/webclient_get_sample.c new file mode 100644 index 0000000..8185229 --- /dev/null +++ b/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/webclient/samples/webclient_get_sample.c @@ -0,0 +1,212 @@ +/* + * Copyright (c) 2006-2018, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2018-08-03 chenyong the first version + */ + +#include +#include + +#define GET_HEADER_BUFSZ 1024 +#define GET_RESP_BUFSZ 1024 + +#define GET_LOCAL_URI "http://www.rt-thread.com/service/rt-thread.txt" + +/* send HTTP GET request by common request interface, it used to receive longer data */ +static int webclient_get_comm(const char *uri) +{ + struct webclient_session* session = RT_NULL; + unsigned char *buffer = RT_NULL; + int index, ret = 0; + int bytes_read, resp_status; + int content_length = -1; + + buffer = (unsigned char *) web_malloc(GET_RESP_BUFSZ); + if (buffer == RT_NULL) + { + rt_kprintf("no memory for receive buffer.\n"); + ret = -RT_ENOMEM; + goto __exit; + + } + + /* create webclient session and set header response size */ + session = webclient_session_create(GET_HEADER_BUFSZ); + if (session == RT_NULL) + { + ret = -RT_ENOMEM; + goto __exit; + } + + /* send GET request by default header */ + if ((resp_status = webclient_get(session, uri)) != 200) + { + rt_kprintf("webclient GET request failed, response(%d) error.\n", resp_status); + ret = -RT_ERROR; + goto __exit; + } + + rt_kprintf("webclient get response data: \n"); + + content_length = webclient_content_length_get(session); + if (content_length < 0) + { + rt_kprintf("webclient GET request type is chunked.\n"); + do + { + bytes_read = webclient_read(session, (void *)buffer, GET_RESP_BUFSZ); + if (bytes_read <= 0) + { + break; + } + + for (index = 0; index < bytes_read; index++) + { + rt_kprintf("%c", buffer[index]); + } + } while (1); + + rt_kprintf("\n"); + } + else + { + int content_pos = 0; + + do + { + bytes_read = webclient_read(session, (void *)buffer, + content_length - content_pos > GET_RESP_BUFSZ ? + GET_RESP_BUFSZ : content_length - content_pos); + if (bytes_read <= 0) + { + break; + } + + for (index = 0; index < bytes_read; index++) + { + rt_kprintf("%c", buffer[index]); + } + + content_pos += bytes_read; + } while (content_pos < content_length); + + rt_kprintf("\n"); + } + +__exit: + if (session) + { + webclient_close(session); + } + + if (buffer) + { + web_free(buffer); + } + + return ret; +} + +/* send HTTP GET request by simplify request interface, it used to received shorter data */ +static int webclient_get_smpl(const char *uri) +{ + char *response = RT_NULL; + size_t resp_len = 0; + int index; + + if (webclient_request(uri, RT_NULL, RT_NULL, 0, (void **)&response, &resp_len) < 0) + { + rt_kprintf("webclient send get request failed."); + return -RT_ERROR; + } + + rt_kprintf("webclient send get request by simplify request interface.\n"); + rt_kprintf("webclient get response data: \n"); + for (index = 0; index < rt_strlen(response); index++) + { + rt_kprintf("%c", response[index]); + } + rt_kprintf("\n"); + + if (response) + { + web_free(response); + } + + return 0; +} + + +int webclient_get_test(int argc, char **argv) +{ + char *uri = RT_NULL; + + if (argc == 1) + { + uri = web_strdup(GET_LOCAL_URI); + if(uri == RT_NULL) + { + rt_kprintf("no memory for create get request uri buffer.\n"); + return -RT_ENOMEM; + } + + webclient_get_comm(uri); + } + else if (argc == 2) + { + if (rt_strcmp(argv[1], "-s") == 0) + { + uri = web_strdup(GET_LOCAL_URI); + if(uri == RT_NULL) + { + rt_kprintf("no memory for create get request uri buffer.\n"); + return -RT_ENOMEM; + } + + webclient_get_smpl(uri); + } + else + { + uri = web_strdup(argv[1]); + if(uri == RT_NULL) + { + rt_kprintf("no memory for create get request uri buffer.\n"); + return -RT_ENOMEM; + } + webclient_get_comm(uri); + } + } + else if(argc == 3 && rt_strcmp(argv[1], "-s") == 0) + { + uri = web_strdup(argv[2]); + if(uri == RT_NULL) + { + rt_kprintf("no memory for create get request uri buffer.\n"); + return -RT_ENOMEM; + } + + webclient_get_smpl(uri); + } + else + { + rt_kprintf("web_get_test [URI] - webclient GET request test.\n"); + rt_kprintf("web_get_test -s [URI] - webclient simplify GET request test.\n"); + return -RT_ERROR; + } + + if (uri) + { + web_free(uri); + } + + return RT_EOK; +} + +#ifdef FINSH_USING_MSH +#include +MSH_CMD_EXPORT_ALIAS(webclient_get_test, web_get_test, webclient get request test); +#endif /* FINSH_USING_MSH */ diff --git a/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/webclient/samples/webclient_post_sample.c b/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/webclient/samples/webclient_post_sample.c new file mode 100644 index 0000000..8efd2a1 --- /dev/null +++ b/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/webclient/samples/webclient_post_sample.c @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2006-2018, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2018-08-03 chenyong the first version + */ + +#include + +#include "agora_log.h" +#define TAG "webclient_postsample" + +#include + +#define POST_RESP_BUFSZ 1024 +#define POST_HEADER_BUFSZ 1024 + +#define POST_LOCAL_URI "https://www.rt-thread.com/service/echo" + +const char *post_data = "RT-Thread is an open source IoT operating system from China!"; + +/* send HTTP POST request by common request interface, it used to receive longer data */ +static int webclient_post_comm(const char *uri, const void *post_data, size_t data_len) +{ + struct webclient_session *session = NULL; + unsigned char *buffer = NULL; + int ret = 0; + int bytes_read, resp_status; + + buffer = (unsigned char *)calloc(POST_RESP_BUFSZ, sizeof(unsigned char)); + if (buffer == NULL) { + AGO_LOGD(TAG, "no memory for receive response buffer."); + ret = -1; + goto __exit; + } + + /* create webclient session and set header response size */ + session = webclient_session_create(POST_HEADER_BUFSZ); + if (session == NULL) { + ret = -1; + goto __exit; + } + + /* build header for upload */ + webclient_header_fields_add(session, "Content-Length: %d\r\n", strlen(post_data)); + webclient_header_fields_add(session, "Content-Type: application/octet-stream\r\n"); + + /* send POST request by default header */ + if ((resp_status = webclient_post(session, uri, post_data, data_len)) != 200) { + AGO_LOGE(TAG, "webclient POST request failed, response(%d) error.", resp_status); + ret = -RT_ERROR; + goto __exit; + } + + AGO_LOGD(TAG, "webclient post response data: "); + do { + bytes_read = webclient_read(session, buffer, POST_RESP_BUFSZ); + if (bytes_read <= 0) { + break; + } + AGO_LOGD(TAG, "%s", buffer); + } while (1); + +__exit: + if (session) { + webclient_close(session); + } + if (buffer) { + free(buffer); + } + + return ret; +} + +/* send HTTP POST request by simplify request interface, it used to received shorter data */ +static int webclient_post_smpl(const char *uri, const char *post_data, size_t data_len) +{ + char *response = NULL; + char *header = NULL; + size_t resp_len = 0; + + webclient_request_header_add(&header, "Content-Length: %d\r\n", strlen(post_data)); + webclient_request_header_add(&header, "Content-Type: application/octet-stream\r\n"); + + if (webclient_request(uri, header, post_data, data_len, (void **)&response, &resp_len) < 0) { + AGO_LOGE(TAG, "webclient send post request failed."); + free(header); + return -RT_ERROR; + } + + AGO_LOGD(TAG, "webclient send post request by simplify request interface."); + AGO_LOGD(TAG, "webclient post response data: "); + AGO_LOGD(TAG, "%s", response); + AGO_LOGD(TAG, "\n"); + + if (header) { + free(header); + } + if (response) { + free(response); + } + + return 0; +} + +int main(int argc, char **argv) +{ + char *uri = NULL; + + if (argc == 1) { + uri = strdup(POST_LOCAL_URI); + if (uri == NULL) { + AGO_LOGD(TAG, "no memory for create post request uri buffer."); + return -1; + } + + webclient_post_comm(uri, (void *)post_data, strlen(post_data)); + } else if (argc == 2) { + if (strcmp(argv[1], "-s") == 0) { + uri = strdup(POST_LOCAL_URI); + if (uri == NULL) { + AGO_LOGD(TAG, "no memory for create post request uri buffer."); + return -1; + } + + webclient_post_smpl(uri, (void *)post_data, strlen(post_data)); + } else { + uri = strdup(argv[1]); + if (uri == NULL) { + AGO_LOGD(TAG, "no memory for create post request uri buffer."); + return -1; + } + webclient_post_comm(uri, (void *)post_data, strlen(post_data)); + } + } else if (argc == 3 && strcmp(argv[1], "-s") == 0) { + uri = strdup(argv[2]); + if (uri == NULL) { + AGO_LOGD(TAG, "no memory for create post request uri buffer."); + return -1; + } + + webclient_post_smpl(uri, (void *)post_data, strlen(post_data)); + } else { + AGO_LOGD(TAG, "web_post_test [uri] - webclient post request test."); + AGO_LOGD(TAG, "web_post_test -s [uri] - webclient simplify post request test."); + return -1; + } + + if (uri) { + free(uri); + } + + return 0; +} diff --git a/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/webclient/src/webclient.c b/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/webclient/src/webclient.c new file mode 100644 index 0000000..2e13ca9 --- /dev/null +++ b/firmware/echoear-platform-agent/components/volc_conv_ai/third_party/webclient/src/webclient.c @@ -0,0 +1,1591 @@ +/* + * Copyright (c) 2006-2019, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2013-05-05 Bernard the first version + * 2013-06-10 Bernard fix the slow speed issue when download file. + * 2015-11-14 aozima add content_length_remainder. + * 2017-12-23 aozima update gethostbyname to getaddrinfo. + * 2018-01-04 aozima add ipv6 address support. + * 2018-07-26 chenyong modify log information + * 2018-08-07 chenyong modify header processing + * 2021-06-09 xiangxistu add shard download function + */ + +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include + +#include "util/volc_list.h" +#include "util/volc_log.h" +#include "volc_osal.h" + +#define TAG "webclient" + +/* default receive or send timeout */ +#define WEBCLIENT_DEFAULT_TIMEO 6 + +extern long int strtol(const char *nptr, char **endptr, int base); + +static int webclient_strncasecmp(const char *a, const char *b, size_t n) +{ + uint8_t c1, c2; + if (n <= 0) + return 0; + do { + c1 = tolower(*a++); + c2 = tolower(*b++); + } while (--n && c1 && c1 == c2); + return c1 - c2; +} + +static const char *webclient_strstri(const char *str, const char *subStr) +{ + int len = strlen(subStr); + + if (len == 0) { + return RT_NULL; + } + + while (*str) { + if (webclient_strncasecmp(str, subStr, len) == 0) { + return str; + } + ++str; + } + return RT_NULL; +} + +static int webclient_send(struct webclient_session *session, const void *buffer, size_t len, int flag) +{ +#ifdef CONFIG_WEBCLIENT_HTTPS_SUPPORTED + if (session->tls_session) { + return mbedtls_client_write(session->tls_session, buffer, len); + } +#endif + + return send(session->socket, buffer, len, flag); +} + +static int webclient_recv(struct webclient_session *session, void *buffer, size_t len, int flag) +{ +#ifdef CONFIG_WEBCLIENT_HTTPS_SUPPORTED + if (session->tls_session) { + return mbedtls_client_read(session->tls_session, buffer, len); + } +#endif + return recv(session->socket, buffer, len, flag); +} + +static int webclient_read_line(struct webclient_session *session, char *buffer, int size) +{ + int rc, count = 0; + char ch = 0, last_ch = 0; + + RT_ASSERT(session); + RT_ASSERT(buffer); + + /* Keep reading until we fill the buffer. */ + while (count < size) { + rc = webclient_recv(session, (unsigned char *)&ch, 1, 0); +#if defined(CONFIG_WEBCLIENT_HTTPS_SUPPORTED) + if (session->is_tls && (rc == MBEDTLS_ERR_SSL_WANT_READ || rc == MBEDTLS_ERR_SSL_WANT_WRITE)) { + continue; + } +#endif + if (rc <= 0) + return rc; + + if (ch == '\n' && last_ch == '\r') + break; + + buffer[count++] = ch; + + last_ch = ch; + } + + if (count > size) { + LOGE( "read line failed. The line data length is out of buffer size(%d)!", count); + return -WEBCLIENT_ERROR; + } + + return count; +} + +/** + * resolve server address + * + * @param session http session + * @param res the server address information + * @param url the input server URI address + * @param request the pointer to point the request url, for example, /index.html + * + * @return 0 on resolve server address OK, others failed + * + * URL example: + * http://www.rt-thread.org + * http://www.rt-thread.org:80 + * https://www.rt-thread.org/ + * http://192.168.1.1:80/index.htm + * http://[fe80::1] + * http://[fe80::1]/ + * http://[fe80::1]/index.html + * http://[fe80::1]:80/index.html + */ +static int webclient_resolve_address(struct webclient_session *session, struct addrinfo **res, const char *url, + const char **request) +{ + int rc = WEBCLIENT_OK; + char *ptr; + char port_str[6] = "80"; /* default port of 80(http) */ + const char *port_ptr; + const char *path_ptr; + + const char *host_addr = 0; + int url_len, host_addr_len = 0; + + RT_ASSERT(res); + RT_ASSERT(request); + + /* make *res == NULL before getaddrinfo */ + *res = RT_NULL; + + url_len = rt_strlen(url); + + /* strip protocol(http or https) */ + if (strncmp(url, "http://", 7) == 0) { + host_addr = url + 7; + } else if (strncmp(url, "https://", 8) == 0) { + rt_strncpy(port_str, "443", 4); + host_addr = url + 8; + } else { + rc = -WEBCLIENT_ERROR; + goto __exit; + } + + /* ipv6 address */ + if (host_addr[0] == '[') { + host_addr += 1; + ptr = rt_strstr(host_addr, "]"); + if (!ptr) { + rc = -WEBCLIENT_ERROR; + goto __exit; + } + host_addr_len = ptr - host_addr; + } + + path_ptr = rt_strstr(host_addr, "/"); + *request = path_ptr ? path_ptr : "/"; + + /* resolve port */ + port_ptr = rt_strstr(host_addr + host_addr_len, ":"); + if (port_ptr && (!path_ptr || (port_ptr < path_ptr))) { + if (!path_ptr) { + rt_strncpy(port_str, port_ptr + 1, url + url_len - (port_ptr + 1)); + } else { + int port_len = path_ptr - port_ptr - 1; + rt_strncpy(port_str, port_ptr + 1, port_len); + port_str[port_len] = '\0'; + } + } else { + port_ptr = RT_NULL; + } + + if (port_ptr && (!path_ptr)) { + rt_strncpy(port_str, port_ptr + 1, url + url_len - (port_ptr + 1)); + } + + /* ipv4 or domain. */ + if (!host_addr_len) { + if (port_ptr) { + host_addr_len = port_ptr - host_addr; + } else if (path_ptr) { + host_addr_len = path_ptr - host_addr; + } else { + host_addr_len = strlen(host_addr); + } + } + + if ((host_addr_len < 1) || (host_addr_len > url_len)) { + rc = -WEBCLIENT_ERROR; + goto __exit; + } + + /* get host address ok. */ + { + char *host_addr_new = volc_osal_malloc(host_addr_len + 1); + + if (!host_addr_new) { + rc = -WEBCLIENT_ERROR; + goto __exit; + } + + memcpy(host_addr_new, host_addr, host_addr_len); + host_addr_new[host_addr_len] = '\0'; + session->host = host_addr_new; + } + +#ifdef CONFIG_WEBCLIENT_HTTPS_SUPPORTED + if (session->tls_session) { + session->tls_session->port = web_strdup(port_str); + session->tls_session->host = web_strdup(session->host); + if (session->tls_session->port == RT_NULL || session->tls_session->host == RT_NULL) { + return -WEBCLIENT_NOMEM; + } + + return rc; + } +#endif + + /* resolve the host name. */ + { + struct addrinfo hint; + int ret; + + rt_memset(&hint, 0, sizeof(hint)); + ret = getaddrinfo(session->host, port_str, &hint, res); + if (ret != 0) { + LOGE( "getaddrinfo err: %d '%s'.", ret, session->host); + rc = -WEBCLIENT_ERROR; + goto __exit; + } + } + +__exit: + if (rc != WEBCLIENT_OK) { + HAL_SAFE_FREE(session->host); + + if (*res) { + freeaddrinfo(*res); + *res = RT_NULL; + } + } + + return rc; +} + +#ifdef CONFIG_WEBCLIENT_HTTPS_SUPPORTED +static int webclient_open_tls(struct webclient_session *session, const char *URI) +{ + int tls_ret = 0; + const char *pers = "webclient"; + + RT_ASSERT(session); + + session->tls_session = (MbedTLSSession *)volc_osal_calloc(1, sizeof(MbedTLSSession)); + if (session->tls_session == RT_NULL) { + return -WEBCLIENT_NOMEM; + } + + session->tls_session->buffer_len = WEBCLIENT_RESPONSE_BUFSZ; + session->tls_session->buffer = volc_osal_malloc(session->tls_session->buffer_len); + if (session->tls_session->buffer == RT_NULL) { + LOGE( "no memory for tls_session buffer!"); + return -WEBCLIENT_ERROR; + } + + if ((tls_ret = mbedtls_client_init(session->tls_session, (void *)pers, strlen(pers))) < 0) { + LOGE( "initialize https client failed return: -0x%x.", -tls_ret); + return -WEBCLIENT_ERROR; + } + + return WEBCLIENT_OK; +} +#endif + +/** + * connect to http server. + * + * @param session webclient session + * @param URI the input server URI address + * + * @return <0: connect failed or other error + * =0: connect success + */ +static int webclient_connect(struct webclient_session *session, const char *URI) +{ + int rc = WEBCLIENT_OK; + int socket_handle; + struct timeval timeout; + struct addrinfo *res = RT_NULL; + const char *req_url; + + RT_ASSERT(session); + RT_ASSERT(URI); + LOGD( "uri: %s", URI); + + timeout.tv_sec = WEBCLIENT_DEFAULT_TIMEO; + timeout.tv_usec = 0; + + if (strncmp(URI, "https://", 8) == 0) { +#ifdef CONFIG_WEBCLIENT_HTTPS_SUPPORTED + if (!session->cert_buf || (session->cert_len <= 1)) { + LOGE( "invalid certificate"); + return -WEBCLIENT_ERROR; + } + if (webclient_open_tls(session, URI) < 0) { + LOGE( "connect failed, https client open URI(%s) failed!", URI); + return -WEBCLIENT_ERROR; + } + session->is_tls = true; +#else + LOGE( "not support https connect, please enable webclient https configure!"); + rc = -WEBCLIENT_ERROR; + goto __exit; +#endif + } + + /* Check valid IP address and URL */ + rc = webclient_resolve_address(session, &res, URI, &req_url); + if (rc != WEBCLIENT_OK) { + LOGE( "connect failed, resolve address error(%d).", rc); + goto __exit; + } + + /* Not use 'getaddrinfo()' for https connection */ + if (session->is_tls == RT_FALSE && res == RT_NULL) { + rc = -WEBCLIENT_ERROR; + goto __exit; + } + + /* copy host address */ + if (req_url) { + session->req_url = web_strdup(req_url); + } else { + LOGE( "connect failed, resolve request address error."); + rc = -WEBCLIENT_ERROR; + goto __exit; + } + +#ifdef CONFIG_WEBCLIENT_HTTPS_SUPPORTED + if (session->tls_session) { + int tls_ret = 0; + + if ((tls_ret = mbedtls_client_context(session->tls_session)) < 0) { + LOGE( "connect failed, https client context return: -0x%x", -tls_ret); + return -WEBCLIENT_ERROR; + } + + if ((tls_ret = mbedtls_client_connect(session->tls_session)) < 0) { + LOGE( "connect failed, https client connect return: -0x%x", -tls_ret); + return -WEBCLIENT_CONNECT_FAILED; + } + + socket_handle = session->tls_session->server_fd.fd; + + /* set recv timeout option */ + setsockopt(socket_handle, SOL_SOCKET, SO_RCVTIMEO, (void *)&timeout, sizeof(timeout)); + setsockopt(socket_handle, SOL_SOCKET, SO_SNDTIMEO, (void *)&timeout, sizeof(timeout)); + + session->socket = socket_handle; + + return WEBCLIENT_OK; + } +#endif + + { + socket_handle = socket(res->ai_family, SOCK_STREAM, IPPROTO_TCP); + + if (socket_handle < 0) { + LOGE( "connect failed, create socket(%d) error.", socket_handle); + rc = -WEBCLIENT_NOSOCKET; + goto __exit; + } + + /* set receive and send timeout option */ + setsockopt(socket_handle, SOL_SOCKET, SO_RCVTIMEO, (void *)&timeout, sizeof(timeout)); + setsockopt(socket_handle, SOL_SOCKET, SO_SNDTIMEO, (void *)&timeout, sizeof(timeout)); + struct sockaddr_in *ipv4 = (struct sockaddr_in *)res->ai_addr; + int port = ntohs(ipv4->sin_port); + if (connect(socket_handle, res->ai_addr, res->ai_addrlen) != 0) { + /* connect failed, close socket */ + LOGE( "connect failed, connect socket(%d) error.", socket_handle); + close(socket_handle); + rc = -WEBCLIENT_CONNECT_FAILED; + goto __exit; + } + session->port = ntohs(((struct sockaddr_in *)res->ai_addr)->sin_port); + session->socket = socket_handle; + } + +__exit: + if (res) { + freeaddrinfo(res); + } + + return rc; +} + +/** + * add fields data to request header data. + * + * @param session webclient session + * @param fmt fields format + * + * @return >0: data length of successfully added + * <0: not enough header buffer size + */ +int webclient_header_fields_add(struct webclient_session *session, const char *fmt, ...) +{ + rt_int32_t length; + va_list args; + + RT_ASSERT(session); + RT_ASSERT(session->header->buffer); + + va_start(args, fmt); + length = rt_vsnprintf(session->header->buffer + session->header->length, + session->header->size - session->header->length, fmt, args); + va_end(args); + if (length < 0) { + LOGE( "add fields header data failed, return length(%d) error.", (int)length); + return -WEBCLIENT_ERROR; + } + + session->header->length += length; + + /* check header size */ + if (session->header->length >= session->header->size) { + LOGE( "not enough header buffer size(%d)!", (int)session->header->size); + return -WEBCLIENT_ERROR; + } + + return length; +} + +/** + * get fields information from request/response header data. + * + * @param session webclient session + * @param fields fields keyword + * + * @return = NULL: get fields data failed + * != NULL: success get fields data + */ +const char *webclient_header_fields_get(struct webclient_session *session, const char *fields) +{ + char *resp_buf = RT_NULL; + size_t resp_buf_len = 0; + + RT_ASSERT(session); + RT_ASSERT(session->header->buffer); + + resp_buf = session->header->buffer; + while (resp_buf_len < session->header->length) { + if (webclient_strstri(resp_buf, fields) == resp_buf) { + char *mime_ptr = RT_NULL; + + /* jump space */ + mime_ptr = rt_strstr(resp_buf, ":"); + if (mime_ptr != NULL) { + mime_ptr += 1; + + while (*mime_ptr && (*mime_ptr == ' ' || *mime_ptr == '\t')) + mime_ptr++; + + return mime_ptr; + } + } + + if (*resp_buf == '\0') + break; + + resp_buf += rt_strlen(resp_buf) + 1; + resp_buf_len += rt_strlen(resp_buf) + 1; + } + + return RT_NULL; +} + +/** + * get http response status code. + * + * @param session webclient session + * + * @return response status code + */ +int webclient_resp_status_get(struct webclient_session *session) +{ + RT_ASSERT(session); + + return session->resp_status; +} + +/** + * get http response data content length. + * + * @param session webclient session + * + * @return response content length + */ +int webclient_content_length_get(struct webclient_session *session) +{ + RT_ASSERT(session); + + return session->content_length; +} + +static int webclient_send_header(struct webclient_session *session, int method) +{ + int rc = WEBCLIENT_OK; + char *header = RT_NULL; + + RT_ASSERT(session); + + header = session->header->buffer; + + if (session->header->length == 0 && method <= WEBCLIENT_GET) { + /* use default header data */ + if (webclient_header_fields_add(session, "GET %s HTTP/1.1\r\n", session->req_url) < 0) { + return -WEBCLIENT_NOMEM; + } + if (webclient_header_fields_add(session, "Host: %s\r\n", session->host) < 0) { + return -WEBCLIENT_NOMEM; + } + webclient_write(session, (unsigned char *)session->header->buffer, session->header->length); + } else { + if (method != WEBCLIENT_USER_METHOD) { + /* check and add fields header data */ + if (memcmp(header, "HTTP/1.", rt_strlen("HTTP/1."))) { + char *header_buffer = RT_NULL; + int length = 0; + + header_buffer = web_strdup(session->header->buffer); + if (header_buffer == RT_NULL) { + LOGE( "no memory for header buffer!"); + rc = -WEBCLIENT_NOMEM; + goto __exit; + } + + /* splice http request header data */ + if (method == WEBCLIENT_GET) + length = rt_snprintf(session->header->buffer, session->header->size, "GET %s HTTP/1.1\r\n%s", + session->req_url ? session->req_url : "/", header_buffer); + else if (method == WEBCLIENT_POST) + length = rt_snprintf(session->header->buffer, session->header->size, "POST %s HTTP/1.1\r\n%s", + session->req_url ? session->req_url : "/", header_buffer); + else if (method == WEBCLIENT_PUT) + length = rt_snprintf(session->header->buffer, session->header->size, "PUT %s HTTP/1.1\r\n%s", + session->req_url ? session->req_url : "/", header_buffer); + else if (method == WEBCLIENT_HEAD) + length = rt_snprintf(session->header->buffer, session->header->size, "HEAD %s HTTP/1.1\r\n%s", + session->req_url ? session->req_url : "/", header_buffer); + session->header->length = length; + + HAL_SAFE_FREE(header_buffer); + } + + if (strstr(header, "Host:") == RT_NULL) { + if (webclient_header_fields_add(session, "Host: %s\r\n", session->host) < 0) + return -WEBCLIENT_NOMEM; + } + + if (strstr(header, "Accept:") == RT_NULL) { + if (webclient_header_fields_add(session, "Accept: */*\r\n") < 0) + return -WEBCLIENT_NOMEM; + } + + /* header data end */ + rt_snprintf(session->header->buffer + session->header->length, session->header->size - session->header->length, + "\r\n"); + session->header->length += 2; + + /* check header size */ + if (session->header->length > session->header->size) { + LOGE( "send header failed, not enough header buffer size(%d)!", (int)session->header->size); + rc = -WEBCLIENT_NOBUFFER; + goto __exit; + } + + webclient_write(session, (unsigned char *)session->header->buffer, session->header->length); + } else { + webclient_write(session, (unsigned char *)session->header->buffer, session->header->length); + } + } + + /* get and echo request header data */ + { + char *header_str, *header_ptr; + int header_line_len; + LOGD( "request header:"); + + for (header_str = session->header->buffer; (header_ptr = rt_strstr(header_str, "\r\n")) != RT_NULL;) { + header_line_len = header_ptr - header_str; + + if (header_line_len > 0) { + LOGD( "%.*s", header_line_len, header_str); + } + header_str = header_ptr + rt_strlen("\r\n"); + } + } + +__exit: + return rc; +} + +/** + * resolve server response data. + * + * @param session webclient session + * + * @return <0: resolve response data failed + * =0: success + */ +int webclient_handle_response(struct webclient_session *session) +{ + int rc = WEBCLIENT_OK; + char *mime_buffer = RT_NULL; + char *mime_ptr = RT_NULL; + const char *transfer_encoding; + int i; + + RT_ASSERT(session); + + /* clean header buffer and size */ + rt_memset(session->header->buffer, 0x00, session->header->size); + session->header->length = 0; + + LOGD( "response header:"); + /* We now need to read the header information */ + while (1) { + mime_buffer = session->header->buffer + session->header->length; + + /* read a line from the header information. */ + rc = webclient_read_line(session, mime_buffer, session->header->size - session->header->length); + if (rc < 0) + break; + + /* End of headers is a blank line. exit. */ + if (rc == 0) + break; + if ((rc == 1) && (mime_buffer[0] == '\r')) { + mime_buffer[0] = '\0'; + break; + } + + /* set terminal charater */ + mime_buffer[rc - 1] = '\0'; + + /* echo response header data */ + LOGD( "%s", mime_buffer); + + session->header->length += rc; + + if (session->header->length >= session->header->size) { + LOGE( "not enough header buffer size(%d)!", (int)session->header->size); + return -WEBCLIENT_NOMEM; + } + } + + /* get HTTP status code */ + mime_ptr = web_strdup(session->header->buffer); + if (mime_ptr == RT_NULL) { + LOGE( "no memory for get http status code buffer!"); + return -WEBCLIENT_NOMEM; + } + + if (rt_strstr(mime_ptr, "HTTP/1.")) { + char *ptr = mime_ptr; + + ptr += rt_strlen("HTTP/1.x"); + + while (*ptr && (*ptr == ' ' || *ptr == '\t')) + ptr++; + + /* Terminate string after status code */ + for (i = 0; ((ptr[i] != ' ') && (ptr[i] != '\t')); i++) + ; + ptr[i] = '\0'; + + session->resp_status = (int)strtol(ptr, RT_NULL, 10); + } + + /* get content length */ + if (webclient_header_fields_get(session, "Content-Length") != RT_NULL) { + session->content_length = atoi(webclient_header_fields_get(session, "Content-Length")); + } + session->content_remainder = session->content_length ? (size_t)session->content_length : 0xFFFFFFFF; + + transfer_encoding = webclient_header_fields_get(session, "Transfer-Encoding"); + if (transfer_encoding && rt_strcmp(transfer_encoding, "chunked") == 0) { + char line[16]; + + /* chunk mode, we should get the first chunk size */ + webclient_read_line(session, line, session->header->size); + session->chunk_sz = strtol(line, RT_NULL, 16); + session->chunk_offset = 0; + } + + HAL_SAFE_FREE(mime_ptr); + + if (rc < 0) { + return rc; + } + + return session->resp_status; +} + +struct webclient_session *webclient_session_create(size_t header_sz, const char *cert_buf, size_t cert_len) +{ + struct webclient_session *session = RT_NULL; + + /* create session */ + session = (struct webclient_session *)volc_osal_calloc(1, sizeof(struct webclient_session)); + assert(session); + +#ifdef CONFIG_WEBCLIENT_HTTPS_SUPPORTED + if (!cert_buf || (cert_len <= 1)) { + LOGE( "invalid certificate"); + HAL_SAFE_FREE(session); + return RT_NULL; + } + + LOGD( "Certificate Length: %d\n%s", (int)cert_len, cert_buf ? cert_buf : "NULL"); + session->cert_buf = (char *)volc_osal_calloc(cert_len, 1); + assert(session->cert_buf); + memcpy(session->cert_buf, cert_buf, cert_len); + session->cert_len = cert_len; +#endif + + /* initialize the socket of session */ + session->socket = -1; + session->content_length = -1; + + session->header = (struct webclient_header *)volc_osal_calloc(1, sizeof(struct webclient_header)); + assert(session->header); + + session->header->size = header_sz; + session->header->buffer = (char *)volc_osal_calloc(1, header_sz); + assert(session->header->buffer); + + return session; +} + +static int webclient_clean(struct webclient_session *session); + +int webclient_get(struct webclient_session *session, const char *URI) +{ + int rc = WEBCLIENT_OK; + int resp_status = 0; + + RT_ASSERT(session); + RT_ASSERT(URI); + + rc = webclient_connect(session, URI); + if (rc != WEBCLIENT_OK) { + /* connect to webclient server failed. */ + return rc; + } + + rc = webclient_send_header(session, WEBCLIENT_GET); + if (rc != WEBCLIENT_OK) { + /* send header to webclient server failed. */ + return rc; + } + + /* handle the response header of webclient server */ + resp_status = webclient_handle_response(session); + + LOGI( "get position handle response(%d).", resp_status); + + if (resp_status > 0) { + const char *location = webclient_header_fields_get(session, "Location"); + + /* relocation */ + if ((resp_status == 302 || resp_status == 301) && location) { + char *new_url; + + new_url = web_strdup(location); + if (new_url == RT_NULL) { + return -WEBCLIENT_NOMEM; + } + + /* clean webclient session */ + webclient_clean(session); + /* clean webclient session header */ + session->header->length = 0; + rt_memset(session->header->buffer, 0, session->header->size); + + rc = webclient_get(session, new_url); + + HAL_SAFE_FREE(new_url); + return rc; + } + } + + return resp_status; +} + +/** + * register a handle function for http breakpoint resume and shard download. + * + * @param function + * + * @return the pointer + */ +int *webclient_register_shard_position_function(struct webclient_session *session, + int (*handle_function)(char *buffer, int size)) +{ + session->handle_function = handle_function; + + return (int *)session->handle_function; +} + +int webclient_shard_head_function(struct webclient_session *session, const char *URI, int *length) +{ + RT_ASSERT(session); + RT_ASSERT(URI); + + int rc = WEBCLIENT_OK; + int resp_status = 0; + + if (session->socket == -1) { + rc = webclient_connect(session, URI); + if (rc != WEBCLIENT_OK) { + return rc; + } + } + + /* clean header buffer and size */ + rt_memset(session->header->buffer, 0x00, session->header->size); + session->header->length = 0; + + rc = webclient_send_header(session, WEBCLIENT_HEAD); + if (rc != WEBCLIENT_OK) { + return rc; + } + + /* handle the response header of webclient server by HEAD request */ + resp_status = webclient_handle_response(session); + if (resp_status >= 0) { + *length = webclient_content_length_get(session); + LOGI( "The length[%04d] of real data of URI.", *length); + } + + return rc; +} + +int webclient_shard_position_function(struct webclient_session *session, const char *URI, int start, int length, + int mem_size) +{ + int rc = WEBCLIENT_OK; + int result = RT_EOK; + int resp_status = 0; + int resp_len = 0; + char *buffer = RT_NULL; + int start_position, end_position = 0; + int total_len = 0; + + RT_ASSERT(session); + RT_ASSERT(URI); + RT_ASSERT(mem_size); + + /* set the offset of "Range" and "total_len" */ + end_position = start; + total_len = start + length; + + /* clean header buffer and size */ + rt_memset(session->header->buffer, 0x00, session->header->size); + session->header->length = 0; + + for (start_position = end_position; total_len != end_position + 1;) { + RT_ASSERT(start_position <= total_len); + int data_len = 0; + + end_position = start_position + mem_size - 1; + if (end_position >= total_len) { + end_position = total_len - 1; + } + + /* splice header and send header */ + LOGI( "Range: [%04d -> %04d]", start_position, end_position); + webclient_header_fields_add(session, "Range: bytes=%d-%d\r\n", start_position, end_position); + rc = webclient_send_header(session, WEBCLIENT_GET); + if (rc != WEBCLIENT_OK) { + return rc; + } + + /* handle the response header of webclient server */ + resp_status = webclient_handle_response(session); + LOGI( "get position handle response(%d).", resp_status); + if (resp_status == 206) { + /* normal resp_status */ + } else if (resp_status > 0) { + const char *location = webclient_header_fields_get(session, "Location"); + + /* relocation */ + if ((resp_status == 302 || resp_status == 301) && location) { + char *new_url; + + new_url = web_strdup(location); + if (new_url == RT_NULL) { + return -WEBCLIENT_NOMEM; + } + + /* clean webclient session */ + webclient_clean(session); + /* clean webclient session header */ + session->header->length = 0; + rt_memset(session->header->buffer, 0, session->header->size); + + rc = webclient_shard_position_function(session, new_url, start, length, mem_size); + + HAL_SAFE_FREE(new_url); + return rc; + } + } else { + if (resp_status == -WEBCLIENT_ERROR) { + if (session->socket == -WEBCLIENT_ERROR) { + /* clean webclient session */ + webclient_clean(session); + if (webclient_connect(session, URI) == WEBCLIENT_OK) { + LOGI( "webclient reconnect success, retry at [%06d]", end_position); + continue; + } else { + LOGE( "webclient reconnect failed. Please retry by yourself."); + return -WEBCLIENT_ERROR; + } + } + } + } + + /* receive the incoming data */ + data_len = webclient_response(session, (void **)&buffer, (size_t *)&resp_len); + if (data_len > 0) { + start_position += mem_size; + result = session->handle_function(buffer, data_len); + if (result != RT_EOK) { + return -WEBCLIENT_ERROR; + } + } else { + /* clean webclient session */ + webclient_clean(session); + if (session->socket == -WEBCLIENT_ERROR) { + webclient_connect(session, URI); + } + } + + /* clean header buffer and size */ + rt_memset(session->header->buffer, 0x00, session->header->size); + session->header->length = 0; + } + + return rc; +} + +int webclient_put(struct webclient_session *session, const char *URI, const void *put_data, size_t data_len) +{ + int rc = WEBCLIENT_OK; + int resp_status = 0; + + /* check arguments */ + RT_ASSERT(session); + RT_ASSERT(URI); + + if ((put_data != RT_NULL) && (data_len == 0)) { + LOGE( "input put data length failed"); + return -WEBCLIENT_ERROR; + } + + rc = webclient_connect(session, URI); + if (rc != WEBCLIENT_OK) { + /* connect to webclient server failed. */ + return rc; + } + + rc = webclient_send_header(session, WEBCLIENT_PUT); + if (rc != WEBCLIENT_OK) { + /* send header to webclient server failed. */ + return rc; + } + + if (put_data && (data_len > 0)) { + webclient_write(session, put_data, data_len); + } + + /* resolve response data, get http status code */ + resp_status = webclient_handle_response(session); + LOGD( "put handle response(%d).", resp_status); + + return resp_status; +} + +int webclient_post(struct webclient_session *session, const char *URI, const void *post_data, size_t data_len) +{ + int rc = WEBCLIENT_OK; + int resp_status = 0; + + /* check arguments */ + RT_ASSERT(session); + RT_ASSERT(URI); + + if ((post_data != RT_NULL) && (data_len == 0)) { + LOGE( "input post data length failed"); + return -WEBCLIENT_ERROR; + } + + rc = webclient_connect(session, URI); + if (rc != WEBCLIENT_OK) { + /* connect to webclient server failed. */ + return rc; + } + + rc = webclient_send_header(session, WEBCLIENT_POST); + if (rc != WEBCLIENT_OK) { + /* send header to webclient server failed. */ + return rc; + } + + if (post_data && (data_len > 0)) { + webclient_write(session, post_data, data_len); + } + + /* resolve response data, get http status code */ + resp_status = webclient_handle_response(session); + LOGD( "post handle response(%d).", resp_status); + + return resp_status; +} + +#define RT_WAITING_FOREVER -1 +#define RT_TICK_PER_SECOND 100 +rt_uint32_t rt_tick_from_millisecond(rt_int32_t ms) +{ + rt_uint32_t tick; + + if (ms < 0) { + tick = (rt_uint32_t)RT_WAITING_FOREVER; + } else { + tick = RT_TICK_PER_SECOND * (ms / 1000); + tick += (RT_TICK_PER_SECOND * (ms % 1000) + 999) / 1000; + } + + /* return the calculated tick */ + return tick; +} + +/** + * set receive and send data timeout. + * + * @param session http session + * @param millisecond timeout millisecond + * + * @return 0: set timeout success + */ +int webclient_set_timeout(struct webclient_session *session, int millisecond) +{ + struct timeval timeout; + int second = rt_tick_from_millisecond(millisecond) / 1000; + + RT_ASSERT(session); + + timeout.tv_sec = second; + timeout.tv_usec = 0; + + /* set recv timeout option */ + setsockopt(session->socket, SOL_SOCKET, SO_RCVTIMEO, (void *)&timeout, sizeof(timeout)); + setsockopt(session->socket, SOL_SOCKET, SO_SNDTIMEO, (void *)&timeout, sizeof(timeout)); + + return 0; +} + +static int webclient_next_chunk(struct webclient_session *session) +{ + char line[64]; + int length; + + RT_ASSERT(session); + + rt_memset(line, 0x00, sizeof(line)); + length = webclient_read_line(session, line, sizeof(line)); + if (length > 0) { + if (rt_strcmp(line, "\r") == 0) { + length = webclient_read_line(session, line, sizeof(line)); + if (length <= 0) { + close(session->socket); + session->socket = -1; + return length; + } + } + } else { + close(session->socket); + session->socket = -1; + + return length; + } + + session->chunk_sz = strtol(line, RT_NULL, 16); + session->chunk_offset = 0; + + if (session->chunk_sz == 0) { + /* end of chunks */ + close(session->socket); + session->socket = -1; + session->chunk_sz = -1; + } + + return session->chunk_sz; +} + +/** + * read data from http server. + * + * @param session http session + * @param buffer read buffer + * @param length the maximum of read buffer size + * + * @return <0: read data error + * =0: http server disconnect + * >0: successfully read data length + */ +int webclient_read(struct webclient_session *session, void *buffer, size_t length) +{ + int bytes_read = 0; + int total_read = 0; + int left; + + RT_ASSERT(session); + + /* get next chunk size is zero, client is already closed, return zero */ + LOGD( "chunk_sz: %d", session->chunk_sz); + if (session->chunk_sz < 0) { + return 0; + } + + if (session->socket < 0) { + return -WEBCLIENT_DISCONNECT; + } + + if (length == 0) { + return 0; + } + + /* which is transfered as chunk mode */ + if (session->chunk_sz) { + if ((int)length > (session->chunk_sz - session->chunk_offset)) { + length = session->chunk_sz - session->chunk_offset; + } + + bytes_read = webclient_recv(session, buffer, length, 0); + LOGV( "recv bytes: %d", bytes_read); + if (bytes_read <= 0) { + if (errno == EWOULDBLOCK || errno == EAGAIN) { + /* recv timeout */ + return -WEBCLIENT_TIMEOUT; + } else { + close(session->socket); + session->socket = -1; + return 0; + } + } + + session->chunk_offset += bytes_read; + if (session->chunk_offset >= session->chunk_sz) { + webclient_next_chunk(session); + } + + return bytes_read; + } + + if (session->content_length > 0) { + if (length > session->content_remainder) { + length = session->content_remainder; + } + + if (length == 0) { + return 0; + } + } + + /* + * Read until: there is an error, we've read "size" bytes or the remote + * side has closed the connection. + */ + left = length; + do { + bytes_read = webclient_recv(session, (void *)((char *)buffer + total_read), left, 0); + LOGV( "recv bytes: %d", bytes_read); + + if (session->is_tls) { + /* finish reading */ + total_read = bytes_read; + break; + } + + /* read until error happen */ + if (bytes_read <= 0) { + if (total_read > 0) { + break; + } + + /* can't read data from socket */ + if (errno == EWOULDBLOCK || errno == EAGAIN) { + LOGW( "receive data timeout."); + return -WEBCLIENT_TIMEOUT; + } else { + return 0; + } + } + + left -= bytes_read; + total_read += bytes_read; + } while (left); + + if (session->content_length > 0) { + session->content_remainder -= total_read; + } + + return total_read; +} + +/** + * write data to http server. + * + * @param session http session + * @param buffer write buffer + * @param length write buffer size + * + * @return <0: write data error + * =0: http server disconnect + * >0: successfully write data length + */ +int webclient_write(struct webclient_session *session, const void *buffer, size_t length) +{ + int bytes_write = 0; + int total_write = 0; + int left = length; + + RT_ASSERT(session); + + if (session->socket < 0) { + return -WEBCLIENT_DISCONNECT; + } + + if (length == 0) { + return 0; + } + + /* send all of data on the buffer. */ + do { + bytes_write = webclient_send(session, (void *)((char *)buffer + total_write), left, 0); + if (bytes_write <= 0) { +#if defined(CONFIG_WEBCLIENT_HTTPS_SUPPORTED) + if (session->is_tls && (bytes_write == MBEDTLS_ERR_SSL_WANT_READ || bytes_write == MBEDTLS_ERR_SSL_WANT_WRITE)) { + continue; + } +#endif + if (errno == EWOULDBLOCK || errno == EAGAIN) { + /* send timeout */ + if (total_write) { + return total_write; + } + continue; + /* TODO: whether return the TIMEOUT + * return -WEBCLIENT_TIMEOUT; */ + } else { + close(session->socket); + session->socket = -1; + + if (total_write == 0) { + return -WEBCLIENT_DISCONNECT; + } + break; + } + } + + left -= bytes_write; + total_write += bytes_write; + } while (left); + + return total_write; +} + +/* close session socket, free host and request url */ +static int webclient_clean(struct webclient_session *session) +{ +#ifdef CONFIG_WEBCLIENT_HTTPS_SUPPORTED + if (session->tls_session) { + mbedtls_client_close(session->tls_session); + } else { + if (session->socket >= 0) { + close(session->socket); + session->socket = -1; + } + } +#else + if (session->socket >= 0) { + close(session->socket); + session->socket = -1; + } +#endif + + HAL_SAFE_FREE(session->host); + HAL_SAFE_FREE(session->req_url); + + session->content_length = -1; + + return 0; +} + +/** + * close a webclient client session. + * + * @param session http client session + * + * @return 0: close success + */ +int webclient_close(struct webclient_session *session) +{ + RT_ASSERT(session); + + webclient_clean(session); + + if (session->header) { + HAL_SAFE_FREE(session->header->buffer); + } + + HAL_SAFE_FREE(session->header); + +#ifdef CONFIG_WEBCLIENT_HTTPS_SUPPORTED + HAL_SAFE_FREE(session->cert_buf); +#endif + HAL_SAFE_FREE(session); + + return 0; +} + +int webclient_response(struct webclient_session *session, void **response, size_t *resp_len) +{ + unsigned char *buf_ptr; + unsigned char *response_buf = 0; + int length, total_read = 0; + + RT_ASSERT(session); + RT_ASSERT(response); + + /* initialize response */ + *response = RT_NULL; + + /* not content length field kind */ + if (session->content_length < 0) { + size_t result_sz; + + total_read = 0; + while (1) { + unsigned char *new_resp = RT_NULL; + + result_sz = total_read + WEBCLIENT_RESPONSE_BUFSZ; + new_resp = volc_osal_realloc(response_buf, result_sz + 1); + if (new_resp == RT_NULL) { + LOGE( "no memory for realloc new response buffer!"); + break; + } + + response_buf = new_resp; + buf_ptr = (unsigned char *)response_buf + total_read; + + /* read result */ + length = webclient_read(session, buf_ptr, result_sz - total_read); + if (length <= 0) + break; + + total_read += length; + } + } else { + int result_sz; + + result_sz = session->content_length; + response_buf = volc_osal_calloc(1, result_sz + 1); + if (response_buf == RT_NULL) { + return -WEBCLIENT_NOMEM; + } + + buf_ptr = (unsigned char *)response_buf; + for (total_read = 0; total_read < result_sz;) { + length = webclient_read(session, buf_ptr, result_sz - total_read); + if (length <= 0) + break; + + buf_ptr += length; + total_read += length; + } + } + + if ((total_read == 0)) { + HAL_SAFE_FREE(response_buf); + } + + if (response_buf) { + *response = (void *)response_buf; + *(response_buf + total_read) = '\0'; + *resp_len = total_read; + } + + return total_read; +} + +/** + * add request(GET/POST) header data. + * + * @param request_header add request buffer address + * @param fmt fields format + * + * @return <=0: add header failed + * >0: add header data size + */ + +int webclient_request_header_add(char **request_header, const char *fmt, ...) +{ + rt_int32_t length, header_length; + char *header; + va_list args; + + RT_ASSERT(request_header); + + if (*request_header == RT_NULL) { + header = volc_osal_calloc(1, WEBCLIENT_HEADER_BUFSZ); + if (header == RT_NULL) { + LOGE( "No memory for webclient request header add."); + // return RT_NULL; + return 0; + } + *request_header = header; + } else { + header = *request_header; + } + + va_start(args, fmt); + header_length = rt_strlen(header); + length = rt_vsnprintf(header + header_length, WEBCLIENT_HEADER_BUFSZ - header_length, fmt, args); + if (length < 0) { + LOGE( "add request header data failed, return length(%d) error.", (int)length); + return -WEBCLIENT_ERROR; + } + va_end(args); + + /* check header size */ + if (rt_strlen(header) >= WEBCLIENT_HEADER_BUFSZ) { + LOGE( "not enough request header data size(%d)!", WEBCLIENT_HEADER_BUFSZ); + return -WEBCLIENT_ERROR; + } + + return length; +} + +int webclient_request(const char *URI, const char *header, const void *post_data, size_t data_len, void **response, + size_t *resp_len, const char *cert_buf, size_t cert_len) +{ + struct webclient_session *session = RT_NULL; + int rc = WEBCLIENT_OK; + int totle_length = 0; + + RT_ASSERT(URI); + + if (post_data == RT_NULL && response == RT_NULL) { + LOGE( "request get failed, get response data cannot be empty."); + return -WEBCLIENT_ERROR; + } + + if ((post_data != RT_NULL) && (data_len == 0)) { + LOGE( "input post data length failed"); + return -WEBCLIENT_ERROR; + } + + if ((response != RT_NULL && resp_len == RT_NULL) || (response == RT_NULL && resp_len != RT_NULL)) { + LOGE( "input response data or length failed"); + return -WEBCLIENT_ERROR; + } + + if (post_data == RT_NULL) { + /* send get request */ + session = webclient_session_create(WEBCLIENT_HEADER_BUFSZ, cert_buf, cert_len); + if (session == RT_NULL) { + rc = -WEBCLIENT_NOMEM; + goto __exit; + } + + if (header != RT_NULL) { + char *header_str, *header_ptr; + int header_line_length; + + for (header_str = (char *)header; (header_ptr = rt_strstr(header_str, "\r\n")) != RT_NULL;) { + header_line_length = header_ptr + rt_strlen("\r\n") - header_str; + webclient_header_fields_add(session, "%.*s", header_line_length, header_str); + header_str += header_line_length; + } + } + + if (webclient_get(session, URI) != 200) { + rc = -WEBCLIENT_ERROR; + goto __exit; + } + + totle_length = webclient_response(session, response, resp_len); + if (totle_length <= 0) { + rc = -WEBCLIENT_ERROR; + goto __exit; + } + } else { + /* send post request */ + session = webclient_session_create(WEBCLIENT_HEADER_BUFSZ, cert_buf, cert_len); + if (session == RT_NULL) { + rc = -WEBCLIENT_NOMEM; + goto __exit; + } + + if (header != RT_NULL) { + char *header_str, *header_ptr; + int header_line_length; + + for (header_str = (char *)header; (header_ptr = rt_strstr(header_str, "\r\n")) != RT_NULL;) { + header_line_length = header_ptr + rt_strlen("\r\n") - header_str; + webclient_header_fields_add(session, "%.*s", header_line_length, header_str); + header_str += header_line_length; + } + } + + if (rt_strstr(session->header->buffer, "Content-Length") == RT_NULL) { + webclient_header_fields_add(session, "Content-Length: %d\r\n", rt_strlen(post_data)); + } + + if (rt_strstr(session->header->buffer, "Content-Type") == RT_NULL) { + webclient_header_fields_add(session, "Content-Type: application/octet-stream\r\n"); + } + + if (webclient_post(session, URI, post_data, data_len) != 200) { + rc = -WEBCLIENT_ERROR; + goto __exit; + } + + totle_length = webclient_response(session, response, resp_len); + if (totle_length <= 0) { + rc = -WEBCLIENT_ERROR; + goto __exit; + } + } + +__exit: + if (session) { + webclient_close(session); + session = RT_NULL; + } + + if (rc < 0) { + return rc; + } + + return totle_length; +} diff --git a/firmware/echoear-platform-agent/components/volc_conv_ai/volc_conv_ai_AI_Generated_Code_Reading_Guide.md b/firmware/echoear-platform-agent/components/volc_conv_ai/volc_conv_ai_AI_Generated_Code_Reading_Guide.md new file mode 100644 index 0000000..7a90a12 --- /dev/null +++ b/firmware/echoear-platform-agent/components/volc_conv_ai/volc_conv_ai_AI_Generated_Code_Reading_Guide.md @@ -0,0 +1,486 @@ +# `volc_conv_ai` 代码梳理 + +> 说明:本文档由 AI 在阅读当前目录代码后生成,目标是帮助读者快速理解代码结构、模块职责与主要运行流程,作为代码导读使用。 + +## 1. 项目定位 + +这是一个面向嵌入式/端侧场景的会话式 AI SDK,目标是帮助设备快速与火山引擎云端智能体建立连接,并完成音频、视频、文本消息的双向交互。 + +从实现上看,它不是一个“纯算法库”,而是一个“连接层 + 协议封装层 + 平台适配层”的组合,核心职责包括: + +- 解析用户传入的 JSON 配置。 +- 使用 IoT 动态注册接口换取 `device_secret` 和 `RTCAppID`。 +- 根据模式选择 RTC 或 WebSocket 传输通道。 +- 向上层暴露统一的发送/控制 API。 +- 将底层事件、会话状态、媒体数据统一回调给用户。 + +## 2. 对外 API 边界 + +需要特别注意:**对外暴露的 API 只有 `inc/volc_conv_ai.h` 中定义的内容**。 + +目录中的其他头文件,例如: + +- `src/base/*.h` +- `src/transports/**/*.h` +- `src/util/*.h` +- `osal/inc/volc_osal.h` + +都属于内部实现、传输层封装、工具函数或平台移植接口,不应被业务侧当作稳定公共接口直接依赖。 + +`inc/volc_conv_ai.h` 对外主要提供了以下能力: + +- 基础错误码与版本号查询: + - `volc_get_version()` + - `volc_err_2_str()` +- 生命周期管理: + - `volc_create()` + - `volc_destroy()` + - `volc_start()` + - `volc_stop()` +- 数据发送: + - `volc_send_audio_data()` + - `volc_send_video_data()` + - `volc_send_message()` + - `volc_update()`,本质上是以二进制消息方式发送更新数据 +- 控制能力: + - `volc_send_text_to_agent()` + - `volc_interrupt()` +- 回调模型: + - `on_volc_event` + - `on_volc_conversation_status` + - `on_volc_audio_data` + - `on_volc_video_data` + - `on_volc_message_data` + +也就是说,业务方应该只依赖一个抽象句柄 `volc_engine_t`,其余传输方式、鉴权、设备注册、消息魔术字等细节都被隐藏在内部实现中。 + +## 3. 代码总体结构 + +### 3.1 顶层目录 + +- `inc/` + - 唯一公共头文件目录,定义 SDK 对外接口。 +- `src/` + - 主体实现目录。 +- `src/base/` + - 基础消息类型、设备注册与 RTC 建房配置逻辑。 +- `src/transports/high_quality/` + - 高质量链路,基于 RTC。 +- `src/transports/low_load/` + - 低负载链路,基于 WebSocket。 +- `src/util/` + - 鉴权、HTTP、Base64、JSON 读取、日志、链表等通用工具。 +- `osal/` + - 平台抽象层,屏蔽内存、线程、时间、UUID 等系统差异。 +- `third_party/` + - 证书、TLS、HTTP 客户端、zlib 等第三方依赖。 +- `sample/` + - 最小使用示例。 +- `cmake/` + - 各平台构建脚本及源码归类。 + +### 3.2 主入口文件 + +`src/volc_conv_ai.c` 是整个 SDK 的统一入口和调度中心,负责: + +- 维护 `volc_engine_impl_t` 运行时对象。 +- 解析配置并执行设备注册。 +- 创建 RTC/WS 传输对象。 +- 在 `start/stop/send/interrupt` 时按当前模式分发到底层传输实现。 +- 将底层内部消息 `volc_msg_t` 转换成对外事件/会话状态回调。 +- 将底层数据 `volc_data_info_t` 路由到对应用户回调。 + +## 4. 核心数据模型 + +### 4.1 对外数据类型 + +`inc/volc_conv_ai.h` 中定义了 SDK 面向业务的统一数据模型: + +- 音频: + - `volc_audio_data_type_e` + - `volc_audio_codec_type_e` + - `volc_audio_frame_info_t` +- 视频: + - `volc_video_data_type_e` + - `volc_video_codec_type_e` + - `volc_video_frame_info_t` +- 文本/控制消息: + - `volc_message_info_t` +- 通用数据路由包装: + - `volc_data_info_t` +- 会话状态: + - `VOLC_CONV_STATUS_LISTENING` + - `VOLC_CONV_STATUS_THINKING` + - `VOLC_CONV_STATUS_ANSWERING` + - `VOLC_CONV_STATUS_INTERRUPTED` + - `VOLC_CONV_STATUS_ANSWER_FINISH` +- 工作模式: + - `VOLC_MODE_RTC` + - `VOLC_MODE_WS` + +### 4.2 内部消息模型 + +`src/base/volc_base.h` 定义了内部传输层与主入口之间的统一消息协议: + +- `volc_msg_t` + - 表示连接成功、断开、远端用户加入、token 过期、关键帧请求、码率变化、会话状态变化等内部事件。 +- `volc_msg_cb` + - 传输层向主入口上报事件。 +- `volc_data_cb` + - 传输层向主入口上报音视频/消息数据。 + +这层抽象的意义是:RTC 和 WS 两条链路虽然底层协议差异很大,但都能通过相同的内部回调接口接入 `src/volc_conv_ai.c`。 + +## 5. 生命周期主流程 + +### 5.1 `volc_create()` + +`volc_create()` 是 SDK 初始化入口,主要步骤如下: + +1. 为 `volc_engine_impl_t` 分配内存并保存用户回调。 +2. 解析用户传入的 `config_json`。 +3. 读取 `iot` 配置: + - `instance_id` + - `product_key` + - `product_secret` + - `device_name` +4. 调用 `volc_device_register()` 完成设备动态注册。 +5. 从注册结果中得到: + - `device_secret` + - `rtc_app_id` +6. 如果配置中带有 `rtc` 节点,则创建 RTC 传输对象。 +7. 如果编译启用了 WS,则创建 WS 传输对象。 +8. 将引擎状态置为 `CREATED`。 + +这里有一个关键设计点:**设备动态注册在 `create` 阶段完成,而不是在 `start` 阶段完成**。因此 `create()` 成功后,引擎已经持有后续建链所需的基础鉴权信息。 + +### 5.2 `volc_start()` + +`volc_start()` 根据 `volc_opt_t.mode` 选择具体传输通道: + +- `VOLC_MODE_RTC`: + - 调用 `volc_rtc_start()` +- `VOLC_MODE_WS`: + - 调用 `volc_ws_start()` + +同时要求: + +- `handle` 非空 +- `opt` 非空 +- `bot_id` 有效 +- 当前状态必须是 `CREATED` 或 `STOPPED` + +### 5.3 `volc_stop()` / `volc_destroy()` + +- `volc_stop()` 仅停止当前会话链路,保留引擎对象。 +- `volc_destroy()` 释放传输层对象、IoT 信息和引擎对象。 + +`destroy()` 会根据当前 `mode` 分别销毁 RTC 或 WS 实例,因此 SDK 的运行时是“单引擎单活跃模式”模型,而不是双通道同时工作模型。 + +## 6. 设备注册与建链前鉴权 + +相关代码位于 `src/base/volc_device_manager.c`。 + +### 6.1 动态注册 + +`volc_device_register()` 的职责: + +- 基于 `product_secret`、`product_key`、`device_name`、时间戳、随机数生成签名。 +- 向 IoT 服务 `DynamicRegister` 接口发起 HTTP POST。 +- 解析响应中的: + - `Result.payload` + - `Result.RTCAppID` +- 用 `volc_aes_decode()` 解密 `payload`,得到 `device_secret`。 + +这一步是整个 SDK 的前置条件。没有 `device_secret`,后续 RTC 拉取房间配置或 WS 握手都无法继续。 + +### 6.2 拉取 RTC 建房参数 + +`volc_get_rtc_config()` 会在 RTC 模式的 `start()` 阶段调用,向服务端请求: + +- `RoomID` +- `UserID` +- `Token` +- `TaskID` + +这些字段最终用于 RTC 进房。 + +### 6.3 鉴权与解密工具 + +`src/util/volc_auth.c` 提供两个核心能力: + +- `volc_sha256_hmac()` + - 用于请求签名。 +- `volc_aes_decode()` + - 用于解密动态注册返回的加密载荷。 + +配合 `src/util/volc_base64.c`,构成完整的签名与解密工具链。 + +## 7. RTC 模式实现 + +相关代码位于: + +- `src/transports/high_quality/inc/volc_rtc.h` +- `src/transports/high_quality/src/volc_rtc.c` + +RTC 模式是“高质量方案”,依赖火山引擎 RTC Lite SDK。 + +### 7.1 RTC 对象职责 + +`rtc_impl_t` 维护了 RTC 运行所需的关键状态: + +- 是否已启动 pipeline +- 是否已进房 +- 是否有远端用户加入 +- 是否收到了第一帧关键帧 +- 是否启用了音视频发布/订阅 +- appid / channel / uid / token / remote uid +- 回调上下文 +- RTC 引擎实例 + +### 7.2 RTC 初始化 + +`volc_rtc_create()` 会读取 `rtc` 配置中的: + +- `audio.codec` +- `video.codec` +- `audio.publish` +- `audio.subscribe` +- `video.publish` +- `video.subscribe` +- `log_level` +- `params` + +然后完成: + +- 创建 `byte_rtc_engine_t` +- 注册底层事件回调 +- 设置日志级别 +- 设置扩展参数 +- 设置音频/视频编解码器 +- 调用 `byte_rtc_init()` + +### 7.3 RTC 启动流程 + +`volc_rtc_start()` 的链路为: + +1. 调用 `volc_get_rtc_config()` 获取房间配置。 +2. 用返回的 `RoomID/UserID/Token` 调用 `byte_rtc_join_room()`。 +3. 房间加入成功后,通过 `_on_join_channel_success()` 上报 `VOLC_MSG_CONNECTED`。 + +### 7.4 RTC 收发逻辑 + +- 接收音频: + - `_on_audio_data()` 将 RTC 音频帧包装为 `VOLC_DATA_TYPE_AUDIO` 并回调给主入口。 +- 接收视频: + - `_on_video_data()` 会先处理首关键帧逻辑。 + - 若首帧不是关键帧,会主动请求关键帧。 +- 接收消息: + - `_on_message_received()` 区分控制/会话状态消息和普通消息。 + +### 7.5 RTC 控制消息 + +RTC 内部使用了带“魔术字 + 长度 + 内容”的二进制消息封装: + +- `ctrl` + - 控制命令通道。 +- `conv` + - 会话状态通道。 + +对应辅助函数是 `_build_binary_message()`。 + +`volc_interrupt()` 在 RTC 模式下最终会发送: + +```json +{"Command":"interrupt"} +``` + +`volc_send_text_to_agent()` 在 RTC 模式下会发送控制消息: + +- `ExternalTextToSpeech` +- `ExternalTextToLLM` + +所以 RTC 模式不仅能发媒体数据,也支持通过 RTS 消息向云端智能体下发控制/文本命令。 + +## 8. WebSocket 模式实现 + +相关代码位于: + +- `src/transports/low_load/inc/volc_ws.h` +- `src/transports/low_load/src/volc_ws.c` + +WS 模式是“低负载方案”,整体更接近 Realtime API 风格。 + +### 8.1 WS 启动流程 + +`volc_ws_start()` 主要做了这些事: + +1. 保存 `bot_id`。 +2. 解析运行参数里的音频编码配置。 +3. 生成带鉴权头的 WebSocket URL 和 Header。 +4. 建立到 `wss://ai-gateway.vei.volces.com/v1/realtime` 的连接。 +5. 某些音频格式下会等待连接建立后发送 `session.update`。 + +WS 握手使用的鉴权信息来自 `device_secret`,其签名生成逻辑与 RTC 前置注册链路一致。 + +### 8.2 WS 音频发送 + +`volc_send_audio_data()` 在 WS 模式下最终会进入 `__ws_send_audio()`,流程为: + +1. 将原始音频做 Base64 编码。 +2. 发送 `input_audio_buffer.append`。 +3. 如果 `commit=true`,继续发送: + - `input_audio_buffer.commit` + - `response.create` + +因此 WS 模式下,`commit` 的语义很重要,它不仅表示音频片段结束,还会触发一次云端响应生成。 + +### 8.3 WS 消息接收 + +`__ws_recv_data()` 会解析服务端返回的 JSON 事件,重点包括: + +- `response.audio.delta` + - Base64 解码后作为 PCM 音频回调给上层。 +- `input_audio_buffer.speech_started` + - 映射为 `LISTENING` +- `input_audio_buffer.speech_stopped` + - 映射为 `THINKING` +- `response.done` + - 根据状态映射为 `ANSWER_FINISH` 或 `INTERRUPTED` +- 其他事件 + - 作为普通消息数据透传给用户 + +### 8.4 WS 中断机制 + +WS 模式下中断命令发送的是: + +```json +{"type": "response.cancel"} +``` + +如果中断发生在正在回答阶段,还会记录 `response_id`,把被取消响应后续迟到的音频/文本增量丢弃,避免业务层收到过期数据。 + +## 9. 主入口中的统一路由机制 + +`src/volc_conv_ai.c` 做了两层关键统一: + +### 9.1 事件统一 + +内部消息 `volc_msg_t` 会被映射成对外的 `volc_event_t` 或会话状态回调: + +- `VOLC_MSG_CONNECTED` -> `VOLC_EV_CONNECTED` +- `VOLC_MSG_DISCONNECTED` -> `VOLC_EV_DISCONNECTED` +- `VOLC_MSG_QUOTA_EXCEEDED` -> `VOLC_EV_QUOTA_EXCEEDED` +- `VOLC_MSG_CONV_STATUS` -> `on_volc_conversation_status` + +### 9.2 数据统一 + +无论底层来自 RTC 还是 WS,最终都会通过 `volc_data_info_t` 被分发到: + +- `on_volc_audio_data` +- `on_volc_video_data` +- `on_volc_message_data` + +这保证了业务层只需要按“统一数据类型 + 回调”的方式处理,不需要关心底层通道差异。 + +## 10. 工具与基础支撑 + +### 10.1 JSON 工具 + +`src/util/volc_json.c` 提供了点路径读取能力,例如: + +- `ResponseMetadata.Error.CodeN` +- `Result.payload` +- `audio.codec` + +这使得配置解析和响应解析代码保持相对简洁。 + +### 10.2 HTTP 能力 + +`src/util/volc_http.c` 通过第三方 `webclient` 封装了一个最简单的 `volc_http_post()`,用于访问 IoT 接口。 + +### 10.3 OSAL + +`osal/inc/volc_osal.h` 定义了统一的平台适配接口: + +- 内存分配 +- 互斥锁/信号量 +- 时间获取 +- UUID 获取 +- 线程创建与休眠 +- 平台信息获取 +- 随机数填充 + +目前仓库中已经给出: + +- `osal/src/linux/volc_osal.c` +- `osal/src/macos/volc_osal.c` +- `osal/src/espressif/volc_osal.c` + +这说明 SDK 的移植思路是:**业务逻辑层不感知平台差异,平台差异统一下沉到 OSAL。** + +## 11. 构建与编译组织 + +构建脚本的核心信息在 `cmake/common.cmake`、`cmake/linux.cmake`、`cmake/macos.cmake`。 + +### 11.1 公共源码集合 + +`cmake/common.cmake` 将源码分为几组: + +- 公共源码: + - `src/volc_conv_ai.c` + - `src/base/volc_device_manager.c` + - `src/util/*.c` + - TLS / webclient +- 低负载源码: + - `src/transports/low_load/src/volc_ws.c` +- 高质量源码: + - `src/transports/high_quality/src/volc_rtc.c` +- 平台源码: + - `osal/src//volc_osal.c` + +### 11.2 模式选择 + +通过编译选项控制启用的传输模式: + +- `ENABLE_RTC_MODE` +- `ENABLE_WS_MODE` + +Linux 配置中默认更偏向 RTC,macOS 脚本里两个模式默认都关闭,需要显式开启。 + +## 12. sample 的意义 + +`sample/sample.c` 展示了最小调用方式: + +1. 拼接 `config_json` +2. 填写回调函数 +3. `volc_create()` +4. `volc_start()` +5. 循环发送文本给智能体 +6. `volc_stop()` +7. `volc_destroy()` + +这个 sample 也说明了业务接入 SDK 的典型顺序就是: + +`create -> start -> send/recv -> stop -> destroy` + +## 13. 一句话总结 + +这个仓库的本质是一个“统一外部 API + 双传输通道实现 + 设备注册鉴权 + 平台抽象层”的端侧会话 AI SDK: + +- 对外只暴露 `inc/volc_conv_ai.h` +- 对内由 `src/volc_conv_ai.c` 做统一调度 +- RTC 模式负责高质量实时音视频/控制链路 +- WS 模式负责低负载实时语音交互链路 +- `src/base` 负责设备注册与 RTC 建房参数获取 +- `src/util` 提供鉴权、HTTP、JSON、Base64 等基础工具 +- `osal` 负责平台移植 + +如果后续继续阅读源码,建议优先按这条链路理解: + +1. `inc/volc_conv_ai.h` +2. `src/volc_conv_ai.c` +3. `src/base/volc_device_manager.c` +4. `src/transports/high_quality/src/volc_rtc.c` +5. `src/transports/low_load/src/volc_ws.c` +6. `osal/inc/volc_osal.h` diff --git a/firmware/echoear-platform-agent/dependencies.lock b/firmware/echoear-platform-agent/dependencies.lock new file mode 100644 index 0000000..caa676a --- /dev/null +++ b/firmware/echoear-platform-agent/dependencies.lock @@ -0,0 +1,858 @@ +dependencies: + application: + dependencies: [] + source: + path: E:\Codex\story\platform\firmware\echoear-platform-agent\components\application + type: local + version: '*' + espressif/button: + component_hash: a1f34e8ed2e61ffef1a230fd9477005c2db335c7e1f6a239f43e5f119dcbe1b0 + dependencies: + - name: espressif/cmake_utilities + registry_url: https://components.espressif.com + require: private + version: '*' + - name: idf + require: private + version: '>=4.0' + source: + registry_url: https://components.espressif.com/ + type: service + version: 4.2.1 + espressif/cmake_utilities: + component_hash: 351350613ceafba240b761b4ea991e0f231ac7a9f59a9ee901f751bddc0bb18f + dependencies: + - name: idf + require: private + version: '>=4.1' + source: + registry_url: https://components.espressif.com + type: service + version: 0.5.3 + espressif/dl_fft: + component_hash: a955ca29581de2177ddf25759273bd83f082b3c805e735fe6ee105858643989b + dependencies: + - name: idf + require: private + version: '>=5.0' + source: + registry_url: https://components.espressif.com + type: service + version: 0.6.0 + espressif/esp-dsp: + component_hash: 619639efc18cfa361a9e423739b9b0ffc14991effc6c027f955c2f2c3bf1754b + dependencies: + - name: idf + require: private + version: '>=4.2' + source: + registry_url: https://components.espressif.com + type: service + version: 1.6.0 + espressif/esp-sr: + component_hash: 12733d9b4aef5d5e295f35c4671835d605992d00583fcd2f8d21166f62c6b071 + dependencies: + - name: espressif/dl_fft + registry_url: https://components.espressif.com + require: private + version: '>=0.2.0' + - name: espressif/esp-dsp + registry_url: https://components.espressif.com + require: private + version: 1.6.0 + - name: idf + require: private + version: '>=5.0' + source: + registry_url: https://components.espressif.com + type: service + version: 2.1.5 + espressif/esp32-camera: + component_hash: bc9c8a6b51df777a014fa295825b3de5069bc0300c317acff20c97cf4a10ac7d + dependencies: + - name: espressif/esp_jpeg + registry_url: https://components.espressif.com + require: public + version: ^1.3.1 + - name: idf + require: private + version: '>=5.1' + source: + registry_url: https://components.espressif.com + type: service + version: 2.1.7 + espressif/esp_audio_codec: + component_hash: 04c2f0d5eca207cdfabd3aafc50ef4bca53532115ddf7689e5cfabd00eb88b02 + dependencies: + - name: idf + require: private + version: '>=4.4' + source: + registry_url: https://components.espressif.com + type: service + version: 2.3.0 + espressif/esp_audio_effects: + component_hash: 54af7c9a4f40703325482660ffb3a8b5f66fa56cb92f6db220f8b11ec2b6a32b + dependencies: [] + source: + registry_url: https://components.espressif.com/ + type: service + version: 1.1.0 + espressif/esp_audio_render: + component_hash: 6de7ecc0f318779c7dc1e8f2894012ed4b38aac5a724a95bf1e7e43538b44ec9 + dependencies: + - name: espressif/gmf_audio + registry_url: https://components.espressif.com + require: private + version: ^0.7 + - name: idf + require: private + version: '>=4.4' + source: + registry_url: https://components.espressif.com/ + type: service + version: 0.7.2~1 + espressif/esp_audio_simple_player: + component_hash: 44a47b17b4e1a3846d863e759e52f7945daef5e0411586069105ed01bcefb29d + dependencies: + - name: espressif/gmf_audio + registry_url: https://components.espressif.com + require: private + version: ^0.7 + - name: espressif/gmf_io + registry_url: https://components.espressif.com + require: private + version: ^0.7 + source: + registry_url: https://components.espressif.com/ + type: service + version: 0.9.5~1 + espressif/esp_board_manager: + component_hash: 876c53826fd3170fc9b212824bd493fd8d22e0a67438484eff00baf25ca57ff8 + dependencies: + - matches: + - if: $CONFIG{ESP_BOARD_DEV_AUDIO_CODEC_SUPPORT} == True + name: espressif/esp_codec_dev + registry_url: https://components.espressif.com + require: private + version: ~1.5 + - matches: + - if: $CONFIG{ESP_BOARD_DEV_GPIO_EXPANDER_SUPPORT} == True + name: espressif/esp_io_expander + registry_url: https://components.espressif.com + require: private + version: ^1.0.1 + - matches: + - if: $CONFIG{ESP_BOARD_DEV_LCD_TOUCH_I2C_SUPPORT} == True + name: espressif/esp_lcd_touch + registry_url: https://components.espressif.com + require: private + version: '*' + - name: espressif/esp_video + registry_url: https://components.espressif.com + require: private + rules: + - if: target in [esp32p4, esp32s3] + - if: $CONFIG{ESP_BOARD_DEV_CAMERA_SUPPORT} == True + version: ^1.2.0 + source: + registry_url: https://components.espressif.com/ + type: service + version: 0.4.0 + espressif/esp_cam_sensor: + component_hash: db5b1c725799dced61e7b100dc027e1e59558c9973d7e1ed8ce42a5ec89da31c + dependencies: + - name: espressif/cmake_utilities + registry_url: https://components.espressif.com + require: private + version: 0.* + - name: espressif/esp_sccb_intf + registry_url: https://components.espressif.com + require: private + version: '>=0.0.5' + - name: idf + require: private + version: '>=5.3' + source: + registry_url: https://components.espressif.com + type: service + targets: + - esp32p4 + - esp32s3 + - esp32c3 + - esp32c5 + - esp32c6 + - esp32c61 + version: 1.7.0 + espressif/esp_capture: + component_hash: 07a9a270cae9ffee5cc76635b8434f869d475cee3134a23216c2a7438aa4fb3f + dependencies: + - name: espressif/cmake_utilities + registry_url: https://components.espressif.com + require: private + version: 0.* + - name: espressif/esp-sr + registry_url: https://components.espressif.com + require: private + version: 2.1.5 + - name: espressif/esp32-camera + registry_url: https://components.espressif.com + require: private + rules: + - if: target in [esp32s3, esp32s2] + version: ^2.0 + - name: espressif/esp_codec_dev + registry_url: https://components.espressif.com + require: private + version: ^1.3 + - name: espressif/esp_muxer + registry_url: https://components.espressif.com + require: private + version: ^1.2 + - name: espressif/esp_video + registry_url: https://components.espressif.com + require: private + rules: + - if: target in [esp32p4, esp32s3, esp32c3, esp32c5, esp32c6, esp32c61] + version: ^1.0 + - name: espressif/gmf_audio + registry_url: https://components.espressif.com + require: private + version: ^0.7 + - name: espressif/gmf_video + registry_url: https://components.espressif.com + require: private + version: ^0.7 + - name: idf + require: private + version: '>=4.4' + source: + registry_url: https://components.espressif.com/ + type: service + version: 0.7.11 + espressif/esp_codec_dev: + component_hash: df70f10af8d7b922add7b9d07372c9c97ab356e58d72b7a892050227c2d44348 + dependencies: + - name: idf + require: private + version: '>=4.0' + source: + registry_url: https://components.espressif.com + type: service + version: 1.5.11 + espressif/esp_h264: + component_hash: 054a781d7280b94f3d5c29d562ed1582e4c51e7f3ff5566d5d57058e80817805 + dependencies: + - name: idf + require: private + version: '>=4.4' + source: + registry_url: https://components.espressif.com + type: service + targets: + - esp32s3 + - esp32s31 + - esp32p4 + version: 1.4.0 + espressif/esp_image_effects: + component_hash: a35037db02862d3cef6f72c25f422ae62da6e6c88219ce03c9ea24e0315ceaaa + dependencies: + - name: idf + require: private + version: '>=5.0' + source: + registry_url: https://components.espressif.com + type: service + version: 1.0.1 + espressif/esp_jpeg: + component_hash: defb83669293cbf86d0fa86b475ba5517aceed04ed70db435388c151ab37b5d7 + dependencies: + - name: idf + require: private + version: '>=5.0' + source: + registry_url: https://components.espressif.com + type: service + version: 1.3.1 + espressif/esp_lcd_gc9a01: + component_hash: e60bc2b5649c7679bd010797a875ae64b2a57cf9cdbd6a2bf7a0ade66913edc0 + dependencies: + - name: espressif/cmake_utilities + registry_url: https://components.espressif.com + require: private + version: 0.* + - name: idf + require: private + version: '>=4.4' + source: + registry_url: https://components.espressif.com/ + type: service + version: 2.0.4 + espressif/esp_lcd_ili9341: + component_hash: 840387ee0c90a473e43fdc75570f77c52f06423aa331d95479bde8b418c43547 + dependencies: + - name: espressif/cmake_utilities + registry_url: https://components.espressif.com + require: private + version: 0.* + - name: idf + require: private + version: '>=5.2' + source: + registry_url: https://components.espressif.com/ + type: service + version: 2.1.0 + espressif/esp_lcd_st77916: + component_hash: 5fa0f8b1274576d4484e2b8d9358e2a5d09c721511bef0dce6a55b4206b5f0e9 + dependencies: + - name: espressif/cmake_utilities + registry_url: https://components.espressif.com + require: private + version: 0.* + - name: idf + require: private + version: '>5.0.4,!=5.1.1' + source: + registry_url: https://components.espressif.com/ + type: service + version: 1.0.1 + espressif/esp_lcd_touch: + component_hash: 3f85a7d95af876f1a6ecca8eb90a81614890d0f03a038390804e5a77e2caf862 + dependencies: + - name: idf + require: private + version: '>=4.4.2' + source: + registry_url: https://components.espressif.com + type: service + version: 1.2.1 + espressif/esp_lcd_touch_cst816s: + component_hash: f017808eb754d3fe16987d413a2385f6356890f5737ee3fe36b6a548ea1d1d74 + dependencies: + - name: espressif/esp_lcd_touch + registry_url: https://components.espressif.com + require: public + version: ^1.2.0 + - name: idf + require: private + version: '>=5.2' + source: + registry_url: https://components.espressif.com/ + type: service + version: 1.1.2 + espressif/esp_lcd_touch_gt1151: + component_hash: b00201a367fd60092db414efd2f477b36cb58d3bd66cef631165d628a7fe982c + dependencies: + - name: espressif/esp_lcd_touch + registry_url: https://components.espressif.com + require: public + version: ^1.2.0 + - name: idf + require: private + version: '>=5.2' + source: + registry_url: https://components.espressif.com/ + type: service + version: 1.1.1 + espressif/esp_lcd_touch_gt911: + component_hash: 737bdf29e0823eacf041381e81db2aaf5d378f3b5417df7f415f83319e3e790b + dependencies: + - name: espressif/esp_lcd_touch + registry_url: https://components.espressif.com + require: public + version: ^1.2.0 + - name: idf + require: private + version: '>=5.2' + source: + registry_url: https://components.espressif.com/ + type: service + version: 1.2.1 + espressif/esp_lv_decoder: + component_hash: 992e26273dd680cc6b82fe031232e046d9e31a448335c950af7782152033d7fa + dependencies: + - name: espressif/esp_new_jpeg + registry_url: https://components.espressif.com + require: private + version: 0.* + - name: espressif/libpng + registry_url: https://components.espressif.com + require: private + version: 1.* + - name: idf + require: private + version: '>=5.3' + - name: lvgl/lvgl + registry_url: https://components.espressif.com + require: private + version: '>=8,<10' + source: + registry_url: https://components.espressif.com + type: service + targets: + - esp32 + - esp32s2 + - esp32s3 + - esp32p4 + - esp32c2 + - esp32c3 + - esp32c5 + - esp32c6 + version: 0.3.5 + espressif/esp_lv_fs: + component_hash: 66896007884b817df34c964f9a114fff538ee2674e99fee7159162498b93f94b + dependencies: + - name: espressif/cmake_utilities + registry_url: https://components.espressif.com + require: private + version: 0.* + - name: espressif/esp_mmap_assets + registry_url: https://components.espressif.com + require: private + version: '>=1.2' + - name: idf + require: private + version: '>=4.4' + - name: lvgl/lvgl + registry_url: https://components.espressif.com + require: private + version: '>=8,<10' + source: + registry_url: https://components.espressif.com + type: service + version: 1.0.1 + espressif/esp_lvgl_adapter: + component_hash: b14b506f52b8dee571923971db8685d85c368f3e37651892bb8994667dd5606b + dependencies: + - name: espressif/button + registry_url: https://components.espressif.com + require: public + version: 4.* + - name: espressif/esp_lcd_touch + registry_url: https://components.espressif.com + require: public + version: 1.* + - name: espressif/esp_lv_decoder + registry_url: https://components.espressif.com + require: public + version: 0.* + - name: espressif/esp_lv_fs + registry_url: https://components.espressif.com + require: public + version: 1.* + - name: espressif/freetype + registry_url: https://components.espressif.com + require: public + version: 2.* + - name: espressif/knob + registry_url: https://components.espressif.com + require: public + version: 1.* + - name: idf + require: private + version: '>=5.5' + - name: lvgl/lvgl + registry_url: https://components.espressif.com + require: private + version: '>=8,<10' + source: + registry_url: https://components.espressif.com/ + type: service + version: 0.1.0-beta.1 + espressif/esp_lvgl_port: + component_hash: e720c95cf0667554a204591bb5fade4655fb2990465557041200fa44b5bc7556 + dependencies: + - name: idf + require: private + version: '>=4.4' + - name: lvgl/lvgl + registry_url: https://components.espressif.com + require: public + version: '>=8,<10' + source: + registry_url: https://components.espressif.com/ + type: service + version: 2.6.0 + espressif/esp_mmap_assets: + component_hash: 42310c362596de2a37248c2937b1754bc29cd0356b9839e2a77cf791e6bcdde1 + dependencies: + - name: espressif/cmake_utilities + registry_url: https://components.espressif.com + require: private + version: 0.* + - name: idf + require: private + version: '>=5.0' + source: + registry_url: https://components.espressif.com/ + type: service + version: 2.0.1 + espressif/esp_muxer: + component_hash: 7de5d869121f8decaf4dd1dcaf0d7ffa9be0e9bd58be55dcf544eac4958a61ef + dependencies: [] + source: + registry_url: https://components.espressif.com + type: service + version: 1.2.3 + espressif/esp_new_jpeg: + component_hash: e6af208a875abd0ecfc0213d3751a11b504b463ebde6930f24096047925fa5c1 + dependencies: [] + source: + registry_url: https://components.espressif.com + type: service + targets: + - esp32 + - esp32s2 + - esp32s3 + - esp32p4 + - esp32c2 + - esp32c3 + - esp32c5 + - esp32c6 + version: 0.6.1 + espressif/esp_sccb_intf: + component_hash: eb6a7d5c992f09583c163b2e3f5e95a0fdc87a2d9f8e026f3e59153935553889 + dependencies: + - name: idf + require: private + version: '>=5.3' + source: + registry_url: https://components.espressif.com + type: service + version: 0.0.9 + espressif/esp_video: + component_hash: 2239aed49fa4eb1efdf3b0c2b95b05c03fc8b363c101bc2cd70f5e3bcd3c7cc4 + dependencies: + - name: espressif/cmake_utilities + registry_url: https://components.espressif.com + require: private + version: 0.* + - name: espressif/esp_cam_sensor + registry_url: https://components.espressif.com + require: private + version: 1.7.* + - name: espressif/esp_h264 + registry_url: https://components.espressif.com + require: private + rules: + - if: target in [esp32p4] + version: 1.0.4 + - name: espressif/esp_ipa + registry_url: https://components.espressif.com + require: private + rules: + - if: target in [esp32p4] + version: 1.3.* + - name: idf + require: private + version: '>=5.4' + - name: espressif/usb_host_uvc + registry_url: https://components.espressif.com + require: private + rules: + - if: target in [esp32p4, esp32s3] + version: 2.4.* + source: + registry_url: https://components.espressif.com + type: service + targets: + - esp32p4 + - esp32s3 + - esp32c3 + - esp32c5 + - esp32c6 + - esp32c61 + version: 1.4.1 + espressif/esp_video_codec: + component_hash: 4a531eadff5a79a646838cb262472c58af5231a8c161223da5bbfdf95b5b0dcc + dependencies: + - name: espressif/esp_h264 + registry_url: https://components.espressif.com + require: private + rules: + - if: target in [esp32p4, esp32s3, esp32s31] + version: ^1.4.0 + - name: espressif/esp_new_jpeg + registry_url: https://components.espressif.com + require: private + rules: + - if: target in [esp32, esp32s2, esp32s3, esp32p4, esp32c2, esp32c3, esp32c5, esp32c6, esp32c61, esp32h4, esp32s31] + version: '*' + - name: idf + require: private + version: '>=4.4' + source: + registry_url: https://components.espressif.com/ + type: service + version: 0.5.6 + espressif/freetype: + component_hash: aa9f7657a69b2b1c1f49044017fc424081d4e0fe6e97834cb43883548d44e3ca + dependencies: + - name: idf + require: private + version: '>=5.1' + source: + registry_url: https://components.espressif.com + type: service + version: 2.14.3~1 + espressif/gmf_ai_audio: + component_hash: 2356f1656d1297ba9af0d319b19858a42e8421bbcbf62719539d347d8fe51a27 + dependencies: + - name: espressif/esp-sr + registry_url: https://components.espressif.com + require: private + version: 2.1.5 + - name: espressif/gmf_core + registry_url: https://components.espressif.com + require: private + version: ^0.7 + source: + registry_url: https://components.espressif.com/ + type: service + version: 0.7.5 + espressif/gmf_io: + component_hash: 2915da1958dc8ff584bd3920a0061b13dfc64b068d3ce68a49597fe5e90bac6f + dependencies: + - name: espressif/esp_codec_dev + registry_url: https://components.espressif.com + require: private + version: ^1.4 + - name: espressif/gmf_core + registry_url: https://components.espressif.com + require: private + version: ^0.7 + source: + registry_url: https://components.espressif.com + type: service + version: 0.7.6 + espressif/gmf_loader: + component_hash: 3a45c66f0c9f5b7635b9575df9b740f25d20db1f21e76d5aadad7da2c208a6ff + dependencies: + - name: espressif/esp_audio_codec + registry_url: https://components.espressif.com + require: private + version: ^2.2 + - name: espressif/esp_codec_dev + registry_url: https://components.espressif.com + require: private + version: ^1.3.4 + - name: espressif/esp_video_codec + registry_url: https://components.espressif.com + require: private + version: ~0.5.2 + - name: espressif/gmf_ai_audio + registry_url: https://components.espressif.com + require: private + version: ^0.7 + - name: espressif/gmf_audio + registry_url: https://components.espressif.com + require: private + version: ^0.7 + - name: espressif/gmf_core + registry_url: https://components.espressif.com + require: private + version: ^0.7 + - name: espressif/gmf_io + registry_url: https://components.espressif.com + require: private + version: ^0.7 + - name: espressif/gmf_misc + registry_url: https://components.espressif.com + require: private + version: ^0.7 + - name: espressif/gmf_video + registry_url: https://components.espressif.com + require: private + version: ^0.7 + source: + registry_url: https://components.espressif.com/ + type: service + version: 0.7.3~1 + espressif/gmf_misc: + component_hash: 2e03ac6fcd5738e9f21b35f9ff7c353713a43e62bb4e77504d53fc5fb50666f3 + dependencies: + - name: espressif/gmf_core + registry_url: https://components.espressif.com + require: private + version: '>=0.7' + source: + registry_url: https://components.espressif.com + type: service + version: 0.7.0 + espressif/gmf_video: + component_hash: f846518801ae7e748ecde24e5ac4b21518ca42fffb533b9d9d67a4cd7ed2c3b8 + dependencies: + - name: espressif/esp_image_effects + registry_url: https://components.espressif.com + require: private + version: ~1.0.0 + - name: espressif/esp_video_codec + registry_url: https://components.espressif.com + require: private + version: ~0.5.2 + - name: espressif/gmf_core + registry_url: https://components.espressif.com + require: private + version: ~0.7.0 + source: + registry_url: https://components.espressif.com + type: service + version: 0.7.1 + espressif/knob: + component_hash: b56694054cd34a683ac2a6f3661963ba00b1dab741a4f3bb59ed6666cbc9f3fb + dependencies: + - name: espressif/cmake_utilities + registry_url: https://components.espressif.com + require: private + version: 0.* + - name: idf + require: private + version: '>=4.4.1' + source: + registry_url: https://components.espressif.com + type: service + version: 1.1.0 + espressif/libpng: + component_hash: b8c715f76b79a127d9b1b632e09acc23e7b29d2194af890a837adc2be5a2804b + dependencies: + - name: idf + require: private + version: '>=5.1' + - name: espressif/zlib + registry_url: https://components.espressif.com + require: private + version: ^1.2.13 + source: + registry_url: https://components.espressif.com + type: service + version: 1.6.58~1 + espressif/usb_host_uvc: + component_hash: f20c4e4dea50536e2e29daaf589b5f5984402284c155867daef27d6b64869f78 + dependencies: + - name: idf + require: private + version: '>=5.0' + - name: espressif/usb + registry_url: https://components.espressif.com + require: public + rules: + - if: idf_version >=6.0 + - if: target not in ["linux"] + version: ^1.0.0 + source: + registry_url: https://components.espressif.com + type: service + targets: + - esp32s2 + - esp32s3 + - esp32p4 + - esp32h4 + - linux + version: 2.4.2 + espressif/zlib: + component_hash: 9083cada65f1c9d298ef92266232d071c6f2f335164e2799554b3eae73aedc55 + dependencies: + - name: idf + require: private + version: '>=5.1' + source: + registry_url: https://components.espressif.com/ + type: service + version: 1.3.2~1 + espressif2022/esp_emote_gfx: + component_hash: 7478561e4e8bd0efd4307647ec7221da2519de4ff1cd0cb08dccfbd0161cadaa + dependencies: + - name: espressif/cmake_utilities + registry_url: https://components.espressif.com + require: private + version: 0.* + - name: espressif/esp_new_jpeg + registry_url: https://components.espressif.com + require: private + version: 0.6.* + - name: espressif/freetype + registry_url: https://components.espressif.com + require: private + version: 2.* + - name: idf + require: private + version: '>=5.0' + source: + registry_url: https://components.espressif.com/ + type: service + version: 1.2.0~1 + espressif2022/esp_vocat_base_control: + component_hash: 89acd3551c3fdbb710e63b15f3285a96d0c6b147b03b23e7f2e665dafc2719b7 + dependencies: [] + source: + registry_url: https://components.espressif.com/ + type: service + version: 1.0.1 + gmf_audio: + dependencies: + - name: espressif/esp_audio_codec + require: public + version: ~2.3 + - name: espressif/esp_audio_effects + require: public + version: ~1.1 + - name: espressif/gmf_core + require: public + version: ^0.7 + source: + path: E:\Codex\story\platform\firmware\echoear-platform-agent\components\esp-gmf\elements\gmf_audio + type: local + version: 0.7.2 + gmf_core: + dependencies: [] + source: + path: E:\Codex\story\platform\firmware\echoear-platform-agent\components\esp-gmf\gmf_core + type: local + version: 0.7.7 + idf: + source: + type: idf + version: 5.5.1 + lvgl/lvgl: + component_hash: 096c69af22eaf8a2b721e3913da91918c5e6bf1a762a113ec01f401aa61337a0 + dependencies: [] + source: + registry_url: https://components.espressif.com/ + type: service + version: 9.2.2 + volc_conv_ai: + dependencies: [] + source: + path: E:\Codex\story\platform\firmware\echoear-platform-agent\components\volc_conv_ai + type: local + version: '*' +direct_dependencies: +- application +- espressif/button +- espressif/esp_audio_render +- espressif/esp_audio_simple_player +- espressif/esp_board_manager +- espressif/esp_capture +- espressif/esp_lcd_gc9a01 +- espressif/esp_lcd_ili9341 +- espressif/esp_lcd_st77916 +- espressif/esp_lcd_touch_cst816s +- espressif/esp_lcd_touch_gt1151 +- espressif/esp_lcd_touch_gt911 +- espressif/esp_lvgl_adapter +- espressif/esp_lvgl_port +- espressif/esp_mmap_assets +- espressif/esp_video_codec +- espressif/gmf_ai_audio +- espressif/gmf_loader +- espressif/zlib +- espressif2022/esp_emote_gfx +- espressif2022/esp_vocat_base_control +- gmf_audio +- gmf_core +- idf +- lvgl/lvgl +- volc_conv_ai +manifest_hash: 626587b8acfa35639e4896b6912c4eeb03bdfefca3be8329fa4c79a365dc9904 +target: esp32s3 +version: 2.0.0 diff --git a/firmware/echoear-platform-agent/main/CMakeLists.txt b/firmware/echoear-platform-agent/main/CMakeLists.txt new file mode 100644 index 0000000..a5cc1ee --- /dev/null +++ b/firmware/echoear-platform-agent/main/CMakeLists.txt @@ -0,0 +1,33 @@ +# Copyright (2025) Beijing Volcano Engine Technology Ltd. +# SPDX-License-Identifier: Apache License 2.0 + +file(GLOB APP_SRC "./echoear_app/*.c") + +set(COMPONENT_SRCS "conv_ai_embedded_kit.c configuration_ap.c network.c platform_client.c" ) + +if (CONFIG_PRINT_TASK_INFO) + add_definitions(-DPRINT_TASK_INFO) +endif() + +set(COMPONENT_ADD_INCLUDEDIRS .) +set(COMPONENT_EMBED_TXTFILES assets/wifi_configuration_ap.html) + +set(COMPONENT_REQUIRES esp_http_client esp_http_server esp_wifi esp-tls mbedtls nvs_flash json espressif__gmf_ai_audio espressif__esp_board_manager espressif__esp_lcd_touch) + +register_component() + +set(assets_root "${CMAKE_CURRENT_LIST_DIR}/../assets") + +if(EXISTS "${assets_root}") + spiffs_create_partition_assets(eaf "${assets_root}" FLASH_IN_PROJECT + MMAP_FILE_SUPPORT_FORMAT ".eaf" + IMPORT_INC_PATH "${CMAKE_CURRENT_SOURCE_DIR}") + add_dependencies(${COMPONENT_LIB} assets_eaf_bin) + # The application component includes mmap_generate_eaf.h, which is emitted by + # the assets generator into this directory. Make that relationship explicit + # so a clean parallel build cannot compile the display source too early. + idf_component_get_property(application_lib application COMPONENT_LIB) + add_dependencies(${application_lib} assets_eaf_bin) +endif() + +set(COMPONENT_ADD_INCLUDEDIRS . ${FRAMEWORK_INC} ${SERVICE_INC}) diff --git a/firmware/echoear-platform-agent/main/Kconfig.projbuild b/firmware/echoear-platform-agent/main/Kconfig.projbuild new file mode 100644 index 0000000..4bf039d --- /dev/null +++ b/firmware/echoear-platform-agent/main/Kconfig.projbuild @@ -0,0 +1,31 @@ +menu "Example Configuration" + +choice AI_AGENT_MODE + prompt "Volcano RTC/WS mode" + default VOLC_WS_MODE + + config VOLC_WS_MODE + bool "enable volcano ws mode" + +endchoice + +menu "Develop Configuration" + +choice VOLC_AUDIO_CODEC + prompt "supported audio codec" + default VOLC_AUDIO_G711A + + config VOLC_AUDIO_G711A + bool "External G711A codec: 8k G711A data to SDK for upstream; 8k G711A data callback from SDK for downstream" + + config VOLC_AUDIO_PCM16 + bool "No audio codec: 16k pcm data to SDK for upstream; 16k pcm data callback from SDK for downstream" +endchoice + +config PRINT_TASK_INFO + bool "print task info" + default n + +endmenu + +endmenu diff --git a/firmware/echoear-platform-agent/main/assets/wifi_configuration_ap.html b/firmware/echoear-platform-agent/main/assets/wifi_configuration_ap.html new file mode 100644 index 0000000..aaf07c0 --- /dev/null +++ b/firmware/echoear-platform-agent/main/assets/wifi_configuration_ap.html @@ -0,0 +1,32 @@ + +EchoEar 平台激活 + +

    EchoEar 平台激活

    填写家中的2.4 GHz Wi-Fi 和平台分配给这台设备的凭据。

    +
    + + + + +

    请保持手机连接设备热点。

    +
    +
    diff --git a/firmware/echoear-platform-agent/main/common.h b/firmware/echoear-platform-agent/main/common.h new file mode 100644 index 0000000..b20b78c --- /dev/null +++ b/firmware/echoear-platform-agent/main/common.h @@ -0,0 +1,20 @@ +// Copyright (2025) Beijing Volcano Engine Technology Ltd. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef __COMMON_H__ +#define __COMMON_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + const char *key; + const char *value; +} http_req_header_t; + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/firmware/echoear-platform-agent/main/configuration_ap.c b/firmware/echoear-platform-agent/main/configuration_ap.c new file mode 100644 index 0000000..8894104 --- /dev/null +++ b/firmware/echoear-platform-agent/main/configuration_ap.c @@ -0,0 +1,381 @@ +// Copyright (2025) Beijing Volcano Engine Technology Ltd. +// SPDX-License-Identifier: Apache-2.0 + +#include "configuration_ap.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include "platform_client.h" +#include "cJSON.h" +#include "freertos/task.h" + +#define TAG "CONFIGURATION_AP" + +#define WIFI_CONNECTED_BIT BIT0 +#define WIFI_FAIL_BIT BIT1 + +#define WEB_SERVER_URL "http://192.168.4.1" + +extern const char index_html_start[] asm("_binary_wifi_configuration_ap_html_start"); +ap_t ap; +typedef enum { SETUP_IDLE, SETUP_CONNECTING, SETUP_SAVED, SETUP_WIFI_FAILED, SETUP_SAVE_FAILED } setup_state_t; +static setup_state_t setup_state; +static portMUX_TYPE setup_mux = portMUX_INITIALIZER_UNLOCKED; +static setup_state_t get_setup_state(void) { + portENTER_CRITICAL(&setup_mux); + setup_state_t state = setup_state; + portEXIT_CRITICAL(&setup_mux); + return state; +} +static void set_setup_state(setup_state_t state) { + portENTER_CRITICAL(&setup_mux); + setup_state = state; + portEXIT_CRITICAL(&setup_mux); +} +static esp_err_t send_json(httpd_req_t *r, const char *status, const char *body) { + httpd_resp_set_status(r, status); + httpd_resp_set_type(r, "application/json; charset=utf-8"); + httpd_resp_set_hdr(r, "Cache-Control", "no-store"); + return httpd_resp_send(r, body, HTTPD_RESP_USE_STRLEN); +} + + +void start_access_point() +{ + // Get the SSID + uint8_t mac[6]; + esp_read_mac(mac, ESP_MAC_WIFI_STA); + snprintf(ap.ssid, 32, "%s-%02X%02X%02X", "VolcConvAI", mac[3], mac[4], mac[5]); + // Initialize the TCP/IP stack + ESP_ERROR_CHECK(esp_netif_init()); + + // Create the default event loop + esp_netif_t *netif = esp_netif_create_default_wifi_ap(); + esp_netif_create_default_wifi_sta(); + + // Set the router IP address to 192.168.4.1 + esp_netif_ip_info_t ip_info; + IP4_ADDR(&ip_info.ip, 192, 168, 4, 1); + IP4_ADDR(&ip_info.gw, 192, 168, 4, 1); + IP4_ADDR(&ip_info.netmask, 255, 255, 255, 0); + esp_netif_dhcps_stop(netif); + esp_netif_set_ip_info(netif, &ip_info); + esp_netif_dhcps_start(netif); + + // Initialize the WiFi stack in Access Point mode + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK(esp_wifi_init(&cfg)); + + // Set the WiFi configuration + wifi_config_t wifi_config = {}; + strncpy((char *)wifi_config.ap.ssid, ap.ssid, sizeof(wifi_config.ap.ssid)); + wifi_config.ap.ssid_len = strlen(ap.ssid); + wifi_config.ap.max_connection = 4; + wifi_config.ap.authmode = WIFI_AUTH_WPA2_PSK; + strncpy((char *)wifi_config.ap.password, ap.ssid, sizeof(wifi_config.ap.password) - 1); + wifi_config.ap.password[sizeof(wifi_config.ap.password) - 1] = '\0'; + + // Start the WiFi Access Point + ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_APSTA)); + ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &wifi_config)); + ESP_ERROR_CHECK(esp_wifi_start()); + + ESP_LOGI(TAG, "Access Point started with SSID %s", ap.ssid); +} + +esp_err_t handler_index_html(httpd_req_t *r) +{ + httpd_resp_set_type(r, "text/html; charset=utf-8"); + httpd_resp_set_hdr(r, "Cache-Control", "no-store"); + httpd_resp_send(r, index_html_start, strlen(index_html_start)); + return ESP_OK; +} + +esp_err_t handler_scan(httpd_req_t *r) +{ + if (get_setup_state() == SETUP_CONNECTING || get_setup_state() == SETUP_SAVED) + return send_json(r, "409 Conflict", "{\"error\":\"busy\"}"); + if (esp_wifi_scan_start(NULL, true) != ESP_OK) + return send_json(r, "503 Service Unavailable", "{\"error\":\"scan_failed\"}"); + uint16_t count = 0; + esp_wifi_scan_get_ap_num(&count); + if (count > 32) count = 32; + wifi_ap_record_t *records = count ? calloc(count, sizeof(*records)) : NULL; + if (count && !records) { + esp_wifi_clear_ap_list(); + return send_json(r, "503 Service Unavailable", "{\"error\":\"no_memory\"}"); + } + if (count && esp_wifi_scan_get_ap_records(&count, records) != ESP_OK) { + free(records); + esp_wifi_clear_ap_list(); + return send_json(r, "503 Service Unavailable", "{\"error\":\"scan_failed\"}"); + } + esp_wifi_clear_ap_list(); + cJSON *list = cJSON_CreateArray(); + for (int i = 0; list && i < count; i++) { + cJSON *item = cJSON_CreateObject(); + if (!item) break; + cJSON_AddStringToObject(item, "ssid", (const char *)records[i].ssid); + cJSON_AddNumberToObject(item, "rssi", records[i].rssi); + cJSON_AddNumberToObject(item, "authmode", records[i].authmode); + cJSON_AddItemToArray(list, item); + } + free(records); + char *body = list ? cJSON_PrintUnformatted(list) : NULL; + cJSON_Delete(list); + esp_err_t ret = body ? send_json(r, "200 OK", body) : + send_json(r, "503 Service Unavailable", "{\"error\":\"no_memory\"}"); + free(body); + return ret; +} + +bool connect2wifi(char *ssid, char *password, EventGroupHandle_t event_group) +{ + xEventGroupClearBits(event_group, WIFI_CONNECTED_BIT | WIFI_FAIL_BIT); + if (esp_wifi_disconnect() == ESP_OK) { + xEventGroupWaitBits(event_group, WIFI_FAIL_BIT, pdTRUE, pdFALSE, pdMS_TO_TICKS(1000)); + } + xEventGroupClearBits(event_group, WIFI_CONNECTED_BIT | WIFI_FAIL_BIT); + // auto esp_netif = esp_netif_create_default_wifi_sta(); + + wifi_config_t wifi_config; + bzero(&wifi_config, sizeof(wifi_config)); + strncpy((char *)wifi_config.sta.ssid, ssid, sizeof(wifi_config.sta.ssid)); + strncpy((char *)wifi_config.sta.password, password, sizeof(wifi_config.sta.password)); + wifi_config.sta.scan_method = WIFI_ALL_CHANNEL_SCAN; + wifi_config.sta.failure_retry_cnt = 1; + + if (esp_wifi_set_config(WIFI_IF_STA, &wifi_config) != ESP_OK) return false; + esp_err_t ret = esp_wifi_connect(); + if (ret != ESP_OK) + { + ESP_LOGE(TAG, "Failed to connect to WiFi: %d", ret); + return false; + } + ESP_LOGI(TAG, "Connecting to WiFi %s", ssid); + + // Require an IP address, with a bounded wait so the form can be retried. + EventBits_t bits = xEventGroupWaitBits(event_group, WIFI_CONNECTED_BIT | WIFI_FAIL_BIT, pdTRUE, pdFALSE, pdMS_TO_TICKS(30000)); + if (bits & WIFI_CONNECTED_BIT) + { + ESP_LOGI(TAG, "Connected to WiFi %s", ssid); + return true; + } + else + { + ESP_LOGE(TAG, "Failed to connect to WiFi %s", ssid); + return false; + } +} + +typedef struct { + char ssid[33]; + char password[65]; + char device_id[37]; + char device_secret[129]; +} setup_input_t; + +static esp_err_t save_setup(const setup_input_t *input) +{ + nvs_handle_t nvs; + esp_err_t ret = nvs_open("wifi", NVS_READWRITE, &nvs); + if (ret != ESP_OK) return ret; + // Keep recovery available if persisting either namespace fails. + ret = nvs_set_u8(nvs, "setup_requested", 1); + if (ret == ESP_OK) ret = nvs_commit(nvs); + if (ret == ESP_OK) ret = platform_identity_save(input->device_id, input->device_secret); + if (ret == ESP_OK) ret = nvs_set_str(nvs, "ssid", input->ssid); + if (ret == ESP_OK) ret = nvs_set_str(nvs, "password", input->password); + if (ret == ESP_OK) ret = nvs_set_u8(nvs, "setup_requested", 0); + if (ret == ESP_OK) ret = nvs_commit(nvs); + nvs_close(nvs); + return ret; +} + +static void setup_worker(void *arg) +{ + setup_input_t *input = arg; + if (!connect2wifi(input->ssid, input->password, ap.event_group)) { + set_setup_state(SETUP_WIFI_FAILED); + } else { + esp_err_t ret = save_setup(input); + ESP_LOGI(TAG, "Setup save result: %s", esp_err_to_name(ret)); + set_setup_state(ret == ESP_OK ? SETUP_SAVED : SETUP_SAVE_FAILED); + } + memset(input, 0, sizeof(*input)); + free(input); + if (get_setup_state() == SETUP_SAVED) { + // Leave time for the browser to retrieve the result before AP shutdown. + vTaskDelay(pdMS_TO_TICKS(15000)); + esp_restart(); + } + vTaskDelete(NULL); +} + +static bool read_field(cJSON *json, const char *name, char *out, size_t capacity, size_t min) +{ + cJSON *value = cJSON_GetObjectItemCaseSensitive(json, name); + if (!cJSON_IsString(value) || !value->valuestring) return false; + size_t length = strlen(value->valuestring); + if (length < min || length >= capacity) return false; + memcpy(out, value->valuestring, length + 1); + return true; +} + +esp_err_t handler_setup_status(httpd_req_t *r) +{ + const char *states[] = {"idle", "connecting", "saved", "wifi_failed", "save_failed"}; + char body[64]; + snprintf(body, sizeof(body), "{\"state\":\"%s\"}", states[get_setup_state()]); + return send_json(r, "200 OK", body); +} + +esp_err_t handler_form_submission(httpd_req_t *r) +{ + if (get_setup_state() == SETUP_CONNECTING || get_setup_state() == SETUP_SAVED) + return send_json(r, "409 Conflict", "{\"error\":\"busy\"}"); + if (r->content_len <= 0 || r->content_len > 1536) + return send_json(r, "400 Bad Request", "{\"error\":\"invalid_fields\"}"); + char *body = calloc(1, r->content_len + 1); + if (!body) return send_json(r, "503 Service Unavailable", "{\"error\":\"no_memory\"}"); + size_t received = 0; + while (received < r->content_len) { + int count = httpd_req_recv(r, body + received, r->content_len - received); + if (count <= 0) { + memset(body, 0, r->content_len); + free(body); + return send_json(r, "408 Request Timeout", "{\"error\":\"receive_timeout\"}"); + } + received += count; + } + cJSON *json = cJSON_Parse(body); + memset(body, 0, r->content_len); + free(body); + setup_input_t *input = calloc(1, sizeof(*input)); + bool valid = input && cJSON_IsObject(json) && + read_field(json, "ssid", input->ssid, sizeof(input->ssid), 1) && + read_field(json, "password", input->password, sizeof(input->password), 0) && + read_field(json, "device_id", input->device_id, sizeof(input->device_id), 36) && + read_field(json, "device_secret", input->device_secret, sizeof(input->device_secret), 32); + cJSON_Delete(json); + if (!valid) { + if (input) { memset(input, 0, sizeof(*input)); free(input); } + ESP_LOGW(TAG, "Setup submission rejected: invalid fields"); + return send_json(r, "400 Bad Request", "{\"error\":\"invalid_fields\"}"); + } + set_setup_state(SETUP_CONNECTING); + if (xTaskCreate(setup_worker, "setup_connect", 6144, input, 5, NULL) != pdPASS) { + memset(input, 0, sizeof(*input)); + free(input); + set_setup_state(SETUP_IDLE); + return send_json(r, "503 Service Unavailable", "{\"error\":\"no_memory\"}"); + } + ESP_LOGI(TAG, "Setup submission accepted; connecting in background"); + return send_json(r, "202 Accepted", "{\"state\":\"connecting\"}"); +} + +void start_web_server() +{ + // Start the web server + httpd_config_t config = HTTPD_DEFAULT_CONFIG(); + config.uri_match_fn = httpd_uri_match_wildcard; + ESP_ERROR_CHECK(httpd_start(&(ap.server), &config)); + + // Register the index.html file + httpd_uri_t index_html = { + .uri = "/", + .method = HTTP_GET, + .handler = handler_index_html, + .user_ctx = NULL}; + ESP_ERROR_CHECK(httpd_register_uri_handler(ap.server, &index_html)); + + // Register the /scan URI + httpd_uri_t scan = { + .uri = "/scan", + .method = HTTP_GET, + .handler = handler_scan, + .user_ctx = NULL}; + ESP_ERROR_CHECK(httpd_register_uri_handler(ap.server, &scan)); + + // Register the form submission + httpd_uri_t form_submit = { + .uri = "/submit", + .method = HTTP_POST, + .handler = handler_form_submission, + .user_ctx = NULL}; + ESP_ERROR_CHECK(httpd_register_uri_handler(ap.server, &form_submit)); + + httpd_uri_t status = { .uri = "/setup-status", .method = HTTP_GET, + .handler = handler_setup_status }; + ESP_ERROR_CHECK(httpd_register_uri_handler(ap.server, &status)); + ESP_LOGI(TAG, "Web server started"); +} + +void handler_event_wifi_ap(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) +{ + if (event_id == WIFI_EVENT_AP_STACONNECTED) + { + wifi_event_ap_staconnected_t *event = (wifi_event_ap_staconnected_t *)event_data; + ESP_LOGI(TAG, "Station " MACSTR " joined, AID=%d", MAC2STR(event->mac), event->aid); + } + else if (event_id == WIFI_EVENT_AP_STADISCONNECTED) + { + wifi_event_ap_stadisconnected_t *event = (wifi_event_ap_stadisconnected_t *)event_data; + ESP_LOGI(TAG, "Station " MACSTR " left, AID=%d", MAC2STR(event->mac), event->aid); + } + else if (event_id == WIFI_EVENT_STA_CONNECTED) + { + // Association alone does not mean DHCP has completed. + } + else if (event_id == WIFI_EVENT_STA_DISCONNECTED) + { + xEventGroupSetBits(ap.event_group, WIFI_FAIL_BIT); + } +} + +void handler_event_ip_ap(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) +{ + if (event_id == IP_EVENT_STA_GOT_IP) + { + ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data; + ESP_LOGI(TAG, "Got IP:" IPSTR, IP2STR(&event->ip_info.ip)); + xEventGroupSetBits(ap.event_group, WIFI_CONNECTED_BIT); + } +} + +void configure_ap() +{ + ap.ssid[0] = '\0'; + ap.event_group = xEventGroupCreate(); + // Register event handlers + ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, + ESP_EVENT_ANY_ID, + &handler_event_wifi_ap, + NULL, + &(ap.instance_any_id))); + + ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, + IP_EVENT_STA_GOT_IP, + &handler_event_ip_ap, + NULL, + &(ap.instance_got_ip))); + + start_access_point(); + start_web_server(); + ESP_LOGI(TAG, "Setup portal ready at " WEB_SERVER_URL); +} diff --git a/firmware/echoear-platform-agent/main/configuration_ap.h b/firmware/echoear-platform-agent/main/configuration_ap.h new file mode 100644 index 0000000..3a621fc --- /dev/null +++ b/firmware/echoear-platform-agent/main/configuration_ap.h @@ -0,0 +1,15 @@ +// Copyright (2025) Beijing Volcano Engine Technology Ltd. +// SPDX-License-Identifier: Apache-2.0 + +#include + +typedef struct +{ + httpd_handle_t server; + EventGroupHandle_t event_group; + esp_event_handler_instance_t instance_any_id; + esp_event_handler_instance_t instance_got_ip; + char ssid[32]; +} ap_t; + +void configure_ap(); \ No newline at end of file diff --git a/firmware/echoear-platform-agent/main/conv_ai_embedded_kit.c b/firmware/echoear-platform-agent/main/conv_ai_embedded_kit.c new file mode 100644 index 0000000..47f8603 --- /dev/null +++ b/firmware/echoear-platform-agent/main/conv_ai_embedded_kit.c @@ -0,0 +1,381 @@ +// Copyright (2025) Beijing Volcano Engine Technology Ltd. +// SPDX-License-Identifier: Apache-2.0 + +#include +#include +#include +#include +#include +#include +#include +#include "esp_board_manager.h" +#include "dev_lcd_touch_i2c.h" + +#include "esp_event.h" +#include "esp_log.h" +#include "esp_system.h" +#include "nvs_flash.h" +#include "esp_netif.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_heap_task_info.h" +#include "esp_random.h" +#include "esp_sntp.h" + +#include "freertos/semphr.h" +#include "esp_err.h" +#include "sdkconfig.h" + +// #include "periph_wifi.h" +// #include "fatfs_stream.h" + +#include "cJSON.h" +#include "network.h" +#include "volc_manager_service.h" +#include "volc_service_common.h" +#include "volc_conv_service.h" +#include "local_logic_service.h" +#include "volc_function_call_service.h" + +#include "volc_hal.h" +#include "volc_hal_display.h" +#include "volc_hal_capture.h" +#include "volc_lvgl_source.h" +// #include "board.h" +#include "esp_mac.h" +#include "esp_gmf_afe.h" +#include "mmap_generate_eaf.h" +#include "platform_client.h" +#include "hal/usb_serial_jtag_ll.h" +#define STATS_TASK_PRIO 5 + +static const char *TAG = "VolcConvAI"; +static volatile bool is_interrupt = false; +static volatile bool conversation_service_ready = false; +extern void conv_ai_service_diagnostic_say(void); + +/* A local USB diagnostic command lets service tests run without repeated taps. + * It does not run automatically or accept commands over the network. */ +static void usb_diagnostic_task(void *arg) +{ + char command[32]; + size_t length = 0; + while (true) { + while (usb_serial_jtag_ll_rxfifo_data_available()) { + uint8_t ch; + if (!usb_serial_jtag_ll_read_rxfifo(&ch, 1)) break; + if (ch == '\n' || ch == '\r') { + command[length] = 0; + if (conversation_service_ready && !strcmp(command, "diag-say")) { + ESP_LOGI(TAG, "USB diagnostic: requesting fixed spoken sentence"); + conv_ai_service_diagnostic_say(); + } + length = 0; + } else if (length < sizeof(command) - 1) { + command[length++] = ch; + } else { + length = 0; + } + } + vTaskDelay(pdMS_TO_TICKS(20)); + } +} + +void session_init(){ + volc_service_manager_init(); + volc_conv_service_manager_init(); + function_call_service_init(); + local_logic_service_init(); +} + +void Task(void* data){ + aios_init(VOLC_SERVICE_EVENT_MAX); // AIOS初始化 + session_init(); + conversation_service_ready = true; + xTaskCreate(usb_diagnostic_task, "usb_diagnostic", 3072, NULL, 3, NULL); + aios_run(); // AIOS启动 + return; +} + +static void sys_monitor_task(void *pvParameters) +{ + static char run_info[1024] = {0}; + while (1) + { + // vTaskGetRunTimeStats(run_info); + vTaskList(run_info); + printf("Task Runtime Stats:\n%s", run_info); + printf("--------------------------------\n"); + // ESP_LOGI(TAG, "Task Runtime Stats:\n%s", run_info); + // ESP_LOGI(TAG, "--------------------------------\n"); + vTaskDelay(pdMS_TO_TICKS(5000)); + } +} + +void initialize_sntp(void) +{ + ESP_LOGI(TAG, "Initializing SNTP"); + + // 设置SNTP操作模式为轮询(客户端) + esp_sntp_setoperatingmode(SNTP_OPMODE_POLL); + + // 设置NTP服务器(可以设置多个备用) + esp_sntp_setservername(0, "pool.ntp.org"); // 主服务器 + esp_sntp_setservername(1, "cn.pool.ntp.org"); // 备用服务器:中国区 + esp_sntp_setservername(2, "ntp1.aliyun.com"); // 备用服务器:阿里云 + + // 初始化SNTP服务 + esp_sntp_init(); + + // 设置时区(例如北京时间CST-8) + setenv("TZ", "CST-8", 1); + tzset(); +} + +void wait_for_time_sync(void) +{ + ESP_LOGI(TAG, "Waiting for time synchronization..."); + + // 检查时间是否已同步(1970年之后) + time_t now = 0; + struct tm timeinfo = {0}; + int retry_count = 5; // 最大重试次数 + + while (timeinfo.tm_year < (2024 - 1900) && retry_count > 0) + { + ESP_LOGI(TAG, "Time not set yet, retrying... (%d)", retry_count); + vTaskDelay(1000 / portTICK_PERIOD_MS); + time(&now); + localtime_r(&now, &timeinfo); + retry_count--; + } + + if (retry_count > 0) + { + // print_current_time(); + } + else + { + ESP_LOGE(TAG, "Failed to synchronize time within timeout period"); + } +} + +static void rec_engine_cb(void *event, void *user_data) +{ + esp_gmf_afe_evt_t * e = (esp_gmf_afe_evt_t *)event; + + if (ESP_GMF_AFE_EVT_WAKEUP_START == e->type) { + if (conversation_service_ready) { + ESP_LOGI(TAG, "Wake word detected; requesting conversation"); + volc_hal_context_t *ctx = volc_get_global_hal_context(); + if (ctx && ctx->display_handle) { + int index = MMAP_EAF_EMOTION_BLINK_FAST_284_126_EAF; + volc_hal_display_set_content(ctx->display_handle, VOLC_DISPLAY_OBJ_MAIN, + VOLC_DISPLAY_IMAGE, &index); + } + aios_event_pub(VOLC_SERVICE_AI_CONVERSATION,NULL,NULL); + } + } + return; +} + +static void wifi_setup_touch_task(void *arg) +{ + dev_lcd_touch_i2c_handles_t *touch = arg; + TickType_t pressed_at = 0; + TickType_t last_conversation_request = 0; + uint16_t pressed_x = 0; + uint16_t pressed_y = 0; + bool holding = false; + bool setup_triggered = false; + for (;;) { + uint16_t x = 0, y = 0; + uint8_t count = 0; + bool pressed = esp_lcd_touch_read_data(touch->touch_handle) == ESP_OK && + esp_lcd_touch_get_coordinates(touch->touch_handle, &x, &y, NULL, &count, 1) && + count > 0; + if (!pressed) { + if (holding && !setup_triggered) { + TickType_t held_for = xTaskGetTickCount() - pressed_at; + ESP_LOGI(TAG, "Touch released at (%u,%u) after %u ms", + pressed_x, pressed_y, (unsigned)(held_for * portTICK_PERIOD_MS)); + if (held_for >= pdMS_TO_TICKS(50) && + held_for < pdMS_TO_TICKS(1500) && conversation_service_ready) { + TickType_t now = xTaskGetTickCount(); + if (last_conversation_request != 0 && + now - last_conversation_request < pdMS_TO_TICKS(5000)) { + ESP_LOGI(TAG, "Ignoring repeated conversation tap"); + holding = false; + vTaskDelay(pdMS_TO_TICKS(50)); + continue; + } + last_conversation_request = now; + ESP_LOGI(TAG, "Screen tapped; requesting conversation"); + volc_hal_context_t *ctx = volc_get_global_hal_context(); + if (ctx && ctx->display_handle) { + int index = MMAP_EAF_EMOTION_BLINK_FAST_284_126_EAF; + volc_hal_display_set_content(ctx->display_handle, VOLC_DISPLAY_OBJ_MAIN, + VOLC_DISPLAY_IMAGE, &index); + } + aios_event_pub(VOLC_SERVICE_AI_CONVERSATION, NULL, NULL); + } + } + holding = false; + setup_triggered = false; + } else if (!holding) { + holding = true; + pressed_at = xTaskGetTickCount(); + pressed_x = x; + pressed_y = y; + } else if (!setup_triggered && pressed_y >= 280 && pressed_y <= 335 && + pressed_x >= 60 && pressed_x <= 300 && + (TickType_t)(xTaskGetTickCount() - pressed_at) >= pdMS_TO_TICKS(3000)) { + nvs_handle_t nvs; + esp_err_t ret = nvs_open("wifi", NVS_READWRITE, &nvs); + if (ret == ESP_OK) { + ret = nvs_set_u8(nvs, "setup_requested", 1); + if (ret == ESP_OK) ret = nvs_commit(nvs); + nvs_close(nvs); + } + if (ret == ESP_OK) { + ESP_LOGI(TAG, "Restarting into setup; device identity and Wi-Fi preserved"); + setup_triggered = true; + esp_restart(); + } + ESP_LOGE(TAG, "Cannot request setup: %s", esp_err_to_name(ret)); + holding = false; + } + vTaskDelay(pdMS_TO_TICKS(50)); + } +} + +static void wifi_setup_button_init(void) +{ + dev_lcd_touch_i2c_handles_t *touch = NULL; + esp_err_t ret = esp_board_manager_get_device_handle("lcd_touch", (void **)&touch); + if (ret != ESP_OK || !touch || !touch->touch_handle) { + ESP_LOGE(TAG, "Wi-Fi setup touch input unavailable"); + return; + } + if (xTaskCreate(wifi_setup_touch_task, "wifi_setup", 4096, touch, 5, NULL) != pdPASS) { + ESP_LOGE(TAG, "Cannot start Wi-Fi setup touch task"); + } +} + +void hal_level_init() +{ + + volc_hal_context_t* g_hal_context = volc_get_global_hal_context(); + volc_hal_capture_config_t config = {0}; + config.media_type = VOLC_MEDIA_TYPE_AUDIO; + config.data_cb = NULL; + config.user_data = g_hal_context; + config.audio_wakeup_cb = (volc_hal_audio_wakeup_cb_t)rec_engine_cb; + volc_hal_capture_t capture = volc_hal_capture_create(&config); + + volc_hal_capture_start(capture,VOLC_AUDIO_MODE_WAKEUP); + + + // Allow other core to finish initialization + // vTaskDelay(pdMS_TO_TICKS(2000)); +} + +static void application_boot(void) +{ + // xTaskCreate(&sys_monitor_task, "sys_monitor_task", 4096, NULL, 5, NULL); + volc_hal_init(); + volc_hal_display_config_t display_config = {0}; + volc_hal_display_create(&display_config); + + volc_hal_context_t* g_hal_context = volc_get_global_hal_context(); + if(g_hal_context == NULL){ + return; + } + volc_hal_display_t global_display = g_hal_context->display_handle; + + /* Initialize the default event loop */ + ESP_ERROR_CHECK(esp_event_loop_create_default()); + esp_log_level_set("*", ESP_LOG_WARN); + esp_log_level_set("*", ESP_LOG_INFO); + + // /* Initialize NVS flash for WiFi configuration */ + esp_err_t ret = nvs_flash_init(); + if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) + { + ESP_LOGW(TAG, "Erasing NVS flash to fix corruption"); + ESP_ERROR_CHECK(nvs_flash_erase()); + ret = nvs_flash_init(); + } + ESP_ERROR_CHECK(ret); + ESP_ERROR_CHECK(platform_client_init()); + ESP_ERROR_CHECK(esp_netif_init()); + wifi_setup_button_init(); + + uint8_t station_mac[6] = {0}; + char setup_status[96] = "等待配网"; + if (esp_read_mac(station_mac, ESP_MAC_WIFI_STA) == ESP_OK) { + snprintf(setup_status, sizeof(setup_status), + "WiFi: VolcConvAI-%02X%02X%02X", + station_mac[3], station_mac[4], station_mac[5]); + } + volc_hal_display_set_content(global_display, VOLC_DISPLAY_OBJ_STATUS, + VOLC_DISPLAY_TEXT, setup_status); + + bool connected = configure_network(); + if (connected == false) + { + volc_hal_display_set_content(global_display, VOLC_DISPLAY_OBJ_STATUS, + VOLC_DISPLAY_TEXT, setup_status); + volc_hal_display_set_content(global_display, VOLC_DISPLAY_OBJ_SUBTITLE, + VOLC_DISPLAY_TEXT, "192.168.4.1 密码同热点名"); + ESP_LOGI(TAG, "Waiting for setup through the Wi-Fi portal"); + return; + } + initialize_sntp(); + wait_for_time_sync(); + if (!platform_identity_is_configured()) { + ESP_LOGE(TAG, "Platform identity is missing; erase Wi-Fi NVS or use the setup portal"); + volc_hal_display_set_content(global_display, VOLC_DISPLAY_OBJ_STATUS, VOLC_DISPLAY_TEXT, + "请通过配网页填写平台设备凭据"); + return; + } + volc_hal_display_set_content(global_display, VOLC_DISPLAY_OBJ_STATUS, VOLC_DISPLAY_TEXT, + "网络已连接,正在激活"); + ret = platform_provision_volc_credential(); + ESP_LOGI(TAG, "Startup stack headroom after activation: %u bytes", (unsigned)uxTaskGetStackHighWaterMark(NULL)); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Platform provider provisioning failed: %s", esp_err_to_name(ret)); + const char *code = platform_last_error_code(); + const char *message = "平台激活失败,请检查配置"; + if (strcmp(code, "DEVICE_ACTIVATION_REQUIRED") == 0) message = "请先在平台绑定家庭和长者"; + else if (strcmp(code, "HARDWARE_AGENT_BINDING_REQUIRED") == 0) message = "请在平台配置硬件智能体"; + else if (strcmp(code, "HARDWARE_AGENT_PRODUCT_MISMATCH") == 0) message = "平台产品配置不一致"; + else if (strcmp(code, "HARDWARE_AGENT_HARDWARE_MISMATCH") == 0) message = "设备凭据与本机不匹配"; + volc_hal_display_set_content(global_display, VOLC_DISPLAY_OBJ_STATUS, VOLC_DISPLAY_TEXT, message); + return; + } + hal_level_init(); + ESP_LOGI(TAG, "Startup stack headroom after audio: %u bytes", (unsigned)uxTaskGetStackHighWaterMark(NULL)); + xTaskCreate(&Task, "aios_task", 8192, NULL, 5, NULL); + int index = MMAP_EAF_HAPPY_EAF; + volc_hal_display_set_content(global_display,VOLC_DISPLAY_OBJ_MAIN,VOLC_DISPLAY_IMAGE,&index); + volc_hal_display_set_content(global_display,VOLC_DISPLAY_OBJ_STATUS,VOLC_DISPLAY_TEXT,"轻触屏幕或说嗨,乐鑫"); +} + +/* Network signing/TLS and audio startup exceed the default 4 KiB main stack. + * Keep this memory available only during startup and free it on every exit. */ +static void application_boot_task(void *arg) +{ + application_boot(); + ESP_LOGI(TAG, "Startup task finished; stack headroom: %u bytes", + (unsigned)uxTaskGetStackHighWaterMark(NULL)); + vTaskDelete(NULL); +} + +void app_main(void) +{ + if (xTaskCreate(application_boot_task, "platform_boot", 24 * 1024, + NULL, 5, NULL) != pdPASS) { + ESP_LOGE(TAG, "Cannot allocate platform startup task"); + } +} diff --git a/firmware/echoear-platform-agent/main/idf_component.yml b/firmware/echoear-platform-agent/main/idf_component.yml new file mode 100644 index 0000000..9d61f4e --- /dev/null +++ b/firmware/echoear-platform-agent/main/idf_component.yml @@ -0,0 +1,69 @@ +## IDF Component Manager Manifest File +dependencies: + ## Required IDF version + idf: + version: '>=5.5.0' + # espressif/esp_board_manager: ^0.4.0 + + # # Put list of dependencies here + # # For components maintained by Espressif: + # component: "~1.0.0" + # # For 3rd party components: + # username/component: ">=1.0.0,<2.0.0" + # username2/component2: + # version: "~1.0.0" + # # For transient dependencies `public` flag can be set. + # # `public` flag doesn't have an effect dependencies of the `main` component. + # # All dependencies of `main` are public by default. + # public: true + volc_conv_ai: + path: ../components/volc_conv_ai + public: true + application: + path: ../components/application + public: true + espressif/button: =4.* + espressif/esp_hosted: + version: ~1.1 + rules: + - if: target in [esp32p4] + + espressif/esp_wifi_remote: + matches: + - if: idf_version <=5.4.0 && target in [esp32p4] + version: ~0.4 + - if: idf_version >5.4.0 && target in [esp32p4] + version: ~0.6 + + lvgl/lvgl: 9.2.* + + espressif/esp_lcd_st77916: ^1.0.1 + + espressif/esp_lcd_touch_cst816s: + version: ^1.1.0 + public: true + + # Keep the versions used by the upstream EchoEar example. Newer releases + # changed LVGL color-format and board-manager Kconfig symbols. + espressif/esp_lvgl_port: + version: "2.6.0" + public: true + espressif/zlib: '*' + + espressif2022/esp_emote_gfx: + version: "^1.2.0~1" + public: true + + espressif2022/esp_vocat_base_control: ^1.0.0 + + espressif/esp_mmap_assets: + version: "*" + public: true + + gmf_core: + override_path: ../components/esp-gmf/gmf_core + gmf_audio: + override_path: ../components/esp-gmf/elements/gmf_audio + esp_board_manager: + version: "0.4.0" + public: true diff --git a/firmware/echoear-platform-agent/main/mmap_generate_eaf.h b/firmware/echoear-platform-agent/main/mmap_generate_eaf.h new file mode 100644 index 0000000..594ecc0 --- /dev/null +++ b/firmware/echoear-platform-agent/main/mmap_generate_eaf.h @@ -0,0 +1,27 @@ +/* + * SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief This file was generated by esp_mmap_assets, don't modify it + */ + +#pragma once + +#include "esp_mmap_assets.h" + +#define MMAP_EAF_FILES 7 +#define MMAP_EAF_CHECKSUM 0x0B63 + +enum MMAP_EAF_LISTS { + MMAP_EAF_HAPPY_EAF = 0, /*!< Happy.eaf */ + MMAP_EAF_BOOT_360_360_EAF = 1, /*!< boot_360_360.eaf */ + MMAP_EAF_EMOTION_ANGRY_284_126_EAF = 2, /*!< emotion_angry_284_126.eaf */ + MMAP_EAF_EMOTION_BLINK1_284_126_EAF = 3, /*!< emotion_blink1_284_126.eaf */ + MMAP_EAF_EMOTION_BLINK_FAST_284_126_EAF = 4, /*!< emotion_blink_fast_284_126.eaf */ + MMAP_EAF_EMOTION_DIZZY_284_126_EAF = 5, /*!< emotion_dizzy_284_126.eaf */ + MMAP_EAF_EMOTION_SLEEP_284_126_EAF = 6, /*!< emotion_sleep_284_126.eaf */ +}; diff --git a/firmware/echoear-platform-agent/main/network.c b/firmware/echoear-platform-agent/main/network.c new file mode 100644 index 0000000..19afee7 --- /dev/null +++ b/firmware/echoear-platform-agent/main/network.c @@ -0,0 +1,214 @@ +// Copyright (2025) Beijing Volcano Engine Technology Ltd. +// SPDX-License-Identifier: Apache-2.0 + +#include "network.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "configuration_ap.h" +#include "platform_client.h" + +#define TAG "NETWORK" + +#define WIFI_EVENT_CONNECTED BIT0 +#define WIFI_EVENT_FAILED BIT1 +#define WIFI_EVENT_STARTED BIT2 +#define MAX_RECONNECT_COUNT 5 + +static network_t network; + +int8_t get_rssi() +{ + // Get station info + wifi_ap_record_t ap_info; + if (esp_wifi_sta_get_ap_info(&ap_info) != ESP_OK) return 0; + return ap_info.rssi; +} + +uint8_t get_channel() +{ + // Get station info + wifi_ap_record_t ap_info; + ESP_ERROR_CHECK(esp_wifi_sta_get_ap_info(&ap_info)); + return ap_info.primary; +} + +void handler_event_wifi_st(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) +{ + if (event_id == WIFI_EVENT_STA_START) + { + xEventGroupSetBits(network.event_group, WIFI_EVENT_STARTED); + } + else if (event_id == WIFI_EVENT_STA_DISCONNECTED) + { + xEventGroupClearBits(network.event_group, WIFI_EVENT_CONNECTED); + if (network.reconnect_count < MAX_RECONNECT_COUNT) + { + /* Retry the selected AP first, but allow another AP if it is no longer + * reachable. Never permanently bind this device to one mesh node. */ + if (network.reconnect_count == 2) { + wifi_config_t config; + if (esp_wifi_get_config(WIFI_IF_STA, &config) == ESP_OK && config.sta.bssid_set) { + config.sta.bssid_set = false; + config.sta.channel = 0; + esp_wifi_set_config(WIFI_IF_STA, &config); + ESP_LOGW(TAG, "Preferred AP unavailable; falling back to SSID scan"); + } + } + esp_wifi_connect(); + network.reconnect_count++; + ESP_LOGI(TAG, "Reconnecting WiFi (attempt %d)", network.reconnect_count); + } + else + { + xEventGroupSetBits(network.event_group, WIFI_EVENT_FAILED); + ESP_LOGI(TAG, "WiFi connection failed"); + } + } +} + +void handler_event_ip_st(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) +{ + ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data; + network.reconnect_count = 0; + + esp_ip4addr_ntoa(&event->ip_info.ip, network.ip_address, sizeof(network.ip_address)); + ESP_LOGI(TAG, "Got IP: %s", network.ip_address); + xEventGroupSetBits(network.event_group, WIFI_EVENT_CONNECTED); +} + +bool is_connected() +{ + return xEventGroupGetBits(network.event_group) & WIFI_EVENT_CONNECTED; +} + +bool configure_network() +{ + + // Create the event group + network.ssid[0] = '\0'; + network.password[0] = '\0'; + network.reconnect_count = 0; + network.ip_address[0] = '\0'; + network.event_group = xEventGroupCreate(); + + // Get ssid and password from NVS + nvs_handle_t nvs_handle; + uint8_t setup_requested = 0; + esp_err_t ret = nvs_open("wifi", NVS_READONLY, &nvs_handle); + if (ret == ESP_OK) + { + nvs_get_u8(nvs_handle, "setup_requested", &setup_requested); + size_t length = sizeof(network.ssid); + nvs_get_str(nvs_handle, "ssid", network.ssid, &length); + length = sizeof(network.password); + nvs_get_str(nvs_handle, "password", network.password, &length); + nvs_close(nvs_handle); + } + if (setup_requested || !platform_identity_is_configured()) + { + ESP_LOGI(TAG, "Starting setup: requested or platform identity missing"); + configure_ap(); + return false; + } + // Try to connect to WiFi, if failed, launch the WiFi configuration AP + if (network.ssid[0] != '\0') + { + ESP_LOGI(TAG, "Connecting to WiFi %s", network.ssid); + // Initialize the TCP/IP stack + ESP_ERROR_CHECK(esp_netif_init()); + + esp_event_handler_instance_t instance_any_id; + esp_event_handler_instance_t instance_got_ip; + ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, + ESP_EVENT_ANY_ID, + &handler_event_wifi_st, + NULL, + &instance_any_id)); + ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, + IP_EVENT_STA_GOT_IP, + &handler_event_ip_st, + NULL, + &instance_got_ip)); + + // Create the default event loop + esp_netif_t *station_netif = esp_netif_create_default_wifi_sta(); + + // Initialize the WiFi stack in station mode + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK(esp_wifi_init(&cfg)); + ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); + ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM)); + + ESP_LOGI(TAG, "Connecting to WiFi ssid=%s password=***", network.ssid); + wifi_config_t wifi_config; + bzero(&wifi_config, sizeof(wifi_config)); + strncpy((char *)wifi_config.sta.ssid, network.ssid, sizeof(wifi_config.sta.ssid)); + strncpy((char *)wifi_config.sta.password, network.password, sizeof(wifi_config.sta.password)); + wifi_config.sta.scan_method = WIFI_ALL_CHANNEL_SCAN; + wifi_config.sta.sort_method = WIFI_CONNECT_AP_BY_SIGNAL; + wifi_config.sta.threshold.authmode = WIFI_AUTH_OPEN; + ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config)); + + // Start the WiFi stack + ESP_ERROR_CHECK(esp_wifi_start()); + ESP_ERROR_CHECK(esp_wifi_set_ps(WIFI_PS_NONE)); + xEventGroupWaitBits(network.event_group, WIFI_EVENT_STARTED, pdFALSE, pdFALSE, pdMS_TO_TICKS(5000)); + wifi_scan_config_t scan = {.ssid = (uint8_t *)network.ssid}; + if (esp_wifi_scan_start(&scan, true) == ESP_OK) { + wifi_ap_record_t records[16]; + uint16_t count = 16; + if (esp_wifi_scan_get_ap_records(&count, records) == ESP_OK && count > 0) { + uint16_t best = 0; + for (uint16_t i = 1; i < count; ++i) { + if (records[i].rssi > records[best].rssi) best = i; + } + wifi_config.sta.bssid_set = true; + memcpy(wifi_config.sta.bssid, records[best].bssid, sizeof(wifi_config.sta.bssid)); + wifi_config.sta.channel = records[best].primary; + ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config)); + ESP_LOGI(TAG, "Selected strongest AP among %u: rssi=%d channel=%u bssid=%02x:%02x:%02x:%02x:%02x:%02x", + count, records[best].rssi, records[best].primary, + records[best].bssid[0], records[best].bssid[1], records[best].bssid[2], + records[best].bssid[3], records[best].bssid[4], records[best].bssid[5]); + } + } + ESP_ERROR_CHECK(esp_wifi_connect()); + + // Wait for the WiFi stack to start + EventBits_t bits = xEventGroupWaitBits(network.event_group, WIFI_EVENT_CONNECTED | WIFI_EVENT_FAILED, pdFALSE, pdFALSE, pdMS_TO_TICKS(30 * 1000)); + if (!(bits & WIFI_EVENT_CONNECTED)) + { + ESP_LOGE(TAG, "WifiStation failed"); + // Reset the WiFi stack + + // 取消注册事件处理程序 + ESP_ERROR_CHECK(esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, instance_any_id)); + ESP_ERROR_CHECK(esp_event_handler_instance_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, instance_got_ip)); + ESP_ERROR_CHECK(esp_wifi_stop()); + ESP_ERROR_CHECK(esp_wifi_deinit()); + esp_netif_destroy_default_wifi(station_netif); + xEventGroupClearBits(network.event_group, WIFI_EVENT_CONNECTED | WIFI_EVENT_FAILED); + // nvs_flash_erase(); + // return false; + } else { + ESP_LOGI(TAG, "Connected to %s rssi=%d channel=%d", network.ssid, get_rssi(), get_channel()); + } + } + + if (!is_connected()) + { + configure_ap(); + ESP_LOGI(TAG, "WiFi not connected, starting AP"); + return false; + } + + return true; +} diff --git a/firmware/echoear-platform-agent/main/network.h b/firmware/echoear-platform-agent/main/network.h new file mode 100644 index 0000000..b003f64 --- /dev/null +++ b/firmware/echoear-platform-agent/main/network.h @@ -0,0 +1,17 @@ +// Copyright (2025) Beijing Volcano Engine Technology Ltd. +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#include + +typedef struct +{ + EventGroupHandle_t event_group; + + char ssid[32]; + char password[64]; + int reconnect_count; + char ip_address[16]; +} network_t; + +bool configure_network(); \ No newline at end of file diff --git a/firmware/echoear-platform-agent/main/pipeline.c b/firmware/echoear-platform-agent/main/pipeline.c new file mode 100644 index 0000000..a308091 --- /dev/null +++ b/firmware/echoear-platform-agent/main/pipeline.c @@ -0,0 +1,301 @@ +// Copyright (2025) Beijing Volcano Engine Technology Ltd. +// SPDX-License-Identifier: Apache-2.0 +#include "pipeline.h" +#include +#include "esp_log.h" +#include "sdkconfig.h" +#include "audio_event_iface.h" +#include "audio_common.h" +#include "audio_sys.h" +#include "board.h" +#include "algorithm_stream.h" +#include "filter_resample.h" +#include "i2s_stream.h" +#include "pthread.h" +#if (CONFIG_ESP32_S3_KORVO2_V3_BOARD || CONFIG_ESP32_S3_ECHOEAR_V1_2_BOARD) +#include "es7210.h" +#elif CONFIG_M5STACK_ATOMS3R_BOARD +#include "es8311.h" +#endif +#include "esp_timer.h" +#if defined(CONFIG_VOLC_AUDIO_G711A) +#include "g711_encoder.h" +#include "g711_decoder.h" +#endif +#include "audio_idf_version.h" +#include "raw_stream.h" +#define CHANNEL 1 +static const char *TAG = "AUDIO_PIPELINE"; +#define I2S_SAMPLE_RATE 16000 +#define ALGO_SAMPLE_RATE 16000 +#if (CONFIG_ESP32_S3_KORVO2_V3_BOARD || CONFIG_ESP32_S3_ECHOEAR_V1_2_BOARD) +#define ALGORITHM_STREAM_SAMPLE_BIT 32 +#define CHANNEL_FORMAT I2S_CHANNEL_TYPE_ONLY_LEFT +#define ALGORITHM_INPUT_FORMAT "RM" +#define CHANNEL_NUM 1 +#elif CONFIG_M5STACK_ATOMS3R_BOARD +#define ALGORITHM_STREAM_SAMPLE_BIT 16 +#define CHANNEL_FORMAT I2S_CHANNEL_TYPE_RIGHT_LEFT +#define ALGORITHM_INPUT_FORMAT "MR" +#define CHANNEL_NUM 2 +#endif +#if (CONFIG_VOLC_AUDIO_G711A) +#define CODEC_NAME "g711a" +#define CODEC_SAMPLE_RATE 8000 +#else +#define CODEC_NAME "pcm" +#define CODEC_SAMPLE_RATE 16000 +#endif + +static audio_element_handle_t create_resample_stream(int src_rate, int src_ch, int dest_rate, int dest_ch) +{ + rsp_filter_cfg_t rsp_cfg = DEFAULT_RESAMPLE_FILTER_CONFIG(); + rsp_cfg.src_rate = src_rate; + rsp_cfg.src_ch = src_ch; + rsp_cfg.dest_rate = dest_rate; + rsp_cfg.dest_ch = dest_ch; + rsp_cfg.complexity = 5; + audio_element_handle_t stream = rsp_filter_init(&rsp_cfg); + return stream; +} + +static audio_element_handle_t create_record_i2s_stream(void) +{ +#if (CONFIG_ESP32_S3_KORVO2_V3_BOARD || CONFIG_ESP32_S3_ECHOEAR_V1_2_BOARD) + es7210_adc_set_gain(ES7210_INPUT_MIC3, GAIN_30DB); +#elif CONFIG_M5STACK_ATOMS3R_BOARD + es8311_set_mic_gain(ES8311_MIC_GAIN_36DB); +#endif + i2s_stream_cfg_t i2s_cfg = I2S_STREAM_CFG_DEFAULT_WITH_PARA(CODEC_ADC_I2S_PORT, I2S_SAMPLE_RATE, ALGORITHM_STREAM_SAMPLE_BIT, AUDIO_STREAM_READER); // 参数需要仔细检查 + i2s_cfg.type = AUDIO_STREAM_READER; + i2s_stream_set_channel_type(&i2s_cfg, CHANNEL_FORMAT); + i2s_cfg.std_cfg.clk_cfg.sample_rate_hz = I2S_SAMPLE_RATE; + return i2s_stream_init(&i2s_cfg); +} +static audio_element_handle_t create_record_encoder_stream(void) +{ +#if(CONFIG_VOLC_AUDIO_G711A) + g711_encoder_cfg_t g711_cfg = DEFAULT_G711_ENCODER_CONFIG(); + return g711_encoder_init(&g711_cfg); +#else + return NULL; +#endif +} + +static audio_element_handle_t create_record_raw_stream(void) +{ + audio_element_handle_t raw_stream = NULL; + raw_stream_cfg_t raw_cfg = RAW_STREAM_CFG_DEFAULT(); + raw_cfg.type = AUDIO_STREAM_WRITER; + raw_cfg.out_rb_size = 2 * 1024; + raw_stream = raw_stream_init(&raw_cfg); + audio_element_set_output_timeout(raw_stream, portMAX_DELAY); + return raw_stream; +} + +static audio_element_handle_t create_record_algo_stream(void) +{ + ESP_LOGI(TAG, "[3.1] Create algorithm stream for aec"); + algorithm_stream_cfg_t algo_config = ALGORITHM_STREAM_CFG_DEFAULT(); + algo_config.sample_rate = ALGO_SAMPLE_RATE; + algo_config.out_rb_size = 256; + algo_config.algo_mask = ALGORITHM_STREAM_DEFAULT_MASK | ALGORITHM_STREAM_USE_AGC; + algo_config.input_format = ALGORITHM_INPUT_FORMAT; + audio_element_handle_t element_algo = algo_stream_init(&algo_config); + audio_element_set_music_info(element_algo, ALGO_SAMPLE_RATE, 1, 16); + audio_element_set_input_timeout(element_algo, portMAX_DELAY); + return element_algo; +} + +recorder_pipeline_handle_t recorder_pipeline_open() +{ + recorder_pipeline_handle_t pipeline = heap_caps_calloc(1, sizeof(recorder_pipeline_t), MALLOC_CAP_SPIRAM | MALLOC_CAP_DEFAULT); + esp_log_level_set(TAG, ESP_LOG_INFO); + // create and register streams + audio_pipeline_cfg_t pipeline_cfg = DEFAULT_AUDIO_PIPELINE_CONFIG(); + pipeline->audio_pipeline = audio_pipeline_init(&pipeline_cfg); + mem_assert(pipeline->audio_pipeline); + pipeline->i2s_stream_reader = create_record_i2s_stream(); + audio_pipeline_register(pipeline->audio_pipeline, pipeline->i2s_stream_reader, "i2s"); + pipeline->algo_aec = create_record_algo_stream(); + audio_pipeline_register(pipeline->audio_pipeline, pipeline->algo_aec, "algo"); + pipeline->rsp = create_resample_stream(I2S_SAMPLE_RATE, 1, CODEC_SAMPLE_RATE, 1); + audio_pipeline_register(pipeline->audio_pipeline, pipeline->rsp, "rsp"); + pipeline->audio_encoder = create_record_encoder_stream(); + if (pipeline->audio_encoder) { + audio_pipeline_register(pipeline->audio_pipeline, pipeline->audio_encoder, CODEC_NAME); + } + pipeline->raw_reader = create_record_raw_stream(); + audio_pipeline_register(pipeline->audio_pipeline, pipeline->raw_reader, "raw"); +#if (CONFIG_VOLC_AUDIO_G711A) + const char *link_tag[] = {"i2s", "algo", "rsp", CODEC_NAME, "raw"}; +#else + const char *link_tag[] = {"i2s", "algo", "raw"}; +#endif + audio_pipeline_link(pipeline->audio_pipeline, &link_tag[0], sizeof(link_tag) / sizeof(link_tag[0])); + return pipeline; +} + +void recorder_pipeline_close(recorder_pipeline_handle_t pipeline) +{ + audio_pipeline_stop(pipeline->audio_pipeline); + audio_pipeline_wait_for_stop(pipeline->audio_pipeline); + audio_pipeline_terminate(pipeline->audio_pipeline); + if (pipeline->i2s_stream_reader) + { + audio_pipeline_unregister(pipeline->audio_pipeline, pipeline->i2s_stream_reader); + audio_element_deinit(pipeline->i2s_stream_reader); + } + if (pipeline->audio_encoder) { + audio_pipeline_unregister(pipeline->audio_pipeline, pipeline->audio_encoder); + audio_element_deinit(pipeline->audio_encoder); + } + if (pipeline->raw_reader) + { + audio_pipeline_unregister(pipeline->audio_pipeline, pipeline->raw_reader); + audio_element_deinit(pipeline->raw_reader); + } + if (pipeline->rsp) + { + audio_pipeline_unregister(pipeline->audio_pipeline, pipeline->rsp); + audio_element_deinit(pipeline->rsp); + } + if (pipeline->algo_aec) + { + audio_pipeline_unregister(pipeline->audio_pipeline, pipeline->algo_aec); + audio_element_deinit(pipeline->algo_aec); + } + audio_pipeline_deinit(pipeline->audio_pipeline); + heap_caps_free(pipeline); +}; + +void recorder_pipeline_run(recorder_pipeline_handle_t pipeline) +{ + audio_pipeline_run(pipeline->audio_pipeline); +}; + +int recorder_pipeline_get_default_read_size(recorder_pipeline_handle_t pipeline) +{ + // 60ms +#if (CONFIG_VOLC_AUDIO_G711A) + return (160 * 3); +#else + return 1920; +#endif +}; + +audio_element_handle_t recorder_pipeline_get_raw_reader(recorder_pipeline_handle_t pipeline) +{ + return pipeline->raw_reader; +}; + +audio_pipeline_handle_t recorder_pipeline_get_pipeline(recorder_pipeline_handle_t pipeline) +{ + return pipeline->audio_pipeline; +}; + +int recorder_pipeline_read(recorder_pipeline_handle_t pipeline, char *buffer, int buf_size) +{ + return raw_stream_read(pipeline->raw_reader, buffer, buf_size); +} + +static audio_element_handle_t create_player_raw_stream(void) +{ + raw_stream_cfg_t raw_cfg = RAW_STREAM_CFG_DEFAULT(); + raw_cfg.type = AUDIO_STREAM_READER; + raw_cfg.out_rb_size = 8 * 1024; + return raw_stream_init(&raw_cfg); +} + +static audio_element_handle_t create_player_i2s_stream(void) +{ + i2s_stream_cfg_t i2s_cfg = I2S_STREAM_CFG_DEFAULT_WITH_PARA(I2S_NUM_0, I2S_SAMPLE_RATE, ALGORITHM_STREAM_SAMPLE_BIT, AUDIO_STREAM_WRITER); + i2s_cfg.type = AUDIO_STREAM_WRITER; +#if (CONFIG_ESP32_S3_KORVO2_V3_BOARD || CONFIG_ESP32_S3_ECHOEAR_V1_2_BOARD) + i2s_cfg.need_expand = (16 != 32); +#endif + i2s_cfg.out_rb_size = 8 * 1024; + i2s_cfg.buffer_len = 1416; // 708 + i2s_stream_set_channel_type(&i2s_cfg, CHANNEL_FORMAT); + audio_element_handle_t stream = i2s_stream_init(&i2s_cfg); + i2s_stream_set_clk(stream, I2S_SAMPLE_RATE, ALGORITHM_STREAM_SAMPLE_BIT, CHANNEL_NUM); + return stream; +} + +static audio_element_handle_t create_player_decoder_stream(void) +{ +#if (CONFIG_VOLC_AUDIO_G711A) + g711_decoder_cfg_t g711_dec_cfg = DEFAULT_G711_DECODER_CONFIG(); + g711_dec_cfg.out_rb_size = 8 * 1024; + return g711_decoder_init(&g711_dec_cfg); +#else + return NULL; +#endif +} + +player_pipeline_handle_t player_pipeline_open(void) +{ + player_pipeline_handle_t player_pipeline = heap_caps_calloc(1, sizeof(player_pipeline_t), MALLOC_CAP_SPIRAM | MALLOC_CAP_DEFAULT); + esp_log_level_set(TAG, ESP_LOG_INFO); + assert(player_pipeline != 0); + audio_pipeline_cfg_t pipeline_cfg = DEFAULT_AUDIO_PIPELINE_CONFIG(); + player_pipeline->audio_pipeline = audio_pipeline_init(&pipeline_cfg); + mem_assert(pipeline); + player_pipeline->raw_writer = create_player_raw_stream(); + audio_pipeline_register(player_pipeline->audio_pipeline, player_pipeline->raw_writer, "raw"); + player_pipeline->i2s_stream_writer = create_player_i2s_stream(); + audio_pipeline_register(player_pipeline->audio_pipeline, player_pipeline->i2s_stream_writer, "i2s"); + player_pipeline->rsp = create_resample_stream(CODEC_SAMPLE_RATE, 1, I2S_SAMPLE_RATE, CHANNEL_NUM); + audio_element_set_output_timeout(player_pipeline->rsp, portMAX_DELAY); + audio_pipeline_register(player_pipeline->audio_pipeline, player_pipeline->rsp, "rsp"); + player_pipeline->audio_decoder = create_player_decoder_stream(); + if (player_pipeline->audio_decoder != NULL) { + audio_pipeline_register(player_pipeline->audio_pipeline, player_pipeline->audio_decoder, CODEC_NAME); + } +#if (CONFIG_VOLC_AUDIO_G711A) + const char *link_tag[] = {"raw", CODEC_NAME, "rsp", "i2s"}; +#else + const char *link_tag[] = {"raw", "rsp", "i2s"}; +#endif + audio_pipeline_link(player_pipeline->audio_pipeline, &link_tag[0], sizeof(link_tag) / sizeof(link_tag[0])); + return player_pipeline; +} + +void player_pipeline_run(player_pipeline_handle_t player_pipeline) +{ + audio_pipeline_run(player_pipeline->audio_pipeline); +}; + +void player_pipeline_close(player_pipeline_handle_t player_pipeline) +{ + audio_pipeline_stop(player_pipeline->audio_pipeline); + audio_pipeline_wait_for_stop(player_pipeline->audio_pipeline); + audio_pipeline_terminate(player_pipeline->audio_pipeline); + if (player_pipeline->raw_writer) + { + audio_pipeline_unregister(player_pipeline->audio_pipeline, player_pipeline->raw_writer); + audio_element_deinit(player_pipeline->raw_writer); + } + if (player_pipeline->rsp) + { + audio_pipeline_unregister(player_pipeline->audio_pipeline, player_pipeline->rsp); + audio_element_deinit(player_pipeline->rsp); + } + if (player_pipeline->i2s_stream_writer) + { + audio_pipeline_unregister(player_pipeline->audio_pipeline, player_pipeline->i2s_stream_writer); + audio_element_deinit(player_pipeline->i2s_stream_writer); + } + if (player_pipeline->audio_decoder) { + audio_pipeline_unregister(player_pipeline->audio_pipeline, player_pipeline->audio_decoder); + audio_element_deinit(player_pipeline->audio_decoder); + } + audio_pipeline_deinit(player_pipeline->audio_pipeline); + heap_caps_free(player_pipeline); +}; + +int player_pipeline_write(player_pipeline_handle_t player_pipeline, char *buffer, int buf_size) +{ + raw_stream_write(player_pipeline->raw_writer, buffer, buf_size); + return 0; +}; diff --git a/firmware/echoear-platform-agent/main/pipeline.h b/firmware/echoear-platform-agent/main/pipeline.h new file mode 100644 index 0000000..6f90d45 --- /dev/null +++ b/firmware/echoear-platform-agent/main/pipeline.h @@ -0,0 +1,53 @@ +// Copyright (2025) Beijing Volcano Engine Technology Ltd. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef __PIPELINE_H__ +#define __PIPELINE_H__ + +#include +#include +#include +#include "audio_pipeline.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + struct recorder_pipeline_t + { + audio_pipeline_handle_t audio_pipeline; + audio_element_handle_t i2s_stream_reader; + audio_element_handle_t audio_encoder; + audio_element_handle_t raw_reader; + audio_element_handle_t rsp; + audio_element_handle_t algo_aec; + }; + + typedef struct recorder_pipeline_t recorder_pipeline_t, *recorder_pipeline_handle_t; + recorder_pipeline_handle_t recorder_pipeline_open(); + void recorder_pipeline_run(recorder_pipeline_handle_t); + void recorder_pipeline_close(recorder_pipeline_handle_t); + int recorder_pipeline_get_default_read_size(recorder_pipeline_handle_t); + int recorder_pipeline_read(recorder_pipeline_handle_t, char *buffer, int buf_size); + + struct player_pipeline_t + { + audio_pipeline_handle_t audio_pipeline; + audio_element_handle_t raw_writer; + audio_element_handle_t audio_decoder; + audio_element_handle_t rsp; + audio_element_handle_t i2s_stream_writer; + }; + typedef struct player_pipeline_t player_pipeline_t, *player_pipeline_handle_t; + player_pipeline_handle_t player_pipeline_open(); + void player_pipeline_run(player_pipeline_handle_t); + void player_pipeline_close(player_pipeline_handle_t); + int player_pipeline_get_default_read_size(player_pipeline_handle_t); + int player_pipeline_write(player_pipeline_handle_t, char *buffer, int buf_size); + void player_pipeline_write_play_buffer_flag(player_pipeline_handle_t player_pipeline); + +#ifdef __cplusplus +} +#endif +#endif \ No newline at end of file diff --git a/firmware/echoear-platform-agent/main/platform_client.c b/firmware/echoear-platform-agent/main/platform_client.c new file mode 100644 index 0000000..52d4e0e --- /dev/null +++ b/firmware/echoear-platform-agent/main/platform_client.c @@ -0,0 +1,714 @@ +#include "platform_client.h" + +#include +#include +#include +#include + +#include "cJSON.h" +#include "esp_crt_bundle.h" +#include "esp_http_client.h" +#include "esp_log.h" +#include "esp_mac.h" +#include "esp_random.h" +#include "esp_check.h" +#include "mbedtls/base64.h" +#include "mbedtls/md.h" +#include "mbedtls/sha256.h" +#include "nvs.h" +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" +#include "freertos/task.h" + +#define PLATFORM_API_ORIGIN "https://121.41.46.238" +#define PLATFORM_RESPONSE_MAX (32 * 1024) +#define PLATFORM_FIRMWARE_VERSION "1.0.0-echoear-platform.1" +#define PLATFORM_HARDWARE_AGENT_SDK_VERSION "2.0.0" + +static const char *TAG = "platform_client"; + +typedef struct { + char *data; + size_t length; + size_t capacity; +} response_buffer_t; + +static char s_device_id[PLATFORM_DEVICE_ID_MAX]; +static char s_device_secret[PLATFORM_DEVICE_SECRET_MAX]; +static char s_hardware_id[PLATFORM_HARDWARE_ID_MAX]; +static char s_last_error_code[80]; + +const char *platform_last_error_code(void) +{ + return s_last_error_code; +} +static platform_volc_credential_t s_volc; +static int64_t s_config_revision = 1; +static int64_t s_event_sequence = 0; +static int64_t s_turn_sequence = 0; +static platform_agent_session_t s_active_session; +static esp_http_client_handle_t s_http_client; +static StaticSemaphore_t s_http_mutex_storage; +static SemaphoreHandle_t s_http_mutex; +static unsigned s_http_connection_count; +static uint32_t s_heartbeat_interval_ms = 15000; +static TickType_t s_heartbeat_due; + +typedef struct { + uint32_t version; + int64_t sequence; + char conversation_id[37]; + char provider_session_ref[201]; + char session_nonce[257]; +} pending_session_t; + +static esp_err_t persist_pending_session(const platform_agent_session_t *session, int64_t sequence) +{ + nvs_handle_t nvs; + esp_err_t err = nvs_open("platform", NVS_READWRITE, &nvs); + if (err != ESP_OK) return err; + if (session) { + pending_session_t pending = {.version = 1, .sequence = sequence}; + snprintf(pending.conversation_id, sizeof(pending.conversation_id), "%s", session->conversation_id); + snprintf(pending.provider_session_ref, sizeof(pending.provider_session_ref), "%s", session->provider_session_ref); + snprintf(pending.session_nonce, sizeof(pending.session_nonce), "%s", session->session_nonce); + err = nvs_set_blob(nvs, "pending_conv", &pending, sizeof(pending)); + } else { + err = nvs_erase_key(nvs, "pending_conv"); + if (err == ESP_ERR_NVS_NOT_FOUND) err = ESP_OK; + } + if (err == ESP_OK) err = nvs_commit(nvs); + nvs_close(nvs); + return err; +} + +static esp_err_t recover_pending_session(void) +{ + nvs_handle_t nvs; + esp_err_t err = nvs_open("platform", NVS_READONLY, &nvs); + if (err != ESP_OK) return err; + pending_session_t pending = {0}; + size_t size = sizeof(pending); + err = nvs_get_blob(nvs, "pending_conv", &pending, &size); + nvs_close(nvs); + if (err == ESP_ERR_NVS_NOT_FOUND) return ESP_OK; + if (err != ESP_OK) return err; + if (size != sizeof(pending) || pending.version != 1) return ESP_ERR_INVALID_STATE; + platform_agent_session_t session = {0}; + memcpy(session.conversation_id, pending.conversation_id, sizeof(session.conversation_id)); + memcpy(session.provider_session_ref, pending.provider_session_ref, sizeof(session.provider_session_ref)); + memcpy(session.session_nonce, pending.session_nonce, sizeof(session.session_nonce)); + session.conversation_id[sizeof(session.conversation_id) - 1] = '\0'; + session.provider_session_ref[sizeof(session.provider_session_ref) - 1] = '\0'; + session.session_nonce[sizeof(session.session_nonce) - 1] = '\0'; + s_event_sequence = pending.sequence; + err = platform_report_session_event(&session, "session_failed", "DEVICE_RESTARTED"); + /* The event sequence is persisted before every send. A higher recovery + * sequence can conflict only when this owned session is already terminal. */ + if (err == ESP_OK || strcmp(s_last_error_code, "HARDWARE_AGENT_EVENT_CONFLICT") == 0 || + strcmp(s_last_error_code, "CONVERSATION_NOT_FOUND") == 0) { + ESP_LOGI(TAG, "Recovered previous device session after restart"); + return persist_pending_session(NULL, 0); + } + return err; +} + +static void copy_json_string(cJSON *object, const char *name, char *target, size_t target_size) +{ + cJSON *item = cJSON_GetObjectItemCaseSensitive(object, name); + if (cJSON_IsString(item) && item->valuestring != NULL) { + snprintf(target, target_size, "%s", item->valuestring); + } +} + +static esp_err_t load_string(nvs_handle_t nvs, const char *key, char *target, size_t target_size) +{ + size_t required = target_size; + esp_err_t err = nvs_get_str(nvs, key, target, &required); + if (err != ESP_OK || required < 2 || required > target_size) { + target[0] = '\0'; + return err == ESP_OK ? ESP_ERR_INVALID_SIZE : err; + } + return ESP_OK; +} + +esp_err_t platform_identity_save(const char *device_id, const char *device_secret) +{ + if (device_id == NULL || device_secret == NULL || strlen(device_id) != 36 || + strlen(device_secret) < 32 || strlen(device_secret) >= PLATFORM_DEVICE_SECRET_MAX) { + return ESP_ERR_INVALID_ARG; + } + nvs_handle_t nvs; + esp_err_t err = nvs_open("platform", NVS_READWRITE, &nvs); + if (err != ESP_OK) return err; + err = nvs_set_str(nvs, "device_id", device_id); + if (err == ESP_OK) err = nvs_set_str(nvs, "device_secret", device_secret); + if (err == ESP_OK) err = nvs_commit(nvs); + nvs_close(nvs); + if (err == ESP_OK) { + snprintf(s_device_id, sizeof(s_device_id), "%s", device_id); + snprintf(s_device_secret, sizeof(s_device_secret), "%s", device_secret); + } + return err; +} + +bool platform_identity_is_configured(void) +{ + return strlen(s_device_id) == 36 && strlen(s_device_secret) >= 32; +} + +const char *platform_hardware_id(void) +{ + return s_hardware_id; +} + +const platform_volc_credential_t *platform_volc_credential(void) +{ + return s_volc.device_secret[0] ? &s_volc : NULL; +} + +esp_err_t platform_client_init(void) +{ + if (!s_http_mutex) s_http_mutex = xSemaphoreCreateMutexStatic(&s_http_mutex_storage); + if (!s_http_mutex) return ESP_ERR_NO_MEM; + uint8_t mac[6] = {0}; + ESP_RETURN_ON_ERROR(esp_read_mac(mac, ESP_MAC_WIFI_STA), TAG, "read station MAC"); + snprintf(s_hardware_id, sizeof(s_hardware_id), "echoear-%02x%02x%02x%02x%02x%02x", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + + nvs_handle_t nvs; + esp_err_t err = nvs_open("platform", NVS_READONLY, &nvs); + if (err == ESP_OK) { + (void)load_string(nvs, "device_id", s_device_id, sizeof(s_device_id)); + (void)load_string(nvs, "device_secret", s_device_secret, sizeof(s_device_secret)); + (void)load_string(nvs, "instance_id", s_volc.instance_id, sizeof(s_volc.instance_id)); + (void)load_string(nvs, "product_key", s_volc.product_key, sizeof(s_volc.product_key)); + (void)load_string(nvs, "device_name", s_volc.device_name, sizeof(s_volc.device_name)); + (void)load_string(nvs, "volc_secret", s_volc.device_secret, sizeof(s_volc.device_secret)); + nvs_close(nvs); + } + snprintf(s_volc.hardware_id, sizeof(s_volc.hardware_id), "%s", s_hardware_id); + ESP_LOGI(TAG, "hardware_id=%s platform_identity=%s", s_hardware_id, + platform_identity_is_configured() ? "configured" : "missing"); + return ESP_OK; +} + +static void timestamp_now(char output[32]) +{ + time_t now = time(NULL); + struct tm utc = {0}; + gmtime_r(&now, &utc); + strftime(output, 32, "%Y-%m-%dT%H:%M:%S.000Z", &utc); +} + +static void random_hex(char *output, size_t bytes) +{ + static const char hex[] = "0123456789abcdef"; + for (size_t i = 0; i < bytes; ++i) { + uint8_t value = (uint8_t)esp_random(); + output[i * 2] = hex[value >> 4]; + output[i * 2 + 1] = hex[value & 0x0f]; + } + output[bytes * 2] = '\0'; +} + +static void sha256_hex(const char *body, char output[65]) +{ + unsigned char digest[32]; + mbedtls_sha256((const unsigned char *)body, strlen(body), digest, 0); + for (size_t i = 0; i < sizeof(digest); ++i) snprintf(output + i * 2, 3, "%02x", digest[i]); +} + +static esp_err_t signature_header(const char *method, const char *path, const char *timestamp, + const char *nonce, const char *digest, char output[64]) +{ + char canonical[768]; + int written = snprintf(canonical, sizeof(canonical), "%s\n%s\n%s\n%s\n%s", + method, path, timestamp, nonce, digest); + if (written <= 0 || written >= (int)sizeof(canonical)) return ESP_ERR_INVALID_SIZE; + unsigned char mac[32]; + const mbedtls_md_info_t *info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256); + if (mbedtls_md_hmac(info, (const unsigned char *)s_device_secret, strlen(s_device_secret), + (const unsigned char *)canonical, written, mac) != 0) return ESP_FAIL; + unsigned char encoded[48]; + size_t encoded_length = 0; + if (mbedtls_base64_encode(encoded, sizeof(encoded), &encoded_length, mac, sizeof(mac)) != 0) return ESP_FAIL; + while (encoded_length > 0 && encoded[encoded_length - 1] == '=') --encoded_length; + for (size_t i = 0; i < encoded_length; ++i) { + if (encoded[i] == '+') encoded[i] = '-'; + else if (encoded[i] == '/') encoded[i] = '_'; + } + encoded[encoded_length] = '\0'; + snprintf(output, 64, "%s", encoded); + return ESP_OK; +} + +static esp_err_t http_event(esp_http_client_event_t *event) +{ + if (event->event_id == HTTP_EVENT_ON_CONNECTED) ++s_http_connection_count; + response_buffer_t *buffer = (response_buffer_t *)event->user_data; + if (event->event_id != HTTP_EVENT_ON_DATA || buffer == NULL || event->data_len <= 0) return ESP_OK; + if (buffer->length + event->data_len + 1 > buffer->capacity) return ESP_ERR_NO_MEM; + memcpy(buffer->data + buffer->length, event->data, event->data_len); + buffer->length += event->data_len; + buffer->data[buffer->length] = '\0'; + return ESP_OK; +} + +static esp_err_t platform_request_impl(const char *method, const char *path, const char *body, + const char *idempotency_key, char **response_out) +{ + s_last_error_code[0] = '\0'; + if (!platform_identity_is_configured()) return ESP_ERR_INVALID_STATE; + if (body == NULL) body = ""; + char url[384]; + snprintf(url, sizeof(url), "%s%s", PLATFORM_API_ORIGIN, path); + response_buffer_t response = { + .data = calloc(1, PLATFORM_RESPONSE_MAX), .capacity = PLATFORM_RESPONSE_MAX, + }; + if (response.data == NULL) return ESP_ERR_NO_MEM; + esp_http_client_config_t config = { + .url = url, + .event_handler = http_event, + .user_data = &response, + .timeout_ms = 15000, + .crt_bundle_attach = esp_crt_bundle_attach, + .buffer_size = 2048, + .buffer_size_tx = 2048, + .keep_alive_enable = true, + }; + if (!s_http_client) s_http_client = esp_http_client_init(&config); + esp_http_client_handle_t client = s_http_client; + if (client == NULL) { + free(response.data); + return ESP_ERR_NO_MEM; + } + esp_http_client_method_t verb = strcmp(method, "POST") == 0 ? HTTP_METHOD_POST : HTTP_METHOD_GET; + esp_err_t err = esp_http_client_set_url(client, url); + if (err == ESP_OK) err = esp_http_client_set_user_data(client, &response); + if (err == ESP_OK) err = esp_http_client_set_method(client, verb); + /* Reused handles must not inherit a preceding request's body or key. */ + if (err == ESP_OK) err = esp_http_client_set_post_field(client, body, strlen(body)); + if (!idempotency_key) (void)esp_http_client_delete_header(client, "Idempotency-Key"); + if (!body[0]) (void)esp_http_client_delete_header(client, "Content-Type"); + char timestamp[32], nonce[33], digest[65], signature[64]; + timestamp_now(timestamp); + random_hex(nonce, 16); + sha256_hex(body, digest); + if (err == ESP_OK) err = signature_header(method, path, timestamp, nonce, digest, signature); + if (err == ESP_OK) err = esp_http_client_set_header(client, "X-Device-Id", s_device_id); + if (err == ESP_OK) err = esp_http_client_set_header(client, "X-Device-Timestamp", timestamp); + if (err == ESP_OK) err = esp_http_client_set_header(client, "X-Device-Nonce", nonce); + if (err == ESP_OK) err = esp_http_client_set_header(client, "X-Device-Content-SHA256", digest); + if (err == ESP_OK) err = esp_http_client_set_header(client, "X-Device-Signature", signature); + if (err == ESP_OK) err = esp_http_client_set_header(client, "Accept", "application/json"); + if (err == ESP_OK && idempotency_key != NULL) err = esp_http_client_set_header(client, "Idempotency-Key", idempotency_key); + if (err == ESP_OK && body[0] != '\0') { + err = esp_http_client_set_header(client, "Content-Type", "application/json"); + } + TickType_t request_started = xTaskGetTickCount(); + unsigned connection_count = s_http_connection_count; + if (err == ESP_OK) err = esp_http_client_perform(client); + int status = esp_http_client_get_status_code(client); + ESP_LOGI(TAG, "PLATFORM_HTTP method=%s status=%d elapsed_ms=%u new_connection=%u", + method, status, (unsigned)((xTaskGetTickCount() - request_started) * portTICK_PERIOD_MS), + s_http_connection_count - connection_count); + (void)esp_http_client_set_user_data(client, NULL); + (void)esp_http_client_set_post_field(client, NULL, 0); + if (err != ESP_OK) { + /* An uncertain POST is not replayed automatically. Reconnect on the + * next request while retaining the existing event/idempotency rules. */ + esp_http_client_cleanup(client); + s_http_client = NULL; + } + if (err == ESP_OK && (status < 200 || status >= 300)) { + // Log only a machine-readable error code, never credentials or response bodies. + cJSON *root = cJSON_Parse(response.data); + cJSON *error = cJSON_GetObjectItemCaseSensitive(root, "error"); + cJSON *code = cJSON_GetObjectItemCaseSensitive(error, "code"); + if (cJSON_IsString(code) && code->valuestring) { + size_t length = strlen(code->valuestring); + if (length > 0 && length < sizeof(s_last_error_code) && + strspn(code->valuestring, "ABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789") == length) { + snprintf(s_last_error_code, sizeof(s_last_error_code), "%s", code->valuestring); + } + } + cJSON_Delete(root); + ESP_LOGE(TAG, "%s %s failed: HTTP %d", method, path, status); + ESP_LOGE(TAG, "Platform error code: %s", s_last_error_code[0] ? s_last_error_code : "UNKNOWN"); + err = ESP_FAIL; + } + if (err == ESP_OK && response_out != NULL) { + *response_out = response.data; + response.data = NULL; + } + free(response.data); + return err; +} + +static esp_err_t platform_request(const char *method, const char *path, const char *body, + const char *idempotency_key, char **response_out) +{ + if (!s_http_mutex) return ESP_ERR_INVALID_STATE; + xSemaphoreTake(s_http_mutex, portMAX_DELAY); + esp_err_t err = platform_request_impl(method, path, body, idempotency_key, response_out); + xSemaphoreGive(s_http_mutex); + return err; +} + +static cJSON *response_data(char *response, cJSON **root_out) +{ + cJSON *root = cJSON_Parse(response); + if (root == NULL) return NULL; + cJSON *data = cJSON_GetObjectItemCaseSensitive(root, "data"); + if (!cJSON_IsObject(data)) { + cJSON_Delete(root); + return NULL; + } + *root_out = root; + return data; +} + +static esp_err_t save_volc_credential(void) +{ + nvs_handle_t nvs; + esp_err_t err = nvs_open("platform", NVS_READWRITE, &nvs); + if (err != ESP_OK) return err; + if (err == ESP_OK) err = nvs_set_str(nvs, "instance_id", s_volc.instance_id); + if (err == ESP_OK) err = nvs_set_str(nvs, "product_key", s_volc.product_key); + if (err == ESP_OK) err = nvs_set_str(nvs, "device_name", s_volc.device_name); + if (err == ESP_OK) err = nvs_set_str(nvs, "volc_secret", s_volc.device_secret); + if (err == ESP_OK) err = nvs_commit(nvs); + nvs_close(nvs); + return err; +} + +esp_err_t platform_provision_volc_credential(void) +{ + char body[160]; + snprintf(body, sizeof(body), "{\"hardware_id\":\"%s\"}", s_hardware_id); + char key[96]; + snprintf(key, sizeof(key), "echoear-provision-%s", s_hardware_id); + char *response = NULL; + esp_err_t err = platform_request("POST", "/device/v3/hardware-agent-credentials:provision", body, key, &response); + if (err != ESP_OK) return err; + cJSON *root = NULL; + cJSON *data = response_data(response, &root); + if (data == NULL) { free(response); return ESP_FAIL; } + memset(&s_volc, 0, sizeof(s_volc)); + copy_json_string(data, "instance_id", s_volc.instance_id, sizeof(s_volc.instance_id)); + copy_json_string(data, "product_key", s_volc.product_key, sizeof(s_volc.product_key)); + copy_json_string(data, "device_name", s_volc.device_name, sizeof(s_volc.device_name)); + copy_json_string(data, "device_secret", s_volc.device_secret, sizeof(s_volc.device_secret)); + snprintf(s_volc.hardware_id, sizeof(s_volc.hardware_id), "%s", s_hardware_id); + cJSON_Delete(root); + free(response); + if (!s_volc.instance_id[0] || !s_volc.product_key[0] || !s_volc.device_name[0] || strlen(s_volc.device_secret) < 16) return ESP_FAIL; + ESP_LOGI(TAG, "provider credential received for device_name=%s", s_volc.device_name); + return save_volc_credential(); +} + +static esp_err_t refresh_config_revision(void) +{ + char *response = NULL; + esp_err_t err = platform_request("GET", "/device/v2/config", "", NULL, &response); + if (err != ESP_OK) return err; + cJSON *root = NULL; + cJSON *data = response_data(response, &root); + cJSON *revision = data ? cJSON_GetObjectItemCaseSensitive(data, "revision") : NULL; + if (cJSON_IsNumber(revision) && revision->valuedouble >= 1) s_config_revision = (int64_t)revision->valuedouble; + cJSON_Delete(root); + free(response); + return data == NULL ? ESP_FAIL : ESP_OK; +} + +static esp_err_t report_bootstrap(void) +{ + char timestamp[32], path[320]; + timestamp_now(timestamp); + snprintf(path, sizeof(path), + "/device/v2/bootstrap?firmware_version=%s&device_time=%s&board_rev=echoear&rtc_sdk_version=%s&codec=pcm16&sample_rate=16000&network=wifi", + PLATFORM_FIRMWARE_VERSION, timestamp, PLATFORM_HARDWARE_AGENT_SDK_VERSION); + char *response = NULL; + esp_err_t err = platform_request("GET", path, "", NULL, &response); + if (err != ESP_OK) return err; + cJSON *root = NULL; + cJSON *data = response_data(response, &root); + cJSON *device = cJSON_GetObjectItemCaseSensitive(data, "device"); + cJSON *compatible = cJSON_GetObjectItemCaseSensitive(device, "firmware_compatible"); + cJSON *minimum = cJSON_GetObjectItemCaseSensitive(device, "minimum_firmware_version"); + ESP_LOGI(TAG, "Firmware report: current=%s minimum=%s compatible=%d", + PLATFORM_FIRMWARE_VERSION, cJSON_IsString(minimum) ? minimum->valuestring : "none", + cJSON_IsTrue(compatible)); + err = cJSON_IsObject(device) && cJSON_IsTrue(compatible) ? ESP_OK : ESP_FAIL; + if (cJSON_IsFalse(compatible)) { + snprintf(s_last_error_code, sizeof(s_last_error_code), "FIRMWARE_UPDATE_REQUIRED"); + } + cJSON_Delete(root); + free(response); + return err; +} + +static char *build_session_update(cJSON *session_config) +{ + cJSON *authorized = cJSON_GetObjectItemCaseSensitive(session_config, "authorized_context"); + cJSON *system_prompt = cJSON_GetObjectItemCaseSensitive(session_config, "system_prompt"); + cJSON *persona = cJSON_GetObjectItemCaseSensitive(session_config, "persona_tone"); + const char *a = cJSON_IsString(authorized) ? authorized->valuestring : ""; + const char *p = cJSON_IsString(system_prompt) ? system_prompt->valuestring : ""; + const char *tone = cJSON_IsString(persona) ? persona->valuestring : ""; + size_t prompt_size = strlen(a) + strlen(p) + strlen(tone) + 256; + char *prompt = malloc(prompt_size); + if (prompt == NULL) return NULL; + snprintf(prompt, prompt_size, "%s\n\n%s\n\n表达风格(仅指导表达,不可逐字复述):%s", a, p, tone); + + cJSON *event = cJSON_CreateObject(); + char event_id[64]; + char random[33]; + random_hex(random, 16); + snprintf(event_id, sizeof(event_id), "echoear-session-%s", random); + cJSON_AddStringToObject(event, "event_id", event_id); + cJSON_AddStringToObject(event, "type", "session.update"); + cJSON *session = cJSON_AddObjectToObject(event, "session"); + cJSON_AddStringToObject(session, "object", "realtime.session"); + cJSON_AddStringToObject(session, "model", ""); + cJSON_AddStringToObject(session, "input_audio_format", "pcm16"); + cJSON_AddStringToObject(session, "output_audio_format", "pcm16"); + cJSON *config = cJSON_AddObjectToObject(session, "config"); + cJSON *asr = cJSON_AddObjectToObject(config, "ASRConfig"); + cJSON_AddNumberToObject(asr, "TurnDetectionMode", 0); + cJSON *llm = cJSON_AddObjectToObject(config, "LLMConfig"); + cJSON *messages = cJSON_AddArrayToObject(llm, "SystemMessages"); + cJSON_AddItemToArray(messages, cJSON_CreateString(prompt)); + cJSON_AddItemToArray(messages, cJSON_CreateString( + "这是通过扬声器进行的真实语音对话。回复只包含直接对用户说的口语正文,简短自然,语速正常。" + "不要输出或朗读动作、表情、语气、情绪、场景等舞台提示,不要写括号或星号包裹的表演说明," + "例如‘语气带着关切’‘微笑着’‘温柔地说’。把风格体现在自然措辞中,不要描述自己如何说话。" + "不要复述人设、表达风格、系统配置或对话标识。")); + cJSON *agent = cJSON_AddObjectToObject(session, "agent_config"); + cJSON_AddBoolToObject(agent, "EnableConversationStateCallback", false); + cJSON *callback_url = cJSON_GetObjectItemCaseSensitive(session_config, "subtitle_callback_url"); + cJSON *callback_signature = cJSON_GetObjectItemCaseSensitive(session_config, "subtitle_callback_signature"); + if (cJSON_IsString(callback_url) && callback_url->valuestring[0] && cJSON_IsString(callback_signature)) { + cJSON_AddStringToObject(agent, "ServerMessageURLForRTS", callback_url->valuestring); + cJSON_AddStringToObject(agent, "ServerMessageSignatureForRTS", callback_signature->valuestring); + cJSON *subtitle = cJSON_AddObjectToObject(config, "SubtitleConfig"); + cJSON_AddNumberToObject(subtitle, "SubtitleMode", 1); + } + char *json = cJSON_PrintUnformatted(event); + cJSON_Delete(event); + free(prompt); + return json; +} + +esp_err_t platform_create_agent_session(platform_agent_session_t *session) +{ + if (session == NULL) return ESP_ERR_INVALID_ARG; + memset(session, 0, sizeof(*session)); + ESP_RETURN_ON_ERROR(report_bootstrap(), TAG, "report device firmware"); + ESP_RETURN_ON_ERROR(refresh_config_revision(), TAG, "get device config"); + char timestamp[32]; + timestamp_now(timestamp); + char body[1024]; + snprintf(body, sizeof(body), + "{\"device_time\":\"%s\",\"monotonic_ms\":0,\"capabilities\":{" + "\"hardware_agent\":true,\"sdk_version\":\"%s\",\"transports\":[\"low_load\"]," + "\"input_audio_format\":\"pcm_s16le_16000_mono\",\"output_audio_format\":\"pcm_s16le_16000_mono\"," + "\"server_event_callback\":false,\"native_subtitle_callback\":true,\"transcript_upload\":true,\"dynamic_session_config\":true," + "\"credential_rotation\":true,\"near_pcm_recording\":false,\"far_pcm_recording\":false}," + "\"network\":{\"type\":\"wifi\"},\"requested_goal\":\"open_conversation\",\"config_revision\":%lld}", + timestamp, PLATFORM_HARDWARE_AGENT_SDK_VERSION, (long long)s_config_revision); + char random[33], key[64]; + random_hex(random, 16); + snprintf(key, sizeof(key), "echoear-session-%s", random); + char *response = NULL; + esp_err_t err = platform_request("POST", "/device/v3/agent-sessions", body, key, &response); + if (err != ESP_OK) return err; + cJSON *root = NULL; + cJSON *data = response_data(response, &root); + cJSON *runtime = data ? cJSON_GetObjectItemCaseSensitive(data, "runtime") : NULL; + cJSON *session_config = data ? cJSON_GetObjectItemCaseSensitive(data, "session_config") : NULL; + cJSON *limits = data ? cJSON_GetObjectItemCaseSensitive(data, "session") : NULL; + cJSON *interval = limits ? cJSON_GetObjectItemCaseSensitive(limits, "heartbeat_interval_sec") : NULL; + s_heartbeat_interval_ms = cJSON_IsNumber(interval) && interval->valueint >= 1 && interval->valueint <= 60 + ? (uint32_t)interval->valueint * 1000 : 15000; + if (!cJSON_IsObject(runtime) || !cJSON_IsObject(session_config)) { + cJSON_Delete(root); free(response); return ESP_FAIL; + } + copy_json_string(data, "conversation_id", session->conversation_id, sizeof(session->conversation_id)); + copy_json_string(runtime, "bot_id", session->bot_id, sizeof(session->bot_id)); + copy_json_string(runtime, "provider_session_ref", session->provider_session_ref, sizeof(session->provider_session_ref)); + copy_json_string(runtime, "session_nonce", session->session_nonce, sizeof(session->session_nonce)); + copy_json_string(runtime, "config_digest", session->config_digest, sizeof(session->config_digest)); + session->session_update_json = build_session_update(session_config); + cJSON *subtitle_source = cJSON_GetObjectItemCaseSensitive(session_config, "subtitle_source"); + session->native_subtitle_primary = cJSON_IsString(subtitle_source) && strcmp(subtitle_source->valuestring, "provider") == 0; + cJSON_Delete(root); + free(response); + if (!session->conversation_id[0] || !session->bot_id[0] || !session->provider_session_ref[0] || + !session->session_nonce[0] || !session->config_digest[0] || session->session_update_json == NULL) { + platform_agent_session_free(session); + return ESP_FAIL; + } + ESP_LOGI(TAG, "platform session ready conversation_id=%s", session->conversation_id); + return persist_pending_session(session, s_event_sequence); +} + +static esp_err_t report_event(const platform_agent_session_t *session, const char *event_type, + const char *provider_connection_ref, const char *error_code) +{ + if (session == NULL || event_type == NULL) return ESP_ERR_INVALID_ARG; + char timestamp[32], random[33], event_id[64], path[256]; + timestamp_now(timestamp); + random_hex(random, 16); + snprintf(event_id, sizeof(event_id), "echoear-event-%s", random); + snprintf(path, sizeof(path), "/device/v3/agent-sessions/%s/events", session->conversation_id); + cJSON *body = cJSON_CreateObject(); + cJSON_AddStringToObject(body, "event_id", event_id); + cJSON_AddNumberToObject(body, "sequence_no", ++s_event_sequence); + esp_err_t saved = persist_pending_session(session, s_event_sequence); + if (saved != ESP_OK) { cJSON_Delete(body); return saved; } + cJSON_AddStringToObject(body, "event_type", event_type); + cJSON_AddStringToObject(body, "occurred_at", timestamp); + cJSON_AddStringToObject(body, "provider_session_ref", session->provider_session_ref); + cJSON_AddStringToObject(body, "session_nonce", session->session_nonce); + if (strcmp(event_type, "session_started") == 0) { + cJSON_AddStringToObject(body, "provider_connection_ref", provider_connection_ref); + cJSON_AddStringToObject(body, "bot_id", session->bot_id); + cJSON_AddStringToObject(body, "transport", "low_load"); + cJSON_AddStringToObject(body, "config_digest", session->config_digest); + cJSON_AddBoolToObject(body, "dynamic_config_applied", true); + } + if (error_code != NULL && error_code[0]) cJSON_AddStringToObject(body, "error_code", error_code); + char *json = cJSON_PrintUnformatted(body); + cJSON_Delete(body); + if (json == NULL) return ESP_ERR_NO_MEM; + char *response = NULL; + esp_err_t err = platform_request("POST", path, json, NULL, &response); + free(json); + if (err == ESP_OK && strcmp(event_type, "session_updated") == 0) { + cJSON *root = NULL; + cJSON *data = response_data(response, &root); + cJSON *action = data ? cJSON_GetObjectItemCaseSensitive(data, "action") : NULL; + bool stop = cJSON_IsString(action) && strcmp(action->valuestring, "stop_session") == 0; + ESP_LOGI(TAG, "PLATFORM_HEARTBEAT stop=%d", stop); + if (!data) err = ESP_FAIL; + else if (stop) err = ESP_ERR_INVALID_STATE; + cJSON_Delete(root); + } + free(response); + return err; +} + +esp_err_t platform_report_session_started(const platform_agent_session_t *session, + const char *provider_connection_ref) +{ + return report_event(session, "session_started", provider_connection_ref, NULL); +} + +esp_err_t platform_report_session_event(const platform_agent_session_t *session, + const char *event_type, const char *error_code) +{ + return report_event(session, event_type, NULL, error_code); +} + +void platform_agent_session_free(platform_agent_session_t *session) +{ + if (session == NULL) return; + free(session->session_update_json); + memset(session, 0, sizeof(*session)); +} + +const char *platform_runtime_instance_id(void) { return s_volc.instance_id; } +const char *platform_runtime_product_key(void) { return s_volc.product_key; } +const char *platform_runtime_device_name(void) { return s_volc.device_name; } +const char *platform_runtime_device_secret(void) { return s_volc.device_secret; } + +esp_err_t platform_conversation_begin(void) +{ + platform_agent_session_free(&s_active_session); + ESP_RETURN_ON_ERROR(recover_pending_session(), TAG, "recover interrupted session"); + s_event_sequence = 0; + s_turn_sequence = 0; + return platform_create_agent_session(&s_active_session); +} + +const char *platform_conversation_bot_id(void) { return s_active_session.bot_id; } +const char *platform_conversation_update_json(void) { return s_active_session.session_update_json; } + +esp_err_t platform_conversation_started(const char *provider_connection_ref) +{ + if (provider_connection_ref == NULL || provider_connection_ref[0] == '\0') return ESP_ERR_INVALID_ARG; + esp_err_t err = platform_report_session_started(&s_active_session, provider_connection_ref); + s_heartbeat_due = xTaskGetTickCount() + pdMS_TO_TICKS(s_heartbeat_interval_ms); + return err; +} + +int platform_conversation_poll(void) +{ + if (!s_active_session.conversation_id[0]) return 1; + TickType_t now = xTaskGetTickCount(); + if ((int32_t)(now - s_heartbeat_due) < 0) return 0; + esp_err_t err = platform_report_session_event(&s_active_session, "session_updated", NULL); + s_heartbeat_due = xTaskGetTickCount() + pdMS_TO_TICKS(err == ESP_OK ? s_heartbeat_interval_ms : 5000); + if (err == ESP_ERR_INVALID_STATE) return 1; + if (err != ESP_OK) ESP_LOGW(TAG, "PLATFORM_HEARTBEAT failed=%s", esp_err_to_name(err)); + return 0; +} + +esp_err_t platform_conversation_turn(const char *speaker, const char *source_id, const char *text) +{ + if (s_active_session.native_subtitle_primary) return ESP_OK; + if (speaker == NULL || text == NULL || text[0] == '\0' || s_active_session.conversation_id[0] == '\0') return ESP_ERR_INVALID_ARG; + char timestamp[32], path[256], key[96], random[33]; + timestamp_now(timestamp); + random_hex(random, 16); + snprintf(path, sizeof(path), "/device/v3/agent-sessions/%s/turns:batch", s_active_session.conversation_id); + snprintf(key, sizeof(key), "echoear-turn-%s", random); + cJSON *payload = cJSON_CreateObject(); + cJSON *turns = cJSON_AddArrayToObject(payload, "turns"); + cJSON *turn = cJSON_CreateObject(); + cJSON_AddItemToArray(turns, turn); + char turn_key[256]; + snprintf(turn_key, sizeof(turn_key), "echoear-%s-%s", speaker, source_id && source_id[0] ? source_id : random); + cJSON_AddStringToObject(turn, "turn_key", turn_key); + cJSON_AddNumberToObject(turn, "sequence_no", ++s_turn_sequence); + cJSON_AddStringToObject(turn, "speaker", speaker); + cJSON_AddStringToObject(turn, "start_at", timestamp); + cJSON_AddStringToObject(turn, "end_at", timestamp); + cJSON_AddStringToObject(turn, "final_text", text); + cJSON_AddBoolToObject(turn, "interrupted", false); + char *json = cJSON_PrintUnformatted(payload); + cJSON_Delete(payload); + if (json == NULL) return ESP_ERR_NO_MEM; + char *response = NULL; + esp_err_t err = platform_request("POST", path, json, key, &response); + free(json); + if (err == ESP_OK) { + cJSON *root = NULL; + cJSON *data = response_data(response, &root); + cJSON *accepted = data ? cJSON_GetObjectItemCaseSensitive(data, "accepted_count") : NULL; + cJSON *duplicate = data ? cJSON_GetObjectItemCaseSensitive(data, "duplicate_count") : NULL; + if (!cJSON_IsNumber(accepted) || !cJSON_IsNumber(duplicate) || accepted->valueint + duplicate->valueint != 1) { + err = ESP_FAIL; + } else { + ESP_LOGI(TAG, "PLATFORM_TURN speaker=%s sequence=%lld accepted=%d duplicate=%d", + speaker, (long long)s_turn_sequence, accepted->valueint, duplicate->valueint); + } + cJSON_Delete(root); + } + if (err != ESP_OK) ESP_LOGE(TAG, "PLATFORM_TURN upload failed sequence=%lld", (long long)s_turn_sequence); + free(response); + return err; +} + +void platform_conversation_finish(bool failed, const char *error_code) +{ + if (s_active_session.conversation_id[0]) { + esp_err_t err = platform_report_session_event(&s_active_session, + failed ? "session_failed" : "session_ended", + failed ? error_code : NULL); + if (err == ESP_OK) (void)persist_pending_session(NULL, 0); + } + platform_agent_session_free(&s_active_session); +} diff --git a/firmware/echoear-platform-agent/main/platform_client.h b/firmware/echoear-platform-agent/main/platform_client.h new file mode 100644 index 0000000..b3c94b1 --- /dev/null +++ b/firmware/echoear-platform-agent/main/platform_client.h @@ -0,0 +1,63 @@ +#pragma once + +#include +#include +#include "esp_err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define PLATFORM_DEVICE_ID_MAX 37 +#define PLATFORM_DEVICE_SECRET_MAX 129 +#define PLATFORM_HARDWARE_ID_MAX 65 + +typedef struct { + char instance_id[201]; + char product_key[201]; + char device_name[129]; + char device_secret[257]; + char hardware_id[PLATFORM_HARDWARE_ID_MAX]; +} platform_volc_credential_t; + +typedef struct { + char conversation_id[37]; + char bot_id[201]; + char provider_session_ref[201]; + char session_nonce[257]; + char config_digest[65]; + char *session_update_json; + bool native_subtitle_primary; +} platform_agent_session_t; + +esp_err_t platform_identity_save(const char *device_id, const char *device_secret); +bool platform_identity_is_configured(void); +const char *platform_last_error_code(void); +esp_err_t platform_client_init(void); +const char *platform_hardware_id(void); +const platform_volc_credential_t *platform_volc_credential(void); +esp_err_t platform_provision_volc_credential(void); +esp_err_t platform_create_agent_session(platform_agent_session_t *session); +esp_err_t platform_report_session_started(const platform_agent_session_t *session, + const char *provider_connection_ref); +esp_err_t platform_report_session_event(const platform_agent_session_t *session, + const char *event_type, + const char *error_code); +void platform_agent_session_free(platform_agent_session_t *session); + +/* Link-safe hooks used by the vendor application component. */ +const char *platform_runtime_instance_id(void); +const char *platform_runtime_product_key(void); +const char *platform_runtime_device_name(void); +const char *platform_runtime_device_secret(void); +esp_err_t platform_conversation_begin(void); +const char *platform_conversation_bot_id(void); +const char *platform_conversation_update_json(void); +esp_err_t platform_conversation_started(const char *provider_connection_ref); +int platform_conversation_poll(void); +esp_err_t platform_conversation_turn(const char *speaker, const char *source_id, const char *text); +void platform_conversation_finish(bool failed, const char *error_code); + +#ifdef __cplusplus +} +#endif diff --git a/firmware/echoear-platform-agent/partitions.csv b/firmware/echoear-platform-agent/partitions.csv new file mode 100644 index 0000000..a9897f7 --- /dev/null +++ b/firmware/echoear-platform-agent/partitions.csv @@ -0,0 +1,9 @@ +# Name, Type, SubType, Offset, Size, Flags +# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap +nvs, data, nvs, 0x9000, 0x6000, +phy_init, data, phy, 0xf000, 0x1000, +factory, app, factory, 0x10000, 5M, +model, data, spiffs, , 4152K, +coredump, data, coredump,, 64K +storage, data, littlefs, , 2M, +eaf, data, spiffs, , 3200K, diff --git a/firmware/echoear-platform-agent/sdkconfig b/firmware/echoear-platform-agent/sdkconfig new file mode 100644 index 0000000..2cd9127 --- /dev/null +++ b/firmware/echoear-platform-agent/sdkconfig @@ -0,0 +1,3673 @@ +# +# Automatically generated file. DO NOT EDIT. +# Espressif IoT Development Framework (ESP-IDF) 5.5.1 Project Configuration +# +CONFIG_SOC_ADC_SUPPORTED=y +CONFIG_SOC_UART_SUPPORTED=y +CONFIG_SOC_PCNT_SUPPORTED=y +CONFIG_SOC_PHY_SUPPORTED=y +CONFIG_SOC_WIFI_SUPPORTED=y +CONFIG_SOC_TWAI_SUPPORTED=y +CONFIG_SOC_GDMA_SUPPORTED=y +CONFIG_SOC_UHCI_SUPPORTED=y +CONFIG_SOC_AHB_GDMA_SUPPORTED=y +CONFIG_SOC_GPTIMER_SUPPORTED=y +CONFIG_SOC_LCDCAM_SUPPORTED=y +CONFIG_SOC_LCDCAM_CAM_SUPPORTED=y +CONFIG_SOC_LCDCAM_I80_LCD_SUPPORTED=y +CONFIG_SOC_LCDCAM_RGB_LCD_SUPPORTED=y +CONFIG_SOC_MCPWM_SUPPORTED=y +CONFIG_SOC_DEDICATED_GPIO_SUPPORTED=y +CONFIG_SOC_CACHE_SUPPORT_WRAP=y +CONFIG_SOC_ULP_SUPPORTED=y +CONFIG_SOC_ULP_FSM_SUPPORTED=y +CONFIG_SOC_RISCV_COPROC_SUPPORTED=y +CONFIG_SOC_BT_SUPPORTED=y +CONFIG_SOC_USB_OTG_SUPPORTED=y +CONFIG_SOC_USB_SERIAL_JTAG_SUPPORTED=y +CONFIG_SOC_CCOMP_TIMER_SUPPORTED=y +CONFIG_SOC_ASYNC_MEMCPY_SUPPORTED=y +CONFIG_SOC_SUPPORTS_SECURE_DL_MODE=y +CONFIG_SOC_EFUSE_KEY_PURPOSE_FIELD=y +CONFIG_SOC_EFUSE_SUPPORTED=y +CONFIG_SOC_SDMMC_HOST_SUPPORTED=y +CONFIG_SOC_RTC_FAST_MEM_SUPPORTED=y +CONFIG_SOC_RTC_SLOW_MEM_SUPPORTED=y +CONFIG_SOC_RTC_MEM_SUPPORTED=y +CONFIG_SOC_PSRAM_DMA_CAPABLE=y +CONFIG_SOC_XT_WDT_SUPPORTED=y +CONFIG_SOC_I2S_SUPPORTED=y +CONFIG_SOC_RMT_SUPPORTED=y +CONFIG_SOC_SDM_SUPPORTED=y +CONFIG_SOC_GPSPI_SUPPORTED=y +CONFIG_SOC_LEDC_SUPPORTED=y +CONFIG_SOC_I2C_SUPPORTED=y +CONFIG_SOC_SYSTIMER_SUPPORTED=y +CONFIG_SOC_SUPPORT_COEXISTENCE=y +CONFIG_SOC_TEMP_SENSOR_SUPPORTED=y +CONFIG_SOC_AES_SUPPORTED=y +CONFIG_SOC_MPI_SUPPORTED=y +CONFIG_SOC_SHA_SUPPORTED=y +CONFIG_SOC_HMAC_SUPPORTED=y +CONFIG_SOC_DIG_SIGN_SUPPORTED=y +CONFIG_SOC_FLASH_ENC_SUPPORTED=y +CONFIG_SOC_SECURE_BOOT_SUPPORTED=y +CONFIG_SOC_MEMPROT_SUPPORTED=y +CONFIG_SOC_TOUCH_SENSOR_SUPPORTED=y +CONFIG_SOC_BOD_SUPPORTED=y +CONFIG_SOC_CLK_TREE_SUPPORTED=y +CONFIG_SOC_MPU_SUPPORTED=y +CONFIG_SOC_WDT_SUPPORTED=y +CONFIG_SOC_SPI_FLASH_SUPPORTED=y +CONFIG_SOC_RNG_SUPPORTED=y +CONFIG_SOC_LIGHT_SLEEP_SUPPORTED=y +CONFIG_SOC_DEEP_SLEEP_SUPPORTED=y +CONFIG_SOC_LP_PERIPH_SHARE_INTERRUPT=y +CONFIG_SOC_PM_SUPPORTED=y +CONFIG_SOC_SIMD_INSTRUCTION_SUPPORTED=y +CONFIG_SOC_XTAL_SUPPORT_40M=y +CONFIG_SOC_APPCPU_HAS_CLOCK_GATING_BUG=y +CONFIG_SOC_ADC_RTC_CTRL_SUPPORTED=y +CONFIG_SOC_ADC_DIG_CTRL_SUPPORTED=y +CONFIG_SOC_ADC_ARBITER_SUPPORTED=y +CONFIG_SOC_ADC_DIG_IIR_FILTER_SUPPORTED=y +CONFIG_SOC_ADC_MONITOR_SUPPORTED=y +CONFIG_SOC_ADC_DMA_SUPPORTED=y +CONFIG_SOC_ADC_PERIPH_NUM=2 +CONFIG_SOC_ADC_MAX_CHANNEL_NUM=10 +CONFIG_SOC_ADC_ATTEN_NUM=4 +CONFIG_SOC_ADC_DIGI_CONTROLLER_NUM=2 +CONFIG_SOC_ADC_PATT_LEN_MAX=24 +CONFIG_SOC_ADC_DIGI_MIN_BITWIDTH=12 +CONFIG_SOC_ADC_DIGI_MAX_BITWIDTH=12 +CONFIG_SOC_ADC_DIGI_RESULT_BYTES=4 +CONFIG_SOC_ADC_DIGI_DATA_BYTES_PER_CONV=4 +CONFIG_SOC_ADC_DIGI_IIR_FILTER_NUM=2 +CONFIG_SOC_ADC_DIGI_MONITOR_NUM=2 +CONFIG_SOC_ADC_SAMPLE_FREQ_THRES_HIGH=83333 +CONFIG_SOC_ADC_SAMPLE_FREQ_THRES_LOW=611 +CONFIG_SOC_ADC_RTC_MIN_BITWIDTH=12 +CONFIG_SOC_ADC_RTC_MAX_BITWIDTH=12 +CONFIG_SOC_ADC_CALIBRATION_V1_SUPPORTED=y +CONFIG_SOC_ADC_SELF_HW_CALI_SUPPORTED=y +CONFIG_SOC_ADC_SHARED_POWER=y +CONFIG_SOC_APB_BACKUP_DMA=y +CONFIG_SOC_BROWNOUT_RESET_SUPPORTED=y +CONFIG_SOC_CACHE_WRITEBACK_SUPPORTED=y +CONFIG_SOC_CACHE_FREEZE_SUPPORTED=y +CONFIG_SOC_CACHE_ACS_INVALID_STATE_ON_PANIC=y +CONFIG_SOC_CPU_CORES_NUM=2 +CONFIG_SOC_CPU_INTR_NUM=32 +CONFIG_SOC_CPU_HAS_FPU=y +CONFIG_SOC_HP_CPU_HAS_MULTIPLE_CORES=y +CONFIG_SOC_CPU_BREAKPOINTS_NUM=2 +CONFIG_SOC_CPU_WATCHPOINTS_NUM=2 +CONFIG_SOC_CPU_WATCHPOINT_MAX_REGION_SIZE=0x40 +CONFIG_SOC_SIMD_PREFERRED_DATA_ALIGNMENT=16 +CONFIG_SOC_DS_SIGNATURE_MAX_BIT_LEN=4096 +CONFIG_SOC_DS_KEY_PARAM_MD_IV_LENGTH=16 +CONFIG_SOC_DS_KEY_CHECK_MAX_WAIT_US=1100 +CONFIG_SOC_AHB_GDMA_VERSION=1 +CONFIG_SOC_GDMA_NUM_GROUPS_MAX=1 +CONFIG_SOC_GDMA_PAIRS_PER_GROUP=5 +CONFIG_SOC_GDMA_PAIRS_PER_GROUP_MAX=5 +CONFIG_SOC_AHB_GDMA_SUPPORT_PSRAM=y +CONFIG_SOC_GPIO_PORT=1 +CONFIG_SOC_GPIO_PIN_COUNT=49 +CONFIG_SOC_GPIO_SUPPORT_PIN_GLITCH_FILTER=y +CONFIG_SOC_GPIO_FILTER_CLK_SUPPORT_APB=y +CONFIG_SOC_GPIO_SUPPORT_RTC_INDEPENDENT=y +CONFIG_SOC_GPIO_SUPPORT_FORCE_HOLD=y +CONFIG_SOC_GPIO_VALID_GPIO_MASK=0x1FFFFFFFFFFFF +CONFIG_SOC_GPIO_IN_RANGE_MAX=48 +CONFIG_SOC_GPIO_OUT_RANGE_MAX=48 +CONFIG_SOC_GPIO_VALID_DIGITAL_IO_PAD_MASK=0x0001FFFFFC000000 +CONFIG_SOC_GPIO_CLOCKOUT_BY_IO_MUX=y +CONFIG_SOC_GPIO_CLOCKOUT_CHANNEL_NUM=3 +CONFIG_SOC_GPIO_SUPPORT_HOLD_IO_IN_DSLP=y +CONFIG_SOC_DEDIC_GPIO_OUT_CHANNELS_NUM=8 +CONFIG_SOC_DEDIC_GPIO_IN_CHANNELS_NUM=8 +CONFIG_SOC_DEDIC_GPIO_OUT_AUTO_ENABLE=y +CONFIG_SOC_I2C_NUM=2 +CONFIG_SOC_HP_I2C_NUM=2 +CONFIG_SOC_I2C_FIFO_LEN=32 +CONFIG_SOC_I2C_CMD_REG_NUM=8 +CONFIG_SOC_I2C_SUPPORT_SLAVE=y +CONFIG_SOC_I2C_SUPPORT_HW_CLR_BUS=y +CONFIG_SOC_I2C_SUPPORT_XTAL=y +CONFIG_SOC_I2C_SUPPORT_RTC=y +CONFIG_SOC_I2C_SUPPORT_10BIT_ADDR=y +CONFIG_SOC_I2C_SLAVE_SUPPORT_BROADCAST=y +CONFIG_SOC_I2C_SLAVE_SUPPORT_I2CRAM_ACCESS=y +CONFIG_SOC_I2C_SLAVE_CAN_GET_STRETCH_CAUSE=y +CONFIG_SOC_I2S_NUM=2 +CONFIG_SOC_I2S_HW_VERSION_2=y +CONFIG_SOC_I2S_SUPPORTS_XTAL=y +CONFIG_SOC_I2S_SUPPORTS_PLL_F160M=y +CONFIG_SOC_I2S_SUPPORTS_PCM=y +CONFIG_SOC_I2S_SUPPORTS_PDM=y +CONFIG_SOC_I2S_SUPPORTS_PDM_TX=y +CONFIG_SOC_I2S_SUPPORTS_PCM2PDM=y +CONFIG_SOC_I2S_SUPPORTS_PDM_RX=y +CONFIG_SOC_I2S_SUPPORTS_PDM2PCM=y +CONFIG_SOC_I2S_PDM_MAX_TX_LINES=2 +CONFIG_SOC_I2S_PDM_MAX_RX_LINES=4 +CONFIG_SOC_I2S_SUPPORTS_TDM=y +CONFIG_SOC_LEDC_SUPPORT_APB_CLOCK=y +CONFIG_SOC_LEDC_SUPPORT_XTAL_CLOCK=y +CONFIG_SOC_LEDC_TIMER_NUM=4 +CONFIG_SOC_LEDC_CHANNEL_NUM=8 +CONFIG_SOC_LEDC_TIMER_BIT_WIDTH=14 +CONFIG_SOC_LEDC_SUPPORT_FADE_STOP=y +CONFIG_SOC_MCPWM_GROUPS=2 +CONFIG_SOC_MCPWM_TIMERS_PER_GROUP=3 +CONFIG_SOC_MCPWM_OPERATORS_PER_GROUP=3 +CONFIG_SOC_MCPWM_COMPARATORS_PER_OPERATOR=2 +CONFIG_SOC_MCPWM_GENERATORS_PER_OPERATOR=2 +CONFIG_SOC_MCPWM_TRIGGERS_PER_OPERATOR=2 +CONFIG_SOC_MCPWM_GPIO_FAULTS_PER_GROUP=3 +CONFIG_SOC_MCPWM_CAPTURE_TIMERS_PER_GROUP=y +CONFIG_SOC_MCPWM_CAPTURE_CHANNELS_PER_TIMER=3 +CONFIG_SOC_MCPWM_GPIO_SYNCHROS_PER_GROUP=3 +CONFIG_SOC_MCPWM_SWSYNC_CAN_PROPAGATE=y +CONFIG_SOC_MMU_LINEAR_ADDRESS_REGION_NUM=1 +CONFIG_SOC_MMU_PERIPH_NUM=1 +CONFIG_SOC_MPU_MIN_REGION_SIZE=0x20000000 +CONFIG_SOC_MPU_REGIONS_MAX_NUM=8 +CONFIG_SOC_PCNT_GROUPS=1 +CONFIG_SOC_PCNT_UNITS_PER_GROUP=4 +CONFIG_SOC_PCNT_CHANNELS_PER_UNIT=2 +CONFIG_SOC_PCNT_THRES_POINT_PER_UNIT=2 +CONFIG_SOC_RMT_GROUPS=1 +CONFIG_SOC_RMT_TX_CANDIDATES_PER_GROUP=4 +CONFIG_SOC_RMT_RX_CANDIDATES_PER_GROUP=4 +CONFIG_SOC_RMT_CHANNELS_PER_GROUP=8 +CONFIG_SOC_RMT_MEM_WORDS_PER_CHANNEL=48 +CONFIG_SOC_RMT_SUPPORT_RX_PINGPONG=y +CONFIG_SOC_RMT_SUPPORT_RX_DEMODULATION=y +CONFIG_SOC_RMT_SUPPORT_TX_ASYNC_STOP=y +CONFIG_SOC_RMT_SUPPORT_TX_LOOP_COUNT=y +CONFIG_SOC_RMT_SUPPORT_TX_LOOP_AUTO_STOP=y +CONFIG_SOC_RMT_SUPPORT_TX_SYNCHRO=y +CONFIG_SOC_RMT_SUPPORT_TX_CARRIER_DATA_ONLY=y +CONFIG_SOC_RMT_SUPPORT_XTAL=y +CONFIG_SOC_RMT_SUPPORT_RC_FAST=y +CONFIG_SOC_RMT_SUPPORT_APB=y +CONFIG_SOC_RMT_SUPPORT_DMA=y +CONFIG_SOC_LCD_I80_SUPPORTED=y +CONFIG_SOC_LCD_RGB_SUPPORTED=y +CONFIG_SOC_LCD_I80_BUSES=1 +CONFIG_SOC_LCD_RGB_PANELS=1 +CONFIG_SOC_LCD_I80_BUS_WIDTH=16 +CONFIG_SOC_LCD_RGB_DATA_WIDTH=16 +CONFIG_SOC_LCD_SUPPORT_RGB_YUV_CONV=y +CONFIG_SOC_LCDCAM_I80_NUM_BUSES=1 +CONFIG_SOC_LCDCAM_I80_BUS_WIDTH=16 +CONFIG_SOC_LCDCAM_RGB_NUM_PANELS=1 +CONFIG_SOC_LCDCAM_RGB_DATA_WIDTH=16 +CONFIG_SOC_RTC_CNTL_CPU_PD_DMA_BUS_WIDTH=128 +CONFIG_SOC_RTC_CNTL_CPU_PD_REG_FILE_NUM=549 +CONFIG_SOC_RTC_CNTL_TAGMEM_PD_DMA_BUS_WIDTH=128 +CONFIG_SOC_RTCIO_PIN_COUNT=22 +CONFIG_SOC_RTCIO_INPUT_OUTPUT_SUPPORTED=y +CONFIG_SOC_RTCIO_HOLD_SUPPORTED=y +CONFIG_SOC_RTCIO_WAKE_SUPPORTED=y +CONFIG_SOC_LP_IO_CLOCK_IS_INDEPENDENT=y +CONFIG_SOC_SDM_GROUPS=1 +CONFIG_SOC_SDM_CHANNELS_PER_GROUP=8 +CONFIG_SOC_SDM_CLK_SUPPORT_APB=y +CONFIG_SOC_SPI_PERIPH_NUM=3 +CONFIG_SOC_SPI_MAX_CS_NUM=6 +CONFIG_SOC_SPI_MAXIMUM_BUFFER_SIZE=64 +CONFIG_SOC_SPI_SUPPORT_DDRCLK=y +CONFIG_SOC_SPI_SLAVE_SUPPORT_SEG_TRANS=y +CONFIG_SOC_SPI_SUPPORT_CD_SIG=y +CONFIG_SOC_SPI_SUPPORT_CONTINUOUS_TRANS=y +CONFIG_SOC_SPI_SUPPORT_SLAVE_HD_VER2=y +CONFIG_SOC_SPI_SUPPORT_CLK_APB=y +CONFIG_SOC_SPI_SUPPORT_CLK_XTAL=y +CONFIG_SOC_SPI_PERIPH_SUPPORT_CONTROL_DUMMY_OUT=y +CONFIG_SOC_MEMSPI_IS_INDEPENDENT=y +CONFIG_SOC_SPI_MAX_PRE_DIVIDER=16 +CONFIG_SOC_SPI_SUPPORT_OCT=y +CONFIG_SOC_SPI_SCT_SUPPORTED=y +CONFIG_SOC_SPI_SCT_REG_NUM=14 +CONFIG_SOC_SPI_SCT_BUFFER_NUM_MAX=y +CONFIG_SOC_SPI_SCT_CONF_BITLEN_MAX=0x3FFFA +CONFIG_SOC_MEMSPI_SRC_FREQ_120M_SUPPORTED=y +CONFIG_SOC_MEMSPI_SRC_FREQ_80M_SUPPORTED=y +CONFIG_SOC_MEMSPI_SRC_FREQ_40M_SUPPORTED=y +CONFIG_SOC_MEMSPI_SRC_FREQ_20M_SUPPORTED=y +CONFIG_SOC_SPIRAM_SUPPORTED=y +CONFIG_SOC_SPIRAM_XIP_SUPPORTED=y +CONFIG_SOC_SYSTIMER_COUNTER_NUM=2 +CONFIG_SOC_SYSTIMER_ALARM_NUM=3 +CONFIG_SOC_SYSTIMER_BIT_WIDTH_LO=32 +CONFIG_SOC_SYSTIMER_BIT_WIDTH_HI=20 +CONFIG_SOC_SYSTIMER_FIXED_DIVIDER=y +CONFIG_SOC_SYSTIMER_INT_LEVEL=y +CONFIG_SOC_SYSTIMER_ALARM_MISS_COMPENSATE=y +CONFIG_SOC_TIMER_GROUPS=2 +CONFIG_SOC_TIMER_GROUP_TIMERS_PER_GROUP=2 +CONFIG_SOC_TIMER_GROUP_COUNTER_BIT_WIDTH=54 +CONFIG_SOC_TIMER_GROUP_SUPPORT_XTAL=y +CONFIG_SOC_TIMER_GROUP_SUPPORT_APB=y +CONFIG_SOC_TIMER_GROUP_TOTAL_TIMERS=4 +CONFIG_SOC_LP_TIMER_BIT_WIDTH_LO=32 +CONFIG_SOC_LP_TIMER_BIT_WIDTH_HI=16 +CONFIG_SOC_TOUCH_SENSOR_VERSION=2 +CONFIG_SOC_TOUCH_SENSOR_NUM=15 +CONFIG_SOC_TOUCH_MIN_CHAN_ID=1 +CONFIG_SOC_TOUCH_MAX_CHAN_ID=14 +CONFIG_SOC_TOUCH_SUPPORT_BENCHMARK=y +CONFIG_SOC_TOUCH_SUPPORT_SLEEP_WAKEUP=y +CONFIG_SOC_TOUCH_SUPPORT_WATERPROOF=y +CONFIG_SOC_TOUCH_SUPPORT_PROX_SENSING=y +CONFIG_SOC_TOUCH_SUPPORT_DENOISE_CHAN=y +CONFIG_SOC_TOUCH_PROXIMITY_CHANNEL_NUM=3 +CONFIG_SOC_TOUCH_PROXIMITY_MEAS_DONE_SUPPORTED=y +CONFIG_SOC_TOUCH_SAMPLE_CFG_NUM=1 +CONFIG_SOC_TWAI_CONTROLLER_NUM=1 +CONFIG_SOC_TWAI_MASK_FILTER_NUM=1 +CONFIG_SOC_TWAI_CLK_SUPPORT_APB=y +CONFIG_SOC_TWAI_BRP_MIN=2 +CONFIG_SOC_TWAI_BRP_MAX=16384 +CONFIG_SOC_TWAI_SUPPORTS_RX_STATUS=y +CONFIG_SOC_UART_NUM=3 +CONFIG_SOC_UART_HP_NUM=3 +CONFIG_SOC_UART_FIFO_LEN=128 +CONFIG_SOC_UART_BITRATE_MAX=5000000 +CONFIG_SOC_UART_SUPPORT_FSM_TX_WAIT_SEND=y +CONFIG_SOC_UART_SUPPORT_WAKEUP_INT=y +CONFIG_SOC_UART_SUPPORT_APB_CLK=y +CONFIG_SOC_UART_SUPPORT_RTC_CLK=y +CONFIG_SOC_UART_SUPPORT_XTAL_CLK=y +CONFIG_SOC_UART_WAKEUP_SUPPORT_ACTIVE_THRESH_MODE=y +CONFIG_SOC_UHCI_NUM=1 +CONFIG_SOC_USB_OTG_PERIPH_NUM=1 +CONFIG_SOC_SHA_DMA_MAX_BUFFER_SIZE=3968 +CONFIG_SOC_SHA_SUPPORT_DMA=y +CONFIG_SOC_SHA_SUPPORT_RESUME=y +CONFIG_SOC_SHA_GDMA=y +CONFIG_SOC_SHA_SUPPORT_SHA1=y +CONFIG_SOC_SHA_SUPPORT_SHA224=y +CONFIG_SOC_SHA_SUPPORT_SHA256=y +CONFIG_SOC_SHA_SUPPORT_SHA384=y +CONFIG_SOC_SHA_SUPPORT_SHA512=y +CONFIG_SOC_SHA_SUPPORT_SHA512_224=y +CONFIG_SOC_SHA_SUPPORT_SHA512_256=y +CONFIG_SOC_SHA_SUPPORT_SHA512_T=y +CONFIG_SOC_MPI_MEM_BLOCKS_NUM=4 +CONFIG_SOC_MPI_OPERATIONS_NUM=3 +CONFIG_SOC_RSA_MAX_BIT_LEN=4096 +CONFIG_SOC_AES_SUPPORT_DMA=y +CONFIG_SOC_AES_GDMA=y +CONFIG_SOC_AES_SUPPORT_AES_128=y +CONFIG_SOC_AES_SUPPORT_AES_256=y +CONFIG_SOC_PM_SUPPORT_EXT0_WAKEUP=y +CONFIG_SOC_PM_SUPPORT_EXT1_WAKEUP=y +CONFIG_SOC_PM_SUPPORT_EXT_WAKEUP=y +CONFIG_SOC_PM_SUPPORT_WIFI_WAKEUP=y +CONFIG_SOC_PM_SUPPORT_BT_WAKEUP=y +CONFIG_SOC_PM_SUPPORT_TOUCH_SENSOR_WAKEUP=y +CONFIG_SOC_PM_SUPPORT_CPU_PD=y +CONFIG_SOC_PM_SUPPORT_TAGMEM_PD=y +CONFIG_SOC_PM_SUPPORT_RTC_PERIPH_PD=y +CONFIG_SOC_PM_SUPPORT_RC_FAST_PD=y +CONFIG_SOC_PM_SUPPORT_VDDSDIO_PD=y +CONFIG_SOC_PM_SUPPORT_MAC_BB_PD=y +CONFIG_SOC_PM_SUPPORT_MODEM_PD=y +CONFIG_SOC_CONFIGURABLE_VDDSDIO_SUPPORTED=y +CONFIG_SOC_PM_SUPPORT_DEEPSLEEP_CHECK_STUB_ONLY=y +CONFIG_SOC_PM_CPU_RETENTION_BY_RTCCNTL=y +CONFIG_SOC_PM_MODEM_RETENTION_BY_BACKUPDMA=y +CONFIG_SOC_PM_MODEM_PD_BY_SW=y +CONFIG_SOC_CLK_RC_FAST_D256_SUPPORTED=y +CONFIG_SOC_RTC_SLOW_CLK_SUPPORT_RC_FAST_D256=y +CONFIG_SOC_CLK_RC_FAST_SUPPORT_CALIBRATION=y +CONFIG_SOC_CLK_XTAL32K_SUPPORTED=y +CONFIG_SOC_CLK_LP_FAST_SUPPORT_XTAL_D2=y +CONFIG_SOC_EFUSE_DIS_DOWNLOAD_ICACHE=y +CONFIG_SOC_EFUSE_DIS_DOWNLOAD_DCACHE=y +CONFIG_SOC_EFUSE_HARD_DIS_JTAG=y +CONFIG_SOC_EFUSE_DIS_USB_JTAG=y +CONFIG_SOC_EFUSE_SOFT_DIS_JTAG=y +CONFIG_SOC_EFUSE_DIS_DIRECT_BOOT=y +CONFIG_SOC_EFUSE_DIS_ICACHE=y +CONFIG_SOC_EFUSE_BLOCK9_KEY_PURPOSE_QUIRK=y +CONFIG_SOC_SECURE_BOOT_V2_RSA=y +CONFIG_SOC_EFUSE_SECURE_BOOT_KEY_DIGESTS=3 +CONFIG_SOC_EFUSE_REVOKE_BOOT_KEY_DIGESTS=y +CONFIG_SOC_SUPPORT_SECURE_BOOT_REVOKE_KEY=y +CONFIG_SOC_FLASH_ENCRYPTED_XTS_AES_BLOCK_MAX=64 +CONFIG_SOC_FLASH_ENCRYPTION_XTS_AES=y +CONFIG_SOC_FLASH_ENCRYPTION_XTS_AES_OPTIONS=y +CONFIG_SOC_FLASH_ENCRYPTION_XTS_AES_128=y +CONFIG_SOC_FLASH_ENCRYPTION_XTS_AES_256=y +CONFIG_SOC_MEMPROT_CPU_PREFETCH_PAD_SIZE=16 +CONFIG_SOC_MEMPROT_MEM_ALIGN_SIZE=256 +CONFIG_SOC_PHY_DIG_REGS_MEM_SIZE=21 +CONFIG_SOC_MAC_BB_PD_MEM_SIZE=192 +CONFIG_SOC_WIFI_LIGHT_SLEEP_CLK_WIDTH=12 +CONFIG_SOC_SPI_MEM_SUPPORT_AUTO_WAIT_IDLE=y +CONFIG_SOC_SPI_MEM_SUPPORT_AUTO_SUSPEND=y +CONFIG_SOC_SPI_MEM_SUPPORT_AUTO_RESUME=y +CONFIG_SOC_SPI_MEM_SUPPORT_SW_SUSPEND=y +CONFIG_SOC_SPI_MEM_SUPPORT_FLASH_OPI_MODE=y +CONFIG_SOC_SPI_MEM_SUPPORT_TIMING_TUNING=y +CONFIG_SOC_SPI_MEM_SUPPORT_CONFIG_GPIO_BY_EFUSE=y +CONFIG_SOC_SPI_MEM_SUPPORT_WRAP=y +CONFIG_SOC_MEMSPI_TIMING_TUNING_BY_MSPI_DELAY=y +CONFIG_SOC_MEMSPI_CORE_CLK_SHARED_WITH_PSRAM=y +CONFIG_SOC_SPI_MEM_SUPPORT_CACHE_32BIT_ADDR_MAP=y +CONFIG_SOC_COEX_HW_PTI=y +CONFIG_SOC_EXTERNAL_COEX_LEADER_TX_LINE=y +CONFIG_SOC_SDMMC_USE_GPIO_MATRIX=y +CONFIG_SOC_SDMMC_NUM_SLOTS=2 +CONFIG_SOC_SDMMC_SUPPORT_XTAL_CLOCK=y +CONFIG_SOC_SDMMC_DELAY_PHASE_NUM=4 +CONFIG_SOC_TEMPERATURE_SENSOR_SUPPORT_FAST_RC=y +CONFIG_SOC_WIFI_HW_TSF=y +CONFIG_SOC_WIFI_FTM_SUPPORT=y +CONFIG_SOC_WIFI_GCMP_SUPPORT=y +CONFIG_SOC_WIFI_WAPI_SUPPORT=y +CONFIG_SOC_WIFI_CSI_SUPPORT=y +CONFIG_SOC_WIFI_MESH_SUPPORT=y +CONFIG_SOC_WIFI_SUPPORT_VARIABLE_BEACON_WINDOW=y +CONFIG_SOC_WIFI_PHY_NEEDS_USB_WORKAROUND=y +CONFIG_SOC_BLE_SUPPORTED=y +CONFIG_SOC_BLE_MESH_SUPPORTED=y +CONFIG_SOC_BLE_50_SUPPORTED=y +CONFIG_SOC_BLE_DEVICE_PRIVACY_SUPPORTED=y +CONFIG_SOC_BLUFI_SUPPORTED=y +CONFIG_SOC_ULP_HAS_ADC=y +CONFIG_SOC_PHY_COMBO_MODULE=y +CONFIG_SOC_LCDCAM_CAM_SUPPORT_RGB_YUV_CONV=y +CONFIG_SOC_LCDCAM_CAM_PERIPH_NUM=1 +CONFIG_SOC_LCDCAM_CAM_DATA_WIDTH_MAX=16 +CONFIG_IDF_CMAKE=y +CONFIG_IDF_TOOLCHAIN="gcc" +CONFIG_IDF_TOOLCHAIN_GCC=y +CONFIG_IDF_TARGET_ARCH_XTENSA=y +CONFIG_IDF_TARGET_ARCH="xtensa" +CONFIG_IDF_TARGET="esp32s3" +CONFIG_IDF_INIT_VERSION="5.4.0" +CONFIG_IDF_TARGET_ESP32S3=y +CONFIG_IDF_FIRMWARE_CHIP_ID=0x0009 + +# +# Build type +# +CONFIG_APP_BUILD_TYPE_APP_2NDBOOT=y +# CONFIG_APP_BUILD_TYPE_RAM is not set +CONFIG_APP_BUILD_GENERATE_BINARIES=y +CONFIG_APP_BUILD_BOOTLOADER=y +CONFIG_APP_BUILD_USE_FLASH_SECTIONS=y +# CONFIG_APP_REPRODUCIBLE_BUILD is not set +# CONFIG_APP_NO_BLOBS is not set +# end of Build type + +# +# Bootloader config +# + +# +# Bootloader manager +# +CONFIG_BOOTLOADER_COMPILE_TIME_DATE=y +CONFIG_BOOTLOADER_PROJECT_VER=1 +# end of Bootloader manager + +# +# Application Rollback +# +# CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE is not set +# end of Application Rollback + +# +# Recovery Bootloader and Rollback +# +# end of Recovery Bootloader and Rollback + +CONFIG_BOOTLOADER_OFFSET_IN_FLASH=0x0 +CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y +# CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_DEBUG is not set +# CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_PERF is not set +# CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_NONE is not set + +# +# Log +# +CONFIG_BOOTLOADER_LOG_VERSION_1=y +CONFIG_BOOTLOADER_LOG_VERSION=1 +# CONFIG_BOOTLOADER_LOG_LEVEL_NONE is not set +# CONFIG_BOOTLOADER_LOG_LEVEL_ERROR is not set +# CONFIG_BOOTLOADER_LOG_LEVEL_WARN is not set +CONFIG_BOOTLOADER_LOG_LEVEL_INFO=y +# CONFIG_BOOTLOADER_LOG_LEVEL_DEBUG is not set +# CONFIG_BOOTLOADER_LOG_LEVEL_VERBOSE is not set +CONFIG_BOOTLOADER_LOG_LEVEL=3 + +# +# Format +# +# CONFIG_BOOTLOADER_LOG_COLORS is not set +CONFIG_BOOTLOADER_LOG_TIMESTAMP_SOURCE_CPU_TICKS=y +# end of Format + +# +# Settings +# +CONFIG_BOOTLOADER_LOG_MODE_TEXT_EN=y +CONFIG_BOOTLOADER_LOG_MODE_TEXT=y +# end of Settings +# end of Log + +# +# Serial Flash Configurations +# +CONFIG_BOOTLOADER_FLASH_DC_AWARE=y +CONFIG_BOOTLOADER_FLASH_XMC_SUPPORT=y +# end of Serial Flash Configurations + +CONFIG_BOOTLOADER_VDDSDIO_BOOST_1_9V=y +# CONFIG_BOOTLOADER_FACTORY_RESET is not set +# CONFIG_BOOTLOADER_APP_TEST is not set +CONFIG_BOOTLOADER_REGION_PROTECTION_ENABLE=y +CONFIG_BOOTLOADER_WDT_ENABLE=y +# CONFIG_BOOTLOADER_WDT_DISABLE_IN_USER_CODE is not set +CONFIG_BOOTLOADER_WDT_TIME_MS=9000 +# CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP is not set +# CONFIG_BOOTLOADER_SKIP_VALIDATE_ON_POWER_ON is not set +# CONFIG_BOOTLOADER_SKIP_VALIDATE_ALWAYS is not set +CONFIG_BOOTLOADER_RESERVE_RTC_SIZE=0 +# CONFIG_BOOTLOADER_CUSTOM_RESERVE_RTC is not set +# end of Bootloader config + +# +# Security features +# +CONFIG_SECURE_BOOT_V2_RSA_SUPPORTED=y +CONFIG_SECURE_BOOT_V2_PREFERRED=y +# CONFIG_SECURE_SIGNED_APPS_NO_SECURE_BOOT is not set +# CONFIG_SECURE_BOOT is not set +# CONFIG_SECURE_FLASH_ENC_ENABLED is not set +CONFIG_SECURE_ROM_DL_MODE_ENABLED=y +# end of Security features + +# +# Application manager +# +CONFIG_APP_COMPILE_TIME_DATE=y +# CONFIG_APP_EXCLUDE_PROJECT_VER_VAR is not set +# CONFIG_APP_EXCLUDE_PROJECT_NAME_VAR is not set +# CONFIG_APP_PROJECT_VER_FROM_CONFIG is not set +CONFIG_APP_RETRIEVE_LEN_ELF_SHA=9 +# end of Application manager + +CONFIG_ESP_ROM_HAS_CRC_LE=y +CONFIG_ESP_ROM_HAS_CRC_BE=y +CONFIG_ESP_ROM_HAS_MZ_CRC32=y +CONFIG_ESP_ROM_HAS_JPEG_DECODE=y +CONFIG_ESP_ROM_UART_CLK_IS_XTAL=y +CONFIG_ESP_ROM_HAS_RETARGETABLE_LOCKING=y +CONFIG_ESP_ROM_USB_OTG_NUM=3 +CONFIG_ESP_ROM_USB_SERIAL_DEVICE_NUM=4 +CONFIG_ESP_ROM_HAS_ERASE_0_REGION_BUG=y +CONFIG_ESP_ROM_HAS_ENCRYPTED_WRITES_USING_LEGACY_DRV=y +CONFIG_ESP_ROM_GET_CLK_FREQ=y +CONFIG_ESP_ROM_HAS_HAL_WDT=y +CONFIG_ESP_ROM_NEEDS_SWSETUP_WORKAROUND=y +CONFIG_ESP_ROM_HAS_LAYOUT_TABLE=y +CONFIG_ESP_ROM_HAS_SPI_FLASH=y +CONFIG_ESP_ROM_HAS_SPI_FLASH_MMAP=y +CONFIG_ESP_ROM_HAS_ETS_PRINTF_BUG=y +CONFIG_ESP_ROM_HAS_NEWLIB=y +CONFIG_ESP_ROM_HAS_NEWLIB_NANO_FORMAT=y +CONFIG_ESP_ROM_HAS_NEWLIB_32BIT_TIME=y +CONFIG_ESP_ROM_NEEDS_SET_CACHE_MMU_SIZE=y +CONFIG_ESP_ROM_RAM_APP_NEEDS_MMU_INIT=y +CONFIG_ESP_ROM_HAS_FLASH_COUNT_PAGES_BUG=y +CONFIG_ESP_ROM_HAS_CACHE_SUSPEND_WAITI_BUG=y +CONFIG_ESP_ROM_HAS_CACHE_WRITEBACK_BUG=y +CONFIG_ESP_ROM_HAS_SW_FLOAT=y +CONFIG_ESP_ROM_HAS_VERSION=y +CONFIG_ESP_ROM_SUPPORT_DEEP_SLEEP_WAKEUP_STUB=y +CONFIG_ESP_ROM_HAS_OUTPUT_PUTC_FUNC=y +CONFIG_ESP_ROM_CONSOLE_OUTPUT_SECONDARY=y + +# +# Boot ROM Behavior +# +CONFIG_BOOT_ROM_LOG_ALWAYS_ON=y +# CONFIG_BOOT_ROM_LOG_ALWAYS_OFF is not set +# CONFIG_BOOT_ROM_LOG_ON_GPIO_HIGH is not set +# CONFIG_BOOT_ROM_LOG_ON_GPIO_LOW is not set +# end of Boot ROM Behavior + +# +# Serial flasher config +# +# CONFIG_ESPTOOLPY_NO_STUB is not set +# CONFIG_ESPTOOLPY_OCT_FLASH is not set +CONFIG_ESPTOOLPY_FLASH_MODE_AUTO_DETECT=y +CONFIG_ESPTOOLPY_FLASHMODE_QIO=y +# CONFIG_ESPTOOLPY_FLASHMODE_QOUT is not set +# CONFIG_ESPTOOLPY_FLASHMODE_DIO is not set +# CONFIG_ESPTOOLPY_FLASHMODE_DOUT is not set +CONFIG_ESPTOOLPY_FLASH_SAMPLE_MODE_STR=y +CONFIG_ESPTOOLPY_FLASHMODE="dio" +# CONFIG_ESPTOOLPY_FLASHFREQ_120M is not set +CONFIG_ESPTOOLPY_FLASHFREQ_80M=y +# CONFIG_ESPTOOLPY_FLASHFREQ_40M is not set +# CONFIG_ESPTOOLPY_FLASHFREQ_20M is not set +CONFIG_ESPTOOLPY_FLASHFREQ="80m" +# CONFIG_ESPTOOLPY_FLASHSIZE_1MB is not set +# CONFIG_ESPTOOLPY_FLASHSIZE_2MB is not set +# CONFIG_ESPTOOLPY_FLASHSIZE_4MB is not set +# CONFIG_ESPTOOLPY_FLASHSIZE_8MB is not set +CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y +# CONFIG_ESPTOOLPY_FLASHSIZE_32MB is not set +# CONFIG_ESPTOOLPY_FLASHSIZE_64MB is not set +# CONFIG_ESPTOOLPY_FLASHSIZE_128MB is not set +CONFIG_ESPTOOLPY_FLASHSIZE="16MB" +CONFIG_ESPTOOLPY_HEADER_FLASHSIZE_UPDATE=y +CONFIG_ESPTOOLPY_BEFORE_RESET=y +# CONFIG_ESPTOOLPY_BEFORE_NORESET is not set +CONFIG_ESPTOOLPY_BEFORE="default_reset" +CONFIG_ESPTOOLPY_AFTER_RESET=y +# CONFIG_ESPTOOLPY_AFTER_NORESET is not set +CONFIG_ESPTOOLPY_AFTER="hard_reset" +CONFIG_ESPTOOLPY_MONITOR_BAUD=115200 +# end of Serial flasher config + +# +# Partition Table +# +# CONFIG_PARTITION_TABLE_SINGLE_APP is not set +# CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE is not set +# CONFIG_PARTITION_TABLE_TWO_OTA is not set +# CONFIG_PARTITION_TABLE_TWO_OTA_LARGE is not set +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" +CONFIG_PARTITION_TABLE_FILENAME="partitions.csv" +CONFIG_PARTITION_TABLE_OFFSET=0x8000 +CONFIG_PARTITION_TABLE_MD5=y +# end of Partition Table + +# +# Example Configuration +# +CONFIG_VOLC_WS_MODE=y + +# +# Develop Configuration +# +# CONFIG_VOLC_AUDIO_G711A is not set +CONFIG_VOLC_AUDIO_PCM16=y +# CONFIG_PRINT_TASK_INFO is not set +# end of Develop Configuration +# end of Example Configuration + +# +# Audio/Video Processor Configuration +# + +# +# Media Dump +# +# CONFIG_MEDIA_DUMP_ENABLE is not set +# end of Media Dump +# end of Audio/Video Processor Configuration + +# +# Hardware Configuration +# +CONFIG_EXAMPLE_LCD_INTERFACE_QSPI=y +# CONFIG_EXAMPLE_LCD_INTERFACE_RGB is not set +# CONFIG_EXAMPLE_LCD_INTERFACE_SPI_WITH_PSRAM is not set +# CONFIG_EXAMPLE_LCD_INTERFACE_SPI_WITHOUT_PSRAM is not set +CONFIG_EXAMPLE_DISPLAY_ROTATION_0=y +# CONFIG_EXAMPLE_DISPLAY_ROTATION_90 is not set +# CONFIG_EXAMPLE_DISPLAY_ROTATION_180 is not set +# CONFIG_EXAMPLE_DISPLAY_ROTATION_270 is not set +# end of Hardware Configuration + +# +# ESP Speech Recognition +# +CONFIG_MODEL_IN_FLASH=y +# CONFIG_MODEL_IN_SDCARD is not set +CONFIG_AFE_INTERFACE_V1=y +CONFIG_SR_NSN_WEBRTC=y +# CONFIG_SR_NSN_NSNET2 is not set +CONFIG_SR_VADN_WEBRTC=y +# CONFIG_SR_VADN_VADNET1_MEDIUM is not set + +# +# Load Multiple Wake Words (WakeNet9s) +# +# CONFIG_SR_WN_WN9S_HILEXIN is not set +# CONFIG_SR_WN_WN9S_HIESP is not set +# CONFIG_SR_WN_WN9S_NIHAOXIAOZHI is not set +# CONFIG_SR_WN_WN9S_HIJASON is not set +# end of Load Multiple Wake Words (WakeNet9s) + +# +# Load Multiple Wake Words (WakeNet9) +# +CONFIG_SR_WN_WN9_HILEXIN=y +# CONFIG_SR_WN_WN9_NIHAOMIAOBAN_TTS2 is not set +# CONFIG_SR_WN_WN9_XIAOAITONGXUE is not set +# CONFIG_SR_WN_WN9_NIHAOXIAOZHI_TTS is not set +# CONFIG_SR_WN_WN9_ALEXA is not set +# CONFIG_SR_WN_WN9_HIESP is not set +# CONFIG_SR_WN_WN9_JARVIS_TTS is not set +# CONFIG_SR_WN_WN9_COMPUTER_TTS is not set +# CONFIG_SR_WN_WN9_HEYWILLOW_TTS is not set +# CONFIG_SR_WN_WN9_HIMFIVE is not set +# CONFIG_SR_WN_WN9_SOPHIA_TTS is not set +# CONFIG_SR_WN_WN9_HEYWANDA_TTS is not set +# CONFIG_SR_WN_WN9_HIJOLLY_TTS2 is not set +# CONFIG_SR_WN_WN9_HIFAIRY_TTS2 is not set +# CONFIG_SR_WN_WN9_HEYPRINTER_TTS is not set +# CONFIG_SR_WN_WN9_MYCROFT_TTS is not set +# CONFIG_SR_WN_WN9_HIJOY_TTS is not set +# CONFIG_SR_WN_WN9_HIJASON_TTS2 is not set +# CONFIG_SR_WN_WN9_ASTROLABE_TTS is not set +# CONFIG_SR_WN_WN9_HEYILY_TTS2 is not set +# CONFIG_SR_WN_WN9_BLUECHIP_TTS2 is not set +# CONFIG_SR_WN_WN9_XIAOLONGXIAOLONG_TTS is not set +# CONFIG_SR_WN_WN9_HIMIAOMIAO_TTS is not set +# CONFIG_SR_WN_WN9_MIAOMIAOTONGXUE_TTS is not set +# CONFIG_SR_WN_WN9_NIHAOXIAOXIN_TTS is not set +# CONFIG_SR_WN_WN9_XIAOMEITONGXUE_TTS is not set +# CONFIG_SR_WN_WN9_HILILI_TTS is not set +# CONFIG_SR_WN_WN9_HITELLY_TTS is not set +# CONFIG_SR_WN_WN9_XIAOBINXIAOBIN_TTS is not set +# CONFIG_SR_WN_WN9_HAIXIAOWU_TTS is not set +# CONFIG_SR_WN_WN9_XIAOYAXIAOYA_TTS2 is not set +# CONFIG_SR_WN_WN9_LINAIBAN_TTS2 is not set +# CONFIG_SR_WN_WN9_XIAOSUROU_TTS2 is not set +# CONFIG_SR_WN_WN9_XIAOYUTONGXUE_TTS2 is not set +# CONFIG_SR_WN_WN9_XIAOMINGTONGXUE_TTS2 is not set +# CONFIG_SR_WN_WN9_XIAOKANGTONGXUE_TTS2 is not set +# CONFIG_SR_WN_WN9_XIAOJIANXIAOJIAN_TTS2 is not set +# CONFIG_SR_WN_WN9_XIAOTEXIAOTE_TTS2 is not set +# CONFIG_SR_WN_WN9_NIHAOXIAOYI_TTS2 is not set +# CONFIG_SR_WN_WN9_NIHAOBAIYING_TTS2 is not set +# CONFIG_SR_WN_WN9_NIHAODONGDONG_TTS2 is not set +# CONFIG_SR_WN_WN9_HIWALLE_TTS2 is not set +# CONFIG_SR_WN_WN9_XIAOLUXIAOLU_TTS2 is not set +# CONFIG_SR_WN_WN9_NIHAOXIAOAN_TTS2 is not set +# end of Load Multiple Wake Words (WakeNet9) + +CONFIG_SR_MN_CN_NONE=y +# CONFIG_SR_MN_CN_MULTINET5_RECOGNITION_QUANT8 is not set +# CONFIG_SR_MN_CN_MULTINET6_QUANT is not set +# CONFIG_SR_MN_CN_MULTINET6_AC_QUANT is not set +# CONFIG_SR_MN_CN_MULTINET7_QUANT is not set +# CONFIG_SR_MN_CN_MULTINET7_AC_QUANT is not set +CONFIG_SR_MN_EN_NONE=y +# CONFIG_SR_MN_EN_MULTINET5_SINGLE_RECOGNITION_QUANT8 is not set +# CONFIG_SR_MN_EN_MULTINET6_QUANT is not set +# CONFIG_SR_MN_EN_MULTINET7_QUANT is not set +# end of ESP Speech Recognition + +# +# ESP Board Manager Configuration +# + +# +# Board Manager Setting +# +CONFIG_ESP_BOARD_MANAGER_AUTO_CONFIG_DEVICE_AND_PERIPHERAL=y +# end of Board Manager Setting + +# +# Board Selection +# +# CONFIG_BOARD_DUAL_EYES_BOARD_V1_0 is not set +# CONFIG_BOARD_ECHOEAR_CORE_BOARD_V1_0 is not set +CONFIG_BOARD_ECHOEAR_CORE_BOARD_V1_2=y +# CONFIG_BOARD_ESP32_C5_SPOT is not set +# CONFIG_BOARD_ESP32_P4_FUNCTION_EV is not set +# CONFIG_BOARD_ESP32_S3_KORVO2_V3 is not set +# CONFIG_BOARD_ESP32_S3_KORVO2L is not set +# CONFIG_BOARD_ESP_BOX_3 is not set +# CONFIG_BOARD_LYRAT_MINI_V1_1 is not set +CONFIG_BOARD_NAME="echoear_core_board_v1_2" +# end of Board Selection + +# +# Peripheral Support +# +CONFIG_ESP_BOARD_PERIPH_GPIO_SUPPORT=y +CONFIG_ESP_BOARD_PERIPH_I2C_SUPPORT=y +CONFIG_ESP_BOARD_PERIPH_I2S_SUPPORT=y +CONFIG_ESP_BOARD_PERIPH_LEDC_SUPPORT=y +CONFIG_ESP_BOARD_PERIPH_SPI_SUPPORT=y +# end of Peripheral Support + +# +# Device Support +# +CONFIG_ESP_BOARD_DEV_AUDIO_CODEC_SUPPORT=y +# CONFIG_ESP_BOARD_DEV_CAMERA_SUPPORT is not set +# CONFIG_ESP_BOARD_DEV_CUSTOM_SUPPORT is not set +CONFIG_ESP_BOARD_DEV_DISPLAY_LCD_SPI_SUPPORT=y +CONFIG_ESP_BOARD_DEV_FATFS_SDCARD_SUPPORT=y +# CONFIG_ESP_BOARD_DEV_FATFS_SDCARD_SPI_SUPPORT is not set +# CONFIG_ESP_BOARD_DEV_FS_SPIFFS_SUPPORT is not set +CONFIG_ESP_BOARD_DEV_GPIO_CTRL_SUPPORT=y +# CONFIG_ESP_BOARD_DEV_GPIO_EXPANDER_SUPPORT is not set +CONFIG_ESP_BOARD_DEV_LCD_TOUCH_I2C_SUPPORT=y +CONFIG_ESP_BOARD_DEV_LEDC_CTRL_SUPPORT=y +# end of Device Support +# end of ESP Board Manager Configuration + +# +# ESP GMF Loader +# + +# +# GMF IO Configurations +# + +# +# GMF IO Reader +# +CONFIG_GMF_IO_INIT_CODEC_DEV_RX=y +CONFIG_GMF_IO_INIT_FILE_READER=y + +# +# IO File Configuration +# +CONFIG_GMF_IO_FILE_READ_CACHE_SIZE=2048 +# end of IO File Configuration + +CONFIG_GMF_IO_INIT_HTTP_READER=y + +# +# HTTP Reader Configuration +# +CONFIG_GMF_IO_HTTP_READER_TASK_STACK=3072 +CONFIG_GMF_IO_HTTP_READER_TASK_CORE=0 +CONFIG_GMF_IO_HTTP_READER_TASK_PRIORITY=5 +# CONFIG_GMF_IO_HTTP_READER_STACK_IN_EXT is not set +CONFIG_GMF_IO_HTTP_READER_OUT_BUF_SIZE=8192 +CONFIG_GMF_IO_HTTP_READER_CERT_PEM="" +# end of HTTP Reader Configuration + +CONFIG_GMF_IO_INIT_FLASH_READER=y +# end of GMF IO Reader + +# +# GMF IO Writer +# +CONFIG_GMF_IO_INIT_CODEC_DEV_TX=y +CONFIG_GMF_IO_INIT_FILE_WRITER=y + +# +# IO File Configuration +# +CONFIG_GMF_IO_FILE_WRITE_CACHE_SIZE=4096 +# end of IO File Configuration + +# CONFIG_GMF_IO_INIT_HTTP_WRITER is not set +# end of GMF IO Writer +# end of GMF IO Configurations + +# +# GMF Audio Configurations +# + +# +# GMF Audio Codec +# +CONFIG_GMF_AUDIO_CODEC_INIT_DECODER=y +CONFIG_GMF_AUDIO_CODEC_DECODER_TYPE_AAC=y +# CONFIG_GMF_AUDIO_CODEC_DECODER_TYPE_MP3 is not set +# CONFIG_GMF_AUDIO_CODEC_DECODER_TYPE_AMRNB is not set +# CONFIG_GMF_AUDIO_CODEC_DECODER_TYPE_AMRWB is not set +# CONFIG_GMF_AUDIO_CODEC_DECODER_TYPE_FLAC is not set +# CONFIG_GMF_AUDIO_CODEC_DECODER_TYPE_WAV is not set +# CONFIG_GMF_AUDIO_CODEC_DECODER_TYPE_M4A is not set +# CONFIG_GMF_AUDIO_CODEC_DECODER_TYPE_TS is not set +# CONFIG_GMF_AUDIO_CODEC_DECODER_TYPE_RAW_OPUS is not set +# CONFIG_GMF_AUDIO_CODEC_DECODER_TYPE_G711A is not set +# CONFIG_GMF_AUDIO_CODEC_DECODER_TYPE_G711U is not set +# CONFIG_GMF_AUDIO_CODEC_DECODER_TYPE_PCM is not set +# CONFIG_GMF_AUDIO_CODEC_DECODER_TYPE_ADPCM is not set +# CONFIG_GMF_AUDIO_CODEC_DECODER_TYPE_SBC is not set +# CONFIG_GMF_AUDIO_CODEC_DECODER_TYPE_LC3 is not set + +# +# AAC Decoder Configuration +# +# CONFIG_GMF_AUDIO_CODEC_DEC_AAC_NO_ADTS_HEADER is not set +# CONFIG_GMF_AUDIO_CODEC_DEC_AAC_PLUS_ENABLE is not set +# end of AAC Decoder Configuration + +CONFIG_GMF_AUDIO_CODEC_INIT_ENCODER=y +CONFIG_GMF_AUDIO_CODEC_ENCODER_TYPE_AAC=y +# CONFIG_GMF_AUDIO_CODEC_ENCODER_TYPE_AMRNB is not set +# CONFIG_GMF_AUDIO_CODEC_ENCODER_TYPE_AMRWB is not set +# CONFIG_GMF_AUDIO_CODEC_ENCODER_TYPE_G711A is not set +# CONFIG_GMF_AUDIO_CODEC_ENCODER_TYPE_G711U is not set +# CONFIG_GMF_AUDIO_CODEC_ENCODER_TYPE_OPUS is not set +# CONFIG_GMF_AUDIO_CODEC_ENCODER_TYPE_ADPCM is not set +# CONFIG_GMF_AUDIO_CODEC_ENCODER_TYPE_PCM is not set +# CONFIG_GMF_AUDIO_CODEC_ENCODER_TYPE_ALAC is not set +# CONFIG_GMF_AUDIO_CODEC_ENCODER_TYPE_SBC is not set +# CONFIG_GMF_AUDIO_CODEC_ENCODER_TYPE_LC3 is not set + +# +# AAC Encoder Configuration +# +CONFIG_GMF_AUDIO_CODEC_ENC_AAC_BITRATE=90000 +CONFIG_GMF_AUDIO_CODEC_ENC_AAC_ADTS_USED=y +# end of AAC Encoder Configuration +# end of GMF Audio Codec + +# +# GMF Audio Effects +# +CONFIG_GMF_AUDIO_EFFECT_INIT_ALC=y +CONFIG_GMF_AUDIO_EFFECT_INIT_CH_CVT=y +CONFIG_GMF_AUDIO_EFFECT_CH_CVT_DEST_CH=2 +CONFIG_GMF_AUDIO_EFFECT_INIT_BIT_CVT=y +CONFIG_GMF_AUDIO_EFFECT_BIT_CVT_DEST_BITS=16 +CONFIG_GMF_AUDIO_EFFECT_INIT_RATE_CVT=y +CONFIG_GMF_AUDIO_EFFECT_RATE_CVT_DEST_RATE=44100 +CONFIG_GMF_AUDIO_EFFECT_RATE_CVT_COMPLEXITY=2 +CONFIG_GMF_AUDIO_EFFECT_RATE_CVT_PERF_TYPE_MEMORY=y +# CONFIG_GMF_AUDIO_EFFECT_RATE_CVT_PERF_TYPE_SPEED is not set +CONFIG_GMF_AUDIO_EFFECT_RATE_CVT_PERF_TYPE=0 +# CONFIG_GMF_AUDIO_EFFECT_INIT_DEINTERLEAVE is not set +# CONFIG_GMF_AUDIO_EFFECT_INIT_INTERLEAVE is not set +# CONFIG_GMF_AUDIO_EFFECT_INIT_MIXER is not set +# CONFIG_GMF_AUDIO_EFFECT_INIT_EQ is not set +# CONFIG_GMF_AUDIO_EFFECT_INIT_SONIC is not set +# CONFIG_GMF_AUDIO_EFFECT_INIT_FADE is not set +# end of GMF Audio Effects + +# +# GMF AI Audio +# +CONFIG_GMF_AI_AUDIO_INIT_AEC=y + +# +# Acoustic Echo Cancellation (AEC) Configuration +# +# CONFIG_GMF_AI_AUDIO_AEC_INPUT_SAMPLING_RATE_8K is not set +CONFIG_GMF_AI_AUDIO_AEC_INPUT_SAMPLING_RATE_16K=y +CONFIG_GMF_AI_AUDIO_AEC_TYPE=1 +# CONFIG_GMF_AI_AUDIO_AEC_MODE_LOW_COST is not set +CONFIG_GMF_AI_AUDIO_AEC_MODE_HIGH_PERFORMANCE=y +CONFIG_GMF_AI_AUDIO_AEC_MODE=1 +CONFIG_GMF_AI_AUDIO_AEC_FILTER_LEN=4 +CONFIG_GMF_AI_AUDIO_AEC_CH_ALLOCATION="RMNM" +# end of Acoustic Echo Cancellation (AEC) Configuration + +# CONFIG_GMF_AI_AUDIO_INIT_WN is not set +# CONFIG_GMF_AI_AUDIO_INIT_AFE is not set +# end of GMF AI Audio +# end of GMF Audio Configurations + +# +# GMF Video Configurations +# + +# +# GMF Video Codec +# +CONFIG_FOURCC_H263=0x48323633 +CONFIG_FOURCC_H264=0x48323634 +CONFIG_FOURCC_AVC1=0x41564331 +CONFIG_FOURCC_H265=0x48323635 +CONFIG_FOURCC_VP8=0x56503820 +CONFIG_FOURCC_MJPG=0x4D4A5047 +CONFIG_FOURCC_RGB16=0x4C424752 +CONFIG_FOURCC_BGR16=0x4C524742 +CONFIG_FOURCC_RGB16_BE=0x42424752 +CONFIG_FOURCC_BGR16_BE=0x42524742 +CONFIG_FOURCC_RGB24=0x33424752 +CONFIG_FOURCC_BGR24=0x33524742 +CONFIG_FOURCC_ARGB32=0x34324241 +CONFIG_FOURCC_ABGR32=0x34325241 +CONFIG_FOURCC_GREY=0x59455247 +CONFIG_FOURCC_Y16=0x4C363159 +CONFIG_FOURCC_Y16_BE=0x42363159 +CONFIG_FOURCC_YUYV=0x56595559 +CONFIG_FOURCC_UYVY=0x59565955 +CONFIG_FOURCC_YUV=0x38303356 +CONFIG_FOURCC_OUYY_EVYY=0x5645554F +CONFIG_FOURCC_NV12=0x3231564E +CONFIG_FOURCC_NV21=0x3132564E +CONFIG_FOURCC_YUV420P=0x32315559 +CONFIG_FOURCC_YVU420P=0x32315659 +CONFIG_FOURCC_YUV444P=0x50343434 +CONFIG_FOURCC_YUV420M=0x32314D59 +CONFIG_FOURCC_YVU420M=0x31324D59 +CONFIG_FOURCC_YUV444M=0x34324D59 +CONFIG_FOURCC_RAW8=0x38574152 +CONFIG_FOURCC_RAW10=0x41574152 +CONFIG_FOURCC_RAW12=0x43574152 +CONFIG_FOURCC_RAW16=0x47574152 +# CONFIG_GMF_VIDEO_CODEC_INIT_DECODER is not set +# CONFIG_GMF_VIDEO_CODEC_INIT_ENCODER is not set +# end of GMF Video Codec + +# +# GMF Video Effects +# +# CONFIG_GMF_VIDEO_EFFECTS_INIT_FPS_CONVERT is not set +# CONFIG_GMF_VIDEO_EFFECTS_INIT_OVERLAY is not set +# CONFIG_GMF_VIDEO_EFFECTS_INIT_COLOR_CONVERT is not set +# CONFIG_GMF_VIDEO_EFFECTS_INIT_SCALE is not set +# CONFIG_GMF_VIDEO_EFFECTS_INIT_CROP is not set +# CONFIG_GMF_VIDEO_EFFECTS_INIT_ROTATE is not set +# end of GMF Video Effects +# end of GMF Video Configurations + +# +# GMF Miscellaneous +# +# CONFIG_GMF_MISC_INIT_COPIER is not set +# end of GMF Miscellaneous +# end of ESP GMF Loader + +# +# Compiler options +# +# CONFIG_COMPILER_OPTIMIZATION_DEBUG is not set +# CONFIG_COMPILER_OPTIMIZATION_SIZE is not set +CONFIG_COMPILER_OPTIMIZATION_PERF=y +# CONFIG_COMPILER_OPTIMIZATION_NONE is not set +CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_ENABLE=y +# CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT is not set +# CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_DISABLE is not set +CONFIG_COMPILER_ASSERT_NDEBUG_EVALUATE=y +CONFIG_COMPILER_FLOAT_LIB_FROM_GCCLIB=y +CONFIG_COMPILER_OPTIMIZATION_ASSERTION_LEVEL=2 +# CONFIG_COMPILER_OPTIMIZATION_CHECKS_SILENT is not set +CONFIG_COMPILER_HIDE_PATHS_MACROS=y +# CONFIG_COMPILER_CXX_EXCEPTIONS is not set +# CONFIG_COMPILER_CXX_RTTI is not set +CONFIG_COMPILER_STACK_CHECK_MODE_NONE=y +# CONFIG_COMPILER_STACK_CHECK_MODE_NORM is not set +# CONFIG_COMPILER_STACK_CHECK_MODE_STRONG is not set +# CONFIG_COMPILER_STACK_CHECK_MODE_ALL is not set +# CONFIG_COMPILER_NO_MERGE_CONSTANTS is not set +# CONFIG_COMPILER_WARN_WRITE_STRINGS is not set +CONFIG_COMPILER_DISABLE_DEFAULT_ERRORS=y +# CONFIG_COMPILER_DISABLE_GCC12_WARNINGS is not set +# CONFIG_COMPILER_DISABLE_GCC13_WARNINGS is not set +# CONFIG_COMPILER_DISABLE_GCC14_WARNINGS is not set +# CONFIG_COMPILER_DUMP_RTL_FILES is not set +CONFIG_COMPILER_RT_LIB_GCCLIB=y +CONFIG_COMPILER_RT_LIB_NAME="gcc" +CONFIG_COMPILER_ORPHAN_SECTIONS_WARNING=y +# CONFIG_COMPILER_ORPHAN_SECTIONS_PLACE is not set +# CONFIG_COMPILER_STATIC_ANALYZER is not set +# end of Compiler options + +# +# Component config +# + +# +# Application Level Tracing +# +# CONFIG_APPTRACE_DEST_JTAG is not set +CONFIG_APPTRACE_DEST_NONE=y +# CONFIG_APPTRACE_DEST_UART1 is not set +# CONFIG_APPTRACE_DEST_UART2 is not set +# CONFIG_APPTRACE_DEST_USB_CDC is not set +CONFIG_APPTRACE_DEST_UART_NONE=y +CONFIG_APPTRACE_UART_TASK_PRIO=1 +CONFIG_APPTRACE_LOCK_ENABLE=y +# end of Application Level Tracing + +# +# Bluetooth +# +# CONFIG_BT_ENABLED is not set + +# +# Common Options +# +# CONFIG_BT_BLE_LOG_SPI_OUT_ENABLED is not set +# CONFIG_BT_BLE_LOG_UHCI_OUT_ENABLED is not set +# end of Common Options +# end of Bluetooth + +# +# Console Library +# +# CONFIG_CONSOLE_SORTED_HELP is not set +# end of Console Library + +# +# Driver Configurations +# + +# +# Legacy TWAI Driver Configurations +# +# CONFIG_TWAI_SKIP_LEGACY_CONFLICT_CHECK is not set +CONFIG_TWAI_ERRATA_FIX_LISTEN_ONLY_DOM=y +# end of Legacy TWAI Driver Configurations + +# +# Legacy ADC Driver Configuration +# +# CONFIG_ADC_SUPPRESS_DEPRECATE_WARN is not set +# CONFIG_ADC_SKIP_LEGACY_CONFLICT_CHECK is not set + +# +# Legacy ADC Calibration Configuration +# +# CONFIG_ADC_CALI_SUPPRESS_DEPRECATE_WARN is not set +# end of Legacy ADC Calibration Configuration +# end of Legacy ADC Driver Configuration + +# +# Legacy MCPWM Driver Configurations +# +# CONFIG_MCPWM_SUPPRESS_DEPRECATE_WARN is not set +# CONFIG_MCPWM_SKIP_LEGACY_CONFLICT_CHECK is not set +# end of Legacy MCPWM Driver Configurations + +# +# Legacy Timer Group Driver Configurations +# +# CONFIG_GPTIMER_SUPPRESS_DEPRECATE_WARN is not set +# CONFIG_GPTIMER_SKIP_LEGACY_CONFLICT_CHECK is not set +# end of Legacy Timer Group Driver Configurations + +# +# Legacy RMT Driver Configurations +# +# CONFIG_RMT_SUPPRESS_DEPRECATE_WARN is not set +# CONFIG_RMT_SKIP_LEGACY_CONFLICT_CHECK is not set +# end of Legacy RMT Driver Configurations + +# +# Legacy I2S Driver Configurations +# +# CONFIG_I2S_SUPPRESS_DEPRECATE_WARN is not set +# CONFIG_I2S_SKIP_LEGACY_CONFLICT_CHECK is not set +# end of Legacy I2S Driver Configurations + +# +# Legacy I2C Driver Configurations +# +# CONFIG_I2C_SKIP_LEGACY_CONFLICT_CHECK is not set +# end of Legacy I2C Driver Configurations + +# +# Legacy PCNT Driver Configurations +# +# CONFIG_PCNT_SUPPRESS_DEPRECATE_WARN is not set +# CONFIG_PCNT_SKIP_LEGACY_CONFLICT_CHECK is not set +# end of Legacy PCNT Driver Configurations + +# +# Legacy SDM Driver Configurations +# +# CONFIG_SDM_SUPPRESS_DEPRECATE_WARN is not set +# CONFIG_SDM_SKIP_LEGACY_CONFLICT_CHECK is not set +# end of Legacy SDM Driver Configurations + +# +# Legacy Temperature Sensor Driver Configurations +# +# CONFIG_TEMP_SENSOR_SUPPRESS_DEPRECATE_WARN is not set +# CONFIG_TEMP_SENSOR_SKIP_LEGACY_CONFLICT_CHECK is not set +# end of Legacy Temperature Sensor Driver Configurations + +# +# Legacy Touch Sensor Driver Configurations +# +# CONFIG_TOUCH_SUPPRESS_DEPRECATE_WARN is not set +# CONFIG_TOUCH_SKIP_LEGACY_CONFLICT_CHECK is not set +# end of Legacy Touch Sensor Driver Configurations +# end of Driver Configurations + +# +# eFuse Bit Manager +# +# CONFIG_EFUSE_CUSTOM_TABLE is not set +# CONFIG_EFUSE_VIRTUAL is not set +CONFIG_EFUSE_MAX_BLK_LEN=256 +# end of eFuse Bit Manager + +# +# ESP-TLS +# +CONFIG_ESP_TLS_USING_MBEDTLS=y +# CONFIG_ESP_TLS_USE_SECURE_ELEMENT is not set +CONFIG_ESP_TLS_USE_DS_PERIPHERAL=y +# CONFIG_ESP_TLS_CLIENT_SESSION_TICKETS is not set +# CONFIG_ESP_TLS_SERVER_SESSION_TICKETS is not set +# CONFIG_ESP_TLS_SERVER_CERT_SELECT_HOOK is not set +# CONFIG_ESP_TLS_SERVER_MIN_AUTH_MODE_OPTIONAL is not set +# CONFIG_ESP_TLS_PSK_VERIFICATION is not set +CONFIG_ESP_TLS_INSECURE=y +CONFIG_ESP_TLS_SKIP_SERVER_CERT_VERIFY=y +CONFIG_ESP_TLS_DYN_BUF_STRATEGY_SUPPORTED=y +# end of ESP-TLS + +# +# ADC and ADC Calibration +# +# CONFIG_ADC_ONESHOT_CTRL_FUNC_IN_IRAM is not set +# CONFIG_ADC_CONTINUOUS_ISR_IRAM_SAFE is not set +# CONFIG_ADC_CONTINUOUS_FORCE_USE_ADC2_ON_C3_S3 is not set +# CONFIG_ADC_ENABLE_DEBUG_LOG is not set +# end of ADC and ADC Calibration + +# +# Wireless Coexistence +# +CONFIG_ESP_COEX_ENABLED=y +# CONFIG_ESP_COEX_EXTERNAL_COEXIST_ENABLE is not set +# CONFIG_ESP_COEX_GPIO_DEBUG is not set +# end of Wireless Coexistence + +# +# Common ESP-related +# +CONFIG_ESP_ERR_TO_NAME_LOOKUP=y +# end of Common ESP-related + +# +# ESP-Driver:Camera Controller Configurations +# +# CONFIG_CAM_CTLR_DVP_CAM_ISR_CACHE_SAFE is not set +# end of ESP-Driver:Camera Controller Configurations + +# +# ESP-Driver:GPIO Configurations +# +# CONFIG_GPIO_CTRL_FUNC_IN_IRAM is not set +# end of ESP-Driver:GPIO Configurations + +# +# ESP-Driver:GPTimer Configurations +# +CONFIG_GPTIMER_ISR_HANDLER_IN_IRAM=y +# CONFIG_GPTIMER_CTRL_FUNC_IN_IRAM is not set +# CONFIG_GPTIMER_ISR_CACHE_SAFE is not set +CONFIG_GPTIMER_OBJ_CACHE_SAFE=y +# CONFIG_GPTIMER_ENABLE_DEBUG_LOG is not set +# end of ESP-Driver:GPTimer Configurations + +# +# ESP-Driver:I2C Configurations +# +# CONFIG_I2C_ISR_IRAM_SAFE is not set +# CONFIG_I2C_ENABLE_DEBUG_LOG is not set +# CONFIG_I2C_ENABLE_SLAVE_DRIVER_VERSION_2 is not set +CONFIG_I2C_MASTER_ISR_HANDLER_IN_IRAM=y +# end of ESP-Driver:I2C Configurations + +# +# ESP-Driver:I2S Configurations +# +# CONFIG_I2S_ISR_IRAM_SAFE is not set +# CONFIG_I2S_ENABLE_DEBUG_LOG is not set +# end of ESP-Driver:I2S Configurations + +# +# ESP-Driver:LEDC Configurations +# +# CONFIG_LEDC_CTRL_FUNC_IN_IRAM is not set +# end of ESP-Driver:LEDC Configurations + +# +# ESP-Driver:MCPWM Configurations +# +CONFIG_MCPWM_ISR_HANDLER_IN_IRAM=y +# CONFIG_MCPWM_ISR_CACHE_SAFE is not set +# CONFIG_MCPWM_CTRL_FUNC_IN_IRAM is not set +CONFIG_MCPWM_OBJ_CACHE_SAFE=y +# CONFIG_MCPWM_ENABLE_DEBUG_LOG is not set +# end of ESP-Driver:MCPWM Configurations + +# +# ESP-Driver:PCNT Configurations +# +# CONFIG_PCNT_CTRL_FUNC_IN_IRAM is not set +# CONFIG_PCNT_ISR_IRAM_SAFE is not set +# CONFIG_PCNT_ENABLE_DEBUG_LOG is not set +# end of ESP-Driver:PCNT Configurations + +# +# ESP-Driver:RMT Configurations +# +CONFIG_RMT_ENCODER_FUNC_IN_IRAM=y +CONFIG_RMT_TX_ISR_HANDLER_IN_IRAM=y +CONFIG_RMT_RX_ISR_HANDLER_IN_IRAM=y +# CONFIG_RMT_RECV_FUNC_IN_IRAM is not set +# CONFIG_RMT_TX_ISR_CACHE_SAFE is not set +# CONFIG_RMT_RX_ISR_CACHE_SAFE is not set +CONFIG_RMT_OBJ_CACHE_SAFE=y +# CONFIG_RMT_ENABLE_DEBUG_LOG is not set +# CONFIG_RMT_ISR_IRAM_SAFE is not set +# end of ESP-Driver:RMT Configurations + +# +# ESP-Driver:Sigma Delta Modulator Configurations +# +# CONFIG_SDM_CTRL_FUNC_IN_IRAM is not set +# CONFIG_SDM_ENABLE_DEBUG_LOG is not set +# end of ESP-Driver:Sigma Delta Modulator Configurations + +# +# ESP-Driver:SPI Configurations +# +# CONFIG_SPI_MASTER_IN_IRAM is not set +CONFIG_SPI_MASTER_ISR_IN_IRAM=y +# CONFIG_SPI_SLAVE_IN_IRAM is not set +CONFIG_SPI_SLAVE_ISR_IN_IRAM=y +# end of ESP-Driver:SPI Configurations + +# +# ESP-Driver:Touch Sensor Configurations +# +# CONFIG_TOUCH_CTRL_FUNC_IN_IRAM is not set +# CONFIG_TOUCH_ISR_IRAM_SAFE is not set +# CONFIG_TOUCH_ENABLE_DEBUG_LOG is not set +# CONFIG_TOUCH_SKIP_FSM_CHECK is not set +# end of ESP-Driver:Touch Sensor Configurations + +# +# ESP-Driver:Temperature Sensor Configurations +# +# CONFIG_TEMP_SENSOR_ENABLE_DEBUG_LOG is not set +# end of ESP-Driver:Temperature Sensor Configurations + +# +# ESP-Driver:TWAI Configurations +# +# CONFIG_TWAI_ISR_IN_IRAM is not set +# CONFIG_TWAI_ISR_CACHE_SAFE is not set +# CONFIG_TWAI_ENABLE_DEBUG_LOG is not set +# end of ESP-Driver:TWAI Configurations + +# +# ESP-Driver:UART Configurations +# +# CONFIG_UART_ISR_IN_IRAM is not set +# end of ESP-Driver:UART Configurations + +# +# ESP-Driver:UHCI Configurations +# +# CONFIG_UHCI_ISR_HANDLER_IN_IRAM is not set +# CONFIG_UHCI_ISR_CACHE_SAFE is not set +# CONFIG_UHCI_ENABLE_DEBUG_LOG is not set +# end of ESP-Driver:UHCI Configurations + +# +# ESP-Driver:USB Serial/JTAG Configuration +# +CONFIG_USJ_ENABLE_USB_SERIAL_JTAG=y +# end of ESP-Driver:USB Serial/JTAG Configuration + +# +# Ethernet +# +CONFIG_ETH_ENABLED=y +CONFIG_ETH_USE_SPI_ETHERNET=y +# CONFIG_ETH_SPI_ETHERNET_DM9051 is not set +# CONFIG_ETH_SPI_ETHERNET_W5500 is not set +# CONFIG_ETH_SPI_ETHERNET_KSZ8851SNL is not set +# CONFIG_ETH_USE_OPENETH is not set +# CONFIG_ETH_TRANSMIT_MUTEX is not set +# end of Ethernet + +# +# Event Loop Library +# +# CONFIG_ESP_EVENT_LOOP_PROFILING is not set +CONFIG_ESP_EVENT_POST_FROM_ISR=y +CONFIG_ESP_EVENT_POST_FROM_IRAM_ISR=y +# end of Event Loop Library + +# +# GDB Stub +# +CONFIG_ESP_GDBSTUB_ENABLED=y +# CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME is not set +CONFIG_ESP_GDBSTUB_SUPPORT_TASKS=y +CONFIG_ESP_GDBSTUB_MAX_TASKS=32 +# end of GDB Stub + +# +# ESP HID +# +CONFIG_ESPHID_TASK_SIZE_BT=2048 +CONFIG_ESPHID_TASK_SIZE_BLE=4096 +# end of ESP HID + +# +# ESP HTTP client +# +CONFIG_ESP_HTTP_CLIENT_ENABLE_HTTPS=y +# CONFIG_ESP_HTTP_CLIENT_ENABLE_BASIC_AUTH is not set +# CONFIG_ESP_HTTP_CLIENT_ENABLE_DIGEST_AUTH is not set +# CONFIG_ESP_HTTP_CLIENT_ENABLE_CUSTOM_TRANSPORT is not set +CONFIG_ESP_HTTP_CLIENT_EVENT_POST_TIMEOUT=2000 +# end of ESP HTTP client + +# +# HTTP Server +# +CONFIG_HTTPD_MAX_REQ_HDR_LEN=1024 +CONFIG_HTTPD_MAX_URI_LEN=512 +CONFIG_HTTPD_ERR_RESP_NO_DELAY=y +CONFIG_HTTPD_PURGE_BUF_LEN=32 +# CONFIG_HTTPD_LOG_PURGE_DATA is not set +# CONFIG_HTTPD_WS_SUPPORT is not set +# CONFIG_HTTPD_QUEUE_WORK_BLOCKING is not set +CONFIG_HTTPD_SERVER_EVENT_POST_TIMEOUT=2000 +# end of HTTP Server + +# +# ESP HTTPS OTA +# +# CONFIG_ESP_HTTPS_OTA_DECRYPT_CB is not set +# CONFIG_ESP_HTTPS_OTA_ALLOW_HTTP is not set +CONFIG_ESP_HTTPS_OTA_EVENT_POST_TIMEOUT=2000 +# end of ESP HTTPS OTA + +# +# ESP HTTPS server +# +# CONFIG_ESP_HTTPS_SERVER_ENABLE is not set +CONFIG_ESP_HTTPS_SERVER_EVENT_POST_TIMEOUT=2000 +# CONFIG_ESP_HTTPS_SERVER_CERT_SELECT_HOOK is not set +# end of ESP HTTPS server + +# +# Hardware Settings +# + +# +# Chip revision +# +CONFIG_ESP32S3_REV_MIN_0=y +# CONFIG_ESP32S3_REV_MIN_1 is not set +# CONFIG_ESP32S3_REV_MIN_2 is not set +CONFIG_ESP32S3_REV_MIN_FULL=0 +CONFIG_ESP_REV_MIN_FULL=0 + +# +# Maximum Supported ESP32-S3 Revision (Rev v0.99) +# +CONFIG_ESP32S3_REV_MAX_FULL=99 +CONFIG_ESP_REV_MAX_FULL=99 +CONFIG_ESP_EFUSE_BLOCK_REV_MIN_FULL=0 +CONFIG_ESP_EFUSE_BLOCK_REV_MAX_FULL=199 + +# +# Maximum Supported ESP32-S3 eFuse Block Revision (eFuse Block Rev v1.99) +# +# end of Chip revision + +# +# MAC Config +# +CONFIG_ESP_MAC_ADDR_UNIVERSE_WIFI_STA=y +CONFIG_ESP_MAC_ADDR_UNIVERSE_WIFI_AP=y +CONFIG_ESP_MAC_ADDR_UNIVERSE_BT=y +CONFIG_ESP_MAC_ADDR_UNIVERSE_ETH=y +CONFIG_ESP_MAC_UNIVERSAL_MAC_ADDRESSES_FOUR=y +CONFIG_ESP_MAC_UNIVERSAL_MAC_ADDRESSES=4 +# CONFIG_ESP32S3_UNIVERSAL_MAC_ADDRESSES_TWO is not set +CONFIG_ESP32S3_UNIVERSAL_MAC_ADDRESSES_FOUR=y +CONFIG_ESP32S3_UNIVERSAL_MAC_ADDRESSES=4 +# CONFIG_ESP_MAC_USE_CUSTOM_MAC_AS_BASE_MAC is not set +# end of MAC Config + +# +# Sleep Config +# +CONFIG_ESP_SLEEP_FLASH_LEAKAGE_WORKAROUND=y +CONFIG_ESP_SLEEP_PSRAM_LEAKAGE_WORKAROUND=y +CONFIG_ESP_SLEEP_MSPI_NEED_ALL_IO_PU=y +CONFIG_ESP_SLEEP_RTC_BUS_ISO_WORKAROUND=y +CONFIG_ESP_SLEEP_GPIO_RESET_WORKAROUND=y +CONFIG_ESP_SLEEP_WAIT_FLASH_READY_EXTRA_DELAY=2000 +# CONFIG_ESP_SLEEP_CACHE_SAFE_ASSERTION is not set +# CONFIG_ESP_SLEEP_DEBUG is not set +CONFIG_ESP_SLEEP_GPIO_ENABLE_INTERNAL_RESISTORS=y +# end of Sleep Config + +# +# RTC Clock Config +# +CONFIG_RTC_CLK_SRC_INT_RC=y +# CONFIG_RTC_CLK_SRC_EXT_CRYS is not set +# CONFIG_RTC_CLK_SRC_EXT_OSC is not set +# CONFIG_RTC_CLK_SRC_INT_8MD256 is not set +CONFIG_RTC_CLK_CAL_CYCLES=1024 +# end of RTC Clock Config + +# +# Peripheral Control +# +CONFIG_ESP_PERIPH_CTRL_FUNC_IN_IRAM=y +CONFIG_ESP_REGI2C_CTRL_FUNC_IN_IRAM=y +# end of Peripheral Control + +# +# GDMA Configurations +# +CONFIG_GDMA_CTRL_FUNC_IN_IRAM=y +CONFIG_GDMA_ISR_HANDLER_IN_IRAM=y +CONFIG_GDMA_OBJ_DRAM_SAFE=y +# CONFIG_GDMA_ENABLE_DEBUG_LOG is not set +# CONFIG_GDMA_ISR_IRAM_SAFE is not set +# end of GDMA Configurations + +# +# Main XTAL Config +# +CONFIG_XTAL_FREQ_40=y +CONFIG_XTAL_FREQ=40 +# end of Main XTAL Config + +# +# Power Supplier +# + +# +# Brownout Detector +# +CONFIG_ESP_BROWNOUT_DET=y +CONFIG_ESP_BROWNOUT_DET_LVL_SEL_7=y +# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_6 is not set +# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_5 is not set +# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_4 is not set +# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_3 is not set +# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_2 is not set +# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_1 is not set +CONFIG_ESP_BROWNOUT_DET_LVL=7 +CONFIG_ESP_BROWNOUT_USE_INTR=y +# end of Brownout Detector +# end of Power Supplier + +CONFIG_ESP_SPI_BUS_LOCK_ISR_FUNCS_IN_IRAM=y +CONFIG_ESP_INTR_IN_IRAM=y +# end of Hardware Settings + +# +# ESP-Driver:LCD Controller Configurations +# +# CONFIG_LCD_ENABLE_DEBUG_LOG is not set +# CONFIG_LCD_RGB_ISR_IRAM_SAFE is not set +# CONFIG_LCD_RGB_RESTART_IN_VSYNC is not set +# end of ESP-Driver:LCD Controller Configurations + +# +# ESP-MM: Memory Management Configurations +# +# CONFIG_ESP_MM_CACHE_MSYNC_C2M_CHUNKED_OPS is not set +# end of ESP-MM: Memory Management Configurations + +# +# ESP NETIF Adapter +# +CONFIG_ESP_NETIF_IP_LOST_TIMER_INTERVAL=120 +# CONFIG_ESP_NETIF_PROVIDE_CUSTOM_IMPLEMENTATION is not set +CONFIG_ESP_NETIF_TCPIP_LWIP=y +# CONFIG_ESP_NETIF_LOOPBACK is not set +CONFIG_ESP_NETIF_USES_TCPIP_WITH_BSD_API=y +CONFIG_ESP_NETIF_REPORT_DATA_TRAFFIC=y +# CONFIG_ESP_NETIF_RECEIVE_REPORT_ERRORS is not set +# CONFIG_ESP_NETIF_L2_TAP is not set +# CONFIG_ESP_NETIF_BRIDGE_EN is not set +# CONFIG_ESP_NETIF_SET_DNS_PER_DEFAULT_NETIF is not set +# end of ESP NETIF Adapter + +# +# Partition API Configuration +# +# end of Partition API Configuration + +# +# PHY +# +CONFIG_ESP_PHY_ENABLED=y +CONFIG_ESP_PHY_CALIBRATION_AND_DATA_STORAGE=y +# CONFIG_ESP_PHY_INIT_DATA_IN_PARTITION is not set +CONFIG_ESP_PHY_MAX_WIFI_TX_POWER=20 +CONFIG_ESP_PHY_MAX_TX_POWER=20 +# CONFIG_ESP_PHY_REDUCE_TX_POWER is not set +CONFIG_ESP_PHY_ENABLE_USB=y +# CONFIG_ESP_PHY_ENABLE_CERT_TEST is not set +CONFIG_ESP_PHY_RF_CAL_PARTIAL=y +# CONFIG_ESP_PHY_RF_CAL_NONE is not set +# CONFIG_ESP_PHY_RF_CAL_FULL is not set +CONFIG_ESP_PHY_CALIBRATION_MODE=0 +CONFIG_ESP_PHY_PLL_TRACK_PERIOD_MS=1000 +# CONFIG_ESP_PHY_PLL_TRACK_DEBUG is not set +# CONFIG_ESP_PHY_RECORD_USED_TIME is not set +CONFIG_ESP_PHY_IRAM_OPT=y +# CONFIG_ESP_PHY_DEBUG is not set +# end of PHY + +# +# Power Management +# +CONFIG_PM_SLEEP_FUNC_IN_IRAM=y +# CONFIG_PM_ENABLE is not set +CONFIG_PM_SLP_IRAM_OPT=y +CONFIG_PM_POWER_DOWN_CPU_IN_LIGHT_SLEEP=y +CONFIG_PM_RESTORE_CACHE_TAGMEM_AFTER_LIGHT_SLEEP=y +# end of Power Management + +# +# ESP PSRAM +# +CONFIG_SPIRAM=y + +# +# SPI RAM config +# +# CONFIG_SPIRAM_MODE_QUAD is not set +CONFIG_SPIRAM_MODE_OCT=y +CONFIG_SPIRAM_TYPE_AUTO=y +# CONFIG_SPIRAM_TYPE_ESPPSRAM64 is not set +CONFIG_SPIRAM_CLK_IO=30 +CONFIG_SPIRAM_CS_IO=26 +CONFIG_SPIRAM_XIP_FROM_PSRAM=y +CONFIG_SPIRAM_FETCH_INSTRUCTIONS=y +CONFIG_SPIRAM_RODATA=y +# CONFIG_SPIRAM_SPEED_120M is not set +CONFIG_SPIRAM_SPEED_80M=y +# CONFIG_SPIRAM_SPEED_40M is not set +CONFIG_SPIRAM_SPEED=80 +# CONFIG_SPIRAM_ECC_ENABLE is not set +CONFIG_SPIRAM_BOOT_HW_INIT=y +CONFIG_SPIRAM_BOOT_INIT=y +CONFIG_SPIRAM_PRE_CONFIGURE_MEMORY_PROTECTION=y +# CONFIG_SPIRAM_IGNORE_NOTFOUND is not set +# CONFIG_SPIRAM_USE_CAPS_ALLOC is not set +CONFIG_SPIRAM_USE_MALLOC=y +CONFIG_SPIRAM_MEMTEST=y +CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=4096 +CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP=y +CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL=32768 +# CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY is not set +# CONFIG_SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY is not set +# end of SPI RAM config +# end of ESP PSRAM + +# +# ESP Ringbuf +# +# CONFIG_RINGBUF_PLACE_FUNCTIONS_INTO_FLASH is not set +# end of ESP Ringbuf + +# +# ESP-ROM +# +CONFIG_ESP_ROM_PRINT_IN_IRAM=y +# end of ESP-ROM + +# +# ESP Security Specific +# +# end of ESP Security Specific + +# +# ESP System Settings +# +# CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_80 is not set +# CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_160 is not set +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ=240 + +# +# Cache config +# +# CONFIG_ESP32S3_INSTRUCTION_CACHE_16KB is not set +CONFIG_ESP32S3_INSTRUCTION_CACHE_32KB=y +CONFIG_ESP32S3_INSTRUCTION_CACHE_SIZE=0x8000 +# CONFIG_ESP32S3_INSTRUCTION_CACHE_4WAYS is not set +CONFIG_ESP32S3_INSTRUCTION_CACHE_8WAYS=y +CONFIG_ESP32S3_ICACHE_ASSOCIATED_WAYS=8 +CONFIG_ESP32S3_INSTRUCTION_CACHE_LINE_32B=y +CONFIG_ESP32S3_INSTRUCTION_CACHE_LINE_SIZE=32 +# CONFIG_ESP32S3_DATA_CACHE_16KB is not set +# CONFIG_ESP32S3_DATA_CACHE_32KB is not set +CONFIG_ESP32S3_DATA_CACHE_64KB=y +CONFIG_ESP32S3_DATA_CACHE_SIZE=0x10000 +# CONFIG_ESP32S3_DATA_CACHE_4WAYS is not set +CONFIG_ESP32S3_DATA_CACHE_8WAYS=y +CONFIG_ESP32S3_DCACHE_ASSOCIATED_WAYS=8 +# CONFIG_ESP32S3_DATA_CACHE_LINE_32B is not set +CONFIG_ESP32S3_DATA_CACHE_LINE_64B=y +CONFIG_ESP32S3_DATA_CACHE_LINE_SIZE=64 +# end of Cache config + +# +# Memory +# +# CONFIG_ESP32S3_RTCDATA_IN_FAST_MEM is not set +# CONFIG_ESP32S3_USE_FIXED_STATIC_RAM_SIZE is not set +# end of Memory + +# +# Trace memory +# +# CONFIG_ESP32S3_TRAX is not set +CONFIG_ESP32S3_TRACEMEM_RESERVE_DRAM=0x0 +# end of Trace memory + +CONFIG_ESP_SYSTEM_IN_IRAM=y +# CONFIG_ESP_SYSTEM_PANIC_PRINT_HALT is not set +CONFIG_ESP_SYSTEM_PANIC_PRINT_REBOOT=y +# CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT is not set +# CONFIG_ESP_SYSTEM_PANIC_GDBSTUB is not set +CONFIG_ESP_SYSTEM_PANIC_REBOOT_DELAY_SECONDS=0 +CONFIG_ESP_SYSTEM_RTC_FAST_MEM_AS_HEAP_DEPCHECK=y +CONFIG_ESP_SYSTEM_ALLOW_RTC_FAST_MEM_AS_HEAP=y + +# +# Memory protection +# +CONFIG_ESP_SYSTEM_MEMPROT_FEATURE=y +CONFIG_ESP_SYSTEM_MEMPROT_FEATURE_LOCK=y +# end of Memory protection + +CONFIG_ESP_SYSTEM_EVENT_QUEUE_SIZE=32 +CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=2304 +CONFIG_ESP_MAIN_TASK_STACK_SIZE=4096 +CONFIG_ESP_MAIN_TASK_AFFINITY_CPU0=y +# CONFIG_ESP_MAIN_TASK_AFFINITY_CPU1 is not set +# CONFIG_ESP_MAIN_TASK_AFFINITY_NO_AFFINITY is not set +CONFIG_ESP_MAIN_TASK_AFFINITY=0x0 +CONFIG_ESP_MINIMAL_SHARED_STACK_SIZE=2048 +CONFIG_ESP_CONSOLE_UART_DEFAULT=y +# CONFIG_ESP_CONSOLE_USB_CDC is not set +# CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG is not set +# CONFIG_ESP_CONSOLE_UART_CUSTOM is not set +# CONFIG_ESP_CONSOLE_NONE is not set +# CONFIG_ESP_CONSOLE_SECONDARY_NONE is not set +CONFIG_ESP_CONSOLE_SECONDARY_USB_SERIAL_JTAG=y +CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG_ENABLED=y +CONFIG_ESP_CONSOLE_UART=y +CONFIG_ESP_CONSOLE_UART_NUM=0 +CONFIG_ESP_CONSOLE_ROM_SERIAL_PORT_NUM=0 +CONFIG_ESP_CONSOLE_UART_BAUDRATE=115200 +CONFIG_ESP_INT_WDT=y +CONFIG_ESP_INT_WDT_TIMEOUT_MS=300 +CONFIG_ESP_INT_WDT_CHECK_CPU1=y +CONFIG_ESP_TASK_WDT_EN=y +CONFIG_ESP_TASK_WDT_INIT=y +# CONFIG_ESP_TASK_WDT_PANIC is not set +CONFIG_ESP_TASK_WDT_TIMEOUT_S=10 +CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0=y +CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU1=y +# CONFIG_ESP_PANIC_HANDLER_IRAM is not set +# CONFIG_ESP_DEBUG_STUBS_ENABLE is not set +CONFIG_ESP_DEBUG_OCDAWARE=y +CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_4=y +CONFIG_ESP_SYSTEM_BBPLL_RECALIB=y +# end of ESP System Settings + +# +# IPC (Inter-Processor Call) +# +CONFIG_ESP_IPC_ENABLE=y +CONFIG_ESP_IPC_TASK_STACK_SIZE=1280 +CONFIG_ESP_IPC_USES_CALLERS_PRIORITY=y +CONFIG_ESP_IPC_ISR_ENABLE=y +# end of IPC (Inter-Processor Call) + +# +# ESP Timer (High Resolution Timer) +# +CONFIG_ESP_TIMER_IN_IRAM=y +# CONFIG_ESP_TIMER_PROFILING is not set +CONFIG_ESP_TIME_FUNCS_USE_RTC_TIMER=y +CONFIG_ESP_TIME_FUNCS_USE_ESP_TIMER=y +CONFIG_ESP_TIMER_TASK_STACK_SIZE=3584 +CONFIG_ESP_TIMER_INTERRUPT_LEVEL=1 +# CONFIG_ESP_TIMER_SHOW_EXPERIMENTAL is not set +CONFIG_ESP_TIMER_TASK_AFFINITY=0x0 +CONFIG_ESP_TIMER_TASK_AFFINITY_CPU0=y +CONFIG_ESP_TIMER_ISR_AFFINITY_CPU0=y +# CONFIG_ESP_TIMER_SUPPORTS_ISR_DISPATCH_METHOD is not set +CONFIG_ESP_TIMER_IMPL_SYSTIMER=y +# end of ESP Timer (High Resolution Timer) + +# +# Wi-Fi +# +CONFIG_ESP_WIFI_ENABLED=y +CONFIG_ESP_WIFI_STATIC_RX_BUFFER_NUM=16 +CONFIG_ESP_WIFI_DYNAMIC_RX_BUFFER_NUM=32 +CONFIG_ESP_WIFI_STATIC_TX_BUFFER=y +CONFIG_ESP_WIFI_TX_BUFFER_TYPE=0 +CONFIG_ESP_WIFI_STATIC_TX_BUFFER_NUM=16 +CONFIG_ESP_WIFI_CACHE_TX_BUFFER_NUM=32 +CONFIG_ESP_WIFI_STATIC_RX_MGMT_BUFFER=y +# CONFIG_ESP_WIFI_DYNAMIC_RX_MGMT_BUFFER is not set +CONFIG_ESP_WIFI_DYNAMIC_RX_MGMT_BUF=0 +CONFIG_ESP_WIFI_RX_MGMT_BUF_NUM_DEF=5 +# CONFIG_ESP_WIFI_CSI_ENABLED is not set +CONFIG_ESP_WIFI_AMPDU_TX_ENABLED=y +CONFIG_ESP_WIFI_TX_BA_WIN=6 +CONFIG_ESP_WIFI_AMPDU_RX_ENABLED=y +CONFIG_ESP_WIFI_RX_BA_WIN=16 +# CONFIG_ESP_WIFI_AMSDU_TX_ENABLED is not set +CONFIG_ESP_WIFI_NVS_ENABLED=y +CONFIG_ESP_WIFI_TASK_PINNED_TO_CORE_0=y +# CONFIG_ESP_WIFI_TASK_PINNED_TO_CORE_1 is not set +CONFIG_ESP_WIFI_SOFTAP_BEACON_MAX_LEN=752 +CONFIG_ESP_WIFI_MGMT_SBUF_NUM=32 +CONFIG_ESP_WIFI_IRAM_OPT=y +# CONFIG_ESP_WIFI_EXTRA_IRAM_OPT is not set +CONFIG_ESP_WIFI_RX_IRAM_OPT=y +CONFIG_ESP_WIFI_ENABLE_WPA3_SAE=y +CONFIG_ESP_WIFI_ENABLE_SAE_PK=y +CONFIG_ESP_WIFI_ENABLE_SAE_H2E=y +CONFIG_ESP_WIFI_SOFTAP_SAE_SUPPORT=y +CONFIG_ESP_WIFI_ENABLE_WPA3_OWE_STA=y +# CONFIG_ESP_WIFI_SLP_IRAM_OPT is not set +CONFIG_ESP_WIFI_SLP_DEFAULT_MIN_ACTIVE_TIME=50 +# CONFIG_ESP_WIFI_BSS_MAX_IDLE_SUPPORT is not set +CONFIG_ESP_WIFI_SLP_DEFAULT_MAX_ACTIVE_TIME=10 +CONFIG_ESP_WIFI_SLP_DEFAULT_WAIT_BROADCAST_DATA_TIME=15 +# CONFIG_ESP_WIFI_FTM_ENABLE is not set +CONFIG_ESP_WIFI_STA_DISCONNECTED_PM_ENABLE=y +# CONFIG_ESP_WIFI_GCMP_SUPPORT is not set +CONFIG_ESP_WIFI_GMAC_SUPPORT=y +CONFIG_ESP_WIFI_SOFTAP_SUPPORT=y +# CONFIG_ESP_WIFI_SLP_BEACON_LOST_OPT is not set +CONFIG_ESP_WIFI_ESPNOW_MAX_ENCRYPT_NUM=7 +CONFIG_ESP_WIFI_MBEDTLS_CRYPTO=y +CONFIG_ESP_WIFI_MBEDTLS_TLS_CLIENT=y +# CONFIG_ESP_WIFI_EAP_TLS1_3 is not set +# CONFIG_ESP_WIFI_WAPI_PSK is not set +# CONFIG_ESP_WIFI_SUITE_B_192 is not set +# CONFIG_ESP_WIFI_11KV_SUPPORT is not set +# CONFIG_ESP_WIFI_MBO_SUPPORT is not set +# CONFIG_ESP_WIFI_ENABLE_ROAMING_APP is not set +# CONFIG_ESP_WIFI_DPP_SUPPORT is not set +# CONFIG_ESP_WIFI_11R_SUPPORT is not set +# CONFIG_ESP_WIFI_WPS_SOFTAP_REGISTRAR is not set + +# +# WPS Configuration Options +# +# CONFIG_ESP_WIFI_WPS_STRICT is not set +# CONFIG_ESP_WIFI_WPS_PASSPHRASE is not set +# end of WPS Configuration Options + +# CONFIG_ESP_WIFI_DEBUG_PRINT is not set +# CONFIG_ESP_WIFI_TESTING_OPTIONS is not set +CONFIG_ESP_WIFI_ENTERPRISE_SUPPORT=y +# CONFIG_ESP_WIFI_ENT_FREE_DYNAMIC_BUFFER is not set +# end of Wi-Fi + +# +# Core dump +# +# CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH is not set +# CONFIG_ESP_COREDUMP_ENABLE_TO_UART is not set +CONFIG_ESP_COREDUMP_ENABLE_TO_NONE=y +# end of Core dump + +# +# FAT Filesystem support +# +CONFIG_FATFS_VOLUME_COUNT=2 +CONFIG_FATFS_LFN_NONE=y +# CONFIG_FATFS_LFN_HEAP is not set +# CONFIG_FATFS_LFN_STACK is not set +# CONFIG_FATFS_SECTOR_512 is not set +CONFIG_FATFS_SECTOR_4096=y +# CONFIG_FATFS_CODEPAGE_DYNAMIC is not set +CONFIG_FATFS_CODEPAGE_437=y +# CONFIG_FATFS_CODEPAGE_720 is not set +# CONFIG_FATFS_CODEPAGE_737 is not set +# CONFIG_FATFS_CODEPAGE_771 is not set +# CONFIG_FATFS_CODEPAGE_775 is not set +# CONFIG_FATFS_CODEPAGE_850 is not set +# CONFIG_FATFS_CODEPAGE_852 is not set +# CONFIG_FATFS_CODEPAGE_855 is not set +# CONFIG_FATFS_CODEPAGE_857 is not set +# CONFIG_FATFS_CODEPAGE_860 is not set +# CONFIG_FATFS_CODEPAGE_861 is not set +# CONFIG_FATFS_CODEPAGE_862 is not set +# CONFIG_FATFS_CODEPAGE_863 is not set +# CONFIG_FATFS_CODEPAGE_864 is not set +# CONFIG_FATFS_CODEPAGE_865 is not set +# CONFIG_FATFS_CODEPAGE_866 is not set +# CONFIG_FATFS_CODEPAGE_869 is not set +# CONFIG_FATFS_CODEPAGE_932 is not set +# CONFIG_FATFS_CODEPAGE_936 is not set +# CONFIG_FATFS_CODEPAGE_949 is not set +# CONFIG_FATFS_CODEPAGE_950 is not set +CONFIG_FATFS_CODEPAGE=437 +CONFIG_FATFS_FS_LOCK=0 +CONFIG_FATFS_TIMEOUT_MS=10000 +CONFIG_FATFS_PER_FILE_CACHE=y +CONFIG_FATFS_ALLOC_PREFER_EXTRAM=y +# CONFIG_FATFS_USE_FASTSEEK is not set +CONFIG_FATFS_USE_STRFUNC_NONE=y +# CONFIG_FATFS_USE_STRFUNC_WITHOUT_CRLF_CONV is not set +# CONFIG_FATFS_USE_STRFUNC_WITH_CRLF_CONV is not set +CONFIG_FATFS_VFS_FSTAT_BLKSIZE=0 +# CONFIG_FATFS_IMMEDIATE_FSYNC is not set +# CONFIG_FATFS_USE_LABEL is not set +CONFIG_FATFS_LINK_LOCK=y +# CONFIG_FATFS_USE_DYN_BUFFERS is not set + +# +# File system free space calculation behavior +# +CONFIG_FATFS_DONT_TRUST_FREE_CLUSTER_CNT=0 +CONFIG_FATFS_DONT_TRUST_LAST_ALLOC=0 +# end of File system free space calculation behavior +# end of FAT Filesystem support + +# +# FreeRTOS +# + +# +# Kernel +# +# CONFIG_FREERTOS_SMP is not set +# CONFIG_FREERTOS_UNICORE is not set +CONFIG_FREERTOS_HZ=1000 +# CONFIG_FREERTOS_CHECK_STACKOVERFLOW_NONE is not set +# CONFIG_FREERTOS_CHECK_STACKOVERFLOW_PTRVAL is not set +CONFIG_FREERTOS_CHECK_STACKOVERFLOW_CANARY=y +CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS=1 +CONFIG_FREERTOS_IDLE_TASK_STACKSIZE=1536 +# CONFIG_FREERTOS_USE_IDLE_HOOK is not set +# CONFIG_FREERTOS_USE_TICK_HOOK is not set +CONFIG_FREERTOS_MAX_TASK_NAME_LEN=16 +CONFIG_FREERTOS_ENABLE_BACKWARD_COMPATIBILITY=y +CONFIG_FREERTOS_USE_TIMERS=y +CONFIG_FREERTOS_TIMER_SERVICE_TASK_NAME="Tmr Svc" +# CONFIG_FREERTOS_TIMER_TASK_AFFINITY_CPU0 is not set +# CONFIG_FREERTOS_TIMER_TASK_AFFINITY_CPU1 is not set +CONFIG_FREERTOS_TIMER_TASK_NO_AFFINITY=y +CONFIG_FREERTOS_TIMER_SERVICE_TASK_CORE_AFFINITY=0x7FFFFFFF +CONFIG_FREERTOS_TIMER_TASK_PRIORITY=1 +CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=4096 +CONFIG_FREERTOS_TIMER_QUEUE_LENGTH=10 +CONFIG_FREERTOS_QUEUE_REGISTRY_SIZE=0 +CONFIG_FREERTOS_TASK_NOTIFICATION_ARRAY_ENTRIES=1 +CONFIG_FREERTOS_USE_TRACE_FACILITY=y +CONFIG_FREERTOS_USE_STATS_FORMATTING_FUNCTIONS=y +# CONFIG_FREERTOS_USE_LIST_DATA_INTEGRITY_CHECK_BYTES is not set +CONFIG_FREERTOS_VTASKLIST_INCLUDE_COREID=y +CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS=y +CONFIG_FREERTOS_RUN_TIME_COUNTER_TYPE_U32=y +# CONFIG_FREERTOS_RUN_TIME_COUNTER_TYPE_U64 is not set +# CONFIG_FREERTOS_USE_APPLICATION_TASK_TAG is not set +# end of Kernel + +# +# Port +# +# CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK is not set +CONFIG_FREERTOS_TLSP_DELETION_CALLBACKS=y +# CONFIG_FREERTOS_TASK_PRE_DELETION_HOOK is not set +# CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP is not set +CONFIG_FREERTOS_CHECK_MUTEX_GIVEN_BY_OWNER=y +CONFIG_FREERTOS_ISR_STACKSIZE=1536 +CONFIG_FREERTOS_INTERRUPT_BACKTRACE=y +# CONFIG_FREERTOS_FPU_IN_ISR is not set +CONFIG_FREERTOS_TICK_SUPPORT_SYSTIMER=y +CONFIG_FREERTOS_CORETIMER_SYSTIMER_LVL1=y +# CONFIG_FREERTOS_CORETIMER_SYSTIMER_LVL3 is not set +CONFIG_FREERTOS_SYSTICK_USES_SYSTIMER=y +CONFIG_FREERTOS_RUN_TIME_STATS_USING_ESP_TIMER=y +# CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH is not set +# CONFIG_FREERTOS_CHECK_PORT_CRITICAL_COMPLIANCE is not set +# end of Port + +# +# Extra +# +CONFIG_FREERTOS_TASK_CREATE_ALLOW_EXT_MEM=y +# end of Extra + +CONFIG_FREERTOS_PORT=y +CONFIG_FREERTOS_NO_AFFINITY=0x7FFFFFFF +CONFIG_FREERTOS_SUPPORT_STATIC_ALLOCATION=y +CONFIG_FREERTOS_DEBUG_OCDAWARE=y +CONFIG_FREERTOS_ENABLE_TASK_SNAPSHOT=y +CONFIG_FREERTOS_PLACE_SNAPSHOT_FUNS_INTO_FLASH=y +CONFIG_FREERTOS_NUMBER_OF_CORES=2 +CONFIG_FREERTOS_IN_IRAM=y +# end of FreeRTOS + +# +# Hardware Abstraction Layer (HAL) and Low Level (LL) +# +CONFIG_HAL_ASSERTION_EQUALS_SYSTEM=y +# CONFIG_HAL_ASSERTION_DISABLE is not set +# CONFIG_HAL_ASSERTION_SILENT is not set +# CONFIG_HAL_ASSERTION_ENABLE is not set +CONFIG_HAL_DEFAULT_ASSERTION_LEVEL=2 +CONFIG_HAL_WDT_USE_ROM_IMPL=y +# end of Hardware Abstraction Layer (HAL) and Low Level (LL) + +# +# Heap memory debugging +# +CONFIG_HEAP_POISONING_DISABLED=y +# CONFIG_HEAP_POISONING_LIGHT is not set +# CONFIG_HEAP_POISONING_COMPREHENSIVE is not set +CONFIG_HEAP_TRACING_OFF=y +# CONFIG_HEAP_TRACING_STANDALONE is not set +# CONFIG_HEAP_TRACING_TOHOST is not set +# CONFIG_HEAP_USE_HOOKS is not set +# CONFIG_HEAP_TASK_TRACKING is not set +# CONFIG_HEAP_ABORT_WHEN_ALLOCATION_FAILS is not set +# CONFIG_HEAP_PLACE_FUNCTION_INTO_FLASH is not set +# end of Heap memory debugging + +# +# Log +# +CONFIG_LOG_VERSION_1=y +# CONFIG_LOG_VERSION_2 is not set +CONFIG_LOG_VERSION=1 + +# +# Log Level +# +# CONFIG_LOG_DEFAULT_LEVEL_NONE is not set +# CONFIG_LOG_DEFAULT_LEVEL_ERROR is not set +# CONFIG_LOG_DEFAULT_LEVEL_WARN is not set +CONFIG_LOG_DEFAULT_LEVEL_INFO=y +# CONFIG_LOG_DEFAULT_LEVEL_DEBUG is not set +# CONFIG_LOG_DEFAULT_LEVEL_VERBOSE is not set +CONFIG_LOG_DEFAULT_LEVEL=3 +CONFIG_LOG_MAXIMUM_EQUALS_DEFAULT=y +# CONFIG_LOG_MAXIMUM_LEVEL_DEBUG is not set +# CONFIG_LOG_MAXIMUM_LEVEL_VERBOSE is not set +CONFIG_LOG_MAXIMUM_LEVEL=3 + +# +# Level Settings +# +# CONFIG_LOG_MASTER_LEVEL is not set +CONFIG_LOG_DYNAMIC_LEVEL_CONTROL=y +# CONFIG_LOG_TAG_LEVEL_IMPL_NONE is not set +# CONFIG_LOG_TAG_LEVEL_IMPL_LINKED_LIST is not set +CONFIG_LOG_TAG_LEVEL_IMPL_CACHE_AND_LINKED_LIST=y +# CONFIG_LOG_TAG_LEVEL_CACHE_ARRAY is not set +CONFIG_LOG_TAG_LEVEL_CACHE_BINARY_MIN_HEAP=y +CONFIG_LOG_TAG_LEVEL_IMPL_CACHE_SIZE=31 +# end of Level Settings +# end of Log Level + +# +# Format +# +# CONFIG_LOG_COLORS is not set +CONFIG_LOG_TIMESTAMP_SOURCE_RTOS=y +# CONFIG_LOG_TIMESTAMP_SOURCE_SYSTEM is not set +# end of Format + +# +# Settings +# +CONFIG_LOG_MODE_TEXT_EN=y +CONFIG_LOG_MODE_TEXT=y +# end of Settings + +CONFIG_LOG_IN_IRAM=y +# end of Log + +# +# LWIP +# +CONFIG_LWIP_ENABLE=y +CONFIG_LWIP_LOCAL_HOSTNAME="espressif" +CONFIG_LWIP_TCPIP_TASK_PRIO=18 +# CONFIG_LWIP_TCPIP_CORE_LOCKING is not set +# CONFIG_LWIP_CHECK_THREAD_SAFETY is not set +CONFIG_LWIP_DNS_SUPPORT_MDNS_QUERIES=y +# CONFIG_LWIP_L2_TO_L3_COPY is not set +# CONFIG_LWIP_IRAM_OPTIMIZATION is not set +# CONFIG_LWIP_EXTRA_IRAM_OPTIMIZATION is not set +CONFIG_LWIP_TIMERS_ONDEMAND=y +CONFIG_LWIP_ND6=y +# CONFIG_LWIP_FORCE_ROUTER_FORWARDING is not set +CONFIG_LWIP_MAX_SOCKETS=10 +# CONFIG_LWIP_USE_ONLY_LWIP_SELECT is not set +# CONFIG_LWIP_SO_LINGER is not set +CONFIG_LWIP_SO_REUSE=y +CONFIG_LWIP_SO_REUSE_RXTOALL=y +# CONFIG_LWIP_SO_RCVBUF is not set +# CONFIG_LWIP_NETBUF_RECVINFO is not set +CONFIG_LWIP_IP_DEFAULT_TTL=64 +CONFIG_LWIP_IP4_FRAG=y +CONFIG_LWIP_IP6_FRAG=y +# CONFIG_LWIP_IP4_REASSEMBLY is not set +# CONFIG_LWIP_IP6_REASSEMBLY is not set +CONFIG_LWIP_IP_REASS_MAX_PBUFS=10 +# CONFIG_LWIP_IP_FORWARD is not set +# CONFIG_LWIP_STATS is not set +CONFIG_LWIP_ESP_GRATUITOUS_ARP=y +CONFIG_LWIP_GARP_TMR_INTERVAL=60 +CONFIG_LWIP_ESP_MLDV6_REPORT=y +CONFIG_LWIP_MLDV6_TMR_INTERVAL=40 +CONFIG_LWIP_TCPIP_RECVMBOX_SIZE=32 +CONFIG_LWIP_DHCP_DOES_ARP_CHECK=y +# CONFIG_LWIP_DHCP_DOES_ACD_CHECK is not set +# CONFIG_LWIP_DHCP_DOES_NOT_CHECK_OFFERED_IP is not set +# CONFIG_LWIP_DHCP_DISABLE_CLIENT_ID is not set +CONFIG_LWIP_DHCP_DISABLE_VENDOR_CLASS_ID=y +# CONFIG_LWIP_DHCP_RESTORE_LAST_IP is not set +CONFIG_LWIP_DHCP_OPTIONS_LEN=69 +CONFIG_LWIP_NUM_NETIF_CLIENT_DATA=0 +CONFIG_LWIP_DHCP_COARSE_TIMER_SECS=1 + +# +# DHCP server +# +CONFIG_LWIP_DHCPS=y +CONFIG_LWIP_DHCPS_LEASE_UNIT=60 +CONFIG_LWIP_DHCPS_MAX_STATION_NUM=8 +CONFIG_LWIP_DHCPS_STATIC_ENTRIES=y +CONFIG_LWIP_DHCPS_ADD_DNS=y +# end of DHCP server + +# CONFIG_LWIP_AUTOIP is not set +CONFIG_LWIP_IPV4=y +CONFIG_LWIP_IPV6=y +# CONFIG_LWIP_IPV6_AUTOCONFIG is not set +CONFIG_LWIP_IPV6_NUM_ADDRESSES=3 +# CONFIG_LWIP_IPV6_FORWARD is not set +# CONFIG_LWIP_NETIF_STATUS_CALLBACK is not set +CONFIG_LWIP_NETIF_LOOPBACK=y +CONFIG_LWIP_LOOPBACK_MAX_PBUFS=8 + +# +# TCP +# +CONFIG_LWIP_MAX_ACTIVE_TCP=16 +CONFIG_LWIP_MAX_LISTENING_TCP=16 +CONFIG_LWIP_TCP_HIGH_SPEED_RETRANSMISSION=y +CONFIG_LWIP_TCP_MAXRTX=12 +CONFIG_LWIP_TCP_SYNMAXRTX=12 +CONFIG_LWIP_TCP_MSS=1440 +CONFIG_LWIP_TCP_TMR_INTERVAL=250 +CONFIG_LWIP_TCP_MSL=60000 +CONFIG_LWIP_TCP_FIN_WAIT_TIMEOUT=20000 +CONFIG_LWIP_TCP_SND_BUF_DEFAULT=32768 +CONFIG_LWIP_TCP_WND_DEFAULT=32768 +CONFIG_LWIP_TCP_RECVMBOX_SIZE=6 +CONFIG_LWIP_TCP_ACCEPTMBOX_SIZE=6 +CONFIG_LWIP_TCP_QUEUE_OOSEQ=y +CONFIG_LWIP_TCP_OOSEQ_TIMEOUT=6 +CONFIG_LWIP_TCP_OOSEQ_MAX_PBUFS=0 +# CONFIG_LWIP_TCP_SACK_OUT is not set +CONFIG_LWIP_TCP_OVERSIZE_MSS=y +# CONFIG_LWIP_TCP_OVERSIZE_QUARTER_MSS is not set +# CONFIG_LWIP_TCP_OVERSIZE_DISABLE is not set +# CONFIG_LWIP_WND_SCALE is not set +CONFIG_LWIP_TCP_RTO_TIME=1500 +# end of TCP + +# +# UDP +# +CONFIG_LWIP_MAX_UDP_PCBS=16 +CONFIG_LWIP_UDP_RECVMBOX_SIZE=6 +# end of UDP + +# +# Checksums +# +# CONFIG_LWIP_CHECKSUM_CHECK_IP is not set +# CONFIG_LWIP_CHECKSUM_CHECK_UDP is not set +CONFIG_LWIP_CHECKSUM_CHECK_ICMP=y +# end of Checksums + +CONFIG_LWIP_TCPIP_TASK_STACK_SIZE=3072 +CONFIG_LWIP_TCPIP_TASK_AFFINITY_NO_AFFINITY=y +# CONFIG_LWIP_TCPIP_TASK_AFFINITY_CPU0 is not set +# CONFIG_LWIP_TCPIP_TASK_AFFINITY_CPU1 is not set +CONFIG_LWIP_TCPIP_TASK_AFFINITY=0x7FFFFFFF +CONFIG_LWIP_IPV6_MEMP_NUM_ND6_QUEUE=3 +CONFIG_LWIP_IPV6_ND6_NUM_NEIGHBORS=5 +CONFIG_LWIP_IPV6_ND6_NUM_PREFIXES=5 +CONFIG_LWIP_IPV6_ND6_NUM_ROUTERS=3 +CONFIG_LWIP_IPV6_ND6_NUM_DESTINATIONS=10 +# CONFIG_LWIP_PPP_SUPPORT is not set +# CONFIG_LWIP_SLIP_SUPPORT is not set + +# +# ICMP +# +CONFIG_LWIP_ICMP=y +# CONFIG_LWIP_MULTICAST_PING is not set +# CONFIG_LWIP_BROADCAST_PING is not set +# end of ICMP + +# +# LWIP RAW API +# +CONFIG_LWIP_MAX_RAW_PCBS=16 +# end of LWIP RAW API + +# +# SNTP +# +CONFIG_LWIP_SNTP_MAX_SERVERS=1 +# CONFIG_LWIP_DHCP_GET_NTP_SRV is not set +CONFIG_LWIP_SNTP_UPDATE_DELAY=3600000 +CONFIG_LWIP_SNTP_STARTUP_DELAY=y +CONFIG_LWIP_SNTP_MAXIMUM_STARTUP_DELAY=5000 +# end of SNTP + +# +# DNS +# +CONFIG_LWIP_DNS_MAX_HOST_IP=1 +CONFIG_LWIP_DNS_MAX_SERVERS=3 +# CONFIG_LWIP_FALLBACK_DNS_SERVER_SUPPORT is not set +# CONFIG_LWIP_DNS_SETSERVER_WITH_NETIF is not set +# CONFIG_LWIP_USE_ESP_GETADDRINFO is not set +# end of DNS + +CONFIG_LWIP_BRIDGEIF_MAX_PORTS=7 +CONFIG_LWIP_ESP_LWIP_ASSERT=y + +# +# Hooks +# +# CONFIG_LWIP_HOOK_TCP_ISN_NONE is not set +CONFIG_LWIP_HOOK_TCP_ISN_DEFAULT=y +# CONFIG_LWIP_HOOK_TCP_ISN_CUSTOM is not set +CONFIG_LWIP_HOOK_IP6_ROUTE_NONE=y +# CONFIG_LWIP_HOOK_IP6_ROUTE_DEFAULT is not set +# CONFIG_LWIP_HOOK_IP6_ROUTE_CUSTOM is not set +CONFIG_LWIP_HOOK_ND6_GET_GW_NONE=y +# CONFIG_LWIP_HOOK_ND6_GET_GW_DEFAULT is not set +# CONFIG_LWIP_HOOK_ND6_GET_GW_CUSTOM is not set +CONFIG_LWIP_HOOK_IP6_SELECT_SRC_ADDR_NONE=y +# CONFIG_LWIP_HOOK_IP6_SELECT_SRC_ADDR_DEFAULT is not set +# CONFIG_LWIP_HOOK_IP6_SELECT_SRC_ADDR_CUSTOM is not set +CONFIG_LWIP_HOOK_DHCP_EXTRA_OPTION_NONE=y +# CONFIG_LWIP_HOOK_DHCP_EXTRA_OPTION_DEFAULT is not set +# CONFIG_LWIP_HOOK_DHCP_EXTRA_OPTION_CUSTOM is not set +CONFIG_LWIP_HOOK_NETCONN_EXT_RESOLVE_NONE=y +# CONFIG_LWIP_HOOK_NETCONN_EXT_RESOLVE_DEFAULT is not set +# CONFIG_LWIP_HOOK_NETCONN_EXT_RESOLVE_CUSTOM is not set +CONFIG_LWIP_HOOK_DNS_EXT_RESOLVE_NONE=y +# CONFIG_LWIP_HOOK_DNS_EXT_RESOLVE_CUSTOM is not set +# CONFIG_LWIP_HOOK_IP6_INPUT_NONE is not set +CONFIG_LWIP_HOOK_IP6_INPUT_DEFAULT=y +# CONFIG_LWIP_HOOK_IP6_INPUT_CUSTOM is not set +# end of Hooks + +# CONFIG_LWIP_DEBUG is not set +# end of LWIP + +# +# mbedTLS +# +# CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC is not set +CONFIG_MBEDTLS_EXTERNAL_MEM_ALLOC=y +# CONFIG_MBEDTLS_DEFAULT_MEM_ALLOC is not set +# CONFIG_MBEDTLS_CUSTOM_MEM_ALLOC is not set +CONFIG_MBEDTLS_ASYMMETRIC_CONTENT_LEN=y +CONFIG_MBEDTLS_SSL_IN_CONTENT_LEN=16384 +CONFIG_MBEDTLS_SSL_OUT_CONTENT_LEN=4096 +# CONFIG_MBEDTLS_DYNAMIC_BUFFER is not set +# CONFIG_MBEDTLS_DEBUG is not set + +# +# mbedTLS v3.x related +# +# CONFIG_MBEDTLS_SSL_PROTO_TLS1_3 is not set +# CONFIG_MBEDTLS_SSL_VARIABLE_BUFFER_LENGTH is not set +# CONFIG_MBEDTLS_X509_TRUSTED_CERT_CALLBACK is not set +# CONFIG_MBEDTLS_SSL_CONTEXT_SERIALIZATION is not set +CONFIG_MBEDTLS_SSL_KEEP_PEER_CERTIFICATE=y +# CONFIG_MBEDTLS_SSL_KEYING_MATERIAL_EXPORT is not set +CONFIG_MBEDTLS_PKCS7_C=y +# end of mbedTLS v3.x related + +# +# Certificate Bundle +# +CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=y +CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_FULL=y +# CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_CMN is not set +# CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_NONE is not set +# CONFIG_MBEDTLS_CUSTOM_CERTIFICATE_BUNDLE is not set +# CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEPRECATED_LIST is not set +CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_MAX_CERTS=200 +# end of Certificate Bundle + +# CONFIG_MBEDTLS_ECP_RESTARTABLE is not set +CONFIG_MBEDTLS_CMAC_C=y +# CONFIG_MBEDTLS_HARDWARE_AES is not set +CONFIG_MBEDTLS_HARDWARE_MPI=y +# CONFIG_MBEDTLS_LARGE_KEY_SOFTWARE_MPI is not set +CONFIG_MBEDTLS_MPI_USE_INTERRUPT=y +CONFIG_MBEDTLS_MPI_INTERRUPT_LEVEL=0 +# CONFIG_MBEDTLS_HARDWARE_SHA is not set +CONFIG_MBEDTLS_ROM_MD5=y +# CONFIG_MBEDTLS_ATCA_HW_ECDSA_SIGN is not set +# CONFIG_MBEDTLS_ATCA_HW_ECDSA_VERIFY is not set +CONFIG_MBEDTLS_HAVE_TIME=y +# CONFIG_MBEDTLS_PLATFORM_TIME_ALT is not set +# CONFIG_MBEDTLS_HAVE_TIME_DATE is not set +CONFIG_MBEDTLS_ECDSA_DETERMINISTIC=y +CONFIG_MBEDTLS_SHA1_C=y +CONFIG_MBEDTLS_SHA512_C=y +# CONFIG_MBEDTLS_SHA3_C is not set +CONFIG_MBEDTLS_TLS_SERVER_AND_CLIENT=y +# CONFIG_MBEDTLS_TLS_SERVER_ONLY is not set +# CONFIG_MBEDTLS_TLS_CLIENT_ONLY is not set +# CONFIG_MBEDTLS_TLS_DISABLED is not set +CONFIG_MBEDTLS_TLS_SERVER=y +CONFIG_MBEDTLS_TLS_CLIENT=y +CONFIG_MBEDTLS_TLS_ENABLED=y + +# +# TLS Key Exchange Methods +# +# CONFIG_MBEDTLS_PSK_MODES is not set +CONFIG_MBEDTLS_KEY_EXCHANGE_RSA=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ELLIPTIC_CURVE=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_RSA=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_RSA=y +# end of TLS Key Exchange Methods + +CONFIG_MBEDTLS_SSL_RENEGOTIATION=y +CONFIG_MBEDTLS_SSL_PROTO_TLS1_2=y +# CONFIG_MBEDTLS_SSL_PROTO_GMTSSL1_1 is not set +# CONFIG_MBEDTLS_SSL_PROTO_DTLS is not set +CONFIG_MBEDTLS_SSL_ALPN=y +CONFIG_MBEDTLS_CLIENT_SSL_SESSION_TICKETS=y +CONFIG_MBEDTLS_SERVER_SSL_SESSION_TICKETS=y + +# +# Symmetric Ciphers +# +CONFIG_MBEDTLS_AES_C=y +# CONFIG_MBEDTLS_CAMELLIA_C is not set +# CONFIG_MBEDTLS_DES_C is not set +# CONFIG_MBEDTLS_BLOWFISH_C is not set +# CONFIG_MBEDTLS_XTEA_C is not set +CONFIG_MBEDTLS_CCM_C=y +CONFIG_MBEDTLS_GCM_C=y +# CONFIG_MBEDTLS_NIST_KW_C is not set +# end of Symmetric Ciphers + +# CONFIG_MBEDTLS_RIPEMD160_C is not set + +# +# Certificates +# +CONFIG_MBEDTLS_PEM_PARSE_C=y +CONFIG_MBEDTLS_PEM_WRITE_C=y +CONFIG_MBEDTLS_X509_CRL_PARSE_C=y +CONFIG_MBEDTLS_X509_CSR_PARSE_C=y +# end of Certificates + +CONFIG_MBEDTLS_ECP_C=y +CONFIG_MBEDTLS_PK_PARSE_EC_EXTENDED=y +CONFIG_MBEDTLS_PK_PARSE_EC_COMPRESSED=y +# CONFIG_MBEDTLS_DHM_C is not set +CONFIG_MBEDTLS_ECDH_C=y +CONFIG_MBEDTLS_ECDSA_C=y +# CONFIG_MBEDTLS_ECJPAKE_C is not set +CONFIG_MBEDTLS_ECP_DP_SECP192R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP224R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP256R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP384R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP521R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP192K1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP224K1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP256K1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_BP256R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_BP384R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_BP512R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_CURVE25519_ENABLED=y +CONFIG_MBEDTLS_ECP_NIST_OPTIM=y +# CONFIG_MBEDTLS_ECP_FIXED_POINT_OPTIM is not set +# CONFIG_MBEDTLS_POLY1305_C is not set +# CONFIG_MBEDTLS_CHACHA20_C is not set +# CONFIG_MBEDTLS_HKDF_C is not set +# CONFIG_MBEDTLS_THREADING_C is not set +CONFIG_MBEDTLS_ERROR_STRINGS=y +CONFIG_MBEDTLS_FS_IO=y +# CONFIG_MBEDTLS_ALLOW_WEAK_CERTIFICATE_VERIFICATION is not set +# end of mbedTLS + +# +# ESP-MQTT Configurations +# +CONFIG_MQTT_PROTOCOL_311=y +# CONFIG_MQTT_PROTOCOL_5 is not set +CONFIG_MQTT_TRANSPORT_SSL=y +CONFIG_MQTT_TRANSPORT_WEBSOCKET=y +CONFIG_MQTT_TRANSPORT_WEBSOCKET_SECURE=y +# CONFIG_MQTT_MSG_ID_INCREMENTAL is not set +# CONFIG_MQTT_SKIP_PUBLISH_IF_DISCONNECTED is not set +# CONFIG_MQTT_REPORT_DELETED_MESSAGES is not set +# CONFIG_MQTT_USE_CUSTOM_CONFIG is not set +# CONFIG_MQTT_TASK_CORE_SELECTION_ENABLED is not set +# CONFIG_MQTT_CUSTOM_OUTBOX is not set +# end of ESP-MQTT Configurations + +# +# LibC +# +CONFIG_LIBC_NEWLIB=y +# CONFIG_LIBC_PICOLIBC is not set +CONFIG_LIBC_MISC_IN_IRAM=y +CONFIG_LIBC_LOCKS_PLACE_IN_IRAM=y +CONFIG_LIBC_STDOUT_LINE_ENDING_CRLF=y +# CONFIG_LIBC_STDOUT_LINE_ENDING_LF is not set +# CONFIG_LIBC_STDOUT_LINE_ENDING_CR is not set +# CONFIG_LIBC_STDIN_LINE_ENDING_CRLF is not set +# CONFIG_LIBC_STDIN_LINE_ENDING_LF is not set +CONFIG_LIBC_STDIN_LINE_ENDING_CR=y +# CONFIG_LIBC_NEWLIB_NANO_FORMAT is not set +CONFIG_LIBC_TIME_SYSCALL_USE_RTC_HRT=y +# CONFIG_LIBC_TIME_SYSCALL_USE_RTC is not set +# CONFIG_LIBC_TIME_SYSCALL_USE_HRT is not set +# CONFIG_LIBC_TIME_SYSCALL_USE_NONE is not set +# end of LibC + +CONFIG_STDATOMIC_S32C1I_SPIRAM_WORKAROUND=y + +# +# NVS +# +# CONFIG_NVS_ENCRYPTION is not set +# CONFIG_NVS_ASSERT_ERROR_CHECK is not set +# CONFIG_NVS_LEGACY_DUP_KEYS_COMPATIBILITY is not set +# CONFIG_NVS_ALLOCATE_CACHE_IN_SPIRAM is not set +# end of NVS + +# +# OpenThread +# +# CONFIG_OPENTHREAD_ENABLED is not set + +# +# OpenThread Spinel +# +# CONFIG_OPENTHREAD_SPINEL_ONLY is not set +# end of OpenThread Spinel +# end of OpenThread + +# +# Protocomm +# +CONFIG_ESP_PROTOCOMM_SUPPORT_SECURITY_VERSION_0=y +CONFIG_ESP_PROTOCOMM_SUPPORT_SECURITY_VERSION_1=y +CONFIG_ESP_PROTOCOMM_SUPPORT_SECURITY_VERSION_2=y +CONFIG_ESP_PROTOCOMM_SUPPORT_SECURITY_PATCH_VERSION=y +# end of Protocomm + +# +# PThreads +# +CONFIG_PTHREAD_TASK_PRIO_DEFAULT=5 +CONFIG_PTHREAD_TASK_STACK_SIZE_DEFAULT=3072 +CONFIG_PTHREAD_STACK_MIN=768 +CONFIG_PTHREAD_DEFAULT_CORE_NO_AFFINITY=y +# CONFIG_PTHREAD_DEFAULT_CORE_0 is not set +# CONFIG_PTHREAD_DEFAULT_CORE_1 is not set +CONFIG_PTHREAD_TASK_CORE_DEFAULT=-1 +CONFIG_PTHREAD_TASK_NAME_DEFAULT="pthread" +# end of PThreads + +# +# MMU Config +# +CONFIG_MMU_PAGE_SIZE_64KB=y +CONFIG_MMU_PAGE_MODE="64KB" +CONFIG_MMU_PAGE_SIZE=0x10000 +# end of MMU Config + +# +# Main Flash configuration +# + +# +# SPI Flash behavior when brownout +# +CONFIG_SPI_FLASH_BROWNOUT_RESET_XMC=y +CONFIG_SPI_FLASH_BROWNOUT_RESET=y +# end of SPI Flash behavior when brownout + +# +# Optional and Experimental Features (READ DOCS FIRST) +# + +# +# Features here require specific hardware (READ DOCS FIRST!) +# +CONFIG_SPI_FLASH_HPM_ENA=y +# CONFIG_SPI_FLASH_HPM_AUTO is not set +# CONFIG_SPI_FLASH_HPM_DIS is not set +CONFIG_SPI_FLASH_HPM_ON=y +CONFIG_SPI_FLASH_HPM_DC_AUTO=y +# CONFIG_SPI_FLASH_HPM_DC_DISABLE is not set +CONFIG_SPI_FLASH_HPM_DC_ON=y +# CONFIG_SPI_FLASH_AUTO_SUSPEND is not set +CONFIG_SPI_FLASH_SUSPEND_TSUS_VAL_US=50 +# CONFIG_SPI_FLASH_FORCE_ENABLE_XMC_C_SUSPEND is not set +# CONFIG_SPI_FLASH_FORCE_ENABLE_C6_H2_SUSPEND is not set +CONFIG_SPI_FLASH_PLACE_FUNCTIONS_IN_IRAM=y +# end of Optional and Experimental Features (READ DOCS FIRST) +# end of Main Flash configuration + +# +# SPI Flash driver +# +# CONFIG_SPI_FLASH_VERIFY_WRITE is not set +# CONFIG_SPI_FLASH_ENABLE_COUNTERS is not set +CONFIG_SPI_FLASH_ROM_DRIVER_PATCH=y +# CONFIG_SPI_FLASH_ROM_IMPL is not set +CONFIG_SPI_FLASH_DANGEROUS_WRITE_ABORTS=y +# CONFIG_SPI_FLASH_DANGEROUS_WRITE_FAILS is not set +# CONFIG_SPI_FLASH_DANGEROUS_WRITE_ALLOWED is not set +# CONFIG_SPI_FLASH_BYPASS_BLOCK_ERASE is not set +CONFIG_SPI_FLASH_YIELD_DURING_ERASE=y +CONFIG_SPI_FLASH_ERASE_YIELD_DURATION_MS=20 +CONFIG_SPI_FLASH_ERASE_YIELD_TICKS=1 +CONFIG_SPI_FLASH_WRITE_CHUNK_SIZE=8192 +# CONFIG_SPI_FLASH_SIZE_OVERRIDE is not set +# CONFIG_SPI_FLASH_CHECK_ERASE_TIMEOUT_DISABLED is not set +# CONFIG_SPI_FLASH_OVERRIDE_CHIP_DRIVER_LIST is not set + +# +# Auto-detect flash chips +# +CONFIG_SPI_FLASH_VENDOR_XMC_SUPPORT_ENABLED=y +CONFIG_SPI_FLASH_VENDOR_GD_SUPPORT_ENABLED=y +CONFIG_SPI_FLASH_VENDOR_ISSI_SUPPORT_ENABLED=y +CONFIG_SPI_FLASH_VENDOR_MXIC_SUPPORT_ENABLED=y +CONFIG_SPI_FLASH_VENDOR_WINBOND_SUPPORT_ENABLED=y +CONFIG_SPI_FLASH_VENDOR_BOYA_SUPPORT_ENABLED=y +CONFIG_SPI_FLASH_VENDOR_TH_SUPPORT_ENABLED=y +CONFIG_SPI_FLASH_SUPPORT_ISSI_CHIP=y +CONFIG_SPI_FLASH_SUPPORT_MXIC_CHIP=y +CONFIG_SPI_FLASH_SUPPORT_GD_CHIP=y +CONFIG_SPI_FLASH_SUPPORT_WINBOND_CHIP=y +CONFIG_SPI_FLASH_SUPPORT_BOYA_CHIP=y +CONFIG_SPI_FLASH_SUPPORT_TH_CHIP=y +CONFIG_SPI_FLASH_SUPPORT_MXIC_OPI_CHIP=y +# end of Auto-detect flash chips + +CONFIG_SPI_FLASH_ENABLE_ENCRYPTED_READ_WRITE=y +# end of SPI Flash driver + +# +# SPIFFS Configuration +# +CONFIG_SPIFFS_MAX_PARTITIONS=3 + +# +# SPIFFS Cache Configuration +# +CONFIG_SPIFFS_CACHE=y +CONFIG_SPIFFS_CACHE_WR=y +# CONFIG_SPIFFS_CACHE_STATS is not set +# end of SPIFFS Cache Configuration + +CONFIG_SPIFFS_PAGE_CHECK=y +CONFIG_SPIFFS_GC_MAX_RUNS=10 +# CONFIG_SPIFFS_GC_STATS is not set +CONFIG_SPIFFS_PAGE_SIZE=256 +CONFIG_SPIFFS_OBJ_NAME_LEN=32 +# CONFIG_SPIFFS_FOLLOW_SYMLINKS is not set +CONFIG_SPIFFS_USE_MAGIC=y +CONFIG_SPIFFS_USE_MAGIC_LENGTH=y +CONFIG_SPIFFS_META_LENGTH=4 +CONFIG_SPIFFS_USE_MTIME=y + +# +# Debug Configuration +# +# CONFIG_SPIFFS_DBG is not set +# CONFIG_SPIFFS_API_DBG is not set +# CONFIG_SPIFFS_GC_DBG is not set +# CONFIG_SPIFFS_CACHE_DBG is not set +# CONFIG_SPIFFS_CHECK_DBG is not set +# CONFIG_SPIFFS_TEST_VISUALISATION is not set +# end of Debug Configuration +# end of SPIFFS Configuration + +# +# TCP Transport +# + +# +# Websocket +# +CONFIG_WS_TRANSPORT=y +CONFIG_WS_BUFFER_SIZE=1024 +# CONFIG_WS_DYNAMIC_BUFFER is not set +# end of Websocket +# end of TCP Transport + +# +# Ultra Low Power (ULP) Co-processor +# +# CONFIG_ULP_COPROC_ENABLED is not set + +# +# ULP Debugging Options +# +# end of ULP Debugging Options +# end of Ultra Low Power (ULP) Co-processor + +# +# Unity unit testing library +# +CONFIG_UNITY_ENABLE_FLOAT=y +CONFIG_UNITY_ENABLE_DOUBLE=y +# CONFIG_UNITY_ENABLE_64BIT is not set +# CONFIG_UNITY_ENABLE_COLOR is not set +CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=y +# CONFIG_UNITY_ENABLE_FIXTURE is not set +# CONFIG_UNITY_ENABLE_BACKTRACE_ON_FAIL is not set +# CONFIG_UNITY_TEST_ORDER_BY_FILE_PATH_AND_LINE is not set +# end of Unity unit testing library + +# +# USB-OTG +# +CONFIG_USB_HOST_CONTROL_TRANSFER_MAX_SIZE=256 +CONFIG_USB_HOST_HW_BUFFER_BIAS_BALANCED=y +# CONFIG_USB_HOST_HW_BUFFER_BIAS_IN is not set +# CONFIG_USB_HOST_HW_BUFFER_BIAS_PERIODIC_OUT is not set + +# +# Hub Driver Configuration +# + +# +# Root Port configuration +# +CONFIG_USB_HOST_DEBOUNCE_DELAY_MS=250 +CONFIG_USB_HOST_RESET_HOLD_MS=30 +CONFIG_USB_HOST_RESET_RECOVERY_MS=30 +CONFIG_USB_HOST_SET_ADDR_RECOVERY_MS=10 +# end of Root Port configuration + +# CONFIG_USB_HOST_HUBS_SUPPORTED is not set +# end of Hub Driver Configuration + +# CONFIG_USB_HOST_ENABLE_ENUM_FILTER_CALLBACK is not set +CONFIG_USB_OTG_SUPPORTED=y +# end of USB-OTG + +# +# Virtual file system +# +CONFIG_VFS_SUPPORT_IO=y +CONFIG_VFS_SUPPORT_DIR=y +CONFIG_VFS_SUPPORT_SELECT=y +CONFIG_VFS_SUPPRESS_SELECT_DEBUG_OUTPUT=y +# CONFIG_VFS_SELECT_IN_RAM is not set +CONFIG_VFS_SUPPORT_TERMIOS=y +CONFIG_VFS_MAX_COUNT=8 + +# +# Host File System I/O (Semihosting) +# +CONFIG_VFS_SEMIHOSTFS_MAX_MOUNT_POINTS=1 +# end of Host File System I/O (Semihosting) + +CONFIG_VFS_INITIALIZE_DEV_NULL=y +# end of Virtual file system + +# +# Wear Levelling +# +# CONFIG_WL_SECTOR_SIZE_512 is not set +CONFIG_WL_SECTOR_SIZE_4096=y +CONFIG_WL_SECTOR_SIZE=4096 +# end of Wear Levelling + +# +# Wi-Fi Provisioning Manager +# +CONFIG_WIFI_PROV_SCAN_MAX_ENTRIES=16 +CONFIG_WIFI_PROV_AUTOSTOP_TIMEOUT=30 +CONFIG_WIFI_PROV_STA_ALL_CHANNEL_SCAN=y +# CONFIG_WIFI_PROV_STA_FAST_SCAN is not set +# end of Wi-Fi Provisioning Manager + +# +# Board Support Package(EchoEar) +# +# CONFIG_BSP_PCB_VERSION_V1_0 is not set +CONFIG_BSP_PCB_VERSION_V1_2=y +CONFIG_BSP_ERROR_CHECK=y + +# +# I2C +# +CONFIG_BSP_I2C_NUM=1 +CONFIG_BSP_I2C_FAST_MODE=y +CONFIG_BSP_I2C_CLK_SPEED_HZ=400000 +# end of I2C + +# +# SPIFFS - Virtual File System +# +# CONFIG_BSP_SPIFFS_FORMAT_ON_MOUNT_FAIL is not set +CONFIG_BSP_SPIFFS_MOUNT_POINT="/spiffs" +CONFIG_BSP_SPIFFS_PARTITION_LABEL="storage" +CONFIG_BSP_SPIFFS_MAX_FILES=5 +# end of SPIFFS - Virtual File System + +# +# SD card - Virtual File System +# +# CONFIG_BSP_SD_FORMAT_ON_MOUNT_FAIL is not set +CONFIG_BSP_SD_MOUNT_POINT="/sdcard" +# end of SD card - Virtual File System + +# +# Display +# +CONFIG_BSP_DISPLAY_BRIGHTNESS_LEDC_CH=1 +CONFIG_BSP_LCD_DRAW_BUF_HEIGHT=100 +# CONFIG_BSP_LCD_DRAW_BUF_DOUBLE is not set +# end of Display + +CONFIG_BSP_HEAD_LED_LEDC_CH=2 +# end of Board Support Package(EchoEar) + +# +# ESP Emote Graphics Framework +# +# CONFIG_GFX_FONT_FREETYPE_SUPPORT is not set +# end of ESP Emote Graphics Framework + +# +# IoT Button +# +CONFIG_BUTTON_PERIOD_TIME_MS=5 +CONFIG_BUTTON_DEBOUNCE_TICKS=2 +CONFIG_BUTTON_SHORT_PRESS_TIME_MS=180 +CONFIG_BUTTON_LONG_PRESS_TIME_MS=1500 +CONFIG_BUTTON_LONG_PRESS_HOLD_SERIAL_TIME_MS=20 +CONFIG_ADC_BUTTON_MAX_CHANNEL=3 +CONFIG_ADC_BUTTON_MAX_BUTTON_PER_CHANNEL=8 +CONFIG_ADC_BUTTON_SAMPLE_TIMES=1 +# end of IoT Button + +# +# CMake Utilities +# +# CONFIG_CU_RELINKER_ENABLE is not set +# CONFIG_CU_DIAGNOSTICS_COLOR_NEVER is not set +CONFIG_CU_DIAGNOSTICS_COLOR_ALWAYS=y +# CONFIG_CU_DIAGNOSTICS_COLOR_AUTO is not set +# CONFIG_CU_GCC_LTO_ENABLE is not set +# CONFIG_CU_GCC_STRING_1BYTE_ALIGN is not set +# end of CMake Utilities + +# +# DSP Library +# +CONFIG_DSP_OPTIMIZATIONS_SUPPORTED=y +# CONFIG_DSP_ANSI is not set +CONFIG_DSP_OPTIMIZED=y +CONFIG_DSP_OPTIMIZATION=1 +# CONFIG_DSP_MAX_FFT_SIZE_512 is not set +# CONFIG_DSP_MAX_FFT_SIZE_1024 is not set +# CONFIG_DSP_MAX_FFT_SIZE_2048 is not set +CONFIG_DSP_MAX_FFT_SIZE_4096=y +# CONFIG_DSP_MAX_FFT_SIZE_8192 is not set +# CONFIG_DSP_MAX_FFT_SIZE_16384 is not set +# CONFIG_DSP_MAX_FFT_SIZE_32768 is not set +CONFIG_DSP_MAX_FFT_SIZE=4096 +# end of DSP Library + +# +# Camera configuration +# +CONFIG_OV7670_SUPPORT=y +CONFIG_OV7725_SUPPORT=y +CONFIG_NT99141_SUPPORT=y +CONFIG_OV2640_SUPPORT=y +CONFIG_OV3660_SUPPORT=y +CONFIG_OV5640_SUPPORT=y +# CONFIG_CAMERA_AF_SUPPORT is not set +CONFIG_GC2145_SUPPORT=y +CONFIG_GC032A_SUPPORT=y +CONFIG_GC0308_SUPPORT=y +CONFIG_BF3005_SUPPORT=y +CONFIG_BF20A6_SUPPORT=y +# CONFIG_SC101IOT_SUPPORT is not set +CONFIG_SC030IOT_SUPPORT=y +# CONFIG_SC031GS_SUPPORT is not set +CONFIG_HM1055_SUPPORT=y +CONFIG_HM0360_SUPPORT=y +CONFIG_MEGA_CCM_SUPPORT=y +# CONFIG_SCCB_HARDWARE_I2C_DRIVER_LEGACY is not set +CONFIG_SCCB_HARDWARE_I2C_DRIVER_NEW=y +# CONFIG_SCCB_HARDWARE_I2C_PORT0 is not set +CONFIG_SCCB_HARDWARE_I2C_PORT1=y +CONFIG_SCCB_CLK_FREQ=100000 +# CONFIG_GC_SENSOR_WINDOWING_MODE is not set +CONFIG_GC_SENSOR_SUBSAMPLE_MODE=y +CONFIG_CAMERA_TASK_STACK_SIZE=4096 +CONFIG_CAMERA_CORE0=y +# CONFIG_CAMERA_CORE1 is not set +# CONFIG_CAMERA_NO_AFFINITY is not set +CONFIG_CAMERA_DMA_BUFFER_SIZE_MAX=32768 +# CONFIG_CAMERA_PSRAM_DMA is not set +CONFIG_CAMERA_JPEG_MODE_FRAME_SIZE_AUTO=y +# CONFIG_CAMERA_JPEG_MODE_FRAME_SIZE_CUSTOM is not set +# CONFIG_CAMERA_CONVERTER_ENABLED is not set +# CONFIG_LCD_CAM_ISR_IRAM_SAFE is not set +# end of Camera configuration + +# +# Audio Codec Configuration +# + +# +# Audio Decoder Configuration +# +CONFIG_AUDIO_DECODER_AAC_SUPPORT=y +CONFIG_AUDIO_DECODER_MP3_SUPPORT=y +CONFIG_AUDIO_DECODER_G711_SUPPORT=y +CONFIG_AUDIO_DECODER_AMRNB_SUPPORT=y +CONFIG_AUDIO_DECODER_AMRWB_SUPPORT=y +CONFIG_AUDIO_DECODER_FLAC_SUPPORT=y +CONFIG_AUDIO_DECODER_OPUS_SUPPORT=y +CONFIG_AUDIO_DECODER_VORBIS_SUPPORT=y +CONFIG_AUDIO_DECODER_ADPCM_SUPPORT=y +CONFIG_AUDIO_DECODER_ALAC_SUPPORT=y +CONFIG_AUDIO_DECODER_PCM_SUPPORT=y +CONFIG_AUDIO_DECODER_SBC_SUPPORT=y +CONFIG_AUDIO_DECODER_LC3_SUPPORT=y +# end of Audio Decoder Configuration + +# +# Audio Simple Decoder Configuration +# +CONFIG_AUDIO_SIMPLE_DEC_WAV_SUPPORT=y +CONFIG_AUDIO_SIMPLE_DEC_M4A_SUPPORT=y +CONFIG_AUDIO_SIMPLE_DEC_TS_SUPPORT=y +# end of Audio Simple Decoder Configuration + +# +# Audio Encoder Configuration +# +CONFIG_AUDIO_ENCODER_AAC_SUPPORT=y +CONFIG_AUDIO_ENCODER_G711_SUPPORT=y +CONFIG_AUDIO_ENCODER_AMRNB_SUPPORT=y +CONFIG_AUDIO_ENCODER_AMRWB_SUPPORT=y +CONFIG_AUDIO_ENCODER_OPUS_SUPPORT=y +CONFIG_AUDIO_ENCODER_ADPCM_SUPPORT=y +CONFIG_AUDIO_ENCODER_ALAC_SUPPORT=y +CONFIG_AUDIO_ENCODER_PCM_SUPPORT=y +CONFIG_AUDIO_ENCODER_SBC_SUPPORT=y +CONFIG_AUDIO_ENCODER_LC3_SUPPORT=y +# end of Audio Encoder Configuration +# end of Audio Codec Configuration + +# +# Espressif Audio Render Configuration +# +CONFIG_ESP_AUDIO_RENDER_MIXER_THREAD_STACK_SIZE=4096 +CONFIG_ESP_AUDIO_RENDER_MIXER_THREAD_CORE_ID=0 +CONFIG_ESP_AUDIO_RENDER_MIXER_THREAD_PRIORITY=20 +# end of Espressif Audio Render Configuration + +# +# ESP Audio Simple Player +# + +# +# Audio Simple Player IO Stream +# +CONFIG_ESP_AUDIO_SIMPLE_PLAYER_HTTP_EN=y +CONFIG_ESP_AUDIO_SIMPLE_PLAYER_FILE_EN=y +CONFIG_ESP_AUDIO_SIMPLE_PLAYER_EMBED_FLASH_EN=y +# end of Audio Simple Player IO Stream + +# +# Audio Simple Player Filters +# +CONFIG_ESP_AUDIO_SIMPLE_PLAYER_RESAMPLE_EN=y +CONFIG_AUDIO_SIMPLE_PLAYER_RESAMPLE_DEST_RATE=48000 +# CONFIG_ESP_AUDIO_SIMPLE_PLAYER_CH_CVT_EN is not set +# CONFIG_ESP_AUDIO_SIMPLE_PLAYER_BIT_CVT_EN is not set +# end of Audio Simple Player Filters +# end of ESP Audio Simple Player + +# +# Espressif Camera Sensors Configurations +# + +# +# Camera Sensor Configuration +# + +# +# Select and Set Camera Sensor +# +# CONFIG_CAMERA_BF3901 is not set +# CONFIG_CAMERA_BF3925 is not set +# CONFIG_CAMERA_BF3A03 is not set +# CONFIG_CAMERA_BF20A6 is not set +# CONFIG_CAMERA_GC0308 is not set +# CONFIG_CAMERA_GC2145 is not set +# CONFIG_CAMERA_MT9D111 is not set +# CONFIG_CAMERA_OV2640 is not set +# CONFIG_CAMERA_OV3660 is not set +# CONFIG_CAMERA_OV5640 is not set +# CONFIG_CAMERA_SC030IOT is not set +# CONFIG_CAMERA_SC101IOT is not set +# CONFIG_CAMERA_SC2336 is not set +# end of Select and Set Camera Sensor +# end of Camera Sensor Configuration + +# +# Camera XCLK Generator Configuration +# +CONFIG_CAMERA_XCLK_USE_LEDC=y +# end of Camera XCLK Generator Configuration + +# +# Camera Motor Control Configuration +# +# CONFIG_CAM_MOTOR_DW9714 is not set +# end of Camera Motor Control Configuration + +CONFIG_CAM_CTRL_SPI_ENABLE=y +CONFIG_CAM_CTLR_SPI_ISR_CACHE_SAFE=y +# CONFIG_CAM_CTLR_SPI_DISABLE_BACKUP_BUFFER is not set +# CONFIG_CAM_CTLR_SPI_DISABLE_AUTO_DECODE is not set +CONFIG_CAM_CTLR_SPI_DECODE_TASK_STACK_SIZE=4096 +CONFIG_CAM_CTLR_SPI_DECODE_TASK_PRIORITY=10 +# end of Espressif Camera Sensors Configurations + +# +# Espressif Capture Configuration +# +CONFIG_ESP_CAPTURE_ENABLE_AUDIO=y +CONFIG_ESP_CAPTURE_ENABLE_VIDEO=y +CONFIG_ESP_CAPTURE_ENABLE_VIDEO_OVERLAY=y +# CONFIG_ESP_CAPTURE_ENABLE_PERF_MON is not set + +# +# Capture Text Overlay +# +CONFIG_ESP_PAINTER_FORMAT_SIZE_MAX=128 + +# +# fonts +# +CONFIG_ESP_PAINTER_BASIC_FONT_12=y +# CONFIG_ESP_PAINTER_BASIC_FONT_16 is not set +# CONFIG_ESP_PAINTER_BASIC_FONT_20 is not set +# CONFIG_ESP_PAINTER_BASIC_FONT_24 is not set +# CONFIG_ESP_PAINTER_BASIC_FONT_28 is not set +# CONFIG_ESP_PAINTER_BASIC_FONT_32 is not set +# CONFIG_ESP_PAINTER_BASIC_FONT_36 is not set +# CONFIG_ESP_PAINTER_BASIC_FONT_40 is not set +# CONFIG_ESP_PAINTER_BASIC_FONT_44 is not set +# CONFIG_ESP_PAINTER_BASIC_FONT_48 is not set +# end of fonts +# end of Capture Text Overlay +# end of Espressif Capture Configuration + +# +# Audio Codec Device Configuration +# +CONFIG_CODEC_ES8311_SUPPORT=y +CONFIG_CODEC_ES7210_SUPPORT=y +CONFIG_CODEC_ES7243_SUPPORT=y +CONFIG_CODEC_ES7243E_SUPPORT=y +CONFIG_CODEC_ES8156_SUPPORT=y +CONFIG_CODEC_AW88298_SUPPORT=y +CONFIG_CODEC_ES8389_SUPPORT=y +CONFIG_CODEC_ES8374_SUPPORT=y +CONFIG_CODEC_ES8388_SUPPORT=y +CONFIG_CODEC_TAS5805M_SUPPORT=y +# CONFIG_CODEC_ZL38063_SUPPORT is not set +# CONFIG_CODEC_CJC8910_SUPPORT is not set +# CONFIG_CODEC_DUMMY_SUPPORT is not set +# CONFIG_CODEC_DATA_ADC_SUPPORT is not set +# end of Audio Codec Device Configuration + +# +# ESP H264 Configuration +# +CONFIG_ESP_H264_DECODER_IRAM=y +# CONFIG_ESP_H264_DUAL_TASK is not set +# end of ESP H264 Configuration + +# +# JPEG Decoder +# +CONFIG_JD_USE_ROM=y +# end of JPEG Decoder + +# +# ESP LCD TOUCH +# +CONFIG_ESP_LCD_TOUCH_MAX_POINTS=5 +CONFIG_ESP_LCD_TOUCH_MAX_BUTTONS=1 +# end of ESP LCD TOUCH + +# +# ESP LCD TOUCH - CST816S +# +# CONFIG_ESP_LCD_TOUCH_CST816S_DISABLE_READ_ID is not set +# end of ESP LCD TOUCH - CST816S + +# +# ESP LV Adapter +# +# CONFIG_ESP_LVGL_ADAPTER_ENABLE_FS is not set +# CONFIG_ESP_LVGL_ADAPTER_ENABLE_DECODER is not set +# CONFIG_ESP_LVGL_ADAPTER_ENABLE_FREETYPE is not set +# CONFIG_ESP_LVGL_ADAPTER_ENABLE_FPS_STATS is not set +# CONFIG_ESP_LVGL_ADAPTER_ENABLE_BUTTON is not set +# CONFIG_ESP_LVGL_ADAPTER_ENABLE_KNOB is not set +# end of ESP LV Adapter + +# +# ESP LVGL PORT +# +# end of ESP LVGL PORT + +# +# mmap file support format +# +CONFIG_MMAP_FILE_NAME_LENGTH=16 +# end of mmap file support format + +# +# ESP_Muxer Configuration +# +CONFIG_ESP_MUXER_MP4_SUPPORT=y +CONFIG_ESP_MUXER_TS_SUPPORT=y +CONFIG_ESP_MUXER_OGG_SUPPORT=y +CONFIG_ESP_MUXER_WAV_SUPPORT=y +CONFIG_ESP_MUXER_FLV_SUPPORT=y +CONFIG_ESP_MUXER_CAF_SUPPORT=y +CONFIG_ESP_MUXER_AVI_SUPPORT=y +# end of ESP_Muxer Configuration + +# +# Espressif SCCB Configurations +# +CONFIG_ESP_SCCB_TRANS_TIMEOUT_DEFAULT=500 +# end of Espressif SCCB Configurations + +# +# Espressif Video Configuration +# +CONFIG_ESP_VIDEO_CHECK_PARAMETERS=y +CONFIG_ESP_VIDEO_ENABLE_DVP_VIDEO_DEVICE=y +# CONFIG_ESP_VIDEO_ENABLE_SPI_VIDEO_DEVICE is not set +# CONFIG_ESP_VIDEO_ENABLE_USB_UVC_VIDEO_DEVICE is not set + +# +# Video Data Preprocessing +# +# end of Video Data Preprocessing +# end of Espressif Video Configuration + +# +# Video Codec Configuration +# + +# +# Video Decoder Configuration +# +CONFIG_VIDEO_DECODER_SW_H264_SUPPORT=y +CONFIG_VIDEO_DECODER_SW_MJPEG_SUPPORT=y +# end of Video Decoder Configuration + +# +# Video Encoder Configuration +# +CONFIG_VIDEO_ENCODER_SW_H264_SUPPORT=y +CONFIG_VIDEO_ENCODER_SW_MJPEG_SUPPORT=y +# end of Video Encoder Configuration +# end of Video Codec Configuration + +# +# IOT Knob +# +CONFIG_KNOB_PERIOD_TIME_MS=3 +CONFIG_KNOB_DEBOUNCE_TICKS=2 +CONFIG_KNOB_HIGH_LIMIT=1000 +CONFIG_KNOB_LOW_LIMIT=-1000 +# end of IOT Knob + +# +# USB HOST UVC +# +# CONFIG_UVC_PRINTF_CONFIGURATION_DESCRIPTOR is not set +CONFIG_UVC_INTERVAL_ARRAY_SIZE=3 +CONFIG_UVC_CHECK_PAYLOAD_HEADER_EOH=y +# end of USB HOST UVC + +# +# LVGL configuration +# +CONFIG_LV_CONF_SKIP=y +# CONFIG_LV_CONF_MINIMAL is not set + +# +# Color Settings +# +# CONFIG_LV_COLOR_DEPTH_32 is not set +# CONFIG_LV_COLOR_DEPTH_24 is not set +CONFIG_LV_COLOR_DEPTH_16=y +# CONFIG_LV_COLOR_DEPTH_8 is not set +# CONFIG_LV_COLOR_DEPTH_1 is not set +CONFIG_LV_COLOR_DEPTH=16 +# end of Color Settings + +# +# Memory Settings +# +CONFIG_LV_USE_BUILTIN_MALLOC=y +# CONFIG_LV_USE_CLIB_MALLOC is not set +# CONFIG_LV_USE_MICROPYTHON_MALLOC is not set +# CONFIG_LV_USE_RTTHREAD_MALLOC is not set +# CONFIG_LV_USE_CUSTOM_MALLOC is not set +CONFIG_LV_USE_BUILTIN_STRING=y +# CONFIG_LV_USE_CLIB_STRING is not set +# CONFIG_LV_USE_CUSTOM_STRING is not set +CONFIG_LV_USE_BUILTIN_SPRINTF=y +# CONFIG_LV_USE_CLIB_SPRINTF is not set +# CONFIG_LV_USE_CUSTOM_SPRINTF is not set +CONFIG_LV_MEM_SIZE_KILOBYTES=64 +CONFIG_LV_MEM_POOL_EXPAND_SIZE_KILOBYTES=0 +CONFIG_LV_MEM_ADR=0x0 +# end of Memory Settings + +# +# HAL Settings +# +CONFIG_LV_DEF_REFR_PERIOD=33 +CONFIG_LV_DPI_DEF=130 +# end of HAL Settings + +# +# Operating System (OS) +# +CONFIG_LV_OS_NONE=y +# CONFIG_LV_OS_PTHREAD is not set +# CONFIG_LV_OS_FREERTOS is not set +# CONFIG_LV_OS_CMSIS_RTOS2 is not set +# CONFIG_LV_OS_RTTHREAD is not set +# CONFIG_LV_OS_WINDOWS is not set +# CONFIG_LV_OS_MQX is not set +# CONFIG_LV_OS_CUSTOM is not set +CONFIG_LV_USE_OS=0 +# end of Operating System (OS) + +# +# Rendering Configuration +# +CONFIG_LV_DRAW_BUF_STRIDE_ALIGN=1 +CONFIG_LV_DRAW_BUF_ALIGN=4 +CONFIG_LV_DRAW_LAYER_SIMPLE_BUF_SIZE=24576 +CONFIG_LV_USE_DRAW_SW=y +CONFIG_LV_DRAW_SW_SUPPORT_RGB565=y +CONFIG_LV_DRAW_SW_SUPPORT_RGB565A8=y +CONFIG_LV_DRAW_SW_SUPPORT_RGB888=y +CONFIG_LV_DRAW_SW_SUPPORT_XRGB8888=y +CONFIG_LV_DRAW_SW_SUPPORT_ARGB8888=y +CONFIG_LV_DRAW_SW_SUPPORT_L8=y +CONFIG_LV_DRAW_SW_SUPPORT_AL88=y +CONFIG_LV_DRAW_SW_SUPPORT_A8=y +CONFIG_LV_DRAW_SW_SUPPORT_I1=y +CONFIG_LV_DRAW_SW_DRAW_UNIT_CNT=1 +# CONFIG_LV_USE_DRAW_ARM2D_SYNC is not set +# CONFIG_LV_USE_NATIVE_HELIUM_ASM is not set +CONFIG_LV_DRAW_SW_COMPLEX=y +# CONFIG_LV_USE_DRAW_SW_COMPLEX_GRADIENTS is not set +CONFIG_LV_DRAW_SW_SHADOW_CACHE_SIZE=0 +CONFIG_LV_DRAW_SW_CIRCLE_CACHE_SIZE=4 +CONFIG_LV_DRAW_SW_ASM_NONE=y +# CONFIG_LV_DRAW_SW_ASM_NEON is not set +# CONFIG_LV_DRAW_SW_ASM_HELIUM is not set +# CONFIG_LV_DRAW_SW_ASM_CUSTOM is not set +CONFIG_LV_USE_DRAW_SW_ASM=0 +# CONFIG_LV_USE_DRAW_VGLITE is not set +# CONFIG_LV_USE_PXP is not set +# CONFIG_LV_USE_DRAW_DAVE2D is not set +# CONFIG_LV_USE_DRAW_SDL is not set +# CONFIG_LV_USE_DRAW_VG_LITE is not set +# CONFIG_LV_USE_VECTOR_GRAPHIC is not set +# end of Rendering Configuration + +# +# Feature Configuration +# + +# +# Logging +# +# CONFIG_LV_USE_LOG is not set +# end of Logging + +# +# Asserts +# +CONFIG_LV_USE_ASSERT_NULL=y +CONFIG_LV_USE_ASSERT_MALLOC=y +# CONFIG_LV_USE_ASSERT_STYLE is not set +# CONFIG_LV_USE_ASSERT_MEM_INTEGRITY is not set +# CONFIG_LV_USE_ASSERT_OBJ is not set +CONFIG_LV_ASSERT_HANDLER_INCLUDE="assert.h" +# end of Asserts + +# +# Debug +# +# CONFIG_LV_USE_REFR_DEBUG is not set +# CONFIG_LV_USE_LAYER_DEBUG is not set +# CONFIG_LV_USE_PARALLEL_DRAW_DEBUG is not set +# end of Debug + +# +# Others +# +# CONFIG_LV_ENABLE_GLOBAL_CUSTOM is not set +CONFIG_LV_CACHE_DEF_SIZE=0 +CONFIG_LV_IMAGE_HEADER_CACHE_DEF_CNT=0 +CONFIG_LV_GRADIENT_MAX_STOPS=2 +CONFIG_LV_COLOR_MIX_ROUND_OFS=128 +# CONFIG_LV_OBJ_STYLE_CACHE is not set +# CONFIG_LV_USE_OBJ_ID is not set +# CONFIG_LV_USE_OBJ_PROPERTY is not set +# end of Others +# end of Feature Configuration + +# +# Compiler Settings +# +# CONFIG_LV_BIG_ENDIAN_SYSTEM is not set +CONFIG_LV_ATTRIBUTE_MEM_ALIGN_SIZE=1 +# CONFIG_LV_ATTRIBUTE_FAST_MEM_USE_IRAM is not set +# CONFIG_LV_USE_FLOAT is not set +# CONFIG_LV_USE_MATRIX is not set +# CONFIG_LV_USE_PRIVATE_API is not set +# end of Compiler Settings + +# +# Font Usage +# + +# +# Enable built-in fonts +# +# CONFIG_LV_FONT_MONTSERRAT_8 is not set +# CONFIG_LV_FONT_MONTSERRAT_10 is not set +# CONFIG_LV_FONT_MONTSERRAT_12 is not set +CONFIG_LV_FONT_MONTSERRAT_14=y +# CONFIG_LV_FONT_MONTSERRAT_16 is not set +# CONFIG_LV_FONT_MONTSERRAT_18 is not set +# CONFIG_LV_FONT_MONTSERRAT_20 is not set +# CONFIG_LV_FONT_MONTSERRAT_22 is not set +# CONFIG_LV_FONT_MONTSERRAT_24 is not set +# CONFIG_LV_FONT_MONTSERRAT_26 is not set +# CONFIG_LV_FONT_MONTSERRAT_28 is not set +# CONFIG_LV_FONT_MONTSERRAT_30 is not set +# CONFIG_LV_FONT_MONTSERRAT_32 is not set +# CONFIG_LV_FONT_MONTSERRAT_34 is not set +# CONFIG_LV_FONT_MONTSERRAT_36 is not set +# CONFIG_LV_FONT_MONTSERRAT_38 is not set +# CONFIG_LV_FONT_MONTSERRAT_40 is not set +# CONFIG_LV_FONT_MONTSERRAT_42 is not set +# CONFIG_LV_FONT_MONTSERRAT_44 is not set +# CONFIG_LV_FONT_MONTSERRAT_46 is not set +# CONFIG_LV_FONT_MONTSERRAT_48 is not set +# CONFIG_LV_FONT_MONTSERRAT_28_COMPRESSED is not set +# CONFIG_LV_FONT_DEJAVU_16_PERSIAN_HEBREW is not set +# CONFIG_LV_FONT_SIMSUN_14_CJK is not set +# CONFIG_LV_FONT_SIMSUN_16_CJK is not set +# CONFIG_LV_FONT_UNSCII_8 is not set +# CONFIG_LV_FONT_UNSCII_16 is not set +# end of Enable built-in fonts + +# CONFIG_LV_FONT_DEFAULT_MONTSERRAT_8 is not set +# CONFIG_LV_FONT_DEFAULT_MONTSERRAT_10 is not set +# CONFIG_LV_FONT_DEFAULT_MONTSERRAT_12 is not set +CONFIG_LV_FONT_DEFAULT_MONTSERRAT_14=y +# CONFIG_LV_FONT_DEFAULT_MONTSERRAT_16 is not set +# CONFIG_LV_FONT_DEFAULT_MONTSERRAT_18 is not set +# CONFIG_LV_FONT_DEFAULT_MONTSERRAT_20 is not set +# CONFIG_LV_FONT_DEFAULT_MONTSERRAT_22 is not set +# CONFIG_LV_FONT_DEFAULT_MONTSERRAT_24 is not set +# CONFIG_LV_FONT_DEFAULT_MONTSERRAT_26 is not set +# CONFIG_LV_FONT_DEFAULT_MONTSERRAT_28 is not set +# CONFIG_LV_FONT_DEFAULT_MONTSERRAT_30 is not set +# CONFIG_LV_FONT_DEFAULT_MONTSERRAT_32 is not set +# CONFIG_LV_FONT_DEFAULT_MONTSERRAT_34 is not set +# CONFIG_LV_FONT_DEFAULT_MONTSERRAT_36 is not set +# CONFIG_LV_FONT_DEFAULT_MONTSERRAT_38 is not set +# CONFIG_LV_FONT_DEFAULT_MONTSERRAT_40 is not set +# CONFIG_LV_FONT_DEFAULT_MONTSERRAT_42 is not set +# CONFIG_LV_FONT_DEFAULT_MONTSERRAT_44 is not set +# CONFIG_LV_FONT_DEFAULT_MONTSERRAT_46 is not set +# CONFIG_LV_FONT_DEFAULT_MONTSERRAT_48 is not set +# CONFIG_LV_FONT_DEFAULT_MONTSERRAT_28_COMPRESSED is not set +# CONFIG_LV_FONT_DEFAULT_DEJAVU_16_PERSIAN_HEBREW is not set +# CONFIG_LV_FONT_DEFAULT_SIMSUN_14_CJK is not set +# CONFIG_LV_FONT_DEFAULT_SIMSUN_16_CJK is not set +# CONFIG_LV_FONT_DEFAULT_UNSCII_8 is not set +# CONFIG_LV_FONT_DEFAULT_UNSCII_16 is not set +# CONFIG_LV_FONT_FMT_TXT_LARGE is not set +# CONFIG_LV_USE_FONT_COMPRESSED is not set +CONFIG_LV_USE_FONT_PLACEHOLDER=y +# end of Font Usage + +# +# Text Settings +# +CONFIG_LV_TXT_ENC_UTF8=y +# CONFIG_LV_TXT_ENC_ASCII is not set +CONFIG_LV_TXT_BREAK_CHARS=" ,.;:-_)}" +CONFIG_LV_TXT_LINE_BREAK_LONG_LEN=0 +# CONFIG_LV_USE_BIDI is not set +# CONFIG_LV_USE_ARABIC_PERSIAN_CHARS is not set +# end of Text Settings + +# +# Widget Usage +# +CONFIG_LV_WIDGETS_HAS_DEFAULT_VALUE=y +CONFIG_LV_USE_ANIMIMG=y +CONFIG_LV_USE_ARC=y +CONFIG_LV_USE_BAR=y +CONFIG_LV_USE_BUTTON=y +CONFIG_LV_USE_BUTTONMATRIX=y +CONFIG_LV_USE_CALENDAR=y +# CONFIG_LV_CALENDAR_WEEK_STARTS_MONDAY is not set +CONFIG_LV_USE_CALENDAR_HEADER_ARROW=y +CONFIG_LV_USE_CALENDAR_HEADER_DROPDOWN=y +# CONFIG_LV_USE_CALENDAR_CHINESE is not set +CONFIG_LV_USE_CANVAS=y +CONFIG_LV_USE_CHART=y +CONFIG_LV_USE_CHECKBOX=y +CONFIG_LV_USE_DROPDOWN=y +CONFIG_LV_USE_IMAGE=y +CONFIG_LV_USE_IMAGEBUTTON=y +CONFIG_LV_USE_KEYBOARD=y +CONFIG_LV_USE_LABEL=y +CONFIG_LV_LABEL_TEXT_SELECTION=y +CONFIG_LV_LABEL_LONG_TXT_HINT=y +CONFIG_LV_LABEL_WAIT_CHAR_COUNT=3 +CONFIG_LV_USE_LED=y +CONFIG_LV_USE_LINE=y +CONFIG_LV_USE_LIST=y +CONFIG_LV_USE_MENU=y +CONFIG_LV_USE_MSGBOX=y +CONFIG_LV_USE_ROLLER=y +CONFIG_LV_USE_SCALE=y +CONFIG_LV_USE_SLIDER=y +CONFIG_LV_USE_SPAN=y +CONFIG_LV_SPAN_SNIPPET_STACK_SIZE=64 +CONFIG_LV_USE_SPINBOX=y +CONFIG_LV_USE_SPINNER=y +CONFIG_LV_USE_SWITCH=y +CONFIG_LV_USE_TEXTAREA=y +CONFIG_LV_TEXTAREA_DEF_PWD_SHOW_TIME=1500 +CONFIG_LV_USE_TABLE=y +CONFIG_LV_USE_TABVIEW=y +CONFIG_LV_USE_TILEVIEW=y +CONFIG_LV_USE_WIN=y +# end of Widget Usage + +# +# Themes +# +CONFIG_LV_USE_THEME_DEFAULT=y +# CONFIG_LV_THEME_DEFAULT_DARK is not set +CONFIG_LV_THEME_DEFAULT_GROW=y +CONFIG_LV_THEME_DEFAULT_TRANSITION_TIME=80 +CONFIG_LV_USE_THEME_SIMPLE=y +# CONFIG_LV_USE_THEME_MONO is not set +# end of Themes + +# +# Layouts +# +CONFIG_LV_USE_FLEX=y +CONFIG_LV_USE_GRID=y +# end of Layouts + +# +# 3rd Party Libraries +# +CONFIG_LV_FS_DEFAULT_DRIVE_LETTER=0 +# CONFIG_LV_USE_FS_STDIO is not set +# CONFIG_LV_USE_FS_POSIX is not set +# CONFIG_LV_USE_FS_WIN32 is not set +# CONFIG_LV_USE_FS_FATFS is not set +# CONFIG_LV_USE_FS_MEMFS is not set +# CONFIG_LV_USE_FS_LITTLEFS is not set +# CONFIG_LV_USE_FS_ARDUINO_ESP_LITTLEFS is not set +# CONFIG_LV_USE_FS_ARDUINO_SD is not set +# CONFIG_LV_USE_LODEPNG is not set +# CONFIG_LV_USE_LIBPNG is not set +# CONFIG_LV_USE_BMP is not set +# CONFIG_LV_USE_TJPGD is not set +# CONFIG_LV_USE_LIBJPEG_TURBO is not set +# CONFIG_LV_USE_GIF is not set +# CONFIG_LV_BIN_DECODER_RAM_LOAD is not set +# CONFIG_LV_USE_RLE is not set +# CONFIG_LV_USE_QRCODE is not set +# CONFIG_LV_USE_BARCODE is not set +# CONFIG_LV_USE_FREETYPE is not set +# CONFIG_LV_USE_TINY_TTF is not set +# CONFIG_LV_USE_RLOTTIE is not set +# CONFIG_LV_USE_THORVG is not set +# CONFIG_LV_USE_LZ4 is not set +# CONFIG_LV_USE_FFMPEG is not set +# end of 3rd Party Libraries + +# +# Others +# +# CONFIG_LV_USE_SNAPSHOT is not set +# CONFIG_LV_USE_SYSMON is not set +# CONFIG_LV_USE_PROFILER is not set +# CONFIG_LV_USE_MONKEY is not set +# CONFIG_LV_USE_GRIDNAV is not set +# CONFIG_LV_USE_FRAGMENT is not set +# CONFIG_LV_USE_IMGFONT is not set +CONFIG_LV_USE_OBSERVER=y +# CONFIG_LV_USE_IME_PINYIN is not set +# CONFIG_LV_USE_FILE_EXPLORER is not set +CONFIG_LVGL_VERSION_MAJOR=9 +CONFIG_LVGL_VERSION_MINOR=2 +CONFIG_LVGL_VERSION_PATCH=2 +# end of Others + +# +# Devices +# +# CONFIG_LV_USE_SDL is not set +# CONFIG_LV_USE_X11 is not set +# CONFIG_LV_USE_WAYLAND is not set +# CONFIG_LV_USE_LINUX_FBDEV is not set +# CONFIG_LV_USE_NUTTX is not set +# CONFIG_LV_USE_LINUX_DRM is not set +# CONFIG_LV_USE_TFT_ESPI is not set +# CONFIG_LV_USE_EVDEV is not set +# CONFIG_LV_USE_LIBINPUT is not set +# CONFIG_LV_USE_ST7735 is not set +# CONFIG_LV_USE_ST7789 is not set +# CONFIG_LV_USE_ST7796 is not set +# CONFIG_LV_USE_ILI9341 is not set +# CONFIG_LV_USE_GENERIC_MIPI is not set +# CONFIG_LV_USE_RENESAS_GLCDC is not set +# CONFIG_LV_USE_OPENGLES is not set +# CONFIG_LV_USE_QNX is not set +# end of Devices + +# +# Examples +# +CONFIG_LV_BUILD_EXAMPLES=y +# end of Examples + +# +# Demos +# +# CONFIG_LV_USE_DEMO_WIDGETS is not set +# CONFIG_LV_USE_DEMO_KEYPAD_AND_ENCODER is not set +# CONFIG_LV_USE_DEMO_RENDER is not set +# CONFIG_LV_USE_DEMO_SCROLL is not set +# CONFIG_LV_USE_DEMO_STRESS is not set +# CONFIG_LV_USE_DEMO_MUSIC is not set +# CONFIG_LV_USE_DEMO_FLEX_LAYOUT is not set +# CONFIG_LV_USE_DEMO_MULTILANG is not set +# end of Demos +# end of LVGL configuration +# end of Component config + +CONFIG_IDF_EXPERIMENTAL_FEATURES=y + +# Deprecated options for backward compatibility +# CONFIG_APP_BUILD_TYPE_ELF_RAM is not set +# CONFIG_NO_BLOBS is not set +# CONFIG_APP_ROLLBACK_ENABLE is not set +# CONFIG_LOG_BOOTLOADER_LEVEL_NONE is not set +# CONFIG_LOG_BOOTLOADER_LEVEL_ERROR is not set +# CONFIG_LOG_BOOTLOADER_LEVEL_WARN is not set +CONFIG_LOG_BOOTLOADER_LEVEL_INFO=y +# CONFIG_LOG_BOOTLOADER_LEVEL_DEBUG is not set +# CONFIG_LOG_BOOTLOADER_LEVEL_VERBOSE is not set +CONFIG_LOG_BOOTLOADER_LEVEL=3 +# CONFIG_FLASH_ENCRYPTION_ENABLED is not set +CONFIG_FLASHMODE_QIO=y +# CONFIG_FLASHMODE_QOUT is not set +# CONFIG_FLASHMODE_DIO is not set +# CONFIG_FLASHMODE_DOUT is not set +CONFIG_MONITOR_BAUD=115200 +# CONFIG_OPTIMIZATION_LEVEL_DEBUG is not set +# CONFIG_COMPILER_OPTIMIZATION_LEVEL_DEBUG is not set +# CONFIG_COMPILER_OPTIMIZATION_DEFAULT is not set +# CONFIG_OPTIMIZATION_LEVEL_RELEASE is not set +# CONFIG_COMPILER_OPTIMIZATION_LEVEL_RELEASE is not set +CONFIG_OPTIMIZATION_ASSERTIONS_ENABLED=y +# CONFIG_OPTIMIZATION_ASSERTIONS_SILENT is not set +# CONFIG_OPTIMIZATION_ASSERTIONS_DISABLED is not set +CONFIG_OPTIMIZATION_ASSERTION_LEVEL=2 +# CONFIG_CXX_EXCEPTIONS is not set +CONFIG_STACK_CHECK_NONE=y +# CONFIG_STACK_CHECK_NORM is not set +# CONFIG_STACK_CHECK_STRONG is not set +# CONFIG_STACK_CHECK_ALL is not set +# CONFIG_WARN_WRITE_STRINGS is not set +# CONFIG_ESP32_APPTRACE_DEST_TRAX is not set +CONFIG_ESP32_APPTRACE_DEST_NONE=y +CONFIG_ESP32_APPTRACE_LOCK_ENABLE=y +# CONFIG_EXTERNAL_COEX_ENABLE is not set +# CONFIG_ESP_WIFI_EXTERNAL_COEXIST_ENABLE is not set +# CONFIG_CAM_CTLR_DVP_CAM_ISR_IRAM_SAFE is not set +# CONFIG_GPTIMER_ISR_IRAM_SAFE is not set +# CONFIG_MCPWM_ISR_IRAM_SAFE is not set +# CONFIG_EVENT_LOOP_PROFILING is not set +CONFIG_POST_EVENTS_FROM_ISR=y +CONFIG_POST_EVENTS_FROM_IRAM_ISR=y +CONFIG_GDBSTUB_SUPPORT_TASKS=y +CONFIG_GDBSTUB_MAX_TASKS=32 +# CONFIG_OTA_ALLOW_HTTP is not set +CONFIG_ESP32S3_DEEP_SLEEP_WAKEUP_DELAY=2000 +CONFIG_ESP_SLEEP_DEEP_SLEEP_WAKEUP_DELAY=2000 +CONFIG_ESP32S3_RTC_CLK_SRC_INT_RC=y +# CONFIG_ESP32S3_RTC_CLK_SRC_EXT_CRYS is not set +# CONFIG_ESP32S3_RTC_CLK_SRC_EXT_OSC is not set +# CONFIG_ESP32S3_RTC_CLK_SRC_INT_8MD256 is not set +CONFIG_ESP32S3_RTC_CLK_CAL_CYCLES=1024 +CONFIG_PERIPH_CTRL_FUNC_IN_IRAM=y +CONFIG_BROWNOUT_DET=y +CONFIG_ESP32S3_BROWNOUT_DET=y +CONFIG_BROWNOUT_DET_LVL_SEL_7=y +CONFIG_ESP32S3_BROWNOUT_DET_LVL_SEL_7=y +# CONFIG_BROWNOUT_DET_LVL_SEL_6 is not set +# CONFIG_ESP32S3_BROWNOUT_DET_LVL_SEL_6 is not set +# CONFIG_BROWNOUT_DET_LVL_SEL_5 is not set +# CONFIG_ESP32S3_BROWNOUT_DET_LVL_SEL_5 is not set +# CONFIG_BROWNOUT_DET_LVL_SEL_4 is not set +# CONFIG_ESP32S3_BROWNOUT_DET_LVL_SEL_4 is not set +# CONFIG_BROWNOUT_DET_LVL_SEL_3 is not set +# CONFIG_ESP32S3_BROWNOUT_DET_LVL_SEL_3 is not set +# CONFIG_BROWNOUT_DET_LVL_SEL_2 is not set +# CONFIG_ESP32S3_BROWNOUT_DET_LVL_SEL_2 is not set +# CONFIG_BROWNOUT_DET_LVL_SEL_1 is not set +# CONFIG_ESP32S3_BROWNOUT_DET_LVL_SEL_1 is not set +CONFIG_BROWNOUT_DET_LVL=7 +CONFIG_ESP32S3_BROWNOUT_DET_LVL=7 +CONFIG_ESP_SYSTEM_BROWNOUT_INTR=y +CONFIG_ESP32_PHY_CALIBRATION_AND_DATA_STORAGE=y +# CONFIG_ESP32_PHY_INIT_DATA_IN_PARTITION is not set +CONFIG_ESP32_PHY_MAX_WIFI_TX_POWER=20 +CONFIG_ESP32_PHY_MAX_TX_POWER=20 +# CONFIG_REDUCE_PHY_TX_POWER is not set +# CONFIG_ESP32_REDUCE_PHY_TX_POWER is not set +CONFIG_ESP_SYSTEM_PM_POWER_DOWN_CPU=y +CONFIG_PM_POWER_DOWN_TAGMEM_IN_LIGHT_SLEEP=y +CONFIG_ESP32S3_SPIRAM_SUPPORT=y +CONFIG_DEFAULT_PSRAM_CLK_IO=30 +CONFIG_DEFAULT_PSRAM_CS_IO=26 +# CONFIG_ESP32S3_DEFAULT_CPU_FREQ_80 is not set +# CONFIG_ESP32S3_DEFAULT_CPU_FREQ_160 is not set +CONFIG_ESP32S3_DEFAULT_CPU_FREQ_240=y +CONFIG_ESP32S3_DEFAULT_CPU_FREQ_MHZ=240 +CONFIG_SYSTEM_EVENT_QUEUE_SIZE=32 +CONFIG_SYSTEM_EVENT_TASK_STACK_SIZE=2304 +CONFIG_MAIN_TASK_STACK_SIZE=4096 +CONFIG_CONSOLE_UART_DEFAULT=y +# CONFIG_CONSOLE_UART_CUSTOM is not set +# CONFIG_CONSOLE_UART_NONE is not set +# CONFIG_ESP_CONSOLE_UART_NONE is not set +CONFIG_CONSOLE_UART=y +CONFIG_CONSOLE_UART_NUM=0 +CONFIG_CONSOLE_UART_BAUDRATE=115200 +CONFIG_INT_WDT=y +CONFIG_INT_WDT_TIMEOUT_MS=300 +CONFIG_INT_WDT_CHECK_CPU1=y +CONFIG_TASK_WDT=y +CONFIG_ESP_TASK_WDT=y +# CONFIG_TASK_WDT_PANIC is not set +CONFIG_TASK_WDT_TIMEOUT_S=10 +CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU0=y +CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1=y +# CONFIG_ESP32_DEBUG_STUBS_ENABLE is not set +CONFIG_ESP32S3_DEBUG_OCDAWARE=y +CONFIG_IPC_TASK_STACK_SIZE=1280 +CONFIG_TIMER_TASK_STACK_SIZE=3584 +CONFIG_ESP32_WIFI_ENABLED=y +CONFIG_ESP32_WIFI_STATIC_RX_BUFFER_NUM=16 +CONFIG_ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM=32 +CONFIG_ESP32_WIFI_STATIC_TX_BUFFER=y +CONFIG_ESP32_WIFI_TX_BUFFER_TYPE=0 +CONFIG_ESP32_WIFI_STATIC_TX_BUFFER_NUM=16 +CONFIG_ESP32_WIFI_CACHE_TX_BUFFER_NUM=32 +# CONFIG_ESP32_WIFI_CSI_ENABLED is not set +CONFIG_ESP32_WIFI_AMPDU_TX_ENABLED=y +CONFIG_ESP32_WIFI_TX_BA_WIN=6 +CONFIG_ESP32_WIFI_AMPDU_RX_ENABLED=y +CONFIG_ESP32_WIFI_RX_BA_WIN=16 +# CONFIG_ESP32_WIFI_AMSDU_TX_ENABLED is not set +CONFIG_ESP32_WIFI_NVS_ENABLED=y +CONFIG_ESP32_WIFI_TASK_PINNED_TO_CORE_0=y +# CONFIG_ESP32_WIFI_TASK_PINNED_TO_CORE_1 is not set +CONFIG_ESP32_WIFI_SOFTAP_BEACON_MAX_LEN=752 +CONFIG_ESP32_WIFI_MGMT_SBUF_NUM=32 +CONFIG_ESP32_WIFI_IRAM_OPT=y +CONFIG_ESP32_WIFI_RX_IRAM_OPT=y +CONFIG_ESP32_WIFI_ENABLE_WPA3_SAE=y +CONFIG_ESP32_WIFI_ENABLE_WPA3_OWE_STA=y +CONFIG_WPA_MBEDTLS_CRYPTO=y +CONFIG_WPA_MBEDTLS_TLS_CLIENT=y +# CONFIG_WPA_WAPI_PSK is not set +# CONFIG_WPA_SUITE_B_192 is not set +# CONFIG_WPA_11KV_SUPPORT is not set +# CONFIG_WPA_MBO_SUPPORT is not set +# CONFIG_WPA_DPP_SUPPORT is not set +# CONFIG_WPA_11R_SUPPORT is not set +# CONFIG_WPA_WPS_SOFTAP_REGISTRAR is not set +# CONFIG_WPA_WPS_STRICT is not set +# CONFIG_WPA_DEBUG_PRINT is not set +# CONFIG_WPA_TESTING_OPTIONS is not set +# CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH is not set +# CONFIG_ESP32_ENABLE_COREDUMP_TO_UART is not set +CONFIG_ESP32_ENABLE_COREDUMP_TO_NONE=y +CONFIG_TIMER_TASK_PRIORITY=1 +CONFIG_TIMER_TASK_STACK_DEPTH=4096 +CONFIG_TIMER_QUEUE_LENGTH=10 +# CONFIG_ENABLE_STATIC_TASK_CLEAN_UP_HOOK is not set +CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY=y +# CONFIG_HAL_ASSERTION_SILIENT is not set +# CONFIG_L2_TO_L3_COPY is not set +CONFIG_ESP_GRATUITOUS_ARP=y +CONFIG_GARP_TMR_INTERVAL=60 +CONFIG_TCPIP_RECVMBOX_SIZE=32 +CONFIG_TCP_MAXRTX=12 +CONFIG_TCP_SYNMAXRTX=12 +CONFIG_TCP_MSS=1440 +CONFIG_TCP_MSL=60000 +CONFIG_TCP_SND_BUF_DEFAULT=32768 +CONFIG_TCP_WND_DEFAULT=32768 +CONFIG_TCP_RECVMBOX_SIZE=6 +CONFIG_TCP_QUEUE_OOSEQ=y +CONFIG_TCP_OVERSIZE_MSS=y +# CONFIG_TCP_OVERSIZE_QUARTER_MSS is not set +# CONFIG_TCP_OVERSIZE_DISABLE is not set +CONFIG_UDP_RECVMBOX_SIZE=6 +CONFIG_TCPIP_TASK_STACK_SIZE=3072 +CONFIG_TCPIP_TASK_AFFINITY_NO_AFFINITY=y +# CONFIG_TCPIP_TASK_AFFINITY_CPU0 is not set +# CONFIG_TCPIP_TASK_AFFINITY_CPU1 is not set +CONFIG_TCPIP_TASK_AFFINITY=0x7FFFFFFF +# CONFIG_PPP_SUPPORT is not set +CONFIG_NEWLIB_STDOUT_LINE_ENDING_CRLF=y +# CONFIG_NEWLIB_STDOUT_LINE_ENDING_LF is not set +# CONFIG_NEWLIB_STDOUT_LINE_ENDING_CR is not set +# CONFIG_NEWLIB_STDIN_LINE_ENDING_CRLF is not set +# CONFIG_NEWLIB_STDIN_LINE_ENDING_LF is not set +CONFIG_NEWLIB_STDIN_LINE_ENDING_CR=y +# CONFIG_NEWLIB_NANO_FORMAT is not set +CONFIG_NEWLIB_TIME_SYSCALL_USE_RTC_HRT=y +CONFIG_ESP32S3_TIME_SYSCALL_USE_RTC_SYSTIMER=y +CONFIG_ESP32S3_TIME_SYSCALL_USE_RTC_FRC1=y +# CONFIG_NEWLIB_TIME_SYSCALL_USE_RTC is not set +# CONFIG_ESP32S3_TIME_SYSCALL_USE_RTC is not set +# CONFIG_NEWLIB_TIME_SYSCALL_USE_HRT is not set +# CONFIG_ESP32S3_TIME_SYSCALL_USE_SYSTIMER is not set +# CONFIG_ESP32S3_TIME_SYSCALL_USE_FRC1 is not set +# CONFIG_NEWLIB_TIME_SYSCALL_USE_NONE is not set +# CONFIG_ESP32S3_TIME_SYSCALL_USE_NONE is not set +CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT=5 +CONFIG_ESP32_PTHREAD_TASK_STACK_SIZE_DEFAULT=3072 +CONFIG_ESP32_PTHREAD_STACK_MIN=768 +CONFIG_ESP32_DEFAULT_PTHREAD_CORE_NO_AFFINITY=y +# CONFIG_ESP32_DEFAULT_PTHREAD_CORE_0 is not set +# CONFIG_ESP32_DEFAULT_PTHREAD_CORE_1 is not set +CONFIG_ESP32_PTHREAD_TASK_CORE_DEFAULT=-1 +CONFIG_ESP32_PTHREAD_TASK_NAME_DEFAULT="pthread" +CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ABORTS=y +# CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_FAILS is not set +# CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ALLOWED is not set +CONFIG_SUPPRESS_SELECT_DEBUG_OUTPUT=y +CONFIG_SUPPORT_TERMIOS=y +CONFIG_SEMIHOSTFS_MAX_MOUNT_POINTS=1 +# End of deprecated options diff --git a/firmware/echoear-platform-agent/sdkconfig.defaults b/firmware/echoear-platform-agent/sdkconfig.defaults new file mode 100644 index 0000000..a5a9621 --- /dev/null +++ b/firmware/echoear-platform-agent/sdkconfig.defaults @@ -0,0 +1,68 @@ +# +# Serial flasher config +# +CONFIG_ESPTOOLPY_FLASHMODE_QIO=y +CONFIG_ESPTOOLPY_FLASH_SAMPLE_MODE_STR=y +CONFIG_ESPTOOLPY_FLASHFREQ_80M=y +CONFIG_ESPTOOLPY_FLASHFREQ="80m" +CONFIG_ESPTOOLPY_HEADER_FLASHSIZE_UPDATE=y +# CONFIG_ESPTOOLPY_FLASHSIZE_8MB is not set +# end of Serial flasher config + + +# +# Partition Table +# +# CONFIG_PARTITION_TABLE_SINGLE_APP is not set +# CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE is not set +# CONFIG_PARTITION_TABLE_TWO_OTA is not set +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" +CONFIG_PARTITION_TABLE_FILENAME="partitions.csv" +CONFIG_PARTITION_TABLE_MD5=y +# end of Partition Table + +# +# Compiler options +# +CONFIG_COMPILER_OPTIMIZATION_PERF=y + +# +# ESP WDT CONFIG +# +# CONFIG_TASK_WDT_PANIC is not set +CONFIG_TASK_WDT_TIMEOUT_S=10 + +# +# ESP System Settings +# +CONFIG_ESP_MAIN_TASK_STACK_SIZE=4096 + +# +# ESP-TLS +# +CONFIG_ESP_TLS_INSECURE=y +CONFIG_ESP_TLS_SKIP_SERVER_CERT_VERIFY=y + +# +# FREERTOS +# +CONFIG_FREERTOS_ENABLE_BACKWARD_COMPATIBILITY=y +CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=4096 +CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS=y +CONFIG_FREERTOS_VTASKLIST_INCLUDE_COREID=y +CONFIG_FREERTOS_HZ=1000 + +# +# HTTP Server +# +CONFIG_HTTPD_MAX_REQ_HDR_LEN=1024 +CONFIG_HTTPD_MAX_URI_LEN=512 +CONFIG_HTTPD_ERR_RESP_NO_DELAY=y +CONFIG_HTTPD_PURGE_BUF_LEN=32 +# CONFIG_HTTPD_LOG_PURGE_DATA is not set +# CONFIG_HTTPD_WS_SUPPORT is not set +# CONFIG_HTTPD_QUEUE_WORK_BLOCKING is not set +CONFIG_HTTPD_SERVER_EVENT_POST_TIMEOUT=2000 +# end of HTTP Server +CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y diff --git a/firmware/echoear-platform-agent/sdkconfig.defaults.esp32s3 b/firmware/echoear-platform-agent/sdkconfig.defaults.esp32s3 new file mode 100644 index 0000000..815e3f4 --- /dev/null +++ b/firmware/echoear-platform-agent/sdkconfig.defaults.esp32s3 @@ -0,0 +1,126 @@ +CONFIG_IDF_CMAKE=y +CONFIG_IDF_TARGET_ARCH_XTENSA=y +CONFIG_IDF_TARGET="esp32s3" +CONFIG_IDF_TARGET_ESP32S3=y +CONFIG_IDF_FIRMWARE_CHIP_ID=0x0009 + +# +# Make experimental features visible +# +CONFIG_IDF_EXPERIMENTAL_FEATURES=y + +# +# Serial flasher config +# +CONFIG_BOOTLOADER_FLASH_DC_AWARE=y +# CONFIG_ESPTOOLPY_FLASHFREQ_120M is not set +CONFIG_SPI_FLASH_HPM_ENA=y +# end of Serial flasher config + +# +# Audio HAL +# +CONFIG_ESP32_S3_KORVO2_V3_BOARD=y +# end of Audio HAL + +# +# Audio Recorder +# +CONFIG_AFE_MIC_NUM=2 +# end of Audio Recorder + +# +# ESP Speech Recognition +# +CONFIG_MODEL_IN_FLASH=y +CONFIG_AFE_INTERFACE_V1=y +CONFIG_SR_WN_WN9_HILEXIN=n +# end of ESP Speech Recognition + +# +# Component config +# + +# +# Driver configurations +# + +# +# mbedTLS +# +# CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC is not set +CONFIG_MBEDTLS_EXTERNAL_MEM_ALLOC=y +# CONFIG_MBEDTLS_HARDWARE_AES is not set +# CONFIG_MBEDTLS_HARDWARE_SHA is not set +# end of mbedTLS + + +# +# ESP32s3-PSRAM +# +CONFIG_SPIRAM_XIP_FROM_PSRAM=y + +# +# ESP32S3-Specific +# +# CONFIG_ESP32S3_DEFAULT_CPU_FREQ_80 is not set +# CONFIG_ESP32S3_DEFAULT_CPU_FREQ_160 is not set +CONFIG_ESP32S3_DEFAULT_CPU_FREQ_240=y +CONFIG_ESP32S3_DEFAULT_CPU_FREQ_MHZ=240 + +# +# Cache config +# +# CONFIG_ESP32S3_INSTRUCTION_CACHE_16KB is not set +CONFIG_ESP32S3_INSTRUCTION_CACHE_32KB=y +CONFIG_ESP32S3_INSTRUCTION_CACHE_SIZE=0x8000 +# CONFIG_ESP32S3_INSTRUCTION_CACHE_4WAYS is not set +CONFIG_ESP32S3_INSTRUCTION_CACHE_8WAYS=y +CONFIG_ESP32S3_ICACHE_ASSOCIATED_WAYS=8 +CONFIG_ESP32S3_INSTRUCTION_CACHE_LINE_32B=y +CONFIG_ESP32S3_INSTRUCTION_CACHE_LINE_SIZE=32 +# CONFIG_ESP32S3_INSTRUCTION_CACHE_WRAP is not set +# CONFIG_ESP32S3_DATA_CACHE_16KB is not set +# CONFIG_ESP32S3_DATA_CACHE_32KB is not set +CONFIG_ESP32S3_DATA_CACHE_64KB=y +CONFIG_ESP32S3_DATA_CACHE_SIZE=0x10000 +# CONFIG_ESP32S3_DATA_CACHE_4WAYS is not set +CONFIG_ESP32S3_DATA_CACHE_8WAYS=y +CONFIG_ESP32S3_DCACHE_ASSOCIATED_WAYS=8 +# CONFIG_ESP32S3_DATA_CACHE_LINE_32B is not set +CONFIG_ESP32S3_DATA_CACHE_LINE_64B=y +CONFIG_ESP32S3_DATA_CACHE_LINE_SIZE=64 +# CONFIG_ESP32S3_DATA_CACHE_WRAP is not set +# end of Cache config + +CONFIG_ESP32S3_SPIRAM_SUPPORT=y + +# +# SPI RAM config +# +CONFIG_SPIRAM_MODE_OCT=y +CONFIG_SPIRAM_TYPE_AUTO=y +# CONFIG_SPIRAM_SPEED_120M is not set +CONFIG_SPIRAM=y +CONFIG_SPIRAM_BOOT_INIT=y +CONFIG_SPIRAM_USE_MALLOC=y +CONFIG_SPIRAM_MEMTEST=y +CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=4096 +CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP=y +CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL=32768 +# end of SPI RAM config +# end of ESP32S3-Specific + +CONFIG_ESPTOOLPY_FLASHFREQ_80M=y + +CONFIG_SPIRAM_SPEED_80M=y + +CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y + +CONFIG_SR_WN_WN9_HILEXIN=y + +# EchoEar uses a 360x360 ST77916 QSPI panel. This choice also defines the +# rendering dimensions consumed by volc_hal_display.c. +CONFIG_EXAMPLE_LCD_INTERFACE_QSPI=y +CONFIG_LWIP_TCP_SND_BUF_DEFAULT=32768 +CONFIG_LWIP_TCP_WND_DEFAULT=32768 diff --git a/firmware/echoear-platform-agent/tests/setup-portal.test.cjs b/firmware/echoear-platform-agent/tests/setup-portal.test.cjs new file mode 100644 index 0000000..d03440a --- /dev/null +++ b/firmware/echoear-platform-agent/tests/setup-portal.test.cjs @@ -0,0 +1,37 @@ +const {test}=require('node:test'); +const assert=require('node:assert/strict'); +const fs=require('node:fs'); +const vm=require('node:vm'); +const html=fs.readFileSync(require('node:path').join(__dirname,'../main/assets/wifi_configuration_ap.html'),'utf8'); +const source=html.match(/