Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 75 additions & 9 deletions src/NFCManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1759,16 +1759,82 @@ bool NFCManager::executeTigerTagWrite(const NFCWriteRequest& request) {
if (!validateWriteUid(request.expected_spool_id, "WRITE_TIGERTAG")) return false;
if (!checkWriteCapacity(4, 10, "WRITE_TIGERTAG")) return false;

bool ok = connection_->writeISO14443Pages(4, 10, request.data.tigertag_data, 40);
if (ok) {
Serial.println("NFCManager: WRITE_TIGERTAG succeeded");
LogBuffer::getInstance().logPrintf("Write TigerTag: OK\n");
forceRescan();
} else {
Serial.println("NFCManager: WRITE_TIGERTAG failed");
LogBuffer::getInstance().logPrintf("Write TigerTag: FAILED\n");
// Pre-read current tag contents so we can write only the pages that actually changed.
// TigerTag has a fixed binary layout across pages 4–13, so byte offsets never shift
// and page-granular partial writes are safe.
uint8_t current[40];
uint16_t bytesRead = connection_->readISO14443Pages(4, 10, current, sizeof(current), true);
if (bytesRead < 40) {
Serial.printf("NFCManager: WRITE_TIGERTAG pre-read returned %u bytes, falling back to full write\n",
bytesRead);
LogBuffer::getInstance().logPrintf("Write TigerTag: pre-read failed, full write\n");
bool ok = connection_->writeISO14443Pages(4, 10, request.data.tigertag_data, 40);
if (ok) {
Serial.println("NFCManager: WRITE_TIGERTAG succeeded");
LogBuffer::getInstance().logPrintf("Write TigerTag: OK\n");
forceRescan();
} else {
Serial.println("NFCManager: WRITE_TIGERTAG failed");
LogBuffer::getInstance().logPrintf("Write TigerTag: FAILED\n");
}
return ok;
}

// Build contiguous runs of changed pages. 10 pages alternating → at most 5 runs.
struct PageRun { uint8_t startPage; uint8_t pageCount; };
PageRun runs[5];
uint8_t runCount = 0;
bool inRun = false;

for (uint8_t i = 0; i < 10; ++i) {
bool changed = memcmp(&request.data.tigertag_data[i * 4], &current[i * 4], 4) != 0;
if (changed) {
if (!inRun) {
runs[runCount].startPage = 4 + i;
runs[runCount].pageCount = 1;
inRun = true;
} else {
runs[runCount].pageCount++;
}
} else if (inRun) {
runCount++;
inRun = false;
}
}
return ok;
if (inRun) runCount++;

if (runCount == 0) {
Serial.println("NFCManager: WRITE_TIGERTAG no changes, skipping write");
LogBuffer::getInstance().logPrintf("Write TigerTag: no changes\n");
return true;
}

uint8_t totalPages = 0;
for (uint8_t r = 0; r < runCount; ++r) {
const PageRun& run = runs[r];
const uint8_t* data = request.data.tigertag_data + (run.startPage - 4) * 4;
if (!connection_->writeISO14443Pages(run.startPage, run.pageCount, data, run.pageCount * 4)) {
Serial.printf("NFCManager: WRITE_TIGERTAG run %u failed (page %u, %u pages), attempting full rewrite\n",
r, run.startPage, run.pageCount);
LogBuffer::getInstance().logPrintf("Write TigerTag: run failed, full rewrite\n");
if (connection_->writeISO14443Pages(4, 10, request.data.tigertag_data, 40)) {
Serial.println("NFCManager: WRITE_TIGERTAG full rewrite succeeded");
LogBuffer::getInstance().logPrintf("Write TigerTag: OK (full rewrite)\n");
forceRescan();
return true;
}
Serial.println("NFCManager: WRITE_TIGERTAG full rewrite failed");
LogBuffer::getInstance().logPrintf("Write TigerTag: FAILED\n");
return false;
}
totalPages += run.pageCount;
}

Serial.printf("NFCManager: WRITE_TIGERTAG wrote %u/10 pages in %u run(s)\n", totalPages, runCount);
LogBuffer::getInstance().logPrintf("Write TigerTag: OK (%u/10 pages, %u run%s)\n",
totalPages, runCount, runCount == 1 ? "" : "s");
forceRescan();
return true;
Comment on lines +1812 to +1837
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Add convergence/verification after partial TigerTag writes.

Line 1816 writes each changed run independently, but Line 1820 returns after a failed later run even if earlier runs already changed the tag. Also, Lines 1825-1829 report success without re-reading pages 4-13, so this can return success for an unverified or mixed TigerTag image. Add a full-write fallback on run failure and verify the final 40 bytes before returning true.

🐛 Proposed fix: converge on full write after run failure and verify read-back
     uint8_t totalPages = 0;
+    bool needsFullWriteFallback = false;
     for (uint8_t r = 0; r < runCount; ++r) {
         const PageRun& run = runs[r];
         const uint8_t* data = request.data.tigertag_data + (run.startPage - 4) * 4;
         if (!connection_->writeISO14443Pages(run.startPage, run.pageCount, data, run.pageCount * 4)) {
             Serial.printf("NFCManager: WRITE_TIGERTAG failed at run %u (page %u, %u pages)\n",
                           r, run.startPage, run.pageCount);
-            LogBuffer::getInstance().logPrintf("Write TigerTag: FAILED\n");
-            return false;
+            LogBuffer::getInstance().logPrintf("Write TigerTag: partial run failed, trying full write\n");
+            needsFullWriteFallback = true;
+            break;
         }
         totalPages += run.pageCount;
     }
+
+    if (needsFullWriteFallback) {
+        if (!connection_->writeISO14443Pages(4, 10, request.data.tigertag_data, 40)) {
+            Serial.println("NFCManager: WRITE_TIGERTAG full-write fallback failed");
+            LogBuffer::getInstance().logPrintf("Write TigerTag: FAILED\n");
+            return false;
+        }
+        totalPages = 10;
+    }
+
+    uint8_t verify[40];
+    uint16_t verifyBytes = connection_->readISO14443Pages(4, 10, verify, sizeof(verify), true);
+    if (verifyBytes < 40 || memcmp(verify, request.data.tigertag_data, sizeof(verify)) != 0) {
+        Serial.printf("NFCManager: WRITE_TIGERTAG verify failed (%u bytes read)\n", verifyBytes);
+        LogBuffer::getInstance().logPrintf("Write TigerTag: VERIFY FAILED\n");
+        return false;
+    }
 
     Serial.printf("NFCManager: WRITE_TIGERTAG wrote %u/10 pages in %u run(s)\n", totalPages, runCount);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
uint8_t totalPages = 0;
for (uint8_t r = 0; r < runCount; ++r) {
const PageRun& run = runs[r];
const uint8_t* data = request.data.tigertag_data + (run.startPage - 4) * 4;
if (!connection_->writeISO14443Pages(run.startPage, run.pageCount, data, run.pageCount * 4)) {
Serial.printf("NFCManager: WRITE_TIGERTAG failed at run %u (page %u, %u pages)\n",
r, run.startPage, run.pageCount);
LogBuffer::getInstance().logPrintf("Write TigerTag: FAILED\n");
return false;
}
totalPages += run.pageCount;
}
Serial.printf("NFCManager: WRITE_TIGERTAG wrote %u/10 pages in %u run(s)\n", totalPages, runCount);
LogBuffer::getInstance().logPrintf("Write TigerTag: OK (%u/10 pages, %u run%s)\n",
totalPages, runCount, runCount == 1 ? "" : "s");
forceRescan();
return true;
uint8_t totalPages = 0;
bool needsFullWriteFallback = false;
for (uint8_t r = 0; r < runCount; ++r) {
const PageRun& run = runs[r];
const uint8_t* data = request.data.tigertag_data + (run.startPage - 4) * 4;
if (!connection_->writeISO14443Pages(run.startPage, run.pageCount, data, run.pageCount * 4)) {
Serial.printf("NFCManager: WRITE_TIGERTAG failed at run %u (page %u, %u pages)\n",
r, run.startPage, run.pageCount);
LogBuffer::getInstance().logPrintf("Write TigerTag: partial run failed, trying full write\n");
needsFullWriteFallback = true;
break;
}
totalPages += run.pageCount;
}
if (needsFullWriteFallback) {
if (!connection_->writeISO14443Pages(4, 10, request.data.tigertag_data, 40)) {
Serial.println("NFCManager: WRITE_TIGERTAG full-write fallback failed");
LogBuffer::getInstance().logPrintf("Write TigerTag: FAILED\n");
return false;
}
totalPages = 10;
}
uint8_t verify[40];
uint16_t verifyBytes = connection_->readISO14443Pages(4, 10, verify, sizeof(verify), true);
if (verifyBytes < 40 || memcmp(verify, request.data.tigertag_data, sizeof(verify)) != 0) {
Serial.printf("NFCManager: WRITE_TIGERTAG verify failed (%u bytes read)\n", verifyBytes);
LogBuffer::getInstance().logPrintf("Write TigerTag: VERIFY FAILED\n");
return false;
}
Serial.printf("NFCManager: WRITE_TIGERTAG wrote %u/10 pages in %u run(s)\n", totalPages, runCount);
LogBuffer::getInstance().logPrintf("Write TigerTag: OK (%u/10 pages, %u run%s)\n",
totalPages, runCount, runCount == 1 ? "" : "s");
forceRescan();
return true;
🧰 Tools
🪛 Clang (14.0.6)

[warning] 1813-1813: loop variable name 'r' is too short, expected at least 2 characters

(readability-identifier-length)


[warning] 1819-1819: do not call c-style vararg functions

(cppcoreguidelines-pro-type-vararg)


[warning] 1826-1826: do not call c-style vararg functions

(cppcoreguidelines-pro-type-vararg)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/NFCManager.cpp` around lines 1812 - 1829, The partial-run write loop in
NFCManager (using connection_->writeISO14443Pages and
request.data.tigertag_data) can leave a mixed/partial TigerTag if a later run
fails; modify the logic so that on any run failure you attempt a full-write
fallback of pages 4–13 (all 10 pages) using the original tigertag_data, then
read back the full 40 bytes (pages 4–13) via connection_->readISO14443Pages (or
the existing read method) to verify the contents match
request.data.tigertag_data before reporting success; ensure failure paths still
log via LogBuffer::getInstance().logPrintf and Serial.printf, call forceRescan()
only after verified success, and return false if verification fails.

}

bool NFCManager::executeOpenTag3DWrite(const NFCWriteRequest& request) {
Expand Down