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.
|
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
|
progress.bin
|
||||||
cover.bmp
|
cover.bmp
|
||||||
sections/*.bin
|
sections/*.bin
|
||||||
settings.bin
|
settings.json
|
||||||
state.bin
|
state.json
|
||||||
```
|
```
|
||||||
|
|
||||||
For binary cache formats, see `docs/file-formats.md`.
|
For binary cache formats, see `docs/file-formats.md`.
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
#include <Logging.h>
|
#include <Logging.h>
|
||||||
#include <MD5Builder.h>
|
#include <MD5Builder.h>
|
||||||
#include <ObfuscationUtils.h>
|
#include <ObfuscationUtils.h>
|
||||||
#include <Serialization.h>
|
|
||||||
|
|
||||||
#include "../../src/JsonSettingsIO.h"
|
#include "../../src/JsonSettingsIO.h"
|
||||||
|
|
||||||
@@ -12,26 +11,10 @@
|
|||||||
KOReaderCredentialStore KOReaderCredentialStore::instance;
|
KOReaderCredentialStore KOReaderCredentialStore::instance;
|
||||||
|
|
||||||
namespace {
|
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_JSON[] = "/.crosspoint/koreader.json";
|
||||||
constexpr char KOREADER_FILE_BAK[] = "/.crosspoint/koreader.bin.bak";
|
|
||||||
|
|
||||||
// Default sync server URL
|
// Default sync server URL
|
||||||
constexpr char DEFAULT_SERVER_URL[] = "https://sync.koreader.rocks:443";
|
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
|
} // namespace
|
||||||
|
|
||||||
bool KOReaderCredentialStore::saveToFile() const {
|
bool KOReaderCredentialStore::saveToFile() const {
|
||||||
@@ -40,7 +23,6 @@ bool KOReaderCredentialStore::saveToFile() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool KOReaderCredentialStore::loadFromFile() {
|
bool KOReaderCredentialStore::loadFromFile() {
|
||||||
// Try JSON first
|
|
||||||
if (Storage.exists(KOREADER_FILE_JSON)) {
|
if (Storage.exists(KOREADER_FILE_JSON)) {
|
||||||
String json = Storage.readFile(KOREADER_FILE_JSON);
|
String json = Storage.readFile(KOREADER_FILE_JSON);
|
||||||
if (!json.isEmpty()) {
|
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");
|
LOG_DBG("KRS", "No credentials file found");
|
||||||
return false;
|
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) {
|
void KOReaderCredentialStore::setCredentials(const std::string& user, const std::string& pass) {
|
||||||
username = user;
|
username = user;
|
||||||
password = pass;
|
password = pass;
|
||||||
|
|||||||
@@ -32,8 +32,6 @@ class KOReaderCredentialStore {
|
|||||||
// Private constructor for singleton
|
// Private constructor for singleton
|
||||||
KOReaderCredentialStore() = default;
|
KOReaderCredentialStore() = default;
|
||||||
|
|
||||||
bool loadFromBinaryFile();
|
|
||||||
|
|
||||||
friend bool JsonSettingsIO::saveKOReader(const KOReaderCredentialStore&, const char*);
|
friend bool JsonSettingsIO::saveKOReader(const KOReaderCredentialStore&, const char*);
|
||||||
friend bool JsonSettingsIO::loadKOReader(KOReaderCredentialStore&, const char*, bool*);
|
friend bool JsonSettingsIO::loadKOReader(KOReaderCredentialStore&, const char*, bool*);
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
#include <HalStorage.h>
|
#include <HalStorage.h>
|
||||||
#include <JsonSettingsIO.h>
|
#include <JsonSettingsIO.h>
|
||||||
#include <Logging.h>
|
#include <Logging.h>
|
||||||
#include <Serialization.h>
|
|
||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -31,50 +30,8 @@ static_assert(SMALL_FONT_ID != 0, "Font ID collision with sentinel");
|
|||||||
// Initialize the static instance
|
// Initialize the static instance
|
||||||
CrossPointSettings CrossPointSettings::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 {
|
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_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) {
|
void enforceFixedShortActions(CrossPointSettings& settings) {
|
||||||
settings.btnShortBack = static_cast<uint8_t>(CrossPointSettings::BUTTON_ACTION::BTN_DEFAULT);
|
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;
|
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 {
|
float CrossPointSettings::getReaderLineCompression() const {
|
||||||
const int effectiveFontId = getReaderFontId();
|
const int effectiveFontId = getReaderFontId();
|
||||||
const int notosansId = getBuiltinReaderFontId(NOTOSANS, fontSize);
|
const int notosansId = getBuiltinReaderFontId(NOTOSANS, fontSize);
|
||||||
|
|||||||
@@ -372,9 +372,6 @@ class CrossPointSettings {
|
|||||||
// global default.
|
// global default.
|
||||||
static int getBuiltinReaderFontId(uint8_t family, uint8_t size);
|
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 saveToFile() const;
|
||||||
bool loadFromFile();
|
bool loadFromFile();
|
||||||
void loadStartupFromNvs();
|
void loadStartupFromNvs();
|
||||||
@@ -382,10 +379,6 @@ class CrossPointSettings {
|
|||||||
|
|
||||||
static void validateFrontButtonMapping(CrossPointSettings& settings);
|
static void validateFrontButtonMapping(CrossPointSettings& settings);
|
||||||
|
|
||||||
private:
|
|
||||||
bool loadFromBinaryFile();
|
|
||||||
|
|
||||||
public:
|
|
||||||
float getReaderLineCompression() const;
|
float getReaderLineCompression() const;
|
||||||
unsigned long getSleepTimeoutMs() const;
|
unsigned long getSleepTimeoutMs() const;
|
||||||
int getRefreshFrequency() const;
|
int getRefreshFrequency() const;
|
||||||
|
|||||||
@@ -3,13 +3,9 @@
|
|||||||
#include <HalStorage.h>
|
#include <HalStorage.h>
|
||||||
#include <JsonSettingsIO.h>
|
#include <JsonSettingsIO.h>
|
||||||
#include <Logging.h>
|
#include <Logging.h>
|
||||||
#include <Serialization.h>
|
|
||||||
|
|
||||||
namespace {
|
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_JSON[] = "/.crosspoint/state.json";
|
||||||
constexpr char STATE_FILE_BAK[] = "/.crosspoint/state.bin.bak";
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
CrossPointState CrossPointState::instance;
|
CrossPointState CrossPointState::instance;
|
||||||
@@ -20,65 +16,11 @@ bool CrossPointState::saveToFile() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool CrossPointState::loadFromFile() {
|
bool CrossPointState::loadFromFile() {
|
||||||
// Try JSON first
|
|
||||||
if (Storage.exists(STATE_FILE_JSON)) {
|
if (Storage.exists(STATE_FILE_JSON)) {
|
||||||
String json = Storage.readFile(STATE_FILE_JSON);
|
String json = Storage.readFile(STATE_FILE_JSON);
|
||||||
if (!json.isEmpty()) {
|
if (!json.isEmpty()) {
|
||||||
return JsonSettingsIO::loadState(*this, json.c_str());
|
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;
|
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 saveToFile() const;
|
||||||
|
|
||||||
bool loadFromFile();
|
bool loadFromFile();
|
||||||
|
|
||||||
private:
|
|
||||||
bool loadFromBinaryFile();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Helper macro to access settings
|
// Helper macro to access settings
|
||||||
|
|||||||
@@ -5,16 +5,12 @@
|
|||||||
#include <HalStorage.h>
|
#include <HalStorage.h>
|
||||||
#include <JsonSettingsIO.h>
|
#include <JsonSettingsIO.h>
|
||||||
#include <Logging.h>
|
#include <Logging.h>
|
||||||
#include <Serialization.h>
|
|
||||||
#include <Xtc.h>
|
#include <Xtc.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
namespace {
|
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_JSON[] = "/.crosspoint/recent.json";
|
||||||
constexpr char RECENT_BOOKS_FILE_BAK[] = "/.crosspoint/recent.bin.bak";
|
|
||||||
constexpr int MAX_RECENT_BOOKS = 10;
|
constexpr int MAX_RECENT_BOOKS = 10;
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
@@ -231,114 +227,12 @@ RecentBook RecentBooksStore::getDataFromBook(std::string path) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool RecentBooksStore::loadFromFile() {
|
bool RecentBooksStore::loadFromFile() {
|
||||||
// Try JSON first
|
|
||||||
if (Storage.exists(RECENT_BOOKS_FILE_JSON)) {
|
if (Storage.exists(RECENT_BOOKS_FILE_JSON)) {
|
||||||
String json = Storage.readFile(RECENT_BOOKS_FILE_JSON);
|
String json = Storage.readFile(RECENT_BOOKS_FILE_JSON);
|
||||||
if (!json.isEmpty()) {
|
if (!json.isEmpty()) {
|
||||||
return JsonSettingsIO::loadRecentBooks(*this, json.c_str());
|
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;
|
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,
|
bool setReaderOverrides(const std::string& path, int8_t embeddedStyleOverride, int8_t imageRenderingOverride,
|
||||||
int8_t fontFamilyOverride, const std::string& sdFontFamilyOverride, int8_t fontSizeOverride,
|
int8_t fontFamilyOverride, const std::string& sdFontFamilyOverride, int8_t fontSizeOverride,
|
||||||
bool bionicReadingOverride, int8_t paragraphAlignmentOverride);
|
bool bionicReadingOverride, int8_t paragraphAlignmentOverride);
|
||||||
|
|
||||||
private:
|
|
||||||
bool loadFromBinaryFile();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Helper macro to access recent books store
|
// Helper macro to access recent books store
|
||||||
|
|||||||
@@ -4,29 +4,12 @@
|
|||||||
#include <JsonSettingsIO.h>
|
#include <JsonSettingsIO.h>
|
||||||
#include <Logging.h>
|
#include <Logging.h>
|
||||||
#include <ObfuscationUtils.h>
|
#include <ObfuscationUtils.h>
|
||||||
#include <Serialization.h>
|
|
||||||
|
|
||||||
// Initialize the static instance
|
// Initialize the static instance
|
||||||
WifiCredentialStore WifiCredentialStore::instance;
|
WifiCredentialStore WifiCredentialStore::instance;
|
||||||
|
|
||||||
namespace {
|
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_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
|
} // namespace
|
||||||
|
|
||||||
bool WifiCredentialStore::saveToFile() const {
|
bool WifiCredentialStore::saveToFile() const {
|
||||||
@@ -35,7 +18,6 @@ bool WifiCredentialStore::saveToFile() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool WifiCredentialStore::loadFromFile() {
|
bool WifiCredentialStore::loadFromFile() {
|
||||||
// Try JSON first
|
|
||||||
if (Storage.exists(WIFI_FILE_JSON)) {
|
if (Storage.exists(WIFI_FILE_JSON)) {
|
||||||
String json = Storage.readFile(WIFI_FILE_JSON);
|
String json = Storage.readFile(WIFI_FILE_JSON);
|
||||||
if (!json.isEmpty()) {
|
if (!json.isEmpty()) {
|
||||||
@@ -48,61 +30,9 @@ bool WifiCredentialStore::loadFromFile() {
|
|||||||
return result;
|
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;
|
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) {
|
bool WifiCredentialStore::addCredential(const std::string& ssid, const std::string& password) {
|
||||||
// Check if this SSID already exists and update it
|
// Check if this SSID already exists and update it
|
||||||
const auto cred = find_if(credentials.begin(), credentials.end(),
|
const auto cred = find_if(credentials.begin(), credentials.end(),
|
||||||
|
|||||||
@@ -31,8 +31,6 @@ class WifiCredentialStore {
|
|||||||
// Private constructor for singleton
|
// Private constructor for singleton
|
||||||
WifiCredentialStore() = default;
|
WifiCredentialStore() = default;
|
||||||
|
|
||||||
bool loadFromBinaryFile();
|
|
||||||
|
|
||||||
friend bool JsonSettingsIO::saveWifi(const WifiCredentialStore&, const char*);
|
friend bool JsonSettingsIO::saveWifi(const WifiCredentialStore&, const char*);
|
||||||
friend bool JsonSettingsIO::loadWifi(WifiCredentialStore&, const char*, bool*);
|
friend bool JsonSettingsIO::loadWifi(WifiCredentialStore&, const char*, bool*);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user