diff --git a/minimateplus/blastware_file.py b/minimateplus/blastware_file.py index 774f2b1..f708435 100644 --- a/minimateplus/blastware_file.py +++ b/minimateplus/blastware_file.py @@ -672,10 +672,7 @@ def write_blastware_file( # Do NOT use a5_frames[-1] — if _a5_frames contains stray frames from a # subsequent event (a known get_events side-effect), the last frame will # not be the terminator and the footer will be mis-identified. - # TERM detection (v0.14.0): - # Legacy walk: TERM has page_key == 0x0000. - # BW-exact walk: TERM has page_key != 0x0010 (e.g. 0x0001). - # The TERM is always the LAST frame in the list (include_terminator=True). + # TERM detection (v0.14.0): last frame if page_key != 0x0010 (sample marker) term_idx: Optional[int] = None if a5_frames and a5_frames[-1].page_key != 0x0010: term_idx = len(a5_frames) - 1 @@ -688,13 +685,8 @@ def write_blastware_file( term_frame = None # Frame contribution loop (v0.14.0 BW-exact walk). - # Frame structure: - # Event 1: [probe] [meta@0x1002] [meta@0x1004] [samples ...] [TERM-not-in-body] - # Event N: [probe@start+0x46] [samples ...] [TERM-not-in-body] - # - # Skip values per frame (confirmed from byte-diff vs BW-saved file - # M529LKIQ.G10): - # probe (fi=0): probe_skip (depends on STRT position) + # Skip values: + # probe (fi=0): probe_skip # meta@0x1002 (fi=1): 13 (6-byte inner header) # meta@0x1004 (fi=2): 13 (6-byte inner header) # sample chunks (fi=3+): 12 (5-byte inner header) @@ -740,10 +732,26 @@ def write_blastware_file( bytes(all_bytes[-28:]).hex() if len(all_bytes) >= 28 else bytes(all_bytes).hex(), ) - # Find the first valid 0e 08 footer marker (v0.14.0). The device's - # TERM response contains the real Blastware footer; older walks - # accidentally fetched data past the footer. Validate by checking the - # year field (uint16 BE at offset+4) is in 2015..2050. + # Strip embedded "duplicate header+STRT" blocks from body (v0.14.1). + # Chunk@0x1000 sometimes lands on the device's metadata-mirror page, + # whose response includes a 25-byte "00 12 03 00 STRT ..." block that + # mirrors the file's own header + STRT record. BW treats embedded STRT + # markers as second-event starts and rejects the file. Replace these + # blocks with zeros to preserve file size + alignment. + needle = b"\x00\x12\x03\x00STRT" + pos = bytes(all_bytes).find(needle) + while pos >= 0: + end = pos + 25 + if end <= len(all_bytes): + all_bytes[pos:end] = b"\x00" * 25 + log.warning( + "write_blastware_file: stripped duplicate header+STRT at " + "all_bytes[%d:%d] (replaced with 25 zero-bytes)", + pos, end, + ) + pos = bytes(all_bytes).find(needle, end) + + # Find the first valid 0e 08 footer marker (v0.14.0). footer_pos = -1 pos = 0 while True: