re: a Stage 02 clear condition read end to end, with squadron names
Resolving symbol-table-2 indices turns the bytecode into mission logic. At 0xF524 Stage02.ssb polls unit_state on ADN110, ADN111 and ADN112, updates each one's objective marker, then latches set_flag(8) -- exactly the trigger/predicate/set_flag/END_PHASE shape predicted from the disassembly, now observed in the mission's own code with names the roster tables already gave. The 12 END_PHASE sites are outro sequences (wait_cmds_drained / fade_sound(3) / builtin85(3) / wait_s(3) / END_PHASE / yield) -- the terminator, not the decision. Fixes a decode bug that hid every argument: the tracker only followed local[i] = special[0], but the common form is an immediate written straight into local[i] (k=01,03), so every unit predicate printed with NO arguments. The disassembly looked complete while being empty exactly where it mattered. Also records the live probe result: the phase mirror at [*(0x828F35F8)+236] stayed 0 for ~530s of actively-hunting flight, no advance observed -- which is what the static analysis predicts for phase 1, since ChangePhase only posts once the ordinal exceeds 1.
This commit is contained in:
@@ -105,7 +105,33 @@ def load(path):
|
||||
return open(path, 'rb').read()
|
||||
|
||||
|
||||
def dis(b, off, count=40, code_base=0x24, args=True):
|
||||
def symbols(b, which):
|
||||
"""Parse a .ssb symbol table -> {index: (type, name)}.
|
||||
|
||||
Built-in argument blobs carry INDICES into these: fields that index
|
||||
`[phase+244]` are symtab-1 (routes, messages, subobjectives) and fields that
|
||||
index `[phase+324]` are symtab-2 (the unit ids). Resolving them is what turns
|
||||
`unit_state(0x2b)` into `unit_state(ADN201)`.
|
||||
"""
|
||||
off = struct.unpack_from('>I', b, 0x0C if which == 1 else 0x10)[0]
|
||||
cnt = struct.unpack_from('>I', b, off)[0]
|
||||
base = off + 4
|
||||
out = {}
|
||||
for i in range(cnt):
|
||||
o = struct.unpack_from('>I', b, base + 4 * i)[0]
|
||||
if o == 0:
|
||||
continue
|
||||
rp = base + o
|
||||
typ = struct.unpack_from('>I', b, rp)[0]
|
||||
e = b.index(b'\0', rp + 4)
|
||||
out[i] = (typ, b[rp + 4:e].decode('latin-1'))
|
||||
return out
|
||||
|
||||
|
||||
UNIT_ARG = {18, 20, 24, 26, 56, 69, 70, 94, 95, 105, 109} # unit idx at blob[4]
|
||||
|
||||
|
||||
def dis(b, off, count=40, code_base=0x24, args=True, sym2=None):
|
||||
out = []
|
||||
staged = {} # local[] slot -> last value staged into it
|
||||
pending = None # value most recently put in special[0]
|
||||
@@ -152,12 +178,26 @@ def dis(b, off, count=40, code_base=0x24, args=True):
|
||||
pending = words[1]
|
||||
elif k0 == 3 and k1 == 2 and pending is not None:
|
||||
staged[words[0]] = pending
|
||||
elif k0 == 3 and k1 == 1:
|
||||
# local[i] = immediate, DIRECTLY -- the common form. Missing this
|
||||
# made every unit predicate print with no arguments at all.
|
||||
if op == 1:
|
||||
lo = words[2] if len(words) > 2 else 0
|
||||
staged[words[0]] = '%.6g' % struct.unpack(
|
||||
'>d', struct.pack('>II', words[1], lo))[0]
|
||||
else:
|
||||
staged[words[0]] = words[1]
|
||||
if op == 19 and words:
|
||||
extra = ' %s' % BUILTIN.get(words[0], 'builtin%d' % words[0])
|
||||
if args and staged:
|
||||
extra += '(' + ', '.join(
|
||||
'%s' % (('0x%X' % v) if isinstance(v, int) else v)
|
||||
for _, v in sorted(staged.items())) + ')'
|
||||
parts = []
|
||||
for slot, v in sorted(staged.items()):
|
||||
txt = ('0x%X' % v) if isinstance(v, int) else v
|
||||
if (sym2 and slot == 4 and words[0] in UNIT_ARG
|
||||
and isinstance(v, int) and v in sym2):
|
||||
txt = sym2[v][1]
|
||||
parts.append(txt)
|
||||
extra += '(' + ', '.join(parts) + ')'
|
||||
staged = {}
|
||||
elif op == 12 and words:
|
||||
extra = ' -> code+0x%X (file 0x%X)' % (words[0], code_base + words[0])
|
||||
@@ -228,7 +268,8 @@ if __name__ == '__main__':
|
||||
if st is None:
|
||||
print('could not resync into 0x%X' % t); sys.exit(1)
|
||||
print('resync from 0x%X' % st)
|
||||
print('\n'.join(dis(b, st, int(sys.argv[4], 0) if len(sys.argv) > 4 else 40)))
|
||||
print('\n'.join(dis(b, st, int(sys.argv[4], 0) if len(sys.argv) > 4 else 40,
|
||||
sym2=symbols(b, 2))))
|
||||
sys.exit(0)
|
||||
code_base = struct.unpack_from('>I', b, CODE_BASE_FIELD)[0]
|
||||
a = sys.argv[2]
|
||||
|
||||
Reference in New Issue
Block a user