feat: Migrate binary settings to json#920
feat: Migrate binary settings to json#920daveallie merged 14 commits intocrosspoint-reader:masterfrom
Conversation
src/JsonSettingsIO.cpp
Outdated
| // ---- WifiCredentialStore ---- | ||
|
|
||
| namespace { | ||
| constexpr uint8_t WIFI_OBFUSCATION_KEY[] = {0x43, 0x72, 0x6F, 0x73, 0x73, 0x50, 0x6F, 0x69, 0x6E, 0x74}; |
There was a problem hiding this comment.
Why no generate a random one per user, and persist it in the SD card?
There was a problem hiding this comment.
In the end that would not make a lot difference, would it? If you have the SD-card, you have stuff...
There was a problem hiding this comment.
Indeed ... I was thinking on when some settings are shared in a forum, for example. In that case, then maybe the obfuscation can be dropped?
There was a problem hiding this comment.
I am just checking whether the unique MAC-address of the chip would not work... Then you would need device + sd card, sd card alone (or publishing) would not provide an (immediate) breach
There was a problem hiding this comment.
Not my call to make - removal of obfuscation is easy, but someone needs to say what's more important - security or convenience.
(Although I could check whether a "password_clear" exists, read it, obfuscate and write back, delete it)
There was a problem hiding this comment.
A more simple solution is to create a file wifi.txt that has first line as wifi name and second line as wifi password:
MyHomeWifi789
this_1s_the_passw0rd
There was a problem hiding this comment.
The objective is not to modernize wifi or koreader settings save / restore, just for sake of easier external readibility. I want to step away from that binary fixed format setting file to ease the addition of new settings for future extensions / personalisation. I faced the challenge myself a couple of times during the few days I started contributing to this repo. Any new feature I wanted to be user-selectable faced the same challenge - when would the PR be merged aka in which position do I need to store / will I find my setting. Now this is no longer relavant: regardless where I define, when I define a new setting - it will just end up in the json file.
There was a problem hiding this comment.
And btw: I created my wifi.json file from scratch:
{"lastConnectedSsid":"PYSY","credentials":[{"ssid":"PYSY","password":"my_very_secret_password"}]
and after the next connection, ie read of the setting it got converted to:
{"lastConnectedSsid":"PYSY","credentials":[{"ssid":"PYSY","password_obf":"qSb/nO9XvDmR1tko"}]
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds JsonSettingsIO JSON (de)serialization for multiple stores, implements hardware-key XOR obfuscation utilities, and introduces per-store binary→JSON migration paths (read legacy .bin, write .json, rename .bin→.bak). Also adds HalStorage::rename and per-store loadFromBinaryFile helpers for migration. Changes
Sequence Diagram(s)mermaid Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes 🚥 Pre-merge checks | ✅ 2✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
lib/KOReaderSync/KOReaderCredentialStore.h (1)
17-21:⚠️ Potential issue | 🟡 MinorOutdated documentation comment.
The comment mentions
koreader.binbut the implementation now useskoreader.jsonas the primary storage format. Consider updating the comment to reflect the JSON-based storage.📝 Proposed fix
/** * Singleton class for storing KOReader sync credentials on the SD card. - * Credentials are stored in /sd/.crosspoint/koreader.bin with basic - * XOR obfuscation to prevent casual reading (not cryptographically secure). + * Credentials are stored in /sd/.crosspoint/koreader.json with password + * obfuscation to prevent casual reading (not cryptographically secure). */src/WifiCredentialStore.h (1)
16-20:⚠️ Potential issue | 🟡 MinorOutdated documentation comment.
Similar to
KOReaderCredentialStore.h, the comment mentionswifi.binbut the implementation now useswifi.jsonas the primary storage format.📝 Proposed fix
/** * Singleton class for storing WiFi credentials on the SD card. - * Credentials are stored in /sd/.crosspoint/wifi.bin with basic - * XOR obfuscation to prevent casual reading (not cryptographically secure). + * Credentials are stored in /sd/.crosspoint/wifi.json with password + * obfuscation to prevent casual reading (not cryptographically secure). */
🤖 Fix all issues with AI agents
In `@lib/KOReaderSync/KOReaderCredentialStore.cpp`:
- Around line 51-59: The migration currently renames KOREADER_FILE_BIN to
KOREADER_FILE_BAK even if saveToFile() fails, risking data loss; change the flow
in the block that checks Storage.exists(KOREADER_FILE_BIN) so that after
loadFromBinaryFile() returns true you call saveToFile() and only if saveToFile()
succeeds then call Storage.rename(KOREADER_FILE_BIN, KOREADER_FILE_BAK) and
return true; if saveToFile() fails, log an error (use LOG_DBG/LOG_ERR consistent
with file) and do not rename the binary or return success so the original binary
remains intact.
In `@lib/Serialization/ObfuscationUtils.h`:
- Around line 52-63: In deobfuscateFromBase64, check the return values of both
mbedtls_base64_decode calls (the size-query call and the actual decode call) and
handle non-zero errors instead of proceeding to use decodedLen; on error, abort
decoding (e.g., log/return empty string or propagate an error) and avoid
resizing/using result or calling xorTransform with invalid data. Specifically,
in deobfuscateFromBase64 capture the int ret from mbedtls_base64_decode for the
initial call that populates decodedLen and for the second call that writes into
result, validate decodedLen before allocating result, and only call
xorTransform(result) when the decode succeeded.
- Around line 17-25: getHwKey() performs non-thread-safe lazy initialization of
the static key[] and initialized flag which can race if called concurrently;
replace the manual bool guard with a thread-safe one-time init (e.g. add a
static std::once_flag and use std::call_once to invoke
esp_efuse_mac_get_default(key)) or convert key into a function-local static
initialized via a single-expression initializer so C++11 guarantees thread-safe
init; update references to getHwKey, key, and the initialization call
esp_efuse_mac_get_default accordingly.
In `@src/CrossPointState.cpp`:
- Around line 31-39: The migration code currently renames STATE_FILE_BIN to
STATE_FILE_BAK regardless of saveToFile() outcome, risking data loss; change the
logic in the block that checks Storage.exists(STATE_FILE_BIN) so that after
loadFromBinaryFile() returns true you call saveToFile() and verify its return
value (or success) before calling Storage.rename(STATE_FILE_BIN, STATE_FILE_BAK)
and logging the migration; if saveToFile() fails, log an error (using
LOG_ERR/LOG_DBG as appropriate), do not rename the original binary, and return
false (or propagate failure) so the original data remains intact.
In `@src/JsonSettingsIO.cpp`:
- Around line 70-72: The OPDS password is being written as plaintext
(doc["opdsPassword"] = s.opdsPassword) while other passwords use obfuscation;
update saveSettings to store the OPDS password using
obfuscation::obfuscateToBase64(s.opdsPassword) and update loadSettings to
reverse this using the corresponding deobfuscation helper (e.g.,
obfuscation::deobfuscateFromBase64 or equivalent) when reading
doc["opdsPassword"]; also apply the same change for the other occurrence noted
(lines ~142-144) and handle empty/missing values safely so you don't attempt to
deobfuscate a null/empty string.
🧹 Nitpick comments (4)
lib/KOReaderSync/KOReaderCredentialStore.cpp (1)
9-9: Consider using a non-relative include path.The relative path
../../src/JsonSettingsIO.his fragile and could break if the directory structure changes. Consider adding thesrcdirectory to the include path in the build configuration, allowing#include <JsonSettingsIO.h>or#include "JsonSettingsIO.h".src/RecentBooksStore.cpp (1)
90-110: Migration may silently fail to persist if saveToFile returns false.After successfully loading from the binary file, if
saveToFile()fails on line 102, the migration isn't logged and the binary file isn't renamed. The function still returnstruewith data in memory, but the next startup will retry migration. This is actually reasonable resilience, but consider logging a warning when the JSON save fails during migration.💡 Optional: Add warning log on migration save failure
// 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"); + if (saveToFile()) { + Storage.rename(RECENT_BOOKS_FILE_BIN, RECENT_BOOKS_FILE_BAK); + LOG_DBG("RBS", "Migrated recent.bin to recent.json"); + } else { + LOG_WARN("RBS", "Loaded from binary but failed to save JSON"); + } return true; } }src/JsonSettingsIO.cpp (2)
252-253: Hardcoded limit of 10 instead of using a constant.The limit check uses a hardcoded
10whileRecentBooksStore.cppdefinesMAX_RECENT_BOOKS = 10. Consider using the constant for maintainability, or expose it via the store's interface.💡 Optional: Use constant instead of magic number
The simplest fix would be to expose
MAX_RECENT_BOOKSfromRecentBooksStoreor document why10is used here. IfgetCount()already returns a value capped at the maximum, this check is redundant anyway.
175-176: Consider validating matchMethod range to handle potential invalid JSON values.The
matchMethodvalue is loaded from JSON and cast directly toDocumentMatchMethodwithout range validation. The enum only defines two valid values:FILENAME(0) andBINARY(1). If the JSON contains an invalid value, this could result in an out-of-range enum value causing unexpected behavior. The default fallback (| (uint8_t)0) only handles missing fields, not invalid ones.💡 Optional: Add range check for matchMethod
uint8_t method = doc["matchMethod"] | (uint8_t)0; + if (method > static_cast<uint8_t>(DocumentMatchMethod::BINARY)) { + method = static_cast<uint8_t>(DocumentMatchMethod::FILENAME); + } store.matchMethod = static_cast<DocumentMatchMethod>(method);
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (15)
lib/KOReaderSync/KOReaderCredentialStore.cpplib/KOReaderSync/KOReaderCredentialStore.hlib/Serialization/ObfuscationUtils.hlib/hal/HalStorage.cpplib/hal/HalStorage.hsrc/CrossPointSettings.cppsrc/CrossPointSettings.hsrc/CrossPointState.cppsrc/CrossPointState.hsrc/JsonSettingsIO.cppsrc/JsonSettingsIO.hsrc/RecentBooksStore.cppsrc/RecentBooksStore.hsrc/WifiCredentialStore.cppsrc/WifiCredentialStore.h
🧰 Additional context used
🧬 Code graph analysis (13)
lib/hal/HalStorage.h (1)
lib/hal/HalStorage.cpp (2)
rename(39-39)rename(39-39)
src/CrossPointState.h (1)
src/CrossPointState.cpp (2)
loadFromBinaryFile(44-77)loadFromBinaryFile(44-44)
src/WifiCredentialStore.h (2)
src/JsonSettingsIO.h (1)
JsonSettingsIO(9-31)src/JsonSettingsIO.cpp (4)
saveWifi(184-198)saveWifi(184-184)loadWifi(200-222)loadWifi(200-200)
src/RecentBooksStore.cpp (1)
src/JsonSettingsIO.cpp (4)
saveRecentBooks(226-240)saveRecentBooks(226-226)loadRecentBooks(242-264)loadRecentBooks(242-242)
lib/KOReaderSync/KOReaderCredentialStore.cpp (1)
src/JsonSettingsIO.cpp (4)
saveKOReader(152-162)saveKOReader(152-152)loadKOReader(164-180)loadKOReader(164-164)
src/RecentBooksStore.h (3)
src/JsonSettingsIO.h (1)
JsonSettingsIO(9-31)src/JsonSettingsIO.cpp (2)
loadRecentBooks(242-264)loadRecentBooks(242-242)src/RecentBooksStore.cpp (2)
loadFromBinaryFile(112-165)loadFromBinaryFile(112-112)
lib/KOReaderSync/KOReaderCredentialStore.h (3)
src/JsonSettingsIO.h (1)
JsonSettingsIO(9-31)src/JsonSettingsIO.cpp (4)
saveKOReader(152-162)saveKOReader(152-152)loadKOReader(164-180)loadKOReader(164-164)lib/KOReaderSync/KOReaderCredentialStore.cpp (2)
loadFromBinaryFile(65-109)loadFromBinaryFile(65-65)
src/CrossPointSettings.cpp (4)
src/WifiCredentialStore.cpp (6)
saveToFile(32-35)saveToFile(32-32)loadFromFile(37-57)loadFromFile(37-37)loadFromBinaryFile(59-94)loadFromBinaryFile(59-59)src/CrossPointState.cpp (6)
saveToFile(17-20)saveToFile(17-17)loadFromFile(22-42)loadFromFile(22-22)loadFromBinaryFile(44-77)loadFromBinaryFile(44-44)src/RecentBooksStore.cpp (6)
saveToFile(57-60)saveToFile(57-57)loadFromFile(90-110)loadFromFile(90-90)loadFromBinaryFile(112-165)loadFromBinaryFile(112-112)src/JsonSettingsIO.cpp (4)
saveSettings(47-83)saveSettings(47-47)loadSettings(85-148)loadSettings(85-85)
src/JsonSettingsIO.h (3)
lib/KOReaderSync/KOReaderCredentialStore.h (2)
KOReaderCredentialStore(22-74)JsonSettingsIO(12-15)src/WifiCredentialStore.h (1)
JsonSettingsIO(11-14)src/RecentBooksStore.h (1)
JsonSettingsIO(15-17)
src/JsonSettingsIO.cpp (2)
lib/Serialization/ObfuscationUtils.h (2)
string(52-63)obfuscateToBase64(44-49)src/CrossPointSettings.cpp (2)
validateFrontButtonMapping(62-76)validateFrontButtonMapping(62-62)
src/CrossPointState.cpp (1)
src/JsonSettingsIO.cpp (4)
saveState(18-28)saveState(18-18)loadState(30-43)loadState(30-30)
src/WifiCredentialStore.cpp (2)
lib/KOReaderSync/KOReaderCredentialStore.cpp (4)
loadFromBinaryFile(65-109)loadFromBinaryFile(65-65)legacyDeobfuscate(30-34)legacyDeobfuscate(30-30)src/JsonSettingsIO.cpp (4)
saveWifi(184-198)saveWifi(184-184)loadWifi(200-222)loadWifi(200-200)
src/CrossPointSettings.h (1)
src/CrossPointSettings.cpp (4)
validateFrontButtonMapping(62-76)validateFrontButtonMapping(62-62)loadFromBinaryFile(105-212)loadFromBinaryFile(105-105)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: build
- GitHub Check: cppcheck
🔇 Additional comments (27)
lib/hal/HalStorage.cpp (1)
39-39: LGTM — clean delegation for rename.
Matches the existing storage wrapper pattern and keeps behavior centralized.lib/hal/HalStorage.h (1)
31-31: LGTM — header update matches implementation.
The new API is consistent with the storage wrapper surface.lib/Serialization/ObfuscationUtils.h (1)
1-88: LGTM overall - good device-specific obfuscation approach.The use of the ESP32 MAC address as a device-specific key improves upon a fixed key, making data non-portable (as intended for credential protection). The self-test function and clear documentation about XOR not being cryptographically secure are appreciated.
src/CrossPointState.h (1)
22-25: LGTM!Clean addition of the private
loadFromBinaryFile()helper method to support the binary-to-JSON migration path.src/CrossPointSettings.h (1)
194-199: LGTM - consider consolidating access specifier sections.The functionality is correct. The alternating
public/private/publicsections (lines 194-199) work but could be consolidated for readability by grouping all private members together. This is a minor organizational suggestion.src/RecentBooksStore.h (2)
14-17: LGTM!The forward declarations and friend access pattern is consistent with other credential stores in this PR, enabling
JsonSettingsIO::loadRecentBooksto directly modify the privaterecentBooksvector.Also applies to: 25-25
50-53: LGTM!The private
loadFromBinaryFile()declaration follows the established migration pattern.src/CrossPointState.cpp (1)
44-77: LGTM!The
loadFromBinaryFile()implementation correctly handles versioned binary deserialization with proper file closure in all code paths.lib/KOReaderSync/KOReaderCredentialStore.h (1)
11-15: LGTM!The forward declarations, friend access pattern, and private
loadFromBinaryFile()helper are consistent with other credential stores in this PR.Also applies to: 33-36
src/WifiCredentialStore.h (1)
10-14: LGTM!The forward declarations and friend access pattern are consistent with other stores in this PR.
Also applies to: 32-35
lib/KOReaderSync/KOReaderCredentialStore.cpp (2)
26-34: LGTM - legacy deobfuscation preserved correctly.The legacy "KOReader" XOR key is correctly preserved for migrating existing binary credentials to the new JSON format with hardware-key-based obfuscation.
65-109: LGTM!The
loadFromBinaryFile()implementation correctly handles the legacy binary format with proper field-by-field reading and legacy deobfuscation of the password.src/RecentBooksStore.cpp (3)
16-18: LGTM!The file path constants follow a consistent naming pattern and support the JSON-first migration strategy with backup retention.
57-60: LGTM!The save implementation correctly ensures the directory exists before delegating to the JSON serializer.
112-164: LGTM!The binary loading logic correctly handles multiple file versions with proper error handling and file cleanup. The v1/v2 migration path that enriches metadata from the actual book files is a nice touch for data recovery.
src/WifiCredentialStore.cpp (3)
21-29: LGTM!The legacy obfuscation handling is correctly scoped to the anonymous namespace and only used during binary migration. Keeping this separate from the new device-specific obfuscation in
ObfuscationUtils.his the right approach for backward compatibility.
37-57: LGTM!The JSON-first loading with binary migration fallback follows the established pattern consistently.
59-94: LGTM!The binary loading correctly handles version compatibility, applies legacy deobfuscation during migration, and has proper bounds checking on credential count.
src/JsonSettingsIO.cpp (4)
1-14: LGTM!The includes are well-organized and bring in the necessary dependencies for JSON handling, storage, logging, and obfuscation utilities.
18-43: LGTM!State save/load functions correctly serialize all fields with appropriate defaults on load.
93-94: LGTM!The
clamplambda provides a clean validation pattern for bounded enum values with sensible fallback to defaults.
184-222: LGTM!WiFi credential save/load correctly handles the array of credentials with proper obfuscation and respects the
MAX_NETWORKSlimit.src/CrossPointSettings.cpp (4)
26-28: LGTM!File path constants follow the established naming pattern for the JSON migration.
62-76: LGTM!The validation logic correctly detects duplicate button mappings and resets to safe defaults. The nested loop approach is appropriate for the small fixed-size array.
78-103: LGTM!The save and load functions follow the established JSON-first migration pattern consistently with other stores.
105-212: LGTM!The binary loading logic correctly handles the extensible settings format with partial reads, properly tracks which fields were read, and applies appropriate validation or legacy conversion for button mappings.
src/JsonSettingsIO.h (1)
1-31: LGTM!The header provides a clean, minimal interface with forward declarations to avoid circular dependencies. The namespace organization and consistent function naming (save/load pairs) make the API intuitive.
|
Personally I think it's not a good idea to use JSON. I'm pretty sure that the JSON parsing code will add quite a lot of overhead for both flash and RAM usage, especially when used with templated types. This PR seems to take >10KB flash already: Instead, I think we can implement a more future-proof binary format (can be shared for caching too). |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@lib/Serialization/ObfuscationUtils.cpp`:
- Around line 51-55: The xorTransform function can divide by zero when keyLen is
0; add a defensive guard at the start of xorTransform (the function taking
std::string& data, const uint8_t* key, size_t keyLen) to handle keyLen == 0 —
for example return early or throw std::invalid_argument if keyLen is zero and/or
log an error — so the subsequent data[i] ^= key[i % keyLen] never performs a
modulo by zero.
🧹 Nitpick comments (3)
lib/KOReaderSync/KOReaderCredentialStore.cpp (2)
9-9: Prefer angle brackets for project includes.The relative path
"../../src/JsonSettingsIO.h"is fragile and depends on the directory structure. Consider adding thesrc/directory to the include paths and using angle brackets instead.-#include "../../src/JsonSettingsIO.h" +#include <JsonSettingsIO.h>
102-108: Consider validating thematchMethodvalue before casting.The
methodvalue read from the file is cast directly toDocumentMatchMethodwithout validation. A corrupted or malicious file could contain an invalid enum value.if (file.available()) { uint8_t method; serialization::readPod(file, method); - matchMethod = static_cast<DocumentMatchMethod>(method); + matchMethod = (method <= static_cast<uint8_t>(DocumentMatchMethod::BINARY)) + ? static_cast<DocumentMatchMethod>(method) + : DocumentMatchMethod::FILENAME; } else { matchMethod = DocumentMatchMethod::FILENAME; }lib/Serialization/ObfuscationUtils.cpp (1)
89-109: Consider adding error-path coverage to selfTest.The self-test covers round-trip correctness but doesn't verify that
deobfuscateFromBase64correctly handles invalid input and sets theokflag tofalse.🧪 Optional: Add error handling test
if (allPassed) { JSON_LOG_DBG("OBF", "Obfuscation self-test PASSED"); } + + // Verify error handling for invalid base64 + bool ok = true; + std::string result = deobfuscateFromBase64("!!!invalid!!!", &ok); + if (ok || !result.empty()) { + JSON_LOG_ERR("OBF", "FAIL: invalid base64 should set ok=false"); + } }
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
lib/KOReaderSync/KOReaderCredentialStore.cpplib/Serialization/ObfuscationUtils.cpplib/Serialization/ObfuscationUtils.hsrc/CrossPointState.cppsrc/JsonSettingsIO.cppsrc/WifiCredentialStore.cpp
🚧 Files skipped from review as they are similar to previous changes (1)
- src/JsonSettingsIO.cpp
🧰 Additional context used
🧬 Code graph analysis (3)
src/CrossPointState.cpp (4)
src/WifiCredentialStore.cpp (6)
saveToFile(32-35)saveToFile(32-32)loadFromFile(37-61)loadFromFile(37-37)loadFromBinaryFile(63-98)loadFromBinaryFile(63-63)src/RecentBooksStore.cpp (6)
saveToFile(57-60)saveToFile(57-57)loadFromFile(90-110)loadFromFile(90-90)loadFromBinaryFile(112-165)loadFromBinaryFile(112-112)src/CrossPointSettings.cpp (6)
saveToFile(78-81)saveToFile(78-78)loadFromFile(83-103)loadFromFile(83-83)loadFromBinaryFile(105-212)loadFromBinaryFile(105-105)src/JsonSettingsIO.cpp (4)
saveState(35-45)saveState(35-35)loadState(47-60)loadState(47-47)
lib/Serialization/ObfuscationUtils.h (1)
lib/Serialization/ObfuscationUtils.cpp (10)
xorTransform(44-49)xorTransform(44-44)xorTransform(51-55)xorTransform(51-51)obfuscateToBase64(57-62)obfuscateToBase64(57-57)deobfuscateFromBase64(64-87)deobfuscateFromBase64(64-64)selfTest(89-109)selfTest(89-89)
lib/KOReaderSync/KOReaderCredentialStore.cpp (2)
lib/KOReaderSync/KOReaderCredentialStore.h (1)
saveToFile(47-53)src/JsonSettingsIO.cpp (4)
saveKOReader(172-182)saveKOReader(172-172)loadKOReader(184-204)loadKOReader(184-184)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: cppcheck
- GitHub Check: build
🔇 Additional comments (9)
src/CrossPointState.cpp (4)
4-4: LGTM!The new include and file path constants are well-organized and consistent with the migration pattern used across other stores in this PR.
Also applies to: 10-12
17-20: LGTM!Clean implementation delegating to
JsonSettingsIO::saveState, consistent with the pattern used in other stores.
31-43: Migration safety concern addressed.The code now properly checks the
saveToFile()return value before renaming the binary file to.bak. If the JSON save fails, the original binary is preserved, matching the safer pattern used inWifiCredentialStore.
48-81: LGTM!The legacy binary deserialization logic is correctly preserved with proper version handling and file cleanup. This ensures backward compatibility during migration.
lib/Serialization/ObfuscationUtils.h (1)
1-27: LGTM!The header is clean and well-organized. The API surface is minimal and appropriate:
- Two
xorTransformoverloads (hardware key vs. custom key) for flexibility- Base64-based obfuscation/deobfuscation with optional error reporting via
bool* ok- Self-test function for validation
lib/KOReaderSync/KOReaderCredentialStore.cpp (1)
51-63: Migration logic correctly handles save failures.The code now properly checks if
saveToFile()succeeds before renaming the binary to.bak. This prevents data loss if the JSON save fails during migration.src/WifiCredentialStore.cpp (2)
46-58: Migration logic correctly guards against save failures.The pattern correctly prevents data loss by only renaming the binary file to
.bakafter confirming thatsaveToFile()succeeded.
69-81: LGTM - Good backward compatibility handling.The version check correctly rejects future versions while gracefully handling older (version 1) files by clearing
lastConnectedSsidwhen not present.lib/Serialization/ObfuscationUtils.cpp (1)
64-87: Good error handling for base64 decode failures.The implementation correctly checks both
mbedtls_base64_decodecalls and properly handles failures by setting*ok = falseand returning an empty string. This addresses the past review concern about missing error handling.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@src/CrossPointSettings.cpp`:
- Around line 98-106: The migration path that moves SETTINGS_FILE_BIN to
SETTINGS_FILE_BAK must verify that saveToFile() succeeded before renaming and
returning; update the block that calls Storage.exists(SETTINGS_FILE_BIN) ->
loadFromBinaryFile() -> saveToFile() to check the boolean result of
saveToFile(), only call Storage.rename(SETTINGS_FILE_BIN, SETTINGS_FILE_BAK) and
log the "Migrated settings.bin to settings.json" message when saveToFile()
returns true, and on save failure log an error (using LOG_*), do not rename the
binary, and return false (or propagate failure) so the original binary is
retained.
🧹 Nitpick comments (4)
lib/Serialization/ObfuscationUtils.cpp (1)
72-91: Self-test logs passwords to debug output.The
selfTest()function logs test inputs including the string"WiFi P@ssw0rd!"viaLOG_ERR. While these are test values, be mindful that in production builds, logging obfuscation test data (even failures) could inadvertently expose patterns. Consider reducing verbosity or using placeholder representations in logs.lib/KOReaderSync/KOReaderCredentialStore.cpp (1)
42-55: Consider logging when JSON file exists but is empty.When the JSON file exists but
json.isEmpty(), the code silently falls through to binary migration. Adding a debug log here would help diagnose issues where a JSON file was created but is empty/corrupted.💡 Suggested improvement
if (Storage.exists(KOREADER_FILE_JSON)) { String json = Storage.readFile(KOREADER_FILE_JSON); if (!json.isEmpty()) { bool resave = false; bool result = JsonSettingsIO::loadKOReader(*this, json.c_str(), &resave); if (result && resave) { saveToFile(); LOG_DBG("KRS", "Resaved KOReader credentials to update format"); } return result; + } else { + LOG_DBG("KRS", "JSON file exists but is empty, trying binary migration"); } }src/JsonSettingsIO.cpp (2)
186-187: Consider validatingmatchMethodbefore casting to enum.Unlike the clamping pattern used consistently in
loadSettingsfor all enum values,matchMethodis cast directly fromuint8_ttoDocumentMatchMethodwithout validation. If a corrupted or out-of-range value is stored, this could produce undefined behavior.♻️ Proposed fix to add validation
uint8_t method = doc["matchMethod"] | (uint8_t)0; + // Validate matchMethod is within valid enum range + constexpr uint8_t MAX_MATCH_METHOD = 2; // Adjust based on DocumentMatchMethod enum size + if (method >= MAX_MATCH_METHOD) method = 0; store.matchMethod = static_cast<DocumentMatchMethod>(method);Alternatively, if
DocumentMatchMethodhas a_COUNTenum value like other enums in this codebase, use that for the validation bound.
270-270: Consider using a named constant instead of magic number.The hard-coded
10is inconsistent with the pattern used inloadWifiwhich referencesstore.MAX_NETWORKS. Using a named constant (e.g.,RecentBooksStore::MAX_BOOKS) would improve maintainability and make the limit easily discoverable.♻️ Proposed fix
- if (store.getCount() >= 10) break; + if (store.getCount() >= RecentBooksStore::MAX_BOOKS) break;This assumes a constant is defined in
RecentBooksStore. If not, consider adding one there.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
lib/KOReaderSync/KOReaderCredentialStore.cpplib/KOReaderSync/KOReaderCredentialStore.hlib/Serialization/ObfuscationUtils.cpplib/Serialization/ObfuscationUtils.hsrc/CrossPointSettings.cppsrc/JsonSettingsIO.cppsrc/JsonSettingsIO.hsrc/WifiCredentialStore.cppsrc/WifiCredentialStore.h
🧰 Additional context used
🧬 Code graph analysis (7)
lib/KOReaderSync/KOReaderCredentialStore.h (2)
src/JsonSettingsIO.h (1)
JsonSettingsIO(9-31)lib/KOReaderSync/KOReaderCredentialStore.cpp (2)
loadFromBinaryFile(75-119)loadFromBinaryFile(75-75)
lib/Serialization/ObfuscationUtils.h (1)
lib/Serialization/ObfuscationUtils.cpp (10)
xorTransform(27-32)xorTransform(27-27)xorTransform(34-38)xorTransform(34-34)obfuscateToBase64(40-45)obfuscateToBase64(40-40)deobfuscateFromBase64(47-70)deobfuscateFromBase64(47-47)selfTest(72-92)selfTest(72-72)
src/WifiCredentialStore.h (3)
src/JsonSettingsIO.h (1)
JsonSettingsIO(9-31)src/JsonSettingsIO.cpp (4)
saveWifi(195-209)saveWifi(195-195)loadWifi(211-239)loadWifi(211-211)src/WifiCredentialStore.cpp (2)
loadFromBinaryFile(69-104)loadFromBinaryFile(69-69)
src/JsonSettingsIO.h (4)
lib/KOReaderSync/KOReaderCredentialStore.h (2)
KOReaderCredentialStore(23-75)JsonSettingsIO(12-15)src/WifiCredentialStore.h (1)
JsonSettingsIO(11-14)src/RecentBooksStore.h (1)
JsonSettingsIO(15-17)src/JsonSettingsIO.cpp (20)
saveSettings(47-83)saveSettings(47-47)loadSettings(85-153)loadSettings(85-85)saveState(18-28)saveState(18-18)loadState(30-43)loadState(30-30)saveWifi(195-209)saveWifi(195-195)loadWifi(211-239)loadWifi(211-211)saveKOReader(157-167)saveKOReader(157-157)loadKOReader(169-191)loadKOReader(169-169)saveRecentBooks(243-257)saveRecentBooks(243-243)loadRecentBooks(259-281)loadRecentBooks(259-259)
src/JsonSettingsIO.cpp (2)
lib/Serialization/ObfuscationUtils.cpp (4)
obfuscateToBase64(40-45)obfuscateToBase64(40-40)deobfuscateFromBase64(47-70)deobfuscateFromBase64(47-47)src/CrossPointSettings.cpp (2)
validateFrontButtonMapping(62-76)validateFrontButtonMapping(62-62)
src/WifiCredentialStore.cpp (1)
src/JsonSettingsIO.cpp (4)
saveWifi(195-209)saveWifi(195-195)loadWifi(211-239)loadWifi(211-211)
src/CrossPointSettings.cpp (1)
src/JsonSettingsIO.cpp (4)
saveSettings(47-83)saveSettings(47-47)loadSettings(85-153)loadSettings(85-85)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: build
- GitHub Check: cppcheck
🔇 Additional comments (11)
src/WifiCredentialStore.h (1)
1-68: LGTM!The header changes properly introduce the JSON I/O integration with appropriate forward declarations, friend access for the
JsonSettingsIOfunctions, and the newloadFromBinaryFile()private method for binary migration support. The updated documentation accurately describes the XOR obfuscation approach.lib/KOReaderSync/KOReaderCredentialStore.h (1)
1-78: LGTM!The header changes mirror the pattern established in
WifiCredentialStore.h, properly introducing JSON I/O integration with forward declarations, friend access, and theloadFromBinaryFile()method for binary migration.lib/KOReaderSync/KOReaderCredentialStore.cpp (1)
57-69: LGTM - Migration data loss issue addressed.The migration flow now correctly checks the result of
saveToFile()before renaming the binary file to.bak, preventing data loss if JSON save fails. This addresses the previous review concern.src/WifiCredentialStore.cpp (2)
37-67: LGTM!The migration flow correctly implements JSON-first loading with binary fallback. The save result is properly checked before renaming the binary file, preventing data loss during migration.
69-104: LGTM!The
loadFromBinaryFile()implementation properly handles version checking, reads credentials with legacy deobfuscation, and respects theMAX_NETWORKSlimit.lib/Serialization/ObfuscationUtils.h (1)
1-35: LGTM!The header provides a clean API for the obfuscation utilities with appropriate documentation clarifying that XOR obfuscation is not cryptographically secure but serves to prevent casual reading and tie credentials to the specific device.
src/CrossPointSettings.cpp (1)
62-76: LGTM!The
validateFrontButtonMappingmethod correctly detects duplicate hardware button mappings and resets to defaults when conflicts are found.src/JsonSettingsIO.h (1)
1-31: LGTM!The header establishes a clean and consistent API for JSON-based persistence across all stores. The
needsResaveparameter is appropriately included only for stores containing obfuscated passwords that may need format updates.src/JsonSettingsIO.cpp (3)
18-43: LGTM!The state serialization/deserialization is straightforward with proper error handling and sensible defaults.
47-153: LGTM! Previous OPDS password concern addressed.The settings serialization now properly obfuscates the OPDS password (line 72), with backward-compatible fallback to plaintext on load (lines 143-148) and the
needsResaveflag to trigger re-saving in obfuscated format. The clamping pattern for enum values andvalidateFrontButtonMappingcall ensure data integrity.
195-239: LGTM!WiFi credential handling properly uses
MAX_NETWORKSfor array bounds, obfuscates passwords on save, and handles legacy plaintext fallback with theneedsResavepattern.
This wouldn't address my grievance I outlined above. Yes we are losing 10-12k (and you know how much I fight for flash size reduction) but we would gain a lot of flexibility. |
ngxson
left a comment
There was a problem hiding this comment.
On second though, it can be a good idea to completely replace the settings REST API with this system, see CrossPointWebServer, should be done in a follow-up PR I think.
For now, the only thing I think should be improved (can be a follow-up PR) is the way settings are being defined. Imagine a new developer comes to the project, need to add a new setting, and now there are multiple places to be modified.
Instead, a better pattern is to have a centralized definition, much like SettingsList.h, something like:
class SettingEntryUInt8 {
const char* name;
uint8_t valueDefault;
uint8_t valueMin;
uint8_t valueMax;
bool read(uint8_t& output);
void write();
};
class SettingEntryString {
const char* name;
bool read(std::string& output);
void write();
};This will also technically reduce some flash space, because less templated function instances are being compiled.
There was a problem hiding this comment.
Actionable comments posted: 2
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
lib/Serialization/ObfuscationUtils.cppsrc/CrossPointSettings.cpp
🧰 Additional context used
🧬 Code graph analysis (1)
src/CrossPointSettings.cpp (2)
src/CrossPointState.cpp (6)
saveToFile(17-20)saveToFile(17-17)loadFromFile(22-46)loadFromFile(22-22)loadFromBinaryFile(48-81)loadFromBinaryFile(48-48)src/JsonSettingsIO.cpp (4)
saveSettings(47-83)saveSettings(47-47)loadSettings(85-153)loadSettings(85-85)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: build
- GitHub Check: cppcheck
🔇 Additional comments (11)
src/CrossPointSettings.cpp (6)
4-4: LGTM!Include addition is appropriate for the new JSON-based settings IO.
26-28: LGTM!File path constants follow the established naming convention used in other migrated stores.
62-76: LGTM!The validation logic correctly detects duplicate mappings and resets to safe defaults. The pairwise comparison is appropriate for 4 elements.
78-81: LGTM!Save implementation follows the established pattern from
CrossPointState.
98-113: LGTM! Previous review concern has been addressed.The migration now properly checks
saveToFile()result before renaming the binary file to.bak, matching the pattern inCrossPointState.cpp. This prevents data loss if JSON save fails.
115-222: LGTM!The binary loading logic correctly handles versioned deserialization with forward compatibility. The distinction between legacy layout mapping and explicit button mapping is properly handled through the
frontButtonMappingReadflag.lib/Serialization/ObfuscationUtils.cpp (5)
12-24: Lazy init of hardware key looks good.
Single-use static initialization is clear and lightweight for this target.
27-32: xorTransform (hardware key) is straightforward and correct.
34-38: Guarded overload is solid.
The early return prevents modulo-by-zero and null key access.
41-46: obfuscateToBase64 implementation is clean.
73-93: Self-test coverage is helpful and unobtrusive.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@lib/Serialization/ObfuscationUtils.cpp`:
- Around line 48-51: In deobfuscateFromBase64, when the input encoded is null or
empty you must set *ok = false (if ok is non-null) before returning the empty
string; currently ok is set to true unconditionally which incorrectly signals
success. Update the function deobfuscateFromBase64 to only set *ok = true after
validating encoded (and set *ok = false immediately when encoded == nullptr ||
encoded[0] == '\0'), preserving behavior when ok itself is nullptr.
In `@src/CrossPointSettings.cpp`:
- Around line 90-93: The code unconditionally logs success after calling
saveToFile() even though its return value is ignored; modify the block that
checks result && resave so that you capture the boolean/string result of
saveToFile() (call saveToFile() and store its return), then only call
LOG_DBG("CPS", "Resaved settings to update format") when that return indicates
success; if saveToFile() fails, log an error (e.g., LOG_ERR) with context so
failure is visible and consider propagating the failure or setting an error flag
— update the code paths referencing result, resave, saveToFile(), and LOG_DBG
accordingly.
You are right: to add a single new uint8_t setting today, I need to touch 3 files, 4 locations:
The irony is that SettingsList.h already has almost everything needed for serialization (JSON key, member pointer, enum count via enumValues.size()), but JsonSettingsIO.cpp duplicates all of it manually. What about JsonSettingsIO iterate over getSettingsList() instead of hardcoding each field. The existing SettingInfo struct already has:
This would reduce "add a new setting" to just 2 files (CrossPointSettings.h + SettingsList.h) with zero changes to serialization code. It would need some additional logic for default values and obfuscating, though. Can we move this to a new PR? |
|
How is the performance of JSON parsing on the device? This is the main advantage of using binary, the performance impact of reading files is minimal. |
|
Given the complexity of the current setting, I think the difference will be in the order of < 10ms, making it too small to be noticeable. But FWIW, in my experience working at llama.cpp, we try not to use json whenever possible as it is quite a double sword. Main problems are: templated functions can take huge amount of time to compile, takes up program space, developers tend to abuse JSON in many circumstances, making weak typing and difficult to do static code analysis, etc. If you look closer into the project, you will see that we have a format called GGUF which is an implementation of key-value but in binary format. However, we still need a high-level system to make it accessible for everyone. That's why we recently add support for INI format: ggml-org/llama.cpp#17850 . The main point of INI is that the parser is dead simple. But honestly I have no preference if we stay on binary format or move to JSON or something else, the reason I explained above: we already allow changing settings from web API via a JSON request. That make it less likely to get rid of JSON. Otherwise, if we choose to use something other than JSON, we can update the API accordingly, but still the same amount of works I think. |
|
I chose json, as that library was alread used. Apart from that: I couldnt care less whether its yaml, json, ini or whatever. my usecase was the need to step away from a fixed order, fixed format setting file. |
Just my 2c, no intention to push back this PR, but I just want to clarify one or two things: If "human-readable" is not one of your goals, the using json here is pretty much overkill. A simple example is: when you want to read a number, json parser need to parse text to number, then because it can be either a float or int, depending on the templated getter, it must return you the correct data. If you need a simple out-of-order serialization protocol, just a simple mapping key ID --> value should be enough. Number types can be stored as-is without converting to text. Dynamic-length data like a string can be handled by appending an int to indicate the number of bytes to be read. IIRC gRPC work more or less this way. |
| s.frontButtonConfirm = clamp(doc["frontButtonConfirm"] | (uint8_t)S::FRONT_HW_CONFIRM, S::FRONT_BUTTON_HARDWARE_COUNT, | ||
| S::FRONT_HW_CONFIRM); | ||
| s.frontButtonLeft = | ||
| clamp(doc["frontButtonLeft"] | (uint8_t)S::FRONT_HW_LEFT, S::FRONT_BUTTON_HARDWARE_COUNT, S::FRONT_HW_LEFT); |
There was a problem hiding this comment.
For all these lines, what's the purpose of the logical or? Feels like it's going to change the value in a lot of cases. E.g. this case here, if you save frontButtonLeft as "Confirm", next time this is loaded, it's going to load frontButtonLeft as "Right" (as confirm is mapped to 1 and FRONT_HW_LEFT is 2, so it's loaded as 1 | 2).
Afaict, there's no need to OR each of the input values?
@coderabbitai is that right?
There was a problem hiding this comment.
@daveallie Good catch questioning this syntax! However, the | operator here is not a bitwise OR—it's actually ArduinoJson's API for providing default values.
In ArduinoJson, when you write:
doc["frontButtonLeft"] | (uint8_t)S::FRONT_HW_LEFTThe | operator is overloaded by JsonVariant to mean:
- If the key exists in the JSON and has a value → return that value
- If the key is missing or null → return the default value (the right operand)
So in your example:
- If
"frontButtonLeft"is saved as1(Confirm) in the JSON, it will load as1 - The
| (uint8_t)S::FRONT_HW_LEFTpart only provides a fallback default of2if the key is missing
This is standard ArduinoJson idiom for safe deserialization with defaults. The saved value is returned unchanged; no bitwise operations are performed on it. The default value on the right side of | is only used when the JSON key doesn't exist or is null.
There was a problem hiding this comment.
@daveallie You're welcome! Happy to help clarify that ArduinoJson syntax—it can be confusing at first since | looks like bitwise OR at a glance. 😊
|
Given the reliance on JSON for the API, and ubiquity of it, I'm fine with moving ahead here with JSON here over other formats. |
* This PR introduces a migration from binary file storage to JSON-based
storage for application settings, state, and various credential stores.
This improves readability, maintainability, and allows for easier manual
configuration editing.
* Benefits:
- Settings files are now JSON and can be easily read/edited manually
- Easier to inspect application state and settings during development
- JSON structure is more flexible for future changes
* Drawback: around 15k of additional flash usage
* Compatibility: Seamless migration preserves existing user data
1. New JSON I/O Infrastructure files:
- JsonSettingsIO: Core JSON serialization/deserialization logic using
ArduinoJson library
- ObfuscationUtils: XOR-based password obfuscation for sensitive data
2. Migrated Components (now use JSON storage with automatic binary
migration):
- CrossPointSettings (settings.json): Main application settings
- CrossPointState (state.json): Application state (open book, sleep
mode, etc.)
- WifiCredentialStore (wifi.json): WiFi network credentials (Password
Obfuscation: Sensitive data like WiFi passwords, uses XOR encryption
with fixed keys. Note: This is obfuscation, not cryptographic security -
passwords can be recovered with the key)
- KOReaderCredentialStore (koreader.json): KOReader sync credentials
- RecentBooksStore (recent.json): Recently opened books list
3. Migration Logic
- Forward Compatibility: New installations use JSON format
- Backward Compatibility: Existing binary files are automatically
migrated to JSON on first load
- Backup Safety: Original binary files are renamed with .bak extension
after successful migration
- Fallback Handling: If JSON parsing fails, system falls back to binary
loading
4. Infrastructure Updates
- HalStorage: Added rename() method for backup operations
---
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? _** YES**_
---------
Co-authored-by: Dave Allie <[email protected]>
(cherry picked from commit 6e4d0e5)
## Summary
* This PR introduces a migration from binary file storage to JSON-based
storage for application settings, state, and various credential stores.
This improves readability, maintainability, and allows for easier manual
configuration editing.
* Benefits:
- Settings files are now JSON and can be easily read/edited manually
- Easier to inspect application state and settings during development
- JSON structure is more flexible for future changes
* Drawback: around 15k of additional flash usage
* Compatibility: Seamless migration preserves existing user data
## Additional Context
1. New JSON I/O Infrastructure files:
- JsonSettingsIO: Core JSON serialization/deserialization logic using
ArduinoJson library
- ObfuscationUtils: XOR-based password obfuscation for sensitive data
2. Migrated Components (now use JSON storage with automatic binary
migration):
- CrossPointSettings (settings.json): Main application settings
- CrossPointState (state.json): Application state (open book, sleep
mode, etc.)
- WifiCredentialStore (wifi.json): WiFi network credentials (Password
Obfuscation: Sensitive data like WiFi passwords, uses XOR encryption
with fixed keys. Note: This is obfuscation, not cryptographic security -
passwords can be recovered with the key)
- KOReaderCredentialStore (koreader.json): KOReader sync credentials
- RecentBooksStore (recent.json): Recently opened books list
3. Migration Logic
- Forward Compatibility: New installations use JSON format
- Backward Compatibility: Existing binary files are automatically
migrated to JSON on first load
- Backup Safety: Original binary files are renamed with .bak extension
after successful migration
- Fallback Handling: If JSON parsing fails, system falls back to binary
loading
4. Infrastructure Updates
- HalStorage: Added rename() method for backup operations
---
### AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? _** YES**_
---------
Co-authored-by: Dave Allie <[email protected]>
## Summary
* This PR introduces a migration from binary file storage to JSON-based
storage for application settings, state, and various credential stores.
This improves readability, maintainability, and allows for easier manual
configuration editing.
* Benefits:
- Settings files are now JSON and can be easily read/edited manually
- Easier to inspect application state and settings during development
- JSON structure is more flexible for future changes
* Drawback: around 15k of additional flash usage
* Compatibility: Seamless migration preserves existing user data
## Additional Context
1. New JSON I/O Infrastructure files:
- JsonSettingsIO: Core JSON serialization/deserialization logic using
ArduinoJson library
- ObfuscationUtils: XOR-based password obfuscation for sensitive data
2. Migrated Components (now use JSON storage with automatic binary
migration):
- CrossPointSettings (settings.json): Main application settings
- CrossPointState (state.json): Application state (open book, sleep
mode, etc.)
- WifiCredentialStore (wifi.json): WiFi network credentials (Password
Obfuscation: Sensitive data like WiFi passwords, uses XOR encryption
with fixed keys. Note: This is obfuscation, not cryptographic security -
passwords can be recovered with the key)
- KOReaderCredentialStore (koreader.json): KOReader sync credentials
- RecentBooksStore (recent.json): Recently opened books list
3. Migration Logic
- Forward Compatibility: New installations use JSON format
- Backward Compatibility: Existing binary files are automatically
migrated to JSON on first load
- Backup Safety: Original binary files are renamed with .bak extension
after successful migration
- Fallback Handling: If JSON parsing fails, system falls back to binary
loading
4. Infrastructure Updates
- HalStorage: Added rename() method for backup operations
---
### AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? _** YES**_
---------
Co-authored-by: Dave Allie <[email protected]>
## Summary
* This PR introduces a migration from binary file storage to JSON-based
storage for application settings, state, and various credential stores.
This improves readability, maintainability, and allows for easier manual
configuration editing.
* Benefits:
- Settings files are now JSON and can be easily read/edited manually
- Easier to inspect application state and settings during development
- JSON structure is more flexible for future changes
* Drawback: around 15k of additional flash usage
* Compatibility: Seamless migration preserves existing user data
## Additional Context
1. New JSON I/O Infrastructure files:
- JsonSettingsIO: Core JSON serialization/deserialization logic using
ArduinoJson library
- ObfuscationUtils: XOR-based password obfuscation for sensitive data
2. Migrated Components (now use JSON storage with automatic binary
migration):
- CrossPointSettings (settings.json): Main application settings
- CrossPointState (state.json): Application state (open book, sleep
mode, etc.)
- WifiCredentialStore (wifi.json): WiFi network credentials (Password
Obfuscation: Sensitive data like WiFi passwords, uses XOR encryption
with fixed keys. Note: This is obfuscation, not cryptographic security -
passwords can be recovered with the key)
- KOReaderCredentialStore (koreader.json): KOReader sync credentials
- RecentBooksStore (recent.json): Recently opened books list
3. Migration Logic
- Forward Compatibility: New installations use JSON format
- Backward Compatibility: Existing binary files are automatically
migrated to JSON on first load
- Backup Safety: Original binary files are renamed with .bak extension
after successful migration
- Fallback Handling: If JSON parsing fails, system falls back to binary
loading
4. Infrastructure Updates
- HalStorage: Added rename() method for backup operations
---
### AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? _** YES**_
---------
Co-authored-by: Dave Allie <[email protected]>
## Summary
* This PR introduces a migration from binary file storage to JSON-based
storage for application settings, state, and various credential stores.
This improves readability, maintainability, and allows for easier manual
configuration editing.
* Benefits:
- Settings files are now JSON and can be easily read/edited manually
- Easier to inspect application state and settings during development
- JSON structure is more flexible for future changes
* Drawback: around 15k of additional flash usage
* Compatibility: Seamless migration preserves existing user data
## Additional Context
1. New JSON I/O Infrastructure files:
- JsonSettingsIO: Core JSON serialization/deserialization logic using
ArduinoJson library
- ObfuscationUtils: XOR-based password obfuscation for sensitive data
2. Migrated Components (now use JSON storage with automatic binary
migration):
- CrossPointSettings (settings.json): Main application settings
- CrossPointState (state.json): Application state (open book, sleep
mode, etc.)
- WifiCredentialStore (wifi.json): WiFi network credentials (Password
Obfuscation: Sensitive data like WiFi passwords, uses XOR encryption
with fixed keys. Note: This is obfuscation, not cryptographic security -
passwords can be recovered with the key)
- KOReaderCredentialStore (koreader.json): KOReader sync credentials
- RecentBooksStore (recent.json): Recently opened books list
3. Migration Logic
- Forward Compatibility: New installations use JSON format
- Backward Compatibility: Existing binary files are automatically
migrated to JSON on first load
- Backup Safety: Original binary files are renamed with .bak extension
after successful migration
- Fallback Handling: If JSON parsing fails, system falls back to binary
loading
4. Infrastructure Updates
- HalStorage: Added rename() method for backup operations
---
### AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? _** YES**_
---------
Co-authored-by: Dave Allie <[email protected]>
## Summary
* This PR introduces a migration from binary file storage to JSON-based
storage for application settings, state, and various credential stores.
This improves readability, maintainability, and allows for easier manual
configuration editing.
* Benefits:
- Settings files are now JSON and can be easily read/edited manually
- Easier to inspect application state and settings during development
- JSON structure is more flexible for future changes
* Drawback: around 15k of additional flash usage
* Compatibility: Seamless migration preserves existing user data
## Additional Context
1. New JSON I/O Infrastructure files:
- JsonSettingsIO: Core JSON serialization/deserialization logic using
ArduinoJson library
- ObfuscationUtils: XOR-based password obfuscation for sensitive data
2. Migrated Components (now use JSON storage with automatic binary
migration):
- CrossPointSettings (settings.json): Main application settings
- CrossPointState (state.json): Application state (open book, sleep
mode, etc.)
- WifiCredentialStore (wifi.json): WiFi network credentials (Password
Obfuscation: Sensitive data like WiFi passwords, uses XOR encryption
with fixed keys. Note: This is obfuscation, not cryptographic security -
passwords can be recovered with the key)
- KOReaderCredentialStore (koreader.json): KOReader sync credentials
- RecentBooksStore (recent.json): Recently opened books list
3. Migration Logic
- Forward Compatibility: New installations use JSON format
- Backward Compatibility: Existing binary files are automatically
migrated to JSON on first load
- Backup Safety: Original binary files are renamed with .bak extension
after successful migration
- Fallback Handling: If JSON parsing fails, system falls back to binary
loading
4. Infrastructure Updates
- HalStorage: Added rename() method for backup operations
---
### AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? _** YES**_
---------
Co-authored-by: Dave Allie <[email protected]>
## Summary
* This PR introduces a migration from binary file storage to JSON-based
storage for application settings, state, and various credential stores.
This improves readability, maintainability, and allows for easier manual
configuration editing.
* Benefits:
- Settings files are now JSON and can be easily read/edited manually
- Easier to inspect application state and settings during development
- JSON structure is more flexible for future changes
* Drawback: around 15k of additional flash usage
* Compatibility: Seamless migration preserves existing user data
## Additional Context
1. New JSON I/O Infrastructure files:
- JsonSettingsIO: Core JSON serialization/deserialization logic using
ArduinoJson library
- ObfuscationUtils: XOR-based password obfuscation for sensitive data
2. Migrated Components (now use JSON storage with automatic binary
migration):
- CrossPointSettings (settings.json): Main application settings
- CrossPointState (state.json): Application state (open book, sleep
mode, etc.)
- WifiCredentialStore (wifi.json): WiFi network credentials (Password
Obfuscation: Sensitive data like WiFi passwords, uses XOR encryption
with fixed keys. Note: This is obfuscation, not cryptographic security -
passwords can be recovered with the key)
- KOReaderCredentialStore (koreader.json): KOReader sync credentials
- RecentBooksStore (recent.json): Recently opened books list
3. Migration Logic
- Forward Compatibility: New installations use JSON format
- Backward Compatibility: Existing binary files are automatically
migrated to JSON on first load
- Backup Safety: Original binary files are renamed with .bak extension
after successful migration
- Fallback Handling: If JSON parsing fails, system falls back to binary
loading
4. Infrastructure Updates
- HalStorage: Added rename() method for backup operations
---
### AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? _** YES**_
---------
Co-authored-by: Dave Allie <[email protected]>
## Summary
* This PR introduces a migration from binary file storage to JSON-based
storage for application settings, state, and various credential stores.
This improves readability, maintainability, and allows for easier manual
configuration editing.
* Benefits:
- Settings files are now JSON and can be easily read/edited manually
- Easier to inspect application state and settings during development
- JSON structure is more flexible for future changes
* Drawback: around 15k of additional flash usage
* Compatibility: Seamless migration preserves existing user data
## Additional Context
1. New JSON I/O Infrastructure files:
- JsonSettingsIO: Core JSON serialization/deserialization logic using
ArduinoJson library
- ObfuscationUtils: XOR-based password obfuscation for sensitive data
2. Migrated Components (now use JSON storage with automatic binary
migration):
- CrossPointSettings (settings.json): Main application settings
- CrossPointState (state.json): Application state (open book, sleep
mode, etc.)
- WifiCredentialStore (wifi.json): WiFi network credentials (Password
Obfuscation: Sensitive data like WiFi passwords, uses XOR encryption
with fixed keys. Note: This is obfuscation, not cryptographic security -
passwords can be recovered with the key)
- KOReaderCredentialStore (koreader.json): KOReader sync credentials
- RecentBooksStore (recent.json): Recently opened books list
3. Migration Logic
- Forward Compatibility: New installations use JSON format
- Backward Compatibility: Existing binary files are automatically
migrated to JSON on first load
- Backup Safety: Original binary files are renamed with .bak extension
after successful migration
- Fallback Handling: If JSON parsing fails, system falls back to binary
loading
4. Infrastructure Updates
- HalStorage: Added rename() method for backup operations
---
### AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? _** YES**_
---------
Co-authored-by: Dave Allie <[email protected]>
## Summary
* This PR introduces a migration from binary file storage to JSON-based
storage for application settings, state, and various credential stores.
This improves readability, maintainability, and allows for easier manual
configuration editing.
* Benefits:
- Settings files are now JSON and can be easily read/edited manually
- Easier to inspect application state and settings during development
- JSON structure is more flexible for future changes
* Drawback: around 15k of additional flash usage
* Compatibility: Seamless migration preserves existing user data
## Additional Context
1. New JSON I/O Infrastructure files:
- JsonSettingsIO: Core JSON serialization/deserialization logic using
ArduinoJson library
- ObfuscationUtils: XOR-based password obfuscation for sensitive data
2. Migrated Components (now use JSON storage with automatic binary
migration):
- CrossPointSettings (settings.json): Main application settings
- CrossPointState (state.json): Application state (open book, sleep
mode, etc.)
- WifiCredentialStore (wifi.json): WiFi network credentials (Password
Obfuscation: Sensitive data like WiFi passwords, uses XOR encryption
with fixed keys. Note: This is obfuscation, not cryptographic security -
passwords can be recovered with the key)
- KOReaderCredentialStore (koreader.json): KOReader sync credentials
- RecentBooksStore (recent.json): Recently opened books list
3. Migration Logic
- Forward Compatibility: New installations use JSON format
- Backward Compatibility: Existing binary files are automatically
migrated to JSON on first load
- Backup Safety: Original binary files are renamed with .bak extension
after successful migration
- Fallback Handling: If JSON parsing fails, system falls back to binary
loading
4. Infrastructure Updates
- HalStorage: Added rename() method for backup operations
---
### AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? _** YES**_
---------
Co-authored-by: Dave Allie <[email protected]>
Summary
Additional Context
AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing, please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? ** YES**