Compare commits

..
65 Commits
Author SHA1 Message Date
Zach Nelson 896b9de9b6 Merge branch 'master' into jpegdec-fork 2026-05-25 09:50:21 -05:00
Jeremy Klein 88b10c82a3 fix: serialize SdFat FsFile close through HalStorage mutex (#2135)
SdFat's SdSpiCard tracks SPI bus state with an unsynchronized
m_spiActive bool. When two tasks call into SdFat concurrently they can
confuse that state machine, ending with one task calling
SPIClass::endTransaction() against a paramLock the other task holds.
That trips FreeRTOS's xTaskPriorityDisinherit assert (tasks.c:5156,
pxTCB == pxCurrentTCBs[0]) and panics the system.

HalStorage already serialized every explicit method call via
storageMutex, but HalFile's destructor was `= default`, which let the
underlying SdFat FsFile destructor run close() outside any lock
(DESTRUCTOR_CLOSES_FILE=1). Any task that destructed a HalFile while
another task was mid-SD-op would race the unsynchronized state.

Move the locking discipline into HalFile::Impl::~Impl: an explicit
close() under StorageLock, then the FsFile member destructor's redundant
close() is a no-op. HalFile's special members can stay = default.

Switch storageMutex to xSemaphoreCreateRecursiveMutex so openFileForRead
and openFileForWrite can hold the lock while assigning to a HalFile&
out-param whose prior Impl needs locked teardown. Priority inheritance
still applies to recursive mutexes.

Also documented the no-bypass rule in CLAUDE.md: never call SdFat /
SdSpiCard / FsBaseFile / SDCardManager directly, never define
HAL_STORAGE_IMPL outside HalStorage.cpp.

Addresses my admittedly synthetic repro for #2047

Did you use AI tools to help write this code? partial
2026-05-24 21:48:33 -04:00
Jeremy Klein 19954aa1b5 refactor: route OTA version check through HttpDownloader (#2076)
With both OtaUpdater and HttpDownloader on esp_http_client
(https://github.com/crosspoint-reader/crosspoint-reader/pull/2074,
https://github.com/crosspoint-reader/crosspoint-reader/pull/2075),
checkForUpdate no longer needs its own client and event handler to fetch
the release JSON. It streams the response straight into
ReleaseJsonParser through a new HttpDownloader::fetchUrl(url,
DataCallback) overload, dropping the duplicate esp_http_client setup,
the HTTP_EVENT_ON_DATA handler, and the totalBytesReceived file global.

DataCallback hands body chunks to a callback without buffering. The
naive alternative, collecting the ~32KB JSON into a std::string, aborts
under -fno-exceptions: the growing allocation collides with the TLS
session's heap mid-fetch and operator new calls abort().

The OTA install path stays on esp_https_ota (flash-write streaming),
which has no HttpDownloader equivalent.

HttpDownloader.h must precede the lwip (esp_http_client) headers in
OtaUpdater.cpp, or Arduino/SdFat macros collide with lwip.


Did you use AI tools to help write this code? partial
2026-05-24 21:44:30 -04:00
Jeremy Klein 2823a4a2cd refactor: move HttpDownloader onto esp_http_client (#2075)
Based on the learnings from
https://github.com/crosspoint-reader/crosspoint-reader/pull/2074 , I
wanted to bring the same buffer savings to the rest of our HTTP Client
stack. That being said, HttpDownloader (fonts/OPDS) used the Arduino
HTTPClient.

HttpDownloader was the last consumer of the Arduino HTTPClient +
NetworkClientSecure stack. OtaUpdater already runs on esp_http_client,
so this drops the parallel HTTP/TLS implementation. It also fixes a
class of OPDS/font download failures: HTTPClient's setTimeout is uint16
and truncates, and its short per-read deadline killed slow or chunked
responses (the -11 / incomplete-data errors).

What changed:

- Rewrote fetchUrl/downloadToFile around esp_http_client with a
streaming open() -> fetch_headers() -> read() loop, manual redirect
following, and is_complete_data_received() as the completeness gate.
Body bytes go straight to the sink (OPDS parser stream, std::string, or
file), so nothing buffers the payload.

- HTTPS is now verified against the CA bundle instead of
NetworkClientSecure::setInsecure(). esp-tls is built with
CONFIG_ESP_TLS_INSECURE off, so an unverified handshake can't be set up
anyway; the model is public servers over verified https and local
servers over plain http (transport is chosen from the URL scheme).

** Self-signed https servers are no longer supported, by design. **

- timeout_ms is 60s; esp_http_client's timeout is uint32, so unlike
HTTPClient it doesn't silently truncate.

- HTTP buffers are 4096 (rx) / 1024 (tx). 4096 holds real OPDS server
headers; the GitHub release CDN sends more and logs a non-fatal
truncation warning, but the headers we read (Location, Content-Length)
come first and survive.

- Removed the now-unused UrlUtils::isHttpsUrl and a stale HTTPClient
comment in FontDownloadActivity.

Validated on device: OPDS browse and a 3.4 MB book download over
verified https, GitHub font downloads (crc-checked), redirect handling
matching curl, and slow/erroring servers surfaced correctly.


Did you use AI tools to help write this code? partial
2026-05-24 00:03:56 -04:00
Danila Yudin 929f290042 fix: close leaked resource handles (#2040) 2026-05-23 21:26:13 +03:00
Leopoldo Pla Sempere 0f021ea5f1 fix: normalize Wi-Fi spelling across remaining locales (#2094) 2026-05-23 21:01:12 +03:00
Justin Mitchell 99ab8b2772 fix: improves Edge case font/glyph handling (#2100) 2026-05-23 20:40:19 +03:00
Julia 7accc607af feat: ports hr tag rendering from crossink (#2117) 2026-05-23 09:47:16 -04:00
Julia f39ba7037f fix(settings): preserve quick resume timeout preference (#2101)
## Summary

### **What is the goal of this PR?**
This fixes an unintended settings side effect when cycling the `Sleep
Screen` option through `Quick Resume`.

Previously, selecting `Sleep Screen = Quick Resume` globally forced
`Quick Resume on Timeout = ON` and left it enabled even after the user
toggled `Sleep Screen` to another option within the same settings
session. Now the auto-enable behavior is scoped to the Settings screen
session:

- If `Quick Resume on Timeout` was already `ON` when entering Settings,
it stays `ON`.
- If it was `OFF`, selecting `Sleep Screen = Quick Resume` temporarily
turns it `ON`.
- If the user then switches away from `Quick Resume`, it turns back
`OFF`.

### **What changes are included?**

- Removes the global logic that permanently forced `Quick Resume on
Timeout` to `ON` whenever `Sleep Screen` was set to `Quick Resume`, even
if it was just due to toggling through the options.
- Adds Settings-screen session tracking so `Quick Resume on Timeout` is
only auto-enabled while the user has `Sleep Screen = Quick Resume`.
- Restores `Quick Resume on Timeout` back to `OFF` when the user
switches away, but only if it was `OFF` when they entered Settings.
- Preserves existing `ON` timeout preferences.
- Same behavior applies to the web settings

## Additional Context

- Tested this on device and via the settings UI
---

### AI Usage

While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it helps set the right
context for reviewers.

Did you use AI tools to help write this code? _**< YES  >**_
2026-05-21 21:06:51 -04:00
WuTofu 2dd491b62e refactor: unify book cache clearing for epub, txt, and xtc files (#1875) 2026-05-21 14:44:10 +03:00
Matteo Scopel dc404b41c7 chore: fix the Italian translation (#2095) 2026-05-21 13:08:50 +03:00
Jeremy Klein 4ffc2a7e7e fix: sleep from a WiFi activity instead of silent-rebooting (#2092)
Unify the two splash-skip signals (RTC silent-reboot flag, SD
seamless-sleep flag) into one BootResume enum driving a single switch.
Storage unchanged; behavior-preserving apart from the fix.

Holding power to sleep from a WiFi activity (Font Download, OPDS, web
server, Calibre, KOReader sync) rebooted to home instead of sleeping.
goToSleep() runs the outgoing activity's onExit(), and those activities
call silentRestart() to clear heap fragmentation, so the heap-defrag
reboot fired before deep sleep could start.

enterDeepSleep() now latches deepSleepInProgress before goToSleep();
silentRestart()/silentRestartToReader() no-op while it's set. Deep sleep
is a full chip reset on wake, so it already clears the fragmentation the
reboot existed for.



Did you use AI tools to help write this code? partial
2026-05-21 00:03:46 -04:00
Julia c44555007b feat(settings): modify "Page as Sleep Screen" to "Quick Resume" options (#2089)
## Summary

**What is the goal of this PR?**

Adds a clearer Quick Resume sleep-screen flow. The previous “Page as
Sleep Screen” behavior is now exposed as a dedicated `Sleep Screen >
Quick Resume `option, with the timeout-only behavior controlled by a
renamed `Quick Resume on Timeout `setting.

**What changes are included?**

- Adds `Quick Resume` as a new `Sleep Screen` option.
- Renames the old `Page as Sleep Screen` setting to `Quick Resume on
Timeout`.
- Changes that setting’s choices from `Never / After Timeout / Always`
to `OFF / ON`.
- Makes `Quick Resume on Timeout = ON` equivalent to the old `After
Timeout` behavior.
- Makes `Sleep Screen > Quick Resume` equivalent to the old `Always`
behavior.
- Automatically forces `Quick Resume on Timeout` to `ON` when `Sleep
Screen` is set to `Quick Resume`.
- Renames internal setting references from `seamlessSleepScreen` to
`quickResumeSleepScreen`.
- Updates translations for the renamed setting label.

**Additional Context**

- This is mostly a settings/labeling restructure around existing
behavior, not a new rendering path.
- The runtime quick-resume behavior still uses the existing saved
framebuffer / last-screen sleep flow.
- Review focus areas:
  - Sleep entry behavior from manual sleep vs timeout sleep.
- The automatic dependency where selecting `Sleep Screen > Quick Resume`
sets `Quick Resume on Timeout` to `ON`.

---

### AI Usage

While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.

Did you use AI tools to help write this code? _**< YES >**_

---
**New `Quick Resume` option for `Sleep Screen` will automatically set
`Quick Resume on Timeout` to `ON`**:
<img width="480" height="800" alt="quick resume"
src="https://github.com/user-attachments/assets/94c553fd-a122-47a8-add9-f29694f55566"
/>

**Example where a different sleep screen setting like `Cover` can be
used in combination with the `Quick Resume on Timeout` setting**:
<img width="480" height="800" alt="cover + quick resume"
src="https://github.com/user-attachments/assets/dd18ce18-230b-4b78-808b-ac85f5e7d5d8"
/>
2026-05-20 22:43:48 -04:00
Vadim Kaushan d9aa5b4de1 fix: take orientation into account for border generation in ScreenshotUtil (#1977)
## Summary

Previously `ScreenshotUtil` used physical display size to draw a border
around the screen contents. Because of this, in landscape orientation
the border was shown as a broken square. This PR changes border drawing
to use logical screen size instead of a physical display size to take
orientation into account.

## Additional Context

* Tested on X4 in all 4 reading orientations. Behavior is now correct,
however it doesn't look perfect on my X4: the border is much closer to
the physical top side of the display than to the other sides. This might
be related to assembly variation during manufacturing, but it might as
well be related to the way a eink controller is connected to the display
(controller supports bigger display sizes, so an offset may be present).

---

### AI Usage

While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.

Did you use AI tools to help write this code? _**NO**_
2026-05-20 15:48:06 -04:00
jpirnayandArthur Tazhitdinov a7aa4c55d8 fix: Prefer epub format over derived formats when downloading from opds server (#1480)
## Summary

* **What is the goal of this PR?** Prefer epub format over kepub or
other formats offered from an OPDS server
* **What changes are included?**

## Additional Context

Should address #1419 

---

### AI Usage

While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.

Did you use AI tools to help write this code? _**<  NO >**_

---------

Co-authored-by: Arthur Tazhitdinov <lisnake@gmail.com>
2026-05-20 11:46:11 -05:00
Zach Nelson 7e3110d296 chore: Replace JPEGDEC patch script with in-org fork 2026-05-20 08:43:21 -05:00
Jeremy Klein 3800179595 fix: wire through silent restart clear resume state with sdk (#2033)
## Summary

After a silent reboot, there was a small window where the esp32 would
listen for button presses but the full refresh would hold the event
loop. This gave a UX experience where the silent reboot had completed to
the home screen, a user taps select (at any time during the process),
and they find themselves unexpectedly in a book.

## Additional Context

This must land after
https://github.com/crosspoint-reader/community-sdk/pull/11 and will need
the submodule SHA changes included in. 

---

### AI Usage

While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.

Did you use AI tools to help write this code? partially
2026-05-20 08:58:43 -04:00
Jeremy Klein 0b9d1a7d23 fix: keep wifi OTA off the heap floor (#2074)
OTA install streams the full 5.8MB image over a multi-minute TLS session
while wifi/LWIP already holds the big internal arena. Measured on
device, the arena bottomed out at ~7.7KB free with the largest
contiguous block down to ~2.4KB; for 80% of the download there wasn't
even a contiguous 8KB block. It finishes on a clean heap but tips into
OOM for anyone carrying more pre-OTA fragmentation.

Two avoidable drains, both in OtaUpdater:

- The esp_http_client RX/TX buffers were 8192/8192 on both the version
check and the install. RX only has to hold response headers (bodies
stream through the parser / OTA writer) and TX only carries our GET, so
trim both to 4096/1024. 4096 still fits the github->CDN redirect
headers; the 512 IDF default truncates them, which is why they got
oversized in the first place.

- installUpdate fired the progress callback every ~100ms perform
iteration, waking the render task on every tick. Its framebuffer work
fights the TLS session for the same arena, and epd can't really repaint
faster than a percent anyway. Throttle it to whole-percent changes.

On device, combined: floor 7.7KB -> 19KB, worstcase contiguous block
2.4KB -> 21KB, zero sub 8KB iterations across the whole download.

KOReaderSyncClient already uses small buffers; HttpDownloader is on the
Arduino HTTPClient stack with no equivalent knob, so neither changed.



Did you use AI tools to help write this code? partial, heap-exploration
assisted by Claude.
2026-05-20 08:46:49 -04:00
Justin Mitchell 252a64fa97 chore: Add funding badge for contributors in README (#2072) 2026-05-19 22:42:45 -04:00
Jeremy Klein 75764bc6eb fix: 0 -> 1, even more deep-sleep fix (#2073)
setTimeout(0) on the serial could trigger a subtle but obnoxious
underflow.

Eat a milli, save a reset button.


Did you use AI tools to help write this code? no
2026-05-19 22:40:44 -04:00
Dave Allie 0ddefdc0c9 chore: Remove FUNDING.yml (#2071)
## Summary

* I am no longer maintaining or running the project, so avoiding
collecting money for nothing
* This will likely be updated by Justin in the near future with
different details
* See
https://github.com/crosspoint-reader/crosspoint-reader/discussions/2070
2026-05-20 11:33:32 +10:00
Eloren1andClaude Sonnet 4.6 41e6e15229 feat: Seamless sleep/wake screens for displaying book pages during deep sleep (#2064)
<img width="605" height="454" alt="image"
src="https://github.com/user-attachments/assets/bfd84afe-3b58-436e-9a5d-539af3ec3d4e"
/>

Actualized "Last" sleep screen setting from previous PRs, rebranded as a
~~`Seamless Sleep`~~ `Page as Sleep Screen` option with more
improvements.


https://github.com/user-attachments/assets/59029ba6-007e-4841-abfa-f680d7e98b79

---

New option: `Page as Sleep Screen` - `Never (default)`, `After Timeout`,
`Always`

When enabled, it seamlessly sleeps on timeout or power off, making a
fast refresh for the moon icon. When waking up, we still show the last
page, instead of the boot screen, making it fully seamless.

I tried different icons such as "refresh arrow" and others, but they
looked not as nice as 3 simple dots.

With this mode, the device turns off 4 seconds faster. And has 6 seconds
less delay when turning back on. Much more responsive.

Previously, even a 10-minute timeout sometimes wasn't enough, and I'd
worry about seeing the book cover. It's now easier to use a shorter
sleep timeout: if I get distracted during a reading session but don't
want to stop, the new screen is much more inviting to come back to.

---

Did you use AI tools to help write this code? _**PARTIALLY**_.

---

Test v1.3.0 firmware.bin file
[download](https://github.com/user-attachments/files/28015193/firmware.zip)

Based on PRs #410 and #495

Closes #400, #1649

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 18:45:28 -04:00
Danila Yudin 082b5f295b docs: update delete endpoint reference (#1940)
## Summary

Update the `/delete` endpoint reference to match the current web server
handler.

## Details

The endpoint docs still described a `type` form field and the old
`Deleted successfully` response. The handler now accepts either `path`
for a single item or `paths` as a JSON array for multi-delete, infers
file versus folder from the SD card entry, and returns `All items
deleted successfully` when all deletes complete.

This updates the curl examples, parameter table, success response, and
error response list to match the implemented behavior.

## Validation

- Compared the documented parameters and response strings with
`CrossPointWebServer::handleDelete()`
- `git diff --check`
2026-05-19 17:39:04 -05:00
Jackson beb7876ccf feat: Make page turn naturally follow orientation (#2023) 2026-05-19 23:13:27 +03:00
Uri Tauber b69111bea5 refactor: Consolidate theme rendering into ThemeMetrics (#1868) 2026-05-19 22:50:26 +03:00
Jeremy Klein 86a9510957 fix: stabilize deep sleep wake on USB power (#2060)
When the device went into deep sleep while plugged into USB, a
power-button press would occasionally not wake it. The display held its
last frame, the chip stayed in deep sleep, and recovery required an
unplug + reset + hold-power cycle. On battery the symptom never surfaced
because the power button physically re-energises the chip.

Two peripherals were holding power domains alive across the deep sleep
boundary and interfering with the configured GPIO wake on the power
button:

1. HWCDC. Once Serial is initialized, the USB Serial/JTAG peripheral
keeps its power domain configured even with TX timeout at zero and no
host draining. Tear it down with Serial.end() in
HalPowerManager::startDeepSleep, gated by ENABLE_SERIAL_LOG to match the
Serial.begin site. This hit me if I was charging off my computer.

2. WiFi. enterDeepSleep had no WiFi teardown, so sleeping from any
network-using activity left the modem domain alive. Call
WiFi.disconnect(true) + WiFi.mode(WIFI_OFF) when WiFi is active. Wake
from deep sleep is effectively a chip reset, so no WiFi state needs to
survive. While this doesn't cause higher power drain, it apparently was
causing issues where I'd occasionally have the chip hang on sleep
transition from a wifi activity.

Confirmed on device.


Did you use AI tools to help write this code? partial
2026-05-19 12:04:38 -04:00
Jeremy Klein acc1ed4358 fix: guard DC writes in JPEGDEC MCU_SKIP path (#2058)
EIGHT_BIT_GRAYSCALE decode of a 3-component progressive JPEG calls
JPEGDecodeMCU_P with MCU_SKIP for Cb and Cr after every Y MCU. The
existing safe-pMCU patch redirects the wild pointer to &sMCUs[0] but
leaves the DC store unguarded, so each chroma skip overwrites the
just-decoded Y DC with the chroma DC predictor. Output reads sMCUs[0],
gets the trailing Cr DC (~0), and renders an all-black image.

Add `if (iMCU >= 0)` guards to the two pMCU[0] writes (main DC store and
successive-approximation update). The pointer redirect stays as the AC
wild-pointer defense; the new guards stop the silent corruption at
sMCUs[0]. The two fixes are independent and both required.

fixes the progressive 8bit grayscale jpeg regression in 1.3.0

Did you use AI tools to help write this code? partial
2026-05-19 09:52:29 -04:00
Uri Tauber 08461c08e7 fix: small QoL: return to the last selected menu location (#1629)
## Summary

* **What is the goal of this PR?** Small UX improvement to the Home
screen by preserving the last selected cursor position when returning to
it.

It supersedes #985 and #1103, which are both significantly outdated and
hundreds of commits behind master.

---

### AI Usage

Did you use AI tools to help write this code? _**< YES >**_
2026-05-19 08:41:39 -05:00
Justin Mitchell dac7fef49d fix: Update documentation with new features and links (#1991) 2026-05-19 07:32:30 +03:00
Jeremy Klein a14c8e762d perf: shrink HomeActivity cover cache from 48KB framebuffer to 16KB region (#2035)
On-device repro showed the cover snapshot pinning ~52KB of contiguous
heap (cloning the full 48KB framebuffer with malloc overhead). MaxAlloc
on Home was 61KB; nothing was leaving headroom for HTTPS, which needs
30-50KB contiguous for the mbedTLS handshake.

Add region-aware framebuffer helpers to GfxRenderer that translate a
logical rect through rotateCoordinates and copy only the byte range that
contains the rotated rect. HomeActivity records the tile rect it passes
to drawRecentBookCover and caches only that subregion.

Measured on device (X3, Portrait):

  Idle on Home    | Free 102K -> 139K  | MaxAlloc 61K -> 115K
  Mid-EPUB-read   | Free  81K -> 134K  | MaxAlloc 70K -> 115K
  Cover cache     |        ~52K -> ~16K (per allocation)

Works in all four orientations because the bounds helper samples the
four logical corners through the existing rotation, so the cached byte
range always covers the pixels the theme could have drawn into.

Savings will vary with theme, but should be significant across all.
2026-05-18 22:41:02 -04:00
Jeremy Klein a525606d7f fix: USB serial logs now flow on cold+warm boot without jiggle (#2034)
The "logs only flow if you unplug and replug the USB cable at the right
moment" symptom traced to two interacting problems with the ESP32-C3 USB
Serial/JTAG controller (HWCDC):

1. Serial.begin was gated on gpio.isUsbConnected(). That check sampled
USB state at one specific microsecond during boot. If USB enumeration on
the host hadn't completed by that moment (common after a reset that
auto- reconnects a moment later), Serial was never initialized and
stayed dead until the next boot where the timing happened to win.

2. HWCDC writes block for up to the configured TX timeout (default 250
ms) when the host has the port open but isn't actively draining — a
state the macOS USB CDC stack enters intermittently after reconnect. The
firmware then appears to hang on logging until a USB unplug+replug
cycles the peripheral and flushes the TX FIFO.

Fix: move the Serial init to the very top of setup() with a 250 ms stall
before Serial.begin (lets the USB peripheral power-on and host
enumeration complete on cold boot), and call logSerial.setTxTimeoutMs(0)
so writes drop bytes harmlessly when the host is slow instead of
stalling the firmware. Both warm reboot and cold power-on now produce
logs immediately.

Did you use AI tools to help write this code? partial
2026-05-18 22:26:25 -04:00
KemoNine df53faab91 feat: allow removing book from recent list (#2045)
## Summary

Add ability to long press 'confirm' on a book in the recent books list
to be prompted to remove it from the list.

---

### AI Usage

While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.

Did you use AI tools to help write this code? *Yes, Claude*

---------
2026-05-18 22:16:33 -04:00
Justin Mitchell f2adefe729 chore: Update open-x4-sdk submodule for faster page turns on x3 (#2055) 2026-05-18 21:41:40 -04:00
KemoNine 6a98c2d865 feat: add setting that allows removing books from recent list when read (#2043) 2026-05-18 21:13:41 -04:00
Justinian cfd3a381ed feat: X3 clock display with DS3231 RTC and NTP sync (#1612) 2026-05-18 21:06:56 -04:00
Jeremy Klein 151bf1dae4 fix: silent-restart on exit from KOReader auth and OTA update (#2036)
PR #1908 silent-restarts on exit from any wifi-using activity to defuse
LWIP/mbedTLS heap fragmentation, but two of the wifi-using paths slipped
through that audit:

  KOReaderAuthActivity (Settings -> KOReader sync -> Authenticate)
  OtaUpdateActivity    (Settings -> Check for update, back-out paths)

Both used WiFi.disconnect + WiFi.mode(WIFI_OFF) on exit and returned
control to Settings, leaving ~50KB of contiguous heap stranded for the
rest of the session.

Mirror the FontDownloadActivity pattern: if WiFi was activated,
disconnect and silentRestart. OTA's success path is unchanged:
SHUTTING_DOWN already calls plain ESP.restart() so the new firmware
boots normally; only the cancel/fail/no-update back-out paths now go
through silentRestart().


Did you use AI tools to help write this code? partial
2026-05-18 21:04:11 -04:00
KemoNine 06d28d6ffa feat: port crossink 'read book move' feature to crosspoint (#2032) 2026-05-18 12:39:50 -04:00
CaptainFrito 8a11f44571 feat: Themed reader menus (#1072) 2026-05-18 17:35:27 +03:00
WuTofu 061c7688a4 chore: add 3-minute sleep option (#1948) 2026-05-18 00:17:24 +03:00
muhasandmuhas 510ee10153 feat: update Russian translation (#2017)
Co-authored-by: muhas <mail@muhas.name>
2026-05-17 15:17:56 +03:00
zgredexandJustin Mitchell c6d116024c fix: harden EPUB optimiser UI gating, size reporting, and picker teardown (#1947)
Co-authored-by: Justin Mitchell <justin@jmitch.com>
2026-05-17 12:12:56 +03:00
Matteo Scopel 85e08f9a93 feat: add the Domitian font family (#2016) 2026-05-17 12:08:16 +03:00
Justin Mitchell 0af0ad5a17 fix: bump open-x4-sdk to clear grayscale state after AA cleanup (#2022)
Pulls in community-sdk PR #9, which clears inGrayscaleMode inside
cleanupGrayscaleBuffers() after the restored BW frame is written back
into RED RAM. Without this, the next BW page turn would still see the
flag set and trigger a redundant grayscaleRevert() refresh, producing
visible ghosting on the X4 with text anti-aliasing enabled.

Regression introduced by SDK commit 0a8ada2 (factory LUT grayscale
support), which removed a redundant inGrayscaleMode guard in
grayscaleRevert() and so caused the cleanup to actually run for the
first time.

Bypassing rules to avoid this going stale and all nightly builds being broken for x4 users
2026-05-17 03:45:11 -04:00
KemoNine 93e81daf41 fix: prune books missing form sd card in recent books list (#1959) 2026-05-16 22:03:49 +03:00
mvidelatraduc a3e51f9b1e chore: Update spanish.yaml (#2011) 2026-05-16 21:55:27 +03:00
Blue 90d4c885e1 fix: update URL-encoded image during EPUB optimization (#1985)
## Summary

* **What is the goal of this PR?**  
Fix EPUB optimization when XHTML image references are URL-encoded.

* **What changes are included?**  
The optimizer already converts image files to `.jpg`, but XHTML files
could still reference the original URL-encoded image path, for example:

```html
<img src="images/wensday%201%20full%202.png">
````

The optimized EPUB then contained the converted file:

```text
images/wensday 1 full 2.jpg
```

but the XHTML still pointed to the old `.png`, so CrossPoint failed to
extract/render the image.

The issue was that the previous replacement logic matched only the plain
filename form, such as:

```text
wensday 1 full 2.png
```

but not the URL-encoded form:

```text
wensday%201%20full%202.png
```

This PR updates XHTML image `src` attributes through the existing
DOMParser pass by decoding and resolving the image path before matching
it against renamed images.

After this fix, the optimized EPUB correctly rewrites the XHTML image
reference to the generated `.jpg`, and the image renders correctly.

---

### AI Usage

While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.

Did you use AI tools to help write this code? _**PARTIALLY**_

---

Please let me know if you have questions,
Thank you!
2026-05-15 21:30:49 -05:00
Kira ee947b06d1 fix: prevent card overflow on screens (#1943)
## Summary

prevent card overflow

## Additional Context

<img width="1920" height="1080" alt="bug"
src="https://github.com/user-attachments/assets/df84e233-908e-4ce1-8289-d0e9b579bc13"
/>
<img width="1920" height="1080" alt="Bug"
src="https://github.com/user-attachments/assets/cfd20f51-6421-4271-9a62-9c1987cc0dd0"
/>
<img width="1920" height="1080" alt="fix"
src="https://github.com/user-attachments/assets/4fbe83fe-5376-4393-bd45-a825f24e19e3"
/>
<img width="1920" height="1080" alt="fix2"
src="https://github.com/user-attachments/assets/4397848e-d8fa-4c51-ab4d-c32b2fcf33d1"
/>


---

### AI Usage

Did you use AI tools to help write this code? _**NO**_
2026-05-15 21:29:38 -05:00
marcinoktawian 28b907321e fix: use power button held time for shutdown logic (#1890)
## Summary

* **What is the goal of this PR?**
Fix incorrect power button long-press detection during shutdown/wake
verification by introducing dedicated power button timing logic.
* **What changes are included?**
* Added getPowerButtonHeldTime() to HalGPIO as a wrapper over input
manager logic
* Replaced generic getHeldTime() usage with power-button-specific timing
in verifyPowerButtonWakeup()
* Ensures shutdown/wake decision is based only on actual power button
hold duration, not any-button timing
  * Minor header update for new API exposure in HalGPIO.h
## Additional Context

This fixes a bug where holding another button while briefly pressing the
power button could incorrectly trigger shutdown behavior due to shared
timing state (getHeldTime()).

The change isolates power button timing to prevent cross-button
interference and makes shutdown logic reliable during multi-button
interactions.

No behavioral changes are expected outside of power-button handling
logic.

**Dependencies**
- SDK PR: https://github.com/crosspoint-reader/community-sdk/pull/3

This PR requires the `community-sdk` submodule to be updated after the
SDK change is merged.

- Fixes: #1881

---

### AI Usage
Did you use AI tools to help write this code? _**PARTIALLY**_
2026-05-15 21:28:52 -05:00
Stefan Blixten Karlsson 77afea4d95 feat: Add swedish hyphenation (#1637)
## Summary

* Add swedish hyphenation using scripts/update_hypenation.sh
* Add hyphenation test data using the Swedish translation of Andy Weir's
Project Hail Mary

---

### AI Usage

While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.

Did you use AI tools to help write this code? _**NO**_
2026-05-15 21:27:52 -05:00
WuTofu 2f342508bc fix: several QoL updates for SD font's UI (#1965)
## Summary

* **What is the goal of this PR?**  
Improve the UI based on feedback from someone on discord

> Downloading ALL fonts feature.
> 1.1 Disable sleep when downloading, in my case went directly to sleep
just right after downloading.
> 1.2 It would be great to have and overall progress indicator as we
only have the indication of each font family
> 1.3 Any cancel or pause function might come in handy in case battery
is running out and then resume or retry with pending fonts

* **What changes are included?**  
- Now the UI can show overall progress across every file being
downloaded in the batch, not just progress inside the current family.
- Extended `HttpDownloader::downloadToFile()` to accept a cancel flag
and abort the download.
- Rendered a cancel button in the font download UI while a download is
in progress.
- `preventAutoSleep()` in `FontDownloadActivity.h` now returns true for
`state_ == COMPLETE` and `state_ == ERROR` in addition to
`LOADING_MANIFEST` and `DOWNLOADING`

## Additional Context

Not very satisfied with how `HttpDownloader.cpp` is right now, might try
to refactor it after v1.3.0

---

### AI Usage

While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.

Did you use AI tools to help write this code? _**PARTIALLY**_
2026-05-15 21:25:48 -05:00
luca 7bb1f7ed76 fix: update Italian translation (#1970)
## Summary
* **What is the goal of this PR?** Update the Italian translation.
* **What changes are included?** Took the latest `english.yaml` as
reference and updated `italian.yaml` accordingly, translating new
strings and revising existing ones where needed. Specific changes can be
inspected from the diff.

## Additional Context
* Nothing special to flag — happy to adjust any wording the reviewer
disagrees with.

---
### AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it helps set the right
context for reviewers.
Did you use AI tools to help write this code? _**PARTIALLY**_ — Claude
provided a first-pass draft; I revised and rewrote a substantial portion
by hand.
2026-05-15 20:52:42 -05:00
Jeremy Klein 7acc31bc34 fix: silent-reboot on wifi activity exit to clear heap fragmentation (#1908)
WiFi/LWIP/netif teardown scatters long-lived allocations across the
heap, leaving ~50KB of contiguous space unrecoverable without a reboot.

Reboot the SoC on exit from any wifi-using activity to guarantee a clean
heap. An RTC_NOINIT flag survives the reboot and tells setup() to skip
the boot splash and route the user back where they came from:
  - File transfer / Calibre / OPDS / Font download -> home
  - KOReader sync -> currently-open EPUB

Activities check WiFi.getMode() before rebooting, so backing out of the
network mode menu without joining doesn't trigger a cycle. KOSync also
esp_wifi_stop()s after the sync result so the radio is off while the
user reads it; full teardown happens at the reboot.


## Additional Context

The silent reboot skips the booting splash screen - it visibly looks
like a screen refresh. This does cause a disconnection/reconnection blip
for developers actively pulling logs over serial, but `pio device
monitor` and the like successfully reconnect and feed in the early boot
serial.
as an example: 
```
[256676] [DBG] [ACT] Exiting activity: KOReaderSync
[256706] [DBG] [MAIN] Silent restart (target=reader)

ESP-ROM:esp32c3-api1-20210207
Build:Feb  7 2021
rst:0xc (RTC_SW_CPU_RST),boot:0xf (SPI_FAST_FLASH_BOOT)
Saved PC:0x403872bc
SPIWP:0xee
mode:DIO, clock div:1
load:0x3fcd72a0,len:0x990
load:0x403cbf10,len:0xac8
load:0x403ce710,len:0x4d28
entry 0x403cbf10
[22] [INF] [MAIN] Hardware detect: X4
[29] [SD] SD card detected
[43] [DBG] [CPS] Settings loaded from file
[58] [DBG] [KRS] Loaded KOReader credentials for user: jeremydk
[69] [DBG] [OPS] Loaded 1 OPDS servers from file
[69] [DBG] [UI] Using Lyra theme
[70] [DBG] [MAIN] Starting CrossPoint version 1.2.0-dev-detached-bde75787

...

[203] [DBG] [ACT] Entering activity: Reader
[211] [DBG] [EBP] Loading ePub: /Halting State - Charles Stross.epub
[221] [DBG] [BMC] Loaded cache data: 51 spine, 41 TOC entries
[246] [DBG] [CSS] Loaded 41 rules from cache
[247] [DBG] [EBP] Loaded ePub: /Halting State - Charles Stross.epub
```
---
### AI Usage

While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.

Did you use AI tools to help write this code? _PARTIALLY_
2026-05-15 19:27:54 -05:00
938ca3fac1 chore: Update version to 1.3.0 (#1827)
## Summary

This release adds SD card fonts — the most-requested feature since
launch — brings the X3 to first-class status, redesigns the on-screen
keyboard, overhauls OPDS, and ships SD-card firmware updates. 144
changes from 53 contributors, 32 of whom are new to the project.

**🔠 SD Card Fonts**
Custom fonts are here. A complete font subsystem lets you install and
use fonts beyond the three built-in families. A new `.cpfont` binary
format packs multiple styles (regular, bold, italic, bold-italic) into a
single file per size, with on-demand glyph loading from the SD card. A
two-pass prewarm renderer bulk-reads glyphs per page, achieving
near-flash performance for Latin text and viable CJK rendering. Fonts
can be downloaded over WiFi directly from the device, uploaded via the
web interface, or copied manually to the SD card. The build pipeline
ships a 17-family font library (serif, sans, mono, accessibility) with
CI distribution via a dedicated crosspoint-fonts repository. As a bonus,
CJK characters no longer get spurious hyphens at line breaks, and an
advance-table cache eliminates 30+ second stalls during CJK section
indexing.

**📱 X3 Comes of Age**
The X3 graduates from initial bring-up to a proper target. Grayscale
antialiasing is sharper, EPUB images render correctly, OTA updates work,
and sleep screen dimensions are dialed in. The headline addition:
gyroscope-based tilt page turning via the QMI8658 IMU — tilt the device
to turn pages hands-free. SD-card firmware update support and X3
bootloader compatibility mean users can update without a USB connection.

**⌨️ Redesigned On-Screen Keyboard**
The keyboard has been completely redesigned with improved layout, better
key feedback, and a fix for the space key barely moving the cursor. Text
entry across WiFi setup, OPDS search, and KOSync login is noticeably
smoother.

**👁️ Focus Reading**
A new reading mode bolds the initial characters of each word (similar to
Bionic Reading) to create artificial fixation points, helping improve
reading speed and focus. The bolding ratio is 45%, with a minimum of 1
character and a maximum of 9, applied dynamically during indexing.

**📚 OPDS Overhaul**
OPDS gains in-catalog search with next/prev page navigation, support for
multiple servers, correct handling of relative paths and query
parameters (fixing CopyParty compatibility), and KOReader-compatible
download filenames.

**🔤 Text Rendering Refinements**
Combining marks (diacritics) now use font metrics for positioning
instead of heuristics, proportional numeral spacing is supported, and
differential rounding eliminates uneven inter-glyph gaps. Hyphenation
now recognizes ISO 639-2 language codes, nested block-level CSS styles
are tracked correctly, and horizontal CSS insets are capped at 2em to
prevent runaway margins. Bookerly has been replaced with Noto Serif for
licensing reasons.

**🎨 New Theme: RoundedRaff**
A new rounded theme joins the theme picker, with fixes for sleep cover
crop grid artifacts.

**🔋 Battery & Power**
Battery percentage smoothing on the X4 eliminates jittery readings. A
short press on the power button can be set to trigger a manual screen
refresh — handy for clearing ghosting.

**📶 WiFi & Networking**
WiFi connections now self-heal from transient drops without manual
intervention, and a dBm signal strength indicator appears during web
server sessions. WiFi networks can be edited directly from the web UI.

**🔄 KOSync**
Reading position sync is significantly more accurate. The old
character-offset approach frequently landed on the wrong paragraph after
syncing between devices — the new xpath-based mapping syncs at the
paragraph level, matching KOReader's own behavior. A separate fix
switches the HTTP layer to `esp_http_client`, and the reader now
releases ~65KB of EPUB heap before the TLS handshake — together these
eliminate the out-of-memory crashes that plagued KOSync on large books.

**🛡️ Stability**
Two memory leaks patched, a wild pointer crash in JPEGDEC MCU_SKIP
handling fixed, boot loops with large XTC files eliminated, legacy XTC
headers supported, the OTA updater now streams GitHub release JSON
instead of buffering it in RAM, and a JPEG downscaler y-axis scale
factor bug is corrected.

**🌐 Languages**
Slovenian is new. Russian, Ukrainian, Swedish, Italian, and Spanish
translations received significant updates.

---

Also in this release: **SD-card firmware updates without USB**, **file
extensions in the file browser**, **full path bar navigation**,
**end-of-book navigation improvements**, **XTC status bar**, **smarter
"Cover + Custom" sleep screens**, **set sleep cover from the BMP
viewer**, **orientation-aware popups**, **page turn buttons that follow
orientation**, **long-press delete for directories**, **context-aware
screenshot filenames with book title**, **crash reason displayed on
boot**, **empty line rendering in the TXT reader**, **wallpaper recency
buffer to prevent clustering**, **font family deletion from the
device**, **next/prev labels in the BMP viewer**, **non-breaking space
justification fix**, **README guidance for USB-locked third-party Xteink
units**, and a long tail of web UI polish, i18n memory optimizations,
and code quality improvements.

## What's Changed

### Features

* feat: add SD card font support with on-device download and web
management by @adriancaruana, @znelson, @itsthisjustin, @jpirnay, and
@mcrosson
* feat: Initial support for the x3 by @itsthisjustin in
https://github.com/crosspoint-reader/crosspoint-reader/pull/875
* feat: X3 grayscale antialiasing improvements by @juicecultus in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1607
* feat: X3 gyroscope-based tilt page turning via QMI8658 IMU by
@juicecultus in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1636
* feat(update): SD-card firmware update + X3 bootloader compatibility by
@eunchurn in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1786
* feat: self-heal from transient WiFi loss, add dBm indicator during
WebServerActivity by @jeremydk in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1780
* feat: edit wifi networks in webui by @osteotek in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1743
* feat: add OPDS search support & next/prev page navigation by @rxmmah
in https://github.com/crosspoint-reader/crosspoint-reader/pull/1462
* feat: Support for multiple OPDS servers by @osteotek in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1209
* feat: Adjust Navigation at End of Book by @nscheung in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1425
* feat: Display file extensions in File Browser by @CaptainFrito in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1019
* feat: show full path bar in file browser by @zgredex in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1411
* feat: enable manual screen refresh on power button short press by
@bdeshi in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1626
* feat: Rework "Cover + Custom" sleep screens to show covers only when
currently reading by @iandchasse in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1256
* feat: Set sleep cover from BMP viewer by @el in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1104
* feat: show crash reason on boot by @ngxson in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1453
* feat: Support for proportional numeral spacing by @znelson in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1414
* feat: add orientation-aware popups for reader activities by @mrtnvgr
in https://github.com/crosspoint-reader/crosspoint-reader/pull/1428
* feat: smooth battery percentage for x4 by @jonvex in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1635
* feat: context-aware screenshot filenames with book title by
@jonstieglitz in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1589
* feat(theme): add roundedraff theme and fix sleep cover crop grid
artifacts by @bunsoootchi in
https://github.com/crosspoint-reader/crosspoint-reader/pull/918
* feat: Page turn button orientation change by @mchuck in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1069
* feat: Status bar for XTC files by @leecming82 in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1849
* feat: enhance long press action to delete both files and directories
by @WuTofu in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1803
* feat: Added Slovenian translation by @thehijacker in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1551
* feat: focus reading by @vjapolitzer in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1670
* feat: add next / prev labels to bmp viewer by @Telemaniaka in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1852
* feat: add font family deletion functionality by @WuTofu in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1919
* feat: separate into "Download All" and "Update All" in font manager by
@WuTofu in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1955
* feat: verify CRC32 checksum for font files by @WuTofu in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1904
* feat: increase default weight of Bitter font for improved rendering by
@uxjulia in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1922
* feat: allow unnamed intervals by @steka in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1903

### Fixes

* fix: epub images not rendering correctly on x3 by @itsthisjustin in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1572
* fix: OTA update on x3 and progress bar on x4 and x3 by @itsthisjustin
in https://github.com/crosspoint-reader/crosspoint-reader/pull/1805
* fix: boot looping when opening large XTC files by @itsthisjustin in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1648
* fix: Wild pointer crash in JPEGDEC MCU_SKIP handling by @itsthisjustin
in https://github.com/crosspoint-reader/crosspoint-reader/pull/1627
* fix: two small memory leaks by @Uri-Tauber in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1628
* fix: use esp_http_client for KOSync to prevent TLS OOM on ESP32-C3 by
@trilwu in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1381
* fix: Read GH release JSON as stream in OTA updater by @znelson in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1810
* fix: support legacy XTC file headers where pageTableOffset=48 by
@uxjulia in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1816
* fix: Use font metrics for combining mark positioning by @znelson in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1310
* fix: Use differential rounding for consistent inter-glyph spacing by
@znelson in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1413
* fix: Support hyphenation for EPUBs using ISO 639-2 language codes by
@znelson in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1461
* fix: Track block style stack for nested styles by @daveallie in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1582
* fix: cap per-side horizontal CSS inset at 2em by @rhoopr in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1694
* fix: increase loadable epub size by @CSCMe in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1638
* fix: Switch to xpath map for paragraph level syncing in KOSync by
@itsthisjustin in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1686
* fix: free Epub RAM and simplify KOSync navigation via ActivityManager
by @wylanswets in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1860
* fix: improve KOSync bidirectional position matching accuracy by
@wylanswets in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1897
* fix: Fix failing very first wifi connection attempt by @jpirnay in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1521
* fix: avoid skipping chapter after screenshot by @Mraulio in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1625
* fix: back navigation from BMPViewer by @Telemaniaka in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1597
* fix: Fix ghosting on exit of BMPViewer by @jpirnay in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1432
* fix: make footnotes consider orientation for gutters by @Telemaniaka
in https://github.com/crosspoint-reader/crosspoint-reader/pull/1665
* fix: footnote link text by @steka in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1666
* fix: Erroneous navigation with long filenames in footnote links by
@CSCMe in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1723
* fix: prevent wallpaper clustering with 16-entry recency buffer by
@zgredex in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1606
* fix: webserver /delete API backward compatibility by @DianaNites in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1475
* fix: relative opds paths and query param with copyparty by @philips in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1535
* fix: use same file name as KOReader for OPDS downloads by @spfenwick
in https://github.com/crosspoint-reader/crosspoint-reader/pull/1286
* fix: pressing space barely moves input cursor (#1729) by @pablohc in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1733
* fix: keyboard feedback #1644 by @pablohc in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1697
* fix: pluralize folder/file counts correctly in file list summary by
@fain182 in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1701
* fix: rendering bug of scrollbar in RoundedRaff theme by @Uri-Tauber in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1814
* fix: two roundedraff bugs by @Uri-Tauber in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1851
* fix: overlap in download font list layout by @pablohc in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1900
* fix: remove duplicate 'Download Fonts' menu entry and improve
navigation by @zgredex in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1893
* fix: Add common ligatures to SD font conversion ranges by @znelson
* fix: capture instantiateVariableFont return value by @jpirnay in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1911
* fix: Roundraff theme home menu offset with no recent books by @znelson
in https://github.com/crosspoint-reader/crosspoint-reader/pull/1845
* fix: Missing navigation button labels in Roundedraff theme by
@Uri-Tauber in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1905
* fix: gracefully resolve fonts missing variants by @Uri-Tauber in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1921
* fix: distribute justifyExtra to non-breaking space tokens by
@prawnwhoyawns in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1783
* fix: remove percent rendering from activities by @mcrosson in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1901
* fix: Restore performance in fontconvert_sdcard.py by @znelson in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1924
* fix: Prepare SD card font caches from txt reader by @znelson in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1973
* fix: make script help paths lightweight by @sabraman in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1937
* fix: Replaced Bookerly with Noto Serif for licensing reasons by
@znelson in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1736
* fix: incorrect y-axis scale factor in jpeg nearest-neighbor downscaler
by @WuTofu in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1807
* fix: display empty lines in txt reader by @Uri-Tauber in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1841
* fix: short-press power action triggered after screenshot combo release
by @pablohc in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1853
* fix: correct Russian auto-turn translations by @a-ignatev in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1566
* fix: Update Ukrainian translations for footnotes (issue 1409) by
@mirus-ua in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1585
* fix: missing swedish translations by @steka in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1667
* fix: Add swedish keyboard translations by @steka in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1726
* fix: swedish translations by @steka in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1762
* fix: swedish translation by @steka in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1829
* fix: swedish translation by @steka in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1888
* fix: Polish translation by @th0m4sek in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1909
* fix: Ukrainian-translation by @KymAndriy in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1946
* fix: Ukrainian translation by @KymAndriy in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1939
* fix: python requirements files by @steka in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1768
* fix: missing requirement by @steka in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1896
* fix: Use LOG_ macros in loc functions by @znelson in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1794

### Internal

* refactor: redesign on-screen keyboard by @pablohc in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1644
* refactor: replace picojpeg with JPEGDEC for cover art conversion by
@jpirnay in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1517
* refactor: Refactor drawArc / fillArc for faster execution by @jpirnay
in https://github.com/crosspoint-reader/crosspoint-reader/pull/1540
* perf: replace i18n pointer tables with offset tables, strip unused
strings by @jpirnay in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1408
* refactor: Store only unique localization strings in offset buffers by
@znelson in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1802
* refactor: Move language setting into JSON settings by @znelson in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1796
* refactor: Use C++20 'requires' in ActivityResult constructor by
@znelson in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1420
* refactor: Use default member initializers for JpegContext and
PngContext by @znelson in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1435
* refactor: logPrintf and predefined log level strings by @CSCMe in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1546
* refactor: RAII scoped open/close for ZipFile by @znelson in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1433
* refactor: Deduplicated BMP header writing in Xtc by @znelson in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1439
* refactor: Added shared XML parser teardown helper by @znelson in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1438
* refactor: Removed redundant FsFile close() calls by @znelson in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1434
* refactor: Deduplicate battery drawing code and fix Lyra charging
indicator by @znelson in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1437
* refactor: Deduplicate Roundraff battery drawing by @znelson in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1847
* refactor: Simplify sort in GfxRenderer::fillPolygon by @znelson in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1817
* refactor: Avoid vector for page turn rates list by @znelson in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1818
* refactor: Use std::size instead of sizeof/sizeof by @znelson in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1819
* refactor: Use fixed-size integers for BookMetadataCache data by
@znelson in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1844
* refactor: Simplify isReaderActivity bookkeeping by @znelson in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1838
* refactor: Simplify XtcReaderActivity with detectPageTurn by @znelson
in https://github.com/crosspoint-reader/crosspoint-reader/pull/1837
* refactor: change ukrainian translation to adaptation and add missing
lines by @KymAndriy in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1828
* chore: drop JPEGDEC patch in favour of upstream fix by @martinbrook in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1465
* chore: clang-format.fix.ps1 script: Add .venv to list of path
exclusions by @jpirnay in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1515
* chore: Updating sleep screen dimensions for X3 by @jensechu in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1688
* chore: Clarify X3 RTC in SCOPE.md by @znelson in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1687
* chore: Improved Italian translations by @znelson in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1685
* chore: change ukrainian translation to adaptation by @KymAndriy in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1684
* chore: Update spanish.yaml by @mvidelatraduc in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1717
* chore: One Italian translation tweak by @znelson in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1718
* chore: git pre-commit hook for format fix by @osteotek in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1730
* chore: Update SDK to fork in CrossPoint org by @znelson in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1836
* chore: Added RAM to firmware_size_history.py script by @znelson in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1830
* chore: Updated docs to reflect DESTRUCTOR_CLOSES_FILE=1 by @znelson in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1878
* feat: cap compressed group size at 64 KB by @jpirnay in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1913
* fix: build-script bug fixes for fontconvert{,_sdcard}.py by @jpirnay
in https://github.com/crosspoint-reader/crosspoint-reader/pull/1910
* feat: include short SHA in CROSSPOINT_VERSION by @osteotek in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1728
* feat: show long branch names by @steka in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1727
* feat: enable pio build cache by @Uri-Tauber in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1769
* style: put page name first in browser titles by @fain182 in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1703
* style: unify page headers across web UI by @fain182 in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1702
* style: move file type badges into Type column by @fain182 in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1793
* style: align action buttons vertically with page title by @fain182 in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1795
* docs: Update README with firmware flashing instructions by @ryneches
in https://github.com/crosspoint-reader/crosspoint-reader/pull/1654
* docs: fix typos by @kianmeng in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1705
* docs: update README.md to reflect the current state of crosspoint by
@Uri-Tauber in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1812
* docs: Add documentation for USB-locked Xteink devices by
@itsthisjustin in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1990
* docs: expand first use of OPDS acronym and provide a wikipedia link by
@sizezero in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1824
* docs: fix KOReader sync guide link by @sabraman in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1930
* docs: fix hyphenation updater script name by @sabraman in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1931
* fix: sd font download urls in docs by @mcrosson in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1945
* fix: sd font folder paths in documentation by @mcrosson in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1944
* chore: Add verbose mode to build-sd-fonts.py by @znelson in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1923

## New Contributors
* @a-ignatev made their first contribution in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1566
* @CSCMe made their first contribution in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1546
* @thehijacker made their first contribution in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1551
* @Telemaniaka made their first contribution in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1597
* @Mraulio made their first contribution in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1625
* @rxmmah made their first contribution in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1462
* @bdeshi made their first contribution in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1626
* @DianaNites made their first contribution in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1475
* @ryneches made their first contribution in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1654
* @zgredex made their first contribution in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1411
* @jonvex made their first contribution in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1635
* @KymAndriy made their first contribution in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1684
* @jensechu made their first contribution in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1688
* @kianmeng made their first contribution in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1705
* @philips made their first contribution in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1535
* @fain182 made their first contribution in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1701
* @mvidelatraduc made their first contribution in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1717
* @bunsoootchi made their first contribution in
https://github.com/crosspoint-reader/crosspoint-reader/pull/918
* @rhoopr made their first contribution in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1694
* @spfenwick made their first contribution in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1286
* @trilwu made their first contribution in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1381
* @jonstieglitz made their first contribution in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1589
* @uxjulia made their first contribution in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1816
* @mchuck made their first contribution in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1069
* @sizezero made their first contribution in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1824
* @leecming82 made their first contribution in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1849
* @jeremydk made their first contribution in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1780
* @WuTofu made their first contribution in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1803
* @wylanswets made their first contribution in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1860
* @sabraman made their first contribution in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1930
* @prawnwhoyawns made their first contribution in
https://github.com/crosspoint-reader/crosspoint-reader/pull/1783
* @mcrosson made their first contribution as co-author on SD card font
support

**Full Changelog**:
https://github.com/crosspoint-reader/crosspoint-reader/compare/1.2.0...release/1.3.0

---------

Co-authored-by: Justin Mitchell <justin@jmitch.com>
Co-authored-by: Chun Ming Lee <95391408+leecming82@users.noreply.github.com>
Co-authored-by: Uri Tauber <uritaube@gmail.com>
2026-05-15 15:51:43 -05:00
Uri TauberandZach Nelson 0fbe6ff469 fix: update README.md to reflect the current state of crosspoint (#1812)
## Summary

As noted in
[#1680](https://github.com/crosspoint-reader/crosspoint-reader/discussions/1680#discussioncomment-16661106),
the README hasn't been updated in a while and has fallen behind the
actual firmware. This PR brings it up to date.

Beyond the feature list, I added a section acknowledging community forks
worth knowing about. I also took some deliberate editorial choices
around how CrossPoint is framed — I think it has the potential to be
more than just "an alternative Xteink firmware", and the wording
reflects that.

A note on process: I wrote the bulk of the text myself, but used AI
tools to scan the codebase and catch features I might have missed, and
to clean up my English (I'm fluent but not a native speaker). If any
line reads as unnatural or AI-sounding, please flag it — I'd rather fix
it than leave it.

---

One thing outside the scope of this PR: I think the cover photo could
use a refresh, ideally replaced with a small gallery showing different
CrossPoint screens. If you have a professional camera and an Xteink
device and want to help with that, let me know.

---------

Co-authored-by: Zach Nelson <zach@zdnelson.com>
2026-05-15 15:38:22 -05:00
Justin Mitchell 43b20bd8be fix: Add documentation for USB-locked Xteink devices (#1990)
Document the Xteink Unlocker tool requirement for third-party purchased
xteink units that ship with USB flashing locked. Include warnings about
bricking risks when flashing unsupported firmwares (e.g. Papyrix) on
locked devices, as they may permanently lock the device with no recovery
path.
2026-05-15 10:44:47 -05:00
Zach NelsonandJustin Mitchell b186529120 fix: Prepare SD card font caches from txt reader (#1973)
## Summary

SD card font fixes:
- `TxtReaderActivity` needs to call `renderer.ensureSdCardFontReady` to
build the advance lookup table to support rendering with SD card fonts.
This revealed that `TxtReaderActivity` was inconsistently performing
layout with `getTextWidth`, when the renderer actually uses
`getTextAdvanceX`, which can lead to minor inconsistencies in alignment.
- Avoid allocating one big `allText` string in
`ParsedText::layoutAndExtractLines`. Instead, pass the vector of word
strings directly to `SdCardFont::buildAdvanceTable`, where the algorithm
just needs to iterate codepoints anyway.

---

### AI Usage

While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.

Did you use AI tools to help write this code? _**PARTIALLY**_

---------

Co-authored-by: Justin Mitchell <justin@jmitch.com>
2026-05-15 09:50:41 -05:00
IjonFryderykandClaude Opus 4.6 5fa5a71ba2 feat: Add Polish hyphenation support (#1590)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-14 16:32:59 +03:00
Zach Nelson aa43bd3569 chore: Removed unused icon header files (#1975)
## Summary

Deleted two unused header files containing binary icon data.

---

### AI Usage

While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.

Did you use AI tools to help write this code? _**NO**_
2026-05-14 07:34:35 -05:00
Zach Nelson 8377ac9e31 refactor: Added utils for non-throwing memory allocation and scoped cleanup (#1832)
## Summary

Pared down version of #1418.

Following up on b5df6cb2b5. Added
lib/Memory/Memory.h with:
- `makeUniqueNoThrow<T>` a `nothrow` wrapper for `std::make_unique` that
return `nullptr` on OOM instead of calling `abort()` (the behavior of
bare `new` with `-fno-exceptions`)
- `ScopedCleanup` a helper to call a cleanup lambda on scope exit.

These utilities help to write code that handles OOM scenarios
gracefully, and consistently cleans up resources on scope exit.

JpegToBmpConverter.cpp has been converted to use these utilities. Other
files can be converted later.

This will simplify some of the SD card font resource management in
#1327.

---

### AI Usage

While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.

Did you use AI tools to help write this code? _**NO**_
2026-05-13 21:19:47 -05:00
Fabio Barbonanddrbourbon 71518b3336 fix: KOSync authentication with Calibre-Web-Automated (#1951)
## Summary

* **What is the goal of this PR?** Fix regression of
Calibre-Web-Automated authentication
* **What changes are included?** Trivial fix using ESP HTTP Client
primitives

## Additional Context

Calibre-Web-Automated KOSync stopped working after refactoring HTTP
client to use `ESP HTTP Client`, resulting in a _Server error (try again
later)_ error.

---

### AI Usage

Did you use AI tools to help write this code? _**NO**_

---------

Co-authored-by: drbourbon <fabio@MacBook-Air-di-Fabio.local>
2026-05-13 08:32:18 -05:00
Zach Nelson 30b14f2ecf refactor: Removed SdCardFontGlobals.h (#1962)
## Summary

First of several changes to decouple and clean up SD card fonts
integration. This change eliminates SdCardFontGlobals.h:
- Simply declare the `extern SdCardFontSystem sdFontSystem` in
SdCardFontSystem.h.
- `ActivityManager::goToReader` should not care about loading SD card
fonts. Instead do the same work in `ReaderActivity::onEnter`, after the
previous activity has exited and after ReaderActivity has validated the
file path.

---

### AI Usage

While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.

Did you use AI tools to help write this code? _**NO**_
2026-05-13 08:26:56 -05:00
Zach Nelson 6f7f4c592a refactor: Eliminated relative path includes (#1961)
## Summary

Relative includes can hide inappropriate dependency relationships. In
this case, I found that lib/KOReaderSync/KOReaderCredentialStore.cpp was
dependent on src/JsonSettingsIO.h -- a lib -> app dependency, the
opposite direction dependencies should flow in this project.

This change replaces all relative includes with root-relative includes,
and corrects the KOReaderCredentialStore dependency by moving its JSON
settings serialization local to the KOReaderSync library.

---

### AI Usage

While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.

Did you use AI tools to help write this code? _**PARTIALLY**_
2026-05-13 08:26:30 -05:00
zgredex bc57e5d64b fix: jump page on hold in font family and language selection (#1925)
## Summary
- Holding the navigation button in **Settings → Reader → Font family**
now advances the selection by a full visible page instead of one item at
a time.
- Same fix applied to **Settings → Language**, which had the same
one-item-only behavior.
- Mirrors the pattern already used in font download (ceb3fed) and
chapter selection screens.

## Test plan
- [ ] Settings → Reader → Font family: tap moves by one item; hold jumps
a page (wraps at ends)
- [ ] Settings → Language: tap moves by one item; hold jumps a page
(wraps at ends)
2026-05-12 17:37:56 -04:00
Chun Ming LeeandUri Tauber db3bb850b2 fix: handle fallbacks for advance table and prewarm (#1929)
## Summary
- Fixes #1928 by having the prewarm and advance table functions resolve
fallback styles
---

### AI Usage
Did you use AI tools to help write this code?  YES - Codex

---------

Co-authored-by: Uri Tauber <uritaube@gmail.com>
2026-05-12 22:11:37 +03:00
Uri Tauber 30a209de8b fix: Characters from unsupported characterset are overlapping (#1958)
## Summary

* **What is the goal of this PR?** fix #1956.

## Additional Context



[NixOS-and-Flakes-Book_ch3_p7_4pct_130044.bmp](https://github.com/user-attachments/files/27648392/NixOS-and-Flakes-Book_ch3_p7_4pct_130044.bmp)

---

### AI Usage

While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.

Did you use AI tools to help write this code? _**< YES >**_
2026-05-12 14:00:17 -05:00
177 changed files with 16455 additions and 1398 deletions
+1 -2
View File
@@ -1,2 +1 @@
github: [daveallie]
ko_fi: daveallie
custom: ["https://app.royalty.dev/crosspoint-reader/crosspoint-reader"]
+65 -23
View File
@@ -58,6 +58,7 @@ find src -name "*.cpp" -o -name "*.h" | xargs clang-format -i
6. `constexpr` First: Compile-time constants and lookup tables must be `constexpr`, not just `static const`. This moves computation to compile time, enables dead-branch elimination, and guarantees flash placement. Use `static constexpr` for class-level constants.
7. `std::vector` Pre-allocation: Always call `.reserve(N)` before any `push_back()` loop. Each growth event allocates a new block (2×), copies all elements, then frees the old one — three heap operations that fragment DRAM. When the final size is unknown, estimate conservatively.
8. SPIFFS Write Throttling: Never write a settings file on every user interaction. Guard all writes with a value-change check (`if (newVal == _current) return;`). Progress saves during reading must be debounced — write on activity exit or every N page turns, not on every turn. SPIFFS sectors have a finite erase cycle limit.
9. `new` is not nothrow on ESP32: With `-fno-exceptions`, bare `new` that fails calls `abort()` — it does NOT return `nullptr`. Always use `new (std::nothrow)` and null-check the result, or use `makeUniqueNoThrow<T>()` from `lib/Memory/Memory.h`. Never write bare `new` for any fallible allocation.
---
@@ -160,6 +161,12 @@ if (Storage.openFileForRead("MODULE", "/path/to/file.bin", file)) {
**Usage**: See example above. Uses `FsFile` (SdFat), NOT Arduino `File`. Do NOT add `file.close()` for local variables (see DESTRUCTOR_CLOSES_FILE above).
**SdFat is not thread-safe; all SD access MUST go through HalStorage**:
- SdFat's `SdSpiCard` tracks SPI bus state with an unsynchronized `m_spiActive` bool. Two tasks calling SdFat concurrently can confuse that state machine and end with one task calling `SPIClass::endTransaction()` against a paramLock the *other* task is holding. That trips FreeRTOS's `xTaskPriorityDisinherit` assert (`tasks.c:5156, pxTCB == pxCurrentTCBs[0]`) and panics the system. See SdFat issue #518.
- `HalStorage` serializes everything via `storageMutex`. Downstream code includes `<HalStorage.h>`, which transparently `using FsFile = HalFile;`; every method call (read, write, seek, close) takes the mutex. `HalFile`'s destructor also takes the mutex before letting the underlying SdFat `FsFile` close.
- **Never** call into `SdFat` / `SdSpiCard` / `FsBaseFile` / `SDCardManager` directly. **Never** define `HAL_STORAGE_IMPL` outside `HalStorage.cpp`; that disables the `FsFile -> HalFile` typedef and you'll get a raw SdFat handle that bypasses the mutex.
- If you're storing a raw `FsFile` in a place that won't transitively include `<HalStorage.h>` (rare), include the header explicitly so the typedef applies.
---
## Coding Standards
@@ -266,43 +273,78 @@ When a template is necessary, limit instantiations: use explicit template instan
**Rules**: NO exceptions, NO abort(), ALWAYS log before error return
### Acceptable malloc/free Patterns
### Heap Buffer Allocation
**Source**: [src/activities/home/HomeActivity.cpp:166](../src/activities/home/HomeActivity.cpp), [lib/GfxRenderer/GfxRenderer.cpp:439-440](../lib/GfxRenderer/GfxRenderer.cpp)
**Prefer `makeUniqueNoThrow` over `malloc`.** Both are nothrow (return `nullptr` on OOM rather than calling `abort()`), but `malloc` requires a manual `free` on every return path — a common source of leaks. `makeUniqueNoThrow<uint8_t[]>(size)` from `lib/Memory/Memory.h` frees automatically when it goes out of scope.
Despite "prefer stack allocation," malloc is acceptable for:
1. **Large temporary buffers** (> 256 bytes, won't fit on stack)
2. **One-time allocations** during activity initialization
3. **Bitmap rendering buffers** (variable size, used briefly)
**Pattern**:
**Preferred pattern**:
```cpp
// Allocate
auto* buffer = static_cast<uint8_t*>(malloc(bufferSize));
#include <Memory.h>
auto buffer = makeUniqueNoThrow<uint8_t[]>(bufferSize);
if (!buffer) {
LOG_ERR("MODULE", "malloc failed: %d bytes", bufferSize);
return false; // Handle allocation failure
LOG_ERR("MODULE", "OOM: %d bytes", bufferSize);
return false;
}
// Use buffer
processData(buffer, bufferSize);
processData(buffer.get(), bufferSize);
// freed automatically — no manual free needed, no leak on early return
```
// Free immediately after use
free(buffer);
buffer = nullptr;
**`malloc` or `new (std::nothrow)` are still acceptable** when the buffer must be passed to a C API that takes ownership and frees it itself (e.g., certain SDK callbacks). In that case follow the manual pattern:
```cpp
auto* buffer = static_cast<uint8_t*>(malloc(bufferSize)); // or new (std::nothrow) uint8_t[bufferSize]
if (!buffer) {
LOG_ERR("MODULE", "OOM: %d bytes", bufferSize);
return false;
}
sdkApiThatTakesOwnership(buffer, bufferSize); // SDK calls free() / delete[]
```
**Rules**:
- **ALWAYS check for nullptr** after malloc
- **Free immediately** after use (don't hold across multiple operations)
- **Set to nullptr** after free (avoid use-after-free)
- **Document size**: Comment why stack allocation was rejected
- **Prefer `makeUniqueNoThrow`** — automatic cleanup eliminates leak risk on error paths
- **ALWAYS check for nullptr** after any allocation and `LOG_ERR` before returning false
- **Raw allocation only** when a C API takes ownership; document why in a comment
**Examples in codebase**:
- Memory utilities: [Memory.h](../lib/Memory/Memory.h) (`makeUniqueNoThrow`)
- Cover image buffers: [HomeActivity.cpp:166](../src/activities/home/HomeActivity.cpp)
- Text chunk buffers: [TxtReaderActivity.cpp:259](../src/activities/reader/TxtReaderActivity.cpp)
- Bitmap rendering: [GfxRenderer.cpp:439-440](../lib/GfxRenderer/GfxRenderer.cpp)
- OTA update buffer: [OtaUpdater.cpp:40](../src/network/OtaUpdater.cpp)
### Heap Allocation with `new`: Always Use `makeUniqueNoThrow`
**CRITICAL**: With `-fno-exceptions`, bare `new` on OOM calls `abort()` — it does NOT return `nullptr`. Always use `makeUniqueNoThrow` from `lib/Memory/Memory.h`, which wraps `new (std::nothrow)` and returns a `std::unique_ptr` that is null on OOM and automatically frees on scope exit.
**Preferred pattern**:
```cpp
#include <Memory.h>
auto obj = makeUniqueNoThrow<MyClass>(args);
if (!obj) { LOG_ERR("MOD", "OOM: MyClass"); return false; }
auto buf = makeUniqueNoThrow<uint8_t[]>(size);
if (!buf) { LOG_ERR("MOD", "OOM: %d bytes", size); return false; }
// Pass to C APIs via .get(); unique_ptr frees automatically on return
someApi(buf.get(), size);
```
**`new (std::nothrow)` directly is acceptable** when the object must be passed to a C API that takes ownership and calls `delete` itself:
```cpp
auto* obj = new (std::nothrow) MyClass(args);
if (!obj) { LOG_ERR("MOD", "OOM: MyClass"); return false; }
sdkApiThatTakesOwnership(obj); // SDK calls delete
```
**Rules**:
- **Prefer `makeUniqueNoThrow`** — automatic cleanup eliminates leak risk on error paths
- **NEVER use bare `new`** — always `makeUniqueNoThrow` or `new (std::nothrow)`
- **ALWAYS `LOG_ERR` before returning false** on OOM
- **Use `.get()`** to pass the raw pointer to C-style APIs; ownership stays with the `unique_ptr`
- **`new (std::nothrow)` directly only** when a C API takes ownership; document why in a comment
**Examples in codebase**:
- Memory utilities: [Memory.h](../lib/Memory/Memory.h) (`makeUniqueNoThrow`)
---
+16
View File
@@ -1,5 +1,7 @@
# CrossPoint Reader
[![Fund contributors](https://img.shields.io/badge/%F0%9F%91%91_Fund_contributors-royalty.dev-BB953A?style=for-the-badge&labelColor=1a1a1a)](https://app.royalty.dev/crosspoint-reader/crosspoint-reader)
CrossPoint is open-source e-reader firmware - community-built, fully hackable, free forever. It's maintained by a growing community of developers and readers who believe your device should do what you want - not what a manufacturer decided for you.
**Now running on:** ESP32C3-based Xteink [X4](https://www.xteink.com/products/xteink-x4) and [X3](https://www.xteink.com/products/xteink-x3).
@@ -121,6 +123,20 @@ See [Development quick start](#development-quick-start) below.
---
## Custom SD-card fonts
Convert your own TTF/OTF files into `.cpfont` files that load from the SD card. No firmware reflash is needed.
1. Go to https://crosspointreader.com/fonts and open the "SD-card font builder" form.
2. Upload up to four styles (regular, bold, italic, bold-italic), set the family name, point sizes, and Unicode range.
3. Download the generated `.cpfont` files.
4. Copy them to your SD card under `/fonts/YourFont/` (or `/.fonts/YourFont/` to hide the folder).
5. Select the font on the device from the font settings.
Conversion runs the firmware repo's `lib/EpdFont/scripts/fontconvert_sdcard.py` script unmodified, so output matches a local host build.
---
## Documentation
- [User Guide](./USER_GUIDE.md)
+36 -15
View File
@@ -61,29 +61,29 @@ struct TocEntry {
struct BookBin {
// Header
u8 version [[comment("Format version"), color("FFD93D")]];
// Version validation
if (version != EXPECTED_VERSION) {
std::error(std::format("Unsupported version: {} (expected {})", version, EXPECTED_VERSION));
}
u32 lutOffset [[comment("Offset to lookup tables"), color("6BCB77")]];
u16 spineCount [[comment("Number of spine entries"), color("4D96FF")]];
u16 tocCount [[comment("Number of TOC entries"), color("FF6B9D")]];
// Metadata section
Metadata metadata [[comment("Book metadata")]];
// Validate LUT offset alignment
u32 currentOffset = $;
if (currentOffset != lutOffset) {
std::warning(std::format("LUT offset mismatch: expected 0x{:X}, got 0x{:X}", lutOffset, currentOffset));
}
// Lookup Tables
u32 spineLut[spineCount] [[comment("Spine entry offsets"), color("4D96FF")]];
u32 tocLut[tocCount] [[comment("TOC entry offsets"), color("FF6B9D")]];
// Data Entries
SpineEntry spines[spineCount] [[comment("Spine entries (reading order)")]];
TocEntry toc[tocCount] [[comment("Table of contents entries")]];
@@ -104,7 +104,7 @@ if (parsedSize != fileSize) {
## `section.bin`
### Version 8
### Version 24
ImHex Pattern:
@@ -114,7 +114,7 @@ import std.string;
import std.core;
// === Configuration ===
#define EXPECTED_VERSION 8
#define EXPECTED_VERSION 24
#define MAX_STRING_LENGTH 65535
// === String Structure ===
@@ -133,8 +133,10 @@ fn format_string(String s) {
// === Page Structure ===
enum StorageType : u8 {
PageLine = 1
enum PageElementTag : u8 {
PageLine = 1,
PageImage = 2,
PageHorizontalRule = 3
};
enum WordStyle : u8 {
@@ -161,10 +163,29 @@ struct PageLine {
BlockStyle blockStyle;
};
struct PageImage {
s16 xPos;
s16 yPos;
String imagePath;
s16 width;
s16 height;
};
struct PageHorizontalRule {
s16 xPos;
s16 yPos;
u16 width;
u8 thickness;
};
struct PageElement {
u8 pageElementType;
if (pageElementType == 1) {
PageLine pageLine [[inline]];
} else if (pageElementType == 2) {
PageImage pageImage [[inline]];
} else if (pageElementType == 3) {
PageHorizontalRule horizontalRule [[inline]];
} else {
std::error(std::format("Unknown page element type: {}", pageElementType));
}
@@ -180,12 +201,12 @@ struct Page {
struct SectionBin {
// Header
u8 version [[comment("Format version"), color("FFD93D")]];
// Version validation
if (version != EXPECTED_VERSION) {
std::error(std::format("Unsupported version: {} (expected {})", version, EXPECTED_VERSION));
}
// Cache busting parameters
s32 fontId;
float lineCompression;
@@ -194,15 +215,15 @@ struct SectionBin {
u16 vieportHeight;
u16 pageCount;
u32 lutOffset;
Page page[pageCount];
// Validate LUT offset alignment
u32 currentOffset = $;
if (currentOffset != lutOffset) {
std::warning(std::format("LUT offset mismatch: expected 0x{:X}, got 0x{:X}", lutOffset, currentOffset));
}
// Lookup Tables
u32 lut[pageCount];
};
+1
View File
@@ -97,6 +97,7 @@ To convert your own TrueType/OpenType fonts:
| `cyrillic` | Cyrillic + Supplement |
| `cjk` | CJK Unified Ideographs + Hiragana + Katakana + Fullwidth |
| `hangul` | Korean Hangul syllables |
| `reading` | Literary fiction coverage: Latin, Greek, Cyrillic, math/symbol blocks, supplemental punctuation, and CJK quote marks |
| `builtin` | Matches built-in Bookerly coverage exactly |
Combine presets with commas: `--intervals latin-ext,greek,cyrillic`
+19 -18
View File
@@ -204,40 +204,41 @@ Folder created: NewFolder
### POST `/delete` - Delete File or Folder
Deletes a file or folder from the SD card.
Deletes one or more files or empty folders from the SD card.
**Request:**
```bash
# Delete a file
curl -X POST -d "path=/Books/mybook.epub&type=file" http://crosspoint.local/delete
curl -X POST -d "path=/Books/mybook.epub" http://crosspoint.local/delete
# Delete an empty folder
curl -X POST -d "path=/OldFolder&type=folder" http://crosspoint.local/delete
curl -X POST -d "path=/OldFolder" http://crosspoint.local/delete
# Delete multiple items
curl -X POST -d 'paths=["/Books/old.epub","/OldFolder"]' http://crosspoint.local/delete
```
**Form Parameters:**
| Parameter | Required | Default | Description |
| --------- | -------- | ------- | -------------------------------- |
| `path` | Yes | - | Path to the item to delete |
| `type` | No | `file` | Type of item: `file` or `folder` |
| Parameter | Required | Default | Description |
| --------- | -------- | ------- | ----------- |
| `path` | Yes, unless `paths` is provided | - | Path to one item to delete |
| `paths` | Yes, unless `path` is provided | - | JSON array of paths to delete |
**Response (200 OK):**
```
Deleted successfully
```text
All items deleted successfully
```
**Error Responses:**
| Status | Body | Cause |
| ------ | --------------------------------------------- | ----------------------------- |
| 400 | `Missing path` | `path` parameter not provided |
| 400 | `Cannot delete root directory` | Attempted to delete `/` |
| 400 | `Folder is not empty. Delete contents first.` | Non-empty folder |
| 403 | `Cannot delete system files` | Hidden file (starts with `.`) |
| 403 | `Cannot delete protected items` | Protected system folder |
| 404 | `Item not found` | Path does not exist |
| 500 | `Failed to delete item` | SD card error |
| Status | Body | Cause |
| ------ | ------------------------------------------- | ---------------------------------- |
| 400 | `Missing "path" or "paths" argument` | Neither parameter was provided |
| 400 | `Provide either 'path' or 'paths', not both` | Both delete parameters were sent |
| 400 | `Invalid paths format` | `paths` was not valid JSON |
| 400 | `No paths provided` | `paths` was an empty JSON array |
| 500 | `Failed to delete some items: ...` | One or more paths could not be deleted |
**Protected Items:**
- Files/folders starting with `.`
+6 -2
View File
@@ -1079,13 +1079,17 @@ int SdCardFont::fetchAdvancesForCodepoints(uint32_t* codepoints, uint32_t cpCoun
uint32_t needCount = 0;
uint32_t missedThisStyle = 0;
const int32_t replacementIdx = findGlobalGlyphIndex(s, REPLACEMENT_GLYPH);
for (uint32_t i = 0; i < cpCount; i++) {
const uint32_t cp = codepoints[i];
if (advanceTableLookup(si, cp, nullptr)) continue; // already cached
int32_t idx = findGlobalGlyphIndex(s, cp);
if (idx < 0) {
missedThisStyle++;
continue;
if (replacementIdx < 0) {
missedThisStyle++;
continue;
}
idx = replacementIdx;
}
mappings[needCount].codepoint = cp;
mappings[needCount].glyphIndex = idx;
+3 -1
View File
@@ -59,12 +59,14 @@ INTERVAL_PRESETS = {
(0x2190, 0x21FF), (0x2200, 0x22FF), (0x2500, 0x257F),
(0x25A0, 0x25FF), (0x2600, 0x26FF), (0x2700, 0x27BF)],
# Composite preset for English-language literary fiction including scifi/popsci.
# Greek for physics terms, math operators, miscellaneous symbols (♪♫♬), dingbats.
# Greek for physics terms, math operators, geometric shapes, uncommon
# dialogue punctuation, CJK quote marks, miscellaneous symbols (♪♫♬), dingbats.
"reading": [(0x0020, 0x024F), (0x0300, 0x036F), (0x0370, 0x03FF),
(0x0400, 0x04FF), (0x1E00, 0x1EFF), (0x2000, 0x206F),
(0x2070, 0x209F), (0x20A0, 0x20CF), (0x2150, 0x218F),
(0x2190, 0x21FF), (0x2200, 0x22FF), (0x2500, 0x257F),
(0x25A0, 0x25FF), (0x2600, 0x26FF), (0x2700, 0x27BF),
(0x2900, 0x29FF), (0x2E00, 0x2E7F), (0x3000, 0x303F),
(0xFB00, 0xFB06)],
# Matches the built-in font intervals from fontconvert.py exactly
"builtin": [(0x0000, 0x007F), (0x0080, 0x00FF), (0x0100, 0x017F),
+11
View File
@@ -113,6 +113,17 @@ families:
italic: {url: "https://raw.githubusercontent.com/google/fonts/main/ofl/bitter/Bitter-Italic%5Bwght%5D.ttf", variable: {wght: 500}}
bolditalic: {url: "https://raw.githubusercontent.com/google/fonts/main/ofl/bitter/Bitter-Italic%5Bwght%5D.ttf", variable: {wght: 700}}
- name: Domitian
description: "A humanist serif for literary reading (Latin, Greek, Cyrillic)"
intervals: latin-ext,greek,cyrillic
sizes: [12, 14, 16, 18]
styles:
regular: {url: "https://mirrors.ctan.org/fonts/domitian/opentype/Domitian-Roman.otf"}
bold: {url: "https://mirrors.ctan.org/fonts/domitian/opentype/Domitian-Bold.otf"}
italic: {url: "https://mirrors.ctan.org/fonts/domitian/opentype/Domitian-Italic.otf"}
bolditalic: {url: "https://mirrors.ctan.org/fonts/domitian/opentype/Domitian-BoldItalic.otf"}
# ── Sans-serif ─────────────────────────────────────────────────────────
- name: NotoSansExtended
+50
View File
@@ -1,8 +1,11 @@
#include "Page.h"
#include <GfxRenderer.h>
#include <Logging.h>
#include <Serialization.h>
#include <new>
void PageLine::render(GfxRenderer& renderer, const int fontId, const int xOffset, const int yOffset) {
block->render(renderer, fontId, xPos + xOffset, yPos + yOffset);
}
@@ -48,6 +51,47 @@ std::unique_ptr<PageImage> PageImage::deserialize(FsFile& file) {
return std::unique_ptr<PageImage>(new PageImage(std::move(ib), xPos, yPos));
}
void PageHorizontalRule::render(GfxRenderer& renderer, const int fontId, const int xOffset, const int yOffset) {
(void)fontId;
if (width == 0 || thickness == 0) {
return;
}
renderer.drawLine(xPos + xOffset, yPos + yOffset, xPos + xOffset + width - 1, yPos + yOffset, thickness, true);
}
bool PageHorizontalRule::serialize(FsFile& file) {
serialization::writePod(file, xPos);
serialization::writePod(file, yPos);
serialization::writePod(file, width);
serialization::writePod(file, thickness);
return true;
}
std::unique_ptr<PageHorizontalRule> PageHorizontalRule::deserialize(FsFile& file) {
int16_t xPos = 0;
int16_t yPos = 0;
uint16_t width = 0;
uint8_t thickness = 0;
serialization::readPod(file, xPos);
serialization::readPod(file, yPos);
serialization::readPod(file, width);
serialization::readPod(file, thickness);
if (width == 0 || thickness == 0) {
LOG_ERR("PGE", "Deserialization failed: invalid horizontal rule metadata (width=%u thickness=%u)", width,
thickness);
return nullptr;
}
auto* rule = new (std::nothrow) PageHorizontalRule(width, thickness, xPos, yPos);
if (!rule) {
LOG_ERR("PGE", "Deserialization failed: could not allocate PageHorizontalRule");
return nullptr;
}
return std::unique_ptr<PageHorizontalRule>(rule);
}
void Page::render(GfxRenderer& renderer, const int fontId, const int xOffset, const int yOffset) const {
for (auto& element : elements) {
element->render(renderer, fontId, xOffset, yOffset);
@@ -98,6 +142,12 @@ std::unique_ptr<Page> Page::deserialize(FsFile& file) {
} else if (tag == TAG_PageImage) {
auto pi = PageImage::deserialize(file);
page->elements.push_back(std::move(pi));
} else if (tag == TAG_PageHorizontalRule) {
auto rule = PageHorizontalRule::deserialize(file);
if (!rule) {
return nullptr;
}
page->elements.push_back(std::move(rule));
} else {
LOG_ERR("PGE", "Deserialization failed: Unknown tag %u", tag);
return nullptr;
+16 -1
View File
@@ -12,7 +12,8 @@
enum PageElementTag : uint8_t {
TAG_PageLine = 1,
TAG_PageImage = 2, // New tag
TAG_PageImage = 2,
TAG_PageHorizontalRule = 3,
};
// represents something that has been added to a page
@@ -55,6 +56,20 @@ class PageImage final : public PageElement {
const ImageBlock& getImageBlock() const { return *imageBlock; }
};
class PageHorizontalRule final : public PageElement {
uint16_t width;
uint8_t thickness;
public:
PageHorizontalRule(uint16_t width, uint8_t thickness, const int16_t xPos, const int16_t yPos)
: PageElement(xPos, yPos), width(width), thickness(thickness) {}
void render(GfxRenderer& renderer, int fontId, int xOffset, int yOffset) override;
bool serialize(FsFile& file) override;
PageElementTag getTag() const override { return TAG_PageHorizontalRule; }
static std::unique_ptr<PageHorizontalRule> deserialize(FsFile& file);
};
class Page {
public:
// the list of block index and line numbers on this page
+1 -1
View File
@@ -10,7 +10,7 @@
#include "parsers/ChapterHtmlSlimParser.h"
namespace {
constexpr uint8_t SECTION_FILE_VERSION = 23;
constexpr uint8_t SECTION_FILE_VERSION = 24;
constexpr uint32_t HEADER_SIZE = sizeof(uint8_t) + sizeof(int) + sizeof(float) + sizeof(bool) + sizeof(uint8_t) +
sizeof(uint16_t) + sizeof(uint16_t) + sizeof(uint16_t) + sizeof(bool) + sizeof(bool) +
sizeof(uint8_t) + sizeof(bool) + sizeof(uint32_t) + sizeof(uint32_t) +
+2 -2
View File
@@ -4,8 +4,8 @@
#include <Logging.h>
#include <Serialization.h>
#include "../converters/DirectPixelWriter.h"
#include "../converters/ImageDecoderFactory.h"
#include "Epub/converters/DirectPixelWriter.h"
#include "Epub/converters/ImageDecoderFactory.h"
// Cache file format:
// - uint16_t width
@@ -5,8 +5,10 @@
#include <HalStorage.h>
#include <JPEGDEC.h>
#include <Logging.h>
#include <Memory.h>
#include <cstdlib>
#include <memory>
#include <new>
#include "DirectPixelWriter.h"
@@ -347,16 +349,16 @@ bool JpegToFramebufferConverter::getDimensionsStatic(const std::string& imagePat
return false;
}
JPEGDEC* jpeg = new (std::nothrow) JPEGDEC();
std::unique_ptr<JPEGDEC> jpeg(new (std::nothrow) JPEGDEC());
if (!jpeg) {
LOG_ERR("JPG", "Failed to allocate JPEG decoder for dimensions");
return false;
}
int rc = jpeg->open(imagePath.c_str(), jpegOpen, jpegClose, jpegRead, jpegSeek, nullptr);
const ScopedCleanup cleanup{[&jpeg]() { jpeg->close(); }};
if (rc != 1) {
LOG_ERR("JPG", "Failed to open JPEG for dimensions (err=%d): %s", jpeg->getLastError(), imagePath.c_str());
delete jpeg;
return false;
}
@@ -364,8 +366,6 @@ bool JpegToFramebufferConverter::getDimensionsStatic(const std::string& imagePat
out.height = jpeg->getHeight();
LOG_DBG("JPG", "Image dimensions: %dx%d", out.width, out.height);
jpeg->close();
delete jpeg;
return true;
}
@@ -379,7 +379,7 @@ bool JpegToFramebufferConverter::decodeToFramebuffer(const std::string& imagePat
return false;
}
JPEGDEC* jpeg = new (std::nothrow) JPEGDEC();
std::unique_ptr<JPEGDEC> jpeg(new (std::nothrow) JPEGDEC());
if (!jpeg) {
LOG_ERR("JPG", "Failed to allocate JPEG decoder");
return false;
@@ -392,9 +392,9 @@ bool JpegToFramebufferConverter::decodeToFramebuffer(const std::string& imagePat
ctx.screenHeight = renderer.getScreenHeight();
int rc = jpeg->open(imagePath.c_str(), jpegOpen, jpegClose, jpegRead, jpegSeek, jpegDrawCallback);
const ScopedCleanup cleanup{[&jpeg]() { jpeg->close(); }};
if (rc != 1) {
LOG_ERR("JPG", "Failed to open JPEG (err=%d): %s", jpeg->getLastError(), imagePath.c_str());
delete jpeg;
return false;
}
@@ -403,14 +403,10 @@ bool JpegToFramebufferConverter::decodeToFramebuffer(const std::string& imagePat
if (srcWidth <= 0 || srcHeight <= 0) {
LOG_ERR("JPG", "Invalid JPEG dimensions: %dx%d", srcWidth, srcHeight);
jpeg->close();
delete jpeg;
return false;
}
if (!validateImageDimensions(srcWidth, srcHeight, "JPEG")) {
jpeg->close();
delete jpeg;
return false;
}
@@ -453,8 +449,6 @@ bool JpegToFramebufferConverter::decodeToFramebuffer(const std::string& imagePat
if (destWidth <= 0 || destHeight <= 0) {
LOG_ERR("JPG", "Degenerate output dimensions %dx%d for %s, skipping render", destWidth, destHeight,
imagePath.c_str());
jpeg->close();
delete jpeg;
return false;
}
@@ -490,13 +484,9 @@ bool JpegToFramebufferConverter::decodeToFramebuffer(const std::string& imagePat
if (rc != 1) {
LOG_ERR("JPG", "Decode failed (rc=%d, lastError=%d)", rc, jpeg->getLastError());
jpeg->close();
delete jpeg;
return false;
}
jpeg->close();
delete jpeg;
LOG_DBG("JPG", "JPEG decoding complete - render time: %lu ms", decodeTime);
// Write cache file if caching was enabled
@@ -4,9 +4,11 @@
#include <GfxRenderer.h>
#include <HalStorage.h>
#include <Logging.h>
#include <Memory.h>
#include <PNGdec.h>
#include <cstdlib>
#include <memory>
#include <new>
#include "DirectPixelWriter.h"
@@ -244,7 +246,7 @@ bool PngToFramebufferConverter::getDimensionsStatic(const std::string& imagePath
return false;
}
PNG* png = new (std::nothrow) PNG();
std::unique_ptr<PNG> png(new (std::nothrow) PNG());
if (!png) {
LOG_ERR("PNG", "Failed to allocate PNG decoder for dimensions");
return false;
@@ -252,18 +254,16 @@ bool PngToFramebufferConverter::getDimensionsStatic(const std::string& imagePath
int rc = png->open(imagePath.c_str(), pngOpenWithHandle, pngCloseWithHandle, pngReadWithHandle, pngSeekWithHandle,
nullptr);
const ScopedCleanup cleanup{[&png]() { png->close(); }};
if (rc != 0) {
LOG_ERR("PNG", "Failed to open PNG for dimensions: %d", rc);
delete png;
return false;
}
out.width = png->getWidth();
out.height = png->getHeight();
png->close();
delete png;
return true;
}
@@ -278,7 +278,7 @@ bool PngToFramebufferConverter::decodeToFramebuffer(const std::string& imagePath
}
// Heap-allocate PNG decoder (~42 KB) - freed at end of function
PNG* png = new (std::nothrow) PNG();
std::unique_ptr<PNG> png(new (std::nothrow) PNG());
if (!png) {
LOG_ERR("PNG", "Failed to allocate PNG decoder");
return false;
@@ -292,15 +292,13 @@ bool PngToFramebufferConverter::decodeToFramebuffer(const std::string& imagePath
int rc = png->open(imagePath.c_str(), pngOpenWithHandle, pngCloseWithHandle, pngReadWithHandle, pngSeekWithHandle,
pngDrawCallback);
const ScopedCleanup cleanup{[&png]() { png->close(); }};
if (rc != PNG_SUCCESS) {
LOG_ERR("PNG", "Failed to open PNG: %d", rc);
delete png;
return false;
}
if (!validateImageDimensions(png->getWidth(), png->getHeight(), "PNG")) {
png->close();
delete png;
return false;
}
@@ -335,8 +333,6 @@ bool PngToFramebufferConverter::decodeToFramebuffer(const std::string& imagePath
"PNG row buffer too small: need %d bytes for width=%d type=%d, configured PNG_MAX_BUFFERED_PIXELS=%d",
requiredInternal, ctx.srcWidth, pixelType, PNG_MAX_BUFFERED_PIXELS);
LOG_ERR("PNG", "Aborting decode to avoid PNGdec internal buffer overflow");
png->close();
delete png;
return false;
}
@@ -349,8 +345,6 @@ bool PngToFramebufferConverter::decodeToFramebuffer(const std::string& imagePath
ctx.grayLineBuffer = static_cast<uint8_t*>(malloc(grayBufSize));
if (!ctx.grayLineBuffer) {
LOG_ERR("PNG", "Failed to allocate gray line buffer");
png->close();
delete png;
return false;
}
@@ -380,13 +374,9 @@ bool PngToFramebufferConverter::decodeToFramebuffer(const std::string& imagePath
if (rc != PNG_SUCCESS) {
LOG_ERR("PNG", "Decode failed: %d", rc);
png->close();
delete png;
return false;
}
png->close();
delete png;
LOG_DBG("PNG", "PNG decoding complete - render time: %lu ms", decodeTime);
// Write cache file if caching was enabled and buffer was allocated
@@ -13,9 +13,19 @@ uint32_t toLowerLatinImpl(const uint32_t cp) {
return cp + 0x20;
}
// Latin Extended-A (U+0100..U+017E): uppercase letters are paired with
// lowercase at cp+1. Two sub-ranges have different alignment:
// U+0100..U+0137: uppercase on EVEN codepoints
// U+0139..U+0148: uppercase on ODD codepoints
// U+014A..U+0177: uppercase on EVEN codepoints
// U+0179..U+017E: uppercase on ODD codepoints
// Covers Polish (Ą/ą, Ć/ć, Ę/ę, Ł/ł, Ń/ń, Ś/ś, Ź/ź, Ż/ż), Czech, Hungarian, Turkish, etc.
if ((cp >= 0x0100 && cp <= 0x0137 && (cp % 2 == 0)) || (cp >= 0x0139 && cp <= 0x0148 && (cp % 2 == 1)) ||
(cp >= 0x014A && cp <= 0x0177 && (cp % 2 == 0)) || (cp >= 0x0179 && cp <= 0x017E && (cp % 2 == 1))) {
return cp + 1;
}
switch (cp) {
case 0x0152: // Œ
return 0x0153; // œ
case 0x0178: // Ÿ
return 0x00FF; // ÿ
case 0x1E9E: // ẞ
@@ -54,6 +64,11 @@ bool isLatinLetter(const uint32_t cp) {
return true;
}
// Latin Extended-A (U+0100..U+017F): Polish, Czech, Hungarian, Turkish, etc.
if (cp >= 0x0100 && cp <= 0x017F) {
return true;
}
switch (cp) {
case 0x0152: // Œ
case 0x0153: // œ
@@ -273,6 +288,30 @@ std::vector<CodepointInfo> collectCodepoints(const std::string& word) {
case 0x0079:
composed = 0x00FD;
break; // y -> ý
case 0x0043:
composed = 0x0106;
break; // C -> Ć
case 0x0063:
composed = 0x0107;
break; // c -> ć
case 0x004E:
composed = 0x0143;
break; // N -> Ń
case 0x006E:
composed = 0x0144;
break; // n -> ń
case 0x0053:
composed = 0x015A;
break; // S -> Ś
case 0x0073:
composed = 0x015B;
break; // s -> ś
case 0x005A:
composed = 0x0179;
break; // Z -> Ź
case 0x007A:
composed = 0x017A;
break; // z -> ź
default:
break;
}
@@ -373,6 +412,18 @@ std::vector<CodepointInfo> collectCodepoints(const std::string& word) {
break;
}
break;
case 0x0307: // dot above (Polish Ż)
switch (prev) {
case 0x005A:
composed = 0x017B;
break; // Z -> Ż
case 0x007A:
composed = 0x017C;
break; // z -> ż
default:
break;
}
break;
case 0x0327: // cedilla
switch (prev) {
case 0x0043:
@@ -385,6 +436,24 @@ std::vector<CodepointInfo> collectCodepoints(const std::string& word) {
break;
}
break;
case 0x0328: // ogonek (Polish Ą, Ę)
switch (prev) {
case 0x0041:
composed = 0x0104;
break; // A -> Ą
case 0x0061:
composed = 0x0105;
break; // a -> ą
case 0x0045:
composed = 0x0118;
break; // E -> Ę
case 0x0065:
composed = 0x0119;
break; // e -> ę
default:
break;
}
break;
default:
break;
}
+3 -4
View File
@@ -22,10 +22,9 @@ struct Iso639Mapping {
const char* iso639_2;
const char* iso639_1;
};
static constexpr Iso639Mapping kIso639Mappings[] = {
{"eng", "en"}, {"fra", "fr"}, {"fre", "fr"}, {"deu", "de"}, {"ger", "de"},
{"rus", "ru"}, {"spa", "es"}, {"ita", "it"}, {"ukr", "uk"},
};
static constexpr Iso639Mapping kIso639Mappings[] = {{"eng", "en"}, {"fra", "fr"}, {"fre", "fr"}, {"deu", "de"},
{"ger", "de"}, {"rus", "ru"}, {"spa", "es"}, {"ita", "it"},
{"ukr", "uk"}, {"swe", "sv"}};
// Maps a BCP-47 or ISO 639-2 language tag to a language-specific hyphenator.
const LanguageHyphenator* hyphenatorForLanguage(const std::string& langTag) {
@@ -9,7 +9,9 @@
#include "generated/hyph-es.trie.h"
#include "generated/hyph-fr.trie.h"
#include "generated/hyph-it.trie.h"
#include "generated/hyph-pl.trie.h"
#include "generated/hyph-ru.trie.h"
#include "generated/hyph-sv.trie.h"
#include "generated/hyph-uk.trie.h"
namespace {
@@ -21,9 +23,11 @@ LanguageHyphenator germanHyphenator(de_patterns, isLatinLetter, toLowerLatin);
LanguageHyphenator russianHyphenator(ru_patterns, isCyrillicLetter, toLowerCyrillic);
LanguageHyphenator spanishHyphenator(es_patterns, isLatinLetter, toLowerLatin);
LanguageHyphenator italianHyphenator(it_patterns, isLatinLetter, toLowerLatin);
LanguageHyphenator swedishHyphenator(sv_patterns, isLatinLetter, toLowerLatin);
LanguageHyphenator ukrainianHyphenator(uk_patterns, isCyrillicLetter, toLowerCyrillic);
LanguageHyphenator polishHyphenator(pl_patterns, isLatinLetter, toLowerLatin);
using EntryArray = std::array<LanguageEntry, 7>;
using EntryArray = std::array<LanguageEntry, 9>;
const EntryArray& entries() {
static const EntryArray kEntries = {{{"english", "en", &englishHyphenator},
@@ -32,6 +36,8 @@ const EntryArray& entries() {
{"russian", "ru", &russianHyphenator},
{"spanish", "es", &spanishHyphenator},
{"italian", "it", &italianHyphenator},
{"polish", "pl", &polishHyphenator},
{"swedish", "sv", &swedishHyphenator},
{"ukrainian", "uk", &ukrainianHyphenator}}};
return kEntries;
}
@@ -3,7 +3,7 @@
#include <cstddef>
#include <cstdint>
#include "../SerializedHyphenationTrie.h"
#include "Epub/hyphenation/SerializedHyphenationTrie.h"
// Auto-generated by generate_hyphenation_trie.py. Do not edit manually.
alignas(4) constexpr uint8_t de_trie_data[] = {
@@ -3,7 +3,7 @@
#include <cstddef>
#include <cstdint>
#include "../SerializedHyphenationTrie.h"
#include "Epub/hyphenation/SerializedHyphenationTrie.h"
// Auto-generated by generate_hyphenation_trie.py. Do not edit manually.
alignas(4) constexpr uint8_t en_trie_data[] = {
@@ -3,7 +3,7 @@
#include <cstddef>
#include <cstdint>
#include "../SerializedHyphenationTrie.h"
#include "Epub/hyphenation/SerializedHyphenationTrie.h"
// Auto-generated by generate_hyphenation_trie.py. Do not edit manually.
alignas(4) constexpr uint8_t es_trie_data[] = {
@@ -3,7 +3,7 @@
#include <cstddef>
#include <cstdint>
#include "../SerializedHyphenationTrie.h"
#include "Epub/hyphenation/SerializedHyphenationTrie.h"
// Auto-generated by generate_hyphenation_trie.py. Do not edit manually.
alignas(4) constexpr uint8_t fr_trie_data[] = {
@@ -3,7 +3,7 @@
#include <cstddef>
#include <cstdint>
#include "../SerializedHyphenationTrie.h"
#include "Epub/hyphenation/SerializedHyphenationTrie.h"
// Auto-generated by generate_hyphenation_trie.py. Do not edit manually.
alignas(4) constexpr uint8_t it_trie_data[] = {
@@ -0,0 +1,987 @@
#pragma once
#include <cstddef>
#include <cstdint>
#include "Epub/hyphenation/SerializedHyphenationTrie.h"
// Auto-generated by generate_hyphenation_trie.py. Do not edit manually.
alignas(4) constexpr uint8_t pl_trie_data[] = {
0x3A, 0x26, 0x30, 0x48, 0x17, 0x3E, 0x0F, 0x52, 0x67, 0x0C, 0x44, 0x16, 0x0C, 0x0D, 0x16, 0x0D,
0x22, 0x23, 0x21, 0x35, 0x16, 0x35, 0x0C, 0x21, 0x0C, 0x5C, 0x0D, 0x1C, 0x20, 0x0D, 0x21, 0x0E,
0x34, 0x34, 0x0B, 0x3F, 0x16, 0x3F, 0x0C, 0x49, 0x0C, 0x53, 0x16, 0x70, 0x0D, 0x49, 0x16, 0x3E,
0x0D, 0x3F, 0x0E, 0x21, 0x0E, 0x0D, 0x21, 0x16, 0x22, 0x17, 0x22, 0x0D, 0x23, 0x0E, 0x0C, 0x34,
0x0D, 0x35, 0x0E, 0x7B, 0x16, 0x84, 0x0D, 0x5C, 0x17, 0x16, 0x0D, 0x0C, 0x0F, 0x16, 0x16, 0x0B,
0x0C, 0x0B, 0x2C, 0x17, 0x0E, 0x18, 0x0F, 0x0C, 0x2B, 0x0C, 0x52, 0x0D, 0x66, 0x2A, 0x0C, 0x0D,
0x2B, 0x18, 0x2B, 0x18, 0x0D, 0x2C, 0x19, 0x0C, 0x53, 0x0C, 0x2A, 0x17, 0x20, 0x0C, 0x21, 0x20,
0x21, 0x0E, 0x0C, 0x22, 0x0D, 0x0C, 0x0C, 0x22, 0x0F, 0x5F, 0x2B, 0x16, 0x2D, 0x16, 0x2D, 0x0C,
0x36, 0x0D, 0x2B, 0x0C, 0x16, 0x2B, 0x0C, 0x0C, 0x2B, 0x16, 0x0D, 0x41, 0x16, 0x34, 0x0C, 0x2C,
0x0D, 0x2C, 0x21, 0x3E, 0x0C, 0x3F, 0x20, 0x40, 0x0D, 0x0C, 0x4B, 0x52, 0x0C, 0x53, 0x20, 0x53,
0x0E, 0x53, 0x0E, 0x16, 0x53, 0x0C, 0x0C, 0x0D, 0x54, 0x0F, 0x54, 0x0F, 0x16, 0x54, 0x0F, 0x0C,
0x5D, 0x16, 0x5D, 0x0C, 0x7A, 0x17, 0x0E, 0x16, 0x17, 0x10, 0x17, 0x0C, 0x18, 0x11, 0x21, 0x0C,
0x0C, 0x2B, 0x0D, 0x23, 0x16, 0x23, 0x0C, 0x2A, 0x0D, 0x20, 0x16, 0x22, 0x0F, 0x0C, 0x2A, 0x16,
0x34, 0x0B, 0x20, 0x34, 0x17, 0x21, 0x0C, 0x0C, 0x0D, 0x22, 0x0D, 0x0E, 0x0C, 0x0D, 0x22, 0x0F,
0x16, 0x22, 0x17, 0x0C, 0x2B, 0x34, 0x0F, 0x35, 0x20, 0x2C, 0x0B, 0x2C, 0x0C, 0x35, 0x0E, 0x0D,
0x35, 0x0E, 0x16, 0x35, 0x0C, 0x0C, 0x0D, 0x36, 0x0F, 0x36, 0x0F, 0x16, 0x36, 0x0F, 0x0E, 0x48,
0x0D, 0x49, 0x0E, 0x3F, 0x2B, 0x0C, 0x21, 0x0E, 0x0D, 0x16, 0x21, 0x0E, 0x0D, 0x0C, 0x22, 0x0F,
0x0E, 0x0D, 0x36, 0x0F, 0x0C, 0x41, 0x0C, 0x48, 0x0B, 0x66, 0x0D, 0x2A, 0x16, 0x17, 0x20, 0x11,
0x4A, 0x0F, 0x18, 0x0D, 0x0C, 0x18, 0x17, 0x22, 0x0D, 0x20, 0x48, 0x21, 0x3E, 0x17, 0x21, 0x0C,
0x0D, 0x68, 0x34, 0x2B, 0x2B, 0x0E, 0x22, 0x0D, 0x0C, 0x0B, 0x0C, 0x52, 0x17, 0x36, 0x21, 0x15,
0x01, 0x16, 0x02, 0x15, 0x02, 0x0B, 0x02, 0x1F, 0x03, 0x0C, 0x03, 0x0C, 0x16, 0x03, 0x0C, 0x0C,
0x04, 0x17, 0x04, 0x0D, 0x08, 0x16, 0x0D, 0x16, 0x16, 0x0D, 0x16, 0x0D, 0x3E, 0x0B, 0x20, 0x0B,
0x20, 0x0C, 0x0D, 0x20, 0x17, 0x29, 0x16, 0x11, 0x16, 0x0F, 0x20, 0x0B, 0x0C, 0x2A, 0x0B, 0x16,
0x15, 0x18, 0x0E, 0x0D, 0x20, 0x0F, 0x16, 0x11, 0x3F, 0x18, 0x16, 0x0D, 0x16, 0x16, 0x0D, 0x17,
0x0C, 0x20, 0x15, 0x17, 0x0C, 0x0D, 0x16, 0x0C, 0x0B, 0xA0, 0x00, 0x41, 0x21, 0x87, 0xFD, 0x25,
0x82, 0x84, 0x9B, 0xBA, 0xBC, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xA0, 0x00, 0x61, 0xA0, 0x00, 0x72,
0x21, 0x87, 0xFD, 0x21, 0xC4, 0xFD, 0x21, 0x72, 0xFD, 0x21, 0x65, 0xFD, 0xA1, 0x00, 0x61, 0x69,
0xFD, 0xB5, 0x00, 0x51, 0xC4, 0xC5, 0x62, 0x63, 0x64, 0x66, 0x67, 0x68, 0x6A, 0x6B, 0x6C, 0x6D,
0x6E, 0x70, 0x72, 0x73, 0x74, 0x76, 0x77, 0x78, 0x7A, 0xDB, 0xDE, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9,
0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xFB, 0xE9, 0xE9, 0x21, 0x87,
0xD3, 0xD5, 0x00, 0x51, 0xC4, 0xC5, 0x62, 0x63, 0x64, 0x66, 0x67, 0x68, 0x6A, 0x6B, 0x6C, 0x6D,
0x6E, 0x70, 0x72, 0x73, 0x74, 0x76, 0x77, 0x78, 0x7A, 0xFF, 0xAB, 0xFF, 0xAE, 0xFF, 0xB9, 0xFF,
0xB9, 0xFF, 0xB9, 0xFF, 0xB9, 0xFF, 0xB9, 0xFF, 0xB9, 0xFF, 0xB9, 0xFF, 0xB9, 0xFF, 0xB9, 0xFF,
0xB9, 0xFF, 0xB9, 0xFF, 0xB9, 0xFF, 0xB9, 0xFF, 0xB9, 0xFF, 0xB9, 0xFF, 0xB9, 0xFF, 0xB9, 0xFF,
0xB9, 0xFF, 0xB9, 0xA0, 0x00, 0xB1, 0xA1, 0x00, 0x92, 0x72, 0xFD, 0x21, 0x64, 0xFB, 0x21, 0xB3,
0xFD, 0xA1, 0x00, 0x61, 0xC3, 0xFD, 0xA0, 0x00, 0xC2, 0x21, 0x77, 0xFD, 0x21, 0x6F, 0xFD, 0x21,
0x82, 0xFD, 0x21, 0xC5, 0xFD, 0x21, 0x74, 0xFD, 0x21, 0x61, 0xFD, 0xA1, 0x00, 0x61, 0x69, 0xFD,
0xD5, 0x00, 0x51, 0xC4, 0xC5, 0x62, 0x63, 0x64, 0x66, 0x67, 0x68, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E,
0x70, 0x72, 0x73, 0x74, 0x76, 0x77, 0x78, 0x7A, 0xFF, 0x3C, 0xFF, 0x3F, 0xFF, 0x4A, 0xFF, 0x4A,
0xFF, 0x4A, 0xFF, 0x4A, 0xFF, 0x4A, 0xFF, 0x4A, 0xFF, 0x4A, 0xFF, 0x4A, 0xFF, 0x4A, 0xFF, 0x4A,
0xFF, 0x4A, 0xFF, 0x4A, 0xFF, 0xE1, 0xFF, 0x4A, 0xFF, 0x4A, 0xFF, 0x4A, 0xFF, 0xFB, 0xFF, 0x4A,
0xFF, 0x4A, 0xA0, 0x00, 0xE1, 0x21, 0xBA, 0xFD, 0xA1, 0x00, 0x61, 0xC5, 0xFD, 0xD5, 0x00, 0x51,
0xC4, 0xC5, 0x62, 0x63, 0x64, 0x66, 0x67, 0x68, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x70, 0x72, 0x73,
0x74, 0x76, 0x77, 0x78, 0x7A, 0xFE, 0xEF, 0xFE, 0xF2, 0xFE, 0xFD, 0xFE, 0xFD, 0xFF, 0xFB, 0xFE,
0xFD, 0xFE, 0xFD, 0xFE, 0xFD, 0xFE, 0xFD, 0xFE, 0xFD, 0xFE, 0xFD, 0xFE, 0xFD, 0xFE, 0xFD, 0xFE,
0xFD, 0xFE, 0xFD, 0xFE, 0xFD, 0xFE, 0xFD, 0xFE, 0xFD, 0xFE, 0xFD, 0xFE, 0xFD, 0xFE, 0xFD, 0x45,
0x82, 0x84, 0x9B, 0xBA, 0xBC, 0xFF, 0x02, 0xFF, 0x02, 0xFF, 0x71, 0xFF, 0xBE, 0xFF, 0x02, 0xA0,
0x00, 0xF3, 0x21, 0x74, 0xFD, 0x21, 0x73, 0xFD, 0xA0, 0x01, 0x51, 0x21, 0x74, 0xFD, 0xA1, 0x01,
0x41, 0x70, 0xFD, 0xA0, 0x01, 0x41, 0xA1, 0x01, 0x41, 0x75, 0xF2, 0xA2, 0x01, 0x41, 0x70, 0x72,
0xED, 0xED, 0xA6, 0x01, 0x22, 0x61, 0x65, 0x69, 0x6F, 0x75, 0x79, 0xEC, 0xF1, 0xF4, 0xF9, 0xF1,
0xF1, 0xA0, 0x00, 0x91, 0xA5, 0x01, 0x61, 0x61, 0x65, 0x69, 0x6F, 0x75, 0xFD, 0xFD, 0xFD, 0xFD,
0xFD, 0x21, 0x6F, 0xF3, 0x21, 0x72, 0xFD, 0x25, 0x61, 0x65, 0x69, 0x6F, 0x75, 0xEA, 0xEA, 0xEA,
0xEA, 0xEA, 0x21, 0x79, 0xF5, 0x21, 0x74, 0xFD, 0xA0, 0x01, 0x72, 0x21, 0x82, 0xFD, 0xA1, 0x01,
0x92, 0x7A, 0xFA, 0xA0, 0x01, 0x92, 0x29, 0xC5, 0x62, 0x6B, 0x6D, 0x61, 0x65, 0x69, 0x6F, 0x75,
0xF5, 0xF8, 0xFD, 0xFD, 0xCB, 0xCB, 0xCB, 0xCB, 0xCB, 0x21, 0x79, 0xED, 0x21, 0x63, 0xFD, 0xA0,
0x01, 0xB2, 0x21, 0x68, 0xD6, 0xA0, 0x01, 0xD2, 0x21, 0x73, 0xFD, 0x21, 0x6E, 0xFD, 0x21, 0x61,
0xFD, 0x21, 0x72, 0xFD, 0x47, 0x63, 0x61, 0x65, 0x69, 0x6F, 0x74, 0x75, 0xFF, 0xEE, 0xFF, 0x9D,
0xFF, 0x9D, 0xFF, 0x9D, 0xFF, 0x9D, 0xFF, 0xFD, 0xFF, 0x9D, 0xA1, 0x01, 0xB2, 0x6F, 0xEA, 0x23,
0x67, 0x6B, 0x74, 0xD0, 0xD0, 0xFB, 0x46, 0x62, 0x64, 0x65, 0x6E, 0x72, 0x75, 0xFF, 0x4F, 0xFF,
0x6C, 0xFF, 0x8E, 0xFF, 0x9F, 0xFF, 0xC6, 0xFF, 0xF9, 0x41, 0x87, 0xFD, 0xE1, 0x45, 0x82, 0x84,
0x9B, 0xBA, 0xBC, 0xFD, 0xDD, 0xFD, 0xDD, 0xFD, 0xDD, 0xFD, 0xDD, 0xFD, 0xDD, 0xA0, 0x00, 0x51,
0xA0, 0x02, 0x22, 0x21, 0x6E, 0xFD, 0x21, 0x63, 0xFA, 0x21, 0x6B, 0xF7, 0x41, 0x68, 0xFF, 0x45,
0xA0, 0x02, 0x41, 0xA0, 0x02, 0x52, 0x21, 0x6A, 0xFD, 0xA1, 0x02, 0x41, 0x62, 0xFD, 0x41, 0x77,
0xFF, 0x33, 0xA8, 0x02, 0x02, 0x61, 0x65, 0x69, 0x63, 0x6D, 0x6F, 0x77, 0x7A, 0xE1, 0xE4, 0xE7,
0xEA, 0xEE, 0xF7, 0xEE, 0xFC, 0x21, 0x7A, 0xED, 0xC1, 0x00, 0x51, 0x7A, 0xFD, 0x92, 0xB6, 0x01,
0xF1, 0xC4, 0xC5, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x70,
0x72, 0x73, 0x74, 0x76, 0x77, 0x78, 0x7A, 0xAB, 0xAF, 0xBF, 0xBF, 0xBF, 0xF7, 0xBF, 0xBF, 0xBF,
0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xFA, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xA0, 0x02, 0x72,
0x21, 0x9B, 0xFD, 0xA0, 0x02, 0x92, 0x22, 0xC5, 0x6B, 0xFA, 0xFD, 0x21, 0x6F, 0xFB, 0x21, 0x82,
0xFD, 0x21, 0xC5, 0xFD, 0x41, 0x7A, 0xFD, 0x35, 0xA1, 0x00, 0x51, 0x72, 0xFC, 0xA0, 0x02, 0xB2,
0x21, 0x77, 0xFD, 0x21, 0x6F, 0xFD, 0x21, 0x6B, 0xFD, 0xA0, 0x02, 0xD2, 0x21, 0x72, 0xFD, 0x21,
0x6B, 0xFD, 0x21, 0x6F, 0xFD, 0x21, 0x82, 0xFD, 0x21, 0xC5, 0xFD, 0x22, 0x6E, 0x70, 0xEB, 0xFD,
0x21, 0x65, 0xFB, 0x21, 0x6B, 0xDA, 0x21, 0x6F, 0xFD, 0x21, 0x6E, 0xFD, 0x21, 0x72, 0xFD, 0xA0,
0x02, 0xF2, 0x21, 0x74, 0xFD, 0x21, 0x65, 0xFD, 0xA1, 0x00, 0x72, 0x73, 0xFD, 0x21, 0x68, 0xFB,
0x21, 0x63, 0xFD, 0xA0, 0x03, 0x12, 0x21, 0x9B, 0xFD, 0x21, 0xC5, 0xFD, 0x22, 0x65, 0x6F, 0xF4,
0xFD, 0x21, 0x72, 0xFB, 0x21, 0x65, 0xFD, 0xA0, 0x03, 0x52, 0x22, 0x85, 0x99, 0xFD, 0xFD, 0xA4,
0x03, 0x32, 0xC4, 0x61, 0x65, 0x6F, 0xFB, 0xF8, 0xF8, 0xF8, 0x21, 0x72, 0xF5, 0x21, 0xB3, 0xFD,
0x21, 0xC3, 0xFD, 0xC4, 0x00, 0x51, 0x61, 0x6B, 0x74, 0x77, 0xFF, 0xB9, 0xFC, 0xC7, 0xFF, 0xE1,
0xFF, 0xFD, 0xD7, 0x01, 0xF1, 0xC4, 0xC5, 0x61, 0x62, 0x63, 0x64, 0x66, 0x67, 0x68, 0x69, 0x6A,
0x6B, 0x6C, 0x6D, 0x6E, 0x70, 0x72, 0x73, 0x74, 0x76, 0x77, 0x78, 0x7A, 0xFE, 0xD7, 0xFE, 0xDB,
0xFF, 0x6F, 0xFE, 0xEB, 0xFE, 0xEB, 0xFE, 0xEB, 0xFE, 0xEB, 0xFE, 0xEB, 0xFF, 0x76, 0xFF, 0x9E,
0xFE, 0xEB, 0xFE, 0xEB, 0xFE, 0xEB, 0xFE, 0xEB, 0xFE, 0xEB, 0xFE, 0xEB, 0xFE, 0xEB, 0xFE, 0xEB,
0xFE, 0xEB, 0xFE, 0xEB, 0xFE, 0xEB, 0xFE, 0xEB, 0xFF, 0xF1, 0x41, 0x72, 0xFF, 0x79, 0x42, 0x74,
0x77, 0xFF, 0xFC, 0xFF, 0x2F, 0x21, 0x6F, 0xF9, 0x21, 0x67, 0xFD, 0xA1, 0x00, 0x61, 0x75, 0xFD,
0x45, 0x82, 0x84, 0x9B, 0xBA, 0xBC, 0xFF, 0xFB, 0xFC, 0x5A, 0xFC, 0x5A, 0xFC, 0x5A, 0xFC, 0x5A,
0x41, 0x65, 0xFF, 0x16, 0x21, 0x6C, 0xFC, 0xA0, 0x03, 0x73, 0x21, 0x6C, 0xFD, 0x21, 0x69, 0xFD,
0x21, 0x75, 0xF7, 0xA2, 0x02, 0x41, 0x62, 0x77, 0xFA, 0xFD, 0x42, 0x6C, 0x72, 0xFE, 0x66, 0xFE,
0x66, 0xC4, 0x02, 0x02, 0x61, 0x65, 0x79, 0x6F, 0xFF, 0xF2, 0xFF, 0xF9, 0xFE, 0x5F, 0xFE, 0x6F,
0x21, 0x7A, 0xF1, 0xA0, 0x03, 0xA2, 0x21, 0x87, 0xFD, 0xA0, 0x03, 0xC2, 0xA1, 0x03, 0xA2, 0x6B,
0xFD, 0x24, 0x82, 0x9B, 0xBA, 0xBC, 0xFB, 0xF2, 0xF2, 0xF2, 0xC1, 0x02, 0x41, 0x7A, 0xFD, 0x87,
0xA1, 0x01, 0xB2, 0x72, 0xFA, 0xC2, 0x01, 0xB2, 0x68, 0x7A, 0xFE, 0x3B, 0xFE, 0x3B, 0x42, 0xBA,
0xBC, 0xFD, 0x73, 0xFD, 0x73, 0xC2, 0x01, 0xB2, 0xC5, 0x7A, 0xFF, 0xF9, 0xFE, 0x2B, 0x41, 0x7A,
0xFD, 0x63, 0xA1, 0x01, 0xB2, 0x72, 0xFC, 0xA0, 0x03, 0xE2, 0xA1, 0x01, 0xB2, 0x74, 0xFD, 0xA1,
0x01, 0xB2, 0x6E, 0xF8, 0xA0, 0x04, 0x03, 0xA1, 0x03, 0xE2, 0x6E, 0xFD, 0xA1, 0x01, 0xB2, 0x6B,
0xFB, 0xC2, 0x01, 0xB2, 0x63, 0x72, 0xFD, 0xFB, 0xFF, 0xDD, 0x41, 0xBC, 0xFD, 0x37, 0xC3, 0x01,
0xB2, 0x73, 0xC5, 0x7A, 0xFF, 0xD9, 0xFF, 0xFC, 0xFD, 0xF2, 0xC3, 0x01, 0xB2, 0x63, 0x6D, 0x7A,
0xFD, 0xE2, 0xFD, 0xE6, 0xFD, 0xE6, 0xC2, 0x01, 0xB2, 0x6B, 0x72, 0xFD, 0xDA, 0xFD, 0xDA, 0xA1,
0x01, 0xB2, 0x63, 0xB8, 0x51, 0xC4, 0xC5, 0x62, 0x63, 0x64, 0x66, 0x67, 0x68, 0x6B, 0x6C, 0x6D,
0x70, 0x72, 0x73, 0x74, 0x77, 0x7A, 0xFF, 0x72, 0xFF, 0x7D, 0xFF, 0x8C, 0xFF, 0x91, 0xFF, 0xA1,
0xFD, 0x5B, 0xFF, 0xAE, 0xFD, 0x5B, 0xFF, 0xB6, 0xFF, 0xBB, 0xFF, 0xC8, 0xFF, 0xCD, 0xFF, 0xDA,
0xFF, 0xE6, 0xFF, 0xF2, 0xFF, 0xFB, 0xFD, 0x5B, 0x41, 0x77, 0xFD, 0xFB, 0x21, 0x6F, 0xFC, 0x21,
0x67, 0xFD, 0xC2, 0x00, 0x51, 0x6F, 0x7A, 0xFF, 0xFD, 0xFB, 0x48, 0xA0, 0x04, 0x52, 0x22, 0x85,
0x99, 0xFD, 0xFD, 0xA4, 0x04, 0x32, 0xC4, 0x61, 0x65, 0x6F, 0xFB, 0xF8, 0xF8, 0xF8, 0x21, 0x6A,
0xF5, 0x21, 0xB3, 0xFD, 0xA1, 0x00, 0x51, 0xC3, 0xFD, 0x41, 0x61, 0xFD, 0x57, 0xA0, 0x01, 0x61,
0xC5, 0x02, 0x02, 0x65, 0x6F, 0x74, 0x79, 0x7A, 0xFD, 0x50, 0xFD, 0x50, 0xFF, 0xF9, 0xFD, 0x50,
0xFF, 0xFD, 0xC2, 0x02, 0x02, 0x65, 0x75, 0xFD, 0x3E, 0xFD, 0x4E, 0x22, 0x73, 0x7A, 0xE5, 0xF7,
0xA0, 0x04, 0x72, 0x21, 0x9B, 0xFD, 0x21, 0xC5, 0xFD, 0x21, 0x6F, 0xFD, 0x21, 0x69, 0xFD, 0x21,
0x63, 0xFD, 0x21, 0x99, 0xFD, 0x21, 0xC4, 0xFD, 0x21, 0x69, 0xFD, 0xA0, 0x04, 0x92, 0x21, 0x74,
0xFD, 0x21, 0x65, 0xFD, 0xA1, 0x04, 0xB2, 0x73, 0xFD, 0x21, 0x87, 0xFB, 0x22, 0xC4, 0x63, 0xFD,
0xE0, 0x21, 0x99, 0xFB, 0x21, 0xC4, 0xFD, 0x21, 0x69, 0xFD, 0x22, 0x73, 0x77, 0xDE, 0xFD, 0x21,
0x65, 0xFB, 0xA1, 0x00, 0x51, 0x69, 0xFD, 0xD9, 0x01, 0xF1, 0xC4, 0xC5, 0x61, 0x62, 0x63, 0x64,
0x65, 0x66, 0x67, 0x68, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x72, 0x73, 0x74, 0x76, 0x77,
0x78, 0x79, 0x7A, 0xFC, 0xD2, 0xFE, 0x59, 0xFE, 0x6D, 0xFC, 0xE6, 0xFC, 0xE6, 0xFC, 0xE6, 0xFE,
0x99, 0xFC, 0xE6, 0xFC, 0xE6, 0xFC, 0xE6, 0xFC, 0xE6, 0xFC, 0xE6, 0xFC, 0xE6, 0xFC, 0xE6, 0xFC,
0xE6, 0xFF, 0x2D, 0xFC, 0xE6, 0xFF, 0x6B, 0xFC, 0xE6, 0xFC, 0xE6, 0xFC, 0xE6, 0xFF, 0x8D, 0xFC,
0xE6, 0xFF, 0xB4, 0xFF, 0xFB, 0xA0, 0x05, 0x61, 0xA1, 0x00, 0xF3, 0x79, 0xFD, 0x21, 0x73, 0xFB,
0xA0, 0x04, 0xD5, 0x21, 0x74, 0xFD, 0x21, 0x65, 0xFD, 0x21, 0x7A, 0xFD, 0x21, 0x73, 0xFD, 0x21,
0x65, 0xFD, 0xA0, 0x05, 0x24, 0x21, 0x61, 0xFD, 0x21, 0x6D, 0xFD, 0x21, 0x73, 0xFD, 0xA0, 0x05,
0x22, 0x21, 0x61, 0xFD, 0x23, 0x65, 0x68, 0x74, 0xF7, 0xFD, 0xFA, 0x45, 0x61, 0x65, 0x69, 0x6F,
0x75, 0xFC, 0x75, 0xFC, 0x75, 0xFC, 0x75, 0xFC, 0x75, 0xFC, 0x75, 0x21, 0x6F, 0xF0, 0xA0, 0x01,
0xD1, 0x25, 0x61, 0x65, 0x69, 0x6F, 0x75, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0x21, 0x6F, 0xF5, 0x21,
0x72, 0xFD, 0x21, 0x74, 0xFD, 0x21, 0x6B, 0xFD, 0x21, 0x65, 0xFD, 0x45, 0x6B, 0x6D, 0x73, 0x67,
0x6C, 0xFF, 0xA2, 0xFF, 0xB4, 0xFF, 0xC9, 0xFF, 0xE0, 0xFF, 0xFD, 0xD5, 0x01, 0xF1, 0xC4, 0xC5,
0x62, 0x63, 0x64, 0x66, 0x67, 0x68, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x70, 0x72, 0x73, 0x74, 0x76,
0x77, 0x78, 0x7A, 0xFC, 0x0E, 0xFC, 0x12, 0xFC, 0x22, 0xFC, 0x22, 0xFC, 0x22, 0xFC, 0x22, 0xFC,
0x22, 0xFC, 0x22, 0xFC, 0x22, 0xFC, 0x22, 0xFC, 0x22, 0xFC, 0x22, 0xFC, 0x22, 0xFC, 0x22, 0xFC,
0x22, 0xFC, 0x22, 0xFC, 0x22, 0xFC, 0x22, 0xFC, 0x22, 0xFC, 0x22, 0xFC, 0x22, 0xA0, 0x02, 0x02,
0x21, 0x6F, 0xFD, 0x21, 0x75, 0xFA, 0x41, 0x62, 0xFE, 0x56, 0xC2, 0x00, 0x51, 0x75, 0x7A, 0xFF,
0xFC, 0xF9, 0xA0, 0xD7, 0x01, 0xF1, 0xC4, 0xC5, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x6A,
0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x72, 0x73, 0x74, 0x76, 0x77, 0x78, 0x7A, 0xFB, 0xB6, 0xFB,
0xBA, 0xFB, 0xCA, 0xFB, 0xCA, 0xFB, 0xCA, 0xFF, 0xED, 0xFB, 0xCA, 0xFB, 0xCA, 0xFB, 0xCA, 0xFB,
0xCA, 0xFB, 0xCA, 0xFB, 0xCA, 0xFB, 0xCA, 0xFB, 0xCA, 0xFF, 0xF0, 0xFB, 0xCA, 0xFF, 0xF7, 0xFB,
0xCA, 0xFB, 0xCA, 0xFB, 0xCA, 0xFB, 0xCA, 0xFB, 0xCA, 0xFB, 0xCA, 0xA0, 0x00, 0x71, 0xC3, 0x04,
0x32, 0x6F, 0x61, 0x65, 0xFE, 0x0D, 0xFF, 0xFD, 0xFF, 0xFD, 0x21, 0x72, 0xF4, 0x21, 0x65, 0xFD,
0x21, 0x70, 0xFD, 0xD6, 0x01, 0xF1, 0xC4, 0xC5, 0x62, 0x63, 0x64, 0x66, 0x67, 0x68, 0x69, 0x6A,
0x6B, 0x6C, 0x6D, 0x6E, 0x70, 0x72, 0x73, 0x74, 0x76, 0x77, 0x78, 0x7A, 0xFB, 0x56, 0xFB, 0x5A,
0xFB, 0x6A, 0xFB, 0x6A, 0xFB, 0x6A, 0xFB, 0x6A, 0xFB, 0x6A, 0xFB, 0x6A, 0xFF, 0xFD, 0xFB, 0x6A,
0xFB, 0x6A, 0xFB, 0x6A, 0xFB, 0x6A, 0xFB, 0x6A, 0xFB, 0x6A, 0xFB, 0x6A, 0xFB, 0x6A, 0xFB, 0x6A,
0xFB, 0x6A, 0xFB, 0x6A, 0xFB, 0x6A, 0xFB, 0x6A, 0xA0, 0x05, 0x72, 0x21, 0x63, 0xFD, 0xA0, 0x05,
0x93, 0xA0, 0x05, 0xC2, 0xA2, 0x05, 0x72, 0x6B, 0x77, 0xFA, 0xFD, 0x41, 0x6E, 0xFD, 0x0C, 0x21,
0x61, 0xFC, 0x21, 0x6C, 0xFD, 0xA4, 0x01, 0x22, 0x69, 0x6F, 0x75, 0x66, 0xE6, 0xEF, 0xE3, 0xFD,
0xA0, 0x01, 0x22, 0x21, 0x6C, 0xFD, 0x43, 0x6E, 0x73, 0x7A, 0xFF, 0xEF, 0xFF, 0xFD, 0xFE, 0xA5,
0x41, 0x82, 0xFB, 0x83, 0x21, 0xC5, 0xFC, 0x21, 0x64, 0xFD, 0xD6, 0x01, 0xF1, 0xC4, 0xC5, 0x61,
0x62, 0x63, 0x64, 0x66, 0x67, 0x68, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x70, 0x72, 0x73, 0x74, 0x76,
0x77, 0x78, 0x7A, 0xFA, 0xCF, 0xFA, 0xD3, 0xFF, 0xFD, 0xFA, 0xE3, 0xFA, 0xE3, 0xFA, 0xE3, 0xFA,
0xE3, 0xFA, 0xE3, 0xFA, 0xE3, 0xFA, 0xE3, 0xFA, 0xE3, 0xFA, 0xE3, 0xFA, 0xE3, 0xFA, 0xE3, 0xFA,
0xE3, 0xFA, 0xE3, 0xFA, 0xE3, 0xFA, 0xE3, 0xFA, 0xE3, 0xFA, 0xE3, 0xFA, 0xE3, 0xFA, 0xE3, 0xA0,
0x06, 0x01, 0xA1, 0x05, 0xE2, 0x6F, 0xFD, 0x21, 0x74, 0xFB, 0x21, 0x65, 0xFD, 0x21, 0x73, 0xFD,
0x21, 0x75, 0xFD, 0x21, 0x6B, 0xFD, 0x21, 0x6C, 0xFD, 0x41, 0x82, 0xFD, 0x03, 0xA0, 0x06, 0x93,
0x21, 0x69, 0xFD, 0x22, 0x65, 0x79, 0xFA, 0xFA, 0x21, 0x61, 0xF5, 0xA0, 0x06, 0x92, 0x21, 0x6D,
0xFD, 0x21, 0x63, 0xFA, 0x21, 0x74, 0xF7, 0x21, 0x67, 0xF4, 0xA7, 0x06, 0x42, 0x67, 0x73, 0x74,
0x64, 0x6B, 0x6C, 0x72, 0xE6, 0xE9, 0xEE, 0xF4, 0xF7, 0xFA, 0xFD, 0xA0, 0x06, 0x42, 0xA0, 0x06,
0x63, 0xA2, 0x00, 0x71, 0x6C, 0x77, 0xFD, 0xFD, 0xC5, 0x06, 0x13, 0x61, 0x65, 0x6F, 0x79, 0x75,
0xFF, 0xE2, 0xFF, 0xF3, 0xFF, 0xF9, 0xFF, 0xF3, 0xFE, 0xC3, 0x21, 0x72, 0xEE, 0x21, 0x74, 0xFD,
0x42, 0xC5, 0x6E, 0xFF, 0xA9, 0xFF, 0xFD, 0xA0, 0x06, 0xC2, 0x42, 0x74, 0x77, 0xFA, 0xC2, 0xFF,
0xFD, 0x21, 0x6F, 0xF9, 0x21, 0x6B, 0xFD, 0x21, 0x74, 0xFD, 0x21, 0xB3, 0xFD, 0xA0, 0x06, 0xE2,
0x21, 0x87, 0xFD, 0x21, 0xC4, 0xFD, 0xC3, 0x00, 0x51, 0xC3, 0x6F, 0x7A, 0xFF, 0xF4, 0xFF, 0xFD,
0xF7, 0xD4, 0xD7, 0x01, 0xF1, 0xC4, 0xC5, 0x62, 0x63, 0x64, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B,
0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x72, 0x73, 0x74, 0x76, 0x77, 0x78, 0x7A, 0xF9, 0xE7, 0xF9, 0xEB,
0xF9, 0xFB, 0xF9, 0xFB, 0xF9, 0xFB, 0xF9, 0xFB, 0xF9, 0xFB, 0xF9, 0xFB, 0xFF, 0x74, 0xF9, 0xFB,
0xF9, 0xFB, 0xF9, 0xFB, 0xF9, 0xFB, 0xF9, 0xFB, 0xFF, 0xCE, 0xF9, 0xFB, 0xFF, 0xF4, 0xF9, 0xFB,
0xF9, 0xFB, 0xF9, 0xFB, 0xF9, 0xFB, 0xF9, 0xFB, 0xF9, 0xFB, 0x41, 0x77, 0xF9, 0x39, 0x21, 0x6F,
0xFC, 0x21, 0x64, 0xFD, 0xD6, 0x01, 0xF1, 0xC4, 0xC5, 0x62, 0x63, 0x64, 0x66, 0x67, 0x68, 0x6A,
0x6B, 0x6C, 0x6D, 0x6E, 0x70, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x7A, 0xF9, 0x95, 0xF9,
0x99, 0xF9, 0xA9, 0xF9, 0xA9, 0xF9, 0xA9, 0xF9, 0xA9, 0xF9, 0xA9, 0xF9, 0xA9, 0xF9, 0xA9, 0xF9,
0xA9, 0xF9, 0xA9, 0xF9, 0xA9, 0xF9, 0xA9, 0xF9, 0xA9, 0xF9, 0xA9, 0xF9, 0xA9, 0xF9, 0xA9, 0xFF,
0xFD, 0xF9, 0xA9, 0xF9, 0xA9, 0xF9, 0xA9, 0xF9, 0xA9, 0xA0, 0x01, 0x71, 0x21, 0x70, 0xFD, 0x21,
0x6D, 0xFD, 0x21, 0x61, 0xFD, 0x21, 0x69, 0xFD, 0x21, 0x6C, 0xFD, 0x41, 0x6E, 0xF9, 0xE8, 0x21,
0xBC, 0xFC, 0x21, 0xC5, 0xFD, 0xD7, 0x01, 0xF1, 0xC4, 0xC5, 0x62, 0x63, 0x64, 0x66, 0x67, 0x68,
0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x72, 0x73, 0x74, 0x76, 0x77, 0x78, 0x7A, 0xF9,
0x34, 0xF9, 0x38, 0xF9, 0x48, 0xF9, 0x48, 0xF9, 0x48, 0xF9, 0x48, 0xF9, 0x48, 0xF9, 0x48, 0xFF,
0xF3, 0xF9, 0x48, 0xF9, 0x48, 0xF9, 0x48, 0xF9, 0x48, 0xF9, 0x48, 0xFF, 0xFD, 0xF9, 0x48, 0xF9,
0x48, 0xF9, 0x48, 0xF9, 0x48, 0xF9, 0x48, 0xF9, 0x48, 0xF9, 0x48, 0xF9, 0x48, 0xA0, 0x07, 0xE2,
0x43, 0x85, 0x99, 0x87, 0xFB, 0xAD, 0xFB, 0xAD, 0xFF, 0xFD, 0xA0, 0x07, 0x22, 0x41, 0x62, 0xF8,
0xF3, 0xA1, 0x07, 0xE2, 0x75, 0xFC, 0xA0, 0x07, 0xD1, 0x21, 0x6D, 0xFD, 0x21, 0x65, 0xFD, 0x21,
0x69, 0xFD, 0x21, 0x7A, 0xFD, 0x21, 0x64, 0xFD, 0x21, 0x6F, 0xFD, 0xA1, 0x07, 0xE2, 0x72, 0xFD,
0xA0, 0x08, 0x02, 0x24, 0xBA, 0x82, 0x9B, 0xBC, 0xD7, 0xDE, 0xF8, 0xFD, 0xC1, 0x05, 0xC2, 0x72,
0xF8, 0xC4, 0x41, 0x68, 0xF8, 0xBE, 0x21, 0x63, 0xFC, 0xA1, 0x05, 0xC2, 0x75, 0xFD, 0x41, 0x7A,
0xF8, 0xB2, 0x21, 0x63, 0xFC, 0x21, 0x99, 0xFD, 0xA0, 0x07, 0x43, 0x42, 0x63, 0x74, 0xFF, 0xF3,
0xF8, 0xA5, 0x21, 0x70, 0xF9, 0xA0, 0x07, 0xB2, 0x22, 0x85, 0x99, 0xFD, 0xFD, 0x21, 0x7A, 0xF8,
0x21, 0x63, 0xFD, 0xA3, 0x02, 0x22, 0xC4, 0x65, 0x79, 0xF5, 0xFD, 0xF2, 0xC5, 0x05, 0xC2, 0xC4,
0x77, 0x65, 0x75, 0x7A, 0xFF, 0xD9, 0xFF, 0xDC, 0xFF, 0xE6, 0xF8, 0x8D, 0xFF, 0xF7, 0x41, 0x72,
0xF8, 0x75, 0xA1, 0x05, 0xC2, 0x6F, 0xFC, 0x41, 0x6A, 0xFB, 0x16, 0x41, 0x74, 0xFD, 0x46, 0xA1,
0x01, 0x92, 0x61, 0xFC, 0x41, 0x87, 0xF7, 0xD4, 0x44, 0x82, 0x9B, 0xBA, 0xBC, 0xF7, 0xD0, 0xF7,
0xD0, 0xF7, 0xD0, 0xF7, 0xD0, 0xC2, 0x01, 0x92, 0x68, 0x7A, 0xFC, 0xC6, 0xFC, 0xC6, 0x42, 0xBA,
0xBC, 0xF6, 0x95, 0xF6, 0x95, 0xC2, 0x01, 0x92, 0xC5, 0x7A, 0xFF, 0xF9, 0xFC, 0xB6, 0xA0, 0x08,
0x42, 0xA3, 0x01, 0x92, 0x63, 0x6E, 0x74, 0xFD, 0xFD, 0xFD, 0x41, 0xBC, 0xF6, 0x79, 0xC2, 0x01,
0x92, 0xC5, 0x7A, 0xFF, 0xFC, 0xFC, 0x9D, 0xC1, 0x01, 0x92, 0x7A, 0xFC, 0x94, 0xD1, 0x01, 0x61,
0x74, 0xC4, 0xC5, 0x62, 0x63, 0x64, 0x66, 0x67, 0x68, 0x6B, 0x6C, 0x6D, 0x70, 0x72, 0x73, 0x77,
0x7A, 0xFF, 0xB2, 0xFF, 0xB7, 0xFF, 0xBB, 0xF7, 0x96, 0xFF, 0xC8, 0xFF, 0xD8, 0xF7, 0x96, 0xF7,
0x96, 0xF7, 0x96, 0xF7, 0x96, 0xF7, 0x96, 0xF7, 0x96, 0xFF, 0xE4, 0xFF, 0xF1, 0xFF, 0xFA, 0xF7,
0x96, 0xF7, 0x96, 0xA0, 0x07, 0x74, 0x21, 0x82, 0xFD, 0xA0, 0x07, 0x73, 0x21, 0x7A, 0xFD, 0x21,
0x63, 0xFD, 0x21, 0x69, 0xF7, 0x22, 0x6A, 0x77, 0xFA, 0xFD, 0x21, 0x74, 0xEF, 0x21, 0x6F, 0xFD,
0x23, 0xC5, 0x6F, 0x72, 0xE6, 0xF5, 0xFD, 0x21, 0x72, 0xE5, 0x21, 0x6E, 0xDF, 0x21, 0x65, 0xFD,
0x21, 0x69, 0xFD, 0x41, 0x7A, 0xF9, 0xA4, 0x21, 0x72, 0xFC, 0x21, 0x62, 0xFD, 0xA4, 0x01, 0x61,
0x62, 0x64, 0x6B, 0x6C, 0xE3, 0xEA, 0xF3, 0xFD, 0x41, 0x6D, 0xFE, 0xFD, 0xA1, 0x01, 0x61, 0x65,
0xFC, 0xA0, 0x08, 0x01, 0x21, 0xB3, 0xFD, 0xA0, 0x08, 0x22, 0x21, 0x73, 0xFD, 0x21, 0x79, 0xFD,
0x21, 0x7A, 0xF1, 0x21, 0x63, 0xFD, 0x21, 0x79, 0xFD, 0xA5, 0x02, 0x41, 0x69, 0xC3, 0x6D, 0x6F,
0x77, 0xE3, 0xEB, 0xF4, 0xE8, 0xFD, 0xC2, 0x05, 0xC2, 0x68, 0x7A, 0xF6, 0xBB, 0xF6, 0xBB, 0x41,
0xBA, 0xFB, 0xDC, 0xA1, 0x05, 0xC2, 0xC5, 0xFC, 0xC1, 0x05, 0xC2, 0x7A, 0xF6, 0xA9, 0xC1, 0x05,
0xC2, 0x72, 0xF6, 0xA3, 0xD9, 0x07, 0x02, 0xC4, 0xC5, 0x69, 0x6D, 0x72, 0x77, 0x61, 0x65, 0x6F,
0x79, 0x7A, 0x62, 0x63, 0x64, 0x66, 0x67, 0x68, 0x6A, 0x6B, 0x6C, 0x6E, 0x70, 0x73, 0x74, 0x75,
0xFE, 0x4C, 0xFE, 0x7F, 0xFE, 0x88, 0xFE, 0x95, 0xFE, 0xC8, 0xFE, 0xDE, 0xFE, 0xE3, 0xFF, 0x39,
0xFF, 0xA9, 0xF9, 0xF9, 0xFF, 0xD5, 0xFC, 0x2D, 0xFF, 0xE2, 0xFF, 0xEF, 0xFC, 0x2D, 0xFC, 0x2D,
0xFC, 0x2D, 0xFC, 0x2D, 0xFC, 0x2D, 0xFC, 0x2D, 0xFC, 0x2D, 0xFC, 0x2D, 0xFF, 0xF4, 0xFF, 0xFA,
0xFC, 0x2D, 0x41, 0x64, 0xF9, 0xAB, 0xA0, 0x05, 0xC1, 0x21, 0x74, 0xFD, 0x23, 0x7A, 0x6B, 0x75,
0xF6, 0xFD, 0xFA, 0x42, 0x73, 0x6F, 0xFF, 0x10, 0xFF, 0x16, 0x41, 0x66, 0xF8, 0xDD, 0xC9, 0x01,
0x61, 0x66, 0x67, 0x6B, 0x6E, 0x72, 0x73, 0x77, 0x6D, 0x75, 0xFF, 0x0B, 0xFF, 0x0B, 0xFF, 0xF5,
0xFF, 0x0B, 0xFF, 0x0B, 0xFF, 0x0B, 0xFF, 0x0B, 0xFF, 0xFC, 0xF8, 0xD9, 0x44, 0x82, 0x9B, 0xBA,
0xBC, 0xFD, 0xC1, 0xFD, 0xC1, 0xFD, 0xC1, 0xFD, 0xC1, 0xC1, 0x03, 0x32, 0x77, 0xF5, 0x0A, 0x21,
0x7A, 0xFA, 0xA1, 0x05, 0xC2, 0x65, 0xFD, 0x42, 0xBA, 0xBC, 0xFB, 0x24, 0xFB, 0x24, 0x41, 0x87,
0xF7, 0x0F, 0x44, 0x82, 0x9B, 0xBA, 0xBC, 0xF7, 0x0B, 0xF7, 0x0B, 0xF7, 0x0B, 0xF7, 0x0B, 0xC2,
0x02, 0x92, 0x68, 0x7A, 0xF4, 0xE4, 0xF4, 0xE4, 0x42, 0xBA, 0xBC, 0xFA, 0x36, 0xFA, 0x36, 0xC2,
0x02, 0x92, 0xC5, 0x7A, 0xFF, 0xF9, 0xF4, 0xD4, 0xC1, 0x02, 0x92, 0x7A, 0xF4, 0xCB, 0xC1, 0x02,
0x92, 0x6B, 0xF4, 0xC5, 0x51, 0xC4, 0xC5, 0x62, 0x63, 0x64, 0x66, 0x67, 0x68, 0x6B, 0x6C, 0x6D,
0x70, 0x72, 0x73, 0x74, 0x77, 0x7A, 0xFF, 0xCA, 0xFF, 0xCE, 0xF6, 0xDF, 0xFF, 0xDB, 0xFF, 0xEB,
0xF6, 0xDF, 0xF6, 0xDF, 0xF6, 0xDF, 0xF6, 0xDF, 0xF6, 0xDF, 0xF6, 0xDF, 0xF6, 0xDF, 0xFF, 0xF4,
0xFF, 0xF4, 0xFF, 0xFA, 0xF6, 0xDF, 0xF6, 0xDF, 0xC3, 0x05, 0xC2, 0xC5, 0x6F, 0x7A, 0xFF, 0x8F,
0xFF, 0xCC, 0xF5, 0x89, 0xA0, 0x08, 0x63, 0x21, 0x87, 0xFD, 0x24, 0x82, 0x9B, 0xBA, 0xBC, 0xFA,
0xFA, 0xFA, 0xFA, 0x41, 0x7A, 0xF4, 0x70, 0xD1, 0x08, 0x93, 0xC4, 0xC5, 0x63, 0x64, 0x66, 0x67,
0x68, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x70, 0x73, 0x74, 0x77, 0x72, 0xFF, 0x77, 0xFF, 0x7B, 0xFF,
0x88, 0xFF, 0x98, 0xF6, 0x8C, 0xF6, 0x8C, 0xF6, 0x8C, 0xF6, 0x8C, 0xF6, 0x8C, 0xF6, 0x8C, 0xF6,
0x8C, 0xF6, 0x8C, 0xF6, 0x8C, 0xFF, 0xA1, 0xF6, 0x8C, 0xF6, 0x8C, 0xFF, 0xFC, 0xC2, 0x08, 0x93,
0x68, 0x7A, 0xFA, 0x5E, 0xFA, 0x5E, 0xA0, 0x08, 0xF2, 0x43, 0xBA, 0x9B, 0xBC, 0xF4, 0x2A, 0xF6,
0x44, 0xFF, 0xFD, 0xD1, 0x08, 0x93, 0xC5, 0xC4, 0x63, 0x64, 0x66, 0x67, 0x68, 0x6A, 0x6B, 0x6C,
0x6D, 0x6E, 0x70, 0x73, 0x74, 0x77, 0x7A, 0xFF, 0xF6, 0xFF, 0x2B, 0xFF, 0x3C, 0xFF, 0x4C, 0xF6,
0x40, 0xF6, 0x40, 0xF6, 0x40, 0xF6, 0x40, 0xF6, 0x40, 0xF6, 0x40, 0xF6, 0x40, 0xF6, 0x40, 0xF6,
0x40, 0xFF, 0x55, 0xF6, 0x40, 0xF6, 0x40, 0xFA, 0x48, 0xA0, 0x08, 0x93, 0xC1, 0x08, 0x93, 0x7A,
0xFA, 0x0F, 0xD1, 0x05, 0xC2, 0xC4, 0xC5, 0x62, 0x63, 0x64, 0x66, 0x67, 0x68, 0x6B, 0x6C, 0x6D,
0x70, 0x72, 0x73, 0x74, 0x77, 0x7A, 0xFF, 0x65, 0xFF, 0x68, 0xFF, 0x75, 0xFF, 0xAB, 0xFF, 0xC1,
0xFF, 0xF7, 0xFF, 0xF7, 0xFF, 0xF7, 0xFF, 0xF7, 0xFF, 0xF7, 0xFF, 0xF7, 0xFF, 0xF7, 0xFF, 0xFA,
0xFF, 0xFA, 0xFF, 0xF7, 0xFF, 0xF7, 0xFF, 0xF7, 0xC1, 0x08, 0xC3, 0x75, 0xF6, 0x3F, 0x21, 0x7A,
0xFA, 0xC2, 0x05, 0xC2, 0x6F, 0x7A, 0xFF, 0xFD, 0xF4, 0xA0, 0xC2, 0x05, 0xC2, 0x6D, 0x7A, 0xF4,
0x97, 0xF4, 0x97, 0xC2, 0x05, 0xC2, 0x6B, 0x72, 0xF4, 0x8E, 0xF4, 0x8E, 0x41, 0x7A, 0xF9, 0xAF,
0xA1, 0x05, 0xC2, 0x63, 0xFC, 0xC1, 0x05, 0xC2, 0x77, 0xF4, 0x7C, 0xD6, 0x02, 0x01, 0xC4, 0x61,
0x65, 0xC5, 0x62, 0x63, 0x64, 0x66, 0x67, 0x68, 0x69, 0x6B, 0x6C, 0x6D, 0x6F, 0x70, 0x72, 0x73,
0x74, 0x75, 0x77, 0x7A, 0xFC, 0x25, 0xFE, 0x31, 0xFE, 0x43, 0xFE, 0x61, 0xFE, 0x77, 0xFD, 0xBB,
0xFE, 0xED, 0xFA, 0x06, 0xFA, 0x06, 0xFA, 0x06, 0xFA, 0x06, 0xFD, 0xD3, 0xFA, 0x06, 0xFA, 0x06,
0xFF, 0x97, 0xFA, 0x06, 0xFF, 0xD6, 0xFF, 0xDF, 0xFF, 0xE8, 0xFF, 0xF5, 0xFA, 0x06, 0xFF, 0xFA,
0x44, 0x82, 0x9B, 0xBA, 0xBC, 0xF6, 0x93, 0xF6, 0x93, 0xF6, 0x93, 0xF6, 0x93, 0xC2, 0x01, 0xB2,
0x63, 0x74, 0xF6, 0xCA, 0xF6, 0xCA, 0xC1, 0x01, 0xB2, 0x72, 0xF4, 0xDA, 0xA0, 0x02, 0x71, 0xC2,
0x09, 0x12, 0xC5, 0x64, 0xFC, 0x7F, 0xFF, 0xFD, 0xC2, 0x01, 0x92, 0x70, 0x6B, 0xFC, 0x86, 0xF9,
0x33, 0x51, 0x64, 0xC4, 0xC5, 0x62, 0x63, 0x66, 0x67, 0x68, 0x6B, 0x6C, 0x6D, 0x70, 0x72, 0x73,
0x74, 0x77, 0x7A, 0xFF, 0xEE, 0xFC, 0x53, 0xFC, 0x57, 0xF4, 0x32, 0xFC, 0x64, 0xF4, 0x32, 0xF4,
0x32, 0xF4, 0x32, 0xF4, 0x32, 0xF4, 0x32, 0xFF, 0xF7, 0xF4, 0x32, 0xFC, 0x96, 0xFC, 0x96, 0xF4,
0x32, 0xF4, 0x32, 0xF4, 0x32, 0xC2, 0x01, 0xB2, 0x6F, 0x72, 0xFF, 0xCC, 0xF6, 0x69, 0xA0, 0x04,
0x32, 0x21, 0x7A, 0xFD, 0xC8, 0x01, 0xB2, 0x63, 0x64, 0x6B, 0x72, 0x74, 0xC5, 0x6F, 0x7A, 0xF6,
0x63, 0xF6, 0x63, 0xF6, 0x63, 0xF6, 0x63, 0xF6, 0x63, 0xF6, 0x86, 0xFF, 0xFD, 0xF4, 0x7C, 0xC2,
0x01, 0xB2, 0x6D, 0x7A, 0xF4, 0x61, 0xF4, 0x61, 0xC2, 0x01, 0xB2, 0x63, 0x6B, 0xF4, 0x54, 0xF4,
0x58, 0xC2, 0x01, 0xB2, 0x6D, 0x77, 0xF9, 0x20, 0xF4, 0x4F, 0x53, 0x64, 0x6A, 0xC4, 0xC5, 0x62,
0x63, 0x66, 0x67, 0x68, 0x6B, 0x6C, 0x6D, 0x70, 0x72, 0x73, 0x74, 0x75, 0x77, 0x7A, 0xFC, 0xEA,
0xFF, 0x11, 0xF5, 0xEC, 0xFF, 0x56, 0xF6, 0x28, 0xF6, 0x0B, 0xFF, 0x63, 0xF6, 0x28, 0xF3, 0xD5,
0xFF, 0x6C, 0xF3, 0xD5, 0xF3, 0xD5, 0xFF, 0xBB, 0xFF, 0xCA, 0xFF, 0xE5, 0xFF, 0xEE, 0xF3, 0xD5,
0xF3, 0xD5, 0xFF, 0xF7, 0x41, 0x87, 0xFA, 0xF9, 0xA0, 0x09, 0x32, 0xC4, 0x05, 0xC2, 0x63, 0x6B,
0x68, 0x7A, 0xFF, 0xFD, 0xFF, 0xFD, 0xF3, 0x46, 0xF3, 0x46, 0xA0, 0x09, 0x52, 0x42, 0xBA, 0xBC,
0xFF, 0xFD, 0xF8, 0x5E, 0x41, 0x7A, 0xF7, 0x8A, 0xA1, 0x02, 0x92, 0x72, 0xFC, 0xC1, 0x02, 0x92,
0x72, 0xF2, 0x26, 0x41, 0x68, 0xF7, 0x7B, 0xA1, 0x02, 0x92, 0x63, 0xFC, 0x51, 0xC4, 0xC5, 0x62,
0x63, 0x64, 0x66, 0x67, 0x68, 0x6B, 0x6C, 0x6D, 0x70, 0x72, 0x73, 0x74, 0x77, 0x7A, 0xFD, 0x22,
0xFD, 0x26, 0xFF, 0xEC, 0xFD, 0x33, 0xFD, 0x43, 0xF4, 0x37, 0xF4, 0x37, 0xF4, 0x37, 0xFF, 0xF1,
0xF4, 0x37, 0xFD, 0x52, 0xFF, 0xFB, 0xFD, 0x4C, 0xFD, 0x4C, 0xFD, 0x52, 0xF4, 0x37, 0xF4, 0x37,
0xC3, 0x05, 0xC2, 0xC5, 0x6F, 0x7A, 0xFF, 0xAD, 0xFF, 0xCC, 0xF2, 0xE1, 0xC2, 0x05, 0xC2, 0x63,
0x6B, 0xFF, 0x8C, 0xFF, 0x8C, 0xA0, 0x09, 0x92, 0xC1, 0x03, 0x12, 0x75, 0xF4, 0x5F, 0xA0, 0x09,
0xE1, 0x44, 0xBA, 0x82, 0x9B, 0xBC, 0xFF, 0xF4, 0xFF, 0xF7, 0xF4, 0x42, 0xFF, 0xFD, 0x41, 0x68,
0xF4, 0x49, 0x21, 0x63, 0xFC, 0xA1, 0x02, 0xB2, 0x75, 0xFD, 0x41, 0x7A, 0xF4, 0x3D, 0x21, 0x63,
0xFC, 0x21, 0x99, 0xFD, 0x41, 0xBC, 0xF4, 0x33, 0x42, 0xC5, 0x70, 0xFF, 0xFC, 0xF4, 0x2F, 0x42,
0x63, 0x74, 0xFF, 0xEB, 0xF4, 0x28, 0x21, 0x70, 0xF9, 0xA3, 0x02, 0xB2, 0xC4, 0x61, 0x65, 0xE8,
0xEF, 0xFD, 0x41, 0x6A, 0xF4, 0x15, 0xA2, 0x02, 0xB2, 0x61, 0x6F, 0xFC, 0xFC, 0xA0, 0x09, 0xB3,
0x21, 0x63, 0xFD, 0x21, 0x6F, 0xFD, 0x21, 0x68, 0xFD, 0x21, 0x7A, 0xF4, 0x21, 0x72, 0xFD, 0x21,
0x74, 0xFD, 0xA2, 0x02, 0x71, 0x63, 0x73, 0xF4, 0xFD, 0x41, 0x87, 0xF3, 0xDA, 0xC2, 0x02, 0xB2,
0x68, 0x7A, 0xF6, 0xB1, 0xF6, 0xB1, 0x42, 0xBA, 0xBC, 0xF8, 0x59, 0xF8, 0x59, 0xA1, 0x02, 0xB2,
0xC5, 0xF9, 0xC2, 0x02, 0xB2, 0x6D, 0x7A, 0xF6, 0x9C, 0xF6, 0x9C, 0xD5, 0x09, 0x72, 0xC5, 0x6D,
0x72, 0x77, 0x6F, 0x75, 0x7A, 0xC4, 0x62, 0x63, 0x64, 0x66, 0x67, 0x68, 0x6A, 0x6B, 0x6C, 0x6E,
0x70, 0x73, 0x74, 0xFF, 0x76, 0xFF, 0x8A, 0xFF, 0xAE, 0xFF, 0xBB, 0xFF, 0xD7, 0xFE, 0x21, 0xF3,
0x52, 0xFF, 0xDE, 0xF3, 0x72, 0xFF, 0xE2, 0xFF, 0xF2, 0xF3, 0x72, 0xF3, 0x72, 0xF3, 0x72, 0xF3,
0x72, 0xF3, 0x72, 0xF3, 0x72, 0xF3, 0x72, 0xF3, 0x72, 0xFF, 0xF7, 0xF3, 0x72, 0xC2, 0x02, 0x92,
0x6D, 0x7A, 0xF0, 0xF6, 0xF0, 0xF6, 0x51, 0x64, 0xC4, 0xC5, 0x62, 0x63, 0x66, 0x67, 0x68, 0x6B,
0x6C, 0x6D, 0x70, 0x72, 0x73, 0x74, 0x77, 0x7A, 0xFF, 0xB5, 0xFB, 0xF8, 0xFB, 0xFC, 0xF3, 0x0D,
0xFC, 0x09, 0xF3, 0x0D, 0xF3, 0x0D, 0xF3, 0x0D, 0xF3, 0x0D, 0xF3, 0x0D, 0xF3, 0x0D, 0xF3, 0x0D,
0xFC, 0x22, 0xFF, 0xF7, 0xF3, 0x0D, 0xF3, 0x0D, 0xF3, 0x0D, 0xA0, 0x02, 0xF1, 0xA1, 0x02, 0xD2,
0x6B, 0xFD, 0x44, 0x82, 0x9B, 0xBA, 0xBC, 0xFF, 0xFB, 0xF2, 0xF7, 0xF2, 0xF7, 0xF2, 0xF7, 0xA0,
0x0A, 0x12, 0xA0, 0x0A, 0xC2, 0x21, 0x63, 0xFD, 0xA1, 0x0A, 0x32, 0x79, 0xFD, 0x21, 0xBC, 0xFB,
0x21, 0xC5, 0xFD, 0xA1, 0x0B, 0x42, 0x75, 0xFD, 0xA0, 0x0A, 0xE3, 0xA0, 0x0B, 0x42, 0x24, 0xBA,
0x82, 0xBC, 0x9B, 0xE1, 0xF5, 0xFA, 0xFD, 0xA0, 0x0A, 0x32, 0x21, 0x68, 0xFD, 0x21, 0x63, 0xFD,
0xA1, 0x0B, 0x62, 0x75, 0xFD, 0x21, 0x74, 0xF2, 0x21, 0x61, 0xFD, 0x21, 0x6D, 0xFD, 0x21, 0x6B,
0xE9, 0x21, 0x6C, 0xE6, 0xA0, 0x0A, 0x53, 0xA4, 0x0B, 0x62, 0x61, 0x75, 0x79, 0x7A, 0xF4, 0xF7,
0xFA, 0xFD, 0xA1, 0x0B, 0x62, 0x6D, 0xD5, 0xA0, 0x02, 0xD1, 0xA0, 0x0B, 0x13, 0x41, 0x6D, 0xFF,
0xA5, 0x41, 0x6A, 0xFF, 0xA1, 0x22, 0x72, 0x6F, 0xB3, 0xFC, 0xC6, 0x02, 0xD2, 0x61, 0x67, 0x69,
0x6A, 0x6C, 0x77, 0xFF, 0xF0, 0xFF, 0xAE, 0xFF, 0xF3, 0xFF, 0x98, 0xFF, 0x98, 0xFF, 0xFB, 0x41,
0x87, 0xFF, 0x9C, 0xC2, 0x0B, 0x62, 0x68, 0x7A, 0xFF, 0x67, 0xFF, 0x67, 0xA0, 0x0B, 0x81, 0x22,
0xBA, 0xBC, 0xFD, 0xFD, 0xC2, 0x0B, 0x62, 0xC5, 0x7A, 0xFF, 0xFB, 0xFF, 0x56, 0xA0, 0x0B, 0x62,
0xC1, 0x0B, 0x62, 0x7A, 0xFF, 0x7B, 0xD5, 0x09, 0xF2, 0xC5, 0x6D, 0x72, 0x75, 0x79, 0x7A, 0xC4,
0x63, 0x64, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6E, 0x70, 0x73, 0x74, 0x77, 0xFF, 0x78,
0xFF, 0x8A, 0xFF, 0xA1, 0xFF, 0xAC, 0xFF, 0xB1, 0xFF, 0xC4, 0xFF, 0xD9, 0xFF, 0xDD, 0xFF, 0xEE,
0xFF, 0xF7, 0xFF, 0xF7, 0xFF, 0xF7, 0xFF, 0xF7, 0xFF, 0xF7, 0xFF, 0xF7, 0xFF, 0xF7, 0xFF, 0xF7,
0xFF, 0xF7, 0xFF, 0xFA, 0xFF, 0xF7, 0xFF, 0xF7, 0x41, 0x87, 0xF2, 0x01, 0x41, 0x7A, 0xFF, 0x13,
0xA1, 0x06, 0xC2, 0x72, 0xFC, 0xC2, 0x06, 0xC2, 0x68, 0x7A, 0xF6, 0xBA, 0xF6, 0xBA, 0xA0, 0x0A,
0x84, 0x21, 0x73, 0xFD, 0x21, 0x6B, 0xFD, 0x41, 0x7A, 0xFE, 0xE3, 0xA1, 0x06, 0xC2, 0x72, 0xFC,
0xC2, 0x06, 0xC2, 0x6C, 0x72, 0xF6, 0x9F, 0xF6, 0x9F, 0x41, 0x68, 0xFE, 0xD1, 0xA1, 0x06, 0xC2,
0x63, 0xFC, 0x41, 0xBC, 0xFE, 0xC8, 0xC2, 0x06, 0xC2, 0xC5, 0x7A, 0xFF, 0xFC, 0xF6, 0x89, 0xC3,
0x06, 0xC2, 0x63, 0x6D, 0x7A, 0xFF, 0xEA, 0xF6, 0x80, 0xF6, 0x80, 0xC2, 0x06, 0xC2, 0x6B, 0x72,
0xF6, 0x74, 0xF6, 0x74, 0x53, 0xC5, 0x64, 0xC4, 0x62, 0x63, 0x65, 0x66, 0x67, 0x68, 0x6B, 0x6C,
0x6D, 0x6E, 0x70, 0x72, 0x73, 0x74, 0x77, 0x7A, 0xFE, 0xAE, 0xFF, 0x62, 0xFF, 0xA4, 0xFF, 0xAC,
0xFF, 0xB1, 0xFF, 0xC0, 0xF6, 0xE3, 0xFF, 0xC7, 0xF6, 0xE3, 0xFF, 0xCC, 0xF6, 0xE3, 0xF6, 0xE3,
0xF6, 0xE3, 0xFF, 0xD9, 0xFF, 0xE2, 0xFF, 0xEB, 0xFF, 0xF7, 0xF6, 0xE3, 0xF6, 0xE3, 0x21, 0x65,
0xC6, 0x21, 0x7A, 0xFD, 0xC2, 0x05, 0xC2, 0x6F, 0x72, 0xFE, 0x32, 0xFF, 0xFD, 0xC1, 0x03, 0x52,
0x72, 0xF6, 0x9A, 0x41, 0x9B, 0xF4, 0x6B, 0x41, 0x7A, 0xF6, 0x18, 0x21, 0x72, 0xFC, 0x41, 0x72,
0xF4, 0x60, 0xC6, 0x03, 0x32, 0x65, 0x75, 0xC5, 0x62, 0x6D, 0x74, 0xFF, 0xEB, 0xF1, 0x95, 0xFF,
0xF1, 0xFF, 0xF9, 0xEF, 0x01, 0xFF, 0xFC, 0x21, 0x7A, 0xEB, 0xC2, 0x05, 0xC2, 0x6F, 0x7A, 0xFF,
0xFD, 0xEF, 0xE7, 0x41, 0x65, 0xF1, 0x74, 0xA1, 0x03, 0x32, 0x69, 0xFC, 0x21, 0x62, 0xFB, 0xC2,
0x05, 0xC2, 0x75, 0x7A, 0xFF, 0xFD, 0xEF, 0xD2, 0xC2, 0x05, 0xC2, 0x63, 0x77, 0xFB, 0x44, 0xEF,
0xC9, 0xC2, 0x02, 0x92, 0x6B, 0x72, 0xEE, 0xC2, 0xEE, 0xC2, 0x51, 0xC4, 0xC5, 0x62, 0x63, 0x64,
0x66, 0x67, 0x68, 0x6B, 0x6C, 0x6D, 0x70, 0x72, 0x73, 0x74, 0x77, 0x7A, 0xF9, 0xC4, 0xF9, 0xC8,
0xFC, 0x8E, 0xF9, 0xD5, 0xF9, 0xE5, 0xF0, 0xD9, 0xF0, 0xD9, 0xF0, 0xD9, 0xF0, 0xD9, 0xF0, 0xD9,
0xF0, 0xD9, 0xF0, 0xD9, 0xF9, 0xEE, 0xF9, 0xEE, 0xFF, 0xF7, 0xF0, 0xD9, 0xF0, 0xD9, 0xA1, 0x05,
0xC2, 0x79, 0xCC, 0x41, 0x87, 0xF4, 0xA8, 0x44, 0x82, 0x9B, 0xBA, 0xBC, 0xF4, 0xA4, 0xF4, 0xA4,
0xF4, 0xA4, 0xF4, 0xA4, 0x43, 0x9B, 0xBA, 0xBC, 0xF0, 0x89, 0xF0, 0x89, 0xF0, 0x89, 0xCD, 0x00,
0x91, 0xC4, 0xC5, 0x63, 0x64, 0x66, 0x67, 0x68, 0x6A, 0x6B, 0x6D, 0x70, 0x73, 0x77, 0xF9, 0x70,
0xFF, 0xF6, 0xF9, 0x81, 0xF9, 0x91, 0xF0, 0x85, 0xF0, 0x85, 0xF0, 0x85, 0xF0, 0x85, 0xF0, 0x85,
0xF0, 0x85, 0xF0, 0x85, 0xF9, 0x9A, 0xF0, 0x85, 0xC2, 0x00, 0x91, 0x68, 0x7A, 0xF4, 0x63, 0xF4,
0x63, 0x44, 0xBA, 0x82, 0x9B, 0xBC, 0xEE, 0x32, 0xF0, 0x4C, 0xF0, 0x4C, 0xFA, 0x05, 0xC1, 0x00,
0x71, 0x72, 0xFB, 0x0E, 0xD0, 0x00, 0x91, 0xC5, 0xC4, 0x63, 0x64, 0x66, 0x67, 0x68, 0x6A, 0x6B,
0x6C, 0x6E, 0x70, 0x73, 0x74, 0x77, 0x7A, 0xFF, 0xED, 0xF9, 0x2A, 0xF9, 0x3B, 0xF9, 0x4B, 0xF0,
0x3F, 0xF0, 0x3F, 0xF0, 0x3F, 0xF0, 0x3F, 0xF0, 0x3F, 0xF0, 0x3F, 0xF0, 0x3F, 0xF0, 0x3F, 0xF9,
0x54, 0xF0, 0x3F, 0xFF, 0xFA, 0xF4, 0x47, 0xC1, 0x00, 0x91, 0x7A, 0xF4, 0x14, 0xD1, 0x02, 0x41,
0xC4, 0xC5, 0x62, 0x63, 0x64, 0x66, 0x67, 0x68, 0x6B, 0x6C, 0x6D, 0x70, 0x72, 0x73, 0x74, 0x77,
0x7A, 0xFF, 0x66, 0xFF, 0x6A, 0xFF, 0x81, 0xFF, 0xAB, 0xFF, 0xC7, 0xEE, 0xE4, 0xEE, 0xE4, 0xEE,
0xE4, 0xEE, 0xE4, 0xEE, 0xE4, 0xEE, 0xE4, 0xEE, 0xE4, 0xFF, 0xFA, 0xFF, 0xFA, 0xEE, 0xE4, 0xEE,
0xE4, 0xEE, 0xE4, 0x53, 0xC4, 0xC5, 0x62, 0x63, 0x64, 0x66, 0x67, 0x68, 0x6B, 0x6C, 0x6D, 0x70,
0x72, 0x73, 0x74, 0x75, 0x77, 0x7A, 0x6F, 0xFB, 0x61, 0xF8, 0x99, 0xF4, 0x3E, 0xFB, 0x68, 0xFB,
0xCD, 0xF4, 0x3E, 0xF4, 0x3E, 0xF4, 0x3E, 0xF4, 0x3E, 0xF4, 0x3E, 0xFB, 0xD9, 0xFE, 0x91, 0xFE,
0xC7, 0xFE, 0xDC, 0xFA, 0x20, 0xFE, 0xE5, 0xFF, 0x2B, 0xFA, 0x32, 0xFF, 0xCA, 0x21, 0x65, 0xC6,
0xD8, 0x01, 0xF1, 0xC4, 0xC5, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B,
0x6C, 0x6D, 0x6E, 0x70, 0x72, 0x73, 0x74, 0x76, 0x77, 0x78, 0x7A, 0xEF, 0x09, 0xEF, 0x0D, 0xFA,
0xEA, 0xEF, 0x1D, 0xEF, 0x1D, 0xEF, 0x1D, 0xF3, 0x40, 0xEF, 0x1D, 0xEF, 0x1D, 0xEF, 0x1D, 0xFF,
0xFD, 0xEF, 0x1D, 0xEF, 0x1D, 0xEF, 0x1D, 0xEF, 0x1D, 0xEF, 0x1D, 0xEF, 0x1D, 0xEF, 0x1D, 0xEF,
0x1D, 0xEF, 0x1D, 0xEF, 0x1D, 0xEF, 0x1D, 0xEF, 0x1D, 0xEF, 0x1D, 0xA0, 0x06, 0x11, 0x21, 0x87,
0xFD, 0x41, 0x6F, 0xEF, 0xA8, 0x21, 0x69, 0xFC, 0xA1, 0x06, 0x11, 0x6D, 0xFD, 0x23, 0x9B, 0xBA,
0xBC, 0xFB, 0xEE, 0xEE, 0x42, 0x85, 0x99, 0xF3, 0x94, 0xF3, 0x94, 0xA0, 0x05, 0x92, 0xA1, 0x05,
0x72, 0x7A, 0xFD, 0x21, 0x63, 0xFB, 0xA2, 0x03, 0xA2, 0xC4, 0x6F, 0xEE, 0xFD, 0x44, 0x82, 0x9B,
0xBA, 0xBC, 0xFF, 0xF9, 0xF0, 0x56, 0xF0, 0x56, 0xF0, 0x56, 0x41, 0x67, 0xF3, 0x6E, 0x21, 0x7A,
0xFC, 0xA1, 0x01, 0xB2, 0x75, 0xFD, 0x41, 0x87, 0xF3, 0x62, 0x41, 0x6F, 0xF3, 0x5E, 0x22, 0xC4,
0x73, 0xF8, 0xFC, 0x41, 0x84, 0xF3, 0x55, 0x42, 0xC5, 0x6E, 0xFF, 0xFC, 0xF3, 0x51, 0x41, 0xBA,
0xF3, 0x4A, 0x42, 0xC5, 0x7A, 0xFF, 0xFC, 0xF3, 0x46, 0x21, 0x64, 0xB2, 0x22, 0x85, 0x99, 0xAF,
0xFD, 0x21, 0x7A, 0xAA, 0x22, 0x63, 0x74, 0xA7, 0xA7, 0x41, 0x6E, 0xFF, 0xA2, 0xA4, 0x0B, 0x93,
0xC4, 0x65, 0x75, 0x79, 0xEF, 0xF4, 0xF7, 0xFC, 0xA4, 0x01, 0x61, 0x61, 0x6F, 0x79, 0x7A, 0xC6,
0xCF, 0xDA, 0xF5, 0xC3, 0x05, 0xC2, 0x6E, 0x68, 0x7A, 0xFA, 0x45, 0xED, 0x8E, 0xED, 0x8E, 0xC2,
0x05, 0xC2, 0xC5, 0x7A, 0xF7, 0x88, 0xED, 0x82, 0xA0, 0x0C, 0x52, 0x41, 0xBC, 0xF2, 0xA0, 0xC4,
0x05, 0xC2, 0x74, 0xC5, 0x6D, 0x7A, 0xFF, 0xF9, 0xFF, 0xFC, 0xF4, 0xBA, 0xED, 0x72, 0x41, 0x68,
0xF2, 0x8D, 0xC2, 0x05, 0xC2, 0x63, 0x7A, 0xFF, 0xFC, 0xED, 0x5F, 0xC1, 0x05, 0xC2, 0x6B, 0xED,
0x56, 0xC1, 0x05, 0xC2, 0x77, 0xFA, 0x07, 0xD1, 0x00, 0x81, 0xC4, 0xC5, 0x62, 0x63, 0x64, 0x66,
0x67, 0x68, 0x6B, 0x6C, 0x6D, 0x70, 0x72, 0x73, 0x74, 0x77, 0x7A, 0xF9, 0xFD, 0xF7, 0x35, 0xF2,
0xDA, 0xFF, 0xBC, 0xFF, 0xC8, 0xF2, 0xDA, 0xF2, 0xDA, 0xF2, 0xDA, 0xF2, 0xDA, 0xF2, 0xDA, 0xF2,
0xDA, 0xF2, 0xDA, 0xFF, 0xD8, 0xFF, 0xEB, 0xFF, 0xF4, 0xF2, 0xDA, 0xFF, 0xFA, 0xC1, 0x00, 0x81,
0x62, 0xF2, 0xA4, 0x41, 0x7A, 0xED, 0x5C, 0x21, 0x72, 0xFC, 0x21, 0x74, 0xFD, 0x21, 0x73, 0xFD,
0xC1, 0x01, 0xB2, 0x7A, 0xED, 0xC0, 0xA0, 0x0C, 0x23, 0x21, 0x6D, 0xFD, 0xD5, 0x00, 0xF2, 0xC5,
0x6C, 0x72, 0x65, 0x69, 0xC4, 0x63, 0x64, 0x66, 0x67, 0x68, 0x6A, 0x6B, 0x6D, 0x6E, 0x6F, 0x70,
0x73, 0x74, 0x75, 0x77, 0xFF, 0x01, 0xFF, 0x15, 0xFF, 0x5C, 0xFF, 0xAB, 0xFF, 0xE1, 0xEF, 0x5A,
0xEF, 0x79, 0xEF, 0x89, 0xED, 0x43, 0xED, 0x43, 0xED, 0x43, 0xED, 0x43, 0xED, 0x43, 0xED, 0x43,
0xED, 0x43, 0xFF, 0xF1, 0xED, 0x43, 0xFF, 0xF4, 0xED, 0x43, 0xFF, 0xFD, 0xED, 0x43, 0x41, 0x6F,
0xED, 0x01, 0xC2, 0x01, 0xB2, 0x75, 0x7A, 0xF2, 0x36, 0xFF, 0xFC, 0x41, 0x74, 0xF2, 0x2D, 0x21,
0x99, 0xFC, 0x41, 0x70, 0xF2, 0x26, 0x41, 0x6E, 0xF2, 0x22, 0x21, 0x69, 0xFC, 0x21, 0x62, 0xFD,
0x41, 0x69, 0xF2, 0x18, 0x41, 0xC4, 0xFE, 0xB2, 0xC2, 0x02, 0x41, 0x65, 0x77, 0xFF, 0xFC, 0xF2,
0x10, 0xA6, 0x01, 0xB2, 0xC4, 0x61, 0x6F, 0x75, 0x77, 0x7A, 0xDE, 0xE1, 0xEC, 0xDA, 0xEF, 0xF7,
0xA0, 0x0C, 0x02, 0xA1, 0x0B, 0xC2, 0x72, 0xFD, 0xA1, 0x0B, 0xC2, 0x6D, 0xF8, 0x22, 0x61, 0x65,
0xF6, 0xFB, 0xA1, 0x0C, 0x92, 0x69, 0xFB, 0xC1, 0x05, 0xC2, 0x6B, 0xF4, 0x06, 0xC1, 0x05, 0xC2,
0x63, 0xFE, 0xF1, 0xC2, 0x05, 0xC2, 0xC5, 0x7A, 0xFE, 0xD8, 0xEC, 0x4E, 0xC2, 0x05, 0xC2, 0x63,
0x6B, 0xFE, 0xE2, 0xEC, 0x45, 0xD1, 0x00, 0x81, 0xC4, 0xC5, 0x62, 0x63, 0x64, 0x66, 0x67, 0x68,
0x6B, 0x6C, 0x6D, 0x70, 0x72, 0x73, 0x74, 0x77, 0x7A, 0xF8, 0xEF, 0xF6, 0x27, 0xF1, 0xCC, 0xF5,
0x81, 0xFE, 0xBA, 0xF1, 0xCC, 0xF1, 0xCC, 0xF1, 0xCC, 0xF1, 0xCC, 0xF1, 0xCC, 0xFF, 0xE2, 0xFF,
0xE8, 0xFF, 0xEE, 0xF5, 0x93, 0xFF, 0xF7, 0xF1, 0xCC, 0xF1, 0xCC, 0xA0, 0x0C, 0x72, 0x43, 0xBA,
0x9B, 0xBC, 0xEC, 0xC2, 0xEE, 0x65, 0xFF, 0xFD, 0x41, 0x75, 0xEF, 0x55, 0xC1, 0x01, 0xB2, 0x65,
0xF8, 0x35, 0x41, 0x73, 0xEC, 0x3D, 0x42, 0x63, 0x6D, 0xFE, 0xDD, 0xFE, 0xF0, 0xD8, 0x00, 0xF2,
0x69, 0x72, 0x7A, 0x65, 0xC5, 0xC4, 0x61, 0x62, 0x63, 0x64, 0x66, 0x67, 0x68, 0x6A, 0x6B, 0x6C,
0x6D, 0x6E, 0x6F, 0x70, 0x73, 0x74, 0x75, 0x77, 0xFF, 0x35, 0xFF, 0x64, 0xFF, 0x85, 0xFF, 0xA8,
0xFF, 0xE1, 0xEE, 0x49, 0xFF, 0xEB, 0xFF, 0xEF, 0xEE, 0x68, 0xEE, 0x78, 0xEC, 0x32, 0xEC, 0x32,
0xEC, 0x32, 0xEC, 0x32, 0xEE, 0x85, 0xEC, 0x32, 0xEC, 0x32, 0xEC, 0x32, 0xFF, 0xF5, 0xEC, 0x32,
0xFE, 0xE3, 0xEC, 0x32, 0xFF, 0xF9, 0xEC, 0x32, 0x41, 0x82, 0xF1, 0x48, 0x21, 0xC5, 0xFC, 0x21,
0x68, 0xFD, 0xA1, 0x02, 0x01, 0x63, 0xFD, 0xA0, 0x0B, 0xE2, 0x21, 0x6E, 0xFD, 0x21, 0x9B, 0xFD,
0xA0, 0x0C, 0xB2, 0x21, 0x6F, 0xFD, 0x22, 0xC5, 0x65, 0xF7, 0xFD, 0x41, 0x7A, 0xEE, 0xD2, 0xC1,
0x06, 0x11, 0x72, 0xEE, 0x0F, 0xC2, 0x02, 0x01, 0x68, 0x7A, 0xFF, 0xFA, 0xFD, 0x46, 0xA0, 0x02,
0x01, 0x41, 0x6B, 0xF1, 0xE6, 0x21, 0x6F, 0xFC, 0x21, 0x6E, 0xFD, 0x21, 0x6C, 0xFD, 0x21, 0xB3,
0xFD, 0x41, 0x72, 0xEC, 0x6C, 0x21, 0x74, 0xFC, 0x21, 0x6F, 0xFD, 0x21, 0x69, 0xFD, 0xA2, 0x02,
0x01, 0xC3, 0x6E, 0xF0, 0xFD, 0x41, 0x6D, 0xF0, 0xCC, 0xC2, 0x02, 0x01, 0x61, 0x72, 0xFF, 0xFC,
0xFD, 0x12, 0x41, 0x68, 0xEB, 0xEE, 0xA1, 0x02, 0x01, 0x63, 0xFC, 0xA0, 0x0C, 0xD2, 0x21, 0xBC,
0xFD, 0x21, 0x99, 0xBD, 0x21, 0xC4, 0xFD, 0xA0, 0x07, 0x02, 0x23, 0xC5, 0x74, 0x7A, 0xF4, 0xFA,
0xFD, 0xA0, 0x05, 0xE2, 0x21, 0x74, 0xFD, 0x21, 0x65, 0xFD, 0xA1, 0x04, 0x32, 0x73, 0xFD, 0x21,
0x6D, 0xFB, 0x21, 0x65, 0xFD, 0xC2, 0x02, 0x01, 0x69, 0x7A, 0xFF, 0xFD, 0xFC, 0xD6, 0x52, 0xC4,
0xC5, 0x62, 0x64, 0x74, 0x6C, 0x61, 0x63, 0x66, 0x67, 0x68, 0x6B, 0x6D, 0x70, 0x72, 0x73, 0x77,
0x7A, 0xFC, 0xD0, 0xFC, 0xDF, 0xFD, 0xFE, 0xFF, 0x0F, 0xFF, 0x64, 0xFF, 0x78, 0xFF, 0x7D, 0xFF,
0x87, 0xFF, 0x90, 0xFF, 0xB0, 0xFF, 0x90, 0xFF, 0xBB, 0xFF, 0x90, 0xFF, 0xC8, 0xFF, 0xDC, 0xFF,
0xF7, 0xFF, 0x90, 0xFF, 0x90, 0x41, 0x77, 0xF1, 0x42, 0x21, 0x6F, 0xFC, 0x21, 0x6B, 0xFD, 0x21,
0x73, 0xFD, 0xA1, 0x00, 0x61, 0x61, 0xFD, 0x45, 0x82, 0x84, 0x9B, 0xBA, 0xBC, 0xFF, 0xFB, 0xE9,
0x23, 0xE9, 0x23, 0xE9, 0x23, 0xE9, 0x23, 0xC1, 0x00, 0x71, 0x72, 0xE9, 0x9C, 0x41, 0x72, 0xF9,
0x4A, 0x41, 0x64, 0xE9, 0x92, 0xA2, 0x00, 0x71, 0x62, 0x6D, 0xF8, 0xFC, 0x41, 0x6B, 0xE9, 0x9A,
0x21, 0x6D, 0xFC, 0x21, 0x79, 0xFD, 0x21, 0x7A, 0xFD, 0x21, 0x72, 0xFD, 0x42, 0x85, 0x99, 0xF0,
0xCF, 0xF0, 0xCF, 0x41, 0x7A, 0xF0, 0xC8, 0x21, 0x63, 0xFC, 0xC7, 0x06, 0xE2, 0x6B, 0x6D, 0x6F,
0x70, 0xC4, 0x65, 0x79, 0xFF, 0xCD, 0xEF, 0x91, 0xFF, 0xDB, 0xFF, 0xEF, 0xFF, 0xF2, 0xFF, 0xFD,
0xF0, 0xC1, 0x21, 0x82, 0xE8, 0x21, 0xC5, 0xFD, 0x21, 0xB3, 0xFD, 0xC1, 0x00, 0x51, 0x68, 0xE8,
0xBF, 0x41, 0x6B, 0xEC, 0x49, 0x21, 0x6F, 0xFC, 0x21, 0x6E, 0xFD, 0x21, 0x82, 0xFD, 0xA0, 0x0C,
0xF3, 0xA1, 0x02, 0x22, 0x6E, 0xFD, 0xC5, 0x02, 0x02, 0x65, 0x69, 0x6F, 0x75, 0x79, 0xEA, 0xDA,
0xFF, 0xFB, 0xEA, 0xDA, 0xEA, 0xDA, 0xEA, 0xDA, 0x41, 0x6B, 0xEA, 0x67, 0xA0, 0x0D, 0x22, 0x21,
0x6C, 0xFD, 0x21, 0x65, 0xFD, 0x21, 0x73, 0xFD, 0x42, 0x72, 0x73, 0xEA, 0x09, 0xEA, 0x09, 0x21,
0x65, 0xF9, 0x21, 0x65, 0xFD, 0xA0, 0x0D, 0x43, 0x21, 0x72, 0xFD, 0x21, 0x65, 0xFD, 0x21, 0x65,
0xFD, 0x21, 0x70, 0xFD, 0x21, 0x74, 0xFD, 0x21, 0x65, 0xFD, 0x26, 0xC5, 0x72, 0x63, 0x65, 0x70,
0x7A, 0xB1, 0xBC, 0xCE, 0xDB, 0xE8, 0xFD, 0x41, 0x74, 0xEA, 0x2E, 0x21, 0x65, 0xFC, 0xA1, 0x0D,
0x72, 0x73, 0xFD, 0x21, 0x87, 0xFB, 0x41, 0x9B, 0xEB, 0x13, 0x21, 0xC5, 0xFC, 0x21, 0x6F, 0xFD,
0x21, 0x69, 0xFD, 0x22, 0xC4, 0x63, 0xF0, 0xFD, 0x21, 0x99, 0xFB, 0x41, 0x77, 0xEA, 0xF8, 0x21,
0x72, 0xFC, 0x41, 0x6E, 0xEC, 0xDA, 0x23, 0xC4, 0x65, 0x6F, 0xF2, 0xF9, 0xFC, 0xC1, 0x03, 0xA2,
0x6B, 0xE9, 0xA4, 0x41, 0x63, 0xEC, 0x06, 0x45, 0x82, 0x9B, 0xBA, 0xBC, 0x84, 0xFF, 0xF6, 0xEB,
0xFC, 0xEB, 0xFC, 0xEB, 0xFC, 0xFF, 0xFC, 0xC1, 0x07, 0xE2, 0x75, 0xEA, 0x39, 0xA0, 0x0E, 0x83,
0x21, 0x64, 0xFD, 0x21, 0xB3, 0xFD, 0x21, 0xC3, 0xFD, 0xA1, 0x07, 0xE2, 0x72, 0xFD, 0x44, 0xBA,
0x82, 0x9B, 0xBC, 0xF1, 0x2C, 0xFF, 0xE9, 0xFF, 0xFB, 0xF1, 0x52, 0x41, 0x77, 0xEA, 0x15, 0xA1,
0x05, 0xC2, 0x61, 0xFC, 0xC1, 0x02, 0x22, 0x6E, 0xF1, 0x71, 0x21, 0x7A, 0xFA, 0x22, 0x63, 0x74,
0xFD, 0xEE, 0x21, 0x99, 0xFB, 0x41, 0xBC, 0xE9, 0xFB, 0x21, 0xC5, 0xFC, 0x21, 0xB3, 0xFD, 0x41,
0x69, 0xF1, 0x49, 0x42, 0xC5, 0x70, 0xFF, 0xF2, 0xE9, 0xED, 0x41, 0xB3, 0xE9, 0xE6, 0x44, 0xC3,
0x61, 0x6F, 0x79, 0xFF, 0xFC, 0xE9, 0xE2, 0xE9, 0xE2, 0xE9, 0xE2, 0xA0, 0x03, 0x32, 0x44, 0xC5,
0x62, 0x63, 0x7A, 0xFF, 0xD7, 0xFF, 0xF0, 0xF1, 0x20, 0xFF, 0xFD, 0x41, 0x67, 0xE9, 0xC5, 0x21,
0x7A, 0xFC, 0x41, 0x65, 0xE9, 0xBE, 0xC9, 0x05, 0xC2, 0xC4, 0xC3, 0x77, 0x61, 0x65, 0x6F, 0x75,
0x79, 0x7A, 0xFF, 0xBC, 0xFF, 0xC6, 0xFF, 0xC9, 0xFF, 0xCD, 0xF1, 0x1C, 0xFF, 0xE8, 0xFF, 0xF9,
0xFF, 0xF5, 0xFF, 0xFC, 0x42, 0x6A, 0x72, 0xE9, 0x9F, 0xE9, 0x9C, 0x21, 0xB3, 0xF9, 0x41, 0x6A,
0xE9, 0x92, 0x43, 0x69, 0x6A, 0x72, 0xE9, 0x8E, 0xE9, 0x8E, 0xF0, 0xDC, 0xA3, 0x05, 0xC2, 0xC3,
0x61, 0x6F, 0xEF, 0xF2, 0xF6, 0x41, 0x70, 0xF2, 0x81, 0xA1, 0x01, 0x61, 0x6C, 0xFC, 0xA0, 0x0D,
0xD5, 0xA1, 0x01, 0x92, 0x73, 0xFD, 0x41, 0x68, 0xE7, 0xBD, 0xA1, 0x01, 0x92, 0x63, 0xFC, 0xC2,
0x01, 0x92, 0x63, 0x7A, 0xFF, 0xF7, 0xED, 0xDC, 0xC1, 0x01, 0x92, 0x6B, 0xE8, 0xD0, 0xD2, 0x01,
0x61, 0x6A, 0x6B, 0xC4, 0xC5, 0x62, 0x63, 0x64, 0x66, 0x67, 0x68, 0x6C, 0x6D, 0x70, 0x72, 0x73,
0x74, 0x77, 0x7A, 0xEB, 0xFF, 0xFF, 0xE3, 0xF0, 0xF6, 0xF0, 0xFA, 0xE8, 0xD5, 0xF1, 0x07, 0xF1,
0x17, 0xE8, 0xD5, 0xE8, 0xD5, 0xE8, 0xD5, 0xE8, 0xD5, 0xE8, 0xD5, 0xFF, 0xEC, 0xF1, 0x30, 0xFF,
0xF1, 0xFF, 0xFA, 0xE8, 0xD5, 0xE8, 0xD5, 0x42, 0x75, 0x6E, 0xEB, 0xC6, 0xED, 0xFA, 0x41, 0xB3,
0xF1, 0x3E, 0x41, 0x64, 0xF1, 0x37, 0x21, 0x61, 0xFC, 0x41, 0x6A, 0xF1, 0x36, 0x41, 0x61, 0xF1,
0x2F, 0x45, 0xC3, 0x69, 0x6F, 0x72, 0x73, 0xFF, 0xED, 0xFF, 0xF5, 0xFF, 0xF8, 0xFF, 0xFC, 0xF1,
0x52, 0x41, 0x63, 0xF1, 0x18, 0x21, 0x6F, 0xFC, 0x21, 0x68, 0xFD, 0x42, 0x6D, 0x64, 0xF1, 0x0E,
0xEA, 0xDC, 0x41, 0x67, 0xF1, 0x07, 0x21, 0x99, 0xFC, 0x41, 0x73, 0xF1, 0x00, 0x22, 0xC4, 0x65,
0xF9, 0xFC, 0x43, 0x69, 0x6E, 0x72, 0xF1, 0x1B, 0xF0, 0xF7, 0xFF, 0xFB, 0x41, 0x65, 0xF0, 0xF3,
0x21, 0x69, 0xFC, 0x41, 0x77, 0xF0, 0xE6, 0x21, 0x79, 0xFC, 0x41, 0x6B, 0xF0, 0xDF, 0x21, 0x61,
0xFC, 0x21, 0x69, 0xFD, 0x21, 0x6E, 0xFD, 0x42, 0x69, 0x74, 0xFF, 0xFD, 0xF0, 0xF0, 0xCA, 0x01,
0x61, 0x62, 0x63, 0x64, 0x66, 0x67, 0x6B, 0x70, 0x72, 0x73, 0x6C, 0xFF, 0xA3, 0xFF, 0xBA, 0xFF,
0xBD, 0xF0, 0xCB, 0xF0, 0xCB, 0xFF, 0xD4, 0xFF, 0xE2, 0xFF, 0xE9, 0xFF, 0xF9, 0xF0, 0xFC, 0x41,
0x61, 0xF0, 0xAA, 0x41, 0x6C, 0xF0, 0xA6, 0x21, 0x61, 0xFC, 0xA0, 0x04, 0x02, 0x21, 0x6E, 0xFD,
0xA1, 0x07, 0x73, 0x79, 0xFD, 0x21, 0x7A, 0xFB, 0x21, 0x63, 0xFD, 0x22, 0x74, 0x7A, 0xE4, 0xFD,
0xC5, 0x01, 0x61, 0x63, 0x64, 0x70, 0x72, 0x73, 0xF0, 0x8C, 0xF0, 0x8C, 0xFF, 0xDF, 0xFF, 0xE7,
0xFF, 0xFB, 0x41, 0x72, 0xEF, 0xB3, 0x41, 0x74, 0xEF, 0xAF, 0x43, 0x6B, 0x6D, 0x73, 0xFF, 0xF8,
0xEF, 0xAB, 0xFF, 0xFC, 0x41, 0x69, 0xEF, 0xA1, 0x43, 0x85, 0x87, 0x99, 0xEF, 0x9D, 0xEF, 0x9D,
0xEF, 0x9D, 0x41, 0x82, 0xEF, 0x93, 0x41, 0x6E, 0xEF, 0x8F, 0x48, 0xC4, 0xC5, 0x63, 0x65, 0x6C,
0x6D, 0x6F, 0x73, 0xFF, 0xEE, 0xFF, 0xF8, 0xEF, 0x8B, 0xFF, 0xFC, 0xEF, 0x8B, 0xEF, 0x8B, 0xEF,
0x8B, 0xEF, 0x8B, 0x21, 0x69, 0xE7, 0x21, 0x6E, 0xFD, 0x21, 0x65, 0xFD, 0x42, 0x6C, 0x6D, 0xFF,
0xFD, 0xEF, 0x69, 0x42, 0x65, 0x6F, 0xFF, 0xF9, 0xF0, 0x65, 0xA0, 0x0E, 0x23, 0x21, 0x72, 0xFD,
0xC5, 0x03, 0xA2, 0x61, 0x62, 0x65, 0x69, 0x77, 0xFF, 0xAA, 0xFF, 0xB4, 0xEF, 0x55, 0xFF, 0xF3,
0xFF, 0xFD, 0x41, 0x77, 0xE9, 0xD5, 0x21, 0xB3, 0xFC, 0xC1, 0x05, 0xC2, 0xC5, 0xF1, 0x2E, 0xDA,
0x07, 0x02, 0xC4, 0xC5, 0x6D, 0x6E, 0x72, 0x77, 0x61, 0x65, 0x69, 0x6F, 0x75, 0x79, 0x7A, 0xC3,
0x62, 0x63, 0x64, 0x66, 0x67, 0x68, 0x6A, 0x6B, 0x6C, 0x70, 0x73, 0x74, 0xEE, 0xD1, 0xFD, 0xAF,
0xEF, 0x1A, 0xFD, 0xC0, 0xFE, 0x17, 0xFE, 0x4D, 0xFE, 0x5A, 0xFE, 0x7F, 0xFE, 0xB8, 0xFF, 0x2F,
0xFF, 0x71, 0xEA, 0x7E, 0xFF, 0xE1, 0xFF, 0xF7, 0xEC, 0xB2, 0xF0, 0x67, 0xFF, 0xFA, 0xEC, 0xB2,
0xEC, 0xB2, 0xEC, 0xB2, 0xEC, 0xB2, 0xEC, 0xB2, 0xEC, 0xB2, 0xEC, 0xB2, 0xF2, 0x8B, 0xEC, 0xB2,
0xC2, 0x02, 0x41, 0x64, 0x74, 0xE9, 0x39, 0xE9, 0x39, 0xC1, 0x02, 0x41, 0x72, 0xF2, 0x43, 0xA2,
0x01, 0xB2, 0x7A, 0x68, 0xF1, 0xFA, 0xA0, 0x0D, 0x94, 0x21, 0x73, 0xFD, 0x21, 0x6B, 0xFD, 0xC2,
0x01, 0xB2, 0x6C, 0x72, 0xE7, 0x71, 0xE7, 0x71, 0xC1, 0x01, 0xB2, 0x73, 0xE9, 0x4F, 0xC1, 0x03,
0xE2, 0x6B, 0xEA, 0xB7, 0xC2, 0x01, 0xB2, 0x70, 0x6B, 0xFF, 0xFA, 0xE7, 0x5C, 0xC3, 0x01, 0xB2,
0x63, 0x6F, 0x72, 0xE7, 0x4F, 0xED, 0x8D, 0xE9, 0x31, 0x41, 0xBC, 0xE9, 0xE4, 0x41, 0x99, 0xFE,
0xBD, 0xA0, 0x0E, 0xD2, 0xC4, 0x03, 0xE2, 0xC4, 0x77, 0x66, 0x6D, 0xFF, 0xF9, 0xFF, 0xFD, 0xEA,
0x91, 0xEA, 0x91, 0xC1, 0x04, 0x32, 0x75, 0xE9, 0xA8, 0x21, 0x7A, 0xFA, 0xC7, 0x01, 0xB2, 0xC5,
0x63, 0x66, 0x6E, 0x74, 0x6F, 0x7A, 0xFF, 0xDD, 0xE9, 0x0B, 0xE9, 0x0B, 0xE9, 0x0B, 0xFF, 0xE8,
0xFF, 0xFD, 0xE7, 0x24, 0xA0, 0x0E, 0x53, 0x41, 0x6D, 0xE8, 0xB2, 0x21, 0x6F, 0xFC, 0x4B, 0x64,
0x66, 0x67, 0x68, 0x69, 0x6B, 0x6C, 0x6D, 0x70, 0x72, 0x73, 0xE8, 0xAB, 0xE8, 0xAB, 0xE8, 0xAB,
0xE8, 0xAB, 0xFF, 0xF6, 0xE8, 0xAB, 0xE8, 0xAB, 0xE8, 0xAB, 0xE8, 0xAB, 0xFF, 0xFD, 0xE8, 0xAB,
0xC4, 0x01, 0xB2, 0x74, 0x63, 0x6D, 0x7A, 0xFF, 0xDE, 0xE6, 0xDC, 0xE6, 0xE0, 0xE6, 0xE0, 0xA0,
0x0E, 0xF2, 0x41, 0x75, 0xE6, 0x41, 0xC3, 0x01, 0xB2, 0x6D, 0x61, 0x77, 0xFF, 0xF9, 0xFF, 0xFC,
0xE6, 0xCA, 0xA0, 0x0E, 0xB2, 0x42, 0xBA, 0x9B, 0xFF, 0xFD, 0xE7, 0x18, 0xC2, 0x02, 0x92, 0x68,
0x7A, 0xE7, 0x11, 0xE7, 0x11, 0x41, 0xBA, 0xF3, 0x20, 0x21, 0xC5, 0xFC, 0xCF, 0x09, 0x12, 0xC5,
0x6F, 0xC4, 0x63, 0x64, 0x66, 0x67, 0x68, 0x6A, 0x6B, 0x6C, 0x70, 0x73, 0x74, 0x7A, 0xFF, 0xE9,
0xED, 0x2D, 0xEF, 0xF2, 0xFF, 0xF0, 0xFF, 0xFD, 0xE7, 0x07, 0xE7, 0x07, 0xE7, 0x07, 0xE7, 0x07,
0xE7, 0x07, 0xE7, 0x07, 0xE7, 0x07, 0xE7, 0x07, 0xE7, 0x07, 0xEB, 0x0F, 0xC1, 0x01, 0x92, 0x72,
0xF0, 0x47, 0xC2, 0x01, 0x92, 0x7A, 0x68, 0xE5, 0xD6, 0xEA, 0xD9, 0xC1, 0x01, 0x92, 0x74, 0xEE,
0x23, 0xC2, 0x01, 0x92, 0x6D, 0x7A, 0xEA, 0xCA, 0xEA, 0xCA, 0xC2, 0x01, 0x92, 0x6D, 0x77, 0xE6,
0xB9, 0xEA, 0xC1, 0x51, 0x64, 0xC4, 0xC5, 0x62, 0x63, 0x66, 0x67, 0x68, 0x6B, 0x6C, 0x6D, 0x70,
0x72, 0x73, 0x74, 0x77, 0x7A, 0xFF, 0xA9, 0xED, 0xE1, 0xED, 0xE5, 0xFF, 0xD9, 0xFF, 0xDF, 0xFF,
0xE8, 0xE5, 0xC0, 0xE5, 0xC0, 0xE5, 0xC0, 0xE5, 0xC0, 0xE5, 0xC0, 0xE5, 0xC0, 0xEE, 0x24, 0xFF,
0xEE, 0xE5, 0xC0, 0xE5, 0xC0, 0xFF, 0xF7, 0x42, 0x6B, 0x77, 0xE6, 0x7C, 0xE6, 0x7C, 0x21, 0x65,
0xF9, 0x22, 0x61, 0x69, 0xC2, 0xFD, 0x53, 0xC5, 0x64, 0xC4, 0x62, 0x63, 0x65, 0x66, 0x67, 0x68,
0x6B, 0x6C, 0x6D, 0x70, 0x72, 0x73, 0x74, 0x77, 0x7A, 0x6E, 0xFB, 0xB1, 0xFE, 0x29, 0xE7, 0xB0,
0xE7, 0xCA, 0xFE, 0x89, 0xFE, 0x96, 0xE5, 0x99, 0xE7, 0xEC, 0xE5, 0x99, 0xFE, 0x99, 0xFE, 0xA2,
0xFE, 0xAE, 0xFE, 0xB7, 0xFE, 0xE6, 0xFF, 0x2A, 0xE8, 0x30, 0xE5, 0x99, 0xFF, 0x40, 0xFF, 0xFB,
0x41, 0x75, 0xEA, 0xA1, 0x42, 0x6E, 0x7A, 0xFF, 0xFC, 0xEA, 0x9D, 0x41, 0x75, 0xE6, 0x42, 0x21,
0x6E, 0xFC, 0x21, 0x77, 0xFD, 0x21, 0x61, 0xFD, 0x21, 0x72, 0xFD, 0x43, 0x73, 0x77, 0x70, 0xEA,
0x86, 0xFF, 0xE9, 0xFF, 0xFD, 0x41, 0x6F, 0xFB, 0xC6, 0x21, 0x70, 0xFC, 0x21, 0x73, 0xFD, 0x21,
0x79, 0xFD, 0x21, 0x64, 0xFD, 0xC1, 0x01, 0x72, 0x6B, 0xE3, 0xDE, 0x44, 0x82, 0x9B, 0xBA, 0xBC,
0xFF, 0xFA, 0xE4, 0xFD, 0xE4, 0xFD, 0xE4, 0xFD, 0x43, 0x85, 0x99, 0x87, 0xEC, 0x11, 0xEC, 0x11,
0xE5, 0xE5, 0xA0, 0x0F, 0xB2, 0x21, 0x63, 0xFD, 0xA1, 0x04, 0x52, 0x79, 0xFD, 0x21, 0xBC, 0xFB,
0x21, 0xC5, 0xFD, 0xA1, 0x02, 0x72, 0x75, 0xFD, 0xA0, 0x0F, 0xD3, 0x44, 0xBA, 0x82, 0xBC, 0x9B,
0xFE, 0xA7, 0xFF, 0xF8, 0xFF, 0xFD, 0xE5, 0xC2, 0x41, 0x68, 0xE7, 0xD3, 0x21, 0x63, 0xFC, 0xA1,
0x02, 0x92, 0x75, 0xFD, 0xA0, 0x0F, 0x13, 0x21, 0x72, 0xFD, 0xA0, 0x10, 0x03, 0x21, 0x74, 0xFD,
0xA1, 0x0F, 0x13, 0x61, 0xFD, 0x21, 0x74, 0xFB, 0x21, 0x6F, 0xEC, 0xA3, 0x02, 0x92, 0x62, 0x73,
0x7A, 0xEC, 0xFA, 0xFD, 0x41, 0x74, 0xE7, 0xA7, 0x21, 0x61, 0xFC, 0x21, 0x6D, 0xFD, 0x41, 0x6B,
0xE7, 0x9D, 0x41, 0x6C, 0xE7, 0x99, 0xA0, 0x0F, 0x43, 0xA4, 0x02, 0x92, 0x61, 0x75, 0x79, 0x7A,
0xF2, 0xF5, 0xF9, 0xFD, 0xC1, 0x02, 0x92, 0x6D, 0xE7, 0x87, 0xA0, 0x10, 0x32, 0x21, 0x75, 0xFD,
0x21, 0x6B, 0xFD, 0xA1, 0x01, 0x71, 0x73, 0xFD, 0x41, 0x6D, 0xFF, 0x7A, 0x41, 0x6A, 0xFF, 0x76,
0x42, 0x72, 0x6F, 0xFF, 0x88, 0xFF, 0xFC, 0xC6, 0x01, 0x72, 0x61, 0x67, 0x69, 0x6A, 0x6C, 0x77,
0xFF, 0x6B, 0xFF, 0x81, 0xFF, 0xF1, 0xFF, 0x6B, 0xFF, 0x6B, 0xFF, 0xF9, 0x41, 0x6E, 0xE5, 0x37,
0x21, 0x6F, 0xFC, 0x41, 0x63, 0xE5, 0x30, 0x41, 0x70, 0xEF, 0xF5, 0x23, 0x67, 0x6B, 0x6C, 0xF5,
0xF8, 0xFC, 0x41, 0x7A, 0xE5, 0x21, 0x41, 0x72, 0xE5, 0x1D, 0x21, 0x65, 0xFC, 0x22, 0x67, 0x6D,
0xF5, 0xFD, 0xA0, 0x10, 0x73, 0x21, 0x77, 0xFD, 0x21, 0x99, 0xFD, 0x21, 0xC4, 0xFD, 0xC2, 0x02,
0x92, 0x69, 0x7A, 0xFF, 0xFD, 0xE4, 0xFF, 0xD9, 0x09, 0x12, 0xC4, 0xC5, 0x6D, 0x6F, 0x72, 0x75,
0x79, 0x7A, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6E, 0x70,
0x73, 0x74, 0x77, 0xFF, 0x11, 0xFF, 0x34, 0xFF, 0x48, 0xFF, 0x64, 0xFF, 0x82, 0xFF, 0x8D, 0xFF,
0x9C, 0xFF, 0xB0, 0xFF, 0xD4, 0xE4, 0xFC, 0xED, 0xF8, 0xEE, 0x08, 0xFF, 0xE6, 0xE4, 0xFC, 0xF0,
0xB1, 0xE4, 0xFC, 0xE4, 0xFC, 0xE4, 0xFC, 0xE4, 0xFC, 0xE4, 0xFC, 0xE4, 0xFC, 0xE4, 0xFC, 0xFF,
0xF7, 0xE4, 0xFC, 0xE4, 0xFC, 0xC1, 0x00, 0x71, 0x7A, 0xE2, 0x8E, 0xA1, 0x01, 0x92, 0x72, 0xFA,
0xC1, 0x00, 0x71, 0x72, 0xF0, 0x54, 0xA0, 0x10, 0x52, 0x21, 0x65, 0xFD, 0xC3, 0x10, 0x32, 0x69,
0x61, 0x77, 0xFF, 0xFD, 0xE7, 0xD2, 0xE7, 0xD2, 0x21, 0x77, 0xF4, 0xC3, 0x01, 0x92, 0x68, 0x69,
0x7A, 0xFF, 0xE5, 0xFF, 0xFD, 0xE8, 0x90, 0xA0, 0x0F, 0x74, 0x21, 0x73, 0xFD, 0x21, 0x6B, 0xFD,
0xC2, 0x01, 0x92, 0x6C, 0x72, 0xE8, 0x7B, 0xE8, 0x7B, 0xC1, 0x01, 0x92, 0x6B, 0xE8, 0x72, 0xC3,
0x01, 0x92, 0x63, 0x6D, 0x7A, 0xFA, 0x87, 0xE8, 0x6C, 0xE8, 0x6C, 0xC1, 0x00, 0x71, 0x61, 0xE3,
0x90, 0xC2, 0x01, 0x92, 0x6B, 0x72, 0xE8, 0x5A, 0xFF, 0xFA, 0x54, 0xC5, 0x64, 0xC4, 0x62, 0x63,
0x65, 0x66, 0x67, 0x68, 0x6B, 0x6C, 0x6D, 0x6E, 0x70, 0x72, 0x73, 0x74, 0x75, 0x77, 0x7A, 0xFE,
0x51, 0xFF, 0x4D, 0xEB, 0x7A, 0xFF, 0xA1, 0xFF, 0xC1, 0xFF, 0xD3, 0xE3, 0x59, 0xFD, 0x72, 0xE3,
0x59, 0xFF, 0xD6, 0xE3, 0x59, 0xFF, 0xDF, 0xE3, 0x59, 0xFA, 0x70, 0xEB, 0xB4, 0xFF, 0xE5, 0xFF,
0xF7, 0xE3, 0x59, 0xE3, 0x59, 0xE3, 0x59, 0xC1, 0x01, 0x92, 0x72, 0xE8, 0x14, 0x52, 0xC4, 0xC5,
0x62, 0x63, 0x64, 0x66, 0x67, 0x68, 0x6B, 0x6C, 0x6D, 0x70, 0x72, 0x73, 0x74, 0x77, 0x7A, 0x6F,
0xEB, 0x37, 0xEB, 0x3B, 0xFF, 0xFA, 0xEB, 0x48, 0xEB, 0x58, 0xE3, 0x16, 0xFD, 0x2F, 0xE3, 0x16,
0xE3, 0x16, 0xE3, 0x16, 0xFF, 0x9C, 0xFA, 0x2D, 0xEB, 0x71, 0xFA, 0x32, 0xFF, 0x9C, 0xE3, 0x16,
0xE3, 0x16, 0xEE, 0x5F, 0xC2, 0x00, 0x61, 0x65, 0x79, 0xFF, 0x86, 0xFF, 0xC9, 0xC3, 0x00, 0x51,
0x61, 0x65, 0x7A, 0xFD, 0xAE, 0xFD, 0xC5, 0xFF, 0xF7, 0xD9, 0x01, 0xF1, 0xC4, 0xC5, 0xC3, 0x62,
0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x72, 0x73,
0x74, 0x76, 0x77, 0x78, 0x7A, 0xE3, 0x30, 0xF7, 0xEE, 0xF8, 0x4F, 0xE3, 0x44, 0xF8, 0x52, 0xE3,
0x44, 0xF8, 0xB1, 0xE3, 0x44, 0xE3, 0x44, 0xE3, 0x44, 0xF8, 0xED, 0xE3, 0x44, 0xE3, 0x44, 0xE3,
0x44, 0xE3, 0x44, 0xE3, 0x44, 0xFD, 0x4D, 0xE3, 0x44, 0xFF, 0xF4, 0xE3, 0x44, 0xE3, 0x44, 0xE3,
0x44, 0xE3, 0x44, 0xE3, 0x44, 0xE3, 0x44, 0x41, 0x73, 0xFE, 0x03, 0x21, 0x6E, 0xFC, 0x21, 0x61,
0xFD, 0x21, 0x72, 0xFD, 0x21, 0x74, 0xFD, 0x41, 0x69, 0xF8, 0x07, 0x41, 0x65, 0xF8, 0x03, 0x21,
0x69, 0xFC, 0xA2, 0x02, 0x22, 0x67, 0x6E, 0xF5, 0xFD, 0xA0, 0x10, 0xA4, 0x21, 0x87, 0xFD, 0x24,
0x82, 0x9B, 0xBA, 0xBC, 0xFA, 0xFA, 0xFA, 0xFA, 0xA0, 0x10, 0xE4, 0xC2, 0x10, 0xE4, 0x68, 0x7A,
0xE7, 0x40, 0xE7, 0x40, 0xC2, 0x10, 0xE4, 0xC5, 0x7A, 0xEA, 0x7A, 0xE7, 0x37, 0xA0, 0x11, 0x24,
0xA1, 0x10, 0xE4, 0x7A, 0xFD, 0xC1, 0x10, 0xE4, 0x6F, 0xF7, 0xC9, 0xC1, 0x10, 0xE4, 0x63, 0xF9,
0x3B, 0xC2, 0x10, 0xE4, 0xC5, 0x7A, 0xEA, 0x79, 0xE7, 0x1A, 0xC2, 0x10, 0xE4, 0x63, 0x7A, 0xF9,
0x2C, 0xE7, 0x11, 0x21, 0x74, 0xDA, 0xD3, 0x02, 0x22, 0xC4, 0xC5, 0x62, 0x63, 0x64, 0x66, 0x67,
0x68, 0x6B, 0x6C, 0x6D, 0x70, 0x72, 0x73, 0x74, 0x77, 0x7A, 0x6A, 0x6E, 0xFF, 0xB6, 0xFF, 0xB9,
0xFF, 0xC2, 0xFF, 0xC5, 0xFF, 0xCE, 0xFF, 0xC2, 0xFF, 0xDA, 0xFF, 0xC2, 0xFF, 0xC2, 0xFF, 0xC2,
0xFF, 0xDF, 0xFF, 0xE5, 0xFF, 0xEB, 0xFF, 0xF4, 0xFF, 0xC2, 0xFF, 0xC2, 0xFF, 0xC2, 0xE2, 0x8A,
0xFF, 0xFD, 0x41, 0x9B, 0xE1, 0x9F, 0x41, 0x72, 0xED, 0x16, 0xC1, 0x02, 0x41, 0x72, 0xE1, 0x97,
0xCC, 0x02, 0x02, 0x61, 0x65, 0x75, 0xC5, 0x62, 0x64, 0x69, 0x6D, 0x6F, 0x70, 0x74, 0x77, 0xFF,
0x62, 0xFF, 0xB6, 0xE2, 0x40, 0xFF, 0xF2, 0xFF, 0xF6, 0xE2, 0x50, 0xE2, 0x50, 0xE2, 0x50, 0xE2,
0x50, 0xE4, 0xBC, 0xFF, 0xFA, 0xE2, 0x50, 0x21, 0x7A, 0xD9, 0xD7, 0x01, 0xF1, 0xC4, 0xC5, 0x62,
0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x72, 0x73, 0x74,
0x76, 0x77, 0x78, 0x7A, 0xE1, 0xFF, 0xE2, 0x03, 0xE2, 0x13, 0xE2, 0x13, 0xE2, 0x13, 0xFF, 0x2A,
0xE2, 0x13, 0xE2, 0x13, 0xE2, 0x13, 0xE2, 0x13, 0xE2, 0x13, 0xE2, 0x13, 0xE2, 0x13, 0xE2, 0x13,
0xFF, 0xFD, 0xE2, 0x13, 0xE2, 0x13, 0xE2, 0x13, 0xE2, 0x13, 0xE2, 0x13, 0xE2, 0x13, 0xE2, 0x13,
0xE2, 0x13, 0x41, 0x7A, 0xFC, 0xD8, 0x21, 0x6F, 0xFC, 0x45, 0x63, 0x6B, 0x70, 0x77, 0x72, 0xE1,
0x69, 0xE1, 0x4A, 0xE1, 0x4A, 0xE1, 0x4A, 0xFF, 0xFD, 0x21, 0x6F, 0xF0, 0x21, 0x6D, 0xFD, 0xC1,
0x03, 0x32, 0x73, 0xF7, 0x2C, 0x21, 0x6D, 0xFA, 0x42, 0x65, 0x6D, 0xFF, 0xFD, 0xF7, 0x38, 0x21,
0x64, 0xF9, 0x21, 0x65, 0xFD, 0xA0, 0x0E, 0x92, 0x21, 0x65, 0xFD, 0x21, 0xBC, 0xFD, 0x21, 0xC5,
0xFD, 0x21, 0x64, 0xFD, 0x21, 0x85, 0xFD, 0xC3, 0x00, 0x51, 0xC4, 0x6C, 0x72, 0xFF, 0xFD, 0xDF,
0x53, 0xDF, 0x53, 0x41, 0x62, 0xE7, 0xCB, 0xA0, 0x0D, 0x72, 0x21, 0x82, 0xFD, 0x21, 0xC5, 0xFD,
0x21, 0xB3, 0xFD, 0xC3, 0x06, 0x12, 0x7A, 0xC5, 0x64, 0xE8, 0x6A, 0xEA, 0xC4, 0xE8, 0x06, 0xC3,
0x05, 0xC2, 0x6E, 0x74, 0x7A, 0xED, 0x69, 0xED, 0x69, 0xE0, 0xB2, 0x51, 0x64, 0xC4, 0xC5, 0x62,
0x63, 0x66, 0x67, 0x68, 0x6B, 0x6C, 0x6D, 0x70, 0x72, 0x73, 0x74, 0x77, 0x7A, 0xFF, 0xE8, 0xED,
0x59, 0xEA, 0x91, 0xE6, 0x36, 0xE9, 0xEB, 0xE6, 0x36, 0xE6, 0x36, 0xE6, 0x36, 0xE6, 0x36, 0xE6,
0x36, 0xE6, 0x36, 0xE6, 0x36, 0xFF, 0xF4, 0xE9, 0xFD, 0xE6, 0x36, 0xE6, 0x36, 0xE6, 0x36, 0xA2,
0x00, 0x51, 0xC3, 0x6F, 0xB1, 0xCC, 0xC5, 0x03, 0x32, 0x61, 0x65, 0x69, 0x6F, 0x75, 0xDF, 0x6D,
0xDF, 0x6D, 0xDF, 0x6D, 0xDF, 0x6D, 0xDF, 0x6D, 0x21, 0x6F, 0xEE, 0x21, 0x65, 0xFD, 0x21, 0x72,
0xFD, 0xA1, 0x00, 0x51, 0x65, 0xFD, 0x41, 0x74, 0xE0, 0xFD, 0xC2, 0x02, 0x02, 0x69, 0x6F, 0xF7,
0x38, 0xFF, 0xFC, 0xA0, 0x11, 0x63, 0xA1, 0x04, 0x52, 0x72, 0xFD, 0x21, 0x74, 0xFB, 0x41, 0x6F,
0xE3, 0x6D, 0xA0, 0x11, 0x92, 0x21, 0x62, 0xFD, 0xA0, 0x11, 0xB2, 0x21, 0x74, 0xFD, 0x21, 0x75,
0xFD, 0x21, 0x7A, 0xFD, 0x21, 0x72, 0xFD, 0x21, 0x64, 0xFD, 0xC5, 0x04, 0x32, 0x61, 0x69, 0x7A,
0x65, 0x6F, 0xFF, 0xE1, 0xFF, 0xE4, 0xFF, 0xEB, 0xE5, 0x41, 0xFF, 0xFD, 0x21, 0x72, 0xEE, 0x21,
0x65, 0xFD, 0x22, 0x62, 0x70, 0xB8, 0xFD, 0xA0, 0x11, 0xD2, 0x21, 0x74, 0xFD, 0x21, 0x65, 0xFD,
0xA1, 0x11, 0xF3, 0x73, 0xFD, 0x21, 0x87, 0xFB, 0x41, 0x9B, 0xEE, 0x63, 0x21, 0xC5, 0xFC, 0x21,
0x6F, 0xFD, 0x21, 0x69, 0xFD, 0x22, 0xC4, 0x63, 0xF0, 0xFD, 0x21, 0x9B, 0xFB, 0x42, 0xC5, 0x73,
0xFF, 0xFD, 0xF4, 0x53, 0xA1, 0x00, 0x51, 0x65, 0xF9, 0xD9, 0x01, 0xF1, 0xC4, 0xC5, 0x61, 0x62,
0x63, 0x64, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x72, 0x73, 0x74,
0x75, 0x76, 0x77, 0x78, 0x7A, 0xE0, 0x60, 0xE0, 0x64, 0xFE, 0xC3, 0xE0, 0x74, 0xF5, 0x82, 0xE0,
0x74, 0xE0, 0x74, 0xE0, 0x74, 0xE0, 0x74, 0xFE, 0xD9, 0xE0, 0x74, 0xFE, 0xEE, 0xE0, 0x74, 0xE0,
0x74, 0xE0, 0x74, 0xFE, 0xFA, 0xFF, 0x56, 0xE0, 0x74, 0xE0, 0x74, 0xFF, 0x78, 0xFF, 0xC9, 0xE0,
0x74, 0xE0, 0x74, 0xE0, 0x74, 0xFF, 0xFB, 0xA0, 0x12, 0x22, 0x21, 0x6E, 0xFD, 0x21, 0x61, 0xFD,
0x21, 0x7A, 0xFD, 0x42, 0x6F, 0x72, 0xE4, 0x3A, 0xFF, 0xFD, 0x41, 0x61, 0xE9, 0x1C, 0x41, 0x7A,
0xE2, 0x9D, 0x21, 0x63, 0xFC, 0xC2, 0x04, 0x32, 0xC4, 0x65, 0xE2, 0x99, 0xFF, 0xFD, 0x21, 0x6A,
0xF7, 0x21, 0xB3, 0xFD, 0xC1, 0x04, 0x52, 0x75, 0xFA, 0x8E, 0x41, 0x65, 0xE2, 0x81, 0xC6, 0x04,
0x32, 0x65, 0x69, 0x79, 0x7A, 0x61, 0x6F, 0xFF, 0xF6, 0xFF, 0xFC, 0xE2, 0x7D, 0xE6, 0x8B, 0xE4,
0x6D, 0xE4, 0x6D, 0x21, 0x73, 0xEB, 0x21, 0x6E, 0xFD, 0x41, 0x68, 0xF5, 0x55, 0x21, 0x63, 0xFC,
0xA1, 0x00, 0x61, 0x65, 0xFD, 0xA3, 0x00, 0x51, 0xC3, 0x61, 0x7A, 0xCC, 0xF1, 0xFB, 0xA0, 0x12,
0x42, 0x21, 0x82, 0xFD, 0xA1, 0x02, 0xB1, 0xC5, 0xFD, 0xC3, 0x10, 0x32, 0x61, 0x65, 0x7A, 0xFB,
0x8D, 0xFB, 0x8D, 0xFF, 0xFB, 0x21, 0x63, 0xF4, 0x21, 0x85, 0xFD, 0x21, 0xC4, 0xFD, 0x21, 0x69,
0xFD, 0x21, 0x73, 0xFD, 0xD9, 0x01, 0xF1, 0xC4, 0xC5, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
0x68, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x72, 0x73, 0x74, 0x76, 0x77, 0x78, 0x79, 0x7A,
0xDF, 0x85, 0xDF, 0x89, 0xFF, 0x7F, 0xDF, 0x99, 0xF4, 0xA7, 0xDF, 0x99, 0xE3, 0xBC, 0xDF, 0x99,
0xDF, 0x99, 0xDF, 0x99, 0xDF, 0x99, 0xDF, 0x99, 0xDF, 0x99, 0xDF, 0x99, 0xDF, 0x99, 0xFF, 0x86,
0xDF, 0x99, 0xFF, 0xD1, 0xDF, 0x99, 0xDF, 0x99, 0xDF, 0x99, 0xDF, 0x99, 0xDF, 0x99, 0xFF, 0xFD,
0xDF, 0x99, 0xA0, 0x04, 0xC2, 0x44, 0x9B, 0x82, 0xBA, 0xBC, 0xF0, 0x76, 0xFF, 0xFD, 0xFF, 0xFD,
0xFF, 0xFD, 0xC1, 0x0C, 0xB2, 0x77, 0xDE, 0x8F, 0x21, 0x7A, 0xFA, 0xC2, 0x0B, 0xE2, 0x65, 0x72,
0xFF, 0xFD, 0xF0, 0x60, 0xC2, 0x0B, 0xE2, 0x68, 0x7A, 0xF0, 0x57, 0xF0, 0x57, 0xA0, 0x06, 0x72,
0x42, 0xBA, 0xBC, 0xDF, 0x30, 0xDF, 0x30, 0xC3, 0x0B, 0xE2, 0x6B, 0xC5, 0x7A, 0xFF, 0xF6, 0xFF,
0xF9, 0xF0, 0x44, 0xA1, 0x0B, 0xE2, 0x6E, 0xEA, 0x21, 0x6A, 0xE5, 0x21, 0x65, 0xFD, 0xC2, 0x0B,
0xE2, 0x6C, 0x72, 0xFF, 0xFD, 0xF0, 0x2D, 0xA2, 0x0B, 0xE2, 0x73, 0x74, 0xD6, 0xD6, 0x21, 0x72,
0xCF, 0xC2, 0x0B, 0xE2, 0x62, 0x6B, 0xFF, 0xFD, 0xF0, 0x1A, 0xA2, 0x0B, 0xE2, 0x63, 0x64, 0xC3,
0xC3, 0xA0, 0x12, 0x63, 0x21, 0x73, 0xFD, 0xC3, 0x06, 0x12, 0x61, 0xC5, 0x64, 0xE7, 0xDF, 0xE8,
0x30, 0xE5, 0x72, 0x51, 0x64, 0xC4, 0xC5, 0x62, 0x63, 0x66, 0x67, 0x68, 0x6B, 0x6C, 0x6D, 0x70,
0x72, 0x73, 0x74, 0x77, 0x7A, 0xFF, 0xF4, 0xEA, 0xD1, 0xE8, 0x09, 0xE3, 0xAE, 0xE7, 0x63, 0xE3,
0xAE, 0xE3, 0xAE, 0xE3, 0xAE, 0xE3, 0xAE, 0xE3, 0xAE, 0xE3, 0xAE, 0xE3, 0xAE, 0xE7, 0x75, 0xE7,
0x75, 0xE3, 0xAE, 0xE3, 0xAE, 0xE3, 0xAE, 0xC3, 0x0B, 0xE2, 0x70, 0x63, 0x6F, 0xFF, 0xBD, 0xF2,
0xBB, 0xFF, 0xCC, 0x41, 0xBC, 0xDE, 0x9D, 0x41, 0x7A, 0xF2, 0x59, 0xC4, 0x0B, 0xE2, 0x73, 0xC5,
0x6F, 0x7A, 0xFF, 0x62, 0xFF, 0xF8, 0xFF, 0xFC, 0xEF, 0xB0, 0xA0, 0x12, 0x92, 0xA0, 0x05, 0x91,
0x23, 0x6E, 0x63, 0x6B, 0xFA, 0xFD, 0xFD, 0xC3, 0x0B, 0xE2, 0x74, 0x63, 0x7A, 0xFF, 0xF9, 0xF2,
0x8B, 0xEF, 0x94, 0xC2, 0x0B, 0xE2, 0x6B, 0x72, 0xEF, 0x88, 0xEF, 0x88, 0x41, 0x65, 0xFF, 0x31,
0x41, 0x77, 0xE3, 0x31, 0xA2, 0x0B, 0xE2, 0x62, 0x65, 0xF8, 0xFC, 0x52, 0xC4, 0xC5, 0x62, 0x63,
0x64, 0x66, 0x67, 0x68, 0x6B, 0x6C, 0x6D, 0x6E, 0x70, 0x72, 0x73, 0x74, 0x77, 0x7A, 0xEF, 0x73,
0xFE, 0xFA, 0xFF, 0x10, 0xFF, 0x19, 0xFF, 0x2C, 0xFF, 0x38, 0xF2, 0x0C, 0xF2, 0x0C, 0xFF, 0x43,
0xFF, 0x4C, 0xFF, 0x56, 0xFF, 0x5F, 0xFF, 0xAC, 0xFF, 0xC0, 0xFF, 0xDC, 0xFF, 0xE8, 0xF2, 0x0C,
0xFF, 0xF9, 0xC1, 0x03, 0xA2, 0x6E, 0xDF, 0xC7, 0x43, 0x82, 0x9B, 0xBC, 0xFF, 0xFA, 0xDF, 0xBB,
0xDF, 0xBB, 0xC1, 0x01, 0xB2, 0x77, 0xDF, 0xF5, 0xC1, 0x01, 0xB2, 0x6B, 0xDE, 0x08, 0xC2, 0x01,
0xB2, 0x64, 0x74, 0xDF, 0xE9, 0xDF, 0xE9, 0xC1, 0x01, 0xB2, 0x63, 0xDD, 0xF5, 0xC6, 0x01, 0xB2,
0x62, 0x64, 0x6E, 0x73, 0x74, 0x7A, 0xDF, 0xDA, 0xDF, 0xDA, 0xDF, 0xDA, 0xDF, 0xDA, 0xDF, 0xDA,
0xDD, 0xF3, 0xA0, 0x12, 0xB3, 0x21, 0x68, 0xFD, 0x21, 0x63, 0xFD, 0xC3, 0x01, 0xB2, 0x70, 0x74,
0x7A, 0xE6, 0x1C, 0xFF, 0xFD, 0xDD, 0xD5, 0xA0, 0x12, 0xE2, 0x21, 0x7A, 0xFD, 0x21, 0x72, 0xFD,
0x21, 0x74, 0xFD, 0x21, 0x85, 0xFD, 0x21, 0xC4, 0xFD, 0xA1, 0x01, 0xB2, 0x6E, 0xFD, 0x41, 0x72,
0xDF, 0x99, 0x41, 0x82, 0xDF, 0x95, 0x21, 0xC5, 0xFC, 0xA2, 0x01, 0xB2, 0x62, 0x67, 0xF5, 0xFD,
0x52, 0xC4, 0xC5, 0x62, 0x63, 0x64, 0x66, 0x67, 0x68, 0x6B, 0x6C, 0x6D, 0x6E, 0x70, 0x72, 0x73,
0x74, 0x77, 0x7A, 0xDF, 0x46, 0xFF, 0x88, 0xDD, 0x2F, 0xDF, 0x65, 0xDF, 0x75, 0xDD, 0x2F, 0xDD,
0x2F, 0xDD, 0x2F, 0xDF, 0x8A, 0xFF, 0x92, 0xFF, 0x98, 0xFF, 0x9E, 0xFF, 0xA7, 0xFF, 0xAD, 0xFF,
0xCB, 0xFF, 0x98, 0xFF, 0xE9, 0xFF, 0xF9, 0x43, 0xC5, 0x64, 0x6B, 0xDD, 0xC9, 0xDD, 0xCC, 0xDD,
0xCC, 0x41, 0x73, 0xF1, 0x96, 0x22, 0x6F, 0x75, 0xF2, 0xFC, 0x21, 0x6C, 0xFB, 0x41, 0x6D, 0xDD,
0xD0, 0x21, 0x6F, 0xFC, 0x21, 0x7A, 0xFD, 0x21, 0x63, 0xFD, 0x22, 0x65, 0x6C, 0xF0, 0xFD, 0x41,
0x62, 0xDD, 0xC4, 0x21, 0x65, 0xFC, 0xA1, 0x00, 0x51, 0x69, 0xFD, 0x41, 0x77, 0xDC, 0xCA, 0x21,
0x62, 0xFC, 0xC4, 0x13, 0x02, 0x69, 0x6F, 0x75, 0x77, 0xE0, 0xCC, 0xFF, 0xFD, 0xE0, 0xCC, 0xE0,
0xCC, 0x21, 0x82, 0xF1, 0x21, 0xC5, 0xFD, 0x21, 0xB3, 0xFD, 0x21, 0xC3, 0xFD, 0xC2, 0x0D, 0x72,
0x6F, 0x77, 0xDB, 0x56, 0xDB, 0x56, 0x21, 0x68, 0xF7, 0x21, 0x63, 0xFD, 0x21, 0x65, 0xFD, 0xA2,
0x00, 0x51, 0x70, 0x7A, 0xEB, 0xFD, 0x41, 0x7A, 0xDE, 0xA3, 0xA1, 0x03, 0xA2, 0x73, 0xFC, 0x44,
0x82, 0x9B, 0xBA, 0xBC, 0xDE, 0x94, 0xDE, 0x94, 0xDE, 0x94, 0xFF, 0xFB, 0x41, 0x61, 0xDE, 0x8D,
0xA1, 0x02, 0x41, 0x68, 0xFC, 0xC2, 0x01, 0xB2, 0x7A, 0x68, 0xFF, 0xFB, 0xDC, 0xDB, 0xC3, 0x01,
0xB2, 0xC5, 0x72, 0x7A, 0xDE, 0xA0, 0xDC, 0xD2, 0xDC, 0xD2, 0xC2, 0x01, 0xB2, 0x6C, 0x72, 0xDC,
0xC6, 0xDE, 0x80, 0xA0, 0x13, 0x23, 0x21, 0x72, 0xFD, 0x21, 0x64, 0xFD, 0xC2, 0x01, 0xB2, 0xC5,
0x7A, 0xDE, 0xBE, 0xDC, 0xB4, 0xC3, 0x01, 0xB2, 0x63, 0x6B, 0x72, 0xDC, 0xA7, 0xDC, 0xAB, 0xDC,
0xAB, 0xA0, 0x06, 0x13, 0x21, 0x73, 0xFD, 0x21, 0x6B, 0xFD, 0x41, 0x6F, 0xED, 0xB1, 0x21, 0x7A,
0xFC, 0x54, 0xC4, 0xC5, 0x62, 0x63, 0x64, 0x66, 0x67, 0x68, 0x6B, 0x6C, 0x6D, 0x6F, 0x70, 0x72,
0x73, 0x74, 0x77, 0x7A, 0x65, 0x69, 0xDE, 0x35, 0xFF, 0x9E, 0xDE, 0x4F, 0xFF, 0xB4, 0xFF, 0xBD,
0xDC, 0x1E, 0xDE, 0x71, 0xDC, 0x1E, 0xFF, 0xC9, 0xDC, 0x1E, 0xFE, 0x87, 0xFF, 0xD8, 0xDE, 0x90,
0xFF, 0xDB, 0xDE, 0xA9, 0xFF, 0xE4, 0xDC, 0x1E, 0xDC, 0x1E, 0xFF, 0xF6, 0xFF, 0xFD, 0xD9, 0x01,
0xF1, 0xC4, 0xC5, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E,
0x6F, 0x70, 0x72, 0x73, 0x74, 0x76, 0x77, 0x78, 0x79, 0x7A, 0xDC, 0x2B, 0xDC, 0x2F, 0xDC, 0x3F,
0xDC, 0x3F, 0xDC, 0x3F, 0xFE, 0xB2, 0xDC, 0x3F, 0xDC, 0x3F, 0xDC, 0x3F, 0xFF, 0x0C, 0xDC, 0x3F,
0xDC, 0x3F, 0xDC, 0x3F, 0xDC, 0x3F, 0xFF, 0x18, 0xE2, 0x93, 0xDC, 0x3F, 0xDC, 0x3F, 0xFF, 0x51,
0xDC, 0x3F, 0xDC, 0x3F, 0xDC, 0x3F, 0xDC, 0x3F, 0xFF, 0xC3, 0xDC, 0x3F, 0xC1, 0x00, 0x61, 0x6F,
0xE2, 0x3E, 0x45, 0x82, 0x84, 0x9B, 0xBA, 0xBC, 0xFF, 0xFA, 0xD9, 0xB8, 0xD9, 0xB8, 0xD9, 0xB8,
0xD9, 0xB8, 0xA0, 0x13, 0x51, 0xA1, 0x13, 0x62, 0x75, 0xFD, 0x21, 0x87, 0xFB, 0x21, 0xC4, 0xFD,
0x21, 0x9B, 0xFD, 0x21, 0xC5, 0xFD, 0x41, 0x6F, 0xE7, 0x58, 0x21, 0x70, 0xFC, 0x21, 0x73, 0xFD,
0xC5, 0x01, 0xB2, 0xC5, 0x6F, 0x72, 0x79, 0x7A, 0xDD, 0x9E, 0xFF, 0xF3, 0xDB, 0xD0, 0xFF, 0xFD,
0xDB, 0xD0, 0xC3, 0x01, 0xB2, 0x74, 0x6C, 0x72, 0xDD, 0xA5, 0xDB, 0xBE, 0xDD, 0x78, 0xC3, 0x01,
0xB2, 0x67, 0x6B, 0x74, 0xDD, 0x99, 0xDD, 0x99, 0xDD, 0x99, 0x42, 0x72, 0x73, 0xFE, 0xE9, 0xFE,
0xE9, 0x21, 0x62, 0xF9, 0x41, 0x68, 0xDD, 0x83, 0xC3, 0x01, 0xB2, 0x63, 0xC5, 0x7A, 0xFF, 0xFC,
0xDD, 0xA2, 0xDB, 0x98, 0x41, 0x6F, 0xDA, 0xAF, 0xA1, 0x01, 0xB2, 0x74, 0xFC, 0xA0, 0x08, 0x41,
0x21, 0x63, 0xFD, 0xA1, 0x0C, 0xB2, 0x69, 0xFD, 0x42, 0x6E, 0x7A, 0xFF, 0xFB, 0xFE, 0xE2, 0x41,
0x64, 0xDB, 0xD4, 0x21, 0x65, 0xFC, 0x21, 0x69, 0xFD, 0x55, 0xC4, 0xC5, 0x62, 0x63, 0x64, 0x66,
0x67, 0x68, 0x6B, 0x6C, 0x6D, 0x6F, 0x70, 0x72, 0x73, 0x74, 0x75, 0x77, 0x7A, 0x69, 0x6E, 0xDD,
0x0D, 0xE6, 0x77, 0xDD, 0x27, 0xDD, 0x2C, 0xFF, 0x97, 0xDA, 0xF6, 0xDD, 0x49, 0xDA, 0xF6, 0xFF,
0xA9, 0xFF, 0xB5, 0xDD, 0x63, 0xFF, 0xC8, 0xDA, 0xF6, 0xFF, 0xCF, 0xDD, 0x81, 0xDD, 0x8D, 0xFF,
0xDF, 0xDA, 0xF6, 0xDA, 0xF6, 0xFF, 0xEF, 0xFF, 0xFD, 0xA0, 0x13, 0x82, 0x21, 0x75, 0xFD, 0x21,
0x77, 0xFD, 0x22, 0x6C, 0x72, 0xF7, 0xF7, 0xA3, 0x0C, 0xB2, 0x61, 0x65, 0x79, 0xF8, 0xFB, 0xF2,
0x21, 0x7A, 0xF7, 0x41, 0x74, 0xEE, 0xCD, 0x21, 0x6E, 0xFC, 0x41, 0x64, 0xEE, 0xC6, 0x21, 0x65,
0xFC, 0x41, 0x6C, 0xEE, 0xBF, 0x21, 0x61, 0xFC, 0x23, 0x6F, 0x72, 0x77, 0xEF, 0xF6, 0xFD, 0x21,
0x6B, 0xF9, 0x21, 0x73, 0xFD, 0xA2, 0x00, 0x51, 0x65, 0x79, 0xDB, 0xFD, 0xC1, 0x01, 0xB2, 0x6C,
0xDA, 0xE4, 0xC3, 0x01, 0xB2, 0x6B, 0xC5, 0x7A, 0xDC, 0xC5, 0xDC, 0xE8, 0xDA, 0xDE, 0xC3, 0x01,
0xB2, 0x63, 0x6D, 0x7A, 0xDA, 0xCE, 0xFF, 0x4F, 0xDA, 0xD2, 0xA0, 0x13, 0xA5, 0x21, 0x73, 0xFD,
0x42, 0x65, 0x70, 0xFF, 0xFD, 0xDC, 0xA7, 0x21, 0x6C, 0xF3, 0x21, 0x65, 0xFD, 0x22, 0x6D, 0x73,
0xF3, 0xFD, 0x41, 0x70, 0xDC, 0x95, 0x21, 0x61, 0xFC, 0xC3, 0x01, 0xB2, 0x6B, 0x65, 0x68, 0xDC,
0x4A, 0xFF, 0xF4, 0xFF, 0xFD, 0x51, 0xC4, 0xC5, 0x62, 0x63, 0x64, 0x66, 0x67, 0x68, 0x6B, 0x6C,
0x6D, 0x70, 0x72, 0x73, 0x74, 0x77, 0x7A, 0xDC, 0x41, 0xE5, 0xAB, 0xDA, 0x2A, 0xDC, 0x60, 0xDC,
0x70, 0xDA, 0x2A, 0xDA, 0x2A, 0xDA, 0x2A, 0xFF, 0xB7, 0xDA, 0x2A, 0xDA, 0x2A, 0xFC, 0xA2, 0xFF,
0xBD, 0xFF, 0xC9, 0xFF, 0xF4, 0xDA, 0x2A, 0xDA, 0x2A, 0x41, 0x6B, 0xEE, 0x58, 0x21, 0x6F, 0xFC,
0x21, 0x6E, 0xFD, 0x21, 0x6D, 0xFD, 0xC1, 0x13, 0xF2, 0x77, 0xE7, 0xE4, 0x21, 0x68, 0xFA, 0x21,
0x63, 0xFD, 0x21, 0x79, 0xFD, 0x21, 0x77, 0xFD, 0x21, 0x74, 0xFD, 0x21, 0x72, 0xFD, 0xA1, 0x00,
0x51, 0x61, 0xFD, 0x45, 0x82, 0x84, 0x9B, 0xBA, 0xBC, 0xD9, 0xA5, 0xD9, 0xA5, 0xD9, 0xA5, 0xD9,
0xA5, 0xD9, 0xA5, 0xA0, 0x14, 0x12, 0x42, 0xBA, 0xBC, 0xFF, 0xFD, 0xD8, 0x6D, 0xC2, 0x01, 0x92,
0xC5, 0x7A, 0xFF, 0xF9, 0xDE, 0x8E, 0xC1, 0x01, 0x92, 0x63, 0xE1, 0xD8, 0x52, 0xC4, 0xC5, 0x62,
0x63, 0x64, 0x66, 0x67, 0x68, 0x6B, 0x6C, 0x6D, 0x6E, 0x70, 0x72, 0x73, 0x74, 0x77, 0x7A, 0xE1,
0xA8, 0xFF, 0xD7, 0xD9, 0x87, 0xE1, 0xB9, 0xFF, 0xF1, 0xD9, 0x87, 0xD9, 0x87, 0xD9, 0x87, 0xD9,
0x87, 0xD9, 0x87, 0xFF, 0xFA, 0xD9, 0x87, 0xD9, 0x87, 0xE1, 0xEB, 0xE1, 0xEB, 0xD9, 0x87, 0xD9,
0x87, 0xD9, 0x87, 0x21, 0x65, 0xC9, 0xA1, 0x00, 0x51, 0x69, 0xFD, 0xC1, 0x0C, 0xB2, 0x75, 0xFE,
0xAE, 0x21, 0x7A, 0xFA, 0xA1, 0x00, 0x51, 0x6F, 0xFD, 0xD9, 0x01, 0xF1, 0xC4, 0xC5, 0x61, 0x62,
0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x72, 0x73,
0x74, 0x76, 0x77, 0x78, 0x7A, 0xD9, 0xA0, 0xFD, 0xC9, 0xFE, 0x60, 0xD9, 0xB4, 0xD9, 0xB4, 0xFE,
0xDC, 0xFF, 0x2C, 0xD9, 0xB4, 0xD9, 0xB4, 0xD9, 0xB4, 0xFF, 0x6A, 0xD9, 0xB4, 0xD9, 0xB4, 0xD9,
0xB4, 0xFF, 0x85, 0xFF, 0xED, 0xDD, 0xD7, 0xD9, 0xB4, 0xFF, 0xFB, 0xD9, 0xB4, 0xD9, 0xB4, 0xD9,
0xB4, 0xD9, 0xB4, 0xD9, 0xB4, 0xD9, 0xB4, 0x5A, 0xC4, 0xC5, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66,
0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
0x78, 0x7A, 0xD7, 0x77, 0xD8, 0x78, 0xD9, 0x3F, 0xD9, 0xA7, 0xDA, 0x7B, 0xDC, 0x80, 0xDD, 0x34,
0xDD, 0x44, 0xDD, 0x9C, 0xDD, 0xFC, 0xDE, 0x6F, 0xDE, 0x83, 0xDF, 0x6B, 0xDF, 0xBD, 0xE0, 0x1E,
0xEA, 0x49, 0xED, 0xC7, 0xF6, 0x22, 0xF7, 0x53, 0xF8, 0xF2, 0xF9, 0xCD, 0xFB, 0x24, 0xDD, 0x44,
0xFD, 0x27, 0xDD, 0x44, 0xFF, 0xB2, 0xA0, 0x14, 0x31, 0xA0, 0x15, 0x81, 0xA1, 0x14, 0x62, 0x2E,
0xFD, 0x21, 0x2E, 0xF8, 0x25, 0x84, 0x9B, 0xBA, 0xBC, 0x82, 0xF8, 0xF8, 0xF8, 0xF8, 0xFD, 0xA1,
0x15, 0x42, 0x2E, 0xEA, 0x21, 0x87, 0xFB, 0x36, 0xC5, 0x62, 0x63, 0x64, 0x66, 0x67, 0x6B, 0x6D,
0x6E, 0x70, 0x73, 0x74, 0x7A, 0xC4, 0x2E, 0x68, 0x6A, 0x6C, 0x72, 0x76, 0x77, 0x78, 0xED, 0xE5,
0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xFD, 0xE2, 0xEA, 0xEA, 0xEA,
0xEA, 0xEA, 0xEA, 0xEA, 0x43, 0x85, 0x99, 0x87, 0xFF, 0xB2, 0xFF, 0xB2, 0xFF, 0xD3, 0x41, 0x7A,
0xF9, 0x9F, 0x21, 0x63, 0xFC, 0xA1, 0x14, 0x31, 0x77, 0xFD, 0x21, 0xB3, 0xFB, 0xA0, 0x14, 0x42,
0xC2, 0x14, 0x31, 0x69, 0x2E, 0xFF, 0xFD, 0xFF, 0x99, 0xA0, 0x14, 0x61, 0x42, 0x63, 0x2E, 0xFF,
0xFD, 0xFF, 0x8D, 0x21, 0x87, 0xF9, 0x45, 0x9B, 0xBA, 0xBC, 0x82, 0x84, 0xFF, 0x86, 0xFF, 0x86,
0xFF, 0x86, 0xFF, 0x8B, 0xFF, 0x8B, 0x43, 0x6D, 0x6E, 0x2E, 0xFF, 0xE3, 0xFF, 0xE3, 0xFF, 0x73,
0x56, 0x63, 0xC4, 0xC5, 0x62, 0x64, 0x66, 0x67, 0x6B, 0x70, 0x73, 0x74, 0x7A, 0x6C, 0x2E, 0x68,
0x6A, 0x6D, 0x6E, 0x72, 0x76, 0x77, 0x78, 0xFF, 0xD0, 0xFF, 0xE3, 0xFF, 0xE6, 0xFF, 0x6C, 0xFF,
0x6C, 0xFF, 0x6C, 0xFF, 0x6C, 0xFF, 0x6C, 0xFF, 0x6C, 0xFF, 0x6C, 0xFF, 0x6C, 0xFF, 0x6C, 0xFF,
0xF6, 0xFF, 0x69, 0xFF, 0x71, 0xFF, 0x71, 0xFF, 0x71, 0xFF, 0x71, 0xFF, 0x71, 0xFF, 0x71, 0xFF,
0x71, 0xFF, 0x71, 0xA0, 0x00, 0xF1, 0x21, 0xBA, 0xFD, 0xC2, 0x14, 0x62, 0xC5, 0x2E, 0xFF, 0xFD,
0xFF, 0x20, 0x41, 0x87, 0xFF, 0x1A, 0x45, 0x9B, 0xBC, 0xBA, 0x82, 0x84, 0xFF, 0x16, 0xFF, 0x16,
0xFF, 0x29, 0xFF, 0x1B, 0xFF, 0x1B, 0x56, 0x64, 0xC4, 0xC5, 0x62, 0x63, 0x66, 0x67, 0x6B, 0x6C,
0x6D, 0x6E, 0x70, 0x73, 0x74, 0x77, 0x7A, 0x2E, 0x68, 0x6A, 0x72, 0x76, 0x78, 0xFF, 0xE3, 0xFF,
0xEC, 0xFF, 0xF0, 0xFF, 0x06, 0xFF, 0x06, 0xFF, 0x06, 0xFF, 0x06, 0xFF, 0x06, 0xFF, 0x06, 0xFF,
0x06, 0xFF, 0x06, 0xFF, 0x06, 0xFF, 0x06, 0xFF, 0x06, 0xFF, 0x06, 0xFF, 0x06, 0xFF, 0x03, 0xFF,
0x0B, 0xFF, 0x0B, 0xFF, 0x0B, 0xFF, 0x0B, 0xFF, 0x0B, 0x45, 0x84, 0x9B, 0xBA, 0xBC, 0x82, 0xFE,
0xC3, 0xFE, 0xC3, 0xFE, 0xC3, 0xFE, 0xC3, 0xFE, 0xD6, 0x56, 0xC4, 0xC5, 0x62, 0x63, 0x64, 0x66,
0x67, 0x68, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x70, 0x72, 0x73, 0x74, 0x77, 0x7A, 0x2E, 0x76, 0x78,
0xFF, 0x99, 0xFF, 0xF0, 0xFE, 0xB3, 0xFE, 0xB3, 0xFE, 0xB3, 0xFE, 0xB3, 0xFE, 0xB3, 0xFE, 0xB3,
0xFE, 0xB3, 0xFE, 0xB3, 0xFE, 0xB3, 0xFE, 0xB3, 0xFE, 0xB3, 0xFE, 0xB3, 0xFE, 0xB3, 0xFE, 0xB3,
0xFE, 0xB3, 0xFE, 0xB3, 0xFE, 0xB3, 0xFE, 0xB0, 0xFE, 0xB8, 0xFE, 0xB8, 0x45, 0x82, 0x84, 0x9B,
0xBA, 0xBC, 0xFE, 0x70, 0xFE, 0x70, 0xFE, 0x70, 0xFE, 0x70, 0xFE, 0x70, 0x56, 0xC4, 0xC5, 0x62,
0x63, 0x64, 0x66, 0x67, 0x68, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x70, 0x72, 0x73, 0x74, 0x77, 0x7A,
0x2E, 0x76, 0x78, 0xFF, 0x46, 0xFF, 0xF0, 0xFE, 0x60, 0xFE, 0x60, 0xFE, 0x60, 0xFE, 0x60, 0xFE,
0x60, 0xFE, 0x60, 0xFE, 0x60, 0xFE, 0x60, 0xFE, 0x60, 0xFE, 0x60, 0xFE, 0x60, 0xFE, 0x60, 0xFE,
0x60, 0xFE, 0x60, 0xFE, 0x60, 0xFE, 0x60, 0xFE, 0x60, 0xFE, 0x5D, 0xFE, 0x65, 0xFE, 0x65, 0x45,
0x82, 0x84, 0x9B, 0xBA, 0xBC, 0xFE, 0x1D, 0xFE, 0x1D, 0xFE, 0x1D, 0xFE, 0x1D, 0xFE, 0x30, 0x56,
0xC4, 0xC5, 0x62, 0x63, 0x64, 0x66, 0x67, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x70, 0x72, 0x73, 0x74,
0x77, 0x7A, 0x2E, 0x68, 0x76, 0x78, 0xFE, 0xF3, 0xFF, 0xF0, 0xFE, 0x0D, 0xFE, 0x0D, 0xFE, 0x0D,
0xFE, 0x0D, 0xFE, 0x0D, 0xFE, 0x0D, 0xFE, 0x0D, 0xFE, 0x0D, 0xFE, 0x0D, 0xFE, 0x0D, 0xFE, 0x0D,
0xFE, 0x0D, 0xFE, 0x0D, 0xFE, 0x0D, 0xFE, 0x0D, 0xFE, 0x0D, 0xFE, 0x0A, 0xFE, 0x12, 0xFE, 0x12,
0xFE, 0x12, 0x45, 0x9B, 0xBA, 0x82, 0x84, 0xBC, 0xFE, 0x5E, 0xFE, 0xC4, 0xFF, 0x17, 0xFF, 0x6A,
0xFF, 0xBD, 0x42, 0x6B, 0x2E, 0xFE, 0x27, 0xFD, 0xB7, 0xC1, 0x14, 0x82, 0x2E, 0xFD, 0xB0, 0x25,
0x82, 0x84, 0x9B, 0xBA, 0xBC, 0xF3, 0xFA, 0xFA, 0xFA, 0xFA, 0x21, 0x87, 0xEF, 0xA0, 0x16, 0x43,
0x21, 0x79, 0xFD, 0x21, 0x61, 0xFD, 0x21, 0x77, 0xFD, 0x21, 0x64, 0xFD, 0x21, 0x61, 0xFD, 0x45,
0x6E, 0x2E, 0x7A, 0x64, 0x6F, 0xFD, 0xFA, 0xFD, 0x8A, 0xFD, 0x92, 0xFE, 0x64, 0xFF, 0xFD, 0xC1,
0x15, 0x62, 0x2E, 0xFD, 0x7A, 0xA0, 0x16, 0x22, 0x21, 0x7A, 0xFD, 0x21, 0x75, 0xFD, 0x21, 0x61,
0xFD, 0x21, 0x68, 0xFD, 0x21, 0x6B, 0xFD, 0x42, 0x2E, 0x6F, 0xFD, 0x62, 0xFF, 0xFD, 0x41, 0x68,
0xFE, 0x35, 0x21, 0x74, 0xFC, 0xA0, 0x15, 0x93, 0x21, 0x7A, 0xFD, 0x21, 0x73, 0xFD, 0x41, 0x64,
0xD9, 0xF0, 0x21, 0x6E, 0xFC, 0x21, 0x65, 0xFD, 0x21, 0x68, 0xFD, 0x23, 0x65, 0x66, 0x6B, 0xE7,
0xF0, 0xFD, 0xA0, 0x04, 0xD3, 0x21, 0x75, 0xFD, 0x21, 0x6B, 0xFD, 0x21, 0x6F, 0xFD, 0xA0, 0x0B,
0xB2, 0x21, 0x70, 0xFD, 0x21, 0x6F, 0xFD, 0x21, 0x6B, 0xFD, 0x21, 0x7A, 0xFD, 0xA0, 0x15, 0xC4,
0xA1, 0x01, 0x22, 0x6D, 0xFD, 0x21, 0x73, 0xFB, 0x21, 0x65, 0xFD, 0x21, 0x6E, 0xFD, 0xA0, 0x16,
0x02, 0x21, 0x6D, 0xFD, 0x21, 0x61, 0xFD, 0x21, 0x68, 0xFD, 0x21, 0x67, 0xFD, 0x21, 0x6E, 0xFD,
0x21, 0x69, 0xFD, 0x21, 0x6D, 0xFD, 0x24, 0x6E, 0x73, 0x7A, 0x72, 0xC5, 0xD4, 0xE5, 0xFD, 0x41,
0x61, 0xDB, 0x01, 0x21, 0x6D, 0xFC, 0x21, 0x73, 0xFD, 0x21, 0x65, 0xB5, 0x21, 0x6D, 0xFD, 0xA0,
0x13, 0x02, 0xA1, 0x00, 0x71, 0x6D, 0xFD, 0x21, 0x73, 0xFB, 0x21, 0x73, 0xFD, 0x21, 0x65, 0xFD,
0x21, 0x6E, 0xFD, 0x22, 0x7A, 0x69, 0xE9, 0xFD, 0xA0, 0x16, 0x72, 0x21, 0x6E, 0xFD, 0x21, 0x61,
0xFD, 0x21, 0x70, 0xFD, 0x21, 0x7A, 0xFD, 0x21, 0x73, 0xFD, 0x22, 0x73, 0x6B, 0xE9, 0xFD, 0x5A,
0xC5, 0xC4, 0x63, 0x64, 0x66, 0x67, 0x6B, 0x6D, 0x6E, 0x70, 0x73, 0x74, 0x7A, 0x72, 0x62, 0x2E,
0x68, 0x6A, 0x6C, 0x76, 0x77, 0x78, 0x65, 0x69, 0x6F, 0x75, 0xFF, 0x00, 0xFF, 0x0B, 0xFE, 0xFA,
0xFE, 0xFA, 0xFE, 0xFA, 0xFE, 0xFA, 0xFE, 0xFA, 0xFE, 0xFA, 0xFE, 0xFA, 0xFE, 0xFA, 0xFE, 0xFA,
0xFE, 0xFA, 0xFE, 0xFA, 0xFF, 0x20, 0xFF, 0x30, 0xFC, 0xAA, 0xFC, 0xB2, 0xFC, 0xB2, 0xFF, 0x48,
0xFC, 0xB2, 0xFC, 0xB2, 0xFC, 0xB2, 0xFF, 0x6C, 0xFF, 0xB7, 0xFF, 0xC7, 0xFF, 0xFB, 0x45, 0x84,
0x9B, 0xBA, 0xBC, 0x82, 0xFE, 0xAB, 0xFE, 0xAB, 0xFE, 0xAB, 0xFE, 0xAB, 0xFC, 0x63, 0xA0, 0x14,
0x62, 0x21, 0x87, 0xFD, 0xC1, 0x00, 0xF1, 0x2E, 0xFC, 0x45, 0x25, 0x84, 0x9B, 0xBA, 0xBC, 0x82,
0xF4, 0xF4, 0xF4, 0xF4, 0xFA, 0xC1, 0x00, 0xF1, 0x7A, 0xFC, 0x3C, 0xA0, 0x16, 0x91, 0x21, 0x74,
0xFD, 0x21, 0x73, 0xFD, 0xD4, 0x02, 0x31, 0xC4, 0xC5, 0x62, 0x63, 0x64, 0x66, 0x67, 0x6B, 0x6D,
0x6E, 0x70, 0x73, 0x74, 0x7A, 0x2E, 0x72, 0x77, 0x6A, 0x6C, 0x75, 0xFF, 0xDD, 0xFF, 0xE6, 0xFF,
0xDA, 0xFF, 0xDA, 0xFF, 0xDA, 0xFF, 0xDA, 0xFF, 0xDA, 0xFF, 0xDA, 0xFF, 0xDA, 0xFF, 0xDA, 0xFF,
0xDA, 0xFF, 0xDA, 0xFF, 0xDA, 0xFF, 0xDA, 0xFC, 0x25, 0xFF, 0xF1, 0xFF, 0xE0, 0xFC, 0xFF, 0xFC,
0xFF, 0xFF, 0xFD, 0x44, 0x84, 0x9B, 0xBA, 0xBC, 0xFF, 0x9B, 0xFF, 0x9B, 0xFF, 0x9B, 0xFF, 0x9B,
0xD0, 0x02, 0x31, 0xC4, 0xC5, 0x62, 0x63, 0x64, 0x66, 0x67, 0x6B, 0x6C, 0x6D, 0x6E, 0x70, 0x73,
0x74, 0x7A, 0x2E, 0xFF, 0x91, 0xFF, 0xF3, 0xFF, 0x8E, 0xFF, 0x8E, 0xFF, 0x8E, 0xFF, 0x8E, 0xFF,
0x8E, 0xFF, 0x8E, 0xFF, 0x8E, 0xFF, 0x8E, 0xFF, 0x8E, 0xFF, 0x8E, 0xFF, 0x8E, 0xFB, 0xDC, 0xFF,
0x8E, 0xFB, 0xD9, 0x41, 0x6C, 0xFE, 0x32, 0x21, 0x6C, 0xFC, 0x21, 0x65, 0xFD, 0x21, 0x77, 0xFD,
0x21, 0x64, 0xFD, 0x21, 0x6C, 0xFD, 0xA0, 0x16, 0xA2, 0x21, 0x6E, 0xFD, 0x21, 0x6F, 0xFD, 0x21,
0x7A, 0xFD, 0x21, 0x72, 0xFD, 0x58, 0xC4, 0xC5, 0x62, 0x64, 0x66, 0x67, 0x6B, 0x6C, 0x6D, 0x6E,
0x70, 0x73, 0x74, 0x68, 0x7A, 0x63, 0x2E, 0x6A, 0x72, 0x76, 0x77, 0x78, 0x61, 0x75, 0xFD, 0xE5,
0xFF, 0x29, 0xFD, 0xD4, 0xFD, 0xD4, 0xFD, 0xD4, 0xFD, 0xD4, 0xFD, 0xD4, 0xFD, 0xD4, 0xFD, 0xD4,
0xFD, 0xD4, 0xFD, 0xD4, 0xFD, 0xD4, 0xFD, 0xD4, 0xFF, 0x5F, 0xFF, 0xAB, 0xFE, 0x0A, 0xFB, 0x84,
0xFB, 0x8C, 0xFB, 0x8C, 0xFB, 0x8C, 0xFB, 0x8C, 0xFB, 0x8C, 0xFF, 0xEE, 0xFF, 0xFD, 0x41, 0x7A,
0xFB, 0xAB, 0x43, 0x62, 0x73, 0x2E, 0xFB, 0xA7, 0xFF, 0xFC, 0xFB, 0x37, 0xA0, 0x14, 0xA2, 0x21,
0x87, 0xFD, 0x24, 0x84, 0x9B, 0xBA, 0xBC, 0xFA, 0xFA, 0xFA, 0xFA, 0xCF, 0x02, 0x31, 0xC4, 0xC5,
0x62, 0x63, 0x64, 0x66, 0x67, 0x6B, 0x6D, 0x6E, 0x70, 0x73, 0x74, 0x7A, 0x2E, 0xFF, 0xF4, 0xFF,
0xF7, 0xFF, 0xF1, 0xFF, 0xF1, 0xFF, 0xF1, 0xFF, 0xF1, 0xFF, 0xF1, 0xFF, 0xF1, 0xFF, 0xF1, 0xFF,
0xF1, 0xFF, 0xF1, 0xFF, 0xF1, 0xFF, 0xF1, 0xFF, 0xF1, 0xFB, 0x1E, 0x45, 0x84, 0x9B, 0xBA, 0xBC,
0x82, 0xFF, 0xC1, 0xFF, 0xC1, 0xFF, 0xC1, 0xFF, 0xC1, 0xE7, 0xF3, 0x42, 0x62, 0x6D, 0xDF, 0xFD,
0xDF, 0xFD, 0x21, 0x7A, 0xF9, 0xD4, 0x02, 0x31, 0xC4, 0xC5, 0x62, 0x63, 0x64, 0x66, 0x67, 0x6B,
0x6D, 0x6E, 0x70, 0x73, 0x74, 0x7A, 0x2E, 0x6A, 0x6C, 0x72, 0x77, 0x65, 0xFF, 0xAA, 0xFF, 0xE6,
0xFF, 0xA7, 0xFF, 0xA7, 0xFF, 0xA7, 0xFF, 0xA7, 0xFF, 0xA7, 0xFF, 0xA7, 0xFF, 0xA7, 0xFF, 0xA7,
0xFF, 0xA7, 0xFF, 0xA7, 0xFF, 0xA7, 0xFF, 0xA7, 0xFA, 0xD4, 0xE7, 0xD9, 0xE7, 0xD9, 0xE7, 0xD9,
0xE7, 0xD9, 0xFF, 0xFD, 0x45, 0x82, 0xBA, 0xBC, 0x84, 0x9B, 0xFF, 0x5E, 0xFF, 0x77, 0xFF, 0xC1,
0xFC, 0xE5, 0xFC, 0xE5, 0xA0, 0x14, 0xC2, 0x21, 0x77, 0xFD, 0x21, 0x6F, 0xFD, 0xC2, 0x14, 0x82,
0x69, 0x2E, 0xFF, 0xFD, 0xFA, 0x7C, 0x42, 0x2E, 0x77, 0xFA, 0x73, 0xE7, 0x78, 0x43, 0x6E, 0x2E,
0x7A, 0xFA, 0xDC, 0xFA, 0x6C, 0xFF, 0xF9, 0xD0, 0x02, 0x31, 0xC4, 0xC5, 0x62, 0x63, 0x64, 0x66,
0x67, 0x6B, 0x6C, 0x6D, 0x6E, 0x70, 0x73, 0x74, 0x7A, 0x2E, 0xFE, 0x1A, 0xFE, 0x7C, 0xFE, 0x17,
0xFE, 0x17, 0xFE, 0x17, 0xFE, 0x17, 0xFE, 0x17, 0xFE, 0x17, 0xFE, 0x17, 0xFE, 0x17, 0xFE, 0x17,
0xFE, 0x17, 0xFE, 0x17, 0xFE, 0x17, 0xFE, 0x17, 0xFA, 0x62, 0x41, 0x64, 0xDF, 0x60, 0x21, 0x6E,
0xFC, 0x21, 0x61, 0xFD, 0x21, 0x6C, 0xFD, 0x21, 0x68, 0xFD, 0x21, 0x63, 0xFD, 0x21, 0x73, 0xFD,
0x21, 0x74, 0xFD, 0x21, 0x75, 0xFD, 0x41, 0x74, 0xFC, 0xE8, 0x21, 0x73, 0xFC, 0x21, 0x61, 0xFD,
0x21, 0x70, 0xFD, 0x21, 0x7A, 0xFD, 0x21, 0x73, 0xFD, 0x58, 0xC5, 0xC4, 0x62, 0x63, 0x66, 0x67,
0x6B, 0x6D, 0x6E, 0x70, 0x73, 0x74, 0x72, 0x7A, 0x64, 0x2E, 0x68, 0x6A, 0x6C, 0x76, 0x77, 0x78,
0x65, 0x75, 0xFF, 0x6B, 0xFC, 0x61, 0xFC, 0x50, 0xFC, 0x50, 0xFC, 0x50, 0xFC, 0x50, 0xFC, 0x50,
0xFC, 0x50, 0xFF, 0x84, 0xFC, 0x50, 0xFC, 0x50, 0xFC, 0x50, 0xFF, 0x94, 0xFF, 0x9E, 0xFC, 0x86,
0xFA, 0x00, 0xFA, 0x08, 0xFA, 0x08, 0xFA, 0x08, 0xFA, 0x08, 0xFA, 0x08, 0xFA, 0x08, 0xFF, 0xEA,
0xFF, 0xFD, 0x41, 0x87, 0xF9, 0xBF, 0x45, 0x82, 0x84, 0x9B, 0xBA, 0xBC, 0xF9, 0xBB, 0xF9, 0xBB,
0xF9, 0xBB, 0xF9, 0xBB, 0xF9, 0xBB, 0x41, 0x6D, 0xD7, 0xBA, 0x21, 0x72, 0xFC, 0x21, 0x61, 0xFD,
0x41, 0x6E, 0xFC, 0x6E, 0x21, 0x69, 0xFC, 0x21, 0x62, 0xFD, 0x22, 0x68, 0x7A, 0xF3, 0xFD, 0x21,
0x73, 0xFB, 0xA0, 0x16, 0xC2, 0x21, 0x63, 0xFD, 0x21, 0x61, 0xFD, 0x21, 0x7A, 0xFD, 0x41, 0x72,
0xFC, 0x07, 0x21, 0x6F, 0xFC, 0x21, 0x6C, 0xFD, 0x21, 0x6B, 0xFD, 0xA0, 0x16, 0xE3, 0x21, 0x74,
0xFD, 0x21, 0x6F, 0xFD, 0x21, 0x7A, 0xFD, 0x21, 0x61, 0xFD, 0x21, 0x66, 0xFD, 0x44, 0x6B, 0x72,
0x6C, 0x73, 0xD1, 0x98, 0xFF, 0xDE, 0xFF, 0xEB, 0xFF, 0xFD, 0x58, 0x63, 0x6B, 0x6D, 0x6E, 0x66,
0x2E, 0xC4, 0xC5, 0x62, 0x64, 0x67, 0x68, 0x6A, 0x6C, 0x70, 0x72, 0x73, 0x74, 0x76, 0x77, 0x78,
0x7A, 0x69, 0x6F, 0xFB, 0x9F, 0xFB, 0x9F, 0xFB, 0x9F, 0xFB, 0x9F, 0xFB, 0xD5, 0xF9, 0x4F, 0xFF,
0x98, 0xFF, 0x9C, 0xF9, 0x57, 0xF9, 0x57, 0xF9, 0x57, 0xF9, 0x57, 0xF9, 0x57, 0xF9, 0x57, 0xF9,
0x57, 0xF9, 0x57, 0xF9, 0x57, 0xF9, 0x57, 0xF9, 0x57, 0xF9, 0x57, 0xF9, 0x57, 0xF9, 0x57, 0xFF,
0xC5, 0xFF, 0xF3, 0x42, 0x62, 0x2E, 0xF9, 0x76, 0xF9, 0x06, 0x45, 0x82, 0x84, 0x9B, 0xBA, 0xBC,
0xFF, 0xF9, 0xFB, 0x4F, 0xFB, 0x4F, 0xFB, 0x4F, 0xFB, 0x4F, 0xA0, 0x17, 0x12, 0x21, 0x6F, 0xFD,
0x21, 0x6C, 0xFD, 0x21, 0x69, 0xFD, 0x21, 0x64, 0xFD, 0x21, 0x6E, 0xFD, 0x41, 0x74, 0xF4, 0x52,
0x43, 0x2E, 0x61, 0x6F, 0xF8, 0xD9, 0xFF, 0xF9, 0xFF, 0xFC, 0x41, 0x74, 0xE5, 0xAD, 0x21, 0x65,
0xFC, 0x41, 0x61, 0xD6, 0xC0, 0x21, 0x74, 0xFC, 0x21, 0x70, 0xFD, 0x22, 0x67, 0x6F, 0xF3, 0xFD,
0x21, 0x64, 0xFB, 0xC1, 0x02, 0x02, 0x7A, 0xD1, 0x1E, 0x21, 0x73, 0xFA, 0x21, 0x66, 0xFD, 0x21,
0x6C, 0xFD, 0x58, 0xC5, 0xC4, 0x62, 0x63, 0x64, 0x66, 0x6B, 0x6D, 0x70, 0x73, 0x74, 0x7A, 0x67,
0x2E, 0x68, 0x6A, 0x6C, 0x6E, 0x72, 0x76, 0x77, 0x78, 0x61, 0x6F, 0xFF, 0xA8, 0xFB, 0x08, 0xFA,
0xF7, 0xFA, 0xF7, 0xFA, 0xF7, 0xFA, 0xF7, 0xFA, 0xF7, 0xFA, 0xF7, 0xFA, 0xF7, 0xFA, 0xF7, 0xFA,
0xF7, 0xFA, 0xF7, 0xFB, 0x2D, 0xF8, 0xA7, 0xF8, 0xAF, 0xF8, 0xAF, 0xF8, 0xAF, 0xF8, 0xAF, 0xFF,
0xCE, 0xF8, 0xAF, 0xF8, 0xAF, 0xF8, 0xAF, 0xFF, 0xEE, 0xFF, 0xFD, 0x45, 0x82, 0x84, 0x9B, 0xBA,
0xBC, 0xFA, 0xAE, 0xFA, 0xAE, 0xFA, 0xAE, 0xFA, 0xAE, 0xFA, 0xAE, 0x41, 0x7A, 0xFB, 0x26, 0x21,
0x73, 0xFC, 0xA0, 0x17, 0x32, 0x21, 0x77, 0xFD, 0x21, 0x7A, 0xFD, 0x41, 0x79, 0xD6, 0x55, 0x21,
0x65, 0xFC, 0x21, 0x6C, 0xFD, 0x22, 0x63, 0x78, 0xF3, 0xFD, 0x58, 0xC4, 0xC5, 0x62, 0x63, 0x64,
0x66, 0x67, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x70, 0x72, 0x73, 0x74, 0x77, 0x7A, 0x68, 0x2E, 0x76,
0x78, 0x69, 0x75, 0xFA, 0x90, 0xFF, 0xD1, 0xFA, 0x7F, 0xFA, 0x7F, 0xFA, 0x7F, 0xFA, 0x7F, 0xFA,
0x7F, 0xFA, 0x7F, 0xFA, 0x7F, 0xFA, 0x7F, 0xFA, 0x7F, 0xFA, 0x7F, 0xFA, 0x7F, 0xFA, 0x7F, 0xFA,
0x7F, 0xFA, 0x7F, 0xFA, 0x7F, 0xFA, 0x7F, 0xFA, 0xB5, 0xF8, 0x2F, 0xF8, 0x37, 0xF8, 0x37, 0xFF,
0xE5, 0xFF, 0xFB, 0xA0, 0x05, 0x42, 0x21, 0x82, 0xFD, 0xC3, 0x14, 0x82, 0x2E, 0xC5, 0x72, 0xF7,
0xE0, 0xFF, 0xFD, 0xFF, 0xFA, 0xA0, 0x17, 0x53, 0x22, 0x62, 0x6D, 0xFD, 0xFD, 0x21, 0x7A, 0xFB,
0x21, 0x7A, 0xFD, 0x57, 0xC4, 0xC5, 0x62, 0x63, 0x64, 0x66, 0x67, 0x68, 0x6B, 0x6C, 0x6D, 0x6E,
0x70, 0x72, 0x73, 0x74, 0x77, 0x7A, 0x6A, 0x2E, 0x76, 0x78, 0x61, 0xFA, 0x27, 0xFF, 0x68, 0xFA,
0x16, 0xFA, 0x16, 0xFA, 0x16, 0xFA, 0x16, 0xFA, 0x16, 0xFA, 0x16, 0xFA, 0x16, 0xFA, 0x16, 0xFA,
0x16, 0xFA, 0x16, 0xFA, 0x16, 0xFA, 0x16, 0xFA, 0x16, 0xFF, 0xE6, 0xFA, 0x16, 0xFA, 0x16, 0xFA,
0x4C, 0xF7, 0xC6, 0xF7, 0xCE, 0xF7, 0xCE, 0xFF, 0xFD, 0xA0, 0x14, 0xE3, 0xA1, 0x14, 0x82, 0x74,
0xFD, 0xC3, 0x14, 0x82, 0x7A, 0x2E, 0x74, 0xFF, 0xFB, 0xF7, 0x78, 0xF7, 0x80, 0x41, 0x6E, 0xFE,
0x7D, 0x21, 0x6F, 0xFC, 0x21, 0x72, 0xFD, 0x41, 0x65, 0xFE, 0x73, 0x21, 0x68, 0xFC, 0x21, 0x75,
0xFD, 0x22, 0x6B, 0x72, 0xF3, 0xFD, 0x21, 0x73, 0xFB, 0x21, 0x6C, 0xFD, 0x21, 0x72, 0xFD, 0x41,
0x66, 0xE4, 0x4F, 0x21, 0x66, 0xFC, 0x21, 0x6F, 0xFD, 0x21, 0x68, 0xFD, 0x21, 0x68, 0xFD, 0x21,
0x63, 0xFD, 0x21, 0x72, 0xFD, 0x41, 0x6D, 0xE6, 0x76, 0x21, 0x73, 0xFC, 0x21, 0x65, 0xFD, 0x21,
0x72, 0xFD, 0x21, 0x67, 0xFD, 0x21, 0x6E, 0xFD, 0x59, 0xC5, 0xC4, 0x62, 0x63, 0x64, 0x66, 0x67,
0x6D, 0x6E, 0x70, 0x73, 0x74, 0x7A, 0x6B, 0x2E, 0x68, 0x6A, 0x6C, 0x72, 0x76, 0x77, 0x78, 0x61,
0x69, 0x6F, 0xFE, 0x22, 0xF9, 0x82, 0xF9, 0x71, 0xF9, 0x71, 0xF9, 0x71, 0xF9, 0x71, 0xF9, 0x71,
0xF9, 0x71, 0xF9, 0x71, 0xF9, 0x71, 0xFF, 0xA9, 0xF9, 0x71, 0xF9, 0x71, 0xF9, 0xA7, 0xF7, 0x21,
0xF7, 0x29, 0xF7, 0x29, 0xF7, 0x29, 0xF7, 0x29, 0xF7, 0x29, 0xF7, 0x29, 0xF7, 0x29, 0xFF, 0xD4,
0xFF, 0xEA, 0xFF, 0xFD, 0xA0, 0x14, 0xB1, 0x21, 0x77, 0xFD, 0x21, 0x64, 0xFD, 0x41, 0x66, 0xF9,
0xEC, 0x21, 0x66, 0xFC, 0x21, 0x61, 0xFD, 0x21, 0x77, 0xFD, 0x21, 0x74, 0xFD, 0x41, 0x72, 0xFE,
0x75, 0x21, 0x65, 0xFC, 0x21, 0x66, 0xFD, 0x21, 0x73, 0xFD, 0x22, 0x66, 0x6B, 0xF0, 0xFD, 0x41,
0x6F, 0xF7, 0x84, 0x59, 0xC4, 0xC5, 0x62, 0x63, 0x64, 0x66, 0x67, 0x68, 0x6A, 0x6B, 0x6D, 0x6E,
0x70, 0x72, 0x73, 0x74, 0x77, 0x7A, 0x6C, 0x2E, 0x76, 0x78, 0x65, 0x75, 0x79, 0xF9, 0x07, 0xFE,
0x48, 0xF8, 0xF6, 0xF8, 0xF6, 0xF8, 0xF6, 0xF8, 0xF6, 0xF8, 0xF6, 0xF8, 0xF6, 0xF8, 0xF6, 0xF8,
0xF6, 0xF8, 0xF6, 0xF8, 0xF6, 0xF8, 0xF6, 0xF8, 0xF6, 0xF8, 0xF6, 0xF8, 0xF6, 0xF8, 0xF6, 0xF8,
0xF6, 0xF9, 0x2C, 0xF6, 0xA6, 0xF6, 0xAE, 0xF6, 0xAE, 0xFF, 0xD7, 0xFF, 0xF7, 0xFF, 0xFC, 0xA0,
0x15, 0x13, 0xC2, 0x14, 0x82, 0x6E, 0x2E, 0xFF, 0xFD, 0xF6, 0x57, 0x41, 0x74, 0xFB, 0xC9, 0x41,
0x6A, 0xFB, 0xC5, 0x22, 0x73, 0x7A, 0xF8, 0xFC, 0xC2, 0x14, 0x82, 0x65, 0x2E, 0xFF, 0xFB, 0xF6,
0x41, 0x41, 0x6E, 0xFE, 0xB8, 0xC3, 0x14, 0x82, 0x6B, 0x2E, 0x74, 0xFF, 0xFC, 0xF6, 0x34, 0xF6,
0x3C, 0x41, 0x82, 0xFC, 0xA1, 0x43, 0xC5, 0x6C, 0x6E, 0xFF, 0xFC, 0xFC, 0x9D, 0xFC, 0x9D, 0x21,
0x7A, 0xF6, 0x21, 0x72, 0xFD, 0x41, 0x7A, 0xFD, 0xCD, 0x21, 0x73, 0xFC, 0x21, 0x61, 0xFD, 0x21,
0x6D, 0xFD, 0x21, 0x7A, 0xFD, 0xA0, 0x17, 0x82, 0x21, 0x82, 0xFD, 0x22, 0xC5, 0x69, 0xFD, 0xFA,
0x21, 0x7A, 0xFB, 0x21, 0x72, 0xFD, 0x22, 0x73, 0x65, 0xEC, 0xFD, 0x41, 0x6C, 0xD3, 0x22, 0x21,
0x61, 0xFC, 0x21, 0x65, 0xFD, 0x21, 0x72, 0xFD, 0x21, 0x74, 0xFD, 0x41, 0x6B, 0xE2, 0xC5, 0x21,
0x69, 0xFC, 0x21, 0x61, 0xFD, 0x22, 0x6E, 0x7A, 0xF3, 0xFD, 0xA0, 0x17, 0xA3, 0x21, 0x6C, 0xFD,
0x21, 0x68, 0xFD, 0x21, 0x63, 0xFD, 0x21, 0x69, 0xFD, 0x21, 0x73, 0xFD, 0x21, 0x61, 0xFD, 0x21,
0x7A, 0xFD, 0x21, 0x72, 0xFD, 0x5A, 0xC4, 0xC5, 0x62, 0x63, 0x64, 0x66, 0x67, 0x68, 0x6A, 0x6B,
0x6C, 0x6E, 0x70, 0x72, 0x73, 0x74, 0x77, 0x7A, 0x6D, 0x2E, 0x76, 0x78, 0x61, 0x69, 0x6F, 0x75,
0xF8, 0x15, 0xFD, 0x56, 0xF8, 0x04, 0xF8, 0x04, 0xF8, 0x04, 0xF8, 0x04, 0xF8, 0x04, 0xF8, 0x04,
0xF8, 0x04, 0xFF, 0x5D, 0xF8, 0x04, 0xFF, 0x73, 0xF8, 0x04, 0xF8, 0x04, 0xFF, 0x80, 0xF8, 0x04,
0xF8, 0x04, 0xF8, 0x04, 0xF8, 0x3A, 0xF5, 0xB4, 0xF5, 0xBC, 0xF5, 0xBC, 0xFF, 0x9D, 0xFF, 0xC1,
0xFF, 0xE0, 0xFF, 0xFD, 0xC2, 0x14, 0x82, 0x6E, 0x2E, 0xF5, 0xD5, 0xF5, 0x65, 0x41, 0x73, 0xEF,
0xFD, 0x21, 0x77, 0xFC, 0x21, 0x6F, 0xFD, 0x21, 0x6B, 0xFD, 0x21, 0x82, 0xFD, 0x41, 0x76, 0xEF,
0x40, 0x22, 0xC5, 0x72, 0xF9, 0xFC, 0x57, 0xC4, 0xC5, 0x62, 0x63, 0x64, 0x66, 0x67, 0x68, 0x6A,
0x6B, 0x6C, 0x6D, 0x70, 0x72, 0x73, 0x74, 0x77, 0x7A, 0x6E, 0x2E, 0x76, 0x78, 0x61, 0xF7, 0xA4,
0xFC, 0xE5, 0xF7, 0x93, 0xF7, 0x93, 0xF7, 0x93, 0xF7, 0x93, 0xF7, 0x93, 0xF7, 0x93, 0xF7, 0x93,
0xF7, 0x93, 0xF7, 0x93, 0xF7, 0x93, 0xF7, 0x93, 0xF7, 0x93, 0xF7, 0x93, 0xFF, 0xDE, 0xF7, 0x93,
0xF7, 0x93, 0xF7, 0xC9, 0xF5, 0x43, 0xF5, 0x4B, 0xF5, 0x4B, 0xFF, 0xFB, 0x41, 0x75, 0xFA, 0x78,
0xC2, 0x14, 0x82, 0x65, 0x2E, 0xFF, 0xFC, 0xF4, 0xF9, 0xA0, 0x14, 0x82, 0xC2, 0x14, 0x82, 0x7A,
0x2E, 0xFF, 0xFD, 0xF4, 0xED, 0x42, 0x6E, 0x2E, 0xF5, 0x54, 0xF4, 0xE4, 0x41, 0x6C, 0xF5, 0xB7,
0x21, 0x61, 0xFC, 0x21, 0x68, 0xFD, 0x41, 0x61, 0xF8, 0x12, 0x21, 0x70, 0xFC, 0x21, 0x7A, 0xFD,
0x21, 0x73, 0xFD, 0x44, 0x2E, 0x7A, 0x63, 0x65, 0xF4, 0xC6, 0xF4, 0xCE, 0xFF, 0xF0, 0xFF, 0xFD,
0x41, 0x72, 0xF7, 0x82, 0x21, 0x65, 0xFC, 0x21, 0x6D, 0xFD, 0x21, 0x61, 0xFD, 0x42, 0x61, 0x68,
0xD2, 0xC6, 0xD2, 0xC3, 0x21, 0x63, 0xF9, 0x22, 0x6E, 0x73, 0xF3, 0xFD, 0x41, 0x69, 0xCD, 0x37,
0x21, 0x6E, 0xFC, 0x21, 0x64, 0xFD, 0x21, 0x75, 0xFD, 0x21, 0x82, 0xFD, 0x41, 0x7A, 0xD2, 0x7C,
0x21, 0x72, 0xFC, 0x21, 0x70, 0xFD, 0x21, 0x65, 0xFD, 0x21, 0x69, 0xFD, 0x21, 0x6E, 0xFD, 0x42,
0x85, 0x99, 0xE1, 0x58, 0xE1, 0x58, 0x42, 0xC4, 0x69, 0xFF, 0xF9, 0xE1, 0x51, 0x21, 0x6E, 0xF9,
0xA0, 0x17, 0xD4, 0x21, 0x68, 0xFD, 0x21, 0x74, 0xFD, 0x21, 0x75, 0xFD, 0x21, 0x6F, 0xFD, 0x21,
0x6D, 0xFD, 0x41, 0x64, 0xEE, 0xF8, 0x21, 0x6E, 0xFC, 0x21, 0x61, 0xFD, 0x22, 0x73, 0x6C, 0xF3,
0xFD, 0x21, 0x74, 0xFB, 0x41, 0x65, 0xD2, 0x3D, 0x21, 0x72, 0xFC, 0x42, 0x65, 0x75, 0xD4, 0xF0,
0xFF, 0xFD, 0x21, 0x69, 0xF9, 0x41, 0x69, 0xCD, 0xBE, 0x21, 0x6E, 0xFC, 0x21, 0x64, 0xFD, 0x21,
0x65, 0xFD, 0x21, 0x7A, 0xFD, 0x21, 0x73, 0xFD, 0x46, 0xC5, 0x64, 0x6D, 0x72, 0x6C, 0x77, 0xFF,
0x91, 0xFF, 0xA4, 0xFF, 0xB5, 0xFF, 0xD9, 0xFF, 0xEA, 0xFF, 0xFD, 0x58, 0xC4, 0xC5, 0x62, 0x63,
0x64, 0x66, 0x67, 0x6B, 0x6D, 0x6E, 0x73, 0x74, 0x7A, 0x6C, 0x70, 0x2E, 0x68, 0x6A, 0x72, 0x76,
0x77, 0x78, 0x61, 0x6F, 0xF6, 0x6F, 0xF7, 0xB3, 0xF6, 0x5E, 0xF6, 0x5E, 0xF6, 0x5E, 0xF6, 0x5E,
0xF6, 0x5E, 0xF6, 0x5E, 0xF6, 0x5E, 0xFF, 0x15, 0xFF, 0x21, 0xF6, 0x5E, 0xF6, 0x5E, 0xFF, 0x2A,
0xF6, 0x94, 0xF4, 0x0E, 0xF4, 0x16, 0xF4, 0x16, 0xFF, 0x48, 0xF4, 0x16, 0xF4, 0x16, 0xF4, 0x16,
0xFF, 0x6C, 0xFF, 0xED, 0xC2, 0x14, 0x82, 0x2E, 0x7A, 0xF3, 0xC5, 0xF3, 0xCD, 0x45, 0x82, 0x84,
0x9B, 0xBA, 0xBC, 0xF3, 0xBF, 0xF7, 0x71, 0xF7, 0x71, 0xF7, 0x71, 0xF7, 0x71, 0xD3, 0x02, 0x31,
0xC4, 0xC5, 0x62, 0x63, 0x64, 0x66, 0x67, 0x68, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x70, 0x72, 0x73,
0x74, 0x77, 0x2E, 0xF7, 0x64, 0xFF, 0xF0, 0xF7, 0x61, 0xF7, 0x61, 0xF7, 0x61, 0xF7, 0x61, 0xF7,
0x61, 0xF7, 0x61, 0xF7, 0x61, 0xF7, 0x61, 0xF7, 0x61, 0xF7, 0x61, 0xF7, 0x61, 0xF7, 0x61, 0xF7,
0x61, 0xF7, 0x61, 0xF7, 0x61, 0xF7, 0x61, 0xF3, 0xAC, 0x41, 0x6E, 0xF6, 0x1C, 0x21, 0x65, 0xFC,
0x21, 0x67, 0xFD, 0x21, 0x74, 0xFD, 0x21, 0x6E, 0xFD, 0x41, 0x7A, 0xD1, 0x77, 0x21, 0x63, 0xFC,
0x21, 0x6F, 0xFD, 0x21, 0x72, 0xFD, 0xA0, 0x18, 0x14, 0x21, 0x6B, 0xFD, 0x21, 0x63, 0xFD, 0x21,
0x6F, 0xFD, 0x21, 0x74, 0xFD, 0x23, 0x65, 0x6B, 0x73, 0xE1, 0xEE, 0xFD, 0x57, 0xC4, 0xC5, 0x62,
0x63, 0x64, 0x66, 0x67, 0x68, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x70, 0x73, 0x74, 0x77, 0x7A, 0x72,
0x2E, 0x76, 0x78, 0x6F, 0xF5, 0x9E, 0xFA, 0xDF, 0xF5, 0x8D, 0xF5, 0x8D, 0xF5, 0x8D, 0xF5, 0x8D,
0xF5, 0x8D, 0xF5, 0x8D, 0xF5, 0x8D, 0xF5, 0x8D, 0xF5, 0x8D, 0xF5, 0x8D, 0xF5, 0x8D, 0xF5, 0x8D,
0xFF, 0x78, 0xF5, 0x8D, 0xF5, 0x8D, 0xFF, 0x91, 0xF5, 0xC3, 0xF3, 0x3D, 0xF3, 0x45, 0xF3, 0x45,
0xFF, 0xF9, 0x45, 0x82, 0xBA, 0xBC, 0x84, 0x9B, 0xF9, 0xF1, 0xF5, 0x47, 0xF5, 0x47, 0xF2, 0xFF,
0xF2, 0xFF, 0x42, 0x2E, 0x7A, 0xF2, 0xE7, 0xF2, 0xEF, 0x46, 0x6B, 0x6E, 0x73, 0x2E, 0x72, 0x77,
0xF3, 0x50, 0xF3, 0x50, 0xF7, 0xA5, 0xF2, 0xE0, 0xFF, 0xF9, 0xF2, 0xE8, 0x41, 0x9B, 0xF6, 0x82,
0x42, 0x2E, 0x62, 0xF2, 0xC9, 0xF2, 0xD1, 0xA1, 0x14, 0x62, 0x7A, 0xF9, 0xC1, 0x14, 0x62, 0x6E,
0xF3, 0x2D, 0x41, 0x6D, 0xF9, 0xC8, 0x21, 0x6C, 0xFC, 0x21, 0x6F, 0xFD, 0x21, 0x68, 0xFD, 0x21,
0x6B, 0xFD, 0xC3, 0x14, 0x62, 0x2E, 0x72, 0x6F, 0xF2, 0xA7, 0xF2, 0xAF, 0xFF, 0xFD, 0xA0, 0x18,
0x52, 0x21, 0x79, 0xFD, 0x21, 0x82, 0xFD, 0x21, 0xC5, 0xFD, 0x21, 0x7A, 0xFD, 0x21, 0x73, 0xFD,
0x41, 0x67, 0xF5, 0x15, 0x21, 0x6E, 0xFC, 0x21, 0x6F, 0xFD, 0x21, 0x6C, 0xFD, 0x41, 0x87, 0xCA,
0xC6, 0x21, 0xC4, 0xFC, 0x21, 0x9B, 0xFD, 0x22, 0x7A, 0xC5, 0xF3, 0xFD, 0x41, 0x73, 0xF9, 0x7E,
0x21, 0x61, 0xFC, 0x21, 0x77, 0xFD, 0x21, 0x6B, 0xFD, 0x21, 0x6E, 0xFD, 0xD1, 0x02, 0x31, 0xC4,
0xC5, 0x63, 0x66, 0x6B, 0x6C, 0x6D, 0x6E, 0x70, 0x73, 0x74, 0x77, 0x7A, 0x2E, 0x61, 0x65, 0x79,
0xF6, 0x15, 0xFF, 0x90, 0xFF, 0x9B, 0xF6, 0x12, 0xF2, 0x60, 0xFF, 0xA0, 0xF6, 0x12, 0xF2, 0x60,
0xF6, 0x12, 0xF6, 0x12, 0xFF, 0xB6, 0xF6, 0x12, 0xF6, 0x12, 0xF2, 0x5D, 0xFF, 0xD1, 0xFF, 0xEB,
0xFF, 0xFD, 0x42, 0x2E, 0x68, 0xF2, 0x27, 0xF2, 0x2F, 0x41, 0x7A, 0xF2, 0x28, 0x41, 0x72, 0xFC,
0x18, 0x21, 0x65, 0xFC, 0x21, 0x6E, 0xFD, 0x21, 0x6F, 0xFD, 0x21, 0x7A, 0xFD, 0x21, 0x72, 0xFD,
0x43, 0x2E, 0x72, 0x6F, 0xF2, 0x09, 0xFF, 0xE9, 0xFF, 0xFD, 0x42, 0x2E, 0x72, 0xF1, 0xFF, 0xF2,
0xD9, 0x41, 0x65, 0xD0, 0x0F, 0x21, 0x6C, 0xFC, 0x21, 0x74, 0xFD, 0x41, 0x7A, 0xCF, 0xE6, 0x21,
0x69, 0xFC, 0x21, 0x77, 0xFD, 0x41, 0x74, 0xCF, 0xFB, 0x21, 0x70, 0xFC, 0x21, 0x6F, 0xFD, 0x41,
0x6D, 0xCE, 0x7F, 0x21, 0x65, 0xFC, 0x21, 0x74, 0xFD, 0x22, 0x6E, 0x73, 0xF3, 0xFD, 0x59, 0xC5,
0x62, 0x64, 0x66, 0x67, 0x73, 0x6E, 0x74, 0x7A, 0x2E, 0xC4, 0x63, 0x68, 0x6A, 0x6B, 0x6C, 0x6D,
0x70, 0x72, 0x76, 0x77, 0x78, 0x65, 0x6F, 0x79, 0xFE, 0xD4, 0xF4, 0x1B, 0xF4, 0x1B, 0xF4, 0x1B,
0xF4, 0x1B, 0xF4, 0x1B, 0xF4, 0x14, 0xFE, 0xEB, 0xFF, 0x6E, 0xF1, 0xCB, 0xF8, 0x14, 0xFF, 0xA4,
0xF1, 0xD3, 0xF1, 0xD3, 0xFF, 0xC2, 0xF1, 0xD3, 0xFF, 0xCC, 0xF1, 0xD3, 0xF1, 0xD3, 0xF1, 0xD3,
0xF1, 0xD3, 0xF1, 0xD3, 0xFF, 0xDA, 0xFF, 0xE4, 0xFF, 0xFB, 0x43, 0x6B, 0x7A, 0x2E, 0xF1, 0xEF,
0xFC, 0x9B, 0xF1, 0x7F, 0xA0, 0x18, 0x73, 0x21, 0x74, 0xFD, 0x21, 0x6F, 0xFD, 0x21, 0x79, 0xFD,
0x41, 0x74, 0xE0, 0xAB, 0x21, 0x75, 0xFC, 0x21, 0x7A, 0xFD, 0x21, 0x72, 0xFD, 0x21, 0x64, 0xFD,
0x21, 0x6F, 0xFD, 0x21, 0x6F, 0xFD, 0x21, 0x62, 0xFD, 0x21, 0x72, 0xFD, 0x41, 0x69, 0xCF, 0x45,
0x21, 0x6E, 0xFC, 0x21, 0x64, 0xFD, 0x21, 0x6F, 0xFD, 0x21, 0x67, 0xFD, 0x59, 0xC4, 0xC5, 0x62,
0x63, 0x64, 0x66, 0x67, 0x6B, 0x6D, 0x6E, 0x70, 0x73, 0x7A, 0x6C, 0x72, 0x74, 0x2E, 0x68, 0x6A,
0x76, 0x77, 0x78, 0x6F, 0x75, 0x79, 0xF3, 0x9E, 0xF4, 0xE2, 0xF3, 0x8D, 0xF3, 0x8D, 0xF3, 0x8D,
0xF3, 0x8D, 0xF3, 0x8D, 0xF3, 0x8D, 0xF3, 0x8D, 0xF3, 0x8D, 0xF3, 0x8D, 0xF3, 0x8D, 0xF3, 0x8D,
0xFC, 0x59, 0xFF, 0xBE, 0xF3, 0xC3, 0xF1, 0x3D, 0xF1, 0x45, 0xF1, 0x45, 0xF1, 0x45, 0xF1, 0x45,
0xF1, 0x45, 0xFF, 0xD1, 0xFF, 0xED, 0xFF, 0xFD, 0x41, 0x64, 0xF6, 0x6C, 0x41, 0x73, 0xF6, 0x68,
0xC3, 0x14, 0x82, 0x61, 0x6F, 0x2E, 0xFF, 0xF8, 0xFF, 0xFC, 0xF0, 0xE9, 0x45, 0x82, 0x84, 0x9B,
0xBA, 0xBC, 0xFF, 0xF4, 0xF3, 0x2D, 0xF3, 0x2D, 0xF3, 0x2D, 0xF3, 0x2D, 0x21, 0x61, 0xE0, 0xC2,
0x14, 0x82, 0x7A, 0x2E, 0xFF, 0xFD, 0xF0, 0xCA, 0xA0, 0x18, 0xA3, 0x21, 0x64, 0xFD, 0x21, 0x6E,
0xFD, 0x21, 0x65, 0xFD, 0x21, 0x6B, 0xFD, 0x42, 0x66, 0x6D, 0xEB, 0x53, 0xEB, 0x53, 0x21, 0x74,
0xF9, 0x22, 0x65, 0x73, 0xF3, 0xFD, 0x57, 0xC4, 0xC5, 0x62, 0x63, 0x64, 0x66, 0x67, 0x6A, 0x6B,
0x6C, 0x6D, 0x6E, 0x70, 0x72, 0x73, 0x74, 0x7A, 0x77, 0x2E, 0x68, 0x76, 0x78, 0x65, 0xF3, 0x04,
0xFF, 0xC6, 0xF2, 0xF3, 0xFF, 0xD9, 0xF2, 0xF3, 0xF2, 0xF3, 0xF2, 0xF3, 0xF2, 0xF3, 0xF2, 0xF3,
0xF2, 0xF3, 0xF2, 0xF3, 0xF2, 0xF3, 0xF2, 0xF3, 0xF2, 0xF3, 0xF2, 0xF3, 0xF2, 0xF3, 0xF2, 0xF3,
0xF3, 0x29, 0xF0, 0xA3, 0xF0, 0xAB, 0xF0, 0xAB, 0xF0, 0xAB, 0xFF, 0xFB, 0x45, 0x9B, 0x82, 0x84,
0xBA, 0xBC, 0xF2, 0xAD, 0xF0, 0x65, 0xF0, 0x65, 0xF0, 0x65, 0xF0, 0x65, 0xC4, 0x14, 0x82, 0x6B,
0x6E, 0x2E, 0x72, 0xF0, 0xBD, 0xF0, 0xBD, 0xF0, 0x4D, 0xFD, 0x66, 0x41, 0x70, 0xEA, 0x32, 0x21,
0x70, 0xFC, 0x57, 0xC4, 0xC5, 0x63, 0x64, 0x66, 0x6B, 0x70, 0x73, 0x74, 0x7A, 0x2E, 0x62, 0x67,
0x68, 0x6A, 0x6C, 0x6D, 0x6E, 0x72, 0x76, 0x77, 0x78, 0x65, 0xF2, 0x98, 0xFF, 0xDA, 0xF2, 0x87,
0xFF, 0xEA, 0xF2, 0x87, 0xF2, 0x87, 0xF2, 0x87, 0xF2, 0x87, 0xF2, 0x87, 0xF2, 0xBD, 0xF0, 0x37,
0xF0, 0x3F, 0xF0, 0x3F, 0xF0, 0x3F, 0xF0, 0x3F, 0xF0, 0x3F, 0xF0, 0x3F, 0xF0, 0x3F, 0xF0, 0x3F,
0xF0, 0x3F, 0xF0, 0x3F, 0xF0, 0x3F, 0xFF, 0xFD, 0xA0, 0x15, 0xB3, 0x21, 0x73, 0xFD, 0x21, 0x6B,
0xFD, 0x21, 0x6C, 0xFD, 0x57, 0x2E, 0xC4, 0xC5, 0x62, 0x63, 0x64, 0x66, 0x67, 0x68, 0x6A, 0x6B,
0x6C, 0x6D, 0x6E, 0x70, 0x72, 0x73, 0x74, 0x76, 0x77, 0x78, 0x7A, 0x6F, 0xEF, 0xE5, 0xF6, 0x2E,
0xF6, 0x32, 0xEF, 0xED, 0xEF, 0xED, 0xEF, 0xED, 0xEF, 0xED, 0xEF, 0xED, 0xEF, 0xED, 0xEF, 0xED,
0xEF, 0xED, 0xEF, 0xED, 0xEF, 0xED, 0xEF, 0xED, 0xEF, 0xED, 0xEF, 0xED, 0xEF, 0xED, 0xEF, 0xED,
0xEF, 0xED, 0xEF, 0xED, 0xEF, 0xED, 0xEF, 0xED, 0xFF, 0xFD, 0x56, 0x2E, 0xC4, 0xC5, 0x62, 0x63,
0x64, 0x66, 0x67, 0x68, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x70, 0x72, 0x73, 0x74, 0x76, 0x77, 0x78,
0x7A, 0xEF, 0x9F, 0xF5, 0xE8, 0xF5, 0xEC, 0xEF, 0xA7, 0xEF, 0xA7, 0xEF, 0xA7, 0xEF, 0xA7, 0xEF,
0xA7, 0xEF, 0xA7, 0xEF, 0xA7, 0xEF, 0xA7, 0xEF, 0xA7, 0xEF, 0xA7, 0xEF, 0xA7, 0xEF, 0xA7, 0xEF,
0xA7, 0xEF, 0xA7, 0xEF, 0xA7, 0xEF, 0xA7, 0xEF, 0xA7, 0xEF, 0xA7, 0xEF, 0xA7, 0xA0, 0x00, 0xD1,
0xC6, 0x02, 0x61, 0x75, 0x79, 0x61, 0x65, 0x69, 0x6F, 0xFF, 0xFD, 0xFF, 0xFD, 0xF0, 0x33, 0xF0,
0x33, 0xF0, 0x33, 0xF0, 0x33, 0xA0, 0x04, 0xF2, 0x21, 0x63, 0xFD, 0x21, 0x61, 0xFD, 0x21, 0x7A,
0xFD, 0xA0, 0x04, 0xE2, 0x21, 0x7A, 0xFD, 0xA1, 0x00, 0xD1, 0x73, 0xFD, 0xC7, 0x02, 0x61, 0x72,
0x75, 0x79, 0x61, 0x65, 0x69, 0x6F, 0xFF, 0xF2, 0xFF, 0xFB, 0xFF, 0xD1, 0xF0, 0x07, 0xF0, 0x07,
0xF0, 0x07, 0xF0, 0x07, 0x22, 0x85, 0x99, 0xB9, 0xB9, 0x21, 0xB3, 0xB4, 0x41, 0x6B, 0xE9, 0xB1,
0x21, 0x63, 0xFC, 0x21, 0x75, 0xFD, 0x21, 0x72, 0xFD, 0x21, 0x62, 0xFD, 0x21, 0x73, 0xFD, 0x41,
0x63, 0xE9, 0x9E, 0x21, 0x75, 0xFC, 0x21, 0x72, 0xFD, 0x21, 0x62, 0xFD, 0x22, 0x6E, 0x73, 0xF0,
0xFD, 0xC9, 0x02, 0x61, 0xC4, 0xC3, 0x61, 0x65, 0x69, 0x6F, 0x75, 0x79, 0x6E, 0xFF, 0xD3, 0xFF,
0xD8, 0xFF, 0x8C, 0xFF, 0x8C, 0xFF, 0x8C, 0xFF, 0x8C, 0xFF, 0x8C, 0xFF, 0x8C, 0xFF, 0xFB, 0x41,
0x72, 0xDB, 0xBC, 0x21, 0x74, 0xFC, 0x21, 0x73, 0xFD, 0x21, 0x69, 0xFD, 0x21, 0x6D, 0xFD, 0x21,
0x68, 0xFD, 0x41, 0x65, 0xFC, 0xD3, 0x21, 0x73, 0xFC, 0x21, 0x66, 0xFD, 0xC8, 0x02, 0x61, 0x79,
0x61, 0x63, 0x65, 0x66, 0x69, 0x6F, 0x75, 0xFF, 0x51, 0xEF, 0x87, 0xFF, 0xF3, 0xEF, 0x87, 0xFF,
0xFD, 0xEF, 0x87, 0xEF, 0x87, 0xEF, 0x87, 0xC6, 0x02, 0x61, 0x79, 0x61, 0x65, 0x69, 0x6F, 0x75,
0xFF, 0x36, 0xEF, 0x6C, 0xEF, 0x6C, 0xEF, 0x6C, 0xEF, 0x6C, 0xEF, 0x6C, 0xC5, 0x02, 0x61, 0x61,
0x65, 0x69, 0x6F, 0x75, 0xEF, 0x57, 0xEF, 0x57, 0xEF, 0x57, 0xEF, 0x57, 0xEF, 0x57, 0x5D, 0x2E,
0xC4, 0xC3, 0xC5, 0x62, 0x63, 0x64, 0x66, 0x67, 0x68, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x70, 0x72,
0x73, 0x74, 0x77, 0x7A, 0x76, 0x78, 0x61, 0x65, 0x69, 0x6F, 0x75, 0x79, 0xEE, 0x19, 0xEE, 0xB6,
0xEE, 0xCC, 0xF0, 0xA4, 0xF1, 0xC1, 0xF2, 0xE7, 0xF4, 0x6B, 0xF5, 0x1C, 0xF5, 0xC4, 0xF6, 0x3C,
0xF6, 0xA5, 0xF7, 0x4A, 0xF7, 0xC5, 0xF8, 0xB7, 0xF9, 0x28, 0xFA, 0x5D, 0xFB, 0x2E, 0xFC, 0xA0,
0xFD, 0x2E, 0xFD, 0xC8, 0xFE, 0x34, 0xFE, 0x86, 0xFE, 0xCC, 0xFF, 0x12, 0xFF, 0x3E, 0xFF, 0x83,
0xFF, 0xBE, 0xFF, 0xD9, 0xFF, 0xEE,
};
constexpr SerializedHyphenationPatterns pl_patterns = {
0x3C4Eu,
pl_trie_data,
sizeof(pl_trie_data),
};
@@ -3,7 +3,7 @@
#include <cstddef>
#include <cstdint>
#include "../SerializedHyphenationTrie.h"
#include "Epub/hyphenation/SerializedHyphenationTrie.h"
// Auto-generated by generate_hyphenation_trie.py. Do not edit manually.
alignas(4) constexpr uint8_t ru_trie_data[] = {
File diff suppressed because it is too large Load Diff
@@ -3,7 +3,7 @@
#include <cstddef>
#include <cstdint>
#include "../SerializedHyphenationTrie.h"
#include "Epub/hyphenation/SerializedHyphenationTrie.h"
// Auto-generated by generate_hyphenation_trie.py. Do not edit manually.
alignas(4) constexpr uint8_t uk_trie_data[] = {
@@ -8,13 +8,15 @@
#include <XmlParserUtils.h>
#include <expat.h>
#include <algorithm>
#include <iterator>
#include <new>
#include "../../Epub.h"
#include "../Page.h"
#include "../converters/ImageDecoderFactory.h"
#include "../converters/ImageToFramebufferDecoder.h"
#include "../htmlEntities.h"
#include "Epub.h"
#include "Epub/Page.h"
#include "Epub/converters/ImageDecoderFactory.h"
#include "Epub/converters/ImageToFramebufferDecoder.h"
#include "Epub/htmlEntities.h"
// Minimum file size (in bytes) to show indexing popup - smaller chapters don't benefit from it
constexpr size_t MIN_SIZE_FOR_POPUP = 10 * 1024; // 10KB
@@ -145,6 +147,68 @@ void ChapterHtmlSlimParser::startNewTextBlock(const BlockStyle& blockStyle) {
wordsExtractedInBlock = 0;
}
void ChapterHtmlSlimParser::emitHorizontalRule(const BlockStyle& blockStyle) {
if (partWordBufferIndex > 0) {
flushPartWordBuffer();
}
if (currentTextBlock) {
const BlockStyle parentBlockStyle = currentTextBlock->getBlockStyle();
startNewTextBlock(parentBlockStyle);
}
if (!currentPage) {
currentPage.reset(new (std::nothrow) Page());
if (!currentPage) {
LOG_ERR("EHP", "Failed to create page for horizontal rule");
return;
}
currentPageNextY = 0;
}
const int16_t lineHeight = static_cast<int16_t>(renderer.getLineHeight(fontId) * lineCompression + 0.5f);
const int16_t defaultVerticalSpacing = static_cast<int16_t>(lineHeight / 2);
const int16_t topSpacing =
static_cast<int16_t>((blockStyle.marginTop > 0 ? blockStyle.marginTop : defaultVerticalSpacing) +
(blockStyle.paddingTop > 0 ? blockStyle.paddingTop : 0));
const int16_t bottomSpacing =
static_cast<int16_t>((blockStyle.marginBottom > 0 ? blockStyle.marginBottom : defaultVerticalSpacing) +
(blockStyle.paddingBottom > 0 ? blockStyle.paddingBottom : 0));
constexpr uint8_t ruleThickness = 2;
const int16_t availableWidth =
std::max<int16_t>(1, static_cast<int16_t>(viewportWidth - blockStyle.totalHorizontalInset()));
const int16_t width = std::max<int16_t>(1, static_cast<int16_t>(availableWidth / 4));
const int16_t xPos = static_cast<int16_t>(blockStyle.leftInset() + ((availableWidth - width) / 2));
const int16_t totalHeight = static_cast<int16_t>(topSpacing + ruleThickness + bottomSpacing);
if (!currentPage->elements.empty() && currentPageNextY + totalHeight > viewportHeight) {
completePageFn(std::move(currentPage), xpathParagraphIndex, xpathListItemIndex);
completedPageCount++;
currentPage.reset(new (std::nothrow) Page());
if (!currentPage) {
LOG_ERR("EHP", "Failed to create page after horizontal-rule page break");
return;
}
currentPageNextY = 0;
}
currentPageNextY += topSpacing;
auto pageRule = std::shared_ptr<PageHorizontalRule>(
new (std::nothrow) PageHorizontalRule(width, ruleThickness, xPos, currentPageNextY));
if (!pageRule) {
LOG_ERR("EHP", "Failed to create PageHorizontalRule");
return;
}
currentPage->elements.push_back(pageRule);
currentPageNextY = static_cast<int16_t>(currentPageNextY + ruleThickness + bottomSpacing);
if (!pendingAnchorId.empty()) {
anchorData.push_back({std::move(pendingAnchorId), static_cast<uint16_t>(completedPageCount)});
pendingAnchorId.clear();
}
}
void XMLCALL ChapterHtmlSlimParser::startElement(void* userData, const XML_Char* name, const XML_Char** atts) {
auto* self = static_cast<ChapterHtmlSlimParser*>(userData);
@@ -262,6 +326,11 @@ void XMLCALL ChapterHtmlSlimParser::startElement(void* userData, const XML_Char*
return;
}
if (self->tableDepth == 1 && strcmp(name, "hr") == 0) {
self->depth += 1;
return;
}
if (matches(name, IMAGE_TAGS, std::size(IMAGE_TAGS))) {
std::string src;
std::string alt;
@@ -600,6 +669,25 @@ void XMLCALL ChapterHtmlSlimParser::startElement(void* userData, const XML_Char*
const auto userAlignmentBlockStyle = BlockStyle::fromCssStyle(
cssStyle, emSize, static_cast<CssTextAlign>(self->paragraphAlignment), self->viewportWidth);
if (strcmp(name, "hr") == 0) {
auto hrBlockStyle = BlockStyle::fromCssStyle(cssStyle, emSize, CssTextAlign::Left, self->viewportWidth);
if (!self->embeddedStyle) {
hrBlockStyle.marginLeft = 0;
hrBlockStyle.marginRight = 0;
hrBlockStyle.marginTop = 0;
hrBlockStyle.marginBottom = 0;
hrBlockStyle.paddingLeft = 0;
hrBlockStyle.paddingRight = 0;
hrBlockStyle.paddingTop = 0;
hrBlockStyle.paddingBottom = 0;
hrBlockStyle.textIndentDefined = false;
hrBlockStyle.textIndent = 0;
}
self->emitHorizontalRule(hrBlockStyle);
self->depth += 1;
return;
}
if (matches(name, HEADER_TAGS, std::size(HEADER_TAGS))) {
self->currentCssStyle = cssStyle;
auto headerBlockStyle = BlockStyle::fromCssStyle(cssStyle, emSize, CssTextAlign::Center, self->viewportWidth);
@@ -8,12 +8,12 @@
#include <string>
#include <vector>
#include "../FootnoteEntry.h"
#include "../ParsedText.h"
#include "../blocks/ImageBlock.h"
#include "../blocks/TextBlock.h"
#include "../css/CssParser.h"
#include "../css/CssStyle.h"
#include "Epub/FootnoteEntry.h"
#include "Epub/ParsedText.h"
#include "Epub/blocks/ImageBlock.h"
#include "Epub/blocks/TextBlock.h"
#include "Epub/css/CssParser.h"
#include "Epub/css/CssStyle.h"
class Page;
class GfxRenderer;
@@ -91,6 +91,7 @@ class ChapterHtmlSlimParser {
void startNewTextBlock(const BlockStyle& blockStyle);
void flushPartWordBuffer();
void makePages();
void emitHorizontalRule(const BlockStyle& blockStyle);
// 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);
+1 -1
View File
@@ -5,7 +5,7 @@
#include <Serialization.h>
#include <XmlParserUtils.h>
#include "../BookMetadataCache.h"
#include "Epub/BookMetadataCache.h"
namespace {
constexpr char MEDIA_TYPE_NCX[] = "application/x-dtbncx+xml";
+1 -1
View File
@@ -4,7 +4,7 @@
#include <Logging.h>
#include <XmlParserUtils.h>
#include "../BookMetadataCache.h"
#include "Epub/BookMetadataCache.h"
bool TocNavParser::setup() {
parser = XML_ParserCreate(nullptr);
+1 -1
View File
@@ -4,7 +4,7 @@
#include <Logging.h>
#include <XmlParserUtils.h>
#include "../BookMetadataCache.h"
#include "Epub/BookMetadataCache.h"
bool TocNcxParser::setup() {
parser = XML_ParserCreate(nullptr);
+83
View File
@@ -1094,6 +1094,89 @@ int GfxRenderer::getScreenHeight() const {
return panelWidth;
}
// Translate a logical rect through rotateCoordinates and take the bounding
// box of its four corners on the physical panel. Output coords are inclusive
// and clamped. Returns false if the rect ends up fully off-panel.
static bool logicalRectToPhysicalBounds(GfxRenderer::Orientation orientation, int lx, int ly, int lw, int lh,
uint16_t panelWidth, uint16_t panelHeight, int* outX0, int* outY0, int* outX1,
int* outY1) {
if (lw <= 0 || lh <= 0) return false;
int minX = INT32_MAX;
int minY = INT32_MAX;
int maxX = INT32_MIN;
int maxY = INT32_MIN;
const int corners[4][2] = {{lx, ly}, {lx + lw - 1, ly}, {lx, ly + lh - 1}, {lx + lw - 1, ly + lh - 1}};
for (auto& c : corners) {
int phyX;
int phyY;
rotateCoordinates(orientation, c[0], c[1], &phyX, &phyY, panelWidth, panelHeight);
if (phyX < minX) minX = phyX;
if (phyY < minY) minY = phyY;
if (phyX > maxX) maxX = phyX;
if (phyY > maxY) maxY = phyY;
}
if (minX < 0) minX = 0;
if (minY < 0) minY = 0;
if (maxX >= panelWidth) maxX = panelWidth - 1;
if (maxY >= panelHeight) maxY = panelHeight - 1;
if (minX > maxX || minY > maxY) return false;
*outX0 = minX;
*outY0 = minY;
*outX1 = maxX;
*outY1 = maxY;
return true;
}
size_t GfxRenderer::getRegionByteSize(int lx, int ly, int lw, int lh) const {
int x0, y0, x1, y1;
if (!logicalRectToPhysicalBounds(orientation, lx, ly, lw, lh, panelWidth, panelHeight, &x0, &y0, &x1, &y1)) {
return 0;
}
// x bounds are in pixels; widen to byte boundaries on either side so per-row
// memcpy stays byte-aligned even when the logical rect doesn't.
const int byteX0 = x0 / 8;
const int byteX1 = x1 / 8;
const int bytesPerRow = byteX1 - byteX0 + 1;
const int rowCount = y1 - y0 + 1;
return static_cast<size_t>(bytesPerRow) * static_cast<size_t>(rowCount);
}
bool GfxRenderer::copyRegionToBuffer(int lx, int ly, int lw, int lh, uint8_t* buf, size_t bufSize) const {
int x0, y0, x1, y1;
if (!logicalRectToPhysicalBounds(orientation, lx, ly, lw, lh, panelWidth, panelHeight, &x0, &y0, &x1, &y1)) {
return false;
}
const int byteX0 = x0 / 8;
const int byteX1 = x1 / 8;
const int bytesPerRow = byteX1 - byteX0 + 1;
const int rowCount = y1 - y0 + 1;
const size_t needed = static_cast<size_t>(bytesPerRow) * static_cast<size_t>(rowCount);
if (bufSize < needed || !frameBuffer || !buf) return false;
for (int row = 0; row < rowCount; row++) {
const uint8_t* src = frameBuffer + (y0 + row) * panelWidthBytes + byteX0;
memcpy(buf + row * bytesPerRow, src, bytesPerRow);
}
return true;
}
bool GfxRenderer::copyBufferToRegion(int lx, int ly, int lw, int lh, const uint8_t* buf, size_t bufSize) const {
int x0, y0, x1, y1;
if (!logicalRectToPhysicalBounds(orientation, lx, ly, lw, lh, panelWidth, panelHeight, &x0, &y0, &x1, &y1)) {
return false;
}
const int byteX0 = x0 / 8;
const int byteX1 = x1 / 8;
const int bytesPerRow = byteX1 - byteX0 + 1;
const int rowCount = y1 - y0 + 1;
const size_t needed = static_cast<size_t>(bytesPerRow) * static_cast<size_t>(rowCount);
if (bufSize < needed || !frameBuffer || !buf) return false;
for (int row = 0; row < rowCount; row++) {
uint8_t* dst = frameBuffer + (y0 + row) * panelWidthBytes + byteX0;
memcpy(dst, buf + row * bytesPerRow, bytesPerRow);
}
return true;
}
int GfxRenderer::getSpaceWidth(const int fontId, const EpdFontFamily::Style style) const {
// Advance table fast-path for SD card fonts during layout
auto sdIt = sdCardFonts_.find(fontId);
+12
View File
@@ -185,4 +185,16 @@ class GfxRenderer {
uint16_t getDisplayWidth() const { return panelWidth; }
uint16_t getDisplayHeight() const { return panelHeight; }
uint16_t getDisplayWidthBytes() const { return panelWidthBytes; }
// Region cache: take a logical (orientation-aware) rect, hit the framebuffer
// bytes that the rect can have touched, and pump them in or out of a caller-
// supplied buffer. Used by HomeActivity to snapshot just the cover tile
// (~16 KB in Portrait) instead of cloning the entire 48 KB framebuffer.
//
// getRegionByteSize: required buffer length for the rect at current orientation.
// copyRegionToBuffer / copyBufferToRegion: false if `bufSize` is smaller than that.
size_t getRegionByteSize(int logicalX, int logicalY, int logicalW, int logicalH) const;
bool copyRegionToBuffer(int logicalX, int logicalY, int logicalW, int logicalH, uint8_t* buf, size_t bufSize) const;
bool copyBufferToRegion(int logicalX, int logicalY, int logicalW, int logicalH, const uint8_t* buf,
size_t bufSize) const;
};
+8
View File
@@ -69,6 +69,7 @@ STR_TEXT_AA: "Згладжванне тэксту"
STR_SHORT_PWR_BTN: "Кароткае націсканне PWR"
STR_ORIENTATION: "Арыентацыя чытання"
STR_SIDE_BTN_LAYOUT: "Бакавыя кнопкі"
STR_FRONT_BTN_FOLLOW_ORIENTATION: "Арыентаваць пярэднія кнопкі"
STR_LONG_PRESS_SKIP: "Доўгае націсканне - змена раздзела"
STR_FONT_FAMILY: "Шрыфт чытання"
STR_FONT_SIZE: "Памер шрыфту інтэрфейсу"
@@ -143,6 +144,7 @@ STR_ALIGN_LEFT: "Па левым краі"
STR_CENTER: "Па цэнтры"
STR_ALIGN_RIGHT: "Па правым краі"
STR_MIN_1: "1 хв"
STR_MIN_3: "3 хв"
STR_MIN_5: "5 хв"
STR_MIN_10: "10 хв"
STR_MIN_15: "15 хв"
@@ -204,10 +206,14 @@ STR_THEME_CLASSIC: "Класічная"
STR_THEME_LYRA: "Lyra"
STR_THEME_LYRA_EXTENDED: "Lyra Extended"
STR_SUNLIGHT_FADING_FIX: "Кампенсацыя выцвітання"
STR_QUICK_RESUME_TIMEOUT: "Хуткае узнаўленне пасля таймаўту"
STR_AFTER_TIMEOUT: "Пасля таймаўту"
STR_REMAP_FRONT_BUTTONS: "Пераназначыць пярэднія кнопкі"
STR_OPDS_BROWSER: "OPDS браўзер"
STR_COVER_CUSTOM: "Вокладка + Свой"
STR_QUICK_RESUME: "Хуткае узнаўленне"
STR_MENU_RECENT_BOOKS: "Нядаўнія кнігі"
STR_REMOVE_FROM_RECENTS: "Выдаліць з нядаўніх кніг?"
STR_NO_RECENT_BOOKS: "Няма нядаўніх кніг"
STR_CALIBRE_DESC: "Выкарыстоўваць бесправадную перадачу Calibre"
STR_FORGET_AND_REMOVE: "Забыць сетку і выдаліць захаваны пароль?"
@@ -267,6 +273,8 @@ STR_IMAGES_DISPLAY: "Паказваць"
STR_IMAGES_PLACEHOLDER: "Запаўняльнік"
STR_IMAGES_SUPPRESS: "Хаваць"
STR_SHOW_HIDDEN_FILES: "Паказваць схаваныя файлы"
STR_REMOVE_READ_FROM_RECENTS: "Ачышчаць прачытаныя кнігі са спісу нядаўніх"
STR_MOVE_FINISHED_TO_READ: "Перамяшчаць прачытаныя кнігі ў тэчку Read"
STR_SELECTED: "Выбрана"
STR_SHOW: "Паказаць"
STR_HIDE: "Схаваць"
+8
View File
@@ -73,6 +73,7 @@ STR_IMAGES_SUPPRESS: "Suprimir"
STR_SHORT_PWR_BTN: "Clic curt del botó d'engegada"
STR_ORIENTATION: "Orientació de lectura"
STR_SIDE_BTN_LAYOUT: "Disposició botons laterals"
STR_FRONT_BTN_FOLLOW_ORIENTATION: "Orientar botons frontals"
STR_LONG_PRESS_BEHAVIOR: "Comportament de prémer llargament el botó"
STR_LONG_PRESS_BEHAVIOR_OFF: "Desactivat"
STR_LONG_PRESS_BEHAVIOR_SKIP: "Saltar capítols"
@@ -85,6 +86,8 @@ STR_PARA_ALIGNMENT: "Alineació de paràgrafs del lector"
STR_HYPHENATION: "Partició de mots"
STR_TIME_TO_SLEEP: "Temps per entrar en repòs"
STR_SHOW_HIDDEN_FILES: "Mostra fitxers ocults"
STR_REMOVE_READ_FROM_RECENTS: "Esborra els llibres llegits de la llista de recents"
STR_MOVE_FINISHED_TO_READ: "Mou els llibres acabats a la carpeta Read"
STR_REFRESH_FREQ: "Freqüència de refresc"
STR_KOREADER_SYNC: "Sincronització del KOReader"
STR_CHECK_UPDATES: "Comprova si hi ha actualitzacions"
@@ -151,6 +154,7 @@ STR_ALIGN_LEFT: "Esquerra"
STR_CENTER: "Centre"
STR_ALIGN_RIGHT: "Dreta"
STR_MIN_1: "1 min"
STR_MIN_3: "3 min"
STR_MIN_5: "5 min"
STR_MIN_10: "10 min"
STR_MIN_15: "15 min"
@@ -230,10 +234,14 @@ STR_THEME_CLASSIC: "Clàssic"
STR_THEME_LYRA: "Lyra"
STR_THEME_LYRA_EXTENDED: "Lyra Ampliat"
STR_SUNLIGHT_FADING_FIX: "Correcció de l'esvaïment pel sol"
STR_QUICK_RESUME_TIMEOUT: "Represa ràpida després del temps"
STR_AFTER_TIMEOUT: "Després del temps"
STR_REMAP_FRONT_BUTTONS: "Reassigna els botons frontals"
STR_OPDS_BROWSER: "Navegador OPDS"
STR_COVER_CUSTOM: "Portada + Personalitzat"
STR_QUICK_RESUME: "Represa ràpida"
STR_MENU_RECENT_BOOKS: "Llibres recents"
STR_REMOVE_FROM_RECENTS: "Voleu suprimir-lo de Llibres recents?"
STR_NO_RECENT_BOOKS: "No hi ha llibres recents"
STR_CALIBRE_DESC: "Usa les transferències sense fils de Calibre"
STR_FORGET_AND_REMOVE: "Voleu suprimir la contrasenya desada?"
+16 -8
View File
@@ -24,7 +24,7 @@ STR_EMPTY_FILE: "Prázdný soubor"
STR_OUT_OF_BOUNDS: "Mimo hranice"
STR_LOADING: "Načítání..."
STR_LOADING_POPUP: "Načítání"
STR_WIFI_NETWORKS: "WiFi sítě"
STR_WIFI_NETWORKS: "Wi-Fi sítě"
STR_NO_NETWORKS: "Žádné sítě nenalezeny"
STR_NETWORKS_FOUND: "Nalezeno %zu sítí"
STR_SCANNING: "Skenování..."
@@ -36,11 +36,11 @@ STR_SAVE_PASSWORD: "Uložit heslo pro příště?"
STR_PRESS_OK_SCAN: "Stiskněte OK pro přeskenování"
STR_JOIN_NETWORK: "Připojit se k síti"
STR_CREATE_HOTSPOT: "Vytvořit hotspot"
STR_JOIN_DESC: "Připojit se k existující síti WiFi"
STR_HOTSPOT_DESC: "Vytvořit síť WiFi, ke které se mohou připojit ostatní"
STR_JOIN_DESC: "Připojit se k existující síti Wi-Fi"
STR_HOTSPOT_DESC: "Vytvořit síť Wi-Fi, ke které se mohou připojit ostatní"
STR_STARTING_HOTSPOT: "Spouštění hotspotu..."
STR_HOTSPOT_MODE: "Režim hotspotu"
STR_CONNECT_WIFI_HINT: "Připojte své zařízení k této síti WiFi"
STR_CONNECT_WIFI_HINT: "Připojte své zařízení k této síti Wi-Fi"
STR_OPEN_URL_HINT: "Otevřete tuto URL ve svém prohlížeči"
STR_OR_HTTP_PREFIX: "nebo http://"
STR_SCAN_QR_HINT: "nebo naskenujte QR kód telefonem:"
@@ -48,13 +48,13 @@ STR_CALIBRE_WIRELESS: "Calibre Wireless"
STR_CALIBRE_WEB_URL: "OPDS URL"
STR_NETWORK_LEGEND: "* = Šifrováno | + = Uloženo"
STR_MAC_ADDRESS: "MAC adresa:"
STR_CHECKING_WIFI: "Kontrola WiFi..."
STR_ENTER_WIFI_PASSWORD: "Zadejte heslo WiFi"
STR_CHECKING_WIFI: "Kontrola Wi-Fi..."
STR_ENTER_WIFI_PASSWORD: "Zadejte heslo Wi-Fi"
STR_TO_PREFIX: "pro"
STR_CALIBRE_RECEIVING: "Příjem:"
STR_CALIBRE_RECEIVED: "Přijato:"
STR_CALIBRE_INSTRUCTION_1: "1) Nainstalujte plugin CrossPoint Reader"
STR_CALIBRE_INSTRUCTION_2: "2) Buďte ve stejné síti WiFi"
STR_CALIBRE_INSTRUCTION_2: "2) Buďte ve stejné síti Wi-Fi"
STR_CALIBRE_INSTRUCTION_3: "3) V Calibre: „Odeslat do zařízení“"
STR_CALIBRE_INSTRUCTION_4: "„Při odesílání ponechat tuto obrazovku otevřenou“"
STR_CAT_DISPLAY: "Displej"
@@ -69,6 +69,7 @@ STR_TEXT_AA: "Vyhlazování textu"
STR_SHORT_PWR_BTN: "Krátké stisknutí tlačítka napájení"
STR_ORIENTATION: "Orientace čtení"
STR_SIDE_BTN_LAYOUT: "Rozvržení bočních tlačítek (čtečka)"
STR_FRONT_BTN_FOLLOW_ORIENTATION: "Orientovat přední tlačítka"
STR_LONG_PRESS_BEHAVIOR: "Chování při dlouhém stisknutí tlačítka"
STR_LONG_PRESS_BEHAVIOR_OFF: "VYP"
STR_LONG_PRESS_BEHAVIOR_SKIP: "Přeskočení kapitoly"
@@ -80,6 +81,8 @@ STR_SCREEN_MARGIN: "Okraj obrazovky čtečky"
STR_PARA_ALIGNMENT: "Zarovnání odstavců čtečky"
STR_HYPHENATION: "Dělení slov"
STR_TIME_TO_SLEEP: "Čas do uspání"
STR_REMOVE_READ_FROM_RECENTS: "Vymazat přečtené knihy ze seznamu nedávných"
STR_MOVE_FINISHED_TO_READ: "Přesunout přečtené knihy do složky Read"
STR_REFRESH_FREQ: "Frekvence obnovení"
STR_KOREADER_SYNC: "KOReaderu Sync"
STR_CHECK_UPDATES: "Zkontrolovat aktualizace"
@@ -95,7 +98,7 @@ STR_KOREADER_PASSWORD: "Heslo KOReaderu"
STR_FILENAME: "Název souboru"
STR_BINARY: "Binární"
STR_SET_CREDENTIALS_FIRST: "Nastavte přihlašovací údaje"
STR_WIFI_CONN_FAILED: "Připojení k WiFi selhalo"
STR_WIFI_CONN_FAILED: "Připojení k Wi-Fi selhalo"
STR_AUTHENTICATING: "Ověřování..."
STR_AUTH_SUCCESS: "Úspěšné ověření!"
STR_KOREADER_AUTH: "Ověření KOReaderu"
@@ -146,6 +149,7 @@ STR_ALIGN_LEFT: "Vlevo"
STR_CENTER: "Na střed"
STR_ALIGN_RIGHT: "Vpravo"
STR_MIN_1: "1 min"
STR_MIN_3: "3 min"
STR_MIN_5: "5 min"
STR_MIN_10: "10 min"
STR_MIN_15: "15 min"
@@ -208,10 +212,14 @@ STR_THEME_LYRA: "Lyra"
STR_THEME_ROUNDEDRAFF: "RoundedRaff"
STR_THEME_LYRA_EXTENDED: "Lyra Extended"
STR_SUNLIGHT_FADING_FIX: "Oprava blednutí na slunci"
STR_QUICK_RESUME_TIMEOUT: "Rychlé navázání po vypršení"
STR_AFTER_TIMEOUT: "Po vypršení"
STR_REMAP_FRONT_BUTTONS: "Přemapovat přední tlačítka"
STR_OPDS_BROWSER: "Prohlížeč OPDS"
STR_COVER_CUSTOM: "Obálka + Vlastní"
STR_QUICK_RESUME: "Rychlé navázání"
STR_MENU_RECENT_BOOKS: "Nedávné knihy"
STR_REMOVE_FROM_RECENTS: "Odebrat z nedávných knih?"
STR_NO_RECENT_BOOKS: "Žádné nedávné knihy"
STR_CALIBRE_DESC: "Používat přenosy bezdrátových zařízení Calibre"
STR_FORGET_AND_REMOVE: "Zapomenout síť a odstranit uložené heslo?"
+15 -7
View File
@@ -36,11 +36,11 @@ STR_SAVE_PASSWORD: "Gem adgangskode til næste gang?"
STR_PRESS_OK_SCAN: "Tryk OK for at scanne igen"
STR_JOIN_NETWORK: "Tilslut netværk"
STR_CREATE_HOTSPOT: "Opret Hotspot"
STR_JOIN_DESC: "Opret forbindelse til et eksisterende WiFi-netværk"
STR_HOTSPOT_DESC: "Opret et WiFi-netværk andre kan tilslutte sig"
STR_JOIN_DESC: "Opret forbindelse til et eksisterende Wi-Fi-netværk"
STR_HOTSPOT_DESC: "Opret et Wi-Fi-netværk andre kan tilslutte sig"
STR_STARTING_HOTSPOT: "Starter Hotspot..."
STR_HOTSPOT_MODE: "Hotspot-tilstand"
STR_CONNECT_WIFI_HINT: "Opret forbindelse fra din enhed til dette WiFi-netværk"
STR_CONNECT_WIFI_HINT: "Opret forbindelse fra din enhed til dette Wi-Fi-netværk"
STR_OPEN_URL_HINT: "Åbn denne URL i din browser"
STR_OR_HTTP_PREFIX: "eller http://"
STR_SCAN_QR_HINT: "eller scan QR-kode med din telefon:"
@@ -48,13 +48,13 @@ STR_CALIBRE_WIRELESS: "Calibre Wireless"
STR_CALIBRE_WEB_URL: "OPDS URL"
STR_NETWORK_LEGEND: "* = Krypteret | + = Gemt"
STR_MAC_ADDRESS: "MAC-adresse:"
STR_CHECKING_WIFI: "Tjekker WiFi..."
STR_ENTER_WIFI_PASSWORD: "Indtast WiFi-adgangskode"
STR_CHECKING_WIFI: "Tjekker Wi-Fi..."
STR_ENTER_WIFI_PASSWORD: "Indtast Wi-Fi-adgangskode"
STR_TO_PREFIX: "til "
STR_CALIBRE_RECEIVING: "Modtager: "
STR_CALIBRE_RECEIVED: "Modtaget: "
STR_CALIBRE_INSTRUCTION_1: "1) Installer CrossPoint Reader-plugin"
STR_CALIBRE_INSTRUCTION_2: "2) Vær på det samme WiFi-netværk"
STR_CALIBRE_INSTRUCTION_2: "2) Vær på det samme Wi-Fi-netværk"
STR_CALIBRE_INSTRUCTION_3: "3) I Calibre: \"Send til enhed\""
STR_CALIBRE_INSTRUCTION_4: "\"Hold denne skærm åben under afsendelse\""
STR_CAT_DISPLAY: "Skærm"
@@ -73,6 +73,7 @@ STR_IMAGES_SUPPRESS: "Skjul"
STR_SHORT_PWR_BTN: "Kort tryk på tænd/sluk-knap"
STR_ORIENTATION: "Læseretning"
STR_SIDE_BTN_LAYOUT: "Knaplayout på siden (læser)"
STR_FRONT_BTN_FOLLOW_ORIENTATION: "Orientér forreste knapper"
STR_LONG_PRESS_BEHAVIOR: "Comportamiento al mantener pulsado el botón"
STR_LONG_PRESS_BEHAVIOR_OFF: "Desactivado"
STR_LONG_PRESS_BEHAVIOR_SKIP: "Saltar capítulo"
@@ -85,6 +86,8 @@ STR_PARA_ALIGNMENT: "Afsnitsjustering"
STR_HYPHENATION: "Orddeling"
STR_TIME_TO_SLEEP: "Tid til hvile"
STR_SHOW_HIDDEN_FILES: "Vis skjulte filer"
STR_REMOVE_READ_FROM_RECENTS: "Ryd læste bøger fra listen Seneste"
STR_MOVE_FINISHED_TO_READ: "Flyt læste bøger til Read-mappen"
STR_REFRESH_FREQ: "Opdateringsfrekvens"
STR_KOREADER_SYNC: "KOReader Sync"
STR_CHECK_UPDATES: "Søg efter opdateringer"
@@ -100,7 +103,7 @@ STR_KOREADER_PASSWORD: "KOReader adgangskode"
STR_FILENAME: "Filnavn"
STR_BINARY: "Binær"
STR_SET_CREDENTIALS_FIRST: "Angiv legitimationsoplysninger først"
STR_WIFI_CONN_FAILED: "WiFi-forbindelsen mislykkedes"
STR_WIFI_CONN_FAILED: "Wi-Fi-forbindelsen mislykkedes"
STR_AUTHENTICATING: "Godkender..."
STR_AUTH_SUCCESS: "Godkendt!"
STR_KOREADER_AUTH: "KOReader-godkendelse"
@@ -151,6 +154,7 @@ STR_ALIGN_LEFT: "Venstre"
STR_CENTER: "Centreret"
STR_ALIGN_RIGHT: "Højre"
STR_MIN_1: "1 min"
STR_MIN_3: "3 min"
STR_MIN_5: "5 min"
STR_MIN_10: "10 min"
STR_MIN_15: "15 min"
@@ -230,10 +234,14 @@ STR_THEME_CLASSIC: "Klassisk"
STR_THEME_LYRA: "Lyra"
STR_THEME_LYRA_EXTENDED: "Lyra Extended"
STR_SUNLIGHT_FADING_FIX: "Sollysfading-rettelse"
STR_QUICK_RESUME_TIMEOUT: "Hurtig genoptagelse ved timeout"
STR_AFTER_TIMEOUT: "Efter timeout"
STR_REMAP_FRONT_BUTTONS: "Omtildel frontknapper"
STR_OPDS_BROWSER: "OPDS Browser"
STR_COVER_CUSTOM: "Omslag + Brugerdefineret"
STR_QUICK_RESUME: "Hurtig genoptagelse"
STR_MENU_RECENT_BOOKS: "Seneste bøger"
STR_REMOVE_FROM_RECENTS: "Fjern fra Seneste bøger?"
STR_NO_RECENT_BOOKS: "Ingen seneste bøger"
STR_CALIBRE_DESC: "Brug Calibre trådløs enhedsoverførelse"
STR_FORGET_AND_REMOVE: "Glem netværk og fjern gemt adgangskode?"
+17 -9
View File
@@ -24,7 +24,7 @@ STR_EMPTY_FILE: "Leeg bestand"
STR_OUT_OF_BOUNDS: "Buiten bereik"
STR_LOADING: "Laden..."
STR_LOADING_POPUP: "Laden"
STR_WIFI_NETWORKS: "Wifi-netwerken"
STR_WIFI_NETWORKS: "Wi-Fi-netwerken"
STR_NO_NETWORKS: "Geen netwerken gevonden"
STR_NETWORKS_FOUND: "%zu netwerken gevonden"
STR_SCANNING: "Scannen..."
@@ -36,11 +36,11 @@ STR_SAVE_PASSWORD: "Wachtwoord opslaan voor volgende keer?"
STR_PRESS_OK_SCAN: "Druk op OK om opnieuw te scannen"
STR_JOIN_NETWORK: "Verbind met netwerk"
STR_CREATE_HOTSPOT: "Hotspot maken"
STR_JOIN_DESC: "Verbind met een bestaand wifi-netwerk"
STR_HOTSPOT_DESC: "Maak een wifi-netwerk waar anderen mee kunnen verbinden"
STR_JOIN_DESC: "Verbind met een bestaand Wi-Fi-netwerk"
STR_HOTSPOT_DESC: "Maak een Wi-Fi-netwerk waar anderen mee kunnen verbinden"
STR_STARTING_HOTSPOT: "Hotspot starten..."
STR_HOTSPOT_MODE: "Hotspot-modus"
STR_CONNECT_WIFI_HINT: "Verbind je apparaat met dit wifi-netwerk"
STR_CONNECT_WIFI_HINT: "Verbind je apparaat met dit Wi-Fi-netwerk"
STR_OPEN_URL_HINT: "Open deze URL in je browser"
STR_OR_HTTP_PREFIX: "of http://"
STR_SCAN_QR_HINT: "of scan de QR-code met je telefoon:"
@@ -48,13 +48,13 @@ STR_CALIBRE_WIRELESS: "Calibre Wireless"
STR_CALIBRE_WEB_URL: "OPDS URL"
STR_NETWORK_LEGEND: "* = Beveiligd | + = Opgeslagen"
STR_MAC_ADDRESS: "MAC-adres:"
STR_CHECKING_WIFI: "Wifi controleren..."
STR_ENTER_WIFI_PASSWORD: "Voer wifi-wachtwoord in"
STR_CHECKING_WIFI: "Wi-Fi controleren..."
STR_ENTER_WIFI_PASSWORD: "Voer Wi-Fi-wachtwoord in"
STR_TO_PREFIX: "met "
STR_CALIBRE_RECEIVING: "Bezig met ontvangen: "
STR_CALIBRE_RECEIVED: "Ontvangen: "
STR_CALIBRE_INSTRUCTION_1: "1) Installeer CrossPoint Reader plugin"
STR_CALIBRE_INSTRUCTION_2: "2) Gebruik hetzelfde wifi-netwerk"
STR_CALIBRE_INSTRUCTION_2: "2) Gebruik hetzelfde Wi-Fi-netwerk"
STR_CALIBRE_INSTRUCTION_3: "3) In Calibre: \"Send to device\""
STR_CALIBRE_INSTRUCTION_4: "\"Houd dit scherm open tijdens verzenden\""
STR_CAT_DISPLAY: "Scherm"
@@ -73,6 +73,7 @@ STR_IMAGES_SUPPRESS: "Verbergen"
STR_SHORT_PWR_BTN: "Korte klik aan/uit-knop"
STR_ORIENTATION: "Leesstand"
STR_SIDE_BTN_LAYOUT: "Indeling zijknoppen (lezer)"
STR_FRONT_BTN_FOLLOW_ORIENTATION: "Richt voorste knoppen"
STR_LONG_PRESS_BEHAVIOR: "Long-press button behavior"
STR_LONG_PRESS_BEHAVIOR_OFF: "OFF"
STR_LONG_PRESS_BEHAVIOR_SKIP: "Chapter skip"
@@ -85,6 +86,8 @@ STR_PARA_ALIGNMENT: "Uitlijning alinea lezer"
STR_HYPHENATION: "Woordafbreking"
STR_TIME_TO_SLEEP: "Tijd tot slaapstand"
STR_SHOW_HIDDEN_FILES: "Toon verborgen bestanden"
STR_REMOVE_READ_FROM_RECENTS: "Gelezen boeken wissen uit de lijst Recent"
STR_MOVE_FINISHED_TO_READ: "Voltooide boeken naar de Read-map verplaatsen"
STR_REFRESH_FREQ: "Verversingsfrequentie"
STR_KOREADER_SYNC: "KOReader Sync"
STR_CHECK_UPDATES: "Controleren op updates"
@@ -100,7 +103,7 @@ STR_KOREADER_PASSWORD: "KOReader wachtwoord"
STR_FILENAME: "Bestandsnaam"
STR_BINARY: "Binair"
STR_SET_CREDENTIALS_FIRST: "Stel eerst inloggegevens in"
STR_WIFI_CONN_FAILED: "Wifi-verbinding mislukt"
STR_WIFI_CONN_FAILED: "Wi-Fi-verbinding mislukt"
STR_AUTHENTICATING: "Authenticeren..."
STR_AUTH_SUCCESS: "Authenticatie geslaagd!"
STR_KOREADER_AUTH: "KOReader-authenticatie"
@@ -151,6 +154,7 @@ STR_ALIGN_LEFT: "Links"
STR_CENTER: "Centreren"
STR_ALIGN_RIGHT: "Rechts"
STR_MIN_1: "1 min"
STR_MIN_3: "3 min"
STR_MIN_5: "5 min"
STR_MIN_10: "10 min"
STR_MIN_15: "15 min"
@@ -230,10 +234,14 @@ STR_THEME_CLASSIC: "Klassiek"
STR_THEME_LYRA: "Lyra"
STR_THEME_LYRA_EXTENDED: "Lyra Uitgebreid"
STR_SUNLIGHT_FADING_FIX: "Zonlicht vervaging fix"
STR_QUICK_RESUME_TIMEOUT: "Snel hervatten bij timeout"
STR_AFTER_TIMEOUT: "Na timeout"
STR_REMAP_FRONT_BUTTONS: "Knoppen voorzijde wijzigen"
STR_OPDS_BROWSER: "OPDS-browser"
STR_COVER_CUSTOM: "Omslag + Aangepast"
STR_QUICK_RESUME: "Snel hervatten"
STR_MENU_RECENT_BOOKS: "Recente boeken"
STR_REMOVE_FROM_RECENTS: "Verwijderen uit Recente boeken?"
STR_NO_RECENT_BOOKS: "Geen recente boeken"
STR_CALIBRE_DESC: "Gebruik Calibre draadloze overdracht"
STR_FORGET_AND_REMOVE: "Netwerk vergeten en wachtwoord verwijderen?"
@@ -295,4 +303,4 @@ STR_LINK: "[link]"
STR_SCREENSHOT_BUTTON: "Screenshot maken"
STR_AUTO_TURN_ENABLED: "Automatisch omslaan ingeschakeld: "
STR_AUTO_TURN_PAGES_PER_MIN: "Autom. omslaan (pagina's per minuut)"
STR_TILT_PAGE_TURN: "Kantel om te bladeren"
STR_TILT_PAGE_TURN: "Kantel om te bladeren"
+31 -8
View File
@@ -24,7 +24,7 @@ STR_EMPTY_FILE: "Empty file"
STR_OUT_OF_BOUNDS: "Out of bounds"
STR_LOADING: "Loading..."
STR_LOADING_POPUP: "Loading"
STR_WIFI_NETWORKS: "WiFi Networks"
STR_WIFI_NETWORKS: "Wi-Fi Networks"
STR_NO_NETWORKS: "No networks found"
STR_NETWORKS_FOUND: "%zu networks found"
STR_SCANNING: "Scanning..."
@@ -36,11 +36,11 @@ STR_SAVE_PASSWORD: "Save password for next time?"
STR_PRESS_OK_SCAN: "Press OK to scan again"
STR_JOIN_NETWORK: "Join a Network"
STR_CREATE_HOTSPOT: "Create Hotspot"
STR_JOIN_DESC: "Connect to an existing WiFi network"
STR_HOTSPOT_DESC: "Create a WiFi network others can join"
STR_JOIN_DESC: "Connect to an existing Wi-Fi network"
STR_HOTSPOT_DESC: "Create a Wi-Fi network others can join"
STR_STARTING_HOTSPOT: "Starting Hotspot..."
STR_HOTSPOT_MODE: "Hotspot Mode"
STR_CONNECT_WIFI_HINT: "Connect your device to this WiFi network"
STR_CONNECT_WIFI_HINT: "Connect your device to this Wi-Fi network"
STR_OPEN_URL_HINT: "Open this URL in your browser"
STR_OR_HTTP_PREFIX: "or http://"
STR_SCAN_QR_HINT: "or scan QR code with your phone:"
@@ -48,13 +48,13 @@ STR_CALIBRE_WIRELESS: "Calibre Wireless"
STR_CALIBRE_WEB_URL: "OPDS URL"
STR_NETWORK_LEGEND: "* = Encrypted | + = Saved"
STR_MAC_ADDRESS: "MAC address:"
STR_CHECKING_WIFI: "Checking WiFi..."
STR_ENTER_WIFI_PASSWORD: "Enter WiFi Password"
STR_CHECKING_WIFI: "Checking Wi-Fi..."
STR_ENTER_WIFI_PASSWORD: "Enter Wi-Fi password"
STR_TO_PREFIX: "to "
STR_CALIBRE_RECEIVING: "Receiving: "
STR_CALIBRE_RECEIVED: "Received: "
STR_CALIBRE_INSTRUCTION_1: "1) Install CrossPoint Reader plugin"
STR_CALIBRE_INSTRUCTION_2: "2) Be on the same WiFi network"
STR_CALIBRE_INSTRUCTION_2: "2) Be on the same Wi-Fi network"
STR_CALIBRE_INSTRUCTION_3: "3) In Calibre: \"Send to device\""
STR_CALIBRE_INSTRUCTION_4: "\"Keep this screen open while sending\""
STR_CAT_DISPLAY: "Display"
@@ -62,6 +62,8 @@ STR_CAT_READER: "Reader"
STR_CAT_CONTROLS: "Controls"
STR_CAT_SYSTEM: "System"
STR_SLEEP_SCREEN: "Sleep Screen"
STR_QUICK_RESUME_TIMEOUT: "Quick Resume on Timeout"
STR_AFTER_TIMEOUT: "After Timeout"
STR_SLEEP_COVER_MODE: "Sleep Screen Cover Mode"
STR_HIDE_BATTERY: "Hide Battery %"
STR_EXTRA_SPACING: "Extra Paragraph Spacing"
@@ -73,6 +75,7 @@ STR_IMAGES_SUPPRESS: "Suppress"
STR_SHORT_PWR_BTN: "Short Power Button Click"
STR_ORIENTATION: "Reading Orientation"
STR_SIDE_BTN_LAYOUT: "Side Button Layout (reader)"
STR_FRONT_BTN_FOLLOW_ORIENTATION: "Orient front buttons"
STR_LONG_PRESS_BEHAVIOR: "Long-press button behavior"
STR_LONG_PRESS_BEHAVIOR_OFF: "OFF"
STR_LONG_PRESS_BEHAVIOR_SKIP: "Chapter skip"
@@ -85,6 +88,8 @@ STR_PARA_ALIGNMENT: "Reader Paragraph Alignment"
STR_HYPHENATION: "Hyphenation"
STR_TIME_TO_SLEEP: "Time to Sleep"
STR_SHOW_HIDDEN_FILES: "Show Hidden Files"
STR_REMOVE_READ_FROM_RECENTS: "Clear Read Books from Recent List"
STR_MOVE_FINISHED_TO_READ: "Move Finished Books to Read Folder"
STR_REFRESH_FREQ: "Refresh Frequency"
STR_KOREADER_SYNC: "KOReader Sync"
STR_CHECK_UPDATES: "Check for updates"
@@ -100,7 +105,7 @@ STR_KOREADER_PASSWORD: "KOReader Password"
STR_FILENAME: "Filename"
STR_BINARY: "Binary"
STR_SET_CREDENTIALS_FIRST: "Set credentials first"
STR_WIFI_CONN_FAILED: "WiFi connection failed"
STR_WIFI_CONN_FAILED: "Wi-Fi connection failed"
STR_AUTHENTICATING: "Authenticating..."
STR_AUTH_SUCCESS: "Successfully authenticated!"
STR_KOREADER_AUTH: "KOReader Auth"
@@ -152,6 +157,7 @@ STR_ALIGN_LEFT: "Left"
STR_CENTER: "Center"
STR_ALIGN_RIGHT: "Right"
STR_MIN_1: "1 min"
STR_MIN_3: "3 min"
STR_MIN_5: "5 min"
STR_MIN_10: "10 min"
STR_MIN_15: "15 min"
@@ -232,6 +238,21 @@ STR_BATTERY: "Battery"
STR_XTC_STATUS_BAR: "XTC Status Bar"
STR_BOTTOM: "Bottom"
STR_TOP: "Top"
STR_CLOCK: "Clock"
STR_CLOCK_UTC_OFFSET: "Clock UTC Offset"
STR_CLOCK_FORMAT: "Clock Format"
STR_CLOCK_FORMAT_24H: "24-hour"
STR_CLOCK_FORMAT_12H: "12-hour"
STR_CURRENT_TIME: "Current time:"
STR_NEXT_FIELD: "Next"
STR_CLOCK_SYNC: "Sync Clock"
STR_CLOCK_SYNC_NOW: "Sync clock now"
STR_CLOCK_SYNCING: "Syncing from NTP..."
STR_CLOCK_SYNC_OK: "Clock synced"
STR_CLOCK_SYNC_FAIL: "Sync failed"
STR_CLOCK_SYNC_NO_WIFI: "Wi-Fi not connected"
STR_CLOCK_SYNC_NO_WIFI_HINT: "Connect to Wi-Fi first, then try again."
STR_CLOCK_SYNCED: "Clock Synced"
STR_UI_THEME: "UI Theme"
STR_THEME_CLASSIC: "Classic"
STR_THEME_LYRA: "Lyra"
@@ -242,7 +263,9 @@ STR_REMAP_FRONT_BUTTONS: "Remap Front Buttons"
STR_OPDS_BROWSER: "OPDS Browser"
STR_SEARCH: "Search"
STR_COVER_CUSTOM: "Cover + Custom"
STR_QUICK_RESUME: "Quick Resume"
STR_MENU_RECENT_BOOKS: "Recent Books"
STR_REMOVE_FROM_RECENTS: "Remove from Recent Books?"
STR_NO_RECENT_BOOKS: "No recent books"
STR_CALIBRE_DESC: "Use Calibre wireless device transfers"
STR_FORGET_AND_REMOVE: "Forget network and remove saved password?"
+16 -8
View File
@@ -24,7 +24,7 @@ STR_EMPTY_FILE: "Tyhjä tiedosto"
STR_OUT_OF_BOUNDS: "Alueen ulkopuolella"
STR_LOADING: "Ladataan..."
STR_LOADING_POPUP: "Ladataan"
STR_WIFI_NETWORKS: "WiFi-verkot"
STR_WIFI_NETWORKS: "Wi-Fi-verkot"
STR_NO_NETWORKS: "Verkkoja ei löytynyt"
STR_NETWORKS_FOUND: "%zu verkkoa löydetty"
STR_SCANNING: "Etsitään..."
@@ -36,11 +36,11 @@ STR_SAVE_PASSWORD: "Tallenna salasana seuraavaa kertaa varten?"
STR_PRESS_OK_SCAN: "Paina OK etsiäksesi uudelleen"
STR_JOIN_NETWORK: "Liity verkkoon"
STR_CREATE_HOTSPOT: "Luo yhteyspiste"
STR_JOIN_DESC: "Yhdistä olemassa olevaan WiFi-verkkoon"
STR_HOTSPOT_DESC: "Luo WiFi-verkko, johon muut voivat liittyä"
STR_JOIN_DESC: "Yhdistä olemassa olevaan Wi-Fi-verkkoon"
STR_HOTSPOT_DESC: "Luo Wi-Fi-verkko, johon muut voivat liittyä"
STR_STARTING_HOTSPOT: "Käynnistetään yhteyspiste..."
STR_HOTSPOT_MODE: "Yhteyspistetila"
STR_CONNECT_WIFI_HINT: "Yhdistä laitteesi tähän WiFi-verkkoon"
STR_CONNECT_WIFI_HINT: "Yhdistä laitteesi tähän Wi-Fi-verkkoon"
STR_OPEN_URL_HINT: "Avaa tämä osoite selaimessasi"
STR_OR_HTTP_PREFIX: "tai http://"
STR_SCAN_QR_HINT: "tai skannaa QR-koodi puhelimellasi:"
@@ -48,13 +48,13 @@ STR_CALIBRE_WIRELESS: "Calibre langaton"
STR_CALIBRE_WEB_URL: "OPDS URL"
STR_NETWORK_LEGEND: "* = Salattu | + = Tallennettu"
STR_MAC_ADDRESS: "MAC-osoite:"
STR_CHECKING_WIFI: "Tarkistetaan WiFi..."
STR_ENTER_WIFI_PASSWORD: "Syötä WiFi-salasana"
STR_CHECKING_WIFI: "Tarkistetaan Wi-Fi..."
STR_ENTER_WIFI_PASSWORD: "Syötä Wi-Fi-salasana"
STR_TO_PREFIX: "verkkoon "
STR_CALIBRE_RECEIVING: "Vastaanotetaan: "
STR_CALIBRE_RECEIVED: "Vastaanotettu: "
STR_CALIBRE_INSTRUCTION_1: "1) Asenna CrossPoint Reader -lisäosa"
STR_CALIBRE_INSTRUCTION_2: "2) Ole samassa WiFi-verkossa"
STR_CALIBRE_INSTRUCTION_2: "2) Ole samassa Wi-Fi-verkossa"
STR_CALIBRE_INSTRUCTION_3: "3) Calibressa: \"Lähetä laitteelle\""
STR_CALIBRE_INSTRUCTION_4: "\"Pidä tämä näyttö auki lähetyksen aikana\""
STR_CAT_DISPLAY: "Näyttö"
@@ -69,6 +69,7 @@ STR_TEXT_AA: "Tekstin reunanpehmennys"
STR_SHORT_PWR_BTN: "Lyhyt virtapainikkeen painallus"
STR_ORIENTATION: "Lukusuunta"
STR_SIDE_BTN_LAYOUT: "Sivupainikkeiden asettelu (lukija)"
STR_FRONT_BTN_FOLLOW_ORIENTATION: "Suuntaa etupainikkeet"
STR_LONG_PRESS_BEHAVIOR: "Long-press button behavior"
STR_LONG_PRESS_BEHAVIOR_OFF: "OFF"
STR_LONG_PRESS_BEHAVIOR_SKIP: "Chapter skip"
@@ -80,6 +81,8 @@ STR_SCREEN_MARGIN: "Lukijan näyttömarginaali"
STR_PARA_ALIGNMENT: "Lukijan kappaletasaus"
STR_HYPHENATION: "Tavutus"
STR_TIME_TO_SLEEP: "Aika lepotilaan"
STR_REMOVE_READ_FROM_RECENTS: "Tyhjennä luetut kirjat viimeisimpien listalta"
STR_MOVE_FINISHED_TO_READ: "Siirrä luetut kirjat Read-kansioon"
STR_REFRESH_FREQ: "Päivitystaajuus"
STR_KOREADER_SYNC: "KOReader-synkronointi"
STR_CHECK_UPDATES: "Tarkista päivitykset"
@@ -95,7 +98,7 @@ STR_KOREADER_PASSWORD: "KOReader-salasana"
STR_FILENAME: "Tiedostonimi"
STR_BINARY: "Binääri"
STR_SET_CREDENTIALS_FIRST: "Aseta tunnukset ensin"
STR_WIFI_CONN_FAILED: "WiFi-yhteys epäonnistui"
STR_WIFI_CONN_FAILED: "Wi-Fi-yhteys epäonnistui"
STR_AUTHENTICATING: "Tunnistaudutaan..."
STR_AUTH_SUCCESS: "Tunnistautuminen onnistui!"
STR_KOREADER_AUTH: "KOReader-tunnistautuminen"
@@ -146,6 +149,7 @@ STR_ALIGN_LEFT: "Vasen"
STR_CENTER: "Keskitetty"
STR_ALIGN_RIGHT: "Oikea"
STR_MIN_1: "1 min"
STR_MIN_3: "3 min"
STR_MIN_5: "5 min"
STR_MIN_10: "10 min"
STR_MIN_15: "15 min"
@@ -207,10 +211,14 @@ STR_THEME_CLASSIC: "Klassinen"
STR_THEME_LYRA: "Lyra"
STR_THEME_LYRA_EXTENDED: "Lyra Extended"
STR_SUNLIGHT_FADING_FIX: "Auringonvalon haalistumiskorjaus"
STR_QUICK_RESUME_TIMEOUT: "Pikajatko aikakatkaisulla"
STR_AFTER_TIMEOUT: "Aikakatkon jälkeen"
STR_REMAP_FRONT_BUTTONS: "Uudelleenmääritä etupainikkeet"
STR_OPDS_BROWSER: "OPDS-selain"
STR_COVER_CUSTOM: "Kansi + mukautettu"
STR_QUICK_RESUME: "Pikajatko"
STR_MENU_RECENT_BOOKS: "Viimeisimmät kirjat"
STR_REMOVE_FROM_RECENTS: "Poista viimeisimmistä kirjoista?"
STR_NO_RECENT_BOOKS: "Ei viimeisimpiä kirjoja"
STR_CALIBRE_DESC: "Käytä Calibren langatonta laiteyhteyttä"
STR_FORGET_AND_REMOVE: "Unohda verkko ja poista tallennettu salasana?"
+16 -8
View File
@@ -24,7 +24,7 @@ STR_EMPTY_FILE: "Fichier vide"
STR_OUT_OF_BOUNDS: "Dépassement de mémoire"
STR_LOADING: "Chargement…"
STR_LOADING_POPUP: "Chargement"
STR_WIFI_NETWORKS: "Réseaux WiFi"
STR_WIFI_NETWORKS: "Réseaux Wi-Fi"
STR_NO_NETWORKS: "Aucun réseau"
STR_NETWORKS_FOUND: "%zu réseaux"
STR_SCANNING: "Recherche en cours…"
@@ -36,11 +36,11 @@ STR_SAVE_PASSWORD: "Enregistrer le mot de passe ?"
STR_PRESS_OK_SCAN: "Appuyez sur OK pour scanner"
STR_JOIN_NETWORK: "Rejoindre un réseau"
STR_CREATE_HOTSPOT: "Créer un point daccès"
STR_JOIN_DESC: "Se connecter à un WiFi existant"
STR_HOTSPOT_DESC: "Créer un WiFi pour d'autres appareils"
STR_JOIN_DESC: "Se connecter à un Wi-Fi existant"
STR_HOTSPOT_DESC: "Créer un Wi-Fi pour d'autres appareils"
STR_STARTING_HOTSPOT: "Création du point daccès…"
STR_HOTSPOT_MODE: "Mode point daccès"
STR_CONNECT_WIFI_HINT: "Connectez un appareil à ce WiFi"
STR_CONNECT_WIFI_HINT: "Connectez un appareil à ce Wi-Fi"
STR_OPEN_URL_HINT: "Ouvrez cette URL dans un navigateur"
STR_OR_HTTP_PREFIX: "ou http://"
STR_SCAN_QR_HINT: "ou scannez le QR code :"
@@ -48,13 +48,13 @@ STR_CALIBRE_WIRELESS: "Connexion Calibre sans fil"
STR_CALIBRE_WEB_URL: "OPDS URL"
STR_NETWORK_LEGEND: "* = Sécurisé | + = Sauvegardé"
STR_MAC_ADDRESS: "Adresse MAC :"
STR_CHECKING_WIFI: "Vérification du WiFi…"
STR_ENTER_WIFI_PASSWORD: "Entrez le mot de passe WiFi"
STR_CHECKING_WIFI: "Vérification du Wi-Fi…"
STR_ENTER_WIFI_PASSWORD: "Entrez le mot de passe Wi-Fi"
STR_TO_PREFIX: "vers "
STR_CALIBRE_RECEIVING: "Réception : "
STR_CALIBRE_RECEIVED: "Reçu : "
STR_CALIBRE_INSTRUCTION_1: "1) Installer plugin CrossPoint Reader"
STR_CALIBRE_INSTRUCTION_2: "2) Être sur le même réseau WiFi"
STR_CALIBRE_INSTRUCTION_2: "2) Être sur le même réseau Wi-Fi"
STR_CALIBRE_INSTRUCTION_3: "3) Calibre : Envoyer vers lappareil"
STR_CALIBRE_INSTRUCTION_4: "4) Gardez cet écran ouvert"
STR_CAT_DISPLAY: "Affichage"
@@ -73,6 +73,7 @@ STR_IMAGES_SUPPRESS: "Masquer"
STR_SHORT_PWR_BTN: "Appui court alim."
STR_ORIENTATION: "Orientation de lecture"
STR_SIDE_BTN_LAYOUT: "Boutons latéraux"
STR_FRONT_BTN_FOLLOW_ORIENTATION: "Orienter boutons avant"
STR_LONG_PRESS_BEHAVIOR: "Comportement lors d'un appui long"
STR_LONG_PRESS_BEHAVIOR_OFF: "Désactivé"
STR_LONG_PRESS_BEHAVIOR_SKIP: "Saut de chapitre"
@@ -85,6 +86,8 @@ STR_PARA_ALIGNMENT: "Alignement du texte"
STR_HYPHENATION: "Césure"
STR_TIME_TO_SLEEP: "Mise en veille auto"
STR_SHOW_HIDDEN_FILES: "Afficher les fichiers cachés"
STR_REMOVE_READ_FROM_RECENTS: "Effacer les livres lus de la liste des récents"
STR_MOVE_FINISHED_TO_READ: "Déplacer les livres terminés vers le dossier Read"
STR_REFRESH_FREQ: "Fréquence rafraîchissement"
STR_KOREADER_SYNC: "Synchro KOReader"
STR_CHECK_UPDATES: "Mise à jour"
@@ -100,7 +103,7 @@ STR_KOREADER_PASSWORD: "Mot de passe"
STR_FILENAME: "Nom de fichier"
STR_BINARY: "Binaire"
STR_SET_CREDENTIALS_FIRST: "Identifiants manquants"
STR_WIFI_CONN_FAILED: "Échec connexion WiFi"
STR_WIFI_CONN_FAILED: "Échec connexion Wi-Fi"
STR_AUTHENTICATING: "Authentification…"
STR_AUTH_SUCCESS: "Authentifié !"
STR_KOREADER_AUTH: "Auth KOReader"
@@ -151,6 +154,7 @@ STR_ALIGN_LEFT: "Gauche"
STR_CENTER: "Centré"
STR_ALIGN_RIGHT: "Droite"
STR_MIN_1: "1 min"
STR_MIN_3: "3 min"
STR_MIN_5: "5 min"
STR_MIN_10: "10 min"
STR_MIN_15: "15 min"
@@ -231,10 +235,14 @@ STR_THEME_LYRA: "Lyra"
STR_THEME_ROUNDEDRAFF: "RoundedRaff"
STR_THEME_LYRA_EXTENDED: "Lyra Extended"
STR_SUNLIGHT_FADING_FIX: "Correction lisibilité au soleil"
STR_QUICK_RESUME_TIMEOUT: "Reprise rapide après délai"
STR_AFTER_TIMEOUT: "Après délai"
STR_REMAP_FRONT_BUTTONS: "Configurer boutons façade"
STR_OPDS_BROWSER: "Navigateur OPDS"
STR_COVER_CUSTOM: "Couverture + Perso"
STR_QUICK_RESUME: "Reprise rapide"
STR_MENU_RECENT_BOOKS: "Livres récents"
STR_REMOVE_FROM_RECENTS: "Retirer des Livres récents ?"
STR_NO_RECENT_BOOKS: "Aucun livre récent"
STR_CALIBRE_DESC: "Utiliser transfert sans fil Calibre"
STR_FORGET_AND_REMOVE: "Oublier le réseau et le mot de passe ?"
+8
View File
@@ -69,6 +69,7 @@ STR_TEXT_AA: "Schriftglättung"
STR_SHORT_PWR_BTN: "An-Taste kurz drücken"
STR_ORIENTATION: "Leseausrichtung"
STR_SIDE_BTN_LAYOUT: "Seitliche Tasten (Lesen)"
STR_FRONT_BTN_FOLLOW_ORIENTATION: "Vordere Tasten ausrichten"
STR_LONG_PRESS_BEHAVIOR: "Verhalten bei langem Tastendruck"
STR_LONG_PRESS_BEHAVIOR_OFF: "AUS"
STR_LONG_PRESS_BEHAVIOR_SKIP: "Kapitel überspringen"
@@ -151,6 +152,7 @@ STR_ALIGN_LEFT: "Links"
STR_CENTER: "Zentriert"
STR_ALIGN_RIGHT: "Rechts"
STR_MIN_1: "1 Min"
STR_MIN_3: "3 Min"
STR_MIN_5: "5 Min"
STR_MIN_10: "10 Min"
STR_MIN_15: "15 Min"
@@ -231,10 +233,14 @@ STR_THEME_LYRA: "Lyra"
STR_THEME_ROUNDEDRAFF: "RoundedRaff"
STR_THEME_LYRA_EXTENDED: "Lyra Extended"
STR_SUNLIGHT_FADING_FIX: "Anti-Verblassen"
STR_QUICK_RESUME_TIMEOUT: "Schnelles Fortsetzen nach Timeout"
STR_AFTER_TIMEOUT: "Nach Timeout"
STR_REMAP_FRONT_BUTTONS: "Vordere Tasten belegen"
STR_OPDS_BROWSER: "OPDS-Browser"
STR_COVER_CUSTOM: "Umschlag + Eigenes"
STR_QUICK_RESUME: "Schnelles Fortsetzen"
STR_MENU_RECENT_BOOKS: "Zuletzt gelesen"
STR_REMOVE_FROM_RECENTS: "Aus Zuletzt gelesen entfernen?"
STR_NO_RECENT_BOOKS: "Keine Bücher"
STR_CALIBRE_DESC: "Calibre-Übertragung (WLAN)"
STR_FORGET_AND_REMOVE: "WLAN entfernen & Passwort löschen?"
@@ -302,6 +308,8 @@ STR_IMAGES_DISPLAY: "Darstellung"
STR_IMAGES_PLACEHOLDER: "Platzhalter"
STR_IMAGES_SUPPRESS: "Unterdrücken"
STR_SHOW_HIDDEN_FILES: "Versteckte Dateien anzeigen"
STR_REMOVE_READ_FROM_RECENTS: "Gelesene Bücher aus der Zuletzt-Liste löschen"
STR_MOVE_FINISHED_TO_READ: "Gelesene Bücher in den Read-Ordner verschieben"
STR_PAGE_OVERLAY: "Seiten-Overlay"
STR_RECENTS: "Zuletzt"
STR_MAPPING_REMOTE: "Externe Position zuordnen..."
+16 -8
View File
@@ -24,7 +24,7 @@ STR_EMPTY_FILE: "Üres fájl"
STR_OUT_OF_BOUNDS: "Határon kívül"
STR_LOADING: "Betöltés..."
STR_LOADING_POPUP: "Betöltés"
STR_WIFI_NETWORKS: "WiFi hálózatok"
STR_WIFI_NETWORKS: "Wi-Fi hálózatok"
STR_NO_NETWORKS: "Nem található hálózat"
STR_NETWORKS_FOUND: "%zu hálózat található"
STR_SCANNING: "Keresés..."
@@ -36,11 +36,11 @@ STR_SAVE_PASSWORD: "Jelszó mentése legközelebb?"
STR_PRESS_OK_SCAN: "Nyomj OK-t az újraellenőrzéshez"
STR_JOIN_NETWORK: "Csatlakozás hálózathoz"
STR_CREATE_HOTSPOT: "Hotspot létrehozása"
STR_JOIN_DESC: "Csatlakozás meglévő WiFi hálózathoz"
STR_HOTSPOT_DESC: "WiFi hálózat létrehozása másoknak"
STR_JOIN_DESC: "Csatlakozás meglévő Wi-Fi hálózathoz"
STR_HOTSPOT_DESC: "Wi-Fi hálózat létrehozása másoknak"
STR_STARTING_HOTSPOT: "Hotspot indítása..."
STR_HOTSPOT_MODE: "Hotspot mód"
STR_CONNECT_WIFI_HINT: "Csatlakoztasd az eszközöd ehhez a WiFi hálózathoz"
STR_CONNECT_WIFI_HINT: "Csatlakoztasd az eszközöd ehhez a Wi-Fi hálózathoz"
STR_OPEN_URL_HINT: "Nyisd meg ezt az URL-t a böngésződben"
STR_OR_HTTP_PREFIX: "vagy http://"
STR_SCAN_QR_HINT: "vagy olvasd be a QR-kódot a telefonoddal:"
@@ -48,13 +48,13 @@ STR_CALIBRE_WIRELESS: "Calibre Wireless"
STR_CALIBRE_WEB_URL: "OPDS URL"
STR_NETWORK_LEGEND: "* = Titkosított | + = Mentett"
STR_MAC_ADDRESS: "MAC-cím:"
STR_CHECKING_WIFI: "WiFi ellenőrzése..."
STR_ENTER_WIFI_PASSWORD: "Add meg a WiFi jelszót"
STR_CHECKING_WIFI: "Wi-Fi ellenőrzése..."
STR_ENTER_WIFI_PASSWORD: "Add meg a Wi-Fi jelszót"
STR_TO_PREFIX: "- "
STR_CALIBRE_RECEIVING: "Fogadás: "
STR_CALIBRE_RECEIVED: "Fogadva: "
STR_CALIBRE_INSTRUCTION_1: "1) Telepítsd a CrossPoint Reader plugint"
STR_CALIBRE_INSTRUCTION_2: "2) Ugyanazon a WiFi hálózaton légy"
STR_CALIBRE_INSTRUCTION_2: "2) Ugyanazon a Wi-Fi hálózaton légy"
STR_CALIBRE_INSTRUCTION_3: "3) Calibre-ben: \"Küldés az eszközre\""
STR_CALIBRE_INSTRUCTION_4: "\"Tartsd nyitva a képernyőt küldés közben\""
STR_CAT_DISPLAY: "Megjelenítés"
@@ -73,6 +73,7 @@ STR_IMAGES_SUPPRESS: "Elnyomás"
STR_SHORT_PWR_BTN: "Rövid bekapcsológomb nyomás"
STR_ORIENTATION: "Olvasási irány"
STR_SIDE_BTN_LAYOUT: "Oldalsó gomb elrendezés (olvasó)"
STR_FRONT_BTN_FOLLOW_ORIENTATION: "Elülső gombok tájolása"
STR_LONG_PRESS_SKIP: "Hosszú nyomás - fejezet ugrás"
STR_FONT_FAMILY: "Olvasó betűkészlet"
STR_FONT_SIZE: "Olvasó betűméret"
@@ -82,6 +83,8 @@ STR_PARA_ALIGNMENT: "Olvasó szövegigazítás"
STR_HYPHENATION: "Elválasztás"
STR_TIME_TO_SLEEP: "Alvó mód aktiválása"
STR_SHOW_HIDDEN_FILES: "Rejtett fájlok megjelenítése"
STR_REMOVE_READ_FROM_RECENTS: "Olvasott könyvek törlése a legutóbbiak listájáról"
STR_MOVE_FINISHED_TO_READ: "Befejezett könyvek áthelyezése a Read mappába"
STR_REFRESH_FREQ: "Frissítési frekvencia"
STR_KOREADER_SYNC: "KOReader szinkronizálás"
STR_CHECK_UPDATES: "Frissítések keresése"
@@ -97,7 +100,7 @@ STR_KOREADER_PASSWORD: "KOReader jelszó"
STR_FILENAME: "Fájlnév"
STR_BINARY: "Bináris"
STR_SET_CREDENTIALS_FIRST: "Először add meg az adatokat"
STR_WIFI_CONN_FAILED: "WiFi kapcsolat sikertelen"
STR_WIFI_CONN_FAILED: "Wi-Fi kapcsolat sikertelen"
STR_AUTHENTICATING: "Hitelesítés..."
STR_AUTH_SUCCESS: "Sikeres hitelesítés!"
STR_KOREADER_AUTH: "KOReader hitelesítés"
@@ -148,6 +151,7 @@ STR_ALIGN_LEFT: "Bal"
STR_CENTER: "Közép"
STR_ALIGN_RIGHT: "Jobb"
STR_MIN_1: "1 perc"
STR_MIN_3: "3 perc"
STR_MIN_5: "5 perc"
STR_MIN_10: "10 perc"
STR_MIN_15: "15 perc"
@@ -227,10 +231,14 @@ STR_THEME_CLASSIC: "Klasszikus"
STR_THEME_LYRA: "Lyra"
STR_THEME_LYRA_EXTENDED: "Lyra Extended"
STR_SUNLIGHT_FADING_FIX: "Napfény halványulás javítás"
STR_QUICK_RESUME_TIMEOUT: "Gyors folytatás időtúllépéskor"
STR_AFTER_TIMEOUT: "Időtúllépés után"
STR_REMAP_FRONT_BUTTONS: "Elülső gombok átállítása"
STR_OPDS_BROWSER: "OPDS böngésző"
STR_COVER_CUSTOM: "Borító + Egyéni"
STR_QUICK_RESUME: "Gyors folytatás"
STR_MENU_RECENT_BOOKS: "Legutóbbi könyvek"
STR_REMOVE_FROM_RECENTS: "Eltávolítás a legutóbbi könyvek közül?"
STR_NO_RECENT_BOOKS: "Nincsenek legutóbbi könyvek"
STR_CALIBRE_DESC: "Calibre wireless eszköz átvitel használata"
STR_FORGET_AND_REMOVE: "Hálózat elfelejtése és mentett jelszó törlése?"
+101 -30
View File
@@ -24,7 +24,7 @@ STR_EMPTY_FILE: "File vuoto"
STR_OUT_OF_BOUNDS: "Fuori dai limiti"
STR_LOADING: "Caricamento..."
STR_LOADING_POPUP: "Caricamento"
STR_WIFI_NETWORKS: "Reti WiFi"
STR_WIFI_NETWORKS: "Reti Wi-Fi"
STR_NO_NETWORKS: "Nessuna rete trovata"
STR_NETWORKS_FOUND: "%zu reti trovate"
STR_SCANNING: "Scansione..."
@@ -36,25 +36,25 @@ STR_SAVE_PASSWORD: "Salvare la password?"
STR_PRESS_OK_SCAN: "Premere OK per ripetere scansione"
STR_JOIN_NETWORK: "Connetti a una rete"
STR_CREATE_HOTSPOT: "Crea Hotspot"
STR_JOIN_DESC: "Connessione ad una rete WiFi"
STR_HOTSPOT_DESC: "Creazione di una rete WiFi"
STR_JOIN_DESC: "Connessione ad una rete Wi-Fi"
STR_HOTSPOT_DESC: "Creazione di una rete Wi-Fi"
STR_STARTING_HOTSPOT: "Avvio Hotspot..."
STR_HOTSPOT_MODE: "Modalità Hotspot"
STR_CONNECT_WIFI_HINT: "Connettere il dispositivo a questa rete WiFi"
STR_CONNECT_WIFI_HINT: "Connettere il dispositivo a questa rete Wi-Fi"
STR_OPEN_URL_HINT: "Aprire questo URL nel browser"
STR_OR_HTTP_PREFIX: "o http://"
STR_SCAN_QR_HINT: "oppure scansionare il codice QR col telefono:"
STR_CALIBRE_WIRELESS: "Calibre wireless"
STR_CALIBRE_WEB_URL: "URL Web Calibre"
STR_CALIBRE_WEB_URL: "URL OPDS (Calibre)"
STR_NETWORK_LEGEND: "* = Protetta | + = Salvata"
STR_MAC_ADDRESS: "Indirizzo MAC:"
STR_CHECKING_WIFI: "Verifica WiFi..."
STR_ENTER_WIFI_PASSWORD: "Inserire password WiFi"
STR_CHECKING_WIFI: "Verifica Wi-Fi..."
STR_ENTER_WIFI_PASSWORD: "Inserire password Wi-Fi"
STR_TO_PREFIX: "a "
STR_CALIBRE_RECEIVING: "Ricezione: "
STR_CALIBRE_RECEIVED: "Ricevuto: "
STR_CALIBRE_INSTRUCTION_1: "1) Installare il plugin CrossPoint Reader"
STR_CALIBRE_INSTRUCTION_2: "2) Collegarsi sulla stessa rete WiFi"
STR_CALIBRE_INSTRUCTION_2: "2) Collegarsi sulla stessa rete Wi-Fi"
STR_CALIBRE_INSTRUCTION_3: "3) In Calibre: \"Invia al dispositivo\""
STR_CALIBRE_INSTRUCTION_4: "\"Tenere questa schermata aperta durante l'invio\""
STR_CAT_DISPLAY: "Schermo"
@@ -70,14 +70,14 @@ STR_IMAGES: "Immagini"
STR_IMAGES_DISPLAY: "Visualizza"
STR_IMAGES_PLACEHOLDER: "Segnaposto"
STR_IMAGES_SUPPRESS: "Nascondi"
STR_SHORT_PWR_BTN: "Pressione breve tasto accensione"
STR_SHORT_PWR_BTN: "Pressione breve accensione"
STR_ORIENTATION: "Orientamento lettura"
STR_SIDE_BTN_LAYOUT: "Pulsanti laterali (lettore)"
STR_LONG_PRESS_BEHAVIOR: "Long-press button behavior"
STR_FRONT_BTN_FOLLOW_ORIENTATION: "Orienta pulsanti frontali"
STR_LONG_PRESS_BEHAVIOR: "Pressione lunga"
STR_LONG_PRESS_BEHAVIOR_OFF: "OFF"
STR_LONG_PRESS_BEHAVIOR_SKIP: "Chapter skip"
STR_LONG_PRESS_BEHAVIOR_ORIENTATION: "Orientation change"
STR_LONG_PRESS_SKIP: "Pressione lunga: salta capitolo"
STR_LONG_PRESS_BEHAVIOR_SKIP: "Salta capitolo"
STR_LONG_PRESS_BEHAVIOR_ORIENTATION: "Orientamento"
STR_FONT_FAMILY: "Font lettore"
STR_FONT_SIZE: "Dimensione font lettore"
STR_LINE_SPACING: "Interlinea lettore"
@@ -86,6 +86,8 @@ STR_PARA_ALIGNMENT: "Allineamento paragrafo"
STR_HYPHENATION: "Sillabazione"
STR_TIME_TO_SLEEP: "Timeout sospensione"
STR_SHOW_HIDDEN_FILES: "Mostra file nascosti"
STR_REMOVE_READ_FROM_RECENTS: "Cancella i libri letti dall'elenco dei recenti"
STR_MOVE_FINISHED_TO_READ: "Sposta i libri finiti nella cartella Read"
STR_REFRESH_FREQ: "Frequenza di aggiornamento"
STR_KOREADER_SYNC: "Sincronizzazione KOReader"
STR_CHECK_UPDATES: "Cerca aggiornamenti"
@@ -100,8 +102,8 @@ STR_KOREADER_USERNAME: "Nome utente KOReader"
STR_KOREADER_PASSWORD: "Password KOReader"
STR_FILENAME: "Nome file"
STR_BINARY: "Binario"
STR_SET_CREDENTIALS_FIRST: "Impostare prima le credenziali"
STR_WIFI_CONN_FAILED: "Connessione WiFi non riuscita"
STR_SET_CREDENTIALS_FIRST: "Prima le credenziali"
STR_WIFI_CONN_FAILED: "Connessione Wi-Fi non riuscita"
STR_AUTHENTICATING: "Autenticazione..."
STR_AUTH_SUCCESS: "Autenticazione riuscita!"
STR_KOREADER_AUTH: "Autenticazione KOReader"
@@ -120,7 +122,7 @@ STR_CLEAR_CACHE_FAILED: "Impossibile svuotare la cache"
STR_CHECK_SERIAL_OUTPUT: "Controllare l'output seriale per dettagli"
STR_DARK: "Scuro"
STR_LIGHT: "Chiaro"
STR_CUSTOM: "Wallpaper"
STR_CUSTOM: "Sfondo"
STR_COVER: "Copertina"
STR_NONE_OPT: "Nessuno"
STR_FIT: "Adatta"
@@ -131,10 +133,11 @@ STR_ALWAYS: "Sempre"
STR_IGNORE: "Ignora"
STR_SLEEP: "Sospendi"
STR_PAGE_TURN: "Cambio pagina"
STR_FORCE_REFRESH: "Refresh"
STR_PORTRAIT: "Verticale"
STR_LANDSCAPE_CW: "Orizzontale "
STR_INVERTED: "Invertito"
STR_LANDSCAPE_CCW: "Orizzontale "
STR_LANDSCAPE_CW: "Orizzontale Dx"
STR_INVERTED: "Capovolto"
STR_LANDSCAPE_CCW: "Orizzontale Sx"
STR_PREV_NEXT: "Prec/Succ"
STR_NEXT_PREV: "Succ/Prec"
STR_NOTO_SERIF: "Noto Serif"
@@ -144,7 +147,7 @@ STR_SMALL: "Piccolo"
STR_MEDIUM: "Medio"
STR_LARGE: "Grande"
STR_X_LARGE: "Molto grande"
STR_TIGHT: "Stretto"
STR_TIGHT: "Compatto"
STR_NORMAL: "Normale"
STR_WIDE: "Largo"
STR_JUSTIFY: "Giustificato"
@@ -152,6 +155,7 @@ STR_ALIGN_LEFT: "Sinistra"
STR_CENTER: "Centrato"
STR_ALIGN_RIGHT: "Destra"
STR_MIN_1: "1 min"
STR_MIN_3: "3 min"
STR_MIN_5: "5 min"
STR_MIN_10: "10 min"
STR_MIN_15: "15 min"
@@ -171,14 +175,17 @@ STR_NO_UPDATE: "Nessun aggiornamento disponibile"
STR_UPDATE_FAILED: "Aggiornamento non riuscito"
STR_UPDATE_COMPLETE: "Aggiornamento completato"
STR_POWER_ON_HINT: "Tenere premuto il tasto di accensione per riavviare"
STR_RESTARTING_HINT: "Riavvio... se il dispositivo non si accende, tenere premuto il tasto per qualche secondo."
STR_NO_ENTRIES: "Nessuna voce trovata"
STR_DOWNLOADING: "Download..."
STR_DOWNLOAD_FAILED: "Download non riuscito"
STR_ERROR_MSG: "Errore:"
STR_UNNAMED: "Senza nome"
STR_NO_SERVER_URL: "Nessun Server configurato"
STR_NO_SERVER_URL: "Nessun server configurato"
STR_FETCH_FEED_FAILED: "Impossibile recuperare il feed"
STR_PARSE_FEED_FAILED: "Impossibile analizzare il feed"
STR_NEXT_PAGE: "Pag. successiva »"
STR_PREV_PAGE: "« Pag. precedente"
STR_NETWORK_PREFIX: "Rete: "
STR_IP_ADDRESS_PREFIX: "Indirizzo IP: "
STR_ERROR_GENERAL_FAILURE: "Errore: operazione non riuscita"
@@ -226,15 +233,24 @@ STR_EXAMPLE_BOOK: "Titolo del libro"
STR_PREVIEW: "Anteprima"
STR_TITLE: "Titolo"
STR_BATTERY: "Batteria"
STR_XTC_STATUS_BAR: "Barra di stato XTC"
STR_BOTTOM: "Basso"
STR_TOP: "Alto"
STR_UI_THEME: "Tema interfaccia"
STR_THEME_CLASSIC: "Classico"
STR_THEME_LYRA: "Lyra"
STR_THEME_ROUNDEDRAFF: "RoundedRaff"
STR_THEME_LYRA_EXTENDED: "Lyra esteso"
STR_SUNLIGHT_FADING_FIX: "Correzione luce solare"
STR_QUICK_RESUME_TIMEOUT: "Ripresa rapida dopo timeout"
STR_AFTER_TIMEOUT: "Dopo timeout"
STR_REMAP_FRONT_BUTTONS: "Rimappa pulsanti frontali"
STR_OPDS_BROWSER: "Browser OPDS"
STR_SEARCH: "Cerca"
STR_COVER_CUSTOM: "Copertina + Wallpaper"
STR_QUICK_RESUME: "Ripresa rapida"
STR_MENU_RECENT_BOOKS: "Libri recenti"
STR_REMOVE_FROM_RECENTS: "Rimuovere da Libri recenti?"
STR_NO_RECENT_BOOKS: "Nessun libro recente"
STR_CALIBRE_DESC: "Trasferimento wireless con Calibre"
STR_FORGET_AND_REMOVE: "Eliminare la rete e la password salvata?"
@@ -244,15 +260,15 @@ STR_CALIBRE_SETUP: "Configurazione"
STR_CALIBRE_STATUS: "Stato"
STR_CLEAR_BUTTON: "Elimina"
STR_DEFAULT_VALUE: "Predefinito"
STR_REMAP_PROMPT: "Premere un pulsante frontale per ogni funzione"
STR_REMAP_PROMPT: "Assegna premendo un pulsante frontale"
STR_UNASSIGNED: "Non assegnato"
STR_ALREADY_ASSIGNED: "Già assegnato"
STR_REMAP_RESET_HINT: "Laterale Su: ripristina layout predefinito"
STR_REMAP_RESET_HINT: "Laterale Su: ripristina predefiniti"
STR_REMAP_CANCEL_HINT: "Laterale Giù: annulla rimappatura"
STR_HW_BACK_LABEL: "Indietro (1° pulsante)"
STR_HW_CONFIRM_LABEL: "Conferma (2° pulsante)"
STR_HW_LEFT_LABEL: "Sinistra (3° pulsante)"
STR_HW_RIGHT_LABEL: "Destra (4° pulsante)"
STR_HW_BACK_LABEL: "Indietro (1° pul.)"
STR_HW_CONFIRM_LABEL: "Conferma (2° pul.)"
STR_HW_LEFT_LABEL: "Sinistra (3° pul.)"
STR_HW_RIGHT_LABEL: "Destra (4° pul.)"
STR_GO_TO_PERCENT: "Vai al %"
STR_GO_HOME_BUTTON: "Vai alla home"
STR_SYNC_PROGRESS: "Sincronizza avanzamento"
@@ -283,17 +299,72 @@ STR_NO_REMOTE_MSG: "Nessun avanzamento remoto trovato"
STR_UPLOAD_PROMPT: "Inviare la posizione attuale?"
STR_UPLOAD_SUCCESS: "Avanzamenti inviati!"
STR_SYNC_FAILED_MSG: "Sincronizzazione non riuscita"
STR_SAVE_PROGRESS_FAILED: "Impossibile salvare il progresso"
STR_SAVE_PROGRESS_FAILED: "Impossibile salvare avanzamenti"
STR_SECTION_PREFIX: "Sezione "
STR_UPLOAD: "Carica"
STR_BOOK_S_STYLE: "Stile libro"
STR_EMBEDDED_STYLE: "Stile integrato dell'epub"
STR_FOCUS_READING: "Lettura focalizzata"
STR_FOCUS_READING: "Lettura guidata"
STR_OPDS_SERVER_URL: "Server OPDS"
STR_SET_SLEEP_COVER: "Imposta copertina"
STR_FOOTNOTES: "Note a piè pagina"
STR_NO_FOOTNOTES: "Nessuna nota in questa pagina"
STR_LINK: "[link]"
STR_SCREENSHOT_BUTTON: "Screenshot"
STR_ADD_SERVER: "Aggiungi server"
STR_SERVER_NAME: "Nome server"
STR_NO_SERVERS: "Nessun server OPDS configurato"
STR_DELETE_SERVER: "Elimina server"
STR_DELETE_CONFIRM: "Eliminare questo server?"
STR_OPDS_SERVERS: "Server OPDS"
STR_AUTO_TURN_ENABLED: "Cambio pagina automatico: "
STR_AUTO_TURN_PAGES_PER_MIN: "Cambio pagina automatico (pag/min)"
STR_TILT_PAGE_TURN: "Gira pagina con inclinazione"
STR_MANAGE_FONTS: "Gestisci font"
STR_FONT_BROWSER: "Sfoglia font"
STR_LOADING_FONT_LIST: "Elenco font..."
STR_NO_FONTS_AVAILABLE: "Nessun font disponibile"
STR_FONT_INSTALLED: "Font installato!"
STR_FONT_INSTALL_FAILED: "Installazione font non riuscita"
STR_INSTALLED: "Installato"
STR_CONFIRM_DOWNLOAD_PROMPT: "Scaricare?"
STR_SD_CARD_FULL: "Spazio insufficiente su SD"
STR_FILES_LABEL: "File: "
STR_SIZE_LABEL: "Dimensione: "
STR_REDOWNLOAD: "Scarica di nuovo"
STR_DOWNLOAD_ALL: "Scarica tutto"
STR_UPDATE_ALL: "Aggiorna tutto"
STR_ALL_FONTS_INSTALLED: "Tutti i font installati!"
STR_UPDATE_AVAILABLE: "Aggiorna"
STR_CRASH_TITLE: "Errore di sistema"
STR_CRASH_DESCRIPTION: "Un rapporto dettagliato è stato salvato in crash_report.txt. Includere il file per la segnalazione del problema."
STR_CRASH_REASON: "Causa dell'errore:"
STR_CRASH_NO_REASON: "(Nessuna causa registrata)"
STR_TILT_PAGE_TURN: "Inclina per cambio pagina"
STR_KB_HINT_MOVE_CURSOR: "SINISTRA o DESTRA per spostare il cursore"
STR_KB_HINT_RETURN_CURSOR: "SINISTRA per tornare alla posizione del cursore"
STR_KB_HINT_HIDE_PASSWORD: "Prima DESTRA e poi [***] per nascondere la password"
STR_KB_HINT_SHOW_PASSWORD: "Prima DESTRA e poi [abc] per mostrare la password"
STR_KB_HINT_TOGGLE_HIDE_PASSWORD: "Premere [***] per nascondere la password"
STR_KB_HINT_TOGGLE_SHOW_PASSWORD: "Premere [abc] per mostrare la password"
STR_KB_HINT_EDIT_ENTRY: "Tieni premuto SU per modificare la voce"
STR_KB_TIPS: "Suggerimenti:"
STR_KB_HINT_RETURN_KEYBOARD: "GIÙ per tornare alla tastiera"
STR_KB_HINT_EXIT_URL_MODE: "ABC per uscire dalla modalità URL"
STR_KB_HINT_CLEAR_TEXT: "Tieni premuto CANC per cancellare tutto"
STR_KB_HINT_SECONDARY_CHAR: "Tieni premuto SELEZ. per carattere secondario"
STR_KB_HINT_UPPER_SECONDARY: "Tieni premuto SELEZ. per MAIUS. o car. secondario"
STR_KB_HINT_LOWER_SECONDARY: "Tieni premuto SELEZ. per minus. o car. secondario"
STR_KB_HINT_URL_SNIPPETS: "Premi l'URL per le anteprime"
STR_SD_FIRMWARE_UPDATE: "Aggiornamento firmware da SD"
STR_SELECT_FIRMWARE_FILE: "Seleziona il firmware (.bin)"
STR_NO_BIN_FILES: "Nessun file .bin trovato"
STR_VALIDATING_FIRMWARE: "Convalida firmware..."
STR_INVALID_FIRMWARE: "File firmware non valido"
STR_FIRMWARE_TOO_LARGE: "Firmware troppo grande"
STR_FIRMWARE_TOO_SMALL: "Firmware troppo piccolo"
STR_FIRMWARE_UPDATE_PROMPT: "Aggiornare il firmware?"
STR_FIRMWARE_FILE_OPEN_FAILED: "Impossibile aprire il file"
STR_FIRMWARE_WRITE_FAILED: "Aggiornamento firmware non riuscito"
STR_FIRMWARE_UPDATE_DO_NOT_POWER_OFF: "Non spegnere il dispositivo!"
STR_RECOVERY_MODE: "Modalità ripristino"
STR_RECOVERY_MODE_HINT: "Metti il firmware.bin nella SD e selezionalo"
+16 -8
View File
@@ -23,7 +23,7 @@ STR_EMPTY_FILE: "Бос файл"
STR_OUT_OF_BOUNDS: "Шектен тыс"
STR_LOADING: "Жүктелуде..."
STR_LOADING_POPUP: "Жүктелуде"
STR_WIFI_NETWORKS: "WiFi желілері"
STR_WIFI_NETWORKS: "Wi-Fi желілері"
STR_NO_NETWORKS: "Желілер табылмады"
STR_NETWORKS_FOUND: "%zu желі табылды"
STR_SCANNING: "Іздеуде..."
@@ -35,11 +35,11 @@ STR_SAVE_PASSWORD: "Келесі жолға құпия сөзді сақтау
STR_PRESS_OK_SCAN: "Қайта іздеу үшін OK басыңыз"
STR_JOIN_NETWORK: "Желіге қосылу"
STR_CREATE_HOTSPOT: "Хотспот жасау"
STR_JOIN_DESC: "Бар WiFi желісіне қосылу"
STR_HOTSPOT_DESC: "Басқалар қоса алатын WiFi желісін жасау"
STR_JOIN_DESC: "Бар Wi-Fi желісіне қосылу"
STR_HOTSPOT_DESC: "Басқалар қоса алатын Wi-Fi желісін жасау"
STR_STARTING_HOTSPOT: "Хотспот іске қосылуда..."
STR_HOTSPOT_MODE: "Хотспот режимі"
STR_CONNECT_WIFI_HINT: "Құрылғыңызды осы WiFi желісіне қосыңыз"
STR_CONNECT_WIFI_HINT: "Құрылғыңызды осы Wi-Fi желісіне қосыңыз"
STR_OPEN_URL_HINT: "Браузерде осы URL мекенжайын ашыңыз"
STR_OR_HTTP_PREFIX: "немесе http://"
STR_SCAN_QR_HINT: "немесе телефонмен QR кодын сканерлеңіз:"
@@ -47,13 +47,13 @@ STR_CALIBRE_WIRELESS: "Calibre сымсыз"
STR_CALIBRE_WEB_URL: "OPDS URL"
STR_NETWORK_LEGEND: "* = Шифрланған | + = Сақталған"
STR_MAC_ADDRESS: "MAC мекенжайы:"
STR_CHECKING_WIFI: "WiFi тексерілуде..."
STR_ENTER_WIFI_PASSWORD: "WiFi құпия сөзін енгізіңіз"
STR_CHECKING_WIFI: "Wi-Fi тексерілуде..."
STR_ENTER_WIFI_PASSWORD: "Wi-Fi құпия сөзін енгізіңіз"
STR_TO_PREFIX: ""
STR_CALIBRE_RECEIVING: "Қабылдануда: "
STR_CALIBRE_RECEIVED: "Қабылданды: "
STR_CALIBRE_INSTRUCTION_1: "1) CrossPoint Reader плагинін орнатыңыз"
STR_CALIBRE_INSTRUCTION_2: "2) Бір WiFi желісінде болыңыз"
STR_CALIBRE_INSTRUCTION_2: "2) Бір Wi-Fi желісінде болыңыз"
STR_CALIBRE_INSTRUCTION_3: "3) Calibre-де: \"Құрылғыға жіберу\""
STR_CALIBRE_INSTRUCTION_4: "\"Жіберу кезінде осы экранды ашық ұстаңыз\""
STR_CAT_DISPLAY: "Дисплей"
@@ -68,6 +68,7 @@ STR_TEXT_AA: "Мәтін сырғытпасы"
STR_SHORT_PWR_BTN: "Қуат түймесін қысқа басу"
STR_ORIENTATION: "Оқу бағдары"
STR_SIDE_BTN_LAYOUT: "Бүйірлік түймелер орналасуы (оқырман)"
STR_FRONT_BTN_FOLLOW_ORIENTATION: "Алдыңғы түймелерді бағдарлау"
STR_LONG_PRESS_SKIP: "Ұзақ басу арқылы тарау өткізу"
STR_FONT_FAMILY: "Оқырман қаріп тобы"
STR_FONT_SIZE: "Интерфейс қаріп өлшемі"
@@ -76,6 +77,8 @@ STR_SCREEN_MARGIN: "Оқырман экран жиегі"
STR_PARA_ALIGNMENT: "Оқырман абзац туралануы"
STR_HYPHENATION: "Буын бөлу"
STR_TIME_TO_SLEEP: "Ұйқы уақыты"
STR_REMOVE_READ_FROM_RECENTS: "Оқылған кітаптарды соңғылар тізімінен тазалау"
STR_MOVE_FINISHED_TO_READ: "Оқылған кітаптарды Read қалтасына жылжыту"
STR_REFRESH_FREQ: "Жаңарту жиілігі"
STR_KOREADER_SYNC: "KOReader синхронизациясы"
STR_CHECK_UPDATES: "Жаңартуларды тексеру"
@@ -91,7 +94,7 @@ STR_KOREADER_PASSWORD: "KOReader құпия сөзі"
STR_FILENAME: "Файл аты"
STR_BINARY: "Бинарлық"
STR_SET_CREDENTIALS_FIRST: "Алдымен тіркелгі деректерін орнатыңыз"
STR_WIFI_CONN_FAILED: "WiFi қосылуы сәтсіз"
STR_WIFI_CONN_FAILED: "Wi-Fi қосылуы сәтсіз"
STR_AUTHENTICATING: "Аутентификацияланып жатыр..."
STR_AUTH_SUCCESS: "Аутентификация сәтті!"
STR_KOREADER_AUTH: "KOReader аутентификациясы"
@@ -142,6 +145,7 @@ STR_ALIGN_LEFT: "Солға"
STR_CENTER: "Ортаға"
STR_ALIGN_RIGHT: "Оңға"
STR_MIN_1: "1 мин"
STR_MIN_3: "3 мин"
STR_MIN_5: "5 мин"
STR_MIN_10: "10 мин"
STR_MIN_15: "15 мин"
@@ -203,10 +207,14 @@ STR_THEME_CLASSIC: "Классикалық"
STR_THEME_LYRA: "Lyra"
STR_THEME_LYRA_EXTENDED: "Lyra кеңейтілген"
STR_SUNLIGHT_FADING_FIX: "Күн сәулесінен солу түзету"
STR_QUICK_RESUME_TIMEOUT: "Таймауттан кейін жылдам жалғастыру"
STR_AFTER_TIMEOUT: "Таймауттан кейін"
STR_REMAP_FRONT_BUTTONS: "Алдыңғы түймелерді қайта баптау"
STR_OPDS_BROWSER: "OPDS шолғышы"
STR_COVER_CUSTOM: "Мұқаба + Өзгертілген"
STR_QUICK_RESUME: "Жылдам жалғастыру"
STR_MENU_RECENT_BOOKS: "Жуырда оқылған кітаптар"
STR_REMOVE_FROM_RECENTS: "Жуырда оқылған кітаптардан жою?"
STR_NO_RECENT_BOOKS: "Жуырда оқылған кітаптар жоқ"
STR_CALIBRE_DESC: "Calibre сымсыз тасымалдауын пайдалану"
STR_FORGET_AND_REMOVE: "Желіні ұмыту және сақталған құпия сөзді жою керек пе?"
+16 -8
View File
@@ -24,7 +24,7 @@ STR_EMPTY_FILE: "Tuščias failas"
STR_OUT_OF_BOUNDS: "Už ribų"
STR_LOADING: "Kraunama..."
STR_LOADING_POPUP: "Kraunama"
STR_WIFI_NETWORKS: "WiFi tinklai"
STR_WIFI_NETWORKS: "Wi-Fi tinklai"
STR_NO_NETWORKS: "Tinklų nerasta"
STR_NETWORKS_FOUND: "Rasta: %zu"
STR_SCANNING: "Ieškoma..."
@@ -36,11 +36,11 @@ STR_SAVE_PASSWORD: "Išsaugoti slaptažodį?"
STR_PRESS_OK_SCAN: "Ieškoti iš naujo (OK)"
STR_JOIN_NETWORK: "Prisijungti"
STR_CREATE_HOTSPOT: "Sukurti prieigą"
STR_JOIN_DESC: "Jungtis prie esamo WiFi"
STR_HOTSPOT_DESC: "Sukurti WiFi kitiems"
STR_JOIN_DESC: "Jungtis prie esamo Wi-Fi"
STR_HOTSPOT_DESC: "Sukurti Wi-Fi kitiems"
STR_STARTING_HOTSPOT: "Kuriamas ryšys..."
STR_HOTSPOT_MODE: "Prieigos režimas"
STR_CONNECT_WIFI_HINT: "Prijunkite įrenginį prie šio WiFi"
STR_CONNECT_WIFI_HINT: "Prijunkite įrenginį prie šio Wi-Fi"
STR_OPEN_URL_HINT: "Atidarykite šį adresą naršyklėje"
STR_OR_HTTP_PREFIX: "arba http://"
STR_SCAN_QR_HINT: "arba nuskaitykite QR kodą:"
@@ -48,13 +48,13 @@ STR_CALIBRE_WIRELESS: "Calibre belaidis"
STR_CALIBRE_WEB_URL: "OPDS URL"
STR_NETWORK_LEGEND: "* = Užšifruota | + = Išsaugota"
STR_MAC_ADDRESS: "MAC adresas:"
STR_CHECKING_WIFI: "Tikrinamas WiFi..."
STR_ENTER_WIFI_PASSWORD: "WiFi slaptažodis"
STR_CHECKING_WIFI: "Tikrinamas Wi-Fi..."
STR_ENTER_WIFI_PASSWORD: "Wi-Fi slaptažodis"
STR_TO_PREFIX: "į "
STR_CALIBRE_RECEIVING: "Gaunama: "
STR_CALIBRE_RECEIVED: "Gauta: "
STR_CALIBRE_INSTRUCTION_1: "1) Įdiekite CrossPoint įskiepį"
STR_CALIBRE_INSTRUCTION_2: "2) Naudokite tą patį WiFi"
STR_CALIBRE_INSTRUCTION_2: "2) Naudokite tą patį Wi-Fi"
STR_CALIBRE_INSTRUCTION_3: "3) Calibre: „Send to device“"
STR_CALIBRE_INSTRUCTION_4: "Neišjunkite šio ekrano"
STR_CAT_DISPLAY: "Ekranas"
@@ -73,6 +73,7 @@ STR_IMAGES_SUPPRESS: "Slėpti"
STR_SHORT_PWR_BTN: "Trumpas įjungimo pasp."
STR_ORIENTATION: "Orientacija"
STR_SIDE_BTN_LAYOUT: "Šoniniai mygtukai"
STR_FRONT_BTN_FOLLOW_ORIENTATION: "Orientuoti priekinius mygtukus"
STR_LONG_PRESS_SKIP: "Praleisti skyrių (ilgai)"
STR_FONT_FAMILY: "Šriftas"
STR_FONT_SIZE: "Šrifto dydis"
@@ -82,6 +83,8 @@ STR_PARA_ALIGNMENT: "Lygiavimas"
STR_HYPHENATION: "Skiemenuoti"
STR_TIME_TO_SLEEP: "Užmigti po"
STR_SHOW_HIDDEN_FILES: "Rod. paslėptus failus"
STR_REMOVE_READ_FROM_RECENTS: "Išvalyti perskaitytas knygas iš paskutinių sąrašo"
STR_MOVE_FINISHED_TO_READ: "Perkelti perskaitytas knygas į aplanką Read"
STR_REFRESH_FREQ: "Atnaujinimo dažnis"
STR_KOREADER_SYNC: "KOReader sinchr."
STR_CHECK_UPDATES: "Atnaujinimai"
@@ -97,7 +100,7 @@ STR_KOREADER_PASSWORD: "KOReader slaptažodis"
STR_FILENAME: "Failas"
STR_BINARY: "Dvejetainis"
STR_SET_CREDENTIALS_FIRST: "Nustatykite duomenis"
STR_WIFI_CONN_FAILED: "WiFi ryšys nutrūko"
STR_WIFI_CONN_FAILED: "Wi-Fi ryšys nutrūko"
STR_AUTHENTICATING: "Jungiamasi..."
STR_AUTH_SUCCESS: "Prisijungta!"
STR_KOREADER_AUTH: "KOReader Login"
@@ -148,6 +151,7 @@ STR_ALIGN_LEFT: "Kairė"
STR_CENTER: "Centras"
STR_ALIGN_RIGHT: "Dešinė"
STR_MIN_1: "1 min"
STR_MIN_3: "3 min"
STR_MIN_5: "5 min"
STR_MIN_10: "10 min"
STR_MIN_15: "15 min"
@@ -227,10 +231,14 @@ STR_THEME_CLASSIC: "Klasikinė"
STR_THEME_LYRA: "Lyra"
STR_THEME_LYRA_EXTENDED: "Lyra Ext."
STR_SUNLIGHT_FADING_FIX: "Blyškumo pataisa"
STR_QUICK_RESUME_TIMEOUT: "Greitas tęsimas po skirtojo laiko"
STR_AFTER_TIMEOUT: "Po skirtojo laiko"
STR_REMAP_FRONT_BUTTONS: "Keisti mygtukus"
STR_OPDS_BROWSER: "OPDS naršyklė"
STR_COVER_CUSTOM: "Viršelis + Kita"
STR_QUICK_RESUME: "Greitas tęsimas"
STR_MENU_RECENT_BOOKS: "Paskutinės"
STR_REMOVE_FROM_RECENTS: "Pašalinti iš paskutinių?"
STR_NO_RECENT_BOOKS: "Paskutinių nėra"
STR_CALIBRE_DESC: "Calibre belaidis ryšys"
STR_FORGET_AND_REMOVE: "Pamiršti tinklą?"
+16 -8
View File
@@ -24,7 +24,7 @@ STR_EMPTY_FILE: "Pusty plik"
STR_OUT_OF_BOUNDS: "Poza granicami"
STR_LOADING: "Ładowanie..."
STR_LOADING_POPUP: "Ładowanie"
STR_WIFI_NETWORKS: "Sieci WiFi"
STR_WIFI_NETWORKS: "Sieci Wi-Fi"
STR_NO_NETWORKS: "Nie znaleziono sieci"
STR_NETWORKS_FOUND: "Znaleziono %zu sieci"
STR_SCANNING: "Skanowanie..."
@@ -36,11 +36,11 @@ STR_SAVE_PASSWORD: "Zapisać hasło na przyszłość?"
STR_PRESS_OK_SCAN: "Naciśnij OK aby skanować ponownie"
STR_JOIN_NETWORK: "Dołącz do sieci"
STR_CREATE_HOTSPOT: "Stwórz Hotspot"
STR_JOIN_DESC: "Podłącz do istniejącej sieci WiFi"
STR_HOTSPOT_DESC: "Stwórz sieć WiFi do której podłączyć mogą się inni"
STR_JOIN_DESC: "Podłącz do istniejącej sieci Wi-Fi"
STR_HOTSPOT_DESC: "Stwórz sieć Wi-Fi do której podłączyć mogą się inni"
STR_STARTING_HOTSPOT: "Startowanie Hotspota..."
STR_HOTSPOT_MODE: "Tryb Hotspot"
STR_CONNECT_WIFI_HINT: "Podłącz swoje urządzenie do tej sieci WiFi"
STR_CONNECT_WIFI_HINT: "Podłącz swoje urządzenie do tej sieci Wi-Fi"
STR_OPEN_URL_HINT: "Otwórz ten URL w przeglądarce"
STR_OR_HTTP_PREFIX: "albo http://"
STR_SCAN_QR_HINT: "albo zeskanuj kod QR telefonem:"
@@ -48,13 +48,13 @@ STR_CALIBRE_WIRELESS: "Bezprzewodowe połączenie z Calibre"
STR_CALIBRE_WEB_URL: "OPDS URL"
STR_NETWORK_LEGEND: "* = Zaszyfrowane | + = Zapisane"
STR_MAC_ADDRESS: "Adres MAC:"
STR_CHECKING_WIFI: "Sprawdzanie WiFi..."
STR_ENTER_WIFI_PASSWORD: "Wprowadź hasło WiFi"
STR_CHECKING_WIFI: "Sprawdzanie Wi-Fi..."
STR_ENTER_WIFI_PASSWORD: "Wprowadź hasło Wi-Fi"
STR_TO_PREFIX: "Z "
STR_CALIBRE_RECEIVING: "Odbieranie: "
STR_CALIBRE_RECEIVED: "Odebrano: "
STR_CALIBRE_INSTRUCTION_1: "1) Zainstaluj plugin CrossPoint Reader"
STR_CALIBRE_INSTRUCTION_2: "2) Bądź w tej samej sieci WiFi"
STR_CALIBRE_INSTRUCTION_2: "2) Bądź w tej samej sieci Wi-Fi"
STR_CALIBRE_INSTRUCTION_3: "3) W Calibre: \"Wyślij do urządzenia\""
STR_CALIBRE_INSTRUCTION_4: "\"Zostaw ten ekran podczas wysyłania\""
STR_CAT_DISPLAY: "Wyświetlacz"
@@ -73,6 +73,7 @@ STR_IMAGES_SUPPRESS: "Pomijaj"
STR_SHORT_PWR_BTN: "Krótkie naciśnięcie zasilania"
STR_ORIENTATION: "Układ czytania"
STR_SIDE_BTN_LAYOUT: "Układ przycisków bocznych"
STR_FRONT_BTN_FOLLOW_ORIENTATION: "Orientuj przednie przyciski"
STR_LONG_PRESS_BEHAVIOR: "Funkcja długiego przyciśnięcia"
STR_LONG_PRESS_BEHAVIOR_OFF: "Wył."
STR_LONG_PRESS_BEHAVIOR_SKIP: "Przeskocz rozdział"
@@ -85,6 +86,8 @@ STR_PARA_ALIGNMENT: "Wyjustowanie paragrafów"
STR_HYPHENATION: "Dzielenie wyrazów"
STR_TIME_TO_SLEEP: "Czas do uśpienia"
STR_SHOW_HIDDEN_FILES: "Pokaż ukryte pliki"
STR_REMOVE_READ_FROM_RECENTS: "Wyczyść przeczytane książki z listy ostatnich"
STR_MOVE_FINISHED_TO_READ: "Przenoś przeczytane książki do folderu Read"
STR_REFRESH_FREQ: "Częstotliwość odświeżania"
STR_KOREADER_SYNC: "KOReader Sync"
STR_CHECK_UPDATES: "Sprawdź uaktualnia"
@@ -100,7 +103,7 @@ STR_KOREADER_PASSWORD: "Hasło KOReader"
STR_FILENAME: "Nazwa pliku"
STR_BINARY: "Binary"
STR_SET_CREDENTIALS_FIRST: "Najpierw ustaw uwierzytelnianie"
STR_WIFI_CONN_FAILED: "Połączenie z WiFi nieudane"
STR_WIFI_CONN_FAILED: "Połączenie z Wi-Fi nieudane"
STR_AUTHENTICATING: "Uwierzytelnianie..."
STR_AUTH_SUCCESS: "Pomyślnie uwierzytelniono!"
STR_KOREADER_AUTH: "KOReader Auth"
@@ -152,6 +155,7 @@ STR_ALIGN_LEFT: "Lewo"
STR_CENTER: "Środek"
STR_ALIGN_RIGHT: "Prawo"
STR_MIN_1: "1 min"
STR_MIN_3: "3 min"
STR_MIN_5: "5 min"
STR_MIN_10: "10 min"
STR_MIN_15: "15 min"
@@ -238,11 +242,15 @@ STR_THEME_LYRA: "Lyra"
STR_THEME_ROUNDEDRAFF: "RoundedRaff"
STR_THEME_LYRA_EXTENDED: "Lyra Extended"
STR_SUNLIGHT_FADING_FIX: "Przeciwdziałanie blaknięciu od słońca"
STR_QUICK_RESUME_TIMEOUT: "Szybkie wznawianie po czasie"
STR_AFTER_TIMEOUT: "Po upływie czasu"
STR_REMAP_FRONT_BUTTONS: "Skonfiguruj przednie przyciski"
STR_OPDS_BROWSER: "OPDS Browser"
STR_SEARCH: "Szukaj"
STR_COVER_CUSTOM: "Okładka + Własne"
STR_QUICK_RESUME: "Szybkie wznawianie"
STR_MENU_RECENT_BOOKS: "Ostatnio czytane"
STR_REMOVE_FROM_RECENTS: "Usunąć z ostatnio czytanych?"
STR_NO_RECENT_BOOKS: "Brak ostatnio czytanych"
STR_CALIBRE_DESC: "Przesyłaj bezprzewodowo do Calibre"
STR_FORGET_AND_REMOVE: "Zapomnieć zapisane sieci oraz hasła?"
+16 -8
View File
@@ -24,7 +24,7 @@ STR_EMPTY_FILE: "Arquivo vazio"
STR_OUT_OF_BOUNDS: "Fora dos limites"
STR_LOADING: "Carregando..."
STR_LOADING_POPUP: "Carregando"
STR_WIFI_NETWORKS: "Redes WiFi"
STR_WIFI_NETWORKS: "Redes Wi-Fi"
STR_NO_NETWORKS: "Sem redes"
STR_NETWORKS_FOUND: "%zu redes encontradas"
STR_SCANNING: "Procurando..."
@@ -36,11 +36,11 @@ STR_SAVE_PASSWORD: "Salvar senha a próxima vez?"
STR_PRESS_OK_SCAN: "Pressione OK procurar novamente"
STR_JOIN_NETWORK: "Entrar em uma rede"
STR_CREATE_HOTSPOT: "Criar hotspot"
STR_JOIN_DESC: "Conecte-se a uma rede WiFi existente"
STR_HOTSPOT_DESC: "Crie uma rede WiFi outras pessoas entrarem"
STR_JOIN_DESC: "Conecte-se a uma rede Wi-Fi existente"
STR_HOTSPOT_DESC: "Crie uma rede Wi-Fi para outras pessoas entrarem"
STR_STARTING_HOTSPOT: "Iniciando hotspot..."
STR_HOTSPOT_MODE: "Modo hotspot"
STR_CONNECT_WIFI_HINT: "Conecte seu dispositivo a esta rede WiFi"
STR_CONNECT_WIFI_HINT: "Conecte seu dispositivo a esta rede Wi-Fi"
STR_OPEN_URL_HINT: "Abra este URL seu navegador"
STR_OR_HTTP_PREFIX: "ou http://"
STR_SCAN_QR_HINT: "ou escaneie o QR code com seu celular:"
@@ -48,13 +48,13 @@ STR_CALIBRE_WIRELESS: "Calibre sem fio"
STR_CALIBRE_WEB_URL: "OPDS URL"
STR_NETWORK_LEGEND: "* = Criptografada | + = Salva"
STR_MAC_ADDRESS: "Endereço MAC:"
STR_CHECKING_WIFI: "Verificando WiFi..."
STR_ENTER_WIFI_PASSWORD: "Digite a senha WiFi"
STR_CHECKING_WIFI: "Verificando Wi-Fi..."
STR_ENTER_WIFI_PASSWORD: "Digite a senha Wi-Fi"
STR_TO_PREFIX: "para"
STR_CALIBRE_RECEIVING: "Recebendo:"
STR_CALIBRE_RECEIVED: "Recebido:"
STR_CALIBRE_INSTRUCTION_1: "1) Instale o plugin CrossPoint Reader"
STR_CALIBRE_INSTRUCTION_2: "2) Esteja mesma rede WiFi"
STR_CALIBRE_INSTRUCTION_2: "2) Esteja na mesma rede Wi-Fi"
STR_CALIBRE_INSTRUCTION_3: "3) No Calibre: \"Enviar o dispositivo\""
STR_CALIBRE_INSTRUCTION_4: "\"Mantenha esta tela aberta durante o envio\""
STR_CAT_DISPLAY: "Tela"
@@ -69,6 +69,7 @@ STR_TEXT_AA: "Suavização de texto"
STR_SHORT_PWR_BTN: "Clique curto botão ligar"
STR_ORIENTATION: "Orientação de leitura"
STR_SIDE_BTN_LAYOUT: "Disposição botões laterais"
STR_FRONT_BTN_FOLLOW_ORIENTATION: "Orientar botões frontais"
STR_LONG_PRESS_BEHAVIOR: "Comportamento do botão de premir e segurar"
STR_LONG_PRESS_BEHAVIOR_OFF: "Desligado"
STR_LONG_PRESS_BEHAVIOR_SKIP: "Saltar capítulo"
@@ -80,6 +81,8 @@ STR_SCREEN_MARGIN: "Margens da tela"
STR_PARA_ALIGNMENT: "Alinhamento parágrafo"
STR_HYPHENATION: "Hifenização"
STR_TIME_TO_SLEEP: "Tempo para repousar"
STR_REMOVE_READ_FROM_RECENTS: "Limpar livros lidos da lista de recentes"
STR_MOVE_FINISHED_TO_READ: "Mover livros lidos para a pasta Read"
STR_REFRESH_FREQ: "Frequência atualização"
STR_KOREADER_SYNC: "Sincronização KOReader"
STR_CHECK_UPDATES: "Verificar atualizações"
@@ -95,7 +98,7 @@ STR_KOREADER_PASSWORD: "Senha do KOReader"
STR_FILENAME: "Nome do arquivo"
STR_BINARY: "Binário"
STR_SET_CREDENTIALS_FIRST: "Defina as credenciais primeiro"
STR_WIFI_CONN_FAILED: "Falha na conexão WiFi"
STR_WIFI_CONN_FAILED: "Falha na conexão Wi-Fi"
STR_AUTHENTICATING: "Autenticando..."
STR_AUTH_SUCCESS: "Autenticado com sucesso!"
STR_KOREADER_AUTH: "Autenticação KOReader"
@@ -146,6 +149,7 @@ STR_ALIGN_LEFT: "Esquerda"
STR_CENTER: "Centralizar"
STR_ALIGN_RIGHT: "Direita"
STR_MIN_1: "1 min"
STR_MIN_3: "3 min"
STR_MIN_5: "5 min"
STR_MIN_10: "10 min"
STR_MIN_15: "15 min"
@@ -208,10 +212,14 @@ STR_THEME_LYRA: "Lyra"
STR_THEME_ROUNDEDRAFF: "RoundedRaff"
STR_THEME_LYRA_EXTENDED: "Lyra Extended"
STR_SUNLIGHT_FADING_FIX: "Ajuste desbotamento ao sol"
STR_QUICK_RESUME_TIMEOUT: "Retomada rápida após tempo limite"
STR_AFTER_TIMEOUT: "Após tempo limite"
STR_REMAP_FRONT_BUTTONS: "Remapear botões frontais"
STR_OPDS_BROWSER: "Navegador OPDS"
STR_COVER_CUSTOM: "Capa + personalizado"
STR_QUICK_RESUME: "Retomada rápida"
STR_MENU_RECENT_BOOKS: "Livros recentes"
STR_REMOVE_FROM_RECENTS: "Remover dos Livros recentes?"
STR_NO_RECENT_BOOKS: "Sem livros recentes"
STR_CALIBRE_DESC: "Usar transferências sem fio Calibre"
STR_FORGET_AND_REMOVE: "Esquecer a rede e remover a senha salva?"
+16 -8
View File
@@ -24,7 +24,7 @@ STR_EMPTY_FILE: "Fişier gol"
STR_OUT_OF_BOUNDS: "Eroare: În afara limitelor"
STR_LOADING: "Se încarcă..."
STR_LOADING_POPUP: "Se încarcă..."
STR_WIFI_NETWORKS: "Reţele WiFi"
STR_WIFI_NETWORKS: "Reţele Wi-Fi"
STR_NO_NETWORKS: "Nu s-au găsit reţele"
STR_NETWORKS_FOUND: "%zu reţele găsite"
STR_SCANNING: "Scanează..."
@@ -36,11 +36,11 @@ STR_SAVE_PASSWORD: "Salvaţi parola?"
STR_PRESS_OK_SCAN: "Apăsaţi OK pentru a scana din nou"
STR_JOIN_NETWORK: "Conectaţi-vă la o reţea"
STR_CREATE_HOTSPOT: "Creaţi un hotspot"
STR_JOIN_DESC: "Conectaţi-vă la o reţea WiFi existentă"
STR_HOTSPOT_DESC: "Creaţi un hotspot WiFi"
STR_JOIN_DESC: "Conectaţi-vă la o reţea Wi-Fi existentă"
STR_HOTSPOT_DESC: "Creaţi un hotspot Wi-Fi"
STR_STARTING_HOTSPOT: "Hotspot porneşte..."
STR_HOTSPOT_MODE: "Mod Hotspot"
STR_CONNECT_WIFI_HINT: "Conectaţi-vă dispozitivul la această reţea WiFi"
STR_CONNECT_WIFI_HINT: "Conectaţi-vă dispozitivul la această reţea Wi-Fi"
STR_OPEN_URL_HINT: "Deschideţi acest URL în browserul dvs."
STR_OR_HTTP_PREFIX: "sau http://"
STR_SCAN_QR_HINT: "sau scanaţi codul QR cu telefonul dvs.:"
@@ -48,13 +48,13 @@ STR_CALIBRE_WIRELESS: "Calibre Wireless"
STR_CALIBRE_WEB_URL: "OPDS URL"
STR_NETWORK_LEGEND: "* = Criptat | + = Salvat"
STR_MAC_ADDRESS: "Adresă MAC:"
STR_CHECKING_WIFI: "Verificare WiFi..."
STR_ENTER_WIFI_PASSWORD: "Introduceţi parola WiFi"
STR_CHECKING_WIFI: "Verificare Wi-Fi..."
STR_ENTER_WIFI_PASSWORD: "Introduceţi parola Wi-Fi"
STR_TO_PREFIX: "la "
STR_CALIBRE_RECEIVING: "Se primeşte: "
STR_CALIBRE_RECEIVED: "Primite: "
STR_CALIBRE_INSTRUCTION_1: "1) Instalaţi plugin-ul CrossPoint Reader"
STR_CALIBRE_INSTRUCTION_2: "2) Fiţi în aceeaşi reţea WiFi"
STR_CALIBRE_INSTRUCTION_2: "2) Fiţi în aceeaşi reţea Wi-Fi"
STR_CALIBRE_INSTRUCTION_3: "3) În Calibre: \"Trimiteţi la dispozitiv\""
STR_CALIBRE_INSTRUCTION_4: "\"Păstraţi acest ecran deschis în timpul trimiterii\""
STR_CAT_DISPLAY: "Ecran"
@@ -73,6 +73,7 @@ STR_IMAGES_SUPPRESS: "Suprimare"
STR_SHORT_PWR_BTN: "Apăsare scurtă întrerupător"
STR_ORIENTATION: "Orientare lectură"
STR_SIDE_BTN_LAYOUT: "Aspect butoane laterale (lectură)"
STR_FRONT_BTN_FOLLOW_ORIENTATION: "Orientare butoane frontale"
STR_LONG_PRESS_BEHAVIOR: "Comportament buton apăsat lung"
STR_LONG_PRESS_BEHAVIOR_OFF: "Dezactivat"
STR_LONG_PRESS_BEHAVIOR_SKIP: "Sărire capitol"
@@ -85,6 +86,8 @@ STR_PARA_ALIGNMENT: "Aliniere paragrafe reader"
STR_HYPHENATION: "Silabisire"
STR_TIME_TO_SLEEP: "Timp până la repaus"
STR_SHOW_HIDDEN_FILES: "Afişează fişierele ascunse"
STR_REMOVE_READ_FROM_RECENTS: "Șterge cărțile citite din lista de recente"
STR_MOVE_FINISHED_TO_READ: "Mută cărţile terminate în folderul Read"
STR_REFRESH_FREQ: "Frecvenţă reîmprospătare"
STR_KOREADER_SYNC: "Sincronizare KOReader"
STR_CHECK_UPDATES: "Căutaţi actualizări"
@@ -100,7 +103,7 @@ STR_KOREADER_PASSWORD: "Parolă KOReader"
STR_FILENAME: "Nume fişier"
STR_BINARY: "Fişier binar"
STR_SET_CREDENTIALS_FIRST: "Vă rugăm să setaţi mai întâi acreditările"
STR_WIFI_CONN_FAILED: "Conexiune WiFi eşuată"
STR_WIFI_CONN_FAILED: "Conexiune Wi-Fi eşuată"
STR_AUTHENTICATING: "Se autentifică..."
STR_AUTH_SUCCESS: "Autentificare reuşită!"
STR_KOREADER_AUTH: "Autentificare KOReader"
@@ -151,6 +154,7 @@ STR_ALIGN_LEFT: "Stânga"
STR_CENTER: "Centru"
STR_ALIGN_RIGHT: "Dreapta"
STR_MIN_1: "1 min"
STR_MIN_3: "3 min"
STR_MIN_5: "5 min"
STR_MIN_10: "10 min"
STR_MIN_15: "15 min"
@@ -230,10 +234,14 @@ STR_THEME_CLASSIC: "Clasic"
STR_THEME_LYRA: "Lyra"
STR_THEME_LYRA_EXTENDED: "Lyra Extended"
STR_SUNLIGHT_FADING_FIX: "Corecţie estompare lumină"
STR_QUICK_RESUME_TIMEOUT: "Reluare rapidă la timeout"
STR_AFTER_TIMEOUT: "După timeout"
STR_REMAP_FRONT_BUTTONS: "Remapare butoane frontale"
STR_OPDS_BROWSER: "Browser OPDS"
STR_COVER_CUSTOM: "Copertă + Personalizat"
STR_QUICK_RESUME: "Reluare rapidă"
STR_MENU_RECENT_BOOKS: "Cărţi recente"
STR_REMOVE_FROM_RECENTS: "Eliminați din Cărţi recente?"
STR_NO_RECENT_BOOKS: "Nicio carte recentă"
STR_CALIBRE_DESC: "Utilizaţi transferurile wireless ale dispozitivului Calibre"
STR_FORGET_AND_REMOVE: "Uitaţi reţeaua şi eliminaţi parola salvată?"
+72 -7
View File
@@ -73,6 +73,7 @@ STR_IMAGES_SUPPRESS: "Скрыть"
STR_SHORT_PWR_BTN: "Короткое нажатие PWR"
STR_ORIENTATION: "Ориентация чтения"
STR_SIDE_BTN_LAYOUT: "Боковые кнопки"
STR_FRONT_BTN_FOLLOW_ORIENTATION: "Ориентировать передние кнопки"
STR_LONG_PRESS_BEHAVIOR: "Долгое нажатие"
STR_LONG_PRESS_BEHAVIOR_OFF: "Ничего"
STR_LONG_PRESS_BEHAVIOR_SKIP: "Пропуск главы"
@@ -85,6 +86,8 @@ STR_PARA_ALIGNMENT: "Выравнивание абзаца"
STR_HYPHENATION: "Перенос слов"
STR_TIME_TO_SLEEP: "Сон через"
STR_SHOW_HIDDEN_FILES: "Показать скрытые файлы"
STR_REMOVE_READ_FROM_RECENTS: "Очищать прочитанные книги из списка недавних"
STR_MOVE_FINISHED_TO_READ: "Перемещать прочитанные книги в папку Read"
STR_REFRESH_FREQ: "Частота обновления"
STR_KOREADER_SYNC: "Синхронизация KOReader"
STR_CHECK_UPDATES: "Проверить обновления"
@@ -130,6 +133,7 @@ STR_ALWAYS: "Всегда"
STR_IGNORE: "Игнорировать"
STR_SLEEP: "Сон"
STR_PAGE_TURN: "Перелистывание"
STR_FORCE_REFRESH: "Обновление экрана"
STR_PORTRAIT: "Портрет"
STR_LANDSCAPE_CW: "Ландшафт (CW)"
STR_INVERTED: "Инверсия"
@@ -151,6 +155,7 @@ STR_ALIGN_LEFT: "По левому краю"
STR_CENTER: "По центру"
STR_ALIGN_RIGHT: "По правому краю"
STR_MIN_1: "1 мин"
STR_MIN_3: "3 мин"
STR_MIN_5: "5 мин"
STR_MIN_10: "10 мин"
STR_MIN_15: "15 мин"
@@ -170,6 +175,7 @@ STR_NO_UPDATE: "Обновлений нет"
STR_UPDATE_FAILED: "Ошибка обновления"
STR_UPDATE_COMPLETE: "Обновление завершено"
STR_POWER_ON_HINT: "Удерживайте кнопку питания для включения"
STR_RESTARTING_HINT: "Перезапуск... Если устройство не перезагружается, удерживайте кнопку питания несколько секунд"
STR_NO_ENTRIES: "Записи не найдены"
STR_DOWNLOADING: "Загрузка..."
STR_DOWNLOAD_FAILED: "Ошибка загрузки"
@@ -178,6 +184,8 @@ STR_UNNAMED: "Без имени"
STR_NO_SERVER_URL: "URL сервера не настроен"
STR_FETCH_FEED_FAILED: "Не удалось получить ленту"
STR_PARSE_FEED_FAILED: "Не удалось обработать ленту"
STR_NEXT_PAGE: "Следующая страница »"
STR_PREV_PAGE: "« Предыдущая страница"
STR_NETWORK_PREFIX: "Сеть: "
STR_IP_ADDRESS_PREFIX: "IP-адрес: "
STR_ERROR_GENERAL_FAILURE: "Ошибка: Общая ошибка"
@@ -225,19 +233,24 @@ STR_EXAMPLE_BOOK: "Название книги"
STR_PREVIEW: "Предпросмотр"
STR_TITLE: "Заглавие"
STR_BATTERY: "Батарея"
STR_XTC_STATUS_BAR: "Строка состояния XTC"
STR_BOTTOM: "Низ"
STR_TOP: "Верх"
STR_UI_THEME: "Тема интерфейса"
STR_THEME_CLASSIC: "Классическая"
STR_THEME_LYRA: "Lyra"
STR_THEME_ROUNDEDRAFF: "RoundedRaff"
STR_THEME_LYRA_EXTENDED: "Lyra Extended"
STR_SUNLIGHT_FADING_FIX: "Компенсация выцветания"
STR_QUICK_RESUME_TIMEOUT: "Быстрое возобновление по таймауту"
STR_AFTER_TIMEOUT: "По таймауту"
STR_REMAP_FRONT_BUTTONS: "Переназначить передние кнопки"
STR_OPDS_BROWSER: "OPDS браузер"
STR_FOOTNOTES: римечания"
STR_NO_FOOTNOTES: "На данной странице нет примечаний"
STR_LINK: "[ссылка]"
STR_SEARCH: оиск"
STR_COVER_CUSTOM: "Обложка + Свой"
STR_QUICK_RESUME: "Быстрое возобновление"
STR_MENU_RECENT_BOOKS: "Недавние книги"
STR_REMOVE_FROM_RECENTS: "Удалить из недавних книг?"
STR_NO_RECENT_BOOKS: "Нет недавних книг"
STR_CALIBRE_DESC: "Использовать беспроводную передачу Calibre"
STR_FORGET_AND_REMOVE: "Забыть сеть и удалить сохранённый пароль?"
@@ -261,8 +274,8 @@ STR_GO_HOME_BUTTON: "На главную"
STR_SYNC_PROGRESS: "Синхронизировать прогресс"
STR_DELETE_CACHE: "Удалить кэш книги"
STR_DELETE: "Удалить"
STR_CHAPTER_PREFIX: "Глава: "
STR_DISPLAY_QR: "Показать страницу в виде QR-кода"
STR_CHAPTER_PREFIX: "Глава: "
STR_PAGES_SEPARATOR: " стр. | "
STR_BOOK_PREFIX: "Книга: "
STR_CALIBRE_URL_HINT: "Для Calibre добавьте /opds к URL"
@@ -293,13 +306,65 @@ STR_BOOK_S_STYLE: "Стиль книги"
STR_EMBEDDED_STYLE: "Встроенный стиль"
STR_FOCUS_READING: "Фокусное чтение"
STR_OPDS_SERVER_URL: "URL OPDS сервера"
STR_SET_SLEEP_COVER: "Обложкой"
STR_FOOTNOTES: "Примечания"
STR_NO_FOOTNOTES: "На данной странице нет примечаний"
STR_LINK: "[ссылка]"
STR_SCREENSHOT_BUTTON: "Сделать снимок экрана"
STR_AUTO_TURN_ENABLED: "Автоперелистывание: "
STR_AUTO_TURN_PAGES_PER_MIN: "Автоперелистывание (стр./мин)"
STR_TILT_PAGE_TURN: "Перелистывание наклоном"
STR_ADD_SERVER: "Добавить сервер"
STR_SERVER_NAME: "Имя сервера"
STR_NO_SERVERS: "Нет настроенных серверов OPDS"
STR_DELETE_SERVER: "Удалить сервер"
STR_DELETE_CONFIRM: "Удалить этот сервер?"
STR_OPDS_SERVERS: "Серверы OPDS"
STR_AUTO_TURN_ENABLED: "Автоперелистывание: "
STR_AUTO_TURN_PAGES_PER_MIN: "Автоперелистывание (стр./мин)"
STR_MANAGE_FONTS: "Управление шрифтами"
STR_FONT_BROWSER: "Обзор шрифтов"
STR_LOADING_FONT_LIST: "Загрузка списка шрифтов..."
STR_NO_FONTS_AVAILABLE: "Шрифты недоступны"
STR_FONT_INSTALLED: "Шрифт установлен!"
STR_FONT_INSTALL_FAILED: "Не удалоь установить шрифт"
STR_INSTALLED: "Установлен"
STR_CONFIRM_DOWNLOAD_PROMPT: "Загрузить?"
STR_SD_CARD_FULL: "Недостаточно места на SD-карте"
STR_FILES_LABEL: "Файлы: "
STR_SIZE_LABEL: "Размер: "
STR_REDOWNLOAD: "Скачать снова"
STR_DOWNLOAD_ALL: "Загрузить все"
STR_UPDATE_ALL: "Обновить всё"
STR_ALL_FONTS_INSTALLED: "Все шрифты установлены!"
STR_UPDATE_AVAILABLE: "Обновить"
STR_CRASH_TITLE: "Сбой системы"
STR_CRASH_DESCRIPTION: "Подробный отчёт сохранён в crash_report.txt. Пожалуйста, приложите этот файл к вашему отчёту об ошибке."
STR_CRASH_REASON: "Причина сбоя:"
STR_CRASH_NO_REASON: "(Причина не указана)"
STR_TILT_PAGE_TURN: "Перелистывание наклоном"
STR_KB_HINT_MOVE_CURSOR: "Нажмите ВЛЕВО или ВПРАВО для перемещения курсора"
STR_KB_HINT_RETURN_CURSOR: "Нажмите ВЛЕВО, чтобы вернуться к позиции курсора"
STR_KB_HINT_HIDE_PASSWORD: "Удерживайте ВПРАВО, затем нажмите [***], чтобы скрыть пароль"
STR_KB_HINT_SHOW_PASSWORD: "Удерживайте ВПРАВО, затем нажмите [abc], чтобы показать пароль"
STR_KB_HINT_TOGGLE_HIDE_PASSWORD: "Нажмите [***], чтобы скрыть пароль"
STR_KB_HINT_TOGGLE_SHOW_PASSWORD: "Нажмите [abc], чтобы показать пароль"
STR_KB_HINT_EDIT_ENTRY: "Удерживайте ВВЕРХ для редактирования записи"
STR_KB_TIPS: "Подсказки:"
STR_KB_HINT_RETURN_KEYBOARD: "Нажмите ВНИЗ, чтобы вернуться к клавиатуре"
STR_KB_HINT_EXIT_URL_MODE: "Нажмите ABC для выхода из режима URL"
STR_KB_HINT_CLEAR_TEXT: "Удерживайте <— чтобы очитисть весь текст"
STR_KB_HINT_SECONDARY_CHAR: "Удерживайте ВЫБРАТЬ для дополнительного символа"
STR_KB_HINT_UPPER_SECONDARY: "Удерживайте ВЫБРАТЬ для ВЕРХНЕГО РЕГИСТРА или дополнительного символа"
STR_KB_HINT_LOWER_SECONDARY: "Удерживайте ВЫБРАТЬ для нижнего регистра или дополнительного символа"
STR_KB_HINT_URL_SNIPPETS: "Нажмите URL для выбора шаблонов"
STR_SD_FIRMWARE_UPDATE: "Обновление системы с SD-карты"
STR_SELECT_FIRMWARE_FILE: "Выберите файл прошивки (.bin)"
STR_NO_BIN_FILES: ".bin файлы не найдены"
STR_VALIDATING_FIRMWARE: "Проверка файла прошивки..."
STR_INVALID_FIRMWARE: "Не подходящий файл прошивки"
STR_FIRMWARE_TOO_LARGE: "Прошивка слишком велика для раздела"
STR_FIRMWARE_TOO_SMALL: "Файл прошивки слишком мал"
STR_FIRMWARE_UPDATE_PROMPT: "Обновить прошивку?"
STR_FIRMWARE_FILE_OPEN_FAILED: "Не могу открыть файл"
STR_FIRMWARE_WRITE_FAILED: "Ошибка записи прошивки"
STR_FIRMWARE_UPDATE_DO_NOT_POWER_OFF: "Не выключайте питание!"
STR_RECOVERY_MODE: "Режим восстановления"
STR_RECOVERY_MODE_HINT: "Поместите firmware.bin в корень SD-карты и выберите его"
+16 -8
View File
@@ -24,7 +24,7 @@ STR_EMPTY_FILE: "Prazna datoteka"
STR_OUT_OF_BOUNDS: "Izven meja"
STR_LOADING: "Nalaganje..."
STR_LOADING_POPUP: "Nalaganje"
STR_WIFI_NETWORKS: "WiFi omrežja"
STR_WIFI_NETWORKS: "Wi-Fi omrežja"
STR_NO_NETWORKS: "Ni najdenih omrežij"
STR_NETWORKS_FOUND: "Najdenih omrežij: %zu"
STR_SCANNING: "Iskanje..."
@@ -36,11 +36,11 @@ STR_SAVE_PASSWORD: "Shranim geslo za naslednjič?"
STR_PRESS_OK_SCAN: "Pritisni OK za ponovno iskanje"
STR_JOIN_NETWORK: "Poveži se v omrežje"
STR_CREATE_HOTSPOT: "Ustvari dostopno točko"
STR_JOIN_DESC: "Poveži se v obstoječe WiFi omrežje"
STR_HOTSPOT_DESC: "Ustvari WiFi omrežje, v katerega se lahko povežejo drugi"
STR_JOIN_DESC: "Poveži se v obstoječe Wi-Fi omrežje"
STR_HOTSPOT_DESC: "Ustvari Wi-Fi omrežje, v katerega se lahko povežejo drugi"
STR_STARTING_HOTSPOT: "Zaganjanje dostopne točke..."
STR_HOTSPOT_MODE: "Način dostopne točke"
STR_CONNECT_WIFI_HINT: "Poveži svojo napravo v to WiFi omrežje"
STR_CONNECT_WIFI_HINT: "Poveži svojo napravo v to Wi-Fi omrežje"
STR_OPEN_URL_HINT: "Odpri ta URL v svojem brskalniku"
STR_OR_HTTP_PREFIX: "ali http://"
STR_SCAN_QR_HINT: "ali skeniraj QR kodo s telefonom:"
@@ -48,13 +48,13 @@ STR_CALIBRE_WIRELESS: "Brezžični Calibre"
STR_CALIBRE_WEB_URL: "Calibre Web URL"
STR_NETWORK_LEGEND: "* = Šifrirano | + = Shranjeno"
STR_MAC_ADDRESS: "MAC naslov:"
STR_CHECKING_WIFI: "Preverjanje WiFi-ja..."
STR_ENTER_WIFI_PASSWORD: "Vnesi WiFi geslo"
STR_CHECKING_WIFI: "Preverjanje Wi-Fi-ja..."
STR_ENTER_WIFI_PASSWORD: "Vnesi Wi-Fi geslo"
STR_TO_PREFIX: "v "
STR_CALIBRE_RECEIVING: "Prejemanje: "
STR_CALIBRE_RECEIVED: "Prejeto: "
STR_CALIBRE_INSTRUCTION_1: "1) Namesti vtičnik CrossPoint Reader"
STR_CALIBRE_INSTRUCTION_2: "2) Bodi v istem WiFi omrežju"
STR_CALIBRE_INSTRUCTION_2: "2) Bodi v istem Wi-Fi omrežju"
STR_CALIBRE_INSTRUCTION_3: "3) V Calibre: \"Pošlji v napravo\""
STR_CALIBRE_INSTRUCTION_4: "\"Med pošiljanjem pusti ta zaslon odprt\""
STR_CAT_DISPLAY: "Zaslon"
@@ -73,6 +73,7 @@ STR_IMAGES_SUPPRESS: "Zatdi"
STR_SHORT_PWR_BTN: "Kratek pritisk na gumb za vklop"
STR_ORIENTATION: "Orientacija branja"
STR_SIDE_BTN_LAYOUT: "Razpored stranskih gumbov"
STR_FRONT_BTN_FOLLOW_ORIENTATION: "Usmeri sprednje gumbe"
STR_LONG_PRESS_SKIP: "Dolgi pritisk za preskok poglavja"
STR_FONT_FAMILY: "Pisava bralnika"
STR_FONT_SIZE: "Velikost pisave"
@@ -82,6 +83,8 @@ STR_PARA_ALIGNMENT: "Poravnava odstavkov"
STR_HYPHENATION: "Deljenje besed"
STR_TIME_TO_SLEEP: "Čas do spanja"
STR_SHOW_HIDDEN_FILES: "Prikaži skrite datoteke"
STR_REMOVE_READ_FROM_RECENTS: "Počisti prebrane knjige s seznama nedavnih"
STR_MOVE_FINISHED_TO_READ: "Premakni prebrane knjige v mapo Read"
STR_REFRESH_FREQ: "Pogostost osveževanja"
STR_KOREADER_SYNC: "KOReader sinhronizacija"
STR_CHECK_UPDATES: "Preveri posodobitve"
@@ -97,7 +100,7 @@ STR_KOREADER_PASSWORD: "KOReader geslo"
STR_FILENAME: "Ime datoteke"
STR_BINARY: "Binarno"
STR_SET_CREDENTIALS_FIRST: "Najprej nastavi podatke za prijavo"
STR_WIFI_CONN_FAILED: "WiFi povezava ni uspela"
STR_WIFI_CONN_FAILED: "Wi-Fi povezava ni uspela"
STR_AUTHENTICATING: "Preverjanje..."
STR_AUTH_SUCCESS: "Uspešna prijava!"
STR_KOREADER_AUTH: "KOReader avtentikacija"
@@ -148,6 +151,7 @@ STR_ALIGN_LEFT: "Levo"
STR_CENTER: "Sredinsko"
STR_ALIGN_RIGHT: "Desno"
STR_MIN_1: "1 min"
STR_MIN_3: "3 min"
STR_MIN_5: "5 min"
STR_MIN_10: "10 min"
STR_MIN_15: "15 min"
@@ -227,10 +231,14 @@ STR_THEME_CLASSIC: "Klasična"
STR_THEME_LYRA: "Lyra"
STR_THEME_LYRA_EXTENDED: "Lyra razširjena"
STR_SUNLIGHT_FADING_FIX: "Popravek bledenja na soncu"
STR_QUICK_RESUME_TIMEOUT: "Hitro nadaljevanje po izteku"
STR_AFTER_TIMEOUT: "Po izteku"
STR_REMAP_FRONT_BUTTONS: "Prenastavi sprednje gumbe"
STR_OPDS_BROWSER: "OPDS brskalnik"
STR_COVER_CUSTOM: "Naslovnica + po meri"
STR_QUICK_RESUME: "Hitro nadaljevanje"
STR_MENU_RECENT_BOOKS: "Zadnje knjige"
STR_REMOVE_FROM_RECENTS: "Odstrani iz zadnjih knjig?"
STR_NO_RECENT_BOOKS: "Ni zadnjih knjig"
STR_CALIBRE_DESC: "Uporabi brezžični prenos Calibre"
STR_FORGET_AND_REMOVE: "Pozabi omrežje in odstrani shranjeno geslo?"
+84 -13
View File
@@ -26,7 +26,7 @@ STR_LOADING: "Cargando..."
STR_LOADING_POPUP: "Cargando"
STR_WIFI_NETWORKS: "Redes Wi-Fi"
STR_NO_NETWORKS: "No hay redes disponibles"
STR_NETWORKS_FOUND: "%zu redes encontradas"
STR_NETWORKS_FOUND: "%zu red(es) encontrada(s)"
STR_SCANNING: "Buscando..."
STR_CONNECTING: "Conectando..."
STR_CONNECTED: "¡Conectado!"
@@ -45,9 +45,9 @@ STR_OPEN_URL_HINT: "Abra esta dirección en su navegador"
STR_OR_HTTP_PREFIX: "o http://"
STR_SCAN_QR_HINT: "o escanee el código QR con su móvil:"
STR_CALIBRE_WIRELESS: "Calibre inalámbrico"
STR_CALIBRE_WEB_URL: "OPDS URL"
STR_NETWORK_LEGEND: "* (Cifrado) | + (Guardado)"
STR_MAC_ADDRESS: "MAC Address:"
STR_CALIBRE_WEB_URL: "URL OPDS"
STR_NETWORK_LEGEND: "* (Con contraseña) | + (Guardada)"
STR_MAC_ADDRESS: "Dirección MAC:"
STR_CHECKING_WIFI: "Verificando Wi-Fi..."
STR_ENTER_WIFI_PASSWORD: "Introduzca la contraseña del Wi-Fi"
STR_TO_PREFIX: "a "
@@ -61,7 +61,7 @@ STR_CAT_DISPLAY: "Pantalla"
STR_CAT_READER: "Lector"
STR_CAT_CONTROLS: "Controles"
STR_CAT_SYSTEM: "Sistema"
STR_SLEEP_SCREEN: "Pantalla de suspensión"
STR_SLEEP_SCREEN: "Fondo de pantalla de suspensión"
STR_SLEEP_COVER_MODE: "Modo de pantalla de suspensión"
STR_HIDE_BATTERY: "Ocultar % de batería"
STR_EXTRA_SPACING: "Espaciado entre párrafos"
@@ -70,13 +70,14 @@ STR_IMAGES: "Imágenes"
STR_IMAGES_DISPLAY: "Mostrar"
STR_IMAGES_PLACEHOLDER: "Reemplazar"
STR_IMAGES_SUPPRESS: "Ocultar"
STR_SHORT_PWR_BTN: "Toque corto botón encendido"
STR_SHORT_PWR_BTN: "Toque corto del encendido"
STR_ORIENTATION: "Orientación"
STR_SIDE_BTN_LAYOUT: "Función botones laterales (lector)"
STR_LONG_PRESS_BEHAVIOR: "Comportamiento al mantener pulsado el botón"
STR_LONG_PRESS_BEHAVIOR_OFF: "Desactivado"
STR_FRONT_BTN_FOLLOW_ORIENTATION: "Orientar botones frontales"
STR_LONG_PRESS_BEHAVIOR: "Al mantener pulsado un botón"
STR_LONG_PRESS_BEHAVIOR_OFF: "No hacer nada"
STR_LONG_PRESS_BEHAVIOR_SKIP: "Saltar capítulo"
STR_LONG_PRESS_BEHAVIOR_ORIENTATION: "Cambio de orientación"
STR_LONG_PRESS_BEHAVIOR_ORIENTATION: "Cambiar orient."
STR_FONT_FAMILY: "Tipografía"
STR_FONT_SIZE: "Tamaño"
STR_LINE_SPACING: "Interlineado"
@@ -85,6 +86,8 @@ STR_PARA_ALIGNMENT: "Ajuste de párrafo"
STR_HYPHENATION: "División de palabras"
STR_TIME_TO_SLEEP: "Auto suspensión"
STR_SHOW_HIDDEN_FILES: "Mostrar archivos ocultos"
STR_REMOVE_READ_FROM_RECENTS: "Borrar libros leídos de la lista de recientes"
STR_MOVE_FINISHED_TO_READ: "Mover libros terminados a la carpeta Read"
STR_REFRESH_FREQ: "Frecuencia de refresco"
STR_KOREADER_SYNC: "Sincronización de KOReader"
STR_CHECK_UPDATES: "Verificar actualizaciones"
@@ -130,6 +133,7 @@ STR_ALWAYS: "Siempre"
STR_IGNORE: "Ignorar"
STR_SLEEP: "Suspender"
STR_PAGE_TURN: "Pasar página"
STR_FORCE_REFRESH: "Refrescar pant."
STR_PORTRAIT: "Vertical"
STR_LANDSCAPE_CW: "Horizontal (horario)"
STR_INVERTED: "Invertido"
@@ -151,6 +155,7 @@ STR_ALIGN_LEFT: "Izquierda"
STR_CENTER: "Centro"
STR_ALIGN_RIGHT: "Derecha"
STR_MIN_1: "1 min."
STR_MIN_3: "3 min."
STR_MIN_5: "5 min."
STR_MIN_10: "10 min."
STR_MIN_15: "15 min."
@@ -169,7 +174,8 @@ STR_UPDATING: "Actualizando..."
STR_NO_UPDATE: "No hay actualizaciones disponibles"
STR_UPDATE_FAILED: "Fallo de actualización"
STR_UPDATE_COMPLETE: "Actualización completada"
STR_POWER_ON_HINT: "Reinicie manteniendo pulsado botón de encendido"
STR_POWER_ON_HINT: "Reinicie manteniendo pulsado el encendido"
STR_RESTARTING_HINT: "Reiniciando... Si no se reinicia, mantenga pulsado el encendido unos segundos."
STR_NO_ENTRIES: "No se encontraron elementos"
STR_DOWNLOADING: "Descargando..."
STR_DOWNLOAD_FAILED: "Fallo de descarga"
@@ -178,12 +184,14 @@ STR_UNNAMED: "Sin nombre"
STR_NO_SERVER_URL: "No se configuró URL de servidor"
STR_FETCH_FEED_FAILED: "Fallo al obtener el feed"
STR_PARSE_FEED_FAILED: "Fallo al procesar el feed"
STR_NEXT_PAGE: "Pág. siguiente »"
STR_PREV_PAGE: "« Pág. anterior"
STR_NETWORK_PREFIX: "Red: "
STR_IP_ADDRESS_PREFIX: "IP: "
STR_ERROR_GENERAL_FAILURE: "Error: Fallo general"
STR_ERROR_NETWORK_NOT_FOUND: "Error: Red no encontrada"
STR_ERROR_CONNECTION_TIMEOUT: "Error: Tiempo de conexión agotado"
STR_SD_CARD: "Tarjeta microSD"
STR_SD_CARD: "Tarjeta SD"
STR_BACK: "« Atrás"
STR_EXIT: "« Salir"
STR_HOME: "« Inicio"
@@ -225,16 +233,24 @@ STR_EXAMPLE_BOOK: "Título del libro"
STR_PREVIEW: "Previsualización"
STR_TITLE: "Título"
STR_BATTERY: "Batería"
STR_XTC_STATUS_BAR: "Barra de estado de XTC"
STR_BOTTOM: "Abajo"
STR_TOP: "Arriba"
STR_UI_THEME: "Interfaz"
STR_THEME_CLASSIC: "Clásico"
STR_THEME_LYRA: "Lyra"
STR_THEME_LYRA_EXTENDED: "Lyra Extendido"
STR_THEME_ROUNDEDRAFF: "RoundedRaff"
STR_THEME_LYRA_EXTENDED: "Lyra Extendido"
STR_SUNLIGHT_FADING_FIX: "Corrección de desvanecimiento"
STR_QUICK_RESUME_TIMEOUT: "Reanudación rápida tras tiempo"
STR_AFTER_TIMEOUT: "Tras tiempo"
STR_REMAP_FRONT_BUTTONS: "Reconfigurar botones frontales"
STR_OPDS_BROWSER: "Navegador OPDS"
STR_SEARCH: "Buscar"
STR_COVER_CUSTOM: "Portada + Pers."
STR_QUICK_RESUME: "Reanudación rápida"
STR_MENU_RECENT_BOOKS: "Libros recientes"
STR_REMOVE_FROM_RECENTS: "¿Eliminar de Libros recientes?"
STR_NO_RECENT_BOOKS: "No hay libros recientes"
STR_CALIBRE_DESC: "Transferir contenido a este dispositivo"
STR_FORGET_AND_REMOVE: "¿Desea olvidar la red y la contraseña guardada?"
@@ -290,10 +306,65 @@ STR_BOOK_S_STYLE: "Estilo del libro"
STR_EMBEDDED_STYLE: "Estilo integrado"
STR_FOCUS_READING: "Lectura enfocada"
STR_OPDS_SERVER_URL: "URL del servidor OPDS"
STR_SET_SLEEP_COVER: "Pant. sus."
STR_FOOTNOTES: "Pie de página"
STR_NO_FOOTNOTES: "No hay notas al pie de esta página"
STR_LINK: "[enlace]"
STR_SCREENSHOT_BUTTON: "Tomar captura de pantalla"
STR_TILT_PAGE_TURN: "Pasar página inclinando"
STR_ADD_SERVER: "Agregar servidor"
STR_SERVER_NAME: "Nombre de servidor"
STR_NO_SERVERS: "No se configuraron servidores OPDS"
STR_DELETE_SERVER: "Borrar servidor"
STR_DELETE_CONFIRM: "¿Borrar este servidor?"
STR_OPDS_SERVERS: "Servidores OPDS"
STR_AUTO_TURN_ENABLED: "Avance activado: "
STR_AUTO_TURN_PAGES_PER_MIN: "Avance auto. (pág./min)"
STR_MANAGE_FONTS: "Gestionar tipografías"
STR_FONT_BROWSER: "Navegador de tipografías"
STR_LOADING_FONT_LIST: "Cargando lista de tipografías..."
STR_NO_FONTS_AVAILABLE: "No hay tipografías disponibles"
STR_FONT_INSTALLED: "¡Se instaló la tipografía!"
STR_FONT_INSTALL_FAILED: "Falló la instalación de tipografía"
STR_INSTALLED: "Instalada"
STR_CONFIRM_DOWNLOAD_PROMPT: "¿Descargar?"
STR_SD_CARD_FULL: "No hay espacio en la tarjeta SD"
STR_FILES_LABEL: "Archivos: "
STR_SIZE_LABEL: "Tamaño: "
STR_REDOWNLOAD: "Redescargar"
STR_DOWNLOAD_ALL: "Descargar todas"
STR_UPDATE_ALL: "Actualizar todas"
STR_ALL_FONTS_INSTALLED: "¡Se instalaron todas las tipografías!"
STR_UPDATE_AVAILABLE: "Actualizar"
STR_CRASH_TITLE: "Fallo del sistema"
STR_CRASH_DESCRIPTION: "Se guardó un informe detallado en crash_report.txt. Inclúyalo al avisar sobre el error."
STR_CRASH_REASON: "Razón del fallo:"
STR_CRASH_NO_REASON: "(No se registró una razón)."
STR_TILT_PAGE_TURN: "Pasar página inclinando"
STR_KB_HINT_MOVE_CURSOR: "Pulse IZQ. o DCHA. para mover el cursor"
STR_KB_HINT_RETURN_CURSOR: "Pulse IZQ. para volver a la posición del cursor"
STR_KB_HINT_HIDE_PASSWORD: "Pulse DCHA., luego [***] para ocultar la contraseña"
STR_KB_HINT_SHOW_PASSWORD: "Pulse DCHA., luego [abc] para mostrar la contraseña"
STR_KB_HINT_TOGGLE_HIDE_PASSWORD: "Pulse [***] para ocultar la contraseña"
STR_KB_HINT_TOGGLE_SHOW_PASSWORD: "Pulse [abc] para mostrar la contraseña"
STR_KB_HINT_EDIT_ENTRY: "Mantenga SUBIR para editar la entrada"
STR_KB_TIPS: "Consejos: "
STR_KB_HINT_RETURN_KEYBOARD: "Pulse BAJAR para volver al teclado"
STR_KB_HINT_EXIT_URL_MODE: "Pulse ABC para salir del modo URL"
STR_KB_HINT_CLEAR_TEXT: "Mantenga RETROCESO para borrar todo"
STR_KB_HINT_SECONDARY_CHAR: "Mantenga SELECC. para otros caracteres"
STR_KB_HINT_UPPER_SECONDARY: "Mantenga SELECC. para MAYÚS./otros caracteres"
STR_KB_HINT_LOWER_SECONDARY: "Mantenga SELECC. para minús./otros caracteres"
STR_KB_HINT_URL_SNIPPETS: "Pulse URL para ingresar fragmentos"
STR_SD_FIRMWARE_UPDATE: "Actualizar firmware por tarj. SD"
STR_SELECT_FIRMWARE_FILE: "Elija el archivo de firmware (.bin)"
STR_NO_BIN_FILES: "No se encontraron archivos .bin"
STR_VALIDATING_FIRMWARE: "Validando el firmware..."
STR_INVALID_FIRMWARE: "Archivo de firmware no válido"
STR_FIRMWARE_TOO_LARGE: "Firmware demasiado grande p/la partición"
STR_FIRMWARE_TOO_SMALL: "Firmware demasiado pequeño"
STR_FIRMWARE_UPDATE_PROMPT: "¿Actualizar el firmware?"
STR_FIRMWARE_FILE_OPEN_FAILED: "No se puede abrir el archivo"
STR_FIRMWARE_WRITE_FAILED: "Falló la escritura del firmware"
STR_FIRMWARE_UPDATE_DO_NOT_POWER_OFF: "¡No apague el dispositivo!"
STR_RECOVERY_MODE: "Modo de recuperación"
STR_RECOVERY_MODE_HINT: "Ponga firmware.bin en la raíz de la tarj. SD y selecciónelo"
+9 -1
View File
@@ -49,7 +49,7 @@ STR_CALIBRE_WEB_URL: "OPDS URL"
STR_NETWORK_LEGEND: "* = Krypterad | + = Sparad"
STR_MAC_ADDRESS: "MAC-adress:"
STR_CHECKING_WIFI: "Kontrollerar trådlöst nätverk…"
STR_ENTER_WIFI_PASSWORD: "Skriv in WiFi-lösenord"
STR_ENTER_WIFI_PASSWORD: "Skriv in Wi-Fi-lösenord"
STR_TO_PREFIX: "till "
STR_CALIBRE_RECEIVING: "Tar emot:"
STR_CALIBRE_RECEIVED: "Mottaget:"
@@ -73,6 +73,7 @@ STR_IMAGES_SUPPRESS: "Dölj"
STR_SHORT_PWR_BTN: "Kort strömknappsklick"
STR_ORIENTATION: "Läsrikting"
STR_SIDE_BTN_LAYOUT: "Sidoknappslayout (Läsare)"
STR_FRONT_BTN_FOLLOW_ORIENTATION: "Rikta främre knappar"
STR_LONG_PRESS_BEHAVIOR: "Beteende vid lång knapptryckning"
STR_LONG_PRESS_BEHAVIOR_OFF: "AV"
STR_LONG_PRESS_BEHAVIOR_SKIP: "Hoppa över kapitel"
@@ -85,6 +86,8 @@ STR_PARA_ALIGNMENT: "Eboksläsarens paragrafjustering"
STR_HYPHENATION: "Avstavning"
STR_TIME_TO_SLEEP: "Tid för att gå i vila"
STR_SHOW_HIDDEN_FILES: "Visa dolda filer"
STR_REMOVE_READ_FROM_RECENTS: "Rensa lästa böcker från listan Senaste"
STR_MOVE_FINISHED_TO_READ: "Flytta lästa böcker till Read-mappen"
STR_REFRESH_FREQ: "Uppdateringsfrekvens"
STR_KOREADER_SYNC: "KOReader-synkronisering"
STR_CHECK_UPDATES: "Kolla efter uppdateringar"
@@ -152,6 +155,7 @@ STR_ALIGN_LEFT: "Vänster"
STR_CENTER: "Mitten"
STR_ALIGN_RIGHT: "Höger"
STR_MIN_1: "1 min"
STR_MIN_3: "3 min"
STR_MIN_5: "5 min"
STR_MIN_10: "10 min"
STR_MIN_15: "15 min"
@@ -238,11 +242,15 @@ STR_THEME_LYRA: "Lyra"
STR_THEME_ROUNDEDRAFF: "RoundedRaff"
STR_THEME_LYRA_EXTENDED: "Lyra utökad"
STR_SUNLIGHT_FADING_FIX: "Fix för solskensmattning"
STR_QUICK_RESUME_TIMEOUT: "Snabb återupptagning efter timeout"
STR_AFTER_TIMEOUT: "Efter timeout"
STR_REMAP_FRONT_BUTTONS: "Ändra frontknappar"
STR_OPDS_BROWSER: "OPDS-webbläsare"
STR_SEARCH: "Sök"
STR_COVER_CUSTOM: "Omslag + Valfri"
STR_QUICK_RESUME: "Snabb återupptagning"
STR_MENU_RECENT_BOOKS: "Senaste böckerna"
STR_REMOVE_FROM_RECENTS: "Ta bort från Senaste böckerna?"
STR_NO_RECENT_BOOKS: "Inga senaste böcker"
STR_CALIBRE_DESC: "Använd Calibres trådlösa enhetsöverföring"
STR_FORGET_AND_REMOVE: "Glöm nätverk och ta bort sparat lösenord?"
+15 -7
View File
@@ -23,7 +23,7 @@ STR_EMPTY_FILE: "Boş dosya"
STR_OUT_OF_BOUNDS: "Sınırların dışında"
STR_LOADING: "Yükleniyor..."
STR_LOADING_POPUP: "Yükleniyor"
STR_WIFI_NETWORKS: "WiFi Ağları"
STR_WIFI_NETWORKS: "Wi-Fi Ağları"
STR_NO_NETWORKS: "Ağ bulunamadı"
STR_NETWORKS_FOUND: "%zu ağ bulundu"
STR_SCANNING: "Tarıyor..."
@@ -35,11 +35,11 @@ STR_SAVE_PASSWORD: "Şifre kaydedilsin mi?"
STR_PRESS_OK_SCAN: "Tekrar taramak için OK'e basın"
STR_JOIN_NETWORK: "Bir Ağa Katıl"
STR_CREATE_HOTSPOT: "Erişim Noktası Oluştur"
STR_JOIN_DESC: "Mevcut bir WiFi ağına bağlan"
STR_JOIN_DESC: "Mevcut bir Wi-Fi ağına bağlan"
STR_HOTSPOT_DESC: "Başkalarının katılabileceği ağ oluştur"
STR_STARTING_HOTSPOT: "Erişim Noktası Başlatılıyor..."
STR_HOTSPOT_MODE: "Erişim Noktası Modu"
STR_CONNECT_WIFI_HINT: "Cihazınızı bu WiFi ağına bağlayın"
STR_CONNECT_WIFI_HINT: "Cihazınızı bu Wi-Fi ağına bağlayın"
STR_OPEN_URL_HINT: "Tarayıcınızda bu adresi açın"
STR_OR_HTTP_PREFIX: "veya http://"
STR_SCAN_QR_HINT: "veya telefonunuzla QR kodu tarayın:"
@@ -47,13 +47,13 @@ STR_CALIBRE_WIRELESS: "Calibre Kablosuz"
STR_CALIBRE_WEB_URL: "OPDS URL"
STR_NETWORK_LEGEND: "* = Şifreli | + = Kayıtlı"
STR_MAC_ADDRESS: "MAC adresi:"
STR_CHECKING_WIFI: "WiFi kontrol ediliyor..."
STR_ENTER_WIFI_PASSWORD: "WiFi Şifresini Girin"
STR_CHECKING_WIFI: "Wi-Fi kontrol ediliyor..."
STR_ENTER_WIFI_PASSWORD: "Wi-Fi Şifresini Girin"
STR_TO_PREFIX: "Ağ: "
STR_CALIBRE_RECEIVING: "Alınıyor: "
STR_CALIBRE_RECEIVED: "Alındı: "
STR_CALIBRE_INSTRUCTION_1: "1) CrossPoint Reader eklentisini kurun"
STR_CALIBRE_INSTRUCTION_2: "2) Aynı WiFi ağında olun"
STR_CALIBRE_INSTRUCTION_2: "2) Aynı Wi-Fi ağında olun"
STR_CALIBRE_INSTRUCTION_3: "3) Calibre'de: \"Cihaza gönder\""
STR_CALIBRE_INSTRUCTION_4: "\"Gönderim sırasında bu ekranı açık tutun\""
STR_CAT_DISPLAY: "Ekran"
@@ -68,6 +68,7 @@ STR_TEXT_AA: "Metin Yumuşatma (AA)"
STR_SHORT_PWR_BTN: "Kısa Güç Tuşu Tıklaması"
STR_ORIENTATION: "Okuma Yönü"
STR_SIDE_BTN_LAYOUT: "Yan Tuş Dizilimi (okuyucu)"
STR_FRONT_BTN_FOLLOW_ORIENTATION: "Ön düğmeleri yönlendir"
STR_LONG_PRESS_BEHAVIOR: "Long-press button behavior"
STR_LONG_PRESS_BEHAVIOR_OFF: "OFF"
STR_LONG_PRESS_BEHAVIOR_SKIP: "Chapter skip"
@@ -80,6 +81,8 @@ STR_PARA_ALIGNMENT: "Okuyucu Paragraf Hizalaması"
STR_HYPHENATION: "Hecelerden Ayırma"
STR_TIME_TO_SLEEP: "Uykuya Geçme Süresi"
STR_SHOW_HIDDEN_FILES: "Gizli Dosyaları Göster"
STR_REMOVE_READ_FROM_RECENTS: "Okunan kitapları Son listesinden temizle"
STR_MOVE_FINISHED_TO_READ: "Biten kitapları Read klasörüne taşı"
STR_REFRESH_FREQ: "Yenileme Sıklığı"
STR_KOREADER_SYNC: "KOReader Senkronizasyonu"
STR_CHECK_UPDATES: "Güncellemeleri denetle"
@@ -95,7 +98,7 @@ STR_KOREADER_PASSWORD: "KOReader Şifresi"
STR_FILENAME: "Dosya Adı"
STR_BINARY: "İkili"
STR_SET_CREDENTIALS_FIRST: "Önce kimlik bilgilerini ayarlayın"
STR_WIFI_CONN_FAILED: "WiFi bağlantısı başarısız"
STR_WIFI_CONN_FAILED: "Wi-Fi bağlantısı başarısız"
STR_AUTHENTICATING: "Kimlik doğrulanıyor..."
STR_AUTH_SUCCESS: "Kimlik doğrulama başarılı!"
STR_KOREADER_AUTH: "KOReader Doğrulaması"
@@ -146,6 +149,7 @@ STR_ALIGN_LEFT: "Sola Yasla"
STR_CENTER: "Ortala"
STR_ALIGN_RIGHT: "Sağa Yasla"
STR_MIN_1: "1 dak"
STR_MIN_3: "3 dak"
STR_MIN_5: "5 dak"
STR_MIN_10: "10 dak"
STR_MIN_15: "15 dak"
@@ -207,10 +211,14 @@ STR_THEME_CLASSIC: "Klasik"
STR_THEME_LYRA: "Lyra"
STR_THEME_LYRA_EXTENDED: "Lyra Genişletilmiş"
STR_SUNLIGHT_FADING_FIX: "Güneş Işığı Solma Düzeltmesi"
STR_QUICK_RESUME_TIMEOUT: "Zaman aşımında Hızlı Devam"
STR_AFTER_TIMEOUT: "Zaman aşımında"
STR_REMAP_FRONT_BUTTONS: "Ön Tuşları Yeniden Ata"
STR_OPDS_BROWSER: "OPDS Tarayıcı"
STR_COVER_CUSTOM: "Kapak + Özel"
STR_QUICK_RESUME: "Hızlı Devam"
STR_MENU_RECENT_BOOKS: "Son Kitaplar"
STR_REMOVE_FROM_RECENTS: "Son Kitaplar listesinden kaldırılsın mı?"
STR_NO_RECENT_BOOKS: "Son okunan kitap yok"
STR_CALIBRE_DESC: "Calibre kablosuz cihaz transferini kullan"
STR_FORGET_AND_REMOVE: "Ağı unut ve kayıtlı şifreyi sil?"
+15 -7
View File
@@ -24,7 +24,7 @@ STR_EMPTY_FILE: "Порожній файл"
STR_OUT_OF_BOUNDS: "Поза межами"
STR_LOADING: "Завантаження..."
STR_LOADING_POPUP: "Завантаження"
STR_WIFI_NETWORKS: "Мережі WiFi"
STR_WIFI_NETWORKS: "Мережі Wi-Fi"
STR_NO_NETWORKS: "Мереж не знайдено"
STR_NETWORKS_FOUND: "мереж %zu"
STR_SCANNING: "Сканування..."
@@ -36,11 +36,11 @@ STR_SAVE_PASSWORD: "Зберегти пароль на наступний раз
STR_PRESS_OK_SCAN: "Натисніть OK для повторного сканування"
STR_JOIN_NETWORK: "Приєднатися до мережі"
STR_CREATE_HOTSPOT: "Створити точку доступу"
STR_JOIN_DESC: "Підключитися до існуючої мережі WiFi"
STR_JOIN_DESC: "Підключитися до існуючої мережі Wi-Fi"
STR_HOTSPOT_DESC: "Дозволити іншим підключення по Wi-Fi"
STR_STARTING_HOTSPOT: "Запуск точки доступу..."
STR_HOTSPOT_MODE: "Режим точки доступу"
STR_CONNECT_WIFI_HINT: "Підключіть пристрій до Цієї мережі WiFi"
STR_CONNECT_WIFI_HINT: "Підключіть пристрій до цієї мережі Wi-Fi"
STR_OPEN_URL_HINT: "Відкрийте посилання у своєму браузері"
STR_OR_HTTP_PREFIX: "або http://"
STR_SCAN_QR_HINT: "або відскануйте QR-код телефоном:"
@@ -48,13 +48,13 @@ STR_CALIBRE_WIRELESS: "Отримати з Calibre"
STR_CALIBRE_WEB_URL: "OPDS URL"
STR_NETWORK_LEGEND: "* = Зашифровано | + = Збережено"
STR_MAC_ADDRESS: "MAC адреса:"
STR_CHECKING_WIFI: "Перевірка WiFi..."
STR_ENTER_WIFI_PASSWORD: "Введіть пароль WiFi"
STR_CHECKING_WIFI: "Перевірка Wi-Fi..."
STR_ENTER_WIFI_PASSWORD: "Введіть пароль Wi-Fi"
STR_TO_PREFIX: "до "
STR_CALIBRE_RECEIVING: "Отримання: "
STR_CALIBRE_RECEIVED: "Отримано: "
STR_CALIBRE_INSTRUCTION_1: "1) Встановіть плагін CrossPoint Reader"
STR_CALIBRE_INSTRUCTION_2: "2) Перебувайте в тій самій мережі WiFi"
STR_CALIBRE_INSTRUCTION_2: "2) Перебувайте в тій самій мережі Wi-Fi"
STR_CALIBRE_INSTRUCTION_3: "3) В Calibre: \"Надіслати на пристрій\""
STR_CALIBRE_INSTRUCTION_4: "\"Тримайте цей екран відкритим під час надсилання\""
STR_CAT_DISPLAY: "Екран"
@@ -73,6 +73,7 @@ STR_IMAGES_SUPPRESS: "Приховати"
STR_SHORT_PWR_BTN: "Короткий натиск кн. живл."
STR_ORIENTATION: "Орієнтація читання"
STR_SIDE_BTN_LAYOUT: "Схема бічних кнопок"
STR_FRONT_BTN_FOLLOW_ORIENTATION: "Орієнтувати передні кнопки"
STR_LONG_PRESS_BEHAVIOR: "Поведінка при довгому настику"
STR_LONG_PRESS_BEHAVIOR_OFF: "Немає"
STR_LONG_PRESS_BEHAVIOR_SKIP: "Наступ. розділ (утримув.)"
@@ -85,6 +86,8 @@ STR_PARA_ALIGNMENT: "Вирівнювання тексту"
STR_HYPHENATION: "Перенесення слів"
STR_TIME_TO_SLEEP: "Перехід в режим сну"
STR_SHOW_HIDDEN_FILES: "Показати приховані файли"
STR_REMOVE_READ_FROM_RECENTS: "Очищати прочитані книги зі списку останніх"
STR_MOVE_FINISHED_TO_READ: "Переміщати прочитані книги до теки Read"
STR_REFRESH_FREQ: "Частота оновлення екрану"
STR_KOREADER_SYNC: "Синхронізація KOReader"
STR_CHECK_UPDATES: "Перевірити оновлення системи"
@@ -100,7 +103,7 @@ STR_KOREADER_PASSWORD: "Пароль KOReader"
STR_FILENAME: "Ім'я файлу"
STR_BINARY: "Побайтово"
STR_SET_CREDENTIALS_FIRST: "Спочатку вкажіть облікові дані"
STR_WIFI_CONN_FAILED: "Помилка підключення WiFi"
STR_WIFI_CONN_FAILED: "Помилка підключення Wi-Fi"
STR_AUTHENTICATING: "Автентифікація..."
STR_AUTH_SUCCESS: "Успішно автентифіковано!"
STR_KOREADER_AUTH: "Автентифікація KOReader"
@@ -152,6 +155,7 @@ STR_ALIGN_LEFT: "За лівим краєм"
STR_CENTER: "По центру"
STR_ALIGN_RIGHT: "За правим краєм"
STR_MIN_1: "1 хв"
STR_MIN_3: "3 хв"
STR_MIN_5: "5 хв"
STR_MIN_10: "10 хв"
STR_MIN_15: "15 хв"
@@ -238,11 +242,15 @@ STR_THEME_LYRA: "Lyra"
STR_THEME_ROUNDEDRAFF: "RoundedRaff"
STR_THEME_LYRA_EXTENDED: "Lyra Extended"
STR_SUNLIGHT_FADING_FIX: "Виправлення вицвітання на сонці"
STR_QUICK_RESUME_TIMEOUT: "Швидке продовження після таймауту"
STR_AFTER_TIMEOUT: "Після таймауту"
STR_REMAP_FRONT_BUTTONS: "Налаштувати передні кнопки"
STR_OPDS_BROWSER: "Браузер OPDS"
STR_SEARCH: "Пошук"
STR_COVER_CUSTOM: "Обкл. + власне"
STR_QUICK_RESUME: "Швидке продовження"
STR_MENU_RECENT_BOOKS: "Останні книги"
STR_REMOVE_FROM_RECENTS: "Видалити з останніх книг?"
STR_NO_RECENT_BOOKS: "Немає останніх книг"
STR_CALIBRE_DESC: "Бездротова передача на пристрій із Calibre"
STR_FORGET_AND_REMOVE: "Забути мережу та видалити збережений пароль?"
+43 -49
View File
@@ -4,10 +4,10 @@
#include <HalStorage.h>
#include <JPEGDEC.h>
#include <Logging.h>
#include <Memory.h>
#include <cstdio>
#include <cstring>
#include <new>
#include "BitmapHelpers.h"
@@ -211,26 +211,26 @@ struct BmpConvertCtx {
// Accumulates one MCU row (up to MAX_MCU_HEIGHT source rows × srcWidth pixels)
// Filled column-by-column as JPEGDEC callbacks arrive for the same MCU row
uint8_t* mcuBuf;
std::unique_ptr<uint8_t[]> mcuBuf;
// Y-axis area averaging accumulators (needsScaling only)
int currentOutY;
uint32_t nextOutY_srcStart; // 16.16 fixed-point boundary for the next output row
uint32_t* rowAccum;
uint32_t* rowCount;
std::unique_ptr<uint32_t[]> rowAccum;
std::unique_ptr<uint32_t[]> rowCount;
uint8_t* bmpRow;
std::unique_ptr<uint8_t[]> bmpRow;
AtkinsonDitherer* atkinsonDitherer;
FloydSteinbergDitherer* fsDitherer;
Atkinson1BitDitherer* atkinson1BitDitherer;
std::unique_ptr<AtkinsonDitherer> atkinsonDitherer;
std::unique_ptr<FloydSteinbergDitherer> fsDitherer;
std::unique_ptr<Atkinson1BitDitherer> atkinson1BitDitherer;
bool error;
};
// Write a fully-assembled output row (grayscale bytes, length outWidth) to BMP
static void writeOutputRow(BmpConvertCtx* ctx, const uint8_t* srcRow, int outY) {
memset(ctx->bmpRow, 0, ctx->bytesPerRow);
memset(ctx->bmpRow.get(), 0, ctx->bytesPerRow);
if (USE_8BIT_OUTPUT && !ctx->oneBit) {
for (int x = 0; x < ctx->outWidth; x++) {
@@ -262,12 +262,12 @@ static void writeOutputRow(BmpConvertCtx* ctx, const uint8_t* srcRow, int outY)
ctx->fsDitherer->nextRow();
}
ctx->bmpOut->write(ctx->bmpRow, ctx->bytesPerRow);
ctx->bmpOut->write(ctx->bmpRow.get(), ctx->bytesPerRow);
}
// Flush one scaled output row from Y-axis accumulators and advance currentOutY
static void flushScaledRow(BmpConvertCtx* ctx) {
memset(ctx->bmpRow, 0, ctx->bytesPerRow);
memset(ctx->bmpRow.get(), 0, ctx->bytesPerRow);
if (USE_8BIT_OUTPUT && !ctx->oneBit) {
for (int x = 0; x < ctx->outWidth; x++) {
@@ -301,7 +301,7 @@ static void flushScaledRow(BmpConvertCtx* ctx) {
ctx->fsDitherer->nextRow();
}
ctx->bmpOut->write(ctx->bmpRow, ctx->bytesPerRow);
ctx->bmpOut->write(ctx->bmpRow.get(), ctx->bytesPerRow);
ctx->currentOutY++;
}
@@ -324,7 +324,7 @@ int bmpDrawCallback(JPEGDRAW* pDraw) {
for (int r = 0; r < blockH && r < MAX_MCU_HEIGHT; r++) {
const int copyW = (blockX + validW <= ctx->srcWidth) ? validW : (ctx->srcWidth - blockX);
if (copyW <= 0) continue;
memcpy(ctx->mcuBuf + r * ctx->srcWidth + blockX, pixels + r * stride, copyW);
memcpy(ctx->mcuBuf.get() + r * ctx->srcWidth + blockX, pixels + r * stride, copyW);
}
// Wait for the last MCU column before processing any rows
@@ -334,7 +334,7 @@ int bmpDrawCallback(JPEGDRAW* pDraw) {
const int endRow = blockY + blockH;
for (int y = blockY; y < endRow && y < ctx->srcHeight; y++) {
const uint8_t* srcRow = ctx->mcuBuf + (y - blockY) * ctx->srcWidth;
const uint8_t* srcRow = ctx->mcuBuf.get() + (y - blockY) * ctx->srcWidth;
if (!ctx->needsScaling) {
// 1:1 — outWidth == srcWidth, write directly
@@ -364,8 +364,8 @@ int bmpDrawCallback(JPEGDRAW* pDraw) {
flushScaledRow(ctx);
ctx->nextOutY_srcStart = static_cast<uint32_t>(ctx->currentOutY + 1) * ctx->scaleY_fp;
if (srcY_fp >= ctx->nextOutY_srcStart) continue;
memset(ctx->rowAccum, 0, ctx->outWidth * sizeof(uint32_t));
memset(ctx->rowCount, 0, ctx->outWidth * sizeof(uint32_t));
memset(ctx->rowAccum.get(), 0, ctx->outWidth * sizeof(uint32_t));
memset(ctx->rowCount.get(), 0, ctx->outWidth * sizeof(uint32_t));
}
}
}
@@ -387,19 +387,20 @@ bool JpegToBmpConverter::jpegFileToBmpStreamInternal(FsFile& jpegFile, Print& bm
s_jpegFile = &jpegFile;
JPEGDEC* jpeg = new (std::nothrow) JPEGDEC();
const auto jpeg = makeUniqueNoThrow<JPEGDEC>();
if (!jpeg) {
LOG_ERR("JPG", "Failed to allocate JPEG decoder");
LOG_ERR("JPG", "OOM: JPEG decoder");
return false;
}
int rc = jpeg->open("", bmpJpegOpen, bmpJpegClose, bmpJpegRead, bmpJpegSeek, bmpDrawCallback);
if (rc != 1) {
LOG_ERR("JPG", "JPEG open failed (err=%d)", jpeg->getLastError());
delete jpeg;
return false;
}
const ScopedCleanup cleanup{[&jpeg]() { jpeg->close(); }};
const int srcWidth = jpeg->getWidth();
const int srcHeight = jpeg->getHeight();
@@ -411,8 +412,6 @@ bool JpegToBmpConverter::jpegFileToBmpStreamInternal(FsFile& jpegFile, Print& bm
if (srcWidth <= 0 || srcHeight <= 0 || srcWidth > MAX_IMAGE_WIDTH || srcHeight > MAX_IMAGE_HEIGHT) {
LOG_DBG("JPG", "Image too large or invalid (%dx%d), max supported: %dx%d", srcWidth, srcHeight, MAX_IMAGE_WIDTH,
MAX_IMAGE_HEIGHT);
jpeg->close();
delete jpeg;
return false;
}
@@ -472,54 +471,49 @@ bool JpegToBmpConverter::jpegFileToBmpStreamInternal(FsFile& jpegFile, Print& bm
ctx.scaleY_fp = scaleY_fp;
ctx.error = false;
// RAII guard: frees all heap resources on any return path
struct Cleanup {
BmpConvertCtx& ctx;
JPEGDEC* jpeg;
~Cleanup() {
delete[] ctx.rowAccum;
delete[] ctx.rowCount;
delete ctx.atkinsonDitherer;
delete ctx.fsDitherer;
delete ctx.atkinson1BitDitherer;
free(ctx.mcuBuf);
free(ctx.bmpRow);
jpeg->close();
delete jpeg;
}
} cleanup{ctx, jpeg};
// MCU row buffer: MAX_MCU_HEIGHT rows × srcWidth columns of grayscale
ctx.mcuBuf = static_cast<uint8_t*>(malloc(MAX_MCU_HEIGHT * srcWidth));
ctx.mcuBuf = makeUniqueNoThrow<uint8_t[]>(MAX_MCU_HEIGHT * srcWidth);
if (!ctx.mcuBuf) {
LOG_ERR("JPG", "Failed to allocate MCU buffer (%d bytes)", MAX_MCU_HEIGHT * srcWidth);
LOG_ERR("JPG", "OOM: MCU buffer (%d bytes)", MAX_MCU_HEIGHT * srcWidth);
return false;
}
memset(ctx.mcuBuf, 0, MAX_MCU_HEIGHT * srcWidth);
memset(ctx.mcuBuf.get(), 0, MAX_MCU_HEIGHT * srcWidth);
ctx.bmpRow = static_cast<uint8_t*>(malloc(bytesPerRow));
ctx.bmpRow = makeUniqueNoThrow<uint8_t[]>(bytesPerRow);
if (!ctx.bmpRow) {
LOG_ERR("JPG", "Failed to allocate BMP row buffer");
LOG_ERR("JPG", "OOM: BMP row buffer");
return false;
}
if (needsScaling) {
ctx.rowAccum = new (std::nothrow) uint32_t[outWidth]();
ctx.rowCount = new (std::nothrow) uint32_t[outWidth]();
ctx.rowAccum = makeUniqueNoThrow<uint32_t[]>(outWidth);
ctx.rowCount = makeUniqueNoThrow<uint32_t[]>(outWidth);
if (!ctx.rowAccum || !ctx.rowCount) {
LOG_ERR("JPG", "Failed to allocate scaling buffers");
LOG_ERR("JPG", "OOM: scaling buffers");
return false;
}
ctx.nextOutY_srcStart = scaleY_fp;
}
if (oneBit) {
ctx.atkinson1BitDitherer = new (std::nothrow) Atkinson1BitDitherer(outWidth);
ctx.atkinson1BitDitherer = makeUniqueNoThrow<Atkinson1BitDitherer>(outWidth);
if (!ctx.atkinson1BitDitherer) {
LOG_ERR("JPG", "OOM: Atkinson1BitDitherer");
return false;
}
} else if (!USE_8BIT_OUTPUT) {
if (USE_ATKINSON) {
ctx.atkinsonDitherer = new (std::nothrow) AtkinsonDitherer(outWidth);
ctx.atkinsonDitherer = makeUniqueNoThrow<AtkinsonDitherer>(outWidth);
if (!ctx.atkinsonDitherer) {
LOG_ERR("JPG", "OOM: AtkinsonDitherer");
return false;
}
} else if (USE_FLOYD_STEINBERG) {
ctx.fsDitherer = new (std::nothrow) FloydSteinbergDitherer(outWidth);
ctx.fsDitherer = makeUniqueNoThrow<FloydSteinbergDitherer>(outWidth);
if (!ctx.fsDitherer) {
LOG_ERR("JPG", "OOM: FloydSteinbergDitherer");
return false;
}
}
}
+3 -3
View File
@@ -6,7 +6,7 @@
#include <ObfuscationUtils.h>
#include <Serialization.h>
#include "../../src/JsonSettingsIO.h"
#include "KOReaderJsonIO.h"
// Initialize the static instance
KOReaderCredentialStore KOReaderCredentialStore::instance;
@@ -36,7 +36,7 @@ void legacyDeobfuscate(std::string& data) {
bool KOReaderCredentialStore::saveToFile() const {
Storage.mkdir("/.crosspoint");
return JsonSettingsIO::saveKOReader(*this, KOREADER_FILE_JSON);
return KOReaderJsonIO::save(*this, KOREADER_FILE_JSON);
}
bool KOReaderCredentialStore::loadFromFile() {
@@ -45,7 +45,7 @@ bool KOReaderCredentialStore::loadFromFile() {
String json = Storage.readFile(KOREADER_FILE_JSON);
if (!json.isEmpty()) {
bool resave = false;
bool result = JsonSettingsIO::loadKOReader(*this, json.c_str(), &resave);
bool result = KOReaderJsonIO::load(*this, json.c_str(), &resave);
if (result && resave) {
saveToFile();
LOG_DBG("KRS", "Resaved KOReader credentials to update format");
@@ -8,12 +8,6 @@ enum class DocumentMatchMethod : uint8_t {
BINARY = 1, // Match by partial MD5 of file content (more accurate, but files must be identical)
};
class KOReaderCredentialStore;
namespace JsonSettingsIO {
bool saveKOReader(const KOReaderCredentialStore& store, const char* path);
bool loadKOReader(KOReaderCredentialStore& store, const char* json, bool* needsResave);
} // namespace JsonSettingsIO
/**
* Singleton class for storing KOReader sync credentials on the SD card.
* Passwords are XOR-obfuscated with the device's unique hardware MAC address
@@ -33,9 +27,6 @@ class KOReaderCredentialStore {
bool loadFromBinaryFile();
friend bool JsonSettingsIO::saveKOReader(const KOReaderCredentialStore&, const char*);
friend bool JsonSettingsIO::loadKOReader(KOReaderCredentialStore&, const char*, bool*);
public:
// Delete copy constructor and assignment
KOReaderCredentialStore(const KOReaderCredentialStore&) = delete;
+51
View File
@@ -0,0 +1,51 @@
#include "KOReaderJsonIO.h"
#include <ArduinoJson.h>
#include <HalStorage.h>
#include <Logging.h>
#include <ObfuscationUtils.h>
#include "KOReaderCredentialStore.h"
namespace KOReaderJsonIO {
bool save(const KOReaderCredentialStore& store, const char* path) {
JsonDocument doc;
doc["username"] = store.getUsername();
doc["password_obf"] = obfuscation::obfuscateToBase64(store.getPassword());
doc["serverUrl"] = store.getServerUrl();
doc["matchMethod"] = static_cast<uint8_t>(store.getMatchMethod());
String json;
serializeJson(doc, json);
return Storage.writeFile(path, json);
}
bool load(KOReaderCredentialStore& store, const char* json, bool* needsResave) {
if (needsResave) *needsResave = false;
JsonDocument doc;
auto error = deserializeJson(doc, json);
if (error) {
LOG_ERR("KRS", "JSON parse error: %s", error.c_str());
return false;
}
std::string user = doc["username"] | std::string("");
bool ok = false;
std::string pass = obfuscation::deobfuscateFromBase64(doc["password_obf"] | "", &ok);
if (!ok || pass.empty()) {
pass = doc["password"] | std::string("");
if (!pass.empty() && needsResave) *needsResave = true;
}
store.setCredentials(user, pass);
store.setServerUrl(doc["serverUrl"] | std::string(""));
uint8_t method = doc["matchMethod"] | (uint8_t)0;
store.setMatchMethod(static_cast<DocumentMatchMethod>(method));
return true;
}
} // namespace KOReaderJsonIO
+8
View File
@@ -0,0 +1,8 @@
#pragma once
class KOReaderCredentialStore;
namespace KOReaderJsonIO {
bool save(const KOReaderCredentialStore& store, const char* path);
bool load(KOReaderCredentialStore& store, const char* json, bool* needsResave);
} // namespace KOReaderJsonIO
+5 -11
View File
@@ -2,7 +2,6 @@
#include <ArduinoJson.h>
#include <Logging.h>
#include <base64.h>
#include <esp_crt_bundle.h>
#include <esp_http_client.h>
@@ -75,6 +74,11 @@ esp_http_client_handle_t createClient(const char* url, ResponseBuffer* buf,
config.buffer_size_tx = HTTP_BUF_SIZE;
config.crt_bundle_attach = esp_crt_bundle_attach;
// HTTP Basic Auth for Calibre-Web-Automated compatibility
config.username = KOREADER_STORE.getUsername().c_str();
config.password = KOREADER_STORE.getPassword().c_str();
config.auth_type = HTTP_AUTH_TYPE_BASIC;
esp_http_client_handle_t client = esp_http_client_init(&config);
if (!client) return nullptr;
@@ -87,16 +91,6 @@ esp_http_client_handle_t createClient(const char* url, ResponseBuffer* buf,
return nullptr;
}
// HTTP Basic Auth for Calibre-Web-Automated compatibility
std::string credentials = KOREADER_STORE.getUsername() + ":" + KOREADER_STORE.getPassword();
String encoded = base64::encode(reinterpret_cast<const uint8_t*>(credentials.data()), credentials.size());
std::string authHeader = "Basic " + std::string(encoded.c_str());
if (esp_http_client_set_header(client, "Authorization", authHeader.c_str()) != ESP_OK) {
LOG_ERR("KOSync", "Failed to set Authorization header");
esp_http_client_cleanup(client);
return nullptr;
}
return client;
}
} // namespace
+54
View File
@@ -0,0 +1,54 @@
#pragma once
#include <cstddef>
#include <memory>
#include <new>
#include <type_traits>
#include <utility>
// Nothrow versions of std::make_unique. Return nullptr on allocation failure
// instead of calling abort() (the default when exceptions are disabled on ESP32).
//
// Single object:
// auto obj = makeUniqueNoThrow<PNG>();
// if (!obj) { LOG_ERR("TAG", "OOM"); return false; }
//
// Array:
// auto buf = makeUniqueNoThrow<uint8_t[]>(size);
// if (!buf) { LOG_ERR("TAG", "OOM"); return false; }
// buf[0] = 0xFF;
// someApi(buf.get(), size);
//
template <typename T, typename... Args>
requires(!std::is_array_v<T>)
std::unique_ptr<T> makeUniqueNoThrow(Args&&... args) {
return std::unique_ptr<T>(new (std::nothrow) T(std::forward<Args>(args)...));
}
template <typename T>
requires std::is_unbounded_array_v<T>
std::unique_ptr<T> makeUniqueNoThrow(size_t count) {
using Elem = std::remove_extent_t<T>;
return std::unique_ptr<T>(new (std::nothrow) Elem[count]());
}
// Helper struct to call a cleanup function on exit from any scope.
// Use with a lambda to avoid unnecessary allocations from std::function/std::bind:
// Example:
// auto jpeg = makeUniqueNoThrow<JPEGDEC>();
// ScopedCleanup cleanup{[&jpeg]{ jpeg->close(); }};
//
template <typename F>
struct [[nodiscard]] ScopedCleanup final {
const F fn;
explicit ScopedCleanup(F f) : fn{std::move(f)} {}
ScopedCleanup(const ScopedCleanup&) = delete;
ScopedCleanup& operator=(const ScopedCleanup&) = delete;
ScopedCleanup(ScopedCleanup&&) = delete;
ScopedCleanup& operator=(ScopedCleanup&&) = delete;
~ScopedCleanup() { fn(); }
};
template <typename F>
ScopedCleanup(F) -> ScopedCleanup<F>;
+10 -2
View File
@@ -110,8 +110,16 @@ void XMLCALL OpdsParser::startElement(void* userData, const XML_Char* name, cons
if (self->inEntry) {
if (rel && type && strstr(rel, "opds-spec.org/acquisition") != nullptr &&
strcmp(type, "application/epub+zip") == 0) {
self->currentEntry.type = OpdsEntryType::BOOK;
self->currentEntry.href = href;
// Prefer plain EPUB links over derived formats when multiple
// acquisition links are present for one entry.
const bool isPlainEpub = strstr(href, ".epub") != nullptr || strstr(href, "/epub/") != nullptr;
const bool alreadyHasPlainEpub = self->currentEntry.type == OpdsEntryType::BOOK &&
(self->currentEntry.href.find(".epub") != std::string::npos ||
self->currentEntry.href.find("/epub/") != std::string::npos);
if (self->currentEntry.type != OpdsEntryType::BOOK || (isPlainEpub && !alreadyHasPlainEpub)) {
self->currentEntry.type = OpdsEntryType::BOOK;
self->currentEntry.href = href;
}
} else if (type && strstr(type, "application/atom+xml") != nullptr) {
if (self->currentEntry.type != OpdsEntryType::BOOK) {
self->currentEntry.type = OpdsEntryType::NAVIGATION;
+15
View File
@@ -155,6 +155,21 @@ bool Txt::generateCoverBmp() const {
return false;
}
bool Txt::clearCache() const {
if (!Storage.exists(cachePath.c_str())) {
LOG_DBG("TXT", "Cache does not exist, no action needed");
return true;
}
if (!Storage.removeDir(cachePath.c_str())) {
LOG_ERR("TXT", "Failed to clear cache");
return false;
}
LOG_DBG("TXT", "Cache cleared successfully");
return true;
}
bool Txt::readContent(uint8_t* buffer, size_t offset, size_t length) const {
if (!loaded) {
return false;
+1
View File
@@ -22,6 +22,7 @@ class Txt {
[[nodiscard]] size_t getFileSize() const { return fileSize; }
void setupCacheDir() const;
bool clearCache() const;
// Cover image support - looks for cover.bmp/jpg/jpeg/png in same folder as txt file
[[nodiscard]] std::string getCoverBmpPath() const;
+182
View File
@@ -0,0 +1,182 @@
#include "HalClock.h"
#include <Logging.h>
#include <WiFi.h>
#include <esp_sntp.h>
#include <time.h>
#include <cassert>
HalClock halClock; // Singleton instance
// DS3231 register layout (BCD encoded):
// 0x00: Seconds (bits 6-4 = tens, bits 3-0 = ones)
// 0x01: Minutes (bits 6-4 = tens, bits 3-0 = ones)
// 0x02: Hours (bit 6 = 12/24 mode, bits 5-4 = tens, bits 3-0 = ones)
static uint8_t bcdToDec(uint8_t bcd) { return ((bcd >> 4) * 10) + (bcd & 0x0F); }
static uint8_t decToBcd(uint8_t dec) { return ((dec / 10) << 4) | (dec % 10); }
void HalClock::begin() {
if (!gpio.deviceIsX3()) {
_available = false;
return;
}
// I2C is already initialised by HalPowerManager::begin() for X3.
// Probe the DS3231 by reading the seconds register.
Wire.beginTransmission(I2C_ADDR_DS3231);
Wire.write(DS3231_SEC_REG);
if (Wire.endTransmission(false) != 0) {
LOG_INF("CLK", "DS3231 RTC not found");
_available = false;
return;
}
Wire.requestFrom(I2C_ADDR_DS3231, (uint8_t)1);
if (Wire.available() < 1) {
_available = false;
return;
}
Wire.read(); // discard — just testing connectivity
_available = true;
LOG_INF("CLK", "DS3231 RTC found");
// Prime the cache with an initial read
uint8_t h, m;
getTime(h, m);
}
bool HalClock::getTime(uint8_t& hour, uint8_t& minute) const {
if (!_available) return false;
const unsigned long now = millis();
if (_lastPollMs != 0 && (now - _lastPollMs) < CLOCK_POLL_MS) {
hour = _cachedHour;
minute = _cachedMinute;
return true;
}
// Read 3 bytes starting at register 0x00: seconds, minutes, hours
Wire.beginTransmission(I2C_ADDR_DS3231);
Wire.write(DS3231_SEC_REG);
if (Wire.endTransmission(false) != 0) {
if (!_hasCachedTime) return false;
_lastPollMs = now;
hour = _cachedHour;
minute = _cachedMinute;
return true;
}
Wire.requestFrom(I2C_ADDR_DS3231, (uint8_t)3);
if (Wire.available() < 3) {
if (!_hasCachedTime) return false;
_lastPollMs = now;
hour = _cachedHour;
minute = _cachedMinute;
return true;
}
Wire.read(); // seconds — not needed
const uint8_t rawMin = Wire.read();
const uint8_t rawHour = Wire.read();
_cachedMinute = bcdToDec(rawMin & 0x7F);
// Handle 12/24h mode: bit 6 high = 12h mode
if (rawHour & 0x40) {
// 12h mode: bit 5 = PM, bits 4-0 = hours (1-12)
uint8_t h12 = bcdToDec(rawHour & 0x1F);
bool pm = rawHour & 0x20;
if (h12 == 12) h12 = 0;
_cachedHour = pm ? (h12 + 12) : h12;
} else {
// 24h mode: bits 5-0 = hours (0-23)
_cachedHour = bcdToDec(rawHour & 0x3F);
}
_lastPollMs = now;
_hasCachedTime = true;
hour = _cachedHour;
minute = _cachedMinute;
return true;
}
bool HalClock::formatTime(char* buf, size_t bufSize, uint8_t utcOffsetQuarterHoursBiased, bool use12Hour) const {
if (bufSize < (use12Hour ? 9u : 6u)) return false;
uint8_t h, m;
if (!getTime(h, m)) return false;
// Apply UTC offset: convert biased value to signed quarter-hours.
// Clamp against corrupted persisted values so display time can't drift outside [-12:00, +14:00].
if (utcOffsetQuarterHoursBiased > 104) utcOffsetQuarterHoursBiased = 104;
int offsetQuarterHours = static_cast<int>(utcOffsetQuarterHoursBiased) - 48;
int totalMinutes = static_cast<int>(h) * 60 + static_cast<int>(m) + offsetQuarterHours * 15;
// Wrap around 24 hours
totalMinutes = ((totalMinutes % 1440) + 1440) % 1440;
const int hour24 = totalMinutes / 60;
const int min = totalMinutes % 60;
if (use12Hour) {
const bool pm = hour24 >= 12;
int hour12 = hour24 % 12;
if (hour12 == 0) hour12 = 12;
snprintf(buf, bufSize, "%d:%02d %s", hour12, min, pm ? "PM" : "AM");
} else {
snprintf(buf, bufSize, "%02d:%02d", hour24, min);
}
return true;
}
bool HalClock::writeTimeToRTC(uint8_t hour, uint8_t minute, uint8_t second) {
assert(hour < 24);
assert(minute < 60);
assert(second < 60);
Wire.beginTransmission(I2C_ADDR_DS3231);
Wire.write(DS3231_SEC_REG); // Start at register 0x00
Wire.write(decToBcd(second)); // 0x00: Seconds
Wire.write(decToBcd(minute)); // 0x01: Minutes
Wire.write(decToBcd(hour)); // 0x02: Hours (24h mode, bit 6 = 0)
if (Wire.endTransmission() != 0) {
LOG_ERR("CLK", "Failed to write time to DS3231");
return false;
}
// Invalidate cache so next read fetches fresh data
_lastPollMs = 0;
_cachedHour = hour;
_cachedMinute = minute;
_hasCachedTime = true;
return true;
}
bool HalClock::syncFromNTP() {
if (!_available) return false;
if (WiFi.status() != WL_CONNECTED) {
LOG_ERR("CLK", "WiFi not connected, cannot sync NTP");
return false;
}
LOG_INF("CLK", "Starting NTP sync...");
configTzTime("UTC0", "pool.ntp.org", "time.nist.gov");
// Wait for SNTP sync to complete (up to 5 seconds)
constexpr int maxAttempts = 50;
for (int i = 0; i < maxAttempts; i++) {
if (sntp_get_sync_status() == SNTP_SYNC_STATUS_COMPLETED) {
time_t now = time(nullptr);
struct tm timeinfo;
gmtime_r(&now, &timeinfo);
if (writeTimeToRTC(timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec)) {
LOG_INF("CLK", "RTC set to %02d:%02d:%02d UTC", timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec);
return true;
}
return false;
}
delay(100);
}
LOG_ERR("CLK", "NTP sync timed out");
return false;
}
+48
View File
@@ -0,0 +1,48 @@
#pragma once
#include <Arduino.h>
#include <Wire.h>
#include "HalGPIO.h"
class HalClock;
extern HalClock halClock; // Singleton
class HalClock {
bool _available = false;
mutable uint8_t _cachedHour = 0;
mutable uint8_t _cachedMinute = 0;
mutable bool _hasCachedTime = false;
mutable unsigned long _lastPollMs = 0;
static constexpr unsigned long CLOCK_POLL_MS = 10000; // 10 seconds
public:
// Call after gpio.begin() and powerManager.begin() (I2C already initialised for X3)
void begin();
// True if the DS3231 RTC is present on this device
bool isAvailable() const { return _available; }
// Get current hour (0-23) and minute (0-59).
// Returns false if RTC is not available.
bool getTime(uint8_t& hour, uint8_t& minute) const;
// Format time into a caller-provided buffer.
// 24h mode produces "HH:MM" (needs >=6 bytes); 12h mode produces "H:MM AM"/"HH:MM PM" (needs >=9 bytes).
// utcOffsetQuarterHoursBiased: biased quarter-hour offset (48 = UTC+0, 0 = UTC-12, 104 = UTC+14).
// use12Hour: when true, format as 12-hour clock with AM/PM suffix.
// Returns false if RTC is not available.
bool formatTime(char* buf, size_t bufSize, uint8_t utcOffsetQuarterHoursBiased = 48, bool use12Hour = false) const;
// Sync the DS3231 RTC from an NTP server. Requires WiFi to be connected.
// Blocks for up to ~5s while waiting for SNTP response.
// Returns true if the RTC was successfully updated.
//
// Debouncing (skip if already synced once) is enforced by the caller, not here,
// so the HAL stays free of any app-layer settings dependency.
bool syncFromNTP();
private:
bool writeTimeToRTC(uint8_t hour, uint8_t minute, uint8_t second);
};
+9 -2
View File
@@ -10,7 +10,7 @@ HalDisplay::HalDisplay() : einkDisplay(EPD_SCLK, EPD_MOSI, EPD_CS, EPD_DC, EPD_R
HalDisplay::~HalDisplay() {}
void HalDisplay::begin() {
void HalDisplay::begin(bool seamless) {
// Set X3-specific panel mode before initializing.
if (gpio.deviceIsX3()) {
einkDisplay.setDisplayX3();
@@ -18,7 +18,14 @@ void HalDisplay::begin() {
einkDisplay.begin();
// Request resync after specific wakeup events to ensure clean display state
if (seamless) {
// Defuse the SDK's X3 _x3InitialFullSyncsRemaining counter (no-op on X4)
// so the first paint isn't promoted to FULL (~770ms). Skips the wakeup-
// gated requestResync() below for the same reason.
einkDisplay.skipInitialResync();
return;
}
// Request resync after specific wakeup events to ensure clean display state.
const auto wakeupReason = gpio.getWakeupReason();
if (wakeupReason == HalGPIO::WakeupReason::PowerButton || wakeupReason == HalGPIO::WakeupReason::AfterFlash ||
wakeupReason == HalGPIO::WakeupReason::Other) {
+7 -2
View File
@@ -17,8 +17,13 @@ class HalDisplay {
FAST_REFRESH // Fast refresh using custom LUT
};
// Initialize the display hardware and driver
void begin();
// Pass seamless=true on any path where the panel already shows the
// content it should after begin() returns (silent reboot's popup,
// sleep-wake with a restored buffer). Skips the wakeup-gated
// requestResync() and defuses the SDK's X3 _x3InitialFullSyncsRemaining
// counter; otherwise the first two paints get promoted to FULL
// (~770ms each on X3).
void begin(bool seamless = false);
// Display dimensions
static constexpr uint16_t DISPLAY_WIDTH = EInkDisplay::DISPLAY_WIDTH;
+4 -2
View File
@@ -223,6 +223,8 @@ bool HalGPIO::wasAnyReleased() const { return inputMgr.wasAnyReleased(); }
unsigned long HalGPIO::getHeldTime() const { return inputMgr.getHeldTime(); }
unsigned long HalGPIO::getPowerButtonHeldTime() const { return inputMgr.getPowerButtonHeldTime(); }
void HalGPIO::startDeepSleep() {
// Ensure that the power button has been released to avoid immediately turning back on if you're holding it
while (inputMgr.isPressed(BTN_POWER)) {
@@ -258,8 +260,8 @@ void HalGPIO::verifyPowerButtonWakeup(uint16_t requiredDurationMs, bool shortPre
do {
delay(10);
inputMgr.update();
} while (inputMgr.isPressed(BTN_POWER) && inputMgr.getHeldTime() < calibratedDuration);
if (inputMgr.getHeldTime() < calibratedDuration) {
} while (inputMgr.isPressed(BTN_POWER) && inputMgr.getPowerButtonHeldTime() < calibratedDuration);
if (inputMgr.getPowerButtonHeldTime() < calibratedDuration) {
startDeepSleep();
}
} else {
+1
View File
@@ -70,6 +70,7 @@ class HalGPIO {
bool wasReleased(uint8_t buttonIndex) const;
bool wasAnyReleased() const;
unsigned long getHeldTime() const;
unsigned long getPowerButtonHeldTime() const;
// Setup wake up GPIO and enter deep sleep
void startDeepSleep();
+9
View File
@@ -66,6 +66,15 @@ void HalPowerManager::startDeepSleep(HalGPIO& gpio) const {
delay(50);
gpio.update();
}
#ifdef ENABLE_SERIAL_LOG
// Tear down HWCDC so the host sees a clean disconnect and the peripheral
// doesn't hold power domains that interfere with USB-powered GPIO wake.
// logSerial is the raw HWCDC reference; Serial is the MySerialImpl proxy
// (which doesn't expose end()).
logSerial.end();
#endif
// Pre-sleep routines from the original firmware
// GPIO13 is connected to battery latch MOSFET, we need to make sure it's low during sleep
// Note that this means the MCU will be completely powered off during sleep, including RTC
+19 -7
View File
@@ -12,7 +12,13 @@
HalStorage HalStorage::instance;
HalStorage::HalStorage() {
storageMutex = xSemaphoreCreateMutex();
// Recursive so the same task can re-enter StorageLock without self-deadlock.
// openFileForRead/Write take the lock and then assign to a HalFile&
// out-param; if that out-param already held an Impl, its destructor takes
// the lock again to close the prior FsFile under serialization (see
// HalFile::Impl::~Impl below). Priority inheritance still applies to
// recursive mutexes.
storageMutex = xSemaphoreCreateRecursiveMutex();
assert(storageMutex != nullptr);
}
@@ -26,8 +32,8 @@ bool HalStorage::ready() const { return SDCard.ready(); }
class HalStorage::StorageLock {
public:
StorageLock() { xSemaphoreTake(HalStorage::getInstance().storageMutex, portMAX_DELAY); }
~StorageLock() { xSemaphoreGive(HalStorage::getInstance().storageMutex); }
StorageLock() { xSemaphoreTakeRecursive(HalStorage::getInstance().storageMutex, portMAX_DELAY); }
~StorageLock() { xSemaphoreGiveRecursive(HalStorage::getInstance().storageMutex); }
};
#define HAL_STORAGE_WRAPPED_CALL(method, ...) \
@@ -57,17 +63,23 @@ bool HalStorage::ensureDirectoryExists(const char* path) { HAL_STORAGE_WRAPPED_C
class HalFile::Impl {
public:
Impl(FsFile&& fsFile) : file(std::move(fsFile)) {}
// SdFat is not thread-safe; FsFile::close() touches SD/SPI and must run
// under StorageLock or it races SdSpiCard::m_spiActive across tasks and
// trips FreeRTOS's xTaskPriorityDisinherit assert. The FsFile member
// destructor (DESTRUCTOR_CLOSES_FILE=1) will close() again after the lock
// releases, but close() on an already-closed FsFile is a no-op. See SdFat
// issue #518 and the HAL note in CLAUDE.md.
~Impl() {
HalStorage::StorageLock lock;
file.close();
}
FsFile file;
};
HalFile::HalFile() = default;
HalFile::HalFile(std::unique_ptr<Impl> impl) : impl(std::move(impl)) {}
HalFile::~HalFile() = default;
HalFile::HalFile(HalFile&&) = default;
HalFile& HalFile::operator=(HalFile&&) = default;
HalFile HalStorage::open(const char* path, const oflag_t oflag) {
+2 -3
View File
@@ -52,7 +52,6 @@ extra_scripts =
pre:scripts/build_html.py
pre:scripts/gen_i18n.py
pre:scripts/git_branch.py
pre:scripts/patch_jpegdec.py
; Libraries
lib_deps =
@@ -63,7 +62,7 @@ lib_deps =
bblanchon/ArduinoJson @ 7.4.2
ricmoo/QRCode @ 0.0.1
bitbank2/PNGdec @ ^1.0.0
https://github.com/bitbank2/JPEGDEC.git#86282979224c8a32fd51e091ed5a35b0c699a52b
https://github.com/crosspoint-reader/JPEGDEC.git#dac4cfec6eec23289e7fd9f08e1e796cb3c5ef60
links2004/WebSockets @ 2.7.3
[env:default]
@@ -89,7 +88,7 @@ build_flags =
${base.build_flags}
-DCROSSPOINT_VERSION=\"${crosspoint.version}-rc+${sysenv.CROSSPOINT_RC_HASH}\"
-DENABLE_SERIAL_LOG
-DLOG_LEVEL=1 ; Set log level to info for release candidate builds
-DLOG_LEVEL=1 ; Set log level to info for release candidate builds
[env:slim]
extends = base
+1 -1
View File
@@ -60,7 +60,7 @@ def write_header(path: pathlib.Path, blob: bytes, symbol: str) -> None:
#include <cstddef>
#include <cstdint>
#include "../SerializedHyphenationTrie.h"
#include "Epub/hyphenation/SerializedHyphenationTrie.h"
// Auto-generated by generate_hyphenation_trie.py. Do not edit manually.
alignas(4) constexpr uint8_t {data_symbol}[] = {{
-68
View File
@@ -1,68 +0,0 @@
"""
PlatformIO pre-build script: patch JPEGDEC for MCU_SKIP wild pointer crash.
Problem:
JPEGDecodeMCU_P computes pMCU = &sMCUs[iMCU & 0xffffff]. When iMCU is
MCU_SKIP (-8), the bitmask produces index 0xFFFFF8 (16 777 208), creating a
pointer ~33 MB past the 392-entry sMCUs array. If the progressive JPEG's
first scan includes AC coefficients (iScanEnd > 0), the AC decode loop writes
through this wild pointer and crashes with a store-access fault.
Upstream commit 8628297 guarded the DC coefficient write (pMCU[0]) but not the
AC coefficient writes at indices 1-63.
Fix:
Redirect pMCU to sMCUs[0] when MCU_SKIP is active. Writes to sMCUs[1..63]
are harmless: for JPEG_SCALE_EIGHTH only sMCUs[0] is read for output, and
the DC write at sMCUs[0] is already guarded by the existing `if (iMCU >= 0)`
check.
Applied idempotently safe to run on every build.
"""
Import("env")
import os
def patch_jpegdec(env):
libdeps_dir = os.path.join(env["PROJECT_DIR"], ".pio", "libdeps")
if not os.path.isdir(libdeps_dir):
return
for env_dir in os.listdir(libdeps_dir):
jpeg_inl = os.path.join(libdeps_dir, env_dir, "JPEGDEC", "src", "jpeg.inl")
if os.path.isfile(jpeg_inl):
_apply_mcu_skip_pointer_fix(jpeg_inl)
def _apply_mcu_skip_pointer_fix(filepath):
MARKER = "// CrossPoint patch: safe pMCU for MCU_SKIP"
with open(filepath, "r") as f:
content = f.read()
if MARKER in content:
return # already patched
# The wild-pointer line in JPEGDecodeMCU_P:
OLD = " signed short *pMCU = &pJPEG->sMCUs[iMCU & 0xffffff];"
NEW = (
" " + MARKER + "\n"
" signed short *pMCU = (iMCU < 0) ? pJPEG->sMCUs\n"
" : &pJPEG->sMCUs[iMCU & 0xffffff];"
)
if OLD not in content:
print(
"WARNING: JPEGDEC MCU_SKIP pointer patch target not found in %s "
"— library may have been updated" % filepath
)
return
content = content.replace(OLD, NEW, 1)
with open(filepath, "w") as f:
f.write(content)
print("Patched JPEGDEC: safe pMCU for MCU_SKIP in JPEGDecodeMCU_P: %s" % filepath)
# Run immediately at script import time (before compilation).
patch_jpegdec(env)
+2
View File
@@ -23,3 +23,5 @@ process es
process ru
process it
process uk
process pl
process sv
+4
View File
@@ -240,6 +240,8 @@ bool CrossPointSettings::loadFromBinaryFile() {
if (++settingsRead >= fileSettingsCount) break;
serialization::readPod(inputFile, embeddedStyle);
if (++settingsRead >= fileSettingsCount) break;
serialization::readPod(inputFile, frontButtonFollowOrientation);
if (++settingsRead >= fileSettingsCount) break;
} while (false);
if (frontButtonMappingRead) {
@@ -305,6 +307,8 @@ unsigned long CrossPointSettings::getSleepTimeoutMs() const {
switch (sleepTimeout) {
case SLEEP_1_MIN:
return 1UL * 60 * 1000;
case SLEEP_3_MIN:
return 3UL * 60 * 1000;
case SLEEP_5_MIN:
return 5UL * 60 * 1000;
case SLEEP_10_MIN:
+26
View File
@@ -24,6 +24,7 @@ class CrossPointSettings {
COVER = 3,
BLANK = 4,
COVER_CUSTOM = 5,
QUICK_RESUME = 6,
SLEEP_SCREEN_MODE_COUNT
};
enum SLEEP_SCREEN_COVER_MODE { FIT = 0, CROP = 1, SLEEP_SCREEN_COVER_MODE_COUNT };
@@ -119,6 +120,7 @@ class CrossPointSettings {
SLEEP_10_MIN = 2,
SLEEP_15_MIN = 3,
SLEEP_30_MIN = 4,
SLEEP_3_MIN = 5,
SLEEP_TIMEOUT_COUNT
};
@@ -154,6 +156,12 @@ class CrossPointSettings {
enum TILT_PAGE_TURN { TILT_OFF = 0, TILT_NORMAL = 1, TILT_NVERTED = 2, TILT_PAGE_TURN_COUNT };
enum QUICK_RESUME_SLEEP_SCREEN {
QUICK_RESUME_NEVER = 0,
QUICK_RESUME_AFTER_TIMEOUT = 1,
QUICK_RESUME_SLEEP_SCREEN_COUNT
};
// Sleep screen settings
uint8_t sleepScreen = DARK;
// Sleep screen cover mode settings
@@ -169,6 +177,17 @@ class CrossPointSettings {
uint8_t statusBarTitle = CHAPTER_TITLE;
uint8_t statusBarBattery = 1;
uint8_t xtcStatusBarMode = XTC_STATUS_BAR_HIDE;
// Clock display in status bar (X3 only, requires DS3231 RTC)
uint8_t statusBarClock = 0;
// Clock UTC offset in quarter-hour steps, biased by 48 so it fits in uint8_t.
// Value 48 = UTC+0, 0 = UTC-12:00, 104 = UTC+14:00.
// Quarter-hour granularity supports oddball zones like Nepal (+5:45) and Chatham (+12:45).
uint8_t clockUtcOffsetQ = 48;
// Clock display format: 0 = 24-hour, 1 = 12-hour
uint8_t clockFormat = 0;
// Set once an NTP sync succeeds. Used to skip re-syncing on every WiFi connect.
// Resetting to 0 (e.g. via the web UI) forces a re-sync on next WiFi connect.
uint8_t clockHasBeenSynced = 0;
// Text rendering settings
uint8_t extraParagraphSpacing = 1;
uint8_t textAntiAliasing = 1;
@@ -180,6 +199,7 @@ class CrossPointSettings {
// Button layouts (front layout retained for migration only)
uint8_t frontButtonLayout = BACK_CONFIRM_LEFT_RIGHT;
uint8_t sideButtonLayout = PREV_NEXT;
uint8_t frontButtonFollowOrientation = 0;
// Front button remap (logical -> hardware)
// Used by MappedInputManager to translate logical buttons into physical front buttons.
uint8_t frontButtonBack = FRONT_HW_BACK;
@@ -219,12 +239,18 @@ class CrossPointSettings {
char sdFontFamilyName[32] = "";
// Show hidden files/directories (starting with '.') in the file browser (0 = hidden, 1 = show)
uint8_t showHiddenFiles = 0;
// Remove a book from the Recent Books list when its End-of-Book screen is reached (0 = off, 1 = on)
uint8_t removeReadBooksFromRecents = 0;
// Move epub to /Read/ folder on SD card when finished (0 = disabled, 1 = enabled)
uint8_t moveFinishedToReadFolder = 0;
// Image rendering mode in EPUB reader
uint8_t imageRendering = IMAGES_DISPLAY;
// Tilt-based page turning (X3 only — requires QMI8658 IMU)
uint8_t tiltPageTurn = TILT_OFF;
// Language setting (Language enum index, default 0 = EN)
uint8_t language = 0;
// Quick Resume: keep current content visible with moon icon instead of showing a static sleep screen.
uint8_t quickResumeSleepScreen = QUICK_RESUME_NEVER;
~CrossPointSettings() = default;
+1
View File
@@ -15,6 +15,7 @@ class CrossPointState {
uint8_t recentSleepFill = 0; // valid entries (0..SLEEP_RECENT_COUNT)
uint8_t readerActivityLoadCount = 0;
bool lastSleepFromReader = false;
bool showBootScreen = true;
// Returns true if idx was shown within the last checkCount picks.
// Walks backwards from the most recently written slot.
+2 -39
View File
@@ -10,7 +10,6 @@
#include "CrossPointSettings.h"
#include "CrossPointState.h"
#include "KOReaderCredentialStore.h"
#include "OpdsServerStore.h"
#include "RecentBooksStore.h"
#include "SettingsList.h"
@@ -76,6 +75,7 @@ bool JsonSettingsIO::saveState(const CrossPointState& s, const char* path) {
doc["recentSleepFill"] = s.recentSleepFill;
doc["readerActivityLoadCount"] = s.readerActivityLoadCount;
doc["lastSleepFromReader"] = s.lastSleepFromReader;
doc["showBootScreen"] = s.showBootScreen;
String json;
serializeJson(doc, json);
@@ -110,6 +110,7 @@ bool JsonSettingsIO::loadState(CrossPointState& s, const char* json) {
}
s.readerActivityLoadCount = doc["readerActivityLoadCount"] | static_cast<uint8_t>(0);
s.lastSleepFromReader = doc["lastSleepFromReader"] | false;
s.showBootScreen = doc["showBootScreen"] | true;
return true;
}
@@ -251,44 +252,6 @@ bool JsonSettingsIO::loadSettings(CrossPointSettings& s, const char* json, bool*
return true;
}
// ---- KOReaderCredentialStore ----
bool JsonSettingsIO::saveKOReader(const KOReaderCredentialStore& store, const char* path) {
JsonDocument doc;
doc["username"] = store.getUsername();
doc["password_obf"] = obfuscation::obfuscateToBase64(store.getPassword());
doc["serverUrl"] = store.getServerUrl();
doc["matchMethod"] = static_cast<uint8_t>(store.getMatchMethod());
String json;
serializeJson(doc, json);
return Storage.writeFile(path, json);
}
bool JsonSettingsIO::loadKOReader(KOReaderCredentialStore& store, const char* json, bool* needsResave) {
if (needsResave) *needsResave = false;
JsonDocument doc;
auto error = deserializeJson(doc, json);
if (error) {
LOG_ERR("KRS", "JSON parse error: %s", error.c_str());
return false;
}
store.username = doc["username"] | std::string("");
bool ok = false;
store.password = obfuscation::deobfuscateFromBase64(doc["password_obf"] | "", &ok);
if (!ok || store.password.empty()) {
store.password = doc["password"] | std::string("");
if (!store.password.empty() && needsResave) *needsResave = true;
}
store.serverUrl = doc["serverUrl"] | std::string("");
uint8_t method = doc["matchMethod"] | (uint8_t)0;
store.matchMethod = static_cast<DocumentMatchMethod>(method);
LOG_DBG("KRS", "Loaded KOReader credentials for user: %s", store.username.c_str());
return true;
}
// ---- WifiCredentialStore ----
bool JsonSettingsIO::saveWifi(const WifiCredentialStore& store, const char* path) {
-5
View File
@@ -3,7 +3,6 @@
class CrossPointSettings;
class CrossPointState;
class WifiCredentialStore;
class KOReaderCredentialStore;
class RecentBooksStore;
class OpdsServerStore;
@@ -21,10 +20,6 @@ bool loadState(CrossPointState& s, const char* json);
bool saveWifi(const WifiCredentialStore& store, const char* path);
bool loadWifi(WifiCredentialStore& store, const char* json, bool* needsResave = nullptr);
// KOReaderCredentialStore
bool saveKOReader(const KOReaderCredentialStore& store, const char* path);
bool loadKOReader(KOReaderCredentialStore& store, const char* json, bool* needsResave = nullptr);
// RecentBooksStore
bool saveRecentBooks(const RecentBooksStore& store, const char* path);
bool loadRecentBooks(RecentBooksStore& store, const char* json);
+10 -3
View File
@@ -68,6 +68,13 @@ unsigned long MappedInputManager::getHeldTime() const { return gpio.getHeldTime(
MappedInputManager::Labels MappedInputManager::mapLabels(const char* back, const char* confirm, const char* previous,
const char* next) const {
// Swap previous/next labels to match the page turn direction swap in INVERTED and LANDSCAPE_CCW.
const bool swapLabels =
SETTINGS.frontButtonFollowOrientation && (SETTINGS.orientation == CrossPointSettings::INVERTED ||
SETTINGS.orientation == CrossPointSettings::LANDSCAPE_CCW);
const char* leftLabel = swapLabels ? next : previous;
const char* rightLabel = swapLabels ? previous : next;
// Build the label order based on the configured hardware mapping.
auto labelForHardware = [&](uint8_t hw) -> const char* {
// Compare against configured logical roles and return the matching label.
@@ -78,10 +85,10 @@ MappedInputManager::Labels MappedInputManager::mapLabels(const char* back, const
return confirm;
}
if (hw == SETTINGS.frontButtonLeft) {
return previous;
return leftLabel;
}
if (hw == SETTINGS.frontButtonRight) {
return next;
return rightLabel;
}
return "";
};
@@ -106,4 +113,4 @@ int MappedInputManager::getPressedFrontButton() const {
return HalGPIO::BTN_RIGHT;
}
return -1;
}
}
+39
View File
@@ -9,6 +9,7 @@
#include <Xtc.h>
#include <algorithm>
#include <iterator>
namespace {
constexpr uint8_t RECENT_BOOKS_FILE_VERSION = 3;
@@ -22,6 +23,9 @@ RecentBooksStore RecentBooksStore::instance;
void RecentBooksStore::addBook(const std::string& path, const std::string& title, const std::string& author,
const std::string& coverBmpPath) {
// Drop stale entries first so a new add can't evict a valid book in their stead.
pruneMissing();
// Remove existing entry if present
auto it =
std::find_if(recentBooks.begin(), recentBooks.end(), [&](const RecentBook& book) { return book.path == path; });
@@ -53,6 +57,41 @@ void RecentBooksStore::updateBook(const std::string& path, const std::string& ti
}
}
bool RecentBooksStore::removeByPath(const std::string& path) {
auto it =
std::find_if(recentBooks.begin(), recentBooks.end(), [&](const RecentBook& book) { return book.path == path; });
if (it == recentBooks.end()) {
return false;
}
recentBooks.erase(it);
if (!saveToFile()) {
LOG_ERR("RBS", "Failed to persist removal of recent book: %s", path.c_str());
}
return true;
}
void RecentBooksStore::updatePath(const std::string& oldPath, const std::string& newPath,
const std::string& oldCachePath, const std::string& newCachePath) {
auto it = std::find_if(recentBooks.begin(), recentBooks.end(),
[&](const RecentBook& book) { return book.path == oldPath; });
if (it == recentBooks.end()) {
return;
}
it->path = newPath;
if (!oldCachePath.empty() && !it->coverBmpPath.empty() && it->coverBmpPath.rfind(oldCachePath, 0) == 0) {
it->coverBmpPath = newCachePath + it->coverBmpPath.substr(oldCachePath.size());
}
saveToFile();
}
bool RecentBooksStore::isMissing(const RecentBook& book) { return !Storage.exists(book.path.c_str()); }
bool RecentBooksStore::pruneMissing() {
const size_t before = recentBooks.size();
recentBooks.erase(std::remove_if(recentBooks.begin(), recentBooks.end(), &isMissing), recentBooks.end());
return recentBooks.size() != before;
}
bool RecentBooksStore::saveToFile() const {
Storage.mkdir("/.crosspoint");
return JsonSettingsIO::saveRecentBooks(*this, RECENT_BOOKS_FILE_JSON);
+18
View File
@@ -37,6 +37,24 @@ class RecentBooksStore {
void updateBook(const std::string& path, const std::string& title, const std::string& author,
const std::string& coverBmpPath);
// Remove the entry whose path matches (used when a book is removed from recents or finished/read).
// Returns true if an entry was found and removed (no-op + false otherwise).
// Persistence is best-effort: a failed save is logged, not reflected in the return.
bool removeByPath(const std::string& path);
// Repoint an entry's path (and coverBmpPath, if it lived under the old cache dir) after the
// backing file and cache dir were moved on disk. No-op if no entry matches oldPath.
// Persists on success. Keeps the entry's list position (does not reorder).
void updatePath(const std::string& oldPath, const std::string& newPath, const std::string& oldCachePath,
const std::string& newCachePath);
// True if the book's backing file is no longer present on the SD card.
static bool isMissing(const RecentBook& book);
// Remove entries whose backing file is no longer on the SD card.
// Returns true if any entry was removed. Does not persist — caller decides.
bool pruneMissing();
// Get the list of recent books (most recent first)
const std::vector<RecentBook>& getBooks() const { return recentBooks; }
-12
View File
@@ -1,12 +0,0 @@
#pragma once
#include "SdCardFontSystem.h"
class GfxRenderer;
// Global SD card font system instance (defined in main.cpp).
extern SdCardFontSystem sdFontSystem;
// Ensure the correct SD card font family is loaded for current settings.
// Defined in main.cpp; call before entering the reader or after settings change.
extern void ensureSdFontLoaded();
+3
View File
@@ -50,3 +50,6 @@ class SdCardFontSystem {
SdCardFontManager manager_;
std::atomic<bool> registryDirty_{false};
};
// Global SD card font system instance (defined in main.cpp).
extern SdCardFontSystem sdFontSystem;
+26 -2
View File
@@ -1,5 +1,6 @@
#pragma once
#include <HalClock.h>
#include <HalTiltSensor.h>
#include <I18n.h>
#include <SdCardFontRegistry.h>
@@ -105,13 +106,16 @@ inline std::vector<SettingInfo> getSettingsList(const SdCardFontRegistry* regist
// --- Display ---
SettingInfo::Enum(StrId::STR_SLEEP_SCREEN, &CrossPointSettings::sleepScreen,
{StrId::STR_DARK, StrId::STR_LIGHT, StrId::STR_CUSTOM, StrId::STR_COVER, StrId::STR_NONE_OPT,
StrId::STR_COVER_CUSTOM},
StrId::STR_COVER_CUSTOM, StrId::STR_QUICK_RESUME},
"sleepScreen", StrId::STR_CAT_DISPLAY),
SettingInfo::Enum(StrId::STR_SLEEP_COVER_MODE, &CrossPointSettings::sleepScreenCoverMode,
{StrId::STR_FIT, StrId::STR_CROP}, "sleepScreenCoverMode", StrId::STR_CAT_DISPLAY),
SettingInfo::Enum(StrId::STR_SLEEP_COVER_FILTER, &CrossPointSettings::sleepScreenCoverFilter,
{StrId::STR_NONE_OPT, StrId::STR_FILTER_CONTRAST, StrId::STR_INVERTED},
"sleepScreenCoverFilter", StrId::STR_CAT_DISPLAY),
SettingInfo::Enum(StrId::STR_QUICK_RESUME_TIMEOUT, &CrossPointSettings::quickResumeSleepScreen,
{StrId::STR_STATE_OFF, StrId::STR_STATE_ON}, "quickResumeSleepScreen",
StrId::STR_CAT_DISPLAY),
SettingInfo::Enum(StrId::STR_HIDE_BATTERY, &CrossPointSettings::hideBatteryPercentage,
{StrId::STR_NEVER, StrId::STR_IN_READER, StrId::STR_ALWAYS}, "hideBatteryPercentage",
StrId::STR_CAT_DISPLAY),
@@ -162,6 +166,8 @@ inline std::vector<SettingInfo> getSettingsList(const SdCardFontRegistry* regist
// --- Controls ---
SettingInfo::Enum(StrId::STR_SIDE_BTN_LAYOUT, &CrossPointSettings::sideButtonLayout,
{StrId::STR_PREV_NEXT, StrId::STR_NEXT_PREV}, "sideButtonLayout", StrId::STR_CAT_CONTROLS),
SettingInfo::Toggle(StrId::STR_FRONT_BTN_FOLLOW_ORIENTATION, &CrossPointSettings::frontButtonFollowOrientation,
"frontButtonFollowOrientation", StrId::STR_CAT_CONTROLS),
SettingInfo::Enum(StrId::STR_LONG_PRESS_BEHAVIOR, &CrossPointSettings::longPressButtonBehavior,
{StrId::STR_LONG_PRESS_BEHAVIOR_OFF, StrId::STR_LONG_PRESS_BEHAVIOR_SKIP,
StrId::STR_LONG_PRESS_BEHAVIOR_ORIENTATION},
@@ -172,10 +178,15 @@ inline std::vector<SettingInfo> getSettingsList(const SdCardFontRegistry* regist
// --- System ---
SettingInfo::Enum(StrId::STR_TIME_TO_SLEEP, &CrossPointSettings::sleepTimeout,
{StrId::STR_MIN_1, StrId::STR_MIN_5, StrId::STR_MIN_10, StrId::STR_MIN_15, StrId::STR_MIN_30},
{StrId::STR_MIN_1, StrId::STR_MIN_3, StrId::STR_MIN_5, StrId::STR_MIN_10, StrId::STR_MIN_15,
StrId::STR_MIN_30},
"sleepTimeout", StrId::STR_CAT_SYSTEM),
SettingInfo::Toggle(StrId::STR_SHOW_HIDDEN_FILES, &CrossPointSettings::showHiddenFiles, "showHiddenFiles",
StrId::STR_CAT_SYSTEM),
SettingInfo::Toggle(StrId::STR_REMOVE_READ_FROM_RECENTS, &CrossPointSettings::removeReadBooksFromRecents,
"removeReadBooksFromRecents", StrId::STR_CAT_SYSTEM),
SettingInfo::Toggle(StrId::STR_MOVE_FINISHED_TO_READ, &CrossPointSettings::moveFinishedToReadFolder,
"moveFinishedToReadFolder", StrId::STR_CAT_SYSTEM),
// --- KOReader Sync (web-only, uses KOReaderCredentialStore) ---
SettingInfo::DynamicString(
@@ -226,6 +237,19 @@ inline std::vector<SettingInfo> getSettingsList(const SdCardFontRegistry* regist
SettingInfo::Enum(StrId::STR_XTC_STATUS_BAR, &CrossPointSettings::xtcStatusBarMode,
{StrId::STR_HIDE, StrId::STR_BOTTOM, StrId::STR_TOP}, "xtcStatusBarMode",
StrId::STR_CUSTOMISE_STATUS_BAR),
// Clock entries (web settings only; device UI uses ClockOffsetActivity for the offset).
// Range 0..104 = quarter-hour steps from UTC-12:00 to UTC+14:00, biased by 48.
SettingInfo::Toggle(StrId::STR_CLOCK, &CrossPointSettings::statusBarClock, "statusBarClock",
StrId::STR_CUSTOMISE_STATUS_BAR),
SettingInfo::Value(StrId::STR_CLOCK_UTC_OFFSET, &CrossPointSettings::clockUtcOffsetQ, {0, 104, 1},
"clockUtcOffsetQ", StrId::STR_CUSTOMISE_STATUS_BAR),
SettingInfo::Enum(StrId::STR_CLOCK_FORMAT, &CrossPointSettings::clockFormat,
{StrId::STR_CLOCK_FORMAT_24H, StrId::STR_CLOCK_FORMAT_12H}, "clockFormat",
StrId::STR_CUSTOMISE_STATUS_BAR),
// Persistence flag for NTP debounce. Resetting from the web UI forces a re-sync
// on next WiFi connect, which is useful when crossing time zones.
SettingInfo::Toggle(StrId::STR_CLOCK_SYNCED, &CrossPointSettings::clockHasBeenSynced, "clockHasBeenSynced",
StrId::STR_CUSTOMISE_STATUS_BAR),
};
// Only show tilt page turn setting when the QMI8658 IMU is present (X3)
if (halTiltSensor.isAvailable()) {
+8
View File
@@ -0,0 +1,8 @@
#pragma once
// ESP.restart() with an RTC_NOINIT flag that survives the reboot, so setup()
// skips the boot splash and routes straight to a destination. Used to clear
// heap fragmentation accumulated during a wifi session.
void silentRestart(); // home screen
void silentRestartToReader(); // currently-open EPUB (APP_STATE.openEpubPath)
+1 -1
View File
@@ -10,7 +10,7 @@ void Activity::requestUpdate(bool immediate) { activityManager.requestUpdate(imm
void Activity::requestUpdateAndWait() { activityManager.requestUpdateAndWait(); }
void Activity::onGoHome() { activityManager.goHome(); }
void Activity::onGoHome(HomeMenuItem item) { activityManager.goHome(item); }
void Activity::onSelectBook(const std::string& path) { activityManager.goToReader(path); }
+1 -1
View File
@@ -58,6 +58,6 @@ class Activity {
// Convenience method to facilitate API transition to ActivityManager
// TODO: remove this in near future
void onGoHome();
void onGoHome(HomeMenuItem item = HomeMenuItem::NONE);
void onSelectBook(const std::string& path);
};
+19 -6
View File
@@ -5,7 +5,6 @@
#include <algorithm>
#include "OpdsServerStore.h"
#include "SdCardFontGlobals.h"
#include "boot_sleep/BootActivity.h"
#include "boot_sleep/SleepActivity.h"
#include "browser/OpdsBookBrowserActivity.h"
@@ -194,12 +193,11 @@ void ActivityManager::goToBrowser() {
}
void ActivityManager::goToReader(std::string path) {
ensureSdFontLoaded();
replaceActivity(std::make_unique<ReaderActivity>(renderer, mappedInput, std::move(path)));
}
void ActivityManager::goToSleep() {
replaceActivity(std::make_unique<SleepActivity>(renderer, mappedInput));
void ActivityManager::goToSleep(bool fromTimeout) {
replaceActivity(std::make_unique<SleepActivity>(renderer, mappedInput, fromTimeout));
loop(); // Important: sleep screen must be rendered immediately, the caller will go to sleep right after this returns
}
@@ -209,10 +207,25 @@ void ActivityManager::goToFullScreenMessage(std::string message, EpdFontFamily::
replaceActivity(std::make_unique<FullScreenMessageActivity>(renderer, mappedInput, std::move(message), style));
}
void ActivityManager::goHome(HomeMenuItem initialMenuItem) {
if (initialMenuItem == HomeMenuItem::NONE && currentActivity) {
const auto& activityName = currentActivity->name;
if (activityName == "FileBrowser") {
initialMenuItem = HomeMenuItem::FILE_BROWSER;
} else if (activityName == "RecentBooks") {
initialMenuItem = HomeMenuItem::RECENTS;
} else if (activityName == "OpdsBookBrowser") {
initialMenuItem = HomeMenuItem::OPDS_BROWSER;
} else if (activityName == "CrossPointWebServer") {
initialMenuItem = HomeMenuItem::FILE_TRANSFER;
} else if (activityName == "Settings") {
initialMenuItem = HomeMenuItem::SETTINGS_MENU;
}
}
replaceActivity(std::make_unique<HomeActivity>(renderer, mappedInput, initialMenuItem));
}
void ActivityManager::goToCrashReport() { replaceActivity(std::make_unique<CrashActivity>(renderer, mappedInput)); }
void ActivityManager::goHome() { replaceActivity(std::make_unique<HomeActivity>(renderer, mappedInput)); }
void ActivityManager::pushActivity(std::unique_ptr<Activity>&& activity) {
if (pendingActivity) {
// Should never happen in practice
+4 -2
View File
@@ -16,6 +16,8 @@
class Activity; // forward declaration
class RenderLock; // forward declaration
enum class HomeMenuItem { NONE, FILE_BROWSER, RECENTS, OPDS_BROWSER, FILE_TRANSFER, SETTINGS_MENU };
/**
* ActivityManager
*
@@ -84,11 +86,11 @@ class ActivityManager {
void goToRecentBooks();
void goToBrowser();
void goToReader(std::string path);
void goToSleep();
void goToSleep(bool fromTimeout = false);
void goToBoot();
void goToFullScreenMessage(std::string message, EpdFontFamily::Style style = EpdFontFamily::REGULAR);
void goToCrashReport();
void goHome();
void goHome(HomeMenuItem initialMenuItem = HomeMenuItem::NONE);
// This will move current activity to stack instead of deleting it
void pushActivity(std::unique_ptr<Activity>&& activity);
+1 -1
View File
@@ -1,5 +1,5 @@
#pragma once
#include "../Activity.h"
#include "activities/Activity.h"
class BootActivity final : public Activity {
public:
@@ -14,10 +14,20 @@
#include "components/UITheme.h"
#include "fontIds.h"
#include "images/Logo120.h"
#include "images/MoonIcon.h"
void SleepActivity::onEnter() {
Activity::onEnter();
const bool renderQuickResume =
SETTINGS.sleepScreen == CrossPointSettings::SLEEP_SCREEN_MODE::QUICK_RESUME ||
(fromTimeout &&
SETTINGS.quickResumeSleepScreen == CrossPointSettings::QUICK_RESUME_SLEEP_SCREEN::QUICK_RESUME_AFTER_TIMEOUT);
if (renderQuickResume) {
return renderLastScreenSleepScreen();
}
// Show popup with reader orientation only when going to sleep from reader
if (APP_STATE.lastSleepFromReader) {
ReaderUtils::applyOrientation(renderer, SETTINGS.orientation);
@@ -307,6 +317,12 @@ void SleepActivity::renderCoverSleepScreen() const {
return (this->*renderNoCoverSleepScreen)();
}
void SleepActivity::renderLastScreenSleepScreen() const {
const auto pageHeight = renderer.getScreenHeight();
renderer.drawImage(MoonIcon, 0, pageHeight - MOONICON_HEIGHT, MOONICON_WIDTH, MOONICON_HEIGHT);
renderer.displayBuffer(HalDisplay::HALF_REFRESH);
}
void SleepActivity::renderBlankSleepScreen() const {
renderer.clearScreen();
renderer.displayBuffer(HalDisplay::HALF_REFRESH);
+6 -3
View File
@@ -1,12 +1,12 @@
#pragma once
#include "../Activity.h"
#include "activities/Activity.h"
class Bitmap;
class SleepActivity final : public Activity {
public:
explicit SleepActivity(GfxRenderer& renderer, MappedInputManager& mappedInput)
: Activity("Sleep", renderer, mappedInput) {}
explicit SleepActivity(GfxRenderer& renderer, MappedInputManager& mappedInput, bool fromTimeout = false)
: Activity("Sleep", renderer, mappedInput), fromTimeout(fromTimeout) {}
void onEnter() override;
private:
@@ -14,5 +14,8 @@ class SleepActivity final : public Activity {
void renderCustomSleepScreen() const;
void renderCoverSleepScreen() const;
void renderBitmapSleepScreen(const Bitmap& bitmap) const;
void renderLastScreenSleepScreen() const;
void renderBlankSleepScreen() const;
bool fromTimeout = false;
};
@@ -1,6 +1,5 @@
#include "OpdsBookBrowserActivity.h"
#include <Epub.h>
#include <GfxRenderer.h>
#include <I18n.h>
#include <Logging.h>
@@ -8,11 +7,13 @@
#include <WiFi.h>
#include "MappedInputManager.h"
#include "SilentRestart.h"
#include "activities/network/WifiSelectionActivity.h"
#include "activities/util/KeyboardEntryActivity.h"
#include "components/UITheme.h"
#include "fontIds.h"
#include "network/HttpDownloader.h"
#include "util/BookCacheUtils.h"
#include "util/StringUtils.h"
#include "util/UrlUtils.h"
@@ -40,9 +41,14 @@ void OpdsBookBrowserActivity::onEnter() {
void OpdsBookBrowserActivity::onExit() {
Activity::onExit();
WiFi.mode(WIFI_OFF);
entries.clear();
navigationHistory.clear();
if (WiFi.getMode() != WIFI_MODE_NULL) {
WiFi.disconnect(false);
delay(30);
silentRestart();
}
}
void OpdsBookBrowserActivity::loop() {
@@ -274,10 +280,10 @@ void OpdsBookBrowserActivity::downloadBook(const OpdsEntry& book) {
downloadTotal = total;
requestUpdate(true);
},
server.username, server.password);
nullptr, server.username, server.password);
if (result == HttpDownloader::OK) {
Epub(filename, "/.crosspoint").clearCache();
clearBookCache(filename);
state = BrowserState::BROWSING;
} else {
state = BrowserState::ERROR;
@@ -364,8 +370,7 @@ void OpdsBookBrowserActivity::onWifiSelectionComplete(const bool connected) {
requestUpdate(true);
fetchFeed(currentPath);
} else {
WiFi.disconnect();
WiFi.mode(WIFI_OFF);
// Leave WiFi up; onExit's silent reboot handles teardown without fragmenting.
state = BrowserState::ERROR;
errorMessage = tr(STR_WIFI_CONN_FAILED);
requestUpdate();
@@ -5,8 +5,8 @@
#include <utility>
#include <vector>
#include "../Activity.h"
#include "OpdsServerStore.h"
#include "activities/Activity.h"
#include "util/ButtonNavigator.h"
/**

Some files were not shown because too many files have changed in this diff Show More