OpenMFOR/extra/decomp_logic.py

180 lines
5.6 KiB
Python

import xdis
import ast
from collections import defaultdict
def main():
pyc_filename = 'Randomizer.pyc'
(
version_tuple,
timestamp,
magic_int,
topco,
is_pypy,
source_size,
sip_hash,
) = xdis.load_module(pyc_filename)
opc = xdis.get_opcode(version_tuple, is_pypy)
for co in topco.co_consts:
if getattr(co, 'co_name', '') == 'update_requirements':
bc = xdis.Bytecode(co, opc)
ops = set()
for i in bc:
ops.add(i.opname)
#if i.opname == 'EXTENDED_ARG' or 'JUMP' in i.opname:
# print(i)
#print('\n'.join(sorted(ops)))
dump(bc)
def mkbool(op, a1, a2):
if op == 'and':
expr = []
for x in a1, a2:
if isinstance(x, ast.BoolOp) and isinstance(x.op, ast.And):
expr.extend(x.values)
else:
expr.append(x)
return ast.BoolOp(ast.And(), expr)
elif op == 'or':
expr = []
for x in a1, a2:
if isinstance(x, ast.BoolOp) and isinstance(x.op, ast.Or):
expr.extend(x.values)
else:
expr.append(x)
return ast.BoolOp(ast.Or(), expr)
else:
return (op, a1, a2)
def dump(bytecode):
op_stack = []
expr_stack = []
value_stack = []
reduce = defaultdict(int)
push = defaultdict(int)
rindex = dict()
retired = set()
i = 0
for inst in bytecode:
addr = inst.offset
op = inst.opname
arg = inst.argval
#print(addr, op, reduce[addr], push[addr], rindex.get(addr, ""))
if reduce[addr]:
if push[addr]:
if reduce[addr] != push[addr]:
print("stack mismatch at %s: %d != %d", addr, reduce[addr], push[addr])
stash = []
assert len(expr_stack) == len(op_stack), (expr_stack, op_stack)
if push[addr]:
expr = value_stack.pop()
j,k = i,i
i += 1
else:
stash = [op_stack.pop()]
j,k,expr = expr_stack.pop()
r = rindex[addr]
while not j <= r <= k:
j2,k2,a2 = j,k,expr
j1,k1,a1 = expr_stack.pop()
xop = op_stack.pop()
expr = mkbool(xop, a1, a2)
j, k = j1, k2 # min(j1,j2), max(k1, k2)
expr_stack.append((j,k,expr))
op_stack.extend(stash)
if push[addr]:
_,_,top = expr_stack.pop()
#print(top)
#print(ast.dump(top, indent=4))
#print(ast.unparse(top))
value_stack.append(top)
if op == 'LOAD_NAME' or op == 'LOAD_GLOBAL' or op == 'LOAD_FAST':
value_stack.append(ast.Name(arg))
elif op == 'LOAD_CONST':
value_stack.append(ast.Constant(arg))
elif op == 'EXTENDED_ARG':
pass
elif op == 'STORE_NAME':
sys.stdout.flush()
assert not value_stack, value_stack
assert not expr_stack, expr_stack
elif op == 'POP_JUMP_IF_TRUE':
dest = arg
val = value_stack.pop()
expr_stack.append((i,i,val))
op_stack.append('or')
reduce[dest] += 1
rindex.setdefault(dest, i)
i += 1
elif op == 'POP_JUMP_IF_FALSE':
dest = arg
val = value_stack.pop()
expr_stack.append((i,i,val))
op_stack.append('and')
reduce[dest] += 1
rindex.setdefault(dest, i)
i += 1
elif op == 'JUMP_IF_TRUE_OR_POP':
dest = arg
val = value_stack.pop()
expr_stack.append((i,i,val))
op_stack.append('or')
reduce[dest] += 1
push[dest] += 1
rindex.setdefault(dest, i)
i += 1
elif op == 'JUMP_IF_FALSE_OR_POP':
dest = arg
val = value_stack.pop()
expr_stack.append((i,i,val))
op_stack.append('and')
reduce[dest] += 1
push[dest] += 1
rindex.setdefault(dest, i)
i += 1
elif op == 'COMPARE_OP':
a2 = value_stack.pop()
a1 = value_stack.pop()
if arg == '>':
cop = ast.Gt()
elif arg == '>=':
cop = ast.GtE()
else:
print('unknown comparator', arg)
cop = ''
if cop:
value_stack.append(ast.Compare(a1, ops=[cop], comparators=[a2]))
elif op == 'BUILD_CONST_KEY_MAP':
key_expr = value_stack.pop()
if len(value_stack) < arg:
print("error: BUILD_CONST_KEY_MAP: not enough expressions on stack (%d < %d)" % (len(value_stack), arg))
vals = value_stack[-arg:]
value_stack[-arg:] = []
assert isinstance(key_expr, ast.Constant)
assert len(key_expr.value) == arg
l = max(len(str(k)) for k in key_expr.value)
for k, v in zip(key_expr.value, vals):
print(" %s: %s%s," % (k, " "*(l-len(str(k))), ast.unparse(v)))
m = ast.Dict(map(ast.Constant, key_expr.value), vals)
value_stack.append(m)
elif op == 'STORE_ATTR':
obj = value_stack.pop()
val = value_stack.pop()
name = arg
#print(obj, name, val)
x = ast.Assign([ast.Attribute(val, name)], val, lineno=0)
#print(ast.unparse(x))
else:
print('unknown op', op, arg)
print(expr_stack)
print(value_stack)
main()