"""Pin current route-controller slots and settings array reads. No game access."""
import importlib.util,json,struct,hashlib
from pathlib import Path
spec=importlib.util.spec_from_file_location('audit',Path(__file__).with_name('Audit-GalaxyClientCompatibility.py'))
a=importlib.util.module_from_spec(spec);spec.loader.exec_module(a)
old=a.Image(Path('artifacts/ship-model-reference/client-copy/EliteDangerous64.exe'))
new=a.Image(Path('D:/SteamLibrary/steamapps/common/Elite Dangerous/Products/elite-dangerous-odyssey-64/EliteDangerous64.exe'))
assert hashlib.sha256(old.raw).hexdigest()=='6891f70a021fa9f3a0e00458c284f48d1226e68ae7fc02ad48081aed47857e93'
assert hashlib.sha256(new.raw).hexdigest()=='ed5a8f8cd10f9a80d3bda5badd7ba1d4fcfc15996c8c7e018852cb170e264b63'
rows=[];mapped={}
targets=[('routeMode',0x3a6af50,None),('jetCone',0x3a6b040,None),('boostLevel',0x3a6aeb0,None),('cargoMass',0x3a6ae20,None),('mainConstructor',0x3a05660,None),('settingsIntegerRead',0x2601c20,0x60),('settingsIntegerWrite',0x2646820,0x30)]
for name,rva,length in targets:
if length is None:
fn=old.function(rva);assert fn and fn[0]==rva;length=fn[1]-rva
row=dict(name=name,oldRva=hex(rva),**a.match(old,new,rva,length));assert row['status']=='unique',row
rows.append(row);mapped[rva]=int(row['newRva'],16)
refs={before:after for before,after,_,at in a.references(old,new,0x3a05660,mapped[0x3a05660],next(r['length'] for r in rows if r['name']=='mainConstructor'))}
vtables={hex(v):hex(refs[v]) for v in [0x551fec8,0x551fff0]}
slots=[]
for offset,rva in [(0xf0,0x3a6af50),(0xf8,0x3a6b040),(0x100,0x3a6aeb0),(0x108,0x3a6ae20)]:
actual=struct.unpack('<Q',new.data(refs[0x551fff0]+offset,8))[0]-new.base;assert actual==mapped[rva]
slots.append(dict(oldRva=hex(rva),newRva=hex(actual),oldVtable='0x551fff0',newVtable=hex(refs[0x551fff0]),slot=hex(offset)))
type_old=struct.unpack('<Q',old.data(0x551fec8,8))[0]-old.base
type_new=struct.unpack('<Q',new.data(refs[0x551fec8],8))[0]-new.base
before=old.instructions(type_old,32);after=new.instructions(type_new,32)
old_global=type_old+before[0].size+before[0].operands[1].mem.disp
new_global=type_new+after[0].size+after[0].operands[1].mem.disp
assert old_global==0x5fdb224 and new_global==0x5fdb224
assert old.data(type_old,2)==new.data(type_new,2) and old.data(type_old+6,6)==new.data(type_new+6,6)
slots.append(dict(oldRva=hex(type_old),newRva=hex(type_new),oldVtable='0x551fec8',newVtable=hex(refs[0x551fec8]),slot='0x0',bytes=12))
result=dict(runtimeNeutral=True,gameProcessAccess=False,gameCalls=0,runtimeValidated=False,canInstallHooks=False,
oldImageSha256=hashlib.sha256(old.raw).hexdigest(),newImageSha256=hashlib.sha256(new.raw).hexdigest(),
functions=rows,vtables=vtables,slots=slots,globals={'0x5fdb224':'0x5fdb224'},
abi=dict(routeMode='EDX int 0/1/2',jetCone='DL bool',boostLevel='XMM1 float rounded by original F01C10 then uint<4',cargoMass='XMM1 float'),
sourceFields=dict(mode='mainInterface+5AC',cargoMass='mainInterface+5B0',boostEnabled='mainInterface+5B4',boostLevel='mainInterface+5B8',settings='mainInterface+1B8',settingsInteger='ready+5F8=2, index+560<count+530, rows+538, stride248, integer array+54, jet key29'),
originalUi='current map-AS GMRouteSettingsComponent, constants0/1/2 and four exact callbacks')
out=Path('artifacts/galaxy-query-research/route-settings/compatibility.json');out.write_text(json.dumps(result,indent=2)+'\n',encoding='utf-8')
print(json.dumps(dict(functions=[{k:r[k] for k in ['name','oldRva','newRva','length']} for r in rows],vtables=vtables,slots=slots),indent=2))
"""Pin current route-controller slots and settings array reads. No game access."""
import importlib.util,json,struct,hashlib
from pathlib import Path
spec=importlib.util.spec_from_file_location('audit',Path(__file__).with_name('Audit-GalaxyClientCompatibility.py'))
a=importlib.util.module_from_spec(spec);spec.loader.exec_module(a)
old=a.Image(Path('artifacts/ship-model-reference/client-copy/EliteDangerous64.exe'))
new=a.Image(Path('D:/SteamLibrary/steamapps/common/Elite Dangerous/Products/elite-dangerous-odyssey-64/EliteDangerous64.exe'))
assert hashlib.sha256(old.raw).hexdigest()=='6891f70a021fa9f3a0e00458c284f48d1226e68ae7fc02ad48081aed47857e93'
assert hashlib.sha256(new.raw).hexdigest()=='ed5a8f8cd10f9a80d3bda5badd7ba1d4fcfc15996c8c7e018852cb170e264b63'
rows=[];mapped={}
targets=[('routeMode',0x3a6af50,None),('jetCone',0x3a6b040,None),('boostLevel',0x3a6aeb0,None),('cargoMass',0x3a6ae20,None),('mainConstructor',0x3a05660,None),('settingsIntegerRead',0x2601c20,0x60),('settingsIntegerWrite',0x2646820,0x30)]
for name,rva,length in targets:
if length is None:
fn=old.function(rva);assert fn and fn[0]==rva;length=fn[1]-rva
row=dict(name=name,oldRva=hex(rva),**a.match(old,new,rva,length));assert row['status']=='unique',row
rows.append(row);mapped[rva]=int(row['newRva'],16)
refs={before:after for before,after,_,at in a.references(old,new,0x3a05660,mapped[0x3a05660],next(r['length'] for r in rows if r['name']=='mainConstructor'))}
vtables={hex(v):hex(refs[v]) for v in [0x551fec8,0x551fff0]}
slots=[]
for offset,rva in [(0xf0,0x3a6af50),(0xf8,0x3a6b040),(0x100,0x3a6aeb0),(0x108,0x3a6ae20)]:
actual=struct.unpack('<Q',new.data(refs[0x551fff0]+offset,8))[0]-new.base;assert actual==mapped[rva]
slots.append(dict(oldRva=hex(rva),newRva=hex(actual),oldVtable='0x551fff0',newVtable=hex(refs[0x551fff0]),slot=hex(offset)))
type_old=struct.unpack('<Q',old.data(0x551fec8,8))[0]-old.base
type_new=struct.unpack('<Q',new.data(refs[0x551fec8],8))[0]-new.base
before=old.instructions(type_old,32);after=new.instructions(type_new,32)
old_global=type_old+before[0].size+before[0].operands[1].mem.disp
new_global=type_new+after[0].size+after[0].operands[1].mem.disp
assert old_global==0x5fdb224 and new_global==0x5fdb224
assert old.data(type_old,2)==new.data(type_new,2) and old.data(type_old+6,6)==new.data(type_new+6,6)
slots.append(dict(oldRva=hex(type_old),newRva=hex(type_new),oldVtable='0x551fec8',newVtable=hex(refs[0x551fec8]),slot='0x0',bytes=12))
result=dict(runtimeNeutral=True,gameProcessAccess=False,gameCalls=0,runtimeValidated=False,canInstallHooks=False,
oldImageSha256=hashlib.sha256(old.raw).hexdigest(),newImageSha256=hashlib.sha256(new.raw).hexdigest(),
functions=rows,vtables=vtables,slots=slots,globals={'0x5fdb224':'0x5fdb224'},
abi=dict(routeMode='EDX int 0/1/2',jetCone='DL bool',boostLevel='XMM1 float rounded by original F01C10 then uint<4',cargoMass='XMM1 float'),
sourceFields=dict(mode='mainInterface+5AC',cargoMass='mainInterface+5B0',boostEnabled='mainInterface+5B4',boostLevel='mainInterface+5B8',settings='mainInterface+1B8',settingsInteger='ready+5F8=2, index+560<count+530, rows+538, stride248, integer array+54, jet key29'),
originalUi='current map-AS GMRouteSettingsComponent, constants0/1/2 and four exact callbacks')
out=Path('artifacts/galaxy-query-research/route-settings/compatibility.json');out.write_text(json.dumps(result,indent=2)+'\n',encoding='utf-8')
print(json.dumps(dict(functions=[{k:r[k] for k in ['name','oldRva','newRva','length']} for r in rows],vtables=vtables,slots=slots),indent=2))