"""Replay original material blend-state bytecode using pinned offline data.
4F8F90 interprets an effect's kind-5 state through 4FBEC0, then hands the
resulting 48-byte descriptor to 516980 and the actual 513D70 D3D11 creator.
This records each source binding instead of inferring blend from render layer.
It never opens the game, executes its code, or changes production rendering.
"""
from pathlib import Path
import hashlib
import json
import re
import struct
import pefile
IMAGE = Path('artifacts/ship-model-reference/client-copy/EliteDangerous64.exe')
EFFECTS = Path('artifacts/galaxy-query-research/power-outer-sources/effects/Effects_Win64_SM50.arc.raw')
EFFECTS2 = EFFECTS.with_name('Effects2_Win64_SM50.arc.raw')
MATERIALS = Path('artifacts/galaxy-query-research/power-outer-sources/material/original-materials.json')
OUT = Path('artifacts/galaxy-query-research/power-outer-sources/material-slots/original-blend-application.json')
IMAGE_SHA = '6891f70a021fa9f3a0e00458c284f48d1226e68ae7fc02ad48081aed47857e93'
EFFECTS_SHA = '649e3107c868fe31ba23c9e1d19be2b99777b908431987f04f08cd39f0f798ca'
EFFECTS2_SHA = '7d2b122f155c3c6603b5f9cc17832f8067bb6c2be2db7beed452bdbd110289ed'
def digest(data):
return hashlib.sha256(data).hexdigest()
image = IMAGE.read_bytes()
archive = EFFECTS.read_bytes()
if digest(image) != IMAGE_SHA or digest(archive) != EFFECTS_SHA:
raise ValueError('Pinned image/effects changed')
pe = pefile.PE(data=image, fast_load=True)
pe.parse_data_directories(directories=[3])
default = pe.get_data(0x5E737D0, 48)
if default.hex() != '000000000400000000000000000000000100000000000000000000000100000000000000000000000f0f0f0f0f0f0f0f':
raise ValueError('Original unpacked default blend descriptor changed')
conversion = [struct.unpack_from('<2I', pe.get_data(0x4E2E460, 128), i * 8) for i in range(16)]
if conversion[:10] != [(1, 1), (2, 2), (3, 5), (4, 6), (5, 5), (6, 6), (7, 7), (8, 8), (9, 7), (10, 8)]:
raise ValueError('Original RGB/alpha blend enumeration changed')
ops = struct.unpack('<5I', pe.get_data(0x4E2DAE8, 20))
blend_names = ['Zero', 'One', 'SourceColor', 'InverseSourceColor', 'SourceAlpha', 'InverseSourceAlpha',
'DestinationAlpha', 'InverseDestinationAlpha', 'DestinationColor', 'InverseDestinationColor',
'BlendFactor', 'InverseBlendFactor', 'Source1Color', 'InverseSource1Color',
'Source1Alpha', 'InverseSource1Alpha']
def bounded(blob, offset, size):
if offset < 0 or size < 0 or offset + size > len(blob):
raise ValueError('Original effect pointer/length is outside its object')
return blob[offset:offset + size]
def cstring(blob, pointer):
end = blob.find(b'\0', pointer, pointer + 512)
if end < pointer:
raise ValueError('Original effect name is not terminated')
return bounded(blob, pointer, end - pointer).decode('ascii')
def objects(start):
# PtrMov sections are independently aligned; the second marker need not
# immediately follow the first relocation table.
cursor = start + 8
result = []
for _ in range(2):
marker = archive.find(b'\0\1voMrtP', cursor, cursor + 32)
if marker < 0:
raise ValueError('Original PtrMov section missing')
size, relocations = struct.unpack_from('<2I', archive, marker + 8)
blob = bounded(archive, marker + 16, size)
bounded(archive, marker + 16 + size, relocations * 4)
result.append((marker + 16, blob))
cursor = marker + 16 + size + relocations * 4
return result
def effect_rows():
rows = []
for match in re.finditer(b'\x08\x02xfxd3f', archive):
start = match.start()
(first_offset, first), (second_offset, second) = objects(start)
passes_pointer, pass_count = struct.unpack_from('<2Q', first, 8)
passes = []
for index in range(pass_count):
record = bounded(first, passes_pointer + index * 0x70, 0x70)
passes.append(cstring(first, struct.unpack_from('<Q', record)[0]))
variables_pointer, variable_count = struct.unpack_from('<2Q', second, 0x48)
variables = []
for index in range(variable_count):
record = bounded(second, variables_pointer + index * 32, 32)
packed = struct.unpack_from('<H', record, 10)[0]
variables.append(dict(name=cstring(second, struct.unpack_from('<Q', record)[0]),
bank=packed & 3, offset=(packed >> 1) & 0x7FFC, packed=packed,
descriptorOffset=variables_pointer + index * 32))
banks = []
for index in range(3):
pointer, size, _ = struct.unpack_from('<3Q', second, index * 24)
banks.append(bounded(second, pointer, size))
state_pointer, state_count = struct.unpack_from('<2Q', first, 0x38)
blend_states = []
for index in range(state_count):
record = bounded(first, state_pointer + index * 16, 16)
if struct.unpack_from('<H', record, 10)[0] == 5:
blend_states.append(dict(recordOffset=state_pointer + index * 16,
programOffset=struct.unpack_from('<Q', record)[0],
recordRaw=record.hex()))
rows.append(dict(offset=start, firstOffset=first_offset, first=first,
secondOffset=second_offset, second=second, passes=passes,
variables=variables, banks=banks, blendStates=blend_states))
return rows
def replay(effect, state, material):
result = bytearray(default)
cursor = state['programOffset']
target = 0
writes = []
overrides = {p['name']: bytes.fromhex(p['raw']) for p in material['parameters']}
instructions = []
for _ in range(128):
raw = bounded(effect['first'], cursor, 4)
opcode, length, packed = struct.unpack('<BBH', raw)
bank = packed & 3
offset = (packed >> 1) & 0x7FFC
instruction = dict(programOffset=cursor, raw=raw.hex(), opcode=opcode,
targetOffset=target, argument=length)
if opcode in (0, 1):
target += length
instructions.append(instruction)
cursor += 4
if target > 48:
raise ValueError('Blend skip exceeds original descriptor')
if opcode == 0:
break
continue
# Actual selected Galaxy blend programs use only raw DWORD copies and
# DWORD-to-byte copies. Do not guess other opcodes' conversions.
if opcode not in (2, 6) or bank == 3 or packed & 4:
raise ValueError('Blend program requires an unimplemented operation/version')
size = (length >> 2) * 4
source = bounded(effect['banks'][bank], offset, size)
names = [v for v in effect['variables'] if v['bank'] == bank and v['offset'] == offset]
binding = dict(bank=bank, offset=offset, encodedLocation=packed,
names=[v['name'] for v in names], rawDefault=source.hex(), override=None)
applied = [(v['name'], overrides[v['name']]) for v in names if v['name'] in overrides]
if applied:
if len(applied) != 1 or len(applied[0][1]) < size:
raise ValueError('Ambiguous/short original material override')
name, value = applied[0]
source = value[:size]
binding['override'] = name
output = source if opcode == 2 else source[::4]
bounded(result, target, len(output))
result[target:target + len(output)] = output
instruction['source'] = binding
instruction['outputRaw'] = output.hex()
instructions.append(instruction)
writes.append(dict(offset=target, size=len(output), source=binding))
target += len(output)
cursor += 4
else:
raise ValueError('Unterminated original blend-state program')
enabled = [x != 0 for x in result[8:16]]
fields = struct.unpack_from('<6I', result, 16)
src, dst, op, alpha_src, alpha_dst, alpha_op = fields
if not any(enabled):
src, dst, op, alpha_src, alpha_dst, alpha_op = 1, 0, 0, 1, 0, 0
if max(src, dst, alpha_src, alpha_dst) >= len(conversion) or max(op, alpha_op) >= len(ops):
raise ValueError('Original blend enum outside the actual conversion tables')
flags = (int(result[0] != 0) | sum(int(v) << (i + 1) for i, v in enumerate(enabled)) |
op << 9 | src << 12 | dst << 16 | alpha_op << 20 | alpha_src << 23 | alpha_dst << 27)
return dict(programArchiveOffset=effect['firstOffset'] + state['programOffset'],
programRaw=bounded(effect['first'], state['programOffset'], cursor - state['programOffset']).hex(),
instructions=instructions, unpackedDescriptor=result.hex(), packedFlags=hex(flags),
alphaToCoverage=result[0] != 0, blendEnabled=enabled, writeMasks=list(result[40:48]),
originalEnums=dict(rgbSource=src, rgbDestination=dst, rgbOperation=op,
alphaSource=alpha_src, alphaDestination=alpha_dst, alphaOperation=alpha_op),
sourceNames=dict(rgb=blend_names[src], destination=blend_names[dst]),
d3d11=dict(rgbSource=conversion[src][0], rgbDestination=conversion[dst][0], rgbOperation=ops[op],
alphaSource=conversion[alpha_src][1], alphaDestination=conversion[alpha_dst][1],
alphaOperation=ops[alpha_op]))
effects = effect_rows()
for effect in effects:
effect['archiveSource'] = str(EFFECTS)
effect['archiveSha256'] = EFFECTS_SHA
archive = EFFECTS2.read_bytes()
if digest(archive) != EFFECTS2_SHA:
raise ValueError('Pinned Effects2 changed')
secondary = effect_rows()
for effect in secondary:
effect['archiveSource'] = str(EFFECTS2)
effect['archiveSha256'] = EFFECTS2_SHA
effects += secondary
materials = json.loads(MATERIALS.read_text())
outputs = []
for material in materials['materials']:
expected_passes = {material['shaderClass'] + '_Forward0', material['shaderClass'] + '_ReducedSize0',
material['shaderClass'] + '_Forward'}
matches = [e for e in effects if any(name in expected_passes for name in e['passes'])]
# The original runtime's pass/permutation selection remains independent.
# Record all matching passes, never select a convenient one by array order.
for effect in matches:
for state in effect['blendStates']:
try:
value = replay(effect, state, material)
except ValueError as error:
outputs.append(dict(material=material['name'], shaderClass=material['shaderClass'],
passes=effect['passes'], resolved=False, reason=str(error)))
continue
outputs.append(dict(material=material['name'], shaderClass=material['shaderClass'],
passes=effect['passes'], effectArchiveOffset=effect['offset'],
archiveSource=effect['archiveSource'], archiveSha256=effect['archiveSha256'],
firstObjectSha256=digest(effect['first']), secondObjectSha256=digest(effect['second']),
resolved=True, stateRecord=state, **value))
for name in ('traderoute', 'traderoutehotspot', 'sectorline_nodepth'):
rows = [r for r in outputs if r['material'] == name and r['resolved']]
if not rows or any(r['originalEnums'] != dict(rgbSource=4, rgbDestination=1, rgbOperation=0,
alphaSource=4, alphaDestination=1, alphaOperation=0) for r in rows):
raise ValueError('Original literal-bound Route/StarMapLine blend changed')
for name in ('milkywaycoremat', 'milkywayreducedsize'):
rows = [r for r in outputs if r['material'] == name and r['resolved']]
if not rows or any(r['originalEnums']['rgbSource'] != 2 for r in rows):
raise ValueError('Original material-bound SourceColor blend changed')
billboard = [r for r in outputs if r['material'] == 'simplebillboardglow' and r['resolved']]
if len(billboard) != 1 or billboard[0]['originalEnums'] != dict(rgbSource=1, rgbDestination=5, rgbOperation=0,
alphaSource=1, alphaDestination=5, alphaOperation=0):
raise ValueError('Original alpha-zero SimpleBillboard state changed')
source_paths = [Path('artifacts/galaxy-query-research/account-sources/decompiled') / (name + '.c')
for name in ('4f8f90', '4fbec0', '516980')]
source_paths += [Path('artifacts/galaxy-query-research/power-outer-sources/background/effect-loader/solo-4fa720.c')]
source_paths += [Path('artifacts/galaxy-query-research/current-trade/assembly') / ('asm-full-' + name + '.txt')
for name in ('4fa280', '4fd1c0', '4fc6e0')]
functions = []
for rva in (0x4F8F90, 0x4FBEC0, 0x516980, 0x513D70, 0x4FC6E0, 0x4FA720, 0x4FA280, 0x4FD1C0):
row = next(e.struct for e in pe.DIRECTORY_ENTRY_EXCEPTION if e.struct.BeginAddress == rva)
functions.append(dict(rva=hex(rva), firstUnwindBlockSha256=digest(pe.get_data(rva, row.EndAddress - rva))))
result = dict(gameProcessAccess=False, gameCalls=0, productionRenderingChanged=False,
image=str(IMAGE), imageSha256=IMAGE_SHA, effects=str(EFFECTS), effectsSha256=EFFECTS_SHA,
materialsSource=str(MATERIALS), materialsSha256=digest(MATERIALS.read_bytes()),
applicationChain=['0x4F8F90 kind-5 state', '0x4FBEC0 4-byte instruction stream',
'0x516980 unpacked-to-packed blend state', '0x513D70 actual CreateBlendState'],
defaultDescriptorRva='0x5E737D0', defaultDescriptor=default.hex(),
literalBankInitialization=dict(loader='0x4FA720', secondFrozenFields=dict(data='0x18',totalBytes='0x20',scalarBytes='0x28'),
bridge='0x4FA280', runtimeBankHandle='*(effect+0xA0)+0x38, first/thread-zero element',
initializer='0x4FD1C0', destination='(*bankHandle)+8', scalarSizeDestination='(*bankHandle)+0x10',
copy='0x4FD230 calls 0x489CA90 with runtimeData, serializedData, totalBytes',
exactCopyVerified=pe.get_data(0x4FD230,5).hex()=='e85bf83904'),
originalBankDecoder='bank = u16 & 3; byteOffset = (u16 >> 1) & 0x7FFC; bit2 selects current/versioned storage',
functions=functions, sources=[dict(path=str(p), sha256=digest(p.read_bytes())) for p in source_paths],
additionalEffects=dict(path=str(EFFECTS2),sha256=EFFECTS2_SHA),
effectsParsed=len(effects), materials=outputs,
limits=['Runtime pass/permutation selection and post-material overrides remain separate requirements',
'The complete renderer layer/output/HDR pipeline is not implied by material blend replay',
'No game process or live device state was read'], fullOutputRestored=False)
OUT.write_text(json.dumps(result, indent=2) + '\n', encoding='utf-8')
print(json.dumps(dict(path=str(OUT), effects=len(effects), resolved=sum(x['resolved'] for x in outputs),
trade=[dict(material=x['material'], enums=x['originalEnums'], d3d11=x['d3d11'])
for x in outputs if x['material'] in ('traderoute', 'traderoutehotspot') and x['resolved']]), indent=2))
"""Replay original material blend-state bytecode using pinned offline data.
4F8F90 interprets an effect's kind-5 state through 4FBEC0, then hands the
resulting 48-byte descriptor to 516980 and the actual 513D70 D3D11 creator.
This records each source binding instead of inferring blend from render layer.
It never opens the game, executes its code, or changes production rendering.
"""
from pathlib import Path
import hashlib
import json
import re
import struct
import pefile
IMAGE = Path('artifacts/ship-model-reference/client-copy/EliteDangerous64.exe')
EFFECTS = Path('artifacts/galaxy-query-research/power-outer-sources/effects/Effects_Win64_SM50.arc.raw')
EFFECTS2 = EFFECTS.with_name('Effects2_Win64_SM50.arc.raw')
MATERIALS = Path('artifacts/galaxy-query-research/power-outer-sources/material/original-materials.json')
OUT = Path('artifacts/galaxy-query-research/power-outer-sources/material-slots/original-blend-application.json')
IMAGE_SHA = '6891f70a021fa9f3a0e00458c284f48d1226e68ae7fc02ad48081aed47857e93'
EFFECTS_SHA = '649e3107c868fe31ba23c9e1d19be2b99777b908431987f04f08cd39f0f798ca'
EFFECTS2_SHA = '7d2b122f155c3c6603b5f9cc17832f8067bb6c2be2db7beed452bdbd110289ed'
def digest(data):
return hashlib.sha256(data).hexdigest()
image = IMAGE.read_bytes()
archive = EFFECTS.read_bytes()
if digest(image) != IMAGE_SHA or digest(archive) != EFFECTS_SHA:
raise ValueError('Pinned image/effects changed')
pe = pefile.PE(data=image, fast_load=True)
pe.parse_data_directories(directories=[3])
default = pe.get_data(0x5E737D0, 48)
if default.hex() != '000000000400000000000000000000000100000000000000000000000100000000000000000000000f0f0f0f0f0f0f0f':
raise ValueError('Original unpacked default blend descriptor changed')
conversion = [struct.unpack_from('<2I', pe.get_data(0x4E2E460, 128), i * 8) for i in range(16)]
if conversion[:10] != [(1, 1), (2, 2), (3, 5), (4, 6), (5, 5), (6, 6), (7, 7), (8, 8), (9, 7), (10, 8)]:
raise ValueError('Original RGB/alpha blend enumeration changed')
ops = struct.unpack('<5I', pe.get_data(0x4E2DAE8, 20))
blend_names = ['Zero', 'One', 'SourceColor', 'InverseSourceColor', 'SourceAlpha', 'InverseSourceAlpha',
'DestinationAlpha', 'InverseDestinationAlpha', 'DestinationColor', 'InverseDestinationColor',
'BlendFactor', 'InverseBlendFactor', 'Source1Color', 'InverseSource1Color',
'Source1Alpha', 'InverseSource1Alpha']
def bounded(blob, offset, size):
if offset < 0 or size < 0 or offset + size > len(blob):
raise ValueError('Original effect pointer/length is outside its object')
return blob[offset:offset + size]
def cstring(blob, pointer):
end = blob.find(b'\0', pointer, pointer + 512)
if end < pointer:
raise ValueError('Original effect name is not terminated')
return bounded(blob, pointer, end - pointer).decode('ascii')
def objects(start):
# PtrMov sections are independently aligned; the second marker need not
# immediately follow the first relocation table.
cursor = start + 8
result = []
for _ in range(2):
marker = archive.find(b'\0\1voMrtP', cursor, cursor + 32)
if marker < 0:
raise ValueError('Original PtrMov section missing')
size, relocations = struct.unpack_from('<2I', archive, marker + 8)
blob = bounded(archive, marker + 16, size)
bounded(archive, marker + 16 + size, relocations * 4)
result.append((marker + 16, blob))
cursor = marker + 16 + size + relocations * 4
return result
def effect_rows():
rows = []
for match in re.finditer(b'\x08\x02xfxd3f', archive):
start = match.start()
(first_offset, first), (second_offset, second) = objects(start)
passes_pointer, pass_count = struct.unpack_from('<2Q', first, 8)
passes = []
for index in range(pass_count):
record = bounded(first, passes_pointer + index * 0x70, 0x70)
passes.append(cstring(first, struct.unpack_from('<Q', record)[0]))
variables_pointer, variable_count = struct.unpack_from('<2Q', second, 0x48)
variables = []
for index in range(variable_count):
record = bounded(second, variables_pointer + index * 32, 32)
packed = struct.unpack_from('<H', record, 10)[0]
variables.append(dict(name=cstring(second, struct.unpack_from('<Q', record)[0]),
bank=packed & 3, offset=(packed >> 1) & 0x7FFC, packed=packed,
descriptorOffset=variables_pointer + index * 32))
banks = []
for index in range(3):
pointer, size, _ = struct.unpack_from('<3Q', second, index * 24)
banks.append(bounded(second, pointer, size))
state_pointer, state_count = struct.unpack_from('<2Q', first, 0x38)
blend_states = []
for index in range(state_count):
record = bounded(first, state_pointer + index * 16, 16)
if struct.unpack_from('<H', record, 10)[0] == 5:
blend_states.append(dict(recordOffset=state_pointer + index * 16,
programOffset=struct.unpack_from('<Q', record)[0],
recordRaw=record.hex()))
rows.append(dict(offset=start, firstOffset=first_offset, first=first,
secondOffset=second_offset, second=second, passes=passes,
variables=variables, banks=banks, blendStates=blend_states))
return rows
def replay(effect, state, material):
result = bytearray(default)
cursor = state['programOffset']
target = 0
writes = []
overrides = {p['name']: bytes.fromhex(p['raw']) for p in material['parameters']}
instructions = []
for _ in range(128):
raw = bounded(effect['first'], cursor, 4)
opcode, length, packed = struct.unpack('<BBH', raw)
bank = packed & 3
offset = (packed >> 1) & 0x7FFC
instruction = dict(programOffset=cursor, raw=raw.hex(), opcode=opcode,
targetOffset=target, argument=length)
if opcode in (0, 1):
target += length
instructions.append(instruction)
cursor += 4
if target > 48:
raise ValueError('Blend skip exceeds original descriptor')
if opcode == 0:
break
continue
# Actual selected Galaxy blend programs use only raw DWORD copies and
# DWORD-to-byte copies. Do not guess other opcodes' conversions.
if opcode not in (2, 6) or bank == 3 or packed & 4:
raise ValueError('Blend program requires an unimplemented operation/version')
size = (length >> 2) * 4
source = bounded(effect['banks'][bank], offset, size)
names = [v for v in effect['variables'] if v['bank'] == bank and v['offset'] == offset]
binding = dict(bank=bank, offset=offset, encodedLocation=packed,
names=[v['name'] for v in names], rawDefault=source.hex(), override=None)
applied = [(v['name'], overrides[v['name']]) for v in names if v['name'] in overrides]
if applied:
if len(applied) != 1 or len(applied[0][1]) < size:
raise ValueError('Ambiguous/short original material override')
name, value = applied[0]
source = value[:size]
binding['override'] = name
output = source if opcode == 2 else source[::4]
bounded(result, target, len(output))
result[target:target + len(output)] = output
instruction['source'] = binding
instruction['outputRaw'] = output.hex()
instructions.append(instruction)
writes.append(dict(offset=target, size=len(output), source=binding))
target += len(output)
cursor += 4
else:
raise ValueError('Unterminated original blend-state program')
enabled = [x != 0 for x in result[8:16]]
fields = struct.unpack_from('<6I', result, 16)
src, dst, op, alpha_src, alpha_dst, alpha_op = fields
if not any(enabled):
src, dst, op, alpha_src, alpha_dst, alpha_op = 1, 0, 0, 1, 0, 0
if max(src, dst, alpha_src, alpha_dst) >= len(conversion) or max(op, alpha_op) >= len(ops):
raise ValueError('Original blend enum outside the actual conversion tables')
flags = (int(result[0] != 0) | sum(int(v) << (i + 1) for i, v in enumerate(enabled)) |
op << 9 | src << 12 | dst << 16 | alpha_op << 20 | alpha_src << 23 | alpha_dst << 27)
return dict(programArchiveOffset=effect['firstOffset'] + state['programOffset'],
programRaw=bounded(effect['first'], state['programOffset'], cursor - state['programOffset']).hex(),
instructions=instructions, unpackedDescriptor=result.hex(), packedFlags=hex(flags),
alphaToCoverage=result[0] != 0, blendEnabled=enabled, writeMasks=list(result[40:48]),
originalEnums=dict(rgbSource=src, rgbDestination=dst, rgbOperation=op,
alphaSource=alpha_src, alphaDestination=alpha_dst, alphaOperation=alpha_op),
sourceNames=dict(rgb=blend_names[src], destination=blend_names[dst]),
d3d11=dict(rgbSource=conversion[src][0], rgbDestination=conversion[dst][0], rgbOperation=ops[op],
alphaSource=conversion[alpha_src][1], alphaDestination=conversion[alpha_dst][1],
alphaOperation=ops[alpha_op]))
effects = effect_rows()
for effect in effects:
effect['archiveSource'] = str(EFFECTS)
effect['archiveSha256'] = EFFECTS_SHA
archive = EFFECTS2.read_bytes()
if digest(archive) != EFFECTS2_SHA:
raise ValueError('Pinned Effects2 changed')
secondary = effect_rows()
for effect in secondary:
effect['archiveSource'] = str(EFFECTS2)
effect['archiveSha256'] = EFFECTS2_SHA
effects += secondary
materials = json.loads(MATERIALS.read_text())
outputs = []
for material in materials['materials']:
expected_passes = {material['shaderClass'] + '_Forward0', material['shaderClass'] + '_ReducedSize0',
material['shaderClass'] + '_Forward'}
matches = [e for e in effects if any(name in expected_passes for name in e['passes'])]
# The original runtime's pass/permutation selection remains independent.
# Record all matching passes, never select a convenient one by array order.
for effect in matches:
for state in effect['blendStates']:
try:
value = replay(effect, state, material)
except ValueError as error:
outputs.append(dict(material=material['name'], shaderClass=material['shaderClass'],
passes=effect['passes'], resolved=False, reason=str(error)))
continue
outputs.append(dict(material=material['name'], shaderClass=material['shaderClass'],
passes=effect['passes'], effectArchiveOffset=effect['offset'],
archiveSource=effect['archiveSource'], archiveSha256=effect['archiveSha256'],
firstObjectSha256=digest(effect['first']), secondObjectSha256=digest(effect['second']),
resolved=True, stateRecord=state, **value))
for name in ('traderoute', 'traderoutehotspot', 'sectorline_nodepth'):
rows = [r for r in outputs if r['material'] == name and r['resolved']]
if not rows or any(r['originalEnums'] != dict(rgbSource=4, rgbDestination=1, rgbOperation=0,
alphaSource=4, alphaDestination=1, alphaOperation=0) for r in rows):
raise ValueError('Original literal-bound Route/StarMapLine blend changed')
for name in ('milkywaycoremat', 'milkywayreducedsize'):
rows = [r for r in outputs if r['material'] == name and r['resolved']]
if not rows or any(r['originalEnums']['rgbSource'] != 2 for r in rows):
raise ValueError('Original material-bound SourceColor blend changed')
billboard = [r for r in outputs if r['material'] == 'simplebillboardglow' and r['resolved']]
if len(billboard) != 1 or billboard[0]['originalEnums'] != dict(rgbSource=1, rgbDestination=5, rgbOperation=0,
alphaSource=1, alphaDestination=5, alphaOperation=0):
raise ValueError('Original alpha-zero SimpleBillboard state changed')
source_paths = [Path('artifacts/galaxy-query-research/account-sources/decompiled') / (name + '.c')
for name in ('4f8f90', '4fbec0', '516980')]
source_paths += [Path('artifacts/galaxy-query-research/power-outer-sources/background/effect-loader/solo-4fa720.c')]
source_paths += [Path('artifacts/galaxy-query-research/current-trade/assembly') / ('asm-full-' + name + '.txt')
for name in ('4fa280', '4fd1c0', '4fc6e0')]
functions = []
for rva in (0x4F8F90, 0x4FBEC0, 0x516980, 0x513D70, 0x4FC6E0, 0x4FA720, 0x4FA280, 0x4FD1C0):
row = next(e.struct for e in pe.DIRECTORY_ENTRY_EXCEPTION if e.struct.BeginAddress == rva)
functions.append(dict(rva=hex(rva), firstUnwindBlockSha256=digest(pe.get_data(rva, row.EndAddress - rva))))
result = dict(gameProcessAccess=False, gameCalls=0, productionRenderingChanged=False,
image=str(IMAGE), imageSha256=IMAGE_SHA, effects=str(EFFECTS), effectsSha256=EFFECTS_SHA,
materialsSource=str(MATERIALS), materialsSha256=digest(MATERIALS.read_bytes()),
applicationChain=['0x4F8F90 kind-5 state', '0x4FBEC0 4-byte instruction stream',
'0x516980 unpacked-to-packed blend state', '0x513D70 actual CreateBlendState'],
defaultDescriptorRva='0x5E737D0', defaultDescriptor=default.hex(),
literalBankInitialization=dict(loader='0x4FA720', secondFrozenFields=dict(data='0x18',totalBytes='0x20',scalarBytes='0x28'),
bridge='0x4FA280', runtimeBankHandle='*(effect+0xA0)+0x38, first/thread-zero element',
initializer='0x4FD1C0', destination='(*bankHandle)+8', scalarSizeDestination='(*bankHandle)+0x10',
copy='0x4FD230 calls 0x489CA90 with runtimeData, serializedData, totalBytes',
exactCopyVerified=pe.get_data(0x4FD230,5).hex()=='e85bf83904'),
originalBankDecoder='bank = u16 & 3; byteOffset = (u16 >> 1) & 0x7FFC; bit2 selects current/versioned storage',
functions=functions, sources=[dict(path=str(p), sha256=digest(p.read_bytes())) for p in source_paths],
additionalEffects=dict(path=str(EFFECTS2),sha256=EFFECTS2_SHA),
effectsParsed=len(effects), materials=outputs,
limits=['Runtime pass/permutation selection and post-material overrides remain separate requirements',
'The complete renderer layer/output/HDR pipeline is not implied by material blend replay',
'No game process or live device state was read'], fullOutputRestored=False)
OUT.write_text(json.dumps(result, indent=2) + '\n', encoding='utf-8')
print(json.dumps(dict(path=str(OUT), effects=len(effects), resolved=sum(x['resolved'] for x in outputs),
trade=[dict(material=x['material'], enums=x['originalEnums'], d3d11=x['d3d11'])
for x in outputs if x['material'] in ('traderoute', 'traderoutehotspot') and x['resolved']]), indent=2))