From 598e8a4c64a851ea51df44305c216673fb78871f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Furkan=20Erg=C3=BCn?= Date: Sat, 4 Oct 2025 13:53:17 +0300 Subject: [PATCH] Fix offline data parsing with corrupted strokes - Skip corrupted strokes instead of stopping parsing - Force completion to 100% on last chunk if data is missing - Add validation for invalid dotCount values - Continue processing even when checksum fails --- .../pen/bluetooth/comm/CommProcessor20.java | 19 ++++++++-- .../sdk/pen/offline/OfflineByteParser.java | 38 +++++++++++++++++-- 2 files changed, 51 insertions(+), 6 deletions(-) diff --git a/NASDK2.0_Studio/app/src/main/java/kr/neolab/sdk/pen/bluetooth/comm/CommProcessor20.java b/NASDK2.0_Studio/app/src/main/java/kr/neolab/sdk/pen/bluetooth/comm/CommProcessor20.java index 02f4151..06738d7 100644 --- a/NASDK2.0_Studio/app/src/main/java/kr/neolab/sdk/pen/bluetooth/comm/CommProcessor20.java +++ b/NASDK2.0_Studio/app/src/main/java/kr/neolab/sdk/pen/bluetooth/comm/CommProcessor20.java @@ -2387,22 +2387,35 @@ else if(prevPacket.getDotCode() == 3) int position = pack.getDataRangeInt( 7, 1 ); NLog.d( "[CommProcessor20] received RES_OfflineChunk(0x24) command. packetId=" + packetId + ";position=" + position); int sizeBeforeCompress = pack.getDataRangeInt( 3, 2 ); + NLog.d( "[CommProcessor20] Chunk sizeBeforeCompress=" + sizeBeforeCompress + ", oRcvDataSize before=" + oRcvDataSize + ", oTotalDataSize=" + oTotalDataSize); OfflineByteParser parser = new OfflineByteParser( pack.getData() , maxPress); if(factor != null) parser.setCalibrate( factor ); try { offlineStrokes.addAll( parser.parse() ); + // Always increment received size, even if some strokes were corrupted + // This allows progress to reach 100% + oRcvDataSize += sizeBeforeCompress; + + // If this is the last chunk and we haven't reached 100%, force it to complete + if (position == 2 && oRcvDataSize < oTotalDataSize) { + int remaining = oTotalDataSize - oRcvDataSize; + NLog.w("[CommProcessor20] Last chunk but " + remaining + " bytes remaining. Forcing completion to 100%."); + oRcvDataSize = oTotalDataSize; + } + + NLog.d( "[CommProcessor20] Chunk processed. position=" + position + ", oRcvDataSize after=" + oRcvDataSize + ", progress=" + (oRcvDataSize * 100 / oTotalDataSize) + "%"); } catch ( Exception e ) { - NLog.e( "[CommProcessor20] deCompress parse Error !!!" ); + NLog.e( "[CommProcessor20] Fatal deCompression error. Error: " + e.getMessage() ); e.printStackTrace(); + // Only reject chunk if decompression fails completely + write( ProtocolParser20.buildOfflineChunkResponse( 1, packetId, position ) ); mHandler.postDelayed( mChkOfflineFailRunnable, OFFLINE_SEND_FAIL_TIME ); return; } - - oRcvDataSize += sizeBeforeCompress; // If you have received the chunk, process the offline data. if ( position == 2 ) { diff --git a/NASDK2.0_Studio/app/src/main/java/kr/neolab/sdk/pen/offline/OfflineByteParser.java b/NASDK2.0_Studio/app/src/main/java/kr/neolab/sdk/pen/offline/OfflineByteParser.java index bcc14e0..7bfa9fd 100644 --- a/NASDK2.0_Studio/app/src/main/java/kr/neolab/sdk/pen/offline/OfflineByteParser.java +++ b/NASDK2.0_Studio/app/src/main/java/kr/neolab/sdk/pen/offline/OfflineByteParser.java @@ -42,6 +42,7 @@ public class OfflineByteParser implements IFilterListener private int sectionId, ownerId, noteId, pageId; private int strokeCount, sizeAfterCompress, sizeBeforeCompress; private boolean isCompressed = false; + private boolean hasCorruptedData = false; private final static int STROKE_HEADER_LENGTH = 27; private final static int BYTE_DOT_SIZE = 16; @@ -83,6 +84,7 @@ public ArrayList parse() throws Exception stroke = null; strokes.clear(); + hasCorruptedData = false; // Header parsing @@ -105,6 +107,12 @@ public ArrayList parse() throws Exception data = null; + // Don't throw exception - just return whatever strokes we successfully parsed + // Corrupted strokes are skipped but process continues + if (hasCorruptedData) { + NLog.w("[OfflineByteParser] Chunk had corrupted data but continuing with valid strokes."); + } + // if ( strokes == null || strokes.size() <= 0 ) // { // return null; @@ -194,6 +202,26 @@ private void parseBody() throws Exception strokeIndex += STROKE_HEADER_LENGTH; int dotIndex = 0; tempDots = new ArrayList(); + + // Validate dotCount is not negative (corrupted header) + if (dotCount < 0) { + NLog.e("[OfflineByteParser] Invalid negative dotCount " + dotCount + " for stroke " + i + ". Corrupted header detected."); + checksumFailCount++; + hasCorruptedData = true; + break OUTER_STROKE_LOOP; + } + + // Validate that we have enough data for all dots in this stroke + int requiredBytes = strokeIndex + (dotCount * BYTE_DOT_SIZE); + if (requiredBytes > data.length) { + NLog.e("[OfflineByteParser] Insufficient data for stroke " + i + ": need " + + requiredBytes + " bytes but only have " + data.length + + " (dotCount=" + dotCount + "). Marking chunk as corrupted."); + checksumFailCount++; + hasCorruptedData = true; + break OUTER_STROKE_LOOP; + } + for(int j = 0; j < dotCount; j++) { int sectionId = this.sectionId; @@ -288,8 +316,9 @@ else if ( j == dotCount - 1 ) } else { - NLog.e( "[OfflineByteParser] lhCheckSum Fail Stroke cs : " + Integer.toHexString( (int) (lhCheckSum & 0xFF) ) + ", calc : " + Integer.toHexString( (int) (dotCalcCs & 0xFF) ) ); + NLog.e( "[OfflineByteParser] lhCheckSum Fail Stroke cs : " + Integer.toHexString( (int) (lhCheckSum & 0xFF) ) + ", calc : " + Integer.toHexString( (int) (dotCalcCs & 0xFF) ) + ". Skipping this stroke and continuing."); checksumFailCount++; + // Don't add this stroke's dots - just skip it and continue with next stroke } tempDots = new ArrayList(); @@ -298,8 +327,11 @@ else if ( j == dotCount - 1 ) strokeIndex += dotIndex; } - if(checksumFailCount > 3) - throw new CheckSumException( "lhCheckSum Fail Count="+ checksumFailCount); + if(checksumFailCount > 0) { + NLog.w("[OfflineByteParser] Parsing completed with " + checksumFailCount + " checksum failures. Successfully parsed " + strokes.size() + " valid strokes out of " + strokeCount + " total."); + } else { + NLog.i("[OfflineByteParser] Parsing completed successfully. Parsed " + strokes.size() + " strokes."); + } }