# Independent read-only parser for the public FileDiver data layout.
# Data and layout provenance: validation/resource-schema.json.
from pathlib import Path
import gzip,struct,json,hashlib
R=Path(__file__).parent
def dlhash(s):
 h=5381
 for c in s.encode():h=(h*33+c)&0xffffffff
 return(h-5381)&0xffffffff
def parse_types(b):
 magic,version,nt,ne,nm,nv,na,nd,ns=struct.unpack_from('<4s8I',b)
 assert nt<10000 and nm<100000 and magic in [b'LTLD',b'DLTL'],(magic,version)
 th=struct.unpack_from('<'+str(nt)+'I',b,36);typesoff=36+nt*4+ne*4;memberoff=typesoff+36*nt+32*ne;stringoff=memberoff+72*nm+16*nv+8*na+nd
 assert stringoff+ns<=len(b)
 def text(i):
  if i==0xffffffff:return ''
  if ns==0:return str(i)
  assert i<ns
  end=b.index(0,stringoff+i,stringoff+ns);return b[stringoff+i:end].decode()
 types={}
 for i,k in enumerate(th):
  name,flags,s32,s64,a32,a64,count,start,comment=struct.unpack_from('<9I',b,typesoff+36*i)
  assert start+count<=nm
  type_size=s64
  ms=[]
  for j in range(start,start+count):
   off=memberoff+72*j
   nam,com,unk,atom,storage,alen,tid,s32,s64,a32,a64,o32,o64,do,ds,fl=struct.unpack_from('<3IBBH10I',b,off)
   ms.append({'name':text(nam),'atom':atom,'storage':storage,'array_len':alen,'type_id':tid,'size':s64,'offset':o64})
  types[k]={'name':text(name),'size':type_size,'members':ms}
 return types

def find_section(b,key):
 marker=struct.pack('<I',key);matches=[];at=0
 while 1:
  at=b.find(marker,at)
  if at<0:break
  if at+28<len(b)and b[at+4:at+8] in [b'LDLD',b'DLDL']and struct.unpack_from('<I',b,at+12)[0]==key:
   size=struct.unpack_from('<I',b,at+16)[0];assert at+28+size<=len(b);matches.append((at,at+28,size))
  at+=1
 assert len(matches)==1,matches
 return matches[0]

