"""Bounded original Fleet selection/cache bindings; no client invocation."""
from pathlib import Path
import hashlib, importlib.util, json, struct
spec=importlib.util.spec_from_file_location('audit',Path(__file__).with_name('Audit-GalaxyClientCompatibility.py'))
m=importlib.util.module_from_spec(spec);spec.loader.exec_module(m)
old=m.Image(Path('artifacts/ship-model-reference/client-copy/EliteDangerous64.exe'))
new=m.Image(Path('D:/SteamLibrary/steamapps/common/Elite Dangerous/Products/elite-dangerous-odyssey-64/EliteDangerous64.exe'))
functions=[];refs=[]
for before in (0x25BB9B0,0x3A6BB50,0x3A6F3B0,0x25613D0,0x2564BA0,0x2561450,0x255A140,0x25614F0,0x2DAF550,0x8C2AE0):
span=old.function(before);length=span[1]-before if span else 512
row=m.match(old,new,before,length)
if row['status']!='unique':raise ValueError('Unclosed Fleet body '+hex(before)+': '+row['status'])
row['oldRva']=hex(before);functions.append(row)
refs.extend(m.references(old,new,before,int(row['newRva'],16),row['length']))
tables={}
for before in (0x529CC28,0x529CC58,0x529CD58,0x532DE68,0x512AA10):
after={r[1] for r in refs if r[0]==before}
if len(after)!=1:raise ValueError('Unclosed original Fleet vtable '+hex(before))
tables[before]=after.pop()
# The actor table is a caller binding: its original +80/+E0 are the two
# completely matched methods used by the original map's current-ship gate.
actor_old=0x5294D08;slot_matches=[]
for row in functions:
if int(row['oldRva'],16) in (0x2561450,0x255A140):
slot=0x80 if int(row['oldRva'],16)==0x2561450 else 0xE0
needle=struct.pack('<Q',new.base+int(row['newRva'],16));pos=0;candidates=[]
while True:
pos=new.raw.find(needle,pos)
if pos<0:break
candidates.append(new.pe.get_rva_from_offset(pos)-slot);pos+=8
slot_matches.append(set(candidates))
actor_candidates=set.intersection(*slot_matches)
if len(actor_candidates)!=1:raise ValueError('Original actor interface remains ambiguous')
tables[actor_old]=actor_candidates.pop()
slots=[]
for table,offset in ((0x529CD58,0),(0x529CD58,0x20),(0x532DE68,0),(0x532DE68,0x78),(0x512AA10,0),(0x512AA10,0x20),(actor_old,0)):
before=struct.unpack('<Q',old.data(table+offset,8))[0]-old.base
after=struct.unpack('<Q',new.data(tables[table]+offset,8))[0]-new.base
length=next(i.address+i.size-old.base-before for i in old.instructions(before,96) if i.mnemonic=='ret')
raw,mask,*_=m.signature(old,before,length);actual=new.data(after,length)
if any(a!=b for a,b,keep in zip(raw,actual,mask) if keep):raise ValueError('Original Fleet leaf slot changed')
paired=m.references(old,new,before,after,length);refs.extend(paired)
slots.append(dict(oldVtable=hex(table),newVtable=hex(tables[table]),slot=hex(offset),oldRva=hex(before),newRva=hex(after),bytes=length,
oldSha256=hashlib.sha256(raw).hexdigest(),newSha256=hashlib.sha256(actual).hexdigest(),references=[list(map(hex,r)) for r in paired]))
globals={}
for before in (0x5F59D94,0x5F05BA4,0x5F2F24C,0x5F05AC8):
candidates={r[1] for r in refs if r[0]==before}
if len(candidates)!=1:raise ValueError('Original Fleet dynamic ID is not bound')
globals[hex(before)]=hex(candidates.pop())
output=dict(runtimeNeutral=True,canInstallHooks=False,gameCalls=0,networkRequests=0,
oldImage=dict(path=str(old.path),sha256=hashlib.sha256(old.raw).hexdigest()),
newImage=dict(path=str(new.path),sha256=hashlib.sha256(new.raw).hexdigest()),
functions=functions,vtables={hex(a):hex(b) for a,b in tables.items()},slots=slots,globals=globals)
path=Path('artifacts/galaxy-query-research/fleet-mission-sources/compatibility.json');path.write_text(json.dumps(output,indent=2)+'\n')
print(json.dumps(dict(vtables=output['vtables'],globals=globals,functions=len(functions),slots=slots),indent=2))
"""Bounded original Fleet selection/cache bindings; no client invocation."""
from pathlib import Path
import hashlib, importlib.util, json, struct
spec=importlib.util.spec_from_file_location('audit',Path(__file__).with_name('Audit-GalaxyClientCompatibility.py'))
m=importlib.util.module_from_spec(spec);spec.loader.exec_module(m)
old=m.Image(Path('artifacts/ship-model-reference/client-copy/EliteDangerous64.exe'))
new=m.Image(Path('D:/SteamLibrary/steamapps/common/Elite Dangerous/Products/elite-dangerous-odyssey-64/EliteDangerous64.exe'))
functions=[];refs=[]
for before in (0x25BB9B0,0x3A6BB50,0x3A6F3B0,0x25613D0,0x2564BA0,0x2561450,0x255A140,0x25614F0,0x2DAF550,0x8C2AE0):
span=old.function(before);length=span[1]-before if span else 512
row=m.match(old,new,before,length)
if row['status']!='unique':raise ValueError('Unclosed Fleet body '+hex(before)+': '+row['status'])
row['oldRva']=hex(before);functions.append(row)
refs.extend(m.references(old,new,before,int(row['newRva'],16),row['length']))
tables={}
for before in (0x529CC28,0x529CC58,0x529CD58,0x532DE68,0x512AA10):
after={r[1] for r in refs if r[0]==before}
if len(after)!=1:raise ValueError('Unclosed original Fleet vtable '+hex(before))
tables[before]=after.pop()
# The actor table is a caller binding: its original +80/+E0 are the two
# completely matched methods used by the original map's current-ship gate.
actor_old=0x5294D08;slot_matches=[]
for row in functions:
if int(row['oldRva'],16) in (0x2561450,0x255A140):
slot=0x80 if int(row['oldRva'],16)==0x2561450 else 0xE0
needle=struct.pack('<Q',new.base+int(row['newRva'],16));pos=0;candidates=[]
while True:
pos=new.raw.find(needle,pos)
if pos<0:break
candidates.append(new.pe.get_rva_from_offset(pos)-slot);pos+=8
slot_matches.append(set(candidates))
actor_candidates=set.intersection(*slot_matches)
if len(actor_candidates)!=1:raise ValueError('Original actor interface remains ambiguous')
tables[actor_old]=actor_candidates.pop()
slots=[]
for table,offset in ((0x529CD58,0),(0x529CD58,0x20),(0x532DE68,0),(0x532DE68,0x78),(0x512AA10,0),(0x512AA10,0x20),(actor_old,0)):
before=struct.unpack('<Q',old.data(table+offset,8))[0]-old.base
after=struct.unpack('<Q',new.data(tables[table]+offset,8))[0]-new.base
length=next(i.address+i.size-old.base-before for i in old.instructions(before,96) if i.mnemonic=='ret')
raw,mask,*_=m.signature(old,before,length);actual=new.data(after,length)
if any(a!=b for a,b,keep in zip(raw,actual,mask) if keep):raise ValueError('Original Fleet leaf slot changed')
paired=m.references(old,new,before,after,length);refs.extend(paired)
slots.append(dict(oldVtable=hex(table),newVtable=hex(tables[table]),slot=hex(offset),oldRva=hex(before),newRva=hex(after),bytes=length,
oldSha256=hashlib.sha256(raw).hexdigest(),newSha256=hashlib.sha256(actual).hexdigest(),references=[list(map(hex,r)) for r in paired]))
globals={}
for before in (0x5F59D94,0x5F05BA4,0x5F2F24C,0x5F05AC8):
candidates={r[1] for r in refs if r[0]==before}
if len(candidates)!=1:raise ValueError('Original Fleet dynamic ID is not bound')
globals[hex(before)]=hex(candidates.pop())
output=dict(runtimeNeutral=True,canInstallHooks=False,gameCalls=0,networkRequests=0,
oldImage=dict(path=str(old.path),sha256=hashlib.sha256(old.raw).hexdigest()),
newImage=dict(path=str(new.path),sha256=hashlib.sha256(new.raw).hexdigest()),
functions=functions,vtables={hex(a):hex(b) for a,b in tables.items()},slots=slots,globals=globals)
path=Path('artifacts/galaxy-query-research/fleet-mission-sources/compatibility.json');path.write_text(json.dumps(output,indent=2)+'\n')
print(json.dumps(dict(vtables=output['vtables'],globals=globals,functions=len(functions),slots=slots),indent=2))