Consolidate companion components
This commit is contained in:
1 parent
aab8f5038a
commit
ebee99af81
32 files changed
+286
-287
No files matched your search
@@ -1,15 +1,15 @@
|
||||
#include "CompanionJohn.h"
|
||||
#include "CompanionReader.h"
|
||||
|
||||
#if COMPANION_FEATURE_JOHN
|
||||
#if COMPANION_FEATURE_READER
|
||||
#include <Arduino.h>
|
||||
#include "bible/JohnLookup.h"
|
||||
#include "bible/ReaderLookup.h"
|
||||
|
||||
#include "bible/JohnData.generated.h"
|
||||
#include "bible/ReaderData.generated.h"
|
||||
|
||||
namespace mesh {
|
||||
bible::LookupResult readJohnVerse(bible::Reference ref, char* scratch,
|
||||
bible::LookupResult readReaderVerse(bible::Reference ref, char* scratch,
|
||||
size_t capacity, const char*& text) {
|
||||
return bible::lookup(bible::generated::johnCorpus, ref, scratch, capacity, text);
|
||||
return bible::lookup(bible::generated::readerCorpus, ref, scratch, capacity, text);
|
||||
}
|
||||
|
||||
namespace {
|
||||
@@ -19,30 +19,30 @@ namespace {
|
||||
__attribute__((noinline)) void printVerse(bible::Reference ref, Stream& output) {
|
||||
char scratch[bible::kBlockSize];
|
||||
const char* text = nullptr;
|
||||
const bible::LookupResult result = readJohnVerse(ref, scratch, sizeof(scratch), text);
|
||||
const bible::LookupResult result = readReaderVerse(ref, scratch, sizeof(scratch), text);
|
||||
if (result == bible::LookupResult::Found) {
|
||||
output.printf(" John %u:%u (%s)\r\n", static_cast<unsigned>(ref.chapter),
|
||||
static_cast<unsigned>(ref.verse), bible::generated::johnCorpus.translation);
|
||||
output.printf(" Reader %u:%u (%s)\r\n", static_cast<unsigned>(ref.chapter),
|
||||
static_cast<unsigned>(ref.verse), bible::generated::readerCorpus.translation);
|
||||
// Do not feed long text through Adafruit Print::printf's 256-byte scratch.
|
||||
output.print(text);
|
||||
output.print("\r\n");
|
||||
output.print(bible::generated::johnCorpus.attribution);
|
||||
output.print(bible::generated::readerCorpus.attribution);
|
||||
output.print("\r\n");
|
||||
} else if (result == bible::LookupResult::Missing) {
|
||||
output.print(" This verse is not present in the supplied translation.\r\n");
|
||||
} else {
|
||||
output.print(" ERROR: invalid compressed John data\r\n");
|
||||
output.print(" ERROR: invalid compressed Reader data\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool handleJohnCommand(const char* command, Stream& output) {
|
||||
bool handleReaderCommand(const char* command, Stream& output) {
|
||||
bible::Reference ref = {};
|
||||
const bible::ParseResult parsed = bible::parse(command, ref);
|
||||
if (parsed == bible::ParseResult::NoMatch) return false;
|
||||
if (parsed == bible::ParseResult::Invalid) {
|
||||
output.print(" ERROR: use get John <chapter>:<verse> (example: get John 3:16)\r\n");
|
||||
output.print(" ERROR: use get reader <chapter>:<verse> (example: get reader 3:16)\r\n");
|
||||
} else {
|
||||
printVerse(ref, output);
|
||||
}
|
||||
@@ -1,14 +1,14 @@
|
||||
#pragma once
|
||||
#include "CompanionJohnConfig.h"
|
||||
#include "CompanionReaderConfig.h"
|
||||
|
||||
#if COMPANION_FEATURE_JOHN
|
||||
#include "bible/JohnLookup.h"
|
||||
#if COMPANION_FEATURE_READER
|
||||
#include "bible/ReaderLookup.h"
|
||||
class Stream;
|
||||
namespace mesh {
|
||||
// Caller-owned scratch lets the terminal and screen share one flash corpus.
|
||||
bible::LookupResult readJohnVerse(bible::Reference ref, char* scratch,
|
||||
bible::LookupResult readReaderVerse(bible::Reference ref, char* scratch,
|
||||
size_t capacity, const char*& text);
|
||||
// Terminal-only: verses can exceed the framed/remote CLI's 160-byte reply.
|
||||
bool handleJohnCommand(const char* command, Stream& output);
|
||||
bool handleReaderCommand(const char* command, Stream& output);
|
||||
}
|
||||
#endif
|
||||
@@ -1,16 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef COMPANION_FEATURE_JOHN
|
||||
#ifndef COMPANION_FEATURE_READER
|
||||
#if defined(COMPANION_RADIO_FULL) && defined(ENABLE_USB_INTERFACE) \
|
||||
&& (defined(ESP32_PLATFORM) || defined(NRF52_PLATFORM))
|
||||
#define COMPANION_FEATURE_JOHN 1
|
||||
#define COMPANION_FEATURE_READER 1
|
||||
#else
|
||||
#define COMPANION_FEATURE_JOHN 0
|
||||
#define COMPANION_FEATURE_READER 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if COMPANION_FEATURE_JOHN \
|
||||
#if COMPANION_FEATURE_READER \
|
||||
&& !(defined(COMPANION_RADIO_FULL) && defined(ENABLE_USB_INTERFACE) \
|
||||
&& (defined(ESP32_PLATFORM) || defined(NRF52_PLATFORM)))
|
||||
#error "John lookup requires an ESP32 or nRF52 Full Companion USB terminal"
|
||||
#error "Reader lookup requires an ESP32 or nRF52 Full Companion USB terminal"
|
||||
#endif
|
||||
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#include "JohnLookup.h"
|
||||
#include "ReaderLookup.h"
|
||||
|
||||
namespace mesh { namespace bible {
|
||||
|
||||
@@ -92,7 +92,7 @@ inline uint32_t bookmarkChecksum(const uint8_t* data) {
|
||||
return hash;
|
||||
}
|
||||
inline void encodeBookmark(Position pos, uint8_t (&out)[kBookmarkBytes]) {
|
||||
out[0] = 'J'; out[1] = 'N'; out[2] = 1; out[3] = 0;
|
||||
out[0] = 'R'; out[1] = 'D'; out[2] = 1; out[3] = 0;
|
||||
out[4] = pos.verse; out[5] = pos.verse >> 8;
|
||||
out[6] = pos.offset; out[7] = pos.offset >> 8;
|
||||
const uint32_t hash = bookmarkChecksum(out);
|
||||
@@ -100,7 +100,7 @@ inline void encodeBookmark(Position pos, uint8_t (&out)[kBookmarkBytes]) {
|
||||
}
|
||||
inline bool decodeBookmark(const uint8_t (&in)[kBookmarkBytes], Position& pos) {
|
||||
pos = Position{};
|
||||
if (in[0] != 'J' || in[1] != 'N' || in[2] != 1 || in[3] != 0) return false;
|
||||
if (in[0] != 'R' || in[1] != 'D' || in[2] != 1 || in[3] != 0) return false;
|
||||
const uint32_t hash = bookmarkChecksum(in);
|
||||
for (unsigned i = 0; i < 4; ++i)
|
||||
if (in[8 + i] != static_cast<uint8_t>(hash >> (8 * i))) return false;
|
||||
@@ -112,7 +112,7 @@ inline bool decodeBookmark(const uint8_t (&in)[kBookmarkBytes], Position& pos) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Debounced checkpointing: no writes just for opening John 1:1, no rewrite
|
||||
// Debounced checkpointing: no writes just for opening reader 1:1, no rewrite
|
||||
// of unchanged positions, and a failed save remains pending for retry.
|
||||
class ReaderBookmark {
|
||||
Position _position, _saved;
|
||||
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#include "JohnReader.h"
|
||||
#include "Reader.h"
|
||||
|
||||
namespace mesh { namespace bible {
|
||||
// A verified replacement plus one previous record also works on SPIFFS,
|
||||
@@ -9,8 +9,8 @@ template <typename Files>
|
||||
bool loadReaderBookmark(Files& files, Position& pos) {
|
||||
uint8_t data[kBookmarkBytes];
|
||||
pos = Position{};
|
||||
if (files.read("/john.pos", data) && decodeBookmark(data, pos)) return true;
|
||||
return files.read("/john.pos.bak", data) && decodeBookmark(data, pos);
|
||||
if (files.read("/reader.pos", data) && decodeBookmark(data, pos)) return true;
|
||||
return files.read("/reader.pos.bak", data) && decodeBookmark(data, pos);
|
||||
}
|
||||
|
||||
template <typename Files>
|
||||
@@ -19,26 +19,26 @@ bool saveReaderBookmark(Files& files, Position pos) {
|
||||
if (pos.atStart()) {
|
||||
// Remove the fallback before the live record so a completed clear cannot
|
||||
// resurrect an earlier place on reboot. Do not create a start marker.
|
||||
return files.remove("/john.pos.tmp") && files.remove("/john.pos.bak")
|
||||
&& files.remove("/john.pos");
|
||||
return files.remove("/reader.pos.tmp") && files.remove("/reader.pos.bak")
|
||||
&& files.remove("/reader.pos");
|
||||
}
|
||||
uint8_t data[kBookmarkBytes], verify[kBookmarkBytes];
|
||||
encodeBookmark(pos, data);
|
||||
if (!files.remove("/john.pos.tmp") || !files.write("/john.pos.tmp", data)) return false;
|
||||
if (!files.read("/john.pos.tmp", verify) || memcmp(data, verify, sizeof(data)) != 0)
|
||||
if (!files.remove("/reader.pos.tmp") || !files.write("/reader.pos.tmp", data)) return false;
|
||||
if (!files.read("/reader.pos.tmp", verify) || memcmp(data, verify, sizeof(data)) != 0)
|
||||
return false;
|
||||
if (files.exists("/john.pos")) {
|
||||
if (files.exists("/reader.pos")) {
|
||||
// Keep a valid fallback if the live record was interrupted/corrupt.
|
||||
Position old;
|
||||
// A failed read is not proof of corruption. Preserve the live record on
|
||||
// transient I/O failure rather than deleting it before publication.
|
||||
if (!files.read("/john.pos", verify)) return false;
|
||||
if (!files.read("/reader.pos", verify)) return false;
|
||||
if (decodeBookmark(verify, old)) {
|
||||
if (!files.remove("/john.pos.bak") || !files.rename("/john.pos", "/john.pos.bak"))
|
||||
if (!files.remove("/reader.pos.bak") || !files.rename("/reader.pos", "/reader.pos.bak"))
|
||||
return false;
|
||||
} else if (!files.remove("/john.pos")) return false;
|
||||
} else if (!files.remove("/reader.pos")) return false;
|
||||
}
|
||||
if (!files.rename("/john.pos.tmp", "/john.pos")) return false;
|
||||
if (!files.rename("/reader.pos.tmp", "/reader.pos")) return false;
|
||||
// Retain the last valid fallback until the next successful replacement.
|
||||
return true;
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
// Generated by tools/bible/pack_john.py; do not edit.
|
||||
// World English Bible (engwebp), John. Public domain; see tools/bible/README.md.
|
||||
// Generated by tools/bible/pack_reader.py; do not edit.
|
||||
// Offline Reader corpus. Public domain; see tools/bible/README.md.
|
||||
// Firmware typography: ASCII quotes, apostrophes, dashes and spaces; wording unchanged.
|
||||
// Canonical source SHA-256: 916bf9d1feae0ad410d69f61e6c5f328ac6acba10651d751b5bb29719732c49e
|
||||
#pragma once
|
||||
#include "JohnLookup.h"
|
||||
#include "ReaderLookup.h"
|
||||
namespace mesh { namespace bible { namespace generated {
|
||||
static_assert(kBlockSize == 2048, "Regenerate John data after changing block size");
|
||||
static_assert(sizeof(BlockIndex) == 12, "John flash-size accounting expects a 12-byte index");
|
||||
static const uint8_t johnData[] = {
|
||||
static_assert(kBlockSize == 2048, "Regenerate Reader data after changing block size");
|
||||
static_assert(sizeof(BlockIndex) == 12, "Reader flash-size accounting expects a 12-byte index");
|
||||
static const uint8_t readerData[] = {
|
||||
0x8d,0x54,0xc1,0x6e,0xdb,0x30,0x0c,0xcd,0xa7,0x10,0xbe,0xf4,0x12,0xf8,0x03,0x76,0x29,0xb6,0x61,0xed,
|
||||
0x5a,0x6c,0xbb,0xac,0x40,0xcf,0x72,0x4c,0x5b,0x5a,0x64,0x29,0x90,0xec,0x06,0xd9,0xd7,0xef,0x91,0x92,
|
||||
0xdd,0xb4,0xdb,0x61,0x97,0xc4,0xa2,0x28,0xf2,0xf1,0xf1,0x91,0x0f,0x81,0x66,0xcb,0xd4,0xf1,0xe8,0x42,
|
||||
@@ -2175,7 +2175,7 @@ static const uint8_t johnData[] = {
|
||||
0xc6,0x51,0x3c,0x7b,0xbd,0x92,0xc0,0xdb,0xfa,0x70,0xd9,0x94,0x44,0xf6,0xab,0x34,0x0b,0xc1,0x09,0x7c,
|
||||
0x7b,0xf3,0x17,
|
||||
};
|
||||
static const BlockIndex johnBlocks[] = {
|
||||
static const BlockIndex readerBlocks[] = {
|
||||
{0, 841, 1930, 0},
|
||||
{841, 910, 1946, 22},
|
||||
{1751, 884, 1881, 41},
|
||||
@@ -2226,10 +2226,10 @@ static const BlockIndex johnBlocks[] = {
|
||||
{41668, 923, 2027, 854},
|
||||
{42591, 712, 1669, 869},
|
||||
};
|
||||
static const Corpus johnCorpus = {
|
||||
static const Corpus readerCorpus = {
|
||||
"WEB",
|
||||
"World English Bible - public domain (eBible.org).",
|
||||
johnData, sizeof(johnData), johnBlocks,
|
||||
sizeof(johnBlocks) / sizeof(johnBlocks[0]), kVerseCount
|
||||
readerData, sizeof(readerData), readerBlocks,
|
||||
sizeof(readerBlocks) / sizeof(readerBlocks[0]), kVerseCount
|
||||
};
|
||||
}}}
|
||||
@@ -71,7 +71,7 @@ inline ParseResult parse(const char* command, Reference& ref) {
|
||||
skipSpace(command);
|
||||
if (!token(command, "get")) return ParseResult::NoMatch;
|
||||
skipSpace(command);
|
||||
if (!token(command, "john")) return ParseResult::NoMatch;
|
||||
if (!token(command, "reader")) return ParseResult::NoMatch;
|
||||
skipSpace(command);
|
||||
if (!number(command, ref.chapter) || *command != ':') return ParseResult::Invalid;
|
||||
++command;
|
||||
@@ -2,8 +2,8 @@
|
||||
* Compile the vendored tinf core as C. Building the same source as C++ costs
|
||||
* several extra kilobytes with the embedded toolchains used by MeshCore.
|
||||
*/
|
||||
#include "../CompanionJohnConfig.h"
|
||||
#if defined(ENABLE_OTA) || defined(OTA_TRANSPORT_DEFLATE_TEST) || COMPANION_FEATURE_JOHN
|
||||
#include "../CompanionReaderConfig.h"
|
||||
#if defined(ENABLE_OTA) || defined(OTA_TRANSPORT_DEFLATE_TEST) || COMPANION_FEATURE_READER
|
||||
#if defined(__GNUC__) && !defined(__clang__)
|
||||
/* nRF52's Arduino recipe defaults to -Ofast, which more than doubles this
|
||||
* decoder. Keep the size policy local to tinf instead of changing MeshCore. */
|
||||
|
||||
@@ -13,7 +13,7 @@ MeshCore changes are marked in tinf.h and tinflate.c. They add
|
||||
tinf_uncompress_exact(), which accepts legal padding bits in the byte containing
|
||||
the final end-of-block code but rejects any trailing whole input byte. The C
|
||||
implementation is included by ../OtaTinf.c for ENABLE_OTA application builds,
|
||||
COMPANION_FEATURE_JOHN offline lookup, and the native OTA test profile.
|
||||
COMPANION_FEATURE_READER offline lookup, and the native OTA test profile.
|
||||
../OtaDeflate.cpp is the small C++
|
||||
callback adapter. Keeping the decoder in a true C translation unit avoids the
|
||||
several-kilobyte penalty produced by the embedded C++ toolchains. Firmware
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
* MeshCore modifications (2026):
|
||||
* - expose tinf_uncompress_exact() to reject trailing whole input bytes;
|
||||
* - compile this vendored implementation through OtaTinf.c only, keeping
|
||||
* it out of builds which need neither OTA nor offline John lookup.
|
||||
* it out of builds which need neither OTA nor offline reader lookup.
|
||||
* See README.meshcore.txt for the upstream revision and integration details.
|
||||
*/
|
||||
|
||||
|
||||
Reference in new issue
Block a user