"""Recover the original GZM quadtree and its Codex labels. No game execution.

3897C10 maps raw 1/32-ly X/Z through the resource's two fixed-point
coefficients. 3897CA0 selects child=(x>=mid)|(z>=mid)<<1; a missing child
uses its parent's value. Do not reuse the density texture's different scale.
"""
from pathlib import Path
import gzip,hashlib,json,math,struct,zlib

directory=Path('artifacts/navigation-0410-qa/resources')
key='d30f11c57448cff8ec3dd0c5c80d9868482ccadb'
f=(directory/(key+'.fres')).read_bytes();b=zlib.decompress(f[1620:])
np=struct.unpack_from('<I',f,0x34)[0];nr=struct.unpack_from('<I',f,0x20)[0]
if (np,nr,len(b))!=(3227,24,82232836):raise ValueError('Unexpected source schema')
cursor=0;grouped=0
while grouped<np:
    kind,count=struct.unpack_from('<HH',b,cursor);cursor+=4;grouped+=count
pools=[struct.unpack_from('<8I',b,cursor+i*32) for i in range(np)];cursor+=np*32
roots=[struct.unpack_from('<4I',b,cursor+i*16) for i in range(nr)];cursor+=nr*16
start=len(b)-sum(p[2] for p in pools)
if start!=13127884 or start-cursor!=814013*16+16:raise ValueError('Invalid relocation boundary')
refs={}
for i in range(814013):
    s,o,t,v=struct.unpack_from('<4I',b,cursor+i*16)
    if s>=np or t>=np or o>=pools[s][2] or v>=pools[t][2] or (s,o) in refs:raise ValueError('Invalid reference')
    refs[s,o]=(t,v)
def at(p,o):
    if p>=np or o<0 or o>=pools[p][2]:raise ValueError('Invalid pool offset')
    return start+pools[p][3]+o
root=next(r for r in roots if r[:2]==(2854198236,193493299));rp,ro=root[2:]
sx,sz,width,height=struct.unpack_from('<4I',b,at(rp,ro))
if (sx,sz,width,height)!=(83,83,2048,2048):raise ValueError('Original zone scale changed')
gp,go=next(r for r in roots if r[:2]==(1765098201,2090718614))[2:]
origin=[v/32 for v in struct.unpack_from('<3i',b,at(gp,go))]
if origin!=[49985,40985,24105]:raise ValueError('Original origin changed')
names=json.loads(Path('artifacts/current-system-incremental/game-text-resources/all-text-private.json').read_text(encoding='utf-8'))
tp,to=refs[rp,ro+24];name_count=struct.unpack_from('<Q',b,at(rp,ro)+32)[0]
if name_count!=43:raise ValueError('Unexpected original name table')
labels=[]
for i in range(name_count):
    ident=struct.unpack_from('<Q',b,at(tp,to+i*16))[0];sp,so=refs[tp,to+i*16+8]
    p=at(sp,so);name=b[p:b.index(0,p)].decode('ascii')
    if ident!=i or name!=('EmptyRegion' if i==0 else str(i)):raise ValueError('Unexpected zone name/id mapping')
    labels.append(names['codex_regionname_'+name] if i else '')
raster=bytearray(width*height);stats={};visited=set()
def leaf(value,x,z,size):
    if value>42:raise ValueError('Invalid original zone ID')
    row=bytes([value])*size
    for yy in range(z,z+size):raster[yy*width+x:yy*width+x+size]=row
    if value:
        area=size*size;s=stats.setdefault(value,[0,0,0,width,height,0,0])
        s[0]+=area;s[1]+=area*(x+size/2);s[2]+=area*(z+size/2)
        s[3]=min(s[3],x);s[4]=min(s[4],z);s[5]=max(s[5],x+size);s[6]=max(s[6],z+size)
def node(p,o,x,z,size,depth):
    if (p,o) in visited or depth>11:raise ValueError('Cyclic or overdeep original tree')
    visited.add((p,o));value=struct.unpack_from('<Q',b,at(p,o))[0]
    children=[refs.get((p,o+8+i*8)) for i in range(4)]
    if not any(children):leaf(value,x,z,size);return
    if size<=1:raise ValueError('Children below source pixel')
    half=size//2
    for i,child in enumerate(children):
        xx=x+(i&1)*half;zz=z+(i>>1)*half
        if child:node(*child,xx,zz,half,depth+1)
        else:leaf(value,xx,zz,half)
node(*refs[rp,ro+16],0,0,width,0)
def world(pixel,axis):return pixel*4096/(sx if axis==0 else sz)-origin[axis]
zones=[dict(id=i,name=labels[i],pixels=s[0],position=dict(x=world(s[1]/s[0],0),y=0,z=world(s[2]/s[0],2)),
            pixelBounds=s[3:]) for i,s in sorted(stats.items())]
out=Path('XFEEDToolBox.Core/Rendering');compressed=gzip.compress(bytes(raster),compresslevel=9,mtime=0)
(out/'original-galactic-zones.bin.gz').write_bytes(compressed)
source=Path('D:/SteamLibrary/steamapps/common/Elite Dangerous/Products/elite-dangerous-odyssey-64/Win64/0d')/key
result=dict(sourceResource=key,sourceSha256=hashlib.sha256(source.read_bytes()).hexdigest(),frozenSha256=hashlib.sha256(b).hexdigest(),
            rasterSha256=hashlib.sha256(raster).hexdigest(),width=width,height=height,scaleX=sx,scaleZ=sz,sourceOrigin=origin,
            coordinateEvidence='Original 3897C10 fixed-point query and 3897CA0 child selection; original galaxy root raw 1/32-ly origin',
            gameProcessAccess=False,generatedStellarRecords=False,zones=zones)
(out/'original-galactic-zones.json').write_text(json.dumps(result,ensure_ascii=False,indent=2)+'\n',encoding='utf-8')
print(json.dumps(dict(zones=len(zones),nodes=len(visited),compressedBytes=len(compressed),rasterSha256=result['rasterSha256'])))
