from pathlib import Path
import base64,struct,zlib,json,hashlib,datetime
P=Path(__file__).parent
rows=[]
for name in ['valid.png','crc-only-corrupt.png']:
 data=(P/name).read_bytes();encoded=base64.b64encode(data);decoded=base64.b64decode(encoded,validate=True)
 chunks=[];pos=8
 while pos<len(data):
  n=struct.unpack_from('>I',data,pos)[0];kind=data[pos+4:pos+8];body=data[pos+8:pos+8+n];stored=struct.unpack_from('>I',data,pos+8+n)[0];actual=zlib.crc32(kind+body)&0xffffffff
  chunks.append(dict(type=kind.decode(),offset=pos,length=n,stored=f'{stored:08x}',computed=f'{actual:08x}',matches=stored==actual));pos+=n+12
 rows.append(dict(file=name,bytes=len(data),base64_characters=len(encoded),strict_roundtrip=decoded==data,sha256=hashlib.sha256(data).hexdigest(),chunks=chunks))
a=(P/'valid.png').read_bytes();b=(P/'crc-only-corrupt.png').read_bytes();diff=[i for i,(x,y) in enumerate(zip(a,b)) if x!=y]
assert len(a)==len(b)==69 and diff==[53]
assert a[41:53]==b[41:53]
assert all(c['matches'] for c in rows[0]['chunks'])
assert [c['type'] for c in rows[1]['chunks'] if not c['matches']]==['IDAT']
report=dict(at=datetime.datetime.now().astimezone().isoformat(),changed_offsets=diff,idat_payload_equal=a[41:53]==b[41:53],scanline_hex=zlib.decompress(a[41:53]).hex(),rows=rows)
(P/'measurements.json').write_text(json.dumps(report,indent=2))
print(json.dumps(report,indent=2))
