Compare commits

..
36 Commits
Author SHA1 Message Date
Dave Allie de453fed1d Stream inflated EPUB HTMLs down to disk instead of inflating in memory (#4)
* Downgrade miniz for stability

* Stream HTML from ZIP down to disk instead of loading all in mem
2025-12-08 00:39:17 +11:00
Dave Allie c715c18bf7 Sort directories first in file picker 2025-12-07 19:17:43 +11:00
Dave Allie 9fa697ae0c Fix 1-bit font pixel alignment of last byte 2025-12-07 12:25:10 +11:00
Dave Allie 79294f6b8f Build and use 1-bit font, saves a good amount of space 2025-12-07 01:26:49 +11:00
Dave Allie eceffaa289 Reduce number of full screen refreshes to once every 20 pages 2025-12-06 22:12:01 +11:00
Dave Allie bb151caee7 Version section bin files 2025-12-06 22:09:25 +11:00
Dave Allie dd6e649d74 Add expat and swap out EPUB HTML parser (#2)
* Add expat and swap out ERB HTML parser

* Increase EpubHtmlParserSlim file buffer to 1024 bytes

* Cleanup TextBlock functions

* Do not break words when leaving spans
2025-12-06 20:57:24 +11:00
Dave Allie ad8cee12ab Small cleanup 2025-12-06 20:24:24 +11:00
Dave Allie 4ecfdea1a1 More pass by reference changes 2025-12-06 15:56:00 +11:00
Dave Allie 5ed2fe391d Add image to README 2025-12-06 13:32:09 +11:00
Dave Allie 021f77eab3 Sort items on FileSelectionScreen 2025-12-06 13:01:16 +11:00
Dave Allie 6d3d25a288 Fix bug with selectin epubs inside of folders 2025-12-06 12:57:17 +11:00
Dave Allie 9a33030623 Use reference passing for EpdRenderer 2025-12-06 12:56:39 +11:00
Dave Allie 6414f85257 Use InputManager from community-sdk 2025-12-06 12:35:41 +11:00
Dave Allie f0d92da8f2 Update README.md with checkout instructions
Fixes https://github.com/daveallie/crosspoint-reader/issues/1
2025-12-06 04:53:58 +11:00
Dave Allie 8679c8f57c Update sleep screen 2025-12-06 04:20:41 +11:00
Dave Allie 899caab70c Avoid leaving screens mid-display update
Was leave the EPD in a bad state, blocking further actions
2025-12-06 03:02:52 +11:00
Dave Allie 98c8e7e77c Fix memory leak with Epub object getting orphaned 2025-12-06 02:49:10 +11:00
Dave Allie 7198d943b0 Add UI font 2025-12-06 01:44:14 +11:00
Dave Allie 248af4b8fb Add new boot logo screen 2025-12-06 01:44:14 +11:00
Dave Allie 05a027e2bf Wrap up multiple font styles into EpdFontFamily 2025-12-06 01:44:14 +11:00
Dave Allie fa0f27df6a Full screen refresh of EpubReaderScreen every 10 renders
This is done in an effort to minimise ghosting
2025-12-05 22:19:44 +11:00
Dave Allie 2631613b8d Add directory picking to home screen 2025-12-05 22:12:28 +11:00
Dave Allie 72aa7ba3f6 Upgrade open-x4-sdk 2025-12-05 21:14:08 +11:00
Dave Allie e08bac2e10 Show indexing text when indexing 2025-12-05 21:12:15 +11:00
Dave Allie 12d28e2148 Avoid ghosting on sleep screen by doing a full screen update 2025-12-05 17:55:17 +11:00
Dave Allie 85502b417e Speedup boot by not waiting for Serial 2025-12-05 17:47:23 +11:00
Dave Allie ddec7f78dd Fix hold to wake logic
esp_sleep_get_wakeup_cause does not seem to be set when not connected to USB power
2025-12-04 00:57:32 +11:00
Dave Allie 2f9f86b3dd Update README.md features 2025-12-04 00:14:47 +11:00
Dave Allie 47eb1157ef Support left and right buttons in reader and file picker 2025-12-04 00:11:51 +11:00
Dave Allie aee239a931 Adjust input button thresholds to support more devices 2025-12-04 00:11:51 +11:00
Dave Allie 1ee8b728f9 Add file selection screen 2025-12-04 00:11:51 +11:00
Dave Allie 2c80aca7b5 Use correct current page on reader screen
Fixes the page counter not updating
2025-12-03 22:34:16 +11:00
Dave Allie 7704772ebe Handle nested navpoint elements in nxc TOC 2025-12-03 22:30:50 +11:00
Dave Allie 4186c7da9e Remove debug lines 2025-12-03 22:30:13 +11:00
Dave Allie 802c9d0a30 Add web flashing instructions 2025-12-03 22:21:11 +11:00
79 changed files with 28804 additions and 22828 deletions
+30 -5
View File
@@ -6,10 +6,7 @@ Built using **PlatformIO** and targeting the **ESP32-C3** microcontroller.
CrossPoint Reader is a purpose-built firmware designed to be a drop-in, fully open-source replacement for the official CrossPoint Reader is a purpose-built firmware designed to be a drop-in, fully open-source replacement for the official
Xteink firmware. It aims to match or improve upon the standard EPUB reading experience. Xteink firmware. It aims to match or improve upon the standard EPUB reading experience.
// TODO include some images ![](./docs/cover.jpg)
I look at the [**diy-esp32-epub-reader** by atomic14](https://github.com/atomic14/diy-esp32-epub-reader) project a lot
when making CrossPoint and a handful of lessons and some direct source code comes directly from that repo.
## Motivation ## Motivation
@@ -31,7 +28,9 @@ This project is **not affiliated with Xteink**; it's built as a community projec
- [x] EPUB parsing and rendering - [x] EPUB parsing and rendering
- [x] Saved reading position - [x] Saved reading position
- [ ] File explorer with file picker - [ ] File explorer with file picker
- Currently CrossPoint will just open the first EPUB it finds at the root of the SD card - [x] Basic EPUB picker from root directory
- [x] Support nested folders
- [ ] EPUB picker with cover art
- [ ] Image support within EPUB - [ ] Image support within EPUB
- [ ] Configurable font, layout, and display options - [ ] Configurable font, layout, and display options
- [ ] WiFi connectivity - [ ] WiFi connectivity
@@ -46,14 +45,37 @@ This project is **not affiliated with Xteink**; it's built as a community projec
* USB-C cable for flashing the ESP32-C3 * USB-C cable for flashing the ESP32-C3
* Xteink X4 * Xteink X4
### Checking out the code
CrossPoint uses PlatformIO for building and flashing the firmware. To get started, clone the repository:
```
git clone --recursive https://github.com/daveallie/crosspoint-reader
# Or, if you've already cloned without --recursive:
git submodule update --init --recursive
```
### Flashing your device ### Flashing your device
#### Command line #### Command line
Connect your Xteink X4 to your computer via USB-C and run the following command.
```sh ```sh
pio run --target upload pio run --target upload
``` ```
#### Web
1. Connect your Xteink X4 to your computer via USB-C
2. Download the `firmware.bin` file from the latest release via the [releases page](https://github.com/daveallie/crosspoint-reader/releases)
3. Go to https://xteink.dve.al/ and flash the firmware file using the "OTA fast flash controls" section
4. Press the reset button on the Xteink X4 to restart the device
To revert back to the official firmware, you can flash the latest official firmware from https://xteink.dve.al/, or swap
back to the other partition using the "Swap boot partition" button here https://xteink.dve.al/debug.
## Internals ## Internals
CrossPoint Reader is pretty aggressive about caching data down to the SD card to minimise RAM usage. The ESP32-C3 only CrossPoint Reader is pretty aggressive about caching data down to the SD card to minimise RAM usage. The ESP32-C3 only
@@ -104,3 +126,6 @@ Contributions are very welcome!
--- ---
CrossPoint Reader is **not affiliated with Xteink or any manufacturer of the X4 hardware**. CrossPoint Reader is **not affiliated with Xteink or any manufacturer of the X4 hardware**.
Huge shoutout to [**diy-esp32-epub-reader** by atomic14](https://github.com/atomic14/diy-esp32-epub-reader), which was a project I took a lot of inspiration from as I
was making CrossPoint.
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

-3
View File
@@ -2,12 +2,9 @@
#include <Utf8.h> #include <Utf8.h>
EpdFont::EpdFont(const EpdFontData* data) { this->data = data; }
inline int min(const int a, const int b) { return a < b ? a : b; } inline int min(const int a, const int b) { return a < b ? a : b; }
inline int max(const int a, const int b) { return a < b ? b : a; } inline int max(const int a, const int b) { return a < b ? b : a; }
// TODO: Text properties??
void EpdFont::getTextBounds(const char* string, const int startX, const int startY, int* minX, int* minY, int* maxX, void EpdFont::getTextBounds(const char* string, const int startX, const int startY, int* minX, int* minY, int* maxX,
int* maxY) const { int* maxY) const {
*minX = startX; *minX = startX;
+1 -1
View File
@@ -6,7 +6,7 @@ class EpdFont {
public: public:
const EpdFontData* data; const EpdFontData* data;
explicit EpdFont(const EpdFontData* data); explicit EpdFont(const EpdFontData* data) : data(data) {}
~EpdFont() = default; ~EpdFont() = default;
void getTextDimensions(const char* string, int* w, int* h) const; void getTextDimensions(const char* string, int* w, int* h) const;
bool hasPrintableChars(const char* string) const; bool hasPrintableChars(const char* string) const;
+7 -8
View File
@@ -6,13 +6,13 @@
/// Font data stored PER GLYPH /// Font data stored PER GLYPH
typedef struct { typedef struct {
uint8_t width; ///< Bitmap dimensions in pixels uint8_t width; ///< Bitmap dimensions in pixels
uint8_t height; ///< Bitmap dimensions in pixels uint8_t height; ///< Bitmap dimensions in pixels
uint8_t advanceX; ///< Distance to advance cursor (x axis) uint8_t advanceX; ///< Distance to advance cursor (x axis)
int16_t left; ///< X dist from cursor pos to UL corner int16_t left; ///< X dist from cursor pos to UL corner
int16_t top; ///< Y dist from cursor pos to UL corner int16_t top; ///< Y dist from cursor pos to UL corner
uint16_t compressedSize; ///< Size of the zlib-compressed font data. uint16_t dataLength; ///< Size of the font data.
uint32_t dataOffset; ///< Pointer into EpdFont->bitmap uint32_t dataOffset; ///< Pointer into EpdFont->bitmap
} EpdGlyph; } EpdGlyph;
/// Glyph interval structure /// Glyph interval structure
@@ -28,7 +28,6 @@ typedef struct {
const EpdGlyph* glyph; ///< Glyph array const EpdGlyph* glyph; ///< Glyph array
const EpdUnicodeInterval* intervals; ///< Valid unicode intervals for this font const EpdUnicodeInterval* intervals; ///< Valid unicode intervals for this font
uint32_t intervalCount; ///< Number of unicode intervals. uint32_t intervalCount; ///< Number of unicode intervals.
bool compressed; ///< Does this font use compressed glyph bitmaps?
uint8_t advanceY; ///< Newline distance (y axis) uint8_t advanceY; ///< Newline distance (y axis)
int ascender; ///< Maximal height of a glyph above the base line int ascender; ///< Maximal height of a glyph above the base line
int descender; ///< Maximal height of a glyph below the base line int descender; ///< Maximal height of a glyph below the base line
+37
View File
@@ -0,0 +1,37 @@
#include "EpdFontFamily.h"
const EpdFont* EpdFontFamily::getFont(const EpdFontStyle style) const {
if (style == BOLD && bold) {
return bold;
}
if (style == ITALIC && italic) {
return italic;
}
if (style == BOLD_ITALIC) {
if (boldItalic) {
return boldItalic;
}
if (bold) {
return bold;
}
if (italic) {
return italic;
}
}
return regular;
}
void EpdFontFamily::getTextDimensions(const char* string, int* w, int* h, const EpdFontStyle style) const {
getFont(style)->getTextDimensions(string, w, h);
}
bool EpdFontFamily::hasPrintableChars(const char* string, const EpdFontStyle style) const {
return getFont(style)->hasPrintableChars(string);
}
const EpdFontData* EpdFontFamily::getData(const EpdFontStyle style) const { return getFont(style)->data; }
const EpdGlyph* EpdFontFamily::getGlyph(const uint32_t cp, const EpdFontStyle style) const {
return getFont(style)->getGlyph(cp);
};
+24
View File
@@ -0,0 +1,24 @@
#pragma once
#include "EpdFont.h"
enum EpdFontStyle { REGULAR, BOLD, ITALIC, BOLD_ITALIC };
class EpdFontFamily {
const EpdFont* regular;
const EpdFont* bold;
const EpdFont* italic;
const EpdFont* boldItalic;
const EpdFont* getFont(EpdFontStyle style) const;
public:
explicit EpdFontFamily(const EpdFont* regular, const EpdFont* bold = nullptr, const EpdFont* italic = nullptr,
const EpdFont* boldItalic = nullptr)
: regular(regular), bold(bold), italic(italic), boldItalic(boldItalic) {}
~EpdFontFamily() = default;
void getTextDimensions(const char* string, int* w, int* h, EpdFontStyle style = REGULAR) const;
bool hasPrintableChars(const char* string, EpdFontStyle style = REGULAR) const;
const EpdFontData* getData(EpdFontStyle style = REGULAR) const;
const EpdGlyph* getGlyph(uint32_t cp, EpdFontStyle style = REGULAR) const;
};
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+766
View File
@@ -0,0 +1,766 @@
/**
* generated by fontconvert.py
* name: ubuntu_10
* size: 10
*/
#pragma once
#include "EpdFontData.h"
static const uint8_t ubuntu_10Bitmaps[7175] = {
0xEE, 0xEE, 0xEE, 0xE6, 0x66, 0x00, 0xEF, 0xE0, 0xEF, 0xDF, 0xBF, 0x6E, 0xC0, 0x1C, 0x61, 0xCE, 0x1C, 0xE1, 0x8E,
0xFF, 0xFF, 0xFF, 0x39, 0xC3, 0x9C, 0x39, 0xCF, 0xFF, 0xFF, 0xF7, 0x18, 0x73, 0x87, 0x38, 0x63, 0x80, 0x0C, 0x03,
0x03, 0xF9, 0xFE, 0xFB, 0xB8, 0x0E, 0x01, 0xE0, 0x7E, 0x07, 0xE0, 0x78, 0x07, 0x01, 0xF9, 0xFF, 0xFB, 0xFC, 0x0C,
0x03, 0x00, 0xC0, 0x7C, 0x38, 0xEE, 0x38, 0xC6, 0x70, 0xC6, 0x70, 0xC6, 0xE0, 0xEE, 0xE0, 0x7D, 0xC0, 0x01, 0x80,
0x03, 0xBE, 0x07, 0x7F, 0x07, 0x63, 0x0E, 0x63, 0x0E, 0x63, 0x1C, 0x77, 0x1C, 0x3E, 0x0F, 0x80, 0x7F, 0x03, 0xDC,
0x0E, 0x30, 0x39, 0xC0, 0xEF, 0x01, 0xF8, 0x07, 0xC0, 0x7F, 0x19, 0xCE, 0xE6, 0x1F, 0x98, 0x3C, 0x79, 0xF1, 0xFF,
0xE3, 0xF9, 0xC0, 0xFF, 0xFE, 0x1C, 0x73, 0x8E, 0x71, 0xC6, 0x38, 0xE3, 0x8E, 0x38, 0xE1, 0x87, 0x1C, 0x38, 0xE1,
0xC7, 0xC3, 0x87, 0x1C, 0x38, 0xE3, 0x86, 0x18, 0x71, 0xC6, 0x18, 0xE3, 0x8E, 0x71, 0xCE, 0x30, 0x0C, 0x03, 0x07,
0xFD, 0xFE, 0x1E, 0x0F, 0xC3, 0xF8, 0xCC, 0x0C, 0x03, 0x00, 0xC0, 0x30, 0xFF, 0xFF, 0xF0, 0xC0, 0x30, 0x0C, 0x03,
0x00, 0x77, 0x77, 0x60, 0xFF, 0xF0, 0xFF, 0x80, 0x01, 0xC0, 0x60, 0x38, 0x0E, 0x03, 0x01, 0xC0, 0x70, 0x38, 0x0E,
0x03, 0x81, 0xC0, 0x70, 0x1C, 0x0E, 0x03, 0x80, 0xE0, 0x70, 0x1C, 0x06, 0x03, 0x80, 0x3F, 0x1F, 0xE7, 0xFB, 0x87,
0xE1, 0xF8, 0x7E, 0x1F, 0x87, 0xE1, 0xF8, 0x7E, 0x1F, 0x87, 0x7F, 0x9F, 0xE3, 0xF0, 0x0E, 0x3D, 0xFB, 0xF7, 0xE1,
0xC3, 0x87, 0x0E, 0x1C, 0x38, 0x70, 0xE1, 0xC3, 0x80, 0x7F, 0x3F, 0xEF, 0x79, 0x0E, 0x03, 0x80, 0xE0, 0x70, 0x3C,
0x1E, 0x0F, 0x07, 0x81, 0xC0, 0xE0, 0x3F, 0xFF, 0xFC, 0x7F, 0x3F, 0xC6, 0x78, 0x0E, 0x03, 0x83, 0xE3, 0xF0, 0xFC,
0x0F, 0x80, 0xE0, 0x1C, 0x07, 0xE7, 0xBF, 0xEF, 0xF0, 0x03, 0xC0, 0xF8, 0x1F, 0x07, 0xE1, 0xFC, 0x7B, 0x8E, 0x73,
0x8E, 0x71, 0xDF, 0xFF, 0xFF, 0x80, 0xE0, 0x1C, 0x03, 0x80, 0x70, 0x7F, 0x9F, 0xE7, 0x01, 0xC0, 0x70, 0x1F, 0x87,
0xF0, 0x7E, 0x03, 0xC0, 0x70, 0x1C, 0x0F, 0xE7, 0xBF, 0xEF, 0xF0, 0x0F, 0x87, 0xE3, 0xF1, 0xE0, 0x70, 0x3F, 0xCF,
0xFB, 0xDF, 0xE1, 0xF8, 0x7E, 0x1F, 0x87, 0x7B, 0xDF, 0xE3, 0xF0, 0xFF, 0xFF, 0xF0, 0x3C, 0x0E, 0x07, 0x01, 0xC0,
0xE0, 0x38, 0x1C, 0x07, 0x01, 0xC0, 0x60, 0x38, 0x0E, 0x03, 0x80, 0x3F, 0x1F, 0xEF, 0xFB, 0x87, 0xE1, 0xFC, 0xE7,
0xF8, 0xFC, 0x7F, 0xB8, 0xFE, 0x1F, 0x87, 0xF7, 0xFF, 0xE7, 0xF0, 0x3F, 0x1F, 0xEF, 0x7B, 0x87, 0xE1, 0xF8, 0x7F,
0x7F, 0xFF, 0x7F, 0xC0, 0xF0, 0x38, 0x1E, 0x3F, 0x3F, 0x87, 0xC0, 0xFF, 0x80, 0x00, 0xFF, 0x80, 0x77, 0x70, 0x00,
0x00, 0x07, 0x77, 0x76, 0x01, 0xC3, 0xF7, 0xFB, 0xF0, 0xF0, 0x3F, 0x07, 0xF8, 0x3F, 0x03, 0xC0, 0xFF, 0xFF, 0xF0,
0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFC, 0xE0, 0x3F, 0x07, 0xF8, 0x3F, 0x03, 0xC7, 0xF7, 0xFB, 0xF0, 0xE0, 0x00, 0xFE,
0xFF, 0xEF, 0x07, 0x07, 0x0F, 0x1E, 0x1C, 0x38, 0x38, 0x00, 0x00, 0x38, 0x38, 0x38, 0x07, 0xF8, 0x07, 0xFF, 0x83,
0xFB, 0xF0, 0xF0, 0x1E, 0x70, 0x03, 0x9C, 0xFE, 0x6E, 0x7F, 0x9F, 0x9E, 0xE7, 0xEF, 0x39, 0xFB, 0x8E, 0x7E, 0xE3,
0x9F, 0xB8, 0xE7, 0xE7, 0xBB, 0xB9, 0xFF, 0xE7, 0x3F, 0xF1, 0xC0, 0x00, 0x3C, 0x00, 0x0F, 0xE6, 0x01, 0xFF, 0x80,
0x1F, 0xE0, 0x07, 0x80, 0x1E, 0x00, 0x78, 0x03, 0xF0, 0x0F, 0xC0, 0x73, 0x81, 0xCE, 0x06, 0x38, 0x38, 0x70, 0xFF,
0xC7, 0xFF, 0x9C, 0x0E, 0x70, 0x3B, 0x80, 0x7E, 0x01, 0xC0, 0xFF, 0x8F, 0xFC, 0xE3, 0xEE, 0x0E, 0xE0, 0xEE, 0x3E,
0xFF, 0xCF, 0xFE, 0xE3, 0xEE, 0x07, 0xE0, 0x7E, 0x0F, 0xE3, 0xEF, 0xFE, 0xFF, 0xC0, 0x0F, 0xE3, 0xFF, 0x7E, 0xE7,
0x00, 0xF0, 0x0E, 0x00, 0xE0, 0x0E, 0x00, 0xE0, 0x0E, 0x00, 0xE0, 0x07, 0x00, 0x7E, 0xF3, 0xFF, 0x1F, 0xF0, 0xFF,
0x87, 0xFF, 0x38, 0xFD, 0xC1, 0xEE, 0x07, 0xF0, 0x1F, 0x80, 0xFC, 0x07, 0xE0, 0x3F, 0x01, 0xF8, 0x1F, 0xC1, 0xEE,
0x3F, 0x7F, 0xF3, 0xFE, 0x00, 0xFF, 0xDF, 0xFB, 0x80, 0x70, 0x0E, 0x01, 0xC0, 0x3F, 0xE7, 0xFC, 0xE0, 0x1C, 0x03,
0x80, 0x70, 0x0E, 0x01, 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, 0xFE, 0x03, 0x80, 0xE0, 0x38, 0x0F, 0xFB, 0xFE, 0xE0, 0x38,
0x0E, 0x03, 0x80, 0xE0, 0x38, 0x0E, 0x00, 0x0F, 0xF3, 0xFF, 0x7E, 0xF7, 0x00, 0xF0, 0x0E, 0x00, 0xE0, 0x0E, 0x00,
0xE0, 0x7E, 0x07, 0xE0, 0x77, 0x07, 0x7E, 0xF3, 0xFF, 0x1F, 0xF0, 0xE0, 0x3E, 0x03, 0xE0, 0x3E, 0x03, 0xE0, 0x3E,
0x03, 0xFF, 0xFF, 0xFF, 0xE0, 0x3E, 0x03, 0xE0, 0x3E, 0x03, 0xE0, 0x3E, 0x03, 0xE0, 0x30, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xF8, 0x03, 0x81, 0xC0, 0xE0, 0x70, 0x38, 0x1C, 0x0E, 0x07, 0x03, 0x81, 0xC0, 0xE0, 0x7E, 0xFF, 0xFB, 0xF8,
0xE0, 0xF7, 0x0F, 0x38, 0xF1, 0xC7, 0x0E, 0x70, 0x77, 0x03, 0xF0, 0x1F, 0x80, 0xFE, 0x07, 0x78, 0x39, 0xE1, 0xC7,
0x8E, 0x1E, 0x70, 0x7B, 0x81, 0xC0, 0xE0, 0x38, 0x0E, 0x03, 0x80, 0xE0, 0x38, 0x0E, 0x03, 0x80, 0xE0, 0x38, 0x0E,
0x03, 0x80, 0xE0, 0x3F, 0xFF, 0xFC, 0x70, 0x0E, 0x78, 0x0E, 0x78, 0x1E, 0x78, 0x1F, 0x6C, 0x3F, 0x6C, 0x37, 0xEE,
0x37, 0xE6, 0x67, 0xE7, 0x67, 0xE3, 0xE7, 0xE3, 0xC7, 0xE3, 0xC7, 0xE1, 0xC7, 0xE0, 0x07, 0xE0, 0x07, 0xE0, 0x3F,
0x81, 0xFE, 0x0F, 0xF8, 0x7F, 0xE3, 0xF7, 0x1F, 0x9C, 0xFC, 0xF7, 0xE3, 0xBF, 0x0F, 0xF8, 0x7F, 0xC1, 0xFE, 0x07,
0xF0, 0x3F, 0x80, 0xE0, 0x1F, 0xE0, 0x7F, 0xE1, 0xFB, 0xE3, 0x81, 0xEF, 0x01, 0xDC, 0x03, 0xB8, 0x07, 0x70, 0x0F,
0xE0, 0x1D, 0xC0, 0x3B, 0xC0, 0x73, 0x81, 0xE7, 0xEF, 0x87, 0xFE, 0x07, 0xF8, 0x00, 0xFF, 0x8F, 0xFC, 0xE3, 0xEE,
0x0E, 0xE0, 0xEE, 0x0E, 0xE7, 0xEF, 0xFC, 0xFF, 0x8E, 0x00, 0xE0, 0x0E, 0x00, 0xE0, 0x0E, 0x00, 0xE0, 0x00, 0x1F,
0xE0, 0x7F, 0xE1, 0xFB, 0xE3, 0x81, 0xEF, 0x01, 0xDC, 0x03, 0xB8, 0x07, 0x70, 0x0F, 0xE0, 0x1D, 0xC0, 0x3B, 0xC0,
0x73, 0x81, 0xE7, 0xEF, 0x87, 0xFE, 0x07, 0xF8, 0x01, 0xE0, 0x01, 0xF8, 0x01, 0xF0, 0x01, 0xE0, 0xFF, 0x8F, 0xFC,
0xE3, 0xEE, 0x0E, 0xE0, 0xEE, 0x0E, 0xE3, 0xEF, 0xFC, 0xFF, 0x8E, 0x38, 0xE3, 0xCE, 0x1C, 0xE0, 0xEE, 0x0F, 0xE0,
0x70, 0x3F, 0xCF, 0xF9, 0xE7, 0x38, 0x07, 0x00, 0xF0, 0x1F, 0x81, 0xFC, 0x0F, 0xC0, 0x3C, 0x03, 0x80, 0x77, 0x3F,
0xFF, 0x9F, 0xE0, 0xFF, 0xFF, 0xFF, 0x0E, 0x00, 0xE0, 0x0E, 0x00, 0xE0, 0x0E, 0x00, 0xE0, 0x0E, 0x00, 0xE0, 0x0E,
0x00, 0xE0, 0x0E, 0x00, 0xE0, 0x0E, 0x00, 0xE0, 0x7E, 0x07, 0xE0, 0x7E, 0x07, 0xE0, 0x7E, 0x07, 0xE0, 0x7E, 0x07,
0xE0, 0x7E, 0x07, 0xE0, 0x77, 0x07, 0x7D, 0xE3, 0xFE, 0x1F, 0xC0, 0xE0, 0x1F, 0x80, 0xF7, 0x03, 0x9C, 0x0E, 0x70,
0x70, 0xE1, 0xC3, 0x87, 0x0E, 0x38, 0x1C, 0xE0, 0x73, 0x80, 0xFC, 0x03, 0xF0, 0x0F, 0x80, 0x1E, 0x00, 0x78, 0x00,
0xE0, 0x00, 0xFC, 0x00, 0x1D, 0xC3, 0x83, 0xB8, 0x78, 0x77, 0x0F, 0x0E, 0xE3, 0xE3, 0x9C, 0x7E, 0x71, 0xCD, 0xCE,
0x3B, 0xB9, 0xC7, 0x73, 0xB0, 0xEC, 0x7E, 0x0F, 0x87, 0xC1, 0xF0, 0xF8, 0x3C, 0x1E, 0x03, 0x81, 0xC0, 0xF0, 0x7B,
0xC3, 0x8E, 0x38, 0x7B, 0xC1, 0xFC, 0x07, 0xC0, 0x3E, 0x00, 0xE0, 0x0F, 0x80, 0xFE, 0x07, 0x78, 0x71, 0xC7, 0x87,
0x38, 0x3B, 0xC0, 0xE0, 0xE0, 0x7F, 0x83, 0x9C, 0x3C, 0x71, 0xC3, 0x9C, 0x0E, 0xE0, 0x7E, 0x01, 0xF0, 0x0F, 0x00,
0x38, 0x01, 0xC0, 0x0E, 0x00, 0x70, 0x03, 0x80, 0x1C, 0x00, 0x7F, 0xE7, 0xFE, 0x00, 0xE0, 0x1C, 0x03, 0xC0, 0x78,
0x07, 0x00, 0xE0, 0x1E, 0x01, 0xC0, 0x38, 0x07, 0x00, 0x70, 0x0F, 0xFF, 0xFF, 0xF0, 0xFF, 0xF1, 0x8C, 0x63, 0x18,
0xC6, 0x31, 0x8C, 0x63, 0x18, 0xC6, 0x3F, 0xF0, 0xE0, 0x18, 0x07, 0x01, 0xC0, 0x38, 0x0E, 0x03, 0x80, 0x70, 0x1C,
0x07, 0x00, 0xE0, 0x38, 0x0E, 0x01, 0xC0, 0x70, 0x0C, 0x03, 0x80, 0xE0, 0x18, 0x07, 0xFF, 0xC6, 0x31, 0x8C, 0x63,
0x18, 0xC6, 0x31, 0x8C, 0x63, 0x18, 0xFF, 0xF0, 0x0F, 0x01, 0xE0, 0x7E, 0x0F, 0xC3, 0x9C, 0xE3, 0x9C, 0x3B, 0x07,
0xFF, 0xFF, 0xFF, 0xE7, 0x9C, 0x60, 0x3F, 0x0F, 0xE3, 0x3C, 0x07, 0x3F, 0xDF, 0xF7, 0x9F, 0x87, 0x79, 0xDF, 0xF3,
0xFC, 0xE0, 0x1C, 0x03, 0x80, 0x70, 0x0E, 0x01, 0xFF, 0x3F, 0xF7, 0xDE, 0xE1, 0xDC, 0x1F, 0x83, 0xF0, 0x7E, 0x1D,
0xEF, 0xBF, 0xE7, 0xF8, 0x3F, 0x3F, 0xBE, 0xDC, 0x0E, 0x07, 0x03, 0x81, 0xC0, 0xFB, 0x3F, 0xCF, 0xE0, 0x01, 0xC0,
0x70, 0x1C, 0x07, 0x01, 0xCF, 0xF7, 0xFF, 0xEF, 0xE1, 0xF8, 0x7E, 0x1F, 0x87, 0xE1, 0xFE, 0x77, 0xFC, 0xFF, 0x3F,
0x1F, 0xEF, 0xFF, 0x87, 0xFF, 0xFF, 0xFE, 0x03, 0x80, 0xF9, 0x9F, 0xE3, 0xF8, 0x3F, 0x7F, 0xFA, 0xE0, 0xE0, 0xFE,
0xFE, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0x3F, 0xDF, 0xFF, 0x9F, 0x87, 0xE1, 0xF8, 0x7E, 0x1F,
0x87, 0xFF, 0xDF, 0xF3, 0xFC, 0x07, 0x77, 0xFF, 0xEF, 0xF0, 0xE0, 0x38, 0x0E, 0x03, 0x80, 0xE0, 0x3F, 0xCF, 0xFB,
0xDF, 0xE1, 0xF8, 0x7E, 0x1F, 0x87, 0xE1, 0xF8, 0x7E, 0x1F, 0x87, 0xFC, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x1C, 0x70,
0x00, 0x00, 0x71, 0xC7, 0x1C, 0x71, 0xC7, 0x1C, 0x71, 0xC7, 0x1C, 0xFF, 0xFE, 0xE0, 0x38, 0x0E, 0x03, 0x80, 0xE0,
0x38, 0xEE, 0x7B, 0xBC, 0xFE, 0x3F, 0x0F, 0xC3, 0xF8, 0xEF, 0x39, 0xCE, 0x3B, 0x8F, 0xE7, 0x39, 0xCE, 0x73, 0x9C,
0xE7, 0x39, 0xCE, 0x7B, 0xEE, 0xFF, 0xFE, 0xFF, 0xFE, 0xF7, 0xEF, 0xE3, 0x87, 0xE1, 0x87, 0xE1, 0xC7, 0xE1, 0xC7,
0xE1, 0xC7, 0xE1, 0xC7, 0xE1, 0xC7, 0xE1, 0xC7, 0xFF, 0x3F, 0xEF, 0x7F, 0x87, 0xE1, 0xF8, 0x7E, 0x1F, 0x87, 0xE1,
0xF8, 0x7E, 0x1C, 0x3F, 0x0F, 0xF3, 0xFF, 0x70, 0xEE, 0x0F, 0xC1, 0xF8, 0x3F, 0x0E, 0xFB, 0xCF, 0xF0, 0xFC, 0x00,
0xFF, 0x1F, 0xF3, 0xDF, 0x70, 0xEE, 0x0F, 0xC1, 0xF8, 0x3F, 0x0E, 0xFB, 0xDF, 0xFB, 0xFE, 0x70, 0x0E, 0x01, 0xC0,
0x38, 0x00, 0x3F, 0xDF, 0xFF, 0x9F, 0x87, 0xE1, 0xF8, 0x7E, 0x1F, 0x87, 0xFB, 0xDF, 0xF3, 0xFC, 0x07, 0x01, 0xC0,
0x70, 0x1C, 0xFF, 0xFF, 0xDF, 0x0E, 0x1C, 0x38, 0x70, 0xE1, 0xC3, 0x80, 0x3F, 0x3F, 0x9E, 0xCE, 0x07, 0xC1, 0xF8,
0x3E, 0x07, 0x67, 0xBF, 0xDF, 0xC0, 0xE0, 0xE0, 0xE0, 0xFE, 0xFE, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xF6, 0x7E,
0x7E, 0xE1, 0xF8, 0x7E, 0x1F, 0x87, 0xE1, 0xF8, 0x7E, 0x1F, 0x87, 0xFB, 0xDF, 0xF3, 0xFC, 0xE0, 0xFC, 0x39, 0xC7,
0x38, 0xE7, 0x38, 0x77, 0x0E, 0xE1, 0xF8, 0x1F, 0x03, 0xC0, 0x38, 0x00, 0xE1, 0xC7, 0xE1, 0xC7, 0x73, 0xC7, 0x73,
0xC6, 0x73, 0xEE, 0x37, 0xEE, 0x3F, 0x6E, 0x3E, 0x7C, 0x1E, 0x7C, 0x1E, 0x38, 0x1C, 0x38, 0xF1, 0xCE, 0x78, 0xEE,
0x1F, 0x81, 0xE0, 0x1C, 0x07, 0xC1, 0xFC, 0x7B, 0x8E, 0x3B, 0x87, 0x80, 0xE1, 0xDC, 0x39, 0xC7, 0x38, 0xE7, 0x38,
0x77, 0x0E, 0xE0, 0xF8, 0x1F, 0x03, 0xE0, 0x38, 0x07, 0x0B, 0xC1, 0xF8, 0x3E, 0x00, 0x7F, 0x9F, 0xE0, 0x78, 0x3C,
0x0E, 0x07, 0x03, 0xC1, 0xE0, 0x70, 0x3F, 0xFF, 0xFC, 0x1E, 0x3C, 0xF1, 0xC3, 0x87, 0x0E, 0x1C, 0x79, 0xE3, 0xE3,
0xC3, 0x87, 0x0E, 0x1C, 0x38, 0x78, 0x78, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF1, 0xE1, 0xE1, 0xC3, 0x87, 0x0E,
0x1C, 0x3C, 0x3C, 0xF9, 0xE3, 0x87, 0x0E, 0x1C, 0x38, 0xF3, 0xC7, 0x80, 0x3C, 0x6F, 0xFD, 0xFF, 0xB1, 0xE0, 0xEF,
0xE0, 0x06, 0x66, 0xEE, 0xEE, 0xEE, 0xE0, 0x0E, 0x03, 0x80, 0xE0, 0x7E, 0x3F, 0x9F, 0x67, 0x03, 0x80, 0xE0, 0x38,
0x07, 0x01, 0xF6, 0x3F, 0x87, 0xE0, 0xE0, 0x38, 0x0E, 0x00, 0x1F, 0xC7, 0xF3, 0xD8, 0xE0, 0x38, 0x0E, 0x03, 0x83,
0xFE, 0xFF, 0x8E, 0x03, 0x80, 0xC0, 0x30, 0x1F, 0xF7, 0xFC, 0x60, 0x6F, 0xFC, 0xFF, 0x1F, 0xE7, 0x0C, 0xE1, 0x8E,
0xF1, 0xFE, 0x7F, 0xEC, 0x0C, 0xE0, 0x77, 0x0E, 0x70, 0xE3, 0x9C, 0x3F, 0x81, 0xF8, 0x0F, 0x07, 0xFE, 0x7F, 0xE0,
0x60, 0x06, 0x07, 0xFE, 0x7F, 0xE0, 0x60, 0x06, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x3F, 0x8F, 0xE7, 0xB9, 0xC0,
0x78, 0x0F, 0xC7, 0xF9, 0xCF, 0x61, 0xD8, 0x77, 0x9C, 0xFE, 0x1F, 0x80, 0xE0, 0x19, 0xDE, 0x7F, 0x9F, 0xC0, 0xEE,
0xEE, 0x0F, 0xE0, 0x7F, 0xF1, 0xFB, 0xF3, 0x80, 0xEE, 0x7E, 0xFD, 0xD9, 0xF7, 0x01, 0xEE, 0x03, 0xDC, 0x07, 0xDE,
0xDF, 0x9F, 0xBB, 0x80, 0xE7, 0xEF, 0xC7, 0xFF, 0x03, 0xF8, 0x00, 0x7E, 0x2E, 0x06, 0x7F, 0x77, 0xE7, 0x77, 0x7E,
0x1D, 0xCE, 0x73, 0xB9, 0xDC, 0xF7, 0x1D, 0xC3, 0xB8, 0xE7, 0x1D, 0xC0, 0xFF, 0xFF, 0xF0, 0x0C, 0x03, 0x00, 0xC0,
0x30, 0x0C, 0x03, 0xFF, 0xF0, 0x0F, 0xE0, 0x7F, 0xF1, 0xFB, 0xF3, 0x80, 0xEE, 0xFC, 0xFD, 0xFD, 0xF3, 0xB9, 0xE7,
0xE3, 0xCF, 0xC7, 0xDD, 0xDF, 0xB9, 0xBB, 0x80, 0xE7, 0xEF, 0xC7, 0xFF, 0x03, 0xF8, 0x00, 0x7E, 0xFC, 0x7D, 0xFF,
0xBF, 0x7F, 0xEF, 0x80, 0x0C, 0x03, 0x00, 0xC0, 0x30, 0xFF, 0xFF, 0xF0, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0x03, 0xFF,
0xFF, 0xC0, 0x7D, 0xDC, 0x38, 0xE3, 0xCF, 0x1C, 0x3F, 0x7C, 0xF8, 0x71, 0xE1, 0xE1, 0xD3, 0xFE, 0x3B, 0xDC, 0xC0,
0xE1, 0xF8, 0x7E, 0x1F, 0x87, 0xE1, 0xF8, 0x7E, 0x1F, 0x87, 0xF9, 0xFF, 0xFF, 0xFF, 0x80, 0xE0, 0x38, 0x0E, 0x00,
0x1F, 0xF3, 0xF7, 0x7F, 0x77, 0xF7, 0xFF, 0x77, 0xF7, 0x7F, 0x77, 0xF7, 0x3F, 0x70, 0xF7, 0x07, 0x70, 0x77, 0x07,
0x70, 0x77, 0x07, 0x70, 0x77, 0x07, 0x70, 0x77, 0x07, 0x70, 0xFF, 0x80, 0x33, 0x8F, 0xE0, 0x3F, 0xF3, 0x33, 0x33,
0x3E, 0x3F, 0x98, 0xEC, 0x76, 0x3B, 0x1D, 0xDC, 0x7C, 0x67, 0x1D, 0xC7, 0xB8, 0xEF, 0x1D, 0xCE, 0xF7, 0xB9, 0xDC,
0x67, 0x00, 0x30, 0x18, 0x78, 0x1C, 0x3C, 0x1C, 0x06, 0x0E, 0x03, 0x0E, 0x01, 0x87, 0x00, 0xC7, 0x00, 0x63, 0x8E,
0x03, 0x8F, 0x01, 0x87, 0x81, 0xC7, 0xC0, 0xC7, 0x60, 0xE3, 0xF8, 0xE0, 0x18, 0x70, 0x0C, 0x30, 0x38, 0x78, 0x18,
0x3C, 0x1C, 0x06, 0x1C, 0x03, 0x0E, 0x01, 0x8E, 0x00, 0xC7, 0x00, 0x67, 0x7E, 0x03, 0xBB, 0x03, 0x81, 0x81, 0x81,
0xC1, 0xC1, 0xC0, 0xC1, 0xE0, 0xE0, 0xC0, 0xE0, 0xFE, 0x7C, 0x0E, 0x1F, 0x07, 0x01, 0xC1, 0xC0, 0xF0, 0xE0, 0x1E,
0x38, 0x03, 0x9C, 0x04, 0xE6, 0x03, 0xF3, 0x8E, 0x01, 0xC7, 0x80, 0x71, 0xE0, 0x38, 0xF8, 0x0E, 0x76, 0x07, 0x1F,
0xC1, 0xC0, 0x60, 0xE0, 0x18, 0x1C, 0x0F, 0x07, 0x00, 0x00, 0x00, 0x60, 0x70, 0x38, 0x38, 0x38, 0x38, 0x1C, 0x0F,
0x73, 0xF9, 0xFC, 0x0E, 0x00, 0x3C, 0x00, 0x78, 0x00, 0xE0, 0x00, 0x00, 0x1E, 0x00, 0x78, 0x01, 0xE0, 0x0F, 0xC0,
0x3F, 0x01, 0xCE, 0x07, 0x38, 0x18, 0xE0, 0xE1, 0xC3, 0xFF, 0x1F, 0xFE, 0x70, 0x39, 0xC0, 0xEE, 0x01, 0xF8, 0x07,
0x01, 0xC0, 0x0F, 0x00, 0x78, 0x00, 0xC0, 0x00, 0x00, 0x1E, 0x00, 0x78, 0x01, 0xE0, 0x0F, 0xC0, 0x3F, 0x01, 0xCE,
0x07, 0x38, 0x18, 0xE0, 0xE1, 0xC3, 0xFF, 0x1F, 0xFE, 0x70, 0x39, 0xC0, 0xEE, 0x01, 0xF8, 0x07, 0x03, 0x00, 0x1E,
0x00, 0xFC, 0x03, 0x30, 0x00, 0x00, 0x1E, 0x00, 0x78, 0x01, 0xE0, 0x0F, 0xC0, 0x3F, 0x01, 0xCE, 0x07, 0x38, 0x18,
0xE0, 0xE1, 0xC3, 0xFF, 0x1F, 0xFE, 0x70, 0x39, 0xC0, 0xEE, 0x01, 0xF8, 0x07, 0x0F, 0xE0, 0x7F, 0x81, 0xFC, 0x00,
0x00, 0x07, 0x80, 0x1E, 0x00, 0x78, 0x03, 0xF0, 0x0F, 0xC0, 0x73, 0x81, 0xCE, 0x06, 0x38, 0x38, 0x70, 0xFF, 0xC7,
0xFF, 0x9C, 0x0E, 0x70, 0x3B, 0x80, 0x7E, 0x01, 0xC0, 0x1D, 0xC0, 0x77, 0x00, 0x00, 0x01, 0xE0, 0x07, 0x80, 0x1E,
0x00, 0xFC, 0x03, 0xF0, 0x1C, 0xE0, 0x73, 0x81, 0x8E, 0x0E, 0x1C, 0x3F, 0xF1, 0xFF, 0xE7, 0x03, 0x9C, 0x0E, 0xE0,
0x1F, 0x80, 0x70, 0x07, 0x80, 0x3E, 0x00, 0xF8, 0x01, 0xE0, 0x07, 0x80, 0x1E, 0x00, 0xFC, 0x03, 0xF0, 0x1C, 0xE0,
0x73, 0x81, 0x8E, 0x0E, 0x1C, 0x3F, 0xF1, 0xFF, 0xE7, 0x03, 0x9C, 0x0E, 0xE0, 0x1F, 0x80, 0x70, 0x00, 0xFF, 0xE0,
0x1F, 0xFC, 0x07, 0xC0, 0x01, 0xD8, 0x00, 0x3B, 0x00, 0x0E, 0x60, 0x03, 0x8F, 0xF0, 0x71, 0xFE, 0x1C, 0x30, 0x03,
0xFE, 0x00, 0xFF, 0xC0, 0x1C, 0x18, 0x07, 0x03, 0x01, 0xE0, 0x7F, 0xF8, 0x0F, 0xF8, 0x0F, 0xE3, 0xFF, 0x7E, 0xE7,
0x00, 0xF0, 0x0E, 0x00, 0xE0, 0x0E, 0x00, 0xE0, 0x0E, 0x00, 0xE0, 0x07, 0x00, 0x7E, 0xF3, 0xFF, 0x1F, 0xF0, 0x70,
0x0F, 0x80, 0xF0, 0x1C, 0x03, 0x80, 0x38, 0x03, 0x00, 0x01, 0xFF, 0xBF, 0xF7, 0x00, 0xE0, 0x1C, 0x03, 0x80, 0x7F,
0xCF, 0xF9, 0xC0, 0x38, 0x07, 0x00, 0xE0, 0x1C, 0x03, 0xFF, 0xFF, 0xF0, 0x03, 0x00, 0xE0, 0x38, 0x06, 0x00, 0x01,
0xFF, 0xBF, 0xF7, 0x00, 0xE0, 0x1C, 0x03, 0x80, 0x7F, 0xCF, 0xF9, 0xC0, 0x38, 0x07, 0x00, 0xE0, 0x1C, 0x03, 0xFF,
0xFF, 0xF0, 0x0C, 0x03, 0xC0, 0xFC, 0x19, 0x80, 0x01, 0xFF, 0xBF, 0xF7, 0x00, 0xE0, 0x1C, 0x03, 0x80, 0x7F, 0xCF,
0xF9, 0xC0, 0x38, 0x07, 0x00, 0xE0, 0x1C, 0x03, 0xFF, 0xFF, 0xF0, 0x3B, 0x87, 0x70, 0x00, 0x7F, 0xEF, 0xFD, 0xC0,
0x38, 0x07, 0x00, 0xE0, 0x1F, 0xF3, 0xFE, 0x70, 0x0E, 0x01, 0xC0, 0x38, 0x07, 0x00, 0xFF, 0xFF, 0xFC, 0xE7, 0x9C,
0x60, 0x39, 0xCE, 0x73, 0x9C, 0xE7, 0x39, 0xCE, 0x73, 0x9C, 0xE0, 0x33, 0xFD, 0xC0, 0x73, 0x9C, 0xE7, 0x39, 0xCE,
0x73, 0x9C, 0xE7, 0x39, 0xC0, 0x18, 0x79, 0xFB, 0x30, 0x07, 0x0E, 0x1C, 0x38, 0x70, 0xE1, 0xC3, 0x87, 0x0E, 0x1C,
0x38, 0x70, 0xE1, 0xC0, 0xEE, 0xEE, 0x00, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,
0x38, 0x38, 0x38, 0x3F, 0xC0, 0x7F, 0xE0, 0xE7, 0xE1, 0xC1, 0xE3, 0x81, 0xC7, 0x03, 0xBF, 0x87, 0x7F, 0x0E, 0x38,
0x1C, 0x70, 0x38, 0xE0, 0x71, 0xC1, 0xE3, 0x9F, 0x87, 0xFE, 0x0F, 0xF0, 0x00, 0x0F, 0xC0, 0xFE, 0x07, 0xF0, 0x00,
0x0E, 0x03, 0xF8, 0x1F, 0xE0, 0xFF, 0x87, 0xFE, 0x3F, 0x71, 0xF9, 0xCF, 0xCF, 0x7E, 0x3B, 0xF0, 0xFF, 0x87, 0xFC,
0x1F, 0xE0, 0x7F, 0x03, 0xF8, 0x0E, 0x06, 0x00, 0x0E, 0x00, 0x1E, 0x00, 0x1C, 0x00, 0x00, 0x03, 0xFC, 0x0F, 0xFC,
0x3F, 0x7C, 0x70, 0x3D, 0xE0, 0x3B, 0x80, 0x77, 0x00, 0xEE, 0x01, 0xFC, 0x03, 0xB8, 0x07, 0x78, 0x0E, 0x70, 0x3C,
0xFD, 0xF0, 0xFF, 0xC0, 0xFF, 0x00, 0x01, 0xC0, 0x07, 0x80, 0x0E, 0x00, 0x18, 0x00, 0x00, 0x03, 0xFC, 0x0F, 0xFC,
0x3F, 0x7C, 0x70, 0x3D, 0xE0, 0x3B, 0x80, 0x77, 0x00, 0xEE, 0x01, 0xFC, 0x03, 0xB8, 0x07, 0x78, 0x0E, 0x70, 0x3C,
0xFD, 0xF0, 0xFF, 0xC0, 0xFF, 0x00, 0x03, 0x00, 0x0F, 0x00, 0x3F, 0x00, 0x66, 0x00, 0x00, 0x03, 0xFC, 0x0F, 0xFC,
0x3F, 0x7C, 0x70, 0x3D, 0xE0, 0x3B, 0x80, 0x77, 0x00, 0xEE, 0x01, 0xFC, 0x03, 0xB8, 0x07, 0x78, 0x0E, 0x70, 0x3C,
0xFD, 0xF0, 0xFF, 0xC0, 0xFF, 0x00, 0x0F, 0x60, 0x1F, 0xC0, 0x7F, 0x00, 0x00, 0x01, 0xFE, 0x07, 0xFE, 0x1F, 0xBE,
0x38, 0x1E, 0xF0, 0x1D, 0xC0, 0x3B, 0x80, 0x77, 0x00, 0xFE, 0x01, 0xDC, 0x03, 0xBC, 0x07, 0x38, 0x1E, 0x7E, 0xF8,
0x7F, 0xE0, 0x7F, 0x80, 0x0E, 0xE0, 0x1D, 0xC0, 0x00, 0x00, 0xFF, 0x03, 0xFF, 0x0F, 0xDF, 0x1C, 0x0F, 0x78, 0x0E,
0xE0, 0x1D, 0xC0, 0x3B, 0x80, 0x7F, 0x00, 0xEE, 0x01, 0xDE, 0x03, 0x9C, 0x0F, 0x3F, 0x7C, 0x3F, 0xF0, 0x3F, 0xC0,
0x61, 0xBC, 0xE7, 0xF8, 0xFC, 0x1E, 0x0F, 0xC7, 0xFB, 0xCE, 0x61, 0x80, 0x1F, 0xFC, 0x7F, 0xF9, 0xFB, 0xE3, 0x83,
0xEE, 0x0F, 0xDC, 0x3F, 0xB8, 0xF7, 0x71, 0xCF, 0xE7, 0x1D, 0xDC, 0x3B, 0xF0, 0x73, 0xE1, 0xE7, 0xEF, 0x8F, 0xFE,
0x1F, 0xF8, 0x00, 0x0C, 0x00, 0xE0, 0x07, 0x00, 0x30, 0x00, 0x0E, 0x07, 0xE0, 0x7E, 0x07, 0xE0, 0x7E, 0x07, 0xE0,
0x7E, 0x07, 0xE0, 0x7E, 0x07, 0xE0, 0x7E, 0x07, 0x70, 0x77, 0xDE, 0x3F, 0xE1, 0xFC, 0x03, 0x80, 0x38, 0x07, 0x00,
0x60, 0x00, 0x0E, 0x07, 0xE0, 0x7E, 0x07, 0xE0, 0x7E, 0x07, 0xE0, 0x7E, 0x07, 0xE0, 0x7E, 0x07, 0xE0, 0x7E, 0x07,
0x70, 0x77, 0xDE, 0x3F, 0xE1, 0xFC, 0x06, 0x00, 0xF0, 0x1F, 0x81, 0x98, 0x00, 0x0E, 0x07, 0xE0, 0x7E, 0x07, 0xE0,
0x7E, 0x07, 0xE0, 0x7E, 0x07, 0xE0, 0x7E, 0x07, 0xE0, 0x7E, 0x07, 0x70, 0x77, 0xDE, 0x3F, 0xE1, 0xFC, 0x1D, 0xC1,
0xDC, 0x00, 0x0E, 0x07, 0xE0, 0x7E, 0x07, 0xE0, 0x7E, 0x07, 0xE0, 0x7E, 0x07, 0xE0, 0x7E, 0x07, 0xE0, 0x7E, 0x07,
0x70, 0x77, 0xDE, 0x3F, 0xE1, 0xFC, 0x01, 0x80, 0x1C, 0x01, 0xC0, 0x0C, 0x00, 0x00, 0x70, 0x3F, 0xC1, 0xCE, 0x1E,
0x38, 0xE1, 0xCE, 0x07, 0x70, 0x3F, 0x00, 0xF8, 0x07, 0x80, 0x1C, 0x00, 0xE0, 0x07, 0x00, 0x38, 0x01, 0xC0, 0x0E,
0x00, 0xE0, 0x0E, 0x00, 0xFF, 0x8F, 0xFC, 0xE7, 0xEE, 0x0E, 0xE0, 0xEE, 0x0E, 0xE0, 0xEE, 0x7E, 0xFF, 0xCF, 0xF8,
0xE0, 0x0E, 0x00, 0xE0, 0x00, 0x3F, 0x87, 0xF8, 0x7B, 0xCE, 0x1C, 0xE1, 0xCE, 0x38, 0xE7, 0x0E, 0x60, 0xEF, 0x8E,
0x7C, 0xE7, 0xEE, 0x1F, 0xE0, 0x7E, 0xEF, 0xEF, 0xEE, 0xFC, 0x18, 0x07, 0x00, 0xE0, 0x18, 0x00, 0x0F, 0xC3, 0xF8,
0xCF, 0x01, 0xCF, 0xF7, 0xFD, 0xE7, 0xE1, 0xDE, 0x77, 0xFC, 0xFF, 0x03, 0x01, 0xC0, 0xE0, 0x30, 0x00, 0x0F, 0xC3,
0xF8, 0xCF, 0x01, 0xCF, 0xF7, 0xFD, 0xE7, 0xE1, 0xDE, 0x77, 0xFC, 0xFF, 0x0E, 0x03, 0xC1, 0xF0, 0xEE, 0x00, 0x0F,
0xC3, 0xF8, 0xCF, 0x01, 0xCF, 0xF7, 0xFD, 0xE7, 0xE1, 0xDE, 0x77, 0xFC, 0xFF, 0x1F, 0x8F, 0xE3, 0xF0, 0x00, 0x00,
0x0F, 0xC3, 0xF8, 0xCF, 0x01, 0xCF, 0xF7, 0xFD, 0xE7, 0xE1, 0xDE, 0x77, 0xFC, 0xFF, 0x3B, 0x8E, 0xE0, 0x00, 0x00,
0x3F, 0x0F, 0xE3, 0x3C, 0x07, 0x3F, 0xDF, 0xF7, 0x9F, 0x87, 0x79, 0xDF, 0xF3, 0xFC, 0x1F, 0x06, 0xC1, 0xB0, 0x7C,
0x00, 0x0F, 0xC3, 0xF8, 0xCF, 0x01, 0xCF, 0xF7, 0xFD, 0xE7, 0xE1, 0xDE, 0x77, 0xFC, 0xFF, 0x3F, 0x7E, 0x1F, 0xFF,
0x8C, 0xFD, 0xE0, 0x38, 0x73, 0xFF, 0xFB, 0xFF, 0xFD, 0xE7, 0x00, 0xC3, 0x80, 0x7B, 0xFB, 0x3F, 0xFF, 0x8F, 0xFF,
0xC0, 0x3F, 0x3F, 0xBE, 0xDC, 0x0E, 0x07, 0x03, 0x81, 0xC0, 0xFB, 0x3F, 0xCF, 0xE1, 0x80, 0xE0, 0xF0, 0xF8, 0x38,
0x0F, 0x01, 0xE0, 0x38, 0x00, 0x0F, 0xC7, 0xFB, 0xFF, 0xE1, 0xFF, 0xFF, 0xFF, 0x80, 0xE0, 0x3E, 0x67, 0xF8, 0xFE,
0x07, 0x03, 0xC1, 0xE0, 0x70, 0x00, 0x0F, 0xC7, 0xFB, 0xFF, 0xE1, 0xFF, 0xFF, 0xFF, 0x80, 0xE0, 0x3E, 0x67, 0xF8,
0xFE, 0x0C, 0x07, 0x83, 0xF0, 0xCC, 0x00, 0x0F, 0xC7, 0xFB, 0xFF, 0xE1, 0xFF, 0xFF, 0xFF, 0x80, 0xE0, 0x3E, 0x67,
0xF8, 0xFE, 0x7F, 0x9F, 0xE0, 0x00, 0x00, 0x3F, 0x1F, 0xEF, 0xFF, 0x87, 0xFF, 0xFF, 0xFE, 0x03, 0x80, 0xF9, 0x9F,
0xE3, 0xF8, 0xC7, 0x1C, 0x60, 0x39, 0xCE, 0x73, 0x9C, 0xE7, 0x39, 0xCE, 0x33, 0xB9, 0x80, 0x73, 0x9C, 0xE7, 0x39,
0xCE, 0x73, 0x9C, 0x18, 0x79, 0xFB, 0x30, 0x07, 0x0E, 0x1C, 0x38, 0x70, 0xE1, 0xC3, 0x87, 0x0E, 0x1C, 0xEF, 0xDC,
0x00, 0x03, 0x87, 0x0E, 0x1C, 0x38, 0x70, 0xE1, 0xC3, 0x87, 0x0E, 0x00, 0x07, 0xE1, 0xFC, 0x3E, 0x07, 0xE0, 0x1C,
0x7F, 0x9F, 0xF7, 0xDF, 0xE0, 0xFC, 0x1F, 0x83, 0x70, 0xEE, 0x1D, 0xF7, 0x9F, 0xE1, 0xF8, 0x3F, 0x8F, 0xE7, 0xF0,
0x00, 0x00, 0x3F, 0xCF, 0xFB, 0xDF, 0xE1, 0xF8, 0x7E, 0x1F, 0x87, 0xE1, 0xF8, 0x7E, 0x1F, 0x87, 0x18, 0x03, 0x80,
0x38, 0x07, 0x00, 0x00, 0x7E, 0x1F, 0xE7, 0xFE, 0xE1, 0xDC, 0x1F, 0x83, 0xF0, 0x7E, 0x1D, 0xF7, 0x9F, 0xE1, 0xF8,
0x07, 0x00, 0xE0, 0x38, 0x06, 0x00, 0x00, 0x7E, 0x1F, 0xE7, 0xFE, 0xE1, 0xDC, 0x1F, 0x83, 0xF0, 0x7E, 0x1D, 0xF7,
0x9F, 0xE1, 0xF8, 0x0C, 0x03, 0xC0, 0xFC, 0x19, 0x80, 0x00, 0x7E, 0x1F, 0xE7, 0xFE, 0xE1, 0xDC, 0x1F, 0x83, 0xF0,
0x7E, 0x1D, 0xF7, 0x9F, 0xE1, 0xF8, 0x3D, 0x87, 0xF1, 0xFC, 0x00, 0x00, 0x00, 0x7E, 0x1F, 0xE7, 0xFE, 0xE1, 0xDC,
0x1F, 0x83, 0xF0, 0x7E, 0x1D, 0xF7, 0x9F, 0xE1, 0xF8, 0x3B, 0x87, 0x70, 0x00, 0x00, 0x03, 0xF0, 0xFF, 0x3F, 0xF7,
0x0E, 0xE0, 0xFC, 0x1F, 0x83, 0xF0, 0xEF, 0xBC, 0xFF, 0x0F, 0xC0, 0x1C, 0x07, 0x81, 0xC0, 0x00, 0x00, 0x3F, 0xFF,
0xFC, 0x00, 0x00, 0x07, 0x01, 0xE0, 0x70, 0x3F, 0xCF, 0xFB, 0xEF, 0x73, 0xEE, 0x7F, 0xDD, 0xFF, 0x3F, 0xCE, 0xFB,
0xDF, 0xF3, 0xFC, 0x00, 0x38, 0x0F, 0x01, 0xE0, 0x38, 0x00, 0x38, 0x7E, 0x1F, 0x87, 0xE1, 0xF8, 0x7E, 0x1F, 0x87,
0xE1, 0xFE, 0xF7, 0xFC, 0xFF, 0x07, 0x03, 0xC1, 0xE0, 0x70, 0x00, 0x38, 0x7E, 0x1F, 0x87, 0xE1, 0xF8, 0x7E, 0x1F,
0x87, 0xE1, 0xFE, 0xF7, 0xFC, 0xFF, 0x0C, 0x07, 0x83, 0xF0, 0xCC, 0x00, 0x38, 0x7E, 0x1F, 0x87, 0xE1, 0xF8, 0x7E,
0x1F, 0x87, 0xE1, 0xFE, 0xF7, 0xFC, 0xFF, 0x77, 0x1D, 0xC0, 0x00, 0x00, 0xE1, 0xF8, 0x7E, 0x1F, 0x87, 0xE1, 0xF8,
0x7E, 0x1F, 0x87, 0xFB, 0xDF, 0xF3, 0xFC, 0x03, 0x00, 0xE0, 0x38, 0x06, 0x00, 0x01, 0xC3, 0xB8, 0x73, 0x8E, 0x71,
0xCE, 0x70, 0xEE, 0x1D, 0xC1, 0xF0, 0x3E, 0x07, 0xC0, 0x70, 0x0E, 0x17, 0x83, 0xF0, 0x7C, 0x00, 0xE0, 0x1C, 0x03,
0x80, 0x70, 0x0E, 0x01, 0xFE, 0x3F, 0xE7, 0xBE, 0xE1, 0xDC, 0x1F, 0x83, 0xF0, 0x7E, 0x1D, 0xF7, 0xBF, 0xF7, 0xFC,
0xE0, 0x1C, 0x03, 0x80, 0x70, 0x00, 0x3B, 0x87, 0x70, 0x00, 0x00, 0x0E, 0x1D, 0xC3, 0x9C, 0x73, 0x8E, 0x73, 0x87,
0x70, 0xEE, 0x0F, 0x81, 0xF0, 0x3E, 0x03, 0x80, 0x70, 0xBC, 0x1F, 0x83, 0xE0, 0x00, 0x0F, 0xC0, 0x3F, 0x00, 0x00,
0x01, 0xE0, 0x07, 0x80, 0x1E, 0x00, 0xFC, 0x03, 0xF0, 0x1C, 0xE0, 0x73, 0x81, 0x8E, 0x0E, 0x1C, 0x3F, 0xF1, 0xFF,
0xE7, 0x03, 0x9C, 0x0E, 0xE0, 0x1F, 0x80, 0x70, 0x3F, 0x8F, 0xE0, 0x00, 0x00, 0x3F, 0x0F, 0xE3, 0x3C, 0x07, 0x3F,
0xDF, 0xF7, 0x9F, 0x87, 0x79, 0xDF, 0xF3, 0xFC, 0x0F, 0xC0, 0x3F, 0x00, 0x78, 0x00, 0x00, 0x07, 0x80, 0x1E, 0x00,
0x78, 0x03, 0xF0, 0x0F, 0xC0, 0x73, 0x81, 0xCE, 0x06, 0x38, 0x38, 0x70, 0xFF, 0xC7, 0xFF, 0x9C, 0x0E, 0x70, 0x3B,
0x80, 0x7E, 0x01, 0xC0, 0x3B, 0x87, 0xC1, 0xF0, 0x00, 0x00, 0x0F, 0xC3, 0xF8, 0xCF, 0x01, 0xCF, 0xF7, 0xFD, 0xE7,
0xE1, 0xDE, 0x77, 0xFC, 0xFF, 0x07, 0x80, 0x1E, 0x00, 0x78, 0x03, 0xF0, 0x0F, 0xC0, 0x73, 0x81, 0xCE, 0x06, 0x38,
0x38, 0x70, 0xFF, 0xC7, 0xFF, 0x9C, 0x0E, 0x70, 0x3B, 0x80, 0x7E, 0x01, 0xC0, 0x0F, 0x00, 0x38, 0x00, 0xE0, 0x03,
0xC0, 0x3F, 0x0F, 0xE3, 0x3C, 0x07, 0x3F, 0xDF, 0xF7, 0x9F, 0x87, 0x79, 0xDF, 0xF3, 0xFC, 0x0E, 0x07, 0x00, 0xF0,
0x01, 0x80, 0x38, 0x07, 0x00, 0x60, 0x00, 0x00, 0xFE, 0x3F, 0xF7, 0xEE, 0x70, 0x0F, 0x00, 0xE0, 0x0E, 0x00, 0xE0,
0x0E, 0x00, 0xE0, 0x0E, 0x00, 0x70, 0x07, 0xEF, 0x3F, 0xF1, 0xFF, 0x07, 0x07, 0x87, 0x83, 0x80, 0x01, 0xF9, 0xFD,
0xF6, 0xE0, 0x70, 0x38, 0x1C, 0x0E, 0x07, 0xD9, 0xFE, 0x7F, 0x03, 0x00, 0x78, 0x0F, 0xC0, 0xCC, 0x00, 0x00, 0xFE,
0x3F, 0xF7, 0xEE, 0x70, 0x0F, 0x00, 0xE0, 0x0E, 0x00, 0xE0, 0x0E, 0x00, 0xE0, 0x0E, 0x00, 0x70, 0x07, 0xEF, 0x3F,
0xF1, 0xFF, 0x0C, 0x0F, 0x0F, 0xC6, 0x60, 0x01, 0xF9, 0xFD, 0xF6, 0xE0, 0x70, 0x38, 0x1C, 0x0E, 0x07, 0xD9, 0xFE,
0x7F, 0x07, 0x00, 0x70, 0x07, 0x00, 0x00, 0x0F, 0xE3, 0xFF, 0x7E, 0xE7, 0x00, 0xF0, 0x0E, 0x00, 0xE0, 0x0E, 0x00,
0xE0, 0x0E, 0x00, 0xE0, 0x07, 0x00, 0x7E, 0xF3, 0xFF, 0x1F, 0xF0, 0x0C, 0x0F, 0x03, 0x00, 0x00, 0x01, 0xF9, 0xFD,
0xF6, 0xE0, 0x70, 0x38, 0x1C, 0x0E, 0x07, 0xD9, 0xFE, 0x7F, 0x0C, 0xC0, 0xFC, 0x07, 0x80, 0x30, 0x00, 0x00, 0xFE,
0x3F, 0xF7, 0xEE, 0x70, 0x0F, 0x00, 0xE0, 0x0E, 0x00, 0xE0, 0x0E, 0x00, 0xE0, 0x0E, 0x00, 0x70, 0x07, 0xEF, 0x3F,
0xF1, 0xFF, 0x33, 0x1F, 0x87, 0x81, 0x80, 0x01, 0xF9, 0xFD, 0xF6, 0xE0, 0x70, 0x38, 0x1C, 0x0E, 0x07, 0xD9, 0xFE,
0x7F, 0x19, 0x80, 0xFC, 0x03, 0xC0, 0x0C, 0x00, 0x00, 0x7F, 0xC3, 0xFF, 0x9C, 0x7E, 0xE0, 0xF7, 0x03, 0xF8, 0x0F,
0xC0, 0x7E, 0x03, 0xF0, 0x1F, 0x80, 0xFC, 0x0F, 0xE0, 0xF7, 0x1F, 0xBF, 0xF9, 0xFF, 0x00, 0x01, 0xD8, 0x07, 0x60,
0x1D, 0x80, 0x76, 0x01, 0xD8, 0xFF, 0x07, 0xFC, 0x3E, 0xF0, 0xE1, 0xC3, 0x87, 0x0E, 0x1C, 0x38, 0x70, 0xE1, 0xC3,
0xE7, 0x07, 0xFC, 0x0F, 0xF0, 0x3F, 0xC0, 0x7F, 0xE0, 0xE7, 0xE1, 0xC1, 0xE3, 0x81, 0xC7, 0x03, 0xBF, 0x87, 0x7F,
0x0E, 0x38, 0x1C, 0x70, 0x38, 0xE0, 0x71, 0xC1, 0xE3, 0x9F, 0x87, 0xFE, 0x0F, 0xF0, 0x00, 0x01, 0xC0, 0xFF, 0x0F,
0xF0, 0x1C, 0x01, 0xC3, 0xFC, 0x7F, 0xCF, 0xBC, 0xE1, 0xCE, 0x1C, 0xE1, 0xCE, 0x1C, 0xE1, 0xCF, 0x9C, 0x7F, 0xC3,
0xFC, 0x3F, 0x87, 0xF0, 0x00, 0x7F, 0xEF, 0xFD, 0xC0, 0x38, 0x07, 0x00, 0xE0, 0x1F, 0xF3, 0xFE, 0x70, 0x0E, 0x01,
0xC0, 0x38, 0x07, 0x00, 0xFF, 0xFF, 0xFC, 0x7F, 0x1F, 0xC0, 0x00, 0x00, 0x3F, 0x1F, 0xEF, 0xFF, 0x87, 0xFF, 0xFF,
0xFE, 0x03, 0x80, 0xF9, 0x9F, 0xE3, 0xF8, 0x3F, 0x07, 0xE0, 0x78, 0x00, 0x0F, 0xFD, 0xFF, 0xB8, 0x07, 0x00, 0xE0,
0x1C, 0x03, 0xFE, 0x7F, 0xCE, 0x01, 0xC0, 0x38, 0x07, 0x00, 0xE0, 0x1F, 0xFF, 0xFF, 0x80, 0x37, 0x0F, 0xC1, 0xE0,
0x00, 0x00, 0x0F, 0xC7, 0xFB, 0xFF, 0xE1, 0xFF, 0xFF, 0xFF, 0x80, 0xE0, 0x3E, 0x67, 0xF8, 0xFE, 0x0E, 0x01, 0xC0,
0x38, 0x00, 0x0F, 0xFD, 0xFF, 0xB8, 0x07, 0x00, 0xE0, 0x1C, 0x03, 0xFE, 0x7F, 0xCE, 0x01, 0xC0, 0x38, 0x07, 0x00,
0xE0, 0x1F, 0xFF, 0xFF, 0x80, 0x0C, 0x07, 0x80, 0xC0, 0x00, 0x00, 0x0F, 0xC7, 0xFB, 0xFF, 0xE1, 0xFF, 0xFF, 0xFF,
0x80, 0xE0, 0x3E, 0x67, 0xF8, 0xFE, 0xFF, 0xDF, 0xFB, 0x80, 0x70, 0x0E, 0x01, 0xC0, 0x3F, 0xE7, 0xFC, 0xE0, 0x1C,
0x03, 0x80, 0x70, 0x0E, 0x01, 0xFF, 0xFF, 0xF8, 0x0E, 0x03, 0x80, 0x70, 0x07, 0x80, 0x3F, 0x1F, 0xEF, 0xFF, 0x87,
0xFF, 0xFF, 0xFE, 0x03, 0x80, 0xF9, 0x9F, 0xE3, 0xF8, 0x1C, 0x06, 0x01, 0xE0, 0x33, 0x07, 0xE0, 0x78, 0x06, 0x00,
0x01, 0xFF, 0xBF, 0xF7, 0x00, 0xE0, 0x1C, 0x03, 0x80, 0x7F, 0xCF, 0xF9, 0xC0, 0x38, 0x07, 0x00, 0xE0, 0x1C, 0x03,
0xFF, 0xFF, 0xF0, 0x33, 0x0F, 0xC1, 0xE0, 0x30, 0x00, 0x00, 0x03, 0xF1, 0xFE, 0xFF, 0xF8, 0x7F, 0xFF, 0xFF, 0xE0,
0x38, 0x0F, 0x99, 0xFE, 0x3F, 0x80, 0x03, 0x00, 0x78, 0x0F, 0xC0, 0xCC, 0x00, 0x00, 0xFF, 0x3F, 0xF7, 0xEF, 0x70,
0x0F, 0x00, 0xE0, 0x0E, 0x00, 0xE0, 0x0E, 0x07, 0xE0, 0x7E, 0x07, 0x70, 0x77, 0xEF, 0x3F, 0xF1, 0xFF, 0x0C, 0x07,
0x83, 0xF0, 0xCC, 0x00, 0x0F, 0xF7, 0xFF, 0xE7, 0xE1, 0xF8, 0x7E, 0x1F, 0x87, 0xE1, 0xFF, 0xF7, 0xFC, 0xFF, 0x01,
0xDD, 0xFF, 0xFB, 0xFC, 0x0D, 0xC0, 0xFC, 0x07, 0x80, 0x00, 0x0F, 0xF3, 0xFF, 0x7E, 0xF7, 0x00, 0xF0, 0x0E, 0x00,
0xE0, 0x0E, 0x00, 0xE0, 0x7E, 0x07, 0xE0, 0x77, 0x07, 0x7E, 0xF3, 0xFF, 0x1F, 0xF0, 0x3B, 0x8F, 0xC1, 0xF0, 0x00,
0x00, 0x0F, 0xF7, 0xFF, 0xE7, 0xE1, 0xF8, 0x7E, 0x1F, 0x87, 0xE1, 0xFF, 0xF7, 0xFC, 0xFF, 0x01, 0xDD, 0xFF, 0xFB,
0xFC, 0x07, 0x00, 0x78, 0x07, 0x00, 0x00, 0x0F, 0xF3, 0xFF, 0x7E, 0xF7, 0x00, 0xF0, 0x0E, 0x00, 0xE0, 0x0E, 0x00,
0xE0, 0x7E, 0x07, 0xE0, 0x77, 0x07, 0x7E, 0xF3, 0xFF, 0x1F, 0xF0, 0x0E, 0x03, 0x80, 0xE0, 0x00, 0x00, 0x0F, 0xF7,
0xFF, 0xE7, 0xE1, 0xF8, 0x7E, 0x1F, 0x87, 0xE1, 0xFF, 0xF7, 0xFC, 0xFF, 0x01, 0xDD, 0xFF, 0xFB, 0xFC, 0x0F, 0xF3,
0xFF, 0x7E, 0xF7, 0x00, 0xF0, 0x0E, 0x00, 0xE0, 0x0E, 0x00, 0xE0, 0x7E, 0x07, 0xE0, 0x77, 0x07, 0x7E, 0xF3, 0xFF,
0x1F, 0xF0, 0x00, 0x07, 0x00, 0xF0, 0x0F, 0x00, 0x07, 0x03, 0xC0, 0xE0, 0x38, 0x00, 0x00, 0x03, 0xFD, 0xFF, 0xF9,
0xF8, 0x7E, 0x1F, 0x87, 0xE1, 0xF8, 0x7F, 0xFD, 0xFF, 0x3F, 0xC0, 0x77, 0x7F, 0xFE, 0xFF, 0x00, 0x06, 0x00, 0xF0,
0x1F, 0x81, 0xD8, 0x00, 0x0E, 0x03, 0xE0, 0x3E, 0x03, 0xE0, 0x3E, 0x03, 0xE0, 0x3F, 0xFF, 0xFF, 0xFE, 0x03, 0xE0,
0x3E, 0x03, 0xE0, 0x3E, 0x03, 0xE0, 0x3E, 0x03, 0x70, 0x3E, 0x0D, 0xC0, 0x00, 0xE0, 0x38, 0x0E, 0x03, 0x80, 0xE0,
0x3F, 0xCF, 0xFB, 0xDF, 0xE1, 0xF8, 0x7E, 0x1F, 0x87, 0xE1, 0xF8, 0x7E, 0x1F, 0x87, 0x38, 0x1D, 0xFF, 0xFF, 0xFF,
0xF9, 0xC0, 0xE3, 0x81, 0xC7, 0x03, 0x8F, 0xFF, 0x1F, 0xFE, 0x38, 0x1C, 0x70, 0x38, 0xE0, 0x71, 0xC0, 0xE3, 0x81,
0xC7, 0x03, 0x8E, 0x07, 0x00, 0x70, 0x1F, 0xE3, 0xFC, 0x38, 0x07, 0x00, 0xFF, 0x1F, 0xF3, 0xDF, 0x70, 0xEE, 0x1D,
0xC3, 0xB8, 0x77, 0x0E, 0xE1, 0xDC, 0x3B, 0x87, 0x7F, 0xFE, 0xFE, 0x00, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,
0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x7E, 0xFE, 0xFE, 0x00, 0x00, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38,
0x38, 0x38, 0x38, 0x38, 0x38, 0xFF, 0xFC, 0x01, 0xC3, 0x87, 0x0E, 0x1C, 0x38, 0x70, 0xE1, 0xC3, 0x87, 0x0E, 0x1C,
0x38, 0x70, 0xFF, 0xFC, 0x00, 0x03, 0x87, 0x0E, 0x1C, 0x38, 0x70, 0xE1, 0xC3, 0x87, 0x0E, 0x00, 0xDF, 0xFF, 0x80,
0x71, 0xC7, 0x1C, 0x71, 0xC7, 0x1C, 0x71, 0xC7, 0x1C, 0x71, 0xC7, 0x00, 0x6E, 0xFD, 0xF0, 0x00, 0x07, 0x0E, 0x1C,
0x38, 0x70, 0xE1, 0xC3, 0x87, 0x0E, 0x1C, 0x73, 0x9C, 0xE7, 0x39, 0xCE, 0x73, 0x9C, 0xE7, 0x39, 0xCE, 0xE7, 0x3C,
0x77, 0x00, 0x07, 0x77, 0x77, 0x77, 0x77, 0x77, 0x7E, 0xEF, 0xEE, 0xE0, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE,
0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0xE0, 0x1F, 0x80, 0x7E, 0x01, 0xF8, 0x07, 0xE0, 0x1F, 0x80, 0x7E, 0x01, 0xF8,
0x07, 0xE0, 0x1F, 0x80, 0x7E, 0x01, 0xF8, 0x07, 0xE7, 0x7F, 0x9F, 0xEE, 0x7F, 0x00, 0xE7, 0xF3, 0xC0, 0x00, 0x00,
0x07, 0x1B, 0x8D, 0xC6, 0xE3, 0x71, 0xB8, 0xDC, 0x6E, 0x37, 0x1B, 0x8D, 0xC6, 0x03, 0x03, 0x87, 0xC3, 0xC0, 0x01,
0x80, 0x78, 0x1F, 0x83, 0x30, 0x00, 0x07, 0x00, 0xE0, 0x1C, 0x03, 0x80, 0x70, 0x0E, 0x01, 0xC0, 0x38, 0x07, 0x00,
0xE0, 0x1C, 0x03, 0x9D, 0xF3, 0xFC, 0x7F, 0x00, 0x0C, 0x1E, 0x3F, 0x33, 0x00, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C,
0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x3C, 0xFC, 0xF8, 0xE0, 0xF7, 0x0F, 0x38, 0xF1, 0xC7, 0x0E, 0x70, 0x77, 0x03,
0xF0, 0x1F, 0x80, 0xFE, 0x07, 0x78, 0x39, 0xE1, 0xC7, 0x8E, 0x1E, 0x70, 0x7B, 0x81, 0xC0, 0x00, 0x0E, 0x00, 0xF0,
0x07, 0x80, 0xE0, 0x38, 0x0E, 0x03, 0x80, 0xE0, 0x38, 0xEE, 0x7B, 0xBC, 0xFE, 0x3F, 0x0F, 0xC3, 0xF8, 0xEF, 0x39,
0xCE, 0x3B, 0x8F, 0x00, 0x07, 0x03, 0xC0, 0xF0, 0xE3, 0xF9, 0xEE, 0xF3, 0xF8, 0xFC, 0x3F, 0x0F, 0xE3, 0xBC, 0xE7,
0xB8, 0xEE, 0x1C, 0x18, 0x0E, 0x07, 0x01, 0x80, 0x00, 0x38, 0x0E, 0x03, 0x80, 0xE0, 0x38, 0x0E, 0x03, 0x80, 0xE0,
0x38, 0x0E, 0x03, 0x80, 0xE0, 0x38, 0x0F, 0xFF, 0xFF, 0x37, 0xFC, 0x0E, 0x73, 0x9C, 0xE7, 0x39, 0xCE, 0x73, 0x9C,
0xE7, 0xBE, 0xE0, 0xE0, 0x38, 0x0E, 0x03, 0x80, 0xE0, 0x38, 0x0E, 0x03, 0x80, 0xE0, 0x38, 0x0E, 0x03, 0x80, 0xE0,
0x3F, 0xFF, 0xFC, 0x00, 0x0E, 0x07, 0x01, 0xC0, 0x71, 0xC7, 0x1C, 0x71, 0xC7, 0x1C, 0x71, 0xC7, 0x1C, 0x71, 0xE7,
0xCE, 0x01, 0xCF, 0x3C, 0xEE, 0x3B, 0x8E, 0xE3, 0xB8, 0xEE, 0x38, 0x0E, 0x03, 0x80, 0xE0, 0x38, 0x0E, 0x03, 0x80,
0xE0, 0x3F, 0xFF, 0xFC, 0xEF, 0xBE, 0xFB, 0xEF, 0x8E, 0x38, 0xE3, 0x8E, 0x38, 0xE3, 0xCF, 0x9C, 0xE0, 0x38, 0x0E,
0x03, 0x80, 0xE0, 0x38, 0x0E, 0x3B, 0x8E, 0xE3, 0xB8, 0x0E, 0x03, 0x80, 0xE0, 0x3F, 0xFF, 0xFC, 0xE1, 0xC3, 0x87,
0x0E, 0x1C, 0x38, 0x77, 0xEF, 0xDF, 0x87, 0x0E, 0x1E, 0x3E, 0x38, 0x1C, 0x01, 0xC0, 0x1C, 0x01, 0xC0, 0x1D, 0x01,
0xF8, 0x1F, 0x07, 0xE0, 0x7C, 0x07, 0xC0, 0x1C, 0x01, 0xC0, 0x1C, 0x01, 0xFF, 0x1F, 0xF0, 0x30, 0xE1, 0xC3, 0x87,
0x0E, 0x9F, 0x7E, 0xF1, 0xE1, 0xC3, 0x87, 0x07, 0x0F, 0x1E, 0x01, 0x80, 0x1C, 0x01, 0xC0, 0x0C, 0x00, 0x00, 0x70,
0x1F, 0xC0, 0xFF, 0x07, 0xFC, 0x3F, 0xF1, 0xFB, 0x8F, 0xCE, 0x7E, 0x7B, 0xF1, 0xDF, 0x87, 0xFC, 0x3F, 0xE0, 0xFF,
0x03, 0xF8, 0x1F, 0xC0, 0x70, 0x07, 0x03, 0xC0, 0xE0, 0x30, 0x00, 0x3F, 0xCF, 0xFB, 0xDF, 0xE1, 0xF8, 0x7E, 0x1F,
0x87, 0xE1, 0xF8, 0x7E, 0x1F, 0x87, 0xE0, 0x3F, 0x81, 0xFE, 0x0F, 0xF8, 0x7F, 0xE3, 0xF7, 0x1F, 0x9C, 0xFC, 0xF7,
0xE3, 0xBF, 0x0F, 0xF8, 0x7F, 0xC1, 0xFE, 0x07, 0xF0, 0x3F, 0x80, 0xE0, 0x00, 0x07, 0x00, 0x78, 0x03, 0x80, 0xFF,
0x3F, 0xEF, 0x7F, 0x87, 0xE1, 0xF8, 0x7E, 0x1F, 0x87, 0xE1, 0xF8, 0x7E, 0x1C, 0x00, 0x0E, 0x0F, 0x03, 0xC0, 0x0C,
0xC0, 0x7E, 0x01, 0xE0, 0x06, 0x00, 0x00, 0x70, 0x1F, 0xC0, 0xFF, 0x07, 0xFC, 0x3F, 0xF1, 0xFB, 0x8F, 0xCE, 0x7E,
0x7B, 0xF1, 0xDF, 0x87, 0xFC, 0x3F, 0xE0, 0xFF, 0x03, 0xF8, 0x1F, 0xC0, 0x70, 0x33, 0x0F, 0xC1, 0xE0, 0x30, 0x00,
0x3F, 0xCF, 0xFB, 0xDF, 0xE1, 0xF8, 0x7E, 0x1F, 0x87, 0xE1, 0xF8, 0x7E, 0x1F, 0x87, 0xE0, 0x1C, 0x03, 0x80, 0x60,
0x00, 0x00, 0xFF, 0x1F, 0xF3, 0xDF, 0x70, 0xEE, 0x1D, 0xC3, 0xB8, 0x77, 0x0E, 0xE1, 0xDC, 0x3B, 0x87, 0xE0, 0x3F,
0x81, 0xFE, 0x0F, 0xF8, 0x7F, 0xE3, 0xF7, 0x1F, 0x9C, 0xFC, 0xF7, 0xE3, 0xBF, 0x0F, 0xF8, 0x3F, 0xC1, 0xFE, 0x07,
0xF0, 0x3F, 0x80, 0xE0, 0x07, 0x00, 0x78, 0x07, 0xC0, 0x3C, 0xFF, 0x3F, 0xEF, 0x7F, 0x87, 0xE1, 0xF8, 0x7E, 0x1F,
0x87, 0xE1, 0xF8, 0x7E, 0x1C, 0x07, 0x03, 0xC1, 0xF0, 0x78, 0x0F, 0xE0, 0x1F, 0xC0, 0x00, 0x00, 0xFF, 0x03, 0xFF,
0x0F, 0xDF, 0x1C, 0x0F, 0x78, 0x0E, 0xE0, 0x1D, 0xC0, 0x3B, 0x80, 0x7F, 0x00, 0xEE, 0x01, 0xDE, 0x03, 0x9C, 0x0F,
0x3F, 0x7C, 0x3F, 0xF0, 0x3F, 0xC0, 0x3F, 0x87, 0xF0, 0x00, 0x00, 0x03, 0xF0, 0xFF, 0x3F, 0xF7, 0x0E, 0xE0, 0xFC,
0x1F, 0x83, 0xF0, 0xEF, 0xBC, 0xFF, 0x0F, 0xC0, 0x0E, 0xC0, 0x1F, 0x80, 0x1E, 0x00, 0x00, 0x01, 0xFE, 0x07, 0xFE,
0x1F, 0xBE, 0x38, 0x1E, 0xF0, 0x1D, 0xC0, 0x3B, 0x80, 0x77, 0x00, 0xFE, 0x01, 0xDC, 0x03, 0xBC, 0x07, 0x38, 0x1E,
0x7E, 0xF8, 0x7F, 0xE0, 0x7F, 0x80, 0x3B, 0x07, 0xE0, 0x7C, 0x00, 0x00, 0x00, 0x7E, 0x1F, 0xE7, 0xFE, 0xE1, 0xDC,
0x1F, 0x83, 0xF0, 0x7E, 0x1D, 0xF7, 0x9F, 0xE1, 0xF8, 0x03, 0x70, 0x0E, 0xE0, 0x3B, 0x80, 0x76, 0x00, 0x00, 0x03,
0xFC, 0x0F, 0xFC, 0x3F, 0x7C, 0x70, 0x3D, 0xE0, 0x3B, 0x80, 0x77, 0x00, 0xEE, 0x01, 0xFC, 0x03, 0xB8, 0x07, 0x78,
0x0E, 0x70, 0x3C, 0xFD, 0xF0, 0xFF, 0xC0, 0xFF, 0x00, 0x0D, 0xC3, 0xB8, 0xEE, 0x1D, 0x80, 0x00, 0x7E, 0x1F, 0xE7,
0xFE, 0xE1, 0xDC, 0x1F, 0x83, 0xF0, 0x7E, 0x1D, 0xF7, 0x9F, 0xE1, 0xF8, 0x0F, 0xFF, 0xE3, 0xFF, 0xFE, 0x7E, 0x30,
0x07, 0x83, 0x00, 0xF0, 0x30, 0x0E, 0x03, 0x00, 0xE0, 0x3F, 0xCE, 0x03, 0xFC, 0xE0, 0x30, 0x0E, 0x03, 0x00, 0xF0,
0x30, 0x07, 0x83, 0x00, 0x7E, 0x30, 0x03, 0xFF, 0xFF, 0x0F, 0xFF, 0xF0, 0x3F, 0x3F, 0x1F, 0xFF, 0xEF, 0xFF, 0x3F,
0x87, 0x87, 0xE0, 0xFF, 0xF8, 0x3F, 0xFE, 0x0E, 0x03, 0x87, 0x80, 0xFB, 0xFD, 0x9F, 0xFF, 0xE3, 0xF3, 0xF8, 0x07,
0x00, 0xF0, 0x0E, 0x00, 0xC0, 0x00, 0x0F, 0xF8, 0xFF, 0xCE, 0x3E, 0xE0, 0xEE, 0x0E, 0xE0, 0xEE, 0x3E, 0xFF, 0xCF,
0xF8, 0xE3, 0x8E, 0x3C, 0xE1, 0xCE, 0x0E, 0xE0, 0xFE, 0x07, 0x0C, 0x3C, 0xF1, 0xC0, 0x1F, 0xFF, 0xFB, 0xE1, 0xC3,
0x87, 0x0E, 0x1C, 0x38, 0x70, 0xFF, 0x8F, 0xFC, 0xE3, 0xEE, 0x0E, 0xE0, 0xEE, 0x0E, 0xE3, 0xEF, 0xFC, 0xFF, 0x8E,
0x38, 0xE3, 0xCE, 0x1C, 0xE0, 0xEE, 0x0F, 0xE0, 0x70, 0x00, 0x0E, 0x01, 0xE0, 0x1E, 0x00, 0x7F, 0x7F, 0x7B, 0x70,
0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x00, 0x70, 0xF0, 0xE0, 0x33, 0x03, 0xF0, 0x1E, 0x00, 0xC0, 0x00, 0x0F,
0xF8, 0xFF, 0xCE, 0x3E, 0xE0, 0xEE, 0x0E, 0xE0, 0xEE, 0x3E, 0xFF, 0xCF, 0xF8, 0xE3, 0x8E, 0x3C, 0xE1, 0xCE, 0x0E,
0xE0, 0xFE, 0x07, 0x66, 0xFC, 0xF0, 0xC0, 0x1F, 0xFF, 0xFB, 0xE1, 0xC3, 0x87, 0x0E, 0x1C, 0x38, 0x70, 0x03, 0x00,
0xE0, 0x38, 0x07, 0x00, 0x00, 0x7F, 0x9F, 0xF3, 0xCE, 0x70, 0x0E, 0x01, 0xE0, 0x3F, 0x03, 0xF8, 0x1F, 0x80, 0x78,
0x07, 0x00, 0xEE, 0x7F, 0xFF, 0x3F, 0xC0, 0x06, 0x07, 0x07, 0x03, 0x00, 0x01, 0xF9, 0xFC, 0xF6, 0x70, 0x3E, 0x0F,
0xC1, 0xF0, 0x3B, 0x3D, 0xFE, 0xFE, 0x06, 0x01, 0xE0, 0x7E, 0x0C, 0xC0, 0x00, 0x7F, 0x9F, 0xF3, 0xCE, 0x70, 0x0E,
0x01, 0xE0, 0x3F, 0x03, 0xF8, 0x1F, 0x80, 0x78, 0x07, 0x00, 0xEE, 0x7F, 0xFF, 0x3F, 0xC0, 0x0C, 0x0F, 0x0F, 0xC6,
0x60, 0x01, 0xF9, 0xFC, 0xF6, 0x70, 0x3E, 0x0F, 0xC1, 0xF0, 0x3B, 0x3D, 0xFE, 0xFE, 0x3F, 0xCF, 0xF9, 0xE7, 0x38,
0x07, 0x00, 0xF0, 0x1F, 0x81, 0xFC, 0x0F, 0xC0, 0x3C, 0x03, 0x80, 0x77, 0x3F, 0xFF, 0x9F, 0xE0, 0x70, 0x1E, 0x03,
0xC0, 0x3F, 0x3F, 0x9E, 0xCE, 0x07, 0xC1, 0xF8, 0x3E, 0x07, 0x67, 0xBF, 0xDF, 0xC3, 0x83, 0xE1, 0xE0, 0x19, 0x83,
0xF0, 0x3C, 0x03, 0x00, 0x00, 0x7F, 0x9F, 0xF3, 0xCE, 0x70, 0x0E, 0x01, 0xE0, 0x3F, 0x03, 0xF8, 0x1F, 0x80, 0x78,
0x07, 0x00, 0xEE, 0x7F, 0xFF, 0x3F, 0xC0, 0x33, 0x1F, 0x87, 0x81, 0x80, 0x01, 0xF9, 0xFC, 0xF6, 0x70, 0x3E, 0x0F,
0xC1, 0xF0, 0x3B, 0x3D, 0xFE, 0xFE, 0xFF, 0xFF, 0xFF, 0x0E, 0x00, 0xE0, 0x0E, 0x00, 0xE0, 0x0E, 0x00, 0xE0, 0x0E,
0x00, 0xE0, 0x0E, 0x00, 0xE0, 0x0E, 0x00, 0xE0, 0x0E, 0x00, 0xE0, 0x0F, 0x01, 0xF0, 0xE0, 0xE0, 0xE0, 0xFE, 0xFE,
0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xF6, 0x7E, 0x7E, 0x1C, 0x3C, 0x7C, 0x19, 0x81, 0xF8, 0x0F, 0x00, 0x60, 0x00,
0x0F, 0xFF, 0xFF, 0xF0, 0xE0, 0x0E, 0x00, 0xE0, 0x0E, 0x00, 0xE0, 0x0E, 0x00, 0xE0, 0x0E, 0x00, 0xE0, 0x0E, 0x00,
0xE0, 0x0E, 0x00, 0xE0, 0x0C, 0x0C, 0xEC, 0xEC, 0xE0, 0xFE, 0xFE, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xF6, 0x7E,
0x7E, 0xFF, 0xFF, 0xFF, 0x0E, 0x00, 0xE0, 0x0E, 0x00, 0xE0, 0x0E, 0x03, 0xFC, 0x3F, 0xC0, 0xE0, 0x0E, 0x00, 0xE0,
0x0E, 0x00, 0xE0, 0x0E, 0x00, 0xE0, 0xE0, 0xE0, 0xFE, 0xFE, 0xE0, 0xE0, 0xE0, 0xFE, 0xFE, 0xE0, 0xF6, 0x7E, 0x7E,
0x1E, 0xC1, 0xFC, 0x3F, 0x80, 0x00, 0xE0, 0x7E, 0x07, 0xE0, 0x7E, 0x07, 0xE0, 0x7E, 0x07, 0xE0, 0x7E, 0x07, 0xE0,
0x7E, 0x07, 0xE0, 0x77, 0x07, 0x7D, 0xE3, 0xFE, 0x1F, 0xC0, 0x3F, 0x9F, 0xE7, 0xF0, 0x00, 0x00, 0x38, 0x7E, 0x1F,
0x87, 0xE1, 0xF8, 0x7E, 0x1F, 0x87, 0xE1, 0xFE, 0xF7, 0xFC, 0xFF, 0x1F, 0xC1, 0xFC, 0x00, 0x0E, 0x07, 0xE0, 0x7E,
0x07, 0xE0, 0x7E, 0x07, 0xE0, 0x7E, 0x07, 0xE0, 0x7E, 0x07, 0xE0, 0x7E, 0x07, 0x70, 0x77, 0xDE, 0x3F, 0xE1, 0xFC,
0x3F, 0x0F, 0xC0, 0x00, 0x00, 0xE1, 0xF8, 0x7E, 0x1F, 0x87, 0xE1, 0xF8, 0x7E, 0x1F, 0x87, 0xFB, 0xDF, 0xF3, 0xFC,
0x1D, 0x81, 0xF8, 0x0F, 0x80, 0x00, 0xE0, 0x7E, 0x07, 0xE0, 0x7E, 0x07, 0xE0, 0x7E, 0x07, 0xE0, 0x7E, 0x07, 0xE0,
0x7E, 0x07, 0xE0, 0x77, 0x07, 0x7D, 0xE3, 0xFE, 0x1F, 0xC0, 0x3F, 0x0F, 0xC1, 0xE0, 0x00, 0x00, 0x38, 0x7E, 0x1F,
0x87, 0xE1, 0xF8, 0x7E, 0x1F, 0x87, 0xE1, 0xFE, 0xF7, 0xFC, 0xFF, 0x0F, 0x00, 0xF8, 0x0F, 0x80, 0xF0, 0xE0, 0x7E,
0x07, 0xE0, 0x7E, 0x07, 0xE0, 0x7E, 0x07, 0xE0, 0x7E, 0x07, 0xE0, 0x7E, 0x07, 0xE0, 0x77, 0x07, 0x7D, 0xE3, 0xFE,
0x1F, 0xC0, 0x1E, 0x0F, 0x83, 0xE0, 0x78, 0x00, 0x38, 0x7E, 0x1F, 0x87, 0xE1, 0xF8, 0x7E, 0x1F, 0x87, 0xE1, 0xFE,
0xF7, 0xFC, 0xFF, 0x06, 0xE0, 0xEE, 0x1D, 0xC1, 0xD8, 0x00, 0x0E, 0x07, 0xE0, 0x7E, 0x07, 0xE0, 0x7E, 0x07, 0xE0,
0x7E, 0x07, 0xE0, 0x7E, 0x07, 0xE0, 0x7E, 0x07, 0x70, 0x77, 0xDE, 0x3F, 0xE1, 0xFC, 0x0D, 0x87, 0xF3, 0xB8, 0xCC,
0x00, 0x38, 0x7E, 0x1F, 0x87, 0xE1, 0xF8, 0x7E, 0x1F, 0x87, 0xE1, 0xFE, 0xF7, 0xFC, 0xFF, 0xE0, 0x7E, 0x07, 0xE0,
0x7E, 0x07, 0xE0, 0x7E, 0x07, 0xE0, 0x7E, 0x07, 0xE0, 0x7E, 0x07, 0xE0, 0x77, 0x07, 0x7D, 0xE3, 0xFE, 0x1F, 0x80,
0x70, 0x07, 0x00, 0x78, 0xE1, 0xF8, 0x7E, 0x1F, 0x87, 0xE1, 0xF8, 0x7E, 0x1F, 0x87, 0xFB, 0xDF, 0xF3, 0xFC, 0x0E,
0x07, 0x01, 0xF0, 0x00, 0x60, 0x00, 0x1E, 0x00, 0x07, 0xE0, 0x00, 0xCC, 0x00, 0x00, 0x01, 0xC0, 0x01, 0xF8, 0x00,
0x3B, 0x87, 0x07, 0x70, 0xF0, 0xEE, 0x1E, 0x1D, 0xC7, 0xC7, 0x38, 0xFC, 0xE3, 0x9B, 0x9C, 0x77, 0x73, 0x8E, 0xE7,
0x61, 0xD8, 0xFC, 0x1F, 0x0F, 0x83, 0xE1, 0xF0, 0x78, 0x3C, 0x07, 0x03, 0x80, 0x01, 0x80, 0x03, 0xC0, 0x07, 0xE0,
0x06, 0x60, 0x00, 0x00, 0xE1, 0xC7, 0xE1, 0xC7, 0x73, 0xC7, 0x73, 0xC6, 0x73, 0xEE, 0x37, 0xEE, 0x3F, 0x6E, 0x3E,
0x7C, 0x1E, 0x7C, 0x1E, 0x38, 0x1C, 0x38, 0x06, 0x00, 0x78, 0x07, 0xE0, 0x33, 0x00, 0x00, 0x70, 0x3F, 0xC1, 0xCE,
0x1E, 0x38, 0xE1, 0xCE, 0x07, 0x70, 0x3F, 0x00, 0xF8, 0x07, 0x80, 0x1C, 0x00, 0xE0, 0x07, 0x00, 0x38, 0x01, 0xC0,
0x0E, 0x00, 0x0E, 0x03, 0xC0, 0x7C, 0x1D, 0xC0, 0x01, 0xC3, 0xB8, 0x73, 0x8E, 0x71, 0xCE, 0x70, 0xEE, 0x1D, 0xC1,
0xF0, 0x3E, 0x07, 0xC0, 0x70, 0x0E, 0x17, 0x83, 0xF0, 0x7C, 0x00, 0x1D, 0xC0, 0xEE, 0x00, 0x01, 0xC0, 0xFF, 0x07,
0x38, 0x78, 0xE3, 0x87, 0x38, 0x1D, 0xC0, 0xFC, 0x03, 0xE0, 0x1E, 0x00, 0x70, 0x03, 0x80, 0x1C, 0x00, 0xE0, 0x07,
0x00, 0x38, 0x00, 0x03, 0x80, 0x78, 0x07, 0x00, 0x60, 0x00, 0x07, 0xFE, 0x7F, 0xE0, 0x0E, 0x01, 0xC0, 0x3C, 0x07,
0x80, 0x70, 0x0E, 0x01, 0xE0, 0x1C, 0x03, 0x80, 0x70, 0x07, 0x00, 0xFF, 0xFF, 0xFF, 0x06, 0x03, 0xC1, 0xE0, 0x70,
0x00, 0x1F, 0xE7, 0xF8, 0x1E, 0x0F, 0x03, 0x81, 0xC0, 0xF0, 0x78, 0x1C, 0x0F, 0xFF, 0xFF, 0x07, 0x00, 0xF0, 0x07,
0x00, 0x00, 0x7F, 0xE7, 0xFE, 0x00, 0xE0, 0x1C, 0x03, 0xC0, 0x78, 0x07, 0x00, 0xE0, 0x1E, 0x01, 0xC0, 0x38, 0x07,
0x00, 0x70, 0x0F, 0xFF, 0xFF, 0xF0, 0x1C, 0x07, 0x81, 0xC0, 0x00, 0x00, 0x1F, 0xE7, 0xF8, 0x1E, 0x0F, 0x03, 0x81,
0xC0, 0xF0, 0x78, 0x1C, 0x0F, 0xFF, 0xFF, 0x19, 0x81, 0xF8, 0x0F, 0x00, 0x60, 0x00, 0x07, 0xFE, 0x7F, 0xE0, 0x0E,
0x01, 0xC0, 0x3C, 0x07, 0x80, 0x70, 0x0E, 0x01, 0xE0, 0x1C, 0x03, 0x80, 0x70, 0x07, 0x00, 0xFF, 0xFF, 0xFF, 0x33,
0x0F, 0xC1, 0xE0, 0x30, 0x00, 0x1F, 0xE7, 0xF8, 0x1E, 0x0F, 0x03, 0x81, 0xC0, 0xF0, 0x78, 0x1C, 0x0F, 0xFF, 0xFF,
0x3F, 0x7E, 0xF8, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0x7B, 0xFF, 0xC0,
0x7F, 0xF7, 0xFF, 0x7F, 0xFF, 0xFD, 0xFF, 0xFF, 0xF0, 0x7F, 0xFF, 0xFD, 0xFF, 0xFF, 0xF0, 0x76, 0xEE, 0xE0, 0x77,
0x76, 0xE0, 0x77, 0x76, 0xE0, 0x76, 0x6E, 0xEE, 0xEE, 0xEE, 0x77, 0x77, 0x77, 0x6E, 0xEE, 0x77, 0x77, 0x77, 0x6E,
0xEE, 0x0C, 0x03, 0x00, 0xC0, 0x30, 0x0C, 0x3F, 0xFF, 0xFC, 0x30, 0x0C, 0x03, 0x00, 0xC0, 0x30, 0x0C, 0x03, 0x00,
0xC0, 0x30, 0x0C, 0x03, 0x00, 0x0C, 0x03, 0x00, 0xC0, 0x30, 0x0C, 0x3F, 0xFF, 0xFC, 0x30, 0x0C, 0x03, 0x0F, 0xFF,
0xFF, 0x0C, 0x03, 0x00, 0xC0, 0x30, 0x0C, 0x03, 0x00, 0xFB, 0xFF, 0xFF, 0xF8, 0xE1, 0xC3, 0xF0, 0xE1, 0xF8, 0x70,
0xE0, 0x7C, 0x38, 0x00, 0xEE, 0x38, 0x00, 0xC6, 0x70, 0x00, 0xC6, 0x70, 0x00, 0xC6, 0xE0, 0x00, 0xEE, 0xE0, 0x00,
0x7D, 0xC0, 0x00, 0x01, 0x80, 0x00, 0x03, 0xBE, 0x3E, 0x07, 0x7F, 0x7F, 0x07, 0x63, 0x67, 0x0E, 0x63, 0x63, 0x0E,
0x63, 0x67, 0x1C, 0x77, 0x77, 0x1C, 0x3E, 0x3E, 0x1C, 0xE3, 0x9C, 0xF1, 0xC3, 0x8E, 0x1C, 0x61, 0xC7, 0x8E, 0x1C,
0xE7, 0x9C, 0x60, 0x00, 0xE0, 0x38, 0x07, 0x01, 0xC0, 0x38, 0x0E, 0x01, 0x80, 0x70, 0x0C, 0x03, 0x80, 0xE0, 0x1C,
0x07, 0x00, 0xE0, 0x38, 0x00, 0x07, 0xF0, 0xFE, 0x1F, 0x61, 0xC0, 0xFF, 0xCF, 0xFC, 0x38, 0x0F, 0xF8, 0xFF, 0x83,
0x80, 0x1C, 0x01, 0xF7, 0x0F, 0xF0, 0x7F, 0xFF, 0xEF, 0xFE, 0x06, 0x00, 0x60, 0x07, 0xC0, 0x7C, 0x3F, 0x83, 0xE0,
0x3F, 0xC0, 0x7C, 0x3F, 0x83, 0xE0, 0x3E, 0x00, 0x60, 0x06, 0x00, 0x3F, 0x83, 0xFC, 0x3B, 0xC0, 0x0E, 0x00, 0xCF,
0xFE, 0x0F, 0x81, 0xF0, 0xFF, 0xE7, 0x00, 0x70, 0x07, 0x00, 0x3D, 0xC3, 0xFE, 0x1F, 0xE0, 0xFF, 0xFF, 0xFC, 0x3C,
0x01, 0xCF, 0xFD, 0xFF, 0x80, 0xE0, 0xF8, 0xFF, 0x1F, 0x83, 0xC0, 0x3C, 0x01, 0xC0, 0x1C, 0x01, 0xC0, 0x3F, 0x83,
0xFC, 0x33, 0xE0, 0x0E, 0x00, 0xE1, 0xFE, 0x3F, 0xF7, 0xDF, 0x70, 0x77, 0x07, 0xE0, 0xEE, 0x0E, 0x70, 0xE7, 0xBC,
0x7F, 0xC3, 0xF8, 0x07, 0x00, 0x1E, 0x00, 0xF8, 0x03, 0xF0, 0x1D, 0xC0, 0x73, 0x81, 0x8E, 0x0E, 0x38, 0x38, 0x70,
0xC1, 0xC7, 0x03, 0x1C, 0x0E, 0xE0, 0x3B, 0xFF, 0xEF, 0xFF, 0xC0, 0xFF, 0xFF, 0xFF, 0xE0, 0x7E, 0x07, 0xE0, 0x7E,
0x07, 0xE0, 0x7E, 0x07, 0xE0, 0x7E, 0x07, 0xE0, 0x7E, 0x07, 0xE0, 0x7E, 0x07, 0xE0, 0x7E, 0x07, 0xE0, 0x70, 0xFF,
0xFF, 0xFD, 0xC0, 0x3C, 0x03, 0x80, 0x38, 0x07, 0x80, 0x70, 0x07, 0x01, 0xC0, 0x70, 0x1E, 0x03, 0x80, 0xE0, 0x3C,
0x07, 0xFF, 0xFF, 0xE0, 0xFF, 0xFF, 0xF0, 0x00, 0xE0, 0x38, 0x07, 0x01, 0xC0, 0x38, 0x0E, 0x01, 0x80, 0x70, 0x0C,
0x03, 0x80, 0xE0, 0x1C, 0x07, 0x00, 0xE0, 0x38, 0x00, 0xFF, 0x80, 0x00, 0x30, 0x03, 0x80, 0x1C, 0x00, 0xE0, 0x06,
0x00, 0x70, 0x03, 0x80, 0x18, 0xF1, 0xC7, 0xCE, 0x3E, 0x60, 0x3B, 0x01, 0xF8, 0x07, 0xC0, 0x3C, 0x00, 0xE0, 0x07,
0x00, 0x7D, 0xEF, 0xFF, 0xE7, 0x3E, 0x73, 0xFF, 0xF7, 0xDE, 0x0F, 0x1F, 0x1E, 0x1C, 0x38, 0x38, 0x38, 0x38, 0x38,
0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x78, 0xF8, 0xF0, 0x3C, 0x6F, 0xFD, 0xFF, 0xB1, 0xE3, 0xC6, 0xFF,
0xDF, 0xFB, 0x1E, 0x03, 0x80, 0xE0, 0x73, 0xFF, 0xFF, 0xC3, 0x80, 0xC0, 0x70, 0xFF, 0xFF, 0xF3, 0x81, 0xC0, 0x70,
0x00, 0x01, 0xC3, 0xF7, 0xFB, 0xF0, 0xF0, 0x3F, 0x07, 0xF8, 0x3F, 0x03, 0xC0, 0x0F, 0xFF, 0xFF, 0xE0, 0x3F, 0x07,
0xF8, 0x3F, 0x03, 0xC7, 0xF7, 0xFB, 0xF0, 0xE0, 0x00, 0x0F, 0xFF, 0xFF,
};
static const EpdGlyph ubuntu_10Glyphs[] = {
{0, 0, 0, 0, 0, 0, 0}, //
{0, 0, 0, 0, 0, 0, 0}, // 
{0, 0, 5, 0, 0, 0, 0}, //
{0, 0, 5, 0, 0, 0, 0}, //
{0, 0, 0, 0, 0, 0, 0}, // 
{0, 0, 5, 0, 0, 0, 0}, //
{4, 15, 6, 1, 15, 8, 0}, // !
{7, 5, 9, 1, 16, 5, 8}, // "
{12, 15, 14, 1, 15, 23, 13}, // #
{10, 19, 12, 1, 16, 24, 36}, // $
{16, 15, 18, 1, 15, 30, 60}, // %
{14, 15, 14, 0, 15, 27, 90}, // &
{3, 5, 5, 1, 16, 2, 117}, // '
{6, 20, 7, 1, 16, 15, 119}, // (
{6, 20, 7, 0, 16, 15, 134}, // )
{10, 8, 10, 0, 15, 10, 149}, // *
{10, 10, 12, 1, 11, 13, 159}, // +
{4, 5, 5, 0, 2, 3, 172}, // ,
{6, 2, 6, 0, 7, 2, 175}, // -
{3, 3, 5, 1, 3, 2, 177}, // .
{10, 20, 8, -1, 16, 25, 179}, // /
{10, 15, 12, 1, 15, 19, 204}, // 0
{7, 15, 12, 1, 15, 14, 223}, // 1
{10, 15, 12, 1, 15, 19, 237}, // 2
{10, 15, 12, 1, 15, 19, 256}, // 3
{11, 15, 12, 0, 15, 21, 275}, // 4
{10, 15, 12, 1, 15, 19, 296}, // 5
{10, 15, 12, 1, 15, 19, 315}, // 6
{10, 15, 12, 1, 15, 19, 334}, // 7
{10, 15, 12, 1, 15, 19, 353}, // 8
{10, 15, 12, 1, 15, 19, 372}, // 9
{3, 11, 5, 1, 11, 5, 391}, // :
{4, 14, 5, 0, 11, 7, 396}, // ;
{10, 9, 12, 1, 11, 12, 403}, // <
{10, 7, 12, 1, 10, 9, 415}, // =
{10, 9, 12, 1, 11, 12, 424}, // >
{8, 15, 8, 0, 15, 15, 436}, // ?
{18, 20, 20, 1, 16, 45, 451}, // @
{14, 15, 14, 0, 15, 27, 496}, // A
{12, 15, 14, 1, 15, 23, 523}, // B
{12, 15, 13, 1, 15, 23, 546}, // C
{13, 15, 15, 1, 15, 25, 569}, // D
{11, 15, 12, 1, 15, 21, 594}, // E
{10, 15, 11, 1, 15, 19, 615}, // F
{12, 15, 14, 1, 15, 23, 634}, // G
{12, 15, 15, 1, 15, 23, 657}, // H
{3, 15, 6, 1, 15, 6, 680}, // I
{9, 15, 11, 0, 15, 17, 686}, // J
{13, 15, 13, 1, 15, 25, 703}, // K
{10, 15, 11, 1, 15, 19, 728}, // L
{16, 15, 18, 1, 15, 30, 747}, // M
{13, 15, 15, 1, 15, 25, 777}, // N
{15, 15, 16, 1, 15, 29, 802}, // O
{12, 15, 13, 1, 15, 23, 831}, // P
{15, 19, 16, 1, 15, 36, 854}, // Q
{12, 15, 13, 1, 15, 23, 890}, // R
{11, 15, 11, 0, 15, 21, 913}, // S
{12, 15, 12, 0, 15, 23, 934}, // T
{12, 15, 14, 1, 15, 23, 957}, // U
{14, 15, 14, 0, 15, 27, 980}, // V
{19, 15, 20, 0, 15, 36, 1007}, // W
{13, 15, 13, 0, 15, 25, 1043}, // X
{13, 15, 13, 0, 15, 25, 1068}, // Y
{12, 15, 12, 0, 15, 23, 1093}, // Z
{5, 20, 7, 2, 16, 13, 1116}, // [
{10, 20, 8, -1, 16, 25, 1129}, // <backslash>
{5, 20, 7, 0, 16, 13, 1154}, // ]
{11, 8, 12, 0, 15, 11, 1167}, // ^
{12, 2, 10, -1, -2, 3, 1178}, // _
{5, 4, 8, 1, 16, 3, 1181}, // `
{10, 11, 11, 0, 11, 14, 1184}, // a
{11, 16, 12, 1, 16, 22, 1198}, // b
{9, 11, 10, 1, 11, 13, 1220}, // c
{10, 16, 12, 1, 16, 20, 1233}, // d
{10, 11, 12, 1, 11, 14, 1253}, // e
{8, 16, 8, 1, 16, 16, 1267}, // f
{10, 15, 12, 1, 11, 19, 1283}, // g
{10, 16, 12, 1, 16, 20, 1302}, // h
{3, 16, 5, 1, 16, 6, 1322}, // i
{6, 20, 5, -2, 16, 15, 1328}, // j
{10, 16, 11, 1, 16, 20, 1343}, // k
{5, 16, 6, 1, 16, 10, 1363}, // l
{16, 11, 18, 1, 11, 22, 1373}, // m
{10, 11, 12, 1, 11, 14, 1395}, // n
{11, 11, 12, 1, 11, 16, 1409}, // o
{11, 15, 12, 1, 11, 21, 1425}, // p
{10, 15, 12, 1, 11, 19, 1446}, // q
{7, 11, 8, 1, 11, 10, 1465}, // r
{9, 11, 9, 0, 11, 13, 1475}, // s
{8, 14, 8, 1, 14, 14, 1488}, // t
{10, 11, 12, 1, 11, 14, 1502}, // u
{11, 11, 11, 0, 11, 16, 1516}, // v
{16, 11, 16, 0, 11, 22, 1532}, // w
{11, 11, 11, 0, 11, 16, 1554}, // x
{11, 15, 10, 0, 11, 21, 1570}, // y
{10, 11, 10, 0, 11, 14, 1591}, // z
{7, 20, 7, 0, 16, 18, 1605}, // {
{2, 20, 6, 2, 16, 5, 1623}, // |
{7, 20, 7, 0, 16, 18, 1628}, // }
{11, 4, 12, 0, 8, 6, 1646}, // ~
{0, 0, 5, 0, 0, 0, 1652}, //  
{4, 15, 6, 1, 11, 8, 1652}, // ¡
{10, 17, 12, 1, 14, 22, 1660}, // ¢
{10, 15, 12, 1, 15, 19, 1682}, // £
{11, 10, 12, 0, 12, 14, 1701}, // ¤
{12, 15, 12, 0, 15, 23, 1715}, // ¥
{2, 20, 6, 2, 16, 5, 1738}, // ¦
{10, 18, 10, 0, 16, 23, 1743}, // §
{8, 2, 8, 0, 15, 2, 1766}, // ¨
{15, 15, 17, 1, 15, 29, 1768}, // ©
{8, 8, 8, 0, 15, 8, 1797}, // ª
{10, 9, 10, 0, 10, 12, 1805}, // «
{10, 8, 12, 1, 9, 10, 1817}, // ¬
{6, 2, 6, 0, 7, 2, 1827}, // ­
{15, 15, 17, 1, 15, 29, 1829}, // ®
{7, 2, 8, 0, 15, 2, 1858}, // ¯
{7, 6, 7, 0, 16, 6, 1860}, // °
{10, 13, 12, 1, 13, 17, 1866}, // ±
{7, 8, 8, 0, 15, 7, 1883}, // ²
{7, 8, 8, 0, 15, 7, 1890}, // ³
{5, 4, 8, 2, 16, 3, 1897}, // ´
{10, 15, 12, 1, 11, 19, 1900}, // µ
{12, 19, 14, 0, 15, 29, 1919}, // ¶
{3, 3, 5, 1, 8, 2, 1948}, // ·
{5, 4, 8, 1, 0, 3, 1950}, // ¸
{4, 8, 8, 1, 15, 4, 1953}, // ¹
{9, 8, 9, 0, 15, 9, 1957}, // º
{10, 9, 10, 0, 10, 12, 1966}, // »
{17, 15, 18, 1, 15, 32, 1978}, // ¼
{17, 15, 18, 1, 15, 32, 2010}, // ½
{18, 15, 18, 0, 15, 34, 2042}, // ¾
{9, 15, 8, 0, 11, 17, 2076}, // ¿
{14, 20, 14, 0, 20, 35, 2093}, // À
{14, 20, 14, 0, 20, 35, 2128}, // Á
{14, 20, 14, 0, 20, 35, 2163}, // Â
{14, 19, 14, 0, 19, 34, 2198}, // Ã
{14, 18, 14, 0, 18, 32, 2232}, // Ä
{14, 18, 14, 0, 18, 32, 2264}, // Å
{19, 15, 20, 0, 15, 36, 2296}, // Æ
{12, 18, 13, 1, 15, 27, 2332}, // Ç
{11, 20, 12, 1, 20, 28, 2359}, // È
{11, 20, 12, 1, 20, 28, 2387}, // É
{11, 20, 12, 1, 20, 28, 2415}, // Ê
{11, 18, 12, 1, 18, 25, 2443}, // Ë
{5, 20, 6, 0, 20, 13, 2468}, // Ì
{5, 20, 6, 1, 20, 13, 2481}, // Í
{7, 20, 6, -1, 20, 18, 2494}, // Î
{8, 18, 6, -1, 18, 18, 2512}, // Ï
{15, 15, 15, 0, 15, 29, 2530}, // Ð
{13, 19, 15, 1, 19, 31, 2559}, // Ñ
{15, 20, 16, 1, 20, 38, 2590}, // Ò
{15, 20, 16, 1, 20, 38, 2628}, // Ó
{15, 20, 16, 1, 20, 38, 2666}, // Ô
{15, 19, 16, 1, 19, 36, 2704}, // Õ
{15, 18, 16, 1, 18, 34, 2740}, // Ö
{10, 9, 12, 1, 11, 12, 2774}, // ×
{15, 15, 16, 1, 15, 29, 2786}, // Ø
{12, 20, 14, 1, 20, 30, 2815}, // Ù
{12, 20, 14, 1, 20, 30, 2845}, // Ú
{12, 20, 14, 1, 20, 30, 2875}, // Û
{12, 18, 14, 1, 18, 27, 2905}, // Ü
{13, 20, 13, 0, 20, 33, 2932}, // Ý
{12, 15, 13, 1, 15, 23, 2965}, // Þ
{12, 16, 13, 1, 16, 24, 2988}, // ß
{10, 16, 11, 0, 16, 20, 3012}, // à
{10, 16, 11, 0, 16, 20, 3032}, // á
{10, 16, 11, 0, 16, 20, 3052}, // â
{10, 16, 11, 0, 16, 20, 3072}, // ã
{10, 15, 11, 0, 15, 19, 3092}, // ä
{10, 16, 11, 0, 16, 20, 3111}, // å
{17, 11, 18, 0, 11, 24, 3131}, // æ
{9, 15, 10, 1, 11, 17, 3155}, // ç
{10, 16, 12, 1, 16, 20, 3172}, // è
{10, 16, 12, 1, 16, 20, 3192}, // é
{10, 16, 12, 1, 16, 20, 3212}, // ê
{10, 15, 12, 1, 15, 19, 3232}, // ë
{5, 16, 5, 0, 16, 10, 3251}, // ì
{5, 16, 5, 1, 16, 10, 3261}, // í
{7, 16, 5, -1, 16, 14, 3271}, // î
{7, 15, 5, -1, 15, 14, 3285}, // ï
{11, 16, 12, 1, 16, 22, 3299}, // ð
{10, 16, 12, 1, 16, 20, 3321}, // ñ
{11, 16, 12, 1, 16, 22, 3341}, // ò
{11, 16, 12, 1, 16, 22, 3363}, // ó
{11, 16, 12, 1, 16, 22, 3385}, // ô
{11, 16, 12, 1, 16, 22, 3407}, // õ
{11, 15, 12, 1, 15, 21, 3429}, // ö
{10, 12, 12, 1, 12, 15, 3450}, // ÷
{11, 11, 12, 1, 11, 16, 3465}, // ø
{10, 16, 12, 1, 16, 20, 3481}, // ù
{10, 16, 12, 1, 16, 20, 3501}, // ú
{10, 16, 12, 1, 16, 20, 3521}, // û
{10, 15, 12, 1, 15, 19, 3541}, // ü
{11, 20, 10, 0, 16, 28, 3560}, // ý
{11, 20, 12, 1, 16, 28, 3588}, // þ
{11, 19, 10, 0, 15, 27, 3616}, // ÿ
{14, 18, 14, 0, 18, 32, 3643}, // Ā
{10, 15, 11, 0, 15, 19, 3675}, // ā
{14, 19, 14, 0, 19, 34, 3694}, // Ă
{10, 16, 11, 0, 16, 20, 3728}, // ă
{14, 19, 14, 0, 15, 34, 3748}, // Ą
{10, 14, 11, 0, 11, 18, 3782}, // ą
{12, 20, 13, 1, 20, 30, 3800}, // Ć
{9, 16, 10, 1, 16, 18, 3830}, // ć
{12, 20, 13, 1, 20, 30, 3848}, // Ĉ
{9, 16, 10, 1, 16, 18, 3878}, // ĉ
{12, 19, 13, 1, 19, 29, 3896}, // Ċ
{9, 16, 10, 1, 16, 18, 3925}, // ċ
{12, 20, 13, 1, 20, 30, 3943}, // Č
{9, 16, 10, 1, 16, 18, 3973}, // č
{13, 20, 15, 1, 20, 33, 3991}, // Ď
{14, 16, 13, 1, 16, 28, 4024}, // ď
{15, 15, 15, 0, 15, 29, 4052}, // Đ
{12, 16, 12, 1, 16, 24, 4081}, // đ
{11, 18, 12, 1, 18, 25, 4105}, // Ē
{10, 15, 12, 1, 15, 19, 4130}, // ē
{11, 19, 12, 1, 19, 27, 4149}, // Ĕ
{10, 16, 12, 1, 16, 20, 4176}, // ĕ
{11, 19, 12, 1, 19, 27, 4196}, // Ė
{10, 16, 12, 1, 16, 20, 4223}, // ė
{11, 19, 12, 1, 15, 27, 4243}, // Ę
{10, 14, 12, 1, 11, 18, 4270}, // ę
{11, 20, 12, 1, 20, 28, 4288}, // Ě
{10, 17, 12, 1, 17, 22, 4316}, // ě
{12, 20, 14, 1, 20, 30, 4338}, // Ĝ
{10, 20, 12, 1, 16, 25, 4368}, // ĝ
{12, 19, 14, 1, 19, 29, 4393}, // Ğ
{10, 20, 12, 1, 16, 25, 4422}, // ğ
{12, 19, 14, 1, 19, 29, 4447}, // Ġ
{10, 20, 12, 1, 16, 25, 4476}, // ġ
{12, 19, 14, 1, 15, 29, 4501}, // Ģ
{10, 21, 12, 1, 17, 27, 4530}, // ģ
{12, 20, 15, 1, 20, 30, 4557}, // Ĥ
{10, 20, 12, 1, 20, 25, 4587}, // ĥ
{15, 15, 15, 0, 15, 29, 4612}, // Ħ
{11, 16, 12, 0, 16, 22, 4641}, // ħ
{8, 19, 6, -1, 19, 19, 4663}, // Ĩ
{8, 16, 5, -1, 16, 16, 4682}, // ĩ
{7, 18, 6, -1, 18, 16, 4698}, // Ī
{7, 15, 5, -1, 15, 14, 4714}, // ī
{6, 19, 6, 0, 19, 15, 4728}, // Ĭ
{7, 16, 5, -1, 16, 14, 4743}, // ĭ
{5, 19, 6, 0, 15, 12, 4757}, // Į
{4, 20, 5, 0, 16, 10, 4769}, // į
{4, 19, 6, 1, 19, 10, 4779}, // İ
{3, 11, 5, 1, 11, 5, 4789}, // ı
{14, 15, 16, 1, 15, 27, 4794}, // IJ
{9, 20, 11, 1, 16, 23, 4821}, // ij
{11, 20, 11, 0, 20, 28, 4844}, // Ĵ
{8, 20, 5, -2, 16, 20, 4872}, // ĵ
{13, 19, 13, 1, 15, 31, 4892}, // Ķ
{10, 20, 11, 1, 16, 25, 4923}, // ķ
{10, 11, 11, 1, 11, 14, 4948}, // ĸ
{10, 20, 11, 1, 20, 25, 4962}, // Ĺ
{5, 20, 6, 1, 20, 13, 4987}, // ĺ
{10, 19, 11, 1, 15, 24, 5000}, // Ļ
{6, 20, 6, 0, 16, 15, 5024}, // ļ
{10, 15, 11, 1, 15, 19, 5039}, // Ľ
{6, 16, 6, 1, 16, 12, 5058}, // ľ
{10, 15, 11, 1, 15, 19, 5070}, // Ŀ
{7, 16, 8, 1, 16, 14, 5089}, // ŀ
{12, 15, 11, -1, 15, 23, 5103}, // Ł
{7, 16, 6, 0, 16, 14, 5126}, // ł
{13, 20, 15, 1, 20, 33, 5140}, // Ń
{10, 16, 12, 1, 16, 20, 5173}, // ń
{13, 19, 15, 1, 15, 31, 5193}, // Ņ
{10, 15, 12, 1, 11, 19, 5224}, // ņ
{13, 20, 15, 1, 20, 33, 5243}, // Ň
{10, 16, 12, 1, 16, 20, 5276}, // ň
{11, 16, 12, 0, 16, 22, 5296}, // ʼn
{13, 19, 15, 1, 15, 31, 5318}, // Ŋ
{10, 15, 12, 1, 11, 19, 5349}, // ŋ
{15, 18, 16, 1, 18, 34, 5368}, // Ō
{11, 15, 12, 1, 15, 21, 5402}, // ō
{15, 19, 16, 1, 19, 36, 5423}, // Ŏ
{11, 16, 12, 1, 16, 22, 5459}, // ŏ
{15, 20, 16, 1, 20, 38, 5481}, // Ő
{11, 16, 12, 1, 16, 22, 5519}, // ő
{20, 15, 21, 1, 15, 38, 5541}, // Œ
{18, 11, 20, 1, 11, 25, 5579}, // œ
{12, 20, 13, 1, 20, 30, 5604}, // Ŕ
{7, 16, 8, 1, 16, 14, 5634}, // ŕ
{12, 19, 13, 1, 15, 29, 5648}, // Ŗ
{8, 15, 8, 0, 11, 15, 5677}, // ŗ
{12, 20, 13, 1, 20, 30, 5692}, // Ř
{7, 16, 8, 1, 16, 14, 5722}, // ř
{11, 20, 11, 0, 20, 28, 5736}, // Ś
{9, 16, 9, 0, 16, 18, 5764}, // ś
{11, 20, 11, 0, 20, 28, 5782}, // Ŝ
{9, 16, 9, 0, 16, 18, 5810}, // ŝ
{11, 18, 11, 0, 15, 25, 5828}, // Ş
{9, 14, 9, 0, 11, 16, 5853}, // ş
{11, 20, 11, 0, 20, 28, 5869}, // Š
{9, 16, 9, 0, 16, 18, 5897}, // š
{12, 18, 12, 0, 15, 27, 5915}, // Ţ
{8, 17, 8, 1, 14, 17, 5942}, // ţ
{12, 20, 12, 0, 20, 30, 5959}, // Ť
{8, 16, 8, 1, 16, 16, 5989}, // ť
{12, 15, 12, 0, 15, 23, 6005}, // Ŧ
{8, 14, 8, 1, 14, 14, 6028}, // ŧ
{12, 19, 14, 1, 19, 29, 6042}, // Ũ
{10, 16, 12, 1, 16, 20, 6071}, // ũ
{12, 18, 14, 1, 18, 27, 6091}, // Ū
{10, 15, 12, 1, 15, 19, 6118}, // ū
{12, 19, 14, 1, 19, 29, 6137}, // Ŭ
{10, 16, 12, 1, 16, 20, 6166}, // ŭ
{12, 19, 14, 1, 19, 29, 6186}, // Ů
{10, 16, 12, 1, 16, 20, 6215}, // ů
{12, 20, 14, 1, 20, 30, 6235}, // Ű
{10, 16, 12, 1, 16, 20, 6265}, // ű
{12, 18, 14, 1, 15, 27, 6285}, // Ų
{10, 14, 12, 1, 11, 18, 6312}, // ų
{19, 20, 20, 0, 20, 48, 6330}, // Ŵ
{16, 16, 16, 0, 16, 32, 6378}, // ŵ
{13, 20, 13, 0, 20, 33, 6410}, // Ŷ
{11, 20, 10, 0, 16, 28, 6443}, // ŷ
{13, 18, 13, 0, 18, 30, 6471}, // Ÿ
{12, 20, 12, 0, 20, 30, 6501}, // Ź
{10, 16, 10, 0, 16, 20, 6531}, // ź
{12, 19, 12, 0, 19, 29, 6551}, // Ż
{10, 16, 10, 0, 16, 20, 6580}, // ż
{12, 20, 12, 0, 20, 30, 6600}, // Ž
{10, 16, 10, 0, 16, 20, 6630}, // ž
{8, 16, 6, 1, 16, 16, 6650}, // ſ
{6, 3, 8, 1, 16, 3, 6666}, // ̑
{12, 2, 10, -1, 7, 3, 6669}, //
{22, 2, 21, -1, 7, 6, 6672}, // —
{22, 2, 21, -1, 7, 6, 6678}, // ―
{4, 5, 5, 1, 16, 3, 6684}, //
{4, 5, 5, 0, 16, 3, 6687}, //
{4, 5, 5, 0, 2, 3, 6690}, //
{8, 5, 9, 1, 16, 5, 6693}, // “
{8, 5, 9, 0, 16, 5, 6698}, // ”
{8, 5, 9, 0, 2, 5, 6703}, // „
{10, 18, 10, 0, 15, 23, 6708}, // †
{10, 18, 10, 0, 15, 23, 6731}, // ‡
{6, 5, 8, 1, 10, 4, 6754}, // •
{17, 3, 21, 2, 3, 7, 6758}, // …
{24, 15, 26, 1, 15, 45, 6765}, // ‰
{6, 9, 6, 0, 10, 7, 6810}, //
{6, 9, 6, 0, 10, 7, 6817}, //
{11, 15, 3, -4, 15, 21, 6824}, //
{12, 14, 12, 0, 14, 21, 6845}, // €
{12, 15, 12, 0, 15, 23, 6866}, // ₮
{12, 15, 12, 0, 15, 23, 6889}, // ₴
{11, 15, 12, 1, 15, 21, 6912}, // ₹
{12, 16, 12, 0, 16, 24, 6933}, // ∂
{14, 15, 14, 0, 15, 27, 6957}, // ∆
{12, 17, 15, 1, 15, 26, 6984}, // ∏
{11, 17, 11, 0, 15, 24, 7010}, // ∑
{10, 2, 12, 1, 7, 3, 7034}, //
{11, 15, 3, -4, 15, 21, 7037}, //
{3, 3, 5, 1, 8, 2, 7058}, // ∙
{13, 17, 12, 0, 16, 28, 7060}, // √
{12, 6, 12, 0, 9, 9, 7088}, // ∞
{8, 20, 8, 0, 16, 20, 7097}, // ∫
{11, 8, 12, 0, 10, 11, 7117}, // ≈
{10, 13, 12, 1, 13, 17, 7128}, // ≠
{10, 12, 12, 1, 12, 15, 7145}, // ≤
{10, 12, 12, 1, 12, 15, 7160}, // ≥
};
static const EpdUnicodeInterval ubuntu_10Intervals[] = {
{0x0, 0x0, 0x0}, {0x8, 0x9, 0x1}, {0xD, 0xD, 0x3}, {0x1D, 0x1D, 0x4},
{0x20, 0x7E, 0x5}, {0xA0, 0xFF, 0x64}, {0x100, 0x17F, 0xC4}, {0x311, 0x311, 0x144},
{0x2013, 0x2015, 0x145}, {0x2018, 0x201A, 0x148}, {0x201C, 0x201E, 0x14B}, {0x2020, 0x2022, 0x14E},
{0x2026, 0x2026, 0x151}, {0x2030, 0x2030, 0x152}, {0x2039, 0x203A, 0x153}, {0x2044, 0x2044, 0x155},
{0x20AC, 0x20AC, 0x156}, {0x20AE, 0x20AE, 0x157}, {0x20B4, 0x20B4, 0x158}, {0x20B9, 0x20B9, 0x159},
{0x2202, 0x2202, 0x15A}, {0x2206, 0x2206, 0x15B}, {0x220F, 0x220F, 0x15C}, {0x2211, 0x2212, 0x15D},
{0x2215, 0x2215, 0x15F}, {0x2219, 0x221A, 0x160}, {0x221E, 0x221E, 0x162}, {0x222B, 0x222B, 0x163},
{0x2248, 0x2248, 0x164}, {0x2260, 0x2260, 0x165}, {0x2264, 0x2265, 0x166},
};
static const EpdFontData ubuntu_10 = {
ubuntu_10Bitmaps, ubuntu_10Glyphs, ubuntu_10Intervals, 31, 24, 20, -4,
};
+810
View File
@@ -0,0 +1,810 @@
/**
* generated by fontconvert.py
* name: ubuntu_bold_10
* size: 10
*/
#pragma once
#include "EpdFontData.h"
static const uint8_t ubuntu_bold_10Bitmaps[8017] = {
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF, 0xEF, 0xEF, 0xE7, 0x1E, 0xF0, 0xF7, 0x87, 0x3C,
0x39, 0xCF, 0xFF, 0xFF, 0xFC, 0xF7, 0x87, 0xBC, 0x39, 0xE7, 0xFF, 0xFF, 0xFE, 0xF7, 0x87, 0xBC, 0x39, 0xE1, 0xCE,
0x00, 0x0F, 0x01, 0xE0, 0xFF, 0xBF, 0xF7, 0xFC, 0xFB, 0x9E, 0x03, 0xF8, 0x3F, 0xC3, 0xFC, 0x1F, 0x80, 0xF7, 0xBE,
0xFF, 0xFF, 0xFB, 0xFE, 0x0F, 0x01, 0xE0, 0x3C, 0x00, 0x3E, 0x1E, 0x0F, 0xE3, 0xC1, 0xDC, 0xF0, 0x7B, 0xDE, 0x0F,
0x7F, 0x80, 0xEE, 0xF0, 0x1F, 0xFC, 0x01, 0xF7, 0xFE, 0x01, 0xFF, 0xC0, 0x3B, 0xFC, 0x0F, 0x73, 0x83, 0xCE, 0x70,
0x79, 0xFE, 0x1E, 0x3F, 0x83, 0xC3, 0xF0, 0x1F, 0x80, 0x7F, 0x80, 0xFF, 0x81, 0xEF, 0x03, 0xDE, 0x07, 0xF8, 0x07,
0xF0, 0x1F, 0xDE, 0x7F, 0xFC, 0xE7, 0xF3, 0xC7, 0xE7, 0xE7, 0xC7, 0xFF, 0x8F, 0xFF, 0x8F, 0xFF, 0x80, 0xFF, 0xFE,
0xEE, 0x1C, 0x3C, 0xF3, 0xE7, 0x8F, 0x3C, 0x78, 0xF1, 0xE3, 0xC7, 0x8F, 0x1E, 0x1E, 0x3C, 0x7C, 0x78, 0x78, 0xE0,
0x63, 0xCF, 0x9E, 0x78, 0xF3, 0xCF, 0x3C, 0xF3, 0xCF, 0x3C, 0xF3, 0xDE, 0x7B, 0xEF, 0x18, 0x1E, 0x03, 0x87, 0xF9,
0xFF, 0x7F, 0xCF, 0xE7, 0xF8, 0xEC, 0x1E, 0x07, 0x81, 0xE0, 0x78, 0xFF, 0xFF, 0xFF, 0xFC, 0x78, 0x1E, 0x07, 0x81,
0xE0, 0x7B, 0xDF, 0xEF, 0x78, 0xFF, 0xFF, 0xF8, 0x77, 0xFE, 0xE0, 0x01, 0xE0, 0x3C, 0x0F, 0x01, 0xE0, 0x7C, 0x0F,
0x01, 0xE0, 0x7C, 0x0F, 0x01, 0xE0, 0x78, 0x0F, 0x01, 0xE0, 0x78, 0x0F, 0x01, 0xE0, 0x78, 0x0F, 0x01, 0xE0, 0x78,
0x00, 0x1F, 0x83, 0xFC, 0x7F, 0xE7, 0xFE, 0x79, 0xEF, 0x1F, 0xF0, 0xFF, 0x0F, 0xF0, 0xFF, 0x1F, 0x79, 0xE7, 0xFE,
0x7F, 0xE3, 0xFC, 0x1F, 0x80, 0x0F, 0x1F, 0x3F, 0xFF, 0xFF, 0x7F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F,
0x0F, 0x3F, 0x8F, 0xF9, 0xFF, 0xBD, 0xF2, 0x1E, 0x07, 0xC0, 0xF0, 0x3C, 0x0F, 0x83, 0xC0, 0xF0, 0x3E, 0x07, 0xFE,
0xFF, 0xDF, 0xF8, 0x3F, 0x8F, 0xF9, 0xFF, 0x3B, 0xE0, 0x3C, 0x0F, 0x87, 0xE0, 0xFE, 0x07, 0xE0, 0x3C, 0x07, 0xBB,
0xFF, 0xFF, 0xFF, 0xBF, 0xE0, 0x03, 0xC0, 0x7C, 0x0F, 0xC1, 0xFC, 0x1F, 0xC3, 0xFC, 0x7B, 0xC7, 0xBC, 0xF3, 0xCF,
0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0xC0, 0x3C, 0x03, 0xC0, 0x3F, 0xE7, 0xFC, 0xFF, 0x9C, 0x03, 0x80, 0xFE, 0x1F, 0xF3,
0xFF, 0x1F, 0xE0, 0x3C, 0x07, 0xBB, 0xF7, 0xFF, 0xFF, 0x9F, 0xE0, 0x07, 0xE0, 0xFE, 0x3F, 0xE3, 0xF8, 0x7C, 0x07,
0xFC, 0x7F, 0xEF, 0xFE, 0xFB, 0xEF, 0x0F, 0xF8, 0xF7, 0xFE, 0x7F, 0xE3, 0xFC, 0x1F, 0x80, 0xFF, 0xFF, 0xFF, 0xFF,
0xE0, 0x3E, 0x03, 0xC0, 0x7C, 0x07, 0x80, 0xF8, 0x0F, 0x00, 0xF0, 0x1E, 0x01, 0xE0, 0x1E, 0x01, 0xE0, 0x1E, 0x00,
0x1F, 0x83, 0xFC, 0x7F, 0xE7, 0xFE, 0x79, 0xE7, 0x9E, 0x7F, 0xC3, 0xFC, 0x7F, 0xE7, 0xBE, 0xF0, 0xFF, 0x9F, 0x7F,
0xE7, 0xFE, 0x3F, 0xC0, 0x3F, 0x87, 0xFC, 0x7F, 0xEF, 0xBE, 0xF1, 0xEF, 0x1E, 0xFD, 0xE7, 0xFE, 0x7F, 0xE3, 0xFE,
0x03, 0xE1, 0xFC, 0x7F, 0xC7, 0xF8, 0x7E, 0x00, 0x77, 0xFE, 0xE0, 0x00, 0x0E, 0xFF, 0xDC, 0x77, 0xFE, 0xE0, 0x00,
0x00, 0x7B, 0xDF, 0xEF, 0x78, 0x01, 0xC3, 0xF3, 0xFF, 0xFE, 0xFE, 0x3E, 0x0F, 0xE3, 0xFE, 0x3F, 0xC3, 0xF0, 0x1C,
0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xE0, 0x3F, 0x0F, 0xF3, 0xFF, 0x1F, 0xC1, 0xF1, 0xFF,
0xFF, 0xFF, 0x3F, 0x0E, 0x00, 0x7F, 0x3F, 0xEF, 0xF9, 0xDE, 0x07, 0x81, 0xE0, 0xF8, 0x7C, 0x1E, 0x0F, 0x03, 0xC0,
0x00, 0x3C, 0x0F, 0x83, 0xE0, 0xF0, 0x07, 0xFC, 0x03, 0xFF, 0xE0, 0xFE, 0xFE, 0x3E, 0x03, 0xE7, 0xBF, 0xFD, 0xEF,
0xFB, 0xB9, 0xFF, 0x7F, 0x79, 0xEF, 0xEF, 0x3C, 0xFD, 0xE7, 0xBF, 0xBC, 0xF7, 0xF7, 0xDE, 0xEF, 0x7F, 0xFD, 0xE7,
0xFF, 0x1F, 0x00, 0x01, 0xFC, 0xE0, 0x1F, 0xFC, 0x00, 0xFF, 0x80, 0x07, 0xC0, 0x0F, 0x80, 0x3F, 0x80, 0x7F, 0x00,
0xFF, 0x03, 0xDE, 0x07, 0xBC, 0x1F, 0x3C, 0x3C, 0x78, 0x7F, 0xF1, 0xFF, 0xF3, 0xFF, 0xE7, 0x83, 0xFF, 0x07, 0xFC,
0x07, 0x80, 0xFF, 0xC7, 0xFF, 0x3F, 0xFD, 0xE3, 0xEF, 0x1F, 0x7F, 0xF3, 0xFF, 0x9F, 0xFE, 0xF1, 0xF7, 0x87, 0xFC,
0x3F, 0xE7, 0xEF, 0xFF, 0x7F, 0xF3, 0xFF, 0x00, 0x1F, 0xF1, 0xFF, 0x9F, 0xFC, 0xFD, 0xEF, 0x80, 0x78, 0x03, 0xC0,
0x1E, 0x00, 0xF0, 0x07, 0x80, 0x3E, 0x01, 0xFC, 0xE7, 0xFF, 0x1F, 0xF8, 0x7F, 0xC0, 0xFF, 0xC3, 0xFF, 0xCF, 0xFF,
0x3C, 0xFE, 0xF0, 0x7B, 0xC1, 0xFF, 0x03, 0xFC, 0x0F, 0xF0, 0x3F, 0xC1, 0xFF, 0x07, 0xBC, 0xFE, 0xFF, 0xF3, 0xFF,
0x8F, 0xFC, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x0F, 0x01, 0xE0, 0x3F, 0xF7, 0xFE, 0xFF, 0xDE, 0x03, 0xC0, 0x78,
0x0F, 0xFF, 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x0F, 0x01, 0xE0, 0x3F, 0xF7, 0xFE, 0xFF, 0xDE, 0x03,
0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x3C, 0x00, 0x1F, 0xF1, 0xFF, 0x9F, 0xFC, 0xFC, 0xEF, 0x80, 0x78, 0x03, 0xC0, 0x1E,
0x0F, 0xF0, 0x7F, 0x83, 0xFE, 0x1F, 0xFC, 0xF7, 0xFF, 0x9F, 0xFC, 0x7F, 0xE0, 0xF0, 0x7F, 0x83, 0xFC, 0x1F, 0xE0,
0xFF, 0x07, 0xF8, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x83, 0xFC, 0x1F, 0xE0, 0xFF, 0x07, 0xF8, 0x3F, 0xC1, 0xE0,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x03, 0xC0, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x3C, 0x0F, 0x03,
0xC0, 0xF0, 0x3D, 0x9F, 0xFF, 0xFF, 0xEF, 0xF0, 0xF0, 0xFB, 0xC7, 0xCF, 0x3E, 0x3D, 0xF0, 0xFF, 0x83, 0xFC, 0x0F,
0xE0, 0x3F, 0xC0, 0xFF, 0x83, 0xDF, 0x0F, 0x7C, 0x3C, 0xF8, 0xF1, 0xF3, 0xC3, 0xEF, 0x0F, 0x80, 0xF0, 0x1E, 0x03,
0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0x78, 0x0F, 0xFF, 0xFF, 0xFF, 0xF8, 0x78,
0x0F, 0x3E, 0x0F, 0x9F, 0x07, 0xCF, 0xC7, 0xEF, 0xE3, 0xF7, 0xFB, 0xFB, 0xFD, 0xFF, 0xEE, 0xEF, 0xF7, 0xF7, 0xFB,
0xF3, 0xFC, 0xF9, 0xFE, 0x7C, 0xFF, 0x1C, 0x7F, 0x80, 0x3F, 0xC0, 0x1E, 0xF0, 0x7F, 0xE1, 0xFF, 0xC7, 0xFF, 0x9F,
0xFE, 0x7F, 0xFD, 0xFF, 0xFF, 0xFD, 0xFF, 0xF3, 0xFF, 0xC7, 0xFF, 0x1F, 0xFC, 0x3F, 0xF0, 0xFF, 0xC1, 0xFF, 0x03,
0xC0, 0x1F, 0xE0, 0x7F, 0xF1, 0xFF, 0xF3, 0xF7, 0xEF, 0x83, 0xDE, 0x07, 0xFC, 0x07, 0xF8, 0x0F, 0xF0, 0x1F, 0xE0,
0x7F, 0xE0, 0xFF, 0xF7, 0xE7, 0xFF, 0xC7, 0xFF, 0x07, 0xF8, 0x00, 0xFF, 0xCF, 0xFE, 0xFF, 0xFF, 0x3F, 0xF0, 0xFF,
0x0F, 0xF3, 0xFF, 0xFF, 0xFF, 0xEF, 0xFC, 0xF0, 0x0F, 0x00, 0xF0, 0x0F, 0x00, 0xF0, 0x00, 0x1F, 0xE0, 0x7F, 0xF1,
0xFF, 0xE3, 0xF7, 0xEF, 0x83, 0xDE, 0x07, 0xFC, 0x07, 0xF8, 0x0F, 0xF0, 0x1F, 0xE0, 0x7F, 0xE0, 0xFF, 0xF7, 0xE7,
0xFF, 0xC7, 0xFF, 0x07, 0xF8, 0x01, 0xFC, 0x01, 0xFC, 0x01, 0xF8, 0xFF, 0xC7, 0xFF, 0x3F, 0xFD, 0xE7, 0xEF, 0x0F,
0x78, 0x7B, 0xCF, 0xDF, 0xFE, 0xFF, 0xE7, 0xFE, 0x3D, 0xF1, 0xE7, 0xCF, 0x1F, 0x78, 0xFB, 0xC3, 0xE0, 0x1F, 0xE3,
0xFE, 0x7F, 0xE7, 0xDC, 0x78, 0x07, 0xE0, 0x7F, 0x83, 0xFE, 0x0F, 0xE0, 0x3F, 0x01, 0xF7, 0xBF, 0x7F, 0xFF, 0xFE,
0x7F, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x1F, 0x00, 0xF8, 0x07, 0xC0, 0x3E, 0x01, 0xF0, 0x0F, 0x80, 0x7C, 0x03,
0xE0, 0x1F, 0x00, 0xF8, 0x07, 0xC0, 0x3E, 0x00, 0xF0, 0x7F, 0x83, 0xFC, 0x1F, 0xE0, 0xFF, 0x07, 0xF8, 0x3F, 0xC1,
0xFE, 0x0F, 0xF0, 0x7F, 0x83, 0xFE, 0x3F, 0xFB, 0xE7, 0xFF, 0x3F, 0xF0, 0x7F, 0x00, 0xF0, 0x1F, 0xF0, 0x3D, 0xE0,
0xFB, 0xC1, 0xE7, 0xC3, 0xC7, 0x8F, 0x8F, 0x1E, 0x1F, 0x3C, 0x1E, 0xF0, 0x3D, 0xE0, 0x3F, 0xC0, 0x7F, 0x00, 0xFE,
0x00, 0xF8, 0x01, 0xF0, 0x00, 0xF0, 0x00, 0xFF, 0x80, 0x1F, 0x78, 0xF1, 0xE7, 0x8F, 0x1E, 0x78, 0xF9, 0xE7, 0x9F,
0x9E, 0x7D, 0xFB, 0xC3, 0xDF, 0xFC, 0x3F, 0xFF, 0xC3, 0xFB, 0xFC, 0x3F, 0x9F, 0x81, 0xF9, 0xF8, 0x1F, 0x1F, 0x81,
0xF0, 0xF8, 0x0F, 0x0F, 0x00, 0xF8, 0x7D, 0xF3, 0xE7, 0xEF, 0x8F, 0xFC, 0x1F, 0xE0, 0x7F, 0x80, 0xFC, 0x01, 0xF0,
0x0F, 0xC0, 0x7F, 0x83, 0xFF, 0x0F, 0xFC, 0x7C, 0xF9, 0xE1, 0xFF, 0x87, 0xC0, 0xF8, 0x7F, 0xE1, 0xE7, 0xCF, 0x9F,
0x3C, 0x3F, 0xF0, 0x7F, 0x81, 0xFE, 0x03, 0xF0, 0x0F, 0xC0, 0x1E, 0x00, 0x78, 0x01, 0xE0, 0x07, 0x80, 0x1E, 0x00,
0x78, 0x00, 0x7F, 0xF3, 0xFF, 0x9F, 0xFC, 0x03, 0xE0, 0x3E, 0x03, 0xE0, 0x3E, 0x01, 0xE0, 0x1F, 0x01, 0xF0, 0x0F,
0x00, 0xF8, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0xFF, 0xFF, 0xFF, 0x8F, 0x1E, 0x3C, 0x78, 0xF1, 0xE3, 0xC7, 0x8F,
0x1E, 0x3C, 0x78, 0xF1, 0xFF, 0xFF, 0xF0, 0xF0, 0x0F, 0x01, 0xE0, 0x3C, 0x03, 0xC0, 0x78, 0x0F, 0x00, 0xF0, 0x1E,
0x03, 0xC0, 0x3C, 0x07, 0x80, 0xF8, 0x0F, 0x01, 0xE0, 0x3E, 0x03, 0xC0, 0x78, 0x0F, 0x80, 0xF0, 0xFF, 0xFF, 0xCF,
0x3C, 0xF3, 0xCF, 0x3C, 0xF3, 0xCF, 0x3C, 0xF3, 0xCF, 0x3F, 0xFF, 0xFF, 0x0F, 0x01, 0xF8, 0x1F, 0x83, 0xFC, 0x3F,
0xC7, 0x9E, 0x79, 0xEF, 0x0F, 0x70, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x63, 0xCF, 0x9F, 0x18, 0x7F, 0x8F, 0xF8,
0xFF, 0x19, 0xF3, 0xFE, 0xFF, 0xFE, 0x7F, 0xCF, 0xFF, 0xEF, 0xFC, 0xFF, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0x78, 0x0F,
0x01, 0xFF, 0x3F, 0xF7, 0xFF, 0xFB, 0xFE, 0x3F, 0xC7, 0xF8, 0xFF, 0x7F, 0xFF, 0xBF, 0xF7, 0xFC, 0x1F, 0xCF, 0xF7,
0xFD, 0xF6, 0x78, 0x1E, 0x07, 0x81, 0xF3, 0x7F, 0xCF, 0xF1, 0xFC, 0x00, 0xF0, 0x0F, 0x00, 0xF0, 0x0F, 0x00, 0xF1,
0xFF, 0x3F, 0xF7, 0xFF, 0x7D, 0xF7, 0x8F, 0x78, 0xF7, 0x8F, 0x7C, 0xF7, 0xFF, 0x3F, 0xF1, 0xFF, 0x1F, 0xC3, 0xFE,
0x7F, 0xE7, 0xDF, 0x7F, 0xF7, 0xFF, 0x78, 0x07, 0xEE, 0x7F, 0xE3, 0xFE, 0x1F, 0xE0, 0x3F, 0x3F, 0xBF, 0xDF, 0x6F,
0x07, 0xFB, 0xFD, 0xFE, 0xF0, 0x78, 0x3C, 0x1E, 0x0F, 0x07, 0x83, 0xC1, 0xE0, 0x1F, 0xE7, 0xFD, 0xFF, 0xBE, 0xF7,
0x9E, 0xF3, 0xDF, 0x7B, 0xFF, 0x7F, 0xE7, 0xFC, 0x07, 0x9D, 0xF7, 0xFE, 0xFF, 0x9F, 0xE0, 0xF0, 0x3C, 0x0F, 0x03,
0xC0, 0xF0, 0x3F, 0xEF, 0xFF, 0xFF, 0xF7, 0xFC, 0xFF, 0x3F, 0xCF, 0xF3, 0xFC, 0xFF, 0x3F, 0xCF, 0xFF, 0xFF, 0x0F,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x1E, 0x3C, 0x78, 0xF0, 0x03, 0xC7, 0x8F, 0x1E, 0x3C, 0x78, 0xF1, 0xE3, 0xC7, 0x8F,
0x3F, 0xFF, 0xF7, 0xE0, 0xF0, 0x1E, 0x03, 0xC0, 0x78, 0x0F, 0x01, 0xE7, 0xFD, 0xF7, 0xFC, 0xFF, 0x1F, 0xC3, 0xFC,
0x7F, 0xCF, 0x79, 0xEF, 0xBC, 0xFF, 0x8F, 0xF3, 0xCF, 0x3C, 0xF3, 0xCF, 0x3C, 0xF3, 0xCF, 0x3C, 0xFB, 0xFF, 0xDE,
0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFF, 0xF7, 0xFF, 0xF3, 0xCF, 0xF3, 0xCF, 0xF3, 0xCF, 0xF3, 0xCF, 0xF3, 0xCF, 0xF3,
0xCF, 0xF3, 0xCF, 0xFF, 0xBF, 0xFF, 0xFF, 0xDF, 0xF3, 0xFC, 0xFF, 0x3F, 0xCF, 0xF3, 0xFC, 0xFF, 0x3C, 0x1F, 0xC3,
0xFE, 0x7F, 0xF7, 0xDF, 0x78, 0xF7, 0x8F, 0x78, 0xF7, 0xDF, 0x7F, 0xF3, 0xFE, 0x1F, 0xC0, 0xFF, 0x9F, 0xFB, 0xFF,
0x7B, 0xFF, 0x1F, 0xE3, 0xFC, 0x7F, 0xDF, 0xFF, 0xFF, 0xFB, 0xFE, 0x78, 0x0F, 0x01, 0xE0, 0x3C, 0x00, 0x1F, 0xF3,
0xFF, 0x7F, 0xF7, 0xCF, 0x78, 0xF7, 0x8F, 0x78, 0xF7, 0xDF, 0x7F, 0xF3, 0xFF, 0x1F, 0xF0, 0x0F, 0x00, 0xF0, 0x0F,
0x00, 0xF0, 0xFF, 0xFF, 0xFF, 0xF2, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0x3F, 0x9F, 0xE7, 0xF9, 0xEE, 0x7F,
0x9F, 0xE3, 0xFD, 0xDF, 0x7F, 0xDF, 0xE7, 0xF0, 0xF0, 0xF0, 0xF0, 0xFF, 0xFF, 0xFF, 0xF0, 0xF0, 0xF0, 0xF0, 0xFB,
0xFF, 0x7F, 0x7F, 0xF3, 0xFC, 0xFF, 0x3F, 0xCF, 0xF3, 0xFC, 0xFF, 0x3F, 0xEF, 0xFF, 0xDF, 0xF3, 0xFC, 0xF0, 0xFF,
0x1E, 0x79, 0xE7, 0x9E, 0x7B, 0xC3, 0xFC, 0x3F, 0xC3, 0xF8, 0x1F, 0x81, 0xF0, 0x0F, 0x00, 0xF1, 0xCF, 0xF9, 0xE7,
0xBE, 0xF3, 0xCF, 0x7D, 0xE7, 0xBF, 0xE3, 0xFF, 0xF0, 0xFF, 0xF8, 0x7E, 0xF8, 0x3F, 0x7C, 0x0F, 0x3E, 0x07, 0x8E,
0x00, 0xF9, 0xE7, 0xBE, 0x7F, 0xC3, 0xF8, 0x1F, 0x81, 0xF0, 0x1F, 0x83, 0xFC, 0x7F, 0xC7, 0x9E, 0xF1, 0xF0, 0xF1,
0xFF, 0x1E, 0x79, 0xE7, 0x9E, 0x7B, 0xC3, 0xFC, 0x3F, 0xC3, 0xF8, 0x1F, 0x81, 0xF0, 0x0F, 0x0D, 0xF0, 0xFE, 0x0F,
0xC0, 0xF8, 0x00, 0x7F, 0xDF, 0xF7, 0xFC, 0x1E, 0x0F, 0x07, 0x83, 0xC1, 0xF0, 0xFF, 0xFF, 0xFF, 0xFC, 0x1F, 0x1F,
0x3F, 0x3E, 0x3C, 0x3C, 0x3C, 0x3C, 0x7C, 0xF8, 0xF8, 0xFC, 0x7C, 0x3C, 0x3C, 0x3C, 0x3E, 0x3F, 0x3F, 0x1F, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xF8, 0xFC, 0x7C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3E, 0x1F,
0x1F, 0x3F, 0x3E, 0x3C, 0x3C, 0x3C, 0x7C, 0xFC, 0xF8, 0xF0, 0x3C, 0x77, 0xFF, 0xFF, 0xEF, 0xFE, 0xE3, 0xC0, 0xFF,
0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x0F, 0x03, 0xC0, 0xF0, 0x7F, 0x3F, 0xDF, 0xF7, 0xD9, 0xE0, 0x78, 0x1F,
0x37, 0xFC, 0xFF, 0x1F, 0xC3, 0xC0, 0xF0, 0x3C, 0x0F, 0xE3, 0xFC, 0xFF, 0x1F, 0x63, 0xC0, 0x78, 0x3F, 0xE7, 0xFC,
0xFF, 0x87, 0x80, 0xF0, 0x1E, 0x03, 0xFE, 0xFF, 0xDF, 0xF8, 0x00, 0x07, 0x0E, 0xFF, 0xF7, 0xFE, 0x7F, 0xC7, 0x9E,
0x79, 0xE7, 0xFC, 0x7F, 0xEF, 0xFF, 0x70, 0xE0, 0x78, 0x79, 0xF3, 0xE3, 0xCF, 0x0F, 0xFC, 0x1F, 0xE0, 0x7F, 0x80,
0xFC, 0x0F, 0xFC, 0x3F, 0xF0, 0x1E, 0x00, 0x78, 0x0F, 0xFC, 0x3F, 0xF0, 0x1E, 0x00, 0x78, 0x00, 0xFF, 0xFF, 0xFF,
0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x1F, 0xC7, 0xF9, 0xFF, 0x3C, 0xE7, 0xC0, 0xFE, 0x1F, 0xE3, 0xFE, 0xF7,
0xDE, 0x3F, 0xE7, 0x3F, 0xE3, 0xF8, 0x3F, 0x80, 0xF3, 0x9E, 0x7F, 0xDF, 0xFB, 0xFE, 0x00, 0x77, 0xFB, 0xDD, 0xE0,
0x0F, 0xE0, 0x7F, 0xF1, 0xFB, 0xE3, 0x81, 0xEE, 0x7D, 0xFD, 0xF9, 0xFB, 0xB3, 0xFF, 0x07, 0xEE, 0xCF, 0xDF, 0x9F,
0x9F, 0xFB, 0x81, 0xE7, 0xEF, 0xC7, 0xFF, 0x03, 0xF8, 0x00, 0x7E, 0x7F, 0x6F, 0x7F, 0xFF, 0xFF, 0xFF, 0x7F, 0x00,
0x01, 0xC7, 0x3C, 0xF7, 0xDF, 0x7B, 0xEF, 0xBC, 0x7B, 0xE7, 0xDF, 0x3C, 0xF1, 0xC7, 0x00, 0x00, 0xFF, 0xFF, 0xFF,
0xFC, 0x07, 0x01, 0xC0, 0x70, 0x1C, 0x07, 0xFF, 0xFF, 0xF8, 0x0F, 0xE0, 0x7F, 0xF1, 0xFB, 0xE3, 0x81, 0xEE, 0xFD,
0xFD, 0xFD, 0xFB, 0xFB, 0xF7, 0xE7, 0xEF, 0xCF, 0xDF, 0xDF, 0xBB, 0xFB, 0x81, 0xE7, 0xEF, 0xC7, 0xFF, 0x03, 0xF8,
0x00, 0xFF, 0xFF, 0x7C, 0xFE, 0xFF, 0xFF, 0xFE, 0x7C, 0x1E, 0x07, 0x81, 0xE0, 0x78, 0xFF, 0xFF, 0xFF, 0xFC, 0x78,
0x1E, 0x07, 0x81, 0xE0, 0x00, 0xFF, 0xFF, 0xFF, 0xFC, 0x7D, 0xFD, 0xF8, 0xF3, 0xCF, 0x9E, 0x7F, 0xFE, 0xFD, 0xFD,
0xF9, 0xE3, 0xE3, 0xF3, 0xFF, 0xFC, 0x18, 0xF7, 0xFC, 0xE0, 0xF3, 0xFC, 0xFF, 0x3F, 0xCF, 0xF3, 0xFC, 0xFF, 0x3F,
0xEF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0xF0, 0x3C, 0x0F, 0x00, 0x0F, 0xF8, 0xFF, 0xF7, 0xFF, 0xDF, 0xEF, 0xFF, 0xBF,
0xFE, 0xF7, 0xFB, 0xDF, 0xEF, 0x3F, 0xBC, 0x7E, 0xF0, 0x7B, 0xC1, 0xEF, 0x07, 0xBC, 0x1E, 0xF0, 0x7B, 0xC1, 0xEF,
0x07, 0xBC, 0x1E, 0xF0, 0x7B, 0xC0, 0x77, 0xFE, 0xE0, 0x18, 0xE3, 0xDF, 0x7D, 0xE0, 0x1D, 0xFF, 0xDF, 0x1C, 0x71,
0xC7, 0x1C, 0x3F, 0x3F, 0xFF, 0xFE, 0x7F, 0x3F, 0xFD, 0xFE, 0x7E, 0x00, 0x03, 0x9E, 0x3E, 0xF0, 0xFB, 0xC3, 0xDF,
0x1F, 0x78, 0xF7, 0xCF, 0xBC, 0xFB, 0xC3, 0x9E, 0x00, 0x00, 0x3C, 0x1C, 0x1F, 0x87, 0x83, 0xF0, 0xF0, 0x7E, 0x3C,
0x01, 0xC7, 0x80, 0x39, 0xE0, 0x07, 0x3C, 0xF0, 0xEF, 0x3E, 0x1D, 0xEF, 0xC0, 0x7B, 0xB8, 0x0F, 0x7F, 0x83, 0xCF,
0xF0, 0x78, 0x1C, 0x1E, 0x03, 0x83, 0xC0, 0x00, 0x3C, 0x3C, 0x3F, 0x0F, 0x0F, 0xC7, 0x83, 0xF1, 0xE0, 0x1C, 0xF0,
0x07, 0x3C, 0x01, 0xDF, 0xF8, 0x77, 0xFF, 0x1F, 0xDF, 0xC0, 0xF0, 0xF0, 0x78, 0x78, 0x1C, 0x3C, 0x0F, 0x0E, 0x03,
0x87, 0xF1, 0xE1, 0xFC, 0xFC, 0x1E, 0x1F, 0xC3, 0xC1, 0xF8, 0xF0, 0x1F, 0x1E, 0x03, 0xE7, 0x80, 0x1C, 0xF0, 0x13,
0xBC, 0xF7, 0xF7, 0xBE, 0xFD, 0xEF, 0xC0, 0x3F, 0xB8, 0x0F, 0x7F, 0x81, 0xEF, 0xF0, 0x78, 0x1C, 0x0E, 0x03, 0x83,
0xC0, 0x00, 0x1E, 0x07, 0xC1, 0xF0, 0x78, 0x00, 0x03, 0x81, 0xE0, 0xF8, 0x7C, 0x1E, 0x0F, 0x03, 0xEE, 0xFF, 0x9F,
0xE3, 0xF8, 0x03, 0x00, 0x0F, 0x00, 0x1E, 0x00, 0x1E, 0x00, 0x1C, 0x00, 0x00, 0x01, 0xF0, 0x03, 0xE0, 0x0F, 0xE0,
0x1F, 0xC0, 0x3F, 0xC0, 0xF7, 0x81, 0xEF, 0x07, 0xCF, 0x0F, 0x1E, 0x1F, 0xFC, 0x7F, 0xFC, 0xFF, 0xF9, 0xE0, 0xFF,
0xC1, 0xFF, 0x01, 0xE0, 0x01, 0x80, 0x07, 0x80, 0x0F, 0x00, 0x3C, 0x00, 0x70, 0x00, 0x00, 0x01, 0xF0, 0x03, 0xE0,
0x0F, 0xE0, 0x1F, 0xC0, 0x3F, 0xC0, 0xF7, 0x81, 0xEF, 0x07, 0xCF, 0x0F, 0x1E, 0x1F, 0xFC, 0x7F, 0xFC, 0xFF, 0xF9,
0xE0, 0xFF, 0xC1, 0xFF, 0x01, 0xE0, 0x03, 0x80, 0x0F, 0x80, 0x3F, 0x80, 0x7F, 0x00, 0x6E, 0x00, 0x00, 0x01, 0xF0,
0x03, 0xE0, 0x0F, 0xE0, 0x1F, 0xC0, 0x3F, 0xC0, 0xF7, 0x81, 0xEF, 0x07, 0xCF, 0x0F, 0x1E, 0x1F, 0xFC, 0x7F, 0xFC,
0xFF, 0xF9, 0xE0, 0xFF, 0xC1, 0xFF, 0x01, 0xE0, 0x0F, 0xF0, 0x3F, 0xE0, 0x37, 0x80, 0x00, 0x00, 0x7C, 0x00, 0xF8,
0x03, 0xF8, 0x07, 0xF0, 0x0F, 0xF0, 0x3D, 0xE0, 0x7B, 0xC1, 0xF3, 0xC3, 0xC7, 0x87, 0xFF, 0x1F, 0xFF, 0x3F, 0xFE,
0x78, 0x3F, 0xF0, 0x7F, 0xC0, 0x78, 0x1E, 0xF0, 0x3D, 0xE0, 0x7B, 0xC0, 0x00, 0x00, 0x7C, 0x00, 0xF8, 0x03, 0xF8,
0x07, 0xF0, 0x0F, 0xF0, 0x3D, 0xE0, 0x7B, 0xC1, 0xF3, 0xC3, 0xC7, 0x87, 0xFF, 0x1F, 0xFF, 0x3F, 0xFE, 0x78, 0x3F,
0xF0, 0x7F, 0xC0, 0x78, 0x07, 0xC0, 0x0D, 0x80, 0x1F, 0x00, 0x3E, 0x00, 0x7C, 0x00, 0xF8, 0x03, 0xF8, 0x07, 0xF0,
0x1F, 0xF0, 0x3D, 0xE0, 0x7B, 0xC1, 0xF3, 0xC3, 0xC7, 0x87, 0xFF, 0x1F, 0xFF, 0x3F, 0xFE, 0x78, 0x3F, 0xF0, 0x7F,
0xC0, 0x78, 0x00, 0xFF, 0xF0, 0x07, 0xFF, 0x80, 0x7F, 0xFC, 0x07, 0xF8, 0x00, 0x3F, 0xC0, 0x03, 0xDE, 0x00, 0x1E,
0xFF, 0x81, 0xE7, 0xFC, 0x1F, 0x3F, 0xE0, 0xFF, 0xE0, 0x0F, 0xFF, 0x00, 0x7F, 0xF8, 0x07, 0xC3, 0xFF, 0xBC, 0x1F,
0xFF, 0xE0, 0xFF, 0xE0, 0x1F, 0xF1, 0xFF, 0x9F, 0xFC, 0xFD, 0xEF, 0x80, 0x78, 0x03, 0xC0, 0x1E, 0x00, 0xF0, 0x07,
0x80, 0x3E, 0x01, 0xFC, 0xE7, 0xFF, 0x1F, 0xF8, 0x7F, 0xC0, 0xF0, 0x07, 0x80, 0x5C, 0x03, 0xE0, 0x1F, 0x00, 0x0C,
0x03, 0xC0, 0x78, 0x07, 0x80, 0x70, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFE, 0x03, 0xC0, 0x78, 0x0F, 0xFD, 0xFF, 0xBF,
0xF7, 0x80, 0xF0, 0x1E, 0x03, 0xFF, 0xFF, 0xFF, 0xFE, 0x06, 0x00, 0xE0, 0x3C, 0x0F, 0x01, 0xC0, 0x00, 0x3F, 0xFF,
0xFF, 0xFF, 0xFE, 0x03, 0xC0, 0x78, 0x0F, 0xFD, 0xFF, 0xBF, 0xF7, 0x80, 0xF0, 0x1E, 0x03, 0xFF, 0xFF, 0xFF, 0xFE,
0x0E, 0x01, 0xE0, 0x7E, 0x1F, 0xC1, 0x98, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFE, 0x03, 0xC0, 0x78, 0x0F, 0xFD, 0xFF,
0xBF, 0xF7, 0x80, 0xF0, 0x1E, 0x03, 0xFF, 0xFF, 0xFF, 0xFE, 0x7B, 0xCF, 0x79, 0xEF, 0x00, 0x0F, 0xFF, 0xFF, 0xFF,
0xFF, 0x80, 0xF0, 0x1E, 0x03, 0xFF, 0x7F, 0xEF, 0xFD, 0xE0, 0x3C, 0x07, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x31,
0xC7, 0x8F, 0x1C, 0x07, 0x9E, 0x79, 0xE7, 0x9E, 0x79, 0xE7, 0x9E, 0x79, 0xE7, 0x9E, 0x78, 0x33, 0xDF, 0xEE, 0x03,
0xDE, 0xF7, 0xBD, 0xEF, 0x7B, 0xDE, 0xF7, 0xBD, 0xEF, 0x00, 0x30, 0xF3, 0xF7, 0xFC, 0xC0, 0x1E, 0x3C, 0x78, 0xF1,
0xE3, 0xC7, 0x8F, 0x1E, 0x3C, 0x78, 0xF1, 0xE3, 0xC7, 0x80, 0xF7, 0x7B, 0xFD, 0xC0, 0x03, 0xC1, 0xE0, 0xF0, 0x78,
0x3C, 0x1E, 0x0F, 0x07, 0x83, 0xC1, 0xE0, 0xF0, 0x78, 0x3C, 0x1E, 0x0F, 0x00, 0x3F, 0xE0, 0xFF, 0xF1, 0xFF, 0xF3,
0xEF, 0xE7, 0xC3, 0xEF, 0x87, 0xFF, 0xC7, 0xFF, 0x8F, 0x7C, 0x1E, 0xF8, 0x7D, 0xF0, 0xFB, 0xEF, 0xE7, 0xFF, 0xCF,
0xFF, 0x0F, 0xF8, 0x00, 0x0F, 0xE0, 0x7F, 0x81, 0xFC, 0x00, 0x00, 0xF0, 0x7F, 0xE1, 0xFF, 0xC7, 0xFF, 0x9F, 0xFE,
0x7F, 0xFD, 0xFF, 0xFF, 0xFD, 0xFF, 0xF3, 0xFF, 0xC7, 0xFF, 0x1F, 0xFC, 0x3F, 0xF0, 0xFF, 0xC1, 0xFF, 0x03, 0xC0,
0x03, 0x00, 0x0E, 0x00, 0x1E, 0x00, 0x1E, 0x00, 0x1C, 0x00, 0x00, 0x07, 0xF8, 0x1F, 0xFC, 0x7F, 0xFC, 0xFD, 0xFB,
0xE0, 0xF7, 0x81, 0xFF, 0x01, 0xFE, 0x03, 0xFC, 0x07, 0xF8, 0x1F, 0xF8, 0x3F, 0xFD, 0xF9, 0xFF, 0xF1, 0xFF, 0xC1,
0xFE, 0x00, 0x01, 0x80, 0x07, 0x80, 0x1F, 0x00, 0x3C, 0x00, 0x70, 0x00, 0x00, 0x07, 0xF8, 0x1F, 0xFC, 0x7F, 0xFC,
0xFD, 0xFB, 0xE0, 0xF7, 0x81, 0xFF, 0x01, 0xFE, 0x03, 0xFC, 0x07, 0xF8, 0x1F, 0xF8, 0x3F, 0xFD, 0xF9, 0xFF, 0xF1,
0xFF, 0xC1, 0xFE, 0x00, 0x03, 0x00, 0x0F, 0x00, 0x3F, 0x00, 0x7F, 0x00, 0xCC, 0x00, 0x00, 0x07, 0xF8, 0x1F, 0xFC,
0x7F, 0xFC, 0xFD, 0xFB, 0xE0, 0xF7, 0x81, 0xFF, 0x01, 0xFE, 0x03, 0xFC, 0x07, 0xF8, 0x1F, 0xF8, 0x3F, 0xFD, 0xF9,
0xFF, 0xF1, 0xFF, 0xC1, 0xFE, 0x00, 0x0F, 0x60, 0x3F, 0xC0, 0x7F, 0x80, 0x00, 0x01, 0xFE, 0x07, 0xFF, 0x1F, 0xFF,
0x3F, 0x7E, 0xF8, 0x3D, 0xE0, 0x7F, 0xC0, 0x7F, 0x80, 0xFF, 0x01, 0xFE, 0x07, 0xFE, 0x0F, 0xFF, 0x7E, 0x7F, 0xFC,
0x7F, 0xF0, 0x7F, 0x80, 0x1E, 0xE0, 0x3D, 0xE0, 0x7B, 0x80, 0x00, 0x01, 0xFE, 0x07, 0xFF, 0x1F, 0xFF, 0x3F, 0x7E,
0xF8, 0x3D, 0xE0, 0x7F, 0xC0, 0x7F, 0x80, 0xFF, 0x01, 0xFE, 0x07, 0xFE, 0x0F, 0xFF, 0x7E, 0x7F, 0xFC, 0x7F, 0xF0,
0x7F, 0x80, 0x61, 0xBC, 0xFF, 0xFD, 0xFE, 0x3F, 0x1F, 0xEF, 0xFF, 0xCF, 0x61, 0x80, 0x00, 0x08, 0x3F, 0xF8, 0xFF,
0xF3, 0xFF, 0xC7, 0xEF, 0xDF, 0x1F, 0xBC, 0x7F, 0xF9, 0xEF, 0xF7, 0x9F, 0xEE, 0x3F, 0xFC, 0xFF, 0xF1, 0xFF, 0xEF,
0xCF, 0xFF, 0x9F, 0xFE, 0x7F, 0xF0, 0x60, 0x00, 0x0C, 0x00, 0xF0, 0x07, 0xC0, 0x1E, 0x00, 0x30, 0x00, 0x03, 0xC1,
0xFE, 0x0F, 0xF0, 0x7F, 0x83, 0xFC, 0x1F, 0xE0, 0xFF, 0x07, 0xF8, 0x3F, 0xC1, 0xFE, 0x0F, 0xF8, 0xFF, 0xEF, 0x9F,
0xFC, 0xFF, 0xC1, 0xFC, 0x00, 0x03, 0x00, 0x1C, 0x01, 0xE0, 0x1E, 0x00, 0xE0, 0x00, 0x03, 0xC1, 0xFE, 0x0F, 0xF0,
0x7F, 0x83, 0xFC, 0x1F, 0xE0, 0xFF, 0x07, 0xF8, 0x3F, 0xC1, 0xFE, 0x0F, 0xF8, 0xFF, 0xEF, 0x9F, 0xFC, 0xFF, 0xC1,
0xFC, 0x00, 0x07, 0x00, 0x78, 0x07, 0xE0, 0x3F, 0x81, 0x98, 0x00, 0x03, 0xC1, 0xFE, 0x0F, 0xF0, 0x7F, 0x83, 0xFC,
0x1F, 0xE0, 0xFF, 0x07, 0xF8, 0x3F, 0xC1, 0xFE, 0x0F, 0xF8, 0xFF, 0xEF, 0x9F, 0xFC, 0xFF, 0xC1, 0xFC, 0x00, 0x3D,
0xE1, 0xEF, 0x0F, 0x78, 0x00, 0x0F, 0x07, 0xF8, 0x3F, 0xC1, 0xFE, 0x0F, 0xF0, 0x7F, 0x83, 0xFC, 0x1F, 0xE0, 0xFF,
0x07, 0xF8, 0x3F, 0xE3, 0xFF, 0xBE, 0x7F, 0xF3, 0xFF, 0x07, 0xF0, 0x01, 0x80, 0x0F, 0x00, 0x3C, 0x01, 0xE0, 0x07,
0x00, 0x00, 0x0F, 0x87, 0xFE, 0x1E, 0x7C, 0xF9, 0xF3, 0xC3, 0xFF, 0x07, 0xF8, 0x1F, 0xE0, 0x3F, 0x00, 0xFC, 0x01,
0xE0, 0x07, 0x80, 0x1E, 0x00, 0x78, 0x01, 0xE0, 0x07, 0x80, 0xF0, 0x0F, 0x00, 0xFF, 0xCF, 0xFE, 0xFF, 0xFF, 0x3F,
0xF0, 0xFF, 0x0F, 0xF3, 0xFF, 0xFF, 0xFF, 0xEF, 0xFC, 0xF0, 0x0F, 0x00, 0xF0, 0x00, 0x3F, 0x83, 0xFE, 0x1F, 0xF1,
0xF7, 0xCF, 0x1C, 0x79, 0xE3, 0xDE, 0x1E, 0xF0, 0xF7, 0x87, 0xBF, 0x3C, 0xFD, 0xE1, 0xEF, 0x6F, 0x7B, 0xFB, 0xFF,
0xDF, 0xFC, 0x0C, 0x03, 0xC0, 0x78, 0x07, 0x80, 0x70, 0x00, 0x1F, 0xE3, 0xFE, 0x3F, 0xC6, 0x7C, 0xFF, 0xBF, 0xFF,
0x9F, 0xF3, 0xFF, 0xFB, 0xFF, 0x3F, 0xE0, 0x07, 0x00, 0xE0, 0x3C, 0x0F, 0x01, 0xC0, 0x00, 0x1F, 0xE3, 0xFE, 0x3F,
0xC6, 0x7C, 0xFF, 0xBF, 0xFF, 0x9F, 0xF3, 0xFF, 0xFB, 0xFF, 0x3F, 0xE0, 0x0E, 0x03, 0xE0, 0xFE, 0x1F, 0xC3, 0xB0,
0x00, 0x1F, 0xE3, 0xFE, 0x3F, 0xC6, 0x7C, 0xFF, 0xBF, 0xFF, 0x9F, 0xF3, 0xFF, 0xFB, 0xFF, 0x3F, 0xE0, 0x3F, 0xC7,
0xF8, 0xDE, 0x00, 0x00, 0x00, 0xFF, 0x1F, 0xF1, 0xFE, 0x33, 0xE7, 0xFD, 0xFF, 0xFC, 0xFF, 0x9F, 0xFF, 0xDF, 0xF9,
0xFF, 0x7B, 0xCF, 0x79, 0xEF, 0x00, 0x00, 0x00, 0xFF, 0x1F, 0xF1, 0xFE, 0x33, 0xE7, 0xFD, 0xFF, 0xFC, 0xFF, 0x9F,
0xFF, 0xDF, 0xF9, 0xFF, 0x0F, 0x03, 0xE0, 0x7E, 0x0F, 0xC1, 0xF0, 0x1E, 0x00, 0x03, 0xFC, 0x7F, 0xC7, 0xF8, 0xCF,
0x9F, 0xF7, 0xFF, 0xF3, 0xFE, 0x7F, 0xFF, 0x7F, 0xE7, 0xFC, 0x7F, 0xFF, 0x1F, 0xFF, 0xE3, 0xFF, 0xF8, 0xCF, 0xDF,
0x3F, 0xFF, 0xDF, 0xFF, 0xFF, 0x9E, 0x03, 0xE7, 0xEE, 0xFF, 0xFF, 0x9F, 0xFF, 0xE3, 0xFF, 0xF8, 0x1F, 0xCF, 0xF7,
0xFD, 0xF6, 0x78, 0x1E, 0x07, 0x81, 0xF3, 0x7F, 0xCF, 0xF0, 0xFC, 0x3C, 0x0F, 0x07, 0xC1, 0xF0, 0x0C, 0x01, 0xE0,
0x1F, 0x00, 0x78, 0x03, 0x80, 0x00, 0x1F, 0xC3, 0xFE, 0x7F, 0xE7, 0xDF, 0x7F, 0xF7, 0xFF, 0x78, 0x07, 0xEE, 0x7F,
0xE3, 0xFE, 0x1F, 0xE0, 0x03, 0x00, 0x78, 0x0F, 0x80, 0xF0, 0x0E, 0x00, 0x00, 0x1F, 0xC3, 0xFE, 0x7F, 0xE7, 0xDF,
0x7F, 0xF7, 0xFF, 0x78, 0x07, 0xEE, 0x7F, 0xE3, 0xFE, 0x1F, 0xE0, 0x06, 0x00, 0xF0, 0x1F, 0x81, 0xFC, 0x19, 0x80,
0x00, 0x1F, 0xC3, 0xFE, 0x7F, 0xE7, 0xDF, 0x7F, 0xF7, 0xFF, 0x78, 0x07, 0xEE, 0x7F, 0xE3, 0xFE, 0x1F, 0xE0, 0x3D,
0xC3, 0xDE, 0x3D, 0xC0, 0x00, 0x00, 0x01, 0xFC, 0x3F, 0xE7, 0xFE, 0x7D, 0xF7, 0xFF, 0x7F, 0xF7, 0x80, 0x7E, 0xE7,
0xFE, 0x3F, 0xE1, 0xFE, 0x63, 0xCF, 0x8F, 0x18, 0x07, 0x9E, 0x79, 0xE7, 0x9E, 0x79, 0xE7, 0x9E, 0x78, 0x18, 0xF7,
0xDE, 0x70, 0x07, 0x9E, 0x79, 0xE7, 0x9E, 0x79, 0xE7, 0x9E, 0x78, 0x18, 0x3C, 0x7E, 0x7F, 0x66, 0x00, 0x3C, 0x3C,
0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x73, 0xBF, 0xDC, 0xE0, 0x00, 0x00, 0xF0, 0x78, 0x3C, 0x1E,
0x0F, 0x07, 0x83, 0xC1, 0xE0, 0xF0, 0x78, 0x3C, 0x07, 0x10, 0x7F, 0x81, 0xFC, 0x1F, 0xC0, 0x7E, 0x00, 0x78, 0x7F,
0xC7, 0xFE, 0x7F, 0xF3, 0xEF, 0x9E, 0x3C, 0xF1, 0xE7, 0xDF, 0x3F, 0xF0, 0xFF, 0x83, 0xF0, 0x3D, 0x9F, 0xE7, 0xF8,
0x00, 0x00, 0x3F, 0xEF, 0xFF, 0xFF, 0xF7, 0xFC, 0xFF, 0x3F, 0xCF, 0xF3, 0xFC, 0xFF, 0x3F, 0xCF, 0x06, 0x00, 0xE0,
0x0F, 0x00, 0x78, 0x03, 0x80, 0x00, 0x1F, 0xC3, 0xFE, 0x7F, 0xF7, 0xDF, 0x78, 0xF7, 0x8F, 0x78, 0xF7, 0xDF, 0x7F,
0xF3, 0xFE, 0x1F, 0xC0, 0x03, 0x00, 0x78, 0x07, 0x80, 0xF0, 0x0E, 0x00, 0x00, 0x1F, 0xC3, 0xFE, 0x7F, 0xF7, 0xDF,
0x78, 0xF7, 0x8F, 0x78, 0xF7, 0xDF, 0x7F, 0xF3, 0xFE, 0x1F, 0xC0, 0x06, 0x00, 0xF0, 0x1F, 0x81, 0xFC, 0x19, 0x80,
0x00, 0x1F, 0xC3, 0xFE, 0x7F, 0xF7, 0xDF, 0x78, 0xF7, 0x8F, 0x78, 0xF7, 0xDF, 0x7F, 0xF3, 0xFE, 0x1F, 0xC0, 0x1E,
0xC3, 0xFC, 0x3F, 0xC0, 0x00, 0x00, 0x01, 0xFC, 0x3F, 0xE7, 0xFF, 0x7D, 0xF7, 0x8F, 0x78, 0xF7, 0x8F, 0x7D, 0xF7,
0xFF, 0x3F, 0xE1, 0xFC, 0x3D, 0xC3, 0xDE, 0x3D, 0xC0, 0x00, 0x00, 0x01, 0xFC, 0x3F, 0xE7, 0xFF, 0x7D, 0xF7, 0x8F,
0x78, 0xF7, 0x8F, 0x7D, 0xF7, 0xFF, 0x3F, 0xE1, 0xFC, 0x1E, 0x07, 0x81, 0xE0, 0x78, 0x00, 0x3F, 0xFF, 0xFF, 0xFF,
0x00, 0x07, 0x81, 0xE0, 0x78, 0x1E, 0x00, 0x00, 0x61, 0xFF, 0x3F, 0xE7, 0xFF, 0x7F, 0xF7, 0xFF, 0x77, 0xF7, 0xEF,
0x7F, 0xF7, 0xFF, 0x7F, 0xE7, 0xFC, 0x20, 0x00, 0x18, 0x0F, 0x03, 0xC0, 0x78, 0x0E, 0x00, 0x0F, 0x3F, 0xCF, 0xF3,
0xFC, 0xFF, 0x3F, 0xCF, 0xF3, 0xFE, 0xFF, 0xFD, 0xFF, 0x3F, 0xC0, 0x06, 0x03, 0xC1, 0xF0, 0x78, 0x1C, 0x00, 0x0F,
0x3F, 0xCF, 0xF3, 0xFC, 0xFF, 0x3F, 0xCF, 0xF3, 0xFE, 0xFF, 0xFD, 0xFF, 0x3F, 0xC0, 0x0C, 0x07, 0x83, 0xF0, 0xFE,
0x33, 0x00, 0x0F, 0x3F, 0xCF, 0xF3, 0xFC, 0xFF, 0x3F, 0xCF, 0xF3, 0xFE, 0xFF, 0xFD, 0xFF, 0x3F, 0xC0, 0x7B, 0x9F,
0xE7, 0xB8, 0x00, 0x00, 0x3C, 0xFF, 0x3F, 0xCF, 0xF3, 0xFC, 0xFF, 0x3F, 0xCF, 0xFB, 0xFF, 0xF7, 0xFC, 0xFF, 0x03,
0x00, 0x78, 0x07, 0x80, 0xF0, 0x0E, 0x00, 0x00, 0xF1, 0xFF, 0x1E, 0x79, 0xE7, 0x9E, 0x7B, 0xC3, 0xFC, 0x3F, 0xC3,
0xF8, 0x1F, 0x81, 0xF0, 0x0F, 0x0D, 0xF0, 0xFE, 0x0F, 0xC0, 0xF8, 0x00, 0xF0, 0x1E, 0x03, 0xC0, 0x78, 0x0F, 0x01,
0xFF, 0x3F, 0xF7, 0xFE, 0xF7, 0xFE, 0x3F, 0xC7, 0xF8, 0xFF, 0xBF, 0xFF, 0xFF, 0xF7, 0xFC, 0xF0, 0x1E, 0x03, 0xC0,
0x78, 0x00, 0x3B, 0xC7, 0xBC, 0x3B, 0xC0, 0x00, 0x00, 0x0F, 0x1F, 0xF1, 0xE7, 0x9E, 0x79, 0xE7, 0xBC, 0x3F, 0xC3,
0xFC, 0x3F, 0x81, 0xF8, 0x1F, 0x00, 0xF0, 0xDF, 0x0F, 0xE0, 0xFC, 0x0F, 0x80, 0x0F, 0xE0, 0x1F, 0xC0, 0x00, 0x00,
0x3E, 0x00, 0x7C, 0x01, 0xFC, 0x03, 0xF8, 0x07, 0xF8, 0x1E, 0xF0, 0x3D, 0xE0, 0xF9, 0xE1, 0xE3, 0xC3, 0xFF, 0x8F,
0xFF, 0x9F, 0xFF, 0x3C, 0x1F, 0xF8, 0x3F, 0xE0, 0x3C, 0x3F, 0xC7, 0xF8, 0x00, 0x00, 0x07, 0xF8, 0xFF, 0x8F, 0xF1,
0x9F, 0x3F, 0xEF, 0xFF, 0xE7, 0xFC, 0xFF, 0xFE, 0xFF, 0xCF, 0xF8, 0x0F, 0xF0, 0x1F, 0xC0, 0x1F, 0x80, 0x00, 0x00,
0x7C, 0x00, 0xF8, 0x03, 0xF8, 0x07, 0xF0, 0x0F, 0xF0, 0x3D, 0xE0, 0x7B, 0xC1, 0xF3, 0xC3, 0xC7, 0x87, 0xFF, 0x1F,
0xFF, 0x3F, 0xFE, 0x78, 0x3F, 0xF0, 0x7F, 0xC0, 0x78, 0x3F, 0xC7, 0xF0, 0x7E, 0x00, 0x00, 0x00, 0xFF, 0x1F, 0xF1,
0xFE, 0x33, 0xE7, 0xFD, 0xFF, 0xFC, 0xFF, 0x9F, 0xFF, 0xDF, 0xF9, 0xFF, 0x07, 0xC0, 0x07, 0xC0, 0x0F, 0xE0, 0x0F,
0xE0, 0x0F, 0xF0, 0x1E, 0xF0, 0x1E, 0xF0, 0x3E, 0x78, 0x3C, 0x78, 0x3F, 0xF8, 0x7F, 0xFC, 0x7F, 0xFC, 0x78, 0x3E,
0xF8, 0x3E, 0xF0, 0x1E, 0x00, 0x1E, 0x00, 0x1C, 0x00, 0x3C, 0x00, 0x3F, 0x00, 0x1F, 0x7F, 0x8F, 0xF8, 0xFF, 0x19,
0xF3, 0xFE, 0xFF, 0xFE, 0x7F, 0xCF, 0xFF, 0xEF, 0xFC, 0xFF, 0x81, 0xE0, 0x7C, 0x0F, 0xC0, 0xF8, 0x01, 0x80, 0x1E,
0x00, 0xF0, 0x0F, 0x00, 0x70, 0x00, 0x00, 0x7F, 0xC7, 0xFE, 0x7F, 0xF3, 0xF7, 0xBE, 0x01, 0xE0, 0x0F, 0x00, 0x78,
0x03, 0xC0, 0x1E, 0x00, 0xF8, 0x07, 0xF3, 0x9F, 0xFC, 0x7F, 0xE1, 0xFF, 0x00, 0x03, 0x01, 0xE0, 0x78, 0x3C, 0x0E,
0x00, 0x01, 0xFC, 0xFF, 0x7F, 0xDF, 0x67, 0x81, 0xE0, 0x78, 0x1F, 0x37, 0xFC, 0xFF, 0x1F, 0xC0, 0x03, 0x00, 0x3C,
0x03, 0xF0, 0x1F, 0xC0, 0xCC, 0x00, 0x00, 0x7F, 0xC7, 0xFE, 0x7F, 0xF3, 0xF7, 0xBE, 0x01, 0xE0, 0x0F, 0x00, 0x78,
0x03, 0xC0, 0x1E, 0x00, 0xF8, 0x07, 0xF3, 0x9F, 0xFC, 0x7F, 0xE1, 0xFF, 0x00, 0x06, 0x03, 0xC1, 0xF8, 0x7F, 0x19,
0x80, 0x01, 0xFC, 0xFF, 0x7F, 0xDF, 0x67, 0x81, 0xE0, 0x78, 0x1F, 0x37, 0xFC, 0xFF, 0x1F, 0xC0, 0x07, 0x80, 0x3C,
0x01, 0xE0, 0x00, 0x01, 0xFF, 0x1F, 0xF9, 0xFF, 0xCF, 0xDE, 0xF8, 0x07, 0x80, 0x3C, 0x01, 0xE0, 0x0F, 0x00, 0x78,
0x03, 0xE0, 0x1F, 0xCE, 0x7F, 0xF1, 0xFF, 0x87, 0xFC, 0x0F, 0x03, 0xC0, 0xF0, 0x00, 0x00, 0x07, 0xF3, 0xFD, 0xFF,
0x7D, 0x9E, 0x07, 0x81, 0xE0, 0x7C, 0xDF, 0xF3, 0xFC, 0x7F, 0x0C, 0xC0, 0x7F, 0x03, 0xF0, 0x0F, 0x00, 0x30, 0x00,
0x00, 0x7F, 0xC7, 0xFE, 0x7F, 0xF3, 0xF7, 0xBE, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x03, 0xC0, 0x1E, 0x00, 0xF8, 0x07,
0xF3, 0x9F, 0xFC, 0x7F, 0xE1, 0xFF, 0x00, 0x19, 0x87, 0xF1, 0xF8, 0x3C, 0x06, 0x00, 0x01, 0xFC, 0xFF, 0x7F, 0xDF,
0x67, 0x81, 0xE0, 0x78, 0x1F, 0x37, 0xFC, 0xFF, 0x1F, 0xC0, 0x1D, 0x80, 0x7F, 0x01, 0xFC, 0x03, 0xE0, 0x07, 0x00,
0x00, 0x0F, 0xFC, 0x3F, 0xFC, 0xFF, 0xF3, 0xCF, 0xEF, 0x07, 0xBC, 0x1F, 0xF0, 0x3F, 0xC0, 0xFF, 0x03, 0xFC, 0x1F,
0xF0, 0x7B, 0xCF, 0xEF, 0xFF, 0x3F, 0xF8, 0xFF, 0xC0, 0x00, 0xFF, 0x00, 0xFE, 0x00, 0xFE, 0x00, 0xFE, 0x00, 0xFE,
0x1F, 0xF0, 0x3F, 0xF0, 0x7F, 0xF0, 0x7D, 0xF0, 0x78, 0xF0, 0x78, 0xF0, 0x78, 0xF0, 0x7C, 0xF0, 0x7F, 0xF0, 0x3F,
0xF0, 0x1F, 0xF0, 0x3F, 0xE0, 0xFF, 0xF1, 0xFF, 0xF3, 0xEF, 0xE7, 0xC3, 0xEF, 0x87, 0xFF, 0xC7, 0xFF, 0x8F, 0x7C,
0x1E, 0xF8, 0x7D, 0xF0, 0xFB, 0xEF, 0xE7, 0xFF, 0xCF, 0xFF, 0x0F, 0xF8, 0x00, 0x00, 0xF0, 0x3F, 0xC1, 0xFE, 0x01,
0xE0, 0x0F, 0x0F, 0xF8, 0xFF, 0xCF, 0xFE, 0x7D, 0xF3, 0xC7, 0x9E, 0x3C, 0xF1, 0xE7, 0xCF, 0x3F, 0xF8, 0xFF, 0xC3,
0xFE, 0x3F, 0x87, 0xF0, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFC, 0x07, 0x80, 0xF0, 0x1F, 0xFB, 0xFF, 0x7F, 0xEF, 0x01,
0xE0, 0x3C, 0x07, 0xFF, 0xFF, 0xFF, 0xFC, 0x3F, 0xC3, 0xFC, 0x00, 0x00, 0x00, 0x1F, 0xC3, 0xFE, 0x7F, 0xE7, 0xDF,
0x7F, 0xF7, 0xFF, 0x78, 0x07, 0xEE, 0x7F, 0xE3, 0xFE, 0x1F, 0xE0, 0x3F, 0xC7, 0xF0, 0x7E, 0x00, 0x0F, 0xFF, 0xFF,
0xFF, 0xFF, 0x80, 0xF0, 0x1E, 0x03, 0xFF, 0x7F, 0xEF, 0xFD, 0xE0, 0x3C, 0x07, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0x80,
0x3F, 0xC1, 0xFC, 0x1F, 0x80, 0x00, 0x00, 0x01, 0xFC, 0x3F, 0xE7, 0xFE, 0x7D, 0xF7, 0xFF, 0x7F, 0xF7, 0x80, 0x7E,
0xE7, 0xFE, 0x3F, 0xE1, 0xFE, 0x0F, 0x01, 0xE0, 0x3C, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0xF0, 0x1E, 0x03,
0xFF, 0x7F, 0xEF, 0xFD, 0xE0, 0x3C, 0x07, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x0F, 0x00, 0xF0, 0x0F, 0x00, 0x00,
0x00, 0x01, 0xFC, 0x3F, 0xE7, 0xFE, 0x7D, 0xF7, 0xFF, 0x7F, 0xF7, 0x80, 0x7E, 0xE7, 0xFE, 0x3F, 0xE1, 0xFE, 0xFF,
0xEF, 0xFE, 0xFF, 0xEF, 0x00, 0xF0, 0x0F, 0x00, 0xFF, 0xCF, 0xFC, 0xFF, 0xCF, 0x00, 0xF0, 0x0F, 0x00, 0xFF, 0xEF,
0xFE, 0xFF, 0xE0, 0x1E, 0x01, 0xC0, 0x3C, 0x03, 0xF0, 0x1F, 0x1F, 0xC3, 0xFE, 0x7F, 0xE7, 0xDF, 0x7F, 0xF7, 0xFF,
0x78, 0x07, 0xEE, 0x7F, 0xE3, 0xFE, 0x1F, 0xE0, 0x3C, 0x03, 0x80, 0x7E, 0x03, 0xE0, 0x19, 0x87, 0xF0, 0x7E, 0x07,
0x80, 0x60, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFE, 0x03, 0xC0, 0x78, 0x0F, 0xFD, 0xFF, 0xBF, 0xF7, 0x80, 0xF0, 0x1E,
0x03, 0xFF, 0xFF, 0xFF, 0xFE, 0x19, 0x81, 0xFC, 0x1F, 0x80, 0xF0, 0x06, 0x00, 0x00, 0x1F, 0xC3, 0xFE, 0x7F, 0xE7,
0xDF, 0x7F, 0xF7, 0xFF, 0x78, 0x07, 0xEE, 0x7F, 0xE3, 0xFE, 0x1F, 0xE0, 0x03, 0x00, 0x3C, 0x03, 0xF0, 0x1F, 0xC0,
0xCC, 0x00, 0x00, 0x7F, 0xC7, 0xFE, 0x7F, 0xF3, 0xF3, 0xBE, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x3F, 0xC1, 0xFE, 0x0F,
0xF8, 0x7F, 0xF3, 0xDF, 0xFE, 0x7F, 0xF1, 0xFF, 0x80, 0x03, 0x00, 0xF0, 0x3F, 0x0F, 0xE0, 0xCC, 0x00, 0x07, 0xF9,
0xFF, 0x7F, 0xEF, 0xBD, 0xE7, 0xBC, 0xF7, 0xDE, 0xFF, 0xDF, 0xF9, 0xFF, 0x01, 0xE7, 0x7D, 0xFF, 0xBF, 0xE7, 0xF8,
0x1F, 0xE0, 0x7F, 0x03, 0xF0, 0x00, 0x01, 0xFF, 0x1F, 0xF9, 0xFF, 0xCF, 0xCE, 0xF8, 0x07, 0x80, 0x3C, 0x01, 0xE0,
0xFF, 0x07, 0xF8, 0x3F, 0xE1, 0xFF, 0xCF, 0x7F, 0xF9, 0xFF, 0xC7, 0xFE, 0x1F, 0xC3, 0xF8, 0x3E, 0x00, 0x00, 0x00,
0x3F, 0xCF, 0xFB, 0xFF, 0x7D, 0xEF, 0x3D, 0xE7, 0xBE, 0xF7, 0xFE, 0xFF, 0xCF, 0xF8, 0x0F, 0x3B, 0xEF, 0xFD, 0xFF,
0x3F, 0xC0, 0x07, 0x80, 0x3C, 0x01, 0xE0, 0x00, 0x01, 0xFF, 0x1F, 0xF9, 0xFF, 0xCF, 0xCE, 0xF8, 0x07, 0x80, 0x3C,
0x01, 0xE0, 0xFF, 0x07, 0xF8, 0x3F, 0xE1, 0xFF, 0xCF, 0x7F, 0xF9, 0xFF, 0xC7, 0xFE, 0x07, 0x01, 0xE0, 0x1C, 0x00,
0x00, 0x00, 0x3F, 0xCF, 0xFB, 0xFF, 0x7D, 0xEF, 0x3D, 0xE7, 0xBE, 0xF7, 0xFE, 0xFF, 0xCF, 0xF8, 0x0F, 0x3B, 0xEF,
0xFD, 0xFF, 0x3F, 0xC0, 0x1F, 0xF1, 0xFF, 0x9F, 0xFC, 0xFC, 0xEF, 0x80, 0x78, 0x03, 0xC0, 0x1E, 0x0F, 0xF0, 0x7F,
0x83, 0xFE, 0x1F, 0xFC, 0xF7, 0xFF, 0x9F, 0xFC, 0x7F, 0xE0, 0x00, 0x03, 0x80, 0x3C, 0x03, 0xE0, 0x1E, 0x00, 0x03,
0x80, 0xF0, 0x1E, 0x03, 0x80, 0x00, 0x00, 0x07, 0xF9, 0xFF, 0x7F, 0xEF, 0xBD, 0xE7, 0xBC, 0xF7, 0xDE, 0xFF, 0xDF,
0xF9, 0xFF, 0x01, 0xE7, 0x7D, 0xFF, 0xBF, 0xE7, 0xF8, 0x03, 0x00, 0x3C, 0x03, 0xF0, 0x3F, 0x80, 0xCC, 0x00, 0x03,
0xC1, 0xFE, 0x0F, 0xF0, 0x7F, 0x83, 0xFC, 0x1F, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x0F, 0xF0, 0x7F, 0x83,
0xFC, 0x1F, 0xE0, 0xFF, 0x07, 0x80, 0x38, 0x3F, 0x8F, 0xE0, 0x00, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x3F, 0xEF,
0xFF, 0xFF, 0xF7, 0xFC, 0xFF, 0x3F, 0xCF, 0xF3, 0xFC, 0xFF, 0x3F, 0xCF, 0x7C, 0x3C, 0x7C, 0x3C, 0xFF, 0xFF, 0xFF,
0xFF, 0x7C, 0x3C, 0x7C, 0x3C, 0x7F, 0xFC, 0x7F, 0xFC, 0x7F, 0xFC, 0x7C, 0x3C, 0x7C, 0x3C, 0x7C, 0x3C, 0x7C, 0x3C,
0x7C, 0x3C, 0x7C, 0x3C, 0x78, 0x07, 0x80, 0xFF, 0x0F, 0xF0, 0x78, 0x07, 0xFC, 0x7F, 0xE7, 0xFE, 0x7D, 0xE7, 0x8F,
0x78, 0xF7, 0x8F, 0x78, 0xF7, 0x8F, 0x78, 0xF7, 0x8F, 0x7B, 0x7F, 0xBF, 0xC0, 0x03, 0xC1, 0xE0, 0xF0, 0x78, 0x3C,
0x1E, 0x0F, 0x07, 0x83, 0xC1, 0xE0, 0xF0, 0x78, 0x3C, 0x1E, 0x0F, 0x00, 0x7F, 0xFF, 0xFE, 0x00, 0x00, 0x3C, 0x3C,
0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x7F, 0x7F, 0x00, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C,
0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0xFF, 0xFF, 0x00, 0x00, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C,
0x3C, 0x3C, 0x3C, 0x3C, 0xEF, 0xFD, 0xF0, 0x07, 0x8F, 0x1E, 0x3C, 0x78, 0xF1, 0xE3, 0xC7, 0x8F, 0x1E, 0x3C, 0x78,
0xF1, 0xE0, 0xFF, 0x7F, 0x7E, 0x00, 0x00, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x79,
0xE7, 0x9E, 0x79, 0xE7, 0x9E, 0x79, 0xE7, 0x9E, 0x79, 0xE7, 0x9E, 0x73, 0xCF, 0xDF, 0x79, 0xE7, 0x9E, 0x01, 0xE7,
0x9E, 0x79, 0xE7, 0x9E, 0x79, 0xE7, 0x9E, 0x7B, 0xCF, 0xBE, 0xF8, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xF0, 0x1F, 0xF0, 0x1F, 0xF0, 0x1F, 0xF0, 0x1F, 0xF0, 0x1F, 0xF0,
0x1F, 0xF0, 0x1F, 0xF0, 0x1F, 0xF0, 0x1F, 0xF0, 0x1F, 0xF0, 0x1F, 0xF3, 0xBE, 0xF3, 0xFE, 0xF7, 0xFE, 0xF3, 0xFC,
0xF3, 0xFC, 0xFF, 0x3F, 0xCF, 0x00, 0x3C, 0xFF, 0x3F, 0xCF, 0xF3, 0xFC, 0xFF, 0x3F, 0xCF, 0xF3, 0xFC, 0xFF, 0x3F,
0xCF, 0x07, 0xC7, 0xF1, 0xF8, 0x7E, 0x01, 0x80, 0x3C, 0x07, 0xE0, 0xFE, 0x06, 0x60, 0x00, 0x03, 0xC0, 0x3C, 0x03,
0xC0, 0x3C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, 0x3C, 0x03, 0xC0, 0x3C, 0x03, 0xC6, 0x7C, 0xFF, 0xCF, 0xF8, 0xFF, 0x00,
0x0C, 0x0F, 0x0F, 0xCF, 0xF3, 0x30, 0x00, 0x78, 0x3C, 0x1E, 0x0F, 0x07, 0x83, 0xC1, 0xE0, 0xF0, 0x78, 0x3C, 0x1E,
0x1F, 0x3F, 0x9F, 0x8F, 0xC0, 0xF0, 0xFB, 0xC7, 0xCF, 0x3E, 0x3D, 0xF0, 0xFF, 0x83, 0xFC, 0x0F, 0xE0, 0x3F, 0xC0,
0xFF, 0x83, 0xDF, 0x0F, 0x7C, 0x3C, 0xF8, 0xF1, 0xF3, 0xC3, 0xEF, 0x0F, 0x80, 0x00, 0x07, 0x00, 0x3C, 0x00, 0xF0,
0x03, 0xC0, 0xF0, 0x1E, 0x03, 0xC0, 0x78, 0x0F, 0x01, 0xE7, 0xFD, 0xF7, 0xFC, 0xFF, 0x1F, 0xC3, 0xFC, 0x7F, 0xCF,
0x79, 0xEF, 0xBC, 0xFF, 0x8F, 0x00, 0x01, 0xC0, 0x78, 0x1F, 0x03, 0xC0, 0xF3, 0xEF, 0x7C, 0xFF, 0x8F, 0xF0, 0xFE,
0x0F, 0xF0, 0xFF, 0x0F, 0xF8, 0xF7, 0xCF, 0x3E, 0xF1, 0xE0, 0x18, 0x07, 0x81, 0xF0, 0x78, 0x0E, 0x00, 0x00, 0x3C,
0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xFF, 0xFF, 0xFF,
0xFE, 0x19, 0xEF, 0xF8, 0x03, 0xCF, 0x3C, 0xF3, 0xCF, 0x3C, 0xF3, 0xCF, 0x3C, 0xF3, 0xEF, 0xFF, 0x78, 0xF0, 0x1E,
0x03, 0xC0, 0x78, 0x0F, 0x01, 0xE0, 0x3C, 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0, 0x78, 0x0F, 0xFF, 0xFF, 0xFF, 0xF8,
0x00, 0x0F, 0x01, 0xE0, 0x78, 0x0F, 0x00, 0xF3, 0xCF, 0x3C, 0xF3, 0xCF, 0x3C, 0xF3, 0xCF, 0x3C, 0xFB, 0xFF, 0xDE,
0x01, 0xCF, 0x3C, 0xF0, 0xF0, 0x1E, 0x73, 0xCE, 0x79, 0xCF, 0x39, 0xE6, 0x3C, 0x07, 0x80, 0xF0, 0x1E, 0x03, 0xC0,
0x78, 0x0F, 0xFF, 0xFF, 0xFF, 0xF8, 0xF7, 0xF7, 0xF7, 0xF7, 0xF6, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF8,
0xFC, 0xFC, 0x78, 0xF0, 0x1E, 0x03, 0xC0, 0x78, 0x0F, 0x3D, 0xE7, 0xBC, 0xF7, 0x9E, 0xF0, 0x1E, 0x03, 0xC0, 0x78,
0x0F, 0xFF, 0xFF, 0xFF, 0xF8, 0xF0, 0x3C, 0x0F, 0x03, 0xC0, 0xF0, 0x3D, 0xEF, 0x7F, 0xDF, 0xF7, 0xBC, 0x0F, 0x03,
0xC0, 0xF8, 0x3F, 0x0F, 0xC1, 0xE0, 0x3C, 0x01, 0xE0, 0x0F, 0x00, 0x78, 0x03, 0xF8, 0x1F, 0xC0, 0xFE, 0x1F, 0xC0,
0xFC, 0x07, 0xE0, 0x0F, 0x00, 0x78, 0x03, 0xFF, 0x1F, 0xF8, 0xFF, 0xC0, 0x1E, 0x0F, 0x07, 0x83, 0xC1, 0xF0, 0xFC,
0xFE, 0xFC, 0x7E, 0x2F, 0x07, 0x83, 0xC1, 0xF0, 0xF8, 0x7C, 0x1E, 0x01, 0x80, 0x0F, 0x00, 0x7C, 0x03, 0xE0, 0x06,
0x00, 0x00, 0x0F, 0x07, 0xFE, 0x1F, 0xFC, 0x7F, 0xF9, 0xFF, 0xE7, 0xFF, 0xDF, 0xFF, 0xFF, 0xDF, 0xFF, 0x3F, 0xFC,
0x7F, 0xF1, 0xFF, 0xC3, 0xFF, 0x0F, 0xFC, 0x1F, 0xF0, 0x3C, 0x06, 0x03, 0xC1, 0xF0, 0x78, 0x18, 0x00, 0x0F, 0xFB,
0xFF, 0xFF, 0xFD, 0xFF, 0x3F, 0xCF, 0xF3, 0xFC, 0xFF, 0x3F, 0xCF, 0xF3, 0xC0, 0xF0, 0x7F, 0xE1, 0xFF, 0xC7, 0xFF,
0x9F, 0xFE, 0x7F, 0xFD, 0xFF, 0xFF, 0xFD, 0xFF, 0xF3, 0xFF, 0xC7, 0xFF, 0x1F, 0xFC, 0x3F, 0xF0, 0xFF, 0xC1, 0xFF,
0x03, 0xC0, 0x00, 0x03, 0x80, 0x1E, 0x00, 0xF8, 0x03, 0xC0, 0xFF, 0xBF, 0xFF, 0xFF, 0xDF, 0xF3, 0xFC, 0xFF, 0x3F,
0xCF, 0xF3, 0xFC, 0xFF, 0x3C, 0x00, 0x0E, 0x07, 0x83, 0xE0, 0xF0, 0x0C, 0xC0, 0x7F, 0x00, 0xFC, 0x01, 0xE0, 0x03,
0x00, 0x00, 0x0F, 0x07, 0xFE, 0x1F, 0xFC, 0x7F, 0xF9, 0xFF, 0xE7, 0xFF, 0xDF, 0xFF, 0xFF, 0xDF, 0xFF, 0x3F, 0xFC,
0x7F, 0xF1, 0xFF, 0xC3, 0xFF, 0x0F, 0xFC, 0x1F, 0xF0, 0x3C, 0x33, 0x0F, 0xE3, 0xF0, 0x78, 0x0C, 0x00, 0x0F, 0xFB,
0xFF, 0xFF, 0xFD, 0xFF, 0x3F, 0xCF, 0xF3, 0xFC, 0xFF, 0x3F, 0xCF, 0xF3, 0xC0, 0x70, 0x03, 0x80, 0x1C, 0x01, 0xE0,
0x0E, 0x00, 0x0F, 0xF0, 0x7F, 0xC3, 0xFF, 0x1E, 0xF8, 0xF3, 0xC7, 0x9E, 0x3C, 0xF1, 0xE7, 0x8F, 0x3C, 0x79, 0xE3,
0xCF, 0xF0, 0x7F, 0xE1, 0xFF, 0xC7, 0xFF, 0x9F, 0xFE, 0x7F, 0xFD, 0xFF, 0xFF, 0xFD, 0xFF, 0xF3, 0xFF, 0xC7, 0xFF,
0x1F, 0xFC, 0x3F, 0xF0, 0x7F, 0xC1, 0xFF, 0x07, 0xC0, 0x1F, 0x01, 0xF8, 0x07, 0xE0, 0x1F, 0x00, 0xFF, 0xBF, 0xFF,
0xFF, 0xDF, 0xF3, 0xFC, 0xFF, 0x3F, 0xCF, 0xF3, 0xFC, 0xFF, 0x3C, 0x0F, 0x03, 0xC3, 0xF0, 0xF8, 0x1F, 0xE0, 0x3F,
0xC0, 0x00, 0x00, 0xFF, 0x03, 0xFF, 0x8F, 0xFF, 0x9F, 0xBF, 0x7C, 0x1E, 0xF0, 0x3F, 0xE0, 0x3F, 0xC0, 0x7F, 0x80,
0xFF, 0x03, 0xFF, 0x07, 0xFF, 0xBF, 0x3F, 0xFE, 0x3F, 0xF8, 0x3F, 0xC0, 0x1F, 0xC1, 0xFC, 0x00, 0x00, 0x00, 0x1F,
0xC3, 0xFE, 0x7F, 0xF7, 0xDF, 0x78, 0xF7, 0x8F, 0x78, 0xF7, 0xDF, 0x7F, 0xF3, 0xFE, 0x1F, 0xC0, 0x1F, 0xE0, 0x1F,
0xC0, 0x3F, 0x00, 0x00, 0x01, 0xFE, 0x07, 0xFF, 0x1F, 0xFF, 0x3F, 0x7E, 0xF8, 0x3D, 0xE0, 0x7F, 0xC0, 0x7F, 0x80,
0xFF, 0x01, 0xFE, 0x07, 0xFE, 0x0F, 0xFF, 0x7E, 0x7F, 0xFC, 0x7F, 0xF0, 0x7F, 0x80, 0x1D, 0xC1, 0xFC, 0x0F, 0x80,
0x00, 0x00, 0x01, 0xFC, 0x3F, 0xE7, 0xFF, 0x7D, 0xF7, 0x8F, 0x78, 0xF7, 0x8F, 0x7D, 0xF7, 0xFF, 0x3F, 0xE1, 0xFC,
0x03, 0x30, 0x0E, 0xF0, 0x3D, 0xC0, 0x77, 0x00, 0xCC, 0x00, 0x00, 0x07, 0xF8, 0x1F, 0xFC, 0x7F, 0xFC, 0xFD, 0xFB,
0xE0, 0xF7, 0x81, 0xFF, 0x01, 0xFE, 0x03, 0xFC, 0x07, 0xF8, 0x1F, 0xF8, 0x3F, 0xFD, 0xF9, 0xFF, 0xF1, 0xFF, 0xC1,
0xFE, 0x00, 0x0E, 0x60, 0xEF, 0x1F, 0xE1, 0xDC, 0x19, 0x80, 0x00, 0x1F, 0xC3, 0xFE, 0x7F, 0xF7, 0xDF, 0x78, 0xF7,
0x8F, 0x78, 0xF7, 0xDF, 0x7F, 0xF3, 0xFE, 0x1F, 0xC0, 0x0F, 0xFF, 0xE3, 0xFF, 0xFE, 0x7F, 0xFF, 0xEF, 0xE7, 0x80,
0xF8, 0x78, 0x0F, 0x07, 0x80, 0xF0, 0x7F, 0xCF, 0x07, 0xFC, 0xF0, 0x7F, 0xCF, 0x07, 0x80, 0xF8, 0x78, 0x0F, 0xE7,
0x80, 0x7F, 0xFF, 0xF3, 0xFF, 0xFF, 0x0F, 0xFF, 0xF0, 0x1F, 0x9F, 0x87, 0xFF, 0xF9, 0xFF, 0xFF, 0xBE, 0xFC, 0xF7,
0x8F, 0xFE, 0xF1, 0xFF, 0xDE, 0x3C, 0x03, 0xEF, 0xEE, 0x7F, 0xFF, 0xC7, 0xFF, 0xF8, 0x7E, 0x7F, 0x00, 0x03, 0x00,
0x3C, 0x03, 0xE0, 0x3C, 0x01, 0xC0, 0x00, 0x03, 0xFF, 0x1F, 0xFC, 0xFF, 0xF7, 0x9F, 0xBC, 0x3D, 0xE1, 0xEF, 0x3F,
0x7F, 0xFB, 0xFF, 0x9F, 0xF8, 0xF7, 0xC7, 0x9F, 0x3C, 0x7D, 0xE3, 0xEF, 0x0F, 0x80, 0x0C, 0x1E, 0x3E, 0x3C, 0x30,
0x00, 0xFF, 0xFF, 0xFF, 0xF2, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xFF, 0xC7, 0xFF, 0x3F, 0xFD, 0xE7, 0xEF,
0x0F, 0x78, 0x7B, 0xCF, 0xDF, 0xFE, 0xFF, 0xE7, 0xFE, 0x3D, 0xF1, 0xE7, 0xCF, 0x1F, 0x78, 0xFB, 0xC3, 0xE0, 0x00,
0x07, 0x00, 0x78, 0x07, 0xC0, 0x3C, 0x00, 0x7F, 0xBF, 0xDF, 0xEF, 0x27, 0x83, 0xC1, 0xE0, 0xF0, 0x78, 0x3C, 0x1E,
0x00, 0x07, 0x83, 0xC3, 0xC1, 0xE0, 0x19, 0x81, 0xFC, 0x07, 0xE0, 0x1E, 0x00, 0x60, 0x7F, 0xE3, 0xFF, 0x9F, 0xFE,
0xF3, 0xF7, 0x87, 0xBC, 0x3D, 0xE7, 0xEF, 0xFF, 0x7F, 0xF3, 0xFF, 0x1E, 0xF8, 0xF3, 0xE7, 0x8F, 0xBC, 0x7D, 0xE1,
0xF0, 0x66, 0x7F, 0x7E, 0x3C, 0x18, 0x00, 0xFF, 0xFF, 0xFF, 0xF2, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0x03,
0x00, 0x38, 0x07, 0x80, 0xF0, 0x0E, 0x00, 0x00, 0x1F, 0xE3, 0xFE, 0x7F, 0xE7, 0xDC, 0x78, 0x07, 0xE0, 0x7F, 0x83,
0xFE, 0x0F, 0xE0, 0x3F, 0x01, 0xF7, 0xBF, 0x7F, 0xFF, 0xFE, 0x7F, 0xC0, 0x06, 0x03, 0xC0, 0xF0, 0x78, 0x1C, 0x00,
0x03, 0xF9, 0xFE, 0x7F, 0x9E, 0xE7, 0xF9, 0xFE, 0x3F, 0xDD, 0xF7, 0xFD, 0xFE, 0x7F, 0x00, 0x06, 0x00, 0xF0, 0x1F,
0x81, 0xFC, 0x19, 0x80, 0x00, 0x1F, 0xE3, 0xFE, 0x7F, 0xE7, 0xDC, 0x78, 0x07, 0xE0, 0x7F, 0x83, 0xFE, 0x0F, 0xE0,
0x3F, 0x01, 0xF7, 0xBF, 0x7F, 0xFF, 0xFE, 0x7F, 0xC0, 0x0E, 0x07, 0x83, 0xF0, 0xFE, 0x33, 0x00, 0x03, 0xF9, 0xFE,
0x7F, 0x9E, 0xE7, 0xF9, 0xFE, 0x3F, 0xDD, 0xF7, 0xFD, 0xFE, 0x7F, 0x00, 0x1F, 0xE3, 0xFE, 0x7F, 0xE7, 0xDC, 0x78,
0x07, 0xE0, 0x7F, 0x83, 0xFE, 0x0F, 0xE0, 0x3F, 0x01, 0xF7, 0xBF, 0x7F, 0xFF, 0xFE, 0x7F, 0xC0, 0xE0, 0x0F, 0x01,
0x70, 0x1F, 0x01, 0xF0, 0x3F, 0x9F, 0xE7, 0xF9, 0xEE, 0x7F, 0x9F, 0xE3, 0xFD, 0xDF, 0x7F, 0xDF, 0xEF, 0xF0, 0x70,
0x1E, 0x0B, 0x83, 0xE0, 0xF8, 0x19, 0x81, 0xFC, 0x1F, 0x80, 0xF8, 0x07, 0x00, 0x00, 0x1F, 0xE3, 0xFE, 0x7F, 0xE7,
0xDC, 0x78, 0x07, 0xE0, 0x7F, 0x83, 0xFE, 0x0F, 0xE0, 0x3F, 0x01, 0xF7, 0xBF, 0x7F, 0xFF, 0xFE, 0x7F, 0xC0, 0x3B,
0x0F, 0xE3, 0xF8, 0x7C, 0x0E, 0x00, 0x03, 0xF9, 0xFE, 0x7F, 0x9E, 0xE7, 0xF9, 0xFE, 0x3F, 0xDD, 0xF7, 0xFD, 0xFE,
0x7F, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x1F, 0x00, 0xF8, 0x07, 0xC0, 0x3E, 0x01, 0xF0, 0x0F, 0x80, 0x7C, 0x03,
0xE0, 0x1F, 0x00, 0xF8, 0x07, 0xC0, 0x3E, 0x00, 0xE0, 0x0F, 0x00, 0xFC, 0x07, 0xC0, 0xF0, 0xF0, 0xF0, 0xFF, 0xFF,
0xFF, 0xF0, 0xF0, 0xF0, 0xF0, 0xFB, 0xFF, 0x7F, 0x7F, 0x1C, 0x3E, 0x3E, 0x7C, 0x1D, 0x80, 0xFE, 0x07, 0xF0, 0x1F,
0x00, 0x70, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x7C, 0x03, 0xE0, 0x1F, 0x00, 0xF8, 0x07, 0xC0, 0x3E, 0x01,
0xF0, 0x0F, 0x80, 0x7C, 0x03, 0xE0, 0x1F, 0x00, 0xF8, 0x00, 0x0F, 0x0E, 0xFE, 0xFE, 0xF0, 0xFF, 0xFF, 0xFF, 0xF0,
0xF0, 0xF0, 0xF0, 0xFB, 0xFF, 0x7F, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x1F, 0x00, 0xF8, 0x07, 0xC0, 0x3E, 0x0F,
0xFC, 0x7F, 0xE0, 0x7C, 0x03, 0xE0, 0x1F, 0x00, 0xF8, 0x07, 0xC0, 0x3E, 0x00, 0xF0, 0xF0, 0xF0, 0xFF, 0xFF, 0xFF,
0xF0, 0xFF, 0xFF, 0xF0, 0xFB, 0xFF, 0x7F, 0x7F, 0x1F, 0xC1, 0xFF, 0x07, 0xF0, 0x00, 0x0F, 0x07, 0xF8, 0x3F, 0xC1,
0xFE, 0x0F, 0xF0, 0x7F, 0x83, 0xFC, 0x1F, 0xE0, 0xFF, 0x07, 0xF8, 0x3F, 0xE3, 0xFF, 0xBE, 0x7F, 0xF3, 0xFF, 0x07,
0xF0, 0x3F, 0x9F, 0xE7, 0xF8, 0x00, 0x00, 0x3C, 0xFF, 0x3F, 0xCF, 0xF3, 0xFC, 0xFF, 0x3F, 0xCF, 0xFB, 0xFF, 0xF7,
0xFC, 0xFF, 0x1F, 0xC0, 0xFE, 0x00, 0x01, 0xE0, 0xFF, 0x07, 0xF8, 0x3F, 0xC1, 0xFE, 0x0F, 0xF0, 0x7F, 0x83, 0xFC,
0x1F, 0xE0, 0xFF, 0x07, 0xFC, 0x7F, 0xF7, 0xCF, 0xFE, 0x7F, 0xE0, 0xFE, 0x00, 0x7F, 0x9F, 0xE0, 0x00, 0x00, 0xF3,
0xFC, 0xFF, 0x3F, 0xCF, 0xF3, 0xFC, 0xFF, 0x3F, 0xEF, 0xFF, 0xDF, 0xF3, 0xFC, 0x1D, 0xC0, 0xFE, 0x03, 0xE0, 0x00,
0x0F, 0x07, 0xF8, 0x3F, 0xC1, 0xFE, 0x0F, 0xF0, 0x7F, 0x83, 0xFC, 0x1F, 0xE0, 0xFF, 0x07, 0xF8, 0x3F, 0xE3, 0xFF,
0xBE, 0x7F, 0xF3, 0xFF, 0x07, 0xF0, 0x7F, 0x8F, 0xE3, 0xF0, 0x00, 0x00, 0x3C, 0xFF, 0x3F, 0xCF, 0xF3, 0xFC, 0xFF,
0x3F, 0xCF, 0xFB, 0xFF, 0xF7, 0xFC, 0xFF, 0x0F, 0x00, 0x7C, 0x03, 0xE0, 0x1F, 0x00, 0xF8, 0x07, 0x83, 0xC1, 0xFE,
0x0F, 0xF0, 0x7F, 0x83, 0xFC, 0x1F, 0xE0, 0xFF, 0x07, 0xF8, 0x3F, 0xC1, 0xFE, 0x0F, 0xF8, 0xFF, 0xEF, 0x9F, 0xFC,
0xFF, 0xC1, 0xFC, 0x00, 0x1E, 0x07, 0xC3, 0xF0, 0xFC, 0x1F, 0x07, 0x80, 0x03, 0xCF, 0xF3, 0xFC, 0xFF, 0x3F, 0xCF,
0xF3, 0xFC, 0xFF, 0xBF, 0xFF, 0x7F, 0xCF, 0xF0, 0x06, 0x70, 0x7F, 0x87, 0xB8, 0x3B, 0x81, 0x9C, 0x00, 0x03, 0xC1,
0xFE, 0x0F, 0xF0, 0x7F, 0x83, 0xFC, 0x1F, 0xE0, 0xFF, 0x07, 0xF8, 0x3F, 0xC1, 0xFE, 0x0F, 0xF8, 0xFF, 0xEF, 0x9F,
0xFC, 0xFF, 0xC1, 0xFC, 0x00, 0x1D, 0xC3, 0xB8, 0xEF, 0x3D, 0xC7, 0x30, 0x00, 0x3C, 0xF7, 0x9E, 0xF3, 0xDE, 0x7B,
0xCF, 0x79, 0xEF, 0x3D, 0xF7, 0xBF, 0xF3, 0xFE, 0x3F, 0xC0, 0xF0, 0x7F, 0x83, 0xFC, 0x1F, 0xE0, 0xFF, 0x07, 0xF8,
0x3F, 0xC1, 0xFE, 0x0F, 0xF0, 0x7F, 0x83, 0xFE, 0x3F, 0xFB, 0xE7, 0xFF, 0x3F, 0xF0, 0x7F, 0x00, 0xF0, 0x07, 0x00,
0x7E, 0x01, 0xF0, 0xF3, 0xFC, 0xFF, 0x3F, 0xCF, 0xF3, 0xFC, 0xFF, 0x3F, 0xEF, 0xFF, 0xDF, 0xF3, 0xFC, 0x1E, 0x07,
0xC1, 0xF0, 0x7C, 0x00, 0x60, 0x00, 0x0F, 0x00, 0x01, 0xF8, 0x00, 0x3F, 0xC0, 0x01, 0x98, 0x00, 0x00, 0x00, 0xF0,
0x00, 0xFF, 0x80, 0x1F, 0x78, 0xF1, 0xE7, 0x8F, 0x1E, 0x78, 0xF9, 0xE7, 0x9F, 0x9E, 0x7D, 0xFB, 0xC3, 0xDF, 0xFC,
0x3F, 0xFF, 0xC3, 0xFB, 0xFC, 0x3F, 0x9F, 0x81, 0xF9, 0xF8, 0x1F, 0x1F, 0x81, 0xF0, 0xF8, 0x0F, 0x0F, 0x00, 0x01,
0x80, 0x01, 0xE0, 0x01, 0xF8, 0x00, 0xFE, 0x00, 0x66, 0x00, 0x00, 0x03, 0xC7, 0x3F, 0xE7, 0x9E, 0xFB, 0xCF, 0x3D,
0xF7, 0x9E, 0xFF, 0x8F, 0xFF, 0xC3, 0xFF, 0xE1, 0xFB, 0xE0, 0xFD, 0xF0, 0x3C, 0xF8, 0x1E, 0x38, 0x00, 0x03, 0x00,
0x1E, 0x00, 0xFC, 0x07, 0xF8, 0x0C, 0xC0, 0x00, 0x0F, 0x87, 0xFE, 0x1E, 0x7C, 0xF9, 0xF3, 0xC3, 0xFF, 0x07, 0xF8,
0x1F, 0xE0, 0x3F, 0x00, 0xFC, 0x01, 0xE0, 0x07, 0x80, 0x1E, 0x00, 0x78, 0x01, 0xE0, 0x07, 0x80, 0x06, 0x00, 0xF0,
0x1F, 0x83, 0xF8, 0x19, 0x80, 0x00, 0xF1, 0xFF, 0x1E, 0x79, 0xE7, 0x9E, 0x7B, 0xC3, 0xFC, 0x3F, 0xC3, 0xF8, 0x1F,
0x81, 0xF0, 0x0F, 0x0D, 0xF0, 0xFE, 0x0F, 0xC0, 0xF8, 0x00, 0x1C, 0xE0, 0x7F, 0x81, 0xCE, 0x00, 0x00, 0xF8, 0x7F,
0xE1, 0xE7, 0xCF, 0x9F, 0x3C, 0x3F, 0xF0, 0x7F, 0x81, 0xFE, 0x03, 0xF0, 0x0F, 0xC0, 0x1E, 0x00, 0x78, 0x01, 0xE0,
0x07, 0x80, 0x1E, 0x00, 0x78, 0x00, 0x03, 0x00, 0x1C, 0x01, 0xE0, 0x1E, 0x00, 0xE0, 0x00, 0x01, 0xFF, 0xCF, 0xFE,
0x7F, 0xF0, 0x0F, 0x80, 0xF8, 0x0F, 0x80, 0xF8, 0x07, 0x80, 0x7C, 0x07, 0xC0, 0x3C, 0x03, 0xE0, 0x3F, 0xFF, 0xFF,
0xFF, 0xFF, 0x80, 0x06, 0x01, 0xC0, 0xF0, 0x78, 0x1C, 0x00, 0x07, 0xFD, 0xFF, 0x7F, 0xC1, 0xE0, 0xF0, 0x78, 0x3C,
0x1F, 0x0F, 0xFF, 0xFF, 0xFF, 0xC0, 0x07, 0x00, 0x78, 0x01, 0xC0, 0x00, 0x07, 0xFF, 0x3F, 0xF9, 0xFF, 0xC0, 0x3E,
0x03, 0xE0, 0x3E, 0x03, 0xE0, 0x1E, 0x01, 0xF0, 0x1F, 0x00, 0xF0, 0x0F, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x1E,
0x07, 0x81, 0xE0, 0x00, 0x00, 0x1F, 0xF7, 0xFD, 0xFF, 0x07, 0x83, 0xC1, 0xE0, 0xF0, 0x7C, 0x3F, 0xFF, 0xFF, 0xFF,
0x0D, 0xC0, 0xFE, 0x07, 0xF0, 0x1F, 0x00, 0x70, 0x00, 0x01, 0xFF, 0xCF, 0xFE, 0x7F, 0xF0, 0x0F, 0x80, 0xF8, 0x0F,
0x80, 0xF8, 0x07, 0x80, 0x7C, 0x07, 0xC0, 0x3C, 0x03, 0xE0, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x33, 0x0F, 0xE3,
0xF0, 0x7C, 0x0E, 0x00, 0x07, 0xFD, 0xFF, 0x7F, 0xC1, 0xE0, 0xF0, 0x78, 0x3C, 0x1F, 0x0F, 0xFF, 0xFF, 0xFF, 0xC0,
0x3F, 0x3F, 0xBF, 0xDF, 0x6F, 0x07, 0x83, 0xC1, 0xE0, 0xF0, 0x78, 0x3C, 0x1E, 0x0F, 0x07, 0x83, 0xC1, 0xE0, 0x7E,
0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFE, 0x3B, 0xDE, 0xEF, 0x78, 0x7B, 0xDF, 0xEF, 0x70, 0x7B, 0xDF, 0xEF, 0x70, 0x3B, 0xDE, 0xE7,
0xF9, 0xDE, 0xF7, 0xBD, 0xE0, 0x7F, 0xBF, 0xDD, 0xFE, 0xFF, 0x77, 0x38, 0x7F, 0xBF, 0xDD, 0xFE, 0xFF, 0x77, 0x38,
0x1E, 0x07, 0x81, 0xE0, 0x78, 0xFF, 0xFF, 0xFF, 0xFC, 0x78, 0x1E, 0x07, 0x81, 0xE0, 0x78, 0x1E, 0x03, 0x80, 0xE0,
0x38, 0x0E, 0x03, 0x80, 0x1E, 0x07, 0x81, 0xE0, 0x78, 0xFF, 0xFF, 0xFF, 0xFC, 0x78, 0x1E, 0x07, 0x8F, 0xFF, 0xFF,
0xFF, 0xC7, 0x80, 0xE0, 0x38, 0x0E, 0x03, 0x80, 0x7C, 0xFF, 0xFF, 0xF7, 0xEF, 0x80, 0xF0, 0xE1, 0xFF, 0x3E, 0x7F,
0xE7, 0xCF, 0xF8, 0x70, 0xF0, 0x3E, 0x1E, 0x00, 0x0F, 0xE3, 0xC0, 0x01, 0xDC, 0xF0, 0x00, 0x7B, 0xDE, 0x00, 0x0F,
0x7F, 0x80, 0x00, 0xEE, 0xF0, 0x00, 0x1F, 0xFC, 0x00, 0x01, 0xF7, 0xFE, 0x7E, 0x01, 0xFF, 0xCF, 0xE0, 0x3B, 0xFF,
0xFC, 0x0F, 0x73, 0xF3, 0x83, 0xCE, 0x7E, 0x70, 0x79, 0xFF, 0xFE, 0x1E, 0x3F, 0x9F, 0xC3, 0xC3, 0xF3, 0xF0, 0x00,
0x38, 0xF3, 0xE7, 0x9F, 0x1E, 0x3E, 0x3C, 0x38, 0x00, 0x00, 0xE3, 0xE3, 0xE3, 0xC7, 0xCF, 0x3E, 0xF8, 0xE0, 0x00,
0x01, 0xE0, 0x3C, 0x0F, 0x01, 0xE0, 0x78, 0x0F, 0x03, 0xC0, 0x78, 0x1E, 0x03, 0xC0, 0xF0, 0x1E, 0x07, 0x80, 0xF0,
0x3C, 0x00, 0x07, 0xF0, 0xFF, 0x1F, 0xF1, 0xF6, 0x3C, 0x07, 0xFE, 0x7F, 0xC3, 0xC0, 0x7F, 0xC7, 0xFC, 0x3C, 0x03,
0xF7, 0x1F, 0xF0, 0xFF, 0x07, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xF0, 0x0F, 0xC0, 0xFC, 0x1F, 0xC3, 0xF0, 0x3F,
0xC3, 0xFC, 0x1F, 0xC3, 0xF0, 0x3F, 0x03, 0xF0, 0x0F, 0x00, 0x3F, 0x87, 0xFC, 0x3F, 0xE3, 0xBE, 0xFF, 0xFF, 0xFF,
0x1F, 0x83, 0xF0, 0xFF, 0xFF, 0xFF, 0x78, 0x07, 0xDE, 0x7F, 0xE3, 0xFE, 0x1F, 0xE0, 0xFF, 0xFF, 0xFF, 0xC1, 0xF0,
0x07, 0xCF, 0xFF, 0x7F, 0xF8, 0x0F, 0x01, 0xF8, 0xFF, 0x87, 0xF8, 0x1F, 0x00, 0x7C, 0x01, 0xF0, 0x07, 0xC0, 0x1F,
0x00, 0x3F, 0x83, 0xFC, 0x3F, 0xE3, 0xBF, 0x00, 0xF1, 0xFF, 0x3F, 0xF7, 0xFF, 0x7D, 0xFF, 0x0F, 0xF0, 0xFF, 0xBE,
0x7F, 0xE7, 0xFC, 0x3F, 0x80, 0x07, 0x80, 0x0F, 0x80, 0x3F, 0x00, 0x7F, 0x01, 0xFE, 0x03, 0xDC, 0x07, 0xBC, 0x1E,
0x78, 0x3C, 0x78, 0x78, 0xF1, 0xE1, 0xE3, 0xC1, 0xE7, 0xFF, 0xDF, 0xFF, 0xBF, 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xE0, 0xFF, 0x07, 0xF8, 0x3F, 0xC1, 0xFE, 0x0F, 0xF0, 0x7F, 0x83, 0xFC, 0x1F, 0xE0, 0xFF, 0x07, 0xF8, 0x3F,
0xC1, 0xFE, 0x0F, 0xF0, 0x78, 0xFF, 0xFF, 0xFF, 0xFF, 0xBC, 0x07, 0xC0, 0x7C, 0x07, 0x80, 0xF8, 0x0F, 0x83, 0xE0,
0x78, 0x1E, 0x07, 0xC0, 0xF0, 0x3F, 0xFF, 0xFF, 0xFF, 0xE0, 0xFF, 0xFF, 0xFF, 0xFC, 0x01, 0xE0, 0x3C, 0x0F, 0x01,
0xE0, 0x78, 0x0F, 0x03, 0xC0, 0x78, 0x1E, 0x03, 0xC0, 0xF0, 0x1E, 0x07, 0x80, 0xF0, 0x3C, 0x00, 0x77, 0xFE, 0xE0,
0x00, 0x3C, 0x01, 0xE0, 0x07, 0x80, 0x1E, 0x00, 0x70, 0x03, 0xC0, 0x0F, 0x0F, 0x38, 0xFD, 0xE3, 0xF7, 0x87, 0xFC,
0x07, 0xF0, 0x0F, 0xC0, 0x3E, 0x00, 0x78, 0x00, 0x7D, 0xF3, 0xFF, 0xFF, 0xFF, 0xEE, 0x7F, 0xFF, 0xBF, 0xFD, 0xF7,
0xC0, 0x00, 0x07, 0xC7, 0xE7, 0xF3, 0xE1, 0xE0, 0xF0, 0x78, 0x3C, 0x1E, 0x0F, 0x07, 0x83, 0xC1, 0xE0, 0xF0, 0x78,
0x7C, 0x7E, 0x3F, 0x1F, 0x00, 0x00, 0x3C, 0x77, 0xFF, 0x7F, 0xEF, 0xFE, 0xE3, 0xC0, 0x00, 0x3C, 0x77, 0xFF, 0x7F,
0xEF, 0xFE, 0xE3, 0xC0, 0x03, 0x80, 0xE0, 0x7B, 0xFF, 0xFF, 0xFF, 0xF0, 0xE0, 0x70, 0xFF, 0xFF, 0xFF, 0xFD, 0xC0,
0x70, 0x1C, 0x00, 0x07, 0xDF, 0xFF, 0xFF, 0xFE, 0xF8, 0x3F, 0xEF, 0xFD, 0xFF, 0x07, 0xC0, 0x0F, 0xFF, 0xFF, 0xFF,
0xC0, 0xF8, 0x3F, 0xCF, 0xFD, 0xFF, 0x0F, 0xDF, 0xFF, 0xFF, 0xFE, 0xF8, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xC0,
};
static const EpdGlyph ubuntu_bold_10Glyphs[] = {
{0, 0, 0, 0, 0, 0, 0}, //
{0, 0, 0, 0, 0, 0, 0}, // 
{0, 0, 5, 0, 0, 0, 0}, //
{0, 0, 5, 0, 0, 0, 0}, //
{0, 0, 0, 0, 0, 0, 0}, // 
{0, 0, 5, 0, 0, 0, 0}, //
{4, 15, 6, 1, 15, 8, 0}, // !
{8, 6, 10, 1, 16, 6, 8}, // "
{13, 15, 15, 1, 15, 25, 14}, // #
{11, 19, 12, 0, 16, 27, 39}, // $
{19, 15, 19, 0, 15, 36, 66}, // %
{15, 15, 15, 0, 15, 29, 102}, // &
{4, 6, 5, 1, 16, 3, 131}, // '
{7, 20, 7, 1, 16, 18, 134}, // (
{6, 20, 7, 0, 16, 15, 152}, // )
{10, 8, 11, 0, 15, 10, 167}, // *
{10, 11, 12, 1, 12, 14, 177}, // +
{5, 6, 5, 0, 3, 4, 191}, // ,
{7, 3, 7, 0, 8, 3, 195}, // -
{5, 4, 5, 0, 4, 3, 198}, // .
{11, 20, 9, -1, 16, 28, 201}, // /
{12, 15, 12, 0, 15, 23, 229}, // 0
{8, 15, 12, 1, 15, 15, 252}, // 1
{11, 15, 12, 0, 15, 21, 267}, // 2
{11, 15, 12, 0, 15, 21, 288}, // 3
{12, 15, 12, 0, 15, 23, 309}, // 4
{11, 15, 12, 0, 15, 21, 332}, // 5
{12, 15, 12, 0, 15, 23, 353}, // 6
{12, 15, 12, 0, 15, 23, 376}, // 7
{12, 15, 12, 0, 15, 23, 399}, // 8
{12, 15, 12, 0, 15, 23, 422}, // 9
{5, 11, 5, 0, 11, 7, 445}, // :
{5, 14, 5, 0, 11, 9, 452}, // ;
{10, 11, 12, 1, 12, 14, 461}, // <
{10, 8, 12, 1, 10, 10, 475}, // =
{10, 11, 12, 1, 12, 14, 485}, // >
{10, 16, 10, 0, 16, 20, 499}, // ?
{19, 18, 20, 1, 15, 43, 519}, // @
{15, 15, 15, 0, 15, 29, 562}, // A
{13, 15, 14, 1, 15, 25, 591}, // B
{13, 15, 14, 1, 15, 25, 616}, // C
{14, 15, 15, 1, 15, 27, 641}, // D
{11, 15, 13, 1, 15, 21, 668}, // E
{11, 15, 12, 1, 15, 21, 689}, // F
{13, 15, 15, 1, 15, 25, 710}, // G
{13, 15, 15, 1, 15, 25, 735}, // H
{4, 15, 7, 1, 15, 8, 760}, // I
{10, 15, 11, 0, 15, 19, 768}, // J
{14, 15, 14, 1, 15, 27, 787}, // K
{11, 15, 12, 1, 15, 21, 814}, // L
{17, 15, 19, 1, 15, 32, 835}, // M
{14, 15, 16, 1, 15, 27, 867}, // N
{15, 15, 17, 1, 15, 29, 894}, // O
{12, 15, 14, 1, 15, 23, 923}, // P
{15, 18, 17, 1, 15, 34, 946}, // Q
{13, 15, 14, 1, 15, 25, 980}, // R
{12, 15, 12, 0, 15, 23, 1005}, // S
{13, 15, 13, 0, 15, 25, 1028}, // T
{13, 15, 15, 1, 15, 25, 1053}, // U
{15, 15, 15, 0, 15, 29, 1078}, // V
{20, 15, 20, 0, 15, 38, 1107}, // W
{14, 15, 14, 0, 15, 27, 1145}, // X
{14, 15, 14, 0, 15, 27, 1172}, // Y
{13, 15, 13, 0, 15, 25, 1199}, // Z
{7, 20, 8, 1, 16, 18, 1224}, // [
{11, 20, 9, -1, 16, 28, 1242}, // <backslash>
{6, 20, 8, 0, 16, 15, 1270}, // ]
{12, 9, 12, 0, 15, 14, 1285}, // ^
{11, 3, 11, 0, -1, 5, 1299}, // _
{6, 5, 6, 0, 17, 4, 1304}, // `
{11, 11, 12, 0, 11, 16, 1308}, // a
{11, 16, 13, 1, 16, 22, 1324}, // b
{10, 11, 11, 0, 11, 14, 1346}, // c
{12, 16, 13, 0, 16, 24, 1360}, // d
{12, 11, 12, 0, 11, 17, 1384}, // e
{9, 16, 9, 1, 16, 18, 1401}, // f
{11, 15, 12, 0, 11, 21, 1419}, // g
{10, 16, 12, 1, 16, 20, 1440}, // h
{4, 16, 6, 1, 16, 8, 1460}, // i
{7, 20, 6, -2, 16, 18, 1468}, // j
{11, 16, 12, 1, 16, 22, 1486}, // k
{6, 16, 7, 1, 16, 12, 1508}, // l
{16, 11, 18, 1, 11, 22, 1520}, // m
{10, 11, 12, 1, 11, 14, 1542}, // n
{12, 11, 13, 0, 11, 17, 1556}, // o
{11, 15, 13, 1, 11, 21, 1573}, // p
{12, 15, 13, 0, 11, 23, 1594}, // q
{8, 11, 9, 1, 11, 11, 1617}, // r
{10, 11, 10, 0, 11, 14, 1628}, // s
{8, 14, 9, 1, 14, 14, 1642}, // t
{10, 11, 12, 1, 11, 14, 1656}, // u
{12, 11, 12, 0, 11, 17, 1670}, // v
{17, 11, 16, 0, 11, 24, 1687}, // w
{12, 11, 12, 0, 11, 17, 1711}, // x
{12, 15, 11, 0, 11, 23, 1728}, // y
{10, 11, 11, 0, 11, 14, 1751}, // z
{8, 20, 8, 0, 16, 20, 1765}, // {
{4, 20, 7, 1, 16, 10, 1785}, // |
{8, 20, 8, 0, 16, 20, 1795}, // }
{12, 5, 12, 0, 9, 8, 1815}, // ~
{0, 0, 5, 0, 0, 0, 1823}, //  
{4, 15, 6, 1, 11, 8, 1823}, // ¡
{10, 16, 12, 0, 14, 20, 1831}, // ¢
{11, 15, 12, 0, 15, 21, 1851}, // £
{12, 11, 12, 0, 13, 17, 1872}, // ¤
{14, 15, 12, -1, 15, 27, 1889}, // ¥
{4, 20, 6, 1, 16, 10, 1916}, // ¦
{11, 19, 11, 0, 15, 27, 1926}, // §
{9, 3, 11, 1, 16, 4, 1953}, // ¨
{15, 15, 17, 1, 15, 29, 1957}, // ©
{8, 8, 8, 0, 15, 8, 1986}, // ª
{12, 11, 13, 0, 11, 17, 1994}, // «
{10, 8, 12, 1, 9, 10, 2011}, // ¬
{7, 3, 7, 0, 8, 3, 2021}, // ­
{15, 15, 17, 1, 15, 29, 2024}, // ®
{8, 2, 8, 0, 15, 2, 2053}, // ¯
{8, 6, 8, 0, 16, 6, 2055}, // °
{10, 15, 12, 1, 15, 19, 2061}, // ±
{7, 9, 8, 0, 15, 8, 2080}, // ²
{7, 9, 8, 0, 15, 8, 2088}, // ³
{6, 5, 6, 0, 17, 4, 2096}, // ´
{10, 15, 12, 1, 11, 19, 2100}, // µ
{14, 19, 15, 0, 15, 34, 2119}, // ¶
{5, 4, 5, 0, 9, 3, 2153}, // ·
{6, 6, 7, 0, 1, 5, 2156}, // ¸
{6, 9, 8, 0, 15, 7, 2161}, // ¹
{9, 8, 10, 0, 15, 9, 2168}, // º
{13, 11, 13, 0, 11, 18, 2177}, // »
{19, 15, 18, 0, 15, 36, 2195}, // ¼
{18, 15, 18, 0, 15, 34, 2231}, // ½
{19, 15, 18, 0, 15, 36, 2265}, // ¾
{10, 15, 10, 0, 11, 19, 2301}, // ¿
{15, 21, 15, 0, 21, 40, 2320}, // À
{15, 21, 15, 0, 21, 40, 2360}, // Á
{15, 21, 15, 0, 21, 40, 2400}, // Â
{15, 19, 15, 0, 19, 36, 2440}, // Ã
{15, 19, 15, 0, 19, 36, 2476}, // Ä
{15, 19, 15, 0, 19, 36, 2512}, // Å
{21, 15, 21, 0, 15, 40, 2548}, // Æ
{13, 20, 14, 1, 15, 33, 2588}, // Ç
{11, 21, 13, 1, 21, 29, 2621}, // È
{11, 21, 13, 1, 21, 29, 2650}, // É
{11, 21, 13, 1, 21, 29, 2679}, // Ê
{11, 19, 13, 1, 19, 27, 2708}, // Ë
{6, 21, 7, 0, 21, 16, 2735}, // Ì
{5, 21, 7, 1, 21, 14, 2751}, // Í
{7, 21, 7, 0, 21, 19, 2765}, // Î
{9, 19, 7, -1, 19, 22, 2784}, // Ï
{15, 15, 16, 0, 15, 29, 2806}, // Ð
{14, 19, 16, 1, 19, 34, 2835}, // Ñ
{15, 21, 17, 1, 21, 40, 2869}, // Ò
{15, 21, 17, 1, 21, 40, 2909}, // Ó
{15, 21, 17, 1, 21, 40, 2949}, // Ô
{15, 19, 17, 1, 19, 36, 2989}, // Õ
{15, 19, 17, 1, 19, 36, 3025}, // Ö
{10, 9, 12, 1, 11, 12, 3061}, // ×
{15, 17, 17, 1, 16, 32, 3073}, // Ø
{13, 21, 15, 1, 21, 35, 3105}, // Ù
{13, 21, 15, 1, 21, 35, 3140}, // Ú
{13, 21, 15, 1, 21, 35, 3175}, // Û
{13, 19, 15, 1, 19, 31, 3210}, // Ü
{14, 21, 14, 0, 21, 37, 3241}, // Ý
{12, 15, 14, 1, 15, 23, 3278}, // Þ
{13, 16, 14, 1, 16, 26, 3301}, // ß
{11, 17, 12, 0, 17, 24, 3327}, // à
{11, 17, 12, 0, 17, 24, 3351}, // á
{11, 17, 12, 0, 17, 24, 3375}, // â
{11, 16, 12, 0, 16, 22, 3399}, // ã
{11, 16, 12, 0, 16, 22, 3421}, // ä
{11, 18, 12, 0, 18, 25, 3443}, // å
{18, 11, 18, 0, 11, 25, 3468}, // æ
{10, 15, 11, 0, 11, 19, 3493}, // ç
{12, 17, 12, 0, 17, 26, 3512}, // è
{12, 17, 12, 0, 17, 26, 3538}, // é
{12, 17, 12, 0, 17, 26, 3564}, // ê
{12, 16, 12, 0, 16, 24, 3590}, // ë
{6, 17, 6, 0, 17, 13, 3614}, // ì
{6, 17, 6, 0, 17, 13, 3627}, // í
{8, 17, 6, -1, 17, 17, 3640}, // î
{9, 16, 6, -2, 16, 18, 3657}, // ï
{13, 16, 13, 0, 16, 26, 3675}, // ð
{10, 16, 12, 1, 16, 20, 3701}, // ñ
{12, 17, 13, 0, 17, 26, 3721}, // ò
{12, 17, 13, 0, 17, 26, 3747}, // ó
{12, 17, 13, 0, 17, 26, 3773}, // ô
{12, 16, 13, 0, 16, 24, 3799}, // õ
{12, 16, 13, 0, 16, 24, 3823}, // ö
{10, 13, 12, 1, 13, 17, 3847}, // ÷
{12, 13, 13, 0, 12, 20, 3864}, // ø
{10, 17, 12, 1, 17, 22, 3884}, // ù
{10, 17, 12, 1, 17, 22, 3906}, // ú
{10, 17, 12, 1, 17, 22, 3928}, // û
{10, 16, 12, 1, 16, 20, 3950}, // ü
{12, 21, 11, 0, 17, 32, 3970}, // ý
{11, 20, 13, 1, 16, 28, 4002}, // þ
{12, 20, 11, 0, 16, 30, 4030}, // ÿ
{15, 18, 15, 0, 18, 34, 4060}, // Ā
{11, 15, 12, 0, 15, 21, 4094}, // ā
{15, 19, 15, 0, 19, 36, 4115}, // Ă
{11, 16, 12, 0, 16, 22, 4151}, // ă
{16, 20, 15, 0, 15, 40, 4173}, // Ą
{11, 15, 12, 0, 11, 21, 4213}, // ą
{13, 21, 14, 1, 21, 35, 4234}, // Ć
{10, 17, 11, 0, 17, 22, 4269}, // ć
{13, 21, 14, 1, 21, 35, 4291}, // Ĉ
{10, 17, 11, 0, 17, 22, 4326}, // ĉ
{13, 19, 14, 1, 19, 31, 4348}, // Ċ
{10, 16, 11, 0, 16, 20, 4379}, // ċ
{13, 21, 14, 1, 21, 35, 4399}, // Č
{10, 17, 11, 0, 17, 22, 4434}, // č
{14, 21, 15, 1, 21, 37, 4456}, // Ď
{16, 16, 14, 0, 16, 32, 4493}, // ď
{15, 15, 16, 0, 15, 29, 4525}, // Đ
{13, 16, 13, 0, 16, 26, 4554}, // đ
{11, 18, 13, 1, 18, 25, 4580}, // Ē
{12, 15, 12, 0, 15, 23, 4605}, // ē
{11, 19, 13, 1, 19, 27, 4628}, // Ĕ
{12, 16, 12, 0, 16, 24, 4655}, // ĕ
{11, 19, 13, 1, 19, 27, 4679}, // Ė
{12, 16, 12, 0, 16, 24, 4706}, // ė
{12, 20, 13, 1, 15, 30, 4730}, // Ę
{12, 15, 12, 0, 11, 23, 4760}, // ę
{11, 21, 13, 1, 21, 29, 4783}, // Ě
{12, 17, 12, 0, 17, 26, 4812}, // ě
{13, 21, 15, 1, 21, 35, 4838}, // Ĝ
{11, 21, 12, 0, 17, 29, 4873}, // ĝ
{13, 19, 15, 1, 19, 31, 4902}, // Ğ
{11, 20, 12, 0, 16, 28, 4933}, // ğ
{13, 19, 15, 1, 19, 31, 4961}, // Ġ
{11, 20, 12, 0, 16, 28, 4992}, // ġ
{13, 20, 15, 1, 15, 33, 5020}, // Ģ
{11, 21, 12, 0, 17, 29, 5053}, // ģ
{13, 21, 15, 1, 21, 35, 5082}, // Ĥ
{10, 20, 12, 1, 20, 25, 5117}, // ĥ
{16, 15, 16, 0, 15, 30, 5142}, // Ħ
{12, 16, 13, 0, 16, 24, 5172}, // ħ
{9, 19, 7, -1, 19, 22, 5196}, // Ĩ
{8, 16, 6, -1, 16, 16, 5218}, // ĩ
{8, 18, 7, -1, 18, 18, 5234}, // Ī
{8, 15, 6, -1, 15, 15, 5252}, // ī
{7, 19, 7, 0, 19, 17, 5267}, // Ĭ
{8, 16, 6, -1, 16, 16, 5284}, // ĭ
{6, 20, 7, 0, 15, 15, 5300}, // Į
{6, 21, 6, 0, 16, 16, 5315}, // į
{4, 19, 7, 1, 19, 10, 5331}, // İ
{4, 11, 6, 1, 11, 6, 5341}, // ı
{16, 15, 18, 1, 15, 30, 5347}, // IJ
{10, 20, 12, 1, 16, 25, 5377}, // ij
{12, 21, 11, 0, 21, 32, 5402}, // Ĵ
{9, 21, 6, -2, 17, 24, 5434}, // ĵ
{14, 20, 14, 1, 15, 35, 5458}, // Ķ
{11, 21, 12, 1, 16, 29, 5493}, // ķ
{12, 11, 12, 1, 11, 17, 5522}, // ĸ
{11, 21, 12, 1, 21, 29, 5539}, // Ĺ
{6, 21, 7, 1, 21, 16, 5568}, // ĺ
{11, 20, 12, 1, 15, 28, 5584}, // Ļ
{6, 21, 7, 1, 16, 16, 5612}, // ļ
{11, 15, 12, 1, 15, 21, 5628}, // Ľ
{8, 16, 8, 1, 16, 16, 5649}, // ľ
{11, 15, 12, 1, 15, 21, 5665}, // Ŀ
{10, 16, 11, 1, 16, 20, 5686}, // ŀ
{13, 15, 12, 0, 15, 25, 5706}, // Ł
{9, 16, 8, -1, 16, 18, 5731}, // ł
{14, 21, 16, 1, 21, 37, 5749}, // Ń
{10, 17, 12, 1, 17, 22, 5786}, // ń
{14, 20, 16, 1, 15, 35, 5808}, // Ņ
{10, 16, 12, 1, 11, 20, 5843}, // ņ
{14, 21, 16, 1, 21, 37, 5863}, // Ň
{10, 17, 12, 1, 17, 22, 5900}, // ň
{13, 16, 13, -1, 16, 26, 5922}, // ʼn
{14, 19, 16, 1, 15, 34, 5948}, // Ŋ
{10, 15, 12, 1, 11, 19, 5982}, // ŋ
{15, 18, 17, 1, 18, 34, 6001}, // Ō
{12, 15, 13, 0, 15, 23, 6035}, // ō
{15, 19, 17, 1, 19, 36, 6058}, // Ŏ
{12, 16, 13, 0, 16, 24, 6094}, // ŏ
{15, 21, 17, 1, 21, 40, 6118}, // Ő
{12, 17, 13, 0, 17, 26, 6158}, // ő
{20, 15, 21, 1, 15, 38, 6184}, // Œ
{19, 11, 20, 0, 11, 27, 6222}, // œ
{13, 21, 14, 1, 21, 35, 6249}, // Ŕ
{8, 17, 9, 1, 17, 17, 6284}, // ŕ
{13, 20, 14, 1, 15, 33, 6301}, // Ŗ
{9, 16, 9, 0, 11, 18, 6334}, // ŗ
{13, 20, 14, 1, 20, 33, 6352}, // Ř
{8, 17, 9, 1, 17, 17, 6385}, // ř
{12, 21, 12, 0, 21, 32, 6402}, // Ś
{10, 17, 10, 0, 17, 22, 6434}, // ś
{12, 21, 12, 0, 21, 32, 6456}, // Ŝ
{10, 17, 10, 0, 17, 22, 6488}, // ŝ
{12, 20, 12, 0, 15, 30, 6510}, // Ş
{10, 16, 10, 0, 11, 20, 6540}, // ş
{12, 21, 12, 0, 21, 32, 6560}, // Š
{10, 17, 10, 0, 17, 22, 6592}, // š
{13, 19, 13, 0, 15, 31, 6614}, // Ţ
{8, 18, 9, 1, 14, 18, 6645}, // ţ
{13, 21, 13, 0, 21, 35, 6663}, // Ť
{8, 16, 9, 1, 16, 16, 6698}, // ť
{13, 15, 13, 0, 15, 25, 6714}, // Ŧ
{8, 14, 9, 1, 14, 14, 6739}, // ŧ
{13, 19, 15, 1, 19, 31, 6753}, // Ũ
{10, 16, 12, 1, 16, 20, 6784}, // ũ
{13, 18, 15, 1, 18, 30, 6804}, // Ū
{10, 15, 12, 1, 15, 19, 6834}, // ū
{13, 19, 15, 1, 19, 31, 6853}, // Ŭ
{10, 16, 12, 1, 16, 20, 6884}, // ŭ
{13, 21, 15, 1, 21, 35, 6904}, // Ů
{10, 18, 12, 1, 18, 23, 6939}, // ů
{13, 21, 15, 1, 21, 35, 6962}, // Ű
{11, 17, 12, 1, 17, 24, 6997}, // ű
{13, 19, 15, 1, 15, 31, 7021}, // Ų
{10, 15, 12, 1, 11, 19, 7052}, // ų
{20, 21, 20, 0, 21, 53, 7071}, // Ŵ
{17, 17, 16, 0, 17, 37, 7124}, // ŵ
{14, 21, 14, 0, 21, 37, 7161}, // Ŷ
{12, 21, 11, 0, 17, 32, 7198}, // ŷ
{14, 19, 14, 0, 19, 34, 7230}, // Ÿ
{13, 21, 13, 0, 21, 35, 7264}, // Ź
{10, 17, 11, 0, 17, 22, 7299}, // ź
{13, 19, 13, 0, 19, 31, 7321}, // Ż
{10, 16, 11, 0, 16, 20, 7352}, // ż
{13, 21, 13, 0, 21, 35, 7372}, // Ž
{10, 17, 11, 0, 17, 22, 7407}, // ž
{9, 16, 7, 1, 16, 18, 7429}, // ſ
{8, 3, 8, 0, 15, 3, 7447}, // ̑
{11, 3, 11, 0, 8, 5, 7450}, //
{21, 3, 21, 0, 8, 8, 7455}, // —
{21, 3, 21, 0, 8, 8, 7463}, // ―
{5, 6, 5, 0, 16, 4, 7471}, //
{5, 6, 5, 0, 16, 4, 7475}, //
{5, 6, 5, 0, 3, 4, 7479}, //
{10, 6, 10, 0, 16, 8, 7483}, // “
{9, 6, 10, 0, 16, 7, 7491}, // ”
{9, 6, 10, 0, 3, 7, 7498}, // „
{10, 18, 10, 0, 15, 23, 7505}, // †
{10, 18, 10, 0, 15, 23, 7528}, // ‡
{7, 6, 8, 0, 10, 6, 7551}, // •
{19, 4, 21, 1, 4, 10, 7557}, // …
{27, 15, 28, 0, 15, 51, 7567}, // ‰
{7, 11, 7, 0, 11, 10, 7618}, //
{7, 11, 7, 0, 11, 10, 7628}, //
{11, 15, 3, -4, 15, 21, 7638}, //
{12, 15, 12, 0, 15, 23, 7659}, // €
{12, 15, 12, 0, 15, 23, 7682}, // ₮
{12, 15, 12, 0, 15, 23, 7705}, // ₴
{13, 15, 12, 0, 15, 25, 7728}, // ₹
{12, 15, 13, 0, 15, 23, 7753}, // ∂
{15, 15, 15, 0, 15, 29, 7776}, // ∆
{13, 17, 15, 1, 15, 28, 7805}, // ∏
{11, 17, 11, 0, 15, 24, 7833}, // ∑
{10, 3, 12, 1, 8, 4, 7857}, //
{11, 15, 3, -4, 15, 21, 7861}, //
{5, 4, 5, 0, 9, 3, 7882}, // ∙
{14, 15, 13, 0, 15, 27, 7885}, // √
{13, 7, 13, 0, 10, 12, 7912}, // ∞
{9, 21, 8, 0, 16, 24, 7924}, // ∫
{12, 11, 12, 0, 12, 17, 7948}, // ≈
{10, 14, 12, 1, 14, 18, 7965}, // ≠
{10, 13, 12, 1, 13, 17, 7983}, // ≤
{10, 13, 12, 1, 13, 17, 8000}, // ≥
};
static const EpdUnicodeInterval ubuntu_bold_10Intervals[] = {
{0x0, 0x0, 0x0}, {0x8, 0x9, 0x1}, {0xD, 0xD, 0x3}, {0x1D, 0x1D, 0x4},
{0x20, 0x7E, 0x5}, {0xA0, 0xFF, 0x64}, {0x100, 0x17F, 0xC4}, {0x311, 0x311, 0x144},
{0x2013, 0x2015, 0x145}, {0x2018, 0x201A, 0x148}, {0x201C, 0x201E, 0x14B}, {0x2020, 0x2022, 0x14E},
{0x2026, 0x2026, 0x151}, {0x2030, 0x2030, 0x152}, {0x2039, 0x203A, 0x153}, {0x2044, 0x2044, 0x155},
{0x20AC, 0x20AC, 0x156}, {0x20AE, 0x20AE, 0x157}, {0x20B4, 0x20B4, 0x158}, {0x20B9, 0x20B9, 0x159},
{0x2202, 0x2202, 0x15A}, {0x2206, 0x2206, 0x15B}, {0x220F, 0x220F, 0x15C}, {0x2211, 0x2212, 0x15D},
{0x2215, 0x2215, 0x15F}, {0x2219, 0x221A, 0x160}, {0x221E, 0x221E, 0x162}, {0x222B, 0x222B, 0x163},
{0x2248, 0x2248, 0x164}, {0x2260, 0x2260, 0x165}, {0x2264, 0x2265, 0x166},
};
static const EpdFontData ubuntu_bold_10 = {
ubuntu_bold_10Bitmaps, ubuntu_bold_10Glyphs, ubuntu_bold_10Intervals, 31, 24, 20, -4,
};
+32 -22
View File
@@ -7,20 +7,18 @@ import math
import argparse import argparse
from collections import namedtuple from collections import namedtuple
# From: https://github.com/vroland/epdiy # Originally from https://github.com/vroland/epdiy
parser = argparse.ArgumentParser(description="Generate a header file from a font to be used with epdiy.") parser = argparse.ArgumentParser(description="Generate a header file from a font to be used with epdiy.")
parser.add_argument("name", action="store", help="name of the font.") parser.add_argument("name", action="store", help="name of the font.")
parser.add_argument("size", type=int, help="font size to use.") parser.add_argument("size", type=int, help="font size to use.")
parser.add_argument("fontstack", action="store", nargs='+', help="list of font files, ordered by descending priority.") parser.add_argument("fontstack", action="store", nargs='+', help="list of font files, ordered by descending priority.")
parser.add_argument("--compress", dest="compress", action="store_true", help="compress glyph bitmaps.")
parser.add_argument("--additional-intervals", dest="additional_intervals", action="append", help="Additional code point intervals to export as min,max. This argument can be repeated.") parser.add_argument("--additional-intervals", dest="additional_intervals", action="append", help="Additional code point intervals to export as min,max. This argument can be repeated.")
args = parser.parse_args() args = parser.parse_args()
GlyphProps = namedtuple("GlyphProps", ["width", "height", "advance_x", "left", "top", "compressed_size", "data_offset", "code_point"]) GlyphProps = namedtuple("GlyphProps", ["width", "height", "advance_x", "left", "top", "data_length", "data_offset", "code_point"])
font_stack = [freetype.Face(f) for f in args.fontstack] font_stack = [freetype.Face(f) for f in args.fontstack]
compress = args.compress
size = args.size size = args.size
font_name = args.name font_name = args.name
@@ -148,19 +146,18 @@ for i_start, i_end in unvalidated_intervals:
intervals.append((start, i_end)) intervals.append((start, i_end))
for face in font_stack: for face in font_stack:
# shift by 6 bytes, because sizes are given as 6-bit fractions
# the display has about 150 dpi.
face.set_char_size(size << 6, size << 6, 150, 150) face.set_char_size(size << 6, size << 6, 150, 150)
total_size = 0 total_size = 0
total_packed = 0
all_glyphs = [] all_glyphs = []
for i_start, i_end in intervals: for i_start, i_end in intervals:
for code_point in range(i_start, i_end + 1): for code_point in range(i_start, i_end + 1):
face = load_glyph(code_point) face = load_glyph(code_point)
bitmap = face.glyph.bitmap bitmap = face.glyph.bitmap
pixels = []
# Build out 4-bit greyscale bitmap
pixels4g = []
px = 0 px = 0
for i, v in enumerate(bitmap.buffer): for i, v in enumerate(bitmap.buffer):
y = i / bitmap.width y = i / bitmap.width
@@ -169,31 +166,45 @@ for i_start, i_end in intervals:
px = (v >> 4) px = (v >> 4)
else: else:
px = px | (v & 0xF0) px = px | (v & 0xF0)
pixels.append(px); pixels4g.append(px);
px = 0 px = 0
# eol # eol
if x == bitmap.width - 1 and bitmap.width % 2 > 0: if x == bitmap.width - 1 and bitmap.width % 2 > 0:
pixels.append(px) pixels4g.append(px)
px = 0 px = 0
packed = bytes(pixels); # Downsample to 1-bit bitmap - treat any non-zero as black
total_packed += len(packed) pixelsbw = []
compressed = packed px = 0
if compress: pitch = (bitmap.width // 2) + (bitmap.width % 2)
compressed = zlib.compress(packed) for y in range(bitmap.rows):
for x in range(bitmap.width):
px = px << 1
bm = pixels4g[y * pitch + (x // 2)]
px += 1 if ((x & 1) == 0 and bm & 0xF > 0) or ((x & 1) == 1 and bm & 0xF0 > 0) else 0
if (y * bitmap.width + x) % 8 == 7:
pixelsbw.append(px)
px = 0
if (bitmap.width * bitmap.rows) % 8 != 0:
px = px << (8 - (bitmap.width * bitmap.rows) % 8)
pixelsbw.append(px)
# Build output data
packed = bytes(pixelsbw)
glyph = GlyphProps( glyph = GlyphProps(
width = bitmap.width, width = bitmap.width,
height = bitmap.rows, height = bitmap.rows,
advance_x = norm_floor(face.glyph.advance.x), advance_x = norm_floor(face.glyph.advance.x),
left = face.glyph.bitmap_left, left = face.glyph.bitmap_left,
top = face.glyph.bitmap_top, top = face.glyph.bitmap_top,
compressed_size = len(compressed), data_length = len(packed),
data_offset = total_size, data_offset = total_size,
code_point = code_point, code_point = code_point,
) )
total_size += len(compressed) total_size += len(packed)
all_glyphs.append((glyph, compressed)) all_glyphs.append((glyph, packed))
# pipe seems to be a good heuristic for the "real" descender # pipe seems to be a good heuristic for the "real" descender
face = load_glyph(ord('|')) face = load_glyph(ord('|'))
@@ -201,11 +212,11 @@ face = load_glyph(ord('|'))
glyph_data = [] glyph_data = []
glyph_props = [] glyph_props = []
for index, glyph in enumerate(all_glyphs): for index, glyph in enumerate(all_glyphs):
props, compressed = glyph props, packed = glyph
glyph_data.extend([b for b in compressed]) glyph_data.extend([b for b in packed])
glyph_props.append(props) glyph_props.append(props)
print(f"/**\n * generated by fontconvert.py\n * name: {font_name}\n * size: {size}\n * compressed: {compress}\n */") print(f"/**\n * generated by fontconvert.py\n * name: {font_name}\n * size: {size}\n */")
print("#pragma once") print("#pragma once")
print("#include \"EpdFontData.h\"\n") print("#include \"EpdFontData.h\"\n")
print(f"static const uint8_t {font_name}Bitmaps[{len(glyph_data)}] = {{") print(f"static const uint8_t {font_name}Bitmaps[{len(glyph_data)}] = {{")
@@ -230,7 +241,6 @@ print(f" {font_name}Bitmaps,")
print(f" {font_name}Glyphs,") print(f" {font_name}Glyphs,")
print(f" {font_name}Intervals,") print(f" {font_name}Intervals,")
print(f" {len(intervals)},") print(f" {len(intervals)},")
print(f" {1 if compress else 0},")
print(f" {norm_ceil(face.size.height)},") print(f" {norm_ceil(face.size.height)},")
print(f" {norm_ceil(face.size.ascender)},") print(f" {norm_ceil(face.size.ascender)},")
print(f" {norm_floor(face.size.descender)},") print(f" {norm_floor(face.size.descender)},")
+26 -74
View File
@@ -1,74 +1,52 @@
#pragma once #pragma once
#include <EpdFont.h> #include <EpdFontFamily.h>
#include <HardwareSerial.h> #include <HardwareSerial.h>
#include <Utf8.h> #include <Utf8.h>
#include <miniz.h>
inline int min(const int a, const int b) { return a < b ? a : b; } inline int min(const int a, const int b) { return a < b ? a : b; }
inline int max(const int a, const int b) { return a > b ? a : b; } inline int max(const int a, const int b) { return a > b ? a : b; }
static tinfl_decompressor decomp;
template <typename Renderable> template <typename Renderable>
class EpdFontRenderer { class EpdFontRenderer {
Renderable* renderer; Renderable& renderer;
void renderChar(uint32_t cp, int* x, const int* y, uint16_t color); void renderChar(uint32_t cp, int* x, const int* y, uint16_t color, EpdFontStyle style = REGULAR);
public: public:
const EpdFont* font; const EpdFontFamily* fontFamily;
explicit EpdFontRenderer(const EpdFont* font, Renderable* renderer); explicit EpdFontRenderer(const EpdFontFamily* fontFamily, Renderable& renderer)
: fontFamily(fontFamily), renderer(renderer) {}
~EpdFontRenderer() = default; ~EpdFontRenderer() = default;
void renderString(const char* string, int* x, int* y, uint16_t color); void renderString(const char* string, int* x, int* y, uint16_t color, EpdFontStyle style = REGULAR);
}; };
inline int uncompress(uint8_t* dest, size_t uncompressedSize, const uint8_t* source, size_t sourceSize) {
if (uncompressedSize == 0 || dest == nullptr || sourceSize == 0 || source == nullptr) {
return -1;
}
tinfl_init(&decomp);
// we know everything will fit into the buffer.
const tinfl_status decomp_status =
tinfl_decompress(&decomp, source, &sourceSize, dest, dest, &uncompressedSize,
TINFL_FLAG_PARSE_ZLIB_HEADER | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF);
if (decomp_status != TINFL_STATUS_DONE) {
return decomp_status;
}
return 0;
}
template <typename Renderable> template <typename Renderable>
EpdFontRenderer<Renderable>::EpdFontRenderer(const EpdFont* font, Renderable* renderer) { void EpdFontRenderer<Renderable>::renderString(const char* string, int* x, int* y, const uint16_t color,
this->font = font; const EpdFontStyle style) {
this->renderer = renderer;
}
template <typename Renderable>
void EpdFontRenderer<Renderable>::renderString(const char* string, int* x, int* y, const uint16_t color) {
// cannot draw a NULL / empty string // cannot draw a NULL / empty string
if (string == nullptr || *string == '\0') { if (string == nullptr || *string == '\0') {
return; return;
} }
// no printable characters // no printable characters
if (!font->hasPrintableChars(string)) { if (!fontFamily->hasPrintableChars(string, style)) {
return; return;
} }
uint32_t cp; uint32_t cp;
while ((cp = utf8NextCodepoint(reinterpret_cast<const uint8_t**>(&string)))) { while ((cp = utf8NextCodepoint(reinterpret_cast<const uint8_t**>(&string)))) {
renderChar(cp, x, y, color); renderChar(cp, x, y, color, style);
} }
*y += font->data->advanceY; *y += fontFamily->getData(style)->advanceY;
} }
template <typename Renderable> template <typename Renderable>
void EpdFontRenderer<Renderable>::renderChar(const uint32_t cp, int* x, const int* y, uint16_t color) { void EpdFontRenderer<Renderable>::renderChar(const uint32_t cp, int* x, const int* y, uint16_t color,
const EpdGlyph* glyph = font->getGlyph(cp); const EpdFontStyle style) {
const EpdGlyph* glyph = fontFamily->getGlyph(cp, style);
if (!glyph) { if (!glyph) {
// TODO: Replace with fallback glyph property? // TODO: Replace with fallback glyph property?
glyph = font->getGlyph('?'); glyph = fontFamily->getGlyph('?', style);
} }
// no glyph? // no glyph?
@@ -82,50 +60,24 @@ void EpdFontRenderer<Renderable>::renderChar(const uint32_t cp, int* x, const in
const uint8_t height = glyph->height; const uint8_t height = glyph->height;
const int left = glyph->left; const int left = glyph->left;
const int byteWidth = width / 2 + width % 2;
const unsigned long bitmapSize = byteWidth * height;
const uint8_t* bitmap = nullptr; const uint8_t* bitmap = nullptr;
bitmap = &fontFamily->getData(style)->bitmap[offset];
if (font->data->compressed) {
auto* tmpBitmap = static_cast<uint8_t*>(malloc(bitmapSize));
if (tmpBitmap == nullptr && bitmapSize) {
// ESP_LOGE("font", "malloc failed.");
return;
}
uncompress(tmpBitmap, bitmapSize, &font->data->bitmap[offset], glyph->compressedSize);
bitmap = tmpBitmap;
} else {
bitmap = &font->data->bitmap[offset];
}
if (bitmap != nullptr) { if (bitmap != nullptr) {
for (int localY = 0; localY < height; localY++) { for (int glyphY = 0; glyphY < height; glyphY++) {
int yy = *y - glyph->top + localY; int screenY = *y - glyph->top + glyphY;
const int startPos = *x + left; for (int glyphX = 0; glyphX < width; glyphX++) {
bool byteComplete = startPos % 2; const int pixelPosition = glyphY * width + glyphX;
int localX = max(0, -startPos); int screenX = *x + left + glyphX;
const int maxX = startPos + width;
for (int xx = startPos; xx < maxX; xx++) { const uint8_t byte = bitmap[pixelPosition / 8];
uint8_t bm = bitmap[localY * byteWidth + localX / 2]; const uint8_t bit_index = 7 - (pixelPosition % 8);
if ((localX & 1) == 0) {
bm = bm & 0xF;
} else {
bm = bm >> 4;
}
if (bm) { if ((byte >> bit_index) & 1) {
renderer->drawPixel(xx, yy, color); renderer.drawPixel(screenX, screenY, color);
} }
byteComplete = !byteComplete;
localX++;
} }
} }
if (font->data->compressed) {
free(const_cast<uint8_t*>(bitmap));
}
} }
*x += glyph->advanceX; *x += glyph->advanceX;
+87 -70
View File
@@ -5,66 +5,82 @@
#include "builtinFonts/bookerly_bold.h" #include "builtinFonts/bookerly_bold.h"
#include "builtinFonts/bookerly_bold_italic.h" #include "builtinFonts/bookerly_bold_italic.h"
#include "builtinFonts/bookerly_italic.h" #include "builtinFonts/bookerly_italic.h"
#include "builtinFonts/ubuntu_10.h"
#include "builtinFonts/ubuntu_bold_10.h"
EpdRenderer::EpdRenderer(XteinkDisplay* display) { EpdFont bookerlyFont(&bookerly);
this->display = display; EpdFont bookerlyBoldFont(&bookerly_bold);
this->regularFont = new EpdFontRenderer<XteinkDisplay>(new EpdFont(&bookerly), display); EpdFont bookerlyItalicFont(&bookerly_italic);
this->boldFont = new EpdFontRenderer<XteinkDisplay>(new EpdFont(&bookerly_bold), display); EpdFont bookerlyBoldItalicFont(&bookerly_bold_italic);
this->italicFont = new EpdFontRenderer<XteinkDisplay>(new EpdFont(&bookerly_italic), display); EpdFontFamily bookerlyFontFamily(&bookerlyFont, &bookerlyBoldFont, &bookerlyItalicFont, &bookerlyBoldItalicFont);
this->bold_italicFont = new EpdFontRenderer<XteinkDisplay>(new EpdFont(&bookerly_bold_italic), display);
this->smallFont = new EpdFontRenderer<XteinkDisplay>(new EpdFont(&babyblue), display);
this->marginTop = 11; EpdFont smallFont(&babyblue);
this->marginBottom = 30; EpdFontFamily smallFontFamily(&smallFont);
this->marginLeft = 10;
this->marginRight = 10; EpdFont ubuntu10Font(&ubuntu_10);
this->lineCompression = 0.95f; EpdFont ununtuBold10Font(&ubuntu_bold_10);
EpdFontFamily ubuntuFontFamily(&ubuntu10Font, &ununtuBold10Font);
EpdRenderer::EpdRenderer(XteinkDisplay& display)
: display(display), marginTop(11), marginBottom(30), marginLeft(10), marginRight(10), lineCompression(0.95f) {
this->regularFontRenderer = new EpdFontRenderer<XteinkDisplay>(&bookerlyFontFamily, display);
this->smallFontRenderer = new EpdFontRenderer<XteinkDisplay>(&smallFontFamily, display);
this->uiFontRenderer = new EpdFontRenderer<XteinkDisplay>(&ubuntuFontFamily, display);
} }
EpdFontRenderer<XteinkDisplay>* EpdRenderer::getFontRenderer(const bool bold, const bool italic) const { EpdRenderer::~EpdRenderer() {
if (bold && italic) { delete regularFontRenderer;
return bold_italicFont; delete smallFontRenderer;
} delete uiFontRenderer;
if (bold) {
return boldFont;
}
if (italic) {
return italicFont;
}
return regularFont;
} }
int EpdRenderer::getTextWidth(const char* text, const bool bold, const bool italic) const { int EpdRenderer::getTextWidth(const char* text, const EpdFontStyle style) const {
int w = 0, h = 0; int w = 0, h = 0;
getFontRenderer(bold, italic)->font->getTextDimensions(text, &w, &h); regularFontRenderer->fontFamily->getTextDimensions(text, &w, &h, style);
return w; return w;
} }
int EpdRenderer::getSmallTextWidth(const char* text) const { int EpdRenderer::getUiTextWidth(const char* text, const EpdFontStyle style) const {
int w = 0, h = 0; int w = 0, h = 0;
smallFont->font->getTextDimensions(text, &w, &h); uiFontRenderer->fontFamily->getTextDimensions(text, &w, &h, style);
return w; return w;
} }
void EpdRenderer::drawText(const int x, const int y, const char* text, const bool bold, const bool italic, int EpdRenderer::getSmallTextWidth(const char* text, const EpdFontStyle style) const {
const uint16_t color) const { int w = 0, h = 0;
smallFontRenderer->fontFamily->getTextDimensions(text, &w, &h, style);
return w;
}
void EpdRenderer::drawText(const int x, const int y, const char* text, const uint16_t color,
const EpdFontStyle style) const {
int ypos = y + getLineHeight() + marginTop; int ypos = y + getLineHeight() + marginTop;
int xpos = x + marginLeft; int xpos = x + marginLeft;
getFontRenderer(bold, italic)->renderString(text, &xpos, &ypos, color > 0 ? GxEPD_BLACK : GxEPD_WHITE); regularFontRenderer->renderString(text, &xpos, &ypos, color > 0 ? GxEPD_BLACK : GxEPD_WHITE, style);
} }
void EpdRenderer::drawSmallText(const int x, const int y, const char* text) const { void EpdRenderer::drawUiText(const int x, const int y, const char* text, const uint16_t color,
int ypos = y + smallFont->font->data->advanceY + marginTop; const EpdFontStyle style) const {
int ypos = y + uiFontRenderer->fontFamily->getData(style)->advanceY + marginTop;
int xpos = x + marginLeft; int xpos = x + marginLeft;
smallFont->renderString(text, &xpos, &ypos, GxEPD_BLACK); uiFontRenderer->renderString(text, &xpos, &ypos, color > 0 ? GxEPD_BLACK : GxEPD_WHITE, style);
}
void EpdRenderer::drawSmallText(const int x, const int y, const char* text, const uint16_t color,
const EpdFontStyle style) const {
int ypos = y + smallFontRenderer->fontFamily->getData(style)->advanceY + marginTop;
int xpos = x + marginLeft;
smallFontRenderer->renderString(text, &xpos, &ypos, color > 0 ? GxEPD_BLACK : GxEPD_WHITE, style);
} }
void EpdRenderer::drawTextBox(const int x, const int y, const std::string& text, const int width, const int height, void EpdRenderer::drawTextBox(const int x, const int y, const std::string& text, const int width, const int height,
const bool bold, const bool italic) const { const EpdFontStyle style) const {
const size_t length = text.length(); const size_t length = text.length();
// fit the text into the box // fit the text into the box
int start = 0; int start = 0;
@@ -72,7 +88,7 @@ void EpdRenderer::drawTextBox(const int x, const int y, const std::string& text,
int ypos = 0; int ypos = 0;
while (true) { while (true) {
if (end >= length) { if (end >= length) {
drawText(x, y + ypos, text.substr(start, length - start).c_str(), bold, italic); drawText(x, y + ypos, text.substr(start, length - start).c_str(), 1, style);
break; break;
} }
@@ -81,15 +97,15 @@ void EpdRenderer::drawTextBox(const int x, const int y, const std::string& text,
} }
if (text[end - 1] == '\n') { if (text[end - 1] == '\n') {
drawText(x, y + ypos, text.substr(start, end - start).c_str(), bold, italic); drawText(x, y + ypos, text.substr(start, end - start).c_str(), 1, style);
ypos += getLineHeight(); ypos += getLineHeight();
start = end; start = end;
end = start + 1; end = start + 1;
continue; continue;
} }
if (getTextWidth(text.substr(start, end - start).c_str(), bold, italic) > width) { if (getTextWidth(text.substr(start, end - start).c_str(), style) > width) {
drawText(x, y + ypos, text.substr(start, end - start - 1).c_str(), bold, italic); drawText(x, y + ypos, text.substr(start, end - start - 1).c_str(), 1, style);
ypos += getLineHeight(); ypos += getLineHeight();
start = end - 1; start = end - 1;
continue; continue;
@@ -100,52 +116,53 @@ void EpdRenderer::drawTextBox(const int x, const int y, const std::string& text,
} }
void EpdRenderer::drawLine(int x1, int y1, int x2, int y2, uint16_t color) const { void EpdRenderer::drawLine(int x1, int y1, int x2, int y2, uint16_t color) const {
display->drawLine(x1 + marginLeft, y1 + marginTop, x2 + marginLeft, y2 + marginTop, display.drawLine(x1 + marginLeft, y1 + marginTop, x2 + marginLeft, y2 + marginTop,
color > 0 ? GxEPD_BLACK : GxEPD_WHITE); color > 0 ? GxEPD_BLACK : GxEPD_WHITE);
} }
void EpdRenderer::drawRect(const int x, const int y, const int width, const int height, const uint16_t color) const { void EpdRenderer::drawRect(const int x, const int y, const int width, const int height, const uint16_t color) const {
display->drawRect(x + marginLeft, y + marginTop, width, height, color > 0 ? GxEPD_BLACK : GxEPD_WHITE); display.drawRect(x + marginLeft, y + marginTop, width, height, color > 0 ? GxEPD_BLACK : GxEPD_WHITE);
} }
void EpdRenderer::fillRect(const int x, const int y, const int width, const int height, void EpdRenderer::fillRect(const int x, const int y, const int width, const int height, const uint16_t color) const {
const uint16_t color = 0) const { display.fillRect(x + marginLeft, y + marginTop, width, height, color > 0 ? GxEPD_BLACK : GxEPD_WHITE);
display->fillRect(x + marginLeft, y + marginTop, width, height, color > 0 ? GxEPD_BLACK : GxEPD_WHITE); }
void EpdRenderer::drawCircle(const int x, const int y, const int radius, const uint16_t color) const {
display.drawCircle(x + marginLeft, y + marginTop, radius, color > 0 ? GxEPD_BLACK : GxEPD_WHITE);
}
void EpdRenderer::fillCircle(const int x, const int y, const int radius, const uint16_t color) const {
display.fillCircle(x + marginLeft, y + marginTop, radius, color > 0 ? GxEPD_BLACK : GxEPD_WHITE);
}
void EpdRenderer::drawImage(const uint8_t bitmap[], const int x, const int y, const int width, const int height,
const bool invert, const bool mirrorY) const {
drawImageNoMargin(bitmap, x + marginLeft, y + marginTop, width, height, invert, mirrorY);
}
void EpdRenderer::drawImageNoMargin(const uint8_t bitmap[], const int x, const int y, const int width, const int height,
const bool invert, const bool mirrorY) const {
display.drawImage(bitmap, x, y, width, height, invert, mirrorY);
} }
void EpdRenderer::clearScreen(const bool black) const { void EpdRenderer::clearScreen(const bool black) const {
Serial.println("Clearing screen"); Serial.println("Clearing screen");
display->fillScreen(black ? GxEPD_BLACK : GxEPD_WHITE); display.fillScreen(black ? GxEPD_BLACK : GxEPD_WHITE);
} }
void EpdRenderer::flushDisplay() const { display->display(true); } void EpdRenderer::flushDisplay(const bool partialUpdate) const { display.display(partialUpdate); }
void EpdRenderer::flushArea(int x, int y, int width, int height) const { void EpdRenderer::flushArea(const int x, const int y, const int width, const int height) const {
// TODO: Fix display.displayWindow(x + marginLeft, y + marginTop, width, height);
display->display(true);
} }
int EpdRenderer::getPageWidth() const { return display->width() - marginLeft - marginRight; } int EpdRenderer::getPageWidth() const { return display.width() - marginLeft - marginRight; }
int EpdRenderer::getPageHeight() const { return display->height() - marginTop - marginBottom; } int EpdRenderer::getPageHeight() const { return display.height() - marginTop - marginBottom; }
int EpdRenderer::getSpaceWidth() const { return regularFont->font->getGlyph(' ')->advanceX; } int EpdRenderer::getSpaceWidth() const { return regularFontRenderer->fontFamily->getGlyph(' ', REGULAR)->advanceX; }
int EpdRenderer::getLineHeight() const { return regularFont->font->data->advanceY * lineCompression; } int EpdRenderer::getLineHeight() const {
return regularFontRenderer->fontFamily->getData(REGULAR)->advanceY * lineCompression;
// deep sleep helper - persist any state to disk that may be needed on wake }
bool EpdRenderer::dehydrate() {
// TODO: Implement
return false;
};
// deep sleep helper - retrieve any state from disk after wake
bool EpdRenderer::hydrate() {
// TODO: Implement
return false;
};
// really really clear the screen
void EpdRenderer::reset() {
// TODO: Implement
};
+23 -27
View File
@@ -7,33 +7,37 @@
#define XteinkDisplay GxEPD2_BW<GxEPD2_426_GDEQ0426T82, GxEPD2_426_GDEQ0426T82::HEIGHT> #define XteinkDisplay GxEPD2_BW<GxEPD2_426_GDEQ0426T82, GxEPD2_426_GDEQ0426T82::HEIGHT>
class EpdRenderer { class EpdRenderer {
XteinkDisplay* display; XteinkDisplay& display;
EpdFontRenderer<XteinkDisplay>* regularFont; EpdFontRenderer<XteinkDisplay>* regularFontRenderer;
EpdFontRenderer<XteinkDisplay>* boldFont; EpdFontRenderer<XteinkDisplay>* smallFontRenderer;
EpdFontRenderer<XteinkDisplay>* italicFont; EpdFontRenderer<XteinkDisplay>* uiFontRenderer;
EpdFontRenderer<XteinkDisplay>* bold_italicFont;
EpdFontRenderer<XteinkDisplay>* smallFont;
int marginTop; int marginTop;
int marginBottom; int marginBottom;
int marginLeft; int marginLeft;
int marginRight; int marginRight;
float lineCompression; float lineCompression;
EpdFontRenderer<XteinkDisplay>* getFontRenderer(bool bold, bool italic) const;
public: public:
explicit EpdRenderer(XteinkDisplay* display); explicit EpdRenderer(XteinkDisplay& display);
~EpdRenderer() = default; ~EpdRenderer();
int getTextWidth(const char* text, bool bold = false, bool italic = false) const; int getTextWidth(const char* text, EpdFontStyle style = REGULAR) const;
int getSmallTextWidth(const char* text) const; int getUiTextWidth(const char* text, EpdFontStyle style = REGULAR) const;
void drawText(int x, int y, const char* text, bool bold = false, bool italic = false, uint16_t color = 1) const; int getSmallTextWidth(const char* text, EpdFontStyle style = REGULAR) const;
void drawSmallText(int x, int y, const char* text) const; void drawText(int x, int y, const char* text, uint16_t color = 1, EpdFontStyle style = REGULAR) const;
void drawTextBox(int x, int y, const std::string& text, int width, int height, bool bold = false, void drawUiText(int x, int y, const char* text, uint16_t color = 1, EpdFontStyle style = REGULAR) const;
bool italic = false) const; void drawSmallText(int x, int y, const char* text, uint16_t color = 1, EpdFontStyle style = REGULAR) const;
void drawLine(int x1, int y1, int x2, int y2, uint16_t color) const; void drawTextBox(int x, int y, const std::string& text, int width, int height, EpdFontStyle style = REGULAR) const;
void drawRect(int x, int y, int width, int height, uint16_t color) const; void drawLine(int x1, int y1, int x2, int y2, uint16_t color = 1) const;
void fillRect(int x, int y, int width, int height, uint16_t color) const; void drawRect(int x, int y, int width, int height, uint16_t color = 1) const;
void fillRect(int x, int y, int width, int height, uint16_t color = 1) const;
void drawCircle(int x, int y, int radius, uint16_t color = 1) const;
void fillCircle(int x, int y, int radius, uint16_t color = 1) const;
void drawImage(const uint8_t bitmap[], int x, int y, int width, int height, bool invert = false,
bool mirrorY = false) const;
void drawImageNoMargin(const uint8_t bitmap[], int x, int y, int width, int height, bool invert = false,
bool mirrorY = false) const;
void clearScreen(bool black = false) const; void clearScreen(bool black = false) const;
void flushDisplay() const; void flushDisplay(bool partialUpdate = true) const;
void flushArea(int x, int y, int width, int height) const; void flushArea(int x, int y, int width, int height) const;
int getPageWidth() const; int getPageWidth() const;
@@ -45,12 +49,4 @@ class EpdRenderer {
void setMarginBottom(const int newMarginBottom) { this->marginBottom = newMarginBottom; } void setMarginBottom(const int newMarginBottom) { this->marginBottom = newMarginBottom; }
void setMarginLeft(const int newMarginLeft) { this->marginLeft = newMarginLeft; } void setMarginLeft(const int newMarginLeft) { this->marginLeft = newMarginLeft; }
void setMarginRight(const int newMarginRight) { this->marginRight = newMarginRight; } void setMarginRight(const int newMarginRight) { this->marginRight = newMarginRight; }
// deep sleep helper - persist any state to disk that may be needed on wake
bool dehydrate();
// deep sleep helper - retrieve any state from disk after wake
bool hydrate();
// really really clear the screen
void reset();
uint8_t temperature = 20;
}; };
+25 -28
View File
@@ -3,14 +3,13 @@
#include <HardwareSerial.h> #include <HardwareSerial.h>
#include <SD.h> #include <SD.h>
#include <ZipFile.h> #include <ZipFile.h>
#include <tinyxml2.h>
#include <map> #include <map>
bool Epub::findContentOpfFile(const ZipFile& zip, std::string& contentOpfFile) { bool Epub::findContentOpfFile(const ZipFile& zip, std::string& contentOpfFile) {
// open up the meta data to find where the content.opf file lives // open up the meta data to find where the content.opf file lives
size_t s; size_t s;
const auto metaInfo = zip.readTextFileToMemory("META-INF/container.xml", &s); const auto metaInfo = reinterpret_cast<char*>(zip.readFileToMemory("META-INF/container.xml", &s, true));
if (!metaInfo) { if (!metaInfo) {
Serial.println("Could not find META-INF/container.xml"); Serial.println("Could not find META-INF/container.xml");
return false; return false;
@@ -58,7 +57,7 @@ bool Epub::findContentOpfFile(const ZipFile& zip, std::string& contentOpfFile) {
bool Epub::parseContentOpf(ZipFile& zip, std::string& content_opf_file) { bool Epub::parseContentOpf(ZipFile& zip, std::string& content_opf_file) {
// read in the content.opf file and parse it // read in the content.opf file and parse it
auto contents = zip.readTextFileToMemory(content_opf_file.c_str()); auto contents = reinterpret_cast<char*>(zip.readFileToMemory(content_opf_file.c_str(), nullptr, true));
// parse the contents // parse the contents
tinyxml2::XMLDocument doc; tinyxml2::XMLDocument doc;
@@ -162,14 +161,14 @@ bool Epub::parseContentOpf(ZipFile& zip, std::string& content_opf_file) {
return true; return true;
} }
bool Epub::parseTocNcxFile(ZipFile& zip) { bool Epub::parseTocNcxFile(const ZipFile& zip) {
// the ncx file should have been specified in the content.opf file // the ncx file should have been specified in the content.opf file
if (tocNcxItem.empty()) { if (tocNcxItem.empty()) {
Serial.println("No ncx file specified"); Serial.println("No ncx file specified");
return false; return false;
} }
auto ncxData = zip.readTextFileToMemory(tocNcxItem.c_str()); const auto ncxData = reinterpret_cast<char*>(zip.readFileToMemory(tocNcxItem.c_str(), nullptr, true));
if (!ncxData) { if (!ncxData) {
Serial.printf("Could not find %s\n", tocNcxItem.c_str()); Serial.printf("Could not find %s\n", tocNcxItem.c_str());
return false; return false;
@@ -177,7 +176,7 @@ bool Epub::parseTocNcxFile(ZipFile& zip) {
// Parse the Toc contents // Parse the Toc contents
tinyxml2::XMLDocument doc; tinyxml2::XMLDocument doc;
auto result = doc.Parse(ncxData); const auto result = doc.Parse(ncxData);
free(ncxData); free(ncxData);
if (result != tinyxml2::XML_SUCCESS) { if (result != tinyxml2::XML_SUCCESS) {
@@ -185,27 +184,30 @@ bool Epub::parseTocNcxFile(ZipFile& zip) {
return false; return false;
} }
auto ncx = doc.FirstChildElement("ncx"); const auto ncx = doc.FirstChildElement("ncx");
if (!ncx) { if (!ncx) {
Serial.println("Could not find first child ncx in toc"); Serial.println("Could not find first child ncx in toc");
return false; return false;
} }
auto navMap = ncx->FirstChildElement("navMap"); const auto navMap = ncx->FirstChildElement("navMap");
if (!navMap) { if (!navMap) {
Serial.println("Could not find navMap child in ncx"); Serial.println("Could not find navMap child in ncx");
return false; return false;
} }
auto navPoint = navMap->FirstChildElement("navPoint"); recursivelyParseNavMap(navMap->FirstChildElement("navPoint"));
return true;
}
void Epub::recursivelyParseNavMap(tinyxml2::XMLElement* element) {
// Fills toc map // Fills toc map
while (navPoint) { while (element) {
std::string navTitle = navPoint->FirstChildElement("navLabel")->FirstChildElement("text")->FirstChild()->Value(); std::string navTitle = element->FirstChildElement("navLabel")->FirstChildElement("text")->FirstChild()->Value();
auto content = navPoint->FirstChildElement("content"); const auto content = element->FirstChildElement("content");
std::string href = contentBasePath + content->Attribute("src"); std::string href = contentBasePath + content->Attribute("src");
// split the href on the # to get the href and the anchor // split the href on the # to get the href and the anchor
size_t pos = href.find('#'); const size_t pos = href.find('#');
std::string anchor; std::string anchor;
if (pos != std::string::npos) { if (pos != std::string::npos) {
@@ -214,10 +216,13 @@ bool Epub::parseTocNcxFile(ZipFile& zip) {
} }
toc.emplace_back(navTitle, href, anchor, 0); toc.emplace_back(navTitle, href, anchor, 0);
navPoint = navPoint->NextSiblingElement("navPoint");
}
return true; tinyxml2::XMLElement* nestedNavPoint = element->FirstChildElement("navPoint");
if (nestedNavPoint) {
recursivelyParseNavMap(nestedNavPoint);
}
element = element->NextSiblingElement("navPoint");
}
} }
// load in the meta data for the epub file // load in the meta data for the epub file
@@ -303,11 +308,11 @@ std::string normalisePath(const std::string& path) {
return result; return result;
} }
uint8_t* Epub::getItemContents(const std::string& itemHref, size_t* size) const { uint8_t* Epub::readItemContentsToBytes(const std::string& itemHref, size_t* size, bool trailingNullByte) const {
const ZipFile zip("/sd" + filepath); const ZipFile zip("/sd" + filepath);
const std::string path = normalisePath(itemHref); const std::string path = normalisePath(itemHref);
const auto content = zip.readFileToMemory(path.c_str(), size); const auto content = zip.readFileToMemory(path.c_str(), size, trailingNullByte);
if (!content) { if (!content) {
Serial.printf("Failed to read item %s\n", path.c_str()); Serial.printf("Failed to read item %s\n", path.c_str());
return nullptr; return nullptr;
@@ -316,17 +321,11 @@ uint8_t* Epub::getItemContents(const std::string& itemHref, size_t* size) const
return content; return content;
} }
char* Epub::getTextItemContents(const std::string& itemHref, size_t* size) const { bool Epub::readItemContentsToStream(const std::string& itemHref, Print& out, const size_t chunkSize) const {
const ZipFile zip("/sd" + filepath); const ZipFile zip("/sd" + filepath);
const std::string path = normalisePath(itemHref); const std::string path = normalisePath(itemHref);
const auto content = zip.readTextFileToMemory(path.c_str(), size); return zip.readFileToStream(path.c_str(), out, chunkSize);
if (!content) {
Serial.printf("Failed to read item %s\n", path.c_str());
return nullptr;
}
return content;
} }
int Epub::getSpineItemsCount() const { return spine.size(); } int Epub::getSpineItemsCount() const { return spine.size(); }
@@ -369,9 +368,7 @@ int Epub::getSpineIndexForTocIndex(const int tocIndex) const {
int Epub::getTocIndexForSpineIndex(const int spineIndex) const { int Epub::getTocIndexForSpineIndex(const int spineIndex) const {
// the toc entry should have an href that matches the spine item // the toc entry should have an href that matches the spine item
// so we can find the toc index by looking for the href // so we can find the toc index by looking for the href
Serial.printf("Looking for %s\n", spine[spineIndex].second.c_str());
for (int i = 0; i < toc.size(); i++) { for (int i = 0; i < toc.size(); i++) {
Serial.printf("Looking at %s\n", toc[i].href.c_str());
if (toc[i].href == spine[spineIndex].second) { if (toc[i].href == spine[spineIndex].second) {
return i; return i;
} }
+6 -11
View File
@@ -1,5 +1,6 @@
#pragma once #pragma once
#include <HardwareSerial.h> #include <HardwareSerial.h>
#include <tinyxml2.h>
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
@@ -38,36 +39,30 @@ class Epub {
// find the path for the content.opf file // find the path for the content.opf file
static bool findContentOpfFile(const ZipFile& zip, std::string& contentOpfFile); static bool findContentOpfFile(const ZipFile& zip, std::string& contentOpfFile);
bool parseContentOpf(ZipFile& zip, std::string& content_opf_file); bool parseContentOpf(ZipFile& zip, std::string& content_opf_file);
bool parseTocNcxFile(ZipFile& zip); bool parseTocNcxFile(const ZipFile& zip);
void recursivelyParseNavMap(tinyxml2::XMLElement* element);
public: public:
explicit Epub(std::string filepath, const std::string& cacheDir) : filepath(std::move(filepath)) { explicit Epub(std::string filepath, const std::string& cacheDir) : filepath(std::move(filepath)) {
// create a cache key based on the filepath // create a cache key based on the filepath
cachePath = cacheDir + "/epub_" + std::to_string(std::hash<std::string>{}(this->filepath)); cachePath = cacheDir + "/epub_" + std::to_string(std::hash<std::string>{}(this->filepath));
} }
~Epub() = default; ~Epub() = default;
std::string& getBasePath() { return contentBasePath; } std::string& getBasePath() { return contentBasePath; }
bool load(); bool load();
void clearCache() const; void clearCache() const;
void setupCacheDir() const; void setupCacheDir() const;
const std::string& getCachePath() const; const std::string& getCachePath() const;
const std::string& getPath() const; const std::string& getPath() const;
const std::string& getTitle() const; const std::string& getTitle() const;
const std::string& getCoverImageItem() const; const std::string& getCoverImageItem() const;
uint8_t* getItemContents(const std::string& itemHref, size_t* size = nullptr) const; uint8_t* readItemContentsToBytes(const std::string& itemHref, size_t* size = nullptr,
char* getTextItemContents(const std::string& itemHref, size_t* size = nullptr) const; bool trailingNullByte = false) const;
bool readItemContentsToStream(const std::string& itemHref, Print& out, size_t chunkSize) const;
std::string& getSpineItem(int spineIndex); std::string& getSpineItem(int spineIndex);
int getSpineItemsCount() const; int getSpineItemsCount() const;
EpubTocEntry& getTocItem(int tocTndex); EpubTocEntry& getTocItem(int tocTndex);
int getTocItemsCount() const; int getTocItemsCount() const;
// work out the section index for a toc index
int getSpineIndexForTocIndex(int tocIndex) const; int getSpineIndexForTocIndex(int tocIndex) const;
int getTocIndexForSpineIndex(int spineIndex) const; int getTocIndexForSpineIndex(int spineIndex) const;
}; };
-181
View File
@@ -1,181 +0,0 @@
#include "EpubHtmlParser.h"
#include <EpdRenderer.h>
#include <HardwareSerial.h>
#include "Page.h"
#include "htmlEntities.h"
const char* HEADER_TAGS[] = {"h1", "h2", "h3", "h4", "h5", "h6"};
constexpr int NUM_HEADER_TAGS = sizeof(HEADER_TAGS) / sizeof(HEADER_TAGS[0]);
const char* BLOCK_TAGS[] = {"p", "li", "div", "br"};
constexpr int NUM_BLOCK_TAGS = sizeof(BLOCK_TAGS) / sizeof(BLOCK_TAGS[0]);
const char* BOLD_TAGS[] = {"b"};
constexpr int NUM_BOLD_TAGS = sizeof(BOLD_TAGS) / sizeof(BOLD_TAGS[0]);
const char* ITALIC_TAGS[] = {"i"};
constexpr int NUM_ITALIC_TAGS = sizeof(ITALIC_TAGS) / sizeof(ITALIC_TAGS[0]);
const char* IMAGE_TAGS[] = {"img"};
constexpr int NUM_IMAGE_TAGS = sizeof(IMAGE_TAGS) / sizeof(IMAGE_TAGS[0]);
const char* SKIP_TAGS[] = {"head", "table"};
constexpr int NUM_SKIP_TAGS = sizeof(SKIP_TAGS) / sizeof(SKIP_TAGS[0]);
// given the start and end of a tag, check to see if it matches a known tag
bool matches(const char* tag_name, const char* possible_tags[], const int possible_tag_count) {
for (int i = 0; i < possible_tag_count; i++) {
if (strcmp(tag_name, possible_tags[i]) == 0) {
return true;
}
}
return false;
}
// start a new text block if needed
void EpubHtmlParser::startNewTextBlock(const BLOCK_STYLE style) {
if (currentTextBlock) {
// already have a text block running and it is empty - just reuse it
if (currentTextBlock->isEmpty()) {
currentTextBlock->set_style(style);
return;
}
currentTextBlock->finish();
makePages();
delete currentTextBlock;
}
currentTextBlock = new TextBlock(style);
}
bool EpubHtmlParser::VisitEnter(const tinyxml2::XMLElement& element, const tinyxml2::XMLAttribute* firstAttribute) {
const char* tag_name = element.Name();
if (matches(tag_name, IMAGE_TAGS, NUM_IMAGE_TAGS)) {
const char* src = element.Attribute("src");
if (src) {
// don't leave an empty text block in the list
// const BLOCK_STYLE style = currentTextBlock->get_style();
if (currentTextBlock->isEmpty()) {
delete currentTextBlock;
currentTextBlock = nullptr;
}
// TODO: Fix this
// blocks.push_back(new ImageBlock(m_base_path + src));
// start a new text block - with the same style as before
// startNewTextBlock(style);
} else {
// ESP_LOGE(TAG, "Could not find src attribute");
}
return false;
}
if (matches(tag_name, SKIP_TAGS, NUM_SKIP_TAGS)) {
return false;
}
// Serial.printf("Text: %s\n", element.GetText());
if (matches(tag_name, HEADER_TAGS, NUM_HEADER_TAGS)) {
insideBoldTag = true;
startNewTextBlock(CENTER_ALIGN);
} else if (matches(tag_name, BLOCK_TAGS, NUM_BLOCK_TAGS)) {
if (strcmp(tag_name, "br") == 0) {
startNewTextBlock(currentTextBlock->get_style());
} else {
startNewTextBlock(JUSTIFIED);
}
} else if (matches(tag_name, BOLD_TAGS, NUM_BOLD_TAGS)) {
insideBoldTag = true;
} else if (matches(tag_name, ITALIC_TAGS, NUM_ITALIC_TAGS)) {
insideItalicTag = true;
}
return true;
}
/// Visit a text node.
bool EpubHtmlParser::Visit(const tinyxml2::XMLText& text) {
const char* content = text.Value();
currentTextBlock->addSpan(replaceHtmlEntities(content), insideBoldTag, insideItalicTag);
return true;
}
bool EpubHtmlParser::VisitExit(const tinyxml2::XMLElement& element) {
const char* tag_name = element.Name();
if (matches(tag_name, HEADER_TAGS, NUM_HEADER_TAGS)) {
insideBoldTag = false;
} else if (matches(tag_name, BLOCK_TAGS, NUM_BLOCK_TAGS)) {
// nothing to do
} else if (matches(tag_name, BOLD_TAGS, NUM_BOLD_TAGS)) {
insideBoldTag = false;
} else if (matches(tag_name, ITALIC_TAGS, NUM_ITALIC_TAGS)) {
insideItalicTag = false;
}
return true;
}
bool EpubHtmlParser::parseAndBuildPages() {
startNewTextBlock(JUSTIFIED);
tinyxml2::XMLDocument doc(false, tinyxml2::COLLAPSE_WHITESPACE);
const tinyxml2::XMLError result = doc.LoadFile(filepath);
if (result != tinyxml2::XML_SUCCESS) {
Serial.printf("Failed to load file, Error: %s\n", tinyxml2::XMLDocument::ErrorIDToName(result));
return false;
}
doc.Accept(this);
if (currentTextBlock) {
makePages();
completePageFn(currentPage);
currentPage = nullptr;
delete currentTextBlock;
currentTextBlock = nullptr;
}
return true;
}
void EpubHtmlParser::makePages() {
if (!currentTextBlock) {
Serial.println("!! No text block to make pages for !!");
return;
}
if (!currentPage) {
currentPage = new Page();
}
const int lineHeight = renderer->getLineHeight();
const int pageHeight = renderer->getPageHeight();
// Long running task, make sure to let other things happen
vTaskDelay(1);
if (currentTextBlock->getType() == TEXT_BLOCK) {
const auto lines = currentTextBlock->splitIntoLines(renderer);
for (const auto line : lines) {
if (currentPage->nextY + lineHeight > pageHeight) {
completePageFn(currentPage);
currentPage = new Page();
}
currentPage->elements.push_back(new PageLine(line, currentPage->nextY));
currentPage->nextY += lineHeight;
}
// TODO: Fix spacing between paras
// add some extra line between blocks
currentPage->nextY += lineHeight / 2;
}
// TODO: Image block support
// if (block->getType() == BlockType::IMAGE_BLOCK) {
// ImageBlock *imageBlock = (ImageBlock *)block;
// if (y + imageBlock->height > page_height) {
// pages.push_back(new Page());
// y = 0;
// }
// pages.back()->elements.push_back(new PageImage(imageBlock, y));
// y += imageBlock->height;
// }
}
-34
View File
@@ -1,34 +0,0 @@
#pragma once
#include <tinyxml2.h>
#include <functional>
#include "blocks/TextBlock.h"
class Page;
class EpdRenderer;
class EpubHtmlParser final : public tinyxml2::XMLVisitor {
const char* filepath;
EpdRenderer* renderer;
std::function<void(Page*)> completePageFn;
bool insideBoldTag = false;
bool insideItalicTag = false;
TextBlock* currentTextBlock = nullptr;
Page* currentPage = nullptr;
void startNewTextBlock(BLOCK_STYLE style);
void makePages();
// xml parser callbacks
bool VisitEnter(const tinyxml2::XMLElement& element, const tinyxml2::XMLAttribute* firstAttribute) override;
bool Visit(const tinyxml2::XMLText& text) override;
bool VisitExit(const tinyxml2::XMLElement& element) override;
// xml parser callbacks
public:
explicit EpubHtmlParser(const char* filepath, EpdRenderer* renderer, const std::function<void(Page*)>& completePageFn)
: filepath(filepath), renderer(renderer), completePageFn(completePageFn) {}
~EpubHtmlParser() override = default;
bool parseAndBuildPages();
};
+293
View File
@@ -0,0 +1,293 @@
#include "EpubHtmlParserSlim.h"
#include <EpdRenderer.h>
#include <HardwareSerial.h>
#include <expat.h>
#include "Page.h"
#include "htmlEntities.h"
const char* HEADER_TAGS[] = {"h1", "h2", "h3", "h4", "h5", "h6"};
constexpr int NUM_HEADER_TAGS = sizeof(HEADER_TAGS) / sizeof(HEADER_TAGS[0]);
const char* BLOCK_TAGS[] = {"p", "li", "div", "br"};
constexpr int NUM_BLOCK_TAGS = sizeof(BLOCK_TAGS) / sizeof(BLOCK_TAGS[0]);
const char* BOLD_TAGS[] = {"b"};
constexpr int NUM_BOLD_TAGS = sizeof(BOLD_TAGS) / sizeof(BOLD_TAGS[0]);
const char* ITALIC_TAGS[] = {"i"};
constexpr int NUM_ITALIC_TAGS = sizeof(ITALIC_TAGS) / sizeof(ITALIC_TAGS[0]);
const char* IMAGE_TAGS[] = {"img"};
constexpr int NUM_IMAGE_TAGS = sizeof(IMAGE_TAGS) / sizeof(IMAGE_TAGS[0]);
const char* SKIP_TAGS[] = {"head", "table"};
constexpr int NUM_SKIP_TAGS = sizeof(SKIP_TAGS) / sizeof(SKIP_TAGS[0]);
bool isWhitespace(const char c) { return c == ' ' || c == '\r' || c == '\n'; }
// given the start and end of a tag, check to see if it matches a known tag
bool matches(const char* tag_name, const char* possible_tags[], const int possible_tag_count) {
for (int i = 0; i < possible_tag_count; i++) {
if (strcmp(tag_name, possible_tags[i]) == 0) {
return true;
}
}
return false;
}
// start a new text block if needed
void EpubHtmlParserSlim::startNewTextBlock(const BLOCK_STYLE style) {
if (currentTextBlock) {
// already have a text block running and it is empty - just reuse it
if (currentTextBlock->isEmpty()) {
currentTextBlock->setStyle(style);
return;
}
currentTextBlock->finish();
makePages();
delete currentTextBlock;
}
currentTextBlock = new TextBlock(style);
}
void XMLCALL EpubHtmlParserSlim::startElement(void* userData, const XML_Char* name, const XML_Char** atts) {
auto* self = static_cast<EpubHtmlParserSlim*>(userData);
(void)atts;
// Middle of skip
if (self->skipUntilDepth < self->depth) {
self->depth += 1;
return;
}
if (matches(name, IMAGE_TAGS, NUM_IMAGE_TAGS)) {
// const char* src = element.Attribute("src");
// if (src) {
// // don't leave an empty text block in the list
// // const BLOCK_STYLE style = currentTextBlock->get_style();
// if (currentTextBlock->isEmpty()) {
// delete currentTextBlock;
// currentTextBlock = nullptr;
// }
// // TODO: Fix this
// // blocks.push_back(new ImageBlock(m_base_path + src));
// // start a new text block - with the same style as before
// // startNewTextBlock(style);
// } else {
// // ESP_LOGE(TAG, "Could not find src attribute");
// }
// start skip
self->skipUntilDepth = self->depth;
self->depth += 1;
return;
}
if (matches(name, SKIP_TAGS, NUM_SKIP_TAGS)) {
// start skip
self->skipUntilDepth = self->depth;
self->depth += 1;
return;
}
if (matches(name, HEADER_TAGS, NUM_HEADER_TAGS)) {
self->startNewTextBlock(CENTER_ALIGN);
self->boldUntilDepth = min(self->boldUntilDepth, self->depth);
} else if (matches(name, BLOCK_TAGS, NUM_BLOCK_TAGS)) {
if (strcmp(name, "br") == 0) {
self->startNewTextBlock(self->currentTextBlock->getStyle());
} else {
self->startNewTextBlock(JUSTIFIED);
}
} else if (matches(name, BOLD_TAGS, NUM_BOLD_TAGS)) {
self->boldUntilDepth = min(self->boldUntilDepth, self->depth);
} else if (matches(name, ITALIC_TAGS, NUM_ITALIC_TAGS)) {
self->italicUntilDepth = min(self->italicUntilDepth, self->depth);
}
self->depth += 1;
}
void XMLCALL EpubHtmlParserSlim::characterData(void* userData, const XML_Char* s, const int len) {
auto* self = static_cast<EpubHtmlParserSlim*>(userData);
// Middle of skip
if (self->skipUntilDepth < self->depth) {
return;
}
for (int i = 0; i < len; i++) {
if (isWhitespace(s[i])) {
// Currently looking at whitespace, if there's anything in the partWordBuffer, flush it
if (self->partWordBufferIndex > 0) {
self->partWordBuffer[self->partWordBufferIndex] = '\0';
self->currentTextBlock->addWord(replaceHtmlEntities(self->partWordBuffer), self->boldUntilDepth < self->depth,
self->italicUntilDepth < self->depth);
self->partWordBufferIndex = 0;
}
// Skip the whitespace char
continue;
}
// If we're about to run out of space, then cut the word off and start a new one
if (self->partWordBufferIndex >= PART_WORD_BUFFER_SIZE - 2) {
self->partWordBuffer[self->partWordBufferIndex] = '\0';
self->currentTextBlock->addWord(replaceHtmlEntities(self->partWordBuffer), self->boldUntilDepth < self->depth,
self->italicUntilDepth < self->depth);
self->partWordBufferIndex = 0;
}
self->partWordBuffer[self->partWordBufferIndex++] = s[i];
}
}
void XMLCALL EpubHtmlParserSlim::endElement(void* userData, const XML_Char* name) {
auto* self = static_cast<EpubHtmlParserSlim*>(userData);
(void)name;
if (self->partWordBufferIndex > 0) {
// Only flush out part word buffer if we're closing a block tag or are at the top of the HTML file.
// We don't want to flush out content when closing inline tags like <span>.
// Currently this also flushes out on closing <b> and <i> tags, but they are line tags so that shouldn't happen,
// text styling needs to be overhauled to fix it.
const bool shouldBreakText =
matches(name, BLOCK_TAGS, NUM_BLOCK_TAGS) || matches(name, HEADER_TAGS, NUM_HEADER_TAGS) ||
matches(name, BOLD_TAGS, NUM_BOLD_TAGS) || matches(name, ITALIC_TAGS, NUM_ITALIC_TAGS) || self->depth == 1;
if (shouldBreakText) {
self->partWordBuffer[self->partWordBufferIndex] = '\0';
self->currentTextBlock->addWord(replaceHtmlEntities(self->partWordBuffer), self->boldUntilDepth < self->depth,
self->italicUntilDepth < self->depth);
self->partWordBufferIndex = 0;
}
}
self->depth -= 1;
// Leaving skip
if (self->skipUntilDepth == self->depth) {
self->skipUntilDepth = INT_MAX;
}
// Leaving bold
if (self->boldUntilDepth == self->depth) {
self->boldUntilDepth = INT_MAX;
}
// Leaving italic
if (self->italicUntilDepth == self->depth) {
self->italicUntilDepth = INT_MAX;
}
}
bool EpubHtmlParserSlim::parseAndBuildPages() {
startNewTextBlock(JUSTIFIED);
const XML_Parser parser = XML_ParserCreate(nullptr);
int done;
if (!parser) {
Serial.println("Couldn't allocate memory for parser");
return false;
}
XML_SetUserData(parser, this);
XML_SetElementHandler(parser, startElement, endElement);
XML_SetCharacterDataHandler(parser, characterData);
FILE* file = fopen(filepath, "r");
if (!file) {
Serial.printf("Couldn't open file %s\n", filepath);
XML_ParserFree(parser);
return false;
}
do {
void* const buf = XML_GetBuffer(parser, 1024);
if (!buf) {
Serial.println("Couldn't allocate memory for buffer");
XML_ParserFree(parser);
fclose(file);
return false;
}
const size_t len = fread(buf, 1, 1024, file);
if (ferror(file)) {
Serial.println("Read error");
XML_ParserFree(parser);
fclose(file);
return false;
}
done = feof(file);
if (XML_ParseBuffer(parser, static_cast<int>(len), done) == XML_STATUS_ERROR) {
Serial.printf("Parse error at line %lu:\n%s\n", XML_GetCurrentLineNumber(parser),
XML_ErrorString(XML_GetErrorCode(parser)));
XML_ParserFree(parser);
fclose(file);
return false;
}
} while (!done);
XML_ParserFree(parser);
fclose(file);
// Process last page if there is still text
if (currentTextBlock) {
makePages();
completePageFn(currentPage);
currentPage = nullptr;
delete currentTextBlock;
currentTextBlock = nullptr;
}
return true;
}
void EpubHtmlParserSlim::makePages() {
if (!currentTextBlock) {
Serial.println("!! No text block to make pages for !!");
return;
}
if (!currentPage) {
currentPage = new Page();
}
const int lineHeight = renderer.getLineHeight();
const int pageHeight = renderer.getPageHeight();
// Long running task, make sure to let other things happen
vTaskDelay(1);
if (currentTextBlock->getType() == TEXT_BLOCK) {
const auto lines = currentTextBlock->splitIntoLines(renderer);
for (const auto line : lines) {
if (currentPage->nextY + lineHeight > pageHeight) {
completePageFn(currentPage);
currentPage = new Page();
}
currentPage->elements.push_back(new PageLine(line, currentPage->nextY));
currentPage->nextY += lineHeight;
}
// add some extra line between blocks
currentPage->nextY += lineHeight / 2;
}
// TODO: Image block support
// if (block->getType() == BlockType::IMAGE_BLOCK) {
// ImageBlock *imageBlock = (ImageBlock *)block;
// if (y + imageBlock->height > page_height) {
// pages.push_back(new Page());
// y = 0;
// }
// pages.back()->elements.push_back(new PageImage(imageBlock, y));
// y += imageBlock->height;
// }
}
+42
View File
@@ -0,0 +1,42 @@
#pragma once
#include <expat.h>
#include <limits.h>
#include <functional>
#include "blocks/TextBlock.h"
class Page;
class EpdRenderer;
#define PART_WORD_BUFFER_SIZE 200
class EpubHtmlParserSlim {
const char* filepath;
EpdRenderer& renderer;
std::function<void(Page*)> completePageFn;
int depth = 0;
int skipUntilDepth = INT_MAX;
int boldUntilDepth = INT_MAX;
int italicUntilDepth = INT_MAX;
// If we encounter words longer than this, but this is pretty large
char partWordBuffer[PART_WORD_BUFFER_SIZE] = {};
int partWordBufferIndex = 0;
TextBlock* currentTextBlock = nullptr;
Page* currentPage = nullptr;
void startNewTextBlock(BLOCK_STYLE style);
void makePages();
// XML callbacks
static void XMLCALL startElement(void* userData, const XML_Char* name, const XML_Char** atts);
static void XMLCALL characterData(void* userData, const XML_Char* s, int len);
static void XMLCALL endElement(void* userData, const XML_Char* name);
public:
explicit EpubHtmlParserSlim(const char* filepath, EpdRenderer& renderer,
const std::function<void(Page*)>& completePageFn)
: filepath(filepath), renderer(renderer), completePageFn(completePageFn) {}
~EpubHtmlParserSlim() = default;
bool parseAndBuildPages();
};
+2 -2
View File
@@ -3,7 +3,7 @@
#include <HardwareSerial.h> #include <HardwareSerial.h>
#include <Serialization.h> #include <Serialization.h>
void PageLine::render(EpdRenderer* renderer) { block->render(renderer, 0, yPos); } void PageLine::render(EpdRenderer& renderer) { block->render(renderer, 0, yPos); }
void PageLine::serialize(std::ostream& os) { void PageLine::serialize(std::ostream& os) {
serialization::writePod(os, yPos); serialization::writePod(os, yPos);
@@ -20,7 +20,7 @@ PageLine* PageLine::deserialize(std::istream& is) {
return new PageLine(tb, yPos); return new PageLine(tb, yPos);
} }
void Page::render(EpdRenderer* renderer) const { void Page::render(EpdRenderer& renderer) const {
const auto start = millis(); const auto start = millis();
for (const auto element : elements) { for (const auto element : elements) {
element->render(renderer); element->render(renderer);
+3 -3
View File
@@ -11,7 +11,7 @@ class PageElement {
int yPos; int yPos;
explicit PageElement(const int yPos) : yPos(yPos) {} explicit PageElement(const int yPos) : yPos(yPos) {}
virtual ~PageElement() = default; virtual ~PageElement() = default;
virtual void render(EpdRenderer* renderer) = 0; virtual void render(EpdRenderer& renderer) = 0;
virtual void serialize(std::ostream& os) = 0; virtual void serialize(std::ostream& os) = 0;
}; };
@@ -22,7 +22,7 @@ class PageLine final : public PageElement {
public: public:
PageLine(const TextBlock* block, const int yPos) : PageElement(yPos), block(block) {} PageLine(const TextBlock* block, const int yPos) : PageElement(yPos), block(block) {}
~PageLine() override { delete block; } ~PageLine() override { delete block; }
void render(EpdRenderer* renderer) override; void render(EpdRenderer& renderer) override;
void serialize(std::ostream& os) override; void serialize(std::ostream& os) override;
static PageLine* deserialize(std::istream& is); static PageLine* deserialize(std::istream& is);
}; };
@@ -32,7 +32,7 @@ class Page {
int nextY = 0; int nextY = 0;
// the list of block index and line numbers on this page // the list of block index and line numbers on this page
std::vector<PageElement*> elements; std::vector<PageElement*> elements;
void render(EpdRenderer* renderer) const; void render(EpdRenderer& renderer) const;
~Page() { ~Page() {
for (const auto element : elements) { for (const auto element : elements) {
delete element; delete element;
+44 -43
View File
@@ -5,15 +5,16 @@
#include <fstream> #include <fstream>
#include "EpubHtmlParser.h" #include "EpubHtmlParserSlim.h"
#include "Page.h" #include "Page.h"
#include "Serialization.h"
constexpr uint8_t SECTION_FILE_VERSION = 2;
void Section::onPageComplete(const Page* page) { void Section::onPageComplete(const Page* page) {
Serial.printf("Page %d complete\n", pageCount); Serial.printf("Page %d complete - free mem: %lu\n", pageCount, ESP.getFreeHeap());
const auto filePath = cachePath + "/page_" + std::to_string(pageCount) + ".bin"; const auto filePath = cachePath + "/page_" + std::to_string(pageCount) + ".bin";
// TODO can this be removed?
SD.open(filePath.c_str(), FILE_WRITE).close();
std::ofstream outputFile("/sd" + filePath); std::ofstream outputFile("/sd" + filePath);
page->serialize(outputFile); page->serialize(outputFile);
@@ -23,7 +24,14 @@ void Section::onPageComplete(const Page* page) {
delete page; delete page;
} }
bool Section::hasCache() { void Section::writeCacheMetadata() const {
std::ofstream outputFile(("/sd" + cachePath + "/section.bin").c_str());
serialization::writePod(outputFile, SECTION_FILE_VERSION);
serialization::writePod(outputFile, pageCount);
outputFile.close();
}
bool Section::loadCacheMetadata() {
if (!SD.exists(cachePath.c_str())) { if (!SD.exists(cachePath.c_str())) {
return false; return false;
} }
@@ -33,14 +41,18 @@ bool Section::hasCache() {
return false; return false;
} }
File sectionFile = SD.open(sectionFilePath.c_str(), FILE_READ); std::ifstream inputFile(("/sd" + sectionFilePath).c_str());
uint8_t pageCountBytes[2] = {0, 0}; uint8_t version;
sectionFile.read(pageCountBytes, 2); serialization::readPod(inputFile, version);
sectionFile.close(); if (version != SECTION_FILE_VERSION) {
inputFile.close();
pageCount = pageCountBytes[0] + (pageCountBytes[1] << 8); SD.remove(sectionFilePath.c_str());
Serial.printf("Section state file: Unknown version %u\n", version);
return false;
}
serialization::readPod(inputFile, pageCount);
inputFile.close();
Serial.printf("Loaded cache: %d pages\n", pageCount); Serial.printf("Loaded cache: %d pages\n", pageCount);
return true; return true;
} }
@@ -52,52 +64,41 @@ void Section::setupCacheDir() const {
void Section::clearCache() const { SD.rmdir(cachePath.c_str()); } void Section::clearCache() const { SD.rmdir(cachePath.c_str()); }
bool Section::persistPageDataToSD() { bool Section::persistPageDataToSD() {
size_t size = 0; const auto localPath = epub->getSpineItem(spineIndex);
auto localPath = epub->getSpineItem(spineIndex);
const auto html = epub->getItemContents(epub->getSpineItem(spineIndex), &size); // TODO: Should we get rid of this file all together?
if (!html) { // It currently saves us a bit of memory by allowing for all the inflation bits to be released
Serial.println("Failed to read item contents"); // before loading the XML parser
return false;
}
// TODO: Would love to stream this through an XML visitor
const auto tmpHtmlPath = epub->getCachePath() + "/.tmp_" + std::to_string(spineIndex) + ".html"; const auto tmpHtmlPath = epub->getCachePath() + "/.tmp_" + std::to_string(spineIndex) + ".html";
File f = SD.open(tmpHtmlPath.c_str(), FILE_WRITE); File f = SD.open(tmpHtmlPath.c_str(), FILE_WRITE, true);
const auto written = f.write(html, size); bool success = epub->readItemContentsToStream(localPath, f, 1024);
f.close(); f.close();
free(html);
Serial.printf("Wrote %d bytes to %s\n", written, tmpHtmlPath.c_str()); if (!success) {
Serial.println("Failed to stream item contents");
if (size != written) {
Serial.println("Failed to inflate section contents to SD");
SD.remove(tmpHtmlPath.c_str());
return false; return false;
} }
Serial.printf("Streamed HTML to %s\n", tmpHtmlPath.c_str());
const auto sdTmpHtmlPath = "/sd" + tmpHtmlPath; const auto sdTmpHtmlPath = "/sd" + tmpHtmlPath;
auto visitor =
EpubHtmlParser(sdTmpHtmlPath.c_str(), renderer, [this](const Page* page) { this->onPageComplete(page); });
// TODO: Come back and see if mem used here can be lowered? auto visitor =
const bool success = visitor.parseAndBuildPages(); EpubHtmlParserSlim(sdTmpHtmlPath.c_str(), renderer, [this](const Page* page) { this->onPageComplete(page); });
success = visitor.parseAndBuildPages();
SD.remove(tmpHtmlPath.c_str()); SD.remove(tmpHtmlPath.c_str());
if (!success) { if (!success) {
Serial.println("Failed to parse and build pages"); Serial.println("Failed to parse and build pages");
return false; return false;
} }
File sectionFile = SD.open((cachePath + "/section.bin").c_str(), FILE_WRITE, true); writeCacheMetadata();
const uint8_t pageCountBytes[2] = {static_cast<uint8_t>(pageCount & 0xFF),
static_cast<uint8_t>((pageCount >> 8) & 0xFF)};
sectionFile.write(pageCountBytes, 2);
sectionFile.close();
return true; return true;
} }
void Section::renderPage() { void Section::renderPage() const {
if (0 <= currentPage && currentPage < pageCount) { if (0 <= currentPage && currentPage < pageCount) {
const auto filePath = "/sd" + cachePath + "/page_" + std::to_string(currentPage) + ".bin"; const auto filePath = "/sd" + cachePath + "/page_" + std::to_string(currentPage) + ".bin";
std::ifstream inputFile(filePath); std::ifstream inputFile(filePath);
@@ -107,11 +108,11 @@ void Section::renderPage() {
delete p; delete p;
} else if (pageCount == 0) { } else if (pageCount == 0) {
Serial.println("No pages to render"); Serial.println("No pages to render");
const int width = renderer->getTextWidth("Empty chapter", true); const int width = renderer.getTextWidth("Empty chapter", BOLD);
renderer->drawText((renderer->getPageWidth() - width) / 2, 300, "Empty chapter", true); renderer.drawText((renderer.getPageWidth() - width) / 2, 300, "Empty chapter", 1, BOLD);
} else { } else {
Serial.printf("Page out of bounds: %d (max %d)\n", currentPage, pageCount); Serial.printf("Page out of bounds: %d (max %d)\n", currentPage, pageCount);
const int width = renderer->getTextWidth("Out of bounds", true); const int width = renderer.getTextWidth("Out of bounds", BOLD);
renderer->drawText((renderer->getPageWidth() - width) / 2, 300, "Out of bounds", true); renderer.drawText((renderer.getPageWidth() - width) / 2, 300, "Out of bounds", 1, BOLD);
} }
} }
+5 -4
View File
@@ -7,7 +7,7 @@ class EpdRenderer;
class Section { class Section {
Epub* epub; Epub* epub;
const int spineIndex; const int spineIndex;
EpdRenderer* renderer; EpdRenderer& renderer;
std::string cachePath; std::string cachePath;
void onPageComplete(const Page* page); void onPageComplete(const Page* page);
@@ -16,14 +16,15 @@ class Section {
int pageCount = 0; int pageCount = 0;
int currentPage = 0; int currentPage = 0;
explicit Section(Epub* epub, const int spineIndex, EpdRenderer* renderer) explicit Section(Epub* epub, const int spineIndex, EpdRenderer& renderer)
: epub(epub), spineIndex(spineIndex), renderer(renderer) { : epub(epub), spineIndex(spineIndex), renderer(renderer) {
cachePath = epub->getCachePath() + "/" + std::to_string(spineIndex); cachePath = epub->getCachePath() + "/" + std::to_string(spineIndex);
} }
~Section() = default; ~Section() = default;
bool hasCache(); void writeCacheMetadata() const;
bool loadCacheMetadata();
void setupCacheDir() const; void setupCacheDir() const;
void clearCache() const; void clearCache() const;
bool persistPageDataToSD(); bool persistPageDataToSD();
void renderPage(); void renderPage() const;
}; };
+1 -1
View File
@@ -8,7 +8,7 @@ typedef enum { TEXT_BLOCK, IMAGE_BLOCK } BlockType;
class Block { class Block {
public: public:
virtual ~Block() = default; virtual ~Block() = default;
virtual void layout(EpdRenderer* renderer) = 0; virtual void layout(EpdRenderer& renderer) = 0;
virtual BlockType getType() = 0; virtual BlockType getType() = 0;
virtual bool isEmpty() = 0; virtual bool isEmpty() = 0;
virtual void finish() {} virtual void finish() {}
+28 -45
View File
@@ -3,50 +3,17 @@
#include <EpdRenderer.h> #include <EpdRenderer.h>
#include <Serialization.h> #include <Serialization.h>
static bool isWhitespace(const char c) { return c == ' ' || c == '\r' || c == '\n'; } void TextBlock::addWord(const std::string& word, const bool is_bold, const bool is_italic) {
if (word.length() == 0) return;
// move past anything that should be considered part of a work words.push_back(word);
static int skipWord(const std::string& text, int index, const int length) { wordStyles.push_back((is_bold ? BOLD_SPAN : 0) | (is_italic ? ITALIC_SPAN : 0));
while (index < length && !isWhitespace(text[index])) {
index++;
}
return index;
} }
// skip past any white space characters std::list<TextBlock*> TextBlock::splitIntoLines(const EpdRenderer& renderer) {
static int skipWhitespace(const std::string& html, int index, const int length) {
while (index < length && isWhitespace(html[index])) {
index++;
}
return index;
}
void TextBlock::addSpan(const std::string& span, const bool is_bold, const bool is_italic) {
// adding a span to text block
// make a copy of the text as we'll modify it
const int length = span.length();
// const auto text = new char[length + 1];
// strcpy(text, span);
// work out where each word is in the span
int index = 0;
while (index < length) {
// skip past any whitespace to the start of a word
index = skipWhitespace(span, index, length);
const int wordStart = index;
// find the end of the word
index = skipWord(span, index, length);
const int wordLength = index - wordStart;
if (wordLength > 0) {
words.push_back(span.substr(wordStart, wordLength));
wordStyles.push_back((is_bold ? BOLD_SPAN : 0) | (is_italic ? ITALIC_SPAN : 0));
}
}
}
std::list<TextBlock*> TextBlock::splitIntoLines(const EpdRenderer* renderer) {
const int totalWordCount = words.size(); const int totalWordCount = words.size();
const int pageWidth = renderer->getPageWidth(); const int pageWidth = renderer.getPageWidth();
const int spaceWidth = renderer->getSpaceWidth(); const int spaceWidth = renderer.getSpaceWidth();
words.shrink_to_fit(); words.shrink_to_fit();
wordStyles.shrink_to_fit(); wordStyles.shrink_to_fit();
@@ -56,7 +23,17 @@ std::list<TextBlock*> TextBlock::splitIntoLines(const EpdRenderer* renderer) {
uint16_t wordWidths[totalWordCount]; uint16_t wordWidths[totalWordCount];
for (int i = 0; i < words.size(); i++) { for (int i = 0; i < words.size(); i++) {
// measure the word // measure the word
const int width = renderer->getTextWidth(words[i].c_str(), wordStyles[i] & BOLD_SPAN, wordStyles[i] & ITALIC_SPAN); EpdFontStyle fontStyle = REGULAR;
if (wordStyles[i] & BOLD_SPAN) {
if (wordStyles[i] & ITALIC_SPAN) {
fontStyle = BOLD_ITALIC;
} else {
fontStyle = BOLD;
}
} else if (wordStyles[i] & ITALIC_SPAN) {
fontStyle = ITALIC;
}
const int width = renderer.getTextWidth(words[i].c_str(), fontStyle);
wordWidths[i] = width; wordWidths[i] = width;
} }
@@ -177,12 +154,18 @@ std::list<TextBlock*> TextBlock::splitIntoLines(const EpdRenderer* renderer) {
return lines; return lines;
} }
void TextBlock::render(const EpdRenderer* renderer, const int x, const int y) const { void TextBlock::render(const EpdRenderer& renderer, const int x, const int y) const {
for (int i = 0; i < words.size(); i++) { for (int i = 0; i < words.size(); i++) {
// get the style
const uint8_t wordStyle = wordStyles[i];
// render the word // render the word
renderer->drawText(x + wordXpos[i], y, words[i].c_str(), wordStyle & BOLD_SPAN, wordStyle & ITALIC_SPAN); EpdFontStyle fontStyle = REGULAR;
if (wordStyles[i] & BOLD_SPAN && wordStyles[i] & ITALIC_SPAN) {
fontStyle = BOLD_ITALIC;
} else if (wordStyles[i] & BOLD_SPAN) {
fontStyle = BOLD;
} else if (wordStyles[i] & ITALIC_SPAN) {
fontStyle = ITALIC;
}
renderer.drawText(x + wordXpos[i], y, words[i].c_str(), 1, fontStyle);
} }
} }
+6 -6
View File
@@ -30,20 +30,20 @@ class TextBlock final : public Block {
BLOCK_STYLE style; BLOCK_STYLE style;
public: public:
void addSpan(const std::string& span, bool is_bold, bool is_italic);
explicit TextBlock(const BLOCK_STYLE style) : style(style) {} explicit TextBlock(const BLOCK_STYLE style) : style(style) {}
explicit TextBlock(const std::vector<std::string>& words, const std::vector<uint16_t>& word_xpos, explicit TextBlock(const std::vector<std::string>& words, const std::vector<uint16_t>& word_xpos,
// the styles of each word // the styles of each word
const std::vector<uint8_t>& word_styles, const BLOCK_STYLE style) const std::vector<uint8_t>& word_styles, const BLOCK_STYLE style)
: words(words), wordXpos(word_xpos), wordStyles(word_styles), style(style) {} : words(words), wordXpos(word_xpos), wordStyles(word_styles), style(style) {}
~TextBlock() override = default; ~TextBlock() override = default;
void set_style(const BLOCK_STYLE style) { this->style = style; } void addWord(const std::string& word, bool is_bold, bool is_italic);
BLOCK_STYLE get_style() const { return style; } void setStyle(const BLOCK_STYLE style) { this->style = style; }
BLOCK_STYLE getStyle() const { return style; }
bool isEmpty() override { return words.empty(); } bool isEmpty() override { return words.empty(); }
void layout(EpdRenderer* renderer) override {}; void layout(EpdRenderer& renderer) override {};
// given a renderer works out where to break the words into lines // given a renderer works out where to break the words into lines
std::list<TextBlock*> splitIntoLines(const EpdRenderer* renderer); std::list<TextBlock*> splitIntoLines(const EpdRenderer& renderer);
void render(const EpdRenderer* renderer, int x, int y) const; void render(const EpdRenderer& renderer, int x, int y) const;
BlockType getType() override { return TEXT_BLOCK; } BlockType getType() override { return TEXT_BLOCK; }
void serialize(std::ostream& os) const; void serialize(std::ostream& os) const;
static TextBlock* deserialize(std::istream& is); static TextBlock* deserialize(std::istream& is);
+226 -59
View File
@@ -3,51 +3,37 @@
#include <HardwareSerial.h> #include <HardwareSerial.h>
#include <miniz.h> #include <miniz.h>
int libzInflateOneShot(const uint8_t* inputBuff, const size_t compSize, uint8_t* outputBuff, const size_t uncompSize) { bool inflateOneShot(const uint8_t* inputBuf, const size_t deflatedSize, uint8_t* outputBuf, const size_t inflatedSize) {
mz_stream pStream = { // Setup inflator
.next_in = inputBuff, const auto inflator = static_cast<tinfl_decompressor*>(malloc(sizeof(tinfl_decompressor)));
.avail_in = compSize, if (!inflator) {
.total_in = 0, Serial.println("Failed to allocate memory for inflator");
.next_out = outputBuff, return false;
.avail_out = uncompSize, }
.total_out = 0, memset(inflator, 0, sizeof(tinfl_decompressor));
}; tinfl_init(inflator);
int status = 0; size_t inBytes = deflatedSize;
status = mz_inflateInit2(&pStream, -MZ_DEFAULT_WINDOW_BITS); size_t outBytes = inflatedSize;
const tinfl_status status = tinfl_decompress(inflator, inputBuf, &inBytes, nullptr, outputBuf, &outBytes,
TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF);
free(inflator);
if (status != MZ_OK) { if (status != TINFL_STATUS_DONE) {
Serial.printf("inflateInit2 failed: %d\n", status); Serial.printf("tinfl_decompress() failed with status %d\n", status);
return status; return false;
} }
status = mz_inflate(&pStream, MZ_FINISH); return true;
if (status != MZ_STREAM_END) {
Serial.printf("inflate failed: %d\n", status);
return status;
}
status = mz_inflateEnd(&pStream);
if (status != MZ_OK) {
Serial.printf("inflateEnd failed: %d\n", status);
return status;
}
return status;
} }
char* ZipFile::readTextFileToMemory(const char* filename, size_t* size) const { bool ZipFile::loadFileStat(const char* filename, mz_zip_archive_file_stat* fileStat) const {
const auto data = readFileToMemory(filename, size, true);
return data ? reinterpret_cast<char*>(data) : nullptr;
}
uint8_t* ZipFile::readFileToMemory(const char* filename, size_t* size, bool trailingNullByte) const {
mz_zip_archive zipArchive = {}; mz_zip_archive zipArchive = {};
const bool status = mz_zip_reader_init_file(&zipArchive, filePath.c_str(), 0); const bool status = mz_zip_reader_init_file(&zipArchive, filePath.c_str(), 0);
if (!status) { if (!status) {
Serial.printf("mz_zip_reader_init_file() failed!\nError %s\n", mz_zip_get_error_string(zipArchive.m_last_error)); Serial.printf("mz_zip_reader_init_file() failed!\nError %s\n", mz_zip_get_error_string(zipArchive.m_last_error));
return nullptr; return false;
} }
// find the file // find the file
@@ -55,41 +41,57 @@ uint8_t* ZipFile::readFileToMemory(const char* filename, size_t* size, bool trai
if (!mz_zip_reader_locate_file_v2(&zipArchive, filename, nullptr, 0, &fileIndex)) { if (!mz_zip_reader_locate_file_v2(&zipArchive, filename, nullptr, 0, &fileIndex)) {
Serial.printf("Could not find file %s\n", filename); Serial.printf("Could not find file %s\n", filename);
mz_zip_reader_end(&zipArchive); mz_zip_reader_end(&zipArchive);
return nullptr; return false;
} }
mz_zip_archive_file_stat fileStat; if (!mz_zip_reader_file_stat(&zipArchive, fileIndex, fileStat)) {
if (!mz_zip_reader_file_stat(&zipArchive, fileIndex, &fileStat)) {
Serial.printf("mz_zip_reader_file_stat() failed!\nError %s\n", mz_zip_get_error_string(zipArchive.m_last_error)); Serial.printf("mz_zip_reader_file_stat() failed!\nError %s\n", mz_zip_get_error_string(zipArchive.m_last_error));
mz_zip_reader_end(&zipArchive); mz_zip_reader_end(&zipArchive);
return nullptr; return false;
} }
mz_zip_reader_end(&zipArchive); mz_zip_reader_end(&zipArchive);
return true;
}
uint8_t pLocalHeader[30]; long ZipFile::getDataOffset(const mz_zip_archive_file_stat& fileStat) const {
uint64_t fileOffset = fileStat.m_local_header_ofs; constexpr auto localHeaderSize = 30;
// Reopen the file to manual read out delated bytes uint8_t pLocalHeader[localHeaderSize];
FILE* file = fopen(filePath.c_str(), "rb"); const uint64_t fileOffset = fileStat.m_local_header_ofs;
FILE* file = fopen(filePath.c_str(), "r");
fseek(file, fileOffset, SEEK_SET); fseek(file, fileOffset, SEEK_SET);
const size_t read = fread(pLocalHeader, 1, localHeaderSize, file);
fclose(file);
const size_t read = fread(pLocalHeader, 1, 30, file); if (read != localHeaderSize) {
if (read != 30) {
Serial.println("Something went wrong reading the local header"); Serial.println("Something went wrong reading the local header");
fclose(file); return -1;
return nullptr;
} }
if (pLocalHeader[0] + (pLocalHeader[1] << 8) + (pLocalHeader[2] << 16) + (pLocalHeader[3] << 24) != if (pLocalHeader[0] + (pLocalHeader[1] << 8) + (pLocalHeader[2] << 16) + (pLocalHeader[3] << 24) !=
0x04034b50 /* MZ_ZIP_LOCAL_DIR_HEADER_SIG */) { 0x04034b50 /* MZ_ZIP_LOCAL_DIR_HEADER_SIG */) {
Serial.println("Not a valid zip file header"); Serial.println("Not a valid zip file header");
fclose(file); return -1;
return nullptr;
} }
const uint16_t filenameLength = pLocalHeader[26] + (pLocalHeader[27] << 8); const uint16_t filenameLength = pLocalHeader[26] + (pLocalHeader[27] << 8);
const uint16_t extraOffset = pLocalHeader[28] + (pLocalHeader[29] << 8); const uint16_t extraOffset = pLocalHeader[28] + (pLocalHeader[29] << 8);
fileOffset += 30 + filenameLength + extraOffset; return fileOffset + localHeaderSize + filenameLength + extraOffset;
}
uint8_t* ZipFile::readFileToMemory(const char* filename, size_t* size, const bool trailingNullByte) const {
mz_zip_archive_file_stat fileStat;
if (!loadFileStat(filename, &fileStat)) {
return nullptr;
}
const long fileOffset = getDataOffset(fileStat);
if (fileOffset < 0) {
return nullptr;
}
FILE* file = fopen(filePath.c_str(), "rb");
fseek(file, fileOffset, SEEK_SET); fseek(file, fileOffset, SEEK_SET);
const auto deflatedDataSize = static_cast<size_t>(fileStat.m_comp_size); const auto deflatedDataSize = static_cast<size_t>(fileStat.m_comp_size);
@@ -97,15 +99,19 @@ uint8_t* ZipFile::readFileToMemory(const char* filename, size_t* size, bool trai
const auto dataSize = trailingNullByte ? inflatedDataSize + 1 : inflatedDataSize; const auto dataSize = trailingNullByte ? inflatedDataSize + 1 : inflatedDataSize;
const auto data = static_cast<uint8_t*>(malloc(dataSize)); const auto data = static_cast<uint8_t*>(malloc(dataSize));
if (!fileStat.m_method) { if (fileStat.m_method == MZ_NO_COMPRESSION) {
// no deflation, just read content // no deflation, just read content
const size_t dataRead = fread(data, 1, inflatedDataSize, file); const size_t dataRead = fread(data, 1, inflatedDataSize, file);
fclose(file); fclose(file);
if (dataRead != inflatedDataSize) { if (dataRead != inflatedDataSize) {
Serial.println("Failed to read data"); Serial.println("Failed to read data");
free(data);
return nullptr; return nullptr;
} }
} else {
// Continue out of block with data set
} else if (fileStat.m_method == MZ_DEFLATED) {
// Read out deflated content from file // Read out deflated content from file
const auto deflatedData = static_cast<uint8_t*>(malloc(deflatedDataSize)); const auto deflatedData = static_cast<uint8_t*>(malloc(deflatedDataSize));
if (deflatedData == nullptr) { if (deflatedData == nullptr) {
@@ -116,25 +122,186 @@ uint8_t* ZipFile::readFileToMemory(const char* filename, size_t* size, bool trai
const size_t dataRead = fread(deflatedData, 1, deflatedDataSize, file); const size_t dataRead = fread(deflatedData, 1, deflatedDataSize, file);
fclose(file); fclose(file);
if (dataRead != deflatedDataSize) { if (dataRead != deflatedDataSize) {
Serial.printf("Failed to read data, expected %d got %d\n", deflatedDataSize, dataRead); Serial.printf("Failed to read data, expected %d got %d\n", deflatedDataSize, dataRead);
free(deflatedData); free(deflatedData);
free(data);
return nullptr; return nullptr;
} }
const int result = libzInflateOneShot(deflatedData, deflatedDataSize, data, inflatedDataSize); bool success = inflateOneShot(deflatedData, deflatedDataSize, data, inflatedDataSize);
free(deflatedData); free(deflatedData);
if (result != MZ_OK) {
if (!success) {
Serial.println("Failed to inflate file"); Serial.println("Failed to inflate file");
free(data);
return nullptr; return nullptr;
} }
// Continue out of block with data set
} else {
Serial.println("Unsupported compression method");
fclose(file);
return nullptr;
} }
if (trailingNullByte) { if (trailingNullByte) data[inflatedDataSize] = '\0';
data[inflatedDataSize] = '\0'; if (size) *size = inflatedDataSize;
}
if (size) {
*size = inflatedDataSize;
}
return data; return data;
} }
bool ZipFile::readFileToStream(const char* filename, Print& out, const size_t chunkSize) const {
mz_zip_archive_file_stat fileStat;
if (!loadFileStat(filename, &fileStat)) {
return false;
}
const long fileOffset = getDataOffset(fileStat);
if (fileOffset < 0) {
return false;
}
FILE* file = fopen(filePath.c_str(), "rb");
fseek(file, fileOffset, SEEK_SET);
const auto deflatedDataSize = static_cast<size_t>(fileStat.m_comp_size);
const auto inflatedDataSize = static_cast<size_t>(fileStat.m_uncomp_size);
if (fileStat.m_method == MZ_NO_COMPRESSION) {
// no deflation, just read content
const auto buffer = static_cast<uint8_t*>(malloc(chunkSize));
if (!buffer) {
Serial.println("Failed to allocate memory for buffer");
fclose(file);
return false;
}
size_t remaining = inflatedDataSize;
while (remaining > 0) {
const size_t dataRead = fread(buffer, 1, remaining < chunkSize ? remaining : chunkSize, file);
if (dataRead == 0) {
Serial.println("Could not read more bytes");
free(buffer);
fclose(file);
return false;
}
out.write(buffer, dataRead);
remaining -= dataRead;
}
fclose(file);
free(buffer);
return true;
}
if (fileStat.m_method == MZ_DEFLATED) {
// Setup inflator
const auto inflator = static_cast<tinfl_decompressor*>(malloc(sizeof(tinfl_decompressor)));
if (!inflator) {
Serial.println("Failed to allocate memory for inflator");
fclose(file);
return false;
}
memset(inflator, 0, sizeof(tinfl_decompressor));
tinfl_init(inflator);
// Setup file read buffer
const auto fileReadBuffer = static_cast<uint8_t*>(malloc(chunkSize));
if (!fileReadBuffer) {
Serial.println("Failed to allocate memory for zip file read buffer");
free(inflator);
fclose(file);
return false;
}
const auto outputBuffer = static_cast<uint8_t*>(malloc(TINFL_LZ_DICT_SIZE));
if (!outputBuffer) {
Serial.println("Failed to allocate memory for dictionary");
free(inflator);
free(fileReadBuffer);
fclose(file);
return false;
}
memset(outputBuffer, 0, TINFL_LZ_DICT_SIZE);
size_t fileRemainingBytes = deflatedDataSize;
size_t processedOutputBytes = 0;
size_t fileReadBufferFilledBytes = 0;
size_t fileReadBufferCursor = 0;
size_t outputCursor = 0; // Current offset in the circular dictionary
while (true) {
// Load more compressed bytes when needed
if (fileReadBufferCursor >= fileReadBufferFilledBytes) {
if (fileRemainingBytes == 0) {
// Should not be hit, but a safe protection
break; // EOF
}
fileReadBufferFilledBytes =
fread(fileReadBuffer, 1, fileRemainingBytes < chunkSize ? fileRemainingBytes : chunkSize, file);
fileRemainingBytes -= fileReadBufferFilledBytes;
fileReadBufferCursor = 0;
if (fileReadBufferFilledBytes == 0) {
// Bad read
break; // EOF
}
}
// Available bytes in fileReadBuffer to process
size_t inBytes = fileReadBufferFilledBytes - fileReadBufferCursor;
// Space remaining in outputBuffer
size_t outBytes = TINFL_LZ_DICT_SIZE - outputCursor;
const tinfl_status status = tinfl_decompress(inflator, fileReadBuffer + fileReadBufferCursor, &inBytes,
outputBuffer, outputBuffer + outputCursor, &outBytes,
fileRemainingBytes > 0 ? TINFL_FLAG_HAS_MORE_INPUT : 0);
// Update input position
fileReadBufferCursor += inBytes;
// Write output chunk
if (outBytes > 0) {
processedOutputBytes += outBytes;
out.write(outputBuffer + outputCursor, outBytes);
// Update output position in buffer (with wraparound)
outputCursor = (outputCursor + outBytes) & (TINFL_LZ_DICT_SIZE - 1);
}
Serial.printf("Decompressing - %d/%d deflated into %d/%d inflated\n", deflatedDataSize - fileRemainingBytes,
deflatedDataSize, processedOutputBytes, inflatedDataSize);
if (status < 0) {
Serial.printf("tinfl_decompress() failed with status %d\n", status);
fclose(file);
free(outputBuffer);
free(fileReadBuffer);
free(inflator);
return false;
}
if (status == TINFL_STATUS_DONE) {
Serial.println("Decompression finished");
fclose(file);
free(inflator);
free(fileReadBuffer);
free(outputBuffer);
return true;
}
}
// If we get here, EOF reached without TINFL_STATUS_DONE
Serial.println("Unexpected EOF");
fclose(file);
free(outputBuffer);
free(fileReadBuffer);
free(inflator);
return false;
}
Serial.println("Unsupported compression method");
return false;
}
+8 -1
View File
@@ -1,12 +1,19 @@
#pragma once #pragma once
#include <Print.h>
#include <functional>
#include <string> #include <string>
#include "miniz.h"
class ZipFile { class ZipFile {
std::string filePath; std::string filePath;
bool loadFileStat(const char* filename, mz_zip_archive_file_stat* fileStat) const;
long getDataOffset(const mz_zip_archive_file_stat& fileStat) const;
public: public:
explicit ZipFile(std::string filePath) : filePath(std::move(filePath)) {} explicit ZipFile(std::string filePath) : filePath(std::move(filePath)) {}
~ZipFile() = default; ~ZipFile() = default;
char* readTextFileToMemory(const char* filename, size_t* size = nullptr) const;
uint8_t* readFileToMemory(const char* filename, size_t* size = nullptr, bool trailingNullByte = false) const; uint8_t* readFileToMemory(const char* filename, size_t* size = nullptr, bool trailingNullByte = false) const;
bool readFileToStream(const char* filename, Print& out, size_t chunkSize) const;
}; };
+17
View File
@@ -0,0 +1,17 @@
Makefile
.libs
*.lo
Debug
Debug-w
Release
Release-w
expat.ncb
expat.opt
expat.plg
Debug_static
Debug-w_static
Release_static
Release-w_static
expat_static.plg
expatw.plg
expatw_static.plg
+87
View File
@@ -0,0 +1,87 @@
#
# __ __ _
# ___\ \/ /_ __ __ _| |_
# / _ \\ /| '_ \ / _` | __|
# | __// \| |_) | (_| | |_
# \___/_/\_\ .__/ \__,_|\__|
# |_| XML parser
#
# Copyright (c) 2017-2024 Sebastian Pipping <sebastian@pipping.org>
# Copyright (c) 2017 Tomasz Kłoczko <kloczek@fedoraproject.org>
# Copyright (c) 2019 David Loffredo <loffredo@steptools.com>
# Licensed under the MIT license:
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to permit
# persons to whom the Software is furnished to do so, subject to the
# following conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
# NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
# USE OR OTHER DEALINGS IN THE SOFTWARE.
include_HEADERS = \
../expat_config.h \
expat.h \
expat_external.h
lib_LTLIBRARIES = libexpat.la
if WITH_TESTS
noinst_LTLIBRARIES = libtestpat.la
endif
libexpat_la_LDFLAGS = \
@AM_LDFLAGS@ \
@LIBM@ \
-no-undefined \
-version-info @LIBCURRENT@:@LIBREVISION@:@LIBAGE@
libexpat_la_SOURCES = \
xmlparse.c \
xmltok.c \
xmlrole.c
if WITH_TESTS
libtestpat_la_CPPFLAGS = -DXML_TESTING
libtestpat_la_SOURCES = $(libexpat_la_SOURCES)
endif
doc_DATA = \
../AUTHORS \
../Changes
install-data-hook:
cd "$(DESTDIR)$(docdir)" && $(am__mv) Changes changelog
uninstall-local:
$(RM) "$(DESTDIR)$(docdir)/changelog"
EXTRA_DIST = \
ascii.h \
asciitab.h \
expat_external.h \
expat.h \
iasciitab.h \
internal.h \
latin1tab.h \
libexpat.def.cmake \
nametab.h \
siphash.h \
utf8tab.h \
winconfig.h \
xmlrole.h \
xmltok.h \
xmltok_impl.c \
xmltok_impl.h \
xmltok_ns.c
+123
View File
@@ -0,0 +1,123 @@
/*
__ __ _
___\ \/ /_ __ __ _| |_
/ _ \\ /| '_ \ / _` | __|
| __// \| |_) | (_| | |_
\___/_/\_\ .__/ \__,_|\__|
|_| XML parser
Copyright (c) 1999-2000 Thai Open Source Software Center Ltd
Copyright (c) 2000 Clark Cooper <coopercc@users.sourceforge.net>
Copyright (c) 2002 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
Copyright (c) 2007 Karl Waclawek <karl@waclawek.net>
Copyright (c) 2017 Sebastian Pipping <sebastian@pipping.org>
Licensed under the MIT license:
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit
persons to whom the Software is furnished to do so, subject to the
following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#define ASCII_A 0x41
#define ASCII_B 0x42
#define ASCII_C 0x43
#define ASCII_D 0x44
#define ASCII_E 0x45
#define ASCII_F 0x46
#define ASCII_G 0x47
#define ASCII_H 0x48
#define ASCII_I 0x49
#define ASCII_J 0x4A
#define ASCII_K 0x4B
#define ASCII_L 0x4C
#define ASCII_M 0x4D
#define ASCII_N 0x4E
#define ASCII_O 0x4F
#define ASCII_P 0x50
#define ASCII_Q 0x51
#define ASCII_R 0x52
#define ASCII_S 0x53
#define ASCII_T 0x54
#define ASCII_U 0x55
#define ASCII_V 0x56
#define ASCII_W 0x57
#define ASCII_X 0x58
#define ASCII_Y 0x59
#define ASCII_Z 0x5A
#define ASCII_a 0x61
#define ASCII_b 0x62
#define ASCII_c 0x63
#define ASCII_d 0x64
#define ASCII_e 0x65
#define ASCII_f 0x66
#define ASCII_g 0x67
#define ASCII_h 0x68
#define ASCII_i 0x69
#define ASCII_j 0x6A
#define ASCII_k 0x6B
#define ASCII_l 0x6C
#define ASCII_m 0x6D
#define ASCII_n 0x6E
#define ASCII_o 0x6F
#define ASCII_p 0x70
#define ASCII_q 0x71
#define ASCII_r 0x72
#define ASCII_s 0x73
#define ASCII_t 0x74
#define ASCII_u 0x75
#define ASCII_v 0x76
#define ASCII_w 0x77
#define ASCII_x 0x78
#define ASCII_y 0x79
#define ASCII_z 0x7A
#define ASCII_0 0x30
#define ASCII_1 0x31
#define ASCII_2 0x32
#define ASCII_3 0x33
#define ASCII_4 0x34
#define ASCII_5 0x35
#define ASCII_6 0x36
#define ASCII_7 0x37
#define ASCII_8 0x38
#define ASCII_9 0x39
#define ASCII_TAB 0x09
#define ASCII_SPACE 0x20
#define ASCII_EXCL 0x21
#define ASCII_QUOT 0x22
#define ASCII_AMP 0x26
#define ASCII_APOS 0x27
#define ASCII_MINUS 0x2D
#define ASCII_PERIOD 0x2E
#define ASCII_COLON 0x3A
#define ASCII_SEMI 0x3B
#define ASCII_LT 0x3C
#define ASCII_EQUALS 0x3D
#define ASCII_GT 0x3E
#define ASCII_LSQB 0x5B
#define ASCII_RSQB 0x5D
#define ASCII_UNDERSCORE 0x5F
#define ASCII_LPAREN 0x28
#define ASCII_RPAREN 0x29
#define ASCII_FF 0x0C
#define ASCII_SLASH 0x2F
#define ASCII_HASH 0x23
#define ASCII_PIPE 0x7C
#define ASCII_COMMA 0x2C
+66
View File
@@ -0,0 +1,66 @@
/*
__ __ _
___\ \/ /_ __ __ _| |_
/ _ \\ /| '_ \ / _` | __|
| __// \| |_) | (_| | |_
\___/_/\_\ .__/ \__,_|\__|
|_| XML parser
Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
Copyright (c) 2000 Clark Cooper <coopercc@users.sourceforge.net>
Copyright (c) 2002 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
Copyright (c) 2017 Sebastian Pipping <sebastian@pipping.org>
Licensed under the MIT license:
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit
persons to whom the Software is furnished to do so, subject to the
following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/* 0x00 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
/* 0x04 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
/* 0x08 */ BT_NONXML, BT_S, BT_LF, BT_NONXML,
/* 0x0C */ BT_NONXML, BT_CR, BT_NONXML, BT_NONXML,
/* 0x10 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
/* 0x14 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
/* 0x18 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
/* 0x1C */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
/* 0x20 */ BT_S, BT_EXCL, BT_QUOT, BT_NUM,
/* 0x24 */ BT_OTHER, BT_PERCNT, BT_AMP, BT_APOS,
/* 0x28 */ BT_LPAR, BT_RPAR, BT_AST, BT_PLUS,
/* 0x2C */ BT_COMMA, BT_MINUS, BT_NAME, BT_SOL,
/* 0x30 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT,
/* 0x34 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT,
/* 0x38 */ BT_DIGIT, BT_DIGIT, BT_COLON, BT_SEMI,
/* 0x3C */ BT_LT, BT_EQUALS, BT_GT, BT_QUEST,
/* 0x40 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX,
/* 0x44 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT,
/* 0x48 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0x4C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0x50 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0x54 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0x58 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_LSQB,
/* 0x5C */ BT_OTHER, BT_RSQB, BT_OTHER, BT_NMSTRT,
/* 0x60 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX,
/* 0x64 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT,
/* 0x68 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0x6C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0x70 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0x74 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0x78 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER,
/* 0x7C */ BT_VERBAR, BT_OTHER, BT_OTHER, BT_OTHER,
+1031
View File
File diff suppressed because it is too large Load Diff
View File
+163
View File
@@ -0,0 +1,163 @@
/*
__ __ _
___\ \/ /_ __ __ _| |_
/ _ \\ /| '_ \ / _` | __|
| __// \| |_) | (_| | |_
\___/_/\_\ .__/ \__,_|\__|
|_| XML parser
Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
Copyright (c) 2000 Clark Cooper <coopercc@users.sourceforge.net>
Copyright (c) 2000-2004 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
Copyright (c) 2001-2002 Greg Stein <gstein@users.sourceforge.net>
Copyright (c) 2002-2006 Karl Waclawek <karl@waclawek.net>
Copyright (c) 2016 Cristian Rodríguez <crrodriguez@opensuse.org>
Copyright (c) 2016-2019 Sebastian Pipping <sebastian@pipping.org>
Copyright (c) 2017 Rhodri James <rhodri@wildebeest.org.uk>
Copyright (c) 2018 Yury Gribov <tetra2005@gmail.com>
Licensed under the MIT license:
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit
persons to whom the Software is furnished to do so, subject to the
following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef Expat_External_INCLUDED
#define Expat_External_INCLUDED 1
/* External API definitions */
/* Expat tries very hard to make the API boundary very specifically
defined. There are two macros defined to control this boundary;
each of these can be defined before including this header to
achieve some different behavior, but doing so it not recommended or
tested frequently.
XMLCALL - The calling convention to use for all calls across the
"library boundary." This will default to cdecl, and
try really hard to tell the compiler that's what we
want.
XMLIMPORT - Whatever magic is needed to note that a function is
to be imported from a dynamically loaded library
(.dll, .so, or .sl, depending on your platform).
The XMLCALL macro was added in Expat 1.95.7. The only one which is
expected to be directly useful in client code is XMLCALL.
Note that on at least some Unix versions, the Expat library must be
compiled with the cdecl calling convention as the default since
system headers may assume the cdecl convention.
*/
#ifndef XMLCALL
#if defined(_MSC_VER)
#define XMLCALL __cdecl
#elif defined(__GNUC__) && defined(__i386) && !defined(__INTEL_COMPILER)
#define XMLCALL __attribute__((cdecl))
#else
/* For any platform which uses this definition and supports more than
one calling convention, we need to extend this definition to
declare the convention used on that platform, if it's possible to
do so.
If this is the case for your platform, please file a bug report
with information on how to identify your platform via the C
pre-processor and how to specify the same calling convention as the
platform's malloc() implementation.
*/
#define XMLCALL
#endif
#endif /* not defined XMLCALL */
#if !defined(XML_STATIC) && !defined(XMLIMPORT)
#ifndef XML_BUILDING_EXPAT
/* using Expat from an application */
#if defined(_MSC_VER) && !defined(__BEOS__) && !defined(__CYGWIN__)
#define XMLIMPORT __declspec(dllimport)
#endif
#endif
#endif /* not defined XML_STATIC */
#ifndef XML_ENABLE_VISIBILITY
#define XML_ENABLE_VISIBILITY 0
#endif
#if !defined(XMLIMPORT) && XML_ENABLE_VISIBILITY
#define XMLIMPORT __attribute__((visibility("default")))
#endif
/* If we didn't define it above, define it away: */
#ifndef XMLIMPORT
#define XMLIMPORT
#endif
#if defined(__GNUC__) && (__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 96))
#define XML_ATTR_MALLOC __attribute__((__malloc__))
#else
#define XML_ATTR_MALLOC
#endif
#if defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))
#define XML_ATTR_ALLOC_SIZE(x) __attribute__((__alloc_size__(x)))
#else
#define XML_ATTR_ALLOC_SIZE(x)
#endif
#define XMLPARSEAPI(type) XMLIMPORT type XMLCALL
#ifdef __cplusplus
extern "C" {
#endif
#ifdef XML_UNICODE_WCHAR_T
#ifndef XML_UNICODE
#define XML_UNICODE
#endif
#if defined(__SIZEOF_WCHAR_T__) && (__SIZEOF_WCHAR_T__ != 2)
#error "sizeof(wchar_t) != 2; Need -fshort-wchar for both Expat and libc"
#endif
#endif
#ifdef XML_UNICODE /* Information is UTF-16 encoded. */
#ifdef XML_UNICODE_WCHAR_T
typedef wchar_t XML_Char;
typedef wchar_t XML_LChar;
#else
typedef unsigned short XML_Char;
typedef char XML_LChar;
#endif /* XML_UNICODE_WCHAR_T */
#else /* Information is UTF-8 encoded. */
typedef char XML_Char;
typedef char XML_LChar;
#endif /* XML_UNICODE */
#ifdef XML_LARGE_SIZE /* Use large integers for file/stream positions. */
typedef long long XML_Index;
typedef unsigned long long XML_Size;
#else
typedef long XML_Index;
typedef unsigned long XML_Size;
#endif /* XML_LARGE_SIZE */
#ifdef __cplusplus
}
#endif
#endif /* not Expat_External_INCLUDED */
+67
View File
@@ -0,0 +1,67 @@
/*
__ __ _
___\ \/ /_ __ __ _| |_
/ _ \\ /| '_ \ / _` | __|
| __// \| |_) | (_| | |_
\___/_/\_\ .__/ \__,_|\__|
|_| XML parser
Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
Copyright (c) 2000 Clark Cooper <coopercc@users.sourceforge.net>
Copyright (c) 2002 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
Copyright (c) 2017 Sebastian Pipping <sebastian@pipping.org>
Licensed under the MIT license:
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit
persons to whom the Software is furnished to do so, subject to the
following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/* Like asciitab.h, except that 0xD has code BT_S rather than BT_CR */
/* 0x00 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
/* 0x04 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
/* 0x08 */ BT_NONXML, BT_S, BT_LF, BT_NONXML,
/* 0x0C */ BT_NONXML, BT_S, BT_NONXML, BT_NONXML,
/* 0x10 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
/* 0x14 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
/* 0x18 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
/* 0x1C */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
/* 0x20 */ BT_S, BT_EXCL, BT_QUOT, BT_NUM,
/* 0x24 */ BT_OTHER, BT_PERCNT, BT_AMP, BT_APOS,
/* 0x28 */ BT_LPAR, BT_RPAR, BT_AST, BT_PLUS,
/* 0x2C */ BT_COMMA, BT_MINUS, BT_NAME, BT_SOL,
/* 0x30 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT,
/* 0x34 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT,
/* 0x38 */ BT_DIGIT, BT_DIGIT, BT_COLON, BT_SEMI,
/* 0x3C */ BT_LT, BT_EQUALS, BT_GT, BT_QUEST,
/* 0x40 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX,
/* 0x44 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT,
/* 0x48 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0x4C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0x50 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0x54 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0x58 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_LSQB,
/* 0x5C */ BT_OTHER, BT_RSQB, BT_OTHER, BT_NMSTRT,
/* 0x60 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX,
/* 0x64 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT,
/* 0x68 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0x6C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0x70 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0x74 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0x78 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER,
/* 0x7C */ BT_VERBAR, BT_OTHER, BT_OTHER, BT_OTHER,
+187
View File
@@ -0,0 +1,187 @@
/* internal.h
Internal definitions used by Expat. This is not needed to compile
client code.
The following calling convention macros are defined for frequently
called functions:
FASTCALL - Used for those internal functions that have a simple
body and a low number of arguments and local variables.
PTRCALL - Used for functions called though function pointers.
PTRFASTCALL - Like PTRCALL, but for low number of arguments.
inline - Used for selected internal functions for which inlining
may improve performance on some platforms.
Note: Use of these macros is based on judgement, not hard rules,
and therefore subject to change.
__ __ _
___\ \/ /_ __ __ _| |_
/ _ \\ /| '_ \ / _` | __|
| __// \| |_) | (_| | |_
\___/_/\_\ .__/ \__,_|\__|
|_| XML parser
Copyright (c) 2002-2003 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
Copyright (c) 2002-2006 Karl Waclawek <karl@waclawek.net>
Copyright (c) 2003 Greg Stein <gstein@users.sourceforge.net>
Copyright (c) 2016-2025 Sebastian Pipping <sebastian@pipping.org>
Copyright (c) 2018 Yury Gribov <tetra2005@gmail.com>
Copyright (c) 2019 David Loffredo <loffredo@steptools.com>
Copyright (c) 2023-2024 Sony Corporation / Snild Dolkow <snild@sony.com>
Copyright (c) 2024 Taichi Haradaguchi <20001722@ymail.ne.jp>
Licensed under the MIT license:
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit
persons to whom the Software is furnished to do so, subject to the
following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#if defined(__GNUC__) && defined(__i386__) && !defined(__MINGW32__)
/* We'll use this version by default only where we know it helps.
regparm() generates warnings on Solaris boxes. See SF bug #692878.
Instability reported with egcs on a RedHat Linux 7.3.
Let's comment out:
#define FASTCALL __attribute__((stdcall, regparm(3)))
and let's try this:
*/
#define FASTCALL __attribute__((regparm(3)))
#define PTRFASTCALL __attribute__((regparm(3)))
#endif
/* Using __fastcall seems to have an unexpected negative effect under
MS VC++, especially for function pointers, so we won't use it for
now on that platform. It may be reconsidered for a future release
if it can be made more effective.
Likely reason: __fastcall on Windows is like stdcall, therefore
the compiler cannot perform stack optimizations for call clusters.
*/
/* Make sure all of these are defined if they aren't already. */
#ifndef FASTCALL
#define FASTCALL
#endif
#ifndef PTRCALL
#define PTRCALL
#endif
#ifndef PTRFASTCALL
#define PTRFASTCALL
#endif
#ifndef XML_MIN_SIZE
#if !defined(__cplusplus) && !defined(inline)
#ifdef __GNUC__
#define inline __inline
#endif /* __GNUC__ */
#endif
#endif /* XML_MIN_SIZE */
#ifdef __cplusplus
#define inline inline
#else
#ifndef inline
#define inline
#endif
#endif
#include <limits.h> // ULONG_MAX
#include <stddef.h> // size_t
#if defined(_WIN32) && (!defined(__USE_MINGW_ANSI_STDIO) || (1 - __USE_MINGW_ANSI_STDIO - 1 == 0))
#define EXPAT_FMT_ULL(midpart) "%" midpart "I64u"
#if defined(_WIN64) // Note: modifiers "td" and "zu" do not work for MinGW
#define EXPAT_FMT_PTRDIFF_T(midpart) "%" midpart "I64d"
#define EXPAT_FMT_SIZE_T(midpart) "%" midpart "I64u"
#else
#define EXPAT_FMT_PTRDIFF_T(midpart) "%" midpart "d"
#define EXPAT_FMT_SIZE_T(midpart) "%" midpart "u"
#endif
#else
#define EXPAT_FMT_ULL(midpart) "%" midpart "llu"
#if !defined(ULONG_MAX)
#error Compiler did not define ULONG_MAX for us
#elif ULONG_MAX == 18446744073709551615u // 2^64-1
#define EXPAT_FMT_PTRDIFF_T(midpart) "%" midpart "ld"
#define EXPAT_FMT_SIZE_T(midpart) "%" midpart "lu"
#elif defined(EMSCRIPTEN) // 32bit mode Emscripten
#define EXPAT_FMT_PTRDIFF_T(midpart) "%" midpart "ld"
#define EXPAT_FMT_SIZE_T(midpart) "%" midpart "zu"
#else
#define EXPAT_FMT_PTRDIFF_T(midpart) "%" midpart "d"
#define EXPAT_FMT_SIZE_T(midpart) "%" midpart "u"
#endif
#endif
#ifndef UNUSED_P
#define UNUSED_P(p) (void)p
#endif
/* NOTE BEGIN If you ever patch these defaults to greater values
for non-attack XML payload in your environment,
please file a bug report with libexpat. Thank you!
*/
#define EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_MAXIMUM_AMPLIFICATION_DEFAULT 100.0f
#define EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT 8388608 // 8 MiB, 2^23
#define EXPAT_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION_DEFAULT 100.0f
#define EXPAT_ALLOC_TRACKER_ACTIVATION_THRESHOLD_DEFAULT 67108864 // 64 MiB, 2^26
// NOTE: If function expat_alloc was user facing, EXPAT_MALLOC_ALIGNMENT would
// have to take sizeof(long double) into account
#define EXPAT_MALLOC_ALIGNMENT sizeof(long long) // largest parser (sub)member
#define EXPAT_MALLOC_PADDING ((EXPAT_MALLOC_ALIGNMENT) - sizeof(size_t))
/* NOTE END */
#include "expat.h" // so we can use type XML_Parser below
#ifdef __cplusplus
extern "C" {
#endif
void _INTERNAL_trim_to_complete_utf8_characters(const char* from, const char** fromLimRef);
#if defined(XML_GE) && XML_GE == 1
unsigned long long testingAccountingGetCountBytesDirect(XML_Parser parser);
unsigned long long testingAccountingGetCountBytesIndirect(XML_Parser parser);
const char* unsignedCharToPrintable(unsigned char c);
#endif
extern
#if !defined(XML_TESTING)
const
#endif
XML_Bool g_reparseDeferralEnabledDefault; // written ONLY in runtests.c
#if defined(XML_TESTING)
void* expat_malloc(XML_Parser parser, size_t size, int sourceLine);
void expat_free(XML_Parser parser, void* ptr, int sourceLine);
void* expat_realloc(XML_Parser parser, void* ptr, size_t size, int sourceLine);
extern unsigned int g_bytesScanned; // used for testing only
#endif
#ifdef __cplusplus
}
#endif
+66
View File
@@ -0,0 +1,66 @@
/*
__ __ _
___\ \/ /_ __ __ _| |_
/ _ \\ /| '_ \ / _` | __|
| __// \| |_) | (_| | |_
\___/_/\_\ .__/ \__,_|\__|
|_| XML parser
Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
Copyright (c) 2000 Clark Cooper <coopercc@users.sourceforge.net>
Copyright (c) 2002 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
Copyright (c) 2017 Sebastian Pipping <sebastian@pipping.org>
Licensed under the MIT license:
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit
persons to whom the Software is furnished to do so, subject to the
following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/* 0x80 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
/* 0x84 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
/* 0x88 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
/* 0x8C */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
/* 0x90 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
/* 0x94 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
/* 0x98 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
/* 0x9C */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
/* 0xA0 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
/* 0xA4 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
/* 0xA8 */ BT_OTHER, BT_OTHER, BT_NMSTRT, BT_OTHER,
/* 0xAC */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
/* 0xB0 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
/* 0xB4 */ BT_OTHER, BT_NMSTRT, BT_OTHER, BT_NAME,
/* 0xB8 */ BT_OTHER, BT_OTHER, BT_NMSTRT, BT_OTHER,
/* 0xBC */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
/* 0xC0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0xC4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0xC8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0xCC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0xD0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0xD4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER,
/* 0xD8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0xDC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0xE0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0xE4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0xE8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0xEC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0xF0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0xF4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER,
/* 0xF8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
/* 0xFC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+84
View File
@@ -0,0 +1,84 @@
; DEF file for MS VC++
EXPORTS
XML_DefaultCurrent @1
XML_ErrorString @2
XML_ExpatVersion @3
XML_ExpatVersionInfo @4
XML_ExternalEntityParserCreate @5
XML_GetBase @6
XML_GetBuffer @7
XML_GetCurrentByteCount @8
XML_GetCurrentByteIndex @9
XML_GetCurrentColumnNumber @10
XML_GetCurrentLineNumber @11
XML_GetErrorCode @12
XML_GetIdAttributeIndex @13
XML_GetInputContext @14
XML_GetSpecifiedAttributeCount @15
XML_Parse @16
XML_ParseBuffer @17
XML_ParserCreate @18
XML_ParserCreateNS @19
XML_ParserCreate_MM @20
XML_ParserFree @21
XML_SetAttlistDeclHandler @22
XML_SetBase @23
XML_SetCdataSectionHandler @24
XML_SetCharacterDataHandler @25
XML_SetCommentHandler @26
XML_SetDefaultHandler @27
XML_SetDefaultHandlerExpand @28
XML_SetDoctypeDeclHandler @29
XML_SetElementDeclHandler @30
XML_SetElementHandler @31
XML_SetEncoding @32
XML_SetEndCdataSectionHandler @33
XML_SetEndDoctypeDeclHandler @34
XML_SetEndElementHandler @35
XML_SetEndNamespaceDeclHandler @36
XML_SetEntityDeclHandler @37
XML_SetExternalEntityRefHandler @38
XML_SetExternalEntityRefHandlerArg @39
XML_SetNamespaceDeclHandler @40
XML_SetNotStandaloneHandler @41
XML_SetNotationDeclHandler @42
XML_SetParamEntityParsing @43
XML_SetProcessingInstructionHandler @44
XML_SetReturnNSTriplet @45
XML_SetStartCdataSectionHandler @46
XML_SetStartDoctypeDeclHandler @47
XML_SetStartElementHandler @48
XML_SetStartNamespaceDeclHandler @49
XML_SetUnknownEncodingHandler @50
XML_SetUnparsedEntityDeclHandler @51
XML_SetUserData @52
XML_SetXmlDeclHandler @53
XML_UseParserAsHandlerArg @54
; added with version 1.95.3
XML_ParserReset @55
XML_SetSkippedEntityHandler @56
; added with version 1.95.5
XML_GetFeatureList @57
XML_UseForeignDTD @58
; added with version 1.95.6
XML_FreeContentModel @59
XML_MemMalloc @60
XML_MemRealloc @61
XML_MemFree @62
; added with version 1.95.8
XML_StopParser @63
XML_ResumeParser @64
XML_GetParsingStatus @65
; added with version 2.1.1
@_EXPAT_COMMENT_ATTR_INFO@ XML_GetAttributeInfo @66
XML_SetHashSalt @67
; internal @68 removed with version 2.3.1
; added with version 2.4.0
@_EXPAT_COMMENT_DTD_OR_GE@ XML_SetBillionLaughsAttackProtectionActivationThreshold @69
@_EXPAT_COMMENT_DTD_OR_GE@ XML_SetBillionLaughsAttackProtectionMaximumAmplification @70
; added with version 2.6.0
XML_SetReparseDeferralEnabled @71
; added with version 2.7.2
@_EXPAT_COMMENT_DTD_OR_GE@ XML_SetAllocTrackerMaximumAmplification @72
@_EXPAT_COMMENT_DTD_OR_GE@ XML_SetAllocTrackerActivationThreshold @73
+11
View File
@@ -0,0 +1,11 @@
{
"name": "expat",
"version": "2.7.3",
"build": {
"srcFilter": [
"+<xmlparse.c>",
"+<xmlrole.c>",
"+<xmltok.c>"
]
}
}
+102
View File
@@ -0,0 +1,102 @@
/*
__ __ _
___\ \/ /_ __ __ _| |_
/ _ \\ /| '_ \ / _` | __|
| __// \| |_) | (_| | |_
\___/_/\_\ .__/ \__,_|\__|
|_| XML parser
Copyright (c) 2000 Clark Cooper <coopercc@users.sourceforge.net>
Copyright (c) 2017 Sebastian Pipping <sebastian@pipping.org>
Licensed under the MIT license:
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit
persons to whom the Software is furnished to do so, subject to the
following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
static const unsigned namingBitmap[] = {
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFF,
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x04000000,
0x87FFFFFE, 0x07FFFFFE, 0x00000000, 0x00000000, 0xFF7FFFFF, 0xFF7FFFFF, 0xFFFFFFFF, 0x7FF3FFFF, 0xFFFFFDFE,
0x7FFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFE00F, 0xFC31FFFF, 0x00FFFFFF, 0x00000000, 0xFFFF0000, 0xFFFFFFFF,
0xFFFFFFFF, 0xF80001FF, 0x00000003, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFFFFD740,
0xFFFFFFFB, 0x547F7FFF, 0x000FFFFD, 0xFFFFDFFE, 0xFFFFFFFF, 0xDFFEFFFF, 0xFFFFFFFF, 0xFFFF0003, 0xFFFFFFFF,
0xFFFF199F, 0x033FCFFF, 0x00000000, 0xFFFE0000, 0x027FFFFF, 0xFFFFFFFE, 0x0000007F, 0x00000000, 0xFFFF0000,
0x000707FF, 0x00000000, 0x07FFFFFE, 0x000007FE, 0xFFFE0000, 0xFFFFFFFF, 0x7CFFFFFF, 0x002F7FFF, 0x00000060,
0xFFFFFFE0, 0x23FFFFFF, 0xFF000000, 0x00000003, 0xFFF99FE0, 0x03C5FDFF, 0xB0000000, 0x00030003, 0xFFF987E0,
0x036DFDFF, 0x5E000000, 0x001C0000, 0xFFFBAFE0, 0x23EDFDFF, 0x00000000, 0x00000001, 0xFFF99FE0, 0x23CDFDFF,
0xB0000000, 0x00000003, 0xD63DC7E0, 0x03BFC718, 0x00000000, 0x00000000, 0xFFFDDFE0, 0x03EFFDFF, 0x00000000,
0x00000003, 0xFFFDDFE0, 0x03EFFDFF, 0x40000000, 0x00000003, 0xFFFDDFE0, 0x03FFFDFF, 0x00000000, 0x00000003,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFE, 0x000D7FFF, 0x0000003F, 0x00000000, 0xFEF02596,
0x200D6CAE, 0x0000001F, 0x00000000, 0x00000000, 0x00000000, 0xFFFFFEFF, 0x000003FF, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFF003F,
0x007FFFFF, 0x0007DAED, 0x50000000, 0x82315001, 0x002C62AB, 0x40000000, 0xF580C900, 0x00000007, 0x02010800,
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x0FFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x03FFFFFF, 0x3F3FFFFF,
0xFFFFFFFF, 0xAAFF3F3F, 0x3FFFFFFF, 0xFFFFFFFF, 0x5FDFFFFF, 0x0FCF1FDC, 0x1FDC1FFF, 0x00000000, 0x00004C40,
0x00000000, 0x00000000, 0x00000007, 0x00000000, 0x00000000, 0x00000000, 0x00000080, 0x000003FE, 0xFFFFFFFE,
0xFFFFFFFF, 0x001FFFFF, 0xFFFFFFFE, 0xFFFFFFFF, 0x07FFFFFF, 0xFFFFFFE0, 0x00001FFF, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
0x0000003F, 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x0000000F,
0x00000000, 0x00000000, 0x00000000, 0x07FF6000, 0x87FFFFFE, 0x07FFFFFE, 0x00000000, 0x00800000, 0xFF7FFFFF,
0xFF7FFFFF, 0x00FFFFFF, 0x00000000, 0xFFFF0000, 0xFFFFFFFF, 0xFFFFFFFF, 0xF80001FF, 0x00030003, 0x00000000,
0xFFFFFFFF, 0xFFFFFFFF, 0x0000003F, 0x00000003, 0xFFFFD7C0, 0xFFFFFFFB, 0x547F7FFF, 0x000FFFFD, 0xFFFFDFFE,
0xFFFFFFFF, 0xDFFEFFFF, 0xFFFFFFFF, 0xFFFF007B, 0xFFFFFFFF, 0xFFFF199F, 0x033FCFFF, 0x00000000, 0xFFFE0000,
0x027FFFFF, 0xFFFFFFFE, 0xFFFE007F, 0xBBFFFFFB, 0xFFFF0016, 0x000707FF, 0x00000000, 0x07FFFFFE, 0x0007FFFF,
0xFFFF03FF, 0xFFFFFFFF, 0x7CFFFFFF, 0xFFEF7FFF, 0x03FF3DFF, 0xFFFFFFEE, 0xF3FFFFFF, 0xFF1E3FFF, 0x0000FFCF,
0xFFF99FEE, 0xD3C5FDFF, 0xB080399F, 0x0003FFCF, 0xFFF987E4, 0xD36DFDFF, 0x5E003987, 0x001FFFC0, 0xFFFBAFEE,
0xF3EDFDFF, 0x00003BBF, 0x0000FFC1, 0xFFF99FEE, 0xF3CDFDFF, 0xB0C0398F, 0x0000FFC3, 0xD63DC7EC, 0xC3BFC718,
0x00803DC7, 0x0000FF80, 0xFFFDDFEE, 0xC3EFFDFF, 0x00603DDF, 0x0000FFC3, 0xFFFDDFEC, 0xC3EFFDFF, 0x40603DDF,
0x0000FFC3, 0xFFFDDFEC, 0xC3FFFDFF, 0x00803DCF, 0x0000FFC3, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0xFFFFFFFE, 0x07FF7FFF, 0x03FF7FFF, 0x00000000, 0xFEF02596, 0x3BFF6CAE, 0x03FF3F5F, 0x00000000, 0x03000000,
0xC2A003FF, 0xFFFFFEFF, 0xFFFE03FF, 0xFEBF0FDF, 0x02FE3FFF, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x1FFF0000, 0x00000002, 0x000000A0, 0x003EFFFE, 0xFFFFFFFE,
0xFFFFFFFF, 0x661FFFFF, 0xFFFFFFFE, 0xFFFFFFFF, 0x77FFFFFF,
};
static const unsigned char nmstrtPages[] = {
0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x00, 0x00, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x13, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
static const unsigned char namePages[] = {
0x19, 0x03, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x00, 0x00, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x10, 0x11, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x13, 0x26, 0x14, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x27, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
+379
View File
@@ -0,0 +1,379 @@
/* ==========================================================================
* siphash.h - SipHash-2-4 in a single header file
* --------------------------------------------------------------------------
* Derived by William Ahern from the reference implementation[1] published[2]
* by Jean-Philippe Aumasson and Daniel J. Berstein.
* Minimal changes by Sebastian Pipping and Victor Stinner on top, see below.
* Licensed under the CC0 Public Domain Dedication license.
*
* 1. https://www.131002.net/siphash/siphash24.c
* 2. https://www.131002.net/siphash/
* --------------------------------------------------------------------------
* HISTORY:
*
* 2020-10-03 (Sebastian Pipping)
* - Drop support for Visual Studio 9.0/2008 and earlier
*
* 2019-08-03 (Sebastian Pipping)
* - Mark part of sip24_valid as to be excluded from clang-format
* - Re-format code using clang-format 9
*
* 2018-07-08 (Anton Maklakov)
* - Add "fall through" markers for GCC's -Wimplicit-fallthrough
*
* 2017-11-03 (Sebastian Pipping)
* - Hide sip_tobin and sip_binof unless SIPHASH_TOBIN macro is defined
*
* 2017-07-25 (Vadim Zeitlin)
* - Fix use of SIPHASH_MAIN macro
*
* 2017-07-05 (Sebastian Pipping)
* - Use _SIP_ULL macro to not require a C++11 compiler if compiled as C++
* - Add const qualifiers at two places
* - Ensure <=80 characters line length (assuming tab width 4)
*
* 2017-06-23 (Victor Stinner)
* - Address Win64 compile warnings
*
* 2017-06-18 (Sebastian Pipping)
* - Clarify license note in the header
* - Address C89 issues:
* - Stop using inline keyword (and let compiler decide)
* - Replace _Bool by int
* - Turn macro siphash24 into a function
* - Address invalid conversion (void pointer) by explicit cast
* - Address lack of stdint.h for Visual Studio 2003 to 2008
* - Always expose sip24_valid (for self-tests)
*
* 2012-11-04 - Born. (William Ahern)
* --------------------------------------------------------------------------
* USAGE:
*
* SipHash-2-4 takes as input two 64-bit words as the key, some number of
* message bytes, and outputs a 64-bit word as the message digest. This
* implementation employs two data structures: a struct sipkey for
* representing the key, and a struct siphash for representing the hash
* state.
*
* For converting a 16-byte unsigned char array to a key, use either the
* macro sip_keyof or the routine sip_tokey. The former instantiates a
* compound literal key, while the latter requires a key object as a
* parameter.
*
* unsigned char secret[16];
* arc4random_buf(secret, sizeof secret);
* struct sipkey *key = sip_keyof(secret);
*
* For hashing a message, use either the convenience macro siphash24 or the
* routines sip24_init, sip24_update, and sip24_final.
*
* struct siphash state;
* void *msg;
* size_t len;
* uint64_t hash;
*
* sip24_init(&state, key);
* sip24_update(&state, msg, len);
* hash = sip24_final(&state);
*
* or
*
* hash = siphash24(msg, len, key);
*
* To convert the 64-bit hash value to a canonical 8-byte little-endian
* binary representation, use either the macro sip_binof or the routine
* sip_tobin. The former instantiates and returns a compound literal array,
* while the latter requires an array object as a parameter.
* --------------------------------------------------------------------------
* NOTES:
*
* o Neither sip_keyof, sip_binof, nor siphash24 will work with compilers
* lacking compound literal support. Instead, you must use the lower-level
* interfaces which take as parameters the temporary state objects.
*
* o Uppercase macros may evaluate parameters more than once. Lowercase
* macros should not exhibit any such side effects.
* ==========================================================================
*/
#ifndef SIPHASH_H
#define SIPHASH_H
#include <stddef.h> /* size_t */
#include <stdint.h> /* uint64_t uint32_t uint8_t */
/*
* Workaround to not require a C++11 compiler for using ULL suffix
* if this code is included and compiled as C++; related GCC warning is:
* warning: use of C++11 long long integer constant [-Wlong-long]
*/
#define SIP_ULL(high, low) ((((uint64_t)high) << 32) | (low))
#define SIP_ROTL(x, b) (uint64_t)(((x) << (b)) | ((x) >> (64 - (b))))
#define SIP_U32TO8_LE(p, v) \
(p)[0] = (uint8_t)((v) >> 0); \
(p)[1] = (uint8_t)((v) >> 8); \
(p)[2] = (uint8_t)((v) >> 16); \
(p)[3] = (uint8_t)((v) >> 24);
#define SIP_U64TO8_LE(p, v) \
SIP_U32TO8_LE((p) + 0, (uint32_t)((v) >> 0)); \
SIP_U32TO8_LE((p) + 4, (uint32_t)((v) >> 32));
#define SIP_U8TO64_LE(p) \
(((uint64_t)((p)[0]) << 0) | ((uint64_t)((p)[1]) << 8) | ((uint64_t)((p)[2]) << 16) | ((uint64_t)((p)[3]) << 24) | \
((uint64_t)((p)[4]) << 32) | ((uint64_t)((p)[5]) << 40) | ((uint64_t)((p)[6]) << 48) | ((uint64_t)((p)[7]) << 56))
#define SIPHASH_INITIALIZER {0, 0, 0, 0, {0}, 0, 0}
struct siphash {
uint64_t v0, v1, v2, v3;
unsigned char buf[8], *p;
uint64_t c;
}; /* struct siphash */
#define SIP_KEYLEN 16
struct sipkey {
uint64_t k[2];
}; /* struct sipkey */
#define sip_keyof(k) sip_tokey(&(struct sipkey){{0}}, (k))
static struct sipkey* sip_tokey(struct sipkey* key, const void* src) {
key->k[0] = SIP_U8TO64_LE((const unsigned char*)src);
key->k[1] = SIP_U8TO64_LE((const unsigned char*)src + 8);
return key;
} /* sip_tokey() */
#ifdef SIPHASH_TOBIN
#define sip_binof(v) sip_tobin((unsigned char[8]){0}, (v))
static void* sip_tobin(void* dst, uint64_t u64) {
SIP_U64TO8_LE((unsigned char*)dst, u64);
return dst;
} /* sip_tobin() */
#endif /* SIPHASH_TOBIN */
static void sip_round(struct siphash* H, const int rounds) {
int i;
for (i = 0; i < rounds; i++) {
H->v0 += H->v1;
H->v1 = SIP_ROTL(H->v1, 13);
H->v1 ^= H->v0;
H->v0 = SIP_ROTL(H->v0, 32);
H->v2 += H->v3;
H->v3 = SIP_ROTL(H->v3, 16);
H->v3 ^= H->v2;
H->v0 += H->v3;
H->v3 = SIP_ROTL(H->v3, 21);
H->v3 ^= H->v0;
H->v2 += H->v1;
H->v1 = SIP_ROTL(H->v1, 17);
H->v1 ^= H->v2;
H->v2 = SIP_ROTL(H->v2, 32);
}
} /* sip_round() */
static struct siphash* sip24_init(struct siphash* H, const struct sipkey* key) {
H->v0 = SIP_ULL(0x736f6d65U, 0x70736575U) ^ key->k[0];
H->v1 = SIP_ULL(0x646f7261U, 0x6e646f6dU) ^ key->k[1];
H->v2 = SIP_ULL(0x6c796765U, 0x6e657261U) ^ key->k[0];
H->v3 = SIP_ULL(0x74656462U, 0x79746573U) ^ key->k[1];
H->p = H->buf;
H->c = 0;
return H;
} /* sip24_init() */
#define sip_endof(a) (&(a)[sizeof(a) / sizeof *(a)])
static struct siphash* sip24_update(struct siphash* H, const void* src, size_t len) {
const unsigned char *p = (const unsigned char*)src, *pe = p + len;
uint64_t m;
do {
while (p < pe && H->p < sip_endof(H->buf)) *H->p++ = *p++;
if (H->p < sip_endof(H->buf)) break;
m = SIP_U8TO64_LE(H->buf);
H->v3 ^= m;
sip_round(H, 2);
H->v0 ^= m;
H->p = H->buf;
H->c += 8;
} while (p < pe);
return H;
} /* sip24_update() */
static uint64_t sip24_final(struct siphash* H) {
const char left = (char)(H->p - H->buf);
uint64_t b = (H->c + left) << 56;
switch (left) {
case 7:
b |= (uint64_t)H->buf[6] << 48;
/* fall through */
case 6:
b |= (uint64_t)H->buf[5] << 40;
/* fall through */
case 5:
b |= (uint64_t)H->buf[4] << 32;
/* fall through */
case 4:
b |= (uint64_t)H->buf[3] << 24;
/* fall through */
case 3:
b |= (uint64_t)H->buf[2] << 16;
/* fall through */
case 2:
b |= (uint64_t)H->buf[1] << 8;
/* fall through */
case 1:
b |= (uint64_t)H->buf[0] << 0;
/* fall through */
case 0:
break;
}
H->v3 ^= b;
sip_round(H, 2);
H->v0 ^= b;
H->v2 ^= 0xff;
sip_round(H, 4);
return H->v0 ^ H->v1 ^ H->v2 ^ H->v3;
} /* sip24_final() */
static uint64_t siphash24(const void* src, size_t len, const struct sipkey* key) {
struct siphash state = SIPHASH_INITIALIZER;
return sip24_final(sip24_update(sip24_init(&state, key), src, len));
} /* siphash24() */
/*
* SipHash-2-4 output with
* k = 00 01 02 ...
* and
* in = (empty string)
* in = 00 (1 byte)
* in = 00 01 (2 bytes)
* in = 00 01 02 (3 bytes)
* ...
* in = 00 01 02 ... 3e (63 bytes)
*/
static int sip24_valid(void) {
/* clang-format off */
static const unsigned char vectors[64][8] = {
{ 0x31, 0x0e, 0x0e, 0xdd, 0x47, 0xdb, 0x6f, 0x72, },
{ 0xfd, 0x67, 0xdc, 0x93, 0xc5, 0x39, 0xf8, 0x74, },
{ 0x5a, 0x4f, 0xa9, 0xd9, 0x09, 0x80, 0x6c, 0x0d, },
{ 0x2d, 0x7e, 0xfb, 0xd7, 0x96, 0x66, 0x67, 0x85, },
{ 0xb7, 0x87, 0x71, 0x27, 0xe0, 0x94, 0x27, 0xcf, },
{ 0x8d, 0xa6, 0x99, 0xcd, 0x64, 0x55, 0x76, 0x18, },
{ 0xce, 0xe3, 0xfe, 0x58, 0x6e, 0x46, 0xc9, 0xcb, },
{ 0x37, 0xd1, 0x01, 0x8b, 0xf5, 0x00, 0x02, 0xab, },
{ 0x62, 0x24, 0x93, 0x9a, 0x79, 0xf5, 0xf5, 0x93, },
{ 0xb0, 0xe4, 0xa9, 0x0b, 0xdf, 0x82, 0x00, 0x9e, },
{ 0xf3, 0xb9, 0xdd, 0x94, 0xc5, 0xbb, 0x5d, 0x7a, },
{ 0xa7, 0xad, 0x6b, 0x22, 0x46, 0x2f, 0xb3, 0xf4, },
{ 0xfb, 0xe5, 0x0e, 0x86, 0xbc, 0x8f, 0x1e, 0x75, },
{ 0x90, 0x3d, 0x84, 0xc0, 0x27, 0x56, 0xea, 0x14, },
{ 0xee, 0xf2, 0x7a, 0x8e, 0x90, 0xca, 0x23, 0xf7, },
{ 0xe5, 0x45, 0xbe, 0x49, 0x61, 0xca, 0x29, 0xa1, },
{ 0xdb, 0x9b, 0xc2, 0x57, 0x7f, 0xcc, 0x2a, 0x3f, },
{ 0x94, 0x47, 0xbe, 0x2c, 0xf5, 0xe9, 0x9a, 0x69, },
{ 0x9c, 0xd3, 0x8d, 0x96, 0xf0, 0xb3, 0xc1, 0x4b, },
{ 0xbd, 0x61, 0x79, 0xa7, 0x1d, 0xc9, 0x6d, 0xbb, },
{ 0x98, 0xee, 0xa2, 0x1a, 0xf2, 0x5c, 0xd6, 0xbe, },
{ 0xc7, 0x67, 0x3b, 0x2e, 0xb0, 0xcb, 0xf2, 0xd0, },
{ 0x88, 0x3e, 0xa3, 0xe3, 0x95, 0x67, 0x53, 0x93, },
{ 0xc8, 0xce, 0x5c, 0xcd, 0x8c, 0x03, 0x0c, 0xa8, },
{ 0x94, 0xaf, 0x49, 0xf6, 0xc6, 0x50, 0xad, 0xb8, },
{ 0xea, 0xb8, 0x85, 0x8a, 0xde, 0x92, 0xe1, 0xbc, },
{ 0xf3, 0x15, 0xbb, 0x5b, 0xb8, 0x35, 0xd8, 0x17, },
{ 0xad, 0xcf, 0x6b, 0x07, 0x63, 0x61, 0x2e, 0x2f, },
{ 0xa5, 0xc9, 0x1d, 0xa7, 0xac, 0xaa, 0x4d, 0xde, },
{ 0x71, 0x65, 0x95, 0x87, 0x66, 0x50, 0xa2, 0xa6, },
{ 0x28, 0xef, 0x49, 0x5c, 0x53, 0xa3, 0x87, 0xad, },
{ 0x42, 0xc3, 0x41, 0xd8, 0xfa, 0x92, 0xd8, 0x32, },
{ 0xce, 0x7c, 0xf2, 0x72, 0x2f, 0x51, 0x27, 0x71, },
{ 0xe3, 0x78, 0x59, 0xf9, 0x46, 0x23, 0xf3, 0xa7, },
{ 0x38, 0x12, 0x05, 0xbb, 0x1a, 0xb0, 0xe0, 0x12, },
{ 0xae, 0x97, 0xa1, 0x0f, 0xd4, 0x34, 0xe0, 0x15, },
{ 0xb4, 0xa3, 0x15, 0x08, 0xbe, 0xff, 0x4d, 0x31, },
{ 0x81, 0x39, 0x62, 0x29, 0xf0, 0x90, 0x79, 0x02, },
{ 0x4d, 0x0c, 0xf4, 0x9e, 0xe5, 0xd4, 0xdc, 0xca, },
{ 0x5c, 0x73, 0x33, 0x6a, 0x76, 0xd8, 0xbf, 0x9a, },
{ 0xd0, 0xa7, 0x04, 0x53, 0x6b, 0xa9, 0x3e, 0x0e, },
{ 0x92, 0x59, 0x58, 0xfc, 0xd6, 0x42, 0x0c, 0xad, },
{ 0xa9, 0x15, 0xc2, 0x9b, 0xc8, 0x06, 0x73, 0x18, },
{ 0x95, 0x2b, 0x79, 0xf3, 0xbc, 0x0a, 0xa6, 0xd4, },
{ 0xf2, 0x1d, 0xf2, 0xe4, 0x1d, 0x45, 0x35, 0xf9, },
{ 0x87, 0x57, 0x75, 0x19, 0x04, 0x8f, 0x53, 0xa9, },
{ 0x10, 0xa5, 0x6c, 0xf5, 0xdf, 0xcd, 0x9a, 0xdb, },
{ 0xeb, 0x75, 0x09, 0x5c, 0xcd, 0x98, 0x6c, 0xd0, },
{ 0x51, 0xa9, 0xcb, 0x9e, 0xcb, 0xa3, 0x12, 0xe6, },
{ 0x96, 0xaf, 0xad, 0xfc, 0x2c, 0xe6, 0x66, 0xc7, },
{ 0x72, 0xfe, 0x52, 0x97, 0x5a, 0x43, 0x64, 0xee, },
{ 0x5a, 0x16, 0x45, 0xb2, 0x76, 0xd5, 0x92, 0xa1, },
{ 0xb2, 0x74, 0xcb, 0x8e, 0xbf, 0x87, 0x87, 0x0a, },
{ 0x6f, 0x9b, 0xb4, 0x20, 0x3d, 0xe7, 0xb3, 0x81, },
{ 0xea, 0xec, 0xb2, 0xa3, 0x0b, 0x22, 0xa8, 0x7f, },
{ 0x99, 0x24, 0xa4, 0x3c, 0xc1, 0x31, 0x57, 0x24, },
{ 0xbd, 0x83, 0x8d, 0x3a, 0xaf, 0xbf, 0x8d, 0xb7, },
{ 0x0b, 0x1a, 0x2a, 0x32, 0x65, 0xd5, 0x1a, 0xea, },
{ 0x13, 0x50, 0x79, 0xa3, 0x23, 0x1c, 0xe6, 0x60, },
{ 0x93, 0x2b, 0x28, 0x46, 0xe4, 0xd7, 0x06, 0x66, },
{ 0xe1, 0x91, 0x5f, 0x5c, 0xb1, 0xec, 0xa4, 0x6c, },
{ 0xf3, 0x25, 0x96, 0x5c, 0xa1, 0x6d, 0x62, 0x9f, },
{ 0x57, 0x5f, 0xf2, 0x8e, 0x60, 0x38, 0x1b, 0xe5, },
{ 0x72, 0x45, 0x06, 0xeb, 0x4c, 0x32, 0x8a, 0x95, }
};
/* clang-format on */
unsigned char in[64];
struct sipkey k;
size_t i;
sip_tokey(&k,
"\000\001\002\003\004\005\006\007\010\011"
"\012\013\014\015\016\017");
for (i = 0; i < sizeof in; ++i) {
in[i] = (unsigned char)i;
if (siphash24(in, i, &k) != SIP_U8TO64_LE(vectors[i])) return 0;
}
return 1;
} /* sip24_valid() */
#ifdef SIPHASH_MAIN
#include <stdio.h>
int main(void) {
const int ok = sip24_valid();
if (ok)
puts("OK");
else
puts("FAIL");
return !ok;
} /* main() */
#endif /* SIPHASH_MAIN */
#endif /* SIPHASH_H */
+66
View File
@@ -0,0 +1,66 @@
/*
__ __ _
___\ \/ /_ __ __ _| |_
/ _ \\ /| '_ \ / _` | __|
| __// \| |_) | (_| | |_
\___/_/\_\ .__/ \__,_|\__|
|_| XML parser
Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
Copyright (c) 2000 Clark Cooper <coopercc@users.sourceforge.net>
Copyright (c) 2002 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
Copyright (c) 2017 Sebastian Pipping <sebastian@pipping.org>
Licensed under the MIT license:
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit
persons to whom the Software is furnished to do so, subject to the
following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/* 0x80 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
/* 0x84 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
/* 0x88 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
/* 0x8C */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
/* 0x90 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
/* 0x94 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
/* 0x98 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
/* 0x9C */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
/* 0xA0 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
/* 0xA4 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
/* 0xA8 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
/* 0xAC */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
/* 0xB0 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
/* 0xB4 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
/* 0xB8 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
/* 0xBC */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
/* 0xC0 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
/* 0xC4 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
/* 0xC8 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
/* 0xCC */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
/* 0xD0 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
/* 0xD4 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
/* 0xD8 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
/* 0xDC */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
/* 0xE0 */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3,
/* 0xE4 */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3,
/* 0xE8 */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3,
/* 0xEC */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3,
/* 0xF0 */ BT_LEAD4, BT_LEAD4, BT_LEAD4, BT_LEAD4,
/* 0xF4 */ BT_LEAD4, BT_NONXML, BT_NONXML, BT_NONXML,
/* 0xF8 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
/* 0xFC */ BT_NONXML, BT_NONXML, BT_MALFORM, BT_MALFORM,
+48
View File
@@ -0,0 +1,48 @@
/*
__ __ _
___\ \/ /_ __ __ _| |_
/ _ \\ /| '_ \ / _` | __|
| __// \| |_) | (_| | |_
\___/_/\_\ .__/ \__,_|\__|
|_| XML parser
Copyright (c) 2000 Clark Cooper <coopercc@users.sourceforge.net>
Copyright (c) 2002 Greg Stein <gstein@users.sourceforge.net>
Copyright (c) 2005 Karl Waclawek <karl@waclawek.net>
Copyright (c) 2017-2023 Sebastian Pipping <sebastian@pipping.org>
Copyright (c) 2023 Orgad Shaneh <orgad.shaneh@audiocodes.com>
Licensed under the MIT license:
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit
persons to whom the Software is furnished to do so, subject to the
following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef WINCONFIG_H
#define WINCONFIG_H
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#undef WIN32_LEAN_AND_MEAN
#include <memory.h>
#include <string.h>
#endif /* ndef WINCONFIG_H */
+8210
View File
File diff suppressed because it is too large Load Diff
+1108
View File
File diff suppressed because it is too large Load Diff
+134
View File
@@ -0,0 +1,134 @@
/*
__ __ _
___\ \/ /_ __ __ _| |_
/ _ \\ /| '_ \ / _` | __|
| __// \| |_) | (_| | |_
\___/_/\_\ .__/ \__,_|\__|
|_| XML parser
Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
Copyright (c) 2000 Clark Cooper <coopercc@users.sourceforge.net>
Copyright (c) 2002 Karl Waclawek <karl@waclawek.net>
Copyright (c) 2002 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
Copyright (c) 2017-2025 Sebastian Pipping <sebastian@pipping.org>
Licensed under the MIT license:
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit
persons to whom the Software is furnished to do so, subject to the
following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef XmlRole_INCLUDED
#define XmlRole_INCLUDED 1
#include "xmltok.h"
#ifdef __cplusplus
extern "C" {
#endif
enum {
XML_ROLE_ERROR = -1,
XML_ROLE_NONE = 0,
XML_ROLE_XML_DECL,
XML_ROLE_INSTANCE_START,
XML_ROLE_DOCTYPE_NONE,
XML_ROLE_DOCTYPE_NAME,
XML_ROLE_DOCTYPE_SYSTEM_ID,
XML_ROLE_DOCTYPE_PUBLIC_ID,
XML_ROLE_DOCTYPE_INTERNAL_SUBSET,
XML_ROLE_DOCTYPE_CLOSE,
XML_ROLE_GENERAL_ENTITY_NAME,
XML_ROLE_PARAM_ENTITY_NAME,
XML_ROLE_ENTITY_NONE,
XML_ROLE_ENTITY_VALUE,
XML_ROLE_ENTITY_SYSTEM_ID,
XML_ROLE_ENTITY_PUBLIC_ID,
XML_ROLE_ENTITY_COMPLETE,
XML_ROLE_ENTITY_NOTATION_NAME,
XML_ROLE_NOTATION_NONE,
XML_ROLE_NOTATION_NAME,
XML_ROLE_NOTATION_SYSTEM_ID,
XML_ROLE_NOTATION_NO_SYSTEM_ID,
XML_ROLE_NOTATION_PUBLIC_ID,
XML_ROLE_ATTRIBUTE_NAME,
XML_ROLE_ATTRIBUTE_TYPE_CDATA,
XML_ROLE_ATTRIBUTE_TYPE_ID,
XML_ROLE_ATTRIBUTE_TYPE_IDREF,
XML_ROLE_ATTRIBUTE_TYPE_IDREFS,
XML_ROLE_ATTRIBUTE_TYPE_ENTITY,
XML_ROLE_ATTRIBUTE_TYPE_ENTITIES,
XML_ROLE_ATTRIBUTE_TYPE_NMTOKEN,
XML_ROLE_ATTRIBUTE_TYPE_NMTOKENS,
XML_ROLE_ATTRIBUTE_ENUM_VALUE,
XML_ROLE_ATTRIBUTE_NOTATION_VALUE,
XML_ROLE_ATTLIST_NONE,
XML_ROLE_ATTLIST_ELEMENT_NAME,
XML_ROLE_IMPLIED_ATTRIBUTE_VALUE,
XML_ROLE_REQUIRED_ATTRIBUTE_VALUE,
XML_ROLE_DEFAULT_ATTRIBUTE_VALUE,
XML_ROLE_FIXED_ATTRIBUTE_VALUE,
XML_ROLE_ELEMENT_NONE,
XML_ROLE_ELEMENT_NAME,
XML_ROLE_CONTENT_ANY,
XML_ROLE_CONTENT_EMPTY,
XML_ROLE_CONTENT_PCDATA,
XML_ROLE_GROUP_OPEN,
XML_ROLE_GROUP_CLOSE,
XML_ROLE_GROUP_CLOSE_REP,
XML_ROLE_GROUP_CLOSE_OPT,
XML_ROLE_GROUP_CLOSE_PLUS,
XML_ROLE_GROUP_CHOICE,
XML_ROLE_GROUP_SEQUENCE,
XML_ROLE_CONTENT_ELEMENT,
XML_ROLE_CONTENT_ELEMENT_REP,
XML_ROLE_CONTENT_ELEMENT_OPT,
XML_ROLE_CONTENT_ELEMENT_PLUS,
XML_ROLE_PI,
XML_ROLE_COMMENT,
#ifdef XML_DTD
XML_ROLE_TEXT_DECL,
XML_ROLE_IGNORE_SECT,
XML_ROLE_INNER_PARAM_ENTITY_REF,
#endif /* XML_DTD */
XML_ROLE_PARAM_ENTITY_REF
};
typedef struct prolog_state {
int(PTRCALL* handler)(struct prolog_state* state, int tok, const char* ptr, const char* end, const ENCODING* enc);
unsigned level;
int role_none;
#ifdef XML_DTD
unsigned includeLevel;
int documentEntity;
int inEntityValue;
#endif /* XML_DTD */
} PROLOG_STATE;
void XmlPrologStateInit(PROLOG_STATE* state);
#ifdef XML_DTD
void XmlPrologStateInitExternalEntity(PROLOG_STATE* state);
#endif /* XML_DTD */
#define XmlTokenRole(state, tok, ptr, end, enc) (((state)->handler)(state, tok, ptr, end, enc))
#ifdef __cplusplus
}
#endif
#endif /* not XmlRole_INCLUDED */
+1489
View File
File diff suppressed because it is too large Load Diff
+288
View File
@@ -0,0 +1,288 @@
/*
__ __ _
___\ \/ /_ __ __ _| |_
/ _ \\ /| '_ \ / _` | __|
| __// \| |_) | (_| | |_
\___/_/\_\ .__/ \__,_|\__|
|_| XML parser
Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
Copyright (c) 2000 Clark Cooper <coopercc@users.sourceforge.net>
Copyright (c) 2002 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
Copyright (c) 2002-2005 Karl Waclawek <karl@waclawek.net>
Copyright (c) 2016-2024 Sebastian Pipping <sebastian@pipping.org>
Copyright (c) 2017 Rhodri James <rhodri@wildebeest.org.uk>
Licensed under the MIT license:
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit
persons to whom the Software is furnished to do so, subject to the
following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef XmlTok_INCLUDED
#define XmlTok_INCLUDED 1
#ifdef __cplusplus
extern "C" {
#endif
/* The following token may be returned by XmlContentTok */
#define XML_TOK_TRAILING_RSQB \
-5 /* ] or ]] at the end of the scan; might be \
start of illegal ]]> sequence */
/* The following tokens may be returned by both XmlPrologTok and
XmlContentTok.
*/
#define XML_TOK_NONE -4 /* The string to be scanned is empty */
#define XML_TOK_TRAILING_CR \
-3 /* A CR at the end of the scan; \
might be part of CRLF sequence */
#define XML_TOK_PARTIAL_CHAR -2 /* only part of a multibyte sequence */
#define XML_TOK_PARTIAL -1 /* only part of a token */
#define XML_TOK_INVALID 0
/* The following tokens are returned by XmlContentTok; some are also
returned by XmlAttributeValueTok, XmlEntityTok, XmlCdataSectionTok.
*/
#define XML_TOK_START_TAG_WITH_ATTS 1
#define XML_TOK_START_TAG_NO_ATTS 2
#define XML_TOK_EMPTY_ELEMENT_WITH_ATTS 3 /* empty element tag <e/> */
#define XML_TOK_EMPTY_ELEMENT_NO_ATTS 4
#define XML_TOK_END_TAG 5
#define XML_TOK_DATA_CHARS 6
#define XML_TOK_DATA_NEWLINE 7
#define XML_TOK_CDATA_SECT_OPEN 8
#define XML_TOK_ENTITY_REF 9
#define XML_TOK_CHAR_REF 10 /* numeric character reference */
/* The following tokens may be returned by both XmlPrologTok and
XmlContentTok.
*/
#define XML_TOK_PI 11 /* processing instruction */
#define XML_TOK_XML_DECL 12 /* XML decl or text decl */
#define XML_TOK_COMMENT 13
#define XML_TOK_BOM 14 /* Byte order mark */
/* The following tokens are returned only by XmlPrologTok */
#define XML_TOK_PROLOG_S 15
#define XML_TOK_DECL_OPEN 16 /* <!foo */
#define XML_TOK_DECL_CLOSE 17 /* > */
#define XML_TOK_NAME 18
#define XML_TOK_NMTOKEN 19
#define XML_TOK_POUND_NAME 20 /* #name */
#define XML_TOK_OR 21 /* | */
#define XML_TOK_PERCENT 22
#define XML_TOK_OPEN_PAREN 23
#define XML_TOK_CLOSE_PAREN 24
#define XML_TOK_OPEN_BRACKET 25
#define XML_TOK_CLOSE_BRACKET 26
#define XML_TOK_LITERAL 27
#define XML_TOK_PARAM_ENTITY_REF 28
#define XML_TOK_INSTANCE_START 29
/* The following occur only in element type declarations */
#define XML_TOK_NAME_QUESTION 30 /* name? */
#define XML_TOK_NAME_ASTERISK 31 /* name* */
#define XML_TOK_NAME_PLUS 32 /* name+ */
#define XML_TOK_COND_SECT_OPEN 33 /* <![ */
#define XML_TOK_COND_SECT_CLOSE 34 /* ]]> */
#define XML_TOK_CLOSE_PAREN_QUESTION 35 /* )? */
#define XML_TOK_CLOSE_PAREN_ASTERISK 36 /* )* */
#define XML_TOK_CLOSE_PAREN_PLUS 37 /* )+ */
#define XML_TOK_COMMA 38
/* The following token is returned only by XmlAttributeValueTok */
#define XML_TOK_ATTRIBUTE_VALUE_S 39
/* The following token is returned only by XmlCdataSectionTok */
#define XML_TOK_CDATA_SECT_CLOSE 40
/* With namespace processing this is returned by XmlPrologTok for a
name with a colon.
*/
#define XML_TOK_PREFIXED_NAME 41
#ifdef XML_DTD
#define XML_TOK_IGNORE_SECT 42
#endif /* XML_DTD */
#ifdef XML_DTD
#define XML_N_STATES 4
#else /* not XML_DTD */
#define XML_N_STATES 3
#endif /* not XML_DTD */
#define XML_PROLOG_STATE 0
#define XML_CONTENT_STATE 1
#define XML_CDATA_SECTION_STATE 2
#ifdef XML_DTD
#define XML_IGNORE_SECTION_STATE 3
#endif /* XML_DTD */
#define XML_N_LITERAL_TYPES 2
#define XML_ATTRIBUTE_VALUE_LITERAL 0
#define XML_ENTITY_VALUE_LITERAL 1
/* The size of the buffer passed to XmlUtf8Encode must be at least this. */
#define XML_UTF8_ENCODE_MAX 4
/* The size of the buffer passed to XmlUtf16Encode must be at least this. */
#define XML_UTF16_ENCODE_MAX 2
typedef struct position {
/* first line and first column are 0 not 1 */
XML_Size lineNumber;
XML_Size columnNumber;
} POSITION;
typedef struct {
const char* name;
const char* valuePtr;
const char* valueEnd;
char normalized;
} ATTRIBUTE;
struct encoding;
typedef struct encoding ENCODING;
typedef int(PTRCALL* SCANNER)(const ENCODING*, const char*, const char*, const char**);
enum XML_Convert_Result {
XML_CONVERT_COMPLETED = 0,
XML_CONVERT_INPUT_INCOMPLETE = 1,
XML_CONVERT_OUTPUT_EXHAUSTED = 2 /* and therefore potentially input remaining as well */
};
struct encoding {
SCANNER scanners[XML_N_STATES];
SCANNER literalScanners[XML_N_LITERAL_TYPES];
int(PTRCALL* nameMatchesAscii)(const ENCODING*, const char*, const char*, const char*);
int(PTRFASTCALL* nameLength)(const ENCODING*, const char*);
const char*(PTRFASTCALL* skipS)(const ENCODING*, const char*);
int(PTRCALL* getAtts)(const ENCODING* enc, const char* ptr, int attsMax, ATTRIBUTE* atts);
int(PTRFASTCALL* charRefNumber)(const ENCODING* enc, const char* ptr);
int(PTRCALL* predefinedEntityName)(const ENCODING*, const char*, const char*);
void(PTRCALL* updatePosition)(const ENCODING*, const char* ptr, const char* end, POSITION*);
int(PTRCALL* isPublicId)(const ENCODING* enc, const char* ptr, const char* end, const char** badPtr);
enum XML_Convert_Result(PTRCALL* utf8Convert)(const ENCODING* enc, const char** fromP, const char* fromLim,
char** toP, const char* toLim);
enum XML_Convert_Result(PTRCALL* utf16Convert)(const ENCODING* enc, const char** fromP, const char* fromLim,
unsigned short** toP, const unsigned short* toLim);
int minBytesPerChar;
char isUtf8;
char isUtf16;
};
/* Scan the string starting at ptr until the end of the next complete
token, but do not scan past eptr. Return an integer giving the
type of token.
Return XML_TOK_NONE when ptr == eptr; nextTokPtr will not be set.
Return XML_TOK_PARTIAL when the string does not contain a complete
token; nextTokPtr will not be set.
Return XML_TOK_INVALID when the string does not start a valid
token; nextTokPtr will be set to point to the character which made
the token invalid.
Otherwise the string starts with a valid token; nextTokPtr will be
set to point to the character following the end of that token.
Each data character counts as a single token, but adjacent data
characters may be returned together. Similarly for characters in
the prolog outside literals, comments and processing instructions.
*/
#define XmlTok(enc, state, ptr, end, nextTokPtr) (((enc)->scanners[state])(enc, ptr, end, nextTokPtr))
#define XmlPrologTok(enc, ptr, end, nextTokPtr) XmlTok(enc, XML_PROLOG_STATE, ptr, end, nextTokPtr)
#define XmlContentTok(enc, ptr, end, nextTokPtr) XmlTok(enc, XML_CONTENT_STATE, ptr, end, nextTokPtr)
#define XmlCdataSectionTok(enc, ptr, end, nextTokPtr) XmlTok(enc, XML_CDATA_SECTION_STATE, ptr, end, nextTokPtr)
#ifdef XML_DTD
#define XmlIgnoreSectionTok(enc, ptr, end, nextTokPtr) XmlTok(enc, XML_IGNORE_SECTION_STATE, ptr, end, nextTokPtr)
#endif /* XML_DTD */
/* This is used for performing a 2nd-level tokenization on the content
of a literal that has already been returned by XmlTok.
*/
#define XmlLiteralTok(enc, literalType, ptr, end, nextTokPtr) \
(((enc)->literalScanners[literalType])(enc, ptr, end, nextTokPtr))
#define XmlAttributeValueTok(enc, ptr, end, nextTokPtr) \
XmlLiteralTok(enc, XML_ATTRIBUTE_VALUE_LITERAL, ptr, end, nextTokPtr)
#define XmlEntityValueTok(enc, ptr, end, nextTokPtr) XmlLiteralTok(enc, XML_ENTITY_VALUE_LITERAL, ptr, end, nextTokPtr)
#define XmlNameMatchesAscii(enc, ptr1, end1, ptr2) (((enc)->nameMatchesAscii)(enc, ptr1, end1, ptr2))
#define XmlNameLength(enc, ptr) (((enc)->nameLength)(enc, ptr))
#define XmlSkipS(enc, ptr) (((enc)->skipS)(enc, ptr))
#define XmlGetAttributes(enc, ptr, attsMax, atts) (((enc)->getAtts)(enc, ptr, attsMax, atts))
#define XmlCharRefNumber(enc, ptr) (((enc)->charRefNumber)(enc, ptr))
#define XmlPredefinedEntityName(enc, ptr, end) (((enc)->predefinedEntityName)(enc, ptr, end))
#define XmlUpdatePosition(enc, ptr, end, pos) (((enc)->updatePosition)(enc, ptr, end, pos))
#define XmlIsPublicId(enc, ptr, end, badPtr) (((enc)->isPublicId)(enc, ptr, end, badPtr))
#define XmlUtf8Convert(enc, fromP, fromLim, toP, toLim) (((enc)->utf8Convert)(enc, fromP, fromLim, toP, toLim))
#define XmlUtf16Convert(enc, fromP, fromLim, toP, toLim) (((enc)->utf16Convert)(enc, fromP, fromLim, toP, toLim))
typedef struct {
ENCODING initEnc;
const ENCODING** encPtr;
} INIT_ENCODING;
int XmlParseXmlDecl(int isGeneralTextEntity, const ENCODING* enc, const char* ptr, const char* end, const char** badPtr,
const char** versionPtr, const char** versionEndPtr, const char** encodingNamePtr,
const ENCODING** namedEncodingPtr, int* standalonePtr);
int XmlInitEncoding(INIT_ENCODING* p, const ENCODING** encPtr, const char* name);
const ENCODING* XmlGetUtf8InternalEncoding(void);
const ENCODING* XmlGetUtf16InternalEncoding(void);
int FASTCALL XmlUtf8Encode(int charNumber, char* buf);
int FASTCALL XmlUtf16Encode(int charNumber, unsigned short* buf);
int XmlSizeOfUnknownEncoding(void);
typedef int(XMLCALL* CONVERTER)(void* userData, const char* p);
ENCODING* XmlInitUnknownEncoding(void* mem, const int* table, CONVERTER convert, void* userData);
int XmlParseXmlDeclNS(int isGeneralTextEntity, const ENCODING* enc, const char* ptr, const char* end,
const char** badPtr, const char** versionPtr, const char** versionEndPtr,
const char** encodingNamePtr, const ENCODING** namedEncodingPtr, int* standalonePtr);
int XmlInitEncodingNS(INIT_ENCODING* p, const ENCODING** encPtr, const char* name);
const ENCODING* XmlGetUtf8InternalEncodingNS(void);
const ENCODING* XmlGetUtf16InternalEncodingNS(void);
ENCODING* XmlInitUnknownEncodingNS(void* mem, const int* table, CONVERTER convert, void* userData);
#ifdef __cplusplus
}
#endif
#endif /* not XmlTok_INCLUDED */
File diff suppressed because it is too large Load Diff
+74
View File
@@ -0,0 +1,74 @@
/*
__ __ _
___\ \/ /_ __ __ _| |_
/ _ \\ /| '_ \ / _` | __|
| __// \| |_) | (_| | |_
\___/_/\_\ .__/ \__,_|\__|
|_| XML parser
Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
Copyright (c) 2000 Clark Cooper <coopercc@users.sourceforge.net>
Copyright (c) 2017-2019 Sebastian Pipping <sebastian@pipping.org>
Licensed under the MIT license:
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit
persons to whom the Software is furnished to do so, subject to the
following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
enum {
BT_NONXML, /* e.g. noncharacter-FFFF */
BT_MALFORM, /* illegal, with regard to encoding */
BT_LT, /* less than = "<" */
BT_AMP, /* ampersand = "&" */
BT_RSQB, /* right square bracket = "[" */
BT_LEAD2, /* lead byte of a 2-byte UTF-8 character */
BT_LEAD3, /* lead byte of a 3-byte UTF-8 character */
BT_LEAD4, /* lead byte of a 4-byte UTF-8 character */
BT_TRAIL, /* trailing unit, e.g. second 16-bit unit of a 4-byte char. */
BT_CR, /* carriage return = "\r" */
BT_LF, /* line feed = "\n" */
BT_GT, /* greater than = ">" */
BT_QUOT, /* quotation character = "\"" */
BT_APOS, /* apostrophe = "'" */
BT_EQUALS, /* equal sign = "=" */
BT_QUEST, /* question mark = "?" */
BT_EXCL, /* exclamation mark = "!" */
BT_SOL, /* solidus, slash = "/" */
BT_SEMI, /* semicolon = ";" */
BT_NUM, /* number sign = "#" */
BT_LSQB, /* left square bracket = "[" */
BT_S, /* white space, e.g. "\t", " "[, "\r"] */
BT_NMSTRT, /* non-hex name start letter = "G".."Z" + "g".."z" + "_" */
BT_COLON, /* colon = ":" */
BT_HEX, /* hex letter = "A".."F" + "a".."f" */
BT_DIGIT, /* digit = "0".."9" */
BT_NAME, /* dot and middle dot = "." + chr(0xb7) */
BT_MINUS, /* minus = "-" */
BT_OTHER, /* known not to be a name or name start character */
BT_NONASCII, /* might be a name or name start character */
BT_PERCNT, /* percent sign = "%" */
BT_LPAR, /* left parenthesis = "(" */
BT_RPAR, /* right parenthesis = "(" */
BT_AST, /* asterisk = "*" */
BT_PLUS, /* plus sign = "+" */
BT_COMMA, /* comma = "," */
BT_VERBAR /* vertical bar = "|" */
};
#include <stddef.h>
+98
View File
@@ -0,0 +1,98 @@
/* This file is included!
__ __ _
___\ \/ /_ __ __ _| |_
/ _ \\ /| '_ \ / _` | __|
| __// \| |_) | (_| | |_
\___/_/\_\ .__/ \__,_|\__|
|_| XML parser
Copyright (c) 1997-2000 Thai Open Source Software Center Ltd
Copyright (c) 2000 Clark Cooper <coopercc@users.sourceforge.net>
Copyright (c) 2002 Greg Stein <gstein@users.sourceforge.net>
Copyright (c) 2002 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
Copyright (c) 2002-2006 Karl Waclawek <karl@waclawek.net>
Copyright (c) 2017-2021 Sebastian Pipping <sebastian@pipping.org>
Licensed under the MIT license:
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit
persons to whom the Software is furnished to do so, subject to the
following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifdef XML_TOK_NS_C
const ENCODING* NS(XmlGetUtf8InternalEncoding)(void) { return &ns(internal_utf8_encoding).enc; }
const ENCODING* NS(XmlGetUtf16InternalEncoding)(void) {
#if BYTEORDER == 1234
return &ns(internal_little2_encoding).enc;
#elif BYTEORDER == 4321
return &ns(internal_big2_encoding).enc;
#else
const short n = 1;
return (*(const char*)&n ? &ns(internal_little2_encoding).enc : &ns(internal_big2_encoding).enc);
#endif
}
static const ENCODING* const NS(encodings)[] = {
&ns(latin1_encoding).enc, &ns(ascii_encoding).enc, &ns(utf8_encoding).enc, &ns(big2_encoding).enc,
&ns(big2_encoding).enc, &ns(little2_encoding).enc, &ns(utf8_encoding).enc /* NO_ENC */
};
static int PTRCALL NS(initScanProlog)(const ENCODING* enc, const char* ptr, const char* end, const char** nextTokPtr) {
return initScan(NS(encodings), (const INIT_ENCODING*)enc, XML_PROLOG_STATE, ptr, end, nextTokPtr);
}
static int PTRCALL NS(initScanContent)(const ENCODING* enc, const char* ptr, const char* end, const char** nextTokPtr) {
return initScan(NS(encodings), (const INIT_ENCODING*)enc, XML_CONTENT_STATE, ptr, end, nextTokPtr);
}
int NS(XmlInitEncoding)(INIT_ENCODING* p, const ENCODING** encPtr, const char* name) {
int i = getEncodingIndex(name);
if (i == UNKNOWN_ENC) return 0;
SET_INIT_ENC_INDEX(p, i);
p->initEnc.scanners[XML_PROLOG_STATE] = NS(initScanProlog);
p->initEnc.scanners[XML_CONTENT_STATE] = NS(initScanContent);
p->initEnc.updatePosition = initUpdatePosition;
p->encPtr = encPtr;
*encPtr = &(p->initEnc);
return 1;
}
static const ENCODING* NS(findEncoding)(const ENCODING* enc, const char* ptr, const char* end) {
#define ENCODING_MAX 128
char buf[ENCODING_MAX] = "";
char* p = buf;
int i;
XmlUtf8Convert(enc, &ptr, end, &p, p + ENCODING_MAX - 1);
if (ptr != end) return 0;
*p = 0;
if (streqci(buf, KW_UTF_16) && enc->minBytesPerChar == 2) return enc;
i = getEncodingIndex(buf);
if (i == UNKNOWN_ENC) return 0;
return NS(encodings)[i];
}
int NS(XmlParseXmlDecl)(int isGeneralTextEntity, const ENCODING* enc, const char* ptr, const char* end,
const char** badPtr, const char** versionPtr, const char** versionEndPtr,
const char** encodingName, const ENCODING** encoding, int* standalone) {
return doParseXmlDecl(NS(findEncoding), isGeneralTextEntity, enc, ptr, end, badPtr, versionPtr, versionEndPtr,
encodingName, encoding, standalone);
}
#endif /* XML_TOK_NS_C */
+213 -385
View File
File diff suppressed because it is too large Load Diff
+383 -630
View File
File diff suppressed because it is too large Load Diff
+4
View File
@@ -28,9 +28,13 @@ build_flags =
-DARDUINO_USB_MODE=1 -DARDUINO_USB_MODE=1
-DARDUINO_USB_CDC_ON_BOOT=1 -DARDUINO_USB_CDC_ON_BOOT=1
-DMINIZ_NO_ZLIB_COMPATIBLE_NAMES=1 -DMINIZ_NO_ZLIB_COMPATIBLE_NAMES=1
# https://libexpat.github.io/doc/api/latest/#XML_GE
-DXML_GE=0
-DXML_CONTEXT_BYTES=1024
; Libraries ; Libraries
lib_deps = lib_deps =
zinggjm/GxEPD2@^1.6.5 zinggjm/GxEPD2@^1.6.5
https://github.com/leethomason/tinyxml2.git#11.0.0 https://github.com/leethomason/tinyxml2.git#11.0.0
BatteryMonitor=symlink://open-x4-sdk/libs/hardware/BatteryMonitor BatteryMonitor=symlink://open-x4-sdk/libs/hardware/BatteryMonitor
InputManager=symlink://open-x4-sdk/libs/hardware/InputManager
+35
View File
@@ -0,0 +1,35 @@
#include "CrossPointState.h"
#include <HardwareSerial.h>
#include <SD.h>
#include <Serialization.h>
#include <fstream>
constexpr uint8_t STATE_FILE_VERSION = 1;
constexpr char STATE_FILE[] = "/sd/.crosspoint/state.bin";
bool CrossPointState::saveToFile() const {
std::ofstream outputFile(STATE_FILE);
serialization::writePod(outputFile, STATE_FILE_VERSION);
serialization::writeString(outputFile, openEpubPath);
outputFile.close();
return true;
}
bool CrossPointState::loadFromFile() {
std::ifstream inputFile(STATE_FILE);
uint8_t version;
serialization::readPod(inputFile, version);
if (version != STATE_FILE_VERSION) {
Serial.printf("CrossPointState: Unknown version %u\n", version);
inputFile.close();
return false;
}
serialization::readString(inputFile, openEpubPath);
inputFile.close();
return true;
}
+13
View File
@@ -0,0 +1,13 @@
#pragma once
#include <iosfwd>
#include <string>
class CrossPointState {
public:
std::string openEpubPath;
~CrossPointState() = default;
bool saveToFile() const;
bool loadFromFile();
};
-43
View File
@@ -1,43 +0,0 @@
#include "Input.h"
#include <esp32-hal-adc.h>
void setupInputPinModes() {
pinMode(BTN_GPIO1, INPUT);
pinMode(BTN_GPIO2, INPUT);
pinMode(BTN_GPIO3, INPUT_PULLUP); // Power button
}
// Get currently pressed button by reading ADC values (and digital for power
// button)
Button getPressedButton() {
// Check BTN_GPIO3 (Power button) - digital read
if (digitalRead(BTN_GPIO3) == LOW) return POWER;
// Check BTN_GPIO1 (4 buttons on resistor ladder)
const int btn1 = analogRead(BTN_GPIO1);
if (btn1 < BTN_RIGHT_VAL + BTN_THRESHOLD) return RIGHT;
if (btn1 < BTN_LEFT_VAL + BTN_THRESHOLD) return LEFT;
if (btn1 < BTN_CONFIRM_VAL + BTN_THRESHOLD) return CONFIRM;
if (btn1 < BTN_BACK_VAL + BTN_THRESHOLD) return BACK;
// Check BTN_GPIO2 (2 buttons on resistor ladder)
const int btn2 = analogRead(BTN_GPIO2);
if (btn2 < BTN_VOLUME_DOWN_VAL + BTN_THRESHOLD) return VOLUME_DOWN;
if (btn2 < BTN_VOLUME_UP_VAL + BTN_THRESHOLD) return VOLUME_UP;
return NONE;
}
Input getInput(const bool skipWait) {
const Button button = getPressedButton();
if (button == NONE) return {NONE, 0};
if (skipWait) {
return {button, 0};
}
const auto start = millis();
while (getPressedButton() == button) delay(50);
return {button, millis() - start};
}
-28
View File
@@ -1,28 +0,0 @@
#pragma once
// 4 buttons on ADC resistor ladder: Back, Confirm, Left, Right
#define BTN_GPIO1 1
// 2 buttons on ADC resistor ladder: Volume Up, Volume Down
#define BTN_GPIO2 2
// Power button (digital)
#define BTN_GPIO3 3
// Button ADC thresholds
#define BTN_THRESHOLD 100 // Threshold tolerance
#define BTN_RIGHT_VAL 3
#define BTN_LEFT_VAL 1470
#define BTN_CONFIRM_VAL 2655
#define BTN_BACK_VAL 3470
#define BTN_VOLUME_DOWN_VAL 3
#define BTN_VOLUME_UP_VAL 2305
enum Button { NONE = 0, RIGHT, LEFT, CONFIRM, BACK, VOLUME_UP, VOLUME_DOWN, POWER };
struct Input {
Button button;
unsigned long pressTime;
};
void setupInputPinModes();
Button getPressedButton();
Input getInput(bool skipWait = false);
+113
View File
@@ -0,0 +1,113 @@
#pragma once
#include <cstdint>
extern const uint8_t CrossLarge[] = {
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xEF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC7, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x7F, 0xFF,
0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF,
0xF8, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x1F,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x00, 0x0F, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xE0, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x01,
0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0xFF, 0xFF, 0xFF,
0xFE, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFC, 0x00, 0x00,
0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x3F, 0xFF,
0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF,
0xF8, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00,
0x00, 0x07, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x03, 0xFF,
0xFF, 0x80, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0x00, 0x00,
0x00, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00,
0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF,
0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xFC, 0x00, 0x00,
0x00, 0x00, 0x00, 0x1F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00,
0x0F, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xC0, 0x00,
0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x80, 0x00, 0x00, 0x00, 0x00,
0x00, 0x03, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF,
0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x07, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x0F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFC,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFE, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x0F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x07, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xC0, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00,
0x00, 0x03, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF,
0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xF8, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF,
0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF,
0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF,
0xFF, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF,
0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x3F, 0xFF, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xFF,
0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xE0, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xC0, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xE0, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F,
0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xF8, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x3F, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x7F, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x00, 0x00,
0x00, 0x00, 0x00, 0x03, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xE0, 0x00,
0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xF0, 0x00, 0x00, 0x00, 0x00,
0x00, 0x1F, 0xFF, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF,
0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFE, 0x00,
0x00, 0x00, 0x00, 0x00, 0x3F, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
0x00, 0x7F, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF,
0x80, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xC0, 0x00, 0x00,
0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x0F, 0xFF,
0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF,
0xF8, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x00,
0x00, 0x1F, 0xFF, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x3F, 0xFF,
0xFF, 0xFE, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x00,
0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x03, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xE0, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0,
0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x0F, 0xFF,
0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF,
0xFC, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
};
File diff suppressed because it is too large Load Diff
+103 -61
View File
@@ -2,13 +2,17 @@
#include <EpdRenderer.h> #include <EpdRenderer.h>
#include <Epub.h> #include <Epub.h>
#include <GxEPD2_BW.h> #include <GxEPD2_BW.h>
#include <InputManager.h>
#include <SD.h> #include <SD.h>
#include <SPI.h> #include <SPI.h>
#include "Battery.h" #include "Battery.h"
#include "Input.h" #include "CrossPointState.h"
#include "screens/BootLogoScreen.h"
#include "screens/EpubReaderScreen.h" #include "screens/EpubReaderScreen.h"
#include "screens/FileSelectionScreen.h"
#include "screens/FullScreenMessageScreen.h" #include "screens/FullScreenMessageScreen.h"
#include "screens/SleepScreen.h"
#define SPI_FQ 40000000 #define SPI_FQ 40000000
// Display SPI pins (custom pins for XteinkX4, not hardware SPI defaults) // Display SPI pins (custom pins for XteinkX4, not hardware SPI defaults)
@@ -26,18 +30,20 @@
GxEPD2_BW<GxEPD2_426_GDEQ0426T82, GxEPD2_426_GDEQ0426T82::HEIGHT> display(GxEPD2_426_GDEQ0426T82(EPD_CS, EPD_DC, GxEPD2_BW<GxEPD2_426_GDEQ0426T82, GxEPD2_426_GDEQ0426T82::HEIGHT> display(GxEPD2_426_GDEQ0426T82(EPD_CS, EPD_DC,
EPD_RST, EPD_BUSY)); EPD_RST, EPD_BUSY));
auto renderer = new EpdRenderer(&display); InputManager inputManager;
EpdRenderer renderer(display);
Screen* currentScreen; Screen* currentScreen;
CrossPointState appState;
// Power button timing // Power button timing
// Time required to confirm boot from sleep // Time required to confirm boot from sleep
constexpr unsigned long POWER_BUTTON_WAKEUP_MS = 1500; constexpr unsigned long POWER_BUTTON_WAKEUP_MS = 1000;
// Time required to enter sleep mode // Time required to enter sleep mode
constexpr unsigned long POWER_BUTTON_SLEEP_MS = 1000; constexpr unsigned long POWER_BUTTON_SLEEP_MS = 1000;
Epub* loadEpub(const std::string& path) { Epub* loadEpub(const std::string& path) {
if (!SD.exists(path.c_str())) { if (!SD.exists(path.c_str())) {
Serial.println("File does not exist"); Serial.printf("File does not exist: %s\n", path.c_str());
return nullptr; return nullptr;
} }
@@ -47,40 +53,75 @@ Epub* loadEpub(const std::string& path) {
} }
Serial.println("Failed to load epub"); Serial.println("Failed to load epub");
free(epub); delete epub;
return nullptr; return nullptr;
} }
void enterNewScreen(Screen* screen) { void exitScreen() {
if (currentScreen) { if (currentScreen) {
currentScreen->onExit(); currentScreen->onExit();
delete currentScreen; delete currentScreen;
} }
}
void enterNewScreen(Screen* screen) {
currentScreen = screen; currentScreen = screen;
currentScreen->onEnter(); currentScreen->onEnter();
} }
// Verify long press on wake-up from deep sleep // Verify long press on wake-up from deep sleep
void verifyWakeupLongPress() { void verifyWakeupLongPress() {
const auto input = getInput(); // Give the user up to 1000ms to start holding the power button, and must hold for POWER_BUTTON_WAKEUP_MS
const auto start = millis();
bool abort = false;
if (input.button == POWER && input.pressTime > POWER_BUTTON_WAKEUP_MS) { Serial.println("Verifying power button press");
inputManager.update();
while (!inputManager.isPressed(InputManager::BTN_POWER) && millis() - start < 1000) {
delay(50);
inputManager.update();
Serial.println("Waiting...");
}
Serial.printf("Made it? %s\n", inputManager.isPressed(InputManager::BTN_POWER) ? "yes" : "no");
if (inputManager.isPressed(InputManager::BTN_POWER)) {
do {
delay(50);
inputManager.update();
} while (inputManager.isPressed(InputManager::BTN_POWER) && inputManager.getHeldTime() < POWER_BUTTON_WAKEUP_MS);
abort = inputManager.getHeldTime() < POWER_BUTTON_WAKEUP_MS;
} else {
abort = true;
}
Serial.printf("held for %lu\n", inputManager.getHeldTime());
if (abort) {
// Button released too early. Returning to sleep. // Button released too early. Returning to sleep.
// IMPORTANT: Re-arm the wakeup trigger before sleeping again // IMPORTANT: Re-arm the wakeup trigger before sleeping again
esp_deep_sleep_enable_gpio_wakeup(1ULL << BTN_GPIO3, ESP_GPIO_WAKEUP_GPIO_LOW); esp_deep_sleep_enable_gpio_wakeup(1ULL << InputManager::POWER_BUTTON_PIN, ESP_GPIO_WAKEUP_GPIO_LOW);
esp_deep_sleep_start(); esp_deep_sleep_start();
} }
} }
void waitForPowerRelease() {
inputManager.update();
while (inputManager.isPressed(InputManager::BTN_POWER)) {
delay(50);
inputManager.update();
}
}
// Enter deep sleep mode // Enter deep sleep mode
void enterDeepSleep() { void enterDeepSleep() {
enterNewScreen(new FullScreenMessageScreen(renderer, "Sleeping", true, false, true)); exitScreen();
enterNewScreen(new SleepScreen(renderer, inputManager));
Serial.println("Power button released after a long press. Entering deep sleep."); Serial.println("Power button released after a long press. Entering deep sleep.");
delay(2000); // Allow Serial buffer to empty and display to update delay(1000); // Allow Serial buffer to empty and display to update
// Enable Wakeup on LOW (button press) // Enable Wakeup on LOW (button press)
esp_deep_sleep_enable_gpio_wakeup(1ULL << BTN_GPIO3, ESP_GPIO_WAKEUP_GPIO_LOW); esp_deep_sleep_enable_gpio_wakeup(1ULL << InputManager::POWER_BUTTON_PIN, ESP_GPIO_WAKEUP_GPIO_LOW);
display.hibernate(); display.hibernate();
@@ -88,32 +129,40 @@ void enterDeepSleep() {
esp_deep_sleep_start(); esp_deep_sleep_start();
} }
void setupSerial() { void onGoHome();
Serial.begin(115200); void onSelectEpubFile(const std::string& path) {
// Wait for serial monitor exitScreen();
const unsigned long start = millis(); enterNewScreen(new FullScreenMessageScreen(renderer, inputManager, "Loading..."));
while (!Serial && (millis() - start) < 3000) {
delay(10);
}
if (Serial) { Epub* epub = loadEpub(path);
// delay for monitor to start reading if (epub) {
delay(1000); appState.openEpubPath = path;
appState.saveToFile();
exitScreen();
enterNewScreen(new EpubReaderScreen(renderer, inputManager, epub, onGoHome));
} else {
exitScreen();
enterNewScreen(new FullScreenMessageScreen(renderer, inputManager, "Failed to load epub", REGULAR, false, false));
delay(2000);
onGoHome();
} }
} }
void onGoHome() {
exitScreen();
enterNewScreen(new FileSelectionScreen(renderer, inputManager, onSelectEpubFile));
}
void setup() { void setup() {
setupInputPinModes(); inputManager.begin();
verifyWakeupLongPress();
// Check if boot was triggered by the Power Button (Deep Sleep Wakeup) // Begin serial only if USB connected
// If triggered by RST pin or Battery insertion, this will be false, allowing pinMode(UART0_RXD, INPUT);
// normal boot. if (digitalRead(UART0_RXD) == HIGH) {
if (esp_sleep_get_wakeup_cause() == ESP_SLEEP_WAKEUP_GPIO) { Serial.begin(115200);
verifyWakeupLongPress();
} }
setupSerial();
// Initialize pins // Initialize pins
pinMode(BAT_GPIO0, INPUT); pinMode(BAT_GPIO0, INPUT);
@@ -127,56 +176,49 @@ void setup() {
display.setTextColor(GxEPD_BLACK); display.setTextColor(GxEPD_BLACK);
Serial.println("Display initialized"); Serial.println("Display initialized");
enterNewScreen(new FullScreenMessageScreen(renderer, "Loading...", true)); exitScreen();
enterNewScreen(new BootLogoScreen(renderer, inputManager));
// SD Card Initialization // SD Card Initialization
SD.begin(SD_SPI_CS, SPI, SPI_FQ); SD.begin(SD_SPI_CS, SPI, SPI_FQ);
// TODO: Add a file selection screen, for now just load the first file appState.loadFromFile();
File root = SD.open("/"); if (!appState.openEpubPath.empty()) {
String filename; Epub* epub = loadEpub(appState.openEpubPath);
while (true) { if (epub) {
filename = root.getNextFileName(); exitScreen();
if (!filename) { enterNewScreen(new EpubReaderScreen(renderer, inputManager, epub, onGoHome));
break; // Ensure we're not still holding the power button before leaving setup
} waitForPowerRelease();
return;
if (filename.substring(filename.length() - 5) == ".epub") {
Serial.printf("Found epub: %s\n", filename.c_str());
break;
} }
} }
if (!filename) { exitScreen();
enterNewScreen(new FullScreenMessageScreen(renderer, "Could not find epub")); enterNewScreen(new FileSelectionScreen(renderer, inputManager, onSelectEpubFile));
return;
}
Epub* epub = loadEpub(std::string(filename.c_str())); // Ensure we're not still holding the power button before leaving setup
if (epub) { waitForPowerRelease();
enterNewScreen(new EpubReaderScreen(renderer, epub));
} else {
enterNewScreen(new FullScreenMessageScreen(renderer, "Failed to load epub"));
}
} }
void loop() { void loop() {
delay(50); delay(10);
const Input input = getInput(); static unsigned long lastMemPrint = 0;
if (Serial && millis() - lastMemPrint >= 5000) {
if (input.button == NONE) { Serial.printf("[%lu] Memory - Free: %d bytes, Total: %d bytes, Min Free: %d bytes\n", millis(), ESP.getFreeHeap(),
return; ESP.getHeapSize(), ESP.getMinFreeHeap());
lastMemPrint = millis();
} }
if (input.button == POWER && input.pressTime > POWER_BUTTON_SLEEP_MS) { inputManager.update();
if (inputManager.wasReleased(InputManager::BTN_POWER) && inputManager.getHeldTime() > POWER_BUTTON_WAKEUP_MS) {
enterDeepSleep(); enterDeepSleep();
// This should never be hit as `enterDeepSleep` calls esp_deep_sleep_start // This should never be hit as `enterDeepSleep` calls esp_deep_sleep_start
delay(1000);
return; return;
} }
if (currentScreen) { if (currentScreen) {
currentScreen->handleInput(input); currentScreen->handleInput();
} }
} }
+14
View File
@@ -0,0 +1,14 @@
#include "BootLogoScreen.h"
#include <EpdRenderer.h>
#include "images/CrossLarge.h"
void BootLogoScreen::onEnter() {
const auto pageWidth = renderer.getPageWidth();
const auto pageHeight = renderer.getPageHeight();
renderer.clearScreen();
// Location for images is from top right in landscape orientation
renderer.drawImage(CrossLarge, (pageHeight - 128) / 2, (pageWidth - 128) / 2, 128, 128);
}
+8
View File
@@ -0,0 +1,8 @@
#pragma once
#include "Screen.h"
class BootLogoScreen final : public Screen {
public:
explicit BootLogoScreen(EpdRenderer& renderer, InputManager& inputManager) : Screen(renderer, inputManager) {}
void onEnter() override;
};
+115 -67
View File
@@ -5,6 +5,7 @@
#include "Battery.h" #include "Battery.h"
constexpr int PAGES_PER_REFRESH = 20;
constexpr unsigned long SKIP_CHAPTER_MS = 700; constexpr unsigned long SKIP_CHAPTER_MS = 700;
void EpubReaderScreen::taskTrampoline(void* param) { void EpubReaderScreen::taskTrampoline(void* param) {
@@ -13,7 +14,11 @@ void EpubReaderScreen::taskTrampoline(void* param) {
} }
void EpubReaderScreen::onEnter() { void EpubReaderScreen::onEnter() {
sectionMutex = xSemaphoreCreateMutex(); if (!epub) {
return;
}
renderingMutex = xSemaphoreCreateMutex();
epub->setupCacheDir(); epub->setupCacheDir();
@@ -40,56 +45,80 @@ void EpubReaderScreen::onEnter() {
} }
void EpubReaderScreen::onExit() { void EpubReaderScreen::onExit() {
vTaskDelete(displayTaskHandle); // Wait until not rendering to delete task to avoid killing mid-instruction to EPD
xSemaphoreTake(sectionMutex, portMAX_DELAY); xSemaphoreTake(renderingMutex, portMAX_DELAY);
vSemaphoreDelete(sectionMutex); if (displayTaskHandle) {
sectionMutex = nullptr; vTaskDelete(displayTaskHandle);
displayTaskHandle = nullptr;
}
vSemaphoreDelete(renderingMutex);
renderingMutex = nullptr;
delete section;
section = nullptr;
delete epub;
epub = nullptr;
} }
void EpubReaderScreen::handleInput(const Input input) { void EpubReaderScreen::handleInput() {
if (input.button == VOLUME_UP || input.button == VOLUME_DOWN) { if (inputManager.wasPressed(InputManager::BTN_BACK)) {
const bool skipChapter = input.pressTime > SKIP_CHAPTER_MS; onGoHome();
return;
}
// No current section, attempt to rerender the book const bool prevReleased =
if (!section) { inputManager.wasReleased(InputManager::BTN_UP) || inputManager.wasReleased(InputManager::BTN_LEFT);
updateRequired = true; const bool nextReleased =
return; inputManager.wasReleased(InputManager::BTN_DOWN) || inputManager.wasReleased(InputManager::BTN_RIGHT);
}
if (input.button == VOLUME_UP && skipChapter) { if (!prevReleased && !nextReleased) {
nextPageNumber = 0; return;
}
const bool skipChapter = inputManager.getHeldTime() > SKIP_CHAPTER_MS;
if (skipChapter) {
// We don't want to delete the section mid-render, so grab the semaphore
xSemaphoreTake(renderingMutex, portMAX_DELAY);
nextPageNumber = 0;
currentSpineIndex = nextReleased ? currentSpineIndex + 1 : currentSpineIndex - 1;
delete section;
section = nullptr;
xSemaphoreGive(renderingMutex);
updateRequired = true;
return;
}
// No current section, attempt to rerender the book
if (!section) {
updateRequired = true;
return;
}
if (prevReleased) {
if (section->currentPage > 0) {
section->currentPage--;
} else {
// We don't want to delete the section mid-render, so grab the semaphore
xSemaphoreTake(renderingMutex, portMAX_DELAY);
nextPageNumber = UINT16_MAX;
currentSpineIndex--; currentSpineIndex--;
delete section; delete section;
section = nullptr; section = nullptr;
} else if (input.button == VOLUME_DOWN && skipChapter) { xSemaphoreGive(renderingMutex);
}
updateRequired = true;
} else {
if (section->currentPage < section->pageCount - 1) {
section->currentPage++;
} else {
// We don't want to delete the section mid-render, so grab the semaphore
xSemaphoreTake(renderingMutex, portMAX_DELAY);
nextPageNumber = 0; nextPageNumber = 0;
currentSpineIndex++; currentSpineIndex++;
delete section; delete section;
section = nullptr; section = nullptr;
} else if (input.button == VOLUME_UP) { xSemaphoreGive(renderingMutex);
if (section->currentPage > 0) {
section->currentPage--;
} else {
xSemaphoreTake(sectionMutex, portMAX_DELAY);
nextPageNumber = UINT16_MAX;
currentSpineIndex--;
delete section;
section = nullptr;
xSemaphoreGive(sectionMutex);
}
} else if (input.button == VOLUME_DOWN) {
if (section->currentPage < section->pageCount - 1) {
section->currentPage++;
} else {
xSemaphoreTake(sectionMutex, portMAX_DELAY);
nextPageNumber = 0;
currentSpineIndex++;
delete section;
section = nullptr;
xSemaphoreGive(sectionMutex);
}
} }
updateRequired = true; updateRequired = true;
} }
} }
@@ -98,12 +127,15 @@ void EpubReaderScreen::displayTaskLoop() {
while (true) { while (true) {
if (updateRequired) { if (updateRequired) {
updateRequired = false; updateRequired = false;
xSemaphoreTake(renderingMutex, portMAX_DELAY);
renderPage(); renderPage();
xSemaphoreGive(renderingMutex);
} }
vTaskDelay(10 / portTICK_PERIOD_MS); vTaskDelay(10 / portTICK_PERIOD_MS);
} }
} }
// TODO: Failure handling
void EpubReaderScreen::renderPage() { void EpubReaderScreen::renderPage() {
if (!epub) { if (!epub) {
return; return;
@@ -113,19 +145,31 @@ void EpubReaderScreen::renderPage() {
currentSpineIndex = 0; currentSpineIndex = 0;
} }
xSemaphoreTake(sectionMutex, portMAX_DELAY);
if (!section) { if (!section) {
const auto filepath = epub->getSpineItem(currentSpineIndex); const auto filepath = epub->getSpineItem(currentSpineIndex);
Serial.printf("Loading file: %s, index: %d\n", filepath.c_str(), currentSpineIndex); Serial.printf("Loading file: %s, index: %d\n", filepath.c_str(), currentSpineIndex);
section = new Section(epub, currentSpineIndex, renderer); section = new Section(epub, currentSpineIndex, renderer);
if (!section->hasCache()) { if (!section->loadCacheMetadata()) {
Serial.println("Cache not found, building..."); Serial.println("Cache not found, building...");
{
const int textWidth = renderer.getTextWidth("Indexing...");
constexpr int margin = 20;
const int x = (renderer.getPageWidth() - textWidth - margin * 2) / 2;
constexpr int y = 50;
const int w = textWidth + margin * 2;
const int h = renderer.getLineHeight() + margin * 2;
renderer.fillRect(x, y, w, h, 0);
renderer.drawText(x + margin, y + margin, "Indexing...");
renderer.drawRect(x + 5, y + 5, w - 10, h - 10);
renderer.flushArea(x, y, w, h);
}
section->setupCacheDir(); section->setupCacheDir();
if (!section->persistPageDataToSD()) { if (!section->persistPageDataToSD()) {
Serial.println("Failed to persist page data to SD"); Serial.println("Failed to persist page data to SD");
free(section); delete section;
section = nullptr; section = nullptr;
xSemaphoreGive(sectionMutex);
return; return;
} }
} else { } else {
@@ -139,10 +183,16 @@ void EpubReaderScreen::renderPage() {
} }
} }
renderer->clearScreen(); renderer.clearScreen();
section->renderPage(); section->renderPage();
renderStatusBar(); renderStatusBar();
renderer->flushDisplay(); if (pagesUntilFullRefresh <= 1) {
renderer.flushDisplay(false);
pagesUntilFullRefresh = PAGES_PER_REFRESH;
} else {
renderer.flushDisplay();
pagesUntilFullRefresh--;
}
File f = SD.open((epub->getCachePath() + "/progress.bin").c_str(), FILE_WRITE); File f = SD.open((epub->getCachePath() + "/progress.bin").c_str(), FILE_WRITE);
uint8_t data[4]; uint8_t data[4];
@@ -152,46 +202,44 @@ void EpubReaderScreen::renderPage() {
data[3] = (section->currentPage >> 8) & 0xFF; data[3] = (section->currentPage >> 8) & 0xFF;
f.write(data, 4); f.write(data, 4);
f.close(); f.close();
xSemaphoreGive(sectionMutex);
} }
void EpubReaderScreen::renderStatusBar() const { void EpubReaderScreen::renderStatusBar() const {
const auto pageWidth = renderer->getPageWidth(); const auto pageWidth = renderer.getPageWidth();
std::string progress = std::to_string(currentPage + 1) + " / " + std::to_string(section->pageCount); const std::string progress = std::to_string(section->currentPage + 1) + " / " + std::to_string(section->pageCount);
const auto progressTextWidth = renderer->getSmallTextWidth(progress.c_str()); const auto progressTextWidth = renderer.getSmallTextWidth(progress.c_str());
renderer->drawSmallText(pageWidth - progressTextWidth, 765, progress.c_str()); renderer.drawSmallText(pageWidth - progressTextWidth, 765, progress.c_str());
const uint16_t percentage = battery.readPercentage(); const uint16_t percentage = battery.readPercentage();
auto percentageText = std::to_string(percentage) + "%"; const auto percentageText = std::to_string(percentage) + "%";
const auto percentageTextWidth = renderer->getSmallTextWidth(percentageText.c_str()); const auto percentageTextWidth = renderer.getSmallTextWidth(percentageText.c_str());
renderer->drawSmallText(20, 765, percentageText.c_str()); renderer.drawSmallText(20, 765, percentageText.c_str());
// 1 column on left, 2 columns on right, 5 columns of battery body // 1 column on left, 2 columns on right, 5 columns of battery body
constexpr int batteryWidth = 15; constexpr int batteryWidth = 15;
constexpr int batteryHeight = 10; constexpr int batteryHeight = 10;
const int x = 0; constexpr int x = 0;
const int y = 772; constexpr int y = 772;
// Top line // Top line
renderer->drawLine(x, y, x + batteryWidth - 4, y, 1); renderer.drawLine(x, y, x + batteryWidth - 4, y);
// Bottom line // Bottom line
renderer->drawLine(x, y + batteryHeight - 1, x + batteryWidth - 4, y + batteryHeight - 1, 1); renderer.drawLine(x, y + batteryHeight - 1, x + batteryWidth - 4, y + batteryHeight - 1);
// Left line // Left line
renderer->drawLine(x, y, x, y + batteryHeight - 1, 1); renderer.drawLine(x, y, x, y + batteryHeight - 1);
// Battery end // Battery end
renderer->drawLine(x + batteryWidth - 4, y, x + batteryWidth - 4, y + batteryHeight - 1, 1); renderer.drawLine(x + batteryWidth - 4, y, x + batteryWidth - 4, y + batteryHeight - 1);
renderer->drawLine(x + batteryWidth - 3, y + 2, x + batteryWidth - 3, y + batteryHeight - 3, 1); renderer.drawLine(x + batteryWidth - 3, y + 2, x + batteryWidth - 3, y + batteryHeight - 3);
renderer->drawLine(x + batteryWidth - 2, y + 2, x + batteryWidth - 2, y + batteryHeight - 3, 1); renderer.drawLine(x + batteryWidth - 2, y + 2, x + batteryWidth - 2, y + batteryHeight - 3);
renderer->drawLine(x + batteryWidth - 1, y + 2, x + batteryWidth - 1, y + batteryHeight - 3, 1); renderer.drawLine(x + batteryWidth - 1, y + 2, x + batteryWidth - 1, y + batteryHeight - 3);
// The +1 is to round up, so that we always fill at least one pixel // The +1 is to round up, so that we always fill at least one pixel
int filledWidth = percentage * (batteryWidth - 5) / 100 + 1; int filledWidth = percentage * (batteryWidth - 5) / 100 + 1;
if (filledWidth > batteryWidth - 5) { if (filledWidth > batteryWidth - 5) {
filledWidth = batteryWidth - 5; // Ensure we don't overflow filledWidth = batteryWidth - 5; // Ensure we don't overflow
} }
renderer->fillRect(x + 1, y + 1, filledWidth, batteryHeight - 2, 1); renderer.fillRect(x + 1, y + 1, filledWidth, batteryHeight - 2);
// Page width minus existing content with 30px padding on each side // Page width minus existing content with 30px padding on each side
const int leftMargin = 20 + percentageTextWidth + 30; const int leftMargin = 20 + percentageTextWidth + 30;
@@ -199,11 +247,11 @@ void EpubReaderScreen::renderStatusBar() const {
const int availableTextWidth = pageWidth - leftMargin - rightMargin; const int availableTextWidth = pageWidth - leftMargin - rightMargin;
const auto tocItem = epub->getTocItem(epub->getTocIndexForSpineIndex(currentSpineIndex)); const auto tocItem = epub->getTocItem(epub->getTocIndexForSpineIndex(currentSpineIndex));
auto title = tocItem.title; auto title = tocItem.title;
int titleWidth = renderer->getSmallTextWidth(title.c_str()); int titleWidth = renderer.getSmallTextWidth(title.c_str());
while (titleWidth > availableTextWidth) { while (titleWidth > availableTextWidth) {
title = title.substr(0, title.length() - 8) + "..."; title = title.substr(0, title.length() - 8) + "...";
titleWidth = renderer->getSmallTextWidth(title.c_str()); titleWidth = renderer.getSmallTextWidth(title.c_str());
} }
renderer->drawSmallText(leftMargin + (availableTextWidth - titleWidth) / 2, 765, title.c_str()); renderer.drawSmallText(leftMargin + (availableTextWidth - titleWidth) / 2, 765, title.c_str());
} }
+7 -5
View File
@@ -11,11 +11,12 @@ class EpubReaderScreen final : public Screen {
Epub* epub; Epub* epub;
Section* section = nullptr; Section* section = nullptr;
TaskHandle_t displayTaskHandle = nullptr; TaskHandle_t displayTaskHandle = nullptr;
SemaphoreHandle_t sectionMutex = nullptr; SemaphoreHandle_t renderingMutex = nullptr;
int currentSpineIndex = 0; int currentSpineIndex = 0;
int nextPageNumber = 0; int nextPageNumber = 0;
int currentPage = 0; int pagesUntilFullRefresh = 0;
bool updateRequired = false; bool updateRequired = false;
const std::function<void()> onGoHome;
static void taskTrampoline(void* param); static void taskTrampoline(void* param);
[[noreturn]] void displayTaskLoop(); [[noreturn]] void displayTaskLoop();
@@ -23,9 +24,10 @@ class EpubReaderScreen final : public Screen {
void renderStatusBar() const; void renderStatusBar() const;
public: public:
explicit EpubReaderScreen(EpdRenderer* renderer, Epub* epub) : Screen(renderer), epub(epub) {} explicit EpubReaderScreen(EpdRenderer& renderer, InputManager& inputManager, Epub* epub,
~EpubReaderScreen() override { free(section); } const std::function<void()>& onGoHome)
: Screen(renderer, inputManager), epub(epub), onGoHome(onGoHome) {}
void onEnter() override; void onEnter() override;
void onExit() override; void onExit() override;
void handleInput(Input input) override; void handleInput() override;
}; };
+138
View File
@@ -0,0 +1,138 @@
#include "FileSelectionScreen.h"
#include <EpdRenderer.h>
#include <SD.h>
void sortFileList(std::vector<std::string>& strs) {
std::sort(begin(strs), end(strs), [](const std::string& str1, const std::string& str2) {
if (str1.back() == '/' && str2.back() != '/') return true;
if (str1.back() != '/' && str2.back() == '/') return false;
return lexicographical_compare(
begin(str1), end(str1), begin(str2), end(str2),
[](const char& char1, const char& char2) { return tolower(char1) < tolower(char2); });
});
}
void FileSelectionScreen::taskTrampoline(void* param) {
auto* self = static_cast<FileSelectionScreen*>(param);
self->displayTaskLoop();
}
void FileSelectionScreen::loadFiles() {
files.clear();
selectorIndex = 0;
auto root = SD.open(basepath.c_str());
for (File file = root.openNextFile(); file; file = root.openNextFile()) {
auto filename = std::string(file.name());
if (filename[0] == '.') {
file.close();
continue;
}
if (file.isDirectory()) {
files.emplace_back(filename + "/");
} else if (filename.substr(filename.length() - 5) == ".epub") {
files.emplace_back(filename);
}
file.close();
}
root.close();
sortFileList(files);
}
void FileSelectionScreen::onEnter() {
renderingMutex = xSemaphoreCreateMutex();
basepath = "/";
loadFiles();
selectorIndex = 0;
// Trigger first update
updateRequired = true;
xTaskCreate(&FileSelectionScreen::taskTrampoline, "FileSelectionScreenTask",
1024, // Stack size
this, // Parameters
1, // Priority
&displayTaskHandle // Task handle
);
}
void FileSelectionScreen::onExit() {
// Wait until not rendering to delete task to avoid killing mid-instruction to EPD
xSemaphoreTake(renderingMutex, portMAX_DELAY);
if (displayTaskHandle) {
vTaskDelete(displayTaskHandle);
displayTaskHandle = nullptr;
}
vSemaphoreDelete(renderingMutex);
renderingMutex = nullptr;
files.clear();
}
void FileSelectionScreen::handleInput() {
const bool prevPressed =
inputManager.wasPressed(InputManager::BTN_UP) || inputManager.wasPressed(InputManager::BTN_LEFT);
const bool nextPressed =
inputManager.wasPressed(InputManager::BTN_DOWN) || inputManager.wasPressed(InputManager::BTN_RIGHT);
if (inputManager.wasPressed(InputManager::BTN_CONFIRM)) {
if (files.empty()) {
return;
}
if (basepath.back() != '/') basepath += "/";
if (files[selectorIndex].back() == '/') {
basepath += files[selectorIndex].substr(0, files[selectorIndex].length() - 1);
loadFiles();
updateRequired = true;
} else {
onSelect(basepath + files[selectorIndex]);
}
} else if (inputManager.wasPressed(InputManager::BTN_BACK) && basepath != "/") {
basepath = basepath.substr(0, basepath.rfind('/'));
if (basepath.empty()) basepath = "/";
loadFiles();
updateRequired = true;
} else if (prevPressed) {
selectorIndex = (selectorIndex + files.size() - 1) % files.size();
updateRequired = true;
} else if (nextPressed) {
selectorIndex = (selectorIndex + 1) % files.size();
updateRequired = true;
}
}
void FileSelectionScreen::displayTaskLoop() {
while (true) {
if (updateRequired) {
updateRequired = false;
xSemaphoreTake(renderingMutex, portMAX_DELAY);
render();
xSemaphoreGive(renderingMutex);
}
vTaskDelay(10 / portTICK_PERIOD_MS);
}
}
void FileSelectionScreen::render() const {
renderer.clearScreen();
const auto pageWidth = renderer.getPageWidth();
const auto titleWidth = renderer.getTextWidth("CrossPoint Reader", BOLD);
renderer.drawText((pageWidth - titleWidth) / 2, 0, "CrossPoint Reader", 1, BOLD);
if (files.empty()) {
renderer.drawUiText(10, 50, "No EPUBs found");
} else {
// Draw selection
renderer.fillRect(0, 50 + selectorIndex * 30 + 2, pageWidth - 1, 30);
for (size_t i = 0; i < files.size(); i++) {
const auto file = files[i];
renderer.drawUiText(10, 50 + i * 30, file.c_str(), i == selectorIndex ? 0 : 1);
}
}
renderer.flushDisplay();
}
+33
View File
@@ -0,0 +1,33 @@
#pragma once
#include <freertos/FreeRTOS.h>
#include <freertos/semphr.h>
#include <freertos/task.h>
#include <functional>
#include <string>
#include <vector>
#include "Screen.h"
class FileSelectionScreen final : public Screen {
TaskHandle_t displayTaskHandle = nullptr;
SemaphoreHandle_t renderingMutex = nullptr;
std::string basepath = "/";
std::vector<std::string> files;
int selectorIndex = 0;
bool updateRequired = false;
const std::function<void(const std::string&)> onSelect;
static void taskTrampoline(void* param);
[[noreturn]] void displayTaskLoop();
void render() const;
void loadFiles();
public:
explicit FileSelectionScreen(EpdRenderer& renderer, InputManager& inputManager,
const std::function<void(const std::string&)>& onSelect)
: Screen(renderer, inputManager), onSelect(onSelect) {}
void onEnter() override;
void onExit() override;
void handleInput() override;
};
+7 -7
View File
@@ -3,12 +3,12 @@
#include <EpdRenderer.h> #include <EpdRenderer.h>
void FullScreenMessageScreen::onEnter() { void FullScreenMessageScreen::onEnter() {
const auto width = renderer->getTextWidth(text.c_str(), bold, italic); const auto width = renderer.getUiTextWidth(text.c_str(), style);
const auto height = renderer->getLineHeight(); const auto height = renderer.getLineHeight();
const auto left = (renderer->getPageWidth() - width) / 2; const auto left = (renderer.getPageWidth() - width) / 2;
const auto top = (renderer->getPageHeight() - height) / 2; const auto top = (renderer.getPageHeight() - height) / 2;
renderer->clearScreen(invert); renderer.clearScreen(invert);
renderer->drawText(left, top, text.c_str(), bold, italic, invert ? 0 : 1); renderer.drawUiText(left, top, text.c_str(), invert ? 0 : 1, style);
renderer->flushDisplay(); renderer.flushDisplay(partialUpdate);
} }
+11 -5
View File
@@ -2,17 +2,23 @@
#include <string> #include <string>
#include <utility> #include <utility>
#include "EpdFontFamily.h"
#include "Screen.h" #include "Screen.h"
class FullScreenMessageScreen final : public Screen { class FullScreenMessageScreen final : public Screen {
std::string text; std::string text;
bool bold; EpdFontStyle style;
bool italic;
bool invert; bool invert;
bool partialUpdate;
public: public:
explicit FullScreenMessageScreen(EpdRenderer* renderer, std::string text, const bool bold = false, explicit FullScreenMessageScreen(EpdRenderer& renderer, InputManager& inputManager, std::string text,
const bool italic = false, const bool invert = false) const EpdFontStyle style = REGULAR, const bool invert = false,
: Screen(renderer), text(std::move(text)), bold(bold), italic(italic), invert(invert) {} const bool partialUpdate = true)
: Screen(renderer, inputManager),
text(std::move(text)),
style(style),
invert(invert),
partialUpdate(partialUpdate) {}
void onEnter() override; void onEnter() override;
}; };
+5 -4
View File
@@ -1,16 +1,17 @@
#pragma once #pragma once
#include "Input.h" #include <InputManager.h>
class EpdRenderer; class EpdRenderer;
class Screen { class Screen {
protected: protected:
EpdRenderer* renderer; EpdRenderer& renderer;
InputManager& inputManager;
public: public:
explicit Screen(EpdRenderer* renderer) : renderer(renderer) {} explicit Screen(EpdRenderer& renderer, InputManager& inputManager) : renderer(renderer), inputManager(inputManager) {}
virtual ~Screen() = default; virtual ~Screen() = default;
virtual void onEnter() {} virtual void onEnter() {}
virtual void onExit() {} virtual void onExit() {}
virtual void handleInput(Input input) {} virtual void handleInput() {}
}; };
+7
View File
@@ -0,0 +1,7 @@
#include "SleepScreen.h"
#include <EpdRenderer.h>
#include "images/SleepScreenImg.h"
void SleepScreen::onEnter() { renderer.drawImageNoMargin(SleepScreenImg, 0, 0, 800, 480, false, true); }
+8
View File
@@ -0,0 +1,8 @@
#pragma once
#include "Screen.h"
class SleepScreen final : public Screen {
public:
explicit SleepScreen(EpdRenderer& renderer, InputManager& inputManager) : Screen(renderer, inputManager) {}
void onEnter() override;
};