Add chapter select support to XTC files (#145)

## Summary

- **What is the goal of this PR?** Add chapter selection support to the
XTC reader activity, including parsing chapter metadata from XTC files.
- **What changes are included?** Implemented XTC chapter parsing and
exposure in the XTC library, added a chapter selection activity for XTC,
integrated it into XtcReaderActivity, and normalized chapter page
indices by shifting them to 0-based.

  ## Additional Context

- The reader uses 0-based page indexing (first page = 0), but the XTC
chapter table appears to be 1-based (first page = 1), so chapter
start/end pages are shifted down by 1 during parsing.
This commit is contained in:
Sam Davis
2025-12-30 12:49:18 +11:00
committed by GitHub
parent b01eb50325
commit 278b056bd0
9 changed files with 376 additions and 5 deletions

View File

@@ -13,6 +13,7 @@
#include "CrossPointSettings.h"
#include "CrossPointState.h"
#include "XtcReaderChapterSelectionActivity.h"
#include "config.h"
namespace {
@@ -27,7 +28,7 @@ void XtcReaderActivity::taskTrampoline(void* param) {
}
void XtcReaderActivity::onEnter() {
Activity::onEnter();
ActivityWithSubactivity::onEnter();
if (!xtc) {
return;
@@ -56,7 +57,7 @@ void XtcReaderActivity::onEnter() {
}
void XtcReaderActivity::onExit() {
Activity::onExit();
ActivityWithSubactivity::onExit();
// Wait until not rendering to delete task
xSemaphoreTake(renderingMutex, portMAX_DELAY);
@@ -70,6 +71,32 @@ void XtcReaderActivity::onExit() {
}
void XtcReaderActivity::loop() {
// Pass input responsibility to sub activity if exists
if (subActivity) {
subActivity->loop();
return;
}
// Enter chapter selection activity
if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) {
if (xtc && xtc->hasChapters() && !xtc->getChapters().empty()) {
xSemaphoreTake(renderingMutex, portMAX_DELAY);
exitActivity();
enterNewActivity(new XtcReaderChapterSelectionActivity(
this->renderer, this->mappedInput, xtc, currentPage,
[this] {
exitActivity();
updateRequired = true;
},
[this](const uint32_t newPage) {
currentPage = newPage;
exitActivity();
updateRequired = true;
}));
xSemaphoreGive(renderingMutex);
}
}
// Long press BACK (1s+) goes directly to home
if (mappedInput.isPressed(MappedInputManager::Button::Back) && mappedInput.getHeldTime() >= goHomeMs) {
onGoHome();