Remove ancient binary settings code
This commit is contained in:
+1
-1
@@ -447,4 +447,4 @@ pio device monitor
|
||||
|
||||
If the device is stuck in a bootloop, press and release the Reset button. Then, press and hold on to the configured Back button and the Power Button to boot to the Home Screen.
|
||||
|
||||
There can be issues with broken cache or config. In this case, delete the `.crosspoint` directory on your SD card (or consider deleting only `settings.bin`, `state.bin`, or `epub_*` cache directories in the `.crosspoint/` folder).
|
||||
There can be issues with broken cache or config. In this case, delete the `.crosspoint` directory on your SD card (or consider deleting only `settings.json`, `state.json`, or `epub_*` cache directories in the `.crosspoint/` folder).
|
||||
|
||||
@@ -158,8 +158,8 @@ Typical persisted areas on SD:
|
||||
progress.bin
|
||||
cover.bmp
|
||||
sections/*.bin
|
||||
settings.bin
|
||||
state.bin
|
||||
settings.json
|
||||
state.json
|
||||
```
|
||||
|
||||
For binary cache formats, see `docs/file-formats.md`.
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#include <Logging.h>
|
||||
#include <MD5Builder.h>
|
||||
#include <ObfuscationUtils.h>
|
||||
#include <Serialization.h>
|
||||
|
||||
#include "../../src/JsonSettingsIO.h"
|
||||
|
||||
@@ -12,26 +11,10 @@
|
||||
KOReaderCredentialStore KOReaderCredentialStore::instance;
|
||||
|
||||
namespace {
|
||||
// File format version (for binary migration)
|
||||
constexpr uint8_t KOREADER_FILE_VERSION = 1;
|
||||
|
||||
// File paths
|
||||
constexpr char KOREADER_FILE_BIN[] = "/.crosspoint/koreader.bin";
|
||||
constexpr char KOREADER_FILE_JSON[] = "/.crosspoint/koreader.json";
|
||||
constexpr char KOREADER_FILE_BAK[] = "/.crosspoint/koreader.bin.bak";
|
||||
|
||||
// Default sync server URL
|
||||
constexpr char DEFAULT_SERVER_URL[] = "https://sync.koreader.rocks:443";
|
||||
|
||||
// Legacy obfuscation key - "KOReader" in ASCII (only used for binary migration)
|
||||
constexpr uint8_t LEGACY_OBFUSCATION_KEY[] = {0x4B, 0x4F, 0x52, 0x65, 0x61, 0x64, 0x65, 0x72};
|
||||
constexpr size_t LEGACY_KEY_LENGTH = sizeof(LEGACY_OBFUSCATION_KEY);
|
||||
|
||||
void legacyDeobfuscate(std::string& data) {
|
||||
for (size_t i = 0; i < data.size(); i++) {
|
||||
data[i] ^= LEGACY_OBFUSCATION_KEY[i % LEGACY_KEY_LENGTH];
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
bool KOReaderCredentialStore::saveToFile() const {
|
||||
@@ -40,7 +23,6 @@ bool KOReaderCredentialStore::saveToFile() const {
|
||||
}
|
||||
|
||||
bool KOReaderCredentialStore::loadFromFile() {
|
||||
// Try JSON first
|
||||
if (Storage.exists(KOREADER_FILE_JSON)) {
|
||||
String json = Storage.readFile(KOREADER_FILE_JSON);
|
||||
if (!json.isEmpty()) {
|
||||
@@ -54,70 +36,10 @@ bool KOReaderCredentialStore::loadFromFile() {
|
||||
}
|
||||
}
|
||||
|
||||
// Fall back to binary migration
|
||||
if (Storage.exists(KOREADER_FILE_BIN)) {
|
||||
if (loadFromBinaryFile()) {
|
||||
if (saveToFile()) {
|
||||
Storage.rename(KOREADER_FILE_BIN, KOREADER_FILE_BAK);
|
||||
LOG_DBG("KRS", "Migrated koreader.bin to koreader.json");
|
||||
return true;
|
||||
} else {
|
||||
LOG_ERR("KRS", "Failed to save KOReader credentials during migration");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LOG_DBG("KRS", "No credentials file found");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool KOReaderCredentialStore::loadFromBinaryFile() {
|
||||
FsFile file;
|
||||
if (!Storage.openFileForRead("KRS", KOREADER_FILE_BIN, file)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t version;
|
||||
serialization::readPod(file, version);
|
||||
if (version != KOREADER_FILE_VERSION) {
|
||||
LOG_DBG("KRS", "Unknown file version: %u", version);
|
||||
file.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (file.available()) {
|
||||
serialization::readString(file, username);
|
||||
} else {
|
||||
username.clear();
|
||||
}
|
||||
|
||||
if (file.available()) {
|
||||
serialization::readString(file, password);
|
||||
legacyDeobfuscate(password);
|
||||
} else {
|
||||
password.clear();
|
||||
}
|
||||
|
||||
if (file.available()) {
|
||||
serialization::readString(file, serverUrl);
|
||||
} else {
|
||||
serverUrl.clear();
|
||||
}
|
||||
|
||||
if (file.available()) {
|
||||
uint8_t method;
|
||||
serialization::readPod(file, method);
|
||||
matchMethod = static_cast<DocumentMatchMethod>(method);
|
||||
} else {
|
||||
matchMethod = DocumentMatchMethod::FILENAME;
|
||||
}
|
||||
|
||||
file.close();
|
||||
LOG_DBG("KRS", "Loaded KOReader credentials from binary for user: %s", username.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
void KOReaderCredentialStore::setCredentials(const std::string& user, const std::string& pass) {
|
||||
username = user;
|
||||
password = pass;
|
||||
|
||||
@@ -32,8 +32,6 @@ class KOReaderCredentialStore {
|
||||
// Private constructor for singleton
|
||||
KOReaderCredentialStore() = default;
|
||||
|
||||
bool loadFromBinaryFile();
|
||||
|
||||
friend bool JsonSettingsIO::saveKOReader(const KOReaderCredentialStore&, const char*);
|
||||
friend bool JsonSettingsIO::loadKOReader(KOReaderCredentialStore&, const char*, bool*);
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
#include <HalStorage.h>
|
||||
#include <JsonSettingsIO.h>
|
||||
#include <Logging.h>
|
||||
#include <Serialization.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
@@ -31,50 +30,8 @@ static_assert(SMALL_FONT_ID != 0, "Font ID collision with sentinel");
|
||||
// Initialize the static instance
|
||||
CrossPointSettings CrossPointSettings::instance;
|
||||
|
||||
void readAndValidate(FsFile& file, uint8_t& member, const uint8_t maxValue) {
|
||||
uint8_t tempValue;
|
||||
serialization::readPod(file, tempValue);
|
||||
if (tempValue < maxValue) {
|
||||
member = tempValue;
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
constexpr uint8_t SETTINGS_FILE_VERSION = 1;
|
||||
constexpr char SETTINGS_FILE_BIN[] = "/.crosspoint/settings.bin";
|
||||
constexpr char SETTINGS_FILE_JSON[] = "/.crosspoint/settings.json";
|
||||
constexpr char SETTINGS_FILE_BAK[] = "/.crosspoint/settings.bin.bak";
|
||||
|
||||
// Convert legacy front button layout into explicit logical->hardware mapping.
|
||||
void applyLegacyFrontButtonLayout(CrossPointSettings& settings) {
|
||||
switch (static_cast<CrossPointSettings::FRONT_BUTTON_LAYOUT>(settings.frontButtonLayout)) {
|
||||
case CrossPointSettings::LEFT_RIGHT_BACK_CONFIRM:
|
||||
settings.frontButtonBack = CrossPointSettings::FRONT_HW_LEFT;
|
||||
settings.frontButtonConfirm = CrossPointSettings::FRONT_HW_RIGHT;
|
||||
settings.frontButtonLeft = CrossPointSettings::FRONT_HW_BACK;
|
||||
settings.frontButtonRight = CrossPointSettings::FRONT_HW_CONFIRM;
|
||||
break;
|
||||
case CrossPointSettings::LEFT_BACK_CONFIRM_RIGHT:
|
||||
settings.frontButtonBack = CrossPointSettings::FRONT_HW_CONFIRM;
|
||||
settings.frontButtonConfirm = CrossPointSettings::FRONT_HW_LEFT;
|
||||
settings.frontButtonLeft = CrossPointSettings::FRONT_HW_BACK;
|
||||
settings.frontButtonRight = CrossPointSettings::FRONT_HW_RIGHT;
|
||||
break;
|
||||
case CrossPointSettings::BACK_CONFIRM_RIGHT_LEFT:
|
||||
settings.frontButtonBack = CrossPointSettings::FRONT_HW_BACK;
|
||||
settings.frontButtonConfirm = CrossPointSettings::FRONT_HW_CONFIRM;
|
||||
settings.frontButtonLeft = CrossPointSettings::FRONT_HW_RIGHT;
|
||||
settings.frontButtonRight = CrossPointSettings::FRONT_HW_LEFT;
|
||||
break;
|
||||
case CrossPointSettings::BACK_CONFIRM_LEFT_RIGHT:
|
||||
default:
|
||||
settings.frontButtonBack = CrossPointSettings::FRONT_HW_BACK;
|
||||
settings.frontButtonConfirm = CrossPointSettings::FRONT_HW_CONFIRM;
|
||||
settings.frontButtonLeft = CrossPointSettings::FRONT_HW_LEFT;
|
||||
settings.frontButtonRight = CrossPointSettings::FRONT_HW_RIGHT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void enforceFixedShortActions(CrossPointSettings& settings) {
|
||||
settings.btnShortBack = static_cast<uint8_t>(CrossPointSettings::BUTTON_ACTION::BTN_DEFAULT);
|
||||
@@ -147,142 +104,9 @@ bool CrossPointSettings::loadFromFile() {
|
||||
}
|
||||
}
|
||||
|
||||
// Fall back to binary migration
|
||||
if (Storage.exists(SETTINGS_FILE_BIN)) {
|
||||
if (loadFromBinaryFile()) {
|
||||
enforceFixedShortActions(*this);
|
||||
if (saveToFile()) {
|
||||
Storage.rename(SETTINGS_FILE_BIN, SETTINGS_FILE_BAK);
|
||||
LOG_DBG("CPS", "Migrated settings.bin to settings.json");
|
||||
return true;
|
||||
} else {
|
||||
LOG_ERR("CPS", "Failed to save migrated settings to JSON");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CrossPointSettings::loadFromBinaryFile() {
|
||||
FsFile inputFile;
|
||||
if (!Storage.openFileForRead("CPS", SETTINGS_FILE_BIN, inputFile)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t version;
|
||||
serialization::readPod(inputFile, version);
|
||||
if (version != SETTINGS_FILE_VERSION) {
|
||||
LOG_ERR("CPS", "Deserialization failed: Unknown version %u", version);
|
||||
inputFile.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t fileSettingsCount = 0;
|
||||
serialization::readPod(inputFile, fileSettingsCount);
|
||||
|
||||
uint8_t settingsRead = 0;
|
||||
bool frontButtonMappingRead = false;
|
||||
do {
|
||||
readAndValidate(inputFile, sleepScreen, SLEEP_SCREEN_MODE_COUNT);
|
||||
if (++settingsRead >= fileSettingsCount) break;
|
||||
serialization::readPod(inputFile, extraParagraphSpacing);
|
||||
if (++settingsRead >= fileSettingsCount) break;
|
||||
{
|
||||
uint8_t ignored;
|
||||
serialization::readPod(inputFile, ignored);
|
||||
} // legacy shortPwrBtn field
|
||||
if (++settingsRead >= fileSettingsCount) break;
|
||||
readAndValidate(inputFile, statusBar, STATUS_BAR_MODE_COUNT); // legacy
|
||||
if (++settingsRead >= fileSettingsCount) break;
|
||||
readAndValidate(inputFile, orientation, ORIENTATION_COUNT);
|
||||
if (++settingsRead >= fileSettingsCount) break;
|
||||
readAndValidate(inputFile, frontButtonLayout, FRONT_BUTTON_LAYOUT_COUNT);
|
||||
if (++settingsRead >= fileSettingsCount) break;
|
||||
{
|
||||
uint8_t ignored;
|
||||
serialization::readPod(inputFile, ignored);
|
||||
} // legacy sideButtonLayout field
|
||||
if (++settingsRead >= fileSettingsCount) break;
|
||||
readAndValidate(inputFile, fontFamily, FONT_FAMILY_COUNT);
|
||||
if (++settingsRead >= fileSettingsCount) break;
|
||||
readAndValidate(inputFile, fontSize, FONT_SIZE_COUNT);
|
||||
if (++settingsRead >= fileSettingsCount) break;
|
||||
readAndValidate(inputFile, lineSpacing, LINE_COMPRESSION_COUNT);
|
||||
if (++settingsRead >= fileSettingsCount) break;
|
||||
readAndValidate(inputFile, paragraphAlignment, PARAGRAPH_ALIGNMENT_COUNT);
|
||||
if (++settingsRead >= fileSettingsCount) break;
|
||||
readAndValidate(inputFile, sleepTimeout, SLEEP_TIMEOUT_COUNT);
|
||||
if (++settingsRead >= fileSettingsCount) break;
|
||||
readAndValidate(inputFile, refreshFrequency, REFRESH_FREQUENCY_COUNT);
|
||||
if (++settingsRead >= fileSettingsCount) break;
|
||||
serialization::readPod(inputFile, screenMargin);
|
||||
if (++settingsRead >= fileSettingsCount) break;
|
||||
readAndValidate(inputFile, sleepScreenCoverMode, SLEEP_SCREEN_COVER_MODE_COUNT);
|
||||
if (++settingsRead >= fileSettingsCount) break;
|
||||
{
|
||||
std::string urlStr;
|
||||
serialization::readString(inputFile, urlStr);
|
||||
strncpy(opdsServerUrl, urlStr.c_str(), sizeof(opdsServerUrl) - 1);
|
||||
opdsServerUrl[sizeof(opdsServerUrl) - 1] = '\0';
|
||||
}
|
||||
if (++settingsRead >= fileSettingsCount) break;
|
||||
serialization::readPod(inputFile, textAntiAliasing);
|
||||
if (++settingsRead >= fileSettingsCount) break;
|
||||
readAndValidate(inputFile, hideBatteryPercentage, HIDE_BATTERY_PERCENTAGE_COUNT);
|
||||
if (++settingsRead >= fileSettingsCount) break;
|
||||
{
|
||||
uint8_t ignored;
|
||||
serialization::readPod(inputFile, ignored);
|
||||
} // was longPressChapterSkip
|
||||
if (++settingsRead >= fileSettingsCount) break;
|
||||
serialization::readPod(inputFile, hyphenationEnabled);
|
||||
if (++settingsRead >= fileSettingsCount) break;
|
||||
{
|
||||
std::string usernameStr;
|
||||
serialization::readString(inputFile, usernameStr);
|
||||
strncpy(opdsUsername, usernameStr.c_str(), sizeof(opdsUsername) - 1);
|
||||
opdsUsername[sizeof(opdsUsername) - 1] = '\0';
|
||||
}
|
||||
if (++settingsRead >= fileSettingsCount) break;
|
||||
{
|
||||
std::string passwordStr;
|
||||
serialization::readString(inputFile, passwordStr);
|
||||
strncpy(opdsPassword, passwordStr.c_str(), sizeof(opdsPassword) - 1);
|
||||
opdsPassword[sizeof(opdsPassword) - 1] = '\0';
|
||||
}
|
||||
if (++settingsRead >= fileSettingsCount) break;
|
||||
readAndValidate(inputFile, sleepScreenCoverFilter, SLEEP_SCREEN_COVER_FILTER_COUNT);
|
||||
if (++settingsRead >= fileSettingsCount) break;
|
||||
serialization::readPod(inputFile, uiTheme);
|
||||
if (++settingsRead >= fileSettingsCount) break;
|
||||
readAndValidate(inputFile, frontButtonBack, FRONT_BUTTON_HARDWARE_COUNT);
|
||||
if (++settingsRead >= fileSettingsCount) break;
|
||||
readAndValidate(inputFile, frontButtonConfirm, FRONT_BUTTON_HARDWARE_COUNT);
|
||||
if (++settingsRead >= fileSettingsCount) break;
|
||||
readAndValidate(inputFile, frontButtonLeft, FRONT_BUTTON_HARDWARE_COUNT);
|
||||
if (++settingsRead >= fileSettingsCount) break;
|
||||
readAndValidate(inputFile, frontButtonRight, FRONT_BUTTON_HARDWARE_COUNT);
|
||||
frontButtonMappingRead = true;
|
||||
if (++settingsRead >= fileSettingsCount) break;
|
||||
serialization::readPod(inputFile, fadingFix);
|
||||
if (++settingsRead >= fileSettingsCount) break;
|
||||
serialization::readPod(inputFile, embeddedStyle);
|
||||
if (++settingsRead >= fileSettingsCount) break;
|
||||
} while (false);
|
||||
|
||||
if (frontButtonMappingRead) {
|
||||
CrossPointSettings::validateFrontButtonMapping(*this);
|
||||
} else {
|
||||
applyLegacyFrontButtonLayout(*this);
|
||||
}
|
||||
|
||||
inputFile.close();
|
||||
LOG_DBG("CPS", "Settings loaded from binary file");
|
||||
return true;
|
||||
}
|
||||
|
||||
float CrossPointSettings::getReaderLineCompression() const {
|
||||
const int effectiveFontId = getReaderFontId();
|
||||
const int notosansId = getBuiltinReaderFontId(NOTOSANS, fontSize);
|
||||
|
||||
@@ -372,9 +372,6 @@ class CrossPointSettings {
|
||||
// global default.
|
||||
static int getBuiltinReaderFontId(uint8_t family, uint8_t size);
|
||||
|
||||
// If count_only is true, returns the number of settings items that would be written.
|
||||
uint8_t writeSettings(FsFile& file, bool count_only = false) const;
|
||||
|
||||
bool saveToFile() const;
|
||||
bool loadFromFile();
|
||||
void loadStartupFromNvs();
|
||||
@@ -382,10 +379,6 @@ class CrossPointSettings {
|
||||
|
||||
static void validateFrontButtonMapping(CrossPointSettings& settings);
|
||||
|
||||
private:
|
||||
bool loadFromBinaryFile();
|
||||
|
||||
public:
|
||||
float getReaderLineCompression() const;
|
||||
unsigned long getSleepTimeoutMs() const;
|
||||
int getRefreshFrequency() const;
|
||||
|
||||
@@ -3,13 +3,9 @@
|
||||
#include <HalStorage.h>
|
||||
#include <JsonSettingsIO.h>
|
||||
#include <Logging.h>
|
||||
#include <Serialization.h>
|
||||
|
||||
namespace {
|
||||
constexpr uint8_t STATE_FILE_VERSION = 4;
|
||||
constexpr char STATE_FILE_BIN[] = "/.crosspoint/state.bin";
|
||||
constexpr char STATE_FILE_JSON[] = "/.crosspoint/state.json";
|
||||
constexpr char STATE_FILE_BAK[] = "/.crosspoint/state.bin.bak";
|
||||
} // namespace
|
||||
|
||||
CrossPointState CrossPointState::instance;
|
||||
@@ -20,65 +16,11 @@ bool CrossPointState::saveToFile() const {
|
||||
}
|
||||
|
||||
bool CrossPointState::loadFromFile() {
|
||||
// Try JSON first
|
||||
if (Storage.exists(STATE_FILE_JSON)) {
|
||||
String json = Storage.readFile(STATE_FILE_JSON);
|
||||
if (!json.isEmpty()) {
|
||||
return JsonSettingsIO::loadState(*this, json.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// Fall back to binary migration
|
||||
if (Storage.exists(STATE_FILE_BIN)) {
|
||||
if (loadFromBinaryFile()) {
|
||||
if (saveToFile()) {
|
||||
Storage.rename(STATE_FILE_BIN, STATE_FILE_BAK);
|
||||
LOG_DBG("CPS", "Migrated state.bin to state.json");
|
||||
return true;
|
||||
} else {
|
||||
LOG_ERR("CPS", "Failed to save state during migration");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CrossPointState::loadFromBinaryFile() {
|
||||
FsFile inputFile;
|
||||
if (!Storage.openFileForRead("CPS", STATE_FILE_BIN, inputFile)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t version;
|
||||
serialization::readPod(inputFile, version);
|
||||
if (version > STATE_FILE_VERSION) {
|
||||
LOG_ERR("CPS", "Deserialization failed: Unknown version %u", version);
|
||||
inputFile.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
serialization::readString(inputFile, openEpubPath);
|
||||
if (version >= 2) {
|
||||
serialization::readPod(inputFile, lastSleepImage);
|
||||
} else {
|
||||
lastSleepImage = SIZE_MAX;
|
||||
}
|
||||
|
||||
if (version >= 3) {
|
||||
serialization::readPod(inputFile, readerActivityLoadCount);
|
||||
}
|
||||
|
||||
if (version >= 4) {
|
||||
serialization::readPod(inputFile, lastSleepFromReader);
|
||||
} else {
|
||||
lastSleepFromReader = false;
|
||||
}
|
||||
|
||||
koReaderSyncSession.clear();
|
||||
pendingBookmarkJump.clear();
|
||||
|
||||
inputFile.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -106,9 +106,6 @@ class CrossPointState {
|
||||
bool saveToFile() const;
|
||||
|
||||
bool loadFromFile();
|
||||
|
||||
private:
|
||||
bool loadFromBinaryFile();
|
||||
};
|
||||
|
||||
// Helper macro to access settings
|
||||
|
||||
@@ -5,16 +5,12 @@
|
||||
#include <HalStorage.h>
|
||||
#include <JsonSettingsIO.h>
|
||||
#include <Logging.h>
|
||||
#include <Serialization.h>
|
||||
#include <Xtc.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace {
|
||||
constexpr uint8_t RECENT_BOOKS_FILE_VERSION = 3;
|
||||
constexpr char RECENT_BOOKS_FILE_BIN[] = "/.crosspoint/recent.bin";
|
||||
constexpr char RECENT_BOOKS_FILE_JSON[] = "/.crosspoint/recent.json";
|
||||
constexpr char RECENT_BOOKS_FILE_BAK[] = "/.crosspoint/recent.bin.bak";
|
||||
constexpr int MAX_RECENT_BOOKS = 10;
|
||||
} // namespace
|
||||
|
||||
@@ -231,114 +227,12 @@ RecentBook RecentBooksStore::getDataFromBook(std::string path) const {
|
||||
}
|
||||
|
||||
bool RecentBooksStore::loadFromFile() {
|
||||
// Try JSON first
|
||||
if (Storage.exists(RECENT_BOOKS_FILE_JSON)) {
|
||||
String json = Storage.readFile(RECENT_BOOKS_FILE_JSON);
|
||||
if (!json.isEmpty()) {
|
||||
return JsonSettingsIO::loadRecentBooks(*this, json.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// Fall back to binary migration
|
||||
if (Storage.exists(RECENT_BOOKS_FILE_BIN)) {
|
||||
if (loadFromBinaryFile()) {
|
||||
saveToFile();
|
||||
Storage.rename(RECENT_BOOKS_FILE_BIN, RECENT_BOOKS_FILE_BAK);
|
||||
LOG_DBG("RBS", "Migrated recent.bin to recent.json");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool RecentBooksStore::loadFromBinaryFile() {
|
||||
FsFile inputFile;
|
||||
if (!Storage.openFileForRead("RBS", RECENT_BOOKS_FILE_BIN, inputFile)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t version;
|
||||
serialization::readPod(inputFile, version);
|
||||
if (version == 1 || version == 2) {
|
||||
// Old version, just read paths
|
||||
uint8_t count;
|
||||
serialization::readPod(inputFile, count);
|
||||
std::vector<RecentBook> tmpRecentBooks;
|
||||
tmpRecentBooks.reserve(count);
|
||||
for (uint8_t i = 0; i < count; i++) {
|
||||
std::string path;
|
||||
if (!serialization::readString(inputFile, path)) {
|
||||
LOG_ERR("RBS", "Corrupt recent.bin: string too long at entry %u", i);
|
||||
inputFile.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
// load book to get missing data
|
||||
RecentBook book = getDataFromBook(path);
|
||||
if (version == 2) {
|
||||
// v2 always stores title and author after path; consume them regardless
|
||||
// of whether live metadata was found, to keep the stream aligned.
|
||||
std::string storedTitle, storedAuthor;
|
||||
if (!serialization::readString(inputFile, storedTitle) || !serialization::readString(inputFile, storedAuthor)) {
|
||||
LOG_ERR("RBS", "Corrupt recent.bin: string too long at entry %u", i);
|
||||
inputFile.close();
|
||||
return false;
|
||||
}
|
||||
// Prefer live metadata; fall back to stored when live is unavailable.
|
||||
const std::string& title = !book.title.empty() ? book.title : storedTitle;
|
||||
const std::string& author = !book.title.empty() ? book.author : storedAuthor;
|
||||
if (!title.empty()) {
|
||||
tmpRecentBooks.push_back({path, title, author, "", ""});
|
||||
}
|
||||
} else {
|
||||
// v1: no stored title/author bytes
|
||||
if (!book.title.empty()) {
|
||||
tmpRecentBooks.push_back(book);
|
||||
}
|
||||
}
|
||||
}
|
||||
recentBooks = std::move(tmpRecentBooks);
|
||||
} else if (version == 3) {
|
||||
uint8_t count;
|
||||
serialization::readPod(inputFile, count);
|
||||
|
||||
std::vector<RecentBook> tmpRecentBooks;
|
||||
tmpRecentBooks.reserve(count);
|
||||
uint8_t omitted = 0;
|
||||
|
||||
for (uint8_t i = 0; i < count; i++) {
|
||||
std::string path, title, author, coverBmpPath;
|
||||
if (!serialization::readString(inputFile, path) || !serialization::readString(inputFile, title) ||
|
||||
!serialization::readString(inputFile, author) || !serialization::readString(inputFile, coverBmpPath)) {
|
||||
LOG_ERR("RBS", "Corrupt recent.bin: string too long at entry %u", i);
|
||||
inputFile.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Omit books with missing title (e.g. saved before metadata was available)
|
||||
if (title.empty()) {
|
||||
omitted++;
|
||||
continue;
|
||||
}
|
||||
|
||||
tmpRecentBooks.push_back({path, title, author, "", coverBmpPath});
|
||||
}
|
||||
recentBooks = std::move(tmpRecentBooks);
|
||||
|
||||
if (omitted > 0) {
|
||||
inputFile.close();
|
||||
saveToFile();
|
||||
LOG_DBG("RBS", "Omitted %u recent book(s) with missing title", omitted);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
LOG_ERR("RBS", "Deserialization failed: Unknown version %u", version);
|
||||
inputFile.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
inputFile.close();
|
||||
LOG_DBG("RBS", "Recent books loaded from binary file (%d entries)", static_cast<int>(recentBooks.size()));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -89,9 +89,6 @@ class RecentBooksStore {
|
||||
bool setReaderOverrides(const std::string& path, int8_t embeddedStyleOverride, int8_t imageRenderingOverride,
|
||||
int8_t fontFamilyOverride, const std::string& sdFontFamilyOverride, int8_t fontSizeOverride,
|
||||
bool bionicReadingOverride, int8_t paragraphAlignmentOverride);
|
||||
|
||||
private:
|
||||
bool loadFromBinaryFile();
|
||||
};
|
||||
|
||||
// Helper macro to access recent books store
|
||||
|
||||
@@ -4,29 +4,12 @@
|
||||
#include <JsonSettingsIO.h>
|
||||
#include <Logging.h>
|
||||
#include <ObfuscationUtils.h>
|
||||
#include <Serialization.h>
|
||||
|
||||
// Initialize the static instance
|
||||
WifiCredentialStore WifiCredentialStore::instance;
|
||||
|
||||
namespace {
|
||||
// File format version (for binary migration)
|
||||
constexpr uint8_t WIFI_FILE_VERSION = 2;
|
||||
|
||||
// File paths
|
||||
constexpr char WIFI_FILE_BIN[] = "/.crosspoint/wifi.bin";
|
||||
constexpr char WIFI_FILE_JSON[] = "/.crosspoint/wifi.json";
|
||||
constexpr char WIFI_FILE_BAK[] = "/.crosspoint/wifi.bin.bak";
|
||||
|
||||
// Legacy obfuscation key - "CrossPoint" in ASCII (only used for binary migration)
|
||||
constexpr uint8_t LEGACY_OBFUSCATION_KEY[] = {0x43, 0x72, 0x6F, 0x73, 0x73, 0x50, 0x6F, 0x69, 0x6E, 0x74};
|
||||
constexpr size_t LEGACY_KEY_LENGTH = sizeof(LEGACY_OBFUSCATION_KEY);
|
||||
|
||||
void legacyDeobfuscate(std::string& data) {
|
||||
for (size_t i = 0; i < data.size(); i++) {
|
||||
data[i] ^= LEGACY_OBFUSCATION_KEY[i % LEGACY_KEY_LENGTH];
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
bool WifiCredentialStore::saveToFile() const {
|
||||
@@ -35,7 +18,6 @@ bool WifiCredentialStore::saveToFile() const {
|
||||
}
|
||||
|
||||
bool WifiCredentialStore::loadFromFile() {
|
||||
// Try JSON first
|
||||
if (Storage.exists(WIFI_FILE_JSON)) {
|
||||
String json = Storage.readFile(WIFI_FILE_JSON);
|
||||
if (!json.isEmpty()) {
|
||||
@@ -48,61 +30,9 @@ bool WifiCredentialStore::loadFromFile() {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// Fall back to binary migration
|
||||
if (Storage.exists(WIFI_FILE_BIN)) {
|
||||
if (loadFromBinaryFile()) {
|
||||
if (saveToFile()) {
|
||||
Storage.rename(WIFI_FILE_BIN, WIFI_FILE_BAK);
|
||||
LOG_DBG("WCS", "Migrated wifi.bin to wifi.json");
|
||||
return true;
|
||||
} else {
|
||||
LOG_ERR("WCS", "Failed to save wifi during migration");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WifiCredentialStore::loadFromBinaryFile() {
|
||||
FsFile file;
|
||||
if (!Storage.openFileForRead("WCS", WIFI_FILE_BIN, file)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t version;
|
||||
serialization::readPod(file, version);
|
||||
if (version > WIFI_FILE_VERSION) {
|
||||
LOG_DBG("WCS", "Unknown file version: %u", version);
|
||||
file.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (version >= 2) {
|
||||
serialization::readString(file, lastConnectedSsid);
|
||||
} else {
|
||||
lastConnectedSsid.clear();
|
||||
}
|
||||
|
||||
uint8_t count;
|
||||
serialization::readPod(file, count);
|
||||
|
||||
credentials.clear();
|
||||
for (uint8_t i = 0; i < count && i < MAX_NETWORKS; i++) {
|
||||
WifiCredential cred;
|
||||
serialization::readString(file, cred.ssid);
|
||||
serialization::readString(file, cred.password);
|
||||
legacyDeobfuscate(cred.password);
|
||||
credentials.push_back(cred);
|
||||
}
|
||||
|
||||
file.close();
|
||||
// LOG_DBG("WCS", "Loaded %zu WiFi credentials from binary file", credentials.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WifiCredentialStore::addCredential(const std::string& ssid, const std::string& password) {
|
||||
// Check if this SSID already exists and update it
|
||||
const auto cred = find_if(credentials.begin(), credentials.end(),
|
||||
|
||||
@@ -31,8 +31,6 @@ class WifiCredentialStore {
|
||||
// Private constructor for singleton
|
||||
WifiCredentialStore() = default;
|
||||
|
||||
bool loadFromBinaryFile();
|
||||
|
||||
friend bool JsonSettingsIO::saveWifi(const WifiCredentialStore&, const char*);
|
||||
friend bool JsonSettingsIO::loadWifi(WifiCredentialStore&, const char*, bool*);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user