Files
2026-11-09 12:34:50 +03:30

100 خطوط
4.7 KiB
Python

"""Tiny CFB writer solely for bounded native-format unit fixtures (never application data)."""
import math
import struct
FREE=0xffffffff; END=0xfffffffe; FAT=0xfffffffd
def cfb(streams):
entries=[('Root Entry',5,b'',[])]
storages={}
for name,data in streams.items():
parts=name.split('/'); parent=0
for part in parts[:-1]:
key=(parent,part)
if key not in storages:
storages[key]=len(entries);entries.append((part,1,b'',[]));entries[parent][3].append(storages[key])
parent=storages[key]
entries[parent][3].append(len(entries));entries.append((parts[-1],2,data,[]))
# This fixture deliberately uses normal >=4096-byte streams, with padding INSIDE native records.
assert all(len(e[2])>=4096 for e in entries if e[1]==2)
sectors=[];links=[];starts={}
def allocate(data):
start=len(sectors); count=math.ceil(len(data)/512)
for i in range(count):sectors.append(data[i*512:(i+1)*512].ljust(512,b'\0'));links.append(start+i+1 if i+1<count else END)
return start
for i,e in enumerate(entries):
if e[1]==2:starts[i]=allocate(e[2])
directory=bytearray(math.ceil(len(entries)/4)*512)
for i,(name,kind,data,children) in enumerate(entries):
offset=i*128;encoded=(name+'\0').encode('utf-16le');directory[offset:offset+len(encoded)]=encoded
struct.pack_into('<HBB',directory,offset+64,len(encoded),kind,1)
siblings=[]
for parent in entries:
if i in parent[3]:siblings=parent[3];break
right=siblings[siblings.index(i)+1] if siblings and siblings.index(i)+1<len(siblings) else FREE
struct.pack_into('<III',directory,offset+68,FREE,right,children[0] if children else FREE)
struct.pack_into('<IQ',directory,offset+116,starts.get(i,END),len(data))
dir_start=allocate(directory)
fat_id=len(sectors); assert fat_id<128
links.append(FAT)
fat=struct.pack('<128I',*(links+[FREE]*(128-len(links))))
sectors.append(fat)
header=bytearray(512);header[:8]=bytes.fromhex('d0cf11e0a1b11ae1')
struct.pack_into('<HHHHH',header,24,0x3e,3,0xfffe,9,6)
struct.pack_into('<IIIIIIIII',header,40,0,1,dir_start,0,4096,END,0,END,0)
struct.pack_into('<109I',header,76,fat_id,*([FREE]*108))
return bytes(header)+b''.join(sectors)
def text(props):
data=('|'+'|'.join(f'{k}={v}' for k,v in props.items())+'|_PADDING=').encode()
data=data.ljust(4091,b' ')+b'\0'
return struct.pack('<I',len(data))+data
def record(tag,data):
data=bytes(data).ljust(4091,b'\0')
return bytes([tag])+struct.pack('<I',len(data))+data
def pcb(*, width=100000, x=100000, net='GND', version='5.01', track=True, corrupt=False):
header=b'PCB 6.0 Binary File'
board={'KIND':'Protel_Advanced_PCB','VERSION':version,'LAYER1NEXT':'32','LAYER32PREV':'1',
'VX0':'0mil','VY0':'0mil','VX1':'100mil','VY1':'0mil','VX2':'100mil','VY2':'100mil','VX3':'0mil','VY3':'100mil'}
streams={'FileHeaderSix':(struct.pack('<IB',len(header),len(header))+header).ljust(4096,b'\0'),
'Board6/Data':text(board),'Nets6/Data':text({'NAME':net})}
if track:
p=bytearray(45);p[0]=1
struct.pack_into('<HHH',p,3,0,65535,65535)
struct.pack_into('<iiiii',p,13,x,100000,x+200000,100000,width)
streams['Tracks6/Data']=record(4,p)
if corrupt:streams['Tracks6/Data']=b'\x01'+streams['Tracks6/Data'][1:]
return cfb(streams)
def rich_pcb():
"""Pad stack and blind via records follow the documented V6 field offsets."""
import io
import olefile
with olefile.OleFileIO(io.BytesIO(pcb())) as ole:
streams={'/'.join(e):ole.openstream(e).read() for e in ole.listdir()}
streams['Components6/Data']=text({'SOURCEDESIGNATOR':'U1','PATTERN':'QFN','SOURCEUNIQUEID':'FOOTPRINT_UID',
'X':'10mil','Y':'10mil','LAYER':'TOP','ROTATION':'0'})
p=bytearray(110);p[0]=74
struct.pack_into('<HHH',p,3,0,65535,0)
struct.pack_into('<ii',p,13,100000,100000)
struct.pack_into('<iiiiiii',p,21,100000,200000,150000,250000,200000,300000,50000)
struct.pack_into('<BBB',p,49,1,2,3);p[60]=1
stack=bytearray(3959);stack[262]=2
struct.pack_into('<i',stack,263,100000) # slotted hole
parts=[b'\x011',b'',b'',b'',bytes(p),bytes(stack)]
streams['Pads6/Data']=b'\x02'+b''.join(struct.pack('<I',len(part))+part for part in parts)
v=bytearray(203);v[0]=74
struct.pack_into('<HHH',v,3,0,65535,65535)
struct.pack_into('<iiii',v,13,500000,500000,100000,50000)
v[29]=1;v[30]=32;struct.pack_into('<32i',v,75,*([100000]*31+[200000]))
streams['Vias6/Data']=record(3,v)
streams['UniqueIDPrimitiveInformation/Data']=text({'PRIMITIVEINDEX':'0','PRIMITIVEOBJECTID':'Pad','UNIQUEID':'PAD_UID'})
return cfb(streams)