fix: prevent data leakage between pages by splitting on row sequence reversal
All checks were successful
Build Linux / Build Linux (push) Successful in 1m31s
Build Windows / Build Windows (push) Successful in 4m47s

This commit is contained in:
2026-02-21 12:17:14 +01:00
parent 62dc11fd40
commit fe4253c8df
2 changed files with 35 additions and 25 deletions

View File

@@ -34,6 +34,10 @@ def load_t42(file_path: str, progress_callback: Optional[Callable[[int, int], No
total_packets = (total_bytes - best_offset) // 42 total_packets = (total_bytes - best_offset) // 42
processed_packets = 0 processed_packets = 0
# Track current active page for each magazine to handle interleaved packets
current_pages_by_mag = {} # mag -> Page object
last_row_by_mag = {} # mag -> int
with open(file_path, 'rb') as f: with open(file_path, 'rb') as f:
f.seek(best_offset) f.seek(best_offset)
while True: while True:
@@ -50,42 +54,45 @@ def load_t42(file_path: str, progress_callback: Optional[Callable[[int, int], No
packet = Packet(chunk) packet = Packet(chunk)
service.all_packets.append(packet) service.all_packets.append(packet)
mag = packet.magazine
row = packet.row
# Logic to group into pages. # Logic to group into pages.
if packet.row == 0: if row == 0:
# Start of a new page header. # Start of a new page header.
# Byte 2-9 of header contain Page Number, Subcode, Control bits etc.
# We need to parse the header to identify the page.
# Header format (after Mag/Row):
# Bytes: P1 P2 S1 S2 S3 S4 C1 C2 ...
# All Hamming 8/4 encoded.
# For now, let's just create a new page entry for every Header we see,
# or find the existing one if we want to support updates (but T42 usually is a stream capture).
# If it's an editor file, it's likely sequential.
p_num, sub_code, language = parse_header(packet.data) p_num, sub_code, language = parse_header(packet.data)
# Create new page # Create new page
new_page = Page(magazine=packet.magazine, page_number=p_num, sub_code=sub_code, language=language) new_page = Page(magazine=mag, page_number=p_num, sub_code=sub_code, language=language)
new_page.packets.append(packet) new_page.packets.append(packet)
service.pages.append(new_page) service.pages.append(new_page)
# Update tracking
current_pages_by_mag[mag] = new_page
last_row_by_mag[mag] = 0
else: else:
# Add to the "current" page of this magazine. # Add to the "current" page of this magazine.
# We need to track the current active page for each magazine. target_page = current_pages_by_mag.get(mag)
# A simplistic approach: add to the last page added that matches the magazine ?? prev_row = last_row_by_mag.get(mag, -1)
# Robust approach: Maintain a dict of current_pages_by_magazine.
# Let's find the last page in service that matches the packet's magazine # Robustness check for VHS captures:
# This is O(N) but N (pages) is small. # If we see a row number that has already passed (e.g. Row 1 after Row 25)
target_page = None # AND we didn't see a Row 0, it means a new page started but we missed the header.
for p in reversed(service.pages): # We should split into a new Page object to avoid data corruption.
if p.magazine == packet.magazine: if target_page and row <= prev_row and row != prev_row: # Strictly less than (or handle duplicate rows?)
target_page = p # In some captures, we might see the same row twice (Field 1/2).
break # If it's the SAME row number, we just append (overwrites in renderer).
# If it's a LOWER row number, it's definitely a new cycle.
# Create a "Lost Header" page
# We use page_number=0xFF to indicate unknown, but we keep mag.
target_page = Page(magazine=mag, page_number=0xFF, sub_code=0, language=0)
service.pages.append(target_page)
current_pages_by_mag[mag] = target_page
if target_page: if target_page:
target_page.packets.append(packet) target_page.packets.append(packet)
last_row_by_mag[mag] = row
else: else:
# Packet without a header? Orphaned. Just keep in all_packets # Packet without a header? Orphaned. Just keep in all_packets
pass pass

View File

@@ -870,6 +870,9 @@ class MainWindow(QMainWindow):
for mag, pnum in sorted_keys: for mag, pnum in sorted_keys:
# Display as Hex # Display as Hex
if pnum == 0xFF:
label = f"{mag}?? (Lost Header)"
else:
label = f"{mag}{pnum:02X}" label = f"{mag}{pnum:02X}"
item = QListWidgetItem(label) item = QListWidgetItem(label)
item.setData(Qt.ItemDataRole.UserRole, (mag, pnum)) item.setData(Qt.ItemDataRole.UserRole, (mag, pnum))