"""Bind the actual graphics-quality selector to ED5A without invoking it."""
import hashlib
import importlib.util
import json
import struct
from pathlib import Path
spec=importlib.util.spec_from_file_location('audit',Path(__file__).with_name('Audit-GalaxyClientCompatibility.py'))
audit=importlib.util.module_from_spec(spec);spec.loader.exec_module(audit)
old=audit.Image(Path('artifacts/ship-model-reference/client-copy/EliteDangerous64.exe'))
new=audit.Image(Path('D:/SteamLibrary/steamapps/common/Elite Dangerous/Products/elite-dangerous-odyssey-64/EliteDangerous64.exe'))
expected=('6891f70a021fa9f3a0e00458c284f48d1226e68ae7fc02ad48081aed47857e93','ed5a8f8cd10f9a80d3bda5badd7ba1d4fcfc15996c8c7e018852cb170e264b63')
if tuple(hashlib.sha256(i.raw).hexdigest() for i in (old,new))!=expected:raise ValueError('Pinned quality-provider image changed')
functions=[];references=[]
for before in (0x27FE600,0x27FC160,0x27FE0C0,0x2806640,0x2897E10):
span=old.function(before)
length=span[1]-before if span else next(i.address+i.size-old.base-before for i in old.instructions(before,128) if i.mnemonic=='ret')
row=audit.match(old,new,before,length)
if row['status']!='unique':
# The generated selector constructor has many identical bodies apart
# from its table address. Bind it through the actual parent ctor call,
# then verify its complete leaf body and paired RIP table operand.
targets={r[1] for r in references if r[0]==before}
if len(targets)!=1:raise ValueError('Ambiguous quality-provider body '+hex(before))
after=targets.pop();raw,mask,*_=audit.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('Paired quality ctor body changed')
row.update(status='unique',newRva=hex(after),method='unique-actual-parent-constructor-call-and-full-body')
row['oldRva']=hex(before);functions.append(row)
references.extend(audit.references(old,new,before,int(row['newRva'],16),length))
tables={}
for before in (0x52EC9B8,0x52EC1E0,0x52EC7C8,0x52F66E8):
matches={r[1] for r in references if r[0]==before}
if len(matches)!=1:raise ValueError('Quality-provider table lacks unique paired constructor reference '+hex(before))
tables[hex(before)]=hex(matches.pop())
getters=[]
for old_table,slot,expected_getter in ((0x52EC9B8,0,0x2840C90),(0x52EC9B8,0xA0,0x2843CD0),(0x52EC1E0,0x10,0x28437D0),(0x52EC7C8,0xB8,0x2841310)):
new_table=int(tables[hex(old_table)],16)
before=struct.unpack('<Q',old.data(old_table+slot,8))[0]-old.base
after=struct.unpack('<Q',new.data(new_table+slot,8))[0]-new.base
if before!=expected_getter:raise ValueError('Actual original quality-provider getter slot changed')
length=next(i.address+i.size-old.base-before for i in old.instructions(before,128) if i.mnemonic=='ret')
raw,mask,*_=audit.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('Quality-provider getter body changed')
refs=audit.references(old,new,before,after,length);references.extend(refs)
getters.append(dict(oldVtable=hex(old_table),newVtable=hex(new_table),slot=hex(slot),oldRva=hex(before),newRva=hex(after),bytes=length,
normalizedIdentical=True,oldSha256=hashlib.sha256(raw).hexdigest(),newSha256=hashlib.sha256(actual).hexdigest(),references=[list(map(hex,r)) for r in refs]))
global_pairs={r[1] for r in references if r[0]==0x5F05BF4}
if len(global_pairs)!=1:raise ValueError('Quality-provider runtime type global is not bound')
result=dict(runtimeNeutral=True,canInstallHooks=False,runtimeValidated=False,gameCalls=0,networkRequests=0,
oldImageSha256=expected[0],newImageSha256=expected[1],functions=functions,vtables=tables,getters=getters,
globals={'0x5f05bf4':hex(global_pairs.pop())},references=[list(map(hex,r)) for r in references],
fieldContract=dict(interfaceComponentOffset=0x950,selectorFromInterface=-0x310,controllerFromInterface=0x880,
ownerFromController=0x248,embeddedInterfaceFromOwner=0x60,selectorFromEmbedded=0x5E0,
currentIndex=8,count=0x20,pointerVector=0x28,nFromSelectedRecord=0x3C,countLimit=64))
path=Path('artifacts/galaxy-query-research/constellations/quality-provider-compatibility.json')
path.write_text(json.dumps(result,indent=2)+'\n',encoding='utf8')
print(json.dumps(dict(report=str(path),vtables=tables,getters=getters,globals=result['globals']),indent=2))
"""Bind the actual graphics-quality selector to ED5A without invoking it."""
import hashlib
import importlib.util
import json
import struct
from pathlib import Path
spec=importlib.util.spec_from_file_location('audit',Path(__file__).with_name('Audit-GalaxyClientCompatibility.py'))
audit=importlib.util.module_from_spec(spec);spec.loader.exec_module(audit)
old=audit.Image(Path('artifacts/ship-model-reference/client-copy/EliteDangerous64.exe'))
new=audit.Image(Path('D:/SteamLibrary/steamapps/common/Elite Dangerous/Products/elite-dangerous-odyssey-64/EliteDangerous64.exe'))
expected=('6891f70a021fa9f3a0e00458c284f48d1226e68ae7fc02ad48081aed47857e93','ed5a8f8cd10f9a80d3bda5badd7ba1d4fcfc15996c8c7e018852cb170e264b63')
if tuple(hashlib.sha256(i.raw).hexdigest() for i in (old,new))!=expected:raise ValueError('Pinned quality-provider image changed')
functions=[];references=[]
for before in (0x27FE600,0x27FC160,0x27FE0C0,0x2806640,0x2897E10):
span=old.function(before)
length=span[1]-before if span else next(i.address+i.size-old.base-before for i in old.instructions(before,128) if i.mnemonic=='ret')
row=audit.match(old,new,before,length)
if row['status']!='unique':
# The generated selector constructor has many identical bodies apart
# from its table address. Bind it through the actual parent ctor call,
# then verify its complete leaf body and paired RIP table operand.
targets={r[1] for r in references if r[0]==before}
if len(targets)!=1:raise ValueError('Ambiguous quality-provider body '+hex(before))
after=targets.pop();raw,mask,*_=audit.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('Paired quality ctor body changed')
row.update(status='unique',newRva=hex(after),method='unique-actual-parent-constructor-call-and-full-body')
row['oldRva']=hex(before);functions.append(row)
references.extend(audit.references(old,new,before,int(row['newRva'],16),length))
tables={}
for before in (0x52EC9B8,0x52EC1E0,0x52EC7C8,0x52F66E8):
matches={r[1] for r in references if r[0]==before}
if len(matches)!=1:raise ValueError('Quality-provider table lacks unique paired constructor reference '+hex(before))
tables[hex(before)]=hex(matches.pop())
getters=[]
for old_table,slot,expected_getter in ((0x52EC9B8,0,0x2840C90),(0x52EC9B8,0xA0,0x2843CD0),(0x52EC1E0,0x10,0x28437D0),(0x52EC7C8,0xB8,0x2841310)):
new_table=int(tables[hex(old_table)],16)
before=struct.unpack('<Q',old.data(old_table+slot,8))[0]-old.base
after=struct.unpack('<Q',new.data(new_table+slot,8))[0]-new.base
if before!=expected_getter:raise ValueError('Actual original quality-provider getter slot changed')
length=next(i.address+i.size-old.base-before for i in old.instructions(before,128) if i.mnemonic=='ret')
raw,mask,*_=audit.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('Quality-provider getter body changed')
refs=audit.references(old,new,before,after,length);references.extend(refs)
getters.append(dict(oldVtable=hex(old_table),newVtable=hex(new_table),slot=hex(slot),oldRva=hex(before),newRva=hex(after),bytes=length,
normalizedIdentical=True,oldSha256=hashlib.sha256(raw).hexdigest(),newSha256=hashlib.sha256(actual).hexdigest(),references=[list(map(hex,r)) for r in refs]))
global_pairs={r[1] for r in references if r[0]==0x5F05BF4}
if len(global_pairs)!=1:raise ValueError('Quality-provider runtime type global is not bound')
result=dict(runtimeNeutral=True,canInstallHooks=False,runtimeValidated=False,gameCalls=0,networkRequests=0,
oldImageSha256=expected[0],newImageSha256=expected[1],functions=functions,vtables=tables,getters=getters,
globals={'0x5f05bf4':hex(global_pairs.pop())},references=[list(map(hex,r)) for r in references],
fieldContract=dict(interfaceComponentOffset=0x950,selectorFromInterface=-0x310,controllerFromInterface=0x880,
ownerFromController=0x248,embeddedInterfaceFromOwner=0x60,selectorFromEmbedded=0x5E0,
currentIndex=8,count=0x20,pointerVector=0x28,nFromSelectedRecord=0x3C,countLimit=64))
path=Path('artifacts/galaxy-query-research/constellations/quality-provider-compatibility.json')
path.write_text(json.dumps(result,indent=2)+'\n',encoding='utf8')
print(json.dumps(dict(report=str(path),vtables=tables,getters=getters,globals=result['globals']),indent=2))