diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 000000000..8529eb7fc
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1,3 @@
+# These are supported funding model platforms
+
+open_collective: meshtastic
diff --git a/.github/actions/build-variant/action.yml b/.github/actions/build-variant/action.yml
index 67d002eea..f611908ee 100644
--- a/.github/actions/build-variant/action.yml
+++ b/.github/actions/build-variant/action.yml
@@ -27,10 +27,10 @@ inputs:
description: A newline separated list of paths to store as artifacts
required: false
default: ""
- include-web-ui:
- description: Include the web UI in the build
- required: false
- default: "false"
+ # include-web-ui:
+ # description: Include the web UI in the build
+ # required: false
+ # default: "false"
arch:
description: Processor arch name
required: true
@@ -43,29 +43,29 @@ runs:
id: base
uses: ./.github/actions/setup-base
- - name: Get web ui version
- if: inputs.include-web-ui == 'true'
- id: webver
- shell: bash
- run: |
- echo "ver=$(cat bin/web.version)" >> $GITHUB_OUTPUT
+ # - name: Get web ui version
+ # if: inputs.include-web-ui == 'true'
+ # id: webver
+ # shell: bash
+ # run: |
+ # echo "ver=$(cat bin/web.version)" >> $GITHUB_OUTPUT
- - name: Pull web ui
- if: inputs.include-web-ui == 'true'
- uses: dsaltares/fetch-gh-release-asset@master
- with:
- repo: meshtastic/web
- file: build.tar
- target: build.tar
- token: ${{ inputs.github_token }}
- version: tags/v${{ steps.webver.outputs.ver }}
+ # - name: Pull web ui
+ # if: inputs.include-web-ui == 'true'
+ # uses: dsaltares/fetch-gh-release-asset@master
+ # with:
+ # repo: meshtastic/web
+ # file: build.tar
+ # target: build.tar
+ # token: ${{ inputs.github_token }}
+ # version: tags/v${{ steps.webver.outputs.ver }}
- - name: Unpack web ui
- if: inputs.include-web-ui == 'true'
- shell: bash
- run: |
- tar -xf build.tar -C data/static
- rm build.tar
+ # - name: Unpack web ui
+ # if: inputs.include-web-ui == 'true'
+ # shell: bash
+ # run: |
+ # tar -xf build.tar -C data/static
+ # rm build.tar
- name: Remove debug flags for release
shell: bash
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
index a15b34aae..0142c57a2 100644
--- a/.github/pull_request_template.md
+++ b/.github/pull_request_template.md
@@ -1,6 +1,7 @@
## ๐ Thank you for sending in a pull request, here's some tips to get started!
### โ (Please delete all these tips and replace them with your text) โ
+
- Before starting on some new big chunk of code, it it is optional but highly recommended to open an issue first
to say "Hey, I think this idea X should be implemented and I'm starting work on it. My general plan is Y, any feedback
is appreciated." This will allow other devs to potentially save you time by not accidentially duplicating work etc...
@@ -15,12 +16,12 @@
- If you do not have the affected hardware to test your code changes adequately against regressions, please indicate this, so that contributors and commnunity members can help test your changes.
- If your PR gets accepted you can request a "Contributor" role in the Meshtastic Discord
-
## ๐ค Attestations
+
- [ ] I have tested that my proposed changes behave as described.
- [ ] I have tested that my proposed changes do not cause any obvious regressions on the following devices:
- [ ] Heltec (Lora32) V3
- - [ ] LilyGo T-Deck
+ - [ ] LilyGo T-Deck
- [ ] LilyGo T-Beam
- [ ] RAK WisBlock 4631
- [ ] Seeed Studio T-1000E tracker card
diff --git a/.github/workflows/build_esp32.yml b/.github/workflows/build_esp32.yml
index 4fc31f22c..32cd45000 100644
--- a/.github/workflows/build_esp32.yml
+++ b/.github/workflows/build_esp32.yml
@@ -11,27 +11,30 @@ permissions: read-all
jobs:
build-esp32:
- runs-on: ubuntu-latest
+ runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
+ - name: Get release version string
+ shell: bash
+ run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
+ id: version
+
- name: Build ESP32
id: build
- uses: ./.github/actions/build-variant
+ uses: meshtastic/gh-action-firmware@main
with:
- github_token: ${{ secrets.GITHUB_TOKEN }}
- board: ${{ inputs.board }}
- remove-debug-flags: >-
- ./arch/esp32/esp32.ini
- ./arch/esp32/esp32s2.ini
- ./arch/esp32/esp32s3.ini
- ./arch/esp32/esp32c3.ini
- ./arch/esp32/esp32c6.ini
- build-script-path: bin/build-esp32.sh
- ota-firmware-source: firmware.bin
- ota-firmware-target: release/bleota.bin
- artifact-paths: |
+ pio_platform: esp32
+ pio_env: ${{ inputs.board }}
+ pio_target: build
+ ota_firmware_source: firmware.bin
+ ota_firmware_target: release/bleota.bin
+
+ - name: Store binaries as an artifact
+ uses: actions/upload-artifact@v4
+ with:
+ name: firmware-esp32-${{ inputs.board }}-${{ steps.version.outputs.long }}.zip
+ overwrite: true
+ path: |
release/*.bin
release/*.elf
- include-web-ui: true
- arch: esp32
diff --git a/.github/workflows/build_esp32_c3.yml b/.github/workflows/build_esp32_c3.yml
index 546762952..161786f99 100644
--- a/.github/workflows/build_esp32_c3.yml
+++ b/.github/workflows/build_esp32_c3.yml
@@ -11,27 +11,30 @@ permissions: read-all
jobs:
build-esp32-c3:
- runs-on: ubuntu-latest
+ runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
+ - name: Get release version string
+ shell: bash
+ run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
+ id: version
+
- name: Build ESP32-C3
id: build
- uses: ./.github/actions/build-variant
+ uses: meshtastic/gh-action-firmware@main
with:
- github_token: ${{ secrets.GITHUB_TOKEN }}
- board: ${{ inputs.board }}
- remove-debug-flags: >-
- ./arch/esp32/esp32.ini
- ./arch/esp32/esp32s2.ini
- ./arch/esp32/esp32s3.ini
- ./arch/esp32/esp32c3.ini
- ./arch/esp32/esp32c6.ini
- build-script-path: bin/build-esp32.sh
- ota-firmware-source: firmware-c3.bin
- ota-firmware-target: release/bleota-c3.bin
- artifact-paths: |
+ pio_platform: esp32
+ pio_env: ${{ inputs.board }}
+ pio_target: build
+ ota_firmware_source: firmware-c3.bin
+ ota_firmware_target: release/bleota-c3.bin
+
+ - name: Store binaries as an artifact
+ uses: actions/upload-artifact@v4
+ with:
+ name: firmware-esp32c3-${{ inputs.board }}-${{ steps.version.outputs.long }}.zip
+ overwrite: true
+ path: |
release/*.bin
release/*.elf
- include-web-ui: true
- arch: esp32c3
diff --git a/.github/workflows/build_esp32_c6.yml b/.github/workflows/build_esp32_c6.yml
index 56d4d806d..90cdcc78e 100644
--- a/.github/workflows/build_esp32_c6.yml
+++ b/.github/workflows/build_esp32_c6.yml
@@ -11,27 +11,30 @@ permissions: read-all
jobs:
build-esp32-c6:
- runs-on: ubuntu-latest
+ runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
+ - name: Get release version string
+ shell: bash
+ run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
+ id: version
+
- name: Build ESP32-C6
id: build
- uses: ./.github/actions/build-variant
+ uses: meshtastic/gh-action-firmware@main
with:
- github_token: ${{ secrets.GITHUB_TOKEN }}
- board: ${{ inputs.board }}
- remove-debug-flags: >-
- ./arch/esp32/esp32.ini
- ./arch/esp32/esp32s2.ini
- ./arch/esp32/esp32s3.ini
- ./arch/esp32/esp32c3.ini
- ./arch/esp32/esp32c6.ini
- build-script-path: bin/build-esp32.sh
- ota-firmware-source: firmware-c3.bin
- ota-firmware-target: release/bleota-c3.bin
- artifact-paths: |
+ pio_platform: esp32
+ pio_env: ${{ inputs.board }}
+ pio_target: build
+ ota_firmware_source: firmware-c3.bin
+ ota_firmware_target: release/bleota-c3.bin
+
+ - name: Store binaries as an artifact
+ uses: actions/upload-artifact@v4
+ with:
+ name: firmware-esp32c6-${{ inputs.board }}-${{ steps.version.outputs.long }}.zip
+ overwrite: true
+ path: |
release/*.bin
release/*.elf
- include-web-ui: true
- arch: esp32c6
diff --git a/.github/workflows/build_esp32_s3.yml b/.github/workflows/build_esp32_s3.yml
index a9c067ee1..e5ed48e3e 100644
--- a/.github/workflows/build_esp32_s3.yml
+++ b/.github/workflows/build_esp32_s3.yml
@@ -11,27 +11,30 @@ permissions: read-all
jobs:
build-esp32-s3:
- runs-on: ubuntu-latest
+ runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
+ - name: Get release version string
+ shell: bash
+ run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
+ id: version
+
- name: Build ESP32-S3
id: build
- uses: ./.github/actions/build-variant
+ uses: meshtastic/gh-action-firmware@main
with:
- github_token: ${{ secrets.GITHUB_TOKEN }}
- board: ${{ inputs.board }}
- remove-debug-flags: >-
- ./arch/esp32/esp32.ini
- ./arch/esp32/esp32s2.ini
- ./arch/esp32/esp32s3.ini
- ./arch/esp32/esp32c3.ini
- ./arch/esp32/esp32c6.ini
- build-script-path: bin/build-esp32.sh
- ota-firmware-source: firmware-s3.bin
- ota-firmware-target: release/bleota-s3.bin
- artifact-paths: |
+ pio_platform: esp32
+ pio_env: ${{ inputs.board }}
+ pio_target: build
+ ota_firmware_source: firmware-s3.bin
+ ota_firmware_target: release/bleota-s3.bin
+
+ - name: Store binaries as an artifact
+ uses: actions/upload-artifact@v4
+ with:
+ name: firmware-esp32s3-${{ inputs.board }}-${{ steps.version.outputs.long }}.zip
+ overwrite: true
+ path: |
release/*.bin
release/*.elf
- include-web-ui: true
- arch: esp32s3
diff --git a/.github/workflows/build_nrf52.yml b/.github/workflows/build_nrf52.yml
index 786508f86..312aeb372 100644
--- a/.github/workflows/build_nrf52.yml
+++ b/.github/workflows/build_nrf52.yml
@@ -11,20 +11,30 @@ permissions: read-all
jobs:
build-nrf52:
- runs-on: ubuntu-latest
+ runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
+ - name: Get release version string
+ shell: bash
+ run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
+ id: version
+
- name: Build NRF52
id: build
- uses: ./.github/actions/build-variant
+ uses: meshtastic/gh-action-firmware@main
with:
- github_token: ${{ secrets.GITHUB_TOKEN }}
- board: ${{ inputs.board }}
- build-script-path: bin/build-nrf52.sh
- artifact-paths: |
- release/*.hex
+ pio_platform: nrf52
+ pio_env: ${{ inputs.board }}
+ pio_target: build
+
+ - name: Store binaries as an artifact
+ uses: actions/upload-artifact@v4
+ with:
+ name: firmware-nrf52840-${{ inputs.board }}-${{ steps.version.outputs.long }}.zip
+ overwrite: true
+ path: |
release/*.uf2
release/*.elf
- release/*.zip
- arch: nrf52840
+ release/*.hex
+ release/*-ota.zip
diff --git a/.github/workflows/build_rpi2040.yml b/.github/workflows/build_rpi2040.yml
index 53fee34d2..2abd7a839 100644
--- a/.github/workflows/build_rpi2040.yml
+++ b/.github/workflows/build_rpi2040.yml
@@ -11,18 +11,28 @@ permissions: read-all
jobs:
build-rpi2040:
- runs-on: ubuntu-latest
+ runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
+ - name: Get release version string
+ shell: bash
+ run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
+ id: version
+
- name: Build Raspberry Pi 2040
id: build
- uses: ./.github/actions/build-variant
+ uses: meshtastic/gh-action-firmware@main
with:
- github_token: ${{ secrets.GITHUB_TOKEN }}
- board: ${{ inputs.board }}
- build-script-path: bin/build-rpi2040.sh
- artifact-paths: |
+ pio_platform: rp2xx0
+ pio_env: ${{ inputs.board }}
+ pio_target: build
+
+ - name: Store binaries as an artifact
+ uses: actions/upload-artifact@v4
+ with:
+ name: firmware-rp2040-${{ inputs.board }}-${{ steps.version.outputs.long }}.zip
+ overwrite: true
+ path: |
release/*.uf2
release/*.elf
- arch: rp2040
diff --git a/.github/workflows/build_stm32.yml b/.github/workflows/build_stm32.yml
index dc469d994..10680f422 100644
--- a/.github/workflows/build_stm32.yml
+++ b/.github/workflows/build_stm32.yml
@@ -11,19 +11,29 @@ permissions: read-all
jobs:
build-stm32:
- runs-on: ubuntu-latest
+ runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
+ - name: Get release version string
+ shell: bash
+ run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
+ id: version
+
- name: Build STM32WL
id: build
- uses: ./.github/actions/build-variant
+ uses: meshtastic/gh-action-firmware@main
with:
- github_token: ${{ secrets.GITHUB_TOKEN }}
- board: ${{ inputs.board }}
- build-script-path: bin/build-stm32.sh
- artifact-paths: |
+ pio_platform: stm32wl
+ pio_env: ${{ inputs.board }}
+ pio_target: build
+
+ - name: Store binaries as an artifact
+ uses: actions/upload-artifact@v4
+ with:
+ name: firmware-stm32-${{ inputs.board }}-${{ steps.version.outputs.long }}.zip
+ overwrite: true
+ path: |
release/*.hex
release/*.bin
release/*.elf
- arch: stm32
diff --git a/.github/workflows/daily_packaging.yml b/.github/workflows/daily_packaging.yml
index 11fe2043a..63d24687b 100644
--- a/.github/workflows/daily_packaging.yml
+++ b/.github/workflows/daily_packaging.yml
@@ -1,7 +1,7 @@
name: Daily Packaging
on:
schedule:
- - cron: 0 9 * * *
+ - cron: 0 2 * * *
workflow_dispatch:
push:
branches:
@@ -30,7 +30,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- series: [plucky, oracular, noble, jammy]
+ series: [plucky, noble, jammy]
uses: ./.github/workflows/package_ppa.yml
with:
ppa_repo: ppa:meshtastic/daily
diff --git a/.github/workflows/main_matrix.yml b/.github/workflows/main_matrix.yml
index 9b9877e04..a676efa1e 100644
--- a/.github/workflows/main_matrix.yml
+++ b/.github/workflows/main_matrix.yml
@@ -135,6 +135,7 @@ jobs:
board: ${{ matrix.board }}
build-debian-src:
+ if: github.repository == 'meshtastic/firmware'
uses: ./.github/workflows/build_debian_src.yml
with:
series: UNRELEASED
@@ -257,7 +258,6 @@ jobs:
./device-*.sh
./device-*.bat
./littlefs-*.bin
- ./littlefswebui-*.bin
./bleota*bin
./Meshtastic_nRF52_factory_erase*.uf2
retention-days: 30
@@ -426,7 +426,7 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
publish-firmware:
- runs-on: ubuntu-latest
+ runs-on: ubuntu-24.04
if: ${{ github.event_name == 'workflow_dispatch' }}
needs: [release-firmware]
env:
diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml
index 36ec22f17..309772b12 100644
--- a/.github/workflows/nightly.yml
+++ b/.github/workflows/nightly.yml
@@ -8,6 +8,7 @@ permissions: read-all
jobs:
trunk_check:
+ if: github.repository == 'meshtastic/firmware'
name: Trunk Check and Upload
runs-on: ubuntu-24.04
@@ -21,6 +22,7 @@ jobs:
trunk-token: ${{ secrets.TRUNK_TOKEN }}
trunk_upgrade:
+ if: github.repository == 'meshtastic/firmware'
# See: https://github.com/trunk-io/trunk-action/blob/v1/readme.md#automatic-upgrades
name: Trunk Upgrade (PR)
runs-on: ubuntu-24.04
diff --git a/.github/workflows/release_channels.yml b/.github/workflows/release_channels.yml
index 6f216b411..ed2de1717 100644
--- a/.github/workflows/release_channels.yml
+++ b/.github/workflows/release_channels.yml
@@ -20,7 +20,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- series: [plucky, oracular, noble, jammy]
+ series: [plucky, noble, jammy]
uses: ./.github/workflows/package_ppa.yml
with:
ppa_repo: |-
@@ -98,6 +98,7 @@ jobs:
uses: peter-evans/create-pull-request@v7
with:
base: ${{ github.event.repository.default_branch }}
+ branch: create-pull-request/bump-version
title: Bump release version
commit-message: automated bumps
add-paths: |
diff --git a/.github/workflows/sec_sast_semgrep_cron.yml b/.github/workflows/sec_sast_semgrep_cron.yml
index d7eef29b4..e391aa07b 100644
--- a/.github/workflows/sec_sast_semgrep_cron.yml
+++ b/.github/workflows/sec_sast_semgrep_cron.yml
@@ -13,6 +13,7 @@ permissions:
jobs:
semgrep-full:
+ if: github.repository == 'meshtastic/firmware'
runs-on: ubuntu-24.04
container:
image: semgrep/semgrep
diff --git a/.github/workflows/stale_bot.yml b/.github/workflows/stale_bot.yml
index 5ae6bdfc9..5a11fdfa8 100644
--- a/.github/workflows/stale_bot.yml
+++ b/.github/workflows/stale_bot.yml
@@ -11,6 +11,7 @@ permissions:
jobs:
stale_issues:
+ if: github.repository == 'meshtastic/firmware'
name: Close Stale Issues
runs-on: ubuntu-latest
diff --git a/.github/workflows/test_native.yml b/.github/workflows/test_native.yml
index 536d93665..dc05959fd 100644
--- a/.github/workflows/test_native.yml
+++ b/.github/workflows/test_native.yml
@@ -143,7 +143,7 @@ jobs:
merge-multiple: true
- name: Test Report
- uses: dorny/test-reporter@v2.1.0
+ uses: dorny/test-reporter@v2.1.1
with:
name: PlatformIO Tests
path: testreport.xml
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 28b6a40a5..34b28b39c 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -12,9 +12,11 @@ permissions:
jobs:
native-tests:
+ if: github.repository == 'meshtastic/firmware'
uses: ./.github/workflows/test_native.yml
hardware-tests:
+ if: github.repository == 'meshtastic/firmware'
runs-on: test-runner
steps:
- name: Checkout code
diff --git a/.github/workflows/update_protobufs.yml b/.github/workflows/update_protobufs.yml
index 5aa295b89..ccdcc19ae 100644
--- a/.github/workflows/update_protobufs.yml
+++ b/.github/workflows/update_protobufs.yml
@@ -33,6 +33,7 @@ jobs:
- name: Create pull request
uses: peter-evans/create-pull-request@v7
with:
+ branch: create-pull-request/update-protobufs
title: Update protobufs and classes
add-paths: |
protobufs
diff --git a/.gitignore b/.gitignore
index b63f431d1..cc742c6c1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -37,4 +37,7 @@ release/
.vscode/extensions.json
/compile_commands.json
src/mesh/raspihttp/certificate.pem
-src/mesh/raspihttp/private_key.pem
\ No newline at end of file
+src/mesh/raspihttp/private_key.pem
+
+# Ignore logo (set at build time with platformio-custom.py)
+data/boot/logo.*
diff --git a/.trunk/trunk.yaml b/.trunk/trunk.yaml
index fd827e229..f0271c856 100644
--- a/.trunk/trunk.yaml
+++ b/.trunk/trunk.yaml
@@ -4,31 +4,31 @@ cli:
plugins:
sources:
- id: trunk
- ref: v1.7.0
+ ref: v1.7.1
uri: https://github.com/trunk-io/plugins
lint:
enabled:
- - checkov@3.2.435
- - renovate@40.36.2
- - prettier@3.5.3
- - trufflehog@3.88.35
+ - checkov@3.2.450
+ - renovate@41.29.1
+ - prettier@3.6.2
+ - trufflehog@3.89.2
- yamllint@1.37.1
- - bandit@1.8.3
- - trivy@0.63.0
+ - bandit@1.8.6
+ - trivy@0.64.1
- taplo@0.9.3
- - ruff@0.11.12
+ - ruff@0.12.2
- isort@6.0.1
- markdownlint@0.45.0
- oxipng@9.1.5
- - svgo@3.3.2
+ - svgo@4.0.0
- actionlint@1.7.7
- - flake8@7.2.0
+ - flake8@7.3.0
- hadolint@2.12.1-beta
- shfmt@3.6.0
- shellcheck@0.10.0
- black@25.1.0
- git-diff-check
- - gitleaks@8.26.0
+ - gitleaks@8.27.2
- clang-format@16.0.3
ignore:
- linters: [ALL]
diff --git a/README.md b/README.md
index f34bf1839..a53fe9646 100644
--- a/README.md
+++ b/README.md
@@ -37,3 +37,4 @@ Join our community and help improve Meshtastic! ๐
## Stats

+
diff --git a/alpine.Dockerfile b/alpine.Dockerfile
index bf7cad6d4..670736241 100644
--- a/alpine.Dockerfile
+++ b/alpine.Dockerfile
@@ -3,7 +3,7 @@
# trunk-ignore-all(hadolint/DL3018): Do not pin apk package versions
# trunk-ignore-all(hadolint/DL3013): Do not pin pip package versions
-FROM python:3.13-alpine3.21 AS builder
+FROM python:3.13-alpine3.22 AS builder
ARG PIO_ENV=native
ENV PIP_ROOT_USER_ACTION=ignore
@@ -27,7 +27,7 @@ RUN bash ./bin/build-native.sh "$PIO_ENV" && \
# ##### PRODUCTION BUILD #############
-FROM alpine:3.21
+FROM alpine:3.22
LABEL org.opencontainers.image.title="Meshtastic" \
org.opencontainers.image.description="Alpine Meshtastic daemon" \
org.opencontainers.image.url="https://meshtastic.org" \
diff --git a/arch/esp32/esp32.ini b/arch/esp32/esp32.ini
index a6eff7bf9..6b9ebcb24 100644
--- a/arch/esp32/esp32.ini
+++ b/arch/esp32/esp32.ini
@@ -4,7 +4,7 @@ extends = arduino_base
custom_esp32_kind = esp32
platform =
# renovate: datasource=custom.pio depName=platformio/espressif32 packageName=platformio/platform/espressif32
- platformio/espressif32@6.10.0
+ platformio/espressif32@6.11.0
build_src_filter =
${arduino_base.build_src_filter} - - - - -
@@ -49,13 +49,13 @@ lib_deps =
${environmental_extra.lib_deps}
${radiolib_base.lib_deps}
# renovate: datasource=git-refs depName=meshtastic-esp32_https_server packageName=https://github.com/meshtastic/esp32_https_server gitBranch=master
- https://github.com/meshtastic/esp32_https_server/archive/896f1771ceb5979987a0b41028bf1b4e7aad419b.zip
+ https://github.com/meshtastic/esp32_https_server/archive/3223704846752e6d545139204837bdb2a55459ca.zip
# renovate: datasource=custom.pio depName=NimBLE-Arduino packageName=h2zero/library/NimBLE-Arduino
h2zero/NimBLE-Arduino@^1.4.3
# renovate: datasource=git-refs depName=libpax packageName=https://github.com/dbinfrago/libpax gitBranch=master
https://github.com/dbinfrago/libpax/archive/3cdc0371c375676a97967547f4065607d4c53fd1.zip
# renovate: datasource=custom.pio depName=XPowersLib packageName=lewisxhe/library/XPowersLib
- lewisxhe/XPowersLib@^0.2.7
+ lewisxhe/XPowersLib@0.3.0
# renovate: datasource=git-refs depName=meshtastic-ESP32_Codec2 packageName=https://github.com/meshtastic/ESP32_Codec2 gitBranch=master
https://github.com/meshtastic/ESP32_Codec2/archive/633326c78ac251c059ab3a8c430fcdf25b41672f.zip
# renovate: datasource=custom.pio depName=rweather/Crypto packageName=rweather/library/Crypto
diff --git a/arch/esp32/esp32c6.ini b/arch/esp32/esp32c6.ini
index 26b5c0f5b..1afb9b547 100644
--- a/arch/esp32/esp32c6.ini
+++ b/arch/esp32/esp32c6.ini
@@ -28,7 +28,7 @@ lib_deps =
${environmental_extra.lib_deps}
${radiolib_base.lib_deps}
# renovate: datasource=custom.pio depName=XPowersLib packageName=lewisxhe/library/XPowersLib
- lewisxhe/XPowersLib@^0.2.7
+ lewisxhe/XPowersLib@0.3.0
# renovate: datasource=git-refs depName=meshtastic-ESP32_Codec2 packageName=https://github.com/meshtastic/ESP32_Codec2 gitBranch=master
https://github.com/meshtastic/ESP32_Codec2/archive/633326c78ac251c059ab3a8c430fcdf25b41672f.zip
# renovate: datasource=custom.pio depName=rweather/Crypto packageName=rweather/library/Crypto
diff --git a/arch/portduino/portduino.ini b/arch/portduino/portduino.ini
index a19c50319..03a8a6583 100644
--- a/arch/portduino/portduino.ini
+++ b/arch/portduino/portduino.ini
@@ -2,7 +2,7 @@
[portduino_base]
platform =
# renovate: datasource=git-refs depName=platform-native packageName=https://github.com/meshtastic/platform-native gitBranch=develop
- https://github.com/meshtastic/platform-native/archive/622341c6de8a239704318b10c3dbb00c21a3eab3.zip
+ https://github.com/meshtastic/platform-native/archive/6cb7a455b440dd0738e8ed74a18136ed5cf7ea63.zip
framework = arduino
build_src_filter =
@@ -30,6 +30,8 @@ lib_deps =
lovyan03/LovyanGFX@^1.2.0
# renovate: datasource=git-refs depName=libch341-spi-userspace packageName=https://github.com/pine64/libch341-spi-userspace gitBranch=main
https://github.com/pine64/libch341-spi-userspace/archive/af9bc27c9c30fa90772279925b7c5913dff789b4.zip
+ # renovate: datasource=custom.pio depName=adafruit/Adafruit seesaw Library packageName=adafruit/library/Adafruit seesaw Library
+ adafruit/Adafruit seesaw Library@1.7.9
build_flags =
${arduino_base.build_flags}
@@ -48,4 +50,7 @@ build_flags =
-std=gnu17
-std=c++17
-lib_ignore = Adafruit NeoPixel
+lib_ignore =
+ Adafruit NeoPixel
+ Adafruit ST7735 and ST7789 Library
+ SD
diff --git a/arch/stm32/stm32.ini b/arch/stm32/stm32.ini
index 60bc41d23..355a2e313 100644
--- a/arch/stm32/stm32.ini
+++ b/arch/stm32/stm32.ini
@@ -2,7 +2,7 @@
extends = arduino_base
platform =
# renovate: datasource=custom.pio depName=platformio/ststm32 packageName=platformio/platform/ststm32
- platformio/ststm32@19.1.0
+ platformio/ststm32@19.2.0
platform_packages =
# TODO renovate
platformio/framework-arduinoststm32@https://github.com/stm32duino/Arduino_Core_STM32/archive/2.10.1.zip
@@ -16,14 +16,17 @@ build_flags =
${arduino_base.build_flags}
-flto
-Isrc/platform/stm32wl -g
- -DMESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
- -DMESHTASTIC_EXCLUDE_INPUTBROKER
- -DMESHTASTIC_EXCLUDE_I2C
- -DMESHTASTIC_EXCLUDE_POWERMON
- -DMESHTASTIC_EXCLUDE_SCREEN
- -DMESHTASTIC_EXCLUDE_MQTT
- -DMESHTASTIC_EXCLUDE_BLUETOOTH
- -DMESHTASTIC_EXCLUDE_GPS
+ -DMESHTASTIC_EXCLUDE_AUDIO=1
+ -DMESHTASTIC_EXCLUDE_ATAK=1 ; ATAK is quite big, disable it for big flash savings.
+ -DMESHTASTIC_EXCLUDE_INPUTBROKER=1
+ -DMESHTASTIC_EXCLUDE_POWERMON=1
+ -DMESHTASTIC_EXCLUDE_SCREEN=1
+ -DMESHTASTIC_EXCLUDE_MQTT=1
+ -DMESHTASTIC_EXCLUDE_BLUETOOTH=1
+ -DMESHTASTIC_EXCLUDE_GPS=1
+ -DMESHTASTIC_EXCLUDE_WIFI=1
+ -DMESHTASTIC_EXCLUDE_TZ=1 ; Exclude TZ to save some flash space.
+ -DPIO_FRAMEWORK_ARDUINO_NANOLIB_FLOAT_PRINTF ; This is REQUIRED for at least traceroute debug prints - without it the length ends up uninitialized.
;-DDEBUG_MUTE
-fmerge-all-constants
-ffunction-sections
@@ -38,10 +41,10 @@ debug_tool = stlink
lib_deps =
${env.lib_deps}
${radiolib_base.lib_deps}
+
# renovate: datasource=git-refs depName=caveman99-stm32-Crypto packageName=https://github.com/caveman99/Crypto gitBranch=main
https://github.com/caveman99/Crypto/archive/eae9c768054118a9399690f8af202853d1ae8516.zip
lib_ignore =
- Wire
I2CKeyPad
- mathertel/OneButton@2.6.1
+ OneButton
diff --git a/bin/build-esp32.sh b/bin/build-esp32.sh
index a0635e997..92836db23 100755
--- a/bin/build-esp32.sh
+++ b/bin/build-esp32.sh
@@ -11,7 +11,7 @@ rm -f $OUTDIR/firmware*
rm -r $OUTDIR/* || true
# Important to pull latest version of libs into all device flavors, otherwise some devices might be stale
-platformio pkg update -e $1
+platformio pkg install -e $1
echo "Building for $1 with $PLATFORMIO_BUILD_FLAGS"
rm -f .pio/build/$1/firmware.*
@@ -34,11 +34,12 @@ SRCBIN=.pio/build/$1/firmware.bin
cp $SRCBIN $OUTDIR/$basename-update.bin
echo "Building Filesystem for ESP32 targets"
-pio run --environment $1 -t buildfs
-cp .pio/build/$1/littlefs.bin $OUTDIR/littlefswebui-$1-$VERSION.bin
-# Remove webserver files from the filesystem and rebuild
-ls -l data/static # Diagnostic list of files
-rm -rf data/static
+# If you want to build the webui, uncomment the following lines
+# pio run --environment $1 -t buildfs
+# cp .pio/build/$1/littlefs.bin $OUTDIR/littlefswebui-$1-$VERSION.bin
+# # Remove webserver files from the filesystem and rebuild
+# ls -l data/static # Diagnostic list of files
+# rm -rf data/static
pio run --environment $1 -t buildfs
cp .pio/build/$1/littlefs.bin $OUTDIR/littlefs-$1-$VERSION.bin
cp bin/device-install.* $OUTDIR
diff --git a/bin/build-native.sh b/bin/build-native.sh
index 51379ad76..fff86e87e 100755
--- a/bin/build-native.sh
+++ b/bin/build-native.sh
@@ -25,7 +25,7 @@ mkdir -p $OUTDIR/
rm -r $OUTDIR/* || true
# Important to pull latest version of libs into all device flavors, otherwise some devices might be stale
-pio pkg update --environment "$PIO_ENV" || platformioFailed
+pio pkg install --environment "$PIO_ENV" || platformioFailed
pio run --environment "$PIO_ENV" || platformioFailed
cp ".pio/build/$PIO_ENV/program" "$OUTDIR/meshtasticd_linux_$(uname -m)"
cp bin/native-install.* $OUTDIR
diff --git a/bin/build-nrf52.sh b/bin/build-nrf52.sh
index 9d0b3dfdd..deca209d2 100755
--- a/bin/build-nrf52.sh
+++ b/bin/build-nrf52.sh
@@ -11,7 +11,7 @@ rm -f $OUTDIR/firmware*
rm -r $OUTDIR/* || true
# Important to pull latest version of libs into all device flavors, otherwise some devices might be stale
-platformio pkg update -e $1
+platformio pkg install -e $1
echo "Building for $1 with $PLATFORMIO_BUILD_FLAGS"
rm -f .pio/build/$1/firmware.*
diff --git a/bin/build-rpi2040.sh b/bin/build-rp2xx0.sh
similarity index 96%
rename from bin/build-rpi2040.sh
rename to bin/build-rp2xx0.sh
index dad6a7e67..cb4865914 100755
--- a/bin/build-rpi2040.sh
+++ b/bin/build-rp2xx0.sh
@@ -11,7 +11,7 @@ rm -f $OUTDIR/firmware*
rm -r $OUTDIR/* || true
# Important to pull latest version of libs into all device flavors, otherwise some devices might be stale
-platformio pkg update -e $1
+platformio pkg install -e $1
echo "Building for $1 with $PLATFORMIO_BUILD_FLAGS"
rm -f .pio/build/$1/firmware.*
diff --git a/bin/build-stm32.sh b/bin/build-stm32wl.sh
similarity index 95%
rename from bin/build-stm32.sh
rename to bin/build-stm32wl.sh
index 76c5a75fb..f62df4842 100755
--- a/bin/build-stm32.sh
+++ b/bin/build-stm32wl.sh
@@ -11,7 +11,7 @@ rm -f $OUTDIR/firmware*
rm -r $OUTDIR/* || true
# Important to pull latest version of libs into all device flavors, otherwise some devices might be stale
-platformio pkg update -e $1
+platformio pkg install -e $1
echo "Building for $1 with $PLATFORMIO_BUILD_FLAGS"
rm -f .pio/build/$1/firmware.*
diff --git a/bin/check-all.sh b/bin/check-all.sh
index d1b50a8aa..29d6b5532 100755
--- a/bin/check-all.sh
+++ b/bin/check-all.sh
@@ -23,4 +23,4 @@ for BOARD in $BOARDS; do
CHECK="${CHECK} -e ${BOARD}"
done
-pio check --flags "-DAPP_VERSION=${APP_VERSION} --suppressions-list=suppressions.txt" $CHECK --skip-packages --pattern="src/" --fail-on-defect=medium --fail-on-defect=high
+pio check --flags "-DAPP_VERSION=${APP_VERSION} --suppressions-list=suppressions.txt --inline-suppr" $CHECK --skip-packages --pattern="src/" --fail-on-defect=medium --fail-on-defect=high
diff --git a/bin/config-dist.yaml b/bin/config-dist.yaml
index 55e8648d9..b4cc81792 100644
--- a/bin/config-dist.yaml
+++ b/bin/config-dist.yaml
@@ -96,9 +96,9 @@ Lora:
### Some devices, like the pinedio, may require spidev0.1 as a workaround.
# spidev: spidev0.0
-### Define GPIO buttons here:
+### Deprecated location for User Button:
-GPIO:
+#GPIO:
# User: 6
### Define GPS
@@ -115,17 +115,6 @@ I2C:
Display:
-### Waveshare 1.44inch LCD HAT
-# Panel: ST7735S
-# CS: 8 #Chip Select
-# DC: 25 # Data/Command pin
-# Backlight: 24
-# Width: 128
-# Height: 128
-# Reset: 27
-# OffsetX: 0
-# OffsetY: 0
-
### Adafruit PiTFT 2.8 TFT+Touchscreen
# Panel: ILI9341
# CS: 8
@@ -180,6 +169,16 @@ Input:
# KeyboardDevice: /dev/input/by-id/usb-_Raspberry_Pi_Internal_Keyboard-event-kbd
+### Standard User Button Config
+# UserButton: 6
+
+### Trackball/Joystick input
+# TrackballUp: 6
+# TrackballDown: 19
+# TrackballLeft: 5
+# TrackballRight: 26
+# TrackballPress: 13
+
###
Logging:
@@ -200,6 +199,10 @@ HostMetrics:
# UserStringCommand: cat /sys/firmware/devicetree/base/serial-number # Command to execute, to send the results as the userString
+Config:
+# DisplayMode: TWOCOLOR # uncomment to force BaseUI
+# DisplayMode: COLOR # uncomment to force MUI
+
General:
MaxNodes: 200
MaxMessageQueue: 100
diff --git a/bin/config.d/display-waveshare-1-44.yaml b/bin/config.d/display-waveshare-1-44.yaml
new file mode 100644
index 000000000..d37f6cf6a
--- /dev/null
+++ b/bin/config.d/display-waveshare-1-44.yaml
@@ -0,0 +1,26 @@
+### Waveshare 1.44inch LCD HAT
+Display:
+ Panel: ST7735S
+ spidev: spidev0.0 # Specify either the spidev here, or the CS below
+# CS: 8 #Chip Select # Optional, as this is the default pin for spidev0.0
+ DC: 25 # Data/Command pin
+ Backlight: 24
+ Width: 128
+ Height: 128
+ Reset: 27
+ OffsetX: 2
+ OffsetY: 1
+
+
+# OffsetY: 31 # These two options are used to properly flip the screen 180 degrees
+# OffsetRotate: 3
+
+
+Input:
+ TrackballUp: 6
+ TrackballDown: 19
+ TrackballLeft: 5
+ TrackballRight: 26
+ TrackballPress: 13
+ TrackballDirection: FALLING
+# User: 21
diff --git a/bin/config.d/lora-MeshAdv-Mini-900M22S.yaml b/bin/config.d/lora-MeshAdv-Mini-900M22S.yaml
index 554116b57..b47b5c996 100644
--- a/bin/config.d/lora-MeshAdv-Mini-900M22S.yaml
+++ b/bin/config.d/lora-MeshAdv-Mini-900M22S.yaml
@@ -6,6 +6,6 @@ Lora:
IRQ: 16
Busy: 20
Reset: 24
- TXen: 13
+ RXen: 12
DIO2_AS_RF_SWITCH: true
DIO3_TCXO_VOLTAGE: true
diff --git a/bin/config.d/lora-RAK6421.yaml b/bin/config.d/lora-RAK6421.yaml
new file mode 100644
index 000000000..bbf38a474
--- /dev/null
+++ b/bin/config.d/lora-RAK6421.yaml
@@ -0,0 +1,21 @@
+Lora:
+
+ ### RAK13300in Slot 1
+ Module: sx1262
+ IRQ: 22 #IO6
+ Reset: 16 # IO4
+ Busy: 24 # IO5
+ # Ant_sw: 13 # IO3
+ DIO3_TCXO_VOLTAGE: true
+ DIO2_AS_RF_SWITCH: true
+ spidev: spidev0.0
+ # CS: 8
+
+
+ ### RAK13300in Slot 2 pins
+# IRQ: 18 #IO6
+# Reset: 24 # IO4
+# Busy: 19 # IO5
+# # Ant_sw: 23 # IO3
+# spidev: spidev0.1
+# # CS: 7
\ No newline at end of file
diff --git a/bin/config.d/lora-lyra-picocalc-wio-sx1262.yaml b/bin/config.d/lora-lyra-picocalc-wio-sx1262.yaml
new file mode 100644
index 000000000..2fd128ce8
--- /dev/null
+++ b/bin/config.d/lora-lyra-picocalc-wio-sx1262.yaml
@@ -0,0 +1,18 @@
+Lora:
+ Module: sx1262
+ DIO2_AS_RF_SWITCH: true
+ DIO3_TCXO_VOLTAGE: true
+ gpiochip: 0
+ MOSI: 12
+ MISO: 13
+ IRQ: 1
+ Busy: 23
+ Reset: 22
+ RXen: 0
+ gpiochip: 1
+ CS: 9
+ SCK: 11
+# TXen: bridge to DIO2 on E22 module
+ SX126X_MAX_POWER: 22
+ spidev: spidev1.0
+ spiSpeed: 2000000
diff --git a/bin/config.d/lora-ws-raspberry-pico-to-orangepi-03.yaml b/bin/config.d/lora-ws-raspberry-pico-to-orangepi-03.yaml
new file mode 100644
index 000000000..37d7e27d2
--- /dev/null
+++ b/bin/config.d/lora-ws-raspberry-pico-to-orangepi-03.yaml
@@ -0,0 +1,52 @@
+# https://www.waveshare.com/pico-lora-sx1262-868m.htm
+# http://www.orangepi.org/html/hardWare/computerAndMicrocontrollers/details/Orange-Pi-Zero-3.html
+#
+# See Orange Pi Zero3 manual, chapter 3.16, page 124 for 26-pin header pinout
+#
+# Pin Connection
+# Waveshare Orange Pi Zero3
+# 36 3.3V 17
+# 15 MOSI 19
+# 16 MISO 21
+# 14 CLK 23
+# 38 GND 25
+# 4 BUSY 18
+# 20 RESET 22
+# 5 CS 24
+# 26 DIO1/IRQ 26
+
+Lora:
+ Module: sx1262 # Waveshare Raspberry Pico Lora module
+ DIO2_AS_RF_SWITCH: true
+ DIO3_TCXO_VOLTAGE: true
+ # Specify either the spidev1_1 or the CS below, not both!
+ # On DietPi Linux, when using the user overlay dietpi-spi1_1.dtbo, CS will be configured with spidev1.1
+ spidev: spidev1.1 # See Orange Pi Zero3 manual, chapter 3.18.3, page 130
+# CS: # CS PIN_24 -> chip 1, line 233
+# pin: 24
+# gpiochip: 1
+# line: 233
+ SCK: # SCK PIN_23 -> chip 1, line 230
+ pin: 23
+ gpiochip: 1
+ line: 230
+ Busy: # BUSY PIN_18 -> chip 1, line 78
+ pin: 18
+ gpiochip: 1
+ line: 78
+ MOSI: # MOSI PIN_19 -> chip 1, line 231
+ pin: 19
+ gpiochip: 1
+ line: 231
+ MISO: # MISO PIN_21 -> chip 1, line 232
+ pin: 21
+ gpiochip: 1
+ line: 232
+ Reset: # NRST PIN_22 -> chip 1, line 71
+ pin: 22
+ gpiochip: 1
+ line: 71
+ IRQ: # DIO1 PIN_26 -> chip 1, line 74
+ pin: 26
+ gpiochip: 1
+ line: 74
diff --git a/bin/device-install.bat b/bin/device-install.bat
index 816d2fbba..12bfd4f6e 100755
--- a/bin/device-install.bat
+++ b/bin/device-install.bat
@@ -5,7 +5,6 @@ TITLE Meshtastic device-install
SET "SCRIPT_NAME=%~nx0"
SET "DEBUG=0"
SET "PYTHON="
-SET "WEB_APP=0"
SET "TFT_BUILD=0"
SET "BIGDB8=0"
SET "BIGDB16=0"
@@ -25,7 +24,7 @@ GOTO getopts
:help
ECHO Flash image file to device, but first erasing and writing system information.
ECHO.
-ECHO Usage: %SCRIPT_NAME% -f filename [-p PORT] [-P python] (--web) [--1200bps-reset]
+ECHO Usage: %SCRIPT_NAME% -f filename [-p PORT] [-P python] [--1200bps-reset]
ECHO.
ECHO Options:
ECHO -f filename The firmware .bin file to flash. Custom to your device type and region. (required)
@@ -35,13 +34,12 @@ ECHO If not set, ESPTOOL iterates all ports (Dangerous).
ECHO -P python Specify alternate python interpreter to use to invoke esptool. (default: python)
ECHO If supplied the script will use python.
ECHO If not supplied the script will try to find esptool in Path.
-ECHO --web Enable WebUI. (default: false)
ECHO --1200bps-reset Attempt to place the device in correct mode. (1200bps Reset)
ECHO Some hardware requires this twice.
ECHO.
ECHO Example: %SCRIPT_NAME% -p COM17 --1200bps-reset
ECHO Example: %SCRIPT_NAME% -f firmware-t-deck-tft-2.6.0.0b106d4.bin -p COM11
-ECHO Example: %SCRIPT_NAME% -f firmware-unphone-2.6.0.0b106d4.bin -p COM11 --web
+ECHO Example: %SCRIPT_NAME% -f firmware-unphone-2.6.0.0b106d4.bin -p COM11
GOTO eof
:version
@@ -61,7 +59,6 @@ IF /I "%~1"=="-f" SET "FILENAME=%~2" & SHIFT
IF "%~1"=="-p" SET "ESPTOOL_PORT=%~2" & SHIFT
IF /I "%~1"=="--port" SET "ESPTOOL_PORT=%~2" & SHIFT
IF "%~1"=="-P" SET "PYTHON=%~2" & SHIFT
-IF /I "%~1"=="--web" SET "WEB_APP=1"
IF /I "%~1"=="--1200bps-reset" SET "BPS_RESET=1"
SHIFT
GOTO getopts
@@ -153,9 +150,6 @@ IF %BPS_RESET% EQU 1 (
@REM https://github.com/meshtastic/web-flasher/blob/main/types/resources.ts#L3
IF NOT "!FILENAME:-tft-=!"=="!FILENAME!" (
CALL :LOG_MESSAGE DEBUG "We are working with a *-tft-* file. !FILENAME!"
- IF %WEB_APP% EQU 1 (
- CALL :LOG_MESSAGE ERROR "Cannot enable WebUI (--web) and MUI." & GOTO eof
- )
SET "TFT_BUILD=1"
) ELSE (
CALL :LOG_MESSAGE DEBUG "We are NOT working with a *-tft-* file. !FILENAME!"
@@ -209,13 +203,8 @@ SET "OTA_FILENAME=bleota.bin"
:end_loop_c3
CALL :LOG_MESSAGE DEBUG "Set OTA_FILENAME to: !OTA_FILENAME!"
-@REM Check if (--web) is enabled and prefix BASENAME with "littlefswebui-" else "littlefs-".
-IF %WEB_APP% EQU 1 (
- CALL :LOG_MESSAGE INFO "WebUI selected."
- SET "SPIFFS_FILENAME=littlefswebui-%BASENAME%"
-) ELSE (
- SET "SPIFFS_FILENAME=littlefs-%BASENAME%"
-)
+@REM Set SPIFFS filename with "littlefs-" prefix.
+SET "SPIFFS_FILENAME=littlefs-%BASENAME%"
CALL :LOG_MESSAGE DEBUG "Set SPIFFS_FILENAME to: !SPIFFS_FILENAME!"
@REM Default offsets.
diff --git a/bin/device-install.sh b/bin/device-install.sh
index 76765bb5f..4674113b6 100755
--- a/bin/device-install.sh
+++ b/bin/device-install.sh
@@ -1,7 +1,6 @@
#!/bin/bash
PYTHON=${PYTHON:-$(which python3 python | head -n 1)}
-WEB_APP=false
BPS_RESET=false
TFT_BUILD=false
MCU=""
@@ -33,93 +32,92 @@ BIGDB_16MB=(
"ESP32-S3-Pico"
"m5stack-cores3"
"station-g2"
- "t-eth-elite"
- "t-watch-s3"
+ "t-eth-elite"
+ "t-watch-s3"
+ "elecrow-adv-35-tft"
+ "elecrow-adv-24-28-tft"
+ "elecrow-adv1-43-50-70-tft"
)
S3_VARIANTS=(
- "s3"
- "-v3"
- "t-deck"
- "wireless-paper"
- "wireless-tracker"
- "station-g2"
- "unphone"
- "t-eth-elite"
- "mesh-tab"
- "dreamcatcher"
- "ESP32-S3-Pico"
- "seeed-sensecap-indicator"
- "heltec_capsule_sensor_v3"
- "vision-master"
- "icarus"
- "tracksenger"
- "elecrow-adv"
+ "s3"
+ "-v3"
+ "t-deck"
+ "wireless-paper"
+ "wireless-tracker"
+ "station-g2"
+ "unphone"
+ "t-eth-elite"
+ "mesh-tab"
+ "dreamcatcher"
+ "ESP32-S3-Pico"
+ "seeed-sensecap-indicator"
+ "heltec_capsule_sensor_v3"
+ "vision-master"
+ "icarus"
+ "tracksenger"
+ "elecrow-adv"
)
# Determine the correct esptool command to use
if "$PYTHON" -m esptool version >/dev/null 2>&1; then
- ESPTOOL_CMD="$PYTHON -m esptool"
+ ESPTOOL_CMD="$PYTHON -m esptool"
elif command -v esptool >/dev/null 2>&1; then
- ESPTOOL_CMD="esptool"
+ ESPTOOL_CMD="esptool"
elif command -v esptool.py >/dev/null 2>&1; then
- ESPTOOL_CMD="esptool.py"
+ ESPTOOL_CMD="esptool.py"
else
- echo "Error: esptool not found"
- exit 1
+ echo "Error: esptool not found"
+ exit 1
fi
set -e
# Usage info
show_help() {
- cat <&2
- exit 1
- ;;
- esac
- shift # Move to the next argument
+ case "$1" in
+ -h | --help)
+ show_help
+ exit 0
+ ;;
+ -p)
+ ESPTOOL_CMD="$ESPTOOL_CMD --port $2"
+ shift
+ ;;
+ -P)
+ PYTHON="$2"
+ shift
+ ;;
+ -f)
+ FILENAME="$2"
+ shift
+ ;;
+ --1200bps-reset)
+ BPS_RESET=true
+ ;;
+ --) # Stop parsing options
+ shift
+ break
+ ;;
+ *)
+ echo "Unknown argument: $1" >&2
+ exit 1
+ ;;
+ esac
+ shift # Move to the next argument
done
if [[ $BPS_RESET == true ]]; then
@@ -127,100 +125,92 @@ if [[ $BPS_RESET == true ]]; then
exit 0
fi
-[ -z "$FILENAME" -a -n "$1" ] && {
- FILENAME=$1
- shift
+[ -z "$FILENAME" ] && [ -n "$1" ] && {
+ FILENAME="$1"
+ shift
}
-if [[ $FILENAME != firmware-* ]]; then
+if [[ "$FILENAME" != firmware-* ]]; then
echo "Filename must be a firmware-* file."
exit 1
fi
-# Check if FILENAME contains "-tft-" and prevent web/mui comingling.
-if [[ ${FILENAME//-tft-/} != "$FILENAME" ]]; then
- TFT_BUILD=true
- if [[ $WEB_APP == true ]] && [[ $TFT_BUILD == true ]]; then
- echo "Cannot enable WebUI (--web) and MUI."
- exit 1
- fi
+# Check if FILENAME contains "-tft-" and set target partitionScheme accordingly.
+if [[ "${FILENAME//-tft-/}" != "$FILENAME" ]]; then
+ TFT_BUILD=true
fi
# Extract BASENAME from %FILENAME% for later use.
BASENAME="${FILENAME/firmware-/}"
if [ -f "${FILENAME}" ] && [ -n "${FILENAME##*"update"*}" ]; then
- # Default littlefs* offset (--web).
- OFFSET=0x300000
+ # Default littlefs* offset.
+ OFFSET=0x300000
- # Default OTA Offset
- OTA_OFFSET=0x260000
+ # Default OTA Offset
+ OTA_OFFSET=0x260000
- # littlefs* offset for BigDB 8mb and OTA OFFSET.
- for variant in "${BIGDB_8MB[@]}"; do
- if [ -z "${FILENAME##*"$variant"*}" ]; then
- OFFSET=0x670000
- OTA_OFFSET=0x340000
- fi
- done
+ # littlefs* offset for BigDB 8mb and OTA OFFSET.
+ for variant in "${BIGDB_8MB[@]}"; do
+ if [ -z "${FILENAME##*"$variant"*}" ]; then
+ OFFSET=0x670000
+ OTA_OFFSET=0x340000
+ fi
+ done
- # littlefs* offset for BigDB 16mb and OTA OFFSET.
- for variant in "${BIGDB_16MB[@]}"; do
- if [ -z "${FILENAME##*"$variant"*}" ]; then
- OFFSET=0xc90000
- OTA_OFFSET=0x650000
- fi
- done
+ # littlefs* offset for BigDB 16mb and OTA OFFSET.
+ for variant in "${BIGDB_16MB[@]}"; do
+ if [ -z "${FILENAME##*"$variant"*}" ]; then
+ OFFSET=0xc90000
+ OTA_OFFSET=0x650000
+ fi
+ done
- # Account for S3 board's different OTA partition
- # FIXME: Use PlatformIO info to determine MCU type, this is unmaintainable
- for variant in "${S3_VARIANTS[@]}"; do
- if [ -z "${FILENAME##*"$variant"*}" ]; then
- MCU="esp32s3"
- fi
- done
+ # Account for S3 board's different OTA partition
+ # FIXME: Use PlatformIO info to determine MCU type, this is unmaintainable
+ for variant in "${S3_VARIANTS[@]}"; do
+ if [ -z "${FILENAME##*"$variant"*}" ]; then
+ MCU="esp32s3"
+ fi
+ done
- if [ "$MCU" != "esp32s3" ]; then
- if [ -n "${FILENAME##*"esp32c3"*}" ]; then
- OTAFILE=bleota.bin
- else
- OTAFILE=bleota-c3.bin
- fi
- else
- OTAFILE=bleota-s3.bin
- fi
+ if [ "$MCU" != "esp32s3" ]; then
+ if [ -n "${FILENAME##*"esp32c3"*}" ]; then
+ OTAFILE=bleota.bin
+ else
+ OTAFILE=bleota-c3.bin
+ fi
+ else
+ OTAFILE=bleota-s3.bin
+ fi
- # Check if WEB_APP (--web) is enabled and add "littlefswebui-" to BASENAME else "littlefs-".
- if [ "$WEB_APP" = true ]; then
- SPIFFSFILE=littlefswebui-${BASENAME}
- else
- SPIFFSFILE=littlefs-${BASENAME}
- fi
+ # Set SPIFFS filename with "littlefs-" prefix.
+ SPIFFSFILE=littlefs-${BASENAME}
- if [[ ! -f $FILENAME ]]; then
- echo "Error: file ${FILENAME} wasn't found. Terminating."
- exit 1
- fi
- if [[ ! -f $OTAFILE ]]; then
- echo "Error: file ${OTAFILE} wasn't found. Terminating."
- exit 1
- fi
- if [[ ! -f $SPIFFSFILE ]]; then
- echo "Error: file ${SPIFFSFILE} wasn't found. Terminating."
- exit 1
- fi
+ if [[ ! -f "$FILENAME" ]]; then
+ echo "Error: file ${FILENAME} wasn't found. Terminating."
+ exit 1
+ fi
+ if [[ ! -f "$OTAFILE" ]]; then
+ echo "Error: file ${OTAFILE} wasn't found. Terminating."
+ exit 1
+ fi
+ if [[ ! -f "$SPIFFSFILE" ]]; then
+ echo "Error: file ${SPIFFSFILE} wasn't found. Terminating."
+ exit 1
+ fi
- echo "Trying to flash ${FILENAME}, but first erasing and writing system information"
- $ESPTOOL_CMD erase_flash
- $ESPTOOL_CMD write_flash 0x00 "${FILENAME}"
- echo "Trying to flash ${OTAFILE} at offset ${OTA_OFFSET}"
- $ESPTOOL_CMD write_flash $OTA_OFFSET "${OTAFILE}"
- echo "Trying to flash ${SPIFFSFILE}, at offset ${OFFSET}"
- $ESPTOOL_CMD write_flash $OFFSET "${SPIFFSFILE}"
+ echo "Trying to flash ${FILENAME}, but first erasing and writing system information"
+ $ESPTOOL_CMD erase_flash
+ $ESPTOOL_CMD write_flash 0x00 "${FILENAME}"
+ echo "Trying to flash ${OTAFILE} at offset ${OTA_OFFSET}"
+ $ESPTOOL_CMD write_flash $OTA_OFFSET "${OTAFILE}"
+ echo "Trying to flash ${SPIFFSFILE}, at offset ${OFFSET}"
+ $ESPTOOL_CMD write_flash $OFFSET "${SPIFFSFILE}"
else
- show_help
- echo "Invalid file: ${FILENAME}"
+ show_help
+ echo "Invalid file: ${FILENAME}"
fi
exit 0
diff --git a/bin/device-update.sh b/bin/device-update.sh
index c32b953e6..ce0b5e434 100755
--- a/bin/device-update.sh
+++ b/bin/device-update.sh
@@ -18,8 +18,8 @@ fi
# Usage info
show_help() {
cat << EOF
-Usage: $(basename $0) [-h] [-p ESPTOOL_PORT] [-P PYTHON] [-f FILENAME|FILENAME] [--change-mode]
-Flash image file to device, leave existing system intact.
+Usage: $(basename "$0") [-h] [-p ESPTOOL_PORT] [-P PYTHON] [-f FILENAME|FILENAME] [--change-mode]
+Flash image file to device, leave existing system intact."
-h Display this help and exit
-p ESPTOOL_PORT Set the environment variable for ESPTOOL_PORT. If not set, ESPTOOL iterates all ports (Dangerous).
@@ -30,6 +30,17 @@ Flash image file to device, leave existing system intact.
EOF
}
+# Check for --change-mode and remove it from arguments
+NEW_ARGS=()
+for arg in "$@"; do
+ if [ "$arg" = "--change-mode" ]; then
+ CHANGE_MODE=true
+ else
+ NEW_ARGS+=("$arg")
+ fi
+done
+
+set -- "${NEW_ARGS[@]}"
while getopts ":hp:P:f:" opt; do
case "${opt}" in
@@ -38,16 +49,13 @@ while getopts ":hp:P:f:" opt; do
exit 0
;;
p) ESPTOOL_CMD="$ESPTOOL_CMD --port ${OPTARG}"
- ;;
+ ;;
P) PYTHON=${OPTARG}
;;
f) FILENAME=${OPTARG}
;;
- --change-mode)
- CHANGE_MODE=true
- ;;
*)
- echo "Invalid flag."
+ echo "Invalid flag."
show_help >&2
exit 1
;;
@@ -55,22 +63,22 @@ while getopts ":hp:P:f:" opt; do
done
shift "$((OPTIND-1))"
-if [[ $CHANGE_MODE == true ]]; then
+if [ "$CHANGE_MODE" = true ]; then
$ESPTOOL_CMD --baud 1200 --after no_reset read_flash_status
exit 0
fi
-[ -z "$FILENAME" -a -n "$1" ] && {
- FILENAME=$1
+[ -z "$FILENAME" ] && [ -n "$1" ] && {
+ FILENAME="$1"
shift
}
if [ -f "${FILENAME}" ] && [ -z "${FILENAME##*"update"*}" ]; then
- printf "Trying to flash update ${FILENAME}"
- $ESPTOOL_CMD --baud 115200 write_flash 0x10000 ${FILENAME}
+ echo "Trying to flash update ${FILENAME}"
+ $ESPTOOL_CMD --baud 115200 write_flash 0x10000 "${FILENAME}"
else
- show_help
- echo "Invalid file: ${FILENAME}"
+ show_help
+ echo "Invalid file: ${FILENAME}"
fi
exit 0
diff --git a/bin/generate_ci_matrix.py b/bin/generate_ci_matrix.py
index 7513ccff5..0ce6b0f6b 100755
--- a/bin/generate_ci_matrix.py
+++ b/bin/generate_ci_matrix.py
@@ -27,7 +27,7 @@ for subdir, dirs, files in os.walk(rootdir):
if c.startswith("env:"):
section = config[c].name[4:]
if "extends" in config[config[c].name]:
- if config[config[c].name]["extends"] == options[0] + "_base":
+ if options[0] + "_base" in config[config[c].name]["extends"]:
if "board_level" in config[config[c].name]:
if (
config[config[c].name]["board_level"] == "extra"
diff --git a/bin/org.meshtastic.meshtasticd.metainfo.xml b/bin/org.meshtastic.meshtasticd.metainfo.xml
index 40f86fb0b..291fe7a7c 100644
--- a/bin/org.meshtastic.meshtasticd.metainfo.xml
+++ b/bin/org.meshtastic.meshtasticd.metainfo.xml
@@ -87,6 +87,24 @@
+
+ https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.3
+
+
+ https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.2
+
+
+ https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.1
+
+
+ https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.0
+
+
+ https://github.com/meshtastic/firmware/releases?q=tag%3Av2.6.13
+
+
+ https://github.com/meshtastic/firmware/releases?q=tag%3Av2.6.12
+
https://github.com/meshtastic/firmware/releases?q=tag%3Av2.6.11
diff --git a/bin/platformio-custom.py b/bin/platformio-custom.py
index 600f9447f..be2a9ab71 100644
--- a/bin/platformio-custom.py
+++ b/bin/platformio-custom.py
@@ -131,3 +131,33 @@ for lb in env.GetLibBuilders():
if lb.name == "meshtastic-device-ui":
lb.env.Append(CPPDEFINES=[("APP_VERSION", verObj["long"])])
break
+
+# Get the display resolution from macros
+def get_display_resolution(build_flags):
+ # Check "DISPLAY_SIZE" to determine the screen resolution
+ for flag in build_flags:
+ if isinstance(flag, tuple) and flag[0] == "DISPLAY_SIZE":
+ screen_width, screen_height = map(int, flag[1].split("x"))
+ return screen_width, screen_height
+ print("No screen resolution defined in build_flags. Please define DISPLAY_SIZE.")
+ exit(1)
+
+def load_boot_logo(source, target, env):
+ build_flags = env.get("CPPDEFINES", [])
+ logo_w, logo_h = get_display_resolution(build_flags)
+ print(f"TFT build with {logo_w}x{logo_h} resolution detected")
+
+ # Load the boot logo from `branding/logo_x.png` if it exists
+ source_path = join(env["PROJECT_DIR"], "branding", f"logo_{logo_w}x{logo_h}.png")
+ dest_dir = join(env["PROJECT_DIR"], "data", "boot")
+ dest_path = join(dest_dir, "logo.png")
+ if env.File(source_path).exists():
+ print(f"Loading boot logo from {source_path}")
+ # Prepare the destination
+ env.Execute(f"mkdir -p {dest_dir} && rm -f {dest_path}")
+ # Copy the logo to the `data/boot` directory
+ env.Execute(f"cp {source_path} {dest_path}")
+
+# Load the boot logo on TFT builds
+if ("HAS_TFT", 1) in env.get("CPPDEFINES", []):
+ env.AddPreAction('$BUILD_DIR/littlefs.bin', load_boot_logo)
diff --git a/boards/ThinkNode-M1.json b/boards/ThinkNode-M1.json
index e55da3ec7..2d6dbc352 100644
--- a/boards/ThinkNode-M1.json
+++ b/boards/ThinkNode-M1.json
@@ -48,6 +48,6 @@
"require_upload_port": true,
"wait_for_upload_port": true
},
- "url": "FIXME",
+ "url": "https://www.elecrow.com/thinknode-m1-meshtastic-lora-signal-transceiver-powered-by-nrf52840-with-154-screen-support-gps.html",
"vendor": "ELECROW"
}
diff --git a/boards/gat562_mesh_trial_tracker.json b/boards/gat562_mesh_trial_tracker.json
new file mode 100644
index 000000000..a3fb8a264
--- /dev/null
+++ b/boards/gat562_mesh_trial_tracker.json
@@ -0,0 +1,52 @@
+{
+ "build": {
+ "arduino": {
+ "ldscript": "nrf52840_s140_v6.ld"
+ },
+ "core": "nRF5",
+ "cpu": "cortex-m4",
+ "extra_flags": "-DARDUINO_NRF52840_FEATHER -DNRF52840_XXAA",
+ "f_cpu": "64000000L",
+ "hwids": [
+ ["0x239A", "0x8029"],
+ ["0x239A", "0x0029"],
+ ["0x239A", "0x002A"],
+ ["0x239A", "0x802A"]
+ ],
+ "usb_product": "GAT562 Mesh Trial Tracker",
+ "mcu": "nrf52840",
+ "variant": "gat562_mesh_trial_tracker",
+ "bsp": {
+ "name": "adafruit"
+ },
+ "softdevice": {
+ "sd_flags": "-DS140",
+ "sd_name": "s140",
+ "sd_version": "6.1.1",
+ "sd_fwid": "0x00B6"
+ },
+ "bootloader": {
+ "settings_addr": "0xFF000"
+ }
+ },
+ "connectivity": ["bluetooth"],
+ "debug": {
+ "jlink_device": "nRF52840_xxAA",
+ "svd_path": "nrf52840.svd",
+ "openocd_target": "nrf52840-mdk-rs"
+ },
+ "frameworks": ["arduino", "freertos"],
+ "name": "GAT562 Mesh Trial Tracker",
+ "upload": {
+ "maximum_ram_size": 248832,
+ "maximum_size": 815104,
+ "speed": 115200,
+ "protocol": "nrfutil",
+ "protocols": ["jlink", "nrfjprog", "nrfutil", "stlink"],
+ "use_1200bps_touch": true,
+ "require_upload_port": true,
+ "wait_for_upload_port": true
+ },
+ "url": "http://www.gat-iot.com/",
+ "vendor": "GAT-IOT"
+}
diff --git a/boards/heltec_mesh_node_t114.json b/boards/heltec_mesh_node_t114.json
index 2bd306eb9..eda0ac3df 100644
--- a/boards/heltec_mesh_node_t114.json
+++ b/boards/heltec_mesh_node_t114.json
@@ -10,7 +10,8 @@
"hwids": [
["0x239A", "0x4405"],
["0x239A", "0x0029"],
- ["0x239A", "0x002A"]
+ ["0x239A", "0x002A"],
+ ["0x2886", "0x1667"]
],
"usb_product": "HT-n5262",
"mcu": "nrf52840",
@@ -48,6 +49,6 @@
"require_upload_port": true,
"wait_for_upload_port": true
},
- "url": "FIXME",
+ "url": "https://heltec.org/project/mesh-node-t114/",
"vendor": "Heltec"
}
diff --git a/boards/seeed_wio_tracker_L1.json b/boards/seeed_wio_tracker_L1.json
index 7c7bc62fa..e2bb93573 100644
--- a/boards/seeed_wio_tracker_L1.json
+++ b/boards/seeed_wio_tracker_L1.json
@@ -7,7 +7,10 @@
"cpu": "cortex-m4",
"extra_flags": "-DARDUINO_MDBT50Q_RX -DNRF52840_XXAA",
"f_cpu": "64000000L",
- "hwids": [["0x2886", "0x1668"]],
+ "hwids": [
+ ["0x2886", "0x1668"],
+ ["0x2886", "0x1667"]
+ ],
"usb_product": "TRACKER L1",
"mcu": "nrf52840",
"variant": "seeed_wio_tracker_L1",
diff --git a/boards/seeed_xiao_nrf52840_kit.json b/boards/seeed_xiao_nrf52840_kit.json
index 4c5fdbeda..676733874 100644
--- a/boards/seeed_xiao_nrf52840_kit.json
+++ b/boards/seeed_xiao_nrf52840_kit.json
@@ -7,9 +7,7 @@
"cpu": "cortex-m4",
"extra_flags": "-DARDUINO_MDBT50Q_RX -DNRF52840_XXAA",
"f_cpu": "64000000L",
- "hwids": [
- ["0x2886", "0x0166"]
- ],
+ "hwids": [["0x2886", "0x0166"]],
"usb_product": "XIAO-BOOT",
"mcu": "nrf52840",
"variant": "seeed_xiao_nrf52840_kit",
diff --git a/boards/tracker-t1000-e.json b/boards/tracker-t1000-e.json
index 2be716e22..9e8870041 100644
--- a/boards/tracker-t1000-e.json
+++ b/boards/tracker-t1000-e.json
@@ -11,7 +11,8 @@
["0x239A", "0x8029"],
["0x239A", "0x0029"],
["0x239A", "0x002A"],
- ["0x239A", "0x802A"]
+ ["0x239A", "0x802A"],
+ ["0x2886", "0x0057"]
],
"usb_product": "T1000-E-BOOT",
"mcu": "nrf52840",
diff --git a/boards/wiscore_rak3312.json b/boards/wiscore_rak3312.json
new file mode 100644
index 000000000..192e1c03c
--- /dev/null
+++ b/boards/wiscore_rak3312.json
@@ -0,0 +1,41 @@
+{
+ "build": {
+ "arduino": {
+ "ldscript": "esp32s3_out.ld",
+ "memory_type": "qio_opi",
+ "partitions": "default_16MB.csv"
+ },
+ "core": "esp32",
+ "extra_flags": [
+ "-DRAK3312",
+ "-DARDUINO_USB_CDC_ON_BOOT=1",
+ "-DARDUINO_USB_MODE=1",
+ "-DARDUINO_RUNNING_CORE=1",
+ "-DARDUINO_EVENT_RUNNING_CORE=1",
+ "-DBOARD_HAS_PSRAM"
+ ],
+ "f_cpu": "240000000L",
+ "f_flash": "80000000L",
+ "flash_mode": "dio",
+ "hwids": [["0x303A", "0x1001"]],
+ "mcu": "esp32s3",
+ "variant": "rak3312"
+ },
+ "connectivity": ["wifi", "bluetooth"],
+ "debug": {
+ "openocd_target": "esp32s3.cfg"
+ },
+ "frameworks": ["arduino", "espidf"],
+ "name": "WisCore RAK3312 Board",
+ "upload": {
+ "flash_size": "16MB",
+ "maximum_ram_size": 327680,
+ "maximum_size": 16777216,
+ "use_1200bps_touch": true,
+ "wait_for_upload_port": true,
+ "require_upload_port": true,
+ "speed": 921600
+ },
+ "url": "https://www.rakwireless.com/en-us",
+ "vendor": "rakwireless"
+}
diff --git a/branding/README.md b/branding/README.md
new file mode 100644
index 000000000..3a558bf20
--- /dev/null
+++ b/branding/README.md
@@ -0,0 +1,17 @@
+# Meshtastic Branding / Whitelabeling
+
+This directory is consumed during the creation of **event** firmware.
+
+`bin/platformio-custom.py` determines the display resolution, and locates the corresponding `logo_x.png`.
+
+Ex:
+
+- `logo_800x480.png`
+- `logo_480x480.png`
+- `logo_480x320.png`
+- `logo_320x480.png`
+- `logo_320x240.png`
+
+This file is copied to `data/boot/logo.png` before filesytem image compilation.
+
+For additional examples see the [`event/defcon33` branch](https://github.com/meshtastic/firmware/tree/event/defcon33).
diff --git a/debian/changelog b/debian/changelog
index 4b67eecd4..b5009028a 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,4 +1,4 @@
-meshtasticd (2.6.11.0) UNRELEASED; urgency=medium
+meshtasticd (2.7.3.0) UNRELEASED; urgency=medium
[ Austin Lane ]
* Initial packaging
@@ -19,4 +19,16 @@ meshtasticd (2.6.11.0) UNRELEASED; urgency=medium
[ ]
* GitHub Actions Automatic version bump
- -- Mon, 02 Jun 2025 20:00:55 +0000
+ [ ]
+ * GitHub Actions Automatic version bump
+
+ [ ]
+ * GitHub Actions Automatic version bump
+
+ [ ]
+ * GitHub Actions Automatic version bump
+
+ [ Ubuntu ]
+ * GitHub Actions Automatic version bump
+
+ -- Ubuntu Thu, 10 Jul 2025 16:29:27 +0000
diff --git a/platformio.ini b/platformio.ini
index 4ed55ece0..2930d1a8d 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -49,10 +49,11 @@ build_flags = -Wno-missing-field-initializers
-DMESHTASTIC_EXCLUDE_DROPZONE=1
-DMESHTASTIC_EXCLUDE_REMOTEHARDWARE=1
-DMESHTASTIC_EXCLUDE_HEALTH_TELEMETRY=1
- -DMESHTASTIC_EXCLUDE_POWERSTRESS=1 ; exclude power stress test module from main firmware
+ -DMESHTASTIC_EXCLUDE_POWERSTRESS=1 ; exclude power stress test module from main firmware
-DMESHTASTIC_EXCLUDE_GENERIC_THREAD_MODULE=1
- #-DBUILD_EPOCH=$UNIX_TIME
- #-D OLED_PL=1
+ -D MAX_THREADS=40 ; As we've split modules, we have more threads to manage
+ #-DBUILD_EPOCH=$UNIX_TIME
+ #-D OLED_PL=1
monitor_speed = 115200
monitor_filters = direct
@@ -104,18 +105,18 @@ lib_deps =
[radiolib_base]
lib_deps =
# renovate: datasource=custom.pio depName=RadioLib packageName=jgromes/library/RadioLib
- jgromes/RadioLib@7.1.2
+ jgromes/RadioLib@7.2.1
[device-ui_base]
lib_deps =
# renovate: datasource=git-refs depName=meshtastic/device-ui packageName=https://github.com/meshtastic/device-ui gitBranch=master
- https://github.com/meshtastic/device-ui/archive/649e0953508ee4aabf1171519ee2eb69fb125647.zip
+ https://github.com/meshtastic/device-ui/archive/86a09a7360f92d10053fbbf8d74f67f85b0ceb09.zip
; Common libs for environmental measurements in telemetry module
[environmental_base]
lib_deps =
# renovate: datasource=custom.pio depName=Adafruit BusIO packageName=adafruit/library/Adafruit BusIO
- adafruit/Adafruit BusIO@1.17.1
+ adafruit/Adafruit BusIO@1.17.2
# renovate: datasource=custom.pio depName=Adafruit Unified Sensor packageName=adafruit/library/Adafruit Unified Sensor
adafruit/Adafruit Unified Sensor@1.1.15
# renovate: datasource=custom.pio depName=Adafruit BMP280 packageName=adafruit/library/Adafruit BMP280 Library
@@ -129,7 +130,7 @@ lib_deps =
# renovate: datasource=custom.pio depName=Adafruit MCP9808 packageName=adafruit/library/Adafruit MCP9808 Library
adafruit/Adafruit MCP9808 Library@2.0.2
# renovate: datasource=custom.pio depName=Adafruit INA260 packageName=adafruit/library/Adafruit INA260 Library
- adafruit/Adafruit INA260 Library@1.5.2
+ adafruit/Adafruit INA260 Library@1.5.3
# renovate: datasource=custom.pio depName=Adafruit INA219 packageName=adafruit/library/Adafruit INA219
adafruit/Adafruit INA219@1.2.3
# renovate: datasource=custom.pio depName=Adafruit PM25 AQI Sensor packageName=adafruit/library/Adafruit PM25 AQI Sensor
@@ -166,8 +167,10 @@ lib_deps =
adafruit/Adafruit LTR390 Library@1.1.2
# renovate: datasource=custom.pio depName=Adafruit PCT2075 packageName=adafruit/library/Adafruit PCT2075
adafruit/Adafruit PCT2075@1.0.5
-
- ; (not included in native / portduino)
+ # renovate: datasource=custom.pio depName=DFRobot_BMM150 packageName=dfrobot/library/DFRobot_BMM150
+ dfrobot/DFRobot_BMM150@1.0.0
+
+; (not included in native / portduino)
[environmental_extra]
lib_deps =
# renovate: datasource=custom.pio depName=Adafruit BMP3XX packageName=adafruit/library/Adafruit BMP3XX Library
@@ -193,4 +196,8 @@ lib_deps =
# renovate: datasource=custom.pio depName=Bosch BME68x packageName=boschsensortec/library/BME68x Sensor Library
boschsensortec/BME68x Sensor Library@1.3.40408
# renovate: datasource=git-refs depName=meshtastic-DFRobot_LarkWeatherStation packageName=https://github.com/meshtastic/DFRobot_LarkWeatherStation gitBranch=master
- https://github.com/meshtastic/DFRobot_LarkWeatherStation/archive/4de3a9cadef0f6a5220a8a906cf9775b02b0040d.zip
\ No newline at end of file
+ https://github.com/meshtastic/DFRobot_LarkWeatherStation/archive/4de3a9cadef0f6a5220a8a906cf9775b02b0040d.zip
+ # renovate: datasource=custom.pio depName=Sensirion Core packageName=sensirion/library/Sensirion Core
+ sensirion/Sensirion Core@0.7.1
+ # renovate: datasource=custom.pio depName=Sensirion I2C SCD4x packageName=sensirion/library/Sensirion I2C SCD4x
+ sensirion/Sensirion I2C SCD4x@1.1.0
diff --git a/protobufs b/protobufs
index 24c7a3d28..584f0a3a3 160000
--- a/protobufs
+++ b/protobufs
@@ -1 +1 @@
-Subproject commit 24c7a3d287a4bd269ce191827e5dabd8ce8f57a7
+Subproject commit 584f0a3a359103acf0bfce506c1b1fc32c639841
diff --git a/src/AmbientLightingThread.h b/src/AmbientLightingThread.h
index bff8846d6..e4ef3b443 100644
--- a/src/AmbientLightingThread.h
+++ b/src/AmbientLightingThread.h
@@ -59,82 +59,82 @@ class AmbientLightingThread : public concurrency::OSThread
return;
}
LOG_DEBUG("AmbientLighting init");
-#if defined(HAS_NCP5623) || defined(HAS_LP5562)
+#ifdef HAS_NCP5623
if (_type == ScanI2C::NCP5623) {
rgb.begin();
#endif
#ifdef HAS_LP5562
- } else if (_type == ScanI2C::LP5562) {
- rgbw.begin();
+ if (_type == ScanI2C::LP5562) {
+ rgbw.begin();
#endif
#ifdef RGBLED_RED
- pinMode(RGBLED_RED, OUTPUT);
- pinMode(RGBLED_GREEN, OUTPUT);
- pinMode(RGBLED_BLUE, OUTPUT);
+ pinMode(RGBLED_RED, OUTPUT);
+ pinMode(RGBLED_GREEN, OUTPUT);
+ pinMode(RGBLED_BLUE, OUTPUT);
#endif
#ifdef HAS_NEOPIXEL
- pixels.begin(); // Initialise the pixel(s)
- pixels.clear(); // Set all pixel colors to 'off'
- pixels.setBrightness(moduleConfig.ambient_lighting.current);
+ pixels.begin(); // Initialise the pixel(s)
+ pixels.clear(); // Set all pixel colors to 'off'
+ pixels.setBrightness(moduleConfig.ambient_lighting.current);
#endif
- setLighting();
+ setLighting();
#endif
#if defined(HAS_NCP5623) || defined(HAS_LP5562)
- }
+ }
#endif
- }
+ }
- protected:
- int32_t runOnce() override
- {
+ protected:
+ int32_t runOnce() override
+ {
#ifdef HAS_RGB_LED
#if defined(HAS_NCP5623) || defined(HAS_LP5562)
- if ((_type == ScanI2C::NCP5623 || _type == ScanI2C::LP5562) && moduleConfig.ambient_lighting.led_state) {
+ if ((_type == ScanI2C::NCP5623 || _type == ScanI2C::LP5562) && moduleConfig.ambient_lighting.led_state) {
#endif
- setLighting();
- return 30000; // 30 seconds to reset from any animations that may have been running from Ext. Notification
+ setLighting();
+ return 30000; // 30 seconds to reset from any animations that may have been running from Ext. Notification
#if defined(HAS_NCP5623) || defined(HAS_LP5562)
+ }
+#endif
+#endif
+ return disable();
}
-#endif
-#endif
- return disable();
- }
- // When shutdown() is issued, setLightingOff will be called.
- CallbackObserver notifyDeepSleepObserver =
- CallbackObserver(this, &AmbientLightingThread::setLightingOff);
+ // When shutdown() is issued, setLightingOff will be called.
+ CallbackObserver notifyDeepSleepObserver =
+ CallbackObserver(this, &AmbientLightingThread::setLightingOff);
- private:
- ScanI2C::DeviceType _type = ScanI2C::DeviceType::NONE;
+ private:
+ ScanI2C::DeviceType _type = ScanI2C::DeviceType::NONE;
- // Turn RGB lighting off, is used in junction to shutdown()
- int setLightingOff(void *unused)
- {
+ // Turn RGB lighting off, is used in junction to shutdown()
+ int setLightingOff(void *unused)
+ {
#ifdef HAS_NCP5623
- rgb.setCurrent(0);
- rgb.setRed(0);
- rgb.setGreen(0);
- rgb.setBlue(0);
- LOG_INFO("OFF: NCP5623 Ambient lighting");
+ rgb.setCurrent(0);
+ rgb.setRed(0);
+ rgb.setGreen(0);
+ rgb.setBlue(0);
+ LOG_INFO("OFF: NCP5623 Ambient lighting");
#endif
#ifdef HAS_LP5562
- rgbw.setCurrent(0);
- rgbw.setRed(0);
- rgbw.setGreen(0);
- rgbw.setBlue(0);
- rgbw.setWhite(0);
- LOG_INFO("OFF: LP5562 Ambient lighting");
+ rgbw.setCurrent(0);
+ rgbw.setRed(0);
+ rgbw.setGreen(0);
+ rgbw.setBlue(0);
+ rgbw.setWhite(0);
+ LOG_INFO("OFF: LP5562 Ambient lighting");
#endif
#ifdef HAS_NEOPIXEL
- pixels.clear();
- pixels.show();
- LOG_INFO("OFF: NeoPixel Ambient lighting");
+ pixels.clear();
+ pixels.show();
+ LOG_INFO("OFF: NeoPixel Ambient lighting");
#endif
#ifdef RGBLED_CA
- analogWrite(RGBLED_RED, 255 - 0);
- analogWrite(RGBLED_GREEN, 255 - 0);
- analogWrite(RGBLED_BLUE, 255 - 0);
- LOG_INFO("OFF: Ambient light RGB Common Anode");
+ analogWrite(RGBLED_RED, 255 - 0);
+ analogWrite(RGBLED_GREEN, 255 - 0);
+ analogWrite(RGBLED_BLUE, 255 - 0);
+ LOG_INFO("OFF: Ambient light RGB Common Anode");
#elif defined(RGBLED_RED)
analogWrite(RGBLED_RED, 0);
analogWrite(RGBLED_GREEN, 0);
@@ -142,56 +142,57 @@ class AmbientLightingThread : public concurrency::OSThread
LOG_INFO("OFF: Ambient light RGB Common Cathode");
#endif
#ifdef UNPHONE
- unphone.rgb(0, 0, 0);
- LOG_INFO("OFF: unPhone Ambient lighting");
+ unphone.rgb(0, 0, 0);
+ LOG_INFO("OFF: unPhone Ambient lighting");
#endif
- return 0;
- }
+ return 0;
+ }
- void setLighting()
- {
+ void setLighting()
+ {
#ifdef HAS_NCP5623
- rgb.setCurrent(moduleConfig.ambient_lighting.current);
- rgb.setRed(moduleConfig.ambient_lighting.red);
- rgb.setGreen(moduleConfig.ambient_lighting.green);
- rgb.setBlue(moduleConfig.ambient_lighting.blue);
- LOG_DEBUG("Init NCP5623 Ambient light w/ current=%d, red=%d, green=%d, blue=%d", moduleConfig.ambient_lighting.current,
- moduleConfig.ambient_lighting.red, moduleConfig.ambient_lighting.green, moduleConfig.ambient_lighting.blue);
+ rgb.setCurrent(moduleConfig.ambient_lighting.current);
+ rgb.setRed(moduleConfig.ambient_lighting.red);
+ rgb.setGreen(moduleConfig.ambient_lighting.green);
+ rgb.setBlue(moduleConfig.ambient_lighting.blue);
+ LOG_DEBUG("Init NCP5623 Ambient light w/ current=%d, red=%d, green=%d, blue=%d",
+ moduleConfig.ambient_lighting.current, moduleConfig.ambient_lighting.red,
+ moduleConfig.ambient_lighting.green, moduleConfig.ambient_lighting.blue);
#endif
#ifdef HAS_LP5562
- rgbw.setCurrent(moduleConfig.ambient_lighting.current);
- rgbw.setRed(moduleConfig.ambient_lighting.red);
- rgbw.setGreen(moduleConfig.ambient_lighting.green);
- rgbw.setBlue(moduleConfig.ambient_lighting.blue);
- LOG_DEBUG("Init LP5562 Ambient light w/ current=%d, red=%d, green=%d, blue=%d", moduleConfig.ambient_lighting.current,
- moduleConfig.ambient_lighting.red, moduleConfig.ambient_lighting.green, moduleConfig.ambient_lighting.blue);
+ rgbw.setCurrent(moduleConfig.ambient_lighting.current);
+ rgbw.setRed(moduleConfig.ambient_lighting.red);
+ rgbw.setGreen(moduleConfig.ambient_lighting.green);
+ rgbw.setBlue(moduleConfig.ambient_lighting.blue);
+ LOG_DEBUG("Init LP5562 Ambient light w/ current=%d, red=%d, green=%d, blue=%d", moduleConfig.ambient_lighting.current,
+ moduleConfig.ambient_lighting.red, moduleConfig.ambient_lighting.green, moduleConfig.ambient_lighting.blue);
#endif
#ifdef HAS_NEOPIXEL
- pixels.fill(pixels.Color(moduleConfig.ambient_lighting.red, moduleConfig.ambient_lighting.green,
- moduleConfig.ambient_lighting.blue),
- 0, NEOPIXEL_COUNT);
+ pixels.fill(pixels.Color(moduleConfig.ambient_lighting.red, moduleConfig.ambient_lighting.green,
+ moduleConfig.ambient_lighting.blue),
+ 0, NEOPIXEL_COUNT);
// RadioMaster Bandit has addressable LED at the two buttons
// this allow us to set different lighting for them in variant.h file.
#ifdef RADIOMASTER_900_BANDIT
#if defined(BUTTON1_COLOR) && defined(BUTTON1_COLOR_INDEX)
- pixels.fill(BUTTON1_COLOR, BUTTON1_COLOR_INDEX, 1);
+ pixels.fill(BUTTON1_COLOR, BUTTON1_COLOR_INDEX, 1);
#endif
#if defined(BUTTON2_COLOR) && defined(BUTTON2_COLOR_INDEX)
- pixels.fill(BUTTON2_COLOR, BUTTON2_COLOR_INDEX, 1);
+ pixels.fill(BUTTON2_COLOR, BUTTON2_COLOR_INDEX, 1);
#endif
#endif
- pixels.show();
- LOG_DEBUG("Init NeoPixel Ambient light w/ brightness(current)=%d, red=%d, green=%d, blue=%d",
- moduleConfig.ambient_lighting.current, moduleConfig.ambient_lighting.red, moduleConfig.ambient_lighting.green,
- moduleConfig.ambient_lighting.blue);
+ pixels.show();
+ LOG_DEBUG("Init NeoPixel Ambient light w/ brightness(current)=%d, red=%d, green=%d, blue=%d",
+ moduleConfig.ambient_lighting.current, moduleConfig.ambient_lighting.red,
+ moduleConfig.ambient_lighting.green, moduleConfig.ambient_lighting.blue);
#endif
#ifdef RGBLED_CA
- analogWrite(RGBLED_RED, 255 - moduleConfig.ambient_lighting.red);
- analogWrite(RGBLED_GREEN, 255 - moduleConfig.ambient_lighting.green);
- analogWrite(RGBLED_BLUE, 255 - moduleConfig.ambient_lighting.blue);
- LOG_DEBUG("Init Ambient light RGB Common Anode w/ red=%d, green=%d, blue=%d", moduleConfig.ambient_lighting.red,
- moduleConfig.ambient_lighting.green, moduleConfig.ambient_lighting.blue);
+ analogWrite(RGBLED_RED, 255 - moduleConfig.ambient_lighting.red);
+ analogWrite(RGBLED_GREEN, 255 - moduleConfig.ambient_lighting.green);
+ analogWrite(RGBLED_BLUE, 255 - moduleConfig.ambient_lighting.blue);
+ LOG_DEBUG("Init Ambient light RGB Common Anode w/ red=%d, green=%d, blue=%d", moduleConfig.ambient_lighting.red,
+ moduleConfig.ambient_lighting.green, moduleConfig.ambient_lighting.blue);
#elif defined(RGBLED_RED)
analogWrite(RGBLED_RED, moduleConfig.ambient_lighting.red);
analogWrite(RGBLED_GREEN, moduleConfig.ambient_lighting.green);
@@ -200,11 +201,12 @@ class AmbientLightingThread : public concurrency::OSThread
moduleConfig.ambient_lighting.green, moduleConfig.ambient_lighting.blue);
#endif
#ifdef UNPHONE
- unphone.rgb(moduleConfig.ambient_lighting.red, moduleConfig.ambient_lighting.green, moduleConfig.ambient_lighting.blue);
- LOG_DEBUG("Init unPhone Ambient light w/ red=%d, green=%d, blue=%d", moduleConfig.ambient_lighting.red,
- moduleConfig.ambient_lighting.green, moduleConfig.ambient_lighting.blue);
+ unphone.rgb(moduleConfig.ambient_lighting.red, moduleConfig.ambient_lighting.green,
+ moduleConfig.ambient_lighting.blue);
+ LOG_DEBUG("Init unPhone Ambient light w/ red=%d, green=%d, blue=%d", moduleConfig.ambient_lighting.red,
+ moduleConfig.ambient_lighting.green, moduleConfig.ambient_lighting.blue);
#endif
- }
-};
+ }
+ };
} // namespace concurrency
diff --git a/src/AudioThread.h b/src/AudioThread.h
index 04ff64a6e..286729909 100644
--- a/src/AudioThread.h
+++ b/src/AudioThread.h
@@ -47,6 +47,20 @@ class AudioThread : public concurrency::OSThread
setCPUFast(false);
}
+ void readAloud(const char *text)
+ {
+ if (i2sRtttl != nullptr) {
+ i2sRtttl->stop();
+ delete i2sRtttl;
+ i2sRtttl = nullptr;
+ }
+
+ ESP8266SAM *sam = new ESP8266SAM;
+ sam->Say(audioOut, text);
+ delete sam;
+ setCPUFast(false);
+ }
+
protected:
int32_t runOnce() override
{
diff --git a/src/BluetoothStatus.h b/src/BluetoothStatus.h
index 526b6f243..f6bb43cc2 100644
--- a/src/BluetoothStatus.h
+++ b/src/BluetoothStatus.h
@@ -88,10 +88,16 @@ class BluetoothStatus : public Status
break;
case ConnectionState::CONNECTED:
LOG_DEBUG("BluetoothStatus CONNECTED");
+#ifdef BLE_LED
+ digitalWrite(BLE_LED, HIGH);
+#endif
break;
case ConnectionState::DISCONNECTED:
LOG_DEBUG("BluetoothStatus DISCONNECTED");
+#ifdef BLE_LED
+ digitalWrite(BLE_LED, LOW);
+#endif
break;
}
}
@@ -102,4 +108,4 @@ class BluetoothStatus : public Status
} // namespace meshtastic
-extern meshtastic::BluetoothStatus *bluetoothStatus;
\ No newline at end of file
+extern meshtastic::BluetoothStatus *bluetoothStatus;
diff --git a/src/ButtonThread.cpp b/src/ButtonThread.cpp
deleted file mode 100644
index 8db52c074..000000000
--- a/src/ButtonThread.cpp
+++ /dev/null
@@ -1,467 +0,0 @@
-#include "ButtonThread.h"
-
-#include "configuration.h"
-#if !MESHTASTIC_EXCLUDE_GPS
-#include "GPS.h"
-#endif
-#include "MeshService.h"
-#include "PowerFSM.h"
-#include "RadioLibInterface.h"
-#include "buzz.h"
-#include "main.h"
-#include "modules/ExternalNotificationModule.h"
-#include "power.h"
-#include "sleep.h"
-#ifdef ARCH_PORTDUINO
-#include "platform/portduino/PortduinoGlue.h"
-#endif
-
-#define DEBUG_BUTTONS 0
-#if DEBUG_BUTTONS
-#define LOG_BUTTON(...) LOG_DEBUG(__VA_ARGS__)
-#else
-#define LOG_BUTTON(...)
-#endif
-
-using namespace concurrency;
-
-ButtonThread *buttonThread; // Declared extern in header
-volatile ButtonThread::ButtonEventType ButtonThread::btnEvent = ButtonThread::BUTTON_EVENT_NONE;
-
-#if defined(BUTTON_PIN) || defined(ARCH_PORTDUINO) || defined(USERPREFS_BUTTON_PIN)
-OneButton ButtonThread::userButton; // Get reference to static member
-#endif
-ButtonThread::ButtonThread() : OSThread("Button")
-{
-#if defined(BUTTON_PIN) || defined(ARCH_PORTDUINO) || defined(USERPREFS_BUTTON_PIN)
-
-#if defined(ARCH_PORTDUINO)
- if (settingsMap.count(user) != 0 && settingsMap[user] != RADIOLIB_NC) {
- this->userButton = OneButton(settingsMap[user], true, true);
- LOG_DEBUG("Use GPIO%02d for button", settingsMap[user]);
- }
-#elif defined(BUTTON_PIN)
-#if !defined(USERPREFS_BUTTON_PIN)
- int pin = config.device.button_gpio ? config.device.button_gpio : BUTTON_PIN; // Resolved button pin
-#endif
-#ifdef USERPREFS_BUTTON_PIN
- int pin = config.device.button_gpio ? config.device.button_gpio : USERPREFS_BUTTON_PIN; // Resolved button pin
-#endif
-#if defined(HELTEC_CAPSULE_SENSOR_V3) || defined(HELTEC_SENSOR_HUB)
- this->userButton = OneButton(pin, false, false);
-#elif defined(BUTTON_ACTIVE_LOW)
- this->userButton = OneButton(pin, BUTTON_ACTIVE_LOW, BUTTON_ACTIVE_PULLUP);
-#else
- this->userButton = OneButton(pin, true, true);
-#endif
- LOG_DEBUG("Use GPIO%02d for button", pin);
-#endif
-
-#ifdef INPUT_PULLUP_SENSE
- // Some platforms (nrf52) have a SENSE variant which allows wake from sleep - override what OneButton did
-#ifdef BUTTON_SENSE_TYPE
- pinMode(pin, BUTTON_SENSE_TYPE);
-#else
- pinMode(pin, INPUT_PULLUP_SENSE);
-#endif
-#endif
-
-#if defined(BUTTON_PIN) || defined(ARCH_PORTDUINO) || defined(USERPREFS_BUTTON_PIN)
- userButton.attachClick(userButtonPressed);
- userButton.setClickMs(BUTTON_CLICK_MS);
- userButton.setPressMs(BUTTON_LONGPRESS_MS);
- userButton.setDebounceMs(1);
- userButton.attachDoubleClick(userButtonDoublePressed);
- userButton.attachMultiClick(userButtonMultiPressed, this); // Reference to instance: get click count from non-static OneButton
-#if !defined(T_DECK) && \
- !defined( \
- ELECROW_ThinkNode_M2) // T-Deck immediately wakes up after shutdown, Thinknode M2 has this on the smaller ALT button
- userButton.attachLongPressStart(userButtonPressedLongStart);
- userButton.attachLongPressStop(userButtonPressedLongStop);
-#endif
-#endif
-
-#ifdef BUTTON_PIN_ALT
-#if defined(ELECROW_ThinkNode_M2)
- this->userButtonAlt = OneButton(BUTTON_PIN_ALT, false, false);
-#else
- this->userButtonAlt = OneButton(BUTTON_PIN_ALT, true, true);
-#endif
-#ifdef INPUT_PULLUP_SENSE
- // Some platforms (nrf52) have a SENSE variant which allows wake from sleep - override what OneButton did
- pinMode(BUTTON_PIN_ALT, INPUT_PULLUP_SENSE);
-#endif
- userButtonAlt.attachClick(userButtonPressedScreen);
- userButtonAlt.setClickMs(BUTTON_CLICK_MS);
- userButtonAlt.setPressMs(BUTTON_LONGPRESS_MS);
- userButtonAlt.setDebounceMs(1);
- userButtonAlt.attachLongPressStart(userButtonPressedLongStart);
- userButtonAlt.attachLongPressStop(userButtonPressedLongStop);
-#endif
-
-#ifdef BUTTON_PIN_TOUCH
- userButtonTouch = OneButton(BUTTON_PIN_TOUCH, true, true);
- userButtonTouch.setPressMs(BUTTON_TOUCH_MS);
- userButtonTouch.attachLongPressStart(touchPressedLongStart); // Better handling with longpress than click?
-#endif
-
-#ifdef ARCH_ESP32
- // Register callbacks for before and after lightsleep
- // Used to detach and reattach interrupts
- lsObserver.observe(¬ifyLightSleep);
- lsEndObserver.observe(¬ifyLightSleepEnd);
-#endif
-
- attachButtonInterrupts();
-#endif
-}
-
-void ButtonThread::switchPage()
-{
-#ifdef BUTTON_PIN
-#if !defined(USERPREFS_BUTTON_PIN)
- if (((config.device.button_gpio ? config.device.button_gpio : BUTTON_PIN) !=
- moduleConfig.canned_message.inputbroker_pin_press) ||
- !(moduleConfig.canned_message.updown1_enabled || moduleConfig.canned_message.rotary1_enabled) ||
- !moduleConfig.canned_message.enabled) {
- powerFSM.trigger(EVENT_PRESS);
- }
-#endif
-#if defined(USERPREFS_BUTTON_PIN)
- if (((config.device.button_gpio ? config.device.button_gpio : USERPREFS_BUTTON_PIN) !=
- moduleConfig.canned_message.inputbroker_pin_press) ||
- !(moduleConfig.canned_message.updown1_enabled || moduleConfig.canned_message.rotary1_enabled) ||
- !moduleConfig.canned_message.enabled) {
- powerFSM.trigger(EVENT_PRESS);
- }
-#endif
-
-#endif
-#if defined(ARCH_PORTDUINO)
- if ((settingsMap.count(user) != 0 && settingsMap[user] != RADIOLIB_NC) &&
- (settingsMap[user] != moduleConfig.canned_message.inputbroker_pin_press) ||
- !moduleConfig.canned_message.enabled) {
- powerFSM.trigger(EVENT_PRESS);
- }
-#endif
-}
-
-void ButtonThread::sendAdHocPosition()
-{
- service->refreshLocalMeshNode();
- auto sentPosition = service->trySendPosition(NODENUM_BROADCAST, true);
- if (screen) {
- if (sentPosition)
- screen->print("Sent ad-hoc position\n");
- else
- screen->print("Sent ad-hoc nodeinfo\n");
- screen->forceDisplay(true); // Force a new UI frame, then force an EInk update
- }
-}
-
-int32_t ButtonThread::runOnce()
-{
- // If the button is pressed we suppress CPU sleep until release
- canSleep = true; // Assume we should not keep the board awake
-
-#if defined(BUTTON_PIN) || defined(USERPREFS_BUTTON_PIN)
- userButton.tick();
- canSleep &= userButton.isIdle();
-#elif defined(ARCH_PORTDUINO)
- if (settingsMap.count(user) != 0 && settingsMap[user] != RADIOLIB_NC) {
- userButton.tick();
- canSleep &= userButton.isIdle();
- }
-#endif
-#ifdef BUTTON_PIN_ALT
- userButtonAlt.tick();
- canSleep &= userButtonAlt.isIdle();
-#endif
-#ifdef BUTTON_PIN_TOUCH
- userButtonTouch.tick();
- canSleep &= userButtonTouch.isIdle();
-#endif
-
- if (btnEvent != BUTTON_EVENT_NONE) {
- switch (btnEvent) {
- case BUTTON_EVENT_PRESSED: {
- LOG_BUTTON("press!");
- // If a nag notification is running, stop it and prevent other actions
- if (moduleConfig.external_notification.enabled && (externalNotificationModule->nagCycleCutoff != UINT32_MAX)) {
- externalNotificationModule->stopNow();
- break;
- }
-#ifdef ELECROW_ThinkNode_M1
- sendAdHocPosition();
- break;
-#endif
- switchPage();
- break;
- }
-
- case BUTTON_EVENT_PRESSED_SCREEN: {
- LOG_BUTTON("AltPress!");
-#ifdef ELECROW_ThinkNode_M1
- // If a nag notification is running, stop it and prevent other actions
- if (moduleConfig.external_notification.enabled && (externalNotificationModule->nagCycleCutoff != UINT32_MAX)) {
- externalNotificationModule->stopNow();
- break;
- }
- switchPage();
- break;
-#endif
- // turn screen on or off
- screen_flag = !screen_flag;
- if (screen)
- screen->setOn(screen_flag);
- break;
- }
-
- case BUTTON_EVENT_DOUBLE_PRESSED: {
- LOG_BUTTON("Double press!");
-#ifdef ELECROW_ThinkNode_M1
- digitalWrite(PIN_EINK_EN, digitalRead(PIN_EINK_EN) == LOW);
- break;
-#endif
- sendAdHocPosition();
- break;
- }
-
- case BUTTON_EVENT_MULTI_PRESSED: {
- LOG_BUTTON("Mulitipress! %hux", multipressClickCount);
- switch (multipressClickCount) {
-#if HAS_GPS && !defined(ELECROW_ThinkNode_M1)
- // 3 clicks: toggle GPS
- case 3:
- if (!config.device.disable_triple_click && (gps != nullptr)) {
- gps->toggleGpsMode();
- if (screen)
- screen->forceDisplay(true); // Force a new UI frame, then force an EInk update
- }
- break;
-#elif defined(ELECROW_ThinkNode_M1) || defined(ELECROW_ThinkNode_M2)
- case 3:
- LOG_INFO("3 clicks: toggle buzzer");
- buzzer_flag = !buzzer_flag;
- if (!buzzer_flag)
- noTone(PIN_BUZZER);
- break;
-
-#endif
-
-#if defined(USE_EINK) && defined(PIN_EINK_EN) && !defined(ELECROW_ThinkNode_M1) // i.e. T-Echo
- // 4 clicks: toggle backlight
- case 4:
- digitalWrite(PIN_EINK_EN, digitalRead(PIN_EINK_EN) == LOW);
- break;
-#endif
-#if !MESHTASTIC_EXCLUDE_SCREEN && HAS_SCREEN
- // 5 clicks: start accelerometer/magenetometer calibration for 30 seconds
- case 5:
- if (accelerometerThread) {
- accelerometerThread->calibrate(30);
- }
- break;
- // 6 clicks: start accelerometer/magenetometer calibration for 60 seconds
- case 6:
- if (accelerometerThread) {
- accelerometerThread->calibrate(60);
- }
- break;
-#endif
- // No valid multipress action
- default:
- break;
- } // end switch: click count
-
- break;
- } // end multipress event
-
- case BUTTON_EVENT_LONG_PRESSED: {
- LOG_BUTTON("Long press!");
- powerFSM.trigger(EVENT_PRESS);
- if (screen) {
- screen->startAlert("Shutting down...");
- }
- playBeep();
- break;
- }
-
- // Do actual shutdown when button released, otherwise the button release
- // may wake the board immediatedly.
- case BUTTON_EVENT_LONG_RELEASED: {
- LOG_INFO("Shutdown from long press");
- playShutdownMelody();
- delay(3000);
- power->shutdown();
- break;
- }
-
-#ifdef BUTTON_PIN_TOUCH
- case BUTTON_EVENT_TOUCH_LONG_PRESSED: {
- LOG_BUTTON("Touch press!");
- // Ignore if: no screen
- if (!screen)
- break;
-
-#ifdef TTGO_T_ECHO
- // Ignore if: TX in progress
- // Uncommon T-Echo hardware bug, LoRa TX triggers touch button
- if (!RadioLibInterface::instance || RadioLibInterface::instance->isSending())
- break;
-#endif
-
- // Wake if asleep
- if (powerFSM.getState() == &stateDARK)
- powerFSM.trigger(EVENT_PRESS);
-
- // Update display (legacy behaviour)
- screen->forceDisplay();
- break;
- }
-#endif // BUTTON_PIN_TOUCH
-
- default:
- break;
- }
- btnEvent = BUTTON_EVENT_NONE;
- }
-
- return 50;
-}
-
-/*
- * Attach (or re-attach) hardware interrupts for buttons
- * Public method. Used outside class when waking from MCU sleep
- */
-void ButtonThread::attachButtonInterrupts()
-{
-#if defined(ARCH_PORTDUINO)
- if (settingsMap.count(user) != 0 && settingsMap[user] != RADIOLIB_NC)
- wakeOnIrq(settingsMap[user], FALLING);
-#elif defined(BUTTON_PIN)
- // Interrupt for user button, during normal use. Improves responsiveness.
- attachInterrupt(
-#if !defined(USERPREFS_BUTTON_PIN)
- config.device.button_gpio ? config.device.button_gpio : BUTTON_PIN,
-#endif
-#if defined(USERPREFS_BUTTON_PIN)
- config.device.button_gpio ? config.device.button_gpio : USERPREFS_BUTTON_PIN,
-#endif
- []() {
- ButtonThread::userButton.tick();
- runASAP = true;
- BaseType_t higherWake = 0;
- mainDelay.interruptFromISR(&higherWake);
- },
- CHANGE);
-#endif
-
-#ifdef BUTTON_PIN_ALT
-#ifdef ELECROW_ThinkNode_M2
- wakeOnIrq(BUTTON_PIN_ALT, RISING);
-#else
- wakeOnIrq(BUTTON_PIN_ALT, FALLING);
-#endif
-#endif
-
-#ifdef BUTTON_PIN_TOUCH
- wakeOnIrq(BUTTON_PIN_TOUCH, FALLING);
-#endif
-}
-
-/*
- * Detach the "normal" button interrupts.
- * Public method. Used before attaching a "wake-on-button" interrupt for MCU sleep
- */
-void ButtonThread::detachButtonInterrupts()
-{
-#if defined(ARCH_PORTDUINO)
- if (settingsMap.count(user) != 0 && settingsMap[user] != RADIOLIB_NC)
- detachInterrupt(settingsMap[user]);
-#elif defined(BUTTON_PIN)
-#if !defined(USERPREFS_BUTTON_PIN)
- detachInterrupt(config.device.button_gpio ? config.device.button_gpio : BUTTON_PIN);
-#endif
-#if defined(USERPREFS_BUTTON_PIN)
- detachInterrupt(config.device.button_gpio ? config.device.button_gpio : USERPREFS_BUTTON_PIN);
-#endif
-#endif
-
-#ifdef BUTTON_PIN_ALT
- detachInterrupt(BUTTON_PIN_ALT);
-#endif
-
-#ifdef BUTTON_PIN_TOUCH
- detachInterrupt(BUTTON_PIN_TOUCH);
-#endif
-}
-
-#ifdef ARCH_ESP32
-
-// Detach our class' interrupts before lightsleep
-// Allows sleep.cpp to configure its own interrupts, which wake the device on user-button press
-int ButtonThread::beforeLightSleep(void *unused)
-{
- detachButtonInterrupts();
- return 0; // Indicates success
-}
-
-// Reconfigure our interrupts
-// Our class' interrupts were disconnected during sleep, to allow the user button to wake the device from sleep
-int ButtonThread::afterLightSleep(esp_sleep_wakeup_cause_t cause)
-{
- attachButtonInterrupts();
- return 0; // Indicates success
-}
-
-#endif
-
-/**
- * Watch a GPIO and if we get an IRQ, wake the main thread.
- * Use to add wake on button press
- */
-void ButtonThread::wakeOnIrq(int irq, int mode)
-{
- attachInterrupt(
- irq,
- [] {
- BaseType_t higherWake = 0;
- mainDelay.interruptFromISR(&higherWake);
- runASAP = true;
- },
- FALLING);
-}
-
-// Static callback
-void ButtonThread::userButtonMultiPressed(void *callerThread)
-{
- // Grab click count from non-static button, while the info is still valid
- ButtonThread *thread = (ButtonThread *)callerThread;
- thread->storeClickCount();
-
- // Then handle later, in the usual way
- btnEvent = BUTTON_EVENT_MULTI_PRESSED;
-}
-
-// Non-static method, runs during callback. Grabs info while still valid
-void ButtonThread::storeClickCount()
-{
-#if defined(BUTTON_PIN) || defined(USERPREFS_BUTTON_PIN)
- multipressClickCount = userButton.getNumberClicks();
-#endif
-}
-
-void ButtonThread::userButtonPressedLongStart()
-{
- if (millis() > c_holdOffTime) {
- btnEvent = BUTTON_EVENT_LONG_PRESSED;
- }
-}
-
-void ButtonThread::userButtonPressedLongStop()
-{
- if (millis() > c_holdOffTime) {
- btnEvent = BUTTON_EVENT_LONG_RELEASED;
- }
-}
\ No newline at end of file
diff --git a/src/ButtonThread.h b/src/ButtonThread.h
deleted file mode 100644
index 3af700dd0..000000000
--- a/src/ButtonThread.h
+++ /dev/null
@@ -1,91 +0,0 @@
-#pragma once
-
-#include "OneButton.h"
-#include "concurrency/OSThread.h"
-#include "configuration.h"
-
-#ifndef BUTTON_CLICK_MS
-#define BUTTON_CLICK_MS 250
-#endif
-
-#ifndef BUTTON_LONGPRESS_MS
-#define BUTTON_LONGPRESS_MS 5000
-#endif
-
-#ifndef BUTTON_TOUCH_MS
-#define BUTTON_TOUCH_MS 400
-#endif
-
-class ButtonThread : public concurrency::OSThread
-{
- public:
- static const uint32_t c_holdOffTime = 30000; // hold off 30s after boot
-
- enum ButtonEventType {
- BUTTON_EVENT_NONE,
- BUTTON_EVENT_PRESSED,
- BUTTON_EVENT_PRESSED_SCREEN,
- BUTTON_EVENT_DOUBLE_PRESSED,
- BUTTON_EVENT_MULTI_PRESSED,
- BUTTON_EVENT_LONG_PRESSED,
- BUTTON_EVENT_LONG_RELEASED,
- BUTTON_EVENT_TOUCH_LONG_PRESSED,
- };
-
- ButtonThread();
- int32_t runOnce() override;
- void attachButtonInterrupts();
- void detachButtonInterrupts();
- void storeClickCount();
- bool isBuzzing() { return buzzer_flag; }
- void setScreenFlag(bool flag) { screen_flag = flag; }
- bool getScreenFlag() { return screen_flag; }
-
- // Disconnect and reconnect interrupts for light sleep
-#ifdef ARCH_ESP32
- int beforeLightSleep(void *unused);
- int afterLightSleep(esp_sleep_wakeup_cause_t cause);
-#endif
- private:
-#if defined(BUTTON_PIN) || defined(ARCH_PORTDUINO) || defined(USERPREFS_BUTTON_PIN)
- static OneButton userButton; // Static - accessed from an interrupt
-#endif
-#ifdef BUTTON_PIN_ALT
- OneButton userButtonAlt;
-#endif
-#ifdef BUTTON_PIN_TOUCH
- OneButton userButtonTouch;
-#endif
-
-#ifdef ARCH_ESP32
- // Get notified when lightsleep begins and ends
- CallbackObserver lsObserver =
- CallbackObserver(this, &ButtonThread::beforeLightSleep);
- CallbackObserver lsEndObserver =
- CallbackObserver(this, &ButtonThread::afterLightSleep);
-#endif
-
- // set during IRQ
- static volatile ButtonEventType btnEvent;
- bool buzzer_flag = false;
- bool screen_flag = true;
-
- // Store click count during callback, for later use
- volatile int multipressClickCount = 0;
-
- static void wakeOnIrq(int irq, int mode);
-
- static void sendAdHocPosition();
- static void switchPage();
-
- // IRQ callbacks
- static void userButtonPressed() { btnEvent = BUTTON_EVENT_PRESSED; }
- static void userButtonPressedScreen() { btnEvent = BUTTON_EVENT_PRESSED_SCREEN; }
- static void userButtonDoublePressed() { btnEvent = BUTTON_EVENT_DOUBLE_PRESSED; }
- static void userButtonMultiPressed(void *callerThread); // Retrieve click count from non-static Onebutton while still valid
- static void userButtonPressedLongStart();
- static void userButtonPressedLongStop();
- static void touchPressedLongStart() { btnEvent = BUTTON_EVENT_TOUCH_LONG_PRESSED; }
-};
-
-extern ButtonThread *buttonThread;
diff --git a/src/Power.cpp b/src/Power.cpp
index a9ed6360e..9c67977bd 100644
--- a/src/Power.cpp
+++ b/src/Power.cpp
@@ -103,7 +103,7 @@ NullSensor ina3221Sensor;
#endif
-#if !MESHTASTIC_EXCLUDE_I2C && !defined(ARCH_STM32WL)
+#if !MESHTASTIC_EXCLUDE_I2C
#include "modules/Telemetry/Sensor/MAX17048Sensor.h"
#include
extern std::pair nodeTelemetrySensorsMap[_meshtastic_TelemetrySensorType_MAX + 1];
@@ -278,7 +278,7 @@ class AnalogBatteryLevel : public HasBatteryLevel
}
#endif
-#if HAS_TELEMETRY && !defined(ARCH_STM32WL) && !defined(HAS_PMU) && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
+#if HAS_TELEMETRY && !defined(HAS_PMU) && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
if (hasINA()) {
return getINAVoltage();
}
@@ -456,8 +456,7 @@ class AnalogBatteryLevel : public HasBatteryLevel
#ifdef EXT_CHRG_DETECT
return digitalRead(EXT_CHRG_DETECT) == ext_chrg_detect_value;
#else
-#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && !defined(ARCH_STM32WL) && \
- !defined(DISABLE_INA_CHARGING_DETECTION)
+#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && !defined(DISABLE_INA_CHARGING_DETECTION)
if (hasINA()) {
// get current flow from INA sensor - negative value means power flowing into the battery
// default assuming BATTERY+ <--> INA_VIN+ <--> SHUNT RESISTOR <--> INA_VIN- <--> LOAD
@@ -503,7 +502,7 @@ class AnalogBatteryLevel : public HasBatteryLevel
}
#endif
-#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && !defined(ARCH_STM32WL)
+#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
uint16_t getINAVoltage()
{
if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA219].first == config.power.device_battery_ina_address) {
@@ -661,12 +660,14 @@ bool Power::analogInit()
*/
bool Power::setup()
{
- // initialise one power sensor (only)
- bool found = axpChipInit();
- if (!found)
- found = lipoInit();
- if (!found)
- found = analogInit();
+ bool found = false;
+ if (axpChipInit()) {
+ found = true;
+ } else if (lipoInit()) {
+ found = true;
+ } else if (analogInit()) {
+ found = true;
+ }
#ifdef NRF_APM
found = true;
@@ -680,7 +681,7 @@ bool Power::setup()
void Power::shutdown()
{
- LOG_INFO("Shutting down");
+ LOG_INFO("Shutting Down");
#if defined(ARCH_NRF52) || defined(ARCH_ESP32) || defined(ARCH_RP2040)
#ifdef PIN_LED1
@@ -853,7 +854,8 @@ int32_t Power::runOnce()
#ifndef T_WATCH_S3 // FIXME - why is this triggering on the T-Watch S3?
if (PMU->isPekeyLongPressIrq()) {
LOG_DEBUG("PEK long button press");
- screen->setOn(false);
+ if (screen)
+ screen->setOn(false);
}
#endif
@@ -1158,7 +1160,7 @@ bool Power::axpChipInit()
#endif
}
-#if !MESHTASTIC_EXCLUDE_I2C && !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL)
+#if !MESHTASTIC_EXCLUDE_I2C && __has_include()
/**
* Wrapper class for an I2C MAX17048 Lipo battery sensor.
diff --git a/src/PowerFSM.cpp b/src/PowerFSM.cpp
index dbe4796cf..3b3f8080d 100644
--- a/src/PowerFSM.cpp
+++ b/src/PowerFSM.cpp
@@ -26,7 +26,7 @@
#ifndef SLEEP_TIME
#define SLEEP_TIME 30
#endif
-#if EXCLUDE_POWER_FSM
+#if MESHTASTIC_EXCLUDE_POWER_FSM
FakeFsm powerFSM;
void PowerFSM_setup(){};
#else
@@ -82,7 +82,8 @@ static uint32_t secsSlept;
static void lsEnter()
{
LOG_INFO("lsEnter begin, ls_secs=%u", config.power.ls_secs);
- screen->setOn(false);
+ if (screen)
+ screen->setOn(false);
secsSlept = 0; // How long have we been sleeping this time
// LOG_INFO("lsEnter end");
@@ -160,7 +161,8 @@ static void lsExit()
static void nbEnter()
{
LOG_DEBUG("State: NB");
- screen->setOn(false);
+ if (screen)
+ screen->setOn(false);
#ifdef ARCH_ESP32
// Only ESP32 should turn off bluetooth
setBluetoothEnable(false);
@@ -172,22 +174,23 @@ static void nbEnter()
static void darkEnter()
{
setBluetoothEnable(true);
- screen->setOn(false);
+ if (screen)
+ screen->setOn(false);
}
static void serialEnter()
{
LOG_DEBUG("State: SERIAL");
setBluetoothEnable(false);
- screen->setOn(true);
- screen->print("Serial connected\n");
+ if (screen) {
+ screen->setOn(true);
+ }
}
static void serialExit()
{
// Turn bluetooth back on when we leave serial stream API
setBluetoothEnable(true);
- screen->print("Serial disconnected\n");
}
static void powerEnter()
@@ -198,15 +201,10 @@ static void powerEnter()
LOG_INFO("Loss of power in Powered");
powerFSM.trigger(EVENT_POWER_DISCONNECTED);
} else {
- screen->setOn(true);
+ if (screen)
+ screen->setOn(true);
setBluetoothEnable(true);
// within enter() the function getState() returns the state we came from
-
- // Mothballed: print change of power-state to device screen
- /* if (strcmp(powerFSM.getState()->name, "BOOT") != 0 && strcmp(powerFSM.getState()->name, "POWER") != 0 &&
- strcmp(powerFSM.getState()->name, "DARK") != 0) {
- screen->print("Powered...\n");
- }*/
}
}
@@ -221,18 +219,16 @@ static void powerIdle()
static void powerExit()
{
- screen->setOn(true);
+ if (screen)
+ screen->setOn(true);
setBluetoothEnable(true);
-
- // Mothballed: print change of power-state to device screen
- /*if (!isPowered())
- screen->print("Unpowered...\n");*/
}
static void onEnter()
{
LOG_DEBUG("State: ON");
- screen->setOn(true);
+ if (screen)
+ screen->setOn(true);
setBluetoothEnable(true);
}
@@ -244,11 +240,6 @@ static void onIdle()
}
}
-static void screenPress()
-{
- screen->onPress();
-}
-
static void bootEnter()
{
LOG_DEBUG("State: BOOT");
@@ -292,9 +283,9 @@ void PowerFSM_setup()
powerFSM.add_transition(&stateLS, &stateON, EVENT_PRESS, NULL, "Press");
powerFSM.add_transition(&stateNB, &stateON, EVENT_PRESS, NULL, "Press");
powerFSM.add_transition(&stateDARK, isPowered() ? &statePOWER : &stateON, EVENT_PRESS, NULL, "Press");
- powerFSM.add_transition(&statePOWER, &statePOWER, EVENT_PRESS, screenPress, "Press");
- powerFSM.add_transition(&stateON, &stateON, EVENT_PRESS, screenPress, "Press"); // reenter On to restart our timers
- powerFSM.add_transition(&stateSERIAL, &stateSERIAL, EVENT_PRESS, screenPress,
+ powerFSM.add_transition(&statePOWER, &statePOWER, EVENT_PRESS, NULL, "Press");
+ powerFSM.add_transition(&stateON, &stateON, EVENT_PRESS, NULL, "Press"); // reenter On to restart our timers
+ powerFSM.add_transition(&stateSERIAL, &stateSERIAL, EVENT_PRESS, NULL,
"Press"); // Allow button to work while in serial API
// Handle critically low power battery by forcing deep sleep
@@ -328,10 +319,10 @@ void PowerFSM_setup()
// if any packet destined for phone arrives, turn on bluetooth at least
powerFSM.add_transition(&stateNB, &stateDARK, EVENT_PACKET_FOR_PHONE, NULL, "Packet for phone");
- // show the latest node when we get a new node db update
- powerFSM.add_transition(&stateNB, &stateON, EVENT_NODEDB_UPDATED, NULL, "NodeDB update");
- powerFSM.add_transition(&stateDARK, &stateON, EVENT_NODEDB_UPDATED, NULL, "NodeDB update");
- powerFSM.add_transition(&stateON, &stateON, EVENT_NODEDB_UPDATED, NULL, "NodeDB update");
+ // Removed 2.7: we don't show the nodes individually for every node on the screen anymore
+ // powerFSM.add_transition(&stateNB, &stateON, EVENT_NODEDB_UPDATED, NULL, "NodeDB update");
+ // powerFSM.add_transition(&stateDARK, &stateON, EVENT_NODEDB_UPDATED, NULL, "NodeDB update");
+ // powerFSM.add_transition(&stateON, &stateON, EVENT_NODEDB_UPDATED, NULL, "NodeDB update");
// Show the received text message
powerFSM.add_transition(&stateLS, &stateON, EVENT_RECEIVED_MSG, NULL, "Received text");
diff --git a/src/PowerFSM.h b/src/PowerFSM.h
index 13dfdc4cc..6330a5fc6 100644
--- a/src/PowerFSM.h
+++ b/src/PowerFSM.h
@@ -11,7 +11,7 @@
#define EVENT_RECEIVED_MSG 5
// #define EVENT_BOOT 6 // now done with a timed transition
#define EVENT_BLUETOOTH_PAIR 7
-#define EVENT_NODEDB_UPDATED 8 // NodeDB has a big enough change that we think you should turn on the screen
+// #define EVENT_NODEDB_UPDATED 8 // Now defunct: NodeDB has a big enough change that we think you should turn on the screen
#define EVENT_CONTACT_FROM_PHONE 9 // the phone just talked to us over bluetooth
#define EVENT_LOW_BATTERY 10 // Battery is critically low, go to sleep
#define EVENT_SERIAL_CONNECTED 11
@@ -22,7 +22,7 @@
#define EVENT_SHUTDOWN 16 // force a full shutdown now (not just sleep)
#define EVENT_INPUT 17 // input broker wants something, we need to wake up and enable screen
-#if EXCLUDE_POWER_FSM
+#if MESHTASTIC_EXCLUDE_POWER_FSM
class FakeFsm
{
public:
diff --git a/src/PowerFSMThread.h b/src/PowerFSMThread.h
index c842f4515..135f53298 100644
--- a/src/PowerFSMThread.h
+++ b/src/PowerFSMThread.h
@@ -18,7 +18,7 @@ class PowerFSMThread : public OSThread
protected:
int32_t runOnce() override
{
-#if !EXCLUDE_POWER_FSM
+#if !MESHTASTIC_EXCLUDE_POWER_FSM
powerFSM.run_machine();
/// If we are in power state we force the CPU to wake every 10ms to check for serial characters (we don't yet wake
diff --git a/src/RedirectablePrint.cpp b/src/RedirectablePrint.cpp
index 07f873864..7c8d77651 100644
--- a/src/RedirectablePrint.cpp
+++ b/src/RedirectablePrint.cpp
@@ -352,8 +352,8 @@ void RedirectablePrint::hexDump(const char *logLevel, unsigned char *buf, uint16
for (uint16_t i = 0; i < len; i += 16) {
if (i % 128 == 0)
log(logLevel, " +------------------------------------------------+ +----------------+");
- char s[] = "| | | |\n";
- uint8_t ix = 1, iy = 52;
+ char s[] = " | | | |\n";
+ uint8_t ix = 5, iy = 56;
for (uint8_t j = 0; j < 16; j++) {
if (i + j < len) {
uint8_t c = buf[i + j];
@@ -367,10 +367,8 @@ void RedirectablePrint::hexDump(const char *logLevel, unsigned char *buf, uint16
}
}
uint8_t index = i / 16;
- if (i < 256)
- log(logLevel, " ");
- log(logLevel, "%02x", index);
- log(logLevel, ".");
+ sprintf(s, "%03x", index);
+ s[3] = '.';
log(logLevel, s);
}
log(logLevel, " +------------------------------------------------+ +----------------+");
@@ -393,4 +391,4 @@ std::string RedirectablePrint::mt_sprintf(const std::string fmt_str, ...)
break;
}
return std::string(formatted.get());
-}
\ No newline at end of file
+}
diff --git a/src/buzz/BuzzerFeedbackThread.cpp b/src/buzz/BuzzerFeedbackThread.cpp
new file mode 100644
index 000000000..2bd3158a3
--- /dev/null
+++ b/src/buzz/BuzzerFeedbackThread.cpp
@@ -0,0 +1,79 @@
+#include "BuzzerFeedbackThread.h"
+#include "NodeDB.h"
+#include "buzz.h"
+#include "configuration.h"
+
+BuzzerFeedbackThread *buzzerFeedbackThread;
+
+BuzzerFeedbackThread::BuzzerFeedbackThread() : OSThread("BuzzerFeedback")
+{
+ if (inputBroker)
+ inputObserver.observe(inputBroker);
+}
+
+int BuzzerFeedbackThread::handleInputEvent(const InputEvent *event)
+{
+ // Only provide feedback if buzzer is enabled for notifications
+ if (config.device.buzzer_mode == meshtastic_Config_DeviceConfig_BuzzerMode_DISABLED ||
+ config.device.buzzer_mode == meshtastic_Config_DeviceConfig_BuzzerMode_NOTIFICATIONS_ONLY) {
+ return 0; // Let other handlers process the event
+ }
+
+ // Track last event time for potential future use
+ lastEventTime = millis();
+ needsUpdate = true;
+
+ // Handle different input events with appropriate buzzer feedback
+ switch (event->inputEvent) {
+ case INPUT_BROKER_USER_PRESS:
+ case INPUT_BROKER_ALT_PRESS:
+ case INPUT_BROKER_SELECT:
+ playBeep(); // Confirmation feedback
+ break;
+
+ case INPUT_BROKER_UP:
+ case INPUT_BROKER_DOWN:
+ case INPUT_BROKER_LEFT:
+ case INPUT_BROKER_RIGHT:
+ playChirp(); // Navigation feedback
+ break;
+
+ case INPUT_BROKER_CANCEL:
+ case INPUT_BROKER_BACK:
+ playBoop(); // Cancel/back feedback
+ break;
+
+ case INPUT_BROKER_SEND_PING:
+ playComboTune(); // Ping sent feedback
+ break;
+
+ case INPUT_BROKER_SHUTDOWN:
+ playShutdownMelody(); // Shutdown feedback
+ break;
+
+ default:
+ // For other events, check if it's a printable character
+ if (event->kbchar >= 32 && event->kbchar <= 126) {
+ // Typing feedback - very short boop
+ // Removing this for now, too chatty
+ // playChirp();
+ }
+ break;
+ }
+
+ return 0; // Allow other handlers to process the event
+}
+
+int32_t BuzzerFeedbackThread::runOnce()
+{
+ // This thread is primarily event-driven, but we can use runOnce
+ // for any periodic tasks if needed in the future
+
+ if (needsUpdate) {
+ needsUpdate = false;
+ // Could add any periodic processing here
+ }
+
+ // Run every 100ms when active, less frequently when idle
+ return needsUpdate ? 100 : 1000;
+}
diff --git a/src/buzz/BuzzerFeedbackThread.h b/src/buzz/BuzzerFeedbackThread.h
new file mode 100644
index 000000000..dedea9860
--- /dev/null
+++ b/src/buzz/BuzzerFeedbackThread.h
@@ -0,0 +1,24 @@
+#pragma once
+
+#include "Observer.h"
+#include "concurrency/OSThread.h"
+#include "input/InputBroker.h"
+
+class BuzzerFeedbackThread : public concurrency::OSThread
+{
+ CallbackObserver inputObserver =
+ CallbackObserver(this, &BuzzerFeedbackThread::handleInputEvent);
+
+ public:
+ BuzzerFeedbackThread();
+ int handleInputEvent(const InputEvent *event);
+
+ protected:
+ virtual int32_t runOnce() override;
+
+ private:
+ uint32_t lastEventTime = 0;
+ bool needsUpdate = false;
+};
+
+extern BuzzerFeedbackThread *buzzerFeedbackThread;
diff --git a/src/buzz/buzz.cpp b/src/buzz/buzz.cpp
index 6ba2f4140..b09d7a82c 100644
--- a/src/buzz/buzz.cpp
+++ b/src/buzz/buzz.cpp
@@ -38,6 +38,11 @@ const int DURATION_1_1 = 1000; // 1/1 note
void playTones(const ToneDuration *tone_durations, int size)
{
+ if (config.device.buzzer_mode == meshtastic_Config_DeviceConfig_BuzzerMode_DISABLED ||
+ config.device.buzzer_mode == meshtastic_Config_DeviceConfig_BuzzerMode_NOTIFICATIONS_ONLY) {
+ // Buzzer is disabled or not set to system tones
+ return;
+ }
#ifdef PIN_BUZZER
if (!config.device.buzzer_gpio)
config.device.buzzer_gpio = PIN_BUZZER;
@@ -54,7 +59,7 @@ void playTones(const ToneDuration *tone_durations, int size)
void playBeep()
{
- ToneDuration melody[] = {{NOTE_B3, DURATION_1_4}};
+ ToneDuration melody[] = {{NOTE_B3, DURATION_1_8}};
playTones(melody, sizeof(melody) / sizeof(ToneDuration));
}
@@ -87,3 +92,72 @@ void playShutdownMelody()
ToneDuration melody[] = {{NOTE_CS4, DURATION_1_8}, {NOTE_AS3, DURATION_1_8}, {NOTE_FS3, DURATION_1_4}};
playTones(melody, sizeof(melody) / sizeof(ToneDuration));
}
+
+void playChirp()
+{
+ // A short, friendly "chirp" sound for key presses
+ ToneDuration melody[] = {{NOTE_AS3, 20}}; // Very short AS3 note
+ playTones(melody, sizeof(melody) / sizeof(ToneDuration));
+}
+
+void playBoop()
+{
+ // A short, friendly "boop" sound for button presses
+ ToneDuration melody[] = {{NOTE_A3, 50}}; // Very short A3 note
+ playTones(melody, sizeof(melody) / sizeof(ToneDuration));
+}
+
+void playLongPressLeadUp()
+{
+ // An ascending lead-up sequence for long press - builds anticipation
+ ToneDuration melody[] = {
+ {NOTE_C3, 100}, // Start low
+ {NOTE_E3, 100}, // Step up
+ {NOTE_G3, 100}, // Keep climbing
+ {NOTE_B3, 150} // Peak with longer note for emphasis
+ };
+ playTones(melody, sizeof(melody) / sizeof(ToneDuration));
+}
+
+// Static state for progressive lead-up notes
+static int leadUpNoteIndex = 0;
+static const ToneDuration leadUpNotes[] = {
+ {NOTE_C3, 100}, // Start low
+ {NOTE_E3, 100}, // Step up
+ {NOTE_G3, 100}, // Keep climbing
+ {NOTE_B3, 150} // Peak with longer note for emphasis
+};
+static const int leadUpNotesCount = sizeof(leadUpNotes) / sizeof(ToneDuration);
+
+bool playNextLeadUpNote()
+{
+ if (leadUpNoteIndex >= leadUpNotesCount) {
+ return false; // All notes have been played
+ }
+
+ // Use playTones to handle buzzer logic consistently
+ const auto ¬e = leadUpNotes[leadUpNoteIndex];
+ playTones(¬e, 1); // Play single note using existing playTones function
+
+ leadUpNoteIndex++;
+ return true; // Note was played (playTones handles buzzer availability internally)
+}
+
+void resetLeadUpSequence()
+{
+ leadUpNoteIndex = 0;
+}
+
+void playComboTune()
+{
+ // Quick high-pitched notes with trills
+ ToneDuration melody[] = {
+ {NOTE_G3, 80}, // Quick chirp
+ {NOTE_B3, 60}, // Higher chirp
+ {NOTE_CS4, 80}, // Even higher
+ {NOTE_G3, 60}, // Quick trill down
+ {NOTE_CS4, 60}, // Quick trill up
+ {NOTE_B3, 120} // Ending chirp
+ };
+ playTones(melody, sizeof(melody) / sizeof(ToneDuration));
+}
diff --git a/src/buzz/buzz.h b/src/buzz/buzz.h
index adeaca73d..c25a54a5b 100644
--- a/src/buzz/buzz.h
+++ b/src/buzz/buzz.h
@@ -5,4 +5,10 @@ void playLongBeep();
void playStartMelody();
void playShutdownMelody();
void playGPSEnableBeep();
-void playGPSDisableBeep();
\ No newline at end of file
+void playGPSDisableBeep();
+void playComboTune();
+void playBoop();
+void playChirp();
+void playLongPressLeadUp();
+bool playNextLeadUpNote(); // Play the next note in the lead-up sequence
+void resetLeadUpSequence(); // Reset the lead-up sequence to start from beginning
\ No newline at end of file
diff --git a/src/commands.h b/src/commands.h
index f2b783010..603003e5c 100644
--- a/src/commands.h
+++ b/src/commands.h
@@ -12,7 +12,7 @@ enum class Cmd {
STOP_ALERT_FRAME,
START_FIRMWARE_UPDATE_SCREEN,
STOP_BOOT_SCREEN,
- PRINT,
SHOW_PREV_FRAME,
- SHOW_NEXT_FRAME
+ SHOW_NEXT_FRAME,
+ NOOP
};
\ No newline at end of file
diff --git a/src/concurrency/Lock.cpp b/src/concurrency/Lock.cpp
index 11501359b..0fe80e455 100644
--- a/src/concurrency/Lock.cpp
+++ b/src/concurrency/Lock.cpp
@@ -9,17 +9,23 @@ namespace concurrency
Lock::Lock() : handle(xSemaphoreCreateBinary())
{
assert(handle);
- assert(xSemaphoreGive(handle));
+ if (xSemaphoreGive(handle) == false) {
+ abort();
+ }
}
void Lock::lock()
{
- assert(xSemaphoreTake(handle, portMAX_DELAY));
+ if (xSemaphoreTake(handle, portMAX_DELAY) == false) {
+ abort();
+ }
}
void Lock::unlock()
{
- assert(xSemaphoreGive(handle));
+ if (xSemaphoreGive(handle) == false) {
+ abort();
+ }
}
#else
Lock::Lock() {}
diff --git a/src/configuration.h b/src/configuration.h
index ab2143a03..f0c5132b0 100644
--- a/src/configuration.h
+++ b/src/configuration.h
@@ -81,7 +81,43 @@ along with this program. If not, see .
// #define REGULATORY_LORA_REGIONCODE meshtastic_Config_LoRaConfig_RegionCode_SG_923
// Total system gain in dBm to subtract from Tx power to remain within regulatory and Tx PA limits
-// This value should be set in variant.h and is PA gain + antenna gain (if variant has a non-removable antenna)
+// The value consists of PA gain + antenna gain (if variant has a non-removable antenna)
+// TX_GAIN_LORA should be set with definitions below for common modules, or in variant.h.
+
+// Gain for common modules with transmit PAs
+#ifdef EBYTE_E22_900M30S
+// 10dB PA gain and 30dB rated output; based on measurements from
+// https://github.com/S5NC/EBYTE_ESP32-S3/blob/main/E22-900M30S%20power%20output%20testing.txt
+#define TX_GAIN_LORA 7
+#define SX126X_MAX_POWER 22
+#endif
+
+#ifdef EBYTE_E22_900M33S
+// 25dB PA gain and 33dB rated output; based on TX Power Curve from E22-900M33S_UserManual_EN_v1.0.pdf
+#define TX_GAIN_LORA 25
+#define SX126X_MAX_POWER 8
+#endif
+
+#ifdef NICERF_MINIF27
+// Note that datasheet power level of 9 corresponds with SX1262 at 22dBm
+// Maximum output power of 29dBm with VCC_PA = 5V
+#define TX_GAIN_LORA 7
+#define SX126X_MAX_POWER 22
+#endif
+
+#ifdef NICERF_F30_HF
+// Maximum output power of 29.6dBm with VCC = 5V and SX1262 at 22dBm
+#define TX_GAIN_LORA 8
+#define SX126X_MAX_POWER 22
+#endif
+
+#ifdef NICERF_F30_LF
+// Maximum output power of 32.0dBm with VCC = 5V and SX1262 at 22dBm
+#define TX_GAIN_LORA 10
+#define SX126X_MAX_POWER 22
+#endif
+
+// Default system gain to 0 if not defined
#ifndef TX_GAIN_LORA
#define TX_GAIN_LORA 0
#endif
@@ -154,6 +190,7 @@ along with this program. If not, see .
#define DFROBOT_RAIN_ADDR 0x1d
#define NAU7802_ADDR 0x2A
#define MAX30102_ADDR 0x57
+#define SCD4X_ADDR 0x62
#define MLX90614_ADDR_DEF 0x5A
#define CGRADSENS_ADDR 0x66
#define LTR390UV_ADDR 0x53
@@ -172,6 +209,7 @@ along with this program. If not, see .
#define BMX160_ADDR 0x69
#define ICM20948_ADDR 0x69
#define ICM20948_ADDR_ALT 0x68
+#define BMM150_ADDR 0x13
// -----------------------------------------------------------------------------
// LED
@@ -194,6 +232,15 @@ along with this program. If not, see .
// -----------------------------------------------------------------------------
#define FT6336U_ADDR 0x48
+// -----------------------------------------------------------------------------
+// RAK12035VB Soil Monitor (using RAK12023 up to 3 RAK12035 monitors can be connected)
+// - the default i2c address for this sensor is 0x20, and users are instructed to
+// set 0x21 and 0x22 for the second and third sensor if present.
+// -----------------------------------------------------------------------------
+#define RAK120351_ADDR 0x20
+#define RAK120352_ADDR 0x21
+#define RAK120353_ADDR 0x22
+
// -----------------------------------------------------------------------------
// BIAS-T Generator
// -----------------------------------------------------------------------------
@@ -303,11 +350,41 @@ along with this program. If not, see .
#error HW_VENDOR must be defined
#endif
+#ifndef TB_DOWN
+#define TB_DOWN 255
+#endif
+#ifndef TB_UP
+#define TB_UP 255
+#endif
+#ifndef TB_LEFT
+#define TB_LEFT 255
+#endif
+#ifndef TB_RIGHT
+#define TB_RIGHT 255
+#endif
+#ifndef TB_PRESS
+#define TB_PRESS 255
+#endif
+
// Support multiple RGB LED configuration
#if defined(HAS_NCP5623) || defined(HAS_LP5562) || defined(RGBLED_RED) || defined(HAS_NEOPIXEL) || defined(UNPHONE)
#define HAS_RGB_LED
#endif
+// default mapping of pins
+#if defined(PIN_BUTTON2) && !defined(CANCEL_BUTTON_PIN)
+#define ALT_BUTTON_PIN PIN_BUTTON2
+#endif
+#if defined ALT_BUTTON_PIN
+
+#ifndef ALT_BUTTON_ACTIVE_LOW
+#define ALT_BUTTON_ACTIVE_LOW true
+#endif
+#ifndef ALT_BUTTON_ACTIVE_PULLUP
+#define ALT_BUTTON_ACTIVE_PULLUP true
+#endif
+#endif
+
// -----------------------------------------------------------------------------
// Global switches to turn off features for a minimized build
// -----------------------------------------------------------------------------
diff --git a/src/detect/ScanI2C.cpp b/src/detect/ScanI2C.cpp
index 881b75405..3f73107ad 100644
--- a/src/detect/ScanI2C.cpp
+++ b/src/detect/ScanI2C.cpp
@@ -37,8 +37,14 @@ ScanI2C::FoundDevice ScanI2C::firstKeyboard() const
ScanI2C::FoundDevice ScanI2C::firstAccelerometer() const
{
- ScanI2C::DeviceType types[] = {MPU6050, LIS3DH, BMA423, LSM6DS3, BMX160, STK8BAXX, ICM20948, QMA6100P};
- return firstOfOrNONE(8, types);
+ ScanI2C::DeviceType types[] = {MPU6050, LIS3DH, BMA423, LSM6DS3, BMX160, STK8BAXX, ICM20948, QMA6100P, BMM150};
+ return firstOfOrNONE(9, types);
+}
+
+ScanI2C::FoundDevice ScanI2C::firstAQI() const
+{
+ ScanI2C::DeviceType types[] = {PMSA0031, SCD4X};
+ return firstOfOrNONE(2, types);
}
ScanI2C::FoundDevice ScanI2C::firstRGBLED() const
@@ -80,4 +86,4 @@ bool ScanI2C::DeviceAddress::operator<(const ScanI2C::DeviceAddress &other) cons
|| (port != NO_I2C && other.port != NO_I2C && (address < other.address));
}
-ScanI2C::FoundDevice::FoundDevice(ScanI2C::DeviceType type, ScanI2C::DeviceAddress address) : type(type), address(address) {}
\ No newline at end of file
+ScanI2C::FoundDevice::FoundDevice(ScanI2C::DeviceType type, ScanI2C::DeviceAddress address) : type(type), address(address) {}
diff --git a/src/detect/ScanI2C.h b/src/detect/ScanI2C.h
index 72b2bda81..81d2510db 100644
--- a/src/detect/ScanI2C.h
+++ b/src/detect/ScanI2C.h
@@ -62,6 +62,7 @@ class ScanI2C
FT6336U,
STK8BAXX,
ICM20948,
+ SCD4X,
MAX30102,
TPS65233,
MPR121KB,
@@ -71,8 +72,10 @@ class ScanI2C
DFROBOT_RAIN,
DPS310,
LTR390UV,
+ RAK12035,
TCA8418KB,
PCT2075,
+ BMM150,
} DeviceType;
// typedef uint8_t DeviceAddress;
@@ -125,6 +128,8 @@ class ScanI2C
FoundDevice firstAccelerometer() const;
+ FoundDevice firstAQI() const;
+
FoundDevice firstRGBLED() const;
virtual FoundDevice find(DeviceType) const;
diff --git a/src/detect/ScanI2CTwoWire.cpp b/src/detect/ScanI2CTwoWire.cpp
index f8d9ae907..1c26d4c23 100644
--- a/src/detect/ScanI2CTwoWire.cpp
+++ b/src/detect/ScanI2CTwoWire.cpp
@@ -361,7 +361,7 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
case SHT31_4x_ADDR: // same as OPT3001_ADDR_ALT
case SHT31_4x_ADDR_ALT: // same as OPT3001_ADDR
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x89), 2);
- if (registerValue == 0x11a2 || registerValue == 0x11da || registerValue == 0xe9c) {
+ if (registerValue == 0x11a2 || registerValue == 0x11da || registerValue == 0xe9c || registerValue == 0xc8d) {
type = SHT4X;
logFoundDevice("SHT4X", (uint8_t)addr.address);
} else if (getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x7E), 2) == 0x5449) {
@@ -426,9 +426,21 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
logFoundDevice("BMA423", (uint8_t)addr.address);
}
break;
+ case TCA9535_ADDR:
+ case RAK120352_ADDR:
+ case RAK120353_ADDR:
+ registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x02), 1);
+ if (registerValue == addr.address) { // RAK12035 returns its I2C address at 0x02 (eg 0x20)
+ type = RAK12035;
+ logFoundDevice("RAK12035", (uint8_t)addr.address);
+ } else {
+ type = TCA9535;
+ logFoundDevice("TCA9535", (uint8_t)addr.address);
+ }
+
+ break;
SCAN_SIMPLE_CASE(LSM6DS3_ADDR, LSM6DS3, "LSM6DS3", (uint8_t)addr.address);
- SCAN_SIMPLE_CASE(TCA9535_ADDR, TCA9535, "TCA9535", (uint8_t)addr.address);
SCAN_SIMPLE_CASE(TCA9555_ADDR, TCA9555, "TCA9555", (uint8_t)addr.address);
SCAN_SIMPLE_CASE(VEML7700_ADDR, VEML7700, "VEML7700", (uint8_t)addr.address);
SCAN_SIMPLE_CASE(TSL25911_ADDR, TSL2591, "TSL2591", (uint8_t)addr.address);
@@ -438,6 +450,8 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
SCAN_SIMPLE_CASE(DFROBOT_RAIN_ADDR, DFROBOT_RAIN, "DFRobot Rain Gauge", (uint8_t)addr.address);
SCAN_SIMPLE_CASE(LTR390UV_ADDR, LTR390UV, "LTR390UV", (uint8_t)addr.address);
SCAN_SIMPLE_CASE(PCT2075_ADDR, PCT2075, "PCT2075", (uint8_t)addr.address);
+ SCAN_SIMPLE_CASE(SCD4X_ADDR, SCD4X, "SCD4X", (uint8_t)addr.address);
+ SCAN_SIMPLE_CASE(BMM150_ADDR, BMM150, "BMM150", (uint8_t)addr.address);
#ifdef HAS_TPS65233
SCAN_SIMPLE_CASE(TPS65233_ADDR, TPS65233, "TPS65233", (uint8_t)addr.address);
#endif
@@ -546,4 +560,4 @@ void ScanI2CTwoWire::logFoundDevice(const char *device, uint8_t address)
{
LOG_INFO("%s found at address 0x%x", device, address);
}
-#endif
\ No newline at end of file
+#endif
diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp
index 142241c43..345c738d6 100644
--- a/src/gps/GPS.cpp
+++ b/src/gps/GPS.cpp
@@ -1536,7 +1536,10 @@ The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of s
if (t.tm_mon > -1) {
LOG_DEBUG("NMEA GPS time %02d-%02d-%02d %02d:%02d:%02d age %d", d.year(), d.month(), t.tm_mday, t.tm_hour, t.tm_min,
t.tm_sec, ti.age());
- perhapsSetRTC(RTCQualityGPS, t);
+ if (perhapsSetRTC(RTCQualityGPS, t) == RTCSetResultInvalidTime) {
+ // Clear the GPS buffer if we got an invalid time
+ clearBuffer();
+ }
return true;
} else
return false;
diff --git a/src/gps/RTC.cpp b/src/gps/RTC.cpp
index af964eab5..5054be3f0 100644
--- a/src/gps/RTC.cpp
+++ b/src/gps/RTC.cpp
@@ -105,7 +105,7 @@ void readFromRTC()
*
* If we haven't yet set our RTC this boot, set it from a GPS derived time
*/
-bool perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpdate)
+RTCSetResult perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpdate)
{
static uint32_t lastSetMsec = 0;
uint32_t now = millis();
@@ -113,7 +113,7 @@ bool perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpdate)
#ifdef BUILD_EPOCH
if (tv->tv_sec < BUILD_EPOCH) {
LOG_WARN("Ignore time (%ld) before build epoch (%ld)!", printableEpoch, BUILD_EPOCH);
- return false;
+ return RTCSetResultInvalidTime;
}
#endif
@@ -184,9 +184,9 @@ bool perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpdate)
readFromRTC();
#endif
- return true;
+ return RTCSetResultSuccess;
} else {
- return false;
+ return RTCSetResultNotSet; // RTC was already set with a higher quality time
}
}
@@ -215,7 +215,7 @@ const char *RtcName(RTCQuality quality)
* @param t The time to potentially set the RTC to.
* @return True if the RTC was set to the provided time, false otherwise.
*/
-bool perhapsSetRTC(RTCQuality q, struct tm &t)
+RTCSetResult perhapsSetRTC(RTCQuality q, struct tm &t)
{
/* Convert to unix time
The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of seconds that have elapsed since January 1, 1970
@@ -231,7 +231,7 @@ bool perhapsSetRTC(RTCQuality q, struct tm &t)
// LOG_DEBUG("Got time from GPS month=%d, year=%d, unixtime=%ld", t.tm_mon, t.tm_year, tv.tv_sec);
if (t.tm_year < 0 || t.tm_year >= 300) {
// LOG_DEBUG("Ignore invalid GPS month=%d, year=%d, unixtime=%ld", t.tm_mon, t.tm_year, tv.tv_sec);
- return false;
+ return RTCSetResultInvalidTime;
} else {
return perhapsSetRTC(q, &tv);
}
@@ -244,11 +244,15 @@ bool perhapsSetRTC(RTCQuality q, struct tm &t)
*/
int32_t getTZOffset()
{
+#if MESHTASTIC_EXCLUDE_TZ
+ return 0;
+#else
time_t now = getTime(false);
struct tm *gmt;
gmt = gmtime(&now);
gmt->tm_isdst = -1;
return (int32_t)difftime(now, mktime(gmt));
+#endif
}
/**
diff --git a/src/gps/RTC.h b/src/gps/RTC.h
index caa48dc06..96dec575b 100644
--- a/src/gps/RTC.h
+++ b/src/gps/RTC.h
@@ -22,13 +22,22 @@ enum RTCQuality {
RTCQualityGPS = 4
};
+/// The RTC set result codes
+/// Used to indicate the result of an attempt to set the RTC.
+enum RTCSetResult {
+ RTCSetResultNotSet = 0, ///< RTC was set successfully
+ RTCSetResultSuccess = 1, ///< RTC was set successfully
+ RTCSetResultInvalidTime = 3, ///< The provided time was invalid (e.g., before the build epoch)
+ RTCSetResultError = 4 ///< An error occurred while setting the RTC
+};
+
RTCQuality getRTCQuality();
extern uint32_t lastSetFromPhoneNtpOrGps;
/// If we haven't yet set our RTC this boot, set it from a GPS derived time
-bool perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpdate = false);
-bool perhapsSetRTC(RTCQuality q, struct tm &t);
+RTCSetResult perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpdate = false);
+RTCSetResult perhapsSetRTC(RTCQuality q, struct tm &t);
/// Return a string name for the quality
const char *RtcName(RTCQuality quality);
diff --git a/src/graphics/EInkDisplay2.cpp b/src/graphics/EInkDisplay2.cpp
index 5a2749482..66c7938b5 100644
--- a/src/graphics/EInkDisplay2.cpp
+++ b/src/graphics/EInkDisplay2.cpp
@@ -6,6 +6,10 @@
#include "main.h"
#include
+#ifdef GXEPD2_DRIVER_0
+#include "einkDetect.h"
+#endif
+
/*
The macros EINK_DISPLAY_MODEL, EINK_WIDTH, and EINK_HEIGHT are defined as build_flags in a variant's platformio.ini
Previously, these macros were defined at the top of this file.
@@ -174,9 +178,8 @@ bool EInkDisplay::connect()
}
}
-#elif defined(HELTEC_WIRELESS_PAPER_V1_0) || defined(HELTEC_WIRELESS_PAPER) || defined(HELTEC_VISION_MASTER_E213) || \
- defined(HELTEC_VISION_MASTER_E290) || defined(TLORA_T3S3_EPAPER) || defined(CROWPANEL_ESP32S3_5_EPAPER) || \
- defined(CROWPANEL_ESP32S3_4_EPAPER) || defined(CROWPANEL_ESP32S3_2_EPAPER)
+#elif defined(HELTEC_WIRELESS_PAPER_V1_0) || defined(HELTEC_VISION_MASTER_E290) || defined(TLORA_T3S3_EPAPER) || \
+ defined(CROWPANEL_ESP32S3_5_EPAPER) || defined(CROWPANEL_ESP32S3_4_EPAPER) || defined(CROWPANEL_ESP32S3_2_EPAPER)
{
// Start HSPI
hspi = new SPIClass(HSPI);
@@ -232,6 +235,23 @@ bool EInkDisplay::connect()
adafruitDisplay->init();
adafruitDisplay->setRotation(3);
}
+#elif defined(HELTEC_WIRELESS_PAPER) || defined(HELTEC_VISION_MASTER_E213)
+
+ // Detect display model, before starting SPI
+ EInkDetectionResult displayModel = detectEInk();
+
+ // Start HSPI
+ hspi = new SPIClass(HSPI);
+ hspi->begin(PIN_EINK_SCLK, -1, PIN_EINK_MOSI, PIN_EINK_CS); // SCLK, MISO, MOSI, SS
+
+ // Create GxEPD2 object
+ adafruitDisplay = new GxEPD2_Multi((uint8_t)displayModel, PIN_EINK_CS, PIN_EINK_DC,
+ PIN_EINK_RES, PIN_EINK_BUSY, *hspi);
+
+ // Init GxEPD2
+ adafruitDisplay->init();
+ adafruitDisplay->setRotation(3);
+
#endif
return true;
diff --git a/src/graphics/EInkDisplay2.h b/src/graphics/EInkDisplay2.h
index 93be197b0..284337627 100644
--- a/src/graphics/EInkDisplay2.h
+++ b/src/graphics/EInkDisplay2.h
@@ -5,6 +5,10 @@
#include "GxEPD2_BW.h"
#include
+#ifdef GXEPD2_DRIVER_0 // If variant has multiple possible display models
+#include "GxEPD2Multi.h"
+#endif
+
/**
* An adapter class that allows using the GxEPD2 library as if it was an OLEDDisplay implementation.
*
@@ -63,8 +67,15 @@ class EInkDisplay : public OLEDDisplay
// Connect to the display
virtual bool connect() override;
- // AdafruitGFX display object - instantiated in connect(), variant specific
+#ifdef GXEPD2_DRIVER_0
+ // AdafruitGFX display object - wrapper for multiple drivers
+ // Allows runtime detection of multiple displays
+ // Avoid this situation if possible!
+ GxEPD2_Multi *adafruitDisplay = NULL;
+#else
+ // AdafruitGFX display object (for single display model) - instantiated in connect(), variant specific
GxEPD2_BW *adafruitDisplay = NULL;
+#endif
// If display uses HSPI
#if defined(HELTEC_WIRELESS_PAPER) || defined(HELTEC_WIRELESS_PAPER_V1_0) || defined(HELTEC_VISION_MASTER_E213) || \
diff --git a/src/graphics/GxEPD2Multi.h b/src/graphics/GxEPD2Multi.h
new file mode 100644
index 000000000..f3807c9de
--- /dev/null
+++ b/src/graphics/GxEPD2Multi.h
@@ -0,0 +1,135 @@
+// Wrapper class for GxEPD2_BW
+
+// Generic signature at build-time, so that we can detect display model at run-time
+// Workaround for issue of GxEPD2_BW objects not having a shared base class
+// Only exposes methods which we are actually using
+
+template class GxEPD2_Multi
+{
+ public:
+ void drawPixel(int16_t x, int16_t y, uint16_t color)
+ {
+ if (which == 0)
+ driver0->drawPixel(x, y, color);
+ else
+ driver1->drawPixel(x, y, color);
+ }
+
+ bool nextPage()
+ {
+ if (which == 0)
+ return driver0->nextPage();
+ else
+ return driver1->nextPage();
+ }
+
+ void hibernate()
+ {
+ if (which == 0)
+ driver0->hibernate();
+ else
+ driver1->hibernate();
+ }
+
+ void init(uint32_t serial_diag_bitrate = 0)
+ {
+ if (which == 0)
+ driver0->init(serial_diag_bitrate);
+ else
+ driver1->init(serial_diag_bitrate);
+ }
+
+ void init(uint32_t serial_diag_bitrate, bool initial, uint16_t reset_duration = 20, bool pulldown_rst_mode = false)
+ {
+ if (which == 0)
+ driver0->init(serial_diag_bitrate, initial, reset_duration, pulldown_rst_mode);
+ else
+ driver1->init(serial_diag_bitrate, initial, reset_duration, pulldown_rst_mode);
+ }
+
+ void setRotation(uint8_t x)
+ {
+ if (which == 0)
+ driver0->setRotation(x);
+ else
+ driver1->setRotation(x);
+ }
+
+ void setPartialWindow(uint16_t x, uint16_t y, uint16_t w, uint16_t h)
+ {
+ if (which == 0)
+ driver0->setPartialWindow(x, y, w, h);
+ else
+ driver1->setPartialWindow(x, y, w, h);
+ }
+
+ void setFullWindow()
+ {
+ if (which == 0)
+ driver0->setFullWindow();
+ else
+ driver1->setFullWindow();
+ }
+
+ int16_t width()
+ {
+ if (which == 0)
+ return driver0->width();
+ else
+ return driver1->width();
+ }
+
+ int16_t height()
+ {
+ if (which == 0)
+ return driver0->height();
+ else
+ return driver1->height();
+ }
+
+ void clearScreen(uint8_t value = 0xFF)
+ {
+ if (which == 0)
+ driver0->clearScreen();
+ else
+ driver1->clearScreen();
+ }
+
+ void endAsyncFull()
+ {
+ if (which == 0)
+ driver0->endAsyncFull();
+ else
+ driver1->endAsyncFull();
+ }
+
+ // Exposes methods of the GxEPD2_EPD object which is usually available as GxEPD2_BW::epd
+ class Epd2Wrapper
+ {
+ public:
+ bool isBusy() { return m_epd2->isBusy(); }
+ GxEPD2_EPD *m_epd2;
+ } epd2;
+
+ // Constructor
+ // Select driver by passing whichDriver as 0 or 1
+ GxEPD2_Multi(uint8_t whichDriver, int16_t cs, int16_t dc, int16_t rst, int16_t busy, SPIClass &spi)
+ {
+ assert(whichDriver == 0 || whichDriver == 1);
+ which = whichDriver;
+ LOG_DEBUG("GxEPD2_Multi driver: %d", which);
+
+ if (which == 0) {
+ driver0 = new GxEPD2_BW(Driver0(cs, dc, rst, busy, spi));
+ epd2.m_epd2 = &(driver0->epd2);
+ } else if (which == 1) {
+ driver1 = new GxEPD2_BW(Driver1(cs, dc, rst, busy, spi));
+ epd2.m_epd2 = &(driver1->epd2);
+ }
+ }
+
+ private:
+ uint8_t which;
+ GxEPD2_BW *driver0;
+ GxEPD2_BW *driver1;
+};
\ No newline at end of file
diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp
index 61999ee79..1f2e7e4d9 100644
--- a/src/graphics/Screen.cpp
+++ b/src/graphics/Screen.cpp
@@ -1,9 +1,10 @@
/*
+BaseUI
-SSD1306 - Screen module
-
-Copyright (C) 2018 by Xose Pรฉrez
-
+Developed and Maintained By:
+- Ronald Garcia (HarukiToreda) โ Lead development and implementation.
+- JasonP (Xaositek) โ Screen layout and icon design, UI improvements and testing.
+- TonyG (Tropho) โ Project management, structural planning, and testing
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -20,6 +21,7 @@ along with this program. If not, see .
*/
#include "Screen.h"
+#include "NodeDB.h"
#include "PowerMon.h"
#include "Throttle.h"
#include "configuration.h"
@@ -27,38 +29,53 @@ along with this program. If not, see .
#include
#include "DisplayFormatters.h"
+#include "TimeFormatters.h"
+#include "draw/ClockRenderer.h"
+#include "draw/DebugRenderer.h"
+#include "draw/MenuHandler.h"
+#include "draw/MessageRenderer.h"
+#include "draw/NodeListRenderer.h"
+#include "draw/NotificationRenderer.h"
+#include "draw/UIRenderer.h"
+#include "modules/CannedMessageModule.h"
+
#if !MESHTASTIC_EXCLUDE_GPS
#include "GPS.h"
+#include "buzz.h"
#endif
-#include "ButtonThread.h"
+#include "FSCommon.h"
#include "MeshService.h"
-#include "NodeDB.h"
+#include "RadioLibInterface.h"
#include "error.h"
#include "gps/GeoCoord.h"
#include "gps/RTC.h"
#include "graphics/ScreenFonts.h"
+#include "graphics/SharedUIDisplay.h"
+#include "graphics/emotes.h"
#include "graphics/images.h"
-#include "input/ScanAndSelect.h"
#include "input/TouchScreenImpl1.h"
#include "main.h"
#include "mesh-pb-constants.h"
#include "mesh/Channels.h"
#include "mesh/generated/meshtastic/deviceonly.pb.h"
#include "meshUtils.h"
-#include "modules/AdminModule.h"
#include "modules/ExternalNotificationModule.h"
#include "modules/TextMessageModule.h"
#include "modules/WaypointModule.h"
#include "sleep.h"
#include "target_specific.h"
+using graphics::Emote;
+using graphics::emotes;
+using graphics::numEmotes;
+
+extern uint16_t TFT_MESH;
+
#if HAS_WIFI && !defined(ARCH_PORTDUINO)
#include "mesh/wifi/WiFiAPClient.h"
#endif
#ifdef ARCH_ESP32
-#include "esp_task_wdt.h"
-#include "modules/StoreForwardModule.h"
#endif
#if ARCH_PORTDUINO
@@ -82,14 +99,10 @@ namespace graphics
// A text message frame + debug frame + all the node infos
FrameCallback *normalFrames;
static uint32_t targetFramerate = IDLE_FRAMERATE;
+// Global variables for alert banner - explicitly define with extern "C" linkage to prevent optimization
uint32_t logo_timeout = 5000; // 4 seconds for EACH logo
-uint32_t hours_in_month = 730;
-
-// This image definition is here instead of images.h because it's modified dynamically by the drawBattery function
-uint8_t imgBattery[16] = {0xFF, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0xE7, 0x3C};
-
// Threshold values for the GPS lock accuracy bar display
uint32_t dopThresholds[5] = {2000, 1000, 500, 200, 100};
@@ -97,13 +110,9 @@ uint32_t dopThresholds[5] = {2000, 1000, 500, 200, 100};
// we'll need to hold onto pointers for the modules that can draw a frame.
std::vector moduleFrames;
-// Stores the last 4 of our hardware ID, to make finding the device for pairing easier
-static char ourId[5];
-
-// vector where symbols (string) are displayed in bottom corner of display.
+// Global variables for screen function overlay symbols
std::vector functionSymbol;
-// string displayed in bottom right corner of display. Created from elements in functionSymbol vector
-std::string functionSymbolString = "";
+std::string functionSymbolString;
#if HAS_GPS
// GeoCoord object for the screen
@@ -114,257 +123,98 @@ GeoCoord geoCoord;
static bool heartbeat = false;
#endif
-// Quick access to screen dimensions from static drawing functions
-// DEPRECATED. To-do: move static functions inside Screen class
-#define SCREEN_WIDTH display->getWidth()
-#define SCREEN_HEIGHT display->getHeight()
-
#include "graphics/ScreenFonts.h"
#include
-#define getStringCenteredX(s) ((SCREEN_WIDTH - display->getStringWidth(s)) / 2)
+// Usage: int stringWidth = formatDateTime(datetimeStr, sizeof(datetimeStr), rtc_sec, display);
+// End Functions to write date/time to the screen
-// Check if the display can render a string (detect special chars; emoji)
-static bool haveGlyphs(const char *str)
+extern bool hasUnreadMessage;
+
+// ==============================
+// Overlay Alert Banner Renderer
+// ==============================
+// Displays a temporary centered banner message (e.g., warning, status, etc.)
+// The banner appears in the center of the screen and disappears after the specified duration
+
+void Screen::showSimpleBanner(const char *message, uint32_t durationMs)
{
-#if defined(OLED_PL) || defined(OLED_UA) || defined(OLED_RU) || defined(OLED_CS)
- // Don't want to make any assumptions about custom language support
- return true;
-#endif
-
- // Check each character with the lookup function for the OLED library
- // We're not really meant to use this directly..
- bool have = true;
- for (uint16_t i = 0; i < strlen(str); i++) {
- uint8_t result = Screen::customFontTableLookup((uint8_t)str[i]);
- // If font doesn't support a character, it is substituted for ยฟ
- if (result == 191 && (uint8_t)str[i] != 191) {
- have = false;
- break;
- }
- }
-
- LOG_DEBUG("haveGlyphs=%d", have);
- return have;
+ BannerOverlayOptions options;
+ options.message = message;
+ options.durationMs = durationMs;
+ options.notificationType = notificationTypeEnum::text_banner;
+ showOverlayBanner(options);
}
-/**
- * Draw the icon with extra info printed around the corners
- */
-static void drawIconScreen(const char *upperMsg, OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
+// Called to trigger a banner with custom message and duration
+void Screen::showOverlayBanner(BannerOverlayOptions banner_overlay_options)
{
- // draw an xbm image.
- // Please note that everything that should be transitioned
- // needs to be drawn relative to x and y
-
- // draw centered icon left to right and centered above the one line of app text
- display->drawXbm(x + (SCREEN_WIDTH - icon_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - icon_height) / 2 + 2,
- icon_width, icon_height, icon_bits);
-
- display->setFont(FONT_MEDIUM);
- display->setTextAlignment(TEXT_ALIGN_LEFT);
- const char *title = "meshtastic.org";
- display->drawString(x + getStringCenteredX(title), y + SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM, title);
- display->setFont(FONT_SMALL);
-
- // Draw region in upper left
- if (upperMsg)
- display->drawString(x + 0, y + 0, upperMsg);
-
- // Draw version and short name in upper right
- char buf[25];
- snprintf(buf, sizeof(buf), "%s\n%s", xstr(APP_VERSION_SHORT), haveGlyphs(owner.short_name) ? owner.short_name : "");
-
- display->setTextAlignment(TEXT_ALIGN_RIGHT);
- display->drawString(x + SCREEN_WIDTH, y + 0, buf);
- screen->forceDisplay();
-
- display->setTextAlignment(TEXT_ALIGN_LEFT); // Restore left align, just to be kind to any other unsuspecting code
-}
-
-#ifdef USERPREFS_OEM_TEXT
-
-static void drawOEMIconScreen(const char *upperMsg, OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
-{
- static const uint8_t xbm[] = USERPREFS_OEM_IMAGE_DATA;
- display->drawXbm(x + (SCREEN_WIDTH - USERPREFS_OEM_IMAGE_WIDTH) / 2,
- y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - USERPREFS_OEM_IMAGE_HEIGHT) / 2 + 2, USERPREFS_OEM_IMAGE_WIDTH,
- USERPREFS_OEM_IMAGE_HEIGHT, xbm);
-
- switch (USERPREFS_OEM_FONT_SIZE) {
- case 0:
- display->setFont(FONT_SMALL);
- break;
- case 2:
- display->setFont(FONT_LARGE);
- break;
- default:
- display->setFont(FONT_MEDIUM);
- break;
- }
-
- display->setTextAlignment(TEXT_ALIGN_LEFT);
- const char *title = USERPREFS_OEM_TEXT;
- display->drawString(x + getStringCenteredX(title), y + SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM, title);
- display->setFont(FONT_SMALL);
-
- // Draw region in upper left
- if (upperMsg)
- display->drawString(x + 0, y + 0, upperMsg);
-
- // Draw version and shortname in upper right
- char buf[25];
- snprintf(buf, sizeof(buf), "%s\n%s", xstr(APP_VERSION_SHORT), haveGlyphs(owner.short_name) ? owner.short_name : "");
-
- display->setTextAlignment(TEXT_ALIGN_RIGHT);
- display->drawString(x + SCREEN_WIDTH, y + 0, buf);
- screen->forceDisplay();
-
- display->setTextAlignment(TEXT_ALIGN_LEFT); // Restore left align, just to be kind to any other unsuspecting code
-}
-
-static void drawOEMBootScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
-{
- // Draw region in upper left
- const char *region = myRegion ? myRegion->name : NULL;
- drawOEMIconScreen(region, display, state, x, y);
-}
-
-#endif
-
-void Screen::drawFrameText(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y, const char *message)
-{
- uint16_t x_offset = display->width() / 2;
- display->setTextAlignment(TEXT_ALIGN_CENTER);
- display->setFont(FONT_MEDIUM);
- display->drawString(x_offset + x, 26 + y, message);
-}
-
-// Used on boot when a certificate is being created
-static void drawSSLScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
-{
- display->setTextAlignment(TEXT_ALIGN_CENTER);
- display->setFont(FONT_SMALL);
- display->drawString(64 + x, y, "Creating SSL certificate");
-
-#ifdef ARCH_ESP32
- yield();
- esp_task_wdt_reset();
-#endif
-
- display->setFont(FONT_SMALL);
- if ((millis() / 1000) % 2) {
- display->drawString(64 + x, FONT_HEIGHT_SMALL + y + 2, "Please wait . . .");
- } else {
- display->drawString(64 + x, FONT_HEIGHT_SMALL + y + 2, "Please wait . . ");
- }
-}
-
-// Used when booting without a region set
-static void drawWelcomeScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
-{
- display->setFont(FONT_SMALL);
- display->setTextAlignment(TEXT_ALIGN_CENTER);
- display->drawString(64 + x, y, "//\\ E S H T /\\ S T / C");
- display->drawString(64 + x, y + FONT_HEIGHT_SMALL, getDeviceName());
- display->setTextAlignment(TEXT_ALIGN_LEFT);
-
- if ((millis() / 10000) % 2) {
- display->drawString(x, y + FONT_HEIGHT_SMALL * 2 - 3, "Set the region using the");
- display->drawString(x, y + FONT_HEIGHT_SMALL * 3 - 3, "Meshtastic Android, iOS,");
- display->drawString(x, y + FONT_HEIGHT_SMALL * 4 - 3, "Web or CLI clients.");
- } else {
- display->drawString(x, y + FONT_HEIGHT_SMALL * 2 - 3, "Visit meshtastic.org");
- display->drawString(x, y + FONT_HEIGHT_SMALL * 3 - 3, "for more information.");
- display->drawString(x, y + FONT_HEIGHT_SMALL * 4 - 3, "");
- }
-
-#ifdef ARCH_ESP32
- yield();
- esp_task_wdt_reset();
-#endif
-}
-
-// draw overlay in bottom right corner of screen to show when notifications are muted or modifier key is active
-static void drawFunctionOverlay(OLEDDisplay *display, OLEDDisplayUiState *state)
-{
- // LOG_DEBUG("Draw function overlay");
- if (functionSymbol.begin() != functionSymbol.end()) {
- char buf[64];
- display->setFont(FONT_SMALL);
- snprintf(buf, sizeof(buf), "%s", functionSymbolString.c_str());
- display->drawString(SCREEN_WIDTH - display->getStringWidth(buf), SCREEN_HEIGHT - FONT_HEIGHT_SMALL, buf);
- }
-}
-
#ifdef USE_EINK
-/// Used on eink displays while in deep sleep
-static void drawDeepSleepScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
-{
-
- // Next frame should use full-refresh, and block while running, else device will sleep before async callback
- EINK_ADD_FRAMEFLAG(display, COSMETIC);
- EINK_ADD_FRAMEFLAG(display, BLOCKING);
-
- LOG_DEBUG("Draw deep sleep screen");
-
- // Display displayStr on the screen
- drawIconScreen("Sleeping", display, state, x, y);
-}
-
-/// Used on eink displays when screen updates are paused
-static void drawScreensaverOverlay(OLEDDisplay *display, OLEDDisplayUiState *state)
-{
- LOG_DEBUG("Draw screensaver overlay");
-
- EINK_ADD_FRAMEFLAG(display, COSMETIC); // Take the opportunity for a full-refresh
-
- // Config
- display->setFont(FONT_SMALL);
- display->setTextAlignment(TEXT_ALIGN_LEFT);
- const char *pauseText = "Screen Paused";
- const char *idText = owner.short_name;
- const bool useId = haveGlyphs(idText); // This bool is used to hide the idText box if we can't render the short name
- constexpr uint16_t padding = 5;
- constexpr uint8_t dividerGap = 1;
- constexpr uint8_t imprecision = 5; // How far the box origins can drift from center. Combat burn-in.
-
- // Dimensions
- const uint16_t idTextWidth = display->getStringWidth(idText, strlen(idText), true); // "true": handle utf8 chars
- const uint16_t pauseTextWidth = display->getStringWidth(pauseText, strlen(pauseText));
- const uint16_t boxWidth = padding + (useId ? idTextWidth + padding + padding : 0) + pauseTextWidth + padding;
- const uint16_t boxHeight = padding + FONT_HEIGHT_SMALL + padding;
-
- // Position
- const int16_t boxLeft = (display->width() / 2) - (boxWidth / 2) + random(-imprecision, imprecision + 1);
- // const int16_t boxRight = boxLeft + boxWidth - 1;
- const int16_t boxTop = (display->height() / 2) - (boxHeight / 2 + random(-imprecision, imprecision + 1));
- const int16_t boxBottom = boxTop + boxHeight - 1;
- const int16_t idTextLeft = boxLeft + padding;
- const int16_t idTextTop = boxTop + padding;
- const int16_t pauseTextLeft = boxLeft + (useId ? padding + idTextWidth + padding : 0) + padding;
- const int16_t pauseTextTop = boxTop + padding;
- const int16_t dividerX = boxLeft + padding + idTextWidth + padding;
- const int16_t dividerTop = boxTop + 1 + dividerGap;
- const int16_t dividerBottom = boxBottom - 1 - dividerGap;
-
- // Draw: box
- display->setColor(EINK_WHITE);
- display->fillRect(boxLeft - 1, boxTop - 1, boxWidth + 2, boxHeight + 2); // Clear a slightly oversized area for the box
- display->setColor(EINK_BLACK);
- display->drawRect(boxLeft, boxTop, boxWidth, boxHeight);
-
- // Draw: Text
- if (useId)
- display->drawString(idTextLeft, idTextTop, idText);
- display->drawString(pauseTextLeft, pauseTextTop, pauseText);
- display->drawString(pauseTextLeft + 1, pauseTextTop, pauseText); // Faux bold
-
- // Draw: divider
- if (useId)
- display->drawLine(dividerX, dividerTop, dividerX, dividerBottom);
-}
+ EINK_ADD_FRAMEFLAG(dispdev, DEMAND_FAST); // Skip full refresh for all overlay menus
#endif
+ // Store the message and set the expiration timestamp
+ strncpy(NotificationRenderer::alertBannerMessage, banner_overlay_options.message, 255);
+ NotificationRenderer::alertBannerMessage[255] = '\0'; // Ensure null termination
+ NotificationRenderer::alertBannerUntil =
+ (banner_overlay_options.durationMs == 0) ? 0 : millis() + banner_overlay_options.durationMs;
+ NotificationRenderer::optionsArrayPtr = banner_overlay_options.optionsArrayPtr;
+ NotificationRenderer::optionsEnumPtr = banner_overlay_options.optionsEnumPtr;
+ NotificationRenderer::alertBannerOptions = banner_overlay_options.optionsCount;
+ NotificationRenderer::alertBannerCallback = banner_overlay_options.bannerCallback;
+ NotificationRenderer::curSelected = banner_overlay_options.InitialSelected;
+ NotificationRenderer::pauseBanner = false;
+ NotificationRenderer::current_notification_type = notificationTypeEnum::selection_picker;
+ static OverlayCallback overlays[] = {graphics::UIRenderer::drawNavigationBar, NotificationRenderer::drawBannercallback};
+ ui->setOverlays(overlays, sizeof(overlays) / sizeof(overlays[0]));
+ ui->setTargetFPS(60);
+ ui->update();
+}
+
+// Called to trigger a banner with custom message and duration
+void Screen::showNodePicker(const char *message, uint32_t durationMs, std::function bannerCallback)
+{
+#ifdef USE_EINK
+ EINK_ADD_FRAMEFLAG(dispdev, DEMAND_FAST); // Skip full refresh for all overlay menus
+#endif
+ nodeDB->pause_sort(true);
+ // Store the message and set the expiration timestamp
+ strncpy(NotificationRenderer::alertBannerMessage, message, 255);
+ NotificationRenderer::alertBannerMessage[255] = '\0'; // Ensure null termination
+ NotificationRenderer::alertBannerUntil = (durationMs == 0) ? 0 : millis() + durationMs;
+ NotificationRenderer::alertBannerCallback = bannerCallback;
+ NotificationRenderer::pauseBanner = false;
+ NotificationRenderer::curSelected = 0;
+ NotificationRenderer::current_notification_type = notificationTypeEnum::node_picker;
+
+ static OverlayCallback overlays[] = {graphics::UIRenderer::drawNavigationBar, NotificationRenderer::drawBannercallback};
+ ui->setOverlays(overlays, sizeof(overlays) / sizeof(overlays[0]));
+ ui->setTargetFPS(60);
+ ui->update();
+}
+
+// Called to trigger a banner with custom message and duration
+void Screen::showNumberPicker(const char *message, uint32_t durationMs, uint8_t digits,
+ std::function bannerCallback)
+{
+#ifdef USE_EINK
+ EINK_ADD_FRAMEFLAG(dispdev, DEMAND_FAST); // Skip full refresh for all overlay menus
+#endif
+ // Store the message and set the expiration timestamp
+ strncpy(NotificationRenderer::alertBannerMessage, message, 255);
+ NotificationRenderer::alertBannerMessage[255] = '\0'; // Ensure null termination
+ NotificationRenderer::alertBannerUntil = (durationMs == 0) ? 0 : millis() + durationMs;
+ NotificationRenderer::alertBannerCallback = bannerCallback;
+ NotificationRenderer::pauseBanner = false;
+ NotificationRenderer::curSelected = 0;
+ NotificationRenderer::current_notification_type = notificationTypeEnum::number_picker;
+ NotificationRenderer::numDigits = digits;
+ NotificationRenderer::currentNumber = 0;
+
+ static OverlayCallback overlays[] = {graphics::UIRenderer::drawNavigationBar, NotificationRenderer::drawBannercallback};
+ ui->setOverlays(overlays, sizeof(overlays) / sizeof(overlays[0]));
+ ui->setTargetFPS(60);
+ ui->update();
+}
static void drawModuleFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{
@@ -388,875 +238,12 @@ static void drawModuleFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int
pi.drawFrame(display, state, x, y);
}
-static void drawFrameFirmware(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
-{
- display->setTextAlignment(TEXT_ALIGN_CENTER);
- display->setFont(FONT_MEDIUM);
- display->drawString(64 + x, y, "Updating");
-
- display->setFont(FONT_SMALL);
- display->setTextAlignment(TEXT_ALIGN_LEFT);
- display->drawStringMaxWidth(0 + x, 2 + y + FONT_HEIGHT_SMALL * 2, x + display->getWidth(),
- "Please be patient and do not power off.");
-}
-
-/// Draw the last text message we received
-static void drawCriticalFaultFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
-{
- display->setTextAlignment(TEXT_ALIGN_LEFT);
- display->setFont(FONT_MEDIUM);
-
- char tempBuf[24];
- snprintf(tempBuf, sizeof(tempBuf), "Critical fault #%d", error_code);
- display->drawString(0 + x, 0 + y, tempBuf);
- display->setTextAlignment(TEXT_ALIGN_LEFT);
- display->setFont(FONT_SMALL);
- display->drawString(0 + x, FONT_HEIGHT_MEDIUM + y, "For help, please visit \nmeshtastic.org");
-}
-
// Ignore messages originating from phone (from the current node 0x0) unless range test or store and forward module are enabled
static bool shouldDrawMessage(const meshtastic_MeshPacket *packet)
{
return packet->from != 0 && !moduleConfig.store_forward.enabled;
}
-// Draw power bars or a charging indicator on an image of a battery, determined by battery charge voltage or percentage.
-static void drawBattery(OLEDDisplay *display, int16_t x, int16_t y, uint8_t *imgBuffer, const PowerStatus *powerStatus)
-{
- static const uint8_t powerBar[3] = {0x81, 0xBD, 0xBD};
- static const uint8_t lightning[8] = {0xA1, 0xA1, 0xA5, 0xAD, 0xB5, 0xA5, 0x85, 0x85};
- // Clear the bar area on the battery image
- for (int i = 1; i < 14; i++) {
- imgBuffer[i] = 0x81;
- }
- // If charging, draw a charging indicator
- if (powerStatus->getIsCharging()) {
- memcpy(imgBuffer + 3, lightning, 8);
- // If not charging, Draw power bars
- } else {
- for (int i = 0; i < 4; i++) {
- if (powerStatus->getBatteryChargePercent() >= 25 * i)
- memcpy(imgBuffer + 1 + (i * 3), powerBar, 3);
- }
- }
- display->drawFastImage(x, y, 16, 8, imgBuffer);
-}
-
-#if defined(DISPLAY_CLOCK_FRAME)
-
-void Screen::drawWatchFaceToggleButton(OLEDDisplay *display, int16_t x, int16_t y, bool digitalMode, float scale)
-{
- uint16_t segmentWidth = SEGMENT_WIDTH * scale;
- uint16_t segmentHeight = SEGMENT_HEIGHT * scale;
-
- if (digitalMode) {
- uint16_t radius = (segmentWidth + (segmentHeight * 2) + 4) / 2;
- uint16_t centerX = (x + segmentHeight + 2) + (radius / 2);
- uint16_t centerY = (y + segmentHeight + 2) + (radius / 2);
-
- display->drawCircle(centerX, centerY, radius);
- display->drawCircle(centerX, centerY, radius + 1);
- display->drawLine(centerX, centerY, centerX, centerY - radius + 3);
- display->drawLine(centerX, centerY, centerX + radius - 3, centerY);
- } else {
- uint16_t segmentOneX = x + segmentHeight + 2;
- uint16_t segmentOneY = y;
-
- uint16_t segmentTwoX = segmentOneX + segmentWidth + 2;
- uint16_t segmentTwoY = segmentOneY + segmentHeight + 2;
-
- uint16_t segmentThreeX = segmentOneX;
- uint16_t segmentThreeY = segmentTwoY + segmentWidth + 2;
-
- uint16_t segmentFourX = x;
- uint16_t segmentFourY = y + segmentHeight + 2;
-
- drawHorizontalSegment(display, segmentOneX, segmentOneY, segmentWidth, segmentHeight);
- drawVerticalSegment(display, segmentTwoX, segmentTwoY, segmentWidth, segmentHeight);
- drawHorizontalSegment(display, segmentThreeX, segmentThreeY, segmentWidth, segmentHeight);
- drawVerticalSegment(display, segmentFourX, segmentFourY, segmentWidth, segmentHeight);
- }
-}
-
-// Draw a digital clock
-void Screen::drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
-{
- display->setTextAlignment(TEXT_ALIGN_LEFT);
-
- drawBattery(display, x, y + 7, imgBattery, powerStatus);
-
- if (powerStatus->getHasBattery()) {
- String batteryPercent = String(powerStatus->getBatteryChargePercent()) + "%";
-
- display->setFont(FONT_SMALL);
-
- display->drawString(x + 20, y + 2, batteryPercent);
- }
-
- if (nimbleBluetooth && nimbleBluetooth->isConnected()) {
- drawBluetoothConnectedIcon(display, display->getWidth() - 18, y + 2);
- }
-
- drawWatchFaceToggleButton(display, display->getWidth() - 36, display->getHeight() - 36, screen->digitalWatchFace, 1);
-
- display->setColor(OLEDDISPLAY_COLOR::WHITE);
-
- uint32_t rtc_sec = getValidTime(RTCQuality::RTCQualityDevice, true); // Display local timezone
- if (rtc_sec > 0) {
- long hms = rtc_sec % SEC_PER_DAY;
- hms = (hms + SEC_PER_DAY) % SEC_PER_DAY;
-
- int hour = hms / SEC_PER_HOUR;
- int minute = (hms % SEC_PER_HOUR) / SEC_PER_MIN;
- int second = (hms % SEC_PER_HOUR) % SEC_PER_MIN; // or hms % SEC_PER_MIN
-
- hour = hour > 12 ? hour - 12 : hour;
-
- if (hour == 0) {
- hour = 12;
- }
-
- // hours string
- String hourString = String(hour);
-
- // minutes string
- String minuteString = minute < 10 ? "0" + String(minute) : String(minute);
-
- String timeString = hourString + ":" + minuteString;
-
- // seconds string
- String secondString = second < 10 ? "0" + String(second) : String(second);
-
- float scale = 1.5;
-
- uint16_t segmentWidth = SEGMENT_WIDTH * scale;
- uint16_t segmentHeight = SEGMENT_HEIGHT * scale;
-
- // calculate hours:minutes string width
- uint16_t timeStringWidth = timeString.length() * 5;
-
- for (uint8_t i = 0; i < timeString.length(); i++) {
- String character = String(timeString[i]);
-
- if (character == ":") {
- timeStringWidth += segmentHeight;
- } else {
- timeStringWidth += segmentWidth + (segmentHeight * 2) + 4;
- }
- }
-
- // calculate seconds string width
- uint16_t secondStringWidth = (secondString.length() * 12) + 4;
-
- // sum these to get total string width
- uint16_t totalWidth = timeStringWidth + secondStringWidth;
-
- uint16_t hourMinuteTextX = (display->getWidth() / 2) - (totalWidth / 2);
-
- uint16_t startingHourMinuteTextX = hourMinuteTextX;
-
- uint16_t hourMinuteTextY = (display->getHeight() / 2) - (((segmentWidth * 2) + (segmentHeight * 3) + 8) / 2);
-
- // iterate over characters in hours:minutes string and draw segmented characters
- for (uint8_t i = 0; i < timeString.length(); i++) {
- String character = String(timeString[i]);
-
- if (character == ":") {
- drawSegmentedDisplayColon(display, hourMinuteTextX, hourMinuteTextY, scale);
-
- hourMinuteTextX += segmentHeight + 6;
- } else {
- drawSegmentedDisplayCharacter(display, hourMinuteTextX, hourMinuteTextY, character.toInt(), scale);
-
- hourMinuteTextX += segmentWidth + (segmentHeight * 2) + 4;
- }
-
- hourMinuteTextX += 5;
- }
-
- // draw seconds string
- display->setFont(FONT_MEDIUM);
- display->drawString(startingHourMinuteTextX + timeStringWidth + 4,
- (display->getHeight() - hourMinuteTextY) - FONT_HEIGHT_MEDIUM + 6, secondString);
- }
-}
-
-void Screen::drawSegmentedDisplayColon(OLEDDisplay *display, int x, int y, float scale)
-{
- uint16_t segmentWidth = SEGMENT_WIDTH * scale;
- uint16_t segmentHeight = SEGMENT_HEIGHT * scale;
-
- uint16_t cellHeight = (segmentWidth * 2) + (segmentHeight * 3) + 8;
-
- uint16_t topAndBottomX = x + (4 * scale);
-
- uint16_t quarterCellHeight = cellHeight / 4;
-
- uint16_t topY = y + quarterCellHeight;
- uint16_t bottomY = y + (quarterCellHeight * 3);
-
- display->fillRect(topAndBottomX, topY, segmentHeight, segmentHeight);
- display->fillRect(topAndBottomX, bottomY, segmentHeight, segmentHeight);
-}
-
-void Screen::drawSegmentedDisplayCharacter(OLEDDisplay *display, int x, int y, uint8_t number, float scale)
-{
- // the numbers 0-9, each expressed as an array of seven boolean (0|1) values encoding the on/off state of
- // segment {innerIndex + 1}
- // e.g., to display the numeral '0', segments 1-6 are on, and segment 7 is off.
- uint8_t numbers[10][7] = {
- {1, 1, 1, 1, 1, 1, 0}, // 0 Display segment key
- {0, 1, 1, 0, 0, 0, 0}, // 1 1
- {1, 1, 0, 1, 1, 0, 1}, // 2 ___
- {1, 1, 1, 1, 0, 0, 1}, // 3 6 | | 2
- {0, 1, 1, 0, 0, 1, 1}, // 4 |_7ฬฒ_|
- {1, 0, 1, 1, 0, 1, 1}, // 5 5 | | 3
- {1, 0, 1, 1, 1, 1, 1}, // 6 |___|
- {1, 1, 1, 0, 0, 1, 0}, // 7
- {1, 1, 1, 1, 1, 1, 1}, // 8 4
- {1, 1, 1, 1, 0, 1, 1}, // 9
- };
-
- // the width and height of each segment's central rectangle:
- // _____________________
- // โฐ| (only this part, |โฑ
- // โฐ | not including | โฑ
- // โฑ | the triangles | โฐ
- // โฑ| on the ends) |โฐ
- // โพโพโพโพโพโพโพโพโพโพโพโพโพโพโพโพโพโพโพโพโพ
-
- uint16_t segmentWidth = SEGMENT_WIDTH * scale;
- uint16_t segmentHeight = SEGMENT_HEIGHT * scale;
-
- // segment x and y coordinates
- uint16_t segmentOneX = x + segmentHeight + 2;
- uint16_t segmentOneY = y;
-
- uint16_t segmentTwoX = segmentOneX + segmentWidth + 2;
- uint16_t segmentTwoY = segmentOneY + segmentHeight + 2;
-
- uint16_t segmentThreeX = segmentTwoX;
- uint16_t segmentThreeY = segmentTwoY + segmentWidth + 2 + segmentHeight + 2;
-
- uint16_t segmentFourX = segmentOneX;
- uint16_t segmentFourY = segmentThreeY + segmentWidth + 2;
-
- uint16_t segmentFiveX = x;
- uint16_t segmentFiveY = segmentThreeY;
-
- uint16_t segmentSixX = x;
- uint16_t segmentSixY = segmentTwoY;
-
- uint16_t segmentSevenX = segmentOneX;
- uint16_t segmentSevenY = segmentTwoY + segmentWidth + 2;
-
- if (numbers[number][0]) {
- drawHorizontalSegment(display, segmentOneX, segmentOneY, segmentWidth, segmentHeight);
- }
-
- if (numbers[number][1]) {
- drawVerticalSegment(display, segmentTwoX, segmentTwoY, segmentWidth, segmentHeight);
- }
-
- if (numbers[number][2]) {
- drawVerticalSegment(display, segmentThreeX, segmentThreeY, segmentWidth, segmentHeight);
- }
-
- if (numbers[number][3]) {
- drawHorizontalSegment(display, segmentFourX, segmentFourY, segmentWidth, segmentHeight);
- }
-
- if (numbers[number][4]) {
- drawVerticalSegment(display, segmentFiveX, segmentFiveY, segmentWidth, segmentHeight);
- }
-
- if (numbers[number][5]) {
- drawVerticalSegment(display, segmentSixX, segmentSixY, segmentWidth, segmentHeight);
- }
-
- if (numbers[number][6]) {
- drawHorizontalSegment(display, segmentSevenX, segmentSevenY, segmentWidth, segmentHeight);
- }
-}
-
-void Screen::drawHorizontalSegment(OLEDDisplay *display, int x, int y, int width, int height)
-{
- int halfHeight = height / 2;
-
- // draw central rectangle
- display->fillRect(x, y, width, height);
-
- // draw end triangles
- display->fillTriangle(x, y, x, y + height - 1, x - halfHeight, y + halfHeight);
-
- display->fillTriangle(x + width, y, x + width + halfHeight, y + halfHeight, x + width, y + height - 1);
-}
-
-void Screen::drawVerticalSegment(OLEDDisplay *display, int x, int y, int width, int height)
-{
- int halfHeight = height / 2;
-
- // draw central rectangle
- display->fillRect(x, y, height, width);
-
- // draw end triangles
- display->fillTriangle(x + halfHeight, y - halfHeight, x + height - 1, y, x, y);
-
- display->fillTriangle(x, y + width, x + height - 1, y + width, x + halfHeight, y + width + halfHeight);
-}
-
-void Screen::drawBluetoothConnectedIcon(OLEDDisplay *display, int16_t x, int16_t y)
-{
- display->drawFastImage(x, y, 18, 14, bluetoothConnectedIcon);
-}
-
-// Draw an analog clock
-void Screen::drawAnalogClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
-{
- display->setTextAlignment(TEXT_ALIGN_LEFT);
-
- drawBattery(display, x, y + 7, imgBattery, powerStatus);
-
- if (powerStatus->getHasBattery()) {
- String batteryPercent = String(powerStatus->getBatteryChargePercent()) + "%";
-
- display->setFont(FONT_SMALL);
-
- display->drawString(x + 20, y + 2, batteryPercent);
- }
-
- if (nimbleBluetooth && nimbleBluetooth->isConnected()) {
- drawBluetoothConnectedIcon(display, display->getWidth() - 18, y + 2);
- }
-
- drawWatchFaceToggleButton(display, display->getWidth() - 36, display->getHeight() - 36, screen->digitalWatchFace, 1);
-
- // clock face center coordinates
- int16_t centerX = display->getWidth() / 2;
- int16_t centerY = display->getHeight() / 2;
-
- // clock face radius
- int16_t radius = (display->getWidth() / 2) * 0.8;
-
- // noon (0 deg) coordinates (outermost circle)
- int16_t noonX = centerX;
- int16_t noonY = centerY - radius;
-
- // second hand radius and y coordinate (outermost circle)
- int16_t secondHandNoonY = noonY + 1;
-
- // tick mark outer y coordinate; (first nested circle)
- int16_t tickMarkOuterNoonY = secondHandNoonY;
-
- // seconds tick mark inner y coordinate; (second nested circle)
- double secondsTickMarkInnerNoonY = (double)noonY + 8;
-
- // hours tick mark inner y coordinate; (third nested circle)
- double hoursTickMarkInnerNoonY = (double)noonY + 16;
-
- // minute hand y coordinate
- int16_t minuteHandNoonY = secondsTickMarkInnerNoonY + 4;
-
- // hour string y coordinate
- int16_t hourStringNoonY = minuteHandNoonY + 18;
-
- // hour hand radius and y coordinate
- int16_t hourHandRadius = radius * 0.55;
- int16_t hourHandNoonY = centerY - hourHandRadius;
-
- display->setColor(OLEDDISPLAY_COLOR::WHITE);
- display->drawCircle(centerX, centerY, radius);
-
- uint32_t rtc_sec = getValidTime(RTCQuality::RTCQualityDevice, true); // Display local timezone
- if (rtc_sec > 0) {
- long hms = rtc_sec % SEC_PER_DAY;
- hms = (hms + SEC_PER_DAY) % SEC_PER_DAY;
-
- // Tear apart hms into h:m:s
- int hour = hms / SEC_PER_HOUR;
- int minute = (hms % SEC_PER_HOUR) / SEC_PER_MIN;
- int second = (hms % SEC_PER_HOUR) % SEC_PER_MIN; // or hms % SEC_PER_MIN
-
- hour = hour > 12 ? hour - 12 : hour;
-
- int16_t degreesPerHour = 30;
- int16_t degreesPerMinuteOrSecond = 6;
-
- double hourBaseAngle = hour * degreesPerHour;
- double hourAngleOffset = ((double)minute / 60) * degreesPerHour;
- double hourAngle = radians(hourBaseAngle + hourAngleOffset);
-
- double minuteBaseAngle = minute * degreesPerMinuteOrSecond;
- double minuteAngleOffset = ((double)second / 60) * degreesPerMinuteOrSecond;
- double minuteAngle = radians(minuteBaseAngle + minuteAngleOffset);
-
- double secondAngle = radians(second * degreesPerMinuteOrSecond);
-
- double hourX = sin(-hourAngle) * (hourHandNoonY - centerY) + noonX;
- double hourY = cos(-hourAngle) * (hourHandNoonY - centerY) + centerY;
-
- double minuteX = sin(-minuteAngle) * (minuteHandNoonY - centerY) + noonX;
- double minuteY = cos(-minuteAngle) * (minuteHandNoonY - centerY) + centerY;
-
- double secondX = sin(-secondAngle) * (secondHandNoonY - centerY) + noonX;
- double secondY = cos(-secondAngle) * (secondHandNoonY - centerY) + centerY;
-
- display->setFont(FONT_MEDIUM);
-
- // draw minute and hour tick marks and hour numbers
- for (uint16_t angle = 0; angle < 360; angle += 6) {
- double angleInRadians = radians(angle);
-
- double sineAngleInRadians = sin(-angleInRadians);
- double cosineAngleInRadians = cos(-angleInRadians);
-
- double endX = sineAngleInRadians * (tickMarkOuterNoonY - centerY) + noonX;
- double endY = cosineAngleInRadians * (tickMarkOuterNoonY - centerY) + centerY;
-
- if (angle % degreesPerHour == 0) {
- double startX = sineAngleInRadians * (hoursTickMarkInnerNoonY - centerY) + noonX;
- double startY = cosineAngleInRadians * (hoursTickMarkInnerNoonY - centerY) + centerY;
-
- // draw hour tick mark
- display->drawLine(startX, startY, endX, endY);
-
- static char buffer[2];
-
- uint8_t hourInt = (angle / 30);
-
- if (hourInt == 0) {
- hourInt = 12;
- }
-
- // hour number x offset needs to be adjusted for some cases
- int8_t hourStringXOffset;
- int8_t hourStringYOffset = 13;
-
- switch (hourInt) {
- case 3:
- hourStringXOffset = 5;
- break;
- case 9:
- hourStringXOffset = 7;
- break;
- case 10:
- case 11:
- hourStringXOffset = 8;
- break;
- case 12:
- hourStringXOffset = 13;
- break;
- default:
- hourStringXOffset = 6;
- break;
- }
-
- double hourStringX = (sineAngleInRadians * (hourStringNoonY - centerY) + noonX) - hourStringXOffset;
- double hourStringY = (cosineAngleInRadians * (hourStringNoonY - centerY) + centerY) - hourStringYOffset;
-
- // draw hour number
- display->drawStringf(hourStringX, hourStringY, buffer, "%d", hourInt);
- }
-
- if (angle % degreesPerMinuteOrSecond == 0) {
- double startX = sineAngleInRadians * (secondsTickMarkInnerNoonY - centerY) + noonX;
- double startY = cosineAngleInRadians * (secondsTickMarkInnerNoonY - centerY) + centerY;
-
- // draw minute tick mark
- display->drawLine(startX, startY, endX, endY);
- }
- }
-
- // draw hour hand
- display->drawLine(centerX, centerY, hourX, hourY);
-
- // draw minute hand
- display->drawLine(centerX, centerY, minuteX, minuteY);
-
- // draw second hand
- display->drawLine(centerX, centerY, secondX, secondY);
- }
-}
-
-#endif
-
-// Get an absolute time from "seconds ago" info. Returns false if no valid timestamp possible
-bool deltaToTimestamp(uint32_t secondsAgo, uint8_t *hours, uint8_t *minutes, int32_t *daysAgo)
-{
- // Cache the result - avoid frequent recalculation
- static uint8_t hoursCached = 0, minutesCached = 0;
- static uint32_t daysAgoCached = 0;
- static uint32_t secondsAgoCached = 0;
- static bool validCached = false;
-
- // Abort: if timezone not set
- if (strlen(config.device.tzdef) == 0) {
- validCached = false;
- return validCached;
- }
-
- // Abort: if invalid pointers passed
- if (hours == nullptr || minutes == nullptr || daysAgo == nullptr) {
- validCached = false;
- return validCached;
- }
-
- // Abort: if time seems invalid.. (> 6 months ago, probably seen before RTC set)
- if (secondsAgo > SEC_PER_DAY * 30UL * 6) {
- validCached = false;
- return validCached;
- }
-
- // If repeated request, don't bother recalculating
- if (secondsAgo - secondsAgoCached < 60 && secondsAgoCached != 0) {
- if (validCached) {
- *hours = hoursCached;
- *minutes = minutesCached;
- *daysAgo = daysAgoCached;
- }
- return validCached;
- }
-
- // Get local time
- uint32_t secondsRTC = getValidTime(RTCQuality::RTCQualityDevice, true); // Get local time
-
- // Abort: if RTC not set
- if (!secondsRTC) {
- validCached = false;
- return validCached;
- }
-
- // Get absolute time when last seen
- uint32_t secondsSeenAt = secondsRTC - secondsAgo;
-
- // Calculate daysAgo
- *daysAgo = (secondsRTC / SEC_PER_DAY) - (secondsSeenAt / SEC_PER_DAY); // How many "midnights" have passed
-
- // Get seconds since midnight
- uint32_t hms = (secondsRTC - secondsAgo) % SEC_PER_DAY;
- hms = (hms + SEC_PER_DAY) % SEC_PER_DAY;
-
- // Tear apart hms into hours and minutes
- *hours = hms / SEC_PER_HOUR;
- *minutes = (hms % SEC_PER_HOUR) / SEC_PER_MIN;
-
- // Cache the result
- daysAgoCached = *daysAgo;
- hoursCached = *hours;
- minutesCached = *minutes;
- secondsAgoCached = secondsAgo;
-
- validCached = true;
- return validCached;
-}
-
-/// Draw the last text message we received
-static void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
-{
- // the max length of this buffer is much longer than we can possibly print
- static char tempBuf[237];
-
- const meshtastic_MeshPacket &mp = devicestate.rx_text_message;
- meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(getFrom(&mp));
- // LOG_DEBUG("Draw text message from 0x%x: %s", mp.from,
- // mp.decoded.variant.data.decoded.bytes);
-
- // Demo for drawStringMaxWidth:
- // with the third parameter you can define the width after which words will
- // be wrapped. Currently only spaces and "-" are allowed for wrapping
- display->setTextAlignment(TEXT_ALIGN_LEFT);
- display->setFont(FONT_SMALL);
- if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_INVERTED) {
- display->fillRect(0 + x, 0 + y, x + display->getWidth(), y + FONT_HEIGHT_SMALL);
- display->setColor(BLACK);
- }
-
- // For time delta
- uint32_t seconds = sinceReceived(&mp);
- uint32_t minutes = seconds / 60;
- uint32_t hours = minutes / 60;
- uint32_t days = hours / 24;
-
- // For timestamp
- uint8_t timestampHours, timestampMinutes;
- int32_t daysAgo;
- bool useTimestamp = deltaToTimestamp(seconds, ×tampHours, ×tampMinutes, &daysAgo);
-
- // If bold, draw twice, shifting right by one pixel
- for (uint8_t xOff = 0; xOff <= (config.display.heading_bold ? 1 : 0); xOff++) {
- // Show a timestamp if received today, but longer than 15 minutes ago
- if (useTimestamp && minutes >= 15 && daysAgo == 0) {
- display->drawStringf(xOff + x, 0 + y, tempBuf, "At %02hu:%02hu from %s", timestampHours, timestampMinutes,
- (node && node->has_user) ? node->user.short_name : "???");
- }
- // Timestamp yesterday (if display is wide enough)
- else if (useTimestamp && daysAgo == 1 && display->width() >= 200) {
- display->drawStringf(xOff + x, 0 + y, tempBuf, "Yesterday %02hu:%02hu from %s", timestampHours, timestampMinutes,
- (node && node->has_user) ? node->user.short_name : "???");
- }
- // Otherwise, show a time delta
- else {
- display->drawStringf(xOff + x, 0 + y, tempBuf, "%s ago from %s",
- screen->drawTimeDelta(days, hours, minutes, seconds).c_str(),
- (node && node->has_user) ? node->user.short_name : "???");
- }
- }
-
- display->setColor(WHITE);
-#ifndef EXCLUDE_EMOJI
- const char *msg = reinterpret_cast(mp.decoded.payload.bytes);
- if (strcmp(msg, "\U0001F44D") == 0) {
- display->drawXbm(x + (SCREEN_WIDTH - thumbs_width) / 2,
- y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - thumbs_height) / 2 + 2 + 5, thumbs_width, thumbs_height,
- thumbup);
- } else if (strcmp(msg, "\U0001F44E") == 0) {
- display->drawXbm(x + (SCREEN_WIDTH - thumbs_width) / 2,
- y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - thumbs_height) / 2 + 2 + 5, thumbs_width, thumbs_height,
- thumbdown);
- } else if (strcmp(msg, "\U0001F60A") == 0 || strcmp(msg, "\U0001F600") == 0 || strcmp(msg, "\U0001F642") == 0 ||
- strcmp(msg, "\U0001F609") == 0 ||
- strcmp(msg, "\U0001F601") == 0) { // matches 5 different common smileys, so that the phone user doesn't have to
- // remember which one is compatible
- display->drawXbm(x + (SCREEN_WIDTH - smiley_width) / 2,
- y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - smiley_height) / 2 + 2 + 5, smiley_width, smiley_height,
- smiley);
- } else if (strcmp(msg, "โ") == 0) {
- display->drawXbm(x + (SCREEN_WIDTH - question_width) / 2,
- y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - question_height) / 2 + 2 + 5, question_width, question_height,
- question);
- } else if (strcmp(msg, "โผ๏ธ") == 0) {
- display->drawXbm(x + (SCREEN_WIDTH - bang_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - bang_height) / 2 + 2 + 5,
- bang_width, bang_height, bang);
- } else if (strcmp(msg, "\U0001F4A9") == 0) {
- display->drawXbm(x + (SCREEN_WIDTH - poo_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - poo_height) / 2 + 2 + 5,
- poo_width, poo_height, poo);
- } else if (strcmp(msg, "\U0001F923") == 0) {
- display->drawXbm(x + (SCREEN_WIDTH - haha_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - haha_height) / 2 + 2 + 5,
- haha_width, haha_height, haha);
- } else if (strcmp(msg, "\U0001F44B") == 0) {
- display->drawXbm(x + (SCREEN_WIDTH - wave_icon_width) / 2,
- y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - wave_icon_height) / 2 + 2 + 5, wave_icon_width,
- wave_icon_height, wave_icon);
- } else if (strcmp(msg, "\U0001F920") == 0) {
- display->drawXbm(x + (SCREEN_WIDTH - cowboy_width) / 2,
- y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - cowboy_height) / 2 + 2 + 5, cowboy_width, cowboy_height,
- cowboy);
- } else if (strcmp(msg, "\U0001F42D") == 0) {
- display->drawXbm(x + (SCREEN_WIDTH - deadmau5_width) / 2,
- y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - deadmau5_height) / 2 + 2 + 5, deadmau5_width, deadmau5_height,
- deadmau5);
- } else if (strcmp(msg, "\xE2\x98\x80\xEF\xB8\x8F") == 0) {
- display->drawXbm(x + (SCREEN_WIDTH - sun_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - sun_height) / 2 + 2 + 5,
- sun_width, sun_height, sun);
- } else if (strcmp(msg, "\u2614") == 0) {
- display->drawXbm(x + (SCREEN_WIDTH - rain_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - rain_height) / 2 + 2 + 10,
- rain_width, rain_height, rain);
- } else if (strcmp(msg, "โ๏ธ") == 0) {
- display->drawXbm(x + (SCREEN_WIDTH - cloud_width) / 2,
- y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - cloud_height) / 2 + 2 + 5, cloud_width, cloud_height, cloud);
- } else if (strcmp(msg, "๐ซ๏ธ") == 0) {
- display->drawXbm(x + (SCREEN_WIDTH - fog_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - fog_height) / 2 + 2 + 5,
- fog_width, fog_height, fog);
- } else if (strcmp(msg, "\U0001F608") == 0) {
- display->drawXbm(x + (SCREEN_WIDTH - devil_width) / 2,
- y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - devil_height) / 2 + 2 + 5, devil_width, devil_height, devil);
- } else if (strcmp(msg, "โฅ๏ธ") == 0 || strcmp(msg, "\U0001F9E1") == 0 || strcmp(msg, "\U00002763") == 0 ||
- strcmp(msg, "\U00002764") == 0 || strcmp(msg, "\U0001F495") == 0 || strcmp(msg, "\U0001F496") == 0 ||
- strcmp(msg, "\U0001F497") == 0 || strcmp(msg, "\U0001F498") == 0) {
- display->drawXbm(x + (SCREEN_WIDTH - heart_width) / 2,
- y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - heart_height) / 2 + 2 + 5, heart_width, heart_height, heart);
- } else {
- snprintf(tempBuf, sizeof(tempBuf), "%s", mp.decoded.payload.bytes);
- display->drawStringMaxWidth(0 + x, 0 + y + FONT_HEIGHT_SMALL, x + display->getWidth(), tempBuf);
- }
-#else
- snprintf(tempBuf, sizeof(tempBuf), "%s", mp.decoded.payload.bytes);
- display->drawStringMaxWidth(0 + x, 0 + y + FONT_HEIGHT_SMALL, x + display->getWidth(), tempBuf);
-#endif
-}
-
-/// Draw a series of fields in a column, wrapping to multiple columns if needed
-void Screen::drawColumns(OLEDDisplay *display, int16_t x, int16_t y, const char **fields)
-{
- // The coordinates define the left starting point of the text
- display->setTextAlignment(TEXT_ALIGN_LEFT);
-
- const char **f = fields;
- int xo = x, yo = y;
- while (*f) {
- display->drawString(xo, yo, *f);
- if ((display->getColor() == BLACK) && config.display.heading_bold)
- display->drawString(xo + 1, yo, *f);
-
- display->setColor(WHITE);
- yo += FONT_HEIGHT_SMALL;
- if (yo > SCREEN_HEIGHT - FONT_HEIGHT_SMALL) {
- xo += SCREEN_WIDTH / 2;
- yo = 0;
- }
- f++;
- }
-}
-
-// Draw nodes status
-static void drawNodes(OLEDDisplay *display, int16_t x, int16_t y, const NodeStatus *nodeStatus)
-{
- char usersString[20];
- snprintf(usersString, sizeof(usersString), "%d/%d", nodeStatus->getNumOnline(), nodeStatus->getNumTotal());
-#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \
- defined(ST7789_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(HX8357_CS)) && \
- !defined(DISPLAY_FORCE_SMALL_FONTS)
- display->drawFastImage(x, y + 3, 8, 8, imgUser);
-#else
- display->drawFastImage(x, y, 8, 8, imgUser);
-#endif
- display->drawString(x + 10, y - 2, usersString);
- if (config.display.heading_bold)
- display->drawString(x + 11, y - 2, usersString);
-}
-#if HAS_GPS
-// Draw GPS status summary
-static void drawGPS(OLEDDisplay *display, int16_t x, int16_t y, const GPSStatus *gps)
-{
- if (config.position.fixed_position) {
- // GPS coordinates are currently fixed
- display->drawString(x - 1, y - 2, "Fixed GPS");
- if (config.display.heading_bold)
- display->drawString(x, y - 2, "Fixed GPS");
- return;
- }
- if (!gps->getIsConnected()) {
- display->drawString(x, y - 2, "No GPS");
- if (config.display.heading_bold)
- display->drawString(x + 1, y - 2, "No GPS");
- return;
- }
- display->drawFastImage(x, y, 6, 8, gps->getHasLock() ? imgPositionSolid : imgPositionEmpty);
- if (!gps->getHasLock()) {
- display->drawString(x + 8, y - 2, "No sats");
- if (config.display.heading_bold)
- display->drawString(x + 9, y - 2, "No sats");
- return;
- } else {
- char satsString[3];
- uint8_t bar[2] = {0};
-
- // Draw DOP signal bars
- for (int i = 0; i < 5; i++) {
- if (gps->getDOP() <= dopThresholds[i])
- bar[0] = ~((1 << (5 - i)) - 1);
- else
- bar[0] = 0b10000000;
- // bar[1] = bar[0];
- display->drawFastImage(x + 9 + (i * 2), y, 2, 8, bar);
- }
-
- // Draw satellite image
- display->drawFastImage(x + 24, y, 8, 8, imgSatellite);
-
- // Draw the number of satellites
- snprintf(satsString, sizeof(satsString), "%u", gps->getNumSatellites());
- display->drawString(x + 34, y - 2, satsString);
- if (config.display.heading_bold)
- display->drawString(x + 35, y - 2, satsString);
- }
-}
-
-// Draw status when GPS is disabled or not present
-static void drawGPSpowerstat(OLEDDisplay *display, int16_t x, int16_t y, const GPSStatus *gps)
-{
- String displayLine;
- int pos;
- if (y < FONT_HEIGHT_SMALL) { // Line 1: use short string
- displayLine = config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_NOT_PRESENT ? "No GPS" : "GPS off";
- pos = SCREEN_WIDTH - display->getStringWidth(displayLine);
- } else {
- displayLine = config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_NOT_PRESENT ? "GPS not present"
- : "GPS is disabled";
- pos = (SCREEN_WIDTH - display->getStringWidth(displayLine)) / 2;
- }
- display->drawString(x + pos, y, displayLine);
-}
-
-static void drawGPSAltitude(OLEDDisplay *display, int16_t x, int16_t y, const GPSStatus *gps)
-{
- String displayLine = "";
- if (!gps->getIsConnected() && !config.position.fixed_position) {
- // displayLine = "No GPS Module";
- // display->drawString(x + (SCREEN_WIDTH - (display->getStringWidth(displayLine))) / 2, y, displayLine);
- } else if (!gps->getHasLock() && !config.position.fixed_position) {
- // displayLine = "No GPS Lock";
- // display->drawString(x + (SCREEN_WIDTH - (display->getStringWidth(displayLine))) / 2, y, displayLine);
- } else {
- geoCoord.updateCoords(int32_t(gps->getLatitude()), int32_t(gps->getLongitude()), int32_t(gps->getAltitude()));
- displayLine = "Altitude: " + String(geoCoord.getAltitude()) + "m";
- if (config.display.units == meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL)
- displayLine = "Altitude: " + String(geoCoord.getAltitude() * METERS_TO_FEET) + "ft";
- display->drawString(x + (SCREEN_WIDTH - (display->getStringWidth(displayLine))) / 2, y, displayLine);
- }
-}
-
-// Draw GPS status coordinates
-static void drawGPScoordinates(OLEDDisplay *display, int16_t x, int16_t y, const GPSStatus *gps)
-{
- auto gpsFormat = config.display.gps_format;
- String displayLine = "";
-
- if (!gps->getIsConnected() && !config.position.fixed_position) {
- displayLine = "No GPS present";
- display->drawString(x + (SCREEN_WIDTH - (display->getStringWidth(displayLine))) / 2, y, displayLine);
- } else if (!gps->getHasLock() && !config.position.fixed_position) {
- displayLine = "No GPS Lock";
- display->drawString(x + (SCREEN_WIDTH - (display->getStringWidth(displayLine))) / 2, y, displayLine);
- } else {
-
- geoCoord.updateCoords(int32_t(gps->getLatitude()), int32_t(gps->getLongitude()), int32_t(gps->getAltitude()));
-
- if (gpsFormat != meshtastic_Config_DisplayConfig_GpsCoordinateFormat_DMS) {
- char coordinateLine[22];
- if (gpsFormat == meshtastic_Config_DisplayConfig_GpsCoordinateFormat_DEC) { // Decimal Degrees
- snprintf(coordinateLine, sizeof(coordinateLine), "%f %f", geoCoord.getLatitude() * 1e-7,
- geoCoord.getLongitude() * 1e-7);
- } else if (gpsFormat == meshtastic_Config_DisplayConfig_GpsCoordinateFormat_UTM) { // Universal Transverse Mercator
- snprintf(coordinateLine, sizeof(coordinateLine), "%2i%1c %06u %07u", geoCoord.getUTMZone(), geoCoord.getUTMBand(),
- geoCoord.getUTMEasting(), geoCoord.getUTMNorthing());
- } else if (gpsFormat == meshtastic_Config_DisplayConfig_GpsCoordinateFormat_MGRS) { // Military Grid Reference System
- snprintf(coordinateLine, sizeof(coordinateLine), "%2i%1c %1c%1c %05u %05u", geoCoord.getMGRSZone(),
- geoCoord.getMGRSBand(), geoCoord.getMGRSEast100k(), geoCoord.getMGRSNorth100k(),
- geoCoord.getMGRSEasting(), geoCoord.getMGRSNorthing());
- } else if (gpsFormat == meshtastic_Config_DisplayConfig_GpsCoordinateFormat_OLC) { // Open Location Code
- geoCoord.getOLCCode(coordinateLine);
- } else if (gpsFormat == meshtastic_Config_DisplayConfig_GpsCoordinateFormat_OSGR) { // Ordnance Survey Grid Reference
- if (geoCoord.getOSGRE100k() == 'I' || geoCoord.getOSGRN100k() == 'I') // OSGR is only valid around the UK region
- snprintf(coordinateLine, sizeof(coordinateLine), "%s", "Out of Boundary");
- else
- snprintf(coordinateLine, sizeof(coordinateLine), "%1c%1c %05u %05u", geoCoord.getOSGRE100k(),
- geoCoord.getOSGRN100k(), geoCoord.getOSGREasting(), geoCoord.getOSGRNorthing());
- }
-
- // If fixed position, display text "Fixed GPS" alternating with the coordinates.
- if (config.position.fixed_position) {
- if ((millis() / 10000) % 2) {
- display->drawString(x + (SCREEN_WIDTH - (display->getStringWidth(coordinateLine))) / 2, y, coordinateLine);
- } else {
- display->drawString(x + (SCREEN_WIDTH - (display->getStringWidth("Fixed GPS"))) / 2, y, "Fixed GPS");
- }
- } else {
- display->drawString(x + (SCREEN_WIDTH - (display->getStringWidth(coordinateLine))) / 2, y, coordinateLine);
- }
- } else {
- char latLine[22];
- char lonLine[22];
- snprintf(latLine, sizeof(latLine), "%2iยฐ %2i' %2u\" %1c", geoCoord.getDMSLatDeg(), geoCoord.getDMSLatMin(),
- geoCoord.getDMSLatSec(), geoCoord.getDMSLatCP());
- snprintf(lonLine, sizeof(lonLine), "%3iยฐ %2i' %2u\" %1c", geoCoord.getDMSLonDeg(), geoCoord.getDMSLonMin(),
- geoCoord.getDMSLonSec(), geoCoord.getDMSLonCP());
- display->drawString(x + (SCREEN_WIDTH - (display->getStringWidth(latLine))) / 2, y - FONT_HEIGHT_SMALL * 1, latLine);
- display->drawString(x + (SCREEN_WIDTH - (display->getStringWidth(lonLine))) / 2, y, lonLine);
- }
- }
-}
-#endif
/**
* Given a recent lat/lon return a guess of the heading the user is walking on.
*
@@ -1280,7 +267,7 @@ float Screen::estimatedHeading(double lat, double lon)
if (d < 10) // haven't moved enough, just keep current bearing
return b;
- b = GeoCoord::bearing(oldLat, oldLon, lat, lon);
+ b = GeoCoord::bearing(oldLat, oldLon, lat, lon) * RAD_TO_DEG;
oldLat = lat;
oldLon = lon;
@@ -1289,239 +276,10 @@ float Screen::estimatedHeading(double lat, double lon)
/// We will skip one node - the one for us, so we just blindly loop over all
/// nodes
-static size_t nodeIndex;
static int8_t prevFrame = -1;
-// Draw the arrow pointing to a node's location
-void Screen::drawNodeHeading(OLEDDisplay *display, int16_t compassX, int16_t compassY, uint16_t compassDiam, float headingRadian)
-{
- Point tip(0.0f, 0.5f), tail(0.0f, -0.35f); // pointing up initially
- float arrowOffsetX = 0.14f, arrowOffsetY = 1.0f;
- Point leftArrow(tip.x - arrowOffsetX, tip.y - arrowOffsetY), rightArrow(tip.x + arrowOffsetX, tip.y - arrowOffsetY);
-
- Point *arrowPoints[] = {&tip, &tail, &leftArrow, &rightArrow};
-
- for (int i = 0; i < 4; i++) {
- arrowPoints[i]->rotate(headingRadian);
- arrowPoints[i]->scale(compassDiam * 0.6);
- arrowPoints[i]->translate(compassX, compassY);
- }
- /* Old arrow
- display->drawLine(tip.x, tip.y, tail.x, tail.y);
- display->drawLine(leftArrow.x, leftArrow.y, tip.x, tip.y);
- display->drawLine(rightArrow.x, rightArrow.y, tip.x, tip.y);
- display->drawLine(leftArrow.x, leftArrow.y, tail.x, tail.y);
- display->drawLine(rightArrow.x, rightArrow.y, tail.x, tail.y);
- */
-#ifdef USE_EINK
- display->drawTriangle(tip.x, tip.y, rightArrow.x, rightArrow.y, tail.x, tail.y);
-#else
- display->fillTriangle(tip.x, tip.y, rightArrow.x, rightArrow.y, tail.x, tail.y);
-#endif
- display->drawTriangle(tip.x, tip.y, leftArrow.x, leftArrow.y, tail.x, tail.y);
-}
-
-// Get a string representation of the time passed since something happened
-void Screen::getTimeAgoStr(uint32_t agoSecs, char *timeStr, uint8_t maxLength)
-{
- // Use an absolute timestamp in some cases.
- // Particularly useful with E-Ink displays. Static UI, fewer refreshes.
- uint8_t timestampHours, timestampMinutes;
- int32_t daysAgo;
- bool useTimestamp = deltaToTimestamp(agoSecs, ×tampHours, ×tampMinutes, &daysAgo);
-
- if (agoSecs < 120) // last 2 mins?
- snprintf(timeStr, maxLength, "%u seconds ago", agoSecs);
- // -- if suitable for timestamp --
- else if (useTimestamp && agoSecs < 15 * SECONDS_IN_MINUTE) // Last 15 minutes
- snprintf(timeStr, maxLength, "%u minutes ago", agoSecs / SECONDS_IN_MINUTE);
- else if (useTimestamp && daysAgo == 0) // Today
- snprintf(timeStr, maxLength, "Last seen: %02u:%02u", (unsigned int)timestampHours, (unsigned int)timestampMinutes);
- else if (useTimestamp && daysAgo == 1) // Yesterday
- snprintf(timeStr, maxLength, "Seen yesterday");
- else if (useTimestamp && daysAgo > 1) // Last six months (capped by deltaToTimestamp method)
- snprintf(timeStr, maxLength, "%li days ago", (long)daysAgo);
- // -- if using time delta instead --
- else if (agoSecs < 120 * 60) // last 2 hrs
- snprintf(timeStr, maxLength, "%u minutes ago", agoSecs / 60);
- // Only show hours ago if it's been less than 6 months. Otherwise, we may have bad data.
- else if ((agoSecs / 60 / 60) < (hours_in_month * 6))
- snprintf(timeStr, maxLength, "%u hours ago", agoSecs / 60 / 60);
- else
- snprintf(timeStr, maxLength, "unknown age");
-}
-
-void Screen::drawCompassNorth(OLEDDisplay *display, int16_t compassX, int16_t compassY, float myHeading)
-{
- // If north is supposed to be at the top of the compass we want rotation to be +0
- if (config.display.compass_north_top)
- myHeading = -0;
- /* N sign points currently not deleted*/
- Point N1(-0.04f, 0.65f), N2(0.04f, 0.65f); // N sign points (N1-N4)
- Point N3(-0.04f, 0.55f), N4(0.04f, 0.55f);
- Point NC1(0.00f, 0.50f); // north circle center point
- Point *rosePoints[] = {&N1, &N2, &N3, &N4, &NC1};
-
- uint16_t compassDiam = Screen::getCompassDiam(SCREEN_WIDTH, SCREEN_HEIGHT);
-
- for (int i = 0; i < 5; i++) {
- // North on compass will be negative of heading
- rosePoints[i]->rotate(-myHeading);
- rosePoints[i]->scale(compassDiam);
- rosePoints[i]->translate(compassX, compassY);
- }
-
- /* changed the N sign to a small circle on the compass circle.
- display->drawLine(N1.x, N1.y, N3.x, N3.y);
- display->drawLine(N2.x, N2.y, N4.x, N4.y);
- display->drawLine(N1.x, N1.y, N4.x, N4.y);
- */
- display->drawCircle(NC1.x, NC1.y, 4); // North sign circle, 4px radius is sufficient for all displays.
-}
-
-uint16_t Screen::getCompassDiam(uint32_t displayWidth, uint32_t displayHeight)
-{
- uint16_t diam = 0;
- uint16_t offset = 0;
-
- if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_DEFAULT)
- offset = FONT_HEIGHT_SMALL;
-
- // get the smaller of the 2 dimensions and subtract 20
- if (displayWidth > (displayHeight - offset)) {
- diam = displayHeight - offset;
- // if 2/3 of the other size would be smaller, use that
- if (diam > (displayWidth * 2 / 3)) {
- diam = displayWidth * 2 / 3;
- }
- } else {
- diam = displayWidth;
- if (diam > ((displayHeight - offset) * 2 / 3)) {
- diam = (displayHeight - offset) * 2 / 3;
- }
- }
-
- return diam - 20;
-};
-
-static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
-{
- // We only advance our nodeIndex if the frame # has changed - because
- // drawNodeInfo will be called repeatedly while the frame is shown
- if (state->currentFrame != prevFrame) {
- prevFrame = state->currentFrame;
-
- nodeIndex = (nodeIndex + 1) % nodeDB->getNumMeshNodes();
- meshtastic_NodeInfoLite *n = nodeDB->getMeshNodeByIndex(nodeIndex);
- if (n->num == nodeDB->getNodeNum()) {
- // Don't show our node, just skip to next
- nodeIndex = (nodeIndex + 1) % nodeDB->getNumMeshNodes();
- n = nodeDB->getMeshNodeByIndex(nodeIndex);
- }
- }
-
- meshtastic_NodeInfoLite *node = nodeDB->getMeshNodeByIndex(nodeIndex);
-
- display->setFont(FONT_SMALL);
-
- // The coordinates define the left starting point of the text
- display->setTextAlignment(TEXT_ALIGN_LEFT);
-
- if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_INVERTED) {
- display->fillRect(0 + x, 0 + y, x + display->getWidth(), y + FONT_HEIGHT_SMALL);
- }
-
- const char *username = node->has_user ? node->user.long_name : "Unknown Name";
-
- static char signalStr[20];
-
- // section here to choose whether to display hops away rather than signal strength if more than 0 hops away.
- if (node->hops_away > 0) {
- snprintf(signalStr, sizeof(signalStr), "Hops Away: %d", node->hops_away);
- } else {
- snprintf(signalStr, sizeof(signalStr), "Signal: %d%%", clamp((int)((node->snr + 10) * 5), 0, 100));
- }
-
- static char lastStr[20];
- screen->getTimeAgoStr(sinceLastSeen(node), lastStr, sizeof(lastStr));
-
- static char distStr[20];
- if (config.display.units == meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL) {
- strncpy(distStr, "? mi ?ยฐ", sizeof(distStr)); // might not have location data
- } else {
- strncpy(distStr, "? km ?ยฐ", sizeof(distStr));
- }
- meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum());
- const char *fields[] = {username, lastStr, signalStr, distStr, NULL};
- int16_t compassX = 0, compassY = 0;
- uint16_t compassDiam = Screen::getCompassDiam(SCREEN_WIDTH, SCREEN_HEIGHT);
-
- // coordinates for the center of the compass/circle
- if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_DEFAULT) {
- compassX = x + SCREEN_WIDTH - compassDiam / 2 - 5;
- compassY = y + SCREEN_HEIGHT / 2;
- } else {
- compassX = x + SCREEN_WIDTH - compassDiam / 2 - 5;
- compassY = y + FONT_HEIGHT_SMALL + (SCREEN_HEIGHT - FONT_HEIGHT_SMALL) / 2;
- }
- bool hasNodeHeading = false;
-
- if (ourNode && (nodeDB->hasValidPosition(ourNode) || screen->hasHeading())) {
- const meshtastic_PositionLite &op = ourNode->position;
- float myHeading;
- if (screen->hasHeading())
- myHeading = (screen->getHeading()) * PI / 180; // gotta convert compass degrees to Radians
- else
- myHeading = screen->estimatedHeading(DegD(op.latitude_i), DegD(op.longitude_i));
- screen->drawCompassNorth(display, compassX, compassY, myHeading);
-
- if (nodeDB->hasValidPosition(node)) {
- // display direction toward node
- hasNodeHeading = true;
- const meshtastic_PositionLite &p = node->position;
- float d =
- GeoCoord::latLongToMeter(DegD(p.latitude_i), DegD(p.longitude_i), DegD(op.latitude_i), DegD(op.longitude_i));
-
- float bearingToOther =
- GeoCoord::bearing(DegD(op.latitude_i), DegD(op.longitude_i), DegD(p.latitude_i), DegD(p.longitude_i));
- // If the top of the compass is a static north then bearingToOther can be drawn on the compass directly
- // If the top of the compass is not a static north we need adjust bearingToOther based on heading
- if (!config.display.compass_north_top)
- bearingToOther -= myHeading;
- screen->drawNodeHeading(display, compassX, compassY, compassDiam, bearingToOther);
-
- float bearingToOtherDegrees = (bearingToOther < 0) ? bearingToOther + 2 * PI : bearingToOther;
- bearingToOtherDegrees = bearingToOtherDegrees * 180 / PI;
-
- if (config.display.units == meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL) {
- if (d < (2 * MILES_TO_FEET))
- snprintf(distStr, sizeof(distStr), "%.0fft %.0fยฐ", d * METERS_TO_FEET, bearingToOtherDegrees);
- else
- snprintf(distStr, sizeof(distStr), "%.1fmi %.0fยฐ", d * METERS_TO_FEET / MILES_TO_FEET,
- bearingToOtherDegrees);
- } else {
- if (d < 2000)
- snprintf(distStr, sizeof(distStr), "%.0fm %.0fยฐ", d, bearingToOtherDegrees);
- else
- snprintf(distStr, sizeof(distStr), "%.1fkm %.0fยฐ", d / 1000, bearingToOtherDegrees);
- }
- }
- }
- if (!hasNodeHeading) {
- // direction to node is unknown so display question mark
- // Debug info for gps lock errors
- // LOG_DEBUG("ourNode %d, ourPos %d, theirPos %d", !!ourNode, ourNode && hasValidPosition(ourNode),
- // hasValidPosition(node));
- display->drawString(compassX - FONT_HEIGHT_SMALL / 4, compassY - FONT_HEIGHT_SMALL / 2, "?");
- }
- display->drawCircle(compassX, compassY, compassDiam / 2);
-
- if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_INVERTED) {
- display->setColor(BLACK);
- }
- // Must be after distStr is populated
- screen->drawColumns(display, x, y, fields);
-}
+// Combined dynamic node list frame cycling through LastHeard, HopSignal, and Distance modes
+// Uses a single frame and changes data every few seconds (E-Ink variant is separate)
#if defined(ESP_PLATFORM) && defined(USE_ST7789)
SPIClass SPI1(HSPI);
@@ -1531,6 +289,20 @@ Screen::Screen(ScanI2C::DeviceAddress address, meshtastic_Config_DisplayConfig_O
: concurrency::OSThread("Screen"), address_found(address), model(screenType), geometry(geometry), cmdQueue(32)
{
graphics::normalFrames = new FrameCallback[MAX_NUM_NODES + NUM_EXTRA_FRAMES];
+
+ LOG_INFO("Protobuf Value uiconfig.screen_rgb_color: %d", uiconfig.screen_rgb_color);
+ int32_t rawRGB = uiconfig.screen_rgb_color;
+ if (rawRGB > 0 && rawRGB <= 255255255) {
+ uint8_t TFT_MESH_r = (rawRGB >> 16) & 0xFF;
+ uint8_t TFT_MESH_g = (rawRGB >> 8) & 0xFF;
+ uint8_t TFT_MESH_b = rawRGB & 0xFF;
+ LOG_INFO("Values of r,g,b: %d, %d, %d", TFT_MESH_r, TFT_MESH_g, TFT_MESH_b);
+
+ if (TFT_MESH_r <= 255 && TFT_MESH_g <= 255 && TFT_MESH_b <= 255) {
+ TFT_MESH = COLOR565(TFT_MESH_r, TFT_MESH_g, TFT_MESH_b);
+ }
+ }
+
#if defined(USE_SH1106) || defined(USE_SH1107) || defined(USE_SH1107_128_64)
dispdev = new SH1106Wire(address.address, -1, -1, geometry,
(address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
@@ -1541,6 +313,7 @@ Screen::Screen(ScanI2C::DeviceAddress address, meshtastic_Config_DisplayConfig_O
#else
dispdev = new ST7789Spi(&SPI1, ST7789_RESET, ST7789_RS, ST7789_NSS, GEOMETRY_RAWMODE, TFT_WIDTH, TFT_HEIGHT);
#endif
+ static_cast(dispdev)->setRGB(TFT_MESH);
#elif defined(USE_SSD1306)
dispdev = new SSD1306Wire(address.address, -1, -1, geometry,
(address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
@@ -1557,15 +330,17 @@ Screen::Screen(ScanI2C::DeviceAddress address, meshtastic_Config_DisplayConfig_O
#elif defined(USE_ST7567)
dispdev = new ST7567Wire(address.address, -1, -1, geometry,
(address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
-#elif ARCH_PORTDUINO && !HAS_TFT
- if (settingsMap[displayPanel] != no_screen) {
- LOG_DEBUG("Make TFTDisplay!");
- dispdev = new TFTDisplay(address.address, -1, -1, geometry,
- (address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
- } else {
- dispdev = new AutoOLEDWire(address.address, -1, -1, geometry,
- (address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
- isAUTOOled = true;
+#elif ARCH_PORTDUINO
+ if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR) {
+ if (settingsMap[displayPanel] != no_screen) {
+ LOG_DEBUG("Make TFTDisplay!");
+ dispdev = new TFTDisplay(address.address, -1, -1, geometry,
+ (address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
+ } else {
+ dispdev = new AutoOLEDWire(address.address, -1, -1, geometry,
+ (address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
+ isAUTOOled = true;
+ }
}
#else
dispdev = new AutoOLEDWire(address.address, -1, -1, geometry,
@@ -1589,7 +364,7 @@ Screen::~Screen()
void Screen::doDeepSleep()
{
#ifdef USE_EINK
- setOn(false, drawDeepSleepScreen);
+ setOn(false, graphics::UIRenderer::drawDeepSleepFrame);
#ifdef PIN_EINK_EN
digitalWrite(PIN_EINK_EN, LOW); // power off backlight
#endif
@@ -1607,7 +382,6 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver)
if (on != screenOn) {
if (on) {
LOG_INFO("Turn on screen");
- buttonThread->setScreenFlag(true);
powerMon->setState(meshtastic_PowerMon_State_Screen_On);
#ifdef T_WATCH_S3
PMU->enablePowerOutput(XPOWERS_ALDO2);
@@ -1651,8 +425,6 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver)
// eInkScreensaver parameter is usually NULL (default argument), default frame used instead
setScreensaverFrames(einkScreensaver);
#endif
- LOG_INFO("Turn off screen");
- buttonThread->setScreenFlag(false);
#ifdef ELECROW_ThinkNode_M1
if (digitalRead(PIN_EINK_EN) == HIGH) {
digitalWrite(PIN_EINK_EN, LOW);
@@ -1687,10 +459,23 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver)
void Screen::setup()
{
- // We don't set useDisplay until setup() is called, because some boards have a declaration of this object but the device
- // is never found when probing i2c and therefore we don't call setup and never want to do (invalid) accesses to this device.
+
+ // === Enable display rendering ===
useDisplay = true;
+ // === Load saved brightness from UI config ===
+ // For OLED displays (SSD1306), default brightness is 255 if not set
+ if (uiconfig.screen_brightness == 0) {
+#if defined(USE_OLED) || defined(USE_SSD1306) || defined(USE_SH1106) || defined(USE_SH1107)
+ brightness = 255; // Default for OLED
+#else
+ brightness = BRIGHTNESS_DEFAULT;
+#endif
+ } else {
+ brightness = uiconfig.screen_brightness;
+ }
+
+ // === Detect OLED subtype (if supported by board variant) ===
#ifdef AutoOLEDWire_h
if (isAUTOOled)
static_cast(dispdev)->setDetected(model);
@@ -1701,66 +486,69 @@ void Screen::setup()
#endif
#if defined(USE_ST7789) && defined(TFT_MESH)
- // Heltec T114 and T190: honor a custom text color, if defined in variant.h
+ // Apply custom RGB color (e.g. Heltec T114/T190)
static_cast(dispdev)->setRGB(TFT_MESH);
#endif
- // Initialising the UI will init the display too.
+ // === Initialize display and UI system ===
ui->init();
-
displayWidth = dispdev->width();
displayHeight = dispdev->height();
- ui->setTimePerTransition(0);
+ ui->setTimePerTransition(0); // Disable animation delays
+ ui->setIndicatorPosition(BOTTOM); // Not used (indicators disabled below)
+ ui->setIndicatorDirection(LEFT_RIGHT); // Not used (indicators disabled below)
+ ui->setFrameAnimation(SLIDE_LEFT); // Used only when indicators are active
+ ui->disableAllIndicators(); // Disable page indicator dots
+ ui->getUiState()->userData = this; // Allow static callbacks to access Screen instance
- ui->setIndicatorPosition(BOTTOM);
- // Defines where the first frame is located in the bar.
- ui->setIndicatorDirection(LEFT_RIGHT);
- ui->setFrameAnimation(SLIDE_LEFT);
- // Don't show the page swipe dots while in boot screen.
- ui->disableAllIndicators();
- // Store a pointer to Screen so we can get to it from static functions.
- ui->getUiState()->userData = this;
+ // === Apply loaded brightness ===
+#if defined(ST7789_CS)
+ static_cast(dispdev)->setDisplayBrightness(brightness);
+#elif defined(USE_OLED) || defined(USE_SSD1306) || defined(USE_SH1106) || defined(USE_SH1107)
+ dispdev->setBrightness(brightness);
+#endif
+ LOG_INFO("Applied screen brightness: %d", brightness);
- // Set the utf8 conversion function
+ // === Set custom overlay callbacks ===
+ static OverlayCallback overlays[] = {
+ graphics::UIRenderer::drawNavigationBar // Custom indicator icons for each frame
+ };
+ ui->setOverlays(overlays, sizeof(overlays) / sizeof(overlays[0]));
+
+ // === Enable UTF-8 to display mapping ===
dispdev->setFontTableLookupFunction(customFontTableLookup);
#ifdef USERPREFS_OEM_TEXT
- logo_timeout *= 2; // Double the time if we have a custom logo
+ logo_timeout *= 2; // Give more time for branded boot logos
#endif
- // Add frames.
- EINK_ADD_FRAMEFLAG(dispdev, DEMAND_FAST);
- alertFrames[0] = [this](OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) -> void {
+ // === Configure alert frames (e.g., "Resuming..." or region name) ===
+ EINK_ADD_FRAMEFLAG(dispdev, DEMAND_FAST); // Skip slow refresh
+ alertFrames[0] = [this](OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) {
#ifdef ARCH_ESP32
- if (wakeCause == ESP_SLEEP_WAKEUP_TIMER || wakeCause == ESP_SLEEP_WAKEUP_EXT1) {
- drawFrameText(display, state, x, y, "Resuming...");
- } else
+ if (wakeCause == ESP_SLEEP_WAKEUP_TIMER || wakeCause == ESP_SLEEP_WAKEUP_EXT1)
+ graphics::UIRenderer::drawFrameText(display, state, x, y, "Resuming...");
+ else
#endif
{
- // Draw region in upper left
- const char *region = myRegion ? myRegion->name : NULL;
- drawIconScreen(region, display, state, x, y);
+ const char *region = myRegion ? myRegion->name : nullptr;
+ graphics::UIRenderer::drawIconScreen(region, display, state, x, y);
}
};
ui->setFrames(alertFrames, 1);
- // No overlays.
- ui->setOverlays(nullptr, 0);
+ ui->disableAutoTransition(); // Require manual navigation between frames
- // Require presses to switch between frames.
- ui->disableAutoTransition();
-
- // Set up a log buffer with 3 lines, 32 chars each.
+ // === Log buffer for on-screen logs (3 lines max) ===
dispdev->setLogBuffer(3, 32);
+ // === Optional screen mirroring or flipping (e.g. for T-Beam orientation) ===
#ifdef SCREEN_MIRROR
dispdev->mirrorScreen();
#else
- // Standard behaviour is to FLIP the screen (needed on T-Beam). If this config item is set, unflip it, and thereby logically
- // flip it. If you have a headache now, you're welcome.
if (!config.display.flip_screen) {
-#if defined(ST7701_CS) || defined(ST7735_CS) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || \
- defined(ST7789_CS) || defined(RAK14014) || defined(HX8357_CS) || defined(ILI9488_CS)
+#if defined(ST7701_CS) || defined(ST7735_CS) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7789_CS) || \
+ defined(RAK14014) || defined(HX8357_CS) || defined(ILI9488_CS)
static_cast(dispdev)->flipScreenVertically();
#elif defined(USE_ST7789)
static_cast(dispdev)->flipScreenVertically();
@@ -1770,30 +558,31 @@ void Screen::setup()
}
#endif
- // Get our hardware ID
+ // === Generate device ID from MAC address ===
uint8_t dmac[6];
getMacAddr(dmac);
- snprintf(ourId, sizeof(ourId), "%02x%02x", dmac[4], dmac[5]);
+ snprintf(screen->ourId, sizeof(screen->ourId), "%02x%02x", dmac[4], dmac[5]);
+
#if ARCH_PORTDUINO
- handleSetOn(false); // force clean init
+ handleSetOn(false); // Ensure proper init for Arduino targets
#endif
- // Turn on the display.
+ // === Turn on display and trigger first draw ===
handleSetOn(true);
-
- // On some ssd1306 clones, the first draw command is discarded, so draw it
- // twice initially. Skip this for EINK Displays to save a few seconds during boot
+ determineResolution(dispdev->height(), dispdev->width());
ui->update();
#ifndef USE_EINK
- ui->update();
+ ui->update(); // Some SSD1306 clones drop the first draw, so run twice
#endif
serialSinceMsec = millis();
-#if ARCH_PORTDUINO && !HAS_TFT
- if (settingsMap[touchscreenModule]) {
- touchScreenImpl1 =
- new TouchScreenImpl1(dispdev->getWidth(), dispdev->getHeight(), static_cast(dispdev)->getTouch);
- touchScreenImpl1->init();
+#if ARCH_PORTDUINO
+ if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR) {
+ if (settingsMap[touchscreenModule]) {
+ touchScreenImpl1 =
+ new TouchScreenImpl1(dispdev->getWidth(), dispdev->getHeight(), static_cast(dispdev)->getTouch);
+ touchScreenImpl1->init();
+ }
}
#elif HAS_TOUCHSCREEN
touchScreenImpl1 =
@@ -1801,10 +590,11 @@ void Screen::setup()
touchScreenImpl1->init();
#endif
- // Subscribe to status updates
+ // === Subscribe to device status updates ===
powerStatusObserver.observe(&powerStatus->onNewStatus);
gpsStatusObserver.observe(&gpsStatus->onNewStatus);
nodeStatusObserver.observe(&nodeStatus->onNewStatus);
+
#if !MESHTASTIC_EXCLUDE_ADMIN
adminMessageObserver.observe(adminModule);
#endif
@@ -1813,7 +603,7 @@ void Screen::setup()
if (inputBroker)
inputObserver.observe(inputBroker);
- // Modules can notify screen about refresh
+ // === Notify modules that support UI events ===
MeshModule::observeUIEvents(&uiFrameEventObserver);
}
@@ -1850,6 +640,11 @@ void Screen::forceDisplay(bool forceUiUpdate)
// Tell EInk class to update the display
static_cast(dispdev)->forceDisplay();
+#else
+ // No delay between UI frame rendering
+ if (forceUiUpdate) {
+ setFastFramerate();
+ }
#endif
}
@@ -1866,6 +661,7 @@ int32_t Screen::runOnce()
if (displayHeight == 0) {
displayHeight = dispdev->getHeight();
}
+ menuHandler::handleMenuSwitch(dispdev);
// Show boot screen for first logo_timeout seconds, then switch to normal operation.
// serialSinceMsec adjusts for additional serial wait time during nRF52 bootup
@@ -1881,7 +677,7 @@ int32_t Screen::runOnce()
if (showingOEMBootScreen && (millis() > ((logo_timeout / 2) + serialSinceMsec))) {
LOG_INFO("Switch to OEM screen...");
// Change frames.
- static FrameCallback bootOEMFrames[] = {drawOEMBootScreen};
+ static FrameCallback bootOEMFrames[] = {graphics::UIRenderer::drawOEMBootScreen};
static const int bootOEMFrameCount = sizeof(bootOEMFrames) / sizeof(bootOEMFrames[0]);
ui->setFrames(bootOEMFrames, bootOEMFrameCount);
ui->update();
@@ -1893,10 +689,13 @@ int32_t Screen::runOnce()
#endif
#ifndef DISABLE_WELCOME_UNSET
- if (showingNormalScreen && config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_UNSET) {
- setWelcomeFrames();
+ if (!NotificationRenderer::isOverlayBannerShowing() && config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_UNSET) {
+ menuHandler::LoraRegionPicker(0);
}
#endif
+ if (!NotificationRenderer::isOverlayBannerShowing() && rebootAtMsec != 0) {
+ showSimpleBanner("Rebooting...", 0);
+ }
// Process incoming commands.
for (;;) {
@@ -1923,6 +722,7 @@ int32_t Screen::runOnce()
case Cmd::START_ALERT_FRAME: {
showingBootScreen = false; // this should avoid the edge case where an alert triggers before the boot screen goes away
showingNormalScreen = false;
+ NotificationRenderer::pauseBanner = true;
alertFrames[0] = alertFrame;
#ifdef USE_EINK
EINK_ADD_FRAMEFLAG(dispdev, DEMAND_FAST); // Use fast-refresh for next frame, no skip please
@@ -1936,13 +736,12 @@ int32_t Screen::runOnce()
handleStartFirmwareUpdateScreen();
break;
case Cmd::STOP_ALERT_FRAME:
+ NotificationRenderer::pauseBanner = false;
case Cmd::STOP_BOOT_SCREEN:
EINK_ADD_FRAMEFLAG(dispdev, COSMETIC); // E-Ink: Explicitly use full-refresh for next frame
setFrames();
break;
- case Cmd::PRINT:
- handlePrint(cmd.print_text);
- free(cmd.print_text);
+ case Cmd::NOOP:
break;
default:
LOG_ERROR("Invalid screen cmd");
@@ -1962,6 +761,7 @@ int32_t Screen::runOnce()
// Switch to a low framerate (to save CPU) when we are not in transition
// but we should only call setTargetFPS when framestate changes, because
// otherwise that breaks animations.
+
if (targetFramerate != IDLE_FRAMERATE && ui->getUiState()->frameState == FIXED) {
// oldFrameState = ui->getUiState()->frameState;
targetFramerate = IDLE_FRAMERATE;
@@ -1977,8 +777,8 @@ int32_t Screen::runOnce()
if (config.display.auto_screen_carousel_secs > 0 &&
!Throttle::isWithinTimespanMs(lastScreenTransition, config.display.auto_screen_carousel_secs * 1000)) {
-// If an E-Ink display struggles with fast refresh, force carousel to use full refresh instead
-// Carousel is potentially a major source of E-Ink display wear
+ // If an E-Ink display struggles with fast refresh, force carousel to use full refresh instead
+ // Carousel is potentially a major source of E-Ink display wear
#if !defined(EINK_BACKGROUND_USES_FAST)
EINK_ADD_FRAMEFLAG(dispdev, COSMETIC);
#endif
@@ -1996,47 +796,18 @@ int32_t Screen::runOnce()
return (1000 / targetFramerate);
}
-void Screen::drawDebugInfoTrampoline(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
-{
- Screen *screen2 = reinterpret_cast(state->userData);
- screen2->debugInfo.drawFrame(display, state, x, y);
-}
-
-void Screen::drawDebugInfoSettingsTrampoline(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
-{
- Screen *screen2 = reinterpret_cast(state->userData);
- screen2->debugInfo.drawFrameSettings(display, state, x, y);
-}
-
-void Screen::drawDebugInfoWiFiTrampoline(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
-{
- Screen *screen2 = reinterpret_cast(state->userData);
- screen2->debugInfo.drawFrameWiFi(display, state, x, y);
-}
-
/* show a message that the SSL cert is being built
* it is expected that this will be used during the boot phase */
void Screen::setSSLFrames()
{
if (address_found.address) {
// LOG_DEBUG("Show SSL frames");
- static FrameCallback sslFrames[] = {drawSSLScreen};
+ static FrameCallback sslFrames[] = {NotificationRenderer::drawSSLScreen};
ui->setFrames(sslFrames, 1);
ui->update();
}
}
-/* show a message that the SSL cert is being built
- * it is expected that this will be used during the boot phase */
-void Screen::setWelcomeFrames()
-{
- if (address_found.address) {
- // LOG_DEBUG("Show Welcome frames");
- static FrameCallback frames[] = {drawWelcomeScreen};
- setFrameImmediateDraw(frames);
- }
-}
-
#ifdef USE_EINK
/// Determine which screensaver frame to use, then set the FrameCallback
void Screen::setScreensaverFrames(FrameCallback einkScreensaver)
@@ -2059,7 +830,7 @@ void Screen::setScreensaverFrames(FrameCallback einkScreensaver)
// Else, display the usual "overlay" screensaver
else {
- screensaverOverlay = drawScreensaverOverlay;
+ screensaverOverlay = graphics::UIRenderer::drawScreensaverOverlay;
ui->setOverlays(&screensaverOverlay, 1);
}
@@ -2095,125 +866,177 @@ void Screen::setScreensaverFrames(FrameCallback einkScreensaver)
void Screen::setFrames(FrameFocus focus)
{
uint8_t originalPosition = ui->getUiState()->currentFrame;
+ uint8_t previousFrameCount = framesetInfo.frameCount;
FramesetInfo fsi; // Location of specific frames, for applying focus parameter
LOG_DEBUG("Show standard frames");
showingNormalScreen = true;
+ indicatorIcons.clear();
+
+ size_t numframes = 0;
+
+ // If we have a critical fault, show it first
+ fsi.positions.fault = numframes;
+ if (error_code) {
+ normalFrames[numframes++] = NotificationRenderer::drawCriticalFaultFrame;
+ indicatorIcons.push_back(icon_error);
+ focus = FOCUS_FAULT; // Change our "focus" parameter, to ensure we show the fault frame
+ }
+
+#if defined(DISPLAY_CLOCK_FRAME)
+ fsi.positions.clock = numframes;
+ normalFrames[numframes++] = uiconfig.is_clockface_analog ? graphics::ClockRenderer::drawAnalogClockFrame
+ : graphics::ClockRenderer::drawDigitalClockFrame;
+ indicatorIcons.push_back(digital_icon_clock);
+#endif
+
+ // Declare this early so itโs available in FOCUS_PRESERVE block
+ bool willInsertTextMessage = shouldDrawMessage(&devicestate.rx_text_message);
+
+ fsi.positions.home = numframes;
+ normalFrames[numframes++] = graphics::UIRenderer::drawDeviceFocused;
+ indicatorIcons.push_back(icon_home);
+
+ fsi.positions.textMessage = numframes;
+ normalFrames[numframes++] = graphics::MessageRenderer::drawTextMessageFrame;
+ indicatorIcons.push_back(icon_mail);
+
+#ifndef USE_EINK
+ fsi.positions.nodelist = numframes;
+ normalFrames[numframes++] = graphics::NodeListRenderer::drawDynamicNodeListScreen;
+ indicatorIcons.push_back(icon_nodes);
+#endif
+
+// Show detailed node views only on E-Ink builds
#ifdef USE_EINK
- // If user has disabled the screensaver, warn them after boot
- static bool warnedScreensaverDisabled = false;
- if (config.display.screen_on_secs == 0 && !warnedScreensaverDisabled) {
- screen->print("Screensaver disabled\n");
- warnedScreensaverDisabled = true;
+ fsi.positions.nodelist_lastheard = numframes;
+ normalFrames[numframes++] = graphics::NodeListRenderer::drawLastHeardScreen;
+ indicatorIcons.push_back(icon_nodes);
+
+ fsi.positions.nodelist_hopsignal = numframes;
+ normalFrames[numframes++] = graphics::NodeListRenderer::drawHopSignalScreen;
+ indicatorIcons.push_back(icon_signal);
+
+ fsi.positions.nodelist_distance = numframes;
+ normalFrames[numframes++] = graphics::NodeListRenderer::drawDistanceScreen;
+ indicatorIcons.push_back(icon_distance);
+#endif
+#if HAS_GPS
+ fsi.positions.nodelist_bearings = numframes;
+ normalFrames[numframes++] = graphics::NodeListRenderer::drawNodeListWithCompasses;
+ indicatorIcons.push_back(icon_list);
+
+ fsi.positions.gps = numframes;
+ normalFrames[numframes++] = graphics::UIRenderer::drawCompassAndLocationScreen;
+ indicatorIcons.push_back(icon_compass);
+#endif
+ if (RadioLibInterface::instance) {
+ fsi.positions.lora = numframes;
+ normalFrames[numframes++] = graphics::DebugRenderer::drawLoRaFocused;
+ indicatorIcons.push_back(icon_radio);
+ }
+ if (!dismissedFrames.memory) {
+ fsi.positions.memory = numframes;
+ normalFrames[numframes++] = graphics::DebugRenderer::drawMemoryUsage;
+ indicatorIcons.push_back(icon_memory);
+ }
+#if !defined(DISPLAY_CLOCK_FRAME)
+ fsi.positions.clock = numframes;
+ normalFrames[numframes++] = uiconfig.is_clockface_analog ? graphics::ClockRenderer::drawAnalogClockFrame
+ : graphics::ClockRenderer::drawDigitalClockFrame;
+ indicatorIcons.push_back(digital_icon_clock);
+#endif
+
+#if HAS_WIFI && !defined(ARCH_PORTDUINO)
+ if (!dismissedFrames.wifi && isWifiAvailable()) {
+ fsi.positions.wifi = numframes;
+ normalFrames[numframes++] = graphics::DebugRenderer::drawDebugInfoWiFiTrampoline;
+ indicatorIcons.push_back(icon_wifi);
}
#endif
- moduleFrames = MeshModule::GetMeshModulesWithUIFrames();
+ // Beware of what changes you make in this code!
+ // We pass numframes into GetMeshModulesWithUIFrames() which is highly important!
+ // Inside of that callback, goes over to MeshModule.cpp and we run
+ // modulesWithUIFrames.resize(startIndex, nullptr), to insert nullptr
+ // entries until we're ready to start building the matching entries.
+ // We are doing our best to keep the normalFrames vector
+ // and the moduleFrames vector in lock step.
+ moduleFrames = MeshModule::GetMeshModulesWithUIFrames(numframes);
LOG_DEBUG("Show %d module frames", moduleFrames.size());
-#ifdef DEBUG_PORT
- int totalFrameCount = MAX_NUM_NODES + NUM_EXTRA_FRAMES + moduleFrames.size();
- LOG_DEBUG("Total frame count: %d", totalFrameCount);
-#endif
+
+ for (auto i = moduleFrames.begin(); i != moduleFrames.end(); ++i) {
+ // Draw the module frame, using the hack described above
+ if (*i != nullptr) {
+ normalFrames[numframes] = drawModuleFrame;
+
+ // Check if the module being drawn has requested focus
+ // We will honor this request later, if setFrames was triggered by a UIFrameEvent
+ MeshModule *m = *i;
+ if (m && m->isRequestingFocus())
+ fsi.positions.focusedModule = numframes;
+ if (m && m == waypointModule)
+ fsi.positions.waypoint = numframes;
+
+ indicatorIcons.push_back(icon_module);
+ numframes++;
+ }
+ }
+
+ LOG_DEBUG("Added modules. numframes: %d", numframes);
// We don't show the node info of our node (if we have it yet - we should)
size_t numMeshNodes = nodeDB->getNumMeshNodes();
if (numMeshNodes > 0)
numMeshNodes--;
- size_t numframes = 0;
+ // Temporary array to hold favorite node frames
+ std::vector favoriteFrames;
- // put all of the module frames first.
- // this is a little bit of a dirty hack; since we're going to call
- // the same drawModuleFrame handler here for all of these module frames
- // and then we'll just assume that the state->currentFrame value
- // is the same offset into the moduleFrames vector
- // so that we can invoke the module's callback
- for (auto i = moduleFrames.begin(); i != moduleFrames.end(); ++i) {
- // Draw the module frame, using the hack described above
- normalFrames[numframes] = drawModuleFrame;
-
- // Check if the module being drawn has requested focus
- // We will honor this request later, if setFrames was triggered by a UIFrameEvent
- MeshModule *m = *i;
- if (m->isRequestingFocus()) {
- fsi.positions.focusedModule = numframes;
+ for (size_t i = 0; i < nodeDB->getNumMeshNodes(); i++) {
+ const meshtastic_NodeInfoLite *n = nodeDB->getMeshNodeByIndex(i);
+ if (n && n->num != nodeDB->getNodeNum() && n->is_favorite) {
+ favoriteFrames.push_back(graphics::UIRenderer::drawNodeInfo);
}
-
- // Identify the position of specific modules, if we need to know this later
- if (m == waypointModule)
- fsi.positions.waypoint = numframes;
-
- numframes++;
}
- LOG_DEBUG("Added modules. numframes: %d", numframes);
-
- // If we have a critical fault, show it first
- fsi.positions.fault = numframes;
- if (error_code) {
- normalFrames[numframes++] = drawCriticalFaultFrame;
- focus = FOCUS_FAULT; // Change our "focus" parameter, to ensure we show the fault frame
+ // Insert favorite frames *after* collecting them all
+ if (!favoriteFrames.empty()) {
+ fsi.positions.firstFavorite = numframes;
+ for (auto &f : favoriteFrames) {
+ normalFrames[numframes++] = f;
+ indicatorIcons.push_back(icon_node);
+ }
+ fsi.positions.lastFavorite = numframes - 1;
+ } else {
+ fsi.positions.firstFavorite = 255;
+ fsi.positions.lastFavorite = 255;
}
-#if defined(DISPLAY_CLOCK_FRAME)
- normalFrames[numframes++] = screen->digitalWatchFace ? &Screen::drawDigitalClockFrame : &Screen::drawAnalogClockFrame;
-#endif
-
- // If we have a text message - show it next, unless it's a phone message and we aren't using any special modules
- if (devicestate.has_rx_text_message && shouldDrawMessage(&devicestate.rx_text_message)) {
- fsi.positions.textMessage = numframes;
- normalFrames[numframes++] = drawTextMessageFrame;
- }
-
- // then all the nodes
- // We only show a few nodes in our scrolling list - because meshes with many nodes would have too many screens
- size_t numToShow = min(numMeshNodes, 4U);
- for (size_t i = 0; i < numToShow; i++)
- normalFrames[numframes++] = drawNodeInfo;
-
- // then the debug info
- //
- // Since frames are basic function pointers, we have to use a helper to
- // call a method on debugInfo object.
- fsi.positions.log = numframes;
- normalFrames[numframes++] = &Screen::drawDebugInfoTrampoline;
-
- // call a method on debugInfoScreen object (for more details)
- fsi.positions.settings = numframes;
- normalFrames[numframes++] = &Screen::drawDebugInfoSettingsTrampoline;
-
- fsi.positions.wifi = numframes;
-#if HAS_WIFI && !defined(ARCH_PORTDUINO)
- if (isWifiAvailable()) {
- // call a method on debugInfoScreen object (for more details)
- normalFrames[numframes++] = &Screen::drawDebugInfoWiFiTrampoline;
- }
-#endif
-
- fsi.frameCount = numframes; // Total framecount is used to apply FOCUS_PRESERVE
+ fsi.frameCount = numframes; // Total framecount is used to apply FOCUS_PRESERVE
+ this->frameCount = numframes; // โ
Save frame count for use in custom overlay
LOG_DEBUG("Finished build frames. numframes: %d", numframes);
ui->setFrames(normalFrames, numframes);
- ui->enableAllIndicators();
+ ui->disableAllIndicators();
- // Add function overlay here. This can show when notifications muted, modifier key is active etc
- static OverlayCallback functionOverlay[] = {drawFunctionOverlay};
- static const int functionOverlayCount = sizeof(functionOverlay) / sizeof(functionOverlay[0]);
- ui->setOverlays(functionOverlay, functionOverlayCount);
+ // Add overlays: frame icons and alert banner)
+ static OverlayCallback overlays[] = {graphics::UIRenderer::drawNavigationBar, NotificationRenderer::drawBannercallback};
+ ui->setOverlays(overlays, sizeof(overlays) / sizeof(overlays[0]));
- prevFrame = -1; // Force drawNodeInfo to pick a new node (because our list
- // just changed)
+ prevFrame = -1; // Force drawNodeInfo to pick a new node (because our list just changed)
// Focus on a specific frame, in the frame set we just created
switch (focus) {
case FOCUS_DEFAULT:
- ui->switchToFrame(0); // First frame
+ ui->switchToFrame(fsi.positions.deviceFocused);
break;
case FOCUS_FAULT:
ui->switchToFrame(fsi.positions.fault);
break;
case FOCUS_TEXTMESSAGE:
+ hasUnreadMessage = false; // โ
Clear when message is *viewed*
ui->switchToFrame(fsi.positions.textMessage);
break;
case FOCUS_MODULE:
@@ -2221,33 +1044,24 @@ void Screen::setFrames(FrameFocus focus)
// If no module requested focus, will show the first frame instead
ui->switchToFrame(fsi.positions.focusedModule);
break;
+ case FOCUS_CLOCK:
+ // Whichever frame was marked by MeshModule::requestFocus(), if any
+ // If no module requested focus, will show the first frame instead
+ ui->switchToFrame(fsi.positions.clock);
+ break;
+ case FOCUS_SYSTEM:
+ ui->switchToFrame(fsi.positions.memory);
+ break;
case FOCUS_PRESERVE:
- // If we can identify which type of frame "originalPosition" was, can move directly to it in the new frameset
- const FramesetInfo &oldFsi = this->framesetInfo;
- if (originalPosition == oldFsi.positions.log)
- ui->switchToFrame(fsi.positions.log);
- else if (originalPosition == oldFsi.positions.settings)
- ui->switchToFrame(fsi.positions.settings);
- else if (originalPosition == oldFsi.positions.wifi)
- ui->switchToFrame(fsi.positions.wifi);
-
- // If frame count has decreased
- else if (fsi.frameCount < oldFsi.frameCount) {
- uint8_t numDropped = oldFsi.frameCount - fsi.frameCount;
- // Move n frames backwards
- if (numDropped <= originalPosition)
- ui->switchToFrame(originalPosition - numDropped);
- // Unless that would put us "out of bounds" (< 0)
- else
- ui->switchToFrame(0);
- }
-
- // If we're not sure exactly which frame we were on, at least return to the same frame number
- // (node frames; module frames)
- else
+ // No more adjustment โ force stay on same index
+ if (previousFrameCount > fsi.frameCount) {
+ ui->switchToFrame(originalPosition - 1);
+ } else if (previousFrameCount < fsi.frameCount) {
+ ui->switchToFrame(originalPosition + 1);
+ } else {
ui->switchToFrame(originalPosition);
-
+ }
break;
}
@@ -2275,18 +1089,25 @@ void Screen::dismissCurrentFrame()
if (currentFrame == framesetInfo.positions.textMessage && devicestate.has_rx_text_message) {
LOG_INFO("Dismiss Text Message");
devicestate.has_rx_text_message = false;
- dismissed = true;
- }
-
- else if (currentFrame == framesetInfo.positions.waypoint && devicestate.has_rx_waypoint) {
+ memset(&devicestate.rx_text_message, 0, sizeof(devicestate.rx_text_message));
+ } else if (currentFrame == framesetInfo.positions.waypoint && devicestate.has_rx_waypoint) {
LOG_DEBUG("Dismiss Waypoint");
devicestate.has_rx_waypoint = false;
+ dismissedFrames.waypoint = true;
+ dismissed = true;
+ } else if (currentFrame == framesetInfo.positions.wifi) {
+ LOG_DEBUG("Dismiss WiFi Screen");
+ dismissedFrames.wifi = true;
+ dismissed = true;
+ } else if (currentFrame == framesetInfo.positions.memory) {
+ LOG_INFO("Dismiss Memory");
+ dismissedFrames.memory = true;
dismissed = true;
}
- // If we did make changes to dismiss, we now need to regenerate the frameset
- if (dismissed)
- setFrames();
+ if (dismissed) {
+ setFrames(FOCUS_DEFAULT); // You could also use FOCUS_PRESERVE
+ }
}
void Screen::handleStartFirmwareUpdateScreen()
@@ -2295,7 +1116,7 @@ void Screen::handleStartFirmwareUpdateScreen()
showingNormalScreen = false;
EINK_ADD_FRAMEFLAG(dispdev, DEMAND_FAST); // E-Ink: Explicitly use fast-refresh for next frame
- static FrameCallback frames[] = {drawFrameFirmware};
+ static FrameCallback frames[] = {graphics::NotificationRenderer::drawFrameFirmware};
setFrameImmediateDraw(frames);
}
@@ -2362,41 +1183,8 @@ void Screen::removeFunctionSymbol(std::string sym)
setFastFramerate();
}
-std::string Screen::drawTimeDelta(uint32_t days, uint32_t hours, uint32_t minutes, uint32_t seconds)
-{
- std::string uptime;
-
- if (days > (hours_in_month * 6))
- uptime = "?";
- else if (days >= 2)
- uptime = std::to_string(days) + "d";
- else if (hours >= 2)
- uptime = std::to_string(hours) + "h";
- else if (minutes >= 1)
- uptime = std::to_string(minutes) + "m";
- else
- uptime = std::to_string(seconds) + "s";
- return uptime;
-}
-
-void Screen::handlePrint(const char *text)
-{
- // the string passed into us probably has a newline, but that would confuse the logging system
- // so strip it
- LOG_DEBUG("Screen: %.*s", strlen(text) - 1, text);
- if (!useDisplay || !showingNormalScreen)
- return;
-
- dispdev->print(text);
-}
-
void Screen::handleOnPress()
{
- // If Canned Messages is using the "Scan and Select" input, dismiss the canned message frame when user button is pressed
- // Minimize impact as a courtesy, as "scan and select" may be used as default config for some boards
- if (scanAndSelectInput != nullptr && scanAndSelectInput->dismissCannedMessageFrame())
- return;
-
// If screen was off, just wake it, otherwise advance to next frame
// If we are in a transition, the press must have bounced, drop it.
if (ui->getUiState()->frameState == FIXED) {
@@ -2442,321 +1230,6 @@ void Screen::setFastFramerate()
runASAP = true;
}
-void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
-{
- display->setFont(FONT_SMALL);
-
- // The coordinates define the left starting point of the text
- display->setTextAlignment(TEXT_ALIGN_LEFT);
-
- if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_INVERTED) {
- display->fillRect(0 + x, 0 + y, x + display->getWidth(), y + FONT_HEIGHT_SMALL);
- display->setColor(BLACK);
- }
-
- char channelStr[20];
- {
- concurrency::LockGuard guard(&lock);
- snprintf(channelStr, sizeof(channelStr), "#%s", channels.getName(channels.getPrimaryIndex()));
- }
-
- // Display power status
- if (powerStatus->getHasBattery()) {
- if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_DEFAULT) {
- drawBattery(display, x, y + 2, imgBattery, powerStatus);
- } else {
- drawBattery(display, x + 1, y + 3, imgBattery, powerStatus);
- }
- } else if (powerStatus->knowsUSB()) {
- if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_DEFAULT) {
- display->drawFastImage(x, y + 2, 16, 8, powerStatus->getHasUSB() ? imgUSB : imgPower);
- } else {
- display->drawFastImage(x + 1, y + 3, 16, 8, powerStatus->getHasUSB() ? imgUSB : imgPower);
- }
- }
- // Display nodes status
- if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_DEFAULT) {
- drawNodes(display, x + (SCREEN_WIDTH * 0.25), y + 2, nodeStatus);
- } else {
- drawNodes(display, x + (SCREEN_WIDTH * 0.25), y + 3, nodeStatus);
- }
-#if HAS_GPS
- // Display GPS status
- if (config.position.gps_mode != meshtastic_Config_PositionConfig_GpsMode_ENABLED) {
- drawGPSpowerstat(display, x, y + 2, gpsStatus);
- } else {
- if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_DEFAULT) {
- drawGPS(display, x + (SCREEN_WIDTH * 0.63), y + 2, gpsStatus);
- } else {
- drawGPS(display, x + (SCREEN_WIDTH * 0.63), y + 3, gpsStatus);
- }
- }
-#endif
- display->setColor(WHITE);
- // Draw the channel name
- display->drawString(x, y + FONT_HEIGHT_SMALL, channelStr);
- // Draw our hardware ID to assist with bluetooth pairing. Either prefix with Info or S&F Logo
- if (moduleConfig.store_forward.enabled) {
-#ifdef ARCH_ESP32
- if (!Throttle::isWithinTimespanMs(storeForwardModule->lastHeartbeat,
- (storeForwardModule->heartbeatInterval * 1200))) { // no heartbeat, overlap a bit
-#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \
- defined(ST7789_CS) || defined(USE_ST7789) || defined(HX8357_CS) || defined(ILI9488_CS) || ARCH_PORTDUINO) && \
- !defined(DISPLAY_FORCE_SMALL_FONTS)
- display->drawFastImage(x + SCREEN_WIDTH - 14 - display->getStringWidth(ourId), y + 3 + FONT_HEIGHT_SMALL, 12, 8,
- imgQuestionL1);
- display->drawFastImage(x + SCREEN_WIDTH - 14 - display->getStringWidth(ourId), y + 11 + FONT_HEIGHT_SMALL, 12, 8,
- imgQuestionL2);
-#else
- display->drawFastImage(x + SCREEN_WIDTH - 10 - display->getStringWidth(ourId), y + 2 + FONT_HEIGHT_SMALL, 8, 8,
- imgQuestion);
-#endif
- } else {
-#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \
- defined(ST7789_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(HX8357_CS)) && \
- !defined(DISPLAY_FORCE_SMALL_FONTS)
- display->drawFastImage(x + SCREEN_WIDTH - 18 - display->getStringWidth(ourId), y + 3 + FONT_HEIGHT_SMALL, 16, 8,
- imgSFL1);
- display->drawFastImage(x + SCREEN_WIDTH - 18 - display->getStringWidth(ourId), y + 11 + FONT_HEIGHT_SMALL, 16, 8,
- imgSFL2);
-#else
- display->drawFastImage(x + SCREEN_WIDTH - 13 - display->getStringWidth(ourId), y + 2 + FONT_HEIGHT_SMALL, 11, 8,
- imgSF);
-#endif
- }
-#endif
- } else {
- // TODO: Raspberry Pi supports more than just the one screen size
-#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \
- defined(ST7789_CS) || defined(USE_ST7789) || defined(HX8357_CS) || defined(ILI9488_CS) || ARCH_PORTDUINO) && \
- !defined(DISPLAY_FORCE_SMALL_FONTS)
- display->drawFastImage(x + SCREEN_WIDTH - 14 - display->getStringWidth(ourId), y + 3 + FONT_HEIGHT_SMALL, 12, 8,
- imgInfoL1);
- display->drawFastImage(x + SCREEN_WIDTH - 14 - display->getStringWidth(ourId), y + 11 + FONT_HEIGHT_SMALL, 12, 8,
- imgInfoL2);
-#else
- display->drawFastImage(x + SCREEN_WIDTH - 10 - display->getStringWidth(ourId), y + 2 + FONT_HEIGHT_SMALL, 8, 8, imgInfo);
-#endif
- }
-
- display->drawString(x + SCREEN_WIDTH - display->getStringWidth(ourId), y + FONT_HEIGHT_SMALL, ourId);
-
- // Draw any log messages
- display->drawLogBuffer(x, y + (FONT_HEIGHT_SMALL * 2));
-
- /* Display a heartbeat pixel that blinks every time the frame is redrawn */
-#ifdef SHOW_REDRAWS
- if (heartbeat)
- display->setPixel(0, 0);
- heartbeat = !heartbeat;
-#endif
-}
-
-// Jm
-void DebugInfo::drawFrameWiFi(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
-{
-#if HAS_WIFI && !defined(ARCH_PORTDUINO)
- const char *wifiName = config.network.wifi_ssid;
-
- display->setFont(FONT_SMALL);
-
- // The coordinates define the left starting point of the text
- display->setTextAlignment(TEXT_ALIGN_LEFT);
-
- if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_INVERTED) {
- display->fillRect(0 + x, 0 + y, x + display->getWidth(), y + FONT_HEIGHT_SMALL);
- display->setColor(BLACK);
- }
-
- if (WiFi.status() != WL_CONNECTED) {
- display->drawString(x, y, String("WiFi: Not Connected"));
- if (config.display.heading_bold)
- display->drawString(x + 1, y, String("WiFi: Not Connected"));
- } else {
- display->drawString(x, y, String("WiFi: Connected"));
- if (config.display.heading_bold)
- display->drawString(x + 1, y, String("WiFi: Connected"));
-
- display->drawString(x + SCREEN_WIDTH - display->getStringWidth("RSSI " + String(WiFi.RSSI())), y,
- "RSSI " + String(WiFi.RSSI()));
- if (config.display.heading_bold) {
- display->drawString(x + SCREEN_WIDTH - display->getStringWidth("RSSI " + String(WiFi.RSSI())) - 1, y,
- "RSSI " + String(WiFi.RSSI()));
- }
- }
-
- display->setColor(WHITE);
-
- /*
- - WL_CONNECTED: assigned when connected to a WiFi network;
- - WL_NO_SSID_AVAIL: assigned when no SSID are available;
- - WL_CONNECT_FAILED: assigned when the connection fails for all the attempts;
- - WL_CONNECTION_LOST: assigned when the connection is lost;
- - WL_DISCONNECTED: assigned when disconnected from a network;
- - WL_IDLE_STATUS: it is a temporary status assigned when WiFi.begin() is called and remains active until the number of
- attempts expires (resulting in WL_CONNECT_FAILED) or a connection is established (resulting in WL_CONNECTED);
- - WL_SCAN_COMPLETED: assigned when the scan networks is completed;
- - WL_NO_SHIELD: assigned when no WiFi shield is present;
-
- */
- if (WiFi.status() == WL_CONNECTED) {
- display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "IP: " + String(WiFi.localIP().toString().c_str()));
- } else if (WiFi.status() == WL_NO_SSID_AVAIL) {
- display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "SSID Not Found");
- } else if (WiFi.status() == WL_CONNECTION_LOST) {
- display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "Connection Lost");
- } else if (WiFi.status() == WL_CONNECT_FAILED) {
- display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "Connection Failed");
- } else if (WiFi.status() == WL_IDLE_STATUS) {
- display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "Idle ... Reconnecting");
- }
-#ifdef ARCH_ESP32
- else {
- // Codes:
- // https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/wifi.html#wi-fi-reason-code
- display->drawString(x, y + FONT_HEIGHT_SMALL * 1,
- WiFi.disconnectReasonName(static_cast(getWifiDisconnectReason())));
- }
-#else
- else {
- display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "Unkown status: " + String(WiFi.status()));
- }
-#endif
-
- display->drawString(x, y + FONT_HEIGHT_SMALL * 2, "SSID: " + String(wifiName));
-
- display->drawString(x, y + FONT_HEIGHT_SMALL * 3, "http://meshtastic.local");
-
- /* Display a heartbeat pixel that blinks every time the frame is redrawn */
-#ifdef SHOW_REDRAWS
- if (heartbeat)
- display->setPixel(0, 0);
- heartbeat = !heartbeat;
-#endif
-#endif
-}
-
-void DebugInfo::drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
-{
- display->setFont(FONT_SMALL);
-
- // The coordinates define the left starting point of the text
- display->setTextAlignment(TEXT_ALIGN_LEFT);
-
- if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_INVERTED) {
- display->fillRect(0 + x, 0 + y, x + display->getWidth(), y + FONT_HEIGHT_SMALL);
- display->setColor(BLACK);
- }
-
- char batStr[20];
- if (powerStatus->getHasBattery()) {
- int batV = powerStatus->getBatteryVoltageMv() / 1000;
- int batCv = (powerStatus->getBatteryVoltageMv() % 1000) / 10;
-
- snprintf(batStr, sizeof(batStr), "B %01d.%02dV %3d%% %c%c", batV, batCv, powerStatus->getBatteryChargePercent(),
- powerStatus->getIsCharging() ? '+' : ' ', powerStatus->getHasUSB() ? 'U' : ' ');
-
- // Line 1
- display->drawString(x, y, batStr);
- if (config.display.heading_bold)
- display->drawString(x + 1, y, batStr);
- } else {
- // Line 1
- display->drawString(x, y, String("USB"));
- if (config.display.heading_bold)
- display->drawString(x + 1, y, String("USB"));
- }
-
- // auto mode = DisplayFormatters::getModemPresetDisplayName(config.lora.modem_preset, true);
-
- // display->drawString(x + SCREEN_WIDTH - display->getStringWidth(mode), y, mode);
- // if (config.display.heading_bold)
- // display->drawString(x + SCREEN_WIDTH - display->getStringWidth(mode) - 1, y, mode);
-
- uint32_t currentMillis = millis();
- uint32_t seconds = currentMillis / 1000;
- uint32_t minutes = seconds / 60;
- uint32_t hours = minutes / 60;
- uint32_t days = hours / 24;
- // currentMillis %= 1000;
- // seconds %= 60;
- // minutes %= 60;
- // hours %= 24;
-
- // Show uptime as days, hours, minutes OR seconds
- std::string uptime = screen->drawTimeDelta(days, hours, minutes, seconds);
-
- // Line 1 (Still)
- display->drawString(x + SCREEN_WIDTH - display->getStringWidth(uptime.c_str()), y, uptime.c_str());
- if (config.display.heading_bold)
- display->drawString(x - 1 + SCREEN_WIDTH - display->getStringWidth(uptime.c_str()), y, uptime.c_str());
-
- display->setColor(WHITE);
-
- // Setup string to assemble analogClock string
- std::string analogClock = "";
-
- uint32_t rtc_sec = getValidTime(RTCQuality::RTCQualityDevice, true); // Display local timezone
- if (rtc_sec > 0) {
- long hms = rtc_sec % SEC_PER_DAY;
- // hms += tz.tz_dsttime * SEC_PER_HOUR;
- // hms -= tz.tz_minuteswest * SEC_PER_MIN;
- // mod `hms` to ensure in positive range of [0...SEC_PER_DAY)
- hms = (hms + SEC_PER_DAY) % SEC_PER_DAY;
-
- // Tear apart hms into h:m:s
- int hour = hms / SEC_PER_HOUR;
- int min = (hms % SEC_PER_HOUR) / SEC_PER_MIN;
- int sec = (hms % SEC_PER_HOUR) % SEC_PER_MIN; // or hms % SEC_PER_MIN
-
- char timebuf[12];
-
- if (config.display.use_12h_clock) {
- std::string meridiem = "am";
- if (hour >= 12) {
- if (hour > 12)
- hour -= 12;
- meridiem = "pm";
- }
- if (hour == 00) {
- hour = 12;
- }
- snprintf(timebuf, sizeof(timebuf), "%d:%02d:%02d%s", hour, min, sec, meridiem.c_str());
- } else {
- snprintf(timebuf, sizeof(timebuf), "%02d:%02d:%02d", hour, min, sec);
- }
- analogClock += timebuf;
- }
-
- // Line 2
- display->drawString(x, y + FONT_HEIGHT_SMALL * 1, analogClock.c_str());
-
- // Display Channel Utilization
- char chUtil[13];
- snprintf(chUtil, sizeof(chUtil), "ChUtil %2.0f%%", airTime->channelUtilizationPercent());
- display->drawString(x + SCREEN_WIDTH - display->getStringWidth(chUtil), y + FONT_HEIGHT_SMALL * 1, chUtil);
-
-#if HAS_GPS
- if (config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_ENABLED) {
- // Line 3
- if (config.display.gps_format !=
- meshtastic_Config_DisplayConfig_GpsCoordinateFormat_DMS) // if DMS then don't draw altitude
- drawGPSAltitude(display, x, y + FONT_HEIGHT_SMALL * 2, gpsStatus);
-
- // Line 4
- drawGPScoordinates(display, x, y + FONT_HEIGHT_SMALL * 3, gpsStatus);
- } else {
- drawGPSpowerstat(display, x, y + FONT_HEIGHT_SMALL * 2, gpsStatus);
- }
-#endif
-/* Display a heartbeat pixel that blinks every time the frame is redrawn */
-#ifdef SHOW_REDRAWS
- if (heartbeat)
- display->setPixel(0, 0);
- heartbeat = !heartbeat;
-#endif
-}
-
int Screen::handleStatusUpdate(const meshtastic::Status *arg)
{
// LOG_DEBUG("Screen got status update %d", arg->getStatusType());
@@ -2772,16 +1245,63 @@ int Screen::handleStatusUpdate(const meshtastic::Status *arg)
return 0;
}
+// Handles when message is received; will jump to text message frame.
int Screen::handleTextMessage(const meshtastic_MeshPacket *packet)
{
if (showingNormalScreen) {
- // Outgoing message
- if (packet->from == 0)
- setFrames(FOCUS_PRESERVE); // Return to same frame (quietly hiding the rx text message frame)
+ if (packet->from == 0) {
+ // Outgoing message (likely sent from phone)
+ devicestate.has_rx_text_message = false;
+ memset(&devicestate.rx_text_message, 0, sizeof(devicestate.rx_text_message));
+ dismissedFrames.textMessage = true;
+ hasUnreadMessage = false; // Clear unread state when user replies
- // Incoming message
- else
- setFrames(FOCUS_TEXTMESSAGE); // Focus on the new message
+ setFrames(FOCUS_PRESERVE); // Stay on same frame, silently update frame list
+ } else {
+ // Incoming message
+ devicestate.has_rx_text_message = true; // Needed to include the message frame
+ hasUnreadMessage = true; // Enables mail icon in the header
+ setFrames(FOCUS_PRESERVE); // Refresh frame list without switching view
+
+ // Only wake/force display if the configuration allows it
+ if (shouldWakeOnReceivedMessage()) {
+ setOn(true); // Wake up the screen first
+ forceDisplay(); // Forces screen redraw
+
+ // === Prepare banner content ===
+ const meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(packet->from);
+ const char *longName = (node && node->has_user) ? node->user.long_name : nullptr;
+
+ const char *msgRaw = reinterpret_cast(packet->decoded.payload.bytes);
+
+ char banner[256];
+
+ // Check for bell character in message to determine alert type
+ bool isAlert = false;
+ for (size_t i = 0; i < packet->decoded.payload.size && i < 100; i++) {
+ if (msgRaw[i] == '\x07') {
+ isAlert = true;
+ break;
+ }
+ }
+
+ if (isAlert) {
+ if (longName && longName[0]) {
+ snprintf(banner, sizeof(banner), "Alert Received from\n%s", longName);
+ } else {
+ strcpy(banner, "Alert Received");
+ }
+ } else {
+ if (longName && longName[0]) {
+ snprintf(banner, sizeof(banner), "New Message from\n%s", longName);
+ } else {
+ strcpy(banner, "New Message");
+ }
+ }
+
+ screen->showSimpleBanner(banner, 3000);
+ }
+ }
}
return 0;
@@ -2809,20 +1329,25 @@ int Screen::handleUIFrameEvent(const UIFrameEvent *event)
int Screen::handleInputEvent(const InputEvent *event)
{
+ if (!screenOn)
+ return 0;
-#if defined(DISPLAY_CLOCK_FRAME)
- // For the T-Watch, intercept touches to the 'toggle digital/analog watch face' button
- uint8_t watchFaceFrame = error_code ? 1 : 0;
-
- if (this->ui->getUiState()->currentFrame == watchFaceFrame && event->touchX >= 204 && event->touchX <= 240 &&
- event->touchY >= 204 && event->touchY <= 240) {
- screen->digitalWatchFace = !screen->digitalWatchFace;
-
- setFrames();
+#ifdef USE_EINK // the screen is the last input handler, so if an event makes it here, we can assume it will prompt a screen draw.
+ EINK_ADD_FRAMEFLAG(dispdev, DEMAND_FAST); // Use fast-refresh for next frame, no skip please
+ EINK_ADD_FRAMEFLAG(dispdev, BLOCKING); // Edge case: if this frame is promoted to COSMETIC, wait for update
+ handleSetOn(true); // Ensure power-on to receive deep-sleep screensaver (PowerFSM should handle?)
+ setFastFramerate(); // Draw ASAP
+#endif
+ if (NotificationRenderer::isOverlayBannerShowing()) {
+ NotificationRenderer::inEvent = *event;
+ static OverlayCallback overlays[] = {graphics::UIRenderer::drawNavigationBar, NotificationRenderer::drawBannercallback};
+ ui->setOverlays(overlays, sizeof(overlays) / sizeof(overlays[0]));
+ setFastFramerate(); // Draw ASAP
+ ui->update();
+ menuHandler::handleMenuSwitch(dispdev);
return 0;
}
-#endif
// Use left or right input from a keyboard to move between frames,
// so long as a mesh module isn't using these events for some other purpose
@@ -2831,28 +1356,64 @@ int Screen::handleInputEvent(const InputEvent *event)
// Ask any MeshModules if they're handling keyboard input right now
bool inputIntercepted = false;
for (MeshModule *module : moduleFrames) {
- if (module->interceptingKeyboardInput())
+ if (module && module->interceptingKeyboardInput())
inputIntercepted = true;
}
// If no modules are using the input, move between frames
if (!inputIntercepted) {
- if (event->inputEvent == static_cast(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_LEFT))
+ if (event->inputEvent == INPUT_BROKER_LEFT || event->inputEvent == INPUT_BROKER_ALT_PRESS) {
showPrevFrame();
- else if (event->inputEvent == static_cast(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT))
+ } else if (event->inputEvent == INPUT_BROKER_RIGHT || event->inputEvent == INPUT_BROKER_USER_PRESS) {
showNextFrame();
+ } else if (event->inputEvent == INPUT_BROKER_SELECT) {
+ if (this->ui->getUiState()->currentFrame == framesetInfo.positions.home) {
+ menuHandler::homeBaseMenu();
+ } else if (this->ui->getUiState()->currentFrame == framesetInfo.positions.memory) {
+ menuHandler::systemBaseMenu();
+#if HAS_GPS
+ } else if (this->ui->getUiState()->currentFrame == framesetInfo.positions.gps && gps) {
+ menuHandler::positionBaseMenu();
+#endif
+ } else if (this->ui->getUiState()->currentFrame == framesetInfo.positions.clock) {
+ menuHandler::clockMenu();
+ } else if (this->ui->getUiState()->currentFrame == framesetInfo.positions.lora) {
+ menuHandler::LoraRegionPicker();
+ } else if (this->ui->getUiState()->currentFrame == framesetInfo.positions.textMessage &&
+ devicestate.rx_text_message.from) {
+ menuHandler::messageResponseMenu();
+ } else if (framesetInfo.positions.firstFavorite != 255 &&
+ this->ui->getUiState()->currentFrame >= framesetInfo.positions.firstFavorite &&
+ this->ui->getUiState()->currentFrame <= framesetInfo.positions.lastFavorite) {
+ menuHandler::favoriteBaseMenu();
+ } else if (this->ui->getUiState()->currentFrame == framesetInfo.positions.nodelist ||
+ this->ui->getUiState()->currentFrame == framesetInfo.positions.nodelist_lastheard ||
+ this->ui->getUiState()->currentFrame == framesetInfo.positions.nodelist_hopsignal ||
+ this->ui->getUiState()->currentFrame == framesetInfo.positions.nodelist_distance ||
+ this->ui->getUiState()->currentFrame == framesetInfo.positions.nodelist_hopsignal ||
+ this->ui->getUiState()->currentFrame == framesetInfo.positions.nodelist_bearings) {
+ menuHandler::nodeListMenu();
+ } else if (this->ui->getUiState()->currentFrame == framesetInfo.positions.wifi) {
+ menuHandler::wifiBaseMenu();
+ }
+ } else if (event->inputEvent == INPUT_BROKER_BACK) {
+ showPrevFrame();
+ } else if (event->inputEvent == INPUT_BROKER_CANCEL) {
+ setOn(false);
+ }
}
}
return 0;
}
-int Screen::handleAdminMessage(const meshtastic_AdminMessage *arg)
+int Screen::handleAdminMessage(AdminModule_ObserverData *arg)
{
- switch (arg->which_payload_variant) {
+ switch (arg->request->which_payload_variant) {
// Node removed manually (i.e. via app)
case meshtastic_AdminMessage_remove_by_nodenum_tag:
setFrames(FOCUS_PRESERVE);
+ *arg->result = AdminMessageHandleResult::HANDLED;
break;
// Default no-op, in case the admin message observable gets used by other classes in future
@@ -2862,7 +1423,33 @@ int Screen::handleAdminMessage(const meshtastic_AdminMessage *arg)
return 0;
}
+bool Screen::isOverlayBannerShowing()
+{
+ return NotificationRenderer::isOverlayBannerShowing();
+}
+
} // namespace graphics
+
#else
graphics::Screen::Screen(ScanI2C::DeviceAddress, meshtastic_Config_DisplayConfig_OledType, OLEDDISPLAY_GEOMETRY) {}
#endif // HAS_SCREEN
+
+bool shouldWakeOnReceivedMessage()
+{
+ /*
+ The goal here is to determine when we do NOT wake up the screen on message received:
+ - Any ext. notifications are turned on
+ - If role is not client / client_mute
+ - If the battery level is very low
+ */
+ if (moduleConfig.external_notification.enabled) {
+ return false;
+ }
+ if (!meshtastic_Config_DeviceConfig_Role_CLIENT && !meshtastic_Config_DeviceConfig_Role_CLIENT_MUTE) {
+ return false;
+ }
+ if (powerStatus && powerStatus->getBatteryChargePercent() < 10) {
+ return false;
+ }
+ return true;
+}
diff --git a/src/graphics/Screen.h b/src/graphics/Screen.h
index ce416156f..265900131 100644
--- a/src/graphics/Screen.h
+++ b/src/graphics/Screen.h
@@ -5,6 +5,28 @@
#include "detect/ScanI2C.h"
#include "mesh/generated/meshtastic/config.pb.h"
#include
+#include
+#include
+#include
+
+#define getStringCenteredX(s) ((SCREEN_WIDTH - display->getStringWidth(s)) / 2)
+namespace graphics
+{
+enum notificationTypeEnum { none, text_banner, selection_picker, node_picker, number_picker };
+
+struct BannerOverlayOptions {
+ const char *message;
+ uint32_t durationMs = 30000;
+ const char **optionsArrayPtr = nullptr;
+ const int *optionsEnumPtr = nullptr;
+ uint8_t optionsCount = 0;
+ std::function bannerCallback = nullptr;
+ int8_t InitialSelected = 0;
+ notificationTypeEnum notificationType = notificationTypeEnum::text_banner;
+};
+} // namespace graphics
+
+bool shouldWakeOnReceivedMessage();
#if !HAS_SCREEN
#include "power.h"
@@ -14,11 +36,20 @@ namespace graphics
class Screen
{
public:
+ enum FrameFocus : uint8_t {
+ FOCUS_DEFAULT, // No specific frame
+ FOCUS_PRESERVE, // Return to the previous frame
+ FOCUS_FAULT,
+ FOCUS_TEXTMESSAGE,
+ FOCUS_MODULE, // Note: target module should call requestFocus(), otherwise no info about which module to focus
+ FOCUS_CLOCK,
+ FOCUS_SYSTEM,
+ };
+
explicit Screen(ScanI2C::DeviceAddress, meshtastic_Config_DisplayConfig_OledType, OLEDDISPLAY_GEOMETRY);
void onPress() {}
void setup() {}
void setOn(bool) {}
- void print(const char *) {}
void doDeepSleep() {}
void forceDisplay(bool forceUiUpdate = false) {}
void startFirmwareUpdateScreen() {}
@@ -27,6 +58,9 @@ class Screen
void setFunctionSymbol(std::string) {}
void removeFunctionSymbol(std::string) {}
void startAlert(const char *) {}
+ void showSimpleBanner(const char *message, uint32_t durationMs = 0) {}
+ void showOverlayBanner(BannerOverlayOptions) {}
+ void setFrames(FrameFocus focus) {}
void endAlert() {}
};
} // namespace graphics
@@ -60,10 +94,13 @@ class Screen
#include "commands.h"
#include "concurrency/LockGuard.h"
#include "concurrency/OSThread.h"
+#include "graphics/draw/MenuHandler.h"
#include "input/InputBroker.h"
#include "mesh/MeshModule.h"
+#include "modules/AdminModule.h"
#include "power.h"
#include
+#include
// 0 to 255, though particular variants might define different defaults
#ifndef BRIGHTNESS_DEFAULT
@@ -90,7 +127,7 @@ class Screen
/// Convert an integer GPS coords to a floating point
#define DegD(i) (i * 1e-7)
-
+extern bool hasUnreadMessage;
namespace
{
/// A basic 2D point class for drawing
@@ -176,14 +213,31 @@ class Screen : public concurrency::OSThread
CallbackObserver(this, &Screen::handleUIFrameEvent); // Sent by Mesh Modules
CallbackObserver inputObserver =
CallbackObserver(this, &Screen::handleInputEvent);
- CallbackObserver adminMessageObserver =
- CallbackObserver(this, &Screen::handleAdminMessage);
+ CallbackObserver adminMessageObserver =
+ CallbackObserver(this, &Screen::handleAdminMessage);
public:
+ OLEDDisplay *getDisplayDevice() { return dispdev; }
explicit Screen(ScanI2C::DeviceAddress, meshtastic_Config_DisplayConfig_OledType, OLEDDISPLAY_GEOMETRY);
-
+ size_t frameCount = 0; // Total number of active frames
~Screen();
+ // Which frame we want to be displayed, after we regen the frameset by calling setFrames
+ enum FrameFocus : uint8_t {
+ FOCUS_DEFAULT, // No specific frame
+ FOCUS_PRESERVE, // Return to the previous frame
+ FOCUS_FAULT,
+ FOCUS_TEXTMESSAGE,
+ FOCUS_MODULE, // Note: target module should call requestFocus(), otherwise no info about which module to focus
+ FOCUS_CLOCK,
+ FOCUS_SYSTEM,
+ };
+
+ // Regenerate the normal set of frames, focusing a specific frame if requested
+ // Call when a frame should be added / removed, or custom frames should be cleared
+ void setFrames(FrameFocus focus = FOCUS_DEFAULT);
+
+ std::vector indicatorIcons; // Per-frame custom icon pointers
Screen(const Screen &) = delete;
Screen &operator=(const Screen &) = delete;
@@ -191,6 +245,12 @@ class Screen : public concurrency::OSThread
meshtastic_Config_DisplayConfig_OledType model;
OLEDDISPLAY_GEOMETRY geometry;
+ bool isOverlayBannerShowing();
+
+ // Stores the last 4 of our hardware ID, to make finding the device for pairing easier
+ // FIXME: Needs refactoring and getMacAddr needs to be moved to a utility class
+ char ourId[5];
+
/// Initializes the UI, turns on the display, starts showing boot screen.
//
// Not thread safe - must be called before any other methods are called.
@@ -214,21 +274,9 @@ class Screen : public concurrency::OSThread
void blink();
- void drawFrameText(OLEDDisplay *, OLEDDisplayUiState *, int16_t, int16_t, const char *);
-
- void getTimeAgoStr(uint32_t agoSecs, char *timeStr, uint8_t maxLength);
-
// Draw north
- void drawCompassNorth(OLEDDisplay *display, int16_t compassX, int16_t compassY, float myHeading);
-
- static uint16_t getCompassDiam(uint32_t displayWidth, uint32_t displayHeight);
-
float estimatedHeading(double lat, double lon);
- void drawNodeHeading(OLEDDisplay *display, int16_t compassX, int16_t compassY, uint16_t compassDiam, float headingRadian);
-
- void drawColumns(OLEDDisplay *display, int16_t x, int16_t y, const char **fields);
-
/// Handle button press, trackball or swipe action)
void onPress() { enqueueCmd(ScreenCmd{.cmd = Cmd::ON_PRESS}); }
void showPrevFrame() { enqueueCmd(ScreenCmd{.cmd = Cmd::SHOW_PREV_FRAME}); }
@@ -260,6 +308,18 @@ class Screen : public concurrency::OSThread
enqueueCmd(cmd);
}
+ void showSimpleBanner(const char *message, uint32_t durationMs = 0);
+ void showOverlayBanner(BannerOverlayOptions);
+
+ void showNodePicker(const char *message, uint32_t durationMs, std::function bannerCallback);
+ void showNumberPicker(const char *message, uint32_t durationMs, uint8_t digits, std::function bannerCallback);
+
+ void requestMenu(graphics::menuHandler::screenMenus menuToShow)
+ {
+ graphics::menuHandler::menuQueue = menuToShow;
+ runNow();
+ }
+
void startFirmwareUpdateScreen()
{
ScreenCmd cmd;
@@ -272,7 +332,7 @@ class Screen : public concurrency::OSThread
void setHeading(long _heading)
{
hasCompass = true;
- compassHeading = _heading;
+ compassHeading = fmod(_heading, 360);
}
bool hasHeading() { return hasCompass; }
@@ -292,23 +352,12 @@ class Screen : public concurrency::OSThread
/// Stops showing the boot screen.
void stopBootScreen() { enqueueCmd(ScreenCmd{.cmd = Cmd::STOP_BOOT_SCREEN}); }
- /// Writes a string to the screen.
- void print(const char *text)
+ void runNow()
{
- ScreenCmd cmd;
- cmd.cmd = Cmd::PRINT;
- // TODO(girts): strdup() here is scary, but we can't use std::string as
- // FreeRTOS queue is just dumbly copying memory contents. It would be
- // nice if we had a queue that could copy objects by value.
- cmd.print_text = strdup(text);
- if (!enqueueCmd(cmd)) {
- free(cmd.print_text);
- }
+ setFastFramerate();
+ enqueueCmd(ScreenCmd{.cmd = Cmd::NOOP});
}
- /// generates a very brief time delta display
- std::string drawTimeDelta(uint32_t days, uint32_t hours, uint32_t minutes, uint32_t seconds);
-
/// Overrides the default utf8 character conversion, to replace empty space with question marks
static char customFontTableLookup(const uint8_t ch)
{
@@ -533,7 +582,7 @@ class Screen : public concurrency::OSThread
int handleTextMessage(const meshtastic_MeshPacket *arg);
int handleUIFrameEvent(const UIFrameEvent *arg);
int handleInputEvent(const InputEvent *arg);
- int handleAdminMessage(const meshtastic_AdminMessage *arg);
+ int handleAdminMessage(AdminModule_ObserverData *arg);
/// Used to force (super slow) eink displays to draw critical frames
void forceDisplay(bool forceUiUpdate = false);
@@ -541,8 +590,6 @@ class Screen : public concurrency::OSThread
/// Draws our SSL cert screen during boot (called from WebServer)
void setSSLFrames();
- void setWelcomeFrames();
-
// Dismiss the currently focussed frame, if possible (e.g. text message, waypoint)
void dismissCurrentFrame();
@@ -591,7 +638,6 @@ class Screen : public concurrency::OSThread
void handleOnPress();
void handleShowNextFrame();
void handleShowPrevFrame();
- void handlePrint(const char *text);
void handleStartFirmwareUpdateScreen();
// Info collected by setFrames method.
@@ -600,30 +646,37 @@ class Screen : public concurrency::OSThread
// - Used to dismiss the currently shown frame (txt; waypoint) by CardKB combo
struct FramesetInfo {
struct FramePositions {
- uint8_t fault = 0;
- uint8_t textMessage = 0;
- uint8_t waypoint = 0;
- uint8_t focusedModule = 0;
- uint8_t log = 0;
- uint8_t settings = 0;
- uint8_t wifi = 0;
+ uint8_t fault = 255;
+ uint8_t waypoint = 255;
+ uint8_t focusedModule = 255;
+ uint8_t log = 255;
+ uint8_t settings = 255;
+ uint8_t wifi = 255;
+ uint8_t deviceFocused = 255;
+ uint8_t memory = 255;
+ uint8_t gps = 255;
+ uint8_t home = 255;
+ uint8_t textMessage = 255;
+ uint8_t nodelist = 255;
+ uint8_t nodelist_lastheard = 255;
+ uint8_t nodelist_hopsignal = 255;
+ uint8_t nodelist_distance = 255;
+ uint8_t nodelist_bearings = 255;
+ uint8_t clock = 255;
+ uint8_t firstFavorite = 255;
+ uint8_t lastFavorite = 255;
+ uint8_t lora = 255;
} positions;
uint8_t frameCount = 0;
} framesetInfo;
- // Which frame we want to be displayed, after we regen the frameset by calling setFrames
- enum FrameFocus : uint8_t {
- FOCUS_DEFAULT, // No specific frame
- FOCUS_PRESERVE, // Return to the previous frame
- FOCUS_FAULT,
- FOCUS_TEXTMESSAGE,
- FOCUS_MODULE, // Note: target module should call requestFocus(), otherwise no info about which module to focus
- };
-
- // Regenerate the normal set of frames, focusing a specific frame if requested
- // Call when a frame should be added / removed, or custom frames should be cleared
- void setFrames(FrameFocus focus = FOCUS_DEFAULT);
+ struct DismissedFrames {
+ bool textMessage = false;
+ bool waypoint = false;
+ bool wifi = false;
+ bool memory = false;
+ } dismissedFrames;
/// Try to start drawing ASAP
void setFastFramerate();
@@ -631,34 +684,6 @@ class Screen : public concurrency::OSThread
// Sets frame up for immediate drawing
void setFrameImmediateDraw(FrameCallback *drawFrames);
- /// Called when debug screen is to be drawn, calls through to debugInfo.drawFrame.
- static void drawDebugInfoTrampoline(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
-
- static void drawDebugInfoSettingsTrampoline(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
-
- static void drawDebugInfoWiFiTrampoline(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
-
-#if defined(DISPLAY_CLOCK_FRAME)
- static void drawAnalogClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
-
- static void drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
-
- static void drawSegmentedDisplayCharacter(OLEDDisplay *display, int x, int y, uint8_t number, float scale = 1);
-
- static void drawHorizontalSegment(OLEDDisplay *display, int x, int y, int width, int height);
-
- static void drawVerticalSegment(OLEDDisplay *display, int x, int y, int width, int height);
-
- static void drawSegmentedDisplayColon(OLEDDisplay *display, int x, int y, float scale = 1);
-
- static void drawWatchFaceToggleButton(OLEDDisplay *display, int16_t x, int16_t y, bool digitalMode = true, float scale = 1);
-
- static void drawBluetoothConnectedIcon(OLEDDisplay *display, int16_t x, int16_t y);
-
- // Whether we are showing the digital watch face or the analog one
- bool digitalWatchFace = true;
-#endif
-
/// callback for current alert frame
FrameCallback alertFrame;
@@ -691,4 +716,9 @@ class Screen : public concurrency::OSThread
} // namespace graphics
+// Extern declarations for function symbols used in UIRenderer
+extern std::vector functionSymbol;
+extern std::string functionSymbolString;
+extern graphics::Screen *screen;
+
#endif
\ No newline at end of file
diff --git a/src/graphics/ScreenGlobals.cpp b/src/graphics/ScreenGlobals.cpp
new file mode 100644
index 000000000..bc139faaf
--- /dev/null
+++ b/src/graphics/ScreenGlobals.cpp
@@ -0,0 +1,6 @@
+#include
+#include
+
+// Global variables for screen function overlay
+std::vector functionSymbol;
+std::string functionSymbolString;
diff --git a/src/graphics/SharedUIDisplay.cpp b/src/graphics/SharedUIDisplay.cpp
new file mode 100644
index 000000000..7cd876ac5
--- /dev/null
+++ b/src/graphics/SharedUIDisplay.cpp
@@ -0,0 +1,394 @@
+#include "graphics/SharedUIDisplay.h"
+#include "RTC.h"
+#include "graphics/ScreenFonts.h"
+#include "main.h"
+#include "meshtastic/config.pb.h"
+#include "power.h"
+#include
+#include
+
+namespace graphics
+{
+
+void determineResolution(int16_t screenheight, int16_t screenwidth)
+{
+ if (screenwidth > 128) {
+ isHighResolution = true;
+ }
+
+ // Special case for Heltec Wireless Tracker v1.1
+ if (screenwidth == 160 && screenheight == 80) {
+ isHighResolution = false;
+ }
+}
+
+// === Shared External State ===
+bool hasUnreadMessage = false;
+bool isMuted = false;
+bool isHighResolution = false;
+
+// === Internal State ===
+bool isBoltVisibleShared = true;
+uint32_t lastBlinkShared = 0;
+bool isMailIconVisible = true;
+uint32_t lastMailBlink = 0;
+
+// *********************************
+// * Rounded Header when inverted *
+// *********************************
+void drawRoundedHighlight(OLEDDisplay *display, int16_t x, int16_t y, int16_t w, int16_t h, int16_t r)
+{
+ // Draw the center and side rectangles
+ display->fillRect(x + r, y, w - 2 * r, h); // center bar
+ display->fillRect(x, y + r, r, h - 2 * r); // left edge
+ display->fillRect(x + w - r, y + r, r, h - 2 * r); // right edge
+
+ // Draw the rounded corners using filled circles
+ display->fillCircle(x + r + 1, y + r, r); // top-left
+ display->fillCircle(x + w - r - 1, y + r, r); // top-right
+ display->fillCircle(x + r + 1, y + h - r - 1, r); // bottom-left
+ display->fillCircle(x + w - r - 1, y + h - r - 1, r); // bottom-right
+}
+
+// *************************
+// * Common Header Drawing *
+// *************************
+void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const char *titleStr, bool battery_only)
+{
+ constexpr int HEADER_OFFSET_Y = 1;
+ y += HEADER_OFFSET_Y;
+
+ display->setFont(FONT_SMALL);
+ display->setTextAlignment(TEXT_ALIGN_LEFT);
+
+ const int xOffset = 4;
+ const int highlightHeight = FONT_HEIGHT_SMALL - 1;
+ const bool isInverted = (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_INVERTED);
+ const bool isBold = config.display.heading_bold;
+
+ const int screenW = display->getWidth();
+ const int screenH = display->getHeight();
+
+ if (!battery_only) {
+ // === Inverted Header Background ===
+ if (isInverted) {
+ display->setColor(BLACK);
+ display->fillRect(0, 0, screenW, highlightHeight + 2);
+ display->setColor(WHITE);
+ drawRoundedHighlight(display, x, y, screenW, highlightHeight, 2);
+ display->setColor(BLACK);
+ } else {
+ display->setColor(BLACK);
+ display->fillRect(0, 0, screenW, highlightHeight + 2);
+ display->setColor(WHITE);
+ if (isHighResolution) {
+ display->drawLine(0, 20, screenW, 20);
+ } else {
+ display->drawLine(0, 14, screenW, 14);
+ }
+ }
+
+ // === Screen Title ===
+ display->setTextAlignment(TEXT_ALIGN_CENTER);
+ display->drawString(SCREEN_WIDTH / 2, y, titleStr);
+ if (config.display.heading_bold) {
+ display->drawString((SCREEN_WIDTH / 2) + 1, y, titleStr);
+ }
+ }
+ display->setTextAlignment(TEXT_ALIGN_LEFT);
+
+ // === Battery State ===
+ int chargePercent = powerStatus->getBatteryChargePercent();
+ bool isCharging = powerStatus->getIsCharging();
+ bool usbPowered = powerStatus->getHasUSB();
+
+ if (chargePercent >= 100) {
+ isCharging = false;
+ }
+ if (chargePercent == 101) {
+ usbPowered = true; // Forcing this flag on for the express purpose that some devices have no concept of having a USB cable
+ // plugged in
+ }
+
+ uint32_t now = millis();
+
+#ifndef USE_EINK
+ if (isCharging && now - lastBlinkShared > 500) {
+ isBoltVisibleShared = !isBoltVisibleShared;
+ lastBlinkShared = now;
+ }
+#endif
+
+ bool useHorizontalBattery = (isHighResolution && screenW >= screenH);
+ const int textY = y + (highlightHeight - FONT_HEIGHT_SMALL) / 2;
+
+ int batteryX = 1;
+ int batteryY = HEADER_OFFSET_Y + 1;
+
+ // === Battery Icons ===
+ if (usbPowered && !isCharging) { // This is a basic check to determine USB Powered is flagged but not charging
+ batteryX += 1;
+ batteryY += 2;
+ if (isHighResolution) {
+ display->drawXbm(batteryX, batteryY, 19, 12, imgUSB_HighResolution);
+ batteryX += 20; // Icon + 1 pixel
+ } else {
+ display->drawXbm(batteryX, batteryY, 10, 8, imgUSB);
+ batteryX += 11; // Icon + 1 pixel
+ }
+ } else {
+ if (useHorizontalBattery) {
+ batteryX += 1;
+ batteryY += 2;
+ display->drawXbm(batteryX, batteryY, 9, 13, batteryBitmap_h_bottom);
+ display->drawXbm(batteryX + 9, batteryY, 9, 13, batteryBitmap_h_top);
+ if (isCharging && isBoltVisibleShared)
+ display->drawXbm(batteryX + 4, batteryY, 9, 13, lightning_bolt_h);
+ else {
+ display->drawLine(batteryX + 5, batteryY, batteryX + 10, batteryY);
+ display->drawLine(batteryX + 5, batteryY + 12, batteryX + 10, batteryY + 12);
+ int fillWidth = 14 * chargePercent / 100;
+ display->fillRect(batteryX + 1, batteryY + 1, fillWidth, 11);
+ }
+ batteryX += 18; // Icon + 2 pixels
+ } else {
+#ifdef USE_EINK
+ batteryY += 2;
+#endif
+ display->drawXbm(batteryX, batteryY, 7, 11, batteryBitmap_v);
+ if (isCharging && isBoltVisibleShared)
+ display->drawXbm(batteryX + 1, batteryY + 3, 5, 5, lightning_bolt_v);
+ else {
+ display->drawXbm(batteryX - 1, batteryY + 4, 8, 3, batteryBitmap_sidegaps_v);
+ int fillHeight = 8 * chargePercent / 100;
+ int fillY = batteryY - fillHeight;
+ display->fillRect(batteryX + 1, fillY + 10, 5, fillHeight);
+ }
+ batteryX += 9; // Icon + 2 pixels
+ }
+ }
+
+ if (chargePercent != 101) {
+ // === Battery % Display ===
+ char chargeStr[4];
+ snprintf(chargeStr, sizeof(chargeStr), "%d", chargePercent);
+ int chargeNumWidth = display->getStringWidth(chargeStr);
+ display->drawString(batteryX, textY, chargeStr);
+ display->drawString(batteryX + chargeNumWidth - 1, textY, "%");
+ if (isBold) {
+ display->drawString(batteryX + 1, textY, chargeStr);
+ display->drawString(batteryX + chargeNumWidth, textY, "%");
+ }
+ }
+
+ // === Time and Right-aligned Icons ===
+ uint32_t rtc_sec = getValidTime(RTCQuality::RTCQualityDevice, true);
+ char timeStr[10] = "--:--"; // Fallback display
+ int timeStrWidth = display->getStringWidth("12:34"); // Default alignment
+ int timeX = screenW - xOffset - timeStrWidth + 4;
+
+ if (rtc_sec > 0 && !battery_only) {
+ // === Build Time String ===
+ long hms = (rtc_sec % SEC_PER_DAY + SEC_PER_DAY) % SEC_PER_DAY;
+ int hour = hms / SEC_PER_HOUR;
+ int minute = (hms % SEC_PER_HOUR) / SEC_PER_MIN;
+ snprintf(timeStr, sizeof(timeStr), "%d:%02d", hour, minute);
+
+ if (config.display.use_12h_clock) {
+ bool isPM = hour >= 12;
+ hour %= 12;
+ if (hour == 0)
+ hour = 12;
+ snprintf(timeStr, sizeof(timeStr), "%d:%02d%s", hour, minute, isPM ? "p" : "a");
+ }
+
+ timeStrWidth = display->getStringWidth(timeStr);
+ timeX = screenW - xOffset - timeStrWidth + 3;
+
+ // === Show Mail or Mute Icon to the Left of Time ===
+ int iconRightEdge = timeX - 1;
+
+ bool showMail = false;
+
+#ifndef USE_EINK
+ if (hasUnreadMessage) {
+ if (now - lastMailBlink > 500) {
+ isMailIconVisible = !isMailIconVisible;
+ lastMailBlink = now;
+ }
+ showMail = isMailIconVisible;
+ }
+#else
+ if (hasUnreadMessage) {
+ showMail = true;
+ }
+#endif
+
+ if (showMail) {
+ if (useHorizontalBattery) {
+ int iconW = 16, iconH = 12;
+ int iconX = iconRightEdge - iconW;
+ int iconY = textY + (FONT_HEIGHT_SMALL - iconH) / 2 - 1;
+ if (isInverted) {
+ display->setColor(WHITE);
+ display->fillRect(iconX - 1, iconY - 1, iconW + 3, iconH + 2);
+ display->setColor(BLACK);
+ } else {
+ display->setColor(BLACK);
+ display->fillRect(iconX - 1, iconY - 1, iconW + 3, iconH + 2);
+ display->setColor(WHITE);
+ }
+ display->drawRect(iconX, iconY, iconW + 1, iconH);
+ display->drawLine(iconX, iconY, iconX + iconW / 2, iconY + iconH - 4);
+ display->drawLine(iconX + iconW, iconY, iconX + iconW / 2, iconY + iconH - 4);
+ } else {
+ int iconX = iconRightEdge - (mail_width - 2);
+ int iconY = textY + (FONT_HEIGHT_SMALL - mail_height) / 2;
+ if (isInverted) {
+ display->setColor(WHITE);
+ display->fillRect(iconX - 1, iconY - 1, mail_width + 2, mail_height + 2);
+ display->setColor(BLACK);
+ } else {
+ display->setColor(BLACK);
+ display->fillRect(iconX - 1, iconY - 1, mail_width + 2, mail_height + 2);
+ display->setColor(WHITE);
+ }
+ display->drawXbm(iconX, iconY, mail_width, mail_height, mail);
+ }
+ } else if (isMuted) {
+ if (isHighResolution) {
+ int iconX = iconRightEdge - mute_symbol_big_width;
+ int iconY = textY + (FONT_HEIGHT_SMALL - mute_symbol_big_height) / 2;
+
+ if (isInverted) {
+ display->setColor(WHITE);
+ display->fillRect(iconX - 1, iconY - 1, mute_symbol_big_width + 2, mute_symbol_big_height + 2);
+ display->setColor(BLACK);
+ } else {
+ display->setColor(BLACK);
+ display->fillRect(iconX - 1, iconY - 1, mute_symbol_big_width + 2, mute_symbol_big_height + 2);
+ display->setColor(WHITE);
+ }
+ display->drawXbm(iconX, iconY, mute_symbol_big_width, mute_symbol_big_height, mute_symbol_big);
+ } else {
+ int iconX = iconRightEdge - mute_symbol_width;
+ int iconY = textY + (FONT_HEIGHT_SMALL - mail_height) / 2;
+
+ if (isInverted) {
+ display->setColor(WHITE);
+ display->fillRect(iconX - 1, iconY - 1, mute_symbol_width + 2, mute_symbol_height + 2);
+ display->setColor(BLACK);
+ } else {
+ display->setColor(BLACK);
+ display->fillRect(iconX - 1, iconY - 1, mute_symbol_width + 2, mute_symbol_height + 2);
+ display->setColor(WHITE);
+ }
+ display->drawXbm(iconX, iconY, mute_symbol_width, mute_symbol_height, mute_symbol);
+ }
+ }
+
+ // === Draw Time ===
+ display->drawString(timeX, textY, timeStr);
+ if (isBold)
+ display->drawString(timeX - 1, textY, timeStr);
+
+ } else {
+ // === No Time Available: Mail/Mute Icon Moves to Far Right ===
+ int iconRightEdge = screenW - xOffset;
+
+ bool showMail = false;
+
+#ifndef USE_EINK
+ if (hasUnreadMessage) {
+ if (now - lastMailBlink > 500) {
+ isMailIconVisible = !isMailIconVisible;
+ lastMailBlink = now;
+ }
+ showMail = isMailIconVisible;
+ }
+#else
+ if (hasUnreadMessage) {
+ showMail = true;
+ }
+#endif
+
+ if (showMail) {
+ if (useHorizontalBattery) {
+ int iconW = 16, iconH = 12;
+ int iconX = iconRightEdge - iconW;
+ int iconY = textY + (FONT_HEIGHT_SMALL - iconH) / 2 - 1;
+ display->drawRect(iconX, iconY, iconW + 1, iconH);
+ display->drawLine(iconX, iconY, iconX + iconW / 2, iconY + iconH - 4);
+ display->drawLine(iconX + iconW, iconY, iconX + iconW / 2, iconY + iconH - 4);
+ } else {
+ int iconX = iconRightEdge - mail_width;
+ int iconY = textY + (FONT_HEIGHT_SMALL - mail_height) / 2;
+ display->drawXbm(iconX, iconY, mail_width, mail_height, mail);
+ }
+ } else if (isMuted) {
+ if (isHighResolution) {
+ int iconX = iconRightEdge - mute_symbol_big_width;
+ int iconY = textY + (FONT_HEIGHT_SMALL - mute_symbol_big_height) / 2;
+ display->drawXbm(iconX, iconY, mute_symbol_big_width, mute_symbol_big_height, mute_symbol_big);
+ } else {
+ int iconX = iconRightEdge - mute_symbol_width;
+ int iconY = textY + (FONT_HEIGHT_SMALL - mail_height) / 2;
+ display->drawXbm(iconX, iconY, mute_symbol_width, mute_symbol_height, mute_symbol);
+ }
+ }
+ }
+
+ display->setColor(WHITE); // Reset for other UI
+}
+
+const int *getTextPositions(OLEDDisplay *display)
+{
+ static int textPositions[7]; // Static array that persists beyond function scope
+
+ if (isHighResolution) {
+ textPositions[0] = textZeroLine;
+ textPositions[1] = textFirstLine_medium;
+ textPositions[2] = textSecondLine_medium;
+ textPositions[3] = textThirdLine_medium;
+ textPositions[4] = textFourthLine_medium;
+ textPositions[5] = textFifthLine_medium;
+ textPositions[6] = textSixthLine_medium;
+ } else {
+ textPositions[0] = textZeroLine;
+ textPositions[1] = textFirstLine;
+ textPositions[2] = textSecondLine;
+ textPositions[3] = textThirdLine;
+ textPositions[4] = textFourthLine;
+ textPositions[5] = textFifthLine;
+ textPositions[6] = textSixthLine;
+ }
+ return textPositions;
+}
+
+bool isAllowedPunctuation(char c)
+{
+ const std::string allowed = ".,!?;:-_()[]{}'\"@#$/\\&+=%~^ ";
+ return allowed.find(c) != std::string::npos;
+}
+
+std::string sanitizeString(const std::string &input)
+{
+ std::string output;
+ bool inReplacement = false;
+
+ for (char c : input) {
+ if (std::isalnum(static_cast(c)) || isAllowedPunctuation(c)) {
+ output += c;
+ inReplacement = false;
+ } else {
+ if (!inReplacement) {
+ output += 0xbf; // ISO-8859-1 for inverted question mark
+ inReplacement = true;
+ }
+ }
+ }
+
+ return output;
+}
+
+} // namespace graphics
diff --git a/src/graphics/SharedUIDisplay.h b/src/graphics/SharedUIDisplay.h
new file mode 100644
index 000000000..b8d82795e
--- /dev/null
+++ b/src/graphics/SharedUIDisplay.h
@@ -0,0 +1,60 @@
+#pragma once
+
+#include
+#include
+
+namespace graphics
+{
+
+// =======================
+// Shared UI Helpers
+// =======================
+
+#define textZeroLine 0
+// Consistent Line Spacing - this is standard for all display and the fall-back spacing
+#define textFirstLine (FONT_HEIGHT_SMALL - 1)
+#define textSecondLine (textFirstLine + (FONT_HEIGHT_SMALL - 5))
+#define textThirdLine (textSecondLine + (FONT_HEIGHT_SMALL - 5))
+#define textFourthLine (textThirdLine + (FONT_HEIGHT_SMALL - 5))
+#define textFifthLine (textFourthLine + (FONT_HEIGHT_SMALL - 5))
+#define textSixthLine (textFifthLine + (FONT_HEIGHT_SMALL - 5))
+
+// Consistent Line Spacing for devices like T114 and TEcho/ThinkNode M1 of devices
+#define textFirstLine_medium (FONT_HEIGHT_SMALL + 1)
+#define textSecondLine_medium (textFirstLine_medium + FONT_HEIGHT_SMALL)
+#define textThirdLine_medium (textSecondLine_medium + FONT_HEIGHT_SMALL)
+#define textFourthLine_medium (textThirdLine_medium + FONT_HEIGHT_SMALL)
+#define textFifthLine_medium (textFourthLine_medium + FONT_HEIGHT_SMALL)
+#define textSixthLine_medium (textFifthLine_medium + FONT_HEIGHT_SMALL)
+
+// Consistent Line Spacing for devices like VisionMaster T190
+#define textFirstLine_large (FONT_HEIGHT_SMALL + 1)
+#define textSecondLine_large (textFirstLine_large + (FONT_HEIGHT_SMALL + 5))
+#define textThirdLine_large (textSecondLine_large + (FONT_HEIGHT_SMALL + 5))
+#define textFourthLine_large (textThirdLine_large + (FONT_HEIGHT_SMALL + 5))
+#define textFifthLine_large (textFourthLine_large + (FONT_HEIGHT_SMALL + 5))
+#define textSixthLine_large (textFifthLine_large + (FONT_HEIGHT_SMALL + 5))
+
+// Quick screen access
+#define SCREEN_WIDTH display->getWidth()
+#define SCREEN_HEIGHT display->getHeight()
+
+// Shared state (declare inside namespace)
+extern bool hasUnreadMessage;
+extern bool isMuted;
+extern bool isHighResolution;
+void determineResolution(int16_t screenheight, int16_t screenwidth);
+
+// Rounded highlight (used for inverted headers)
+void drawRoundedHighlight(OLEDDisplay *display, int16_t x, int16_t y, int16_t w, int16_t h, int16_t r);
+
+// Shared battery/time/mail header
+void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const char *titleStr = "", bool battery_only = false);
+
+const int *getTextPositions(OLEDDisplay *display);
+
+bool isAllowedPunctuation(char c);
+
+std::string sanitizeString(const std::string &input);
+
+} // namespace graphics
diff --git a/src/graphics/TFTDisplay.cpp b/src/graphics/TFTDisplay.cpp
index 14787baff..3e9bafc6c 100644
--- a/src/graphics/TFTDisplay.cpp
+++ b/src/graphics/TFTDisplay.cpp
@@ -1,5 +1,6 @@
#include "configuration.h"
#include "main.h"
+
#if ARCH_PORTDUINO
#include "platform/portduino/PortduinoGlue.h"
#endif
@@ -14,8 +15,10 @@
extern SX1509 gpioExtender;
#endif
-#ifndef TFT_MESH
-#define TFT_MESH COLOR565(0x67, 0xEA, 0x94)
+#ifdef TFT_MESH_OVERRIDE
+uint16_t TFT_MESH = TFT_MESH_OVERRIDE;
+#else
+uint16_t TFT_MESH = COLOR565(0x67, 0xEA, 0x94);
#endif
#if defined(ST7735S)
@@ -467,18 +470,27 @@ class LGFX : public lgfx::LGFX_Device
// The following setting values โโare general initial values โโfor each panel, so please comment out any
// unknown items and try them.
-
- cfg.memory_width = TFT_WIDTH; // Maximum width supported by the driver IC
- cfg.memory_height = TFT_HEIGHT; // Maximum height supported by the driver IC
- cfg.panel_width = TFT_WIDTH; // actual displayable width
- cfg.panel_height = TFT_HEIGHT; // actual displayable height
- cfg.offset_x = TFT_OFFSET_X; // Panel offset amount in X direction
- cfg.offset_y = TFT_OFFSET_Y; // Panel offset amount in Y direction
- cfg.offset_rotation = TFT_OFFSET_ROTATION; // Rotation direction value offset 0~7 (4~7 is mirrored)
+#if defined(T_WATCH_S3)
+ cfg.panel_width = 240;
+ cfg.panel_height = 240;
+ cfg.memory_width = 240;
+ cfg.memory_height = 320;
+ cfg.offset_x = 0;
+ cfg.offset_y = 0; // No vertical shift needed โ panel is top-aligned
+ cfg.offset_rotation = 2; // Rotate 180ยฐ to correct upside-down layout
+#else
+ cfg.memory_width = TFT_WIDTH; // Maximum width supported by the driver IC
+ cfg.memory_height = TFT_HEIGHT; // Maximum height supported by the driver IC
+ cfg.panel_width = TFT_WIDTH; // actual displayable width
+ cfg.panel_height = TFT_HEIGHT; // actual displayable height
+ cfg.offset_x = TFT_OFFSET_X; // Panel offset amount in X direction
+ cfg.offset_y = TFT_OFFSET_Y; // Panel offset amount in Y direction
+ cfg.offset_rotation = TFT_OFFSET_ROTATION; // Rotation direction value offset 0~7 (4~7 is mirrored)
+#endif
#ifdef TFT_DUMMY_READ_PIXELS
cfg.dummy_read_pixel = TFT_DUMMY_READ_PIXELS; // Number of bits for dummy read before pixel readout
#else
- cfg.dummy_read_pixel = 9; // Number of bits for dummy read before pixel readout
+ cfg.dummy_read_pixel = 9; // Number of bits for dummy read before pixel readout
#endif
cfg.dummy_read_bits = 1; // Number of bits for dummy read before non-pixel data read
cfg.readable = true; // Set to true if data can be read
@@ -653,7 +665,7 @@ static LGFX *tft = nullptr;
#include // Graphics and font library for ILI9342 driver chip
static TFT_eSPI *tft = nullptr; // Invoke library, pins defined in User_Setup.h
-#elif ARCH_PORTDUINO && HAS_SCREEN != 0 && !HAS_TFT
+#elif ARCH_PORTDUINO
#include // Graphics and font library for ST7735 driver chip
class LGFX : public lgfx::LGFX_Device
@@ -697,11 +709,16 @@ class LGFX : public lgfx::LGFX_Device
_panel_instance->setBus(&_bus_instance); // set the bus on the panel.
auto cfg = _panel_instance->config(); // Gets a structure for display panel settings.
- LOG_DEBUG("Height: %d, Width: %d ", settingsMap[displayHeight], settingsMap[displayWidth]);
+ LOG_DEBUG("Width: %d, Height: %d", settingsMap[displayWidth], settingsMap[displayHeight]);
cfg.pin_cs = settingsMap[displayCS]; // Pin number where CS is connected (-1 = disable)
cfg.pin_rst = settingsMap[displayReset];
- cfg.panel_width = settingsMap[displayWidth]; // actual displayable width
- cfg.panel_height = settingsMap[displayHeight]; // actual displayable height
+ if (settingsMap[displayRotate]) {
+ cfg.panel_width = settingsMap[displayHeight]; // actual displayable width
+ cfg.panel_height = settingsMap[displayWidth]; // actual displayable height
+ } else {
+ cfg.panel_width = settingsMap[displayWidth]; // actual displayable width
+ cfg.panel_height = settingsMap[displayHeight]; // actual displayable height
+ }
cfg.offset_x = settingsMap[displayOffsetX]; // Panel offset amount in X direction
cfg.offset_y = settingsMap[displayOffsetY]; // Panel offset amount in Y direction
cfg.offset_rotation = settingsMap[displayOffsetRotate]; // Rotation direction value offset 0~7 (4~7 is mirrored)
@@ -978,9 +995,9 @@ TFTDisplay::TFTDisplay(uint8_t address, int sda, int scl, OLEDDISPLAY_GEOMETRY g
#if ARCH_PORTDUINO
if (settingsMap[displayRotate]) {
- setGeometry(GEOMETRY_RAWMODE, settingsMap[configNames::displayHeight], settingsMap[configNames::displayWidth]);
- } else {
setGeometry(GEOMETRY_RAWMODE, settingsMap[configNames::displayWidth], settingsMap[configNames::displayHeight]);
+ } else {
+ setGeometry(GEOMETRY_RAWMODE, settingsMap[configNames::displayHeight], settingsMap[configNames::displayWidth]);
}
#elif defined(SCREEN_ROTATE)
@@ -1169,6 +1186,8 @@ bool TFTDisplay::connect()
tft->setRotation(1); // T-Deck has the TFT in landscape
#elif defined(T_WATCH_S3) || defined(SENSECAP_INDICATOR)
tft->setRotation(2); // T-Watch S3 left-handed orientation
+#elif ARCH_PORTDUINO
+ tft->setRotation(0); // use config.yaml to set rotation
#else
tft->setRotation(3); // Orient horizontal and wide underneath the silkscreen name label
#endif
diff --git a/src/graphics/TimeFormatters.cpp b/src/graphics/TimeFormatters.cpp
new file mode 100644
index 000000000..47036078b
--- /dev/null
+++ b/src/graphics/TimeFormatters.cpp
@@ -0,0 +1,103 @@
+#include "TimeFormatters.h"
+#include "configuration.h"
+#include "gps/RTC.h"
+#include "mesh/NodeDB.h"
+#include
+
+bool deltaToTimestamp(uint32_t secondsAgo, uint8_t *hours, uint8_t *minutes, int32_t *daysAgo)
+{
+ // Cache the result - avoid frequent recalculation
+ static uint8_t hoursCached = 0, minutesCached = 0;
+ static uint32_t daysAgoCached = 0;
+ static uint32_t secondsAgoCached = 0;
+ static bool validCached = false;
+
+ // Abort: if timezone not set
+ if (strlen(config.device.tzdef) == 0) {
+ validCached = false;
+ return validCached;
+ }
+
+ // Abort: if invalid pointers passed
+ if (hours == nullptr || minutes == nullptr || daysAgo == nullptr) {
+ validCached = false;
+ return validCached;
+ }
+
+ // Abort: if time seems invalid.. (> 6 months ago, probably seen before RTC set)
+ if (secondsAgo > SEC_PER_DAY * 30UL * 6) {
+ validCached = false;
+ return validCached;
+ }
+
+ // If repeated request, don't bother recalculating
+ if (secondsAgo - secondsAgoCached < 60 && secondsAgoCached != 0) {
+ if (validCached) {
+ *hours = hoursCached;
+ *minutes = minutesCached;
+ *daysAgo = daysAgoCached;
+ }
+ return validCached;
+ }
+
+ // Get local time
+ uint32_t secondsRTC = getValidTime(RTCQuality::RTCQualityDevice, true); // Get local time
+
+ // Abort: if RTC not set
+ if (!secondsRTC) {
+ validCached = false;
+ return validCached;
+ }
+
+ // Get absolute time when last seen
+ uint32_t secondsSeenAt = secondsRTC - secondsAgo;
+
+ // Calculate daysAgo
+ *daysAgo = (secondsRTC / SEC_PER_DAY) - (secondsSeenAt / SEC_PER_DAY); // How many "midnights" have passed
+
+ // Get seconds since midnight
+ uint32_t hms = (secondsRTC - secondsAgo) % SEC_PER_DAY;
+ hms = (hms + SEC_PER_DAY) % SEC_PER_DAY;
+
+ // Tear apart hms into hours and minutes
+ *hours = hms / SEC_PER_HOUR;
+ *minutes = (hms % SEC_PER_HOUR) / SEC_PER_MIN;
+
+ // Cache the result
+ daysAgoCached = *daysAgo;
+ hoursCached = *hours;
+ minutesCached = *minutes;
+ secondsAgoCached = secondsAgo;
+
+ validCached = true;
+ return validCached;
+}
+
+void getTimeAgoStr(uint32_t agoSecs, char *timeStr, uint8_t maxLength)
+{
+ // Use an absolute timestamp in some cases.
+ // Particularly useful with E-Ink displays. Static UI, fewer refreshes.
+ uint8_t timestampHours, timestampMinutes;
+ int32_t daysAgo;
+ bool useTimestamp = deltaToTimestamp(agoSecs, ×tampHours, ×tampMinutes, &daysAgo);
+
+ if (agoSecs < 120) // last 2 mins?
+ snprintf(timeStr, maxLength, "%u seconds ago", agoSecs);
+ // -- if suitable for timestamp --
+ else if (useTimestamp && agoSecs < 15 * SECONDS_IN_MINUTE) // Last 15 minutes
+ snprintf(timeStr, maxLength, "%u minutes ago", agoSecs / SECONDS_IN_MINUTE);
+ else if (useTimestamp && daysAgo == 0) // Today
+ snprintf(timeStr, maxLength, "Last seen: %02u:%02u", (unsigned int)timestampHours, (unsigned int)timestampMinutes);
+ else if (useTimestamp && daysAgo == 1) // Yesterday
+ snprintf(timeStr, maxLength, "Seen yesterday");
+ else if (useTimestamp && daysAgo > 1) // Last six months (capped by deltaToTimestamp method)
+ snprintf(timeStr, maxLength, "%li days ago", (long)daysAgo);
+ // -- if using time delta instead --
+ else if (agoSecs < 120 * 60) // last 2 hrs
+ snprintf(timeStr, maxLength, "%u minutes ago", agoSecs / 60);
+ // Only show hours ago if it's been less than 6 months. Otherwise, we may have bad data.
+ else if ((agoSecs / 60 / 60) < (730 * 6))
+ snprintf(timeStr, maxLength, "%u hours ago", agoSecs / 60 / 60);
+ else
+ snprintf(timeStr, maxLength, "unknown age");
+}
diff --git a/src/graphics/TimeFormatters.h b/src/graphics/TimeFormatters.h
new file mode 100644
index 000000000..b3d8413a2
--- /dev/null
+++ b/src/graphics/TimeFormatters.h
@@ -0,0 +1,26 @@
+#pragma once
+
+#include "configuration.h"
+#include "gps/RTC.h"
+#include
+#include
+
+/**
+ * Convert a delta in seconds ago to timestamp information (hours, minutes, days ago).
+ *
+ * @param secondsAgo Number of seconds ago to convert
+ * @param hours Pointer to store the hours (0-23)
+ * @param minutes Pointer to store the minutes (0-59)
+ * @param daysAgo Pointer to store the number of days ago
+ * @return true if conversion was successful, false if invalid input or time not available
+ */
+bool deltaToTimestamp(uint32_t secondsAgo, uint8_t *hours, uint8_t *minutes, int32_t *daysAgo);
+
+/**
+ * Get a human-readable string representing the time ago in a format like "2 days, 3 hours, 15 minutes".
+ *
+ * @param agoSecs Number of seconds ago to convert
+ * @param timeStr Pointer to store the resulting string
+ * @param maxLength Maximum length of the resulting string buffer
+ */
+void getTimeAgoStr(uint32_t agoSecs, char *timeStr, uint8_t maxLength);
diff --git a/src/graphics/draw/ClockRenderer.cpp b/src/graphics/draw/ClockRenderer.cpp
new file mode 100644
index 000000000..8d7e91000
--- /dev/null
+++ b/src/graphics/draw/ClockRenderer.cpp
@@ -0,0 +1,516 @@
+#include "configuration.h"
+#if HAS_SCREEN
+#include "ClockRenderer.h"
+#include "NodeDB.h"
+#include "UIRenderer.h"
+#include "configuration.h"
+#include "gps/GeoCoord.h"
+#include "gps/RTC.h"
+#include "graphics/ScreenFonts.h"
+#include "graphics/SharedUIDisplay.h"
+#include "graphics/emotes.h"
+#include "graphics/images.h"
+#include "main.h"
+
+#if !MESHTASTIC_EXCLUDE_BLUETOOTH
+#include "nimble/NimbleBluetooth.h"
+#endif
+
+namespace graphics
+{
+
+namespace ClockRenderer
+{
+
+void drawSegmentedDisplayColon(OLEDDisplay *display, int x, int y, float scale)
+{
+ uint16_t segmentWidth = SEGMENT_WIDTH * scale;
+ uint16_t segmentHeight = SEGMENT_HEIGHT * scale;
+
+ uint16_t cellHeight = (segmentWidth * 2) + (segmentHeight * 3) + 8;
+
+ uint16_t topAndBottomX = x + (4 * scale);
+
+ uint16_t quarterCellHeight = cellHeight / 4;
+
+ uint16_t topY = y + quarterCellHeight;
+ uint16_t bottomY = y + (quarterCellHeight * 3);
+
+ display->fillRect(topAndBottomX, topY, segmentHeight, segmentHeight);
+ display->fillRect(topAndBottomX, bottomY, segmentHeight, segmentHeight);
+}
+
+void drawSegmentedDisplayCharacter(OLEDDisplay *display, int x, int y, uint8_t number, float scale)
+{
+ // the numbers 0-9, each expressed as an array of seven boolean (0|1) values encoding the on/off state of
+ // segment {innerIndex + 1}
+ // e.g., to display the numeral '0', segments 1-6 are on, and segment 7 is off.
+ uint8_t numbers[10][7] = {
+ {1, 1, 1, 1, 1, 1, 0}, // 0 Display segment key
+ {0, 1, 1, 0, 0, 0, 0}, // 1 1
+ {1, 1, 0, 1, 1, 0, 1}, // 2 ___
+ {1, 1, 1, 1, 0, 0, 1}, // 3 6 | | 2
+ {0, 1, 1, 0, 0, 1, 1}, // 4 |_7ฬฒ_|
+ {1, 0, 1, 1, 0, 1, 1}, // 5 5 | | 3
+ {1, 0, 1, 1, 1, 1, 1}, // 6 |___|
+ {1, 1, 1, 0, 0, 1, 0}, // 7
+ {1, 1, 1, 1, 1, 1, 1}, // 8 4
+ {1, 1, 1, 1, 0, 1, 1}, // 9
+ };
+
+ // the width and height of each segment's central rectangle:
+ // _____________________
+ // โฐ| (only this part, |โฑ
+ // โฐ | not including | โฑ
+ // โฑ | the triangles | โฐ
+ // โฑ| on the ends) |โฐ
+ // โพโพโพโพโพโพโพโพโพโพโพโพโพโพโพโพโพโพโพโพโพ
+
+ uint16_t segmentWidth = SEGMENT_WIDTH * scale;
+ uint16_t segmentHeight = SEGMENT_HEIGHT * scale;
+
+ // segment x and y coordinates
+ uint16_t segmentOneX = x + segmentHeight + 2;
+ uint16_t segmentOneY = y;
+
+ uint16_t segmentTwoX = segmentOneX + segmentWidth + 2;
+ uint16_t segmentTwoY = segmentOneY + segmentHeight + 2;
+
+ uint16_t segmentThreeX = segmentTwoX;
+ uint16_t segmentThreeY = segmentTwoY + segmentWidth + 2 + segmentHeight + 2;
+
+ uint16_t segmentFourX = segmentOneX;
+ uint16_t segmentFourY = segmentThreeY + segmentWidth + 2;
+
+ uint16_t segmentFiveX = x;
+ uint16_t segmentFiveY = segmentThreeY;
+
+ uint16_t segmentSixX = x;
+ uint16_t segmentSixY = segmentTwoY;
+
+ uint16_t segmentSevenX = segmentOneX;
+ uint16_t segmentSevenY = segmentTwoY + segmentWidth + 2;
+
+ if (numbers[number][0]) {
+ graphics::ClockRenderer::drawHorizontalSegment(display, segmentOneX, segmentOneY, segmentWidth, segmentHeight);
+ }
+
+ if (numbers[number][1]) {
+ graphics::ClockRenderer::drawVerticalSegment(display, segmentTwoX, segmentTwoY, segmentWidth, segmentHeight);
+ }
+
+ if (numbers[number][2]) {
+ graphics::ClockRenderer::drawVerticalSegment(display, segmentThreeX, segmentThreeY, segmentWidth, segmentHeight);
+ }
+
+ if (numbers[number][3]) {
+ graphics::ClockRenderer::drawHorizontalSegment(display, segmentFourX, segmentFourY, segmentWidth, segmentHeight);
+ }
+
+ if (numbers[number][4]) {
+ graphics::ClockRenderer::drawVerticalSegment(display, segmentFiveX, segmentFiveY, segmentWidth, segmentHeight);
+ }
+
+ if (numbers[number][5]) {
+ graphics::ClockRenderer::drawVerticalSegment(display, segmentSixX, segmentSixY, segmentWidth, segmentHeight);
+ }
+
+ if (numbers[number][6]) {
+ graphics::ClockRenderer::drawHorizontalSegment(display, segmentSevenX, segmentSevenY, segmentWidth, segmentHeight);
+ }
+}
+
+void drawHorizontalSegment(OLEDDisplay *display, int x, int y, int width, int height)
+{
+ int halfHeight = height / 2;
+
+ // draw central rectangle
+ display->fillRect(x, y, width, height);
+
+ // draw end triangles
+ display->fillTriangle(x, y, x, y + height - 1, x - halfHeight, y + halfHeight);
+
+ display->fillTriangle(x + width, y, x + width + halfHeight, y + halfHeight, x + width, y + height - 1);
+}
+
+void drawVerticalSegment(OLEDDisplay *display, int x, int y, int width, int height)
+{
+ int halfHeight = height / 2;
+
+ // draw central rectangle
+ display->fillRect(x, y, height, width);
+
+ // draw end triangles
+ display->fillTriangle(x + halfHeight, y - halfHeight, x + height - 1, y, x, y);
+
+ display->fillTriangle(x, y + width, x + height - 1, y + width, x + halfHeight, y + width + halfHeight);
+}
+
+/*
+void drawWatchFaceToggleButton(OLEDDisplay *display, int16_t x, int16_t y, bool digitalMode, float scale)
+{
+ uint16_t segmentWidth = SEGMENT_WIDTH * scale;
+ uint16_t segmentHeight = SEGMENT_HEIGHT * scale;
+
+ if (digitalMode) {
+ uint16_t radius = (segmentWidth + (segmentHeight * 2) + 4) / 2;
+ uint16_t centerX = (x + segmentHeight + 2) + (radius / 2);
+ uint16_t centerY = (y + segmentHeight + 2) + (radius / 2);
+
+ display->drawCircle(centerX, centerY, radius);
+ display->drawCircle(centerX, centerY, radius + 1);
+ display->drawLine(centerX, centerY, centerX, centerY - radius + 3);
+ display->drawLine(centerX, centerY, centerX + radius - 3, centerY);
+ } else {
+ uint16_t segmentOneX = x + segmentHeight + 2;
+ uint16_t segmentOneY = y;
+
+ uint16_t segmentTwoX = segmentOneX + segmentWidth + 2;
+ uint16_t segmentTwoY = segmentOneY + segmentHeight + 2;
+
+ uint16_t segmentThreeX = segmentOneX;
+ uint16_t segmentThreeY = segmentTwoY + segmentWidth + 2;
+
+ uint16_t segmentFourX = x;
+ uint16_t segmentFourY = y + segmentHeight + 2;
+
+ drawHorizontalSegment(display, segmentOneX, segmentOneY, segmentWidth, segmentHeight);
+ drawVerticalSegment(display, segmentTwoX, segmentTwoY, segmentWidth, segmentHeight);
+ drawHorizontalSegment(display, segmentThreeX, segmentThreeY, segmentWidth, segmentHeight);
+ drawVerticalSegment(display, segmentFourX, segmentFourY, segmentWidth, segmentHeight);
+ }
+}
+*/
+// Draw a digital clock
+void drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
+{
+ display->clear();
+ display->setTextAlignment(TEXT_ALIGN_LEFT);
+ int line = 1;
+ // === Set Title, Blank for Clock
+ const char *titleStr = "";
+ // === Header ===
+ graphics::drawCommonHeader(display, x, y, titleStr, true);
+
+#ifdef T_WATCH_S3
+ if (nimbleBluetooth && nimbleBluetooth->isConnected()) {
+ graphics::ClockRenderer::drawBluetoothConnectedIcon(display, display->getWidth() - 18, display->getHeight() - 14);
+ }
+#endif
+
+ uint32_t rtc_sec = getValidTime(RTCQuality::RTCQualityDevice, true); // Display local timezone
+ char timeString[16];
+ int hour = 0;
+ int minute = 0;
+ int second = 0;
+ if (rtc_sec > 0) {
+ long hms = rtc_sec % SEC_PER_DAY;
+ hms = (hms + SEC_PER_DAY) % SEC_PER_DAY;
+
+ hour = hms / SEC_PER_HOUR;
+ minute = (hms % SEC_PER_HOUR) / SEC_PER_MIN;
+ second = (hms % SEC_PER_HOUR) % SEC_PER_MIN; // or hms % SEC_PER_MIN
+ }
+
+ bool isPM = hour >= 12;
+ // hour = hour > 12 ? hour - 12 : hour;
+ if (config.display.use_12h_clock) {
+ hour %= 12;
+ if (hour == 0)
+ hour = 12;
+ bool isPM = hour >= 12;
+ snprintf(timeString, sizeof(timeString), "%d:%02d", hour, minute);
+ } else {
+ snprintf(timeString, sizeof(timeString), "%02d:%02d", hour, minute);
+ }
+
+ // Format seconds string
+ char secondString[8];
+ snprintf(secondString, sizeof(secondString), "%02d", second);
+
+#ifdef T_WATCH_S3
+ float scale = 1.5;
+#else
+ float scale = 0.75;
+ if (isHighResolution) {
+ scale = 1.5;
+ }
+#endif
+
+ uint16_t segmentWidth = SEGMENT_WIDTH * scale;
+ uint16_t segmentHeight = SEGMENT_HEIGHT * scale;
+
+ // calculate hours:minutes string width
+ uint16_t timeStringWidth = strlen(timeString) * 5;
+
+ for (uint8_t i = 0; i < strlen(timeString); i++) {
+ char character = timeString[i];
+
+ if (character == ':') {
+ timeStringWidth += segmentHeight;
+ } else {
+ timeStringWidth += segmentWidth + (segmentHeight * 2) + 4;
+ }
+ }
+
+ uint16_t hourMinuteTextX = (display->getWidth() / 2) - (timeStringWidth / 2);
+
+ uint16_t startingHourMinuteTextX = hourMinuteTextX;
+
+ uint16_t hourMinuteTextY = (display->getHeight() / 2) - (((segmentWidth * 2) + (segmentHeight * 3) + 8) / 2);
+
+ // iterate over characters in hours:minutes string and draw segmented characters
+ for (uint8_t i = 0; i < strlen(timeString); i++) {
+ char character = timeString[i];
+
+ if (character == ':') {
+ drawSegmentedDisplayColon(display, hourMinuteTextX, hourMinuteTextY, scale);
+
+ hourMinuteTextX += segmentHeight + 6;
+ } else {
+ drawSegmentedDisplayCharacter(display, hourMinuteTextX, hourMinuteTextY, character - '0', scale);
+
+ hourMinuteTextX += segmentWidth + (segmentHeight * 2) + 4;
+ }
+
+ hourMinuteTextX += 5;
+ }
+
+ // draw seconds string
+ display->setFont(FONT_SMALL);
+ int xOffset = (isHighResolution) ? 0 : -1;
+ if (hour >= 10) {
+ xOffset += (isHighResolution) ? 32 : 18;
+ }
+ int yOffset = (isHighResolution) ? 3 : 1;
+#ifdef SENSECAP_INDICATOR
+ yOffset -= 3;
+#endif
+ if (config.display.use_12h_clock) {
+ display->drawString(startingHourMinuteTextX + xOffset, (display->getHeight() - hourMinuteTextY) - yOffset - 2,
+ isPM ? "pm" : "am");
+ }
+#ifndef USE_EINK
+ xOffset = (isHighResolution) ? 18 : 10;
+ display->drawString(startingHourMinuteTextX + timeStringWidth - xOffset, (display->getHeight() - hourMinuteTextY) - yOffset,
+ secondString);
+#endif
+}
+
+void drawBluetoothConnectedIcon(OLEDDisplay *display, int16_t x, int16_t y)
+{
+ display->drawFastImage(x, y, 18, 14, bluetoothConnectedIcon);
+}
+
+// Draw an analog clock
+void drawAnalogClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
+{
+ display->setTextAlignment(TEXT_ALIGN_LEFT);
+ // === Set Title, Blank for Clock
+ const char *titleStr = "";
+ // === Header ===
+ graphics::drawCommonHeader(display, x, y, titleStr, true);
+
+#ifdef T_WATCH_S3
+ if (nimbleBluetooth && nimbleBluetooth->isConnected()) {
+ drawBluetoothConnectedIcon(display, display->getWidth() - 18, display->getHeight() - 14);
+ }
+#endif
+ // clock face center coordinates
+ int16_t centerX = display->getWidth() / 2;
+ int16_t centerY = display->getHeight() / 2;
+
+ // clock face radius
+ int16_t radius = 0;
+ if (display->getHeight() < display->getWidth()) {
+ radius = (display->getHeight() / 2) * 0.9;
+ } else {
+ radius = (display->getWidth() / 2) * 0.9;
+ }
+#ifdef T_WATCH_S3
+ radius = (display->getWidth() / 2) * 0.8;
+#endif
+
+ // noon (0 deg) coordinates (outermost circle)
+ int16_t noonX = centerX;
+ int16_t noonY = centerY - radius;
+
+ // second hand radius and y coordinate (outermost circle)
+ int16_t secondHandNoonY = noonY + 1;
+
+ // tick mark outer y coordinate; (first nested circle)
+ int16_t tickMarkOuterNoonY = secondHandNoonY;
+
+ // seconds tick mark inner y coordinate; (second nested circle)
+ double secondsTickMarkInnerNoonY = (double)noonY + 4;
+ if (isHighResolution) {
+ secondsTickMarkInnerNoonY = (double)noonY + 8;
+ }
+
+ // hours tick mark inner y coordinate; (third nested circle)
+ double hoursTickMarkInnerNoonY = (double)noonY + 6;
+ if (isHighResolution) {
+ hoursTickMarkInnerNoonY = (double)noonY + 16;
+ }
+
+ // minute hand y coordinate
+ int16_t minuteHandNoonY = secondsTickMarkInnerNoonY + 4;
+
+ // hour string y coordinate
+ int16_t hourStringNoonY = minuteHandNoonY + 18;
+
+ // hour hand radius and y coordinate
+ int16_t hourHandRadius = radius * 0.35;
+ if (isHighResolution) {
+ int16_t hourHandRadius = radius * 0.55;
+ }
+ int16_t hourHandNoonY = centerY - hourHandRadius;
+
+ display->setColor(OLEDDISPLAY_COLOR::WHITE);
+ display->drawCircle(centerX, centerY, radius);
+
+ uint32_t rtc_sec = getValidTime(RTCQuality::RTCQualityDevice, true); // Display local timezone
+ if (rtc_sec > 0) {
+ long hms = rtc_sec % SEC_PER_DAY;
+ hms = (hms + SEC_PER_DAY) % SEC_PER_DAY;
+
+ // Tear apart hms into h:m:s
+ int hour = hms / SEC_PER_HOUR;
+ int minute = (hms % SEC_PER_HOUR) / SEC_PER_MIN;
+ int second = (hms % SEC_PER_HOUR) % SEC_PER_MIN; // or hms % SEC_PER_MIN
+
+ bool isPM = hour >= 12;
+ if (config.display.use_12h_clock) {
+ bool isPM = hour >= 12;
+ display->setFont(FONT_SMALL);
+ int yOffset = isHighResolution ? 1 : 0;
+#ifdef USE_EINK
+ yOffset += 3;
+#endif
+ display->drawString(centerX - (display->getStringWidth(isPM ? "pm" : "am") / 2), centerY + yOffset,
+ isPM ? "pm" : "am");
+ }
+ hour %= 12;
+ if (hour == 0)
+ hour = 12;
+
+ int16_t degreesPerHour = 30;
+ int16_t degreesPerMinuteOrSecond = 6;
+
+ double hourBaseAngle = hour * degreesPerHour;
+ double hourAngleOffset = ((double)minute / 60) * degreesPerHour;
+ double hourAngle = radians(hourBaseAngle + hourAngleOffset);
+
+ double minuteBaseAngle = minute * degreesPerMinuteOrSecond;
+ double minuteAngleOffset = ((double)second / 60) * degreesPerMinuteOrSecond;
+ double minuteAngle = radians(minuteBaseAngle + minuteAngleOffset);
+
+ double secondAngle = radians(second * degreesPerMinuteOrSecond);
+
+ double hourX = sin(-hourAngle) * (hourHandNoonY - centerY) + noonX;
+ double hourY = cos(-hourAngle) * (hourHandNoonY - centerY) + centerY;
+
+ double minuteX = sin(-minuteAngle) * (minuteHandNoonY - centerY) + noonX;
+ double minuteY = cos(-minuteAngle) * (minuteHandNoonY - centerY) + centerY;
+
+ double secondX = sin(-secondAngle) * (secondHandNoonY - centerY) + noonX;
+ double secondY = cos(-secondAngle) * (secondHandNoonY - centerY) + centerY;
+
+ display->setFont(FONT_MEDIUM);
+
+ // draw minute and hour tick marks and hour numbers
+ for (uint16_t angle = 0; angle < 360; angle += 6) {
+ double angleInRadians = radians(angle);
+
+ double sineAngleInRadians = sin(-angleInRadians);
+ double cosineAngleInRadians = cos(-angleInRadians);
+
+ double endX = sineAngleInRadians * (tickMarkOuterNoonY - centerY) + noonX;
+ double endY = cosineAngleInRadians * (tickMarkOuterNoonY - centerY) + centerY;
+
+ if (angle % degreesPerHour == 0) {
+ double startX = sineAngleInRadians * (hoursTickMarkInnerNoonY - centerY) + noonX;
+ double startY = cosineAngleInRadians * (hoursTickMarkInnerNoonY - centerY) + centerY;
+
+ // draw hour tick mark
+ display->drawLine(startX, startY, endX, endY);
+
+ static char buffer[2];
+
+ uint8_t hourInt = (angle / 30);
+
+ if (hourInt == 0) {
+ hourInt = 12;
+ }
+
+ // hour number x offset needs to be adjusted for some cases
+ int8_t hourStringXOffset;
+ int8_t hourStringYOffset = 13;
+
+ switch (hourInt) {
+ case 3:
+ hourStringXOffset = 5;
+ break;
+ case 9:
+ hourStringXOffset = 7;
+ break;
+ case 10:
+ case 11:
+ hourStringXOffset = 8;
+ break;
+ case 12:
+ hourStringXOffset = 13;
+ break;
+ default:
+ hourStringXOffset = 6;
+ break;
+ }
+
+ double hourStringX = (sineAngleInRadians * (hourStringNoonY - centerY) + noonX) - hourStringXOffset;
+ double hourStringY = (cosineAngleInRadians * (hourStringNoonY - centerY) + centerY) - hourStringYOffset;
+
+#ifdef T_WATCH_S3
+ // draw hour number
+ display->drawStringf(hourStringX, hourStringY, buffer, "%d", hourInt);
+#else
+#ifdef USE_EINK
+ if (isHighResolution) {
+ // draw hour number
+ display->drawStringf(hourStringX, hourStringY, buffer, "%d", hourInt);
+ }
+#else
+ if (isHighResolution && (hourInt == 3 || hourInt == 6 || hourInt == 9 || hourInt == 12)) {
+ // draw hour number
+ display->drawStringf(hourStringX, hourStringY, buffer, "%d", hourInt);
+ }
+#endif
+#endif
+ }
+
+ if (angle % degreesPerMinuteOrSecond == 0) {
+ double startX = sineAngleInRadians * (secondsTickMarkInnerNoonY - centerY) + noonX;
+ double startY = cosineAngleInRadians * (secondsTickMarkInnerNoonY - centerY) + centerY;
+
+ if (isHighResolution) {
+ // draw minute tick mark
+ display->drawLine(startX, startY, endX, endY);
+ }
+ }
+ }
+
+ // draw hour hand
+ display->drawLine(centerX, centerY, hourX, hourY);
+
+ // draw minute hand
+ display->drawLine(centerX, centerY, minuteX, minuteY);
+#ifndef USE_EINK
+ // draw second hand
+ display->drawLine(centerX, centerY, secondX, secondY);
+#endif
+ }
+}
+
+} // namespace ClockRenderer
+
+} // namespace graphics
+#endif
\ No newline at end of file
diff --git a/src/graphics/draw/ClockRenderer.h b/src/graphics/draw/ClockRenderer.h
new file mode 100644
index 000000000..c8ba62868
--- /dev/null
+++ b/src/graphics/draw/ClockRenderer.h
@@ -0,0 +1,31 @@
+#pragma once
+
+#include
+#include
+
+namespace graphics
+{
+
+/// Forward declarations
+class Screen;
+
+namespace ClockRenderer
+{
+
+// Clock frame functions
+void drawAnalogClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
+void drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
+
+// Segmented display functions
+void drawSegmentedDisplayCharacter(OLEDDisplay *display, int x, int y, uint8_t number, float scale = 1);
+void drawSegmentedDisplayColon(OLEDDisplay *display, int x, int y, float scale = 1);
+void drawHorizontalSegment(OLEDDisplay *display, int x, int y, int width, int height);
+void drawVerticalSegment(OLEDDisplay *display, int x, int y, int width, int height);
+
+// UI elements for clock displays
+// void drawWatchFaceToggleButton(OLEDDisplay *display, int16_t x, int16_t y, bool digitalMode = true, float scale = 1);
+void drawBluetoothConnectedIcon(OLEDDisplay *display, int16_t x, int16_t y);
+
+} // namespace ClockRenderer
+
+} // namespace graphics
diff --git a/src/graphics/draw/CompassRenderer.cpp b/src/graphics/draw/CompassRenderer.cpp
new file mode 100644
index 000000000..0e5a1d727
--- /dev/null
+++ b/src/graphics/draw/CompassRenderer.cpp
@@ -0,0 +1,137 @@
+#include "CompassRenderer.h"
+#include "NodeDB.h"
+#include "UIRenderer.h"
+#include "configuration.h"
+#include "gps/GeoCoord.h"
+#include "graphics/ScreenFonts.h"
+#include "graphics/SharedUIDisplay.h"
+#include
+
+namespace graphics
+{
+namespace CompassRenderer
+{
+
+// Point helper class for compass calculations
+struct Point {
+ float x, y;
+ Point(float x, float y) : x(x), y(y) {}
+
+ void rotate(float angle)
+ {
+ float cos_a = cos(angle);
+ float sin_a = sin(angle);
+ float new_x = x * cos_a - y * sin_a;
+ float new_y = x * sin_a + y * cos_a;
+ x = new_x;
+ y = new_y;
+ }
+
+ void scale(float factor)
+ {
+ x *= factor;
+ y *= factor;
+ }
+
+ void translate(float dx, float dy)
+ {
+ x += dx;
+ y += dy;
+ }
+};
+
+void drawCompassNorth(OLEDDisplay *display, int16_t compassX, int16_t compassY, float myHeading, int16_t radius)
+{
+ // Show the compass heading (not implemented in original)
+ // This could draw a "N" indicator or north arrow
+ // For now, we'll draw a simple north indicator
+ // const float radius = 17.0f;
+ if (isHighResolution) {
+ radius += 4;
+ }
+ Point north(0, -radius);
+ if (uiconfig.compass_mode != meshtastic_CompassMode_FIXED_RING)
+ north.rotate(-myHeading);
+ north.translate(compassX, compassY);
+
+ display->setFont(FONT_SMALL);
+ display->setTextAlignment(TEXT_ALIGN_CENTER);
+ display->setColor(BLACK);
+ if (isHighResolution) {
+ display->fillRect(north.x - 8, north.y - 1, display->getStringWidth("N") + 3, FONT_HEIGHT_SMALL - 6);
+ } else {
+ display->fillRect(north.x - 4, north.y - 1, display->getStringWidth("N") + 2, FONT_HEIGHT_SMALL - 6);
+ }
+ display->setColor(WHITE);
+ display->drawString(north.x, north.y - 3, "N");
+}
+
+void drawNodeHeading(OLEDDisplay *display, int16_t compassX, int16_t compassY, uint16_t compassDiam, float headingRadian)
+{
+ Point tip(0.0f, -0.5f), tail(0.0f, 0.35f); // pointing up initially
+ float arrowOffsetX = 0.14f, arrowOffsetY = 0.9f;
+ Point leftArrow(tip.x - arrowOffsetX, tip.y + arrowOffsetY), rightArrow(tip.x + arrowOffsetX, tip.y + arrowOffsetY);
+
+ Point *arrowPoints[] = {&tip, &tail, &leftArrow, &rightArrow};
+
+ for (int i = 0; i < 4; i++) {
+ arrowPoints[i]->rotate(headingRadian);
+ arrowPoints[i]->scale(compassDiam * 0.6);
+ arrowPoints[i]->translate(compassX, compassY);
+ }
+
+#ifdef USE_EINK
+ display->drawTriangle(tip.x, tip.y, rightArrow.x, rightArrow.y, tail.x, tail.y);
+#else
+ display->fillTriangle(tip.x, tip.y, rightArrow.x, rightArrow.y, tail.x, tail.y);
+#endif
+ display->drawTriangle(tip.x, tip.y, leftArrow.x, leftArrow.y, tail.x, tail.y);
+}
+
+void drawArrowToNode(OLEDDisplay *display, int16_t x, int16_t y, int16_t size, float bearing)
+{
+ float radians = bearing * DEG_TO_RAD;
+
+ Point tip(0, -size / 2);
+ Point left(-size / 6, size / 4);
+ Point right(size / 6, size / 4);
+ Point tail(0, size / 4.5);
+
+ tip.rotate(radians);
+ left.rotate(radians);
+ right.rotate(radians);
+ tail.rotate(radians);
+
+ tip.translate(x, y);
+ left.translate(x, y);
+ right.translate(x, y);
+ tail.translate(x, y);
+
+ display->fillTriangle(tip.x, tip.y, left.x, left.y, tail.x, tail.y);
+ display->fillTriangle(tip.x, tip.y, right.x, right.y, tail.x, tail.y);
+}
+
+float estimatedHeading(double lat, double lon)
+{
+ // Simple magnetic declination estimation
+ // This is a very basic implementation - the original might be more sophisticated
+ return 0.0f; // Return 0 for now, indicating no heading available
+}
+
+uint16_t getCompassDiam(uint32_t displayWidth, uint32_t displayHeight)
+{
+ // Calculate appropriate compass diameter based on display size
+ uint16_t minDimension = (displayWidth < displayHeight) ? displayWidth : displayHeight;
+ uint16_t maxDiam = minDimension / 3; // Use 1/3 of the smaller dimension
+
+ // Ensure minimum and maximum bounds
+ if (maxDiam < 16)
+ maxDiam = 16;
+ if (maxDiam > 64)
+ maxDiam = 64;
+
+ return maxDiam;
+}
+
+} // namespace CompassRenderer
+} // namespace graphics
diff --git a/src/graphics/draw/CompassRenderer.h b/src/graphics/draw/CompassRenderer.h
new file mode 100644
index 000000000..ca7532b66
--- /dev/null
+++ b/src/graphics/draw/CompassRenderer.h
@@ -0,0 +1,33 @@
+#pragma once
+
+#include "graphics/Screen.h"
+#include "mesh/generated/meshtastic/mesh.pb.h"
+#include
+#include
+
+namespace graphics
+{
+
+/// Forward declarations
+class Screen;
+
+/**
+ * @brief Compass and navigation drawing functions
+ *
+ * Contains all functions related to drawing compass elements, headings,
+ * navigation arrows, and location-based UI components.
+ */
+namespace CompassRenderer
+{
+// Compass drawing functions
+void drawCompassNorth(OLEDDisplay *display, int16_t compassX, int16_t compassY, float myHeading, int16_t radius);
+void drawNodeHeading(OLEDDisplay *display, int16_t compassX, int16_t compassY, uint16_t compassDiam, float headingRadian);
+void drawArrowToNode(OLEDDisplay *display, int16_t x, int16_t y, int16_t size, float bearing);
+
+// Navigation and location functions
+float estimatedHeading(double lat, double lon);
+uint16_t getCompassDiam(uint32_t displayWidth, uint32_t displayHeight);
+
+} // namespace CompassRenderer
+
+} // namespace graphics
diff --git a/src/graphics/draw/DebugRenderer.cpp b/src/graphics/draw/DebugRenderer.cpp
new file mode 100644
index 000000000..b1a901f99
--- /dev/null
+++ b/src/graphics/draw/DebugRenderer.cpp
@@ -0,0 +1,622 @@
+#include "configuration.h"
+#if HAS_SCREEN
+#include "../Screen.h"
+#include "DebugRenderer.h"
+#include "FSCommon.h"
+#include "NodeDB.h"
+#include "Throttle.h"
+#include "UIRenderer.h"
+#include "airtime.h"
+#include "gps/RTC.h"
+#include "graphics/ScreenFonts.h"
+#include "graphics/SharedUIDisplay.h"
+#include "graphics/images.h"
+#include "main.h"
+#include "mesh/Channels.h"
+#include "mesh/generated/meshtastic/deviceonly.pb.h"
+#include "sleep.h"
+
+#if HAS_WIFI && !defined(ARCH_PORTDUINO)
+#include "mesh/wifi/WiFiAPClient.h"
+#include
+#ifdef ARCH_ESP32
+#include "mesh/wifi/WiFiAPClient.h"
+#endif
+#endif
+
+#ifdef ARCH_ESP32
+#include "modules/StoreForwardModule.h"
+#endif
+#include
+#include
+#include
+
+using namespace meshtastic;
+
+// External variables
+extern graphics::Screen *screen;
+extern PowerStatus *powerStatus;
+extern NodeStatus *nodeStatus;
+extern GPSStatus *gpsStatus;
+extern Channels channels;
+extern AirTime *airTime;
+
+// External functions from Screen.cpp
+extern bool heartbeat;
+
+#ifdef ARCH_ESP32
+extern StoreForwardModule *storeForwardModule;
+#endif
+
+namespace graphics
+{
+namespace DebugRenderer
+{
+
+void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
+{
+ display->setFont(FONT_SMALL);
+
+ // The coordinates define the left starting point of the text
+ display->setTextAlignment(TEXT_ALIGN_LEFT);
+
+ if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_INVERTED) {
+ display->fillRect(0 + x, 0 + y, x + display->getWidth(), y + FONT_HEIGHT_SMALL);
+ display->setColor(BLACK);
+ }
+
+ char channelStr[20];
+ snprintf(channelStr, sizeof(channelStr), "#%s", channels.getName(channels.getPrimaryIndex()));
+ // Display nodes status
+ if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_DEFAULT) {
+ UIRenderer::drawNodes(display, x + (SCREEN_WIDTH * 0.25), y + 2, nodeStatus);
+ } else {
+ UIRenderer::drawNodes(display, x + (SCREEN_WIDTH * 0.25), y + 3, nodeStatus);
+ }
+#if HAS_GPS
+ // Display GPS status
+ if (config.position.gps_mode != meshtastic_Config_PositionConfig_GpsMode_ENABLED) {
+ UIRenderer::drawGpsPowerStatus(display, x, y + 2, gpsStatus);
+ } else {
+ if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_DEFAULT) {
+ UIRenderer::drawGps(display, x + (SCREEN_WIDTH * 0.63), y + 2, gpsStatus);
+ } else {
+ UIRenderer::drawGps(display, x + (SCREEN_WIDTH * 0.63), y + 3, gpsStatus);
+ }
+ }
+#endif
+ display->setColor(WHITE);
+ // Draw the channel name
+ display->drawString(x, y + FONT_HEIGHT_SMALL, channelStr);
+ // Draw our hardware ID to assist with bluetooth pairing. Either prefix with Info or S&F Logo
+ if (moduleConfig.store_forward.enabled) {
+#ifdef ARCH_ESP32
+ if (!Throttle::isWithinTimespanMs(storeForwardModule->lastHeartbeat,
+ (storeForwardModule->heartbeatInterval * 1200))) { // no heartbeat, overlap a bit
+#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \
+ defined(ST7789_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(HX8357_CS) || ARCH_PORTDUINO) && \
+ !defined(DISPLAY_FORCE_SMALL_FONTS)
+ display->drawFastImage(x + SCREEN_WIDTH - 14 - display->getStringWidth(screen->ourId), y + 3 + FONT_HEIGHT_SMALL, 12,
+ 8, imgQuestionL1);
+ display->drawFastImage(x + SCREEN_WIDTH - 14 - display->getStringWidth(screen->ourId), y + 11 + FONT_HEIGHT_SMALL, 12,
+ 8, imgQuestionL2);
+#else
+ display->drawFastImage(x + SCREEN_WIDTH - 10 - display->getStringWidth(screen->ourId), y + 2 + FONT_HEIGHT_SMALL, 8,
+ 8, imgQuestion);
+#endif
+ } else {
+#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \
+ defined(ST7789_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(HX8357_CS)) && \
+ !defined(DISPLAY_FORCE_SMALL_FONTS)
+ display->drawFastImage(x + SCREEN_WIDTH - 18 - display->getStringWidth(screen->ourId), y + 3 + FONT_HEIGHT_SMALL, 16,
+ 8, imgSFL1);
+ display->drawFastImage(x + SCREEN_WIDTH - 18 - display->getStringWidth(screen->ourId), y + 11 + FONT_HEIGHT_SMALL, 16,
+ 8, imgSFL2);
+#else
+ display->drawFastImage(x + SCREEN_WIDTH - 13 - display->getStringWidth(screen->ourId), y + 2 + FONT_HEIGHT_SMALL, 11,
+ 8, imgSF);
+#endif
+ }
+#endif
+ } else {
+ // TODO: Raspberry Pi supports more than just the one screen size
+#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \
+ defined(ST7789_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(HX8357_CS) || ARCH_PORTDUINO) && \
+ !defined(DISPLAY_FORCE_SMALL_FONTS)
+ display->drawFastImage(x + SCREEN_WIDTH - 14 - display->getStringWidth(screen->ourId), y + 3 + FONT_HEIGHT_SMALL, 12, 8,
+ imgInfoL1);
+ display->drawFastImage(x + SCREEN_WIDTH - 14 - display->getStringWidth(screen->ourId), y + 11 + FONT_HEIGHT_SMALL, 12, 8,
+ imgInfoL2);
+#else
+ display->drawFastImage(x + SCREEN_WIDTH - 10 - display->getStringWidth(screen->ourId), y + 2 + FONT_HEIGHT_SMALL, 8, 8,
+ imgInfo);
+#endif
+ }
+
+ display->drawString(x + SCREEN_WIDTH - display->getStringWidth(screen->ourId), y + FONT_HEIGHT_SMALL, screen->ourId);
+
+ // Draw any log messages
+ display->drawLogBuffer(x, y + (FONT_HEIGHT_SMALL * 2));
+
+ /* Display a heartbeat pixel that blinks every time the frame is redrawn */
+#ifdef SHOW_REDRAWS
+ if (heartbeat)
+ display->setPixel(0, 0);
+ heartbeat = !heartbeat;
+#endif
+}
+
+// ****************************
+// * WiFi Screen *
+// ****************************
+void drawFrameWiFi(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
+{
+#if HAS_WIFI && !defined(ARCH_PORTDUINO)
+ display->clear();
+ display->setTextAlignment(TEXT_ALIGN_LEFT);
+ display->setFont(FONT_SMALL);
+ int line = 1;
+
+ // === Set Title
+ const char *titleStr = "WiFi";
+
+ // === Header ===
+ graphics::drawCommonHeader(display, x, y, titleStr);
+
+ const char *wifiName = config.network.wifi_ssid;
+
+ if (WiFi.status() != WL_CONNECTED) {
+ display->drawString(x, getTextPositions(display)[line++], "WiFi: Not Connected");
+ } else {
+ display->drawString(x, getTextPositions(display)[line++], "WiFi: Connected");
+
+ char rssiStr[32];
+ snprintf(rssiStr, sizeof(rssiStr), "RSSI: %d", WiFi.RSSI());
+ display->drawString(x, getTextPositions(display)[line++], rssiStr);
+ }
+
+ /*
+ - WL_CONNECTED: assigned when connected to a WiFi network;
+ - WL_NO_SSID_AVAIL: assigned when no SSID are available;
+ - WL_CONNECT_FAILED: assigned when the connection fails for all the attempts;
+ - WL_CONNECTION_LOST: assigned when the connection is lost;
+ - WL_DISCONNECTED: assigned when disconnected from a network;
+ - WL_IDLE_STATUS: it is a temporary status assigned when WiFi.begin() is called and remains active until the number of
+ attempts expires (resulting in WL_CONNECT_FAILED) or a connection is established (resulting in WL_CONNECTED);
+ - WL_SCAN_COMPLETED: assigned when the scan networks is completed;
+ - WL_NO_SHIELD: assigned when no WiFi shield is present;
+
+ */
+ if (WiFi.status() == WL_CONNECTED) {
+ char ipStr[64];
+ snprintf(ipStr, sizeof(ipStr), "IP: %s", WiFi.localIP().toString().c_str());
+ display->drawString(x, getTextPositions(display)[line++], ipStr);
+ } else if (WiFi.status() == WL_NO_SSID_AVAIL) {
+ display->drawString(x, getTextPositions(display)[line++], "SSID Not Found");
+ } else if (WiFi.status() == WL_CONNECTION_LOST) {
+ display->drawString(x, getTextPositions(display)[line++], "Connection Lost");
+ } else if (WiFi.status() == WL_IDLE_STATUS) {
+ display->drawString(x, getTextPositions(display)[line++], "Idle ... Reconnecting");
+ } else if (WiFi.status() == WL_CONNECT_FAILED) {
+ display->drawString(x, getTextPositions(display)[line++], "Connection Failed");
+ }
+#ifdef ARCH_ESP32
+ else {
+ // Codes:
+ // https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/wifi.html#wi-fi-reason-code
+ display->drawString(x, getTextPositions(display)[line++],
+ WiFi.disconnectReasonName(static_cast(getWifiDisconnectReason())));
+ }
+#else
+ else {
+ char statusStr[32];
+ snprintf(statusStr, sizeof(statusStr), "Unknown status: %d", WiFi.status());
+ display->drawString(x, getTextPositions(display)[line++], statusStr);
+ }
+#endif
+
+ char ssidStr[64];
+ snprintf(ssidStr, sizeof(ssidStr), "SSID: %s", wifiName);
+ display->drawString(x, getTextPositions(display)[line++], ssidStr);
+
+ display->drawString(x, getTextPositions(display)[line++], "URL: http://meshtastic.local");
+
+ /* Display a heartbeat pixel that blinks every time the frame is redrawn */
+#ifdef SHOW_REDRAWS
+ if (heartbeat)
+ display->setPixel(0, 0);
+ heartbeat = !heartbeat;
+#endif
+#endif
+}
+
+void drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
+{
+ display->setFont(FONT_SMALL);
+
+ // The coordinates define the left starting point of the text
+ display->setTextAlignment(TEXT_ALIGN_LEFT);
+
+ if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_INVERTED) {
+ display->fillRect(0 + x, 0 + y, x + display->getWidth(), y + FONT_HEIGHT_SMALL);
+ display->setColor(BLACK);
+ }
+
+ char batStr[20];
+ if (powerStatus->getHasBattery()) {
+ int batV = powerStatus->getBatteryVoltageMv() / 1000;
+ int batCv = (powerStatus->getBatteryVoltageMv() % 1000) / 10;
+
+ snprintf(batStr, sizeof(batStr), "B %01d.%02dV %3d%% %c%c", batV, batCv, powerStatus->getBatteryChargePercent(),
+ powerStatus->getIsCharging() ? '+' : ' ', powerStatus->getHasUSB() ? 'U' : ' ');
+
+ // Line 1
+ display->drawString(x, y, batStr);
+ if (config.display.heading_bold)
+ display->drawString(x + 1, y, batStr);
+ } else {
+ // Line 1
+ display->drawString(x, y, "USB");
+ if (config.display.heading_bold)
+ display->drawString(x + 1, y, "USB");
+ }
+
+ // auto mode = DisplayFormatters::getModemPresetDisplayName(config.lora.modem_preset, true);
+
+ // display->drawString(x + SCREEN_WIDTH - display->getStringWidth(mode), y, mode);
+ // if (config.display.heading_bold)
+ // display->drawString(x + SCREEN_WIDTH - display->getStringWidth(mode) - 1, y, mode);
+
+ uint32_t currentMillis = millis();
+ uint32_t seconds = currentMillis / 1000;
+ uint32_t minutes = seconds / 60;
+ uint32_t hours = minutes / 60;
+ uint32_t days = hours / 24;
+ // currentMillis %= 1000;
+ // seconds %= 60;
+ // minutes %= 60;
+ // hours %= 24;
+
+ // Show uptime as days, hours, minutes OR seconds
+ std::string uptime = UIRenderer::drawTimeDelta(days, hours, minutes, seconds);
+
+ // Line 1 (Still)
+ display->drawString(x + SCREEN_WIDTH - display->getStringWidth(uptime.c_str()), y, uptime.c_str());
+ if (config.display.heading_bold)
+ display->drawString(x - 1 + SCREEN_WIDTH - display->getStringWidth(uptime.c_str()), y, uptime.c_str());
+
+ display->setColor(WHITE);
+
+ // Setup string to assemble analogClock string
+ std::string analogClock = "";
+
+ uint32_t rtc_sec = getValidTime(RTCQuality::RTCQualityDevice, true); // Display local timezone
+ if (rtc_sec > 0) {
+ long hms = rtc_sec % SEC_PER_DAY;
+ // hms += tz.tz_dsttime * SEC_PER_HOUR;
+ // hms -= tz.tz_minuteswest * SEC_PER_MIN;
+ // mod `hms` to ensure in positive range of [0...SEC_PER_DAY)
+ hms = (hms + SEC_PER_DAY) % SEC_PER_DAY;
+
+ // Tear apart hms into h:m:s
+ int hour = hms / SEC_PER_HOUR;
+ int min = (hms % SEC_PER_HOUR) / SEC_PER_MIN;
+ int sec = (hms % SEC_PER_HOUR) % SEC_PER_MIN; // or hms % SEC_PER_MIN
+
+ char timebuf[12];
+
+ if (config.display.use_12h_clock) {
+ std::string meridiem = "am";
+ if (hour >= 12) {
+ if (hour > 12)
+ hour -= 12;
+ meridiem = "pm";
+ }
+ if (hour == 00) {
+ hour = 12;
+ }
+ snprintf(timebuf, sizeof(timebuf), "%d:%02d:%02d%s", hour, min, sec, meridiem.c_str());
+ } else {
+ snprintf(timebuf, sizeof(timebuf), "%02d:%02d:%02d", hour, min, sec);
+ }
+ analogClock += timebuf;
+ }
+
+ // Line 2
+ display->drawString(x, y + FONT_HEIGHT_SMALL * 1, analogClock.c_str());
+
+ // Display Channel Utilization
+ char chUtil[13];
+ snprintf(chUtil, sizeof(chUtil), "ChUtil %2.0f%%", airTime->channelUtilizationPercent());
+ display->drawString(x + SCREEN_WIDTH - display->getStringWidth(chUtil), y + FONT_HEIGHT_SMALL * 1, chUtil);
+
+#if HAS_GPS
+ if (config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_ENABLED) {
+ // Line 3
+ if (config.display.gps_format !=
+ meshtastic_Config_DisplayConfig_GpsCoordinateFormat_DMS) // if DMS then don't draw altitude
+ UIRenderer::drawGpsAltitude(display, x, y + FONT_HEIGHT_SMALL * 2, gpsStatus);
+
+ // Line 4
+ UIRenderer::drawGpsCoordinates(display, x, y + FONT_HEIGHT_SMALL * 3, gpsStatus);
+ } else {
+ UIRenderer::drawGpsPowerStatus(display, x, y + FONT_HEIGHT_SMALL * 2, gpsStatus);
+ }
+#endif
+/* Display a heartbeat pixel that blinks every time the frame is redrawn */
+#ifdef SHOW_REDRAWS
+ if (heartbeat)
+ display->setPixel(0, 0);
+ heartbeat = !heartbeat;
+#endif
+}
+
+// Trampoline functions for DebugInfo class access
+void drawDebugInfoTrampoline(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
+{
+ drawFrame(display, state, x, y);
+}
+
+void drawDebugInfoSettingsTrampoline(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
+{
+ drawFrameSettings(display, state, x, y);
+}
+
+void drawDebugInfoWiFiTrampoline(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
+{
+ drawFrameWiFi(display, state, x, y);
+}
+
+// ****************************
+// * LoRa Focused Screen *
+// ****************************
+void drawLoRaFocused(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
+{
+ display->clear();
+ display->setTextAlignment(TEXT_ALIGN_LEFT);
+ display->setFont(FONT_SMALL);
+ int line = 1;
+
+ // === Set Title
+ const char *titleStr = (isHighResolution) ? "LoRa Info" : "LoRa";
+
+ // === Header ===
+ graphics::drawCommonHeader(display, x, y, titleStr);
+
+ // === First Row: Region / BLE Name ===
+ graphics::UIRenderer::drawNodes(display, x, getTextPositions(display)[line] + 2, nodeStatus, 0, true, "");
+
+ uint8_t dmac[6];
+ char shortnameble[35];
+ getMacAddr(dmac);
+ snprintf(screen->ourId, sizeof(screen->ourId), "%02x%02x", dmac[4], dmac[5]);
+ snprintf(shortnameble, sizeof(shortnameble), "BLE: %s", screen->ourId);
+ int textWidth = display->getStringWidth(shortnameble);
+ int nameX = (SCREEN_WIDTH - textWidth);
+ display->drawString(nameX, getTextPositions(display)[line++], shortnameble);
+
+ // === Second Row: Radio Preset ===
+ auto mode = DisplayFormatters::getModemPresetDisplayName(config.lora.modem_preset, false);
+ char regionradiopreset[25];
+ const char *region = myRegion ? myRegion->name : NULL;
+ if (region != nullptr) {
+ snprintf(regionradiopreset, sizeof(regionradiopreset), "%s/%s", region, mode);
+ }
+ textWidth = display->getStringWidth(regionradiopreset);
+ nameX = (SCREEN_WIDTH - textWidth) / 2;
+ display->drawString(nameX, getTextPositions(display)[line++], regionradiopreset);
+
+ // === Third Row: Frequency / ChanNum ===
+ char frequencyslot[35];
+ char freqStr[16];
+ float freq = RadioLibInterface::instance->getFreq();
+ snprintf(freqStr, sizeof(freqStr), "%.3f", freq);
+ if (config.lora.channel_num == 0) {
+ snprintf(frequencyslot, sizeof(frequencyslot), "Freq: %smhz", freqStr);
+ } else {
+ snprintf(frequencyslot, sizeof(frequencyslot), "Freq/Ch: %smhz (%d)", freqStr, config.lora.channel_num);
+ }
+ size_t len = strlen(frequencyslot);
+ if (len >= 4 && strcmp(frequencyslot + len - 4, " (0)") == 0) {
+ frequencyslot[len - 4] = '\0'; // Remove the last three characters
+ }
+ textWidth = display->getStringWidth(frequencyslot);
+ nameX = (SCREEN_WIDTH - textWidth) / 2;
+ display->drawString(nameX, getTextPositions(display)[line++], frequencyslot);
+
+ // === Fourth Row: Channel Utilization ===
+ const char *chUtil = "ChUtil:";
+ char chUtilPercentage[10];
+ snprintf(chUtilPercentage, sizeof(chUtilPercentage), "%2.0f%%", airTime->channelUtilizationPercent());
+
+ int chUtil_x = (isHighResolution) ? display->getStringWidth(chUtil) + 10 : display->getStringWidth(chUtil) + 5;
+ int chUtil_y = getTextPositions(display)[line] + 3;
+
+ int chutil_bar_width = (isHighResolution) ? 100 : 50;
+ int chutil_bar_height = (isHighResolution) ? 12 : 7;
+ int extraoffset = (isHighResolution) ? 6 : 3;
+ int chutil_percent = airTime->channelUtilizationPercent();
+
+ int centerofscreen = SCREEN_WIDTH / 2;
+ int total_line_content_width = (chUtil_x + chutil_bar_width + display->getStringWidth(chUtilPercentage) + extraoffset) / 2;
+ int starting_position = centerofscreen - total_line_content_width;
+
+ display->drawString(starting_position, getTextPositions(display)[line++], chUtil);
+
+ // Force 56% or higher to show a full 100% bar, text would still show related percent.
+ if (chutil_percent >= 61) {
+ chutil_percent = 100;
+ }
+
+ // Weighting for nonlinear segments
+ float milestone1 = 25;
+ float milestone2 = 40;
+ float weight1 = 0.45; // Weight for 0โ25%
+ float weight2 = 0.35; // Weight for 25โ40%
+ float weight3 = 0.20; // Weight for 40โ100%
+ float totalWeight = weight1 + weight2 + weight3;
+
+ int seg1 = chutil_bar_width * (weight1 / totalWeight);
+ int seg2 = chutil_bar_width * (weight2 / totalWeight);
+ int seg3 = chutil_bar_width * (weight3 / totalWeight);
+
+ int fillRight = 0;
+
+ if (chutil_percent <= milestone1) {
+ fillRight = (seg1 * (chutil_percent / milestone1));
+ } else if (chutil_percent <= milestone2) {
+ fillRight = seg1 + (seg2 * ((chutil_percent - milestone1) / (milestone2 - milestone1)));
+ } else {
+ fillRight = seg1 + seg2 + (seg3 * ((chutil_percent - milestone2) / (100 - milestone2)));
+ }
+
+ // Draw outline
+ display->drawRect(starting_position + chUtil_x, chUtil_y, chutil_bar_width, chutil_bar_height);
+
+ // Fill progress
+ if (fillRight > 0) {
+ display->fillRect(starting_position + chUtil_x, chUtil_y, fillRight, chutil_bar_height);
+ }
+
+ display->drawString(starting_position + chUtil_x + chutil_bar_width + extraoffset, getTextPositions(display)[4],
+ chUtilPercentage);
+}
+
+// ****************************
+// * Memory Screen *
+// ****************************
+void drawMemoryUsage(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
+{
+ display->clear();
+ display->setFont(FONT_SMALL);
+ display->setTextAlignment(TEXT_ALIGN_LEFT);
+
+ // === Set Title
+ const char *titleStr = "System";
+
+ // === Header ===
+ graphics::drawCommonHeader(display, x, y, titleStr);
+
+ // === Layout ===
+ int line = 1;
+ const int barHeight = 6;
+ const int labelX = x;
+ int barsOffset = (isHighResolution) ? 24 : 0;
+#ifdef USE_EINK
+ barsOffset -= 12;
+#endif
+ const int barX = x + 40 + barsOffset;
+
+ auto drawUsageRow = [&](const char *label, uint32_t used, uint32_t total, bool isHeap = false) {
+ if (total == 0)
+ return;
+
+ int percent = (used * 100) / total;
+
+ char combinedStr[24];
+ if (isHighResolution) {
+ snprintf(combinedStr, sizeof(combinedStr), "%s%3d%% %u/%uKB", (percent > 80) ? "! " : "", percent, used / 1024,
+ total / 1024);
+ } else {
+ snprintf(combinedStr, sizeof(combinedStr), "%s%3d%%", (percent > 80) ? "! " : "", percent);
+ }
+
+ int textWidth = display->getStringWidth(combinedStr);
+ int adjustedBarWidth = SCREEN_WIDTH - barX - textWidth - 6;
+ if (adjustedBarWidth < 10)
+ adjustedBarWidth = 10;
+
+ int fillWidth = (used * adjustedBarWidth) / total;
+
+ // Label
+ display->setTextAlignment(TEXT_ALIGN_LEFT);
+ display->drawString(labelX, getTextPositions(display)[line], label);
+
+ // Bar
+ int barY = getTextPositions(display)[line] + (FONT_HEIGHT_SMALL - barHeight) / 2;
+ display->setColor(WHITE);
+ display->drawRect(barX, barY, adjustedBarWidth, barHeight);
+
+ display->fillRect(barX, barY, fillWidth, barHeight);
+ display->setColor(WHITE);
+
+ // Value string
+ display->setTextAlignment(TEXT_ALIGN_RIGHT);
+ display->drawString(SCREEN_WIDTH - 2, getTextPositions(display)[line], combinedStr);
+ };
+
+ // === Memory values ===
+ uint32_t heapUsed = memGet.getHeapSize() - memGet.getFreeHeap();
+ uint32_t heapTotal = memGet.getHeapSize();
+
+ uint32_t psramUsed = memGet.getPsramSize() - memGet.getFreePsram();
+ uint32_t psramTotal = memGet.getPsramSize();
+
+ uint32_t flashUsed = 0, flashTotal = 0;
+#ifdef ESP32
+ flashUsed = FSCom.usedBytes();
+ flashTotal = FSCom.totalBytes();
+#endif
+
+ uint32_t sdUsed = 0, sdTotal = 0;
+ bool hasSD = false;
+ /*
+ #ifdef HAS_SDCARD
+ hasSD = SD.cardType() != CARD_NONE;
+ if (hasSD) {
+ sdUsed = SD.usedBytes();
+ sdTotal = SD.totalBytes();
+ }
+ #endif
+ */
+ // === Draw memory rows
+ drawUsageRow("Heap:", heapUsed, heapTotal, true);
+#ifdef ESP32
+ if (psramUsed > 0) {
+ line += 1;
+ drawUsageRow("PSRAM:", psramUsed, psramTotal);
+ }
+ if (flashTotal > 0) {
+ line += 1;
+ drawUsageRow("Flash:", flashUsed, flashTotal);
+ }
+#endif
+ if (hasSD && sdTotal > 0) {
+ line += 1;
+ drawUsageRow("SD:", sdUsed, sdTotal);
+ }
+
+ display->setTextAlignment(TEXT_ALIGN_LEFT);
+ // System Uptime
+ if (line < 2) {
+ line += 1;
+ }
+ line += 1;
+ char appversionstr[35];
+ snprintf(appversionstr, sizeof(appversionstr), "Ver.: %s", optstr(APP_VERSION));
+ int textWidth = display->getStringWidth(appversionstr);
+ int nameX = (SCREEN_WIDTH - textWidth) / 2;
+ display->drawString(nameX, getTextPositions(display)[line], appversionstr);
+
+ if (SCREEN_HEIGHT > 64 || (SCREEN_HEIGHT <= 64 && line < 4)) { // Only show uptime if the screen can show it
+ line += 1;
+ char uptimeStr[32] = "";
+ uint32_t uptime = millis() / 1000;
+ uint32_t days = uptime / 86400;
+ uint32_t hours = (uptime % 86400) / 3600;
+ uint32_t mins = (uptime % 3600) / 60;
+ // Show as "Up: 2d 3h", "Up: 5h 14m", or "Up: 37m"
+ if (days)
+ snprintf(uptimeStr, sizeof(uptimeStr), " Up: %ud %uh", days, hours);
+ else if (hours)
+ snprintf(uptimeStr, sizeof(uptimeStr), " Up: %uh %um", hours, mins);
+ else
+ snprintf(uptimeStr, sizeof(uptimeStr), " Uptime: %um", mins);
+ textWidth = display->getStringWidth(uptimeStr);
+ nameX = (SCREEN_WIDTH - textWidth) / 2;
+ display->drawString(nameX, getTextPositions(display)[line], uptimeStr);
+ }
+}
+} // namespace DebugRenderer
+} // namespace graphics
+#endif
\ No newline at end of file
diff --git a/src/graphics/draw/DebugRenderer.h b/src/graphics/draw/DebugRenderer.h
new file mode 100644
index 000000000..f4d484f58
--- /dev/null
+++ b/src/graphics/draw/DebugRenderer.h
@@ -0,0 +1,38 @@
+#pragma once
+
+#include
+#include
+
+namespace graphics
+{
+
+/// Forward declarations
+class Screen;
+class DebugInfo;
+
+/**
+ * @brief Debug and diagnostic drawing functions
+ *
+ * Contains all functions related to drawing debug information,
+ * WiFi status, settings screens, and diagnostic data.
+ */
+namespace DebugRenderer
+{
+// Debug frame functions
+void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
+void drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
+void drawFrameWiFi(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
+
+// Trampoline functions for framework callback compatibility
+void drawDebugInfoTrampoline(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
+void drawDebugInfoSettingsTrampoline(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
+void drawDebugInfoWiFiTrampoline(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
+
+// LoRa information display
+void drawLoRaFocused(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
+
+// Memory screen display
+void drawMemoryUsage(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
+} // namespace DebugRenderer
+
+} // namespace graphics
diff --git a/src/graphics/draw/DrawRenderers.h b/src/graphics/draw/DrawRenderers.h
new file mode 100644
index 000000000..6f1929ebd
--- /dev/null
+++ b/src/graphics/draw/DrawRenderers.h
@@ -0,0 +1,38 @@
+#pragma once
+
+/**
+ * @brief Master include file for all Screen draw renderers
+ *
+ * This file includes all the individual renderer headers to provide
+ * a convenient single include for accessing all draw functions.
+ */
+
+#include "graphics/draw/ClockRenderer.h"
+#include "graphics/draw/CompassRenderer.h"
+#include "graphics/draw/DebugRenderer.h"
+#include "graphics/draw/NodeListRenderer.h"
+#include "graphics/draw/ScreenRenderer.h"
+#include "graphics/draw/UIRenderer.h"
+
+namespace graphics
+{
+
+/**
+ * @brief Collection of all draw renderers
+ *
+ * This namespace provides access to all the specialized rendering
+ * functions organized by category.
+ */
+namespace DrawRenderers
+{
+// Re-export all renderer namespaces for convenience
+using namespace ClockRenderer;
+using namespace CompassRenderer;
+using namespace DebugRenderer;
+using namespace NodeListRenderer;
+using namespace ScreenRenderer;
+using namespace UIRenderer;
+
+} // namespace DrawRenderers
+
+} // namespace graphics
diff --git a/src/graphics/draw/MenuHandler.cpp b/src/graphics/draw/MenuHandler.cpp
new file mode 100644
index 000000000..f6b250ebc
--- /dev/null
+++ b/src/graphics/draw/MenuHandler.cpp
@@ -0,0 +1,1145 @@
+#include "configuration.h"
+#if HAS_SCREEN
+#include "ClockRenderer.h"
+#include "GPS.h"
+#include "MenuHandler.h"
+#include "MeshRadio.h"
+#include "MeshService.h"
+#include "NodeDB.h"
+#include "buzz.h"
+#include "graphics/Screen.h"
+#include "graphics/SharedUIDisplay.h"
+#include "graphics/draw/UIRenderer.h"
+#include "main.h"
+#include "modules/AdminModule.h"
+#include "modules/CannedMessageModule.h"
+#include "modules/KeyVerificationModule.h"
+
+extern uint16_t TFT_MESH;
+
+namespace graphics
+{
+menuHandler::screenMenus menuHandler::menuQueue = menu_none;
+bool test_enabled = false;
+uint8_t test_count = 0;
+
+void menuHandler::LoraRegionPicker(uint32_t duration)
+{
+ static const char *optionsArray[] = {"Back",
+ "US",
+ "EU_433",
+ "EU_868",
+ "CN",
+ "JP",
+ "ANZ",
+ "KR",
+ "TW",
+ "RU",
+ "IN",
+ "NZ_865",
+ "TH",
+ "LORA_24",
+ "UA_433",
+ "UA_868",
+ "MY_433",
+ "MY_"
+ "919",
+ "SG_"
+ "923",
+ "PH_433",
+ "PH_868",
+ "PH_915",
+ "ANZ_433",
+ "KZ_433",
+ "KZ_863"};
+ BannerOverlayOptions bannerOptions;
+ bannerOptions.message = "Set the LoRa region";
+ bannerOptions.durationMs = duration;
+ bannerOptions.optionsArrayPtr = optionsArray;
+ bannerOptions.optionsCount = 25;
+ bannerOptions.InitialSelected = 0;
+ bannerOptions.bannerCallback = [](int selected) -> void {
+ if (selected != 0 && config.lora.region != _meshtastic_Config_LoRaConfig_RegionCode(selected)) {
+ config.lora.region = _meshtastic_Config_LoRaConfig_RegionCode(selected);
+ // This is needed as we wait til picking the LoRa region to generate keys for the first time.
+ if (!owner.is_licensed) {
+ bool keygenSuccess = false;
+ if (config.security.private_key.size == 32) {
+ // public key is derived from private, so this will always have the same result.
+ if (crypto->regeneratePublicKey(config.security.public_key.bytes, config.security.private_key.bytes)) {
+ keygenSuccess = true;
+ }
+ } else {
+ LOG_INFO("Generate new PKI keys");
+ crypto->generateKeyPair(config.security.public_key.bytes, config.security.private_key.bytes);
+ keygenSuccess = true;
+ }
+ if (keygenSuccess) {
+ config.security.public_key.size = 32;
+ config.security.private_key.size = 32;
+ owner.public_key.size = 32;
+ memcpy(owner.public_key.bytes, config.security.public_key.bytes, 32);
+ }
+ }
+ config.lora.tx_enabled = true;
+ initRegion();
+ if (myRegion->dutyCycle < 100) {
+ config.lora.ignore_mqtt = true; // Ignore MQTT by default if region has a duty cycle limit
+ }
+ service->reloadConfig(SEGMENT_CONFIG);
+ rebootAtMsec = (millis() + DEFAULT_REBOOT_SECONDS * 1000);
+ }
+ };
+ screen->showOverlayBanner(bannerOptions);
+}
+
+void menuHandler::TwelveHourPicker()
+{
+ static const char *optionsArray[] = {"Back", "12-hour", "24-hour"};
+ enum optionsNumbers { Back = 0, twelve = 1, twentyfour = 2 };
+ BannerOverlayOptions bannerOptions;
+ bannerOptions.message = "Time Format";
+ bannerOptions.optionsArrayPtr = optionsArray;
+ bannerOptions.optionsCount = 3;
+ bannerOptions.bannerCallback = [](int selected) -> void {
+ if (selected == Back) {
+ menuHandler::menuQueue = menuHandler::clock_menu;
+ screen->runNow();
+ } else if (selected == twelve) {
+ config.display.use_12h_clock = true;
+ } else {
+ config.display.use_12h_clock = false;
+ }
+ service->reloadConfig(SEGMENT_CONFIG);
+ };
+ screen->showOverlayBanner(bannerOptions);
+}
+
+void menuHandler::ClockFacePicker()
+{
+ static const char *optionsArray[] = {"Back", "Digital", "Analog"};
+ enum optionsNumbers { Back = 0, Digital = 1, Analog = 2 };
+ BannerOverlayOptions bannerOptions;
+ bannerOptions.message = "Which Face?";
+ bannerOptions.optionsArrayPtr = optionsArray;
+ bannerOptions.optionsCount = 3;
+ bannerOptions.bannerCallback = [](int selected) -> void {
+ if (selected == Back) {
+ menuHandler::menuQueue = menuHandler::clock_menu;
+ screen->runNow();
+ } else if (selected == Digital) {
+ uiconfig.is_clockface_analog = false;
+ saveUIConfig();
+ screen->setFrames(Screen::FOCUS_CLOCK);
+ } else {
+ uiconfig.is_clockface_analog = true;
+ saveUIConfig();
+ screen->setFrames(Screen::FOCUS_CLOCK);
+ }
+ };
+ bannerOptions.InitialSelected = uiconfig.is_clockface_analog ? 2 : 1;
+ screen->showOverlayBanner(bannerOptions);
+}
+
+void menuHandler::TZPicker()
+{
+ static const char *optionsArray[] = {"Back",
+ "US/Hawaii",
+ "US/Alaska",
+ "US/Pacific",
+ "US/Arizona",
+ "US/Mountain",
+ "US/Central",
+ "US/Eastern",
+ "UTC",
+ "EU/Western",
+ "EU/"
+ "Central",
+ "EU/Eastern",
+ "Asia/Kolkata",
+ "Asia/Hong_Kong",
+ "AU/AWST",
+ "AU/ACST",
+ "AU/AEST",
+ "Pacific/NZ"};
+ BannerOverlayOptions bannerOptions;
+ bannerOptions.message = "Pick Timezone";
+ bannerOptions.optionsArrayPtr = optionsArray;
+ bannerOptions.optionsCount = 17;
+ bannerOptions.bannerCallback = [](int selected) -> void {
+ if (selected == 0) {
+ menuHandler::menuQueue = menuHandler::clock_menu;
+ screen->runNow();
+ } else if (selected == 1) { // Hawaii
+ strncpy(config.device.tzdef, "HST10", sizeof(config.device.tzdef));
+ } else if (selected == 2) { // Alaska
+ strncpy(config.device.tzdef, "AKST9AKDT,M3.2.0,M11.1.0", sizeof(config.device.tzdef));
+ } else if (selected == 3) { // Pacific
+ strncpy(config.device.tzdef, "PST8PDT,M3.2.0,M11.1.0", sizeof(config.device.tzdef));
+ } else if (selected == 4) { // Arizona
+ strncpy(config.device.tzdef, "MST7", sizeof(config.device.tzdef));
+ } else if (selected == 5) { // Mountain
+ strncpy(config.device.tzdef, "MST7MDT,M3.2.0,M11.1.0", sizeof(config.device.tzdef));
+ } else if (selected == 6) { // Central
+ strncpy(config.device.tzdef, "CST6CDT,M3.2.0,M11.1.0", sizeof(config.device.tzdef));
+ } else if (selected == 7) { // Eastern
+ strncpy(config.device.tzdef, "EST5EDT,M3.2.0,M11.1.0", sizeof(config.device.tzdef));
+ } else if (selected == 8) { // UTC
+ strncpy(config.device.tzdef, "UTC", sizeof(config.device.tzdef));
+ } else if (selected == 9) { // EU/Western
+ strncpy(config.device.tzdef, "GMT0BST,M3.5.0/1,M10.5.0", sizeof(config.device.tzdef));
+ } else if (selected == 10) { // EU/Central
+ strncpy(config.device.tzdef, "CET-1CEST,M3.5.0,M10.5.0/3", sizeof(config.device.tzdef));
+ } else if (selected == 11) { // EU/Eastern
+ strncpy(config.device.tzdef, "EET-2EEST,M3.5.0/3,M10.5.0/4", sizeof(config.device.tzdef));
+ } else if (selected == 12) { // Asia/Kolkata
+ strncpy(config.device.tzdef, "IST-5:30", sizeof(config.device.tzdef));
+ } else if (selected == 13) { // China
+ strncpy(config.device.tzdef, "HKT-8", sizeof(config.device.tzdef));
+ } else if (selected == 14) { // AU/AWST
+ strncpy(config.device.tzdef, "AWST-8", sizeof(config.device.tzdef));
+ } else if (selected == 15) { // AU/ACST
+ strncpy(config.device.tzdef, "ACST-9:30ACDT,M10.1.0,M4.1.0/3", sizeof(config.device.tzdef));
+ } else if (selected == 16) { // AU/AEST
+ strncpy(config.device.tzdef, "AEST-10AEDT,M10.1.0,M4.1.0/3", sizeof(config.device.tzdef));
+ } else if (selected == 17) { // NZ
+ strncpy(config.device.tzdef, "NZST-12NZDT,M9.5.0,M4.1.0/3", sizeof(config.device.tzdef));
+ }
+ if (selected != 0) {
+ setenv("TZ", config.device.tzdef, 1);
+ service->reloadConfig(SEGMENT_CONFIG);
+ }
+ };
+ screen->showOverlayBanner(bannerOptions);
+}
+
+void menuHandler::clockMenu()
+{
+ static const char *optionsArray[] = {"Back", "Clock Face", "Time Format", "Timezone"};
+ enum optionsNumbers { Back = 0, Clock = 1, Time = 2, Timezone = 3 };
+ BannerOverlayOptions bannerOptions;
+ bannerOptions.message = "Clock Action";
+ bannerOptions.optionsArrayPtr = optionsArray;
+ bannerOptions.optionsCount = 4;
+ bannerOptions.bannerCallback = [](int selected) -> void {
+ if (selected == Clock) {
+ menuHandler::menuQueue = menuHandler::clock_face_picker;
+ screen->runNow();
+ } else if (selected == Time) {
+ menuHandler::menuQueue = menuHandler::twelve_hour_picker;
+ screen->runNow();
+ } else if (selected == Timezone) {
+ menuHandler::menuQueue = menuHandler::TZ_picker;
+ screen->runNow();
+ }
+ };
+ screen->showOverlayBanner(bannerOptions);
+}
+
+void menuHandler::messageResponseMenu()
+{
+ enum optionsNumbers { Back = 0, Dismiss = 1, Preset = 2, Freetext = 3, Aloud = 4, enumEnd = 5 };
+
+ static const char *optionsArray[enumEnd] = {"Back", "Dismiss", "Reply via Preset"};
+ static int optionsEnumArray[enumEnd] = {Back, Dismiss, Preset};
+ int options = 3;
+
+ if (kb_found) {
+ optionsArray[options] = "Reply via Freetext";
+ optionsEnumArray[options++] = Freetext;
+ }
+
+#ifdef HAS_I2S
+ optionsArray[options] = "Read Aloud";
+ optionsEnumArray[options++] = Aloud;
+#endif
+ BannerOverlayOptions bannerOptions;
+ bannerOptions.message = "Message Action";
+ bannerOptions.optionsArrayPtr = optionsArray;
+ bannerOptions.optionsEnumPtr = optionsEnumArray;
+ bannerOptions.optionsCount = options;
+ bannerOptions.bannerCallback = [](int selected) -> void {
+ if (selected == Dismiss) {
+ screen->dismissCurrentFrame();
+ } else if (selected == Preset) {
+ if (devicestate.rx_text_message.to == NODENUM_BROADCAST) {
+ cannedMessageModule->LaunchWithDestination(NODENUM_BROADCAST, devicestate.rx_text_message.channel);
+ } else {
+ cannedMessageModule->LaunchWithDestination(devicestate.rx_text_message.from);
+ }
+ } else if (selected == Freetext) {
+ if (devicestate.rx_text_message.to == NODENUM_BROADCAST) {
+ cannedMessageModule->LaunchFreetextWithDestination(NODENUM_BROADCAST, devicestate.rx_text_message.channel);
+ } else {
+ cannedMessageModule->LaunchFreetextWithDestination(devicestate.rx_text_message.from);
+ }
+ }
+#ifdef HAS_I2S
+ else if (selected == Aloud) {
+ const meshtastic_MeshPacket &mp = devicestate.rx_text_message;
+ const char *msg = reinterpret_cast(mp.decoded.payload.bytes);
+
+ audioThread->readAloud(msg);
+ }
+#endif
+ };
+ screen->showOverlayBanner(bannerOptions);
+}
+
+void menuHandler::homeBaseMenu()
+{
+ enum optionsNumbers { Back, Backlight, Position, Preset, Freetext, Bluetooth, Sleep, enumEnd };
+
+ static const char *optionsArray[enumEnd] = {"Back"};
+ static int optionsEnumArray[enumEnd] = {Back};
+ int options = 1;
+
+#ifdef PIN_EINK_EN
+ optionsArray[options] = "Toggle Backlight";
+ optionsEnumArray[options++] = Backlight;
+#else
+ optionsArray[options] = "Sleep Screen";
+ optionsEnumArray[options++] = Sleep;
+#endif
+
+ optionsArray[options] = "Send Position";
+ optionsEnumArray[options++] = Position;
+ optionsArray[options] = "New Preset Msg";
+ optionsEnumArray[options++] = Preset;
+ if (kb_found) {
+ optionsArray[options] = "New Freetext Msg";
+ optionsEnumArray[options++] = Freetext;
+ }
+ optionsArray[options] = "Bluetooth Toggle";
+ optionsEnumArray[options++] = Bluetooth;
+
+ BannerOverlayOptions bannerOptions;
+ bannerOptions.message = "Home Action";
+ bannerOptions.optionsArrayPtr = optionsArray;
+ bannerOptions.optionsEnumPtr = optionsEnumArray;
+ bannerOptions.optionsCount = options;
+ bannerOptions.bannerCallback = [](int selected) -> void {
+ if (selected == Backlight) {
+#ifdef PIN_EINK_EN
+ if (digitalRead(PIN_EINK_EN) == HIGH) {
+ digitalWrite(PIN_EINK_EN, LOW);
+ } else {
+ digitalWrite(PIN_EINK_EN, HIGH);
+ }
+#endif
+ } else if (selected == Sleep) {
+ screen->setOn(false);
+ } else if (selected == Position) {
+ InputEvent event = {.inputEvent = (input_broker_event)INPUT_BROKER_SEND_PING, .kbchar = 0, .touchX = 0, .touchY = 0};
+ inputBroker->injectInputEvent(&event);
+ } else if (selected == Preset) {
+ cannedMessageModule->LaunchWithDestination(NODENUM_BROADCAST);
+ } else if (selected == Freetext) {
+ cannedMessageModule->LaunchFreetextWithDestination(NODENUM_BROADCAST);
+ } else if (selected == Bluetooth) {
+ menuQueue = bluetooth_toggle_menu;
+ screen->runNow();
+ }
+ };
+ screen->showOverlayBanner(bannerOptions);
+}
+
+void menuHandler::systemBaseMenu()
+{
+ // Check if brightness is supported
+ bool hasSupportBrightness = false;
+#if defined(ST7789_CS) || defined(USE_OLED) || defined(USE_SSD1306) || defined(USE_SH1106) || defined(USE_SH1107) || HAS_TFT
+ hasSupportBrightness = true;
+#endif
+
+ enum optionsNumbers { Back, Notifications, ScreenOptions, PowerMenu, Test, enumEnd };
+ static const char *optionsArray[enumEnd] = {"Back"};
+ static int optionsEnumArray[enumEnd] = {Back};
+ int options = 1;
+
+ optionsArray[options] = "Notifications";
+ optionsEnumArray[options++] = Notifications;
+#if defined(ST7789_CS) || defined(USE_OLED) || defined(USE_SSD1306) || defined(USE_SH1106) || defined(USE_SH1107) || \
+ defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190) || HAS_TFT
+ optionsArray[options] = "Screen Options";
+ optionsEnumArray[options++] = ScreenOptions;
+#endif
+
+ optionsArray[options] = "Reboot/Shutdown";
+ optionsEnumArray[options++] = PowerMenu;
+
+ if (test_enabled) {
+ optionsArray[options] = "Test Menu";
+ optionsEnumArray[options++] = Test;
+ }
+
+ BannerOverlayOptions bannerOptions;
+ bannerOptions.message = "System Action";
+ bannerOptions.optionsArrayPtr = optionsArray;
+ bannerOptions.optionsCount = options;
+ bannerOptions.optionsEnumPtr = optionsEnumArray;
+ bannerOptions.bannerCallback = [](int selected) -> void {
+ if (selected == Notifications) {
+ menuHandler::menuQueue = menuHandler::notifications_menu;
+ screen->runNow();
+ } else if (selected == ScreenOptions) {
+ menuHandler::menuQueue = menuHandler::screen_options_menu;
+ screen->runNow();
+ } else if (selected == PowerMenu) {
+ menuHandler::menuQueue = menuHandler::power_menu;
+ screen->runNow();
+ } else if (selected == Test) {
+ menuHandler::menuQueue = menuHandler::test_menu;
+ screen->runNow();
+ } else if (selected == Back && !test_enabled) {
+ test_count++;
+ if (test_count > 4) {
+ test_enabled = true;
+ }
+ }
+ };
+ screen->showOverlayBanner(bannerOptions);
+}
+
+void menuHandler::favoriteBaseMenu()
+{
+ enum optionsNumbers { Back, Preset, Freetext, Remove, enumEnd };
+ static const char *optionsArray[enumEnd] = {"Back", "New Preset Msg"};
+ static int optionsEnumArray[enumEnd] = {Back, Preset};
+ int options = 2;
+
+ if (kb_found) {
+ optionsArray[options] = "New Freetext Msg";
+ optionsEnumArray[options++] = Freetext;
+ }
+ optionsArray[options] = "Remove Favorite";
+ optionsEnumArray[options++] = Remove;
+
+ BannerOverlayOptions bannerOptions;
+ bannerOptions.message = "Favorites Action";
+ bannerOptions.optionsArrayPtr = optionsArray;
+ bannerOptions.optionsEnumPtr = optionsEnumArray;
+ bannerOptions.optionsCount = options;
+ bannerOptions.bannerCallback = [](int selected) -> void {
+ if (selected == 1) {
+ cannedMessageModule->LaunchWithDestination(graphics::UIRenderer::currentFavoriteNodeNum);
+ } else if (selected == 2 && kb_found) {
+ cannedMessageModule->LaunchFreetextWithDestination(graphics::UIRenderer::currentFavoriteNodeNum);
+ } else if ((!kb_found && selected == 2) || (selected == 3 && kb_found)) {
+ menuHandler::menuQueue = menuHandler::remove_favorite;
+ screen->runNow();
+ }
+ };
+ screen->showOverlayBanner(bannerOptions);
+}
+
+void menuHandler::positionBaseMenu()
+{
+ enum optionsNumbers { Back, GPSToggle, CompassMenu, CompassCalibrate, enumEnd };
+
+ static const char *optionsArray[enumEnd] = {"Back", "GPS Toggle", "Compass"};
+ static int optionsEnumArray[enumEnd] = {Back, GPSToggle, CompassMenu};
+ int options = 3;
+
+ if (accelerometerThread) {
+ optionsArray[options] = "Compass Calibrate";
+ optionsEnumArray[options++] = CompassCalibrate;
+ }
+ BannerOverlayOptions bannerOptions;
+ bannerOptions.message = "Position Action";
+ bannerOptions.optionsArrayPtr = optionsArray;
+ bannerOptions.optionsEnumPtr = optionsEnumArray;
+ bannerOptions.optionsCount = options;
+ bannerOptions.bannerCallback = [](int selected) -> void {
+ if (selected == GPSToggle) {
+ menuQueue = gps_toggle_menu;
+ screen->runNow();
+ } else if (selected == CompassMenu) {
+ menuQueue = compass_point_north_menu;
+ screen->runNow();
+ } else if (selected == CompassCalibrate) {
+ accelerometerThread->calibrate(30);
+ }
+ };
+ screen->showOverlayBanner(bannerOptions);
+}
+
+void menuHandler::nodeListMenu()
+{
+ enum optionsNumbers { Back, Favorite, Verify, Reset };
+ static const char *optionsArray[] = {"Back", "Add Favorite", "Key Verification", "Reset NodeDB"};
+ BannerOverlayOptions bannerOptions;
+ bannerOptions.message = "Node Action";
+ bannerOptions.optionsArrayPtr = optionsArray;
+ bannerOptions.optionsCount = 4;
+ bannerOptions.bannerCallback = [](int selected) -> void {
+ if (selected == Favorite) {
+ menuQueue = add_favorite;
+ screen->runNow();
+ } else if (selected == Verify) {
+ menuQueue = key_verification_init;
+ screen->runNow();
+ } else if (selected == Reset) {
+ menuQueue = reset_node_db_menu;
+ screen->runNow();
+ }
+ };
+ screen->showOverlayBanner(bannerOptions);
+}
+
+void menuHandler::resetNodeDBMenu()
+{
+ static const char *optionsArray[] = {"Back", "Confirm"};
+ BannerOverlayOptions bannerOptions;
+ bannerOptions.message = "Confirm Reset NodeDB";
+ bannerOptions.optionsArrayPtr = optionsArray;
+ bannerOptions.optionsCount = 2;
+ bannerOptions.bannerCallback = [](int selected) -> void {
+ if (selected == 1) {
+ disableBluetooth();
+ LOG_INFO("Initiate node-db reset");
+ nodeDB->resetNodes();
+ rebootAtMsec = (millis() + DEFAULT_REBOOT_SECONDS * 1000);
+ }
+ };
+ screen->showOverlayBanner(bannerOptions);
+}
+
+void menuHandler::compassNorthMenu()
+{
+ enum optionsNumbers { Back, Dynamic, Fixed, Freeze };
+ static const char *optionsArray[] = {"Back", "Dynamic", "Fixed Ring", "Freeze Heading"};
+ BannerOverlayOptions bannerOptions;
+ bannerOptions.message = "North Directions?";
+ bannerOptions.optionsArrayPtr = optionsArray;
+ bannerOptions.optionsCount = 4;
+ bannerOptions.InitialSelected = uiconfig.compass_mode + 1;
+ bannerOptions.bannerCallback = [](int selected) -> void {
+ if (selected == Dynamic) {
+ if (uiconfig.compass_mode != meshtastic_CompassMode_DYNAMIC) {
+ uiconfig.compass_mode = meshtastic_CompassMode_DYNAMIC;
+ saveUIConfig();
+ screen->setFrames(graphics::Screen::FOCUS_PRESERVE);
+ }
+ } else if (selected == Fixed) {
+ if (uiconfig.compass_mode != meshtastic_CompassMode_FIXED_RING) {
+ uiconfig.compass_mode = meshtastic_CompassMode_FIXED_RING;
+ saveUIConfig();
+ screen->setFrames(graphics::Screen::FOCUS_PRESERVE);
+ }
+ } else if (selected == Freeze) {
+ if (uiconfig.compass_mode != meshtastic_CompassMode_FREEZE_HEADING) {
+ uiconfig.compass_mode = meshtastic_CompassMode_FREEZE_HEADING;
+ saveUIConfig();
+ screen->setFrames(graphics::Screen::FOCUS_PRESERVE);
+ }
+ } else if (selected == Back) {
+ menuQueue = position_base_menu;
+ screen->runNow();
+ }
+ };
+ screen->showOverlayBanner(bannerOptions);
+}
+
+#if !MESHTASTIC_EXCLUDE_GPS
+void menuHandler::GPSToggleMenu()
+{
+
+ static const char *optionsArray[] = {"Back", "Enabled", "Disabled"};
+ BannerOverlayOptions bannerOptions;
+ bannerOptions.message = "Toggle GPS";
+ bannerOptions.optionsArrayPtr = optionsArray;
+ bannerOptions.optionsCount = 3;
+ bannerOptions.bannerCallback = [](int selected) -> void {
+ if (selected == 1) {
+ config.position.gps_mode = meshtastic_Config_PositionConfig_GpsMode_ENABLED;
+ playGPSEnableBeep();
+ gps->enable();
+ service->reloadConfig(SEGMENT_CONFIG);
+ } else if (selected == 2) {
+ config.position.gps_mode = meshtastic_Config_PositionConfig_GpsMode_DISABLED;
+ playGPSDisableBeep();
+ gps->disable();
+ service->reloadConfig(SEGMENT_CONFIG);
+ } else {
+ menuQueue = position_base_menu;
+ screen->runNow();
+ }
+ };
+ bannerOptions.InitialSelected = config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_ENABLED ? 1 : 2;
+ screen->showOverlayBanner(bannerOptions);
+}
+#endif
+
+void menuHandler::BluetoothToggleMenu()
+{
+ static const char *optionsArray[] = {"Back", "Enabled", "Disabled"};
+ BannerOverlayOptions bannerOptions;
+ bannerOptions.message = "Toggle Bluetooth";
+ bannerOptions.optionsArrayPtr = optionsArray;
+ bannerOptions.optionsCount = 3;
+ bannerOptions.bannerCallback = [](int selected) -> void {
+ if (selected == 1 || selected == 2) {
+ InputEvent event = {.inputEvent = (input_broker_event)170, .kbchar = 170, .touchX = 0, .touchY = 0};
+ inputBroker->injectInputEvent(&event);
+ }
+ };
+ bannerOptions.InitialSelected = config.bluetooth.enabled ? 1 : 2;
+ screen->showOverlayBanner(bannerOptions);
+}
+
+void menuHandler::BuzzerModeMenu()
+{
+ static const char *optionsArray[] = {"All Enabled", "Disabled", "Notifications", "System Only"};
+ BannerOverlayOptions bannerOptions;
+ bannerOptions.message = "Buzzer Mode";
+ bannerOptions.optionsArrayPtr = optionsArray;
+ bannerOptions.optionsCount = 4;
+ bannerOptions.bannerCallback = [](int selected) -> void {
+ config.device.buzzer_mode = (meshtastic_Config_DeviceConfig_BuzzerMode)selected;
+ service->reloadConfig(SEGMENT_CONFIG);
+ };
+ bannerOptions.InitialSelected = config.device.buzzer_mode;
+ screen->showOverlayBanner(bannerOptions);
+}
+
+void menuHandler::BrightnessPickerMenu()
+{
+ static const char *optionsArray[] = {"Back", "Low", "Medium", "High"};
+
+ // Get current brightness level to set initial selection
+ int currentSelection = 1; // Default to Medium
+ if (uiconfig.screen_brightness >= 255) {
+ currentSelection = 3; // Very High
+ } else if (uiconfig.screen_brightness >= 128) {
+ currentSelection = 2; // High
+ } else {
+ currentSelection = 1; // Medium
+ }
+
+ BannerOverlayOptions bannerOptions;
+ bannerOptions.message = "Brightness";
+ bannerOptions.optionsArrayPtr = optionsArray;
+ bannerOptions.optionsCount = 4;
+ bannerOptions.bannerCallback = [](int selected) -> void {
+ if (selected == 1) { // Medium
+ uiconfig.screen_brightness = 64;
+ } else if (selected == 2) { // High
+ uiconfig.screen_brightness = 128;
+ } else if (selected == 3) { // Very High
+ uiconfig.screen_brightness = 255;
+ }
+
+ if (selected != 0) { // Not "Back"
+ // Apply brightness immediately
+#if defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190)
+ // For HELTEC devices, use analogWrite to control backlight
+ analogWrite(VTFT_LEDA, uiconfig.screen_brightness);
+#elif defined(ST7789_CS)
+ static_cast(screen->getDisplayDevice())->setDisplayBrightness(uiconfig.screen_brightness);
+#elif defined(USE_OLED) || defined(USE_SSD1306) || defined(USE_SH1106) || defined(USE_SH1107)
+ screen->getDisplayDevice()->setBrightness(uiconfig.screen_brightness);
+#endif
+
+ // Save to device
+ saveUIConfig();
+
+ LOG_INFO("Screen brightness set to %d", uiconfig.screen_brightness);
+ }
+ };
+ bannerOptions.InitialSelected = currentSelection;
+ screen->showOverlayBanner(bannerOptions);
+}
+
+void menuHandler::switchToMUIMenu()
+{
+ static const char *optionsArray[] = {"No", "Yes"};
+ BannerOverlayOptions bannerOptions;
+ bannerOptions.message = "Switch to MUI?";
+ bannerOptions.optionsArrayPtr = optionsArray;
+ bannerOptions.optionsCount = 2;
+ bannerOptions.bannerCallback = [](int selected) -> void {
+ if (selected == 1) {
+ config.display.displaymode = meshtastic_Config_DisplayConfig_DisplayMode_COLOR;
+ config.bluetooth.enabled = false;
+ service->reloadConfig(SEGMENT_CONFIG);
+ rebootAtMsec = (millis() + DEFAULT_REBOOT_SECONDS * 1000);
+ }
+ };
+ screen->showOverlayBanner(bannerOptions);
+}
+
+void menuHandler::TFTColorPickerMenu(OLEDDisplay *display)
+{
+ static const char *optionsArray[] = {"Back", "Default", "Meshtastic Green", "Yellow", "Red", "Orange", "Purple", "Teal",
+ "Pink", "White"};
+ BannerOverlayOptions bannerOptions;
+ bannerOptions.message = "Select Screen Color";
+ bannerOptions.optionsArrayPtr = optionsArray;
+ bannerOptions.optionsCount = 10;
+ bannerOptions.bannerCallback = [display](int selected) -> void {
+ uint8_t TFT_MESH_r = 0;
+ uint8_t TFT_MESH_g = 0;
+ uint8_t TFT_MESH_b = 0;
+ if (selected == 1) {
+ LOG_INFO("Setting color to system default or defined variant");
+ // Given just before we set all these to zero, we will allow this to go through
+ } else if (selected == 2) {
+ LOG_INFO("Setting color to Meshtastic Green");
+ TFT_MESH_r = 103;
+ TFT_MESH_g = 234;
+ TFT_MESH_b = 148;
+ } else if (selected == 3) {
+ LOG_INFO("Setting color to Yellow");
+ TFT_MESH_r = 255;
+ TFT_MESH_g = 255;
+ TFT_MESH_b = 128;
+ } else if (selected == 4) {
+ LOG_INFO("Setting color to Red");
+ TFT_MESH_r = 255;
+ TFT_MESH_g = 64;
+ TFT_MESH_b = 64;
+ } else if (selected == 5) {
+ LOG_INFO("Setting color to Orange");
+ TFT_MESH_r = 255;
+ TFT_MESH_g = 160;
+ TFT_MESH_b = 20;
+ } else if (selected == 6) {
+ LOG_INFO("Setting color to Purple");
+ TFT_MESH_r = 204;
+ TFT_MESH_g = 153;
+ TFT_MESH_b = 255;
+ } else if (selected == 7) {
+ LOG_INFO("Setting color to Teal");
+ TFT_MESH_r = 64;
+ TFT_MESH_g = 224;
+ TFT_MESH_b = 208;
+ } else if (selected == 8) {
+ LOG_INFO("Setting color to Pink");
+ TFT_MESH_r = 255;
+ TFT_MESH_g = 105;
+ TFT_MESH_b = 180;
+ } else if (selected == 9) {
+ LOG_INFO("Setting color to White");
+ TFT_MESH_r = 255;
+ TFT_MESH_g = 255;
+ TFT_MESH_b = 255;
+ } else {
+ menuQueue = system_base_menu;
+ screen->runNow();
+ }
+
+#if defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190) || HAS_TFT
+ if (selected != 0) {
+ display->setColor(BLACK);
+ display->fillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
+ display->setColor(WHITE);
+
+ if (TFT_MESH_r == 0 && TFT_MESH_g == 0 && TFT_MESH_b == 0) {
+#ifdef TFT_MESH_OVERRIDE
+ TFT_MESH = TFT_MESH_OVERRIDE;
+#else
+ TFT_MESH = COLOR565(0x67, 0xEA, 0x94);
+#endif
+ } else {
+ TFT_MESH = COLOR565(TFT_MESH_r, TFT_MESH_g, TFT_MESH_b);
+ }
+
+#if defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190)
+ static_cast(screen->getDisplayDevice())->setRGB(TFT_MESH);
+#endif
+
+ screen->setFrames(graphics::Screen::FOCUS_SYSTEM);
+ if (TFT_MESH_r == 0 && TFT_MESH_g == 0 && TFT_MESH_b == 0) {
+ uiconfig.screen_rgb_color = 0;
+ } else {
+ uiconfig.screen_rgb_color = (TFT_MESH_r << 16) | (TFT_MESH_g << 8) | TFT_MESH_b;
+ }
+ LOG_INFO("Storing Value of %d to uiconfig.screen_rgb_color", uiconfig.screen_rgb_color);
+ saveUIConfig();
+ }
+#endif
+ };
+ screen->showOverlayBanner(bannerOptions);
+}
+
+void menuHandler::rebootMenu()
+{
+ static const char *optionsArray[] = {"Back", "Confirm"};
+ BannerOverlayOptions bannerOptions;
+ bannerOptions.message = "Reboot Device?";
+ bannerOptions.optionsArrayPtr = optionsArray;
+ bannerOptions.optionsCount = 2;
+ bannerOptions.bannerCallback = [](int selected) -> void {
+ if (selected == 1) {
+ IF_SCREEN(screen->showSimpleBanner("Rebooting...", 0));
+ nodeDB->saveToDisk();
+ rebootAtMsec = millis() + DEFAULT_REBOOT_SECONDS * 1000;
+ } else {
+ menuQueue = power_menu;
+ screen->runNow();
+ }
+ };
+ screen->showOverlayBanner(bannerOptions);
+}
+
+void menuHandler::shutdownMenu()
+{
+ static const char *optionsArray[] = {"Back", "Confirm"};
+ BannerOverlayOptions bannerOptions;
+ bannerOptions.message = "Shutdown Device?";
+ bannerOptions.optionsArrayPtr = optionsArray;
+ bannerOptions.optionsCount = 2;
+ bannerOptions.bannerCallback = [](int selected) -> void {
+ if (selected == 1) {
+ IF_SCREEN(screen->showSimpleBanner("Shutting Down...", 0));
+ nodeDB->saveToDisk();
+ power->shutdown();
+ } else {
+ menuQueue = power_menu;
+ screen->runNow();
+ }
+ };
+ screen->showOverlayBanner(bannerOptions);
+}
+
+void menuHandler::addFavoriteMenu()
+{
+ screen->showNodePicker("Node To Favorite", 30000, [](uint32_t nodenum) -> void {
+ LOG_WARN("Nodenum: %u", nodenum);
+ nodeDB->set_favorite(true, nodenum);
+ screen->setFrames(graphics::Screen::FOCUS_PRESERVE);
+ });
+}
+
+void menuHandler::removeFavoriteMenu()
+{
+
+ static const char *optionsArray[] = {"Back", "Yes"};
+ BannerOverlayOptions bannerOptions;
+ std::string message = "Unfavorite This Node?\n";
+ auto node = nodeDB->getMeshNode(graphics::UIRenderer::currentFavoriteNodeNum);
+ if (node && node->has_user) {
+ message += sanitizeString(node->user.long_name).substr(0, 15);
+ }
+ bannerOptions.message = message.c_str();
+ bannerOptions.optionsArrayPtr = optionsArray;
+ bannerOptions.optionsCount = 2;
+ bannerOptions.bannerCallback = [](int selected) -> void {
+ if (selected == 1) {
+ nodeDB->set_favorite(false, graphics::UIRenderer::currentFavoriteNodeNum);
+ screen->setFrames(graphics::Screen::FOCUS_PRESERVE);
+ }
+ };
+ screen->showOverlayBanner(bannerOptions);
+}
+
+void menuHandler::testMenu()
+{
+
+ static const char *optionsArray[] = {"Back", "Number Picker"};
+ BannerOverlayOptions bannerOptions;
+ std::string message = "Test to Run?\n";
+ bannerOptions.message = message.c_str();
+ bannerOptions.optionsArrayPtr = optionsArray;
+ bannerOptions.optionsCount = 2;
+ bannerOptions.bannerCallback = [](int selected) -> void {
+ if (selected == 1) {
+ menuQueue = number_test;
+ screen->runNow();
+ }
+ };
+ screen->showOverlayBanner(bannerOptions);
+}
+
+void menuHandler::numberTest()
+{
+ screen->showNumberPicker("Pick a number\n ", 30000, 4,
+ [](int number_picked) -> void { LOG_WARN("Nodenum: %u", number_picked); });
+}
+
+void menuHandler::wifiBaseMenu()
+{
+ enum optionsNumbers { Back, Wifi_toggle };
+
+ static const char *optionsArray[] = {"Back", "WiFi Toggle"};
+ BannerOverlayOptions bannerOptions;
+ bannerOptions.message = "WiFi Menu";
+ bannerOptions.optionsArrayPtr = optionsArray;
+ bannerOptions.optionsCount = 2;
+ bannerOptions.bannerCallback = [](int selected) -> void {
+ if (selected == Wifi_toggle) {
+ menuQueue = wifi_toggle_menu;
+ screen->runNow();
+ }
+ };
+ screen->showOverlayBanner(bannerOptions);
+}
+
+void menuHandler::wifiToggleMenu()
+{
+ enum optionsNumbers { Back, Wifi_toggle };
+
+ static const char *optionsArray[] = {"Back", "Disable"};
+ BannerOverlayOptions bannerOptions;
+ bannerOptions.message = "Disable Wifi and\nEnable Bluetooth?";
+ bannerOptions.optionsArrayPtr = optionsArray;
+ bannerOptions.optionsCount = 2;
+ bannerOptions.bannerCallback = [](int selected) -> void {
+ if (selected == Wifi_toggle) {
+ config.network.wifi_enabled = false;
+ config.bluetooth.enabled = true;
+ service->reloadConfig(SEGMENT_CONFIG);
+ rebootAtMsec = (millis() + DEFAULT_REBOOT_SECONDS * 1000);
+ }
+ };
+ screen->showOverlayBanner(bannerOptions);
+}
+
+void menuHandler::notificationsMenu()
+{
+ enum optionsNumbers { Back, BuzzerActions };
+ static const char *optionsArray[] = {"Back", "Buzzer Actions"};
+ static int optionsEnumArray[] = {Back, BuzzerActions};
+ int options = 2;
+
+ BannerOverlayOptions bannerOptions;
+ bannerOptions.message = "Notifications";
+ bannerOptions.optionsArrayPtr = optionsArray;
+ bannerOptions.optionsCount = options;
+ bannerOptions.optionsEnumPtr = optionsEnumArray;
+ bannerOptions.bannerCallback = [](int selected) -> void {
+ if (selected == BuzzerActions) {
+ menuHandler::menuQueue = menuHandler::buzzermodemenupicker;
+ screen->runNow();
+ } else {
+ menuQueue = system_base_menu;
+ screen->runNow();
+ }
+ };
+ screen->showOverlayBanner(bannerOptions);
+}
+
+void menuHandler::screenOptionsMenu()
+{
+ // Check if brightness is supported
+ bool hasSupportBrightness = false;
+#if defined(ST7789_CS) || defined(USE_OLED) || defined(USE_SSD1306) || defined(USE_SH1106) || defined(USE_SH1107) || HAS_TFT
+ hasSupportBrightness = true;
+#endif
+
+ enum optionsNumbers { Back, Brightness, ScreenColor };
+ static const char *optionsArray[4] = {"Back"};
+ static int optionsEnumArray[4] = {Back};
+ int options = 1;
+
+ // Only show brightness for B&W displays
+ if (hasSupportBrightness && !HAS_TFT) {
+ optionsArray[options] = "Brightness";
+ optionsEnumArray[options++] = Brightness;
+ }
+
+ // Only show screen color for TFT displays
+#if defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190) || HAS_TFT
+ optionsArray[options] = "Screen Color";
+ optionsEnumArray[options++] = ScreenColor;
+#endif
+
+ BannerOverlayOptions bannerOptions;
+ bannerOptions.message = "Screen Options";
+ bannerOptions.optionsArrayPtr = optionsArray;
+ bannerOptions.optionsCount = options;
+ bannerOptions.optionsEnumPtr = optionsEnumArray;
+ bannerOptions.bannerCallback = [](int selected) -> void {
+ if (selected == Brightness) {
+ menuHandler::menuQueue = menuHandler::brightness_picker;
+ screen->runNow();
+ } else if (selected == ScreenColor) {
+ menuHandler::menuQueue = menuHandler::tftcolormenupicker;
+ screen->runNow();
+ } else {
+ menuQueue = system_base_menu;
+ screen->runNow();
+ }
+ };
+ screen->showOverlayBanner(bannerOptions);
+}
+
+void menuHandler::powerMenu()
+{
+
+ enum optionsNumbers { Back, Reboot, Shutdown, MUI };
+ static const char *optionsArray[4] = {"Back"};
+ static int optionsEnumArray[4] = {Back};
+ int options = 1;
+
+ optionsArray[options] = "Reboot";
+ optionsEnumArray[options++] = Reboot;
+
+ optionsArray[options] = "Shutdown";
+ optionsEnumArray[options++] = Shutdown;
+
+#if HAS_TFT
+ optionsArray[options] = "Switch to MUI";
+ optionsEnumArray[options++] = MUI;
+#endif
+
+ BannerOverlayOptions bannerOptions;
+ bannerOptions.message = "Reboot / Shutdown";
+ bannerOptions.optionsArrayPtr = optionsArray;
+ bannerOptions.optionsCount = options;
+ bannerOptions.optionsEnumPtr = optionsEnumArray;
+ bannerOptions.bannerCallback = [](int selected) -> void {
+ if (selected == Reboot) {
+ menuHandler::menuQueue = menuHandler::reboot_menu;
+ screen->runNow();
+ } else if (selected == Shutdown) {
+ menuHandler::menuQueue = menuHandler::shutdown_menu;
+ screen->runNow();
+ } else if (selected == MUI) {
+ menuHandler::menuQueue = menuHandler::mui_picker;
+ screen->runNow();
+ } else {
+ menuQueue = system_base_menu;
+ screen->runNow();
+ }
+ };
+ screen->showOverlayBanner(bannerOptions);
+}
+
+void menuHandler::keyVerificationInitMenu()
+{
+ screen->showNodePicker("Node to Verify", 30000,
+ [](uint32_t selected) -> void { keyVerificationModule->sendInitialRequest(selected); });
+}
+
+void menuHandler::keyVerificationFinalPrompt()
+{
+ char message[40] = {0};
+ memset(message, 0, sizeof(message));
+ sprintf(message, "Verification: \n");
+ keyVerificationModule->generateVerificationCode(message + 15); // send the toPhone packet
+
+ if (screen) {
+ static const char *optionsArray[] = {"Reject", "Accept"};
+ graphics::BannerOverlayOptions options;
+ options.message = message;
+ options.durationMs = 30000;
+ options.optionsArrayPtr = optionsArray;
+ options.optionsCount = 2;
+ options.notificationType = graphics::notificationTypeEnum::selection_picker;
+ options.bannerCallback = [=](int selected) {
+ if (selected == 1) {
+ auto remoteNodePtr = nodeDB->getMeshNode(keyVerificationModule->getCurrentRemoteNode());
+ remoteNodePtr->bitfield |= NODEINFO_BITFIELD_IS_KEY_MANUALLY_VERIFIED_MASK;
+ }
+ };
+ screen->showOverlayBanner(options);
+ }
+}
+
+void menuHandler::handleMenuSwitch(OLEDDisplay *display)
+{
+ if (menuQueue != menu_none)
+ test_count = 0;
+ switch (menuQueue) {
+ case menu_none:
+ break;
+ case lora_picker:
+ LoraRegionPicker();
+ break;
+ case TZ_picker:
+ TZPicker();
+ break;
+ case twelve_hour_picker:
+ TwelveHourPicker();
+ break;
+ case clock_face_picker:
+ ClockFacePicker();
+ break;
+ case clock_menu:
+ clockMenu();
+ break;
+ case system_base_menu:
+ systemBaseMenu();
+ break;
+ case position_base_menu:
+ positionBaseMenu();
+ break;
+#if !MESHTASTIC_EXCLUDE_GPS
+ case gps_toggle_menu:
+ GPSToggleMenu();
+ break;
+#endif
+ case compass_point_north_menu:
+ compassNorthMenu();
+ break;
+ case reset_node_db_menu:
+ resetNodeDBMenu();
+ break;
+ case buzzermodemenupicker:
+ BuzzerModeMenu();
+ break;
+ case mui_picker:
+ switchToMUIMenu();
+ break;
+ case tftcolormenupicker:
+ TFTColorPickerMenu(display);
+ break;
+ case brightness_picker:
+ BrightnessPickerMenu();
+ break;
+ case reboot_menu:
+ rebootMenu();
+ break;
+ case shutdown_menu:
+ shutdownMenu();
+ break;
+ case add_favorite:
+ addFavoriteMenu();
+ break;
+ case remove_favorite:
+ removeFavoriteMenu();
+ break;
+ case test_menu:
+ testMenu();
+ break;
+ case number_test:
+ numberTest();
+ break;
+ case wifi_toggle_menu:
+ wifiToggleMenu();
+ break;
+ case key_verification_init:
+ keyVerificationInitMenu();
+ break;
+ case key_verification_final_prompt:
+ keyVerificationFinalPrompt();
+ break;
+ case bluetooth_toggle_menu:
+ BluetoothToggleMenu();
+ break;
+ case notifications_menu:
+ notificationsMenu();
+ break;
+ case screen_options_menu:
+ screenOptionsMenu();
+ break;
+ case power_menu:
+ powerMenu();
+ break;
+ case throttle_message:
+ screen->showSimpleBanner("Too Many Attempts\nTry again in 60 seconds.", 5000);
+ break;
+ }
+ menuQueue = menu_none;
+}
+
+void menuHandler::saveUIConfig()
+{
+ nodeDB->saveProto("/prefs/uiconfig.proto", meshtastic_DeviceUIConfig_size, &meshtastic_DeviceUIConfig_msg, &uiconfig);
+}
+
+} // namespace graphics
+
+#endif
\ No newline at end of file
diff --git a/src/graphics/draw/MenuHandler.h b/src/graphics/draw/MenuHandler.h
new file mode 100644
index 000000000..2273dbbed
--- /dev/null
+++ b/src/graphics/draw/MenuHandler.h
@@ -0,0 +1,82 @@
+#pragma once
+#if HAS_SCREEN
+#include "configuration.h"
+namespace graphics
+{
+
+class menuHandler
+{
+ public:
+ enum screenMenus {
+ menu_none,
+ lora_picker,
+ TZ_picker,
+ twelve_hour_picker,
+ clock_face_picker,
+ clock_menu,
+ position_base_menu,
+ gps_toggle_menu,
+ compass_point_north_menu,
+ reset_node_db_menu,
+ buzzermodemenupicker,
+ mui_picker,
+ tftcolormenupicker,
+ brightness_picker,
+ reboot_menu,
+ shutdown_menu,
+ add_favorite,
+ remove_favorite,
+ test_menu,
+ number_test,
+ wifi_toggle_menu,
+ bluetooth_toggle_menu,
+ notifications_menu,
+ screen_options_menu,
+ power_menu,
+ system_base_menu,
+ key_verification_init,
+ key_verification_final_prompt,
+ throttle_message
+ };
+ static screenMenus menuQueue;
+
+ static void LoraRegionPicker(uint32_t duration = 30000);
+ static void handleMenuSwitch(OLEDDisplay *display);
+ static void clockMenu();
+ static void TZPicker();
+ static void TwelveHourPicker();
+ static void ClockFacePicker();
+ static void messageResponseMenu();
+ static void homeBaseMenu();
+ static void systemBaseMenu();
+ static void favoriteBaseMenu();
+ static void positionBaseMenu();
+ static void compassNorthMenu();
+ static void GPSToggleMenu();
+ static void BuzzerModeMenu();
+ static void switchToMUIMenu();
+ static void TFTColorPickerMenu(OLEDDisplay *display);
+ static void nodeListMenu();
+ static void resetNodeDBMenu();
+ static void BrightnessPickerMenu();
+ static void rebootMenu();
+ static void shutdownMenu();
+ static void addFavoriteMenu();
+ static void removeFavoriteMenu();
+ static void testMenu();
+ static void numberTest();
+ static void wifiBaseMenu();
+ static void wifiToggleMenu();
+ static void notificationsMenu();
+ static void screenOptionsMenu();
+ static void powerMenu();
+
+ private:
+ static void saveUIConfig();
+ static void keyVerificationInitMenu();
+ static void keyVerificationFinalPrompt();
+ static void BluetoothToggleMenu();
+};
+
+} // namespace graphics
+#endif
\ No newline at end of file
diff --git a/src/graphics/draw/MessageRenderer.cpp b/src/graphics/draw/MessageRenderer.cpp
new file mode 100644
index 000000000..3df8a003c
--- /dev/null
+++ b/src/graphics/draw/MessageRenderer.cpp
@@ -0,0 +1,447 @@
+/*
+BaseUI
+
+Developed and Maintained By:
+- Ronald Garcia (HarukiToreda) โ Lead development and implementation.
+- JasonP (Xaositek) โ Screen layout and icon design, UI improvements and testing.
+- TonyG (Tropho) โ Project management, structural planning, and testing
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+
+*/
+
+#include "configuration.h"
+#if HAS_SCREEN
+#include "MessageRenderer.h"
+
+// Core includes
+#include "NodeDB.h"
+#include "configuration.h"
+#include "gps/RTC.h"
+#include "graphics/ScreenFonts.h"
+#include "graphics/SharedUIDisplay.h"
+#include "graphics/emotes.h"
+#include "main.h"
+#include "meshUtils.h"
+
+// Additional includes for UI rendering
+#include "UIRenderer.h"
+#include "graphics/TimeFormatters.h"
+
+// Additional includes for dependencies
+#include
+#include
+
+// External declarations
+extern bool hasUnreadMessage;
+extern meshtastic_DeviceState devicestate;
+
+using graphics::Emote;
+using graphics::emotes;
+using graphics::numEmotes;
+
+namespace graphics
+{
+namespace MessageRenderer
+{
+
+// Simple cache based on text hash
+static size_t cachedKey = 0;
+static std::vector cachedLines;
+static std::vector cachedHeights;
+
+void drawStringWithEmotes(OLEDDisplay *display, int x, int y, const std::string &line, const Emote *emotes, int emoteCount)
+{
+ int cursorX = x;
+ const int fontHeight = FONT_HEIGHT_SMALL;
+
+ // === Step 1: Find tallest emote in the line ===
+ int maxIconHeight = fontHeight;
+ for (size_t i = 0; i < line.length();) {
+ bool matched = false;
+ for (int e = 0; e < emoteCount; ++e) {
+ size_t emojiLen = strlen(emotes[e].label);
+ if (line.compare(i, emojiLen, emotes[e].label) == 0) {
+ if (emotes[e].height > maxIconHeight)
+ maxIconHeight = emotes[e].height;
+ i += emojiLen;
+ matched = true;
+ break;
+ }
+ }
+ if (!matched) {
+ uint8_t c = static_cast(line[i]);
+ if ((c & 0xE0) == 0xC0)
+ i += 2;
+ else if ((c & 0xF0) == 0xE0)
+ i += 3;
+ else if ((c & 0xF8) == 0xF0)
+ i += 4;
+ else
+ i += 1;
+ }
+ }
+
+ // === Step 2: Baseline alignment ===
+ int lineHeight = std::max(fontHeight, maxIconHeight);
+ int baselineOffset = (lineHeight - fontHeight) / 2;
+ int fontY = y + baselineOffset;
+ int fontMidline = fontY + fontHeight / 2;
+
+ // === Step 3: Render line in segments ===
+ size_t i = 0;
+ bool inBold = false;
+
+ while (i < line.length()) {
+ // Check for ** start/end for faux bold
+ if (line.compare(i, 2, "**") == 0) {
+ inBold = !inBold;
+ i += 2;
+ continue;
+ }
+
+ // Look ahead for the next emote match
+ size_t nextEmotePos = std::string::npos;
+ const Emote *matchedEmote = nullptr;
+ size_t emojiLen = 0;
+
+ for (int e = 0; e < emoteCount; ++e) {
+ size_t pos = line.find(emotes[e].label, i);
+ if (pos != std::string::npos && (nextEmotePos == std::string::npos || pos < nextEmotePos)) {
+ nextEmotePos = pos;
+ matchedEmote = &emotes[e];
+ emojiLen = strlen(emotes[e].label);
+ }
+ }
+
+ // Render normal text segment up to the emote or bold toggle
+ size_t nextControl = std::min(nextEmotePos, line.find("**", i));
+ if (nextControl == std::string::npos)
+ nextControl = line.length();
+
+ if (nextControl > i) {
+ std::string textChunk = line.substr(i, nextControl - i);
+ if (inBold) {
+ // Faux bold: draw twice, offset by 1px
+ display->drawString(cursorX + 1, fontY, textChunk.c_str());
+ }
+ display->drawString(cursorX, fontY, textChunk.c_str());
+ cursorX += display->getStringWidth(textChunk.c_str());
+ i = nextControl;
+ continue;
+ }
+
+ // Render the emote (if found)
+ if (matchedEmote && i == nextEmotePos) {
+ int iconY = fontMidline - matchedEmote->height / 2 - 1;
+ display->drawXbm(cursorX, iconY, matchedEmote->width, matchedEmote->height, matchedEmote->bitmap);
+ cursorX += matchedEmote->width + 1;
+ i += emojiLen;
+ } else {
+ // No more emotes โ render the rest of the line
+ std::string remaining = line.substr(i);
+ if (inBold) {
+ display->drawString(cursorX + 1, fontY, remaining.c_str());
+ }
+ display->drawString(cursorX, fontY, remaining.c_str());
+ cursorX += display->getStringWidth(remaining.c_str());
+ break;
+ }
+ }
+}
+
+void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
+{
+ // Clear the unread message indicator when viewing the message
+ hasUnreadMessage = false;
+
+ const meshtastic_MeshPacket &mp = devicestate.rx_text_message;
+ const char *msg = reinterpret_cast(mp.decoded.payload.bytes);
+
+ display->clear();
+ display->setTextAlignment(TEXT_ALIGN_LEFT);
+ display->setFont(FONT_SMALL);
+
+ const int navHeight = FONT_HEIGHT_SMALL;
+ const int scrollBottom = SCREEN_HEIGHT - navHeight;
+ const int usableHeight = scrollBottom;
+ const int textWidth = SCREEN_WIDTH;
+
+ bool isInverted = (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_INVERTED);
+ bool isBold = config.display.heading_bold;
+
+ // === Set Title
+ const char *titleStr = "Messages";
+
+ // Check if we have more than an empty message to show
+ char messageBuf[237];
+ snprintf(messageBuf, sizeof(messageBuf), "%s", msg);
+ if (strlen(messageBuf) == 0) {
+ // === Header ===
+ graphics::drawCommonHeader(display, x, y, titleStr);
+ const char *messageString = "No messages";
+ int center_text = (SCREEN_WIDTH / 2) - (display->getStringWidth(messageString) / 2);
+ display->drawString(center_text, getTextPositions(display)[2], messageString);
+ return;
+ }
+
+ // === Header Construction ===
+ meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(getFrom(&mp));
+ char headerStr[80];
+ const char *sender = "???";
+ if (node && node->has_user) {
+ if (SCREEN_WIDTH >= 200 && strlen(node->user.long_name) > 0) {
+ sender = node->user.long_name;
+ } else {
+ sender = node->user.short_name;
+ }
+ }
+ uint32_t seconds = sinceReceived(&mp), minutes = seconds / 60, hours = minutes / 60, days = hours / 24;
+ uint8_t timestampHours, timestampMinutes;
+ int32_t daysAgo;
+ bool useTimestamp = deltaToTimestamp(seconds, ×tampHours, ×tampMinutes, &daysAgo);
+
+ if (useTimestamp && minutes >= 15 && daysAgo == 0) {
+ std::string prefix = (daysAgo == 1 && SCREEN_WIDTH >= 200) ? "Yesterday" : "At";
+ if (config.display.use_12h_clock) {
+ bool isPM = timestampHours >= 12;
+ timestampHours = timestampHours % 12;
+ if (timestampHours == 0)
+ timestampHours = 12;
+ snprintf(headerStr, sizeof(headerStr), "%s %d:%02d%s from %s", prefix.c_str(), timestampHours, timestampMinutes,
+ isPM ? "p" : "a", sender);
+ } else {
+ snprintf(headerStr, sizeof(headerStr), "%s %d:%02d from %s", prefix.c_str(), timestampHours, timestampMinutes,
+ sender);
+ }
+ } else {
+ snprintf(headerStr, sizeof(headerStr), "%s ago from %s", UIRenderer::drawTimeDelta(days, hours, minutes, seconds).c_str(),
+ sender);
+ }
+
+ uint32_t now = millis();
+#ifndef EXCLUDE_EMOJI
+ // === Bounce animation setup ===
+ static uint32_t lastBounceTime = 0;
+ static int bounceY = 0;
+ const int bounceRange = 2; // Max pixels to bounce up/down
+ const int bounceInterval = 10; // How quickly to change bounce direction (ms)
+
+ if (now - lastBounceTime >= bounceInterval) {
+ lastBounceTime = now;
+ bounceY = (bounceY + 1) % (bounceRange * 2);
+ }
+ for (int i = 0; i < numEmotes; ++i) {
+ const Emote &e = emotes[i];
+ if (strcmp(msg, e.label) == 0) {
+ int headerY = getTextPositions(display)[1]; // same as scrolling header line
+ display->drawString(x + 3, headerY, headerStr);
+ if (isInverted && isBold)
+ display->drawString(x + 4, headerY, headerStr);
+
+ // Draw separator (same as scroll version)
+ for (int separatorX = 1; separatorX <= (display->getStringWidth(headerStr) + 2); separatorX += 2) {
+ display->setPixel(separatorX, headerY + ((isHighResolution) ? 19 : 13));
+ }
+
+ // Center the emote below the header line + separator + nav
+ int remainingHeight = SCREEN_HEIGHT - (headerY + FONT_HEIGHT_SMALL) - navHeight;
+ int emoteY = headerY + 6 + FONT_HEIGHT_SMALL + (remainingHeight - e.height) / 2 + bounceY - bounceRange;
+ display->drawXbm((SCREEN_WIDTH - e.width) / 2, emoteY, e.width, e.height, e.bitmap);
+
+ // Draw header at the end to sort out overlapping elements
+ graphics::drawCommonHeader(display, x, y, titleStr);
+ return;
+ }
+ }
+#endif
+ // === Generate the cache key ===
+ size_t currentKey = (size_t)mp.from;
+ currentKey ^= ((size_t)mp.to << 8);
+ currentKey ^= ((size_t)mp.rx_time << 16);
+ currentKey ^= ((size_t)mp.id << 24);
+
+ if (cachedKey != currentKey) {
+ LOG_INFO("Message cache key is misssed cachedKey=0x%0x, currentKey=0x%x", cachedKey, currentKey);
+
+ // Cache miss - regenerate lines and heights
+ cachedLines = generateLines(display, headerStr, messageBuf, textWidth);
+ cachedHeights = calculateLineHeights(cachedLines, emotes);
+ cachedKey = currentKey;
+ } else {
+ // Cache hit but update the header line with current time information
+ cachedLines[0] = std::string(headerStr);
+ // The header always has a fixed height since it doesn't contain emotes
+ // As per calculateLineHeights logic for lines without emotes:
+ cachedHeights[0] = FONT_HEIGHT_SMALL - 2;
+ if (cachedHeights[0] < 8)
+ cachedHeights[0] = 8; // minimum safety
+ }
+
+ // === Scrolling logic ===
+ int totalHeight = 0;
+ for (size_t i = 1; i < cachedHeights.size(); ++i) {
+ totalHeight += cachedHeights[i];
+ }
+ int usableScrollHeight = usableHeight - cachedHeights[0]; // remove header height
+ int scrollStop = std::max(0, totalHeight - usableScrollHeight + cachedHeights.back());
+
+ static float scrollY = 0.0f;
+ static uint32_t lastTime = 0, scrollStartDelay = 0, pauseStart = 0;
+ static bool waitingToReset = false, scrollStarted = false;
+
+ // === Smooth scrolling adjustment ===
+ // You can tweak this divisor to change how smooth it scrolls.
+ // Lower = smoother, but can feel slow.
+ float delta = (now - lastTime) / 400.0f;
+ lastTime = now;
+
+ const float scrollSpeed = 2.0f; // pixels per second
+
+ // Delay scrolling start by 2 seconds
+ if (scrollStartDelay == 0)
+ scrollStartDelay = now;
+ if (!scrollStarted && now - scrollStartDelay > 2000)
+ scrollStarted = true;
+
+ if (totalHeight > usableScrollHeight) {
+ if (scrollStarted) {
+ if (!waitingToReset) {
+ scrollY += delta * scrollSpeed;
+ if (scrollY >= scrollStop) {
+ scrollY = scrollStop;
+ waitingToReset = true;
+ pauseStart = lastTime;
+ }
+ } else if (lastTime - pauseStart > 3000) {
+ scrollY = 0;
+ waitingToReset = false;
+ scrollStarted = false;
+ scrollStartDelay = lastTime;
+ }
+ }
+ } else {
+ scrollY = 0;
+ }
+
+ int scrollOffset = static_cast(scrollY);
+ int yOffset = -scrollOffset + getTextPositions(display)[1];
+ for (int separatorX = 1; separatorX <= (display->getStringWidth(headerStr) + 2); separatorX += 2) {
+ display->setPixel(separatorX, yOffset + ((isHighResolution) ? 19 : 13));
+ }
+
+ // === Render visible lines ===
+ renderMessageContent(display, cachedLines, cachedHeights, x, yOffset, scrollBottom, emotes, numEmotes, isInverted, isBold);
+
+ // Draw header at the end to sort out overlapping elements
+ graphics::drawCommonHeader(display, x, y, titleStr);
+}
+
+std::vector generateLines(OLEDDisplay *display, const char *headerStr, const char *messageBuf, int textWidth)
+{
+ std::vector lines;
+ lines.push_back(std::string(headerStr)); // Header line is always first
+
+ std::string line, word;
+ for (int i = 0; messageBuf[i]; ++i) {
+ char ch = messageBuf[i];
+ if ((unsigned char)messageBuf[i] == 0xE2 && (unsigned char)messageBuf[i + 1] == 0x80 &&
+ (unsigned char)messageBuf[i + 2] == 0x99) {
+ ch = '\''; // plain apostrophe
+ i += 2; // skip over the extra UTF-8 bytes
+ }
+ if (ch == '\n') {
+ if (!word.empty())
+ line += word;
+ if (!line.empty())
+ lines.push_back(line);
+ line.clear();
+ word.clear();
+ } else if (ch == ' ') {
+ line += word + ' ';
+ word.clear();
+ } else {
+ word += ch;
+ std::string test = line + word;
+ // Keep these lines for diagnostics
+ // LOG_INFO("Char: '%c' (0x%02X)", ch, (unsigned char)ch);
+ // LOG_INFO("Current String: %s", test.c_str());
+ if (display->getStringWidth(test.c_str()) > textWidth) {
+ if (!line.empty())
+ lines.push_back(line);
+ line = word;
+ word.clear();
+ }
+ }
+ }
+
+ if (!word.empty())
+ line += word;
+ if (!line.empty())
+ lines.push_back(line);
+
+ return lines;
+}
+
+std::vector calculateLineHeights(const std::vector &lines, const Emote *emotes)
+{
+ std::vector rowHeights;
+
+ for (const auto &_line : lines) {
+ int lineHeight = FONT_HEIGHT_SMALL;
+ bool hasEmote = false;
+
+ for (int i = 0; i < numEmotes; ++i) {
+ const Emote &e = emotes[i];
+ if (_line.find(e.label) != std::string::npos) {
+ lineHeight = std::max(lineHeight, e.height);
+ hasEmote = true;
+ }
+ }
+
+ // Apply tighter spacing if no emotes on this line
+ if (!hasEmote) {
+ lineHeight -= 2; // reduce by 2px for tighter spacing
+ if (lineHeight < 8)
+ lineHeight = 8; // minimum safety
+ }
+
+ rowHeights.push_back(lineHeight);
+ }
+
+ return rowHeights;
+}
+
+void renderMessageContent(OLEDDisplay *display, const std::vector &lines, const std::vector &rowHeights, int x,
+ int yOffset, int scrollBottom, const Emote *emotes, int numEmotes, bool isInverted, bool isBold)
+{
+ for (size_t i = 0; i < lines.size(); ++i) {
+ int lineY = yOffset;
+ for (size_t j = 0; j < i; ++j)
+ lineY += rowHeights[j];
+ if (lineY > -rowHeights[i] && lineY < scrollBottom) {
+ if (i == 0 && isInverted) {
+ display->drawString(x, lineY, lines[i].c_str());
+ if (isBold)
+ display->drawString(x, lineY, lines[i].c_str());
+ } else {
+ drawStringWithEmotes(display, x, lineY, lines[i], emotes, numEmotes);
+ }
+ }
+ }
+}
+
+} // namespace MessageRenderer
+} // namespace graphics
+#endif
\ No newline at end of file
diff --git a/src/graphics/draw/MessageRenderer.h b/src/graphics/draw/MessageRenderer.h
new file mode 100644
index 000000000..c15a699f7
--- /dev/null
+++ b/src/graphics/draw/MessageRenderer.h
@@ -0,0 +1,30 @@
+#pragma once
+#include "OLEDDisplay.h"
+#include "OLEDDisplayUi.h"
+#include "graphics/emotes.h"
+#include
+#include
+
+namespace graphics
+{
+namespace MessageRenderer
+{
+
+// Text and emote rendering
+void drawStringWithEmotes(OLEDDisplay *display, int x, int y, const std::string &line, const Emote *emotes, int emoteCount);
+
+/// Draws the text message frame for displaying received messages
+void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
+
+// Function to generate lines with word wrapping
+std::vector generateLines(OLEDDisplay *display, const char *headerStr, const char *messageBuf, int textWidth);
+
+// Function to calculate heights for each line
+std::vector calculateLineHeights(const std::vector &lines, const Emote *emotes);
+
+// Function to render the message content
+void renderMessageContent(OLEDDisplay *display, const std::vector &lines, const std::vector &rowHeights, int x,
+ int yOffset, int scrollBottom, const Emote *emotes, int numEmotes, bool isInverted, bool isBold);
+
+} // namespace MessageRenderer
+} // namespace graphics
diff --git a/src/graphics/draw/NodeListRenderer.cpp b/src/graphics/draw/NodeListRenderer.cpp
new file mode 100644
index 000000000..d8746fb69
--- /dev/null
+++ b/src/graphics/draw/NodeListRenderer.cpp
@@ -0,0 +1,567 @@
+#include "configuration.h"
+#if HAS_SCREEN
+#include "CompassRenderer.h"
+#include "NodeDB.h"
+#include "NodeListRenderer.h"
+#include "UIRenderer.h"
+#include "gps/GeoCoord.h"
+#include "gps/RTC.h" // for getTime() function
+#include "graphics/ScreenFonts.h"
+#include "graphics/SharedUIDisplay.h"
+#include "graphics/images.h"
+#include "meshUtils.h"
+#include
+
+// Forward declarations for functions defined in Screen.cpp
+namespace graphics
+{
+extern bool haveGlyphs(const char *str);
+} // namespace graphics
+
+// Global screen instance
+extern graphics::Screen *screen;
+
+namespace graphics
+{
+namespace NodeListRenderer
+{
+
+// Function moved from Screen.cpp to NodeListRenderer.cpp since it's primarily used here
+void drawScaledXBitmap16x16(int x, int y, int width, int height, const uint8_t *bitmapXBM, OLEDDisplay *display)
+{
+ for (int row = 0; row < height; row++) {
+ uint8_t rowMask = (1 << row);
+ for (int col = 0; col < width; col++) {
+ uint8_t colData = pgm_read_byte(&bitmapXBM[col]);
+ if (colData & rowMask) {
+ // Note: rows become X, columns become Y after transpose
+ display->fillRect(x + row * 2, y + col * 2, 2, 2);
+ }
+ }
+ }
+}
+
+// Static variables for dynamic cycling
+static NodeListMode currentMode = MODE_LAST_HEARD;
+static int scrollIndex = 0;
+
+// =============================
+// Utility Functions
+// =============================
+
+const char *getSafeNodeName(meshtastic_NodeInfoLite *node)
+{
+ static char nodeName[16] = "?";
+ if (node->has_user && strlen(node->user.short_name) > 0) {
+ bool valid = true;
+ const char *name = node->user.short_name;
+ for (size_t i = 0; i < strlen(name); i++) {
+ uint8_t c = (uint8_t)name[i];
+ if (c < 32 || c > 126) {
+ valid = false;
+ break;
+ }
+ }
+ if (valid) {
+ strncpy(nodeName, name, sizeof(nodeName) - 1);
+ nodeName[sizeof(nodeName) - 1] = '\0';
+ } else {
+ snprintf(nodeName, sizeof(nodeName), "(%04X)", (uint16_t)(node->num & 0xFFFF));
+ }
+ } else {
+ snprintf(nodeName, sizeof(nodeName), "(%04X)", (uint16_t)(node->num & 0xFFFF));
+ }
+ return nodeName;
+}
+
+const char *getCurrentModeTitle(int screenWidth)
+{
+ switch (currentMode) {
+ case MODE_LAST_HEARD:
+ return "Last Heard";
+ case MODE_HOP_SIGNAL:
+#ifdef USE_EINK
+ return "Hops/Sig";
+#else
+ return (isHighResolution) ? "Hops/Signal" : "Hops/Sig";
+#endif
+ case MODE_DISTANCE:
+ return "Distance";
+ default:
+ return "Nodes";
+ }
+}
+
+// Use dynamic timing based on mode
+unsigned long getModeCycleIntervalMs()
+{
+ return 3000;
+}
+
+int calculateMaxScroll(int totalEntries, int visibleRows)
+{
+ return std::max(0, (totalEntries - 1) / (visibleRows * 2));
+}
+
+void drawColumnSeparator(OLEDDisplay *display, int16_t x, int16_t yStart, int16_t yEnd)
+{
+ int columnWidth = display->getWidth() / 2;
+ int separatorX = x + columnWidth - 2;
+ for (int y = yStart; y <= yEnd; y += 2) {
+ display->setPixel(separatorX, y);
+ }
+}
+
+void drawScrollbar(OLEDDisplay *display, int visibleNodeRows, int totalEntries, int scrollIndex, int columns, int scrollStartY)
+{
+ if (totalEntries <= visibleNodeRows * columns)
+ return;
+
+ int scrollbarX = display->getWidth() - 2;
+ int scrollbarHeight = display->getHeight() - scrollStartY - 10;
+ int thumbHeight = std::max(4, (scrollbarHeight * visibleNodeRows * columns) / totalEntries);
+ int maxScroll = calculateMaxScroll(totalEntries, visibleNodeRows);
+ int thumbY = scrollStartY + (scrollIndex * (scrollbarHeight - thumbHeight)) / std::max(1, maxScroll);
+
+ for (int i = 0; i < thumbHeight; i++) {
+ display->setPixel(scrollbarX, thumbY + i);
+ }
+}
+
+// =============================
+// Entry Renderers
+// =============================
+
+void drawEntryLastHeard(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16_t x, int16_t y, int columnWidth)
+{
+ bool isLeftCol = (x < SCREEN_WIDTH / 2);
+ int timeOffset = (isHighResolution) ? (isLeftCol ? 7 : 10) : (isLeftCol ? 3 : 7);
+
+ const char *nodeName = getSafeNodeName(node);
+
+ char timeStr[10];
+ uint32_t seconds = sinceLastSeen(node);
+ if (seconds == 0 || seconds == UINT32_MAX) {
+ snprintf(timeStr, sizeof(timeStr), "?");
+ } else {
+ uint32_t minutes = seconds / 60, hours = minutes / 60, days = hours / 24;
+ snprintf(timeStr, sizeof(timeStr), (days > 365 ? "?" : "%d%c"),
+ (days ? days
+ : hours ? hours
+ : minutes),
+ (days ? 'd'
+ : hours ? 'h'
+ : 'm'));
+ }
+
+ display->setTextAlignment(TEXT_ALIGN_LEFT);
+ display->setFont(FONT_SMALL);
+ display->drawString(x + ((isHighResolution) ? 6 : 3), y, nodeName);
+ if (node->is_favorite) {
+ if (isHighResolution) {
+ drawScaledXBitmap16x16(x, y + 6, smallbulletpoint_width, smallbulletpoint_height, smallbulletpoint, display);
+ } else {
+ display->drawXbm(x, y + 5, smallbulletpoint_width, smallbulletpoint_height, smallbulletpoint);
+ }
+ }
+
+ int rightEdge = x + columnWidth - timeOffset;
+ if (timeStr[strlen(timeStr) - 1] == 'm') // Fix the fact that our fonts don't line up well all the time
+ rightEdge -= 1;
+ int textWidth = display->getStringWidth(timeStr);
+ display->drawString(rightEdge - textWidth, y, timeStr);
+}
+
+void drawEntryHopSignal(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16_t x, int16_t y, int columnWidth)
+{
+ bool isLeftCol = (x < SCREEN_WIDTH / 2);
+
+ int nameMaxWidth = columnWidth - 25;
+ int barsOffset = (isHighResolution) ? (isLeftCol ? 20 : 24) : (isLeftCol ? 15 : 19);
+ int hopOffset = (isHighResolution) ? (isLeftCol ? 21 : 29) : (isLeftCol ? 13 : 17);
+
+ int barsXOffset = columnWidth - barsOffset;
+
+ const char *nodeName = getSafeNodeName(node);
+
+ display->setTextAlignment(TEXT_ALIGN_LEFT);
+ display->setFont(FONT_SMALL);
+
+ display->drawStringMaxWidth(x + ((isHighResolution) ? 6 : 3), y, nameMaxWidth, nodeName);
+ if (node->is_favorite) {
+ if (isHighResolution) {
+ drawScaledXBitmap16x16(x, y + 6, smallbulletpoint_width, smallbulletpoint_height, smallbulletpoint, display);
+ } else {
+ display->drawXbm(x, y + 5, smallbulletpoint_width, smallbulletpoint_height, smallbulletpoint);
+ }
+ }
+
+ // Draw signal strength bars
+ int bars = (node->snr > 5) ? 4 : (node->snr > 0) ? 3 : (node->snr > -5) ? 2 : (node->snr > -10) ? 1 : 0;
+ int barWidth = 2;
+ int barStartX = x + barsXOffset;
+ int barStartY = y + 1 + (FONT_HEIGHT_SMALL / 2) + 2;
+
+ for (int b = 0; b < 4; b++) {
+ if (b < bars) {
+ int height = (b * 2);
+ display->fillRect(barStartX + (b * (barWidth + 1)), barStartY - height, barWidth, height);
+ }
+ }
+
+ // Draw hop count
+ char hopStr[6] = "";
+ if (node->has_hops_away && node->hops_away > 0)
+ snprintf(hopStr, sizeof(hopStr), "[%d]", node->hops_away);
+
+ if (hopStr[0] != '\0') {
+ int rightEdge = x + columnWidth - hopOffset;
+ int textWidth = display->getStringWidth(hopStr);
+ display->drawString(rightEdge - textWidth, y, hopStr);
+ }
+}
+
+void drawNodeDistance(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16_t x, int16_t y, int columnWidth)
+{
+ bool isLeftCol = (x < SCREEN_WIDTH / 2);
+ int nameMaxWidth = columnWidth - (isHighResolution ? (isLeftCol ? 25 : 28) : (isLeftCol ? 20 : 22));
+
+ const char *nodeName = getSafeNodeName(node);
+ char distStr[10] = "";
+
+ meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum());
+ if (nodeDB->hasValidPosition(ourNode) && nodeDB->hasValidPosition(node)) {
+ double lat1 = ourNode->position.latitude_i * 1e-7;
+ double lon1 = ourNode->position.longitude_i * 1e-7;
+ double lat2 = node->position.latitude_i * 1e-7;
+ double lon2 = node->position.longitude_i * 1e-7;
+
+ double earthRadiusKm = 6371.0;
+ double dLat = (lat2 - lat1) * DEG_TO_RAD;
+ double dLon = (lon2 - lon1) * DEG_TO_RAD;
+
+ double a =
+ sin(dLat / 2) * sin(dLat / 2) + cos(lat1 * DEG_TO_RAD) * cos(lat2 * DEG_TO_RAD) * sin(dLon / 2) * sin(dLon / 2);
+ double c = 2 * atan2(sqrt(a), sqrt(1 - a));
+ double distanceKm = earthRadiusKm * c;
+
+ if (config.display.units == meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL) {
+ double miles = distanceKm * 0.621371;
+ if (miles < 0.1) {
+ int feet = (int)(miles * 5280);
+ if (feet < 1000)
+ snprintf(distStr, sizeof(distStr), "%dft", feet);
+ else
+ snprintf(distStr, sizeof(distStr), "ยผmi"); // 4-char max
+ } else {
+ int roundedMiles = (int)(miles + 0.5);
+ if (roundedMiles < 1000)
+ snprintf(distStr, sizeof(distStr), "%dmi", roundedMiles);
+ else
+ snprintf(distStr, sizeof(distStr), "999"); // Max display cap
+ }
+ } else {
+ if (distanceKm < 1.0) {
+ int meters = (int)(distanceKm * 1000);
+ if (meters < 1000)
+ snprintf(distStr, sizeof(distStr), "%dm", meters);
+ else
+ snprintf(distStr, sizeof(distStr), "1k");
+ } else {
+ int km = (int)(distanceKm + 0.5);
+ if (km < 1000)
+ snprintf(distStr, sizeof(distStr), "%dk", km);
+ else
+ snprintf(distStr, sizeof(distStr), "999");
+ }
+ }
+ }
+
+ display->setTextAlignment(TEXT_ALIGN_LEFT);
+ display->setFont(FONT_SMALL);
+ display->drawStringMaxWidth(x + ((isHighResolution) ? 6 : 3), y, nameMaxWidth, nodeName);
+ if (node->is_favorite) {
+ if (isHighResolution) {
+ drawScaledXBitmap16x16(x, y + 6, smallbulletpoint_width, smallbulletpoint_height, smallbulletpoint, display);
+ } else {
+ display->drawXbm(x, y + 5, smallbulletpoint_width, smallbulletpoint_height, smallbulletpoint);
+ }
+ }
+
+ if (strlen(distStr) > 0) {
+ int offset = (isHighResolution) ? (isLeftCol ? 7 : 10) // Offset for Wide Screens (Left Column:Right Column)
+ : (isLeftCol ? 4 : 7); // Offset for Narrow Screens (Left Column:Right Column)
+ int rightEdge = x + columnWidth - offset;
+ int textWidth = display->getStringWidth(distStr);
+ display->drawString(rightEdge - textWidth, y, distStr);
+ }
+}
+
+void drawEntryDynamic(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16_t x, int16_t y, int columnWidth)
+{
+ switch (currentMode) {
+ case MODE_LAST_HEARD:
+ drawEntryLastHeard(display, node, x, y, columnWidth);
+ break;
+ case MODE_HOP_SIGNAL:
+ drawEntryHopSignal(display, node, x, y, columnWidth);
+ break;
+ case MODE_DISTANCE:
+ drawNodeDistance(display, node, x, y, columnWidth);
+ break;
+ default:
+ break;
+ }
+}
+
+void drawEntryCompass(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16_t x, int16_t y, int columnWidth)
+{
+ bool isLeftCol = (x < SCREEN_WIDTH / 2);
+
+ // Adjust max text width depending on column and screen width
+ int nameMaxWidth = columnWidth - (isHighResolution ? (isLeftCol ? 25 : 28) : (isLeftCol ? 20 : 22));
+
+ const char *nodeName = getSafeNodeName(node);
+
+ display->setTextAlignment(TEXT_ALIGN_LEFT);
+ display->setFont(FONT_SMALL);
+ display->drawStringMaxWidth(x + ((isHighResolution) ? 6 : 3), y, nameMaxWidth, nodeName);
+ if (node->is_favorite) {
+ if (isHighResolution) {
+ drawScaledXBitmap16x16(x, y + 6, smallbulletpoint_width, smallbulletpoint_height, smallbulletpoint, display);
+ } else {
+ display->drawXbm(x, y + 5, smallbulletpoint_width, smallbulletpoint_height, smallbulletpoint);
+ }
+ }
+}
+
+void drawCompassArrow(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16_t x, int16_t y, int columnWidth, float myHeading,
+ double userLat, double userLon)
+{
+ if (!nodeDB->hasValidPosition(node))
+ return;
+
+ bool isLeftCol = (x < SCREEN_WIDTH / 2);
+ int arrowXOffset = (isHighResolution) ? (isLeftCol ? 22 : 24) : (isLeftCol ? 12 : 18);
+
+ int centerX = x + columnWidth - arrowXOffset;
+ int centerY = y + FONT_HEIGHT_SMALL / 2;
+
+ double nodeLat = node->position.latitude_i * 1e-7;
+ double nodeLon = node->position.longitude_i * 1e-7;
+ float bearing = GeoCoord::bearing(userLat, userLon, nodeLat, nodeLon);
+ float bearingToNode = RAD_TO_DEG * bearing;
+ float relativeBearing = fmod((bearingToNode - myHeading + 360), 360);
+ float angle = relativeBearing * DEG_TO_RAD;
+ // Shrink size by 2px
+ int size = FONT_HEIGHT_SMALL - 5;
+ CompassRenderer::drawArrowToNode(display, centerX, centerY, size, relativeBearing);
+ /*
+ float halfSize = size / 2.0;
+
+ // Point of the arrow
+ int tipX = centerX + halfSize * cos(angle);
+ int tipY = centerY - halfSize * sin(angle);
+
+ float baseAngle = radians(35);
+ float sideLen = halfSize * 0.95;
+ float notchInset = halfSize * 0.35;
+
+ // Left and right corners
+ int leftX = centerX + sideLen * cos(angle + PI - baseAngle);
+ int leftY = centerY - sideLen * sin(angle + PI - baseAngle);
+
+ int rightX = centerX + sideLen * cos(angle + PI + baseAngle);
+ int rightY = centerY - sideLen * sin(angle + PI + baseAngle);
+
+ // Center notch (cut-in)
+ int notchX = centerX - notchInset * cos(angle);
+ int notchY = centerY + notchInset * sin(angle);
+
+ // Draw the chevron-style arrowhead
+ display->fillTriangle(tipX, tipY, leftX, leftY, notchX, notchY);
+ display->fillTriangle(tipX, tipY, notchX, notchY, rightX, rightY);
+ */
+}
+
+// =============================
+// Main Screen Functions
+// =============================
+
+void drawNodeListScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y, const char *title,
+ EntryRenderer renderer, NodeExtrasRenderer extras, float heading, double lat, double lon)
+{
+ const int COMMON_HEADER_HEIGHT = FONT_HEIGHT_SMALL - 1;
+ const int rowYOffset = FONT_HEIGHT_SMALL - 3;
+
+ int columnWidth = display->getWidth() / 2;
+
+ display->clear();
+
+ // Draw the battery/time header
+ graphics::drawCommonHeader(display, x, y, title);
+
+ // Space below header
+ y += COMMON_HEADER_HEIGHT;
+
+ int totalEntries = nodeDB->getNumMeshNodes();
+ int totalRowsAvailable = (display->getHeight() - y) / rowYOffset;
+
+ int visibleNodeRows = totalRowsAvailable;
+ int totalColumns = 2;
+
+ int startIndex = scrollIndex * visibleNodeRows * totalColumns;
+ if (nodeDB->getMeshNodeByIndex(startIndex)->num == nodeDB->getNodeNum()) {
+ startIndex++; // skip own node
+ }
+ int endIndex = std::min(startIndex + visibleNodeRows * totalColumns, totalEntries);
+
+ int yOffset = 0;
+ int col = 0;
+ int lastNodeY = y;
+ int shownCount = 0;
+ int rowCount = 0;
+
+ for (int i = startIndex; i < endIndex; ++i) {
+ int xPos = x + (col * columnWidth);
+ int yPos = y + yOffset;
+ renderer(display, nodeDB->getMeshNodeByIndex(i), xPos, yPos, columnWidth);
+
+ if (extras) {
+ extras(display, nodeDB->getMeshNodeByIndex(i), xPos, yPos, columnWidth, heading, lat, lon);
+ }
+
+ lastNodeY = std::max(lastNodeY, yPos + FONT_HEIGHT_SMALL);
+ yOffset += rowYOffset;
+ shownCount++;
+ rowCount++;
+
+ if (rowCount >= totalRowsAvailable) {
+ yOffset = 0;
+ rowCount = 0;
+ col++;
+ if (col > (totalColumns - 1))
+ break;
+ }
+ }
+
+ // Draw column separator
+ if (shownCount > 0) {
+ const int firstNodeY = y + 3;
+ drawColumnSeparator(display, x, firstNodeY, lastNodeY);
+ }
+
+ const int scrollStartY = y + 3;
+ drawScrollbar(display, visibleNodeRows, totalEntries, scrollIndex, 2, scrollStartY);
+}
+
+// =============================
+// Screen Frame Functions
+// =============================
+
+#ifndef USE_EINK
+void drawDynamicNodeListScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
+{
+ // Static variables to track mode and duration
+ static NodeListMode lastRenderedMode = MODE_COUNT;
+ static unsigned long modeStartTime = 0;
+
+ unsigned long now = millis();
+
+ // On very first call (on boot or state enter)
+ if (lastRenderedMode == MODE_COUNT) {
+ currentMode = MODE_LAST_HEARD;
+ modeStartTime = now;
+ }
+
+ // Time to switch to next mode?
+ if (now - modeStartTime >= getModeCycleIntervalMs()) {
+ currentMode = static_cast((currentMode + 1) % MODE_COUNT);
+ modeStartTime = now;
+ }
+
+ // Render screen based on currentMode
+ const char *title = getCurrentModeTitle(display->getWidth());
+ drawNodeListScreen(display, state, x, y, title, drawEntryDynamic);
+
+ // Track the last mode to avoid reinitializing modeStartTime
+ lastRenderedMode = currentMode;
+}
+#endif
+
+#ifdef USE_EINK
+void drawLastHeardScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
+{
+ const char *title = "Last Heard";
+ drawNodeListScreen(display, state, x, y, title, drawEntryLastHeard);
+}
+
+void drawHopSignalScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
+{
+#ifdef USE_EINK
+ const char *title = "Hops/Sig";
+#else
+
+ const char *title = "Hops/Signal";
+#endif
+ drawNodeListScreen(display, state, x, y, title, drawEntryHopSignal);
+}
+
+void drawDistanceScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
+{
+ const char *title = "Distance";
+ drawNodeListScreen(display, state, x, y, title, drawNodeDistance);
+}
+#endif
+
+void drawNodeListWithCompasses(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
+{
+ float heading = 0;
+ bool validHeading = false;
+ auto ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum());
+ double lat = DegD(ourNode->position.latitude_i);
+ double lon = DegD(ourNode->position.longitude_i);
+
+ if (uiconfig.compass_mode != meshtastic_CompassMode_FREEZE_HEADING) {
+#if HAS_GPS
+ if (screen->hasHeading()) {
+ heading = screen->getHeading(); // degrees
+ validHeading = true;
+ } else {
+ heading = screen->estimatedHeading(lat, lon);
+ validHeading = !isnan(heading);
+ }
+#endif
+
+ if (!validHeading)
+ return;
+ }
+ drawNodeListScreen(display, state, x, y, "Bearings", drawEntryCompass, drawCompassArrow, heading, lat, lon);
+}
+
+/// Draw a series of fields in a column, wrapping to multiple columns if needed
+void drawColumns(OLEDDisplay *display, int16_t x, int16_t y, const char **fields)
+{
+ // The coordinates define the left starting point of the text
+ display->setTextAlignment(TEXT_ALIGN_LEFT);
+
+ const char **f = fields;
+ int xo = x, yo = y;
+ while (*f) {
+ display->drawString(xo, yo, *f);
+ if ((display->getColor() == BLACK) && config.display.heading_bold)
+ display->drawString(xo + 1, yo, *f);
+
+ display->setColor(WHITE);
+ yo += FONT_HEIGHT_SMALL;
+ if (yo > SCREEN_HEIGHT - FONT_HEIGHT_SMALL) {
+ xo += SCREEN_WIDTH / 2;
+ yo = 0;
+ }
+ f++;
+ }
+}
+
+} // namespace NodeListRenderer
+} // namespace graphics
+#endif
\ No newline at end of file
diff --git a/src/graphics/draw/NodeListRenderer.h b/src/graphics/draw/NodeListRenderer.h
new file mode 100644
index 000000000..ea8df8bd9
--- /dev/null
+++ b/src/graphics/draw/NodeListRenderer.h
@@ -0,0 +1,62 @@
+#pragma once
+
+#include "graphics/Screen.h"
+#include "mesh/generated/meshtastic/mesh.pb.h"
+#include
+#include
+
+namespace graphics
+{
+
+/// Forward declarations
+class Screen;
+
+/**
+ * @brief Node list and entry rendering functions
+ *
+ * Contains all functions related to drawing node lists and individual node entries
+ * including last heard, hop signal, distance, and compass views.
+ */
+namespace NodeListRenderer
+{
+// Entry renderer function types
+typedef void (*EntryRenderer)(OLEDDisplay *, meshtastic_NodeInfoLite *, int16_t, int16_t, int);
+typedef void (*NodeExtrasRenderer)(OLEDDisplay *, meshtastic_NodeInfoLite *, int16_t, int16_t, int, float, double, double);
+
+// Node list mode enumeration
+enum NodeListMode { MODE_LAST_HEARD = 0, MODE_HOP_SIGNAL = 1, MODE_DISTANCE = 2, MODE_COUNT = 3 };
+
+// Main node list screen function
+void drawNodeListScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y, const char *title,
+ EntryRenderer renderer, NodeExtrasRenderer extras = nullptr, float heading = 0, double lat = 0,
+ double lon = 0);
+
+// Entry renderers
+void drawEntryLastHeard(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16_t x, int16_t y, int columnWidth);
+void drawEntryHopSignal(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16_t x, int16_t y, int columnWidth);
+void drawNodeDistance(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16_t x, int16_t y, int columnWidth);
+void drawEntryDynamic(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16_t x, int16_t y, int columnWidth);
+void drawEntryCompass(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16_t x, int16_t y, int columnWidth);
+
+// Extras renderers
+void drawCompassArrow(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16_t x, int16_t y, int columnWidth, float myHeading,
+ double userLat, double userLon);
+
+// Screen frame functions
+void drawLastHeardScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
+void drawHopSignalScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
+void drawDistanceScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
+void drawDynamicNodeListScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
+void drawNodeListWithCompasses(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
+
+// Utility functions
+const char *getCurrentModeTitle(int screenWidth);
+const char *getSafeNodeName(meshtastic_NodeInfoLite *node);
+void drawColumns(OLEDDisplay *display, int16_t x, int16_t y, const char **fields);
+
+// Bitmap drawing function
+void drawScaledXBitmap16x16(int x, int y, int width, int height, const uint8_t *bitmapXBM, OLEDDisplay *display);
+
+} // namespace NodeListRenderer
+
+} // namespace graphics
diff --git a/src/graphics/draw/NotificationRenderer.cpp b/src/graphics/draw/NotificationRenderer.cpp
new file mode 100644
index 000000000..7350c204f
--- /dev/null
+++ b/src/graphics/draw/NotificationRenderer.cpp
@@ -0,0 +1,579 @@
+#include "configuration.h"
+#if HAS_SCREEN
+
+#include "DisplayFormatters.h"
+#include "NodeDB.h"
+#include "NotificationRenderer.h"
+#include "graphics/ScreenFonts.h"
+#include "graphics/SharedUIDisplay.h"
+#include "graphics/images.h"
+#include "main.h"
+#include
+#include
+#include
+
+#ifdef ARCH_ESP32
+#include "esp_task_wdt.h"
+#endif
+
+using namespace meshtastic;
+
+// External references to global variables from Screen.cpp
+extern std::vector functionSymbol;
+extern std::string functionSymbolString;
+extern bool hasUnreadMessage;
+
+namespace graphics
+{
+
+InputEvent NotificationRenderer::inEvent;
+int8_t NotificationRenderer::curSelected = 0;
+char NotificationRenderer::alertBannerMessage[256] = {0};
+uint32_t NotificationRenderer::alertBannerUntil = 0; // 0 is a special case meaning forever
+uint8_t NotificationRenderer::alertBannerOptions = 0; // last x lines are seelctable options
+const char **NotificationRenderer::optionsArrayPtr = nullptr;
+const int *NotificationRenderer::optionsEnumPtr = nullptr;
+std::function NotificationRenderer::alertBannerCallback = NULL;
+bool NotificationRenderer::pauseBanner = false;
+notificationTypeEnum NotificationRenderer::current_notification_type = notificationTypeEnum::none;
+uint32_t NotificationRenderer::numDigits = 0;
+uint32_t NotificationRenderer::currentNumber = 0;
+
+uint32_t pow_of_10(uint32_t n)
+{
+ uint32_t ret = 1;
+ for (uint32_t i = 0; i < n; i++) {
+ ret *= 10;
+ }
+ return ret;
+}
+
+// Used on boot when a certificate is being created
+void NotificationRenderer::drawSSLScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
+{
+ display->setTextAlignment(TEXT_ALIGN_CENTER);
+ display->setFont(FONT_SMALL);
+ display->drawString(64 + x, y, "Creating SSL certificate");
+
+#ifdef ARCH_ESP32
+ yield();
+ esp_task_wdt_reset();
+#endif
+
+ display->setFont(FONT_SMALL);
+ if ((millis() / 1000) % 2) {
+ display->drawString(64 + x, FONT_HEIGHT_SMALL + y + 2, "Please wait . . .");
+ } else {
+ display->drawString(64 + x, FONT_HEIGHT_SMALL + y + 2, "Please wait . . ");
+ }
+}
+
+void NotificationRenderer::resetBanner()
+{
+ alertBannerMessage[0] = '\0';
+ current_notification_type = notificationTypeEnum::none;
+
+ inEvent.inputEvent = INPUT_BROKER_NONE;
+ inEvent.kbchar = 0;
+ curSelected = 0;
+ alertBannerOptions = 0; // last x lines are seelctable options
+ optionsArrayPtr = nullptr;
+ optionsEnumPtr = nullptr;
+ alertBannerCallback = NULL;
+ pauseBanner = false;
+ numDigits = 0;
+ currentNumber = 0;
+
+ nodeDB->pause_sort(false);
+}
+
+void NotificationRenderer::drawBannercallback(OLEDDisplay *display, OLEDDisplayUiState *state)
+{
+ if (!isOverlayBannerShowing() && alertBannerMessage[0] != '\0')
+ resetBanner();
+ if (!isOverlayBannerShowing() || pauseBanner)
+ return;
+ switch (current_notification_type) {
+ case notificationTypeEnum::none:
+ // Do nothing - no notification to display
+ break;
+ case notificationTypeEnum::text_banner:
+ case notificationTypeEnum::selection_picker:
+ drawAlertBannerOverlay(display, state);
+ break;
+ case notificationTypeEnum::node_picker:
+ drawNodePicker(display, state);
+ break;
+ case notificationTypeEnum::number_picker:
+ drawNumberPicker(display, state);
+ break;
+ }
+}
+
+void NotificationRenderer::drawNumberPicker(OLEDDisplay *display, OLEDDisplayUiState *state)
+{
+ const char *lineStarts[MAX_LINES + 1] = {0};
+ uint16_t lineCount = 0;
+
+ // Parse lines
+ char *alertEnd = alertBannerMessage + strnlen(alertBannerMessage, sizeof(alertBannerMessage));
+ lineStarts[lineCount] = alertBannerMessage;
+
+ // Find lines
+ while ((lineCount < MAX_LINES) && (lineStarts[lineCount] < alertEnd)) {
+ lineStarts[lineCount + 1] = std::find((char *)lineStarts[lineCount], alertEnd, '\n');
+ if (lineStarts[lineCount + 1][0] == '\n')
+ lineStarts[lineCount + 1] += 1;
+ lineCount++;
+ }
+ // modulo to extract
+ uint8_t this_digit = (currentNumber % (pow_of_10(numDigits - curSelected))) / (pow_of_10(numDigits - curSelected - 1));
+ // Handle input
+ if (inEvent.inputEvent == INPUT_BROKER_UP || inEvent.inputEvent == INPUT_BROKER_ALT_PRESS) {
+ if (this_digit == 9) {
+ currentNumber -= 9 * (pow_of_10(numDigits - curSelected - 1));
+ } else {
+ currentNumber += (pow_of_10(numDigits - curSelected - 1));
+ }
+ } else if (inEvent.inputEvent == INPUT_BROKER_DOWN || inEvent.inputEvent == INPUT_BROKER_USER_PRESS) {
+ if (this_digit == 0) {
+ currentNumber += 9 * (pow_of_10(numDigits - curSelected - 1));
+ } else {
+ currentNumber -= (pow_of_10(numDigits - curSelected - 1));
+ }
+ } else if (inEvent.inputEvent == INPUT_BROKER_ANYKEY) {
+ if (inEvent.kbchar > 47 && inEvent.kbchar < 58) { // have a digit
+ currentNumber -= this_digit * (pow_of_10(numDigits - curSelected - 1));
+ currentNumber += (inEvent.kbchar - 48) * (pow_of_10(numDigits - curSelected - 1));
+ curSelected++;
+ }
+ } else if (inEvent.inputEvent == INPUT_BROKER_SELECT || inEvent.inputEvent == INPUT_BROKER_RIGHT) {
+ curSelected++;
+ } else if (inEvent.inputEvent == INPUT_BROKER_LEFT) {
+ curSelected--;
+ } else if ((inEvent.inputEvent == INPUT_BROKER_CANCEL || inEvent.inputEvent == INPUT_BROKER_ALT_LONG) &&
+ alertBannerUntil != 0) {
+ resetBanner();
+ return;
+ }
+ if (curSelected == numDigits) {
+ alertBannerCallback(currentNumber);
+ resetBanner();
+ return;
+ }
+
+ inEvent.inputEvent = INPUT_BROKER_NONE;
+ if (alertBannerMessage[0] == '\0')
+ return;
+
+ uint16_t totalLines = lineCount + 2;
+ const char *linePointers[totalLines + 1] = {0}; // this is sort of a dynamic allocation
+
+ // copy the linestarts to display to the linePointers holder
+ for (uint16_t i = 0; i < lineCount; i++) {
+ linePointers[i] = lineStarts[i];
+ }
+ std::string digits = " ";
+ std::string arrowPointer = " ";
+ for (uint16_t i = 0; i < numDigits; i++) {
+ // Modulo minus modulo to return just the current number
+ digits += std::to_string((currentNumber % (pow_of_10(numDigits - i))) / (pow_of_10(numDigits - i - 1))) + " ";
+ if (curSelected == i) {
+ arrowPointer += "^ ";
+ } else {
+ arrowPointer += "_ ";
+ }
+ }
+
+ linePointers[lineCount++] = digits.c_str();
+ linePointers[lineCount++] = arrowPointer.c_str();
+
+ drawNotificationBox(display, state, linePointers, totalLines, 0);
+}
+
+void NotificationRenderer::drawNodePicker(OLEDDisplay *display, OLEDDisplayUiState *state)
+{
+ static uint32_t selectedNodenum = 0;
+
+ // === Layout Configuration ===
+ constexpr uint16_t vPadding = 2;
+ alertBannerOptions = nodeDB->getNumMeshNodes() - 1;
+
+ // let the box drawing function calculate the widths?
+
+ const char *lineStarts[MAX_LINES + 1] = {0};
+ uint16_t lineCount = 0;
+
+ // Parse lines
+ char *alertEnd = alertBannerMessage + strnlen(alertBannerMessage, sizeof(alertBannerMessage));
+ lineStarts[lineCount] = alertBannerMessage;
+
+ while ((lineCount < MAX_LINES) && (lineStarts[lineCount] < alertEnd)) {
+ lineStarts[lineCount + 1] = std::find((char *)lineStarts[lineCount], alertEnd, '\n');
+ if (lineStarts[lineCount + 1][0] == '\n')
+ lineStarts[lineCount + 1] += 1;
+ lineCount++;
+ }
+
+ // Handle input
+ if (inEvent.inputEvent == INPUT_BROKER_UP || inEvent.inputEvent == INPUT_BROKER_ALT_PRESS) {
+ curSelected--;
+ } else if (inEvent.inputEvent == INPUT_BROKER_DOWN || inEvent.inputEvent == INPUT_BROKER_USER_PRESS) {
+ curSelected++;
+ } else if (inEvent.inputEvent == INPUT_BROKER_SELECT) {
+ alertBannerCallback(selectedNodenum);
+ resetBanner();
+ return;
+ } else if ((inEvent.inputEvent == INPUT_BROKER_CANCEL || inEvent.inputEvent == INPUT_BROKER_ALT_LONG) &&
+ alertBannerUntil != 0) {
+ resetBanner();
+ return;
+ }
+
+ if (curSelected == -1)
+ curSelected = alertBannerOptions - 1;
+ if (curSelected == alertBannerOptions)
+ curSelected = 0;
+
+ inEvent.inputEvent = INPUT_BROKER_NONE;
+ if (alertBannerMessage[0] == '\0')
+ return;
+
+ uint16_t totalLines = lineCount + alertBannerOptions;
+ uint16_t screenHeight = display->height();
+ uint8_t effectiveLineHeight = FONT_HEIGHT_SMALL - 3;
+ uint8_t visibleTotalLines = std::min(totalLines, (screenHeight - vPadding * 2) / effectiveLineHeight);
+ uint8_t linesShown = lineCount;
+ const char *linePointers[visibleTotalLines + 1] = {0}; // this is sort of a dynamic allocation
+
+ // copy the linestarts to display to the linePointers holder
+ for (int i = 0; i < lineCount; i++) {
+ linePointers[i] = lineStarts[i];
+ }
+ char scratchLineBuffer[visibleTotalLines - lineCount][40];
+
+ uint8_t firstOptionToShow = 0;
+ if (curSelected > 1 && alertBannerOptions > visibleTotalLines - lineCount) {
+ if (curSelected > alertBannerOptions - visibleTotalLines + lineCount)
+ firstOptionToShow = alertBannerOptions - visibleTotalLines + lineCount;
+ else
+ firstOptionToShow = curSelected - 1;
+ } else {
+ firstOptionToShow = 0;
+ }
+ int scratchLineNum = 0;
+ for (int i = firstOptionToShow; i < alertBannerOptions && linesShown < visibleTotalLines; i++, linesShown++) {
+ char temp_name[16] = {0};
+ if (nodeDB->getMeshNodeByIndex(i + 1)->has_user) {
+ std::string sanitized = sanitizeString(nodeDB->getMeshNodeByIndex(i + 1)->user.long_name);
+ strncpy(temp_name, sanitized.c_str(), sizeof(temp_name) - 1);
+
+ } else {
+ snprintf(temp_name, sizeof(temp_name), "(%04X)", (uint16_t)(nodeDB->getMeshNodeByIndex(i + 1)->num & 0xFFFF));
+ }
+ // make temp buffer for name
+ // fi
+ if (i == curSelected) {
+ selectedNodenum = nodeDB->getMeshNodeByIndex(i + 1)->num;
+ if (isHighResolution) {
+ strncpy(scratchLineBuffer[scratchLineNum], "> ", 3);
+ strncpy(scratchLineBuffer[scratchLineNum] + 2, temp_name, 36);
+ strncpy(scratchLineBuffer[scratchLineNum] + strlen(temp_name) + 2, " <", 3);
+ } else {
+ strncpy(scratchLineBuffer[scratchLineNum], ">", 2);
+ strncpy(scratchLineBuffer[scratchLineNum] + 1, temp_name, 37);
+ strncpy(scratchLineBuffer[scratchLineNum] + strlen(temp_name) + 1, "<", 2);
+ }
+ scratchLineBuffer[scratchLineNum][39] = '\0';
+ } else {
+ strncpy(scratchLineBuffer[scratchLineNum], temp_name, 36);
+ }
+ linePointers[linesShown] = scratchLineBuffer[scratchLineNum++];
+ }
+ drawNotificationBox(display, state, linePointers, totalLines, firstOptionToShow);
+}
+
+void NotificationRenderer::drawAlertBannerOverlay(OLEDDisplay *display, OLEDDisplayUiState *state)
+{
+ // === Layout Configuration ===
+ constexpr uint16_t vPadding = 2;
+
+ uint16_t optionWidths[alertBannerOptions] = {0};
+ uint16_t maxWidth = 0;
+ uint16_t arrowsWidth = display->getStringWidth("> <", 4, true);
+ uint16_t lineWidths[MAX_LINES] = {0};
+ uint16_t lineLengths[MAX_LINES] = {0};
+ const char *lineStarts[MAX_LINES + 1] = {0};
+ uint16_t lineCount = 0;
+ char lineBuffer[40] = {0};
+
+ // Parse lines
+ char *alertEnd = alertBannerMessage + strnlen(alertBannerMessage, sizeof(alertBannerMessage));
+ lineStarts[lineCount] = alertBannerMessage;
+
+ while ((lineCount < MAX_LINES) && (lineStarts[lineCount] < alertEnd)) {
+ lineStarts[lineCount + 1] = std::find((char *)lineStarts[lineCount], alertEnd, '\n');
+ lineLengths[lineCount] = lineStarts[lineCount + 1] - lineStarts[lineCount];
+ if (lineStarts[lineCount + 1][0] == '\n')
+ lineStarts[lineCount + 1] += 1;
+ lineWidths[lineCount] = display->getStringWidth(lineStarts[lineCount], lineLengths[lineCount], true);
+ if (lineWidths[lineCount] > maxWidth)
+ maxWidth = lineWidths[lineCount];
+ lineCount++;
+ }
+
+ // Measure option widths
+ for (int i = 0; i < alertBannerOptions; i++) {
+ optionWidths[i] = display->getStringWidth(optionsArrayPtr[i], strlen(optionsArrayPtr[i]), true);
+ if (optionWidths[i] > maxWidth)
+ maxWidth = optionWidths[i];
+ if (optionWidths[i] + arrowsWidth > maxWidth)
+ maxWidth = optionWidths[i] + arrowsWidth;
+ }
+
+ // Handle input
+ if (alertBannerOptions > 0) {
+ if (inEvent.inputEvent == INPUT_BROKER_UP || inEvent.inputEvent == INPUT_BROKER_ALT_PRESS) {
+ curSelected--;
+ } else if (inEvent.inputEvent == INPUT_BROKER_DOWN || inEvent.inputEvent == INPUT_BROKER_USER_PRESS) {
+ curSelected++;
+ } else if (inEvent.inputEvent == INPUT_BROKER_SELECT) {
+ if (optionsEnumPtr != nullptr) {
+ alertBannerCallback(optionsEnumPtr[curSelected]);
+ optionsEnumPtr = nullptr;
+ } else {
+ alertBannerCallback(curSelected);
+ }
+ resetBanner();
+ return;
+ } else if ((inEvent.inputEvent == INPUT_BROKER_CANCEL || inEvent.inputEvent == INPUT_BROKER_ALT_LONG) &&
+ alertBannerUntil != 0) {
+ resetBanner();
+ return;
+ }
+
+ if (curSelected == -1)
+ curSelected = alertBannerOptions - 1;
+ if (curSelected == alertBannerOptions)
+ curSelected = 0;
+ } else {
+ if (inEvent.inputEvent == INPUT_BROKER_SELECT || inEvent.inputEvent == INPUT_BROKER_ALT_LONG ||
+ inEvent.inputEvent == INPUT_BROKER_CANCEL) {
+ resetBanner();
+ return;
+ }
+ }
+
+ inEvent.inputEvent = INPUT_BROKER_NONE;
+ if (alertBannerMessage[0] == '\0')
+ return;
+
+ uint16_t totalLines = lineCount + alertBannerOptions;
+
+ uint16_t screenHeight = display->height();
+ uint8_t effectiveLineHeight = FONT_HEIGHT_SMALL - 3;
+ uint8_t visibleTotalLines = std::min(totalLines, (screenHeight - vPadding * 2) / effectiveLineHeight);
+ uint8_t linesShown = lineCount;
+ const char *linePointers[visibleTotalLines + 1] = {0}; // this is sort of a dynamic allocation
+
+ // copy the linestarts to display to the linePointers holder
+ for (int i = 0; i < lineCount; i++) {
+ linePointers[i] = lineStarts[i];
+ }
+
+ uint8_t firstOptionToShow = 0;
+ if (alertBannerOptions > 0) {
+ if (curSelected > 1 && alertBannerOptions > visibleTotalLines - lineCount) {
+ if (curSelected > alertBannerOptions - visibleTotalLines + lineCount)
+ firstOptionToShow = alertBannerOptions - visibleTotalLines + lineCount;
+ else
+ firstOptionToShow = curSelected - 1;
+ } else {
+ firstOptionToShow = 0;
+ }
+ }
+
+ for (int i = firstOptionToShow; i < alertBannerOptions && linesShown < visibleTotalLines; i++, linesShown++) {
+ if (i == curSelected) {
+ if (isHighResolution) {
+ strncpy(lineBuffer, "> ", 3);
+ strncpy(lineBuffer + 2, optionsArrayPtr[i], 36);
+ strncpy(lineBuffer + strlen(optionsArrayPtr[i]) + 2, " <", 3);
+ } else {
+ strncpy(lineBuffer, ">", 2);
+ strncpy(lineBuffer + 1, optionsArrayPtr[i], 37);
+ strncpy(lineBuffer + strlen(optionsArrayPtr[i]) + 1, "<", 2);
+ }
+ lineBuffer[39] = '\0';
+ linePointers[linesShown] = lineBuffer;
+ } else {
+ linePointers[linesShown] = optionsArrayPtr[i];
+ }
+ }
+ if (alertBannerOptions > 0) {
+ drawNotificationBox(display, state, linePointers, totalLines, firstOptionToShow, maxWidth);
+ } else {
+ drawNotificationBox(display, state, linePointers, totalLines, firstOptionToShow);
+ }
+}
+
+void NotificationRenderer::drawNotificationBox(OLEDDisplay *display, OLEDDisplayUiState *state, const char *lines[],
+ uint16_t totalLines, uint8_t firstOptionToShow, uint16_t maxWidth)
+{
+
+ bool is_picker = false;
+ uint16_t lineCount = 0;
+ // === Layout Configuration ===
+ constexpr uint16_t hPadding = 5;
+ constexpr uint16_t vPadding = 2;
+ bool needs_bell = false;
+ uint16_t lineWidths[totalLines] = {0};
+ uint16_t lineLengths[totalLines] = {0};
+
+ if (maxWidth != 0)
+ is_picker = true;
+
+ // Setup font and alignment
+ display->setFont(FONT_SMALL);
+ display->setTextAlignment(TEXT_ALIGN_LEFT);
+
+ while (lines[lineCount] != nullptr) {
+ auto newlinePointer = strchr(lines[lineCount], '\n');
+ if (newlinePointer)
+ lineLengths[lineCount] = (newlinePointer - lines[lineCount]); // Check for newlines first
+ else // if the newline wasn't found, then pull string length from strlen
+ lineLengths[lineCount] = strlen(lines[lineCount]);
+ lineWidths[lineCount] = display->getStringWidth(lines[lineCount], lineLengths[lineCount], true);
+ if (!is_picker) {
+ needs_bell |= (strstr(alertBannerMessage, "Alert Received") != nullptr);
+ if (lineWidths[lineCount] > maxWidth)
+ maxWidth = lineWidths[lineCount];
+ }
+ lineCount++;
+ }
+ // count lines
+
+ uint16_t boxWidth = hPadding * 2 + maxWidth;
+ if (needs_bell) {
+ if (isHighResolution && boxWidth <= 150)
+ boxWidth += 26;
+ if (!isHighResolution && boxWidth <= 100)
+ boxWidth += 20;
+ }
+
+ uint16_t screenHeight = display->height();
+ uint8_t effectiveLineHeight = FONT_HEIGHT_SMALL - 3;
+ uint8_t visibleTotalLines = std::min(lineCount, (screenHeight - vPadding * 2) / effectiveLineHeight);
+ uint16_t contentHeight = visibleTotalLines * effectiveLineHeight;
+ uint16_t boxHeight = contentHeight + vPadding * 2;
+ if (visibleTotalLines == 1) {
+ boxHeight += (isHighResolution) ? 4 : 3;
+ }
+
+ int16_t boxLeft = (display->width() / 2) - (boxWidth / 2);
+ if (totalLines > visibleTotalLines) {
+ boxWidth += (isHighResolution) ? 4 : 2;
+ }
+ int16_t boxTop = (display->height() / 2) - (boxHeight / 2);
+
+ // === Draw Box ===
+ display->setColor(BLACK);
+ display->fillRect(boxLeft - 1, boxTop - 1, boxWidth + 2, boxHeight + 2);
+ display->fillRect(boxLeft, boxTop - 2, boxWidth, 1);
+ display->fillRect(boxLeft, boxTop + boxHeight + 1, boxWidth, 1);
+ display->fillRect(boxLeft - 2, boxTop, 1, boxHeight);
+ display->fillRect(boxLeft + boxWidth + 1, boxTop, 1, boxHeight);
+ display->setColor(WHITE);
+ display->drawRect(boxLeft, boxTop, boxWidth, boxHeight);
+ display->setColor(BLACK);
+ display->fillRect(boxLeft, boxTop, 1, 1);
+ display->fillRect(boxLeft + boxWidth - 1, boxTop, 1, 1);
+ display->fillRect(boxLeft, boxTop + boxHeight - 1, 1, 1);
+ display->fillRect(boxLeft + boxWidth - 1, boxTop + boxHeight - 1, 1, 1);
+ display->setColor(WHITE);
+
+ // === Draw Content ===
+ int16_t lineY = boxTop + vPadding;
+ for (int i = 0; i < lineCount; i++) {
+ int16_t textX = boxLeft + (boxWidth - lineWidths[i]) / 2;
+ if (needs_bell && i == 0) {
+ int bellY = lineY + (FONT_HEIGHT_SMALL - 8) / 2;
+ display->drawXbm(textX - 10, bellY, 8, 8, bell_alert);
+ display->drawXbm(textX + lineWidths[i] + 2, bellY, 8, 8, bell_alert);
+ }
+ char lineBuffer[lineLengths[i] + 1];
+ strncpy(lineBuffer, lines[i], lineLengths[i]);
+ lineBuffer[lineLengths[i]] = '\0';
+ // Determine if this is a pop-up or a pick list
+ if (alertBannerOptions > 0 && i == 0) {
+ // Pick List
+ display->setColor(WHITE);
+ int background_yOffset = 1;
+ // Determine if we have low hanging characters
+ if (strchr(lineBuffer, 'p') || strchr(lineBuffer, 'g') || strchr(lineBuffer, 'y') || strchr(lineBuffer, 'j')) {
+ background_yOffset = -1;
+ }
+ display->fillRect(boxLeft, boxTop + 1, boxWidth, effectiveLineHeight - background_yOffset);
+ display->setColor(BLACK);
+ int yOffset = 3;
+ display->drawString(textX, lineY - yOffset, lineBuffer);
+ display->setColor(WHITE);
+ lineY += (effectiveLineHeight - 2 - background_yOffset);
+ } else {
+ // Pop-up
+ display->drawString(textX, lineY, lineBuffer);
+ lineY += (effectiveLineHeight);
+ }
+ }
+
+ // === Scroll Bar (Thicker, inside box, not over title) ===
+ if (totalLines > visibleTotalLines) {
+ const uint8_t scrollBarWidth = 5;
+
+ int16_t scrollBarX = boxLeft + boxWidth - scrollBarWidth - 2;
+ int16_t scrollBarY = boxTop + vPadding + effectiveLineHeight; // start after title line
+ uint16_t scrollBarHeight = boxHeight - vPadding * 2 - effectiveLineHeight;
+
+ float ratio = (float)visibleTotalLines / totalLines;
+ uint16_t indicatorHeight = std::max((int)(scrollBarHeight * ratio), 4);
+ float scrollRatio = (float)(firstOptionToShow + lineCount - visibleTotalLines) / (totalLines - visibleTotalLines);
+ uint16_t indicatorY = scrollBarY + scrollRatio * (scrollBarHeight - indicatorHeight);
+
+ display->drawRect(scrollBarX, scrollBarY, scrollBarWidth, scrollBarHeight);
+ display->fillRect(scrollBarX + 1, indicatorY, scrollBarWidth - 2, indicatorHeight);
+ }
+}
+
+/// Draw the last text message we received
+void NotificationRenderer::drawCriticalFaultFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
+{
+ display->setTextAlignment(TEXT_ALIGN_LEFT);
+ display->setFont(FONT_MEDIUM);
+
+ char tempBuf[24];
+ snprintf(tempBuf, sizeof(tempBuf), "Critical fault #%d", error_code);
+ display->drawString(0 + x, 0 + y, tempBuf);
+ display->setTextAlignment(TEXT_ALIGN_LEFT);
+ display->setFont(FONT_SMALL);
+ display->drawString(0 + x, FONT_HEIGHT_MEDIUM + y, "For help, please visit \nmeshtastic.org");
+}
+
+void NotificationRenderer::drawFrameFirmware(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
+{
+ display->setTextAlignment(TEXT_ALIGN_CENTER);
+ display->setFont(FONT_MEDIUM);
+ display->drawString(64 + x, y, "Updating");
+
+ display->setFont(FONT_SMALL);
+ display->setTextAlignment(TEXT_ALIGN_LEFT);
+ display->drawStringMaxWidth(0 + x, 2 + y + FONT_HEIGHT_SMALL * 2, x + display->getWidth(),
+ "Please be patient and do not power off.");
+}
+
+bool NotificationRenderer::isOverlayBannerShowing()
+{
+ return strlen(alertBannerMessage) > 0 && (alertBannerUntil == 0 || millis() <= alertBannerUntil);
+}
+
+} // namespace graphics
+#endif
\ No newline at end of file
diff --git a/src/graphics/draw/NotificationRenderer.h b/src/graphics/draw/NotificationRenderer.h
new file mode 100644
index 000000000..9c30b329c
--- /dev/null
+++ b/src/graphics/draw/NotificationRenderer.h
@@ -0,0 +1,44 @@
+#pragma once
+
+#include "OLEDDisplay.h"
+#include "OLEDDisplayUi.h"
+#include "graphics/Screen.h"
+#define MAX_LINES 5
+
+namespace graphics
+{
+
+class NotificationRenderer
+{
+ public:
+ static InputEvent inEvent;
+ static char inKeypress;
+ static int8_t curSelected;
+ static char alertBannerMessage[256];
+ static uint32_t alertBannerUntil; // 0 is a special case meaning forever
+ static const char **optionsArrayPtr;
+ static const int *optionsEnumPtr;
+ static uint8_t alertBannerOptions; // last x lines are seelctable options
+ static std::function alertBannerCallback;
+ static uint32_t numDigits;
+ static uint32_t currentNumber;
+
+ static bool pauseBanner;
+
+ static void resetBanner();
+ static void drawBannercallback(OLEDDisplay *display, OLEDDisplayUiState *state);
+ static void drawAlertBannerOverlay(OLEDDisplay *display, OLEDDisplayUiState *state);
+ static void drawNumberPicker(OLEDDisplay *display, OLEDDisplayUiState *state);
+ static void drawNodePicker(OLEDDisplay *display, OLEDDisplayUiState *state);
+ static void drawNotificationBox(OLEDDisplay *display, OLEDDisplayUiState *state, const char *lines[MAX_LINES + 1],
+ uint16_t totalLines, uint8_t firstOptionToShow, uint16_t maxWidth = 0);
+
+ static void drawCriticalFaultFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
+ static void drawSSLScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
+ static void drawFrameFirmware(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
+ static bool isOverlayBannerShowing();
+
+ static graphics::notificationTypeEnum current_notification_type;
+};
+
+} // namespace graphics
diff --git a/src/graphics/draw/UIRenderer.cpp b/src/graphics/draw/UIRenderer.cpp
new file mode 100644
index 000000000..9be8b04f4
--- /dev/null
+++ b/src/graphics/draw/UIRenderer.cpp
@@ -0,0 +1,1231 @@
+#include "configuration.h"
+#if HAS_SCREEN
+#include "CompassRenderer.h"
+#include "GPSStatus.h"
+#include "NodeDB.h"
+#include "NodeListRenderer.h"
+#include "UIRenderer.h"
+#include "airtime.h"
+#include "configuration.h"
+#include "gps/GeoCoord.h"
+#include "graphics/Screen.h"
+#include "graphics/ScreenFonts.h"
+#include "graphics/SharedUIDisplay.h"
+#include "graphics/images.h"
+#include "main.h"
+#include "target_specific.h"
+#include
+#include
+#include
+
+// External variables
+extern graphics::Screen *screen;
+
+namespace graphics
+{
+NodeNum UIRenderer::currentFavoriteNodeNum = 0;
+
+#if !MESHTASTIC_EXCLUDE_GPS
+// GeoCoord object for coordinate conversions
+extern GeoCoord geoCoord;
+
+// Threshold values for the GPS lock accuracy bar display
+extern uint32_t dopThresholds[5];
+
+// Draw GPS status summary
+void UIRenderer::drawGps(OLEDDisplay *display, int16_t x, int16_t y, const meshtastic::GPSStatus *gps)
+{
+ // Draw satellite image
+ if (isHighResolution) {
+ NodeListRenderer::drawScaledXBitmap16x16(x, y - 2, imgSatellite_width, imgSatellite_height, imgSatellite, display);
+ } else {
+ display->drawXbm(x + 1, y + 1, imgSatellite_width, imgSatellite_height, imgSatellite);
+ }
+ char textString[10];
+
+ if (config.position.fixed_position) {
+ // GPS coordinates are currently fixed
+ snprintf(textString, sizeof(textString), "Fixed");
+ }
+ if (!gps->getIsConnected()) {
+ snprintf(textString, sizeof(textString), "No Lock");
+ }
+ if (!gps->getHasLock()) {
+ // Draw "No sats" to the right of the icon with slightly more gap
+ snprintf(textString, sizeof(textString), "No Sats");
+ } else {
+ snprintf(textString, sizeof(textString), "%u sats", gps->getNumSatellites());
+ }
+ if (isHighResolution) {
+ display->drawString(x + 18, y, textString);
+ } else {
+ display->drawString(x + 11, y, textString);
+ }
+}
+
+// Draw status when GPS is disabled or not present
+void UIRenderer::drawGpsPowerStatus(OLEDDisplay *display, int16_t x, int16_t y, const meshtastic::GPSStatus *gps)
+{
+ const char *displayLine;
+ int pos;
+ if (y < FONT_HEIGHT_SMALL) { // Line 1: use short string
+ displayLine = config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_NOT_PRESENT ? "No GPS" : "GPS off";
+ pos = display->getWidth() - display->getStringWidth(displayLine);
+ } else {
+ displayLine = config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_NOT_PRESENT ? "GPS not present"
+ : "GPS is disabled";
+ pos = (display->getWidth() - display->getStringWidth(displayLine)) / 2;
+ }
+ display->drawString(x + pos, y, displayLine);
+}
+
+void UIRenderer::drawGpsAltitude(OLEDDisplay *display, int16_t x, int16_t y, const meshtastic::GPSStatus *gps)
+{
+ char displayLine[32];
+ if (!gps->getIsConnected() && !config.position.fixed_position) {
+ // displayLine = "No GPS Module";
+ // display->drawString(x + (SCREEN_WIDTH - (display->getStringWidth(displayLine))) / 2, y, displayLine);
+ } else if (!gps->getHasLock() && !config.position.fixed_position) {
+ // displayLine = "No GPS Lock";
+ // display->drawString(x + (SCREEN_WIDTH - (display->getStringWidth(displayLine))) / 2, y, displayLine);
+ } else {
+ geoCoord.updateCoords(int32_t(gps->getLatitude()), int32_t(gps->getLongitude()), int32_t(gps->getAltitude()));
+ if (config.display.units == meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL)
+ snprintf(displayLine, sizeof(displayLine), "Altitude: %.0fft", geoCoord.getAltitude() * METERS_TO_FEET);
+ else
+ snprintf(displayLine, sizeof(displayLine), "Altitude: %.0im", geoCoord.getAltitude());
+ display->drawString(x + (display->getWidth() - (display->getStringWidth(displayLine))) / 2, y, displayLine);
+ }
+}
+
+// Draw GPS status coordinates
+void UIRenderer::drawGpsCoordinates(OLEDDisplay *display, int16_t x, int16_t y, const meshtastic::GPSStatus *gps)
+{
+ auto gpsFormat = config.display.gps_format;
+ char displayLine[32];
+
+ if (!gps->getIsConnected() && !config.position.fixed_position) {
+ strcpy(displayLine, "No GPS present");
+ display->drawString(x + (display->getWidth() - (display->getStringWidth(displayLine))) / 2, y, displayLine);
+ } else if (!gps->getHasLock() && !config.position.fixed_position) {
+ strcpy(displayLine, "No GPS Lock");
+ display->drawString(x + (display->getWidth() - (display->getStringWidth(displayLine))) / 2, y, displayLine);
+ } else {
+
+ geoCoord.updateCoords(int32_t(gps->getLatitude()), int32_t(gps->getLongitude()), int32_t(gps->getAltitude()));
+
+ if (gpsFormat != meshtastic_Config_DisplayConfig_GpsCoordinateFormat_DMS) {
+ char coordinateLine[22];
+ if (gpsFormat == meshtastic_Config_DisplayConfig_GpsCoordinateFormat_DEC) { // Decimal Degrees
+ snprintf(coordinateLine, sizeof(coordinateLine), "%f %f", geoCoord.getLatitude() * 1e-7,
+ geoCoord.getLongitude() * 1e-7);
+ } else if (gpsFormat == meshtastic_Config_DisplayConfig_GpsCoordinateFormat_UTM) { // Universal Transverse Mercator
+ snprintf(coordinateLine, sizeof(coordinateLine), "%2i%1c %06u %07u", geoCoord.getUTMZone(), geoCoord.getUTMBand(),
+ geoCoord.getUTMEasting(), geoCoord.getUTMNorthing());
+ } else if (gpsFormat == meshtastic_Config_DisplayConfig_GpsCoordinateFormat_MGRS) { // Military Grid Reference System
+ snprintf(coordinateLine, sizeof(coordinateLine), "%2i%1c %1c%1c %05u %05u", geoCoord.getMGRSZone(),
+ geoCoord.getMGRSBand(), geoCoord.getMGRSEast100k(), geoCoord.getMGRSNorth100k(),
+ geoCoord.getMGRSEasting(), geoCoord.getMGRSNorthing());
+ } else if (gpsFormat == meshtastic_Config_DisplayConfig_GpsCoordinateFormat_OLC) { // Open Location Code
+ geoCoord.getOLCCode(coordinateLine);
+ } else if (gpsFormat == meshtastic_Config_DisplayConfig_GpsCoordinateFormat_OSGR) { // Ordnance Survey Grid Reference
+ if (geoCoord.getOSGRE100k() == 'I' || geoCoord.getOSGRN100k() == 'I') // OSGR is only valid around the UK region
+ snprintf(coordinateLine, sizeof(coordinateLine), "%s", "Out of Boundary");
+ else
+ snprintf(coordinateLine, sizeof(coordinateLine), "%1c%1c %05u %05u", geoCoord.getOSGRE100k(),
+ geoCoord.getOSGRN100k(), geoCoord.getOSGREasting(), geoCoord.getOSGRNorthing());
+ }
+
+ // If fixed position, display text "Fixed GPS" alternating with the coordinates.
+ if (config.position.fixed_position) {
+ if ((millis() / 10000) % 2) {
+ display->drawString(x + (display->getWidth() - (display->getStringWidth(coordinateLine))) / 2, y,
+ coordinateLine);
+ } else {
+ display->drawString(x + (display->getWidth() - (display->getStringWidth("Fixed GPS"))) / 2, y, "Fixed GPS");
+ }
+ } else {
+ display->drawString(x + (display->getWidth() - (display->getStringWidth(coordinateLine))) / 2, y, coordinateLine);
+ }
+ } else {
+ char latLine[22];
+ char lonLine[22];
+ snprintf(latLine, sizeof(latLine), "%2iยฐ %2i' %2u\" %1c", geoCoord.getDMSLatDeg(), geoCoord.getDMSLatMin(),
+ geoCoord.getDMSLatSec(), geoCoord.getDMSLatCP());
+ snprintf(lonLine, sizeof(lonLine), "%3iยฐ %2i' %2u\" %1c", geoCoord.getDMSLonDeg(), geoCoord.getDMSLonMin(),
+ geoCoord.getDMSLonSec(), geoCoord.getDMSLonCP());
+ display->drawString(x + (display->getWidth() - (display->getStringWidth(latLine))) / 2, y - FONT_HEIGHT_SMALL * 1,
+ latLine);
+ display->drawString(x + (display->getWidth() - (display->getStringWidth(lonLine))) / 2, y, lonLine);
+ }
+ }
+}
+#endif // !MESHTASTIC_EXCLUDE_GPS
+
+// Draw nodes status
+void UIRenderer::drawNodes(OLEDDisplay *display, int16_t x, int16_t y, const meshtastic::NodeStatus *nodeStatus, int node_offset,
+ bool show_total, String additional_words)
+{
+ char usersString[20];
+ int nodes_online = (nodeStatus->getNumOnline() > 0) ? nodeStatus->getNumOnline() + node_offset : 0;
+
+ snprintf(usersString, sizeof(usersString), "%d %s", nodes_online, additional_words.c_str());
+
+ if (show_total) {
+ int nodes_total = (nodeStatus->getNumTotal() > 0) ? nodeStatus->getNumTotal() + node_offset : 0;
+ snprintf(usersString, sizeof(usersString), "%d/%d %s", nodes_online, nodes_total, additional_words.c_str());
+ }
+
+#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \
+ defined(ST7789_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(HX8357_CS)) && \
+ !defined(DISPLAY_FORCE_SMALL_FONTS)
+
+ if (isHighResolution) {
+ NodeListRenderer::drawScaledXBitmap16x16(x, y - 1, 8, 8, imgUser, display);
+ } else {
+ display->drawFastImage(x, y + 3, 8, 8, imgUser);
+ }
+#else
+ if (isHighResolution) {
+ NodeListRenderer::drawScaledXBitmap16x16(x, y - 1, 8, 8, imgUser, display);
+ } else {
+ display->drawFastImage(x, y + 1, 8, 8, imgUser);
+ }
+#endif
+ int string_offset = (isHighResolution) ? 9 : 0;
+ display->drawString(x + 10 + string_offset, y - 2, usersString);
+}
+
+// **********************
+// * Favorite Node Info *
+// **********************
+void UIRenderer::drawNodeInfo(OLEDDisplay *display, const OLEDDisplayUiState *state, int16_t x, int16_t y)
+{
+ // --- Cache favorite nodes for the current frame only, to save computation ---
+ static std::vector favoritedNodes;
+ static int prevFrame = -1;
+
+ // --- Only rebuild favorites list if we're on a new frame ---
+ if (state->currentFrame != prevFrame) {
+ prevFrame = state->currentFrame;
+ favoritedNodes.clear();
+ size_t total = nodeDB->getNumMeshNodes();
+ for (size_t i = 0; i < total; i++) {
+ meshtastic_NodeInfoLite *n = nodeDB->getMeshNodeByIndex(i);
+ // Skip nulls and ourself
+ if (!n || n->num == nodeDB->getNodeNum())
+ continue;
+ if (n->is_favorite)
+ favoritedNodes.push_back(n);
+ }
+ // Keep a stable, consistent display order
+ std::sort(favoritedNodes.begin(), favoritedNodes.end(),
+ [](const meshtastic_NodeInfoLite *a, const meshtastic_NodeInfoLite *b) { return a->num < b->num; });
+ }
+ if (favoritedNodes.empty())
+ return;
+
+ // --- Only display if index is valid ---
+ int nodeIndex = state->currentFrame - (screen->frameCount - favoritedNodes.size());
+ if (nodeIndex < 0 || nodeIndex >= (int)favoritedNodes.size())
+ return;
+
+ meshtastic_NodeInfoLite *node = favoritedNodes[nodeIndex];
+ if (!node || node->num == nodeDB->getNodeNum() || !node->is_favorite)
+ return;
+
+ display->clear();
+ currentFavoriteNodeNum = node->num;
+ // === Create the shortName and title string ===
+ const char *shortName = (node->has_user && haveGlyphs(node->user.short_name)) ? node->user.short_name : "Node";
+ char titlestr[32] = {0};
+ snprintf(titlestr, sizeof(titlestr), "Fav: %s", shortName);
+
+ // === Draw battery/time/mail header (common across screens) ===
+ graphics::drawCommonHeader(display, x, y, titlestr);
+
+ // ===== DYNAMIC ROW STACKING WITH YOUR MACROS =====
+ // 1. Each potential info row has a macro-defined Y position (not regular increments!).
+ // 2. Each row is only shown if it has valid data.
+ // 3. Each row "moves up" if previous are empty, so there are never any blank rows.
+ // 4. The first line is ALWAYS at your macro position; subsequent lines use the next available macro slot.
+
+ // List of available macro Y positions in order, from top to bottom.
+ int line = 1; // which slot to use next
+ std::string usernameStr;
+
+ // === 1. Long Name (always try to show first) ===
+ const char *username = (node->has_user && node->user.long_name[0]) ? node->user.long_name : nullptr;
+ if (username) {
+ usernameStr = sanitizeString(username); // Sanitize the incoming long_name just in case
+ // Print node's long name (e.g. "Backpack Node")
+ display->drawString(x, getTextPositions(display)[line++], usernameStr.c_str());
+ }
+
+ // === 2. Signal and Hops (combined on one line, if available) ===
+ // If both are present: "Sig: 97% [2hops]"
+ // If only one: show only that one
+ char signalHopsStr[32] = "";
+ bool haveSignal = false;
+ int percentSignal = clamp((int)((node->snr + 10) * 5), 0, 100);
+
+ // Always use "Sig" for the label
+ const char *signalLabel = " Sig";
+
+ // --- Build the Signal/Hops line ---
+ // If SNR looks reasonable, show signal
+ if ((int)((node->snr + 10) * 5) >= 0 && node->snr > -100) {
+ snprintf(signalHopsStr, sizeof(signalHopsStr), "%s: %d%%", signalLabel, percentSignal);
+ haveSignal = true;
+ }
+ // If hops is valid (>0), show right after signal
+ if (node->hops_away > 0) {
+ size_t len = strlen(signalHopsStr);
+ // Decide between "1 Hop" and "N Hops"
+ if (haveSignal) {
+ snprintf(signalHopsStr + len, sizeof(signalHopsStr) - len, " [%d %s]", node->hops_away,
+ (node->hops_away == 1 ? "Hop" : "Hops"));
+ } else {
+ snprintf(signalHopsStr, sizeof(signalHopsStr), "[%d %s]", node->hops_away, (node->hops_away == 1 ? "Hop" : "Hops"));
+ }
+ }
+ if (signalHopsStr[0] && line < 5) {
+ display->drawString(x, getTextPositions(display)[line++], signalHopsStr);
+ }
+
+ // === 3. Heard (last seen, skip if node never seen) ===
+ char seenStr[20] = "";
+ uint32_t seconds = sinceLastSeen(node);
+ if (seconds != 0 && seconds != UINT32_MAX) {
+ uint32_t minutes = seconds / 60, hours = minutes / 60, days = hours / 24;
+ // Format as "Heard: Xm ago", "Heard: Xh ago", or "Heard: Xd ago"
+ snprintf(seenStr, sizeof(seenStr), (days > 365 ? " Heard: ?" : " Heard: %d%c ago"),
+ (days ? days
+ : hours ? hours
+ : minutes),
+ (days ? 'd'
+ : hours ? 'h'
+ : 'm'));
+ }
+ if (seenStr[0] && line < 5) {
+ display->drawString(x, getTextPositions(display)[line++], seenStr);
+ }
+
+ // === 4. Uptime (only show if metric is present) ===
+ char uptimeStr[32] = "";
+ if (node->has_device_metrics && node->device_metrics.has_uptime_seconds) {
+ uint32_t uptime = node->device_metrics.uptime_seconds;
+ uint32_t days = uptime / 86400;
+ uint32_t hours = (uptime % 86400) / 3600;
+ uint32_t mins = (uptime % 3600) / 60;
+ // Show as "Up: 2d 3h", "Up: 5h 14m", or "Up: 37m"
+ if (days)
+ snprintf(uptimeStr, sizeof(uptimeStr), " Uptime: %ud %uh", days, hours);
+ else if (hours)
+ snprintf(uptimeStr, sizeof(uptimeStr), " Uptime: %uh %um", hours, mins);
+ else
+ snprintf(uptimeStr, sizeof(uptimeStr), " Uptime: %um", mins);
+ }
+ if (uptimeStr[0] && line < 5) {
+ display->drawString(x, getTextPositions(display)[line++], uptimeStr);
+ }
+
+ // === 5. Distance (only if both nodes have GPS position) ===
+ meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum());
+ char distStr[24] = ""; // Make buffer big enough for any string
+ bool haveDistance = false;
+
+ if (nodeDB->hasValidPosition(ourNode) && nodeDB->hasValidPosition(node)) {
+ double lat1 = ourNode->position.latitude_i * 1e-7;
+ double lon1 = ourNode->position.longitude_i * 1e-7;
+ double lat2 = node->position.latitude_i * 1e-7;
+ double lon2 = node->position.longitude_i * 1e-7;
+ double earthRadiusKm = 6371.0;
+ double dLat = (lat2 - lat1) * DEG_TO_RAD;
+ double dLon = (lon2 - lon1) * DEG_TO_RAD;
+ double a =
+ sin(dLat / 2) * sin(dLat / 2) + cos(lat1 * DEG_TO_RAD) * cos(lat2 * DEG_TO_RAD) * sin(dLon / 2) * sin(dLon / 2);
+ double c = 2 * atan2(sqrt(a), sqrt(1 - a));
+ double distanceKm = earthRadiusKm * c;
+
+ if (config.display.units == meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL) {
+ double miles = distanceKm * 0.621371;
+ if (miles < 0.1) {
+ int feet = (int)(miles * 5280);
+ if (feet > 0 && feet < 1000) {
+ snprintf(distStr, sizeof(distStr), " Distance: %dft", feet);
+ haveDistance = true;
+ } else if (feet >= 1000) {
+ snprintf(distStr, sizeof(distStr), " Distance: ยผmi");
+ haveDistance = true;
+ }
+ } else {
+ int roundedMiles = (int)(miles + 0.5);
+ if (roundedMiles > 0 && roundedMiles < 1000) {
+ snprintf(distStr, sizeof(distStr), " Distance: %dmi", roundedMiles);
+ haveDistance = true;
+ }
+ }
+ } else {
+ if (distanceKm < 1.0) {
+ int meters = (int)(distanceKm * 1000);
+ if (meters > 0 && meters < 1000) {
+ snprintf(distStr, sizeof(distStr), " Distance: %dm", meters);
+ haveDistance = true;
+ } else if (meters >= 1000) {
+ snprintf(distStr, sizeof(distStr), " Distance: 1km");
+ haveDistance = true;
+ }
+ } else {
+ int km = (int)(distanceKm + 0.5);
+ if (km > 0 && km < 1000) {
+ snprintf(distStr, sizeof(distStr), " Distance: %dkm", km);
+ haveDistance = true;
+ }
+ }
+ }
+ }
+ // Only display if we actually have a value!
+ if (haveDistance && distStr[0] && line < 5) {
+ display->drawString(x, getTextPositions(display)[line++], distStr);
+ }
+
+ // --- Compass Rendering: landscape (wide) screens use the original side-aligned logic ---
+ if (SCREEN_WIDTH > SCREEN_HEIGHT) {
+ bool showCompass = false;
+ if (ourNode && (nodeDB->hasValidPosition(ourNode) || screen->hasHeading()) && nodeDB->hasValidPosition(node)) {
+ showCompass = true;
+ }
+ if (showCompass) {
+ const int16_t topY = getTextPositions(display)[1];
+ const int16_t bottomY = SCREEN_HEIGHT - (FONT_HEIGHT_SMALL - 1);
+ const int16_t usableHeight = bottomY - topY - 5;
+ int16_t compassRadius = usableHeight / 2;
+ if (compassRadius < 8)
+ compassRadius = 8;
+ const int16_t compassDiam = compassRadius * 2;
+ const int16_t compassX = x + SCREEN_WIDTH - compassRadius - 8;
+ const int16_t compassY = topY + (usableHeight / 2) + ((FONT_HEIGHT_SMALL - 1) / 2) + 2;
+
+ const auto &op = ourNode->position;
+ float myHeading = screen->hasHeading() ? screen->getHeading() * PI / 180
+ : screen->estimatedHeading(DegD(op.latitude_i), DegD(op.longitude_i));
+
+ const auto &p = node->position;
+ /* unused
+ float d =
+ GeoCoord::latLongToMeter(DegD(p.latitude_i), DegD(p.longitude_i), DegD(op.latitude_i), DegD(op.longitude_i));
+ */
+ float bearing = GeoCoord::bearing(DegD(op.latitude_i), DegD(op.longitude_i), DegD(p.latitude_i), DegD(p.longitude_i));
+ if (uiconfig.compass_mode == meshtastic_CompassMode_FREEZE_HEADING) {
+ myHeading = 0;
+ } else {
+ bearing -= myHeading;
+ }
+
+ display->drawCircle(compassX, compassY, compassRadius);
+ CompassRenderer::drawCompassNorth(display, compassX, compassY, myHeading, compassRadius);
+ CompassRenderer::drawNodeHeading(display, compassX, compassY, compassDiam, bearing);
+ }
+ // else show nothing
+ } else {
+ // Portrait or square: put compass at the bottom and centered, scaled to fit available space
+ bool showCompass = false;
+ if (ourNode && (nodeDB->hasValidPosition(ourNode) || screen->hasHeading()) && nodeDB->hasValidPosition(node)) {
+ showCompass = true;
+ }
+ if (showCompass) {
+ int yBelowContent = (line > 0 && line <= 5) ? (getTextPositions(display)[line - 1] + FONT_HEIGHT_SMALL + 2)
+ : getTextPositions(display)[1];
+ const int margin = 4;
+// --------- PATCH FOR EINK NAV BAR (ONLY CHANGE BELOW) -----------
+#if defined(USE_EINK)
+ const int iconSize = (isHighResolution) ? 16 : 8;
+ const int navBarHeight = iconSize + 6;
+#else
+ const int navBarHeight = 0;
+#endif
+ int availableHeight = SCREEN_HEIGHT - yBelowContent - navBarHeight - margin;
+ // --------- END PATCH FOR EINK NAV BAR -----------
+
+ if (availableHeight < FONT_HEIGHT_SMALL * 2)
+ return;
+
+ int compassRadius = availableHeight / 2;
+ if (compassRadius < 8)
+ compassRadius = 8;
+ if (compassRadius * 2 > SCREEN_WIDTH - 16)
+ compassRadius = (SCREEN_WIDTH - 16) / 2;
+
+ int compassX = x + SCREEN_WIDTH / 2;
+ int compassY = yBelowContent + availableHeight / 2;
+
+ const auto &op = ourNode->position;
+ float myHeading = 0;
+ if (uiconfig.compass_mode != meshtastic_CompassMode_FREEZE_HEADING) {
+ myHeading = screen->hasHeading() ? screen->getHeading() * PI / 180
+ : screen->estimatedHeading(DegD(op.latitude_i), DegD(op.longitude_i));
+ }
+ graphics::CompassRenderer::drawCompassNorth(display, compassX, compassY, myHeading, compassRadius);
+
+ const auto &p = node->position;
+ /* unused
+ float d =
+ GeoCoord::latLongToMeter(DegD(p.latitude_i), DegD(p.longitude_i), DegD(op.latitude_i), DegD(op.longitude_i));
+ */
+ float bearing = GeoCoord::bearing(DegD(op.latitude_i), DegD(op.longitude_i), DegD(p.latitude_i), DegD(p.longitude_i));
+ if (uiconfig.compass_mode != meshtastic_CompassMode_FREEZE_HEADING)
+ bearing -= myHeading;
+ graphics::CompassRenderer::drawNodeHeading(display, compassX, compassY, compassRadius * 2, bearing);
+
+ display->drawCircle(compassX, compassY, compassRadius);
+ }
+ // else show nothing
+ }
+}
+
+// ****************************
+// * Device Focused Screen *
+// ****************************
+void UIRenderer::drawDeviceFocused(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
+{
+ display->clear();
+ display->setTextAlignment(TEXT_ALIGN_LEFT);
+ display->setFont(FONT_SMALL);
+ int line = 1;
+
+ // === Header ===
+ graphics::drawCommonHeader(display, x, y, "");
+
+ // === Content below header ===
+
+ // Determine if we need to show 4 or 5 rows on the screen
+ int rows = 4;
+ if (!config.bluetooth.enabled) {
+ rows = 5;
+ }
+
+ // === First Row: Region / Channel Utilization and Uptime ===
+ bool origBold = config.display.heading_bold;
+ config.display.heading_bold = false;
+
+ // Display Region and Channel Utilization
+ drawNodes(display, x + 1, getTextPositions(display)[line] + 2, nodeStatus, -1, false, "online");
+
+ char uptimeStr[32] = "";
+ uint32_t uptime = millis() / 1000;
+ uint32_t days = uptime / 86400;
+ uint32_t hours = (uptime % 86400) / 3600;
+ uint32_t mins = (uptime % 3600) / 60;
+ // Show as "Up: 2d 3h", "Up: 5h 14m", or "Up: 37m"
+ if (days)
+ snprintf(uptimeStr, sizeof(uptimeStr), "Up: %ud %uh", days, hours);
+ else if (hours)
+ snprintf(uptimeStr, sizeof(uptimeStr), "Up: %uh %um", hours, mins);
+ else
+ snprintf(uptimeStr, sizeof(uptimeStr), "Up: %um", mins);
+ display->drawString(SCREEN_WIDTH - display->getStringWidth(uptimeStr), getTextPositions(display)[line++], uptimeStr);
+
+ // === Second Row: Satellites and Voltage ===
+ config.display.heading_bold = false;
+
+#if HAS_GPS
+ if (config.position.gps_mode != meshtastic_Config_PositionConfig_GpsMode_ENABLED) {
+ const char *displayLine;
+ if (config.position.fixed_position) {
+ displayLine = "Fixed GPS";
+ } else {
+ displayLine = config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_NOT_PRESENT ? "No GPS" : "GPS off";
+ }
+ int yOffset = (isHighResolution) ? 3 : 1;
+ if (isHighResolution) {
+ NodeListRenderer::drawScaledXBitmap16x16(x, getTextPositions(display)[line] + yOffset - 5, imgSatellite_width,
+ imgSatellite_height, imgSatellite, display);
+ } else {
+ display->drawXbm(x + 1, getTextPositions(display)[line] + yOffset, imgSatellite_width, imgSatellite_height,
+ imgSatellite);
+ }
+ int xOffset = (isHighResolution) ? 6 : 0;
+ display->drawString(x + 11 + xOffset, getTextPositions(display)[line], displayLine);
+ } else {
+ UIRenderer::drawGps(display, 0, getTextPositions(display)[line], gpsStatus);
+ }
+#endif
+
+ if (powerStatus->getHasBattery()) {
+ char batStr[20];
+ int batV = powerStatus->getBatteryVoltageMv() / 1000;
+ int batCv = (powerStatus->getBatteryVoltageMv() % 1000) / 10;
+ snprintf(batStr, sizeof(batStr), "%01d.%02dV", batV, batCv);
+ display->drawString(x + SCREEN_WIDTH - display->getStringWidth(batStr), getTextPositions(display)[line++], batStr);
+ } else {
+ display->drawString(x + SCREEN_WIDTH - display->getStringWidth("USB"), getTextPositions(display)[line++], "USB");
+ }
+
+ config.display.heading_bold = origBold;
+
+ // === Third Row: Channel Utilization Bluetooth Off (Only If Actually Off) ===
+ const char *chUtil = "ChUtil:";
+ char chUtilPercentage[10];
+ snprintf(chUtilPercentage, sizeof(chUtilPercentage), "%2.0f%%", airTime->channelUtilizationPercent());
+
+ int chUtil_x = (isHighResolution) ? display->getStringWidth(chUtil) + 10 : display->getStringWidth(chUtil) + 5;
+ int chUtil_y = getTextPositions(display)[line] + 3;
+
+ int chutil_bar_width = (isHighResolution) ? 100 : 50;
+ if (!config.bluetooth.enabled) {
+#if defined(USE_EINK)
+ chutil_bar_width = (isHighResolution) ? 50 : 30;
+#else
+ chutil_bar_width = (isHighResolution) ? 80 : 40;
+#endif
+ }
+ int chutil_bar_height = (isHighResolution) ? 12 : 7;
+ int extraoffset = (isHighResolution) ? 6 : 3;
+ if (!config.bluetooth.enabled) {
+ extraoffset = (isHighResolution) ? 6 : 1;
+ }
+ int chutil_percent = airTime->channelUtilizationPercent();
+
+ int centerofscreen = SCREEN_WIDTH / 2;
+ int total_line_content_width = (chUtil_x + chutil_bar_width + display->getStringWidth(chUtilPercentage) + extraoffset) / 2;
+ int starting_position = centerofscreen - total_line_content_width;
+ if (!config.bluetooth.enabled) {
+ starting_position = 0;
+ }
+
+ display->drawString(starting_position, getTextPositions(display)[line], chUtil);
+
+ // Force 56% or higher to show a full 100% bar, text would still show related percent.
+ if (chutil_percent >= 61) {
+ chutil_percent = 100;
+ }
+
+ // Weighting for nonlinear segments
+ float milestone1 = 25;
+ float milestone2 = 40;
+ float weight1 = 0.45; // Weight for 0โ25%
+ float weight2 = 0.35; // Weight for 25โ40%
+ float weight3 = 0.20; // Weight for 40โ100%
+ float totalWeight = weight1 + weight2 + weight3;
+
+ int seg1 = chutil_bar_width * (weight1 / totalWeight);
+ int seg2 = chutil_bar_width * (weight2 / totalWeight);
+ int seg3 = chutil_bar_width * (weight3 / totalWeight);
+
+ int fillRight = 0;
+
+ if (chutil_percent <= milestone1) {
+ fillRight = (seg1 * (chutil_percent / milestone1));
+ } else if (chutil_percent <= milestone2) {
+ fillRight = seg1 + (seg2 * ((chutil_percent - milestone1) / (milestone2 - milestone1)));
+ } else {
+ fillRight = seg1 + seg2 + (seg3 * ((chutil_percent - milestone2) / (100 - milestone2)));
+ }
+
+ // Draw outline
+ display->drawRect(starting_position + chUtil_x, chUtil_y, chutil_bar_width, chutil_bar_height);
+
+ // Fill progress
+ if (fillRight > 0) {
+ display->fillRect(starting_position + chUtil_x, chUtil_y, fillRight, chutil_bar_height);
+ }
+
+ display->drawString(starting_position + chUtil_x + chutil_bar_width + extraoffset, getTextPositions(display)[line],
+ chUtilPercentage);
+
+ if (!config.bluetooth.enabled) {
+ display->drawString(SCREEN_WIDTH - display->getStringWidth("BT off"), getTextPositions(display)[line], "BT off");
+ }
+
+ line += 1;
+
+ // === Fourth & Fifth Rows: Node Identity ===
+ int textWidth = 0;
+ int nameX = 0;
+ int yOffset = (isHighResolution) ? 0 : 5;
+ const char *longName = nullptr;
+ std::string longNameStr;
+
+ meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum());
+ if (ourNode && ourNode->has_user && strlen(ourNode->user.long_name) > 0) {
+ longNameStr = sanitizeString(ourNode->user.long_name);
+ }
+ char shortnameble[35];
+ snprintf(shortnameble, sizeof(shortnameble), "%s",
+ graphics::UIRenderer::haveGlyphs(owner.short_name) ? owner.short_name : "");
+
+ char combinedName[50];
+ snprintf(combinedName, sizeof(combinedName), "%s (%s)", longNameStr.empty() ? "" : longNameStr.c_str(), shortnameble);
+ if (SCREEN_WIDTH - (display->getStringWidth(longName) + display->getStringWidth(shortnameble)) > 10) {
+ size_t len = strlen(combinedName);
+ if (len >= 3 && strcmp(combinedName + len - 3, " ()") == 0) {
+ combinedName[len - 3] = '\0'; // Remove the last three characters
+ }
+ textWidth = display->getStringWidth(combinedName);
+ nameX = (SCREEN_WIDTH - textWidth) / 2;
+ display->drawString(
+ nameX, ((rows == 4) ? getTextPositions(display)[line++] : getTextPositions(display)[line++]) + yOffset, combinedName);
+ } else {
+ // === LongName Centered ===
+ textWidth = display->getStringWidth(longName);
+ nameX = (SCREEN_WIDTH - textWidth) / 2;
+ display->drawString(nameX, getTextPositions(display)[line++], longNameStr.c_str());
+
+ // === ShortName Centered ===
+ textWidth = display->getStringWidth(shortnameble);
+ nameX = (SCREEN_WIDTH - textWidth) / 2;
+ display->drawString(nameX, getTextPositions(display)[line++], shortnameble);
+ }
+}
+
+// Start Functions to write date/time to the screen
+// Helper function to check if a year is a leap year
+bool isLeapYear(int year)
+{
+ return (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0));
+}
+
+// Array of days in each month (non-leap year)
+const int daysInMonth[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+
+// Fills the buffer with a formatted date/time string and returns pixel width
+int UIRenderer::formatDateTime(char *buf, size_t bufSize, uint32_t rtc_sec, OLEDDisplay *display, bool includeTime)
+{
+ int sec = rtc_sec % 60;
+ rtc_sec /= 60;
+ int min = rtc_sec % 60;
+ rtc_sec /= 60;
+ int hour = rtc_sec % 24;
+ rtc_sec /= 24;
+
+ int year = 1970;
+ while (true) {
+ int daysInYear = isLeapYear(year) ? 366 : 365;
+ if (rtc_sec >= (uint32_t)daysInYear) {
+ rtc_sec -= daysInYear;
+ year++;
+ } else {
+ break;
+ }
+ }
+
+ int month = 0;
+ while (month < 12) {
+ int dim = daysInMonth[month];
+ if (month == 1 && isLeapYear(year))
+ dim++;
+ if (rtc_sec >= (uint32_t)dim) {
+ rtc_sec -= dim;
+ month++;
+ } else {
+ break;
+ }
+ }
+
+ int day = rtc_sec + 1;
+
+ if (includeTime) {
+ snprintf(buf, bufSize, "%04d-%02d-%02d %02d:%02d:%02d", year, month + 1, day, hour, min, sec);
+ } else {
+ snprintf(buf, bufSize, "%04d-%02d-%02d", year, month + 1, day);
+ }
+
+ return display->getStringWidth(buf);
+}
+
+// Check if the display can render a string (detect special chars; emoji)
+bool UIRenderer::haveGlyphs(const char *str)
+{
+#if defined(OLED_PL) || defined(OLED_UA) || defined(OLED_RU) || defined(OLED_CS)
+ // Don't want to make any assumptions about custom language support
+ return true;
+#endif
+
+ // Check each character with the lookup function for the OLED library
+ // We're not really meant to use this directly..
+ bool have = true;
+ for (uint16_t i = 0; i < strlen(str); i++) {
+ uint8_t result = Screen::customFontTableLookup((uint8_t)str[i]);
+ // If font doesn't support a character, it is substituted for ยฟ
+ if (result == 191 && (uint8_t)str[i] != 191) {
+ have = false;
+ break;
+ }
+ }
+
+ // LOG_DEBUG("haveGlyphs=%d", have);
+ return have;
+}
+
+#ifdef USE_EINK
+/// Used on eink displays while in deep sleep
+void UIRenderer::drawDeepSleepFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
+{
+
+ // Next frame should use full-refresh, and block while running, else device will sleep before async callback
+ EINK_ADD_FRAMEFLAG(display, COSMETIC);
+ EINK_ADD_FRAMEFLAG(display, BLOCKING);
+
+ LOG_DEBUG("Draw deep sleep screen");
+
+ // Display displayStr on the screen
+ graphics::UIRenderer::drawIconScreen("Sleeping", display, state, x, y);
+}
+
+/// Used on eink displays when screen updates are paused
+void UIRenderer::drawScreensaverOverlay(OLEDDisplay *display, OLEDDisplayUiState *state)
+{
+ LOG_DEBUG("Draw screensaver overlay");
+
+ EINK_ADD_FRAMEFLAG(display, COSMETIC); // Full refresh for screensaver
+
+ // Config
+ display->setFont(FONT_SMALL);
+ display->setTextAlignment(TEXT_ALIGN_LEFT);
+ const char *pauseText = "Screen Paused";
+ const char *idText = owner.short_name;
+ const bool useId = haveGlyphs(idText);
+ constexpr uint8_t padding = 2;
+ constexpr uint8_t dividerGap = 1;
+
+ // Text widths
+ const uint16_t idTextWidth = display->getStringWidth(idText, strlen(idText), true);
+ const uint16_t pauseTextWidth = display->getStringWidth(pauseText, strlen(pauseText));
+ const uint16_t boxWidth = padding + (useId ? idTextWidth + padding : 0) + pauseTextWidth + padding;
+ const uint16_t boxHeight = FONT_HEIGHT_SMALL + (padding * 2);
+
+ // Flush with bottom
+ const int16_t boxLeft = (display->width() / 2) - (boxWidth / 2);
+ const int16_t boxTop = display->height() - boxHeight;
+ const int16_t boxBottom = display->height() - 1;
+ const int16_t idTextLeft = boxLeft + padding;
+ const int16_t idTextTop = boxTop + padding;
+ const int16_t pauseTextLeft = boxLeft + (useId ? idTextWidth + (padding * 2) : 0) + padding;
+ const int16_t pauseTextTop = boxTop + padding;
+ const int16_t dividerX = boxLeft + padding + idTextWidth + padding;
+ const int16_t dividerTop = boxTop + dividerGap;
+ const int16_t dividerBottom = boxBottom - dividerGap;
+
+ // Draw: box
+ display->setColor(EINK_WHITE);
+ display->fillRect(boxLeft, boxTop, boxWidth, boxHeight);
+ display->setColor(EINK_BLACK);
+ display->drawRect(boxLeft, boxTop, boxWidth, boxHeight);
+
+ // Draw: text
+ if (useId)
+ display->drawString(idTextLeft, idTextTop, idText);
+ display->drawString(pauseTextLeft, pauseTextTop, pauseText);
+ display->drawString(pauseTextLeft + 1, pauseTextTop, pauseText); // Faux bold
+
+ // Draw: divider
+ if (useId)
+ display->drawLine(dividerX, dividerTop, dividerX, dividerBottom);
+}
+#endif
+
+/**
+ * Draw the icon with extra info printed around the corners
+ */
+void UIRenderer::drawIconScreen(const char *upperMsg, OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
+{
+ // draw an xbm image.
+ // Please note that everything that should be transitioned
+ // needs to be drawn relative to x and y
+
+ // draw centered icon left to right and centered above the one line of app text
+ display->drawXbm(x + (SCREEN_WIDTH - icon_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - icon_height) / 2 + 2,
+ icon_width, icon_height, icon_bits);
+
+ display->setFont(FONT_MEDIUM);
+ display->setTextAlignment(TEXT_ALIGN_LEFT);
+ const char *title = "meshtastic.org";
+ display->drawString(x + getStringCenteredX(title), y + SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM, title);
+ display->setFont(FONT_SMALL);
+
+ // Draw region in upper left
+ if (upperMsg)
+ display->drawString(x + 0, y + 0, upperMsg);
+
+ // Draw version and short name in upper right
+ char buf[25];
+ snprintf(buf, sizeof(buf), "%s\n%s", xstr(APP_VERSION_SHORT),
+ graphics::UIRenderer::haveGlyphs(owner.short_name) ? owner.short_name : "");
+
+ display->setTextAlignment(TEXT_ALIGN_RIGHT);
+ display->drawString(x + SCREEN_WIDTH, y + 0, buf);
+ screen->forceDisplay();
+
+ display->setTextAlignment(TEXT_ALIGN_LEFT); // Restore left align, just to be kind to any other unsuspecting code
+}
+
+// ****************************
+// * My Position Screen *
+// ****************************
+void UIRenderer::drawCompassAndLocationScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
+{
+ display->clear();
+ display->setTextAlignment(TEXT_ALIGN_LEFT);
+ display->setFont(FONT_SMALL);
+ int line = 1;
+
+ // === Set Title
+ const char *titleStr = "Position";
+
+ // === Header ===
+ graphics::drawCommonHeader(display, x, y, titleStr);
+
+ // === First Row: My Location ===
+#if HAS_GPS
+ bool origBold = config.display.heading_bold;
+ config.display.heading_bold = false;
+
+ const char *displayLine = ""; // Initialize to empty string by default
+ if (config.position.gps_mode != meshtastic_Config_PositionConfig_GpsMode_ENABLED) {
+ if (config.position.fixed_position) {
+ displayLine = "Fixed GPS";
+ } else {
+ displayLine = config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_NOT_PRESENT ? "No GPS" : "GPS off";
+ }
+ int yOffset = (isHighResolution) ? 3 : 1;
+ if (isHighResolution) {
+ NodeListRenderer::drawScaledXBitmap16x16(x, getTextPositions(display)[line] + yOffset - 5, imgSatellite_width,
+ imgSatellite_height, imgSatellite, display);
+ } else {
+ display->drawXbm(x + 1, getTextPositions(display)[line] + yOffset, imgSatellite_width, imgSatellite_height,
+ imgSatellite);
+ }
+ int xOffset = (isHighResolution) ? 6 : 0;
+ display->drawString(x + 11 + xOffset, getTextPositions(display)[line++], displayLine);
+ } else {
+ UIRenderer::drawGps(display, 0, getTextPositions(display)[line++], gpsStatus);
+ }
+
+ config.display.heading_bold = origBold;
+
+ // === Update GeoCoord ===
+ geoCoord.updateCoords(int32_t(gpsStatus->getLatitude()), int32_t(gpsStatus->getLongitude()),
+ int32_t(gpsStatus->getAltitude()));
+
+ // === Determine Compass Heading ===
+ float heading = 0;
+ bool validHeading = false;
+ if (uiconfig.compass_mode == meshtastic_CompassMode_FREEZE_HEADING) {
+ validHeading = true;
+ } else {
+ if (screen->hasHeading()) {
+ heading = radians(screen->getHeading());
+ validHeading = true;
+ } else {
+ heading = screen->estimatedHeading(geoCoord.getLatitude() * 1e-7, geoCoord.getLongitude() * 1e-7);
+ validHeading = !isnan(heading);
+ }
+ }
+
+ // If GPS is off, no need to display these parts
+ if (strcmp(displayLine, "GPS off") != 0 && strcmp(displayLine, "No GPS") != 0) {
+
+ // === Second Row: Date ===
+ uint32_t rtc_sec = getValidTime(RTCQuality::RTCQualityDevice, true);
+ char datetimeStr[25];
+ bool showTime = false; // set to true for full datetime
+ UIRenderer::formatDateTime(datetimeStr, sizeof(datetimeStr), rtc_sec, display, showTime);
+ char fullLine[40];
+ snprintf(fullLine, sizeof(fullLine), " Date: %s", datetimeStr);
+ display->drawString(0, getTextPositions(display)[line++], fullLine);
+
+ // === Third Row: Latitude ===
+ char latStr[32];
+ snprintf(latStr, sizeof(latStr), " Lat: %.5f", geoCoord.getLatitude() * 1e-7);
+ display->drawString(x, getTextPositions(display)[line++], latStr);
+
+ // === Fourth Row: Longitude ===
+ char lonStr[32];
+ snprintf(lonStr, sizeof(lonStr), " Lon: %.5f", geoCoord.getLongitude() * 1e-7);
+ display->drawString(x, getTextPositions(display)[line++], lonStr);
+
+ // === Fifth Row: Altitude ===
+ char DisplayLineTwo[32] = {0};
+ if (config.display.units == meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL) {
+ snprintf(DisplayLineTwo, sizeof(DisplayLineTwo), " Alt: %.0fft", geoCoord.getAltitude() * METERS_TO_FEET);
+ } else {
+ snprintf(DisplayLineTwo, sizeof(DisplayLineTwo), " Alt: %.0im", geoCoord.getAltitude());
+ }
+ display->drawString(x, getTextPositions(display)[line++], DisplayLineTwo);
+ }
+
+ // === Draw Compass if heading is valid ===
+ if (validHeading) {
+ // --- Compass Rendering: landscape (wide) screens use original side-aligned logic ---
+ if (SCREEN_WIDTH > SCREEN_HEIGHT) {
+ const int16_t topY = getTextPositions(display)[1];
+ const int16_t bottomY = SCREEN_HEIGHT - (FONT_HEIGHT_SMALL - 1); // nav row height
+ const int16_t usableHeight = bottomY - topY - 5;
+
+ int16_t compassRadius = usableHeight / 2;
+ if (compassRadius < 8)
+ compassRadius = 8;
+ const int16_t compassDiam = compassRadius * 2;
+ const int16_t compassX = x + SCREEN_WIDTH - compassRadius - 8;
+
+ // Center vertically and nudge down slightly to keep "N" clear of header
+ const int16_t compassY = topY + (usableHeight / 2) + ((FONT_HEIGHT_SMALL - 1) / 2) + 2;
+
+ CompassRenderer::drawNodeHeading(display, compassX, compassY, compassDiam, -heading);
+ display->drawCircle(compassX, compassY, compassRadius);
+
+ // "N" label
+ float northAngle = 0;
+ if (uiconfig.compass_mode != meshtastic_CompassMode_FIXED_RING)
+ northAngle = -heading;
+ float radius = compassRadius;
+ int16_t nX = compassX + (radius - 1) * sin(northAngle);
+ int16_t nY = compassY - (radius - 1) * cos(northAngle);
+ int16_t nLabelWidth = display->getStringWidth("N") + 2;
+ int16_t nLabelHeightBox = FONT_HEIGHT_SMALL + 1;
+
+ display->setColor(BLACK);
+ display->fillRect(nX - nLabelWidth / 2, nY - nLabelHeightBox / 2, nLabelWidth, nLabelHeightBox);
+ display->setColor(WHITE);
+ display->setFont(FONT_SMALL);
+ display->setTextAlignment(TEXT_ALIGN_CENTER);
+ display->drawString(nX, nY - FONT_HEIGHT_SMALL / 2, "N");
+ } else {
+ // Portrait or square: put compass at the bottom and centered, scaled to fit available space
+ // For E-Ink screens, account for navigation bar at the bottom!
+ int yBelowContent = getTextPositions(display)[5] + FONT_HEIGHT_SMALL + 2;
+ const int margin = 4;
+ int availableHeight =
+#if defined(USE_EINK)
+ SCREEN_HEIGHT - yBelowContent - 24; // Leave extra space for nav bar on E-Ink
+#else
+ SCREEN_HEIGHT - yBelowContent - margin;
+#endif
+
+ if (availableHeight < FONT_HEIGHT_SMALL * 2)
+ return;
+
+ int compassRadius = availableHeight / 2;
+ if (compassRadius < 8)
+ compassRadius = 8;
+ if (compassRadius * 2 > SCREEN_WIDTH - 16)
+ compassRadius = (SCREEN_WIDTH - 16) / 2;
+
+ int compassX = x + SCREEN_WIDTH / 2;
+ int compassY = yBelowContent + availableHeight / 2;
+
+ CompassRenderer::drawNodeHeading(display, compassX, compassY, compassRadius * 2, -heading);
+ display->drawCircle(compassX, compassY, compassRadius);
+
+ // "N" label
+ float northAngle = 0;
+ if (uiconfig.compass_mode != meshtastic_CompassMode_FIXED_RING)
+ northAngle = -heading;
+ float radius = compassRadius;
+ int16_t nX = compassX + (radius - 1) * sin(northAngle);
+ int16_t nY = compassY - (radius - 1) * cos(northAngle);
+ int16_t nLabelWidth = display->getStringWidth("N") + 2;
+ int16_t nLabelHeightBox = FONT_HEIGHT_SMALL + 1;
+
+ display->setColor(BLACK);
+ display->fillRect(nX - nLabelWidth / 2, nY - nLabelHeightBox / 2, nLabelWidth, nLabelHeightBox);
+ display->setColor(WHITE);
+ display->setFont(FONT_SMALL);
+ display->setTextAlignment(TEXT_ALIGN_CENTER);
+ display->drawString(nX, nY - FONT_HEIGHT_SMALL / 2, "N");
+ }
+ }
+#endif
+}
+
+#ifdef USERPREFS_OEM_TEXT
+
+void UIRenderer::drawOEMIconScreen(const char *upperMsg, OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
+{
+ static const uint8_t xbm[] = USERPREFS_OEM_IMAGE_DATA;
+ if (isHighResolution) {
+ display->drawXbm(x + (SCREEN_WIDTH - USERPREFS_OEM_IMAGE_WIDTH) / 2,
+ y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - USERPREFS_OEM_IMAGE_HEIGHT) / 2 + 2, USERPREFS_OEM_IMAGE_WIDTH,
+ USERPREFS_OEM_IMAGE_HEIGHT, xbm);
+ } else {
+
+ display->drawXbm(x + (SCREEN_WIDTH - USERPREFS_OEM_IMAGE_WIDTH) / 2,
+ y + (SCREEN_HEIGHT - USERPREFS_OEM_IMAGE_HEIGHT) / 2 + 2, USERPREFS_OEM_IMAGE_WIDTH,
+ USERPREFS_OEM_IMAGE_HEIGHT, xbm);
+ }
+
+ switch (USERPREFS_OEM_FONT_SIZE) {
+ case 0:
+ display->setFont(FONT_SMALL);
+ break;
+ case 2:
+ display->setFont(FONT_LARGE);
+ break;
+ default:
+ display->setFont(FONT_MEDIUM);
+ break;
+ }
+
+ display->setTextAlignment(TEXT_ALIGN_LEFT);
+ const char *title = USERPREFS_OEM_TEXT;
+ if (isHighResolution) {
+ display->drawString(x + getStringCenteredX(title), y + SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM, title);
+ }
+ display->setFont(FONT_SMALL);
+
+ // Draw region in upper left
+ if (upperMsg)
+ display->drawString(x + 0, y + 0, upperMsg);
+
+ // Draw version and shortname in upper right
+ char buf[25];
+ snprintf(buf, sizeof(buf), "%s\n%s", xstr(APP_VERSION_SHORT), haveGlyphs(owner.short_name) ? owner.short_name : "");
+
+ display->setTextAlignment(TEXT_ALIGN_RIGHT);
+ display->drawString(x + SCREEN_WIDTH, y + 0, buf);
+ screen->forceDisplay();
+
+ display->setTextAlignment(TEXT_ALIGN_LEFT); // Restore left align, just to be kind to any other unsuspecting code
+}
+
+void UIRenderer::drawOEMBootScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
+{
+ // Draw region in upper left
+ const char *region = myRegion ? myRegion->name : NULL;
+ drawOEMIconScreen(region, display, state, x, y);
+}
+
+#endif
+
+// Navigation bar overlay implementation
+static int8_t lastFrameIndex = -1;
+static uint32_t lastFrameChangeTime = 0;
+constexpr uint32_t ICON_DISPLAY_DURATION_MS = 2000;
+
+void UIRenderer::drawNavigationBar(OLEDDisplay *display, OLEDDisplayUiState *state)
+{
+ int currentFrame = state->currentFrame;
+
+ // Detect frame change and record time
+ if (currentFrame != lastFrameIndex) {
+ lastFrameIndex = currentFrame;
+ lastFrameChangeTime = millis();
+ }
+
+ const int iconSize = isHighResolution ? 16 : 8;
+ const int spacing = isHighResolution ? 8 : 4;
+ const int bigOffset = isHighResolution ? 1 : 0;
+
+ const size_t totalIcons = screen->indicatorIcons.size();
+ if (totalIcons == 0)
+ return;
+
+ const size_t iconsPerPage = (SCREEN_WIDTH + spacing) / (iconSize + spacing);
+ const size_t currentPage = currentFrame / iconsPerPage;
+ const size_t pageStart = currentPage * iconsPerPage;
+ const size_t pageEnd = min(pageStart + iconsPerPage, totalIcons);
+
+ const int totalWidth = (pageEnd - pageStart) * iconSize + (pageEnd - pageStart - 1) * spacing;
+ const int xStart = (SCREEN_WIDTH - totalWidth) / 2;
+
+ // Only show bar briefly after switching frames
+ static uint32_t navBarLastShown = 0;
+ static bool cosmeticRefreshDone = false;
+
+ bool navBarVisible = millis() - lastFrameChangeTime <= ICON_DISPLAY_DURATION_MS;
+ int y = navBarVisible ? (SCREEN_HEIGHT - iconSize - 1) : SCREEN_HEIGHT;
+
+#if defined(USE_EINK)
+ static bool navBarPrevVisible = false;
+
+ if (navBarVisible && !navBarPrevVisible) {
+ EINK_ADD_FRAMEFLAG(display, DEMAND_FAST); // Fast refresh when showing nav bar
+ cosmeticRefreshDone = false;
+ navBarLastShown = millis();
+ }
+
+ if (!navBarVisible && navBarPrevVisible) {
+ EINK_ADD_FRAMEFLAG(display, DEMAND_FAST); // Fast refresh when hiding nav bar
+ navBarLastShown = millis(); // Mark when it disappeared
+ }
+
+ if (!navBarVisible && navBarLastShown != 0 && !cosmeticRefreshDone) {
+ if (millis() - navBarLastShown > 10000) { // 10s after hidden
+ EINK_ADD_FRAMEFLAG(display, COSMETIC); // One-time ghost cleanup
+ cosmeticRefreshDone = true;
+ }
+ }
+
+ navBarPrevVisible = navBarVisible;
+#endif
+
+ // Pre-calculate bounding rect
+ const int rectX = xStart - 2 - bigOffset;
+ const int rectWidth = totalWidth + 4 + (bigOffset * 2);
+ const int rectHeight = iconSize + 6;
+
+ // Clear background and draw border
+ display->setColor(BLACK);
+ display->fillRect(rectX + 1, y - 2, rectWidth - 2, rectHeight - 2);
+ display->setColor(WHITE);
+ display->drawRect(rectX, y - 2, rectWidth, rectHeight);
+
+ // Icon drawing loop for the current page
+ for (size_t i = pageStart; i < pageEnd; ++i) {
+ const uint8_t *icon = screen->indicatorIcons[i];
+ const int x = xStart + (i - pageStart) * (iconSize + spacing);
+ const bool isActive = (i == static_cast(currentFrame));
+
+ if (isActive) {
+ display->setColor(WHITE);
+ display->fillRect(x - 2, y - 2, iconSize + 4, iconSize + 4);
+ display->setColor(BLACK);
+ }
+
+ if (isHighResolution) {
+ NodeListRenderer::drawScaledXBitmap16x16(x, y, 8, 8, icon, display);
+ } else {
+ display->drawXbm(x, y, iconSize, iconSize, icon);
+ }
+
+ if (isActive) {
+ display->setColor(WHITE);
+ }
+ }
+
+ // Knock the corners off the square
+ display->setColor(BLACK);
+ display->drawRect(rectX, y - 2, 1, 1);
+ display->drawRect(rectX + rectWidth - 1, y - 2, 1, 1);
+ display->setColor(WHITE);
+}
+
+void UIRenderer::drawFrameText(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y, const char *message)
+{
+ uint16_t x_offset = display->width() / 2;
+ display->setTextAlignment(TEXT_ALIGN_CENTER);
+ display->setFont(FONT_MEDIUM);
+ display->drawString(x_offset + x, 26 + y, message);
+}
+
+std::string UIRenderer::drawTimeDelta(uint32_t days, uint32_t hours, uint32_t minutes, uint32_t seconds)
+{
+ std::string uptime;
+
+ if (days > (HOURS_IN_MONTH * 6))
+ uptime = "?";
+ else if (days >= 2)
+ uptime = std::to_string(days) + "d";
+ else if (hours >= 2)
+ uptime = std::to_string(hours) + "h";
+ else if (minutes >= 1)
+ uptime = std::to_string(minutes) + "m";
+ else
+ uptime = std::to_string(seconds) + "s";
+ return uptime;
+}
+
+} // namespace graphics
+
+#endif // HAS_SCREEN
\ No newline at end of file
diff --git a/src/graphics/draw/UIRenderer.h b/src/graphics/draw/UIRenderer.h
new file mode 100644
index 000000000..9e5e8c4b4
--- /dev/null
+++ b/src/graphics/draw/UIRenderer.h
@@ -0,0 +1,88 @@
+#pragma once
+
+#include "graphics/Screen.h"
+#include "graphics/emotes.h"
+#include
+#include
+#include
+
+#define HOURS_IN_MONTH 730
+
+// Forward declarations for status types
+namespace meshtastic
+{
+class PowerStatus;
+class NodeStatus;
+class GPSStatus;
+} // namespace meshtastic
+
+namespace graphics
+{
+
+/// Forward declarations
+class Screen;
+
+/**
+ * @brief UI utility drawing functions
+ *
+ * Contains utility functions for drawing common UI elements, overlays,
+ * battery indicators, and other shared graphical components.
+ */
+class UIRenderer
+{
+ public:
+ // Common UI elements
+ static void drawNodes(OLEDDisplay *display, int16_t x, int16_t y, const meshtastic::NodeStatus *nodeStatus,
+ int node_offset = 0, bool show_total = true, String additional_words = "");
+
+ // GPS status functions
+ static void drawGps(OLEDDisplay *display, int16_t x, int16_t y, const meshtastic::GPSStatus *gpsStatus);
+ static void drawGpsCoordinates(OLEDDisplay *display, int16_t x, int16_t y, const meshtastic::GPSStatus *gpsStatus);
+ static void drawGpsAltitude(OLEDDisplay *display, int16_t x, int16_t y, const meshtastic::GPSStatus *gpsStatus);
+ static void drawGpsPowerStatus(OLEDDisplay *display, int16_t x, int16_t y, const meshtastic::GPSStatus *gpsStatus);
+
+ // Layout and utility functions
+ static void drawScrollbar(OLEDDisplay *display, int visibleItems, int totalItems, int scrollIndex, int x, int startY);
+
+ // Overlay and special screens
+ static void drawFrameText(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y, const char *text);
+
+ // Navigation bar overlay
+ static void drawNavigationBar(OLEDDisplay *display, OLEDDisplayUiState *state);
+
+ static void drawNodeInfo(OLEDDisplay *display, const OLEDDisplayUiState *state, int16_t x, int16_t y);
+
+ static void drawDeviceFocused(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
+
+ // Icon and screen drawing functions
+ static void drawIconScreen(const char *upperMsg, OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
+
+ // Compass and location screen
+ static void drawCompassAndLocationScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
+
+ static NodeNum currentFavoriteNodeNum;
+
+// OEM screens
+#ifdef USERPREFS_OEM_TEXT
+ static void drawOEMIconScreen(const char *upperMsg, OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
+ static void drawOEMBootScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
+#endif
+
+#ifdef USE_EINK
+ /// Used on eink displays while in deep sleep
+ static void drawDeepSleepFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
+
+ /// Used on eink displays when screen updates are paused
+ static void drawScreensaverOverlay(OLEDDisplay *display, OLEDDisplayUiState *state);
+#endif
+
+ static std::string drawTimeDelta(uint32_t days, uint32_t hours, uint32_t minutes, uint32_t seconds);
+ static int formatDateTime(char *buffer, size_t bufferSize, uint32_t rtc_sec, OLEDDisplay *display, bool showTime);
+
+ // Message filtering
+ static bool shouldDrawMessage(const meshtastic_MeshPacket *packet);
+ // Check if the display can render a string (detect special chars; emoji)
+ static bool haveGlyphs(const char *str);
+}; // namespace UIRenderer
+
+} // namespace graphics
diff --git a/src/graphics/emotes.cpp b/src/graphics/emotes.cpp
new file mode 100644
index 000000000..e1a105d20
--- /dev/null
+++ b/src/graphics/emotes.cpp
@@ -0,0 +1,277 @@
+#include "emotes.h"
+
+namespace graphics
+{
+
+// Always define Emote list and count
+const Emote emotes[] = {
+#ifndef EXCLUDE_EMOJI
+ // --- Thumbs ---
+ {"\U0001F44D", thumbup, thumbs_width, thumbs_height}, // ๐ Thumbs Up
+ {"\U0001F44E", thumbdown, thumbs_width, thumbs_height}, // ๐ Thumbs Down
+
+ // --- Smileys (Multiple Unicode Aliases) ---
+ {"\U0001F60A", Smiling_Eyes, Smiling_Eyes_width, Smiling_Eyes_height}, // ๐ Smiling Eyes
+ {"\U0001F600", Grinning, Grinning_width, Grinning_height}, // ๐ Grinning Face
+ {"\U0001F642", Slightly_Smiling, Slightly_Smiling_width, Slightly_Smiling_height}, // ๐ Slightly Smiling Face
+ {"\U0001F609", Winking_Face, Winking_Face_width, Winking_Face_height}, // ๐ Winking Face
+ {"\U0001F601", Grinning_Smiling_Eyes, Grinning_Smiling_Eyes_width, Grinning_Smiling_Eyes_height}, // ๐ Grinning Smiling Eyes
+
+ // --- Question/Alert ---
+ {"\u2753", question, question_width, question_height}, // โ Question Mark
+ {"\u203C\uFE0F", bang, bang_width, bang_height}, // โผ๏ธ Double Exclamation Mark
+
+ // --- Laughing Faces ---
+ {"\U0001F602", haha, haha_width, haha_height}, // ๐ Face with Tears of Joy
+ {"\U0001F923", ROFL, ROFL_width, ROFL_height}, // ๐คฃ Rolling on the Floor Laughing
+ {"\U0001F606", Smiling_Closed_Eyes, Smiling_Closed_Eyes_width, Smiling_Closed_Eyes_height}, // ๐ Smiling Closed Eyes
+ {"\U0001F605", haha, haha_width, haha_height}, // ๐
Smiling with Sweat
+ {"\U0001F604", Grinning_SmilingEyes2, Grinning_SmilingEyes2_width,
+ Grinning_SmilingEyes2_height}, // ๐ Grinning Face with Smiling Eyes
+
+ // --- Gestures and People ---
+ {"\U0001F44B", wave_icon, wave_icon_width, wave_icon_height}, // ๐ Waving Hand
+ {"\U0001F920", cowboy, cowboy_width, cowboy_height}, // ๐ค Cowboy Hat Face
+ {"\U0001F3A7", deadmau5, deadmau5_width, deadmau5_height}, // ๐ง Headphones
+
+ // --- Weather ---
+ {"\u2600", sun, sun_width, sun_height}, // โ Sun (without variation selector)
+ {"\u2600\uFE0F", sun, sun_width, sun_height}, // โ๏ธ Sun (with variation selector)
+ {"\U0001F327\uFE0F", rain, rain_width, rain_height}, // ๐ง๏ธ Cloud with Rain
+ {"\u2601\uFE0F", cloud, cloud_width, cloud_height}, // โ๏ธ Cloud
+ {"\U0001F32B\uFE0F", fog, fog_width, fog_height}, // ๐ซ๏ธ Fog
+
+ // --- Misc Faces ---
+ {"\U0001F608", devil, devil_width, devil_height}, // ๐ Smiling Face with Horns
+
+ // --- Hearts (Multiple Unicode Aliases) ---
+ {"\u2764\uFE0F", heart, heart_width, heart_height}, // โค๏ธ Red Heart
+ {"\U0001F9E1", heart, heart_width, heart_height}, // ๐งก Orange Heart
+ {"\U00002763", heart, heart_width, heart_height}, // โฃ Heart Exclamation
+ {"\U00002764", heart, heart_width, heart_height}, // โค Red Heart (legacy)
+ {"\U0001F495", heart, heart_width, heart_height}, // ๐ Two Hearts
+ {"\U0001F496", heart, heart_width, heart_height}, // ๐ Sparkling Heart
+ {"\U0001F497", heart, heart_width, heart_height}, // ๐ Growing Heart
+ {"\U0001F498", heart, heart_width, heart_height}, // ๐ Heart with Arrow
+
+ // --- Objects ---
+ {"\U0001F4A9", poo, poo_width, poo_height}, // ๐ฉ Pile of Poo
+ {"\U0001F514", bell_icon, bell_icon_width, bell_icon_height} // ๐ Bell
+#endif
+};
+
+const int numEmotes = sizeof(emotes) / sizeof(emotes[0]);
+
+#ifndef EXCLUDE_EMOJI
+const unsigned char thumbup[] PROGMEM = {
+ 0x00, 0x1C, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x80, 0x09, 0x00, 0x00,
+ 0xC0, 0x08, 0x00, 0x00, 0x40, 0x08, 0x00, 0x00, 0x20, 0x04, 0x00, 0x00, 0x10, 0x04, 0x00, 0x00, 0x18, 0x02, 0x00, 0x00,
+ 0x0C, 0xCE, 0x7F, 0x00, 0x04, 0x20, 0x80, 0x00, 0x02, 0x20, 0x80, 0x00, 0x02, 0x60, 0xC0, 0x00, 0x01, 0xF8, 0xFF, 0x01,
+ 0x01, 0x08, 0x00, 0x01, 0x01, 0x08, 0x00, 0x01, 0x01, 0xF8, 0xFF, 0x00, 0x01, 0x10, 0x80, 0x00, 0x01, 0x18, 0x80, 0x00,
+ 0x02, 0x30, 0xC0, 0x00, 0x06, 0xE0, 0x3F, 0x00, 0x0C, 0x20, 0x30, 0x00, 0x38, 0x20, 0x10, 0x00, 0xE0, 0xCF, 0x1F, 0x00,
+};
+
+const unsigned char thumbdown[] PROGMEM = {
+ 0xE0, 0xCF, 0x1F, 0x00, 0x38, 0x20, 0x10, 0x00, 0x0C, 0x20, 0x30, 0x00, 0x06, 0xE0, 0x3F, 0x00, 0x02, 0x30, 0xC0, 0x00,
+ 0x01, 0x18, 0x80, 0x00, 0x01, 0x10, 0x80, 0x00, 0x01, 0xF8, 0xFF, 0x00, 0x01, 0x08, 0x00, 0x01, 0x01, 0x08, 0x00, 0x01,
+ 0x01, 0xF8, 0xFF, 0x01, 0x02, 0x60, 0xC0, 0x00, 0x02, 0x20, 0x80, 0x00, 0x04, 0x20, 0x80, 0x00, 0x0C, 0xCE, 0x7F, 0x00,
+ 0x18, 0x02, 0x00, 0x00, 0x10, 0x04, 0x00, 0x00, 0x20, 0x04, 0x00, 0x00, 0x40, 0x08, 0x00, 0x00, 0xC0, 0x08, 0x00, 0x00,
+ 0x80, 0x09, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00,
+};
+
+const unsigned char Smiling_Eyes[] PROGMEM = {
+ 0x00, 0xf8, 0x03, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0xc0, 0xff, 0xff, 0xc0, 0xe0, 0xff, 0xff, 0xc1,
+ 0xf0, 0xff, 0xff, 0xc3, 0xf8, 0xff, 0xff, 0xc7, 0xf8, 0xff, 0xff, 0xcf, 0xfc, 0xff, 0xff, 0xcf, 0xfc, 0xff, 0xff, 0xcf,
+ 0x7e, 0xf8, 0xc3, 0xdf, 0x3e, 0xf0, 0x81, 0xdf, 0xbf, 0xf7, 0xbd, 0xff, 0xff, 0xff, 0xff, 0xff, 0x9f, 0xff, 0x3f, 0xff,
+ 0x6f, 0xff, 0xdf, 0xfe, 0x6f, 0xff, 0xdf, 0xfe, 0x9f, 0xff, 0x3f, 0xff, 0xfe, 0xff, 0xff, 0xdf, 0x7e, 0xff, 0xdf, 0xdf,
+ 0x7c, 0xff, 0xdf, 0xcf, 0xfc, 0xfe, 0xef, 0xcf, 0xf8, 0xf9, 0xf7, 0xc7, 0xf8, 0x03, 0xf8, 0xc7, 0xf0, 0xff, 0xff, 0xc3,
+ 0xe0, 0xff, 0xff, 0xc1, 0xc0, 0xff, 0xff, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x00, 0xf8, 0x07, 0xc0};
+
+const unsigned char Grinning[] PROGMEM = {
+ 0x00, 0xf8, 0x03, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0xc0, 0xff, 0xff, 0xc0, 0xe0, 0xff, 0xff, 0xc1,
+ 0xf0, 0xff, 0xff, 0xc3, 0xf8, 0xff, 0xff, 0xc7, 0xf8, 0xff, 0xff, 0xcf, 0xfc, 0xf9, 0xf3, 0xcf, 0xfc, 0xf0, 0xe1, 0xcf,
+ 0xfe, 0xf0, 0xe1, 0xdf, 0xfe, 0xf0, 0xe1, 0xdf, 0xff, 0xf0, 0xe1, 0xff, 0xff, 0xf9, 0xf3, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x00, 0x80, 0xff, 0xbe, 0xff, 0xbf, 0xdf, 0x7e, 0x00, 0xc0, 0xdf,
+ 0x7c, 0x00, 0xc0, 0xcf, 0xfc, 0x00, 0xe0, 0xcf, 0xf8, 0x01, 0xf0, 0xc7, 0xf8, 0x03, 0xf8, 0xc7, 0xf0, 0xff, 0xff, 0xc3,
+ 0xe0, 0xff, 0xff, 0xc1, 0xc0, 0xff, 0xff, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x00, 0xf8, 0x03, 0xc0};
+
+const unsigned char Slightly_Smiling[] PROGMEM = {
+ 0x00, 0xf8, 0x03, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0xc0, 0xff, 0xff, 0xc0, 0xe0, 0xff, 0xff, 0xc1,
+ 0xf0, 0xff, 0xff, 0xc3, 0xf8, 0xff, 0xff, 0xc7, 0xf8, 0xff, 0xff, 0xcf, 0xfc, 0xf9, 0xf3, 0xcf, 0xfc, 0xf0, 0xe1, 0xcf,
+ 0xfe, 0xf0, 0xe1, 0xdf, 0xfe, 0xf0, 0xe1, 0xdf, 0xff, 0xf0, 0xe1, 0xff, 0xff, 0xf9, 0xf3, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xdf, 0x7e, 0xff, 0xdf, 0xdf,
+ 0x7c, 0xff, 0xdf, 0xcf, 0xfc, 0xfe, 0xef, 0xcf, 0xf8, 0xf9, 0xf7, 0xc7, 0xf8, 0x03, 0xf8, 0xc7, 0xf0, 0xff, 0xff, 0xc3,
+ 0xe0, 0xff, 0xff, 0xc1, 0xc0, 0xff, 0xff, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x00, 0xf8, 0x03, 0xc0};
+
+const unsigned char Winking_Face[] PROGMEM = {
+ 0x00, 0xf8, 0x03, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0xc0, 0xff, 0xff, 0xc0, 0xe0, 0xff, 0xff, 0xc1,
+ 0xf0, 0xf0, 0xff, 0xc3, 0x78, 0xef, 0xc3, 0xc7, 0xb8, 0xdf, 0xbd, 0xcf, 0xfc, 0xf9, 0x7f, 0xcf, 0xfc, 0xf0, 0xff, 0xcf,
+ 0xfe, 0xf0, 0xc3, 0xdf, 0xfe, 0xf0, 0x81, 0xdf, 0xff, 0xf0, 0xbf, 0xff, 0xff, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xdf, 0x7e, 0xff, 0xdf, 0xdf,
+ 0x7c, 0xff, 0xdf, 0xcf, 0xfc, 0xfe, 0xef, 0xcf, 0xf8, 0xf9, 0xf7, 0xc7, 0xf8, 0x03, 0xf8, 0xc7, 0xf0, 0xff, 0xff, 0xc3,
+ 0xe0, 0xff, 0xff, 0xc1, 0xc0, 0xff, 0xff, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x00, 0xf8, 0x07, 0xc0};
+
+const unsigned char Grinning_Smiling_Eyes[] PROGMEM = {
+ 0x00, 0xf8, 0x03, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0xc0, 0xff, 0xff, 0xc0, 0xe0, 0xff, 0xff, 0xc1,
+ 0xf0, 0xff, 0xff, 0xc3, 0xf8, 0xff, 0xff, 0xc7, 0xf8, 0xff, 0xff, 0xcf, 0xfc, 0xf8, 0xe3, 0xcf, 0x7c, 0xf7, 0xdd, 0xcf,
+ 0xbe, 0xef, 0xbe, 0xdf, 0xbe, 0xef, 0xbe, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x00, 0x00, 0xff, 0x5e, 0x55, 0x55, 0xdf, 0x5e, 0x55, 0x55, 0xdf,
+ 0x3c, 0x00, 0x80, 0xcf, 0x7c, 0x55, 0xd5, 0xcf, 0xf8, 0x54, 0xe5, 0xc7, 0xf8, 0x03, 0xf8, 0xc7, 0xf0, 0xff, 0xff, 0xc3,
+ 0xe0, 0xff, 0xff, 0xc1, 0xc0, 0xff, 0xff, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x00, 0xf8, 0x03, 0xc0};
+
+const unsigned char question[] PROGMEM = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x80, 0xFF, 0x01, 0x00, 0xC0, 0xFF, 0x07, 0x00, 0xE0, 0xFF, 0x07, 0x00,
+ 0xE0, 0xC3, 0x0F, 0x00, 0xF0, 0x81, 0x0F, 0x00, 0xF0, 0x01, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x80, 0x0F, 0x00,
+ 0x00, 0xC0, 0x0F, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0xF8, 0x01, 0x00, 0x00, 0x7C, 0x00, 0x00,
+ 0x00, 0x3C, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x3E, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+const unsigned char bang[] PROGMEM = {
+ 0xFF, 0x0F, 0xFC, 0x3F, 0xFF, 0x0F, 0xFC, 0x3F, 0xFF, 0x0F, 0xFC, 0x3F, 0xFF, 0x07, 0xF8, 0x3F, 0xFF, 0x07, 0xF8, 0x3F,
+ 0xFE, 0x07, 0xF8, 0x1F, 0xFE, 0x07, 0xF8, 0x1F, 0xFE, 0x07, 0xF8, 0x1F, 0xFE, 0x07, 0xF8, 0x1F, 0xFE, 0x07, 0xF8, 0x1F,
+ 0xFE, 0x03, 0xF0, 0x1F, 0xFE, 0x03, 0xF0, 0x1F, 0xFC, 0x03, 0xF0, 0x0F, 0xFC, 0x03, 0xF0, 0x0F, 0xFC, 0x03, 0xF0, 0x0F,
+ 0xFC, 0x03, 0xF0, 0x0F, 0xFC, 0x03, 0xF0, 0x0F, 0xFC, 0x03, 0xF0, 0x0F, 0xFC, 0x01, 0xE0, 0x0F, 0xFC, 0x01, 0xE0, 0x0F,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x00, 0xC0, 0x03, 0xFC, 0x03, 0xF0, 0x0F, 0xFE, 0x03, 0xF0, 0x1F,
+ 0xFE, 0x07, 0xF8, 0x1F, 0xFE, 0x07, 0xF8, 0x1F, 0xFE, 0x07, 0xF8, 0x1F, 0xFC, 0x03, 0xF0, 0x0F, 0xF8, 0x01, 0xE0, 0x07,
+};
+
+const unsigned char haha[] PROGMEM = {
+ 0x00, 0xf8, 0x03, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0xc0, 0xff, 0x7f, 0xc0, 0xe0, 0xf9, 0xf3, 0xc0,
+ 0xf0, 0xfe, 0xef, 0xc1, 0x38, 0xff, 0x9f, 0xc3, 0xd8, 0xff, 0x7f, 0xc3, 0xfc, 0xf8, 0xe3, 0xc7, 0x7c, 0xf7, 0xdd, 0xcf,
+ 0xbe, 0xef, 0xbe, 0xcf, 0xfe, 0xff, 0xff, 0xcf, 0xef, 0xff, 0xff, 0xde, 0xe7, 0xff, 0xff, 0xdc, 0xeb, 0xff, 0xff, 0xda,
+ 0xed, 0xff, 0xff, 0xd6, 0xee, 0xff, 0xff, 0xce, 0x36, 0x00, 0x80, 0xcd, 0xb8, 0xff, 0xbf, 0xc3, 0x7e, 0x00, 0xc0, 0xdf,
+ 0x7c, 0x00, 0xc0, 0xcf, 0xfc, 0x00, 0xe0, 0xcf, 0xf8, 0x01, 0xf0, 0xc7, 0xf8, 0x03, 0xf8, 0xc7, 0xf0, 0xff, 0xff, 0xc3,
+ 0xe0, 0xff, 0xff, 0xc1, 0xc0, 0xff, 0xff, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x00, 0xf8, 0x03, 0xc0};
+
+const unsigned char ROFL[] PROGMEM = {
+ 0x00, 0x00, 0x00, 0xc0, 0x00, 0xfc, 0x07, 0xc0, 0x00, 0xff, 0x1f, 0xc0, 0x80, 0xff, 0x7f, 0xc0, 0xc0, 0xff, 0xff, 0xc0,
+ 0xe0, 0x9f, 0xff, 0xc1, 0xf0, 0x9f, 0xff, 0xc0, 0xf8, 0x9f, 0x7f, 0xcb, 0xf8, 0x9f, 0xbf, 0xcb, 0xfc, 0x9f, 0xdf, 0xdb,
+ 0xfc, 0x1f, 0x08, 0xdc, 0xfe, 0x1f, 0xf8, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0x1e, 0xf0, 0x7f, 0xfe, 0x1e, 0xf0, 0xbf, 0xfe,
+ 0xfe, 0xf3, 0xdf, 0xfe, 0xfe, 0xf3, 0x6f, 0xfe, 0xfe, 0xf3, 0x37, 0xfe, 0xfe, 0xeb, 0x1b, 0xfe, 0xfc, 0xef, 0x0d, 0xde,
+ 0xfc, 0xe7, 0x06, 0xcf, 0xf8, 0x6b, 0x83, 0xcf, 0xf8, 0x0d, 0xc0, 0xc7, 0xf0, 0xed, 0xff, 0xc7, 0xe0, 0xee, 0xff, 0xc3,
+ 0xc0, 0xee, 0xff, 0xc1, 0x80, 0xee, 0xff, 0xc0, 0x00, 0xe6, 0x3f, 0xc0, 0x00, 0xf0, 0x0f, 0xc0, 0x00, 0x00, 0x00, 0xc0};
+
+const unsigned char Smiling_Closed_Eyes[] PROGMEM = {
+ 0x00, 0xf8, 0x03, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0xc0, 0xff, 0xff, 0xc0, 0xe0, 0xff, 0xff, 0xc1,
+ 0xf0, 0xff, 0xff, 0xc3, 0xf8, 0xff, 0xff, 0xc7, 0xf8, 0xff, 0xff, 0xcf, 0x7c, 0xfe, 0xcf, 0xcf, 0xfc, 0xfc, 0xe7, 0xcf,
+ 0xfe, 0xf9, 0xf3, 0xdf, 0xfe, 0xf3, 0xf9, 0xdf, 0xff, 0xf9, 0xf3, 0xff, 0xff, 0xfc, 0xe7, 0xff, 0x7f, 0xfe, 0xcf, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x00, 0x80, 0xff, 0xbe, 0xff, 0xbf, 0xdf, 0x7e, 0x00, 0xc0, 0xdf,
+ 0x7c, 0x00, 0xc0, 0xcf, 0xfc, 0x00, 0xe0, 0xcf, 0xf8, 0x01, 0xf0, 0xc7, 0xf8, 0x03, 0xf8, 0xc7, 0xf0, 0xff, 0xff, 0xc3,
+ 0xe0, 0xff, 0xff, 0xc1, 0xc0, 0xff, 0xff, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x00, 0xf8, 0x03, 0xc0};
+
+const unsigned char Grinning_SmilingEyes2[] PROGMEM = {
+ 0x00, 0xf8, 0x03, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0xc0, 0xff, 0x7f, 0xc0, 0xe0, 0xff, 0xff, 0xc0,
+ 0xf0, 0xff, 0xff, 0xc1, 0xf8, 0xff, 0xff, 0xc3, 0xf8, 0xff, 0xff, 0xc3, 0xfc, 0xf8, 0xe3, 0xc7, 0x7c, 0xf7, 0xdd, 0xc7,
+ 0xbe, 0xef, 0xbe, 0xcf, 0xfe, 0xff, 0xff, 0xcf, 0xff, 0xff, 0xff, 0xdf, 0xff, 0xff, 0xff, 0xdf, 0xff, 0xff, 0xff, 0xdf,
+ 0xff, 0xff, 0xff, 0xdf, 0xff, 0xff, 0xff, 0xdf, 0x3f, 0x00, 0x80, 0xdf, 0xbe, 0xff, 0xbf, 0xcf, 0x7e, 0x00, 0xc0, 0xcf,
+ 0x7c, 0x00, 0xc0, 0xc7, 0xfc, 0x00, 0xe0, 0xc7, 0xf8, 0x01, 0xf0, 0xc3, 0xf8, 0x03, 0xf8, 0xc3, 0xf0, 0xff, 0xff, 0xc1,
+ 0xe0, 0xff, 0xff, 0xc0, 0xc0, 0xff, 0x7f, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x00, 0xf8, 0x03, 0xc0};
+
+const unsigned char wave_icon[] PROGMEM = {
+ 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x7f, 0xc0, 0x00, 0x00, 0xc0, 0xc1, 0x00, 0x00, 0x00, 0xc7,
+ 0x00, 0x00, 0x1e, 0xcc, 0x00, 0x00, 0x30, 0xc8, 0x00, 0x00, 0x60, 0xd8, 0x00, 0x08, 0xc0, 0xd0, 0x00, 0x1a, 0x81, 0xd1,
+ 0x00, 0x36, 0x03, 0xd3, 0x80, 0x6d, 0x06, 0xd2, 0x00, 0xdb, 0x0c, 0xc2, 0x80, 0xb6, 0x1d, 0xc0, 0x80, 0x6d, 0x1f, 0xc0,
+ 0x00, 0xdb, 0x3f, 0xc0, 0x00, 0xf6, 0x7f, 0xc0, 0x00, 0xfc, 0x7f, 0xc0, 0x08, 0xf8, 0x7f, 0xc0, 0x48, 0xf0, 0x7f, 0xc0,
+ 0x48, 0xe0, 0x7f, 0xc0, 0xc8, 0xc0, 0x3f, 0xc0, 0x98, 0x81, 0x1f, 0xc0, 0x10, 0x03, 0x00, 0xc0, 0x30, 0x0e, 0x00, 0xc0,
+ 0x20, 0x38, 0x00, 0xc0, 0xe0, 0x00, 0x00, 0xc0, 0x80, 0x07, 0x00, 0xc0, 0x00, 0x1e, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xc0};
+
+const unsigned char cowboy[] PROGMEM = {
+ 0x00, 0x0c, 0x0c, 0xc0, 0x00, 0x02, 0x10, 0xc0, 0x00, 0x01, 0x20, 0xc0, 0xbc, 0x00, 0x40, 0xcf, 0xc2, 0x01, 0xe0, 0xd0,
+ 0x01, 0x01, 0x20, 0xe0, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xc0,
+ 0xc1, 0x3f, 0xff, 0xe0, 0xe1, 0xff, 0xff, 0xe1, 0xf2, 0xf3, 0xf3, 0xd3, 0xf4, 0xf1, 0xe3, 0xcb, 0xfc, 0xf1, 0xe3, 0xc7,
+ 0xf8, 0xf1, 0xe3, 0xc7, 0xf8, 0xf1, 0xe3, 0xc7, 0xf8, 0xfb, 0xf7, 0xc7, 0xf8, 0xff, 0xff, 0xc7, 0xf8, 0xff, 0xff, 0xc7,
+ 0x70, 0xf8, 0x8f, 0xc3, 0x70, 0x03, 0xb0, 0xc3, 0x70, 0xfe, 0xbf, 0xc3, 0x60, 0x00, 0x80, 0xc1, 0xc0, 0x00, 0xc0, 0xc0,
+ 0x80, 0x01, 0x60, 0xc0, 0x00, 0x07, 0x38, 0xc0, 0x00, 0xfe, 0x1f, 0xc0, 0x00, 0xf0, 0x03, 0xc0, 0x00, 0x00, 0x00, 0xc0};
+
+const unsigned char deadmau5[] PROGMEM = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x07, 0x00,
+ 0x00, 0xFC, 0x03, 0x00, 0x00, 0xFF, 0x3F, 0x00, 0x80, 0xFF, 0x0F, 0x00, 0xC0, 0xFF, 0xFF, 0x00, 0xE0, 0xFF, 0x3F, 0x00,
+ 0xE0, 0xFF, 0xFF, 0x01, 0xF0, 0xFF, 0x7F, 0x00, 0xF0, 0xFF, 0xFF, 0x03, 0xF8, 0xFF, 0xFF, 0x00, 0xF0, 0xFF, 0xFF, 0x07,
+ 0xFC, 0xFF, 0xFF, 0x00, 0xF0, 0xFF, 0xFF, 0x0F, 0xFC, 0xFF, 0xFF, 0x00, 0xF0, 0xFF, 0xFF, 0x0F, 0xFE, 0xFF, 0xFF, 0x00,
+ 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xE0, 0xFF, 0x3F, 0xFC,
+ 0x0F, 0xFF, 0x7F, 0x00, 0xC0, 0xFF, 0x1F, 0xF8, 0x0F, 0xFC, 0x3F, 0x00, 0x80, 0xFF, 0x0F, 0xF8, 0x1F, 0xFC, 0x1F, 0x00,
+ 0x00, 0xFF, 0x0F, 0xFC, 0x3F, 0xFC, 0x0F, 0x00, 0x00, 0xF8, 0x1F, 0xFF, 0xFF, 0xFE, 0x01, 0x00, 0x00, 0x00, 0xFC, 0xFF,
+ 0xFF, 0x07, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00,
+ 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x00, 0xC0, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x07, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+const unsigned char sun[] PROGMEM = {
+ 0x00, 0xC0, 0x00, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x30, 0xC0, 0x00, 0x03,
+ 0x70, 0x00, 0x80, 0x03, 0xF0, 0x00, 0xC0, 0x03, 0xF0, 0xF8, 0xC7, 0x03, 0xE0, 0xFC, 0xCF, 0x01, 0x00, 0xFE, 0x1F, 0x00,
+ 0x00, 0xFF, 0x3F, 0x00, 0x80, 0xFF, 0x7F, 0x00, 0x80, 0xFF, 0x7F, 0x00, 0x8E, 0xFF, 0x7F, 0x1C, 0x9F, 0xFF, 0x7F, 0x3E,
+ 0x9F, 0xFF, 0x7F, 0x3E, 0x8E, 0xFF, 0x7F, 0x1C, 0x80, 0xFF, 0x7F, 0x00, 0x80, 0xFF, 0x7F, 0x00, 0x00, 0xFF, 0x3F, 0x00,
+ 0x00, 0xFE, 0x1F, 0x00, 0x00, 0xFC, 0x0F, 0x00, 0xC0, 0xF9, 0xE7, 0x00, 0xE0, 0x01, 0xE0, 0x01, 0xF0, 0x01, 0xE0, 0x03,
+ 0xF0, 0xC0, 0xC0, 0x03, 0x00, 0xE0, 0x01, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0xC0, 0x00, 0x00,
+};
+
+const unsigned char rain[] PROGMEM = {
+ 0xC0, 0x0F, 0xC0, 0x00, 0x40, 0x00, 0x80, 0x00, 0x20, 0x00, 0x80, 0x00, 0x20, 0x00, 0x80, 0x03, 0x38, 0x00,
+ 0x00, 0x0E, 0x0C, 0x00, 0x00, 0x18, 0x02, 0x00, 0x00, 0x10, 0x03, 0x00, 0x00, 0x30, 0x01, 0x00, 0x00, 0x20,
+ 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x03, 0x00, 0x00, 0x30, 0x02, 0x00,
+ 0x00, 0x10, 0x06, 0x00, 0x00, 0x08, 0xFC, 0xFF, 0xFF, 0x07, 0xF0, 0xFF, 0xFF, 0x01, 0x80, 0x00, 0x01, 0x00,
+ 0xC0, 0xC0, 0x81, 0x03, 0xA0, 0x60, 0xC1, 0x03, 0x90, 0x20, 0x41, 0x01, 0xF0, 0xE0, 0xC0, 0x01, 0x60, 0x4C,
+ 0x98, 0x00, 0x00, 0x0E, 0x1C, 0x00, 0x00, 0x0B, 0x12, 0x00, 0x00, 0x09, 0x1A, 0x00, 0x00, 0x06, 0x0E, 0x00,
+};
+
+const unsigned char cloud[] PROGMEM = {
+ 0x00, 0x80, 0x07, 0x00, 0x00, 0xE0, 0x1F, 0x00, 0x00, 0x70, 0x30, 0x00, 0x00, 0x10, 0x60, 0x00, 0x80, 0x1F, 0x40, 0x00,
+ 0xC0, 0x0F, 0xC0, 0x00, 0xC0, 0x00, 0x80, 0x00, 0x60, 0x00, 0x80, 0x00, 0x20, 0x00, 0x80, 0x00, 0x20, 0x00, 0x80, 0x01,
+ 0x20, 0x00, 0x00, 0x07, 0x38, 0x00, 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x08, 0x06, 0x00, 0x00, 0x18, 0x02, 0x00, 0x00, 0x10,
+ 0x02, 0x00, 0x00, 0x30, 0x03, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20,
+ 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x30, 0x03, 0x00, 0x00, 0x10,
+ 0x02, 0x00, 0x00, 0x10, 0x06, 0x00, 0x00, 0x18, 0x0C, 0x00, 0x00, 0x0C, 0xFC, 0xFF, 0xFF, 0x07, 0xF0, 0xFF, 0xFF, 0x03,
+};
+
+const unsigned char fog[] PROGMEM = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x3C, 0x00, 0xFE, 0x01, 0xFF, 0x00, 0x87, 0xC7, 0xC3, 0x01, 0x03, 0xFE, 0x80, 0x01,
+ 0x00, 0x38, 0x00, 0x00, 0xFC, 0x00, 0x7E, 0x00, 0xFF, 0x83, 0xFF, 0x01, 0x03, 0xFF, 0x81, 0x01, 0x00, 0x7C, 0x00, 0x00,
+ 0xF8, 0x00, 0x3E, 0x00, 0xFE, 0x01, 0xFF, 0x00, 0x87, 0xC7, 0xC3, 0x01, 0x03, 0xFE, 0x80, 0x01, 0x00, 0x38, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+const unsigned char devil[] PROGMEM = {
+ 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0xe0, 0x03, 0x00, 0x00, 0xf0, 0x0f, 0xfc, 0x0f, 0xfc,
+ 0x3f, 0xff, 0x3f, 0xff, 0xfe, 0xff, 0xff, 0xdf, 0xfe, 0xff, 0xff, 0xdf, 0xfe, 0xff, 0xff, 0xdf, 0xfc, 0xff, 0xff, 0xcf,
+ 0xfc, 0xff, 0xff, 0xcf, 0xf8, 0xff, 0xff, 0xc7, 0xf0, 0xff, 0xff, 0xc3, 0xf0, 0xff, 0xff, 0xc3, 0xf0, 0xf1, 0xe3, 0xc3,
+ 0xf0, 0xe7, 0xf9, 0xc3, 0xf0, 0xe7, 0xf9, 0xc3, 0xf0, 0xe3, 0xf1, 0xc3, 0xf0, 0xe3, 0xf1, 0xc3, 0xf0, 0xe7, 0xf9, 0xc3,
+ 0xf0, 0xff, 0xff, 0xc3, 0xe0, 0xfd, 0xef, 0xc1, 0xe0, 0xf3, 0xf3, 0xc1, 0xc0, 0x07, 0xf8, 0xc0, 0x80, 0x1f, 0x7e, 0xc0,
+ 0x00, 0xff, 0x3f, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x00, 0xf8, 0x03, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xc0};
+
+const unsigned char heart[] PROGMEM = {
+ 0x00, 0x00, 0x00, 0x00, 0xC0, 0x03, 0xF0, 0x00, 0xF8, 0x0F, 0xFC, 0x07, 0xFC, 0x1F, 0x06, 0x0E, 0xFE, 0x3F, 0x03, 0x18,
+ 0xFE, 0xFF, 0x7F, 0x10, 0xFF, 0xFF, 0xFF, 0x31, 0xFF, 0xFF, 0xFF, 0x33, 0xFF, 0xFF, 0xFF, 0x37, 0xFF, 0xFF, 0xFF, 0x37,
+ 0xFF, 0xFF, 0xFF, 0x3F, 0xFF, 0xFF, 0xFF, 0x3F, 0xFF, 0xFF, 0xFF, 0x3F, 0xFE, 0xFF, 0xFF, 0x1F, 0xFE, 0xFF, 0xFF, 0x1F,
+ 0xFC, 0xFF, 0xFF, 0x0F, 0xFC, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF, 0x07, 0xF0, 0xFF, 0xFF, 0x03, 0xF0, 0xFF, 0xFF, 0x03,
+ 0xE0, 0xFF, 0xFF, 0x01, 0xC0, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0x7F, 0x00, 0x00, 0xFF, 0x3F, 0x00, 0x00, 0xFE, 0x1F, 0x00,
+ 0x00, 0xFC, 0x0F, 0x00, 0x00, 0xF8, 0x07, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x80, 0x00, 0x00,
+};
+
+const unsigned char poo[] PROGMEM = {
+ 0x00, 0x1c, 0x00, 0xc0, 0x00, 0x7c, 0x00, 0xc0, 0x00, 0xfc, 0x00, 0xc0, 0x00, 0x7c, 0x03, 0xc0, 0x00, 0xbe, 0x03, 0xc0,
+ 0x00, 0xdf, 0x0f, 0xc0, 0x80, 0xcf, 0x0f, 0xc0, 0xc0, 0xf1, 0x0f, 0xc0, 0x60, 0xfc, 0x0f, 0xc0, 0x30, 0xff, 0x07, 0xc0,
+ 0x90, 0xff, 0x3b, 0xc0, 0xc0, 0xff, 0x7d, 0xc0, 0xf8, 0xff, 0xfc, 0xc0, 0xf8, 0x3f, 0xf0, 0xc0, 0x78, 0x88, 0xc0, 0xc0,
+ 0x20, 0xe3, 0x18, 0xc0, 0x98, 0xe7, 0xbc, 0xc1, 0x9c, 0x64, 0xa4, 0xc3, 0x9e, 0x64, 0xa4, 0xc7, 0xbe, 0xe4, 0xa4, 0xc7,
+ 0xbc, 0x27, 0xbc, 0xc7, 0x38, 0x03, 0xd9, 0xc3, 0x00, 0xf0, 0x63, 0xc0, 0xf8, 0xfc, 0x3f, 0xcf, 0xfc, 0xff, 0x87, 0xdf,
+ 0xfe, 0xff, 0xe0, 0xdf, 0xfc, 0x1f, 0xfe, 0xdf, 0xf8, 0x07, 0xf8, 0xcf, 0xf0, 0x03, 0xe0, 0xc7, 0x00, 0x00, 0x00, 0xc0};
+
+const unsigned char bell_icon[] PROGMEM = {
+ 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b11110000,
+ 0b00000011, 0b00000000, 0b00000000, 0b11111100, 0b00001111, 0b00000000, 0b00000000, 0b00001111, 0b00111100, 0b00000000,
+ 0b00000000, 0b00000011, 0b00110000, 0b00000000, 0b10000000, 0b00000001, 0b01100000, 0b00000000, 0b11000000, 0b00000000,
+ 0b11000000, 0b00000000, 0b11000000, 0b00000000, 0b11000000, 0b00000000, 0b11000000, 0b00000000, 0b11000000, 0b00000000,
+ 0b11000000, 0b00000000, 0b11000000, 0b00000000, 0b11000000, 0b00000000, 0b11000000, 0b00000000, 0b11000000, 0b00000000,
+ 0b11000000, 0b00000000, 0b11000000, 0b00000000, 0b11000000, 0b00000000, 0b11000000, 0b00000000, 0b11000000, 0b00000000,
+ 0b11000000, 0b00000000, 0b11000000, 0b00000000, 0b01000000, 0b00000000, 0b10000000, 0b00000000, 0b01100000, 0b00000000,
+ 0b10000000, 0b00000001, 0b01110000, 0b00000000, 0b10000000, 0b00000011, 0b00110000, 0b00000000, 0b00000000, 0b00000011,
+ 0b00011000, 0b00000000, 0b00000000, 0b00000110, 0b11110000, 0b11111111, 0b11111111, 0b00000011, 0b00000000, 0b00001100,
+ 0b00001100, 0b00000000, 0b00000000, 0b00011000, 0b00000110, 0b00000000, 0b00000000, 0b11111000, 0b00000111, 0b00000000,
+ 0b00000000, 0b11100000, 0b00000001, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000,
+ 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000};
+#endif
+
+} // namespace graphics
diff --git a/src/graphics/emotes.h b/src/graphics/emotes.h
new file mode 100644
index 000000000..30b164cbc
--- /dev/null
+++ b/src/graphics/emotes.h
@@ -0,0 +1,114 @@
+#pragma once
+#include
+
+namespace graphics
+{
+
+// === Emote List ===
+struct Emote {
+ const char *label;
+ const unsigned char *bitmap;
+ int width;
+ int height;
+};
+
+extern const Emote emotes[/* numEmotes */];
+extern const int numEmotes;
+
+#ifndef EXCLUDE_EMOJI
+// === Emote Bitmaps ===
+#define thumbs_height 25
+#define thumbs_width 25
+extern const unsigned char thumbup[] PROGMEM;
+extern const unsigned char thumbdown[] PROGMEM;
+
+#define Smiling_Eyes_height 30
+#define Smiling_Eyes_width 30
+extern const unsigned char Smiling_Eyes[] PROGMEM;
+
+#define Grinning_height 30
+#define Grinning_width 30
+extern const unsigned char Grinning[] PROGMEM;
+
+#define Slightly_Smiling_height 30
+#define Slightly_Smiling_width 30
+extern const unsigned char Slightly_Smiling[] PROGMEM;
+
+#define Winking_Face_height 30
+#define Winking_Face_width 30
+extern const unsigned char Winking_Face[] PROGMEM;
+
+#define Grinning_Smiling_Eyes_height 30
+#define Grinning_Smiling_Eyes_width 30
+extern const unsigned char Grinning_Smiling_Eyes[] PROGMEM;
+
+#define question_height 25
+#define question_width 25
+extern const unsigned char question[] PROGMEM;
+
+#define bang_height 30
+#define bang_width 30
+extern const unsigned char bang[] PROGMEM;
+
+#define haha_height 30
+#define haha_width 30
+extern const unsigned char haha[] PROGMEM;
+
+#define ROFL_height 30
+#define ROFL_width 30
+extern const unsigned char ROFL[] PROGMEM;
+
+#define Smiling_Closed_Eyes_height 30
+#define Smiling_Closed_Eyes_width 30
+extern const unsigned char Smiling_Closed_Eyes[] PROGMEM;
+
+#define Grinning_SmilingEyes2_height 30
+#define Grinning_SmilingEyes2_width 30
+extern const unsigned char Grinning_SmilingEyes2[] PROGMEM;
+
+#define wave_icon_height 30
+#define wave_icon_width 30
+extern const unsigned char wave_icon[] PROGMEM;
+
+#define cowboy_height 30
+#define cowboy_width 30
+extern const unsigned char cowboy[] PROGMEM;
+
+#define deadmau5_height 30
+#define deadmau5_width 60
+extern const unsigned char deadmau5[] PROGMEM;
+
+#define sun_height 30
+#define sun_width 30
+extern const unsigned char sun[] PROGMEM;
+
+#define rain_height 30
+#define rain_width 30
+extern const unsigned char rain[] PROGMEM;
+
+#define cloud_height 30
+#define cloud_width 30
+extern const unsigned char cloud[] PROGMEM;
+
+#define fog_height 25
+#define fog_width 25
+extern const unsigned char fog[] PROGMEM;
+
+#define devil_height 30
+#define devil_width 30
+extern const unsigned char devil[] PROGMEM;
+
+#define heart_height 30
+#define heart_width 30
+extern const unsigned char heart[] PROGMEM;
+
+#define poo_height 30
+#define poo_width 30
+extern const unsigned char poo[] PROGMEM;
+
+#define bell_icon_width 30
+#define bell_icon_height 30
+extern const unsigned char bell_icon[] PROGMEM;
+#endif // EXCLUDE_EMOJI
+
+} // namespace graphics
diff --git a/src/graphics/images.h b/src/graphics/images.h
index 069839a16..beef3a1b2 100644
--- a/src/graphics/images.h
+++ b/src/graphics/images.h
@@ -6,19 +6,25 @@ const uint8_t SATELLITE_IMAGE[] PROGMEM = {0x00, 0x08, 0x00, 0x1C, 0x00, 0x0E, 0
0xF8, 0x00, 0xF0, 0x01, 0xE0, 0x03, 0xC8, 0x01, 0x9C, 0x54,
0x0E, 0x52, 0x07, 0x48, 0x02, 0x26, 0x00, 0x10, 0x00, 0x0E};
-const uint8_t imgSatellite[] PROGMEM = {0x70, 0x71, 0x22, 0xFA, 0xFA, 0x22, 0x71, 0x70};
-const uint8_t imgUSB[] PROGMEM = {0x60, 0x60, 0x30, 0x18, 0x18, 0x18, 0x24, 0x42, 0x42, 0x42, 0x42, 0x7E, 0x24, 0x24, 0x24, 0x3C};
+#define imgSatellite_width 8
+#define imgSatellite_height 8
+const uint8_t imgSatellite[] PROGMEM = {
+ 0b00000000, 0b00000000, 0b00000000, 0b00011000, 0b11011011, 0b11111111, 0b11011011, 0b00011000,
+};
+
+const uint8_t imgUSB[] PROGMEM = {0x00, 0xfc, 0xf0, 0xfc, 0x88, 0xff, 0x86, 0xfe, 0x85, 0xfe, 0x89, 0xff, 0xf1, 0xfc, 0x00, 0xfc};
+const uint8_t imgUSB_HighResolution[] PROGMEM = {0x00, 0x3e, 0xf8, 0x80, 0x43, 0xf8, 0xc0, 0xc2, 0xff, 0x60, 0x42, 0xfc,
+ 0x3c, 0xc2, 0xff, 0x22, 0x42, 0xf8, 0x3d, 0x42, 0xf8, 0x22, 0xc2, 0xff,
+ 0x61, 0x42, 0xfc, 0xc0, 0xc2, 0xff, 0x80, 0x43, 0xf8, 0x00, 0x3e, 0xf8};
const uint8_t imgPower[] PROGMEM = {0x40, 0x40, 0x40, 0x58, 0x48, 0x08, 0x08, 0x08,
0x1C, 0x22, 0x22, 0x41, 0x7F, 0x22, 0x22, 0x22};
const uint8_t imgUser[] PROGMEM = {0x3C, 0x42, 0x99, 0xA5, 0xA5, 0x99, 0x42, 0x3C};
const uint8_t imgPositionEmpty[] PROGMEM = {0x20, 0x30, 0x28, 0x24, 0x42, 0xFF};
const uint8_t imgPositionSolid[] PROGMEM = {0x20, 0x30, 0x38, 0x3C, 0x7E, 0xFF};
-#if defined(DISPLAY_CLOCK_FRAME)
const uint8_t bluetoothConnectedIcon[36] PROGMEM = {0xfe, 0x01, 0xff, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00, 0xe3, 0x1f,
0xf3, 0x3f, 0x33, 0x30, 0x33, 0x33, 0x33, 0x33, 0x03, 0x33, 0xff, 0x33,
0xfe, 0x31, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0xf0, 0x3f, 0xe0, 0x1f};
-#endif
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \
defined(ST7789_CS) || defined(USE_ST7789) || defined(HX8357_CS) || defined(ILI9488_CS) || ARCH_PORTDUINO) && \
@@ -37,181 +43,249 @@ const uint8_t imgQuestion[] PROGMEM = {0xbf, 0x41, 0xc0, 0x8b, 0xdb, 0x70, 0xa1,
const uint8_t imgSF[] PROGMEM = {0xd2, 0xb7, 0xad, 0xbb, 0x92, 0x01, 0xfd, 0xfd, 0x15, 0x85, 0xf5};
#endif
-#ifndef EXCLUDE_EMOJI
-#define thumbs_height 25
-#define thumbs_width 25
-static unsigned char thumbup[] PROGMEM = {
- 0x00, 0x1C, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x80, 0x09, 0x00, 0x00,
- 0xC0, 0x08, 0x00, 0x00, 0x40, 0x08, 0x00, 0x00, 0x20, 0x04, 0x00, 0x00, 0x10, 0x04, 0x00, 0x00, 0x18, 0x02, 0x00, 0x00,
- 0x0C, 0xCE, 0x7F, 0x00, 0x04, 0x20, 0x80, 0x00, 0x02, 0x20, 0x80, 0x00, 0x02, 0x60, 0xC0, 0x00, 0x01, 0xF8, 0xFF, 0x01,
- 0x01, 0x08, 0x00, 0x01, 0x01, 0x08, 0x00, 0x01, 0x01, 0xF8, 0xFF, 0x00, 0x01, 0x10, 0x80, 0x00, 0x01, 0x18, 0x80, 0x00,
- 0x02, 0x30, 0xC0, 0x00, 0x06, 0xE0, 0x3F, 0x00, 0x0C, 0x20, 0x30, 0x00, 0x38, 0x20, 0x10, 0x00, 0xE0, 0xCF, 0x1F, 0x00,
+// === Horizontal battery ===
+// Basic battery design and all related pieces
+const unsigned char batteryBitmap_h_bottom[] PROGMEM = {
+ 0b00011110, 0b00000000, 0b00000001, 0b00000000, 0b00000001, 0b00000000, 0b00000001, 0b00000000, 0b00000001,
+ 0b00000000, 0b00000001, 0b00000000, 0b00000001, 0b00000000, 0b00000001, 0b00000000, 0b00000001, 0b00000000,
+ 0b00000001, 0b00000000, 0b00000001, 0b00000000, 0b00000001, 0b00000000, 0b00011110, 0b00000000};
+
+const unsigned char batteryBitmap_h_top[] PROGMEM = {
+ 0b00111100, 0b00000000, 0b01000000, 0b00000000, 0b01000000, 0b00000000, 0b01000000, 0b00000000, 0b01000000,
+ 0b00000000, 0b11000000, 0b00000000, 0b11000000, 0b00000000, 0b11000000, 0b00000000, 0b01000000, 0b00000000,
+ 0b01000000, 0b00000000, 0b01000000, 0b00000000, 0b01000000, 0b00000000, 0b00111100, 0b00000000};
+
+// Lightning Bolt
+const unsigned char lightning_bolt_h[] PROGMEM = {
+ 0b00000000, 0b00000000, 0b00100000, 0b00000000, 0b00110000, 0b00000000, 0b00111000, 0b00000000, 0b00111100,
+ 0b00000000, 0b00011110, 0b00000000, 0b11111111, 0b00000000, 0b01111000, 0b00000000, 0b00111100, 0b00000000,
+ 0b00011100, 0b00000000, 0b00001100, 0b00000000, 0b00000100, 0b00000000, 0b00000000, 0b00000000};
+
+// === Vertical battery ===
+// Basic battery design and all related pieces
+const unsigned char batteryBitmap_v[] PROGMEM = {0b00011100, 0b00111110, 0b01000001, 0b01000001, 0b00000000, 0b00000000,
+ 0b00000000, 0b01000001, 0b01000001, 0b01000001, 0b00111110};
+// This is the left and right bars for the fill in
+const unsigned char batteryBitmap_sidegaps_v[] PROGMEM = {0b10000010, 0b10000010, 0b10000010};
+// Lightning Bolt
+const unsigned char lightning_bolt_v[] PROGMEM = {0b00000100, 0b00000110, 0b00011111, 0b00001100, 0b00000100};
+
+#define mail_width 10
+#define mail_height 7
+static const unsigned char mail[] PROGMEM = {
+ 0b11111111, 0b00, // Top line
+ 0b10000001, 0b00, // Edges
+ 0b11000011, 0b00, // Diagonals start
+ 0b10100101, 0b00, // Inner M part
+ 0b10011001, 0b00, // Inner M part
+ 0b10000001, 0b00, // Edges
+ 0b11111111, 0b00 // Bottom line
};
-static unsigned char thumbdown[] PROGMEM = {
- 0xE0, 0xCF, 0x1F, 0x00, 0x38, 0x20, 0x10, 0x00, 0x0C, 0x20, 0x30, 0x00, 0x06, 0xE0, 0x3F, 0x00, 0x02, 0x30, 0xC0, 0x00,
- 0x01, 0x18, 0x80, 0x00, 0x01, 0x10, 0x80, 0x00, 0x01, 0xF8, 0xFF, 0x00, 0x01, 0x08, 0x00, 0x01, 0x01, 0x08, 0x00, 0x01,
- 0x01, 0xF8, 0xFF, 0x01, 0x02, 0x60, 0xC0, 0x00, 0x02, 0x20, 0x80, 0x00, 0x04, 0x20, 0x80, 0x00, 0x0C, 0xCE, 0x7F, 0x00,
- 0x18, 0x02, 0x00, 0x00, 0x10, 0x04, 0x00, 0x00, 0x20, 0x04, 0x00, 0x00, 0x40, 0x08, 0x00, 0x00, 0xC0, 0x08, 0x00, 0x00,
- 0x80, 0x09, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00,
+// ๐ฌ Mail / Message
+const uint8_t icon_mail[] PROGMEM = {
+ 0b11111111, // โโโโโโโโ top border
+ 0b10000001, // โ โ sides
+ 0b11000011, // โโ โโ diagonal
+ 0b10100101, // โ โ โ โ inner M
+ 0b10011001, // โ โโ โ inner M
+ 0b10000001, // โ โ sides
+ 0b10000001, // โ โ sides
+ 0b11111111 // โโโโโโโโ bottom
};
-#define smiley_height 30
-#define smiley_width 30
-static unsigned char smiley[] PROGMEM = {
- 0x00, 0xfe, 0x0f, 0x00, 0x80, 0x01, 0x30, 0x00, 0x40, 0x00, 0xc0, 0x00, 0x20, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x02,
- 0x08, 0x00, 0x00, 0x04, 0x04, 0x00, 0x00, 0x08, 0x04, 0x00, 0x00, 0x10, 0x02, 0x0e, 0x0e, 0x10, 0x02, 0x09, 0x12, 0x10,
- 0x01, 0x09, 0x12, 0x20, 0x01, 0x0f, 0x1e, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20,
- 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x81, 0x00, 0x20, 0x20,
- 0x82, 0x00, 0x20, 0x10, 0x02, 0x01, 0x10, 0x10, 0x04, 0x02, 0x08, 0x08, 0x04, 0xfc, 0x07, 0x08, 0x08, 0x00, 0x00, 0x04,
- 0x10, 0x00, 0x00, 0x02, 0x20, 0x00, 0x00, 0x01, 0x40, 0x00, 0xc0, 0x00, 0x80, 0x01, 0x30, 0x00, 0x00, 0xfe, 0x0f, 0x00};
-
-#define question_height 25
-#define question_width 25
-static unsigned char question[] PROGMEM = {
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x80, 0xFF, 0x01, 0x00, 0xC0, 0xFF, 0x07, 0x00, 0xE0, 0xFF, 0x07, 0x00,
- 0xE0, 0xC3, 0x0F, 0x00, 0xF0, 0x81, 0x0F, 0x00, 0xF0, 0x01, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x80, 0x0F, 0x00,
- 0x00, 0xC0, 0x0F, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0xF8, 0x01, 0x00, 0x00, 0x7C, 0x00, 0x00,
- 0x00, 0x3C, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x3E, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+// ๐ GPS Screen / Location Pin
+const unsigned char icon_compass[] PROGMEM = {
+ 0x3C, // Row 0: ..####..
+ 0x52, // Row 1: .#..#.#.
+ 0x91, // Row 2: #...#..#
+ 0x91, // Row 3: #...#..#
+ 0x91, // Row 4: #...#..#
+ 0x81, // Row 5: #......#
+ 0x42, // Row 6: .#....#.
+ 0x3C // Row 7: ..####..
};
-#define bang_height 30
-#define bang_width 30
-static unsigned char bang[] PROGMEM = {
- 0xFF, 0x0F, 0xFC, 0x3F, 0xFF, 0x0F, 0xFC, 0x3F, 0xFF, 0x0F, 0xFC, 0x3F, 0xFF, 0x07, 0xF8, 0x3F, 0xFF, 0x07, 0xF8, 0x3F,
- 0xFE, 0x07, 0xF8, 0x1F, 0xFE, 0x07, 0xF8, 0x1F, 0xFE, 0x07, 0xF8, 0x1F, 0xFE, 0x07, 0xF8, 0x1F, 0xFE, 0x07, 0xF8, 0x1F,
- 0xFE, 0x03, 0xF0, 0x1F, 0xFE, 0x03, 0xF0, 0x1F, 0xFC, 0x03, 0xF0, 0x0F, 0xFC, 0x03, 0xF0, 0x0F, 0xFC, 0x03, 0xF0, 0x0F,
- 0xFC, 0x03, 0xF0, 0x0F, 0xFC, 0x03, 0xF0, 0x0F, 0xFC, 0x03, 0xF0, 0x0F, 0xFC, 0x01, 0xE0, 0x0F, 0xFC, 0x01, 0xE0, 0x0F,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x00, 0xC0, 0x03, 0xFC, 0x03, 0xF0, 0x0F, 0xFE, 0x03, 0xF0, 0x1F,
- 0xFE, 0x07, 0xF8, 0x1F, 0xFE, 0x07, 0xF8, 0x1F, 0xFE, 0x07, 0xF8, 0x1F, 0xFC, 0x03, 0xF0, 0x0F, 0xF8, 0x01, 0xE0, 0x07,
+const uint8_t icon_radio[] PROGMEM = {
+ 0x0F, // Row 0: ####....
+ 0x10, // Row 1: ....#...
+ 0x27, // Row 2: ###..#..
+ 0x48, // Row 3: ...#..#.
+ 0x93, // Row 4: ##..#..#
+ 0xA4, // Row 5: ..#..#.#
+ 0xA8, // Row 6: ...#.#.#
+ 0xA9 // Row 7: #..#.#.#
};
-#define haha_height 30
-#define haha_width 30
-static unsigned char haha[] PROGMEM = {
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x01, 0x00,
- 0x00, 0xFC, 0x0F, 0x00, 0x00, 0x1F, 0x3E, 0x00, 0x80, 0x03, 0x70, 0x00, 0xC0, 0x01, 0xE0, 0x00, 0xC0, 0x00, 0xC2, 0x00,
- 0x60, 0x00, 0x03, 0x00, 0x60, 0x00, 0xC1, 0x1F, 0x60, 0x80, 0x8F, 0x31, 0x30, 0x0E, 0x80, 0x31, 0x30, 0x10, 0x30, 0x1F,
- 0x30, 0x08, 0x58, 0x00, 0x30, 0x04, 0x6C, 0x03, 0x60, 0x00, 0xF3, 0x01, 0x60, 0xC0, 0xFC, 0x01, 0x80, 0x38, 0xBF, 0x01,
- 0xE0, 0xC5, 0xDF, 0x00, 0xB0, 0xF9, 0xEF, 0x00, 0x30, 0xF1, 0x73, 0x00, 0xB0, 0x1D, 0x3E, 0x00, 0xF0, 0xFD, 0x0F, 0x00,
- 0xE0, 0xE0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+// ๐ช Memory Icon
+const uint8_t icon_memory[] PROGMEM = {
+ 0x24, // Row 0: ..#..#..
+ 0x3C, // Row 1: ..####..
+ 0xC3, // Row 2: ##....##
+ 0x5A, // Row 3: .#.##.#.
+ 0x5A, // Row 4: .#.##.#.
+ 0xC3, // Row 5: ##....##
+ 0x3C, // Row 6: ..####..
+ 0x24 // Row 7: ..#..#..
};
-#define wave_icon_height 30
-#define wave_icon_width 30
-static unsigned char wave_icon[] PROGMEM = {
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0xC0, 0x00,
- 0x00, 0x0C, 0x9C, 0x01, 0x80, 0x17, 0x20, 0x01, 0x80, 0x26, 0x46, 0x02, 0x80, 0x44, 0x88, 0x02, 0xC0, 0x89, 0x8A, 0x02,
- 0x40, 0x93, 0x8B, 0x02, 0x40, 0x26, 0x13, 0x00, 0x80, 0x44, 0x16, 0x00, 0xC0, 0x89, 0x24, 0x00, 0x40, 0x93, 0x60, 0x00,
- 0x40, 0x26, 0x40, 0x00, 0x80, 0x0C, 0x80, 0x00, 0x00, 0x09, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x40, 0x06, 0x80, 0x00,
- 0x50, 0x0C, 0x80, 0x00, 0x50, 0x08, 0x40, 0x00, 0x90, 0x10, 0x20, 0x00, 0xB0, 0x21, 0x10, 0x00, 0x20, 0x47, 0x18, 0x00,
- 0x40, 0x80, 0x0F, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+// ๐ Wi-Fi
+const uint8_t icon_wifi[] PROGMEM = {0b00000000, 0b00011000, 0b00111100, 0b01111110,
+ 0b11011011, 0b00011000, 0b00011000, 0b00000000};
+
+const uint8_t icon_nodes[] PROGMEM = {
+ 0xF9, // Row 0 #..#######
+ 0x00, // Row 1
+ 0xF9, // Row 2 #..#######
+ 0x00, // Row 3
+ 0xF9, // Row 4 #..#######
+ 0x00, // Row 5
+ 0xF9, // Row 6 #..#######
+ 0x00 // Row 7
};
-#define cowboy_height 30
-#define cowboy_width 30
-static unsigned char cowboy[] PROGMEM = {
- 0x00, 0xF0, 0x03, 0x00, 0x00, 0xFC, 0x0F, 0x00, 0x00, 0xFE, 0x1F, 0x00, 0x00, 0xFF, 0x3F, 0x00, 0x3C, 0xFE, 0x1F, 0x0F,
- 0xFE, 0xFE, 0xDF, 0x1F, 0xFF, 0xFF, 0xFF, 0x3F, 0xFF, 0xFF, 0xFF, 0x3F, 0xFF, 0xFF, 0xFF, 0x3F, 0xFF, 0xFF, 0xFF, 0x3F,
- 0x3E, 0xC0, 0x00, 0x1F, 0x1E, 0x00, 0x00, 0x1E, 0x0C, 0x0C, 0x0C, 0x0C, 0x08, 0x0E, 0x1C, 0x04, 0x00, 0x0E, 0x1C, 0x00,
- 0x04, 0x0E, 0x1C, 0x08, 0x04, 0x0E, 0x1C, 0x08, 0x04, 0x04, 0x08, 0x08, 0x04, 0x00, 0x00, 0x08, 0x04, 0x00, 0x00, 0x08,
- 0x8C, 0x07, 0x70, 0x0C, 0x88, 0xFC, 0x4F, 0x04, 0x88, 0x01, 0x40, 0x04, 0x90, 0xFF, 0x7F, 0x02, 0x30, 0x03, 0x30, 0x03,
- 0x60, 0x0E, 0x9C, 0x01, 0xC0, 0xF8, 0xC7, 0x00, 0x80, 0x01, 0x60, 0x00, 0x00, 0x0E, 0x1C, 0x00, 0x00, 0xF8, 0x07, 0x00,
+// โค Chevron Triangle Arrow Icon (8x8)
+const uint8_t icon_list[] PROGMEM = {
+ 0x10, // Row 0: ...#....
+ 0x10, // Row 1: ...#....
+ 0x38, // Row 2: ..###...
+ 0x38, // Row 3: ..###...
+ 0x7C, // Row 4: .#####..
+ 0x6C, // Row 5: .##.##..
+ 0xC6, // Row 6: ##...##.
+ 0x82 // Row 7: #.....#.
};
-#define deadmau5_height 30
-#define deadmau5_width 60
-static unsigned char deadmau5[] PROGMEM = {
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x07, 0x00,
- 0x00, 0xFC, 0x03, 0x00, 0x00, 0xFF, 0x3F, 0x00, 0x80, 0xFF, 0x0F, 0x00, 0xC0, 0xFF, 0xFF, 0x00, 0xE0, 0xFF, 0x3F, 0x00,
- 0xE0, 0xFF, 0xFF, 0x01, 0xF0, 0xFF, 0x7F, 0x00, 0xF0, 0xFF, 0xFF, 0x03, 0xF8, 0xFF, 0xFF, 0x00, 0xF0, 0xFF, 0xFF, 0x07,
- 0xFC, 0xFF, 0xFF, 0x00, 0xF0, 0xFF, 0xFF, 0x0F, 0xFC, 0xFF, 0xFF, 0x00, 0xF0, 0xFF, 0xFF, 0x0F, 0xFE, 0xFF, 0xFF, 0x00,
- 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xE0, 0xFF, 0x3F, 0xFC,
- 0x0F, 0xFF, 0x7F, 0x00, 0xC0, 0xFF, 0x1F, 0xF8, 0x0F, 0xFC, 0x3F, 0x00, 0x80, 0xFF, 0x0F, 0xF8, 0x1F, 0xFC, 0x1F, 0x00,
- 0x00, 0xFF, 0x0F, 0xFC, 0x3F, 0xFC, 0x0F, 0x00, 0x00, 0xF8, 0x1F, 0xFF, 0xFF, 0xFE, 0x01, 0x00, 0x00, 0x00, 0xFC, 0xFF,
- 0xFF, 0x07, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00,
- 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x00, 0xC0, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x80, 0x07, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+// ๐ถ Signal Bars Icon (left to right, small to large with spacing)
+const uint8_t icon_signal[] PROGMEM = {
+ 0b00000000, // โโโโโโโ
+ 0b10000000, // โโโโโโโ
+ 0b10100000, // โโโโโโโ
+ 0b10100000, // โโโโโโโ
+ 0b10101000, // โโโโโโโ
+ 0b10101000, // โโโโโโโ
+ 0b10101010, // โโโโโโโ
+ 0b11111111 // โโโโโโโ
};
-#define sun_width 30
-#define sun_height 30
-static unsigned char sun[] PROGMEM = {
- 0x00, 0xC0, 0x00, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x30, 0xC0, 0x00, 0x03,
- 0x70, 0x00, 0x80, 0x03, 0xF0, 0x00, 0xC0, 0x03, 0xF0, 0xF8, 0xC7, 0x03, 0xE0, 0xFC, 0xCF, 0x01, 0x00, 0xFE, 0x1F, 0x00,
- 0x00, 0xFF, 0x3F, 0x00, 0x80, 0xFF, 0x7F, 0x00, 0x80, 0xFF, 0x7F, 0x00, 0x8E, 0xFF, 0x7F, 0x1C, 0x9F, 0xFF, 0x7F, 0x3E,
- 0x9F, 0xFF, 0x7F, 0x3E, 0x8E, 0xFF, 0x7F, 0x1C, 0x80, 0xFF, 0x7F, 0x00, 0x80, 0xFF, 0x7F, 0x00, 0x00, 0xFF, 0x3F, 0x00,
- 0x00, 0xFE, 0x1F, 0x00, 0x00, 0xFC, 0x0F, 0x00, 0xC0, 0xF9, 0xE7, 0x00, 0xE0, 0x01, 0xE0, 0x01, 0xF0, 0x01, 0xE0, 0x03,
- 0xF0, 0xC0, 0xC0, 0x03, 0x00, 0xE0, 0x01, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0xC0, 0x00, 0x00,
+// โ๏ธ Distance / Measurement Icon (double-ended arrow)
+const uint8_t icon_distance[] PROGMEM = {
+ 0b00000000, // โโโโโโโโ
+ 0b10000001, // โโโโโโโ arrowheads
+ 0b01000010, // โโโโโโโ
+ 0b00100100, // โโโโโโโ
+ 0b00011000, // โโโโโโโ center
+ 0b00100100, // โโโโโโโ
+ 0b01000010, // โโโโโโโ
+ 0b10000001 // โโโโโโโ
};
-#define rain_width 30
-#define rain_height 30
-static unsigned char rain[] PROGMEM = {
- 0xC0, 0x0F, 0xC0, 0x00, 0x40, 0x00, 0x80, 0x00, 0x20, 0x00, 0x80, 0x00, 0x20, 0x00, 0x80, 0x03, 0x38, 0x00,
- 0x00, 0x0E, 0x0C, 0x00, 0x00, 0x18, 0x02, 0x00, 0x00, 0x10, 0x03, 0x00, 0x00, 0x30, 0x01, 0x00, 0x00, 0x20,
- 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x03, 0x00, 0x00, 0x30, 0x02, 0x00,
- 0x00, 0x10, 0x06, 0x00, 0x00, 0x08, 0xFC, 0xFF, 0xFF, 0x07, 0xF0, 0xFF, 0xFF, 0x01, 0x80, 0x00, 0x01, 0x00,
- 0xC0, 0xC0, 0x81, 0x03, 0xA0, 0x60, 0xC1, 0x03, 0x90, 0x20, 0x41, 0x01, 0xF0, 0xE0, 0xC0, 0x01, 0x60, 0x4C,
- 0x98, 0x00, 0x00, 0x0E, 0x1C, 0x00, 0x00, 0x0B, 0x12, 0x00, 0x00, 0x09, 0x1A, 0x00, 0x00, 0x06, 0x0E, 0x00,
+// โ ๏ธ Error / Fault
+const uint8_t icon_error[] PROGMEM = {
+ 0b00011000, // โโโโโโโโ
+ 0b00011000, // โโโโโโโโ
+ 0b00011000, // โโโโโโโโ
+ 0b00011000, // โโโโโโโโ
+ 0b00000000, // โโโโโโโโ
+ 0b00011000, // โโโโโโโโ
+ 0b00000000, // โโโโโโโโ
+ 0b00000000 // โโโโโโโโ
};
-#define cloud_height 30
-#define cloud_width 30
-static unsigned char cloud[] PROGMEM = {
- 0x00, 0x80, 0x07, 0x00, 0x00, 0xE0, 0x1F, 0x00, 0x00, 0x70, 0x30, 0x00, 0x00, 0x10, 0x60, 0x00, 0x80, 0x1F, 0x40, 0x00,
- 0xC0, 0x0F, 0xC0, 0x00, 0xC0, 0x00, 0x80, 0x00, 0x60, 0x00, 0x80, 0x00, 0x20, 0x00, 0x80, 0x00, 0x20, 0x00, 0x80, 0x01,
- 0x20, 0x00, 0x00, 0x07, 0x38, 0x00, 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x08, 0x06, 0x00, 0x00, 0x18, 0x02, 0x00, 0x00, 0x10,
- 0x02, 0x00, 0x00, 0x30, 0x03, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20,
- 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x30, 0x03, 0x00, 0x00, 0x10,
- 0x02, 0x00, 0x00, 0x10, 0x06, 0x00, 0x00, 0x18, 0x0C, 0x00, 0x00, 0x0C, 0xFC, 0xFF, 0xFF, 0x07, 0xF0, 0xFF, 0xFF, 0x03,
+// ๐ Optimized Home Icon (8x8)
+const uint8_t icon_home[] PROGMEM = {
+ 0b00011000, // โโ
+ 0b00111100, // โโโโ
+ 0b01111110, // โโโโโโ
+ 0b11111111, // โโโโโโโ
+ 0b11000011, // โโ โโ
+ 0b11011011, // โโ โโ โโ
+ 0b11011011, // โโ โโ โโ
+ 0b11111111 // โโโโโโโ
};
-#define fog_height 25
-#define fog_width 25
-static unsigned char fog[] PROGMEM = {
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x3C, 0x00, 0xFE, 0x01, 0xFF, 0x00, 0x87, 0xC7, 0xC3, 0x01, 0x03, 0xFE, 0x80, 0x01,
- 0x00, 0x38, 0x00, 0x00, 0xFC, 0x00, 0x7E, 0x00, 0xFF, 0x83, 0xFF, 0x01, 0x03, 0xFF, 0x81, 0x01, 0x00, 0x7C, 0x00, 0x00,
- 0xF8, 0x00, 0x3E, 0x00, 0xFE, 0x01, 0xFF, 0x00, 0x87, 0xC7, 0xC3, 0x01, 0x03, 0xFE, 0x80, 0x01, 0x00, 0x38, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+// ๐ง Generic module (gear-like shape)
+const uint8_t icon_module[] PROGMEM = {
+ 0b00011000, // โโโโโโโโ
+ 0b00111100, // โโโโโโโโ
+ 0b01111110, // โโโโโโโโ
+ 0b11011011, // โโโโโโโโ
+ 0b11011011, // โโโโโโโโ
+ 0b01111110, // โโโโโโโโ
+ 0b00111100, // โโโโโโโโ
+ 0b00011000 // โโโโโโโโ
};
-#define devil_height 30
-#define devil_width 30
-static unsigned char devil[] PROGMEM = {
- 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x10, 0x03, 0xC0, 0x01, 0x38, 0x07, 0x7C, 0x0F, 0x38, 0x1F, 0x03, 0x30, 0x1E,
- 0xFE, 0x01, 0xE0, 0x1F, 0x7E, 0x00, 0x80, 0x1F, 0x3C, 0x00, 0x00, 0x0F, 0x1C, 0x00, 0x00, 0x0E, 0x18, 0x00, 0x00, 0x06,
- 0x08, 0x00, 0x00, 0x04, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x0E, 0x1C, 0x0C,
- 0x0C, 0x18, 0x06, 0x0C, 0x0C, 0x1C, 0x06, 0x0C, 0x0C, 0x1C, 0x0E, 0x0C, 0x0C, 0x1C, 0x0E, 0x0C, 0x0C, 0x0C, 0x06, 0x0C,
- 0x08, 0x00, 0x00, 0x06, 0x18, 0x02, 0x10, 0x06, 0x10, 0x0C, 0x0C, 0x03, 0x30, 0xF8, 0x07, 0x03, 0x60, 0xE0, 0x80, 0x01,
- 0xC0, 0x00, 0xC0, 0x00, 0x80, 0x01, 0x70, 0x00, 0x00, 0x06, 0x1C, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
+#define mute_symbol_width 8
+#define mute_symbol_height 8
+const uint8_t mute_symbol[] PROGMEM = {
+ 0b00011001, // โ
+ 0b00100110, // โ
+ 0b00100100, // โโโโ
+ 0b01001010, // โ โ โ
+ 0b01010010, // โ โ โ
+ 0b01100010, // โโโโโโโโ
+ 0b11111111, // โ โ
+ 0b10011000, // โ
};
-#define heart_height 30
-#define heart_width 30
-static unsigned char heart[] PROGMEM = {
- 0x00, 0x00, 0x00, 0x00, 0xC0, 0x03, 0xF0, 0x00, 0xF8, 0x0F, 0xFC, 0x07, 0xFC, 0x1F, 0x06, 0x0E, 0xFE, 0x3F, 0x03, 0x18,
- 0xFE, 0xFF, 0x7F, 0x10, 0xFF, 0xFF, 0xFF, 0x31, 0xFF, 0xFF, 0xFF, 0x33, 0xFF, 0xFF, 0xFF, 0x37, 0xFF, 0xFF, 0xFF, 0x37,
- 0xFF, 0xFF, 0xFF, 0x3F, 0xFF, 0xFF, 0xFF, 0x3F, 0xFF, 0xFF, 0xFF, 0x3F, 0xFE, 0xFF, 0xFF, 0x1F, 0xFE, 0xFF, 0xFF, 0x1F,
- 0xFC, 0xFF, 0xFF, 0x0F, 0xFC, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF, 0x07, 0xF0, 0xFF, 0xFF, 0x03, 0xF0, 0xFF, 0xFF, 0x03,
- 0xE0, 0xFF, 0xFF, 0x01, 0xC0, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0x7F, 0x00, 0x00, 0xFF, 0x3F, 0x00, 0x00, 0xFE, 0x1F, 0x00,
- 0x00, 0xFC, 0x0F, 0x00, 0x00, 0xF8, 0x07, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x80, 0x00, 0x00,
+#define mute_symbol_big_width 16
+#define mute_symbol_big_height 16
+const uint8_t mute_symbol_big[] PROGMEM = {0b00000001, 0b00000000, 0b11000010, 0b00000011, 0b00110100, 0b00001100, 0b00011000,
+ 0b00001000, 0b00011000, 0b00010000, 0b00101000, 0b00010000, 0b01001000, 0b00010000,
+ 0b10001000, 0b00010000, 0b00001000, 0b00010001, 0b00001000, 0b00010010, 0b00001000,
+ 0b00010100, 0b00000100, 0b00101000, 0b11111100, 0b00111111, 0b01000000, 0b00100010,
+ 0b10000000, 0b01000001, 0b00000000, 0b10000000};
+
+// Bell icon for Alert Message
+#define bell_alert_width 8
+#define bell_alert_height 8
+const unsigned char bell_alert[] PROGMEM = {0b00011000, 0b00100100, 0b00100100, 0b01000010,
+ 0b01000010, 0b01000010, 0b11111111, 0b00011000};
+
+#define key_symbol_width 8
+#define key_symbol_height 8
+const uint8_t key_symbol[] PROGMEM = {0b00000000, 0b00000000, 0b00000110, 0b11111001,
+ 0b10101001, 0b10000110, 0b00000000, 0b00000000};
+
+#define placeholder_width 8
+#define placeholder_height 8
+const uint8_t placeholder[] PROGMEM = {0b11111111, 0b11111111, 0b11111111, 0b11111111,
+ 0b11111111, 0b11111111, 0b11111111, 0b11111111};
+
+#define icon_node_width 8
+#define icon_node_height 8
+static const uint8_t icon_node[] PROGMEM = {
+ 0x10, // #
+ 0x10, // # โ antenna
+ 0x10, // #
+ 0xFE, // ####### โ device top
+ 0x82, // # #
+ 0xAA, // # # # # โ body with pattern
+ 0x92, // # # #
+ 0xFE // ####### โ device base
};
-#define poo_width 30
-#define poo_height 30
-static unsigned char poo[] PROGMEM = {
- 0x00, 0x1C, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0xEC, 0x01, 0x00, 0x00, 0x8C, 0x07, 0x00, 0x00, 0x0C, 0x06, 0x00,
- 0x00, 0x24, 0x0C, 0x00, 0x00, 0x34, 0x08, 0x00, 0x00, 0x1F, 0x08, 0x00, 0xC0, 0x0F, 0x08, 0x00, 0xC0, 0x00, 0x3C, 0x00,
- 0x60, 0x00, 0x7C, 0x00, 0x60, 0x00, 0xC6, 0x00, 0x20, 0x00, 0xCB, 0x00, 0xA0, 0xC7, 0xFF, 0x00, 0xE0, 0x7F, 0xF7, 0x00,
- 0xF0, 0x18, 0xE3, 0x03, 0x78, 0x18, 0x41, 0x03, 0x6C, 0x9B, 0x5D, 0x06, 0x64, 0x9B, 0x5D, 0x04, 0x44, 0x1A, 0x41, 0x04,
- 0x4C, 0xD8, 0x63, 0x06, 0xF8, 0xFC, 0x36, 0x06, 0xFE, 0x0F, 0x9C, 0x1F, 0x07, 0x03, 0xC0, 0x30, 0x03, 0x00, 0x78, 0x20,
- 0x01, 0x00, 0x1F, 0x20, 0x03, 0xE0, 0x03, 0x20, 0x07, 0x7E, 0x04, 0x30, 0xFE, 0x0F, 0xFC, 0x1F, 0xF0, 0x00, 0xF0, 0x0F,
-};
-#endif
+#define bluetoothdisabled_width 8
+#define bluetoothdisabled_height 8
+const uint8_t bluetoothdisabled[] PROGMEM = {0b11101100, 0b01010100, 0b01001100, 0b01010100,
+ 0b01001100, 0b00000000, 0b00000000, 0b00000000};
+
+#define smallbulletpoint_width 8
+#define smallbulletpoint_height 8
+const uint8_t smallbulletpoint[] PROGMEM = {0b00000011, 0b00000011, 0b00000000, 0b00000000,
+ 0b00000000, 0b00000000, 0b00000000, 0b00000000};
+
+// Digital Clock
+#define digital_icon_clock_width 8
+#define digital_icon_clock_height 8
+const uint8_t digital_icon_clock[] PROGMEM = {0b00111100, 0b01000010, 0b10000101, 0b10101001,
+ 0b10010001, 0b10000001, 0b01000010, 0b00111100};
+// Analog Clock
+#define analog_icon_clock_width 8
+#define analog_icon_clock_height 8
+const uint8_t analog_icon_clock[] PROGMEM = {0b11111111, 0b01000010, 0b00100100, 0b00011000,
+ 0b00100100, 0b01000010, 0b01000010, 0b11111111};
#include "img/icon.xbm"
+static_assert(sizeof(icon_bits) >= 0, "Silence unused variable warning");
\ No newline at end of file
diff --git a/src/graphics/niche/Drivers/EInk/DEPG0213BNS800.h b/src/graphics/niche/Drivers/EInk/DEPG0213BNS800.h
index e1bb96450..3ce16e473 100644
--- a/src/graphics/niche/Drivers/EInk/DEPG0213BNS800.h
+++ b/src/graphics/niche/Drivers/EInk/DEPG0213BNS800.h
@@ -5,7 +5,7 @@ E-Ink display driver
- Manufacturer: DKE
- Size: 2.13 inch
- Resolution: 122px x 250px
- - Flex connector marking: FPC-7528B
+ - Flex connector marking (not a unique identifier): FPC-7528B
Note: this is from an older generation of DKE panels, which still used Solomon Systech controller ICs.
DKE's website suggests that the latest DEPG0213BN displays may use Fitipower controllers instead.
diff --git a/src/graphics/niche/Drivers/EInk/DEPG0290BNS800.h b/src/graphics/niche/Drivers/EInk/DEPG0290BNS800.h
index 72062e0d6..257fed1a6 100644
--- a/src/graphics/niche/Drivers/EInk/DEPG0290BNS800.h
+++ b/src/graphics/niche/Drivers/EInk/DEPG0290BNS800.h
@@ -5,7 +5,7 @@ E-Ink display driver
- Manufacturer: DKE
- Size: 2.9 inch
- Resolution: 128px x 296px
- - Flex connector marking: FPC-7519 rev.b
+ - Flex connector marking (not a unique identifier): FPC-7519 rev.b
*/
diff --git a/src/graphics/niche/Drivers/EInk/E0213A367.cpp b/src/graphics/niche/Drivers/EInk/E0213A367.cpp
new file mode 100644
index 000000000..f19cb4ff7
--- /dev/null
+++ b/src/graphics/niche/Drivers/EInk/E0213A367.cpp
@@ -0,0 +1,84 @@
+#include "./E0213A367.h"
+
+#ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS
+
+using namespace NicheGraphics::Drivers;
+
+// Map the display controller IC's output to the connected panel
+void E0213A367::configScanning()
+{
+ // "Driver output control"
+ // Scan gates from 0 to 249 (vertical resolution 250px)
+ sendCommand(0x01);
+ sendData(0xF9);
+ sendData(0x00);
+}
+
+// Specify which information is used to control the sequence of voltages applied to move the pixels
+void E0213A367::configWaveform()
+{
+ // This command (0x37) is poorly documented
+ // As of July 2025, the datasheet for this display's controller IC is unavailable
+ // The values are supplied by Heltec, who presumably have privileged access to information from the display manufacturer
+ // Datasheet for the similar SSD1680 IC hints at the function of this command:
+
+ // "Spare VCOM OTP selection":
+ // Unclear why 0x40 is set. Sane values for related SSD1680 seem to be 0x80 or 0x00.
+ // Maybe value is redundant? No noticeable impact when set to 0x00.
+ // We'll leave it set to 0x40, following Heltec's lead, just in case.
+
+ // "Display Mode"
+ // Seems to specify whether a waveform stored in OTP should use display mode 1 or 2 (full refresh or differential refresh)
+
+ // Unusual that waveforms are programmed to OTP, but this meta information is not ..?
+
+ sendCommand(0x37); // "Write Register for Display Option" ?
+ sendData(0x40); // "Spare VCOM OTP selection" ?
+ sendData(0x80); // "Display Mode for WS[7:0]" ?
+ sendData(0x03); // "Display Mode for WS[15:8]" ?
+ sendData(0x0E); // "Display Mode [23:16]" ?
+
+ switch (updateType) {
+ case FAST:
+ sendCommand(0x3C); // Border waveform:
+ sendData(0x81); // As specified by Heltec. Actually VCOM (0x80)?. Bit 0 seems redundant here.
+ break;
+ case FULL:
+ default:
+ sendCommand(0x3C); // Border waveform:
+ sendData(0x01); // Follow LUT 1 (blink same as white pixels)
+ break;
+ }
+}
+
+// Tell controller IC which operations to run
+void E0213A367::configUpdateSequence()
+{
+ switch (updateType) {
+ case FAST:
+ sendCommand(0x22); // Set "update sequence"
+ sendData(0xFF); // Will load LUT from OTP memory, Display mode 2 "differential refresh"
+ break;
+ case FULL:
+ default:
+ sendCommand(0x22); // Set "update sequence"
+ sendData(0xF7); // Will load LUT from OTP memory, Display mode 1 "full refresh"
+ break;
+ }
+}
+
+// Once the refresh operation has been started,
+// begin periodically polling the display to check for completion, using the normal Meshtastic threading code
+// Only used when refresh is "async"
+void E0213A367::detachFromUpdate()
+{
+ switch (updateType) {
+ case FAST:
+ return beginPolling(50, 500); // At least 500ms for fast refresh
+ case FULL:
+ default:
+ return beginPolling(100, 1500); // At least 1.5 seconds for full refresh
+ }
+}
+
+#endif // MESHTASTIC_INCLUDE_NICHE_GRAPHICS
\ No newline at end of file
diff --git a/src/graphics/niche/Drivers/EInk/E0213A367.h b/src/graphics/niche/Drivers/EInk/E0213A367.h
new file mode 100644
index 000000000..a36fcb407
--- /dev/null
+++ b/src/graphics/niche/Drivers/EInk/E0213A367.h
@@ -0,0 +1,41 @@
+/*
+
+E-Ink display driver
+ - SSD1682
+ - Manufacturer: SEEKINK
+ - Size: 2.13 inch
+ - Resolution: 122px x 255px
+ - Flex connector marking: HINK-E0213A162-A1 (hidden, printed on reverse)
+
+*/
+
+#pragma once
+
+#ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS
+
+#include "configuration.h"
+
+#include "./SSD1682.h"
+
+namespace NicheGraphics::Drivers
+{
+class E0213A367 : public SSD1682
+{
+ // Display properties
+ private:
+ static constexpr uint32_t width = 122;
+ static constexpr uint32_t height = 250;
+ static constexpr UpdateTypes supported = (UpdateTypes)(FULL | FAST);
+
+ public:
+ E0213A367() : SSD1682(width, height, supported, 0) {}
+
+ protected:
+ void configScanning() override;
+ void configWaveform() override;
+ void configUpdateSequence() override;
+ void detachFromUpdate() override;
+};
+
+} // namespace NicheGraphics::Drivers
+#endif // MESHTASTIC_INCLUDE_NICHE_GRAPHICS
\ No newline at end of file
diff --git a/src/graphics/niche/Drivers/EInk/GDEY0154D67.cpp b/src/graphics/niche/Drivers/EInk/GDEY0154D67.cpp
index 2cab179b9..9a06fa841 100644
--- a/src/graphics/niche/Drivers/EInk/GDEY0154D67.cpp
+++ b/src/graphics/niche/Drivers/EInk/GDEY0154D67.cpp
@@ -9,12 +9,9 @@ void GDEY0154D67::configScanning()
{
// "Driver output control"
sendCommand(0x01);
- sendData(0xC7);
+ sendData(0xC7); // Scan until gate 199 (200px vertical res.)
sendData(0x00);
sendData(0x00);
-
- // To-do: delete this method?
- // Values set here might be redundant: C7, 00, 00 seems to be default
}
// Specify which information is used to control the sequence of voltages applied to move the pixels
@@ -52,10 +49,10 @@ void GDEY0154D67::detachFromUpdate()
{
switch (updateType) {
case FAST:
- return beginPolling(50, 500); // At least 500ms for fast refresh
+ return beginPolling(50, 300); // At least 300ms for fast refresh
case FULL:
default:
- return beginPolling(100, 2000); // At least 2 seconds for full refresh
+ return beginPolling(100, 1500); // At least 1.5 seconds for full refresh
}
}
#endif // MESHTASTIC_INCLUDE_NICHE_GRAPHICS
\ No newline at end of file
diff --git a/src/graphics/niche/Drivers/EInk/GDEY0154D67.h b/src/graphics/niche/Drivers/EInk/GDEY0154D67.h
index fc4d93d12..e391eea50 100644
--- a/src/graphics/niche/Drivers/EInk/GDEY0154D67.h
+++ b/src/graphics/niche/Drivers/EInk/GDEY0154D67.h
@@ -5,7 +5,7 @@ E-Ink display driver
- Manufacturer: Goodisplay
- Size: 1.54 inch
- Resolution: 200px x 200px
- - Flex connector marking: FPC-B001
+ - Flex connector marking (not a unique identifier): FPC-B001
*/
@@ -31,9 +31,9 @@ class GDEY0154D67 : public SSD16XX
GDEY0154D67() : SSD16XX(width, height, supported) {}
protected:
- virtual void configScanning() override;
- virtual void configWaveform() override;
- virtual void configUpdateSequence() override;
+ void configScanning() override;
+ void configWaveform() override;
+ void configUpdateSequence() override;
void detachFromUpdate() override;
};
diff --git a/src/graphics/niche/Drivers/EInk/GDEY0213B74.cpp b/src/graphics/niche/Drivers/EInk/GDEY0213B74.cpp
index a0ff63258..b3a585eb7 100644
--- a/src/graphics/niche/Drivers/EInk/GDEY0213B74.cpp
+++ b/src/graphics/niche/Drivers/EInk/GDEY0213B74.cpp
@@ -12,9 +12,6 @@ void GDEY0213B74::configScanning()
sendData(0xF9);
sendData(0x00);
sendData(0x00);
-
- // To-do: delete this method?
- // Values set here might be redundant: F9, 00, 00 seems to be default
}
// Specify which information is used to control the sequence of voltages applied to move the pixels
diff --git a/src/graphics/niche/Drivers/EInk/GDEY0213B74.h b/src/graphics/niche/Drivers/EInk/GDEY0213B74.h
index 2212fe92a..1c36f295d 100644
--- a/src/graphics/niche/Drivers/EInk/GDEY0213B74.h
+++ b/src/graphics/niche/Drivers/EInk/GDEY0213B74.h
@@ -5,7 +5,9 @@ E-Ink display driver
- Manufacturer: Goodisplay
- Size: 2.13 inch
- Resolution: 250px x 122px
- - Flex connector marking: FPC-A002
+ - Flex connector marking (not a unique identifier):
+ - FPC-A002
+ - FPC-A005 20.06.15 TRX
*/
diff --git a/src/graphics/niche/Drivers/EInk/HINK_E0213A289.cpp b/src/graphics/niche/Drivers/EInk/HINK_E0213A289.cpp
new file mode 100644
index 000000000..0509b0502
--- /dev/null
+++ b/src/graphics/niche/Drivers/EInk/HINK_E0213A289.cpp
@@ -0,0 +1,61 @@
+#include "./HINK_E0213A289.h"
+
+#ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS
+
+using namespace NicheGraphics::Drivers;
+
+// Map the display controller IC's output to the connected panel
+void HINK_E0213A289::configScanning()
+{
+ // "Driver output control"
+ // Scan gates from 0 to 249 (vertical resolution 250px)
+ sendCommand(0x01);
+ sendData(0xF9); // Maximum gate # (249, bits 0-7)
+ sendData(0x00); // Maximum gate # (bit 8)
+ sendData(0x00); // (Do not invert scanning order)
+}
+
+// Specify which information is used to control the sequence of voltages applied to move the pixels
+// - For this display, configUpdateSequence() specifies that a suitable LUT will be loaded from
+// the controller IC's OTP memory, when the update procedure begins.
+void HINK_E0213A289::configWaveform()
+{
+ sendCommand(0x3C); // Border waveform:
+ sendData(0x05); // Screen border should follow LUT1 waveform (actively drive pixels white)
+
+ sendCommand(0x18); // Temperature sensor:
+ sendData(0x80); // Use internal temperature sensor to select an appropriate refresh waveform
+}
+
+// Describes the sequence of events performed by the displays controller IC during a refresh
+// Includes "power up", "load settings from memory", "update the pixels", etc
+void HINK_E0213A289::configUpdateSequence()
+{
+ switch (updateType) {
+ case FAST:
+ sendCommand(0x22); // Set "update sequence"
+ sendData(0xFF); // Will load LUT from OTP memory, Display mode 2 "differential refresh"
+ break;
+
+ case FULL:
+ default:
+ sendCommand(0x22); // Set "update sequence"
+ sendData(0xF7); // Will load LUT from OTP memory
+ break;
+ }
+}
+
+// Once the refresh operation has been started,
+// begin periodically polling the display to check for completion, using the normal Meshtastic threading code
+// Only used when refresh is "async"
+void HINK_E0213A289::detachFromUpdate()
+{
+ switch (updateType) {
+ case FAST:
+ return beginPolling(50, 500); // At least 500ms for fast refresh
+ case FULL:
+ default:
+ return beginPolling(100, 1000); // At least 1 second for full refresh (quick; display only blinks pixels once)
+ }
+}
+#endif // MESHTASTIC_INCLUDE_NICHE_GRAPHICS
\ No newline at end of file
diff --git a/src/graphics/niche/Drivers/EInk/HINK_E0213A289.h b/src/graphics/niche/Drivers/EInk/HINK_E0213A289.h
new file mode 100644
index 000000000..eab0bf59d
--- /dev/null
+++ b/src/graphics/niche/Drivers/EInk/HINK_E0213A289.h
@@ -0,0 +1,44 @@
+/*
+
+E-Ink display driver
+ - HINK_E0213A289
+ - Manufacturer: Holitech
+ - Size: 2.13 inch
+ - Resolution: 122px x 250px
+ - Flex connector label (not a unique identifier): FPC-7528B
+
+ Note: as of Feb. 2025, these panels are used for "WeActStudio 2.13in B&W" display modules
+
+*/
+
+#pragma once
+
+#ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS
+
+#include "configuration.h"
+
+#include "./SSD16XX.h"
+
+namespace NicheGraphics::Drivers
+{
+class HINK_E0213A289 : public SSD16XX
+{
+ // Display properties
+ private:
+ static constexpr uint32_t width = 122;
+ static constexpr uint32_t height = 250;
+ static constexpr UpdateTypes supported = (UpdateTypes)(FULL | FAST);
+
+ public:
+ HINK_E0213A289() : SSD16XX(width, height, supported, 1) {}
+
+ protected:
+ void configScanning() override;
+ void configWaveform() override;
+ void configUpdateSequence() override;
+ void detachFromUpdate() override;
+};
+
+} // namespace NicheGraphics::Drivers
+
+#endif // MESHTASTIC_INCLUDE_NICHE_GRAPHICS
\ No newline at end of file
diff --git a/src/graphics/niche/Drivers/EInk/HINK_E042A87.h b/src/graphics/niche/Drivers/EInk/HINK_E042A87.h
index ac03b65ef..612072b50 100644
--- a/src/graphics/niche/Drivers/EInk/HINK_E042A87.h
+++ b/src/graphics/niche/Drivers/EInk/HINK_E042A87.h
@@ -5,7 +5,7 @@ E-Ink display driver
- Manufacturer: Holitech
- Size: 4.2 inch
- Resolution: 400px x 300px
- - Flex connector marking: HINK-E042A07-FPC-A1
+ - Flex connector marking (not a unique identifier): HINK-E042A07-FPC-A1
- Silver sticker with QR code, marked: HE042A87
Note: as of Feb. 2025, these panels are used for "WeActStudio 4.2in B&W" display modules
diff --git a/src/graphics/niche/Drivers/EInk/LCMEN2R13ECC1.h b/src/graphics/niche/Drivers/EInk/LCMEN2R13ECC1.h
index b78e3bcca..9fa6eaac9 100644
--- a/src/graphics/niche/Drivers/EInk/LCMEN2R13ECC1.h
+++ b/src/graphics/niche/Drivers/EInk/LCMEN2R13ECC1.h
@@ -5,7 +5,6 @@ E-Ink display driver
- Manufacturer: WISEVAST
- Size: 2.13 inch
- Resolution: 122px x 255px
- - Flex connector marking: Soldering connector, no connector is needed
*/
diff --git a/src/graphics/niche/Drivers/EInk/LCMEN2R13EFC1.h b/src/graphics/niche/Drivers/EInk/LCMEN2R13EFC1.h
index f9da202aa..499daef05 100644
--- a/src/graphics/niche/Drivers/EInk/LCMEN2R13EFC1.h
+++ b/src/graphics/niche/Drivers/EInk/LCMEN2R13EFC1.h
@@ -5,7 +5,7 @@ E-Ink display driver
- Manufacturer: Wisevast
- Size: 2.13 inch
- Resolution: 122px x 250px
- - Flex connector marking: HINK-E0213A162-FPC-A0 (Hidden, printed on back-side)
+ - Flex connector marking (not a unique identifier): HINK-E0213A162-FPC-A0 (Hidden, printed on back-side)
Note: this display uses an uncommon controller IC, Fitipower JD79656.
It is implemented as a "one-off", directly inheriting the EInk base class, unlike SSD16XX displays.
diff --git a/src/graphics/niche/Drivers/EInk/SSD1682.cpp b/src/graphics/niche/Drivers/EInk/SSD1682.cpp
new file mode 100644
index 000000000..c3d7f7786
--- /dev/null
+++ b/src/graphics/niche/Drivers/EInk/SSD1682.cpp
@@ -0,0 +1,41 @@
+#include "./SSD1682.h"
+
+#ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS
+
+using namespace NicheGraphics::Drivers;
+
+SSD1682::SSD1682(uint16_t width, uint16_t height, EInk::UpdateTypes supported, uint8_t bufferOffsetX)
+ : SSD16XX(width, height, supported, bufferOffsetX)
+{
+}
+
+// SSD1682 only accepts single-byte x and y values
+// This causes an incompatibility with the default SSD16XX::configFullscreen
+void SSD1682::configFullscreen()
+{
+ // Define the boundaries of the "fullscreen" region, for the controller IC
+ static const uint8_t sx = bufferOffsetX; // Notice the offset
+ static const uint8_t sy = 0;
+ static const uint8_t ex = bufferRowSize + bufferOffsetX - 1; // End is "max index", not "count". Minus 1 handles this
+ static const uint8_t ey = height;
+
+ // Data entry mode - Left to Right, Top to Bottom
+ sendCommand(0x11);
+ sendData(0x03);
+
+ // Select controller IC memory region to display a fullscreen image
+ sendCommand(0x44); // Memory X start - end
+ sendData(sx);
+ sendData(ex);
+ sendCommand(0x45); // Memory Y start - end
+ sendData(sy);
+ sendData(ey);
+
+ // Place the cursor at the start of this memory region, ready to send image data x=0 y=0
+ sendCommand(0x4E); // Memory cursor X
+ sendData(sx);
+ sendCommand(0x4F); // Memory cursor y
+ sendData(sy);
+}
+
+#endif
\ No newline at end of file
diff --git a/src/graphics/niche/Drivers/EInk/SSD1682.h b/src/graphics/niche/Drivers/EInk/SSD1682.h
new file mode 100644
index 000000000..ba3008537
--- /dev/null
+++ b/src/graphics/niche/Drivers/EInk/SSD1682.h
@@ -0,0 +1,31 @@
+/*
+
+E-Ink base class for displays based on SSD1682
+
+SSD1682 has a few quirks. We're implementing them here in a new base class,
+to avoid re-implementing them every time we need to add a new SSD1682-based display.
+
+*/
+
+#pragma once
+
+#ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS
+
+#include "configuration.h"
+
+#include "./SSD16XX.h"
+
+namespace NicheGraphics::Drivers
+{
+
+class SSD1682 : public SSD16XX
+{
+ public:
+ SSD1682(uint16_t width, uint16_t height, EInk::UpdateTypes supported, uint8_t bufferOffsetX = 0);
+ virtual void configFullscreen(); // Select memory region on controller IC
+ virtual void deepSleep() {} // Not usable (image memory not retained)
+};
+
+} // namespace NicheGraphics::Drivers
+
+#endif // MESHTASTIC_INCLUDE_NICHE_GRAPHICS
\ No newline at end of file
diff --git a/src/graphics/niche/Drivers/EInk/ZJY128296_029EAAMFGN.cpp b/src/graphics/niche/Drivers/EInk/ZJY128296_029EAAMFGN.cpp
new file mode 100644
index 000000000..a8f43420f
--- /dev/null
+++ b/src/graphics/niche/Drivers/EInk/ZJY128296_029EAAMFGN.cpp
@@ -0,0 +1,59 @@
+#include "./ZJY128296_029EAAMFGN.h"
+
+#ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS
+
+using namespace NicheGraphics::Drivers;
+
+// Map the display controller IC's output to the connected panel
+void ZJY128296_029EAAMFGN::configScanning()
+{
+ // "Driver output control"
+ // Scan gates from 0 to 295 (vertical resolution 296px)
+ sendCommand(0x01);
+ sendData(0x27); // Number of gates (295, bits 0-7)
+ sendData(0x01); // Number of gates (295, bit 8)
+ sendData(0x00); // (Do not invert scanning order)
+}
+
+// Specify which information is used to control the sequence of voltages applied to move the pixels
+// - For this display, configUpdateSequence() specifies that a suitable LUT will be loaded from
+// the controller IC's OTP memory, when the update procedure begins.
+void ZJY128296_029EAAMFGN::configWaveform()
+{
+ sendCommand(0x3C); // Border waveform:
+ sendData(0x05); // Screen border should follow LUT1 waveform (actively drive pixels white)
+
+ sendCommand(0x18); // Temperature sensor:
+ sendData(0x80); // Use internal temperature sensor to select an appropriate refresh waveform
+}
+
+void ZJY128296_029EAAMFGN::configUpdateSequence()
+{
+ switch (updateType) {
+ case FAST:
+ sendCommand(0x22); // Set "update sequence"
+ sendData(0xFF); // Will load LUT from OTP memory, Display mode 2 "differential refresh"
+ break;
+
+ case FULL:
+ default:
+ sendCommand(0x22); // Set "update sequence"
+ sendData(0xF7); // Will load LUT from OTP memory
+ break;
+ }
+}
+
+// Once the refresh operation has been started,
+// begin periodically polling the display to check for completion, using the normal Meshtastic threading code
+// Only used when refresh is "async"
+void ZJY128296_029EAAMFGN::detachFromUpdate()
+{
+ switch (updateType) {
+ case FAST:
+ return beginPolling(50, 300); // At least 300ms for fast refresh
+ case FULL:
+ default:
+ return beginPolling(100, 2000); // At least 2 seconds for full refresh
+ }
+}
+#endif // MESHTASTIC_INCLUDE_NICHE_GRAPHICS
\ No newline at end of file
diff --git a/src/graphics/niche/Drivers/EInk/ZJY128296_029EAAMFGN.h b/src/graphics/niche/Drivers/EInk/ZJY128296_029EAAMFGN.h
new file mode 100644
index 000000000..27644e709
--- /dev/null
+++ b/src/graphics/niche/Drivers/EInk/ZJY128296_029EAAMFGN.h
@@ -0,0 +1,44 @@
+/*
+
+E-Ink display driver
+ - ZJY128296-029EAAMFGN
+ - Manufacturer: Zhongjingyuan
+ - Size: 2.9 inch
+ - Resolution: 128px x 296px
+ - Flex connector label (not a unique identifier): FPC-A005 20.06.15 TRX
+
+ Note: as of Feb. 2025, these panels are used for "WeActStudio 2.9in B&W" display modules
+
+*/
+
+#pragma once
+
+#ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS
+
+#include "configuration.h"
+
+#include "./SSD16XX.h"
+
+namespace NicheGraphics::Drivers
+{
+class ZJY128296_029EAAMFGN : public SSD16XX
+{
+ // Display properties
+ private:
+ static constexpr uint32_t width = 128;
+ static constexpr uint32_t height = 296;
+ static constexpr UpdateTypes supported = (UpdateTypes)(FULL | FAST);
+
+ public:
+ ZJY128296_029EAAMFGN() : SSD16XX(width, height, supported) {}
+
+ protected:
+ void configScanning() override;
+ void configWaveform() override;
+ void configUpdateSequence() override;
+ void detachFromUpdate() override;
+};
+
+} // namespace NicheGraphics::Drivers
+
+#endif // MESHTASTIC_INCLUDE_NICHE_GRAPHICS
\ No newline at end of file
diff --git a/src/graphics/niche/Drivers/EInk/ZJY200200_0154DAAMFGN.h b/src/graphics/niche/Drivers/EInk/ZJY200200_0154DAAMFGN.h
new file mode 100644
index 000000000..fb16bcf2f
--- /dev/null
+++ b/src/graphics/niche/Drivers/EInk/ZJY200200_0154DAAMFGN.h
@@ -0,0 +1,32 @@
+/*
+
+E-Ink display driver
+ - ZJY200200-0154DAAMFGN
+ - Manufacturer: Zhongjingyuan
+ - Size: 1.54 inch
+ - Resolution: 200px x 200px
+ - Flex connector marking: FPC-B001
+
+ Note: as of Feb. 2025, these panels are used for "WeActStudio 1.54in B&W" display modules
+
+ This *is* a distinct panel, however the driver is currently identical to GDEY0154D67
+ We recognize it as separate now, to avoid breaking any custom builds if the drivers do need to diverge in future.
+
+*/
+
+#pragma once
+
+#ifdef MESHTASTIC_INCLUDE_NICHE_GRAPHICS
+
+#include "configuration.h"
+
+#include "./GDEY0154D67.h"
+
+namespace NicheGraphics::Drivers
+{
+
+typedef GDEY0154D67 ZJY200200_0154DAAMFGN;
+
+} // namespace NicheGraphics::Drivers
+
+#endif // MESHTASTIC_INCLUDE_NICHE_GRAPHICS
\ No newline at end of file
diff --git a/src/graphics/niche/Fonts/FreeSans12pt_Win1250.h b/src/graphics/niche/Fonts/FreeSans12pt_Win1250.h
new file mode 100644
index 000000000..66edcc6ad
--- /dev/null
+++ b/src/graphics/niche/Fonts/FreeSans12pt_Win1250.h
@@ -0,0 +1,527 @@
+// trunk-ignore-all(clang-format)
+#pragma once
+/* PROPERTIES
+
+FONT_NAME FreeSans12pt_Win1250
+*/
+const uint8_t FreeSans12pt_Win1250Bitmaps[] PROGMEM = {
+/* 0x01 */ 0x00, 0x30, 0x00, 0x09, 0x00, 0x01, 0x20, 0x00, 0x24, 0x00, 0x04, 0x80, 0x01, 0x90, 0x00, 0x62, 0x00, 0x30, 0xFE, 0x04, 0x10, 0x5F, 0x02, 0x0B, 0x00, 0x7F, 0xE0, 0x0C, 0x1C, 0x02, 0x83, 0x81, 0x9F, 0xF0, 0x02, 0x1E, 0x00, 0x41, 0xC0, 0x0E, 0x7F, 0x81, 0x78, 0x18, 0x62, 0x00, 0xFF, 0xC0,
+/* 0x02 */ 0x00, 0xFF, 0x80, 0x61, 0x13, 0xF0, 0x62, 0x60, 0x07, 0xFC, 0x00, 0x83, 0x80, 0x10, 0xF0, 0x33, 0xF6, 0x01, 0x41, 0xC0, 0x18, 0x38, 0x03, 0xFF, 0xE0, 0x47, 0x02, 0x08, 0x20, 0x61, 0xC4, 0x06, 0x17, 0x00, 0x22, 0x00, 0x02, 0x40, 0x00, 0x48, 0x00, 0x09, 0x00, 0x01, 0x20, 0x00, 0x3C, 0x00,
+/* 0x03 */ 0x01, 0xFC, 0x00, 0x38, 0x18, 0x02, 0x00, 0x30, 0x20, 0x00, 0xC2, 0x00, 0x02, 0x30, 0x00, 0x09, 0x04, 0x08, 0x48, 0x70, 0xE1, 0xC3, 0x87, 0x0E, 0x08, 0x10, 0x70, 0x00, 0x03, 0x80, 0x00, 0x14, 0x00, 0x00, 0xA1, 0x81, 0x8D, 0x87, 0xF0, 0x44, 0x00, 0x06, 0x30, 0x00, 0x60, 0xC0, 0x06, 0x03, 0x80, 0x60, 0x07, 0xFC, 0x00,
+/* 0x04 */ 0x01, 0xFC, 0x00, 0x38, 0x18, 0x02, 0x00, 0x30, 0x20, 0x00, 0xC2, 0x00, 0x02, 0x30, 0x00, 0x09, 0x10, 0x02, 0x48, 0xE0, 0x61, 0xC1, 0xCC, 0x0E, 0x78, 0x1C, 0x70, 0x00, 0x03, 0x80, 0x00, 0x14, 0xFF, 0xFC, 0xA6, 0x00, 0xCD, 0x9F, 0xFE, 0x44, 0x71, 0xE6, 0x30, 0xFC, 0x60, 0xC0, 0x06, 0x03, 0x80, 0x60, 0x07, 0xFC, 0x00,
+/* 0x05 */ 0x00, 0x18, 0x00, 0x00, 0x40, 0x01, 0x90, 0x01, 0xF4, 0x08, 0x12, 0x23, 0xC1, 0x91, 0x2C, 0x1C, 0x8A, 0xC3, 0x64, 0x64, 0x13, 0x22, 0x41, 0x98, 0x26, 0x2C, 0xC4, 0x22, 0x60, 0x42, 0x13, 0x04, 0x30, 0x80, 0x61, 0xA4, 0x02, 0x18, 0x20, 0x03, 0x41, 0x00, 0x20, 0x08, 0x02, 0x00, 0x60, 0x40, 0x03, 0xF8,
+/* 0x06 */ 0x00, 0x10, 0x00, 0x03, 0x00, 0x1C, 0x48, 0x00, 0xB4, 0x80, 0x09, 0xF9, 0xC0, 0xE0, 0xE4, 0x0C, 0x02, 0x8F, 0x80, 0x38, 0x88, 0x01, 0x0D, 0x00, 0x18, 0x30, 0x01, 0x60, 0x80, 0x13, 0x18, 0x03, 0xF2, 0xC0, 0x20, 0x26, 0x06, 0x07, 0xFF, 0xA0, 0x02, 0x39, 0x00, 0x14, 0x70, 0x01, 0xC3, 0x00, 0x18, 0x00,
+/* 0x07 */
+/* 0x08 */ 0x00, 0x1F, 0x80, 0x00, 0x60, 0x80, 0x01, 0x00, 0x80, 0x06, 0x00, 0x80, 0x3C, 0x01, 0x01, 0x8C, 0x02, 0x02, 0x08, 0x04, 0x04, 0x08, 0x0C, 0x38, 0x00, 0x04, 0x80, 0x00, 0x06, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x2E, 0xC0, 0x01, 0x83, 0x7E, 0x0C, 0x10, 0x37, 0xE2, 0x61, 0x00, 0x0C, 0xC6, 0x10, 0x98, 0x0C, 0x63, 0x00, 0x00, 0xC6, 0x00,
+/* 0x09 */ 0x00, 0x1F, 0x80, 0x00, 0x60, 0x80, 0x01, 0x00, 0x80, 0x06, 0x00, 0x80, 0x3C, 0x01, 0x01, 0x8C, 0x02, 0x02, 0x08, 0x04, 0x04, 0x08, 0x0C, 0x38, 0x00, 0x04, 0x80, 0x00, 0x06, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x2E, 0xC0, 0x01, 0x83, 0x7E, 0x0C, 0x00, 0x37, 0xE0,
+/* 0x0A */
+/* 0x0B */ 0x1F, 0x07, 0xC1, 0x86, 0x41, 0x10, 0x0C, 0x04, 0x80, 0x40, 0x18, 0x00, 0x00, 0xC0, 0x00, 0x06, 0x00, 0x00, 0x30, 0x00, 0x01, 0x40, 0x00, 0x0A, 0x00, 0x00, 0x88, 0x00, 0x04, 0x40, 0x00, 0x41, 0x00, 0x02, 0x04, 0x00, 0x20, 0x20, 0x02, 0x00, 0x80, 0x20, 0x02, 0x02, 0x00, 0x08, 0x20, 0x00, 0x22, 0x00, 0x00, 0xE0, 0x00,
+/* 0x0C */ 0x01, 0x00, 0x00, 0x38, 0x00, 0x04, 0xC0, 0x01, 0x08, 0x00, 0x18, 0x80, 0x1C, 0x10, 0x02, 0x07, 0x80, 0x81, 0x10, 0x1F, 0xC2, 0x02, 0x00, 0x60, 0x80, 0x1A, 0x20, 0x1C, 0x42, 0x1C, 0x08, 0xFE, 0x03, 0xA0, 0x01, 0x8C, 0x01, 0xC1, 0x43, 0xD0, 0x27, 0x81, 0xF8,
+/* 0x0D */
+/* 0x0E */ 0x00, 0xE0, 0x00, 0x11, 0x00, 0x01, 0x10, 0x00, 0x0B, 0x00, 0x03, 0xF8, 0x00, 0x60, 0x60, 0x09, 0x02, 0x00, 0xA0, 0x10, 0x16, 0x01, 0x01, 0x40, 0x10, 0x10, 0x01, 0x01, 0x00, 0x08, 0x10, 0x00, 0x82, 0x1F, 0x08, 0x3F, 0x90, 0x44, 0x00, 0x06, 0xBF, 0xFF, 0xAF, 0xF0, 0xFF, 0xFF, 0x0F, 0xE3, 0xFB, 0xFC,
+/* 0x0F */ 0x01, 0xFC, 0x00, 0x38, 0x18, 0x02, 0x00, 0x30, 0x20, 0x00, 0xC2, 0x40, 0x12, 0x34, 0x00, 0x69, 0x40, 0x01, 0x49, 0xE0, 0xF1, 0xCD, 0x06, 0x8E, 0x28, 0x14, 0x71, 0x40, 0xA3, 0x8B, 0xFD, 0x14, 0x50, 0x68, 0xA2, 0x81, 0x4D, 0x97, 0xFA, 0x44, 0xBF, 0xD6, 0x31, 0x02, 0xE0, 0xC8, 0x16, 0x08, 0x61, 0x08, 0x21, 0xF0, 0x80, 0xF8, 0x78, 0x00,
+/* 0x10 */ 0x00, 0xF0, 0x00, 0x3A, 0x00, 0x07, 0xC0, 0x00, 0xA8, 0x00, 0x1F, 0x00, 0x02, 0xB0, 0x00, 0x52, 0x00, 0x0A, 0x40, 0x02, 0x48, 0x00, 0x49, 0x00, 0x09, 0x30, 0x01, 0x22, 0x01, 0xC4, 0x70, 0xF0, 0x85, 0xE1, 0x10, 0x88, 0x37, 0x20, 0x03, 0x9C, 0x00, 0x37, 0x00, 0x06, 0x40, 0x01, 0x86, 0x00,
+/* 0x11 */ 0x01, 0xFC, 0x00, 0x38, 0x18, 0x02, 0x00, 0x30, 0x20, 0x00, 0xC2, 0x60, 0x02, 0x36, 0x00, 0x09, 0x04, 0x0C, 0x48, 0x60, 0xC1, 0xC3, 0x0F, 0x0E, 0x00, 0x08, 0x70, 0x00, 0x23, 0x80, 0x63, 0x84, 0x01, 0x9F, 0x20, 0x0C, 0xFD, 0x80, 0x27, 0xE4, 0x03, 0x3F, 0x30, 0x33, 0xE0, 0xC0, 0x00, 0x03, 0x80, 0x60, 0x07, 0xFC, 0x00,
+/* 0x12 */ 0x00, 0xC2, 0x00, 0x1C, 0x24, 0x02, 0x18, 0x60, 0x64, 0x02, 0x02, 0x40, 0x20, 0x00, 0xF2, 0x03, 0x89, 0xE0, 0x7C, 0x80, 0x0E, 0x25, 0x80, 0xE1, 0x00, 0x1A, 0x08, 0x71, 0xB0, 0xC4, 0x39, 0x84, 0xC2, 0xCC, 0x40, 0x76, 0x7C, 0x05, 0xBB, 0x80, 0x4C, 0xE0, 0x0A, 0x78, 0x00, 0x9C, 0x00, 0x0F, 0x00, 0x00,
+/* 0x13 */ 0x01, 0xFC, 0x00, 0x38, 0x18, 0x02, 0x00, 0x30, 0x20, 0x00, 0xC2, 0x00, 0x02, 0x30, 0x00, 0x09, 0x00, 0x00, 0x48, 0x60, 0xC1, 0xC6, 0xC9, 0x0E, 0x00, 0x00, 0x70, 0x00, 0x03, 0x80, 0x00, 0x14, 0xFF, 0xF8, 0xA6, 0x00, 0xCD, 0x9F, 0xFE, 0x44, 0x71, 0xE6, 0x30, 0xFC, 0x60, 0xC0, 0x06, 0x03, 0x80, 0x60, 0x07, 0xFC, 0x00,
+/* 0x14 */ 0x01, 0xFC, 0x00, 0x38, 0x18, 0x02, 0x00, 0x30, 0x20, 0x00, 0xC2, 0x20, 0x22, 0x33, 0x01, 0x89, 0x20, 0x02, 0x48, 0x60, 0xE1, 0xC8, 0x80, 0x8E, 0x46, 0x46, 0x72, 0x32, 0x33, 0x9F, 0x9F, 0x94, 0x78, 0x78, 0xA0, 0x00, 0x0D, 0x80, 0x00, 0x44, 0x0E, 0x06, 0x30, 0x00, 0x60, 0xC0, 0x06, 0x03, 0x80, 0x60, 0x07, 0xFC, 0x00,
+/* 0x15 */ 0x03, 0xFC, 0x20, 0x38, 0x1C, 0x81, 0x80, 0x1D, 0x08, 0x00, 0x32, 0x60, 0x00, 0x89, 0x00, 0x02, 0x18, 0x00, 0x08, 0x61, 0xC3, 0x22, 0x8D, 0x93, 0x72, 0x00, 0x00, 0x48, 0x00, 0x01, 0x20, 0x00, 0x04, 0x9F, 0xFF, 0x92, 0x60, 0x0E, 0x44, 0xFF, 0xF2, 0x11, 0xC3, 0x88, 0x21, 0xF8, 0x40, 0x40, 0x02, 0x00, 0xC0, 0x30, 0x00, 0xFF, 0x00,
+/* 0x16 */ 0x03, 0x80, 0x03, 0xC0, 0x01, 0xE0, 0x01, 0xE0, 0x03, 0xF0, 0x03, 0xF0, 0x27, 0xF0, 0x6F, 0x70, 0x6E, 0x60, 0xFC, 0x60, 0xFC, 0x7E, 0xFC, 0x7E, 0xFC, 0x3F, 0xF4, 0x1F, 0xF4, 0x1F, 0xF0, 0x0E, 0x70, 0x0E, 0x30, 0x1C, 0x38, 0x38, 0x0F, 0xF0,
+/* 0x17 */ 0x01, 0xFC, 0x00, 0x38, 0x18, 0x02, 0x00, 0x30, 0x20, 0x00, 0xC2, 0x00, 0x02, 0x30, 0x00, 0x09, 0x00, 0x00, 0x48, 0x00, 0x21, 0xC0, 0x02, 0x8E, 0x20, 0xF4, 0x70, 0x84, 0x11, 0x82, 0x40, 0x84, 0x01, 0x03, 0x20, 0x0F, 0x85, 0x80, 0x03, 0x04, 0x00, 0x04, 0x30, 0x78, 0x10, 0xC0, 0x06, 0x03, 0x80, 0x60, 0x07, 0xFC, 0x00,
+/* 0x18 */ 0x00, 0xFC, 0x00, 0x02, 0x06, 0x00, 0x08, 0x24, 0x00, 0x21, 0xA4, 0x00, 0x4C, 0x48, 0x00, 0xA0, 0x50, 0x01, 0x92, 0x60, 0x03, 0x24, 0xC0, 0x06, 0x01, 0x81, 0x28, 0x03, 0x49, 0x6C, 0xC4, 0xAD, 0xD8, 0x16, 0xA4, 0xCC, 0xC4, 0x44, 0x86, 0x13, 0x05, 0x00, 0x28, 0x0A, 0x00, 0x50, 0x14, 0x00, 0x90, 0x48, 0x01, 0x20, 0x90, 0x02, 0x41, 0x20, 0x00, 0x00,
+/* 0x19 */ 0x01, 0xFC, 0x00, 0x38, 0x18, 0x02, 0x00, 0x30, 0x20, 0x00, 0xC2, 0x00, 0x02, 0x30, 0x00, 0x09, 0x00, 0x00, 0x49, 0xC3, 0x81, 0xC0, 0x00, 0x0E, 0x78, 0xF0, 0x77, 0xEF, 0xC3, 0xA7, 0x4E, 0x15, 0x0A, 0x10, 0xA7, 0x8F, 0x0D, 0x80, 0x00, 0x44, 0x00, 0x06, 0x33, 0xF0, 0x60, 0xC0, 0x06, 0x03, 0x80, 0x60, 0x07, 0xFC, 0x00,
+/* 0x1A */ 0xFF, 0xFF, 0x00, 0x06, 0x00, 0x0C, 0x3E, 0x18, 0x82, 0x32, 0x02, 0x64, 0x04, 0xC8, 0x09, 0x80, 0x23, 0x00, 0x86, 0x02, 0x0C, 0x08, 0x18, 0x10, 0x30, 0x00, 0x60, 0x00, 0xC0, 0x81, 0x80, 0x03, 0x00, 0x07, 0xFF, 0xF8,
+/* 0x1B */ 0x00, 0xFE, 0x00, 0x03, 0x81, 0x80, 0x04, 0x00, 0x60, 0x08, 0x00, 0x30, 0x10, 0x00, 0x10, 0x30, 0x07, 0x88, 0x23, 0xC8, 0x08, 0x22, 0x00, 0x04, 0x60, 0x00, 0x44, 0x60, 0x00, 0x84, 0x63, 0x03, 0x04, 0x61, 0xFC, 0x04, 0x6B, 0x00, 0x9E, 0xA5, 0x01, 0x6A, 0xD5, 0x01, 0x43, 0xA8, 0x81, 0x05, 0xD0, 0x82, 0x0A, 0xA0, 0x82, 0x05, 0xC0, 0x82, 0x02, 0x61, 0xFF, 0x0C, 0x1E, 0x00, 0xF0,
+/* 0x1C */ 0x01, 0xFC, 0x00, 0x38, 0x18, 0x02, 0x00, 0x30, 0x20, 0x00, 0xC2, 0x30, 0x02, 0x32, 0x00, 0x09, 0x00, 0x00, 0x48, 0x20, 0x61, 0xC3, 0x84, 0x0E, 0x1C, 0x78, 0x70, 0x40, 0x03, 0x80, 0x00, 0x14, 0x00, 0x00, 0xA0, 0x03, 0x0D, 0x83, 0xF0, 0x44, 0x00, 0x06, 0x30, 0x00, 0x60, 0xC0, 0x06, 0x03, 0x80, 0x60, 0x07, 0xFC, 0x00,
+/* 0x1D */ 0x01, 0xFE, 0x00, 0x3A, 0x1C, 0x03, 0x00, 0x30, 0x23, 0x1E, 0xC3, 0x38, 0x03, 0x10, 0xC3, 0x09, 0x00, 0x18, 0x68, 0x00, 0xC1, 0x40, 0x00, 0x0A, 0x07, 0x80, 0x50, 0x46, 0x02, 0x80, 0x00, 0x1A, 0x1E, 0x00, 0xCB, 0x10, 0x0D, 0x03, 0x00, 0x48, 0x60, 0x06, 0x40, 0x00, 0x22, 0x0C, 0x02, 0x10, 0x60, 0x60, 0x43, 0xFC, 0x01, 0xE0, 0x00, 0x00,
+/* 0x1E */ 0x01, 0xF0, 0x00, 0xEA, 0xC0, 0x31, 0x5F, 0x04, 0x5F, 0x88, 0x80, 0xA0, 0x48, 0x0E, 0x02, 0x8F, 0x40, 0x3C, 0x10, 0x21, 0x66, 0x87, 0x15, 0x98, 0x71, 0x41, 0x02, 0x14, 0x00, 0x01, 0x40, 0x00, 0x14, 0x00, 0x01, 0x21, 0xFE, 0x12, 0x00, 0x02, 0x10, 0x00, 0x60, 0x80, 0x0C, 0x06, 0x01, 0x80, 0x3F, 0xE0,
+/* 0x1F */ 0x0E, 0x00, 0x13, 0x00, 0x23, 0x00, 0xF3, 0x01, 0x31, 0x01, 0x11, 0x03, 0xD3, 0x06, 0xF2, 0x30, 0x34, 0xC7, 0x25, 0x33, 0x2B, 0xC2, 0x57, 0x04, 0x3A, 0x08, 0x72, 0x30, 0xA3, 0xC3, 0x40, 0x04, 0x40, 0x18, 0x40, 0x60, 0x7F, 0x80,
+/* ' ' 0x20 */
+/* '!' 0x21 */ 0xFF, 0xFF, 0xFF, 0xF0, 0xF0,
+/* '"' 0x22 */ 0xCF, 0x3C, 0xF3, 0x8A, 0x20,
+/* '#' 0x23 */ 0x06, 0x30, 0x31, 0x03, 0x18, 0x18, 0xC7, 0xFF, 0xBF, 0xFC, 0x31, 0x01, 0x18, 0x18, 0xC7, 0xFF, 0xBF, 0xFC, 0x31, 0x01, 0x18, 0x18, 0xC0, 0xC6, 0x06, 0x30,
+/* '$' 0x24 */ 0x04, 0x03, 0xE1, 0xFF, 0x72, 0x7C, 0x47, 0x88, 0xF1, 0x07, 0xA0, 0x7E, 0x03, 0xF0, 0x17, 0x02, 0x7C, 0x47, 0x88, 0xF1, 0x1B, 0x26, 0x7F, 0xC3, 0xE0, 0x10, 0x02, 0x00,
+/* '%' 0x25 */ 0x00, 0x06, 0x03, 0xC0, 0x40, 0x7E, 0x0C, 0x0E, 0x70, 0x80, 0xC3, 0x18, 0x0C, 0x31, 0x00, 0xE7, 0x30, 0x07, 0xE6, 0x00, 0x3C, 0x40, 0x00, 0x0C, 0x7C, 0x00, 0x8F, 0xE0, 0x19, 0xC7, 0x01, 0x18, 0x30, 0x31, 0x83, 0x02, 0x1C, 0x70, 0x40, 0xFE, 0x04, 0x07, 0xC0,
+/* '&' 0x26 */ 0x0F, 0x00, 0x7E, 0x03, 0x9C, 0x0C, 0x30, 0x30, 0xC0, 0xE7, 0x01, 0xF8, 0x03, 0x80, 0x3E, 0x01, 0xCC, 0x6E, 0x39, 0xB0, 0x7C, 0xC0, 0xF3, 0x03, 0xCE, 0x1F, 0x9F, 0xE6, 0x3E, 0x1C,
+/* ''' 0x27 */ 0xFF, 0xA0,
+/* '(' 0x28 */ 0x08, 0x8C, 0x46, 0x31, 0x98, 0xC6, 0x31, 0x8C, 0x63, 0x08, 0x63, 0x08, 0x61, 0x0C, 0x20,
+/* ')' 0x29 */ 0x82, 0x18, 0xC3, 0x18, 0xC3, 0x18, 0xC6, 0x31, 0x8C, 0x62, 0x31, 0x88, 0xC4, 0x62, 0x00,
+/* '*' 0x2A */ 0x10, 0x23, 0x5B, 0xE3, 0x8D, 0x91, 0x00,
+/* '+' 0x2B */ 0x0C, 0x03, 0x00, 0xC0, 0x30, 0xFF, 0xFF, 0xF0, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0,
+/* ',' 0x2C */ 0xF5, 0x60,
+/* '-' 0x2D */ 0xFF, 0xF0,
+/* '.' 0x2E */ 0xF0,
+/* '/' 0x2F */ 0x02, 0x0C, 0x10, 0x20, 0xC1, 0x02, 0x0C, 0x10, 0x20, 0xC1, 0x02, 0x0C, 0x10, 0x20, 0xC1, 0x00,
+/* '0' 0x30 */ 0x1F, 0x07, 0xF1, 0xC7, 0x30, 0x6C, 0x0F, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x3E, 0x0E, 0xC1, 0x9C, 0x71, 0xFC, 0x1F, 0x00,
+/* '1' 0x31 */ 0x08, 0xCF, 0xFF, 0x8C, 0x63, 0x18, 0xC6, 0x31, 0x8C, 0x63, 0x18,
+/* '2' 0x32 */ 0x1F, 0x0F, 0xF9, 0x87, 0x60, 0x7C, 0x06, 0x00, 0xC0, 0x18, 0x07, 0x01, 0xC0, 0xF0, 0x78, 0x1C, 0x06, 0x00, 0xC0, 0x30, 0x07, 0xFF, 0xFF, 0xE0,
+/* '3' 0x33 */ 0x3F, 0x0F, 0xF3, 0x87, 0x60, 0x6C, 0x0C, 0x01, 0x80, 0x60, 0x78, 0x0F, 0x80, 0x18, 0x01, 0x80, 0x3C, 0x07, 0x80, 0xD8, 0x73, 0xFC, 0x3F, 0x00,
+/* '4' 0x34 */ 0x01, 0x80, 0x70, 0x0E, 0x03, 0xC0, 0xD8, 0x1B, 0x06, 0x61, 0x8C, 0x21, 0x8C, 0x33, 0x06, 0x7F, 0xFF, 0xFE, 0x03, 0x00, 0x60, 0x0C, 0x01, 0x80,
+/* '5' 0x35 */ 0x3F, 0xCF, 0xF9, 0x80, 0x30, 0x06, 0x00, 0xDE, 0x1F, 0xE7, 0x0E, 0x00, 0xE0, 0x0C, 0x01, 0x80, 0x30, 0x07, 0x81, 0xB8, 0x73, 0xFC, 0x1F, 0x00,
+/* '6' 0x36 */ 0x0F, 0x07, 0xF9, 0xC3, 0x30, 0x74, 0x01, 0x80, 0x33, 0xC7, 0xFE, 0xF1, 0xDC, 0x1F, 0x01, 0xE0, 0x3C, 0x06, 0xC1, 0xDC, 0x71, 0xFC, 0x1F, 0x00,
+/* '7' 0x37 */ 0xFF, 0xFF, 0xFC, 0x01, 0x00, 0x60, 0x18, 0x02, 0x00, 0xC0, 0x30, 0x06, 0x01, 0x80, 0x30, 0x04, 0x01, 0x80, 0x30, 0x06, 0x01, 0x80, 0x30, 0x00,
+/* '8' 0x38 */ 0x1F, 0x07, 0xF1, 0xC7, 0x30, 0x66, 0x0C, 0xC1, 0x8C, 0x61, 0xF8, 0x3F, 0x8E, 0x3B, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xD8, 0x31, 0xFC, 0x1F, 0x00,
+/* '9' 0x39 */ 0x1F, 0x07, 0xF1, 0xC7, 0x70, 0x6C, 0x07, 0x80, 0xF0, 0x1E, 0x07, 0x61, 0xEF, 0xFC, 0x79, 0x80, 0x30, 0x05, 0xC1, 0x98, 0x73, 0xFC, 0x1E, 0x00,
+/* ':' 0x3A */ 0xF0, 0x00, 0x03, 0xC0,
+/* ';' 0x3B */ 0xF0, 0x00, 0x0F, 0x56,
+/* '<' 0x3C */ 0x00, 0x70, 0x1E, 0x0F, 0x83, 0xC0, 0xF0, 0x0E, 0x00, 0x7C, 0x00, 0xF0, 0x03, 0xC0, 0x0F, 0x00, 0x10,
+/* '=' 0x3D */ 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF,
+/* '>' 0x3E */ 0xE0, 0x07, 0x80, 0x1F, 0x00, 0x7C, 0x00, 0xF0, 0x07, 0x01, 0xE0, 0xF0, 0x3C, 0x0F, 0x00, 0x80, 0x00,
+/* '?' 0x3F */ 0x3F, 0x1F, 0xEE, 0x1F, 0x03, 0xC0, 0xC0, 0x30, 0x0C, 0x06, 0x03, 0x81, 0xC0, 0xE0, 0x30, 0x0C, 0x03, 0x00, 0x00, 0x00, 0x0C, 0x03, 0x00,
+/* '@' 0x40 */ 0x00, 0xFE, 0x00, 0x0F, 0xFE, 0x00, 0xF0, 0x3E, 0x07, 0x00, 0x3C, 0x38, 0x00, 0x38, 0xC1, 0xE0, 0x66, 0x0F, 0xD9, 0xD8, 0x61, 0xC3, 0xC3, 0x07, 0x0F, 0x1C, 0x1C, 0x3C, 0x60, 0x60, 0xF1, 0x81, 0x83, 0xC6, 0x06, 0x1B, 0x18, 0x38, 0xEE, 0x71, 0xE7, 0x18, 0xFD, 0xF8, 0x71, 0xE7, 0xC0, 0xE0, 0x00, 0x01, 0xE0, 0x00, 0x01, 0xFF, 0xC0, 0x01, 0xFC, 0x00,
+/* 'A' 0x41 */ 0x07, 0x80, 0x1E, 0x00, 0x78, 0x03, 0xF0, 0x0C, 0xC0, 0x33, 0x01, 0xCE, 0x06, 0x18, 0x18, 0x60, 0xE1, 0xC3, 0x03, 0x0F, 0xFC, 0x7F, 0xF9, 0x80, 0x66, 0x01, 0xB8, 0x07, 0xC0, 0x0F, 0x00, 0x30,
+/* 'B' 0x42 */ 0xFF, 0xC7, 0xFF, 0x30, 0x1D, 0x80, 0x6C, 0x03, 0x60, 0x1B, 0x00, 0xD8, 0x0C, 0xFF, 0xC7, 0xFF, 0x30, 0x0D, 0x80, 0x3C, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x06, 0xFF, 0xF7, 0xFE, 0x00,
+/* 'C' 0x43 */ 0x07, 0xE0, 0x3F, 0xF0, 0xE0, 0x73, 0x80, 0x76, 0x00, 0x6C, 0x00, 0x30, 0x00, 0x60, 0x00, 0xC0, 0x01, 0x80, 0x03, 0x00, 0x06, 0x00, 0x0E, 0x00, 0x6C, 0x00, 0xDC, 0x03, 0x1E, 0x0E, 0x1F, 0xF8, 0x0F, 0xC0,
+/* 'D' 0x44 */ 0xFF, 0xC3, 0xFF, 0x8C, 0x07, 0x30, 0x0E, 0xC0, 0x1B, 0x00, 0x7C, 0x00, 0xF0, 0x03, 0xC0, 0x0F, 0x00, 0x3C, 0x00, 0xF0, 0x03, 0xC0, 0x1F, 0x00, 0x6C, 0x03, 0xB0, 0x1C, 0xFF, 0xE3, 0xFE, 0x00,
+/* 'E' 0x45 */ 0xFF, 0xFF, 0xFF, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00, 0xFF, 0xEF, 0xFE, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00, 0xFF, 0xFF, 0xFF,
+/* 'F' 0x46 */ 0xFF, 0xFF, 0xFF, 0x00, 0x60, 0x0C, 0x01, 0x80, 0x30, 0x06, 0x00, 0xFF, 0xDF, 0xFB, 0x00, 0x60, 0x0C, 0x01, 0x80, 0x30, 0x06, 0x00, 0xC0, 0x18, 0x00,
+/* 'G' 0x47 */ 0x07, 0xF0, 0x1F, 0xFC, 0x3C, 0x1E, 0x70, 0x07, 0x60, 0x03, 0xE0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x7F, 0xC0, 0x7F, 0xC0, 0x03, 0xC0, 0x03, 0x60, 0x03, 0x60, 0x07, 0x30, 0x0F, 0x3C, 0x1F, 0x1F, 0xFB, 0x07, 0xE1,
+/* 'H' 0x48 */ 0xC0, 0x1E, 0x00, 0xF0, 0x07, 0x80, 0x3C, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x03, 0xFF, 0xFF, 0xFF, 0xF0, 0x07, 0x80, 0x3C, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x03, 0xC0, 0x1E, 0x00, 0xC0,
+/* 'I' 0x49 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xF0,
+/* 'J' 0x4A */ 0x01, 0x80, 0xC0, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x01, 0x80, 0xC0, 0x60, 0x3C, 0x1E, 0x0F, 0x07, 0xC7, 0x7F, 0x1F, 0x00,
+/* 'K' 0x4B */ 0xC0, 0x3E, 0x03, 0xB0, 0x39, 0x83, 0x8C, 0x38, 0x63, 0x83, 0x38, 0x19, 0xC0, 0xDE, 0x07, 0xB8, 0x38, 0xE1, 0x83, 0x0C, 0x1C, 0x60, 0x73, 0x01, 0x98, 0x0E, 0xC0, 0x3E, 0x00, 0xC0,
+/* 'L' 0x4C */ 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xFF, 0xFF, 0xF0,
+/* 'M' 0x4D */ 0xE0, 0x07, 0xE0, 0x07, 0xF0, 0x0F, 0xF0, 0x0F, 0xD0, 0x0F, 0xD8, 0x1B, 0xD8, 0x1B, 0xD8, 0x1B, 0xCC, 0x33, 0xCC, 0x33, 0xCC, 0x33, 0xC6, 0x63, 0xC6, 0x63, 0xC6, 0x63, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC1, 0x83,
+/* 'N' 0x4E */ 0xE0, 0x1F, 0x00, 0xFC, 0x07, 0xE0, 0x3D, 0x81, 0xEE, 0x0F, 0x30, 0x79, 0xC3, 0xC6, 0x1E, 0x18, 0xF0, 0xE7, 0x83, 0x3C, 0x1D, 0xE0, 0x6F, 0x01, 0xF8, 0x0F, 0xC0, 0x3E, 0x01, 0xC0,
+/* 'O' 0x4F */ 0x07, 0xF0, 0x0F, 0xFE, 0x0F, 0x07, 0x86, 0x00, 0xC6, 0x00, 0x33, 0x00, 0x1B, 0x00, 0x07, 0x80, 0x03, 0xC0, 0x01, 0xE0, 0x00, 0xF0, 0x00, 0x78, 0x00, 0x36, 0x00, 0x33, 0x00, 0x18, 0xC0, 0x18, 0x78, 0x3C, 0x1F, 0xFC, 0x03, 0xF8, 0x00,
+/* 'P' 0x50 */ 0xFF, 0x8F, 0xFE, 0xC0, 0x6C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, 0x3C, 0x06, 0xFF, 0xEF, 0xFC, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00,
+/* 'Q' 0x51 */ 0x07, 0xF0, 0x0F, 0xFE, 0x0F, 0x07, 0x86, 0x00, 0xC6, 0x00, 0x33, 0x00, 0x1B, 0x00, 0x07, 0x80, 0x03, 0xC0, 0x01, 0xE0, 0x00, 0xF0, 0x00, 0x78, 0x00, 0x36, 0x00, 0x33, 0x01, 0x98, 0xC0, 0xFC, 0x78, 0x3C, 0x1F, 0xFF, 0x03, 0xF9, 0x80, 0x00, 0x40,
+/* 'R' 0x52 */ 0xFF, 0xE3, 0xFF, 0xCC, 0x03, 0xB0, 0x06, 0xC0, 0x1B, 0x00, 0x6C, 0x01, 0xB0, 0x0C, 0xFF, 0xE3, 0xFF, 0xCC, 0x03, 0xB0, 0x06, 0xC0, 0x1B, 0x00, 0x6C, 0x01, 0xB0, 0x06, 0xC0, 0x1B, 0x00, 0x70,
+/* 'S' 0x53 */ 0x0F, 0xE0, 0x7F, 0xC3, 0x83, 0x98, 0x07, 0x60, 0x0D, 0x80, 0x07, 0x00, 0x1E, 0x00, 0x3F, 0x80, 0x3F, 0xC0, 0x0F, 0x80, 0x07, 0xC0, 0x0F, 0x00, 0x3E, 0x00, 0xDE, 0x0E, 0x3F, 0xF0, 0x3F, 0x80,
+/* 'T' 0x54 */ 0xFF, 0xFF, 0xFF, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60,
+/* 'U' 0x55 */ 0xC0, 0x1E, 0x00, 0xF0, 0x07, 0x80, 0x3C, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x03, 0xC0, 0x1E, 0x00, 0xF0, 0x07, 0x80, 0x3C, 0x01, 0xE0, 0x0F, 0x80, 0xEE, 0x0E, 0x3F, 0xE0, 0xFC, 0x00,
+/* 'V' 0x56 */ 0xC0, 0x0F, 0x00, 0x7E, 0x01, 0x98, 0x06, 0x60, 0x39, 0xC0, 0xC3, 0x03, 0x0C, 0x1C, 0x38, 0x60, 0x61, 0x81, 0x8E, 0x07, 0x30, 0x0C, 0xC0, 0x37, 0x00, 0xF8, 0x01, 0xE0, 0x07, 0x80, 0x1C, 0x00,
+/* 'W' 0x57 */ 0xE0, 0x30, 0x1D, 0x80, 0xE0, 0x76, 0x07, 0x81, 0xDC, 0x1E, 0x06, 0x70, 0x7C, 0x18, 0xC1, 0xB0, 0xE3, 0x0C, 0xC3, 0x8C, 0x33, 0x0C, 0x38, 0xC6, 0x30, 0x67, 0x18, 0xC1, 0x98, 0x67, 0x06, 0x61, 0xD8, 0x1D, 0x83, 0x60, 0x3C, 0x0D, 0x80, 0xF0, 0x3E, 0x03, 0xC0, 0x70, 0x0F, 0x01, 0xC0, 0x18, 0x07, 0x00,
+/* 'X' 0x58 */ 0xE0, 0x1D, 0x80, 0xE7, 0x03, 0x0E, 0x1C, 0x18, 0x60, 0x73, 0x00, 0xFC, 0x01, 0xE0, 0x07, 0x00, 0x1E, 0x00, 0xF8, 0x03, 0x30, 0x1C, 0xE0, 0xE1, 0x83, 0x07, 0x1C, 0x0E, 0xE0, 0x1B, 0x00, 0x70,
+/* 'Y' 0x59 */ 0xC0, 0x0F, 0x80, 0x76, 0x01, 0x9C, 0x0C, 0x38, 0x70, 0x61, 0x81, 0xCE, 0x03, 0x30, 0x0F, 0x80, 0x1E, 0x00, 0x30, 0x00, 0xC0, 0x03, 0x00, 0x0C, 0x00, 0x30, 0x00, 0xC0, 0x03, 0x00, 0x0C, 0x00,
+/* 'Z' 0x5A */ 0xFF, 0xFF, 0xFF, 0xC0, 0x0E, 0x00, 0xE0, 0x0E, 0x00, 0x60, 0x07, 0x00, 0x70, 0x07, 0x00, 0x30, 0x03, 0x80, 0x38, 0x03, 0x80, 0x18, 0x01, 0xC0, 0x1C, 0x00, 0xFF, 0xFF, 0xFF, 0xC0,
+/* '[' 0x5B */ 0xFF, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCF, 0xF0,
+/* '\' 0x5C */ 0x81, 0x81, 0x02, 0x06, 0x04, 0x08, 0x18, 0x10, 0x20, 0x60, 0x40, 0x81, 0x81, 0x02, 0x06, 0x04,
+/* ']' 0x5D */ 0xFF, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x3F, 0xF0,
+/* '^' 0x5E */ 0x0C, 0x0E, 0x05, 0x86, 0xC3, 0x21, 0x19, 0x8C, 0x83, 0xC1, 0x80,
+/* '_' 0x5F */ 0xFF, 0xFE,
+/* '`' 0x60 */ 0xE3, 0x8C, 0x30,
+/* 'a' 0x61 */ 0x3F, 0x07, 0xF8, 0xE1, 0xCC, 0x0C, 0x00, 0xC0, 0x1C, 0x3F, 0xCF, 0x8C, 0xC0, 0xCC, 0x0C, 0xE3, 0xC7, 0xEF, 0x3C, 0x70,
+/* 'b' 0x62 */ 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0xF8, 0xDF, 0xCF, 0x0E, 0xE0, 0x7C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, 0x3C, 0x03, 0xE0, 0x6F, 0x0E, 0xDF, 0xCC, 0xF8,
+/* 'c' 0x63 */ 0x1F, 0x0F, 0xE6, 0x1F, 0x83, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x38, 0x37, 0x1C, 0xFE, 0x1F, 0x00,
+/* 'd' 0x64 */ 0x00, 0x60, 0x0C, 0x01, 0x80, 0x30, 0x06, 0x3C, 0xCF, 0xFB, 0x8F, 0xE0, 0xF8, 0x0F, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xF8, 0x3B, 0x8F, 0x3F, 0x63, 0xCC,
+/* 'e' 0x65 */ 0x1F, 0x07, 0xF1, 0xC7, 0x70, 0x3C, 0x07, 0xFF, 0xFF, 0xFE, 0x00, 0xC0, 0x1C, 0x0D, 0xC3, 0x1F, 0xC1, 0xF0,
+/* 'f' 0x66 */ 0x3B, 0xD8, 0xC6, 0x7F, 0xEC, 0x63, 0x18, 0xC6, 0x31, 0x8C, 0x63, 0x00,
+/* 'g' 0x67 */ 0x1E, 0x67, 0xFD, 0xC7, 0xF0, 0x7C, 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0x7C, 0x1D, 0xC7, 0x9F, 0xB1, 0xE6, 0x00, 0xC0, 0x3E, 0x0E, 0x7F, 0xC7, 0xE0,
+/* 'h' 0x68 */ 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x33, 0xCD, 0xFB, 0xC7, 0xE0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x30,
+/* 'i' 0x69 */ 0xF0, 0x3F, 0xFF, 0xFF, 0xF0,
+/* 'j' 0x6A */ 0x33, 0x00, 0x03, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x3F, 0xE0,
+/* 'k' 0x6B */ 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x30, 0x6C, 0x33, 0x18, 0xCC, 0x37, 0x0F, 0xC3, 0xB8, 0xC6, 0x31, 0xCC, 0x3B, 0x06, 0xC1, 0xF0, 0x30,
+/* 'l' 0x6C */ 0xFF, 0xFF, 0xFF, 0xFF, 0xF0,
+/* 'm' 0x6D */ 0xCF, 0x1F, 0x6F, 0xDF, 0xFC, 0x78, 0xFC, 0x18, 0x3C, 0x0C, 0x1E, 0x06, 0x0F, 0x03, 0x07, 0x81, 0x83, 0xC0, 0xC1, 0xE0, 0x60, 0xF0, 0x30, 0x78, 0x18, 0x3C, 0x0C, 0x18,
+/* 'n' 0x6E */ 0xCF, 0x37, 0xEF, 0x1F, 0x83, 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xC0,
+/* 'o' 0x6F */ 0x1F, 0x07, 0xF1, 0xC7, 0x70, 0x7C, 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0x7C, 0x1D, 0xC7, 0x1F, 0xC1, 0xF0,
+/* 'p' 0x70 */ 0xCF, 0x8D, 0xFC, 0xF0, 0xEE, 0x06, 0xC0, 0x3C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, 0x3E, 0x06, 0xF0, 0xEF, 0xFC, 0xCF, 0x8C, 0x00, 0xC0, 0x0C, 0x00, 0xC0, 0x00,
+/* 'q' 0x71 */ 0x1E, 0x67, 0xFD, 0xC7, 0xF0, 0x7C, 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0x7C, 0x1D, 0xC7, 0x9F, 0xF1, 0xE6, 0x00, 0xC0, 0x18, 0x03, 0x00, 0x60,
+/* 'r' 0x72 */ 0xCF, 0x7F, 0x38, 0xC3, 0x0C, 0x30, 0xC3, 0x0C, 0x30, 0xC0,
+/* 's' 0x73 */ 0x3E, 0x1F, 0xEE, 0x1B, 0x00, 0xC0, 0x3C, 0x07, 0xF0, 0x3F, 0x01, 0xF0, 0x3E, 0x1D, 0xFE, 0x3F, 0x00,
+/* 't' 0x74 */ 0x63, 0x19, 0xFF, 0xB1, 0x8C, 0x63, 0x18, 0xC6, 0x31, 0xE7,
+/* 'u' 0x75 */ 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x7E, 0x3D, 0xFB, 0x3C, 0xC0,
+/* 'v' 0x76 */ 0xE0, 0x6C, 0x0D, 0x81, 0xB8, 0x63, 0x0C, 0x61, 0x8E, 0x60, 0xCC, 0x19, 0x83, 0xE0, 0x3C, 0x07, 0x00, 0xE0,
+/* 'w' 0x77 */ 0xC1, 0xC1, 0xB0, 0xE1, 0xD8, 0x70, 0xCC, 0x2C, 0x66, 0x36, 0x31, 0x9B, 0x18, 0xCD, 0x98, 0x64, 0x6C, 0x16, 0x36, 0x0F, 0x1A, 0x07, 0x8F, 0x03, 0x83, 0x80, 0xC1, 0xC0,
+/* 'x' 0x78 */ 0xC1, 0xF8, 0x66, 0x30, 0xCC, 0x3E, 0x07, 0x00, 0xC0, 0x78, 0x36, 0x0C, 0xC6, 0x3B, 0x06, 0xC0, 0xC0,
+/* 'y' 0x79 */ 0xE0, 0x6C, 0x0D, 0x83, 0x38, 0x63, 0x0C, 0x63, 0x0C, 0x60, 0xCC, 0x1B, 0x03, 0x60, 0x3C, 0x07, 0x00, 0xE0, 0x18, 0x03, 0x00, 0xE0, 0x78, 0x0E, 0x00,
+/* 'z' 0x7A */ 0xFF, 0xFF, 0xF0, 0x18, 0x0C, 0x07, 0x03, 0x81, 0xC0, 0x60, 0x30, 0x18, 0x0E, 0x03, 0xFF, 0xFF, 0xC0,
+/* '{' 0x7B */ 0x19, 0xCC, 0x63, 0x18, 0xC6, 0x31, 0x99, 0x86, 0x18, 0xC6, 0x31, 0x8C, 0x63, 0x1C, 0x60,
+/* '|' 0x7C */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC,
+/* '}' 0x7D */ 0xC7, 0x18, 0xC6, 0x31, 0x8C, 0x63, 0x0C, 0x33, 0x31, 0x8C, 0x63, 0x18, 0xC6, 0x73, 0x00,
+/* '~' 0x7E */ 0x70, 0x3E, 0x09, 0xE4, 0x1F, 0x03, 0x80,
+/* 0x7F */
+/* 0x80 */ 0x01, 0xF0, 0x1F, 0xF0, 0xE0, 0xC7, 0x00, 0x18, 0x00, 0xC0, 0x07, 0xFF, 0x3F, 0xFC, 0x30, 0x01, 0xFF, 0x8F, 0xFC, 0x0C, 0x00, 0x18, 0x00, 0x70, 0x00, 0xE0, 0x81, 0xFE, 0x03, 0xF0,
+/* 0x81 */
+/* 0x82 */ 0xF5, 0x80,
+/* 0x83 */
+/* 0x84 */ 0xCF, 0x34, 0x51, 0x88,
+/* 0x85 */ 0xC6, 0x3C, 0x63,
+/* 0x86 */ 0x0C, 0x03, 0x00, 0xC0, 0x30, 0x0C, 0x3F, 0xFF, 0xFC, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x30, 0x0C, 0x00,
+/* 0x87 */ 0x0C, 0x03, 0x00, 0xC0, 0x30, 0x0C, 0x3F, 0xFF, 0xFC, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x30, 0x0C, 0x03, 0x0F, 0xFF, 0xFF, 0x0C, 0x03, 0x00, 0xC0, 0x30,
+/* 0x88 */
+/* 0x89 */ 0x38, 0x18, 0x00, 0xF8, 0x30, 0x03, 0x18, 0xC0, 0x04, 0x11, 0x80, 0x0C, 0x66, 0x00, 0x0F, 0x8C, 0x00, 0x0E, 0x30, 0x00, 0x00, 0x40, 0x00, 0x01, 0x80, 0x00, 0x06, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x31, 0xC0, 0xE0, 0x67, 0xC3, 0xC1, 0x98, 0xCC, 0xC3, 0x20, 0x90, 0x8C, 0x63, 0x33, 0x10, 0x7C, 0x3C, 0x60, 0x70, 0x38,
+/* 0x8A */ 0x0C, 0x40, 0x1F, 0x00, 0x38, 0x03, 0xF8, 0x1F, 0xF0, 0xE0, 0xE6, 0x01, 0xD8, 0x03, 0x60, 0x01, 0xC0, 0x07, 0x80, 0x0F, 0xE0, 0x0F, 0xF0, 0x03, 0xE0, 0x01, 0xF0, 0x03, 0xC0, 0x0F, 0x80, 0x37, 0x83, 0x8F, 0xFC, 0x0F, 0xE0,
+/* 0x8B */ 0x2F, 0x49, 0x99,
+/* 0x8C */ 0x01, 0x80, 0x0C, 0x00, 0x60, 0x00, 0x00, 0x0F, 0xE0, 0x7F, 0xC3, 0x83, 0x98, 0x07, 0x60, 0x0D, 0x80, 0x07, 0x00, 0x1E, 0x00, 0x3F, 0x80, 0x3F, 0xC0, 0x0F, 0x80, 0x07, 0xC0, 0x0F, 0x00, 0x3E, 0x00, 0xDE, 0x0E, 0x3F, 0xF0, 0x3F, 0x80,
+/* 0x8D */ 0x0C, 0xC0, 0xF8, 0x07, 0x0F, 0xFF, 0xFF, 0xF0, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00,
+/* 0x8E */ 0x0C, 0xC0, 0x3C, 0x00, 0xE1, 0xFF, 0xFF, 0xFF, 0x80, 0x1C, 0x01, 0xC0, 0x1C, 0x00, 0xC0, 0x0E, 0x00, 0xE0, 0x0E, 0x00, 0x60, 0x07, 0x00, 0x70, 0x07, 0x00, 0x30, 0x03, 0x80, 0x38, 0x01, 0xFF, 0xFF, 0xFF, 0x80,
+/* 0x8F */ 0x01, 0x80, 0x18, 0x01, 0x80, 0x00, 0x0F, 0xFF, 0xFF, 0xFC, 0x00, 0xE0, 0x0E, 0x00, 0xE0, 0x06, 0x00, 0x70, 0x07, 0x00, 0x70, 0x03, 0x00, 0x38, 0x03, 0x80, 0x38, 0x01, 0x80, 0x1C, 0x01, 0xC0, 0x0F, 0xFF, 0xFF, 0xFC,
+/* 0x90 */
+/* 0x91 */ 0x6A, 0xF0,
+/* 0x92 */ 0xF5, 0x60,
+/* 0x93 */ 0x4E, 0x28, 0xA2, 0xCF, 0x30,
+/* 0x94 */ 0xCF, 0x34, 0x51, 0x4E, 0x20,
+/* 0x95 */ 0x7B, 0xFF, 0xFF, 0xFD, 0xE0,
+/* 0x96 */ 0xFF, 0xFF, 0xF0,
+/* 0x97 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0,
+/* 0x98 */
+/* 0x99 */ 0xFF, 0x70, 0x1F, 0xFD, 0xC0, 0x71, 0x87, 0x83, 0xC6, 0x1E, 0x0F, 0x18, 0x68, 0x3C, 0x61, 0xB1, 0xB1, 0x86, 0xC6, 0xC6, 0x19, 0x1B, 0x18, 0x66, 0xCC, 0x61, 0x9B, 0x31, 0x86, 0x3C, 0xC6, 0x18, 0xE3, 0x18, 0x63, 0x8C,
+/* 0x9A */ 0x63, 0x0D, 0x83, 0x60, 0x70, 0x00, 0x0F, 0x87, 0xFB, 0x86, 0xC0, 0x30, 0x0F, 0x01, 0xFC, 0x0F, 0xC0, 0x7C, 0x0F, 0x87, 0x7F, 0x8F, 0xC0,
+/* 0x9B */ 0x99, 0x92, 0xF4,
+/* 0x9C */ 0x07, 0x03, 0x80, 0xC0, 0x60, 0x00, 0x0F, 0x87, 0xFB, 0x86, 0xC0, 0x30, 0x0F, 0x01, 0xFC, 0x0F, 0xC0, 0x7C, 0x0F, 0x87, 0x7F, 0x8F, 0xC0,
+/* 0x9D */ 0x03, 0x06, 0x66, 0x64, 0x60, 0xF8, 0xF8, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x78, 0x38,
+/* 0x9E */ 0x63, 0x0C, 0x83, 0x60, 0x70, 0x00, 0x3F, 0xFF, 0xFC, 0x06, 0x03, 0x01, 0xC0, 0xE0, 0x70, 0x18, 0x0C, 0x06, 0x03, 0x80, 0xFF, 0xFF, 0xF0,
+/* 0x9F */ 0x07, 0x01, 0x80, 0xC0, 0x20, 0x00, 0x3F, 0xFF, 0xFC, 0x06, 0x03, 0x01, 0xC0, 0xE0, 0x70, 0x18, 0x0C, 0x06, 0x03, 0x80, 0xFF, 0xFF, 0xF0,
+/* 0xA0 */
+/* 0xA1 */ 0xC6, 0xD9, 0xB1, 0xC0,
+/* 0xA2 */ 0x83, 0x8D, 0xF1, 0xC0,
+/* 0xA3 */ 0x30, 0x01, 0x80, 0x0C, 0x00, 0x60, 0x03, 0x00, 0x18, 0x80, 0xCC, 0x06, 0xC0, 0x3C, 0x01, 0xC0, 0x3C, 0x01, 0x60, 0x03, 0x00, 0x18, 0x00, 0xC0, 0x06, 0x00, 0x3F, 0xF9, 0xFF, 0xC0,
+/* 0xA4 */ 0xDD, 0xFF, 0xD8, 0xD8, 0x3C, 0x1E, 0x0F, 0x8D, 0xFF, 0xDD, 0x80,
+/* 0xA5 */ 0x03, 0x80, 0x03, 0xC0, 0x07, 0xC0, 0x07, 0xC0, 0x04, 0xE0, 0x0C, 0xE0, 0x0C, 0xE0, 0x08, 0x70, 0x18, 0x70, 0x18, 0x70, 0x10, 0x38, 0x3F, 0xF8, 0x3F, 0xF8, 0x30, 0x1C, 0x70, 0x0C, 0x60, 0x0C, 0x60, 0x0E, 0xE0, 0x06, 0x00, 0x0E, 0x00, 0x18, 0x00, 0x18, 0x00, 0x18, 0x00, 0x0F,
+/* 0xA6 */ 0xFF, 0xFF, 0xF0, 0x3F, 0xFF, 0xFC,
+/* 0xA7 */ 0x0F, 0x03, 0xF0, 0xE7, 0x18, 0x63, 0x0C, 0x70, 0x07, 0x03, 0xF8, 0xC3, 0x98, 0x3B, 0x03, 0xF0, 0x37, 0x06, 0x78, 0xC7, 0xB0, 0x7C, 0x03, 0x80, 0x39, 0x83, 0x30, 0x67, 0x1C, 0x7F, 0x07, 0xC0,
+/* 0xA8 */ 0xCF, 0x30,
+/* 0xA9 */ 0x03, 0xF0, 0x03, 0xFF, 0x01, 0xE0, 0xE0, 0xE3, 0x1C, 0x73, 0xF3, 0x99, 0x86, 0x6C, 0xC1, 0x8F, 0x30, 0x03, 0xCC, 0x00, 0xF3, 0x00, 0x3C, 0xC1, 0x8D, 0x98, 0x66, 0x77, 0xF3, 0x8E, 0x79, 0xC1, 0xC0, 0xE0, 0x3F, 0xF0, 0x03, 0xF0, 0x00,
+/* 0xAA */ 0x0F, 0xC0, 0xFF, 0xC3, 0x03, 0x98, 0x07, 0x60, 0x0D, 0x80, 0x07, 0x00, 0x1E, 0x00, 0x3F, 0x80, 0x3F, 0xC0, 0x0F, 0x80, 0x07, 0xC0, 0x0F, 0x00, 0x3E, 0x00, 0xDC, 0x0E, 0x3F, 0xF0, 0x3F, 0x00, 0x20, 0x01, 0xE0, 0x01, 0x80, 0x06, 0x00, 0xF0, 0x00,
+/* 0xAB */ 0x21, 0x63, 0xE7, 0x84, 0x84, 0xE7, 0x63, 0x21,
+/* 0xAC */ 0xFF, 0xFF, 0xFF, 0x00, 0x30, 0x03, 0x00, 0x30, 0x03,
+/* 0xAD */ 0xFF, 0xF0,
+/* 0xAE */ 0x03, 0xF0, 0x03, 0xFF, 0x01, 0xE0, 0xE0, 0xFF, 0x1C, 0x7F, 0xF3, 0x9B, 0x04, 0x6C, 0xC1, 0x8F, 0x30, 0x43, 0xCF, 0xF0, 0xF3, 0xFC, 0x3C, 0xC1, 0x0D, 0xB0, 0x66, 0x7C, 0x1B, 0x8F, 0x07, 0xC1, 0xC0, 0xE0, 0x3F, 0xF0, 0x03, 0xF0, 0x00,
+/* 0xAF */ 0x03, 0x00, 0x18, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0x80, 0x1C, 0x01, 0xC0, 0x1C, 0x00, 0xC0, 0x0E, 0x00, 0xE0, 0x0E, 0x00, 0x60, 0x07, 0x00, 0x70, 0x07, 0x00, 0x30, 0x03, 0x80, 0x38, 0x01, 0xFF, 0xFF, 0xFF, 0x80,
+/* 0xB0 */ 0x38, 0xFB, 0x1C, 0x18, 0x38, 0xDF, 0x1C,
+/* 0xB1 */ 0x06, 0x00, 0x60, 0x06, 0x00, 0x60, 0x7F, 0xE7, 0xFE, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xF0,
+/* 0xB2 */ 0x76, 0x31, 0x87, 0x80,
+/* 0xB3 */ 0x66, 0x66, 0x66, 0x67, 0x7E, 0xE6, 0x66, 0x66, 0x66,
+/* 0xB4 */ 0x3B, 0x99, 0x80,
+/* 0xB5 */ 0xC0, 0xCC, 0x0C, 0xC0, 0xCC, 0x0C, 0xC0, 0xCC, 0x0C, 0xC0, 0xCC, 0x0C, 0xC0, 0xCC, 0x1C, 0xE3, 0xCF, 0xEF, 0xFC, 0x7C, 0x00, 0xC0, 0x0C, 0x00, 0xC0, 0x00,
+/* 0xB6 */ 0x1F, 0xE7, 0xFD, 0xF3, 0x7E, 0x6F, 0xCD, 0xF9, 0xBF, 0x37, 0xE6, 0x7C, 0xCF, 0x98, 0xF3, 0x06, 0x60, 0xCC, 0x19, 0x83, 0x30, 0x66, 0x0C, 0xC1, 0x98, 0x33, 0x06, 0x60, 0xCC,
+/* 0xB7 */ 0xF0,
+/* 0xB8 */ 0x10, 0xF0, 0xE3, 0x78,
+/* 0xB9 */ 0x1F, 0x01, 0xFC, 0x1C, 0x70, 0xC1, 0x80, 0x0C, 0x00, 0xE0, 0xFF, 0x1F, 0x18, 0xC0, 0xC6, 0x06, 0x38, 0x70, 0xFF, 0xE3, 0xC7, 0x00, 0x30, 0x03, 0x00, 0x18, 0x00, 0xC0, 0x03, 0xC0,
+/* 0xBA */ 0x3F, 0x1F, 0xEE, 0x1B, 0x00, 0xC0, 0x3C, 0x07, 0xF0, 0x3E, 0x01, 0xF0, 0x3C, 0x0D, 0xDE, 0x7F, 0x02, 0x01, 0xE0, 0x18, 0x46, 0x0F, 0x00,
+/* 0xBB */ 0x88, 0xC6, 0xE7, 0x21, 0x21, 0xE7, 0xC6, 0x88,
+/* 0xBC */ 0xC3, 0x31, 0x8C, 0x63, 0x10, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xFF, 0xFF, 0xF0,
+/* 0xBD */ 0x77, 0x66, 0xCC, 0xC8,
+/* 0xBE */ 0xC7, 0x9B, 0x36, 0xCC, 0x18, 0x30, 0x60, 0xC1, 0x83, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC1, 0x80,
+/* 0xBF */ 0x0C, 0x03, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xF0, 0x18, 0x0C, 0x07, 0x03, 0x81, 0xC0, 0x60, 0x30, 0x18, 0x0E, 0x03, 0xFF, 0xFF, 0xC0,
+/* 0xC0 */ 0x03, 0x80, 0x18, 0x00, 0x40, 0x00, 0x00, 0xFF, 0xE3, 0xFF, 0xCC, 0x03, 0xB0, 0x06, 0xC0, 0x1B, 0x00, 0x6C, 0x01, 0xB0, 0x0C, 0xFF, 0xE3, 0xFF, 0xCC, 0x03, 0xB0, 0x06, 0xC0, 0x1B, 0x00, 0x6C, 0x01, 0xB0, 0x06, 0xC0, 0x1B, 0x00, 0x70,
+/* 0xC1 */ 0x01, 0xC0, 0x0C, 0x00, 0x20, 0x00, 0x00, 0x07, 0x80, 0x1E, 0x00, 0x78, 0x03, 0xF0, 0x0C, 0xC0, 0x33, 0x01, 0xCE, 0x06, 0x18, 0x18, 0x60, 0xE1, 0xC3, 0x03, 0x0F, 0xFC, 0x7F, 0xF9, 0x80, 0x66, 0x01, 0xB8, 0x07, 0xC0, 0x0F, 0x00, 0x30,
+/* 0xC2 */ 0x07, 0x00, 0x3E, 0x01, 0x8C, 0x00, 0x00, 0x07, 0x80, 0x1E, 0x00, 0x78, 0x03, 0xF0, 0x0C, 0xC0, 0x33, 0x01, 0xCE, 0x06, 0x18, 0x18, 0x60, 0xE1, 0xC3, 0x03, 0x0F, 0xFC, 0x7F, 0xF9, 0x80, 0x66, 0x01, 0xB8, 0x07, 0xC0, 0x0F, 0x00, 0x30,
+/* 0xC3 */ 0x10, 0x40, 0x63, 0x00, 0xF8, 0x01, 0xE0, 0x07, 0x80, 0x1E, 0x00, 0x78, 0x03, 0xF0, 0x0C, 0xC0, 0x33, 0x01, 0xCE, 0x06, 0x18, 0x18, 0x60, 0xE1, 0xC3, 0x03, 0x0F, 0xFC, 0x7F, 0xF9, 0x80, 0x66, 0x01, 0xB8, 0x07, 0xC0, 0x0F, 0x00, 0x30,
+/* 0xC4 */ 0x0C, 0xC0, 0x33, 0x00, 0x00, 0x01, 0xE0, 0x07, 0x80, 0x1E, 0x00, 0xFC, 0x03, 0x30, 0x0C, 0xC0, 0x73, 0x81, 0x86, 0x06, 0x18, 0x38, 0x70, 0xC0, 0xC3, 0xFF, 0x1F, 0xFE, 0x60, 0x19, 0x80, 0x6E, 0x01, 0xF0, 0x03, 0xC0, 0x0C,
+/* 0xC5 */ 0x18, 0x0C, 0x06, 0x00, 0x00, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xFF, 0xFF, 0xF0,
+/* 0xC6 */ 0x00, 0xC0, 0x03, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x7E, 0x03, 0xFF, 0x0E, 0x07, 0x38, 0x07, 0x60, 0x06, 0xC0, 0x03, 0x00, 0x06, 0x00, 0x0C, 0x00, 0x18, 0x00, 0x30, 0x00, 0x60, 0x00, 0xE0, 0x06, 0xC0, 0x0D, 0xC0, 0x31, 0xE0, 0xE1, 0xFF, 0x80, 0xFC, 0x00,
+/* 0xC7 */ 0x07, 0xE0, 0x3F, 0xF0, 0xE0, 0x73, 0x80, 0x66, 0x00, 0x7C, 0x00, 0x30, 0x00, 0x60, 0x00, 0xC0, 0x01, 0x80, 0x03, 0x00, 0x06, 0x00, 0x0E, 0x00, 0x6C, 0x01, 0xDC, 0x03, 0x1C, 0x1E, 0x1F, 0xF8, 0x0F, 0xC0, 0x08, 0x00, 0x1E, 0x00, 0x0C, 0x01, 0x18, 0x01, 0xE0, 0x00,
+/* 0xC8 */ 0x06, 0x30, 0x07, 0xC0, 0x07, 0x00, 0x3F, 0x01, 0xFF, 0x87, 0x03, 0x9C, 0x03, 0xB0, 0x03, 0x60, 0x01, 0x80, 0x03, 0x00, 0x06, 0x00, 0x0C, 0x00, 0x18, 0x00, 0x30, 0x00, 0x70, 0x03, 0x60, 0x06, 0xE0, 0x18, 0xF0, 0x70, 0xFF, 0xC0, 0x7E, 0x00,
+/* 0xC9 */ 0x07, 0x00, 0x60, 0x0C, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00, 0xFF, 0xEF, 0xFE, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00, 0xFF, 0xFF, 0xFF,
+/* 0xCA */ 0xFF, 0xE7, 0xFF, 0x30, 0x01, 0x80, 0x0C, 0x00, 0x60, 0x03, 0x00, 0x18, 0x00, 0xFF, 0xE7, 0xFF, 0x30, 0x01, 0x80, 0x0C, 0x00, 0x60, 0x03, 0x00, 0x18, 0x00, 0xFF, 0xF7, 0xFF, 0x80, 0x18, 0x01, 0x80, 0x0C, 0x00, 0x60, 0x01, 0xE0,
+/* 0xCB */ 0x19, 0x81, 0x98, 0x00, 0x0F, 0xFF, 0xFF, 0xFC, 0x00, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00, 0xC0, 0x0F, 0xFE, 0xFF, 0xEC, 0x00, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00, 0xC0, 0x0F, 0xFF, 0xFF, 0xF0,
+/* 0xCC */ 0x08, 0xC0, 0xF8, 0x07, 0x0F, 0xFF, 0xFF, 0xFC, 0x00, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00, 0xC0, 0x0F, 0xFE, 0xFF, 0xEC, 0x00, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00, 0xC0, 0x0F, 0xFF, 0xFF, 0xF0,
+/* 0xCD */ 0x36, 0xC0, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
+/* 0xCE */ 0x39, 0xFC, 0x40, 0x30, 0xC3, 0x0C, 0x30, 0xC3, 0x0C, 0x30, 0xC3, 0x0C, 0x30, 0xC3, 0x0C, 0x30, 0xC0,
+/* 0xCF */ 0x18, 0xC0, 0x3E, 0x00, 0x70, 0x3F, 0xF0, 0xFF, 0xE3, 0x01, 0xCC, 0x03, 0xB0, 0x06, 0xC0, 0x1F, 0x00, 0x3C, 0x00, 0xF0, 0x03, 0xC0, 0x0F, 0x00, 0x3C, 0x00, 0xF0, 0x07, 0xC0, 0x1B, 0x00, 0xEC, 0x07, 0x3F, 0xF8, 0xFF, 0x80,
+/* 0xD0 */ 0x7F, 0xE0, 0xFF, 0xE1, 0x80, 0xE3, 0x00, 0xE6, 0x00, 0xCC, 0x01, 0xD8, 0x01, 0xB0, 0x03, 0xFE, 0x07, 0xFC, 0x0D, 0x80, 0x1B, 0x00, 0x36, 0x00, 0x6C, 0x01, 0x98, 0x07, 0x30, 0x1C, 0x7F, 0xF0, 0xFF, 0xC0,
+/* 0xD1 */ 0x01, 0x80, 0x18, 0x01, 0x80, 0x00, 0x0E, 0x01, 0xF0, 0x0F, 0xC0, 0x7E, 0x03, 0xD8, 0x1E, 0xE0, 0xF3, 0x07, 0x9C, 0x3C, 0x61, 0xE1, 0x8F, 0x0E, 0x78, 0x33, 0xC1, 0xDE, 0x06, 0xF0, 0x1F, 0x80, 0xFC, 0x03, 0xE0, 0x1C,
+/* 0xD2 */ 0x0C, 0xC0, 0x3C, 0x00, 0xE1, 0xC0, 0x3E, 0x01, 0xF8, 0x0F, 0xC0, 0x7B, 0x03, 0xDC, 0x1E, 0x60, 0xF3, 0x87, 0x8C, 0x3C, 0x31, 0xE1, 0xCF, 0x06, 0x78, 0x3B, 0xC0, 0xDE, 0x03, 0xF0, 0x1F, 0x80, 0x7C, 0x03, 0x80,
+/* 0xD3 */ 0x00, 0xE0, 0x00, 0x60, 0x00, 0x40, 0x00, 0x00, 0x00, 0x7F, 0x00, 0xFF, 0xE0, 0xF0, 0x78, 0x60, 0x0C, 0x60, 0x03, 0x30, 0x01, 0xB0, 0x00, 0x78, 0x00, 0x3C, 0x00, 0x1E, 0x00, 0x0F, 0x00, 0x07, 0x80, 0x03, 0x60, 0x03, 0x30, 0x01, 0x8C, 0x01, 0x87, 0x83, 0xC1, 0xFF, 0xC0, 0x3F, 0x80,
+/* 0xD4 */ 0x03, 0xC0, 0x01, 0xE0, 0x01, 0x98, 0x00, 0x00, 0x00, 0x7F, 0x00, 0xFF, 0xE0, 0xF0, 0x78, 0x60, 0x0C, 0x60, 0x03, 0x30, 0x01, 0xB0, 0x00, 0x78, 0x00, 0x3C, 0x00, 0x1E, 0x00, 0x0F, 0x00, 0x07, 0x80, 0x03, 0x60, 0x03, 0x30, 0x01, 0x8C, 0x01, 0x87, 0x83, 0xC1, 0xFF, 0xC0, 0x3F, 0x80,
+/* 0xD5 */ 0x03, 0xB8, 0x01, 0x98, 0x00, 0x98, 0x00, 0x00, 0x00, 0x7F, 0x00, 0xFF, 0xE0, 0xF0, 0x78, 0x60, 0x0C, 0x60, 0x03, 0x30, 0x01, 0xB0, 0x00, 0x78, 0x00, 0x3C, 0x00, 0x1E, 0x00, 0x0F, 0x00, 0x07, 0x80, 0x03, 0x60, 0x03, 0x30, 0x01, 0x8C, 0x01, 0x87, 0x83, 0xC1, 0xFF, 0xC0, 0x3F, 0x80,
+/* 0xD6 */ 0x06, 0x30, 0x03, 0x18, 0x00, 0x00, 0x00, 0xFE, 0x01, 0xFF, 0xC1, 0xE0, 0xF0, 0xC0, 0x18, 0xC0, 0x06, 0x60, 0x03, 0x60, 0x00, 0xF0, 0x00, 0x78, 0x00, 0x3C, 0x00, 0x1E, 0x00, 0x0F, 0x00, 0x06, 0xC0, 0x06, 0x60, 0x03, 0x18, 0x03, 0x0F, 0x07, 0x83, 0xFF, 0x80, 0x7F, 0x00,
+/* 0xD7 */ 0x81, 0xC3, 0x66, 0x3C, 0x18, 0x3C, 0x66, 0xC3, 0x81,
+/* 0xD8 */ 0x0C, 0xC0, 0x1E, 0x00, 0x78, 0x3F, 0xF8, 0xFF, 0xF3, 0x00, 0xEC, 0x01, 0xB0, 0x06, 0xC0, 0x1B, 0x00, 0x6C, 0x03, 0x3F, 0xF8, 0xFF, 0xF3, 0x00, 0xEC, 0x01, 0xB0, 0x06, 0xC0, 0x1B, 0x00, 0x6C, 0x01, 0xB0, 0x06, 0xC0, 0x1C,
+/* 0xD9 */ 0x03, 0x00, 0x7C, 0x03, 0x70, 0x19, 0x80, 0xF8, 0x03, 0xC3, 0x00, 0x78, 0x03, 0xC0, 0x1E, 0x00, 0xF0, 0x07, 0x80, 0x3C, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x03, 0xC0, 0x1E, 0x00, 0xF0, 0x07, 0x80, 0x3E, 0x03, 0xB8, 0x38, 0xFF, 0x83, 0xF0,
+/* 0xDA */ 0x03, 0x80, 0x18, 0x01, 0x80, 0x00, 0x0C, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x03, 0xC0, 0x1E, 0x00, 0xF0, 0x07, 0x80, 0x3C, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x03, 0xC0, 0x1E, 0x00, 0xF8, 0x0E, 0xE0, 0xE3, 0xFE, 0x0F, 0xC0,
+/* 0xDB */ 0x0E, 0xE0, 0x66, 0x03, 0x60, 0x00, 0x0C, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x03, 0xC0, 0x1E, 0x00, 0xF0, 0x07, 0x80, 0x3C, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x03, 0xC0, 0x1E, 0x00, 0xF8, 0x0E, 0xE0, 0xE3, 0xFE, 0x0F, 0xC0,
+/* 0xDC */ 0x0C, 0xC0, 0x66, 0x00, 0x01, 0x80, 0x3C, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x03, 0xC0, 0x1E, 0x00, 0xF0, 0x07, 0x80, 0x3C, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x03, 0xC0, 0x1F, 0x01, 0xDC, 0x1C, 0x7F, 0xC1, 0xF8, 0x00,
+/* 0xDD */ 0x01, 0x80, 0x0C, 0x00, 0x60, 0x00, 0x00, 0xC0, 0x0F, 0x80, 0x76, 0x01, 0x9C, 0x0C, 0x38, 0x70, 0x61, 0x81, 0xCE, 0x03, 0x30, 0x0F, 0x80, 0x1E, 0x00, 0x30, 0x00, 0xC0, 0x03, 0x00, 0x0C, 0x00, 0x30, 0x00, 0xC0, 0x03, 0x00, 0x0C, 0x00,
+/* 0xDE */ 0xFF, 0xFF, 0xFF, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60, 0x04, 0x00, 0x78, 0x01, 0x81, 0x18, 0x0F, 0x00,
+/* 0xDF */ 0x1F, 0x0F, 0xF3, 0x87, 0x60, 0x6C, 0x0D, 0x81, 0xB0, 0x66, 0x38, 0xC7, 0xD8, 0x1B, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xF0, 0x3E, 0x0E, 0xCF, 0x99, 0xE0,
+/* 0xE0 */ 0x0C, 0x61, 0x8C, 0x03, 0x3D, 0xFC, 0xE3, 0x0C, 0x30, 0xC3, 0x0C, 0x30, 0xC3, 0x00,
+/* 0xE1 */ 0x07, 0x00, 0x60, 0x0C, 0x01, 0x80, 0x00, 0x03, 0xF0, 0x7F, 0x8E, 0x1C, 0xC0, 0xC0, 0x0C, 0x01, 0xC3, 0xFC, 0xF8, 0xCC, 0x0C, 0xC0, 0xCE, 0x3C, 0x7E, 0xF3, 0xC7,
+/* 0xE2 */ 0x0C, 0x01, 0xE0, 0x1B, 0x03, 0x30, 0x00, 0x03, 0xF0, 0x7F, 0x8E, 0x1C, 0xC0, 0xC0, 0x0C, 0x01, 0xC3, 0xFC, 0xF8, 0xCC, 0x0C, 0xC0, 0xCE, 0x3C, 0x7E, 0xF3, 0xC7,
+/* 0xE3 */ 0x20, 0x82, 0x10, 0x3F, 0x01, 0xE0, 0x00, 0x03, 0xF0, 0x7F, 0x8E, 0x1C, 0xC0, 0xC0, 0x0C, 0x01, 0xC3, 0xFC, 0xF8, 0xCC, 0x0C, 0xC0, 0xCE, 0x3C, 0x7E, 0xF3, 0xC7,
+/* 0xE4 */ 0x19, 0x81, 0x98, 0x00, 0x00, 0x00, 0x3F, 0x07, 0xF8, 0xE1, 0xCC, 0x0C, 0x00, 0xC0, 0x1C, 0x3F, 0xCF, 0x8C, 0xC0, 0xCC, 0x0C, 0xE3, 0xC7, 0xEF, 0x3C, 0x70,
+/* 0xE5 */ 0x3B, 0x30, 0x06, 0x31, 0x8C, 0x63, 0x18, 0xC6, 0x31, 0x8C, 0x63, 0x18, 0xC6, 0x30,
+/* 0xE6 */ 0x07, 0x01, 0x80, 0xC0, 0x20, 0x00, 0x07, 0xC3, 0xF9, 0x87, 0xE0, 0xF0, 0x0C, 0x03, 0x00, 0xC0, 0x30, 0x0E, 0x0D, 0xC7, 0x3F, 0x87, 0xC0,
+/* 0xE7 */ 0x1F, 0x0F, 0xE7, 0x1D, 0x83, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x38, 0x37, 0x1C, 0xFE, 0x1F, 0x02, 0x00, 0xE0, 0x0C, 0x23, 0x07, 0x80,
+/* 0xE8 */ 0x21, 0x0C, 0xC1, 0xE0, 0x78, 0x00, 0x07, 0xC3, 0xF9, 0x87, 0xE0, 0xF0, 0x0C, 0x03, 0x00, 0xC0, 0x30, 0x0E, 0x0D, 0xC7, 0x3F, 0x87, 0xC0,
+/* 0xE9 */ 0x03, 0x00, 0xC0, 0x30, 0x04, 0x00, 0x00, 0x3E, 0x0F, 0xE3, 0x8E, 0xE0, 0x78, 0x0F, 0xFF, 0xFF, 0xFC, 0x01, 0x80, 0x38, 0x1B, 0x86, 0x3F, 0x83, 0xE0,
+/* 0xEA */ 0x1F, 0x07, 0xF1, 0x87, 0x60, 0x6C, 0x07, 0xFF, 0xFF, 0xFE, 0x00, 0xC0, 0x1C, 0x1D, 0x87, 0x1F, 0xC1, 0xF8, 0x06, 0x01, 0x80, 0x30, 0x06, 0x00, 0x78,
+/* 0xEB */ 0x31, 0x86, 0x30, 0x00, 0x00, 0x01, 0xF0, 0x7F, 0x1C, 0x77, 0x03, 0xC0, 0x7F, 0xFF, 0xFF, 0xE0, 0x0C, 0x01, 0xC0, 0xDC, 0x31, 0xFC, 0x1F, 0x00,
+/* 0xEC */ 0x31, 0x82, 0x60, 0x6C, 0x07, 0x00, 0x00, 0x3E, 0x0F, 0xE3, 0x8E, 0xE0, 0x78, 0x0F, 0xFF, 0xFF, 0xFC, 0x01, 0x80, 0x38, 0x1B, 0x86, 0x3F, 0x83, 0xE0,
+/* 0xED */ 0x39, 0x99, 0x80, 0x18, 0xC6, 0x31, 0x8C, 0x63, 0x18, 0xC6, 0x31, 0x80,
+/* 0xEE */ 0x71, 0xED, 0xA3, 0x00, 0xC3, 0x0C, 0x30, 0xC3, 0x0C, 0x30, 0xC3, 0x0C, 0x30, 0xC0,
+/* 0xEF */ 0x00, 0x67, 0x00, 0x66, 0x00, 0x64, 0x00, 0x6C, 0x00, 0x60, 0x1E, 0x60, 0x3F, 0xE0, 0x71, 0xE0, 0xE0, 0xE0, 0xC0, 0x60, 0xC0, 0x60, 0xC0, 0x60, 0xC0, 0x60, 0xC0, 0x60, 0xE0, 0xE0, 0x71, 0xE0, 0x3F, 0x60, 0x1E, 0x60,
+/* 0xF0 */ 0x00, 0x60, 0x06, 0x03, 0xF0, 0x06, 0x00, 0x61, 0xE6, 0x3F, 0xE7, 0x1E, 0xE0, 0xEC, 0x06, 0xC0, 0x6C, 0x06, 0xC0, 0x6C, 0x06, 0xE0, 0xE7, 0x1E, 0x3F, 0xE1, 0xE6,
+/* 0xF1 */ 0x03, 0x81, 0xC0, 0x60, 0x30, 0x00, 0x33, 0xCD, 0xFB, 0xC7, 0xE0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x30,
+/* 0xF2 */ 0x31, 0x84, 0xC1, 0xA0, 0x38, 0x00, 0x33, 0xCD, 0xFB, 0xC7, 0xE0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x30,
+/* 0xF3 */ 0x07, 0x00, 0xC0, 0x30, 0x04, 0x00, 0x00, 0x3E, 0x0F, 0xE3, 0x8E, 0xE0, 0xF8, 0x0F, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xF8, 0x3B, 0x8E, 0x3F, 0x83, 0xE0,
+/* 0xF4 */ 0x0C, 0x03, 0xC0, 0xD8, 0x19, 0x80, 0x00, 0x3E, 0x0F, 0xE3, 0x8E, 0xE0, 0xF8, 0x0F, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xF8, 0x3B, 0x8E, 0x3F, 0x83, 0xE0,
+/* 0xF5 */ 0x0C, 0xC3, 0xB8, 0x66, 0x0D, 0x80, 0x00, 0x3E, 0x0F, 0xE3, 0x8E, 0xE0, 0xF8, 0x0F, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xF8, 0x3B, 0x8E, 0x3F, 0x83, 0xE0,
+/* 0xF6 */ 0x31, 0x86, 0x30, 0x00, 0x00, 0x01, 0xF0, 0x7F, 0x1C, 0x77, 0x07, 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x3C, 0x07, 0xC1, 0xDC, 0x71, 0xFC, 0x1F, 0x00,
+/* 0xF7 */ 0x06, 0x00, 0x60, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x06, 0x00,
+/* 0xF8 */ 0xC7, 0x37, 0x8E, 0x03, 0x3D, 0xFC, 0xE3, 0x0C, 0x30, 0xC3, 0x0C, 0x30, 0xC3, 0x00,
+/* 0xF9 */ 0x0E, 0x07, 0xC1, 0xB8, 0x6C, 0x1F, 0x03, 0x8C, 0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x0F, 0x07, 0xE3, 0xDF, 0xB3, 0xCC,
+/* 0xFA */ 0x03, 0x01, 0x80, 0x60, 0x30, 0x00, 0x30, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x1F, 0x8F, 0x7E, 0xCF, 0x30,
+/* 0xFB */ 0x1D, 0xC6, 0x61, 0xB0, 0xCC, 0x00, 0x30, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x1F, 0x8F, 0x7E, 0xCF, 0x30,
+/* 0xFC */ 0x31, 0x8C, 0x60, 0x00, 0x00, 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x7E, 0x3D, 0xFB, 0x3C, 0xC0,
+/* 0xFD */ 0x03, 0x80, 0x60, 0x18, 0x06, 0x00, 0x01, 0xC0, 0xD8, 0x1B, 0x06, 0x70, 0xC6, 0x18, 0xC6, 0x18, 0xC1, 0x98, 0x36, 0x06, 0xC0, 0x78, 0x0E, 0x01, 0xC0, 0x30, 0x06, 0x01, 0xC0, 0xF0, 0x1C, 0x00,
+/* 0xFE */ 0x61, 0x86, 0x3E, 0xF9, 0x86, 0x18, 0x61, 0x86, 0x18, 0x61, 0x87, 0x8E, 0x10, 0x83, 0xC3, 0x78,
+/* 0xFF */ 0xF0,
+};
+
+const GFXglyph FreeSans12pt_Win1250Glyphs[] PROGMEM = {
+/* 0x01 */ { 0, 19, 20, 21, 1, -17 },
+/* 0x02 */ { 48, 19, 20, 21, 1, -17 },
+/* 0x03 */ { 96, 21, 20, 23, 1, -17 },
+/* 0x04 */ { 149, 21, 20, 23, 1, -17 },
+/* 0x05 */ { 202, 20, 20, 22, 1, -17 },
+/* 0x06 */ { 252, 20, 20, 22, 1, -17 },
+/* 0x07 */ { 302, 0, 0, 8, 0, 0 },
+/* 0x08 */ { 302, 23, 20, 25, 1, -17 },
+/* 0x09 */ { 360, 23, 16, 25, 1, -16 },
+/* 0x0A */ { 406, 0, 0, 8, 0, 0 },
+/* 0x0B */ { 406, 21, 20, 23, 1, -17 },
+/* 0x0C */ { 459, 19, 18, 21, 1, -15 },
+/* 0x0D */ { 502, 0, 0, 8, 0, 0 },
+/* 0x0E */ { 502, 20, 20, 22, 1, -17 },
+/* 0x0F */ { 552, 21, 21, 23, 1, -18 },
+/* 0x10 */ { 608, 19, 20, 21, 1, -17 },
+/* 0x11 */ { 656, 21, 20, 23, 1, -17 },
+/* 0x12 */ { 709, 20, 20, 22, 1, -17 },
+/* 0x13 */ { 759, 21, 20, 23, 1, -17 },
+/* 0x14 */ { 812, 21, 20, 23, 1, -17 },
+/* 0x15 */ { 865, 22, 20, 24, 1, -17 },
+/* 0x16 */ { 920, 16, 20, 18, 1, -17 },
+/* 0x17 */ { 960, 21, 20, 23, 1, -17 },
+/* 0x18 */ { 1013, 23, 20, 25, 1, -17 },
+/* 0x19 */ { 1071, 21, 20, 23, 1, -17 },
+/* 0x1A */ { 1124, 15, 19, 17, 1, -16 },
+/* 0x1B */ { 1160, 24, 21, 26, 1, -18 },
+/* 0x1C */ { 1223, 21, 20, 23, 1, -17 },
+/* 0x1D */ { 1276, 21, 21, 23, 1, -18 },
+/* 0x1E */ { 1332, 20, 20, 22, 1, -17 },
+/* 0x1F */ { 1382, 15, 20, 17, 1, -17 },
+/* ' ' 0x20 */ { 1420, 0, 0, 6, 0, 0 },
+/* '!' 0x21 */ { 1420, 2, 18, 8, 3, -16 },
+/* '"' 0x22 */ { 1425, 6, 6, 8, 1, -15 },
+/* '#' 0x23 */ { 1430, 13, 16, 13, 0, -14 },
+/* '$' 0x24 */ { 1456, 11, 20, 13, 1, -16 },
+/* '%' 0x25 */ { 1484, 20, 17, 21, 1, -15 },
+/* '&' 0x26 */ { 1527, 14, 17, 16, 1, -15 },
+/* ''' 0x27 */ { 1557, 2, 6, 5, 1, -15 },
+/* '(' 0x28 */ { 1559, 5, 23, 8, 2, -16 },
+/* ')' 0x29 */ { 1574, 5, 23, 8, 1, -16 },
+/* '*' 0x2A */ { 1589, 7, 7, 9, 1, -16 },
+/* '+' 0x2B */ { 1596, 10, 11, 14, 2, -9 },
+/* ',' 0x2C */ { 1610, 2, 6, 7, 2, 0 },
+/* '-' 0x2D */ { 1612, 6, 2, 8, 1, -6 },
+/* '.' 0x2E */ { 1614, 2, 2, 6, 2, 0 },
+/* '/' 0x2F */ { 1615, 7, 18, 7, 0, -16 },
+/* '0' 0x30 */ { 1631, 11, 17, 13, 1, -15 },
+/* '1' 0x31 */ { 1655, 5, 17, 13, 3, -15 },
+/* '2' 0x32 */ { 1666, 11, 17, 13, 1, -15 },
+/* '3' 0x33 */ { 1690, 11, 17, 13, 1, -15 },
+/* '4' 0x34 */ { 1714, 11, 17, 13, 1, -15 },
+/* '5' 0x35 */ { 1738, 11, 17, 13, 1, -15 },
+/* '6' 0x36 */ { 1762, 11, 17, 13, 1, -15 },
+/* '7' 0x37 */ { 1786, 11, 17, 13, 1, -15 },
+/* '8' 0x38 */ { 1810, 11, 17, 13, 1, -15 },
+/* '9' 0x39 */ { 1834, 11, 17, 13, 1, -15 },
+/* ':' 0x3A */ { 1858, 2, 13, 6, 2, -11 },
+/* ';' 0x3B */ { 1862, 2, 16, 6, 2, -10 },
+/* '<' 0x3C */ { 1866, 12, 11, 14, 1, -9 },
+/* '=' 0x3D */ { 1883, 12, 6, 14, 1, -7 },
+/* '>' 0x3E */ { 1892, 12, 11, 14, 1, -9 },
+/* '?' 0x3F */ { 1909, 10, 18, 13, 2, -16 },
+/* '@' 0x40 */ { 1932, 22, 21, 24, 1, -16 },
+/* 'A' 0x41 */ { 1990, 14, 18, 16, 1, -16 },
+/* 'B' 0x42 */ { 2022, 13, 18, 16, 2, -16 },
+/* 'C' 0x43 */ { 2052, 15, 18, 17, 1, -16 },
+/* 'D' 0x44 */ { 2086, 14, 18, 17, 2, -16 },
+/* 'E' 0x45 */ { 2118, 12, 18, 15, 2, -16 },
+/* 'F' 0x46 */ { 2145, 11, 18, 14, 2, -16 },
+/* 'G' 0x47 */ { 2170, 16, 18, 18, 1, -16 },
+/* 'H' 0x48 */ { 2206, 13, 18, 17, 2, -16 },
+/* 'I' 0x49 */ { 2236, 2, 18, 7, 2, -16 },
+/* 'J' 0x4A */ { 2241, 9, 18, 13, 1, -16 },
+/* 'K' 0x4B */ { 2262, 13, 18, 16, 2, -16 },
+/* 'L' 0x4C */ { 2292, 10, 18, 14, 2, -16 },
+/* 'M' 0x4D */ { 2315, 16, 18, 20, 2, -16 },
+/* 'N' 0x4E */ { 2351, 13, 18, 18, 2, -16 },
+/* 'O' 0x4F */ { 2381, 17, 18, 19, 1, -16 },
+/* 'P' 0x50 */ { 2420, 12, 18, 16, 2, -16 },
+/* 'Q' 0x51 */ { 2447, 17, 19, 19, 1, -16 },
+/* 'R' 0x52 */ { 2488, 14, 18, 17, 2, -16 },
+/* 'S' 0x53 */ { 2520, 14, 18, 16, 1, -16 },
+/* 'T' 0x54 */ { 2552, 12, 18, 15, 1, -16 },
+/* 'U' 0x55 */ { 2579, 13, 18, 17, 2, -16 },
+/* 'V' 0x56 */ { 2609, 14, 18, 15, 1, -16 },
+/* 'W' 0x57 */ { 2641, 22, 18, 22, 0, -16 },
+/* 'X' 0x58 */ { 2691, 14, 18, 16, 1, -16 },
+/* 'Y' 0x59 */ { 2723, 14, 18, 16, 1, -16 },
+/* 'Z' 0x5A */ { 2755, 13, 18, 15, 1, -16 },
+/* '[' 0x5B */ { 2785, 4, 23, 7, 2, -16 },
+/* '\' 0x5C */ { 2797, 7, 18, 7, 0, -16 },
+/* ']' 0x5D */ { 2813, 4, 23, 7, 1, -16 },
+/* '^' 0x5E */ { 2825, 9, 9, 11, 1, -15 },
+/* '_' 0x5F */ { 2836, 15, 1, 13, -1, 5 },
+/* '`' 0x60 */ { 2838, 5, 4, 6, 1, -16 },
+/* 'a' 0x61 */ { 2841, 12, 13, 13, 1, -11 },
+/* 'b' 0x62 */ { 2861, 12, 18, 13, 1, -16 },
+/* 'c' 0x63 */ { 2888, 10, 13, 12, 1, -11 },
+/* 'd' 0x64 */ { 2905, 11, 18, 13, 1, -16 },
+/* 'e' 0x65 */ { 2930, 11, 13, 13, 1, -11 },
+/* 'f' 0x66 */ { 2948, 5, 18, 7, 1, -16 },
+/* 'g' 0x67 */ { 2960, 11, 18, 13, 1, -11 },
+/* 'h' 0x68 */ { 2985, 10, 18, 13, 1, -16 },
+/* 'i' 0x69 */ { 3008, 2, 18, 5, 2, -16 },
+/* 'j' 0x6A */ { 3013, 4, 23, 6, 0, -16 },
+/* 'k' 0x6B */ { 3025, 10, 18, 12, 1, -16 },
+/* 'l' 0x6C */ { 3048, 2, 18, 5, 1, -16 },
+/* 'm' 0x6D */ { 3053, 17, 13, 19, 1, -11 },
+/* 'n' 0x6E */ { 3081, 10, 13, 13, 1, -11 },
+/* 'o' 0x6F */ { 3098, 11, 13, 13, 1, -11 },
+/* 'p' 0x70 */ { 3116, 12, 17, 13, 1, -11 },
+/* 'q' 0x71 */ { 3142, 11, 17, 13, 1, -11 },
+/* 'r' 0x72 */ { 3166, 6, 13, 8, 1, -11 },
+/* 's' 0x73 */ { 3176, 10, 13, 12, 1, -11 },
+/* 't' 0x74 */ { 3193, 5, 16, 7, 1, -14 },
+/* 'u' 0x75 */ { 3203, 10, 13, 13, 1, -11 },
+/* 'v' 0x76 */ { 3220, 11, 13, 12, 0, -11 },
+/* 'w' 0x77 */ { 3238, 17, 13, 17, 0, -11 },
+/* 'x' 0x78 */ { 3266, 10, 13, 11, 1, -11 },
+/* 'y' 0x79 */ { 3283, 11, 18, 11, 0, -11 },
+/* 'z' 0x7A */ { 3308, 10, 13, 12, 1, -11 },
+/* '{' 0x7B */ { 3325, 5, 23, 8, 1, -16 },
+/* '|' 0x7C */ { 3340, 2, 23, 6, 2, -16 },
+/* '}' 0x7D */ { 3346, 5, 23, 8, 2, -16 },
+/* '~' 0x7E */ { 3361, 10, 5, 12, 1, -9 },
+/* 0x7F */ { 3368, 0, 0, 0, 0, 0 },
+/* 0x80 */ { 3368, 14, 17, 16, 1, -17 },
+/* 0x81 */ { 3398, 0, 0, 8, 0, 0 },
+/* 0x82 */ { 3398, 2, 5, 6, 2, -2 },
+/* 0x83 */ { 3400, 0, 0, 8, 0, 0 },
+/* 0x84 */ { 3400, 6, 5, 10, 2, -2 },
+/* 0x85 */ { 3404, 12, 2, 16, 2, -2 },
+/* 0x86 */ { 3407, 10, 21, 13, 2, -17 },
+/* 0x87 */ { 3434, 10, 20, 13, 2, -17 },
+/* 0x88 */ { 3459, 0, 0, 8, 0, 0 },
+/* 0x89 */ { 3459, 23, 18, 24, 0, -18 },
+/* 0x8A */ { 3511, 14, 21, 16, 1, -21 },
+/* 0x8B */ { 3548, 3, 8, 6, 1, -11 },
+/* 0x8C */ { 3551, 14, 22, 16, 1, -22 },
+/* 0x8D */ { 3590, 12, 21, 15, 1, -21 },
+/* 0x8E */ { 3622, 13, 21, 15, 1, -21 },
+/* 0x8F */ { 3657, 13, 22, 15, 1, -22 },
+/* 0x90 */ { 3693, 0, 0, 8, 0, 0 },
+/* 0x91 */ { 3693, 2, 6, 6, 2, -18 },
+/* 0x92 */ { 3695, 2, 6, 6, 2, -18 },
+/* 0x93 */ { 3697, 6, 6, 10, 2, -18 },
+/* 0x94 */ { 3702, 6, 6, 10, 2, -18 },
+/* 0x95 */ { 3707, 6, 6, 10, 2, -11 },
+/* 0x96 */ { 3712, 10, 2, 12, 1, -8 },
+/* 0x97 */ { 3715, 22, 2, 24, 1, -8 },
+/* 0x98 */ { 3721, 0, 0, 8, 0, 0 },
+/* 0x99 */ { 3721, 22, 13, 24, 2, -18 },
+/* 0x9A */ { 3757, 10, 18, 12, 1, -18 },
+/* 0x9B */ { 3780, 3, 8, 6, 2, -10 },
+/* 0x9C */ { 3783, 10, 18, 12, 1, -18 },
+/* 0x9D */ { 3806, 8, 18, 11, 1, -18 },
+/* 0x9E */ { 3824, 10, 18, 12, 1, -18 },
+/* 0x9F */ { 3847, 10, 18, 12, 1, -18 },
+/* 0xA0 */ { 3870, 0, 0, 7, 0, 0 },
+/* 0xA1 */ { 3870, 7, 4, 8, 0, -18 },
+/* 0xA2 */ { 3874, 7, 4, 8, 0, -18 },
+/* 0xA3 */ { 3878, 13, 18, 15, 1, -18 },
+/* 0xA4 */ { 3908, 9, 9, 13, 2, -13 },
+/* 0xA5 */ { 3919, 16, 23, 16, 1, -18 },
+/* 0xA6 */ { 3965, 2, 23, 6, 2, -18 },
+/* 0xA7 */ { 3971, 11, 23, 13, 1, -18 },
+/* 0xA8 */ { 4003, 6, 2, 8, 1, -17 },
+/* 0xA9 */ { 4005, 18, 17, 19, 1, -17 },
+/* 0xAA */ { 4044, 14, 23, 16, 1, -18 },
+/* 0xAB */ { 4085, 8, 8, 12, 2, -11 },
+/* 0xAC */ { 4093, 12, 6, 14, 1, -9 },
+/* 0xAD */ { 4102, 6, 2, 8, 1, -8 },
+/* 0xAE */ { 4104, 18, 17, 19, 1, -17 },
+/* 0xAF */ { 4143, 13, 21, 15, 1, -21 },
+/* 0xB0 */ { 4178, 7, 8, 15, 4, -17 },
+/* 0xB1 */ { 4185, 12, 15, 14, 1, -15 },
+/* 0xB2 */ { 4208, 5, 5, 8, 1, 0 },
+/* 0xB3 */ { 4212, 4, 18, 6, 1, -18 },
+/* 0xB4 */ { 4221, 5, 4, 8, 2, -18 },
+/* 0xB5 */ { 4224, 12, 17, 13, 2, -13 },
+/* 0xB6 */ { 4250, 11, 21, 13, 2, -18 },
+/* 0xB7 */ { 4279, 2, 2, 6, 2, -8 },
+/* 0xB8 */ { 4280, 6, 5, 8, 1, 0 },
+/* 0xB9 */ { 4284, 13, 18, 13, 1, -13 },
+/* 0xBA */ { 4314, 10, 18, 12, 1, -13 },
+/* 0xBB */ { 4337, 8, 8, 12, 2, -10 },
+/* 0xBC */ { 4345, 10, 18, 14, 2, -18 },
+/* 0xBD */ { 4368, 8, 4, 8, 0, -18 },
+/* 0xBE */ { 4372, 7, 18, 9, 1, -18 },
+/* 0xBF */ { 4388, 10, 17, 12, 1, -17 },
+/* 0xC0 */ { 4410, 14, 22, 17, 2, -22 },
+/* 0xC1 */ { 4449, 14, 22, 16, 1, -22 },
+/* 0xC2 */ { 4488, 14, 22, 16, 1, -22 },
+/* 0xC3 */ { 4527, 14, 22, 16, 1, -22 },
+/* 0xC4 */ { 4566, 14, 21, 16, 1, -21 },
+/* 0xC5 */ { 4603, 10, 22, 14, 2, -22 },
+/* 0xC6 */ { 4631, 15, 22, 17, 1, -22 },
+/* 0xC7 */ { 4673, 15, 23, 17, 1, -18 },
+/* 0xC8 */ { 4717, 15, 21, 17, 1, -21 },
+/* 0xC9 */ { 4757, 12, 22, 15, 2, -22 },
+/* 0xCA */ { 4790, 13, 23, 15, 2, -18 },
+/* 0xCB */ { 4828, 12, 21, 15, 2, -21 },
+/* 0xCC */ { 4860, 12, 21, 15, 2, -21 },
+/* 0xCD */ { 4892, 4, 22, 7, 1, -22 },
+/* 0xCE */ { 4903, 6, 22, 7, 0, -22 },
+/* 0xCF */ { 4920, 14, 21, 17, 2, -21 },
+/* 0xD0 */ { 4957, 15, 18, 17, 1, -18 },
+/* 0xD1 */ { 4991, 13, 22, 18, 2, -22 },
+/* 0xD2 */ { 5027, 13, 21, 18, 2, -21 },
+/* 0xD3 */ { 5062, 17, 22, 19, 1, -22 },
+/* 0xD4 */ { 5109, 17, 22, 19, 1, -22 },
+/* 0xD5 */ { 5156, 17, 22, 19, 1, -22 },
+/* 0xD6 */ { 5203, 17, 21, 19, 1, -21 },
+/* 0xD7 */ { 5248, 8, 9, 14, 3, -10 },
+/* 0xD8 */ { 5257, 14, 21, 17, 2, -21 },
+/* 0xD9 */ { 5294, 13, 24, 17, 2, -24 },
+/* 0xDA */ { 5333, 13, 22, 17, 2, -22 },
+/* 0xDB */ { 5369, 13, 22, 17, 2, -22 },
+/* 0xDC */ { 5405, 13, 21, 17, 2, -21 },
+/* 0xDD */ { 5440, 14, 22, 16, 1, -22 },
+/* 0xDE */ { 5479, 12, 23, 15, 1, -18 },
+/* 0xDF */ { 5514, 11, 18, 14, 2, -18 },
+/* 0xE0 */ { 5539, 6, 18, 8, 1, -18 },
+/* 0xE1 */ { 5553, 12, 18, 13, 1, -18 },
+/* 0xE2 */ { 5580, 12, 18, 13, 1, -18 },
+/* 0xE3 */ { 5607, 12, 18, 13, 1, -18 },
+/* 0xE4 */ { 5634, 12, 17, 13, 1, -17 },
+/* 0xE5 */ { 5660, 5, 22, 6, 0, -22 },
+/* 0xE6 */ { 5674, 10, 18, 12, 1, -18 },
+/* 0xE7 */ { 5697, 10, 18, 12, 1, -13 },
+/* 0xE8 */ { 5720, 10, 18, 12, 1, -18 },
+/* 0xE9 */ { 5743, 11, 18, 13, 1, -18 },
+/* 0xEA */ { 5768, 11, 18, 13, 1, -13 },
+/* 0xEB */ { 5793, 11, 17, 13, 1, -17 },
+/* 0xEC */ { 5817, 11, 18, 13, 1, -18 },
+/* 0xED */ { 5842, 5, 18, 5, 0, -18 },
+/* 0xEE */ { 5854, 6, 18, 6, 0, -18 },
+/* 0xEF */ { 5868, 16, 18, 18, 1, -18 },
+/* 0xF0 */ { 5904, 12, 18, 14, 1, -18 },
+/* 0xF1 */ { 5931, 10, 18, 13, 1, -18 },
+/* 0xF2 */ { 5954, 10, 18, 13, 1, -18 },
+/* 0xF3 */ { 5977, 11, 18, 13, 1, -18 },
+/* 0xF4 */ { 6002, 11, 18, 13, 1, -18 },
+/* 0xF5 */ { 6027, 11, 18, 13, 1, -18 },
+/* 0xF6 */ { 6052, 11, 17, 13, 1, -17 },
+/* 0xF7 */ { 6076, 12, 11, 14, 1, -11 },
+/* 0xF8 */ { 6093, 6, 18, 8, 1, -18 },
+/* 0xF9 */ { 6107, 10, 19, 13, 1, -19 },
+/* 0xFA */ { 6131, 10, 18, 13, 1, -18 },
+/* 0xFB */ { 6154, 10, 18, 13, 1, -18 },
+/* 0xFC */ { 6177, 10, 17, 13, 1, -17 },
+/* 0xFD */ { 6199, 11, 23, 11, 0, -18 },
+/* 0xFE */ { 6231, 6, 21, 7, 1, -16 },
+/* 0xFF */ { 6247, 2, 2, 8, 3, -17 },
+};
+
+const GFXfont FreeSans12pt_Win1250 PROGMEM = {
+(uint8_t*)FreeSans12pt_Win1250Bitmaps,
+(GFXglyph*)FreeSans12pt_Win1250Glyphs,
+0x01, 0xFF, 19
+};
diff --git a/src/graphics/niche/Fonts/FreeSans12pt_Win1251.h b/src/graphics/niche/Fonts/FreeSans12pt_Win1251.h
new file mode 100644
index 000000000..d2972f836
--- /dev/null
+++ b/src/graphics/niche/Fonts/FreeSans12pt_Win1251.h
@@ -0,0 +1,527 @@
+// trunk-ignore-all(clang-format)
+#pragma once
+/* PROPERTIES
+
+FONT_NAME FreeSans12pt_Win1251
+*/
+const uint8_t FreeSans12pt_Win1251Bitmaps[] PROGMEM = {
+/* 0x01 */ 0x00, 0x30, 0x00, 0x09, 0x00, 0x01, 0x20, 0x00, 0x24, 0x00, 0x04, 0x80, 0x01, 0x90, 0x00, 0x62, 0x00, 0x30, 0xFE, 0x04, 0x10, 0x5F, 0x02, 0x0B, 0x00, 0x7F, 0xE0, 0x0C, 0x1C, 0x02, 0x83, 0x81, 0x9F, 0xF0, 0x02, 0x1E, 0x00, 0x41, 0xC0, 0x0E, 0x7F, 0x81, 0x78, 0x18, 0x62, 0x00, 0xFF, 0xC0,
+/* 0x02 */ 0x00, 0xFF, 0x80, 0x61, 0x13, 0xF0, 0x62, 0x60, 0x07, 0xFC, 0x00, 0x83, 0x80, 0x10, 0xF0, 0x33, 0xF6, 0x01, 0x41, 0xC0, 0x18, 0x38, 0x03, 0xFF, 0xE0, 0x47, 0x02, 0x08, 0x20, 0x61, 0xC4, 0x06, 0x17, 0x00, 0x22, 0x00, 0x02, 0x40, 0x00, 0x48, 0x00, 0x09, 0x00, 0x01, 0x20, 0x00, 0x3C, 0x00,
+/* 0x03 */ 0x01, 0xFC, 0x00, 0x38, 0x18, 0x02, 0x00, 0x30, 0x20, 0x00, 0xC2, 0x00, 0x02, 0x30, 0x00, 0x09, 0x04, 0x08, 0x48, 0x70, 0xE1, 0xC3, 0x87, 0x0E, 0x08, 0x10, 0x70, 0x00, 0x03, 0x80, 0x00, 0x14, 0x00, 0x00, 0xA1, 0x81, 0x8D, 0x87, 0xF0, 0x44, 0x00, 0x06, 0x30, 0x00, 0x60, 0xC0, 0x06, 0x03, 0x80, 0x60, 0x07, 0xFC, 0x00,
+/* 0x04 */ 0x01, 0xFC, 0x00, 0x38, 0x18, 0x02, 0x00, 0x30, 0x20, 0x00, 0xC2, 0x00, 0x02, 0x30, 0x00, 0x09, 0x10, 0x02, 0x48, 0xE0, 0x61, 0xC1, 0xCC, 0x0E, 0x78, 0x1C, 0x70, 0x00, 0x03, 0x80, 0x00, 0x14, 0xFF, 0xFC, 0xA6, 0x00, 0xCD, 0x9F, 0xFE, 0x44, 0x71, 0xE6, 0x30, 0xFC, 0x60, 0xC0, 0x06, 0x03, 0x80, 0x60, 0x07, 0xFC, 0x00,
+/* 0x05 */ 0x00, 0x18, 0x00, 0x00, 0x40, 0x01, 0x90, 0x01, 0xF4, 0x08, 0x12, 0x23, 0xC1, 0x91, 0x2C, 0x1C, 0x8A, 0xC3, 0x64, 0x64, 0x13, 0x22, 0x41, 0x98, 0x26, 0x2C, 0xC4, 0x22, 0x60, 0x42, 0x13, 0x04, 0x30, 0x80, 0x61, 0xA4, 0x02, 0x18, 0x20, 0x03, 0x41, 0x00, 0x20, 0x08, 0x02, 0x00, 0x60, 0x40, 0x03, 0xF8,
+/* 0x06 */ 0x00, 0x10, 0x00, 0x03, 0x00, 0x1C, 0x48, 0x00, 0xB4, 0x80, 0x09, 0xF9, 0xC0, 0xE0, 0xE4, 0x0C, 0x02, 0x8F, 0x80, 0x38, 0x88, 0x01, 0x0D, 0x00, 0x18, 0x30, 0x01, 0x60, 0x80, 0x13, 0x18, 0x03, 0xF2, 0xC0, 0x20, 0x26, 0x06, 0x07, 0xFF, 0xA0, 0x02, 0x39, 0x00, 0x14, 0x70, 0x01, 0xC3, 0x00, 0x18, 0x00,
+/* 0x07 */
+/* 0x08 */ 0x00, 0x1F, 0x80, 0x00, 0x60, 0x80, 0x01, 0x00, 0x80, 0x06, 0x00, 0x80, 0x3C, 0x01, 0x01, 0x8C, 0x02, 0x02, 0x08, 0x04, 0x04, 0x08, 0x0C, 0x38, 0x00, 0x04, 0x80, 0x00, 0x06, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x2E, 0xC0, 0x01, 0x83, 0x7E, 0x0C, 0x10, 0x37, 0xE2, 0x61, 0x00, 0x0C, 0xC6, 0x10, 0x98, 0x0C, 0x63, 0x00, 0x00, 0xC6, 0x00,
+/* 0x09 */ 0x00, 0x1F, 0x80, 0x00, 0x60, 0x80, 0x01, 0x00, 0x80, 0x06, 0x00, 0x80, 0x3C, 0x01, 0x01, 0x8C, 0x02, 0x02, 0x08, 0x04, 0x04, 0x08, 0x0C, 0x38, 0x00, 0x04, 0x80, 0x00, 0x06, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x2E, 0xC0, 0x01, 0x83, 0x7E, 0x0C, 0x00, 0x37, 0xE0,
+/* 0x0A */
+/* 0x0B */ 0x1F, 0x07, 0xC1, 0x86, 0x41, 0x10, 0x0C, 0x04, 0x80, 0x40, 0x18, 0x00, 0x00, 0xC0, 0x00, 0x06, 0x00, 0x00, 0x30, 0x00, 0x01, 0x40, 0x00, 0x0A, 0x00, 0x00, 0x88, 0x00, 0x04, 0x40, 0x00, 0x41, 0x00, 0x02, 0x04, 0x00, 0x20, 0x20, 0x02, 0x00, 0x80, 0x20, 0x02, 0x02, 0x00, 0x08, 0x20, 0x00, 0x22, 0x00, 0x00, 0xE0, 0x00,
+/* 0x0C */ 0x01, 0x00, 0x00, 0x38, 0x00, 0x04, 0xC0, 0x01, 0x08, 0x00, 0x18, 0x80, 0x1C, 0x10, 0x02, 0x07, 0x80, 0x81, 0x10, 0x1F, 0xC2, 0x02, 0x00, 0x60, 0x80, 0x1A, 0x20, 0x1C, 0x42, 0x1C, 0x08, 0xFE, 0x03, 0xA0, 0x01, 0x8C, 0x01, 0xC1, 0x43, 0xD0, 0x27, 0x81, 0xF8,
+/* 0x0D */
+/* 0x0E */ 0x00, 0xE0, 0x00, 0x11, 0x00, 0x01, 0x10, 0x00, 0x0B, 0x00, 0x03, 0xF8, 0x00, 0x60, 0x60, 0x09, 0x02, 0x00, 0xA0, 0x10, 0x16, 0x01, 0x01, 0x40, 0x10, 0x10, 0x01, 0x01, 0x00, 0x08, 0x10, 0x00, 0x82, 0x1F, 0x08, 0x3F, 0x90, 0x44, 0x00, 0x06, 0xBF, 0xFF, 0xAF, 0xF0, 0xFF, 0xFF, 0x0F, 0xE3, 0xFB, 0xFC,
+/* 0x0F */ 0x01, 0xFC, 0x00, 0x38, 0x18, 0x02, 0x00, 0x30, 0x20, 0x00, 0xC2, 0x40, 0x12, 0x34, 0x00, 0x69, 0x40, 0x01, 0x49, 0xE0, 0xF1, 0xCD, 0x06, 0x8E, 0x28, 0x14, 0x71, 0x40, 0xA3, 0x8B, 0xFD, 0x14, 0x50, 0x68, 0xA2, 0x81, 0x4D, 0x97, 0xFA, 0x44, 0xBF, 0xD6, 0x31, 0x02, 0xE0, 0xC8, 0x16, 0x08, 0x61, 0x08, 0x21, 0xF0, 0x80, 0xF8, 0x78, 0x00,
+/* 0x10 */ 0x00, 0xF0, 0x00, 0x3A, 0x00, 0x07, 0xC0, 0x00, 0xA8, 0x00, 0x1F, 0x00, 0x02, 0xB0, 0x00, 0x52, 0x00, 0x0A, 0x40, 0x02, 0x48, 0x00, 0x49, 0x00, 0x09, 0x30, 0x01, 0x22, 0x01, 0xC4, 0x70, 0xF0, 0x85, 0xE1, 0x10, 0x88, 0x37, 0x20, 0x03, 0x9C, 0x00, 0x37, 0x00, 0x06, 0x40, 0x01, 0x86, 0x00,
+/* 0x11 */ 0x01, 0xFC, 0x00, 0x38, 0x18, 0x02, 0x00, 0x30, 0x20, 0x00, 0xC2, 0x60, 0x02, 0x36, 0x00, 0x09, 0x04, 0x0C, 0x48, 0x60, 0xC1, 0xC3, 0x0F, 0x0E, 0x00, 0x08, 0x70, 0x00, 0x23, 0x80, 0x63, 0x84, 0x01, 0x9F, 0x20, 0x0C, 0xFD, 0x80, 0x27, 0xE4, 0x03, 0x3F, 0x30, 0x33, 0xE0, 0xC0, 0x00, 0x03, 0x80, 0x60, 0x07, 0xFC, 0x00,
+/* 0x12 */ 0x00, 0xC2, 0x00, 0x1C, 0x24, 0x02, 0x18, 0x60, 0x64, 0x02, 0x02, 0x40, 0x20, 0x00, 0xF2, 0x03, 0x89, 0xE0, 0x7C, 0x80, 0x0E, 0x25, 0x80, 0xE1, 0x00, 0x1A, 0x08, 0x71, 0xB0, 0xC4, 0x39, 0x84, 0xC2, 0xCC, 0x40, 0x76, 0x7C, 0x05, 0xBB, 0x80, 0x4C, 0xE0, 0x0A, 0x78, 0x00, 0x9C, 0x00, 0x0F, 0x00, 0x00,
+/* 0x13 */ 0x01, 0xFC, 0x00, 0x38, 0x18, 0x02, 0x00, 0x30, 0x20, 0x00, 0xC2, 0x00, 0x02, 0x30, 0x00, 0x09, 0x00, 0x00, 0x48, 0x60, 0xC1, 0xC6, 0xC9, 0x0E, 0x00, 0x00, 0x70, 0x00, 0x03, 0x80, 0x00, 0x14, 0xFF, 0xF8, 0xA6, 0x00, 0xCD, 0x9F, 0xFE, 0x44, 0x71, 0xE6, 0x30, 0xFC, 0x60, 0xC0, 0x06, 0x03, 0x80, 0x60, 0x07, 0xFC, 0x00,
+/* 0x14 */ 0x01, 0xFC, 0x00, 0x38, 0x18, 0x02, 0x00, 0x30, 0x20, 0x00, 0xC2, 0x20, 0x22, 0x33, 0x01, 0x89, 0x20, 0x02, 0x48, 0x60, 0xE1, 0xC8, 0x80, 0x8E, 0x46, 0x46, 0x72, 0x32, 0x33, 0x9F, 0x9F, 0x94, 0x78, 0x78, 0xA0, 0x00, 0x0D, 0x80, 0x00, 0x44, 0x0E, 0x06, 0x30, 0x00, 0x60, 0xC0, 0x06, 0x03, 0x80, 0x60, 0x07, 0xFC, 0x00,
+/* 0x15 */ 0x03, 0xFC, 0x20, 0x38, 0x1C, 0x81, 0x80, 0x1D, 0x08, 0x00, 0x32, 0x60, 0x00, 0x89, 0x00, 0x02, 0x18, 0x00, 0x08, 0x61, 0xC3, 0x22, 0x8D, 0x93, 0x72, 0x00, 0x00, 0x48, 0x00, 0x01, 0x20, 0x00, 0x04, 0x9F, 0xFF, 0x92, 0x60, 0x0E, 0x44, 0xFF, 0xF2, 0x11, 0xC3, 0x88, 0x21, 0xF8, 0x40, 0x40, 0x02, 0x00, 0xC0, 0x30, 0x00, 0xFF, 0x00,
+/* 0x16 */ 0x03, 0x80, 0x03, 0xC0, 0x01, 0xE0, 0x01, 0xE0, 0x03, 0xF0, 0x03, 0xF0, 0x27, 0xF0, 0x6F, 0x70, 0x6E, 0x60, 0xFC, 0x60, 0xFC, 0x7E, 0xFC, 0x7E, 0xFC, 0x3F, 0xF4, 0x1F, 0xF4, 0x1F, 0xF0, 0x0E, 0x70, 0x0E, 0x30, 0x1C, 0x38, 0x38, 0x0F, 0xF0,
+/* 0x17 */ 0x01, 0xFC, 0x00, 0x38, 0x18, 0x02, 0x00, 0x30, 0x20, 0x00, 0xC2, 0x00, 0x02, 0x30, 0x00, 0x09, 0x00, 0x00, 0x48, 0x00, 0x21, 0xC0, 0x02, 0x8E, 0x20, 0xF4, 0x70, 0x84, 0x11, 0x82, 0x40, 0x84, 0x01, 0x03, 0x20, 0x0F, 0x85, 0x80, 0x03, 0x04, 0x00, 0x04, 0x30, 0x78, 0x10, 0xC0, 0x06, 0x03, 0x80, 0x60, 0x07, 0xFC, 0x00,
+/* 0x18 */ 0x00, 0xFC, 0x00, 0x02, 0x06, 0x00, 0x08, 0x24, 0x00, 0x21, 0xA4, 0x00, 0x4C, 0x48, 0x00, 0xA0, 0x50, 0x01, 0x92, 0x60, 0x03, 0x24, 0xC0, 0x06, 0x01, 0x81, 0x28, 0x03, 0x49, 0x6C, 0xC4, 0xAD, 0xD8, 0x16, 0xA4, 0xCC, 0xC4, 0x44, 0x86, 0x13, 0x05, 0x00, 0x28, 0x0A, 0x00, 0x50, 0x14, 0x00, 0x90, 0x48, 0x01, 0x20, 0x90, 0x02, 0x41, 0x20, 0x00, 0x00,
+/* 0x19 */ 0x01, 0xFC, 0x00, 0x38, 0x18, 0x02, 0x00, 0x30, 0x20, 0x00, 0xC2, 0x00, 0x02, 0x30, 0x00, 0x09, 0x00, 0x00, 0x49, 0xC3, 0x81, 0xC0, 0x00, 0x0E, 0x78, 0xF0, 0x77, 0xEF, 0xC3, 0xA7, 0x4E, 0x15, 0x0A, 0x10, 0xA7, 0x8F, 0x0D, 0x80, 0x00, 0x44, 0x00, 0x06, 0x33, 0xF0, 0x60, 0xC0, 0x06, 0x03, 0x80, 0x60, 0x07, 0xFC, 0x00,
+/* 0x1A */ 0xFF, 0xFF, 0x00, 0x06, 0x00, 0x0C, 0x3E, 0x18, 0x82, 0x32, 0x02, 0x64, 0x04, 0xC8, 0x09, 0x80, 0x23, 0x00, 0x86, 0x02, 0x0C, 0x08, 0x18, 0x10, 0x30, 0x00, 0x60, 0x00, 0xC0, 0x81, 0x80, 0x03, 0x00, 0x07, 0xFF, 0xF8,
+/* 0x1B */ 0x00, 0xFE, 0x00, 0x03, 0x81, 0x80, 0x04, 0x00, 0x60, 0x08, 0x00, 0x30, 0x10, 0x00, 0x10, 0x30, 0x07, 0x88, 0x23, 0xC8, 0x08, 0x22, 0x00, 0x04, 0x60, 0x00, 0x44, 0x60, 0x00, 0x84, 0x63, 0x03, 0x04, 0x61, 0xFC, 0x04, 0x6B, 0x00, 0x9E, 0xA5, 0x01, 0x6A, 0xD5, 0x01, 0x43, 0xA8, 0x81, 0x05, 0xD0, 0x82, 0x0A, 0xA0, 0x82, 0x05, 0xC0, 0x82, 0x02, 0x61, 0xFF, 0x0C, 0x1E, 0x00, 0xF0,
+/* 0x1C */ 0x01, 0xFC, 0x00, 0x38, 0x18, 0x02, 0x00, 0x30, 0x20, 0x00, 0xC2, 0x30, 0x02, 0x32, 0x00, 0x09, 0x00, 0x00, 0x48, 0x20, 0x61, 0xC3, 0x84, 0x0E, 0x1C, 0x78, 0x70, 0x40, 0x03, 0x80, 0x00, 0x14, 0x00, 0x00, 0xA0, 0x03, 0x0D, 0x83, 0xF0, 0x44, 0x00, 0x06, 0x30, 0x00, 0x60, 0xC0, 0x06, 0x03, 0x80, 0x60, 0x07, 0xFC, 0x00,
+/* 0x1D */ 0x01, 0xFE, 0x00, 0x3A, 0x1C, 0x03, 0x00, 0x30, 0x23, 0x1E, 0xC3, 0x38, 0x03, 0x10, 0xC3, 0x09, 0x00, 0x18, 0x68, 0x00, 0xC1, 0x40, 0x00, 0x0A, 0x07, 0x80, 0x50, 0x46, 0x02, 0x80, 0x00, 0x1A, 0x1E, 0x00, 0xCB, 0x10, 0x0D, 0x03, 0x00, 0x48, 0x60, 0x06, 0x40, 0x00, 0x22, 0x0C, 0x02, 0x10, 0x60, 0x60, 0x43, 0xFC, 0x01, 0xE0, 0x00, 0x00,
+/* 0x1E */ 0x01, 0xF0, 0x00, 0xEA, 0xC0, 0x31, 0x5F, 0x04, 0x5F, 0x88, 0x80, 0xA0, 0x48, 0x0E, 0x02, 0x8F, 0x40, 0x3C, 0x10, 0x21, 0x66, 0x87, 0x15, 0x98, 0x71, 0x41, 0x02, 0x14, 0x00, 0x01, 0x40, 0x00, 0x14, 0x00, 0x01, 0x21, 0xFE, 0x12, 0x00, 0x02, 0x10, 0x00, 0x60, 0x80, 0x0C, 0x06, 0x01, 0x80, 0x3F, 0xE0,
+/* 0x1F */ 0x0E, 0x00, 0x13, 0x00, 0x23, 0x00, 0xF3, 0x01, 0x31, 0x01, 0x11, 0x03, 0xD3, 0x06, 0xF2, 0x30, 0x34, 0xC7, 0x25, 0x33, 0x2B, 0xC2, 0x57, 0x04, 0x3A, 0x08, 0x72, 0x30, 0xA3, 0xC3, 0x40, 0x04, 0x40, 0x18, 0x40, 0x60, 0x7F, 0x80,
+/* ' ' 0x20 */
+/* '!' 0x21 */ 0xFF, 0xFF, 0xFF, 0xF0, 0xF0,
+/* '"' 0x22 */ 0xCF, 0x3C, 0xF3, 0x8A, 0x20,
+/* '#' 0x23 */ 0x06, 0x30, 0x31, 0x03, 0x18, 0x18, 0xC7, 0xFF, 0xBF, 0xFC, 0x31, 0x01, 0x18, 0x18, 0xC7, 0xFF, 0xBF, 0xFC, 0x31, 0x01, 0x18, 0x18, 0xC0, 0xC6, 0x06, 0x30,
+/* '$' 0x24 */ 0x04, 0x03, 0xE1, 0xFF, 0x72, 0x7C, 0x47, 0x88, 0xF1, 0x07, 0xA0, 0x7E, 0x03, 0xF0, 0x17, 0x02, 0x7C, 0x47, 0x88, 0xF1, 0x1B, 0x26, 0x7F, 0xC3, 0xE0, 0x10, 0x02, 0x00,
+/* '%' 0x25 */ 0x00, 0x06, 0x03, 0xC0, 0x40, 0x7E, 0x0C, 0x0E, 0x70, 0x80, 0xC3, 0x18, 0x0C, 0x31, 0x00, 0xE7, 0x30, 0x07, 0xE6, 0x00, 0x3C, 0x40, 0x00, 0x0C, 0x7C, 0x00, 0x8F, 0xE0, 0x19, 0xC7, 0x01, 0x18, 0x30, 0x31, 0x83, 0x02, 0x1C, 0x70, 0x40, 0xFE, 0x04, 0x07, 0xC0,
+/* '&' 0x26 */ 0x0F, 0x00, 0x7E, 0x03, 0x9C, 0x0C, 0x30, 0x30, 0xC0, 0xE7, 0x01, 0xF8, 0x03, 0x80, 0x3E, 0x01, 0xCC, 0x6E, 0x39, 0xB0, 0x7C, 0xC0, 0xF3, 0x03, 0xCE, 0x1F, 0x9F, 0xE6, 0x3E, 0x1C,
+/* ''' 0x27 */ 0xFF, 0xA0,
+/* '(' 0x28 */ 0x08, 0x8C, 0x46, 0x31, 0x98, 0xC6, 0x31, 0x8C, 0x63, 0x08, 0x63, 0x08, 0x61, 0x0C, 0x20,
+/* ')' 0x29 */ 0x82, 0x18, 0xC3, 0x18, 0xC3, 0x18, 0xC6, 0x31, 0x8C, 0x62, 0x31, 0x88, 0xC4, 0x62, 0x00,
+/* '*' 0x2A */ 0x10, 0x23, 0x5B, 0xE3, 0x8D, 0x91, 0x00,
+/* '+' 0x2B */ 0x0C, 0x03, 0x00, 0xC0, 0x30, 0xFF, 0xFF, 0xF0, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0,
+/* ',' 0x2C */ 0xF5, 0x60,
+/* '-' 0x2D */ 0xFF, 0xF0,
+/* '.' 0x2E */ 0xF0,
+/* '/' 0x2F */ 0x02, 0x0C, 0x10, 0x20, 0xC1, 0x02, 0x0C, 0x10, 0x20, 0xC1, 0x02, 0x0C, 0x10, 0x20, 0xC1, 0x00,
+/* '0' 0x30 */ 0x1F, 0x07, 0xF1, 0xC7, 0x30, 0x6C, 0x0F, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x3E, 0x0E, 0xC1, 0x9C, 0x71, 0xFC, 0x1F, 0x00,
+/* '1' 0x31 */ 0x08, 0xCF, 0xFF, 0x8C, 0x63, 0x18, 0xC6, 0x31, 0x8C, 0x63, 0x18,
+/* '2' 0x32 */ 0x1F, 0x0F, 0xF9, 0x87, 0x60, 0x7C, 0x06, 0x00, 0xC0, 0x18, 0x07, 0x01, 0xC0, 0xF0, 0x78, 0x1C, 0x06, 0x00, 0xC0, 0x30, 0x07, 0xFF, 0xFF, 0xE0,
+/* '3' 0x33 */ 0x3F, 0x0F, 0xF3, 0x87, 0x60, 0x6C, 0x0C, 0x01, 0x80, 0x60, 0x78, 0x0F, 0x80, 0x18, 0x01, 0x80, 0x3C, 0x07, 0x80, 0xD8, 0x73, 0xFC, 0x3F, 0x00,
+/* '4' 0x34 */ 0x01, 0x80, 0x70, 0x0E, 0x03, 0xC0, 0xD8, 0x1B, 0x06, 0x61, 0x8C, 0x21, 0x8C, 0x33, 0x06, 0x7F, 0xFF, 0xFE, 0x03, 0x00, 0x60, 0x0C, 0x01, 0x80,
+/* '5' 0x35 */ 0x3F, 0xCF, 0xF9, 0x80, 0x30, 0x06, 0x00, 0xDE, 0x1F, 0xE7, 0x0E, 0x00, 0xE0, 0x0C, 0x01, 0x80, 0x30, 0x07, 0x81, 0xB8, 0x73, 0xFC, 0x1F, 0x00,
+/* '6' 0x36 */ 0x0F, 0x07, 0xF9, 0xC3, 0x30, 0x74, 0x01, 0x80, 0x33, 0xC7, 0xFE, 0xF1, 0xDC, 0x1F, 0x01, 0xE0, 0x3C, 0x06, 0xC1, 0xDC, 0x71, 0xFC, 0x1F, 0x00,
+/* '7' 0x37 */ 0xFF, 0xFF, 0xFC, 0x01, 0x00, 0x60, 0x18, 0x02, 0x00, 0xC0, 0x30, 0x06, 0x01, 0x80, 0x30, 0x04, 0x01, 0x80, 0x30, 0x06, 0x01, 0x80, 0x30, 0x00,
+/* '8' 0x38 */ 0x1F, 0x07, 0xF1, 0xC7, 0x30, 0x66, 0x0C, 0xC1, 0x8C, 0x61, 0xF8, 0x3F, 0x8E, 0x3B, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xD8, 0x31, 0xFC, 0x1F, 0x00,
+/* '9' 0x39 */ 0x1F, 0x07, 0xF1, 0xC7, 0x70, 0x6C, 0x07, 0x80, 0xF0, 0x1E, 0x07, 0x61, 0xEF, 0xFC, 0x79, 0x80, 0x30, 0x05, 0xC1, 0x98, 0x73, 0xFC, 0x1E, 0x00,
+/* ':' 0x3A */ 0xF0, 0x00, 0x03, 0xC0,
+/* ';' 0x3B */ 0xF0, 0x00, 0x0F, 0x56,
+/* '<' 0x3C */ 0x00, 0x70, 0x1E, 0x0F, 0x83, 0xC0, 0xF0, 0x0E, 0x00, 0x7C, 0x00, 0xF0, 0x03, 0xC0, 0x0F, 0x00, 0x10,
+/* '=' 0x3D */ 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF,
+/* '>' 0x3E */ 0xE0, 0x07, 0x80, 0x1F, 0x00, 0x7C, 0x00, 0xF0, 0x07, 0x01, 0xE0, 0xF0, 0x3C, 0x0F, 0x00, 0x80, 0x00,
+/* '?' 0x3F */ 0x3F, 0x1F, 0xEE, 0x1F, 0x03, 0xC0, 0xC0, 0x30, 0x0C, 0x06, 0x03, 0x81, 0xC0, 0xE0, 0x30, 0x0C, 0x03, 0x00, 0x00, 0x00, 0x0C, 0x03, 0x00,
+/* '@' 0x40 */ 0x00, 0xFE, 0x00, 0x0F, 0xFE, 0x00, 0xF0, 0x3E, 0x07, 0x00, 0x3C, 0x38, 0x00, 0x38, 0xC1, 0xE0, 0x66, 0x0F, 0xD9, 0xD8, 0x61, 0xC3, 0xC3, 0x07, 0x0F, 0x1C, 0x1C, 0x3C, 0x60, 0x60, 0xF1, 0x81, 0x83, 0xC6, 0x06, 0x1B, 0x18, 0x38, 0xEE, 0x71, 0xE7, 0x18, 0xFD, 0xF8, 0x71, 0xE7, 0xC0, 0xE0, 0x00, 0x01, 0xE0, 0x00, 0x01, 0xFF, 0xC0, 0x01, 0xFC, 0x00,
+/* 'A' 0x41 */ 0x07, 0x80, 0x1E, 0x00, 0x78, 0x03, 0xF0, 0x0C, 0xC0, 0x33, 0x01, 0xCE, 0x06, 0x18, 0x18, 0x60, 0xE1, 0xC3, 0x03, 0x0F, 0xFC, 0x7F, 0xF9, 0x80, 0x66, 0x01, 0xB8, 0x07, 0xC0, 0x0F, 0x00, 0x30,
+/* 'B' 0x42 */ 0xFF, 0xC7, 0xFF, 0x30, 0x1D, 0x80, 0x6C, 0x03, 0x60, 0x1B, 0x00, 0xD8, 0x0C, 0xFF, 0xC7, 0xFF, 0x30, 0x0D, 0x80, 0x3C, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x06, 0xFF, 0xF7, 0xFE, 0x00,
+/* 'C' 0x43 */ 0x07, 0xE0, 0x3F, 0xF0, 0xE0, 0x73, 0x80, 0x76, 0x00, 0x6C, 0x00, 0x30, 0x00, 0x60, 0x00, 0xC0, 0x01, 0x80, 0x03, 0x00, 0x06, 0x00, 0x0E, 0x00, 0x6C, 0x00, 0xDC, 0x03, 0x1E, 0x0E, 0x1F, 0xF8, 0x0F, 0xC0,
+/* 'D' 0x44 */ 0xFF, 0xC3, 0xFF, 0x8C, 0x07, 0x30, 0x0E, 0xC0, 0x1B, 0x00, 0x7C, 0x00, 0xF0, 0x03, 0xC0, 0x0F, 0x00, 0x3C, 0x00, 0xF0, 0x03, 0xC0, 0x1F, 0x00, 0x6C, 0x03, 0xB0, 0x1C, 0xFF, 0xE3, 0xFE, 0x00,
+/* 'E' 0x45 */ 0xFF, 0xFF, 0xFF, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00, 0xFF, 0xEF, 0xFE, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00, 0xFF, 0xFF, 0xFF,
+/* 'F' 0x46 */ 0xFF, 0xFF, 0xFF, 0x00, 0x60, 0x0C, 0x01, 0x80, 0x30, 0x06, 0x00, 0xFF, 0xDF, 0xFB, 0x00, 0x60, 0x0C, 0x01, 0x80, 0x30, 0x06, 0x00, 0xC0, 0x18, 0x00,
+/* 'G' 0x47 */ 0x07, 0xF0, 0x1F, 0xFC, 0x3C, 0x1E, 0x70, 0x07, 0x60, 0x03, 0xE0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x7F, 0xC0, 0x7F, 0xC0, 0x03, 0xC0, 0x03, 0x60, 0x03, 0x60, 0x07, 0x30, 0x0F, 0x3C, 0x1F, 0x1F, 0xFB, 0x07, 0xE1,
+/* 'H' 0x48 */ 0xC0, 0x1E, 0x00, 0xF0, 0x07, 0x80, 0x3C, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x03, 0xFF, 0xFF, 0xFF, 0xF0, 0x07, 0x80, 0x3C, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x03, 0xC0, 0x1E, 0x00, 0xC0,
+/* 'I' 0x49 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xF0,
+/* 'J' 0x4A */ 0x01, 0x80, 0xC0, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x01, 0x80, 0xC0, 0x60, 0x3C, 0x1E, 0x0F, 0x07, 0xC7, 0x7F, 0x1F, 0x00,
+/* 'K' 0x4B */ 0xC0, 0x3E, 0x03, 0xB0, 0x39, 0x83, 0x8C, 0x38, 0x63, 0x83, 0x38, 0x19, 0xC0, 0xDE, 0x07, 0xB8, 0x38, 0xE1, 0x83, 0x0C, 0x1C, 0x60, 0x73, 0x01, 0x98, 0x0E, 0xC0, 0x3E, 0x00, 0xC0,
+/* 'L' 0x4C */ 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xFF, 0xFF, 0xF0,
+/* 'M' 0x4D */ 0xE0, 0x07, 0xE0, 0x07, 0xF0, 0x0F, 0xF0, 0x0F, 0xD0, 0x0F, 0xD8, 0x1B, 0xD8, 0x1B, 0xD8, 0x1B, 0xCC, 0x33, 0xCC, 0x33, 0xCC, 0x33, 0xC6, 0x63, 0xC6, 0x63, 0xC6, 0x63, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC1, 0x83,
+/* 'N' 0x4E */ 0xE0, 0x1F, 0x00, 0xFC, 0x07, 0xE0, 0x3D, 0x81, 0xEE, 0x0F, 0x30, 0x79, 0xC3, 0xC6, 0x1E, 0x18, 0xF0, 0xE7, 0x83, 0x3C, 0x1D, 0xE0, 0x6F, 0x01, 0xF8, 0x0F, 0xC0, 0x3E, 0x01, 0xC0,
+/* 'O' 0x4F */ 0x07, 0xF0, 0x0F, 0xFE, 0x0F, 0x07, 0x86, 0x00, 0xC6, 0x00, 0x33, 0x00, 0x1B, 0x00, 0x07, 0x80, 0x03, 0xC0, 0x01, 0xE0, 0x00, 0xF0, 0x00, 0x78, 0x00, 0x36, 0x00, 0x33, 0x00, 0x18, 0xC0, 0x18, 0x78, 0x3C, 0x1F, 0xFC, 0x03, 0xF8, 0x00,
+/* 'P' 0x50 */ 0xFF, 0x8F, 0xFE, 0xC0, 0x6C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, 0x3C, 0x06, 0xFF, 0xEF, 0xFC, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00,
+/* 'Q' 0x51 */ 0x07, 0xF0, 0x0F, 0xFE, 0x0F, 0x07, 0x86, 0x00, 0xC6, 0x00, 0x33, 0x00, 0x1B, 0x00, 0x07, 0x80, 0x03, 0xC0, 0x01, 0xE0, 0x00, 0xF0, 0x00, 0x78, 0x00, 0x36, 0x00, 0x33, 0x01, 0x98, 0xC0, 0xFC, 0x78, 0x3C, 0x1F, 0xFF, 0x03, 0xF9, 0x80, 0x00, 0x40,
+/* 'R' 0x52 */ 0xFF, 0xE3, 0xFF, 0xCC, 0x03, 0xB0, 0x06, 0xC0, 0x1B, 0x00, 0x6C, 0x01, 0xB0, 0x0C, 0xFF, 0xE3, 0xFF, 0xCC, 0x03, 0xB0, 0x06, 0xC0, 0x1B, 0x00, 0x6C, 0x01, 0xB0, 0x06, 0xC0, 0x1B, 0x00, 0x70,
+/* 'S' 0x53 */ 0x0F, 0xE0, 0x7F, 0xC3, 0x83, 0x98, 0x07, 0x60, 0x0D, 0x80, 0x07, 0x00, 0x1E, 0x00, 0x3F, 0x80, 0x3F, 0xC0, 0x0F, 0x80, 0x07, 0xC0, 0x0F, 0x00, 0x3E, 0x00, 0xDE, 0x0E, 0x3F, 0xF0, 0x3F, 0x80,
+/* 'T' 0x54 */ 0xFF, 0xFF, 0xFF, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60,
+/* 'U' 0x55 */ 0xC0, 0x1E, 0x00, 0xF0, 0x07, 0x80, 0x3C, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x03, 0xC0, 0x1E, 0x00, 0xF0, 0x07, 0x80, 0x3C, 0x01, 0xE0, 0x0F, 0x80, 0xEE, 0x0E, 0x3F, 0xE0, 0xFC, 0x00,
+/* 'V' 0x56 */ 0xC0, 0x0F, 0x00, 0x7E, 0x01, 0x98, 0x06, 0x60, 0x39, 0xC0, 0xC3, 0x03, 0x0C, 0x1C, 0x38, 0x60, 0x61, 0x81, 0x8E, 0x07, 0x30, 0x0C, 0xC0, 0x37, 0x00, 0xF8, 0x01, 0xE0, 0x07, 0x80, 0x1C, 0x00,
+/* 'W' 0x57 */ 0xE0, 0x30, 0x1D, 0x80, 0xE0, 0x76, 0x07, 0x81, 0xDC, 0x1E, 0x06, 0x70, 0x7C, 0x18, 0xC1, 0xB0, 0xE3, 0x0C, 0xC3, 0x8C, 0x33, 0x0C, 0x38, 0xC6, 0x30, 0x67, 0x18, 0xC1, 0x98, 0x67, 0x06, 0x61, 0xD8, 0x1D, 0x83, 0x60, 0x3C, 0x0D, 0x80, 0xF0, 0x3E, 0x03, 0xC0, 0x70, 0x0F, 0x01, 0xC0, 0x18, 0x07, 0x00,
+/* 'X' 0x58 */ 0xE0, 0x1D, 0x80, 0xE7, 0x03, 0x0E, 0x1C, 0x18, 0x60, 0x73, 0x00, 0xFC, 0x01, 0xE0, 0x07, 0x00, 0x1E, 0x00, 0xF8, 0x03, 0x30, 0x1C, 0xE0, 0xE1, 0x83, 0x07, 0x1C, 0x0E, 0xE0, 0x1B, 0x00, 0x70,
+/* 'Y' 0x59 */ 0xC0, 0x0F, 0x80, 0x76, 0x01, 0x9C, 0x0C, 0x38, 0x70, 0x61, 0x81, 0xCE, 0x03, 0x30, 0x0F, 0x80, 0x1E, 0x00, 0x30, 0x00, 0xC0, 0x03, 0x00, 0x0C, 0x00, 0x30, 0x00, 0xC0, 0x03, 0x00, 0x0C, 0x00,
+/* 'Z' 0x5A */ 0xFF, 0xFF, 0xFF, 0xC0, 0x0E, 0x00, 0xE0, 0x0E, 0x00, 0x60, 0x07, 0x00, 0x70, 0x07, 0x00, 0x30, 0x03, 0x80, 0x38, 0x03, 0x80, 0x18, 0x01, 0xC0, 0x1C, 0x00, 0xFF, 0xFF, 0xFF, 0xC0,
+/* '[' 0x5B */ 0xFF, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCF, 0xF0,
+/* '\' 0x5C */ 0x81, 0x81, 0x02, 0x06, 0x04, 0x08, 0x18, 0x10, 0x20, 0x60, 0x40, 0x81, 0x81, 0x02, 0x06, 0x04,
+/* ']' 0x5D */ 0xFF, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x3F, 0xF0,
+/* '^' 0x5E */ 0x0C, 0x0E, 0x05, 0x86, 0xC3, 0x21, 0x19, 0x8C, 0x83, 0xC1, 0x80,
+/* '_' 0x5F */ 0xFF, 0xFE,
+/* '`' 0x60 */ 0xE3, 0x8C, 0x30,
+/* 'a' 0x61 */ 0x3F, 0x07, 0xF8, 0xE1, 0xCC, 0x0C, 0x00, 0xC0, 0x1C, 0x3F, 0xCF, 0x8C, 0xC0, 0xCC, 0x0C, 0xE3, 0xC7, 0xEF, 0x3C, 0x70,
+/* 'b' 0x62 */ 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0xF8, 0xDF, 0xCF, 0x0E, 0xE0, 0x7C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, 0x3C, 0x03, 0xE0, 0x6F, 0x0E, 0xDF, 0xCC, 0xF8,
+/* 'c' 0x63 */ 0x1F, 0x0F, 0xE6, 0x1F, 0x83, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x38, 0x37, 0x1C, 0xFE, 0x1F, 0x00,
+/* 'd' 0x64 */ 0x00, 0x60, 0x0C, 0x01, 0x80, 0x30, 0x06, 0x3C, 0xCF, 0xFB, 0x8F, 0xE0, 0xF8, 0x0F, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xF8, 0x3B, 0x8F, 0x3F, 0x63, 0xCC,
+/* 'e' 0x65 */ 0x1F, 0x07, 0xF1, 0xC7, 0x70, 0x3C, 0x07, 0xFF, 0xFF, 0xFE, 0x00, 0xC0, 0x1C, 0x0D, 0xC3, 0x1F, 0xC1, 0xF0,
+/* 'f' 0x66 */ 0x3B, 0xD8, 0xC6, 0x7F, 0xEC, 0x63, 0x18, 0xC6, 0x31, 0x8C, 0x63, 0x00,
+/* 'g' 0x67 */ 0x1E, 0x67, 0xFD, 0xC7, 0xF0, 0x7C, 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0x7C, 0x1D, 0xC7, 0x9F, 0xB1, 0xE6, 0x00, 0xC0, 0x3E, 0x0E, 0x7F, 0xC7, 0xE0,
+/* 'h' 0x68 */ 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x33, 0xCD, 0xFB, 0xC7, 0xE0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x30,
+/* 'i' 0x69 */ 0xF0, 0x3F, 0xFF, 0xFF, 0xF0,
+/* 'j' 0x6A */ 0x33, 0x00, 0x03, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x3F, 0xE0,
+/* 'k' 0x6B */ 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x30, 0x6C, 0x33, 0x18, 0xCC, 0x37, 0x0F, 0xC3, 0xB8, 0xC6, 0x31, 0xCC, 0x3B, 0x06, 0xC1, 0xF0, 0x30,
+/* 'l' 0x6C */ 0xFF, 0xFF, 0xFF, 0xFF, 0xF0,
+/* 'm' 0x6D */ 0xCF, 0x1F, 0x6F, 0xDF, 0xFC, 0x78, 0xFC, 0x18, 0x3C, 0x0C, 0x1E, 0x06, 0x0F, 0x03, 0x07, 0x81, 0x83, 0xC0, 0xC1, 0xE0, 0x60, 0xF0, 0x30, 0x78, 0x18, 0x3C, 0x0C, 0x18,
+/* 'n' 0x6E */ 0xCF, 0x37, 0xEF, 0x1F, 0x83, 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xC0,
+/* 'o' 0x6F */ 0x1F, 0x07, 0xF1, 0xC7, 0x70, 0x7C, 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0x7C, 0x1D, 0xC7, 0x1F, 0xC1, 0xF0,
+/* 'p' 0x70 */ 0xCF, 0x8D, 0xFC, 0xF0, 0xEE, 0x06, 0xC0, 0x3C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, 0x3E, 0x06, 0xF0, 0xEF, 0xFC, 0xCF, 0x8C, 0x00, 0xC0, 0x0C, 0x00, 0xC0, 0x00,
+/* 'q' 0x71 */ 0x1E, 0x67, 0xFD, 0xC7, 0xF0, 0x7C, 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0x7C, 0x1D, 0xC7, 0x9F, 0xF1, 0xE6, 0x00, 0xC0, 0x18, 0x03, 0x00, 0x60,
+/* 'r' 0x72 */ 0xCF, 0x7F, 0x38, 0xC3, 0x0C, 0x30, 0xC3, 0x0C, 0x30, 0xC0,
+/* 's' 0x73 */ 0x3E, 0x1F, 0xEE, 0x1B, 0x00, 0xC0, 0x3C, 0x07, 0xF0, 0x3F, 0x01, 0xF0, 0x3E, 0x1D, 0xFE, 0x3F, 0x00,
+/* 't' 0x74 */ 0x63, 0x19, 0xFF, 0xB1, 0x8C, 0x63, 0x18, 0xC6, 0x31, 0xE7,
+/* 'u' 0x75 */ 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x7E, 0x3D, 0xFB, 0x3C, 0xC0,
+/* 'v' 0x76 */ 0xE0, 0x6C, 0x0D, 0x81, 0xB8, 0x63, 0x0C, 0x61, 0x8E, 0x60, 0xCC, 0x19, 0x83, 0xE0, 0x3C, 0x07, 0x00, 0xE0,
+/* 'w' 0x77 */ 0xC1, 0xC1, 0xB0, 0xE1, 0xD8, 0x70, 0xCC, 0x2C, 0x66, 0x36, 0x31, 0x9B, 0x18, 0xCD, 0x98, 0x64, 0x6C, 0x16, 0x36, 0x0F, 0x1A, 0x07, 0x8F, 0x03, 0x83, 0x80, 0xC1, 0xC0,
+/* 'x' 0x78 */ 0xC1, 0xF8, 0x66, 0x30, 0xCC, 0x3E, 0x07, 0x00, 0xC0, 0x78, 0x36, 0x0C, 0xC6, 0x3B, 0x06, 0xC0, 0xC0,
+/* 'y' 0x79 */ 0xE0, 0x6C, 0x0D, 0x83, 0x38, 0x63, 0x0C, 0x63, 0x0C, 0x60, 0xCC, 0x1B, 0x03, 0x60, 0x3C, 0x07, 0x00, 0xE0, 0x18, 0x03, 0x00, 0xE0, 0x78, 0x0E, 0x00,
+/* 'z' 0x7A */ 0xFF, 0xFF, 0xF0, 0x18, 0x0C, 0x07, 0x03, 0x81, 0xC0, 0x60, 0x30, 0x18, 0x0E, 0x03, 0xFF, 0xFF, 0xC0,
+/* '{' 0x7B */ 0x19, 0xCC, 0x63, 0x18, 0xC6, 0x31, 0x99, 0x86, 0x18, 0xC6, 0x31, 0x8C, 0x63, 0x1C, 0x60,
+/* '|' 0x7C */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC,
+/* '}' 0x7D */ 0xC7, 0x18, 0xC6, 0x31, 0x8C, 0x63, 0x0C, 0x33, 0x31, 0x8C, 0x63, 0x18, 0xC6, 0x73, 0x00,
+/* '~' 0x7E */ 0x70, 0x3E, 0x09, 0xE4, 0x1F, 0x03, 0x80,
+/* 0x7F */
+/* 0x80 */ 0xFF, 0xE0, 0xFF, 0xE0, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x07, 0xFC, 0x07, 0xFE, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x1E, 0x00, 0x1C,
+/* 0x81 */ 0x07, 0x01, 0xC0, 0x20, 0x00, 0x0F, 0xFF, 0xFF, 0xF0, 0x06, 0x00, 0xC0, 0x18, 0x03, 0x00, 0x60, 0x0C, 0x01, 0x80, 0x30, 0x06, 0x00, 0xC0, 0x18, 0x03, 0x00, 0x60, 0x0C, 0x01, 0x80, 0x00,
+/* 0x82 */ 0xF5, 0x80,
+/* 0x83 */ 0x0C, 0x38, 0x61, 0x80, 0x1F, 0xFF, 0xE0, 0xC1, 0x83, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC1, 0x80,
+/* 0x84 */ 0xCF, 0x34, 0x51, 0x88,
+/* 0x85 */ 0xC6, 0x3C, 0x63,
+/* 0x86 */ 0x0C, 0x03, 0x00, 0xC0, 0x30, 0x0C, 0x3F, 0xFF, 0xFC, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x30, 0x0C, 0x00,
+/* 0x87 */ 0x0C, 0x03, 0x00, 0xC0, 0x30, 0x0C, 0x3F, 0xFF, 0xFC, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x30, 0x0C, 0x03, 0x0F, 0xFF, 0xFF, 0x0C, 0x03, 0x00, 0xC0, 0x30,
+/* 0x88 */ 0x01, 0xF0, 0x1F, 0xF0, 0xE0, 0xC7, 0x00, 0x18, 0x00, 0xC0, 0x07, 0xFF, 0x3F, 0xFC, 0x30, 0x01, 0xFF, 0x8F, 0xFC, 0x0C, 0x00, 0x18, 0x00, 0x70, 0x00, 0xE0, 0x81, 0xFE, 0x03, 0xF0,
+/* 0x89 */ 0x38, 0x18, 0x00, 0xF8, 0x30, 0x03, 0x18, 0xC0, 0x04, 0x11, 0x80, 0x0C, 0x66, 0x00, 0x0F, 0x8C, 0x00, 0x0E, 0x30, 0x00, 0x00, 0x40, 0x00, 0x01, 0x80, 0x00, 0x06, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x31, 0xC0, 0xE0, 0x67, 0xC3, 0xC1, 0x98, 0xCC, 0xC3, 0x20, 0x90, 0x8C, 0x63, 0x33, 0x10, 0x7C, 0x3C, 0x60, 0x70, 0x38,
+/* 0x8A */ 0x1F, 0xF8, 0x00, 0x3F, 0xF0, 0x00, 0x60, 0x60, 0x00, 0xC0, 0xC0, 0x01, 0x81, 0x80, 0x03, 0x03, 0x00, 0x06, 0x06, 0x00, 0x0C, 0x0C, 0x00, 0x18, 0x1F, 0xF0, 0x30, 0x3F, 0xF0, 0x60, 0x60, 0x30, 0xC0, 0xC0, 0x31, 0x81, 0x80, 0x66, 0x03, 0x00, 0xCC, 0x06, 0x01, 0xB8, 0x0C, 0x06, 0xE0, 0x1F, 0xFD, 0x80, 0x3F, 0xE0,
+/* 0x8B */ 0x2F, 0x49, 0x99,
+/* 0x8C */ 0xC0, 0x60, 0x06, 0x03, 0x00, 0x30, 0x18, 0x01, 0x80, 0xC0, 0x0C, 0x06, 0x00, 0x60, 0x30, 0x03, 0x01, 0x80, 0x18, 0x0C, 0x00, 0xFF, 0xFF, 0xC7, 0xFF, 0xFF, 0x30, 0x18, 0x1D, 0x80, 0xC0, 0x3C, 0x06, 0x01, 0xE0, 0x30, 0x0F, 0x01, 0x80, 0x78, 0x0C, 0x06, 0xC0, 0x7F, 0xF6, 0x03, 0xFE, 0x00,
+/* 0x8D */ 0x03, 0x00, 0x60, 0x0C, 0x00, 0x00, 0xC0, 0x7C, 0x0E, 0xC1, 0xCC, 0x38, 0xC7, 0x0C, 0xE0, 0xDC, 0x0F, 0x80, 0xF0, 0x0F, 0x80, 0xDC, 0x0C, 0xE0, 0xC7, 0x0C, 0x30, 0xC3, 0x8C, 0x1C, 0xC0, 0xEC, 0x07,
+/* 0x8E */ 0xFF, 0xE0, 0xFF, 0xE0, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x06, 0x00, 0x07, 0xFC, 0x07, 0xFE, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03, 0x06, 0x03,
+/* 0x8F */ 0xC0, 0x1E, 0x00, 0xF0, 0x07, 0x80, 0x3C, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x03, 0xC0, 0x1E, 0x00, 0xF0, 0x07, 0x80, 0x3C, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x03, 0xFF, 0xFF, 0xFF, 0xC0, 0xC0, 0x06, 0x00, 0x30, 0x00,
+/* 0x90 */ 0x30, 0x0F, 0xF0, 0xFF, 0x03, 0x00, 0x30, 0x03, 0x3C, 0x37, 0xE3, 0xC7, 0x38, 0x33, 0x03, 0x30, 0x33, 0x03, 0x30, 0x33, 0x03, 0x30, 0x33, 0x03, 0x30, 0x33, 0x03, 0x00, 0x60, 0x06, 0x01, 0x80, 0x30,
+/* 0x91 */ 0x6A, 0xF0,
+/* 0x92 */ 0xF5, 0x60,
+/* 0x93 */ 0x4E, 0x28, 0xA2, 0xCF, 0x30,
+/* 0x94 */ 0xCF, 0x34, 0x51, 0x4E, 0x20,
+/* 0x95 */ 0x7B, 0xFF, 0xFF, 0xFD, 0xE0,
+/* 0x96 */ 0xFF, 0xFF, 0xF0,
+/* 0x97 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0,
+/* 0x98 */
+/* 0x99 */ 0xFF, 0x70, 0x1F, 0xFD, 0xC0, 0x71, 0x87, 0x83, 0xC6, 0x1E, 0x0F, 0x18, 0x68, 0x3C, 0x61, 0xB1, 0xB1, 0x86, 0xC6, 0xC6, 0x19, 0x1B, 0x18, 0x66, 0xCC, 0x61, 0x9B, 0x31, 0x86, 0x3C, 0xC6, 0x18, 0xE3, 0x18, 0x63, 0x8C,
+/* 0x9A */ 0x7F, 0x80, 0x3F, 0xC0, 0x18, 0x60, 0x0C, 0x30, 0x06, 0x18, 0x03, 0x0F, 0xF1, 0x87, 0xFC, 0xC3, 0x07, 0x61, 0x81, 0xB0, 0xC0, 0xD0, 0x60, 0xF8, 0x3F, 0xEC, 0x1F, 0xE0,
+/* 0x9B */ 0x99, 0x92, 0xF4,
+/* 0x9C */ 0xC0, 0xC0, 0x30, 0x30, 0x0C, 0x0C, 0x03, 0x03, 0x00, 0xC0, 0xC0, 0x3F, 0xFF, 0xCF, 0xFF, 0xFB, 0x03, 0x07, 0xC0, 0xC0, 0xF0, 0x30, 0x3C, 0x0C, 0x1F, 0x03, 0xFE, 0xC0, 0xFF, 0x00,
+/* 0x9D */ 0x07, 0x07, 0x03, 0x03, 0x00, 0x06, 0x0F, 0x0D, 0x8C, 0xCC, 0x6C, 0x3C, 0x1E, 0x0F, 0x86, 0xE3, 0x39, 0x8E, 0xC3, 0xE0, 0xC0,
+/* 0x9E */ 0x30, 0x1F, 0xE3, 0xFC, 0x18, 0x03, 0x00, 0x67, 0x0D, 0xF9, 0xC7, 0x38, 0x66, 0x0C, 0xC1, 0x98, 0x33, 0x06, 0x60, 0xCC, 0x19, 0x83, 0x30, 0x66, 0x0C,
+/* 0x9F */ 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x0F, 0xFF, 0xFF, 0xC1, 0x80, 0x60,
+/* 0xA0 */
+/* 0xA1 */ 0x10, 0x40, 0xC4, 0x07, 0xE0, 0x1E, 0x0C, 0x01, 0xF0, 0x1D, 0x80, 0xCE, 0x0E, 0x30, 0x61, 0xC7, 0x06, 0x30, 0x3B, 0x81, 0xD8, 0x07, 0xC0, 0x3C, 0x00, 0xE0, 0x06, 0x00, 0x70, 0x03, 0x80, 0x38, 0x01, 0xC0, 0x1C, 0x00,
+/* 0xA2 */ 0x21, 0x86, 0x20, 0xFC, 0x07, 0x0E, 0x06, 0xC0, 0xD8, 0x33, 0x86, 0x30, 0xC6, 0x30, 0xC6, 0x0C, 0xC1, 0xB0, 0x36, 0x03, 0xC0, 0x70, 0x0E, 0x01, 0x80, 0x30, 0x0E, 0x07, 0x80, 0xE0, 0x00,
+/* 0xA3 */ 0x01, 0x80, 0xC0, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x01, 0x80, 0xC0, 0x60, 0x3C, 0x1E, 0x0F, 0x07, 0xC7, 0x7F, 0x1F, 0x00,
+/* 0xA4 */ 0xDD, 0xFF, 0xD8, 0xD8, 0x3C, 0x1E, 0x0F, 0x8D, 0xFF, 0xDD, 0x80,
+/* 0xA5 */ 0x00, 0x60, 0x0F, 0xFF, 0xFF, 0xFC, 0x01, 0x80, 0x30, 0x06, 0x00, 0xC0, 0x18, 0x03, 0x00, 0x60, 0x0C, 0x01, 0x80, 0x30, 0x06, 0x00, 0xC0, 0x18, 0x03, 0x00, 0x60, 0x00,
+/* 0xA6 */ 0xFF, 0xFF, 0xF0, 0x3F, 0xFF, 0xFC,
+/* 0xA7 */ 0x0F, 0x03, 0xF0, 0xE7, 0x18, 0x63, 0x0C, 0x70, 0x07, 0x03, 0xF8, 0xC3, 0x98, 0x3B, 0x03, 0xF0, 0x37, 0x06, 0x78, 0xC7, 0xB0, 0x7C, 0x03, 0x80, 0x39, 0x83, 0x30, 0x67, 0x1C, 0x7F, 0x07, 0xC0,
+/* 0xA8 */ 0x19, 0x81, 0x98, 0x00, 0x0F, 0xFF, 0xFF, 0xFC, 0x00, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00, 0xC0, 0x0F, 0xFE, 0xFF, 0xEC, 0x00, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00, 0xC0, 0x0F, 0xFF, 0xFF, 0xF0,
+/* 0xA9 */ 0x03, 0xF0, 0x03, 0xFF, 0x01, 0xE0, 0xE0, 0xE3, 0x1C, 0x73, 0xF3, 0x99, 0x86, 0x6C, 0xC1, 0x8F, 0x30, 0x03, 0xCC, 0x00, 0xF3, 0x00, 0x3C, 0xC1, 0x8D, 0x98, 0x66, 0x77, 0xF3, 0x8E, 0x79, 0xC1, 0xC0, 0xE0, 0x3F, 0xF0, 0x03, 0xF0, 0x00,
+/* 0xAA */ 0x07, 0xE0, 0x3F, 0xF0, 0xF0, 0x71, 0x80, 0x76, 0x00, 0x6C, 0x00, 0x30, 0x00, 0x60, 0x00, 0xFF, 0xE1, 0xFF, 0xC3, 0x00, 0x06, 0x00, 0x0E, 0x00, 0x6C, 0x00, 0xDC, 0x03, 0x1C, 0x0E, 0x1F, 0xF8, 0x0F, 0xC0,
+/* 0xAB */ 0x21, 0x63, 0xE7, 0x84, 0x84, 0xE7, 0x63, 0x21,
+/* 0xAC */ 0xFF, 0xFF, 0xFF, 0x00, 0x30, 0x03, 0x00, 0x30, 0x03,
+/* 0xAD */ 0xFF, 0xF0,
+/* 0xAE */ 0x03, 0xF0, 0x03, 0xFF, 0x01, 0xE0, 0xE0, 0xFF, 0x1C, 0x7F, 0xF3, 0x9B, 0x04, 0x6C, 0xC1, 0x8F, 0x30, 0x43, 0xCF, 0xF0, 0xF3, 0xFC, 0x3C, 0xC1, 0x0D, 0xB0, 0x66, 0x7C, 0x1B, 0x8F, 0x07, 0xC1, 0xC0, 0xE0, 0x3F, 0xF0, 0x03, 0xF0, 0x00,
+/* 0xAF */ 0xC7, 0x8C, 0x01, 0x83, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC1, 0x83, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC1, 0x83, 0x00,
+/* 0xB0 */ 0x38, 0xFB, 0x1C, 0x18, 0x38, 0xDF, 0x1C,
+/* 0xB1 */ 0x06, 0x00, 0x60, 0x06, 0x00, 0x60, 0x7F, 0xE7, 0xFE, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xF0,
+/* 0xB2 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xF0,
+/* 0xB3 */ 0xF0, 0x3F, 0xFF, 0xFF, 0xF0,
+/* 0xB4 */ 0x03, 0x03, 0xFF, 0xFF, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0,
+/* 0xB5 */ 0xC0, 0xCC, 0x0C, 0xC0, 0xCC, 0x0C, 0xC0, 0xCC, 0x0C, 0xC0, 0xCC, 0x0C, 0xC0, 0xCC, 0x1C, 0xE3, 0xCF, 0xEF, 0xFC, 0x7C, 0x00, 0xC0, 0x0C, 0x00, 0xC0, 0x00,
+/* 0xB6 */ 0x1F, 0xE7, 0xFD, 0xF3, 0x7E, 0x6F, 0xCD, 0xF9, 0xBF, 0x37, 0xE6, 0x7C, 0xCF, 0x98, 0xF3, 0x06, 0x60, 0xCC, 0x19, 0x83, 0x30, 0x66, 0x0C, 0xC1, 0x98, 0x33, 0x06, 0x60, 0xCC,
+/* 0xB7 */ 0xF0,
+/* 0xB8 */ 0x19, 0x83, 0x30, 0x00, 0x00, 0x01, 0xF0, 0x7F, 0x1C, 0x77, 0x03, 0xC0, 0x7F, 0xFF, 0xFF, 0xE0, 0x0C, 0x01, 0xC0, 0xDC, 0x31, 0xFC, 0x1F, 0x00,
+/* 0xB9 */ 0xC0, 0xC0, 0x18, 0x18, 0x03, 0x83, 0x00, 0x70, 0x60, 0x0B, 0x0C, 0x01, 0x61, 0x8F, 0xA6, 0x33, 0x1C, 0xC6, 0x41, 0x88, 0xC8, 0x31, 0x99, 0x06, 0x13, 0x20, 0xC3, 0x66, 0x38, 0x6C, 0xEF, 0x07, 0x8F, 0xA0, 0xF0, 0x04, 0x0E, 0x00, 0x81, 0xC7, 0xF0, 0x18, 0xFC,
+/* 0xBA */ 0x1F, 0x87, 0xF9, 0xC3, 0x30, 0x3E, 0x01, 0xFE, 0x3F, 0xC6, 0x00, 0xE0, 0x0C, 0x0D, 0xC3, 0x9F, 0xE1, 0xF8,
+/* 0xBB */ 0x88, 0xC6, 0xE7, 0x21, 0x21, 0xE7, 0xC6, 0x88,
+/* 0xBC */ 0x33, 0x00, 0x03, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x3F, 0xE0,
+/* 0xBD */ 0x0F, 0xE0, 0x7F, 0xC3, 0x83, 0x98, 0x07, 0x60, 0x0D, 0x80, 0x07, 0x00, 0x1E, 0x00, 0x3F, 0x80, 0x3F, 0xC0, 0x0F, 0x80, 0x07, 0xC0, 0x0F, 0x00, 0x3E, 0x00, 0xDE, 0x0E, 0x3F, 0xF0, 0x3F, 0x80,
+/* 0xBE */ 0x3E, 0x1F, 0xEE, 0x1B, 0x00, 0xC0, 0x3C, 0x07, 0xF0, 0x3F, 0x00, 0xF0, 0x3E, 0x1D, 0xFE, 0x3E, 0x00,
+/* 0xBF */ 0xCF, 0x30, 0x00, 0x30, 0xC3, 0x0C, 0x30, 0xC3, 0x0C, 0x30, 0xC3, 0x0C, 0x30,
+/* 0xC0 */ 0x07, 0x80, 0x1E, 0x00, 0x78, 0x03, 0xF0, 0x0C, 0xC0, 0x33, 0x01, 0xCE, 0x06, 0x18, 0x18, 0x60, 0xE1, 0xC3, 0x03, 0x0F, 0xFC, 0x7F, 0xF9, 0x80, 0x66, 0x01, 0xB8, 0x07, 0xC0, 0x0F, 0x00, 0x30,
+/* 0xC1 */ 0xFF, 0xE7, 0xFF, 0x30, 0x01, 0x80, 0x0C, 0x00, 0x60, 0x03, 0x00, 0x1F, 0xF8, 0xFF, 0xF6, 0x01, 0xB0, 0x07, 0x80, 0x3C, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x06, 0xFF, 0xF7, 0xFE, 0x00,
+/* 0xC2 */ 0xFF, 0xC7, 0xFF, 0x30, 0x1D, 0x80, 0x6C, 0x03, 0x60, 0x1B, 0x00, 0xD8, 0x0C, 0xFF, 0xC7, 0xFF, 0x30, 0x0D, 0x80, 0x3C, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x06, 0xFF, 0xF7, 0xFE, 0x00,
+/* 0xC3 */ 0xFF, 0xFF, 0xFF, 0x00, 0x60, 0x0C, 0x01, 0x80, 0x30, 0x06, 0x00, 0xC0, 0x18, 0x03, 0x00, 0x60, 0x0C, 0x01, 0x80, 0x30, 0x06, 0x00, 0xC0, 0x18, 0x00,
+/* 0xC4 */ 0x07, 0xFE, 0x01, 0xFF, 0x80, 0x60, 0x60, 0x18, 0x18, 0x06, 0x06, 0x01, 0x81, 0x80, 0x60, 0x60, 0x18, 0x18, 0x06, 0x06, 0x01, 0x81, 0x80, 0x60, 0x60, 0x18, 0x18, 0x0E, 0x06, 0x03, 0x01, 0x80, 0xC0, 0x60, 0x70, 0x18, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x0F, 0x00, 0x03, 0xC0, 0x00, 0xC0,
+/* 0xC5 */ 0xFF, 0xFF, 0xFF, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00, 0xFF, 0xEF, 0xFE, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00, 0xFF, 0xFF, 0xFF,
+/* 0xC6 */ 0x70, 0x60, 0xE3, 0x06, 0x0C, 0x38, 0x61, 0xC1, 0xC6, 0x38, 0x0E, 0x67, 0x00, 0x66, 0x60, 0x03, 0x6C, 0x00, 0x3F, 0xC0, 0x01, 0xF8, 0x00, 0x1F, 0x80, 0x03, 0xFC, 0x00, 0x76, 0xE0, 0x0E, 0x67, 0x01, 0xC6, 0x38, 0x38, 0x61, 0xC3, 0x06, 0x0C, 0x60, 0x60, 0xEE, 0x06, 0x07,
+/* 0xC7 */ 0x0F, 0x81, 0xFF, 0x0C, 0x18, 0xC0, 0x66, 0x03, 0x00, 0x18, 0x01, 0xC0, 0x1C, 0x07, 0xC0, 0x3F, 0x00, 0x1C, 0x00, 0x7C, 0x01, 0xE0, 0x0F, 0x80, 0x6E, 0x0E, 0x3F, 0xE0, 0x7E, 0x00,
+/* 0xC8 */ 0xC0, 0x3E, 0x01, 0xF0, 0x1F, 0x80, 0xFC, 0x0D, 0xE0, 0xEF, 0x06, 0x78, 0x73, 0xC3, 0x1E, 0x30, 0xF1, 0x87, 0x98, 0x3D, 0xC1, 0xEC, 0x0F, 0xC0, 0x7E, 0x03, 0xE0, 0x1F, 0x00, 0xC0,
+/* 0xC9 */ 0x10, 0x40, 0xC4, 0x07, 0xE0, 0x1E, 0x0C, 0x03, 0xE0, 0x1F, 0x01, 0xF8, 0x0F, 0xC0, 0xDE, 0x0E, 0xF0, 0x67, 0x87, 0x3C, 0x31, 0xE3, 0x0F, 0x18, 0x79, 0x83, 0xDC, 0x1E, 0xC0, 0xFC, 0x07, 0xE0, 0x3E, 0x01, 0xF0, 0x0C,
+/* 0xCA */ 0xC0, 0x7C, 0x0E, 0xC1, 0xCC, 0x38, 0xC7, 0x0C, 0xE0, 0xDC, 0x0F, 0x80, 0xF0, 0x0F, 0x80, 0xDC, 0x0C, 0xE0, 0xC7, 0x0C, 0x30, 0xC3, 0x8C, 0x1C, 0xC0, 0xEC, 0x07,
+/* 0xCB */ 0x1F, 0xFC, 0x7F, 0xF1, 0x80, 0xC6, 0x03, 0x18, 0x0C, 0x60, 0x31, 0x80, 0xC6, 0x03, 0x18, 0x0C, 0x60, 0x31, 0x80, 0xC6, 0x03, 0x18, 0x0C, 0xC0, 0x33, 0x00, 0xDC, 0x03, 0xE0, 0x0F, 0x00, 0x30,
+/* 0xCC */ 0xE0, 0x07, 0xE0, 0x07, 0xF0, 0x0F, 0xF0, 0x0F, 0xD0, 0x0F, 0xD8, 0x1B, 0xD8, 0x1B, 0xD8, 0x1B, 0xCC, 0x33, 0xCC, 0x33, 0xCC, 0x33, 0xC6, 0x63, 0xC6, 0x63, 0xC6, 0x63, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC1, 0x83,
+/* 0xCD */ 0xC0, 0x1E, 0x00, 0xF0, 0x07, 0x80, 0x3C, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x03, 0xFF, 0xFF, 0xFF, 0xF0, 0x07, 0x80, 0x3C, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x03, 0xC0, 0x1E, 0x00, 0xC0,
+/* 0xCE */ 0x07, 0xF0, 0x0F, 0xFE, 0x0F, 0x07, 0x86, 0x00, 0xC6, 0x00, 0x33, 0x00, 0x1B, 0x00, 0x07, 0x80, 0x03, 0xC0, 0x01, 0xE0, 0x00, 0xF0, 0x00, 0x78, 0x00, 0x36, 0x00, 0x33, 0x00, 0x18, 0xC0, 0x18, 0x78, 0x3C, 0x1F, 0xFC, 0x03, 0xF8, 0x00,
+/* 0xCF */ 0xFF, 0xFF, 0xFF, 0xF0, 0x07, 0x80, 0x3C, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x03, 0xC0, 0x1E, 0x00, 0xF0, 0x07, 0x80, 0x3C, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x03, 0xC0, 0x1E, 0x00, 0xC0,
+/* 0xD0 */ 0xFF, 0x8F, 0xFE, 0xC0, 0x6C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, 0x3C, 0x06, 0xFF, 0xEF, 0xFC, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00,
+/* 0xD1 */ 0x07, 0xE0, 0x3F, 0xF0, 0xE0, 0x73, 0x80, 0x76, 0x00, 0x6C, 0x00, 0x30, 0x00, 0x60, 0x00, 0xC0, 0x01, 0x80, 0x03, 0x00, 0x06, 0x00, 0x0E, 0x00, 0x6C, 0x00, 0xDC, 0x03, 0x1E, 0x0E, 0x1F, 0xF8, 0x0F, 0xC0,
+/* 0xD2 */ 0xFF, 0xFF, 0xFF, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60,
+/* 0xD3 */ 0xC0, 0x1F, 0x01, 0xD8, 0x0C, 0xE0, 0xE3, 0x06, 0x1C, 0x70, 0x63, 0x03, 0xB8, 0x1D, 0x80, 0x7C, 0x03, 0xC0, 0x0E, 0x00, 0x60, 0x07, 0x00, 0x38, 0x03, 0x80, 0x1C, 0x01, 0xC0, 0x00,
+/* 0xD4 */ 0x00, 0xC0, 0x00, 0x30, 0x00, 0xFF, 0xC0, 0xFF, 0xFC, 0x78, 0xC7, 0x98, 0x30, 0x6E, 0x0C, 0x1F, 0x03, 0x03, 0xC0, 0xC0, 0xF0, 0x30, 0x3C, 0x0C, 0x0F, 0x83, 0x07, 0x60, 0xC1, 0x9E, 0x31, 0xE3, 0xFF, 0xF0, 0x3F, 0xF0, 0x00, 0xC0, 0x00, 0x30, 0x00,
+/* 0xD5 */ 0xE0, 0x1D, 0x80, 0xE7, 0x03, 0x0E, 0x1C, 0x18, 0x60, 0x73, 0x00, 0xFC, 0x01, 0xE0, 0x07, 0x00, 0x1E, 0x00, 0xF8, 0x03, 0x30, 0x1C, 0xE0, 0xE1, 0x83, 0x07, 0x1C, 0x0E, 0xE0, 0x1B, 0x00, 0x70,
+/* 0xD6 */ 0xC0, 0x19, 0x80, 0x33, 0x00, 0x66, 0x00, 0xCC, 0x01, 0x98, 0x03, 0x30, 0x06, 0x60, 0x0C, 0xC0, 0x19, 0x80, 0x33, 0x00, 0x66, 0x00, 0xCC, 0x01, 0x98, 0x03, 0x30, 0x06, 0x60, 0x0C, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x18, 0x00, 0x30, 0x00, 0x60,
+/* 0xD7 */ 0xC0, 0x3C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, 0x37, 0xFF, 0x3F, 0xF0, 0x03, 0x00, 0x30, 0x03, 0x00, 0x30, 0x03, 0x00, 0x30, 0x03,
+/* 0xD8 */ 0xC1, 0x83, 0xC1, 0x83, 0xC1, 0x83, 0xC1, 0x83, 0xC1, 0x83, 0xC1, 0x83, 0xC1, 0x83, 0xC1, 0x83, 0xC1, 0x83, 0xC1, 0x83, 0xC1, 0x83, 0xC1, 0x83, 0xC1, 0x83, 0xC1, 0x83, 0xC1, 0x83, 0xC1, 0x83, 0xFF, 0xFF, 0xFF, 0xFF,
+/* 0xD9 */ 0xC1, 0x83, 0x30, 0x60, 0xCC, 0x18, 0x33, 0x06, 0x0C, 0xC1, 0x83, 0x30, 0x60, 0xCC, 0x18, 0x33, 0x06, 0x0C, 0xC1, 0x83, 0x30, 0x60, 0xCC, 0x18, 0x33, 0x06, 0x0C, 0xC1, 0x83, 0x30, 0x60, 0xCC, 0x18, 0x33, 0x06, 0x0C, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x0C, 0x00, 0x03, 0x00, 0x00, 0xC0,
+/* 0xDA */ 0xFE, 0x00, 0x3F, 0x80, 0x00, 0x60, 0x00, 0x18, 0x00, 0x06, 0x00, 0x01, 0x80, 0x00, 0x60, 0x00, 0x1F, 0xF8, 0x07, 0xFF, 0x01, 0x80, 0xE0, 0x60, 0x1C, 0x18, 0x03, 0x06, 0x00, 0xC1, 0x80, 0x30, 0x60, 0x1C, 0x18, 0x0E, 0x07, 0xFF, 0x01, 0xFF, 0x80,
+/* 0xDB */ 0xC0, 0x01, 0xE0, 0x00, 0xF0, 0x00, 0x78, 0x00, 0x3C, 0x00, 0x1E, 0x00, 0x0F, 0x00, 0x07, 0xFF, 0x83, 0xFF, 0xE1, 0xE0, 0x38, 0xF0, 0x0E, 0x78, 0x03, 0x3C, 0x01, 0x9E, 0x00, 0xCF, 0x00, 0xE7, 0x80, 0xE3, 0xFF, 0xE1, 0xFF, 0xE0, 0xC0,
+/* 0xDC */ 0xC0, 0x06, 0x00, 0x30, 0x01, 0x80, 0x0C, 0x00, 0x60, 0x03, 0x00, 0x1F, 0xF8, 0xFF, 0xE6, 0x03, 0xB0, 0x0F, 0x80, 0x3C, 0x01, 0xE0, 0x0F, 0x00, 0xF8, 0x0E, 0xFF, 0xE7, 0xFE, 0x00,
+/* 0xDD */ 0x0F, 0xC0, 0x7F, 0xE1, 0xC1, 0xE7, 0x01, 0xCC, 0x01, 0xC0, 0x01, 0x80, 0x03, 0x00, 0x07, 0x1F, 0xFE, 0x3F, 0xFC, 0x00, 0x38, 0x00, 0x7C, 0x00, 0xD8, 0x03, 0x98, 0x0E, 0x38, 0x3C, 0x3F, 0xF0, 0x1F, 0x80,
+/* 0xDE */ 0xC0, 0x3F, 0x06, 0x07, 0xFE, 0x30, 0x70, 0x39, 0x87, 0x00, 0xEC, 0x30, 0x03, 0x61, 0x80, 0x1B, 0x18, 0x00, 0x78, 0xC0, 0x03, 0xFE, 0x00, 0x1F, 0xF0, 0x00, 0xF1, 0x80, 0x07, 0x8C, 0x00, 0x3C, 0x70, 0x03, 0xE1, 0x80, 0x1B, 0x0E, 0x01, 0xD8, 0x38, 0x1C, 0xC0, 0xFF, 0xC6, 0x01, 0xF8, 0x00,
+/* 0xDF */ 0x0F, 0xFC, 0xFF, 0xF3, 0x00, 0xD8, 0x03, 0x60, 0x0D, 0x80, 0x36, 0x00, 0xCC, 0x03, 0x3F, 0xFC, 0x3F, 0xF0, 0x38, 0xC1, 0xC3, 0x0E, 0x0C, 0x70, 0x31, 0x80, 0xCE, 0x03, 0x70, 0x0F, 0x80, 0x30,
+/* 0xE0 */ 0x3F, 0x07, 0xF8, 0xE1, 0xCC, 0x0C, 0x00, 0xC0, 0x1C, 0x3F, 0xCF, 0x8C, 0xC0, 0xCC, 0x0C, 0xE3, 0xC7, 0xEF, 0x3C, 0x70,
+/* 0xE1 */ 0x00, 0xC0, 0x38, 0x3F, 0x1F, 0x87, 0x00, 0xC0, 0x17, 0xC7, 0xFC, 0xF1, 0xDC, 0x1F, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xF0, 0x1F, 0x07, 0x71, 0xC7, 0xF0, 0x7C, 0x00,
+/* 0xE2 */ 0xFE, 0x3F, 0xEC, 0x3B, 0x06, 0xC1, 0xB0, 0xEF, 0xF3, 0x0E, 0xC0, 0xF0, 0x3C, 0x1F, 0xFE, 0xFF, 0x00,
+/* 0xE3 */ 0xFF, 0xFF, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC1, 0x83, 0x06, 0x0C, 0x00,
+/* 0xE4 */ 0x0F, 0xF0, 0x3F, 0xC0, 0xC3, 0x03, 0x0C, 0x0C, 0x30, 0x30, 0xC0, 0xC3, 0x03, 0x0C, 0x1C, 0x30, 0x60, 0xC1, 0x83, 0x3F, 0xFF, 0xFF, 0xFF, 0x00, 0x3C, 0x00, 0xC0,
+/* 0xE5 */ 0x1F, 0x07, 0xF1, 0xC7, 0x70, 0x3C, 0x07, 0xFF, 0xFF, 0xFE, 0x00, 0xC0, 0x1C, 0x0D, 0xC3, 0x1F, 0xC1, 0xF0,
+/* 0xE6 */ 0xE1, 0x87, 0x71, 0x8E, 0x39, 0x9C, 0x1D, 0xB8, 0x0F, 0xF0, 0x07, 0xE0, 0x07, 0xE0, 0x0F, 0xF0, 0x1D, 0xB8, 0x39, 0x9C, 0x71, 0x8E, 0xE1, 0x87, 0xC1, 0x83,
+/* 0xE7 */ 0x3E, 0x7F, 0xB0, 0xE0, 0x30, 0x18, 0x78, 0x3C, 0x07, 0x01, 0xE0, 0xF8, 0xEF, 0xE3, 0xE0,
+/* 0xE8 */ 0xC0, 0xF8, 0x3F, 0x07, 0xE1, 0xFC, 0x37, 0x8C, 0xF3, 0x9E, 0x63, 0xD8, 0x7F, 0x0F, 0xC1, 0xF8, 0x3E, 0x06,
+/* 0xE9 */ 0x21, 0x86, 0x20, 0xFC, 0x0F, 0x0C, 0x0F, 0x83, 0xF0, 0x7E, 0x1F, 0xC3, 0x78, 0xCF, 0x39, 0xE6, 0x3D, 0x87, 0xF0, 0xFC, 0x1F, 0x83, 0xE0, 0x60,
+/* 0xEA */ 0xC1, 0xE1, 0xB1, 0x99, 0x8D, 0x87, 0x83, 0xC1, 0xF0, 0xDC, 0x67, 0x31, 0xD8, 0x7C, 0x18,
+/* 0xEB */ 0x3F, 0xCF, 0xF3, 0x0C, 0xC3, 0x30, 0xCC, 0x33, 0x0C, 0xC3, 0x30, 0xDC, 0x36, 0x0F, 0x83, 0xC0, 0xC0,
+/* 0xEC */ 0xE0, 0x7E, 0x07, 0xF0, 0xFF, 0x0F, 0xF0, 0xFD, 0x9B, 0xD9, 0xBD, 0xFB, 0xCF, 0x3C, 0xF3, 0xC6, 0x3C, 0x63, 0xC0, 0x30,
+/* 0xED */ 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xFF, 0xFF, 0xFF, 0x03, 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xC0,
+/* 0xEE */ 0x1F, 0x07, 0xF1, 0xC7, 0x70, 0x7C, 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0x7C, 0x1D, 0xC7, 0x1F, 0xC1, 0xF0,
+/* 0xEF */ 0xFF, 0xFF, 0xFC, 0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xC0,
+/* 0xF0 */ 0xCF, 0x8D, 0xFC, 0xF0, 0xEE, 0x06, 0xC0, 0x3C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, 0x3E, 0x06, 0xF0, 0xEF, 0xFC, 0xCF, 0x8C, 0x00, 0xC0, 0x0C, 0x00, 0xC0, 0x00,
+/* 0xF1 */ 0x1F, 0x0F, 0xE6, 0x1F, 0x83, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x38, 0x37, 0x1C, 0xFE, 0x1F, 0x00,
+/* 0xF2 */ 0xFF, 0xFF, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+/* 0xF3 */ 0xE0, 0x6C, 0x0D, 0x83, 0x38, 0x63, 0x0C, 0x63, 0x0C, 0x60, 0xCC, 0x1B, 0x03, 0x60, 0x3C, 0x07, 0x00, 0xE0, 0x18, 0x03, 0x00, 0xE0, 0x78, 0x0E, 0x00,
+/* 0xF4 */ 0x00, 0xC0, 0x00, 0x18, 0x00, 0x03, 0x00, 0x0F, 0x67, 0x87, 0xFD, 0xF8, 0xC3, 0xE3, 0xB8, 0x78, 0x3E, 0x06, 0x03, 0xC0, 0xC0, 0x78, 0x18, 0x0F, 0x03, 0x01, 0xE0, 0x60, 0x3E, 0x1E, 0x0E, 0xC3, 0xE3, 0x9F, 0xFF, 0xE1, 0xF6, 0x78, 0x00, 0xC0, 0x00, 0x18, 0x00, 0x03, 0x00, 0x00, 0x60, 0x00,
+/* 0xF5 */ 0xC1, 0xF8, 0x66, 0x30, 0xCC, 0x3E, 0x07, 0x00, 0xC0, 0x78, 0x36, 0x0C, 0xC6, 0x3B, 0x06, 0xC0, 0xC0,
+/* 0xF6 */ 0xC0, 0xCC, 0x0C, 0xC0, 0xCC, 0x0C, 0xC0, 0xCC, 0x0C, 0xC0, 0xCC, 0x0C, 0xC0, 0xCC, 0x0C, 0xC0, 0xCF, 0xFF, 0xFF, 0xF0, 0x03, 0x00, 0x30,
+/* 0xF7 */ 0xC1, 0xE0, 0xF0, 0x78, 0x3C, 0x1E, 0x0F, 0xFE, 0xFF, 0x01, 0x80, 0xC0, 0x60, 0x30, 0x18,
+/* 0xF8 */ 0xC3, 0x0F, 0x0C, 0x3C, 0x30, 0xF0, 0xC3, 0xC3, 0x0F, 0x0C, 0x3C, 0x30, 0xF0, 0xC3, 0xC3, 0x0F, 0x0C, 0x3C, 0x30, 0xFF, 0xFF, 0xFF, 0xFC,
+/* 0xF9 */ 0xC3, 0x0C, 0xC3, 0x0C, 0xC3, 0x0C, 0xC3, 0x0C, 0xC3, 0x0C, 0xC3, 0x0C, 0xC3, 0x0C, 0xC3, 0x0C, 0xC3, 0x0C, 0xC3, 0x0C, 0xC3, 0x0C, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x03, 0x00, 0x03,
+/* 0xFA */ 0xFC, 0x03, 0xF0, 0x00, 0xC0, 0x03, 0x00, 0x0F, 0xF0, 0x3F, 0xE0, 0xC1, 0xC3, 0x03, 0x0C, 0x0C, 0x30, 0x30, 0xC1, 0xC3, 0xFE, 0x0F, 0xF0,
+/* 0xFB */ 0xC0, 0x0F, 0x00, 0x3C, 0x00, 0xF0, 0x03, 0xFF, 0x0F, 0xFE, 0x3C, 0x1C, 0xF0, 0x33, 0xC0, 0xCF, 0x03, 0x3C, 0x1C, 0xFF, 0xE3, 0xFF, 0x0C,
+/* 0xFC */ 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xFF, 0x3F, 0xEC, 0x1F, 0x03, 0xC0, 0xF0, 0x3C, 0x1F, 0xFE, 0xFF, 0x00,
+/* 0xFD */ 0x3F, 0x0F, 0xF1, 0x87, 0x60, 0x60, 0x0E, 0x3F, 0xC7, 0xF8, 0x03, 0x00, 0xD8, 0x1B, 0x87, 0x3F, 0xC3, 0xF0,
+/* 0xFE */ 0xC0, 0xF8, 0xC1, 0xFC, 0xC3, 0x8E, 0xC7, 0x07, 0xC6, 0x03, 0xFE, 0x03, 0xFE, 0x03, 0xC6, 0x03, 0xC6, 0x03, 0xC7, 0x06, 0xC3, 0x8E, 0xC1, 0xFC, 0xC0, 0xF8,
+/* 0xFF */ 0x1F, 0xCF, 0xF7, 0x0D, 0x83, 0x60, 0xD8, 0x33, 0xFC, 0x7F, 0x0C, 0xC6, 0x33, 0x0D, 0x83, 0xC0, 0xC0,
+};
+
+const GFXglyph FreeSans12pt_Win1251Glyphs[] PROGMEM = {
+/* 0x01 */ { 0, 19, 20, 21, 1, -17 },
+/* 0x02 */ { 48, 19, 20, 21, 1, -17 },
+/* 0x03 */ { 96, 21, 20, 23, 1, -17 },
+/* 0x04 */ { 149, 21, 20, 23, 1, -17 },
+/* 0x05 */ { 202, 20, 20, 22, 1, -17 },
+/* 0x06 */ { 252, 20, 20, 22, 1, -17 },
+/* 0x07 */ { 302, 0, 0, 8, 0, 0 },
+/* 0x08 */ { 302, 23, 20, 25, 1, -17 },
+/* 0x09 */ { 360, 23, 16, 25, 1, -16 },
+/* 0x0A */ { 406, 0, 0, 8, 0, 0 },
+/* 0x0B */ { 406, 21, 20, 23, 1, -17 },
+/* 0x0C */ { 459, 19, 18, 21, 1, -15 },
+/* 0x0D */ { 502, 0, 0, 8, 0, 0 },
+/* 0x0E */ { 502, 20, 20, 22, 1, -17 },
+/* 0x0F */ { 552, 21, 21, 23, 1, -18 },
+/* 0x10 */ { 608, 19, 20, 21, 1, -17 },
+/* 0x11 */ { 656, 21, 20, 23, 1, -17 },
+/* 0x12 */ { 709, 20, 20, 22, 1, -17 },
+/* 0x13 */ { 759, 21, 20, 23, 1, -17 },
+/* 0x14 */ { 812, 21, 20, 23, 1, -17 },
+/* 0x15 */ { 865, 22, 20, 24, 1, -17 },
+/* 0x16 */ { 920, 16, 20, 18, 1, -17 },
+/* 0x17 */ { 960, 21, 20, 23, 1, -17 },
+/* 0x18 */ { 1013, 23, 20, 25, 1, -17 },
+/* 0x19 */ { 1071, 21, 20, 23, 1, -17 },
+/* 0x1A */ { 1124, 15, 19, 17, 1, -16 },
+/* 0x1B */ { 1160, 24, 21, 26, 1, -18 },
+/* 0x1C */ { 1223, 21, 20, 23, 1, -17 },
+/* 0x1D */ { 1276, 21, 21, 23, 1, -18 },
+/* 0x1E */ { 1332, 20, 20, 22, 1, -17 },
+/* 0x1F */ { 1382, 15, 20, 17, 1, -17 },
+/* ' ' 0x20 */ { 1420, 0, 0, 6, 0, 0 },
+/* '!' 0x21 */ { 1420, 2, 18, 8, 3, -16 },
+/* '"' 0x22 */ { 1425, 6, 6, 8, 1, -15 },
+/* '#' 0x23 */ { 1430, 13, 16, 13, 0, -14 },
+/* '$' 0x24 */ { 1456, 11, 20, 13, 1, -16 },
+/* '%' 0x25 */ { 1484, 20, 17, 21, 1, -15 },
+/* '&' 0x26 */ { 1527, 14, 17, 16, 1, -15 },
+/* ''' 0x27 */ { 1557, 2, 6, 5, 1, -15 },
+/* '(' 0x28 */ { 1559, 5, 23, 8, 2, -16 },
+/* ')' 0x29 */ { 1574, 5, 23, 8, 1, -16 },
+/* '*' 0x2A */ { 1589, 7, 7, 9, 1, -16 },
+/* '+' 0x2B */ { 1596, 10, 11, 14, 2, -9 },
+/* ',' 0x2C */ { 1610, 2, 6, 7, 2, 0 },
+/* '-' 0x2D */ { 1612, 6, 2, 8, 1, -6 },
+/* '.' 0x2E */ { 1614, 2, 2, 6, 2, 0 },
+/* '/' 0x2F */ { 1615, 7, 18, 7, 0, -16 },
+/* '0' 0x30 */ { 1631, 11, 17, 13, 1, -15 },
+/* '1' 0x31 */ { 1655, 5, 17, 13, 3, -15 },
+/* '2' 0x32 */ { 1666, 11, 17, 13, 1, -15 },
+/* '3' 0x33 */ { 1690, 11, 17, 13, 1, -15 },
+/* '4' 0x34 */ { 1714, 11, 17, 13, 1, -15 },
+/* '5' 0x35 */ { 1738, 11, 17, 13, 1, -15 },
+/* '6' 0x36 */ { 1762, 11, 17, 13, 1, -15 },
+/* '7' 0x37 */ { 1786, 11, 17, 13, 1, -15 },
+/* '8' 0x38 */ { 1810, 11, 17, 13, 1, -15 },
+/* '9' 0x39 */ { 1834, 11, 17, 13, 1, -15 },
+/* ':' 0x3A */ { 1858, 2, 13, 6, 2, -11 },
+/* ';' 0x3B */ { 1862, 2, 16, 6, 2, -10 },
+/* '<' 0x3C */ { 1866, 12, 11, 14, 1, -9 },
+/* '=' 0x3D */ { 1883, 12, 6, 14, 1, -7 },
+/* '>' 0x3E */ { 1892, 12, 11, 14, 1, -9 },
+/* '?' 0x3F */ { 1909, 10, 18, 13, 2, -16 },
+/* '@' 0x40 */ { 1932, 22, 21, 24, 1, -16 },
+/* 'A' 0x41 */ { 1990, 14, 18, 16, 1, -16 },
+/* 'B' 0x42 */ { 2022, 13, 18, 16, 2, -16 },
+/* 'C' 0x43 */ { 2052, 15, 18, 17, 1, -16 },
+/* 'D' 0x44 */ { 2086, 14, 18, 17, 2, -16 },
+/* 'E' 0x45 */ { 2118, 12, 18, 15, 2, -16 },
+/* 'F' 0x46 */ { 2145, 11, 18, 14, 2, -16 },
+/* 'G' 0x47 */ { 2170, 16, 18, 18, 1, -16 },
+/* 'H' 0x48 */ { 2206, 13, 18, 17, 2, -16 },
+/* 'I' 0x49 */ { 2236, 2, 18, 7, 2, -16 },
+/* 'J' 0x4A */ { 2241, 9, 18, 13, 1, -16 },
+/* 'K' 0x4B */ { 2262, 13, 18, 16, 2, -16 },
+/* 'L' 0x4C */ { 2292, 10, 18, 14, 2, -16 },
+/* 'M' 0x4D */ { 2315, 16, 18, 20, 2, -16 },
+/* 'N' 0x4E */ { 2351, 13, 18, 18, 2, -16 },
+/* 'O' 0x4F */ { 2381, 17, 18, 19, 1, -16 },
+/* 'P' 0x50 */ { 2420, 12, 18, 16, 2, -16 },
+/* 'Q' 0x51 */ { 2447, 17, 19, 19, 1, -16 },
+/* 'R' 0x52 */ { 2488, 14, 18, 17, 2, -16 },
+/* 'S' 0x53 */ { 2520, 14, 18, 16, 1, -16 },
+/* 'T' 0x54 */ { 2552, 12, 18, 15, 1, -16 },
+/* 'U' 0x55 */ { 2579, 13, 18, 17, 2, -16 },
+/* 'V' 0x56 */ { 2609, 14, 18, 15, 1, -16 },
+/* 'W' 0x57 */ { 2641, 22, 18, 22, 0, -16 },
+/* 'X' 0x58 */ { 2691, 14, 18, 16, 1, -16 },
+/* 'Y' 0x59 */ { 2723, 14, 18, 16, 1, -16 },
+/* 'Z' 0x5A */ { 2755, 13, 18, 15, 1, -16 },
+/* '[' 0x5B */ { 2785, 4, 23, 7, 2, -16 },
+/* '\' 0x5C */ { 2797, 7, 18, 7, 0, -16 },
+/* ']' 0x5D */ { 2813, 4, 23, 7, 1, -16 },
+/* '^' 0x5E */ { 2825, 9, 9, 11, 1, -15 },
+/* '_' 0x5F */ { 2836, 15, 1, 13, -1, 5 },
+/* '`' 0x60 */ { 2838, 5, 4, 6, 1, -16 },
+/* 'a' 0x61 */ { 2841, 12, 13, 13, 1, -11 },
+/* 'b' 0x62 */ { 2861, 12, 18, 13, 1, -16 },
+/* 'c' 0x63 */ { 2888, 10, 13, 12, 1, -11 },
+/* 'd' 0x64 */ { 2905, 11, 18, 13, 1, -16 },
+/* 'e' 0x65 */ { 2930, 11, 13, 13, 1, -11 },
+/* 'f' 0x66 */ { 2948, 5, 18, 7, 1, -16 },
+/* 'g' 0x67 */ { 2960, 11, 18, 13, 1, -11 },
+/* 'h' 0x68 */ { 2985, 10, 18, 13, 1, -16 },
+/* 'i' 0x69 */ { 3008, 2, 18, 5, 2, -16 },
+/* 'j' 0x6A */ { 3013, 4, 23, 6, 0, -16 },
+/* 'k' 0x6B */ { 3025, 10, 18, 12, 1, -16 },
+/* 'l' 0x6C */ { 3048, 2, 18, 5, 1, -16 },
+/* 'm' 0x6D */ { 3053, 17, 13, 19, 1, -11 },
+/* 'n' 0x6E */ { 3081, 10, 13, 13, 1, -11 },
+/* 'o' 0x6F */ { 3098, 11, 13, 13, 1, -11 },
+/* 'p' 0x70 */ { 3116, 12, 17, 13, 1, -11 },
+/* 'q' 0x71 */ { 3142, 11, 17, 13, 1, -11 },
+/* 'r' 0x72 */ { 3166, 6, 13, 8, 1, -11 },
+/* 's' 0x73 */ { 3176, 10, 13, 12, 1, -11 },
+/* 't' 0x74 */ { 3193, 5, 16, 7, 1, -14 },
+/* 'u' 0x75 */ { 3203, 10, 13, 13, 1, -11 },
+/* 'v' 0x76 */ { 3220, 11, 13, 12, 0, -11 },
+/* 'w' 0x77 */ { 3238, 17, 13, 17, 0, -11 },
+/* 'x' 0x78 */ { 3266, 10, 13, 11, 1, -11 },
+/* 'y' 0x79 */ { 3283, 11, 18, 11, 0, -11 },
+/* 'z' 0x7A */ { 3308, 10, 13, 12, 1, -11 },
+/* '{' 0x7B */ { 3325, 5, 23, 8, 1, -16 },
+/* '|' 0x7C */ { 3340, 2, 23, 6, 2, -16 },
+/* '}' 0x7D */ { 3346, 5, 23, 8, 2, -16 },
+/* '~' 0x7E */ { 3361, 10, 5, 12, 1, -9 },
+/* 0x7F */ { 3368, 0, 0, 0, 0, 0 },
+/* 0x80 */ { 3368, 16, 22, 18, 1, -18 },
+/* 0x81 */ { 3412, 11, 22, 14, 2, -22 },
+/* 0x82 */ { 3443, 2, 5, 6, 2, -2 },
+/* 0x83 */ { 3445, 7, 18, 9, 1, -18 },
+/* 0x84 */ { 3461, 6, 5, 10, 2, -2 },
+/* 0x85 */ { 3465, 12, 2, 16, 2, -2 },
+/* 0x86 */ { 3468, 10, 21, 13, 2, -17 },
+/* 0x87 */ { 3495, 10, 20, 13, 2, -17 },
+/* 0x88 */ { 3520, 14, 17, 16, 1, -17 },
+/* 0x89 */ { 3550, 23, 18, 24, 0, -18 },
+/* 0x8A */ { 3602, 23, 18, 24, 0, -18 },
+/* 0x8B */ { 3654, 3, 8, 6, 1, -11 },
+/* 0x8C */ { 3657, 21, 18, 24, 2, -18 },
+/* 0x8D */ { 3705, 12, 22, 15, 2, -22 },
+/* 0x8E */ { 3738, 16, 18, 18, 1, -18 },
+/* 0x8F */ { 3774, 13, 21, 17, 2, -18 },
+/* 0x90 */ { 3809, 12, 22, 14, 0, -18 },
+/* 0x91 */ { 3842, 2, 6, 6, 2, -18 },
+/* 0x92 */ { 3844, 2, 6, 6, 2, -18 },
+/* 0x93 */ { 3846, 6, 6, 10, 2, -18 },
+/* 0x94 */ { 3851, 6, 6, 10, 2, -18 },
+/* 0x95 */ { 3856, 6, 6, 10, 2, -11 },
+/* 0x96 */ { 3861, 10, 2, 12, 1, -8 },
+/* 0x97 */ { 3864, 22, 2, 24, 1, -8 },
+/* 0x98 */ { 3870, 0, 0, 8, 0, 0 },
+/* 0x99 */ { 3870, 22, 13, 24, 2, -18 },
+/* 0x9A */ { 3906, 17, 13, 19, 1, -13 },
+/* 0x9B */ { 3934, 3, 8, 6, 2, -10 },
+/* 0x9C */ { 3937, 18, 13, 20, 1, -13 },
+/* 0x9D */ { 3967, 9, 18, 12, 1, -18 },
+/* 0x9E */ { 3988, 11, 18, 14, 1, -18 },
+/* 0x9F */ { 4013, 10, 15, 13, 1, -13 },
+/* 0xA0 */ { 4032, 0, 0, 7, 0, 0 },
+/* 0xA1 */ { 4032, 13, 22, 15, 1, -22 },
+/* 0xA2 */ { 4068, 11, 22, 11, 0, -17 },
+/* 0xA3 */ { 4099, 9, 18, 13, 1, -18 },
+/* 0xA4 */ { 4120, 9, 9, 13, 2, -13 },
+/* 0xA5 */ { 4131, 11, 20, 14, 2, -20 },
+/* 0xA6 */ { 4159, 2, 23, 6, 2, -18 },
+/* 0xA7 */ { 4165, 11, 23, 13, 1, -18 },
+/* 0xA8 */ { 4197, 12, 21, 15, 2, -21 },
+/* 0xA9 */ { 4229, 18, 17, 19, 1, -17 },
+/* 0xAA */ { 4268, 15, 18, 17, 1, -18 },
+/* 0xAB */ { 4302, 8, 8, 12, 2, -11 },
+/* 0xAC */ { 4310, 12, 6, 14, 1, -9 },
+/* 0xAD */ { 4319, 6, 2, 8, 1, -8 },
+/* 0xAE */ { 4321, 18, 17, 19, 1, -17 },
+/* 0xAF */ { 4360, 7, 21, 7, 0, -21 },
+/* 0xB0 */ { 4379, 7, 8, 15, 4, -17 },
+/* 0xB1 */ { 4386, 12, 15, 14, 1, -15 },
+/* 0xB2 */ { 4409, 2, 18, 7, 2, -18 },
+/* 0xB3 */ { 4414, 2, 18, 5, 2, -18 },
+/* 0xB4 */ { 4419, 8, 15, 9, 1, -15 },
+/* 0xB5 */ { 4434, 12, 17, 13, 2, -13 },
+/* 0xB6 */ { 4460, 11, 21, 13, 2, -18 },
+/* 0xB7 */ { 4489, 2, 2, 6, 2, -8 },
+/* 0xB8 */ { 4490, 11, 17, 13, 1, -17 },
+/* 0xB9 */ { 4514, 19, 18, 22, 2, -18 },
+/* 0xBA */ { 4557, 11, 13, 12, 0, -13 },
+/* 0xBB */ { 4575, 8, 8, 12, 2, -10 },
+/* 0xBC */ { 4583, 4, 23, 6, 0, -18 },
+/* 0xBD */ { 4595, 14, 18, 16, 1, -18 },
+/* 0xBE */ { 4627, 10, 13, 12, 1, -13 },
+/* 0xBF */ { 4644, 6, 17, 6, 0, -17 },
+/* 0xC0 */ { 4657, 14, 18, 16, 1, -18 },
+/* 0xC1 */ { 4689, 13, 18, 16, 2, -18 },
+/* 0xC2 */ { 4719, 13, 18, 16, 2, -18 },
+/* 0xC3 */ { 4749, 11, 18, 14, 2, -18 },
+/* 0xC4 */ { 4774, 18, 21, 19, 1, -18 },
+/* 0xC5 */ { 4822, 12, 18, 15, 2, -18 },
+/* 0xC6 */ { 4849, 20, 18, 22, 1, -18 },
+/* 0xC7 */ { 4894, 13, 18, 16, 1, -18 },
+/* 0xC8 */ { 4924, 13, 18, 18, 2, -18 },
+/* 0xC9 */ { 4954, 13, 22, 18, 2, -22 },
+/* 0xCA */ { 4990, 12, 18, 15, 2, -18 },
+/* 0xCB */ { 5017, 14, 18, 16, 0, -18 },
+/* 0xCC */ { 5049, 16, 18, 20, 2, -18 },
+/* 0xCD */ { 5085, 13, 18, 17, 2, -18 },
+/* 0xCE */ { 5115, 17, 18, 19, 1, -18 },
+/* 0xCF */ { 5154, 13, 18, 17, 2, -18 },
+/* 0xD0 */ { 5184, 12, 18, 16, 2, -18 },
+/* 0xD1 */ { 5211, 15, 18, 17, 1, -18 },
+/* 0xD2 */ { 5245, 12, 18, 15, 1, -18 },
+/* 0xD3 */ { 5272, 13, 18, 15, 1, -18 },
+/* 0xD4 */ { 5302, 18, 18, 20, 1, -18 },
+/* 0xD5 */ { 5343, 14, 18, 16, 1, -18 },
+/* 0xD6 */ { 5375, 15, 21, 18, 2, -18 },
+/* 0xD7 */ { 5415, 12, 18, 15, 1, -18 },
+/* 0xD8 */ { 5442, 16, 18, 20, 2, -18 },
+/* 0xD9 */ { 5478, 18, 21, 20, 2, -18 },
+/* 0xDA */ { 5526, 18, 18, 20, 1, -18 },
+/* 0xDB */ { 5567, 17, 18, 21, 2, -18 },
+/* 0xDC */ { 5606, 13, 18, 16, 2, -18 },
+/* 0xDD */ { 5636, 15, 18, 17, 1, -18 },
+/* 0xDE */ { 5670, 21, 18, 24, 2, -18 },
+/* 0xDF */ { 5718, 14, 18, 16, 0, -18 },
+/* 0xE0 */ { 5750, 12, 13, 13, 1, -13 },
+/* 0xE1 */ { 5770, 11, 19, 13, 1, -19 },
+/* 0xE2 */ { 5797, 10, 13, 12, 1, -13 },
+/* 0xE3 */ { 5814, 7, 13, 9, 1, -13 },
+/* 0xE4 */ { 5826, 14, 15, 14, 0, -13 },
+/* 0xE5 */ { 5853, 11, 13, 13, 1, -13 },
+/* 0xE6 */ { 5871, 16, 13, 18, 1, -13 },
+/* 0xE7 */ { 5897, 9, 13, 12, 1, -13 },
+/* 0xE8 */ { 5912, 11, 13, 13, 1, -13 },
+/* 0xE9 */ { 5930, 11, 17, 13, 1, -17 },
+/* 0xEA */ { 5954, 9, 13, 12, 1, -13 },
+/* 0xEB */ { 5969, 10, 13, 12, 0, -13 },
+/* 0xEC */ { 5986, 12, 13, 14, 1, -13 },
+/* 0xED */ { 6006, 10, 13, 13, 1, -13 },
+/* 0xEE */ { 6023, 11, 13, 13, 1, -13 },
+/* 0xEF */ { 6041, 10, 13, 13, 1, -13 },
+/* 0xF0 */ { 6058, 12, 17, 13, 1, -13 },
+/* 0xF1 */ { 6084, 10, 13, 12, 1, -13 },
+/* 0xF2 */ { 6101, 8, 13, 10, 1, -13 },
+/* 0xF3 */ { 6114, 11, 18, 11, 0, -13 },
+/* 0xF4 */ { 6139, 19, 20, 20, 1, -16 },
+/* 0xF5 */ { 6187, 10, 13, 11, 1, -13 },
+/* 0xF6 */ { 6204, 12, 15, 13, 1, -13 },
+/* 0xF7 */ { 6227, 9, 13, 12, 1, -13 },
+/* 0xF8 */ { 6242, 14, 13, 16, 1, -13 },
+/* 0xF9 */ { 6265, 16, 15, 17, 1, -13 },
+/* 0xFA */ { 6295, 14, 13, 15, 1, -13 },
+/* 0xFB */ { 6318, 14, 13, 16, 1, -13 },
+/* 0xFC */ { 6341, 10, 13, 12, 1, -13 },
+/* 0xFD */ { 6358, 11, 13, 12, 1, -13 },
+/* 0xFE */ { 6376, 16, 13, 18, 1, -13 },
+/* 0xFF */ { 6402, 10, 13, 13, 1, -13 },
+};
+
+const GFXfont FreeSans12pt_Win1251 PROGMEM = {
+(uint8_t*)FreeSans12pt_Win1251Bitmaps,
+(GFXglyph*)FreeSans12pt_Win1251Glyphs,
+0x01, 0xFF, 19
+};
diff --git a/src/graphics/niche/Fonts/FreeSans12pt_Win1252.h b/src/graphics/niche/Fonts/FreeSans12pt_Win1252.h
new file mode 100644
index 000000000..752925d6d
--- /dev/null
+++ b/src/graphics/niche/Fonts/FreeSans12pt_Win1252.h
@@ -0,0 +1,527 @@
+// trunk-ignore-all(clang-format)
+#pragma once
+/* PROPERTIES
+
+FONT_NAME FreeSans12pt_Win1252
+*/
+const uint8_t FreeSans12pt_Win1252Bitmaps[] PROGMEM = {
+/* 0x01 */ 0x00, 0x30, 0x00, 0x09, 0x00, 0x01, 0x20, 0x00, 0x24, 0x00, 0x04, 0x80, 0x01, 0x90, 0x00, 0x62, 0x00, 0x30, 0xFE, 0x04, 0x10, 0x5F, 0x02, 0x0B, 0x00, 0x7F, 0xE0, 0x0C, 0x1C, 0x02, 0x83, 0x81, 0x9F, 0xF0, 0x02, 0x1E, 0x00, 0x41, 0xC0, 0x0E, 0x7F, 0x81, 0x78, 0x18, 0x62, 0x00, 0xFF, 0xC0,
+/* 0x02 */ 0x00, 0xFF, 0x80, 0x61, 0x13, 0xF0, 0x62, 0x60, 0x07, 0xFC, 0x00, 0x83, 0x80, 0x10, 0xF0, 0x33, 0xF6, 0x01, 0x41, 0xC0, 0x18, 0x38, 0x03, 0xFF, 0xE0, 0x47, 0x02, 0x08, 0x20, 0x61, 0xC4, 0x06, 0x17, 0x00, 0x22, 0x00, 0x02, 0x40, 0x00, 0x48, 0x00, 0x09, 0x00, 0x01, 0x20, 0x00, 0x3C, 0x00,
+/* 0x03 */ 0x01, 0xFC, 0x00, 0x38, 0x18, 0x02, 0x00, 0x30, 0x20, 0x00, 0xC2, 0x00, 0x02, 0x30, 0x00, 0x09, 0x04, 0x08, 0x48, 0x70, 0xE1, 0xC3, 0x87, 0x0E, 0x08, 0x10, 0x70, 0x00, 0x03, 0x80, 0x00, 0x14, 0x00, 0x00, 0xA1, 0x81, 0x8D, 0x87, 0xF0, 0x44, 0x00, 0x06, 0x30, 0x00, 0x60, 0xC0, 0x06, 0x03, 0x80, 0x60, 0x07, 0xFC, 0x00,
+/* 0x04 */ 0x01, 0xFC, 0x00, 0x38, 0x18, 0x02, 0x00, 0x30, 0x20, 0x00, 0xC2, 0x00, 0x02, 0x30, 0x00, 0x09, 0x10, 0x02, 0x48, 0xE0, 0x61, 0xC1, 0xCC, 0x0E, 0x78, 0x1C, 0x70, 0x00, 0x03, 0x80, 0x00, 0x14, 0xFF, 0xFC, 0xA6, 0x00, 0xCD, 0x9F, 0xFE, 0x44, 0x71, 0xE6, 0x30, 0xFC, 0x60, 0xC0, 0x06, 0x03, 0x80, 0x60, 0x07, 0xFC, 0x00,
+/* 0x05 */ 0x00, 0x18, 0x00, 0x00, 0x40, 0x01, 0x90, 0x01, 0xF4, 0x08, 0x12, 0x23, 0xC1, 0x91, 0x2C, 0x1C, 0x8A, 0xC3, 0x64, 0x64, 0x13, 0x22, 0x41, 0x98, 0x26, 0x2C, 0xC4, 0x22, 0x60, 0x42, 0x13, 0x04, 0x30, 0x80, 0x61, 0xA4, 0x02, 0x18, 0x20, 0x03, 0x41, 0x00, 0x20, 0x08, 0x02, 0x00, 0x60, 0x40, 0x03, 0xF8,
+/* 0x06 */ 0x00, 0x10, 0x00, 0x03, 0x00, 0x1C, 0x48, 0x00, 0xB4, 0x80, 0x09, 0xF9, 0xC0, 0xE0, 0xE4, 0x0C, 0x02, 0x8F, 0x80, 0x38, 0x88, 0x01, 0x0D, 0x00, 0x18, 0x30, 0x01, 0x60, 0x80, 0x13, 0x18, 0x03, 0xF2, 0xC0, 0x20, 0x26, 0x06, 0x07, 0xFF, 0xA0, 0x02, 0x39, 0x00, 0x14, 0x70, 0x01, 0xC3, 0x00, 0x18, 0x00,
+/* 0x07 */
+/* 0x08 */ 0x00, 0x1F, 0x80, 0x00, 0x60, 0x80, 0x01, 0x00, 0x80, 0x06, 0x00, 0x80, 0x3C, 0x01, 0x01, 0x8C, 0x02, 0x02, 0x08, 0x04, 0x04, 0x08, 0x0C, 0x38, 0x00, 0x04, 0x80, 0x00, 0x06, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x2E, 0xC0, 0x01, 0x83, 0x7E, 0x0C, 0x10, 0x37, 0xE2, 0x61, 0x00, 0x0C, 0xC6, 0x10, 0x98, 0x0C, 0x63, 0x00, 0x00, 0xC6, 0x00,
+/* 0x09 */ 0x00, 0x1F, 0x80, 0x00, 0x60, 0x80, 0x01, 0x00, 0x80, 0x06, 0x00, 0x80, 0x3C, 0x01, 0x01, 0x8C, 0x02, 0x02, 0x08, 0x04, 0x04, 0x08, 0x0C, 0x38, 0x00, 0x04, 0x80, 0x00, 0x06, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x2E, 0xC0, 0x01, 0x83, 0x7E, 0x0C, 0x00, 0x37, 0xE0,
+/* 0x0A */
+/* 0x0B */ 0x1F, 0x07, 0xC1, 0x86, 0x41, 0x10, 0x0C, 0x04, 0x80, 0x40, 0x18, 0x00, 0x00, 0xC0, 0x00, 0x06, 0x00, 0x00, 0x30, 0x00, 0x01, 0x40, 0x00, 0x0A, 0x00, 0x00, 0x88, 0x00, 0x04, 0x40, 0x00, 0x41, 0x00, 0x02, 0x04, 0x00, 0x20, 0x20, 0x02, 0x00, 0x80, 0x20, 0x02, 0x02, 0x00, 0x08, 0x20, 0x00, 0x22, 0x00, 0x00, 0xE0, 0x00,
+/* 0x0C */ 0x01, 0x00, 0x00, 0x38, 0x00, 0x04, 0xC0, 0x01, 0x08, 0x00, 0x18, 0x80, 0x1C, 0x10, 0x02, 0x07, 0x80, 0x81, 0x10, 0x1F, 0xC2, 0x02, 0x00, 0x60, 0x80, 0x1A, 0x20, 0x1C, 0x42, 0x1C, 0x08, 0xFE, 0x03, 0xA0, 0x01, 0x8C, 0x01, 0xC1, 0x43, 0xD0, 0x27, 0x81, 0xF8,
+/* 0x0D */
+/* 0x0E */ 0x00, 0xE0, 0x00, 0x11, 0x00, 0x01, 0x10, 0x00, 0x0B, 0x00, 0x03, 0xF8, 0x00, 0x60, 0x60, 0x09, 0x02, 0x00, 0xA0, 0x10, 0x16, 0x01, 0x01, 0x40, 0x10, 0x10, 0x01, 0x01, 0x00, 0x08, 0x10, 0x00, 0x82, 0x1F, 0x08, 0x3F, 0x90, 0x44, 0x00, 0x06, 0xBF, 0xFF, 0xAF, 0xF0, 0xFF, 0xFF, 0x0F, 0xE3, 0xFB, 0xFC,
+/* 0x0F */ 0x01, 0xFC, 0x00, 0x38, 0x18, 0x02, 0x00, 0x30, 0x20, 0x00, 0xC2, 0x40, 0x12, 0x34, 0x00, 0x69, 0x40, 0x01, 0x49, 0xE0, 0xF1, 0xCD, 0x06, 0x8E, 0x28, 0x14, 0x71, 0x40, 0xA3, 0x8B, 0xFD, 0x14, 0x50, 0x68, 0xA2, 0x81, 0x4D, 0x97, 0xFA, 0x44, 0xBF, 0xD6, 0x31, 0x02, 0xE0, 0xC8, 0x16, 0x08, 0x61, 0x08, 0x21, 0xF0, 0x80, 0xF8, 0x78, 0x00,
+/* 0x10 */ 0x00, 0xF0, 0x00, 0x3A, 0x00, 0x07, 0xC0, 0x00, 0xA8, 0x00, 0x1F, 0x00, 0x02, 0xB0, 0x00, 0x52, 0x00, 0x0A, 0x40, 0x02, 0x48, 0x00, 0x49, 0x00, 0x09, 0x30, 0x01, 0x22, 0x01, 0xC4, 0x70, 0xF0, 0x85, 0xE1, 0x10, 0x88, 0x37, 0x20, 0x03, 0x9C, 0x00, 0x37, 0x00, 0x06, 0x40, 0x01, 0x86, 0x00,
+/* 0x11 */ 0x01, 0xFC, 0x00, 0x38, 0x18, 0x02, 0x00, 0x30, 0x20, 0x00, 0xC2, 0x60, 0x02, 0x36, 0x00, 0x09, 0x04, 0x0C, 0x48, 0x60, 0xC1, 0xC3, 0x0F, 0x0E, 0x00, 0x08, 0x70, 0x00, 0x23, 0x80, 0x63, 0x84, 0x01, 0x9F, 0x20, 0x0C, 0xFD, 0x80, 0x27, 0xE4, 0x03, 0x3F, 0x30, 0x33, 0xE0, 0xC0, 0x00, 0x03, 0x80, 0x60, 0x07, 0xFC, 0x00,
+/* 0x12 */ 0x00, 0xC2, 0x00, 0x1C, 0x24, 0x02, 0x18, 0x60, 0x64, 0x02, 0x02, 0x40, 0x20, 0x00, 0xF2, 0x03, 0x89, 0xE0, 0x7C, 0x80, 0x0E, 0x25, 0x80, 0xE1, 0x00, 0x1A, 0x08, 0x71, 0xB0, 0xC4, 0x39, 0x84, 0xC2, 0xCC, 0x40, 0x76, 0x7C, 0x05, 0xBB, 0x80, 0x4C, 0xE0, 0x0A, 0x78, 0x00, 0x9C, 0x00, 0x0F, 0x00, 0x00,
+/* 0x13 */ 0x01, 0xFC, 0x00, 0x38, 0x18, 0x02, 0x00, 0x30, 0x20, 0x00, 0xC2, 0x00, 0x02, 0x30, 0x00, 0x09, 0x00, 0x00, 0x48, 0x60, 0xC1, 0xC6, 0xC9, 0x0E, 0x00, 0x00, 0x70, 0x00, 0x03, 0x80, 0x00, 0x14, 0xFF, 0xF8, 0xA6, 0x00, 0xCD, 0x9F, 0xFE, 0x44, 0x71, 0xE6, 0x30, 0xFC, 0x60, 0xC0, 0x06, 0x03, 0x80, 0x60, 0x07, 0xFC, 0x00,
+/* 0x14 */ 0x01, 0xFC, 0x00, 0x38, 0x18, 0x02, 0x00, 0x30, 0x20, 0x00, 0xC2, 0x20, 0x22, 0x33, 0x01, 0x89, 0x20, 0x02, 0x48, 0x60, 0xE1, 0xC8, 0x80, 0x8E, 0x46, 0x46, 0x72, 0x32, 0x33, 0x9F, 0x9F, 0x94, 0x78, 0x78, 0xA0, 0x00, 0x0D, 0x80, 0x00, 0x44, 0x0E, 0x06, 0x30, 0x00, 0x60, 0xC0, 0x06, 0x03, 0x80, 0x60, 0x07, 0xFC, 0x00,
+/* 0x15 */ 0x03, 0xFC, 0x20, 0x38, 0x1C, 0x81, 0x80, 0x1D, 0x08, 0x00, 0x32, 0x60, 0x00, 0x89, 0x00, 0x02, 0x18, 0x00, 0x08, 0x61, 0xC3, 0x22, 0x8D, 0x93, 0x72, 0x00, 0x00, 0x48, 0x00, 0x01, 0x20, 0x00, 0x04, 0x9F, 0xFF, 0x92, 0x60, 0x0E, 0x44, 0xFF, 0xF2, 0x11, 0xC3, 0x88, 0x21, 0xF8, 0x40, 0x40, 0x02, 0x00, 0xC0, 0x30, 0x00, 0xFF, 0x00,
+/* 0x16 */ 0x03, 0x80, 0x03, 0xC0, 0x01, 0xE0, 0x01, 0xE0, 0x03, 0xF0, 0x03, 0xF0, 0x27, 0xF0, 0x6F, 0x70, 0x6E, 0x60, 0xFC, 0x60, 0xFC, 0x7E, 0xFC, 0x7E, 0xFC, 0x3F, 0xF4, 0x1F, 0xF4, 0x1F, 0xF0, 0x0E, 0x70, 0x0E, 0x30, 0x1C, 0x38, 0x38, 0x0F, 0xF0,
+/* 0x17 */ 0x01, 0xFC, 0x00, 0x38, 0x18, 0x02, 0x00, 0x30, 0x20, 0x00, 0xC2, 0x00, 0x02, 0x30, 0x00, 0x09, 0x00, 0x00, 0x48, 0x00, 0x21, 0xC0, 0x02, 0x8E, 0x20, 0xF4, 0x70, 0x84, 0x11, 0x82, 0x40, 0x84, 0x01, 0x03, 0x20, 0x0F, 0x85, 0x80, 0x03, 0x04, 0x00, 0x04, 0x30, 0x78, 0x10, 0xC0, 0x06, 0x03, 0x80, 0x60, 0x07, 0xFC, 0x00,
+/* 0x18 */ 0x00, 0xFC, 0x00, 0x02, 0x06, 0x00, 0x08, 0x24, 0x00, 0x21, 0xA4, 0x00, 0x4C, 0x48, 0x00, 0xA0, 0x50, 0x01, 0x92, 0x60, 0x03, 0x24, 0xC0, 0x06, 0x01, 0x81, 0x28, 0x03, 0x49, 0x6C, 0xC4, 0xAD, 0xD8, 0x16, 0xA4, 0xCC, 0xC4, 0x44, 0x86, 0x13, 0x05, 0x00, 0x28, 0x0A, 0x00, 0x50, 0x14, 0x00, 0x90, 0x48, 0x01, 0x20, 0x90, 0x02, 0x41, 0x20, 0x00, 0x00,
+/* 0x19 */ 0x01, 0xFC, 0x00, 0x38, 0x18, 0x02, 0x00, 0x30, 0x20, 0x00, 0xC2, 0x00, 0x02, 0x30, 0x00, 0x09, 0x00, 0x00, 0x49, 0xC3, 0x81, 0xC0, 0x00, 0x0E, 0x78, 0xF0, 0x77, 0xEF, 0xC3, 0xA7, 0x4E, 0x15, 0x0A, 0x10, 0xA7, 0x8F, 0x0D, 0x80, 0x00, 0x44, 0x00, 0x06, 0x33, 0xF0, 0x60, 0xC0, 0x06, 0x03, 0x80, 0x60, 0x07, 0xFC, 0x00,
+/* 0x1A */ 0xFF, 0xFF, 0x00, 0x06, 0x00, 0x0C, 0x3E, 0x18, 0x82, 0x32, 0x02, 0x64, 0x04, 0xC8, 0x09, 0x80, 0x23, 0x00, 0x86, 0x02, 0x0C, 0x08, 0x18, 0x10, 0x30, 0x00, 0x60, 0x00, 0xC0, 0x81, 0x80, 0x03, 0x00, 0x07, 0xFF, 0xF8,
+/* 0x1B */ 0x00, 0xFE, 0x00, 0x03, 0x81, 0x80, 0x04, 0x00, 0x60, 0x08, 0x00, 0x30, 0x10, 0x00, 0x10, 0x30, 0x07, 0x88, 0x23, 0xC8, 0x08, 0x22, 0x00, 0x04, 0x60, 0x00, 0x44, 0x60, 0x00, 0x84, 0x63, 0x03, 0x04, 0x61, 0xFC, 0x04, 0x6B, 0x00, 0x9E, 0xA5, 0x01, 0x6A, 0xD5, 0x01, 0x43, 0xA8, 0x81, 0x05, 0xD0, 0x82, 0x0A, 0xA0, 0x82, 0x05, 0xC0, 0x82, 0x02, 0x61, 0xFF, 0x0C, 0x1E, 0x00, 0xF0,
+/* 0x1C */ 0x01, 0xFC, 0x00, 0x38, 0x18, 0x02, 0x00, 0x30, 0x20, 0x00, 0xC2, 0x30, 0x02, 0x32, 0x00, 0x09, 0x00, 0x00, 0x48, 0x20, 0x61, 0xC3, 0x84, 0x0E, 0x1C, 0x78, 0x70, 0x40, 0x03, 0x80, 0x00, 0x14, 0x00, 0x00, 0xA0, 0x03, 0x0D, 0x83, 0xF0, 0x44, 0x00, 0x06, 0x30, 0x00, 0x60, 0xC0, 0x06, 0x03, 0x80, 0x60, 0x07, 0xFC, 0x00,
+/* 0x1D */ 0x01, 0xFE, 0x00, 0x3A, 0x1C, 0x03, 0x00, 0x30, 0x23, 0x1E, 0xC3, 0x38, 0x03, 0x10, 0xC3, 0x09, 0x00, 0x18, 0x68, 0x00, 0xC1, 0x40, 0x00, 0x0A, 0x07, 0x80, 0x50, 0x46, 0x02, 0x80, 0x00, 0x1A, 0x1E, 0x00, 0xCB, 0x10, 0x0D, 0x03, 0x00, 0x48, 0x60, 0x06, 0x40, 0x00, 0x22, 0x0C, 0x02, 0x10, 0x60, 0x60, 0x43, 0xFC, 0x01, 0xE0, 0x00, 0x00,
+/* 0x1E */ 0x01, 0xF0, 0x00, 0xEA, 0xC0, 0x31, 0x5F, 0x04, 0x5F, 0x88, 0x80, 0xA0, 0x48, 0x0E, 0x02, 0x8F, 0x40, 0x3C, 0x10, 0x21, 0x66, 0x87, 0x15, 0x98, 0x71, 0x41, 0x02, 0x14, 0x00, 0x01, 0x40, 0x00, 0x14, 0x00, 0x01, 0x21, 0xFE, 0x12, 0x00, 0x02, 0x10, 0x00, 0x60, 0x80, 0x0C, 0x06, 0x01, 0x80, 0x3F, 0xE0,
+/* 0x1F */ 0x0E, 0x00, 0x13, 0x00, 0x23, 0x00, 0xF3, 0x01, 0x31, 0x01, 0x11, 0x03, 0xD3, 0x06, 0xF2, 0x30, 0x34, 0xC7, 0x25, 0x33, 0x2B, 0xC2, 0x57, 0x04, 0x3A, 0x08, 0x72, 0x30, 0xA3, 0xC3, 0x40, 0x04, 0x40, 0x18, 0x40, 0x60, 0x7F, 0x80,
+/* ' ' 0x20 */
+/* '!' 0x21 */ 0xFF, 0xFF, 0xFF, 0xF0, 0xF0,
+/* '"' 0x22 */ 0xCF, 0x3C, 0xF3, 0x8A, 0x20,
+/* '#' 0x23 */ 0x06, 0x30, 0x31, 0x03, 0x18, 0x18, 0xC7, 0xFF, 0xBF, 0xFC, 0x31, 0x01, 0x18, 0x18, 0xC7, 0xFF, 0xBF, 0xFC, 0x31, 0x01, 0x18, 0x18, 0xC0, 0xC6, 0x06, 0x30,
+/* '$' 0x24 */ 0x04, 0x03, 0xE1, 0xFF, 0x72, 0x7C, 0x47, 0x88, 0xF1, 0x07, 0xA0, 0x7E, 0x03, 0xF0, 0x17, 0x02, 0x7C, 0x47, 0x88, 0xF1, 0x1B, 0x26, 0x7F, 0xC3, 0xE0, 0x10, 0x02, 0x00,
+/* '%' 0x25 */ 0x00, 0x06, 0x03, 0xC0, 0x40, 0x7E, 0x0C, 0x0E, 0x70, 0x80, 0xC3, 0x18, 0x0C, 0x31, 0x00, 0xE7, 0x30, 0x07, 0xE6, 0x00, 0x3C, 0x40, 0x00, 0x0C, 0x7C, 0x00, 0x8F, 0xE0, 0x19, 0xC7, 0x01, 0x18, 0x30, 0x31, 0x83, 0x02, 0x1C, 0x70, 0x40, 0xFE, 0x04, 0x07, 0xC0,
+/* '&' 0x26 */ 0x0F, 0x00, 0x7E, 0x03, 0x9C, 0x0C, 0x30, 0x30, 0xC0, 0xE7, 0x01, 0xF8, 0x03, 0x80, 0x3E, 0x01, 0xCC, 0x6E, 0x39, 0xB0, 0x7C, 0xC0, 0xF3, 0x03, 0xCE, 0x1F, 0x9F, 0xE6, 0x3E, 0x1C,
+/* ''' 0x27 */ 0xFF, 0xA0,
+/* '(' 0x28 */ 0x08, 0x8C, 0x46, 0x31, 0x98, 0xC6, 0x31, 0x8C, 0x63, 0x08, 0x63, 0x08, 0x61, 0x0C, 0x20,
+/* ')' 0x29 */ 0x82, 0x18, 0xC3, 0x18, 0xC3, 0x18, 0xC6, 0x31, 0x8C, 0x62, 0x31, 0x88, 0xC4, 0x62, 0x00,
+/* '*' 0x2A */ 0x10, 0x23, 0x5B, 0xE3, 0x8D, 0x91, 0x00,
+/* '+' 0x2B */ 0x0C, 0x03, 0x00, 0xC0, 0x30, 0xFF, 0xFF, 0xF0, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0,
+/* ',' 0x2C */ 0xF5, 0x60,
+/* '-' 0x2D */ 0xFF, 0xF0,
+/* '.' 0x2E */ 0xF0,
+/* '/' 0x2F */ 0x02, 0x0C, 0x10, 0x20, 0xC1, 0x02, 0x0C, 0x10, 0x20, 0xC1, 0x02, 0x0C, 0x10, 0x20, 0xC1, 0x00,
+/* '0' 0x30 */ 0x1F, 0x07, 0xF1, 0xC7, 0x30, 0x6C, 0x0F, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x3E, 0x0E, 0xC1, 0x9C, 0x71, 0xFC, 0x1F, 0x00,
+/* '1' 0x31 */ 0x08, 0xCF, 0xFF, 0x8C, 0x63, 0x18, 0xC6, 0x31, 0x8C, 0x63, 0x18,
+/* '2' 0x32 */ 0x1F, 0x0F, 0xF9, 0x87, 0x60, 0x7C, 0x06, 0x00, 0xC0, 0x18, 0x07, 0x01, 0xC0, 0xF0, 0x78, 0x1C, 0x06, 0x00, 0xC0, 0x30, 0x07, 0xFF, 0xFF, 0xE0,
+/* '3' 0x33 */ 0x3F, 0x0F, 0xF3, 0x87, 0x60, 0x6C, 0x0C, 0x01, 0x80, 0x60, 0x78, 0x0F, 0x80, 0x18, 0x01, 0x80, 0x3C, 0x07, 0x80, 0xD8, 0x73, 0xFC, 0x3F, 0x00,
+/* '4' 0x34 */ 0x01, 0x80, 0x70, 0x0E, 0x03, 0xC0, 0xD8, 0x1B, 0x06, 0x61, 0x8C, 0x21, 0x8C, 0x33, 0x06, 0x7F, 0xFF, 0xFE, 0x03, 0x00, 0x60, 0x0C, 0x01, 0x80,
+/* '5' 0x35 */ 0x3F, 0xCF, 0xF9, 0x80, 0x30, 0x06, 0x00, 0xDE, 0x1F, 0xE7, 0x0E, 0x00, 0xE0, 0x0C, 0x01, 0x80, 0x30, 0x07, 0x81, 0xB8, 0x73, 0xFC, 0x1F, 0x00,
+/* '6' 0x36 */ 0x0F, 0x07, 0xF9, 0xC3, 0x30, 0x74, 0x01, 0x80, 0x33, 0xC7, 0xFE, 0xF1, 0xDC, 0x1F, 0x01, 0xE0, 0x3C, 0x06, 0xC1, 0xDC, 0x71, 0xFC, 0x1F, 0x00,
+/* '7' 0x37 */ 0xFF, 0xFF, 0xFC, 0x01, 0x00, 0x60, 0x18, 0x02, 0x00, 0xC0, 0x30, 0x06, 0x01, 0x80, 0x30, 0x04, 0x01, 0x80, 0x30, 0x06, 0x01, 0x80, 0x30, 0x00,
+/* '8' 0x38 */ 0x1F, 0x07, 0xF1, 0xC7, 0x30, 0x66, 0x0C, 0xC1, 0x8C, 0x61, 0xF8, 0x3F, 0x8E, 0x3B, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xD8, 0x31, 0xFC, 0x1F, 0x00,
+/* '9' 0x39 */ 0x1F, 0x07, 0xF1, 0xC7, 0x70, 0x6C, 0x07, 0x80, 0xF0, 0x1E, 0x07, 0x61, 0xEF, 0xFC, 0x79, 0x80, 0x30, 0x05, 0xC1, 0x98, 0x73, 0xFC, 0x1E, 0x00,
+/* ':' 0x3A */ 0xF0, 0x00, 0x03, 0xC0,
+/* ';' 0x3B */ 0xF0, 0x00, 0x0F, 0x56,
+/* '<' 0x3C */ 0x00, 0x70, 0x1E, 0x0F, 0x83, 0xC0, 0xF0, 0x0E, 0x00, 0x7C, 0x00, 0xF0, 0x03, 0xC0, 0x0F, 0x00, 0x10,
+/* '=' 0x3D */ 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF,
+/* '>' 0x3E */ 0xE0, 0x07, 0x80, 0x1F, 0x00, 0x7C, 0x00, 0xF0, 0x07, 0x01, 0xE0, 0xF0, 0x3C, 0x0F, 0x00, 0x80, 0x00,
+/* '?' 0x3F */ 0x3F, 0x1F, 0xEE, 0x1F, 0x03, 0xC0, 0xC0, 0x30, 0x0C, 0x06, 0x03, 0x81, 0xC0, 0xE0, 0x30, 0x0C, 0x03, 0x00, 0x00, 0x00, 0x0C, 0x03, 0x00,
+/* '@' 0x40 */ 0x00, 0xFE, 0x00, 0x0F, 0xFE, 0x00, 0xF0, 0x3E, 0x07, 0x00, 0x3C, 0x38, 0x00, 0x38, 0xC1, 0xE0, 0x66, 0x0F, 0xD9, 0xD8, 0x61, 0xC3, 0xC3, 0x07, 0x0F, 0x1C, 0x1C, 0x3C, 0x60, 0x60, 0xF1, 0x81, 0x83, 0xC6, 0x06, 0x1B, 0x18, 0x38, 0xEE, 0x71, 0xE7, 0x18, 0xFD, 0xF8, 0x71, 0xE7, 0xC0, 0xE0, 0x00, 0x01, 0xE0, 0x00, 0x01, 0xFF, 0xC0, 0x01, 0xFC, 0x00,
+/* 'A' 0x41 */ 0x07, 0x80, 0x1E, 0x00, 0x78, 0x03, 0xF0, 0x0C, 0xC0, 0x33, 0x01, 0xCE, 0x06, 0x18, 0x18, 0x60, 0xE1, 0xC3, 0x03, 0x0F, 0xFC, 0x7F, 0xF9, 0x80, 0x66, 0x01, 0xB8, 0x07, 0xC0, 0x0F, 0x00, 0x30,
+/* 'B' 0x42 */ 0xFF, 0xC7, 0xFF, 0x30, 0x1D, 0x80, 0x6C, 0x03, 0x60, 0x1B, 0x00, 0xD8, 0x0C, 0xFF, 0xC7, 0xFF, 0x30, 0x0D, 0x80, 0x3C, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x06, 0xFF, 0xF7, 0xFE, 0x00,
+/* 'C' 0x43 */ 0x07, 0xE0, 0x3F, 0xF0, 0xE0, 0x73, 0x80, 0x76, 0x00, 0x6C, 0x00, 0x30, 0x00, 0x60, 0x00, 0xC0, 0x01, 0x80, 0x03, 0x00, 0x06, 0x00, 0x0E, 0x00, 0x6C, 0x00, 0xDC, 0x03, 0x1E, 0x0E, 0x1F, 0xF8, 0x0F, 0xC0,
+/* 'D' 0x44 */ 0xFF, 0xC3, 0xFF, 0x8C, 0x07, 0x30, 0x0E, 0xC0, 0x1B, 0x00, 0x7C, 0x00, 0xF0, 0x03, 0xC0, 0x0F, 0x00, 0x3C, 0x00, 0xF0, 0x03, 0xC0, 0x1F, 0x00, 0x6C, 0x03, 0xB0, 0x1C, 0xFF, 0xE3, 0xFE, 0x00,
+/* 'E' 0x45 */ 0xFF, 0xFF, 0xFF, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00, 0xFF, 0xEF, 0xFE, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00, 0xFF, 0xFF, 0xFF,
+/* 'F' 0x46 */ 0xFF, 0xFF, 0xFF, 0x00, 0x60, 0x0C, 0x01, 0x80, 0x30, 0x06, 0x00, 0xFF, 0xDF, 0xFB, 0x00, 0x60, 0x0C, 0x01, 0x80, 0x30, 0x06, 0x00, 0xC0, 0x18, 0x00,
+/* 'G' 0x47 */ 0x07, 0xF0, 0x1F, 0xFC, 0x3C, 0x1E, 0x70, 0x07, 0x60, 0x03, 0xE0, 0x00, 0xC0, 0x00, 0xC0, 0x00, 0xC0, 0x7F, 0xC0, 0x7F, 0xC0, 0x03, 0xC0, 0x03, 0x60, 0x03, 0x60, 0x07, 0x30, 0x0F, 0x3C, 0x1F, 0x1F, 0xFB, 0x07, 0xE1,
+/* 'H' 0x48 */ 0xC0, 0x1E, 0x00, 0xF0, 0x07, 0x80, 0x3C, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x03, 0xFF, 0xFF, 0xFF, 0xF0, 0x07, 0x80, 0x3C, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x03, 0xC0, 0x1E, 0x00, 0xC0,
+/* 'I' 0x49 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xF0,
+/* 'J' 0x4A */ 0x01, 0x80, 0xC0, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x01, 0x80, 0xC0, 0x60, 0x3C, 0x1E, 0x0F, 0x07, 0xC7, 0x7F, 0x1F, 0x00,
+/* 'K' 0x4B */ 0xC0, 0x3E, 0x03, 0xB0, 0x39, 0x83, 0x8C, 0x38, 0x63, 0x83, 0x38, 0x19, 0xC0, 0xDE, 0x07, 0xB8, 0x38, 0xE1, 0x83, 0x0C, 0x1C, 0x60, 0x73, 0x01, 0x98, 0x0E, 0xC0, 0x3E, 0x00, 0xC0,
+/* 'L' 0x4C */ 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xFF, 0xFF, 0xF0,
+/* 'M' 0x4D */ 0xE0, 0x07, 0xE0, 0x07, 0xF0, 0x0F, 0xF0, 0x0F, 0xD0, 0x0F, 0xD8, 0x1B, 0xD8, 0x1B, 0xD8, 0x1B, 0xCC, 0x33, 0xCC, 0x33, 0xCC, 0x33, 0xC6, 0x63, 0xC6, 0x63, 0xC6, 0x63, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC1, 0x83,
+/* 'N' 0x4E */ 0xE0, 0x1F, 0x00, 0xFC, 0x07, 0xE0, 0x3D, 0x81, 0xEE, 0x0F, 0x30, 0x79, 0xC3, 0xC6, 0x1E, 0x18, 0xF0, 0xE7, 0x83, 0x3C, 0x1D, 0xE0, 0x6F, 0x01, 0xF8, 0x0F, 0xC0, 0x3E, 0x01, 0xC0,
+/* 'O' 0x4F */ 0x07, 0xF0, 0x0F, 0xFE, 0x0F, 0x07, 0x86, 0x00, 0xC6, 0x00, 0x33, 0x00, 0x1B, 0x00, 0x07, 0x80, 0x03, 0xC0, 0x01, 0xE0, 0x00, 0xF0, 0x00, 0x78, 0x00, 0x36, 0x00, 0x33, 0x00, 0x18, 0xC0, 0x18, 0x78, 0x3C, 0x1F, 0xFC, 0x03, 0xF8, 0x00,
+/* 'P' 0x50 */ 0xFF, 0x8F, 0xFE, 0xC0, 0x6C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, 0x3C, 0x06, 0xFF, 0xEF, 0xFC, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00,
+/* 'Q' 0x51 */ 0x07, 0xF0, 0x0F, 0xFE, 0x0F, 0x07, 0x86, 0x00, 0xC6, 0x00, 0x33, 0x00, 0x1B, 0x00, 0x07, 0x80, 0x03, 0xC0, 0x01, 0xE0, 0x00, 0xF0, 0x00, 0x78, 0x00, 0x36, 0x00, 0x33, 0x01, 0x98, 0xC0, 0xFC, 0x78, 0x3C, 0x1F, 0xFF, 0x03, 0xF9, 0x80, 0x00, 0x40,
+/* 'R' 0x52 */ 0xFF, 0xE3, 0xFF, 0xCC, 0x03, 0xB0, 0x06, 0xC0, 0x1B, 0x00, 0x6C, 0x01, 0xB0, 0x0C, 0xFF, 0xE3, 0xFF, 0xCC, 0x03, 0xB0, 0x06, 0xC0, 0x1B, 0x00, 0x6C, 0x01, 0xB0, 0x06, 0xC0, 0x1B, 0x00, 0x70,
+/* 'S' 0x53 */ 0x0F, 0xE0, 0x7F, 0xC3, 0x83, 0x98, 0x07, 0x60, 0x0D, 0x80, 0x07, 0x00, 0x1E, 0x00, 0x3F, 0x80, 0x3F, 0xC0, 0x0F, 0x80, 0x07, 0xC0, 0x0F, 0x00, 0x3E, 0x00, 0xDE, 0x0E, 0x3F, 0xF0, 0x3F, 0x80,
+/* 'T' 0x54 */ 0xFF, 0xFF, 0xFF, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60,
+/* 'U' 0x55 */ 0xC0, 0x1E, 0x00, 0xF0, 0x07, 0x80, 0x3C, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x03, 0xC0, 0x1E, 0x00, 0xF0, 0x07, 0x80, 0x3C, 0x01, 0xE0, 0x0F, 0x80, 0xEE, 0x0E, 0x3F, 0xE0, 0xFC, 0x00,
+/* 'V' 0x56 */ 0xC0, 0x0F, 0x00, 0x7E, 0x01, 0x98, 0x06, 0x60, 0x39, 0xC0, 0xC3, 0x03, 0x0C, 0x1C, 0x38, 0x60, 0x61, 0x81, 0x8E, 0x07, 0x30, 0x0C, 0xC0, 0x37, 0x00, 0xF8, 0x01, 0xE0, 0x07, 0x80, 0x1C, 0x00,
+/* 'W' 0x57 */ 0xE0, 0x30, 0x1D, 0x80, 0xE0, 0x76, 0x07, 0x81, 0xDC, 0x1E, 0x06, 0x70, 0x7C, 0x18, 0xC1, 0xB0, 0xE3, 0x0C, 0xC3, 0x8C, 0x33, 0x0C, 0x38, 0xC6, 0x30, 0x67, 0x18, 0xC1, 0x98, 0x67, 0x06, 0x61, 0xD8, 0x1D, 0x83, 0x60, 0x3C, 0x0D, 0x80, 0xF0, 0x3E, 0x03, 0xC0, 0x70, 0x0F, 0x01, 0xC0, 0x18, 0x07, 0x00,
+/* 'X' 0x58 */ 0xE0, 0x1D, 0x80, 0xE7, 0x03, 0x0E, 0x1C, 0x18, 0x60, 0x73, 0x00, 0xFC, 0x01, 0xE0, 0x07, 0x00, 0x1E, 0x00, 0xF8, 0x03, 0x30, 0x1C, 0xE0, 0xE1, 0x83, 0x07, 0x1C, 0x0E, 0xE0, 0x1B, 0x00, 0x70,
+/* 'Y' 0x59 */ 0xC0, 0x0F, 0x80, 0x76, 0x01, 0x9C, 0x0C, 0x38, 0x70, 0x61, 0x81, 0xCE, 0x03, 0x30, 0x0F, 0x80, 0x1E, 0x00, 0x30, 0x00, 0xC0, 0x03, 0x00, 0x0C, 0x00, 0x30, 0x00, 0xC0, 0x03, 0x00, 0x0C, 0x00,
+/* 'Z' 0x5A */ 0xFF, 0xFF, 0xFF, 0xC0, 0x0E, 0x00, 0xE0, 0x0E, 0x00, 0x60, 0x07, 0x00, 0x70, 0x07, 0x00, 0x30, 0x03, 0x80, 0x38, 0x03, 0x80, 0x18, 0x01, 0xC0, 0x1C, 0x00, 0xFF, 0xFF, 0xFF, 0xC0,
+/* '[' 0x5B */ 0xFF, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCF, 0xF0,
+/* '\' 0x5C */ 0x81, 0x81, 0x02, 0x06, 0x04, 0x08, 0x18, 0x10, 0x20, 0x60, 0x40, 0x81, 0x81, 0x02, 0x06, 0x04,
+/* ']' 0x5D */ 0xFF, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x3F, 0xF0,
+/* '^' 0x5E */ 0x0C, 0x0E, 0x05, 0x86, 0xC3, 0x21, 0x19, 0x8C, 0x83, 0xC1, 0x80,
+/* '_' 0x5F */ 0xFF, 0xFE,
+/* '`' 0x60 */ 0xE3, 0x8C, 0x30,
+/* 'a' 0x61 */ 0x3F, 0x07, 0xF8, 0xE1, 0xCC, 0x0C, 0x00, 0xC0, 0x1C, 0x3F, 0xCF, 0x8C, 0xC0, 0xCC, 0x0C, 0xE3, 0xC7, 0xEF, 0x3C, 0x70,
+/* 'b' 0x62 */ 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0xF8, 0xDF, 0xCF, 0x0E, 0xE0, 0x7C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, 0x3C, 0x03, 0xE0, 0x6F, 0x0E, 0xDF, 0xCC, 0xF8,
+/* 'c' 0x63 */ 0x1F, 0x0F, 0xE6, 0x1F, 0x83, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x38, 0x37, 0x1C, 0xFE, 0x1F, 0x00,
+/* 'd' 0x64 */ 0x00, 0x60, 0x0C, 0x01, 0x80, 0x30, 0x06, 0x3C, 0xCF, 0xFB, 0x8F, 0xE0, 0xF8, 0x0F, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xF8, 0x3B, 0x8F, 0x3F, 0x63, 0xCC,
+/* 'e' 0x65 */ 0x1F, 0x07, 0xF1, 0xC7, 0x70, 0x3C, 0x07, 0xFF, 0xFF, 0xFE, 0x00, 0xC0, 0x1C, 0x0D, 0xC3, 0x1F, 0xC1, 0xF0,
+/* 'f' 0x66 */ 0x3B, 0xD8, 0xC6, 0x7F, 0xEC, 0x63, 0x18, 0xC6, 0x31, 0x8C, 0x63, 0x00,
+/* 'g' 0x67 */ 0x1E, 0x67, 0xFD, 0xC7, 0xF0, 0x7C, 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0x7C, 0x1D, 0xC7, 0x9F, 0xB1, 0xE6, 0x00, 0xC0, 0x3E, 0x0E, 0x7F, 0xC7, 0xE0,
+/* 'h' 0x68 */ 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x33, 0xCD, 0xFB, 0xC7, 0xE0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x30,
+/* 'i' 0x69 */ 0xF0, 0x3F, 0xFF, 0xFF, 0xF0,
+/* 'j' 0x6A */ 0x33, 0x00, 0x03, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x3F, 0xE0,
+/* 'k' 0x6B */ 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x30, 0x6C, 0x33, 0x18, 0xCC, 0x37, 0x0F, 0xC3, 0xB8, 0xC6, 0x31, 0xCC, 0x3B, 0x06, 0xC1, 0xF0, 0x30,
+/* 'l' 0x6C */ 0xFF, 0xFF, 0xFF, 0xFF, 0xF0,
+/* 'm' 0x6D */ 0xCF, 0x1F, 0x6F, 0xDF, 0xFC, 0x78, 0xFC, 0x18, 0x3C, 0x0C, 0x1E, 0x06, 0x0F, 0x03, 0x07, 0x81, 0x83, 0xC0, 0xC1, 0xE0, 0x60, 0xF0, 0x30, 0x78, 0x18, 0x3C, 0x0C, 0x18,
+/* 'n' 0x6E */ 0xCF, 0x37, 0xEF, 0x1F, 0x83, 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xC0,
+/* 'o' 0x6F */ 0x1F, 0x07, 0xF1, 0xC7, 0x70, 0x7C, 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0x7C, 0x1D, 0xC7, 0x1F, 0xC1, 0xF0,
+/* 'p' 0x70 */ 0xCF, 0x8D, 0xFC, 0xF0, 0xEE, 0x06, 0xC0, 0x3C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, 0x3E, 0x06, 0xF0, 0xEF, 0xFC, 0xCF, 0x8C, 0x00, 0xC0, 0x0C, 0x00, 0xC0, 0x00,
+/* 'q' 0x71 */ 0x1E, 0x67, 0xFD, 0xC7, 0xF0, 0x7C, 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0x7C, 0x1D, 0xC7, 0x9F, 0xF1, 0xE6, 0x00, 0xC0, 0x18, 0x03, 0x00, 0x60,
+/* 'r' 0x72 */ 0xCF, 0x7F, 0x38, 0xC3, 0x0C, 0x30, 0xC3, 0x0C, 0x30, 0xC0,
+/* 's' 0x73 */ 0x3E, 0x1F, 0xEE, 0x1B, 0x00, 0xC0, 0x3C, 0x07, 0xF0, 0x3F, 0x01, 0xF0, 0x3E, 0x1D, 0xFE, 0x3F, 0x00,
+/* 't' 0x74 */ 0x63, 0x19, 0xFF, 0xB1, 0x8C, 0x63, 0x18, 0xC6, 0x31, 0xE7,
+/* 'u' 0x75 */ 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x7E, 0x3D, 0xFB, 0x3C, 0xC0,
+/* 'v' 0x76 */ 0xE0, 0x6C, 0x0D, 0x81, 0xB8, 0x63, 0x0C, 0x61, 0x8E, 0x60, 0xCC, 0x19, 0x83, 0xE0, 0x3C, 0x07, 0x00, 0xE0,
+/* 'w' 0x77 */ 0xC1, 0xC1, 0xB0, 0xE1, 0xD8, 0x70, 0xCC, 0x2C, 0x66, 0x36, 0x31, 0x9B, 0x18, 0xCD, 0x98, 0x64, 0x6C, 0x16, 0x36, 0x0F, 0x1A, 0x07, 0x8F, 0x03, 0x83, 0x80, 0xC1, 0xC0,
+/* 'x' 0x78 */ 0xC1, 0xF8, 0x66, 0x30, 0xCC, 0x3E, 0x07, 0x00, 0xC0, 0x78, 0x36, 0x0C, 0xC6, 0x3B, 0x06, 0xC0, 0xC0,
+/* 'y' 0x79 */ 0xE0, 0x6C, 0x0D, 0x83, 0x38, 0x63, 0x0C, 0x63, 0x0C, 0x60, 0xCC, 0x1B, 0x03, 0x60, 0x3C, 0x07, 0x00, 0xE0, 0x18, 0x03, 0x00, 0xE0, 0x78, 0x0E, 0x00,
+/* 'z' 0x7A */ 0xFF, 0xFF, 0xF0, 0x18, 0x0C, 0x07, 0x03, 0x81, 0xC0, 0x60, 0x30, 0x18, 0x0E, 0x03, 0xFF, 0xFF, 0xC0,
+/* '{' 0x7B */ 0x19, 0xCC, 0x63, 0x18, 0xC6, 0x31, 0x99, 0x86, 0x18, 0xC6, 0x31, 0x8C, 0x63, 0x1C, 0x60,
+/* '|' 0x7C */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC,
+/* '}' 0x7D */ 0xC7, 0x18, 0xC6, 0x31, 0x8C, 0x63, 0x0C, 0x33, 0x31, 0x8C, 0x63, 0x18, 0xC6, 0x73, 0x00,
+/* '~' 0x7E */ 0x70, 0x3E, 0x09, 0xE4, 0x1F, 0x03, 0x80,
+/* 0x7F */
+/* 0x80 */ 0x01, 0xF0, 0x1F, 0xF0, 0xE0, 0xC7, 0x00, 0x18, 0x00, 0xC0, 0x07, 0xFF, 0x3F, 0xFC, 0x30, 0x01, 0xFF, 0x8F, 0xFC, 0x0C, 0x00, 0x18, 0x00, 0x70, 0x00, 0xE0, 0x81, 0xFE, 0x03, 0xF0,
+/* 0x81 */
+/* 0x82 */ 0xF5, 0x80,
+/* 0x83 */ 0x1C, 0xF3, 0x0C, 0x31, 0xF7, 0xCC, 0x30, 0xC3, 0x0C, 0x30, 0xC3, 0x0C, 0x30, 0xC3, 0x0C, 0x33, 0xCE, 0x00,
+/* 0x84 */ 0xCF, 0x34, 0x51, 0x88,
+/* 0x85 */ 0xC6, 0x3C, 0x63,
+/* 0x86 */ 0x0C, 0x03, 0x00, 0xC0, 0x30, 0x0C, 0x3F, 0xFF, 0xFC, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x30, 0x0C, 0x00,
+/* 0x87 */ 0x0C, 0x03, 0x00, 0xC0, 0x30, 0x0C, 0x3F, 0xFF, 0xFC, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x30, 0x0C, 0x03, 0x0F, 0xFF, 0xFF, 0x0C, 0x03, 0x00, 0xC0, 0x30,
+/* 0x88 */ 0x38, 0xD9, 0xB6, 0x30,
+/* 0x89 */ 0x38, 0x18, 0x00, 0xF8, 0x30, 0x03, 0x18, 0xC0, 0x04, 0x11, 0x80, 0x0C, 0x66, 0x00, 0x0F, 0x8C, 0x00, 0x0E, 0x30, 0x00, 0x00, 0x40, 0x00, 0x01, 0x80, 0x00, 0x06, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x31, 0xC0, 0xE0, 0x67, 0xC3, 0xC1, 0x98, 0xCC, 0xC3, 0x20, 0x90, 0x8C, 0x63, 0x33, 0x10, 0x7C, 0x3C, 0x60, 0x70, 0x38,
+/* 0x8A */ 0x0C, 0x40, 0x1F, 0x00, 0x38, 0x03, 0xF8, 0x1F, 0xF0, 0xE0, 0xE6, 0x01, 0xD8, 0x03, 0x60, 0x01, 0xC0, 0x07, 0x80, 0x0F, 0xE0, 0x0F, 0xF0, 0x03, 0xE0, 0x01, 0xF0, 0x03, 0xC0, 0x0F, 0x80, 0x37, 0x83, 0x8F, 0xFC, 0x0F, 0xE0,
+/* 0x8B */ 0x2F, 0x49, 0x99,
+/* 0x8C */ 0x07, 0xCF, 0xFC, 0x7F, 0xFF, 0xF3, 0x83, 0xC0, 0x18, 0x07, 0x00, 0x60, 0x0C, 0x03, 0x00, 0x30, 0x0C, 0x00, 0xC0, 0x30, 0x03, 0x00, 0xC0, 0x0F, 0xFF, 0x00, 0x3F, 0xFC, 0x00, 0xC0, 0x30, 0x03, 0x00, 0xC0, 0x0C, 0x01, 0x80, 0x30, 0x07, 0x01, 0xC0, 0x0E, 0x0F, 0x00, 0x1F, 0xEF, 0xFC, 0x1F, 0x3F, 0xF0,
+/* 0x8D */
+/* 0x8E */ 0x0C, 0xC0, 0x3C, 0x00, 0xE1, 0xFF, 0xFF, 0xFF, 0x80, 0x1C, 0x01, 0xC0, 0x1C, 0x00, 0xC0, 0x0E, 0x00, 0xE0, 0x0E, 0x00, 0x60, 0x07, 0x00, 0x70, 0x07, 0x00, 0x30, 0x03, 0x80, 0x38, 0x01, 0xFF, 0xFF, 0xFF, 0x80,
+/* 0x8F */
+/* 0x90 */
+/* 0x91 */ 0x6A, 0xF0,
+/* 0x92 */ 0xF5, 0x60,
+/* 0x93 */ 0x4E, 0x28, 0xA2, 0xCF, 0x30,
+/* 0x94 */ 0xCF, 0x34, 0x51, 0x4E, 0x20,
+/* 0x95 */ 0x7B, 0xFF, 0xFF, 0xFD, 0xE0,
+/* 0x96 */ 0xFF, 0xFF, 0xF0,
+/* 0x97 */ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0,
+/* 0x98 */ 0x63, 0xFE, 0x70,
+/* 0x99 */ 0xFF, 0x70, 0x1F, 0xFD, 0xC0, 0x71, 0x87, 0x83, 0xC6, 0x1E, 0x0F, 0x18, 0x68, 0x3C, 0x61, 0xB1, 0xB1, 0x86, 0xC6, 0xC6, 0x19, 0x1B, 0x18, 0x66, 0xCC, 0x61, 0x9B, 0x31, 0x86, 0x3C, 0xC6, 0x18, 0xE3, 0x18, 0x63, 0x8C,
+/* 0x9A */ 0x63, 0x0D, 0x83, 0x60, 0x70, 0x00, 0x0F, 0x87, 0xFB, 0x86, 0xC0, 0x30, 0x0F, 0x01, 0xFC, 0x0F, 0xC0, 0x7C, 0x0F, 0x87, 0x7F, 0x8F, 0xC0,
+/* 0x9B */ 0x99, 0x92, 0xF4,
+/* 0x9C */ 0x1F, 0x0F, 0x83, 0xF9, 0xFC, 0x71, 0xF8, 0x6E, 0x0F, 0x03, 0xC0, 0x60, 0x3C, 0x07, 0xFF, 0xC0, 0x7F, 0xFC, 0x06, 0x00, 0xC0, 0x60, 0x0E, 0x0F, 0x03, 0x71, 0xF8, 0x63, 0xF9, 0xFC, 0x1F, 0x0F, 0x80,
+/* 0x9D */
+/* 0x9E */ 0x63, 0x0C, 0x83, 0x60, 0x70, 0x00, 0x3F, 0xFF, 0xFC, 0x06, 0x03, 0x01, 0xC0, 0xE0, 0x70, 0x18, 0x0C, 0x06, 0x03, 0x80, 0xFF, 0xFF, 0xF0,
+/* 0x9F */ 0x0C, 0xC0, 0x33, 0x00, 0x00, 0x30, 0x03, 0xE0, 0x1D, 0x80, 0x67, 0x03, 0x0E, 0x1C, 0x18, 0x60, 0x73, 0x80, 0xCC, 0x03, 0xE0, 0x07, 0x80, 0x0C, 0x00, 0x30, 0x00, 0xC0, 0x03, 0x00, 0x0C, 0x00, 0x30, 0x00, 0xC0, 0x03, 0x00,
+/* 0xA0 */
+/* 0xA1 */ 0xF0, 0xBF, 0xFF, 0xFF, 0xF0,
+/* 0xA2 */ 0x04, 0x00, 0x80, 0x7C, 0x1F, 0xE7, 0x4C, 0xC8, 0xF1, 0x1E, 0x20, 0xC4, 0x18, 0x83, 0x10, 0x72, 0x37, 0x4E, 0x7F, 0x87, 0xC0, 0x20, 0x04, 0x00,
+/* 0xA3 */ 0x0F, 0xC1, 0xFE, 0x38, 0x76, 0x03, 0x60, 0x36, 0x00, 0x70, 0x03, 0x80, 0xFF, 0x0F, 0xF0, 0x1C, 0x00, 0xC0, 0x0C, 0x01, 0x80, 0x10, 0x02, 0xF1, 0x7F, 0xF6, 0x1F,
+/* 0xA4 */ 0xDD, 0xFF, 0xD8, 0xD8, 0x3C, 0x1E, 0x0F, 0x8D, 0xFF, 0xDD, 0x80,
+/* 0xA5 */ 0xC0, 0x3E, 0x06, 0x60, 0x63, 0x0C, 0x30, 0xC1, 0x98, 0x19, 0x80, 0xF0, 0x0F, 0x07, 0xFE, 0x06, 0x00, 0x60, 0x7F, 0xE0, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00,
+/* 0xA6 */ 0xFF, 0xFF, 0xF0, 0x3F, 0xFF, 0xFC,
+/* 0xA7 */ 0x0F, 0x03, 0xF0, 0xE7, 0x18, 0x63, 0x0C, 0x70, 0x07, 0x03, 0xF8, 0xC3, 0x98, 0x3B, 0x03, 0xF0, 0x37, 0x06, 0x78, 0xC7, 0xB0, 0x7C, 0x03, 0x80, 0x39, 0x83, 0x30, 0x67, 0x1C, 0x7F, 0x07, 0xC0,
+/* 0xA8 */ 0xCF, 0x30,
+/* 0xA9 */ 0x03, 0xF0, 0x03, 0xFF, 0x01, 0xE0, 0xE0, 0xE3, 0x1C, 0x73, 0xF3, 0x99, 0x86, 0x6C, 0xC1, 0x8F, 0x30, 0x03, 0xCC, 0x00, 0xF3, 0x00, 0x3C, 0xC1, 0x8D, 0x98, 0x66, 0x77, 0xF3, 0x8E, 0x79, 0xC1, 0xC0, 0xE0, 0x3F, 0xF0, 0x03, 0xF0, 0x00,
+/* 0xAA */ 0x79, 0x08, 0x11, 0xEE, 0x50, 0xA3, 0x3B, 0x00, 0x03, 0xF8,
+/* 0xAB */ 0x21, 0x63, 0xE7, 0x84, 0x84, 0xE7, 0x63, 0x21,
+/* 0xAC */ 0xFF, 0xFF, 0xFF, 0x00, 0x30, 0x03, 0x00, 0x30, 0x03,
+/* 0xAD */ 0xFF, 0xF0,
+/* 0xAE */ 0x03, 0xF0, 0x03, 0xFF, 0x01, 0xE0, 0xE0, 0xFF, 0x1C, 0x7F, 0xF3, 0x9B, 0x04, 0x6C, 0xC1, 0x8F, 0x30, 0x43, 0xCF, 0xF0, 0xF3, 0xFC, 0x3C, 0xC1, 0x0D, 0xB0, 0x66, 0x7C, 0x1B, 0x8F, 0x07, 0xC1, 0xC0, 0xE0, 0x3F, 0xF0, 0x03, 0xF0, 0x00,
+/* 0xAF */ 0xFF, 0xF0,
+/* 0xB0 */ 0x38, 0xFB, 0x1C, 0x18, 0x38, 0xDF, 0x1C,
+/* 0xB1 */ 0x06, 0x00, 0x60, 0x06, 0x00, 0x60, 0x7F, 0xE7, 0xFE, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xF0,
+/* 0xB2 */ 0x7D, 0x8F, 0x18, 0x30, 0xC6, 0x18, 0x60, 0xFF, 0xFC,
+/* 0xB3 */ 0x7D, 0x8F, 0x18, 0x31, 0x80, 0xC1, 0xE3, 0xC6, 0xF8,
+/* 0xB4 */ 0x3B, 0x99, 0x80,
+/* 0xB5 */ 0xC0, 0xCC, 0x0C, 0xC0, 0xCC, 0x0C, 0xC0, 0xCC, 0x0C, 0xC0, 0xCC, 0x0C, 0xC0, 0xCC, 0x1C, 0xE3, 0xCF, 0xEF, 0xFC, 0x7C, 0x00, 0xC0, 0x0C, 0x00, 0xC0, 0x00,
+/* 0xB6 */ 0x1F, 0xE7, 0xFD, 0xF3, 0x7E, 0x6F, 0xCD, 0xF9, 0xBF, 0x37, 0xE6, 0x7C, 0xCF, 0x98, 0xF3, 0x06, 0x60, 0xCC, 0x19, 0x83, 0x30, 0x66, 0x0C, 0xC1, 0x98, 0x33, 0x06, 0x60, 0xCC,
+/* 0xB7 */ 0xF0,
+/* 0xB8 */ 0x10, 0xF0, 0xE3, 0x78,
+/* 0xB9 */ 0x2F, 0xB6, 0xDB, 0x6C,
+/* 0xBA */ 0x79, 0x38, 0x61, 0x86, 0x1C, 0xDE, 0x00, 0x0F, 0xC0,
+/* 0xBB */ 0x88, 0xC6, 0xE7, 0x21, 0x21, 0xE7, 0xC6, 0x88,
+/* 0xBC */ 0x20, 0x08, 0x30, 0x0C, 0x38, 0x04, 0x0C, 0x06, 0x06, 0x02, 0x03, 0x02, 0x01, 0x81, 0x00, 0xC1, 0x06, 0x61, 0x87, 0x30, 0x83, 0x80, 0xC2, 0xC0, 0x42, 0x60, 0x43, 0x30, 0x21, 0xFC, 0x20, 0x0C, 0x30, 0x06, 0x10, 0x03, 0x00,
+/* 0xBD */ 0x20, 0x00, 0x08, 0x02, 0x06, 0x01, 0x83, 0x80, 0x40, 0x60, 0x20, 0x18, 0x18, 0x06, 0x04, 0x01, 0x83, 0x00, 0x61, 0x9F, 0x98, 0x4E, 0x76, 0x33, 0x0C, 0x08, 0x03, 0x04, 0x03, 0x83, 0x01, 0x80, 0x81, 0x80, 0x60, 0xC0, 0x30, 0x3F, 0xC8, 0x0F, 0xF0,
+/* 0xBE */ 0x7C, 0x00, 0x18, 0xC0, 0x43, 0x18, 0x18, 0x03, 0x02, 0x00, 0x60, 0xC0, 0x30, 0x10, 0x01, 0x84, 0x00, 0x31, 0x80, 0xC6, 0x20, 0xD8, 0xC8, 0x39, 0xF1, 0x07, 0x00, 0x41, 0x60, 0x18, 0x4C, 0x02, 0x11, 0x80, 0x83, 0xF8, 0x10, 0x06, 0x04, 0x00, 0xC1, 0x00, 0x18,
+/* 0xBF */ 0x0C, 0x06, 0x00, 0x00, 0x00, 0xC0, 0x60, 0x60, 0x30, 0x30, 0x38, 0x38, 0x18, 0x0C, 0x06, 0x0F, 0x07, 0xC7, 0x7F, 0x1F, 0x00,
+/* 0xC0 */ 0x0C, 0x00, 0x18, 0x00, 0x30, 0x00, 0x00, 0x07, 0x80, 0x1E, 0x00, 0x78, 0x03, 0xF0, 0x0C, 0xC0, 0x33, 0x01, 0xCE, 0x06, 0x18, 0x18, 0x60, 0xE1, 0xC3, 0x03, 0x0F, 0xFC, 0x7F, 0xF9, 0x80, 0x66, 0x01, 0xB8, 0x07, 0xC0, 0x0F, 0x00, 0x30,
+/* 0xC1 */ 0x01, 0xC0, 0x0C, 0x00, 0x20, 0x00, 0x00, 0x07, 0x80, 0x1E, 0x00, 0x78, 0x03, 0xF0, 0x0C, 0xC0, 0x33, 0x01, 0xCE, 0x06, 0x18, 0x18, 0x60, 0xE1, 0xC3, 0x03, 0x0F, 0xFC, 0x7F, 0xF9, 0x80, 0x66, 0x01, 0xB8, 0x07, 0xC0, 0x0F, 0x00, 0x30,
+/* 0xC2 */ 0x07, 0x00, 0x3E, 0x01, 0x8C, 0x00, 0x00, 0x07, 0x80, 0x1E, 0x00, 0x78, 0x03, 0xF0, 0x0C, 0xC0, 0x33, 0x01, 0xCE, 0x06, 0x18, 0x18, 0x60, 0xE1, 0xC3, 0x03, 0x0F, 0xFC, 0x7F, 0xF9, 0x80, 0x66, 0x01, 0xB8, 0x07, 0xC0, 0x0F, 0x00, 0x30,
+/* 0xC3 */ 0x0E, 0x40, 0x7F, 0x01, 0x98, 0x00, 0x00, 0x07, 0x80, 0x1E, 0x00, 0x78, 0x03, 0xF0, 0x0C, 0xC0, 0x33, 0x01, 0xCE, 0x06, 0x18, 0x18, 0x60, 0xE1, 0xC3, 0x03, 0x0F, 0xFC, 0x7F, 0xF9, 0x80, 0x66, 0x01, 0xB8, 0x07, 0xC0, 0x0F, 0x00, 0x30,
+/* 0xC4 */ 0x0C, 0xC0, 0x33, 0x00, 0x00, 0x01, 0xE0, 0x07, 0x80, 0x1E, 0x00, 0xFC, 0x03, 0x30, 0x0C, 0xC0, 0x73, 0x81, 0x86, 0x06, 0x18, 0x38, 0x70, 0xC0, 0xC3, 0xFF, 0x1F, 0xFE, 0x60, 0x19, 0x80, 0x6E, 0x01, 0xF0, 0x03, 0xC0, 0x0C,
+/* 0xC5 */ 0x03, 0x00, 0x1E, 0x00, 0xEC, 0x03, 0x30, 0x0F, 0xC0, 0x1E, 0x00, 0x78, 0x01, 0xE0, 0x07, 0x80, 0x3F, 0x00, 0xCC, 0x03, 0x30, 0x1C, 0xE0, 0x61, 0x81, 0x86, 0x0E, 0x1C, 0x30, 0x30, 0xFF, 0xC7, 0xFF, 0x98, 0x06, 0x60, 0x1B, 0x80, 0x7C, 0x00, 0xF0, 0x03,
+/* 0xC6 */ 0x01, 0xFF, 0xFC, 0x07, 0xFF, 0xF0, 0x31, 0x80, 0x00, 0xC6, 0x00, 0x07, 0x18, 0x00, 0x18, 0x60, 0x00, 0x61, 0x80, 0x03, 0x86, 0x00, 0x0C, 0x1F, 0xF8, 0x70, 0x7F, 0xE1, 0x81, 0x80, 0x07, 0xFE, 0x00, 0x3F, 0xF8, 0x00, 0xC0, 0x60, 0x07, 0x01, 0x80, 0x1C, 0x06, 0x00, 0x60, 0x1F, 0xFF, 0x80, 0x7F, 0xF0,
+/* 0xC7 */ 0x07, 0xE0, 0x3F, 0xF0, 0xE0, 0x73, 0x80, 0x66, 0x00, 0x7C, 0x00, 0x30, 0x00, 0x60, 0x00, 0xC0, 0x01, 0x80, 0x03, 0x00, 0x06, 0x00, 0x0E, 0x00, 0x6C, 0x01, 0xDC, 0x03, 0x1C, 0x1E, 0x1F, 0xF8, 0x0F, 0xC0, 0x08, 0x00, 0x1E, 0x00, 0x0C, 0x01, 0x18, 0x01, 0xE0, 0x00,
+/* 0xC8 */ 0x1C, 0x00, 0xC0, 0x02, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00, 0xFF, 0xEF, 0xFE, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00, 0xFF, 0xFF, 0xFF,
+/* 0xC9 */ 0x07, 0x00, 0x60, 0x0C, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00, 0xFF, 0xEF, 0xFE, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00, 0xFF, 0xFF, 0xFF,
+/* 0xCA */ 0x0E, 0x01, 0xF0, 0x31, 0x80, 0x00, 0xFF, 0xFF, 0xFF, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00, 0xFF, 0xEF, 0xFE, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00, 0xFF, 0xFF, 0xFF,
+/* 0xCB */ 0x19, 0x81, 0x98, 0x00, 0x0F, 0xFF, 0xFF, 0xFC, 0x00, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00, 0xC0, 0x0F, 0xFE, 0xFF, 0xEC, 0x00, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00, 0xC0, 0x0F, 0xFF, 0xFF, 0xF0,
+/* 0xCC */ 0xE7, 0x10, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,
+/* 0xCD */ 0x36, 0xC0, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
+/* 0xCE */ 0x39, 0xFC, 0x40, 0x30, 0xC3, 0x0C, 0x30, 0xC3, 0x0C, 0x30, 0xC3, 0x0C, 0x30, 0xC3, 0x0C, 0x30, 0xC0,
+/* 0xCF */ 0xC7, 0x8C, 0x01, 0x83, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC1, 0x83, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC1, 0x83, 0x00,
+/* 0xD0 */ 0x7F, 0xE0, 0xFF, 0xE1, 0x80, 0xE3, 0x00, 0xE6, 0x00, 0xCC, 0x01, 0xD8, 0x01, 0xB0, 0x03, 0xFE, 0x07, 0xFC, 0x0D, 0x80, 0x1B, 0x00, 0x36, 0x00, 0x6C, 0x01, 0x98, 0x07, 0x30, 0x1C, 0x7F, 0xF0, 0xFF, 0xC0,
+/* 0xD1 */ 0x08, 0xC0, 0xFE, 0x05, 0xE0, 0x00, 0x0E, 0x01, 0xF0, 0x0F, 0xC0, 0x7E, 0x03, 0xD8, 0x1E, 0xE0, 0xF3, 0x07, 0x9C, 0x3C, 0x61, 0xE1, 0x8F, 0x0E, 0x78, 0x33, 0xC1, 0xDE, 0x06, 0xF0, 0x1F, 0x80, 0xFC, 0x03, 0xE0, 0x1C,
+/* 0xD2 */ 0x07, 0x00, 0x01, 0x80, 0x00, 0x60, 0x00, 0x00, 0x00, 0x7F, 0x00, 0xFF, 0xE0, 0xF0, 0x78, 0x60, 0x0C, 0x60, 0x03, 0x30, 0x01, 0xB0, 0x00, 0x78, 0x00, 0x3C, 0x00, 0x1E, 0x00, 0x0F, 0x00, 0x07, 0x80, 0x03, 0x60, 0x03, 0x30, 0x01, 0x8C, 0x01, 0x87, 0x83, 0xC1, 0xFF, 0xC0, 0x3F, 0x80,
+/* 0xD3 */ 0x00, 0xE0, 0x00, 0x60, 0x00, 0x40, 0x00, 0x00, 0x00, 0x7F, 0x00, 0xFF, 0xE0, 0xF0, 0x78, 0x60, 0x0C, 0x60, 0x03, 0x30, 0x01, 0xB0, 0x00, 0x78, 0x00, 0x3C, 0x00, 0x1E, 0x00, 0x0F, 0x00, 0x07, 0x80, 0x03, 0x60, 0x03, 0x30, 0x01, 0x8C, 0x01, 0x87, 0x83, 0xC1, 0xFF, 0xC0, 0x3F, 0x80,
+/* 0xD4 */ 0x03, 0xC0, 0x01, 0xE0, 0x01, 0x98, 0x00, 0x00, 0x00, 0x7F, 0x00, 0xFF, 0xE0, 0xF0, 0x78, 0x60, 0x0C, 0x60, 0x03, 0x30, 0x01, 0xB0, 0x00, 0x78, 0x00, 0x3C, 0x00, 0x1E, 0x00, 0x0F, 0x00, 0x07, 0x80, 0x03, 0x60, 0x03, 0x30, 0x01, 0x8C, 0x01, 0x87, 0x83, 0xC1, 0xFF, 0xC0, 0x3F, 0x80,
+/* 0xD5 */ 0x07, 0x20, 0x03, 0xF0, 0x01, 0x38, 0x00, 0x00, 0x00, 0x7F, 0x00, 0xFF, 0xE0, 0xF0, 0x78, 0x60, 0x0C, 0x60, 0x03, 0x30, 0x01, 0xB0, 0x00, 0x78, 0x00, 0x3C, 0x00, 0x1E, 0x00, 0x0F, 0x00, 0x07, 0x80, 0x03, 0x60, 0x03, 0x30, 0x01, 0x8C, 0x01, 0x87, 0x83, 0xC1, 0xFF, 0xC0, 0x3F, 0x80,
+/* 0xD6 */ 0x06, 0x30, 0x03, 0x18, 0x00, 0x00, 0x00, 0xFE, 0x01, 0xFF, 0xC1, 0xE0, 0xF0, 0xC0, 0x18, 0xC0, 0x06, 0x60, 0x03, 0x60, 0x00, 0xF0, 0x00, 0x78, 0x00, 0x3C, 0x00, 0x1E, 0x00, 0x0F, 0x00, 0x06, 0xC0, 0x06, 0x60, 0x03, 0x18, 0x03, 0x0F, 0x07, 0x83, 0xFF, 0x80, 0x7F, 0x00,
+/* 0xD7 */ 0x81, 0xC3, 0x66, 0x3C, 0x18, 0x3C, 0x66, 0xC3, 0x81,
+/* 0xD8 */ 0x07, 0xF0, 0x8F, 0xFE, 0x8F, 0x07, 0xC6, 0x00, 0xE6, 0x00, 0xF3, 0x00, 0xDF, 0x00, 0xC7, 0x80, 0xC3, 0xC0, 0xC1, 0xE0, 0xC0, 0xF0, 0xC0, 0x78, 0xC0, 0x3E, 0xC0, 0x33, 0xC0, 0x19, 0xC0, 0x1C, 0xF8, 0x3C, 0xDF, 0xF8, 0x43, 0xF8, 0x00,
+/* 0xD9 */ 0x0C, 0x00, 0x30, 0x00, 0xC0, 0x00, 0x0C, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x03, 0xC0, 0x1E, 0x00, 0xF0, 0x07, 0x80, 0x3C, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x03, 0xC0, 0x1E, 0x00, 0xF8, 0x0E, 0xE0, 0xE3, 0xFE, 0x0F, 0xC0,
+/* 0xDA */ 0x03, 0x80, 0x18, 0x01, 0x80, 0x00, 0x0C, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x03, 0xC0, 0x1E, 0x00, 0xF0, 0x07, 0x80, 0x3C, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x03, 0xC0, 0x1E, 0x00, 0xF8, 0x0E, 0xE0, 0xE3, 0xFE, 0x0F, 0xC0,
+/* 0xDB */ 0x07, 0x00, 0x7C, 0x06, 0x20, 0x00, 0x0C, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x03, 0xC0, 0x1E, 0x00, 0xF0, 0x07, 0x80, 0x3C, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x03, 0xC0, 0x1E, 0x00, 0xF8, 0x0E, 0xE0, 0xE3, 0xFE, 0x0F, 0xC0,
+/* 0xDC */ 0x0C, 0xC0, 0x66, 0x00, 0x01, 0x80, 0x3C, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x03, 0xC0, 0x1E, 0x00, 0xF0, 0x07, 0x80, 0x3C, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x03, 0xC0, 0x1F, 0x01, 0xDC, 0x1C, 0x7F, 0xC1, 0xF8, 0x00,
+/* 0xDD */ 0x01, 0x80, 0x0C, 0x00, 0x60, 0x00, 0x00, 0xC0, 0x0F, 0x80, 0x76, 0x01, 0x9C, 0x0C, 0x38, 0x70, 0x61, 0x81, 0xCE, 0x03, 0x30, 0x0F, 0x80, 0x1E, 0x00, 0x30, 0x00, 0xC0, 0x03, 0x00, 0x0C, 0x00, 0x30, 0x00, 0xC0, 0x03, 0x00, 0x0C, 0x00,
+/* 0xDE */ 0xC0, 0x0C, 0x00, 0xC0, 0x0F, 0xF8, 0xFF, 0xEC, 0x06, 0xC0, 0x3C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, 0x6F, 0xFE, 0xFF, 0x8C, 0x00, 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00,
+/* 0xDF */ 0x1F, 0x0F, 0xF3, 0x87, 0x60, 0x6C, 0x0D, 0x81, 0xB0, 0x66, 0x38, 0xC7, 0xD8, 0x1B, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xF0, 0x3E, 0x0E, 0xCF, 0x99, 0xE0,
+/* 0xE0 */ 0x1C, 0x00, 0xC0, 0x06, 0x00, 0x20, 0x00, 0x03, 0xF0, 0x7F, 0x8E, 0x1C, 0xC0, 0xC0, 0x0C, 0x01, 0xC3, 0xFC, 0xF8, 0xCC, 0x0C, 0xC0, 0xCE, 0x3C, 0x7E, 0xF3, 0xC7,
+/* 0xE1 */ 0x07, 0x00, 0x60, 0x0C, 0x01, 0x80, 0x00, 0x03, 0xF0, 0x7F, 0x8E, 0x1C, 0xC0, 0xC0, 0x0C, 0x01, 0xC3, 0xFC, 0xF8, 0xCC, 0x0C, 0xC0, 0xCE, 0x3C, 0x7E, 0xF3, 0xC7,
+/* 0xE2 */ 0x0C, 0x01, 0xE0, 0x1B, 0x03, 0x30, 0x00, 0x03, 0xF0, 0x7F, 0x8E, 0x1C, 0xC0, 0xC0, 0x0C, 0x01, 0xC3, 0xFC, 0xF8, 0xCC, 0x0C, 0xC0, 0xCE, 0x3C, 0x7E, 0xF3, 0xC7,
+/* 0xE3 */ 0x19, 0x83, 0xF0, 0x27, 0x00, 0x00, 0x00, 0x03, 0xF0, 0x7F, 0x8E, 0x1C, 0xC0, 0xC0, 0x0C, 0x01, 0xC3, 0xFC, 0xF8, 0xCC, 0x0C, 0xC0, 0xCE, 0x3C, 0x7E, 0xF3, 0xC7,
+/* 0xE4 */ 0x19, 0x81, 0x98, 0x00, 0x00, 0x00, 0x3F, 0x07, 0xF8, 0xE1, 0xCC, 0x0C, 0x00, 0xC0, 0x1C, 0x3F, 0xCF, 0x8C, 0xC0, 0xCC, 0x0C, 0xE3, 0xC7, 0xEF, 0x3C, 0x70,
+/* 0xE5 */ 0x0E, 0x01, 0xF0, 0x1B, 0x81, 0xB8, 0x1F, 0x00, 0xE0, 0x3F, 0x07, 0xF8, 0xE1, 0xCC, 0x0C, 0x00, 0xC0, 0x1C, 0x3F, 0xCF, 0x8C, 0xC0, 0xCC, 0x0C, 0xE3, 0xC7, 0xEF, 0x3C, 0x70,
+/* 0xE6 */ 0x3F, 0x1F, 0x0F, 0xF7, 0xF3, 0x87, 0xC3, 0x60, 0x70, 0x30, 0x0C, 0x06, 0x3F, 0xFF, 0xDF, 0xFF, 0xFF, 0x06, 0x00, 0xC0, 0xC0, 0x18, 0x3C, 0x0F, 0x8F, 0xC7, 0x3F, 0x9F, 0xE3, 0xC1, 0xF0,
+/* 0xE7 */ 0x1F, 0x0F, 0xE7, 0x1D, 0x83, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x38, 0x37, 0x1C, 0xFE, 0x1F, 0x02, 0x00, 0xE0, 0x0C, 0x23, 0x07, 0x80,
+/* 0xE8 */ 0x1C, 0x01, 0x80, 0x18, 0x01, 0x00, 0x00, 0x3E, 0x0F, 0xE3, 0x8E, 0xE0, 0x78, 0x0F, 0xFF, 0xFF, 0xFC, 0x01, 0x80, 0x38, 0x1B, 0x86, 0x3F, 0x83, 0xE0,
+/* 0xE9 */ 0x03, 0x00, 0xC0, 0x30, 0x04, 0x00, 0x00, 0x3E, 0x0F, 0xE3, 0x8E, 0xE0, 0x78, 0x0F, 0xFF, 0xFF, 0xFC, 0x01, 0x80, 0x38, 0x1B, 0x86, 0x3F, 0x83, 0xE0,
+/* 0xEA */ 0x0C, 0x03, 0xC0, 0x6C, 0x18, 0x80, 0x00, 0x3E, 0x0F, 0xE3, 0x8E, 0xE0, 0x78, 0x0F, 0xFF, 0xFF, 0xFC, 0x01, 0x80, 0x38, 0x1B, 0x86, 0x3F, 0x83, 0xE0,
+/* 0xEB */ 0x31, 0x86, 0x30, 0x00, 0x00, 0x01, 0xF0, 0x7F, 0x1C, 0x77, 0x03, 0xC0, 0x7F, 0xFF, 0xFF, 0xE0, 0x0C, 0x01, 0xC0, 0xDC, 0x31, 0xFC, 0x1F, 0x00,
+/* 0xEC */ 0xC6, 0x31, 0x06, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
+/* 0xED */ 0x39, 0x99, 0x80, 0x18, 0xC6, 0x31, 0x8C, 0x63, 0x18, 0xC6, 0x31, 0x80,
+/* 0xEE */ 0x71, 0xED, 0xA3, 0x00, 0xC3, 0x0C, 0x30, 0xC3, 0x0C, 0x30, 0xC3, 0x0C, 0x30, 0xC0,
+/* 0xEF */ 0xCF, 0x30, 0x00, 0x30, 0xC3, 0x0C, 0x30, 0xC3, 0x0C, 0x30, 0xC3, 0x0C, 0x30,
+/* 0xF0 */ 0x20, 0x07, 0xE0, 0x70, 0x3B, 0x00, 0x30, 0x3F, 0x0F, 0xF3, 0x8E, 0xE0, 0xF8, 0x0F, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xF8, 0x3B, 0x8E, 0x3F, 0x83, 0xE0,
+/* 0xF1 */ 0x19, 0x8F, 0xE2, 0x70, 0x00, 0x00, 0x33, 0xCD, 0xFB, 0xC7, 0xE0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x30,
+/* 0xF2 */ 0x1C, 0x01, 0x80, 0x18, 0x01, 0x00, 0x00, 0x3E, 0x0F, 0xE3, 0x8E, 0xE0, 0xF8, 0x0F, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xF8, 0x3B, 0x8E, 0x3F, 0x83, 0xE0,
+/* 0xF3 */ 0x07, 0x00, 0xC0, 0x30, 0x04, 0x00, 0x00, 0x3E, 0x0F, 0xE3, 0x8E, 0xE0, 0xF8, 0x0F, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xF8, 0x3B, 0x8E, 0x3F, 0x83, 0xE0,
+/* 0xF4 */ 0x0C, 0x03, 0xC0, 0xD8, 0x19, 0x80, 0x00, 0x3E, 0x0F, 0xE3, 0x8E, 0xE0, 0xF8, 0x0F, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xF8, 0x3B, 0x8E, 0x3F, 0x83, 0xE0,
+/* 0xF5 */ 0x19, 0x87, 0xE0, 0x9C, 0x00, 0x00, 0x00, 0x3E, 0x0F, 0xE3, 0x8E, 0xE0, 0xF8, 0x0F, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xF8, 0x3B, 0x8E, 0x3F, 0x83, 0xE0,
+/* 0xF6 */ 0x31, 0x86, 0x30, 0x00, 0x00, 0x01, 0xF0, 0x7F, 0x1C, 0x77, 0x07, 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x3C, 0x07, 0xC1, 0xDC, 0x71, 0xFC, 0x1F, 0x00,
+/* 0xF7 */ 0x06, 0x00, 0x60, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x06, 0x00,
+/* 0xF8 */ 0x1F, 0x27, 0xF5, 0xC7, 0x70, 0x7C, 0x17, 0x84, 0xF1, 0x1E, 0x43, 0xD0, 0x7C, 0x1D, 0xC7, 0x3F, 0xC9, 0xF0,
+/* 0xF9 */ 0x18, 0x03, 0x00, 0x60, 0x08, 0x00, 0x30, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x1F, 0x8F, 0x7E, 0xCF, 0x30,
+/* 0xFA */ 0x03, 0x01, 0x80, 0x60, 0x30, 0x00, 0x30, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x1F, 0x8F, 0x7E, 0xCF, 0x30,
+/* 0xFB */ 0x0C, 0x07, 0x81, 0x20, 0xCC, 0x00, 0x30, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x1F, 0x8F, 0x7E, 0xCF, 0x30,
+/* 0xFC */ 0x31, 0x8C, 0x60, 0x00, 0x00, 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x7E, 0x3D, 0xFB, 0x3C, 0xC0,
+/* 0xFD */ 0x03, 0x80, 0x60, 0x18, 0x06, 0x00, 0x01, 0xC0, 0xD8, 0x1B, 0x06, 0x70, 0xC6, 0x18, 0xC6, 0x18, 0xC1, 0x98, 0x36, 0x06, 0xC0, 0x78, 0x0E, 0x01, 0xC0, 0x30, 0x06, 0x01, 0xC0, 0xF0, 0x1C, 0x00,
+/* 0xFE */ 0xC0, 0x0C, 0x00, 0xC0, 0x0C, 0x00, 0xCF, 0x8F, 0xFC, 0xF0, 0xEE, 0x06, 0xC0, 0x3C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, 0x3E, 0x06, 0xF0, 0xEF, 0xFC, 0xCF, 0x8C, 0x00, 0xC0, 0x0C, 0x00, 0xC0, 0x00,
+/* 0xFF */ 0x19, 0x83, 0x30, 0x00, 0x00, 0x0E, 0x06, 0xC0, 0xD8, 0x33, 0x86, 0x30, 0xC6, 0x30, 0xC6, 0x0C, 0xC1, 0xB0, 0x36, 0x03, 0xC0, 0x70, 0x0E, 0x01, 0x80, 0x30, 0x0E, 0x07, 0x80, 0xE0, 0x00,
+};
+
+const GFXglyph FreeSans12pt_Win1252Glyphs[] PROGMEM = {
+/* 0x01 */ { 0, 19, 20, 21, 1, -17 },
+/* 0x02 */ { 48, 19, 20, 21, 1, -17 },
+/* 0x03 */ { 96, 21, 20, 23, 1, -17 },
+/* 0x04 */ { 149, 21, 20, 23, 1, -17 },
+/* 0x05 */ { 202, 20, 20, 22, 1, -17 },
+/* 0x06 */ { 252, 20, 20, 22, 1, -17 },
+/* 0x07 */ { 302, 0, 0, 8, 0, 0 },
+/* 0x08 */ { 302, 23, 20, 25, 1, -17 },
+/* 0x09 */ { 360, 23, 16, 25, 1, -16 },
+/* 0x0A */ { 406, 0, 0, 8, 0, 0 },
+/* 0x0B */ { 406, 21, 20, 23, 1, -17 },
+/* 0x0C */ { 459, 19, 18, 21, 1, -15 },
+/* 0x0D */ { 502, 0, 0, 8, 0, 0 },
+/* 0x0E */ { 502, 20, 20, 22, 1, -17 },
+/* 0x0F */ { 552, 21, 21, 23, 1, -18 },
+/* 0x10 */ { 608, 19, 20, 21, 1, -17 },
+/* 0x11 */ { 656, 21, 20, 23, 1, -17 },
+/* 0x12 */ { 709, 20, 20, 22, 1, -17 },
+/* 0x13 */ { 759, 21, 20, 23, 1, -17 },
+/* 0x14 */ { 812, 21, 20, 23, 1, -17 },
+/* 0x15 */ { 865, 22, 20, 24, 1, -17 },
+/* 0x16 */ { 920, 16, 20, 18, 1, -17 },
+/* 0x17 */ { 960, 21, 20, 23, 1, -17 },
+/* 0x18 */ { 1013, 23, 20, 25, 1, -17 },
+/* 0x19 */ { 1071, 21, 20, 23, 1, -17 },
+/* 0x1A */ { 1124, 15, 19, 17, 1, -16 },
+/* 0x1B */ { 1160, 24, 21, 26, 1, -18 },
+/* 0x1C */ { 1223, 21, 20, 23, 1, -17 },
+/* 0x1D */ { 1276, 21, 21, 23, 1, -18 },
+/* 0x1E */ { 1332, 20, 20, 22, 1, -17 },
+/* 0x1F */ { 1382, 15, 20, 17, 1, -17 },
+/* ' ' 0x20 */ { 1420, 0, 0, 6, 0, 0 },
+/* '!' 0x21 */ { 1420, 2, 18, 8, 3, -16 },
+/* '"' 0x22 */ { 1425, 6, 6, 8, 1, -15 },
+/* '#' 0x23 */ { 1430, 13, 16, 13, 0, -14 },
+/* '$' 0x24 */ { 1456, 11, 20, 13, 1, -16 },
+/* '%' 0x25 */ { 1484, 20, 17, 21, 1, -15 },
+/* '&' 0x26 */ { 1527, 14, 17, 16, 1, -15 },
+/* ''' 0x27 */ { 1557, 2, 6, 5, 1, -15 },
+/* '(' 0x28 */ { 1559, 5, 23, 8, 2, -16 },
+/* ')' 0x29 */ { 1574, 5, 23, 8, 1, -16 },
+/* '*' 0x2A */ { 1589, 7, 7, 9, 1, -16 },
+/* '+' 0x2B */ { 1596, 10, 11, 14, 2, -9 },
+/* ',' 0x2C */ { 1610, 2, 6, 7, 2, 0 },
+/* '-' 0x2D */ { 1612, 6, 2, 8, 1, -6 },
+/* '.' 0x2E */ { 1614, 2, 2, 6, 2, 0 },
+/* '/' 0x2F */ { 1615, 7, 18, 7, 0, -16 },
+/* '0' 0x30 */ { 1631, 11, 17, 13, 1, -15 },
+/* '1' 0x31 */ { 1655, 5, 17, 13, 3, -15 },
+/* '2' 0x32 */ { 1666, 11, 17, 13, 1, -15 },
+/* '3' 0x33 */ { 1690, 11, 17, 13, 1, -15 },
+/* '4' 0x34 */ { 1714, 11, 17, 13, 1, -15 },
+/* '5' 0x35 */ { 1738, 11, 17, 13, 1, -15 },
+/* '6' 0x36 */ { 1762, 11, 17, 13, 1, -15 },
+/* '7' 0x37 */ { 1786, 11, 17, 13, 1, -15 },
+/* '8' 0x38 */ { 1810, 11, 17, 13, 1, -15 },
+/* '9' 0x39 */ { 1834, 11, 17, 13, 1, -15 },
+/* ':' 0x3A */ { 1858, 2, 13, 6, 2, -11 },
+/* ';' 0x3B */ { 1862, 2, 16, 6, 2, -10 },
+/* '<' 0x3C */ { 1866, 12, 11, 14, 1, -9 },
+/* '=' 0x3D */ { 1883, 12, 6, 14, 1, -7 },
+/* '>' 0x3E */ { 1892, 12, 11, 14, 1, -9 },
+/* '?' 0x3F */ { 1909, 10, 18, 13, 2, -16 },
+/* '@' 0x40 */ { 1932, 22, 21, 24, 1, -16 },
+/* 'A' 0x41 */ { 1990, 14, 18, 16, 1, -16 },
+/* 'B' 0x42 */ { 2022, 13, 18, 16, 2, -16 },
+/* 'C' 0x43 */ { 2052, 15, 18, 17, 1, -16 },
+/* 'D' 0x44 */ { 2086, 14, 18, 17, 2, -16 },
+/* 'E' 0x45 */ { 2118, 12, 18, 15, 2, -16 },
+/* 'F' 0x46 */ { 2145, 11, 18, 14, 2, -16 },
+/* 'G' 0x47 */ { 2170, 16, 18, 18, 1, -16 },
+/* 'H' 0x48 */ { 2206, 13, 18, 17, 2, -16 },
+/* 'I' 0x49 */ { 2236, 2, 18, 7, 2, -16 },
+/* 'J' 0x4A */ { 2241, 9, 18, 13, 1, -16 },
+/* 'K' 0x4B */ { 2262, 13, 18, 16, 2, -16 },
+/* 'L' 0x4C */ { 2292, 10, 18, 14, 2, -16 },
+/* 'M' 0x4D */ { 2315, 16, 18, 20, 2, -16 },
+/* 'N' 0x4E */ { 2351, 13, 18, 18, 2, -16 },
+/* 'O' 0x4F */ { 2381, 17, 18, 19, 1, -16 },
+/* 'P' 0x50 */ { 2420, 12, 18, 16, 2, -16 },
+/* 'Q' 0x51 */ { 2447, 17, 19, 19, 1, -16 },
+/* 'R' 0x52 */ { 2488, 14, 18, 17, 2, -16 },
+/* 'S' 0x53 */ { 2520, 14, 18, 16, 1, -16 },
+/* 'T' 0x54 */ { 2552, 12, 18, 15, 1, -16 },
+/* 'U' 0x55 */ { 2579, 13, 18, 17, 2, -16 },
+/* 'V' 0x56 */ { 2609, 14, 18, 15, 1, -16 },
+/* 'W' 0x57 */ { 2641, 22, 18, 22, 0, -16 },
+/* 'X' 0x58 */ { 2691, 14, 18, 16, 1, -16 },
+/* 'Y' 0x59 */ { 2723, 14, 18, 16, 1, -16 },
+/* 'Z' 0x5A */ { 2755, 13, 18, 15, 1, -16 },
+/* '[' 0x5B */ { 2785, 4, 23, 7, 2, -16 },
+/* '\' 0x5C */ { 2797, 7, 18, 7, 0, -16 },
+/* ']' 0x5D */ { 2813, 4, 23, 7, 1, -16 },
+/* '^' 0x5E */ { 2825, 9, 9, 11, 1, -15 },
+/* '_' 0x5F */ { 2836, 15, 1, 13, -1, 5 },
+/* '`' 0x60 */ { 2838, 5, 4, 6, 1, -16 },
+/* 'a' 0x61 */ { 2841, 12, 13, 13, 1, -11 },
+/* 'b' 0x62 */ { 2861, 12, 18, 13, 1, -16 },
+/* 'c' 0x63 */ { 2888, 10, 13, 12, 1, -11 },
+/* 'd' 0x64 */ { 2905, 11, 18, 13, 1, -16 },
+/* 'e' 0x65 */ { 2930, 11, 13, 13, 1, -11 },
+/* 'f' 0x66 */ { 2948, 5, 18, 7, 1, -16 },
+/* 'g' 0x67 */ { 2960, 11, 18, 13, 1, -11 },
+/* 'h' 0x68 */ { 2985, 10, 18, 13, 1, -16 },
+/* 'i' 0x69 */ { 3008, 2, 18, 5, 2, -16 },
+/* 'j' 0x6A */ { 3013, 4, 23, 6, 0, -16 },
+/* 'k' 0x6B */ { 3025, 10, 18, 12, 1, -16 },
+/* 'l' 0x6C */ { 3048, 2, 18, 5, 1, -16 },
+/* 'm' 0x6D */ { 3053, 17, 13, 19, 1, -11 },
+/* 'n' 0x6E */ { 3081, 10, 13, 13, 1, -11 },
+/* 'o' 0x6F */ { 3098, 11, 13, 13, 1, -11 },
+/* 'p' 0x70 */ { 3116, 12, 17, 13, 1, -11 },
+/* 'q' 0x71 */ { 3142, 11, 17, 13, 1, -11 },
+/* 'r' 0x72 */ { 3166, 6, 13, 8, 1, -11 },
+/* 's' 0x73 */ { 3176, 10, 13, 12, 1, -11 },
+/* 't' 0x74 */ { 3193, 5, 16, 7, 1, -14 },
+/* 'u' 0x75 */ { 3203, 10, 13, 13, 1, -11 },
+/* 'v' 0x76 */ { 3220, 11, 13, 12, 0, -11 },
+/* 'w' 0x77 */ { 3238, 17, 13, 17, 0, -11 },
+/* 'x' 0x78 */ { 3266, 10, 13, 11, 1, -11 },
+/* 'y' 0x79 */ { 3283, 11, 18, 11, 0, -11 },
+/* 'z' 0x7A */ { 3308, 10, 13, 12, 1, -11 },
+/* '{' 0x7B */ { 3325, 5, 23, 8, 1, -16 },
+/* '|' 0x7C */ { 3340, 2, 23, 6, 2, -16 },
+/* '}' 0x7D */ { 3346, 5, 23, 8, 2, -16 },
+/* '~' 0x7E */ { 3361, 10, 5, 12, 1, -9 },
+/* 0x7F */ { 3368, 0, 0, 0, 0, 0 },
+/* 0x80 */ { 3368, 14, 17, 16, 1, -15 },
+/* 0x81 */ { 3398, 0, 0, 8, 0, 0 },
+/* 0x82 */ { 3398, 2, 5, 6, 2, 0 },
+/* 0x83 */ { 3400, 6, 23, 7, 0, -16 },
+/* 0x84 */ { 3418, 6, 5, 10, 2, 0 },
+/* 0x85 */ { 3422, 12, 2, 16, 2, 0 },
+/* 0x86 */ { 3425, 10, 21, 13, 2, -15 },
+/* 0x87 */ { 3452, 10, 20, 13, 2, -15 },
+/* 0x88 */ { 3477, 7, 4, 8, 0, -16 },
+/* 0x89 */ { 3481, 23, 18, 24, 0, -16 },
+/* 0x8A */ { 3533, 14, 21, 16, 1, -19 },
+/* 0x8B */ { 3570, 3, 8, 6, 1, -9 },
+/* 0x8C */ { 3573, 22, 18, 24, 1, -16 },
+/* 0x8D */ { 3623, 0, 0, 8, 0, 0 },
+/* 0x8E */ { 3623, 13, 21, 15, 1, -19 },
+/* 0x8F */ { 3658, 0, 0, 8, 0, 0 },
+/* 0x90 */ { 3658, 0, 0, 8, 0, 0 },
+/* 0x91 */ { 3658, 2, 6, 6, 2, -16 },
+/* 0x92 */ { 3660, 2, 6, 6, 2, -16 },
+/* 0x93 */ { 3662, 6, 6, 10, 2, -16 },
+/* 0x94 */ { 3667, 6, 6, 10, 2, -16 },
+/* 0x95 */ { 3672, 6, 6, 10, 2, -9 },
+/* 0x96 */ { 3677, 10, 2, 12, 1, -6 },
+/* 0x97 */ { 3680, 22, 2, 24, 1, -6 },
+/* 0x98 */ { 3686, 7, 3, 8, 0, -16 },
+/* 0x99 */ { 3689, 22, 13, 24, 2, -16 },
+/* 0x9A */ { 3725, 10, 18, 12, 1, -16 },
+/* 0x9B */ { 3748, 3, 8, 6, 2, -8 },
+/* 0x9C */ { 3751, 20, 13, 22, 1, -11 },
+/* 0x9D */ { 3784, 0, 0, 8, 0, 0 },
+/* 0x9E */ { 3784, 10, 18, 12, 1, -16 },
+/* 0x9F */ { 3807, 14, 21, 16, 1, -19 },
+/* 0xA0 */ { 3844, 0, 0, 7, 0, 0 },
+/* 0xA1 */ { 3844, 2, 18, 8, 3, -11 },
+/* 0xA2 */ { 3849, 11, 17, 13, 1, -13 },
+/* 0xA3 */ { 3873, 12, 18, 13, 0, -16 },
+/* 0xA4 */ { 3900, 9, 9, 13, 2, -11 },
+/* 0xA5 */ { 3911, 12, 17, 13, 1, -15 },
+/* 0xA6 */ { 3937, 2, 23, 6, 2, -16 },
+/* 0xA7 */ { 3943, 11, 23, 13, 1, -16 },
+/* 0xA8 */ { 3975, 6, 2, 8, 1, -15 },
+/* 0xA9 */ { 3977, 18, 17, 19, 1, -15 },
+/* 0xAA */ { 4016, 7, 11, 9, 1, -16 },
+/* 0xAB */ { 4026, 8, 8, 12, 2, -9 },
+/* 0xAC */ { 4034, 12, 6, 14, 1, -7 },
+/* 0xAD */ { 4043, 6, 2, 8, 1, -6 },
+/* 0xAE */ { 4045, 18, 17, 19, 1, -15 },
+/* 0xAF */ { 4084, 6, 2, 8, 1, -15 },
+/* 0xB0 */ { 4086, 7, 8, 15, 4, -15 },
+/* 0xB1 */ { 4093, 12, 15, 14, 1, -13 },
+/* 0xB2 */ { 4116, 7, 10, 8, 1, -17 },
+/* 0xB3 */ { 4125, 7, 10, 8, 1, -17 },
+/* 0xB4 */ { 4134, 5, 4, 8, 2, -16 },
+/* 0xB5 */ { 4137, 12, 17, 13, 2, -11 },
+/* 0xB6 */ { 4163, 11, 21, 13, 2, -16 },
+/* 0xB7 */ { 4192, 2, 2, 6, 2, -6 },
+/* 0xB8 */ { 4193, 6, 5, 8, 1, 2 },
+/* 0xB9 */ { 4197, 3, 10, 8, 3, -18 },
+/* 0xBA */ { 4201, 6, 11, 9, 1, -16 },
+/* 0xBB */ { 4210, 8, 8, 12, 2, -8 },
+/* 0xBC */ { 4218, 17, 17, 21, 3, -15 },
+/* 0xBD */ { 4255, 18, 18, 21, 3, -16 },
+/* 0xBE */ { 4296, 19, 18, 21, 1, -16 },
+/* 0xBF */ { 4339, 9, 18, 13, 3, -11 },
+/* 0xC0 */ { 4360, 14, 22, 16, 1, -20 },
+/* 0xC1 */ { 4399, 14, 22, 16, 1, -20 },
+/* 0xC2 */ { 4438, 14, 22, 16, 1, -20 },
+/* 0xC3 */ { 4477, 14, 22, 16, 1, -20 },
+/* 0xC4 */ { 4516, 14, 21, 16, 1, -19 },
+/* 0xC5 */ { 4553, 14, 24, 16, 1, -22 },
+/* 0xC6 */ { 4595, 22, 18, 24, 0, -16 },
+/* 0xC7 */ { 4645, 15, 23, 17, 1, -16 },
+/* 0xC8 */ { 4689, 12, 22, 15, 2, -20 },
+/* 0xC9 */ { 4722, 12, 22, 15, 2, -20 },
+/* 0xCA */ { 4755, 12, 22, 15, 2, -20 },
+/* 0xCB */ { 4788, 12, 21, 15, 2, -19 },
+/* 0xCC */ { 4820, 4, 22, 7, 0, -20 },
+/* 0xCD */ { 4831, 4, 22, 7, 1, -20 },
+/* 0xCE */ { 4842, 6, 22, 7, 0, -20 },
+/* 0xCF */ { 4859, 7, 21, 7, 0, -19 },
+/* 0xD0 */ { 4878, 15, 18, 17, 1, -16 },
+/* 0xD1 */ { 4912, 13, 22, 18, 2, -20 },
+/* 0xD2 */ { 4948, 17, 22, 19, 1, -20 },
+/* 0xD3 */ { 4995, 17, 22, 19, 1, -20 },
+/* 0xD4 */ { 5042, 17, 22, 19, 1, -20 },
+/* 0xD5 */ { 5089, 17, 22, 19, 1, -20 },
+/* 0xD6 */ { 5136, 17, 21, 19, 1, -19 },
+/* 0xD7 */ { 5181, 8, 9, 14, 3, -8 },
+/* 0xD8 */ { 5190, 17, 18, 19, 1, -16 },
+/* 0xD9 */ { 5229, 13, 22, 17, 2, -20 },
+/* 0xDA */ { 5265, 13, 22, 17, 2, -20 },
+/* 0xDB */ { 5301, 13, 22, 17, 2, -20 },
+/* 0xDC */ { 5337, 13, 21, 17, 2, -19 },
+/* 0xDD */ { 5372, 14, 22, 16, 1, -20 },
+/* 0xDE */ { 5411, 12, 18, 15, 2, -16 },
+/* 0xDF */ { 5438, 11, 18, 14, 2, -16 },
+/* 0xE0 */ { 5463, 12, 18, 13, 1, -16 },
+/* 0xE1 */ { 5490, 12, 18, 13, 1, -16 },
+/* 0xE2 */ { 5517, 12, 18, 13, 1, -16 },
+/* 0xE3 */ { 5544, 12, 18, 13, 1, -16 },
+/* 0xE4 */ { 5571, 12, 17, 13, 1, -15 },
+/* 0xE5 */ { 5597, 12, 19, 13, 1, -17 },
+/* 0xE6 */ { 5626, 19, 13, 21, 1, -11 },
+/* 0xE7 */ { 5657, 10, 18, 12, 1, -11 },
+/* 0xE8 */ { 5680, 11, 18, 13, 1, -16 },
+/* 0xE9 */ { 5705, 11, 18, 13, 1, -16 },
+/* 0xEA */ { 5730, 11, 18, 13, 1, -16 },
+/* 0xEB */ { 5755, 11, 17, 13, 1, -15 },
+/* 0xEC */ { 5779, 4, 18, 5, 1, -16 },
+/* 0xED */ { 5788, 5, 18, 5, 0, -16 },
+/* 0xEE */ { 5800, 6, 18, 6, 0, -16 },
+/* 0xEF */ { 5814, 6, 17, 6, 0, -15 },
+/* 0xF0 */ { 5827, 11, 18, 13, 1, -16 },
+/* 0xF1 */ { 5852, 10, 18, 13, 1, -16 },
+/* 0xF2 */ { 5875, 11, 18, 13, 1, -16 },
+/* 0xF3 */ { 5900, 11, 18, 13, 1, -16 },
+/* 0xF4 */ { 5925, 11, 18, 13, 1, -16 },
+/* 0xF5 */ { 5950, 11, 18, 13, 1, -16 },
+/* 0xF6 */ { 5975, 11, 17, 13, 1, -15 },
+/* 0xF7 */ { 5999, 12, 11, 14, 1, -9 },
+/* 0xF8 */ { 6016, 11, 13, 13, 1, -11 },
+/* 0xF9 */ { 6034, 10, 18, 13, 1, -16 },
+/* 0xFA */ { 6057, 10, 18, 13, 1, -16 },
+/* 0xFB */ { 6080, 10, 18, 13, 1, -16 },
+/* 0xFC */ { 6103, 10, 17, 13, 1, -15 },
+/* 0xFD */ { 6125, 11, 23, 11, 0, -16 },
+/* 0xFE */ { 6157, 12, 21, 13, 1, -15 },
+/* 0xFF */ { 6189, 11, 22, 11, 0, -15 },
+};
+
+const GFXfont FreeSans12pt_Win1252 PROGMEM = {
+(uint8_t*)FreeSans12pt_Win1252Bitmaps,
+(GFXglyph*)FreeSans12pt_Win1252Glyphs,
+0x01, 0xFF, 19
+};
diff --git a/src/graphics/niche/Fonts/FreeSans6pt_Win1250.h b/src/graphics/niche/Fonts/FreeSans6pt_Win1250.h
index aee777783..4042bd835 100644
--- a/src/graphics/niche/Fonts/FreeSans6pt_Win1250.h
+++ b/src/graphics/niche/Fonts/FreeSans6pt_Win1250.h
@@ -1,457 +1,527 @@
+// trunk-ignore-all(clang-format)
#pragma once
+/* PROPERTIES
+
+FONT_NAME FreeSans6pt_Win1250
+*/
const uint8_t FreeSans6pt_Win1250Bitmaps[] PROGMEM = {
- /* ' ' 0x20 */
- 0xFC, 0x80, /* '!' 0x21 */
- 0xB6, 0x80, /* '"' 0x22 */
- 0x24, 0x51, 0xF9, 0x42, 0x9F, 0x92, 0x28, /* '#' 0x23 */
- 0x10, 0xE5, 0x55, 0x50, 0xE1, 0x65, 0x55, 0xE1, 0x00, /* '$' 0x24 */
- 0x71, 0x24, 0x89, 0x22, 0x50, 0x74, 0x02, 0x70, 0xA4, 0x49, 0x11, 0xC0, /* '%' 0x25 */
- 0x71, 0x24, 0x9C, 0x62, 0x58, 0xA7, 0xF4, /* '&' 0x26 */
- 0xE0, /* ''' 0x27 */
- 0x5A, 0xAA, 0x94, /* '(' 0x28 */
- 0x89, 0x12, 0x49, 0x29, 0x00, /* ')' 0x29 */
- 0x5E, 0x80, /* '*' 0x2A */
- 0x21, 0x3E, 0x42, 0x00, /* '+' 0x2B */
- 0xE0, /* ',' 0x2C */
- 0xC0, /* '-' 0x2D */
- 0x80, /* '.' 0x2E */
- 0x24, 0xA4, 0xA4, 0x80, /* '/' 0x2F */
- 0x76, 0xE3, 0x18, 0xC6, 0x3B, 0x70, /* '0' 0x30 */
- 0x27, 0x92, 0x49, 0x20, /* '1' 0x31 */
- 0x79, 0x10, 0x41, 0x08, 0xC6, 0x10, 0xFC, /* '2' 0x32 */
- 0x79, 0x30, 0x43, 0x18, 0x10, 0x71, 0x78, /* '3' 0x33 */
- 0x08, 0x61, 0x8A, 0x49, 0x2F, 0xC2, 0x08, /* '4' 0x34 */
- 0xFC, 0x21, 0xE8, 0x84, 0x31, 0xF0, /* '5' 0x35 */
- 0x74, 0x61, 0xE8, 0xC6, 0x31, 0x70, /* '6' 0x36 */
- 0xF8, 0x44, 0x22, 0x11, 0x08, 0x40, /* '7' 0x37 */
- 0x39, 0x34, 0x53, 0x39, 0x1C, 0x51, 0x38, /* '8' 0x38 */
- 0x39, 0x3C, 0x71, 0x4C, 0xF0, 0x53, 0x78, /* '9' 0x39 */
- 0x82, /* ':' 0x3A */
- 0x87, /* ';' 0x3B */
- 0x3E, 0x30, 0x60, 0x80, /* '<' 0x3C */
- 0xF8, 0x3E, /* '=' 0x3D */
- 0xE0, 0xC6, 0xC8, 0x00, /* '>' 0x3E */
- 0x74, 0x42, 0x11, 0x10, 0x80, 0x20, /* '?' 0x3F */
- 0x0F, 0x86, 0x19, 0x9A, 0xA4, 0xD9, 0x13, 0x22, 0x56, 0xDA, 0x6E, 0x60, 0x06, 0x00, 0x3C, 0x00, /* '@' 0x40 */
- 0x18, 0x18, 0x24, 0x24, 0x24, 0x7E, 0x42, 0x42, 0xC3, /* 'A' 0x41 */
- 0xFA, 0x18, 0x61, 0xFA, 0x18, 0x61, 0xFC, /* 'B' 0x42 */
- 0x3E, 0x63, 0x40, 0x40, 0xC0, 0x40, 0x41, 0x63, 0x3E, /* 'C' 0x43 */
- 0xF9, 0x0A, 0x1C, 0x18, 0x30, 0x61, 0xC2, 0xF8, /* 'D' 0x44 */
- 0xFE, 0x08, 0x20, 0xFE, 0x08, 0x20, 0xFC, /* 'E' 0x45 */
- 0xFE, 0x08, 0x20, 0xFA, 0x08, 0x20, 0x80, /* 'F' 0x46 */
- 0x1E, 0x61, 0x40, 0x40, 0xC7, 0x41, 0x41, 0x63, 0x1D, /* 'G' 0x47 */
- 0x83, 0x06, 0x0C, 0x1F, 0xF0, 0x60, 0xC1, 0x82, /* 'H' 0x48 */
- 0xFF, 0x80, /* 'I' 0x49 */
- 0x08, 0x42, 0x10, 0x87, 0x29, 0x70, /* 'J' 0x4A */
- 0x85, 0x12, 0x45, 0x0D, 0x13, 0x22, 0x42, 0x86, /* 'K' 0x4B */
- 0x84, 0x21, 0x08, 0x42, 0x10, 0xF8, /* 'L' 0x4C */
- 0xC3, 0xC3, 0xC3, 0xA5, 0xA5, 0xA5, 0x99, 0x99, 0x99, /* 'M' 0x4D */
- 0x83, 0x86, 0x8D, 0x19, 0x33, 0x62, 0xC3, 0x86, /* 'N' 0x4E */
- 0x1E, 0x31, 0x90, 0x68, 0x1C, 0x0A, 0x05, 0x06, 0xC6, 0x1E, 0x00, /* 'O' 0x4F */
- 0xFA, 0x18, 0x61, 0xFA, 0x08, 0x20, 0x80, /* 'P' 0x50 */
- 0x1E, 0x31, 0x90, 0x68, 0x1C, 0x0A, 0x05, 0x16, 0xC6, 0x1F, 0x00, 0x40, /* 'Q' 0x51 */
- 0xFD, 0x0E, 0x1C, 0x2F, 0x90, 0xA1, 0x42, 0x86, /* 'R' 0x52 */
- 0x7A, 0x18, 0x30, 0x78, 0x38, 0x61, 0x78, /* 'S' 0x53 */
- 0xFE, 0x20, 0x40, 0x81, 0x02, 0x04, 0x08, 0x10, /* 'T' 0x54 */
- 0x83, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xE2, 0x78, /* 'U' 0x55 */
- 0xC2, 0x85, 0x0B, 0x22, 0x44, 0x8E, 0x0C, 0x18, /* 'V' 0x56 */
- 0xC4, 0x28, 0xCD, 0x29, 0x25, 0x24, 0xA4, 0x52, 0x8C, 0x61, 0x8C, 0x31, 0x80, /* 'W' 0x57 */
- 0x87, 0x34, 0x8C, 0x30, 0xC4, 0xA3, 0x84, /* 'X' 0x58 */
- 0xC3, 0x42, 0x24, 0x34, 0x18, 0x08, 0x08, 0x08, 0x08, /* 'Y' 0x59 */
- 0x7E, 0x0C, 0x30, 0x41, 0x06, 0x18, 0x20, 0xFE, /* 'Z' 0x5A */
- 0xEA, 0xAA, 0xAB, /* '[' 0x5B */
- 0x92, 0x24, 0x89, 0x20, /* '\' 0x5C */
- 0xD5, 0x55, 0x57, /* ']' 0x5D */
- 0x46, 0xA9, /* '^' 0x5E */
- 0xFE, /* '_' 0x5F */
- 0x80, /* '`' 0x60 */
- 0x79, 0x20, 0x4F, 0xC6, 0x37, 0x40, /* 'a' 0x61 */
- 0x84, 0x3D, 0x18, 0xC6, 0x31, 0xF0, /* 'b' 0x62 */
- 0x39, 0x3C, 0x20, 0xC1, 0x33, 0x80, /* 'c' 0x63 */
- 0x04, 0x13, 0xD3, 0xC6, 0x1C, 0x53, 0x3C, /* 'd' 0x64 */
- 0x39, 0x38, 0x7F, 0x81, 0x13, 0x80, /* 'e' 0x65 */
- 0x6B, 0xA4, 0x92, 0x40, /* 'f' 0x66 */
- 0x35, 0x3C, 0x61, 0xC5, 0x33, 0x41, 0x4D, 0xE0, /* 'g' 0x67 */
- 0x84, 0x3D, 0x38, 0xC6, 0x31, 0x88, /* 'h' 0x68 */
- 0xBF, 0x80, /* 'i' 0x69 */
- 0x45, 0x55, 0x57, /* 'j' 0x6A */
- 0x84, 0x25, 0x4E, 0x52, 0xD2, 0x88, /* 'k' 0x6B */
- 0xFF, 0x80, /* 'l' 0x6C */
- 0xF7, 0x99, 0x91, 0x91, 0x91, 0x91, 0x91, /* 'm' 0x6D */
- 0xF4, 0x63, 0x18, 0xC6, 0x20, /* 'n' 0x6E */
- 0x39, 0x3C, 0x61, 0xC5, 0x33, 0x80, /* 'o' 0x6F */
- 0xF4, 0x63, 0x18, 0xC7, 0xD0, 0x80, /* 'p' 0x70 */
- 0x3D, 0x3C, 0x61, 0xC5, 0x37, 0x41, 0x04, /* 'q' 0x71 */
- 0xF2, 0x49, 0x20, /* 'r' 0x72 */
- 0x7A, 0x50, 0xE0, 0xE5, 0xE0, /* 's' 0x73 */
- 0x5D, 0x24, 0x93, /* 't' 0x74 */
- 0x8C, 0x63, 0x18, 0xCF, 0xA0, /* 'u' 0x75 */
- 0x85, 0x24, 0x92, 0x30, 0xC3, 0x00, /* 'v' 0x76 */
- 0x89, 0x59, 0x59, 0x55, 0x56, 0x26, 0x26, /* 'w' 0x77 */
- 0x4A, 0x4C, 0x43, 0x27, 0x20, /* 'x' 0x78 */
- 0x8A, 0x52, 0xA5, 0x18, 0x84, 0x22, 0x00, /* 'y' 0x79 */
- 0x78, 0x44, 0x46, 0x23, 0xE0, /* 'z' 0x7A */
- 0x6A, 0xAA, 0xA9, /* '{' 0x7B */
- 0xFF, 0xE0, /* '|' 0x7C */
- 0x95, 0x55, 0x56, /* '}' 0x7D */
- 0x66, 0x60, /* '~' 0x7E */
- 0xFF, 0xC0, 0x67, 0x34, 0x58, 0x4C, 0x46, 0x03, 0x11, 0x80, 0xFF, 0xC0, /* 0x7F */
- 0x1C, 0x45, 0x07, 0xE4, 0x1F, 0x10, 0x10, 0x1E, /* 0x80 */
- /* 0x81 */
- 0xE0, /* 0x82 */
- /* 0x83 */
- 0xB6, 0x80, /* 0x84 */
- 0xA8, /* 0x85 */
- 0x21, 0x09, 0xF2, 0x10, 0x84, 0x21, 0x08, /* 0x86 */
- 0x21, 0x09, 0xF2, 0x10, 0x84, 0xF9, 0x08, /* 0x87 */
- /* 0x88 */
- 0x62, 0x09, 0x40, 0x98, 0x06, 0x80, 0x10, 0x01, 0x66, 0x29, 0x92, 0x99, 0x06, 0x60, /* 0x89 */
- 0x28, 0x47, 0xA1, 0x83, 0x07, 0x83, 0x87, 0x17, 0x80, /* 0x8A */
- 0x64, /* 0x8B */
- 0x10, 0x87, 0xA1, 0x83, 0x07, 0x83, 0x87, 0x17, 0x80, /* 0x8C */
- 0x28, 0x4F, 0xC4, 0x10, 0x41, 0x04, 0x10, 0x40, /* 0x8D */
- 0x14, 0x11, 0xF8, 0x30, 0xC1, 0x04, 0x18, 0x61, 0xFC, /* 0x8E */
- 0x08, 0x21, 0xF8, 0x30, 0xC1, 0x04, 0x18, 0x61, 0xFC, /* 0x8F */
- /* 0x90 */
- 0xE0, /* 0x91 */
- 0xE0, /* 0x92 */
- 0xB6, 0x80, /* 0x93 */
- 0xB6, 0x80, /* 0x94 */
- 0xFF, 0x80, /* 0x95 */
- 0xFC, /* 0x96 */
- 0xFF, 0xF0, /* 0x97 */
- /* 0x98 */
- 0xE6, 0x28, 0xCD, 0x19, 0xA3, 0x34, 0x6A, 0x8B, 0x51, 0x68, /* 0x99 */
- 0x52, 0x69, 0x8E, 0x19, 0x60, /* 0x9A */
- 0x98, /* 0x9B */
- 0x24, 0x06, 0x98, 0xE1, 0x96, /* 0x9C */
- 0x15, 0xE4, 0x44, 0x44, 0x60, /* 0x9D */
- 0x51, 0x00, 0xF0, 0x88, 0x8C, 0x47, 0xC0, /* 0x9E */
- 0x11, 0x00, 0xF0, 0x88, 0x8C, 0x47, 0xC0, /* 0x9F */
- /* 0xA0 */
- 0xA8, /* 0xA1 */
- 0x96, /* 0xA2 */
- 0x41, 0x05, 0x18, 0x43, 0x04, 0x10, 0x7C, /* 0xA3 */
- 0xFC, 0x63, 0xF0, /* 0xA4 */
- 0x30, 0x38, 0x28, 0x48, 0x4C, 0x7C, 0x84, 0x86, 0x82, 0x04, 0x07, /* 0xA5 */
- 0xF9, 0xF0, /* 0xA6 */
- 0x32, 0x91, 0xC9, 0x47, 0x26, 0x14, 0xA4, 0xC0, /* 0xA7 */
- 0xA0, /* 0xA8 */
- 0x3E, 0x3F, 0xB8, 0xF4, 0x1A, 0x0D, 0x17, 0x76, 0xC6, 0x3E, 0x00, /* 0xA9 */
- 0x7A, 0x18, 0x30, 0x78, 0x38, 0x61, 0x78, 0xC1, 0x0C, /* 0xAA */
- 0x5A, 0xA5, /* 0xAB */
- 0xFC, 0x10, 0x40, /* 0xAC */
- /* 0xAD */
- 0x3E, 0x31, 0xB7, 0x72, 0x99, 0xCC, 0xC7, 0x56, 0xC6, 0x3E, 0x00, /* 0xAE */
- 0x18, 0x31, 0xF8, 0x30, 0xC1, 0x04, 0x18, 0x61, 0xFC, /* 0xAF */
- 0x69, 0x96, /* 0xB0 */
- 0x21, 0x3E, 0x42, 0x03, 0xE0, /* 0xB1 */
- 0x9C, /* 0xB2 */
- 0x49, 0x35, 0x92, 0x40, /* 0xB3 */
- 0x80, /* 0xB4 */
- 0x8A, 0x28, 0xA2, 0x8A, 0x6E, 0xE0, 0x80, /* 0xB5 */
- 0x7F, 0xAE, 0xBA, 0x68, 0xA2, 0x8A, 0x28, 0xA0, /* 0xB6 */
- 0x80, /* 0xB7 */
- 0x67, 0x80, /* 0xB8 */
- 0x78, 0x84, 0x04, 0x3C, 0xC4, 0x8C, 0x76, 0x04, 0x07, /* 0xB9 */
- 0x69, 0x8E, 0x19, 0x66, 0x26, /* 0xBA */
- 0xA5, 0x5A, /* 0xBB */
- 0xA5, 0x21, 0x08, 0x42, 0x10, 0xF8, /* 0xBC */
- 0xA0, /* 0xBD */
- 0xBA, 0x49, 0x24, 0x90, /* 0xBE */
- 0x31, 0x9E, 0x11, 0x11, 0x88, 0xF8, /* 0xBF */
- 0x10, 0x43, 0xE4, 0x28, 0x50, 0xBE, 0x42, 0x85, 0x0C, /* 0xC0 */
- 0x08, 0x10, 0x00, 0x18, 0x3C, 0x24, 0x24, 0x7E, 0x42, 0xC3, /* 0xC1 */
- 0x18, 0x24, 0x00, 0x18, 0x3C, 0x24, 0x24, 0x7E, 0x42, 0xC3, /* 0xC2 */
- 0x24, 0x18, 0x00, 0x18, 0x3C, 0x24, 0x24, 0x7E, 0x42, 0xC3, /* 0xC3 */
- 0x24, 0x00, 0x18, 0x3C, 0x24, 0x24, 0x7E, 0x42, 0x42, 0xC3, /* 0xC4 */
- 0x11, 0x21, 0x08, 0x42, 0x10, 0x87, 0xC0, /* 0xC5 */
- 0x08, 0x20, 0x01, 0xE4, 0x30, 0x20, 0x40, 0x82, 0x8C, 0xF0, /* 0xC6 */
- 0x3E, 0x61, 0xC0, 0x80, 0x80, 0x80, 0xC1, 0x63, 0x3E, 0x0C, 0x04, 0x1C, /* 0xC7 */
- 0x28, 0x20, 0x01, 0xE4, 0x30, 0x20, 0x40, 0x82, 0x8C, 0xF0, /* 0xC8 */
- 0x08, 0x40, 0x3F, 0x82, 0x0F, 0xA0, 0x83, 0xF0, /* 0xC9 */
- 0xFD, 0x02, 0x04, 0x0F, 0xD0, 0x20, 0x40, 0xFC, 0x10, 0x38, /* 0xCA */
- 0x28, 0x0F, 0xE0, 0x83, 0xE8, 0x20, 0x83, 0xF0, /* 0xCB */
- 0x28, 0x40, 0x3F, 0x82, 0x0F, 0xA0, 0x82, 0x0F, 0xC0, /* 0xCC */
- 0x62, 0xAA, 0xA0, /* 0xCD */
- 0x54, 0x24, 0x92, 0x48, /* 0xCE */
- 0x50, 0x43, 0xE4, 0x28, 0x30, 0x60, 0xC1, 0x85, 0xF0, /* 0xCF */
- 0x7C, 0x42, 0x41, 0x41, 0xF1, 0x41, 0x41, 0x42, 0x7C, /* 0xD0 */
- 0x08, 0x23, 0x0F, 0x1B, 0x32, 0x66, 0xC7, 0x87, 0x04, /* 0xD1 */
- 0x28, 0x23, 0x0F, 0x1B, 0x32, 0x66, 0xC7, 0x87, 0x04, /* 0xD2 */
- 0x04, 0x04, 0x0F, 0x8C, 0x6C, 0x1C, 0x06, 0x03, 0x83, 0x63, 0x1F, 0x00, /* 0xD3 */
- 0x08, 0x0A, 0x00, 0x07, 0xC6, 0x36, 0x0E, 0x03, 0x01, 0xC1, 0xB1, 0x8F, 0x80, /* 0xD4 */
- 0x0A, 0x0A, 0x00, 0x07, 0xC6, 0x36, 0x0E, 0x03, 0x01, 0xC1, 0xB1, 0x8F, 0x80, /* 0xD5 */
- 0x14, 0x00, 0x00, 0x07, 0xC6, 0x36, 0x0E, 0x03, 0x01, 0xC1, 0xB1, 0x8F, 0x80, /* 0xD6 */
- 0x8A, 0x88, 0xA8, 0x80, /* 0xD7 */
- 0x50, 0x43, 0xE4, 0x28, 0x50, 0xBE, 0x42, 0x85, 0x0C, /* 0xD8 */
- 0x10, 0x52, 0x4C, 0x18, 0x30, 0x60, 0xC1, 0xC6, 0xF8, /* 0xD9 */
- 0x08, 0x22, 0x0C, 0x18, 0x30, 0x60, 0xC1, 0xC6, 0xF8, /* 0xDA */
- 0x14, 0x52, 0x0C, 0x18, 0x30, 0x60, 0xC1, 0xC6, 0xF8, /* 0xDB */
- 0x29, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC1, 0xC6, 0xF8, /* 0xDC */
- 0x09, 0x25, 0x12, 0x22, 0x87, 0x04, 0x08, 0x10, 0x20, /* 0xDD */
- 0xFC, 0x41, 0x04, 0x10, 0x41, 0x04, 0x10, 0x60, 0x8E, /* 0xDE */
- 0x7A, 0x18, 0x61, 0x8A, 0x18, 0x61, 0xB8, /* 0xDF */
- 0x42, 0xE9, 0x24, 0x80, /* 0xE0 */
- 0x10, 0x40, 0x03, 0xC8, 0x40, 0x8F, 0x62, 0x8C, 0xEC, /* 0xE1 */
- 0x10, 0x50, 0x03, 0xC8, 0x40, 0x8F, 0x62, 0x8C, 0xEC, /* 0xE2 */
- 0x48, 0x60, 0x03, 0xC8, 0x40, 0x8F, 0x62, 0x8C, 0xEC, /* 0xE3 */
- 0x28, 0x01, 0xE4, 0x20, 0x47, 0xB1, 0x46, 0x76, /* 0xE4 */
- 0x62, 0xAA, 0xA0, /* 0xE5 */
- 0x10, 0x80, 0x1E, 0xC6, 0x08, 0x20, 0xC5, 0xE0, /* 0xE6 */
- 0x7B, 0x18, 0x20, 0x83, 0x17, 0x8C, 0x11, 0xC0, /* 0xE7 */
- 0x28, 0x40, 0x1E, 0xC6, 0x08, 0x20, 0xC5, 0xE0, /* 0xE8 */
- 0x10, 0x80, 0x1E, 0xCE, 0x1F, 0xE0, 0xC5, 0xE0, /* 0xE9 */
- 0x7B, 0x38, 0x7F, 0x83, 0x37, 0x84, 0x1C, /* 0xEA */
- 0x28, 0x07, 0xB3, 0x87, 0xF8, 0x31, 0x78, /* 0xEB */
- 0x28, 0x40, 0x1E, 0xCE, 0x1F, 0xE0, 0xC5, 0xE0, /* 0xEC */
- 0x62, 0xAA, 0xA0, /* 0xED */
- 0x54, 0x24, 0x92, 0x48, /* 0xEE */
- 0x02, 0x0C, 0x13, 0xEC, 0xD0, 0xA1, 0x42, 0xCC, 0xE8, /* 0xEF */
- 0x04, 0x1D, 0xD6, 0x68, 0x50, 0xA1, 0x66, 0x74, /* 0xF0 */
- 0x11, 0x01, 0x6C, 0xC6, 0x31, 0x8C, 0x40, /* 0xF1 */
- 0x20, 0x81, 0x6C, 0xC6, 0x31, 0x8C, 0x40, /* 0xF2 */
- 0x10, 0x80, 0x1E, 0xCE, 0x18, 0x61, 0xCD, 0xE0, /* 0xF3 */
- 0x10, 0xA0, 0x1E, 0xCE, 0x18, 0x61, 0xCD, 0xE0, /* 0xF4 */
- 0x29, 0x40, 0x1E, 0xCE, 0x18, 0x61, 0xCD, 0xE0, /* 0xF5 */
- 0x28, 0x07, 0xB3, 0x86, 0x18, 0x73, 0x78, /* 0xF6 */
- 0x20, 0x3E, 0x02, 0x00, /* 0xF7 */
- 0xA8, 0x5D, 0x24, 0x90, /* 0xF8 */
- 0x22, 0x89, 0x18, 0xC6, 0x31, 0x9B, 0x40, /* 0xF9 */
- 0x11, 0x23, 0x18, 0xC6, 0x33, 0x68, /* 0xFA */
- 0x2A, 0x81, 0x18, 0xC6, 0x31, 0x9B, 0x40, /* 0xFB */
- 0x50, 0x23, 0x18, 0xC6, 0x33, 0x68, /* 0xFC */
- 0x10, 0x88, 0x52, 0x49, 0x23, 0x0C, 0x30, 0x82, 0x18, /* 0xFD */
- 0x4E, 0x44, 0x44, 0x46, 0x31, 0x70, /* 0xFE */
- 0x80, /* 0xFF */
+/* 0x01 */ 0x1C, 0x0A, 0x05, 0x04, 0xFE, 0x08, 0x1C, 0x02, 0x07, 0xE0, 0x9F, 0xC0,
+/* 0x02 */ 0x3F, 0xF0, 0x40, 0xE0, 0x10, 0x3F, 0x04, 0x9E, 0x28, 0x14, 0x0E, 0x00,
+/* 0x03 */ 0x3F, 0x10, 0x28, 0x06, 0x49, 0x80, 0x60, 0x19, 0x26, 0x31, 0x40, 0x8F, 0xC0,
+/* 0x04 */ 0x3F, 0x10, 0x2A, 0x16, 0x49, 0xA1, 0x60, 0x19, 0xE6, 0x31, 0x40, 0x8F, 0xC0,
+/* 0x05 */ 0x28, 0x15, 0x2A, 0xB5, 0x55, 0xA8, 0x54, 0x12, 0x04, 0x41, 0x08, 0x81, 0xC0,
+/* 0x06 */ 0x04, 0x08, 0x88, 0x82, 0x07, 0x01, 0x11, 0xA2, 0xC4, 0x40, 0x70, 0x20, 0x88, 0x88, 0x10, 0x00,
+/* 0x07 */
+/* 0x08 */ 0x03, 0x83, 0x44, 0x48, 0x28, 0x01, 0x80, 0x17, 0xFE, 0x08, 0x45, 0x28, 0x84, 0x00,
+/* 0x09 */ 0x01, 0xC0, 0x68, 0x82, 0x41, 0x10, 0x02, 0x80, 0x06, 0x00, 0x14, 0x00, 0x8F, 0xFC,
+/* 0x0A */
+/* 0x0B */ 0x22, 0x2A, 0xA2, 0x30, 0x18, 0x0A, 0x09, 0x04, 0x44, 0x14, 0x04, 0x00,
+/* 0x0C */ 0x46, 0x00, 0x19, 0x03, 0x21, 0x20, 0x93, 0x04, 0x20, 0x11, 0x80, 0x50, 0x02, 0x7F, 0xE0,
+/* 0x0D */
+/* 0x0E */ 0x08, 0x0E, 0x08, 0x88, 0x24, 0x12, 0x09, 0x05, 0x01, 0xFF, 0x8A, 0x02, 0x00,
+/* 0x0F */ 0x3F, 0x14, 0xAA, 0x16, 0x01, 0x92, 0x60, 0x18, 0xC6, 0x49, 0x40, 0x8F, 0xC0,
+/* 0x10 */ 0x1B, 0x02, 0xA0, 0x54, 0x12, 0x42, 0x48, 0x49, 0x31, 0x1E, 0x23, 0xEA, 0xFE, 0x3C,
+/* 0x11 */ 0x3F, 0x02, 0x00, 0x20, 0x6D, 0x27, 0xF8, 0x3F, 0xC1, 0xFE, 0x37, 0xD0, 0xBE, 0x40, 0xE1, 0xE2, 0x00,
+/* 0x12 */ 0x12, 0x42, 0x20, 0x24, 0xC0, 0x29, 0x99, 0x05, 0x23, 0x30, 0xB0, 0x30, 0x00,
+/* 0x13 */ 0x3F, 0x88, 0x0A, 0x44, 0xD5, 0x58, 0x03, 0x00, 0x67, 0xCC, 0x71, 0x40, 0x47, 0xF0,
+/* 0x14 */ 0x3F, 0x18, 0x69, 0x26, 0x85, 0xA1, 0x6C, 0xD8, 0x06, 0x31, 0x40, 0x8F, 0xC0,
+/* 0x15 */ 0x3F, 0x11, 0x00, 0xE8, 0x03, 0xA0, 0x1F, 0xB3, 0x7E, 0x00, 0xE9, 0xE0, 0x23, 0x00, 0x40, 0x40, 0xFE, 0x00,
+/* 0x16 */ 0x30, 0x38, 0x3A, 0x3E, 0x6E, 0xEB, 0xC3, 0xC3, 0x66, 0x3C,
+/* 0x17 */ 0x3F, 0x04, 0x00, 0x82, 0x88, 0x5C, 0xA4, 0x49, 0x22, 0x81, 0x98, 0xC4, 0x40, 0xA3, 0xF0,
+/* 0x18 */ 0x07, 0x80, 0x42, 0x04, 0x08, 0x21, 0x41, 0x42, 0x60, 0x0E, 0x8C, 0xB2, 0x89, 0x50, 0x52, 0x82, 0x80,
+/* 0x19 */ 0x3F, 0xC4, 0x02, 0x80, 0x18, 0x01, 0xB3, 0x1B, 0xB9, 0x80, 0x19, 0xE1, 0x40, 0x23, 0xFC,
+/* 0x1A */ 0xFF, 0xC0, 0x67, 0x34, 0x58, 0x4C, 0x46, 0x03, 0x11, 0x80, 0xFF, 0xC0,
+/* 0x1B */ 0x0F, 0xC0, 0x40, 0x82, 0x49, 0x08, 0x04, 0x00, 0x00, 0x12, 0x02, 0x31, 0x34, 0x0B, 0x88, 0x45, 0x00, 0x20,
+/* 0x1C */ 0x3F, 0x88, 0x0A, 0x44, 0xC9, 0x19, 0x3B, 0x00, 0x60, 0x4C, 0x71, 0x40, 0x47, 0xF0,
+/* 0x1D */ 0x3F, 0x8B, 0x0A, 0x00, 0xC8, 0x18, 0x13, 0x00, 0x48, 0xCA, 0xC1, 0x44, 0x53, 0x30,
+/* 0x1E */ 0x19, 0xC2, 0x02, 0x50, 0x1E, 0x49, 0x80, 0x12, 0x01, 0x27, 0x92, 0x01, 0x10, 0x20, 0xFC,
+/* 0x1F */ 0x30, 0x1C, 0x0C, 0x3E, 0x7E, 0xCF, 0x07, 0xC7, 0x7F, 0x3F,
+/* ' ' 0x20 */
+/* '!' 0x21 */ 0xFC, 0x80,
+/* '"' 0x22 */ 0xB6, 0x80,
+/* '#' 0x23 */ 0x24, 0x51, 0xF9, 0x42, 0x9F, 0x92, 0x28,
+/* '$' 0x24 */ 0x10, 0xE5, 0x55, 0x50, 0xE1, 0x65, 0x55, 0xE1, 0x00,
+/* '%' 0x25 */ 0x71, 0x24, 0x89, 0x22, 0x50, 0x74, 0x02, 0x70, 0xA4, 0x49, 0x11, 0xC0,
+/* '&' 0x26 */ 0x71, 0x24, 0x9C, 0x62, 0x58, 0xA7, 0xF4,
+/* ''' 0x27 */ 0xE0,
+/* '(' 0x28 */ 0x5A, 0xAA, 0x94,
+/* ')' 0x29 */ 0x89, 0x12, 0x49, 0x29, 0x00,
+/* '*' 0x2A */ 0x5E, 0x80,
+/* '+' 0x2B */ 0x21, 0x3E, 0x42, 0x00,
+/* ',' 0x2C */ 0xE0,
+/* '-' 0x2D */ 0xC0,
+/* '.' 0x2E */ 0x80,
+/* '/' 0x2F */ 0x24, 0xA4, 0xA4, 0x80,
+/* '0' 0x30 */ 0x76, 0xE3, 0x18, 0xC6, 0x3B, 0x70,
+/* '1' 0x31 */ 0x27, 0x92, 0x49, 0x20,
+/* '2' 0x32 */ 0x79, 0x10, 0x41, 0x08, 0xC6, 0x10, 0xFC,
+/* '3' 0x33 */ 0x79, 0x30, 0x43, 0x18, 0x10, 0x71, 0x78,
+/* '4' 0x34 */ 0x08, 0x61, 0x8A, 0x49, 0x2F, 0xC2, 0x08,
+/* '5' 0x35 */ 0xFC, 0x21, 0xE8, 0x84, 0x31, 0xF0,
+/* '6' 0x36 */ 0x74, 0x61, 0xE8, 0xC6, 0x31, 0x70,
+/* '7' 0x37 */ 0xF8, 0x44, 0x22, 0x11, 0x08, 0x40,
+/* '8' 0x38 */ 0x39, 0x34, 0x53, 0x39, 0x1C, 0x51, 0x38,
+/* '9' 0x39 */ 0x39, 0x3C, 0x71, 0x4C, 0xF0, 0x53, 0x78,
+/* ':' 0x3A */ 0x82,
+/* ';' 0x3B */ 0x87,
+/* '<' 0x3C */ 0x3E, 0x30, 0x60, 0x80,
+/* '=' 0x3D */ 0xF8, 0x3E,
+/* '>' 0x3E */ 0xE0, 0xC6, 0xC8, 0x00,
+/* '?' 0x3F */ 0x74, 0x42, 0x11, 0x10, 0x80, 0x20,
+/* '@' 0x40 */ 0x0F, 0x86, 0x19, 0x9A, 0xA4, 0xD9, 0x13, 0x22, 0x56, 0xDA, 0x6E, 0x60, 0x06, 0x00, 0x3C, 0x00,
+/* 'A' 0x41 */ 0x18, 0x18, 0x24, 0x24, 0x24, 0x7E, 0x42, 0x42, 0xC3,
+/* 'B' 0x42 */ 0xFA, 0x18, 0x61, 0xFA, 0x18, 0x61, 0xFC,
+/* 'C' 0x43 */ 0x3E, 0x63, 0x40, 0x40, 0xC0, 0x40, 0x41, 0x63, 0x3E,
+/* 'D' 0x44 */ 0xF9, 0x0A, 0x1C, 0x18, 0x30, 0x61, 0xC2, 0xF8,
+/* 'E' 0x45 */ 0xFE, 0x08, 0x20, 0xFE, 0x08, 0x20, 0xFC,
+/* 'F' 0x46 */ 0xFE, 0x08, 0x20, 0xFA, 0x08, 0x20, 0x80,
+/* 'G' 0x47 */ 0x1E, 0x61, 0x40, 0x40, 0xC7, 0x41, 0x41, 0x63, 0x1D,
+/* 'H' 0x48 */ 0x83, 0x06, 0x0C, 0x1F, 0xF0, 0x60, 0xC1, 0x82,
+/* 'I' 0x49 */ 0xFF, 0x80,
+/* 'J' 0x4A */ 0x08, 0x42, 0x10, 0x87, 0x29, 0x70,
+/* 'K' 0x4B */ 0x85, 0x12, 0x45, 0x0D, 0x13, 0x22, 0x42, 0x86,
+/* 'L' 0x4C */ 0x84, 0x21, 0x08, 0x42, 0x10, 0xF8,
+/* 'M' 0x4D */ 0xC3, 0xC3, 0xC3, 0xA5, 0xA5, 0xA5, 0x99, 0x99, 0x99,
+/* 'N' 0x4E */ 0x83, 0x86, 0x8D, 0x19, 0x33, 0x62, 0xC3, 0x86,
+/* 'O' 0x4F */ 0x1E, 0x31, 0x90, 0x68, 0x1C, 0x0A, 0x05, 0x06, 0xC6, 0x1E, 0x00,
+/* 'P' 0x50 */ 0xFA, 0x18, 0x61, 0xFA, 0x08, 0x20, 0x80,
+/* 'Q' 0x51 */ 0x1E, 0x31, 0x90, 0x68, 0x1C, 0x0A, 0x05, 0x16, 0xC6, 0x1F, 0x00, 0x40,
+/* 'R' 0x52 */ 0xFD, 0x0E, 0x1C, 0x2F, 0x90, 0xA1, 0x42, 0x86,
+/* 'S' 0x53 */ 0x7A, 0x18, 0x30, 0x78, 0x38, 0x61, 0x78,
+/* 'T' 0x54 */ 0xFE, 0x20, 0x40, 0x81, 0x02, 0x04, 0x08, 0x10,
+/* 'U' 0x55 */ 0x83, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xE2, 0x78,
+/* 'V' 0x56 */ 0xC2, 0x85, 0x0B, 0x22, 0x44, 0x8E, 0x0C, 0x18,
+/* 'W' 0x57 */ 0xC4, 0x28, 0xCD, 0x29, 0x25, 0x24, 0xA4, 0x52, 0x8C, 0x61, 0x8C, 0x31, 0x80,
+/* 'X' 0x58 */ 0x87, 0x34, 0x8C, 0x30, 0xC4, 0xA3, 0x84,
+/* 'Y' 0x59 */ 0xC3, 0x42, 0x24, 0x34, 0x18, 0x08, 0x08, 0x08, 0x08,
+/* 'Z' 0x5A */ 0x7E, 0x0C, 0x30, 0x41, 0x06, 0x18, 0x20, 0xFE,
+/* '[' 0x5B */ 0xEA, 0xAA, 0xAB,
+/* '\' 0x5C */ 0x92, 0x24, 0x89, 0x20,
+/* ']' 0x5D */ 0xD5, 0x55, 0x57,
+/* '^' 0x5E */ 0x46, 0xA9,
+/* '_' 0x5F */ 0xFE,
+/* '`' 0x60 */ 0x80,
+/* 'a' 0x61 */ 0x79, 0x20, 0x4F, 0xC6, 0x37, 0x40,
+/* 'b' 0x62 */ 0x84, 0x3D, 0x18, 0xC6, 0x31, 0xF0,
+/* 'c' 0x63 */ 0x39, 0x3C, 0x20, 0xC1, 0x33, 0x80,
+/* 'd' 0x64 */ 0x04, 0x13, 0xD3, 0xC6, 0x1C, 0x53, 0x3C,
+/* 'e' 0x65 */ 0x39, 0x38, 0x7F, 0x81, 0x13, 0x80,
+/* 'f' 0x66 */ 0x6B, 0xA4, 0x92, 0x40,
+/* 'g' 0x67 */ 0x35, 0x3C, 0x61, 0xC5, 0x33, 0x41, 0x4D, 0xE0,
+/* 'h' 0x68 */ 0x84, 0x3D, 0x38, 0xC6, 0x31, 0x88,
+/* 'i' 0x69 */ 0xBF, 0x80,
+/* 'j' 0x6A */ 0x45, 0x55, 0x57,
+/* 'k' 0x6B */ 0x84, 0x25, 0x4E, 0x52, 0xD2, 0x88,
+/* 'l' 0x6C */ 0xFF, 0x80,
+/* 'm' 0x6D */ 0xF7, 0x99, 0x91, 0x91, 0x91, 0x91, 0x91,
+/* 'n' 0x6E */ 0xF4, 0x63, 0x18, 0xC6, 0x20,
+/* 'o' 0x6F */ 0x39, 0x3C, 0x61, 0xC5, 0x33, 0x80,
+/* 'p' 0x70 */ 0xF4, 0x63, 0x18, 0xC7, 0xD0, 0x80,
+/* 'q' 0x71 */ 0x3D, 0x3C, 0x61, 0xC5, 0x37, 0x41, 0x04,
+/* 'r' 0x72 */ 0xF2, 0x49, 0x20,
+/* 's' 0x73 */ 0x7A, 0x50, 0xE0, 0xE5, 0xE0,
+/* 't' 0x74 */ 0x5D, 0x24, 0x93,
+/* 'u' 0x75 */ 0x8C, 0x63, 0x18, 0xCF, 0xA0,
+/* 'v' 0x76 */ 0x85, 0x24, 0x92, 0x30, 0xC3, 0x00,
+/* 'w' 0x77 */ 0x89, 0x59, 0x59, 0x55, 0x56, 0x26, 0x26,
+/* 'x' 0x78 */ 0x4A, 0x4C, 0x43, 0x27, 0x20,
+/* 'y' 0x79 */ 0x8A, 0x52, 0xA5, 0x18, 0x84, 0x22, 0x00,
+/* 'z' 0x7A */ 0x78, 0x44, 0x46, 0x23, 0xE0,
+/* '{' 0x7B */ 0x6A, 0xAA, 0xA9,
+/* '|' 0x7C */ 0xFF, 0xE0,
+/* '}' 0x7D */ 0x95, 0x55, 0x56,
+/* '~' 0x7E */ 0x66, 0x60,
+/* 0x7F */ 0xFF, 0xC0, 0x67, 0x34, 0x58, 0x4C, 0x46, 0x03, 0x11, 0x80, 0xFF, 0xC0,
+/* 0x80 */ 0x1C, 0x45, 0x07, 0xE4, 0x1F, 0x10, 0x10, 0x1E,
+/* 0x81 */
+/* 0x82 */ 0xE0,
+/* 0x83 */
+/* 0x84 */ 0xB6, 0x80,
+/* 0x85 */ 0xA8,
+/* 0x86 */ 0x21, 0x09, 0xF2, 0x10, 0x84, 0x21, 0x08,
+/* 0x87 */ 0x21, 0x09, 0xF2, 0x10, 0x84, 0xF9, 0x08,
+/* 0x88 */
+/* 0x89 */ 0x62, 0x09, 0x40, 0x98, 0x06, 0x80, 0x10, 0x01, 0x66, 0x29, 0x92, 0x99, 0x06, 0x60,
+/* 0x8A */ 0x28, 0x47, 0xA1, 0x83, 0x07, 0x83, 0x87, 0x17, 0x80,
+/* 0x8B */ 0x64,
+/* 0x8C */ 0x10, 0x87, 0xA1, 0x83, 0x07, 0x83, 0x87, 0x17, 0x80,
+/* 0x8D */ 0x28, 0x4F, 0xC4, 0x10, 0x41, 0x04, 0x10, 0x40,
+/* 0x8E */ 0x14, 0x11, 0xF8, 0x30, 0xC1, 0x04, 0x18, 0x61, 0xFC,
+/* 0x8F */ 0x08, 0x21, 0xF8, 0x30, 0xC1, 0x04, 0x18, 0x61, 0xFC,
+/* 0x90 */
+/* 0x91 */ 0xE0,
+/* 0x92 */ 0xE0,
+/* 0x93 */ 0xB6, 0x80,
+/* 0x94 */ 0xB6, 0x80,
+/* 0x95 */ 0xFF, 0x80,
+/* 0x96 */ 0xFC,
+/* 0x97 */ 0xFF, 0xF0,
+/* 0x98 */
+/* 0x99 */ 0xE6, 0x28, 0xCD, 0x19, 0xA3, 0x34, 0x6A, 0x8B, 0x51, 0x68,
+/* 0x9A */ 0x52, 0x69, 0x8E, 0x19, 0x60,
+/* 0x9B */ 0x98,
+/* 0x9C */ 0x24, 0x06, 0x98, 0xE1, 0x96,
+/* 0x9D */ 0x15, 0xE4, 0x44, 0x44, 0x60,
+/* 0x9E */ 0x51, 0x00, 0xF0, 0x88, 0x8C, 0x47, 0xC0,
+/* 0x9F */ 0x11, 0x00, 0xF0, 0x88, 0x8C, 0x47, 0xC0,
+/* 0xA0 */
+/* 0xA1 */ 0xA8,
+/* 0xA2 */ 0x96,
+/* 0xA3 */ 0x41, 0x05, 0x18, 0x43, 0x04, 0x10, 0x7C,
+/* 0xA4 */ 0xFC, 0x63, 0xF0,
+/* 0xA5 */ 0x30, 0x38, 0x28, 0x48, 0x4C, 0x7C, 0x84, 0x86, 0x82, 0x04, 0x07,
+/* 0xA6 */ 0xF9, 0xF0,
+/* 0xA7 */ 0x32, 0x91, 0xC9, 0x47, 0x26, 0x14, 0xA4, 0xC0,
+/* 0xA8 */ 0xA0,
+/* 0xA9 */ 0x3E, 0x3F, 0xB8, 0xF4, 0x1A, 0x0D, 0x17, 0x76, 0xC6, 0x3E, 0x00,
+/* 0xAA */ 0x7A, 0x18, 0x30, 0x78, 0x38, 0x61, 0x78, 0xC1, 0x0C,
+/* 0xAB */ 0x5A, 0xA5,
+/* 0xAC */ 0xFC, 0x10, 0x40,
+/* 0xAD */
+/* 0xAE */ 0x3E, 0x31, 0xB7, 0x72, 0x99, 0xCC, 0xC7, 0x56, 0xC6, 0x3E, 0x00,
+/* 0xAF */ 0x18, 0x31, 0xF8, 0x30, 0xC1, 0x04, 0x18, 0x61, 0xFC,
+/* 0xB0 */ 0x69, 0x96,
+/* 0xB1 */ 0x21, 0x3E, 0x42, 0x03, 0xE0,
+/* 0xB2 */ 0x9C,
+/* 0xB3 */ 0x49, 0x35, 0x92, 0x40,
+/* 0xB4 */ 0x80,
+/* 0xB5 */ 0x8A, 0x28, 0xA2, 0x8A, 0x6E, 0xE0, 0x80,
+/* 0xB6 */ 0x7F, 0xAE, 0xBA, 0x68, 0xA2, 0x8A, 0x28, 0xA0,
+/* 0xB7 */ 0x80,
+/* 0xB8 */ 0x67, 0x80,
+/* 0xB9 */ 0x78, 0x84, 0x04, 0x3C, 0xC4, 0x8C, 0x76, 0x04, 0x07,
+/* 0xBA */ 0x69, 0x8E, 0x19, 0x66, 0x26,
+/* 0xBB */ 0xA5, 0x5A,
+/* 0xBC */ 0xA5, 0x21, 0x08, 0x42, 0x10, 0xF8,
+/* 0xBD */ 0xA0,
+/* 0xBE */ 0xBA, 0x49, 0x24, 0x90,
+/* 0xBF */ 0x31, 0x9E, 0x11, 0x11, 0x88, 0xF8,
+/* 0xC0 */ 0x10, 0x43, 0xE4, 0x28, 0x50, 0xBE, 0x42, 0x85, 0x0C,
+/* 0xC1 */ 0x08, 0x10, 0x00, 0x18, 0x3C, 0x24, 0x24, 0x7E, 0x42, 0xC3,
+/* 0xC2 */ 0x18, 0x24, 0x00, 0x18, 0x3C, 0x24, 0x24, 0x7E, 0x42, 0xC3,
+/* 0xC3 */ 0x24, 0x18, 0x00, 0x18, 0x3C, 0x24, 0x24, 0x7E, 0x42, 0xC3,
+/* 0xC4 */ 0x24, 0x00, 0x18, 0x3C, 0x24, 0x24, 0x7E, 0x42, 0x42, 0xC3,
+/* 0xC5 */ 0x11, 0x21, 0x08, 0x42, 0x10, 0x87, 0xC0,
+/* 0xC6 */ 0x08, 0x20, 0x01, 0xE4, 0x30, 0x20, 0x40, 0x82, 0x8C, 0xF0,
+/* 0xC7 */ 0x3E, 0x61, 0xC0, 0x80, 0x80, 0x80, 0xC1, 0x63, 0x3E, 0x0C, 0x04, 0x1C,
+/* 0xC8 */ 0x28, 0x20, 0x01, 0xE4, 0x30, 0x20, 0x40, 0x82, 0x8C, 0xF0,
+/* 0xC9 */ 0x08, 0x40, 0x3F, 0x82, 0x0F, 0xA0, 0x83, 0xF0,
+/* 0xCA */ 0xFD, 0x02, 0x04, 0x0F, 0xD0, 0x20, 0x40, 0xFC, 0x10, 0x38,
+/* 0xCB */ 0x28, 0x0F, 0xE0, 0x83, 0xE8, 0x20, 0x83, 0xF0,
+/* 0xCC */ 0x28, 0x40, 0x3F, 0x82, 0x0F, 0xA0, 0x82, 0x0F, 0xC0,
+/* 0xCD */ 0x62, 0xAA, 0xA0,
+/* 0xCE */ 0x54, 0x24, 0x92, 0x48,
+/* 0xCF */ 0x50, 0x43, 0xE4, 0x28, 0x30, 0x60, 0xC1, 0x85, 0xF0,
+/* 0xD0 */ 0x7C, 0x42, 0x41, 0x41, 0xF1, 0x41, 0x41, 0x42, 0x7C,
+/* 0xD1 */ 0x08, 0x23, 0x0F, 0x1B, 0x32, 0x66, 0xC7, 0x87, 0x04,
+/* 0xD2 */ 0x28, 0x23, 0x0F, 0x1B, 0x32, 0x66, 0xC7, 0x87, 0x04,
+/* 0xD3 */ 0x04, 0x04, 0x0F, 0x8C, 0x6C, 0x1C, 0x06, 0x03, 0x83, 0x63, 0x1F, 0x00,
+/* 0xD4 */ 0x08, 0x0A, 0x00, 0x07, 0xC6, 0x36, 0x0E, 0x03, 0x01, 0xC1, 0xB1, 0x8F, 0x80,
+/* 0xD5 */ 0x0A, 0x0A, 0x00, 0x07, 0xC6, 0x36, 0x0E, 0x03, 0x01, 0xC1, 0xB1, 0x8F, 0x80,
+/* 0xD6 */ 0x14, 0x00, 0x00, 0x07, 0xC6, 0x36, 0x0E, 0x03, 0x01, 0xC1, 0xB1, 0x8F, 0x80,
+/* 0xD7 */ 0x8A, 0x88, 0xA8, 0x80,
+/* 0xD8 */ 0x50, 0x43, 0xE4, 0x28, 0x50, 0xBE, 0x42, 0x85, 0x0C,
+/* 0xD9 */ 0x10, 0x52, 0x4C, 0x18, 0x30, 0x60, 0xC1, 0xC6, 0xF8,
+/* 0xDA */ 0x08, 0x22, 0x0C, 0x18, 0x30, 0x60, 0xC1, 0xC6, 0xF8,
+/* 0xDB */ 0x14, 0x52, 0x0C, 0x18, 0x30, 0x60, 0xC1, 0xC6, 0xF8,
+/* 0xDC */ 0x29, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC1, 0xC6, 0xF8,
+/* 0xDD */ 0x09, 0x25, 0x12, 0x22, 0x87, 0x04, 0x08, 0x10, 0x20,
+/* 0xDE */ 0xFC, 0x41, 0x04, 0x10, 0x41, 0x04, 0x10, 0x60, 0x8E,
+/* 0xDF */ 0x7A, 0x18, 0x61, 0x8A, 0x18, 0x61, 0xB8,
+/* 0xE0 */ 0x42, 0xE9, 0x24, 0x80,
+/* 0xE1 */ 0x10, 0x40, 0x03, 0xC8, 0x40, 0x8F, 0x62, 0x8C, 0xEC,
+/* 0xE2 */ 0x10, 0x50, 0x03, 0xC8, 0x40, 0x8F, 0x62, 0x8C, 0xEC,
+/* 0xE3 */ 0x48, 0x60, 0x03, 0xC8, 0x40, 0x8F, 0x62, 0x8C, 0xEC,
+/* 0xE4 */ 0x28, 0x01, 0xE4, 0x20, 0x47, 0xB1, 0x46, 0x76,
+/* 0xE5 */ 0x62, 0xAA, 0xA0,
+/* 0xE6 */ 0x10, 0x80, 0x1E, 0xC6, 0x08, 0x20, 0xC5, 0xE0,
+/* 0xE7 */ 0x7B, 0x18, 0x20, 0x83, 0x17, 0x8C, 0x11, 0xC0,
+/* 0xE8 */ 0x28, 0x40, 0x1E, 0xC6, 0x08, 0x20, 0xC5, 0xE0,
+/* 0xE9 */ 0x10, 0x80, 0x1E, 0xCE, 0x1F, 0xE0, 0xC5, 0xE0,
+/* 0xEA */ 0x7B, 0x38, 0x7F, 0x83, 0x37, 0x84, 0x1C,
+/* 0xEB */ 0x28, 0x07, 0xB3, 0x87, 0xF8, 0x31, 0x78,
+/* 0xEC */ 0x28, 0x40, 0x1E, 0xCE, 0x1F, 0xE0, 0xC5, 0xE0,
+/* 0xED */ 0x62, 0xAA, 0xA0,
+/* 0xEE */ 0x54, 0x24, 0x92, 0x48,
+/* 0xEF */ 0x02, 0x0C, 0x13, 0xEC, 0xD0, 0xA1, 0x42, 0xCC, 0xE8,
+/* 0xF0 */ 0x04, 0x1D, 0xD6, 0x68, 0x50, 0xA1, 0x66, 0x74,
+/* 0xF1 */ 0x11, 0x01, 0x6C, 0xC6, 0x31, 0x8C, 0x40,
+/* 0xF2 */ 0x20, 0x81, 0x6C, 0xC6, 0x31, 0x8C, 0x40,
+/* 0xF3 */ 0x10, 0x80, 0x1E, 0xCE, 0x18, 0x61, 0xCD, 0xE0,
+/* 0xF4 */ 0x10, 0xA0, 0x1E, 0xCE, 0x18, 0x61, 0xCD, 0xE0,
+/* 0xF5 */ 0x29, 0x40, 0x1E, 0xCE, 0x18, 0x61, 0xCD, 0xE0,
+/* 0xF6 */ 0x28, 0x07, 0xB3, 0x86, 0x18, 0x73, 0x78,
+/* 0xF7 */ 0x20, 0x3E, 0x02, 0x00,
+/* 0xF8 */ 0xA8, 0x5D, 0x24, 0x90,
+/* 0xF9 */ 0x22, 0x89, 0x18, 0xC6, 0x31, 0x9B, 0x40,
+/* 0xFA */ 0x11, 0x23, 0x18, 0xC6, 0x33, 0x68,
+/* 0xFB */ 0x2A, 0x81, 0x18, 0xC6, 0x31, 0x9B, 0x40,
+/* 0xFC */ 0x50, 0x23, 0x18, 0xC6, 0x33, 0x68,
+/* 0xFD */ 0x10, 0x88, 0x52, 0x49, 0x23, 0x0C, 0x30, 0x82, 0x18,
+/* 0xFE */ 0x4E, 0x44, 0x44, 0x46, 0x31, 0x70,
+/* 0xFF */ 0x80,
};
const GFXglyph FreeSans6pt_Win1250Glyphs[] PROGMEM = {
- /* ' ' 0x20 */ {0, 0, 0, 3, 0, 0},
- /* '!' 0x21 */ {0, 1, 9, 4, 2, -8},
- /* '"' 0x22 */ {2, 3, 3, 4, 0, -8},
- /* '#' 0x23 */ {4, 7, 8, 7, 0, -7},
- /* '$' 0x24 */ {11, 6, 11, 7, 0, -9},
- /* '%' 0x25 */ {20, 10, 9, 11, 0, -8},
- /* '&' 0x26 */ {32, 6, 9, 8, 1, -8},
- /* ''' 0x27 */ {39, 1, 3, 2, 1, -8},
- /* '(' 0x28 */ {40, 2, 11, 4, 1, -8},
- /* ')' 0x29 */ {43, 3, 11, 4, 0, -8},
- /* '*' 0x2A */ {48, 3, 3, 5, 1, -8},
- /* '+' 0x2B */ {50, 5, 5, 7, 1, -4},
- /* ',' 0x2C */ {54, 1, 3, 3, 1, 0},
- /* '-' 0x2D */ {55, 2, 1, 4, 1, -3},
- /* '.' 0x2E */ {56, 1, 1, 3, 1, 0},
- /* '/' 0x2F */ {57, 3, 9, 3, 0, -8},
- /* '0' 0x30 */ {61, 5, 9, 7, 1, -8},
- /* '1' 0x31 */ {67, 3, 9, 7, 1, -8},
- /* '2' 0x32 */ {71, 6, 9, 7, 0, -8},
- /* '3' 0x33 */ {78, 6, 9, 7, 0, -8},
- /* '4' 0x34 */ {85, 6, 9, 7, 0, -8},
- /* '5' 0x35 */ {92, 5, 9, 7, 1, -8},
- /* '6' 0x36 */ {98, 5, 9, 7, 1, -8},
- /* '7' 0x37 */ {104, 5, 9, 7, 1, -8},
- /* '8' 0x38 */ {110, 6, 9, 7, 0, -8},
- /* '9' 0x39 */ {117, 6, 9, 7, 0, -8},
- /* ':' 0x3A */ {124, 1, 7, 3, 1, -6},
- /* ';' 0x3B */ {125, 1, 8, 3, 1, -5},
- /* '<' 0x3C */ {126, 5, 5, 7, 1, -4},
- /* '=' 0x3D */ {130, 5, 3, 7, 1, -3},
- /* '>' 0x3E */ {132, 5, 5, 7, 1, -4},
- /* '?' 0x3F */ {136, 5, 9, 7, 1, -8},
- /* '@' 0x40 */ {142, 11, 11, 12, 0, -8},
- /* 'A' 0x41 */ {158, 8, 9, 8, 0, -8},
- /* 'B' 0x42 */ {167, 6, 9, 8, 1, -8},
- /* 'C' 0x43 */ {174, 8, 9, 9, 0, -8},
- /* 'D' 0x44 */ {183, 7, 9, 8, 1, -8},
- /* 'E' 0x45 */ {191, 6, 9, 8, 1, -8},
- /* 'F' 0x46 */ {198, 6, 9, 7, 1, -8},
- /* 'G' 0x47 */ {205, 8, 9, 9, 0, -8},
- /* 'H' 0x48 */ {214, 7, 9, 9, 1, -8},
- /* 'I' 0x49 */ {222, 1, 9, 3, 1, -8},
- /* 'J' 0x4A */ {224, 5, 9, 6, 0, -8},
- /* 'K' 0x4B */ {230, 7, 9, 8, 1, -8},
- /* 'L' 0x4C */ {238, 5, 9, 7, 1, -8},
- /* 'M' 0x4D */ {244, 8, 9, 10, 1, -8},
- /* 'N' 0x4E */ {253, 7, 9, 9, 1, -8},
- /* 'O' 0x4F */ {261, 9, 9, 9, 0, -8},
- /* 'P' 0x50 */ {272, 6, 9, 8, 1, -8},
- /* 'Q' 0x51 */ {279, 9, 10, 9, 0, -8},
- /* 'R' 0x52 */ {291, 7, 9, 9, 1, -8},
- /* 'S' 0x53 */ {299, 6, 9, 8, 1, -8},
- /* 'T' 0x54 */ {306, 7, 9, 8, 0, -8},
- /* 'U' 0x55 */ {314, 7, 9, 9, 1, -8},
- /* 'V' 0x56 */ {322, 7, 9, 8, 0, -8},
- /* 'W' 0x57 */ {330, 11, 9, 11, 0, -8},
- /* 'X' 0x58 */ {343, 6, 9, 8, 1, -8},
- /* 'Y' 0x59 */ {350, 8, 9, 8, 0, -8},
- /* 'Z' 0x5A */ {359, 7, 9, 7, 0, -8},
- /* '[' 0x5B */ {367, 2, 12, 3, 1, -8},
- /* '\' 0x5C */ {370, 3, 9, 3, 0, -8},
- /* ']' 0x5D */ {374, 2, 12, 3, 0, -8},
- /* '^' 0x5E */ {377, 4, 4, 6, 1, -8},
- /* '_' 0x5F */ {379, 7, 1, 7, 0, 2},
- /* '`' 0x60 */ {380, 1, 1, 3, 1, -8},
- /* 'a' 0x61 */ {381, 6, 7, 7, 0, -6},
- /* 'b' 0x62 */ {387, 5, 9, 7, 1, -8},
- /* 'c' 0x63 */ {393, 6, 7, 6, 0, -6},
- /* 'd' 0x64 */ {399, 6, 9, 7, 0, -8},
- /* 'e' 0x65 */ {406, 6, 7, 6, 0, -6},
- /* 'f' 0x66 */ {412, 3, 9, 3, 0, -8},
- /* 'g' 0x67 */ {416, 6, 10, 7, 0, -6},
- /* 'h' 0x68 */ {424, 5, 9, 6, 1, -8},
- /* 'i' 0x69 */ {430, 1, 9, 3, 1, -8},
- /* 'j' 0x6A */ {432, 2, 12, 3, 0, -8},
- /* 'k' 0x6B */ {435, 5, 9, 6, 1, -8},
- /* 'l' 0x6C */ {441, 1, 9, 3, 1, -8},
- /* 'm' 0x6D */ {443, 8, 7, 10, 1, -6},
- /* 'n' 0x6E */ {450, 5, 7, 6, 1, -6},
- /* 'o' 0x6F */ {455, 6, 7, 6, 0, -6},
- /* 'p' 0x70 */ {461, 5, 9, 7, 1, -6},
- /* 'q' 0x71 */ {467, 6, 9, 7, 0, -6},
- /* 'r' 0x72 */ {474, 3, 7, 4, 1, -6},
- /* 's' 0x73 */ {477, 5, 7, 6, 0, -6},
- /* 't' 0x74 */ {482, 3, 8, 3, 0, -7},
- /* 'u' 0x75 */ {485, 5, 7, 6, 1, -6},
- /* 'v' 0x76 */ {490, 6, 7, 6, 0, -6},
- /* 'w' 0x77 */ {496, 8, 7, 9, 0, -6},
- /* 'x' 0x78 */ {503, 5, 7, 6, 0, -6},
- /* 'y' 0x79 */ {508, 5, 10, 6, 0, -6},
- /* 'z' 0x7A */ {515, 5, 7, 6, 0, -6},
- /* '{' 0x7B */ {520, 2, 12, 4, 1, -8},
- /* '|' 0x7C */ {523, 1, 11, 3, 1, -8},
- /* '}' 0x7D */ {525, 2, 12, 4, 1, -8},
- /* '~' 0x7E */ {528, 6, 2, 6, 0, -4},
- /* 0x7F */ {530, 9, 10, 11, 1, -8},
- /* 0x80 */ {542, 7, 9, 8, 0, -8},
- /* 0x81 */ {550, 0, 0, 8, 0, 0},
- /* 0x82 */ {550, 1, 3, 3, 1, 0},
- /* 0x83 */ {551, 0, 0, 8, 0, 0},
- /* 0x84 */ {551, 3, 3, 5, 1, 0},
- /* 0x85 */ {553, 5, 1, 7, 1, 0},
- /* 0x86 */ {554, 5, 11, 7, 1, -8},
- /* 0x87 */ {561, 5, 11, 7, 1, -8},
- /* 0x88 */ {568, 0, 0, 8, 0, 0},
- /* 0x89 */ {568, 12, 9, 12, 0, -8},
- /* 0x8A */ {582, 6, 11, 8, 1, -9},
- /* 0x8B */ {591, 2, 3, 4, 1, -4},
- /* 0x8C */ {592, 6, 11, 8, 1, -10},
- /* 0x8D */ {601, 6, 10, 8, 0, -9},
- /* 0x8E */ {609, 7, 10, 7, 0, -9},
- /* 0x8F */ {618, 7, 10, 7, 0, -9},
- /* 0x90 */ {627, 0, 0, 8, 0, 0},
- /* 0x91 */ {627, 1, 3, 3, 1, -8},
- /* 0x92 */ {628, 1, 3, 2, 1, -8},
- /* 0x93 */ {629, 3, 3, 5, 1, -8},
- /* 0x94 */ {631, 3, 3, 5, 1, -8},
- /* 0x95 */ {633, 3, 3, 5, 1, -5},
- /* 0x96 */ {635, 6, 1, 6, 0, -3},
- /* 0x97 */ {636, 12, 1, 12, 0, -3},
- /* 0x98 */ {638, 0, 0, 8, 0, 0},
- /* 0x99 */ {638, 11, 7, 12, 1, -8},
- /* 0x9A */ {648, 4, 9, 6, 1, -8},
- /* 0x9B */ {653, 2, 3, 3, 1, -4},
- /* 0x9C */ {654, 4, 10, 6, 1, -9},
- /* 0x9D */ {659, 4, 9, 5, 0, -8},
- /* 0x9E */ {664, 5, 10, 6, 0, -9},
- /* 0x9F */ {671, 5, 10, 6, 0, -9},
- /* 0xA0 */ {678, 0, 0, 3, 0, 0},
- /* 0xA1 */ {678, 3, 2, 4, 0, -8},
- /* 0xA2 */ {679, 4, 2, 4, 0, -8},
- /* 0xA3 */ {680, 6, 9, 7, 0, -8},
- /* 0xA4 */ {687, 5, 4, 7, 1, -5},
- /* 0xA5 */ {690, 8, 11, 8, 1, -8},
- /* 0xA6 */ {701, 1, 12, 3, 1, -8},
- /* 0xA7 */ {703, 5, 12, 7, 1, -8},
- /* 0xA8 */ {711, 3, 1, 4, 0, -7},
- /* 0xA9 */ {712, 9, 9, 10, 0, -8},
- /* 0xAA */ {723, 6, 12, 8, 1, -8},
- /* 0xAB */ {732, 4, 4, 6, 1, -4},
- /* 0xAC */ {734, 6, 3, 7, 1, -4},
- /* 0xAD */ {737, 0, 0, 0, 0, 0},
- /* 0xAE */ {737, 9, 9, 10, 0, -8},
- /* 0xAF */ {748, 7, 10, 7, 0, -9},
- /* 0xB0 */ {757, 4, 4, 7, 2, -8},
- /* 0xB1 */ {759, 5, 7, 7, 1, -6},
- /* 0xB2 */ {764, 3, 2, 4, 1, 1},
- /* 0xB3 */ {765, 3, 9, 3, 0, -8},
- /* 0xB4 */ {769, 1, 1, 4, 1, -8},
- /* 0xB5 */ {770, 6, 9, 7, 1, -6},
- /* 0xB6 */ {777, 6, 10, 6, 1, -8},
- /* 0xB7 */ {785, 1, 1, 3, 1, -2},
- /* 0xB8 */ {786, 3, 3, 4, 1, 1},
- /* 0xB9 */ {788, 8, 9, 7, 0, -6},
- /* 0xBA */ {797, 4, 10, 6, 1, -6},
- /* 0xBB */ {802, 4, 4, 6, 1, -5},
- /* 0xBC */ {804, 5, 9, 7, 1, -8},
- /* 0xBD */ {810, 3, 1, 4, 0, -8},
- /* 0xBE */ {811, 3, 10, 3, 1, -9},
- /* 0xBF */ {815, 5, 9, 6, 0, -8},
- /* 0xC0 */ {821, 7, 10, 9, 1, -9},
- /* 0xC1 */ {830, 8, 10, 8, 0, -9},
- /* 0xC2 */ {840, 8, 10, 8, 0, -9},
- /* 0xC3 */ {850, 8, 10, 8, 0, -9},
- /* 0xC4 */ {860, 8, 10, 8, 0, -9},
- /* 0xC5 */ {870, 5, 10, 7, 1, -9},
- /* 0xC6 */ {877, 7, 11, 9, 0, -10},
- /* 0xC7 */ {887, 8, 12, 9, 0, -8},
- /* 0xC8 */ {899, 7, 11, 9, 0, -10},
- /* 0xC9 */ {909, 6, 10, 8, 1, -9},
- /* 0xCA */ {917, 7, 11, 8, 1, -8},
- /* 0xCB */ {927, 6, 10, 8, 1, -9},
- /* 0xCC */ {935, 6, 11, 8, 1, -10},
- /* 0xCD */ {944, 2, 10, 3, 1, -9},
- /* 0xCE */ {947, 3, 10, 4, 0, -9},
- /* 0xCF */ {951, 7, 10, 8, 1, -9},
- /* 0xD0 */ {960, 8, 9, 8, 0, -8},
- /* 0xD1 */ {969, 7, 10, 9, 1, -9},
- /* 0xD2 */ {978, 7, 10, 9, 1, -9},
- /* 0xD3 */ {987, 9, 10, 9, 0, -9},
- /* 0xD4 */ {999, 9, 11, 9, 0, -10},
- /* 0xD5 */ {1012, 9, 11, 9, 0, -10},
- /* 0xD6 */ {1025, 9, 11, 9, 0, -10},
- /* 0xD7 */ {1038, 5, 5, 7, 1, -5},
- /* 0xD8 */ {1042, 7, 10, 9, 1, -9},
- /* 0xD9 */ {1051, 7, 10, 9, 1, -9},
- /* 0xDA */ {1060, 7, 10, 9, 1, -9},
- /* 0xDB */ {1069, 7, 10, 9, 1, -9},
- /* 0xDC */ {1078, 7, 10, 9, 1, -9},
- /* 0xDD */ {1087, 7, 10, 8, 1, -9},
- /* 0xDE */ {1096, 6, 12, 7, 0, -8},
- /* 0xDF */ {1105, 6, 9, 7, 1, -8},
- /* 0xE0 */ {1112, 3, 9, 4, 1, -8},
- /* 0xE1 */ {1116, 7, 10, 7, 0, -9},
- /* 0xE2 */ {1125, 7, 10, 7, 0, -9},
- /* 0xE3 */ {1134, 7, 10, 7, 0, -9},
- /* 0xE4 */ {1143, 7, 9, 7, 0, -8},
- /* 0xE5 */ {1151, 2, 10, 3, 1, -9},
- /* 0xE6 */ {1154, 6, 10, 6, 0, -9},
- /* 0xE7 */ {1162, 6, 10, 6, 0, -6},
- /* 0xE8 */ {1170, 6, 10, 6, 0, -9},
- /* 0xE9 */ {1178, 6, 10, 6, 0, -9},
- /* 0xEA */ {1186, 6, 9, 6, 0, -6},
- /* 0xEB */ {1193, 6, 9, 6, 0, -8},
- /* 0xEC */ {1200, 6, 10, 6, 0, -9},
- /* 0xED */ {1208, 2, 10, 3, 1, -9},
- /* 0xEE */ {1211, 3, 10, 3, 0, -9},
- /* 0xEF */ {1215, 7, 10, 7, 0, -9},
- /* 0xF0 */ {1224, 7, 9, 7, 0, -8},
- /* 0xF1 */ {1232, 5, 10, 6, 1, -9},
- /* 0xF2 */ {1239, 5, 10, 6, 1, -9},
- /* 0xF3 */ {1246, 6, 10, 6, 0, -9},
- /* 0xF4 */ {1254, 6, 10, 6, 0, -9},
- /* 0xF5 */ {1262, 6, 10, 6, 0, -9},
- /* 0xF6 */ {1270, 6, 9, 6, 0, -8},
- /* 0xF7 */ {1277, 5, 5, 7, 1, -5},
- /* 0xF8 */ {1281, 3, 10, 4, 1, -9},
- /* 0xF9 */ {1285, 5, 10, 6, 1, -9},
- /* 0xFA */ {1292, 5, 9, 6, 1, -8},
- /* 0xFB */ {1298, 5, 10, 6, 1, -9},
- /* 0xFC */ {1305, 5, 9, 6, 1, -8},
- /* 0xFD */ {1311, 6, 12, 6, 0, -8},
- /* 0xFE */ {1320, 4, 11, 3, 0, -7},
- /* 0xFF */ {1326, 1, 1, 4, 1, -7},
+/* 0x01 */ { 0, 9, 10, 11, 1, -9 },
+/* 0x02 */ { 12, 9, 10, 11, 1, -8 },
+/* 0x03 */ { 24, 10, 10, 12, 1, -8 },
+/* 0x04 */ { 37, 10, 10, 12, 1, -8 },
+/* 0x05 */ { 50, 10, 10, 12, 1, -9 },
+/* 0x06 */ { 63, 11, 11, 13, 1, -9 },
+/* 0x07 */ { 79, 0, 0, 8, 0, 0 },
+/* 0x08 */ { 79, 12, 9, 14, 1, -8 },
+/* 0x09 */ { 93, 14, 8, 16, 1, -7 },
+/* 0x0A */ { 107, 0, 0, 8, 0, 0 },
+/* 0x0B */ { 107, 9, 10, 11, 1, -9 },
+/* 0x0C */ { 119, 13, 9, 15, 1, -8 },
+/* 0x0D */ { 134, 0, 0, 8, 0, 0 },
+/* 0x0E */ { 134, 9, 11, 11, 1, -9 },
+/* 0x0F */ { 147, 10, 10, 12, 1, -9 },
+/* 0x10 */ { 160, 11, 10, 13, 1, -9 },
+/* 0x11 */ { 174, 13, 10, 15, 1, -9 },
+/* 0x12 */ { 191, 10, 10, 12, 1, -9 },
+/* 0x13 */ { 204, 11, 10, 13, 1, -9 },
+/* 0x14 */ { 218, 10, 10, 12, 1, -9 },
+/* 0x15 */ { 231, 14, 10, 16, 1, -9 },
+/* 0x16 */ { 249, 8, 10, 10, 1, -9 },
+/* 0x17 */ { 259, 12, 10, 14, 1, -9 },
+/* 0x18 */ { 274, 13, 10, 15, 1, -9 },
+/* 0x19 */ { 291, 12, 10, 14, 1, -9 },
+/* 0x1A */ { 306, 9, 10, 11, 1, -8 },
+/* 0x1B */ { 318, 14, 10, 16, 1, -9 },
+/* 0x1C */ { 336, 11, 10, 13, 1, -9 },
+/* 0x1D */ { 350, 11, 10, 13, 1, -9 },
+/* 0x1E */ { 364, 12, 10, 14, 1, -9 },
+/* 0x1F */ { 379, 8, 10, 11, 2, -9 },
+/* ' ' 0x20 */ { 389, 0, 0, 3, 0, 0 },
+/* '!' 0x21 */ { 389, 1, 9, 4, 2, -8 },
+/* '"' 0x22 */ { 391, 3, 3, 4, 0, -8 },
+/* '#' 0x23 */ { 393, 7, 8, 7, 0, -7 },
+/* '$' 0x24 */ { 400, 6, 11, 7, 0, -9 },
+/* '%' 0x25 */ { 409, 10, 9, 11, 0, -8 },
+/* '&' 0x26 */ { 421, 6, 9, 8, 1, -8 },
+/* ''' 0x27 */ { 428, 1, 3, 2, 1, -8 },
+/* '(' 0x28 */ { 429, 2, 11, 4, 1, -8 },
+/* ')' 0x29 */ { 432, 3, 11, 4, 0, -8 },
+/* '*' 0x2A */ { 437, 3, 3, 5, 1, -8 },
+/* '+' 0x2B */ { 439, 5, 5, 7, 1, -4 },
+/* ',' 0x2C */ { 443, 1, 3, 3, 1, 0 },
+/* '-' 0x2D */ { 444, 2, 1, 4, 1, -3 },
+/* '.' 0x2E */ { 445, 1, 1, 3, 1, 0 },
+/* '/' 0x2F */ { 446, 3, 9, 3, 0, -8 },
+/* '0' 0x30 */ { 450, 5, 9, 7, 1, -8 },
+/* '1' 0x31 */ { 456, 3, 9, 7, 1, -8 },
+/* '2' 0x32 */ { 460, 6, 9, 7, 0, -8 },
+/* '3' 0x33 */ { 467, 6, 9, 7, 0, -8 },
+/* '4' 0x34 */ { 474, 6, 9, 7, 0, -8 },
+/* '5' 0x35 */ { 481, 5, 9, 7, 1, -8 },
+/* '6' 0x36 */ { 487, 5, 9, 7, 1, -8 },
+/* '7' 0x37 */ { 493, 5, 9, 7, 1, -8 },
+/* '8' 0x38 */ { 499, 6, 9, 7, 0, -8 },
+/* '9' 0x39 */ { 506, 6, 9, 7, 0, -8 },
+/* ':' 0x3A */ { 513, 1, 7, 3, 1, -6 },
+/* ';' 0x3B */ { 514, 1, 8, 3, 1, -5 },
+/* '<' 0x3C */ { 515, 5, 5, 7, 1, -4 },
+/* '=' 0x3D */ { 519, 5, 3, 7, 1, -3 },
+/* '>' 0x3E */ { 521, 5, 5, 7, 1, -4 },
+/* '?' 0x3F */ { 525, 5, 9, 7, 1, -8 },
+/* '@' 0x40 */ { 531, 11, 11, 12, 0, -8 },
+/* 'A' 0x41 */ { 547, 8, 9, 8, 0, -8 },
+/* 'B' 0x42 */ { 556, 6, 9, 8, 1, -8 },
+/* 'C' 0x43 */ { 563, 8, 9, 9, 0, -8 },
+/* 'D' 0x44 */ { 572, 7, 9, 8, 1, -8 },
+/* 'E' 0x45 */ { 580, 6, 9, 8, 1, -8 },
+/* 'F' 0x46 */ { 587, 6, 9, 7, 1, -8 },
+/* 'G' 0x47 */ { 594, 8, 9, 9, 0, -8 },
+/* 'H' 0x48 */ { 603, 7, 9, 9, 1, -8 },
+/* 'I' 0x49 */ { 611, 1, 9, 3, 1, -8 },
+/* 'J' 0x4A */ { 613, 5, 9, 6, 0, -8 },
+/* 'K' 0x4B */ { 619, 7, 9, 8, 1, -8 },
+/* 'L' 0x4C */ { 627, 5, 9, 7, 1, -8 },
+/* 'M' 0x4D */ { 633, 8, 9, 10, 1, -8 },
+/* 'N' 0x4E */ { 642, 7, 9, 9, 1, -8 },
+/* 'O' 0x4F */ { 650, 9, 9, 9, 0, -8 },
+/* 'P' 0x50 */ { 661, 6, 9, 8, 1, -8 },
+/* 'Q' 0x51 */ { 668, 9, 10, 9, 0, -8 },
+/* 'R' 0x52 */ { 680, 7, 9, 9, 1, -8 },
+/* 'S' 0x53 */ { 688, 6, 9, 8, 1, -8 },
+/* 'T' 0x54 */ { 695, 7, 9, 8, 0, -8 },
+/* 'U' 0x55 */ { 703, 7, 9, 9, 1, -8 },
+/* 'V' 0x56 */ { 711, 7, 9, 8, 0, -8 },
+/* 'W' 0x57 */ { 719, 11, 9, 11, 0, -8 },
+/* 'X' 0x58 */ { 732, 6, 9, 8, 1, -8 },
+/* 'Y' 0x59 */ { 739, 8, 9, 8, 0, -8 },
+/* 'Z' 0x5A */ { 748, 7, 9, 7, 0, -8 },
+/* '[' 0x5B */ { 756, 2, 12, 3, 1, -8 },
+/* '\' 0x5C */ { 759, 3, 9, 3, 0, -8 },
+/* ']' 0x5D */ { 763, 2, 12, 3, 0, -8 },
+/* '^' 0x5E */ { 766, 4, 4, 6, 1, -8 },
+/* '_' 0x5F */ { 768, 7, 1, 7, 0, 2 },
+/* '`' 0x60 */ { 769, 1, 1, 3, 1, -8 },
+/* 'a' 0x61 */ { 770, 6, 7, 7, 0, -6 },
+/* 'b' 0x62 */ { 776, 5, 9, 7, 1, -8 },
+/* 'c' 0x63 */ { 782, 6, 7, 6, 0, -6 },
+/* 'd' 0x64 */ { 788, 6, 9, 7, 0, -8 },
+/* 'e' 0x65 */ { 795, 6, 7, 6, 0, -6 },
+/* 'f' 0x66 */ { 801, 3, 9, 3, 0, -8 },
+/* 'g' 0x67 */ { 805, 6, 10, 7, 0, -6 },
+/* 'h' 0x68 */ { 813, 5, 9, 6, 1, -8 },
+/* 'i' 0x69 */ { 819, 1, 9, 3, 1, -8 },
+/* 'j' 0x6A */ { 821, 2, 12, 3, 0, -8 },
+/* 'k' 0x6B */ { 824, 5, 9, 6, 1, -8 },
+/* 'l' 0x6C */ { 830, 1, 9, 3, 1, -8 },
+/* 'm' 0x6D */ { 832, 8, 7, 10, 1, -6 },
+/* 'n' 0x6E */ { 839, 5, 7, 6, 1, -6 },
+/* 'o' 0x6F */ { 844, 6, 7, 6, 0, -6 },
+/* 'p' 0x70 */ { 850, 5, 9, 7, 1, -6 },
+/* 'q' 0x71 */ { 856, 6, 9, 7, 0, -6 },
+/* 'r' 0x72 */ { 863, 3, 7, 4, 1, -6 },
+/* 's' 0x73 */ { 866, 5, 7, 6, 0, -6 },
+/* 't' 0x74 */ { 871, 3, 8, 3, 0, -7 },
+/* 'u' 0x75 */ { 874, 5, 7, 6, 1, -6 },
+/* 'v' 0x76 */ { 879, 6, 7, 6, 0, -6 },
+/* 'w' 0x77 */ { 885, 8, 7, 9, 0, -6 },
+/* 'x' 0x78 */ { 892, 5, 7, 6, 0, -6 },
+/* 'y' 0x79 */ { 897, 5, 10, 6, 0, -6 },
+/* 'z' 0x7A */ { 904, 5, 7, 6, 0, -6 },
+/* '{' 0x7B */ { 909, 2, 12, 4, 1, -8 },
+/* '|' 0x7C */ { 912, 1, 11, 3, 1, -8 },
+/* '}' 0x7D */ { 914, 2, 12, 4, 1, -8 },
+/* '~' 0x7E */ { 917, 6, 2, 6, 0, -4 },
+/* 0x7F */ { 919, 9, 10, 0, 1, -8 },
+/* 0x80 */ { 931, 7, 9, 8, 0, -8 },
+/* 0x81 */ { 939, 0, 0, 8, 0, 0 },
+/* 0x82 */ { 939, 1, 3, 3, 1, 0 },
+/* 0x83 */ { 940, 0, 0, 8, 0, 0 },
+/* 0x84 */ { 940, 3, 3, 5, 1, 0 },
+/* 0x85 */ { 942, 5, 1, 7, 1, 0 },
+/* 0x86 */ { 943, 5, 11, 7, 1, -8 },
+/* 0x87 */ { 950, 5, 11, 7, 1, -8 },
+/* 0x88 */ { 957, 0, 0, 8, 0, 0 },
+/* 0x89 */ { 957, 12, 9, 12, 0, -8 },
+/* 0x8A */ { 971, 6, 11, 8, 1, -9 },
+/* 0x8B */ { 980, 2, 3, 4, 1, -4 },
+/* 0x8C */ { 981, 6, 11, 8, 1, -10 },
+/* 0x8D */ { 990, 6, 10, 8, 0, -9 },
+/* 0x8E */ { 998, 7, 10, 7, 0, -9 },
+/* 0x8F */ { 1007, 7, 10, 7, 0, -9 },
+/* 0x90 */ { 1016, 0, 0, 8, 0, 0 },
+/* 0x91 */ { 1016, 1, 3, 3, 1, -8 },
+/* 0x92 */ { 1017, 1, 3, 2, 1, -8 },
+/* 0x93 */ { 1018, 3, 3, 5, 1, -8 },
+/* 0x94 */ { 1020, 3, 3, 5, 1, -8 },
+/* 0x95 */ { 1022, 3, 3, 5, 1, -5 },
+/* 0x96 */ { 1024, 6, 1, 6, 0, -3 },
+/* 0x97 */ { 1025, 12, 1, 12, 0, -3 },
+/* 0x98 */ { 1027, 0, 0, 8, 0, 0 },
+/* 0x99 */ { 1027, 11, 7, 12, 1, -8 },
+/* 0x9A */ { 1037, 4, 9, 6, 1, -8 },
+/* 0x9B */ { 1042, 2, 3, 3, 1, -4 },
+/* 0x9C */ { 1043, 4, 10, 6, 1, -9 },
+/* 0x9D */ { 1048, 4, 9, 5, 0, -8 },
+/* 0x9E */ { 1053, 5, 10, 6, 0, -9 },
+/* 0x9F */ { 1060, 5, 10, 6, 0, -9 },
+/* 0xA0 */ { 1067, 0, 0, 3, 0, 0 },
+/* 0xA1 */ { 1067, 3, 2, 4, 0, -8 },
+/* 0xA2 */ { 1068, 4, 2, 4, 0, -8 },
+/* 0xA3 */ { 1069, 6, 9, 7, 0, -8 },
+/* 0xA4 */ { 1076, 5, 4, 7, 1, -5 },
+/* 0xA5 */ { 1079, 8, 11, 8, 1, -8 },
+/* 0xA6 */ { 1090, 1, 12, 3, 1, -8 },
+/* 0xA7 */ { 1092, 5, 12, 7, 1, -8 },
+/* 0xA8 */ { 1100, 3, 1, 4, 0, -7 },
+/* 0xA9 */ { 1101, 9, 9, 10, 0, -8 },
+/* 0xAA */ { 1112, 6, 12, 8, 1, -8 },
+/* 0xAB */ { 1121, 4, 4, 6, 1, -4 },
+/* 0xAC */ { 1123, 6, 3, 7, 1, -4 },
+/* 0xAD */ { 1126, 0, 0, 0, 0, 0 },
+/* 0xAE */ { 1126, 9, 9, 10, 0, -8 },
+/* 0xAF */ { 1137, 7, 10, 7, 0, -9 },
+/* 0xB0 */ { 1146, 4, 4, 7, 2, -8 },
+/* 0xB1 */ { 1148, 5, 7, 7, 1, -6 },
+/* 0xB2 */ { 1153, 3, 2, 4, 1, 1 },
+/* 0xB3 */ { 1154, 3, 9, 3, 0, -8 },
+/* 0xB4 */ { 1158, 1, 1, 4, 1, -8 },
+/* 0xB5 */ { 1159, 6, 9, 7, 1, -6 },
+/* 0xB6 */ { 1166, 6, 10, 6, 1, -8 },
+/* 0xB7 */ { 1174, 1, 1, 3, 1, -2 },
+/* 0xB8 */ { 1175, 3, 3, 4, 1, 1 },
+/* 0xB9 */ { 1177, 8, 9, 7, 0, -6 },
+/* 0xBA */ { 1186, 4, 10, 6, 1, -6 },
+/* 0xBB */ { 1191, 4, 4, 6, 1, -5 },
+/* 0xBC */ { 1193, 5, 9, 7, 1, -8 },
+/* 0xBD */ { 1199, 3, 1, 4, 0, -8 },
+/* 0xBE */ { 1200, 3, 10, 3, 1, -9 },
+/* 0xBF */ { 1204, 5, 9, 6, 0, -8 },
+/* 0xC0 */ { 1210, 7, 10, 9, 1, -9 },
+/* 0xC1 */ { 1219, 8, 10, 8, 0, -9 },
+/* 0xC2 */ { 1229, 8, 10, 8, 0, -9 },
+/* 0xC3 */ { 1239, 8, 10, 8, 0, -9 },
+/* 0xC4 */ { 1249, 8, 10, 8, 0, -9 },
+/* 0xC5 */ { 1259, 5, 10, 7, 1, -9 },
+/* 0xC6 */ { 1266, 7, 11, 9, 0, -10 },
+/* 0xC7 */ { 1276, 8, 12, 9, 0, -8 },
+/* 0xC8 */ { 1288, 7, 11, 9, 0, -10 },
+/* 0xC9 */ { 1298, 6, 10, 8, 1, -9 },
+/* 0xCA */ { 1306, 7, 11, 8, 1, -8 },
+/* 0xCB */ { 1316, 6, 10, 8, 1, -9 },
+/* 0xCC */ { 1324, 6, 11, 8, 1, -10 },
+/* 0xCD */ { 1333, 2, 10, 3, 1, -9 },
+/* 0xCE */ { 1336, 3, 10, 4, 0, -9 },
+/* 0xCF */ { 1340, 7, 10, 8, 1, -9 },
+/* 0xD0 */ { 1349, 8, 9, 8, 0, -8 },
+/* 0xD1 */ { 1358, 7, 10, 9, 1, -9 },
+/* 0xD2 */ { 1367, 7, 10, 9, 1, -9 },
+/* 0xD3 */ { 1376, 9, 10, 9, 0, -9 },
+/* 0xD4 */ { 1388, 9, 11, 9, 0, -10 },
+/* 0xD5 */ { 1401, 9, 11, 9, 0, -10 },
+/* 0xD6 */ { 1414, 9, 11, 9, 0, -10 },
+/* 0xD7 */ { 1427, 5, 5, 7, 1, -5 },
+/* 0xD8 */ { 1431, 7, 10, 9, 1, -9 },
+/* 0xD9 */ { 1440, 7, 10, 9, 1, -9 },
+/* 0xDA */ { 1449, 7, 10, 9, 1, -9 },
+/* 0xDB */ { 1458, 7, 10, 9, 1, -9 },
+/* 0xDC */ { 1467, 7, 10, 9, 1, -9 },
+/* 0xDD */ { 1476, 7, 10, 8, 1, -9 },
+/* 0xDE */ { 1485, 6, 12, 7, 0, -8 },
+/* 0xDF */ { 1494, 6, 9, 7, 1, -8 },
+/* 0xE0 */ { 1501, 3, 9, 4, 1, -8 },
+/* 0xE1 */ { 1505, 7, 10, 7, 0, -9 },
+/* 0xE2 */ { 1514, 7, 10, 7, 0, -9 },
+/* 0xE3 */ { 1523, 7, 10, 7, 0, -9 },
+/* 0xE4 */ { 1532, 7, 9, 7, 0, -8 },
+/* 0xE5 */ { 1540, 2, 10, 3, 1, -9 },
+/* 0xE6 */ { 1543, 6, 10, 6, 0, -9 },
+/* 0xE7 */ { 1551, 6, 10, 6, 0, -6 },
+/* 0xE8 */ { 1559, 6, 10, 6, 0, -9 },
+/* 0xE9 */ { 1567, 6, 10, 6, 0, -9 },
+/* 0xEA */ { 1575, 6, 9, 6, 0, -6 },
+/* 0xEB */ { 1582, 6, 9, 6, 0, -8 },
+/* 0xEC */ { 1589, 6, 10, 6, 0, -9 },
+/* 0xED */ { 1597, 2, 10, 3, 1, -9 },
+/* 0xEE */ { 1600, 3, 10, 3, 0, -9 },
+/* 0xEF */ { 1604, 7, 10, 7, 0, -9 },
+/* 0xF0 */ { 1613, 7, 9, 7, 0, -8 },
+/* 0xF1 */ { 1621, 5, 10, 6, 1, -9 },
+/* 0xF2 */ { 1628, 5, 10, 6, 1, -9 },
+/* 0xF3 */ { 1635, 6, 10, 6, 0, -9 },
+/* 0xF4 */ { 1643, 6, 10, 6, 0, -9 },
+/* 0xF5 */ { 1651, 6, 10, 6, 0, -9 },
+/* 0xF6 */ { 1659, 6, 9, 6, 0, -8 },
+/* 0xF7 */ { 1666, 5, 5, 7, 1, -5 },
+/* 0xF8 */ { 1670, 3, 10, 4, 1, -9 },
+/* 0xF9 */ { 1674, 5, 10, 6, 1, -9 },
+/* 0xFA */ { 1681, 5, 9, 6, 1, -8 },
+/* 0xFB */ { 1687, 5, 10, 6, 1, -9 },
+/* 0xFC */ { 1694, 5, 9, 6, 1, -8 },
+/* 0xFD */ { 1700, 6, 12, 6, 0, -8 },
+/* 0xFE */ { 1709, 4, 11, 3, 0, -7 },
+/* 0xFF */ { 1715, 1, 1, 4, 1, -7 },
};
-const GFXfont FreeSans6pt_Win1250 PROGMEM = {(uint8_t *)FreeSans6pt_Win1250Bitmaps, (GFXglyph *)FreeSans6pt_Win1250Glyphs, 0x20,
- 0xFF, 14};
+const GFXfont FreeSans6pt_Win1250 PROGMEM = {
+(uint8_t*)FreeSans6pt_Win1250Bitmaps,
+(GFXglyph*)FreeSans6pt_Win1250Glyphs,
+0x01, 0xFF, 14
+};
diff --git a/src/graphics/niche/Fonts/FreeSans6pt_Win1251.h b/src/graphics/niche/Fonts/FreeSans6pt_Win1251.h
index 4d3ad1705..8fe505fbd 100644
--- a/src/graphics/niche/Fonts/FreeSans6pt_Win1251.h
+++ b/src/graphics/niche/Fonts/FreeSans6pt_Win1251.h
@@ -1,457 +1,527 @@
+// trunk-ignore-all(clang-format)
#pragma once
+/* PROPERTIES
+
+FONT_NAME FreeSans6pt_Win1251
+*/
const uint8_t FreeSans6pt_Win1251Bitmaps[] PROGMEM = {
- /* ' ' 0x20 */
- 0xFC, 0x80, /* '!' 0x21 */
- 0xB6, 0x80, /* '"' 0x22 */
- 0x24, 0x51, 0xF9, 0x42, 0x9F, 0x92, 0x28, /* '#' 0x23 */
- 0x10, 0xE5, 0x55, 0x50, 0xE1, 0x65, 0x55, 0xE1, 0x00, /* '$' 0x24 */
- 0x71, 0x24, 0x89, 0x22, 0x50, 0x74, 0x02, 0x70, 0xA4, 0x49, 0x11, 0xC0, /* '%' 0x25 */
- 0x71, 0x24, 0x9C, 0x62, 0x58, 0xA7, 0xF4, /* '&' 0x26 */
- 0xE0, /* ''' 0x27 */
- 0x5A, 0xAA, 0x94, /* '(' 0x28 */
- 0x89, 0x12, 0x49, 0x29, 0x00, /* ')' 0x29 */
- 0x5E, 0x80, /* '*' 0x2A */
- 0x21, 0x3E, 0x42, 0x00, /* '+' 0x2B */
- 0xE0, /* ',' 0x2C */
- 0xC0, /* '-' 0x2D */
- 0x80, /* '.' 0x2E */
- 0x24, 0xA4, 0xA4, 0x80, /* '/' 0x2F */
- 0x76, 0xE3, 0x18, 0xC6, 0x3B, 0x70, /* '0' 0x30 */
- 0x27, 0x92, 0x49, 0x20, /* '1' 0x31 */
- 0x79, 0x10, 0x41, 0x08, 0xC6, 0x10, 0xFC, /* '2' 0x32 */
- 0x79, 0x30, 0x43, 0x18, 0x10, 0x71, 0x78, /* '3' 0x33 */
- 0x08, 0x61, 0x8A, 0x49, 0x2F, 0xC2, 0x08, /* '4' 0x34 */
- 0xFC, 0x21, 0xE8, 0x84, 0x31, 0xF0, /* '5' 0x35 */
- 0x74, 0x61, 0xE8, 0xC6, 0x31, 0x70, /* '6' 0x36 */
- 0xF8, 0x44, 0x22, 0x11, 0x08, 0x40, /* '7' 0x37 */
- 0x39, 0x34, 0x53, 0x39, 0x1C, 0x51, 0x38, /* '8' 0x38 */
- 0x39, 0x3C, 0x71, 0x4C, 0xF0, 0x53, 0x78, /* '9' 0x39 */
- 0x82, /* ':' 0x3A */
- 0x87, /* ';' 0x3B */
- 0x3E, 0x30, 0x60, 0x80, /* '<' 0x3C */
- 0xF8, 0x3E, /* '=' 0x3D */
- 0xE0, 0xC6, 0xC8, 0x00, /* '>' 0x3E */
- 0x74, 0x42, 0x11, 0x10, 0x80, 0x20, /* '?' 0x3F */
- 0x0F, 0x86, 0x19, 0x9A, 0xA4, 0xD9, 0x13, 0x22, 0x56, 0xDA, 0x6E, 0x60, 0x06, 0x00, 0x3C, 0x00, /* '@' 0x40 */
- 0x18, 0x18, 0x24, 0x24, 0x24, 0x7E, 0x42, 0x42, 0xC3, /* 'A' 0x41 */
- 0xFA, 0x18, 0x61, 0xFA, 0x18, 0x61, 0xFC, /* 'B' 0x42 */
- 0x3E, 0x63, 0x40, 0x40, 0xC0, 0x40, 0x41, 0x63, 0x3E, /* 'C' 0x43 */
- 0xF9, 0x0A, 0x1C, 0x18, 0x30, 0x61, 0xC2, 0xF8, /* 'D' 0x44 */
- 0xFE, 0x08, 0x20, 0xFE, 0x08, 0x20, 0xFC, /* 'E' 0x45 */
- 0xFE, 0x08, 0x20, 0xFA, 0x08, 0x20, 0x80, /* 'F' 0x46 */
- 0x1E, 0x61, 0x40, 0x40, 0xC7, 0x41, 0x41, 0x63, 0x1D, /* 'G' 0x47 */
- 0x83, 0x06, 0x0C, 0x1F, 0xF0, 0x60, 0xC1, 0x82, /* 'H' 0x48 */
- 0xFF, 0x80, /* 'I' 0x49 */
- 0x08, 0x42, 0x10, 0x87, 0x29, 0x70, /* 'J' 0x4A */
- 0x85, 0x12, 0x45, 0x0D, 0x13, 0x22, 0x42, 0x86, /* 'K' 0x4B */
- 0x84, 0x21, 0x08, 0x42, 0x10, 0xF8, /* 'L' 0x4C */
- 0xC3, 0xC3, 0xC3, 0xA5, 0xA5, 0xA5, 0x99, 0x99, 0x99, /* 'M' 0x4D */
- 0x83, 0x86, 0x8D, 0x19, 0x33, 0x62, 0xC3, 0x86, /* 'N' 0x4E */
- 0x1E, 0x31, 0x90, 0x68, 0x1C, 0x0A, 0x05, 0x06, 0xC6, 0x1E, 0x00, /* 'O' 0x4F */
- 0xFA, 0x18, 0x61, 0xFA, 0x08, 0x20, 0x80, /* 'P' 0x50 */
- 0x1E, 0x31, 0x90, 0x68, 0x1C, 0x0A, 0x05, 0x16, 0xC6, 0x1F, 0x00, 0x40, /* 'Q' 0x51 */
- 0xFD, 0x0E, 0x1C, 0x2F, 0x90, 0xA1, 0x42, 0x86, /* 'R' 0x52 */
- 0x7A, 0x18, 0x30, 0x78, 0x38, 0x61, 0x78, /* 'S' 0x53 */
- 0xFE, 0x20, 0x40, 0x81, 0x02, 0x04, 0x08, 0x10, /* 'T' 0x54 */
- 0x83, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xE2, 0x78, /* 'U' 0x55 */
- 0xC2, 0x85, 0x0B, 0x22, 0x44, 0x8E, 0x0C, 0x18, /* 'V' 0x56 */
- 0xC4, 0x28, 0xCD, 0x29, 0x25, 0x24, 0xA4, 0x52, 0x8C, 0x61, 0x8C, 0x31, 0x80, /* 'W' 0x57 */
- 0x87, 0x34, 0x8C, 0x30, 0xC4, 0xA3, 0x84, /* 'X' 0x58 */
- 0xC3, 0x42, 0x24, 0x34, 0x18, 0x08, 0x08, 0x08, 0x08, /* 'Y' 0x59 */
- 0x7E, 0x0C, 0x30, 0x41, 0x06, 0x18, 0x20, 0xFE, /* 'Z' 0x5A */
- 0xEA, 0xAA, 0xAB, /* '[' 0x5B */
- 0x92, 0x24, 0x89, 0x20, /* '\' 0x5C */
- 0xD5, 0x55, 0x57, /* ']' 0x5D */
- 0x46, 0xA9, /* '^' 0x5E */
- 0xFE, /* '_' 0x5F */
- 0x80, /* '`' 0x60 */
- 0x79, 0x20, 0x4F, 0xC6, 0x37, 0x40, /* 'a' 0x61 */
- 0x84, 0x3D, 0x18, 0xC6, 0x31, 0xF0, /* 'b' 0x62 */
- 0x39, 0x3C, 0x20, 0xC1, 0x33, 0x80, /* 'c' 0x63 */
- 0x04, 0x13, 0xD3, 0xC6, 0x1C, 0x53, 0x3C, /* 'd' 0x64 */
- 0x39, 0x38, 0x7F, 0x81, 0x13, 0x80, /* 'e' 0x65 */
- 0x6B, 0xA4, 0x92, 0x40, /* 'f' 0x66 */
- 0x35, 0x3C, 0x61, 0xC5, 0x33, 0x41, 0x4D, 0xE0, /* 'g' 0x67 */
- 0x84, 0x3D, 0x38, 0xC6, 0x31, 0x88, /* 'h' 0x68 */
- 0xBF, 0x80, /* 'i' 0x69 */
- 0x45, 0x55, 0x57, /* 'j' 0x6A */
- 0x84, 0x25, 0x4E, 0x52, 0xD2, 0x88, /* 'k' 0x6B */
- 0xFF, 0x80, /* 'l' 0x6C */
- 0xF7, 0x99, 0x91, 0x91, 0x91, 0x91, 0x91, /* 'm' 0x6D */
- 0xF4, 0x63, 0x18, 0xC6, 0x20, /* 'n' 0x6E */
- 0x39, 0x3C, 0x61, 0xC5, 0x33, 0x80, /* 'o' 0x6F */
- 0xF4, 0x63, 0x18, 0xC7, 0xD0, 0x80, /* 'p' 0x70 */
- 0x3D, 0x3C, 0x61, 0xC5, 0x37, 0x41, 0x04, /* 'q' 0x71 */
- 0xF2, 0x49, 0x20, /* 'r' 0x72 */
- 0x7A, 0x50, 0xE0, 0xE5, 0xE0, /* 's' 0x73 */
- 0x5D, 0x24, 0x93, /* 't' 0x74 */
- 0x8C, 0x63, 0x18, 0xCF, 0xA0, /* 'u' 0x75 */
- 0x85, 0x24, 0x92, 0x30, 0xC3, 0x00, /* 'v' 0x76 */
- 0x89, 0x59, 0x59, 0x55, 0x56, 0x26, 0x26, /* 'w' 0x77 */
- 0x4A, 0x4C, 0x43, 0x27, 0x20, /* 'x' 0x78 */
- 0x8A, 0x52, 0xA5, 0x18, 0x84, 0x22, 0x00, /* 'y' 0x79 */
- 0x78, 0x44, 0x46, 0x23, 0xE0, /* 'z' 0x7A */
- 0x6A, 0xAA, 0xA9, /* '{' 0x7B */
- 0xFF, 0xE0, /* '|' 0x7C */
- 0x95, 0x55, 0x56, /* '}' 0x7D */
- 0x66, 0x60, /* '~' 0x7E */
- 0xFF, 0xC0, 0x67, 0x34, 0x58, 0x4C, 0x46, 0x03, 0x11, 0x80, 0xFF, 0xC0, /* 0x7F */
- 0xFC, 0x08, 0x04, 0x02, 0x01, 0xF0, 0x8C, 0x46, 0x23, 0x11, 0x80, 0xC0, 0xC0, /* 0x80 */
- 0x10, 0x8F, 0xE0, 0x82, 0x08, 0x20, 0x82, 0x00, /* 0x81 */
- 0xE0, /* 0x82 */
- 0x24, 0x0F, 0x88, 0x88, 0x80, /* 0x83 */
- 0xB6, 0x80, /* 0x84 */
- 0xA8, /* 0x85 */
- 0x21, 0x09, 0xF2, 0x10, 0x84, 0x21, 0x08, /* 0x86 */
- 0x21, 0x09, 0xF2, 0x10, 0x84, 0xF9, 0x08, /* 0x87 */
- 0x1C, 0x45, 0x07, 0xE4, 0x1F, 0x10, 0x10, 0x1E, /* 0x88 */
- 0x62, 0x09, 0x40, 0x98, 0x06, 0x80, 0x10, 0x01, 0x66, 0x29, 0x92, 0x99, 0x06, 0x60, /* 0x89 */
- 0x7C, 0x08, 0x81, 0x10, 0x22, 0x04, 0x7C, 0x88, 0x51, 0x0A, 0x21, 0x87, 0xC0, /* 0x8A */
- 0x64, /* 0x8B */
- 0x84, 0x10, 0x82, 0x10, 0x42, 0x0F, 0xFD, 0x08, 0xA1, 0x0C, 0x23, 0x87, 0xC0, /* 0x8C */
- 0x10, 0x88, 0xE6, 0xB3, 0x8C, 0x28, 0x92, 0x28, 0xC0, /* 0x8D */
- 0xFC, 0x08, 0x04, 0x02, 0x01, 0xF0, 0x8C, 0x46, 0x23, 0x11, 0x80, /* 0x8E */
- 0x83, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC1, 0xFE, 0x20, 0x40, /* 0x8F */
- 0x43, 0xC4, 0x1F, 0x45, 0x14, 0x51, 0x44, 0x11, 0x80, /* 0x90 */
- 0xE0, /* 0x91 */
- 0xE0, /* 0x92 */
- 0xB6, 0x80, /* 0x93 */
- 0xB6, 0x80, /* 0x94 */
- 0xFF, 0x80, /* 0x95 */
- 0xFC, /* 0x96 */
- 0xFF, 0xF0, /* 0x97 */
- /* 0x98 */
- 0xE6, 0x28, 0xCD, 0x19, 0xA3, 0x34, 0x6A, 0x8B, 0x51, 0x68, /* 0x99 */
- 0x78, 0x24, 0x13, 0xC9, 0x14, 0x8E, 0x7C, /* 0x9A */
- 0x98, /* 0x9B */
- 0x88, 0x44, 0x3F, 0xD1, 0x38, 0x8C, 0x78, /* 0x9C */
- 0x24, 0x09, 0xAC, 0xCA, 0x90, /* 0x9D */
- 0x43, 0xC4, 0x1F, 0x45, 0x14, 0x51, 0x44, /* 0x9E */
- 0x8C, 0x63, 0x18, 0xFC, 0x80, /* 0x9F */
- /* 0xA0 */
- 0x24, 0x33, 0x0A, 0x36, 0x45, 0x8E, 0x0C, 0x10, 0x60, 0x80, /* 0xA1 */
- 0x51, 0x22, 0x95, 0xA8, 0xC4, 0x23, 0x10, /* 0xA2 */
- 0x08, 0x42, 0x10, 0x86, 0x31, 0x78, /* 0xA3 */
- 0xFC, 0x63, 0xF0, /* 0xA4 */
- 0x07, 0xF8, 0x20, 0x82, 0x08, 0x20, 0x82, 0x00, /* 0xA5 */
- 0xF9, 0xF0, /* 0xA6 */
- 0x32, 0x91, 0xC9, 0x47, 0x26, 0x14, 0xA4, 0xC0, /* 0xA7 */
- 0x28, 0x0F, 0xE0, 0x82, 0x0F, 0xE0, 0x82, 0x0F, 0xC0, /* 0xA8 */
- 0x3E, 0x3F, 0xB8, 0xF4, 0x1A, 0x0D, 0x17, 0x76, 0xC6, 0x3E, 0x00, /* 0xA9 */
- 0x38, 0x8A, 0x0C, 0x0F, 0x90, 0x20, 0xE3, 0x7C, /* 0xAA */
- 0x5A, 0xA5, /* 0xAB */
- 0x51, 0x55, 0x56, /* 0xAC */
- /* 0xAD */
- 0x3E, 0x31, 0xB7, 0x72, 0x99, 0xCC, 0xC7, 0x56, 0xC6, 0x3E, 0x00, /* 0xAE */
- 0xA1, 0x24, 0x92, 0x49, 0x00, /* 0xAF */
- 0x69, 0x96, /* 0xB0 */
- 0x21, 0x3E, 0x42, 0x03, 0xE0, /* 0xB1 */
- 0xFF, 0x80, /* 0xB2 */
- 0xDF, 0x80, /* 0xB3 */
- 0x27, 0xC9, 0x24, /* 0xB4 */
- 0x8A, 0x28, 0xA2, 0x8A, 0x6E, 0xE0, 0x80, /* 0xB5 */
- 0x7F, 0xAE, 0xBA, 0x68, 0xA2, 0x8A, 0x28, 0xA0, /* 0xB6 */
- 0x80, /* 0xB7 */
- 0x28, 0xA0, 0x1E, 0x47, 0xFC, 0x11, 0x78, /* 0xB8 */
- 0x88, 0x44, 0x32, 0x59, 0xDA, 0xCD, 0x66, 0x6B, 0x32, 0x8B, 0x80, /* 0xB9 */
- 0x79, 0x1F, 0x30, 0x45, 0xE0, /* 0xBA */
- 0xA5, 0x5A, /* 0xBB */
- 0x45, 0x55, 0x57, /* 0xBC */
- 0x7A, 0x18, 0x70, 0x78, 0x38, 0x61, 0x7C, /* 0xBD */
- 0x7A, 0x1C, 0x1C, 0xBC, /* 0xBE */
- 0xB4, 0x24, 0x92, 0x40, /* 0xBF */
- 0x18, 0x18, 0x3C, 0x24, 0x24, 0x7E, 0x42, 0x42, 0xC3, /* 0xC0 */
- 0xFE, 0x08, 0x20, 0xFA, 0x18, 0x61, 0xF8, /* 0xC1 */
- 0xFA, 0x18, 0x61, 0xFA, 0x18, 0x61, 0xFC, /* 0xC2 */
- 0xFE, 0x08, 0x20, 0x82, 0x08, 0x20, 0x80, /* 0xC3 */
- 0x1F, 0x08, 0x84, 0x42, 0x21, 0x10, 0x88, 0x44, 0x42, 0xFF, 0xC0, 0x60, 0x20, /* 0xC4 */
- 0xFE, 0x08, 0x20, 0xFE, 0x08, 0x20, 0xFC, /* 0xC5 */
- 0x88, 0xA4, 0x9A, 0x87, 0xC1, 0xC1, 0xF1, 0xAD, 0x92, 0x88, 0x80, /* 0xC6 */
- 0x7A, 0x18, 0x41, 0x38, 0x18, 0x61, 0x7C, /* 0xC7 */
- 0x87, 0x0E, 0x2C, 0x59, 0x34, 0x68, 0xE1, 0xC2, /* 0xC8 */
- 0x28, 0x22, 0x1C, 0x38, 0xB1, 0x64, 0xD1, 0xA3, 0x87, 0x08, /* 0xC9 */
- 0x8E, 0x6B, 0x38, 0xC2, 0x89, 0x22, 0x8C, /* 0xCA */
- 0x3E, 0x44, 0x89, 0x12, 0x24, 0x58, 0xA1, 0xC2, /* 0xCB */
- 0xC3, 0xC3, 0xC3, 0xA5, 0xA5, 0xA5, 0x99, 0x99, 0x99, /* 0xCC */
- 0x83, 0x06, 0x0C, 0x1F, 0xF0, 0x60, 0xC1, 0x82, /* 0xCD */
- 0x3C, 0x42, 0x81, 0x81, 0x81, 0x81, 0x81, 0xC2, 0x7C, /* 0xCE */
- 0xFF, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC1, 0x82, /* 0xCF */
- 0xFA, 0x18, 0x61, 0xFE, 0x08, 0x20, 0x80, /* 0xD0 */
- 0x38, 0x8A, 0x0C, 0x08, 0x10, 0x20, 0xE3, 0x7C, /* 0xD1 */
- 0xFE, 0x20, 0x40, 0x81, 0x02, 0x04, 0x08, 0x10, /* 0xD2 */
- 0xC2, 0x8D, 0x91, 0x63, 0x83, 0x04, 0x18, 0x20, /* 0xD3 */
- 0x08, 0x1F, 0x32, 0x71, 0x18, 0x8C, 0x47, 0x26, 0xFE, 0x08, 0x00, /* 0xD4 */
- 0x87, 0x34, 0x8C, 0x30, 0xC4, 0xB3, 0x84, /* 0xD5 */
- 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0xFF, 0x01, 0x01, /* 0xD6 */
- 0x8E, 0x38, 0xE3, 0x8D, 0xF0, 0xC3, 0x0C, /* 0xD7 */
- 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0xFF, /* 0xD8 */
- 0x99, 0x4C, 0xA6, 0x53, 0x29, 0x94, 0xCA, 0x65, 0x32, 0xFF, 0x80, 0x40, 0x20, /* 0xD9 */
- 0xF0, 0x04, 0x01, 0x00, 0x40, 0x1F, 0x84, 0x21, 0x0C, 0x42, 0x1F, 0x00, /* 0xDA */
- 0x81, 0xC0, 0xE0, 0x70, 0x3F, 0xDC, 0x2E, 0x17, 0x0B, 0xF9, 0x80, /* 0xDB */
- 0x82, 0x08, 0x20, 0xFE, 0x18, 0x61, 0xF8, /* 0xDC */
- 0x79, 0x8A, 0x18, 0x13, 0xE0, 0x60, 0xC2, 0x7C, /* 0xDD */
- 0x87, 0x26, 0x39, 0x06, 0x41, 0xF0, 0x64, 0x19, 0x06, 0x63, 0x8F, 0x80, /* 0xDE */
- 0x7E, 0x18, 0x61, 0x7C, 0xD6, 0x71, 0x84, /* 0xDF */
- 0x79, 0x11, 0xD9, 0xCD, 0xD0, /* 0xE0 */
- 0x0D, 0xC4, 0x1E, 0x47, 0x1C, 0x51, 0x78, /* 0xE1 */
- 0xF4, 0xBD, 0x29, 0xF8, /* 0xE2 */
- 0xF8, 0x88, 0x88, /* 0xE3 */
- 0x3C, 0x48, 0x91, 0x22, 0x5F, 0xE0, 0x80, /* 0xE4 */
- 0x79, 0x1F, 0xF0, 0x45, 0xE0, /* 0xE5 */
- 0x92, 0x54, 0x38, 0x3C, 0x56, 0x93, /* 0xE6 */
- 0x78, 0x23, 0x82, 0xCD, 0xE0, /* 0xE7 */
- 0x9C, 0xEB, 0x5C, 0xC4, /* 0xE8 */
- 0x70, 0x27, 0x3A, 0xD7, 0x31, /* 0xE9 */
- 0x9A, 0xCC, 0xA9, /* 0xEA */
- 0x7A, 0x52, 0x94, 0xE4, /* 0xEB */
- 0x8F, 0x3D, 0x6D, 0xA6, 0x90, /* 0xEC */
- 0x8C, 0x7F, 0x18, 0xC4, /* 0xED */
- 0x79, 0x1C, 0x71, 0x45, 0xE0, /* 0xEE */
- 0xFC, 0x63, 0x18, 0xC4, /* 0xEF */
- 0xFC, 0x63, 0x18, 0xFA, 0x10, 0x80, /* 0xF0 */
- 0x79, 0x1C, 0x30, 0x45, 0xE0, /* 0xF1 */
- 0xF9, 0x08, 0x42, 0x10, /* 0xF2 */
- 0x8A, 0x56, 0xA3, 0x10, 0x8C, 0x40, /* 0xF3 */
- 0x04, 0x01, 0x07, 0xF9, 0x31, 0xC4, 0x71, 0x14, 0xC5, 0xFE, 0x04, 0x01, 0x00, 0x40, /* 0xF4 */
- 0x4B, 0x8C, 0x65, 0xE4, /* 0xF5 */
- 0x8A, 0x28, 0xA2, 0x8B, 0xF0, 0x40, /* 0xF6 */
- 0x99, 0x97, 0x11, /* 0xF7 */
- 0x96, 0x59, 0x65, 0x97, 0xF0, /* 0xF8 */
- 0x95, 0x2A, 0x54, 0xA9, 0x5F, 0xC0, 0x80, /* 0xF9 */
- 0xF0, 0x20, 0x78, 0x91, 0x23, 0xC0, /* 0xFA */
- 0x86, 0x1F, 0x63, 0x8F, 0xD0, /* 0xFB */
- 0x84, 0x3D, 0x18, 0xF8, /* 0xFC */
- 0xF4, 0xDE, 0x19, 0xF8, /* 0xFD */
- 0x9E, 0xA2, 0xE1, 0xA1, 0xA2, 0x9E, /* 0xFE */
- 0xFC, 0x7E, 0xD4, 0xC4, /* 0xFF */
+/* 0x01 */ 0x1C, 0x0A, 0x05, 0x04, 0xFE, 0x08, 0x1C, 0x02, 0x07, 0xE0, 0x9F, 0xC0,
+/* 0x02 */ 0x3F, 0xF0, 0x40, 0xE0, 0x10, 0x3F, 0x04, 0x9E, 0x28, 0x14, 0x0E, 0x00,
+/* 0x03 */ 0x3F, 0x10, 0x28, 0x06, 0x49, 0x80, 0x60, 0x19, 0x26, 0x31, 0x40, 0x8F, 0xC0,
+/* 0x04 */ 0x3F, 0x10, 0x2A, 0x16, 0x49, 0xA1, 0x60, 0x19, 0xE6, 0x31, 0x40, 0x8F, 0xC0,
+/* 0x05 */ 0x28, 0x15, 0x2A, 0xB5, 0x55, 0xA8, 0x54, 0x12, 0x04, 0x41, 0x08, 0x81, 0xC0,
+/* 0x06 */ 0x04, 0x08, 0x88, 0x82, 0x07, 0x01, 0x11, 0xA2, 0xC4, 0x40, 0x70, 0x20, 0x88, 0x88, 0x10, 0x00,
+/* 0x07 */
+/* 0x08 */ 0x03, 0x83, 0x44, 0x48, 0x28, 0x01, 0x80, 0x17, 0xFE, 0x08, 0x45, 0x28, 0x84, 0x00,
+/* 0x09 */ 0x01, 0xC0, 0x68, 0x82, 0x41, 0x10, 0x02, 0x80, 0x06, 0x00, 0x14, 0x00, 0x8F, 0xFC,
+/* 0x0A */
+/* 0x0B */ 0x22, 0x2A, 0xA2, 0x30, 0x18, 0x0A, 0x09, 0x04, 0x44, 0x14, 0x04, 0x00,
+/* 0x0C */ 0x46, 0x00, 0x19, 0x03, 0x21, 0x20, 0x93, 0x04, 0x20, 0x11, 0x80, 0x50, 0x02, 0x7F, 0xE0,
+/* 0x0D */
+/* 0x0E */ 0x08, 0x0E, 0x08, 0x88, 0x24, 0x12, 0x09, 0x05, 0x01, 0xFF, 0x8A, 0x02, 0x00,
+/* 0x0F */ 0x3F, 0x14, 0xAA, 0x16, 0x01, 0x92, 0x60, 0x18, 0xC6, 0x49, 0x40, 0x8F, 0xC0,
+/* 0x10 */ 0x1B, 0x02, 0xA0, 0x54, 0x12, 0x42, 0x48, 0x49, 0x31, 0x1E, 0x23, 0xEA, 0xFE, 0x3C,
+/* 0x11 */ 0x3F, 0x02, 0x00, 0x20, 0x6D, 0x27, 0xF8, 0x3F, 0xC1, 0xFE, 0x37, 0xD0, 0xBE, 0x40, 0xE1, 0xE2, 0x00,
+/* 0x12 */ 0x12, 0x42, 0x20, 0x24, 0xC0, 0x29, 0x99, 0x05, 0x23, 0x30, 0xB0, 0x30, 0x00,
+/* 0x13 */ 0x3F, 0x88, 0x0A, 0x44, 0xD5, 0x58, 0x03, 0x00, 0x67, 0xCC, 0x71, 0x40, 0x47, 0xF0,
+/* 0x14 */ 0x3F, 0x18, 0x69, 0x26, 0x85, 0xA1, 0x6C, 0xD8, 0x06, 0x31, 0x40, 0x8F, 0xC0,
+/* 0x15 */ 0x3F, 0x11, 0x00, 0xE8, 0x03, 0xA0, 0x1F, 0xB3, 0x7E, 0x00, 0xE9, 0xE0, 0x23, 0x00, 0x40, 0x40, 0xFE, 0x00,
+/* 0x16 */ 0x30, 0x38, 0x3A, 0x3E, 0x6E, 0xEB, 0xC3, 0xC3, 0x66, 0x3C,
+/* 0x17 */ 0x3F, 0x04, 0x00, 0x82, 0x88, 0x5C, 0xA4, 0x49, 0x22, 0x81, 0x98, 0xC4, 0x40, 0xA3, 0xF0,
+/* 0x18 */ 0x07, 0x80, 0x42, 0x04, 0x08, 0x21, 0x41, 0x42, 0x60, 0x0E, 0x8C, 0xB2, 0x89, 0x50, 0x52, 0x82, 0x80,
+/* 0x19 */ 0x3F, 0xC4, 0x02, 0x80, 0x18, 0x01, 0xB3, 0x1B, 0xB9, 0x80, 0x19, 0xE1, 0x40, 0x23, 0xFC,
+/* 0x1A */ 0xFF, 0xC0, 0x67, 0x34, 0x58, 0x4C, 0x46, 0x03, 0x11, 0x80, 0xFF, 0xC0,
+/* 0x1B */ 0x0F, 0xC0, 0x40, 0x82, 0x49, 0x08, 0x04, 0x00, 0x00, 0x12, 0x02, 0x31, 0x34, 0x0B, 0x88, 0x45, 0x00, 0x20,
+/* 0x1C */ 0x3F, 0x88, 0x0A, 0x44, 0xC9, 0x19, 0x3B, 0x00, 0x60, 0x4C, 0x71, 0x40, 0x47, 0xF0,
+/* 0x1D */ 0x3F, 0x8B, 0x0A, 0x00, 0xC8, 0x18, 0x13, 0x00, 0x48, 0xCA, 0xC1, 0x44, 0x53, 0x30,
+/* 0x1E */ 0x19, 0xC2, 0x02, 0x50, 0x1E, 0x49, 0x80, 0x12, 0x01, 0x27, 0x92, 0x01, 0x10, 0x20, 0xFC,
+/* 0x1F */ 0x30, 0x1C, 0x0C, 0x3E, 0x7E, 0xCF, 0x07, 0xC7, 0x7F, 0x3F,
+/* ' ' 0x20 */
+/* '!' 0x21 */ 0xFC, 0x80,
+/* '"' 0x22 */ 0xB6, 0x80,
+/* '#' 0x23 */ 0x24, 0x51, 0xF9, 0x42, 0x9F, 0x92, 0x28,
+/* '$' 0x24 */ 0x10, 0xE5, 0x55, 0x50, 0xE1, 0x65, 0x55, 0xE1, 0x00,
+/* '%' 0x25 */ 0x71, 0x24, 0x89, 0x22, 0x50, 0x74, 0x02, 0x70, 0xA4, 0x49, 0x11, 0xC0,
+/* '&' 0x26 */ 0x71, 0x24, 0x9C, 0x62, 0x58, 0xA7, 0xF4,
+/* ''' 0x27 */ 0xE0,
+/* '(' 0x28 */ 0x5A, 0xAA, 0x94,
+/* ')' 0x29 */ 0x89, 0x12, 0x49, 0x29, 0x00,
+/* '*' 0x2A */ 0x5E, 0x80,
+/* '+' 0x2B */ 0x21, 0x3E, 0x42, 0x00,
+/* ',' 0x2C */ 0xE0,
+/* '-' 0x2D */ 0xC0,
+/* '.' 0x2E */ 0x80,
+/* '/' 0x2F */ 0x24, 0xA4, 0xA4, 0x80,
+/* '0' 0x30 */ 0x76, 0xE3, 0x18, 0xC6, 0x3B, 0x70,
+/* '1' 0x31 */ 0x27, 0x92, 0x49, 0x20,
+/* '2' 0x32 */ 0x79, 0x10, 0x41, 0x08, 0xC6, 0x10, 0xFC,
+/* '3' 0x33 */ 0x79, 0x30, 0x43, 0x18, 0x10, 0x71, 0x78,
+/* '4' 0x34 */ 0x08, 0x61, 0x8A, 0x49, 0x2F, 0xC2, 0x08,
+/* '5' 0x35 */ 0xFC, 0x21, 0xE8, 0x84, 0x31, 0xF0,
+/* '6' 0x36 */ 0x74, 0x61, 0xE8, 0xC6, 0x31, 0x70,
+/* '7' 0x37 */ 0xF8, 0x44, 0x22, 0x11, 0x08, 0x40,
+/* '8' 0x38 */ 0x39, 0x34, 0x53, 0x39, 0x1C, 0x51, 0x38,
+/* '9' 0x39 */ 0x39, 0x3C, 0x71, 0x4C, 0xF0, 0x53, 0x78,
+/* ':' 0x3A */ 0x82,
+/* ';' 0x3B */ 0x87,
+/* '<' 0x3C */ 0x3E, 0x30, 0x60, 0x80,
+/* '=' 0x3D */ 0xF8, 0x3E,
+/* '>' 0x3E */ 0xE0, 0xC6, 0xC8, 0x00,
+/* '?' 0x3F */ 0x74, 0x42, 0x11, 0x10, 0x80, 0x20,
+/* '@' 0x40 */ 0x0F, 0x86, 0x19, 0x9A, 0xA4, 0xD9, 0x13, 0x22, 0x56, 0xDA, 0x6E, 0x60, 0x06, 0x00, 0x3C, 0x00,
+/* 'A' 0x41 */ 0x18, 0x18, 0x24, 0x24, 0x24, 0x7E, 0x42, 0x42, 0xC3,
+/* 'B' 0x42 */ 0xFA, 0x18, 0x61, 0xFA, 0x18, 0x61, 0xFC,
+/* 'C' 0x43 */ 0x3E, 0x63, 0x40, 0x40, 0xC0, 0x40, 0x41, 0x63, 0x3E,
+/* 'D' 0x44 */ 0xF9, 0x0A, 0x1C, 0x18, 0x30, 0x61, 0xC2, 0xF8,
+/* 'E' 0x45 */ 0xFE, 0x08, 0x20, 0xFE, 0x08, 0x20, 0xFC,
+/* 'F' 0x46 */ 0xFE, 0x08, 0x20, 0xFA, 0x08, 0x20, 0x80,
+/* 'G' 0x47 */ 0x1E, 0x61, 0x40, 0x40, 0xC7, 0x41, 0x41, 0x63, 0x1D,
+/* 'H' 0x48 */ 0x83, 0x06, 0x0C, 0x1F, 0xF0, 0x60, 0xC1, 0x82,
+/* 'I' 0x49 */ 0xFF, 0x80,
+/* 'J' 0x4A */ 0x08, 0x42, 0x10, 0x87, 0x29, 0x70,
+/* 'K' 0x4B */ 0x85, 0x12, 0x45, 0x0D, 0x13, 0x22, 0x42, 0x86,
+/* 'L' 0x4C */ 0x84, 0x21, 0x08, 0x42, 0x10, 0xF8,
+/* 'M' 0x4D */ 0xC3, 0xC3, 0xC3, 0xA5, 0xA5, 0xA5, 0x99, 0x99, 0x99,
+/* 'N' 0x4E */ 0x83, 0x86, 0x8D, 0x19, 0x33, 0x62, 0xC3, 0x86,
+/* 'O' 0x4F */ 0x1E, 0x31, 0x90, 0x68, 0x1C, 0x0A, 0x05, 0x06, 0xC6, 0x1E, 0x00,
+/* 'P' 0x50 */ 0xFA, 0x18, 0x61, 0xFA, 0x08, 0x20, 0x80,
+/* 'Q' 0x51 */ 0x1E, 0x31, 0x90, 0x68, 0x1C, 0x0A, 0x05, 0x16, 0xC6, 0x1F, 0x00, 0x40,
+/* 'R' 0x52 */ 0xFD, 0x0E, 0x1C, 0x2F, 0x90, 0xA1, 0x42, 0x86,
+/* 'S' 0x53 */ 0x7A, 0x18, 0x30, 0x78, 0x38, 0x61, 0x78,
+/* 'T' 0x54 */ 0xFE, 0x20, 0x40, 0x81, 0x02, 0x04, 0x08, 0x10,
+/* 'U' 0x55 */ 0x83, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xE2, 0x78,
+/* 'V' 0x56 */ 0xC2, 0x85, 0x0B, 0x22, 0x44, 0x8E, 0x0C, 0x18,
+/* 'W' 0x57 */ 0xC4, 0x28, 0xCD, 0x29, 0x25, 0x24, 0xA4, 0x52, 0x8C, 0x61, 0x8C, 0x31, 0x80,
+/* 'X' 0x58 */ 0x87, 0x34, 0x8C, 0x30, 0xC4, 0xA3, 0x84,
+/* 'Y' 0x59 */ 0xC3, 0x42, 0x24, 0x34, 0x18, 0x08, 0x08, 0x08, 0x08,
+/* 'Z' 0x5A */ 0x7E, 0x0C, 0x30, 0x41, 0x06, 0x18, 0x20, 0xFE,
+/* '[' 0x5B */ 0xEA, 0xAA, 0xAB,
+/* '\' 0x5C */ 0x92, 0x24, 0x89, 0x20,
+/* ']' 0x5D */ 0xD5, 0x55, 0x57,
+/* '^' 0x5E */ 0x46, 0xA9,
+/* '_' 0x5F */ 0xFE,
+/* '`' 0x60 */ 0x80,
+/* 'a' 0x61 */ 0x79, 0x20, 0x4F, 0xC6, 0x37, 0x40,
+/* 'b' 0x62 */ 0x84, 0x3D, 0x18, 0xC6, 0x31, 0xF0,
+/* 'c' 0x63 */ 0x39, 0x3C, 0x20, 0xC1, 0x33, 0x80,
+/* 'd' 0x64 */ 0x04, 0x13, 0xD3, 0xC6, 0x1C, 0x53, 0x3C,
+/* 'e' 0x65 */ 0x39, 0x38, 0x7F, 0x81, 0x13, 0x80,
+/* 'f' 0x66 */ 0x6B, 0xA4, 0x92, 0x40,
+/* 'g' 0x67 */ 0x35, 0x3C, 0x61, 0xC5, 0x33, 0x41, 0x4D, 0xE0,
+/* 'h' 0x68 */ 0x84, 0x3D, 0x38, 0xC6, 0x31, 0x88,
+/* 'i' 0x69 */ 0xBF, 0x80,
+/* 'j' 0x6A */ 0x45, 0x55, 0x57,
+/* 'k' 0x6B */ 0x84, 0x25, 0x4E, 0x52, 0xD2, 0x88,
+/* 'l' 0x6C */ 0xFF, 0x80,
+/* 'm' 0x6D */ 0xF7, 0x99, 0x91, 0x91, 0x91, 0x91, 0x91,
+/* 'n' 0x6E */ 0xF4, 0x63, 0x18, 0xC6, 0x20,
+/* 'o' 0x6F */ 0x39, 0x3C, 0x61, 0xC5, 0x33, 0x80,
+/* 'p' 0x70 */ 0xF4, 0x63, 0x18, 0xC7, 0xD0, 0x80,
+/* 'q' 0x71 */ 0x3D, 0x3C, 0x61, 0xC5, 0x37, 0x41, 0x04,
+/* 'r' 0x72 */ 0xF2, 0x49, 0x20,
+/* 's' 0x73 */ 0x7A, 0x50, 0xE0, 0xE5, 0xE0,
+/* 't' 0x74 */ 0x5D, 0x24, 0x93,
+/* 'u' 0x75 */ 0x8C, 0x63, 0x18, 0xCF, 0xA0,
+/* 'v' 0x76 */ 0x85, 0x24, 0x92, 0x30, 0xC3, 0x00,
+/* 'w' 0x77 */ 0x89, 0x59, 0x59, 0x55, 0x56, 0x26, 0x26,
+/* 'x' 0x78 */ 0x4A, 0x4C, 0x43, 0x27, 0x20,
+/* 'y' 0x79 */ 0x8A, 0x52, 0xA5, 0x18, 0x84, 0x22, 0x00,
+/* 'z' 0x7A */ 0x78, 0x44, 0x46, 0x23, 0xE0,
+/* '{' 0x7B */ 0x6A, 0xAA, 0xA9,
+/* '|' 0x7C */ 0xFF, 0xE0,
+/* '}' 0x7D */ 0x95, 0x55, 0x56,
+/* '~' 0x7E */ 0x66, 0x60,
+/* 0x7F */
+/* 0x80 */ 0xFC, 0x08, 0x04, 0x02, 0x01, 0xF0, 0x8C, 0x46, 0x23, 0x11, 0x80, 0xC0, 0xC0,
+/* 0x81 */ 0x10, 0x8F, 0xE0, 0x82, 0x08, 0x20, 0x82, 0x00,
+/* 0x82 */ 0xE0,
+/* 0x83 */ 0x24, 0x0F, 0x88, 0x88, 0x80,
+/* 0x84 */ 0xB6, 0x80,
+/* 0x85 */ 0xA8,
+/* 0x86 */ 0x21, 0x09, 0xF2, 0x10, 0x84, 0x21, 0x08,
+/* 0x87 */ 0x21, 0x09, 0xF2, 0x10, 0x84, 0xF9, 0x08,
+/* 0x88 */ 0x1C, 0x45, 0x07, 0xE4, 0x1F, 0x10, 0x10, 0x1E,
+/* 0x89 */ 0x62, 0x09, 0x40, 0x98, 0x06, 0x80, 0x10, 0x01, 0x66, 0x29, 0x92, 0x99, 0x06, 0x60,
+/* 0x8A */ 0x7C, 0x08, 0x81, 0x10, 0x22, 0x04, 0x7C, 0x88, 0x51, 0x0A, 0x21, 0x87, 0xC0,
+/* 0x8B */ 0x64,
+/* 0x8C */ 0x84, 0x10, 0x82, 0x10, 0x42, 0x0F, 0xFD, 0x08, 0xA1, 0x0C, 0x23, 0x87, 0xC0,
+/* 0x8D */ 0x10, 0x88, 0xE6, 0xB3, 0x8C, 0x28, 0x92, 0x28, 0xC0,
+/* 0x8E */ 0xFC, 0x08, 0x04, 0x02, 0x01, 0xF0, 0x8C, 0x46, 0x23, 0x11, 0x80,
+/* 0x8F */ 0x83, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC1, 0xFE, 0x20, 0x40,
+/* 0x90 */ 0x43, 0xC4, 0x1F, 0x45, 0x14, 0x51, 0x44, 0x11, 0x80,
+/* 0x91 */ 0xE0,
+/* 0x92 */ 0xE0,
+/* 0x93 */ 0xB6, 0x80,
+/* 0x94 */ 0xB6, 0x80,
+/* 0x95 */ 0xFF, 0x80,
+/* 0x96 */ 0xFC,
+/* 0x97 */ 0xFF, 0xF0,
+/* 0x98 */
+/* 0x99 */ 0xE6, 0x28, 0xCD, 0x19, 0xA3, 0x34, 0x6A, 0x8B, 0x51, 0x68,
+/* 0x9A */ 0x78, 0x24, 0x13, 0xC9, 0x14, 0x8E, 0x7C,
+/* 0x9B */ 0x98,
+/* 0x9C */ 0x88, 0x44, 0x3F, 0xD1, 0x38, 0x8C, 0x78,
+/* 0x9D */ 0x24, 0x09, 0xAC, 0xCA, 0x90,
+/* 0x9E */ 0x43, 0xC4, 0x1F, 0x45, 0x14, 0x51, 0x44,
+/* 0x9F */ 0x8C, 0x63, 0x18, 0xFC, 0x80,
+/* 0xA0 */
+/* 0xA1 */ 0x24, 0x33, 0x0A, 0x36, 0x45, 0x8E, 0x0C, 0x10, 0x60, 0x80,
+/* 0xA2 */ 0x51, 0x22, 0x95, 0xA8, 0xC4, 0x23, 0x10,
+/* 0xA3 */ 0x08, 0x42, 0x10, 0x86, 0x31, 0x78,
+/* 0xA4 */ 0xFC, 0x63, 0xF0,
+/* 0xA5 */ 0x07, 0xF8, 0x20, 0x82, 0x08, 0x20, 0x82, 0x00,
+/* 0xA6 */ 0xF9, 0xF0,
+/* 0xA7 */ 0x32, 0x91, 0xC9, 0x47, 0x26, 0x14, 0xA4, 0xC0,
+/* 0xA8 */ 0x28, 0x0F, 0xE0, 0x82, 0x0F, 0xE0, 0x82, 0x0F, 0xC0,
+/* 0xA9 */ 0x3E, 0x3F, 0xB8, 0xF4, 0x1A, 0x0D, 0x17, 0x76, 0xC6, 0x3E, 0x00,
+/* 0xAA */ 0x38, 0x8A, 0x0C, 0x0F, 0x90, 0x20, 0xE3, 0x7C,
+/* 0xAB */ 0x5A, 0xA5,
+/* 0xAC */ 0x51, 0x55, 0x56,
+/* 0xAD */
+/* 0xAE */ 0x3E, 0x31, 0xB7, 0x72, 0x99, 0xCC, 0xC7, 0x56, 0xC6, 0x3E, 0x00,
+/* 0xAF */ 0xA1, 0x24, 0x92, 0x49, 0x00,
+/* 0xB0 */ 0x69, 0x96,
+/* 0xB1 */ 0x21, 0x3E, 0x42, 0x03, 0xE0,
+/* 0xB2 */ 0xFF, 0x80,
+/* 0xB3 */ 0xDF, 0x80,
+/* 0xB4 */ 0x27, 0xC9, 0x24,
+/* 0xB5 */ 0x8A, 0x28, 0xA2, 0x8A, 0x6E, 0xE0, 0x80,
+/* 0xB6 */ 0x7F, 0xAE, 0xBA, 0x68, 0xA2, 0x8A, 0x28, 0xA0,
+/* 0xB7 */ 0x80,
+/* 0xB8 */ 0x28, 0xA0, 0x1E, 0x47, 0xFC, 0x11, 0x78,
+/* 0xB9 */ 0x88, 0x44, 0x32, 0x59, 0xDA, 0xCD, 0x66, 0x6B, 0x32, 0x8B, 0x80,
+/* 0xBA */ 0x79, 0x1F, 0x30, 0x45, 0xE0,
+/* 0xBB */ 0xA5, 0x5A,
+/* 0xBC */ 0x45, 0x55, 0x57,
+/* 0xBD */ 0x7A, 0x18, 0x70, 0x78, 0x38, 0x61, 0x7C,
+/* 0xBE */ 0x7A, 0x1C, 0x1C, 0xBC,
+/* 0xBF */ 0xB4, 0x24, 0x92, 0x40,
+/* 0xC0 */ 0x18, 0x18, 0x3C, 0x24, 0x24, 0x7E, 0x42, 0x42, 0xC3,
+/* 0xC1 */ 0xFE, 0x08, 0x20, 0xFA, 0x18, 0x61, 0xF8,
+/* 0xC2 */ 0xFA, 0x18, 0x61, 0xFA, 0x18, 0x61, 0xFC,
+/* 0xC3 */ 0xFE, 0x08, 0x20, 0x82, 0x08, 0x20, 0x80,
+/* 0xC4 */ 0x1F, 0x08, 0x84, 0x42, 0x21, 0x10, 0x88, 0x44, 0x42, 0xFF, 0xC0, 0x60, 0x20,
+/* 0xC5 */ 0xFE, 0x08, 0x20, 0xFE, 0x08, 0x20, 0xFC,
+/* 0xC6 */ 0x88, 0xA4, 0x9A, 0x87, 0xC1, 0xC1, 0xF1, 0xAD, 0x92, 0x88, 0x80,
+/* 0xC7 */ 0x7A, 0x18, 0x41, 0x38, 0x18, 0x61, 0x7C,
+/* 0xC8 */ 0x87, 0x0E, 0x2C, 0x59, 0x34, 0x68, 0xE1, 0xC2,
+/* 0xC9 */ 0x28, 0x22, 0x1C, 0x38, 0xB1, 0x64, 0xD1, 0xA3, 0x87, 0x08,
+/* 0xCA */ 0x8E, 0x6B, 0x38, 0xC2, 0x89, 0x22, 0x8C,
+/* 0xCB */ 0x3E, 0x44, 0x89, 0x12, 0x24, 0x58, 0xA1, 0xC2,
+/* 0xCC */ 0xC3, 0xC3, 0xC3, 0xA5, 0xA5, 0xA5, 0x99, 0x99, 0x99,
+/* 0xCD */ 0x83, 0x06, 0x0C, 0x1F, 0xF0, 0x60, 0xC1, 0x82,
+/* 0xCE */ 0x3C, 0x42, 0x81, 0x81, 0x81, 0x81, 0x81, 0xC2, 0x7C,
+/* 0xCF */ 0xFF, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC1, 0x82,
+/* 0xD0 */ 0xFA, 0x18, 0x61, 0xFE, 0x08, 0x20, 0x80,
+/* 0xD1 */ 0x38, 0x8A, 0x0C, 0x08, 0x10, 0x20, 0xE3, 0x7C,
+/* 0xD2 */ 0xFE, 0x20, 0x40, 0x81, 0x02, 0x04, 0x08, 0x10,
+/* 0xD3 */ 0xC2, 0x8D, 0x91, 0x63, 0x83, 0x04, 0x18, 0x20,
+/* 0xD4 */ 0x08, 0x1F, 0x32, 0x71, 0x18, 0x8C, 0x47, 0x26, 0xFE, 0x08, 0x00,
+/* 0xD5 */ 0x87, 0x34, 0x8C, 0x30, 0xC4, 0xB3, 0x84,
+/* 0xD6 */ 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0xFF, 0x01, 0x01,
+/* 0xD7 */ 0x8E, 0x38, 0xE3, 0x8D, 0xF0, 0xC3, 0x0C,
+/* 0xD8 */ 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0xFF,
+/* 0xD9 */ 0x99, 0x4C, 0xA6, 0x53, 0x29, 0x94, 0xCA, 0x65, 0x32, 0xFF, 0x80, 0x40, 0x20,
+/* 0xDA */ 0xF0, 0x04, 0x01, 0x00, 0x40, 0x1F, 0x84, 0x21, 0x0C, 0x42, 0x1F, 0x00,
+/* 0xDB */ 0x81, 0xC0, 0xE0, 0x70, 0x3F, 0xDC, 0x2E, 0x17, 0x0B, 0xF9, 0x80,
+/* 0xDC */ 0x82, 0x08, 0x20, 0xFE, 0x18, 0x61, 0xF8,
+/* 0xDD */ 0x79, 0x8A, 0x18, 0x13, 0xE0, 0x60, 0xC2, 0x7C,
+/* 0xDE */ 0x87, 0x26, 0x39, 0x06, 0x41, 0xF0, 0x64, 0x19, 0x06, 0x63, 0x8F, 0x80,
+/* 0xDF */ 0x7E, 0x18, 0x61, 0x7C, 0xD6, 0x71, 0x84,
+/* 0xE0 */ 0x79, 0x11, 0xD9, 0xCD, 0xD0,
+/* 0xE1 */ 0x0D, 0xC4, 0x1E, 0x47, 0x1C, 0x51, 0x78,
+/* 0xE2 */ 0xF4, 0xBD, 0x29, 0xF8,
+/* 0xE3 */ 0xF8, 0x88, 0x88,
+/* 0xE4 */ 0x3C, 0x48, 0x91, 0x22, 0x5F, 0xE0, 0x80,
+/* 0xE5 */ 0x79, 0x1F, 0xF0, 0x45, 0xE0,
+/* 0xE6 */ 0x92, 0x54, 0x38, 0x3C, 0x56, 0x93,
+/* 0xE7 */ 0x78, 0x23, 0x82, 0xCD, 0xE0,
+/* 0xE8 */ 0x9C, 0xEB, 0x5C, 0xC4,
+/* 0xE9 */ 0x70, 0x27, 0x3A, 0xD7, 0x31,
+/* 0xEA */ 0x9A, 0xCC, 0xA9,
+/* 0xEB */ 0x7A, 0x52, 0x94, 0xE4,
+/* 0xEC */ 0x8F, 0x3D, 0x6D, 0xA6, 0x90,
+/* 0xED */ 0x8C, 0x7F, 0x18, 0xC4,
+/* 0xEE */ 0x79, 0x1C, 0x71, 0x45, 0xE0,
+/* 0xEF */ 0xFC, 0x63, 0x18, 0xC4,
+/* 0xF0 */ 0xFC, 0x63, 0x18, 0xFA, 0x10, 0x80,
+/* 0xF1 */ 0x79, 0x1C, 0x30, 0x45, 0xE0,
+/* 0xF2 */ 0xF9, 0x08, 0x42, 0x10,
+/* 0xF3 */ 0x8A, 0x56, 0xA3, 0x10, 0x8C, 0x40,
+/* 0xF4 */ 0x04, 0x01, 0x07, 0xF9, 0x31, 0xC4, 0x71, 0x14, 0xC5, 0xFE, 0x04, 0x01, 0x00, 0x40,
+/* 0xF5 */ 0x4B, 0x8C, 0x65, 0xE4,
+/* 0xF6 */ 0x8A, 0x28, 0xA2, 0x8B, 0xF0, 0x40,
+/* 0xF7 */ 0x99, 0x97, 0x11,
+/* 0xF8 */ 0x96, 0x59, 0x65, 0x97, 0xF0,
+/* 0xF9 */ 0x95, 0x2A, 0x54, 0xA9, 0x5F, 0xC0, 0x80,
+/* 0xFA */ 0xF0, 0x20, 0x78, 0x91, 0x23, 0xC0,
+/* 0xFB */ 0x86, 0x1F, 0x63, 0x8F, 0xD0,
+/* 0xFC */ 0x84, 0x3D, 0x18, 0xF8,
+/* 0xFD */ 0xF4, 0xDE, 0x19, 0xF8,
+/* 0xFE */ 0x9E, 0xA2, 0xE1, 0xA1, 0xA2, 0x9E,
+/* 0xFF */ 0xFC, 0x7E, 0xD4, 0xC4,
};
const GFXglyph FreeSans6pt_Win1251Glyphs[] PROGMEM = {
- /* ' ' 0x20 */ {0, 0, 0, 3, 0, 0},
- /* '!' 0x21 */ {0, 1, 9, 4, 2, -8},
- /* '"' 0x22 */ {2, 3, 3, 4, 0, -8},
- /* '#' 0x23 */ {4, 7, 8, 7, 0, -7},
- /* '$' 0x24 */ {11, 6, 11, 7, 0, -9},
- /* '%' 0x25 */ {20, 10, 9, 11, 0, -8},
- /* '&' 0x26 */ {32, 6, 9, 8, 1, -8},
- /* ''' 0x27 */ {39, 1, 3, 2, 1, -8},
- /* '(' 0x28 */ {40, 2, 11, 4, 1, -8},
- /* ')' 0x29 */ {43, 3, 11, 4, 0, -8},
- /* '*' 0x2A */ {48, 3, 3, 5, 1, -8},
- /* '+' 0x2B */ {50, 5, 5, 7, 1, -4},
- /* ',' 0x2C */ {54, 1, 3, 3, 1, 0},
- /* '-' 0x2D */ {55, 2, 1, 4, 1, -3},
- /* '.' 0x2E */ {56, 1, 1, 3, 1, 0},
- /* '/' 0x2F */ {57, 3, 9, 3, 0, -8},
- /* '0' 0x30 */ {61, 5, 9, 7, 1, -8},
- /* '1' 0x31 */ {67, 3, 9, 7, 1, -8},
- /* '2' 0x32 */ {71, 6, 9, 7, 0, -8},
- /* '3' 0x33 */ {78, 6, 9, 7, 0, -8},
- /* '4' 0x34 */ {85, 6, 9, 7, 0, -8},
- /* '5' 0x35 */ {92, 5, 9, 7, 1, -8},
- /* '6' 0x36 */ {98, 5, 9, 7, 1, -8},
- /* '7' 0x37 */ {104, 5, 9, 7, 1, -8},
- /* '8' 0x38 */ {110, 6, 9, 7, 0, -8},
- /* '9' 0x39 */ {117, 6, 9, 7, 0, -8},
- /* ':' 0x3A */ {124, 1, 7, 3, 1, -6},
- /* ';' 0x3B */ {125, 1, 8, 3, 1, -5},
- /* '<' 0x3C */ {126, 5, 5, 7, 1, -4},
- /* '=' 0x3D */ {130, 5, 3, 7, 1, -3},
- /* '>' 0x3E */ {132, 5, 5, 7, 1, -4},
- /* '?' 0x3F */ {136, 5, 9, 7, 1, -8},
- /* '@' 0x40 */ {142, 11, 11, 12, 0, -8},
- /* 'A' 0x41 */ {158, 8, 9, 8, 0, -8},
- /* 'B' 0x42 */ {167, 6, 9, 8, 1, -8},
- /* 'C' 0x43 */ {174, 8, 9, 9, 0, -8},
- /* 'D' 0x44 */ {183, 7, 9, 8, 1, -8},
- /* 'E' 0x45 */ {191, 6, 9, 8, 1, -8},
- /* 'F' 0x46 */ {198, 6, 9, 7, 1, -8},
- /* 'G' 0x47 */ {205, 8, 9, 9, 0, -8},
- /* 'H' 0x48 */ {214, 7, 9, 9, 1, -8},
- /* 'I' 0x49 */ {222, 1, 9, 3, 1, -8},
- /* 'J' 0x4A */ {224, 5, 9, 6, 0, -8},
- /* 'K' 0x4B */ {230, 7, 9, 8, 1, -8},
- /* 'L' 0x4C */ {238, 5, 9, 7, 1, -8},
- /* 'M' 0x4D */ {244, 8, 9, 10, 1, -8},
- /* 'N' 0x4E */ {253, 7, 9, 9, 1, -8},
- /* 'O' 0x4F */ {261, 9, 9, 9, 0, -8},
- /* 'P' 0x50 */ {272, 6, 9, 8, 1, -8},
- /* 'Q' 0x51 */ {279, 9, 10, 9, 0, -8},
- /* 'R' 0x52 */ {291, 7, 9, 9, 1, -8},
- /* 'S' 0x53 */ {299, 6, 9, 8, 1, -8},
- /* 'T' 0x54 */ {306, 7, 9, 8, 0, -8},
- /* 'U' 0x55 */ {314, 7, 9, 9, 1, -8},
- /* 'V' 0x56 */ {322, 7, 9, 8, 0, -8},
- /* 'W' 0x57 */ {330, 11, 9, 11, 0, -8},
- /* 'X' 0x58 */ {343, 6, 9, 8, 1, -8},
- /* 'Y' 0x59 */ {350, 8, 9, 8, 0, -8},
- /* 'Z' 0x5A */ {359, 7, 9, 7, 0, -8},
- /* '[' 0x5B */ {367, 2, 12, 3, 1, -8},
- /* '\' 0x5C */ {370, 3, 9, 3, 0, -8},
- /* ']' 0x5D */ {374, 2, 12, 3, 0, -8},
- /* '^' 0x5E */ {377, 4, 4, 6, 1, -8},
- /* '_' 0x5F */ {379, 7, 1, 7, 0, 2},
- /* '`' 0x60 */ {380, 1, 1, 3, 1, -8},
- /* 'a' 0x61 */ {381, 6, 7, 7, 0, -6},
- /* 'b' 0x62 */ {387, 5, 9, 7, 1, -8},
- /* 'c' 0x63 */ {393, 6, 7, 6, 0, -6},
- /* 'd' 0x64 */ {399, 6, 9, 7, 0, -8},
- /* 'e' 0x65 */ {406, 6, 7, 6, 0, -6},
- /* 'f' 0x66 */ {412, 3, 9, 3, 0, -8},
- /* 'g' 0x67 */ {416, 6, 10, 7, 0, -6},
- /* 'h' 0x68 */ {424, 5, 9, 6, 1, -8},
- /* 'i' 0x69 */ {430, 1, 9, 3, 1, -8},
- /* 'j' 0x6A */ {432, 2, 12, 3, 0, -8},
- /* 'k' 0x6B */ {435, 5, 9, 6, 1, -8},
- /* 'l' 0x6C */ {441, 1, 9, 3, 1, -8},
- /* 'm' 0x6D */ {443, 8, 7, 10, 1, -6},
- /* 'n' 0x6E */ {450, 5, 7, 6, 1, -6},
- /* 'o' 0x6F */ {455, 6, 7, 6, 0, -6},
- /* 'p' 0x70 */ {461, 5, 9, 7, 1, -6},
- /* 'q' 0x71 */ {467, 6, 9, 7, 0, -6},
- /* 'r' 0x72 */ {474, 3, 7, 4, 1, -6},
- /* 's' 0x73 */ {477, 5, 7, 6, 0, -6},
- /* 't' 0x74 */ {482, 3, 8, 3, 0, -7},
- /* 'u' 0x75 */ {485, 5, 7, 6, 1, -6},
- /* 'v' 0x76 */ {490, 6, 7, 6, 0, -6},
- /* 'w' 0x77 */ {496, 8, 7, 9, 0, -6},
- /* 'x' 0x78 */ {503, 5, 7, 6, 0, -6},
- /* 'y' 0x79 */ {508, 5, 10, 6, 0, -6},
- /* 'z' 0x7A */ {515, 5, 7, 6, 0, -6},
- /* '{' 0x7B */ {520, 2, 12, 4, 1, -8},
- /* '|' 0x7C */ {523, 1, 11, 3, 1, -8},
- /* '}' 0x7D */ {525, 2, 12, 4, 1, -8},
- /* '~' 0x7E */ {528, 6, 2, 6, 0, -4},
- /* 0x7F */ {530, 9, 10, 11, 1, -8},
- /* 0x80 */ {542, 9, 11, 9, 0, -8},
- /* 0x81 */ {555, 6, 10, 7, 1, -9},
- /* 0x82 */ {563, 1, 3, 3, 1, 0},
- /* 0x83 */ {564, 4, 9, 5, 1, -8},
- /* 0x84 */ {569, 3, 3, 5, 1, 0},
- /* 0x85 */ {571, 5, 1, 7, 1, 0},
- /* 0x86 */ {572, 5, 11, 7, 1, -8},
- /* 0x87 */ {579, 5, 11, 7, 1, -8},
- /* 0x88 */ {586, 7, 9, 8, 0, -8},
- /* 0x89 */ {594, 12, 9, 12, 0, -8},
- /* 0x8A */ {608, 11, 9, 13, 1, -8},
- /* 0x8B */ {621, 2, 3, 4, 1, -4},
- /* 0x8C */ {622, 11, 9, 12, 1, -8},
- /* 0x8D */ {635, 6, 11, 8, 1, -10},
- /* 0x8E */ {644, 9, 9, 9, 0, -8},
- /* 0x8F */ {655, 7, 11, 9, 1, -8},
- /* 0x90 */ {665, 6, 11, 7, 0, -8},
- /* 0x91 */ {674, 1, 3, 3, 1, -8},
- /* 0x92 */ {675, 1, 3, 2, 1, -8},
- /* 0x93 */ {676, 3, 3, 5, 1, -8},
- /* 0x94 */ {678, 3, 3, 5, 1, -8},
- /* 0x95 */ {680, 3, 3, 5, 1, -5},
- /* 0x96 */ {682, 6, 1, 6, 0, -3},
- /* 0x97 */ {683, 12, 1, 12, 0, -3},
- /* 0x98 */ {685, 0, 0, 8, 0, 0},
- /* 0x99 */ {685, 11, 7, 12, 1, -8},
- /* 0x9A */ {695, 9, 6, 10, 0, -5},
- /* 0x9B */ {702, 2, 3, 3, 1, -4},
- /* 0x9C */ {703, 9, 6, 10, 1, -5},
- /* 0x9D */ {710, 4, 9, 6, 1, -8},
- /* 0x9E */ {715, 6, 9, 7, 0, -8},
- /* 0x9F */ {722, 5, 7, 7, 1, -5},
- /* 0xA0 */ {727, 0, 0, 3, 0, 0},
- /* 0xA1 */ {727, 7, 11, 7, 0, -10},
- /* 0xA2 */ {737, 5, 11, 6, 0, -7},
- /* 0xA3 */ {744, 5, 9, 6, 0, -8},
- /* 0xA4 */ {750, 5, 4, 7, 1, -5},
- /* 0xA5 */ {753, 6, 10, 7, 1, -9},
- /* 0xA6 */ {761, 1, 12, 3, 1, -8},
- /* 0xA7 */ {763, 5, 12, 7, 1, -8},
- /* 0xA8 */ {771, 6, 11, 8, 1, -10},
- /* 0xA9 */ {780, 9, 9, 10, 0, -8},
- /* 0xAA */ {791, 7, 9, 9, 1, -8},
- /* 0xAB */ {799, 4, 4, 6, 1, -4},
- /* 0xAC */ {801, 2, 12, 3, 0, -8},
- /* 0xAD */ {804, 0, 0, 0, 0, 0},
- /* 0xAE */ {804, 9, 9, 10, 0, -8},
- /* 0xAF */ {815, 3, 11, 3, 0, -10},
- /* 0xB0 */ {820, 4, 4, 7, 2, -8},
- /* 0xB1 */ {822, 5, 7, 7, 1, -6},
- /* 0xB2 */ {827, 1, 9, 3, 1, -8},
- /* 0xB3 */ {829, 1, 9, 3, 1, -8},
- /* 0xB4 */ {831, 3, 8, 5, 1, -7},
- /* 0xB5 */ {834, 6, 9, 7, 1, -6},
- /* 0xB6 */ {841, 6, 10, 6, 1, -8},
- /* 0xB7 */ {849, 1, 1, 3, 1, -2},
- /* 0xB8 */ {850, 6, 9, 7, 0, -8},
- /* 0xB9 */ {857, 9, 9, 11, 1, -8},
- /* 0xBA */ {868, 6, 6, 6, 0, -5},
- /* 0xBB */ {873, 4, 4, 6, 1, -5},
- /* 0xBC */ {875, 2, 12, 3, 0, -8},
- /* 0xBD */ {878, 6, 9, 8, 1, -8},
- /* 0xBE */ {885, 5, 6, 6, 0, -5},
- /* 0xBF */ {889, 3, 9, 3, 0, -8},
- /* 0xC0 */ {893, 8, 9, 8, 0, -8},
- /* 0xC1 */ {902, 6, 9, 8, 1, -8},
- /* 0xC2 */ {909, 6, 9, 8, 1, -8},
- /* 0xC3 */ {916, 6, 9, 7, 1, -8},
- /* 0xC4 */ {923, 9, 11, 10, 0, -8},
- /* 0xC5 */ {936, 6, 9, 8, 1, -8},
- /* 0xC6 */ {943, 9, 9, 11, 1, -8},
- /* 0xC7 */ {954, 6, 9, 8, 1, -8},
- /* 0xC8 */ {961, 7, 9, 9, 1, -8},
- /* 0xC9 */ {969, 7, 11, 9, 1, -10},
- /* 0xCA */ {979, 6, 9, 8, 1, -8},
- /* 0xCB */ {986, 7, 9, 8, 0, -8},
- /* 0xCC */ {994, 8, 9, 10, 1, -8},
- /* 0xCD */ {1003, 7, 9, 9, 1, -8},
- /* 0xCE */ {1011, 8, 9, 10, 1, -8},
- /* 0xCF */ {1020, 7, 9, 9, 1, -8},
- /* 0xD0 */ {1028, 6, 9, 8, 1, -8},
- /* 0xD1 */ {1035, 7, 9, 9, 1, -8},
- /* 0xD2 */ {1043, 7, 9, 7, 0, -8},
- /* 0xD3 */ {1051, 7, 9, 7, 0, -8},
- /* 0xD4 */ {1059, 9, 9, 10, 1, -8},
- /* 0xD5 */ {1070, 6, 9, 8, 1, -8},
- /* 0xD6 */ {1077, 8, 11, 9, 1, -8},
- /* 0xD7 */ {1088, 6, 9, 8, 1, -8},
- /* 0xD8 */ {1095, 8, 9, 10, 1, -8},
- /* 0xD9 */ {1104, 9, 11, 10, 1, -8},
- /* 0xDA */ {1117, 10, 9, 10, 0, -8},
- /* 0xDB */ {1129, 9, 9, 10, 1, -8},
- /* 0xDC */ {1140, 6, 9, 8, 1, -8},
- /* 0xDD */ {1147, 7, 9, 9, 1, -8},
- /* 0xDE */ {1155, 10, 9, 12, 1, -8},
- /* 0xDF */ {1167, 6, 9, 8, 1, -8},
- /* 0xE0 */ {1174, 6, 6, 7, 0, -5},
- /* 0xE1 */ {1179, 6, 9, 7, 0, -8},
- /* 0xE2 */ {1186, 5, 6, 6, 1, -5},
- /* 0xE3 */ {1190, 4, 6, 5, 1, -5},
- /* 0xE4 */ {1193, 7, 7, 7, 0, -5},
- /* 0xE5 */ {1200, 6, 6, 7, 0, -5},
- /* 0xE6 */ {1205, 8, 6, 9, 1, -5},
- /* 0xE7 */ {1211, 6, 6, 6, 0, -5},
- /* 0xE8 */ {1216, 5, 6, 7, 1, -5},
- /* 0xE9 */ {1220, 5, 8, 7, 1, -7},
- /* 0xEA */ {1225, 4, 6, 6, 1, -5},
- /* 0xEB */ {1228, 5, 6, 6, 0, -5},
- /* 0xEC */ {1232, 6, 6, 7, 1, -5},
- /* 0xED */ {1237, 5, 6, 7, 1, -5},
- /* 0xEE */ {1241, 6, 6, 7, 0, -5},
- /* 0xEF */ {1246, 5, 6, 7, 1, -5},
- /* 0xF0 */ {1250, 5, 9, 7, 1, -5},
- /* 0xF1 */ {1256, 6, 6, 6, 0, -5},
- /* 0xF2 */ {1261, 5, 6, 5, 0, -5},
- /* 0xF3 */ {1265, 5, 9, 6, 0, -5},
- /* 0xF4 */ {1271, 10, 11, 10, 0, -7},
- /* 0xF5 */ {1285, 5, 6, 6, 0, -5},
- /* 0xF6 */ {1289, 6, 7, 7, 1, -5},
- /* 0xF7 */ {1295, 4, 6, 6, 1, -5},
- /* 0xF8 */ {1298, 6, 6, 8, 1, -5},
- /* 0xF9 */ {1303, 7, 7, 9, 1, -5},
- /* 0xFA */ {1310, 7, 6, 8, 0, -5},
- /* 0xFB */ {1316, 6, 6, 8, 1, -5},
- /* 0xFC */ {1321, 5, 6, 6, 1, -5},
- /* 0xFD */ {1325, 5, 6, 6, 1, -5},
- /* 0xFE */ {1329, 8, 6, 9, 1, -5},
- /* 0xFF */ {1335, 5, 6, 7, 1, -5},
+/* 0x01 */ { 0, 9, 10, 11, 1, -9 },
+/* 0x02 */ { 12, 9, 10, 11, 1, -8 },
+/* 0x03 */ { 24, 10, 10, 12, 1, -8 },
+/* 0x04 */ { 37, 10, 10, 12, 1, -8 },
+/* 0x05 */ { 50, 10, 10, 12, 1, -9 },
+/* 0x06 */ { 63, 11, 11, 13, 1, -9 },
+/* 0x07 */ { 79, 0, 0, 8, 0, 0 },
+/* 0x08 */ { 79, 12, 9, 14, 1, -8 },
+/* 0x09 */ { 93, 14, 8, 16, 1, -7 },
+/* 0x0A */ { 107, 0, 0, 8, 0, 0 },
+/* 0x0B */ { 107, 9, 10, 11, 1, -9 },
+/* 0x0C */ { 119, 13, 9, 15, 1, -8 },
+/* 0x0D */ { 134, 0, 0, 8, 0, 0 },
+/* 0x0E */ { 134, 9, 11, 11, 1, -9 },
+/* 0x0F */ { 147, 10, 10, 12, 1, -9 },
+/* 0x10 */ { 160, 11, 10, 13, 1, -9 },
+/* 0x11 */ { 174, 13, 10, 15, 1, -9 },
+/* 0x12 */ { 191, 10, 10, 12, 1, -9 },
+/* 0x13 */ { 204, 11, 10, 13, 1, -9 },
+/* 0x14 */ { 218, 10, 10, 12, 1, -9 },
+/* 0x15 */ { 231, 14, 10, 16, 1, -9 },
+/* 0x16 */ { 249, 8, 10, 10, 1, -9 },
+/* 0x17 */ { 259, 12, 10, 14, 1, -9 },
+/* 0x18 */ { 274, 13, 10, 15, 1, -9 },
+/* 0x19 */ { 291, 12, 10, 14, 1, -9 },
+/* 0x1A */ { 306, 9, 10, 11, 1, -8 },
+/* 0x1B */ { 318, 14, 10, 16, 1, -9 },
+/* 0x1C */ { 336, 11, 10, 13, 1, -9 },
+/* 0x1D */ { 350, 11, 10, 13, 1, -9 },
+/* 0x1E */ { 364, 12, 10, 14, 1, -9 },
+/* 0x1F */ { 379, 8, 10, 11, 2, -9 },
+/* ' ' 0x20 */ { 389, 0, 0, 3, 0, 0 },
+/* '!' 0x21 */ { 389, 1, 9, 4, 2, -8 },
+/* '"' 0x22 */ { 391, 3, 3, 4, 0, -8 },
+/* '#' 0x23 */ { 393, 7, 8, 7, 0, -7 },
+/* '$' 0x24 */ { 400, 6, 11, 7, 0, -9 },
+/* '%' 0x25 */ { 409, 10, 9, 11, 0, -8 },
+/* '&' 0x26 */ { 421, 6, 9, 8, 1, -8 },
+/* ''' 0x27 */ { 428, 1, 3, 2, 1, -8 },
+/* '(' 0x28 */ { 429, 2, 11, 4, 1, -8 },
+/* ')' 0x29 */ { 432, 3, 11, 4, 0, -8 },
+/* '*' 0x2A */ { 437, 3, 3, 5, 1, -8 },
+/* '+' 0x2B */ { 439, 5, 5, 7, 1, -4 },
+/* ',' 0x2C */ { 443, 1, 3, 3, 1, 0 },
+/* '-' 0x2D */ { 444, 2, 1, 4, 1, -3 },
+/* '.' 0x2E */ { 445, 1, 1, 3, 1, 0 },
+/* '/' 0x2F */ { 446, 3, 9, 3, 0, -8 },
+/* '0' 0x30 */ { 450, 5, 9, 7, 1, -8 },
+/* '1' 0x31 */ { 456, 3, 9, 7, 1, -8 },
+/* '2' 0x32 */ { 460, 6, 9, 7, 0, -8 },
+/* '3' 0x33 */ { 467, 6, 9, 7, 0, -8 },
+/* '4' 0x34 */ { 474, 6, 9, 7, 0, -8 },
+/* '5' 0x35 */ { 481, 5, 9, 7, 1, -8 },
+/* '6' 0x36 */ { 487, 5, 9, 7, 1, -8 },
+/* '7' 0x37 */ { 493, 5, 9, 7, 1, -8 },
+/* '8' 0x38 */ { 499, 6, 9, 7, 0, -8 },
+/* '9' 0x39 */ { 506, 6, 9, 7, 0, -8 },
+/* ':' 0x3A */ { 513, 1, 7, 3, 1, -6 },
+/* ';' 0x3B */ { 514, 1, 8, 3, 1, -5 },
+/* '<' 0x3C */ { 515, 5, 5, 7, 1, -4 },
+/* '=' 0x3D */ { 519, 5, 3, 7, 1, -3 },
+/* '>' 0x3E */ { 521, 5, 5, 7, 1, -4 },
+/* '?' 0x3F */ { 525, 5, 9, 7, 1, -8 },
+/* '@' 0x40 */ { 531, 11, 11, 12, 0, -8 },
+/* 'A' 0x41 */ { 547, 8, 9, 8, 0, -8 },
+/* 'B' 0x42 */ { 556, 6, 9, 8, 1, -8 },
+/* 'C' 0x43 */ { 563, 8, 9, 9, 0, -8 },
+/* 'D' 0x44 */ { 572, 7, 9, 8, 1, -8 },
+/* 'E' 0x45 */ { 580, 6, 9, 8, 1, -8 },
+/* 'F' 0x46 */ { 587, 6, 9, 7, 1, -8 },
+/* 'G' 0x47 */ { 594, 8, 9, 9, 0, -8 },
+/* 'H' 0x48 */ { 603, 7, 9, 9, 1, -8 },
+/* 'I' 0x49 */ { 611, 1, 9, 3, 1, -8 },
+/* 'J' 0x4A */ { 613, 5, 9, 6, 0, -8 },
+/* 'K' 0x4B */ { 619, 7, 9, 8, 1, -8 },
+/* 'L' 0x4C */ { 627, 5, 9, 7, 1, -8 },
+/* 'M' 0x4D */ { 633, 8, 9, 10, 1, -8 },
+/* 'N' 0x4E */ { 642, 7, 9, 9, 1, -8 },
+/* 'O' 0x4F */ { 650, 9, 9, 9, 0, -8 },
+/* 'P' 0x50 */ { 661, 6, 9, 8, 1, -8 },
+/* 'Q' 0x51 */ { 668, 9, 10, 9, 0, -8 },
+/* 'R' 0x52 */ { 680, 7, 9, 9, 1, -8 },
+/* 'S' 0x53 */ { 688, 6, 9, 8, 1, -8 },
+/* 'T' 0x54 */ { 695, 7, 9, 8, 0, -8 },
+/* 'U' 0x55 */ { 703, 7, 9, 9, 1, -8 },
+/* 'V' 0x56 */ { 711, 7, 9, 8, 0, -8 },
+/* 'W' 0x57 */ { 719, 11, 9, 11, 0, -8 },
+/* 'X' 0x58 */ { 732, 6, 9, 8, 1, -8 },
+/* 'Y' 0x59 */ { 739, 8, 9, 8, 0, -8 },
+/* 'Z' 0x5A */ { 748, 7, 9, 7, 0, -8 },
+/* '[' 0x5B */ { 756, 2, 12, 3, 1, -8 },
+/* '\' 0x5C */ { 759, 3, 9, 3, 0, -8 },
+/* ']' 0x5D */ { 763, 2, 12, 3, 0, -8 },
+/* '^' 0x5E */ { 766, 4, 4, 6, 1, -8 },
+/* '_' 0x5F */ { 768, 7, 1, 7, 0, 2 },
+/* '`' 0x60 */ { 769, 1, 1, 3, 1, -8 },
+/* 'a' 0x61 */ { 770, 6, 7, 7, 0, -6 },
+/* 'b' 0x62 */ { 776, 5, 9, 7, 1, -8 },
+/* 'c' 0x63 */ { 782, 6, 7, 6, 0, -6 },
+/* 'd' 0x64 */ { 788, 6, 9, 7, 0, -8 },
+/* 'e' 0x65 */ { 795, 6, 7, 6, 0, -6 },
+/* 'f' 0x66 */ { 801, 3, 9, 3, 0, -8 },
+/* 'g' 0x67 */ { 805, 6, 10, 7, 0, -6 },
+/* 'h' 0x68 */ { 813, 5, 9, 6, 1, -8 },
+/* 'i' 0x69 */ { 819, 1, 9, 3, 1, -8 },
+/* 'j' 0x6A */ { 821, 2, 12, 3, 0, -8 },
+/* 'k' 0x6B */ { 824, 5, 9, 6, 1, -8 },
+/* 'l' 0x6C */ { 830, 1, 9, 3, 1, -8 },
+/* 'm' 0x6D */ { 832, 8, 7, 10, 1, -6 },
+/* 'n' 0x6E */ { 839, 5, 7, 6, 1, -6 },
+/* 'o' 0x6F */ { 844, 6, 7, 6, 0, -6 },
+/* 'p' 0x70 */ { 850, 5, 9, 7, 1, -6 },
+/* 'q' 0x71 */ { 856, 6, 9, 7, 0, -6 },
+/* 'r' 0x72 */ { 863, 3, 7, 4, 1, -6 },
+/* 's' 0x73 */ { 866, 5, 7, 6, 0, -6 },
+/* 't' 0x74 */ { 871, 3, 8, 3, 0, -7 },
+/* 'u' 0x75 */ { 874, 5, 7, 6, 1, -6 },
+/* 'v' 0x76 */ { 879, 6, 7, 6, 0, -6 },
+/* 'w' 0x77 */ { 885, 8, 7, 9, 0, -6 },
+/* 'x' 0x78 */ { 892, 5, 7, 6, 0, -6 },
+/* 'y' 0x79 */ { 897, 5, 10, 6, 0, -6 },
+/* 'z' 0x7A */ { 904, 5, 7, 6, 0, -6 },
+/* '{' 0x7B */ { 909, 2, 12, 4, 1, -8 },
+/* '|' 0x7C */ { 912, 1, 11, 3, 1, -8 },
+/* '}' 0x7D */ { 914, 2, 12, 4, 1, -8 },
+/* '~' 0x7E */ { 917, 6, 2, 6, 0, -4 },
+/* 0x7F */ { 919, 0, 0, 0, 0, 0 },
+/* 0x80 */ { 919, 9, 11, 9, 0, -8 },
+/* 0x81 */ { 932, 6, 10, 7, 1, -9 },
+/* 0x82 */ { 940, 1, 3, 3, 1, 0 },
+/* 0x83 */ { 941, 4, 9, 5, 1, -8 },
+/* 0x84 */ { 946, 3, 3, 5, 1, 0 },
+/* 0x85 */ { 948, 5, 1, 7, 1, 0 },
+/* 0x86 */ { 949, 5, 11, 7, 1, -8 },
+/* 0x87 */ { 956, 5, 11, 7, 1, -8 },
+/* 0x88 */ { 963, 7, 9, 8, 0, -8 },
+/* 0x89 */ { 971, 12, 9, 12, 0, -8 },
+/* 0x8A */ { 985, 11, 9, 13, 1, -8 },
+/* 0x8B */ { 998, 2, 3, 4, 1, -4 },
+/* 0x8C */ { 999, 11, 9, 12, 1, -8 },
+/* 0x8D */ { 1012, 6, 11, 8, 1, -10 },
+/* 0x8E */ { 1021, 9, 9, 9, 0, -8 },
+/* 0x8F */ { 1032, 7, 11, 9, 1, -8 },
+/* 0x90 */ { 1042, 6, 11, 7, 0, -8 },
+/* 0x91 */ { 1051, 1, 3, 3, 1, -8 },
+/* 0x92 */ { 1052, 1, 3, 2, 1, -8 },
+/* 0x93 */ { 1053, 3, 3, 5, 1, -8 },
+/* 0x94 */ { 1055, 3, 3, 5, 1, -8 },
+/* 0x95 */ { 1057, 3, 3, 5, 1, -5 },
+/* 0x96 */ { 1059, 6, 1, 6, 0, -3 },
+/* 0x97 */ { 1060, 12, 1, 12, 0, -3 },
+/* 0x98 */ { 1062, 0, 0, 8, 0, 0 },
+/* 0x99 */ { 1062, 11, 7, 12, 1, -8 },
+/* 0x9A */ { 1072, 9, 6, 10, 0, -5 },
+/* 0x9B */ { 1079, 2, 3, 3, 1, -4 },
+/* 0x9C */ { 1080, 9, 6, 10, 1, -5 },
+/* 0x9D */ { 1087, 4, 9, 6, 1, -8 },
+/* 0x9E */ { 1092, 6, 9, 7, 0, -8 },
+/* 0x9F */ { 1099, 5, 7, 7, 1, -5 },
+/* 0xA0 */ { 1104, 0, 0, 3, 0, 0 },
+/* 0xA1 */ { 1104, 7, 11, 7, 0, -10 },
+/* 0xA2 */ { 1114, 5, 11, 6, 0, -7 },
+/* 0xA3 */ { 1121, 5, 9, 6, 0, -8 },
+/* 0xA4 */ { 1127, 5, 4, 7, 1, -5 },
+/* 0xA5 */ { 1130, 6, 10, 7, 1, -9 },
+/* 0xA6 */ { 1138, 1, 12, 3, 1, -8 },
+/* 0xA7 */ { 1140, 5, 12, 7, 1, -8 },
+/* 0xA8 */ { 1148, 6, 11, 8, 1, -10 },
+/* 0xA9 */ { 1157, 9, 9, 10, 0, -8 },
+/* 0xAA */ { 1168, 7, 9, 9, 1, -8 },
+/* 0xAB */ { 1176, 4, 4, 6, 1, -4 },
+/* 0xAC */ { 1178, 2, 12, 3, 0, -8 },
+/* 0xAD */ { 1181, 0, 0, 0, 0, 0 },
+/* 0xAE */ { 1181, 9, 9, 10, 0, -8 },
+/* 0xAF */ { 1192, 3, 11, 3, 0, -10 },
+/* 0xB0 */ { 1197, 4, 4, 7, 2, -8 },
+/* 0xB1 */ { 1199, 5, 7, 7, 1, -6 },
+/* 0xB2 */ { 1204, 1, 9, 3, 1, -8 },
+/* 0xB3 */ { 1206, 1, 9, 3, 1, -8 },
+/* 0xB4 */ { 1208, 3, 8, 5, 1, -7 },
+/* 0xB5 */ { 1211, 6, 9, 7, 1, -6 },
+/* 0xB6 */ { 1218, 6, 10, 6, 1, -8 },
+/* 0xB7 */ { 1226, 1, 1, 3, 1, -2 },
+/* 0xB8 */ { 1227, 6, 9, 7, 0, -8 },
+/* 0xB9 */ { 1234, 9, 9, 11, 1, -8 },
+/* 0xBA */ { 1245, 6, 6, 6, 0, -5 },
+/* 0xBB */ { 1250, 4, 4, 6, 1, -5 },
+/* 0xBC */ { 1252, 2, 12, 3, 0, -8 },
+/* 0xBD */ { 1255, 6, 9, 8, 1, -8 },
+/* 0xBE */ { 1262, 5, 6, 6, 0, -5 },
+/* 0xBF */ { 1266, 3, 9, 3, 0, -8 },
+/* 0xC0 */ { 1270, 8, 9, 8, 0, -8 },
+/* 0xC1 */ { 1279, 6, 9, 8, 1, -8 },
+/* 0xC2 */ { 1286, 6, 9, 8, 1, -8 },
+/* 0xC3 */ { 1293, 6, 9, 7, 1, -8 },
+/* 0xC4 */ { 1300, 9, 11, 10, 0, -8 },
+/* 0xC5 */ { 1313, 6, 9, 8, 1, -8 },
+/* 0xC6 */ { 1320, 9, 9, 11, 1, -8 },
+/* 0xC7 */ { 1331, 6, 9, 8, 1, -8 },
+/* 0xC8 */ { 1338, 7, 9, 9, 1, -8 },
+/* 0xC9 */ { 1346, 7, 11, 9, 1, -10 },
+/* 0xCA */ { 1356, 6, 9, 8, 1, -8 },
+/* 0xCB */ { 1363, 7, 9, 8, 0, -8 },
+/* 0xCC */ { 1371, 8, 9, 10, 1, -8 },
+/* 0xCD */ { 1380, 7, 9, 9, 1, -8 },
+/* 0xCE */ { 1388, 8, 9, 10, 1, -8 },
+/* 0xCF */ { 1397, 7, 9, 9, 1, -8 },
+/* 0xD0 */ { 1405, 6, 9, 8, 1, -8 },
+/* 0xD1 */ { 1412, 7, 9, 9, 1, -8 },
+/* 0xD2 */ { 1420, 7, 9, 7, 0, -8 },
+/* 0xD3 */ { 1428, 7, 9, 7, 0, -8 },
+/* 0xD4 */ { 1436, 9, 9, 10, 1, -8 },
+/* 0xD5 */ { 1447, 6, 9, 8, 1, -8 },
+/* 0xD6 */ { 1454, 8, 11, 9, 1, -8 },
+/* 0xD7 */ { 1465, 6, 9, 8, 1, -8 },
+/* 0xD8 */ { 1472, 8, 9, 10, 1, -8 },
+/* 0xD9 */ { 1481, 9, 11, 10, 1, -8 },
+/* 0xDA */ { 1494, 10, 9, 10, 0, -8 },
+/* 0xDB */ { 1506, 9, 9, 10, 1, -8 },
+/* 0xDC */ { 1517, 6, 9, 8, 1, -8 },
+/* 0xDD */ { 1524, 7, 9, 9, 1, -8 },
+/* 0xDE */ { 1532, 10, 9, 12, 1, -8 },
+/* 0xDF */ { 1544, 6, 9, 8, 1, -8 },
+/* 0xE0 */ { 1551, 6, 6, 7, 0, -5 },
+/* 0xE1 */ { 1556, 6, 9, 7, 0, -8 },
+/* 0xE2 */ { 1563, 5, 6, 6, 1, -5 },
+/* 0xE3 */ { 1567, 4, 6, 5, 1, -5 },
+/* 0xE4 */ { 1570, 7, 7, 7, 0, -5 },
+/* 0xE5 */ { 1577, 6, 6, 7, 0, -5 },
+/* 0xE6 */ { 1582, 8, 6, 9, 1, -5 },
+/* 0xE7 */ { 1588, 6, 6, 6, 0, -5 },
+/* 0xE8 */ { 1593, 5, 6, 7, 1, -5 },
+/* 0xE9 */ { 1597, 5, 8, 7, 1, -7 },
+/* 0xEA */ { 1602, 4, 6, 6, 1, -5 },
+/* 0xEB */ { 1605, 5, 6, 6, 0, -5 },
+/* 0xEC */ { 1609, 6, 6, 7, 1, -5 },
+/* 0xED */ { 1614, 5, 6, 7, 1, -5 },
+/* 0xEE */ { 1618, 6, 6, 7, 0, -5 },
+/* 0xEF */ { 1623, 5, 6, 7, 1, -5 },
+/* 0xF0 */ { 1627, 5, 9, 7, 1, -5 },
+/* 0xF1 */ { 1633, 6, 6, 6, 0, -5 },
+/* 0xF2 */ { 1638, 5, 6, 5, 0, -5 },
+/* 0xF3 */ { 1642, 5, 9, 6, 0, -5 },
+/* 0xF4 */ { 1648, 10, 11, 10, 0, -7 },
+/* 0xF5 */ { 1662, 5, 6, 6, 0, -5 },
+/* 0xF6 */ { 1666, 6, 7, 7, 1, -5 },
+/* 0xF7 */ { 1672, 4, 6, 6, 1, -5 },
+/* 0xF8 */ { 1675, 6, 6, 8, 1, -5 },
+/* 0xF9 */ { 1680, 7, 7, 9, 1, -5 },
+/* 0xFA */ { 1687, 7, 6, 8, 0, -5 },
+/* 0xFB */ { 1693, 6, 6, 8, 1, -5 },
+/* 0xFC */ { 1698, 5, 6, 6, 1, -5 },
+/* 0xFD */ { 1702, 5, 6, 6, 1, -5 },
+/* 0xFE */ { 1706, 8, 6, 9, 1, -5 },
+/* 0xFF */ { 1712, 5, 6, 7, 1, -5 },
};
-const GFXfont FreeSans6pt_Win1251 PROGMEM = {(uint8_t *)FreeSans6pt_Win1251Bitmaps, (GFXglyph *)FreeSans6pt_Win1251Glyphs, 0x20,
- 0xFF, 14};
+const GFXfont FreeSans6pt_Win1251 PROGMEM = {
+(uint8_t*)FreeSans6pt_Win1251Bitmaps,
+(GFXglyph*)FreeSans6pt_Win1251Glyphs,
+0x01, 0xFF, 14
+};
diff --git a/src/graphics/niche/Fonts/FreeSans6pt_Win1252.h b/src/graphics/niche/Fonts/FreeSans6pt_Win1252.h
index 32f995270..b17be8756 100644
--- a/src/graphics/niche/Fonts/FreeSans6pt_Win1252.h
+++ b/src/graphics/niche/Fonts/FreeSans6pt_Win1252.h
@@ -1,457 +1,527 @@
+// trunk-ignore-all(clang-format)
#pragma once
+/* PROPERTIES
+
+FONT_NAME FreeSans6pt_Win1252
+*/
const uint8_t FreeSans6pt_Win1252Bitmaps[] PROGMEM = {
- /* ' ' 0x20 */
- 0xFC, 0x80, /* '!' 0x21 */
- 0xB6, 0x80, /* '"' 0x22 */
- 0x24, 0x51, 0xF9, 0x42, 0x9F, 0x92, 0x28, /* '#' 0x23 */
- 0x10, 0xE5, 0x55, 0x50, 0xE1, 0x65, 0x55, 0xE1, 0x00, /* '$' 0x24 */
- 0x71, 0x24, 0x89, 0x22, 0x50, 0x74, 0x02, 0x70, 0xA4, 0x49, 0x11, 0xC0, /* '%' 0x25 */
- 0x71, 0x24, 0x9C, 0x62, 0x58, 0xA7, 0xF4, /* '&' 0x26 */
- 0xE0, /* ''' 0x27 */
- 0x5A, 0xAA, 0x94, /* '(' 0x28 */
- 0x89, 0x12, 0x49, 0x29, 0x00, /* ')' 0x29 */
- 0x5E, 0x80, /* '*' 0x2A */
- 0x21, 0x3E, 0x42, 0x00, /* '+' 0x2B */
- 0xE0, /* ',' 0x2C */
- 0xC0, /* '-' 0x2D */
- 0x80, /* '.' 0x2E */
- 0x24, 0xA4, 0xA4, 0x80, /* '/' 0x2F */
- 0x76, 0xE3, 0x18, 0xC6, 0x3B, 0x70, /* '0' 0x30 */
- 0x27, 0x92, 0x49, 0x20, /* '1' 0x31 */
- 0x79, 0x10, 0x41, 0x08, 0xC6, 0x10, 0xFC, /* '2' 0x32 */
- 0x79, 0x30, 0x43, 0x18, 0x10, 0x71, 0x78, /* '3' 0x33 */
- 0x08, 0x61, 0x8A, 0x49, 0x2F, 0xC2, 0x08, /* '4' 0x34 */
- 0xFC, 0x21, 0xE8, 0x84, 0x31, 0xF0, /* '5' 0x35 */
- 0x74, 0x61, 0xE8, 0xC6, 0x31, 0x70, /* '6' 0x36 */
- 0xF8, 0x44, 0x22, 0x11, 0x08, 0x40, /* '7' 0x37 */
- 0x39, 0x34, 0x53, 0x39, 0x1C, 0x51, 0x38, /* '8' 0x38 */
- 0x39, 0x3C, 0x71, 0x4C, 0xF0, 0x53, 0x78, /* '9' 0x39 */
- 0x82, /* ':' 0x3A */
- 0x87, /* ';' 0x3B */
- 0x3E, 0x30, 0x60, 0x80, /* '<' 0x3C */
- 0xF8, 0x3E, /* '=' 0x3D */
- 0xE0, 0xC6, 0xC8, 0x00, /* '>' 0x3E */
- 0x74, 0x42, 0x11, 0x10, 0x80, 0x20, /* '?' 0x3F */
- 0x0F, 0x86, 0x19, 0x9A, 0xA4, 0xD9, 0x13, 0x22, 0x56, 0xDA, 0x6E, 0x60, 0x06, 0x00, 0x3C, 0x00, /* '@' 0x40 */
- 0x18, 0x18, 0x24, 0x24, 0x24, 0x7E, 0x42, 0x42, 0xC3, /* 'A' 0x41 */
- 0xFA, 0x18, 0x61, 0xFA, 0x18, 0x61, 0xFC, /* 'B' 0x42 */
- 0x3E, 0x63, 0x40, 0x40, 0xC0, 0x40, 0x41, 0x63, 0x3E, /* 'C' 0x43 */
- 0xF9, 0x0A, 0x1C, 0x18, 0x30, 0x61, 0xC2, 0xF8, /* 'D' 0x44 */
- 0xFE, 0x08, 0x20, 0xFE, 0x08, 0x20, 0xFC, /* 'E' 0x45 */
- 0xFE, 0x08, 0x20, 0xFA, 0x08, 0x20, 0x80, /* 'F' 0x46 */
- 0x1E, 0x61, 0x40, 0x40, 0xC7, 0x41, 0x41, 0x63, 0x1D, /* 'G' 0x47 */
- 0x83, 0x06, 0x0C, 0x1F, 0xF0, 0x60, 0xC1, 0x82, /* 'H' 0x48 */
- 0xFF, 0x80, /* 'I' 0x49 */
- 0x08, 0x42, 0x10, 0x87, 0x29, 0x70, /* 'J' 0x4A */
- 0x85, 0x12, 0x45, 0x0D, 0x13, 0x22, 0x42, 0x86, /* 'K' 0x4B */
- 0x84, 0x21, 0x08, 0x42, 0x10, 0xF8, /* 'L' 0x4C */
- 0xC3, 0xC3, 0xC3, 0xA5, 0xA5, 0xA5, 0x99, 0x99, 0x99, /* 'M' 0x4D */
- 0x83, 0x86, 0x8D, 0x19, 0x33, 0x62, 0xC3, 0x86, /* 'N' 0x4E */
- 0x1E, 0x31, 0x90, 0x68, 0x1C, 0x0A, 0x05, 0x06, 0xC6, 0x1E, 0x00, /* 'O' 0x4F */
- 0xFA, 0x18, 0x61, 0xFA, 0x08, 0x20, 0x80, /* 'P' 0x50 */
- 0x1E, 0x31, 0x90, 0x68, 0x1C, 0x0A, 0x05, 0x16, 0xC6, 0x1F, 0x00, 0x40, /* 'Q' 0x51 */
- 0xFD, 0x0E, 0x1C, 0x2F, 0x90, 0xA1, 0x42, 0x86, /* 'R' 0x52 */
- 0x7A, 0x18, 0x30, 0x78, 0x38, 0x61, 0x78, /* 'S' 0x53 */
- 0xFE, 0x20, 0x40, 0x81, 0x02, 0x04, 0x08, 0x10, /* 'T' 0x54 */
- 0x83, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xE2, 0x78, /* 'U' 0x55 */
- 0xC2, 0x85, 0x0B, 0x22, 0x44, 0x8E, 0x0C, 0x18, /* 'V' 0x56 */
- 0xC4, 0x28, 0xCD, 0x29, 0x25, 0x24, 0xA4, 0x52, 0x8C, 0x61, 0x8C, 0x31, 0x80, /* 'W' 0x57 */
- 0x87, 0x34, 0x8C, 0x30, 0xC4, 0xA3, 0x84, /* 'X' 0x58 */
- 0xC3, 0x42, 0x24, 0x34, 0x18, 0x08, 0x08, 0x08, 0x08, /* 'Y' 0x59 */
- 0x7E, 0x0C, 0x30, 0x41, 0x06, 0x18, 0x20, 0xFE, /* 'Z' 0x5A */
- 0xEA, 0xAA, 0xAB, /* '[' 0x5B */
- 0x92, 0x24, 0x89, 0x20, /* '\' 0x5C */
- 0xD5, 0x55, 0x57, /* ']' 0x5D */
- 0x46, 0xA9, /* '^' 0x5E */
- 0xFE, /* '_' 0x5F */
- 0x80, /* '`' 0x60 */
- 0x79, 0x20, 0x4F, 0xC6, 0x37, 0x40, /* 'a' 0x61 */
- 0x84, 0x3D, 0x18, 0xC6, 0x31, 0xF0, /* 'b' 0x62 */
- 0x39, 0x3C, 0x20, 0xC1, 0x33, 0x80, /* 'c' 0x63 */
- 0x04, 0x13, 0xD3, 0xC6, 0x1C, 0x53, 0x3C, /* 'd' 0x64 */
- 0x39, 0x38, 0x7F, 0x81, 0x13, 0x80, /* 'e' 0x65 */
- 0x6B, 0xA4, 0x92, 0x40, /* 'f' 0x66 */
- 0x35, 0x3C, 0x61, 0xC5, 0x33, 0x41, 0x4D, 0xE0, /* 'g' 0x67 */
- 0x84, 0x3D, 0x38, 0xC6, 0x31, 0x88, /* 'h' 0x68 */
- 0xBF, 0x80, /* 'i' 0x69 */
- 0x45, 0x55, 0x57, /* 'j' 0x6A */
- 0x84, 0x25, 0x4E, 0x52, 0xD2, 0x88, /* 'k' 0x6B */
- 0xFF, 0x80, /* 'l' 0x6C */
- 0xF7, 0x99, 0x91, 0x91, 0x91, 0x91, 0x91, /* 'm' 0x6D */
- 0xF4, 0x63, 0x18, 0xC6, 0x20, /* 'n' 0x6E */
- 0x39, 0x3C, 0x61, 0xC5, 0x33, 0x80, /* 'o' 0x6F */
- 0xF4, 0x63, 0x18, 0xC7, 0xD0, 0x80, /* 'p' 0x70 */
- 0x3D, 0x3C, 0x61, 0xC5, 0x37, 0x41, 0x04, /* 'q' 0x71 */
- 0xF2, 0x49, 0x20, /* 'r' 0x72 */
- 0x7A, 0x50, 0xE0, 0xE5, 0xE0, /* 's' 0x73 */
- 0x5D, 0x24, 0x93, /* 't' 0x74 */
- 0x8C, 0x63, 0x18, 0xCF, 0xA0, /* 'u' 0x75 */
- 0x85, 0x24, 0x92, 0x30, 0xC3, 0x00, /* 'v' 0x76 */
- 0x89, 0x59, 0x59, 0x55, 0x56, 0x26, 0x26, /* 'w' 0x77 */
- 0x4A, 0x4C, 0x43, 0x27, 0x20, /* 'x' 0x78 */
- 0x8A, 0x52, 0xA5, 0x18, 0x84, 0x22, 0x00, /* 'y' 0x79 */
- 0x78, 0x44, 0x46, 0x23, 0xE0, /* 'z' 0x7A */
- 0x6A, 0xAA, 0xA9, /* '{' 0x7B */
- 0xFF, 0xE0, /* '|' 0x7C */
- 0x95, 0x55, 0x56, /* '}' 0x7D */
- 0x66, 0x60, /* '~' 0x7E */
- 0xFF, 0xC0, 0x67, 0x34, 0x58, 0x4C, 0x46, 0x03, 0x11, 0x80, 0xFF, 0xC0, /* 0x7F */
- 0x1C, 0x45, 0x07, 0xE4, 0x1F, 0x10, 0x10, 0x1E, /* 0x80 */
- /* 0x81 */
- 0xE0, /* 0x82 */
- 0x6B, 0xA4, 0x92, 0x49, 0x60, /* 0x83 */
- 0xB6, 0x80, /* 0x84 */
- 0xA8, /* 0x85 */
- 0x21, 0x09, 0xF2, 0x10, 0x84, 0x21, 0x08, /* 0x86 */
- 0x21, 0x09, 0xF2, 0x10, 0x84, 0xF9, 0x08, /* 0x87 */
- 0x54, /* 0x88 */
- 0x62, 0x09, 0x40, 0x98, 0x06, 0x80, 0x10, 0x01, 0x66, 0x29, 0x92, 0x99, 0x06, 0x60, /* 0x89 */
- 0x28, 0x47, 0xA1, 0x83, 0x07, 0x83, 0x87, 0x17, 0x80, /* 0x8A */
- 0x64, /* 0x8B */
- 0x3B, 0xE8, 0xC2, 0x08, 0x41, 0x08, 0x3F, 0x04, 0x20, 0x82, 0x30, 0x3B, 0xE0, /* 0x8C */
- /* 0x8D */
- 0x14, 0x11, 0xF8, 0x30, 0xC1, 0x04, 0x18, 0x61, 0xFC, /* 0x8E */
- /* 0x8F */
- /* 0x90 */
- 0xE0, /* 0x91 */
- 0xE0, /* 0x92 */
- 0xB6, 0x80, /* 0x93 */
- 0xB6, 0x80, /* 0x94 */
- 0xFF, 0x80, /* 0x95 */
- 0xFC, /* 0x96 */
- 0xFF, 0xF0, /* 0x97 */
- 0xDB, /* 0x98 */
- 0xE6, 0x28, 0xCD, 0x19, 0xA3, 0x34, 0x6A, 0x8B, 0x51, 0x68, /* 0x99 */
- 0x52, 0x69, 0x8E, 0x19, 0x60, /* 0x9A */
- 0x98, /* 0x9B */
- 0x7B, 0xD9, 0xCE, 0x10, 0xC3, 0xF8, 0x41, 0x9C, 0x5E, 0xF0, /* 0x9C */
- /* 0x9D */
- 0x51, 0x1E, 0x11, 0x11, 0x88, 0xF8, /* 0x9E */
- 0x29, 0x05, 0x12, 0x22, 0x87, 0x04, 0x08, 0x10, 0x20, /* 0x9F */
- /* 0xA0 */
- 0xBF, 0x80, /* 0xA1 */
- 0x23, 0xAB, 0x4A, 0x52, 0xAE, 0x20, /* 0xA2 */
- 0x39, 0x14, 0x10, 0xF0, 0x82, 0x1C, 0x4C, /* 0xA3 */
- 0xFC, 0x63, 0xF0, /* 0xA4 */
- 0x8C, 0x54, 0xAF, 0x93, 0xE4, 0x20, /* 0xA5 */
- 0xF9, 0xF0, /* 0xA6 */
- 0x32, 0x91, 0xC9, 0x47, 0x26, 0x14, 0xA4, 0xC0, /* 0xA7 */
- 0xA0, /* 0xA8 */
- 0x3E, 0x3F, 0xB8, 0xF4, 0x1A, 0x0D, 0x17, 0x76, 0xC6, 0x3E, 0x00, /* 0xA9 */
- 0x61, 0x79, 0x60, /* 0xAA */
- 0x5A, 0xA5, /* 0xAB */
- 0xFC, 0x10, 0x40, /* 0xAC */
- /* 0xAD */
- 0x3E, 0x31, 0xB7, 0x72, 0x99, 0xCC, 0xC7, 0x56, 0xC6, 0x3E, 0x00, /* 0xAE */
- 0xE0, /* 0xAF */
- 0x69, 0x96, /* 0xB0 */
- 0x21, 0x3E, 0x42, 0x03, 0xE0, /* 0xB1 */
- 0x69, 0x3C, 0xF0, /* 0xB2 */
- 0x79, 0x29, 0x70, /* 0xB3 */
- 0x80, /* 0xB4 */
- 0x8A, 0x28, 0xA2, 0x8A, 0x6E, 0xE0, 0x80, /* 0xB5 */
- 0x7F, 0xAE, 0xBA, 0x68, 0xA2, 0x8A, 0x28, 0xA0, /* 0xB6 */
- 0x80, /* 0xB7 */
- 0x67, 0x80, /* 0xB8 */
- 0x75, 0x50, /* 0xB9 */
- 0x69, 0x96, 0xF0, /* 0xBA */
- 0xA5, 0x5A, /* 0xBB */
- 0x42, 0x30, 0x84, 0x41, 0x10, 0x48, 0x82, 0x61, 0x28, 0x8F, 0x20, 0x80, /* 0xBC */
- 0x40, 0x63, 0x11, 0x09, 0x74, 0xA8, 0x84, 0x44, 0x44, 0x43, 0x80, /* 0xBD */
- 0x71, 0x24, 0x82, 0x20, 0x50, 0x98, 0x9A, 0x61, 0x28, 0x4F, 0x20, 0x80, /* 0xBE */
- 0x20, 0x08, 0x44, 0x42, 0x11, 0x70, /* 0xBF */
- 0x10, 0x08, 0x00, 0x18, 0x3C, 0x24, 0x24, 0x7E, 0x42, 0xC3, /* 0xC0 */
- 0x08, 0x10, 0x00, 0x18, 0x3C, 0x24, 0x24, 0x7E, 0x42, 0xC3, /* 0xC1 */
- 0x18, 0x24, 0x00, 0x18, 0x3C, 0x24, 0x24, 0x7E, 0x42, 0xC3, /* 0xC2 */
- 0x34, 0x2C, 0x00, 0x18, 0x3C, 0x24, 0x24, 0x7E, 0x42, 0xC3, /* 0xC3 */
- 0x24, 0x00, 0x18, 0x3C, 0x24, 0x24, 0x7E, 0x42, 0x42, 0xC3, /* 0xC4 */
- 0x18, 0x24, 0x18, 0x18, 0x3C, 0x24, 0x24, 0x7E, 0x42, 0xC3, /* 0xC5 */
- 0x1F, 0xC5, 0x02, 0x40, 0x90, 0x47, 0xDF, 0x04, 0x42, 0x10, 0x87, 0xC0, /* 0xC6 */
- 0x3E, 0x61, 0xC0, 0x80, 0x80, 0x80, 0xC1, 0x63, 0x3E, 0x0C, 0x04, 0x1C, /* 0xC7 */
- 0x20, 0x40, 0x3F, 0x82, 0x0F, 0xA0, 0x83, 0xF0, /* 0xC8 */
- 0x08, 0x40, 0x3F, 0x82, 0x0F, 0xA0, 0x83, 0xF0, /* 0xC9 */
- 0x10, 0xA0, 0x3F, 0x82, 0x0F, 0xA0, 0x83, 0xF0, /* 0xCA */
- 0x28, 0x0F, 0xE0, 0x83, 0xE8, 0x20, 0x83, 0xF0, /* 0xCB */
- 0x91, 0x55, 0x50, /* 0xCC */
- 0x62, 0xAA, 0xA0, /* 0xCD */
- 0x54, 0x24, 0x92, 0x48, /* 0xCE */
- 0xA1, 0x24, 0x92, 0x48, /* 0xCF */
- 0x7C, 0x42, 0x41, 0x41, 0xF1, 0x41, 0x41, 0x42, 0x7C, /* 0xD0 */
- 0x14, 0x53, 0x0F, 0x1B, 0x32, 0x66, 0xC7, 0x87, 0x04, /* 0xD1 */
- 0x10, 0x04, 0x0F, 0x8C, 0x6C, 0x1C, 0x06, 0x03, 0x83, 0x63, 0x1F, 0x00, /* 0xD2 */
- 0x04, 0x04, 0x0F, 0x8C, 0x6C, 0x1C, 0x06, 0x03, 0x83, 0x63, 0x1F, 0x00, /* 0xD3 */
- 0x08, 0x0A, 0x00, 0x07, 0xC6, 0x36, 0x0E, 0x03, 0x01, 0xC1, 0xB1, 0x8F, 0x80, /* 0xD4 */
- 0x1A, 0x0B, 0x00, 0x07, 0xC6, 0x36, 0x0E, 0x03, 0x01, 0xC1, 0xB1, 0x8F, 0x80, /* 0xD5 */
- 0x14, 0x00, 0x00, 0x07, 0xC6, 0x36, 0x0E, 0x03, 0x01, 0xC1, 0xB1, 0x8F, 0x80, /* 0xD6 */
- 0x8A, 0x88, 0xA8, 0x80, /* 0xD7 */
- 0x3E, 0xB1, 0xB0, 0xF0, 0x98, 0x8C, 0x87, 0x86, 0xC6, 0xBE, 0x00, /* 0xD8 */
- 0x20, 0x22, 0x0C, 0x18, 0x30, 0x60, 0xC1, 0xC6, 0xF8, /* 0xD9 */
- 0x08, 0x22, 0x0C, 0x18, 0x30, 0x60, 0xC1, 0xC6, 0xF8, /* 0xDA */
- 0x10, 0x52, 0x0C, 0x18, 0x30, 0x60, 0xC1, 0xC6, 0xF8, /* 0xDB */
- 0x29, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC1, 0xC6, 0xF8, /* 0xDC */
- 0x09, 0x25, 0x12, 0x22, 0x87, 0x04, 0x08, 0x10, 0x20, /* 0xDD */
- 0x83, 0xE8, 0x61, 0x87, 0xE8, 0x20, 0x80, /* 0xDE */
- 0x7A, 0x18, 0x61, 0x8A, 0x18, 0x61, 0xB8, /* 0xDF */
- 0x20, 0x20, 0x03, 0xC8, 0x40, 0x8F, 0x62, 0x8C, 0xEC, /* 0xE0 */
- 0x10, 0x40, 0x03, 0xC8, 0x40, 0x8F, 0x62, 0x8C, 0xEC, /* 0xE1 */
- 0x10, 0x50, 0x03, 0xC8, 0x40, 0x8F, 0x62, 0x8C, 0xEC, /* 0xE2 */
- 0x68, 0xB0, 0x03, 0xC8, 0x40, 0x8F, 0x62, 0x8C, 0xEC, /* 0xE3 */
- 0x28, 0x01, 0xE4, 0x20, 0x47, 0xB1, 0x46, 0x76, /* 0xE4 */
- 0x10, 0x50, 0x43, 0xC8, 0x40, 0x8F, 0x62, 0x8C, 0xEC, /* 0xE5 */
- 0x7B, 0xA1, 0x90, 0x45, 0xFF, 0x84, 0x23, 0x17, 0x38, /* 0xE6 */
- 0x7B, 0x18, 0x20, 0x83, 0x17, 0x8C, 0x11, 0xC0, /* 0xE7 */
- 0x20, 0x40, 0x1E, 0xCE, 0x1F, 0xE0, 0xC5, 0xE0, /* 0xE8 */
- 0x10, 0x80, 0x1E, 0xCE, 0x1F, 0xE0, 0xC5, 0xE0, /* 0xE9 */
- 0x10, 0xA0, 0x1E, 0xCE, 0x1F, 0xE0, 0xC5, 0xE0, /* 0xEA */
- 0x28, 0x07, 0xB3, 0x87, 0xF8, 0x31, 0x78, /* 0xEB */
- 0x91, 0x55, 0x50, /* 0xEC */
- 0x62, 0xAA, 0xA0, /* 0xED */
- 0x54, 0x24, 0x92, 0x48, /* 0xEE */
- 0xA1, 0x24, 0x92, 0x40, /* 0xEF */
- 0x28, 0x42, 0x8F, 0x46, 0x18, 0x52, 0x30, /* 0xF0 */
- 0x6A, 0xC1, 0x6C, 0xC6, 0x31, 0x8C, 0x40, /* 0xF1 */
- 0x20, 0x40, 0x1E, 0xCE, 0x18, 0x61, 0xCD, 0xE0, /* 0xF2 */
- 0x10, 0x80, 0x1E, 0xCE, 0x18, 0x61, 0xCD, 0xE0, /* 0xF3 */
- 0x10, 0xA0, 0x1E, 0xCE, 0x18, 0x61, 0xCD, 0xE0, /* 0xF4 */
- 0x69, 0x60, 0x1E, 0xCE, 0x18, 0x61, 0xCD, 0xE0, /* 0xF5 */
- 0x28, 0x07, 0xB3, 0x86, 0x18, 0x73, 0x78, /* 0xF6 */
- 0x20, 0x3E, 0x02, 0x00, /* 0xF7 */
- 0x7F, 0x39, 0x69, 0xC7, 0x3F, 0x80, /* 0xF8 */
- 0x41, 0x23, 0x18, 0xC6, 0x33, 0x68, /* 0xF9 */
- 0x11, 0x23, 0x18, 0xC6, 0x33, 0x68, /* 0xFA */
- 0x22, 0x81, 0x18, 0xC6, 0x31, 0x9B, 0x40, /* 0xFB */
- 0x50, 0x23, 0x18, 0xC6, 0x33, 0x68, /* 0xFC */
- 0x10, 0x88, 0x52, 0x49, 0x23, 0x0C, 0x30, 0x82, 0x18, /* 0xFD */
- 0x84, 0x3D, 0xB8, 0xC6, 0x3B, 0xF4, 0x20, /* 0xFE */
- 0x28, 0x08, 0x52, 0x49, 0x23, 0x0C, 0x30, 0x82, 0x18, /* 0xFF */
+/* 0x01 */ 0x1C, 0x0A, 0x05, 0x04, 0xFE, 0x08, 0x1C, 0x02, 0x07, 0xE0, 0x9F, 0xC0,
+/* 0x02 */ 0x3F, 0xF0, 0x40, 0xE0, 0x10, 0x3F, 0x04, 0x9E, 0x28, 0x14, 0x0E, 0x00,
+/* 0x03 */ 0x3F, 0x10, 0x28, 0x06, 0x49, 0x80, 0x60, 0x19, 0x26, 0x31, 0x40, 0x8F, 0xC0,
+/* 0x04 */ 0x3F, 0x10, 0x2A, 0x16, 0x49, 0xA1, 0x60, 0x19, 0xE6, 0x31, 0x40, 0x8F, 0xC0,
+/* 0x05 */ 0x28, 0x15, 0x2A, 0xB5, 0x55, 0xA8, 0x54, 0x12, 0x04, 0x41, 0x08, 0x81, 0xC0,
+/* 0x06 */ 0x04, 0x08, 0x88, 0x82, 0x07, 0x01, 0x11, 0xA2, 0xC4, 0x40, 0x70, 0x20, 0x88, 0x88, 0x10, 0x00,
+/* 0x07 */
+/* 0x08 */ 0x03, 0x83, 0x44, 0x48, 0x28, 0x01, 0x80, 0x17, 0xFE, 0x08, 0x45, 0x28, 0x84, 0x00,
+/* 0x09 */ 0x01, 0xC0, 0x68, 0x82, 0x41, 0x10, 0x02, 0x80, 0x06, 0x00, 0x14, 0x00, 0x8F, 0xFC,
+/* 0x0A */
+/* 0x0B */ 0x22, 0x2A, 0xA2, 0x30, 0x18, 0x0A, 0x09, 0x04, 0x44, 0x14, 0x04, 0x00,
+/* 0x0C */ 0x46, 0x00, 0x19, 0x03, 0x21, 0x20, 0x93, 0x04, 0x20, 0x11, 0x80, 0x50, 0x02, 0x7F, 0xE0,
+/* 0x0D */
+/* 0x0E */ 0x08, 0x0E, 0x08, 0x88, 0x24, 0x12, 0x09, 0x05, 0x01, 0xFF, 0x8A, 0x02, 0x00,
+/* 0x0F */ 0x3F, 0x14, 0xAA, 0x16, 0x01, 0x92, 0x60, 0x18, 0xC6, 0x49, 0x40, 0x8F, 0xC0,
+/* 0x10 */ 0x1B, 0x02, 0xA0, 0x54, 0x12, 0x42, 0x48, 0x49, 0x31, 0x1E, 0x23, 0xEA, 0xFE, 0x3C,
+/* 0x11 */ 0x3F, 0x02, 0x00, 0x20, 0x6D, 0x27, 0xF8, 0x3F, 0xC1, 0xFE, 0x37, 0xD0, 0xBE, 0x40, 0xE1, 0xE2, 0x00,
+/* 0x12 */ 0x12, 0x42, 0x20, 0x24, 0xC0, 0x29, 0x99, 0x05, 0x23, 0x30, 0xB0, 0x30, 0x00,
+/* 0x13 */ 0x3F, 0x88, 0x0A, 0x44, 0xD5, 0x58, 0x03, 0x00, 0x67, 0xCC, 0x71, 0x40, 0x47, 0xF0,
+/* 0x14 */ 0x3F, 0x18, 0x69, 0x26, 0x85, 0xA1, 0x6C, 0xD8, 0x06, 0x31, 0x40, 0x8F, 0xC0,
+/* 0x15 */ 0x3F, 0x11, 0x00, 0xE8, 0x03, 0xA0, 0x1F, 0xB3, 0x7E, 0x00, 0xE9, 0xE0, 0x23, 0x00, 0x40, 0x40, 0xFE, 0x00,
+/* 0x16 */ 0x30, 0x38, 0x3A, 0x3E, 0x6E, 0xEB, 0xC3, 0xC3, 0x66, 0x3C,
+/* 0x17 */ 0x3F, 0x04, 0x00, 0x82, 0x88, 0x5C, 0xA4, 0x49, 0x22, 0x81, 0x98, 0xC4, 0x40, 0xA3, 0xF0,
+/* 0x18 */ 0x07, 0x80, 0x42, 0x04, 0x08, 0x21, 0x41, 0x42, 0x60, 0x0E, 0x8C, 0xB2, 0x89, 0x50, 0x52, 0x82, 0x80,
+/* 0x19 */ 0x3F, 0xC4, 0x02, 0x80, 0x18, 0x01, 0xB3, 0x1B, 0xB9, 0x80, 0x19, 0xE1, 0x40, 0x23, 0xFC,
+/* 0x1A */ 0xFF, 0xC0, 0x67, 0x34, 0x58, 0x4C, 0x46, 0x03, 0x11, 0x80, 0xFF, 0xC0,
+/* 0x1B */ 0x0F, 0xC0, 0x40, 0x82, 0x49, 0x08, 0x04, 0x00, 0x00, 0x12, 0x02, 0x31, 0x34, 0x0B, 0x88, 0x45, 0x00, 0x20,
+/* 0x1C */ 0x3F, 0x88, 0x0A, 0x44, 0xC9, 0x19, 0x3B, 0x00, 0x60, 0x4C, 0x71, 0x40, 0x47, 0xF0,
+/* 0x1D */ 0x3F, 0x8B, 0x0A, 0x00, 0xC8, 0x18, 0x13, 0x00, 0x48, 0xCA, 0xC1, 0x44, 0x53, 0x30,
+/* 0x1E */ 0x19, 0xC2, 0x02, 0x50, 0x1E, 0x49, 0x80, 0x12, 0x01, 0x27, 0x92, 0x01, 0x10, 0x20, 0xFC,
+/* 0x1F */ 0x30, 0x1C, 0x0C, 0x3E, 0x7E, 0xCF, 0x07, 0xC7, 0x7F, 0x3F,
+/* ' ' 0x20 */
+/* '!' 0x21 */ 0xFC, 0x80,
+/* '"' 0x22 */ 0xB6, 0x80,
+/* '#' 0x23 */ 0x24, 0x51, 0xF9, 0x42, 0x9F, 0x92, 0x28,
+/* '$' 0x24 */ 0x10, 0xE5, 0x55, 0x50, 0xE1, 0x65, 0x55, 0xE1, 0x00,
+/* '%' 0x25 */ 0x71, 0x24, 0x89, 0x22, 0x50, 0x74, 0x02, 0x70, 0xA4, 0x49, 0x11, 0xC0,
+/* '&' 0x26 */ 0x71, 0x24, 0x9C, 0x62, 0x58, 0xA7, 0xF4,
+/* ''' 0x27 */ 0xE0,
+/* '(' 0x28 */ 0x5A, 0xAA, 0x94,
+/* ')' 0x29 */ 0x89, 0x12, 0x49, 0x29, 0x00,
+/* '*' 0x2A */ 0x5E, 0x80,
+/* '+' 0x2B */ 0x21, 0x3E, 0x42, 0x00,
+/* ',' 0x2C */ 0xE0,
+/* '-' 0x2D */ 0xC0,
+/* '.' 0x2E */ 0x80,
+/* '/' 0x2F */ 0x24, 0xA4, 0xA4, 0x80,
+/* '0' 0x30 */ 0x76, 0xE3, 0x18, 0xC6, 0x3B, 0x70,
+/* '1' 0x31 */ 0x27, 0x92, 0x49, 0x20,
+/* '2' 0x32 */ 0x79, 0x10, 0x41, 0x08, 0xC6, 0x10, 0xFC,
+/* '3' 0x33 */ 0x79, 0x30, 0x43, 0x18, 0x10, 0x71, 0x78,
+/* '4' 0x34 */ 0x08, 0x61, 0x8A, 0x49, 0x2F, 0xC2, 0x08,
+/* '5' 0x35 */ 0xFC, 0x21, 0xE8, 0x84, 0x31, 0xF0,
+/* '6' 0x36 */ 0x74, 0x61, 0xE8, 0xC6, 0x31, 0x70,
+/* '7' 0x37 */ 0xF8, 0x44, 0x22, 0x11, 0x08, 0x40,
+/* '8' 0x38 */ 0x39, 0x34, 0x53, 0x39, 0x1C, 0x51, 0x38,
+/* '9' 0x39 */ 0x39, 0x3C, 0x71, 0x4C, 0xF0, 0x53, 0x78,
+/* ':' 0x3A */ 0x82,
+/* ';' 0x3B */ 0x87,
+/* '<' 0x3C */ 0x3E, 0x30, 0x60, 0x80,
+/* '=' 0x3D */ 0xF8, 0x3E,
+/* '>' 0x3E */ 0xE0, 0xC6, 0xC8, 0x00,
+/* '?' 0x3F */ 0x74, 0x42, 0x11, 0x10, 0x80, 0x20,
+/* '@' 0x40 */ 0x0F, 0x86, 0x19, 0x9A, 0xA4, 0xD9, 0x13, 0x22, 0x56, 0xDA, 0x6E, 0x60, 0x06, 0x00, 0x3C, 0x00,
+/* 'A' 0x41 */ 0x18, 0x18, 0x24, 0x24, 0x24, 0x7E, 0x42, 0x42, 0xC3,
+/* 'B' 0x42 */ 0xFA, 0x18, 0x61, 0xFA, 0x18, 0x61, 0xFC,
+/* 'C' 0x43 */ 0x3E, 0x63, 0x40, 0x40, 0xC0, 0x40, 0x41, 0x63, 0x3E,
+/* 'D' 0x44 */ 0xF9, 0x0A, 0x1C, 0x18, 0x30, 0x61, 0xC2, 0xF8,
+/* 'E' 0x45 */ 0xFE, 0x08, 0x20, 0xFE, 0x08, 0x20, 0xFC,
+/* 'F' 0x46 */ 0xFE, 0x08, 0x20, 0xFA, 0x08, 0x20, 0x80,
+/* 'G' 0x47 */ 0x1E, 0x61, 0x40, 0x40, 0xC7, 0x41, 0x41, 0x63, 0x1D,
+/* 'H' 0x48 */ 0x83, 0x06, 0x0C, 0x1F, 0xF0, 0x60, 0xC1, 0x82,
+/* 'I' 0x49 */ 0xFF, 0x80,
+/* 'J' 0x4A */ 0x08, 0x42, 0x10, 0x87, 0x29, 0x70,
+/* 'K' 0x4B */ 0x85, 0x12, 0x45, 0x0D, 0x13, 0x22, 0x42, 0x86,
+/* 'L' 0x4C */ 0x84, 0x21, 0x08, 0x42, 0x10, 0xF8,
+/* 'M' 0x4D */ 0xC3, 0xC3, 0xC3, 0xA5, 0xA5, 0xA5, 0x99, 0x99, 0x99,
+/* 'N' 0x4E */ 0x83, 0x86, 0x8D, 0x19, 0x33, 0x62, 0xC3, 0x86,
+/* 'O' 0x4F */ 0x1E, 0x31, 0x90, 0x68, 0x1C, 0x0A, 0x05, 0x06, 0xC6, 0x1E, 0x00,
+/* 'P' 0x50 */ 0xFA, 0x18, 0x61, 0xFA, 0x08, 0x20, 0x80,
+/* 'Q' 0x51 */ 0x1E, 0x31, 0x90, 0x68, 0x1C, 0x0A, 0x05, 0x16, 0xC6, 0x1F, 0x00, 0x40,
+/* 'R' 0x52 */ 0xFD, 0x0E, 0x1C, 0x2F, 0x90, 0xA1, 0x42, 0x86,
+/* 'S' 0x53 */ 0x7A, 0x18, 0x30, 0x78, 0x38, 0x61, 0x78,
+/* 'T' 0x54 */ 0xFE, 0x20, 0x40, 0x81, 0x02, 0x04, 0x08, 0x10,
+/* 'U' 0x55 */ 0x83, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xE2, 0x78,
+/* 'V' 0x56 */ 0xC2, 0x85, 0x0B, 0x22, 0x44, 0x8E, 0x0C, 0x18,
+/* 'W' 0x57 */ 0xC4, 0x28, 0xCD, 0x29, 0x25, 0x24, 0xA4, 0x52, 0x8C, 0x61, 0x8C, 0x31, 0x80,
+/* 'X' 0x58 */ 0x87, 0x34, 0x8C, 0x30, 0xC4, 0xA3, 0x84,
+/* 'Y' 0x59 */ 0xC3, 0x42, 0x24, 0x34, 0x18, 0x08, 0x08, 0x08, 0x08,
+/* 'Z' 0x5A */ 0x7E, 0x0C, 0x30, 0x41, 0x06, 0x18, 0x20, 0xFE,
+/* '[' 0x5B */ 0xEA, 0xAA, 0xAB,
+/* '\' 0x5C */ 0x92, 0x24, 0x89, 0x20,
+/* ']' 0x5D */ 0xD5, 0x55, 0x57,
+/* '^' 0x5E */ 0x46, 0xA9,
+/* '_' 0x5F */ 0xFE,
+/* '`' 0x60 */ 0x80,
+/* 'a' 0x61 */ 0x79, 0x20, 0x4F, 0xC6, 0x37, 0x40,
+/* 'b' 0x62 */ 0x84, 0x3D, 0x18, 0xC6, 0x31, 0xF0,
+/* 'c' 0x63 */ 0x39, 0x3C, 0x20, 0xC1, 0x33, 0x80,
+/* 'd' 0x64 */ 0x04, 0x13, 0xD3, 0xC6, 0x1C, 0x53, 0x3C,
+/* 'e' 0x65 */ 0x39, 0x38, 0x7F, 0x81, 0x13, 0x80,
+/* 'f' 0x66 */ 0x6B, 0xA4, 0x92, 0x40,
+/* 'g' 0x67 */ 0x35, 0x3C, 0x61, 0xC5, 0x33, 0x41, 0x4D, 0xE0,
+/* 'h' 0x68 */ 0x84, 0x3D, 0x38, 0xC6, 0x31, 0x88,
+/* 'i' 0x69 */ 0xBF, 0x80,
+/* 'j' 0x6A */ 0x45, 0x55, 0x57,
+/* 'k' 0x6B */ 0x84, 0x25, 0x4E, 0x52, 0xD2, 0x88,
+/* 'l' 0x6C */ 0xFF, 0x80,
+/* 'm' 0x6D */ 0xF7, 0x99, 0x91, 0x91, 0x91, 0x91, 0x91,
+/* 'n' 0x6E */ 0xF4, 0x63, 0x18, 0xC6, 0x20,
+/* 'o' 0x6F */ 0x39, 0x3C, 0x61, 0xC5, 0x33, 0x80,
+/* 'p' 0x70 */ 0xF4, 0x63, 0x18, 0xC7, 0xD0, 0x80,
+/* 'q' 0x71 */ 0x3D, 0x3C, 0x61, 0xC5, 0x37, 0x41, 0x04,
+/* 'r' 0x72 */ 0xF2, 0x49, 0x20,
+/* 's' 0x73 */ 0x7A, 0x50, 0xE0, 0xE5, 0xE0,
+/* 't' 0x74 */ 0x5D, 0x24, 0x93,
+/* 'u' 0x75 */ 0x8C, 0x63, 0x18, 0xCF, 0xA0,
+/* 'v' 0x76 */ 0x85, 0x24, 0x92, 0x30, 0xC3, 0x00,
+/* 'w' 0x77 */ 0x89, 0x59, 0x59, 0x55, 0x56, 0x26, 0x26,
+/* 'x' 0x78 */ 0x4A, 0x4C, 0x43, 0x27, 0x20,
+/* 'y' 0x79 */ 0x8A, 0x52, 0xA5, 0x18, 0x84, 0x22, 0x00,
+/* 'z' 0x7A */ 0x78, 0x44, 0x46, 0x23, 0xE0,
+/* '{' 0x7B */ 0x6A, 0xAA, 0xA9,
+/* '|' 0x7C */ 0xFF, 0xE0,
+/* '}' 0x7D */ 0x95, 0x55, 0x56,
+/* '~' 0x7E */ 0x66, 0x60,
+/* 0x7F */
+/* 0x80 */ 0x1C, 0x45, 0x07, 0xE4, 0x1F, 0x10, 0x10, 0x1E,
+/* 0x81 */
+/* 0x82 */ 0xE0,
+/* 0x83 */ 0x6B, 0xA4, 0x92, 0x49, 0x60,
+/* 0x84 */ 0xB6, 0x80,
+/* 0x85 */ 0xA8,
+/* 0x86 */ 0x21, 0x09, 0xF2, 0x10, 0x84, 0x21, 0x08,
+/* 0x87 */ 0x21, 0x09, 0xF2, 0x10, 0x84, 0xF9, 0x08,
+/* 0x88 */ 0x54,
+/* 0x89 */ 0x62, 0x09, 0x40, 0x98, 0x06, 0x80, 0x10, 0x01, 0x66, 0x29, 0x92, 0x99, 0x06, 0x60,
+/* 0x8A */ 0x28, 0x47, 0xA1, 0x83, 0x07, 0x83, 0x87, 0x17, 0x80,
+/* 0x8B */ 0x64,
+/* 0x8C */ 0x3B, 0xE8, 0xC2, 0x08, 0x41, 0x08, 0x3F, 0x04, 0x20, 0x82, 0x30, 0x3B, 0xE0,
+/* 0x8D */
+/* 0x8E */ 0x14, 0x11, 0xF8, 0x30, 0xC1, 0x04, 0x18, 0x61, 0xFC,
+/* 0x8F */
+/* 0x90 */
+/* 0x91 */ 0xE0,
+/* 0x92 */ 0xE0,
+/* 0x93 */ 0xB6, 0x80,
+/* 0x94 */ 0xB6, 0x80,
+/* 0x95 */ 0xFF, 0x80,
+/* 0x96 */ 0xFC,
+/* 0x97 */ 0xFF, 0xF0,
+/* 0x98 */ 0xDB,
+/* 0x99 */ 0xE6, 0x28, 0xCD, 0x19, 0xA3, 0x34, 0x6A, 0x8B, 0x51, 0x68,
+/* 0x9A */ 0x52, 0x69, 0x8E, 0x19, 0x60,
+/* 0x9B */ 0x98,
+/* 0x9C */ 0x7B, 0xD9, 0xCE, 0x10, 0xC3, 0xF8, 0x41, 0x9C, 0x5E, 0xF0,
+/* 0x9D */
+/* 0x9E */ 0x51, 0x1E, 0x11, 0x11, 0x88, 0xF8,
+/* 0x9F */ 0x29, 0x05, 0x12, 0x22, 0x87, 0x04, 0x08, 0x10, 0x20,
+/* 0xA0 */
+/* 0xA1 */ 0xBF, 0x80,
+/* 0xA2 */ 0x23, 0xAB, 0x4A, 0x52, 0xAE, 0x20,
+/* 0xA3 */ 0x39, 0x14, 0x10, 0xF0, 0x82, 0x1C, 0x4C,
+/* 0xA4 */ 0xFC, 0x63, 0xF0,
+/* 0xA5 */ 0x8C, 0x54, 0xAF, 0x93, 0xE4, 0x20,
+/* 0xA6 */ 0xF9, 0xF0,
+/* 0xA7 */ 0x32, 0x91, 0xC9, 0x47, 0x26, 0x14, 0xA4, 0xC0,
+/* 0xA8 */ 0xA0,
+/* 0xA9 */ 0x3E, 0x3F, 0xB8, 0xF4, 0x1A, 0x0D, 0x17, 0x76, 0xC6, 0x3E, 0x00,
+/* 0xAA */ 0x61, 0x79, 0x60,
+/* 0xAB */ 0x5A, 0xA5,
+/* 0xAC */ 0xFC, 0x10, 0x40,
+/* 0xAD */
+/* 0xAE */ 0x3E, 0x31, 0xB7, 0x72, 0x99, 0xCC, 0xC7, 0x56, 0xC6, 0x3E, 0x00,
+/* 0xAF */ 0xE0,
+/* 0xB0 */ 0x69, 0x96,
+/* 0xB1 */ 0x21, 0x3E, 0x42, 0x03, 0xE0,
+/* 0xB2 */ 0x69, 0x3C, 0xF0,
+/* 0xB3 */ 0x79, 0x29, 0x70,
+/* 0xB4 */ 0x80,
+/* 0xB5 */ 0x8A, 0x28, 0xA2, 0x8A, 0x6E, 0xE0, 0x80,
+/* 0xB6 */ 0x7F, 0xAE, 0xBA, 0x68, 0xA2, 0x8A, 0x28, 0xA0,
+/* 0xB7 */ 0x80,
+/* 0xB8 */ 0x67, 0x80,
+/* 0xB9 */ 0x75, 0x50,
+/* 0xBA */ 0x69, 0x96, 0xF0,
+/* 0xBB */ 0xA5, 0x5A,
+/* 0xBC */ 0x42, 0x30, 0x84, 0x41, 0x10, 0x48, 0x82, 0x61, 0x28, 0x8F, 0x20, 0x80,
+/* 0xBD */ 0x40, 0x63, 0x11, 0x09, 0x74, 0xA8, 0x84, 0x44, 0x44, 0x43, 0x80,
+/* 0xBE */ 0x71, 0x24, 0x82, 0x20, 0x50, 0x98, 0x9A, 0x61, 0x28, 0x4F, 0x20, 0x80,
+/* 0xBF */ 0x20, 0x08, 0x44, 0x42, 0x11, 0x70,
+/* 0xC0 */ 0x10, 0x08, 0x00, 0x18, 0x3C, 0x24, 0x24, 0x7E, 0x42, 0xC3,
+/* 0xC1 */ 0x08, 0x10, 0x00, 0x18, 0x3C, 0x24, 0x24, 0x7E, 0x42, 0xC3,
+/* 0xC2 */ 0x18, 0x24, 0x00, 0x18, 0x3C, 0x24, 0x24, 0x7E, 0x42, 0xC3,
+/* 0xC3 */ 0x34, 0x2C, 0x00, 0x18, 0x3C, 0x24, 0x24, 0x7E, 0x42, 0xC3,
+/* 0xC4 */ 0x24, 0x00, 0x18, 0x3C, 0x24, 0x24, 0x7E, 0x42, 0x42, 0xC3,
+/* 0xC5 */ 0x18, 0x24, 0x18, 0x18, 0x3C, 0x24, 0x24, 0x7E, 0x42, 0xC3,
+/* 0xC6 */ 0x1F, 0xC5, 0x02, 0x40, 0x90, 0x47, 0xDF, 0x04, 0x42, 0x10, 0x87, 0xC0,
+/* 0xC7 */ 0x3E, 0x61, 0xC0, 0x80, 0x80, 0x80, 0xC1, 0x63, 0x3E, 0x0C, 0x04, 0x1C,
+/* 0xC8 */ 0x20, 0x40, 0x3F, 0x82, 0x0F, 0xA0, 0x83, 0xF0,
+/* 0xC9 */ 0x08, 0x40, 0x3F, 0x82, 0x0F, 0xA0, 0x83, 0xF0,
+/* 0xCA */ 0x10, 0xA0, 0x3F, 0x82, 0x0F, 0xA0, 0x83, 0xF0,
+/* 0xCB */ 0x28, 0x0F, 0xE0, 0x83, 0xE8, 0x20, 0x83, 0xF0,
+/* 0xCC */ 0x91, 0x55, 0x50,
+/* 0xCD */ 0x62, 0xAA, 0xA0,
+/* 0xCE */ 0x54, 0x24, 0x92, 0x48,
+/* 0xCF */ 0xA1, 0x24, 0x92, 0x48,
+/* 0xD0 */ 0x7C, 0x42, 0x41, 0x41, 0xF1, 0x41, 0x41, 0x42, 0x7C,
+/* 0xD1 */ 0x14, 0x53, 0x0F, 0x1B, 0x32, 0x66, 0xC7, 0x87, 0x04,
+/* 0xD2 */ 0x10, 0x04, 0x0F, 0x8C, 0x6C, 0x1C, 0x06, 0x03, 0x83, 0x63, 0x1F, 0x00,
+/* 0xD3 */ 0x04, 0x04, 0x0F, 0x8C, 0x6C, 0x1C, 0x06, 0x03, 0x83, 0x63, 0x1F, 0x00,
+/* 0xD4 */ 0x08, 0x0A, 0x00, 0x07, 0xC6, 0x36, 0x0E, 0x03, 0x01, 0xC1, 0xB1, 0x8F, 0x80,
+/* 0xD5 */ 0x1A, 0x0B, 0x00, 0x07, 0xC6, 0x36, 0x0E, 0x03, 0x01, 0xC1, 0xB1, 0x8F, 0x80,
+/* 0xD6 */ 0x14, 0x00, 0x00, 0x07, 0xC6, 0x36, 0x0E, 0x03, 0x01, 0xC1, 0xB1, 0x8F, 0x80,
+/* 0xD7 */ 0x8A, 0x88, 0xA8, 0x80,
+/* 0xD8 */ 0x3E, 0xB1, 0xB0, 0xF0, 0x98, 0x8C, 0x87, 0x86, 0xC6, 0xBE, 0x00,
+/* 0xD9 */ 0x20, 0x22, 0x0C, 0x18, 0x30, 0x60, 0xC1, 0xC6, 0xF8,
+/* 0xDA */ 0x08, 0x22, 0x0C, 0x18, 0x30, 0x60, 0xC1, 0xC6, 0xF8,
+/* 0xDB */ 0x10, 0x52, 0x0C, 0x18, 0x30, 0x60, 0xC1, 0xC6, 0xF8,
+/* 0xDC */ 0x29, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC1, 0xC6, 0xF8,
+/* 0xDD */ 0x09, 0x25, 0x12, 0x22, 0x87, 0x04, 0x08, 0x10, 0x20,
+/* 0xDE */ 0x83, 0xE8, 0x61, 0x87, 0xE8, 0x20, 0x80,
+/* 0xDF */ 0x7A, 0x18, 0x61, 0x8A, 0x18, 0x61, 0xB8,
+/* 0xE0 */ 0x20, 0x20, 0x03, 0xC8, 0x40, 0x8F, 0x62, 0x8C, 0xEC,
+/* 0xE1 */ 0x10, 0x40, 0x03, 0xC8, 0x40, 0x8F, 0x62, 0x8C, 0xEC,
+/* 0xE2 */ 0x10, 0x50, 0x03, 0xC8, 0x40, 0x8F, 0x62, 0x8C, 0xEC,
+/* 0xE3 */ 0x68, 0xB0, 0x03, 0xC8, 0x40, 0x8F, 0x62, 0x8C, 0xEC,
+/* 0xE4 */ 0x28, 0x01, 0xE4, 0x20, 0x47, 0xB1, 0x46, 0x76,
+/* 0xE5 */ 0x10, 0x50, 0x43, 0xC8, 0x40, 0x8F, 0x62, 0x8C, 0xEC,
+/* 0xE6 */ 0x7B, 0xA1, 0x90, 0x45, 0xFF, 0x84, 0x23, 0x17, 0x38,
+/* 0xE7 */ 0x7B, 0x18, 0x20, 0x83, 0x17, 0x8C, 0x11, 0xC0,
+/* 0xE8 */ 0x20, 0x40, 0x1E, 0xCE, 0x1F, 0xE0, 0xC5, 0xE0,
+/* 0xE9 */ 0x10, 0x80, 0x1E, 0xCE, 0x1F, 0xE0, 0xC5, 0xE0,
+/* 0xEA */ 0x10, 0xA0, 0x1E, 0xCE, 0x1F, 0xE0, 0xC5, 0xE0,
+/* 0xEB */ 0x28, 0x07, 0xB3, 0x87, 0xF8, 0x31, 0x78,
+/* 0xEC */ 0x91, 0x55, 0x50,
+/* 0xED */ 0x62, 0xAA, 0xA0,
+/* 0xEE */ 0x54, 0x24, 0x92, 0x48,
+/* 0xEF */ 0xA1, 0x24, 0x92, 0x40,
+/* 0xF0 */ 0x28, 0x42, 0x8F, 0x46, 0x18, 0x52, 0x30,
+/* 0xF1 */ 0x6A, 0xC1, 0x6C, 0xC6, 0x31, 0x8C, 0x40,
+/* 0xF2 */ 0x20, 0x40, 0x1E, 0xCE, 0x18, 0x61, 0xCD, 0xE0,
+/* 0xF3 */ 0x10, 0x80, 0x1E, 0xCE, 0x18, 0x61, 0xCD, 0xE0,
+/* 0xF4 */ 0x10, 0xA0, 0x1E, 0xCE, 0x18, 0x61, 0xCD, 0xE0,
+/* 0xF5 */ 0x69, 0x60, 0x1E, 0xCE, 0x18, 0x61, 0xCD, 0xE0,
+/* 0xF6 */ 0x28, 0x07, 0xB3, 0x86, 0x18, 0x73, 0x78,
+/* 0xF7 */ 0x20, 0x3E, 0x02, 0x00,
+/* 0xF8 */ 0x7F, 0x39, 0x69, 0xC7, 0x3F, 0x80,
+/* 0xF9 */ 0x41, 0x23, 0x18, 0xC6, 0x33, 0x68,
+/* 0xFA */ 0x11, 0x23, 0x18, 0xC6, 0x33, 0x68,
+/* 0xFB */ 0x22, 0x81, 0x18, 0xC6, 0x31, 0x9B, 0x40,
+/* 0xFC */ 0x50, 0x23, 0x18, 0xC6, 0x33, 0x68,
+/* 0xFD */ 0x10, 0x88, 0x52, 0x49, 0x23, 0x0C, 0x30, 0x82, 0x18,
+/* 0xFE */ 0x84, 0x3D, 0xB8, 0xC6, 0x3B, 0xF4, 0x20,
+/* 0xFF */ 0x28, 0x08, 0x52, 0x49, 0x23, 0x0C, 0x30, 0x82, 0x18,
};
const GFXglyph FreeSans6pt_Win1252Glyphs[] PROGMEM = {
- /* ' ' 0x20 */ {0, 0, 0, 3, 0, 0},
- /* '!' 0x21 */ {0, 1, 9, 4, 2, -8},
- /* '"' 0x22 */ {2, 3, 3, 4, 0, -8},
- /* '#' 0x23 */ {4, 7, 8, 7, 0, -7},
- /* '$' 0x24 */ {11, 6, 11, 7, 0, -9},
- /* '%' 0x25 */ {20, 10, 9, 11, 0, -8},
- /* '&' 0x26 */ {32, 6, 9, 8, 1, -8},
- /* ''' 0x27 */ {39, 1, 3, 2, 1, -8},
- /* '(' 0x28 */ {40, 2, 11, 4, 1, -8},
- /* ')' 0x29 */ {43, 3, 11, 4, 0, -8},
- /* '*' 0x2A */ {48, 3, 3, 5, 1, -8},
- /* '+' 0x2B */ {50, 5, 5, 7, 1, -4},
- /* ',' 0x2C */ {54, 1, 3, 3, 1, 0},
- /* '-' 0x2D */ {55, 2, 1, 4, 1, -3},
- /* '.' 0x2E */ {56, 1, 1, 3, 1, 0},
- /* '/' 0x2F */ {57, 3, 9, 3, 0, -8},
- /* '0' 0x30 */ {61, 5, 9, 7, 1, -8},
- /* '1' 0x31 */ {67, 3, 9, 7, 1, -8},
- /* '2' 0x32 */ {71, 6, 9, 7, 0, -8},
- /* '3' 0x33 */ {78, 6, 9, 7, 0, -8},
- /* '4' 0x34 */ {85, 6, 9, 7, 0, -8},
- /* '5' 0x35 */ {92, 5, 9, 7, 1, -8},
- /* '6' 0x36 */ {98, 5, 9, 7, 1, -8},
- /* '7' 0x37 */ {104, 5, 9, 7, 1, -8},
- /* '8' 0x38 */ {110, 6, 9, 7, 0, -8},
- /* '9' 0x39 */ {117, 6, 9, 7, 0, -8},
- /* ':' 0x3A */ {124, 1, 7, 3, 1, -6},
- /* ';' 0x3B */ {125, 1, 8, 3, 1, -5},
- /* '<' 0x3C */ {126, 5, 5, 7, 1, -4},
- /* '=' 0x3D */ {130, 5, 3, 7, 1, -3},
- /* '>' 0x3E */ {132, 5, 5, 7, 1, -4},
- /* '?' 0x3F */ {136, 5, 9, 7, 1, -8},
- /* '@' 0x40 */ {142, 11, 11, 12, 0, -8},
- /* 'A' 0x41 */ {158, 8, 9, 8, 0, -8},
- /* 'B' 0x42 */ {167, 6, 9, 8, 1, -8},
- /* 'C' 0x43 */ {174, 8, 9, 9, 0, -8},
- /* 'D' 0x44 */ {183, 7, 9, 8, 1, -8},
- /* 'E' 0x45 */ {191, 6, 9, 8, 1, -8},
- /* 'F' 0x46 */ {198, 6, 9, 7, 1, -8},
- /* 'G' 0x47 */ {205, 8, 9, 9, 0, -8},
- /* 'H' 0x48 */ {214, 7, 9, 9, 1, -8},
- /* 'I' 0x49 */ {222, 1, 9, 3, 1, -8},
- /* 'J' 0x4A */ {224, 5, 9, 6, 0, -8},
- /* 'K' 0x4B */ {230, 7, 9, 8, 1, -8},
- /* 'L' 0x4C */ {238, 5, 9, 7, 1, -8},
- /* 'M' 0x4D */ {244, 8, 9, 10, 1, -8},
- /* 'N' 0x4E */ {253, 7, 9, 9, 1, -8},
- /* 'O' 0x4F */ {261, 9, 9, 9, 0, -8},
- /* 'P' 0x50 */ {272, 6, 9, 8, 1, -8},
- /* 'Q' 0x51 */ {279, 9, 10, 9, 0, -8},
- /* 'R' 0x52 */ {291, 7, 9, 9, 1, -8},
- /* 'S' 0x53 */ {299, 6, 9, 8, 1, -8},
- /* 'T' 0x54 */ {306, 7, 9, 8, 0, -8},
- /* 'U' 0x55 */ {314, 7, 9, 9, 1, -8},
- /* 'V' 0x56 */ {322, 7, 9, 8, 0, -8},
- /* 'W' 0x57 */ {330, 11, 9, 11, 0, -8},
- /* 'X' 0x58 */ {343, 6, 9, 8, 1, -8},
- /* 'Y' 0x59 */ {350, 8, 9, 8, 0, -8},
- /* 'Z' 0x5A */ {359, 7, 9, 7, 0, -8},
- /* '[' 0x5B */ {367, 2, 12, 3, 1, -8},
- /* '\' 0x5C */ {370, 3, 9, 3, 0, -8},
- /* ']' 0x5D */ {374, 2, 12, 3, 0, -8},
- /* '^' 0x5E */ {377, 4, 4, 6, 1, -8},
- /* '_' 0x5F */ {379, 7, 1, 7, 0, 2},
- /* '`' 0x60 */ {380, 1, 1, 3, 1, -8},
- /* 'a' 0x61 */ {381, 6, 7, 7, 0, -6},
- /* 'b' 0x62 */ {387, 5, 9, 7, 1, -8},
- /* 'c' 0x63 */ {393, 6, 7, 6, 0, -6},
- /* 'd' 0x64 */ {399, 6, 9, 7, 0, -8},
- /* 'e' 0x65 */ {406, 6, 7, 6, 0, -6},
- /* 'f' 0x66 */ {412, 3, 9, 3, 0, -8},
- /* 'g' 0x67 */ {416, 6, 10, 7, 0, -6},
- /* 'h' 0x68 */ {424, 5, 9, 6, 1, -8},
- /* 'i' 0x69 */ {430, 1, 9, 3, 1, -8},
- /* 'j' 0x6A */ {432, 2, 12, 3, 0, -8},
- /* 'k' 0x6B */ {435, 5, 9, 6, 1, -8},
- /* 'l' 0x6C */ {441, 1, 9, 3, 1, -8},
- /* 'm' 0x6D */ {443, 8, 7, 10, 1, -6},
- /* 'n' 0x6E */ {450, 5, 7, 6, 1, -6},
- /* 'o' 0x6F */ {455, 6, 7, 6, 0, -6},
- /* 'p' 0x70 */ {461, 5, 9, 7, 1, -6},
- /* 'q' 0x71 */ {467, 6, 9, 7, 0, -6},
- /* 'r' 0x72 */ {474, 3, 7, 4, 1, -6},
- /* 's' 0x73 */ {477, 5, 7, 6, 0, -6},
- /* 't' 0x74 */ {482, 3, 8, 3, 0, -7},
- /* 'u' 0x75 */ {485, 5, 7, 6, 1, -6},
- /* 'v' 0x76 */ {490, 6, 7, 6, 0, -6},
- /* 'w' 0x77 */ {496, 8, 7, 9, 0, -6},
- /* 'x' 0x78 */ {503, 5, 7, 6, 0, -6},
- /* 'y' 0x79 */ {508, 5, 10, 6, 0, -6},
- /* 'z' 0x7A */ {515, 5, 7, 6, 0, -6},
- /* '{' 0x7B */ {520, 2, 12, 4, 1, -8},
- /* '|' 0x7C */ {523, 1, 11, 3, 1, -8},
- /* '}' 0x7D */ {525, 2, 12, 4, 1, -8},
- /* '~' 0x7E */ {528, 6, 2, 6, 0, -4},
- /* 0x7F */ {530, 9, 10, 11, 1, -8},
- /* 0x80 */ {542, 7, 9, 8, 0, -8},
- /* 0x81 */ {550, 0, 0, 8, 0, 0},
- /* 0x82 */ {550, 1, 3, 3, 1, 0},
- /* 0x83 */ {551, 3, 12, 3, 0, -8},
- /* 0x84 */ {556, 3, 3, 5, 1, 0},
- /* 0x85 */ {558, 5, 1, 7, 1, 0},
- /* 0x86 */ {559, 5, 11, 7, 1, -8},
- /* 0x87 */ {566, 5, 11, 7, 1, -8},
- /* 0x88 */ {573, 3, 2, 4, 0, -9},
- /* 0x89 */ {574, 12, 9, 12, 0, -8},
- /* 0x8A */ {588, 6, 11, 8, 1, -9},
- /* 0x8B */ {597, 2, 3, 4, 1, -4},
- /* 0x8C */ {598, 11, 9, 12, 0, -8},
- /* 0x8D */ {611, 0, 0, 8, 0, 0},
- /* 0x8E */ {611, 7, 10, 7, 0, -9},
- /* 0x8F */ {620, 0, 0, 8, 0, 0},
- /* 0x90 */ {620, 0, 0, 8, 0, 0},
- /* 0x91 */ {620, 1, 3, 3, 1, -8},
- /* 0x92 */ {621, 1, 3, 2, 1, -8},
- /* 0x93 */ {622, 3, 3, 5, 1, -8},
- /* 0x94 */ {624, 3, 3, 5, 1, -8},
- /* 0x95 */ {626, 3, 3, 5, 1, -5},
- /* 0x96 */ {628, 6, 1, 6, 0, -3},
- /* 0x97 */ {629, 12, 1, 12, 0, -3},
- /* 0x98 */ {631, 4, 2, 4, 0, -8},
- /* 0x99 */ {632, 11, 7, 12, 1, -8},
- /* 0x9A */ {642, 4, 9, 6, 1, -8},
- /* 0x9B */ {647, 2, 3, 3, 1, -4},
- /* 0x9C */ {648, 11, 7, 11, 0, -6},
- /* 0x9D */ {658, 0, 0, 8, 0, 0},
- /* 0x9E */ {658, 5, 9, 6, 0, -8},
- /* 0x9F */ {664, 7, 10, 8, 1, -9},
- /* 0xA0 */ {673, 0, 0, 3, 0, 0},
- /* 0xA1 */ {673, 1, 9, 4, 1, -5},
- /* 0xA2 */ {675, 5, 9, 7, 1, -7},
- /* 0xA3 */ {681, 6, 9, 7, 0, -8},
- /* 0xA4 */ {688, 5, 4, 7, 1, -5},
- /* 0xA5 */ {691, 5, 9, 7, 1, -8},
- /* 0xA6 */ {697, 1, 12, 3, 1, -8},
- /* 0xA7 */ {699, 5, 12, 7, 1, -8},
- /* 0xA8 */ {707, 3, 1, 4, 0, -7},
- /* 0xA9 */ {708, 9, 9, 10, 0, -8},
- /* 0xAA */ {719, 4, 5, 4, 0, -8},
- /* 0xAB */ {722, 4, 4, 6, 1, -4},
- /* 0xAC */ {724, 6, 3, 7, 1, -4},
- /* 0xAD */ {727, 0, 0, 0, 0, 0},
- /* 0xAE */ {727, 9, 9, 10, 0, -8},
- /* 0xAF */ {738, 3, 1, 4, 0, -8},
- /* 0xB0 */ {739, 4, 4, 7, 2, -8},
- /* 0xB1 */ {741, 5, 7, 7, 1, -6},
- /* 0xB2 */ {746, 4, 5, 4, 0, -9},
- /* 0xB3 */ {749, 4, 5, 4, 0, -9},
- /* 0xB4 */ {752, 1, 1, 4, 1, -8},
- /* 0xB5 */ {753, 6, 9, 7, 1, -6},
- /* 0xB6 */ {760, 6, 10, 6, 1, -8},
- /* 0xB7 */ {768, 1, 1, 3, 1, -2},
- /* 0xB8 */ {769, 3, 3, 4, 1, 1},
- /* 0xB9 */ {771, 2, 6, 4, 1, -9},
- /* 0xBA */ {773, 4, 5, 4, 0, -8},
- /* 0xBB */ {776, 4, 4, 6, 1, -5},
- /* 0xBC */ {778, 10, 9, 10, 1, -8},
- /* 0xBD */ {790, 9, 9, 10, 1, -8},
- /* 0xBE */ {801, 10, 9, 11, 0, -8},
- /* 0xBF */ {813, 5, 9, 7, 1, -5},
- /* 0xC0 */ {819, 8, 10, 8, 0, -9},
- /* 0xC1 */ {829, 8, 10, 8, 0, -9},
- /* 0xC2 */ {839, 8, 10, 8, 0, -9},
- /* 0xC3 */ {849, 8, 10, 8, 0, -9},
- /* 0xC4 */ {859, 8, 10, 8, 0, -9},
- /* 0xC5 */ {869, 8, 10, 8, 0, -9},
- /* 0xC6 */ {879, 10, 9, 12, 1, -8},
- /* 0xC7 */ {891, 8, 12, 9, 0, -8},
- /* 0xC8 */ {903, 6, 10, 8, 1, -9},
- /* 0xC9 */ {911, 6, 10, 8, 1, -9},
- /* 0xCA */ {919, 6, 10, 8, 1, -9},
- /* 0xCB */ {927, 6, 10, 8, 1, -9},
- /* 0xCC */ {935, 2, 10, 3, 0, -9},
- /* 0xCD */ {938, 2, 10, 3, 1, -9},
- /* 0xCE */ {941, 3, 10, 4, 0, -9},
- /* 0xCF */ {945, 3, 10, 4, 0, -9},
- /* 0xD0 */ {949, 8, 9, 8, 0, -8},
- /* 0xD1 */ {958, 7, 10, 9, 1, -9},
- /* 0xD2 */ {967, 9, 10, 9, 0, -9},
- /* 0xD3 */ {979, 9, 10, 9, 0, -9},
- /* 0xD4 */ {991, 9, 11, 9, 0, -10},
- /* 0xD5 */ {1004, 9, 11, 9, 0, -10},
- /* 0xD6 */ {1017, 9, 11, 9, 0, -10},
- /* 0xD7 */ {1030, 5, 5, 7, 1, -5},
- /* 0xD8 */ {1034, 9, 9, 9, 0, -8},
- /* 0xD9 */ {1045, 7, 10, 9, 1, -9},
- /* 0xDA */ {1054, 7, 10, 9, 1, -9},
- /* 0xDB */ {1063, 7, 10, 9, 1, -9},
- /* 0xDC */ {1072, 7, 10, 9, 1, -9},
- /* 0xDD */ {1081, 7, 10, 8, 1, -9},
- /* 0xDE */ {1090, 6, 9, 8, 1, -8},
- /* 0xDF */ {1097, 6, 9, 7, 1, -8},
- /* 0xE0 */ {1104, 7, 10, 7, 0, -9},
- /* 0xE1 */ {1113, 7, 10, 7, 0, -9},
- /* 0xE2 */ {1122, 7, 10, 7, 0, -9},
- /* 0xE3 */ {1131, 7, 10, 7, 0, -9},
- /* 0xE4 */ {1140, 7, 9, 7, 0, -8},
- /* 0xE5 */ {1148, 7, 10, 7, 0, -9},
- /* 0xE6 */ {1157, 10, 7, 10, 0, -6},
- /* 0xE7 */ {1166, 6, 10, 6, 0, -6},
- /* 0xE8 */ {1174, 6, 10, 6, 0, -9},
- /* 0xE9 */ {1182, 6, 10, 6, 0, -9},
- /* 0xEA */ {1190, 6, 10, 6, 0, -9},
- /* 0xEB */ {1198, 6, 9, 6, 0, -8},
- /* 0xEC */ {1205, 2, 10, 3, 0, -9},
- /* 0xED */ {1208, 2, 10, 3, 1, -9},
- /* 0xEE */ {1211, 3, 10, 3, 0, -9},
- /* 0xEF */ {1215, 3, 9, 3, 0, -8},
- /* 0xF0 */ {1219, 6, 9, 6, 0, -8},
- /* 0xF1 */ {1226, 5, 10, 6, 1, -9},
- /* 0xF2 */ {1233, 6, 10, 6, 0, -9},
- /* 0xF3 */ {1241, 6, 10, 6, 0, -9},
- /* 0xF4 */ {1249, 6, 10, 6, 0, -9},
- /* 0xF5 */ {1257, 6, 10, 6, 0, -9},
- /* 0xF6 */ {1265, 6, 9, 6, 0, -8},
- /* 0xF7 */ {1272, 5, 5, 7, 1, -5},
- /* 0xF8 */ {1276, 6, 7, 6, 0, -6},
- /* 0xF9 */ {1282, 5, 9, 6, 1, -8},
- /* 0xFA */ {1288, 5, 9, 6, 1, -8},
- /* 0xFB */ {1294, 5, 10, 6, 1, -9},
- /* 0xFC */ {1301, 5, 9, 6, 1, -8},
- /* 0xFD */ {1307, 6, 12, 6, 0, -8},
- /* 0xFE */ {1316, 5, 11, 7, 1, -8},
- /* 0xFF */ {1323, 6, 12, 6, 0, -8},
+/* 0x01 */ { 0, 9, 10, 11, 1, -9 },
+/* 0x02 */ { 12, 9, 10, 11, 1, -8 },
+/* 0x03 */ { 24, 10, 10, 12, 1, -8 },
+/* 0x04 */ { 37, 10, 10, 12, 1, -8 },
+/* 0x05 */ { 50, 10, 10, 12, 1, -9 },
+/* 0x06 */ { 63, 11, 11, 13, 1, -9 },
+/* 0x07 */ { 79, 0, 0, 8, 0, 0 },
+/* 0x08 */ { 79, 12, 9, 14, 1, -8 },
+/* 0x09 */ { 93, 14, 8, 16, 1, -7 },
+/* 0x0A */ { 107, 0, 0, 8, 0, 0 },
+/* 0x0B */ { 107, 9, 10, 11, 1, -9 },
+/* 0x0C */ { 119, 13, 9, 15, 1, -8 },
+/* 0x0D */ { 134, 0, 0, 8, 0, 0 },
+/* 0x0E */ { 134, 9, 11, 11, 1, -9 },
+/* 0x0F */ { 147, 10, 10, 12, 1, -9 },
+/* 0x10 */ { 160, 11, 10, 13, 1, -9 },
+/* 0x11 */ { 174, 13, 10, 15, 1, -9 },
+/* 0x12 */ { 191, 10, 10, 12, 1, -9 },
+/* 0x13 */ { 204, 11, 10, 13, 1, -9 },
+/* 0x14 */ { 218, 10, 10, 12, 1, -9 },
+/* 0x15 */ { 231, 14, 10, 16, 1, -9 },
+/* 0x16 */ { 249, 8, 10, 10, 1, -9 },
+/* 0x17 */ { 259, 12, 10, 14, 1, -9 },
+/* 0x18 */ { 274, 13, 10, 15, 1, -9 },
+/* 0x19 */ { 291, 12, 10, 14, 1, -9 },
+/* 0x1A */ { 306, 9, 10, 11, 1, -8 },
+/* 0x1B */ { 318, 14, 10, 16, 1, -9 },
+/* 0x1C */ { 336, 11, 10, 13, 1, -9 },
+/* 0x1D */ { 350, 11, 10, 13, 1, -9 },
+/* 0x1E */ { 364, 12, 10, 14, 1, -9 },
+/* 0x1F */ { 379, 8, 10, 11, 2, -9 },
+/* ' ' 0x20 */ { 389, 0, 0, 3, 0, 0 },
+/* '!' 0x21 */ { 389, 1, 9, 4, 2, -8 },
+/* '"' 0x22 */ { 391, 3, 3, 4, 0, -8 },
+/* '#' 0x23 */ { 393, 7, 8, 7, 0, -7 },
+/* '$' 0x24 */ { 400, 6, 11, 7, 0, -9 },
+/* '%' 0x25 */ { 409, 10, 9, 11, 0, -8 },
+/* '&' 0x26 */ { 421, 6, 9, 8, 1, -8 },
+/* ''' 0x27 */ { 428, 1, 3, 2, 1, -8 },
+/* '(' 0x28 */ { 429, 2, 11, 4, 1, -8 },
+/* ')' 0x29 */ { 432, 3, 11, 4, 0, -8 },
+/* '*' 0x2A */ { 437, 3, 3, 5, 1, -8 },
+/* '+' 0x2B */ { 439, 5, 5, 7, 1, -4 },
+/* ',' 0x2C */ { 443, 1, 3, 3, 1, 0 },
+/* '-' 0x2D */ { 444, 2, 1, 4, 1, -3 },
+/* '.' 0x2E */ { 445, 1, 1, 3, 1, 0 },
+/* '/' 0x2F */ { 446, 3, 9, 3, 0, -8 },
+/* '0' 0x30 */ { 450, 5, 9, 7, 1, -8 },
+/* '1' 0x31 */ { 456, 3, 9, 7, 1, -8 },
+/* '2' 0x32 */ { 460, 6, 9, 7, 0, -8 },
+/* '3' 0x33 */ { 467, 6, 9, 7, 0, -8 },
+/* '4' 0x34 */ { 474, 6, 9, 7, 0, -8 },
+/* '5' 0x35 */ { 481, 5, 9, 7, 1, -8 },
+/* '6' 0x36 */ { 487, 5, 9, 7, 1, -8 },
+/* '7' 0x37 */ { 493, 5, 9, 7, 1, -8 },
+/* '8' 0x38 */ { 499, 6, 9, 7, 0, -8 },
+/* '9' 0x39 */ { 506, 6, 9, 7, 0, -8 },
+/* ':' 0x3A */ { 513, 1, 7, 3, 1, -6 },
+/* ';' 0x3B */ { 514, 1, 8, 3, 1, -5 },
+/* '<' 0x3C */ { 515, 5, 5, 7, 1, -4 },
+/* '=' 0x3D */ { 519, 5, 3, 7, 1, -3 },
+/* '>' 0x3E */ { 521, 5, 5, 7, 1, -4 },
+/* '?' 0x3F */ { 525, 5, 9, 7, 1, -8 },
+/* '@' 0x40 */ { 531, 11, 11, 12, 0, -8 },
+/* 'A' 0x41 */ { 547, 8, 9, 8, 0, -8 },
+/* 'B' 0x42 */ { 556, 6, 9, 8, 1, -8 },
+/* 'C' 0x43 */ { 563, 8, 9, 9, 0, -8 },
+/* 'D' 0x44 */ { 572, 7, 9, 8, 1, -8 },
+/* 'E' 0x45 */ { 580, 6, 9, 8, 1, -8 },
+/* 'F' 0x46 */ { 587, 6, 9, 7, 1, -8 },
+/* 'G' 0x47 */ { 594, 8, 9, 9, 0, -8 },
+/* 'H' 0x48 */ { 603, 7, 9, 9, 1, -8 },
+/* 'I' 0x49 */ { 611, 1, 9, 3, 1, -8 },
+/* 'J' 0x4A */ { 613, 5, 9, 6, 0, -8 },
+/* 'K' 0x4B */ { 619, 7, 9, 8, 1, -8 },
+/* 'L' 0x4C */ { 627, 5, 9, 7, 1, -8 },
+/* 'M' 0x4D */ { 633, 8, 9, 10, 1, -8 },
+/* 'N' 0x4E */ { 642, 7, 9, 9, 1, -8 },
+/* 'O' 0x4F */ { 650, 9, 9, 9, 0, -8 },
+/* 'P' 0x50 */ { 661, 6, 9, 8, 1, -8 },
+/* 'Q' 0x51 */ { 668, 9, 10, 9, 0, -8 },
+/* 'R' 0x52 */ { 680, 7, 9, 9, 1, -8 },
+/* 'S' 0x53 */ { 688, 6, 9, 8, 1, -8 },
+/* 'T' 0x54 */ { 695, 7, 9, 8, 0, -8 },
+/* 'U' 0x55 */ { 703, 7, 9, 9, 1, -8 },
+/* 'V' 0x56 */ { 711, 7, 9, 8, 0, -8 },
+/* 'W' 0x57 */ { 719, 11, 9, 11, 0, -8 },
+/* 'X' 0x58 */ { 732, 6, 9, 8, 1, -8 },
+/* 'Y' 0x59 */ { 739, 8, 9, 8, 0, -8 },
+/* 'Z' 0x5A */ { 748, 7, 9, 7, 0, -8 },
+/* '[' 0x5B */ { 756, 2, 12, 3, 1, -8 },
+/* '\' 0x5C */ { 759, 3, 9, 3, 0, -8 },
+/* ']' 0x5D */ { 763, 2, 12, 3, 0, -8 },
+/* '^' 0x5E */ { 766, 4, 4, 6, 1, -8 },
+/* '_' 0x5F */ { 768, 7, 1, 7, 0, 2 },
+/* '`' 0x60 */ { 769, 1, 1, 3, 1, -8 },
+/* 'a' 0x61 */ { 770, 6, 7, 7, 0, -6 },
+/* 'b' 0x62 */ { 776, 5, 9, 7, 1, -8 },
+/* 'c' 0x63 */ { 782, 6, 7, 6, 0, -6 },
+/* 'd' 0x64 */ { 788, 6, 9, 7, 0, -8 },
+/* 'e' 0x65 */ { 795, 6, 7, 6, 0, -6 },
+/* 'f' 0x66 */ { 801, 3, 9, 3, 0, -8 },
+/* 'g' 0x67 */ { 805, 6, 10, 7, 0, -6 },
+/* 'h' 0x68 */ { 813, 5, 9, 6, 1, -8 },
+/* 'i' 0x69 */ { 819, 1, 9, 3, 1, -8 },
+/* 'j' 0x6A */ { 821, 2, 12, 3, 0, -8 },
+/* 'k' 0x6B */ { 824, 5, 9, 6, 1, -8 },
+/* 'l' 0x6C */ { 830, 1, 9, 3, 1, -8 },
+/* 'm' 0x6D */ { 832, 8, 7, 10, 1, -6 },
+/* 'n' 0x6E */ { 839, 5, 7, 6, 1, -6 },
+/* 'o' 0x6F */ { 844, 6, 7, 6, 0, -6 },
+/* 'p' 0x70 */ { 850, 5, 9, 7, 1, -6 },
+/* 'q' 0x71 */ { 856, 6, 9, 7, 0, -6 },
+/* 'r' 0x72 */ { 863, 3, 7, 4, 1, -6 },
+/* 's' 0x73 */ { 866, 5, 7, 6, 0, -6 },
+/* 't' 0x74 */ { 871, 3, 8, 3, 0, -7 },
+/* 'u' 0x75 */ { 874, 5, 7, 6, 1, -6 },
+/* 'v' 0x76 */ { 879, 6, 7, 6, 0, -6 },
+/* 'w' 0x77 */ { 885, 8, 7, 9, 0, -6 },
+/* 'x' 0x78 */ { 892, 5, 7, 6, 0, -6 },
+/* 'y' 0x79 */ { 897, 5, 10, 6, 0, -6 },
+/* 'z' 0x7A */ { 904, 5, 7, 6, 0, -6 },
+/* '{' 0x7B */ { 909, 2, 12, 4, 1, -8 },
+/* '|' 0x7C */ { 912, 1, 11, 3, 1, -8 },
+/* '}' 0x7D */ { 914, 2, 12, 4, 1, -8 },
+/* '~' 0x7E */ { 917, 6, 2, 6, 0, -4 },
+/* 0x7F */ { 919, 0, 0, 0, 0, 0 },
+/* 0x80 */ { 919, 7, 9, 8, 0, -8 },
+/* 0x81 */ { 927, 0, 0, 8, 0, 0 },
+/* 0x82 */ { 927, 1, 3, 3, 1, 0 },
+/* 0x83 */ { 928, 3, 12, 3, 0, -8 },
+/* 0x84 */ { 933, 3, 3, 5, 1, 0 },
+/* 0x85 */ { 935, 5, 1, 7, 1, 0 },
+/* 0x86 */ { 936, 5, 11, 7, 1, -8 },
+/* 0x87 */ { 943, 5, 11, 7, 1, -8 },
+/* 0x88 */ { 950, 3, 2, 4, 0, -9 },
+/* 0x89 */ { 951, 12, 9, 12, 0, -8 },
+/* 0x8A */ { 965, 6, 11, 8, 1, -9 },
+/* 0x8B */ { 974, 2, 3, 4, 1, -4 },
+/* 0x8C */ { 975, 11, 9, 12, 0, -8 },
+/* 0x8D */ { 988, 0, 0, 8, 0, 0 },
+/* 0x8E */ { 988, 7, 10, 7, 0, -9 },
+/* 0x8F */ { 997, 0, 0, 8, 0, 0 },
+/* 0x90 */ { 997, 0, 0, 8, 0, 0 },
+/* 0x91 */ { 997, 1, 3, 3, 1, -8 },
+/* 0x92 */ { 998, 1, 3, 2, 1, -8 },
+/* 0x93 */ { 999, 3, 3, 5, 1, -8 },
+/* 0x94 */ { 1001, 3, 3, 5, 1, -8 },
+/* 0x95 */ { 1003, 3, 3, 5, 1, -5 },
+/* 0x96 */ { 1005, 6, 1, 6, 0, -3 },
+/* 0x97 */ { 1006, 12, 1, 12, 0, -3 },
+/* 0x98 */ { 1008, 4, 2, 4, 0, -8 },
+/* 0x99 */ { 1009, 11, 7, 12, 1, -8 },
+/* 0x9A */ { 1019, 4, 9, 6, 1, -8 },
+/* 0x9B */ { 1024, 2, 3, 3, 1, -4 },
+/* 0x9C */ { 1025, 11, 7, 11, 0, -6 },
+/* 0x9D */ { 1035, 0, 0, 8, 0, 0 },
+/* 0x9E */ { 1035, 5, 9, 6, 0, -8 },
+/* 0x9F */ { 1041, 7, 10, 8, 1, -9 },
+/* 0xA0 */ { 1050, 0, 0, 3, 0, 0 },
+/* 0xA1 */ { 1050, 1, 9, 4, 1, -5 },
+/* 0xA2 */ { 1052, 5, 9, 7, 1, -7 },
+/* 0xA3 */ { 1058, 6, 9, 7, 0, -8 },
+/* 0xA4 */ { 1065, 5, 4, 7, 1, -5 },
+/* 0xA5 */ { 1068, 5, 9, 7, 1, -8 },
+/* 0xA6 */ { 1074, 1, 12, 3, 1, -8 },
+/* 0xA7 */ { 1076, 5, 12, 7, 1, -8 },
+/* 0xA8 */ { 1084, 3, 1, 4, 0, -7 },
+/* 0xA9 */ { 1085, 9, 9, 10, 0, -8 },
+/* 0xAA */ { 1096, 4, 5, 4, 0, -8 },
+/* 0xAB */ { 1099, 4, 4, 6, 1, -4 },
+/* 0xAC */ { 1101, 6, 3, 7, 1, -4 },
+/* 0xAD */ { 1104, 0, 0, 0, 0, 0 },
+/* 0xAE */ { 1104, 9, 9, 10, 0, -8 },
+/* 0xAF */ { 1115, 3, 1, 4, 0, -8 },
+/* 0xB0 */ { 1116, 4, 4, 7, 2, -8 },
+/* 0xB1 */ { 1118, 5, 7, 7, 1, -6 },
+/* 0xB2 */ { 1123, 4, 5, 4, 0, -9 },
+/* 0xB3 */ { 1126, 4, 5, 4, 0, -9 },
+/* 0xB4 */ { 1129, 1, 1, 4, 1, -8 },
+/* 0xB5 */ { 1130, 6, 9, 7, 1, -6 },
+/* 0xB6 */ { 1137, 6, 10, 6, 1, -8 },
+/* 0xB7 */ { 1145, 1, 1, 3, 1, -2 },
+/* 0xB8 */ { 1146, 3, 3, 4, 1, 1 },
+/* 0xB9 */ { 1148, 2, 6, 4, 1, -9 },
+/* 0xBA */ { 1150, 4, 5, 4, 0, -8 },
+/* 0xBB */ { 1153, 4, 4, 6, 1, -5 },
+/* 0xBC */ { 1155, 10, 9, 10, 1, -8 },
+/* 0xBD */ { 1167, 9, 9, 10, 1, -8 },
+/* 0xBE */ { 1178, 10, 9, 11, 0, -8 },
+/* 0xBF */ { 1190, 5, 9, 7, 1, -5 },
+/* 0xC0 */ { 1196, 8, 10, 8, 0, -9 },
+/* 0xC1 */ { 1206, 8, 10, 8, 0, -9 },
+/* 0xC2 */ { 1216, 8, 10, 8, 0, -9 },
+/* 0xC3 */ { 1226, 8, 10, 8, 0, -9 },
+/* 0xC4 */ { 1236, 8, 10, 8, 0, -9 },
+/* 0xC5 */ { 1246, 8, 10, 8, 0, -9 },
+/* 0xC6 */ { 1256, 10, 9, 12, 1, -8 },
+/* 0xC7 */ { 1268, 8, 12, 9, 0, -8 },
+/* 0xC8 */ { 1280, 6, 10, 8, 1, -9 },
+/* 0xC9 */ { 1288, 6, 10, 8, 1, -9 },
+/* 0xCA */ { 1296, 6, 10, 8, 1, -9 },
+/* 0xCB */ { 1304, 6, 10, 8, 1, -9 },
+/* 0xCC */ { 1312, 2, 10, 3, 0, -9 },
+/* 0xCD */ { 1315, 2, 10, 3, 1, -9 },
+/* 0xCE */ { 1318, 3, 10, 4, 0, -9 },
+/* 0xCF */ { 1322, 3, 10, 4, 0, -9 },
+/* 0xD0 */ { 1326, 8, 9, 8, 0, -8 },
+/* 0xD1 */ { 1335, 7, 10, 9, 1, -9 },
+/* 0xD2 */ { 1344, 9, 10, 9, 0, -9 },
+/* 0xD3 */ { 1356, 9, 10, 9, 0, -9 },
+/* 0xD4 */ { 1368, 9, 11, 9, 0, -10 },
+/* 0xD5 */ { 1381, 9, 11, 9, 0, -10 },
+/* 0xD6 */ { 1394, 9, 11, 9, 0, -10 },
+/* 0xD7 */ { 1407, 5, 5, 7, 1, -5 },
+/* 0xD8 */ { 1411, 9, 9, 9, 0, -8 },
+/* 0xD9 */ { 1422, 7, 10, 9, 1, -9 },
+/* 0xDA */ { 1431, 7, 10, 9, 1, -9 },
+/* 0xDB */ { 1440, 7, 10, 9, 1, -9 },
+/* 0xDC */ { 1449, 7, 10, 9, 1, -9 },
+/* 0xDD */ { 1458, 7, 10, 8, 1, -9 },
+/* 0xDE */ { 1467, 6, 9, 8, 1, -8 },
+/* 0xDF */ { 1474, 6, 9, 7, 1, -8 },
+/* 0xE0 */ { 1481, 7, 10, 7, 0, -9 },
+/* 0xE1 */ { 1490, 7, 10, 7, 0, -9 },
+/* 0xE2 */ { 1499, 7, 10, 7, 0, -9 },
+/* 0xE3 */ { 1508, 7, 10, 7, 0, -9 },
+/* 0xE4 */ { 1517, 7, 9, 7, 0, -8 },
+/* 0xE5 */ { 1525, 7, 10, 7, 0, -9 },
+/* 0xE6 */ { 1534, 10, 7, 10, 0, -6 },
+/* 0xE7 */ { 1543, 6, 10, 6, 0, -6 },
+/* 0xE8 */ { 1551, 6, 10, 6, 0, -9 },
+/* 0xE9 */ { 1559, 6, 10, 6, 0, -9 },
+/* 0xEA */ { 1567, 6, 10, 6, 0, -9 },
+/* 0xEB */ { 1575, 6, 9, 6, 0, -8 },
+/* 0xEC */ { 1582, 2, 10, 3, 0, -9 },
+/* 0xED */ { 1585, 2, 10, 3, 1, -9 },
+/* 0xEE */ { 1588, 3, 10, 3, 0, -9 },
+/* 0xEF */ { 1592, 3, 9, 3, 0, -8 },
+/* 0xF0 */ { 1596, 6, 9, 6, 0, -8 },
+/* 0xF1 */ { 1603, 5, 10, 6, 1, -9 },
+/* 0xF2 */ { 1610, 6, 10, 6, 0, -9 },
+/* 0xF3 */ { 1618, 6, 10, 6, 0, -9 },
+/* 0xF4 */ { 1626, 6, 10, 6, 0, -9 },
+/* 0xF5 */ { 1634, 6, 10, 6, 0, -9 },
+/* 0xF6 */ { 1642, 6, 9, 6, 0, -8 },
+/* 0xF7 */ { 1649, 5, 5, 7, 1, -5 },
+/* 0xF8 */ { 1653, 6, 7, 6, 0, -6 },
+/* 0xF9 */ { 1659, 5, 9, 6, 1, -8 },
+/* 0xFA */ { 1665, 5, 9, 6, 1, -8 },
+/* 0xFB */ { 1671, 5, 10, 6, 1, -9 },
+/* 0xFC */ { 1678, 5, 9, 6, 1, -8 },
+/* 0xFD */ { 1684, 6, 12, 6, 0, -8 },
+/* 0xFE */ { 1693, 5, 11, 7, 1, -8 },
+/* 0xFF */ { 1700, 6, 12, 6, 0, -8 },
};
-const GFXfont FreeSans6pt_Win1252 PROGMEM = {(uint8_t *)FreeSans6pt_Win1252Bitmaps, (GFXglyph *)FreeSans6pt_Win1252Glyphs, 0x20,
- 0xFF, 14};
+const GFXfont FreeSans6pt_Win1252 PROGMEM = {
+(uint8_t*)FreeSans6pt_Win1252Bitmaps,
+(GFXglyph*)FreeSans6pt_Win1252Glyphs,
+0x01, 0xFF, 10
+};
diff --git a/src/graphics/niche/Fonts/FreeSans9pt_Win1250.h b/src/graphics/niche/Fonts/FreeSans9pt_Win1250.h
index 7022939a0..dd66801a1 100644
--- a/src/graphics/niche/Fonts/FreeSans9pt_Win1250.h
+++ b/src/graphics/niche/Fonts/FreeSans9pt_Win1250.h
@@ -1,494 +1,527 @@
+// trunk-ignore-all(clang-format)
#pragma once
+/* PROPERTIES
+
+FONT_NAME FreeSans9pt_Win1250
+*/
const uint8_t FreeSans9pt_Win1250Bitmaps[] PROGMEM = {
- /* ' ' 0x20 */
- 0xFF, 0xFF, 0xF0, 0xC0, /* '!' 0x21 */
- 0xDE, 0xF7, 0x20, /* '"' 0x22 */
- 0x09, 0x86, 0x41, 0x91, 0xFF, 0x13, 0x04, 0xC3, 0x20, 0xC8, 0xFF, 0x89, 0x82, 0x61, 0x90, /* '#' 0x23 */
- 0x10, 0x1F, 0x14, 0xDA, 0x3D, 0x1E, 0x83, 0x40, 0x78, 0x17, 0x08, 0xF4, 0x7A, 0x35, 0x33, 0xF0, 0x40, 0x20, /* '$' 0x24 */
- 0x38, 0x10, 0xEC, 0x20, 0xC6, 0x20, 0xC6, 0x40, 0xC6, 0x40, 0x6C, 0x80, 0x39, 0x00, 0x01, 0x3C, 0x02, 0x77, 0x02, 0x63, 0x04,
- 0x63, 0x04, 0x77, 0x08, 0x3C, /* '%' 0x25 */
- 0x0E, 0x0C, 0xC3, 0x30, 0xCC, 0x1E, 0x03, 0x03, 0xC1, 0x9B, 0xC2, 0xF0, 0xEC, 0x19, 0x8F, 0x3C, 0x40, /* '&' 0x26 */
- 0xFE, /* ''' 0x27 */
- 0x13, 0x26, 0x6C, 0xCC, 0xCC, 0xC4, 0x66, 0x23, 0x10, /* '(' 0x28 */
- 0x8C, 0x46, 0x63, 0x33, 0x33, 0x32, 0x66, 0x4C, 0x80, /* ')' 0x29 */
- 0x25, 0x7E, 0xA5, 0x00, /* '*' 0x2A */
- 0x30, 0xC3, 0x3F, 0x30, 0xC3, 0x0C, /* '+' 0x2B */
- 0xD6, /* ',' 0x2C */
- 0xF0, /* '-' 0x2D */
- 0xC0, /* '.' 0x2E */
- 0x08, 0x44, 0x21, 0x10, 0x84, 0x42, 0x11, 0x08, 0x00, /* '/' 0x2F */
- 0x3C, 0x66, 0x42, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x42, 0x66, 0x3C, /* '0' 0x30 */
- 0x11, 0x3F, 0x33, 0x33, 0x33, 0x33, 0x30, /* '1' 0x31 */
- 0x3E, 0x31, 0xB0, 0x78, 0x30, 0x18, 0x1C, 0x1C, 0x1C, 0x18, 0x18, 0x10, 0x08, 0x07, 0xF8, /* '2' 0x32 */
- 0x3C, 0x66, 0xC3, 0xC3, 0x03, 0x06, 0x1C, 0x07, 0x03, 0xC3, 0xC3, 0x66, 0x3C, /* '3' 0x33 */
- 0x0C, 0x18, 0x71, 0x62, 0xC9, 0xA3, 0x46, 0xFE, 0x18, 0x30, 0x60, 0xC0, /* '4' 0x34 */
- 0x7F, 0x20, 0x10, 0x08, 0x08, 0x07, 0xF3, 0x8C, 0x03, 0x01, 0x80, 0xF0, 0x6C, 0x63, 0xE0, /* '5' 0x35 */
- 0x1E, 0x31, 0x98, 0x78, 0x0C, 0x06, 0xF3, 0x8D, 0x83, 0xC1, 0xE0, 0xD0, 0x6C, 0x63, 0xE0, /* '6' 0x36 */
- 0xFF, 0x03, 0x02, 0x06, 0x04, 0x0C, 0x08, 0x18, 0x18, 0x18, 0x10, 0x30, 0x30, /* '7' 0x37 */
- 0x3E, 0x31, 0xB0, 0x78, 0x3C, 0x1B, 0x18, 0xF8, 0xC6, 0xC1, 0xE0, 0xF0, 0x6C, 0x63, 0xE0, /* '8' 0x38 */
- 0x3C, 0x66, 0xC2, 0xC3, 0xC3, 0xC3, 0x67, 0x3B, 0x03, 0x03, 0xC2, 0x66, 0x3C, /* '9' 0x39 */
- 0xC0, 0x00, 0x30, /* ':' 0x3A */
- 0xC0, 0x00, 0x00, 0x64, 0xA0, /* ';' 0x3B */
- 0x00, 0x81, 0xC7, 0x8E, 0x0C, 0x07, 0x80, 0x70, 0x0E, 0x01, 0x80, /* '<' 0x3C */
- 0xFF, 0x80, 0x00, 0x1F, 0xF0, /* '=' 0x3D */
- 0xE0, 0x1C, 0x03, 0x80, 0x30, 0x70, 0xE3, 0x81, 0x00, /* '>' 0x3E */
- 0x3E, 0x31, 0xB0, 0x78, 0x30, 0x18, 0x18, 0x38, 0x18, 0x18, 0x0C, 0x00, 0x00, 0x01, 0x80, /* '?' 0x3F */
- 0x03, 0xF0, 0x06, 0x0E, 0x06, 0x01, 0x86, 0x00, 0x66, 0x1D, 0xBB, 0x31, 0xCF, 0x18, 0xC7, 0x98, 0x63, 0xCC, 0x31, 0xE6, 0x11,
- 0xB3, 0x99, 0xCC, 0xF7, 0x86, 0x00, 0x01, 0x80, 0x00, 0x70, 0x40, 0x0F, 0xE0, /* '@' 0x40 */
- 0x06, 0x00, 0xF0, 0x0F, 0x00, 0x90, 0x19, 0x81, 0x98, 0x10, 0x83, 0x0C, 0x3F, 0xC2, 0x04, 0x60, 0x66, 0x06, 0xC0,
- 0x30, /* 'A' 0x41 */
- 0xFF, 0x18, 0x33, 0x03, 0x60, 0x6C, 0x0D, 0x83, 0x3F, 0xC6, 0x06, 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x6F, 0xF8, /* 'B' 0x42 */
- 0x1F, 0x86, 0x19, 0x81, 0xA0, 0x3C, 0x01, 0x80, 0x30, 0x06, 0x00, 0xC0, 0x68, 0x0D, 0x83, 0x18, 0x61, 0xF0, /* 'C' 0x43 */
- 0xFF, 0x18, 0x33, 0x03, 0x60, 0x3C, 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0x78, 0x0F, 0x03, 0x60, 0xCF, 0xF0, /* 'D' 0x44 */
- 0xFF, 0xE0, 0x30, 0x18, 0x0C, 0x06, 0x03, 0xFD, 0x80, 0xC0, 0x60, 0x30, 0x18, 0x0F, 0xF8, /* 'E' 0x45 */
- 0xFF, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xFE, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, /* 'F' 0x46 */
- 0x0F, 0x83, 0x0E, 0x60, 0x66, 0x03, 0xC0, 0x0C, 0x00, 0xC1, 0xFC, 0x03, 0xC0, 0x36, 0x03, 0x60, 0x73, 0x0F, 0x0F,
- 0x10, /* 'G' 0x47 */
- 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xFF, 0xFE, 0x03, 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x3C, 0x06, /* 'H' 0x48 */
- 0xFF, 0xFF, 0xFF, 0xC0, /* 'I' 0x49 */
- 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC1, 0x83, 0x07, 0x8F, 0x1E, 0x27, 0x80, /* 'J' 0x4A */
- 0xC0, 0xF0, 0x6C, 0x33, 0x18, 0xCC, 0x37, 0x0F, 0xC3, 0x98, 0xC3, 0x30, 0xCC, 0x1B, 0x03, 0xC0, 0xC0, /* 'K' 0x4B */
- 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xFF, /* 'L' 0x4C */
- 0xE0, 0x3F, 0x01, 0xFC, 0x1F, 0xE0, 0xFD, 0x05, 0xEC, 0x6F, 0x63, 0x79, 0x13, 0xCD, 0x9E, 0x6C, 0xF1, 0x47, 0x8E, 0x3C, 0x71,
- 0x80, /* 'M' 0x4D */
- 0xE0, 0x7C, 0x0F, 0xC1, 0xE8, 0x3D, 0x87, 0x98, 0xF1, 0x1E, 0x33, 0xC3, 0x78, 0x6F, 0x07, 0xE0, 0x7C, 0x0E, /* 'N' 0x4E */
- 0x0F, 0x81, 0x83, 0x18, 0x0C, 0xC0, 0x6C, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x03, 0xC0, 0x1B, 0x01, 0x98, 0x0C, 0x60, 0xC0, 0xF8,
- 0x00, /* 'O' 0x4F */
- 0xFF, 0x30, 0x6C, 0x0F, 0x03, 0xC0, 0xF0, 0x6F, 0xF3, 0x00, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x00, /* 'P' 0x50 */
- 0x0F, 0x81, 0x83, 0x18, 0x0C, 0xC0, 0x6C, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x03, 0xC0, 0x1B, 0x01, 0x98, 0x6C, 0x60, 0xC0, 0xFB,
- 0x00, 0x08, /* 'Q' 0x51 */
- 0xFF, 0x8C, 0x0E, 0xC0, 0x6C, 0x06, 0xC0, 0x6C, 0x0C, 0xFF, 0x8C, 0x0E, 0xC0, 0x6C, 0x06, 0xC0, 0x6C, 0x06, 0xC0,
- 0x70, /* 'R' 0x52 */
- 0x3F, 0x18, 0x6C, 0x0F, 0x03, 0xC0, 0x1E, 0x01, 0xF0, 0x0E, 0x00, 0xF0, 0x3C, 0x0D, 0x86, 0x3F, 0x00, /* 'S' 0x53 */
- 0xFF, 0x86, 0x03, 0x01, 0x80, 0xC0, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x01, 0x80, 0xC0, /* 'T' 0x54 */
- 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0x78, 0x0F, 0x01, 0xB0, 0x61, 0xF0, /* 'U' 0x55 */
- 0xC0, 0x6C, 0x0D, 0x81, 0x10, 0x63, 0x0C, 0x61, 0x04, 0x60, 0xCC, 0x19, 0x01, 0x60, 0x3C, 0x07, 0x00, 0x60, /* 'V' 0x56 */
- 0xC1, 0x81, 0x61, 0xC3, 0x61, 0xC3, 0x61, 0x43, 0x62, 0x62, 0x22, 0x66, 0x32, 0x26, 0x36, 0x26, 0x14, 0x34, 0x14, 0x34, 0x1C,
- 0x1C, 0x18, 0x1C, 0x08, 0x18, /* 'W' 0x57 */
- 0xC0, 0xD8, 0x66, 0x18, 0xCC, 0x1E, 0x07, 0x00, 0xC0, 0x78, 0x32, 0x0C, 0xC6, 0x1B, 0x07, 0xC0, 0xC0, /* 'X' 0x58 */
- 0xC0, 0x36, 0x06, 0x30, 0xC3, 0x0C, 0x19, 0x81, 0xD8, 0x0F, 0x00, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60, 0x06,
- 0x00, /* 'Y' 0x59 */
- 0xFF, 0xC0, 0x60, 0x30, 0x0C, 0x06, 0x03, 0x01, 0xC0, 0x60, 0x30, 0x18, 0x06, 0x03, 0x00, 0xFF, 0xC0, /* 'Z' 0x5A */
- 0xFB, 0x6D, 0xB6, 0xDB, 0x6D, 0xB6, 0xE0, /* '[' 0x5B */
- 0x84, 0x10, 0x84, 0x10, 0x84, 0x10, 0x84, 0x10, 0x80, /* '\' 0x5C */
- 0xED, 0xB6, 0xDB, 0x6D, 0xB6, 0xDB, 0xE0, /* ']' 0x5D */
- 0x30, 0x60, 0xA2, 0x44, 0xD8, 0xA1, 0x80, /* '^' 0x5E */
- 0xFF, 0xC0, /* '_' 0x5F */
- 0xC6, 0x30, /* '`' 0x60 */
- 0x7E, 0x71, 0xB0, 0xC0, 0x60, 0xF3, 0xDB, 0x0D, 0x86, 0xC7, 0x3D, 0xC0, /* 'a' 0x61 */
- 0xC0, 0x60, 0x30, 0x1B, 0xCE, 0x36, 0x0F, 0x07, 0x83, 0xC1, 0xE0, 0xF0, 0x7C, 0x6D, 0xE0, /* 'b' 0x62 */
- 0x3C, 0x66, 0xC3, 0xC0, 0xC0, 0xC0, 0xC0, 0xC3, 0x66, 0x3C, /* 'c' 0x63 */
- 0x03, 0x03, 0x03, 0x3B, 0x67, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x67, 0x3B, /* 'd' 0x64 */
- 0x3C, 0x66, 0xC3, 0xC3, 0xFF, 0xC0, 0xC0, 0xC3, 0x66, 0x3C, /* 'e' 0x65 */
- 0x36, 0x6F, 0x66, 0x66, 0x66, 0x66, 0x60, /* 'f' 0x66 */
- 0x3B, 0x67, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x67, 0x3B, 0x03, 0x03, 0xC6, 0x7C, /* 'g' 0x67 */
- 0xC0, 0xC0, 0xC0, 0xDE, 0xE3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, /* 'h' 0x68 */
- 0xC3, 0xFF, 0xFF, 0xC0, /* 'i' 0x69 */
- 0x30, 0x03, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0xE0, /* 'j' 0x6A */
- 0xC0, 0xC0, 0xC0, 0xC2, 0xC4, 0xCC, 0xD8, 0xF8, 0xEC, 0xC4, 0xC6, 0xC3, 0xC3, /* 'k' 0x6B */
- 0xFF, 0xFF, 0xFF, 0xC0, /* 'l' 0x6C */
- 0xDE, 0xF7, 0x1C, 0xF0, 0xC7, 0x86, 0x3C, 0x31, 0xE1, 0x8F, 0x0C, 0x78, 0x63, 0xC3, 0x1E, 0x18, 0xC0, /* 'm' 0x6D */
- 0xDE, 0xE3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, /* 'n' 0x6E */
- 0x3C, 0x66, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x66, 0x3C, /* 'o' 0x6F */
- 0xDE, 0x71, 0xB0, 0x78, 0x3C, 0x1E, 0x0F, 0x07, 0x83, 0xE3, 0x6F, 0x30, 0x18, 0x0C, 0x00, /* 'p' 0x70 */
- 0x3B, 0x67, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x67, 0x3B, 0x03, 0x03, 0x03, /* 'q' 0x71 */
- 0xDF, 0x31, 0x8C, 0x63, 0x18, 0xC6, 0x00, /* 'r' 0x72 */
- 0x3E, 0xE3, 0xC0, 0xC0, 0xE0, 0x3C, 0x07, 0xC3, 0xE3, 0x7E, /* 's' 0x73 */
- 0x66, 0xF6, 0x66, 0x66, 0x66, 0x67, /* 't' 0x74 */
- 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC7, 0x7B, /* 'u' 0x75 */
- 0xC1, 0xA0, 0x98, 0xCC, 0x42, 0x21, 0xB0, 0xD0, 0x28, 0x1C, 0x0C, 0x00, /* 'v' 0x76 */
- 0xC6, 0x1E, 0x38, 0x91, 0xC4, 0xCA, 0x66, 0xD3, 0x16, 0xD0, 0xA6, 0x87, 0x1C, 0x38, 0xC0, 0xC6, 0x00, /* 'w' 0x77 */
- 0x87, 0x89, 0xB1, 0xC3, 0x07, 0x1E, 0x26, 0xC5, 0x0C, /* 'x' 0x78 */
- 0xC1, 0x43, 0x63, 0x62, 0x26, 0x36, 0x34, 0x1C, 0x1C, 0x18, 0x18, 0x18, 0x10, 0x60, /* 'y' 0x79 */
- 0xFE, 0x0C, 0x30, 0xC1, 0x86, 0x18, 0x20, 0xC1, 0xFC, /* 'z' 0x7A */
- 0x36, 0x66, 0x66, 0x6E, 0xCE, 0x66, 0x66, 0x66, 0x30, /* '{' 0x7B */
- 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, /* '|' 0x7C */
- 0xC6, 0x66, 0x66, 0x67, 0x37, 0x66, 0x66, 0x66, 0xC0, /* '}' 0x7D */
- 0x61, 0x24, 0x38, /* '~' 0x7E */
- 0xFF, 0xFC, 0x00, 0x63, 0xE3, 0x31, 0x99, 0x04, 0xC8, 0x66, 0x06, 0x30, 0x61, 0x83, 0x0C, 0x18, 0x60, 0x03, 0x06, 0x18, 0x00,
- 0xFF, 0xFC, /* 0x7F */
- 0x07, 0xC6, 0x13, 0x00, 0xC0, 0x60, 0x3F, 0xE6, 0x03, 0xFC, 0x60, 0x0C, 0x03, 0x00, 0x61, 0x07, 0xC0, /* 0x80 */
- /* 0x81 */
- 0xDC, /* 0x82 */
- /* 0x83 */
- 0xDA, 0x76, /* 0x84 */
- 0xCC, 0xC0, /* 0x85 */
- 0x18, 0x18, 0x18, 0x18, 0xFF, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, /* 0x86 */
- 0x18, 0x18, 0x18, 0x18, 0xFF, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xFF, 0x18, 0x18, 0x18, 0x18, /* 0x87 */
- /* 0x88 */
- 0x70, 0x80, 0x22, 0x20, 0x08, 0x90, 0x02, 0x24, 0x00, 0x72, 0x00, 0x00, 0x80, 0x00, 0x40, 0x00, 0x10, 0x00, 0x09, 0xC7, 0x84,
- 0x8B, 0x31, 0x22, 0x84, 0x88, 0xB3, 0x21, 0xC7, 0x80, /* 0x89 */
- 0x1B, 0x03, 0x83, 0xF1, 0x86, 0xC0, 0xF0, 0x3C, 0x01, 0xE0, 0x1F, 0x00, 0xE0, 0x0F, 0x03, 0xC0, 0xD8, 0x63, 0xF0, /* 0x8A */
- 0x69, /* 0x8B */
- 0x06, 0x03, 0x03, 0xF1, 0x86, 0xC0, 0xF0, 0x3C, 0x01, 0xE0, 0x1F, 0x00, 0xE0, 0x0F, 0x03, 0xC0, 0xD8, 0x63, 0xF0, /* 0x8C */
- 0x33, 0x0F, 0x3F, 0xE1, 0x80, 0xC0, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x01, 0x80, 0xC0, 0x60, 0x30, /* 0x8D */
- 0x1B, 0x03, 0x8F, 0xFC, 0x06, 0x03, 0x00, 0xC0, 0x60, 0x30, 0x1C, 0x06, 0x03, 0x01, 0x80, 0x60, 0x30, 0x0F, 0xFC, /* 0x8E */
- 0x0C, 0x06, 0x0F, 0xFC, 0x06, 0x03, 0x00, 0xC0, 0x60, 0x30, 0x1C, 0x06, 0x03, 0x01, 0x80, 0x60, 0x30, 0x0F, 0xFC, /* 0x8F */
- /* 0x90 */
- 0x6B, /* 0x91 */
- 0xD6, /* 0x92 */
- 0x4C, 0xA5, 0xB0, /* 0x93 */
- 0xDA, 0x53, 0x20, /* 0x94 */
- 0x6F, 0xFF, 0x60, /* 0x95 */
- 0xFE, /* 0x96 */
- 0xFF, 0xFF, /* 0x97 */
- /* 0x98 */
- 0xFC, 0xE1, 0xCC, 0x38, 0x73, 0x0E, 0x1C, 0xC3, 0x8F, 0x30, 0xD2, 0xCC, 0x34, 0xB3, 0x0D, 0x6C, 0xC3, 0x53, 0x30, 0xCC, 0xCC,
- 0x33, 0x30, /* 0x99 */
- 0x24, 0x3C, 0x18, 0x7E, 0xE3, 0xC0, 0xC0, 0x60, 0x3C, 0x07, 0xC3, 0xE3, 0x7E, /* 0x9A */
- 0x96, /* 0x9B */
- 0x0C, 0x18, 0x10, 0x3E, 0xE3, 0xC0, 0xC0, 0xE0, 0x3C, 0x07, 0xC3, 0xE3, 0x7E, /* 0x9C */
- 0x0D, 0xA7, 0x3C, 0x61, 0x86, 0x18, 0x61, 0x86, 0x18, 0x70, /* 0x9D */
- 0x48, 0xF0, 0xC7, 0xF0, 0x61, 0x86, 0x0C, 0x30, 0xC1, 0x06, 0x0F, 0xE0, /* 0x9E */
- 0x0C, 0x10, 0x47, 0xF0, 0x61, 0x86, 0x0C, 0x30, 0xC1, 0x06, 0x0F, 0xE0, /* 0x9F */
- /* 0xA0 */
- 0x8A, 0x9C, /* 0xA1 */
- 0x85, 0xE0, /* 0xA2 */
- 0x60, 0x30, 0x18, 0x0C, 0x86, 0xC3, 0xC1, 0xC1, 0xC0, 0xE0, 0x30, 0x18, 0x0C, 0x07, 0xF8, /* 0xA3 */
- 0xFF, 0xDF, 0x1E, 0x3E, 0xFF, 0xC0, /* 0xA4 */
- 0x06, 0x00, 0xF0, 0x0F, 0x01, 0x30, 0x13, 0x81, 0x38, 0x21, 0x82, 0x1C, 0x3F, 0xC6, 0x04, 0x60, 0x66, 0x06, 0xC0, 0x30, 0x06,
- 0x00, 0xC0, 0x0C, 0x00, 0x70, /* 0xA5 */
- 0xFF, 0xFC, 0x0F, 0xFF, 0xC0, /* 0xA6 */
- 0x0C, 0x09, 0x0C, 0xC6, 0x63, 0x81, 0xE3, 0x19, 0x87, 0xE1, 0xB8, 0xC6, 0x41, 0xC0, 0x73, 0x19, 0x8C, 0x66, 0x1E,
- 0x00, /* 0xA7 */
- 0xCC, /* 0xA8 */
- 0x0F, 0xC0, 0x61, 0x87, 0x03, 0x9B, 0xC6, 0xD9, 0x8F, 0x60, 0x3D, 0x00, 0xF4, 0x03, 0xD8, 0x0D, 0xE6, 0x67, 0xF3, 0x86, 0x18,
- 0x0F, 0xC0, /* 0xA9 */
- 0x3F, 0x18, 0x6C, 0x0F, 0x03, 0xC0, 0x1E, 0x01, 0xF0, 0x0E, 0x00, 0xF0, 0x3C, 0x0D, 0x86, 0x3F, 0x02, 0x00, 0xE0, 0x18, 0x1C,
- 0x00, /* 0xAA */
- 0x22, 0xCF, 0x26, 0x46, 0x64, 0x40, /* 0xAB */
- 0xFF, 0x80, 0xC0, 0x60, 0x30, 0x18, /* 0xAC */
- /* 0xAD */
- 0x0F, 0xC0, 0x61, 0x87, 0x03, 0x9F, 0xE6, 0xD0, 0x8F, 0x42, 0x3D, 0xF0, 0xF4, 0x23, 0xD0, 0x8D, 0xC2, 0x67, 0x0B, 0x86, 0x18,
- 0x0F, 0xC0, /* 0xAE */
- 0x0C, 0x00, 0x0F, 0xFC, 0x06, 0x03, 0x00, 0xC0, 0x60, 0x30, 0x1C, 0x06, 0x03, 0x01, 0x80, 0x60, 0x30, 0x0F, 0xFC, /* 0xAF */
- 0x74, 0x63, 0x17, 0x00, /* 0xB0 */
- 0x0C, 0x06, 0x03, 0x07, 0xE0, 0xC0, 0x60, 0x30, 0x18, 0x00, 0x00, 0x3F, 0xE0, /* 0xB1 */
- 0x6C, 0xC7, /* 0xB2 */
- 0x66, 0x66, 0x67, 0x6E, 0x66, 0x66, 0x60, /* 0xB3 */
- 0x36, 0xC0, /* 0xB4 */
- 0xC3, 0x61, 0xB0, 0xD8, 0x6C, 0x36, 0x1B, 0x0D, 0x86, 0xE7, 0x7D, 0xF0, 0x18, 0x0C, 0x00, /* 0xB5 */
- 0x3F, 0x7E, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0x72, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, /* 0xB6 */
- 0xE0, /* 0xB7 */
- 0x21, 0xC7, 0xE0, /* 0xB8 */
- 0x7E, 0x38, 0xCC, 0x30, 0x0C, 0x0F, 0x1E, 0xCC, 0x33, 0x0C, 0xC7, 0x1E, 0xE0, 0x10, 0x0C, 0x03, 0x00, 0x70, /* 0xB9 */
- 0x3E, 0xE3, 0xC0, 0xC0, 0xE0, 0x3C, 0x07, 0xC3, 0xC3, 0x7E, 0x10, 0x1C, 0x0C, 0x38, /* 0xBA */
- 0x89, 0x98, 0x99, 0x3C, 0xD1, 0x00, /* 0xBB */
- 0xC6, 0xC4, 0xC8, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xFF, /* 0xBC */
- 0x6F, 0x69, 0x00, /* 0xBD */
- 0xDE, 0xB9, 0x8C, 0x63, 0x18, 0xC6, 0x31, 0x8C, 0x00, /* 0xBE */
- 0x30, 0x03, 0xF8, 0x30, 0xC3, 0x06, 0x18, 0x60, 0x83, 0x07, 0xF0, /* 0xBF */
- 0x06, 0x00, 0xC0, 0xFF, 0x8C, 0x0E, 0xC0, 0x6C, 0x06, 0xC0, 0x6C, 0x0C, 0xFF, 0x8C, 0x0E, 0xC0, 0x6C, 0x06, 0xC0, 0x6C, 0x06,
- 0xC0, 0x70, /* 0xC0 */
- 0x06, 0x03, 0x00, 0x00, 0x30, 0x1E, 0x07, 0x81, 0x20, 0xCC, 0x33, 0x0F, 0xC6, 0x19, 0x86, 0x40, 0xB0, 0x30, /* 0xC1 */
- 0x0C, 0x04, 0x80, 0x00, 0x30, 0x1E, 0x07, 0x81, 0x20, 0xCC, 0x33, 0x0F, 0xC6, 0x19, 0x86, 0x40, 0xB0, 0x30, /* 0xC2 */
- 0x21, 0x07, 0x80, 0x00, 0x30, 0x1E, 0x07, 0x81, 0x20, 0xCC, 0x33, 0x0F, 0xC6, 0x19, 0x86, 0x40, 0xB0, 0x30, /* 0xC3 */
- 0x33, 0x00, 0x00, 0xC0, 0x78, 0x1E, 0x04, 0x83, 0x30, 0xCC, 0x33, 0x1F, 0xE6, 0x19, 0x02, 0xC0, 0xF0, 0x30, /* 0xC4 */
- 0x30, 0x60, 0x00, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xFF, /* 0xC5 */
- 0x06, 0x01, 0x80, 0x00, 0x0F, 0xC3, 0x0C, 0xC0, 0xD0, 0x1E, 0x00, 0xC0, 0x18, 0x03, 0x01, 0xA0, 0x36, 0x0C, 0x61, 0x87,
- 0xC0, /* 0xC6 */
- 0x1F, 0x06, 0x19, 0x83, 0xA0, 0x3C, 0x01, 0x80, 0x30, 0x06, 0x00, 0xC0, 0x68, 0x0D, 0x83, 0x18, 0xE1, 0xF0, 0x08, 0x01, 0xC0,
- 0x18, 0x0E, 0x00, /* 0xC7 */
- 0x19, 0x81, 0xE0, 0x00, 0x0F, 0xC3, 0x0C, 0xC0, 0xF0, 0x1E, 0x00, 0xC0, 0x18, 0x03, 0x01, 0xA0, 0x36, 0x0C, 0x61, 0x87,
- 0xC0, /* 0xC8 */
- 0x0C, 0x0C, 0x00, 0x1F, 0xFC, 0x06, 0x03, 0x01, 0x80, 0xFF, 0x60, 0x30, 0x18, 0x0C, 0x07, 0xFC, /* 0xC9 */
- 0xFF, 0xD8, 0x03, 0x00, 0x60, 0x0C, 0x01, 0x80, 0x3F, 0xF6, 0x00, 0xC0, 0x18, 0x03, 0x00, 0x60, 0x0F, 0xFC, 0x01, 0x80, 0x60,
- 0x0C, 0x00, 0xE0, /* 0xCA */
- 0x33, 0x00, 0x3F, 0xF8, 0x0C, 0x06, 0x03, 0x01, 0xFE, 0xC0, 0x60, 0x30, 0x18, 0x0C, 0x07, 0xFC, /* 0xCB */
- 0x33, 0x0F, 0x00, 0x1F, 0xFC, 0x06, 0x03, 0x01, 0x80, 0xFF, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0xFE, /* 0xCC */
- 0x78, 0x36, 0xDB, 0x6D, 0xB6, 0xC0, /* 0xCD */
- 0x76, 0xC0, 0x63, 0x18, 0xC6, 0x31, 0x8C, 0x63, 0x18, /* 0xCE */
- 0x66, 0x0F, 0x00, 0x03, 0xF8, 0xC3, 0x30, 0x6C, 0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC1, 0xB0, 0xEF, 0xE0, /* 0xCF */
- 0x7F, 0x0C, 0x31, 0x83, 0x30, 0x36, 0x06, 0xC0, 0xFE, 0x1B, 0x03, 0x60, 0x6C, 0x0D, 0x83, 0x30, 0xE7, 0xF0, /* 0xD0 */
- 0x03, 0x01, 0x83, 0x81, 0xF0, 0x3F, 0x07, 0xA0, 0xF6, 0x1E, 0x63, 0xC4, 0x78, 0xCF, 0x0D, 0xE1, 0xBC, 0x1F, 0x81,
- 0xC0, /* 0xD1 */
- 0x19, 0x81, 0xE3, 0x81, 0xF0, 0x3F, 0x07, 0xA0, 0xF6, 0x1E, 0x63, 0xC4, 0x78, 0xCF, 0x0D, 0xE1, 0xBC, 0x1F, 0x81,
- 0xC0, /* 0xD2 */
- 0x03, 0x00, 0x60, 0x00, 0x00, 0xF0, 0x39, 0xC6, 0x06, 0x60, 0x6C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, 0x36, 0x06, 0x60, 0x63, 0x9C,
- 0x0F, 0x00, /* 0xD3 */
- 0x0F, 0x01, 0x98, 0x00, 0x00, 0xF0, 0x39, 0xC6, 0x06, 0x60, 0x6C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, 0x36, 0x06, 0x60, 0x63, 0x9C,
- 0x0F, 0x00, /* 0xD4 */
- 0x0D, 0x81, 0xB0, 0x00, 0x00, 0xF0, 0x39, 0xC6, 0x06, 0x60, 0x6C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, 0x36, 0x06, 0x60, 0x63, 0x9C,
- 0x0F, 0x00, /* 0xD5 */
- 0x19, 0x81, 0x98, 0x00, 0x00, 0xF0, 0x39, 0xC6, 0x06, 0x60, 0x6C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, 0x36, 0x06, 0x60, 0x63, 0x9C,
- 0x0F, 0x00, /* 0xD6 */
- 0x83, 0x89, 0xA1, 0x83, 0x89, 0xA1, 0x80, /* 0xD7 */
- 0x33, 0x01, 0xE0, 0xFF, 0x8C, 0x0E, 0xC0, 0x6C, 0x06, 0xC0, 0x6C, 0x0C, 0xFF, 0x8C, 0x0E, 0xC0, 0x6C, 0x06, 0xC0, 0x6C, 0x06,
- 0xC0, 0x70, /* 0xD8 */
- 0x04, 0x01, 0x43, 0x11, 0xE0, 0x3C, 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x36, 0x0C, 0x3E,
- 0x00, /* 0xD9 */
- 0x06, 0x01, 0x83, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x36, 0x0C, 0x3E,
- 0x00, /* 0xDA */
- 0x0D, 0x83, 0x63, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x36, 0x0C, 0x3E,
- 0x00, /* 0xDB */
- 0x1B, 0x00, 0x03, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x36, 0x0C, 0x3E,
- 0x00, /* 0xDC */
- 0x03, 0x0C, 0x63, 0x60, 0x63, 0x0C, 0x30, 0xC1, 0x98, 0x1D, 0x80, 0xF0, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00,
- 0x60, /* 0xDD */
- 0xFF, 0x86, 0x03, 0x01, 0x80, 0xC0, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x01, 0x80, 0xC0, 0x40, 0x3C, 0x06, 0x1E,
- 0x00, /* 0xDE */
- 0x3C, 0x33, 0x30, 0xD8, 0x6C, 0x36, 0x33, 0x39, 0x86, 0xC1, 0xE0, 0xF0, 0x78, 0x6D, 0xE0, /* 0xDF */
- 0x19, 0x89, 0xBE, 0x63, 0x18, 0xC6, 0x31, 0x8C, 0x00, /* 0xE0 */
- 0x0C, 0x04, 0x04, 0x0F, 0xCE, 0x36, 0x18, 0x0C, 0x1E, 0x7B, 0x61, 0xB0, 0xD8, 0xE7, 0xB8, /* 0xE1 */
- 0x10, 0x14, 0x1B, 0x0F, 0xCE, 0x36, 0x18, 0x0C, 0x1E, 0x7B, 0x61, 0xB0, 0xD8, 0xE7, 0xB8, /* 0xE2 */
- 0x66, 0x1E, 0x00, 0x0F, 0xCE, 0x36, 0x18, 0x0C, 0x1E, 0x7B, 0x61, 0xB0, 0xD8, 0xE7, 0xB8, /* 0xE3 */
- 0x66, 0x00, 0x1F, 0x9C, 0x6C, 0x30, 0x18, 0x3C, 0xF6, 0xC3, 0x61, 0xB1, 0xCF, 0x70, /* 0xE4 */
- 0x78, 0x36, 0xDB, 0x6D, 0xB6, 0xD8, /* 0xE5 */
- 0x0C, 0x08, 0x10, 0x3C, 0x66, 0xC3, 0xC0, 0xC0, 0xC0, 0xC0, 0xC3, 0x66, 0x3C, /* 0xE6 */
- 0x3C, 0x66, 0xC3, 0xC0, 0xC0, 0xC0, 0xC0, 0xC3, 0x66, 0x3C, 0x10, 0x1C, 0x0C, 0x38, /* 0xE7 */
- 0x44, 0x28, 0x38, 0x3C, 0x66, 0xC3, 0xC0, 0xC0, 0xC0, 0xC0, 0xC3, 0x66, 0x3C, /* 0xE8 */
- 0x0C, 0x08, 0x18, 0x3C, 0x66, 0xC3, 0xC3, 0xFF, 0xC0, 0xC0, 0xC3, 0x66, 0x3C, /* 0xE9 */
- 0x3C, 0x62, 0xC3, 0xC3, 0xFF, 0xC0, 0xC0, 0xC3, 0x66, 0x3E, 0x04, 0x0C, 0x0C, 0x06, /* 0xEA */
- 0x66, 0x00, 0x3C, 0x66, 0xC3, 0xC3, 0xFF, 0xC0, 0xC0, 0xC3, 0x66, 0x3C, /* 0xEB */
- 0x64, 0x2C, 0x18, 0x3C, 0x66, 0xC3, 0xC3, 0xFF, 0xC0, 0xC0, 0xC3, 0x66, 0x3C, /* 0xEC */
- 0x7A, 0x6D, 0xB6, 0xDB, 0x6C, /* 0xED */
- 0x69, 0x06, 0x66, 0x66, 0x66, 0x66, 0x60, /* 0xEE */
- 0x03, 0x30, 0x32, 0x03, 0x43, 0xB0, 0x67, 0x0C, 0x30, 0xC3, 0x0C, 0x30, 0xC3, 0x0C, 0x30, 0xC3, 0x06, 0x70, 0x3B,
- 0x00, /* 0xEF */
- 0x03, 0x07, 0xC0, 0xC7, 0x66, 0x76, 0x1B, 0x0D, 0x86, 0xC3, 0x61, 0xB0, 0xCC, 0xE3, 0xB0, /* 0xF0 */
- 0x0C, 0x18, 0x00, 0xDE, 0xE3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, /* 0xF1 */
- 0x66, 0x3C, 0x00, 0xDE, 0xE3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, /* 0xF2 */
- 0x0C, 0x18, 0x00, 0x3C, 0x66, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x66, 0x3C, /* 0xF3 */
- 0x18, 0x24, 0x00, 0x3C, 0x66, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x66, 0x3C, /* 0xF4 */
- 0x36, 0x6C, 0x00, 0x3C, 0x66, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x66, 0x3C, /* 0xF5 */
- 0x66, 0x00, 0x3C, 0x66, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x66, 0x3C, /* 0xF6 */
- 0x18, 0x00, 0x00, 0x1F, 0xF0, 0x00, 0x00, 0x00, 0x30, /* 0xF7 */
- 0xDB, 0x81, 0xBE, 0x63, 0x18, 0xC6, 0x31, 0x8C, 0x00, /* 0xF8 */
- 0x10, 0x28, 0x10, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC7, 0x7B, /* 0xF9 */
- 0x06, 0x0C, 0x18, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC7, 0x7B, /* 0xFA */
- 0x36, 0x6C, 0x00, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC7, 0x7B, /* 0xFB */
- 0x66, 0x00, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC7, 0x7B, /* 0xFC */
- 0x06, 0x04, 0x08, 0xC1, 0x43, 0x63, 0x62, 0x26, 0x36, 0x34, 0x1C, 0x1C, 0x18, 0x18, 0x18, 0x10, 0x60, /* 0xFD */
- 0x63, 0x3C, 0xC6, 0x31, 0x8C, 0x63, 0x18, 0xE2, 0x1C, 0x6F, /* 0xFE */
- 0xC0, /* 0xFF */
+/* 0x01 */ 0x07, 0x00, 0x0A, 0x00, 0x24, 0x00, 0x48, 0x01, 0x10, 0x04, 0x40, 0x10, 0xFF, 0x20, 0x02, 0x81, 0xFD, 0x00, 0x06, 0x07, 0xF4, 0x08, 0x24, 0x0F, 0x88, 0x11, 0x0F, 0xDC, 0x00,
+/* 0x02 */ 0x3F, 0x70, 0x81, 0x11, 0x03, 0xE4, 0x08, 0x28, 0x1F, 0xD0, 0x00, 0x60, 0x7F, 0x20, 0x02, 0x43, 0xFC, 0x44, 0x00, 0x44, 0x00, 0x48, 0x00, 0x90, 0x00, 0xA0, 0x01, 0xC0, 0x00,
+/* 0x03 */ 0x07, 0xC0, 0x30, 0x60, 0x80, 0x22, 0x00, 0x28, 0x00, 0x31, 0x8C, 0x63, 0x18, 0xC0, 0x01, 0x80, 0x03, 0x00, 0x06, 0x20, 0x8C, 0x3E, 0x14, 0x00, 0x44, 0x01, 0x06, 0x0C, 0x03, 0xE0,
+/* 0x04 */ 0x07, 0xC0, 0x30, 0x60, 0x80, 0x22, 0x00, 0x28, 0x82, 0x30, 0x88, 0x62, 0x08, 0xC0, 0x01, 0x80, 0x03, 0x00, 0x06, 0x3F, 0x8C, 0x3E, 0x14, 0x00, 0x44, 0x01, 0x06, 0x0C, 0x03, 0xE0,
+/* 0x05 */ 0x0B, 0x10, 0x14, 0xA8, 0x12, 0x50, 0x29, 0x42, 0x24, 0xA5, 0x32, 0x95, 0x5A, 0x09, 0x48, 0x09, 0x24, 0x01, 0x10, 0x01, 0x48, 0x02, 0xA4, 0x02, 0x42, 0x04, 0x01, 0x98, 0x00, 0x60,
+/* 0x06 */ 0x00, 0x80, 0x22, 0x80, 0x65, 0x00, 0xBE, 0xE1, 0x82, 0x4E, 0x03, 0x24, 0x04, 0x28, 0x06, 0x30, 0x12, 0x20, 0x3C, 0xA0, 0xC3, 0xFE, 0x80, 0x4D, 0x00, 0xA6, 0x01, 0x80, 0x00,
+/* 0x07 */
+/* 0x08 */ 0x00, 0xF8, 0x00, 0x82, 0x00, 0x80, 0x83, 0xE0, 0x41, 0x10, 0x21, 0x04, 0x1B, 0x00, 0x03, 0x00, 0x01, 0x80, 0x00, 0xE0, 0x00, 0x4F, 0xE1, 0xC0, 0x0F, 0x02, 0x00, 0x03, 0x01, 0x00, 0x09, 0x88, 0x0C, 0x0C,
+/* 0x09 */ 0x00, 0xF8, 0x00, 0x82, 0x00, 0x80, 0x83, 0xE0, 0x41, 0x10, 0x21, 0x04, 0x1B, 0x00, 0x03, 0x00, 0x01, 0x80, 0x00, 0xE0, 0x00, 0x4F, 0xE1, 0xC0, 0x0F, 0x00,
+/* 0x0A */
+/* 0x0B */ 0x1C, 0x1C, 0x31, 0xB1, 0x90, 0x50, 0x50, 0x10, 0x18, 0x00, 0x0C, 0x00, 0x06, 0x00, 0x02, 0x80, 0x02, 0x40, 0x01, 0x10, 0x01, 0x04, 0x01, 0x01, 0x01, 0x00, 0x41, 0x00, 0x11, 0x00, 0x07, 0x00, 0x01, 0x00,
+/* 0x0C */ 0x06, 0x00, 0x0A, 0x00, 0x12, 0x00, 0x32, 0x01, 0x84, 0x04, 0x10, 0x08, 0x98, 0x1C, 0x18, 0x40, 0x48, 0x82, 0x11, 0xF0, 0x74, 0x02, 0x18, 0x70, 0x2F, 0x9F, 0x80,
+/* 0x0D */
+/* 0x0E */ 0x01, 0x00, 0x05, 0x00, 0x0A, 0x00, 0x3E, 0x00, 0x82, 0x02, 0x82, 0x06, 0x04, 0x10, 0x04, 0x20, 0x08, 0x40, 0x10, 0xFF, 0x22, 0x00, 0x29, 0xFF, 0x3F, 0x8F, 0xDF, 0x9F, 0x01, 0xC0,
+/* 0x0F */ 0x07, 0xC0, 0x30, 0x60, 0x80, 0x22, 0x00, 0x28, 0x82, 0x36, 0x03, 0x60, 0x00, 0xCC, 0x19, 0xA4, 0x4B, 0x00, 0x06, 0x8E, 0x2B, 0x22, 0x66, 0x7C, 0xCC, 0x71, 0x98, 0x03, 0x00,
+/* 0x10 */ 0x03, 0x80, 0x07, 0x00, 0x0E, 0x00, 0x1E, 0x00, 0x54, 0x00, 0xA8, 0x01, 0x50, 0x02, 0xA0, 0x05, 0x20, 0x32, 0x61, 0xC4, 0x74, 0x49, 0x10, 0x6C, 0x00, 0xD8, 0x01, 0x10, 0x00,
+/* 0x11 */ 0x07, 0xC0, 0x30, 0x60, 0x80, 0x22, 0x40, 0x29, 0x00, 0x31, 0x84, 0x63, 0x18, 0xC0, 0x00, 0x80, 0x15, 0x03, 0x7E, 0x02, 0xFA, 0x04, 0xE4, 0x18, 0x84, 0x00, 0x06, 0x0C, 0x03, 0xE0,
+/* 0x12 */ 0x02, 0x08, 0x01, 0x08, 0x40, 0x10, 0xC0, 0x08, 0xC0, 0x60, 0x80, 0x28, 0x04, 0x12, 0x4C, 0x10, 0x80, 0x08, 0x23, 0x0E, 0x08, 0xC4, 0x82, 0x04, 0x20, 0x83, 0x09, 0x82, 0x47, 0x01, 0x1C, 0x01, 0x30, 0x00, 0xE0, 0x00, 0x00,
+/* 0x13 */ 0x07, 0xC0, 0x30, 0x60, 0x80, 0x22, 0x00, 0x28, 0x00, 0x31, 0x08, 0x65, 0x28, 0xC0, 0x01, 0x80, 0x03, 0x00, 0x06, 0x3F, 0x8C, 0x3E, 0x14, 0x00, 0x44, 0x01, 0x06, 0x0C, 0x03, 0xE0,
+/* 0x14 */ 0x07, 0xC0, 0x30, 0x60, 0x80, 0x22, 0x22, 0x29, 0x83, 0x30, 0x00, 0x65, 0x14, 0xD3, 0x4D, 0xBA, 0xEB, 0x38, 0xE6, 0x00, 0x0A, 0x00, 0x24, 0x38, 0x44, 0x01, 0x07, 0x1C, 0x01, 0xC0,
+/* 0x15 */ 0x07, 0xC0, 0x30, 0x18, 0x80, 0x32, 0x00, 0xF8, 0x01, 0xF1, 0x09, 0xA5, 0x28, 0x40, 0x01, 0x80, 0x03, 0x00, 0x06, 0x3F, 0x8C, 0x3E, 0x14, 0x00, 0x44, 0x01, 0x06, 0x0C, 0x03, 0xE0,
+/* 0x16 */ 0x0C, 0x00, 0xC0, 0x1C, 0x03, 0x80, 0xF8, 0xBB, 0x36, 0xC7, 0x99, 0xF3, 0xFE, 0x3F, 0xC3, 0xF0, 0x7E, 0x0E, 0xC1, 0x8E, 0xE0, 0x20,
+/* 0x17 */ 0x07, 0xC0, 0x30, 0x60, 0x80, 0x22, 0x00, 0x28, 0x00, 0x10, 0x01, 0x20, 0x1D, 0x44, 0x42, 0x84, 0x85, 0x00, 0x86, 0x00, 0xC4, 0x00, 0x44, 0x7C, 0x44, 0x00, 0x06, 0x0C, 0x03, 0xE0,
+/* 0x18 */ 0x01, 0xE0, 0x00, 0x84, 0x00, 0x40, 0x80, 0x20, 0x10, 0x08, 0x24, 0x02, 0x41, 0x00, 0x86, 0x03, 0x12, 0x03, 0xB4, 0x03, 0x52, 0x81, 0x23, 0x80, 0x70, 0xA0, 0x14, 0x28, 0x05, 0x0A, 0x01, 0x42, 0x80, 0x50,
+/* 0x19 */ 0x07, 0xC0, 0x30, 0x60, 0x80, 0x22, 0x00, 0x28, 0x00, 0x33, 0x18, 0x60, 0x00, 0xDC, 0xE1, 0xB9, 0xC3, 0x7B, 0xC6, 0x63, 0x0A, 0x00, 0x24, 0xF0, 0x44, 0x01, 0x06, 0x0C, 0x03, 0xE0,
+/* 0x1A */ 0xFF, 0xFC, 0x00, 0x63, 0xE3, 0x31, 0x99, 0x04, 0xC8, 0x66, 0x06, 0x30, 0x61, 0x82, 0x0C, 0x10, 0x60, 0x03, 0x04, 0x18, 0x00, 0xFF, 0xFC,
+/* 0x1B */ 0x07, 0xF0, 0x06, 0x0C, 0x04, 0x01, 0x04, 0x00, 0x44, 0x22, 0x12, 0x2A, 0x89, 0x00, 0x04, 0x80, 0x02, 0x44, 0x11, 0x01, 0xF0, 0x04, 0x01, 0x0D, 0x01, 0x6A, 0x41, 0x2C, 0x00, 0x05, 0xC0, 0x0E, 0x18, 0x18,
+/* 0x1C */ 0x07, 0xC0, 0x30, 0x60, 0x80, 0x22, 0xC0, 0x2A, 0x00, 0x33, 0x00, 0x66, 0x00, 0xCC, 0x39, 0x80, 0x83, 0x00, 0x06, 0x00, 0x8C, 0x3E, 0x14, 0x00, 0x44, 0x01, 0x06, 0x0C, 0x03, 0xE0,
+/* 0x1D */ 0x07, 0xC0, 0x30, 0x60, 0x80, 0x22, 0x70, 0x28, 0x00, 0x31, 0x80, 0x63, 0x18, 0xC0, 0x31, 0x80, 0x03, 0x00, 0x06, 0x60, 0x0D, 0x33, 0x12, 0x10, 0x48, 0x21, 0x23, 0x8C, 0x00,
+/* 0x1E */ 0x03, 0x00, 0x07, 0x9E, 0x07, 0x00, 0x86, 0x00, 0x27, 0xC0, 0x0F, 0xC0, 0x07, 0x8C, 0x62, 0x06, 0x31, 0x20, 0x00, 0x90, 0x00, 0x48, 0x00, 0x24, 0x3E, 0x11, 0x00, 0x10, 0x40, 0x10, 0x18, 0x30, 0x03, 0xE0,
+/* 0x1F */ 0x18, 0x02, 0x80, 0x4C, 0x16, 0x41, 0x24, 0x3C, 0x88, 0x6E, 0x65, 0xF2, 0x78, 0x46, 0x88, 0xCF, 0x18, 0x02, 0x80, 0x8C, 0x60, 0x70,
+/* ' ' 0x20 */
+/* '!' 0x21 */ 0xFF, 0xFF, 0xF0, 0xC0,
+/* '"' 0x22 */ 0xDE, 0xF7, 0x20,
+/* '#' 0x23 */ 0x09, 0x86, 0x41, 0x91, 0xFF, 0x13, 0x04, 0xC3, 0x20, 0xC8, 0xFF, 0x89, 0x82, 0x61, 0x90,
+/* '$' 0x24 */ 0x10, 0x1F, 0x14, 0xDA, 0x3D, 0x1E, 0x83, 0x40, 0x78, 0x17, 0x08, 0xF4, 0x7A, 0x35, 0x33, 0xF0, 0x40, 0x20,
+/* '%' 0x25 */ 0x38, 0x10, 0xEC, 0x20, 0xC6, 0x20, 0xC6, 0x40, 0xC6, 0x40, 0x6C, 0x80, 0x39, 0x00, 0x01, 0x3C, 0x02, 0x77, 0x02, 0x63, 0x04, 0x63, 0x04, 0x77, 0x08, 0x3C,
+/* '&' 0x26 */ 0x0E, 0x0C, 0xC3, 0x30, 0xCC, 0x1E, 0x03, 0x03, 0xC1, 0x9B, 0xC2, 0xF0, 0xEC, 0x19, 0x8F, 0x3C, 0x40,
+/* ''' 0x27 */ 0xFE,
+/* '(' 0x28 */ 0x13, 0x26, 0x6C, 0xCC, 0xCC, 0xC4, 0x66, 0x23, 0x10,
+/* ')' 0x29 */ 0x8C, 0x46, 0x63, 0x33, 0x33, 0x32, 0x66, 0x4C, 0x80,
+/* '*' 0x2A */ 0x25, 0x7E, 0xA5, 0x00,
+/* '+' 0x2B */ 0x30, 0xC3, 0x3F, 0x30, 0xC3, 0x0C,
+/* ',' 0x2C */ 0xD6,
+/* '-' 0x2D */ 0xF0,
+/* '.' 0x2E */ 0xC0,
+/* '/' 0x2F */ 0x08, 0x44, 0x21, 0x10, 0x84, 0x42, 0x11, 0x08, 0x00,
+/* '0' 0x30 */ 0x3C, 0x66, 0x42, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x42, 0x66, 0x3C,
+/* '1' 0x31 */ 0x11, 0x3F, 0x33, 0x33, 0x33, 0x33, 0x30,
+/* '2' 0x32 */ 0x3E, 0x31, 0xB0, 0x78, 0x30, 0x18, 0x1C, 0x1C, 0x1C, 0x18, 0x18, 0x10, 0x08, 0x07, 0xF8,
+/* '3' 0x33 */ 0x3C, 0x66, 0xC3, 0xC3, 0x03, 0x06, 0x1C, 0x07, 0x03, 0xC3, 0xC3, 0x66, 0x3C,
+/* '4' 0x34 */ 0x0C, 0x18, 0x71, 0x62, 0xC9, 0xA3, 0x46, 0xFE, 0x18, 0x30, 0x60, 0xC0,
+/* '5' 0x35 */ 0x7F, 0x20, 0x10, 0x08, 0x08, 0x07, 0xF3, 0x8C, 0x03, 0x01, 0x80, 0xF0, 0x6C, 0x63, 0xE0,
+/* '6' 0x36 */ 0x1E, 0x31, 0x98, 0x78, 0x0C, 0x06, 0xF3, 0x8D, 0x83, 0xC1, 0xE0, 0xD0, 0x6C, 0x63, 0xE0,
+/* '7' 0x37 */ 0xFF, 0x03, 0x02, 0x06, 0x04, 0x0C, 0x08, 0x18, 0x18, 0x18, 0x10, 0x30, 0x30,
+/* '8' 0x38 */ 0x3E, 0x31, 0xB0, 0x78, 0x3C, 0x1B, 0x18, 0xF8, 0xC6, 0xC1, 0xE0, 0xF0, 0x6C, 0x63, 0xE0,
+/* '9' 0x39 */ 0x3C, 0x66, 0xC2, 0xC3, 0xC3, 0xC3, 0x67, 0x3B, 0x03, 0x03, 0xC2, 0x66, 0x3C,
+/* ':' 0x3A */ 0xC0, 0x00, 0x30,
+/* ';' 0x3B */ 0xC0, 0x00, 0x00, 0x64, 0xA0,
+/* '<' 0x3C */ 0x00, 0x81, 0xC7, 0x8E, 0x0C, 0x07, 0x80, 0x70, 0x0E, 0x01, 0x80,
+/* '=' 0x3D */ 0xFF, 0x80, 0x00, 0x1F, 0xF0,
+/* '>' 0x3E */ 0xE0, 0x1C, 0x03, 0x80, 0x30, 0x70, 0xE3, 0x81, 0x00,
+/* '?' 0x3F */ 0x3E, 0x31, 0xB0, 0x78, 0x30, 0x18, 0x18, 0x38, 0x18, 0x18, 0x0C, 0x00, 0x00, 0x01, 0x80,
+/* '@' 0x40 */ 0x03, 0xF0, 0x06, 0x0E, 0x06, 0x01, 0x86, 0x00, 0x66, 0x1D, 0xBB, 0x31, 0xCF, 0x18, 0xC7, 0x98, 0x63, 0xCC, 0x31, 0xE6, 0x11, 0xB3, 0x99, 0xCC, 0xF7, 0x86, 0x00, 0x01, 0x80, 0x00, 0x70, 0x40, 0x0F, 0xE0,
+/* 'A' 0x41 */ 0x06, 0x00, 0xF0, 0x0F, 0x00, 0x90, 0x19, 0x81, 0x98, 0x10, 0x83, 0x0C, 0x3F, 0xC2, 0x04, 0x60, 0x66, 0x06, 0xC0, 0x30,
+/* 'B' 0x42 */ 0xFF, 0x18, 0x33, 0x03, 0x60, 0x6C, 0x0D, 0x83, 0x3F, 0xC6, 0x06, 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x6F, 0xF8,
+/* 'C' 0x43 */ 0x1F, 0x86, 0x19, 0x81, 0xA0, 0x3C, 0x01, 0x80, 0x30, 0x06, 0x00, 0xC0, 0x68, 0x0D, 0x83, 0x18, 0x61, 0xF0,
+/* 'D' 0x44 */ 0xFF, 0x18, 0x33, 0x03, 0x60, 0x3C, 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0x78, 0x0F, 0x03, 0x60, 0xCF, 0xF0,
+/* 'E' 0x45 */ 0xFF, 0xE0, 0x30, 0x18, 0x0C, 0x06, 0x03, 0xFD, 0x80, 0xC0, 0x60, 0x30, 0x18, 0x0F, 0xF8,
+/* 'F' 0x46 */ 0xFF, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xFE, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0,
+/* 'G' 0x47 */ 0x0F, 0x83, 0x0E, 0x60, 0x66, 0x03, 0xC0, 0x0C, 0x00, 0xC1, 0xFC, 0x03, 0xC0, 0x36, 0x03, 0x60, 0x73, 0x0F, 0x0F, 0x10,
+/* 'H' 0x48 */ 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xFF, 0xFE, 0x03, 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x3C, 0x06,
+/* 'I' 0x49 */ 0xFF, 0xFF, 0xFF, 0xC0,
+/* 'J' 0x4A */ 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC1, 0x83, 0x07, 0x8F, 0x1E, 0x27, 0x80,
+/* 'K' 0x4B */ 0xC0, 0xF0, 0x6C, 0x33, 0x18, 0xCC, 0x37, 0x0F, 0xC3, 0x98, 0xC3, 0x30, 0xCC, 0x1B, 0x03, 0xC0, 0xC0,
+/* 'L' 0x4C */ 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xFF,
+/* 'M' 0x4D */ 0xE0, 0x3F, 0x01, 0xFC, 0x1F, 0xE0, 0xFD, 0x05, 0xEC, 0x6F, 0x63, 0x79, 0x13, 0xCD, 0x9E, 0x6C, 0xF1, 0x47, 0x8E, 0x3C, 0x71, 0x80,
+/* 'N' 0x4E */ 0xE0, 0x7C, 0x0F, 0xC1, 0xE8, 0x3D, 0x87, 0x98, 0xF1, 0x1E, 0x33, 0xC3, 0x78, 0x6F, 0x07, 0xE0, 0x7C, 0x0E,
+/* 'O' 0x4F */ 0x0F, 0x81, 0x83, 0x18, 0x0C, 0xC0, 0x6C, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x03, 0xC0, 0x1B, 0x01, 0x98, 0x0C, 0x60, 0xC0, 0xF8, 0x00,
+/* 'P' 0x50 */ 0xFF, 0x30, 0x6C, 0x0F, 0x03, 0xC0, 0xF0, 0x6F, 0xF3, 0x00, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x00,
+/* 'Q' 0x51 */ 0x0F, 0x81, 0x83, 0x18, 0x0C, 0xC0, 0x6C, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x03, 0xC0, 0x1B, 0x01, 0x98, 0x6C, 0x60, 0xC0, 0xFB, 0x00, 0x08,
+/* 'R' 0x52 */ 0xFF, 0x8C, 0x0E, 0xC0, 0x6C, 0x06, 0xC0, 0x6C, 0x0C, 0xFF, 0x8C, 0x0E, 0xC0, 0x6C, 0x06, 0xC0, 0x6C, 0x06, 0xC0, 0x70,
+/* 'S' 0x53 */ 0x3F, 0x18, 0x6C, 0x0F, 0x03, 0xC0, 0x1E, 0x01, 0xF0, 0x0E, 0x00, 0xF0, 0x3C, 0x0D, 0x86, 0x3F, 0x00,
+/* 'T' 0x54 */ 0xFF, 0x86, 0x03, 0x01, 0x80, 0xC0, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x01, 0x80, 0xC0,
+/* 'U' 0x55 */ 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0x78, 0x0F, 0x01, 0xB0, 0x61, 0xF0,
+/* 'V' 0x56 */ 0xC0, 0x6C, 0x0D, 0x81, 0x10, 0x63, 0x0C, 0x61, 0x04, 0x60, 0xCC, 0x19, 0x01, 0x60, 0x3C, 0x07, 0x00, 0x60,
+/* 'W' 0x57 */ 0xC1, 0x81, 0x61, 0xC3, 0x61, 0xC3, 0x61, 0x43, 0x62, 0x62, 0x22, 0x66, 0x32, 0x26, 0x36, 0x26, 0x14, 0x34, 0x14, 0x34, 0x1C, 0x1C, 0x18, 0x1C, 0x08, 0x18,
+/* 'X' 0x58 */ 0xC0, 0xD8, 0x66, 0x18, 0xCC, 0x1E, 0x07, 0x00, 0xC0, 0x78, 0x32, 0x0C, 0xC6, 0x1B, 0x07, 0xC0, 0xC0,
+/* 'Y' 0x59 */ 0xC0, 0x36, 0x06, 0x30, 0xC3, 0x0C, 0x19, 0x81, 0xD8, 0x0F, 0x00, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00,
+/* 'Z' 0x5A */ 0xFF, 0xC0, 0x60, 0x30, 0x0C, 0x06, 0x03, 0x01, 0xC0, 0x60, 0x30, 0x18, 0x06, 0x03, 0x00, 0xFF, 0xC0,
+/* '[' 0x5B */ 0xFB, 0x6D, 0xB6, 0xDB, 0x6D, 0xB6, 0xE0,
+/* '\' 0x5C */ 0x84, 0x10, 0x84, 0x10, 0x84, 0x10, 0x84, 0x10, 0x80,
+/* ']' 0x5D */ 0xED, 0xB6, 0xDB, 0x6D, 0xB6, 0xDB, 0xE0,
+/* '^' 0x5E */ 0x30, 0x60, 0xA2, 0x44, 0xD8, 0xA1, 0x80,
+/* '_' 0x5F */ 0xFF, 0xC0,
+/* '`' 0x60 */ 0xC6, 0x30,
+/* 'a' 0x61 */ 0x7E, 0x71, 0xB0, 0xC0, 0x60, 0xF3, 0xDB, 0x0D, 0x86, 0xC7, 0x3D, 0xC0,
+/* 'b' 0x62 */ 0xC0, 0x60, 0x30, 0x1B, 0xCE, 0x36, 0x0F, 0x07, 0x83, 0xC1, 0xE0, 0xF0, 0x7C, 0x6D, 0xE0,
+/* 'c' 0x63 */ 0x3C, 0x66, 0xC3, 0xC0, 0xC0, 0xC0, 0xC0, 0xC3, 0x66, 0x3C,
+/* 'd' 0x64 */ 0x03, 0x03, 0x03, 0x3B, 0x67, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x67, 0x3B,
+/* 'e' 0x65 */ 0x3C, 0x66, 0xC3, 0xC3, 0xFF, 0xC0, 0xC0, 0xC3, 0x66, 0x3C,
+/* 'f' 0x66 */ 0x36, 0x6F, 0x66, 0x66, 0x66, 0x66, 0x60,
+/* 'g' 0x67 */ 0x3B, 0x67, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x67, 0x3B, 0x03, 0x03, 0xC6, 0x7C,
+/* 'h' 0x68 */ 0xC0, 0xC0, 0xC0, 0xDE, 0xE3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3,
+/* 'i' 0x69 */ 0xC3, 0xFF, 0xFF, 0xC0,
+/* 'j' 0x6A */ 0x30, 0x03, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0xE0,
+/* 'k' 0x6B */ 0xC0, 0xC0, 0xC0, 0xC2, 0xC4, 0xCC, 0xD8, 0xF8, 0xEC, 0xC4, 0xC6, 0xC3, 0xC3,
+/* 'l' 0x6C */ 0xFF, 0xFF, 0xFF, 0xC0,
+/* 'm' 0x6D */ 0xDE, 0xF7, 0x1C, 0xF0, 0xC7, 0x86, 0x3C, 0x31, 0xE1, 0x8F, 0x0C, 0x78, 0x63, 0xC3, 0x1E, 0x18, 0xC0,
+/* 'n' 0x6E */ 0xDE, 0xE3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3,
+/* 'o' 0x6F */ 0x3C, 0x66, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x66, 0x3C,
+/* 'p' 0x70 */ 0xDE, 0x71, 0xB0, 0x78, 0x3C, 0x1E, 0x0F, 0x07, 0x83, 0xE3, 0x6F, 0x30, 0x18, 0x0C, 0x00,
+/* 'q' 0x71 */ 0x3B, 0x67, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x67, 0x3B, 0x03, 0x03, 0x03,
+/* 'r' 0x72 */ 0xDF, 0x31, 0x8C, 0x63, 0x18, 0xC6, 0x00,
+/* 's' 0x73 */ 0x3E, 0xE3, 0xC0, 0xC0, 0xE0, 0x3C, 0x07, 0xC3, 0xE3, 0x7E,
+/* 't' 0x74 */ 0x66, 0xF6, 0x66, 0x66, 0x66, 0x67,
+/* 'u' 0x75 */ 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC7, 0x7B,
+/* 'v' 0x76 */ 0xC1, 0xA0, 0x98, 0xCC, 0x42, 0x21, 0xB0, 0xD0, 0x28, 0x1C, 0x0C, 0x00,
+/* 'w' 0x77 */ 0xC6, 0x1E, 0x38, 0x91, 0xC4, 0xCA, 0x66, 0xD3, 0x16, 0xD0, 0xA6, 0x87, 0x1C, 0x38, 0xC0, 0xC6, 0x00,
+/* 'x' 0x78 */ 0x87, 0x89, 0xB1, 0xC3, 0x07, 0x1E, 0x26, 0xC5, 0x0C,
+/* 'y' 0x79 */ 0xC1, 0x43, 0x63, 0x62, 0x26, 0x36, 0x34, 0x1C, 0x1C, 0x18, 0x18, 0x18, 0x10, 0x60,
+/* 'z' 0x7A */ 0xFE, 0x0C, 0x30, 0xC1, 0x86, 0x18, 0x20, 0xC1, 0xFC,
+/* '{' 0x7B */ 0x36, 0x66, 0x66, 0x6E, 0xCE, 0x66, 0x66, 0x66, 0x30,
+/* '|' 0x7C */ 0xFF, 0xFF, 0xFF, 0xFF, 0xC0,
+/* '}' 0x7D */ 0xC6, 0x66, 0x66, 0x67, 0x37, 0x66, 0x66, 0x66, 0xC0,
+/* '~' 0x7E */ 0x61, 0x24, 0x38,
+/* 0x7F */ 0xFF, 0xFC, 0x00, 0x63, 0xE3, 0x31, 0x99, 0x04, 0xC8, 0x66, 0x06, 0x30, 0x61, 0x83, 0x0C, 0x18, 0x60, 0x03, 0x06, 0x18, 0x00, 0xFF, 0xFC,
+/* 0x80 */ 0x07, 0xC6, 0x13, 0x00, 0xC0, 0x60, 0x3F, 0xE6, 0x03, 0xFC, 0x60, 0x0C, 0x03, 0x00, 0x61, 0x07, 0xC0,
+/* 0x81 */
+/* 0x82 */ 0xDC,
+/* 0x83 */
+/* 0x84 */ 0xDA, 0x76,
+/* 0x85 */ 0xCC, 0xC0,
+/* 0x86 */ 0x18, 0x18, 0x18, 0x18, 0xFF, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+/* 0x87 */ 0x18, 0x18, 0x18, 0x18, 0xFF, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xFF, 0x18, 0x18, 0x18, 0x18,
+/* 0x88 */
+/* 0x89 */ 0x70, 0x80, 0x22, 0x20, 0x08, 0x90, 0x02, 0x24, 0x00, 0x72, 0x00, 0x00, 0x80, 0x00, 0x40, 0x00, 0x10, 0x00, 0x09, 0xC7, 0x84, 0x8B, 0x31, 0x22, 0x84, 0x88, 0xB3, 0x21, 0xC7, 0x80,
+/* 0x8A */ 0x1B, 0x03, 0x83, 0xF1, 0x86, 0xC0, 0xF0, 0x3C, 0x01, 0xE0, 0x1F, 0x00, 0xE0, 0x0F, 0x03, 0xC0, 0xD8, 0x63, 0xF0,
+/* 0x8B */ 0x69,
+/* 0x8C */ 0x06, 0x03, 0x03, 0xF1, 0x86, 0xC0, 0xF0, 0x3C, 0x01, 0xE0, 0x1F, 0x00, 0xE0, 0x0F, 0x03, 0xC0, 0xD8, 0x63, 0xF0,
+/* 0x8D */ 0x33, 0x0F, 0x3F, 0xE1, 0x80, 0xC0, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x01, 0x80, 0xC0, 0x60, 0x30,
+/* 0x8E */ 0x1B, 0x03, 0x8F, 0xFC, 0x06, 0x03, 0x00, 0xC0, 0x60, 0x30, 0x1C, 0x06, 0x03, 0x01, 0x80, 0x60, 0x30, 0x0F, 0xFC,
+/* 0x8F */ 0x0C, 0x06, 0x0F, 0xFC, 0x06, 0x03, 0x00, 0xC0, 0x60, 0x30, 0x1C, 0x06, 0x03, 0x01, 0x80, 0x60, 0x30, 0x0F, 0xFC,
+/* 0x90 */
+/* 0x91 */ 0x6B,
+/* 0x92 */ 0xD6,
+/* 0x93 */ 0x4C, 0xA5, 0xB0,
+/* 0x94 */ 0xDA, 0x53, 0x20,
+/* 0x95 */ 0x6F, 0xFF, 0x60,
+/* 0x96 */ 0xFE,
+/* 0x97 */ 0xFF, 0xFF,
+/* 0x98 */
+/* 0x99 */ 0xFC, 0xE1, 0xCC, 0x38, 0x73, 0x0E, 0x1C, 0xC3, 0x8F, 0x30, 0xD2, 0xCC, 0x34, 0xB3, 0x0D, 0x6C, 0xC3, 0x53, 0x30, 0xCC, 0xCC, 0x33, 0x30,
+/* 0x9A */ 0x24, 0x3C, 0x18, 0x7E, 0xE3, 0xC0, 0xC0, 0x60, 0x3C, 0x07, 0xC3, 0xE3, 0x7E,
+/* 0x9B */ 0x96,
+/* 0x9C */ 0x0C, 0x18, 0x10, 0x3E, 0xE3, 0xC0, 0xC0, 0xE0, 0x3C, 0x07, 0xC3, 0xE3, 0x7E,
+/* 0x9D */ 0x0D, 0xA7, 0x3C, 0x61, 0x86, 0x18, 0x61, 0x86, 0x18, 0x70,
+/* 0x9E */ 0x48, 0xF0, 0xC7, 0xF0, 0x61, 0x86, 0x0C, 0x30, 0xC1, 0x06, 0x0F, 0xE0,
+/* 0x9F */ 0x0C, 0x10, 0x47, 0xF0, 0x61, 0x86, 0x0C, 0x30, 0xC1, 0x06, 0x0F, 0xE0,
+/* 0xA0 */
+/* 0xA1 */ 0x8A, 0x9C,
+/* 0xA2 */ 0x85, 0xE0,
+/* 0xA3 */ 0x60, 0x30, 0x18, 0x0C, 0x86, 0xC3, 0xC1, 0xC1, 0xC0, 0xE0, 0x30, 0x18, 0x0C, 0x07, 0xF8,
+/* 0xA4 */ 0xFF, 0xDF, 0x1E, 0x3E, 0xFF, 0xC0,
+/* 0xA5 */ 0x06, 0x00, 0xF0, 0x0F, 0x01, 0x30, 0x13, 0x81, 0x38, 0x21, 0x82, 0x1C, 0x3F, 0xC6, 0x04, 0x60, 0x66, 0x06, 0xC0, 0x30, 0x06, 0x00, 0xC0, 0x0C, 0x00, 0x70,
+/* 0xA6 */ 0xFF, 0xFC, 0x0F, 0xFF, 0xC0,
+/* 0xA7 */ 0x0C, 0x09, 0x0C, 0xC6, 0x63, 0x81, 0xE3, 0x19, 0x87, 0xE1, 0xB8, 0xC6, 0x41, 0xC0, 0x73, 0x19, 0x8C, 0x66, 0x1E, 0x00,
+/* 0xA8 */ 0xCC,
+/* 0xA9 */ 0x0F, 0xC0, 0x61, 0x87, 0x03, 0x9B, 0xC6, 0xD9, 0x8F, 0x60, 0x3D, 0x00, 0xF4, 0x03, 0xD8, 0x0D, 0xE6, 0x67, 0xF3, 0x86, 0x18, 0x0F, 0xC0,
+/* 0xAA */ 0x3F, 0x18, 0x6C, 0x0F, 0x03, 0xC0, 0x1E, 0x01, 0xF0, 0x0E, 0x00, 0xF0, 0x3C, 0x0D, 0x86, 0x3F, 0x02, 0x00, 0xE0, 0x18, 0x1C, 0x00,
+/* 0xAB */ 0x22, 0xCF, 0x26, 0x46, 0x64, 0x40,
+/* 0xAC */ 0xFF, 0x80, 0xC0, 0x60, 0x30, 0x18,
+/* 0xAD */
+/* 0xAE */ 0x0F, 0xC0, 0x61, 0x87, 0x03, 0x9F, 0xE6, 0xD0, 0x8F, 0x42, 0x3D, 0xF0, 0xF4, 0x23, 0xD0, 0x8D, 0xC2, 0x67, 0x0B, 0x86, 0x18, 0x0F, 0xC0,
+/* 0xAF */ 0x0C, 0x00, 0x0F, 0xFC, 0x06, 0x03, 0x00, 0xC0, 0x60, 0x30, 0x1C, 0x06, 0x03, 0x01, 0x80, 0x60, 0x30, 0x0F, 0xFC,
+/* 0xB0 */ 0x74, 0x63, 0x17, 0x00,
+/* 0xB1 */ 0x0C, 0x06, 0x03, 0x07, 0xE0, 0xC0, 0x60, 0x30, 0x18, 0x00, 0x00, 0x3F, 0xE0,
+/* 0xB2 */ 0x6C, 0xC7,
+/* 0xB3 */ 0x66, 0x66, 0x67, 0x6E, 0x66, 0x66, 0x60,
+/* 0xB4 */ 0x36, 0xC0,
+/* 0xB5 */ 0xC3, 0x61, 0xB0, 0xD8, 0x6C, 0x36, 0x1B, 0x0D, 0x86, 0xE7, 0x7D, 0xF0, 0x18, 0x0C, 0x00,
+/* 0xB6 */ 0x3F, 0x7E, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0x72, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
+/* 0xB7 */ 0xE0,
+/* 0xB8 */ 0x21, 0xC7, 0xE0,
+/* 0xB9 */ 0x7E, 0x38, 0xCC, 0x30, 0x0C, 0x0F, 0x1E, 0xCC, 0x33, 0x0C, 0xC7, 0x1E, 0xE0, 0x10, 0x0C, 0x03, 0x00, 0x70,
+/* 0xBA */ 0x3E, 0xE3, 0xC0, 0xC0, 0xE0, 0x3C, 0x07, 0xC3, 0xC3, 0x7E, 0x10, 0x1C, 0x0C, 0x38,
+/* 0xBB */ 0x89, 0x98, 0x99, 0x3C, 0xD1, 0x00,
+/* 0xBC */ 0xC6, 0xC4, 0xC8, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xFF,
+/* 0xBD */ 0x6F, 0x69, 0x00,
+/* 0xBE */ 0xDE, 0xB9, 0x8C, 0x63, 0x18, 0xC6, 0x31, 0x8C, 0x00,
+/* 0xBF */ 0x30, 0x03, 0xF8, 0x30, 0xC3, 0x06, 0x18, 0x60, 0x83, 0x07, 0xF0,
+/* 0xC0 */ 0x06, 0x00, 0xC0, 0xFF, 0x8C, 0x0E, 0xC0, 0x6C, 0x06, 0xC0, 0x6C, 0x0C, 0xFF, 0x8C, 0x0E, 0xC0, 0x6C, 0x06, 0xC0, 0x6C, 0x06, 0xC0, 0x70,
+/* 0xC1 */ 0x06, 0x03, 0x00, 0x00, 0x30, 0x1E, 0x07, 0x81, 0x20, 0xCC, 0x33, 0x0F, 0xC6, 0x19, 0x86, 0x40, 0xB0, 0x30,
+/* 0xC2 */ 0x0C, 0x04, 0x80, 0x00, 0x30, 0x1E, 0x07, 0x81, 0x20, 0xCC, 0x33, 0x0F, 0xC6, 0x19, 0x86, 0x40, 0xB0, 0x30,
+/* 0xC3 */ 0x21, 0x07, 0x80, 0x00, 0x30, 0x1E, 0x07, 0x81, 0x20, 0xCC, 0x33, 0x0F, 0xC6, 0x19, 0x86, 0x40, 0xB0, 0x30,
+/* 0xC4 */ 0x33, 0x00, 0x00, 0xC0, 0x78, 0x1E, 0x04, 0x83, 0x30, 0xCC, 0x33, 0x1F, 0xE6, 0x19, 0x02, 0xC0, 0xF0, 0x30,
+/* 0xC5 */ 0x30, 0x60, 0x00, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xFF,
+/* 0xC6 */ 0x06, 0x01, 0x80, 0x00, 0x0F, 0xC3, 0x0C, 0xC0, 0xD0, 0x1E, 0x00, 0xC0, 0x18, 0x03, 0x01, 0xA0, 0x36, 0x0C, 0x61, 0x87, 0xC0,
+/* 0xC7 */ 0x1F, 0x06, 0x19, 0x83, 0xA0, 0x3C, 0x01, 0x80, 0x30, 0x06, 0x00, 0xC0, 0x68, 0x0D, 0x83, 0x18, 0xE1, 0xF0, 0x08, 0x01, 0xC0, 0x18, 0x0E, 0x00,
+/* 0xC8 */ 0x19, 0x81, 0xE0, 0x00, 0x0F, 0xC3, 0x0C, 0xC0, 0xF0, 0x1E, 0x00, 0xC0, 0x18, 0x03, 0x01, 0xA0, 0x36, 0x0C, 0x61, 0x87, 0xC0,
+/* 0xC9 */ 0x0C, 0x0C, 0x00, 0x1F, 0xFC, 0x06, 0x03, 0x01, 0x80, 0xFF, 0x60, 0x30, 0x18, 0x0C, 0x07, 0xFC,
+/* 0xCA */ 0xFF, 0xD8, 0x03, 0x00, 0x60, 0x0C, 0x01, 0x80, 0x3F, 0xF6, 0x00, 0xC0, 0x18, 0x03, 0x00, 0x60, 0x0F, 0xFC, 0x01, 0x80, 0x60, 0x0C, 0x00, 0xE0,
+/* 0xCB */ 0x33, 0x00, 0x3F, 0xF8, 0x0C, 0x06, 0x03, 0x01, 0xFE, 0xC0, 0x60, 0x30, 0x18, 0x0C, 0x07, 0xFC,
+/* 0xCC */ 0x33, 0x0F, 0x00, 0x1F, 0xFC, 0x06, 0x03, 0x01, 0x80, 0xFF, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0xFE,
+/* 0xCD */ 0x78, 0x36, 0xDB, 0x6D, 0xB6, 0xC0,
+/* 0xCE */ 0x76, 0xC0, 0x63, 0x18, 0xC6, 0x31, 0x8C, 0x63, 0x18,
+/* 0xCF */ 0x66, 0x0F, 0x00, 0x03, 0xF8, 0xC3, 0x30, 0x6C, 0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC1, 0xB0, 0xEF, 0xE0,
+/* 0xD0 */ 0x7F, 0x0C, 0x31, 0x83, 0x30, 0x36, 0x06, 0xC0, 0xFE, 0x1B, 0x03, 0x60, 0x6C, 0x0D, 0x83, 0x30, 0xE7, 0xF0,
+/* 0xD1 */ 0x03, 0x01, 0x83, 0x81, 0xF0, 0x3F, 0x07, 0xA0, 0xF6, 0x1E, 0x63, 0xC4, 0x78, 0xCF, 0x0D, 0xE1, 0xBC, 0x1F, 0x81, 0xC0,
+/* 0xD2 */ 0x19, 0x81, 0xE3, 0x81, 0xF0, 0x3F, 0x07, 0xA0, 0xF6, 0x1E, 0x63, 0xC4, 0x78, 0xCF, 0x0D, 0xE1, 0xBC, 0x1F, 0x81, 0xC0,
+/* 0xD3 */ 0x03, 0x00, 0x60, 0x00, 0x00, 0xF0, 0x39, 0xC6, 0x06, 0x60, 0x6C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, 0x36, 0x06, 0x60, 0x63, 0x9C, 0x0F, 0x00,
+/* 0xD4 */ 0x0F, 0x01, 0x98, 0x00, 0x00, 0xF0, 0x39, 0xC6, 0x06, 0x60, 0x6C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, 0x36, 0x06, 0x60, 0x63, 0x9C, 0x0F, 0x00,
+/* 0xD5 */ 0x0D, 0x81, 0xB0, 0x00, 0x00, 0xF0, 0x39, 0xC6, 0x06, 0x60, 0x6C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, 0x36, 0x06, 0x60, 0x63, 0x9C, 0x0F, 0x00,
+/* 0xD6 */ 0x19, 0x81, 0x98, 0x00, 0x00, 0xF0, 0x39, 0xC6, 0x06, 0x60, 0x6C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, 0x36, 0x06, 0x60, 0x63, 0x9C, 0x0F, 0x00,
+/* 0xD7 */ 0x83, 0x89, 0xA1, 0x83, 0x89, 0xA1, 0x80,
+/* 0xD8 */ 0x33, 0x01, 0xE0, 0xFF, 0x8C, 0x0E, 0xC0, 0x6C, 0x06, 0xC0, 0x6C, 0x0C, 0xFF, 0x8C, 0x0E, 0xC0, 0x6C, 0x06, 0xC0, 0x6C, 0x06, 0xC0, 0x70,
+/* 0xD9 */ 0x04, 0x01, 0x43, 0x11, 0xE0, 0x3C, 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x36, 0x0C, 0x3E, 0x00,
+/* 0xDA */ 0x06, 0x01, 0x83, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x36, 0x0C, 0x3E, 0x00,
+/* 0xDB */ 0x0D, 0x83, 0x63, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x36, 0x0C, 0x3E, 0x00,
+/* 0xDC */ 0x1B, 0x00, 0x03, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x36, 0x0C, 0x3E, 0x00,
+/* 0xDD */ 0x03, 0x0C, 0x63, 0x60, 0x63, 0x0C, 0x30, 0xC1, 0x98, 0x1D, 0x80, 0xF0, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60,
+/* 0xDE */ 0xFF, 0x86, 0x03, 0x01, 0x80, 0xC0, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x01, 0x80, 0xC0, 0x40, 0x3C, 0x06, 0x1E, 0x00,
+/* 0xDF */ 0x3C, 0x33, 0x30, 0xD8, 0x6C, 0x36, 0x33, 0x39, 0x86, 0xC1, 0xE0, 0xF0, 0x78, 0x6D, 0xE0,
+/* 0xE0 */ 0x19, 0x89, 0xBE, 0x63, 0x18, 0xC6, 0x31, 0x8C, 0x00,
+/* 0xE1 */ 0x0C, 0x04, 0x04, 0x0F, 0xCE, 0x36, 0x18, 0x0C, 0x1E, 0x7B, 0x61, 0xB0, 0xD8, 0xE7, 0xB8,
+/* 0xE2 */ 0x10, 0x14, 0x1B, 0x0F, 0xCE, 0x36, 0x18, 0x0C, 0x1E, 0x7B, 0x61, 0xB0, 0xD8, 0xE7, 0xB8,
+/* 0xE3 */ 0x66, 0x1E, 0x00, 0x0F, 0xCE, 0x36, 0x18, 0x0C, 0x1E, 0x7B, 0x61, 0xB0, 0xD8, 0xE7, 0xB8,
+/* 0xE4 */ 0x66, 0x00, 0x1F, 0x9C, 0x6C, 0x30, 0x18, 0x3C, 0xF6, 0xC3, 0x61, 0xB1, 0xCF, 0x70,
+/* 0xE5 */ 0x78, 0x36, 0xDB, 0x6D, 0xB6, 0xD8,
+/* 0xE6 */ 0x0C, 0x08, 0x10, 0x3C, 0x66, 0xC3, 0xC0, 0xC0, 0xC0, 0xC0, 0xC3, 0x66, 0x3C,
+/* 0xE7 */ 0x3C, 0x66, 0xC3, 0xC0, 0xC0, 0xC0, 0xC0, 0xC3, 0x66, 0x3C, 0x10, 0x1C, 0x0C, 0x38,
+/* 0xE8 */ 0x44, 0x28, 0x38, 0x3C, 0x66, 0xC3, 0xC0, 0xC0, 0xC0, 0xC0, 0xC3, 0x66, 0x3C,
+/* 0xE9 */ 0x0C, 0x08, 0x18, 0x3C, 0x66, 0xC3, 0xC3, 0xFF, 0xC0, 0xC0, 0xC3, 0x66, 0x3C,
+/* 0xEA */ 0x3C, 0x62, 0xC3, 0xC3, 0xFF, 0xC0, 0xC0, 0xC3, 0x66, 0x3E, 0x04, 0x0C, 0x0C, 0x06,
+/* 0xEB */ 0x66, 0x00, 0x3C, 0x66, 0xC3, 0xC3, 0xFF, 0xC0, 0xC0, 0xC3, 0x66, 0x3C,
+/* 0xEC */ 0x64, 0x2C, 0x18, 0x3C, 0x66, 0xC3, 0xC3, 0xFF, 0xC0, 0xC0, 0xC3, 0x66, 0x3C,
+/* 0xED */ 0x7A, 0x6D, 0xB6, 0xDB, 0x6C,
+/* 0xEE */ 0x69, 0x06, 0x66, 0x66, 0x66, 0x66, 0x60,
+/* 0xEF */ 0x03, 0x30, 0x32, 0x03, 0x43, 0xB0, 0x67, 0x0C, 0x30, 0xC3, 0x0C, 0x30, 0xC3, 0x0C, 0x30, 0xC3, 0x06, 0x70, 0x3B, 0x00,
+/* 0xF0 */ 0x03, 0x07, 0xC0, 0xC7, 0x66, 0x76, 0x1B, 0x0D, 0x86, 0xC3, 0x61, 0xB0, 0xCC, 0xE3, 0xB0,
+/* 0xF1 */ 0x0C, 0x18, 0x00, 0xDE, 0xE3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3,
+/* 0xF2 */ 0x66, 0x3C, 0x00, 0xDE, 0xE3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3,
+/* 0xF3 */ 0x0C, 0x18, 0x00, 0x3C, 0x66, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x66, 0x3C,
+/* 0xF4 */ 0x18, 0x24, 0x00, 0x3C, 0x66, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x66, 0x3C,
+/* 0xF5 */ 0x36, 0x6C, 0x00, 0x3C, 0x66, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x66, 0x3C,
+/* 0xF6 */ 0x66, 0x00, 0x3C, 0x66, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x66, 0x3C,
+/* 0xF7 */ 0x18, 0x00, 0x00, 0x1F, 0xF0, 0x00, 0x00, 0x00, 0x30,
+/* 0xF8 */ 0xDB, 0x81, 0xBE, 0x63, 0x18, 0xC6, 0x31, 0x8C, 0x00,
+/* 0xF9 */ 0x10, 0x28, 0x10, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC7, 0x7B,
+/* 0xFA */ 0x06, 0x0C, 0x18, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC7, 0x7B,
+/* 0xFB */ 0x36, 0x6C, 0x00, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC7, 0x7B,
+/* 0xFC */ 0x66, 0x00, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC7, 0x7B,
+/* 0xFD */ 0x06, 0x04, 0x08, 0xC1, 0x43, 0x63, 0x62, 0x26, 0x36, 0x34, 0x1C, 0x1C, 0x18, 0x18, 0x18, 0x10, 0x60,
+/* 0xFE */ 0x63, 0x3C, 0xC6, 0x31, 0x8C, 0x63, 0x18, 0xE2, 0x1C, 0x6F,
+/* 0xFF */ 0xC0,
};
const GFXglyph FreeSans9pt_Win1250Glyphs[] PROGMEM = {
- /* ' ' 0x20 */ {0, 0, 0, 5, 0, 0},
- /* '!' 0x21 */ {0, 2, 13, 6, 2, -12},
- /* '"' 0x22 */ {4, 5, 4, 6, 1, -12},
- /* '#' 0x23 */ {7, 10, 12, 10, 0, -11},
- /* '$' 0x24 */ {22, 9, 16, 10, 1, -13},
- /* '%' 0x25 */ {40, 16, 13, 16, 1, -12},
- /* '&' 0x26 */ {66, 10, 13, 12, 1, -12},
- /* ''' 0x27 */ {83, 2, 4, 4, 1, -12},
- /* '(' 0x28 */ {84, 4, 17, 6, 1, -12},
- /* ')' 0x29 */ {93, 4, 17, 6, 1, -12},
- /* '*' 0x2A */ {102, 5, 5, 7, 1, -12},
- /* '+' 0x2B */ {106, 6, 8, 11, 3, -7},
- /* ',' 0x2C */ {112, 2, 4, 5, 2, 0},
- /* '-' 0x2D */ {113, 4, 1, 6, 1, -4},
- /* '.' 0x2E */ {114, 2, 1, 5, 1, 0},
- /* '/' 0x2F */ {115, 5, 13, 5, 0, -12},
- /* '0' 0x30 */ {124, 8, 13, 10, 1, -12},
- /* '1' 0x31 */ {137, 4, 13, 10, 3, -12},
- /* '2' 0x32 */ {144, 9, 13, 10, 1, -12},
- /* '3' 0x33 */ {159, 8, 13, 10, 1, -12},
- /* '4' 0x34 */ {172, 7, 13, 10, 2, -12},
- /* '5' 0x35 */ {184, 9, 13, 10, 1, -12},
- /* '6' 0x36 */ {199, 9, 13, 10, 1, -12},
- /* '7' 0x37 */ {214, 8, 13, 10, 0, -12},
- /* '8' 0x38 */ {227, 9, 13, 10, 1, -12},
- /* '9' 0x39 */ {242, 8, 13, 10, 1, -12},
- /* ':' 0x3A */ {255, 2, 10, 5, 1, -9},
- /* ';' 0x3B */ {258, 3, 12, 5, 1, -8},
- /* '<' 0x3C */ {263, 9, 9, 11, 1, -8},
- /* '=' 0x3D */ {274, 9, 4, 11, 1, -5},
- /* '>' 0x3E */ {279, 9, 8, 11, 1, -7},
- /* '?' 0x3F */ {288, 9, 13, 10, 1, -12},
- /* '@' 0x40 */ {303, 17, 16, 18, 1, -12},
- /* 'A' 0x41 */ {337, 12, 13, 12, 0, -12},
- /* 'B' 0x42 */ {357, 11, 13, 12, 1, -12},
- /* 'C' 0x43 */ {375, 11, 13, 13, 1, -12},
- /* 'D' 0x44 */ {393, 11, 13, 13, 1, -12},
- /* 'E' 0x45 */ {411, 9, 13, 11, 1, -12},
- /* 'F' 0x46 */ {426, 8, 13, 11, 1, -12},
- /* 'G' 0x47 */ {439, 12, 13, 14, 1, -12},
- /* 'H' 0x48 */ {459, 11, 13, 13, 1, -12},
- /* 'I' 0x49 */ {477, 2, 13, 5, 2, -12},
- /* 'J' 0x4A */ {481, 7, 13, 10, 1, -12},
- /* 'K' 0x4B */ {493, 10, 13, 12, 1, -12},
- /* 'L' 0x4C */ {510, 8, 13, 10, 1, -12},
- /* 'M' 0x4D */ {523, 13, 13, 15, 1, -12},
- /* 'N' 0x4E */ {545, 11, 13, 13, 1, -12},
- /* 'O' 0x4F */ {563, 13, 13, 14, 1, -12},
- /* 'P' 0x50 */ {585, 10, 13, 12, 1, -12},
- /* 'Q' 0x51 */ {602, 13, 14, 14, 1, -12},
- /* 'R' 0x52 */ {625, 12, 13, 13, 1, -12},
- /* 'S' 0x53 */ {645, 10, 13, 12, 1, -12},
- /* 'T' 0x54 */ {662, 9, 13, 11, 1, -12},
- /* 'U' 0x55 */ {677, 11, 13, 13, 1, -12},
- /* 'V' 0x56 */ {695, 11, 13, 11, 0, -12},
- /* 'W' 0x57 */ {713, 16, 13, 17, 0, -12},
- /* 'X' 0x58 */ {739, 10, 13, 12, 1, -12},
- /* 'Y' 0x59 */ {756, 12, 13, 12, 0, -12},
- /* 'Z' 0x5A */ {776, 10, 13, 11, 1, -12},
- /* '[' 0x5B */ {793, 3, 17, 5, 1, -12},
- /* '\' 0x5C */ {800, 5, 13, 5, 0, -12},
- /* ']' 0x5D */ {809, 3, 17, 5, 0, -12},
- /* '^' 0x5E */ {816, 7, 7, 8, 1, -12},
- /* '_' 0x5F */ {823, 10, 1, 10, 0, 3},
- /* '`' 0x60 */ {825, 4, 3, 5, 0, -12},
- /* 'a' 0x61 */ {827, 9, 10, 10, 1, -9},
- /* 'b' 0x62 */ {839, 9, 13, 10, 1, -12},
- /* 'c' 0x63 */ {854, 8, 10, 9, 1, -9},
- /* 'd' 0x64 */ {864, 8, 13, 10, 1, -12},
- /* 'e' 0x65 */ {877, 8, 10, 10, 1, -9},
- /* 'f' 0x66 */ {887, 4, 13, 5, 1, -12},
- /* 'g' 0x67 */ {894, 8, 14, 10, 1, -9},
- /* 'h' 0x68 */ {908, 8, 13, 10, 1, -12},
- /* 'i' 0x69 */ {921, 2, 13, 4, 1, -12},
- /* 'j' 0x6A */ {925, 4, 17, 4, 0, -12},
- /* 'k' 0x6B */ {934, 8, 13, 9, 1, -12},
- /* 'l' 0x6C */ {947, 2, 13, 4, 1, -12},
- /* 'm' 0x6D */ {951, 13, 10, 15, 1, -9},
- /* 'n' 0x6E */ {968, 8, 10, 10, 1, -9},
- /* 'o' 0x6F */ {978, 8, 10, 10, 1, -9},
- /* 'p' 0x70 */ {988, 9, 13, 10, 1, -9},
- /* 'q' 0x71 */ {1003, 8, 13, 10, 1, -9},
- /* 'r' 0x72 */ {1016, 5, 10, 6, 1, -9},
- /* 's' 0x73 */ {1023, 8, 10, 9, 1, -9},
- /* 't' 0x74 */ {1033, 4, 12, 5, 1, -11},
- /* 'u' 0x75 */ {1039, 8, 10, 10, 1, -9},
- /* 'v' 0x76 */ {1049, 9, 10, 9, 0, -9},
- /* 'w' 0x77 */ {1061, 13, 10, 13, 0, -9},
- /* 'x' 0x78 */ {1078, 7, 10, 9, 1, -9},
- /* 'y' 0x79 */ {1087, 8, 14, 9, 0, -9},
- /* 'z' 0x7A */ {1101, 7, 10, 9, 1, -9},
- /* '{' 0x7B */ {1110, 4, 17, 6, 1, -12},
- /* '|' 0x7C */ {1119, 2, 17, 4, 2, -12},
- /* '}' 0x7D */ {1124, 4, 17, 6, 1, -12},
- /* '~' 0x7E */ {1133, 7, 3, 9, 1, -7},
- /* 0x7F */ {1136, 13, 14, 15, 1, -12},
- /* 0x80 */ {1159, 10, 13, 12, 1, -12},
- /* 0x81 */ {1176, 0, 0, 0, 0, 0},
- /* 0x82 */ {1176, 2, 3, 5, 1, 0},
- /* 0x83 */ {1177, 0, 0, 0, 0, 0},
- /* 0x84 */ {1177, 5, 3, 7, 1, 0},
- /* 0x85 */ {1179, 10, 1, 12, 1, 0},
- /* 0x86 */ {1181, 8, 16, 10, 1, -12},
- /* 0x87 */ {1197, 8, 16, 10, 1, -12},
- /* 0x88 */ {1213, 0, 0, 0, 0, 0},
- /* 0x89 */ {1213, 18, 13, 18, 0, -12},
- /* 0x8A */ {1243, 10, 15, 12, 1, -14},
- /* 0x8B */ {1262, 2, 4, 4, 1, -6},
- /* 0x8C */ {1263, 10, 15, 12, 1, -14},
- /* 0x8D */ {1282, 9, 15, 11, 1, -14},
- /* 0x8E */ {1299, 10, 15, 11, 1, -14},
- /* 0x8F */ {1318, 10, 15, 11, 1, -14},
- /* 0x90 */ {1337, 0, 0, 0, 0, 0},
- /* 0x91 */ {1337, 2, 4, 4, 2, -12},
- /* 0x92 */ {1338, 2, 4, 4, 1, -12},
- /* 0x93 */ {1339, 5, 4, 7, 2, -12},
- /* 0x94 */ {1342, 5, 4, 7, 1, -12},
- /* 0x95 */ {1345, 4, 5, 7, 1, -8},
- /* 0x96 */ {1348, 7, 1, 9, 1, -4},
- /* 0x97 */ {1349, 16, 1, 18, 1, -4},
- /* 0x98 */ {1351, 0, 0, 0, 0, 0},
- /* 0x99 */ {1351, 18, 10, 18, 1, -13},
- /* 0x9A */ {1374, 8, 13, 9, 1, -12},
- /* 0x9B */ {1387, 2, 4, 5, 2, -6},
- /* 0x9C */ {1388, 8, 13, 9, 1, -12},
- /* 0x9D */ {1401, 6, 13, 8, 1, -12},
- /* 0x9E */ {1411, 7, 13, 9, 1, -12},
- /* 0x9F */ {1423, 7, 13, 9, 1, -12},
- /* 0xA0 */ {1435, 0, 0, 5, 0, 0},
- /* 0xA1 */ {1435, 5, 3, 6, 0, -12},
- /* 0xA2 */ {1437, 6, 2, 6, 0, -12},
- /* 0xA3 */ {1439, 9, 13, 11, 1, -12},
- /* 0xA4 */ {1454, 7, 6, 10, 2, -8},
- /* 0xA5 */ {1460, 12, 17, 12, 1, -12},
- /* 0xA6 */ {1486, 2, 17, 5, 2, -12},
- /* 0xA7 */ {1491, 9, 17, 10, 1, -12},
- /* 0xA8 */ {1511, 6, 1, 6, 0, -11},
- /* 0xA9 */ {1512, 14, 13, 14, 1, -12},
- /* 0xAA */ {1535, 10, 17, 12, 1, -12},
- /* 0xAB */ {1557, 7, 6, 9, 1, -7},
- /* 0xAC */ {1563, 9, 5, 11, 2, -5},
- /* 0xAD */ {1569, 0, 0, 0, 0, 0},
- /* 0xAE */ {1569, 14, 13, 14, 1, -12},
- /* 0xAF */ {1592, 10, 15, 11, 1, -14},
- /* 0xB0 */ {1611, 5, 5, 11, 3, -11},
- /* 0xB1 */ {1615, 9, 11, 11, 1, -10},
- /* 0xB2 */ {1628, 4, 4, 6, 1, 1},
- /* 0xB3 */ {1630, 4, 13, 5, 1, -12},
- /* 0xB4 */ {1637, 4, 3, 6, 2, -12},
- /* 0xB5 */ {1639, 9, 13, 10, 1, -9},
- /* 0xB6 */ {1654, 8, 16, 10, 2, -12},
- /* 0xB7 */ {1670, 3, 1, 5, 1, -4},
- /* 0xB8 */ {1671, 5, 4, 6, 1, 1},
- /* 0xB9 */ {1674, 10, 14, 10, 1, -9},
- /* 0xBA */ {1692, 8, 14, 9, 1, -9},
- /* 0xBB */ {1706, 7, 6, 9, 1, -7},
- /* 0xBC */ {1712, 8, 13, 10, 1, -12},
- /* 0xBD */ {1725, 6, 3, 6, 0, -12},
- /* 0xBE */ {1728, 5, 13, 7, 1, -12},
- /* 0xBF */ {1737, 7, 12, 9, 1, -11},
- /* 0xC0 */ {1748, 12, 15, 13, 1, -14},
- /* 0xC1 */ {1771, 10, 14, 12, 1, -13},
- /* 0xC2 */ {1789, 10, 14, 12, 1, -13},
- /* 0xC3 */ {1807, 10, 14, 12, 1, -13},
- /* 0xC4 */ {1825, 10, 14, 12, 1, -13},
- /* 0xC5 */ {1843, 8, 14, 10, 1, -13},
- /* 0xC6 */ {1857, 11, 15, 13, 1, -14},
- /* 0xC7 */ {1878, 11, 17, 13, 1, -12},
- /* 0xC8 */ {1902, 11, 15, 13, 1, -14},
- /* 0xC9 */ {1923, 9, 14, 11, 1, -13},
- /* 0xCA */ {1939, 11, 17, 12, 1, -12},
- /* 0xCB */ {1963, 9, 14, 11, 1, -13},
- /* 0xCC */ {1979, 9, 15, 11, 1, -14},
- /* 0xCD */ {1996, 3, 14, 5, 1, -13},
- /* 0xCE */ {2002, 5, 14, 5, 0, -13},
- /* 0xCF */ {2011, 10, 15, 13, 2, -14},
- /* 0xD0 */ {2030, 11, 13, 13, 1, -12},
- /* 0xD1 */ {2048, 11, 14, 13, 1, -13},
- /* 0xD2 */ {2068, 11, 14, 13, 1, -13},
- /* 0xD3 */ {2088, 12, 15, 13, 1, -14},
- /* 0xD4 */ {2111, 12, 15, 13, 1, -14},
- /* 0xD5 */ {2134, 12, 15, 13, 1, -14},
- /* 0xD6 */ {2157, 12, 15, 13, 1, -14},
- /* 0xD7 */ {2180, 7, 7, 11, 2, -7},
- /* 0xD8 */ {2187, 12, 15, 13, 1, -14},
- /* 0xD9 */ {2210, 11, 14, 13, 1, -13},
- /* 0xDA */ {2230, 11, 14, 13, 1, -13},
- /* 0xDB */ {2250, 11, 14, 13, 1, -13},
- /* 0xDC */ {2270, 11, 14, 13, 1, -13},
- /* 0xDD */ {2290, 12, 14, 12, 0, -13},
- /* 0xDE */ {2311, 9, 17, 11, 1, -12},
- /* 0xDF */ {2331, 9, 13, 11, 1, -12},
- /* 0xE0 */ {2346, 5, 13, 6, 1, -12},
- /* 0xE1 */ {2355, 9, 13, 10, 1, -12},
- /* 0xE2 */ {2370, 9, 13, 10, 1, -12},
- /* 0xE3 */ {2385, 9, 13, 10, 1, -12},
- /* 0xE4 */ {2400, 9, 12, 10, 1, -11},
- /* 0xE5 */ {2414, 3, 15, 4, 0, -14},
- /* 0xE6 */ {2420, 8, 13, 9, 1, -12},
- /* 0xE7 */ {2433, 8, 14, 9, 1, -9},
- /* 0xE8 */ {2447, 8, 13, 9, 1, -12},
- /* 0xE9 */ {2460, 8, 13, 10, 1, -12},
- /* 0xEA */ {2473, 8, 14, 10, 1, -9},
- /* 0xEB */ {2487, 8, 12, 10, 1, -11},
- /* 0xEC */ {2499, 8, 13, 10, 1, -12},
- /* 0xED */ {2512, 3, 13, 4, 1, -12},
- /* 0xEE */ {2517, 4, 13, 5, 0, -12},
- /* 0xEF */ {2524, 12, 13, 12, 1, -12},
- /* 0xF0 */ {2544, 9, 13, 10, 1, -12},
- /* 0xF1 */ {2559, 8, 13, 10, 1, -12},
- /* 0xF2 */ {2572, 8, 13, 10, 1, -12},
- /* 0xF3 */ {2585, 8, 13, 10, 1, -12},
- /* 0xF4 */ {2598, 8, 13, 10, 1, -12},
- /* 0xF5 */ {2611, 8, 13, 10, 1, -12},
- /* 0xF6 */ {2624, 8, 12, 10, 1, -11},
- /* 0xF7 */ {2636, 9, 8, 11, 1, -7},
- /* 0xF8 */ {2645, 5, 13, 6, 1, -12},
- /* 0xF9 */ {2654, 8, 13, 10, 1, -12},
- /* 0xFA */ {2667, 8, 13, 10, 1, -12},
- /* 0xFB */ {2680, 8, 13, 10, 1, -12},
- /* 0xFC */ {2693, 8, 12, 10, 1, -11},
- /* 0xFD */ {2705, 8, 17, 9, 0, -12},
- /* 0xFE */ {2722, 5, 16, 5, 1, -11},
- /* 0xFF */ {2732, 2, 1, 6, 2, -11},
+/* 0x01 */ { 0, 15, 15, 17, 1, -13 },
+/* 0x02 */ { 29, 15, 15, 17, 1, -13 },
+/* 0x03 */ { 58, 15, 16, 17, 1, -14 },
+/* 0x04 */ { 88, 15, 16, 17, 1, -14 },
+/* 0x05 */ { 118, 16, 15, 18, 1, -13 },
+/* 0x06 */ { 148, 15, 15, 17, 1, -13 },
+/* 0x07 */ { 177, 0, 0, 8, 0, 0 },
+/* 0x08 */ { 177, 17, 16, 19, 1, -14 },
+/* 0x09 */ { 211, 17, 12, 19, 1, -12 },
+/* 0x0A */ { 237, 0, 0, 8, 0, 0 },
+/* 0x0B */ { 237, 17, 16, 19, 1, -14 },
+/* 0x0C */ { 271, 15, 14, 17, 1, -12 },
+/* 0x0D */ { 298, 0, 0, 8, 0, 0 },
+/* 0x0E */ { 298, 15, 16, 17, 1, -14 },
+/* 0x0F */ { 328, 15, 15, 17, 1, -13 },
+/* 0x10 */ { 357, 15, 15, 17, 1, -13 },
+/* 0x11 */ { 386, 15, 16, 17, 1, -14 },
+/* 0x12 */ { 416, 17, 17, 19, 1, -15 },
+/* 0x13 */ { 453, 15, 16, 17, 1, -14 },
+/* 0x14 */ { 483, 15, 16, 17, 1, -14 },
+/* 0x15 */ { 513, 15, 16, 17, 1, -14 },
+/* 0x16 */ { 543, 11, 16, 13, 1, -14 },
+/* 0x17 */ { 565, 15, 16, 17, 1, -14 },
+/* 0x18 */ { 595, 18, 15, 20, 1, -13 },
+/* 0x19 */ { 629, 15, 16, 17, 1, -14 },
+/* 0x1A */ { 659, 13, 14, 15, 1, -12 },
+/* 0x1B */ { 682, 17, 16, 19, 1, -14 },
+/* 0x1C */ { 716, 15, 16, 17, 1, -14 },
+/* 0x1D */ { 746, 15, 15, 17, 1, -13 },
+/* 0x1E */ { 775, 17, 16, 19, 1, -14 },
+/* 0x1F */ { 809, 11, 16, 13, 1, -14 },
+/* ' ' 0x20 */ { 831, 0, 0, 5, 0, 0 },
+/* '!' 0x21 */ { 831, 2, 13, 6, 2, -12 },
+/* '"' 0x22 */ { 835, 5, 4, 6, 1, -12 },
+/* '#' 0x23 */ { 838, 10, 12, 10, 0, -11 },
+/* '$' 0x24 */ { 853, 9, 16, 10, 1, -13 },
+/* '%' 0x25 */ { 871, 16, 13, 16, 1, -12 },
+/* '&' 0x26 */ { 897, 10, 13, 12, 1, -12 },
+/* ''' 0x27 */ { 914, 2, 4, 4, 1, -12 },
+/* '(' 0x28 */ { 915, 4, 17, 6, 1, -12 },
+/* ')' 0x29 */ { 924, 4, 17, 6, 1, -12 },
+/* '*' 0x2A */ { 933, 5, 5, 7, 1, -12 },
+/* '+' 0x2B */ { 937, 6, 8, 11, 3, -7 },
+/* ',' 0x2C */ { 943, 2, 4, 5, 2, 0 },
+/* '-' 0x2D */ { 944, 4, 1, 6, 1, -4 },
+/* '.' 0x2E */ { 945, 2, 1, 5, 1, 0 },
+/* '/' 0x2F */ { 946, 5, 13, 5, 0, -12 },
+/* '0' 0x30 */ { 955, 8, 13, 10, 1, -12 },
+/* '1' 0x31 */ { 968, 4, 13, 10, 3, -12 },
+/* '2' 0x32 */ { 975, 9, 13, 10, 1, -12 },
+/* '3' 0x33 */ { 990, 8, 13, 10, 1, -12 },
+/* '4' 0x34 */ { 1003, 7, 13, 10, 2, -12 },
+/* '5' 0x35 */ { 1015, 9, 13, 10, 1, -12 },
+/* '6' 0x36 */ { 1030, 9, 13, 10, 1, -12 },
+/* '7' 0x37 */ { 1045, 8, 13, 10, 0, -12 },
+/* '8' 0x38 */ { 1058, 9, 13, 10, 1, -12 },
+/* '9' 0x39 */ { 1073, 8, 13, 10, 1, -12 },
+/* ':' 0x3A */ { 1086, 2, 10, 5, 1, -9 },
+/* ';' 0x3B */ { 1089, 3, 12, 5, 1, -8 },
+/* '<' 0x3C */ { 1094, 9, 9, 11, 1, -8 },
+/* '=' 0x3D */ { 1105, 9, 4, 11, 1, -5 },
+/* '>' 0x3E */ { 1110, 9, 8, 11, 1, -7 },
+/* '?' 0x3F */ { 1119, 9, 13, 10, 1, -12 },
+/* '@' 0x40 */ { 1134, 17, 16, 18, 1, -12 },
+/* 'A' 0x41 */ { 1168, 12, 13, 12, 0, -12 },
+/* 'B' 0x42 */ { 1188, 11, 13, 12, 1, -12 },
+/* 'C' 0x43 */ { 1206, 11, 13, 13, 1, -12 },
+/* 'D' 0x44 */ { 1224, 11, 13, 13, 1, -12 },
+/* 'E' 0x45 */ { 1242, 9, 13, 11, 1, -12 },
+/* 'F' 0x46 */ { 1257, 8, 13, 11, 1, -12 },
+/* 'G' 0x47 */ { 1270, 12, 13, 14, 1, -12 },
+/* 'H' 0x48 */ { 1290, 11, 13, 13, 1, -12 },
+/* 'I' 0x49 */ { 1308, 2, 13, 5, 2, -12 },
+/* 'J' 0x4A */ { 1312, 7, 13, 10, 1, -12 },
+/* 'K' 0x4B */ { 1324, 10, 13, 12, 1, -12 },
+/* 'L' 0x4C */ { 1341, 8, 13, 10, 1, -12 },
+/* 'M' 0x4D */ { 1354, 13, 13, 15, 1, -12 },
+/* 'N' 0x4E */ { 1376, 11, 13, 13, 1, -12 },
+/* 'O' 0x4F */ { 1394, 13, 13, 14, 1, -12 },
+/* 'P' 0x50 */ { 1416, 10, 13, 12, 1, -12 },
+/* 'Q' 0x51 */ { 1433, 13, 14, 14, 1, -12 },
+/* 'R' 0x52 */ { 1456, 12, 13, 13, 1, -12 },
+/* 'S' 0x53 */ { 1476, 10, 13, 12, 1, -12 },
+/* 'T' 0x54 */ { 1493, 9, 13, 11, 1, -12 },
+/* 'U' 0x55 */ { 1508, 11, 13, 13, 1, -12 },
+/* 'V' 0x56 */ { 1526, 11, 13, 11, 0, -12 },
+/* 'W' 0x57 */ { 1544, 16, 13, 17, 0, -12 },
+/* 'X' 0x58 */ { 1570, 10, 13, 12, 1, -12 },
+/* 'Y' 0x59 */ { 1587, 12, 13, 12, 0, -12 },
+/* 'Z' 0x5A */ { 1607, 10, 13, 11, 1, -12 },
+/* '[' 0x5B */ { 1624, 3, 17, 5, 1, -12 },
+/* '\' 0x5C */ { 1631, 5, 13, 5, 0, -12 },
+/* ']' 0x5D */ { 1640, 3, 17, 5, 0, -12 },
+/* '^' 0x5E */ { 1647, 7, 7, 8, 1, -12 },
+/* '_' 0x5F */ { 1654, 10, 1, 10, 0, 3 },
+/* '`' 0x60 */ { 1656, 4, 3, 5, 0, -12 },
+/* 'a' 0x61 */ { 1658, 9, 10, 10, 1, -9 },
+/* 'b' 0x62 */ { 1670, 9, 13, 10, 1, -12 },
+/* 'c' 0x63 */ { 1685, 8, 10, 9, 1, -9 },
+/* 'd' 0x64 */ { 1695, 8, 13, 10, 1, -12 },
+/* 'e' 0x65 */ { 1708, 8, 10, 10, 1, -9 },
+/* 'f' 0x66 */ { 1718, 4, 13, 5, 1, -12 },
+/* 'g' 0x67 */ { 1725, 8, 14, 10, 1, -9 },
+/* 'h' 0x68 */ { 1739, 8, 13, 10, 1, -12 },
+/* 'i' 0x69 */ { 1752, 2, 13, 4, 1, -12 },
+/* 'j' 0x6A */ { 1756, 4, 17, 4, 0, -12 },
+/* 'k' 0x6B */ { 1765, 8, 13, 9, 1, -12 },
+/* 'l' 0x6C */ { 1778, 2, 13, 4, 1, -12 },
+/* 'm' 0x6D */ { 1782, 13, 10, 15, 1, -9 },
+/* 'n' 0x6E */ { 1799, 8, 10, 10, 1, -9 },
+/* 'o' 0x6F */ { 1809, 8, 10, 10, 1, -9 },
+/* 'p' 0x70 */ { 1819, 9, 13, 10, 1, -9 },
+/* 'q' 0x71 */ { 1834, 8, 13, 10, 1, -9 },
+/* 'r' 0x72 */ { 1847, 5, 10, 6, 1, -9 },
+/* 's' 0x73 */ { 1854, 8, 10, 9, 1, -9 },
+/* 't' 0x74 */ { 1864, 4, 12, 5, 1, -11 },
+/* 'u' 0x75 */ { 1870, 8, 10, 10, 1, -9 },
+/* 'v' 0x76 */ { 1880, 9, 10, 9, 0, -9 },
+/* 'w' 0x77 */ { 1892, 13, 10, 13, 0, -9 },
+/* 'x' 0x78 */ { 1909, 7, 10, 9, 1, -9 },
+/* 'y' 0x79 */ { 1918, 8, 14, 9, 0, -9 },
+/* 'z' 0x7A */ { 1932, 7, 10, 9, 1, -9 },
+/* '{' 0x7B */ { 1941, 4, 17, 6, 1, -12 },
+/* '|' 0x7C */ { 1950, 2, 17, 4, 2, -12 },
+/* '}' 0x7D */ { 1955, 4, 17, 6, 1, -12 },
+/* '~' 0x7E */ { 1964, 7, 3, 9, 1, -7 },
+/* 0x7F */ { 1967, 13, 14, 15, 1, -12 },
+/* 0x80 */ { 1990, 10, 13, 12, 1, -12 },
+/* 0x81 */ { 2007, 0, 0, 0, 0, 0 },
+/* 0x82 */ { 2007, 2, 3, 5, 1, 0 },
+/* 0x83 */ { 2008, 0, 0, 0, 0, 0 },
+/* 0x84 */ { 2008, 5, 3, 7, 1, 0 },
+/* 0x85 */ { 2010, 10, 1, 12, 1, 0 },
+/* 0x86 */ { 2012, 8, 16, 10, 1, -12 },
+/* 0x87 */ { 2028, 8, 16, 10, 1, -12 },
+/* 0x88 */ { 2044, 0, 0, 0, 0, 0 },
+/* 0x89 */ { 2044, 18, 13, 18, 0, -12 },
+/* 0x8A */ { 2074, 10, 15, 12, 1, -14 },
+/* 0x8B */ { 2093, 2, 4, 4, 1, -6 },
+/* 0x8C */ { 2094, 10, 15, 12, 1, -14 },
+/* 0x8D */ { 2113, 9, 15, 11, 1, -14 },
+/* 0x8E */ { 2130, 10, 15, 11, 1, -14 },
+/* 0x8F */ { 2149, 10, 15, 11, 1, -14 },
+/* 0x90 */ { 2168, 0, 0, 0, 0, 0 },
+/* 0x91 */ { 2168, 2, 4, 4, 2, -12 },
+/* 0x92 */ { 2169, 2, 4, 4, 1, -12 },
+/* 0x93 */ { 2170, 5, 4, 7, 2, -12 },
+/* 0x94 */ { 2173, 5, 4, 7, 1, -12 },
+/* 0x95 */ { 2176, 4, 5, 7, 1, -8 },
+/* 0x96 */ { 2179, 7, 1, 9, 1, -4 },
+/* 0x97 */ { 2180, 16, 1, 18, 1, -4 },
+/* 0x98 */ { 2182, 0, 0, 0, 0, 0 },
+/* 0x99 */ { 2182, 18, 10, 18, 1, -13 },
+/* 0x9A */ { 2205, 8, 13, 9, 1, -12 },
+/* 0x9B */ { 2218, 2, 4, 5, 2, -6 },
+/* 0x9C */ { 2219, 8, 13, 9, 1, -12 },
+/* 0x9D */ { 2232, 6, 13, 8, 1, -12 },
+/* 0x9E */ { 2242, 7, 13, 9, 1, -12 },
+/* 0x9F */ { 2254, 7, 13, 9, 1, -12 },
+/* 0xA0 */ { 2266, 0, 0, 5, 0, 0 },
+/* 0xA1 */ { 2266, 5, 3, 6, 0, -12 },
+/* 0xA2 */ { 2268, 6, 2, 6, 0, -12 },
+/* 0xA3 */ { 2270, 9, 13, 11, 1, -12 },
+/* 0xA4 */ { 2285, 7, 6, 10, 2, -8 },
+/* 0xA5 */ { 2291, 12, 17, 12, 1, -12 },
+/* 0xA6 */ { 2317, 2, 17, 5, 2, -12 },
+/* 0xA7 */ { 2322, 9, 17, 10, 1, -12 },
+/* 0xA8 */ { 2342, 6, 1, 6, 0, -11 },
+/* 0xA9 */ { 2343, 14, 13, 14, 1, -12 },
+/* 0xAA */ { 2366, 10, 17, 12, 1, -12 },
+/* 0xAB */ { 2388, 7, 6, 9, 1, -7 },
+/* 0xAC */ { 2394, 9, 5, 11, 2, -5 },
+/* 0xAD */ { 2400, 0, 0, 0, 0, 0 },
+/* 0xAE */ { 2400, 14, 13, 14, 1, -12 },
+/* 0xAF */ { 2423, 10, 15, 11, 1, -14 },
+/* 0xB0 */ { 2442, 5, 5, 11, 3, -11 },
+/* 0xB1 */ { 2446, 9, 11, 11, 1, -10 },
+/* 0xB2 */ { 2459, 4, 4, 6, 1, 1 },
+/* 0xB3 */ { 2461, 4, 13, 5, 1, -12 },
+/* 0xB4 */ { 2468, 4, 3, 6, 2, -12 },
+/* 0xB5 */ { 2470, 9, 13, 10, 1, -9 },
+/* 0xB6 */ { 2485, 8, 16, 10, 2, -12 },
+/* 0xB7 */ { 2501, 3, 1, 5, 1, -4 },
+/* 0xB8 */ { 2502, 5, 4, 6, 1, 1 },
+/* 0xB9 */ { 2505, 10, 14, 10, 1, -9 },
+/* 0xBA */ { 2523, 8, 14, 9, 1, -9 },
+/* 0xBB */ { 2537, 7, 6, 9, 1, -7 },
+/* 0xBC */ { 2543, 8, 13, 10, 1, -12 },
+/* 0xBD */ { 2556, 6, 3, 6, 0, -12 },
+/* 0xBE */ { 2559, 5, 13, 7, 1, -12 },
+/* 0xBF */ { 2568, 7, 12, 9, 1, -11 },
+/* 0xC0 */ { 2579, 12, 15, 13, 1, -14 },
+/* 0xC1 */ { 2602, 10, 14, 12, 1, -13 },
+/* 0xC2 */ { 2620, 10, 14, 12, 1, -13 },
+/* 0xC3 */ { 2638, 10, 14, 12, 1, -13 },
+/* 0xC4 */ { 2656, 10, 14, 12, 1, -13 },
+/* 0xC5 */ { 2674, 8, 14, 10, 1, -13 },
+/* 0xC6 */ { 2688, 11, 15, 13, 1, -14 },
+/* 0xC7 */ { 2709, 11, 17, 13, 1, -12 },
+/* 0xC8 */ { 2733, 11, 15, 13, 1, -14 },
+/* 0xC9 */ { 2754, 9, 14, 11, 1, -13 },
+/* 0xCA */ { 2770, 11, 17, 12, 1, -12 },
+/* 0xCB */ { 2794, 9, 14, 11, 1, -13 },
+/* 0xCC */ { 2810, 9, 15, 11, 1, -14 },
+/* 0xCD */ { 2827, 3, 14, 5, 1, -13 },
+/* 0xCE */ { 2833, 5, 14, 5, 0, -13 },
+/* 0xCF */ { 2842, 10, 15, 13, 2, -14 },
+/* 0xD0 */ { 2861, 11, 13, 13, 1, -12 },
+/* 0xD1 */ { 2879, 11, 14, 13, 1, -13 },
+/* 0xD2 */ { 2899, 11, 14, 13, 1, -13 },
+/* 0xD3 */ { 2919, 12, 15, 13, 1, -14 },
+/* 0xD4 */ { 2942, 12, 15, 13, 1, -14 },
+/* 0xD5 */ { 2965, 12, 15, 13, 1, -14 },
+/* 0xD6 */ { 2988, 12, 15, 13, 1, -14 },
+/* 0xD7 */ { 3011, 7, 7, 11, 2, -7 },
+/* 0xD8 */ { 3018, 12, 15, 13, 1, -14 },
+/* 0xD9 */ { 3041, 11, 14, 13, 1, -13 },
+/* 0xDA */ { 3061, 11, 14, 13, 1, -13 },
+/* 0xDB */ { 3081, 11, 14, 13, 1, -13 },
+/* 0xDC */ { 3101, 11, 14, 13, 1, -13 },
+/* 0xDD */ { 3121, 12, 14, 12, 0, -13 },
+/* 0xDE */ { 3142, 9, 17, 11, 1, -12 },
+/* 0xDF */ { 3162, 9, 13, 11, 1, -12 },
+/* 0xE0 */ { 3177, 5, 13, 6, 1, -12 },
+/* 0xE1 */ { 3186, 9, 13, 10, 1, -12 },
+/* 0xE2 */ { 3201, 9, 13, 10, 1, -12 },
+/* 0xE3 */ { 3216, 9, 13, 10, 1, -12 },
+/* 0xE4 */ { 3231, 9, 12, 10, 1, -11 },
+/* 0xE5 */ { 3245, 3, 15, 4, 0, -14 },
+/* 0xE6 */ { 3251, 8, 13, 9, 1, -12 },
+/* 0xE7 */ { 3264, 8, 14, 9, 1, -9 },
+/* 0xE8 */ { 3278, 8, 13, 9, 1, -12 },
+/* 0xE9 */ { 3291, 8, 13, 10, 1, -12 },
+/* 0xEA */ { 3304, 8, 14, 10, 1, -9 },
+/* 0xEB */ { 3318, 8, 12, 10, 1, -11 },
+/* 0xEC */ { 3330, 8, 13, 10, 1, -12 },
+/* 0xED */ { 3343, 3, 13, 4, 1, -12 },
+/* 0xEE */ { 3348, 4, 13, 5, 0, -12 },
+/* 0xEF */ { 3355, 12, 13, 12, 1, -12 },
+/* 0xF0 */ { 3375, 9, 13, 10, 1, -12 },
+/* 0xF1 */ { 3390, 8, 13, 10, 1, -12 },
+/* 0xF2 */ { 3403, 8, 13, 10, 1, -12 },
+/* 0xF3 */ { 3416, 8, 13, 10, 1, -12 },
+/* 0xF4 */ { 3429, 8, 13, 10, 1, -12 },
+/* 0xF5 */ { 3442, 8, 13, 10, 1, -12 },
+/* 0xF6 */ { 3455, 8, 12, 10, 1, -11 },
+/* 0xF7 */ { 3467, 9, 8, 11, 1, -7 },
+/* 0xF8 */ { 3476, 5, 13, 6, 1, -12 },
+/* 0xF9 */ { 3485, 8, 13, 10, 1, -12 },
+/* 0xFA */ { 3498, 8, 13, 10, 1, -12 },
+/* 0xFB */ { 3511, 8, 13, 10, 1, -12 },
+/* 0xFC */ { 3524, 8, 12, 10, 1, -11 },
+/* 0xFD */ { 3536, 8, 17, 9, 0, -12 },
+/* 0xFE */ { 3553, 5, 16, 5, 1, -11 },
+/* 0xFF */ { 3563, 2, 1, 6, 2, -11 },
};
-const GFXfont FreeSans9pt_Win1250 PROGMEM = {(uint8_t *)FreeSans9pt_Win1250Bitmaps, (GFXglyph *)FreeSans9pt_Win1250Glyphs, 0x20,
- 0xFF, 21};
+const GFXfont FreeSans9pt_Win1250 PROGMEM = {
+(uint8_t*)FreeSans9pt_Win1250Bitmaps,
+(GFXglyph*)FreeSans9pt_Win1250Glyphs,
+0x01, 0xFF, 21
+};
diff --git a/src/graphics/niche/Fonts/FreeSans9pt_Win1251.h b/src/graphics/niche/Fonts/FreeSans9pt_Win1251.h
index 82857cb91..b1511d996 100644
--- a/src/graphics/niche/Fonts/FreeSans9pt_Win1251.h
+++ b/src/graphics/niche/Fonts/FreeSans9pt_Win1251.h
@@ -1,493 +1,527 @@
+// trunk-ignore-all(clang-format)
#pragma once
+/* PROPERTIES
+
+FONT_NAME FreeSans9pt_Win1251
+*/
const uint8_t FreeSans9pt_Win1251Bitmaps[] PROGMEM = {
- /* ' ' 0x20 */
- 0xFF, 0xFF, 0xF0, 0xC0, /* '!' 0x21 */
- 0xDE, 0xF7, 0x20, /* '"' 0x22 */
- 0x09, 0x86, 0x41, 0x91, 0xFF, 0x13, 0x04, 0xC3, 0x20, 0xC8, 0xFF, 0x89, 0x82, 0x61, 0x90, /* '#' 0x23 */
- 0x10, 0x1F, 0x14, 0xDA, 0x3D, 0x1E, 0x83, 0x40, 0x78, 0x17, 0x08, 0xF4, 0x7A, 0x35, 0x33, 0xF0, 0x40, 0x20, /* '$' 0x24 */
- 0x38, 0x10, 0xEC, 0x20, 0xC6, 0x20, 0xC6, 0x40, 0xC6, 0x40, 0x6C, 0x80, 0x39, 0x00, 0x01, 0x3C, 0x02, 0x77, 0x02, 0x63, 0x04,
- 0x63, 0x04, 0x77, 0x08, 0x3C, /* '%' 0x25 */
- 0x0E, 0x0C, 0xC3, 0x30, 0xCC, 0x1E, 0x03, 0x03, 0xC1, 0x9B, 0xC2, 0xF0, 0xEC, 0x19, 0x8F, 0x3C, 0x40, /* '&' 0x26 */
- 0xFE, /* ''' 0x27 */
- 0x13, 0x26, 0x6C, 0xCC, 0xCC, 0xC4, 0x66, 0x23, 0x10, /* '(' 0x28 */
- 0x8C, 0x46, 0x63, 0x33, 0x33, 0x32, 0x66, 0x4C, 0x80, /* ')' 0x29 */
- 0x25, 0x7E, 0xA5, 0x00, /* '*' 0x2A */
- 0x30, 0xC3, 0x3F, 0x30, 0xC3, 0x0C, /* '+' 0x2B */
- 0xD6, /* ',' 0x2C */
- 0xF0, /* '-' 0x2D */
- 0xC0, /* '.' 0x2E */
- 0x08, 0x44, 0x21, 0x10, 0x84, 0x42, 0x11, 0x08, 0x00, /* '/' 0x2F */
- 0x3C, 0x66, 0x42, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x42, 0x66, 0x3C, /* '0' 0x30 */
- 0x11, 0x3F, 0x33, 0x33, 0x33, 0x33, 0x30, /* '1' 0x31 */
- 0x3E, 0x31, 0xB0, 0x78, 0x30, 0x18, 0x1C, 0x1C, 0x1C, 0x18, 0x18, 0x10, 0x08, 0x07, 0xF8, /* '2' 0x32 */
- 0x3C, 0x66, 0xC3, 0xC3, 0x03, 0x06, 0x1C, 0x07, 0x03, 0xC3, 0xC3, 0x66, 0x3C, /* '3' 0x33 */
- 0x0C, 0x18, 0x71, 0x62, 0xC9, 0xA3, 0x46, 0xFE, 0x18, 0x30, 0x60, 0xC0, /* '4' 0x34 */
- 0x7F, 0x20, 0x10, 0x08, 0x08, 0x07, 0xF3, 0x8C, 0x03, 0x01, 0x80, 0xF0, 0x6C, 0x63, 0xE0, /* '5' 0x35 */
- 0x1E, 0x31, 0x98, 0x78, 0x0C, 0x06, 0xF3, 0x8D, 0x83, 0xC1, 0xE0, 0xD0, 0x6C, 0x63, 0xE0, /* '6' 0x36 */
- 0xFF, 0x03, 0x02, 0x06, 0x04, 0x0C, 0x08, 0x18, 0x18, 0x18, 0x10, 0x30, 0x30, /* '7' 0x37 */
- 0x3E, 0x31, 0xB0, 0x78, 0x3C, 0x1B, 0x18, 0xF8, 0xC6, 0xC1, 0xE0, 0xF0, 0x6C, 0x63, 0xE0, /* '8' 0x38 */
- 0x3C, 0x66, 0xC2, 0xC3, 0xC3, 0xC3, 0x67, 0x3B, 0x03, 0x03, 0xC2, 0x66, 0x3C, /* '9' 0x39 */
- 0xC0, 0x00, 0x30, /* ':' 0x3A */
- 0xC0, 0x00, 0x00, 0x64, 0xA0, /* ';' 0x3B */
- 0x00, 0x81, 0xC7, 0x8E, 0x0C, 0x07, 0x80, 0x70, 0x0E, 0x01, 0x80, /* '<' 0x3C */
- 0xFF, 0x80, 0x00, 0x1F, 0xF0, /* '=' 0x3D */
- 0xE0, 0x1C, 0x03, 0x80, 0x30, 0x70, 0xE3, 0x81, 0x00, /* '>' 0x3E */
- 0x3E, 0x31, 0xB0, 0x78, 0x30, 0x18, 0x18, 0x38, 0x18, 0x18, 0x0C, 0x00, 0x00, 0x01, 0x80, /* '?' 0x3F */
- 0x03, 0xF0, 0x06, 0x0E, 0x06, 0x01, 0x86, 0x00, 0x66, 0x1D, 0xBB, 0x31, 0xCF, 0x18, 0xC7, 0x98, 0x63, 0xCC, 0x31, 0xE6, 0x11,
- 0xB3, 0x99, 0xCC, 0xF7, 0x86, 0x00, 0x01, 0x80, 0x00, 0x70, 0x40, 0x0F, 0xE0, /* '@' 0x40 */
- 0x06, 0x00, 0xF0, 0x0F, 0x00, 0x90, 0x19, 0x81, 0x98, 0x10, 0x83, 0x0C, 0x3F, 0xC2, 0x04, 0x60, 0x66, 0x06, 0xC0,
- 0x30, /* 'A' 0x41 */
- 0xFF, 0x18, 0x33, 0x03, 0x60, 0x6C, 0x0D, 0x83, 0x3F, 0xC6, 0x06, 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x6F, 0xF8, /* 'B' 0x42 */
- 0x1F, 0x86, 0x19, 0x81, 0xA0, 0x3C, 0x01, 0x80, 0x30, 0x06, 0x00, 0xC0, 0x68, 0x0D, 0x83, 0x18, 0x61, 0xF0, /* 'C' 0x43 */
- 0xFF, 0x18, 0x33, 0x03, 0x60, 0x3C, 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0x78, 0x0F, 0x03, 0x60, 0xCF, 0xF0, /* 'D' 0x44 */
- 0xFF, 0xE0, 0x30, 0x18, 0x0C, 0x06, 0x03, 0xFD, 0x80, 0xC0, 0x60, 0x30, 0x18, 0x0F, 0xF8, /* 'E' 0x45 */
- 0xFF, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xFE, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, /* 'F' 0x46 */
- 0x0F, 0x83, 0x0E, 0x60, 0x66, 0x03, 0xC0, 0x0C, 0x00, 0xC1, 0xFC, 0x03, 0xC0, 0x36, 0x03, 0x60, 0x73, 0x0F, 0x0F,
- 0x10, /* 'G' 0x47 */
- 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xFF, 0xFE, 0x03, 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x3C, 0x06, /* 'H' 0x48 */
- 0xFF, 0xFF, 0xFF, 0xC0, /* 'I' 0x49 */
- 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC1, 0x83, 0x07, 0x8F, 0x1E, 0x27, 0x80, /* 'J' 0x4A */
- 0xC0, 0xF0, 0x6C, 0x33, 0x18, 0xCC, 0x37, 0x0F, 0xC3, 0x98, 0xC3, 0x30, 0xCC, 0x1B, 0x03, 0xC0, 0xC0, /* 'K' 0x4B */
- 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xFF, /* 'L' 0x4C */
- 0xE0, 0x3F, 0x01, 0xFC, 0x1F, 0xE0, 0xFD, 0x05, 0xEC, 0x6F, 0x63, 0x79, 0x13, 0xCD, 0x9E, 0x6C, 0xF1, 0x47, 0x8E, 0x3C, 0x71,
- 0x80, /* 'M' 0x4D */
- 0xE0, 0x7C, 0x0F, 0xC1, 0xE8, 0x3D, 0x87, 0x98, 0xF1, 0x1E, 0x33, 0xC3, 0x78, 0x6F, 0x07, 0xE0, 0x7C, 0x0E, /* 'N' 0x4E */
- 0x0F, 0x81, 0x83, 0x18, 0x0C, 0xC0, 0x6C, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x03, 0xC0, 0x1B, 0x01, 0x98, 0x0C, 0x60, 0xC0, 0xF8,
- 0x00, /* 'O' 0x4F */
- 0xFF, 0x30, 0x6C, 0x0F, 0x03, 0xC0, 0xF0, 0x6F, 0xF3, 0x00, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x00, /* 'P' 0x50 */
- 0x0F, 0x81, 0x83, 0x18, 0x0C, 0xC0, 0x6C, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x03, 0xC0, 0x1B, 0x01, 0x98, 0x6C, 0x60, 0xC0, 0xFB,
- 0x00, 0x08, /* 'Q' 0x51 */
- 0xFF, 0x8C, 0x0E, 0xC0, 0x6C, 0x06, 0xC0, 0x6C, 0x0C, 0xFF, 0x8C, 0x0E, 0xC0, 0x6C, 0x06, 0xC0, 0x6C, 0x06, 0xC0,
- 0x70, /* 'R' 0x52 */
- 0x3F, 0x18, 0x6C, 0x0F, 0x03, 0xC0, 0x1E, 0x01, 0xF0, 0x0E, 0x00, 0xF0, 0x3C, 0x0D, 0x86, 0x3F, 0x00, /* 'S' 0x53 */
- 0xFF, 0x86, 0x03, 0x01, 0x80, 0xC0, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x01, 0x80, 0xC0, /* 'T' 0x54 */
- 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0x78, 0x0F, 0x01, 0xB0, 0x61, 0xF0, /* 'U' 0x55 */
- 0xC0, 0x6C, 0x0D, 0x81, 0x10, 0x63, 0x0C, 0x61, 0x04, 0x60, 0xCC, 0x19, 0x01, 0x60, 0x3C, 0x07, 0x00, 0x60, /* 'V' 0x56 */
- 0xC1, 0x81, 0x61, 0xC3, 0x61, 0xC3, 0x61, 0x43, 0x62, 0x62, 0x22, 0x66, 0x32, 0x26, 0x36, 0x26, 0x14, 0x34, 0x14, 0x34, 0x1C,
- 0x1C, 0x18, 0x1C, 0x08, 0x18, /* 'W' 0x57 */
- 0xC0, 0xD8, 0x66, 0x18, 0xCC, 0x1E, 0x07, 0x00, 0xC0, 0x78, 0x32, 0x0C, 0xC6, 0x1B, 0x07, 0xC0, 0xC0, /* 'X' 0x58 */
- 0xC0, 0x36, 0x06, 0x30, 0xC3, 0x0C, 0x19, 0x81, 0xD8, 0x0F, 0x00, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60, 0x06,
- 0x00, /* 'Y' 0x59 */
- 0xFF, 0xC0, 0x60, 0x30, 0x0C, 0x06, 0x03, 0x01, 0xC0, 0x60, 0x30, 0x18, 0x06, 0x03, 0x00, 0xFF, 0xC0, /* 'Z' 0x5A */
- 0xFB, 0x6D, 0xB6, 0xDB, 0x6D, 0xB6, 0xE0, /* '[' 0x5B */
- 0x84, 0x10, 0x84, 0x10, 0x84, 0x10, 0x84, 0x10, 0x80, /* '\' 0x5C */
- 0xED, 0xB6, 0xDB, 0x6D, 0xB6, 0xDB, 0xE0, /* ']' 0x5D */
- 0x30, 0x60, 0xA2, 0x44, 0xD8, 0xA1, 0x80, /* '^' 0x5E */
- 0xFF, 0xC0, /* '_' 0x5F */
- 0xC6, 0x30, /* '`' 0x60 */
- 0x7E, 0x71, 0xB0, 0xC0, 0x60, 0xF3, 0xDB, 0x0D, 0x86, 0xC7, 0x3D, 0xC0, /* 'a' 0x61 */
- 0xC0, 0x60, 0x30, 0x1B, 0xCE, 0x36, 0x0F, 0x07, 0x83, 0xC1, 0xE0, 0xF0, 0x7C, 0x6D, 0xE0, /* 'b' 0x62 */
- 0x3C, 0x66, 0xC3, 0xC0, 0xC0, 0xC0, 0xC0, 0xC3, 0x66, 0x3C, /* 'c' 0x63 */
- 0x03, 0x03, 0x03, 0x3B, 0x67, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x67, 0x3B, /* 'd' 0x64 */
- 0x3C, 0x66, 0xC3, 0xC3, 0xFF, 0xC0, 0xC0, 0xC3, 0x66, 0x3C, /* 'e' 0x65 */
- 0x36, 0x6F, 0x66, 0x66, 0x66, 0x66, 0x60, /* 'f' 0x66 */
- 0x3B, 0x67, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x67, 0x3B, 0x03, 0x03, 0xC6, 0x7C, /* 'g' 0x67 */
- 0xC0, 0xC0, 0xC0, 0xDE, 0xE3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, /* 'h' 0x68 */
- 0xC3, 0xFF, 0xFF, 0xC0, /* 'i' 0x69 */
- 0x30, 0x03, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0xE0, /* 'j' 0x6A */
- 0xC0, 0xC0, 0xC0, 0xC2, 0xC4, 0xCC, 0xD8, 0xF8, 0xEC, 0xC4, 0xC6, 0xC3, 0xC3, /* 'k' 0x6B */
- 0xFF, 0xFF, 0xFF, 0xC0, /* 'l' 0x6C */
- 0xDE, 0xF7, 0x1C, 0xF0, 0xC7, 0x86, 0x3C, 0x31, 0xE1, 0x8F, 0x0C, 0x78, 0x63, 0xC3, 0x1E, 0x18, 0xC0, /* 'm' 0x6D */
- 0xDE, 0xE3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, /* 'n' 0x6E */
- 0x3C, 0x66, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x66, 0x3C, /* 'o' 0x6F */
- 0xDE, 0x71, 0xB0, 0x78, 0x3C, 0x1E, 0x0F, 0x07, 0x83, 0xE3, 0x6F, 0x30, 0x18, 0x0C, 0x00, /* 'p' 0x70 */
- 0x3B, 0x67, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x67, 0x3B, 0x03, 0x03, 0x03, /* 'q' 0x71 */
- 0xDF, 0x31, 0x8C, 0x63, 0x18, 0xC6, 0x00, /* 'r' 0x72 */
- 0x3E, 0xE3, 0xC0, 0xC0, 0xE0, 0x3C, 0x07, 0xC3, 0xE3, 0x7E, /* 's' 0x73 */
- 0x66, 0xF6, 0x66, 0x66, 0x66, 0x67, /* 't' 0x74 */
- 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC7, 0x7B, /* 'u' 0x75 */
- 0xC1, 0xA0, 0x98, 0xCC, 0x42, 0x21, 0xB0, 0xD0, 0x28, 0x1C, 0x0C, 0x00, /* 'v' 0x76 */
- 0xC6, 0x1E, 0x38, 0x91, 0xC4, 0xCA, 0x66, 0xD3, 0x16, 0xD0, 0xA6, 0x87, 0x1C, 0x38, 0xC0, 0xC6, 0x00, /* 'w' 0x77 */
- 0x87, 0x89, 0xB1, 0xC3, 0x07, 0x1E, 0x26, 0xC5, 0x0C, /* 'x' 0x78 */
- 0xC1, 0x43, 0x63, 0x62, 0x26, 0x36, 0x34, 0x1C, 0x1C, 0x18, 0x18, 0x18, 0x10, 0x60, /* 'y' 0x79 */
- 0xFE, 0x0C, 0x30, 0xC1, 0x86, 0x18, 0x20, 0xC1, 0xFC, /* 'z' 0x7A */
- 0x36, 0x66, 0x66, 0x6E, 0xCE, 0x66, 0x66, 0x66, 0x30, /* '{' 0x7B */
- 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, /* '|' 0x7C */
- 0xC6, 0x66, 0x66, 0x67, 0x37, 0x66, 0x66, 0x66, 0xC0, /* '}' 0x7D */
- 0x61, 0x24, 0x38, /* '~' 0x7E */
- 0xFF, 0xFC, 0x00, 0x63, 0xE3, 0x31, 0x99, 0x04, 0xC8, 0x66, 0x06, 0x30, 0x61, 0x83, 0x0C, 0x18, 0x60, 0x03, 0x06, 0x18, 0x00,
- 0xFF, 0xFC, /* 0x7F */
- 0xFF, 0x01, 0x80, 0x18, 0x01, 0x80, 0x18, 0x01, 0xFE, 0x18, 0x31, 0x83, 0x18, 0x31, 0x83, 0x18, 0x31, 0x83, 0x18, 0x30, 0x03,
- 0x00, 0x30, 0x0E, /* 0x80 */
- 0x0C, 0x18, 0x00, 0xFF, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, /* 0x81 */
- 0xDC, /* 0x82 */
- 0x18, 0x89, 0xFC, 0x63, 0x18, 0xC6, 0x31, 0x8C, 0x00, /* 0x83 */
- 0xDA, 0x76, /* 0x84 */
- 0xCC, 0xC0, /* 0x85 */
- 0x18, 0x18, 0x18, 0x18, 0xFF, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, /* 0x86 */
- 0x18, 0x18, 0x18, 0x18, 0xFF, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xFF, 0x18, 0x18, 0x18, 0x18, /* 0x87 */
- 0x07, 0xC6, 0x13, 0x00, 0xC0, 0x60, 0x3F, 0xE6, 0x03, 0xFC, 0x60, 0x0C, 0x03, 0x00, 0x61, 0x07, 0xC0, /* 0x88 */
- 0x70, 0x80, 0x22, 0x20, 0x08, 0x90, 0x02, 0x24, 0x00, 0x72, 0x00, 0x00, 0x80, 0x00, 0x40, 0x00, 0x10, 0x00, 0x09, 0xC7, 0x84,
- 0x8B, 0x31, 0x22, 0x84, 0x88, 0xB3, 0x21, 0xC7, 0x80, /* 0x89 */
- 0x3F, 0x80, 0x18, 0xC0, 0x0C, 0x60, 0x06, 0x30, 0x03, 0x18, 0x01, 0x8C, 0x00, 0xC7, 0xF8, 0x63, 0x06, 0x31, 0x81, 0x90, 0xC0,
- 0xD8, 0x60, 0x6C, 0x30, 0x6C, 0x1F, 0xE0, /* 0x8A */
- 0x69, /* 0x8B */
- 0xC0, 0xC0, 0x60, 0x60, 0x30, 0x30, 0x18, 0x18, 0x0C, 0x0C, 0x06, 0x06, 0x03, 0xFF, 0xF9, 0x81, 0x86, 0xC0, 0xC1, 0xE0, 0x60,
- 0xF0, 0x30, 0x78, 0x18, 0x6C, 0x0F, 0xE0, /* 0x8C */
- 0x0C, 0x06, 0x0C, 0x1B, 0x0C, 0xC6, 0x33, 0x0D, 0x83, 0xC0, 0xF0, 0x3E, 0x0D, 0xC3, 0x38, 0xC7, 0x30, 0xEC, 0x1C, /* 0x8D */
- 0xFF, 0x01, 0x80, 0x18, 0x01, 0x80, 0x18, 0x01, 0xFE, 0x18, 0x31, 0x83, 0x18, 0x31, 0x83, 0x18, 0x31, 0x83, 0x18,
- 0x30, /* 0x8E */
- 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x3F, 0xFE, 0x0C, 0x01,
- 0x80, /* 0x8F */
- 0x60, 0x7C, 0x18, 0x0D, 0xE7, 0x1B, 0x0D, 0x86, 0xC3, 0x61, 0xB0, 0xD8, 0x6C, 0x36, 0x18, 0x18, 0x08, 0x08, /* 0x90 */
- 0x6B, /* 0x91 */
- 0xD6, /* 0x92 */
- 0x4C, 0xA5, 0xB0, /* 0x93 */
- 0xDA, 0x53, 0x20, /* 0x94 */
- 0x6F, 0xFF, 0x60, /* 0x95 */
- 0xFE, /* 0x96 */
- 0xFF, 0xFF, /* 0x97 */
- /* 0x98 */
- 0xFC, 0xE1, 0xCC, 0x38, 0x73, 0x0E, 0x1C, 0xC3, 0x8F, 0x30, 0xD2, 0xCC, 0x34, 0xB3, 0x0D, 0x6C, 0xC3, 0x53, 0x30, 0xCC, 0xCC,
- 0x33, 0x30, /* 0x99 */
- 0x7E, 0x03, 0x30, 0x19, 0x80, 0xCC, 0x06, 0x60, 0x33, 0xF9, 0x98, 0x6C, 0xC3, 0x46, 0x1E, 0x3F, 0x80, /* 0x9A */
- 0x96, /* 0x9B */
- 0xC3, 0x03, 0x0C, 0x0C, 0x30, 0x30, 0xC0, 0xC3, 0x03, 0xFF, 0xEC, 0x30, 0xF0, 0xC3, 0xC3, 0x0F, 0x0F, 0xE0, /* 0x9C */
- 0x0C, 0x30, 0x46, 0x3C, 0xDB, 0x34, 0x70, 0xF1, 0xB3, 0x36, 0x3C, 0x20, /* 0x9D */
- 0x60, 0x7C, 0x18, 0x0D, 0xE7, 0x3B, 0x0D, 0x86, 0xC3, 0x61, 0xB0, 0xD8, 0x6C, 0x36, 0x18, /* 0x9E */
- 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xFF, 0x18, 0x18, /* 0x9F */
- /* 0xA0 */
- 0x21, 0x07, 0x8C, 0x0F, 0x06, 0x61, 0x98, 0xC3, 0x30, 0xD8, 0x1E, 0x07, 0x00, 0xC0, 0x60, 0x18, 0x0C, 0x03, 0x00, /* 0xA1 */
- 0x66, 0x18, 0xC1, 0x43, 0x63, 0x62, 0x26, 0x36, 0x34, 0x1C, 0x1C, 0x18, 0x18, 0x18, 0x10, 0x60, /* 0xA2 */
- 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC1, 0x83, 0x07, 0x8F, 0x1E, 0x27, 0x80, /* 0xA3 */
- 0xFF, 0xDF, 0x1E, 0x3E, 0xFF, 0xC0, /* 0xA4 */
- 0x00, 0xC0, 0x3F, 0xFF, 0x00, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x30, 0x00, /* 0xA5 */
- 0xFF, 0xFC, 0x0F, 0xFF, 0xC0, /* 0xA6 */
- 0x0C, 0x09, 0x0C, 0xC6, 0x63, 0x81, 0xE3, 0x19, 0x87, 0xE1, 0xB8, 0xC6, 0x41, 0xC0, 0x73, 0x19, 0x8C, 0x66, 0x1E,
- 0x00, /* 0xA7 */
- 0x33, 0x00, 0x3F, 0xF8, 0x0C, 0x06, 0x03, 0x01, 0x80, 0xFF, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0xFE, /* 0xA8 */
- 0x0F, 0xC0, 0x61, 0x87, 0x03, 0x9B, 0xC6, 0xD9, 0x8F, 0x60, 0x3D, 0x00, 0xF4, 0x03, 0xD8, 0x0D, 0xE6, 0x67, 0xF3, 0x86, 0x18,
- 0x0F, 0xC0, /* 0xA9 */
- 0x1F, 0x86, 0x19, 0x81, 0xB0, 0x3C, 0x01, 0x80, 0x3F, 0xC6, 0x00, 0xC0, 0x68, 0x0D, 0x83, 0x18, 0x61, 0xF0, /* 0xAA */
- 0x22, 0xCF, 0x26, 0x46, 0x64, 0x40, /* 0xAB */
- 0xFF, 0x80, 0xC0, 0x60, 0x30, 0x18, /* 0xAC */
- /* 0xAD */
- 0x0F, 0xC0, 0x61, 0x87, 0x03, 0x9F, 0xE6, 0xD0, 0x8F, 0x42, 0x3D, 0xF0, 0xF4, 0x23, 0xD0, 0x8D, 0xC2, 0x67, 0x0B, 0x86, 0x18,
- 0x0F, 0xC0, /* 0xAE */
- 0xCC, 0x03, 0x0C, 0x30, 0xC3, 0x0C, 0x30, 0xC3, 0x0C, 0x30, 0xC3, 0x00, /* 0xAF */
- 0x74, 0x63, 0x17, 0x00, /* 0xB0 */
- 0x0C, 0x06, 0x03, 0x07, 0xE0, 0xC0, 0x60, 0x30, 0x18, 0x00, 0x00, 0x3F, 0xE0, /* 0xB1 */
- 0xFF, 0xFF, 0xFF, 0xC0, /* 0xB2 */
- 0xC3, 0xFF, 0xFF, 0xC0, /* 0xB3 */
- 0x0C, 0x3F, 0xF0, 0xC3, 0x0C, 0x30, 0xC3, 0x0C, 0x30, /* 0xB4 */
- 0xC3, 0x61, 0xB0, 0xD8, 0x6C, 0x36, 0x1B, 0x0D, 0x86, 0xE7, 0x7D, 0xF0, 0x18, 0x0C, 0x00, /* 0xB5 */
- 0x3F, 0x7E, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0x72, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, /* 0xB6 */
- 0xE0, /* 0xB7 */
- 0x66, 0x00, 0x3C, 0x66, 0xC3, 0xC3, 0xFF, 0xC0, 0xC0, 0xC3, 0x66, 0x3C, /* 0xB8 */
- 0xC1, 0x81, 0x83, 0x03, 0x86, 0x05, 0x0C, 0xEB, 0x1A, 0x32, 0x34, 0x66, 0x68, 0xC4, 0xD1, 0x8D, 0xB3, 0x0B, 0x3A, 0x1E, 0x04,
- 0x1C, 0x08, 0x1B, 0xC0, /* 0xB9 */
- 0x3C, 0x46, 0xC3, 0x80, 0xF8, 0x80, 0x80, 0xC3, 0x46, 0x3C, /* 0xBA */
- 0x89, 0x98, 0x99, 0x3C, 0xD1, 0x00, /* 0xBB */
- 0x30, 0x03, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0xE0, /* 0xBC */
- 0x3F, 0x18, 0x6C, 0x0F, 0x03, 0xC0, 0x1E, 0x01, 0xF0, 0x0E, 0x00, 0xF0, 0x3C, 0x0D, 0x86, 0x3F, 0x00, /* 0xBD */
- 0x3E, 0xE3, 0xC0, 0xC0, 0x60, 0x3C, 0x07, 0xC3, 0xE3, 0x7E, /* 0xBE */
- 0xCC, 0x03, 0x0C, 0x30, 0xC3, 0x0C, 0x30, 0xC3, 0x0C, /* 0xBF */
- 0x06, 0x00, 0xF0, 0x0F, 0x00, 0x90, 0x19, 0x81, 0x98, 0x10, 0x83, 0x0C, 0x3F, 0xC2, 0x04, 0x60, 0x66, 0x06, 0xC0,
- 0x30, /* 0xC0 */
- 0xFF, 0x18, 0x03, 0x00, 0x60, 0x0C, 0x01, 0x80, 0x3F, 0xE6, 0x06, 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x6F, 0xF8, /* 0xC1 */
- 0xFF, 0x18, 0x33, 0x03, 0x60, 0x6C, 0x0D, 0x83, 0x3F, 0xC6, 0x06, 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x6F, 0xF8, /* 0xC2 */
- 0xFF, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, /* 0xC3 */
- 0x1F, 0xF0, 0x60, 0xC1, 0x83, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC1, 0x83, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC1, 0x03, 0x0C, 0x0C,
- 0xFF, 0xFF, 0x00, 0x3C, 0x00, 0xF0, 0x03, /* 0xC4 */
- 0xFF, 0xE0, 0x30, 0x18, 0x0C, 0x06, 0x03, 0xFD, 0x80, 0xC0, 0x60, 0x30, 0x18, 0x0F, 0xF8, /* 0xC5 */
- 0x61, 0x86, 0x31, 0x8C, 0x19, 0x98, 0x19, 0x98, 0x0D, 0xB0, 0x07, 0xE0, 0x03, 0xC0, 0x07, 0xE0, 0x0D, 0xB0, 0x19, 0x98, 0x31,
- 0x8C, 0x61, 0x86, 0xC1, 0x83, /* 0xC6 */
- 0x3F, 0x18, 0x6C, 0x0F, 0x03, 0x00, 0xC0, 0x60, 0xF0, 0x06, 0x00, 0xF0, 0x3C, 0x0D, 0x86, 0x3F, 0x00, /* 0xC7 */
- 0xC0, 0xF8, 0x1F, 0x07, 0xE0, 0xBC, 0x37, 0x8C, 0xF1, 0x1E, 0x63, 0xD8, 0x7A, 0x0F, 0xC1, 0xF0, 0x3E, 0x06, /* 0xC8 */
- 0x11, 0x03, 0xE0, 0x00, 0x60, 0x7C, 0x0F, 0x83, 0xF0, 0x5E, 0x1B, 0xC6, 0x78, 0x8F, 0x31, 0xEC, 0x3D, 0x07, 0xE0, 0xF8, 0x1F,
- 0x03, /* 0xC9 */
- 0xC1, 0xB0, 0xCC, 0x63, 0x30, 0xD8, 0x3C, 0x0F, 0x03, 0xE0, 0xDC, 0x33, 0x8C, 0x73, 0x0E, 0xC1, 0xC0, /* 0xCA */
- 0x3F, 0xCC, 0x33, 0x0C, 0xC3, 0x30, 0xCC, 0x33, 0x0C, 0xC3, 0x30, 0xC8, 0x36, 0x0D, 0x83, 0xC0, 0xC0, /* 0xCB */
- 0xE0, 0x3F, 0x01, 0xFC, 0x1F, 0xE0, 0xFD, 0x05, 0xEC, 0x6F, 0x63, 0x79, 0x13, 0xCD, 0x9E, 0x6C, 0xF1, 0x47, 0x8E, 0x3C, 0x71,
- 0x80, /* 0xCC */
- 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xFF, 0xFE, 0x03, 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x3C, 0x06, /* 0xCD */
- 0x0F, 0x81, 0x83, 0x18, 0x0C, 0xC0, 0x6C, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x03, 0xC0, 0x1B, 0x01, 0x98, 0x0C, 0x60, 0xC0, 0xF8,
- 0x00, /* 0xCE */
- 0xFF, 0xF8, 0x0F, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x3C, 0x06, /* 0xCF */
- 0xFF, 0x30, 0x6C, 0x0F, 0x03, 0xC0, 0xF0, 0x6F, 0xF3, 0x00, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x00, /* 0xD0 */
- 0x1F, 0x86, 0x19, 0x81, 0xA0, 0x3C, 0x01, 0x80, 0x30, 0x06, 0x00, 0xC0, 0x68, 0x0D, 0x83, 0x18, 0x61, 0xF0, /* 0xD1 */
- 0xFF, 0x86, 0x03, 0x01, 0x80, 0xC0, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x01, 0x80, 0xC0, /* 0xD2 */
- 0xC0, 0xF0, 0x66, 0x19, 0x8C, 0x33, 0x0D, 0x81, 0xE0, 0x70, 0x0C, 0x06, 0x01, 0x80, 0xC0, 0x30, 0x00, /* 0xD3 */
- 0x03, 0x00, 0x0C, 0x01, 0xFE, 0x1C, 0xCE, 0xE3, 0x1F, 0x0C, 0x3C, 0x30, 0xF0, 0xC3, 0xE3, 0x1D, 0xCC, 0xE3, 0xFF, 0x00, 0xC0,
- 0x03, 0x00, /* 0xD4 */
- 0xC0, 0xD8, 0x66, 0x18, 0xCC, 0x1E, 0x07, 0x00, 0xC0, 0x78, 0x32, 0x0C, 0xC6, 0x1B, 0x07, 0xC0, 0xC0, /* 0xD5 */
- 0xC0, 0x66, 0x03, 0x30, 0x19, 0x80, 0xCC, 0x06, 0x60, 0x33, 0x01, 0x98, 0x0C, 0xC0, 0x66, 0x03, 0x30, 0x19, 0x80, 0xCF, 0xFF,
- 0x80, 0x0C, 0x00, 0x60, /* 0xD6 */
- 0xC1, 0xE0, 0xF0, 0x78, 0x3C, 0x1E, 0x0F, 0x06, 0xFF, 0x01, 0x80, 0xC0, 0x60, 0x30, 0x18, /* 0xD7 */
- 0xC3, 0x1E, 0x18, 0xF0, 0xC7, 0x86, 0x3C, 0x31, 0xE1, 0x8F, 0x0C, 0x78, 0x63, 0xC3, 0x1E, 0x18, 0xF0, 0xC7, 0x86, 0x3F, 0xFF,
- 0x80, /* 0xD8 */
- 0xC3, 0x19, 0x86, 0x33, 0x0C, 0x66, 0x18, 0xCC, 0x31, 0x98, 0x63, 0x30, 0xC6, 0x61, 0x8C, 0xC3, 0x19, 0x86, 0x33, 0x0C, 0x66,
- 0x18, 0xCF, 0xFF, 0xE0, 0x00, 0xC0, 0x01, 0x80, /* 0xD9 */
- 0xF8, 0x00, 0xC0, 0x06, 0x00, 0x30, 0x01, 0x80, 0x0F, 0xF0, 0x60, 0xC3, 0x03, 0x18, 0x18, 0xC0, 0xC6, 0x06, 0x30, 0x61, 0xFE,
- 0x00, /* 0xDA */
- 0xC0, 0x0F, 0x00, 0x3C, 0x00, 0xF0, 0x03, 0xC0, 0x0F, 0xFE, 0x3C, 0x0C, 0xF0, 0x1B, 0xC0, 0x6F, 0x01, 0xBC, 0x06, 0xF0, 0x33,
- 0xFF, 0x8C, /* 0xDB */
- 0xC0, 0x18, 0x03, 0x00, 0x60, 0x0C, 0x01, 0xFF, 0x30, 0x36, 0x03, 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x6F, 0xF8, /* 0xDC */
- 0x3F, 0x0C, 0x33, 0x83, 0x60, 0x20, 0x06, 0x00, 0x47, 0xF8, 0x01, 0xC0, 0x78, 0x0D, 0x81, 0x30, 0xC1, 0xF0, /* 0xDD */
- 0xC0, 0xF8, 0x61, 0x83, 0x31, 0x80, 0xD8, 0xC0, 0x6C, 0xC0, 0x1E, 0x60, 0x0F, 0xF0, 0x07, 0x98, 0x03, 0xCC, 0x01, 0xE3, 0x01,
- 0xB1, 0x80, 0xD8, 0x60, 0xCC, 0x0F, 0x80, /* 0xDE */
- 0x3F, 0xD8, 0x3C, 0x0F, 0x03, 0xC0, 0xD8, 0x33, 0xFC, 0x33, 0x18, 0xCC, 0x36, 0x0D, 0x83, 0xC0, 0xC0, /* 0xDF */
- 0x7E, 0x71, 0xB0, 0xC0, 0x60, 0xF3, 0xDB, 0x0D, 0x86, 0xC7, 0x3D, 0xC0, /* 0xE0 */
- 0x03, 0x1F, 0x78, 0x40, 0xFC, 0xE6, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x66, 0x3C, /* 0xE1 */
- 0xFD, 0x8F, 0x0E, 0x3F, 0xDF, 0xB1, 0xE1, 0xC7, 0xF8, /* 0xE2 */
- 0xFE, 0x31, 0x8C, 0x63, 0x18, 0xC6, 0x00, /* 0xE3 */
- 0x1F, 0x83, 0x30, 0x66, 0x0C, 0xC1, 0x98, 0x33, 0x06, 0x61, 0x8C, 0x31, 0x9F, 0xFF, 0x01, 0xE0, 0x30, /* 0xE4 */
- 0x3C, 0x66, 0xC3, 0xC3, 0xFF, 0xC0, 0xC0, 0xC3, 0x66, 0x3C, /* 0xE5 */
- 0xC6, 0x36, 0x66, 0x36, 0xC1, 0xF8, 0x0F, 0x01, 0xF8, 0x36, 0xC6, 0x66, 0xC6, 0x38, 0x61, /* 0xE6 */
- 0x79, 0x8C, 0x18, 0x30, 0x43, 0x01, 0xE3, 0xC6, 0xF8, /* 0xE7 */
- 0xC7, 0xC7, 0xCF, 0xCB, 0xCB, 0xD3, 0xD3, 0xF3, 0xE3, 0xE3, /* 0xE8 */
- 0x66, 0x18, 0xC7, 0xC7, 0xCF, 0xCB, 0xCB, 0xD3, 0xD3, 0xF3, 0xE3, 0xE3, /* 0xE9 */
- 0xC7, 0x9B, 0x66, 0x8E, 0x1E, 0x36, 0x66, 0xC7, 0x84, /* 0xEA */
- 0x7E, 0xCD, 0x9B, 0x36, 0x6C, 0xD9, 0xA3, 0xC7, 0x0C, /* 0xEB */
- 0xE3, 0xF1, 0xF8, 0xFE, 0xFF, 0x7E, 0xAF, 0x77, 0x93, 0xC9, 0xE0, 0xC0, /* 0xEC */
- 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xFF, 0xC3, 0xC3, 0xC3, 0xC3, /* 0xED */
- 0x3C, 0x66, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x66, 0x3C, /* 0xEE */
- 0xFF, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, /* 0xEF */
- 0xDE, 0x71, 0xB0, 0x78, 0x3C, 0x1E, 0x0F, 0x07, 0x83, 0xE3, 0x6F, 0x30, 0x18, 0x0C, 0x00, /* 0xF0 */
- 0x3C, 0x66, 0xC3, 0xC0, 0xC0, 0xC0, 0xC0, 0xC3, 0x66, 0x3C, /* 0xF1 */
- 0xFC, 0xC3, 0x0C, 0x30, 0xC3, 0x0C, 0x30, 0xC0, /* 0xF2 */
- 0xC1, 0x43, 0x63, 0x62, 0x26, 0x36, 0x34, 0x1C, 0x1C, 0x18, 0x18, 0x18, 0x10, 0x60, /* 0xF3 */
- 0x03, 0x00, 0x0C, 0x03, 0xB7, 0x19, 0xE6, 0xC3, 0x0F, 0x0C, 0x3C, 0x30, 0xF0, 0xC3, 0xC3, 0x0F, 0x0C, 0x36, 0x79, 0x8E, 0xDC,
- 0x03, 0x00, 0x0C, 0x00, 0x30, 0x00, /* 0xF4 */
- 0x87, 0x89, 0xB1, 0xC3, 0x07, 0x1E, 0x26, 0xC5, 0x0C, /* 0xF5 */
- 0xC3, 0x30, 0xCC, 0x33, 0x0C, 0xC3, 0x30, 0xCC, 0x33, 0x0C, 0xC3, 0x3F, 0xF0, 0x0C, 0x03, /* 0xF6 */
- 0xC7, 0x8F, 0x1E, 0x3C, 0x6F, 0xC1, 0x83, 0x06, 0x0C, /* 0xF7 */
- 0xCC, 0xF3, 0x3C, 0xCF, 0x33, 0xCC, 0xF3, 0x3C, 0xCF, 0x33, 0xCC, 0xFF, 0xF0, /* 0xF8 */
- 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCF, 0xFF, 0x00, 0x30, 0x03, /* 0xF9 */
- 0xF0, 0x18, 0x0C, 0x06, 0x03, 0xF1, 0x8C, 0xC6, 0x63, 0x31, 0x9F, 0x80, /* 0xFA */
- 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xFE, 0xF0, 0xFC, 0x3F, 0x0F, 0xC3, 0xFF, 0xB0, /* 0xFB */
- 0xC0, 0xC0, 0xC0, 0xC0, 0xFE, 0xC3, 0xC3, 0xC3, 0xC3, 0xFE, /* 0xFC */
- 0x3C, 0x62, 0xC3, 0x01, 0x1F, 0x01, 0x01, 0xC3, 0x62, 0x3C, /* 0xFD */
- 0xC7, 0xCC, 0xC6, 0xD8, 0x3D, 0x83, 0xF8, 0x3D, 0x83, 0xD8, 0x3C, 0xC2, 0xCC, 0x6C, 0x7C, /* 0xFE */
- 0x7F, 0xC3, 0xC3, 0xC3, 0x7F, 0x13, 0x33, 0x63, 0xC3, 0x83, /* 0xFF */
+/* 0x01 */ 0x07, 0x00, 0x0A, 0x00, 0x24, 0x00, 0x48, 0x01, 0x10, 0x04, 0x40, 0x10, 0xFF, 0x20, 0x02, 0x81, 0xFD, 0x00, 0x06, 0x07, 0xF4, 0x08, 0x24, 0x0F, 0x88, 0x11, 0x0F, 0xDC, 0x00,
+/* 0x02 */ 0x3F, 0x70, 0x81, 0x11, 0x03, 0xE4, 0x08, 0x28, 0x1F, 0xD0, 0x00, 0x60, 0x7F, 0x20, 0x02, 0x43, 0xFC, 0x44, 0x00, 0x44, 0x00, 0x48, 0x00, 0x90, 0x00, 0xA0, 0x01, 0xC0, 0x00,
+/* 0x03 */ 0x07, 0xC0, 0x30, 0x60, 0x80, 0x22, 0x00, 0x28, 0x00, 0x31, 0x8C, 0x63, 0x18, 0xC0, 0x01, 0x80, 0x03, 0x00, 0x06, 0x20, 0x8C, 0x3E, 0x14, 0x00, 0x44, 0x01, 0x06, 0x0C, 0x03, 0xE0,
+/* 0x04 */ 0x07, 0xC0, 0x30, 0x60, 0x80, 0x22, 0x00, 0x28, 0x82, 0x30, 0x88, 0x62, 0x08, 0xC0, 0x01, 0x80, 0x03, 0x00, 0x06, 0x3F, 0x8C, 0x3E, 0x14, 0x00, 0x44, 0x01, 0x06, 0x0C, 0x03, 0xE0,
+/* 0x05 */ 0x0B, 0x10, 0x14, 0xA8, 0x12, 0x50, 0x29, 0x42, 0x24, 0xA5, 0x32, 0x95, 0x5A, 0x09, 0x48, 0x09, 0x24, 0x01, 0x10, 0x01, 0x48, 0x02, 0xA4, 0x02, 0x42, 0x04, 0x01, 0x98, 0x00, 0x60,
+/* 0x06 */ 0x00, 0x80, 0x22, 0x80, 0x65, 0x00, 0xBE, 0xE1, 0x82, 0x4E, 0x03, 0x24, 0x04, 0x28, 0x06, 0x30, 0x12, 0x20, 0x3C, 0xA0, 0xC3, 0xFE, 0x80, 0x4D, 0x00, 0xA6, 0x01, 0x80, 0x00,
+/* 0x07 */
+/* 0x08 */ 0x00, 0xF8, 0x00, 0x82, 0x00, 0x80, 0x83, 0xE0, 0x41, 0x10, 0x21, 0x04, 0x1B, 0x00, 0x03, 0x00, 0x01, 0x80, 0x00, 0xE0, 0x00, 0x4F, 0xE1, 0xC0, 0x0F, 0x02, 0x00, 0x03, 0x01, 0x00, 0x09, 0x88, 0x0C, 0x0C,
+/* 0x09 */ 0x00, 0xF8, 0x00, 0x82, 0x00, 0x80, 0x83, 0xE0, 0x41, 0x10, 0x21, 0x04, 0x1B, 0x00, 0x03, 0x00, 0x01, 0x80, 0x00, 0xE0, 0x00, 0x4F, 0xE1, 0xC0, 0x0F, 0x00,
+/* 0x0A */
+/* 0x0B */ 0x1C, 0x1C, 0x31, 0xB1, 0x90, 0x50, 0x50, 0x10, 0x18, 0x00, 0x0C, 0x00, 0x06, 0x00, 0x02, 0x80, 0x02, 0x40, 0x01, 0x10, 0x01, 0x04, 0x01, 0x01, 0x01, 0x00, 0x41, 0x00, 0x11, 0x00, 0x07, 0x00, 0x01, 0x00,
+/* 0x0C */ 0x06, 0x00, 0x0A, 0x00, 0x12, 0x00, 0x32, 0x01, 0x84, 0x04, 0x10, 0x08, 0x98, 0x1C, 0x18, 0x40, 0x48, 0x82, 0x11, 0xF0, 0x74, 0x02, 0x18, 0x70, 0x2F, 0x9F, 0x80,
+/* 0x0D */
+/* 0x0E */ 0x01, 0x00, 0x05, 0x00, 0x0A, 0x00, 0x3E, 0x00, 0x82, 0x02, 0x82, 0x06, 0x04, 0x10, 0x04, 0x20, 0x08, 0x40, 0x10, 0xFF, 0x22, 0x00, 0x29, 0xFF, 0x3F, 0x8F, 0xDF, 0x9F, 0x01, 0xC0,
+/* 0x0F */ 0x07, 0xC0, 0x30, 0x60, 0x80, 0x22, 0x00, 0x28, 0x82, 0x36, 0x03, 0x60, 0x00, 0xCC, 0x19, 0xA4, 0x4B, 0x00, 0x06, 0x8E, 0x2B, 0x22, 0x66, 0x7C, 0xCC, 0x71, 0x98, 0x03, 0x00,
+/* 0x10 */ 0x03, 0x80, 0x07, 0x00, 0x0E, 0x00, 0x1E, 0x00, 0x54, 0x00, 0xA8, 0x01, 0x50, 0x02, 0xA0, 0x05, 0x20, 0x32, 0x61, 0xC4, 0x74, 0x49, 0x10, 0x6C, 0x00, 0xD8, 0x01, 0x10, 0x00,
+/* 0x11 */ 0x07, 0xC0, 0x30, 0x60, 0x80, 0x22, 0x40, 0x29, 0x00, 0x31, 0x84, 0x63, 0x18, 0xC0, 0x00, 0x80, 0x15, 0x03, 0x7E, 0x02, 0xFA, 0x04, 0xE4, 0x18, 0x84, 0x00, 0x06, 0x0C, 0x03, 0xE0,
+/* 0x12 */ 0x02, 0x08, 0x01, 0x08, 0x40, 0x10, 0xC0, 0x08, 0xC0, 0x60, 0x80, 0x28, 0x04, 0x12, 0x4C, 0x10, 0x80, 0x08, 0x23, 0x0E, 0x08, 0xC4, 0x82, 0x04, 0x20, 0x83, 0x09, 0x82, 0x47, 0x01, 0x1C, 0x01, 0x30, 0x00, 0xE0, 0x00, 0x00,
+/* 0x13 */ 0x07, 0xC0, 0x30, 0x60, 0x80, 0x22, 0x00, 0x28, 0x00, 0x31, 0x08, 0x65, 0x28, 0xC0, 0x01, 0x80, 0x03, 0x00, 0x06, 0x3F, 0x8C, 0x3E, 0x14, 0x00, 0x44, 0x01, 0x06, 0x0C, 0x03, 0xE0,
+/* 0x14 */ 0x07, 0xC0, 0x30, 0x60, 0x80, 0x22, 0x22, 0x29, 0x83, 0x30, 0x00, 0x65, 0x14, 0xD3, 0x4D, 0xBA, 0xEB, 0x38, 0xE6, 0x00, 0x0A, 0x00, 0x24, 0x38, 0x44, 0x01, 0x07, 0x1C, 0x01, 0xC0,
+/* 0x15 */ 0x07, 0xC0, 0x30, 0x18, 0x80, 0x32, 0x00, 0xF8, 0x01, 0xF1, 0x09, 0xA5, 0x28, 0x40, 0x01, 0x80, 0x03, 0x00, 0x06, 0x3F, 0x8C, 0x3E, 0x14, 0x00, 0x44, 0x01, 0x06, 0x0C, 0x03, 0xE0,
+/* 0x16 */ 0x0C, 0x00, 0xC0, 0x1C, 0x03, 0x80, 0xF8, 0xBB, 0x36, 0xC7, 0x99, 0xF3, 0xFE, 0x3F, 0xC3, 0xF0, 0x7E, 0x0E, 0xC1, 0x8E, 0xE0, 0x20,
+/* 0x17 */ 0x07, 0xC0, 0x30, 0x60, 0x80, 0x22, 0x00, 0x28, 0x00, 0x10, 0x01, 0x20, 0x1D, 0x44, 0x42, 0x84, 0x85, 0x00, 0x86, 0x00, 0xC4, 0x00, 0x44, 0x7C, 0x44, 0x00, 0x06, 0x0C, 0x03, 0xE0,
+/* 0x18 */ 0x01, 0xE0, 0x00, 0x84, 0x00, 0x40, 0x80, 0x20, 0x10, 0x08, 0x24, 0x02, 0x41, 0x00, 0x86, 0x03, 0x12, 0x03, 0xB4, 0x03, 0x52, 0x81, 0x23, 0x80, 0x70, 0xA0, 0x14, 0x28, 0x05, 0x0A, 0x01, 0x42, 0x80, 0x50,
+/* 0x19 */ 0x07, 0xC0, 0x30, 0x60, 0x80, 0x22, 0x00, 0x28, 0x00, 0x33, 0x18, 0x60, 0x00, 0xDC, 0xE1, 0xB9, 0xC3, 0x7B, 0xC6, 0x63, 0x0A, 0x00, 0x24, 0xF0, 0x44, 0x01, 0x06, 0x0C, 0x03, 0xE0,
+/* 0x1A */ 0xFF, 0xFC, 0x00, 0x63, 0xE3, 0x31, 0x99, 0x04, 0xC8, 0x66, 0x06, 0x30, 0x61, 0x82, 0x0C, 0x10, 0x60, 0x03, 0x04, 0x18, 0x00, 0xFF, 0xFC,
+/* 0x1B */ 0x07, 0xF0, 0x06, 0x0C, 0x04, 0x01, 0x04, 0x00, 0x44, 0x22, 0x12, 0x2A, 0x89, 0x00, 0x04, 0x80, 0x02, 0x44, 0x11, 0x01, 0xF0, 0x04, 0x01, 0x0D, 0x01, 0x6A, 0x41, 0x2C, 0x00, 0x05, 0xC0, 0x0E, 0x18, 0x18,
+/* 0x1C */ 0x07, 0xC0, 0x30, 0x60, 0x80, 0x22, 0xC0, 0x2A, 0x00, 0x33, 0x00, 0x66, 0x00, 0xCC, 0x39, 0x80, 0x83, 0x00, 0x06, 0x00, 0x8C, 0x3E, 0x14, 0x00, 0x44, 0x01, 0x06, 0x0C, 0x03, 0xE0,
+/* 0x1D */ 0x07, 0xC0, 0x30, 0x60, 0x80, 0x22, 0x70, 0x28, 0x00, 0x31, 0x80, 0x63, 0x18, 0xC0, 0x31, 0x80, 0x03, 0x00, 0x06, 0x60, 0x0D, 0x33, 0x12, 0x10, 0x48, 0x21, 0x23, 0x8C, 0x00,
+/* 0x1E */ 0x03, 0x00, 0x07, 0x9E, 0x07, 0x00, 0x86, 0x00, 0x27, 0xC0, 0x0F, 0xC0, 0x07, 0x8C, 0x62, 0x06, 0x31, 0x20, 0x00, 0x90, 0x00, 0x48, 0x00, 0x24, 0x3E, 0x11, 0x00, 0x10, 0x40, 0x10, 0x18, 0x30, 0x03, 0xE0,
+/* 0x1F */ 0x18, 0x02, 0x80, 0x4C, 0x16, 0x41, 0x24, 0x3C, 0x88, 0x6E, 0x65, 0xF2, 0x78, 0x46, 0x88, 0xCF, 0x18, 0x02, 0x80, 0x8C, 0x60, 0x70,
+/* ' ' 0x20 */
+/* '!' 0x21 */ 0xFF, 0xFF, 0xF0, 0xC0,
+/* '"' 0x22 */ 0xDE, 0xF7, 0x20,
+/* '#' 0x23 */ 0x09, 0x86, 0x41, 0x91, 0xFF, 0x13, 0x04, 0xC3, 0x20, 0xC8, 0xFF, 0x89, 0x82, 0x61, 0x90,
+/* '$' 0x24 */ 0x10, 0x1F, 0x14, 0xDA, 0x3D, 0x1E, 0x83, 0x40, 0x78, 0x17, 0x08, 0xF4, 0x7A, 0x35, 0x33, 0xF0, 0x40, 0x20,
+/* '%' 0x25 */ 0x38, 0x10, 0xEC, 0x20, 0xC6, 0x20, 0xC6, 0x40, 0xC6, 0x40, 0x6C, 0x80, 0x39, 0x00, 0x01, 0x3C, 0x02, 0x77, 0x02, 0x63, 0x04, 0x63, 0x04, 0x77, 0x08, 0x3C,
+/* '&' 0x26 */ 0x0E, 0x0C, 0xC3, 0x30, 0xCC, 0x1E, 0x03, 0x03, 0xC1, 0x9B, 0xC2, 0xF0, 0xEC, 0x19, 0x8F, 0x3C, 0x40,
+/* ''' 0x27 */ 0xFE,
+/* '(' 0x28 */ 0x13, 0x26, 0x6C, 0xCC, 0xCC, 0xC4, 0x66, 0x23, 0x10,
+/* ')' 0x29 */ 0x8C, 0x46, 0x63, 0x33, 0x33, 0x32, 0x66, 0x4C, 0x80,
+/* '*' 0x2A */ 0x25, 0x7E, 0xA5, 0x00,
+/* '+' 0x2B */ 0x30, 0xC3, 0x3F, 0x30, 0xC3, 0x0C,
+/* ',' 0x2C */ 0xD6,
+/* '-' 0x2D */ 0xF0,
+/* '.' 0x2E */ 0xC0,
+/* '/' 0x2F */ 0x08, 0x44, 0x21, 0x10, 0x84, 0x42, 0x11, 0x08, 0x00,
+/* '0' 0x30 */ 0x3C, 0x66, 0x42, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x42, 0x66, 0x3C,
+/* '1' 0x31 */ 0x11, 0x3F, 0x33, 0x33, 0x33, 0x33, 0x30,
+/* '2' 0x32 */ 0x3E, 0x31, 0xB0, 0x78, 0x30, 0x18, 0x1C, 0x1C, 0x1C, 0x18, 0x18, 0x10, 0x08, 0x07, 0xF8,
+/* '3' 0x33 */ 0x3C, 0x66, 0xC3, 0xC3, 0x03, 0x06, 0x1C, 0x07, 0x03, 0xC3, 0xC3, 0x66, 0x3C,
+/* '4' 0x34 */ 0x0C, 0x18, 0x71, 0x62, 0xC9, 0xA3, 0x46, 0xFE, 0x18, 0x30, 0x60, 0xC0,
+/* '5' 0x35 */ 0x7F, 0x20, 0x10, 0x08, 0x08, 0x07, 0xF3, 0x8C, 0x03, 0x01, 0x80, 0xF0, 0x6C, 0x63, 0xE0,
+/* '6' 0x36 */ 0x1E, 0x31, 0x98, 0x78, 0x0C, 0x06, 0xF3, 0x8D, 0x83, 0xC1, 0xE0, 0xD0, 0x6C, 0x63, 0xE0,
+/* '7' 0x37 */ 0xFF, 0x03, 0x02, 0x06, 0x04, 0x0C, 0x08, 0x18, 0x18, 0x18, 0x10, 0x30, 0x30,
+/* '8' 0x38 */ 0x3E, 0x31, 0xB0, 0x78, 0x3C, 0x1B, 0x18, 0xF8, 0xC6, 0xC1, 0xE0, 0xF0, 0x6C, 0x63, 0xE0,
+/* '9' 0x39 */ 0x3C, 0x66, 0xC2, 0xC3, 0xC3, 0xC3, 0x67, 0x3B, 0x03, 0x03, 0xC2, 0x66, 0x3C,
+/* ':' 0x3A */ 0xC0, 0x00, 0x30,
+/* ';' 0x3B */ 0xC0, 0x00, 0x00, 0x64, 0xA0,
+/* '<' 0x3C */ 0x00, 0x81, 0xC7, 0x8E, 0x0C, 0x07, 0x80, 0x70, 0x0E, 0x01, 0x80,
+/* '=' 0x3D */ 0xFF, 0x80, 0x00, 0x1F, 0xF0,
+/* '>' 0x3E */ 0xE0, 0x1C, 0x03, 0x80, 0x30, 0x70, 0xE3, 0x81, 0x00,
+/* '?' 0x3F */ 0x3E, 0x31, 0xB0, 0x78, 0x30, 0x18, 0x18, 0x38, 0x18, 0x18, 0x0C, 0x00, 0x00, 0x01, 0x80,
+/* '@' 0x40 */ 0x03, 0xF0, 0x06, 0x0E, 0x06, 0x01, 0x86, 0x00, 0x66, 0x1D, 0xBB, 0x31, 0xCF, 0x18, 0xC7, 0x98, 0x63, 0xCC, 0x31, 0xE6, 0x11, 0xB3, 0x99, 0xCC, 0xF7, 0x86, 0x00, 0x01, 0x80, 0x00, 0x70, 0x40, 0x0F, 0xE0,
+/* 'A' 0x41 */ 0x06, 0x00, 0xF0, 0x0F, 0x00, 0x90, 0x19, 0x81, 0x98, 0x10, 0x83, 0x0C, 0x3F, 0xC2, 0x04, 0x60, 0x66, 0x06, 0xC0, 0x30,
+/* 'B' 0x42 */ 0xFF, 0x18, 0x33, 0x03, 0x60, 0x6C, 0x0D, 0x83, 0x3F, 0xC6, 0x06, 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x6F, 0xF8,
+/* 'C' 0x43 */ 0x1F, 0x86, 0x19, 0x81, 0xA0, 0x3C, 0x01, 0x80, 0x30, 0x06, 0x00, 0xC0, 0x68, 0x0D, 0x83, 0x18, 0x61, 0xF0,
+/* 'D' 0x44 */ 0xFF, 0x18, 0x33, 0x03, 0x60, 0x3C, 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0x78, 0x0F, 0x03, 0x60, 0xCF, 0xF0,
+/* 'E' 0x45 */ 0xFF, 0xE0, 0x30, 0x18, 0x0C, 0x06, 0x03, 0xFD, 0x80, 0xC0, 0x60, 0x30, 0x18, 0x0F, 0xF8,
+/* 'F' 0x46 */ 0xFF, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xFE, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0,
+/* 'G' 0x47 */ 0x0F, 0x83, 0x0E, 0x60, 0x66, 0x03, 0xC0, 0x0C, 0x00, 0xC1, 0xFC, 0x03, 0xC0, 0x36, 0x03, 0x60, 0x73, 0x0F, 0x0F, 0x10,
+/* 'H' 0x48 */ 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xFF, 0xFE, 0x03, 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x3C, 0x06,
+/* 'I' 0x49 */ 0xFF, 0xFF, 0xFF, 0xC0,
+/* 'J' 0x4A */ 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC1, 0x83, 0x07, 0x8F, 0x1E, 0x27, 0x80,
+/* 'K' 0x4B */ 0xC0, 0xF0, 0x6C, 0x33, 0x18, 0xCC, 0x37, 0x0F, 0xC3, 0x98, 0xC3, 0x30, 0xCC, 0x1B, 0x03, 0xC0, 0xC0,
+/* 'L' 0x4C */ 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xFF,
+/* 'M' 0x4D */ 0xE0, 0x3F, 0x01, 0xFC, 0x1F, 0xE0, 0xFD, 0x05, 0xEC, 0x6F, 0x63, 0x79, 0x13, 0xCD, 0x9E, 0x6C, 0xF1, 0x47, 0x8E, 0x3C, 0x71, 0x80,
+/* 'N' 0x4E */ 0xE0, 0x7C, 0x0F, 0xC1, 0xE8, 0x3D, 0x87, 0x98, 0xF1, 0x1E, 0x33, 0xC3, 0x78, 0x6F, 0x07, 0xE0, 0x7C, 0x0E,
+/* 'O' 0x4F */ 0x0F, 0x81, 0x83, 0x18, 0x0C, 0xC0, 0x6C, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x03, 0xC0, 0x1B, 0x01, 0x98, 0x0C, 0x60, 0xC0, 0xF8, 0x00,
+/* 'P' 0x50 */ 0xFF, 0x30, 0x6C, 0x0F, 0x03, 0xC0, 0xF0, 0x6F, 0xF3, 0x00, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x00,
+/* 'Q' 0x51 */ 0x0F, 0x81, 0x83, 0x18, 0x0C, 0xC0, 0x6C, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x03, 0xC0, 0x1B, 0x01, 0x98, 0x6C, 0x60, 0xC0, 0xFB, 0x00, 0x08,
+/* 'R' 0x52 */ 0xFF, 0x8C, 0x0E, 0xC0, 0x6C, 0x06, 0xC0, 0x6C, 0x0C, 0xFF, 0x8C, 0x0E, 0xC0, 0x6C, 0x06, 0xC0, 0x6C, 0x06, 0xC0, 0x70,
+/* 'S' 0x53 */ 0x3F, 0x18, 0x6C, 0x0F, 0x03, 0xC0, 0x1E, 0x01, 0xF0, 0x0E, 0x00, 0xF0, 0x3C, 0x0D, 0x86, 0x3F, 0x00,
+/* 'T' 0x54 */ 0xFF, 0x86, 0x03, 0x01, 0x80, 0xC0, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x01, 0x80, 0xC0,
+/* 'U' 0x55 */ 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0x78, 0x0F, 0x01, 0xB0, 0x61, 0xF0,
+/* 'V' 0x56 */ 0xC0, 0x6C, 0x0D, 0x81, 0x10, 0x63, 0x0C, 0x61, 0x04, 0x60, 0xCC, 0x19, 0x01, 0x60, 0x3C, 0x07, 0x00, 0x60,
+/* 'W' 0x57 */ 0xC1, 0x81, 0x61, 0xC3, 0x61, 0xC3, 0x61, 0x43, 0x62, 0x62, 0x22, 0x66, 0x32, 0x26, 0x36, 0x26, 0x14, 0x34, 0x14, 0x34, 0x1C, 0x1C, 0x18, 0x1C, 0x08, 0x18,
+/* 'X' 0x58 */ 0xC0, 0xD8, 0x66, 0x18, 0xCC, 0x1E, 0x07, 0x00, 0xC0, 0x78, 0x32, 0x0C, 0xC6, 0x1B, 0x07, 0xC0, 0xC0,
+/* 'Y' 0x59 */ 0xC0, 0x36, 0x06, 0x30, 0xC3, 0x0C, 0x19, 0x81, 0xD8, 0x0F, 0x00, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00,
+/* 'Z' 0x5A */ 0xFF, 0xC0, 0x60, 0x30, 0x0C, 0x06, 0x03, 0x01, 0xC0, 0x60, 0x30, 0x18, 0x06, 0x03, 0x00, 0xFF, 0xC0,
+/* '[' 0x5B */ 0xFB, 0x6D, 0xB6, 0xDB, 0x6D, 0xB6, 0xE0,
+/* '\' 0x5C */ 0x84, 0x10, 0x84, 0x10, 0x84, 0x10, 0x84, 0x10, 0x80,
+/* ']' 0x5D */ 0xED, 0xB6, 0xDB, 0x6D, 0xB6, 0xDB, 0xE0,
+/* '^' 0x5E */ 0x30, 0x60, 0xA2, 0x44, 0xD8, 0xA1, 0x80,
+/* '_' 0x5F */ 0xFF, 0xC0,
+/* '`' 0x60 */ 0xC6, 0x30,
+/* 'a' 0x61 */ 0x7E, 0x71, 0xB0, 0xC0, 0x60, 0xF3, 0xDB, 0x0D, 0x86, 0xC7, 0x3D, 0xC0,
+/* 'b' 0x62 */ 0xC0, 0x60, 0x30, 0x1B, 0xCE, 0x36, 0x0F, 0x07, 0x83, 0xC1, 0xE0, 0xF0, 0x7C, 0x6D, 0xE0,
+/* 'c' 0x63 */ 0x3C, 0x66, 0xC3, 0xC0, 0xC0, 0xC0, 0xC0, 0xC3, 0x66, 0x3C,
+/* 'd' 0x64 */ 0x03, 0x03, 0x03, 0x3B, 0x67, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x67, 0x3B,
+/* 'e' 0x65 */ 0x3C, 0x66, 0xC3, 0xC3, 0xFF, 0xC0, 0xC0, 0xC3, 0x66, 0x3C,
+/* 'f' 0x66 */ 0x36, 0x6F, 0x66, 0x66, 0x66, 0x66, 0x60,
+/* 'g' 0x67 */ 0x3B, 0x67, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x67, 0x3B, 0x03, 0x03, 0xC6, 0x7C,
+/* 'h' 0x68 */ 0xC0, 0xC0, 0xC0, 0xDE, 0xE3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3,
+/* 'i' 0x69 */ 0xC3, 0xFF, 0xFF, 0xC0,
+/* 'j' 0x6A */ 0x30, 0x03, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0xE0,
+/* 'k' 0x6B */ 0xC0, 0xC0, 0xC0, 0xC2, 0xC4, 0xCC, 0xD8, 0xF8, 0xEC, 0xC4, 0xC6, 0xC3, 0xC3,
+/* 'l' 0x6C */ 0xFF, 0xFF, 0xFF, 0xC0,
+/* 'm' 0x6D */ 0xDE, 0xF7, 0x1C, 0xF0, 0xC7, 0x86, 0x3C, 0x31, 0xE1, 0x8F, 0x0C, 0x78, 0x63, 0xC3, 0x1E, 0x18, 0xC0,
+/* 'n' 0x6E */ 0xDE, 0xE3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3,
+/* 'o' 0x6F */ 0x3C, 0x66, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x66, 0x3C,
+/* 'p' 0x70 */ 0xDE, 0x71, 0xB0, 0x78, 0x3C, 0x1E, 0x0F, 0x07, 0x83, 0xE3, 0x6F, 0x30, 0x18, 0x0C, 0x00,
+/* 'q' 0x71 */ 0x3B, 0x67, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x67, 0x3B, 0x03, 0x03, 0x03,
+/* 'r' 0x72 */ 0xDF, 0x31, 0x8C, 0x63, 0x18, 0xC6, 0x00,
+/* 's' 0x73 */ 0x3E, 0xE3, 0xC0, 0xC0, 0xE0, 0x3C, 0x07, 0xC3, 0xE3, 0x7E,
+/* 't' 0x74 */ 0x66, 0xF6, 0x66, 0x66, 0x66, 0x67,
+/* 'u' 0x75 */ 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC7, 0x7B,
+/* 'v' 0x76 */ 0xC1, 0xA0, 0x98, 0xCC, 0x42, 0x21, 0xB0, 0xD0, 0x28, 0x1C, 0x0C, 0x00,
+/* 'w' 0x77 */ 0xC6, 0x1E, 0x38, 0x91, 0xC4, 0xCA, 0x66, 0xD3, 0x16, 0xD0, 0xA6, 0x87, 0x1C, 0x38, 0xC0, 0xC6, 0x00,
+/* 'x' 0x78 */ 0x87, 0x89, 0xB1, 0xC3, 0x07, 0x1E, 0x26, 0xC5, 0x0C,
+/* 'y' 0x79 */ 0xC1, 0x43, 0x63, 0x62, 0x26, 0x36, 0x34, 0x1C, 0x1C, 0x18, 0x18, 0x18, 0x10, 0x60,
+/* 'z' 0x7A */ 0xFE, 0x0C, 0x30, 0xC1, 0x86, 0x18, 0x20, 0xC1, 0xFC,
+/* '{' 0x7B */ 0x36, 0x66, 0x66, 0x6E, 0xCE, 0x66, 0x66, 0x66, 0x30,
+/* '|' 0x7C */ 0xFF, 0xFF, 0xFF, 0xFF, 0xC0,
+/* '}' 0x7D */ 0xC6, 0x66, 0x66, 0x67, 0x37, 0x66, 0x66, 0x66, 0xC0,
+/* '~' 0x7E */ 0x61, 0x24, 0x38,
+/* 0x7F */ 0xFF, 0xFC, 0x00, 0x63, 0xE3, 0x31, 0x99, 0x04, 0xC8, 0x66, 0x06, 0x30, 0x61, 0x83, 0x0C, 0x18, 0x60, 0x03, 0x06, 0x18, 0x00, 0xFF, 0xFC,
+/* 0x80 */ 0xFF, 0x01, 0x80, 0x18, 0x01, 0x80, 0x18, 0x01, 0xFE, 0x18, 0x31, 0x83, 0x18, 0x31, 0x83, 0x18, 0x31, 0x83, 0x18, 0x30, 0x03, 0x00, 0x30, 0x0E,
+/* 0x81 */ 0x0C, 0x18, 0x00, 0xFF, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0,
+/* 0x82 */ 0xDC,
+/* 0x83 */ 0x18, 0x89, 0xFC, 0x63, 0x18, 0xC6, 0x31, 0x8C, 0x00,
+/* 0x84 */ 0xDA, 0x76,
+/* 0x85 */ 0xCC, 0xC0,
+/* 0x86 */ 0x18, 0x18, 0x18, 0x18, 0xFF, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+/* 0x87 */ 0x18, 0x18, 0x18, 0x18, 0xFF, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xFF, 0x18, 0x18, 0x18, 0x18,
+/* 0x88 */ 0x07, 0xC6, 0x13, 0x00, 0xC0, 0x60, 0x3F, 0xE6, 0x03, 0xFC, 0x60, 0x0C, 0x03, 0x00, 0x61, 0x07, 0xC0,
+/* 0x89 */ 0x70, 0x80, 0x22, 0x20, 0x08, 0x90, 0x02, 0x24, 0x00, 0x72, 0x00, 0x00, 0x80, 0x00, 0x40, 0x00, 0x10, 0x00, 0x09, 0xC7, 0x84, 0x8B, 0x31, 0x22, 0x84, 0x88, 0xB3, 0x21, 0xC7, 0x80,
+/* 0x8A */ 0x3F, 0x80, 0x18, 0xC0, 0x0C, 0x60, 0x06, 0x30, 0x03, 0x18, 0x01, 0x8C, 0x00, 0xC7, 0xF8, 0x63, 0x06, 0x31, 0x81, 0x90, 0xC0, 0xD8, 0x60, 0x6C, 0x30, 0x6C, 0x1F, 0xE0,
+/* 0x8B */ 0x69,
+/* 0x8C */ 0xC0, 0xC0, 0x60, 0x60, 0x30, 0x30, 0x18, 0x18, 0x0C, 0x0C, 0x06, 0x06, 0x03, 0xFF, 0xF9, 0x81, 0x86, 0xC0, 0xC1, 0xE0, 0x60, 0xF0, 0x30, 0x78, 0x18, 0x6C, 0x0F, 0xE0,
+/* 0x8D */ 0x0C, 0x06, 0x0C, 0x1B, 0x0C, 0xC6, 0x33, 0x0D, 0x83, 0xC0, 0xF0, 0x3E, 0x0D, 0xC3, 0x38, 0xC7, 0x30, 0xEC, 0x1C,
+/* 0x8E */ 0xFF, 0x01, 0x80, 0x18, 0x01, 0x80, 0x18, 0x01, 0xFE, 0x18, 0x31, 0x83, 0x18, 0x31, 0x83, 0x18, 0x31, 0x83, 0x18, 0x30,
+/* 0x8F */ 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x3F, 0xFE, 0x0C, 0x01, 0x80,
+/* 0x90 */ 0x60, 0x7C, 0x18, 0x0D, 0xE7, 0x1B, 0x0D, 0x86, 0xC3, 0x61, 0xB0, 0xD8, 0x6C, 0x36, 0x18, 0x18, 0x08, 0x08,
+/* 0x91 */ 0x6B,
+/* 0x92 */ 0xD6,
+/* 0x93 */ 0x4C, 0xA5, 0xB0,
+/* 0x94 */ 0xDA, 0x53, 0x20,
+/* 0x95 */ 0x6F, 0xFF, 0x60,
+/* 0x96 */ 0xFE,
+/* 0x97 */ 0xFF, 0xFF,
+/* 0x98 */
+/* 0x99 */ 0xFC, 0xE1, 0xCC, 0x38, 0x73, 0x0E, 0x1C, 0xC3, 0x8F, 0x30, 0xD2, 0xCC, 0x34, 0xB3, 0x0D, 0x6C, 0xC3, 0x53, 0x30, 0xCC, 0xCC, 0x33, 0x30,
+/* 0x9A */ 0x7E, 0x03, 0x30, 0x19, 0x80, 0xCC, 0x06, 0x60, 0x33, 0xF9, 0x98, 0x6C, 0xC3, 0x46, 0x1E, 0x3F, 0x80,
+/* 0x9B */ 0x96,
+/* 0x9C */ 0xC3, 0x03, 0x0C, 0x0C, 0x30, 0x30, 0xC0, 0xC3, 0x03, 0xFF, 0xEC, 0x30, 0xF0, 0xC3, 0xC3, 0x0F, 0x0F, 0xE0,
+/* 0x9D */ 0x0C, 0x30, 0x46, 0x3C, 0xDB, 0x34, 0x70, 0xF1, 0xB3, 0x36, 0x3C, 0x20,
+/* 0x9E */ 0x60, 0x7C, 0x18, 0x0D, 0xE7, 0x3B, 0x0D, 0x86, 0xC3, 0x61, 0xB0, 0xD8, 0x6C, 0x36, 0x18,
+/* 0x9F */ 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xFF, 0x18, 0x18,
+/* 0xA0 */
+/* 0xA1 */ 0x21, 0x07, 0x8C, 0x0F, 0x06, 0x61, 0x98, 0xC3, 0x30, 0xD8, 0x1E, 0x07, 0x00, 0xC0, 0x60, 0x18, 0x0C, 0x03, 0x00,
+/* 0xA2 */ 0x66, 0x18, 0xC1, 0x43, 0x63, 0x62, 0x26, 0x36, 0x34, 0x1C, 0x1C, 0x18, 0x18, 0x18, 0x10, 0x60,
+/* 0xA3 */ 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC1, 0x83, 0x07, 0x8F, 0x1E, 0x27, 0x80,
+/* 0xA4 */ 0xFF, 0xDF, 0x1E, 0x3E, 0xFF, 0xC0,
+/* 0xA5 */ 0x00, 0xC0, 0x3F, 0xFF, 0x00, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x30, 0x00,
+/* 0xA6 */ 0xFF, 0xFC, 0x0F, 0xFF, 0xC0,
+/* 0xA7 */ 0x0C, 0x09, 0x0C, 0xC6, 0x63, 0x81, 0xE3, 0x19, 0x87, 0xE1, 0xB8, 0xC6, 0x41, 0xC0, 0x73, 0x19, 0x8C, 0x66, 0x1E, 0x00,
+/* 0xA8 */ 0x33, 0x00, 0x3F, 0xF8, 0x0C, 0x06, 0x03, 0x01, 0x80, 0xFF, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0xFE,
+/* 0xA9 */ 0x0F, 0xC0, 0x61, 0x87, 0x03, 0x9B, 0xC6, 0xD9, 0x8F, 0x60, 0x3D, 0x00, 0xF4, 0x03, 0xD8, 0x0D, 0xE6, 0x67, 0xF3, 0x86, 0x18, 0x0F, 0xC0,
+/* 0xAA */ 0x1F, 0x86, 0x19, 0x81, 0xB0, 0x3C, 0x01, 0x80, 0x3F, 0xC6, 0x00, 0xC0, 0x68, 0x0D, 0x83, 0x18, 0x61, 0xF0,
+/* 0xAB */ 0x22, 0xCF, 0x26, 0x46, 0x64, 0x40,
+/* 0xAC */ 0xFF, 0x80, 0xC0, 0x60, 0x30, 0x18,
+/* 0xAD */
+/* 0xAE */ 0x0F, 0xC0, 0x61, 0x87, 0x03, 0x9F, 0xE6, 0xD0, 0x8F, 0x42, 0x3D, 0xF0, 0xF4, 0x23, 0xD0, 0x8D, 0xC2, 0x67, 0x0B, 0x86, 0x18, 0x0F, 0xC0,
+/* 0xAF */ 0xCC, 0x03, 0x0C, 0x30, 0xC3, 0x0C, 0x30, 0xC3, 0x0C, 0x30, 0xC3, 0x00,
+/* 0xB0 */ 0x74, 0x63, 0x17, 0x00,
+/* 0xB1 */ 0x0C, 0x06, 0x03, 0x07, 0xE0, 0xC0, 0x60, 0x30, 0x18, 0x00, 0x00, 0x3F, 0xE0,
+/* 0xB2 */ 0xFF, 0xFF, 0xFF, 0xC0,
+/* 0xB3 */ 0xC3, 0xFF, 0xFF, 0xC0,
+/* 0xB4 */ 0x0C, 0x3F, 0xF0, 0xC3, 0x0C, 0x30, 0xC3, 0x0C, 0x30,
+/* 0xB5 */ 0xC3, 0x61, 0xB0, 0xD8, 0x6C, 0x36, 0x1B, 0x0D, 0x86, 0xE7, 0x7D, 0xF0, 0x18, 0x0C, 0x00,
+/* 0xB6 */ 0x3F, 0x7E, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0x72, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
+/* 0xB7 */ 0xE0,
+/* 0xB8 */ 0x66, 0x00, 0x3C, 0x66, 0xC3, 0xC3, 0xFF, 0xC0, 0xC0, 0xC3, 0x66, 0x3C,
+/* 0xB9 */ 0xC1, 0x81, 0x83, 0x03, 0x86, 0x05, 0x0C, 0xEB, 0x1A, 0x32, 0x34, 0x66, 0x68, 0xC4, 0xD1, 0x8D, 0xB3, 0x0B, 0x3A, 0x1E, 0x04, 0x1C, 0x08, 0x1B, 0xC0,
+/* 0xBA */ 0x3C, 0x46, 0xC3, 0x80, 0xF8, 0x80, 0x80, 0xC3, 0x46, 0x3C,
+/* 0xBB */ 0x89, 0x98, 0x99, 0x3C, 0xD1, 0x00,
+/* 0xBC */ 0x30, 0x03, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0xE0,
+/* 0xBD */ 0x3F, 0x18, 0x6C, 0x0F, 0x03, 0xC0, 0x1E, 0x01, 0xF0, 0x0E, 0x00, 0xF0, 0x3C, 0x0D, 0x86, 0x3F, 0x00,
+/* 0xBE */ 0x3E, 0xE3, 0xC0, 0xC0, 0x60, 0x3C, 0x07, 0xC3, 0xE3, 0x7E,
+/* 0xBF */ 0xCC, 0x03, 0x0C, 0x30, 0xC3, 0x0C, 0x30, 0xC3, 0x0C,
+/* 0xC0 */ 0x06, 0x00, 0xF0, 0x0F, 0x00, 0x90, 0x19, 0x81, 0x98, 0x10, 0x83, 0x0C, 0x3F, 0xC2, 0x04, 0x60, 0x66, 0x06, 0xC0, 0x30,
+/* 0xC1 */ 0xFF, 0x18, 0x03, 0x00, 0x60, 0x0C, 0x01, 0x80, 0x3F, 0xE6, 0x06, 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x6F, 0xF8,
+/* 0xC2 */ 0xFF, 0x18, 0x33, 0x03, 0x60, 0x6C, 0x0D, 0x83, 0x3F, 0xC6, 0x06, 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x6F, 0xF8,
+/* 0xC3 */ 0xFF, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0,
+/* 0xC4 */ 0x1F, 0xF0, 0x60, 0xC1, 0x83, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC1, 0x83, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC1, 0x03, 0x0C, 0x0C, 0xFF, 0xFF, 0x00, 0x3C, 0x00, 0xF0, 0x03,
+/* 0xC5 */ 0xFF, 0xE0, 0x30, 0x18, 0x0C, 0x06, 0x03, 0xFD, 0x80, 0xC0, 0x60, 0x30, 0x18, 0x0F, 0xF8,
+/* 0xC6 */ 0x61, 0x86, 0x31, 0x8C, 0x19, 0x98, 0x19, 0x98, 0x0D, 0xB0, 0x07, 0xE0, 0x03, 0xC0, 0x07, 0xE0, 0x0D, 0xB0, 0x19, 0x98, 0x31, 0x8C, 0x61, 0x86, 0xC1, 0x83,
+/* 0xC7 */ 0x3F, 0x18, 0x6C, 0x0F, 0x03, 0x00, 0xC0, 0x60, 0xF0, 0x06, 0x00, 0xF0, 0x3C, 0x0D, 0x86, 0x3F, 0x00,
+/* 0xC8 */ 0xC0, 0xF8, 0x1F, 0x07, 0xE0, 0xBC, 0x37, 0x8C, 0xF1, 0x1E, 0x63, 0xD8, 0x7A, 0x0F, 0xC1, 0xF0, 0x3E, 0x06,
+/* 0xC9 */ 0x11, 0x03, 0xE0, 0x00, 0x60, 0x7C, 0x0F, 0x83, 0xF0, 0x5E, 0x1B, 0xC6, 0x78, 0x8F, 0x31, 0xEC, 0x3D, 0x07, 0xE0, 0xF8, 0x1F, 0x03,
+/* 0xCA */ 0xC1, 0xB0, 0xCC, 0x63, 0x30, 0xD8, 0x3C, 0x0F, 0x03, 0xE0, 0xDC, 0x33, 0x8C, 0x73, 0x0E, 0xC1, 0xC0,
+/* 0xCB */ 0x3F, 0xCC, 0x33, 0x0C, 0xC3, 0x30, 0xCC, 0x33, 0x0C, 0xC3, 0x30, 0xC8, 0x36, 0x0D, 0x83, 0xC0, 0xC0,
+/* 0xCC */ 0xE0, 0x3F, 0x01, 0xFC, 0x1F, 0xE0, 0xFD, 0x05, 0xEC, 0x6F, 0x63, 0x79, 0x13, 0xCD, 0x9E, 0x6C, 0xF1, 0x47, 0x8E, 0x3C, 0x71, 0x80,
+/* 0xCD */ 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xFF, 0xFE, 0x03, 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x3C, 0x06,
+/* 0xCE */ 0x0F, 0x81, 0x83, 0x18, 0x0C, 0xC0, 0x6C, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x03, 0xC0, 0x1B, 0x01, 0x98, 0x0C, 0x60, 0xC0, 0xF8, 0x00,
+/* 0xCF */ 0xFF, 0xF8, 0x0F, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x3C, 0x06,
+/* 0xD0 */ 0xFF, 0x30, 0x6C, 0x0F, 0x03, 0xC0, 0xF0, 0x6F, 0xF3, 0x00, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x00,
+/* 0xD1 */ 0x1F, 0x86, 0x19, 0x81, 0xA0, 0x3C, 0x01, 0x80, 0x30, 0x06, 0x00, 0xC0, 0x68, 0x0D, 0x83, 0x18, 0x61, 0xF0,
+/* 0xD2 */ 0xFF, 0x86, 0x03, 0x01, 0x80, 0xC0, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x01, 0x80, 0xC0,
+/* 0xD3 */ 0xC0, 0xF0, 0x66, 0x19, 0x8C, 0x33, 0x0D, 0x81, 0xE0, 0x70, 0x0C, 0x06, 0x01, 0x80, 0xC0, 0x30, 0x00,
+/* 0xD4 */ 0x03, 0x00, 0x0C, 0x01, 0xFE, 0x1C, 0xCE, 0xE3, 0x1F, 0x0C, 0x3C, 0x30, 0xF0, 0xC3, 0xE3, 0x1D, 0xCC, 0xE3, 0xFF, 0x00, 0xC0, 0x03, 0x00,
+/* 0xD5 */ 0xC0, 0xD8, 0x66, 0x18, 0xCC, 0x1E, 0x07, 0x00, 0xC0, 0x78, 0x32, 0x0C, 0xC6, 0x1B, 0x07, 0xC0, 0xC0,
+/* 0xD6 */ 0xC0, 0x66, 0x03, 0x30, 0x19, 0x80, 0xCC, 0x06, 0x60, 0x33, 0x01, 0x98, 0x0C, 0xC0, 0x66, 0x03, 0x30, 0x19, 0x80, 0xCF, 0xFF, 0x80, 0x0C, 0x00, 0x60,
+/* 0xD7 */ 0xC1, 0xE0, 0xF0, 0x78, 0x3C, 0x1E, 0x0F, 0x06, 0xFF, 0x01, 0x80, 0xC0, 0x60, 0x30, 0x18,
+/* 0xD8 */ 0xC3, 0x1E, 0x18, 0xF0, 0xC7, 0x86, 0x3C, 0x31, 0xE1, 0x8F, 0x0C, 0x78, 0x63, 0xC3, 0x1E, 0x18, 0xF0, 0xC7, 0x86, 0x3F, 0xFF, 0x80,
+/* 0xD9 */ 0xC3, 0x19, 0x86, 0x33, 0x0C, 0x66, 0x18, 0xCC, 0x31, 0x98, 0x63, 0x30, 0xC6, 0x61, 0x8C, 0xC3, 0x19, 0x86, 0x33, 0x0C, 0x66, 0x18, 0xCF, 0xFF, 0xE0, 0x00, 0xC0, 0x01, 0x80,
+/* 0xDA */ 0xF8, 0x00, 0xC0, 0x06, 0x00, 0x30, 0x01, 0x80, 0x0F, 0xF0, 0x60, 0xC3, 0x03, 0x18, 0x18, 0xC0, 0xC6, 0x06, 0x30, 0x61, 0xFE, 0x00,
+/* 0xDB */ 0xC0, 0x0F, 0x00, 0x3C, 0x00, 0xF0, 0x03, 0xC0, 0x0F, 0xFE, 0x3C, 0x0C, 0xF0, 0x1B, 0xC0, 0x6F, 0x01, 0xBC, 0x06, 0xF0, 0x33, 0xFF, 0x8C,
+/* 0xDC */ 0xC0, 0x18, 0x03, 0x00, 0x60, 0x0C, 0x01, 0xFF, 0x30, 0x36, 0x03, 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x6F, 0xF8,
+/* 0xDD */ 0x3F, 0x0C, 0x33, 0x83, 0x60, 0x20, 0x06, 0x00, 0x47, 0xF8, 0x01, 0xC0, 0x78, 0x0D, 0x81, 0x30, 0xC1, 0xF0,
+/* 0xDE */ 0xC0, 0xF8, 0x61, 0x83, 0x31, 0x80, 0xD8, 0xC0, 0x6C, 0xC0, 0x1E, 0x60, 0x0F, 0xF0, 0x07, 0x98, 0x03, 0xCC, 0x01, 0xE3, 0x01, 0xB1, 0x80, 0xD8, 0x60, 0xCC, 0x0F, 0x80,
+/* 0xDF */ 0x3F, 0xD8, 0x3C, 0x0F, 0x03, 0xC0, 0xD8, 0x33, 0xFC, 0x33, 0x18, 0xCC, 0x36, 0x0D, 0x83, 0xC0, 0xC0,
+/* 0xE0 */ 0x7E, 0x71, 0xB0, 0xC0, 0x60, 0xF3, 0xDB, 0x0D, 0x86, 0xC7, 0x3D, 0xC0,
+/* 0xE1 */ 0x03, 0x1F, 0x78, 0x40, 0xFC, 0xE6, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x66, 0x3C,
+/* 0xE2 */ 0xFD, 0x8F, 0x0E, 0x3F, 0xDF, 0xB1, 0xE1, 0xC7, 0xF8,
+/* 0xE3 */ 0xFE, 0x31, 0x8C, 0x63, 0x18, 0xC6, 0x00,
+/* 0xE4 */ 0x1F, 0x83, 0x30, 0x66, 0x0C, 0xC1, 0x98, 0x33, 0x06, 0x61, 0x8C, 0x31, 0x9F, 0xFF, 0x01, 0xE0, 0x30,
+/* 0xE5 */ 0x3C, 0x66, 0xC3, 0xC3, 0xFF, 0xC0, 0xC0, 0xC3, 0x66, 0x3C,
+/* 0xE6 */ 0xC6, 0x36, 0x66, 0x36, 0xC1, 0xF8, 0x0F, 0x01, 0xF8, 0x36, 0xC6, 0x66, 0xC6, 0x38, 0x61,
+/* 0xE7 */ 0x79, 0x8C, 0x18, 0x30, 0x43, 0x01, 0xE3, 0xC6, 0xF8,
+/* 0xE8 */ 0xC7, 0xC7, 0xCF, 0xCB, 0xCB, 0xD3, 0xD3, 0xF3, 0xE3, 0xE3,
+/* 0xE9 */ 0x66, 0x18, 0xC7, 0xC7, 0xCF, 0xCB, 0xCB, 0xD3, 0xD3, 0xF3, 0xE3, 0xE3,
+/* 0xEA */ 0xC7, 0x9B, 0x66, 0x8E, 0x1E, 0x36, 0x66, 0xC7, 0x84,
+/* 0xEB */ 0x7E, 0xCD, 0x9B, 0x36, 0x6C, 0xD9, 0xA3, 0xC7, 0x0C,
+/* 0xEC */ 0xE3, 0xF1, 0xF8, 0xFE, 0xFF, 0x7E, 0xAF, 0x77, 0x93, 0xC9, 0xE0, 0xC0,
+/* 0xED */ 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xFF, 0xC3, 0xC3, 0xC3, 0xC3,
+/* 0xEE */ 0x3C, 0x66, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x66, 0x3C,
+/* 0xEF */ 0xFF, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3,
+/* 0xF0 */ 0xDE, 0x71, 0xB0, 0x78, 0x3C, 0x1E, 0x0F, 0x07, 0x83, 0xE3, 0x6F, 0x30, 0x18, 0x0C, 0x00,
+/* 0xF1 */ 0x3C, 0x66, 0xC3, 0xC0, 0xC0, 0xC0, 0xC0, 0xC3, 0x66, 0x3C,
+/* 0xF2 */ 0xFC, 0xC3, 0x0C, 0x30, 0xC3, 0x0C, 0x30, 0xC0,
+/* 0xF3 */ 0xC1, 0x43, 0x63, 0x62, 0x26, 0x36, 0x34, 0x1C, 0x1C, 0x18, 0x18, 0x18, 0x10, 0x60,
+/* 0xF4 */ 0x03, 0x00, 0x0C, 0x03, 0xB7, 0x19, 0xE6, 0xC3, 0x0F, 0x0C, 0x3C, 0x30, 0xF0, 0xC3, 0xC3, 0x0F, 0x0C, 0x36, 0x79, 0x8E, 0xDC, 0x03, 0x00, 0x0C, 0x00, 0x30, 0x00,
+/* 0xF5 */ 0x87, 0x89, 0xB1, 0xC3, 0x07, 0x1E, 0x26, 0xC5, 0x0C,
+/* 0xF6 */ 0xC3, 0x30, 0xCC, 0x33, 0x0C, 0xC3, 0x30, 0xCC, 0x33, 0x0C, 0xC3, 0x3F, 0xF0, 0x0C, 0x03,
+/* 0xF7 */ 0xC7, 0x8F, 0x1E, 0x3C, 0x6F, 0xC1, 0x83, 0x06, 0x0C,
+/* 0xF8 */ 0xCC, 0xF3, 0x3C, 0xCF, 0x33, 0xCC, 0xF3, 0x3C, 0xCF, 0x33, 0xCC, 0xFF, 0xF0,
+/* 0xF9 */ 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCF, 0xFF, 0x00, 0x30, 0x03,
+/* 0xFA */ 0xF0, 0x18, 0x0C, 0x06, 0x03, 0xF1, 0x8C, 0xC6, 0x63, 0x31, 0x9F, 0x80,
+/* 0xFB */ 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xFE, 0xF0, 0xFC, 0x3F, 0x0F, 0xC3, 0xFF, 0xB0,
+/* 0xFC */ 0xC0, 0xC0, 0xC0, 0xC0, 0xFE, 0xC3, 0xC3, 0xC3, 0xC3, 0xFE,
+/* 0xFD */ 0x3C, 0x62, 0xC3, 0x01, 0x1F, 0x01, 0x01, 0xC3, 0x62, 0x3C,
+/* 0xFE */ 0xC7, 0xCC, 0xC6, 0xD8, 0x3D, 0x83, 0xF8, 0x3D, 0x83, 0xD8, 0x3C, 0xC2, 0xCC, 0x6C, 0x7C,
+/* 0xFF */ 0x7F, 0xC3, 0xC3, 0xC3, 0x7F, 0x13, 0x33, 0x63, 0xC3, 0x83,
};
const GFXglyph FreeSans9pt_Win1251Glyphs[] PROGMEM = {
- /* ' ' 0x20 */ {0, 0, 0, 5, 0, 0},
- /* '!' 0x21 */ {0, 2, 13, 6, 2, -12},
- /* '"' 0x22 */ {4, 5, 4, 6, 1, -12},
- /* '#' 0x23 */ {7, 10, 12, 10, 0, -11},
- /* '$' 0x24 */ {22, 9, 16, 10, 1, -13},
- /* '%' 0x25 */ {40, 16, 13, 16, 1, -12},
- /* '&' 0x26 */ {66, 10, 13, 12, 1, -12},
- /* ''' 0x27 */ {83, 2, 4, 4, 1, -12},
- /* '(' 0x28 */ {84, 4, 17, 6, 1, -12},
- /* ')' 0x29 */ {93, 4, 17, 6, 1, -12},
- /* '*' 0x2A */ {102, 5, 5, 7, 1, -12},
- /* '+' 0x2B */ {106, 6, 8, 11, 3, -7},
- /* ',' 0x2C */ {112, 2, 4, 5, 2, 0},
- /* '-' 0x2D */ {113, 4, 1, 6, 1, -4},
- /* '.' 0x2E */ {114, 2, 1, 5, 1, 0},
- /* '/' 0x2F */ {115, 5, 13, 5, 0, -12},
- /* '0' 0x30 */ {124, 8, 13, 10, 1, -12},
- /* '1' 0x31 */ {137, 4, 13, 10, 3, -12},
- /* '2' 0x32 */ {144, 9, 13, 10, 1, -12},
- /* '3' 0x33 */ {159, 8, 13, 10, 1, -12},
- /* '4' 0x34 */ {172, 7, 13, 10, 2, -12},
- /* '5' 0x35 */ {184, 9, 13, 10, 1, -12},
- /* '6' 0x36 */ {199, 9, 13, 10, 1, -12},
- /* '7' 0x37 */ {214, 8, 13, 10, 0, -12},
- /* '8' 0x38 */ {227, 9, 13, 10, 1, -12},
- /* '9' 0x39 */ {242, 8, 13, 10, 1, -12},
- /* ':' 0x3A */ {255, 2, 10, 5, 1, -9},
- /* ';' 0x3B */ {258, 3, 12, 5, 1, -8},
- /* '<' 0x3C */ {263, 9, 9, 11, 1, -8},
- /* '=' 0x3D */ {274, 9, 4, 11, 1, -5},
- /* '>' 0x3E */ {279, 9, 8, 11, 1, -7},
- /* '?' 0x3F */ {288, 9, 13, 10, 1, -12},
- /* '@' 0x40 */ {303, 17, 16, 18, 1, -12},
- /* 'A' 0x41 */ {337, 12, 13, 12, 0, -12},
- /* 'B' 0x42 */ {357, 11, 13, 12, 1, -12},
- /* 'C' 0x43 */ {375, 11, 13, 13, 1, -12},
- /* 'D' 0x44 */ {393, 11, 13, 13, 1, -12},
- /* 'E' 0x45 */ {411, 9, 13, 11, 1, -12},
- /* 'F' 0x46 */ {426, 8, 13, 11, 1, -12},
- /* 'G' 0x47 */ {439, 12, 13, 14, 1, -12},
- /* 'H' 0x48 */ {459, 11, 13, 13, 1, -12},
- /* 'I' 0x49 */ {477, 2, 13, 5, 2, -12},
- /* 'J' 0x4A */ {481, 7, 13, 10, 1, -12},
- /* 'K' 0x4B */ {493, 10, 13, 12, 1, -12},
- /* 'L' 0x4C */ {510, 8, 13, 10, 1, -12},
- /* 'M' 0x4D */ {523, 13, 13, 15, 1, -12},
- /* 'N' 0x4E */ {545, 11, 13, 13, 1, -12},
- /* 'O' 0x4F */ {563, 13, 13, 14, 1, -12},
- /* 'P' 0x50 */ {585, 10, 13, 12, 1, -12},
- /* 'Q' 0x51 */ {602, 13, 14, 14, 1, -12},
- /* 'R' 0x52 */ {625, 12, 13, 13, 1, -12},
- /* 'S' 0x53 */ {645, 10, 13, 12, 1, -12},
- /* 'T' 0x54 */ {662, 9, 13, 11, 1, -12},
- /* 'U' 0x55 */ {677, 11, 13, 13, 1, -12},
- /* 'V' 0x56 */ {695, 11, 13, 11, 0, -12},
- /* 'W' 0x57 */ {713, 16, 13, 17, 0, -12},
- /* 'X' 0x58 */ {739, 10, 13, 12, 1, -12},
- /* 'Y' 0x59 */ {756, 12, 13, 12, 0, -12},
- /* 'Z' 0x5A */ {776, 10, 13, 11, 1, -12},
- /* '[' 0x5B */ {793, 3, 17, 5, 1, -12},
- /* '\' 0x5C */ {800, 5, 13, 5, 0, -12},
- /* ']' 0x5D */ {809, 3, 17, 5, 0, -12},
- /* '^' 0x5E */ {816, 7, 7, 8, 1, -12},
- /* '_' 0x5F */ {823, 10, 1, 10, 0, 3},
- /* '`' 0x60 */ {825, 4, 3, 5, 0, -12},
- /* 'a' 0x61 */ {827, 9, 10, 10, 1, -9},
- /* 'b' 0x62 */ {839, 9, 13, 10, 1, -12},
- /* 'c' 0x63 */ {854, 8, 10, 9, 1, -9},
- /* 'd' 0x64 */ {864, 8, 13, 10, 1, -12},
- /* 'e' 0x65 */ {877, 8, 10, 10, 1, -9},
- /* 'f' 0x66 */ {887, 4, 13, 5, 1, -12},
- /* 'g' 0x67 */ {894, 8, 14, 10, 1, -9},
- /* 'h' 0x68 */ {908, 8, 13, 10, 1, -12},
- /* 'i' 0x69 */ {921, 2, 13, 4, 1, -12},
- /* 'j' 0x6A */ {925, 4, 17, 4, 0, -12},
- /* 'k' 0x6B */ {934, 8, 13, 9, 1, -12},
- /* 'l' 0x6C */ {947, 2, 13, 4, 1, -12},
- /* 'm' 0x6D */ {951, 13, 10, 15, 1, -9},
- /* 'n' 0x6E */ {968, 8, 10, 10, 1, -9},
- /* 'o' 0x6F */ {978, 8, 10, 10, 1, -9},
- /* 'p' 0x70 */ {988, 9, 13, 10, 1, -9},
- /* 'q' 0x71 */ {1003, 8, 13, 10, 1, -9},
- /* 'r' 0x72 */ {1016, 5, 10, 6, 1, -9},
- /* 's' 0x73 */ {1023, 8, 10, 9, 1, -9},
- /* 't' 0x74 */ {1033, 4, 12, 5, 1, -11},
- /* 'u' 0x75 */ {1039, 8, 10, 10, 1, -9},
- /* 'v' 0x76 */ {1049, 9, 10, 9, 0, -9},
- /* 'w' 0x77 */ {1061, 13, 10, 13, 0, -9},
- /* 'x' 0x78 */ {1078, 7, 10, 9, 1, -9},
- /* 'y' 0x79 */ {1087, 8, 14, 9, 0, -9},
- /* 'z' 0x7A */ {1101, 7, 10, 9, 1, -9},
- /* '{' 0x7B */ {1110, 4, 17, 6, 1, -12},
- /* '|' 0x7C */ {1119, 2, 17, 4, 2, -12},
- /* '}' 0x7D */ {1124, 4, 17, 6, 1, -12},
- /* '~' 0x7E */ {1133, 7, 3, 9, 1, -7},
- /* 0x7F */ {1136, 13, 14, 15, 1, -12},
- /* 0x80 */ {1159, 12, 16, 14, 1, -12},
- /* 0x81 */ {1183, 8, 15, 11, 1, -14},
- /* 0x82 */ {1198, 2, 3, 5, 1, 0},
- /* 0x83 */ {1199, 5, 13, 7, 1, -12},
- /* 0x84 */ {1208, 5, 3, 7, 1, 0},
- /* 0x85 */ {1210, 10, 1, 12, 1, 0},
- /* 0x86 */ {1212, 8, 16, 10, 1, -12},
- /* 0x87 */ {1228, 8, 16, 10, 1, -12},
- /* 0x88 */ {1244, 10, 13, 12, 1, -12},
- /* 0x89 */ {1261, 18, 13, 18, 0, -12},
- /* 0x8A */ {1291, 17, 13, 18, 1, -12},
- /* 0x8B */ {1319, 2, 4, 4, 1, -6},
- /* 0x8C */ {1320, 17, 13, 18, 1, -12},
- /* 0x8D */ {1348, 10, 15, 11, 1, -14},
- /* 0x8E */ {1367, 12, 13, 14, 1, -12},
- /* 0x8F */ {1387, 11, 15, 13, 1, -12},
- /* 0x90 */ {1408, 9, 16, 10, 1, -12},
- /* 0x91 */ {1426, 2, 4, 4, 2, -12},
- /* 0x92 */ {1427, 2, 4, 4, 1, -12},
- /* 0x93 */ {1428, 5, 4, 7, 2, -12},
- /* 0x94 */ {1431, 5, 4, 7, 1, -12},
- /* 0x95 */ {1434, 4, 5, 7, 1, -8},
- /* 0x96 */ {1437, 7, 1, 9, 1, -4},
- /* 0x97 */ {1438, 16, 1, 18, 1, -4},
- /* 0x98 */ {1440, 0, 0, 0, 0, 0},
- /* 0x99 */ {1440, 18, 10, 18, 1, -13},
- /* 0x9A */ {1463, 13, 10, 14, 1, -9},
- /* 0x9B */ {1480, 2, 4, 5, 2, -6},
- /* 0x9C */ {1481, 14, 10, 15, 1, -9},
- /* 0x9D */ {1499, 7, 13, 9, 1, -12},
- /* 0x9E */ {1511, 9, 13, 10, 1, -12},
- /* 0x9F */ {1526, 8, 12, 10, 1, -9},
- /* 0xA0 */ {1538, 0, 0, 5, 0, 0},
- /* 0xA1 */ {1538, 10, 15, 11, 1, -14},
- /* 0xA2 */ {1557, 8, 16, 9, 0, -11},
- /* 0xA3 */ {1573, 7, 13, 10, 1, -12},
- /* 0xA4 */ {1585, 7, 6, 10, 2, -8},
- /* 0xA5 */ {1591, 10, 14, 11, 1, -13},
- /* 0xA6 */ {1609, 2, 17, 5, 2, -12},
- /* 0xA7 */ {1614, 9, 17, 10, 1, -12},
- /* 0xA8 */ {1634, 9, 15, 12, 1, -14},
- /* 0xA9 */ {1651, 14, 13, 14, 1, -12},
- /* 0xAA */ {1674, 11, 13, 13, 1, -12},
- /* 0xAB */ {1692, 7, 6, 9, 1, -7},
- /* 0xAC */ {1698, 9, 5, 11, 2, -5},
- /* 0xAD */ {1704, 0, 0, 0, 0, 0},
- /* 0xAE */ {1704, 14, 13, 14, 1, -12},
- /* 0xAF */ {1727, 6, 15, 5, 0, -14},
- /* 0xB0 */ {1739, 5, 5, 11, 3, -11},
- /* 0xB1 */ {1743, 9, 11, 11, 1, -10},
- /* 0xB2 */ {1756, 2, 13, 4, 1, -12},
- /* 0xB3 */ {1760, 2, 13, 4, 1, -12},
- /* 0xB4 */ {1764, 6, 12, 7, 1, -11},
- /* 0xB5 */ {1773, 9, 13, 10, 1, -9},
- /* 0xB6 */ {1788, 8, 16, 10, 2, -12},
- /* 0xB7 */ {1804, 3, 1, 5, 1, -4},
- /* 0xB8 */ {1805, 8, 12, 10, 1, -11},
- /* 0xB9 */ {1817, 15, 13, 17, 1, -12},
- /* 0xBA */ {1842, 8, 10, 9, 1, -9},
- /* 0xBB */ {1852, 7, 6, 9, 1, -7},
- /* 0xBC */ {1858, 4, 17, 4, 0, -12},
- /* 0xBD */ {1867, 10, 13, 12, 1, -12},
- /* 0xBE */ {1884, 8, 10, 9, 1, -9},
- /* 0xBF */ {1894, 6, 12, 5, -1, -11},
- /* 0xC0 */ {1903, 12, 13, 12, 0, -12},
- /* 0xC1 */ {1923, 11, 13, 12, 1, -12},
- /* 0xC2 */ {1941, 11, 13, 12, 1, -12},
- /* 0xC3 */ {1959, 8, 13, 8, 1, -12},
- /* 0xC4 */ {1972, 14, 16, 15, 1, -12},
- /* 0xC5 */ {2000, 9, 13, 12, 1, -12},
- /* 0xC6 */ {2015, 16, 13, 16, 0, -12},
- /* 0xC7 */ {2041, 10, 13, 12, 1, -12},
- /* 0xC8 */ {2058, 11, 13, 13, 1, -12},
- /* 0xC9 */ {2076, 11, 16, 13, 1, -15},
- /* 0xCA */ {2098, 10, 13, 11, 1, -12},
- /* 0xCB */ {2115, 10, 13, 12, 1, -12},
- /* 0xCC */ {2132, 13, 13, 15, 1, -12},
- /* 0xCD */ {2154, 11, 13, 13, 1, -12},
- /* 0xCE */ {2172, 13, 13, 14, 1, -12},
- /* 0xCF */ {2194, 11, 13, 13, 1, -12},
- /* 0xD0 */ {2212, 10, 13, 12, 1, -12},
- /* 0xD1 */ {2229, 11, 13, 13, 1, -12},
- /* 0xD2 */ {2247, 9, 13, 11, 1, -12},
- /* 0xD3 */ {2262, 10, 13, 11, 1, -12},
- /* 0xD4 */ {2279, 14, 13, 15, 1, -12},
- /* 0xD5 */ {2302, 10, 13, 12, 1, -12},
- /* 0xD6 */ {2319, 13, 15, 13, 1, -12},
- /* 0xD7 */ {2344, 9, 13, 11, 1, -12},
- /* 0xD8 */ {2359, 13, 13, 15, 1, -12},
- /* 0xD9 */ {2381, 15, 15, 15, 1, -12},
- /* 0xDA */ {2410, 13, 13, 15, 2, -12},
- /* 0xDB */ {2432, 14, 13, 16, 1, -12},
- /* 0xDC */ {2455, 11, 13, 12, 1, -12},
- /* 0xDD */ {2473, 11, 13, 13, 1, -12},
- /* 0xDE */ {2491, 17, 13, 18, 1, -12},
- /* 0xDF */ {2519, 10, 13, 12, 1, -12},
- /* 0xE0 */ {2536, 9, 10, 10, 1, -9},
- /* 0xE1 */ {2548, 8, 14, 10, 1, -13},
- /* 0xE2 */ {2562, 7, 10, 9, 1, -9},
- /* 0xE3 */ {2571, 5, 10, 7, 1, -9},
- /* 0xE4 */ {2578, 11, 12, 10, 0, -9},
- /* 0xE5 */ {2595, 8, 10, 10, 1, -9},
- /* 0xE6 */ {2605, 12, 10, 14, 1, -9},
- /* 0xE7 */ {2620, 7, 10, 9, 1, -9},
- /* 0xE8 */ {2629, 8, 10, 10, 1, -9},
- /* 0xE9 */ {2639, 8, 12, 10, 1, -11},
- /* 0xEA */ {2651, 7, 10, 9, 1, -9},
- /* 0xEB */ {2660, 7, 10, 8, 0, -9},
- /* 0xEC */ {2669, 9, 10, 11, 1, -9},
- /* 0xED */ {2681, 8, 10, 10, 1, -9},
- /* 0xEE */ {2691, 8, 10, 10, 1, -9},
- /* 0xEF */ {2701, 8, 10, 10, 1, -9},
- /* 0xF0 */ {2711, 9, 13, 10, 1, -9},
- /* 0xF1 */ {2726, 8, 10, 9, 1, -9},
- /* 0xF2 */ {2736, 6, 10, 7, 1, -9},
- /* 0xF3 */ {2744, 8, 14, 9, 0, -9},
- /* 0xF4 */ {2758, 14, 15, 15, 1, -11},
- /* 0xF5 */ {2785, 7, 10, 9, 1, -9},
- /* 0xF6 */ {2794, 10, 12, 10, 1, -9},
- /* 0xF7 */ {2809, 7, 10, 9, 1, -9},
- /* 0xF8 */ {2818, 10, 10, 12, 1, -9},
- /* 0xF9 */ {2831, 12, 12, 13, 1, -9},
- /* 0xFA */ {2849, 9, 10, 12, 2, -9},
- /* 0xFB */ {2861, 10, 10, 12, 1, -9},
- /* 0xFC */ {2874, 8, 10, 9, 1, -9},
- /* 0xFD */ {2884, 8, 10, 9, 1, -9},
- /* 0xFE */ {2894, 12, 10, 13, 1, -9},
- /* 0xFF */ {2909, 8, 10, 10, 1, -9},
+/* 0x01 */ { 0, 15, 15, 17, 1, -13 },
+/* 0x02 */ { 29, 15, 15, 17, 1, -13 },
+/* 0x03 */ { 58, 15, 16, 17, 1, -14 },
+/* 0x04 */ { 88, 15, 16, 17, 1, -14 },
+/* 0x05 */ { 118, 16, 15, 18, 1, -13 },
+/* 0x06 */ { 148, 15, 15, 17, 1, -13 },
+/* 0x07 */ { 177, 0, 0, 8, 0, 0 },
+/* 0x08 */ { 177, 17, 16, 19, 1, -14 },
+/* 0x09 */ { 211, 17, 12, 19, 1, -12 },
+/* 0x0A */ { 237, 0, 0, 8, 0, 0 },
+/* 0x0B */ { 237, 17, 16, 19, 1, -14 },
+/* 0x0C */ { 271, 15, 14, 17, 1, -12 },
+/* 0x0D */ { 298, 0, 0, 8, 0, 0 },
+/* 0x0E */ { 298, 15, 16, 17, 1, -14 },
+/* 0x0F */ { 328, 15, 15, 17, 1, -13 },
+/* 0x10 */ { 357, 15, 15, 17, 1, -13 },
+/* 0x11 */ { 386, 15, 16, 17, 1, -14 },
+/* 0x12 */ { 416, 17, 17, 19, 1, -15 },
+/* 0x13 */ { 453, 15, 16, 17, 1, -14 },
+/* 0x14 */ { 483, 15, 16, 17, 1, -14 },
+/* 0x15 */ { 513, 15, 16, 17, 1, -14 },
+/* 0x16 */ { 543, 11, 16, 13, 1, -14 },
+/* 0x17 */ { 565, 15, 16, 17, 1, -14 },
+/* 0x18 */ { 595, 18, 15, 20, 1, -13 },
+/* 0x19 */ { 629, 15, 16, 17, 1, -14 },
+/* 0x1A */ { 659, 13, 14, 15, 1, -12 },
+/* 0x1B */ { 682, 17, 16, 19, 1, -14 },
+/* 0x1C */ { 716, 15, 16, 17, 1, -14 },
+/* 0x1D */ { 746, 15, 15, 17, 1, -13 },
+/* 0x1E */ { 775, 17, 16, 19, 1, -14 },
+/* 0x1F */ { 809, 11, 16, 13, 1, -14 },
+/* ' ' 0x20 */ { 831, 0, 0, 5, 0, 0 },
+/* '!' 0x21 */ { 831, 2, 13, 6, 2, -12 },
+/* '"' 0x22 */ { 835, 5, 4, 6, 1, -12 },
+/* '#' 0x23 */ { 838, 10, 12, 10, 0, -11 },
+/* '$' 0x24 */ { 853, 9, 16, 10, 1, -13 },
+/* '%' 0x25 */ { 871, 16, 13, 16, 1, -12 },
+/* '&' 0x26 */ { 897, 10, 13, 12, 1, -12 },
+/* ''' 0x27 */ { 914, 2, 4, 4, 1, -12 },
+/* '(' 0x28 */ { 915, 4, 17, 6, 1, -12 },
+/* ')' 0x29 */ { 924, 4, 17, 6, 1, -12 },
+/* '*' 0x2A */ { 933, 5, 5, 7, 1, -12 },
+/* '+' 0x2B */ { 937, 6, 8, 11, 3, -7 },
+/* ',' 0x2C */ { 943, 2, 4, 5, 2, 0 },
+/* '-' 0x2D */ { 944, 4, 1, 6, 1, -4 },
+/* '.' 0x2E */ { 945, 2, 1, 5, 1, 0 },
+/* '/' 0x2F */ { 946, 5, 13, 5, 0, -12 },
+/* '0' 0x30 */ { 955, 8, 13, 10, 1, -12 },
+/* '1' 0x31 */ { 968, 4, 13, 10, 3, -12 },
+/* '2' 0x32 */ { 975, 9, 13, 10, 1, -12 },
+/* '3' 0x33 */ { 990, 8, 13, 10, 1, -12 },
+/* '4' 0x34 */ { 1003, 7, 13, 10, 2, -12 },
+/* '5' 0x35 */ { 1015, 9, 13, 10, 1, -12 },
+/* '6' 0x36 */ { 1030, 9, 13, 10, 1, -12 },
+/* '7' 0x37 */ { 1045, 8, 13, 10, 0, -12 },
+/* '8' 0x38 */ { 1058, 9, 13, 10, 1, -12 },
+/* '9' 0x39 */ { 1073, 8, 13, 10, 1, -12 },
+/* ':' 0x3A */ { 1086, 2, 10, 5, 1, -9 },
+/* ';' 0x3B */ { 1089, 3, 12, 5, 1, -8 },
+/* '<' 0x3C */ { 1094, 9, 9, 11, 1, -8 },
+/* '=' 0x3D */ { 1105, 9, 4, 11, 1, -5 },
+/* '>' 0x3E */ { 1110, 9, 8, 11, 1, -7 },
+/* '?' 0x3F */ { 1119, 9, 13, 10, 1, -12 },
+/* '@' 0x40 */ { 1134, 17, 16, 18, 1, -12 },
+/* 'A' 0x41 */ { 1168, 12, 13, 12, 0, -12 },
+/* 'B' 0x42 */ { 1188, 11, 13, 12, 1, -12 },
+/* 'C' 0x43 */ { 1206, 11, 13, 13, 1, -12 },
+/* 'D' 0x44 */ { 1224, 11, 13, 13, 1, -12 },
+/* 'E' 0x45 */ { 1242, 9, 13, 11, 1, -12 },
+/* 'F' 0x46 */ { 1257, 8, 13, 11, 1, -12 },
+/* 'G' 0x47 */ { 1270, 12, 13, 14, 1, -12 },
+/* 'H' 0x48 */ { 1290, 11, 13, 13, 1, -12 },
+/* 'I' 0x49 */ { 1308, 2, 13, 5, 2, -12 },
+/* 'J' 0x4A */ { 1312, 7, 13, 10, 1, -12 },
+/* 'K' 0x4B */ { 1324, 10, 13, 12, 1, -12 },
+/* 'L' 0x4C */ { 1341, 8, 13, 10, 1, -12 },
+/* 'M' 0x4D */ { 1354, 13, 13, 15, 1, -12 },
+/* 'N' 0x4E */ { 1376, 11, 13, 13, 1, -12 },
+/* 'O' 0x4F */ { 1394, 13, 13, 14, 1, -12 },
+/* 'P' 0x50 */ { 1416, 10, 13, 12, 1, -12 },
+/* 'Q' 0x51 */ { 1433, 13, 14, 14, 1, -12 },
+/* 'R' 0x52 */ { 1456, 12, 13, 13, 1, -12 },
+/* 'S' 0x53 */ { 1476, 10, 13, 12, 1, -12 },
+/* 'T' 0x54 */ { 1493, 9, 13, 11, 1, -12 },
+/* 'U' 0x55 */ { 1508, 11, 13, 13, 1, -12 },
+/* 'V' 0x56 */ { 1526, 11, 13, 11, 0, -12 },
+/* 'W' 0x57 */ { 1544, 16, 13, 17, 0, -12 },
+/* 'X' 0x58 */ { 1570, 10, 13, 12, 1, -12 },
+/* 'Y' 0x59 */ { 1587, 12, 13, 12, 0, -12 },
+/* 'Z' 0x5A */ { 1607, 10, 13, 11, 1, -12 },
+/* '[' 0x5B */ { 1624, 3, 17, 5, 1, -12 },
+/* '\' 0x5C */ { 1631, 5, 13, 5, 0, -12 },
+/* ']' 0x5D */ { 1640, 3, 17, 5, 0, -12 },
+/* '^' 0x5E */ { 1647, 7, 7, 8, 1, -12 },
+/* '_' 0x5F */ { 1654, 10, 1, 10, 0, 3 },
+/* '`' 0x60 */ { 1656, 4, 3, 5, 0, -12 },
+/* 'a' 0x61 */ { 1658, 9, 10, 10, 1, -9 },
+/* 'b' 0x62 */ { 1670, 9, 13, 10, 1, -12 },
+/* 'c' 0x63 */ { 1685, 8, 10, 9, 1, -9 },
+/* 'd' 0x64 */ { 1695, 8, 13, 10, 1, -12 },
+/* 'e' 0x65 */ { 1708, 8, 10, 10, 1, -9 },
+/* 'f' 0x66 */ { 1718, 4, 13, 5, 1, -12 },
+/* 'g' 0x67 */ { 1725, 8, 14, 10, 1, -9 },
+/* 'h' 0x68 */ { 1739, 8, 13, 10, 1, -12 },
+/* 'i' 0x69 */ { 1752, 2, 13, 4, 1, -12 },
+/* 'j' 0x6A */ { 1756, 4, 17, 4, 0, -12 },
+/* 'k' 0x6B */ { 1765, 8, 13, 9, 1, -12 },
+/* 'l' 0x6C */ { 1778, 2, 13, 4, 1, -12 },
+/* 'm' 0x6D */ { 1782, 13, 10, 15, 1, -9 },
+/* 'n' 0x6E */ { 1799, 8, 10, 10, 1, -9 },
+/* 'o' 0x6F */ { 1809, 8, 10, 10, 1, -9 },
+/* 'p' 0x70 */ { 1819, 9, 13, 10, 1, -9 },
+/* 'q' 0x71 */ { 1834, 8, 13, 10, 1, -9 },
+/* 'r' 0x72 */ { 1847, 5, 10, 6, 1, -9 },
+/* 's' 0x73 */ { 1854, 8, 10, 9, 1, -9 },
+/* 't' 0x74 */ { 1864, 4, 12, 5, 1, -11 },
+/* 'u' 0x75 */ { 1870, 8, 10, 10, 1, -9 },
+/* 'v' 0x76 */ { 1880, 9, 10, 9, 0, -9 },
+/* 'w' 0x77 */ { 1892, 13, 10, 13, 0, -9 },
+/* 'x' 0x78 */ { 1909, 7, 10, 9, 1, -9 },
+/* 'y' 0x79 */ { 1918, 8, 14, 9, 0, -9 },
+/* 'z' 0x7A */ { 1932, 7, 10, 9, 1, -9 },
+/* '{' 0x7B */ { 1941, 4, 17, 6, 1, -12 },
+/* '|' 0x7C */ { 1950, 2, 17, 4, 2, -12 },
+/* '}' 0x7D */ { 1955, 4, 17, 6, 1, -12 },
+/* '~' 0x7E */ { 1964, 7, 3, 9, 1, -7 },
+/* 0x7F */ { 1967, 13, 14, 15, 1, -12 },
+/* 0x80 */ { 1990, 12, 16, 14, 1, -12 },
+/* 0x81 */ { 2014, 8, 15, 11, 1, -14 },
+/* 0x82 */ { 2029, 2, 3, 5, 1, 0 },
+/* 0x83 */ { 2030, 5, 13, 7, 1, -12 },
+/* 0x84 */ { 2039, 5, 3, 7, 1, 0 },
+/* 0x85 */ { 2041, 10, 1, 12, 1, 0 },
+/* 0x86 */ { 2043, 8, 16, 10, 1, -12 },
+/* 0x87 */ { 2059, 8, 16, 10, 1, -12 },
+/* 0x88 */ { 2075, 10, 13, 12, 1, -12 },
+/* 0x89 */ { 2092, 18, 13, 18, 0, -12 },
+/* 0x8A */ { 2122, 17, 13, 18, 1, -12 },
+/* 0x8B */ { 2150, 2, 4, 4, 1, -6 },
+/* 0x8C */ { 2151, 17, 13, 18, 1, -12 },
+/* 0x8D */ { 2179, 10, 15, 11, 1, -14 },
+/* 0x8E */ { 2198, 12, 13, 14, 1, -12 },
+/* 0x8F */ { 2218, 11, 15, 13, 1, -12 },
+/* 0x90 */ { 2239, 9, 16, 10, 1, -12 },
+/* 0x91 */ { 2257, 2, 4, 4, 2, -12 },
+/* 0x92 */ { 2258, 2, 4, 4, 1, -12 },
+/* 0x93 */ { 2259, 5, 4, 7, 2, -12 },
+/* 0x94 */ { 2262, 5, 4, 7, 1, -12 },
+/* 0x95 */ { 2265, 4, 5, 7, 1, -8 },
+/* 0x96 */ { 2268, 7, 1, 9, 1, -4 },
+/* 0x97 */ { 2269, 16, 1, 18, 1, -4 },
+/* 0x98 */ { 2271, 0, 0, 0, 0, 0 },
+/* 0x99 */ { 2271, 18, 10, 18, 1, -13 },
+/* 0x9A */ { 2294, 13, 10, 14, 1, -9 },
+/* 0x9B */ { 2311, 2, 4, 5, 2, -6 },
+/* 0x9C */ { 2312, 14, 10, 15, 1, -9 },
+/* 0x9D */ { 2330, 7, 13, 9, 1, -12 },
+/* 0x9E */ { 2342, 9, 13, 10, 1, -12 },
+/* 0x9F */ { 2357, 8, 12, 10, 1, -9 },
+/* 0xA0 */ { 2369, 0, 0, 5, 0, 0 },
+/* 0xA1 */ { 2369, 10, 15, 11, 1, -14 },
+/* 0xA2 */ { 2388, 8, 16, 9, 0, -11 },
+/* 0xA3 */ { 2404, 7, 13, 10, 1, -12 },
+/* 0xA4 */ { 2416, 7, 6, 10, 2, -8 },
+/* 0xA5 */ { 2422, 10, 14, 11, 1, -13 },
+/* 0xA6 */ { 2440, 2, 17, 5, 2, -12 },
+/* 0xA7 */ { 2445, 9, 17, 10, 1, -12 },
+/* 0xA8 */ { 2465, 9, 15, 12, 1, -14 },
+/* 0xA9 */ { 2482, 14, 13, 14, 1, -12 },
+/* 0xAA */ { 2505, 11, 13, 13, 1, -12 },
+/* 0xAB */ { 2523, 7, 6, 9, 1, -7 },
+/* 0xAC */ { 2529, 9, 5, 11, 2, -5 },
+/* 0xAD */ { 2535, 0, 0, 0, 0, 0 },
+/* 0xAE */ { 2535, 14, 13, 14, 1, -12 },
+/* 0xAF */ { 2558, 6, 15, 5, 0, -14 },
+/* 0xB0 */ { 2570, 5, 5, 11, 3, -11 },
+/* 0xB1 */ { 2574, 9, 11, 11, 1, -10 },
+/* 0xB2 */ { 2587, 2, 13, 4, 1, -12 },
+/* 0xB3 */ { 2591, 2, 13, 4, 1, -12 },
+/* 0xB4 */ { 2595, 6, 12, 7, 1, -11 },
+/* 0xB5 */ { 2604, 9, 13, 10, 1, -9 },
+/* 0xB6 */ { 2619, 8, 16, 10, 2, -12 },
+/* 0xB7 */ { 2635, 3, 1, 5, 1, -4 },
+/* 0xB8 */ { 2636, 8, 12, 10, 1, -11 },
+/* 0xB9 */ { 2648, 15, 13, 17, 1, -12 },
+/* 0xBA */ { 2673, 8, 10, 9, 1, -9 },
+/* 0xBB */ { 2683, 7, 6, 9, 1, -7 },
+/* 0xBC */ { 2689, 4, 17, 4, 0, -12 },
+/* 0xBD */ { 2698, 10, 13, 12, 1, -12 },
+/* 0xBE */ { 2715, 8, 10, 9, 1, -9 },
+/* 0xBF */ { 2725, 6, 12, 5, -1, -11 },
+/* 0xC0 */ { 2734, 12, 13, 12, 0, -12 },
+/* 0xC1 */ { 2754, 11, 13, 12, 1, -12 },
+/* 0xC2 */ { 2772, 11, 13, 12, 1, -12 },
+/* 0xC3 */ { 2790, 8, 13, 8, 1, -12 },
+/* 0xC4 */ { 2803, 14, 16, 15, 1, -12 },
+/* 0xC5 */ { 2831, 9, 13, 12, 1, -12 },
+/* 0xC6 */ { 2846, 16, 13, 16, 0, -12 },
+/* 0xC7 */ { 2872, 10, 13, 12, 1, -12 },
+/* 0xC8 */ { 2889, 11, 13, 13, 1, -12 },
+/* 0xC9 */ { 2907, 11, 16, 13, 1, -15 },
+/* 0xCA */ { 2929, 10, 13, 11, 1, -12 },
+/* 0xCB */ { 2946, 10, 13, 12, 1, -12 },
+/* 0xCC */ { 2963, 13, 13, 15, 1, -12 },
+/* 0xCD */ { 2985, 11, 13, 13, 1, -12 },
+/* 0xCE */ { 3003, 13, 13, 14, 1, -12 },
+/* 0xCF */ { 3025, 11, 13, 13, 1, -12 },
+/* 0xD0 */ { 3043, 10, 13, 12, 1, -12 },
+/* 0xD1 */ { 3060, 11, 13, 13, 1, -12 },
+/* 0xD2 */ { 3078, 9, 13, 11, 1, -12 },
+/* 0xD3 */ { 3093, 10, 13, 11, 1, -12 },
+/* 0xD4 */ { 3110, 14, 13, 15, 1, -12 },
+/* 0xD5 */ { 3133, 10, 13, 12, 1, -12 },
+/* 0xD6 */ { 3150, 13, 15, 13, 1, -12 },
+/* 0xD7 */ { 3175, 9, 13, 11, 1, -12 },
+/* 0xD8 */ { 3190, 13, 13, 15, 1, -12 },
+/* 0xD9 */ { 3212, 15, 15, 15, 1, -12 },
+/* 0xDA */ { 3241, 13, 13, 15, 2, -12 },
+/* 0xDB */ { 3263, 14, 13, 16, 1, -12 },
+/* 0xDC */ { 3286, 11, 13, 12, 1, -12 },
+/* 0xDD */ { 3304, 11, 13, 13, 1, -12 },
+/* 0xDE */ { 3322, 17, 13, 18, 1, -12 },
+/* 0xDF */ { 3350, 10, 13, 12, 1, -12 },
+/* 0xE0 */ { 3367, 9, 10, 10, 1, -9 },
+/* 0xE1 */ { 3379, 8, 14, 10, 1, -13 },
+/* 0xE2 */ { 3393, 7, 10, 9, 1, -9 },
+/* 0xE3 */ { 3402, 5, 10, 7, 1, -9 },
+/* 0xE4 */ { 3409, 11, 12, 10, 0, -9 },
+/* 0xE5 */ { 3426, 8, 10, 10, 1, -9 },
+/* 0xE6 */ { 3436, 12, 10, 14, 1, -9 },
+/* 0xE7 */ { 3451, 7, 10, 9, 1, -9 },
+/* 0xE8 */ { 3460, 8, 10, 10, 1, -9 },
+/* 0xE9 */ { 3470, 8, 12, 10, 1, -11 },
+/* 0xEA */ { 3482, 7, 10, 9, 1, -9 },
+/* 0xEB */ { 3491, 7, 10, 8, 0, -9 },
+/* 0xEC */ { 3500, 9, 10, 11, 1, -9 },
+/* 0xED */ { 3512, 8, 10, 10, 1, -9 },
+/* 0xEE */ { 3522, 8, 10, 10, 1, -9 },
+/* 0xEF */ { 3532, 8, 10, 10, 1, -9 },
+/* 0xF0 */ { 3542, 9, 13, 10, 1, -9 },
+/* 0xF1 */ { 3557, 8, 10, 9, 1, -9 },
+/* 0xF2 */ { 3567, 6, 10, 7, 1, -9 },
+/* 0xF3 */ { 3575, 8, 14, 9, 0, -9 },
+/* 0xF4 */ { 3589, 14, 15, 15, 1, -11 },
+/* 0xF5 */ { 3616, 7, 10, 9, 1, -9 },
+/* 0xF6 */ { 3625, 10, 12, 10, 1, -9 },
+/* 0xF7 */ { 3640, 7, 10, 9, 1, -9 },
+/* 0xF8 */ { 3649, 10, 10, 12, 1, -9 },
+/* 0xF9 */ { 3662, 12, 12, 13, 1, -9 },
+/* 0xFA */ { 3680, 9, 10, 12, 2, -9 },
+/* 0xFB */ { 3692, 10, 10, 12, 1, -9 },
+/* 0xFC */ { 3705, 8, 10, 9, 1, -9 },
+/* 0xFD */ { 3715, 8, 10, 9, 1, -9 },
+/* 0xFE */ { 3725, 12, 10, 13, 1, -9 },
+/* 0xFF */ { 3740, 8, 10, 10, 1, -9 },
};
-const GFXfont FreeSans9pt_Win1251 PROGMEM = {(uint8_t *)FreeSans9pt_Win1251Bitmaps, (GFXglyph *)FreeSans9pt_Win1251Glyphs, 0x20,
- 0xFF, 21};
+const GFXfont FreeSans9pt_Win1251 PROGMEM = {
+(uint8_t*)FreeSans9pt_Win1251Bitmaps,
+(GFXglyph*)FreeSans9pt_Win1251Glyphs,
+0x01, 0xFF, 21
+};
diff --git a/src/graphics/niche/Fonts/FreeSans9pt_Win1252.h b/src/graphics/niche/Fonts/FreeSans9pt_Win1252.h
index 20f2ddc2f..8bc656632 100644
--- a/src/graphics/niche/Fonts/FreeSans9pt_Win1252.h
+++ b/src/graphics/niche/Fonts/FreeSans9pt_Win1252.h
@@ -1,494 +1,527 @@
+// trunk-ignore-all(clang-format)
#pragma once
+/* PROPERTIES
+
+FONT_NAME FreeSans9pt_Win1252
+*/
const uint8_t FreeSans9pt_Win1252Bitmaps[] PROGMEM = {
- /* ' ' 0x20 */
- 0xFF, 0xFF, 0xF0, 0xC0, /* '!' 0x21 */
- 0xDE, 0xF7, 0x20, /* '"' 0x22 */
- 0x09, 0x86, 0x41, 0x91, 0xFF, 0x13, 0x04, 0xC3, 0x20, 0xC8, 0xFF, 0x89, 0x82, 0x61, 0x90, /* '#' 0x23 */
- 0x10, 0x1F, 0x14, 0xDA, 0x3D, 0x1E, 0x83, 0x40, 0x78, 0x17, 0x08, 0xF4, 0x7A, 0x35, 0x33, 0xF0, 0x40, 0x20, /* '$' 0x24 */
- 0x38, 0x10, 0xEC, 0x20, 0xC6, 0x20, 0xC6, 0x40, 0xC6, 0x40, 0x6C, 0x80, 0x39, 0x00, 0x01, 0x3C, 0x02, 0x77, 0x02, 0x63, 0x04,
- 0x63, 0x04, 0x77, 0x08, 0x3C, /* '%' 0x25 */
- 0x0E, 0x0C, 0xC3, 0x30, 0xCC, 0x1E, 0x03, 0x03, 0xC1, 0x9B, 0xC2, 0xF0, 0xEC, 0x19, 0x8F, 0x3C, 0x40, /* '&' 0x26 */
- 0xFE, /* ''' 0x27 */
- 0x13, 0x26, 0x6C, 0xCC, 0xCC, 0xC4, 0x66, 0x23, 0x10, /* '(' 0x28 */
- 0x8C, 0x46, 0x63, 0x33, 0x33, 0x32, 0x66, 0x4C, 0x80, /* ')' 0x29 */
- 0x25, 0x7E, 0xA5, 0x00, /* '*' 0x2A */
- 0x30, 0xC3, 0x3F, 0x30, 0xC3, 0x0C, /* '+' 0x2B */
- 0xD6, /* ',' 0x2C */
- 0xF0, /* '-' 0x2D */
- 0xC0, /* '.' 0x2E */
- 0x08, 0x44, 0x21, 0x10, 0x84, 0x42, 0x11, 0x08, 0x00, /* '/' 0x2F */
- 0x3C, 0x66, 0x42, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x42, 0x66, 0x3C, /* '0' 0x30 */
- 0x11, 0x3F, 0x33, 0x33, 0x33, 0x33, 0x30, /* '1' 0x31 */
- 0x3E, 0x31, 0xB0, 0x78, 0x30, 0x18, 0x1C, 0x1C, 0x1C, 0x18, 0x18, 0x10, 0x08, 0x07, 0xF8, /* '2' 0x32 */
- 0x3C, 0x66, 0xC3, 0xC3, 0x03, 0x06, 0x1C, 0x07, 0x03, 0xC3, 0xC3, 0x66, 0x3C, /* '3' 0x33 */
- 0x0C, 0x18, 0x71, 0x62, 0xC9, 0xA3, 0x46, 0xFE, 0x18, 0x30, 0x60, 0xC0, /* '4' 0x34 */
- 0x7F, 0x20, 0x10, 0x08, 0x08, 0x07, 0xF3, 0x8C, 0x03, 0x01, 0x80, 0xF0, 0x6C, 0x63, 0xE0, /* '5' 0x35 */
- 0x1E, 0x31, 0x98, 0x78, 0x0C, 0x06, 0xF3, 0x8D, 0x83, 0xC1, 0xE0, 0xD0, 0x6C, 0x63, 0xE0, /* '6' 0x36 */
- 0xFF, 0x03, 0x02, 0x06, 0x04, 0x0C, 0x08, 0x18, 0x18, 0x18, 0x10, 0x30, 0x30, /* '7' 0x37 */
- 0x3E, 0x31, 0xB0, 0x78, 0x3C, 0x1B, 0x18, 0xF8, 0xC6, 0xC1, 0xE0, 0xF0, 0x6C, 0x63, 0xE0, /* '8' 0x38 */
- 0x3C, 0x66, 0xC2, 0xC3, 0xC3, 0xC3, 0x67, 0x3B, 0x03, 0x03, 0xC2, 0x66, 0x3C, /* '9' 0x39 */
- 0xC0, 0x00, 0x30, /* ':' 0x3A */
- 0xC0, 0x00, 0x00, 0x64, 0xA0, /* ';' 0x3B */
- 0x00, 0x81, 0xC7, 0x8E, 0x0C, 0x07, 0x80, 0x70, 0x0E, 0x01, 0x80, /* '<' 0x3C */
- 0xFF, 0x80, 0x00, 0x1F, 0xF0, /* '=' 0x3D */
- 0xE0, 0x1C, 0x03, 0x80, 0x30, 0x70, 0xE3, 0x81, 0x00, /* '>' 0x3E */
- 0x3E, 0x31, 0xB0, 0x78, 0x30, 0x18, 0x18, 0x38, 0x18, 0x18, 0x0C, 0x00, 0x00, 0x01, 0x80, /* '?' 0x3F */
- 0x03, 0xF0, 0x06, 0x0E, 0x06, 0x01, 0x86, 0x00, 0x66, 0x1D, 0xBB, 0x31, 0xCF, 0x18, 0xC7, 0x98, 0x63, 0xCC, 0x31, 0xE6, 0x11,
- 0xB3, 0x99, 0xCC, 0xF7, 0x86, 0x00, 0x01, 0x80, 0x00, 0x70, 0x40, 0x0F, 0xE0, /* '@' 0x40 */
- 0x06, 0x00, 0xF0, 0x0F, 0x00, 0x90, 0x19, 0x81, 0x98, 0x10, 0x83, 0x0C, 0x3F, 0xC2, 0x04, 0x60, 0x66, 0x06, 0xC0,
- 0x30, /* 'A' 0x41 */
- 0xFF, 0x18, 0x33, 0x03, 0x60, 0x6C, 0x0D, 0x83, 0x3F, 0xC6, 0x06, 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x6F, 0xF8, /* 'B' 0x42 */
- 0x1F, 0x86, 0x19, 0x81, 0xA0, 0x3C, 0x01, 0x80, 0x30, 0x06, 0x00, 0xC0, 0x68, 0x0D, 0x83, 0x18, 0x61, 0xF0, /* 'C' 0x43 */
- 0xFF, 0x18, 0x33, 0x03, 0x60, 0x3C, 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0x78, 0x0F, 0x03, 0x60, 0xCF, 0xF0, /* 'D' 0x44 */
- 0xFF, 0xE0, 0x30, 0x18, 0x0C, 0x06, 0x03, 0xFD, 0x80, 0xC0, 0x60, 0x30, 0x18, 0x0F, 0xF8, /* 'E' 0x45 */
- 0xFF, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xFE, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, /* 'F' 0x46 */
- 0x0F, 0x83, 0x0E, 0x60, 0x66, 0x03, 0xC0, 0x0C, 0x00, 0xC1, 0xFC, 0x03, 0xC0, 0x36, 0x03, 0x60, 0x73, 0x0F, 0x0F,
- 0x10, /* 'G' 0x47 */
- 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xFF, 0xFE, 0x03, 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x3C, 0x06, /* 'H' 0x48 */
- 0xFF, 0xFF, 0xFF, 0xC0, /* 'I' 0x49 */
- 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC1, 0x83, 0x07, 0x8F, 0x1E, 0x27, 0x80, /* 'J' 0x4A */
- 0xC0, 0xF0, 0x6C, 0x33, 0x18, 0xCC, 0x37, 0x0F, 0xC3, 0x98, 0xC3, 0x30, 0xCC, 0x1B, 0x03, 0xC0, 0xC0, /* 'K' 0x4B */
- 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xFF, /* 'L' 0x4C */
- 0xE0, 0x3F, 0x01, 0xFC, 0x1F, 0xE0, 0xFD, 0x05, 0xEC, 0x6F, 0x63, 0x79, 0x13, 0xCD, 0x9E, 0x6C, 0xF1, 0x47, 0x8E, 0x3C, 0x71,
- 0x80, /* 'M' 0x4D */
- 0xE0, 0x7C, 0x0F, 0xC1, 0xE8, 0x3D, 0x87, 0x98, 0xF1, 0x1E, 0x33, 0xC3, 0x78, 0x6F, 0x07, 0xE0, 0x7C, 0x0E, /* 'N' 0x4E */
- 0x0F, 0x81, 0x83, 0x18, 0x0C, 0xC0, 0x6C, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x03, 0xC0, 0x1B, 0x01, 0x98, 0x0C, 0x60, 0xC0, 0xF8,
- 0x00, /* 'O' 0x4F */
- 0xFF, 0x30, 0x6C, 0x0F, 0x03, 0xC0, 0xF0, 0x6F, 0xF3, 0x00, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x00, /* 'P' 0x50 */
- 0x0F, 0x81, 0x83, 0x18, 0x0C, 0xC0, 0x6C, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x03, 0xC0, 0x1B, 0x01, 0x98, 0x6C, 0x60, 0xC0, 0xFB,
- 0x00, 0x08, /* 'Q' 0x51 */
- 0xFF, 0x8C, 0x0E, 0xC0, 0x6C, 0x06, 0xC0, 0x6C, 0x0C, 0xFF, 0x8C, 0x0E, 0xC0, 0x6C, 0x06, 0xC0, 0x6C, 0x06, 0xC0,
- 0x70, /* 'R' 0x52 */
- 0x3F, 0x18, 0x6C, 0x0F, 0x03, 0xC0, 0x1E, 0x01, 0xF0, 0x0E, 0x00, 0xF0, 0x3C, 0x0D, 0x86, 0x3F, 0x00, /* 'S' 0x53 */
- 0xFF, 0x86, 0x03, 0x01, 0x80, 0xC0, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x01, 0x80, 0xC0, /* 'T' 0x54 */
- 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0x78, 0x0F, 0x01, 0xB0, 0x61, 0xF0, /* 'U' 0x55 */
- 0xC0, 0x6C, 0x0D, 0x81, 0x10, 0x63, 0x0C, 0x61, 0x04, 0x60, 0xCC, 0x19, 0x01, 0x60, 0x3C, 0x07, 0x00, 0x60, /* 'V' 0x56 */
- 0xC1, 0x81, 0x61, 0xC3, 0x61, 0xC3, 0x61, 0x43, 0x62, 0x62, 0x22, 0x66, 0x32, 0x26, 0x36, 0x26, 0x14, 0x34, 0x14, 0x34, 0x1C,
- 0x1C, 0x18, 0x1C, 0x08, 0x18, /* 'W' 0x57 */
- 0xC0, 0xD8, 0x66, 0x18, 0xCC, 0x1E, 0x07, 0x00, 0xC0, 0x78, 0x32, 0x0C, 0xC6, 0x1B, 0x07, 0xC0, 0xC0, /* 'X' 0x58 */
- 0xC0, 0x36, 0x06, 0x30, 0xC3, 0x0C, 0x19, 0x81, 0xD8, 0x0F, 0x00, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60, 0x06,
- 0x00, /* 'Y' 0x59 */
- 0xFF, 0xC0, 0x60, 0x30, 0x0C, 0x06, 0x03, 0x01, 0xC0, 0x60, 0x30, 0x18, 0x06, 0x03, 0x00, 0xFF, 0xC0, /* 'Z' 0x5A */
- 0xFB, 0x6D, 0xB6, 0xDB, 0x6D, 0xB6, 0xE0, /* '[' 0x5B */
- 0x84, 0x10, 0x84, 0x10, 0x84, 0x10, 0x84, 0x10, 0x80, /* '\' 0x5C */
- 0xED, 0xB6, 0xDB, 0x6D, 0xB6, 0xDB, 0xE0, /* ']' 0x5D */
- 0x30, 0x60, 0xA2, 0x44, 0xD8, 0xA1, 0x80, /* '^' 0x5E */
- 0xFF, 0xC0, /* '_' 0x5F */
- 0xC6, 0x30, /* '`' 0x60 */
- 0x7E, 0x71, 0xB0, 0xC0, 0x60, 0xF3, 0xDB, 0x0D, 0x86, 0xC7, 0x3D, 0xC0, /* 'a' 0x61 */
- 0xC0, 0x60, 0x30, 0x1B, 0xCE, 0x36, 0x0F, 0x07, 0x83, 0xC1, 0xE0, 0xF0, 0x7C, 0x6D, 0xE0, /* 'b' 0x62 */
- 0x3C, 0x66, 0xC3, 0xC0, 0xC0, 0xC0, 0xC0, 0xC3, 0x66, 0x3C, /* 'c' 0x63 */
- 0x03, 0x03, 0x03, 0x3B, 0x67, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x67, 0x3B, /* 'd' 0x64 */
- 0x3C, 0x66, 0xC3, 0xC3, 0xFF, 0xC0, 0xC0, 0xC3, 0x66, 0x3C, /* 'e' 0x65 */
- 0x36, 0x6F, 0x66, 0x66, 0x66, 0x66, 0x60, /* 'f' 0x66 */
- 0x3B, 0x67, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x67, 0x3B, 0x03, 0x03, 0xC6, 0x7C, /* 'g' 0x67 */
- 0xC0, 0xC0, 0xC0, 0xDE, 0xE3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, /* 'h' 0x68 */
- 0xC3, 0xFF, 0xFF, 0xC0, /* 'i' 0x69 */
- 0x30, 0x03, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0xE0, /* 'j' 0x6A */
- 0xC0, 0xC0, 0xC0, 0xC2, 0xC4, 0xCC, 0xD8, 0xF8, 0xEC, 0xC4, 0xC6, 0xC3, 0xC3, /* 'k' 0x6B */
- 0xFF, 0xFF, 0xFF, 0xC0, /* 'l' 0x6C */
- 0xDE, 0xF7, 0x1C, 0xF0, 0xC7, 0x86, 0x3C, 0x31, 0xE1, 0x8F, 0x0C, 0x78, 0x63, 0xC3, 0x1E, 0x18, 0xC0, /* 'm' 0x6D */
- 0xDE, 0xE3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, /* 'n' 0x6E */
- 0x3C, 0x66, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x66, 0x3C, /* 'o' 0x6F */
- 0xDE, 0x71, 0xB0, 0x78, 0x3C, 0x1E, 0x0F, 0x07, 0x83, 0xE3, 0x6F, 0x30, 0x18, 0x0C, 0x00, /* 'p' 0x70 */
- 0x3B, 0x67, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x67, 0x3B, 0x03, 0x03, 0x03, /* 'q' 0x71 */
- 0xDF, 0x31, 0x8C, 0x63, 0x18, 0xC6, 0x00, /* 'r' 0x72 */
- 0x3E, 0xE3, 0xC0, 0xC0, 0xE0, 0x3C, 0x07, 0xC3, 0xE3, 0x7E, /* 's' 0x73 */
- 0x66, 0xF6, 0x66, 0x66, 0x66, 0x67, /* 't' 0x74 */
- 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC7, 0x7B, /* 'u' 0x75 */
- 0xC1, 0xA0, 0x98, 0xCC, 0x42, 0x21, 0xB0, 0xD0, 0x28, 0x1C, 0x0C, 0x00, /* 'v' 0x76 */
- 0xC6, 0x1E, 0x38, 0x91, 0xC4, 0xCA, 0x66, 0xD3, 0x16, 0xD0, 0xA6, 0x87, 0x1C, 0x38, 0xC0, 0xC6, 0x00, /* 'w' 0x77 */
- 0x87, 0x89, 0xB1, 0xC3, 0x07, 0x1E, 0x26, 0xC5, 0x0C, /* 'x' 0x78 */
- 0xC1, 0x43, 0x63, 0x62, 0x26, 0x36, 0x34, 0x1C, 0x1C, 0x18, 0x18, 0x18, 0x10, 0x60, /* 'y' 0x79 */
- 0xFE, 0x0C, 0x30, 0xC1, 0x86, 0x18, 0x20, 0xC1, 0xFC, /* 'z' 0x7A */
- 0x36, 0x66, 0x66, 0x6E, 0xCE, 0x66, 0x66, 0x66, 0x30, /* '{' 0x7B */
- 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, /* '|' 0x7C */
- 0xC6, 0x66, 0x66, 0x67, 0x37, 0x66, 0x66, 0x66, 0xC0, /* '}' 0x7D */
- 0x61, 0x24, 0x38, /* '~' 0x7E */
- 0xFF, 0xFC, 0x00, 0x63, 0xE3, 0x31, 0x99, 0x04, 0xC8, 0x66, 0x06, 0x30, 0x61, 0x83, 0x0C, 0x18, 0x60, 0x03, 0x06, 0x18, 0x00,
- 0xFF, 0xFC, /* 0x7F */
- 0x07, 0xC6, 0x13, 0x00, 0xC0, 0x60, 0x3F, 0xE6, 0x03, 0xFC, 0x60, 0x0C, 0x03, 0x00, 0x61, 0x07, 0xC0, /* 0x80 */
- /* 0x81 */
- 0xDC, /* 0x82 */
- 0x19, 0x8C, 0xF3, 0x18, 0xC6, 0x31, 0x8C, 0x63, 0x18, 0xC6, 0xE0, /* 0x83 */
- 0xDA, 0x76, /* 0x84 */
- 0xCC, 0xC0, /* 0x85 */
- 0x18, 0x18, 0x18, 0x18, 0xFF, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, /* 0x86 */
- 0x18, 0x18, 0x18, 0x18, 0xFF, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xFF, 0x18, 0x18, 0x18, 0x18, /* 0x87 */
- 0x72, 0xA2, /* 0x88 */
- 0x70, 0x80, 0x22, 0x20, 0x08, 0x90, 0x02, 0x24, 0x00, 0x72, 0x00, 0x00, 0x80, 0x00, 0x40, 0x00, 0x10, 0x00, 0x09, 0xC7, 0x84,
- 0x8B, 0x31, 0x22, 0x84, 0x88, 0xB3, 0x21, 0xC7, 0x80, /* 0x89 */
- 0x1B, 0x03, 0x80, 0x00, 0xFC, 0x61, 0xB0, 0x3C, 0x0F, 0x00, 0x78, 0x07, 0xC0, 0x38, 0x03, 0xC0, 0xF0, 0x36, 0x18,
- 0xFC, /* 0x8A */
- 0x69, /* 0x8B */
- 0x1E, 0xFE, 0x43, 0x81, 0x83, 0x06, 0x06, 0x0C, 0x0C, 0x18, 0x18, 0x30, 0x3F, 0xE0, 0x60, 0xC0, 0xC1, 0x81, 0x81, 0x83, 0x01,
- 0x8E, 0x01, 0xEF, 0xE0, /* 0x8C */
- /* 0x8D */
- 0x1B, 0x03, 0x80, 0x03, 0xFF, 0x01, 0x80, 0xC0, 0x30, 0x18, 0x0C, 0x07, 0x01, 0x80, 0xC0, 0x60, 0x18, 0x0C, 0x03,
- 0xFF, /* 0x8E */
- /* 0x8F */
- /* 0x90 */
- 0x6B, /* 0x91 */
- 0xD6, /* 0x92 */
- 0x4C, 0xA5, 0xB0, /* 0x93 */
- 0xDA, 0x53, 0x20, /* 0x94 */
- 0x6F, 0xFF, 0x60, /* 0x95 */
- 0xFE, /* 0x96 */
- 0xFF, 0xFF, /* 0x97 */
- 0x4D, 0xC0, /* 0x98 */
- 0xFC, 0xE1, 0xCC, 0x38, 0x73, 0x0E, 0x1C, 0xC3, 0x8F, 0x30, 0xD2, 0xCC, 0x34, 0xB3, 0x0D, 0x6C, 0xC3, 0x53, 0x30, 0xCC, 0xCC,
- 0x33, 0x30, /* 0x99 */
- 0x24, 0x3C, 0x18, 0x7E, 0xE3, 0xC0, 0xC0, 0x60, 0x3C, 0x07, 0xC3, 0xE3, 0x7E, /* 0x9A */
- 0x96, /* 0x9B */
- 0x3C, 0xF8, 0xCF, 0x1B, 0x0C, 0x1E, 0x18, 0x3C, 0x3F, 0xF8, 0x60, 0x30, 0xC0, 0x61, 0x83, 0x67, 0x8C, 0x79, 0xF0, /* 0x9C */
- /* 0x9D */
- 0x48, 0xF0, 0xC7, 0xF0, 0x61, 0x86, 0x0C, 0x30, 0xC1, 0x06, 0x0F, 0xE0, /* 0x9E */
- 0x19, 0x80, 0x00, 0xC0, 0x36, 0x06, 0x30, 0xC3, 0x0C, 0x19, 0x81, 0xD8, 0x0F, 0x00, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00,
- 0x60, /* 0x9F */
- /* 0xA0 */
- 0xCF, 0xFF, 0xFF, 0xC0, /* 0xA1 */
- 0x08, 0x04, 0x0F, 0x8D, 0x6C, 0x9E, 0x43, 0x21, 0x90, 0xC8, 0x64, 0xDA, 0xC7, 0xC0, 0x80, 0x40, /* 0xA2 */
- 0x1F, 0x0C, 0x66, 0x0D, 0x83, 0x60, 0x0C, 0x0F, 0xC0, 0x60, 0x18, 0x06, 0x03, 0x01, 0xF1, 0x43, 0xC0, /* 0xA3 */
- 0xFF, 0xDF, 0x1E, 0x3E, 0xFF, 0xC0, /* 0xA4 */
- 0xC3, 0x42, 0x42, 0x24, 0x24, 0x3C, 0x18, 0x7E, 0x18, 0x7E, 0x18, 0x18, 0x18, /* 0xA5 */
- 0xFF, 0xFC, 0x0F, 0xFF, 0xC0, /* 0xA6 */
- 0x0C, 0x09, 0x0C, 0xC6, 0x63, 0x81, 0xE3, 0x19, 0x87, 0xE1, 0xB8, 0xC6, 0x41, 0xC0, 0x73, 0x19, 0x8C, 0x66, 0x1E,
- 0x00, /* 0xA7 */
- 0xCC, /* 0xA8 */
- 0x0F, 0xC0, 0x61, 0x87, 0x03, 0x9B, 0xC6, 0xD9, 0x8F, 0x60, 0x3D, 0x00, 0xF4, 0x03, 0xD8, 0x0D, 0xE6, 0x67, 0xF3, 0x86, 0x18,
- 0x0F, 0xC0, /* 0xA9 */
- 0x74, 0x8D, 0xA9, 0x7C, 0x1F, /* 0xAA */
- 0x22, 0xCF, 0x26, 0x46, 0x64, 0x40, /* 0xAB */
- 0xFF, 0x80, 0xC0, 0x60, 0x30, 0x18, /* 0xAC */
- /* 0xAD */
- 0x0F, 0xC0, 0x61, 0x87, 0x03, 0x9F, 0xE6, 0xD0, 0x8F, 0x42, 0x3D, 0xF0, 0xF4, 0x23, 0xD0, 0x8D, 0xC2, 0x67, 0x0B, 0x86, 0x18,
- 0x0F, 0xC0, /* 0xAE */
- 0xF8, /* 0xAF */
- 0x74, 0x63, 0x17, 0x00, /* 0xB0 */
- 0x0C, 0x06, 0x03, 0x07, 0xE0, 0xC0, 0x60, 0x30, 0x18, 0x00, 0x00, 0x3F, 0xE0, /* 0xB1 */
- 0x7B, 0x30, 0xC3, 0x11, 0x84, 0x3F, /* 0xB2 */
- 0x7D, 0x8C, 0x18, 0xC0, 0x60, 0xF1, 0xBE, /* 0xB3 */
- 0x36, 0xC0, /* 0xB4 */
- 0xC3, 0x61, 0xB0, 0xD8, 0x6C, 0x36, 0x1B, 0x0D, 0x86, 0xE7, 0x7D, 0xF0, 0x18, 0x0C, 0x00, /* 0xB5 */
- 0x3F, 0x7E, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0x72, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, /* 0xB6 */
- 0xE0, /* 0xB7 */
- 0x21, 0xC7, 0xE0, /* 0xB8 */
- 0x3D, 0xB6, 0xD8, /* 0xB9 */
- 0x74, 0x63, 0x18, 0xB8, 0x1F, /* 0xBA */
- 0x89, 0x98, 0x99, 0x3C, 0xD1, 0x00, /* 0xBB */
- 0x20, 0x43, 0x81, 0x06, 0x08, 0x18, 0x20, 0x61, 0x01, 0x84, 0x06, 0x21, 0x80, 0x86, 0x04, 0x78, 0x32, 0x60, 0x87, 0xC4, 0x06,
- 0x10, 0x18, /* 0xBC */
- 0x20, 0x43, 0x81, 0x06, 0x08, 0x18, 0x20, 0x61, 0x01, 0x8D, 0xE6, 0x2C, 0xC1, 0x03, 0x0C, 0x0C, 0x20, 0x41, 0x86, 0x0C, 0x30,
- 0x20, 0xFC, /* 0xBD */
- 0x78, 0x11, 0x98, 0x40, 0x31, 0x00, 0x82, 0x00, 0xC8, 0x01, 0x90, 0x33, 0x43, 0x3D, 0x06, 0x02, 0x3C, 0x08, 0x98, 0x10, 0xF8,
- 0x40, 0x61, 0x00, 0xC0, /* 0xBE */
- 0x0C, 0x00, 0x00, 0x01, 0x80, 0xC0, 0xC0, 0xE0, 0xC0, 0xC0, 0x60, 0xF0, 0x6C, 0x63, 0xE0, /* 0xBF */
- 0x18, 0x03, 0x00, 0x00, 0x30, 0x1E, 0x07, 0x81, 0x20, 0xCC, 0x33, 0x0F, 0xC6, 0x19, 0x86, 0x40, 0xB0, 0x30, /* 0xC0 */
- 0x06, 0x03, 0x00, 0x00, 0x30, 0x1E, 0x07, 0x81, 0x20, 0xCC, 0x33, 0x0F, 0xC6, 0x19, 0x86, 0x40, 0xB0, 0x30, /* 0xC1 */
- 0x0C, 0x04, 0x80, 0x00, 0x30, 0x1E, 0x07, 0x81, 0x20, 0xCC, 0x33, 0x0F, 0xC6, 0x19, 0x86, 0x40, 0xB0, 0x30, /* 0xC2 */
- 0x19, 0x09, 0x80, 0x00, 0x30, 0x1E, 0x07, 0x81, 0x20, 0xCC, 0x33, 0x0F, 0xC6, 0x19, 0x86, 0x40, 0xB0, 0x30, /* 0xC3 */
- 0x33, 0x00, 0x00, 0xC0, 0x78, 0x1E, 0x04, 0x83, 0x30, 0xCC, 0x33, 0x1F, 0xE6, 0x19, 0x02, 0xC0, 0xF0, 0x30, /* 0xC4 */
- 0x0C, 0x04, 0x81, 0x20, 0x30, 0x1E, 0x07, 0x81, 0x20, 0xCC, 0x33, 0x0F, 0xC6, 0x19, 0x86, 0x40, 0xB0, 0x30, /* 0xC5 */
- 0x07, 0xFF, 0x04, 0xC0, 0x0C, 0xC0, 0x08, 0xC0, 0x18, 0xC0, 0x18, 0xC0, 0x30, 0xFF, 0x30, 0xC0, 0x3F, 0xC0, 0x60, 0xC0, 0x60,
- 0xC0, 0xC0, 0xC0, 0xC0, 0xFF, /* 0xC6 */
- 0x1F, 0x06, 0x19, 0x83, 0xA0, 0x3C, 0x01, 0x80, 0x30, 0x06, 0x00, 0xC0, 0x68, 0x0D, 0x83, 0x18, 0xE1, 0xF0, 0x08, 0x01, 0xC0,
- 0x18, 0x0E, 0x00, /* 0xC7 */
- 0x18, 0x06, 0x00, 0x1F, 0xFC, 0x06, 0x03, 0x01, 0x80, 0xFF, 0x60, 0x30, 0x18, 0x0C, 0x07, 0xFC, /* 0xC8 */
- 0x0C, 0x0C, 0x00, 0x1F, 0xFC, 0x06, 0x03, 0x01, 0x80, 0xFF, 0x60, 0x30, 0x18, 0x0C, 0x07, 0xFC, /* 0xC9 */
- 0x1C, 0x1B, 0x00, 0x1F, 0xFC, 0x06, 0x03, 0x01, 0x80, 0xFF, 0x60, 0x30, 0x18, 0x0C, 0x07, 0xFC, /* 0xCA */
- 0x33, 0x00, 0x3F, 0xF8, 0x0C, 0x06, 0x03, 0x01, 0xFE, 0xC0, 0x60, 0x30, 0x18, 0x0C, 0x07, 0xFC, /* 0xCB */
- 0xCC, 0x36, 0xDB, 0x6D, 0xB6, 0xD8, /* 0xCC */
- 0x78, 0x36, 0xDB, 0x6D, 0xB6, 0xC0, /* 0xCD */
- 0x76, 0xC0, 0x63, 0x18, 0xC6, 0x31, 0x8C, 0x63, 0x18, /* 0xCE */
- 0xCC, 0x03, 0x0C, 0x30, 0xC3, 0x0C, 0x30, 0xC3, 0x0C, 0x30, 0xC0, /* 0xCF */
- 0x7F, 0x0C, 0x31, 0x83, 0x30, 0x36, 0x06, 0xC0, 0xFE, 0x1B, 0x03, 0x60, 0x6C, 0x0D, 0x83, 0x30, 0xE7, 0xF0, /* 0xD0 */
- 0x19, 0x02, 0xC3, 0x81, 0xF0, 0x3F, 0x07, 0xA0, 0xF6, 0x1E, 0x63, 0xC4, 0x78, 0xCF, 0x0D, 0xE1, 0xBC, 0x1F, 0x81,
- 0xC0, /* 0xD1 */
- 0x0C, 0x00, 0x60, 0x00, 0x00, 0xF0, 0x39, 0xC6, 0x06, 0x60, 0x6C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, 0x36, 0x06, 0x60, 0x63, 0x9C,
- 0x0F, 0x00, /* 0xD2 */
- 0x03, 0x00, 0x60, 0x00, 0x00, 0xF0, 0x39, 0xC6, 0x06, 0x60, 0x6C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, 0x36, 0x06, 0x60, 0x63, 0x9C,
- 0x0F, 0x00, /* 0xD3 */
- 0x0F, 0x01, 0x98, 0x00, 0x00, 0xF0, 0x39, 0xC6, 0x06, 0x60, 0x6C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, 0x36, 0x06, 0x60, 0x63, 0x9C,
- 0x0F, 0x00, /* 0xD4 */
- 0x1C, 0x81, 0x38, 0x00, 0x00, 0xF0, 0x39, 0xC6, 0x06, 0x60, 0x6C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, 0x36, 0x06, 0x60, 0x63, 0x9C,
- 0x0F, 0x00, /* 0xD5 */
- 0x19, 0x81, 0x98, 0x00, 0x00, 0xF0, 0x39, 0xC6, 0x06, 0x60, 0x6C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, 0x36, 0x06, 0x60, 0x63, 0x9C,
- 0x0F, 0x00, /* 0xD6 */
- 0x83, 0x89, 0xA1, 0x83, 0x89, 0xA1, 0x80, /* 0xD7 */
- 0x0F, 0xD9, 0x83, 0x18, 0x1C, 0xC1, 0xEC, 0x19, 0xE0, 0x8F, 0x08, 0x78, 0x83, 0xC8, 0x1B, 0x81, 0x98, 0x0C, 0xE0, 0xC8, 0xF8,
- 0x00, /* 0xD8 */
- 0x0C, 0x00, 0xC3, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x36, 0x0C, 0x3E,
- 0x00, /* 0xD9 */
- 0x06, 0x01, 0x83, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x36, 0x0C, 0x3E,
- 0x00, /* 0xDA */
- 0x0E, 0x03, 0x63, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x36, 0x0C, 0x3E,
- 0x00, /* 0xDB */
- 0x1B, 0x00, 0x03, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x36, 0x0C, 0x3E,
- 0x00, /* 0xDC */
- 0x03, 0x0C, 0x63, 0x60, 0x63, 0x0C, 0x30, 0xC1, 0x98, 0x1D, 0x80, 0xF0, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00,
- 0x60, /* 0xDD */
- 0xC0, 0x30, 0x0F, 0xF3, 0x06, 0xC0, 0xF0, 0x3C, 0x0F, 0x06, 0xFF, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x00, /* 0xDE */
- 0x3C, 0x33, 0x30, 0xD8, 0x6C, 0x36, 0x33, 0x39, 0x86, 0xC1, 0xE0, 0xF0, 0x78, 0x6D, 0xE0, /* 0xDF */
- 0x60, 0x18, 0x06, 0x0F, 0xCE, 0x36, 0x18, 0x0C, 0x1E, 0x7B, 0x61, 0xB0, 0xD8, 0xE7, 0xB8, /* 0xE0 */
- 0x0C, 0x04, 0x04, 0x0F, 0xCE, 0x36, 0x18, 0x0C, 0x1E, 0x7B, 0x61, 0xB0, 0xD8, 0xE7, 0xB8, /* 0xE1 */
- 0x10, 0x14, 0x1B, 0x0F, 0xCE, 0x36, 0x18, 0x0C, 0x1E, 0x7B, 0x61, 0xB0, 0xD8, 0xE7, 0xB8, /* 0xE2 */
- 0x24, 0x2E, 0x00, 0x0F, 0xCE, 0x36, 0x18, 0x0C, 0x1E, 0x7B, 0x61, 0xB0, 0xD8, 0xE7, 0xB8, /* 0xE3 */
- 0x66, 0x00, 0x1F, 0x9C, 0x6C, 0x30, 0x18, 0x3C, 0xF6, 0xC3, 0x61, 0xB1, 0xCF, 0x70, /* 0xE4 */
- 0x1C, 0x1B, 0x0D, 0x83, 0x87, 0xE7, 0x1B, 0x0C, 0x06, 0x0F, 0x3D, 0xB0, 0xD8, 0x6C, 0x73, 0xDC, /* 0xE5 */
- 0x7E, 0xF9, 0xC7, 0x1B, 0x0C, 0x18, 0x18, 0x33, 0xFF, 0xFC, 0x60, 0x30, 0xC0, 0x61, 0x83, 0xC7, 0x8C, 0xF1, 0xF0, /* 0xE6 */
- 0x3C, 0x66, 0xC3, 0xC0, 0xC0, 0xC0, 0xC0, 0xC3, 0x66, 0x3C, 0x10, 0x1C, 0x0C, 0x38, /* 0xE7 */
- 0x60, 0x30, 0x18, 0x3C, 0x66, 0xC3, 0xC3, 0xFF, 0xC0, 0xC0, 0xC3, 0x66, 0x3C, /* 0xE8 */
- 0x0C, 0x08, 0x18, 0x3C, 0x66, 0xC3, 0xC3, 0xFF, 0xC0, 0xC0, 0xC3, 0x66, 0x3C, /* 0xE9 */
- 0x10, 0x28, 0x6C, 0x3C, 0x66, 0xC3, 0xC3, 0xFF, 0xC0, 0xC0, 0xC3, 0x66, 0x3C, /* 0xEA */
- 0x66, 0x00, 0x3C, 0x66, 0xC3, 0xC3, 0xFF, 0xC0, 0xC0, 0xC3, 0x66, 0x3C, /* 0xEB */
- 0xCC, 0xB6, 0xDB, 0x6D, 0xB6, /* 0xEC */
- 0x7A, 0x6D, 0xB6, 0xDB, 0x6C, /* 0xED */
- 0x6E, 0x96, 0x66, 0x66, 0x66, 0x66, 0x60, /* 0xEE */
- 0xCC, 0x03, 0x0C, 0x30, 0xC3, 0x0C, 0x30, 0xC3, 0x0C, /* 0xEF */
- 0x34, 0x0C, 0x16, 0x03, 0x3F, 0x67, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x66, 0x3C, /* 0xF0 */
- 0x24, 0x5C, 0x00, 0xDE, 0xE3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, /* 0xF1 */
- 0x30, 0x18, 0x00, 0x3C, 0x66, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x66, 0x3C, /* 0xF2 */
- 0x0C, 0x18, 0x00, 0x3C, 0x66, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x66, 0x3C, /* 0xF3 */
- 0x18, 0x24, 0x00, 0x3C, 0x66, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x66, 0x3C, /* 0xF4 */
- 0x34, 0x2C, 0x00, 0x3C, 0x66, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x66, 0x3C, /* 0xF5 */
- 0x66, 0x00, 0x3C, 0x66, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x66, 0x3C, /* 0xF6 */
- 0x18, 0x00, 0x00, 0x1F, 0xF0, 0x00, 0x00, 0x00, 0x30, /* 0xF7 */
- 0x3D, 0x66, 0xC7, 0xCB, 0xCB, 0xD3, 0xD3, 0xE3, 0x66, 0xBC, /* 0xF8 */
- 0x60, 0x30, 0x18, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC7, 0x7B, /* 0xF9 */
- 0x06, 0x0C, 0x18, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC7, 0x7B, /* 0xFA */
- 0x3C, 0x66, 0x00, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC7, 0x7B, /* 0xFB */
- 0x66, 0x00, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC7, 0x7B, /* 0xFC */
- 0x06, 0x04, 0x08, 0xC1, 0x43, 0x63, 0x62, 0x26, 0x36, 0x34, 0x1C, 0x1C, 0x18, 0x18, 0x18, 0x10, 0x60, /* 0xFD */
- 0xC0, 0x60, 0x30, 0x1B, 0xCE, 0x36, 0x0F, 0x07, 0x83, 0xC1, 0xE0, 0xF0, 0x7C, 0x6D, 0xE6, 0x03, 0x01, 0x80, /* 0xFE */
- 0x33, 0x00, 0xC1, 0x43, 0x63, 0x62, 0x26, 0x36, 0x34, 0x1C, 0x1C, 0x18, 0x18, 0x18, 0x10, 0x60, /* 0xFF */
+/* 0x01 */ 0x07, 0x00, 0x0A, 0x00, 0x24, 0x00, 0x48, 0x01, 0x10, 0x04, 0x40, 0x10, 0xFF, 0x20, 0x02, 0x81, 0xFD, 0x00, 0x06, 0x07, 0xF4, 0x08, 0x24, 0x0F, 0x88, 0x11, 0x0F, 0xDC, 0x00,
+/* 0x02 */ 0x3F, 0x70, 0x81, 0x11, 0x03, 0xE4, 0x08, 0x28, 0x1F, 0xD0, 0x00, 0x60, 0x7F, 0x20, 0x02, 0x43, 0xFC, 0x44, 0x00, 0x44, 0x00, 0x48, 0x00, 0x90, 0x00, 0xA0, 0x01, 0xC0, 0x00,
+/* 0x03 */ 0x07, 0xC0, 0x30, 0x60, 0x80, 0x22, 0x00, 0x28, 0x00, 0x31, 0x8C, 0x63, 0x18, 0xC0, 0x01, 0x80, 0x03, 0x00, 0x06, 0x20, 0x8C, 0x3E, 0x14, 0x00, 0x44, 0x01, 0x06, 0x0C, 0x03, 0xE0,
+/* 0x04 */ 0x07, 0xC0, 0x30, 0x60, 0x80, 0x22, 0x00, 0x28, 0x82, 0x30, 0x88, 0x62, 0x08, 0xC0, 0x01, 0x80, 0x03, 0x00, 0x06, 0x3F, 0x8C, 0x3E, 0x14, 0x00, 0x44, 0x01, 0x06, 0x0C, 0x03, 0xE0,
+/* 0x05 */ 0x0B, 0x10, 0x14, 0xA8, 0x12, 0x50, 0x29, 0x42, 0x24, 0xA5, 0x32, 0x95, 0x5A, 0x09, 0x48, 0x09, 0x24, 0x01, 0x10, 0x01, 0x48, 0x02, 0xA4, 0x02, 0x42, 0x04, 0x01, 0x98, 0x00, 0x60,
+/* 0x06 */ 0x00, 0x80, 0x22, 0x80, 0x65, 0x00, 0xBE, 0xE1, 0x82, 0x4E, 0x03, 0x24, 0x04, 0x28, 0x06, 0x30, 0x12, 0x20, 0x3C, 0xA0, 0xC3, 0xFE, 0x80, 0x4D, 0x00, 0xA6, 0x01, 0x80, 0x00,
+/* 0x07 */
+/* 0x08 */ 0x00, 0xF8, 0x00, 0x82, 0x00, 0x80, 0x83, 0xE0, 0x41, 0x10, 0x21, 0x04, 0x1B, 0x00, 0x03, 0x00, 0x01, 0x80, 0x00, 0xE0, 0x00, 0x4F, 0xE1, 0xC0, 0x0F, 0x02, 0x00, 0x03, 0x01, 0x00, 0x09, 0x88, 0x0C, 0x0C,
+/* 0x09 */ 0x00, 0xF8, 0x00, 0x82, 0x00, 0x80, 0x83, 0xE0, 0x41, 0x10, 0x21, 0x04, 0x1B, 0x00, 0x03, 0x00, 0x01, 0x80, 0x00, 0xE0, 0x00, 0x4F, 0xE1, 0xC0, 0x0F, 0x00,
+/* 0x0A */
+/* 0x0B */ 0x1C, 0x1C, 0x31, 0xB1, 0x90, 0x50, 0x50, 0x10, 0x18, 0x00, 0x0C, 0x00, 0x06, 0x00, 0x02, 0x80, 0x02, 0x40, 0x01, 0x10, 0x01, 0x04, 0x01, 0x01, 0x01, 0x00, 0x41, 0x00, 0x11, 0x00, 0x07, 0x00, 0x01, 0x00,
+/* 0x0C */ 0x06, 0x00, 0x0A, 0x00, 0x12, 0x00, 0x32, 0x01, 0x84, 0x04, 0x10, 0x08, 0x98, 0x1C, 0x18, 0x40, 0x48, 0x82, 0x11, 0xF0, 0x74, 0x02, 0x18, 0x70, 0x2F, 0x9F, 0x80,
+/* 0x0D */
+/* 0x0E */ 0x01, 0x00, 0x05, 0x00, 0x0A, 0x00, 0x3E, 0x00, 0x82, 0x02, 0x82, 0x06, 0x04, 0x10, 0x04, 0x20, 0x08, 0x40, 0x10, 0xFF, 0x22, 0x00, 0x29, 0xFF, 0x3F, 0x8F, 0xDF, 0x9F, 0x01, 0xC0,
+/* 0x0F */ 0x07, 0xC0, 0x30, 0x60, 0x80, 0x22, 0x00, 0x28, 0x82, 0x36, 0x03, 0x60, 0x00, 0xCC, 0x19, 0xA4, 0x4B, 0x00, 0x06, 0x8E, 0x2B, 0x22, 0x66, 0x7C, 0xCC, 0x71, 0x98, 0x03, 0x00,
+/* 0x10 */ 0x03, 0x80, 0x07, 0x00, 0x0E, 0x00, 0x1E, 0x00, 0x54, 0x00, 0xA8, 0x01, 0x50, 0x02, 0xA0, 0x05, 0x20, 0x32, 0x61, 0xC4, 0x74, 0x49, 0x10, 0x6C, 0x00, 0xD8, 0x01, 0x10, 0x00,
+/* 0x11 */ 0x07, 0xC0, 0x30, 0x60, 0x80, 0x22, 0x40, 0x29, 0x00, 0x31, 0x84, 0x63, 0x18, 0xC0, 0x00, 0x80, 0x15, 0x03, 0x7E, 0x02, 0xFA, 0x04, 0xE4, 0x18, 0x84, 0x00, 0x06, 0x0C, 0x03, 0xE0,
+/* 0x12 */ 0x02, 0x08, 0x01, 0x08, 0x40, 0x10, 0xC0, 0x08, 0xC0, 0x60, 0x80, 0x28, 0x04, 0x12, 0x4C, 0x10, 0x80, 0x08, 0x23, 0x0E, 0x08, 0xC4, 0x82, 0x04, 0x20, 0x83, 0x09, 0x82, 0x47, 0x01, 0x1C, 0x01, 0x30, 0x00, 0xE0, 0x00, 0x00,
+/* 0x13 */ 0x07, 0xC0, 0x30, 0x60, 0x80, 0x22, 0x00, 0x28, 0x00, 0x31, 0x08, 0x65, 0x28, 0xC0, 0x01, 0x80, 0x03, 0x00, 0x06, 0x3F, 0x8C, 0x3E, 0x14, 0x00, 0x44, 0x01, 0x06, 0x0C, 0x03, 0xE0,
+/* 0x14 */ 0x07, 0xC0, 0x30, 0x60, 0x80, 0x22, 0x22, 0x29, 0x83, 0x30, 0x00, 0x65, 0x14, 0xD3, 0x4D, 0xBA, 0xEB, 0x38, 0xE6, 0x00, 0x0A, 0x00, 0x24, 0x38, 0x44, 0x01, 0x07, 0x1C, 0x01, 0xC0,
+/* 0x15 */ 0x07, 0xC0, 0x30, 0x18, 0x80, 0x32, 0x00, 0xF8, 0x01, 0xF1, 0x09, 0xA5, 0x28, 0x40, 0x01, 0x80, 0x03, 0x00, 0x06, 0x3F, 0x8C, 0x3E, 0x14, 0x00, 0x44, 0x01, 0x06, 0x0C, 0x03, 0xE0,
+/* 0x16 */ 0x0C, 0x00, 0xC0, 0x1C, 0x03, 0x80, 0xF8, 0xBB, 0x36, 0xC7, 0x99, 0xF3, 0xFE, 0x3F, 0xC3, 0xF0, 0x7E, 0x0E, 0xC1, 0x8E, 0xE0, 0x20,
+/* 0x17 */ 0x07, 0xC0, 0x30, 0x60, 0x80, 0x22, 0x00, 0x28, 0x00, 0x10, 0x01, 0x20, 0x1D, 0x44, 0x42, 0x84, 0x85, 0x00, 0x86, 0x00, 0xC4, 0x00, 0x44, 0x7C, 0x44, 0x00, 0x06, 0x0C, 0x03, 0xE0,
+/* 0x18 */ 0x01, 0xE0, 0x00, 0x84, 0x00, 0x40, 0x80, 0x20, 0x10, 0x08, 0x24, 0x02, 0x41, 0x00, 0x86, 0x03, 0x12, 0x03, 0xB4, 0x03, 0x52, 0x81, 0x23, 0x80, 0x70, 0xA0, 0x14, 0x28, 0x05, 0x0A, 0x01, 0x42, 0x80, 0x50,
+/* 0x19 */ 0x07, 0xC0, 0x30, 0x60, 0x80, 0x22, 0x00, 0x28, 0x00, 0x33, 0x18, 0x60, 0x00, 0xDC, 0xE1, 0xB9, 0xC3, 0x7B, 0xC6, 0x63, 0x0A, 0x00, 0x24, 0xF0, 0x44, 0x01, 0x06, 0x0C, 0x03, 0xE0,
+/* 0x1A */ 0xFF, 0xFC, 0x00, 0x63, 0xE3, 0x31, 0x99, 0x04, 0xC8, 0x66, 0x06, 0x30, 0x61, 0x82, 0x0C, 0x10, 0x60, 0x03, 0x04, 0x18, 0x00, 0xFF, 0xFC,
+/* 0x1B */ 0x07, 0xF0, 0x06, 0x0C, 0x04, 0x01, 0x04, 0x00, 0x44, 0x22, 0x12, 0x2A, 0x89, 0x00, 0x04, 0x80, 0x02, 0x44, 0x11, 0x01, 0xF0, 0x04, 0x01, 0x0D, 0x01, 0x6A, 0x41, 0x2C, 0x00, 0x05, 0xC0, 0x0E, 0x18, 0x18,
+/* 0x1C */ 0x07, 0xC0, 0x30, 0x60, 0x80, 0x22, 0xC0, 0x2A, 0x00, 0x33, 0x00, 0x66, 0x00, 0xCC, 0x39, 0x80, 0x83, 0x00, 0x06, 0x00, 0x8C, 0x3E, 0x14, 0x00, 0x44, 0x01, 0x06, 0x0C, 0x03, 0xE0,
+/* 0x1D */ 0x07, 0xC0, 0x30, 0x60, 0x80, 0x22, 0x70, 0x28, 0x00, 0x31, 0x80, 0x63, 0x18, 0xC0, 0x31, 0x80, 0x03, 0x00, 0x06, 0x60, 0x0D, 0x33, 0x12, 0x10, 0x48, 0x21, 0x23, 0x8C, 0x00,
+/* 0x1E */ 0x03, 0x00, 0x07, 0x9E, 0x07, 0x00, 0x86, 0x00, 0x27, 0xC0, 0x0F, 0xC0, 0x07, 0x8C, 0x62, 0x06, 0x31, 0x20, 0x00, 0x90, 0x00, 0x48, 0x00, 0x24, 0x3E, 0x11, 0x00, 0x10, 0x40, 0x10, 0x18, 0x30, 0x03, 0xE0,
+/* 0x1F */ 0x18, 0x02, 0x80, 0x4C, 0x16, 0x41, 0x24, 0x3C, 0x88, 0x6E, 0x65, 0xF2, 0x78, 0x46, 0x88, 0xCF, 0x18, 0x02, 0x80, 0x8C, 0x60, 0x70,
+/* ' ' 0x20 */
+/* '!' 0x21 */ 0xFF, 0xFF, 0xF0, 0xC0,
+/* '"' 0x22 */ 0xDE, 0xF7, 0x20,
+/* '#' 0x23 */ 0x09, 0x86, 0x41, 0x91, 0xFF, 0x13, 0x04, 0xC3, 0x20, 0xC8, 0xFF, 0x89, 0x82, 0x61, 0x90,
+/* '$' 0x24 */ 0x10, 0x1F, 0x14, 0xDA, 0x3D, 0x1E, 0x83, 0x40, 0x78, 0x17, 0x08, 0xF4, 0x7A, 0x35, 0x33, 0xF0, 0x40, 0x20,
+/* '%' 0x25 */ 0x38, 0x10, 0xEC, 0x20, 0xC6, 0x20, 0xC6, 0x40, 0xC6, 0x40, 0x6C, 0x80, 0x39, 0x00, 0x01, 0x3C, 0x02, 0x77, 0x02, 0x63, 0x04, 0x63, 0x04, 0x77, 0x08, 0x3C,
+/* '&' 0x26 */ 0x0E, 0x0C, 0xC3, 0x30, 0xCC, 0x1E, 0x03, 0x03, 0xC1, 0x9B, 0xC2, 0xF0, 0xEC, 0x19, 0x8F, 0x3C, 0x40,
+/* ''' 0x27 */ 0xFE,
+/* '(' 0x28 */ 0x13, 0x26, 0x6C, 0xCC, 0xCC, 0xC4, 0x66, 0x23, 0x10,
+/* ')' 0x29 */ 0x8C, 0x46, 0x63, 0x33, 0x33, 0x32, 0x66, 0x4C, 0x80,
+/* '*' 0x2A */ 0x25, 0x7E, 0xA5, 0x00,
+/* '+' 0x2B */ 0x30, 0xC3, 0x3F, 0x30, 0xC3, 0x0C,
+/* ',' 0x2C */ 0xD6,
+/* '-' 0x2D */ 0xF0,
+/* '.' 0x2E */ 0xC0,
+/* '/' 0x2F */ 0x08, 0x44, 0x21, 0x10, 0x84, 0x42, 0x11, 0x08, 0x00,
+/* '0' 0x30 */ 0x3C, 0x66, 0x42, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x42, 0x66, 0x3C,
+/* '1' 0x31 */ 0x11, 0x3F, 0x33, 0x33, 0x33, 0x33, 0x30,
+/* '2' 0x32 */ 0x3E, 0x31, 0xB0, 0x78, 0x30, 0x18, 0x1C, 0x1C, 0x1C, 0x18, 0x18, 0x10, 0x08, 0x07, 0xF8,
+/* '3' 0x33 */ 0x3C, 0x66, 0xC3, 0xC3, 0x03, 0x06, 0x1C, 0x07, 0x03, 0xC3, 0xC3, 0x66, 0x3C,
+/* '4' 0x34 */ 0x0C, 0x18, 0x71, 0x62, 0xC9, 0xA3, 0x46, 0xFE, 0x18, 0x30, 0x60, 0xC0,
+/* '5' 0x35 */ 0x7F, 0x20, 0x10, 0x08, 0x08, 0x07, 0xF3, 0x8C, 0x03, 0x01, 0x80, 0xF0, 0x6C, 0x63, 0xE0,
+/* '6' 0x36 */ 0x1E, 0x31, 0x98, 0x78, 0x0C, 0x06, 0xF3, 0x8D, 0x83, 0xC1, 0xE0, 0xD0, 0x6C, 0x63, 0xE0,
+/* '7' 0x37 */ 0xFF, 0x03, 0x02, 0x06, 0x04, 0x0C, 0x08, 0x18, 0x18, 0x18, 0x10, 0x30, 0x30,
+/* '8' 0x38 */ 0x3E, 0x31, 0xB0, 0x78, 0x3C, 0x1B, 0x18, 0xF8, 0xC6, 0xC1, 0xE0, 0xF0, 0x6C, 0x63, 0xE0,
+/* '9' 0x39 */ 0x3C, 0x66, 0xC2, 0xC3, 0xC3, 0xC3, 0x67, 0x3B, 0x03, 0x03, 0xC2, 0x66, 0x3C,
+/* ':' 0x3A */ 0xC0, 0x00, 0x30,
+/* ';' 0x3B */ 0xC0, 0x00, 0x00, 0x64, 0xA0,
+/* '<' 0x3C */ 0x00, 0x81, 0xC7, 0x8E, 0x0C, 0x07, 0x80, 0x70, 0x0E, 0x01, 0x80,
+/* '=' 0x3D */ 0xFF, 0x80, 0x00, 0x1F, 0xF0,
+/* '>' 0x3E */ 0xE0, 0x1C, 0x03, 0x80, 0x30, 0x70, 0xE3, 0x81, 0x00,
+/* '?' 0x3F */ 0x3E, 0x31, 0xB0, 0x78, 0x30, 0x18, 0x18, 0x38, 0x18, 0x18, 0x0C, 0x00, 0x00, 0x01, 0x80,
+/* '@' 0x40 */ 0x03, 0xF0, 0x06, 0x0E, 0x06, 0x01, 0x86, 0x00, 0x66, 0x1D, 0xBB, 0x31, 0xCF, 0x18, 0xC7, 0x98, 0x63, 0xCC, 0x31, 0xE6, 0x11, 0xB3, 0x99, 0xCC, 0xF7, 0x86, 0x00, 0x01, 0x80, 0x00, 0x70, 0x40, 0x0F, 0xE0,
+/* 'A' 0x41 */ 0x06, 0x00, 0xF0, 0x0F, 0x00, 0x90, 0x19, 0x81, 0x98, 0x10, 0x83, 0x0C, 0x3F, 0xC2, 0x04, 0x60, 0x66, 0x06, 0xC0, 0x30,
+/* 'B' 0x42 */ 0xFF, 0x18, 0x33, 0x03, 0x60, 0x6C, 0x0D, 0x83, 0x3F, 0xC6, 0x06, 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x6F, 0xF8,
+/* 'C' 0x43 */ 0x1F, 0x86, 0x19, 0x81, 0xA0, 0x3C, 0x01, 0x80, 0x30, 0x06, 0x00, 0xC0, 0x68, 0x0D, 0x83, 0x18, 0x61, 0xF0,
+/* 'D' 0x44 */ 0xFF, 0x18, 0x33, 0x03, 0x60, 0x3C, 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0x78, 0x0F, 0x03, 0x60, 0xCF, 0xF0,
+/* 'E' 0x45 */ 0xFF, 0xE0, 0x30, 0x18, 0x0C, 0x06, 0x03, 0xFD, 0x80, 0xC0, 0x60, 0x30, 0x18, 0x0F, 0xF8,
+/* 'F' 0x46 */ 0xFF, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xFE, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0,
+/* 'G' 0x47 */ 0x0F, 0x83, 0x0E, 0x60, 0x66, 0x03, 0xC0, 0x0C, 0x00, 0xC1, 0xFC, 0x03, 0xC0, 0x36, 0x03, 0x60, 0x73, 0x0F, 0x0F, 0x10,
+/* 'H' 0x48 */ 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xFF, 0xFE, 0x03, 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x3C, 0x06,
+/* 'I' 0x49 */ 0xFF, 0xFF, 0xFF, 0xC0,
+/* 'J' 0x4A */ 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC1, 0x83, 0x07, 0x8F, 0x1E, 0x27, 0x80,
+/* 'K' 0x4B */ 0xC0, 0xF0, 0x6C, 0x33, 0x18, 0xCC, 0x37, 0x0F, 0xC3, 0x98, 0xC3, 0x30, 0xCC, 0x1B, 0x03, 0xC0, 0xC0,
+/* 'L' 0x4C */ 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xFF,
+/* 'M' 0x4D */ 0xE0, 0x3F, 0x01, 0xFC, 0x1F, 0xE0, 0xFD, 0x05, 0xEC, 0x6F, 0x63, 0x79, 0x13, 0xCD, 0x9E, 0x6C, 0xF1, 0x47, 0x8E, 0x3C, 0x71, 0x80,
+/* 'N' 0x4E */ 0xE0, 0x7C, 0x0F, 0xC1, 0xE8, 0x3D, 0x87, 0x98, 0xF1, 0x1E, 0x33, 0xC3, 0x78, 0x6F, 0x07, 0xE0, 0x7C, 0x0E,
+/* 'O' 0x4F */ 0x0F, 0x81, 0x83, 0x18, 0x0C, 0xC0, 0x6C, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x03, 0xC0, 0x1B, 0x01, 0x98, 0x0C, 0x60, 0xC0, 0xF8, 0x00,
+/* 'P' 0x50 */ 0xFF, 0x30, 0x6C, 0x0F, 0x03, 0xC0, 0xF0, 0x6F, 0xF3, 0x00, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x00,
+/* 'Q' 0x51 */ 0x0F, 0x81, 0x83, 0x18, 0x0C, 0xC0, 0x6C, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x03, 0xC0, 0x1B, 0x01, 0x98, 0x6C, 0x60, 0xC0, 0xFB, 0x00, 0x08,
+/* 'R' 0x52 */ 0xFF, 0x8C, 0x0E, 0xC0, 0x6C, 0x06, 0xC0, 0x6C, 0x0C, 0xFF, 0x8C, 0x0E, 0xC0, 0x6C, 0x06, 0xC0, 0x6C, 0x06, 0xC0, 0x70,
+/* 'S' 0x53 */ 0x3F, 0x18, 0x6C, 0x0F, 0x03, 0xC0, 0x1E, 0x01, 0xF0, 0x0E, 0x00, 0xF0, 0x3C, 0x0D, 0x86, 0x3F, 0x00,
+/* 'T' 0x54 */ 0xFF, 0x86, 0x03, 0x01, 0x80, 0xC0, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x01, 0x80, 0xC0,
+/* 'U' 0x55 */ 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0x78, 0x0F, 0x01, 0xB0, 0x61, 0xF0,
+/* 'V' 0x56 */ 0xC0, 0x6C, 0x0D, 0x81, 0x10, 0x63, 0x0C, 0x61, 0x04, 0x60, 0xCC, 0x19, 0x01, 0x60, 0x3C, 0x07, 0x00, 0x60,
+/* 'W' 0x57 */ 0xC1, 0x81, 0x61, 0xC3, 0x61, 0xC3, 0x61, 0x43, 0x62, 0x62, 0x22, 0x66, 0x32, 0x26, 0x36, 0x26, 0x14, 0x34, 0x14, 0x34, 0x1C, 0x1C, 0x18, 0x1C, 0x08, 0x18,
+/* 'X' 0x58 */ 0xC0, 0xD8, 0x66, 0x18, 0xCC, 0x1E, 0x07, 0x00, 0xC0, 0x78, 0x32, 0x0C, 0xC6, 0x1B, 0x07, 0xC0, 0xC0,
+/* 'Y' 0x59 */ 0xC0, 0x36, 0x06, 0x30, 0xC3, 0x0C, 0x19, 0x81, 0xD8, 0x0F, 0x00, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00,
+/* 'Z' 0x5A */ 0xFF, 0xC0, 0x60, 0x30, 0x0C, 0x06, 0x03, 0x01, 0xC0, 0x60, 0x30, 0x18, 0x06, 0x03, 0x00, 0xFF, 0xC0,
+/* '[' 0x5B */ 0xFB, 0x6D, 0xB6, 0xDB, 0x6D, 0xB6, 0xE0,
+/* '\' 0x5C */ 0x84, 0x10, 0x84, 0x10, 0x84, 0x10, 0x84, 0x10, 0x80,
+/* ']' 0x5D */ 0xED, 0xB6, 0xDB, 0x6D, 0xB6, 0xDB, 0xE0,
+/* '^' 0x5E */ 0x30, 0x60, 0xA2, 0x44, 0xD8, 0xA1, 0x80,
+/* '_' 0x5F */ 0xFF, 0xC0,
+/* '`' 0x60 */ 0xC6, 0x30,
+/* 'a' 0x61 */ 0x7E, 0x71, 0xB0, 0xC0, 0x60, 0xF3, 0xDB, 0x0D, 0x86, 0xC7, 0x3D, 0xC0,
+/* 'b' 0x62 */ 0xC0, 0x60, 0x30, 0x1B, 0xCE, 0x36, 0x0F, 0x07, 0x83, 0xC1, 0xE0, 0xF0, 0x7C, 0x6D, 0xE0,
+/* 'c' 0x63 */ 0x3C, 0x66, 0xC3, 0xC0, 0xC0, 0xC0, 0xC0, 0xC3, 0x66, 0x3C,
+/* 'd' 0x64 */ 0x03, 0x03, 0x03, 0x3B, 0x67, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x67, 0x3B,
+/* 'e' 0x65 */ 0x3C, 0x66, 0xC3, 0xC3, 0xFF, 0xC0, 0xC0, 0xC3, 0x66, 0x3C,
+/* 'f' 0x66 */ 0x36, 0x6F, 0x66, 0x66, 0x66, 0x66, 0x60,
+/* 'g' 0x67 */ 0x3B, 0x67, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x67, 0x3B, 0x03, 0x03, 0xC6, 0x7C,
+/* 'h' 0x68 */ 0xC0, 0xC0, 0xC0, 0xDE, 0xE3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3,
+/* 'i' 0x69 */ 0xC3, 0xFF, 0xFF, 0xC0,
+/* 'j' 0x6A */ 0x30, 0x03, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0xE0,
+/* 'k' 0x6B */ 0xC0, 0xC0, 0xC0, 0xC2, 0xC4, 0xCC, 0xD8, 0xF8, 0xEC, 0xC4, 0xC6, 0xC3, 0xC3,
+/* 'l' 0x6C */ 0xFF, 0xFF, 0xFF, 0xC0,
+/* 'm' 0x6D */ 0xDE, 0xF7, 0x1C, 0xF0, 0xC7, 0x86, 0x3C, 0x31, 0xE1, 0x8F, 0x0C, 0x78, 0x63, 0xC3, 0x1E, 0x18, 0xC0,
+/* 'n' 0x6E */ 0xDE, 0xE3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3,
+/* 'o' 0x6F */ 0x3C, 0x66, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x66, 0x3C,
+/* 'p' 0x70 */ 0xDE, 0x71, 0xB0, 0x78, 0x3C, 0x1E, 0x0F, 0x07, 0x83, 0xE3, 0x6F, 0x30, 0x18, 0x0C, 0x00,
+/* 'q' 0x71 */ 0x3B, 0x67, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x67, 0x3B, 0x03, 0x03, 0x03,
+/* 'r' 0x72 */ 0xDF, 0x31, 0x8C, 0x63, 0x18, 0xC6, 0x00,
+/* 's' 0x73 */ 0x3E, 0xE3, 0xC0, 0xC0, 0xE0, 0x3C, 0x07, 0xC3, 0xE3, 0x7E,
+/* 't' 0x74 */ 0x66, 0xF6, 0x66, 0x66, 0x66, 0x67,
+/* 'u' 0x75 */ 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC7, 0x7B,
+/* 'v' 0x76 */ 0xC1, 0xA0, 0x98, 0xCC, 0x42, 0x21, 0xB0, 0xD0, 0x28, 0x1C, 0x0C, 0x00,
+/* 'w' 0x77 */ 0xC6, 0x1E, 0x38, 0x91, 0xC4, 0xCA, 0x66, 0xD3, 0x16, 0xD0, 0xA6, 0x87, 0x1C, 0x38, 0xC0, 0xC6, 0x00,
+/* 'x' 0x78 */ 0x87, 0x89, 0xB1, 0xC3, 0x07, 0x1E, 0x26, 0xC5, 0x0C,
+/* 'y' 0x79 */ 0xC1, 0x43, 0x63, 0x62, 0x26, 0x36, 0x34, 0x1C, 0x1C, 0x18, 0x18, 0x18, 0x10, 0x60,
+/* 'z' 0x7A */ 0xFE, 0x0C, 0x30, 0xC1, 0x86, 0x18, 0x20, 0xC1, 0xFC,
+/* '{' 0x7B */ 0x36, 0x66, 0x66, 0x6E, 0xCE, 0x66, 0x66, 0x66, 0x30,
+/* '|' 0x7C */ 0xFF, 0xFF, 0xFF, 0xFF, 0xC0,
+/* '}' 0x7D */ 0xC6, 0x66, 0x66, 0x67, 0x37, 0x66, 0x66, 0x66, 0xC0,
+/* '~' 0x7E */ 0x61, 0x24, 0x38,
+/* 0x7F */
+/* 0x80 */ 0x07, 0xC6, 0x13, 0x00, 0xC0, 0x60, 0x3F, 0xE6, 0x03, 0xFC, 0x60, 0x0C, 0x03, 0x00, 0x61, 0x07, 0xC0,
+/* 0x81 */
+/* 0x82 */ 0xDC,
+/* 0x83 */ 0x19, 0x8C, 0xF3, 0x18, 0xC6, 0x31, 0x8C, 0x63, 0x18, 0xC6, 0xE0,
+/* 0x84 */ 0xDA, 0x76,
+/* 0x85 */ 0xCC, 0xC0,
+/* 0x86 */ 0x18, 0x18, 0x18, 0x18, 0xFF, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+/* 0x87 */ 0x18, 0x18, 0x18, 0x18, 0xFF, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xFF, 0x18, 0x18, 0x18, 0x18,
+/* 0x88 */ 0x72, 0xA2,
+/* 0x89 */ 0x70, 0x80, 0x22, 0x20, 0x08, 0x90, 0x02, 0x24, 0x00, 0x72, 0x00, 0x00, 0x80, 0x00, 0x40, 0x00, 0x10, 0x00, 0x09, 0xC7, 0x84, 0x8B, 0x31, 0x22, 0x84, 0x88, 0xB3, 0x21, 0xC7, 0x80,
+/* 0x8A */ 0x1B, 0x03, 0x80, 0x00, 0xFC, 0x61, 0xB0, 0x3C, 0x0F, 0x00, 0x78, 0x07, 0xC0, 0x38, 0x03, 0xC0, 0xF0, 0x36, 0x18, 0xFC,
+/* 0x8B */ 0x69,
+/* 0x8C */ 0x1E, 0xFE, 0x43, 0x81, 0x83, 0x06, 0x06, 0x0C, 0x0C, 0x18, 0x18, 0x30, 0x3F, 0xE0, 0x60, 0xC0, 0xC1, 0x81, 0x81, 0x83, 0x01, 0x8E, 0x01, 0xEF, 0xE0,
+/* 0x8D */
+/* 0x8E */ 0x1B, 0x03, 0x80, 0x03, 0xFF, 0x01, 0x80, 0xC0, 0x30, 0x18, 0x0C, 0x07, 0x01, 0x80, 0xC0, 0x60, 0x18, 0x0C, 0x03, 0xFF,
+/* 0x8F */
+/* 0x90 */
+/* 0x91 */ 0x6B,
+/* 0x92 */ 0xD6,
+/* 0x93 */ 0x4C, 0xA5, 0xB0,
+/* 0x94 */ 0xDA, 0x53, 0x20,
+/* 0x95 */ 0x6F, 0xFF, 0x60,
+/* 0x96 */ 0xFE,
+/* 0x97 */ 0xFF, 0xFF,
+/* 0x98 */ 0x4D, 0xC0,
+/* 0x99 */ 0xFC, 0xE1, 0xCC, 0x38, 0x73, 0x0E, 0x1C, 0xC3, 0x8F, 0x30, 0xD2, 0xCC, 0x34, 0xB3, 0x0D, 0x6C, 0xC3, 0x53, 0x30, 0xCC, 0xCC, 0x33, 0x30,
+/* 0x9A */ 0x24, 0x3C, 0x18, 0x7E, 0xE3, 0xC0, 0xC0, 0x60, 0x3C, 0x07, 0xC3, 0xE3, 0x7E,
+/* 0x9B */ 0x96,
+/* 0x9C */ 0x3C, 0xF8, 0xCF, 0x1B, 0x0C, 0x1E, 0x18, 0x3C, 0x3F, 0xF8, 0x60, 0x30, 0xC0, 0x61, 0x83, 0x67, 0x8C, 0x79, 0xF0,
+/* 0x9D */
+/* 0x9E */ 0x48, 0xF0, 0xC7, 0xF0, 0x61, 0x86, 0x0C, 0x30, 0xC1, 0x06, 0x0F, 0xE0,
+/* 0x9F */ 0x19, 0x80, 0x00, 0xC0, 0x36, 0x06, 0x30, 0xC3, 0x0C, 0x19, 0x81, 0xD8, 0x0F, 0x00, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60,
+/* 0xA0 */
+/* 0xA1 */ 0xCF, 0xFF, 0xFF, 0xC0,
+/* 0xA2 */ 0x08, 0x04, 0x0F, 0x8D, 0x6C, 0x9E, 0x43, 0x21, 0x90, 0xC8, 0x64, 0xDA, 0xC7, 0xC0, 0x80, 0x40,
+/* 0xA3 */ 0x1F, 0x0C, 0x66, 0x0D, 0x83, 0x60, 0x0C, 0x0F, 0xC0, 0x60, 0x18, 0x06, 0x03, 0x01, 0xF1, 0x43, 0xC0,
+/* 0xA4 */ 0xFF, 0xDF, 0x1E, 0x3E, 0xFF, 0xC0,
+/* 0xA5 */ 0xC3, 0x42, 0x42, 0x24, 0x24, 0x3C, 0x18, 0x7E, 0x18, 0x7E, 0x18, 0x18, 0x18,
+/* 0xA6 */ 0xFF, 0xFC, 0x0F, 0xFF, 0xC0,
+/* 0xA7 */ 0x0C, 0x09, 0x0C, 0xC6, 0x63, 0x81, 0xE3, 0x19, 0x87, 0xE1, 0xB8, 0xC6, 0x41, 0xC0, 0x73, 0x19, 0x8C, 0x66, 0x1E, 0x00,
+/* 0xA8 */ 0xCC,
+/* 0xA9 */ 0x0F, 0xC0, 0x61, 0x87, 0x03, 0x9B, 0xC6, 0xD9, 0x8F, 0x60, 0x3D, 0x00, 0xF4, 0x03, 0xD8, 0x0D, 0xE6, 0x67, 0xF3, 0x86, 0x18, 0x0F, 0xC0,
+/* 0xAA */ 0x74, 0x8D, 0xA9, 0x7C, 0x1F,
+/* 0xAB */ 0x22, 0xCF, 0x26, 0x46, 0x64, 0x40,
+/* 0xAC */ 0xFF, 0x80, 0xC0, 0x60, 0x30, 0x18,
+/* 0xAD */
+/* 0xAE */ 0x0F, 0xC0, 0x61, 0x87, 0x03, 0x9F, 0xE6, 0xD0, 0x8F, 0x42, 0x3D, 0xF0, 0xF4, 0x23, 0xD0, 0x8D, 0xC2, 0x67, 0x0B, 0x86, 0x18, 0x0F, 0xC0,
+/* 0xAF */ 0xF8,
+/* 0xB0 */ 0x74, 0x63, 0x17, 0x00,
+/* 0xB1 */ 0x0C, 0x06, 0x03, 0x07, 0xE0, 0xC0, 0x60, 0x30, 0x18, 0x00, 0x00, 0x3F, 0xE0,
+/* 0xB2 */ 0x7B, 0x30, 0xC3, 0x11, 0x84, 0x3F,
+/* 0xB3 */ 0x7D, 0x8C, 0x18, 0xC0, 0x60, 0xF1, 0xBE,
+/* 0xB4 */ 0x36, 0xC0,
+/* 0xB5 */ 0xC3, 0x61, 0xB0, 0xD8, 0x6C, 0x36, 0x1B, 0x0D, 0x86, 0xE7, 0x7D, 0xF0, 0x18, 0x0C, 0x00,
+/* 0xB6 */ 0x3F, 0x7E, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0x72, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
+/* 0xB7 */ 0xE0,
+/* 0xB8 */ 0x21, 0xC7, 0xE0,
+/* 0xB9 */ 0x3D, 0xB6, 0xD8,
+/* 0xBA */ 0x74, 0x63, 0x18, 0xB8, 0x1F,
+/* 0xBB */ 0x89, 0x98, 0x99, 0x3C, 0xD1, 0x00,
+/* 0xBC */ 0x20, 0x43, 0x81, 0x06, 0x08, 0x18, 0x20, 0x61, 0x01, 0x84, 0x06, 0x21, 0x80, 0x86, 0x04, 0x78, 0x32, 0x60, 0x87, 0xC4, 0x06, 0x10, 0x18,
+/* 0xBD */ 0x20, 0x43, 0x81, 0x06, 0x08, 0x18, 0x20, 0x61, 0x01, 0x8D, 0xE6, 0x2C, 0xC1, 0x03, 0x0C, 0x0C, 0x20, 0x41, 0x86, 0x0C, 0x30, 0x20, 0xFC,
+/* 0xBE */ 0x78, 0x11, 0x98, 0x40, 0x31, 0x00, 0x82, 0x00, 0xC8, 0x01, 0x90, 0x33, 0x43, 0x3D, 0x06, 0x02, 0x3C, 0x08, 0x98, 0x10, 0xF8, 0x40, 0x61, 0x00, 0xC0,
+/* 0xBF */ 0x0C, 0x00, 0x00, 0x01, 0x80, 0xC0, 0xC0, 0xE0, 0xC0, 0xC0, 0x60, 0xF0, 0x6C, 0x63, 0xE0,
+/* 0xC0 */ 0x18, 0x03, 0x00, 0x00, 0x30, 0x1E, 0x07, 0x81, 0x20, 0xCC, 0x33, 0x0F, 0xC6, 0x19, 0x86, 0x40, 0xB0, 0x30,
+/* 0xC1 */ 0x06, 0x03, 0x00, 0x00, 0x30, 0x1E, 0x07, 0x81, 0x20, 0xCC, 0x33, 0x0F, 0xC6, 0x19, 0x86, 0x40, 0xB0, 0x30,
+/* 0xC2 */ 0x0C, 0x04, 0x80, 0x00, 0x30, 0x1E, 0x07, 0x81, 0x20, 0xCC, 0x33, 0x0F, 0xC6, 0x19, 0x86, 0x40, 0xB0, 0x30,
+/* 0xC3 */ 0x19, 0x09, 0x80, 0x00, 0x30, 0x1E, 0x07, 0x81, 0x20, 0xCC, 0x33, 0x0F, 0xC6, 0x19, 0x86, 0x40, 0xB0, 0x30,
+/* 0xC4 */ 0x33, 0x00, 0x00, 0xC0, 0x78, 0x1E, 0x04, 0x83, 0x30, 0xCC, 0x33, 0x1F, 0xE6, 0x19, 0x02, 0xC0, 0xF0, 0x30,
+/* 0xC5 */ 0x0C, 0x04, 0x81, 0x20, 0x30, 0x1E, 0x07, 0x81, 0x20, 0xCC, 0x33, 0x0F, 0xC6, 0x19, 0x86, 0x40, 0xB0, 0x30,
+/* 0xC6 */ 0x07, 0xFF, 0x04, 0xC0, 0x0C, 0xC0, 0x08, 0xC0, 0x18, 0xC0, 0x18, 0xC0, 0x30, 0xFF, 0x30, 0xC0, 0x3F, 0xC0, 0x60, 0xC0, 0x60, 0xC0, 0xC0, 0xC0, 0xC0, 0xFF,
+/* 0xC7 */ 0x1F, 0x06, 0x19, 0x83, 0xA0, 0x3C, 0x01, 0x80, 0x30, 0x06, 0x00, 0xC0, 0x68, 0x0D, 0x83, 0x18, 0xE1, 0xF0, 0x08, 0x01, 0xC0, 0x18, 0x0E, 0x00,
+/* 0xC8 */ 0x18, 0x06, 0x00, 0x1F, 0xFC, 0x06, 0x03, 0x01, 0x80, 0xFF, 0x60, 0x30, 0x18, 0x0C, 0x07, 0xFC,
+/* 0xC9 */ 0x0C, 0x0C, 0x00, 0x1F, 0xFC, 0x06, 0x03, 0x01, 0x80, 0xFF, 0x60, 0x30, 0x18, 0x0C, 0x07, 0xFC,
+/* 0xCA */ 0x1C, 0x1B, 0x00, 0x1F, 0xFC, 0x06, 0x03, 0x01, 0x80, 0xFF, 0x60, 0x30, 0x18, 0x0C, 0x07, 0xFC,
+/* 0xCB */ 0x33, 0x00, 0x3F, 0xF8, 0x0C, 0x06, 0x03, 0x01, 0xFE, 0xC0, 0x60, 0x30, 0x18, 0x0C, 0x07, 0xFC,
+/* 0xCC */ 0xCC, 0x36, 0xDB, 0x6D, 0xB6, 0xD8,
+/* 0xCD */ 0x78, 0x36, 0xDB, 0x6D, 0xB6, 0xC0,
+/* 0xCE */ 0x76, 0xC0, 0x63, 0x18, 0xC6, 0x31, 0x8C, 0x63, 0x18,
+/* 0xCF */ 0xCC, 0x03, 0x0C, 0x30, 0xC3, 0x0C, 0x30, 0xC3, 0x0C, 0x30, 0xC0,
+/* 0xD0 */ 0x7F, 0x0C, 0x31, 0x83, 0x30, 0x36, 0x06, 0xC0, 0xFE, 0x1B, 0x03, 0x60, 0x6C, 0x0D, 0x83, 0x30, 0xE7, 0xF0,
+/* 0xD1 */ 0x19, 0x02, 0xC3, 0x81, 0xF0, 0x3F, 0x07, 0xA0, 0xF6, 0x1E, 0x63, 0xC4, 0x78, 0xCF, 0x0D, 0xE1, 0xBC, 0x1F, 0x81, 0xC0,
+/* 0xD2 */ 0x0C, 0x00, 0x60, 0x00, 0x00, 0xF0, 0x39, 0xC6, 0x06, 0x60, 0x6C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, 0x36, 0x06, 0x60, 0x63, 0x9C, 0x0F, 0x00,
+/* 0xD3 */ 0x03, 0x00, 0x60, 0x00, 0x00, 0xF0, 0x39, 0xC6, 0x06, 0x60, 0x6C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, 0x36, 0x06, 0x60, 0x63, 0x9C, 0x0F, 0x00,
+/* 0xD4 */ 0x0F, 0x01, 0x98, 0x00, 0x00, 0xF0, 0x39, 0xC6, 0x06, 0x60, 0x6C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, 0x36, 0x06, 0x60, 0x63, 0x9C, 0x0F, 0x00,
+/* 0xD5 */ 0x1C, 0x81, 0x38, 0x00, 0x00, 0xF0, 0x39, 0xC6, 0x06, 0x60, 0x6C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, 0x36, 0x06, 0x60, 0x63, 0x9C, 0x0F, 0x00,
+/* 0xD6 */ 0x19, 0x81, 0x98, 0x00, 0x00, 0xF0, 0x39, 0xC6, 0x06, 0x60, 0x6C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, 0x36, 0x06, 0x60, 0x63, 0x9C, 0x0F, 0x00,
+/* 0xD7 */ 0x83, 0x89, 0xA1, 0x83, 0x89, 0xA1, 0x80,
+/* 0xD8 */ 0x0F, 0xD9, 0x83, 0x18, 0x1C, 0xC1, 0xEC, 0x19, 0xE0, 0x8F, 0x08, 0x78, 0x83, 0xC8, 0x1B, 0x81, 0x98, 0x0C, 0xE0, 0xC8, 0xF8, 0x00,
+/* 0xD9 */ 0x0C, 0x00, 0xC3, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x36, 0x0C, 0x3E, 0x00,
+/* 0xDA */ 0x06, 0x01, 0x83, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x36, 0x0C, 0x3E, 0x00,
+/* 0xDB */ 0x0E, 0x03, 0x63, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x36, 0x0C, 0x3E, 0x00,
+/* 0xDC */ 0x1B, 0x00, 0x03, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x36, 0x0C, 0x3E, 0x00,
+/* 0xDD */ 0x03, 0x0C, 0x63, 0x60, 0x63, 0x0C, 0x30, 0xC1, 0x98, 0x1D, 0x80, 0xF0, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60,
+/* 0xDE */ 0xC0, 0x30, 0x0F, 0xF3, 0x06, 0xC0, 0xF0, 0x3C, 0x0F, 0x06, 0xFF, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x00,
+/* 0xDF */ 0x3C, 0x33, 0x30, 0xD8, 0x6C, 0x36, 0x33, 0x39, 0x86, 0xC1, 0xE0, 0xF0, 0x78, 0x6D, 0xE0,
+/* 0xE0 */ 0x60, 0x18, 0x06, 0x0F, 0xCE, 0x36, 0x18, 0x0C, 0x1E, 0x7B, 0x61, 0xB0, 0xD8, 0xE7, 0xB8,
+/* 0xE1 */ 0x0C, 0x04, 0x04, 0x0F, 0xCE, 0x36, 0x18, 0x0C, 0x1E, 0x7B, 0x61, 0xB0, 0xD8, 0xE7, 0xB8,
+/* 0xE2 */ 0x10, 0x14, 0x1B, 0x0F, 0xCE, 0x36, 0x18, 0x0C, 0x1E, 0x7B, 0x61, 0xB0, 0xD8, 0xE7, 0xB8,
+/* 0xE3 */ 0x24, 0x2E, 0x00, 0x0F, 0xCE, 0x36, 0x18, 0x0C, 0x1E, 0x7B, 0x61, 0xB0, 0xD8, 0xE7, 0xB8,
+/* 0xE4 */ 0x66, 0x00, 0x1F, 0x9C, 0x6C, 0x30, 0x18, 0x3C, 0xF6, 0xC3, 0x61, 0xB1, 0xCF, 0x70,
+/* 0xE5 */ 0x1C, 0x1B, 0x0D, 0x83, 0x87, 0xE7, 0x1B, 0x0C, 0x06, 0x0F, 0x3D, 0xB0, 0xD8, 0x6C, 0x73, 0xDC,
+/* 0xE6 */ 0x7E, 0xF9, 0xC7, 0x1B, 0x0C, 0x18, 0x18, 0x33, 0xFF, 0xFC, 0x60, 0x30, 0xC0, 0x61, 0x83, 0xC7, 0x8C, 0xF1, 0xF0,
+/* 0xE7 */ 0x3C, 0x66, 0xC3, 0xC0, 0xC0, 0xC0, 0xC0, 0xC3, 0x66, 0x3C, 0x10, 0x1C, 0x0C, 0x38,
+/* 0xE8 */ 0x60, 0x30, 0x18, 0x3C, 0x66, 0xC3, 0xC3, 0xFF, 0xC0, 0xC0, 0xC3, 0x66, 0x3C,
+/* 0xE9 */ 0x0C, 0x08, 0x18, 0x3C, 0x66, 0xC3, 0xC3, 0xFF, 0xC0, 0xC0, 0xC3, 0x66, 0x3C,
+/* 0xEA */ 0x10, 0x28, 0x6C, 0x3C, 0x66, 0xC3, 0xC3, 0xFF, 0xC0, 0xC0, 0xC3, 0x66, 0x3C,
+/* 0xEB */ 0x66, 0x00, 0x3C, 0x66, 0xC3, 0xC3, 0xFF, 0xC0, 0xC0, 0xC3, 0x66, 0x3C,
+/* 0xEC */ 0xCC, 0xB6, 0xDB, 0x6D, 0xB6,
+/* 0xED */ 0x7A, 0x6D, 0xB6, 0xDB, 0x6C,
+/* 0xEE */ 0x6E, 0x96, 0x66, 0x66, 0x66, 0x66, 0x60,
+/* 0xEF */ 0xCC, 0x03, 0x0C, 0x30, 0xC3, 0x0C, 0x30, 0xC3, 0x0C,
+/* 0xF0 */ 0x34, 0x0C, 0x16, 0x03, 0x3F, 0x67, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x66, 0x3C,
+/* 0xF1 */ 0x24, 0x5C, 0x00, 0xDE, 0xE3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3,
+/* 0xF2 */ 0x30, 0x18, 0x00, 0x3C, 0x66, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x66, 0x3C,
+/* 0xF3 */ 0x0C, 0x18, 0x00, 0x3C, 0x66, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x66, 0x3C,
+/* 0xF4 */ 0x18, 0x24, 0x00, 0x3C, 0x66, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x66, 0x3C,
+/* 0xF5 */ 0x34, 0x2C, 0x00, 0x3C, 0x66, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x66, 0x3C,
+/* 0xF6 */ 0x66, 0x00, 0x3C, 0x66, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x66, 0x3C,
+/* 0xF7 */ 0x18, 0x00, 0x00, 0x1F, 0xF0, 0x00, 0x00, 0x00, 0x30,
+/* 0xF8 */ 0x3D, 0x66, 0xC7, 0xCB, 0xCB, 0xD3, 0xD3, 0xE3, 0x66, 0xBC,
+/* 0xF9 */ 0x60, 0x30, 0x18, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC7, 0x7B,
+/* 0xFA */ 0x06, 0x0C, 0x18, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC7, 0x7B,
+/* 0xFB */ 0x3C, 0x66, 0x00, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC7, 0x7B,
+/* 0xFC */ 0x66, 0x00, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC7, 0x7B,
+/* 0xFD */ 0x06, 0x04, 0x08, 0xC1, 0x43, 0x63, 0x62, 0x26, 0x36, 0x34, 0x1C, 0x1C, 0x18, 0x18, 0x18, 0x10, 0x60,
+/* 0xFE */ 0xC0, 0x60, 0x30, 0x1B, 0xCE, 0x36, 0x0F, 0x07, 0x83, 0xC1, 0xE0, 0xF0, 0x7C, 0x6D, 0xE6, 0x03, 0x01, 0x80,
+/* 0xFF */ 0x33, 0x00, 0xC1, 0x43, 0x63, 0x62, 0x26, 0x36, 0x34, 0x1C, 0x1C, 0x18, 0x18, 0x18, 0x10, 0x60,
};
const GFXglyph FreeSans9pt_Win1252Glyphs[] PROGMEM = {
- /* ' ' 0x20 */ {0, 0, 0, 5, 0, 0},
- /* '!' 0x21 */ {0, 2, 13, 6, 2, -12},
- /* '"' 0x22 */ {4, 5, 4, 6, 1, -12},
- /* '#' 0x23 */ {7, 10, 12, 10, 0, -11},
- /* '$' 0x24 */ {22, 9, 16, 10, 1, -13},
- /* '%' 0x25 */ {40, 16, 13, 16, 1, -12},
- /* '&' 0x26 */ {66, 10, 13, 12, 1, -12},
- /* ''' 0x27 */ {83, 2, 4, 4, 1, -12},
- /* '(' 0x28 */ {84, 4, 17, 6, 1, -12},
- /* ')' 0x29 */ {93, 4, 17, 6, 1, -12},
- /* '*' 0x2A */ {102, 5, 5, 7, 1, -12},
- /* '+' 0x2B */ {106, 6, 8, 11, 3, -7},
- /* ',' 0x2C */ {112, 2, 4, 5, 2, 0},
- /* '-' 0x2D */ {113, 4, 1, 6, 1, -4},
- /* '.' 0x2E */ {114, 2, 1, 5, 1, 0},
- /* '/' 0x2F */ {115, 5, 13, 5, 0, -12},
- /* '0' 0x30 */ {124, 8, 13, 10, 1, -12},
- /* '1' 0x31 */ {137, 4, 13, 10, 3, -12},
- /* '2' 0x32 */ {144, 9, 13, 10, 1, -12},
- /* '3' 0x33 */ {159, 8, 13, 10, 1, -12},
- /* '4' 0x34 */ {172, 7, 13, 10, 2, -12},
- /* '5' 0x35 */ {184, 9, 13, 10, 1, -12},
- /* '6' 0x36 */ {199, 9, 13, 10, 1, -12},
- /* '7' 0x37 */ {214, 8, 13, 10, 0, -12},
- /* '8' 0x38 */ {227, 9, 13, 10, 1, -12},
- /* '9' 0x39 */ {242, 8, 13, 10, 1, -12},
- /* ':' 0x3A */ {255, 2, 10, 5, 1, -9},
- /* ';' 0x3B */ {258, 3, 12, 5, 1, -8},
- /* '<' 0x3C */ {263, 9, 9, 11, 1, -8},
- /* '=' 0x3D */ {274, 9, 4, 11, 1, -5},
- /* '>' 0x3E */ {279, 9, 8, 11, 1, -7},
- /* '?' 0x3F */ {288, 9, 13, 10, 1, -12},
- /* '@' 0x40 */ {303, 17, 16, 18, 1, -12},
- /* 'A' 0x41 */ {337, 12, 13, 12, 0, -12},
- /* 'B' 0x42 */ {357, 11, 13, 12, 1, -12},
- /* 'C' 0x43 */ {375, 11, 13, 13, 1, -12},
- /* 'D' 0x44 */ {393, 11, 13, 13, 1, -12},
- /* 'E' 0x45 */ {411, 9, 13, 11, 1, -12},
- /* 'F' 0x46 */ {426, 8, 13, 11, 1, -12},
- /* 'G' 0x47 */ {439, 12, 13, 14, 1, -12},
- /* 'H' 0x48 */ {459, 11, 13, 13, 1, -12},
- /* 'I' 0x49 */ {477, 2, 13, 5, 2, -12},
- /* 'J' 0x4A */ {481, 7, 13, 10, 1, -12},
- /* 'K' 0x4B */ {493, 10, 13, 12, 1, -12},
- /* 'L' 0x4C */ {510, 8, 13, 10, 1, -12},
- /* 'M' 0x4D */ {523, 13, 13, 15, 1, -12},
- /* 'N' 0x4E */ {545, 11, 13, 13, 1, -12},
- /* 'O' 0x4F */ {563, 13, 13, 14, 1, -12},
- /* 'P' 0x50 */ {585, 10, 13, 12, 1, -12},
- /* 'Q' 0x51 */ {602, 13, 14, 14, 1, -12},
- /* 'R' 0x52 */ {625, 12, 13, 13, 1, -12},
- /* 'S' 0x53 */ {645, 10, 13, 12, 1, -12},
- /* 'T' 0x54 */ {662, 9, 13, 11, 1, -12},
- /* 'U' 0x55 */ {677, 11, 13, 13, 1, -12},
- /* 'V' 0x56 */ {695, 11, 13, 11, 0, -12},
- /* 'W' 0x57 */ {713, 16, 13, 17, 0, -12},
- /* 'X' 0x58 */ {739, 10, 13, 12, 1, -12},
- /* 'Y' 0x59 */ {756, 12, 13, 12, 0, -12},
- /* 'Z' 0x5A */ {776, 10, 13, 11, 1, -12},
- /* '[' 0x5B */ {793, 3, 17, 5, 1, -12},
- /* '\' 0x5C */ {800, 5, 13, 5, 0, -12},
- /* ']' 0x5D */ {809, 3, 17, 5, 0, -12},
- /* '^' 0x5E */ {816, 7, 7, 8, 1, -12},
- /* '_' 0x5F */ {823, 10, 1, 10, 0, 3},
- /* '`' 0x60 */ {825, 4, 3, 5, 0, -12},
- /* 'a' 0x61 */ {827, 9, 10, 10, 1, -9},
- /* 'b' 0x62 */ {839, 9, 13, 10, 1, -12},
- /* 'c' 0x63 */ {854, 8, 10, 9, 1, -9},
- /* 'd' 0x64 */ {864, 8, 13, 10, 1, -12},
- /* 'e' 0x65 */ {877, 8, 10, 10, 1, -9},
- /* 'f' 0x66 */ {887, 4, 13, 5, 1, -12},
- /* 'g' 0x67 */ {894, 8, 14, 10, 1, -9},
- /* 'h' 0x68 */ {908, 8, 13, 10, 1, -12},
- /* 'i' 0x69 */ {921, 2, 13, 4, 1, -12},
- /* 'j' 0x6A */ {925, 4, 17, 4, 0, -12},
- /* 'k' 0x6B */ {934, 8, 13, 9, 1, -12},
- /* 'l' 0x6C */ {947, 2, 13, 4, 1, -12},
- /* 'm' 0x6D */ {951, 13, 10, 15, 1, -9},
- /* 'n' 0x6E */ {968, 8, 10, 10, 1, -9},
- /* 'o' 0x6F */ {978, 8, 10, 10, 1, -9},
- /* 'p' 0x70 */ {988, 9, 13, 10, 1, -9},
- /* 'q' 0x71 */ {1003, 8, 13, 10, 1, -9},
- /* 'r' 0x72 */ {1016, 5, 10, 6, 1, -9},
- /* 's' 0x73 */ {1023, 8, 10, 9, 1, -9},
- /* 't' 0x74 */ {1033, 4, 12, 5, 1, -11},
- /* 'u' 0x75 */ {1039, 8, 10, 10, 1, -9},
- /* 'v' 0x76 */ {1049, 9, 10, 9, 0, -9},
- /* 'w' 0x77 */ {1061, 13, 10, 13, 0, -9},
- /* 'x' 0x78 */ {1078, 7, 10, 9, 1, -9},
- /* 'y' 0x79 */ {1087, 8, 14, 9, 0, -9},
- /* 'z' 0x7A */ {1101, 7, 10, 9, 1, -9},
- /* '{' 0x7B */ {1110, 4, 17, 6, 1, -12},
- /* '|' 0x7C */ {1119, 2, 17, 4, 2, -12},
- /* '}' 0x7D */ {1124, 4, 17, 6, 1, -12},
- /* '~' 0x7E */ {1133, 7, 3, 9, 1, -7},
- /* 0x7F */ {1136, 13, 14, 15, 1, -12},
- /* 0x80 */ {1159, 10, 13, 12, 1, -12},
- /* 0x81 */ {1176, 0, 0, 8, 0, 0},
- /* 0x82 */ {1176, 2, 3, 5, 1, 0},
- /* 0x83 */ {1177, 5, 17, 5, 0, -12},
- /* 0x84 */ {1188, 5, 3, 7, 1, 0},
- /* 0x85 */ {1190, 10, 1, 12, 1, 0},
- /* 0x86 */ {1192, 8, 16, 10, 1, -12},
- /* 0x87 */ {1208, 8, 16, 10, 1, -12},
- /* 0x88 */ {1224, 5, 3, 6, 0, -12},
- /* 0x89 */ {1226, 18, 13, 18, 0, -12},
- /* 0x8A */ {1256, 10, 16, 12, 1, -15},
- /* 0x8B */ {1276, 2, 4, 4, 1, -6},
- /* 0x8C */ {1277, 15, 13, 18, 1, -12},
- /* 0x8D */ {1302, 0, 0, 8, 0, 0},
- /* 0x8E */ {1302, 10, 16, 11, 1, -15},
- /* 0x8F */ {1322, 0, 0, 8, 0, 0},
- /* 0x90 */ {1322, 0, 0, 8, 0, 0},
- /* 0x91 */ {1322, 2, 4, 4, 2, -12},
- /* 0x92 */ {1323, 2, 4, 4, 1, -12},
- /* 0x93 */ {1324, 5, 4, 7, 2, -12},
- /* 0x94 */ {1327, 5, 4, 7, 1, -12},
- /* 0x95 */ {1330, 4, 5, 7, 1, -8},
- /* 0x96 */ {1333, 7, 1, 9, 1, -4},
- /* 0x97 */ {1334, 16, 1, 18, 1, -4},
- /* 0x98 */ {1336, 5, 2, 6, 0, -12},
- /* 0x99 */ {1338, 18, 10, 18, 1, -13},
- /* 0x9A */ {1361, 8, 13, 9, 1, -12},
- /* 0x9B */ {1374, 2, 4, 5, 2, -6},
- /* 0x9C */ {1375, 15, 10, 17, 1, -9},
- /* 0x9D */ {1394, 0, 0, 8, 0, 0},
- /* 0x9E */ {1394, 7, 13, 9, 1, -12},
- /* 0x9F */ {1406, 12, 14, 12, 0, -13},
- /* 0xA0 */ {1427, 0, 0, 5, 0, 0},
- /* 0xA1 */ {1427, 2, 13, 6, 2, -8},
- /* 0xA2 */ {1431, 9, 14, 10, 1, -11},
- /* 0xA3 */ {1447, 10, 13, 10, 0, -12},
- /* 0xA4 */ {1464, 7, 6, 10, 2, -8},
- /* 0xA5 */ {1470, 8, 13, 10, 1, -12},
- /* 0xA6 */ {1483, 2, 17, 5, 2, -12},
- /* 0xA7 */ {1488, 9, 17, 10, 1, -12},
- /* 0xA8 */ {1508, 6, 1, 6, 0, -11},
- /* 0xA9 */ {1509, 14, 13, 14, 1, -12},
- /* 0xAA */ {1532, 5, 8, 7, 1, -12},
- /* 0xAB */ {1537, 7, 6, 9, 1, -7},
- /* 0xAC */ {1543, 9, 5, 11, 2, -5},
- /* 0xAD */ {1549, 0, 0, 0, 0, 0},
- /* 0xAE */ {1549, 14, 13, 14, 1, -12},
- /* 0xAF */ {1572, 5, 1, 6, 0, -12},
- /* 0xB0 */ {1573, 5, 5, 11, 3, -11},
- /* 0xB1 */ {1577, 9, 11, 11, 1, -10},
- /* 0xB2 */ {1590, 6, 8, 6, 1, -13},
- /* 0xB3 */ {1596, 7, 8, 6, 0, -13},
- /* 0xB4 */ {1603, 4, 3, 6, 2, -12},
- /* 0xB5 */ {1605, 9, 13, 10, 1, -9},
- /* 0xB6 */ {1620, 8, 16, 10, 2, -12},
- /* 0xB7 */ {1636, 3, 1, 5, 1, -4},
- /* 0xB8 */ {1637, 5, 4, 6, 1, 1},
- /* 0xB9 */ {1640, 3, 7, 6, 2, -13},
- /* 0xBA */ {1643, 5, 8, 7, 1, -12},
- /* 0xBB */ {1648, 7, 6, 9, 1, -7},
- /* 0xBC */ {1654, 14, 13, 16, 2, -12},
- /* 0xBD */ {1677, 14, 13, 16, 2, -12},
- /* 0xBE */ {1700, 15, 13, 16, 1, -12},
- /* 0xBF */ {1725, 9, 13, 10, 1, -8},
- /* 0xC0 */ {1740, 10, 14, 12, 1, -13},
- /* 0xC1 */ {1758, 10, 14, 12, 1, -13},
- /* 0xC2 */ {1776, 10, 14, 12, 1, -13},
- /* 0xC3 */ {1794, 10, 14, 12, 1, -13},
- /* 0xC4 */ {1812, 10, 14, 12, 1, -13},
- /* 0xC5 */ {1830, 10, 14, 12, 1, -13},
- /* 0xC6 */ {1848, 16, 13, 18, 1, -12},
- /* 0xC7 */ {1874, 11, 17, 13, 1, -12},
- /* 0xC8 */ {1898, 9, 14, 11, 1, -13},
- /* 0xC9 */ {1914, 9, 14, 11, 1, -13},
- /* 0xCA */ {1930, 9, 14, 11, 1, -13},
- /* 0xCB */ {1946, 9, 14, 11, 1, -13},
- /* 0xCC */ {1962, 3, 15, 5, 1, -13},
- /* 0xCD */ {1968, 3, 14, 5, 1, -13},
- /* 0xCE */ {1974, 5, 14, 5, 0, -13},
- /* 0xCF */ {1983, 6, 14, 5, 0, -13},
- /* 0xD0 */ {1994, 11, 13, 13, 1, -12},
- /* 0xD1 */ {2012, 11, 14, 13, 1, -13},
- /* 0xD2 */ {2032, 12, 15, 13, 1, -14},
- /* 0xD3 */ {2055, 12, 15, 13, 1, -14},
- /* 0xD4 */ {2078, 12, 15, 13, 1, -14},
- /* 0xD5 */ {2101, 12, 15, 13, 1, -14},
- /* 0xD6 */ {2124, 12, 15, 13, 1, -14},
- /* 0xD7 */ {2147, 7, 7, 11, 2, -7},
- /* 0xD8 */ {2154, 13, 13, 14, 1, -12},
- /* 0xD9 */ {2176, 11, 14, 13, 1, -13},
- /* 0xDA */ {2196, 11, 14, 13, 1, -13},
- /* 0xDB */ {2216, 11, 14, 13, 1, -13},
- /* 0xDC */ {2236, 11, 14, 13, 1, -13},
- /* 0xDD */ {2256, 12, 14, 12, 0, -13},
- /* 0xDE */ {2277, 10, 13, 12, 1, -12},
- /* 0xDF */ {2294, 9, 13, 11, 1, -12},
- /* 0xE0 */ {2309, 9, 13, 10, 1, -12},
- /* 0xE1 */ {2324, 9, 13, 10, 1, -12},
- /* 0xE2 */ {2339, 9, 13, 10, 1, -12},
- /* 0xE3 */ {2354, 9, 13, 10, 1, -12},
- /* 0xE4 */ {2369, 9, 12, 10, 1, -11},
- /* 0xE5 */ {2383, 9, 14, 10, 1, -13},
- /* 0xE6 */ {2399, 15, 10, 16, 1, -9},
- /* 0xE7 */ {2418, 8, 14, 9, 1, -9},
- /* 0xE8 */ {2432, 8, 13, 10, 1, -12},
- /* 0xE9 */ {2445, 8, 13, 10, 1, -12},
- /* 0xEA */ {2458, 8, 13, 10, 1, -12},
- /* 0xEB */ {2471, 8, 12, 10, 1, -11},
- /* 0xEC */ {2483, 3, 13, 4, 0, -12},
- /* 0xED */ {2488, 3, 13, 4, 1, -12},
- /* 0xEE */ {2493, 4, 13, 5, 0, -12},
- /* 0xEF */ {2500, 6, 12, 5, -1, -11},
- /* 0xF0 */ {2509, 8, 13, 10, 1, -12},
- /* 0xF1 */ {2522, 8, 13, 10, 1, -12},
- /* 0xF2 */ {2535, 8, 13, 10, 1, -12},
- /* 0xF3 */ {2548, 8, 13, 10, 1, -12},
- /* 0xF4 */ {2561, 8, 13, 10, 1, -12},
- /* 0xF5 */ {2574, 8, 13, 10, 1, -12},
- /* 0xF6 */ {2587, 8, 12, 10, 1, -11},
- /* 0xF7 */ {2599, 9, 8, 11, 1, -7},
- /* 0xF8 */ {2608, 8, 10, 10, 1, -9},
- /* 0xF9 */ {2618, 8, 13, 10, 1, -12},
- /* 0xFA */ {2631, 8, 13, 10, 1, -12},
- /* 0xFB */ {2644, 8, 13, 10, 1, -12},
- /* 0xFC */ {2657, 8, 12, 10, 1, -11},
- /* 0xFD */ {2669, 8, 17, 9, 0, -12},
- /* 0xFE */ {2686, 9, 16, 10, 1, -12},
- /* 0xFF */ {2704, 8, 16, 9, 0, -11},
+/* 0x01 */ { 0, 15, 15, 17, 1, -13 },
+/* 0x02 */ { 29, 15, 15, 17, 1, -13 },
+/* 0x03 */ { 58, 15, 16, 17, 1, -14 },
+/* 0x04 */ { 88, 15, 16, 17, 1, -14 },
+/* 0x05 */ { 118, 16, 15, 18, 1, -13 },
+/* 0x06 */ { 148, 15, 15, 17, 1, -13 },
+/* 0x07 */ { 177, 0, 0, 8, 0, 0 },
+/* 0x08 */ { 177, 17, 16, 19, 1, -14 },
+/* 0x09 */ { 211, 17, 12, 19, 1, -12 },
+/* 0x0A */ { 237, 0, 0, 8, 0, 0 },
+/* 0x0B */ { 237, 17, 16, 19, 1, -14 },
+/* 0x0C */ { 271, 15, 14, 17, 1, -12 },
+/* 0x0D */ { 298, 0, 0, 8, 0, 0 },
+/* 0x0E */ { 298, 15, 16, 17, 1, -14 },
+/* 0x0F */ { 328, 15, 15, 17, 1, -13 },
+/* 0x10 */ { 357, 15, 15, 17, 1, -13 },
+/* 0x11 */ { 386, 15, 16, 17, 1, -14 },
+/* 0x12 */ { 416, 17, 17, 19, 1, -15 },
+/* 0x13 */ { 453, 15, 16, 17, 1, -14 },
+/* 0x14 */ { 483, 15, 16, 17, 1, -14 },
+/* 0x15 */ { 513, 15, 16, 17, 1, -14 },
+/* 0x16 */ { 543, 11, 16, 13, 1, -14 },
+/* 0x17 */ { 565, 15, 16, 17, 1, -14 },
+/* 0x18 */ { 595, 18, 15, 20, 1, -13 },
+/* 0x19 */ { 629, 15, 16, 17, 1, -14 },
+/* 0x1A */ { 659, 13, 14, 15, 1, -12 },
+/* 0x1B */ { 682, 17, 16, 19, 1, -14 },
+/* 0x1C */ { 716, 15, 16, 17, 1, -14 },
+/* 0x1D */ { 746, 15, 15, 17, 1, -13 },
+/* 0x1E */ { 775, 17, 16, 19, 1, -14 },
+/* 0x1F */ { 809, 11, 16, 13, 1, -14 },
+/* ' ' 0x20 */ { 831, 0, 0, 5, 0, 0 },
+/* '!' 0x21 */ { 831, 2, 13, 6, 2, -12 },
+/* '"' 0x22 */ { 835, 5, 4, 6, 1, -12 },
+/* '#' 0x23 */ { 838, 10, 12, 10, 0, -11 },
+/* '$' 0x24 */ { 853, 9, 16, 10, 1, -13 },
+/* '%' 0x25 */ { 871, 16, 13, 16, 1, -12 },
+/* '&' 0x26 */ { 897, 10, 13, 12, 1, -12 },
+/* ''' 0x27 */ { 914, 2, 4, 4, 1, -12 },
+/* '(' 0x28 */ { 915, 4, 17, 6, 1, -12 },
+/* ')' 0x29 */ { 924, 4, 17, 6, 1, -12 },
+/* '*' 0x2A */ { 933, 5, 5, 7, 1, -12 },
+/* '+' 0x2B */ { 937, 6, 8, 11, 3, -7 },
+/* ',' 0x2C */ { 943, 2, 4, 5, 2, 0 },
+/* '-' 0x2D */ { 944, 4, 1, 6, 1, -4 },
+/* '.' 0x2E */ { 945, 2, 1, 5, 1, 0 },
+/* '/' 0x2F */ { 946, 5, 13, 5, 0, -12 },
+/* '0' 0x30 */ { 955, 8, 13, 10, 1, -12 },
+/* '1' 0x31 */ { 968, 4, 13, 10, 3, -12 },
+/* '2' 0x32 */ { 975, 9, 13, 10, 1, -12 },
+/* '3' 0x33 */ { 990, 8, 13, 10, 1, -12 },
+/* '4' 0x34 */ { 1003, 7, 13, 10, 2, -12 },
+/* '5' 0x35 */ { 1015, 9, 13, 10, 1, -12 },
+/* '6' 0x36 */ { 1030, 9, 13, 10, 1, -12 },
+/* '7' 0x37 */ { 1045, 8, 13, 10, 0, -12 },
+/* '8' 0x38 */ { 1058, 9, 13, 10, 1, -12 },
+/* '9' 0x39 */ { 1073, 8, 13, 10, 1, -12 },
+/* ':' 0x3A */ { 1086, 2, 10, 5, 1, -9 },
+/* ';' 0x3B */ { 1089, 3, 12, 5, 1, -8 },
+/* '<' 0x3C */ { 1094, 9, 9, 11, 1, -8 },
+/* '=' 0x3D */ { 1105, 9, 4, 11, 1, -5 },
+/* '>' 0x3E */ { 1110, 9, 8, 11, 1, -7 },
+/* '?' 0x3F */ { 1119, 9, 13, 10, 1, -12 },
+/* '@' 0x40 */ { 1134, 17, 16, 18, 1, -12 },
+/* 'A' 0x41 */ { 1168, 12, 13, 12, 0, -12 },
+/* 'B' 0x42 */ { 1188, 11, 13, 12, 1, -12 },
+/* 'C' 0x43 */ { 1206, 11, 13, 13, 1, -12 },
+/* 'D' 0x44 */ { 1224, 11, 13, 13, 1, -12 },
+/* 'E' 0x45 */ { 1242, 9, 13, 11, 1, -12 },
+/* 'F' 0x46 */ { 1257, 8, 13, 11, 1, -12 },
+/* 'G' 0x47 */ { 1270, 12, 13, 14, 1, -12 },
+/* 'H' 0x48 */ { 1290, 11, 13, 13, 1, -12 },
+/* 'I' 0x49 */ { 1308, 2, 13, 5, 2, -12 },
+/* 'J' 0x4A */ { 1312, 7, 13, 10, 1, -12 },
+/* 'K' 0x4B */ { 1324, 10, 13, 12, 1, -12 },
+/* 'L' 0x4C */ { 1341, 8, 13, 10, 1, -12 },
+/* 'M' 0x4D */ { 1354, 13, 13, 15, 1, -12 },
+/* 'N' 0x4E */ { 1376, 11, 13, 13, 1, -12 },
+/* 'O' 0x4F */ { 1394, 13, 13, 14, 1, -12 },
+/* 'P' 0x50 */ { 1416, 10, 13, 12, 1, -12 },
+/* 'Q' 0x51 */ { 1433, 13, 14, 14, 1, -12 },
+/* 'R' 0x52 */ { 1456, 12, 13, 13, 1, -12 },
+/* 'S' 0x53 */ { 1476, 10, 13, 12, 1, -12 },
+/* 'T' 0x54 */ { 1493, 9, 13, 11, 1, -12 },
+/* 'U' 0x55 */ { 1508, 11, 13, 13, 1, -12 },
+/* 'V' 0x56 */ { 1526, 11, 13, 11, 0, -12 },
+/* 'W' 0x57 */ { 1544, 16, 13, 17, 0, -12 },
+/* 'X' 0x58 */ { 1570, 10, 13, 12, 1, -12 },
+/* 'Y' 0x59 */ { 1587, 12, 13, 12, 0, -12 },
+/* 'Z' 0x5A */ { 1607, 10, 13, 11, 1, -12 },
+/* '[' 0x5B */ { 1624, 3, 17, 5, 1, -12 },
+/* '\' 0x5C */ { 1631, 5, 13, 5, 0, -12 },
+/* ']' 0x5D */ { 1640, 3, 17, 5, 0, -12 },
+/* '^' 0x5E */ { 1647, 7, 7, 8, 1, -12 },
+/* '_' 0x5F */ { 1654, 10, 1, 10, 0, 3 },
+/* '`' 0x60 */ { 1656, 4, 3, 5, 0, -12 },
+/* 'a' 0x61 */ { 1658, 9, 10, 10, 1, -9 },
+/* 'b' 0x62 */ { 1670, 9, 13, 10, 1, -12 },
+/* 'c' 0x63 */ { 1685, 8, 10, 9, 1, -9 },
+/* 'd' 0x64 */ { 1695, 8, 13, 10, 1, -12 },
+/* 'e' 0x65 */ { 1708, 8, 10, 10, 1, -9 },
+/* 'f' 0x66 */ { 1718, 4, 13, 5, 1, -12 },
+/* 'g' 0x67 */ { 1725, 8, 14, 10, 1, -9 },
+/* 'h' 0x68 */ { 1739, 8, 13, 10, 1, -12 },
+/* 'i' 0x69 */ { 1752, 2, 13, 4, 1, -12 },
+/* 'j' 0x6A */ { 1756, 4, 17, 4, 0, -12 },
+/* 'k' 0x6B */ { 1765, 8, 13, 9, 1, -12 },
+/* 'l' 0x6C */ { 1778, 2, 13, 4, 1, -12 },
+/* 'm' 0x6D */ { 1782, 13, 10, 15, 1, -9 },
+/* 'n' 0x6E */ { 1799, 8, 10, 10, 1, -9 },
+/* 'o' 0x6F */ { 1809, 8, 10, 10, 1, -9 },
+/* 'p' 0x70 */ { 1819, 9, 13, 10, 1, -9 },
+/* 'q' 0x71 */ { 1834, 8, 13, 10, 1, -9 },
+/* 'r' 0x72 */ { 1847, 5, 10, 6, 1, -9 },
+/* 's' 0x73 */ { 1854, 8, 10, 9, 1, -9 },
+/* 't' 0x74 */ { 1864, 4, 12, 5, 1, -11 },
+/* 'u' 0x75 */ { 1870, 8, 10, 10, 1, -9 },
+/* 'v' 0x76 */ { 1880, 9, 10, 9, 0, -9 },
+/* 'w' 0x77 */ { 1892, 13, 10, 13, 0, -9 },
+/* 'x' 0x78 */ { 1909, 7, 10, 9, 1, -9 },
+/* 'y' 0x79 */ { 1918, 8, 14, 9, 0, -9 },
+/* 'z' 0x7A */ { 1932, 7, 10, 9, 1, -9 },
+/* '{' 0x7B */ { 1941, 4, 17, 6, 1, -12 },
+/* '|' 0x7C */ { 1950, 2, 17, 4, 2, -12 },
+/* '}' 0x7D */ { 1955, 4, 17, 6, 1, -12 },
+/* '~' 0x7E */ { 1964, 7, 3, 9, 1, -7 },
+/* 0x7F */ { 1967, 0, 0, 0, 0, 0 },
+/* 0x80 */ { 1967, 10, 13, 12, 1, -12 },
+/* 0x81 */ { 1984, 0, 0, 8, 0, 0 },
+/* 0x82 */ { 1984, 2, 3, 5, 1, 0 },
+/* 0x83 */ { 1985, 5, 17, 5, 0, -12 },
+/* 0x84 */ { 1996, 5, 3, 7, 1, 0 },
+/* 0x85 */ { 1998, 10, 1, 12, 1, 0 },
+/* 0x86 */ { 2000, 8, 16, 10, 1, -12 },
+/* 0x87 */ { 2016, 8, 16, 10, 1, -12 },
+/* 0x88 */ { 2032, 5, 3, 6, 0, -12 },
+/* 0x89 */ { 2034, 18, 13, 18, 0, -12 },
+/* 0x8A */ { 2064, 10, 16, 12, 1, -15 },
+/* 0x8B */ { 2084, 2, 4, 4, 1, -6 },
+/* 0x8C */ { 2085, 15, 13, 18, 1, -12 },
+/* 0x8D */ { 2110, 0, 0, 8, 0, 0 },
+/* 0x8E */ { 2110, 10, 16, 11, 1, -15 },
+/* 0x8F */ { 2130, 0, 0, 8, 0, 0 },
+/* 0x90 */ { 2130, 0, 0, 8, 0, 0 },
+/* 0x91 */ { 2130, 2, 4, 4, 2, -12 },
+/* 0x92 */ { 2131, 2, 4, 4, 1, -12 },
+/* 0x93 */ { 2132, 5, 4, 7, 2, -12 },
+/* 0x94 */ { 2135, 5, 4, 7, 1, -12 },
+/* 0x95 */ { 2138, 4, 5, 7, 1, -8 },
+/* 0x96 */ { 2141, 7, 1, 9, 1, -4 },
+/* 0x97 */ { 2142, 16, 1, 18, 1, -4 },
+/* 0x98 */ { 2144, 5, 2, 6, 0, -12 },
+/* 0x99 */ { 2146, 18, 10, 18, 1, -13 },
+/* 0x9A */ { 2169, 8, 13, 9, 1, -12 },
+/* 0x9B */ { 2182, 2, 4, 5, 2, -6 },
+/* 0x9C */ { 2183, 15, 10, 17, 1, -9 },
+/* 0x9D */ { 2202, 0, 0, 8, 0, 0 },
+/* 0x9E */ { 2202, 7, 13, 9, 1, -12 },
+/* 0x9F */ { 2214, 12, 14, 12, 0, -13 },
+/* 0xA0 */ { 2235, 0, 0, 5, 0, 0 },
+/* 0xA1 */ { 2235, 2, 13, 6, 2, -8 },
+/* 0xA2 */ { 2239, 9, 14, 10, 1, -11 },
+/* 0xA3 */ { 2255, 10, 13, 10, 0, -12 },
+/* 0xA4 */ { 2272, 7, 6, 10, 2, -8 },
+/* 0xA5 */ { 2278, 8, 13, 10, 1, -12 },
+/* 0xA6 */ { 2291, 2, 17, 5, 2, -12 },
+/* 0xA7 */ { 2296, 9, 17, 10, 1, -12 },
+/* 0xA8 */ { 2316, 6, 1, 6, 0, -11 },
+/* 0xA9 */ { 2317, 14, 13, 14, 1, -12 },
+/* 0xAA */ { 2340, 5, 8, 7, 1, -12 },
+/* 0xAB */ { 2345, 7, 6, 9, 1, -7 },
+/* 0xAC */ { 2351, 9, 5, 11, 2, -5 },
+/* 0xAD */ { 2357, 0, 0, 0, 0, 0 },
+/* 0xAE */ { 2357, 14, 13, 14, 1, -12 },
+/* 0xAF */ { 2380, 5, 1, 6, 0, -12 },
+/* 0xB0 */ { 2381, 5, 5, 11, 3, -11 },
+/* 0xB1 */ { 2385, 9, 11, 11, 1, -10 },
+/* 0xB2 */ { 2398, 6, 8, 6, 1, -13 },
+/* 0xB3 */ { 2404, 7, 8, 6, 0, -13 },
+/* 0xB4 */ { 2411, 4, 3, 6, 2, -12 },
+/* 0xB5 */ { 2413, 9, 13, 10, 1, -9 },
+/* 0xB6 */ { 2428, 8, 16, 10, 2, -12 },
+/* 0xB7 */ { 2444, 3, 1, 5, 1, -4 },
+/* 0xB8 */ { 2445, 5, 4, 6, 1, 1 },
+/* 0xB9 */ { 2448, 3, 7, 6, 2, -13 },
+/* 0xBA */ { 2451, 5, 8, 7, 1, -12 },
+/* 0xBB */ { 2456, 7, 6, 9, 1, -7 },
+/* 0xBC */ { 2462, 14, 13, 16, 2, -12 },
+/* 0xBD */ { 2485, 14, 13, 16, 2, -12 },
+/* 0xBE */ { 2508, 15, 13, 16, 1, -12 },
+/* 0xBF */ { 2533, 9, 13, 10, 1, -8 },
+/* 0xC0 */ { 2548, 10, 14, 12, 1, -13 },
+/* 0xC1 */ { 2566, 10, 14, 12, 1, -13 },
+/* 0xC2 */ { 2584, 10, 14, 12, 1, -13 },
+/* 0xC3 */ { 2602, 10, 14, 12, 1, -13 },
+/* 0xC4 */ { 2620, 10, 14, 12, 1, -13 },
+/* 0xC5 */ { 2638, 10, 14, 12, 1, -13 },
+/* 0xC6 */ { 2656, 16, 13, 18, 1, -12 },
+/* 0xC7 */ { 2682, 11, 17, 13, 1, -12 },
+/* 0xC8 */ { 2706, 9, 14, 11, 1, -13 },
+/* 0xC9 */ { 2722, 9, 14, 11, 1, -13 },
+/* 0xCA */ { 2738, 9, 14, 11, 1, -13 },
+/* 0xCB */ { 2754, 9, 14, 11, 1, -13 },
+/* 0xCC */ { 2770, 3, 15, 5, 1, -13 },
+/* 0xCD */ { 2776, 3, 14, 5, 1, -13 },
+/* 0xCE */ { 2782, 5, 14, 5, 0, -13 },
+/* 0xCF */ { 2791, 6, 14, 5, 0, -13 },
+/* 0xD0 */ { 2802, 11, 13, 13, 1, -12 },
+/* 0xD1 */ { 2820, 11, 14, 13, 1, -13 },
+/* 0xD2 */ { 2840, 12, 15, 13, 1, -14 },
+/* 0xD3 */ { 2863, 12, 15, 13, 1, -14 },
+/* 0xD4 */ { 2886, 12, 15, 13, 1, -14 },
+/* 0xD5 */ { 2909, 12, 15, 13, 1, -14 },
+/* 0xD6 */ { 2932, 12, 15, 13, 1, -14 },
+/* 0xD7 */ { 2955, 7, 7, 11, 2, -7 },
+/* 0xD8 */ { 2962, 13, 13, 14, 1, -12 },
+/* 0xD9 */ { 2984, 11, 14, 13, 1, -13 },
+/* 0xDA */ { 3004, 11, 14, 13, 1, -13 },
+/* 0xDB */ { 3024, 11, 14, 13, 1, -13 },
+/* 0xDC */ { 3044, 11, 14, 13, 1, -13 },
+/* 0xDD */ { 3064, 12, 14, 12, 0, -13 },
+/* 0xDE */ { 3085, 10, 13, 12, 1, -12 },
+/* 0xDF */ { 3102, 9, 13, 11, 1, -12 },
+/* 0xE0 */ { 3117, 9, 13, 10, 1, -12 },
+/* 0xE1 */ { 3132, 9, 13, 10, 1, -12 },
+/* 0xE2 */ { 3147, 9, 13, 10, 1, -12 },
+/* 0xE3 */ { 3162, 9, 13, 10, 1, -12 },
+/* 0xE4 */ { 3177, 9, 12, 10, 1, -11 },
+/* 0xE5 */ { 3191, 9, 14, 10, 1, -13 },
+/* 0xE6 */ { 3207, 15, 10, 16, 1, -9 },
+/* 0xE7 */ { 3226, 8, 14, 9, 1, -9 },
+/* 0xE8 */ { 3240, 8, 13, 10, 1, -12 },
+/* 0xE9 */ { 3253, 8, 13, 10, 1, -12 },
+/* 0xEA */ { 3266, 8, 13, 10, 1, -12 },
+/* 0xEB */ { 3279, 8, 12, 10, 1, -11 },
+/* 0xEC */ { 3291, 3, 13, 4, 0, -12 },
+/* 0xED */ { 3296, 3, 13, 4, 1, -12 },
+/* 0xEE */ { 3301, 4, 13, 5, 0, -12 },
+/* 0xEF */ { 3308, 6, 12, 5, -1, -11 },
+/* 0xF0 */ { 3317, 8, 13, 10, 1, -12 },
+/* 0xF1 */ { 3330, 8, 13, 10, 1, -12 },
+/* 0xF2 */ { 3343, 8, 13, 10, 1, -12 },
+/* 0xF3 */ { 3356, 8, 13, 10, 1, -12 },
+/* 0xF4 */ { 3369, 8, 13, 10, 1, -12 },
+/* 0xF5 */ { 3382, 8, 13, 10, 1, -12 },
+/* 0xF6 */ { 3395, 8, 12, 10, 1, -11 },
+/* 0xF7 */ { 3407, 9, 8, 11, 1, -7 },
+/* 0xF8 */ { 3416, 8, 10, 10, 1, -9 },
+/* 0xF9 */ { 3426, 8, 13, 10, 1, -12 },
+/* 0xFA */ { 3439, 8, 13, 10, 1, -12 },
+/* 0xFB */ { 3452, 8, 13, 10, 1, -12 },
+/* 0xFC */ { 3465, 8, 12, 10, 1, -11 },
+/* 0xFD */ { 3477, 8, 17, 9, 0, -12 },
+/* 0xFE */ { 3494, 9, 16, 10, 1, -12 },
+/* 0xFF */ { 3512, 8, 16, 9, 0, -11 },
};
-const GFXfont FreeSans9pt_Win1252 PROGMEM = {(uint8_t *)FreeSans9pt_Win1252Bitmaps, (GFXglyph *)FreeSans9pt_Win1252Glyphs, 0x20,
- 0xFF, 21};
+const GFXfont FreeSans9pt_Win1252 PROGMEM = {
+(uint8_t*)FreeSans9pt_Win1252Bitmaps,
+(GFXglyph*)FreeSans9pt_Win1252Glyphs,
+0x01, 0xFF, 16
+};
diff --git a/src/graphics/niche/InkHUD/Applet.cpp b/src/graphics/niche/InkHUD/Applet.cpp
index f63bd4bbe..1e89ebe1b 100644
--- a/src/graphics/niche/InkHUD/Applet.cpp
+++ b/src/graphics/niche/InkHUD/Applet.cpp
@@ -8,8 +8,9 @@
using namespace NicheGraphics;
-InkHUD::AppletFont InkHUD::Applet::fontLarge; // General purpose font. Set by setDefaultFonts
-InkHUD::AppletFont InkHUD::Applet::fontSmall; // General purpose font. Set by setDefaultFonts
+InkHUD::AppletFont InkHUD::Applet::fontLarge; // General purpose fonts. Set in nicheGraphics.h
+InkHUD::AppletFont InkHUD::Applet::fontMedium;
+InkHUD::AppletFont InkHUD::Applet::fontSmall;
constexpr float InkHUD::Applet::LOGO_ASPECT_RATIO; // Ratio of the Meshtastic logo
InkHUD::Applet::Applet() : GFX(0, 0)
@@ -353,10 +354,9 @@ std::string InkHUD::Applet::parseShortName(meshtastic_NodeInfoLite *node)
// Determine if all characters of a string are printable using the current font
bool InkHUD::Applet::isPrintable(std::string text)
{
- // Scan for DEL (0x7F), which is the value assigned by AppletFont::applyEncoding if a unicode character is not handled
- // Todo: move this to from DEL to SUB, once the fonts have been changed for this
+ // Scan for SUB (0x1A), which is the value assigned by AppletFont::applyEncoding if a unicode character is not handled
for (char &c : text) {
- if (c == '\x7F')
+ if (c == '\x1A')
return false;
}
diff --git a/src/graphics/niche/InkHUD/Applet.h b/src/graphics/niche/InkHUD/Applet.h
index c6a8a8aad..802186e6e 100644
--- a/src/graphics/niche/InkHUD/Applet.h
+++ b/src/graphics/niche/InkHUD/Applet.h
@@ -95,7 +95,7 @@ class Applet : public GFX
static uint16_t getHeaderHeight(); // How tall the "standard" applet header is
- static AppletFont fontSmall, fontLarge; // The general purpose fonts, used by all applets
+ static AppletFont fontSmall, fontMedium, fontLarge; // The general purpose fonts, used by all applets
const char *name = nullptr; // Shown in applet selection menu. Also used as an identifier by InkHUD::getSystemApplet
diff --git a/src/graphics/niche/InkHUD/AppletFont.cpp b/src/graphics/niche/InkHUD/AppletFont.cpp
index 88fb4054b..db7097f3f 100644
--- a/src/graphics/niche/InkHUD/AppletFont.cpp
+++ b/src/graphics/niche/InkHUD/AppletFont.cpp
@@ -616,9 +616,116 @@ char InkHUD::AppletFont::applyEncoding(std::string utf8)
}
}
- // If not handled, return DEL
- // Todo: swap this to SUB, and modify the fonts
- return '\x7F';
+ else /*ASCII or Unhandled*/ {
+ if (utf8.length() == 1)
+ return utf8.at(0);
+ }
+
+ // All single-byte (ASCII) characters should have been handled by now
+ // Only unhandled multi-byte UTF8 characters should remain
+ assert(utf8.length() > 1);
+
+ // Parse emoji
+ // Strip emoji modifiers
+ switch (toUtf32(utf8)) {
+ REMAP(0x1F44D, 0x01) // ๐ Thumbs Up
+ REMAP(0x1F44E, 0x02) // ๐ Thumbs Down
+
+ REMAP(0x1F60A, 0x03) // ๐ Smiling Face with Smiling Eyes
+ REMAP(0x1F642, 0x03) // ๐ Slightly Smiling Face
+ REMAP(0x1F601, 0x03) // ๐ Grinning Face with Smiling Eye
+
+ REMAP(0x1F602, 0x04) // ๐ Face with Tears of Joy
+ REMAP(0x1F923, 0x04) // ๐คฃ Rolling on the Floor Laughing
+ REMAP(0x1F606, 0x04) // ๐ Smiling with Open Mouth and Closed Eyes
+
+ REMAP(0x1F44B, 0x05) // ๐ Waving Hand
+
+ REMAP(0x02600, 0x06) // โ Sun
+ REMAP(0x1F31E, 0x06) // ๐ Sun with Face
+
+ // 0x07 - Bell character (unused)
+ REMAP(0x1F327, 0x08) // ๐ง๏ธ Cloud with Rain
+
+ REMAP(0x02601, 0x09) // โ๏ธ Cloud
+ REMAP(0x1F32B, 0x09) // Fog
+
+ REMAP(0x1F9E1, 0x0B) // ๐งก Orange Heart
+ REMAP(0x02763, 0x0B) // โฃ Heart Exclamation
+ REMAP(0x02764, 0x0B) // โค Heart
+ REMAP(0x1F495, 0x0B) // ๐ Two Hearts
+ REMAP(0x1F496, 0x0B) // ๐ Sparkling Heart
+ REMAP(0x1F497, 0x0B) // ๐ Growing Heart
+ REMAP(0x1F498, 0x0B) // ๐ Heart with Arrow
+
+ REMAP(0x1F4A9, 0x0C) // ๐ฉ Pile of Poo
+ // 0x0D - Carriage return (unused)
+ REMAP(0x1F514, 0x0E) // ๐ Bell
+
+ REMAP(0x1F62D, 0x0F) // ๐ญ Loudly Crying Face
+ REMAP(0x1F622, 0x0F) // ๐ข Crying Face
+
+ REMAP(0x1F64F, 0x10) // ๐ Person with Folded Hands
+ REMAP(0x1F618, 0x11) // ๐ Face Throwing a Kiss
+ REMAP(0x1F389, 0x12) // ๐ Party Popper
+
+ REMAP(0x1F600, 0x13) // ๐ Grinning Face
+ REMAP(0x1F603, 0x13) // ๐ Smiling Face with Open Mouth
+ REMAP(0x1F604, 0x13) // ๐ Smiling Face with Open Mouth and Smiling Eyes
+
+ REMAP(0x1F97A, 0x14) // ๐ฅบ Face with Pleading Eyes
+ REMAP(0x1F605, 0x15) // ๐
Smiling with Sweat
+ REMAP(0x1F525, 0x16) // ๐ฅ Fire
+ REMAP(0x1F926, 0x17) // ๐คฆ Face Palm
+ REMAP(0x1F937, 0x18) // ๐คท Shrug
+ REMAP(0x1F644, 0x19) // ๐ Face with Rolling Eyes
+ // 0x1A Substitution (unused)
+ REMAP(0x1F917, 0x1B) // ๐ค Hugging Face
+
+ REMAP(0x1F609, 0x1C) // ๐ Winking Face
+ REMAP(0x1F61C, 0x1C) // ๐ Face with Stuck-Out Tongue and Winking Eye
+ REMAP(0x1F60F, 0x1C) // ๐ Smirking Face
+
+ REMAP(0x1F914, 0x1D) // ๐ค Thinking Face
+ REMAP(0x1FAE1, 0x1E) // ๐ซก Saluting Face
+ REMAP(0x1F44C, 0x1F) // ๐ OK Hand Sign
+
+ REMAP(0x02755, '!') // โ
+ REMAP(0x02757, '!') // โ
+ REMAP(0x0203C, '!') // โผ
+ REMAP(0x02753, '?') // โ
+ REMAP(0x02754, '?') // โ
+ REMAP(0x02049, '?') // โ
+
+ // Modifiers (deleted)
+ REMAP(0x02640, 0x7F) // Gender
+ REMAP(0x02642, 0x7F)
+ REMAP(0x1F3FB, 0x7F) // Skin Tones
+ REMAP(0x1F3FC, 0x7F)
+ REMAP(0x1F3FD, 0x7F)
+ REMAP(0x1F3FE, 0x7F)
+ REMAP(0x1F3FF, 0x7F)
+ REMAP(0x0FE00, 0x7F) // Variation Selectors
+ REMAP(0x0FE01, 0x7F)
+ REMAP(0x0FE02, 0x7F)
+ REMAP(0x0FE03, 0x7F)
+ REMAP(0x0FE04, 0x7F)
+ REMAP(0x0FE05, 0x7F)
+ REMAP(0x0FE06, 0x7F)
+ REMAP(0x0FE07, 0x7F)
+ REMAP(0x0FE08, 0x7F)
+ REMAP(0x0FE09, 0x7F)
+ REMAP(0x0FE0A, 0x7F)
+ REMAP(0x0FE0B, 0x7F)
+ REMAP(0x0FE0C, 0x7F)
+ REMAP(0x0FE0D, 0x7F)
+ REMAP(0x0FE0E, 0x7F)
+ REMAP(0x0FE0F, 0x7F)
+ REMAP(0x0200D, 0x7F) // Zero Width Joiner
+ }
+
+ // If not handled, return SUB
+ return '\x1A';
// Sweep up the syntactic sugar
// Don't want ants in the house
diff --git a/src/graphics/niche/InkHUD/AppletFont.h b/src/graphics/niche/InkHUD/AppletFont.h
index 67348b8d3..e1fe37974 100644
--- a/src/graphics/niche/InkHUD/AppletFont.h
+++ b/src/graphics/niche/InkHUD/AppletFont.h
@@ -61,20 +61,26 @@ class AppletFont
// Line padding has been adjusted manually, to compensate for a few *extra tall* diacritics
// Central European
+#include "graphics/niche/Fonts/FreeSans12pt_Win1250.h"
#include "graphics/niche/Fonts/FreeSans6pt_Win1250.h"
#include "graphics/niche/Fonts/FreeSans9pt_Win1250.h"
+#define FREESANS_12PT_WIN1250 InkHUD::AppletFont(FreeSans12pt_Win1250, InkHUD::AppletFont::WINDOWS_1250, -3, 1)
#define FREESANS_9PT_WIN1250 InkHUD::AppletFont(FreeSans9pt_Win1250, InkHUD::AppletFont::WINDOWS_1250, -1, -1)
#define FREESANS_6PT_WIN1250 InkHUD::AppletFont(FreeSans6pt_Win1250, InkHUD::AppletFont::WINDOWS_1250, -1, -2)
// Cyrillic
+#include "graphics/niche/Fonts/FreeSans12pt_Win1251.h"
#include "graphics/niche/Fonts/FreeSans6pt_Win1251.h"
#include "graphics/niche/Fonts/FreeSans9pt_Win1251.h"
+#define FREESANS_12PT_WIN1251 InkHUD::AppletFont(FreeSans12pt_Win1251, InkHUD::AppletFont::WINDOWS_1251, -3, 1)
#define FREESANS_9PT_WIN1251 InkHUD::AppletFont(FreeSans9pt_Win1251, InkHUD::AppletFont::WINDOWS_1251, -2, -1)
#define FREESANS_6PT_WIN1251 InkHUD::AppletFont(FreeSans6pt_Win1251, InkHUD::AppletFont::WINDOWS_1251, -1, -2)
// Western European
+#include "graphics/niche/Fonts/FreeSans12pt_Win1252.h"
#include "graphics/niche/Fonts/FreeSans6pt_Win1252.h"
#include "graphics/niche/Fonts/FreeSans9pt_Win1252.h"
+#define FREESANS_12PT_WIN1252 InkHUD::AppletFont(FreeSans12pt_Win1252, InkHUD::AppletFont::WINDOWS_1252, -3, 1)
#define FREESANS_9PT_WIN1252 InkHUD::AppletFont(FreeSans9pt_Win1252, InkHUD::AppletFont::WINDOWS_1252, -2, -1)
#define FREESANS_6PT_WIN1252 InkHUD::AppletFont(FreeSans6pt_Win1252, InkHUD::AppletFont::WINDOWS_1252, -1, -2)
diff --git a/src/graphics/niche/InkHUD/Applets/Bases/NodeList/NodeListApplet.cpp b/src/graphics/niche/InkHUD/Applets/Bases/NodeList/NodeListApplet.cpp
index 7fa31b244..1b0bfa9d0 100644
--- a/src/graphics/niche/InkHUD/Applets/Bases/NodeList/NodeListApplet.cpp
+++ b/src/graphics/niche/InkHUD/Applets/Bases/NodeList/NodeListApplet.cpp
@@ -168,11 +168,11 @@ void InkHUD::NodeListApplet::onRender()
// Define two lines of text for the card
// We will center our text on these lines
- uint16_t lineAY = cardTopY + (fontLarge.lineHeight() / 2);
- uint16_t lineBY = cardTopY + fontLarge.lineHeight() + (fontSmall.lineHeight() / 2);
+ uint16_t lineAY = cardTopY + (fontMedium.lineHeight() / 2);
+ uint16_t lineBY = cardTopY + fontMedium.lineHeight() + (fontSmall.lineHeight() / 2);
// Print the short name
- setFont(fontLarge);
+ setFont(fontMedium);
printAt(0, lineAY, shortName, LEFT, MIDDLE);
// Print the distance
@@ -182,8 +182,8 @@ void InkHUD::NodeListApplet::onRender()
// If we have a direct connection to the node, draw the signal indicator
if (hopsAway == 0 && signal != SIGNAL_UNKNOWN) {
uint16_t signalW = getTextWidth("Xkm"); // Indicator should be similar width to distance label
- uint16_t signalH = fontLarge.lineHeight() * 0.75;
- int16_t signalY = lineAY + (fontLarge.lineHeight() / 2) - (fontLarge.lineHeight() * 0.75);
+ uint16_t signalH = fontMedium.lineHeight() * 0.75;
+ int16_t signalY = lineAY + (fontMedium.lineHeight() / 2) - (fontMedium.lineHeight() * 0.75);
int16_t signalX = width() - signalW;
drawSignalIndicator(signalX, signalY, signalW, signalH, signal);
}
diff --git a/src/graphics/niche/InkHUD/Applets/Bases/NodeList/NodeListApplet.h b/src/graphics/niche/InkHUD/Applets/Bases/NodeList/NodeListApplet.h
index 0abcad824..c2340027b 100644
--- a/src/graphics/niche/InkHUD/Applets/Bases/NodeList/NodeListApplet.h
+++ b/src/graphics/niche/InkHUD/Applets/Bases/NodeList/NodeListApplet.h
@@ -65,8 +65,8 @@ class NodeListApplet : public Applet, public MeshModule
// Card Dimensions
// - for rendering and for maxCards calc
- const uint8_t cardMarginH = fontSmall.lineHeight() / 2; // Gap between cards
- const uint16_t cardH = fontLarge.lineHeight() + fontSmall.lineHeight() + cardMarginH; // Height of card
+ uint8_t cardMarginH = fontSmall.lineHeight() / 2; // Gap between cards
+ uint16_t cardH = fontMedium.lineHeight() + fontSmall.lineHeight() + cardMarginH; // Height of card
};
} // namespace NicheGraphics::InkHUD
diff --git a/src/graphics/niche/InkHUD/Applets/System/Logo/LogoApplet.cpp b/src/graphics/niche/InkHUD/Applets/System/Logo/LogoApplet.cpp
index fa85deab3..ecaa7cea3 100644
--- a/src/graphics/niche/InkHUD/Applets/System/Logo/LogoApplet.cpp
+++ b/src/graphics/niche/InkHUD/Applets/System/Logo/LogoApplet.cpp
@@ -14,9 +14,10 @@ InkHUD::LogoApplet::LogoApplet() : concurrency::OSThread("LogoApplet")
// During onboarding, show the default short name as well as the version string
// This behavior assists manufacturers during mass production, and should not be modified without good reason
if (!settings->tips.safeShutdownSeen) {
- fontTitle = fontLarge;
+ meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum());
+ fontTitle = fontMedium;
textLeft = xstr(APP_VERSION_SHORT);
- textRight = owner.short_name;
+ textRight = parseShortName(ourNode);
textTitle = "Meshtastic";
} else {
fontTitle = fontSmall;
@@ -51,6 +52,40 @@ void InkHUD::LogoApplet::onRender()
setTextColor(WHITE);
}
+#ifdef USERPREFS_OEM_IMAGE_DATA // Custom boot screen, if defined in userPrefs.jsonc
+
+ // Only show the custom screen at startup
+ // This allows us to draw the usual Meshtastic logo at shutdown
+ // The effect is similar to the two-stage userPrefs boot screen used by BaseUI
+ if (millis() < 10 * 1000UL) {
+
+ // Draw the custom logo
+ const uint8_t logo[] = USERPREFS_OEM_IMAGE_DATA;
+ drawXBitmap(logoCX - (USERPREFS_OEM_IMAGE_WIDTH / 2), // Left
+ logoCY - (USERPREFS_OEM_IMAGE_HEIGHT / 2), // Top
+ logo, // XBM data
+ USERPREFS_OEM_IMAGE_WIDTH, // Width
+ USERPREFS_OEM_IMAGE_HEIGHT, // Height
+ inverted ? WHITE : BLACK // Color
+ );
+
+ // Select the largest font which will still comfortably fit the custom text
+ setFont(fontLarge);
+ if (getTextWidth(USERPREFS_OEM_TEXT) > 0.8 * width())
+ setFont(fontMedium);
+ if (getTextWidth(USERPREFS_OEM_TEXT) > 0.8 * width())
+ setFont(fontSmall);
+
+ // Draw custom text below logo
+ int16_t logoB = logoCY + (USERPREFS_OEM_IMAGE_HEIGHT / 2); // Bottom of the logo
+ printAt(X(0.5), logoB + Y(0.1), USERPREFS_OEM_TEXT, CENTER, TOP);
+
+ // Don't draw the normal boot screen, we've already drawn our custom version
+ return;
+ }
+
+#endif
+
drawLogo(logoCX, logoCY, logoW, logoH, inverted ? WHITE : BLACK);
if (!textLeft.empty()) {
@@ -115,7 +150,7 @@ void InkHUD::LogoApplet::onShutdown()
textLeft = "";
textRight = "";
textTitle = parseShortName(ourNode);
- fontTitle = fontLarge;
+ fontTitle = fontMedium;
// This is then drawn by InkHUD::Events::onShutdown, with a blocking FULL update, after InkHUD's flash write is complete
}
diff --git a/src/graphics/niche/InkHUD/Applets/System/Menu/MenuAction.h b/src/graphics/niche/InkHUD/Applets/System/Menu/MenuAction.h
index f162aa385..c84ee09e0 100644
--- a/src/graphics/niche/InkHUD/Applets/System/Menu/MenuAction.h
+++ b/src/graphics/niche/InkHUD/Applets/System/Menu/MenuAction.h
@@ -19,6 +19,8 @@ namespace NicheGraphics::InkHUD
enum MenuAction {
NO_ACTION,
SEND_PING,
+ STORE_CANNEDMESSAGE_SELECTION,
+ SEND_CANNEDMESSAGE,
SHUTDOWN,
NEXT_TILE,
TOGGLE_BACKLIGHT,
@@ -31,6 +33,7 @@ enum MenuAction {
LAYOUT,
TOGGLE_BATTERY_ICON,
TOGGLE_NOTIFICATIONS,
+ TOGGLE_INVERT_COLOR,
TOGGLE_12H_CLOCK,
};
diff --git a/src/graphics/niche/InkHUD/Applets/System/Menu/MenuApplet.cpp b/src/graphics/niche/InkHUD/Applets/System/Menu/MenuApplet.cpp
index 9fdfad8ee..a1f79a28f 100644
--- a/src/graphics/niche/InkHUD/Applets/System/Menu/MenuApplet.cpp
+++ b/src/graphics/niche/InkHUD/Applets/System/Menu/MenuApplet.cpp
@@ -5,6 +5,7 @@
#include "RTC.h"
#include "MeshService.h"
+#include "Router.h"
#include "airtime.h"
#include "main.h"
#include "power.h"
@@ -31,6 +32,12 @@ InkHUD::MenuApplet::MenuApplet() : concurrency::OSThread("MenuApplet")
if (settings->optionalMenuItems.backlight) {
backlight = Drivers::LatchingBacklight::getInstance();
}
+
+ // Initialize the Canned Message store
+ // This is a shared nicheGraphics component
+ // - handles loading & parsing the canned messages
+ // - handles setting / getting of canned messages via apps (Client API Admin Messages)
+ cm.store = CannedMessageStore::getInstance();
}
void InkHUD::MenuApplet::onForeground()
@@ -65,6 +72,10 @@ void InkHUD::MenuApplet::onForeground()
void InkHUD::MenuApplet::onBackground()
{
+ // Discard any data we generated while selecting a canned message
+ // Frees heap mem
+ freeCannedMessageResources();
+
// If device has a backlight which isn't controlled by aux button:
// Item in options submenu allows keeping backlight on after menu is closed
// If this item is deselected we will turn backlight off again, now that menu is closing
@@ -153,6 +164,16 @@ void InkHUD::MenuApplet::execute(MenuItem item)
inkhud->forceUpdate(Drivers::EInk::UpdateTypes::FULL);
break;
+ case STORE_CANNEDMESSAGE_SELECTION:
+ cm.selectedMessageItem = &cm.messageItems.at(cursor - 1); // Minus one: offset for the initial "Send Ping" entry
+ break;
+
+ case SEND_CANNEDMESSAGE:
+ cm.selectedRecipientItem = &cm.recipientItems.at(cursor);
+ sendText(cm.selectedRecipientItem->dest, cm.selectedRecipientItem->channelIndex, cm.selectedMessageItem->rawText.c_str());
+ inkhud->forceUpdate(Drivers::EInk::UpdateTypes::FULL); // Next refresh should be FULL. Lots of button pressing to get here
+ break;
+
case ROTATE:
inkhud->rotate();
break;
@@ -184,6 +205,15 @@ void InkHUD::MenuApplet::execute(MenuItem item)
settings->optionalFeatures.notifications = !settings->optionalFeatures.notifications;
break;
+ case TOGGLE_INVERT_COLOR:
+ if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_INVERTED)
+ config.display.displaymode = meshtastic_Config_DisplayConfig_DisplayMode_DEFAULT;
+ else
+ config.display.displaymode = meshtastic_Config_DisplayConfig_DisplayMode_INVERTED;
+
+ nodeDB->saveToDisk(SEGMENT_CONFIG);
+ break;
+
case SET_RECENTS:
// Set value of settings.recentlyActiveSeconds
// Uses menu cursor to read RECENTS_OPTIONS_MINUTES array (defined at top of this file)
@@ -260,9 +290,11 @@ void InkHUD::MenuApplet::showPage(MenuPage page)
break;
case SEND:
- items.push_back(MenuItem("Ping", MenuAction::SEND_PING, MenuPage::EXIT));
- // Todo: canned messages
- items.push_back(MenuItem("Exit", MenuPage::EXIT));
+ populateSendPage();
+ break;
+
+ case CANNEDMESSAGE_RECIPIENT:
+ populateRecipientPage();
break;
case OPTIONS:
@@ -293,6 +325,10 @@ void InkHUD::MenuApplet::showPage(MenuPage page)
&settings->optionalFeatures.notifications));
items.push_back(MenuItem("Battery Icon", MenuAction::TOGGLE_BATTERY_ICON, MenuPage::OPTIONS,
&settings->optionalFeatures.batteryIcon));
+
+ invertedColors = (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_INVERTED);
+ items.push_back(MenuItem("Invert Color", MenuAction::TOGGLE_INVERT_COLOR, MenuPage::OPTIONS, &invertedColors));
+
items.push_back(
MenuItem("12-Hour Clock", MenuAction::TOGGLE_12H_CLOCK, MenuPage::OPTIONS, &config.display.use_12h_clock));
items.push_back(MenuItem("Exit", MenuPage::EXIT));
@@ -497,6 +533,8 @@ void InkHUD::MenuApplet::populateAutoshowPage()
}
}
+// Create MenuItem entries to select our definition of "Recent"
+// Controls how long data will remain in any "Recents" flavored applets
void InkHUD::MenuApplet::populateRecentsPage()
{
// How many values are shown for use to choose from
@@ -510,6 +548,112 @@ void InkHUD::MenuApplet::populateRecentsPage()
}
}
+// MenuItem entries for the "send" page
+// Dynamically creates menu items based on available canned messages
+void InkHUD::MenuApplet::populateSendPage()
+{
+ // Position / NodeInfo packet
+ items.push_back(MenuItem("Ping", MenuAction::SEND_PING, MenuPage::EXIT));
+
+ // One menu item for each canned message
+ uint8_t count = cm.store->size();
+ for (uint8_t i = 0; i < count; i++) {
+ // Gather the information for this item
+ CannedMessages::MessageItem messageItem;
+ messageItem.rawText = cm.store->at(i);
+ messageItem.label = parse(messageItem.rawText);
+
+ // Store the item (until the menu closes)
+ cm.messageItems.push_back(messageItem);
+
+ // Create a menu item
+ const char *itemText = cm.messageItems.back().label.c_str();
+ items.push_back(MenuItem(itemText, MenuAction::STORE_CANNEDMESSAGE_SELECTION, MenuPage::CANNEDMESSAGE_RECIPIENT));
+ }
+
+ items.push_back(MenuItem("Exit", MenuPage::EXIT));
+}
+
+// Dynamically create MenuItem entries for possible canned message destinations
+// All available channels are shown
+// Favorite nodes are shown, provided we don't have an *excessive* amount
+void InkHUD::MenuApplet::populateRecipientPage()
+{
+ // Create recipient data (and menu items) for any channels
+ // --------------------------------------------------------
+
+ for (uint8_t i = 0; i < MAX_NUM_CHANNELS; i++) {
+ // Get the channel, and check if it's enabled
+ meshtastic_Channel &channel = channels.getByIndex(i);
+ if (!channel.has_settings || channel.role == meshtastic_Channel_Role_DISABLED)
+ continue;
+
+ CannedMessages::RecipientItem r;
+
+ // Set index
+ r.channelIndex = channel.index;
+
+ // Set a label for the menu item
+ r.label = "Ch " + to_string(i) + ": ";
+ if (channel.role == meshtastic_Channel_Role_PRIMARY)
+ r.label += "Primary";
+ else
+ r.label += parse(channel.settings.name);
+
+ // Add to the list of recipients
+ cm.recipientItems.push_back(r);
+
+ // Add a menu item for this recipient
+ const char *itemText = cm.recipientItems.back().label.c_str();
+ items.push_back(MenuItem(itemText, SEND_CANNEDMESSAGE, MenuPage::EXIT));
+ }
+
+ // Create recipient data (and menu items) for favorite nodes
+ // ---------------------------------------------------------
+
+ uint32_t nodeCount = nodeDB->getNumMeshNodes();
+ uint32_t favoriteCount = 0;
+
+ // Count favorites
+ for (uint32_t i = 0; i < nodeCount; i++) {
+ if (nodeDB->getMeshNodeByIndex(i)->is_favorite)
+ favoriteCount++;
+ }
+
+ // Only add favorites if the number is reasonable
+ // Don't want some monstrous list that takes 100 clicks to reach exit
+ if (favoriteCount < 20) {
+ for (uint32_t i = 0; i < nodeCount; i++) {
+ meshtastic_NodeInfoLite *node = nodeDB->getMeshNodeByIndex(i);
+
+ // Skip node if not a favorite
+ if (!node->is_favorite)
+ continue;
+
+ CannedMessages::RecipientItem r;
+
+ r.dest = node->num;
+ r.channelIndex = nodeDB->getMeshNodeChannel(node->num); // Channel index only relevant if encrypted DM not possible(?)
+
+ // Set a label for the menu item
+ r.label = "DM: ";
+ if (node->has_user)
+ r.label += parse(node->user.long_name);
+ else
+ r.label += hexifyNodeNum(node->num); // Unsure if it's possible to favorite a node without NodeInfo?
+
+ // Add to the list of recipients
+ cm.recipientItems.push_back(r);
+
+ // Add a menu item for this recipient
+ const char *itemText = cm.recipientItems.back().label.c_str();
+ items.push_back(MenuItem(itemText, SEND_CANNEDMESSAGE, MenuPage::EXIT));
+ }
+ }
+
+ items.push_back(MenuItem("Exit", MenuPage::EXIT));
+}
+
// Renders the panel shown at the top of the root menu.
// Displays the clock, and several other pieces of instantaneous system info,
// which we'd prefer not to have displayed in a normal applet, as they update too frequently.
@@ -523,11 +667,11 @@ void InkHUD::MenuApplet::drawSystemInfoPanel(int16_t left, int16_t top, uint16_t
// ====================
std::string clockString = getTimeString();
if (clockString.length() > 0) {
- setFont(fontLarge);
+ setFont(fontMedium);
printAt(width / 2, top, clockString, CENTER, TOP);
- height += fontLarge.lineHeight();
- height += fontLarge.lineHeight() * 0.1; // Padding below clock
+ height += fontMedium.lineHeight();
+ height += fontMedium.lineHeight() * 0.1; // Padding below clock
}
// Stats
@@ -619,4 +763,37 @@ uint16_t InkHUD::MenuApplet::getSystemInfoPanelHeight()
return height;
}
+// Send a text message to the mesh
+// Used to send our canned messages
+void InkHUD::MenuApplet::sendText(NodeNum dest, ChannelIndex channel, const char *message)
+{
+ meshtastic_MeshPacket *p = router->allocForSending();
+ p->decoded.portnum = meshtastic_PortNum_TEXT_MESSAGE_APP;
+ p->to = dest;
+ p->channel = channel;
+ p->want_ack = true;
+ p->decoded.payload.size = strlen(message);
+ memcpy(p->decoded.payload.bytes, message, p->decoded.payload.size);
+
+ // Tack on a bell character if requested
+ if (moduleConfig.canned_message.send_bell && p->decoded.payload.size < meshtastic_Constants_DATA_PAYLOAD_LEN) {
+ p->decoded.payload.bytes[p->decoded.payload.size] = 7; // Bell character
+ p->decoded.payload.bytes[p->decoded.payload.size + 1] = '\0'; // Append Null Terminator
+ p->decoded.payload.size++;
+ }
+
+ LOG_INFO("Send message id=%d, dest=%x, msg=%.*s", p->id, p->to, p->decoded.payload.size, p->decoded.payload.bytes);
+
+ service->sendToMesh(p, RX_SRC_LOCAL, true); // Send to mesh, cc to phone
+}
+
+// Free up any heap mmemory we'd used while selecting / sending canned messages
+void InkHUD::MenuApplet::freeCannedMessageResources()
+{
+ cm.selectedMessageItem = nullptr;
+ cm.selectedRecipientItem = nullptr;
+ cm.messageItems.clear();
+ cm.recipientItems.clear();
+}
+
#endif
\ No newline at end of file
diff --git a/src/graphics/niche/InkHUD/Applets/System/Menu/MenuApplet.h b/src/graphics/niche/InkHUD/Applets/System/Menu/MenuApplet.h
index d9297c8ed..8f9280e6f 100644
--- a/src/graphics/niche/InkHUD/Applets/System/Menu/MenuApplet.h
+++ b/src/graphics/niche/InkHUD/Applets/System/Menu/MenuApplet.h
@@ -6,10 +6,12 @@
#include "graphics/niche/InkHUD/InkHUD.h"
#include "graphics/niche/InkHUD/Persistence.h"
#include "graphics/niche/InkHUD/SystemApplet.h"
+#include "graphics/niche/Utils/CannedMessageStore.h"
#include "./MenuItem.h"
#include "./MenuPage.h"
+#include "Channels.h"
#include "concurrency/OSThread.h"
namespace NicheGraphics::InkHUD
@@ -36,12 +38,18 @@ class MenuApplet : public SystemApplet, public concurrency::OSThread
void execute(MenuItem item); // Perform the MenuAction associated with a MenuItem, if any
void showPage(MenuPage page); // Load and display a MenuPage
+
+ void populateSendPage(); // Dynamically create MenuItems including canned messages
+ void populateRecipientPage(); // Dynamically create a page of possible destinations for a canned message
void populateAppletPage(); // Dynamically create MenuItems for toggling loaded applets
void populateAutoshowPage(); // Dynamically create MenuItems for selecting which applets can autoshow
void populateRecentsPage(); // Create menu items: a choice of values for settings.recentlyActiveSeconds
+
uint16_t getSystemInfoPanelHeight();
void drawSystemInfoPanel(int16_t left, int16_t top, uint16_t width,
- uint16_t *height = nullptr); // Info panel at top of root menu
+ uint16_t *height = nullptr); // Info panel at top of root menu
+ void sendText(NodeNum dest, ChannelIndex channel, const char *message); // Send a text message to mesh
+ void freeCannedMessageResources(); // Clear MenuApplet's canned message processing data
MenuPage currentPage = MenuPage::ROOT;
uint8_t cursor = 0; // Which menu item is currently highlighted
@@ -51,7 +59,40 @@ class MenuApplet : public SystemApplet, public concurrency::OSThread
std::vector