29 lines
1.6 KiB
Python
29 lines
1.6 KiB
Python
#!/usr/bin/env python3
|
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
from __future__ import annotations
|
|
import struct
|
|
MAGIC=b"CHM10DS1";FRAME=struct.Struct("<8sIIiiIIQ24s");NAMES=("app.pbm","app.json","app.xml","app.crc");MAX_FILE=4096;MAX_WIRE=16768
|
|
class ProtocolError(ValueError):pass
|
|
def frame(raw):
|
|
if len(raw)!=64:raise ProtocolError("frame size")
|
|
v=FRAME.unpack(raw)
|
|
if v[0]!=MAGIC or v[1]!=1 or v[8]!=bytes(24):raise ProtocolError("frame identity")
|
|
return v
|
|
def parse_stream(raw:bytes)->dict[str,bytes]:
|
|
if not isinstance(raw,bytes) or not 128<=len(raw)<=MAX_WIRE:raise ProtocolError("stream length")
|
|
first=frame(raw[:64]);kind,status,saved,index,length,total=first[2:8]
|
|
if kind==4:raise ProtocolError(f"target error {status}:{saved}")
|
|
if (kind,status,saved,index,length,total)!=(1,0,0,4,0,0):raise ProtocolError("header invalid")
|
|
offset=64;files={};transferred=0
|
|
for expected,name in enumerate(NAMES):
|
|
if offset+64>len(raw):raise ProtocolError("file frame truncated")
|
|
value=frame(raw[offset:offset+64]);offset+=64;kind,status,saved,index,length,total=value[2:8]
|
|
if kind==4:raise ProtocolError(f"target error {status}:{saved}")
|
|
if kind!=2 or status or saved or index!=expected or length>MAX_FILE or total!=transferred:raise ProtocolError("file frame invalid")
|
|
if offset+length>len(raw):raise ProtocolError("file truncated")
|
|
files[name]=raw[offset:offset+length];offset+=length;transferred+=length
|
|
if offset+64!=len(raw):raise ProtocolError("terminal position")
|
|
value=frame(raw[offset:]);kind,status,saved,index,length,total=value[2:8]
|
|
if (kind,status,saved,index,length,total)!=(3,0,0,4,0,transferred):raise ProtocolError("terminal invalid")
|
|
return files
|