212 Commits

Author SHA1 Message Date
Sam Davis
6aa5d41a42
Add info about sleep screen customisation to user guide (#88)
## Summary

- Updates user guide with information about using custom sleep screens

## Additional Context

N/A
2025-12-21 18:32:50 +11:00
Dave Allie
2a27c6d068
Add JPG image support (#23)
## Summary

- Add basic JPG image support
- Map JPG back to 2-bit BMP output
- Can be used to later render the BMP file from disk or directly pass to
output if wanted
- Give the 3 passes over the data needed to render grayscale content,
putting it on disk is preferred to outputting it multiple times

## Additional Context

- WIP, looking forward to BMP support from
https://github.com/daveallie/crosspoint-reader/pull/16
- Addresses some of #11
2025-12-21 17:15:17 +11:00
Dave Allie
b73ae7fe74
Paginate book list and avoid out of bounds rendering (#86)
## Summary

* Paginate book list
* Avoid out of bounds rendering of long book titles, truncate with
ellipsis instead

## Additional Context

* Should partially help with
https://github.com/daveallie/crosspoint-reader/issues/75 as it was
previously rendering a lot of content off screen, will need to test with
a large directory
2025-12-21 17:12:53 +11:00
Dave Allie
f264efdb12
Extract EPUB TOC into temp file before parsing (#85)
## Summary

* Extract EPUB TOC into temp file before parsing
* Streaming ZIP -> XML parser uses up a lot of memory as we're
allocating inflation buffers while also holding a few copies of the
buffer in different forms
* Instead, but streaming the inflated file down to the SD card (like we
do for HTML parsing, we can lower memory usage)

## Additional Context

* This should help with
https://github.com/daveallie/crosspoint-reader/issues/60 and
https://github.com/daveallie/crosspoint-reader/issues/10. It won't
remove those class of issues completely, but will allow for many more
books to be opened.
2025-12-21 17:08:34 +11:00
Dave Allie
0d32d21d75
Small code cleanup (#83)
## Summary

* Fix cppcheck low violations
* Remove teardown method on parsers, use destructor
* Code cleanup
2025-12-21 15:43:53 +11:00
Dave Allie
9b4dfbd180
Allow any file to be uploaded (#84)
## Summary

- Allow any file to be uploaded
- Removes .epub restriction

## Additional Context

- Fixes #74
2025-12-21 15:43:17 +11:00
Jonas Diemer
926c786705
Keep ZipFile open to speed up getting file stats. (#76)
Still a bit raw, but gets the time required to determine the size of
each chapter (for reading progress) down from ~25ms to 0-1ms.

This is done by keeping the zipArchive open (so simple ;)).

Probably we don't need to cache the spine sizes anymore then...

---------

Co-authored-by: Dave Allie <dave@daveallie.com>
2025-12-21 14:38:51 +11:00
Dave Allie
299623927e
Build out lines when parsing html and holding >750 words in buffer (#73)
## Summary

* Build out lines for pages when holding over 750 buffered words
* Should fix issues with parsing long blocks of text causing memory
crashes
2025-12-21 13:43:19 +11:00
IFAKA
9a3bb81337
fix: add NULL checks after malloc in drawBmp() (#80)
## Problem
`drawBmp()` allocates two row buffers via `malloc()` but doesn't check
if allocations succeed. On low memory, this causes a crash when the NULL
pointers are dereferenced.

## Fix
Add NULL check after both `malloc()` calls. If either fails, log error
and return early.

Changed `lib/GfxRenderer/GfxRenderer.cpp`.

## Test
- Defensive addition only - no logic changes
- Manual device testing appreciated
2025-12-21 13:36:59 +11:00
IFAKA
73d1839ddd
fix: add bounds checks to Epub getter functions (#82)
## Problem
Three Epub getter functions can throw exceptions:
- `getCumulativeSpineItemSize()`: No bounds check before
`.at(spineIndex)`
- `getSpineItem()`: If spine is empty and index invalid, `.at(0)` throws
- `getTocItem()`: If toc is empty and index invalid, `.at(0)` throws

## Fix
- Add bounds check to `getCumulativeSpineItemSize()`, return 0 on error
- Add empty container checks to `getSpineItem()` and `getTocItem()`
- Use static fallback objects for safe reference returns on empty
containers

Changed `lib/Epub/Epub.cpp`.

## Test
- Defensive additions - follows existing bounds check patterns
- No logic changes for valid inputs
- Manual device testing appreciated
2025-12-21 13:36:30 +11:00
IFAKA
cc86533e86
fix: add NULL check after malloc in readFileToMemory() (#81)
## Problem
`readFileToMemory()` allocates an output buffer via `malloc()` at line
120 but doesn't check if allocation succeeds. On low memory, the NULL
pointer is passed to `fread()` causing a crash.

## Fix
Add NULL check after `malloc()` for the output buffer. Follows the
existing pattern already used for `deflatedData` at line 141.

Changed `lib/ZipFile/ZipFile.cpp`.

## Test
- Follows existing validated pattern from same function
- Defensive addition only - no logic changes
2025-12-21 13:35:37 +11:00
IFAKA
bf3f270067
fix: add NULL checks for frameBuffer in GfxRenderer (#79)
## Problem
`invertScreen()`, `storeBwBuffer()`, and `restoreBwBuffer()` dereference
`frameBuffer` without NULL validation. If the display isn't initialized,
these functions will crash.

## Fix
Add NULL checks before using `frameBuffer` in all three functions.
Follows the existing pattern from `drawPixel()` (line 11) which already
validates the pointer.

Changed `lib/GfxRenderer/GfxRenderer.cpp`.

## Test
- Follows existing validated pattern from `drawPixel()`
- No logic changes - only adds early return on NULL
- Manual device testing appreciated
2025-12-21 13:34:58 +11:00
Dave Allie
cfe838e03b
Update user guide 2025-12-20 01:44:39 +11:00
Dave Allie
7484fe478c
Replace cover.jpg 2025-12-20 01:15:20 +11:00
Brendan O'Leary
d41d539435
Add connect to Wifi and File Manager Webserver (#41)
## Summary

- **What is the goal of this PR?**  
Implements wireless EPUB file management via a built-in web server,
enabling users to upload, browse, organize, and delete EPUB files from
any device on the same WiFi network without needing a computer cable
connection.

- **What changes are included?**
- **New Web Server**
([`CrossPointWebServer.cpp`](src/CrossPointWebServer.cpp),
[`CrossPointWebServer.h`](src/CrossPointWebServer.h)):
    - HTTP server on port 80 with a responsive HTML/CSS interface
    - Home page showing device status (version, IP, free memory)
    - File Manager with folder navigation and breadcrumb support
    - EPUB file upload with progress tracking
    - Folder creation and file/folder deletion
    - XSS protection via HTML escaping
- Hidden system folders (`.` prefixed, "System Volume Information",
"XTCache")
  
- **WiFi Screen** ([`WifiScreen.cpp`](src/screens/WifiScreen.cpp),
[`WifiScreen.h`](src/screens/WifiScreen.h)):
    - Network scanning with signal strength indicators
    - Visual indicators for encrypted (`*`) and saved (`+`) networks
- State machine managing: scanning, network selection, password entry,
connecting, save/forget prompts
    - 15-second connection timeout handling
    - Integration with web server (starts on connect, stops on exit)
  
- **WiFi Credential Storage**
([`WifiCredentialStore.cpp`](src/WifiCredentialStore.cpp),
[`WifiCredentialStore.h`](src/WifiCredentialStore.h)):
    - Persistent storage in `/sd/.crosspoint/wifi.bin`
- XOR obfuscation for stored passwords (basic protection against casual
reading)
    - Up to 8 saved networks with add/remove/update operations
  
- **On-Screen Keyboard**
([`OnScreenKeyboard.cpp`](src/screens/OnScreenKeyboard.cpp),
[`OnScreenKeyboard.h`](src/screens/OnScreenKeyboard.h)):
    - Reusable QWERTY keyboard component with shift support
    - Special keys: Shift, Space, Backspace, Done
    - Support for password masking mode
  
- **Settings Screen Integration**
([`SettingsScreen.h`](src/screens/SettingsScreen.h)):
    - Added WiFi action to navigate to the new WiFi screen
  
  - **Documentation** ([`docs/webserver.md`](docs/webserver.md)):
- Comprehensive user guide covering WiFi setup, web interface usage,
file management, troubleshooting, and security notes
    - See this for more screenshots!
- Working "displays the right way in GitHub" on my repo:
https://github.com/olearycrew/crosspoint-reader/blob/feature/connect-to-wifi/docs/webserver.md

**Video demo**


https://github.com/user-attachments/assets/283e32dc-2d9f-4ae2-848e-01f41166a731

## Additional Context

- **Security considerations**: The web server has no
authentication—anyone on the same WiFi network can access files. This is
documented as a limitation, recommending use only on trusted private
networks. Password obfuscation in the credential store is XOR-based, not
cryptographically secure.

- **Memory implications**: The web server and WiFi stack consume
significant memory. The implementation properly cleans up (stops server,
disconnects WiFi, sets `WIFI_OFF` mode) when exiting the WiFi screen to
free resources.

- **Async operations**: Network scanning and connection use async
patterns with FreeRTOS tasks to prevent blocking the UI. The display
task handles rendering on a dedicated thread with mutex protection.

- **Browser compatibility**: The web interface uses standard
HTML5/CSS3/JavaScript and is tested to work with all modern browsers on
desktop and mobile.

---------

Co-authored-by: Dave Allie <dave@daveallie.com>
2025-12-20 01:05:43 +11:00
Dave Allie
cf6fec78dc
Cleanup indexing layout string 2025-12-20 00:33:55 +11:00
Jonas Diemer
10d76dde12
Randomly load Sleep Screen from /sleep/*bmp (if exists). (#71)
Load a random sleep screen. 

Only works for ".bmp" (not ".BMP"), but I think that's OK (we do the
same for EPUBs).
2025-12-20 00:17:26 +11:00
Jonas Diemer
7b5a63d220
Option to short-press power button. (#56)
Adresses #53 

Please check if we still need the code to "Give the user up to 1000ms to
start holding the power button, and must hold for
SETTINGS.getPowerButtonDuration()" - the power button should be pressed
already when waking up...

Also, decided to return before the delay to wait more to make the
behavior more immediate.

---------

Co-authored-by: Dave Allie <dave@daveallie.com>
2025-12-19 23:37:34 +11:00
IFAKA
c1d5f5d562
Add NULL checks after fopen() in ZipFile (#68)
## Problem
Three `fopen()` calls in ZipFile.cpp did not check for NULL before using
the file handle. If files cannot be opened, `fseek`/`fread`/`fclose`
receive NULL and crash.

## Fix
Added NULL checks with appropriate error logging and early returns for
all three locations:
- `getDataOffset()`
- `readFileToMemory()`
- `readFileToStream()`

## Testing
- Builds successfully with `pio run`
- Affects: `lib/ZipFile/ZipFile.cpp`
2025-12-19 23:28:43 +11:00
IFAKA
adfeee063f
Handle empty spine in getBookSize() and calculateProgress() (#67)
## Problem
- `getBookSize()` calls `getCumulativeSpineItemSize(getSpineItemsCount()
- 1)` which passes -1 when spine is empty
- `calculateProgress()` then divides by zero when book size is 0

## Fix
- Return 0 from `getBookSize()` if spine is empty
- Return 0 from `calculateProgress()` if book size is 0

## Testing
- Builds successfully with `pio run`
- Affects: `lib/Epub/Epub.cpp`
2025-12-19 23:28:36 +11:00
IFAKA
2d3928ed81
Validate file handle when reading progress.bin (#66)
## Problem
Reading progress.bin used `SD.exists()` then `SD.open()` without
checking if open succeeded. Race conditions or SD errors could cause
file handle to be invalid.

## Fix
- Removed redundant `SD.exists()` check
- Check if file opened successfully before reading
- Verify correct number of bytes were read

## Testing
- Builds successfully with `pio run`
- Affects: `src/activities/reader/EpubReaderActivity.cpp`
2025-12-19 23:27:08 +11:00
IFAKA
48249fbd1e
Check SD card initialization and show error on failure (#65)
## Problem
`SD.begin()` return value was ignored. If the SD card fails to
initialize, the device continues and crashes when trying to load
settings/state.

## Fix
Check return value and display "SD card error" message instead of
proceeding with undefined state.

## Testing
- Builds successfully with `pio run`
- Affects: `src/main.cpp`
2025-12-19 23:24:25 +11:00
IFAKA
1a53dccebd
Fix title truncation crash for short titles (#63)
## Problem
The status bar title truncation loop crashes when the chapter title is
shorter than 8 characters.

```cpp
// title.length() - 8 underflows when length < 8 (size_t is unsigned)
title = title.substr(0, title.length() - 8) + "...";
```

## Fix
Added a length guard to skip truncation for titles that are too short to
truncate safely.

## Testing
- Builds successfully with `pio run`
- Affects: `src/activities/reader/EpubReaderActivity.cpp`
2025-12-19 23:23:43 +11:00
IFAKA
3e28724b62
Add bounds checking for TOC/spine array access (#64)
## Problem
`getSpineIndexForTocIndex()` and `getTocIndexForSpineIndex()` access
`toc[tocIndex]` and `spine[spineIndex]` without validating indices are
within bounds. Malformed EPUBs or edge cases could trigger out-of-bounds
access.

## Fix
Added bounds validation at the start of both functions before accessing
the arrays.

## Testing
- Builds successfully with `pio run`
- Affects: `lib/Epub/Epub.cpp`
2025-12-19 23:23:23 +11:00
Jonas Diemer
d86b3fe134
Bugfix/word spacing indented (#59)
Simplified the indentation to fix having too large gaps between words
(original calculation was inaccurate).
2025-12-19 08:45:20 +11:00
Dave Allie
1a3d6b125d
Custom sleep screen support with BMP reading (#57)
## Summary

* Builds on top of
https://github.com/daveallie/crosspoint-reader/pull/16 - adresses
https://github.com/daveallie/crosspoint-reader/discussions/14
* This PR adds the ability for the user to supply a custom `sleep.bmp`
image at the root of the SD card that will be shown instead of the
default sleep screen if present.
* Supports:
  * Different BPPs:
    * 1bit
    * 2bit
    * 8bit
    * 24bit
    * 32bit (with alpha-channel ignored)
  * Grayscale rendering

---------

Co-authored-by: Sam Davis <sam@sjd.co>
2025-12-19 08:45:14 +11:00
Dave Allie
b2020f5512
Skip pagebreak blocks when parsing epub file (#58)
## Summary

* Skip pagebreak blocks when parsing epub file
* These blocks break the flow and often contain the page number in them
which should not interrupt the flow of the content
- Attributes sourced from:
  - https://www.w3.org/TR/epub-ssv-11/#pagebreak
  - https://www.w3.org/TR/dpub-aria-1.1/#doc-pagebreak
2025-12-19 01:11:03 +11:00
Dave Allie
70dc0f018e
Cut release 0.7.0 0.7.0 2025-12-19 00:44:22 +11:00
Jonas Diemer
424594488f
Caching of spine item sizes for faster book loading (saves 1-4 seconds). (#54)
As discussed in
https://github.com/daveallie/crosspoint-reader/pull/38#issuecomment-3665142427,
#38
2025-12-18 22:49:14 +11:00
Dave Allie
57fdb1c0fb
Rendering "Indexing..." on white screen to avoid partial update 2025-12-18 22:13:24 +11:00
Dave Allie
5e1694748c
Fix font readability by expanding blacks and trimming whites (#55)
## Summary

* Previously, only pure black pixels in the font were marked as black,
this expands the black range, and makes the lightest pixels white

## Additional Context

* Noticed personally it was kind of "thin" and washed out a bit, this
massively helps, should also address concerns raised here:
https://github.com/daveallie/crosspoint-reader/discussions/39
2025-12-18 21:39:13 +11:00
Jonas Diemer
063a1df851
Bugfix for #46: don't look at previous chapters if in chapter 0. (#48)
Fixes #46
2025-12-18 06:28:06 +11:00
Dave Allie
d429966dd4
Rename Screens to Activities and restructure files (#44)
## Summary

* This PR drastically reshapes the structure of the codebase, moving
from the concept of "Screens" to "Activities", restructing the files and
setting up the concept of subactivities.
* This should help with keep the main file clean and containing all
functional logic in the relevant activity.
* CrossPointState is now also a global singleton which should help with
accessing it from within activities.

## Additional Context

* This is probably going to be a bit disruptive for people with open
PRs, sorry 😞
2025-12-17 23:32:18 +11:00
Jonas Diemer
c78f2a9840
Calculate the progress in the book by file sizes of each chapter. (#38)
## Summary

Addresses #35.

Maybe it could be wise to do some caching of the spine sizes (but
performance isn't too bad).
2025-12-17 23:05:24 +11:00
Dave Allie
11f01d3a41
Add home screen (#42)
## Summary

* Add home screen
* Sits as new root screen, allows for navigation to settings or file
list
2025-12-17 20:47:43 +11:00
Arthur Tazhitdinov
973d372521
TOC location fix (#25)
## Summary

* Rely on media-type="application/x-dtbncx+xml" to find TOC instead of
hardcoded values

## Additional Context

* Most of my epubs don't have id==ncx for toc file location. I think
this media-type is EPUB standard

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-17 18:49:45 +11:00
Dave Allie
67da8139b3
Use 6x8kB chunks instead of 1x48kB chunk for secondary display buffer (#36)
## Summary

- When allocating the `bwBuffer` required to restore the RED RAM in the
EPD, we were previously allocating the whole frame buffer in one
contiguous memory chunk (48kB)
- Depending on the state of memory fragmentation at the time of this
call, it may not be possible to allocate all that memory
- Instead, we now allocate 6 blocks of 8kB instead of the full 48kB,
this should mean the display updates are more resilient to different
memory conditions

## Additional Context
2025-12-17 01:39:22 +11:00
Dave Allie
c287aa03a4
Use single buffer mode for EInkDisplay (#34)
## Summary

* Frees up 48kB of statically allocated RAM in exchange for 48kB just
when grayscale rendering is needed

## Additional Context

* Upstream changes:
https://github.com/open-x4-epaper/community-sdk/pull/7
2025-12-17 00:17:49 +11:00
Dave Allie
5d68c8b305
Cut release 0.6.0 0.6.0 2025-12-16 23:15:47 +11:00
Jonas Diemer
def7abbd60
Improve indent (#28)
* add horizontal indent in first line of paragraph in case Extra Paragraph Spacing is OFF

* Treat tabs as whitespace (so they are properly stripped)

* Changed size of indent to 1 em.

* Fixed calculation of space when indenting (avoiding squeezed text).

* Source code formatting
2025-12-16 23:02:32 +11:00
Jonas Diemer
9ad8111ce7
Wrap-around navigation in Settings. (#31) 2025-12-16 22:54:16 +11:00
Arthur Tazhitdinov
57d1939be7
Add Cyrillic range to fonts (#27)
* Enhance TOC parsing and chapter selection logic

- Update .gitignore to include additional paths
- Refactor Epub::parseContentOpf to improve NCX item retrieval
- Modify ContentOpfParser to store media type in ManifestItem
- Implement rebuildVisibleSpineIndices in EpubReaderChapterSelectionScreen for better chapter navigation
- Adjust rendering logic to handle empty chapter lists gracefully

* Refactor TOC parsing logic to streamline cover image and NCX item retrieval

* add cyrillic ranges

* revert

* clang format fix
2025-12-16 22:52:49 +11:00
Jonas Diemer
012992f904
Feature/auto poweroff (#32)
* Automatically deep sleep after 10 minutes of inactivity.

* formatting.
2025-12-16 22:49:31 +11:00
Dave Allie
c262f222de
Parse cover image path from content.opf file (#24) 2025-12-16 03:15:54 +11:00
Dave Allie
449b3ca161
Fixed light gray text rendering 2025-12-16 02:16:38 +11:00
Dave Allie
6989035ef8
Run CI action on PR as well as push 2025-12-15 23:17:35 +11:00
Dave Allie
108cf57202
Fix formatting 2025-12-15 23:17:23 +11:00
Jonas Diemer
a640fbecf8
Settings Screen and first 2 settings (#18)
* white sleep screen

* quicker pwr button

* no extra spacing between paragraphs

* Added settings class with de/serialization and whiteSleepScreen setting to control inverting the sleep screen

* Added Settings screen for real, made settings a global singleton

* Added setting for extra paragraph spacing.

* fixed typo

* Rework after feedback

* Fixed type from bool to uint8
2025-12-15 23:16:46 +11:00
Dave Allie
7a5719b46d
Upgrade open-x4-sdk to fix white streaks on sleep screen (#21)
https://github.com/open-x4-epaper/community-sdk/pull/6 fixes a power down issue with the display which was causing streaks to appear on the sleep screen
2025-12-15 22:27:27 +11:00
Dave Allie
8c3576e397
Add Github Action to build release firmware on tag (#20) 2025-12-15 20:00:34 +11:00