"""Recover authored region labels and geometry from the installed original GRX.
No game execution or invented stellar records. The coordinate anchor is the
original Core Sys Sector centre; all three axes agree with the original galaxy
data root, and X/Z also agree with the captured galaxy overview.
"""
from pathlib import Path
import hashlib,importlib.util,json,math,struct,sys,zlib
saved=sys.argv;sys.argv=['inventory','artifacts/galaxy-query-research/regions/unused.json']
spec=importlib.util.spec_from_file_location('assets','research/galaxy_asset_inventory.py')
assets=importlib.util.module_from_spec(spec);spec.loader.exec_module(assets);sys.argv=saved
key='6ef5117d31651714add3cbb389f400404565129e';source=assets.ROOT/'06'/key
f=assets.decode(source)
if not f.startswith(b'FRES') or len(f)!=24995:raise ValueError('Unexpected original GRX')
b=zlib.decompress(f[312:])
if len(b)!=76968:raise ValueError('Unexpected original frozen GRX')
np=struct.unpack_from('<I',f,0x34)[0];nr=struct.unpack_from('<I',f,0x20)[0]
if np!=3 or nr!=1 or struct.unpack_from('<I',f,0x38)[0]!=0:raise ValueError('Unexpected original GRX schema')
cursor=0;grouped=0
while grouped<np:
kind,count=struct.unpack_from('<HH',b,cursor);cursor+=4;grouped+=count
if grouped!=np:raise ValueError('Invalid pool grouping')
pools=[struct.unpack_from('<IIIIIIII',b,cursor+i*32) for i in range(np)];cursor+=np*32
roots=[struct.unpack_from('<IIII',b,cursor+i*16) for i in range(nr)];cursor+=nr*16
refs={}
for _ in range(1426):
s,o,t,v=struct.unpack_from('<IIII',b,cursor);cursor+=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 frozen reference')
refs[(s,o)]=(t,v)
start=len(b)-sum(p[2] for p in pools)
if start!=22952 or cursor!=22936:raise ValueError('Unexpected pool-data boundary')
def address(pool,offset):
if pool>=np or offset>=pools[pool][2]:raise ValueError('Reference outside pool')
return start+pools[pool][3]+offset
def text_at(pool,offset):
at=address(pool,offset);end=b.index(b'\0',at)
if end-at>512:raise ValueError('Unexpected label length')
return b[at:end].decode('utf-8')
root_pool,root_offset=roots[0][2:];root=address(root_pool,root_offset)
count=struct.unpack_from('<Q',b,root+8)[0];array_pool,array_offset=refs[(root_pool,root_offset)]
if count!=470 or array_pool!=1 or array_offset!=0:raise ValueError('Unexpected authored-region vector')
rows=[]
for index in range(count):
off=array_offset+index*64;at=address(array_pool,off);name=text_at(*refs[(array_pool,off)])
bounds=struct.unpack_from('<6i',b,at+24);sp,so=refs[(array_pool,off+48)];ns=struct.unpack_from('<Q',b,at+56)[0]
if ns<1 or ns>64 or sp!=2:raise ValueError(f'Invalid region geometry {index} {name!r}: count={ns} pool={sp}')
spheres=[]
for j in range(ns):
sphere=so+j*24;values=struct.unpack_from('<4f',b,address(sp,sphere));suffix=text_at(*refs[(sp,sphere+16)])
if any(not math.isfinite(v) for v in values) or values[3]<=0:raise ValueError('Invalid authored sphere')
spheres.append(dict(sourcePosition=list(values[:3]),radius=values[3],suffix=suffix))
# Some original out-of-galaxy rows have clipped/inverted integer AABBs.
# Preserve that fact; annotation placement uses authored sphere centres.
rows.append(dict(name=name,sourceBounds=list(bounds),boundsValid=all(bounds[i]<=bounds[i+3] for i in range(3)),spheres=spheres))
core=next(r for r in rows if r['name']=='Core Sys Sector');origin=core['spheres'][0]['sourcePosition']
if origin!=[49985.0,40985.0,24105.0] or core['spheres'][0]['radius']!=50:raise ValueError('Original origin anchor changed')
galaxy_key='d30f11c57448cff8ec3dd0c5c80d9868482ccadb';galaxy_source=assets.ROOT/'0d'/galaxy_key
gf=assets.decode(galaxy_source);gb=zlib.decompress(gf[1620:])
gnp=struct.unpack_from('<I',gf,0x34)[0];gnr=struct.unpack_from('<I',gf,0x20)[0]
if (gnp,gnr,len(gb))!=(3227,24,82232836):raise ValueError('Unexpected original galaxy schema')
gc=0;grouped=0
while grouped<gnp:
kind,count=struct.unpack_from('<HH',gb,gc);gc+=4;grouped+=count
gp=[struct.unpack_from('<8I',gb,gc+i*32) for i in range(gnp)];gc+=gnp*32
gr=[struct.unpack_from('<4I',gb,gc+i*16) for i in range(gnr)]
galaxy_root=next(r for r in gr if r[:2]==(1765098201,2090718614))
gstart=len(gb)-sum(p[2] for p in gp);gpool,goff=galaxy_root[2:]
root_origin=[v/32 for v in struct.unpack_from('<3i',gb,gstart+gp[gpool][3]+goff)]
if root_origin!=origin:raise ValueError('Authored region and original galaxy root origins disagree')
for row in rows:
row['minimum']=dict(zip(('x','y','z'),(row['sourceBounds'][i]/32-origin[i] for i in range(3))))
row['maximum']=dict(zip(('x','y','z'),(row['sourceBounds'][i+3]/32-origin[i] for i in range(3))))
for sphere in row['spheres']:
sphere['position']=dict(zip(('x','y','z'),(sphere['sourcePosition'][i]-origin[i] for i in range(3))))
result=dict(sourceResource=key,sourceSha256=hashlib.sha256(source.read_bytes()).hexdigest(),
frozenSha256=hashlib.sha256(b).hexdigest(),deflateOffset=312,sourceOrigin=origin,
coordinateAnchor='All three axes match original galaxy data root and Core Sys Sector authored centre; X/Z also match captured overview',
originResource=galaxy_key,originSourceSha256=hashlib.sha256(galaxy_source.read_bytes()).hexdigest(),
gameProcessAccess=False,generatedStellarRecords=False,regions=rows)
out=Path('XFEEDToolBox.Core/Rendering/original-region-names.json')
out.write_text(json.dumps(result,ensure_ascii=False,indent=2)+'\n',encoding='utf-8')
print(json.dumps(dict(regions=len(rows),spheres=sum(len(r['spheres']) for r in rows),sha256=hashlib.sha256(out.read_bytes()).hexdigest(),sourceSha256=result['sourceSha256'])))
"""Recover authored region labels and geometry from the installed original GRX.
No game execution or invented stellar records. The coordinate anchor is the
original Core Sys Sector centre; all three axes agree with the original galaxy
data root, and X/Z also agree with the captured galaxy overview.
"""
from pathlib import Path
import hashlib,importlib.util,json,math,struct,sys,zlib
saved=sys.argv;sys.argv=['inventory','artifacts/galaxy-query-research/regions/unused.json']
spec=importlib.util.spec_from_file_location('assets','research/galaxy_asset_inventory.py')
assets=importlib.util.module_from_spec(spec);spec.loader.exec_module(assets);sys.argv=saved
key='6ef5117d31651714add3cbb389f400404565129e';source=assets.ROOT/'06'/key
f=assets.decode(source)
if not f.startswith(b'FRES') or len(f)!=24995:raise ValueError('Unexpected original GRX')
b=zlib.decompress(f[312:])
if len(b)!=76968:raise ValueError('Unexpected original frozen GRX')
np=struct.unpack_from('<I',f,0x34)[0];nr=struct.unpack_from('<I',f,0x20)[0]
if np!=3 or nr!=1 or struct.unpack_from('<I',f,0x38)[0]!=0:raise ValueError('Unexpected original GRX schema')
cursor=0;grouped=0
while grouped<np:
kind,count=struct.unpack_from('<HH',b,cursor);cursor+=4;grouped+=count
if grouped!=np:raise ValueError('Invalid pool grouping')
pools=[struct.unpack_from('<IIIIIIII',b,cursor+i*32) for i in range(np)];cursor+=np*32
roots=[struct.unpack_from('<IIII',b,cursor+i*16) for i in range(nr)];cursor+=nr*16
refs={}
for _ in range(1426):
s,o,t,v=struct.unpack_from('<IIII',b,cursor);cursor+=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 frozen reference')
refs[(s,o)]=(t,v)
start=len(b)-sum(p[2] for p in pools)
if start!=22952 or cursor!=22936:raise ValueError('Unexpected pool-data boundary')
def address(pool,offset):
if pool>=np or offset>=pools[pool][2]:raise ValueError('Reference outside pool')
return start+pools[pool][3]+offset
def text_at(pool,offset):
at=address(pool,offset);end=b.index(b'\0',at)
if end-at>512:raise ValueError('Unexpected label length')
return b[at:end].decode('utf-8')
root_pool,root_offset=roots[0][2:];root=address(root_pool,root_offset)
count=struct.unpack_from('<Q',b,root+8)[0];array_pool,array_offset=refs[(root_pool,root_offset)]
if count!=470 or array_pool!=1 or array_offset!=0:raise ValueError('Unexpected authored-region vector')
rows=[]
for index in range(count):
off=array_offset+index*64;at=address(array_pool,off);name=text_at(*refs[(array_pool,off)])
bounds=struct.unpack_from('<6i',b,at+24);sp,so=refs[(array_pool,off+48)];ns=struct.unpack_from('<Q',b,at+56)[0]
if ns<1 or ns>64 or sp!=2:raise ValueError(f'Invalid region geometry {index} {name!r}: count={ns} pool={sp}')
spheres=[]
for j in range(ns):
sphere=so+j*24;values=struct.unpack_from('<4f',b,address(sp,sphere));suffix=text_at(*refs[(sp,sphere+16)])
if any(not math.isfinite(v) for v in values) or values[3]<=0:raise ValueError('Invalid authored sphere')
spheres.append(dict(sourcePosition=list(values[:3]),radius=values[3],suffix=suffix))
# Some original out-of-galaxy rows have clipped/inverted integer AABBs.
# Preserve that fact; annotation placement uses authored sphere centres.
rows.append(dict(name=name,sourceBounds=list(bounds),boundsValid=all(bounds[i]<=bounds[i+3] for i in range(3)),spheres=spheres))
core=next(r for r in rows if r['name']=='Core Sys Sector');origin=core['spheres'][0]['sourcePosition']
if origin!=[49985.0,40985.0,24105.0] or core['spheres'][0]['radius']!=50:raise ValueError('Original origin anchor changed')
galaxy_key='d30f11c57448cff8ec3dd0c5c80d9868482ccadb';galaxy_source=assets.ROOT/'0d'/galaxy_key
gf=assets.decode(galaxy_source);gb=zlib.decompress(gf[1620:])
gnp=struct.unpack_from('<I',gf,0x34)[0];gnr=struct.unpack_from('<I',gf,0x20)[0]
if (gnp,gnr,len(gb))!=(3227,24,82232836):raise ValueError('Unexpected original galaxy schema')
gc=0;grouped=0
while grouped<gnp:
kind,count=struct.unpack_from('<HH',gb,gc);gc+=4;grouped+=count
gp=[struct.unpack_from('<8I',gb,gc+i*32) for i in range(gnp)];gc+=gnp*32
gr=[struct.unpack_from('<4I',gb,gc+i*16) for i in range(gnr)]
galaxy_root=next(r for r in gr if r[:2]==(1765098201,2090718614))
gstart=len(gb)-sum(p[2] for p in gp);gpool,goff=galaxy_root[2:]
root_origin=[v/32 for v in struct.unpack_from('<3i',gb,gstart+gp[gpool][3]+goff)]
if root_origin!=origin:raise ValueError('Authored region and original galaxy root origins disagree')
for row in rows:
row['minimum']=dict(zip(('x','y','z'),(row['sourceBounds'][i]/32-origin[i] for i in range(3))))
row['maximum']=dict(zip(('x','y','z'),(row['sourceBounds'][i+3]/32-origin[i] for i in range(3))))
for sphere in row['spheres']:
sphere['position']=dict(zip(('x','y','z'),(sphere['sourcePosition'][i]-origin[i] for i in range(3))))
result=dict(sourceResource=key,sourceSha256=hashlib.sha256(source.read_bytes()).hexdigest(),
frozenSha256=hashlib.sha256(b).hexdigest(),deflateOffset=312,sourceOrigin=origin,
coordinateAnchor='All three axes match original galaxy data root and Core Sys Sector authored centre; X/Z also match captured overview',
originResource=galaxy_key,originSourceSha256=hashlib.sha256(galaxy_source.read_bytes()).hexdigest(),
gameProcessAccess=False,generatedStellarRecords=False,regions=rows)
out=Path('XFEEDToolBox.Core/Rendering/original-region-names.json')
out.write_text(json.dumps(result,ensure_ascii=False,indent=2)+'\n',encoding='utf-8')
print(json.dumps(dict(regions=len(rows),spheres=sum(len(r['spheres']) for r in rows),sha256=hashlib.sha256(out.read_bytes()).hexdigest(),sourceSha256=result['sourceSha256'])))