"""Offline proof of the original ready/missing-row classification defaults.
This reads two pinned PE files only. It neither creates original descriptors nor
calls an original readiness, request or loader function.
"""
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)
images=[audit.Image(Path(path)) for path in (
'artifacts/ship-model-reference/client-copy/EliteDangerous64.exe',
'D:/SteamLibrary/steamapps/common/Elite Dangerous/Products/elite-dangerous-odyssey-64/EliteDangerous64.exe')]
expected=['6891f70a021fa9f3a0e00458c284f48d1226e68ae7fc02ad48081aed47857e93',
'ed5a8f8cd10f9a80d3bda5badd7ba1d4fcfc15996c8c7e018852cb170e264b63']
if [hashlib.sha256(i.raw).hexdigest() for i in images]!=expected:raise ValueError('Metadata proof image changed')
old,new=images
functions=[]
for rva in [0x39BED80,0x39C9C50,0x39248D0,0x3911CA0,0x391E350,0x3B26180,0x3B25D40,0x3B262E0,0x3B27FA0,0x3B26030,0x1C40B90]:
span=old.function(rva)
if span is not None and span[0]!=rva:raise ValueError('Exact metadata function range missing: '+hex(rva))
if span is None:
# These two original leaf functions have no unwind entry. Bound their
# entire bodies by the first decoded INT3, requiring a preceding RET;
# the complete leaf must be byte-identical after the unique prefix.
decoded=old.instructions(rva,1024)
boundary=next(i for i,item in enumerate(decoded) if item.mnemonic=='int3')
if boundary==0 or decoded[boundary-1].mnemonic!='ret':raise ValueError('Leaf boundary changed')
length=decoded[boundary].address-old.base-rva
match=audit.match(old,new,rva,96)
if match['status']!='unique':raise ValueError('Leaf prefix is not unique')
after=int(match['newRva'],16)
if old.data(rva,length)!=new.data(after,length):raise ValueError('Complete leaf differs')
match.update(fullLeafBytes=length,fullLeafSha256=hashlib.sha256(old.data(rva,length)).hexdigest())
else:match=audit.match(old,new,rva,span[1]-rva)
if match['status']!='unique':raise ValueError('Metadata function no longer uniquely matched: '+hex(rva))
functions.append(dict(oldRva=hex(rva),**match))
def pointer(image,at):return struct.unpack('<Q',image.data(at,8))[0]-image.base
slots=[]
for name,tables,slot,targets in [
('lifecycleReady',(0x551A1E0,0x551A1B0),0x18,(0x39C9CD0,0x39C9C30)),
('completeClassificationReady',(0x551A1E0,0x551A1B0),0x20,(0x39C9C50,0x39C9BB0)),
('lookup',(0x551A1E0,0x551A1B0),0x28,(0x39BED80,0x39BECE0)),
('createDefaultNotCalled',(0x551A260,0x551A230),0x20,(0x39248D0,0x3924830)),
('markClassificationReadyNotCalled',(0x551A260,0x551A230),0x28,(0x39DD110,0x39DD070))]:
if [pointer(im,vt+slot) for im,vt in zip(images,tables)]!=list(targets):raise ValueError('Actual metadata slot changed: '+name)
slots.append(dict(name=name,slot=hex(slot),oldVtable=hex(tables[0]),newVtable=hex(tables[1]),
oldTarget=hex(targets[0]),newTarget=hex(targets[1])))
if old.data(0x39C9CD0,5)!=bytes.fromhex('0fb641e8c3') or new.data(0x39C9C30,5)!=old.data(0x39C9CD0,5):
raise ValueError('Lifecycle readiness leaf changed')
if old.data(0x39DD110,8)!=bytes.fromhex('c6810001000001c3') or new.data(0x39DD070,8)!=old.data(0x39DD110,8):
raise ValueError('Classification readiness writer changed')
folder=Path('artifacts/galaxy-query-research/metadata-loading')
evidence={}
for file in [folder/'decompiled/391e350.c',folder/'decompiled/39c5120.c',
Path('artifacts/galaxy-query-research/palette-resources/decompiled/solo-3b26180.c'),
Path('artifacts/galaxy-query-research/filter-enums/decompiled/solo-3b27fa0.c'),
folder/'assembly/asm-full-3b25d40.txt',folder/'assembly/asm-full-3b262e0.txt',
folder/'assembly/asm-full-3b26030.txt',folder/'assembly/asm-full-1c40b90.txt']:
evidence[str(file)]=hashlib.sha256(file.read_bytes()).hexdigest()
report=dict(runtimeNeutral=True,gameCalls=0,networkRequests=0,descriptorAllocations=0,canInstallHooks=False,
oldImageSha256=expected[0],newImageSha256=expected[1],functions=functions,actualTypedSlots=slots,evidenceSha256=evidence,
readiness=dict(interfaceOffset='component+60',lifecycleByte='interface-18 = component+48',
classificationReadyByte='interface+108 = component+168',requireBothNonzero=True),
lookup=dict(primary='interface+A8',secondary='interface+C8',nodeValue='node+10',
confirmedMissing='Two valid stable map lookups, repeated after both ready gates; changed/corrupt maps stay unknown'),
originalEffectiveDefaults=dict(population='0',governmentId=1,primaryEconomyId=1,secondaryEconomyId=1,
factionStateId=0,allegianceId='18446744073709551615',allegianceKey='None',securityClass=0,securityKey='Anarchy'),
limits=['defaulted=true describes original classifier fallback, not a measured galaxy-wide population fact.',
'A missing row before both readiness gates is unknown; no default or original mutating getter is invoked.',
'Observed rows win over defaults and retain defaulted=false; future enum values remain raw/unknown.',
'39C5120 loads configured original local assets; this implementation does not request or accelerate remote metadata.'])
path=folder/'original-missing-defaults-evidence.json';path.write_text(json.dumps(report,indent=2)+'\n',encoding='utf8')
print(json.dumps(dict(report=str(path),functions=len(functions),typedSlots=len(slots),gameCalls=0),indent=2))
"""Offline proof of the original ready/missing-row classification defaults.
This reads two pinned PE files only. It neither creates original descriptors nor
calls an original readiness, request or loader function.
"""
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)
images=[audit.Image(Path(path)) for path in (
'artifacts/ship-model-reference/client-copy/EliteDangerous64.exe',
'D:/SteamLibrary/steamapps/common/Elite Dangerous/Products/elite-dangerous-odyssey-64/EliteDangerous64.exe')]
expected=['6891f70a021fa9f3a0e00458c284f48d1226e68ae7fc02ad48081aed47857e93',
'ed5a8f8cd10f9a80d3bda5badd7ba1d4fcfc15996c8c7e018852cb170e264b63']
if [hashlib.sha256(i.raw).hexdigest() for i in images]!=expected:raise ValueError('Metadata proof image changed')
old,new=images
functions=[]
for rva in [0x39BED80,0x39C9C50,0x39248D0,0x3911CA0,0x391E350,0x3B26180,0x3B25D40,0x3B262E0,0x3B27FA0,0x3B26030,0x1C40B90]:
span=old.function(rva)
if span is not None and span[0]!=rva:raise ValueError('Exact metadata function range missing: '+hex(rva))
if span is None:
# These two original leaf functions have no unwind entry. Bound their
# entire bodies by the first decoded INT3, requiring a preceding RET;
# the complete leaf must be byte-identical after the unique prefix.
decoded=old.instructions(rva,1024)
boundary=next(i for i,item in enumerate(decoded) if item.mnemonic=='int3')
if boundary==0 or decoded[boundary-1].mnemonic!='ret':raise ValueError('Leaf boundary changed')
length=decoded[boundary].address-old.base-rva
match=audit.match(old,new,rva,96)
if match['status']!='unique':raise ValueError('Leaf prefix is not unique')
after=int(match['newRva'],16)
if old.data(rva,length)!=new.data(after,length):raise ValueError('Complete leaf differs')
match.update(fullLeafBytes=length,fullLeafSha256=hashlib.sha256(old.data(rva,length)).hexdigest())
else:match=audit.match(old,new,rva,span[1]-rva)
if match['status']!='unique':raise ValueError('Metadata function no longer uniquely matched: '+hex(rva))
functions.append(dict(oldRva=hex(rva),**match))
def pointer(image,at):return struct.unpack('<Q',image.data(at,8))[0]-image.base
slots=[]
for name,tables,slot,targets in [
('lifecycleReady',(0x551A1E0,0x551A1B0),0x18,(0x39C9CD0,0x39C9C30)),
('completeClassificationReady',(0x551A1E0,0x551A1B0),0x20,(0x39C9C50,0x39C9BB0)),
('lookup',(0x551A1E0,0x551A1B0),0x28,(0x39BED80,0x39BECE0)),
('createDefaultNotCalled',(0x551A260,0x551A230),0x20,(0x39248D0,0x3924830)),
('markClassificationReadyNotCalled',(0x551A260,0x551A230),0x28,(0x39DD110,0x39DD070))]:
if [pointer(im,vt+slot) for im,vt in zip(images,tables)]!=list(targets):raise ValueError('Actual metadata slot changed: '+name)
slots.append(dict(name=name,slot=hex(slot),oldVtable=hex(tables[0]),newVtable=hex(tables[1]),
oldTarget=hex(targets[0]),newTarget=hex(targets[1])))
if old.data(0x39C9CD0,5)!=bytes.fromhex('0fb641e8c3') or new.data(0x39C9C30,5)!=old.data(0x39C9CD0,5):
raise ValueError('Lifecycle readiness leaf changed')
if old.data(0x39DD110,8)!=bytes.fromhex('c6810001000001c3') or new.data(0x39DD070,8)!=old.data(0x39DD110,8):
raise ValueError('Classification readiness writer changed')
folder=Path('artifacts/galaxy-query-research/metadata-loading')
evidence={}
for file in [folder/'decompiled/391e350.c',folder/'decompiled/39c5120.c',
Path('artifacts/galaxy-query-research/palette-resources/decompiled/solo-3b26180.c'),
Path('artifacts/galaxy-query-research/filter-enums/decompiled/solo-3b27fa0.c'),
folder/'assembly/asm-full-3b25d40.txt',folder/'assembly/asm-full-3b262e0.txt',
folder/'assembly/asm-full-3b26030.txt',folder/'assembly/asm-full-1c40b90.txt']:
evidence[str(file)]=hashlib.sha256(file.read_bytes()).hexdigest()
report=dict(runtimeNeutral=True,gameCalls=0,networkRequests=0,descriptorAllocations=0,canInstallHooks=False,
oldImageSha256=expected[0],newImageSha256=expected[1],functions=functions,actualTypedSlots=slots,evidenceSha256=evidence,
readiness=dict(interfaceOffset='component+60',lifecycleByte='interface-18 = component+48',
classificationReadyByte='interface+108 = component+168',requireBothNonzero=True),
lookup=dict(primary='interface+A8',secondary='interface+C8',nodeValue='node+10',
confirmedMissing='Two valid stable map lookups, repeated after both ready gates; changed/corrupt maps stay unknown'),
originalEffectiveDefaults=dict(population='0',governmentId=1,primaryEconomyId=1,secondaryEconomyId=1,
factionStateId=0,allegianceId='18446744073709551615',allegianceKey='None',securityClass=0,securityKey='Anarchy'),
limits=['defaulted=true describes original classifier fallback, not a measured galaxy-wide population fact.',
'A missing row before both readiness gates is unknown; no default or original mutating getter is invoked.',
'Observed rows win over defaults and retain defaulted=false; future enum values remain raw/unknown.',
'39C5120 loads configured original local assets; this implementation does not request or accelerate remote metadata.'])
path=folder/'original-missing-defaults-evidence.json';path.write_text(json.dumps(report,indent=2)+'\n',encoding='utf8')
print(json.dumps(dict(report=str(path),functions=len(functions),typedSlots=len(slots),gameCalls=0),indent=2))