#pragma once #include #include struct Token { uint32_t offset; uint16_t length; uint8_t trailingLineBreaks; }; static_assert(sizeof(Token) == 8, "Token storage must remain memory-budgeted"); struct StyleToken { uint32_t index; uint8_t styleFlags; uint8_t timingModifier; }; class Reader { public: bool load(const String& book, const std::vector& sidecar); void restoreAtParagraphStart(uint32_t savedWord); const String currentWord() const; bool advance(); bool previousParagraph(); bool nextParagraph(); bool lastParagraph(); uint32_t currentIndex() const; uint32_t wordCount() const; uint8_t progressPercent() const; uint32_t delayMs(uint16_t wpm) const; uint8_t currentStyleFlags() const; bool sidecarValid() const; private: String book_; std::vector tokens_; std::vector styleTokens_; std::vector paragraphStarts_; uint32_t index_ = 0; size_t nextStyleToken_ = 0; bool sidecarValid_ = false; uint64_t sourceFingerprint_ = 0; bool loadSidecar(const std::vector& sidecar); uint32_t paragraphStartFor(uint32_t wordIndex) const; void seekStyleToken(); const StyleToken* currentStyleToken() const; };